1*550db3d6SBojan Novković /*
2*550db3d6SBojan Novković * Copyright (c) 2026 Bojan Novković <bnovkov@FreeBSD.org>
3*550db3d6SBojan Novković *
4*550db3d6SBojan Novković * SPDX-License-Identifier: BSD-2-Clause
5*550db3d6SBojan Novković */
6*550db3d6SBojan Novković
7*550db3d6SBojan Novković #include <sys/param.h>
8*550db3d6SBojan Novković #include <sys/bus.h>
9*550db3d6SBojan Novković #include <sys/kernel.h>
10*550db3d6SBojan Novković #include <sys/sysctl.h>
11*550db3d6SBojan Novković #include <sys/types.h>
12*550db3d6SBojan Novković #include <sys/taskqueue.h>
13*550db3d6SBojan Novković #include <sys/module.h>
14*550db3d6SBojan Novković
15*550db3d6SBojan Novković #include <machine/bus.h>
16*550db3d6SBojan Novković
17*550db3d6SBojan Novković #include <dev/ofw/ofw_bus.h>
18*550db3d6SBojan Novković #include <dev/ofw/ofw_bus_subr.h>
19*550db3d6SBojan Novković #include <dev/ofw/ofw_subr.h>
20*550db3d6SBojan Novković #include <dev/clk/clk.h>
21*550db3d6SBojan Novković #include <dev/ofw/openfirm.h>
22*550db3d6SBojan Novković
23*550db3d6SBojan Novković #include <dev/hwreset/hwreset.h>
24*550db3d6SBojan Novković #include <dev/phy/phy.h>
25*550db3d6SBojan Novković #include <dev/mmc/bridge.h>
26*550db3d6SBojan Novković #include <dev/mmc/mmcbrvar.h>
27*550db3d6SBojan Novković #include <dev/mmc/mmcreg.h>
28*550db3d6SBojan Novković
29*550db3d6SBojan Novković #include <dev/sdhci/sdhci.h>
30*550db3d6SBojan Novković #include <dev/sdhci/sdhci_fdt.h>
31*550db3d6SBojan Novković
32*550db3d6SBojan Novković #include "mmcbr_if.h"
33*550db3d6SBojan Novković #include "sdhci_if.h"
34*550db3d6SBojan Novković
35*550db3d6SBojan Novković #include "opt_mmccam.h"
36*550db3d6SBojan Novković #include "opt_soc.h"
37*550db3d6SBojan Novković
38*550db3d6SBojan Novković static struct ofw_compat_data compat_data[] = {
39*550db3d6SBojan Novković { "spacemit,k1-sdhci", 1 },
40*550db3d6SBojan Novković { NULL, 0 }
41*550db3d6SBojan Novković };
42*550db3d6SBojan Novković
43*550db3d6SBojan Novković static int
sdhci_fdt_spacemit_probe(device_t dev)44*550db3d6SBojan Novković sdhci_fdt_spacemit_probe(device_t dev)
45*550db3d6SBojan Novković {
46*550db3d6SBojan Novković
47*550db3d6SBojan Novković if (!ofw_bus_status_okay(dev))
48*550db3d6SBojan Novković return (ENXIO);
49*550db3d6SBojan Novković
50*550db3d6SBojan Novković if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
51*550db3d6SBojan Novković return (ENXIO);
52*550db3d6SBojan Novković
53*550db3d6SBojan Novković if (ofw_bus_has_prop(dev, "no-sd")) {
54*550db3d6SBojan Novković /* XXX: only attach to the SD card for now. */
55*550db3d6SBojan Novković device_printf(dev, "MMC and SDIO aren't supported yet\n");
56*550db3d6SBojan Novković return (ENXIO);
57*550db3d6SBojan Novković }
58*550db3d6SBojan Novković
59*550db3d6SBojan Novković device_set_desc(dev, "SpacemiT K1 SDHCI controller");
60*550db3d6SBojan Novković
61*550db3d6SBojan Novković return (BUS_PROBE_DEFAULT);
62*550db3d6SBojan Novković }
63*550db3d6SBojan Novković
64*550db3d6SBojan Novković static int
sdhci_fdt_spacemit_attach(device_t dev)65*550db3d6SBojan Novković sdhci_fdt_spacemit_attach(device_t dev)
66*550db3d6SBojan Novković {
67*550db3d6SBojan Novković struct sdhci_fdt_softc *sc;
68*550db3d6SBojan Novković clk_t clk_core;
69*550db3d6SBojan Novković hwreset_t rst;
70*550db3d6SBojan Novković
71*550db3d6SBojan Novković sc = device_get_softc(dev);
72*550db3d6SBojan Novković sc->quirks = SDHCI_QUIRK_PRESET_VALUE_BROKEN |
73*550db3d6SBojan Novković SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
74*550db3d6SBojan Novković
75*550db3d6SBojan Novković if (clk_get_by_ofw_name(dev, 0, "core", &clk_core)) {
76*550db3d6SBojan Novković device_printf(dev, "cannot get core clock\n");
77*550db3d6SBojan Novković return (ENXIO);
78*550db3d6SBojan Novković }
79*550db3d6SBojan Novković if (clk_get_by_ofw_name(dev, 0, "io", &sc->clk_core)) {
80*550db3d6SBojan Novković device_printf(dev, "cannot get io clock\n");
81*550db3d6SBojan Novković return (ENXIO);
82*550db3d6SBojan Novković }
83*550db3d6SBojan Novković if (hwreset_get_by_ofw_name(dev, 0, "sdh", &rst) != 0) {
84*550db3d6SBojan Novković device_printf(dev, "cannot get device reset\n");
85*550db3d6SBojan Novković return (ENXIO);
86*550db3d6SBojan Novković }
87*550db3d6SBojan Novković
88*550db3d6SBojan Novković if (hwreset_deassert(rst) != 0) {
89*550db3d6SBojan Novković device_printf(dev, "cannot reset device\n");
90*550db3d6SBojan Novković return (ENXIO);
91*550db3d6SBojan Novković }
92*550db3d6SBojan Novković if (clk_enable(clk_core) != 0) {
93*550db3d6SBojan Novković device_printf(dev, "cannot enable core clock\n");
94*550db3d6SBojan Novković return (ENXIO);
95*550db3d6SBojan Novković }
96*550db3d6SBojan Novković if (clk_enable(sc->clk_core) != 0) {
97*550db3d6SBojan Novković device_printf(dev, "cannot enable io clock\n");
98*550db3d6SBojan Novković return (ENXIO);
99*550db3d6SBojan Novković }
100*550db3d6SBojan Novković
101*550db3d6SBojan Novković return (sdhci_fdt_attach(dev));
102*550db3d6SBojan Novković }
103*550db3d6SBojan Novković
104*550db3d6SBojan Novković static int
sdhci_fdt_spacemit_set_clock(device_t dev,struct sdhci_slot * slot,int clock)105*550db3d6SBojan Novković sdhci_fdt_spacemit_set_clock(device_t dev, struct sdhci_slot *slot, int clock)
106*550db3d6SBojan Novković {
107*550db3d6SBojan Novković struct sdhci_fdt_softc *sc = device_get_softc(dev);
108*550db3d6SBojan Novković uint64_t freq;
109*550db3d6SBojan Novković
110*550db3d6SBojan Novković sc = device_get_softc(dev);
111*550db3d6SBojan Novković
112*550db3d6SBojan Novković clk_set_freq(sc->clk_core, clock, CLK_SET_ROUND_ANY);
113*550db3d6SBojan Novković clk_get_freq(sc->clk_core, &freq);
114*550db3d6SBojan Novković
115*550db3d6SBojan Novković return ((int)freq);
116*550db3d6SBojan Novković }
117*550db3d6SBojan Novković
118*550db3d6SBojan Novković static device_method_t sdhci_fdt_spacemit_methods[] = {
119*550db3d6SBojan Novković /* device_if */
120*550db3d6SBojan Novković DEVMETHOD(device_probe, sdhci_fdt_spacemit_probe),
121*550db3d6SBojan Novković DEVMETHOD(device_attach, sdhci_fdt_spacemit_attach),
122*550db3d6SBojan Novković
123*550db3d6SBojan Novković DEVMETHOD(sdhci_set_clock, sdhci_fdt_spacemit_set_clock),
124*550db3d6SBojan Novković DEVMETHOD_END
125*550db3d6SBojan Novković };
126*550db3d6SBojan Novković extern driver_t sdhci_fdt_driver;
127*550db3d6SBojan Novković
128*550db3d6SBojan Novković DEFINE_CLASS_1(sdhci_spacemit, sdhci_fdt_spacemit_driver, sdhci_fdt_spacemit_methods,
129*550db3d6SBojan Novković sizeof(struct sdhci_fdt_softc), sdhci_fdt_driver);
130*550db3d6SBojan Novković DRIVER_MODULE(sdhci_spacemit, simplebus, sdhci_fdt_spacemit_driver, NULL, NULL);
131*550db3d6SBojan Novković
132*550db3d6SBojan Novković #ifndef MMCCAM
133*550db3d6SBojan Novković MMC_DECLARE_BRIDGE(sdhci_fdt_spacemit);
134*550db3d6SBojan Novković #endif
135