xref: /openbsd/sys/dev/fdt/cdsdhc.c (revision bfa5fdb5)
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