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
58struct 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
73int	cdsdhc_match(struct device *, void *, void*);
74void	cdsdhc_attach(struct device *, struct device *, void *);
75void	cdsdhc_bus_clock_pre(struct sdhc_softc *, int, int);
76
77const struct cfattach cdsdhc_ca = {
78	sizeof(struct cdsdhc_softc), cdsdhc_match, cdsdhc_attach
79};
80
81struct cfdriver cdsdhc_cd = {
82	NULL, "cdsdhc", DV_DULL
83};
84
85int
86cdsdhc_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
95void
96cdsdhc_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
137disable:
138	clock_disable_all(faa->fa_node);
139unmap:
140	bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size);
141}
142
143void
144cdsdhc_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