All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/14] raspi: add the bcm2835 cprman clock manager
@ 2020-09-25 10:17 Luc Michel
  2020-09-25 10:17 ` [PATCH 01/14] hw/core/clock: provide the VMSTATE_ARRAY_CLOCK macro Luc Michel
                   ` (15 more replies)
  0 siblings, 16 replies; 41+ messages in thread
From: Luc Michel @ 2020-09-25 10:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, qemu-arm, Luc Michel, Philippe Mathieu-Daudé,
	Andrew Baumann

Hi,

This series add the BCM2835 cprman clock manager peripheral to the
Raspberry Pi machine.

Patches 1-3 are preliminary changes, patches 4-12 are the actual
implementation.

The two last patches add a clock input to the PL011 and
connect it to the cprman and are RFC.

This series has been tested with Linux 5.4.61 (the current raspios
version). It fixes the kernel Oops at boot time due to invalid UART
clock value, and other warnings/errors here and there because of bad
clocks or lack of cprman.

Here is the clock tree as seen by Linux when booted in QEMU:
(/sys/kernel/debug/clk/clk_summary with some columns removed)

                        enable  prepare              
   clock                 count    count          rate
-----------------------------------------------------
 otg                         0        0     480000000
 osc                         5        5      19200000
    gp2                      1        1         32768
    tsens                    0        0       1920000
    otp                      0        0       4800000
    timer                    0        0       1000002
    pllh                     4        4     864000000
       pllh_pix_prediv       1        1       3375000
          pllh_pix           0        0        337500
       pllh_aux              1        1     216000000
          vec                0        0     108000000
       pllh_rcal_prediv      1        1       3375000
          pllh_rcal          0        0        337500
    plld                     3        3    2000000024
       plld_dsi1             0        0       7812501
       plld_dsi0             0        0       7812501
       plld_per              3        3     500000006
          gp1                1        1      25000000
          uart               1        2      47999625
       plld_core             2        2     500000006
          sdram              0        0     166666668
    pllc                     3        3    2400000000
       pllc_per              1        1    1200000000
          emmc               0        0     200000000
       pllc_core2            0        0       9375000
       pllc_core1            0        0       9375000
       pllc_core0            2        2    1200000000
          vpu                1        1     700000000
             aux_spi2        0        0     700000000
             aux_spi1        0        0     700000000
             aux_uart        0        0     700000000
             peri_image      0        0     700000000
    plla                     2        2    2250000000
       plla_ccp2             0        0       8789063
       plla_dsi0             0        0       8789063
       plla_core             1        1     750000000
          h264               0        0     250000000
          isp                0        0     250000000
 dsi1p                       0        0             0
 dsi0p                       0        0             0
 dsi1e                       0        0             0
 dsi0e                       0        0             0
 cam1                        0        0             0
 cam0                        0        0             0
 dpi                         0        0             0
 tec                         0        0             0
 smi                         0        0             0
 slim                        0        0             0
 gp0                         0        0             0
 dft                         0        0             0
 aveo                        0        0             0
 pcm                         0        0             0
 pwm                         0        0             0
 hsm                         0        0             0

It shows small differences with real hardware due other missing
peripherals for which the driver turn the clock off (like tsens).

Luc Michel (14):
  hw/core/clock: provide the VMSTATE_ARRAY_CLOCK macro
  hw/core/clock: trace clock values in Hz instead of ns
  hw/arm/raspi: fix cprman base address
  hw/arm/raspi: add a skeleton implementation of the cprman
  hw/misc/bcm2835_cprman: add a PLL skeleton implementation
  hw/misc/bcm2835_cprman: implement PLLs behaviour
  hw/misc/bcm2835_cprman: add a PLL channel skeleton implementation
  hw/misc/bcm2835_cprman: implement PLL channels behaviour
  hw/misc/bcm2835_cprman: add a clock mux skeleton implementation
  hw/misc/bcm2835_cprman: implement clock mux behaviour
  hw/misc/bcm2835_cprman: add the DSI0HSCK multiplexer
  hw/misc/bcm2835_cprman: add sane reset values to the registers
  hw/char/pl011: add a clock input
  hw/arm/bcm2835_peripherals: connect the UART clock

 include/hw/arm/bcm2835_peripherals.h       |    5 +-
 include/hw/arm/raspi_platform.h            |    5 +-
 include/hw/char/pl011.h                    |    1 +
 include/hw/clock.h                         |    5 +
 include/hw/misc/bcm2835_cprman.h           |  209 ++++
 include/hw/misc/bcm2835_cprman_internals.h | 1018 ++++++++++++++++++++
 hw/arm/bcm2835_peripherals.c               |   15 +-
 hw/char/pl011.c                            |   45 +
 hw/core/clock.c                            |    6 +-
 hw/misc/bcm2835_cprman.c                   |  810 ++++++++++++++++
 hw/char/trace-events                       |    1 +
 hw/core/trace-events                       |    4 +-
 hw/misc/meson.build                        |    1 +
 hw/misc/trace-events                       |    5 +
 14 files changed, 2118 insertions(+), 12 deletions(-)
 create mode 100644 include/hw/misc/bcm2835_cprman.h
 create mode 100644 include/hw/misc/bcm2835_cprman_internals.h
 create mode 100644 hw/misc/bcm2835_cprman.c

-- 
2.28.0



^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 01/14] hw/core/clock: provide the VMSTATE_ARRAY_CLOCK macro
  2020-09-25 10:17 [PATCH 00/14] raspi: add the bcm2835 cprman clock manager Luc Michel
@ 2020-09-25 10:17 ` Luc Michel
  2020-09-26 20:36   ` Philippe Mathieu-Daudé
  2020-09-28  8:38   ` Damien Hedde
  2020-09-25 10:17 ` [PATCH 02/14] hw/core/clock: trace clock values in Hz instead of ns Luc Michel
                   ` (14 subsequent siblings)
  15 siblings, 2 replies; 41+ messages in thread
From: Luc Michel @ 2020-09-25 10:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Damien Hedde, Peter Maydell, Luc Michel,
	Philippe Mathieu-Daudé,
	Andrew Baumann, qemu-arm

Signed-off-by: Luc Michel <luc@lmichel.fr>
---
 include/hw/clock.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/include/hw/clock.h b/include/hw/clock.h
index d357594df9..c93e6113cd 100644
--- a/include/hw/clock.h
+++ b/include/hw/clock.h
@@ -79,10 +79,15 @@ struct Clock {
 extern const VMStateDescription vmstate_clock;
 #define VMSTATE_CLOCK(field, state) \
     VMSTATE_CLOCK_V(field, state, 0)
 #define VMSTATE_CLOCK_V(field, state, version) \
     VMSTATE_STRUCT_POINTER_V(field, state, version, vmstate_clock, Clock)
+#define VMSTATE_ARRAY_CLOCK(field, state, num) \
+    VMSTATE_ARRAY_CLOCK_V(field, state, num, 0)
+#define VMSTATE_ARRAY_CLOCK_V(field, state, num, version)          \
+    VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(field, state, num, version, \
+                                       vmstate_clock, Clock)
 
 /**
  * clock_setup_canonical_path:
  * @clk: clock
  *
-- 
2.28.0



^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 02/14] hw/core/clock: trace clock values in Hz instead of ns
  2020-09-25 10:17 [PATCH 00/14] raspi: add the bcm2835 cprman clock manager Luc Michel
  2020-09-25 10:17 ` [PATCH 01/14] hw/core/clock: provide the VMSTATE_ARRAY_CLOCK macro Luc Michel
@ 2020-09-25 10:17 ` Luc Michel
  2020-09-26 20:36   ` Philippe Mathieu-Daudé
  2020-09-25 10:17 ` [PATCH 03/14] hw/arm/raspi: fix cprman base address Luc Michel
                   ` (13 subsequent siblings)
  15 siblings, 1 reply; 41+ messages in thread
From: Luc Michel @ 2020-09-25 10:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Damien Hedde, Peter Maydell, Luc Michel,
	Philippe Mathieu-Daudé,
	Andrew Baumann, qemu-arm

The nanosecond unit greatly limits the dynamic range we can display in
clock value traces, for values in the order of 1GHz and more. The
internal representation can go way beyond this value and it is quite
common for today's clocks to be within those ranges.

For example, a frequency between 500MHz+ and 1GHz will be displayed as
1ns. Beyond 1GHz, it will show up as 0ns.

Replace nanosecond periods traces with frequencies in the Hz unit
to have more dynamic range in the trace output.

Signed-off-by: Luc Michel <luc@lmichel.fr>
---
 hw/core/clock.c      | 6 +++---
 hw/core/trace-events | 4 ++--
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/hw/core/clock.c b/hw/core/clock.c
index 7066282f7b..81184734e0 100644
--- a/hw/core/clock.c
+++ b/hw/core/clock.c
@@ -37,12 +37,12 @@ void clock_clear_callback(Clock *clk)
 bool clock_set(Clock *clk, uint64_t period)
 {
     if (clk->period == period) {
         return false;
     }
-    trace_clock_set(CLOCK_PATH(clk), CLOCK_PERIOD_TO_NS(clk->period),
-                    CLOCK_PERIOD_TO_NS(period));
+    trace_clock_set(CLOCK_PATH(clk), CLOCK_PERIOD_TO_HZ(clk->period),
+                    CLOCK_PERIOD_TO_HZ(period));
     clk->period = period;
 
     return true;
 }
 
@@ -52,11 +52,11 @@ static void clock_propagate_period(Clock *clk, bool call_callbacks)
 
     QLIST_FOREACH(child, &clk->children, sibling) {
         if (child->period != clk->period) {
             child->period = clk->period;
             trace_clock_update(CLOCK_PATH(child), CLOCK_PATH(clk),
-                               CLOCK_PERIOD_TO_NS(clk->period),
+                               CLOCK_PERIOD_TO_HZ(clk->period),
                                call_callbacks);
             if (call_callbacks && child->callback) {
                 child->callback(child->callback_opaque);
             }
             clock_propagate_period(child, call_callbacks);
diff --git a/hw/core/trace-events b/hw/core/trace-events
index 1ac60ede6b..6f96d8bfd0 100644
--- a/hw/core/trace-events
+++ b/hw/core/trace-events
@@ -29,8 +29,8 @@ resettable_phase_exit_end(void *obj, const char *objtype, unsigned count) "obj=%
 resettable_transitional_function(void *obj, const char *objtype) "obj=%p(%s)"
 
 # clock.c
 clock_set_source(const char *clk, const char *src) "'%s', src='%s'"
 clock_disconnect(const char *clk) "'%s'"
-clock_set(const char *clk, uint64_t old, uint64_t new) "'%s', ns=%"PRIu64"->%"PRIu64
+clock_set(const char *clk, uint64_t old, uint64_t new) "'%s', %"PRIu64"hz->%"PRIu64"hz"
 clock_propagate(const char *clk) "'%s'"
-clock_update(const char *clk, const char *src, uint64_t val, int cb) "'%s', src='%s', ns=%"PRIu64", cb=%d"
+clock_update(const char *clk, const char *src, uint64_t hz, int cb) "'%s', src='%s', val=%"PRIu64"hz cb=%d"
-- 
2.28.0



^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 03/14] hw/arm/raspi: fix cprman base address
  2020-09-25 10:17 [PATCH 00/14] raspi: add the bcm2835 cprman clock manager Luc Michel
  2020-09-25 10:17 ` [PATCH 01/14] hw/core/clock: provide the VMSTATE_ARRAY_CLOCK macro Luc Michel
  2020-09-25 10:17 ` [PATCH 02/14] hw/core/clock: trace clock values in Hz instead of ns Luc Michel
@ 2020-09-25 10:17 ` Luc Michel
  2020-09-26 21:04   ` Philippe Mathieu-Daudé
  2020-09-25 10:17 ` [PATCH 04/14] hw/arm/raspi: add a skeleton implementation of the cprman Luc Michel
                   ` (12 subsequent siblings)
  15 siblings, 1 reply; 41+ messages in thread
From: Luc Michel @ 2020-09-25 10:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, qemu-arm, Luc Michel, Philippe Mathieu-Daudé,
	Andrew Baumann

The cprman (clock controller) was mapped at the watchdog/power manager
address. It was also split into two unimplemented peripherals (cm and
a2w) but this is really the same one, as shown by this extract of the
Raspberry Pi 3 Linux device tree:

    watchdog@7e100000 {
            compatible = "brcm,bcm2835-pm\0brcm,bcm2835-pm-wdt";
            [...]
            reg = <0x7e100000 0x114 0x7e00a000 0x24>;
            [...]
    };

    [...]
    cprman@7e101000 {
            compatible = "brcm,bcm2835-cprman";
            [...]
            reg = <0x7e101000 0x2000>;
            [...]
    };

Signed-off-by: Luc Michel <luc@lmichel.fr>
---
 include/hw/arm/bcm2835_peripherals.h | 2 +-
 include/hw/arm/raspi_platform.h      | 5 ++---
 hw/arm/bcm2835_peripherals.c         | 4 ++--
 3 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h
index 13d7c4c553..199088425a 100644
--- a/include/hw/arm/bcm2835_peripherals.h
+++ b/include/hw/arm/bcm2835_peripherals.h
@@ -44,12 +44,12 @@ struct BCM2835PeripheralState {
     qemu_irq irq, fiq;
 
     BCM2835SystemTimerState systmr;
     BCM2835MphiState mphi;
     UnimplementedDeviceState armtmr;
+    UnimplementedDeviceState powermgt;
     UnimplementedDeviceState cprman;
-    UnimplementedDeviceState a2w;
     PL011State uart0;
     BCM2835AuxState aux;
     BCM2835FBState fb;
     BCM2835DMAState dma;
     BCM2835ICState ic;
diff --git a/include/hw/arm/raspi_platform.h b/include/hw/arm/raspi_platform.h
index 61b04a1bd4..51a477cdc2 100644
--- a/include/hw/arm/raspi_platform.h
+++ b/include/hw/arm/raspi_platform.h
@@ -34,13 +34,12 @@
 #define ARMCTRL_OFFSET          (ARM_OFFSET + 0x000)
 #define ARMCTRL_IC_OFFSET       (ARM_OFFSET + 0x200) /* Interrupt controller */
 #define ARMCTRL_TIMER0_1_OFFSET (ARM_OFFSET + 0x400) /* Timer 0 and 1 */
 #define ARMCTRL_0_SBM_OFFSET    (ARM_OFFSET + 0x800) /* User 0 (ARM) Semaphores
                                                       * Doorbells & Mailboxes */
-#define CPRMAN_OFFSET           0x100000 /* Power Management, Watchdog */
-#define CM_OFFSET               0x101000 /* Clock Management */
-#define A2W_OFFSET              0x102000 /* Reset controller */
+#define PM_OFFSET               0x100000 /* Power Management */
+#define CPRMAN_OFFSET           0x101000 /* Clock Management */
 #define AVS_OFFSET              0x103000 /* Audio Video Standard */
 #define RNG_OFFSET              0x104000
 #define GPIO_OFFSET             0x200000
 #define UART0_OFFSET            0x201000
 #define MMCI0_OFFSET            0x202000
diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c
index a9d7f53f6e..f0802c91e0 100644
--- a/hw/arm/bcm2835_peripherals.c
+++ b/hw/arm/bcm2835_peripherals.c
@@ -342,12 +342,12 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
     sysbus_connect_irq(SYS_BUS_DEVICE(&s->dwc2), 0,
         qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
                                INTERRUPT_USB));
 
     create_unimp(s, &s->armtmr, "bcm2835-sp804", ARMCTRL_TIMER0_1_OFFSET, 0x40);
-    create_unimp(s, &s->cprman, "bcm2835-cprman", CPRMAN_OFFSET, 0x1000);
-    create_unimp(s, &s->a2w, "bcm2835-a2w", A2W_OFFSET, 0x1000);
+    create_unimp(s, &s->powermgt, "bcm2835-powermgt", PM_OFFSET, 0x114);
+    create_unimp(s, &s->cprman, "bcm2835-cprman", CPRMAN_OFFSET, 0x2000);
     create_unimp(s, &s->i2s, "bcm2835-i2s", I2S_OFFSET, 0x100);
     create_unimp(s, &s->smi, "bcm2835-smi", SMI_OFFSET, 0x100);
     create_unimp(s, &s->spi[0], "bcm2835-spi0", SPI0_OFFSET, 0x20);
     create_unimp(s, &s->bscsl, "bcm2835-spis", BSC_SL_OFFSET, 0x100);
     create_unimp(s, &s->i2c[0], "bcm2835-i2c0", BSC0_OFFSET, 0x20);
-- 
2.28.0



^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 04/14] hw/arm/raspi: add a skeleton implementation of the cprman
  2020-09-25 10:17 [PATCH 00/14] raspi: add the bcm2835 cprman clock manager Luc Michel
                   ` (2 preceding siblings ...)
  2020-09-25 10:17 ` [PATCH 03/14] hw/arm/raspi: fix cprman base address Luc Michel
@ 2020-09-25 10:17 ` Luc Michel
  2020-09-26 21:05   ` Philippe Mathieu-Daudé
  2020-09-25 10:17 ` [PATCH 05/14] hw/misc/bcm2835_cprman: add a PLL skeleton implementation Luc Michel
                   ` (11 subsequent siblings)
  15 siblings, 1 reply; 41+ messages in thread
From: Luc Michel @ 2020-09-25 10:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, qemu-arm, Luc Michel, Philippe Mathieu-Daudé,
	Andrew Baumann

The BCM2835 cprman is the clock manager of the SoC. It is composed of a
main oscillator, and several sub-components (PLLs, multiplexers, ...) to
generate the BCM2835 clock tree.

This commit adds a skeleton of the cprman, with a dummy register
read/write implementation. It embeds the main oscillator (xosc) from
which all the clocks will be derived.

Signed-off-by: Luc Michel <luc@lmichel.fr>
---
 include/hw/arm/bcm2835_peripherals.h       |   3 +-
 include/hw/misc/bcm2835_cprman.h           |  37 +++++
 include/hw/misc/bcm2835_cprman_internals.h |  24 +++
 hw/arm/bcm2835_peripherals.c               |  11 +-
 hw/misc/bcm2835_cprman.c                   | 171 +++++++++++++++++++++
 hw/misc/meson.build                        |   1 +
 hw/misc/trace-events                       |   5 +
 7 files changed, 250 insertions(+), 2 deletions(-)
 create mode 100644 include/hw/misc/bcm2835_cprman.h
 create mode 100644 include/hw/misc/bcm2835_cprman_internals.h
 create mode 100644 hw/misc/bcm2835_cprman.c

diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h
index 199088425a..002bb5e73b 100644
--- a/include/hw/arm/bcm2835_peripherals.h
+++ b/include/hw/arm/bcm2835_peripherals.h
@@ -21,10 +21,11 @@
 #include "hw/misc/bcm2835_property.h"
 #include "hw/misc/bcm2835_rng.h"
 #include "hw/misc/bcm2835_mbox.h"
 #include "hw/misc/bcm2835_mphi.h"
 #include "hw/misc/bcm2835_thermal.h"
+#include "hw/misc/bcm2835_cprman.h"
 #include "hw/sd/sdhci.h"
 #include "hw/sd/bcm2835_sdhost.h"
 #include "hw/gpio/bcm2835_gpio.h"
 #include "hw/timer/bcm2835_systmr.h"
 #include "hw/usb/hcd-dwc2.h"
@@ -45,11 +46,11 @@ struct BCM2835PeripheralState {
 
     BCM2835SystemTimerState systmr;
     BCM2835MphiState mphi;
     UnimplementedDeviceState armtmr;
     UnimplementedDeviceState powermgt;
-    UnimplementedDeviceState cprman;
+    BCM2835CprmanState cprman;
     PL011State uart0;
     BCM2835AuxState aux;
     BCM2835FBState fb;
     BCM2835DMAState dma;
     BCM2835ICState ic;
diff --git a/include/hw/misc/bcm2835_cprman.h b/include/hw/misc/bcm2835_cprman.h
new file mode 100644
index 0000000000..de9bd01b23
--- /dev/null
+++ b/include/hw/misc/bcm2835_cprman.h
@@ -0,0 +1,37 @@
+/*
+ * BCM2835 cprman clock manager
+ *
+ * Copyright (c) 2020 Luc Michel <luc@lmichel.fr>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_MISC_CPRMAN_H
+#define HW_MISC_CPRMAN_H
+
+#include "hw/sysbus.h"
+#include "hw/qdev-clock.h"
+
+#define TYPE_BCM2835_CPRMAN "bcm2835-cprman"
+
+typedef struct BCM2835CprmanState BCM2835CprmanState;
+
+DECLARE_INSTANCE_CHECKER(BCM2835CprmanState, CPRMAN,
+                         TYPE_BCM2835_CPRMAN)
+
+#define CPRMAN_NUM_REGS (0x2000 / sizeof(uint32_t))
+
+struct BCM2835CprmanState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    MemoryRegion iomem;
+
+    uint32_t regs[CPRMAN_NUM_REGS];
+    uint32_t xosc_freq;
+
+    Clock *xosc;
+};
+
+#endif
diff --git a/include/hw/misc/bcm2835_cprman_internals.h b/include/hw/misc/bcm2835_cprman_internals.h
new file mode 100644
index 0000000000..6a10b60930
--- /dev/null
+++ b/include/hw/misc/bcm2835_cprman_internals.h
@@ -0,0 +1,24 @@
+/*
+ * BCM2835 cprman clock manager
+ *
+ * Copyright (c) 2020 Luc Michel <luc@lmichel.fr>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_MISC_CPRMAN_INTERNALS_H
+#define HW_MISC_CPRMAN_INTERNALS_H
+
+#include "hw/registerfields.h"
+#include "hw/misc/bcm2835_cprman.h"
+
+/* Register map */
+
+/*
+ * This field is common to all registers. Each register write value must match
+ * the CPRMAN_PASSWORD magic value in its 8 MSB.
+ */
+FIELD(CPRMAN, PASSWORD, 24, 8)
+#define CPRMAN_PASSWORD 0x5a
+
+#endif
diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c
index f0802c91e0..958aadeeb9 100644
--- a/hw/arm/bcm2835_peripherals.c
+++ b/hw/arm/bcm2835_peripherals.c
@@ -119,10 +119,13 @@ static void bcm2835_peripherals_init(Object *obj)
     object_initialize_child(obj, "mphi", &s->mphi, TYPE_BCM2835_MPHI);
 
     /* DWC2 */
     object_initialize_child(obj, "dwc2", &s->dwc2, TYPE_DWC2_USB);
 
+    /* CPRMAN clock manager */
+    object_initialize_child(obj, "cprman", &s->cprman, TYPE_BCM2835_CPRMAN);
+
     object_property_add_const_link(OBJECT(&s->dwc2), "dma-mr",
                                    OBJECT(&s->gpu_bus_mr));
 }
 
 static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
@@ -158,10 +161,17 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
     /* Interrupt Controller */
     if (!sysbus_realize(SYS_BUS_DEVICE(&s->ic), errp)) {
         return;
     }
 
+    /* CPRMAN clock manager */
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->cprman), errp)) {
+        return;
+    }
+    memory_region_add_subregion(&s->peri_mr, CPRMAN_OFFSET,
+                sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->cprman), 0));
+
     memory_region_add_subregion(&s->peri_mr, ARMCTRL_IC_OFFSET,
                 sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->ic), 0));
     sysbus_pass_irq(SYS_BUS_DEVICE(s), SYS_BUS_DEVICE(&s->ic));
 
     /* Sys Timer */
@@ -343,11 +353,10 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
         qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
                                INTERRUPT_USB));
 
     create_unimp(s, &s->armtmr, "bcm2835-sp804", ARMCTRL_TIMER0_1_OFFSET, 0x40);
     create_unimp(s, &s->powermgt, "bcm2835-powermgt", PM_OFFSET, 0x114);
-    create_unimp(s, &s->cprman, "bcm2835-cprman", CPRMAN_OFFSET, 0x2000);
     create_unimp(s, &s->i2s, "bcm2835-i2s", I2S_OFFSET, 0x100);
     create_unimp(s, &s->smi, "bcm2835-smi", SMI_OFFSET, 0x100);
     create_unimp(s, &s->spi[0], "bcm2835-spi0", SPI0_OFFSET, 0x20);
     create_unimp(s, &s->bscsl, "bcm2835-spis", BSC_SL_OFFSET, 0x100);
     create_unimp(s, &s->i2c[0], "bcm2835-i2c0", BSC0_OFFSET, 0x20);
diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c
new file mode 100644
index 0000000000..d2ea0c9236
--- /dev/null
+++ b/hw/misc/bcm2835_cprman.c
@@ -0,0 +1,171 @@
+/*
+ * BCM2835 cprman clock manager
+ *
+ * Copyright (c) 2020 Luc Michel <luc@lmichel.fr>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/*
+ * This peripheral is roughly divided into 3 main parts:
+ *   - the PLLs
+ *   - the PLL channels
+ *   - the clock muxes
+ *
+ * A main oscillator (xosc) feeds all the PLLs. Each PLLs has one or more
+ * channels. Those channel are then connected to the clock muxes. Each mux has
+ * multiples sources (usually the xosc, some of the PLL channels and some "test
+ * debug" clocks). It can selects one or the other through a control register.
+ * Each mux has one output clock that also goes out of the CPRMAN. It usually
+ * connects to another peripheral in the SoC (so a given mux is dedicated to a
+ * peripheral).
+ *
+ * At each level (PLL, channel and mux), the clock can be altered through
+ * dividers (and multipliers in case of the PLLs), and can be disabled (in this
+ * case, the next levels see no clock).
+ *
+ * This can be sum-up as follows (this is an example and not the actual BCM2835
+ * clock tree):
+ *
+ *          /-->[PLL]-|->[PLL channel]--...            [mux]--> to peripherals
+ *          |         |->[PLL channel]  muxes takes    [mux]
+ *          |         \->[PLL channel]  inputs from    [mux]
+ *          |                           some channels  [mux]
+ * [xosc]---|-->[PLL]-|->[PLL channel]  and other srcs [mux]
+ *          |         \->[PLL channel]           ...-->[mux]
+ *          |                                          [mux]
+ *          \-->[PLL]--->[PLL channel]                 [mux]
+ *
+ * The page at https://elinux.org/The_Undocumented_Pi gives the actual clock
+ * tree configuration.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "migration/vmstate.h"
+#include "hw/qdev-properties.h"
+#include "hw/misc/bcm2835_cprman.h"
+#include "hw/misc/bcm2835_cprman_internals.h"
+#include "trace.h"
+
+/* CPRMAN "top level" model */
+
+static uint64_t cprman_read(void *opaque, hwaddr offset,
+                                    unsigned size)
+{
+    BCM2835CprmanState *s = CPRMAN(opaque);
+    uint64_t r = 0;
+    size_t idx = offset / sizeof(uint32_t);
+
+    switch (idx) {
+    default:
+        r = s->regs[idx];
+    }
+
+    trace_bcm2835_cprman_read(offset, r);
+    return r;
+}
+
+static void cprman_write(void *opaque, hwaddr offset,
+                         uint64_t value, unsigned size)
+{
+    BCM2835CprmanState *s = CPRMAN(opaque);
+    size_t idx = offset / sizeof(uint32_t);
+
+    if (FIELD_EX32(value, CPRMAN, PASSWORD) != CPRMAN_PASSWORD) {
+        trace_bcm2835_cprman_write_invalid_magic(offset, value);
+        return;
+    }
+
+    value &= ~R_CPRMAN_PASSWORD_MASK;
+
+    trace_bcm2835_cprman_write(offset, value);
+    s->regs[idx] = value;
+
+}
+
+static const MemoryRegionOps cprman_ops = {
+    .read = cprman_read,
+    .write = cprman_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid      = {
+        .min_access_size        = 4,
+        .max_access_size        = 4,
+        .unaligned              = false,
+    },
+};
+
+static void cprman_reset(DeviceState *dev)
+{
+    BCM2835CprmanState *s = CPRMAN(dev);
+
+    memset(s->regs, 0, sizeof(s->regs));
+
+    clock_update_hz(s->xosc, s->xosc_freq);
+}
+
+static Clock *init_internal_clock(BCM2835CprmanState *s,
+                                  const char *name)
+{
+    Object *obj;
+    Clock *clk;
+
+    obj = object_new(TYPE_CLOCK);
+    object_property_add_child(OBJECT(s), name, obj);
+    object_unref(obj);
+
+    clk = CLOCK(obj);
+    clock_setup_canonical_path(clk);
+
+    return clk;
+}
+
+static void cprman_init(Object *obj)
+{
+    BCM2835CprmanState *s = CPRMAN(obj);
+
+    s->xosc = init_internal_clock(s, "xosc");
+
+    memory_region_init_io(&s->iomem, obj, &cprman_ops,
+                          s, "bcm2835-cprman", 0x2000);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
+}
+
+static const VMStateDescription cprman_vmstate = {
+    .name = TYPE_BCM2835_CPRMAN,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, BCM2835CprmanState, CPRMAN_NUM_REGS),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property cprman_properties[] = {
+    DEFINE_PROP_UINT32("xosc-freq", BCM2835CprmanState, xosc_freq, 19200000),
+    DEFINE_PROP_END_OF_LIST()
+};
+
+static void cprman_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->reset = cprman_reset;
+    dc->vmsd = &cprman_vmstate;
+    device_class_set_props(dc, cprman_properties);
+}
+
+static const TypeInfo cprman_info = {
+    .name = TYPE_BCM2835_CPRMAN,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(BCM2835CprmanState),
+    .class_init = cprman_class_init,
+    .instance_init = cprman_init,
+};
+
+static void cprman_register_types(void)
+{
+    type_register_static(&cprman_info);
+}
+
+type_init(cprman_register_types);
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 793d45b1dc..c94cf70e82 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -71,10 +71,11 @@ softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files(
   'bcm2835_mbox.c',
   'bcm2835_mphi.c',
   'bcm2835_property.c',
   'bcm2835_rng.c',
   'bcm2835_thermal.c',
+  'bcm2835_cprman.c',
 ))
 softmmu_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_misc.c'))
 softmmu_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq_slcr.c', 'zynq-xadc.c'))
 softmmu_ss.add(when: 'CONFIG_STM32F2XX_SYSCFG', if_true: files('stm32f2xx_syscfg.c'))
 softmmu_ss.add(when: 'CONFIG_STM32F4XX_SYSCFG', if_true: files('stm32f4xx_syscfg.c'))
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 6054f9adf3..d718a2b177 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -224,5 +224,10 @@ grlib_ahb_pnp_read(uint64_t addr, uint32_t value) "AHB PnP read addr:0x%03"PRIx6
 grlib_apb_pnp_read(uint64_t addr, uint32_t value) "APB PnP read addr:0x%03"PRIx64" data:0x%08x"
 
 # pca9552.c
 pca955x_gpio_status(const char *description, const char *buf) "%s GPIOs 0-15 [%s]"
 pca955x_gpio_change(const char *description, unsigned id, unsigned prev_state, unsigned current_state) "%s GPIO id:%u status: %u -> %u"
+
+# bcm2835_cprman.c
+bcm2835_cprman_read(uint64_t offset, uint64_t value) "offset:0x%" PRIx64 " value:0x%" PRIx64
+bcm2835_cprman_write(uint64_t offset, uint64_t value) "offset:0x%" PRIx64 " value:0x%" PRIx64
+bcm2835_cprman_write_invalid_magic(uint64_t offset, uint64_t value) "offset:0x%" PRIx64 " value:0x%" PRIx64
-- 
2.28.0



^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 05/14] hw/misc/bcm2835_cprman: add a PLL skeleton implementation
  2020-09-25 10:17 [PATCH 00/14] raspi: add the bcm2835 cprman clock manager Luc Michel
                   ` (3 preceding siblings ...)
  2020-09-25 10:17 ` [PATCH 04/14] hw/arm/raspi: add a skeleton implementation of the cprman Luc Michel
@ 2020-09-25 10:17 ` Luc Michel
  2020-09-26 21:17   ` Philippe Mathieu-Daudé
  2020-09-25 10:17 ` [PATCH 06/14] hw/misc/bcm2835_cprman: implement PLLs behaviour Luc Michel
                   ` (10 subsequent siblings)
  15 siblings, 1 reply; 41+ messages in thread
From: Luc Michel @ 2020-09-25 10:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, qemu-arm, Luc Michel, Philippe Mathieu-Daudé,
	Andrew Baumann

There is 5 PLLs in the cprman, namely PLL A, C, D, H and B. All of them
take the xosc clock as input and produce a new clock.

This commit adds a skeleton implementation for the PLLs as sub-devices
of the cprman. The PLLs are instantiated and connected internally to the
main oscillator.

Each PLL has 6 registers : cm, a2w_ctrl, a2w_ana[0,1,2,3], a2w_frac. A
write to any of them triggers a call to the (not yet implemented)
pll_update function. If the main oscillator changes frequency, an update
is also triggered.

Signed-off-by: Luc Michel <luc@lmichel.fr>
---
 include/hw/misc/bcm2835_cprman.h           |  29 +++++
 include/hw/misc/bcm2835_cprman_internals.h | 144 +++++++++++++++++++++
 hw/misc/bcm2835_cprman.c                   | 108 ++++++++++++++++
 3 files changed, 281 insertions(+)

diff --git a/include/hw/misc/bcm2835_cprman.h b/include/hw/misc/bcm2835_cprman.h
index de9bd01b23..f186ab0104 100644
--- a/include/hw/misc/bcm2835_cprman.h
+++ b/include/hw/misc/bcm2835_cprman.h
@@ -19,17 +19,46 @@ typedef struct BCM2835CprmanState BCM2835CprmanState;
 DECLARE_INSTANCE_CHECKER(BCM2835CprmanState, CPRMAN,
                          TYPE_BCM2835_CPRMAN)
 
 #define CPRMAN_NUM_REGS (0x2000 / sizeof(uint32_t))
 
+typedef enum CprmanPLL {
+    CPRMAN_PLLA = 0,
+    CPRMAN_PLLC,
+    CPRMAN_PLLD,
+    CPRMAN_PLLH,
+    CPRMAN_PLLB,
+
+    CPRMAN_NUM_PLL
+} CprmanPLL;
+
+typedef struct CprmanPLLState {
+    /*< private >*/
+    DeviceState parent_obj;
+
+    /*< public >*/
+    CprmanPLL id;
+
+    uint32_t *reg_cm;
+    uint32_t *reg_a2w_ctrl;
+    uint32_t *reg_a2w_ana;
+    uint32_t prediv_mask; /* prediv bit in ana[1] */
+    uint32_t *reg_a2w_frac;
+
+    Clock *xosc_in;
+    Clock *out;
+} CprmanPLLState;
+
 struct BCM2835CprmanState {
     /*< private >*/
     SysBusDevice parent_obj;
 
     /*< public >*/
     MemoryRegion iomem;
 
+    CprmanPLLState plls[CPRMAN_NUM_PLL];
+
     uint32_t regs[CPRMAN_NUM_REGS];
     uint32_t xosc_freq;
 
     Clock *xosc;
 };
diff --git a/include/hw/misc/bcm2835_cprman_internals.h b/include/hw/misc/bcm2835_cprman_internals.h
index 6a10b60930..5cfa849492 100644
--- a/include/hw/misc/bcm2835_cprman_internals.h
+++ b/include/hw/misc/bcm2835_cprman_internals.h
@@ -10,15 +10,159 @@
 #define HW_MISC_CPRMAN_INTERNALS_H
 
 #include "hw/registerfields.h"
 #include "hw/misc/bcm2835_cprman.h"
 
+#define TYPE_CPRMAN_PLL "bcm2835-cprman-pll"
+
+DECLARE_INSTANCE_CHECKER(CprmanPLLState, CPRMAN_PLL,
+                         TYPE_CPRMAN_PLL)
+
 /* Register map */
 
