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