Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F168196483
D59095.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
49 KB
Referenced Files
None
Subscribers
None
D59095.id.diff
View Options
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, ®);
+
+ *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, ®);
+
+ 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, ®);
+
+ /* 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, ®);
+ 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 |= 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, ®);
+
+ *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, ®);
+
+ *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, ®);
+
+ *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, ®);
+
+ 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, ®);
+
+ 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, ®);
+
+ 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
Details
Attached
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)
Attached To
Mode
D59095: DRAFT: Port rpiclock(4) from OpenBSD
Attached
Detach File
Event Timeline
Log In to Comment