+/* PLLs */
+REG32(CM_PLLA, 0x104)
+    FIELD(CM_PLLA, LOADDSI0, 0, 1)
+    FIELD(CM_PLLA, HOLDDSI0, 1, 1)
+    FIELD(CM_PLLA, LOADCCP2, 2, 1)
+    FIELD(CM_PLLA, HOLDCCP2, 3, 1)
+    FIELD(CM_PLLA, LOADCORE, 4, 1)
+    FIELD(CM_PLLA, HOLDCORE, 5, 1)
+    FIELD(CM_PLLA, LOADPER, 6, 1)
+    FIELD(CM_PLLA, HOLDPER, 7, 1)
+    FIELD(CM_PLLx, ANARST, 8, 1)
+REG32(CM_PLLC, 0x108)
+    FIELD(CM_PLLC, LOADCORE0, 0, 1)
+    FIELD(CM_PLLC, HOLDCORE0, 1, 1)
+    FIELD(CM_PLLC, LOADCORE1, 2, 1)
+    FIELD(CM_PLLC, HOLDCORE1, 3, 1)
+    FIELD(CM_PLLC, LOADCORE2, 4, 1)
+    FIELD(CM_PLLC, HOLDCORE2, 5, 1)
+    FIELD(CM_PLLC, LOADPER, 6, 1)
+    FIELD(CM_PLLC, HOLDPER, 7, 1)
+REG32(CM_PLLD, 0x10c)
+    FIELD(CM_PLLD, LOADDSI0, 0, 1)
+    FIELD(CM_PLLD, HOLDDSI0, 1, 1)
+    FIELD(CM_PLLD, LOADDSI1, 2, 1)
+    FIELD(CM_PLLD, HOLDDSI1, 3, 1)
+    FIELD(CM_PLLD, LOADCORE, 4, 1)
+    FIELD(CM_PLLD, HOLDCORE, 5, 1)
+    FIELD(CM_PLLD, LOADPER, 6, 1)
+    FIELD(CM_PLLD, HOLDPER, 7, 1)
+REG32(CM_PLLH, 0x110)
+    FIELD(CM_PLLH, LOADPIX, 0, 1)
+    FIELD(CM_PLLH, LOADAUX, 1, 1)
+    FIELD(CM_PLLH, LOADRCAL, 2, 1)
+REG32(CM_PLLB, 0x170)
+    FIELD(CM_PLLB, LOADARM, 0, 1)
+    FIELD(CM_PLLB, HOLDARM, 1, 1)
+
+REG32(A2W_PLLA_CTRL, 0x1100)
+    FIELD(A2W_PLLx_CTRL, NDIV, 0, 10)
+    FIELD(A2W_PLLx_CTRL, PDIV, 12, 3)
+    FIELD(A2W_PLLx_CTRL, PWRDN, 16, 1)
+    FIELD(A2W_PLLx_CTRL, PRST_DISABLE, 17, 1)
+REG32(A2W_PLLC_CTRL, 0x1120)
+REG32(A2W_PLLD_CTRL, 0x1140)
+REG32(A2W_PLLH_CTRL, 0x1160)
+REG32(A2W_PLLB_CTRL, 0x11e0)
+
+REG32(A2W_PLLA_ANA0, 0x1010)
+REG32(A2W_PLLA_ANA1, 0x1014)
+    FIELD(A2W_PLLx_ANA1, FB_PREDIV, 14, 1)
+REG32(A2W_PLLA_ANA2, 0x1018)
+REG32(A2W_PLLA_ANA3, 0x101c)
+
+REG32(A2W_PLLC_ANA0, 0x1030)
+REG32(A2W_PLLC_ANA1, 0x1034)
+REG32(A2W_PLLC_ANA2, 0x1038)
+REG32(A2W_PLLC_ANA3, 0x103c)
+
+REG32(A2W_PLLD_ANA0, 0x1050)
+REG32(A2W_PLLD_ANA1, 0x1054)
+REG32(A2W_PLLD_ANA2, 0x1058)
+REG32(A2W_PLLD_ANA3, 0x105c)
+
+REG32(A2W_PLLH_ANA0, 0x1070)
+REG32(A2W_PLLH_ANA1, 0x1074)
+    FIELD(A2W_PLLH_ANA1, FB_PREDIV, 11, 1)
+REG32(A2W_PLLH_ANA2, 0x1078)
+REG32(A2W_PLLH_ANA3, 0x107c)
+
+REG32(A2W_PLLB_ANA0, 0x10f0)
+REG32(A2W_PLLB_ANA1, 0x10f4)
+REG32(A2W_PLLB_ANA2, 0x10f8)
+REG32(A2W_PLLB_ANA3, 0x10fc)
+
+REG32(A2W_PLLA_FRAC, 0x1200)
+    FIELD(A2W_PLLx_FRAC, FRAC, 0, 20)
+REG32(A2W_PLLC_FRAC, 0x1220)
+REG32(A2W_PLLD_FRAC, 0x1240)
+REG32(A2W_PLLH_FRAC, 0x1260)
+REG32(A2W_PLLB_FRAC, 0x12e0)
+
 /*
  * This field is common to all registers. Each register write value must match
  * the CPRMAN_PASSWORD magic value in its 8 MSB.
  */
 FIELD(CPRMAN, PASSWORD, 24, 8)
 #define CPRMAN_PASSWORD 0x5a
 
+/* PLL init info */
+typedef struct PLLInitInfo {
+    const char *name;
+    size_t cm_offset;
+    size_t a2w_ctrl_offset;
+    size_t a2w_ana_offset;
+    uint32_t prediv_mask; /* Prediv bit in ana[1] */
+    size_t a2w_frac_offset;
+} PLLInitInfo;
+
+#define FILL_PLL_INIT_INFO(pll_)                \
+    .cm_offset = R_CM_ ## pll_,                 \
+    .a2w_ctrl_offset = R_A2W_ ## pll_ ## _CTRL, \
+    .a2w_ana_offset = R_A2W_ ## pll_ ## _ANA0,  \
+    .a2w_frac_offset = R_A2W_ ## pll_ ## _FRAC
+
+static const PLLInitInfo PLL_INIT_INFO[] = {
+    [CPRMAN_PLLA] = {
+        .name = "plla",
+        .prediv_mask = R_A2W_PLLx_ANA1_FB_PREDIV_MASK,
+        FILL_PLL_INIT_INFO(PLLA),
+    },
+    [CPRMAN_PLLC] = {
+        .name = "pllc",
+        .prediv_mask = R_A2W_PLLx_ANA1_FB_PREDIV_MASK,
+        FILL_PLL_INIT_INFO(PLLC),
+    },
+    [CPRMAN_PLLD] = {
+        .name = "plld",
+        .prediv_mask = R_A2W_PLLx_ANA1_FB_PREDIV_MASK,
+        FILL_PLL_INIT_INFO(PLLD),
+    },
+    [CPRMAN_PLLH] = {
+        .name = "pllh",
+        .prediv_mask = R_A2W_PLLH_ANA1_FB_PREDIV_MASK,
+        FILL_PLL_INIT_INFO(PLLH),
+    },
+    [CPRMAN_PLLB] = {
+        .name = "pllb",
+        .prediv_mask = R_A2W_PLLx_ANA1_FB_PREDIV_MASK,
+        FILL_PLL_INIT_INFO(PLLB),
+    },
+};
+
+#undef FILL_PLL_CHANNEL_INIT_INFO
+
+static inline void set_pll_init_info(BCM2835CprmanState *s,
+                                     CprmanPLLState *pll,
+                                     CprmanPLL id)
+{
+    pll->id = id;
+    pll->reg_cm = &s->regs[PLL_INIT_INFO[id].cm_offset];
+    pll->reg_a2w_ctrl = &s->regs[PLL_INIT_INFO[id].a2w_ctrl_offset];
+    pll->reg_a2w_ana = &s->regs[PLL_INIT_INFO[id].a2w_ana_offset];
+    pll->prediv_mask = PLL_INIT_INFO[id].prediv_mask;
+    pll->reg_a2w_frac = &s->regs[PLL_INIT_INFO[id].a2w_frac_offset];
+}
+
 #endif
diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c
index d2ea0c9236..ad71d30a86 100644
--- a/hw/misc/bcm2835_cprman.c
+++ b/hw/misc/bcm2835_cprman.c
@@ -46,10 +46,56 @@
 #include "hw/qdev-properties.h"
 #include "hw/misc/bcm2835_cprman.h"
 #include "hw/misc/bcm2835_cprman_internals.h"
 #include "trace.h"
 
+/* PLL */
+
+static void pll_update(CprmanPLLState *pll)
+{
+    clock_update(pll->out, 0);
+}
+
+static void pll_xosc_update(void *opaque)
+{
+    pll_update(CPRMAN_PLL(opaque));
+}
+
+static void pll_init(Object *obj)
+{
+    CprmanPLLState *s = CPRMAN_PLL(obj);
+
+    s->xosc_in = qdev_init_clock_in(DEVICE(s), "xosc-in", pll_xosc_update, s);
+    s->out = qdev_init_clock_out(DEVICE(s), "out");
+}
+
+static const VMStateDescription pll_vmstate = {
+    .name = TYPE_CPRMAN_PLL,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_CLOCK(xosc_in, CprmanPLLState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void pll_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd = &pll_vmstate;
+}
+
+static const TypeInfo cprman_pll_info = {
+    .name = TYPE_CPRMAN_PLL,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(CprmanPLLState),
+    .class_init = pll_class_init,
+    .instance_init = pll_init,
+};
+
+
 /* CPRMAN "top level" model */
 
 static uint64_t cprman_read(void *opaque, hwaddr offset,
                                     unsigned size)
 {
@@ -64,10 +110,19 @@ static uint64_t cprman_read(void *opaque, hwaddr offset,
 
     trace_bcm2835_cprman_read(offset, r);
     return r;
 }
 
+#define CASE_PLL_REGS(pll_)       \
+    case R_CM_ ## pll_:           \
+    case R_A2W_ ## pll_ ## _CTRL: \
+    case R_A2W_ ## pll_ ## _ANA0: \
+    case R_A2W_ ## pll_ ## _ANA1: \
+    case R_A2W_ ## pll_ ## _ANA2: \
+    case R_A2W_ ## pll_ ## _ANA3: \
+    case R_A2W_ ## pll_ ## _FRAC
+
 static void cprman_write(void *opaque, hwaddr offset,
                          uint64_t value, unsigned size)
 {
     BCM2835CprmanState *s = CPRMAN(opaque);
     size_t idx = offset / sizeof(uint32_t);
@@ -80,12 +135,35 @@ static void cprman_write(void *opaque, hwaddr offset,
     value &= ~R_CPRMAN_PASSWORD_MASK;
 
     trace_bcm2835_cprman_write(offset, value);
     s->regs[idx] = value;
 
+    switch (idx) {
+    CASE_PLL_REGS(PLLA) :
+        pll_update(&s->plls[CPRMAN_PLLA]);
+        break;
+
+    CASE_PLL_REGS(PLLC) :
+        pll_update(&s->plls[CPRMAN_PLLC]);
+        break;
+
+    CASE_PLL_REGS(PLLD) :
+        pll_update(&s->plls[CPRMAN_PLLD]);
+        break;
+
+    CASE_PLL_REGS(PLLH) :
+        pll_update(&s->plls[CPRMAN_PLLH]);
+        break;
+
+    CASE_PLL_REGS(PLLB) :
+        pll_update(&s->plls[CPRMAN_PLLB]);
+        break;
+    }
 }
 
+#undef CASE_PLL_REGS
+
 static const MemoryRegionOps cprman_ops = {
     .read = cprman_read,
     .write = cprman_write,
     .endianness = DEVICE_LITTLE_ENDIAN,
     .valid      = {
@@ -96,13 +174,18 @@ static const MemoryRegionOps cprman_ops = {
 };
 
 static void cprman_reset(DeviceState *dev)
 {
     BCM2835CprmanState *s = CPRMAN(dev);
+    size_t i;
 
     memset(s->regs, 0, sizeof(s->regs));
 
+    for (i = 0; i < CPRMAN_NUM_PLL; i++) {
+        device_cold_reset(DEVICE(&s->plls[i]));
+    }
+
     clock_update_hz(s->xosc, s->xosc_freq);
 }
 
 static Clock *init_internal_clock(BCM2835CprmanState *s,
                                   const char *name)
@@ -121,18 +204,41 @@ static Clock *init_internal_clock(BCM2835CprmanState *s,
 }
 
 static void cprman_init(Object *obj)
 {
     BCM2835CprmanState *s = CPRMAN(obj);
+    size_t i;
+
+    for (i = 0; i < CPRMAN_NUM_PLL; i++) {
+        object_initialize_child(obj, PLL_INIT_INFO[i].name,
+                                &s->plls[i], TYPE_CPRMAN_PLL);
+        set_pll_init_info(s, &s->plls[i], i);
+    }
 
     s->xosc = init_internal_clock(s, "xosc");
 
     memory_region_init_io(&s->iomem, obj, &cprman_ops,
                           s, "bcm2835-cprman", 0x2000);
     sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
 }
 
+static void cprman_realize(DeviceState *dev, Error **errp)
+{
+    BCM2835CprmanState *s = CPRMAN(dev);
+    size_t i;
+
+    for (i = 0; i < CPRMAN_NUM_PLL; i++) {
+        CprmanPLLState *pll = &s->plls[i];
+
+        clock_set_source(pll->xosc_in, s->xosc);
+
+        if (!qdev_realize(DEVICE(pll), NULL, errp)) {
+            return;
+        }
+    }
+}
+
 static const VMStateDescription cprman_vmstate = {
     .name = TYPE_BCM2835_CPRMAN,
     .version_id = 1,
     .minimum_version_id = 1,
     .fields = (VMStateField[]) {
@@ -148,10 +254,11 @@ static Property cprman_properties[] = {
 
 static void cprman_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
 
+    dc->realize = cprman_realize;
     dc->reset = cprman_reset;
     dc->vmsd = &cprman_vmstate;
     device_class_set_props(dc, cprman_properties);
 }
 
@@ -164,8 +271,9 @@ static const TypeInfo cprman_info = {
 };
 
 static void cprman_register_types(void)
 {
     type_register_static(&cprman_info);
+    type_register_static(&cprman_pll_info);
 }
 
 type_init(cprman_register_types);
-- 
2.28.0



^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 06/14] hw/misc/bcm2835_cprman: implement PLLs behaviour
  2020-09-25 10:17 [PATCH 00/14] raspi: add the bcm2835 cprman clock manager Luc Michel
                   ` (4 preceding siblings ...)
  2020-09-25 10:17 ` [PATCH 05/14] hw/misc/bcm2835_cprman: add a PLL skeleton implementation Luc Michel
@ 2020-09-25 10:17 ` Luc Michel
  2020-09-26 21:26   ` Philippe Mathieu-Daudé
  2020-09-25 10:17 ` [PATCH 07/14] hw/misc/bcm2835_cprman: add a PLL channel skeleton implementation Luc Michel
                   ` (9 subsequent siblings)
  15 siblings, 1 reply; 41+ messages in thread
From: Luc Michel @ 2020-09-25 10:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, qemu-arm, Luc Michel, Philippe Mathieu-Daudé,
	Andrew Baumann

The cprman PLLs generate a clock based on a prescaler, a multiplier and
a divider. The prescaler doubles the parent (xosc) frequency, then the
multiplier/divider are applied. The multiplier has an integer and a
fractionnal part.

This commit also implements the cprman CM_LOCK register. This register
reports which PLL is currently locked. We consider a PLL has being
locked as soon as it is enabled (on real hardware, there is a delay
after turning a PLL on, for it to stabilise).

Signed-off-by: Luc Michel <luc@lmichel.fr>
---
 include/hw/misc/bcm2835_cprman_internals.h |  8 +++
 hw/misc/bcm2835_cprman.c                   | 64 +++++++++++++++++++++-
 2 files changed, 71 insertions(+), 1 deletion(-)

diff --git a/include/hw/misc/bcm2835_cprman_internals.h b/include/hw/misc/bcm2835_cprman_internals.h
index 5cfa849492..22a2500ab0 100644
--- a/include/hw/misc/bcm2835_cprman_internals.h
+++ b/include/hw/misc/bcm2835_cprman_internals.h
@@ -98,10 +98,18 @@ REG32(A2W_PLLA_FRAC, 0x1200)
 REG32(A2W_PLLC_FRAC, 0x1220)
 REG32(A2W_PLLD_FRAC, 0x1240)
 REG32(A2W_PLLH_FRAC, 0x1260)
 REG32(A2W_PLLB_FRAC, 0x12e0)
 
+/* misc registers */
+REG32(CM_LOCK, 0x114)
+    FIELD(CM_LOCK, FLOCKH, 12, 1)
+    FIELD(CM_LOCK, FLOCKD, 11, 1)
+    FIELD(CM_LOCK, FLOCKC, 10, 1)
+    FIELD(CM_LOCK, FLOCKB, 9, 1)
+    FIELD(CM_LOCK, FLOCKA, 8, 1)
+
 /*
  * This field is common to all registers. Each register write value must match
  * the CPRMAN_PASSWORD magic value in its 8 MSB.
  */
 FIELD(CPRMAN, PASSWORD, 24, 8)
diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c
index ad71d30a86..ba82522eb1 100644
--- a/hw/misc/bcm2835_cprman.c
+++ b/hw/misc/bcm2835_cprman.c
@@ -48,13 +48,51 @@
 #include "hw/misc/bcm2835_cprman_internals.h"
 #include "trace.h"
 
 /* PLL */
 
+static bool pll_is_locked(const CprmanPLLState *pll)
+{
+    return !FIELD_EX32(*pll->reg_a2w_ctrl, A2W_PLLx_CTRL, PWRDN)
+        && !FIELD_EX32(*pll->reg_cm, CM_PLLx, ANARST);
+}
+
 static void pll_update(CprmanPLLState *pll)
 {
-    clock_update(pll->out, 0);
+    uint64_t freq, ndiv, fdiv, pdiv;
+
+    if (!pll_is_locked(pll)) {
+        clock_update(pll->out, 0);
+        return;
+    }
+
+    pdiv = FIELD_EX32(*pll->reg_a2w_ctrl, A2W_PLLx_CTRL, PDIV);
+
+    if (!pdiv) {
+        clock_update(pll->out, 0);
+        return;
+    }
+
+    ndiv = FIELD_EX32(*pll->reg_a2w_ctrl, A2W_PLLx_CTRL, NDIV);
+    fdiv = FIELD_EX32(*pll->reg_a2w_frac, A2W_PLLx_FRAC, FRAC);
+
+    if (pll->reg_a2w_ana[1] & pll->prediv_mask) {
+        /* The prescaler doubles the parent frequency */
+        ndiv *= 2;
+        fdiv *= 2;
+    }
+
+    /*
+     * We have a multiplier with an integer part (ndiv) and a fractional part
+     * (fdiv), and a divider (pdiv).
+     */
+    freq = clock_get_hz(pll->xosc_in) *
+        ((ndiv << R_A2W_PLLx_FRAC_FRAC_LENGTH) + fdiv);
+    freq /= pdiv;
+    freq >>= R_A2W_PLLx_FRAC_FRAC_LENGTH;
+
+    clock_update_hz(pll->out, freq);
 }
 
 static void pll_xosc_update(void *opaque)
 {
     pll_update(CPRMAN_PLL(opaque));
@@ -94,18 +132,42 @@ static const TypeInfo cprman_pll_info = {
 };
 
 
 /* CPRMAN "top level" model */
 
+static uint32_t get_cm_lock(const BCM2835CprmanState *s)
+{
+    static const int CM_LOCK_MAPPING[] = {
+        [CPRMAN_PLLA] = R_CM_LOCK_FLOCKA_SHIFT,
+        [CPRMAN_PLLC] = R_CM_LOCK_FLOCKC_SHIFT,
+        [CPRMAN_PLLD] = R_CM_LOCK_FLOCKD_SHIFT,
+        [CPRMAN_PLLH] = R_CM_LOCK_FLOCKH_SHIFT,
+        [CPRMAN_PLLB] = R_CM_LOCK_FLOCKB_SHIFT,
+    };
+
+    uint32_t r = 0;
+    size_t i;
+
+    for (i = 0; i < CPRMAN_NUM_PLL; i++) {
+        r |= pll_is_locked(&s->plls[i]) << CM_LOCK_MAPPING[i];
+    }
+
+    return r;
+}
+
 static uint64_t cprman_read(void *opaque, hwaddr offset,
                                     unsigned size)
 {
     BCM2835CprmanState *s = CPRMAN(opaque);
     uint64_t r = 0;
     size_t idx = offset / sizeof(uint32_t);
 
     switch (idx) {
+    case R_CM_LOCK:
+        r = get_cm_lock(s);
+        break;
+
     default:
         r = s->regs[idx];
     }
 
     trace_bcm2835_cprman_read(offset, r);
-- 
2.28.0



^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 07/14] hw/misc/bcm2835_cprman: add a PLL channel skeleton implementation
  2020-09-25 10:17 [PATCH 00/14] raspi: add the bcm2835 cprman clock manager Luc Michel
                   ` (5 preceding siblings ...)
  2020-09-25 10:17 ` [PATCH 06/14] hw/misc/bcm2835_cprman: implement PLLs behaviour Luc Michel
@ 2020-09-25 10:17 ` Luc Michel
  2020-09-26 21:32   ` Philippe Mathieu-Daudé
  2020-09-25 10:17 ` [PATCH 08/14] hw/misc/bcm2835_cprman: implement PLL channels behaviour Luc Michel
                   ` (8 subsequent siblings)
  15 siblings, 1 reply; 41+ messages in thread
From: Luc Michel @ 2020-09-25 10:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, qemu-arm, Luc Michel, Philippe Mathieu-Daudé,
	Andrew Baumann

PLLs are composed of multiple channels. Each channel outputs one clock
signal. They are modeled as one device taking the PLL generated clock as
input, and outputting a new clock.

A channel shares the cm register with its parent PLL, and has its own
a2w_ctrl register. A write to the cm register will trigger an update of
the PLL and all its channels, while a write to an a2w_ctrl channel
register will update the required channel only.

Signed-off-by: Luc Michel <luc@lmichel.fr>
---
 include/hw/misc/bcm2835_cprman.h           |  44 ++++++
 include/hw/misc/bcm2835_cprman_internals.h | 146 +++++++++++++++++++
 hw/misc/bcm2835_cprman.c                   | 155 +++++++++++++++++++--
 3 files changed, 337 insertions(+), 8 deletions(-)

diff --git a/include/hw/misc/bcm2835_cprman.h b/include/hw/misc/bcm2835_cprman.h
index f186ab0104..aaf15fb20c 100644
--- a/include/hw/misc/bcm2835_cprman.h
+++ b/include/hw/misc/bcm2835_cprman.h
@@ -29,10 +29,35 @@ typedef enum CprmanPLL {
     CPRMAN_PLLB,
 
     CPRMAN_NUM_PLL
 } CprmanPLL;
 
+typedef enum CprmanPLLChannel {
+    CPRMAN_PLLA_CHANNEL_DSI0 = 0,
+    CPRMAN_PLLA_CHANNEL_CORE,
+    CPRMAN_PLLA_CHANNEL_PER,
+    CPRMAN_PLLA_CHANNEL_CCP2,
+
+    CPRMAN_PLLC_CHANNEL_CORE2,
+    CPRMAN_PLLC_CHANNEL_CORE1,
+    CPRMAN_PLLC_CHANNEL_PER,
+    CPRMAN_PLLC_CHANNEL_CORE0,
+
+    CPRMAN_PLLD_CHANNEL_DSI0,
+    CPRMAN_PLLD_CHANNEL_CORE,
+    CPRMAN_PLLD_CHANNEL_PER,
+    CPRMAN_PLLD_CHANNEL_DSI1,
+
+    CPRMAN_PLLH_CHANNEL_AUX,
+    CPRMAN_PLLH_CHANNEL_RCAL,
+    CPRMAN_PLLH_CHANNEL_PIX,
+
+    CPRMAN_PLLB_CHANNEL_ARM,
+
+    CPRMAN_NUM_PLL_CHANNEL,
+} CprmanPLLChannel;
+
 typedef struct CprmanPLLState {
     /*< private >*/
     DeviceState parent_obj;
 
     /*< public >*/
@@ -46,18 +71,37 @@ typedef struct CprmanPLLState {
 
     Clock *xosc_in;
     Clock *out;
 } CprmanPLLState;
 
+typedef struct CprmanPLLChannelState {
+    /*< private >*/
+    DeviceState parent_obj;
+
+    /*< public >*/
+    CprmanPLLChannel id;
+    CprmanPLL parent;
+
+    uint32_t *reg_cm;
+    uint32_t hold_mask;
+    uint32_t load_mask;
+    uint32_t *reg_a2w_ctrl;
+    int fixed_divider;
+
+    Clock *pll_in;
+    Clock *out;
+} CprmanPLLChannelState;
+
 struct BCM2835CprmanState {
     /*< private >*/
     SysBusDevice parent_obj;
 
     /*< public >*/
     MemoryRegion iomem;
 
     CprmanPLLState plls[CPRMAN_NUM_PLL];
+    CprmanPLLChannelState channels[CPRMAN_NUM_PLL_CHANNEL];
 
     uint32_t regs[CPRMAN_NUM_REGS];
     uint32_t xosc_freq;
 
     Clock *xosc;
diff --git a/include/hw/misc/bcm2835_cprman_internals.h b/include/hw/misc/bcm2835_cprman_internals.h
index 22a2500ab0..8a5b9aae67 100644
--- a/include/hw/misc/bcm2835_cprman_internals.h
+++ b/include/hw/misc/bcm2835_cprman_internals.h
@@ -11,13 +11,16 @@
 
 #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"
 
 DECLARE_INSTANCE_CHECKER(CprmanPLLState, CPRMAN_PLL,
                          TYPE_CPRMAN_PLL)
+DECLARE_INSTANCE_CHECKER(CprmanPLLChannelState, CPRMAN_PLL_CHANNEL,
+                         TYPE_CPRMAN_PLL_CHANNEL)
 
 /* Register map */
 
 /* PLLs */
 REG32(CM_PLLA, 0x104)
@@ -98,10 +101,35 @@ REG32(A2W_PLLA_FRAC, 0x1200)
 REG32(A2W_PLLC_FRAC, 0x1220)
 REG32(A2W_PLLD_FRAC, 0x1240)
 REG32(A2W_PLLH_FRAC, 0x1260)
 REG32(A2W_PLLB_FRAC, 0x12e0)
 
+/* PLL channels */
+REG32(A2W_PLLA_DSI0, 0x1300)
+    FIELD(A2W_PLLx_CHANNELy, DIV, 0, 8)
+    FIELD(A2W_PLLx_CHANNELy, DISABLE, 8, 1)
+REG32(A2W_PLLA_CORE, 0x1400)
+REG32(A2W_PLLA_PER, 0x1500)
+REG32(A2W_PLLA_CCP2, 0x1600)
+
+REG32(A2W_PLLC_CORE2, 0x1320)
+REG32(A2W_PLLC_CORE1, 0x1420)
+REG32(A2W_PLLC_PER, 0x1520)
+REG32(A2W_PLLC_CORE0, 0x1620)
+
+REG32(A2W_PLLD_DSI0, 0x1340)
+REG32(A2W_PLLD_CORE, 0x1440)
+REG32(A2W_PLLD_PER, 0x1540)
+REG32(A2W_PLLD_DSI1, 0x1640)
+
+REG32(A2W_PLLH_AUX, 0x1360)
+REG32(A2W_PLLH_RCAL, 0x1460)
+REG32(A2W_PLLH_PIX, 0x1560)
+REG32(A2W_PLLH_STS, 0x1660)
+
+REG32(A2W_PLLB_ARM, 0x13e0)
+
 /* misc registers */
 REG32(CM_LOCK, 0x114)
     FIELD(CM_LOCK, FLOCKH, 12, 1)
     FIELD(CM_LOCK, FLOCKD, 11, 1)
     FIELD(CM_LOCK, FLOCKC, 10, 1)
@@ -171,6 +199,124 @@ static inline void set_pll_init_info(BCM2835CprmanState *s,
     pll->reg_a2w_ana = &s->regs[PLL_INIT_INFO[id].a2w_ana_offset];
     pll->prediv_mask = PLL_INIT_INFO[id].prediv_mask;
     pll->reg_a2w_frac = &s->regs[PLL_INIT_INFO[id].a2w_frac_offset];
 }
 
+
+/* PLL channel init info */
+typedef struct PLLChannelInitInfo {
+    const char *name;
+    CprmanPLL parent;
+    size_t cm_offset;
+    uint32_t cm_hold_mask;
+    uint32_t cm_load_mask;
+    size_t a2w_ctrl_offset;
+    unsigned int fixed_divider;
+} PLLChannelInitInfo;
+
+#define FILL_PLL_CHANNEL_INIT_INFO_common(pll_, channel_)            \
+    .parent = CPRMAN_ ## pll_,                                       \
+    .cm_offset = R_CM_ ## pll_,                                      \
+    .cm_load_mask = R_CM_ ## pll_ ## _ ## LOAD ## channel_ ## _MASK, \
+    .a2w_ctrl_offset = R_A2W_ ## pll_ ## _ ## channel_
+
+#define FILL_PLL_CHANNEL_INIT_INFO(pll_, channel_)                   \
+    FILL_PLL_CHANNEL_INIT_INFO_common(pll_, channel_),               \
+    .cm_hold_mask = R_CM_ ## pll_ ## _ ## HOLD ## channel_ ## _MASK, \
+    .fixed_divider = 1
+
+#define FILL_PLL_CHANNEL_INIT_INFO_nohold(pll_, channel_) \
+    FILL_PLL_CHANNEL_INIT_INFO_common(pll_, channel_),    \
+    .cm_hold_mask = 0
+
+static PLLChannelInitInfo PLL_CHANNEL_INIT_INFO[] = {
+    [CPRMAN_PLLA_CHANNEL_DSI0] = {
+        .name = "plla-dsi0",
+        FILL_PLL_CHANNEL_INIT_INFO(PLLA, DSI0),
+    },
+    [CPRMAN_PLLA_CHANNEL_CORE] = {
+        .name = "plla-core",
+        FILL_PLL_CHANNEL_INIT_INFO(PLLA, CORE),
+    },
+    [CPRMAN_PLLA_CHANNEL_PER] = {
+        .name = "plla-per",
+        FILL_PLL_CHANNEL_INIT_INFO(PLLA, PER),
+    },
+    [CPRMAN_PLLA_CHANNEL_CCP2] = {
+        .name = "plla-ccp2",
+        FILL_PLL_CHANNEL_INIT_INFO(PLLA, CCP2),
+    },
+
+    [CPRMAN_PLLC_CHANNEL_CORE2] = {
+        .name = "pllc-core2",
+        FILL_PLL_CHANNEL_INIT_INFO(PLLC, CORE2),
+    },
+    [CPRMAN_PLLC_CHANNEL_CORE1] = {
+        .name = "pllc-core1",
+        FILL_PLL_CHANNEL_INIT_INFO(PLLC, CORE1),
+    },
+    [CPRMAN_PLLC_CHANNEL_PER] = {
+        .name = "pllc-per",
+        FILL_PLL_CHANNEL_INIT_INFO(PLLC, PER),
+    },
+    [CPRMAN_PLLC_CHANNEL_CORE0] = {
+        .name = "pllc-core0",
+        FILL_PLL_CHANNEL_INIT_INFO(PLLC, CORE0),
+    },
+
+    [CPRMAN_PLLD_CHANNEL_DSI0] = {
+        .name = "plld-dsi0",
+        FILL_PLL_CHANNEL_INIT_INFO(PLLD, DSI0),
+    },
+    [CPRMAN_PLLD_CHANNEL_CORE] = {
+        .name = "plld-core",
+        FILL_PLL_CHANNEL_INIT_INFO(PLLD, CORE),
+    },
+    [CPRMAN_PLLD_CHANNEL_PER] = {
+        .name = "plld-per",
+        FILL_PLL_CHANNEL_INIT_INFO(PLLD, PER),
+    },
+    [CPRMAN_PLLD_CHANNEL_DSI1] = {
+        .name = "plld-dsi1",
+        FILL_PLL_CHANNEL_INIT_INFO(PLLD, DSI1),
+    },
+
+    [CPRMAN_PLLH_CHANNEL_AUX] = {
+        .name = "pllh-aux",
+        .fixed_divider = 1,
+        FILL_PLL_CHANNEL_INIT_INFO_nohold(PLLH, AUX),
+    },
+    [CPRMAN_PLLH_CHANNEL_RCAL] = {
+        .name = "pllh-rcal",
+        .fixed_divider = 10,
+        FILL_PLL_CHANNEL_INIT_INFO_nohold(PLLH, RCAL),
+    },
+    [CPRMAN_PLLH_CHANNEL_PIX] = {
+        .name = "pllh-pix",
+        .fixed_divider = 10,
+        FILL_PLL_CHANNEL_INIT_INFO_nohold(PLLH, PIX),
+    },
+
+    [CPRMAN_PLLB_CHANNEL_ARM] = {
+        .name = "pllb-arm",
+        FILL_PLL_CHANNEL_INIT_INFO(PLLB, ARM),
+    },
+};
+
+#undef FILL_PLL_CHANNEL_INIT_INFO_nohold
+#undef FILL_PLL_CHANNEL_INIT_INFO
+#undef FILL_PLL_CHANNEL_INIT_INFO_common
+
+static inline void set_pll_channel_init_info(BCM2835CprmanState *s,
+                                             CprmanPLLChannelState *channel,
+                                             CprmanPLLChannel id)
+{
+    channel->id = id;
+    channel->parent = PLL_CHANNEL_INIT_INFO[id].parent;
+    channel->reg_cm = &s->regs[PLL_CHANNEL_INIT_INFO[id].cm_offset];
+    channel->hold_mask = PLL_CHANNEL_INIT_INFO[id].cm_hold_mask;
+    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;
+}
+
 #endif
diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c
index ba82522eb1..2c70a3f317 100644
--- a/hw/misc/bcm2835_cprman.c
+++ b/hw/misc/bcm2835_cprman.c
@@ -130,10 +130,73 @@ static const TypeInfo cprman_pll_info = {
     .class_init = pll_class_init,
     .instance_init = pll_init,
 };
 
 
+/* PLL channel */
+
+static void pll_channel_update(CprmanPLLChannelState *channel)
+{
+    clock_update(channel->out, 0);
+}
+
+/* Update a PLL and all its channels */
+static void pll_update_all_channels(BCM2835CprmanState *s,
+                                    CprmanPLLState *pll)
+{
+    size_t i;
+
+    pll_update(pll);
+
+    for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
+        CprmanPLLChannelState *channel = &s->channels[i];
+        if (channel->parent == pll->id) {
+            pll_channel_update(channel);
+        }
+    }
+}
+
+static void pll_channel_pll_in_update(void *opaque)
+{
+    pll_channel_update(CPRMAN_PLL_CHANNEL(opaque));
+}
+
+static void pll_channel_init(Object *obj)
+{
+    CprmanPLLChannelState *s = CPRMAN_PLL_CHANNEL(obj);
+
+    s->pll_in = qdev_init_clock_in(DEVICE(s), "pll-in",
+                                   pll_channel_pll_in_update, s);
+    s->out = qdev_init_clock_out(DEVICE(s), "out");
+}
+
+static const VMStateDescription pll_channel_vmstate = {
+    .name = TYPE_CPRMAN_PLL_CHANNEL,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_CLOCK(pll_in, CprmanPLLChannelState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void pll_channel_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd = &pll_channel_vmstate;
+}
+
+static const TypeInfo cprman_pll_channel_info = {
+    .name = TYPE_CPRMAN_PLL_CHANNEL,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(CprmanPLLChannelState),
+    .class_init = pll_channel_class_init,
+    .instance_init = pll_channel_init,
+};
+
+
 /* CPRMAN "top level" model */
 
 static uint32_t get_cm_lock(const BCM2835CprmanState *s)
 {
     static const int CM_LOCK_MAPPING[] = {
@@ -172,12 +235,36 @@ static uint64_t cprman_read(void *opaque, hwaddr offset,
 
     trace_bcm2835_cprman_read(offset, r);
     return r;
 }
 
-#define CASE_PLL_REGS(pll_)       \
-    case R_CM_ ## pll_:           \
+static inline void update_pll_and_channels_from_cm(BCM2835CprmanState *s,
+                                                   size_t idx)
+{
+    size_t i;
+
+    for (i = 0; i < CPRMAN_NUM_PLL; i++) {
+        if (PLL_INIT_INFO[i].cm_offset == idx) {
+            pll_update_all_channels(s, &s->plls[i]);
+            return;
+        }
+    }
+}
+
+static inline void update_channel_from_a2w(BCM2835CprmanState *s, size_t idx)
+{
+    size_t i;
+
+    for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
+        if (PLL_CHANNEL_INIT_INFO[i].a2w_ctrl_offset == idx) {
+            pll_channel_update(&s->channels[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: \
     case R_A2W_ ## pll_ ## _ANA3: \
@@ -198,33 +285,61 @@ static void cprman_write(void *opaque, hwaddr offset,
 
     trace_bcm2835_cprman_write(offset, value);
     s->regs[idx] = value;
 
     switch (idx) {
-    CASE_PLL_REGS(PLLA) :
+    case R_CM_PLLA ... R_CM_PLLH:
+    case R_CM_PLLB:
+        /*
+         * A given CM_PLLx register is shared by both the PLL and the channels
+         * of this PLL.
+         */
+        update_pll_and_channels_from_cm(s, idx);
+        break;
+
+    CASE_PLL_A2W_REGS(PLLA) :
         pll_update(&s->plls[CPRMAN_PLLA]);
         break;
 
-    CASE_PLL_REGS(PLLC) :
+    CASE_PLL_A2W_REGS(PLLC) :
         pll_update(&s->plls[CPRMAN_PLLC]);
         break;
 
-    CASE_PLL_REGS(PLLD) :
+    CASE_PLL_A2W_REGS(PLLD) :
         pll_update(&s->plls[CPRMAN_PLLD]);
         break;
 
-    CASE_PLL_REGS(PLLH) :
+    CASE_PLL_A2W_REGS(PLLH) :
         pll_update(&s->plls[CPRMAN_PLLH]);
         break;
 
-    CASE_PLL_REGS(PLLB) :
+    CASE_PLL_A2W_REGS(PLLB) :
         pll_update(&s->plls[CPRMAN_PLLB]);
         break;
+
+    case R_A2W_PLLA_DSI0:
+    case R_A2W_PLLA_CORE:
+    case R_A2W_PLLA_PER:
+    case R_A2W_PLLA_CCP2:
+    case R_A2W_PLLC_CORE2:
+    case R_A2W_PLLC_CORE1:
+    case R_A2W_PLLC_PER:
+    case R_A2W_PLLC_CORE0:
+    case R_A2W_PLLD_DSI0:
+    case R_A2W_PLLD_CORE:
+    case R_A2W_PLLD_PER:
+    case R_A2W_PLLD_DSI1:
+    case R_A2W_PLLH_AUX:
+    case R_A2W_PLLH_RCAL:
+    case R_A2W_PLLH_PIX:
+    case R_A2W_PLLB_ARM:
+        update_channel_from_a2w(s, idx);
+        break;
     }
 }
 
-#undef CASE_PLL_REGS
+#undef CASE_PLL_A2W_REGS
 
 static const MemoryRegionOps cprman_ops = {
     .read = cprman_read,
     .write = cprman_write,
     .endianness = DEVICE_LITTLE_ENDIAN,
@@ -244,10 +359,14 @@ static void cprman_reset(DeviceState *dev)
 
     for (i = 0; i < CPRMAN_NUM_PLL; i++) {
         device_cold_reset(DEVICE(&s->plls[i]));
     }
 
+    for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
+        device_cold_reset(DEVICE(&s->channels[i]));
+    }
+
     clock_update_hz(s->xosc, s->xosc_freq);
 }
 
 static Clock *init_internal_clock(BCM2835CprmanState *s,
                                   const char *name)
@@ -274,10 +393,17 @@ static void cprman_init(Object *obj)
         object_initialize_child(obj, PLL_INIT_INFO[i].name,
                                 &s->plls[i], TYPE_CPRMAN_PLL);
         set_pll_init_info(s, &s->plls[i], i);
     }
 
+    for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
+        object_initialize_child(obj, PLL_CHANNEL_INIT_INFO[i].name,
+                                &s->channels[i],
+                                TYPE_CPRMAN_PLL_CHANNEL);
+        set_pll_channel_init_info(s, &s->channels[i], i);
+    }
+
     s->xosc = init_internal_clock(s, "xosc");
 
     memory_region_init_io(&s->iomem, obj, &cprman_ops,
                           s, "bcm2835-cprman", 0x2000);
     sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
@@ -295,10 +421,22 @@ static void cprman_realize(DeviceState *dev, Error **errp)
 
         if (!qdev_realize(DEVICE(pll), NULL, errp)) {
             return;
         }
     }
+
+    for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
+        CprmanPLLChannelState *channel = &s->channels[i];
+        CprmanPLL parent = PLL_CHANNEL_INIT_INFO[i].parent;
+        Clock *parent_clk = s->plls[parent].out;
+
+        clock_set_source(channel->pll_in, parent_clk);
+
+        if (!qdev_realize(DEVICE(channel), NULL, errp)) {
+            return;
+        }
+    }
 }
 
 static const VMStateDescription cprman_vmstate = {
     .name = TYPE_BCM2835_CPRMAN,
     .version_id = 1,
@@ -334,8 +472,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_init(cprman_register_types);
-- 
2.28.0



^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 08/14] hw/misc/bcm2835_cprman: implement PLL channels behaviour
  2020-09-25 10:17 [PATCH 00/14] raspi: add the bcm2835 cprman clock manager Luc Michel
                   ` (6 preceding siblings ...)
  2020-09-25 10:17 ` [PATCH 07/14] hw/misc/bcm2835_cprman: add a PLL channel skeleton implementation Luc Michel
@ 2020-09-25 10:17 ` Luc Michel
  2020-09-26 21:36   ` Philippe Mathieu-Daudé
  2020-09-25 10:17 ` [PATCH 09/14] hw/misc/bcm2835_cprman: add a clock mux skeleton implementation Luc Michel
                   ` (7 subsequent siblings)
  15 siblings, 1 reply; 41+ messages in thread
From: Luc Michel @ 2020-09-25 10:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, qemu-arm, Luc Michel, Philippe Mathieu-Daudé,
	Andrew Baumann

A PLL channel is able to further divide the generated PLL frequency. The
divider is given in the ctrl_a2w register. Some channels have a
additional fixed divider which is always applied to the signal.

Signed-off-by: Luc Michel <luc@lmichel.fr>
---
 hw/misc/bcm2835_cprman.c | 33 ++++++++++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c
index 2c70a3f317..e644aeb2b5 100644
--- a/hw/misc/bcm2835_cprman.c
+++ b/hw/misc/bcm2835_cprman.c
@@ -132,13 +132,44 @@ static const TypeInfo cprman_pll_info = {
 };
 
 
 /* PLL channel */
 
+static bool pll_channel_is_enabled(CprmanPLLChannelState *channel)
+{
+    /*
+     * XXX I'm not sure of the purpose of the LOAD field. The Linux driver does
+     * not set it when enabling the channel, but does clear it when disabling
+     * it.
+     */
+    return !FIELD_EX32(*channel->reg_a2w_ctrl, A2W_PLLx_CHANNELy, DISABLE)
+        && !(*channel->reg_cm & channel->hold_mask);
+}
+
 static void pll_channel_update(CprmanPLLChannelState *channel)
 {
-    clock_update(channel->out, 0);
+    uint64_t freq, div;
+
+    if (!pll_channel_is_enabled(channel)) {
+        clock_update(channel->out, 0);
+        return;
+    }
+
+    div = FIELD_EX32(*channel->reg_a2w_ctrl, A2W_PLLx_CHANNELy, DIV);
+
+    if (!div) {
+        /*
+         * It seems that when the divider value is 0, it is considered as
+         * being maximum by the hardware (see the Linux driver).
+         */
+        div = R_A2W_PLLx_CHANNELy_DIV_MASK;
+    }
+
+    /* Some channels have an additional fixed divider */
+    freq = clock_get_hz(channel->pll_in) / (div * channel->fixed_divider);
+
+    clock_update_hz(channel->out, freq);
 }
 
 /* Update a PLL and all its channels */
 static void pll_update_all_channels(BCM2835CprmanState *s,
                                     CprmanPLLState *pll)
-- 
2.28.0



^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 09/14] hw/misc/bcm2835_cprman: add a clock mux skeleton implementation
  2020-09-25 10:17 [PATCH 00/14] raspi: add the bcm2835 cprman clock manager Luc Michel
                   ` (7 preceding siblings ...)
  2020-09-25 10:17 ` [PATCH 08/14] hw/misc/bcm2835_cprman: implement PLL channels behaviour Luc Michel
@ 2020-09-25 10:17 ` Luc Michel
  2020-10-02 14:42   ` Philippe Mathieu-Daudé
  2020-09-25 10:17 ` [PATCH 10/14] hw/misc/bcm2835_cprman: implement clock mux behaviour Luc Michel
                   ` (6 subsequent siblings)
  15 siblings, 1 reply; 41+ messages in thread
From: Luc Michel @ 2020-09-25 10:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, qemu-arm, Luc Michel, Philippe Mathieu-Daudé,
	Andrew Baumann

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,
 } 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;
 };
 
 #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)) {
+            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);
+
+    }
+
     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);
-- 
2.28.0



^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 10/14] hw/misc/bcm2835_cprman: implement clock mux behaviour
  2020-09-25 10:17 [PATCH 00/14] raspi: add the bcm2835 cprman clock manager Luc Michel
                   ` (8 preceding siblings ...)
  2020-09-25 10:17 ` [PATCH 09/14] hw/misc/bcm2835_cprman: add a clock mux skeleton implementation Luc Michel
@ 2020-09-25 10:17 ` Luc Michel
  2020-09-26 21:40   ` Philippe Mathieu-Daudé
  2020-09-25 10:17 ` [PATCH 11/14] hw/misc/bcm2835_cprman: add the DSI0HSCK multiplexer Luc Michel
                   ` (5 subsequent siblings)
  15 siblings, 1 reply; 41+ messages in thread
From: Luc Michel @ 2020-09-25 10:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, qemu-arm, Luc Michel, Philippe Mathieu-Daudé,
	Andrew Baumann

A clock mux can be configured to select one of its 10 sources through
the cm_ctl register. It also embeds yet another clock divider, composed
of an integer part and a fractionnal part. The number of bits of each
part is mux dependant.

Signed-off-by: Luc Michel <luc@lmichel.fr>
---
 hw/misc/bcm2835_cprman.c | 43 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 42 insertions(+), 1 deletion(-)

diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c
index 8df2db0fd9..75bc11939b 100644
--- a/hw/misc/bcm2835_cprman.c
+++ b/hw/misc/bcm2835_cprman.c
@@ -229,19 +229,60 @@ static const TypeInfo cprman_pll_channel_info = {
 };
 
 
 /* clock mux */
 
+static bool clock_mux_is_enabled(CprmanClockMuxState *mux)
+{
+    return FIELD_EX32(*mux->reg_cm, CM_CLOCKx_CTL, ENABLE);
+}
+
 static void clock_mux_update(CprmanClockMuxState *mux)
 {
-    clock_update(mux->out, 0);
+    uint64_t freq, div;
+    uint32_t src = FIELD_EX32(*mux->reg_cm, CM_CLOCKx_CTL, SRC);
+    bool enabled = clock_mux_is_enabled(mux);
+
+    *mux->reg_cm = FIELD_DP32(*mux->reg_cm, CM_CLOCKx_CTL, BUSY, enabled);
+
+    if (!enabled) {
+        clock_update(mux->out, 0);
+        return;
+    }
+
+    freq = clock_get_hz(mux->srcs[src]);
+
+    if (mux->int_bits == 0 && mux->frac_bits == 0) {
+        clock_update_hz(mux->out, freq);
+        return;
+    }
+
+    /*
+     * The divider has an integer and a fractional part. The size of each part
+     * varies with the muxes (int_bits and frac_bits). Both parts are
+     * concatenated, with the integer part always starting at bit 12.
+     */
+    div = mux->reg_cm[1] >> (R_CM_CLOCKx_DIV_FRAC_LENGTH - mux->frac_bits);
+    div &= (1 << (mux->int_bits + mux->frac_bits)) - 1;
+
+    freq = (freq << mux->frac_bits) / div;
+
+    clock_update_hz(mux->out, freq);
 }
 
 static void clock_mux_src_update(void *opaque)
 {
     CprmanClockMuxState **backref = opaque;
     CprmanClockMuxState *s = *backref;
+    CprmanClockMuxSource src = backref - s->backref;
+    uint32_t current_src;
+
+    current_src = FIELD_EX32(*s->reg_cm, CM_CLOCKx_CTL, SRC);
+
+    if (current_src != src) {
+        return;
+    }
 
     clock_mux_update(s);
 }
 
 static void clock_mux_init(Object *obj)
-- 
2.28.0



^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 11/14] hw/misc/bcm2835_cprman: add the DSI0HSCK multiplexer
  2020-09-25 10:17 [PATCH 00/14] raspi: add the bcm2835 cprman clock manager Luc Michel
                   ` (9 preceding siblings ...)
  2020-09-25 10:17 ` [PATCH 10/14] hw/misc/bcm2835_cprman: implement clock mux behaviour Luc Michel
@ 2020-09-25 10:17 ` Luc Michel
  2020-10-02 14:55   ` Philippe Mathieu-Daudé
  2020-09-25 10:17 ` [PATCH 12/14] hw/misc/bcm2835_cprman: add sane reset values to the registers Luc Michel
                   ` (4 subsequent siblings)
  15 siblings, 1 reply; 41+ messages in thread
From: Luc Michel @ 2020-09-25 10:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, qemu-arm, Luc Michel, Philippe Mathieu-Daudé,
	Andrew Baumann

This simple mux sits between the PLL channels and the DSI0E and DSI0P
clock muxes. This mux selects between PLLA-DSI0 and PLLD-DSI0 channel
and outputs the selected signal to source number 4 of DSI0E/P clock
muxes. It is controlled by the cm_dsi0hsck register.

Signed-off-by: Luc Michel <luc@lmichel.fr>
---
 include/hw/misc/bcm2835_cprman.h           | 15 +++++
 include/hw/misc/bcm2835_cprman_internals.h |  6 ++
 hw/misc/bcm2835_cprman.c                   | 78 +++++++++++++++++++++-
 3 files changed, 98 insertions(+), 1 deletion(-)

diff --git a/include/hw/misc/bcm2835_cprman.h b/include/hw/misc/bcm2835_cprman.h
index c2a89e8e90..5555a299fc 100644
--- a/include/hw/misc/bcm2835_cprman.h
+++ b/include/hw/misc/bcm2835_cprman.h
@@ -171,20 +171,35 @@ typedef struct CprmanClockMuxState {
      * source number.
      */
     struct CprmanClockMuxState *backref[CPRMAN_NUM_CLOCK_MUX_SRC];
 } CprmanClockMuxState;
 
+typedef struct CprmanDsi0HsckMuxState {
+    /*< private >*/
+    DeviceState parent_obj;
+
+    /*< public >*/
+    CprmanClockMux id;
+
+    uint32_t *reg_cm;
+
+    Clock *plla_in;
+    Clock *plld_in;
+    Clock *out;
+} CprmanDsi0HsckMuxState;
+
 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];
