[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH 09/14] hw/misc/bcm2835_cprman: add a clock mux skeleton imple
From: |
Philippe Mathieu-Daudé |
Subject: |
Re: [PATCH 09/14] hw/misc/bcm2835_cprman: add a clock mux skeleton implementation |
Date: |
Fri, 2 Oct 2020 16:42:57 +0200 |
User-agent: |
Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.11.0 |
On 9/25/20 12:17 PM, Luc Michel wrote:
> The clock multiplexers are the last clock stage in the cprman. Each mux
> outputs one clock signal that goes out of the cprman to the SoC
> peripherals.
>
> Each mux has at most 10 sources. The sources 0 to 3 are common to all
> muxes. They are:
> 0. ground (no clock signal)
> 1. the main oscillator (xosc)
> 2. "test debug 0" clock
> 3. "test debug 1" clock
>
> Test debug 0 and 1 are actual clock muxes that can be used as sources to
> other muxes (for debug purpose).
>
> Sources 4 to 9 are mux specific and can be unpopulated (grounded). Those
> sources are fed by the PLL channels outputs.
>
> One corner case exists for DSI0E and DSI0P muxes. They have their source
> number 4 connected to an intermediate multiplexer that can select
> between PLLA-DSI0 and PLLD-DSI0 channel. This multiplexer is called
> DSI0HSCK and is not a clock mux as such. It is really a simple mux from
> the hardware point of view (see https://elinux.org/The_Undocumented_Pi).
> This mux is not implemented in this commit.
>
> Note that there is some muxes for which sources are unknown (because of
> a lack of documentation). For those cases all the sources are connected
> to ground in this implementation.
>
> Each clock mux output is exported by the cprman at the qdev level,
> adding the suffix '-out' to the mux name to form the output clock name.
> (E.g. the 'uart' mux sees its output exported as 'uart-out' at the
> cprman level.)
>
> Signed-off-by: Luc Michel <luc@lmichel.fr>
> ---
> include/hw/misc/bcm2835_cprman.h | 84 ++++
> include/hw/misc/bcm2835_cprman_internals.h | 421 +++++++++++++++++++++
> hw/misc/bcm2835_cprman.c | 151 ++++++++
> 3 files changed, 656 insertions(+)
>
> diff --git a/include/hw/misc/bcm2835_cprman.h
> b/include/hw/misc/bcm2835_cprman.h
> index aaf15fb20c..c2a89e8e90 100644
> --- a/include/hw/misc/bcm2835_cprman.h
> +++ b/include/hw/misc/bcm2835_cprman.h
> @@ -52,12 +52,73 @@ typedef enum CprmanPLLChannel {
> CPRMAN_PLLH_CHANNEL_PIX,
>
> CPRMAN_PLLB_CHANNEL_ARM,
>
> CPRMAN_NUM_PLL_CHANNEL,
> +
> + /* Special values used when connecting clock sources to clocks */
> + CPRMAN_CLOCK_SRC_NORMAL = -1,
> + CPRMAN_CLOCK_SRC_FORCE_GROUND = -2,
> + CPRMAN_CLOCK_SRC_DSI0HSCK = -3,
Why not use CPRMAN_NORMAL_CHANNEL,
CPRMAN_FORCED_GROUND_CHANNEL and CPRMAN_DSI0HSCK_CHANNEL?
> } CprmanPLLChannel;
>
> +typedef enum CprmanClockMux {
> + CPRMAN_CLOCK_GNRIC,
> + CPRMAN_CLOCK_VPU,
> + CPRMAN_CLOCK_SYS,
> + CPRMAN_CLOCK_PERIA,
> + CPRMAN_CLOCK_PERII,
> + CPRMAN_CLOCK_H264,
> + CPRMAN_CLOCK_ISP,
> + CPRMAN_CLOCK_V3D,
> + CPRMAN_CLOCK_CAM0,
> + CPRMAN_CLOCK_CAM1,
> + CPRMAN_CLOCK_CCP2,
> + CPRMAN_CLOCK_DSI0E,
> + CPRMAN_CLOCK_DSI0P,
> + CPRMAN_CLOCK_DPI,
> + CPRMAN_CLOCK_GP0,
> + CPRMAN_CLOCK_GP1,
> + CPRMAN_CLOCK_GP2,
> + CPRMAN_CLOCK_HSM,
> + CPRMAN_CLOCK_OTP,
> + CPRMAN_CLOCK_PCM,
> + CPRMAN_CLOCK_PWM,
> + CPRMAN_CLOCK_SLIM,
> + CPRMAN_CLOCK_SMI,
> + CPRMAN_CLOCK_TEC,
> + CPRMAN_CLOCK_TD0,
> + CPRMAN_CLOCK_TD1,
> + CPRMAN_CLOCK_TSENS,
> + CPRMAN_CLOCK_TIMER,
> + CPRMAN_CLOCK_UART,
> + CPRMAN_CLOCK_VEC,
> + CPRMAN_CLOCK_PULSE,
> + CPRMAN_CLOCK_SDC,
> + CPRMAN_CLOCK_ARM,
> + CPRMAN_CLOCK_AVEO,
> + CPRMAN_CLOCK_EMMC,
> + CPRMAN_CLOCK_EMMC2,
> +
> + CPRMAN_NUM_CLOCK_MUX
> +} CprmanClockMux;
> +
> +typedef enum CprmanClockMuxSource {
> + CPRMAN_CLOCK_SRC_GND = 0,
> + CPRMAN_CLOCK_SRC_XOSC,
> + CPRMAN_CLOCK_SRC_TD0,
> + CPRMAN_CLOCK_SRC_TD1,
> + CPRMAN_CLOCK_SRC_PLLA,
> + CPRMAN_CLOCK_SRC_PLLC,
> + CPRMAN_CLOCK_SRC_PLLD,
> + CPRMAN_CLOCK_SRC_PLLH,
> + CPRMAN_CLOCK_SRC_PLLC_CORE1,
> + CPRMAN_CLOCK_SRC_PLLC_CORE2,
> +
> + CPRMAN_NUM_CLOCK_MUX_SRC
> +} CprmanClockMuxSource;
> +
> typedef struct CprmanPLLState {
> /*< private >*/
> DeviceState parent_obj;
>
> /*< public >*/
> @@ -89,22 +150,45 @@ typedef struct CprmanPLLChannelState {
>
> Clock *pll_in;
> Clock *out;
> } CprmanPLLChannelState;
>
> +typedef struct CprmanClockMuxState {
> + /*< private >*/
> + DeviceState parent_obj;
> +
> + /*< public >*/
> + CprmanClockMux id;
> +
> + uint32_t *reg_cm;
> + int int_bits;
> + int frac_bits;
> +
> + Clock *srcs[CPRMAN_NUM_CLOCK_MUX_SRC];
> + Clock *out;
> +
> + /*
> + * Used by clock srcs update callback to retrieve both the clock and the
> + * source number.
> + */
> + struct CprmanClockMuxState *backref[CPRMAN_NUM_CLOCK_MUX_SRC];
> +} CprmanClockMuxState;
> +
> struct BCM2835CprmanState {
> /*< private >*/
> SysBusDevice parent_obj;
>
> /*< public >*/
> MemoryRegion iomem;
>
> CprmanPLLState plls[CPRMAN_NUM_PLL];
> CprmanPLLChannelState channels[CPRMAN_NUM_PLL_CHANNEL];
> + CprmanClockMuxState clock_muxes[CPRMAN_NUM_CLOCK_MUX];
>
> uint32_t regs[CPRMAN_NUM_REGS];
> uint32_t xosc_freq;
>
> Clock *xosc;
> + Clock *gnd;
This one seems to belong to MachineState in "hw/boards.h".
> };
>
> #endif
> diff --git a/include/hw/misc/bcm2835_cprman_internals.h
> b/include/hw/misc/bcm2835_cprman_internals.h
> index 8a5b9aae67..a2b5a1aa50 100644
> --- a/include/hw/misc/bcm2835_cprman_internals.h
> +++ b/include/hw/misc/bcm2835_cprman_internals.h
> @@ -12,15 +12,18 @@
> #include "hw/registerfields.h"
> #include "hw/misc/bcm2835_cprman.h"
>
> #define TYPE_CPRMAN_PLL "bcm2835-cprman-pll"
> #define TYPE_CPRMAN_PLL_CHANNEL "bcm2835-cprman-pll-channel"
> +#define TYPE_CPRMAN_CLOCK_MUX "bcm2835-cprman-clock-mux"
>
> DECLARE_INSTANCE_CHECKER(CprmanPLLState, CPRMAN_PLL,
> TYPE_CPRMAN_PLL)
> DECLARE_INSTANCE_CHECKER(CprmanPLLChannelState, CPRMAN_PLL_CHANNEL,
> TYPE_CPRMAN_PLL_CHANNEL)
> +DECLARE_INSTANCE_CHECKER(CprmanClockMuxState, CPRMAN_CLOCK_MUX,
> + TYPE_CPRMAN_CLOCK_MUX)
>
> /* Register map */
>
> /* PLLs */
> REG32(CM_PLLA, 0x104)
> @@ -126,10 +129,94 @@ REG32(A2W_PLLH_RCAL, 0x1460)
> REG32(A2W_PLLH_PIX, 0x1560)
> REG32(A2W_PLLH_STS, 0x1660)
>
> REG32(A2W_PLLB_ARM, 0x13e0)
>
> +/* Clock muxes */
> +REG32(CM_GNRICCTL, 0x000)
> + FIELD(CM_CLOCKx_CTL, SRC, 0, 4)
> + FIELD(CM_CLOCKx_CTL, ENABLE, 4, 1)
> + FIELD(CM_CLOCKx_CTL, KILL, 5, 1)
> + FIELD(CM_CLOCKx_CTL, GATE, 6, 1)
> + FIELD(CM_CLOCKx_CTL, BUSY, 7, 1)
> + FIELD(CM_CLOCKx_CTL, BUSYD, 8, 1)
> + FIELD(CM_CLOCKx_CTL, MASH, 9, 2)
> + FIELD(CM_CLOCKx_CTL, FLIP, 11, 1)
> +REG32(CM_GNRICDIV, 0x004)
> + FIELD(CM_CLOCKx_DIV, FRAC, 0, 12)
> +REG32(CM_VPUCTL, 0x008)
> +REG32(CM_VPUDIV, 0x00c)
> +REG32(CM_SYSCTL, 0x010)
> +REG32(CM_SYSDIV, 0x014)
> +REG32(CM_PERIACTL, 0x018)
> +REG32(CM_PERIADIV, 0x01c)
> +REG32(CM_PERIICTL, 0x020)
> +REG32(CM_PERIIDIV, 0x024)
> +REG32(CM_H264CTL, 0x028)
> +REG32(CM_H264DIV, 0x02c)
> +REG32(CM_ISPCTL, 0x030)
> +REG32(CM_ISPDIV, 0x034)
> +REG32(CM_V3DCTL, 0x038)
> +REG32(CM_V3DDIV, 0x03c)
> +REG32(CM_CAM0CTL, 0x040)
> +REG32(CM_CAM0DIV, 0x044)
> +REG32(CM_CAM1CTL, 0x048)
> +REG32(CM_CAM1DIV, 0x04c)
> +REG32(CM_CCP2CTL, 0x050)
> +REG32(CM_CCP2DIV, 0x054)
> +REG32(CM_DSI0ECTL, 0x058)
> +REG32(CM_DSI0EDIV, 0x05c)
> +REG32(CM_DSI0PCTL, 0x060)
> +REG32(CM_DSI0PDIV, 0x064)
> +REG32(CM_DPICTL, 0x068)
> +REG32(CM_DPIDIV, 0x06c)
> +REG32(CM_GP0CTL, 0x070)
> +REG32(CM_GP0DIV, 0x074)
> +REG32(CM_GP1CTL, 0x078)
> +REG32(CM_GP1DIV, 0x07c)
> +REG32(CM_GP2CTL, 0x080)
> +REG32(CM_GP2DIV, 0x084)
> +REG32(CM_HSMCTL, 0x088)
> +REG32(CM_HSMDIV, 0x08c)
> +REG32(CM_OTPCTL, 0x090)
> +REG32(CM_OTPDIV, 0x094)
> +REG32(CM_PCMCTL, 0x098)
> +REG32(CM_PCMDIV, 0x09c)
> +REG32(CM_PWMCTL, 0x0a0)
> +REG32(CM_PWMDIV, 0x0a4)
> +REG32(CM_SLIMCTL, 0x0a8)
> +REG32(CM_SLIMDIV, 0x0ac)
> +REG32(CM_SMICTL, 0x0b0)
> +REG32(CM_SMIDIV, 0x0b4)
> +REG32(CM_TCNTCTL, 0x0c0)
> +REG32(CM_TCNTCNT, 0x0c4)
> +REG32(CM_TECCTL, 0x0c8)
> +REG32(CM_TECDIV, 0x0cc)
> +REG32(CM_TD0CTL, 0x0d0)
> +REG32(CM_TD0DIV, 0x0d4)
> +REG32(CM_TD1CTL, 0x0d8)
> +REG32(CM_TD1DIV, 0x0dc)
> +REG32(CM_TSENSCTL, 0x0e0)
> +REG32(CM_TSENSDIV, 0x0e4)
> +REG32(CM_TIMERCTL, 0x0e8)
> +REG32(CM_TIMERDIV, 0x0ec)
> +REG32(CM_UARTCTL, 0x0f0)
> +REG32(CM_UARTDIV, 0x0f4)
> +REG32(CM_VECCTL, 0x0f8)
> +REG32(CM_VECDIV, 0x0fc)
> +REG32(CM_PULSECTL, 0x190)
> +REG32(CM_PULSEDIV, 0x194)
> +REG32(CM_SDCCTL, 0x1a8)
> +REG32(CM_SDCDIV, 0x1ac)
> +REG32(CM_ARMCTL, 0x1b0)
> +REG32(CM_AVEOCTL, 0x1b8)
> +REG32(CM_AVEODIV, 0x1bc)
> +REG32(CM_EMMCCTL, 0x1c0)
> +REG32(CM_EMMCDIV, 0x1c4)
> +REG32(CM_EMMC2CTL, 0x1d0)
> +REG32(CM_EMMC2DIV, 0x1d4)
> +
> /* misc registers */
> REG32(CM_LOCK, 0x114)
> FIELD(CM_LOCK, FLOCKH, 12, 1)
> FIELD(CM_LOCK, FLOCKD, 11, 1)
> FIELD(CM_LOCK, FLOCKC, 10, 1)
> @@ -317,6 +404,340 @@ static inline void
> set_pll_channel_init_info(BCM2835CprmanState *s,
> channel->load_mask = PLL_CHANNEL_INIT_INFO[id].cm_load_mask;
> channel->reg_a2w_ctrl =
> &s->regs[PLL_CHANNEL_INIT_INFO[id].a2w_ctrl_offset];
> channel->fixed_divider = PLL_CHANNEL_INIT_INFO[id].fixed_divider;
> }
>
> +/* Clock mux init info */
> +typedef struct ClockMuxInitInfo {
> + const char *name;
> + size_t cm_offset;
> + int int_bits;
> + int frac_bits;
> +
> + CprmanPLLChannel src_mapping[CPRMAN_NUM_CLOCK_MUX_SRC];
> +} ClockMuxInitInfo;
> +
> +/*
> + * Each clock mux can have up to 10 sources. Sources 0 to 3 are always the
> + * same (ground, xosc, td0, td1). Sources 4 to 9 are mux specific, and are
> not
> + * always populated. The following macros catch all those cases.
> + */
> +
> +/* Unknown mapping. Connect everything to ground */
> +#define SRC_MAPPING_INFO_unknown \
> + .src_mapping = { \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, /* gnd */ \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, /* xosc */ \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, /* test debug 0 */ \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, /* test debug 1 */ \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, /* pll a */ \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, /* pll c */ \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, /* pll d */ \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, /* pll h */ \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, /* pll c, core1 */ \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, /* pll c, core2 */ \
> + }
> +
> +/* Only the oscillator and the two test debug clocks */
> +#define SRC_MAPPING_INFO_xosc \
> + .src_mapping = { \
> + CPRMAN_CLOCK_SRC_NORMAL, \
> + CPRMAN_CLOCK_SRC_NORMAL, \
> + CPRMAN_CLOCK_SRC_NORMAL, \
> + CPRMAN_CLOCK_SRC_NORMAL, \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, \
> + }
> +
> +/* All the PLL "core" channels */
> +#define SRC_MAPPING_INFO_core \
> + .src_mapping = { \
> + CPRMAN_CLOCK_SRC_NORMAL, \
> + CPRMAN_CLOCK_SRC_NORMAL, \
> + CPRMAN_CLOCK_SRC_NORMAL, \
> + CPRMAN_CLOCK_SRC_NORMAL, \
> + CPRMAN_PLLA_CHANNEL_CORE, \
> + CPRMAN_PLLC_CHANNEL_CORE0, \
> + CPRMAN_PLLD_CHANNEL_CORE, \
> + CPRMAN_PLLH_CHANNEL_AUX, \
> + CPRMAN_PLLC_CHANNEL_CORE1, \
> + CPRMAN_PLLC_CHANNEL_CORE2, \
> + }
> +
> +/* All the PLL "per" channels */
> +#define SRC_MAPPING_INFO_periph \
> + .src_mapping = { \
> + CPRMAN_CLOCK_SRC_NORMAL, \
> + CPRMAN_CLOCK_SRC_NORMAL, \
> + CPRMAN_CLOCK_SRC_NORMAL, \
> + CPRMAN_CLOCK_SRC_NORMAL, \
> + CPRMAN_PLLA_CHANNEL_PER, \
> + CPRMAN_PLLC_CHANNEL_PER, \
> + CPRMAN_PLLD_CHANNEL_PER, \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, \
> + }
> +
> +/*
> + * The DSI0 channels. This one got an intermediate mux between the PLL
> channels
> + * and the clock input.
> + */
> +#define SRC_MAPPING_INFO_dsi0 \
> + .src_mapping = { \
> + CPRMAN_CLOCK_SRC_NORMAL, \
> + CPRMAN_CLOCK_SRC_NORMAL, \
> + CPRMAN_CLOCK_SRC_NORMAL, \
> + CPRMAN_CLOCK_SRC_NORMAL, \
> + CPRMAN_CLOCK_SRC_DSI0HSCK, \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, \
> + }
> +
> +/* The DSI1 channel */
> +#define SRC_MAPPING_INFO_dsi1 \
> + .src_mapping = { \
> + CPRMAN_CLOCK_SRC_NORMAL, \
> + CPRMAN_CLOCK_SRC_NORMAL, \
> + CPRMAN_CLOCK_SRC_NORMAL, \
> + CPRMAN_CLOCK_SRC_NORMAL, \
> + CPRMAN_PLLD_CHANNEL_DSI1, \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, \
> + CPRMAN_CLOCK_SRC_FORCE_GROUND, \
> + }
> +
> +#define FILL_CLOCK_MUX_SRC_MAPPING_INIT_INFO(kind_) \
> + SRC_MAPPING_INFO_ ## kind_
> +
> +#define FILL_CLOCK_MUX_INIT_INFO(clock_, kind_) \
> + .cm_offset = R_CM_ ## clock_ ## CTL, \
> + FILL_CLOCK_MUX_SRC_MAPPING_INIT_INFO(kind_)
> +
> +static ClockMuxInitInfo CLOCK_MUX_INIT_INFO[] = {
> + [CPRMAN_CLOCK_GNRIC] = {
> + .name = "gnric",
> + FILL_CLOCK_MUX_INIT_INFO(GNRIC, unknown),
> + },
> + [CPRMAN_CLOCK_VPU] = {
> + .name = "vpu",
> + .int_bits = 12,
> + .frac_bits = 8,
> + FILL_CLOCK_MUX_INIT_INFO(VPU, core),
> + },
> + [CPRMAN_CLOCK_SYS] = {
> + .name = "sys",
> + FILL_CLOCK_MUX_INIT_INFO(SYS, unknown),
> + },
> + [CPRMAN_CLOCK_PERIA] = {
> + .name = "peria",
> + FILL_CLOCK_MUX_INIT_INFO(PERIA, unknown),
> + },
> + [CPRMAN_CLOCK_PERII] = {
> + .name = "perii",
> + FILL_CLOCK_MUX_INIT_INFO(PERII, unknown),
> + },
> + [CPRMAN_CLOCK_H264] = {
> + .name = "h264",
> + .int_bits = 4,
> + .frac_bits = 8,
> + FILL_CLOCK_MUX_INIT_INFO(H264, core),
> + },
> + [CPRMAN_CLOCK_ISP] = {
> + .name = "isp",
> + .int_bits = 4,
> + .frac_bits = 8,
> + FILL_CLOCK_MUX_INIT_INFO(ISP, core),
> + },
> + [CPRMAN_CLOCK_V3D] = {
> + .name = "v3d",
> + FILL_CLOCK_MUX_INIT_INFO(V3D, core),
> + },
> + [CPRMAN_CLOCK_CAM0] = {
> + .name = "cam0",
> + .int_bits = 4,
> + .frac_bits = 8,
> + FILL_CLOCK_MUX_INIT_INFO(CAM0, periph),
> + },
> + [CPRMAN_CLOCK_CAM1] = {
> + .name = "cam1",
> + .int_bits = 4,
> + .frac_bits = 8,
> + FILL_CLOCK_MUX_INIT_INFO(CAM1, periph),
> + },
> + [CPRMAN_CLOCK_CCP2] = {
> + .name = "ccp2",
> + FILL_CLOCK_MUX_INIT_INFO(CCP2, unknown),
> + },
> + [CPRMAN_CLOCK_DSI0E] = {
> + .name = "dsi0e",
> + .int_bits = 4,
> + .frac_bits = 8,
> + FILL_CLOCK_MUX_INIT_INFO(DSI0E, dsi0),
> + },
> + [CPRMAN_CLOCK_DSI0P] = {
> + .name = "dsi0p",
> + .int_bits = 0,
> + .frac_bits = 0,
> + FILL_CLOCK_MUX_INIT_INFO(DSI0P, dsi0),
> + },
> + [CPRMAN_CLOCK_DPI] = {
> + .name = "dpi",
> + .int_bits = 4,
> + .frac_bits = 8,
> + FILL_CLOCK_MUX_INIT_INFO(DPI, periph),
> + },
> + [CPRMAN_CLOCK_GP0] = {
> + .name = "gp0",
> + .int_bits = 12,
> + .frac_bits = 12,
> + FILL_CLOCK_MUX_INIT_INFO(GP0, periph),
> + },
> + [CPRMAN_CLOCK_GP1] = {
> + .name = "gp1",
> + .int_bits = 12,
> + .frac_bits = 12,
> + FILL_CLOCK_MUX_INIT_INFO(GP1, periph),
> + },
> + [CPRMAN_CLOCK_GP2] = {
> + .name = "gp2",
> + .int_bits = 12,
> + .frac_bits = 12,
> + FILL_CLOCK_MUX_INIT_INFO(GP2, periph),
> + },
> + [CPRMAN_CLOCK_HSM] = {
> + .name = "hsm",
> + .int_bits = 4,
> + .frac_bits = 8,
> + FILL_CLOCK_MUX_INIT_INFO(HSM, periph),
> + },
> + [CPRMAN_CLOCK_OTP] = {
> + .name = "otp",
> + .int_bits = 4,
> + .frac_bits = 0,
> + FILL_CLOCK_MUX_INIT_INFO(OTP, xosc),
> + },
> + [CPRMAN_CLOCK_PCM] = {
> + .name = "pcm",
> + .int_bits = 12,
> + .frac_bits = 12,
> + FILL_CLOCK_MUX_INIT_INFO(PCM, periph),
> + },
> + [CPRMAN_CLOCK_PWM] = {
> + .name = "pwm",
> + .int_bits = 12,
> + .frac_bits = 12,
> + FILL_CLOCK_MUX_INIT_INFO(PWM, periph),
> + },
> + [CPRMAN_CLOCK_SLIM] = {
> + .name = "slim",
> + .int_bits = 12,
> + .frac_bits = 12,
> + FILL_CLOCK_MUX_INIT_INFO(SLIM, periph),
> + },
> + [CPRMAN_CLOCK_SMI] = {
> + .name = "smi",
> + .int_bits = 4,
> + .frac_bits = 8,
> + FILL_CLOCK_MUX_INIT_INFO(SMI, periph),
> + },
> + [CPRMAN_CLOCK_TEC] = {
> + .name = "tec",
> + .int_bits = 6,
> + .frac_bits = 0,
> + FILL_CLOCK_MUX_INIT_INFO(TEC, xosc),
> + },
> + [CPRMAN_CLOCK_TD0] = {
> + .name = "td0",
> + FILL_CLOCK_MUX_INIT_INFO(TD0, unknown),
> + },
> + [CPRMAN_CLOCK_TD1] = {
> + .name = "td1",
> + FILL_CLOCK_MUX_INIT_INFO(TD1, unknown),
> + },
> + [CPRMAN_CLOCK_TSENS] = {
> + .name = "tsens",
> + .int_bits = 5,
> + .frac_bits = 0,
> + FILL_CLOCK_MUX_INIT_INFO(TSENS, xosc),
> + },
> + [CPRMAN_CLOCK_TIMER] = {
> + .name = "timer",
> + .int_bits = 6,
> + .frac_bits = 12,
> + FILL_CLOCK_MUX_INIT_INFO(TIMER, xosc),
> + },
> + [CPRMAN_CLOCK_UART] = {
> + .name = "uart",
> + .int_bits = 10,
> + .frac_bits = 12,
> + FILL_CLOCK_MUX_INIT_INFO(UART, periph),
> + },
> + [CPRMAN_CLOCK_VEC] = {
> + .name = "vec",
> + .int_bits = 4,
> + .frac_bits = 0,
> + FILL_CLOCK_MUX_INIT_INFO(VEC, periph),
> + },
> + [CPRMAN_CLOCK_PULSE] = {
> + .name = "pulse",
> + FILL_CLOCK_MUX_INIT_INFO(PULSE, xosc),
> + },
> + [CPRMAN_CLOCK_SDC] = {
> + .name = "sdram",
> + .int_bits = 6,
> + .frac_bits = 0,
> + FILL_CLOCK_MUX_INIT_INFO(SDC, core),
> + },
> + [CPRMAN_CLOCK_ARM] = {
> + .name = "arm",
> + FILL_CLOCK_MUX_INIT_INFO(ARM, unknown),
> + },
> + [CPRMAN_CLOCK_AVEO] = {
> + .name = "aveo",
> + .int_bits = 4,
> + .frac_bits = 0,
> + FILL_CLOCK_MUX_INIT_INFO(AVEO, periph),
> + },
> + [CPRMAN_CLOCK_EMMC] = {
> + .name = "emmc",
> + .int_bits = 4,
> + .frac_bits = 8,
> + FILL_CLOCK_MUX_INIT_INFO(EMMC, periph),
> + },
> + [CPRMAN_CLOCK_EMMC2] = {
> + .name = "emmc2",
> + .int_bits = 4,
> + .frac_bits = 8,
> + FILL_CLOCK_MUX_INIT_INFO(EMMC2, unknown),
> + },
> +};
> +
> +#undef FILL_CLOCK_MUX_INIT_INFO
> +#undef FILL_CLOCK_MUX_SRC_MAPPING_INIT_INFO
> +#undef SRC_MAPPING_INFO_dsi1
> +#undef SRC_MAPPING_INFO_dsi0
> +#undef SRC_MAPPING_INFO_periph
> +#undef SRC_MAPPING_INFO_core
> +#undef SRC_MAPPING_INFO_xosc
> +#undef SRC_MAPPING_INFO_unknown
> +
> +static inline void set_clock_mux_init_info(BCM2835CprmanState *s,
> + CprmanClockMuxState *mux,
> + CprmanClockMux id)
> +{
> + mux->id = id;
> + mux->reg_cm = &s->regs[CLOCK_MUX_INIT_INFO[id].cm_offset];
> + mux->int_bits = CLOCK_MUX_INIT_INFO[id].int_bits;
> + mux->frac_bits = CLOCK_MUX_INIT_INFO[id].frac_bits;
> +}
> +
> #endif
> diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c
> index e644aeb2b5..8df2db0fd9 100644
> --- a/hw/misc/bcm2835_cprman.c
> +++ b/hw/misc/bcm2835_cprman.c
> @@ -36,10 +36,13 @@
> * | [mux]
> * \-->[PLL]--->[PLL channel] [mux]
> *
> * The page at https://elinux.org/The_Undocumented_Pi gives the actual clock
> * tree configuration.
> + *
> + * The CPRMAN exposes clock outputs with the name of the clock mux suffixed
> + * with "-out" (e.g. "uart-out", "h264-out", ...).
> */
>
> #include "qemu/osdep.h"
> #include "qemu/log.h"
> #include "migration/vmstate.h"
> @@ -224,10 +227,69 @@ static const TypeInfo cprman_pll_channel_info = {
> .class_init = pll_channel_class_init,
> .instance_init = pll_channel_init,
> };
>
>
> +/* clock mux */
> +
> +static void clock_mux_update(CprmanClockMuxState *mux)
> +{
> + clock_update(mux->out, 0);
> +}
> +
> +static void clock_mux_src_update(void *opaque)
> +{
> + CprmanClockMuxState **backref = opaque;
> + CprmanClockMuxState *s = *backref;
> +
> + clock_mux_update(s);
> +}
> +
> +static void clock_mux_init(Object *obj)
> +{
> + CprmanClockMuxState *s = CPRMAN_CLOCK_MUX(obj);
> + size_t i;
> +
> + for (i = 0; i < CPRMAN_NUM_CLOCK_MUX_SRC; i++) {
> + char *name = g_strdup_printf("srcs[%zu]", i);
> + s->backref[i] = s;
> + s->srcs[i] = qdev_init_clock_in(DEVICE(s), name,
> + clock_mux_src_update,
> + &s->backref[i]);
> + g_free(name);
> + }
> +
> + s->out = qdev_init_clock_out(DEVICE(s), "out");
> +}
> +
> +static const VMStateDescription clock_mux_vmstate = {
> + .name = TYPE_CPRMAN_CLOCK_MUX,
> + .version_id = 1,
> + .minimum_version_id = 1,
> + .fields = (VMStateField[]) {
> + VMSTATE_ARRAY_CLOCK(srcs, CprmanClockMuxState,
> + CPRMAN_NUM_CLOCK_MUX_SRC),
> + VMSTATE_END_OF_LIST()
> + }
> +};
> +
> +static void clock_mux_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> +
> + dc->vmsd = &clock_mux_vmstate;
> +}
> +
> +static const TypeInfo cprman_clock_mux_info = {
> + .name = TYPE_CPRMAN_CLOCK_MUX,
> + .parent = TYPE_DEVICE,
> + .instance_size = sizeof(CprmanClockMuxState),
> + .class_init = clock_mux_class_init,
> + .instance_init = clock_mux_init,
> +};
> +
> +
> /* CPRMAN "top level" model */
>
> static uint32_t get_cm_lock(const BCM2835CprmanState *s)
> {
> static const int CM_LOCK_MAPPING[] = {
> @@ -291,10 +353,23 @@ static inline void
> update_channel_from_a2w(BCM2835CprmanState *s, size_t idx)
> return;
> }
> }
> }
>
> +static inline void update_mux_from_cm(BCM2835CprmanState *s, size_t idx)
> +{
> + size_t i;
> +
> + for (i = 0; i < CPRMAN_NUM_CLOCK_MUX; i++) {
> + if ((CLOCK_MUX_INIT_INFO[i].cm_offset == idx)
> + || (CLOCK_MUX_INIT_INFO[i].cm_offset == idx + 4)) {
Indent by one ;)
> + clock_mux_update(&s->clock_muxes[i]);
> + return;
> + }
> + }
> +}
> +
> #define CASE_PLL_A2W_REGS(pll_) \
> case R_A2W_ ## pll_ ## _CTRL: \
> case R_A2W_ ## pll_ ## _ANA0: \
> case R_A2W_ ## pll_ ## _ANA1: \
> case R_A2W_ ## pll_ ## _ANA2: \
> @@ -363,10 +438,19 @@ static void cprman_write(void *opaque, hwaddr offset,
> case R_A2W_PLLH_RCAL:
> case R_A2W_PLLH_PIX:
> case R_A2W_PLLB_ARM:
> update_channel_from_a2w(s, idx);
> break;
> +
> + case R_CM_GNRICCTL ... R_CM_SMIDIV:
> + case R_CM_TCNTCNT ... R_CM_VECDIV:
> + case R_CM_PULSECTL ... R_CM_PULSEDIV:
> + case R_CM_SDCCTL ... R_CM_ARMCTL:
> + case R_CM_AVEOCTL ... R_CM_EMMCDIV:
> + case R_CM_EMMC2CTL ... R_CM_EMMC2DIV:
> + update_mux_from_cm(s, idx);
> + break;
> }
> }
>
> #undef CASE_PLL_A2W_REGS
>
> @@ -394,10 +478,14 @@ static void cprman_reset(DeviceState *dev)
>
> for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
> device_cold_reset(DEVICE(&s->channels[i]));
> }
>
> + for (i = 0; i < CPRMAN_NUM_CLOCK_MUX; i++) {
> + device_cold_reset(DEVICE(&s->clock_muxes[i]));
> + }
> +
> clock_update_hz(s->xosc, s->xosc_freq);
> }
>
> static Clock *init_internal_clock(BCM2835CprmanState *s,
> const char *name)
> @@ -431,17 +519,69 @@ static void cprman_init(Object *obj)
> &s->channels[i],
> TYPE_CPRMAN_PLL_CHANNEL);
> set_pll_channel_init_info(s, &s->channels[i], i);
> }
>
> + for (i = 0; i < CPRMAN_NUM_CLOCK_MUX; i++) {
> + char *alias;
> +
> + object_initialize_child(obj, CLOCK_MUX_INIT_INFO[i].name,
> + &s->clock_muxes[i],
> + TYPE_CPRMAN_CLOCK_MUX);
> + set_clock_mux_init_info(s, &s->clock_muxes[i], i);
> +
> + /* Expose muxes output as CPRMAN outputs */
> + alias = g_strdup_printf("%s-out", CLOCK_MUX_INIT_INFO[i].name);
> + qdev_alias_clock(DEVICE(&s->clock_muxes[i]), "out", DEVICE(obj),
> alias);
> + g_free(alias);
> +
NL.
> + }
> +
> s->xosc = init_internal_clock(s, "xosc");
> + s->gnd = init_internal_clock(s, "gnd");
> +
> + clock_set(s->gnd, 0);
>
> memory_region_init_io(&s->iomem, obj, &cprman_ops,
> s, "bcm2835-cprman", 0x2000);
> sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
> }
>
> +static void connect_mux_sources(BCM2835CprmanState *s,
> + CprmanClockMuxState *mux,
> + const CprmanPLLChannel *clk_mapping)
> +{
> + size_t i;
> + Clock *td0 = s->clock_muxes[CPRMAN_CLOCK_TD0].out;
> + Clock *td1 = s->clock_muxes[CPRMAN_CLOCK_TD1].out;
> +
> + /* For sources from 0 to 3. Source 4 to 9 are mux specific */
> + Clock * const CLK_SRC_MAPPING[] = {
> + [CPRMAN_CLOCK_SRC_GND] = s->gnd,
> + [CPRMAN_CLOCK_SRC_XOSC] = s->xosc,
> + [CPRMAN_CLOCK_SRC_TD0] = td0,
> + [CPRMAN_CLOCK_SRC_TD1] = td1,
> + };
> +
> + for (i = 0; i < CPRMAN_NUM_CLOCK_MUX_SRC; i++) {
> + CprmanPLLChannel mapping = clk_mapping[i];
> + Clock *src;
> +
> + if (mapping == CPRMAN_CLOCK_SRC_FORCE_GROUND) {
> + src = s->gnd;
> + } else if (mapping == CPRMAN_CLOCK_SRC_DSI0HSCK) {
> + src = s->gnd; /* TODO */
> + } else if (i < CPRMAN_CLOCK_SRC_PLLA) {
> + src = CLK_SRC_MAPPING[i];
> + } else {
> + src = s->channels[mapping].out;
> + }
> +
> + clock_set_source(mux->srcs[i], src);
> + }
> +}
> +
> static void cprman_realize(DeviceState *dev, Error **errp)
> {
> BCM2835CprmanState *s = CPRMAN(dev);
> size_t i;
>
> @@ -464,10 +604,20 @@ static void cprman_realize(DeviceState *dev, Error
> **errp)
>
> if (!qdev_realize(DEVICE(channel), NULL, errp)) {
> return;
> }
> }
> +
> + for (i = 0; i < CPRMAN_NUM_CLOCK_MUX; i++) {
> + CprmanClockMuxState *clock_mux = &s->clock_muxes[i];
> +
> + connect_mux_sources(s, clock_mux,
> CLOCK_MUX_INIT_INFO[i].src_mapping);
> +
> + if (!qdev_realize(DEVICE(clock_mux), NULL, errp)) {
> + return;
> + }
> + }
> }
>
> static const VMStateDescription cprman_vmstate = {
> .name = TYPE_BCM2835_CPRMAN,
> .version_id = 1,
> @@ -504,8 +654,9 @@ static const TypeInfo cprman_info = {
> static void cprman_register_types(void)
> {
> type_register_static(&cprman_info);
> type_register_static(&cprman_pll_info);
> type_register_static(&cprman_pll_channel_info);
> + type_register_static(&cprman_clock_mux_info);
> }
>
> type_init(cprman_register_types);
>
Few comments, nice work!
Regards,
Phil.
- Re: [PATCH 09/14] hw/misc/bcm2835_cprman: add a clock mux skeleton implementation,
Philippe Mathieu-Daudé <=