1*dcb10e3aSBojan Novković /*
2*dcb10e3aSBojan Novković * Copyright (c) 2026 Bojan Novković <bnovkov@FreeBSD.org>
3*dcb10e3aSBojan Novković *
4*dcb10e3aSBojan Novković * SPDX-License-Identifier: BSD-2-Clause
5*dcb10e3aSBojan Novković */
6*dcb10e3aSBojan Novković
7*dcb10e3aSBojan Novković /*
8*dcb10e3aSBojan Novković * Driver for the SpacemiT K1 SoC APMU clock control unit.
9*dcb10e3aSBojan Novković *
10*dcb10e3aSBojan Novković * Written using SpacemiT's K1 Chip Product Documentation,
11*dcb10e3aSBojan Novković * Section 9., "Top System (1/2)" and the accompanying clock
12*dcb10e3aSBojan Novković * tree schematic, available at https://www.spacemit.com/community/document/
13*dcb10e3aSBojan Novković */
14*dcb10e3aSBojan Novković
15*dcb10e3aSBojan Novković #include <sys/param.h>
16*dcb10e3aSBojan Novković #include <sys/systm.h>
17*dcb10e3aSBojan Novković #include <sys/bus.h>
18*dcb10e3aSBojan Novković #include <sys/kernel.h>
19*dcb10e3aSBojan Novković #include <sys/module.h>
20*dcb10e3aSBojan Novković #include <sys/mutex.h>
21*dcb10e3aSBojan Novković #include <sys/resource.h>
22*dcb10e3aSBojan Novković #include <sys/rman.h>
23*dcb10e3aSBojan Novković
24*dcb10e3aSBojan Novković #include <machine/bus.h>
25*dcb10e3aSBojan Novković
26*dcb10e3aSBojan Novković #include <dev/fdt/simplebus.h>
27*dcb10e3aSBojan Novković #include <dev/ofw/ofw_bus.h>
28*dcb10e3aSBojan Novković #include <dev/ofw/ofw_bus_subr.h>
29*dcb10e3aSBojan Novković
30*dcb10e3aSBojan Novković #include <dev/clk/clk.h>
31*dcb10e3aSBojan Novković #include <dev/hwreset/hwreset.h>
32*dcb10e3aSBojan Novković
33*dcb10e3aSBojan Novković #include <dt-bindings/clock/spacemit,k1-syscon.h>
34*dcb10e3aSBojan Novković
35*dcb10e3aSBojan Novković #include "k1_clk.h"
36*dcb10e3aSBojan Novković
37*dcb10e3aSBojan Novković #define K1_SDH0_CLK_RES_CTRL 0x54
38*dcb10e3aSBojan Novković #define K1_SDH1_CLK_RES_CTRL 0x58
39*dcb10e3aSBojan Novković #define K1_SDH2_CLK_RES_CTRL 0xE0
40*dcb10e3aSBojan Novković #define K1_PMUA_ACLK_CTRL 0x388
41*dcb10e3aSBojan Novković
42*dcb10e3aSBojan Novković static const struct k1_clk_def apmu_clks[] = {
43*dcb10e3aSBojan Novković K1_CLK_DIV_MUX(pmua_aclk, CLK_PMUA_ACLK, K1_PMUA_ACLK_CTRL,
44*dcb10e3aSBojan Novković 1, 0, 2, 1, 4,
45*dcb10e3aSBojan Novković "pll1_d10_aud", "pll1_d8"),
46*dcb10e3aSBojan Novković
47*dcb10e3aSBojan Novković K1_CLK_GATE(sdh_axi_aclk, CLK_SDH_AXI, K1_SDH0_CLK_RES_CTRL, 4,
48*dcb10e3aSBojan Novković "pmua_aclk"),
49*dcb10e3aSBojan Novković
50*dcb10e3aSBojan Novković K1_CLK_DIV_MUX_GATE(sdh0_clk, CLK_SDH0, K1_SDH0_CLK_RES_CTRL,
51*dcb10e3aSBojan Novković 3, 8, 3, 5, 4, 11,
52*dcb10e3aSBojan Novković "pll1_d6", "pll1_d4", "pll2_d8", "pll2_d5",
53*dcb10e3aSBojan Novković "pll1_d11", "pll1_d11" , "pll1_d13", "pll1_d23"),
54*dcb10e3aSBojan Novković
55*dcb10e3aSBojan Novković K1_CLK_DIV_MUX_GATE(sdh1_clk, CLK_SDH1, K1_SDH1_CLK_RES_CTRL,
56*dcb10e3aSBojan Novković 3, 8, 3, 5, 4, 11,
57*dcb10e3aSBojan Novković "pll1_d6", "pll1_d4", "pll2_d8", "pll2_d5",
58*dcb10e3aSBojan Novković "pll1_d11", "pll1_d11" , "pll1_d13", "pll1_d23"),
59*dcb10e3aSBojan Novković
60*dcb10e3aSBojan Novković K1_CLK_DIV_MUX_GATE(sdh2_clk, CLK_SDH2, K1_SDH2_CLK_RES_CTRL,
61*dcb10e3aSBojan Novković 3, 8, 3, 5, 4, 11,
62*dcb10e3aSBojan Novković "pll1_d6", "pll1_d4", "pll2_d8", "pll1_d3",
63*dcb10e3aSBojan Novković "pll1_d11", "pll1_d11" , "pll1_d13", "pll1_d23")
64*dcb10e3aSBojan Novković };
65*dcb10e3aSBojan Novković
66*dcb10e3aSBojan Novković static const struct k1_reset_def apmu_resets[] = {
67*dcb10e3aSBojan Novković K1_RESET(RESET_SDH_AXI, K1_SDH0_CLK_RES_CTRL, 0, 3),
68*dcb10e3aSBojan Novković K1_RESET(RESET_SDH0, K1_SDH0_CLK_RES_CTRL, 0, 1),
69*dcb10e3aSBojan Novković K1_RESET(RESET_SDH1, K1_SDH1_CLK_RES_CTRL, 0, 1),
70*dcb10e3aSBojan Novković K1_RESET(RESET_SDH2, K1_SDH2_CLK_RES_CTRL, 0, 1),
71*dcb10e3aSBojan Novković };
72*dcb10e3aSBojan Novković
73*dcb10e3aSBojan Novković #define K1_APMU_NRESETS (RESET_SDH2 + 1);
74*dcb10e3aSBojan Novković
75*dcb10e3aSBojan Novković static int
k1_apmu_probe(device_t dev)76*dcb10e3aSBojan Novković k1_apmu_probe(device_t dev)
77*dcb10e3aSBojan Novković {
78*dcb10e3aSBojan Novković
79*dcb10e3aSBojan Novković if (!ofw_bus_status_okay(dev))
80*dcb10e3aSBojan Novković return (ENXIO);
81*dcb10e3aSBojan Novković if (!ofw_bus_is_compatible(dev, "spacemit,k1-syscon-apmu"))
82*dcb10e3aSBojan Novković return (ENXIO);
83*dcb10e3aSBojan Novković device_set_desc(dev, "SpacemitT K1 APMU Clock Controller");
84*dcb10e3aSBojan Novković
85*dcb10e3aSBojan Novković return (BUS_PROBE_DEFAULT);
86*dcb10e3aSBojan Novković }
87*dcb10e3aSBojan Novković
88*dcb10e3aSBojan Novković static int
k1_apmu_attach(device_t dev)89*dcb10e3aSBojan Novković k1_apmu_attach(device_t dev)
90*dcb10e3aSBojan Novković {
91*dcb10e3aSBojan Novković struct k1_clkdev_softc *sc;
92*dcb10e3aSBojan Novković
93*dcb10e3aSBojan Novković sc = device_get_softc(dev);
94*dcb10e3aSBojan Novković sc->clks = apmu_clks;
95*dcb10e3aSBojan Novković sc->nclks = nitems(apmu_clks);
96*dcb10e3aSBojan Novković
97*dcb10e3aSBojan Novković sc->resets = apmu_resets;
98*dcb10e3aSBojan Novković sc->nresets = K1_APMU_NRESETS;
99*dcb10e3aSBojan Novković
100*dcb10e3aSBojan Novković return (k1_clk_attach(dev));
101*dcb10e3aSBojan Novković }
102*dcb10e3aSBojan Novković
103*dcb10e3aSBojan Novković static device_method_t k1_apmu_clkdev_methods[] = {
104*dcb10e3aSBojan Novković /* device_if */
105*dcb10e3aSBojan Novković DEVMETHOD(device_probe, k1_apmu_probe),
106*dcb10e3aSBojan Novković DEVMETHOD(device_attach, k1_apmu_attach),
107*dcb10e3aSBojan Novković
108*dcb10e3aSBojan Novković DEVMETHOD_END
109*dcb10e3aSBojan Novković };
110*dcb10e3aSBojan Novković
111*dcb10e3aSBojan Novković DEFINE_CLASS_1(k1_apmu, k1_apmu_driver, k1_apmu_clkdev_methods,
112*dcb10e3aSBojan Novković sizeof(struct k1_clkdev_softc), k1_clkdev_driver);
113*dcb10e3aSBojan Novković EARLY_DRIVER_MODULE(k1_apmu, simplebus, k1_apmu_driver, 0, 0,
114*dcb10e3aSBojan Novković BUS_PASS_BUS + BUS_PASS_ORDER_LAST);
115