Page MenuHomeFreeBSD

D59095.id.diff
No OneTemporary

D59095.id.diff

diff --git a/sys/arm64/rpi/rpiclock.h b/sys/arm64/rpi/rpiclock.h
new file mode 100644
--- /dev/null
+++ b/sys/arm64/rpi/rpiclock.h
@@ -0,0 +1,310 @@
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/endian.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofw_pci.h>
+
+#include <dev/fdt/fdt_intr.h>
+#include <dev/fdt/simplebus.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/resource.h>
+
+#include <dev/clk/clk.h>
+
+#include "clkdev_if.h"
+
+#include <dt-bindings/clock/raspberrypi,rp1-clocks.h>
+
+#define RD4(sc, reg, val) CLKDEV_READ_4((sc)->clkdev, reg, val)
+#define WR4(sc, reg, val) CLKDEV_WRITE_4((sc)->clkdev, reg, val)
+#define MD4(sc, reg, mask, set) CLKDEV_MODIFY_4((sc)->clkdev, reg, mask, set)
+#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev)
+#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev)
+
+#define PLL_SYS_OFFSET 0x08000
+#define PLL_SYS_CS (PLL_SYS_OFFSET + 0x00)
+#define PLL_SYS_PWR (PLL_SYS_OFFSET + 0x04)
+#define PLL_SYS_FBDIV_INT (PLL_SYS_OFFSET + 0x08)
+#define PLL_SYS_FBDIV_FRAC (PLL_SYS_OFFSET + 0x0c)
+#define PLL_SYS_PRIM (PLL_SYS_OFFSET + 0x10)
+#define PLL_SYS_SEC (PLL_SYS_OFFSET + 0x14)
+
+#define PLL_AUDIO_OFFSET 0x0c000
+#define PLL_AUDIO_CS (PLL_AUDIO_OFFSET + 0x00)
+#define PLL_AUDIO_PWR (PLL_AUDIO_OFFSET + 0x04)
+#define PLL_AUDIO_FBDIV_INT (PLL_AUDIO_OFFSET + 0x08)
+#define PLL_AUDIO_FBDIV_FRAC (PLL_AUDIO_OFFSET + 0x0c)
+#define PLL_AUDIO_PRIM (PLL_AUDIO_OFFSET + 0x10)
+#define PLL_AUDIO_SEC (PLL_AUDIO_OFFSET + 0x14)
+#define PLL_AUDIO_TERN (PLL_AUDIO_OFFSET + 0x18)
+
+#define PLL_VIDEO_OFFSET 0x10000
+#define PLL_VIDEO_CS (PLL_VIDEO_OFFSET + 0x00)
+#define PLL_VIDEO_PWR (PLL_VIDEO_OFFSET + 0x04)
+#define PLL_VIDEO_FBDIV_INT (PLL_VIDEO_OFFSET + 0x08)
+#define PLL_VIDEO_FBDIV_FRAC (PLL_VIDEO_OFFSET + 0x0c)
+#define PLL_VIDEO_PRIM (PLL_VIDEO_OFFSET + 0x10)
+#define PLL_VIDEO_SEC (PLL_VIDEO_OFFSET + 0x14)
+
+#define GPCLK_OE_CTRL 0x00000
+
+#define CLK_SYS_OFFSET 0x00014
+#define CLK_SYS_CTRL (CLK_SYS_OFFSET + 0x00)
+#define CLK_SYS_DIV_INT (CLK_SYS_OFFSET + 0x04)
+#define CLK_SYS_SEL (CLK_SYS_OFFSET + 0x0c)
+
+#define CLK_SLOW_OFFSET 0x00024
+#define CLK_SLOW_SYS_CTRL (CLK_SLOW_OFFSET + 0x00)
+#define CLK_SLOW_SYS_DIV_INT (CLK_SLOW_OFFSET + 0x04)
+#define CLK_SLOW_SYS_SEL (CLK_SLOW_OFFSET + 0x0c)
+
+#define CLK_DMA_OFFSET 0x00044
+#define CLK_DMA_CTRL (CLK_DMA_OFFSET + 0x00)
+#define CLK_DMA_DIV_INT (CLK_DMA_OFFSET + 0x04)
+#define CLK_DMA_SEL (CLK_DMA_OFFSET + 0x0c)
+
+#define CLK_UART_OFFSET 0x00054
+#define CLK_UART_CTRL (CLK_UART_OFFSET + 0x00)
+#define CLK_UART_DIV_INT (CLK_UART_OFFSET + 0x04)
+#define CLK_UART_SEL (CLK_UART_OFFSET + 0x0c)
+
+#define CLK_ETH_OFFSET 0x00064
+#define CLK_ETH_CTRL (CLK_ETH_OFFSET + 0x00)
+#define CLK_ETH_DIV_INT (CLK_ETH_OFFSET + 0x04)
+#define CLK_ETH_SEL (CLK_ETH_OFFSET + 0x0c)
+
+#define CLK_PWM0_OFFSET 0x00074
+#define CLK_PWM0_CTRL (CLK_PWM0_OFFSET + 0x00)
+#define CLK_PWM0_DIV_INT (CLK_PWM0_OFFSET + 0x04)
+#define CLK_PWM0_DIV_FRAC (CLK_PWM0_OFFSET + 0x08)
+#define CLK_PWM0_SEL (CLK_PWM0_OFFSET + 0x0c)
+
+#define CLK_PWM1_OFFSET 0x00084
+#define CLK_PWM1_CTRL (CLK_PWM1_OFFSET + 0x00)
+#define CLK_PWM1_DIV_INT (CLK_PWM1_OFFSET + 0x04)
+#define CLK_PWM1_DIV_FRAC (CLK_PWM1_OFFSET + 0x08)
+#define CLK_PWM1_SEL (CLK_PWM1_OFFSET + 0x0c)
+
+#define CLK_AUDIO_IN_OFFSET 0x00094
+#define CLK_AUDIO_IN_CTRL (CLK_AUDIO_IN_OFFSET + 0x00)
+#define CLK_AUDIO_IN_DIV_INT (CLK_AUDIO_IN_OFFSET + 0x04)
+#define CLK_AUDIO_IN_SEL (CLK_AUDIO_IN_OFFSET + 0x0c)
+
+#define CLK_AUDIO_OUT_OFFSET 0x000a4
+#define CLK_AUDIO_OUT_CTRL (CLK_AUDIO_OUT_OFFSET + 0x00)
+#define CLK_AUDIO_OUT_DIV_INT (CLK_AUDIO_OUT_OFFSET + 0x04)
+#define CLK_AUDIO_OUT_SEL (CLK_AUDIO_OUT_OFFSET + 0x0c)
+
+#define CLK_I2S_OFFSET 0x000b4
+#define CLK_I2S_CTRL (CLK_I2S_OFFSET + 0x00)
+#define CLK_I2S_DIV_INT (CLK_I2S_OFFSET + 0x04)
+#define CLK_I2S_SEL (CLK_I2S_OFFSET + 0x0c)
+
+#define CLK_MIPI0_CFG_OFFSET 0x000c4
+#define CLK_MIPI0_CFG_CTRL (CLK_MIPI0_CFG_OFFSET + 0x00)
+#define CLK_MIPI0_CFG_DIV_INT (CLK_MIPI0_CFG_OFFSET + 0x04)
+#define CLK_MIPI0_CFG_SEL (CLK_MIPI0_CFG_OFFSET + 0x0c)
+
+#define CLK_MIPI1_CFG_OFFSET 0x000d4
+#define CLK_MIPI1_CFG_CTRL (CLK_MIPI1_CFG_OFFSET + 0x00)
+#define CLK_MIPI1_CFG_DIV_INT (CLK_MIPI1_CFG_OFFSET + 0x04)
+#define CLK_MIPI1_CFG_SEL (CLK_MIPI1_CFG_OFFSET + 0x0c)
+
+#define CLK_PCIE_AUX_OFFSET 0x000e4
+#define CLK_PCIE_AUX_CTRL (CLK_PCIE_AUX_OFFSET + 0x00)
+#define CLK_PCIE_AUX_DIV_INT (CLK_PCIE_AUX_OFFSET + 0x04)
+#define CLK_PCIE_AUX_SEL (CLK_PCIE_AUX_OFFSET + 0x0c)
+
+#define CLK_USBH0_MICROFRAME_OFFSET 0x000f4
+#define CLK_USBH0_MICROFRAME_CTRL (CLK_USBH0_MICROFRAME_OFFSET + 0x00)
+#define CLK_USBH0_MICROFRAME_DIV_INT (CLK_USBH0_MICROFRAME_OFFSET + 0x04)
+#define CLK_USBH0_MICROFRAME_SEL (CLK_USBH0_MICROFRAME_OFFSET + 0x0c)
+
+#define CLK_USBH1_MICROFRAME_OFFSET 0x00104
+#define CLK_USBH1_MICROFRAME_CTRL (CLK_USBH1_MICROFRAME_OFFSET + 0x00)
+#define CLK_USBH1_MICROFRAME_DIV_INT (CLK_USBH1_MICROFRAME_OFFSET + 0x04)
+#define CLK_USBH1_MICROFRAME_SEL (CLK_USBH1_MICROFRAME_OFFSET + 0x0c)
+
+#define CLK_USBH0_SUSPEND_OFFSET 0x00114
+#define CLK_USBH0_SUSPEND_CTRL (CLK_USBH0_SUSPEND_OFFSET + 0x00)
+#define CLK_USBH0_SUSPEND_DIV_INT (CLK_USBH0_SUSPEND_OFFSET + 0x04)
+#define CLK_USBH0_SUSPEND_SEL (CLK_USBH0_SUSPEND_OFFSET + 0x0c)
+
+#define CLK_USBH1_SUSPEND_OFFSET 0x00124
+#define CLK_USBH1_SUSPEND_CTRL (CLK_USBH1_SUSPEND_OFFSET + 0x00)
+#define CLK_USBH1_SUSPEND_DIV_INT (CLK_USBH1_SUSPEND_OFFSET + 0x04)
+#define CLK_USBH1_SUSPEND_SEL (CLK_USBH1_SUSPEND_OFFSET + 0x0c)
+
+#define CLK_ETH_TSU_OFFSET 0x00134
+#define CLK_ETH_TSU_CTRL (CLK_ETH_TSU_OFFSET + 0x00)
+#define CLK_ETH_TSU_DIV_INT (CLK_ETH_TSU_OFFSET + 0x04)
+#define CLK_ETH_TSU_SEL (CLK_ETH_TSU_OFFSET + 0x0c)
+
+#define CLK_ADC_OFFSET 0x00144
+#define CLK_ADC_CTRL (CLK_ADC_OFFSET + 0x00)
+#define CLK_ADC_DIV_INT (CLK_ADC_OFFSET + 0x04)
+#define CLK_ADC_SEL (CLK_ADC_OFFSET + 0x0c)
+
+#define CLK_SDIO_TIMER_OFFSET 0x00154
+#define CLK_SDIO_TIMER_CTRL (CLK_SDIO_TIMER_OFFSET + 0x00)
+#define CLK_SDIO_TIMER_DIV_INT (CLK_SDIO_TIMER_OFFSET + 0x04)
+#define CLK_SDIO_TIMER_SEL (CLK_SDIO_TIMER_OFFSET + 0x0c)
+
+#define CLK_SDIO_ALT_SRC_OFFSET 0x00164
+#define CLK_SDIO_ALT_SRC_CTRL (CLK_SDIO_ALT_SRC_OFFSET + 0x00)
+#define CLK_SDIO_ALT_SRC_DIV_INT (CLK_SDIO_ALT_SRC_OFFSET + 0x04)
+#define CLK_SDIO_ALT_SRC_SEL (CLK_SDIO_ALT_SRC_OFFSET + 0x0c)
+
+#define CLK_GP0_OFFSET 0x00174
+#define CLK_GP0_CTRL (CLK_GP0_OFFSET + 0x00)
+#define CLK_GP0_DIV_INT (CLK_GP0_OFFSET + 0x04)
+#define CLK_GP0_DIV_FRAC (CLK_GP0_OFFSET + 0x08)
+#define CLK_GP0_SEL (CLK_GP0_OFFSET + 0x0c)
+
+#define CLK_GP1_OFFSET 0x00184
+#define CLK_GP1_CTRL (CLK_GP1_OFFSET + 0x00)
+#define CLK_GP1_DIV_INT (CLK_GP1_OFFSET + 0x04)
+#define CLK_GP1_DIV_FRAC (CLK_GP1_OFFSET + 0x08)
+#define CLK_GP1_SEL (CLK_GP1_OFFSET + 0x0c)
+
+#define CLK_GP2_OFFSET 0x00194
+#define CLK_GP2_CTRL (CLK_GP2_OFFSET + 0x00)
+#define CLK_GP2_DIV_INT (CLK_GP2_OFFSET + 0x04)
+#define CLK_GP2_DIV_FRAC (CLK_GP2_OFFSET + 0x08)
+#define CLK_GP2_SEL (CLK_GP2_OFFSET + 0x0c)
+
+#define CLK_GP3_OFFSET 0x001a4
+#define CLK_GP3_CTRL (CLK_GP3_OFFSET + 0x00)
+#define CLK_GP3_DIV_INT (CLK_GP3_OFFSET + 0x04)
+#define CLK_GP3_DIV_FRAC (CLK_GP3_OFFSET + 0x08)
+#define CLK_GP3_SEL (CLK_GP3_OFFSET + 0x0c)
+
+#define CLK_GP4_OFFSET 0x001b4
+#define CLK_GP4_CTRL (CLK_GP4_OFFSET + 0x00)
+#define CLK_GP4_DIV_INT (CLK_GP4_OFFSET + 0x04)
+#define CLK_GP4_DIV_FRAC (CLK_GP4_OFFSET + 0x08)
+#define CLK_GP4_SEL (CLK_GP4_OFFSET + 0x0c)
+
+#define CLK_GP5_OFFSET 0x001c4
+#define CLK_GP5_CTRL (CLK_GP5_OFFSET + 0x00)
+#define CLK_GP5_DIV_INT (CLK_GP5_OFFSET + 0x04)
+#define CLK_GP5_DIV_FRAC (CLK_GP5_OFFSET + 0x08)
+#define CLK_GP5_SEL (CLK_GP5_OFFSET + 0x0c)
+
+#define CLK_SYS_RESUS_CTRL 0x0020c
+
+#define CLK_SLOW_SYS_RESUS_CTRL 0x00214
+
+#define FC0_OFFSET 0x0021c
+#define FC0_REF_KHZ (FC0_OFFSET + 0x00)
+#define FC0_MIN_KHZ (FC0_OFFSET + 0x04)
+#define FC0_MAX_KHZ (FC0_OFFSET + 0x08)
+#define FC0_DELAY (FC0_OFFSET + 0x0c)
+#define FC0_INTERVAL (FC0_OFFSET + 0x10)
+#define FC0_SRC (FC0_OFFSET + 0x14)
+#define FC0_STATUS (FC0_OFFSET + 0x18)
+#define FC0_RESULT (FC0_OFFSET + 0x1c)
+#define FC_SIZE 0x20
+#define FC_COUNT 8
+#define FC_NUM(idx, off) ((idx) * 32 + (off))
+
+#define AUX_SEL 1
+
+#define VIDEO_CLOCKS_OFFSET 0x4000
+#define VIDEO_CLK_VEC_CTRL (VIDEO_CLOCKS_OFFSET + 0x0000)
+#define VIDEO_CLK_VEC_DIV_INT (VIDEO_CLOCKS_OFFSET + 0x0004)
+#define VIDEO_CLK_VEC_SEL (VIDEO_CLOCKS_OFFSET + 0x000c)
+#define VIDEO_CLK_DPI_CTRL (VIDEO_CLOCKS_OFFSET + 0x0010)
+#define VIDEO_CLK_DPI_DIV_INT (VIDEO_CLOCKS_OFFSET + 0x0014)
+#define VIDEO_CLK_DPI_SEL (VIDEO_CLOCKS_OFFSET + 0x001c)
+#define VIDEO_CLK_MIPI0_DPI_CTRL (VIDEO_CLOCKS_OFFSET + 0x0020)
+#define VIDEO_CLK_MIPI0_DPI_DIV_INT (VIDEO_CLOCKS_OFFSET + 0x0024)
+#define VIDEO_CLK_MIPI0_DPI_DIV_FRAC (VIDEO_CLOCKS_OFFSET + 0x0028)
+#define VIDEO_CLK_MIPI0_DPI_SEL (VIDEO_CLOCKS_OFFSET + 0x002c)
+#define VIDEO_CLK_MIPI1_DPI_CTRL (VIDEO_CLOCKS_OFFSET + 0x0030)
+#define VIDEO_CLK_MIPI1_DPI_DIV_INT (VIDEO_CLOCKS_OFFSET + 0x0034)
+#define VIDEO_CLK_MIPI1_DPI_DIV_FRAC (VIDEO_CLOCKS_OFFSET + 0x0038)
+#define VIDEO_CLK_MIPI1_DPI_SEL (VIDEO_CLOCKS_OFFSET + 0x003c)
+
+#define DIV_INT_8BIT_MAX (0xff << 0)
+#define DIV_INT_16BIT_MAX (0xffff << 0)
+#define DIV_INT_24BIT_MAX (0xffffff << 0)
+
+#define FC0_STATUS_DONE (1 << 4)
+#define FC0_STATUS_RUNNING (1 << 8)
+#define FC0_RESULT_FRAC_SHIFT 5
+
+#define PLL_PRIM_DIV1_SHIFT 16
+#define PLL_PRIM_DIV1_MASK (0x7 << PLL_PRIM_DIV1_SHIFT)
+#define PLL_PRIM_DIV2_SHIFT 12
+#define PLL_PRIM_DIV2_MASK (0x7 << PLL_PRIM_DIV2_SHIFT)
+
+#define PLL_SEC_DIV_SHIFT 8
+#define PLL_SEC_DIV_MASK (0x1f << PLL_SEC_DIV_SHIFT)
+
+#define PLL_CS_LOCK (1 << 31)
+#define PLL_CS_REFDIV_SHIFT 1
+#define PLL_CS_REFDIV_MASK (1 << PLL_CS_REFDIV_SFHIFT)
+
+#define PLL_PWR_PD (1 << 0)
+#define PLL_PWR_DACPD (1 << 1)
+#define PLL_PWR_DSMPD (1 << 2)
+#define PLL_PWR_POSTDIVPD (1 << 3)
+#define PLL_PWR_4PHASEPD (1 << 4)
+#define PLL_PWR_VCOPD (1 << 5)
+#define PLL_PWR_MASK (0x3f << 0)
+
+#define PLL_SEC_RST (1 << 16)
+#define PLL_SEC_IMPL (1 << 31)
+
+/* PLL phase output for both PRI and SEC */
+#define PLL_PH_EN (1 << 4)
+#define PLL_PH_PHASE_SHIFT 0
+
+#define RP1_PLL_PHASE_0 0
+#define RP1_PLL_PHASE_90 1
+#define RP1_PLL_PHASE_180 2
+#define RP1_PLL_PHASE_270 3
+
+/* Clock fields for all clocks */
+#define CLK_CTRL_ENABLE (1 << 11)
+#define CLK_CTRL_AUXSRC_SHIFT 5
+#define CLK_CTRL_AUXSRC_MASK (0x1f << CLK_CTRL_AUXSRC_SHIFT)
+#define CLK_CTRL_SRC_SHIFT 0
+#define CLK_DIV_FRAC_BITS 16
+
+#define LOCK_TIMEOUT_US 100000
+#define LOCK_POLL_DELAY_US 5
+
+#define MAX_CLK_PARENTS 16
+
+#define PLL_DIV_INVALID 19
+
+#define DIV_ROUND(x, y) ((x) / (y))
+
+struct rpiclock_softc {
+ device_t dev;
+
+ struct clkdom *clkdom;
+
+ struct resource *mem_res;
+
+ struct mtx mtx;
+};
+
+void rpiclock_init_plls(struct rpiclock_softc *sc);
+void rpiclock_init_periphs(struct rpiclock_softc *sc);
diff --git a/sys/arm64/rpi/rpiclock.c b/sys/arm64/rpi/rpiclock.c
new file mode 100644
--- /dev/null
+++ b/sys/arm64/rpi/rpiclock.c
@@ -0,0 +1,187 @@
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/endian.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofw_pci.h>
+
+#include <dev/fdt/fdt_intr.h>
+#include <dev/fdt/simplebus.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/resource.h>
+
+#include <dev/clk/clk.h>
+
+#include "pic_if.h"
+
+#include "rpiclock.h"
+
+static struct ofw_compat_data compat_data[] = {
+ { "raspberrypi,rp1-clocks", 1 },
+ { NULL, 0 }
+};
+
+/*
+ * Determine if a given device can be served by the rpiclock driver.
+ */
+static int
+rpiclock_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev) ||
+ !ofw_bus_search_compatible(dev, compat_data)->ocd_data) {
+ return (ENXIO);
+ }
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static void
+rpiclock_register_clocks(device_t dev)
+{
+ struct rpiclock_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->clkdom = clkdom_create(dev);
+ MPASS(sc->clkdom != NULL);
+
+ rpiclock_init_plls(sc);
+ rpiclock_init_periphs(sc);
+
+ clkdom_finit(sc->clkdom);
+
+ if (bootverbose)
+ clkdom_dump(sc->clkdom);
+}
+
+static int
+rpiclock_attach(device_t dev)
+{
+ struct rpiclock_softc *sc;
+ phandle_t xref, node;
+ int rid, error;
+
+ sc = device_get_softc(dev);
+ mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ sc->dev = dev;
+
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "could not allocate memory resource\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ rpiclock_register_clocks(dev);
+
+ node = ofw_bus_get_node(dev);
+ if (node <= 0)
+ panic("%s called on non-OF device\n", __func__);
+ xref = OF_xref_from_node(node);
+ OF_device_register_xref(xref, dev);
+
+ return (0);
+
+fail:
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, sc->mem_res);
+
+ return error;
+}
+
+static int
+rpiclock_read_4(device_t dev, bus_addr_t base, uint32_t *val)
+{
+ struct rpiclock_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ *val = bus_read_4(sc->mem_res, base);
+ return (0);
+}
+
+static int
+rpiclock_write_4(device_t dev, bus_addr_t base, uint32_t val)
+{
+ struct rpiclock_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ bus_write_4(sc->mem_res, base, val);
+ return (0);
+}
+
+static int
+rpiclock_modify_4(device_t dev, bus_addr_t base, uint32_t clr, uint32_t set)
+{
+ uint32_t val;
+ int error;
+
+ if ((error = rpiclock_read_4(dev, base, &val)))
+ return error;
+
+ val &= ~clr;
+ val |= set;
+
+ if ((error = rpiclock_write_4(dev, base, val)))
+ return error;
+
+ return (0);
+}
+
+static void
+rpiclock_device_lock(device_t dev)
+{
+ struct rpiclock_softc *sc;
+
+ sc = device_get_softc(dev);
+ mtx_lock(&sc->mtx);
+}
+
+static void
+rpiclock_device_unlock(device_t dev)
+{
+ struct rpiclock_softc *sc;
+
+ sc = device_get_softc(dev);
+ mtx_unlock(&sc->mtx);
+}
+
+static device_method_t rpiclock_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, rpiclock_probe),
+ DEVMETHOD(device_attach, rpiclock_attach),
+
+ /* clkdev interface */
+ DEVMETHOD(clkdev_write_4, rpiclock_write_4),
+ DEVMETHOD(clkdev_read_4, rpiclock_read_4),
+ DEVMETHOD(clkdev_modify_4, rpiclock_modify_4),
+ DEVMETHOD(clkdev_device_lock, rpiclock_device_lock),
+ DEVMETHOD(clkdev_device_unlock, rpiclock_device_unlock),
+
+ DEVMETHOD_END
+};
+
+static driver_t rpiclock_driver = {
+ "rpiclock",
+ rpiclock_methods,
+ sizeof(struct rpiclock_softc),
+};
+
+DRIVER_MODULE(rpiclock, simplebus, rpiclock_driver, NULL, NULL);
diff --git a/sys/arm64/rpi/rpiclock_periph.c b/sys/arm64/rpi/rpiclock_periph.c
new file mode 100644
--- /dev/null
+++ b/sys/arm64/rpi/rpiclock_periph.c
@@ -0,0 +1,756 @@
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/endian.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofw_pci.h>
+
+#include <dev/fdt/fdt_intr.h>
+#include <dev/fdt/simplebus.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/resource.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+
+#include <dev/clk/clk.h>
+
+#include "pic_if.h"
+#include "rpiclock.h"
+
+struct rpi_periph_sc {
+ device_t clkdev;
+
+ uint32_t ctrl_reg;
+ uint32_t div_int_reg;
+ uint32_t div_frac_reg;
+ uint32_t sel_reg;
+ uint32_t div_int_max;
+ uint32_t clk_src_mask;
+
+ /*
+ * XXX: Hack for determining whether to use an auxiliary clock source;
+ * we assume all sources have only standard or auxiliary parents.
+ */
+ bool std_only;
+};
+
+struct clknode_periph_def {
+ struct clknode_init_def clkdef;
+
+ uint32_t ctrl_reg;
+ uint32_t div_int_reg;
+ uint32_t div_frac_reg;
+ uint32_t sel_reg;
+ uint32_t div_int_max;
+ uint32_t clk_src_mask;
+
+ bool std_only;
+} rpiclock_periph_defs[] = {
+ {
+ .clkdef = {
+ .name = "clk_sys",
+ .id = RP1_CLK_SYS,
+ .parent_names = (const char *[]){
+ "xosc",
+ NULL,
+ "pll_sys"
+ },
+ .parent_cnt = 3,
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .ctrl_reg = CLK_SYS_CTRL,
+ .div_int_reg = CLK_SYS_DIV_INT,
+ .div_frac_reg = 0,
+ .sel_reg = CLK_SYS_SEL,
+ .div_int_max = DIV_INT_24BIT_MAX,
+ .clk_src_mask = 0x3,
+ .std_only = 1
+ },
+ {
+ .clkdef = {
+ .name = "clk_slow_sys",
+ .id = RP1_CLK_SLOW_SYS,
+ .parent_names = (const char *[]){"xosc"},
+ .parent_cnt = 1,
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .ctrl_reg = CLK_SLOW_SYS_CTRL,
+ .div_int_reg = CLK_SLOW_SYS_DIV_INT,
+ .div_frac_reg = 0,
+ .sel_reg = CLK_SLOW_SYS_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .clk_src_mask = 0x1,
+ .std_only = 1
+ },
+ {
+ .clkdef = {
+ .name = "clk_uart",
+ .id = RP1_CLK_UART,
+ .parent_names = (const char *[]){
+ "pll_sys_pri_ph",
+ "pll_video",
+ "xosc"
+ },
+ .parent_cnt = 3,
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .ctrl_reg = CLK_UART_CTRL,
+ .div_int_reg = CLK_UART_DIV_INT,
+ .sel_reg = CLK_UART_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .std_only = 0
+ },
+ {
+ .clkdef = {
+ .name = "clk_eth",
+ .id = RP1_CLK_ETH,
+ .parent_names = (const char *[]){NULL},
+ .parent_cnt = 0,
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .ctrl_reg = CLK_ETH_CTRL,
+ .div_int_reg = CLK_ETH_DIV_INT,
+ .div_frac_reg = 0,
+ .sel_reg = CLK_ETH_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .std_only = 1
+ },
+ {
+ .clkdef = {
+ .name = "clk_pwm0",
+ .id = RP1_CLK_PWM0,
+ .parent_names = (const char *[]){
+ "pll_audio_pri_ph",
+ "pll_video_sec",
+ "xosc"
+ },
+ .parent_cnt = 3,
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .ctrl_reg = CLK_PWM0_CTRL,
+ .div_int_reg = CLK_PWM0_DIV_INT,
+ .div_frac_reg = CLK_PWM0_DIV_FRAC,
+ .sel_reg = CLK_PWM0_SEL,
+ .div_int_max = DIV_INT_16BIT_MAX,
+ .std_only = 0
+ },
+ {
+ .clkdef = {
+ .name = "clk_pwm1",
+ .id = RP1_CLK_PWM1,
+ .parent_names = (const char *[]){
+ "pll_audio_pri_ph",
+ "pll_video_sec",
+ "xosc"
+ },
+ .parent_cnt = 3,
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .ctrl_reg = CLK_PWM1_CTRL,
+ .div_int_reg = CLK_PWM1_DIV_INT,
+ .div_frac_reg = CLK_PWM1_DIV_FRAC,
+ .sel_reg = CLK_PWM1_SEL,
+ .div_int_max = DIV_INT_16BIT_MAX,
+ .std_only = 0
+ },
+ {
+ .clkdef = {
+ .name = "clk_audio_in",
+ .id = RP1_CLK_AUDIO_IN,
+ .parent_names = (const char *[]){NULL},
+ .parent_cnt = 0,
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .ctrl_reg = CLK_AUDIO_IN_CTRL,
+ .div_int_reg = CLK_AUDIO_IN_DIV_INT,
+ .div_frac_reg = 0,
+ .sel_reg = CLK_AUDIO_IN_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .std_only = 1
+ },
+ {
+ .clkdef = {
+ .name = "clk_audio_out",
+ .id = RP1_CLK_AUDIO_OUT,
+ .parent_names = (const char *[]){NULL},
+ .parent_cnt = 0,
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .ctrl_reg = CLK_AUDIO_OUT_CTRL,
+ .div_int_reg = CLK_AUDIO_OUT_DIV_INT,
+ .div_frac_reg = 0,
+ .sel_reg = CLK_AUDIO_OUT_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .std_only = 1
+ },
+ {
+ .clkdef = {
+ .name = "clk_i2s",
+ .id = RP1_CLK_I2S,
+ .parent_names = (const char *[]){
+ "xosc",
+ "pll_audio",
+ "pll_audio_sec"
+ },
+ .parent_cnt = 3,
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .ctrl_reg = CLK_I2S_CTRL,
+ .div_int_reg = CLK_I2S_DIV_INT,
+ .div_frac_reg = 0,
+ .sel_reg = CLK_I2S_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .std_only = 0
+ },
+ {
+ .clkdef = {
+ .name = "clk_mipi0_cfg",
+ .id = RP1_CLK_MIPI0_CFG,
+ .parent_names = (const char *[]){"xosc"},
+ .parent_cnt = 1,
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .ctrl_reg = CLK_MIPI0_CFG_CTRL,
+ .div_int_reg = CLK_MIPI0_CFG_DIV_INT,
+ .div_frac_reg = 0,
+ .sel_reg = CLK_MIPI0_CFG_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .std_only = 0
+ },
+ {
+ .clkdef = {
+ .name = "clk_mipi1_cfg",
+ .id = RP1_CLK_MIPI1_CFG,
+ .parent_names = (const char *[]){"xosc"},
+ .parent_cnt = 1,
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .ctrl_reg = CLK_MIPI1_CFG_CTRL,
+ .div_int_reg = CLK_MIPI1_CFG_DIV_INT,
+ .div_frac_reg = 0,
+ .sel_reg = CLK_MIPI1_CFG_SEL,
+ .clk_src_mask = 1,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .std_only = 0
+ },
+ {
+ .clkdef = {
+ .name = "clk_eth_tsu",
+ .id = RP1_CLK_ETH_TSU,
+ .parent_names = (const char *[]){"xosc"},
+ .parent_cnt = 1,
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .ctrl_reg = CLK_ETH_TSU_CTRL,
+ .div_int_reg = CLK_ETH_TSU_DIV_INT,
+ .div_frac_reg = 0,
+ .sel_reg = CLK_ETH_TSU_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .std_only = 0
+ },
+ {
+ .clkdef = {
+ .name = "clk_adc",
+ .id = RP1_CLK_ADC,
+ .parent_names = (const char *[]){"xosc"},
+ .parent_cnt = 1,
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .ctrl_reg = CLK_ADC_CTRL,
+ .div_int_reg = CLK_ADC_DIV_INT,
+ .div_frac_reg = 0,
+ .sel_reg = CLK_ADC_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .std_only = 0
+ },
+ {
+ .clkdef = {
+ .name = "clk_sdio_timer",
+ .id = RP1_CLK_SDIO_TIMER,
+ .parent_names = (const char *[]){"xosc"},
+ .parent_cnt = 1,
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .ctrl_reg = CLK_SDIO_TIMER_CTRL,
+ .div_int_reg = CLK_SDIO_TIMER_DIV_INT,
+ .div_frac_reg = 0,
+ .sel_reg = CLK_SDIO_TIMER_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .std_only = 0
+ },
+ {
+ .clkdef = {
+ .name = "clk_sdio_alt_src",
+ .id = RP1_CLK_SDIO_ALT_SRC,
+ .parent_names = (const char *[]){"pll_sys"},
+ .parent_cnt = 1,
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .ctrl_reg = CLK_SDIO_ALT_SRC_CTRL,
+ .div_int_reg = CLK_SDIO_ALT_SRC_DIV_INT,
+ .div_frac_reg = 0,
+ .sel_reg = CLK_SDIO_ALT_SRC_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .std_only = 0
+ },
+ {
+ .clkdef = {
+ .name = "clk_gp0",
+ .id = RP1_CLK_GP0,
+ .parent_names = (const char *[]){"xosc"},
+ .parent_cnt = 1,
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .ctrl_reg = CLK_GP0_CTRL,
+ .div_int_reg = CLK_GP0_DIV_INT,
+ .div_frac_reg = CLK_GP0_DIV_FRAC,
+ .sel_reg = CLK_GP0_SEL,
+ .div_int_max = DIV_INT_16BIT_MAX,
+ .std_only = 0
+ },
+ {
+ .clkdef = {
+ .name = "clk_gp1",
+ .id = RP1_CLK_GP1,
+ .parent_names = (const char *[]){"xosc"},
+ .parent_cnt = 1,
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .ctrl_reg = CLK_GP1_CTRL,
+ .div_int_reg = CLK_GP1_DIV_INT,
+ .div_frac_reg = CLK_GP1_DIV_FRAC,
+ .sel_reg = CLK_GP1_SEL,
+ .div_int_max = DIV_INT_16BIT_MAX,
+ .std_only = 0
+ },
+ {
+ .clkdef = {
+ .name = "clk_gp2",
+ .id = RP1_CLK_GP2,
+ .parent_names = (const char *[]){"xosc"},
+ .parent_cnt = 1,
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .ctrl_reg = CLK_GP2_CTRL,
+ .div_int_reg = CLK_GP2_DIV_INT,
+ .div_frac_reg = CLK_GP2_DIV_FRAC,
+ .sel_reg = CLK_GP2_SEL,
+ .div_int_max = DIV_INT_16BIT_MAX,
+ .std_only = 0
+ },
+ {
+ .clkdef = {
+ .name = "clk_gp3",
+ .id = RP1_CLK_GP3,
+ .parent_names = (const char *[]){"xosc"},
+ .parent_cnt = 1,
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .ctrl_reg = CLK_GP3_CTRL,
+ .div_int_reg = CLK_GP3_DIV_INT,
+ .div_frac_reg = CLK_GP3_DIV_FRAC,
+ .sel_reg = CLK_GP3_SEL,
+ .div_int_max = DIV_INT_16BIT_MAX,
+ .std_only = 0
+ },
+ {
+ .clkdef = {
+ .name = "clk_gp4",
+ .id = RP1_CLK_GP4,
+ .parent_names = (const char *[]){"xosc"},
+ .parent_cnt = 1,
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .ctrl_reg = CLK_GP4_CTRL,
+ .div_int_reg = CLK_GP4_DIV_INT,
+ .div_frac_reg = CLK_GP4_DIV_FRAC,
+ .sel_reg = CLK_GP4_SEL,
+ .div_int_max = DIV_INT_16BIT_MAX,
+ .std_only = 0
+ },
+ {
+ .clkdef = {
+ .name = "clk_gp5",
+ .id = RP1_CLK_GP5,
+ .parent_names = (const char *[]){"xosc"},
+ .parent_cnt = 1,
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .ctrl_reg = CLK_GP5_CTRL,
+ .div_int_reg = CLK_GP5_DIV_INT,
+ .div_frac_reg = CLK_GP5_DIV_FRAC,
+ .sel_reg = CLK_GP5_SEL,
+ .div_int_max = DIV_INT_16BIT_MAX,
+ .std_only = 0
+ },
+ {
+ .clkdef = {
+ .name = "clk_vec",
+ .id = RP1_CLK_VEC,
+ .parent_names = (const char *[]){
+ "pll_sys_pri_ph",
+ "pll_video_sec",
+ "pll_video",
+ "clk_gp0",
+ "clk_gp1",
+ "clk_gp2",
+ "clk_gp3",
+ "clk_gp4"
+ },
+ .parent_cnt = 8,
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .ctrl_reg = VIDEO_CLK_VEC_CTRL,
+ .div_int_reg = VIDEO_CLK_VEC_DIV_INT,
+ .div_frac_reg = 0,
+ .sel_reg = VIDEO_CLK_VEC_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .std_only = 0
+ },
+ {
+ .clkdef = {
+ .name = "clk_dpi",
+ .id = RP1_CLK_DPI,
+ .parent_names = (const char *[]){
+ "pll_sys",
+ "pll_video_sec",
+ "pll_video",
+ "clk_gp0",
+ "clk_gp1",
+ "clk_gp2",
+ "clk_gp3",
+ "clk_gp4"
+ },
+ .parent_cnt = 8,
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .ctrl_reg = VIDEO_CLK_DPI_CTRL,
+ .div_int_reg = VIDEO_CLK_DPI_DIV_INT,
+ .div_frac_reg = 0,
+ .sel_reg = VIDEO_CLK_DPI_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .std_only = 0
+ },
+ {
+ .clkdef = {
+ .name = "clk_mipi0_dpi",
+ .id = RP1_CLK_MIPI0_DPI,
+ .parent_names = (const char *[]){
+ "pll_sys",
+ "pll_video_sec",
+ "pll_video",
+ "clksrc_mipi0_dsi_byteclk",
+ "clk_gp0",
+ "clk_gp1",
+ "clk_gp2",
+ "clk_gp3"
+ },
+ .parent_cnt = 8,
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .ctrl_reg = VIDEO_CLK_MIPI0_DPI_CTRL,
+ .div_int_reg = VIDEO_CLK_MIPI0_DPI_DIV_INT,
+ .div_frac_reg = VIDEO_CLK_MIPI0_DPI_DIV_FRAC,
+ .sel_reg = VIDEO_CLK_MIPI0_DPI_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .std_only = 0
+ },
+ {
+ .clkdef = {
+ .name = "clk_mipi1_dpi",
+ .id = RP1_CLK_MIPI1_DPI,
+ .parent_names = (const char *[]){
+ "pll_sys",
+ "pll_video_sec",
+ "pll_video",
+ "clksrc_mipi1_dsi_byteclk",
+ "clk_gp0",
+ "clk_gp1",
+ "clk_gp2",
+ "clk_gp3"
+ },
+ .parent_cnt = 8,
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .ctrl_reg = VIDEO_CLK_MIPI1_DPI_CTRL,
+ .div_int_reg = VIDEO_CLK_MIPI1_DPI_DIV_INT,
+ .div_frac_reg = VIDEO_CLK_MIPI1_DPI_DIV_FRAC,
+ .sel_reg = VIDEO_CLK_MIPI1_DPI_SEL,
+ .div_int_max = DIV_INT_8BIT_MAX,
+ .std_only = 0
+ },
+};
+
+static int
+rpiclock_periph_init(struct clknode *clk, device_t dev)
+{
+ struct rpi_periph_sc *sc;
+ uint32_t idx;
+ uint32_t sel, ctrl;
+
+ sc = clknode_get_softc(clk);
+
+ /* XXX: Should not assume sel_reg is readable; check ctrl */
+ if (sc->std_only) {
+ RD4(sc, sc->sel_reg, &sel);
+ idx = ffs(sel) - 1;
+ } else {
+ RD4(sc, sc->ctrl_reg, &ctrl);
+ idx = (ctrl & CLK_CTRL_AUXSRC_MASK) >> CLK_CTRL_AUXSRC_SHIFT;
+ }
+
+ if (idx >= clknode_get_parents_num(clk))
+ return (EINVAL);
+
+ clknode_init_parent_idx(clk, idx);
+ return (0);
+}
+
+static int
+rpiclock_periph_recalc(struct clknode *clk, uint64_t *freq)
+{
+ struct clknode *parent_clk;
+ struct rpi_periph_sc *sc;
+ uint64_t parent_freq;
+ uint32_t div_int, div_frac;
+ uint64_t div;
+ int error;
+
+ sc = clknode_get_softc(clk);
+
+ parent_clk = clknode_get_parent(clk);
+
+ error = clknode_get_freq(parent_clk, &parent_freq);
+ if (error != 0)
+ return error;
+
+ RD4(sc, sc->div_int_reg, &div_int);
+ if (sc->div_frac_reg) {
+ RD4(sc, sc->div_frac_reg, &div_frac);
+ } else {
+ div_frac = 0;
+ }
+
+ div =
+ ((uint64_t)div_int << CLK_DIV_FRAC_BITS) |
+ (div_frac >> (32 - CLK_DIV_FRAC_BITS));
+
+ *freq = DIV_ROUND((uint64_t)parent_freq << CLK_DIV_FRAC_BITS, div) >>
+ CLK_DIV_FRAC_BITS;
+
+ return (0);
+}
+
+static void
+calculate_freq(
+ struct rpi_periph_sc *sc, uint64_t parent, uint64_t target,
+ uint64_t *freq, uint64_t *div)
+{
+ *div = DIV_ROUND(parent << CLK_DIV_FRAC_BITS, target);
+
+ /* Round up */
+ *div += (1 << (CLK_DIV_FRAC_BITS - 1));
+
+ /* Clip div_int.div_frac to between 1 and div_int_max */
+ *div = MIN((uint64_t)sc->div_int_max << CLK_DIV_FRAC_BITS, *div);
+ *div = MAX((uint64_t)1 << CLK_DIV_FRAC_BITS, *div);
+
+ *freq = DIV_ROUND(parent, *div);
+}
+
+static int
+rpiclock_periph_set_freq(
+ struct clknode *clk, uint64_t fin, uint64_t *fout, int flags, int *stop)
+{
+ struct rpi_periph_sc *sc;
+
+ uint64_t freq, best_freq;
+ uint64_t div, best_div;
+ uint8_t best_mux;
+
+ uint32_t div_int, div_frac;
+
+ uint32_t ctrl, sel;
+
+ int parent_cnt;
+ const char **parent_names;
+ struct clknode *parent_clk;
+ uint64_t parent_freq;
+
+ int i;
+
+ sc = clknode_get_softc(clk);
+
+ /*
+ * Start out with the current parent. This prevents
+ * unnecessary switching to a different parent.
+ */
+ if (sc->std_only) {
+ RD4(sc, sc->sel_reg, &sel);
+ best_mux = ffs(sel) - 1;
+ } else {
+ RD4(sc, sc->ctrl_reg, &ctrl);
+ best_mux = (ctrl & CLK_CTRL_AUXSRC_MASK) >>
+ CLK_CTRL_AUXSRC_SHIFT;
+ }
+
+ best_freq = *fout;
+
+ /*
+ * Find the parent that allows configuration of a frequency
+ * closest to the target frequency.
+ */
+ parent_cnt = clknode_get_parents_num(clk);
+ parent_names = clknode_get_parent_names(clk);
+ for (i = 0; i < parent_cnt; i++) {
+ if(parent_names[i] == NULL)
+ continue;
+
+ parent_clk = clknode_find_by_name(parent_names[i]);
+ clknode_get_freq(parent_clk, &parent_freq);
+
+ calculate_freq(sc, parent_freq, fin, &freq, &div);
+
+ if ((fin <= freq && freq < best_freq) ||
+ (best_freq < freq && freq <= fin)) {
+ best_div = div;
+ best_freq = freq;
+ best_mux = i;
+ }
+
+ if (freq == fin)
+ break;
+ }
+
+ if (!(flags & CLK_SET_DRYRUN)) {
+ RD4(sc, sc->ctrl_reg, &ctrl);
+ if (sc->std_only) {
+ ctrl &= ~sc->clk_src_mask;
+ ctrl |= (best_mux << CLK_CTRL_SRC_SHIFT);
+ } else {
+ ctrl &= ~CLK_CTRL_AUXSRC_MASK;
+ ctrl |= (best_mux << CLK_CTRL_AUXSRC_SHIFT);
+ ctrl &= ~sc->clk_src_mask;
+ ctrl |= AUX_SEL; // CLK_CTRL_SRC_AUX
+ }
+ WR4(sc, sc->ctrl_reg, ctrl);
+
+ div_int = best_div >> CLK_DIV_FRAC_BITS;
+ div_frac = (best_div & ((1ULL << CLK_DIV_FRAC_BITS) - 1)) <<
+ (32 - CLK_DIV_FRAC_BITS);
+
+ WR4(sc, sc->div_int_reg, div_int);
+ WR4(sc, sc->div_frac_reg, div_frac);
+ }
+
+ *fout = best_freq;
+ *stop = 1;
+
+ return 0;
+}
+
+
+static int
+rpiclock_periph_get_gate(struct clknode *clk, bool *enabled)
+{
+ struct rpi_periph_sc *sc;
+ uint32_t reg;
+
+ sc = clknode_get_softc(clk);
+ RD4(sc, sc->ctrl_reg, &reg);
+
+ *enabled = !!(reg & CLK_CTRL_ENABLE);
+
+ return (0);
+}
+
+static int
+rpiclock_periph_set_gate(struct clknode *clk, bool enable)
+{
+ struct rpi_periph_sc *sc;
+ uint32_t reg;
+
+ sc = clknode_get_softc(clk);
+ RD4(sc, sc->ctrl_reg, &reg);
+
+ if (enable) {
+ reg |= CLK_CTRL_ENABLE;
+ } else {
+ reg &= ~CLK_CTRL_ENABLE;
+ }
+
+ WR4(sc, sc->ctrl_reg, reg);
+
+ return (0);
+}
+
+static clknode_method_t rpiclock_periph_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, rpiclock_periph_init),
+ CLKNODEMETHOD(clknode_recalc_freq, rpiclock_periph_recalc),
+ CLKNODEMETHOD(clknode_set_freq, rpiclock_periph_set_freq),
+ CLKNODEMETHOD(clknode_get_gate, rpiclock_periph_get_gate),
+ CLKNODEMETHOD(clknode_set_gate, rpiclock_periph_set_gate),
+ CLKNODEMETHOD_END
+};
+DEFINE_CLASS_1(rpiclock_periph, rpiclock_periph_class,
+ rpiclock_periph_methods, sizeof(struct rpi_periph_sc), clknode_class);
+
+static int
+rpiclock_init_periph(struct clkdom *clkdom, struct clknode_periph_def *def)
+{
+ struct clknode *clk;
+ struct rpi_periph_sc *sc;
+
+ clk = clknode_create(clkdom, &rpiclock_periph_class,
+ (struct clknode_init_def *)def);
+ if (clk == NULL) {
+ return (ENXIO);
+ }
+
+ sc = clknode_get_softc(clk);
+ sc->clkdev = clknode_get_device(clk);
+
+ sc->ctrl_reg = def->ctrl_reg;
+ sc->div_int_reg = def->div_int_reg;
+ sc->sel_reg = def->sel_reg;
+ sc->div_int_max = def->div_int_max;
+ sc->clk_src_mask = def->clk_src_mask;
+
+ sc->std_only = def->std_only;
+
+ clknode_register(clkdom, clk);
+
+ return (0);
+}
+
+
+void
+rpiclock_init_periphs(struct rpiclock_softc *sc)
+{
+ struct clkdom *clkdom;
+ int error;
+ int i;
+
+ clkdom = sc->clkdom;
+
+ for (i = 0; i < nitems(rpiclock_periph_defs); i++) {
+ error = rpiclock_init_periph(clkdom, &rpiclock_periph_defs[i]);
+ if (error != 0)
+ panic("periph '%s' initialization failed",
+ rpiclock_periph_defs[i].clkdef.name);
+ }
+}
diff --git a/sys/arm64/rpi/rpiclock_pll.c b/sys/arm64/rpi/rpiclock_pll.c
new file mode 100644
--- /dev/null
+++ b/sys/arm64/rpi/rpiclock_pll.c
@@ -0,0 +1,770 @@
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/endian.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofw_pci.h>
+
+#include <dev/fdt/fdt_intr.h>
+#include <dev/fdt/simplebus.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/resource.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+
+#include "pic_if.h"
+#include "rpiclock.h"
+
+/*
+ * Enumeration of the 4 different PLL types: core, primary, secondary, and
+ * phase-shifted primary.
+ */
+enum rpiclock_pll_type {
+ CORE,
+ PRIM,
+ SEC,
+ PH
+};
+
+/*
+ * Registers for core PLL data
+ */
+struct rpi_pll_core_sc {
+ uint32_t cs_reg;
+ uint32_t pwr_reg;
+ uint32_t fbdiv_int_reg;
+ uint32_t fbdiv_frac_reg;
+};
+
+/*
+ * Registers for primary or secondary PLL data
+ */
+struct rpi_pll_data_sc {
+ uint32_t ctrl_reg;
+};
+
+/*
+ * Registers for phase-shifted primary PLL data
+ */
+struct rpi_pll_ph_sc {
+ uint32_t ctrl_reg;
+ uint32_t fixed_divider;
+ uint32_t phase;
+};
+
+/*
+ * Software context for arbitrary PLL type
+ */
+struct rpi_pll_sc {
+ device_t clkdev;
+ enum rpiclock_pll_type type;
+
+ union {
+ struct rpi_pll_core_sc core;
+ struct rpi_pll_data_sc pll;
+ struct rpi_pll_ph_sc pll_ph;
+ };
+};
+
+static const char *pll_sys_core_parents[] = {"xosc"};
+static const char *pll_audio_core_parents[] = {"xosc"};
+static const char *pll_video_core_parents[] = {"xosc"};
+static const char *pll_sys_parents[] = {"pll_sys_core"};
+static const char *pll_audio_parents[] = {"pll_audio_core"};
+static const char *pll_video_parents[] = {"pll_video_core"};
+static const char *pll_sys_pri_ph_parents[] = {"pll_sys"};
+static const char *pll_audio_pri_ph_parents[] = {"pll_audio"};
+static const char *pll_sys_sec_parents[] = {"pll_sys_core"};
+static const char *pll_audio_sec_parents[] = {"pll_audio_core"};
+static const char *pll_video_sec_parents[] = {"pll_video_core"};
+/*
+ * Table of data for each PLL expected to be on the RP1
+ */
+struct clknode_pll_def {
+ struct clknode_init_def clkdef;
+
+ enum rpiclock_pll_type type;
+
+ uint32_t cs_reg;
+ uint32_t pwr_reg;
+ uint32_t fbdiv_int_reg;
+ uint32_t fbdiv_frac_reg;
+
+ uint32_t ctrl_reg;
+ uint32_t fixed_divider;
+ uint32_t phase;
+} rpiclock_pll_defs[] = {
+ {
+ .clkdef = {
+ .id = RP1_PLL_SYS_CORE,
+ .name = "pll_sys_core",
+ .parent_names = pll_sys_core_parents,
+ .parent_cnt = nitems(pll_sys_core_parents),
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .type = CORE,
+ .cs_reg = PLL_SYS_CS,
+ .pwr_reg = PLL_SYS_PWR,
+ .fbdiv_int_reg = PLL_SYS_FBDIV_INT,
+ .fbdiv_frac_reg = PLL_SYS_FBDIV_FRAC,
+ },
+ {
+ .clkdef = {
+ .id = RP1_PLL_AUDIO_CORE,
+ .name = "pll_audio_core",
+ .parent_names = pll_audio_core_parents,
+ .parent_cnt = nitems(pll_audio_core_parents),
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .type = CORE,
+ .cs_reg = PLL_AUDIO_CS,
+ .pwr_reg = PLL_AUDIO_PWR,
+ .fbdiv_int_reg = PLL_AUDIO_FBDIV_INT,
+ .fbdiv_frac_reg = PLL_AUDIO_FBDIV_FRAC,
+ },
+ {
+ .clkdef = {
+ .id = RP1_PLL_VIDEO_CORE,
+ .name = "pll_video_core",
+ .parent_names = pll_video_core_parents,
+ .parent_cnt = nitems(pll_video_core_parents),
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .type = CORE,
+ .cs_reg = PLL_VIDEO_CS,
+ .pwr_reg = PLL_VIDEO_PWR,
+ .fbdiv_int_reg = PLL_VIDEO_FBDIV_INT,
+ .fbdiv_frac_reg = PLL_VIDEO_FBDIV_FRAC,
+ },
+ {
+ .clkdef = {
+ .id = RP1_PLL_SYS,
+ .name = "pll_sys",
+ .parent_names = pll_sys_parents,
+ .parent_cnt = nitems(pll_sys_parents),
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .type = PRIM,
+ .ctrl_reg = PLL_SYS_PRIM,
+ },
+ {
+ .clkdef = {
+ .id = RP1_PLL_AUDIO,
+ .name = "pll_audio",
+ .parent_names = pll_audio_parents,
+ .parent_cnt = nitems(pll_audio_parents),
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .type = PRIM,
+ .ctrl_reg = PLL_AUDIO_PRIM,
+ },
+ {
+ .clkdef = {
+ .id = RP1_PLL_VIDEO,
+ .name = "pll_video",
+ .parent_names = pll_video_parents,
+ .parent_cnt = nitems(pll_video_parents),
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .type = PRIM,
+ .ctrl_reg = PLL_VIDEO_PRIM,
+ },
+ {
+ .clkdef = {
+ .id = RP1_PLL_SYS_PRI_PH,
+ .name = "pll_sys_pri_ph",
+ .parent_names = pll_sys_pri_ph_parents,
+ .parent_cnt = nitems(pll_sys_pri_ph_parents),
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .type = PH,
+ .ctrl_reg = PLL_SYS_PRIM,
+ .fixed_divider = 2,
+ .phase = RP1_PLL_PHASE_0,
+ },
+ {
+ .clkdef = {
+ .id = RP1_PLL_AUDIO_PRI_PH,
+ .name = "pll_audio_pri_ph",
+ .parent_names = pll_audio_pri_ph_parents,
+ .parent_cnt = nitems(pll_audio_pri_ph_parents),
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .type = PH,
+ .ctrl_reg = PLL_AUDIO_PRIM,
+ .fixed_divider = 2,
+ .phase = RP1_PLL_PHASE_0,
+ },
+ {
+ .clkdef = {
+ .id = RP1_PLL_SYS_SEC,
+ .name = "pll_sys_sec",
+ .parent_names = pll_sys_sec_parents,
+ .parent_cnt = nitems(pll_sys_parents),
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .type = SEC,
+ .ctrl_reg = PLL_SYS_SEC,
+ },
+ {
+ .clkdef = {
+ .id = RP1_PLL_AUDIO_SEC,
+ .name = "pll_audio_sec",
+ .parent_names = pll_audio_sec_parents,
+ .parent_cnt = nitems(pll_audio_sec_parents),
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .type = SEC,
+ .ctrl_reg = PLL_AUDIO_SEC,
+ },
+ {
+ .clkdef = {
+ .id = RP1_PLL_VIDEO_SEC,
+ .name = "pll_video_sec",
+ .parent_names = pll_video_sec_parents,
+ .parent_cnt = nitems(pll_video_sec_parents),
+ .flags = CLK_NODE_STATIC_STRINGS
+ },
+ .type = SEC,
+ .ctrl_reg = PLL_VIDEO_SEC,
+ }
+};
+
+static int
+rpiclock_pll_init(struct clknode *clk, device_t dev)
+{
+ /* PLLs all have only one parent */
+ clknode_init_parent_idx(clk, 0);
+ return (0);
+}
+
+static int
+rpiclock_pll_core_recalc(
+ struct clknode *clk, uint32_t parent_freq, uint64_t *freq)
+{
+ struct rpi_pll_sc *sc;
+ uint32_t fbdiv_int, fbdiv_frac;
+ uint64_t multiplier;
+
+ sc = clknode_get_softc(clk);
+
+ RD4(sc, sc->core.fbdiv_int_reg, &fbdiv_int);
+ RD4(sc, sc->core.fbdiv_frac_reg, &fbdiv_frac);
+
+ /*
+ * Scale the core frequency by sc->sc_xosc_freq, rounding up the
+ * nearest integer
+ */
+ multiplier = (((uint64_t)fbdiv_int << 24) + fbdiv_frac) + (1 << 23);
+ *freq = ((uint64_t)parent_freq * multiplier) >> 24;
+
+ return (0);
+}
+
+static int
+rpiclock_pll_prim_recalc(
+ struct clknode *clk, uint32_t parent_freq, uint64_t *freq)
+{
+ struct rpi_pll_sc *sc;
+ uint32_t reg, div1, div2;
+
+ sc = clknode_get_softc(clk);
+
+ RD4(sc, sc->pll.ctrl_reg, &reg);
+
+ /* Extract the 2 3-bit dividers */
+ div1 = (reg & PLL_PRIM_DIV1_MASK) >> PLL_PRIM_DIV1_SHIFT;
+ div2 = (reg & PLL_PRIM_DIV2_MASK) >> PLL_PRIM_DIV2_SHIFT;
+ if (div1 == 0 || div2 == 0)
+ return (EINVAL);
+
+ *freq = DIV_ROUND((uint64_t)parent_freq, div1 * div2);
+
+ return (0);
+}
+
+static int
+rpiclock_pll_sec_recalc(
+ struct clknode *clk, uint32_t parent_freq, uint64_t *freq)
+{
+ struct rpi_pll_sc *sc;
+ uint32_t sec, div;
+
+ sc = clknode_get_softc(clk);
+
+ RD4(sc, sc->pll.ctrl_reg, &sec);
+ /* sec clocks per core clock */
+ div = (sec & PLL_SEC_DIV_MASK) >> PLL_SEC_DIV_SHIFT;
+ if (div == 0)
+ return (EINVAL);
+
+ *freq = parent_freq / div;
+
+ return (0);
+}
+
+static int
+rpiclock_pll_ph_recalc(
+ struct clknode *clk, uint32_t parent_freq, uint64_t *freq)
+{
+ struct rpi_pll_sc *sc;
+
+ sc = clknode_get_softc(clk);
+
+ *freq = DIV_ROUND((uint64_t)parent_freq, sc->pll_ph.fixed_divider);
+
+ return (0);
+}
+
+/*
+ * Calculate and return the frequency of a given PLL
+ */
+static int
+rpiclock_pll_recalc(struct clknode *clk, uint64_t *freq)
+{
+ struct rpi_pll_sc *sc;
+ struct clknode *parent_clk;
+ uint64_t parent_freq;
+ int error;
+
+ sc = clknode_get_softc(clk);
+ parent_clk = clknode_get_parent(clk);
+
+ error = clknode_get_freq(parent_clk, &parent_freq);
+ if (error)
+ return error;
+
+ switch (sc->type) {
+ case CORE:
+ return rpiclock_pll_core_recalc(clk, parent_freq, freq);
+ case PRIM:
+ return rpiclock_pll_prim_recalc(clk, parent_freq, freq);
+ case SEC:
+ return rpiclock_pll_sec_recalc(clk, parent_freq, freq);
+ case PH:
+ return rpiclock_pll_ph_recalc(clk, parent_freq, freq);
+ default:
+ return (EINVAL);
+ }
+}
+
+static int
+rpiclock_pll_core_set_freq(
+ struct clknode *clk, uint64_t fin, uint64_t *fout, int flags, int *stop)
+{
+ struct rpi_pll_sc *sc;
+ uint32_t fbdiv_int, fbdiv_frac;
+ uint64_t div;
+
+ uint32_t reg;
+
+ sc = clknode_get_softc(clk);
+
+ if (*fout > (fin / 16))
+ return -1;
+
+ div = DIV_ROUND(fin << 32, *fout);
+
+ div += (1 << (32 - 24 - 1));
+
+ /* Get the closest 32.24 fixed point factor to approximate target */
+ fbdiv_int = div >> 32;
+ fbdiv_frac = (div >> (32 - 24)) & 0xffffff;
+
+ if (!(flags & CLK_SET_DRYRUN)) {
+ WR4(sc, sc->core.fbdiv_int_reg, fbdiv_int);
+ WR4(sc, sc->core.fbdiv_frac_reg, fbdiv_frac);
+
+ /* Disable power saving iff there exists a fractional component */
+ RD4(sc, sc->core.pwr_reg, &reg);
+ if (fbdiv_frac != 0) {
+ reg &= ~PLL_PWR_DSMPD;
+ } else {
+ reg |= PLL_PWR_DSMPD;
+ }
+ WR4(sc, sc->core.pwr_reg, reg);
+
+ /* Indicate desire to write a new divider value */
+ RD4(sc, sc->core.cs_reg, &reg);
+ reg |= 1 << PLL_CS_REFDIV_SHIFT;
+ WR4(sc, sc->core.cs_reg, reg);
+ }
+
+ *fout *= div;
+ *fout >>= 32;
+
+ *stop = 1;
+
+ return (0);
+}
+
+static int
+rpiclock_pll_prim_set_freq(
+ struct clknode *clk, uint64_t fin, uint64_t *fout, int flags, int *stop)
+{
+ struct rpi_pll_sc *sc;
+
+ uint32_t best_div1, best_div2, best_freq;
+ uint32_t div1, div2, freq;
+
+ uint32_t prim;
+
+ sc = clknode_get_softc(clk);
+
+ /* Both dividers = 1 is a guaranteed option */
+ best_freq = *fout;
+ best_div1 = 1;
+ best_div2 = 1;
+
+ /*
+ * Brute force every combination of cascading dividers to get as close
+ * as possible
+ */
+ for (div1 = 1; div1 <= 7; div1++) {
+ for (div2 = 1; div2 <= div1; div2++) {
+ freq = DIV_ROUND(*fout, div1 * div2);
+
+ if (freq == fin) {
+ best_div1 = div1;
+ best_div2 = div2;
+ best_freq = freq;
+ goto found;
+ }
+
+ /*
+ * Quality is determined by absulute numerical
+ * distance to target_freq.
+ * i.e.
+ * abs(best_freq - target_freq)
+ * vs.
+ * abs(freq - target_freq)
+ *
+ */
+ if ((best_freq > freq && best_freq > fin) ||
+ (best_freq < freq && freq < fin)) {
+ best_div1 = div1;
+ best_div2 = div2;
+ best_freq = freq;
+ }
+ }
+ }
+
+found:
+ /* Write out optimal divider combination */
+ if (!(flags & CLK_SET_DRYRUN)) {
+ RD4(sc, sc->pll.ctrl_reg, &prim);
+ prim &= ~(PLL_PRIM_DIV1_MASK | PLL_PRIM_DIV2_MASK);
+ prim |= (best_div1 << PLL_PRIM_DIV1_SHIFT);
+ prim |= (best_div2 << PLL_PRIM_DIV2_SHIFT);
+ WR4(sc, sc->pll.ctrl_reg, prim);
+ }
+
+ *fout = best_freq;
+ *stop = 1;
+
+ return (0);
+}
+
+/*
+ * Find the best divider (between 8 and 19) to achieve fin, given *fout
+ * initially
+ */
+static int
+rpiclock_pll_sec_set_freq(
+ struct clknode *clk, uint64_t fin, uint64_t *fout, int flags, int *stop)
+{
+ struct rpi_pll_sc *sc;
+ uint32_t sec, div;
+
+ sc = clknode_get_softc(clk);
+
+ /* Clip div between 8 and 19 */
+ div = MIN(19, MAX(8, DIV_ROUND(*fout, fin)));
+
+ RD4(sc, sc->pll.ctrl_reg, &sec);
+ sec &= ~PLL_SEC_DIV_MASK;
+ sec |= (div << PLL_SEC_DIV_SHIFT);
+
+ if (!(flags & CLK_SET_DRYRUN)) {
+ DEVICE_LOCK(sc);
+ WR4(sc, sc->pll.ctrl_reg, sec | PLL_SEC_RST);
+ DELAY((10 * div * 1000000) / *fout);
+ WR4(sc, sc->pll.ctrl_reg, sec);
+ DEVICE_UNLOCK(sc);
+ }
+
+ *fout = *fout / div;
+ *stop = 1;
+
+ return (0);
+}
+
+static int
+rpiclock_pll_ph_set_freq(
+ struct clknode *clk, uint64_t fin, uint64_t *fout, int flags, int *stop)
+{
+ /*
+ * Since it's a phase shift of the PRIM PLL, setting frequency here
+ * doesn't make sense. No-op. Instead, change prim.
+ */
+ *stop = 1;
+ return (0);
+}
+
+/*
+ * Set the frequency of a PLL
+ */
+static int
+rpiclock_pll_set_freq(
+ struct clknode *clk, uint64_t fin, uint64_t *fout, int flags, int *stop)
+{
+ struct rpi_pll_sc *sc;
+
+ sc = clknode_get_softc(clk);
+
+ switch (sc->type) {
+ case CORE:
+ return rpiclock_pll_core_set_freq(clk, fin, fout, flags, stop);
+ case PRIM:
+ return rpiclock_pll_prim_set_freq(clk, fin, fout, flags, stop);
+ case SEC:
+ return rpiclock_pll_sec_set_freq(clk, fin, fout, flags, stop);
+ case PH:
+ return rpiclock_pll_ph_set_freq(clk, fin, fout, flags, stop);
+ default:
+ *stop = 0;
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+static int
+rpiclock_pll_core_get_gate(struct rpi_pll_sc *sc, bool *enabled)
+{
+ uint32_t reg;
+
+ RD4(sc, sc->core.pwr_reg, &reg);
+
+ *enabled = (reg & PLL_PWR_PD) || (reg & PLL_PWR_POSTDIVPD);
+
+ return (0);
+}
+
+static int
+rpiclock_pll_primsec_get_gate(struct rpi_pll_sc *sc, bool *enabled)
+{
+ uint32_t reg;
+
+ RD4(sc, sc->pll.ctrl_reg, &reg);
+
+ *enabled = !(reg & PLL_SEC_RST);
+
+ return (0);
+}
+
+static int
+rpiclock_pll_ph_get_gate(struct rpi_pll_sc *sc, bool *enabled)
+{
+ uint32_t reg;
+
+ RD4(sc, sc->pll_ph.ctrl_reg, &reg);
+
+ *enabled = !!(reg & PLL_PH_EN);
+
+ return (0);
+}
+
+/*
+ * Determine if a given PLL is currently enabled
+ */
+static int
+rpiclock_pll_get_gate(struct clknode *clk, bool *enabled)
+{
+ struct rpi_pll_sc *sc;
+
+ sc = clknode_get_softc(clk);
+
+ switch (sc->type) {
+ case CORE:
+ return rpiclock_pll_core_get_gate(sc, enabled);
+ case PRIM:
+ case SEC:
+ return rpiclock_pll_primsec_get_gate(sc, enabled);
+ case PH:
+ return rpiclock_pll_ph_get_gate(sc, enabled);
+ default:
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+static int
+rpiclock_pll_core_set_gate(struct rpi_pll_sc *sc, bool enable)
+{
+ uint32_t reg;
+
+ RD4(sc, sc->pll.ctrl_reg, &reg);
+
+ if (enable) {
+ reg |= CLK_CTRL_ENABLE;
+ } else {
+ reg &= ~CLK_CTRL_ENABLE;
+ }
+
+ WR4(sc, sc->pll.ctrl_reg, reg);
+
+ return (0);
+}
+
+static int
+rpiclock_pll_primsec_set_gate(struct rpi_pll_sc *sc, bool enable)
+{
+ uint32_t reg;
+
+ RD4(sc, sc->pll.ctrl_reg, &reg);
+
+ if (enable) {
+ reg &= ~PLL_SEC_RST;
+ } else {
+ reg |= PLL_SEC_RST;
+ }
+
+ WR4(sc, sc->pll.ctrl_reg, reg);
+
+ return (0);
+}
+
+static int
+rpiclock_pll_ph_set_gate(struct rpi_pll_sc *sc, bool enable)
+{
+ uint32_t reg;
+
+ RD4(sc, sc->pll.ctrl_reg, &reg);
+
+ if (enable) {
+ reg |= PLL_PH_EN;
+ } else {
+ reg &= ~PLL_PH_EN;
+ }
+
+ WR4(sc, sc->pll.ctrl_reg, reg);
+
+ return (0);
+}
+
+static int
+rpiclock_pll_set_gate(struct clknode *clk, bool enable)
+{
+ struct rpi_pll_sc *sc;
+
+ sc = clknode_get_softc(clk);
+
+ switch (sc->type) {
+ case CORE:
+ return rpiclock_pll_core_set_gate(sc, enable);
+ case PRIM:
+ case SEC:
+ return rpiclock_pll_primsec_set_gate(sc, enable);
+ case PH:
+ return rpiclock_pll_ph_set_gate(sc, enable);
+ default:
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+static clknode_method_t rpiclock_pll_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, rpiclock_pll_init),
+ CLKNODEMETHOD(clknode_recalc_freq, rpiclock_pll_recalc),
+ CLKNODEMETHOD(clknode_set_freq, rpiclock_pll_set_freq),
+ CLKNODEMETHOD(clknode_get_gate, rpiclock_pll_get_gate),
+ CLKNODEMETHOD(clknode_set_gate, rpiclock_pll_set_gate),
+ CLKNODEMETHOD_END
+};
+DEFINE_CLASS_1(rpiclock_pll, rpiclock_pll_class,
+ rpiclock_pll_methods, sizeof(struct rpi_pll_sc), clknode_class);
+
+/*
+ * From a definition, create a new clock node in the given clock domain, then
+ * initialize its software context.
+ */
+static int
+rpiclock_init_pll(struct clkdom *clkdom, struct clknode_pll_def *def)
+{
+ struct clknode *clk;
+ struct rpi_pll_sc *sc;
+
+ clk = clknode_create(clkdom, &rpiclock_pll_class,
+ (const struct clknode_init_def *)def);
+ if (clk == NULL) {
+ return (ENXIO);
+ }
+
+ sc = clknode_get_softc(clk);
+
+ sc->clkdev = clknode_get_device(clk);
+ sc->type = def->type;
+
+ switch (def->type) {
+ case CORE:
+ sc->core.cs_reg = def->cs_reg;
+ sc->core.pwr_reg = def->pwr_reg;
+ sc->core.fbdiv_int_reg = def->fbdiv_int_reg;
+ sc->core.fbdiv_frac_reg = def->fbdiv_frac_reg;
+ break;
+ case PRIM:
+ case SEC:
+ sc->pll.ctrl_reg = def->ctrl_reg;
+ break;
+ case PH:
+ sc->pll_ph.ctrl_reg = def->ctrl_reg;
+ sc->pll_ph.fixed_divider = def->fixed_divider;
+ sc->pll_ph.phase = def->phase;
+ break;
+ default:
+ panic("attempted to initialize unknown PLL type '%d'",
+ def->type);
+ }
+
+ clknode_register(clkdom, clk);
+
+ return (0);
+}
+
+/*
+ * Given a clock domain, iterate over the PLLs in rp1's space and add them
+ * to the domain.
+ */
+void
+rpiclock_init_plls(struct rpiclock_softc *sc)
+{
+ struct clkdom *clkdom;
+ int error;
+ int i;
+
+ clkdom = sc->clkdom;
+
+ for (i = 0; i < nitems(rpiclock_pll_defs); i++) {
+ error = rpiclock_init_pll(clkdom, &rpiclock_pll_defs[i]);
+ if (error != 0)
+ panic("pll '%s' initialization failed",
+ rpiclock_pll_defs[i].clkdef.name);
+ }
+}
diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64
--- a/sys/conf/files.arm64
+++ b/sys/conf/files.arm64
@@ -852,3 +852,8 @@
dev/clk/xilinx/zynqmp_clk_mux.c optional fdt soc_xilinx_zynq
dev/clk/xilinx/zynqmp_clk_pll.c optional fdt soc_xilinx_zynq
dev/clk/xilinx/zynqmp_reset.c optional fdt soc_xilinx_zynq
+
+# Raspberry Pi RP1 clocks
+arm64/rpi/rpiclock.c optional fdt rpone
+arm64/rpi/rpiclock_pll.c optional fdt rpone
+arm64/rpi/rpiclock_periph.c optional fdt rpone

File Metadata

Mime Type
text/plain
Expires
Thu, Aug 27, 8:12 PM (12 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
37234080
Default Alt Text
D59095.id.diff (49 KB)

Event Timeline