qemu-arm
[Top][All Lists]
Advanced

[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.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]