1 /* $OpenBSD: cdsdhc.c,v 1.2 2022/01/18 11:36:21 patrick Exp $ */
2
3 /*
4 * Copyright (c) 2022 Visa Hankala
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 /*
20 * Driver glue for Cadence SD/SDIO/eMMC host controller.
21 */
22
23 #include <sys/param.h>
24 #include <sys/systm.h>
25 #include <sys/conf.h>
26 #include <sys/device.h>
27
28 #include <machine/bus.h>
29 #include <machine/fdt.h>
30
31 #include <dev/ofw/fdt.h>
32 #include <dev/ofw/openfirm.h>
33 #include <dev/ofw/ofw_clock.h>
34
35 #include <dev/sdmmc/sdhcvar.h>
36 #include <dev/sdmmc/sdmmcvar.h>
37
38 /* Host Register Set */
39 #define HRS06 0x0018
40 #define HRS06_ETR (0x1 << 15)
41 #define HRS06_ETV_MASK (0x3f << 8)
42 #define HRS06_ETV_SHIFT 8
43 #define HRS06_EMM_MASK (0x7 << 0)
44 #define HRS06_EMM_SD (0x0 << 0)
45 #define HRS06_EMM_MMC_SDR (0x2 << 0)
46 #define HRS06_EMM_MMC_DDR (0x3 << 0)
47 #define HRS06_EMM_MMC_HS200 (0x4 << 0)
48 #define HRS06_EMM_MMC_HS400 (0x5 << 0)
49 #define HRS06_EMM_MMC_HS400_ENH (0x6 << 0)
50 #define HRS31 0x007c
51 #define HRS31_HOSTCTLVER(x) (((x) >> 16) & 0xfff)
52 #define HRS31_HOSTFIXVER(x) ((x) & 0xff)
53
54 /* Slot Register Set */
55 #define SRS_OFFSET 0x200
56 #define SRS_SIZE 0x100
57
58 struct cdsdhc_softc {
59 struct sdhc_softc sc_sdhc;
60 bus_space_tag_t sc_iot;
61 bus_space_handle_t sc_ioh;
62 bus_space_handle_t sc_srs_ioh;
63 void *sc_ih;
64
65 struct sdhc_host *sc_host;
66 };
67
68 #define HREAD4(sc, reg) \
69 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
70 #define HWRITE4(sc, reg, val) \
71 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
72
73 int cdsdhc_match(struct device *, void *, void*);
74 void cdsdhc_attach(struct device *, struct device *, void *);
75 void cdsdhc_bus_clock_pre(struct sdhc_softc *, int, int);
76
77 const struct cfattach cdsdhc_ca = {
78 sizeof(struct cdsdhc_softc), cdsdhc_match, cdsdhc_attach
79 };
80
81 struct cfdriver cdsdhc_cd = {
82 NULL, "cdsdhc", DV_DULL
83 };
84
85 int
cdsdhc_match(struct device * parent,void * match,void * aux)86 cdsdhc_match(struct device *parent, void *match, void *aux)
87 {
88 struct fdt_attach_args *faa = aux;
89
90 if (faa->fa_nreg < 1)
91 return 0;
92 return OF_is_compatible(faa->fa_node, "cdns,sd4hc");
93 }
94
95 void
cdsdhc_attach(struct device * parent,struct device * self,void * aux)96 cdsdhc_attach(struct device *parent, struct device *self, void *aux)
97 {
98 struct fdt_attach_args *faa = aux;
99 struct cdsdhc_softc *sc = (struct cdsdhc_softc *)self;
100 uint64_t capmask = 0, capset = 0;
101 uint32_t ver;
102
103 sc->sc_iot = faa->fa_iot;
104
105 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size,
106 0, &sc->sc_ioh) != 0) {
107 printf(": can't map registers\n");
108 return;
109 }
110
111 if (bus_space_subregion(sc->sc_iot, sc->sc_ioh, SRS_OFFSET, SRS_SIZE,
112 &sc->sc_srs_ioh) != 0) {
113 printf(": can't map SRS subregion\n");
114 goto unmap;
115 }
116
117 clock_enable_all(faa->fa_node);
118
119 sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_BIO,
120 sdhc_intr, sc, sc->sc_sdhc.sc_dev.dv_xname);
121 if (sc->sc_ih == NULL) {
122 printf(": can't establish interrupt\n");
123 goto disable;
124 }
125
126 ver = HREAD4(sc, HRS31);
127 printf(": rev 0x%x/0x%x\n", HRS31_HOSTCTLVER(ver),
128 HRS31_HOSTFIXVER(ver));
129
130 sc->sc_sdhc.sc_host = &sc->sc_host;
131 sc->sc_sdhc.sc_dmat = faa->fa_dmat;
132 sc->sc_sdhc.sc_bus_clock_pre = cdsdhc_bus_clock_pre;
133 sdhc_host_found(&sc->sc_sdhc, sc->sc_iot, sc->sc_srs_ioh, SRS_SIZE,
134 1, capmask, capset);
135 return;
136
137 disable:
138 clock_disable_all(faa->fa_node);
139 unmap:
140 bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size);
141 }
142
143 void
cdsdhc_bus_clock_pre(struct sdhc_softc * sc_sdhc,int freq,int timing)144 cdsdhc_bus_clock_pre(struct sdhc_softc *sc_sdhc, int freq, int timing)
145 {
146 struct cdsdhc_softc *sc = (struct cdsdhc_softc *)sc_sdhc;
147 uint32_t mode, val;
148
149 switch (timing) {
150 case SDMMC_TIMING_HIGHSPEED:
151 mode = HRS06_EMM_MMC_SDR;
152 break;
153 case SDMMC_TIMING_MMC_DDR52:
154 mode = HRS06_EMM_MMC_DDR;
155 break;
156 case SDMMC_TIMING_MMC_HS200:
157 mode = HRS06_EMM_MMC_HS200;
158 break;
159 default:
160 mode = HRS06_EMM_SD;
161 break;
162 }
163
164 val = HREAD4(sc, HRS06);
165 val &= ~HRS06_EMM_MASK;
166 val |= mode;
167 HWRITE4(sc, HRS06, val);
168 }
169