xref: /freebsd/sys/dev/sdhci/sdhci_fdt_spacemit.c (revision 550db3d6f502946f6cf01eeb34f53e70395ddc6f)
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