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