+    CprmanDsi0HsckMuxState dsi0hsck_mux;
 
     uint32_t regs[CPRMAN_NUM_REGS];
     uint32_t xosc_freq;
 
     Clock *xosc;
diff --git a/include/hw/misc/bcm2835_cprman_internals.h b/include/hw/misc/bcm2835_cprman_internals.h
index a2b5a1aa50..ad07cf5276 100644
--- a/include/hw/misc/bcm2835_cprman_internals.h
+++ b/include/hw/misc/bcm2835_cprman_internals.h
@@ -13,17 +13,20 @@
 #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"
+#define TYPE_CPRMAN_DSI0HSCK_MUX "bcm2835-cprman-dsi0hsck-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)
+DECLARE_INSTANCE_CHECKER(CprmanDsi0HsckMuxState, CPRMAN_DSI0HSCK_MUX,
+                         TYPE_CPRMAN_DSI0HSCK_MUX)
 
 /* Register map */
 
 /* PLLs */
 REG32(CM_PLLA, 0x104)
@@ -221,10 +224,13 @@ REG32(CM_LOCK, 0x114)
     FIELD(CM_LOCK, FLOCKD, 11, 1)
     FIELD(CM_LOCK, FLOCKC, 10, 1)
     FIELD(CM_LOCK, FLOCKB, 9, 1)
     FIELD(CM_LOCK, FLOCKA, 8, 1)
 
+REG32(CM_DSI0HSCK, 0x120)
+    FIELD(CM_DSI0HSCK, SELPLLD, 0, 1)
+
 /*
  * This field is common to all registers. Each register write value must match
  * the CPRMAN_PASSWORD magic value in its 8 MSB.
  */
 FIELD(CPRMAN, PASSWORD, 24, 8)
diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c
index 75bc11939b..e576ab2642 100644
--- a/hw/misc/bcm2835_cprman.c
+++ b/hw/misc/bcm2835_cprman.c
@@ -327,10 +327,62 @@ static const TypeInfo cprman_clock_mux_info = {
     .class_init = clock_mux_class_init,
     .instance_init = clock_mux_init,
 };
 
 
+/* DSI0HSCK mux */
+
+static void dsi0hsck_mux_update(CprmanDsi0HsckMuxState *s)
+{
+    bool src_is_plld = FIELD_EX32(*s->reg_cm, CM_DSI0HSCK, SELPLLD);
+    Clock *src = src_is_plld ? s->plld_in : s->plla_in;
+
+    clock_update(s->out, clock_get(src));
+}
+
+static void dsi0hsck_mux_in_update(void *opaque)
+{
+    dsi0hsck_mux_update(CPRMAN_DSI0HSCK_MUX(opaque));
+}
+
+static void dsi0hsck_mux_init(Object *obj)
+{
+    CprmanDsi0HsckMuxState *s = CPRMAN_DSI0HSCK_MUX(obj);
+    DeviceState *dev = DEVICE(obj);
+
+    s->plla_in = qdev_init_clock_in(dev, "plla-in", dsi0hsck_mux_in_update, s);
+    s->plld_in = qdev_init_clock_in(dev, "plld-in", dsi0hsck_mux_in_update, s);
+    s->out = qdev_init_clock_out(DEVICE(s), "out");
+}
+
+static const VMStateDescription dsi0hsck_mux_vmstate = {
+    .name = TYPE_CPRMAN_DSI0HSCK_MUX,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_CLOCK(plla_in, CprmanDsi0HsckMuxState),
+        VMSTATE_CLOCK(plld_in, CprmanDsi0HsckMuxState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void dsi0hsck_mux_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd = &dsi0hsck_mux_vmstate;
+}
+
+static const TypeInfo cprman_dsi0hsck_mux_info = {
+    .name = TYPE_CPRMAN_DSI0HSCK_MUX,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(CprmanDsi0HsckMuxState),
+    .class_init = dsi0hsck_mux_class_init,
+    .instance_init = dsi0hsck_mux_init,
+};
+
+
 /* CPRMAN "top level" model */
 
 static uint32_t get_cm_lock(const BCM2835CprmanState *s)
 {
     static const int CM_LOCK_MAPPING[] = {
@@ -488,10 +540,14 @@ static void cprman_write(void *opaque, hwaddr offset,
     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;
+
+    case R_CM_DSI0HSCK:
+        dsi0hsck_mux_update(&s->dsi0hsck_mux);
+        break;
     }
 }
 
 #undef CASE_PLL_A2W_REGS
 
@@ -519,10 +575,12 @@ static void cprman_reset(DeviceState *dev)
 
     for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
         device_cold_reset(DEVICE(&s->channels[i]));
     }
 
+    device_cold_reset(DEVICE(&s->dsi0hsck_mux));
+
     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);
@@ -560,10 +618,14 @@ static void cprman_init(Object *obj)
                                 &s->channels[i],
                                 TYPE_CPRMAN_PLL_CHANNEL);
         set_pll_channel_init_info(s, &s->channels[i], i);
     }
 
+    object_initialize_child(obj, "dsi0hsck-mux",
+                            &s->dsi0hsck_mux, TYPE_CPRMAN_DSI0HSCK_MUX);
+    s->dsi0hsck_mux.reg_cm = &s->regs[R_CM_DSI0HSCK];
+
     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],
@@ -608,11 +670,11 @@ static void connect_mux_sources(BCM2835CprmanState *s,
         Clock *src;
 
         if (mapping == CPRMAN_CLOCK_SRC_FORCE_GROUND) {
             src = s->gnd;
         } else if (mapping == CPRMAN_CLOCK_SRC_DSI0HSCK) {
-            src = s->gnd; /* TODO */
+            src = s->dsi0hsck_mux.out;
         } else if (i < CPRMAN_CLOCK_SRC_PLLA) {
             src = CLK_SRC_MAPPING[i];
         } else {
             src = s->channels[mapping].out;
         }
@@ -646,10 +708,23 @@ static void cprman_realize(DeviceState *dev, Error **errp)
         if (!qdev_realize(DEVICE(channel), NULL, errp)) {
             return;
         }
     }
 
+    {
+        CprmanDsi0HsckMuxState *dsi0hsck_mux = &s->dsi0hsck_mux;
+
+        clock_set_source(dsi0hsck_mux->plla_in,
+                         s->channels[CPRMAN_PLLA_CHANNEL_DSI0].out);
+        clock_set_source(dsi0hsck_mux->plld_in,
+                         s->channels[CPRMAN_PLLD_CHANNEL_DSI0].out);
+
+        if (!qdev_realize(DEVICE(dsi0hsck_mux), 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);
 
@@ -696,8 +771,9 @@ 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_register_static(&cprman_dsi0hsck_mux_info);
 }
 
 type_init(cprman_register_types);
-- 
2.28.0



^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 12/14] hw/misc/bcm2835_cprman: add sane reset values to the registers
  2020-09-25 10:17 [PATCH 00/14] raspi: add the bcm2835 cprman clock manager Luc Michel
                   ` (10 preceding siblings ...)
  2020-09-25 10:17 ` [PATCH 11/14] hw/misc/bcm2835_cprman: add the DSI0HSCK multiplexer Luc Michel
@ 2020-09-25 10:17 ` Luc Michel
  2020-09-25 10:17 ` [RFC PATCH 13/14] hw/char/pl011: add a clock input Luc Michel
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 41+ messages in thread
From: Luc Michel @ 2020-09-25 10:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, qemu-arm, Luc Michel, Philippe Mathieu-Daudé,
	Andrew Baumann

Those reset values have been extracted from a Raspberry Pi 3 model B
v1.2, using the 2020-08-20 version of raspios. The dump was done using
the debugfs interface of the cprman driver in Linux (under
'/sys/kernel/debug/clk'). Each exposed clock tree stage (PLLs, channels
and muxes) can be observed by reading the 'regdump' file (e.g.
'plla/regdump').

Those values are set by the Raspberry Pi firmware at boot time (Linux
expects them to be set when it boots up).

Some stages are not exposed by the Linux driver (e.g. the PLL B). For
those, the reset values are unknown and left to 0 which implies a
disabled output.

Once booted in QEMU, the final clock tree is very similar to the one
visible on real hardware. The differences come from some unimplemented
devices for which the driver simply disable the corresponding clock.

Signed-off-by: Luc Michel <luc@lmichel.fr>
---
 include/hw/misc/bcm2835_cprman_internals.h | 269 +++++++++++++++++++++
 hw/misc/bcm2835_cprman.c                   |  31 +++
 2 files changed, 300 insertions(+)

diff --git a/include/hw/misc/bcm2835_cprman_internals.h b/include/hw/misc/bcm2835_cprman_internals.h
index ad07cf5276..f5e168ccf9 100644
--- a/include/hw/misc/bcm2835_cprman_internals.h
+++ b/include/hw/misc/bcm2835_cprman_internals.h
@@ -744,6 +744,275 @@ static inline void set_clock_mux_init_info(BCM2835CprmanState *s,
     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;
 }
 
+
+/*
+ * Object reset info
+ * Those values have been dumped from a Raspberry Pi 3 Model B v1.2 using the
+ * clk debugfs interface in Linux.
+ */
+typedef struct PLLResetInfo {
+    uint32_t cm;
+    uint32_t a2w_ctrl;
+    uint32_t a2w_ana[4];
+    uint32_t a2w_frac;
+} PLLResetInfo;
+
+static const PLLResetInfo PLL_RESET_INFO[] = {
+    [CPRMAN_PLLA] = {
+        .cm = 0x0000008a,
+        .a2w_ctrl = 0x0002103a,
+        .a2w_frac = 0x00098000,
+        .a2w_ana = { 0x00000000, 0x00144000, 0x00000000, 0x00000100 }
+    },
+
+    [CPRMAN_PLLC] = {
+        .cm = 0x00000228,
+        .a2w_ctrl = 0x0002103e,
+        .a2w_frac = 0x00080000,
+        .a2w_ana = { 0x00000000, 0x00144000, 0x00000000, 0x00000100 }
+    },
+
+    [CPRMAN_PLLD] = {
+        .cm = 0x0000020a,
+        .a2w_ctrl = 0x00021034,
+        .a2w_frac = 0x00015556,
+        .a2w_ana = { 0x00000000, 0x00144000, 0x00000000, 0x00000100 }
+    },
+
+    [CPRMAN_PLLH] = {
+        .cm = 0x00000000,
+        .a2w_ctrl = 0x0002102d,
+        .a2w_frac = 0x00000000,
+        .a2w_ana = { 0x00900000, 0x0000000c, 0x00000000, 0x00000000 }
+    },
+
+    [CPRMAN_PLLB] = {
+        /* unknown */
+        .cm = 0x00000000,
+        .a2w_ctrl = 0x00000000,
+        .a2w_frac = 0x00000000,
+        .a2w_ana = { 0x00000000, 0x00000000, 0x00000000, 0x00000000 }
+    }
+};
+
+typedef struct PLLChannelResetInfo {
+    /*
+     * Even though a PLL channel has a CM register, it shares it with its
+     * parent PLL. The parent already takes care of the reset value.
+     */
+    uint32_t a2w_ctrl;
+} PLLChannelResetInfo;
+
+static const PLLChannelResetInfo PLL_CHANNEL_RESET_INFO[] = {
+    [CPRMAN_PLLA_CHANNEL_DSI0] = { .a2w_ctrl = 0x00000100 },
+    [CPRMAN_PLLA_CHANNEL_CORE] = { .a2w_ctrl = 0x00000003 },
+    [CPRMAN_PLLA_CHANNEL_PER] = { .a2w_ctrl = 0x00000000 }, /* unknown */
+    [CPRMAN_PLLA_CHANNEL_CCP2] = { .a2w_ctrl = 0x00000100 },
+
+    [CPRMAN_PLLC_CHANNEL_CORE2] = { .a2w_ctrl = 0x00000100 },
+    [CPRMAN_PLLC_CHANNEL_CORE1] = { .a2w_ctrl = 0x00000100 },
+    [CPRMAN_PLLC_CHANNEL_PER] = { .a2w_ctrl = 0x00000002 },
+    [CPRMAN_PLLC_CHANNEL_CORE0] = { .a2w_ctrl = 0x00000002 },
+
+    [CPRMAN_PLLD_CHANNEL_DSI0] = { .a2w_ctrl = 0x00000100 },
+    [CPRMAN_PLLD_CHANNEL_CORE] = { .a2w_ctrl = 0x00000004 },
+    [CPRMAN_PLLD_CHANNEL_PER] = { .a2w_ctrl = 0x00000004 },
+    [CPRMAN_PLLD_CHANNEL_DSI1] = { .a2w_ctrl = 0x00000100 },
+
+    [CPRMAN_PLLH_CHANNEL_AUX] = { .a2w_ctrl = 0x00000004 },
+    [CPRMAN_PLLH_CHANNEL_RCAL] = { .a2w_ctrl = 0x00000000 },
+    [CPRMAN_PLLH_CHANNEL_PIX] = { .a2w_ctrl = 0x00000000 },
+
+    [CPRMAN_PLLB_CHANNEL_ARM] = { .a2w_ctrl = 0x00000000 }, /* unknown */
+};
+
+typedef struct ClockMuxResetInfo {
+    uint32_t cm_ctrl;
+    uint32_t cm_div;
+} ClockMuxResetInfo;
+
+static const ClockMuxResetInfo CLOCK_MUX_RESET_INFO[] = {
+    [CPRMAN_CLOCK_GNRIC] = {
+        .cm_ctrl = 0, /* unknown */
+        .cm_div = 0
+    },
+
+    [CPRMAN_CLOCK_VPU] = {
+        .cm_ctrl = 0x00000245,
+        .cm_div = 0x00003000,
+    },
+
+    [CPRMAN_CLOCK_SYS] = {
+        .cm_ctrl = 0, /* unknown */
+        .cm_div = 0
+    },
+
+    [CPRMAN_CLOCK_PERIA] = {
+        .cm_ctrl = 0, /* unknown */
+        .cm_div = 0
+    },
+
+    [CPRMAN_CLOCK_PERII] = {
+        .cm_ctrl = 0, /* unknown */
+        .cm_div = 0
+    },
+
+    [CPRMAN_CLOCK_H264] = {
+        .cm_ctrl = 0x00000244,
+        .cm_div = 0x00003000,
+    },
+
+    [CPRMAN_CLOCK_ISP] = {
+        .cm_ctrl = 0x00000244,
+        .cm_div = 0x00003000,
+    },
+
+    [CPRMAN_CLOCK_V3D] = {
+        .cm_ctrl = 0, /* unknown */
+        .cm_div = 0
+    },
+
+    [CPRMAN_CLOCK_CAM0] = {
+        .cm_ctrl = 0x00000000,
+        .cm_div = 0x00000000,
+    },
+
+    [CPRMAN_CLOCK_CAM1] = {
+        .cm_ctrl = 0x00000000,
+        .cm_div = 0x00000000,
+    },
+
+    [CPRMAN_CLOCK_CCP2] = {
+        .cm_ctrl = 0, /* unknown */
+        .cm_div = 0
+    },
+
+    [CPRMAN_CLOCK_DSI0E] = {
+        .cm_ctrl = 0x00000000,
+        .cm_div = 0x00000000,
+    },
+
+    [CPRMAN_CLOCK_DSI0P] = {
+        .cm_ctrl = 0x00000000,
+        .cm_div = 0x00000000,
+    },
+
+    [CPRMAN_CLOCK_DPI] = {
+        .cm_ctrl = 0x00000000,
+        .cm_div = 0x00000000,
+    },
+
+    [CPRMAN_CLOCK_GP0] = {
+        .cm_ctrl = 0x00000200,
+        .cm_div = 0x00000000,
+    },
+
+    [CPRMAN_CLOCK_GP1] = {
+        .cm_ctrl = 0x00000096,
+        .cm_div = 0x00014000,
+    },
+
+    [CPRMAN_CLOCK_GP2] = {
+        .cm_ctrl = 0x00000291,
+        .cm_div = 0x00249f00,
+    },
+
+    [CPRMAN_CLOCK_HSM] = {
+        .cm_ctrl = 0x00000000,
+        .cm_div = 0x00000000,
+    },
+
+    [CPRMAN_CLOCK_OTP] = {
+        .cm_ctrl = 0x00000091,
+        .cm_div = 0x00004000,
+    },
+
+    [CPRMAN_CLOCK_PCM] = {
+        .cm_ctrl = 0x00000200,
+        .cm_div = 0x00000000,
+    },
+
+    [CPRMAN_CLOCK_PWM] = {
+        .cm_ctrl = 0x00000200,
+        .cm_div = 0x00000000,
+    },
+
+    [CPRMAN_CLOCK_SLIM] = {
+        .cm_ctrl = 0x00000200,
+        .cm_div = 0x00000000,
+    },
+
+    [CPRMAN_CLOCK_SMI] = {
+        .cm_ctrl = 0x00000000,
+        .cm_div = 0x00000000,
+    },
+
+    [CPRMAN_CLOCK_TEC] = {
+        .cm_ctrl = 0x00000000,
+        .cm_div = 0x00000000,
+    },
+
+    [CPRMAN_CLOCK_TD0] = {
+        .cm_ctrl = 0, /* unknown */
+        .cm_div = 0
+    },
+
+    [CPRMAN_CLOCK_TD1] = {
+        .cm_ctrl = 0, /* unknown */
+        .cm_div = 0
+    },
+
+    [CPRMAN_CLOCK_TSENS] = {
+        .cm_ctrl = 0x00000091,
+        .cm_div = 0x0000a000,
+    },
+
+    [CPRMAN_CLOCK_TIMER] = {
+        .cm_ctrl = 0x00000291,
+        .cm_div = 0x00013333,
+    },
+
+    [CPRMAN_CLOCK_UART] = {
+        .cm_ctrl = 0x00000296,
+        .cm_div = 0x0000a6ab,
+    },
+
+    [CPRMAN_CLOCK_VEC] = {
+        .cm_ctrl = 0x00000097,
+        .cm_div = 0x00002000,
+    },
+
+    [CPRMAN_CLOCK_PULSE] = {
+        .cm_ctrl = 0, /* unknown */
+        .cm_div = 0
+    },
+
+    [CPRMAN_CLOCK_SDC] = {
+        .cm_ctrl = 0x00004006,
+        .cm_div = 0x00003000,
+    },
+
+    [CPRMAN_CLOCK_ARM] = {
+        .cm_ctrl = 0, /* unknown */
+        .cm_div = 0
+    },
+
+    [CPRMAN_CLOCK_AVEO] = {
+        .cm_ctrl = 0x00000000,
+        .cm_div = 0x00000000,
+    },
+
+    [CPRMAN_CLOCK_EMMC] = {
+        .cm_ctrl = 0x00000295,
+        .cm_div = 0x00006000,
+    },
+
+    [CPRMAN_CLOCK_EMMC2] = {
+        .cm_ctrl = 0, /* unknown */
+        .cm_div = 0
+    },
+};
+
 #endif
diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c
index e576ab2642..a8b54cccdb 100644
--- a/hw/misc/bcm2835_cprman.c
+++ b/hw/misc/bcm2835_cprman.c
@@ -51,10 +51,21 @@
 #include "hw/misc/bcm2835_cprman_internals.h"
 #include "trace.h"
 
 /* PLL */
 
+static void pll_reset(DeviceState *dev)
+{
+    CprmanPLLState *s = CPRMAN_PLL(dev);
+    const PLLResetInfo *info = &PLL_RESET_INFO[s->id];
+
+    *s->reg_cm = info->cm;
+    *s->reg_a2w_ctrl = info->a2w_ctrl;
+    memcpy(s->reg_a2w_ana, info->a2w_ana, sizeof(info->a2w_ana));
+    *s->reg_a2w_frac = info->a2w_frac;
+}
+
 static bool pll_is_locked(const CprmanPLLState *pll)
 {
     return !FIELD_EX32(*pll->reg_a2w_ctrl, A2W_PLLx_CTRL, PWRDN)
         && !FIELD_EX32(*pll->reg_cm, CM_PLLx, ANARST);
 }
@@ -121,10 +132,11 @@ static const VMStateDescription pll_vmstate = {
 
 static void pll_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
 
+    dc->reset = pll_reset;
     dc->vmsd = &pll_vmstate;
 }
 
 static const TypeInfo cprman_pll_info = {
     .name = TYPE_CPRMAN_PLL,
@@ -135,10 +147,18 @@ static const TypeInfo cprman_pll_info = {
 };
 
 
 /* PLL channel */
 
+static void pll_channel_reset(DeviceState *dev)
+{
+    CprmanPLLChannelState *s = CPRMAN_PLL_CHANNEL(dev);
+    const PLLChannelResetInfo *info = &PLL_CHANNEL_RESET_INFO[s->id];
+
+    *s->reg_a2w_ctrl = info->a2w_ctrl;
+}
+
 static bool pll_channel_is_enabled(CprmanPLLChannelState *channel)
 {
     /*
      * XXX I'm not sure of the purpose of the LOAD field. The Linux driver does
      * not set it when enabling the channel, but does clear it when disabling
@@ -215,10 +235,11 @@ static const VMStateDescription pll_channel_vmstate = {
 
 static void pll_channel_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
 
+    dc->reset = pll_channel_reset;
     dc->vmsd = &pll_channel_vmstate;
 }
 
 static const TypeInfo cprman_pll_channel_info = {
     .name = TYPE_CPRMAN_PLL_CHANNEL,
@@ -283,10 +304,19 @@ static void clock_mux_src_update(void *opaque)
     }
 
     clock_mux_update(s);
 }
 
+static void clock_mux_reset(DeviceState *dev)
+{
+    CprmanClockMuxState *clock = CPRMAN_CLOCK_MUX(dev);
+    const ClockMuxResetInfo *info = &CLOCK_MUX_RESET_INFO[clock->id];
+
+    clock->reg_cm[0] = info->cm_ctrl;
+    clock->reg_cm[1] = info->cm_div;
+}
+
 static void clock_mux_init(Object *obj)
 {
     CprmanClockMuxState *s = CPRMAN_CLOCK_MUX(obj);
     size_t i;
 
@@ -315,10 +345,11 @@ static const VMStateDescription clock_mux_vmstate = {
 
 static void clock_mux_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
 
+    dc->reset = clock_mux_reset;
     dc->vmsd = &clock_mux_vmstate;
 }
 
 static const TypeInfo cprman_clock_mux_info = {
     .name = TYPE_CPRMAN_CLOCK_MUX,
-- 
2.28.0



^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [RFC PATCH 13/14] hw/char/pl011: add a clock input
  2020-09-25 10:17 [PATCH 00/14] raspi: add the bcm2835 cprman clock manager Luc Michel
                   ` (11 preceding siblings ...)
  2020-09-25 10:17 ` [PATCH 12/14] hw/misc/bcm2835_cprman: add sane reset values to the registers Luc Michel
@ 2020-09-25 10:17 ` Luc Michel
  2020-09-25 10:36   ` Philippe Mathieu-Daudé
  2020-09-25 10:17 ` [RFC PATCH 14/14] hw/arm/bcm2835_peripherals: connect the UART clock Luc Michel
                   ` (2 subsequent siblings)
  15 siblings, 1 reply; 41+ messages in thread
From: Luc Michel @ 2020-09-25 10:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, qemu-arm, Luc Michel, Philippe Mathieu-Daudé,
	Andrew Baumann

Add a clock input to the PL011 UART so we can compute the current baud
rate and trace it. This is intended for developers who wish to use QEMU
to e.g. debug their firmware or to figure out the baud rate configured
by an unknown/closed source binary.

Signed-off-by: Luc Michel <luc@lmichel.fr>
---
 include/hw/char/pl011.h |  1 +
 hw/char/pl011.c         | 45 +++++++++++++++++++++++++++++++++++++++++
 hw/char/trace-events    |  1 +
 3 files changed, 47 insertions(+)

diff --git a/include/hw/char/pl011.h b/include/hw/char/pl011.h
index a91ea50e11..33e5e5317b 100644
--- a/include/hw/char/pl011.h
+++ b/include/hw/char/pl011.h
@@ -47,10 +47,11 @@ struct PL011State {
     int read_pos;
     int read_count;
     int read_trigger;
     CharBackend chr;
     qemu_irq irq[6];
+    Clock *clk;
     const unsigned char *id;
 };
 
 static inline DeviceState *pl011_create(hwaddr addr,
                                         qemu_irq irq,
diff --git a/hw/char/pl011.c b/hw/char/pl011.c
index 13e784f9d9..ede16c781c 100644
--- a/hw/char/pl011.c
+++ b/hw/char/pl011.c
@@ -20,10 +20,11 @@
 
 #include "qemu/osdep.h"
 #include "hw/char/pl011.h"
 #include "hw/irq.h"
 #include "hw/sysbus.h"
+#include "hw/qdev-clock.h"
 #include "migration/vmstate.h"
 #include "chardev/char-fe.h"
 #include "qemu/log.h"
 #include "qemu/module.h"
 #include "trace.h"
@@ -167,10 +168,29 @@ static void pl011_set_read_trigger(PL011State *s)
     else
 #endif
         s->read_trigger = 1;
 }
 
+static unsigned int pl011_get_baudrate(const PL011State *s)
+{
+    uint64_t clk;
+
+    if (s->fbrd == 0) {
+        return 0;
+    }
+
+    clk = clock_get_hz(s->clk);
+    return (clk / ((s->ibrd << 6) + s->fbrd)) << 2;
+}
+
+static void pl011_trace_baudrate_change(const PL011State *s)
+{
+    trace_pl011_baudrate_change(pl011_get_baudrate(s),
+                                clock_get_hz(s->clk),
+                                s->ibrd, s->fbrd);
+}
+
 static void pl011_write(void *opaque, hwaddr offset,
                         uint64_t value, unsigned size)
 {
     PL011State *s = (PL011State *)opaque;
     unsigned char ch;
@@ -196,13 +216,15 @@ static void pl011_write(void *opaque, hwaddr offset,
     case 8: /* UARTUARTILPR */
         s->ilpr = value;
         break;
     case 9: /* UARTIBRD */
         s->ibrd = value;
+        pl011_trace_baudrate_change(s);
         break;
     case 10: /* UARTFBRD */
         s->fbrd = value;
+        pl011_trace_baudrate_change(s);
         break;
     case 11: /* UARTLCR_H */
         /* Reset the FIFO state on FIFO enable or disable */
         if ((s->lcr ^ value) & 0x10) {
             s->read_count = 0;
@@ -284,16 +306,33 @@ static void pl011_event(void *opaque, QEMUChrEvent event)
 {
     if (event == CHR_EVENT_BREAK)
         pl011_put_fifo(opaque, 0x400);
 }
 
+static void pl011_clock_update(void *opaque)
+{
+    PL011State *s = PL011(opaque);
+
+    pl011_trace_baudrate_change(s);
+}
+
 static const MemoryRegionOps pl011_ops = {
     .read = pl011_read,
     .write = pl011_write,
     .endianness = DEVICE_NATIVE_ENDIAN,
 };
 
+static const VMStateDescription vmstate_pl011_clock = {
+    .name = "pl011/clock",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_CLOCK(clk, PL011State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static const VMStateDescription vmstate_pl011 = {
     .name = "pl011",
     .version_id = 2,
     .minimum_version_id = 2,
     .fields = (VMStateField[]) {
@@ -312,10 +351,14 @@ static const VMStateDescription vmstate_pl011 = {
         VMSTATE_UINT32(ifl, PL011State),
         VMSTATE_INT32(read_pos, PL011State),
         VMSTATE_INT32(read_count, PL011State),
         VMSTATE_INT32(read_trigger, PL011State),
         VMSTATE_END_OF_LIST()
+    },
+    .subsections = (const VMStateDescription * []) {
+        &vmstate_pl011_clock,
+        NULL
     }
 };
 
 static Property pl011_properties[] = {
     DEFINE_PROP_CHR("chardev", PL011State, chr),
@@ -332,10 +375,12 @@ static void pl011_init(Object *obj)
     sysbus_init_mmio(sbd, &s->iomem);
     for (i = 0; i < ARRAY_SIZE(s->irq); i++) {
         sysbus_init_irq(sbd, &s->irq[i]);
     }
 
+    s->clk = qdev_init_clock_in(DEVICE(obj), "clk", pl011_clock_update, s);
+
     s->read_trigger = 1;
     s->ifl = 0x12;
     s->cr = 0x300;
     s->flags = 0x90;
 
diff --git a/hw/char/trace-events b/hw/char/trace-events
index 2442a9f7d5..cc1f0065ac 100644
--- a/hw/char/trace-events
+++ b/hw/char/trace-events
@@ -62,10 +62,11 @@ pl011_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x"
 pl011_read_fifo(int read_count) "FIFO read, read_count now %d"
 pl011_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x"
 pl011_can_receive(uint32_t lcr, int read_count, int r) "LCR 0x%08x read_count %d returning %d"
 pl011_put_fifo(uint32_t c, int read_count) "new char 0x%x read_count now %d"
 pl011_put_fifo_full(void) "FIFO now full, RXFF set"
+pl011_baudrate_change(unsigned int baudrate, uint64_t clock, uint32_t ibrd, uint32_t fbrd) "new baudrate %u (clk: %" PRIu64 "hz, ibrd: %" PRIu32 ", fbrd: %" PRIu32 ")"
 
 # cmsdk-apb-uart.c
 cmsdk_apb_uart_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB UART read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
 cmsdk_apb_uart_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB UART write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
 cmsdk_apb_uart_reset(void) "CMSDK APB UART: reset"
-- 
2.28.0



^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [RFC PATCH 14/14] hw/arm/bcm2835_peripherals: connect the UART clock
  2020-09-25 10:17 [PATCH 00/14] raspi: add the bcm2835 cprman clock manager Luc Michel
                   ` (12 preceding siblings ...)
  2020-09-25 10:17 ` [RFC PATCH 13/14] hw/char/pl011: add a clock input Luc Michel
@ 2020-09-25 10:17 ` Luc Michel
  2020-09-25 11:56 ` [PATCH 00/14] raspi: add the bcm2835 cprman clock manager no-reply
  2020-09-25 12:55 ` Philippe Mathieu-Daudé
  15 siblings, 0 replies; 41+ messages in thread
From: Luc Michel @ 2020-09-25 10:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, qemu-arm, Luc Michel, Philippe Mathieu-Daudé,
	Andrew Baumann

Connect the 'uart-out' clock from the cprman to the PL011 instance.

Signed-off-by: Luc Michel <luc@lmichel.fr>
---
 hw/arm/bcm2835_peripherals.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c
index 958aadeeb9..9e4e85c3ad 100644
--- a/hw/arm/bcm2835_peripherals.c
+++ b/hw/arm/bcm2835_peripherals.c
@@ -167,10 +167,12 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
     if (!sysbus_realize(SYS_BUS_DEVICE(&s->cprman), errp)) {
         return;
     }
     memory_region_add_subregion(&s->peri_mr, CPRMAN_OFFSET,
                 sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->cprman), 0));
+    qdev_connect_clock_in(DEVICE(&s->uart0), "clk",
+                          qdev_get_clock_out(DEVICE(&s->cprman), "uart-out"));
 
     memory_region_add_subregion(&s->peri_mr, ARMCTRL_IC_OFFSET,
                 sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->ic), 0));
     sysbus_pass_irq(SYS_BUS_DEVICE(s), SYS_BUS_DEVICE(&s->ic));
 
-- 
2.28.0



^ permalink raw reply related	[flat|nested] 41+ messages in thread

* Re: [RFC PATCH 13/14] hw/char/pl011: add a clock input
  2020-09-25 10:17 ` [RFC PATCH 13/14] hw/char/pl011: add a clock input Luc Michel
@ 2020-09-25 10:36   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 41+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-09-25 10:36 UTC (permalink / raw)
  To: Luc Michel, qemu-devel; +Cc: Peter Maydell, qemu-arm, Andrew Baumann

On 9/25/20 12:17 PM, Luc Michel wrote:
> Add a clock input to the PL011 UART so we can compute the current baud
> rate and trace it. This is intended for developers who wish to use QEMU
> to e.g. debug their firmware or to figure out the baud rate configured
> by an unknown/closed source binary.
> 
> Signed-off-by: Luc Michel <luc@lmichel.fr>

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

> ---
>  include/hw/char/pl011.h |  1 +
>  hw/char/pl011.c         | 45 +++++++++++++++++++++++++++++++++++++++++
>  hw/char/trace-events    |  1 +
>  3 files changed, 47 insertions(+)
> 
> diff --git a/include/hw/char/pl011.h b/include/hw/char/pl011.h
> index a91ea50e11..33e5e5317b 100644
> --- a/include/hw/char/pl011.h
> +++ b/include/hw/char/pl011.h
> @@ -47,10 +47,11 @@ struct PL011State {
>      int read_pos;
>      int read_count;
>      int read_trigger;
>      CharBackend chr;
>      qemu_irq irq[6];
> +    Clock *clk;
>      const unsigned char *id;
>  };
>  
>  static inline DeviceState *pl011_create(hwaddr addr,
>                                          qemu_irq irq,
> diff --git a/hw/char/pl011.c b/hw/char/pl011.c
> index 13e784f9d9..ede16c781c 100644
> --- a/hw/char/pl011.c
> +++ b/hw/char/pl011.c
> @@ -20,10 +20,11 @@
>  
>  #include "qemu/osdep.h"
>  #include "hw/char/pl011.h"
>  #include "hw/irq.h"
>  #include "hw/sysbus.h"
> +#include "hw/qdev-clock.h"
>  #include "migration/vmstate.h"
>  #include "chardev/char-fe.h"
>  #include "qemu/log.h"
>  #include "qemu/module.h"
>  #include "trace.h"
> @@ -167,10 +168,29 @@ static void pl011_set_read_trigger(PL011State *s)
>      else
>  #endif
>          s->read_trigger = 1;
>  }
>  
> +static unsigned int pl011_get_baudrate(const PL011State *s)
> +{
> +    uint64_t clk;
> +
> +    if (s->fbrd == 0) {
> +        return 0;
> +    }
> +
> +    clk = clock_get_hz(s->clk);
> +    return (clk / ((s->ibrd << 6) + s->fbrd)) << 2;
> +}
> +
> +static void pl011_trace_baudrate_change(const PL011State *s)
> +{
> +    trace_pl011_baudrate_change(pl011_get_baudrate(s),
> +                                clock_get_hz(s->clk),
> +                                s->ibrd, s->fbrd);
> +}
> +
>  static void pl011_write(void *opaque, hwaddr offset,
>                          uint64_t value, unsigned size)
>  {
>      PL011State *s = (PL011State *)opaque;
>      unsigned char ch;
> @@ -196,13 +216,15 @@ static void pl011_write(void *opaque, hwaddr offset,
>      case 8: /* UARTUARTILPR */
>          s->ilpr = value;
>          break;
>      case 9: /* UARTIBRD */
>          s->ibrd = value;
> +        pl011_trace_baudrate_change(s);
>          break;
>      case 10: /* UARTFBRD */
>          s->fbrd = value;
> +        pl011_trace_baudrate_change(s);
>          break;
>      case 11: /* UARTLCR_H */
>          /* Reset the FIFO state on FIFO enable or disable */
>          if ((s->lcr ^ value) & 0x10) {
>              s->read_count = 0;
> @@ -284,16 +306,33 @@ static void pl011_event(void *opaque, QEMUChrEvent event)
>  {
>      if (event == CHR_EVENT_BREAK)
>          pl011_put_fifo(opaque, 0x400);
>  }
>  
> +static void pl011_clock_update(void *opaque)
> +{
> +    PL011State *s = PL011(opaque);
> +
> +    pl011_trace_baudrate_change(s);
> +}
> +
>  static const MemoryRegionOps pl011_ops = {
>      .read = pl011_read,
>      .write = pl011_write,
>      .endianness = DEVICE_NATIVE_ENDIAN,
>  };
>  
> +static const VMStateDescription vmstate_pl011_clock = {
> +    .name = "pl011/clock",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_CLOCK(clk, PL011State),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
>  static const VMStateDescription vmstate_pl011 = {
>      .name = "pl011",
>      .version_id = 2,
>      .minimum_version_id = 2,
>      .fields = (VMStateField[]) {
> @@ -312,10 +351,14 @@ static const VMStateDescription vmstate_pl011 = {
>          VMSTATE_UINT32(ifl, PL011State),
>          VMSTATE_INT32(read_pos, PL011State),
>          VMSTATE_INT32(read_count, PL011State),
>          VMSTATE_INT32(read_trigger, PL011State),
>          VMSTATE_END_OF_LIST()
> +    },
> +    .subsections = (const VMStateDescription * []) {
> +        &vmstate_pl011_clock,
> +        NULL
>      }
>  };
>  
>  static Property pl011_properties[] = {
>      DEFINE_PROP_CHR("chardev", PL011State, chr),
> @@ -332,10 +375,12 @@ static void pl011_init(Object *obj)
>      sysbus_init_mmio(sbd, &s->iomem);
>      for (i = 0; i < ARRAY_SIZE(s->irq); i++) {
>          sysbus_init_irq(sbd, &s->irq[i]);
>      }
>  
> +    s->clk = qdev_init_clock_in(DEVICE(obj), "clk", pl011_clock_update, s);
> +
>      s->read_trigger = 1;
>      s->ifl = 0x12;
>      s->cr = 0x300;
>      s->flags = 0x90;
>  
> diff --git a/hw/char/trace-events b/hw/char/trace-events
> index 2442a9f7d5..cc1f0065ac 100644
> --- a/hw/char/trace-events
> +++ b/hw/char/trace-events
> @@ -62,10 +62,11 @@ pl011_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x"
>  pl011_read_fifo(int read_count) "FIFO read, read_count now %d"
>  pl011_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x"
>  pl011_can_receive(uint32_t lcr, int read_count, int r) "LCR 0x%08x read_count %d returning %d"
>  pl011_put_fifo(uint32_t c, int read_count) "new char 0x%x read_count now %d"
>  pl011_put_fifo_full(void) "FIFO now full, RXFF set"
> +pl011_baudrate_change(unsigned int baudrate, uint64_t clock, uint32_t ibrd, uint32_t fbrd) "new baudrate %u (clk: %" PRIu64 "hz, ibrd: %" PRIu32 ", fbrd: %" PRIu32 ")"
>  
>  # cmsdk-apb-uart.c
>  cmsdk_apb_uart_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB UART read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
>  cmsdk_apb_uart_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB UART write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
>  cmsdk_apb_uart_reset(void) "CMSDK APB UART: reset"
> 


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 00/14] raspi: add the bcm2835 cprman clock manager
  2020-09-25 10:17 [PATCH 00/14] raspi: add the bcm2835 cprman clock manager Luc Michel
                   ` (13 preceding siblings ...)
  2020-09-25 10:17 ` [RFC PATCH 14/14] hw/arm/bcm2835_peripherals: connect the UART clock Luc Michel
@ 2020-09-25 11:56 ` no-reply
  2020-09-25 12:55 ` Philippe Mathieu-Daudé
  15 siblings, 0 replies; 41+ messages in thread
From: no-reply @ 2020-09-25 11:56 UTC (permalink / raw)
  To: luc; +Cc: peter.maydell, luc, qemu-devel, Andrew.Baumann, f4bug, qemu-arm

Patchew URL: https://patchew.org/QEMU/20200925101731.2159827-1-luc@lmichel.fr/



Hi,

This series failed the docker-quick@centos7 build test. Please find the testing commands and
their output below. If you have Docker installed, you can probably reproduce it
locally.

=== TEST SCRIPT BEGIN ===
#!/bin/bash
make docker-image-centos7 V=1 NETWORK=1
time make docker-test-quick@centos7 SHOW_ENV=1 J=14 NETWORK=1
=== TEST SCRIPT END ===

C linker for the host machine: cc ld.bfd 2.27-43
Host machine cpu family: x86_64
Host machine cpu: x86_64
../src/meson.build:10: WARNING: Module unstable-keyval has no backwards or forwards compatibility and might not exist in future releases.
Program sh found: YES
Program python3 found: YES (/usr/bin/python3)
Configuring ninjatool using configuration
---
Running test qtest-aarch64: qmp-test
Broken pipe
../src/tests/qtest/libqtest.c:175: kill_qemu() detected QEMU death from signal 11 (Segmentation fault) (core dumped)
ERROR qtest-aarch64: device-introspect-test - too few tests run (expected 6, got 5)
Could not access KVM kernel module: No such file or directory
qemu-system-x86_64: -accel kvm: failed to initialize kvm: No such file or directory
qemu-system-x86_64: falling back to tcg
make: *** [run-test-166] Error 1
make: *** Waiting for unfinished jobs....

Looking for expected file 'tests/data/acpi/q35/FACP.ipmibt'
---
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['sudo', '-n', 'docker', 'run', '--rm', '--label', 'com.qemu.instance.uuid=4e540e8b62f746b082ad47693e2521fa', '-u', '1001', '--security-opt', 'seccomp=unconfined', '-e', 'TARGET_LIST=', '-e', 'EXTRA_CONFIGURE_OPTS=', '-e', 'V=', '-e', 'J=14', '-e', 'DEBUG=', '-e', 'SHOW_ENV=1', '-e', 'CCACHE_DIR=/var/tmp/ccache', '-v', '/home/patchew/.cache/qemu-docker-ccache:/var/tmp/ccache:z', '-v', '/var/tmp/patchew-tester-tmp-d4ues5y_/src/docker-src.2020-09-25-07.47.48.18126:/var/tmp/qemu:z,ro', 'qemu/centos7', '/var/tmp/qemu/run', 'test-quick']' returned non-zero exit status 2.
filter=--filter=label=com.qemu.instance.uuid=4e540e8b62f746b082ad47693e2521fa
make[1]: *** [docker-run] Error 1
make[1]: Leaving directory `/var/tmp/patchew-tester-tmp-d4ues5y_/src'
make: *** [docker-run-test-quick@centos7] Error 2

real    8m13.133s
user    0m20.580s


The full log is available at
http://patchew.org/logs/20200925101731.2159827-1-luc@lmichel.fr/testing.docker-quick@centos7/?type=message.
---
Email generated automatically by Patchew [https://patchew.org/].
Please send your feedback to patchew-devel@redhat.com

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 00/14] raspi: add the bcm2835 cprman clock manager
  2020-09-25 10:17 [PATCH 00/14] raspi: add the bcm2835 cprman clock manager Luc Michel
                   ` (14 preceding siblings ...)
  2020-09-25 11:56 ` [PATCH 00/14] raspi: add the bcm2835 cprman clock manager no-reply
@ 2020-09-25 12:55 ` Philippe Mathieu-Daudé
  15 siblings, 0 replies; 41+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-09-25 12:55 UTC (permalink / raw)
  To: Luc Michel, qemu-devel
  Cc: Peter Maydell, Havard Skinnemoen, Andrew Baumann, Paul Zimmerman,
	Niek Linnenbank, qemu-arm

Cc'ing Paul/Niek/Havard.

On 9/25/20 12:17 PM, Luc Michel wrote:
> Hi,
> 
> This series add the BCM2835 cprman clock manager peripheral to the
> Raspberry Pi machine.
> 
> Patches 1-3 are preliminary changes, patches 4-12 are the actual
> implementation.
> 
> The two last patches add a clock input to the PL011 and
> connect it to the cprman and are RFC.
> 
> This series has been tested with Linux 5.4.61 (the current raspios
> version). It fixes the kernel Oops at boot time due to invalid UART
> clock value, and other warnings/errors here and there because of bad
> clocks or lack of cprman.
> 
> Here is the clock tree as seen by Linux when booted in QEMU:
> (/sys/kernel/debug/clk/clk_summary with some columns removed)
> 
>                         enable  prepare              
>    clock                 count    count          rate
> -----------------------------------------------------
>  otg                         0        0     480000000
>  osc                         5        5      19200000
>     gp2                      1        1         32768
>     tsens                    0        0       1920000
>     otp                      0        0       4800000
>     timer                    0        0       1000002
>     pllh                     4        4     864000000
>        pllh_pix_prediv       1        1       3375000
>           pllh_pix           0        0        337500
>        pllh_aux              1        1     216000000
>           vec                0        0     108000000
>        pllh_rcal_prediv      1        1       3375000
>           pllh_rcal          0        0        337500
>     plld                     3        3    2000000024
>        plld_dsi1             0        0       7812501
>        plld_dsi0             0        0       7812501
>        plld_per              3        3     500000006
>           gp1                1        1      25000000
>           uart               1        2      47999625
>        plld_core             2        2     500000006
>           sdram              0        0     166666668
>     pllc                     3        3    2400000000
>        pllc_per              1        1    1200000000
>           emmc               0        0     200000000
>        pllc_core2            0        0       9375000
>        pllc_core1            0        0       9375000
>        pllc_core0            2        2    1200000000
>           vpu                1        1     700000000
>              aux_spi2        0        0     700000000
>              aux_spi1        0        0     700000000
>              aux_uart        0        0     700000000
>              peri_image      0        0     700000000
>     plla                     2        2    2250000000
>        plla_ccp2             0        0       8789063
>        plla_dsi0             0        0       8789063
>        plla_core             1        1     750000000
>           h264               0        0     250000000
>           isp                0        0     250000000
>  dsi1p                       0        0             0
>  dsi0p                       0        0             0
>  dsi1e                       0        0             0
>  dsi0e                       0        0             0
>  cam1                        0        0             0
>  cam0                        0        0             0
>  dpi                         0        0             0
>  tec                         0        0             0
>  smi                         0        0             0
>  slim                        0        0             0
>  gp0                         0        0             0
>  dft                         0        0             0
>  aveo                        0        0             0
>  pcm                         0        0             0
>  pwm                         0        0             0
>  hsm                         0        0             0
> 
> It shows small differences with real hardware due other missing
> peripherals for which the driver turn the clock off (like tsens).
> 
> Luc Michel (14):
>   hw/core/clock: provide the VMSTATE_ARRAY_CLOCK macro
>   hw/core/clock: trace clock values in Hz instead of ns
>   hw/arm/raspi: fix cprman base address
>   hw/arm/raspi: add a skeleton implementation of the cprman
>   hw/misc/bcm2835_cprman: add a PLL skeleton implementation
>   hw/misc/bcm2835_cprman: implement PLLs behaviour
>   hw/misc/bcm2835_cprman: add a PLL channel skeleton implementation
>   hw/misc/bcm2835_cprman: implement PLL channels behaviour
>   hw/misc/bcm2835_cprman: add a clock mux skeleton implementation
>   hw/misc/bcm2835_cprman: implement clock mux behaviour
>   hw/misc/bcm2835_cprman: add the DSI0HSCK multiplexer
>   hw/misc/bcm2835_cprman: add sane reset values to the registers
>   hw/char/pl011: add a clock input
>   hw/arm/bcm2835_peripherals: connect the UART clock
> 

>  14 files changed, 2118 insertions(+), 12 deletions(-)
ouch... o_O huge device...

Tested on top of my raspi branch with raspi[0123]:
Tested-by: Philippe Mathieu-Daudé <f4bug@amsat.org>


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 02/14] hw/core/clock: trace clock values in Hz instead of ns
  2020-09-25 10:17 ` [PATCH 02/14] hw/core/clock: trace clock values in Hz instead of ns Luc Michel
@ 2020-09-26 20:36   ` Philippe Mathieu-Daudé
  2020-09-28  8:42     ` Damien Hedde
  0 siblings, 1 reply; 41+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-09-26 20:36 UTC (permalink / raw)
  To: Luc Michel, qemu-devel
  Cc: Damien Hedde, Peter Maydell, qemu-arm, Andrew Baumann

On 9/25/20 12:17 PM, Luc Michel wrote:
> The nanosecond unit greatly limits the dynamic range we can display in
> clock value traces, for values in the order of 1GHz and more. The
> internal representation can go way beyond this value and it is quite
> common for today's clocks to be within those ranges.
> 
> For example, a frequency between 500MHz+ and 1GHz will be displayed as
> 1ns. Beyond 1GHz, it will show up as 0ns.
> 
> Replace nanosecond periods traces with frequencies in the Hz unit
> to have more dynamic range in the trace output.

I have a similar patch adding the freq but keeping the periods in
case, as it might be a matter of taste (for me too the frequency
is more meaningful).

> 
> Signed-off-by: Luc Michel <luc@lmichel.fr>
> ---
>  hw/core/clock.c      | 6 +++---
>  hw/core/trace-events | 4 ++--
>  2 files changed, 5 insertions(+), 5 deletions(-)
> 
> diff --git a/hw/core/clock.c b/hw/core/clock.c
> index 7066282f7b..81184734e0 100644
> --- a/hw/core/clock.c
> +++ b/hw/core/clock.c
> @@ -37,12 +37,12 @@ void clock_clear_callback(Clock *clk)
>  bool clock_set(Clock *clk, uint64_t period)
>  {
>      if (clk->period == period) {
>          return false;
>      }
> -    trace_clock_set(CLOCK_PATH(clk), CLOCK_PERIOD_TO_NS(clk->period),
> -                    CLOCK_PERIOD_TO_NS(period));
> +    trace_clock_set(CLOCK_PATH(clk), CLOCK_PERIOD_TO_HZ(clk->period),
> +                    CLOCK_PERIOD_TO_HZ(period));
>      clk->period = period;
>  
>      return true;
>  }
>  
> @@ -52,11 +52,11 @@ static void clock_propagate_period(Clock *clk, bool call_callbacks)
>  
>      QLIST_FOREACH(child, &clk->children, sibling) {
>          if (child->period != clk->period) {
>              child->period = clk->period;
>              trace_clock_update(CLOCK_PATH(child), CLOCK_PATH(clk),
> -                               CLOCK_PERIOD_TO_NS(clk->period),
> +                               CLOCK_PERIOD_TO_HZ(clk->period),
>                                 call_callbacks);
>              if (call_callbacks && child->callback) {
>                  child->callback(child->callback_opaque);
>              }
>              clock_propagate_period(child, call_callbacks);
> diff --git a/hw/core/trace-events b/hw/core/trace-events
> index 1ac60ede6b..6f96d8bfd0 100644
> --- a/hw/core/trace-events
> +++ b/hw/core/trace-events
> @@ -29,8 +29,8 @@ resettable_phase_exit_end(void *obj, const char *objtype, unsigned count) "obj=%
>  resettable_transitional_function(void *obj, const char *objtype) "obj=%p(%s)"
>  
>  # clock.c
>  clock_set_source(const char *clk, const char *src) "'%s', src='%s'"
>  clock_disconnect(const char *clk) "'%s'"
> -clock_set(const char *clk, uint64_t old, uint64_t new) "'%s', ns=%"PRIu64"->%"PRIu64
> +clock_set(const char *clk, uint64_t old, uint64_t new) "'%s', %"PRIu64"hz->%"PRIu64"hz"

Unit is spell 'Hz'.

>  clock_propagate(const char *clk) "'%s'"
> -clock_update(const char *clk, const char *src, uint64_t val, int cb) "'%s', src='%s', ns=%"PRIu64", cb=%d"
> +clock_update(const char *clk, const char *src, uint64_t hz, int cb) "'%s', src='%s', val=%"PRIu64"hz cb=%d"

Ditto.

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 01/14] hw/core/clock: provide the VMSTATE_ARRAY_CLOCK macro
  2020-09-25 10:17 ` [PATCH 01/14] hw/core/clock: provide the VMSTATE_ARRAY_CLOCK macro Luc Michel
@ 2020-09-26 20:36   ` Philippe Mathieu-Daudé
  2020-09-28  8:38   ` Damien Hedde
  1 sibling, 0 replies; 41+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-09-26 20:36 UTC (permalink / raw)
  To: Luc Michel, qemu-devel
  Cc: Damien Hedde, Peter Maydell, qemu-arm, Andrew Baumann

On 9/25/20 12:17 PM, Luc Michel wrote:
> Signed-off-by: Luc Michel <luc@lmichel.fr>

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

> ---
>  include/hw/clock.h | 5 +++++
>  1 file changed, 5 insertions(+)
> 
> diff --git a/include/hw/clock.h b/include/hw/clock.h
> index d357594df9..c93e6113cd 100644
> --- a/include/hw/clock.h
> +++ b/include/hw/clock.h
> @@ -79,10 +79,15 @@ struct Clock {
>  extern const VMStateDescription vmstate_clock;
>  #define VMSTATE_CLOCK(field, state) \
>      VMSTATE_CLOCK_V(field, state, 0)
>  #define VMSTATE_CLOCK_V(field, state, version) \
>      VMSTATE_STRUCT_POINTER_V(field, state, version, vmstate_clock, Clock)
> +#define VMSTATE_ARRAY_CLOCK(field, state, num) \
> +    VMSTATE_ARRAY_CLOCK_V(field, state, num, 0)
> +#define VMSTATE_ARRAY_CLOCK_V(field, state, num, version)          \
> +    VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(field, state, num, version, \
> +                                       vmstate_clock, Clock)
>  
>  /**
>   * clock_setup_canonical_path:
>   * @clk: clock
>   *
> 


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 03/14] hw/arm/raspi: fix cprman base address
  2020-09-25 10:17 ` [PATCH 03/14] hw/arm/raspi: fix cprman base address Luc Michel
@ 2020-09-26 21:04   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 41+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-09-26 21:04 UTC (permalink / raw)
  To: Luc Michel, qemu-devel; +Cc: Peter Maydell, qemu-arm, Andrew Baumann

On 9/25/20 12:17 PM, Luc Michel wrote:
> The cprman (clock controller) was mapped at the watchdog/power manager
> address. It was also split into two unimplemented peripherals (cm and
> a2w)

Please use CAPS: CPRMAN, CM, A2W.

> but this is really the same one, as shown by this extract of the
> Raspberry Pi 3 Linux device tree:
> 
>     watchdog@7e100000 {
>             compatible = "brcm,bcm2835-pm\0brcm,bcm2835-pm-wdt";
>             [...]
>             reg = <0x7e100000 0x114 0x7e00a000 0x24>;
>             [...]
>     };
> 
>     [...]
>     cprman@7e101000 {
>             compatible = "brcm,bcm2835-cprman";
>             [...]
>             reg = <0x7e101000 0x2000>;
>             [...]
>     };

I wouldn't take Linux and its device tree as a reference...

Also I'm having hard time accepting a 8KB MMIO block aligned
on a 4KB boundary... But accepting it greatly simplify the
model, so let's assume this is the case.

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

> 
> Signed-off-by: Luc Michel <luc@lmichel.fr>
> ---
>  include/hw/arm/bcm2835_peripherals.h | 2 +-
>  include/hw/arm/raspi_platform.h      | 5 ++---
>  hw/arm/bcm2835_peripherals.c         | 4 ++--
>  3 files changed, 5 insertions(+), 6 deletions(-)
> 
> diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h
> index 13d7c4c553..199088425a 100644
> --- a/include/hw/arm/bcm2835_peripherals.h
> +++ b/include/hw/arm/bcm2835_peripherals.h
> @@ -44,12 +44,12 @@ struct BCM2835PeripheralState {
>      qemu_irq irq, fiq;
>  
>      BCM2835SystemTimerState systmr;
>      BCM2835MphiState mphi;
>      UnimplementedDeviceState armtmr;
> +    UnimplementedDeviceState powermgt;
>      UnimplementedDeviceState cprman;
> -    UnimplementedDeviceState a2w;
>      PL011State uart0;
>      BCM2835AuxState aux;
>      BCM2835FBState fb;
>      BCM2835DMAState dma;
>      BCM2835ICState ic;
> diff --git a/include/hw/arm/raspi_platform.h b/include/hw/arm/raspi_platform.h
> index 61b04a1bd4..51a477cdc2 100644
> --- a/include/hw/arm/raspi_platform.h
> +++ b/include/hw/arm/raspi_platform.h
> @@ -34,13 +34,12 @@
>  #define ARMCTRL_OFFSET          (ARM_OFFSET + 0x000)
>  #define ARMCTRL_IC_OFFSET       (ARM_OFFSET + 0x200) /* Interrupt controller */
>  #define ARMCTRL_TIMER0_1_OFFSET (ARM_OFFSET + 0x400) /* Timer 0 and 1 */
>  #define ARMCTRL_0_SBM_OFFSET    (ARM_OFFSET + 0x800) /* User 0 (ARM) Semaphores
>                                                        * Doorbells & Mailboxes */
> -#define CPRMAN_OFFSET           0x100000 /* Power Management, Watchdog */
> -#define CM_OFFSET               0x101000 /* Clock Management */
> -#define A2W_OFFSET              0x102000 /* Reset controller */
> +#define PM_OFFSET               0x100000 /* Power Management */
> +#define CPRMAN_OFFSET           0x101000 /* Clock Management */
>  #define AVS_OFFSET              0x103000 /* Audio Video Standard */
>  #define RNG_OFFSET              0x104000
>  #define GPIO_OFFSET             0x200000
>  #define UART0_OFFSET            0x201000
>  #define MMCI0_OFFSET            0x202000
> diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c
> index a9d7f53f6e..f0802c91e0 100644
> --- a/hw/arm/bcm2835_peripherals.c
> +++ b/hw/arm/bcm2835_peripherals.c
> @@ -342,12 +342,12 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
>      sysbus_connect_irq(SYS_BUS_DEVICE(&s->dwc2), 0,
>          qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
>                                 INTERRUPT_USB));
>  
>      create_unimp(s, &s->armtmr, "bcm2835-sp804", ARMCTRL_TIMER0_1_OFFSET, 0x40);
> -    create_unimp(s, &s->cprman, "bcm2835-cprman", CPRMAN_OFFSET, 0x1000);
> -    create_unimp(s, &s->a2w, "bcm2835-a2w", A2W_OFFSET, 0x1000);
> +    create_unimp(s, &s->powermgt, "bcm2835-powermgt", PM_OFFSET, 0x114);
> +    create_unimp(s, &s->cprman, "bcm2835-cprman", CPRMAN_OFFSET, 0x2000);
>      create_unimp(s, &s->i2s, "bcm2835-i2s", I2S_OFFSET, 0x100);
>      create_unimp(s, &s->smi, "bcm2835-smi", SMI_OFFSET, 0x100);
>      create_unimp(s, &s->spi[0], "bcm2835-spi0", SPI0_OFFSET, 0x20);
>      create_unimp(s, &s->bscsl, "bcm2835-spis", BSC_SL_OFFSET, 0x100);
>      create_unimp(s, &s->i2c[0], "bcm2835-i2c0", BSC0_OFFSET, 0x20);
> 


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 04/14] hw/arm/raspi: add a skeleton implementation of the cprman
  2020-09-25 10:17 ` [PATCH 04/14] hw/arm/raspi: add a skeleton implementation of the cprman Luc Michel
@ 2020-09-26 21:05   ` Philippe Mathieu-Daudé
  2020-09-28  8:45     ` Luc Michel
  0 siblings, 1 reply; 41+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-09-26 21:05 UTC (permalink / raw)
  To: Luc Michel, qemu-devel; +Cc: Peter Maydell, qemu-arm, Andrew Baumann

On 9/25/20 12:17 PM, Luc Michel wrote:
> The BCM2835 cprman is the clock manager of the SoC. It is composed of a

Can we use CPRMAN in caps?

> main oscillator, and several sub-components (PLLs, multiplexers, ...) to
> generate the BCM2835 clock tree.
> 
> This commit adds a skeleton of the cprman, with a dummy register
> read/write implementation. It embeds the main oscillator (xosc) from
> which all the clocks will be derived.
> 
> Signed-off-by: Luc Michel <luc@lmichel.fr>
> ---
>  include/hw/arm/bcm2835_peripherals.h       |   3 +-
>  include/hw/misc/bcm2835_cprman.h           |  37 +++++
>  include/hw/misc/bcm2835_cprman_internals.h |  24 +++
>  hw/arm/bcm2835_peripherals.c               |  11 +-
>  hw/misc/bcm2835_cprman.c                   | 171 +++++++++++++++++++++
>  hw/misc/meson.build                        |   1 +
>  hw/misc/trace-events                       |   5 +
>  7 files changed, 250 insertions(+), 2 deletions(-)
>  create mode 100644 include/hw/misc/bcm2835_cprman.h
>  create mode 100644 include/hw/misc/bcm2835_cprman_internals.h
>  create mode 100644 hw/misc/bcm2835_cprman.c
> 
> diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h
> index 199088425a..002bb5e73b 100644
> --- a/include/hw/arm/bcm2835_peripherals.h
> +++ b/include/hw/arm/bcm2835_peripherals.h
> @@ -21,10 +21,11 @@
>  #include "hw/misc/bcm2835_property.h"
>  #include "hw/misc/bcm2835_rng.h"
>  #include "hw/misc/bcm2835_mbox.h"
>  #include "hw/misc/bcm2835_mphi.h"
>  #include "hw/misc/bcm2835_thermal.h"
> +#include "hw/misc/bcm2835_cprman.h"
>  #include "hw/sd/sdhci.h"
>  #include "hw/sd/bcm2835_sdhost.h"
>  #include "hw/gpio/bcm2835_gpio.h"
>  #include "hw/timer/bcm2835_systmr.h"
>  #include "hw/usb/hcd-dwc2.h"
> @@ -45,11 +46,11 @@ struct BCM2835PeripheralState {
>  
>      BCM2835SystemTimerState systmr;
>      BCM2835MphiState mphi;
>      UnimplementedDeviceState armtmr;
>      UnimplementedDeviceState powermgt;
> -    UnimplementedDeviceState cprman;
> +    BCM2835CprmanState cprman;
>      PL011State uart0;
>      BCM2835AuxState aux;
>      BCM2835FBState fb;
>      BCM2835DMAState dma;
>      BCM2835ICState ic;
> diff --git a/include/hw/misc/bcm2835_cprman.h b/include/hw/misc/bcm2835_cprman.h
> new file mode 100644
> index 0000000000..de9bd01b23
> --- /dev/null
> +++ b/include/hw/misc/bcm2835_cprman.h
> @@ -0,0 +1,37 @@
> +/*
> + * BCM2835 cprman clock manager
> + *
> + * Copyright (c) 2020 Luc Michel <luc@lmichel.fr>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#ifndef HW_MISC_CPRMAN_H
> +#define HW_MISC_CPRMAN_H
> +
> +#include "hw/sysbus.h"
> +#include "hw/qdev-clock.h"
> +
> +#define TYPE_BCM2835_CPRMAN "bcm2835-cprman"
> +
> +typedef struct BCM2835CprmanState BCM2835CprmanState;
> +
> +DECLARE_INSTANCE_CHECKER(BCM2835CprmanState, CPRMAN,
> +                         TYPE_BCM2835_CPRMAN)
> +
> +#define CPRMAN_NUM_REGS (0x2000 / sizeof(uint32_t))
> +
> +struct BCM2835CprmanState {
> +    /*< private >*/
> +    SysBusDevice parent_obj;
> +
> +    /*< public >*/
> +    MemoryRegion iomem;
> +
> +    uint32_t regs[CPRMAN_NUM_REGS];
> +    uint32_t xosc_freq;
> +
> +    Clock *xosc;
> +};
> +
> +#endif
> diff --git a/include/hw/misc/bcm2835_cprman_internals.h b/include/hw/misc/bcm2835_cprman_internals.h
> new file mode 100644
> index 0000000000..6a10b60930
> --- /dev/null
> +++ b/include/hw/misc/bcm2835_cprman_internals.h
> @@ -0,0 +1,24 @@
> +/*
> + * BCM2835 cprman clock manager
> + *
> + * Copyright (c) 2020 Luc Michel <luc@lmichel.fr>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#ifndef HW_MISC_CPRMAN_INTERNALS_H
> +#define HW_MISC_CPRMAN_INTERNALS_H
> +
> +#include "hw/registerfields.h"
> +#include "hw/misc/bcm2835_cprman.h"
> +
> +/* Register map */
> +
> +/*
> + * This field is common to all registers. Each register write value must match
> + * the CPRMAN_PASSWORD magic value in its 8 MSB.
> + */
> +FIELD(CPRMAN, PASSWORD, 24, 8)
> +#define CPRMAN_PASSWORD 0x5a

s/PASSWORD/KEY/?

> +
> +#endif
> diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c
> index f0802c91e0..958aadeeb9 100644
> --- a/hw/arm/bcm2835_peripherals.c
> +++ b/hw/arm/bcm2835_peripherals.c
> @@ -119,10 +119,13 @@ static void bcm2835_peripherals_init(Object *obj)
>      object_initialize_child(obj, "mphi", &s->mphi, TYPE_BCM2835_MPHI);
>  
>      /* DWC2 */
>      object_initialize_child(obj, "dwc2", &s->dwc2, TYPE_DWC2_USB);
>  
> +    /* CPRMAN clock manager */
> +    object_initialize_child(obj, "cprman", &s->cprman, TYPE_BCM2835_CPRMAN);
> +
>      object_property_add_const_link(OBJECT(&s->dwc2), "dma-mr",
>                                     OBJECT(&s->gpu_bus_mr));
>  }
>  
>  static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
> @@ -158,10 +161,17 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
>      /* Interrupt Controller */
>      if (!sysbus_realize(SYS_BUS_DEVICE(&s->ic), errp)) {
>          return;
>      }
>  
> +    /* CPRMAN clock manager */
> +    if (!sysbus_realize(SYS_BUS_DEVICE(&s->cprman), errp)) {
> +        return;
> +    }
> +    memory_region_add_subregion(&s->peri_mr, CPRMAN_OFFSET,
> +                sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->cprman), 0));
> +
>      memory_region_add_subregion(&s->peri_mr, ARMCTRL_IC_OFFSET,
>                  sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->ic), 0));
>      sysbus_pass_irq(SYS_BUS_DEVICE(s), SYS_BUS_DEVICE(&s->ic));
>  
>      /* Sys Timer */
> @@ -343,11 +353,10 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
>          qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
>                                 INTERRUPT_USB));
>  
>      create_unimp(s, &s->armtmr, "bcm2835-sp804", ARMCTRL_TIMER0_1_OFFSET, 0x40);
>      create_unimp(s, &s->powermgt, "bcm2835-powermgt", PM_OFFSET, 0x114);
> -    create_unimp(s, &s->cprman, "bcm2835-cprman", CPRMAN_OFFSET, 0x2000);
>      create_unimp(s, &s->i2s, "bcm2835-i2s", I2S_OFFSET, 0x100);
>      create_unimp(s, &s->smi, "bcm2835-smi", SMI_OFFSET, 0x100);
>      create_unimp(s, &s->spi[0], "bcm2835-spi0", SPI0_OFFSET, 0x20);
>      create_unimp(s, &s->bscsl, "bcm2835-spis", BSC_SL_OFFSET, 0x100);
>      create_unimp(s, &s->i2c[0], "bcm2835-i2c0", BSC0_OFFSET, 0x20);
> diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c
> new file mode 100644
> index 0000000000..d2ea0c9236
> --- /dev/null
> +++ b/hw/misc/bcm2835_cprman.c
> @@ -0,0 +1,171 @@
> +/*
> + * BCM2835 cprman clock manager
> + *
> + * Copyright (c) 2020 Luc Michel <luc@lmichel.fr>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +/*
> + * This peripheral is roughly divided into 3 main parts:
> + *   - the PLLs
> + *   - the PLL channels
> + *   - the clock muxes
> + *
> + * A main oscillator (xosc) feeds all the PLLs. Each PLLs has one or more
> + * channels. Those channel are then connected to the clock muxes. Each mux has
> + * multiples sources (usually the xosc, some of the PLL channels and some "test
> + * debug" clocks). It can selects one or the other through a control register.

"It" is unclear (to me) in this sentence. Assuming the mux.

> + * Each mux has one output clock that also goes out of the CPRMAN. It usually

Here is "It" the mux or the output clock? Assuming the mux.

> + * connects to another peripheral in the SoC (so a given mux is dedicated to a
> + * peripheral).
> + *
> + * At each level (PLL, channel and mux), the clock can be altered through
> + * dividers (and multipliers in case of the PLLs), and can be disabled (in this
> + * case, the next levels see no clock).
> + *
> + * This can be sum-up as follows (this is an example and not the actual BCM2835
> + * clock tree):
> + *
> + *          /-->[PLL]-|->[PLL channel]--...            [mux]--> to peripherals
> + *          |         |->[PLL channel]  muxes takes    [mux]
> + *          |         \->[PLL channel]  inputs from    [mux]
> + *          |                           some channels  [mux]
> + * [xosc]---|-->[PLL]-|->[PLL channel]  and other srcs [mux]
> + *          |         \->[PLL channel]           ...-->[mux]
> + *          |                                          [mux]
> + *          \-->[PLL]--->[PLL channel]                 [mux]
> + *
> + * The page at https://elinux.org/The_Undocumented_Pi gives the actual clock
> + * tree configuration.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/log.h"
> +#include "migration/vmstate.h"
> +#include "hw/qdev-properties.h"
> +#include "hw/misc/bcm2835_cprman.h"
> +#include "hw/misc/bcm2835_cprman_internals.h"
> +#include "trace.h"
> +
> +/* CPRMAN "top level" model */
> +
> +static uint64_t cprman_read(void *opaque, hwaddr offset,
> +                                    unsigned size)

Indent off.

> +{
> +    BCM2835CprmanState *s = CPRMAN(opaque);
> +    uint64_t r = 0;
> +    size_t idx = offset / sizeof(uint32_t);
> +
> +    switch (idx) {
> +    default:
> +        r = s->regs[idx];
> +    }
> +
> +    trace_bcm2835_cprman_read(offset, r);
> +    return r;
> +}
> +
> +static void cprman_write(void *opaque, hwaddr offset,
> +                         uint64_t value, unsigned size)
> +{
> +    BCM2835CprmanState *s = CPRMAN(opaque);
> +    size_t idx = offset / sizeof(uint32_t);
> +
> +    if (FIELD_EX32(value, CPRMAN, PASSWORD) != CPRMAN_PASSWORD) {
> +        trace_bcm2835_cprman_write_invalid_magic(offset, value);
> +        return;
> +    }
> +
> +    value &= ~R_CPRMAN_PASSWORD_MASK;
> +
> +    trace_bcm2835_cprman_write(offset, value);
> +    s->regs[idx] = value;
> +
> +}
> +
> +static const MemoryRegionOps cprman_ops = {
> +    .read = cprman_read,
> +    .write = cprman_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid      = {
> +        .min_access_size        = 4,
> +        .max_access_size        = 4,

I couldn't find this in the public datasheets (any pointer?).

Since your implementation is 32bit, can you explicit .impl
min/max = 4?

> +        .unaligned              = false,
> +    },
> +};
> +
> +static void cprman_reset(DeviceState *dev)
> +{
> +    BCM2835CprmanState *s = CPRMAN(dev);
> +
> +    memset(s->regs, 0, sizeof(s->regs));
> +
> +    clock_update_hz(s->xosc, s->xosc_freq);
> +}
> +
> +static Clock *init_internal_clock(BCM2835CprmanState *s,
> +                                  const char *name)

Interesting. Shouldn't this be a public function from the
Clock API? (Taking Object + name arguments)?

> +{
> +    Object *obj;
> +    Clock *clk;
> +
> +    obj = object_new(TYPE_CLOCK);
> +    object_property_add_child(OBJECT(s), name, obj);
> +    object_unref(obj);
> +
> +    clk = CLOCK(obj);
> +    clock_setup_canonical_path(clk);
> +
> +    return clk;
> +}
> +
> +static void cprman_init(Object *obj)
> +{
> +    BCM2835CprmanState *s = CPRMAN(obj);
> +
> +    s->xosc = init_internal_clock(s, "xosc");
> +
> +    memory_region_init_io(&s->iomem, obj, &cprman_ops,
> +                          s, "bcm2835-cprman", 0x2000);

Again assuming this is a 8KB MMIO device:

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

> +    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
> +}
> +
> +static const VMStateDescription cprman_vmstate = {
> +    .name = TYPE_BCM2835_CPRMAN,
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32_ARRAY(regs, BCM2835CprmanState, CPRMAN_NUM_REGS),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static Property cprman_properties[] = {
> +    DEFINE_PROP_UINT32("xosc-freq", BCM2835CprmanState, xosc_freq, 19200000),

Eventually "xosc-freq-hz".

> +    DEFINE_PROP_END_OF_LIST()
> +};
> +
> +static void cprman_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->reset = cprman_reset;
> +    dc->vmsd = &cprman_vmstate;
> +    device_class_set_props(dc, cprman_properties);
> +}
> +
> +static const TypeInfo cprman_info = {
> +    .name = TYPE_BCM2835_CPRMAN,
> +    .parent = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(BCM2835CprmanState),
> +    .class_init = cprman_class_init,
> +    .instance_init = cprman_init,
> +};
> +
> +static void cprman_register_types(void)
> +{
> +    type_register_static(&cprman_info);
> +}
> +
> +type_init(cprman_register_types);
> diff --git a/hw/misc/meson.build b/hw/misc/meson.build
> index 793d45b1dc..c94cf70e82 100644
> --- a/hw/misc/meson.build
> +++ b/hw/misc/meson.build
> @@ -71,10 +71,11 @@ softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files(
>    'bcm2835_mbox.c',
>    'bcm2835_mphi.c',
>    'bcm2835_property.c',
>    'bcm2835_rng.c',
>    'bcm2835_thermal.c',
> +  'bcm2835_cprman.c',
>  ))
>  softmmu_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_misc.c'))
>  softmmu_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq_slcr.c', 'zynq-xadc.c'))
>  softmmu_ss.add(when: 'CONFIG_STM32F2XX_SYSCFG', if_true: files('stm32f2xx_syscfg.c'))
>  softmmu_ss.add(when: 'CONFIG_STM32F4XX_SYSCFG', if_true: files('stm32f4xx_syscfg.c'))
> diff --git a/hw/misc/trace-events b/hw/misc/trace-events
> index 6054f9adf3..d718a2b177 100644
> --- a/hw/misc/trace-events
> +++ b/hw/misc/trace-events
> @@ -224,5 +224,10 @@ grlib_ahb_pnp_read(uint64_t addr, uint32_t value) "AHB PnP read addr:0x%03"PRIx6
>  grlib_apb_pnp_read(uint64_t addr, uint32_t value) "APB PnP read addr:0x%03"PRIx64" data:0x%08x"
>  
>  # pca9552.c
>  pca955x_gpio_status(const char *description, const char *buf) "%s GPIOs 0-15 [%s]"
>  pca955x_gpio_change(const char *description, unsigned id, unsigned prev_state, unsigned current_state) "%s GPIO id:%u status: %u -> %u"
> +
> +# bcm2835_cprman.c
> +bcm2835_cprman_read(uint64_t offset, uint64_t value) "offset:0x%" PRIx64 " value:0x%" PRIx64
> +bcm2835_cprman_write(uint64_t offset, uint64_t value) "offset:0x%" PRIx64 " value:0x%" PRIx64
> +bcm2835_cprman_write_invalid_magic(uint64_t offset, uint64_t value) "offset:0x%" PRIx64 " value:0x%" PRIx64
> 


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 05/14] hw/misc/bcm2835_cprman: add a PLL skeleton implementation
  2020-09-25 10:17 ` [PATCH 05/14] hw/misc/bcm2835_cprman: add a PLL skeleton implementation Luc Michel
@ 2020-09-26 21:17   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 41+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-09-26 21:17 UTC (permalink / raw)
  To: Luc Michel, qemu-devel; +Cc: Peter Maydell, qemu-arm, Andrew Baumann

On 9/25/20 12:17 PM, Luc Michel wrote:
> There is 5 PLLs in the cprman, namely PLL A, C, D, H and B. All of them
> take the xosc clock as input and produce a new clock.

Typo "There are"?

> 
> This commit adds a skeleton implementation for the PLLs as sub-devices
> of the cprman. The PLLs are instantiated and connected internally to the
> main oscillator.

"CPRMAN".

> 
> Each PLL has 6 registers : cm, a2w_ctrl, a2w_ana[0,1,2,3], a2w_frac. A
> write to any of them triggers a call to the (not yet implemented)
> pll_update function.

[new line]

> If the main oscillator changes frequency, an update
> is also triggered.
> 
> Signed-off-by: Luc Michel <luc@lmichel.fr>
> ---
>  include/hw/misc/bcm2835_cprman.h           |  29 +++++
>  include/hw/misc/bcm2835_cprman_internals.h | 144 +++++++++++++++++++++
>  hw/misc/bcm2835_cprman.c                   | 108 ++++++++++++++++
>  3 files changed, 281 insertions(+)
> 
> diff --git a/include/hw/misc/bcm2835_cprman.h b/include/hw/misc/bcm2835_cprman.h
> index de9bd01b23..f186ab0104 100644
> --- a/include/hw/misc/bcm2835_cprman.h
> +++ b/include/hw/misc/bcm2835_cprman.h
> @@ -19,17 +19,46 @@ typedef struct BCM2835CprmanState BCM2835CprmanState;
>  DECLARE_INSTANCE_CHECKER(BCM2835CprmanState, CPRMAN,
>                           TYPE_BCM2835_CPRMAN)
>  
>  #define CPRMAN_NUM_REGS (0x2000 / sizeof(uint32_t))
>  
> +typedef enum CprmanPLL {
> +    CPRMAN_PLLA = 0,
> +    CPRMAN_PLLC,
> +    CPRMAN_PLLD,
> +    CPRMAN_PLLH,
> +    CPRMAN_PLLB,
> +
> +    CPRMAN_NUM_PLL
> +} CprmanPLL;

CamelCase "CprmanPll"?

> +
> +typedef struct CprmanPLLState {

"CprmanPllState"?

> +    /*< private >*/
> +    DeviceState parent_obj;
> +
> +    /*< public >*/
> +    CprmanPLL id;
> +
> +    uint32_t *reg_cm;
> +    uint32_t *reg_a2w_ctrl;
> +    uint32_t *reg_a2w_ana;
> +    uint32_t prediv_mask; /* prediv bit in ana[1] */
> +    uint32_t *reg_a2w_frac;
> +
> +    Clock *xosc_in;
> +    Clock *out;
> +} CprmanPLLState;
> +
>  struct BCM2835CprmanState {
>      /*< private >*/
>      SysBusDevice parent_obj;
>  
>      /*< public >*/
>      MemoryRegion iomem;
>  
> +    CprmanPLLState plls[CPRMAN_NUM_PLL];
> +
>      uint32_t regs[CPRMAN_NUM_REGS];
>      uint32_t xosc_freq;
>  
>      Clock *xosc;
>  };
> diff --git a/include/hw/misc/bcm2835_cprman_internals.h b/include/hw/misc/bcm2835_cprman_internals.h
> index 6a10b60930..5cfa849492 100644
> --- a/include/hw/misc/bcm2835_cprman_internals.h
> +++ b/include/hw/misc/bcm2835_cprman_internals.h
> @@ -10,15 +10,159 @@
>  #define HW_MISC_CPRMAN_INTERNALS_H
>  
>  #include "hw/registerfields.h"
>  #include "hw/misc/bcm2835_cprman.h"
>  
> +#define TYPE_CPRMAN_PLL "bcm2835-cprman-pll"
> +
> +DECLARE_INSTANCE_CHECKER(CprmanPLLState, CPRMAN_PLL,
> +                         TYPE_CPRMAN_PLL)
> +
>  /* Register map */
>  
> +/* PLLs */
> +REG32(CM_PLLA, 0x104)
> +    FIELD(CM_PLLA, LOADDSI0, 0, 1)
> +    FIELD(CM_PLLA, HOLDDSI0, 1, 1)
> +    FIELD(CM_PLLA, LOADCCP2, 2, 1)
> +    FIELD(CM_PLLA, HOLDCCP2, 3, 1)
> +    FIELD(CM_PLLA, LOADCORE, 4, 1)
> +    FIELD(CM_PLLA, HOLDCORE, 5, 1)
> +    FIELD(CM_PLLA, LOADPER, 6, 1)
> +    FIELD(CM_PLLA, HOLDPER, 7, 1)
> +    FIELD(CM_PLLx, ANARST, 8, 1)
> +REG32(CM_PLLC, 0x108)
> +    FIELD(CM_PLLC, LOADCORE0, 0, 1)
> +    FIELD(CM_PLLC, HOLDCORE0, 1, 1)
> +    FIELD(CM_PLLC, LOADCORE1, 2, 1)
> +    FIELD(CM_PLLC, HOLDCORE1, 3, 1)
> +    FIELD(CM_PLLC, LOADCORE2, 4, 1)
> +    FIELD(CM_PLLC, HOLDCORE2, 5, 1)
> +    FIELD(CM_PLLC, LOADPER, 6, 1)
> +    FIELD(CM_PLLC, HOLDPER, 7, 1)
> +REG32(CM_PLLD, 0x10c)
> +    FIELD(CM_PLLD, LOADDSI0, 0, 1)
> +    FIELD(CM_PLLD, HOLDDSI0, 1, 1)
> +    FIELD(CM_PLLD, LOADDSI1, 2, 1)
> +    FIELD(CM_PLLD, HOLDDSI1, 3, 1)
> +    FIELD(CM_PLLD, LOADCORE, 4, 1)
> +    FIELD(CM_PLLD, HOLDCORE, 5, 1)
> +    FIELD(CM_PLLD, LOADPER, 6, 1)
> +    FIELD(CM_PLLD, HOLDPER, 7, 1)
> +REG32(CM_PLLH, 0x110)
> +    FIELD(CM_PLLH, LOADPIX, 0, 1)
> +    FIELD(CM_PLLH, LOADAUX, 1, 1)
> +    FIELD(CM_PLLH, LOADRCAL, 2, 1)
> +REG32(CM_PLLB, 0x170)
> +    FIELD(CM_PLLB, LOADARM, 0, 1)
> +    FIELD(CM_PLLB, HOLDARM, 1, 1)
> +
> +REG32(A2W_PLLA_CTRL, 0x1100)
> +    FIELD(A2W_PLLx_CTRL, NDIV, 0, 10)
> +    FIELD(A2W_PLLx_CTRL, PDIV, 12, 3)
> +    FIELD(A2W_PLLx_CTRL, PWRDN, 16, 1)
> +    FIELD(A2W_PLLx_CTRL, PRST_DISABLE, 17, 1)
> +REG32(A2W_PLLC_CTRL, 0x1120)
> +REG32(A2W_PLLD_CTRL, 0x1140)
> +REG32(A2W_PLLH_CTRL, 0x1160)
> +REG32(A2W_PLLB_CTRL, 0x11e0)
> +
> +REG32(A2W_PLLA_ANA0, 0x1010)
> +REG32(A2W_PLLA_ANA1, 0x1014)
> +    FIELD(A2W_PLLx_ANA1, FB_PREDIV, 14, 1)
> +REG32(A2W_PLLA_ANA2, 0x1018)
> +REG32(A2W_PLLA_ANA3, 0x101c)
> +
> +REG32(A2W_PLLC_ANA0, 0x1030)
> +REG32(A2W_PLLC_ANA1, 0x1034)
> +REG32(A2W_PLLC_ANA2, 0x1038)
> +REG32(A2W_PLLC_ANA3, 0x103c)
> +
> +REG32(A2W_PLLD_ANA0, 0x1050)
> +REG32(A2W_PLLD_ANA1, 0x1054)
> +REG32(A2W_PLLD_ANA2, 0x1058)
> +REG32(A2W_PLLD_ANA3, 0x105c)
> +
> +REG32(A2W_PLLH_ANA0, 0x1070)
> +REG32(A2W_PLLH_ANA1, 0x1074)
> +    FIELD(A2W_PLLH_ANA1, FB_PREDIV, 11, 1)
> +REG32(A2W_PLLH_ANA2, 0x1078)
> +REG32(A2W_PLLH_ANA3, 0x107c)
> +
> +REG32(A2W_PLLB_ANA0, 0x10f0)
> +REG32(A2W_PLLB_ANA1, 0x10f4)
> +REG32(A2W_PLLB_ANA2, 0x10f8)
> +REG32(A2W_PLLB_ANA3, 0x10fc)
> +
> +REG32(A2W_PLLA_FRAC, 0x1200)
> +    FIELD(A2W_PLLx_FRAC, FRAC, 0, 20)
> +REG32(A2W_PLLC_FRAC, 0x1220)
> +REG32(A2W_PLLD_FRAC, 0x1240)
> +REG32(A2W_PLLH_FRAC, 0x1260)
> +REG32(A2W_PLLB_FRAC, 0x12e0)
> +
>  /*
>   * This field is common to all registers. Each register write value must match
>   * the CPRMAN_PASSWORD magic value in its 8 MSB.
>   */
>  FIELD(CPRMAN, PASSWORD, 24, 8)
>  #define CPRMAN_PASSWORD 0x5a
>  
> +/* PLL init info */
> +typedef struct PLLInitInfo {
> +    const char *name;
> +    size_t cm_offset;
> +    size_t a2w_ctrl_offset;
> +    size_t a2w_ana_offset;
> +    uint32_t prediv_mask; /* Prediv bit in ana[1] */
> +    size_t a2w_frac_offset;
> +} PLLInitInfo;
> +
> +#define FILL_PLL_INIT_INFO(pll_)                \
> +    .cm_offset = R_CM_ ## pll_,                 \
> +    .a2w_ctrl_offset = R_A2W_ ## pll_ ## _CTRL, \
> +    .a2w_ana_offset = R_A2W_ ## pll_ ## _ANA0,  \
> +    .a2w_frac_offset = R_A2W_ ## pll_ ## _FRAC
> +
> +static const PLLInitInfo PLL_INIT_INFO[] = {
> +    [CPRMAN_PLLA] = {
> +        .name = "plla",
> +        .prediv_mask = R_A2W_PLLx_ANA1_FB_PREDIV_MASK,
> +        FILL_PLL_INIT_INFO(PLLA),
> +    },
> +    [CPRMAN_PLLC] = {
> +        .name = "pllc",
> +        .prediv_mask = R_A2W_PLLx_ANA1_FB_PREDIV_MASK,
> +        FILL_PLL_INIT_INFO(PLLC),
> +    },
> +    [CPRMAN_PLLD] = {
> +        .name = "plld",
> +        .prediv_mask = R_A2W_PLLx_ANA1_FB_PREDIV_MASK,
> +        FILL_PLL_INIT_INFO(PLLD),
> +    },
> +    [CPRMAN_PLLH] = {
> +        .name = "pllh",
> +        .prediv_mask = R_A2W_PLLH_ANA1_FB_PREDIV_MASK,
> +        FILL_PLL_INIT_INFO(PLLH),
> +    },
> +    [CPRMAN_PLLB] = {
> +        .name = "pllb",
> +        .prediv_mask = R_A2W_PLLx_ANA1_FB_PREDIV_MASK,
> +        FILL_PLL_INIT_INFO(PLLB),
> +    },
> +};
> +
> +#undef FILL_PLL_CHANNEL_INIT_INFO
> +
> +static inline void set_pll_init_info(BCM2835CprmanState *s,
> +                                     CprmanPLLState *pll,
> +                                     CprmanPLL id)
> +{
> +    pll->id = id;
> +    pll->reg_cm = &s->regs[PLL_INIT_INFO[id].cm_offset];
> +    pll->reg_a2w_ctrl = &s->regs[PLL_INIT_INFO[id].a2w_ctrl_offset];
> +    pll->reg_a2w_ana = &s->regs[PLL_INIT_INFO[id].a2w_ana_offset];
> +    pll->prediv_mask = PLL_INIT_INFO[id].prediv_mask;
> +    pll->reg_a2w_frac = &s->regs[PLL_INIT_INFO[id].a2w_frac_offset];
> +}
> +
>  #endif
> diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c
> index d2ea0c9236..ad71d30a86 100644
> --- a/hw/misc/bcm2835_cprman.c
> +++ b/hw/misc/bcm2835_cprman.c
> @@ -46,10 +46,56 @@
>  #include "hw/qdev-properties.h"
>  #include "hw/misc/bcm2835_cprman.h"
>  #include "hw/misc/bcm2835_cprman_internals.h"
>  #include "trace.h"
>  
> +/* PLL */
> +
> +static void pll_update(CprmanPLLState *pll)
> +{
> +    clock_update(pll->out, 0);
> +}
> +
> +static void pll_xosc_update(void *opaque)
> +{
> +    pll_update(CPRMAN_PLL(opaque));
> +}
> +
> +static void pll_init(Object *obj)
> +{
> +    CprmanPLLState *s = CPRMAN_PLL(obj);
> +
> +    s->xosc_in = qdev_init_clock_in(DEVICE(s), "xosc-in", pll_xosc_update, s);
> +    s->out = qdev_init_clock_out(DEVICE(s), "out");
> +}
> +
> +static const VMStateDescription pll_vmstate = {
> +    .name = TYPE_CPRMAN_PLL,
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_CLOCK(xosc_in, CprmanPLLState),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static void pll_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->vmsd = &pll_vmstate;
> +}
> +
> +static const TypeInfo cprman_pll_info = {
> +    .name = TYPE_CPRMAN_PLL,
> +    .parent = TYPE_DEVICE,
> +    .instance_size = sizeof(CprmanPLLState),
> +    .class_init = pll_class_init,
> +    .instance_init = pll_init,
> +};
> +
> +
>  /* CPRMAN "top level" model */
>  
>  static uint64_t cprman_read(void *opaque, hwaddr offset,
>                                      unsigned size)
>  {
> @@ -64,10 +110,19 @@ static uint64_t cprman_read(void *opaque, hwaddr offset,
>  
>      trace_bcm2835_cprman_read(offset, r);
>      return r;
>  }
>  
> +#define CASE_PLL_REGS(pll_)       \
> +    case R_CM_ ## pll_:           \
> +    case R_A2W_ ## pll_ ## _CTRL: \
> +    case R_A2W_ ## pll_ ## _ANA0: \
> +    case R_A2W_ ## pll_ ## _ANA1: \
> +    case R_A2W_ ## pll_ ## _ANA2: \
> +    case R_A2W_ ## pll_ ## _ANA3: \
> +    case R_A2W_ ## pll_ ## _FRAC
> +
>  static void cprman_write(void *opaque, hwaddr offset,
>                           uint64_t value, unsigned size)
>  {
>      BCM2835CprmanState *s = CPRMAN(opaque);
>      size_t idx = offset / sizeof(uint32_t);
> @@ -80,12 +135,35 @@ static void cprman_write(void *opaque, hwaddr offset,
>      value &= ~R_CPRMAN_PASSWORD_MASK;
>  
>      trace_bcm2835_cprman_write(offset, value);
>      s->regs[idx] = value;
>  
> +    switch (idx) {
> +    CASE_PLL_REGS(PLLA) :
> +        pll_update(&s->plls[CPRMAN_PLLA]);
> +        break;
> +
> +    CASE_PLL_REGS(PLLC) :
> +        pll_update(&s->plls[CPRMAN_PLLC]);
> +        break;
> +
> +    CASE_PLL_REGS(PLLD) :
> +        pll_update(&s->plls[CPRMAN_PLLD]);
> +        break;
> +
> +    CASE_PLL_REGS(PLLH) :
> +        pll_update(&s->plls[CPRMAN_PLLH]);
> +        break;
> +
> +    CASE_PLL_REGS(PLLB) :
> +        pll_update(&s->plls[CPRMAN_PLLB]);
> +        break;
> +    }
>  }
>  
> +#undef CASE_PLL_REGS
> +
>  static const MemoryRegionOps cprman_ops = {
>      .read = cprman_read,
>      .write = cprman_write,
>      .endianness = DEVICE_LITTLE_ENDIAN,
>      .valid      = {
> @@ -96,13 +174,18 @@ static const MemoryRegionOps cprman_ops = {
>  };
>  
>  static void cprman_reset(DeviceState *dev)
>  {
>      BCM2835CprmanState *s = CPRMAN(dev);
> +    size_t i;
>  
>      memset(s->regs, 0, sizeof(s->regs));
>  
> +    for (i = 0; i < CPRMAN_NUM_PLL; i++) {
> +        device_cold_reset(DEVICE(&s->plls[i]));

Assuming cprman_reset() is cprman_cold_reset().

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

> +    }
> +
>      clock_update_hz(s->xosc, s->xosc_freq);
>  }
>  
>  static Clock *init_internal_clock(BCM2835CprmanState *s,
>                                    const char *name)
> @@ -121,18 +204,41 @@ static Clock *init_internal_clock(BCM2835CprmanState *s,
>  }
>  
>  static void cprman_init(Object *obj)
>  {
>      BCM2835CprmanState *s = CPRMAN(obj);
> +    size_t i;
> +
> +    for (i = 0; i < CPRMAN_NUM_PLL; i++) {
> +        object_initialize_child(obj, PLL_INIT_INFO[i].name,
> +                                &s->plls[i], TYPE_CPRMAN_PLL);
> +        set_pll_init_info(s, &s->plls[i], i);
> +    }
>  
>      s->xosc = init_internal_clock(s, "xosc");
>  
>      memory_region_init_io(&s->iomem, obj, &cprman_ops,
>                            s, "bcm2835-cprman", 0x2000);
>      sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
>  }
>  
> +static void cprman_realize(DeviceState *dev, Error **errp)
> +{
> +    BCM2835CprmanState *s = CPRMAN(dev);
> +    size_t i;
> +
> +    for (i = 0; i < CPRMAN_NUM_PLL; i++) {
> +        CprmanPLLState *pll = &s->plls[i];
> +
> +        clock_set_source(pll->xosc_in, s->xosc);
> +
> +        if (!qdev_realize(DEVICE(pll), NULL, errp)) {
> +            return;
> +        }
> +    }
> +}
> +
>  static const VMStateDescription cprman_vmstate = {
>      .name = TYPE_BCM2835_CPRMAN,
>      .version_id = 1,
>      .minimum_version_id = 1,
>      .fields = (VMStateField[]) {
> @@ -148,10 +254,11 @@ static Property cprman_properties[] = {
>  
>  static void cprman_class_init(ObjectClass *klass, void *data)
>  {
>      DeviceClass *dc = DEVICE_CLASS(klass);
>  
> +    dc->realize = cprman_realize;
>      dc->reset = cprman_reset;
>      dc->vmsd = &cprman_vmstate;
>      device_class_set_props(dc, cprman_properties);
>  }
>  
> @@ -164,8 +271,9 @@ static const TypeInfo cprman_info = {
>  };
>  
>  static void cprman_register_types(void)
>  {
>      type_register_static(&cprman_info);
> +    type_register_static(&cprman_pll_info);
>  }
>  
>  type_init(cprman_register_types);
> 


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 06/14] hw/misc/bcm2835_cprman: implement PLLs behaviour
  2020-09-25 10:17 ` [PATCH 06/14] hw/misc/bcm2835_cprman: implement PLLs behaviour Luc Michel
@ 2020-09-26 21:26   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 41+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-09-26 21:26 UTC (permalink / raw)
  To: Luc Michel, qemu-devel; +Cc: Peter Maydell, qemu-arm, Andrew Baumann

On 9/25/20 12:17 PM, Luc Michel wrote:
> The cprman PLLs generate a clock based on a prescaler, a multiplier and
> a divider. The prescaler doubles the parent (xosc) frequency, then the
> multiplier/divider are applied. The multiplier has an integer and a
> fractionnal part.

Typo "fractional".

> 
> This commit also implements the cprman CM_LOCK register. This register
> reports which PLL is currently locked. We consider a PLL has being
> locked as soon as it is enabled (on real hardware, there is a delay
> after turning a PLL on, for it to stabilise).

Typo "stabilize".

> 
> Signed-off-by: Luc Michel <luc@lmichel.fr>
> ---
>  include/hw/misc/bcm2835_cprman_internals.h |  8 +++
>  hw/misc/bcm2835_cprman.c                   | 64 +++++++++++++++++++++-
>  2 files changed, 71 insertions(+), 1 deletion(-)
> 
> diff --git a/include/hw/misc/bcm2835_cprman_internals.h b/include/hw/misc/bcm2835_cprman_internals.h
> index 5cfa849492..22a2500ab0 100644
> --- a/include/hw/misc/bcm2835_cprman_internals.h
> +++ b/include/hw/misc/bcm2835_cprman_internals.h
> @@ -98,10 +98,18 @@ REG32(A2W_PLLA_FRAC, 0x1200)
>  REG32(A2W_PLLC_FRAC, 0x1220)
>  REG32(A2W_PLLD_FRAC, 0x1240)
>  REG32(A2W_PLLH_FRAC, 0x1260)
>  REG32(A2W_PLLB_FRAC, 0x12e0)
>  
> +/* misc registers */
> +REG32(CM_LOCK, 0x114)
> +    FIELD(CM_LOCK, FLOCKH, 12, 1)
> +    FIELD(CM_LOCK, FLOCKD, 11, 1)
> +    FIELD(CM_LOCK, FLOCKC, 10, 1)
> +    FIELD(CM_LOCK, FLOCKB, 9, 1)
> +    FIELD(CM_LOCK, FLOCKA, 8, 1)
> +
>  /*
>   * This field is common to all registers. Each register write value must match
>   * the CPRMAN_PASSWORD magic value in its 8 MSB.
>   */
>  FIELD(CPRMAN, PASSWORD, 24, 8)
> diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c
> index ad71d30a86..ba82522eb1 100644
> --- a/hw/misc/bcm2835_cprman.c
> +++ b/hw/misc/bcm2835_cprman.c
> @@ -48,13 +48,51 @@
>  #include "hw/misc/bcm2835_cprman_internals.h"
>  #include "trace.h"
>  
>  /* PLL */
>  
> +static bool pll_is_locked(const CprmanPLLState *pll)
> +{
> +    return !FIELD_EX32(*pll->reg_a2w_ctrl, A2W_PLLx_CTRL, PWRDN)
> +        && !FIELD_EX32(*pll->reg_cm, CM_PLLx, ANARST);
> +}
> +
>  static void pll_update(CprmanPLLState *pll)
>  {
> -    clock_update(pll->out, 0);
> +    uint64_t freq, ndiv, fdiv, pdiv;
> +
> +    if (!pll_is_locked(pll)) {
> +        clock_update(pll->out, 0);
> +        return;
> +    }
> +
> +    pdiv = FIELD_EX32(*pll->reg_a2w_ctrl, A2W_PLLx_CTRL, PDIV);
> +
> +    if (!pdiv) {
> +        clock_update(pll->out, 0);
> +        return;
> +    }
> +
> +    ndiv = FIELD_EX32(*pll->reg_a2w_ctrl, A2W_PLLx_CTRL, NDIV);
> +    fdiv = FIELD_EX32(*pll->reg_a2w_frac, A2W_PLLx_FRAC, FRAC);
> +
> +    if (pll->reg_a2w_ana[1] & pll->prediv_mask) {
> +        /* The prescaler doubles the parent frequency */
> +        ndiv *= 2;
> +        fdiv *= 2;
> +    }
> +
> +    /*
> +     * We have a multiplier with an integer part (ndiv) and a fractional part
> +     * (fdiv), and a divider (pdiv).
> +     */
> +    freq = clock_get_hz(pll->xosc_in) *
> +        ((ndiv << R_A2W_PLLx_FRAC_FRAC_LENGTH) + fdiv);
> +    freq /= pdiv;
> +    freq >>= R_A2W_PLLx_FRAC_FRAC_LENGTH;

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

> +
> +    clock_update_hz(pll->out, freq);
>  }
>  
>  static void pll_xosc_update(void *opaque)
>  {
>      pll_update(CPRMAN_PLL(opaque));
> @@ -94,18 +132,42 @@ static const TypeInfo cprman_pll_info = {
>  };
>  
>  
>  /* CPRMAN "top level" model */
>  
> +static uint32_t get_cm_lock(const BCM2835CprmanState *s)
> +{
> +    static const int CM_LOCK_MAPPING[] = {

Maybe CM_LOCK_MAPPING[CPRMAN_NUM_PLL].

> +        [CPRMAN_PLLA] = R_CM_LOCK_FLOCKA_SHIFT,
> +        [CPRMAN_PLLC] = R_CM_LOCK_FLOCKC_SHIFT,
> +        [CPRMAN_PLLD] = R_CM_LOCK_FLOCKD_SHIFT,
> +        [CPRMAN_PLLH] = R_CM_LOCK_FLOCKH_SHIFT,
> +        [CPRMAN_PLLB] = R_CM_LOCK_FLOCKB_SHIFT,
> +    };
> +
> +    uint32_t r = 0;
> +    size_t i;
> +
> +    for (i = 0; i < CPRMAN_NUM_PLL; i++) {
> +        r |= pll_is_locked(&s->plls[i]) << CM_LOCK_MAPPING[i];
> +    }
> +
> +    return r;
> +}
> +
>  static uint64_t cprman_read(void *opaque, hwaddr offset,
>                                      unsigned size)
>  {
>      BCM2835CprmanState *s = CPRMAN(opaque);
>      uint64_t r = 0;
>      size_t idx = offset / sizeof(uint32_t);
>  
>      switch (idx) {
> +    case R_CM_LOCK:
> +        r = get_cm_lock(s);
> +        break;
> +
>      default:
>          r = s->regs[idx];
>      }
>  
>      trace_bcm2835_cprman_read(offset, r);
> 


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 07/14] hw/misc/bcm2835_cprman: add a PLL channel skeleton implementation
  2020-09-25 10:17 ` [PATCH 07/14] hw/misc/bcm2835_cprman: add a PLL channel skeleton implementation Luc Michel
@ 2020-09-26 21:32   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 41+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-09-26 21:32 UTC (permalink / raw)
  To: Luc Michel, qemu-devel; +Cc: Peter Maydell, qemu-arm, Andrew Baumann

On 9/25/20 12:17 PM, Luc Michel wrote:
> PLLs are composed of multiple channels. Each channel outputs one clock
> signal. They are modeled as one device taking the PLL generated clock as
> input, and outputting a new clock.
> 
> A channel shares the cm register with its parent PLL, and has its own

CM.

> a2w_ctrl register. A write to the cm register will trigger an update of

A2W_ctrl? CM.

> the PLL and all its channels, while a write to an a2w_ctrl channel
> register will update the required channel only.
> 
> Signed-off-by: Luc Michel <luc@lmichel.fr>
> ---
>  include/hw/misc/bcm2835_cprman.h           |  44 ++++++
>  include/hw/misc/bcm2835_cprman_internals.h | 146 +++++++++++++++++++
>  hw/misc/bcm2835_cprman.c                   | 155 +++++++++++++++++++--
>  3 files changed, 337 insertions(+), 8 deletions(-)
> 
> diff --git a/include/hw/misc/bcm2835_cprman.h b/include/hw/misc/bcm2835_cprman.h
> index f186ab0104..aaf15fb20c 100644
> --- a/include/hw/misc/bcm2835_cprman.h
> +++ b/include/hw/misc/bcm2835_cprman.h
> @@ -29,10 +29,35 @@ typedef enum CprmanPLL {
>      CPRMAN_PLLB,
>  
>      CPRMAN_NUM_PLL
>  } CprmanPLL;
>  
> +typedef enum CprmanPLLChannel {
> +    CPRMAN_PLLA_CHANNEL_DSI0 = 0,
> +    CPRMAN_PLLA_CHANNEL_CORE,
> +    CPRMAN_PLLA_CHANNEL_PER,
> +    CPRMAN_PLLA_CHANNEL_CCP2,
> +
> +    CPRMAN_PLLC_CHANNEL_CORE2,
> +    CPRMAN_PLLC_CHANNEL_CORE1,
> +    CPRMAN_PLLC_CHANNEL_PER,
> +    CPRMAN_PLLC_CHANNEL_CORE0,
> +
> +    CPRMAN_PLLD_CHANNEL_DSI0,
> +    CPRMAN_PLLD_CHANNEL_CORE,
> +    CPRMAN_PLLD_CHANNEL_PER,
> +    CPRMAN_PLLD_CHANNEL_DSI1,
> +
> +    CPRMAN_PLLH_CHANNEL_AUX,
> +    CPRMAN_PLLH_CHANNEL_RCAL,
> +    CPRMAN_PLLH_CHANNEL_PIX,
> +
> +    CPRMAN_PLLB_CHANNEL_ARM,
> +
> +    CPRMAN_NUM_PLL_CHANNEL,
> +} CprmanPLLChannel;
> +
>  typedef struct CprmanPLLState {
>      /*< private >*/
>      DeviceState parent_obj;
>  
>      /*< public >*/
> @@ -46,18 +71,37 @@ typedef struct CprmanPLLState {
>  
>      Clock *xosc_in;
>      Clock *out;
>  } CprmanPLLState;
>  
> +typedef struct CprmanPLLChannelState {
> +    /*< private >*/
> +    DeviceState parent_obj;
> +
> +    /*< public >*/
> +    CprmanPLLChannel id;
> +    CprmanPLL parent;
> +
> +    uint32_t *reg_cm;
> +    uint32_t hold_mask;
> +    uint32_t load_mask;
> +    uint32_t *reg_a2w_ctrl;
> +    int fixed_divider;
> +
> +    Clock *pll_in;
> +    Clock *out;
> +} CprmanPLLChannelState;
> +
>  struct BCM2835CprmanState {
>      /*< private >*/
>      SysBusDevice parent_obj;
>  
>      /*< public >*/
>      MemoryRegion iomem;
>  
>      CprmanPLLState plls[CPRMAN_NUM_PLL];
> +    CprmanPLLChannelState channels[CPRMAN_NUM_PLL_CHANNEL];
>  
>      uint32_t regs[CPRMAN_NUM_REGS];
>      uint32_t xosc_freq;
>  
>      Clock *xosc;
> diff --git a/include/hw/misc/bcm2835_cprman_internals.h b/include/hw/misc/bcm2835_cprman_internals.h
> index 22a2500ab0..8a5b9aae67 100644
> --- a/include/hw/misc/bcm2835_cprman_internals.h
> +++ b/include/hw/misc/bcm2835_cprman_internals.h
> @@ -11,13 +11,16 @@
>  
>  #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"
>  
>  DECLARE_INSTANCE_CHECKER(CprmanPLLState, CPRMAN_PLL,
>                           TYPE_CPRMAN_PLL)
> +DECLARE_INSTANCE_CHECKER(CprmanPLLChannelState, CPRMAN_PLL_CHANNEL,
> +                         TYPE_CPRMAN_PLL_CHANNEL)
>  
>  /* Register map */
>  
>  /* PLLs */
>  REG32(CM_PLLA, 0x104)
> @@ -98,10 +101,35 @@ REG32(A2W_PLLA_FRAC, 0x1200)
>  REG32(A2W_PLLC_FRAC, 0x1220)
>  REG32(A2W_PLLD_FRAC, 0x1240)
>  REG32(A2W_PLLH_FRAC, 0x1260)
>  REG32(A2W_PLLB_FRAC, 0x12e0)
>  
> +/* PLL channels */
> +REG32(A2W_PLLA_DSI0, 0x1300)
> +    FIELD(A2W_PLLx_CHANNELy, DIV, 0, 8)
> +    FIELD(A2W_PLLx_CHANNELy, DISABLE, 8, 1)
> +REG32(A2W_PLLA_CORE, 0x1400)
> +REG32(A2W_PLLA_PER, 0x1500)
> +REG32(A2W_PLLA_CCP2, 0x1600)
> +
> +REG32(A2W_PLLC_CORE2, 0x1320)
> +REG32(A2W_PLLC_CORE1, 0x1420)
> +REG32(A2W_PLLC_PER, 0x1520)
> +REG32(A2W_PLLC_CORE0, 0x1620)
> +
> +REG32(A2W_PLLD_DSI0, 0x1340)
> +REG32(A2W_PLLD_CORE, 0x1440)
> +REG32(A2W_PLLD_PER, 0x1540)
> +REG32(A2W_PLLD_DSI1, 0x1640)
> +
> +REG32(A2W_PLLH_AUX, 0x1360)
> +REG32(A2W_PLLH_RCAL, 0x1460)
> +REG32(A2W_PLLH_PIX, 0x1560)
> +REG32(A2W_PLLH_STS, 0x1660)
> +
> +REG32(A2W_PLLB_ARM, 0x13e0)
> +
>  /* misc registers */
>  REG32(CM_LOCK, 0x114)
>      FIELD(CM_LOCK, FLOCKH, 12, 1)
>      FIELD(CM_LOCK, FLOCKD, 11, 1)
>      FIELD(CM_LOCK, FLOCKC, 10, 1)
> @@ -171,6 +199,124 @@ static inline void set_pll_init_info(BCM2835CprmanState *s,
>      pll->reg_a2w_ana = &s->regs[PLL_INIT_INFO[id].a2w_ana_offset];
>      pll->prediv_mask = PLL_INIT_INFO[id].prediv_mask;
>      pll->reg_a2w_frac = &s->regs[PLL_INIT_INFO[id].a2w_frac_offset];
>  }
>  
> +
> +/* PLL channel init info */
> +typedef struct PLLChannelInitInfo {
> +    const char *name;
> +    CprmanPLL parent;
> +    size_t cm_offset;
> +    uint32_t cm_hold_mask;
> +    uint32_t cm_load_mask;
> +    size_t a2w_ctrl_offset;
> +    unsigned int fixed_divider;
> +} PLLChannelInitInfo;
> +
> +#define FILL_PLL_CHANNEL_INIT_INFO_common(pll_, channel_)            \
> +    .parent = CPRMAN_ ## pll_,                                       \
> +    .cm_offset = R_CM_ ## pll_,                                      \
> +    .cm_load_mask = R_CM_ ## pll_ ## _ ## LOAD ## channel_ ## _MASK, \
> +    .a2w_ctrl_offset = R_A2W_ ## pll_ ## _ ## channel_
> +
> +#define FILL_PLL_CHANNEL_INIT_INFO(pll_, channel_)                   \
> +    FILL_PLL_CHANNEL_INIT_INFO_common(pll_, channel_),               \
> +    .cm_hold_mask = R_CM_ ## pll_ ## _ ## HOLD ## channel_ ## _MASK, \
> +    .fixed_divider = 1
> +
> +#define FILL_PLL_CHANNEL_INIT_INFO_nohold(pll_, channel_) \
> +    FILL_PLL_CHANNEL_INIT_INFO_common(pll_, channel_),    \
> +    .cm_hold_mask = 0
> +
> +static PLLChannelInitInfo PLL_CHANNEL_INIT_INFO[] = {
> +    [CPRMAN_PLLA_CHANNEL_DSI0] = {
> +        .name = "plla-dsi0",
> +        FILL_PLL_CHANNEL_INIT_INFO(PLLA, DSI0),
> +    },
> +    [CPRMAN_PLLA_CHANNEL_CORE] = {
> +        .name = "plla-core",
> +        FILL_PLL_CHANNEL_INIT_INFO(PLLA, CORE),
> +    },
> +    [CPRMAN_PLLA_CHANNEL_PER] = {
> +        .name = "plla-per",
> +        FILL_PLL_CHANNEL_INIT_INFO(PLLA, PER),
> +    },
> +    [CPRMAN_PLLA_CHANNEL_CCP2] = {
> +        .name = "plla-ccp2",
> +        FILL_PLL_CHANNEL_INIT_INFO(PLLA, CCP2),
> +    },
> +
> +    [CPRMAN_PLLC_CHANNEL_CORE2] = {
> +        .name = "pllc-core2",
> +        FILL_PLL_CHANNEL_INIT_INFO(PLLC, CORE2),
> +    },
> +    [CPRMAN_PLLC_CHANNEL_CORE1] = {
> +        .name = "pllc-core1",
> +        FILL_PLL_CHANNEL_INIT_INFO(PLLC, CORE1),
> +    },
> +    [CPRMAN_PLLC_CHANNEL_PER] = {
> +        .name = "pllc-per",
> +        FILL_PLL_CHANNEL_INIT_INFO(PLLC, PER),
> +    },
> +    [CPRMAN_PLLC_CHANNEL_CORE0] = {
> +        .name = "pllc-core0",
> +        FILL_PLL_CHANNEL_INIT_INFO(PLLC, CORE0),
> +    },
> +
> +    [CPRMAN_PLLD_CHANNEL_DSI0] = {
> +        .name = "plld-dsi0",
> +        FILL_PLL_CHANNEL_INIT_INFO(PLLD, DSI0),
> +    },
> +    [CPRMAN_PLLD_CHANNEL_CORE] = {
> +        .name = "plld-core",
> +        FILL_PLL_CHANNEL_INIT_INFO(PLLD, CORE),
> +    },
> +    [CPRMAN_PLLD_CHANNEL_PER] = {
> +        .name = "plld-per",
> +        FILL_PLL_CHANNEL_INIT_INFO(PLLD, PER),
> +    },
> +    [CPRMAN_PLLD_CHANNEL_DSI1] = {
> +        .name = "plld-dsi1",
> +        FILL_PLL_CHANNEL_INIT_INFO(PLLD, DSI1),
> +    },
> +
> +    [CPRMAN_PLLH_CHANNEL_AUX] = {
> +        .name = "pllh-aux",
> +        .fixed_divider = 1,
> +        FILL_PLL_CHANNEL_INIT_INFO_nohold(PLLH, AUX),
> +    },
> +    [CPRMAN_PLLH_CHANNEL_RCAL] = {
> +        .name = "pllh-rcal",
> +        .fixed_divider = 10,
> +        FILL_PLL_CHANNEL_INIT_INFO_nohold(PLLH, RCAL),
> +    },
> +    [CPRMAN_PLLH_CHANNEL_PIX] = {
> +        .name = "pllh-pix",
> +        .fixed_divider = 10,
> +        FILL_PLL_CHANNEL_INIT_INFO_nohold(PLLH, PIX),
> +    },
> +
> +    [CPRMAN_PLLB_CHANNEL_ARM] = {
> +        .name = "pllb-arm",
> +        FILL_PLL_CHANNEL_INIT_INFO(PLLB, ARM),
> +    },
> +};
> +
> +#undef FILL_PLL_CHANNEL_INIT_INFO_nohold
> +#undef FILL_PLL_CHANNEL_INIT_INFO
> +#undef FILL_PLL_CHANNEL_INIT_INFO_common
> +
> +static inline void set_pll_channel_init_info(BCM2835CprmanState *s,
> +                                             CprmanPLLChannelState *channel,
> +                                             CprmanPLLChannel id)
> +{
> +    channel->id = id;
> +    channel->parent = PLL_CHANNEL_INIT_INFO[id].parent;
> +    channel->reg_cm = &s->regs[PLL_CHANNEL_INIT_INFO[id].cm_offset];
> +    channel->hold_mask = PLL_CHANNEL_INIT_INFO[id].cm_hold_mask;
> +    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;
> +}
> +
>  #endif
> diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c
> index ba82522eb1..2c70a3f317 100644
> --- a/hw/misc/bcm2835_cprman.c
> +++ b/hw/misc/bcm2835_cprman.c
> @@ -130,10 +130,73 @@ static const TypeInfo cprman_pll_info = {
>      .class_init = pll_class_init,
>      .instance_init = pll_init,
>  };
>  
>  
> +/* PLL channel */
> +
> +static void pll_channel_update(CprmanPLLChannelState *channel)
> +{
> +    clock_update(channel->out, 0);
> +}
> +
> +/* Update a PLL and all its channels */
> +static void pll_update_all_channels(BCM2835CprmanState *s,
> +                                    CprmanPLLState *pll)
> +{
> +    size_t i;
> +
> +    pll_update(pll);
> +
> +    for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
> +        CprmanPLLChannelState *channel = &s->channels[i];
> +        if (channel->parent == pll->id) {
> +            pll_channel_update(channel);
> +        }
> +    }
> +}
> +
> +static void pll_channel_pll_in_update(void *opaque)
> +{
> +    pll_channel_update(CPRMAN_PLL_CHANNEL(opaque));
> +}
> +
> +static void pll_channel_init(Object *obj)
> +{
> +    CprmanPLLChannelState *s = CPRMAN_PLL_CHANNEL(obj);
> +
> +    s->pll_in = qdev_init_clock_in(DEVICE(s), "pll-in",
> +                                   pll_channel_pll_in_update, s);
> +    s->out = qdev_init_clock_out(DEVICE(s), "out");
> +}
> +
> +static const VMStateDescription pll_channel_vmstate = {
> +    .name = TYPE_CPRMAN_PLL_CHANNEL,
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_CLOCK(pll_in, CprmanPLLChannelState),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static void pll_channel_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->vmsd = &pll_channel_vmstate;
> +}
> +
> +static const TypeInfo cprman_pll_channel_info = {
> +    .name = TYPE_CPRMAN_PLL_CHANNEL,
> +    .parent = TYPE_DEVICE,
> +    .instance_size = sizeof(CprmanPLLChannelState),
> +    .class_init = pll_channel_class_init,
> +    .instance_init = pll_channel_init,
> +};
> +
> +
>  /* CPRMAN "top level" model */
>  
>  static uint32_t get_cm_lock(const BCM2835CprmanState *s)
>  {
>      static const int CM_LOCK_MAPPING[] = {
> @@ -172,12 +235,36 @@ static uint64_t cprman_read(void *opaque, hwaddr offset,
>  
>      trace_bcm2835_cprman_read(offset, r);
>      return r;
>  }
>  
> -#define CASE_PLL_REGS(pll_)       \
> -    case R_CM_ ## pll_:           \
> +static inline void update_pll_and_channels_from_cm(BCM2835CprmanState *s,
> +                                                   size_t idx)
> +{
> +    size_t i;
> +
> +    for (i = 0; i < CPRMAN_NUM_PLL; i++) {
> +        if (PLL_INIT_INFO[i].cm_offset == idx) {
> +            pll_update_all_channels(s, &s->plls[i]);
> +            return;
> +        }
> +    }
> +}
> +
> +static inline void update_channel_from_a2w(BCM2835CprmanState *s, size_t idx)
> +{
> +    size_t i;
> +
> +    for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
> +        if (PLL_CHANNEL_INIT_INFO[i].a2w_ctrl_offset == idx) {
> +            pll_channel_update(&s->channels[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: \
>      case R_A2W_ ## pll_ ## _ANA3: \
> @@ -198,33 +285,61 @@ static void cprman_write(void *opaque, hwaddr offset,
>  
>      trace_bcm2835_cprman_write(offset, value);
>      s->regs[idx] = value;
>  
>      switch (idx) {
> -    CASE_PLL_REGS(PLLA) :
> +    case R_CM_PLLA ... R_CM_PLLH:
> +    case R_CM_PLLB:
> +        /*
> +         * A given CM_PLLx register is shared by both the PLL and the channels
> +         * of this PLL.
> +         */
> +        update_pll_and_channels_from_cm(s, idx);
> +        break;
> +
> +    CASE_PLL_A2W_REGS(PLLA) :
>          pll_update(&s->plls[CPRMAN_PLLA]);
>          break;
>  
> -    CASE_PLL_REGS(PLLC) :
> +    CASE_PLL_A2W_REGS(PLLC) :
>          pll_update(&s->plls[CPRMAN_PLLC]);
>          break;
>  
> -    CASE_PLL_REGS(PLLD) :
> +    CASE_PLL_A2W_REGS(PLLD) :
>          pll_update(&s->plls[CPRMAN_PLLD]);
>          break;
>  
> -    CASE_PLL_REGS(PLLH) :
> +    CASE_PLL_A2W_REGS(PLLH) :
>          pll_update(&s->plls[CPRMAN_PLLH]);
>          break;
>  
> -    CASE_PLL_REGS(PLLB) :
> +    CASE_PLL_A2W_REGS(PLLB) :
>          pll_update(&s->plls[CPRMAN_PLLB]);
>          break;
> +
> +    case R_A2W_PLLA_DSI0:
> +    case R_A2W_PLLA_CORE:
> +    case R_A2W_PLLA_PER:
> +    case R_A2W_PLLA_CCP2:
> +    case R_A2W_PLLC_CORE2:
> +    case R_A2W_PLLC_CORE1:
> +    case R_A2W_PLLC_PER:
> +    case R_A2W_PLLC_CORE0:
> +    case R_A2W_PLLD_DSI0:
> +    case R_A2W_PLLD_CORE:
> +    case R_A2W_PLLD_PER:
> +    case R_A2W_PLLD_DSI1:
> +    case R_A2W_PLLH_AUX:
> +    case R_A2W_PLLH_RCAL:
> +    case R_A2W_PLLH_PIX:
> +    case R_A2W_PLLB_ARM:
> +        update_channel_from_a2w(s, idx);
> +        break;
>      }
>  }
>  
> -#undef CASE_PLL_REGS
> +#undef CASE_PLL_A2W_REGS
>  
>  static const MemoryRegionOps cprman_ops = {
>      .read = cprman_read,
>      .write = cprman_write,
>      .endianness = DEVICE_LITTLE_ENDIAN,
> @@ -244,10 +359,14 @@ static void cprman_reset(DeviceState *dev)
>  
>      for (i = 0; i < CPRMAN_NUM_PLL; i++) {
>          device_cold_reset(DEVICE(&s->plls[i]));
>      }
>  
> +    for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
> +        device_cold_reset(DEVICE(&s->channels[i]));
> +    }
> +
>      clock_update_hz(s->xosc, s->xosc_freq);
>  }
>  
>  static Clock *init_internal_clock(BCM2835CprmanState *s,
>                                    const char *name)
> @@ -274,10 +393,17 @@ static void cprman_init(Object *obj)
>          object_initialize_child(obj, PLL_INIT_INFO[i].name,
>                                  &s->plls[i], TYPE_CPRMAN_PLL);
>          set_pll_init_info(s, &s->plls[i], i);
>      }
>  
> +    for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
> +        object_initialize_child(obj, PLL_CHANNEL_INIT_INFO[i].name,
> +                                &s->channels[i],
> +                                TYPE_CPRMAN_PLL_CHANNEL);
> +        set_pll_channel_init_info(s, &s->channels[i], i);
> +    }
> +
>      s->xosc = init_internal_clock(s, "xosc");
>  
>      memory_region_init_io(&s->iomem, obj, &cprman_ops,
>                            s, "bcm2835-cprman", 0x2000);
>      sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
> @@ -295,10 +421,22 @@ static void cprman_realize(DeviceState *dev, Error **errp)
>  
>          if (!qdev_realize(DEVICE(pll), NULL, errp)) {
>              return;
>          }
>      }
> +
> +    for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
> +        CprmanPLLChannelState *channel = &s->channels[i];
> +        CprmanPLL parent = PLL_CHANNEL_INIT_INFO[i].parent;
> +        Clock *parent_clk = s->plls[parent].out;
> +
> +        clock_set_source(channel->pll_in, parent_clk);
> +
> +        if (!qdev_realize(DEVICE(channel), NULL, errp)) {
> +            return;
> +        }
> +    }
>  }
>  
>  static const VMStateDescription cprman_vmstate = {
>      .name = TYPE_BCM2835_CPRMAN,
>      .version_id = 1,
> @@ -334,8 +472,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_init(cprman_register_types);
> 

Chapeau!

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 08/14] hw/misc/bcm2835_cprman: implement PLL channels behaviour
  2020-09-25 10:17 ` [PATCH 08/14] hw/misc/bcm2835_cprman: implement PLL channels behaviour Luc Michel
@ 2020-09-26 21:36   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 41+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-09-26 21:36 UTC (permalink / raw)
  To: Luc Michel, qemu-devel
  Cc: Peter Maydell, Andrei Warkentin, Andrew Baumann, qemu-arm,
	Pete Batard, Samer El-Haj-Mahmoud, Ard Biesheuvel

On 9/25/20 12:17 PM, Luc Michel wrote:
> A PLL channel is able to further divide the generated PLL frequency. The

You can move the 'The' to the next line =)

> divider is given in the ctrl_a2w register. Some channels have a

s/a/an/

> additional fixed divider which is always applied to the signal.
> 
> Signed-off-by: Luc Michel <luc@lmichel.fr>
> ---
>  hw/misc/bcm2835_cprman.c | 33 ++++++++++++++++++++++++++++++++-
>  1 file changed, 32 insertions(+), 1 deletion(-)
> 
> diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c
> index 2c70a3f317..e644aeb2b5 100644
> --- a/hw/misc/bcm2835_cprman.c
> +++ b/hw/misc/bcm2835_cprman.c
> @@ -132,13 +132,44 @@ static const TypeInfo cprman_pll_info = {
>  };
>  
>  
>  /* PLL channel */
>  
> +static bool pll_channel_is_enabled(CprmanPLLChannelState *channel)
> +{
> +    /*
> +     * XXX I'm not sure of the purpose of the LOAD field. The Linux driver does
> +     * not set it when enabling the channel, but does clear it when disabling
> +     * it.

Cc'ed firmware developers who might have a clue.

> +     */
> +    return !FIELD_EX32(*channel->reg_a2w_ctrl, A2W_PLLx_CHANNELy, DISABLE)
> +        && !(*channel->reg_cm & channel->hold_mask);
> +}
> +
>  static void pll_channel_update(CprmanPLLChannelState *channel)
>  {
> -    clock_update(channel->out, 0);
> +    uint64_t freq, div;
> +
> +    if (!pll_channel_is_enabled(channel)) {
> +        clock_update(channel->out, 0);
> +        return;
> +    }
> +
> +    div = FIELD_EX32(*channel->reg_a2w_ctrl, A2W_PLLx_CHANNELy, DIV);
> +
> +    if (!div) {
> +        /*
> +         * It seems that when the divider value is 0, it is considered as
> +         * being maximum by the hardware (see the Linux driver).
> +         */
> +        div = R_A2W_PLLx_CHANNELy_DIV_MASK;
> +    }
> +
> +    /* Some channels have an additional fixed divider */
> +    freq = clock_get_hz(channel->pll_in) / (div * channel->fixed_divider);
> +
> +    clock_update_hz(channel->out, freq);
>  }
>  
>  /* Update a PLL and all its channels */
>  static void pll_update_all_channels(BCM2835CprmanState *s,
>                                      CprmanPLLState *pll)
> 


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 10/14] hw/misc/bcm2835_cprman: implement clock mux behaviour
  2020-09-25 10:17 ` [PATCH 10/14] hw/misc/bcm2835_cprman: implement clock mux behaviour Luc Michel
@ 2020-09-26 21:40   ` Philippe Mathieu-Daudé
  2020-10-02 14:51     ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 41+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-09-26 21:40 UTC (permalink / raw)
  To: Luc Michel, qemu-devel; +Cc: Peter Maydell, qemu-arm, Andrew Baumann

On 9/25/20 12:17 PM, Luc Michel wrote:
> A clock mux can be configured to select one of its 10 sources through
> the cm_ctl register. It also embeds yet another clock divider, composed
> of an integer part and a fractionnal part. The number of bits of each

Typo "fractional".

> part is mux dependant.

Typo "dependent"?

> 
> Signed-off-by: Luc Michel <luc@lmichel.fr>
> ---
>  hw/misc/bcm2835_cprman.c | 43 +++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 42 insertions(+), 1 deletion(-)
> 
> diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c
> index 8df2db0fd9..75bc11939b 100644
> --- a/hw/misc/bcm2835_cprman.c
> +++ b/hw/misc/bcm2835_cprman.c
> @@ -229,19 +229,60 @@ static const TypeInfo cprman_pll_channel_info = {
>  };
>  
>  
>  /* clock mux */
>  
> +static bool clock_mux_is_enabled(CprmanClockMuxState *mux)
> +{
> +    return FIELD_EX32(*mux->reg_cm, CM_CLOCKx_CTL, ENABLE);
> +}
> +
>  static void clock_mux_update(CprmanClockMuxState *mux)
>  {
> -    clock_update(mux->out, 0);
> +    uint64_t freq, div;
> +    uint32_t src = FIELD_EX32(*mux->reg_cm, CM_CLOCKx_CTL, SRC);
> +    bool enabled = clock_mux_is_enabled(mux);
> +
> +    *mux->reg_cm = FIELD_DP32(*mux->reg_cm, CM_CLOCKx_CTL, BUSY, enabled);
> +
> +    if (!enabled) {
> +        clock_update(mux->out, 0);
> +        return;
> +    }
> +
> +    freq = clock_get_hz(mux->srcs[src]);
> +
> +    if (mux->int_bits == 0 && mux->frac_bits == 0) {
> +        clock_update_hz(mux->out, freq);
> +        return;
> +    }
> +
> +    /*
> +     * The divider has an integer and a fractional part. The size of each part
> +     * varies with the muxes (int_bits and frac_bits). Both parts are
> +     * concatenated, with the integer part always starting at bit 12.
> +     */
> +    div = mux->reg_cm[1] >> (R_CM_CLOCKx_DIV_FRAC_LENGTH - mux->frac_bits);
> +    div &= (1 << (mux->int_bits + mux->frac_bits)) - 1;
> +
> +    freq = (freq << mux->frac_bits) / div;
> +
> +    clock_update_hz(mux->out, freq);
>  }
>  
>  static void clock_mux_src_update(void *opaque)
>  {
>      CprmanClockMuxState **backref = opaque;
>      CprmanClockMuxState *s = *backref;
> +    CprmanClockMuxSource src = backref - s->backref;
> +    uint32_t current_src;
> +
> +    current_src = FIELD_EX32(*s->reg_cm, CM_CLOCKx_CTL, SRC);
> +
> +    if (current_src != src) {
> +        return;
> +    }
>  
>      clock_mux_update(s);
>  }
>  
>  static void clock_mux_init(Object *obj)
> 


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 01/14] hw/core/clock: provide the VMSTATE_ARRAY_CLOCK macro
  2020-09-25 10:17 ` [PATCH 01/14] hw/core/clock: provide the VMSTATE_ARRAY_CLOCK macro Luc Michel
  2020-09-26 20:36   ` Philippe Mathieu-Daudé
@ 2020-09-28  8:38   ` Damien Hedde
  1 sibling, 0 replies; 41+ messages in thread
From: Damien Hedde @ 2020-09-28  8:38 UTC (permalink / raw)
  To: Luc Michel, qemu-devel
  Cc: Peter Maydell, qemu-arm, Philippe Mathieu-Daudé, Andrew Baumann



On 9/25/20 12:17 PM, Luc Michel wrote:
> Signed-off-by: Luc Michel <luc@lmichel.fr>

Reviewed-by: Damien Hedde <damien.hedde@greensocs.com>

> ---
>  include/hw/clock.h | 5 +++++
>  1 file changed, 5 insertions(+)
> 
> diff --git a/include/hw/clock.h b/include/hw/clock.h
> index d357594df9..c93e6113cd 100644
> --- a/include/hw/clock.h
> +++ b/include/hw/clock.h
> @@ -79,10 +79,15 @@ struct Clock {
>  extern const VMStateDescription vmstate_clock;
>  #define VMSTATE_CLOCK(field, state) \
>      VMSTATE_CLOCK_V(field, state, 0)
>  #define VMSTATE_CLOCK_V(field, state, version) \
>      VMSTATE_STRUCT_POINTER_V(field, state, version, vmstate_clock, Clock)
> +#define VMSTATE_ARRAY_CLOCK(field, state, num) \
> +    VMSTATE_ARRAY_CLOCK_V(field, state, num, 0)
> +#define VMSTATE_ARRAY_CLOCK_V(field, state, num, version)          \
> +    VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(field, state, num, version, \
> +                                       vmstate_clock, Clock)
>  
>  /**
>   * clock_setup_canonical_path:
>   * @clk: clock
>   *
> 


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 02/14] hw/core/clock: trace clock values in Hz instead of ns
  2020-09-26 20:36   ` Philippe Mathieu-Daudé
@ 2020-09-28  8:42     ` Damien Hedde
  0 siblings, 0 replies; 41+ messages in thread
From: Damien Hedde @ 2020-09-28  8:42 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé, Luc Michel, qemu-devel
  Cc: Peter Maydell, qemu-arm, Andrew Baumann



On 9/26/20 10:36 PM, Philippe Mathieu-Daudé wrote:
> On 9/25/20 12:17 PM, Luc Michel wrote:
>> The nanosecond unit greatly limits the dynamic range we can display in
>> clock value traces, for values in the order of 1GHz and more. The
>> internal representation can go way beyond this value and it is quite
>> common for today's clocks to be within those ranges.
>>
>> For example, a frequency between 500MHz+ and 1GHz will be displayed as
>> 1ns. Beyond 1GHz, it will show up as 0ns.
>>
>> Replace nanosecond periods traces with frequencies in the Hz unit
>> to have more dynamic range in the trace output.
> 
> I have a similar patch adding the freq but keeping the periods in
> case, as it might be a matter of taste (for me too the frequency
> is more meaningful).
> 
>>
>> Signed-off-by: Luc Michel <luc@lmichel.fr>
>> ---
>>  hw/core/clock.c      | 6 +++---
>>  hw/core/trace-events | 4 ++--
>>  2 files changed, 5 insertions(+), 5 deletions(-)
>>
>> diff --git a/hw/core/clock.c b/hw/core/clock.c
>> index 7066282f7b..81184734e0 100644
>> --- a/hw/core/clock.c
>> +++ b/hw/core/clock.c
>> @@ -37,12 +37,12 @@ void clock_clear_callback(Clock *clk)
>>  bool clock_set(Clock *clk, uint64_t period)
>>  {
>>      if (clk->period == period) {
>>          return false;
>>      }
>> -    trace_clock_set(CLOCK_PATH(clk), CLOCK_PERIOD_TO_NS(clk->period),
>> -                    CLOCK_PERIOD_TO_NS(period));
>> +    trace_clock_set(CLOCK_PATH(clk), CLOCK_PERIOD_TO_HZ(clk->period),
>> +                    CLOCK_PERIOD_TO_HZ(period));
>>      clk->period = period;
>>  
>>      return true;
>>  }
>>  
>> @@ -52,11 +52,11 @@ static void clock_propagate_period(Clock *clk, bool call_callbacks)
>>  
>>      QLIST_FOREACH(child, &clk->children, sibling) {
>>          if (child->period != clk->period) {
>>              child->period = clk->period;
>>              trace_clock_update(CLOCK_PATH(child), CLOCK_PATH(clk),
>> -                               CLOCK_PERIOD_TO_NS(clk->period),
>> +                               CLOCK_PERIOD_TO_HZ(clk->period),
>>                                 call_callbacks);
>>              if (call_callbacks && child->callback) {
>>                  child->callback(child->callback_opaque);
>>              }
>>              clock_propagate_period(child, call_callbacks);
>> diff --git a/hw/core/trace-events b/hw/core/trace-events
>> index 1ac60ede6b..6f96d8bfd0 100644
>> --- a/hw/core/trace-events
>> +++ b/hw/core/trace-events
>> @@ -29,8 +29,8 @@ resettable_phase_exit_end(void *obj, const char *objtype, unsigned count) "obj=%
>>  resettable_transitional_function(void *obj, const char *objtype) "obj=%p(%s)"
>>  
>>  # clock.c
>>  clock_set_source(const char *clk, const char *src) "'%s', src='%s'"
>>  clock_disconnect(const char *clk) "'%s'"
>> -clock_set(const char *clk, uint64_t old, uint64_t new) "'%s', ns=%"PRIu64"->%"PRIu64
>> +clock_set(const char *clk, uint64_t old, uint64_t new) "'%s', %"PRIu64"hz->%"PRIu64"hz"
> 
> Unit is spell 'Hz'.
> 
>>  clock_propagate(const char *clk) "'%s'"
>> -clock_update(const char *clk, const char *src, uint64_t val, int cb) "'%s', src='%s', ns=%"PRIu64", cb=%d"
>> +clock_update(const char *clk, const char *src, uint64_t hz, int cb) "'%s', src='%s', val=%"PRIu64"hz cb=%d"
> 
> Ditto.
> 
> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
> 

Reviewed-by: Damien Hedde <damien.hedde@greensocs.com>


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 04/14] hw/arm/raspi: add a skeleton implementation of the cprman
  2020-09-26 21:05   ` Philippe Mathieu-Daudé
@ 2020-09-28  8:45     ` Luc Michel
  2020-10-02 14:37       ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 41+ messages in thread
From: Luc Michel @ 2020-09-28  8:45 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Peter Maydell, qemu-arm, qemu-devel, Andrew Baumann

On 23:05 Sat 26 Sep     , Philippe Mathieu-Daudé wrote:
> On 9/25/20 12:17 PM, Luc Michel wrote:
> > The BCM2835 cprman is the clock manager of the SoC. It is composed of a
> 
> Can we use CPRMAN in caps?
> 
> > main oscillator, and several sub-components (PLLs, multiplexers, ...) to
> > generate the BCM2835 clock tree.
> > 
> > This commit adds a skeleton of the cprman, with a dummy register
> > read/write implementation. It embeds the main oscillator (xosc) from
> > which all the clocks will be derived.
> > 
> > Signed-off-by: Luc Michel <luc@lmichel.fr>
> > ---
> >  include/hw/arm/bcm2835_peripherals.h       |   3 +-
> >  include/hw/misc/bcm2835_cprman.h           |  37 +++++
> >  include/hw/misc/bcm2835_cprman_internals.h |  24 +++
> >  hw/arm/bcm2835_peripherals.c               |  11 +-
> >  hw/misc/bcm2835_cprman.c                   | 171 +++++++++++++++++++++
> >  hw/misc/meson.build                        |   1 +
> >  hw/misc/trace-events                       |   5 +
> >  7 files changed, 250 insertions(+), 2 deletions(-)
> >  create mode 100644 include/hw/misc/bcm2835_cprman.h
> >  create mode 100644 include/hw/misc/bcm2835_cprman_internals.h
> >  create mode 100644 hw/misc/bcm2835_cprman.c
> > 
> > diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h
> > index 199088425a..002bb5e73b 100644
> > --- a/include/hw/arm/bcm2835_peripherals.h
> > +++ b/include/hw/arm/bcm2835_peripherals.h
> > @@ -21,10 +21,11 @@
> >  #include "hw/misc/bcm2835_property.h"
> >  #include "hw/misc/bcm2835_rng.h"
> >  #include "hw/misc/bcm2835_mbox.h"
> >  #include "hw/misc/bcm2835_mphi.h"
> >  #include "hw/misc/bcm2835_thermal.h"
> > +#include "hw/misc/bcm2835_cprman.h"
> >  #include "hw/sd/sdhci.h"
> >  #include "hw/sd/bcm2835_sdhost.h"
> >  #include "hw/gpio/bcm2835_gpio.h"
> >  #include "hw/timer/bcm2835_systmr.h"
> >  #include "hw/usb/hcd-dwc2.h"
> > @@ -45,11 +46,11 @@ struct BCM2835PeripheralState {
> >  
> >      BCM2835SystemTimerState systmr;
> >      BCM2835MphiState mphi;
> >      UnimplementedDeviceState armtmr;
> >      UnimplementedDeviceState powermgt;
> > -    UnimplementedDeviceState cprman;
> > +    BCM2835CprmanState cprman;
> >      PL011State uart0;
> >      BCM2835AuxState aux;
> >      BCM2835FBState fb;
> >      BCM2835DMAState dma;
> >      BCM2835ICState ic;
> > diff --git a/include/hw/misc/bcm2835_cprman.h b/include/hw/misc/bcm2835_cprman.h
> > new file mode 100644
> > index 0000000000..de9bd01b23
> > --- /dev/null
> > +++ b/include/hw/misc/bcm2835_cprman.h
> > @@ -0,0 +1,37 @@
> > +/*
> > + * BCM2835 cprman clock manager
> > + *
> > + * Copyright (c) 2020 Luc Michel <luc@lmichel.fr>
> > + *
> > + * SPDX-License-Identifier: GPL-2.0-or-later
> > + */
> > +
> > +#ifndef HW_MISC_CPRMAN_H
> > +#define HW_MISC_CPRMAN_H
> > +
> > +#include "hw/sysbus.h"
> > +#include "hw/qdev-clock.h"
> > +
> > +#define TYPE_BCM2835_CPRMAN "bcm2835-cprman"
> > +
> > +typedef struct BCM2835CprmanState BCM2835CprmanState;
> > +
> > +DECLARE_INSTANCE_CHECKER(BCM2835CprmanState, CPRMAN,
> > +                         TYPE_BCM2835_CPRMAN)
> > +
> > +#define CPRMAN_NUM_REGS (0x2000 / sizeof(uint32_t))
> > +
> > +struct BCM2835CprmanState {
> > +    /*< private >*/
> > +    SysBusDevice parent_obj;
> > +
> > +    /*< public >*/
> > +    MemoryRegion iomem;
> > +
> > +    uint32_t regs[CPRMAN_NUM_REGS];
> > +    uint32_t xosc_freq;
> > +
> > +    Clock *xosc;
> > +};
> > +
> > +#endif
> > diff --git a/include/hw/misc/bcm2835_cprman_internals.h b/include/hw/misc/bcm2835_cprman_internals.h
> > new file mode 100644
> > index 0000000000..6a10b60930
> > --- /dev/null
> > +++ b/include/hw/misc/bcm2835_cprman_internals.h
> > @@ -0,0 +1,24 @@
> > +/*
> > + * BCM2835 cprman clock manager
> > + *
> > + * Copyright (c) 2020 Luc Michel <luc@lmichel.fr>
> > + *
> > + * SPDX-License-Identifier: GPL-2.0-or-later
> > + */
> > +
> > +#ifndef HW_MISC_CPRMAN_INTERNALS_H
> > +#define HW_MISC_CPRMAN_INTERNALS_H
> > +
> > +#include "hw/registerfields.h"
> > +#include "hw/misc/bcm2835_cprman.h"
> > +
> > +/* Register map */
> > +
> > +/*
> > + * This field is common to all registers. Each register write value must match
> > + * the CPRMAN_PASSWORD magic value in its 8 MSB.
> > + */
> > +FIELD(CPRMAN, PASSWORD, 24, 8)
> > +#define CPRMAN_PASSWORD 0x5a
> 
> s/PASSWORD/KEY/?
I'm not a big fan of this name either, but this is actually how this
field is named in the datasheet and in various sources.

See:
  - https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
    (page 107)
  - https://elinux.org/BCM2835_registers#CM (auto-generated from
    Broadcom GPU related code)
  - The Linux driver also use this name FWIW.
> 
> > +
> > +#endif
> > diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c
> > index f0802c91e0..958aadeeb9 100644
> > --- a/hw/arm/bcm2835_peripherals.c
> > +++ b/hw/arm/bcm2835_peripherals.c
> > @@ -119,10 +119,13 @@ static void bcm2835_peripherals_init(Object *obj)
> >      object_initialize_child(obj, "mphi", &s->mphi, TYPE_BCM2835_MPHI);
> >  
> >      /* DWC2 */
> >      object_initialize_child(obj, "dwc2", &s->dwc2, TYPE_DWC2_USB);
> >  
> > +    /* CPRMAN clock manager */
> > +    object_initialize_child(obj, "cprman", &s->cprman, TYPE_BCM2835_CPRMAN);
> > +
> >      object_property_add_const_link(OBJECT(&s->dwc2), "dma-mr",
> >                                     OBJECT(&s->gpu_bus_mr));
> >  }
> >  
> >  static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
> > @@ -158,10 +161,17 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
> >      /* Interrupt Controller */
> >      if (!sysbus_realize(SYS_BUS_DEVICE(&s->ic), errp)) {
> >          return;
> >      }
> >  
> > +    /* CPRMAN clock manager */
> > +    if (!sysbus_realize(SYS_BUS_DEVICE(&s->cprman), errp)) {
> > +        return;
> > +    }
> > +    memory_region_add_subregion(&s->peri_mr, CPRMAN_OFFSET,
> > +                sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->cprman), 0));
> > +
> >      memory_region_add_subregion(&s->peri_mr, ARMCTRL_IC_OFFSET,
> >                  sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->ic), 0));
> >      sysbus_pass_irq(SYS_BUS_DEVICE(s), SYS_BUS_DEVICE(&s->ic));
> >  
> >      /* Sys Timer */
> > @@ -343,11 +353,10 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
> >          qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
> >                                 INTERRUPT_USB));
> >  
> >      create_unimp(s, &s->armtmr, "bcm2835-sp804", ARMCTRL_TIMER0_1_OFFSET, 0x40);
> >      create_unimp(s, &s->powermgt, "bcm2835-powermgt", PM_OFFSET, 0x114);
> > -    create_unimp(s, &s->cprman, "bcm2835-cprman", CPRMAN_OFFSET, 0x2000);
> >      create_unimp(s, &s->i2s, "bcm2835-i2s", I2S_OFFSET, 0x100);
> >      create_unimp(s, &s->smi, "bcm2835-smi", SMI_OFFSET, 0x100);
> >      create_unimp(s, &s->spi[0], "bcm2835-spi0", SPI0_OFFSET, 0x20);
> >      create_unimp(s, &s->bscsl, "bcm2835-spis", BSC_SL_OFFSET, 0x100);
> >      create_unimp(s, &s->i2c[0], "bcm2835-i2c0", BSC0_OFFSET, 0x20);
> > diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c
> > new file mode 100644
> > index 0000000000..d2ea0c9236
> > --- /dev/null
> > +++ b/hw/misc/bcm2835_cprman.c
> > @@ -0,0 +1,171 @@
> > +/*
> > + * BCM2835 cprman clock manager
> > + *
> > + * Copyright (c) 2020 Luc Michel <luc@lmichel.fr>
> > + *
> > + * SPDX-License-Identifier: GPL-2.0-or-later
> > + */
> > +
> > +/*
> > + * This peripheral is roughly divided into 3 main parts:
> > + *   - the PLLs
> > + *   - the PLL channels
> > + *   - the clock muxes
> > + *
> > + * A main oscillator (xosc) feeds all the PLLs. Each PLLs has one or more
> > + * channels. Those channel are then connected to the clock muxes. Each mux has
> > + * multiples sources (usually the xosc, some of the PLL channels and some "test
> > + * debug" clocks). It can selects one or the other through a control register.
> 
> "It" is unclear (to me) in this sentence. Assuming the mux.
> 
> > + * Each mux has one output clock that also goes out of the CPRMAN. It usually
> 
> Here is "It" the mux or the output clock? Assuming the mux.
> 
> > + * connects to another peripheral in the SoC (so a given mux is dedicated to a
> > + * peripheral).
> > + *
> > + * At each level (PLL, channel and mux), the clock can be altered through
> > + * dividers (and multipliers in case of the PLLs), and can be disabled (in this
> > + * case, the next levels see no clock).
> > + *
> > + * This can be sum-up as follows (this is an example and not the actual BCM2835
> > + * clock tree):
> > + *
> > + *          /-->[PLL]-|->[PLL channel]--...            [mux]--> to peripherals
> > + *          |         |->[PLL channel]  muxes takes    [mux]
> > + *          |         \->[PLL channel]  inputs from    [mux]
> > + *          |                           some channels  [mux]
> > + * [xosc]---|-->[PLL]-|->[PLL channel]  and other srcs [mux]
> > + *          |         \->[PLL channel]           ...-->[mux]
> > + *          |                                          [mux]
> > + *          \-->[PLL]--->[PLL channel]                 [mux]
> > + *
> > + * The page at https://elinux.org/The_Undocumented_Pi gives the actual clock
> > + * tree configuration.
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +#include "qemu/log.h"
> > +#include "migration/vmstate.h"
> > +#include "hw/qdev-properties.h"
> > +#include "hw/misc/bcm2835_cprman.h"
> > +#include "hw/misc/bcm2835_cprman_internals.h"
> > +#include "trace.h"
> > +
> > +/* CPRMAN "top level" model */
> > +
> > +static uint64_t cprman_read(void *opaque, hwaddr offset,
> > +                                    unsigned size)
> 
> Indent off.
> 
> > +{
> > +    BCM2835CprmanState *s = CPRMAN(opaque);
> > +    uint64_t r = 0;
> > +    size_t idx = offset / sizeof(uint32_t);
> > +
> > +    switch (idx) {
> > +    default:
> > +        r = s->regs[idx];
> > +    }
> > +
> > +    trace_bcm2835_cprman_read(offset, r);
> > +    return r;
> > +}
> > +
> > +static void cprman_write(void *opaque, hwaddr offset,
> > +                         uint64_t value, unsigned size)
> > +{
> > +    BCM2835CprmanState *s = CPRMAN(opaque);
> > +    size_t idx = offset / sizeof(uint32_t);
> > +
> > +    if (FIELD_EX32(value, CPRMAN, PASSWORD) != CPRMAN_PASSWORD) {
> > +        trace_bcm2835_cprman_write_invalid_magic(offset, value);
> > +        return;
> > +    }
> > +
> > +    value &= ~R_CPRMAN_PASSWORD_MASK;
> > +
> > +    trace_bcm2835_cprman_write(offset, value);
> > +    s->regs[idx] = value;
> > +
> > +}
> > +
> > +static const MemoryRegionOps cprman_ops = {
> > +    .read = cprman_read,
> > +    .write = cprman_write,
> > +    .endianness = DEVICE_LITTLE_ENDIAN,
> > +    .valid      = {
> > +        .min_access_size        = 4,
> > +        .max_access_size        = 4,
> 
> I couldn't find this in the public datasheets (any pointer?).
> 
> Since your implementation is 32bit, can you explicit .impl
> min/max = 4?

I could not find this information either, but I assumed this is the
case, mainly because of the 'PASSWORD' field in all registers.

Regarding .impl, I thought that having .valid was enough?

> 
> > +        .unaligned              = false,
> > +    },
> > +};
> > +
> > +static void cprman_reset(DeviceState *dev)
> > +{
> > +    BCM2835CprmanState *s = CPRMAN(dev);
> > +
> > +    memset(s->regs, 0, sizeof(s->regs));
> > +
> > +    clock_update_hz(s->xosc, s->xosc_freq);
> > +}
> > +
> > +static Clock *init_internal_clock(BCM2835CprmanState *s,
> > +                                  const char *name)
> 
> Interesting. Shouldn't this be a public function from the
> Clock API? (Taking Object + name arguments)?
> 
Yes, I hesitated to do just that. I'll add a patch for it when I re-roll.
> > +{
> > +    Object *obj;
> > +    Clock *clk;
> > +
> > +    obj = object_new(TYPE_CLOCK);
> > +    object_property_add_child(OBJECT(s), name, obj);
> > +    object_unref(obj);
> > +
> > +    clk = CLOCK(obj);
> > +    clock_setup_canonical_path(clk);
> > +
> > +    return clk;
> > +}
> > +
> > +static void cprman_init(Object *obj)
> > +{
> > +    BCM2835CprmanState *s = CPRMAN(obj);
> > +
> > +    s->xosc = init_internal_clock(s, "xosc");
> > +
> > +    memory_region_init_io(&s->iomem, obj, &cprman_ops,
> > +                          s, "bcm2835-cprman", 0x2000);
> 
> Again assuming this is a 8KB MMIO device:
> 
> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

Thanks!

-- 
Luc

> 
> > +    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
> > +}
> > +
> > +static const VMStateDescription cprman_vmstate = {
> > +    .name = TYPE_BCM2835_CPRMAN,
> > +    .version_id = 1,
> > +    .minimum_version_id = 1,
> > +    .fields = (VMStateField[]) {
> > +        VMSTATE_UINT32_ARRAY(regs, BCM2835CprmanState, CPRMAN_NUM_REGS),
> > +        VMSTATE_END_OF_LIST()
> > +    }
> > +};
> > +
> > +static Property cprman_properties[] = {
> > +    DEFINE_PROP_UINT32("xosc-freq", BCM2835CprmanState, xosc_freq, 19200000),
> 
> Eventually "xosc-freq-hz".
> 
> > +    DEFINE_PROP_END_OF_LIST()
> > +};
> > +
> > +static void cprman_class_init(ObjectClass *klass, void *data)
> > +{
> > +    DeviceClass *dc = DEVICE_CLASS(klass);
> > +
> > +    dc->reset = cprman_reset;
> > +    dc->vmsd = &cprman_vmstate;
> > +    device_class_set_props(dc, cprman_properties);
> > +}
> > +
> > +static const TypeInfo cprman_info = {
> > +    .name = TYPE_BCM2835_CPRMAN,
> > +    .parent = TYPE_SYS_BUS_DEVICE,
> > +    .instance_size = sizeof(BCM2835CprmanState),
> > +    .class_init = cprman_class_init,
> > +    .instance_init = cprman_init,
> > +};
> > +
> > +static void cprman_register_types(void)
> > +{
> > +    type_register_static(&cprman_info);
> > +}
> > +
> > +type_init(cprman_register_types);
> > diff --git a/hw/misc/meson.build b/hw/misc/meson.build
> > index 793d45b1dc..c94cf70e82 100644
> > --- a/hw/misc/meson.build
> > +++ b/hw/misc/meson.build
> > @@ -71,10 +71,11 @@ softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files(
> >    'bcm2835_mbox.c',
> >    'bcm2835_mphi.c',
> >    'bcm2835_property.c',
> >    'bcm2835_rng.c',
> >    'bcm2835_thermal.c',
> > +  'bcm2835_cprman.c',
> >  ))
> >  softmmu_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_misc.c'))
> >  softmmu_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq_slcr.c', 'zynq-xadc.c'))
> >  softmmu_ss.add(when: 'CONFIG_STM32F2XX_SYSCFG', if_true: files('stm32f2xx_syscfg.c'))
> >  softmmu_ss.add(when: 'CONFIG_STM32F4XX_SYSCFG', if_true: files('stm32f4xx_syscfg.c'))
> > diff --git a/hw/misc/trace-events b/hw/misc/trace-events
> > index 6054f9adf3..d718a2b177 100644
> > --- a/hw/misc/trace-events
> > +++ b/hw/misc/trace-events
> > @@ -224,5 +224,10 @@ grlib_ahb_pnp_read(uint64_t addr, uint32_t value) "AHB PnP read addr:0x%03"PRIx6
> >  grlib_apb_pnp_read(uint64_t addr, uint32_t value) "APB PnP read addr:0x%03"PRIx64" data:0x%08x"
> >  
> >  # pca9552.c
> >  pca955x_gpio_status(const char *description, const char *buf) "%s GPIOs 0-15 [%s]"
> >  pca955x_gpio_change(const char *description, unsigned id, unsigned prev_state, unsigned current_state) "%s GPIO id:%u status: %u -> %u"
> > +
> > +# bcm2835_cprman.c
> > +bcm2835_cprman_read(uint64_t offset, uint64_t value) "offset:0x%" PRIx64 " value:0x%" PRIx64
> > +bcm2835_cprman_write(uint64_t offset, uint64_t value) "offset:0x%" PRIx64 " value:0x%" PRIx64
> > +bcm2835_cprman_write_invalid_magic(uint64_t offset, uint64_t value) "offset:0x%" PRIx64 " value:0x%" PRIx64
> > 


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 04/14] hw/arm/raspi: add a skeleton implementation of the cprman
  2020-09-28  8:45     ` Luc Michel
@ 2020-10-02 14:37       ` Philippe Mathieu-Daudé
  2020-10-03 11:54         ` Luc Michel
  0 siblings, 1 reply; 41+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-10-02 14:37 UTC (permalink / raw)
  To: Luc Michel; +Cc: Peter Maydell, qemu-arm, qemu-devel, Andrew Baumann

On 9/28/20 10:45 AM, Luc Michel wrote:
> On 23:05 Sat 26 Sep     , Philippe Mathieu-Daudé wrote:
>> On 9/25/20 12:17 PM, Luc Michel wrote:
>>> The BCM2835 cprman is the clock manager of the SoC. It is composed of a
>>
>> Can we use CPRMAN in caps?
>>
>>> main oscillator, and several sub-components (PLLs, multiplexers, ...) to
>>> generate the BCM2835 clock tree.
>>>
>>> This commit adds a skeleton of the cprman, with a dummy register
>>> read/write implementation. It embeds the main oscillator (xosc) from
>>> which all the clocks will be derived.
>>>
>>> Signed-off-by: Luc Michel <luc@lmichel.fr>
>>> ---
>>>  include/hw/arm/bcm2835_peripherals.h       |   3 +-
>>>  include/hw/misc/bcm2835_cprman.h           |  37 +++++
>>>  include/hw/misc/bcm2835_cprman_internals.h |  24 +++
>>>  hw/arm/bcm2835_peripherals.c               |  11 +-
>>>  hw/misc/bcm2835_cprman.c                   | 171 +++++++++++++++++++++
>>>  hw/misc/meson.build                        |   1 +
>>>  hw/misc/trace-events                       |   5 +
>>>  7 files changed, 250 insertions(+), 2 deletions(-)
>>>  create mode 100644 include/hw/misc/bcm2835_cprman.h
>>>  create mode 100644 include/hw/misc/bcm2835_cprman_internals.h
>>>  create mode 100644 hw/misc/bcm2835_cprman.c
>>>
>>> diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h
>>> index 199088425a..002bb5e73b 100644
>>> --- a/include/hw/arm/bcm2835_peripherals.h
>>> +++ b/include/hw/arm/bcm2835_peripherals.h
>>> @@ -21,10 +21,11 @@
>>>  #include "hw/misc/bcm2835_property.h"
>>>  #include "hw/misc/bcm2835_rng.h"
>>>  #include "hw/misc/bcm2835_mbox.h"
>>>  #include "hw/misc/bcm2835_mphi.h"
>>>  #include "hw/misc/bcm2835_thermal.h"
>>> +#include "hw/misc/bcm2835_cprman.h"
>>>  #include "hw/sd/sdhci.h"
>>>  #include "hw/sd/bcm2835_sdhost.h"
>>>  #include "hw/gpio/bcm2835_gpio.h"
>>>  #include "hw/timer/bcm2835_systmr.h"
>>>  #include "hw/usb/hcd-dwc2.h"
>>> @@ -45,11 +46,11 @@ struct BCM2835PeripheralState {
>>>  
>>>      BCM2835SystemTimerState systmr;
>>>      BCM2835MphiState mphi;
>>>      UnimplementedDeviceState armtmr;
>>>      UnimplementedDeviceState powermgt;
>>> -    UnimplementedDeviceState cprman;
>>> +    BCM2835CprmanState cprman;
>>>      PL011State uart0;
>>>      BCM2835AuxState aux;
>>>      BCM2835FBState fb;
>>>      BCM2835DMAState dma;
>>>      BCM2835ICState ic;
>>> diff --git a/include/hw/misc/bcm2835_cprman.h b/include/hw/misc/bcm2835_cprman.h
>>> new file mode 100644
>>> index 0000000000..de9bd01b23
>>> --- /dev/null
>>> +++ b/include/hw/misc/bcm2835_cprman.h
>>> @@ -0,0 +1,37 @@
>>> +/*
>>> + * BCM2835 cprman clock manager
>>> + *
>>> + * Copyright (c) 2020 Luc Michel <luc@lmichel.fr>
>>> + *
>>> + * SPDX-License-Identifier: GPL-2.0-or-later
>>> + */
>>> +
>>> +#ifndef HW_MISC_CPRMAN_H
>>> +#define HW_MISC_CPRMAN_H
>>> +
>>> +#include "hw/sysbus.h"
>>> +#include "hw/qdev-clock.h"
>>> +
>>> +#define TYPE_BCM2835_CPRMAN "bcm2835-cprman"
>>> +
>>> +typedef struct BCM2835CprmanState BCM2835CprmanState;
>>> +
>>> +DECLARE_INSTANCE_CHECKER(BCM2835CprmanState, CPRMAN,
>>> +                         TYPE_BCM2835_CPRMAN)
>>> +
>>> +#define CPRMAN_NUM_REGS (0x2000 / sizeof(uint32_t))
>>> +
>>> +struct BCM2835CprmanState {
>>> +    /*< private >*/
>>> +    SysBusDevice parent_obj;
>>> +
>>> +    /*< public >*/
>>> +    MemoryRegion iomem;
>>> +
>>> +    uint32_t regs[CPRMAN_NUM_REGS];
>>> +    uint32_t xosc_freq;
>>> +
>>> +    Clock *xosc;

Isn't it xosc external to the CPRMAN?

>>> +};
>>> +
>>> +#endif
>>> diff --git a/include/hw/misc/bcm2835_cprman_internals.h b/include/hw/misc/bcm2835_cprman_internals.h
>>> new file mode 100644
>>> index 0000000000..6a10b60930
>>> --- /dev/null
>>> +++ b/include/hw/misc/bcm2835_cprman_internals.h
>>> @@ -0,0 +1,24 @@
>>> +/*
>>> + * BCM2835 cprman clock manager
>>> + *
>>> + * Copyright (c) 2020 Luc Michel <luc@lmichel.fr>
>>> + *
>>> + * SPDX-License-Identifier: GPL-2.0-or-later
>>> + */
>>> +
>>> +#ifndef HW_MISC_CPRMAN_INTERNALS_H
>>> +#define HW_MISC_CPRMAN_INTERNALS_H
>>> +
>>> +#include "hw/registerfields.h"
>>> +#include "hw/misc/bcm2835_cprman.h"
>>> +
>>> +/* Register map */
>>> +
>>> +/*
>>> + * This field is common to all registers. Each register write value must match
>>> + * the CPRMAN_PASSWORD magic value in its 8 MSB.
>>> + */
>>> +FIELD(CPRMAN, PASSWORD, 24, 8)
>>> +#define CPRMAN_PASSWORD 0x5a
>>
>> s/PASSWORD/KEY/?
> I'm not a big fan of this name either, but this is actually how this
> field is named in the datasheet and in various sources.
> 
> See:
>   - https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
>     (page 107)
>   - https://elinux.org/BCM2835_registers#CM (auto-generated from
>     Broadcom GPU related code)
>   - The Linux driver also use this name FWIW.

OK.

>>
>>> +
>>> +#endif
>>> diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c
>>> index f0802c91e0..958aadeeb9 100644
>>> --- a/hw/arm/bcm2835_peripherals.c
>>> +++ b/hw/arm/bcm2835_peripherals.c
>>> @@ -119,10 +119,13 @@ static void bcm2835_peripherals_init(Object *obj)
>>>      object_initialize_child(obj, "mphi", &s->mphi, TYPE_BCM2835_MPHI);
>>>  
>>>      /* DWC2 */
>>>      object_initialize_child(obj, "dwc2", &s->dwc2, TYPE_DWC2_USB);
>>>  
>>> +    /* CPRMAN clock manager */
>>> +    object_initialize_child(obj, "cprman", &s->cprman, TYPE_BCM2835_CPRMAN);
>>> +
>>>      object_property_add_const_link(OBJECT(&s->dwc2), "dma-mr",
>>>                                     OBJECT(&s->gpu_bus_mr));
>>>  }
>>>  
>>>  static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
>>> @@ -158,10 +161,17 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
>>>      /* Interrupt Controller */
>>>      if (!sysbus_realize(SYS_BUS_DEVICE(&s->ic), errp)) {
>>>          return;
>>>      }
>>>  
>>> +    /* CPRMAN clock manager */
>>> +    if (!sysbus_realize(SYS_BUS_DEVICE(&s->cprman), errp)) {
>>> +        return;
>>> +    }
>>> +    memory_region_add_subregion(&s->peri_mr, CPRMAN_OFFSET,
>>> +                sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->cprman), 0));
>>> +
>>>      memory_region_add_subregion(&s->peri_mr, ARMCTRL_IC_OFFSET,
>>>                  sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->ic), 0));
>>>      sysbus_pass_irq(SYS_BUS_DEVICE(s), SYS_BUS_DEVICE(&s->ic));
>>>  
>>>      /* Sys Timer */
>>> @@ -343,11 +353,10 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
>>>          qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
>>>                                 INTERRUPT_USB));
>>>  
>>>      create_unimp(s, &s->armtmr, "bcm2835-sp804", ARMCTRL_TIMER0_1_OFFSET, 0x40);
>>>      create_unimp(s, &s->powermgt, "bcm2835-powermgt", PM_OFFSET, 0x114);
>>> -    create_unimp(s, &s->cprman, "bcm2835-cprman", CPRMAN_OFFSET, 0x2000);
>>>      create_unimp(s, &s->i2s, "bcm2835-i2s", I2S_OFFSET, 0x100);
>>>      create_unimp(s, &s->smi, "bcm2835-smi", SMI_OFFSET, 0x100);
>>>      create_unimp(s, &s->spi[0], "bcm2835-spi0", SPI0_OFFSET, 0x20);
>>>      create_unimp(s, &s->bscsl, "bcm2835-spis", BSC_SL_OFFSET, 0x100);
>>>      create_unimp(s, &s->i2c[0], "bcm2835-i2c0", BSC0_OFFSET, 0x20);
>>> diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c
>>> new file mode 100644
>>> index 0000000000..d2ea0c9236
>>> --- /dev/null
>>> +++ b/hw/misc/bcm2835_cprman.c
>>> @@ -0,0 +1,171 @@
>>> +/*
>>> + * BCM2835 cprman clock manager
>>> + *
>>> + * Copyright (c) 2020 Luc Michel <luc@lmichel.fr>
>>> + *
>>> + * SPDX-License-Identifier: GPL-2.0-or-later
>>> + */
>>> +
>>> +/*
>>> + * This peripheral is roughly divided into 3 main parts:
>>> + *   - the PLLs
>>> + *   - the PLL channels
>>> + *   - the clock muxes
>>> + *
>>> + * A main oscillator (xosc) feeds all the PLLs. Each PLLs has one or more
>>> + * channels. Those channel are then connected to the clock muxes. Each mux has
>>> + * multiples sources (usually the xosc, some of the PLL channels and some "test
>>> + * debug" clocks). It can selects one or the other through a control register.
>>
>> "It" is unclear (to me) in this sentence. Assuming the mux.
>>
>>> + * Each mux has one output clock that also goes out of the CPRMAN. It usually
>>
>> Here is "It" the mux or the output clock? Assuming the mux.
>>
>>> + * connects to another peripheral in the SoC (so a given mux is dedicated to a
>>> + * peripheral).
>>> + *
>>> + * At each level (PLL, channel and mux), the clock can be altered through
>>> + * dividers (and multipliers in case of the PLLs), and can be disabled (in this
>>> + * case, the next levels see no clock).
>>> + *
>>> + * This can be sum-up as follows (this is an example and not the actual BCM2835
>>> + * clock tree):
>>> + *
>>> + *          /-->[PLL]-|->[PLL channel]--...            [mux]--> to peripherals
>>> + *          |         |->[PLL channel]  muxes takes    [mux]
>>> + *          |         \->[PLL channel]  inputs from    [mux]
>>> + *          |                           some channels  [mux]
>>> + * [xosc]---|-->[PLL]-|->[PLL channel]  and other srcs [mux]
>>> + *          |         \->[PLL channel]           ...-->[mux]
>>> + *          |                                          [mux]
>>> + *          \-->[PLL]--->[PLL channel]                 [mux]
>>> + *
>>> + * The page at https://elinux.org/The_Undocumented_Pi gives the actual clock
>>> + * tree configuration.
>>> + */
>>> +
>>> +#include "qemu/osdep.h"
>>> +#include "qemu/log.h"
>>> +#include "migration/vmstate.h"
>>> +#include "hw/qdev-properties.h"
>>> +#include "hw/misc/bcm2835_cprman.h"
>>> +#include "hw/misc/bcm2835_cprman_internals.h"
>>> +#include "trace.h"
>>> +
>>> +/* CPRMAN "top level" model */
>>> +
>>> +static uint64_t cprman_read(void *opaque, hwaddr offset,
>>> +                                    unsigned size)
>>
>> Indent off.
>>
>>> +{
>>> +    BCM2835CprmanState *s = CPRMAN(opaque);
>>> +    uint64_t r = 0;
>>> +    size_t idx = offset / sizeof(uint32_t);
>>> +
>>> +    switch (idx) {
>>> +    default:
>>> +        r = s->regs[idx];
>>> +    }
>>> +
>>> +    trace_bcm2835_cprman_read(offset, r);
>>> +    return r;
>>> +}
>>> +
>>> +static void cprman_write(void *opaque, hwaddr offset,
>>> +                         uint64_t value, unsigned size)
>>> +{
>>> +    BCM2835CprmanState *s = CPRMAN(opaque);
>>> +    size_t idx = offset / sizeof(uint32_t);
>>> +
>>> +    if (FIELD_EX32(value, CPRMAN, PASSWORD) != CPRMAN_PASSWORD) {
>>> +        trace_bcm2835_cprman_write_invalid_magic(offset, value);
>>> +        return;
>>> +    }
>>> +
>>> +    value &= ~R_CPRMAN_PASSWORD_MASK;
>>> +
>>> +    trace_bcm2835_cprman_write(offset, value);
>>> +    s->regs[idx] = value;
>>> +
>>> +}
>>> +
>>> +static const MemoryRegionOps cprman_ops = {
>>> +    .read = cprman_read,
>>> +    .write = cprman_write,
>>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>>> +    .valid      = {
>>> +        .min_access_size        = 4,
>>> +        .max_access_size        = 4,
>>
>> I couldn't find this in the public datasheets (any pointer?).
>>
>> Since your implementation is 32bit, can you explicit .impl
>> min/max = 4?
> 
> I could not find this information either, but I assumed this is the
> case, mainly because of the 'PASSWORD' field in all registers.

Good point. Do you mind adding a comment about it here please?

> 
> Regarding .impl, I thought that having .valid was enough?

Until we eventually figure out we can do 64-bit accesses,
so someone change .valid.max to 8 and your model is broken :/

> 
>>
>>> +        .unaligned              = false,
>>> +    },
>>> +};
>>> +
>>> +static void cprman_reset(DeviceState *dev)
>>> +{
>>> +    BCM2835CprmanState *s = CPRMAN(dev);
>>> +
>>> +    memset(s->regs, 0, sizeof(s->regs));
>>> +
>>> +    clock_update_hz(s->xosc, s->xosc_freq);
>>> +}
>>> +
>>> +static Clock *init_internal_clock(BCM2835CprmanState *s,
>>> +                                  const char *name)
>>
>> Interesting. Shouldn't this be a public function from the
>> Clock API? (Taking Object + name arguments)?
>>
> Yes, I hesitated to do just that. I'll add a patch for it when I re-roll.
>>> +{
>>> +    Object *obj;
>>> +    Clock *clk;
>>> +
>>> +    obj = object_new(TYPE_CLOCK);
>>> +    object_property_add_child(OBJECT(s), name, obj);
>>> +    object_unref(obj);
>>> +
>>> +    clk = CLOCK(obj);
>>> +    clock_setup_canonical_path(clk);
>>> +
>>> +    return clk;
>>> +}
>>> +
>>> +static void cprman_init(Object *obj)
>>> +{
>>> +    BCM2835CprmanState *s = CPRMAN(obj);
>>> +
>>> +    s->xosc = init_internal_clock(s, "xosc");
>>> +
>>> +    memory_region_init_io(&s->iomem, obj, &cprman_ops,
>>> +                          s, "bcm2835-cprman", 0x2000);
>>
>> Again assuming this is a 8KB MMIO device:
>>
>> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
> 
> Thanks!
> 


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 09/14] hw/misc/bcm2835_cprman: add a clock mux skeleton implementation
  2020-09-25 10:17 ` [PATCH 09/14] hw/misc/bcm2835_cprman: add a clock mux skeleton implementation Luc Michel
@ 2020-10-02 14:42   ` Philippe Mathieu-Daudé
  2020-10-02 15:34     ` Philippe Mathieu-Daudé
  2020-10-04 19:34     ` Luc Michel
  0 siblings, 2 replies; 41+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-10-02 14:42 UTC (permalink / raw)
  To: Luc Michel, qemu-devel; +Cc: Peter Maydell, qemu-arm, Andrew Baumann

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.


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 10/14] hw/misc/bcm2835_cprman: implement clock mux behaviour
  2020-09-26 21:40   ` Philippe Mathieu-Daudé
@ 2020-10-02 14:51     ` Philippe Mathieu-Daudé
  2020-10-04 18:37       ` Luc Michel
  0 siblings, 1 reply; 41+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-10-02 14:51 UTC (permalink / raw)
  To: Luc Michel, qemu-devel; +Cc: Peter Maydell, qemu-arm, Andrew Baumann

On 9/26/20 11:40 PM, Philippe Mathieu-Daudé wrote:
> On 9/25/20 12:17 PM, Luc Michel wrote:
>> A clock mux can be configured to select one of its 10 sources through
>> the cm_ctl register. It also embeds yet another clock divider, composed
>> of an integer part and a fractionnal part. The number of bits of each
> 
> Typo "fractional".
> 
>> part is mux dependant.
> 
> Typo "dependent"?
> 
>>
>> Signed-off-by: Luc Michel <luc@lmichel.fr>
>> ---
>>  hw/misc/bcm2835_cprman.c | 43 +++++++++++++++++++++++++++++++++++++++-
>>  1 file changed, 42 insertions(+), 1 deletion(-)
>>
>> diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c
>> index 8df2db0fd9..75bc11939b 100644
>> --- a/hw/misc/bcm2835_cprman.c
>> +++ b/hw/misc/bcm2835_cprman.c
>> @@ -229,19 +229,60 @@ static const TypeInfo cprman_pll_channel_info = {
>>  };
>>  
>>  
>>  /* clock mux */
>>  
>> +static bool clock_mux_is_enabled(CprmanClockMuxState *mux)
>> +{
>> +    return FIELD_EX32(*mux->reg_cm, CM_CLOCKx_CTL, ENABLE);
>> +}
>> +
>>  static void clock_mux_update(CprmanClockMuxState *mux)
>>  {
>> -    clock_update(mux->out, 0);
>> +    uint64_t freq, div;
>> +    uint32_t src = FIELD_EX32(*mux->reg_cm, CM_CLOCKx_CTL, SRC);
>> +    bool enabled = clock_mux_is_enabled(mux);
>> +
>> +    *mux->reg_cm = FIELD_DP32(*mux->reg_cm, CM_CLOCKx_CTL, BUSY, enabled);
>> +
>> +    if (!enabled) {
>> +        clock_update(mux->out, 0);
>> +        return;
>> +    }
>> +
>> +    freq = clock_get_hz(mux->srcs[src]);
>> +
>> +    if (mux->int_bits == 0 && mux->frac_bits == 0) {
>> +        clock_update_hz(mux->out, freq);
>> +        return;
>> +    }
>> +
>> +    /*
>> +     * The divider has an integer and a fractional part. The size of each part
>> +     * varies with the muxes (int_bits and frac_bits). Both parts are
>> +     * concatenated, with the integer part always starting at bit 12.
>> +     */
>> +    div = mux->reg_cm[1] >> (R_CM_CLOCKx_DIV_FRAC_LENGTH - mux->frac_bits);
>> +    div &= (1 << (mux->int_bits + mux->frac_bits)) - 1;

Eventually:

    div &= MAKE_64BIT_MASK(mux->int_bits + mux->frac_bits, 64);

>> +
>> +    freq = (freq << mux->frac_bits) / div;

Maybe:

    freq = muldiv64(freq, 1 << mux->frac_bits, div);

>> +
>> +    clock_update_hz(mux->out, freq);
>>  }
>>  
>>  static void clock_mux_src_update(void *opaque)
>>  {
>>      CprmanClockMuxState **backref = opaque;
>>      CprmanClockMuxState *s = *backref;
>> +    CprmanClockMuxSource src = backref - s->backref;
>> +    uint32_t current_src;

Maybe avoid current_src and use in place.

Otherwise:
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

>> +
>> +    current_src = FIELD_EX32(*s->reg_cm, CM_CLOCKx_CTL, SRC);
>> +
>> +    if (current_src != src) {
>> +        return;
>> +    }
>>  
>>      clock_mux_update(s);
>>  }
>>  
>>  static void clock_mux_init(Object *obj)
>>
> 


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 11/14] hw/misc/bcm2835_cprman: add the DSI0HSCK multiplexer
  2020-09-25 10:17 ` [PATCH 11/14] hw/misc/bcm2835_cprman: add the DSI0HSCK multiplexer Luc Michel
@ 2020-10-02 14:55   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 41+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-10-02 14:55 UTC (permalink / raw)
  To: Luc Michel, qemu-devel; +Cc: Peter Maydell, qemu-arm, Andrew Baumann

On 9/25/20 12:17 PM, Luc Michel wrote:
> This simple mux sits between the PLL channels and the DSI0E and DSI0P
> clock muxes. This mux selects between PLLA-DSI0 and PLLD-DSI0 channel
> and outputs the selected signal to source number 4 of DSI0E/P clock
> muxes. It is controlled by the cm_dsi0hsck register.
> 
> Signed-off-by: Luc Michel <luc@lmichel.fr>
> ---
>  include/hw/misc/bcm2835_cprman.h           | 15 +++++
>  include/hw/misc/bcm2835_cprman_internals.h |  6 ++
>  hw/misc/bcm2835_cprman.c                   | 78 +++++++++++++++++++++-
>  3 files changed, 98 insertions(+), 1 deletion(-)
> 
> diff --git a/include/hw/misc/bcm2835_cprman.h b/include/hw/misc/bcm2835_cprman.h
> index c2a89e8e90..5555a299fc 100644
> --- a/include/hw/misc/bcm2835_cprman.h
> +++ b/include/hw/misc/bcm2835_cprman.h
> @@ -171,20 +171,35 @@ typedef struct CprmanClockMuxState {
>       * source number.
>       */
>      struct CprmanClockMuxState *backref[CPRMAN_NUM_CLOCK_MUX_SRC];
>  } CprmanClockMuxState;
>  
> +typedef struct CprmanDsi0HsckMuxState {
> +    /*< private >*/
> +    DeviceState parent_obj;
> +
> +    /*< public >*/
> +    CprmanClockMux id;
> +
> +    uint32_t *reg_cm;
> +
> +    Clock *plla_in;
> +    Clock *plld_in;
> +    Clock *out;
> +} CprmanDsi0HsckMuxState;
> +
>  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];
> +    CprmanDsi0HsckMuxState dsi0hsck_mux;
>  
>      uint32_t regs[CPRMAN_NUM_REGS];
>      uint32_t xosc_freq;
>  
>      Clock *xosc;
> diff --git a/include/hw/misc/bcm2835_cprman_internals.h b/include/hw/misc/bcm2835_cprman_internals.h
> index a2b5a1aa50..ad07cf5276 100644
> --- a/include/hw/misc/bcm2835_cprman_internals.h
> +++ b/include/hw/misc/bcm2835_cprman_internals.h
> @@ -13,17 +13,20 @@
>  #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"
> +#define TYPE_CPRMAN_DSI0HSCK_MUX "bcm2835-cprman-dsi0hsck-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)
> +DECLARE_INSTANCE_CHECKER(CprmanDsi0HsckMuxState, CPRMAN_DSI0HSCK_MUX,
> +                         TYPE_CPRMAN_DSI0HSCK_MUX)
>  
>  /* Register map */
>  
>  /* PLLs */
>  REG32(CM_PLLA, 0x104)
> @@ -221,10 +224,13 @@ REG32(CM_LOCK, 0x114)
>      FIELD(CM_LOCK, FLOCKD, 11, 1)
>      FIELD(CM_LOCK, FLOCKC, 10, 1)
>      FIELD(CM_LOCK, FLOCKB, 9, 1)
>      FIELD(CM_LOCK, FLOCKA, 8, 1)
>  
> +REG32(CM_DSI0HSCK, 0x120)
> +    FIELD(CM_DSI0HSCK, SELPLLD, 0, 1)
> +
>  /*
>   * This field is common to all registers. Each register write value must match
>   * the CPRMAN_PASSWORD magic value in its 8 MSB.
>   */
>  FIELD(CPRMAN, PASSWORD, 24, 8)
> diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c
> index 75bc11939b..e576ab2642 100644
> --- a/hw/misc/bcm2835_cprman.c
> +++ b/hw/misc/bcm2835_cprman.c
> @@ -327,10 +327,62 @@ static const TypeInfo cprman_clock_mux_info = {
>      .class_init = clock_mux_class_init,
>      .instance_init = clock_mux_init,
>  };
>  
>  
> +/* DSI0HSCK mux */
> +
> +static void dsi0hsck_mux_update(CprmanDsi0HsckMuxState *s)
> +{
> +    bool src_is_plld = FIELD_EX32(*s->reg_cm, CM_DSI0HSCK, SELPLLD);
> +    Clock *src = src_is_plld ? s->plld_in : s->plla_in;
> +
> +    clock_update(s->out, clock_get(src));
> +}
> +
> +static void dsi0hsck_mux_in_update(void *opaque)
> +{
> +    dsi0hsck_mux_update(CPRMAN_DSI0HSCK_MUX(opaque));
> +}
> +
> +static void dsi0hsck_mux_init(Object *obj)
> +{
> +    CprmanDsi0HsckMuxState *s = CPRMAN_DSI0HSCK_MUX(obj);
> +    DeviceState *dev = DEVICE(obj);
> +
> +    s->plla_in = qdev_init_clock_in(dev, "plla-in", dsi0hsck_mux_in_update, s);
> +    s->plld_in = qdev_init_clock_in(dev, "plld-in", dsi0hsck_mux_in_update, s);
> +    s->out = qdev_init_clock_out(DEVICE(s), "out");
> +}
> +
> +static const VMStateDescription dsi0hsck_mux_vmstate = {
> +    .name = TYPE_CPRMAN_DSI0HSCK_MUX,
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_CLOCK(plla_in, CprmanDsi0HsckMuxState),
> +        VMSTATE_CLOCK(plld_in, CprmanDsi0HsckMuxState),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static void dsi0hsck_mux_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->vmsd = &dsi0hsck_mux_vmstate;
> +}
> +
> +static const TypeInfo cprman_dsi0hsck_mux_info = {
> +    .name = TYPE_CPRMAN_DSI0HSCK_MUX,
> +    .parent = TYPE_DEVICE,
> +    .instance_size = sizeof(CprmanDsi0HsckMuxState),
> +    .class_init = dsi0hsck_mux_class_init,
> +    .instance_init = dsi0hsck_mux_init,
> +};
> +
> +
>  /* CPRMAN "top level" model */
>  
>  static uint32_t get_cm_lock(const BCM2835CprmanState *s)
>  {
>      static const int CM_LOCK_MAPPING[] = {
> @@ -488,10 +540,14 @@ static void cprman_write(void *opaque, hwaddr offset,
>      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;
> +
> +    case R_CM_DSI0HSCK:
> +        dsi0hsck_mux_update(&s->dsi0hsck_mux);
> +        break;
>      }
>  }
>  
>  #undef CASE_PLL_A2W_REGS
>  
> @@ -519,10 +575,12 @@ static void cprman_reset(DeviceState *dev)
>  
>      for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) {
>          device_cold_reset(DEVICE(&s->channels[i]));
>      }
>  
> +    device_cold_reset(DEVICE(&s->dsi0hsck_mux));
> +
>      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);
> @@ -560,10 +618,14 @@ static void cprman_init(Object *obj)
>                                  &s->channels[i],
>                                  TYPE_CPRMAN_PLL_CHANNEL);
>          set_pll_channel_init_info(s, &s->channels[i], i);
>      }
>  
> +    object_initialize_child(obj, "dsi0hsck-mux",
> +                            &s->dsi0hsck_mux, TYPE_CPRMAN_DSI0HSCK_MUX);
> +    s->dsi0hsck_mux.reg_cm = &s->regs[R_CM_DSI0HSCK];
> +
>      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],
> @@ -608,11 +670,11 @@ static void connect_mux_sources(BCM2835CprmanState *s,
>          Clock *src;
>  
>          if (mapping == CPRMAN_CLOCK_SRC_FORCE_GROUND) {
>              src = s->gnd;
>          } else if (mapping == CPRMAN_CLOCK_SRC_DSI0HSCK) {
> -            src = s->gnd; /* TODO */
> +            src = s->dsi0hsck_mux.out;
>          } else if (i < CPRMAN_CLOCK_SRC_PLLA) {
>              src = CLK_SRC_MAPPING[i];
>          } else {
>              src = s->channels[mapping].out;
>          }
> @@ -646,10 +708,23 @@ static void cprman_realize(DeviceState *dev, Error **errp)
>          if (!qdev_realize(DEVICE(channel), NULL, errp)) {
>              return;
>          }
>      }
>  
> +    {
> +        CprmanDsi0HsckMuxState *dsi0hsck_mux = &s->dsi0hsck_mux;

Why use a new block?

Otherwise:
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

> +
> +        clock_set_source(dsi0hsck_mux->plla_in,
> +                         s->channels[CPRMAN_PLLA_CHANNEL_DSI0].out);
> +        clock_set_source(dsi0hsck_mux->plld_in,
> +                         s->channels[CPRMAN_PLLD_CHANNEL_DSI0].out);
> +
> +        if (!qdev_realize(DEVICE(dsi0hsck_mux), 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);
>  
> @@ -696,8 +771,9 @@ 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_register_static(&cprman_dsi0hsck_mux_info);
>  }
>  
>  type_init(cprman_register_types);
> 


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 09/14] hw/misc/bcm2835_cprman: add a clock mux skeleton implementation
  2020-10-02 14:42   ` Philippe Mathieu-Daudé
@ 2020-10-02 15:34     ` Philippe Mathieu-Daudé
  2020-10-04 19:34     ` Luc Michel
  1 sibling, 0 replies; 41+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-10-02 15:34 UTC (permalink / raw)
  To: Luc Michel, qemu-devel; +Cc: Peter Maydell, qemu-arm, Andrew Baumann

On 10/2/20 4:42 PM, Philippe Mathieu-Daudé wrote:
> 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>
>> ---
[...]
>>  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".

Although it might be easier to have a singleton in hw/core/clock.c...


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 04/14] hw/arm/raspi: add a skeleton implementation of the cprman
  2020-10-02 14:37       ` Philippe Mathieu-Daudé
@ 2020-10-03 11:54         ` Luc Michel
  2020-10-03 18:14           ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 41+ messages in thread
From: Luc Michel @ 2020-10-03 11:54 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Peter Maydell, qemu-arm, qemu-devel, Andrew Baumann

On 16:37 Fri 02 Oct     , Philippe Mathieu-Daudé wrote:

[snip]

> >>> +struct BCM2835CprmanState {
> >>> +    /*< private >*/
> >>> +    SysBusDevice parent_obj;
> >>> +
> >>> +    /*< public >*/
> >>> +    MemoryRegion iomem;
> >>> +
> >>> +    uint32_t regs[CPRMAN_NUM_REGS];
> >>> +    uint32_t xosc_freq;
> >>> +
> >>> +    Clock *xosc;
> 
> Isn't it xosc external to the CPRMAN?
> 
Yes on real hardware I'm pretty sure it's the oscillator we can see on
the board itself, near the SoC (on the bottom side). This is how I first
planned to implement it. I then realized that would add complexity to
the BCM2835Peripherals model for no good reasons IMHO (mainly because of
migration). So at the end I put it inside the CPRMAN for simplicity, and
added a property to set its frequency.

> >>> +};

[snip]

> >>> +static const MemoryRegionOps cprman_ops = {
> >>> +    .read = cprman_read,
> >>> +    .write = cprman_write,
> >>> +    .endianness = DEVICE_LITTLE_ENDIAN,
> >>> +    .valid      = {
> >>> +        .min_access_size        = 4,
> >>> +        .max_access_size        = 4,
> >>
> >> I couldn't find this in the public datasheets (any pointer?).
> >>
> >> Since your implementation is 32bit, can you explicit .impl
> >> min/max = 4?
> > 
> > I could not find this information either, but I assumed this is the
> > case, mainly because of the 'PASSWORD' field in all registers.
> 
> Good point. Do you mind adding a comment about it here please?
> 

OK

> > 
> > Regarding .impl, I thought that having .valid was enough?
> 
> Until we eventually figure out we can do 64-bit accesses,
> so someone change .valid.max to 8 and your model is broken :/

OK, I'll add the .impl constraints.

[snip]

-- 
Luc


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 04/14] hw/arm/raspi: add a skeleton implementation of the cprman
  2020-10-03 11:54         ` Luc Michel
@ 2020-10-03 18:14           ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 41+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-10-03 18:14 UTC (permalink / raw)
  To: Luc Michel; +Cc: Peter Maydell, qemu-arm, qemu-devel, Andrew Baumann

On 10/3/20 1:54 PM, Luc Michel wrote:
> On 16:37 Fri 02 Oct     , Philippe Mathieu-Daudé wrote:
> 
> [snip]
> 
>>>>> +struct BCM2835CprmanState {
>>>>> +    /*< private >*/
>>>>> +    SysBusDevice parent_obj;
>>>>> +
>>>>> +    /*< public >*/
>>>>> +    MemoryRegion iomem;
>>>>> +
>>>>> +    uint32_t regs[CPRMAN_NUM_REGS];
>>>>> +    uint32_t xosc_freq;
>>>>> +
>>>>> +    Clock *xosc;
>>
>> Isn't it xosc external to the CPRMAN?
>>
> Yes on real hardware I'm pretty sure it's the oscillator we can see on
> the board itself, near the SoC (on the bottom side). This is how I first
> planned to implement it. I then realized that would add complexity to
> the BCM2835Peripherals model for no good reasons IMHO (mainly because of
> migration). So at the end I put it inside the CPRMAN for simplicity, and
> added a property to set its frequency.

OK as long as all boards have a 19.2MHz crystal, but if the property
is not easily accessible why not use a #define in
"hw/arm/raspi_platform.h" instead?

Else we should alias the property using object_property_add_alias()
in TYPE_BCM2835_PERIPHERALS.

> 
>>>>> +};
> 
> [snip]
> 
>>>>> +static const MemoryRegionOps cprman_ops = {
>>>>> +    .read = cprman_read,
>>>>> +    .write = cprman_write,
>>>>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>>>>> +    .valid      = {
>>>>> +        .min_access_size        = 4,
>>>>> +        .max_access_size        = 4,
>>>>
>>>> I couldn't find this in the public datasheets (any pointer?).
>>>>
>>>> Since your implementation is 32bit, can you explicit .impl
>>>> min/max = 4?
>>>
>>> I could not find this information either, but I assumed this is the
>>> case, mainly because of the 'PASSWORD' field in all registers.
>>
>> Good point. Do you mind adding a comment about it here please?
>>
> 
> OK
> 
>>>
>>> Regarding .impl, I thought that having .valid was enough?
>>
>> Until we eventually figure out we can do 64-bit accesses,
>> so someone change .valid.max to 8 and your model is broken :/
> 
> OK, I'll add the .impl constraints.
> 
> [snip]
> 


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 10/14] hw/misc/bcm2835_cprman: implement clock mux behaviour
  2020-10-02 14:51     ` Philippe Mathieu-Daudé
@ 2020-10-04 18:37       ` Luc Michel
  2020-10-05 19:50         ` Luc Michel
  0 siblings, 1 reply; 41+ messages in thread
From: Luc Michel @ 2020-10-04 18:37 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Peter Maydell, qemu-arm, qemu-devel, Andrew Baumann

On 16:51 Fri 02 Oct     , Philippe Mathieu-Daudé wrote:
> On 9/26/20 11:40 PM, Philippe Mathieu-Daudé wrote:
> > On 9/25/20 12:17 PM, Luc Michel wrote:
> >> A clock mux can be configured to select one of its 10 sources through
> >> the cm_ctl register. It also embeds yet another clock divider, composed
> >> of an integer part and a fractionnal part. The number of bits of each
> > 
> > Typo "fractional".
> > 
> >> part is mux dependant.
> > 
> > Typo "dependent"?
> > 
> >>
> >> Signed-off-by: Luc Michel <luc@lmichel.fr>
> >> ---
> >>  hw/misc/bcm2835_cprman.c | 43 +++++++++++++++++++++++++++++++++++++++-
> >>  1 file changed, 42 insertions(+), 1 deletion(-)
> >>
> >> diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c
> >> index 8df2db0fd9..75bc11939b 100644
> >> --- a/hw/misc/bcm2835_cprman.c
> >> +++ b/hw/misc/bcm2835_cprman.c
> >> @@ -229,19 +229,60 @@ static const TypeInfo cprman_pll_channel_info = {
> >>  };
> >>  
> >>  
> >>  /* clock mux */
> >>  
> >> +static bool clock_mux_is_enabled(CprmanClockMuxState *mux)
> >> +{
> >> +    return FIELD_EX32(*mux->reg_cm, CM_CLOCKx_CTL, ENABLE);
> >> +}
> >> +
> >>  static void clock_mux_update(CprmanClockMuxState *mux)
> >>  {
> >> -    clock_update(mux->out, 0);
> >> +    uint64_t freq, div;
> >> +    uint32_t src = FIELD_EX32(*mux->reg_cm, CM_CLOCKx_CTL, SRC);
> >> +    bool enabled = clock_mux_is_enabled(mux);
> >> +
> >> +    *mux->reg_cm = FIELD_DP32(*mux->reg_cm, CM_CLOCKx_CTL, BUSY, enabled);
> >> +
> >> +    if (!enabled) {
> >> +        clock_update(mux->out, 0);
> >> +        return;
> >> +    }
> >> +
> >> +    freq = clock_get_hz(mux->srcs[src]);
> >> +
> >> +    if (mux->int_bits == 0 && mux->frac_bits == 0) {
> >> +        clock_update_hz(mux->out, freq);
> >> +        return;
> >> +    }
> >> +
> >> +    /*
> >> +     * The divider has an integer and a fractional part. The size of each part
> >> +     * varies with the muxes (int_bits and frac_bits). Both parts are
> >> +     * concatenated, with the integer part always starting at bit 12.
> >> +     */
> >> +    div = mux->reg_cm[1] >> (R_CM_CLOCKx_DIV_FRAC_LENGTH - mux->frac_bits);
> >> +    div &= (1 << (mux->int_bits + mux->frac_bits)) - 1;
> 
> Eventually:
> 
>     div &= MAKE_64BIT_MASK(mux->int_bits + mux->frac_bits, 64);
I think this is MAKE_64BIT_MASK(0, mux->int_bits + mux->frac_bits)
(The shift macro parameter is 0 to have the ones positioned at the
mask's LSBs.
I'll use this macro in my next re-roll.

> 
> >> +
> >> +    freq = (freq << mux->frac_bits) / div;
> 
> Maybe:
> 
>     freq = muldiv64(freq, 1 << mux->frac_bits, div);
OK

> 
> >> +
> >> +    clock_update_hz(mux->out, freq);
> >>  }
> >>  
> >>  static void clock_mux_src_update(void *opaque)
> >>  {
> >>      CprmanClockMuxState **backref = opaque;
> >>      CprmanClockMuxState *s = *backref;
> >> +    CprmanClockMuxSource src = backref - s->backref;
> >> +    uint32_t current_src;
> 
> Maybe avoid current_src and use in place.
OK

> 
> Otherwise:
> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Thanks !

-- 
Luc

> 
> >> +
> >> +    current_src = FIELD_EX32(*s->reg_cm, CM_CLOCKx_CTL, SRC);
> >> +
> >> +    if (current_src != src) {
> >> +        return;
> >> +    }
> >>  
> >>      clock_mux_update(s);
> >>  }
> >>  
> >>  static void clock_mux_init(Object *obj)
> >>
> > 

-- 


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 09/14] hw/misc/bcm2835_cprman: add a clock mux skeleton implementation
  2020-10-02 14:42   ` Philippe Mathieu-Daudé
  2020-10-02 15:34     ` Philippe Mathieu-Daudé
@ 2020-10-04 19:34     ` Luc Michel
  2020-10-04 20:17       ` Philippe Mathieu-Daudé
  1 sibling, 1 reply; 41+ messages in thread
From: Luc Michel @ 2020-10-04 19:34 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Peter Maydell, qemu-arm, qemu-devel, Andrew Baumann

On 16:42 Fri 02 Oct     , Philippe Mathieu-Daudé wrote:
> 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?
Well, those are special values used when connecting the clock sources to
the muxes in connect_mux_sources(). They are not channels hence the
name. To keep the code simple, I reused the CprmanPLLChannel type for
mux sources (it is used in bcm2835_cprman_internals.h to describe what
source connects to what mux input).

Ideally this type should be named something like ClockMuxSources (and
CprmanPLLChannel should be a sub-set of this type). But doing so
complicates the code quite a bit so I chose to simply have those three
constants here instead.

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

--


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 09/14] hw/misc/bcm2835_cprman: add a clock mux skeleton implementation
  2020-10-04 19:34     ` Luc Michel
@ 2020-10-04 20:17       ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 41+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-10-04 20:17 UTC (permalink / raw)
  To: Luc Michel; +Cc: Peter Maydell, qemu-arm, qemu-devel, Andrew Baumann

On 10/4/20 9:34 PM, Luc Michel wrote:
> On 16:42 Fri 02 Oct     , Philippe Mathieu-Daudé wrote:
>> 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?
> Well, those are special values used when connecting the clock sources to
> the muxes in connect_mux_sources(). They are not channels hence the
> name. To keep the code simple, I reused the CprmanPLLChannel type for
> mux sources (it is used in bcm2835_cprman_internals.h to describe what
> source connects to what mux input).
> 
> Ideally this type should be named something like ClockMuxSources (and
> CprmanPLLChannel should be a sub-set of this type). But doing so
> complicates the code quite a bit so I chose to simply have those three
> constants here instead.

Understood, OK.


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 10/14] hw/misc/bcm2835_cprman: implement clock mux behaviour
  2020-10-04 18:37       ` Luc Michel
@ 2020-10-05 19:50         ` Luc Michel
  0 siblings, 0 replies; 41+ messages in thread
From: Luc Michel @ 2020-10-05 19:50 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Peter Maydell, qemu-arm, qemu-devel, Andrew Baumann

On 20:37 Sun 04 Oct     , Luc Michel wrote:
> On 16:51 Fri 02 Oct     , Philippe Mathieu-Daudé wrote:
> > On 9/26/20 11:40 PM, Philippe Mathieu-Daudé wrote:
> > > On 9/25/20 12:17 PM, Luc Michel wrote:
> > >> A clock mux can be configured to select one of its 10 sources through
> > >> the cm_ctl register. It also embeds yet another clock divider, composed
> > >> of an integer part and a fractionnal part. The number of bits of each
> > > 
> > > Typo "fractional".
> > > 
> > >> part is mux dependant.
> > > 
> > > Typo "dependent"?
> > > 
> > >>
> > >> Signed-off-by: Luc Michel <luc@lmichel.fr>
> > >> ---
> > >>  hw/misc/bcm2835_cprman.c | 43 +++++++++++++++++++++++++++++++++++++++-
> > >>  1 file changed, 42 insertions(+), 1 deletion(-)
> > >>
> > >> diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c
> > >> index 8df2db0fd9..75bc11939b 100644
> > >> --- a/hw/misc/bcm2835_cprman.c
> > >> +++ b/hw/misc/bcm2835_cprman.c
> > >> @@ -229,19 +229,60 @@ static const TypeInfo cprman_pll_channel_info = {
> > >>  };
> > >>  
> > >>  
> > >>  /* clock mux */
> > >>  
> > >> +static bool clock_mux_is_enabled(CprmanClockMuxState *mux)
> > >> +{
> > >> +    return FIELD_EX32(*mux->reg_cm, CM_CLOCKx_CTL, ENABLE);
> > >> +}
> > >> +
> > >>  static void clock_mux_update(CprmanClockMuxState *mux)
> > >>  {
> > >> -    clock_update(mux->out, 0);
> > >> +    uint64_t freq, div;
> > >> +    uint32_t src = FIELD_EX32(*mux->reg_cm, CM_CLOCKx_CTL, SRC);
> > >> +    bool enabled = clock_mux_is_enabled(mux);
> > >> +
> > >> +    *mux->reg_cm = FIELD_DP32(*mux->reg_cm, CM_CLOCKx_CTL, BUSY, enabled);
> > >> +
> > >> +    if (!enabled) {
> > >> +        clock_update(mux->out, 0);
> > >> +        return;
> > >> +    }
> > >> +
> > >> +    freq = clock_get_hz(mux->srcs[src]);
> > >> +
> > >> +    if (mux->int_bits == 0 && mux->frac_bits == 0) {
> > >> +        clock_update_hz(mux->out, freq);
> > >> +        return;
> > >> +    }
> > >> +
> > >> +    /*
> > >> +     * The divider has an integer and a fractional part. The size of each part
> > >> +     * varies with the muxes (int_bits and frac_bits). Both parts are
> > >> +     * concatenated, with the integer part always starting at bit 12.
> > >> +     */
> > >> +    div = mux->reg_cm[1] >> (R_CM_CLOCKx_DIV_FRAC_LENGTH - mux->frac_bits);
> > >> +    div &= (1 << (mux->int_bits + mux->frac_bits)) - 1;
> > 
> > Eventually:
> > 
> >     div &= MAKE_64BIT_MASK(mux->int_bits + mux->frac_bits, 64);
> I think this is MAKE_64BIT_MASK(0, mux->int_bits + mux->frac_bits)
> (The shift macro parameter is 0 to have the ones positioned at the
> mask's LSBs.
> I'll use this macro in my next re-roll.
Actually, I won't, because switching to muldiv64 implied some
modifications. The muldiv64 divisor parameter is uint32_t. Since there
is no MAKE_32BIT_MASK, and I don't use all the "features" of this macro
anyway (shift = 0 in my case), I'll keep this form (but I'll switch to
uin32_t for div).

-- 
Luc

> 
> > 
> > >> +
> > >> +    freq = (freq << mux->frac_bits) / div;
> > 
> > Maybe:
> > 
> >     freq = muldiv64(freq, 1 << mux->frac_bits, div);
> OK
> 
> > 
> > >> +
> > >> +    clock_update_hz(mux->out, freq);
> > >>  }
> > >>  
> > >>  static void clock_mux_src_update(void *opaque)
> > >>  {
> > >>      CprmanClockMuxState **backref = opaque;
> > >>      CprmanClockMuxState *s = *backref;
> > >> +    CprmanClockMuxSource src = backref - s->backref;
> > >> +    uint32_t current_src;
> > 
> > Maybe avoid current_src and use in place.
> OK
> 
> > 
> > Otherwise:
> > Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
> Thanks !
> 
> -- 
> Luc
> 
> > 
> > >> +
> > >> +    current_src = FIELD_EX32(*s->reg_cm, CM_CLOCKx_CTL, SRC);
> > >> +
> > >> +    if (current_src != src) {
> > >> +        return;
> > >> +    }
> > >>  
> > >>      clock_mux_update(s);
> > >>  }
> > >>  
> > >>  static void clock_mux_init(Object *obj)
> > >>
> > > 
> 
> -- 

-- 


^ permalink raw reply	[flat|nested] 41+ messages in thread

end of thread, other threads:[~2020-10-05 19:50 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-09-25 10:17 [PATCH 00/14] raspi: add the bcm2835 cprman clock manager Luc Michel
2020-09-25 10:17 ` [PATCH 01/14] hw/core/clock: provide the VMSTATE_ARRAY_CLOCK macro Luc Michel
2020-09-26 20:36   ` Philippe Mathieu-Daudé
2020-09-28  8:38   ` Damien Hedde
2020-09-25 10:17 ` [PATCH 02/14] hw/core/clock: trace clock values in Hz instead of ns Luc Michel
2020-09-26 20:36   ` Philippe Mathieu-Daudé
2020-09-28  8:42     ` Damien Hedde
2020-09-25 10:17 ` [PATCH 03/14] hw/arm/raspi: fix cprman base address Luc Michel
2020-09-26 21:04   ` Philippe Mathieu-Daudé
2020-09-25 10:17 ` [PATCH 04/14] hw/arm/raspi: add a skeleton implementation of the cprman Luc Michel
2020-09-26 21:05   ` Philippe Mathieu-Daudé
2020-09-28  8:45     ` Luc Michel
2020-10-02 14:37       ` Philippe Mathieu-Daudé
2020-10-03 11:54         ` Luc Michel
2020-10-03 18:14           ` Philippe Mathieu-Daudé
2020-09-25 10:17 ` [PATCH 05/14] hw/misc/bcm2835_cprman: add a PLL skeleton implementation Luc Michel
2020-09-26 21:17   ` Philippe Mathieu-Daudé
2020-09-25 10:17 ` [PATCH 06/14] hw/misc/bcm2835_cprman: implement PLLs behaviour Luc Michel
2020-09-26 21:26   ` Philippe Mathieu-Daudé
2020-09-25 10:17 ` [PATCH 07/14] hw/misc/bcm2835_cprman: add a PLL channel skeleton implementation Luc Michel
2020-09-26 21:32   ` Philippe Mathieu-Daudé
2020-09-25 10:17 ` [PATCH 08/14] hw/misc/bcm2835_cprman: implement PLL channels behaviour Luc Michel
2020-09-26 21:36   ` Philippe Mathieu-Daudé
2020-09-25 10:17 ` [PATCH 09/14] hw/misc/bcm2835_cprman: add a clock mux skeleton implementation Luc Michel
2020-10-02 14:42   ` Philippe Mathieu-Daudé
2020-10-02 15:34     ` Philippe Mathieu-Daudé
2020-10-04 19:34     ` Luc Michel
2020-10-04 20:17       ` Philippe Mathieu-Daudé
2020-09-25 10:17 ` [PATCH 10/14] hw/misc/bcm2835_cprman: implement clock mux behaviour Luc Michel
2020-09-26 21:40   ` Philippe Mathieu-Daudé
2020-10-02 14:51     ` Philippe Mathieu-Daudé
2020-10-04 18:37       ` Luc Michel
2020-10-05 19:50         ` Luc Michel
2020-09-25 10:17 ` [PATCH 11/14] hw/misc/bcm2835_cprman: add the DSI0HSCK multiplexer Luc Michel
2020-10-02 14:55   ` Philippe Mathieu-Daudé
2020-09-25 10:17 ` [PATCH 12/14] hw/misc/bcm2835_cprman: add sane reset values to the registers Luc Michel
2020-09-25 10:17 ` [RFC PATCH 13/14] hw/char/pl011: add a clock input Luc Michel
2020-09-25 10:36   ` Philippe Mathieu-Daudé
2020-09-25 10:17 ` [RFC PATCH 14/14] hw/arm/bcm2835_peripherals: connect the UART clock Luc Michel
2020-09-25 11:56 ` [PATCH 00/14] raspi: add the bcm2835 cprman clock manager no-reply
2020-09-25 12:55 ` Philippe Mathieu-Daudé

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.