xref: /openbsd-src/sys/dev/fdt/dwmmc.c (revision 98e9fa9cafbb26c6ae5b61db95ed73f54294294b)
1*98e9fa9cSpatrick /*	$OpenBSD: dwmmc.c,v 1.31 2024/12/19 18:02:47 patrick Exp $	*/
224225f53Skettenis /*
324225f53Skettenis  * Copyright (c) 2017 Mark Kettenis
424225f53Skettenis  *
524225f53Skettenis  * Permission to use, copy, modify, and distribute this software for any
624225f53Skettenis  * purpose with or without fee is hereby granted, provided that the above
724225f53Skettenis  * copyright notice and this permission notice appear in all copies.
824225f53Skettenis  *
924225f53Skettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1024225f53Skettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1124225f53Skettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1224225f53Skettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1324225f53Skettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1424225f53Skettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1524225f53Skettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1624225f53Skettenis  */
1724225f53Skettenis 
1824225f53Skettenis #include <sys/param.h>
19b9e8b644Skettenis #include <sys/kernel.h>
2024225f53Skettenis #include <sys/malloc.h>
2124225f53Skettenis #include <sys/systm.h>
2224225f53Skettenis 
2324225f53Skettenis #include <machine/bus.h>
2424225f53Skettenis #include <machine/fdt.h>
2524225f53Skettenis #include <machine/intr.h>
2624225f53Skettenis 
2724225f53Skettenis #include <dev/ofw/openfirm.h>
2824225f53Skettenis #include <dev/ofw/ofw_clock.h>
29f8fb77f5Skettenis #include <dev/ofw/ofw_gpio.h>
3024225f53Skettenis #include <dev/ofw/ofw_pinctrl.h>
31*98e9fa9cSpatrick #include <dev/ofw/ofw_regulator.h>
3224225f53Skettenis #include <dev/ofw/fdt.h>
3324225f53Skettenis 
3424225f53Skettenis #include <dev/sdmmc/sdmmcvar.h>
353704a61cSkettenis #include <dev/sdmmc/sdmmc_ioreg.h>
3624225f53Skettenis 
3724225f53Skettenis #define SDMMC_CTRL		0x0000
3824225f53Skettenis #define  SDMMC_CTRL_USE_INTERNAL_DMAC	(1 << 25)
39b9e8b644Skettenis #define  SDMMC_CTRL_DMA_ENABLE		(1 << 5)
40d5957df7Skettenis #define  SDMMC_CTRL_INT_ENABLE		(1 << 4)
4168c9da83Skettenis #define  SDMMC_CTRL_DMA_RESET		(1 << 2)
4268c9da83Skettenis #define  SDMMC_CTRL_FIFO_RESET		(1 << 1)
4368c9da83Skettenis #define  SDMMC_CTRL_CONTROLLER_RESET	(1 << 0)
4424225f53Skettenis #define  SDMMC_CTRL_ALL_RESET	(SDMMC_CTRL_CONTROLLER_RESET | \
4524225f53Skettenis     SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET)
4624225f53Skettenis #define SDMMC_PWREN		0x0004
4724225f53Skettenis #define SDMMC_CLKDIV		0x0008
4824225f53Skettenis #define SDMMC_CLKSRC		0x000c
4924225f53Skettenis #define SDMMC_CLKENA		0x0010
5024225f53Skettenis #define  SDMMC_CLKENA_CCLK_LOW_POWER	(1 << 16)
5124225f53Skettenis #define  SDMMC_CLKENA_CCLK_ENABLE	(1 << 0)
5224225f53Skettenis #define SDMMC_TMOUT		0x0014
5324225f53Skettenis #define SDMMC_CTYPE		0x0018
5424225f53Skettenis #define  SDMMC_CTYPE_8BIT		(1 << 16)
5524225f53Skettenis #define  SDMMC_CTYPE_4BIT		(1 << 0)
5624225f53Skettenis #define SDMMC_BLKSIZ		0x001c
5724225f53Skettenis #define SDMMC_BYTCNT		0x0020
5824225f53Skettenis #define SDMMC_INTMASK		0x0024
5924225f53Skettenis #define SDMMC_CMDARG		0x0028
6024225f53Skettenis #define SDMMC_CMD		0x002c
6124225f53Skettenis #define  SDMMC_CMD_START_CMD		(1U << 31)
6224225f53Skettenis #define  SDMMC_CMD_USE_HOLD_REG		(1 << 29)
6324225f53Skettenis #define  SDMMC_CMD_UPDATE_CLOCK_REGISTERS_ONLY	(1 << 21)
6424225f53Skettenis #define  SDMMC_CMD_SEND_INITIALIZATION	(1 << 15)
6524225f53Skettenis #define  SDMMC_CMD_STOP_ABORT_CMD	(1 << 14)
6624225f53Skettenis #define  SDMMC_CMD_WAIT_PRVDATA_COMPLETE	(1 << 13)
6724225f53Skettenis #define  SDMMC_CMD_SEND_AUTO_STOP	(1 << 12)
6824225f53Skettenis #define  SDMMC_CMD_WR			(1 << 10)
6924225f53Skettenis #define  SDMMC_CMD_DATA_EXPECTED	(1 << 9)
7024225f53Skettenis #define  SDMMC_CMD_CHECK_REPONSE_CRC	(1 << 8)
7124225f53Skettenis #define  SDMMC_CMD_RESPONSE_LENGTH	(1 << 7)
7224225f53Skettenis #define  SDMMC_CMD_RESPONSE_EXPECT	(1 << 6)
7324225f53Skettenis #define SDMMC_RESP0		0x0030
7424225f53Skettenis #define SDMMC_RESP1		0x0034
7524225f53Skettenis #define SDMMC_RESP2		0x0038
7624225f53Skettenis #define SDMMC_RESP3		0x003c
7724225f53Skettenis #define SDMMC_MINTSTS		0x0040
7824225f53Skettenis #define SDMMC_RINTSTS		0x0044
79d5957df7Skettenis #define  SDMMC_RINTSTS_SDIO		(1 << 24)
8024225f53Skettenis #define  SDMMC_RINTSTS_EBE		(1 << 15)
8124225f53Skettenis #define  SDMMC_RINTSTS_ACD		(1 << 14)
8224225f53Skettenis #define  SDMMC_RINTSTS_SBE		(1 << 13)
8324225f53Skettenis #define  SDMMC_RINTSTS_HLE		(1 << 12)
8424225f53Skettenis #define  SDMMC_RINTSTS_FRUN		(1 << 11)
8524225f53Skettenis #define  SDMMC_RINTSTS_HTO		(1 << 10)
8624225f53Skettenis #define  SDMMC_RINTSTS_DRTO		(1 << 9)
8724225f53Skettenis #define  SDMMC_RINTSTS_RTO		(1 << 8)
8824225f53Skettenis #define  SDMMC_RINTSTS_DCRC		(1 << 7)
8924225f53Skettenis #define  SDMMC_RINTSTS_RCRC		(1 << 6)
9024225f53Skettenis #define  SDMMC_RINTSTS_RXDR		(1 << 5)
9124225f53Skettenis #define  SDMMC_RINTSTS_TXDR		(1 << 4)
9224225f53Skettenis #define  SDMMC_RINTSTS_DTO		(1 << 3)
9324225f53Skettenis #define  SDMMC_RINTSTS_CD		(1 << 2)
9424225f53Skettenis #define  SDMMC_RINTSTS_RE		(1 << 1)
9524225f53Skettenis #define  SDMMC_RINTSTS_CDT		(1 << 0)
9624225f53Skettenis #define  SDMMC_RINTSTS_DATA_ERR	(SDMMC_RINTSTS_EBE | SDMMC_RINTSTS_SBE | \
9724225f53Skettenis     SDMMC_RINTSTS_HLE | SDMMC_RINTSTS_FRUN | SDMMC_RINTSTS_DCRC)
9824225f53Skettenis #define  SDMMC_RINTSTS_DATA_TO	(SDMMC_RINTSTS_HTO | SDMMC_RINTSTS_DRTO)
9924225f53Skettenis #define SDMMC_STATUS		0x0048
10024225f53Skettenis #define SDMMC_STATUS_FIFO_COUNT(x)	(((x) >> 17) & 0x1fff)
10124225f53Skettenis #define  SDMMC_STATUS_DATA_BUSY		(1 << 9)
10224225f53Skettenis #define SDMMC_FIFOTH		0x004c
10325f77276Skettenis #define  SDMMC_FIFOTH_MSIZE_SHIFT	28
10425f77276Skettenis #define  SDMMC_FIFOTH_RXWM_SHIFT	16
1050299d551Skettenis #define  SDMMC_FIFOTH_RXWM(x)		(((x) >> 16) & 0xfff)
10625f77276Skettenis #define  SDMMC_FIFOTH_TXWM_SHIFT	0
10724225f53Skettenis #define SDMMC_CDETECT		0x0050
10824225f53Skettenis #define  SDMMC_CDETECT_CARD_DETECT_0	(1 << 0)
10924225f53Skettenis #define SDMMC_WRTPRT		0x0054
11024225f53Skettenis #define SDMMC_TCBCNT		0x005c
11124225f53Skettenis #define SDMMC_TBBCNT		0x0060
11224225f53Skettenis #define SDMMC_DEBNCE		0x0064
11324225f53Skettenis #define SDMMC_USRID		0x0068
11424225f53Skettenis #define SDMMC_VERID		0x006c
11524225f53Skettenis #define SDMMC_HCON		0x0070
11670864666Skettenis #define  SDMMC_HCON_DATA_WIDTH(x)	(((x) >> 7) & 0x7)
1174df26cf2Skettenis #define  SDMMC_HCON_DMA64		(1 << 27)
11824225f53Skettenis #define SDMMC_UHS_REG		0x0074
11924225f53Skettenis #define SDMMC_RST_n		0x0078
12024225f53Skettenis #define SDMMC_BMOD		0x0080
121b9e8b644Skettenis #define  SDMMC_BMOD_DE			(1 << 7)
122b9e8b644Skettenis #define  SDMMC_BMOD_FB			(1 << 1)
123b9e8b644Skettenis #define  SDMMC_BMOD_SWR			(1 << 0)
12424225f53Skettenis #define SDMMC_PLDMND		0x0084
12524225f53Skettenis #define SDMMC_DBADDR		0x0088
1264df26cf2Skettenis #define SDMMC_IDSTS32		0x008c
127b9e8b644Skettenis #define  SDMMC_IDSTS_NIS		(1 << 8)
128b9e8b644Skettenis #define  SDMMC_IDSTS_RI			(1 << 1)
129b9e8b644Skettenis #define  SDMMC_IDSTS_TI			(1 << 0)
1304df26cf2Skettenis #define SDMMC_IDINTEN32		0x0090
131b9e8b644Skettenis #define  SDMMC_IDINTEN_NI		(1 << 8)
132b9e8b644Skettenis #define  SDMMC_IDINTEN_RI		(1 << 1)
133b9e8b644Skettenis #define  SDMMC_IDINTEN_TI		(1 << 0)
13424225f53Skettenis #define SDMMC_DSCADDR		0x0094
13524225f53Skettenis #define SDMMC_BUFADDR		0x0098
13670864666Skettenis #define SDMMC_CLKSEL		0x009c
13724225f53Skettenis #define SDMMC_CARDTHRCTL	0x0100
13824225f53Skettenis #define  SDMMC_CARDTHRCTL_RDTHR_SHIFT	16
13924225f53Skettenis #define  SDMMC_CARDTHRCTL_RDTHREN	(1 << 0)
14024225f53Skettenis #define SDMMC_BACK_END_POWER	0x0104
14124225f53Skettenis #define SDMMC_EMMC_DDR_REG	0x0108
14224225f53Skettenis #define SDMMC_FIFO_BASE		0x0200
14324225f53Skettenis 
1444df26cf2Skettenis #define SDMMC_DBADDRL		0x0088
1454df26cf2Skettenis #define SDMMC_DBADDRH		0x008c
1464df26cf2Skettenis #define SDMMC_IDSTS64		0x0090
1474df26cf2Skettenis #define SDMMC_IDINTEN64		0x0094
1484df26cf2Skettenis #define SDMMC_DSCADDRL		0x0098
1494df26cf2Skettenis #define SDMMC_DSCADDRH		0x009c
1504df26cf2Skettenis #define SDMMC_BUFADDRL		0x00a0
1514df26cf2Skettenis #define SDMMC_BUFADDRH		0x00a4
1524df26cf2Skettenis 
1534df26cf2Skettenis #define SDMMC_IDSTS(sc) \
1544df26cf2Skettenis     ((sc)->sc_dma64 ? SDMMC_IDSTS64 : SDMMC_IDSTS32)
1554df26cf2Skettenis 
15624225f53Skettenis #define HREAD4(sc, reg)							\
15724225f53Skettenis     (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
15824225f53Skettenis #define HWRITE4(sc, reg, val)						\
15924225f53Skettenis     bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
16024225f53Skettenis #define HSET4(sc, reg, bits)						\
16124225f53Skettenis     HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
16224225f53Skettenis #define HCLR4(sc, reg, bits)						\
16324225f53Skettenis     HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
16424225f53Skettenis 
1654df26cf2Skettenis struct dwmmc_desc32 {
166b9e8b644Skettenis 	uint32_t des[4];
167b9e8b644Skettenis };
168b9e8b644Skettenis 
1694df26cf2Skettenis struct dwmmc_desc64 {
1704df26cf2Skettenis 	uint32_t des[8];
1714df26cf2Skettenis };
1724df26cf2Skettenis 
1734df26cf2Skettenis #define DWMMC_NDESC	(PAGE_SIZE / sizeof(struct dwmmc_desc64))
174b9e8b644Skettenis #define DWMMC_MAXSEGSZ	0x1000
175b9e8b644Skettenis 
176b9e8b644Skettenis #define DES0_OWN	(1U << 31)
177b9e8b644Skettenis #define DES0_CES	(1 << 30)
178b9e8b644Skettenis #define DES0_ER		(1 << 5)
179b9e8b644Skettenis #define DES0_CH		(1 << 4)
180b9e8b644Skettenis #define DES0_FS		(1 << 3)
181b9e8b644Skettenis #define DES0_LD		(1 << 2)
182b9e8b644Skettenis #define DES0_DIC	(1 << 1)
183b9e8b644Skettenis 
184b9e8b644Skettenis #define DES1_BS2(sz)	(((sz) & 0x1fff) << 13)
185b9e8b644Skettenis #define DES1_BS1(sz)	(((sz) & 0x1fff) << 0)
1864df26cf2Skettenis #define DES2_BS2(sz)	DES1_BS2(sz)
1874df26cf2Skettenis #define DES2_BS1(sz)	DES1_BS1(sz)
188b9e8b644Skettenis 
18924225f53Skettenis struct dwmmc_softc {
19024225f53Skettenis 	struct device		sc_dev;
19124225f53Skettenis 	bus_space_tag_t		sc_iot;
19224225f53Skettenis 	bus_space_handle_t	sc_ioh;
19324225f53Skettenis 	bus_size_t		sc_size;
194b9e8b644Skettenis 	bus_dma_tag_t		sc_dmat;
195b9e8b644Skettenis 	bus_dmamap_t		sc_dmap;
196f8fb77f5Skettenis 	int			sc_node;
197f8fb77f5Skettenis 
19824225f53Skettenis 	void			*sc_ih;
19924225f53Skettenis 
20024225f53Skettenis 	uint32_t		sc_clkbase;
20124225f53Skettenis 	uint32_t		sc_fifo_depth;
20270864666Skettenis 	uint32_t		sc_fifo_width;
20370864666Skettenis 	void (*sc_read_data)(struct dwmmc_softc *, u_char *, int);
20470864666Skettenis 	void (*sc_write_data)(struct dwmmc_softc *, u_char *, int);
2053704a61cSkettenis 	int			sc_blklen;
20624225f53Skettenis 
207b9e8b644Skettenis 	bus_dmamap_t		sc_desc_map;
208b9e8b644Skettenis 	bus_dma_segment_t	sc_desc_segs[1];
209b9e8b644Skettenis 	caddr_t			sc_desc;
2104df26cf2Skettenis 	int			sc_dma64;
211b9e8b644Skettenis 	int			sc_dmamode;
212b9e8b644Skettenis 	uint32_t		sc_idsts;
213b9e8b644Skettenis 
214f8fb77f5Skettenis 	uint32_t		sc_gpio[4];
215d5957df7Skettenis 	int			sc_sdio_irq;
216*98e9fa9cSpatrick 	uint32_t		sc_vqmmc;
217d5957df7Skettenis 	uint32_t		sc_pwrseq;
218d5957df7Skettenis 	uint32_t		sc_vdd;
219f8fb77f5Skettenis 
22024225f53Skettenis 	struct device		*sc_sdmmc;
22124225f53Skettenis };
22224225f53Skettenis 
22324225f53Skettenis int	dwmmc_match(struct device *, void *, void *);
22424225f53Skettenis void	dwmmc_attach(struct device *, struct device *, void *);
22524225f53Skettenis 
2269fdf0c62Smpi const struct cfattach dwmmc_ca = {
22724225f53Skettenis 	sizeof(struct dwmmc_softc), dwmmc_match, dwmmc_attach
22824225f53Skettenis };
22924225f53Skettenis 
23024225f53Skettenis struct cfdriver dwmmc_cd = {
23124225f53Skettenis 	NULL, "dwmmc", DV_DULL
23224225f53Skettenis };
23324225f53Skettenis 
23424225f53Skettenis int	dwmmc_intr(void *);
23524225f53Skettenis 
23624225f53Skettenis int	dwmmc_host_reset(sdmmc_chipset_handle_t);
23724225f53Skettenis uint32_t dwmmc_host_ocr(sdmmc_chipset_handle_t);
23824225f53Skettenis int	dwmmc_host_maxblklen(sdmmc_chipset_handle_t);
23924225f53Skettenis int	dwmmc_card_detect(sdmmc_chipset_handle_t);
24024225f53Skettenis int	dwmmc_bus_power(sdmmc_chipset_handle_t, uint32_t);
24124225f53Skettenis int	dwmmc_bus_clock(sdmmc_chipset_handle_t, int, int);
24224225f53Skettenis int	dwmmc_bus_width(sdmmc_chipset_handle_t, int);
24324225f53Skettenis void	dwmmc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *);
244d5957df7Skettenis void	dwmmc_card_intr_mask(sdmmc_chipset_handle_t, int);
245d5957df7Skettenis void	dwmmc_card_intr_ack(sdmmc_chipset_handle_t);
246*98e9fa9cSpatrick int	dwmmc_signal_voltage(sdmmc_chipset_handle_t, int);
24724225f53Skettenis 
24824225f53Skettenis struct sdmmc_chip_functions dwmmc_chip_functions = {
24924225f53Skettenis 	.host_reset = dwmmc_host_reset,
25024225f53Skettenis 	.host_ocr = dwmmc_host_ocr,
25124225f53Skettenis 	.host_maxblklen = dwmmc_host_maxblklen,
25224225f53Skettenis 	.card_detect = dwmmc_card_detect,
25324225f53Skettenis 	.bus_power = dwmmc_bus_power,
25424225f53Skettenis 	.bus_clock = dwmmc_bus_clock,
25524225f53Skettenis 	.bus_width = dwmmc_bus_width,
25624225f53Skettenis 	.exec_command = dwmmc_exec_command,
257d5957df7Skettenis 	.card_intr_mask = dwmmc_card_intr_mask,
258d5957df7Skettenis 	.card_intr_ack = dwmmc_card_intr_ack,
259*98e9fa9cSpatrick 	.signal_voltage = dwmmc_signal_voltage,
26024225f53Skettenis };
26124225f53Skettenis 
2623704a61cSkettenis void	dwmmc_pio_mode(struct dwmmc_softc *);
263b9e8b644Skettenis int	dwmmc_alloc_descriptors(struct dwmmc_softc *);
264b9e8b644Skettenis void	dwmmc_init_descriptors(struct dwmmc_softc *);
26524225f53Skettenis void	dwmmc_transfer_data(struct dwmmc_softc *, struct sdmmc_command *);
26670864666Skettenis void	dwmmc_read_data32(struct dwmmc_softc *, u_char *, int);
26770864666Skettenis void	dwmmc_write_data32(struct dwmmc_softc *, u_char *, int);
26870864666Skettenis void	dwmmc_read_data64(struct dwmmc_softc *, u_char *, int);
26970864666Skettenis void	dwmmc_write_data64(struct dwmmc_softc *, u_char *, int);
270d5957df7Skettenis void	dwmmc_pwrseq_pre(uint32_t);
271d5957df7Skettenis void	dwmmc_pwrseq_post(uint32_t);
27224225f53Skettenis 
27324225f53Skettenis int
27424225f53Skettenis dwmmc_match(struct device *parent, void *match, void *aux)
27524225f53Skettenis {
27624225f53Skettenis 	struct fdt_attach_args *faa = aux;
27724225f53Skettenis 
2780299d551Skettenis 	return (OF_is_compatible(faa->fa_node, "hisilicon,hi3660-dw-mshc") ||
2790299d551Skettenis 	    OF_is_compatible(faa->fa_node, "hisilicon,hi3670-dw-mshc") ||
2800299d551Skettenis 	    OF_is_compatible(faa->fa_node, "rockchip,rk3288-dw-mshc") ||
281619f63beSjsg 	    OF_is_compatible(faa->fa_node, "samsung,exynos5420-dw-mshc") ||
28264cee0edSjsing 	    OF_is_compatible(faa->fa_node, "snps,dw-mshc") ||
28364cee0edSjsing 	    OF_is_compatible(faa->fa_node, "starfive,jh7110-mmc"));
28424225f53Skettenis }
28524225f53Skettenis 
28624225f53Skettenis void
28724225f53Skettenis dwmmc_attach(struct device *parent, struct device *self, void *aux)
28824225f53Skettenis {
28924225f53Skettenis 	struct dwmmc_softc *sc = (struct dwmmc_softc *)self;
29024225f53Skettenis 	struct fdt_attach_args *faa = aux;
29124225f53Skettenis 	struct sdmmcbus_attach_args saa;
29228eeafaaSkettenis 	uint32_t freq = 0, div = 0;
293a78d0ad4Skettenis 	uint32_t hcon, width;
2940299d551Skettenis 	uint32_t fifoth;
295b9e8b644Skettenis 	int error, timeout;
29624225f53Skettenis 
29724225f53Skettenis 	if (faa->fa_nreg < 1) {
29824225f53Skettenis 		printf(": no registers\n");
29924225f53Skettenis 		return;
30024225f53Skettenis 	}
30124225f53Skettenis 
302f8fb77f5Skettenis 	sc->sc_node = faa->fa_node;
30324225f53Skettenis 	sc->sc_iot = faa->fa_iot;
30424225f53Skettenis 	sc->sc_size = faa->fa_reg[0].size;
30524225f53Skettenis 
30624225f53Skettenis 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
30724225f53Skettenis 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
30824225f53Skettenis 		printf(": can't map registers\n");
30924225f53Skettenis 		return;
31024225f53Skettenis 	}
31124225f53Skettenis 
31224225f53Skettenis 	pinctrl_byname(faa->fa_node, "default");
31324225f53Skettenis 
31424225f53Skettenis 	clock_enable_all(faa->fa_node);
31524225f53Skettenis 	reset_deassert_all(faa->fa_node);
31624225f53Skettenis 
31770864666Skettenis 	/*
31870864666Skettenis 	 * Determine FIFO width from hardware configuration register.
31970864666Skettenis 	 * We only support 32-bit and 64-bit FIFOs.
32070864666Skettenis 	 */
32170864666Skettenis 	hcon = HREAD4(sc, SDMMC_HCON);
32270864666Skettenis 	switch (SDMMC_HCON_DATA_WIDTH(hcon)) {
32370864666Skettenis 	case 1:
32470864666Skettenis 		sc->sc_fifo_width = 4;
32570864666Skettenis 		sc->sc_read_data = dwmmc_read_data32;
32670864666Skettenis 		sc->sc_write_data = dwmmc_write_data32;
32770864666Skettenis 		break;
32870864666Skettenis 	case 2:
32970864666Skettenis 		sc->sc_fifo_width = 8;
33070864666Skettenis 		sc->sc_read_data = dwmmc_read_data64;
33170864666Skettenis 		sc->sc_write_data = dwmmc_write_data64;
33270864666Skettenis 		break;
33370864666Skettenis 	default:
33470864666Skettenis 		printf(": unsupported FIFO width\n");
33570864666Skettenis 		return;
33670864666Skettenis 	}
33770864666Skettenis 
33870864666Skettenis 	sc->sc_fifo_depth = OF_getpropint(faa->fa_node, "fifo-depth", 0);
33970864666Skettenis 	if (sc->sc_fifo_depth == 0) {
3400299d551Skettenis 		fifoth = HREAD4(sc, SDMMC_FIFOTH);
3410299d551Skettenis 		sc->sc_fifo_depth = SDMMC_FIFOTH_RXWM(fifoth) + 1;
34270864666Skettenis 	}
34370864666Skettenis 
3444df26cf2Skettenis 	if (hcon & SDMMC_HCON_DMA64)
3454df26cf2Skettenis 		sc->sc_dma64 = 1;
3464df26cf2Skettenis 
34732408765Skettenis 	/* Some SoCs pre-divide the clock. */
348a78d0ad4Skettenis 	if (OF_is_compatible(faa->fa_node, "rockchip,rk3288-dw-mshc"))
349a78d0ad4Skettenis 		div = 1;
35032408765Skettenis 	if (OF_is_compatible(faa->fa_node, "hisilicon,hi3660-dw-mshc") ||
35132408765Skettenis 	    OF_is_compatible(faa->fa_node, "hisilicon,hi3670-dw-mshc"))
35232408765Skettenis 		div = 7;
353a78d0ad4Skettenis 
35428eeafaaSkettenis 	/* Force the base clock to 50MHz on Rockchip SoCs. */
35528eeafaaSkettenis 	if (OF_is_compatible(faa->fa_node, "rockchip,rk3288-dw-mshc"))
35628eeafaaSkettenis 		freq = 50000000;
35728eeafaaSkettenis 
35828eeafaaSkettenis 	freq = OF_getpropint(faa->fa_node, "clock-frequency", freq);
359a78d0ad4Skettenis 	if (freq > 0)
360a78d0ad4Skettenis 		clock_set_frequency(faa->fa_node, "ciu", (div + 1) * freq);
361a78d0ad4Skettenis 
36224225f53Skettenis 	sc->sc_clkbase = clock_get_frequency(faa->fa_node, "ciu");
363619f63beSjsg 	/* if ciu clock is missing the rate is clock-frequency */
364619f63beSjsg 	if (sc->sc_clkbase == 0)
365619f63beSjsg 		sc->sc_clkbase = freq;
3662b571f7fSjsing 	if (sc->sc_clkbase == 0) {
3672b571f7fSjsing 		printf(": no clock base\n");
3682b571f7fSjsing 		return;
3692b571f7fSjsing 	}
370a78d0ad4Skettenis 	div = OF_getpropint(faa->fa_node, "samsung,dw-mshc-ciu-div", div);
371a78d0ad4Skettenis 	sc->sc_clkbase /= (div + 1);
37224225f53Skettenis 
37370e69ae2Spatrick 	sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_BIO,
37424225f53Skettenis 	    dwmmc_intr, sc, sc->sc_dev.dv_xname);
37524225f53Skettenis 	if (sc->sc_ih == NULL) {
37624225f53Skettenis 		printf(": can't establish interrupt\n");
37724225f53Skettenis 		goto unmap;
37824225f53Skettenis 	}
37924225f53Skettenis 
380f8fb77f5Skettenis 	OF_getpropintarray(faa->fa_node, "cd-gpios", sc->sc_gpio,
381f8fb77f5Skettenis 	    sizeof(sc->sc_gpio));
382f8fb77f5Skettenis 	if (sc->sc_gpio[0])
383f8fb77f5Skettenis 		gpio_controller_config_pin(sc->sc_gpio, GPIO_CONFIG_INPUT);
384f8fb77f5Skettenis 
385d5957df7Skettenis 	sc->sc_sdio_irq = (OF_getproplen(sc->sc_node, "cap-sdio-irq") == 0);
386*98e9fa9cSpatrick 	sc->sc_vqmmc = OF_getpropint(sc->sc_node, "vqmmc-supply", 0);
387d5957df7Skettenis 	sc->sc_pwrseq = OF_getpropint(sc->sc_node, "mmc-pwrseq", 0);
388d5957df7Skettenis 
38970864666Skettenis 	printf(": %d MHz base clock\n", sc->sc_clkbase / 1000000);
39024225f53Skettenis 
39124225f53Skettenis 	HSET4(sc, SDMMC_CTRL, SDMMC_CTRL_ALL_RESET);
392e036655fSkettenis 	for (timeout = 5000; timeout > 0; timeout--) {
39324225f53Skettenis 		if ((HREAD4(sc, SDMMC_CTRL) & SDMMC_CTRL_ALL_RESET) == 0)
39424225f53Skettenis 			break;
395e036655fSkettenis 		delay(100);
39624225f53Skettenis 	}
39724225f53Skettenis 	if (timeout == 0)
39824225f53Skettenis 		printf("%s: reset failed\n", sc->sc_dev.dv_xname);
39924225f53Skettenis 
400d5957df7Skettenis 	/* Enable interrupts, but mask them all. */
401d5957df7Skettenis 	HWRITE4(sc, SDMMC_INTMASK, 0);
402d5957df7Skettenis 	HWRITE4(sc, SDMMC_RINTSTS, 0xffffffff);
403d5957df7Skettenis 	HSET4(sc, SDMMC_CTRL, SDMMC_CTRL_INT_ENABLE);
40424225f53Skettenis 
40524225f53Skettenis 	dwmmc_bus_width(sc, 1);
40624225f53Skettenis 
4073704a61cSkettenis 	/* Start out in non-DMA mode. */
4083704a61cSkettenis 	dwmmc_pio_mode(sc);
4093704a61cSkettenis 
410b9e8b644Skettenis 	sc->sc_dmat = faa->fa_dmat;
411b9e8b644Skettenis 	dwmmc_alloc_descriptors(sc);
412b9e8b644Skettenis 	dwmmc_init_descriptors(sc);
413b9e8b644Skettenis 
414b9e8b644Skettenis 	error = bus_dmamap_create(sc->sc_dmat, MAXPHYS, DWMMC_NDESC,
415b9e8b644Skettenis 	    DWMMC_MAXSEGSZ, 0, BUS_DMA_WAITOK|BUS_DMA_ALLOCNOW, &sc->sc_dmap);
416b9e8b644Skettenis 	if (error) {
417b9e8b644Skettenis 		printf(": can't create DMA map\n");
418b9e8b644Skettenis 		goto unmap;
419b9e8b644Skettenis 	}
420b9e8b644Skettenis 
42124225f53Skettenis 	memset(&saa, 0, sizeof(saa));
42224225f53Skettenis 	saa.saa_busname = "sdmmc";
42324225f53Skettenis 	saa.sct = &dwmmc_chip_functions;
42424225f53Skettenis 	saa.sch = sc;
425b9e8b644Skettenis 	saa.dmat = sc->sc_dmat;
426b9e8b644Skettenis 	saa.dmap = sc->sc_dmap;
4274481f01cSkettenis 	saa.caps |= SMC_CAPS_DMA;
42824225f53Skettenis 
429c61301d7Skettenis 	if (OF_getproplen(sc->sc_node, "cap-mmc-highspeed") == 0)
430c61301d7Skettenis 		saa.caps |= SMC_CAPS_MMC_HIGHSPEED;
431c61301d7Skettenis 	if (OF_getproplen(sc->sc_node, "cap-sd-highspeed") == 0)
432c61301d7Skettenis 		saa.caps |= SMC_CAPS_SD_HIGHSPEED;
433c61301d7Skettenis 
43424225f53Skettenis 	width = OF_getpropint(faa->fa_node, "bus-width", 1);
43524225f53Skettenis 	if (width >= 8)
43624225f53Skettenis 		saa.caps |= SMC_CAPS_8BIT_MODE;
43724225f53Skettenis 	if (width >= 4)
43824225f53Skettenis 		saa.caps |= SMC_CAPS_4BIT_MODE;
43924225f53Skettenis 
44024225f53Skettenis 	sc->sc_sdmmc = config_found(self, &saa, NULL);
44124225f53Skettenis 	return;
44224225f53Skettenis 
44324225f53Skettenis unmap:
44424225f53Skettenis 	bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size);
44524225f53Skettenis }
44624225f53Skettenis 
44724225f53Skettenis int
448b9e8b644Skettenis dwmmc_alloc_descriptors(struct dwmmc_softc *sc)
449b9e8b644Skettenis {
450b9e8b644Skettenis 	int error, rseg;
451b9e8b644Skettenis 
452b9e8b644Skettenis 	/* Allocate descriptor memory */
453b9e8b644Skettenis 	error = bus_dmamem_alloc(sc->sc_dmat, PAGE_SIZE, PAGE_SIZE,
454b9e8b644Skettenis 	    PAGE_SIZE, sc->sc_desc_segs, 1, &rseg,
455b9e8b644Skettenis 	    BUS_DMA_WAITOK | BUS_DMA_ZERO);
456b9e8b644Skettenis 	if (error)
457b9e8b644Skettenis 		return error;
458b9e8b644Skettenis 	error = bus_dmamem_map(sc->sc_dmat, sc->sc_desc_segs, rseg,
459b9e8b644Skettenis 	    PAGE_SIZE, &sc->sc_desc, BUS_DMA_WAITOK | BUS_DMA_COHERENT);
460b9e8b644Skettenis 	if (error) {
461b9e8b644Skettenis 		bus_dmamem_free(sc->sc_dmat, sc->sc_desc_segs, rseg);
462b9e8b644Skettenis 		return error;
463b9e8b644Skettenis 	}
464b9e8b644Skettenis 	error = bus_dmamap_create(sc->sc_dmat, PAGE_SIZE, 1, PAGE_SIZE,
465b9e8b644Skettenis 	    0, BUS_DMA_WAITOK, &sc->sc_desc_map);
466b9e8b644Skettenis 	if (error) {
467b9e8b644Skettenis 		bus_dmamem_unmap(sc->sc_dmat, sc->sc_desc, PAGE_SIZE);
468b9e8b644Skettenis 		bus_dmamem_free(sc->sc_dmat, sc->sc_desc_segs, rseg);
469b9e8b644Skettenis 		return error;
470b9e8b644Skettenis 	}
471b9e8b644Skettenis 	error = bus_dmamap_load(sc->sc_dmat, sc->sc_desc_map,
472b9e8b644Skettenis 	    sc->sc_desc, PAGE_SIZE, NULL, BUS_DMA_WAITOK | BUS_DMA_WRITE);
473b9e8b644Skettenis 	if (error) {
474b9e8b644Skettenis 		bus_dmamap_destroy(sc->sc_dmat, sc->sc_desc_map);
475b9e8b644Skettenis 		bus_dmamem_unmap(sc->sc_dmat, sc->sc_desc, PAGE_SIZE);
476b9e8b644Skettenis 		bus_dmamem_free(sc->sc_dmat, sc->sc_desc_segs, rseg);
477b9e8b644Skettenis 		return error;
478b9e8b644Skettenis 	}
479b9e8b644Skettenis 
480b9e8b644Skettenis 	return 0;
481b9e8b644Skettenis }
482b9e8b644Skettenis 
483b9e8b644Skettenis void
4844df26cf2Skettenis dwmmc_init_descriptors32(struct dwmmc_softc *sc)
485b9e8b644Skettenis {
4864df26cf2Skettenis 	struct dwmmc_desc32 *desc;
487b9e8b644Skettenis 	bus_addr_t addr;
488b9e8b644Skettenis 	int i;
489b9e8b644Skettenis 
490b9e8b644Skettenis 	desc = (void *)sc->sc_desc;
491b9e8b644Skettenis 	addr = sc->sc_desc_map->dm_segs[0].ds_addr;
492b9e8b644Skettenis 	for (i = 0; i < DWMMC_NDESC; i++) {
4934df26cf2Skettenis 		addr += sizeof(struct dwmmc_desc32);
494b9e8b644Skettenis 		desc[i].des[3] = addr;
495b9e8b644Skettenis 	}
496b9e8b644Skettenis 	desc[DWMMC_NDESC - 1].des[3] = sc->sc_desc_map->dm_segs[0].ds_addr;
497b9e8b644Skettenis 	desc[DWMMC_NDESC - 1].des[0] = DES0_ER;
498b9e8b644Skettenis 	bus_dmamap_sync(sc->sc_dmat, sc->sc_desc_map, 0,
499b9e8b644Skettenis 	    PAGE_SIZE, BUS_DMASYNC_PREWRITE);
500b9e8b644Skettenis 
5014df26cf2Skettenis 	HWRITE4(sc, SDMMC_IDSTS32, 0xffffffff);
5024df26cf2Skettenis 	HWRITE4(sc, SDMMC_IDINTEN32,
503b9e8b644Skettenis 	    SDMMC_IDINTEN_NI | SDMMC_IDINTEN_RI | SDMMC_IDINTEN_TI);
504b9e8b644Skettenis 	HWRITE4(sc, SDMMC_DBADDR, sc->sc_desc_map->dm_segs[0].ds_addr);
505b9e8b644Skettenis }
506b9e8b644Skettenis 
5074df26cf2Skettenis void
5084df26cf2Skettenis dwmmc_init_descriptors64(struct dwmmc_softc *sc)
5094df26cf2Skettenis {
5104df26cf2Skettenis 	struct dwmmc_desc64 *desc;
5114df26cf2Skettenis 	bus_addr_t addr;
5124df26cf2Skettenis 	int i;
5134df26cf2Skettenis 
5144df26cf2Skettenis 	desc = (void *)sc->sc_desc;
5154df26cf2Skettenis 	addr = sc->sc_desc_map->dm_segs[0].ds_addr;
5164df26cf2Skettenis 	for (i = 0; i < DWMMC_NDESC; i++) {
5174df26cf2Skettenis 		addr += sizeof(struct dwmmc_desc64);
5184df26cf2Skettenis 		desc[i].des[6] = addr;
5194df26cf2Skettenis 		desc[i].des[7] = (uint64_t)addr >> 32;
5204df26cf2Skettenis 	}
5214df26cf2Skettenis 	desc[DWMMC_NDESC - 1].des[6] = sc->sc_desc_map->dm_segs[0].ds_addr;
5224df26cf2Skettenis 	desc[DWMMC_NDESC - 1].des[7] =
5234df26cf2Skettenis 	    (uint64_t)sc->sc_desc_map->dm_segs[0].ds_addr >> 32;
5244df26cf2Skettenis 	desc[DWMMC_NDESC - 1].des[0] = DES0_ER;
5254df26cf2Skettenis 	bus_dmamap_sync(sc->sc_dmat, sc->sc_desc_map, 0,
5264df26cf2Skettenis 	    PAGE_SIZE, BUS_DMASYNC_PREWRITE);
5274df26cf2Skettenis 
5284df26cf2Skettenis 	HWRITE4(sc, SDMMC_IDSTS64, 0xffffffff);
5294df26cf2Skettenis 	HWRITE4(sc, SDMMC_IDINTEN64,
5304df26cf2Skettenis 	    SDMMC_IDINTEN_NI | SDMMC_IDINTEN_RI | SDMMC_IDINTEN_TI);
5314df26cf2Skettenis 	HWRITE4(sc, SDMMC_DBADDRL, sc->sc_desc_map->dm_segs[0].ds_addr);
5324df26cf2Skettenis 	HWRITE4(sc, SDMMC_DBADDRH,
5334df26cf2Skettenis 	    (uint64_t)sc->sc_desc_map->dm_segs[0].ds_addr >> 32);
5344df26cf2Skettenis }
5354df26cf2Skettenis 
5364df26cf2Skettenis void
5374df26cf2Skettenis dwmmc_init_descriptors(struct dwmmc_softc *sc)
5384df26cf2Skettenis {
5394df26cf2Skettenis 	if (sc->sc_dma64)
5404df26cf2Skettenis 		dwmmc_init_descriptors64(sc);
5414df26cf2Skettenis 	else
5424df26cf2Skettenis 		dwmmc_init_descriptors32(sc);
5434df26cf2Skettenis }
5444df26cf2Skettenis 
545b9e8b644Skettenis int
54624225f53Skettenis dwmmc_intr(void *arg)
54724225f53Skettenis {
548d5957df7Skettenis 	struct dwmmc_softc *sc = arg;
549d5957df7Skettenis 	uint32_t stat;
550d5957df7Skettenis 	int handled = 0;
551d5957df7Skettenis 
5524df26cf2Skettenis 	stat = HREAD4(sc, SDMMC_IDSTS(sc));
553b9e8b644Skettenis 	if (stat) {
5544df26cf2Skettenis 		HWRITE4(sc, SDMMC_IDSTS(sc), stat);
555b9e8b644Skettenis 		sc->sc_idsts |= stat;
556b9e8b644Skettenis 		wakeup(&sc->sc_idsts);
557b9e8b644Skettenis 		handled = 1;
558b9e8b644Skettenis 	}
559b9e8b644Skettenis 
560d5957df7Skettenis 	stat = HREAD4(sc, SDMMC_MINTSTS);
561d5957df7Skettenis 	if (stat & SDMMC_RINTSTS_SDIO) {
5628fc6eeeeSpatrick 		HWRITE4(sc, SDMMC_RINTSTS, SDMMC_RINTSTS_SDIO);
563d5957df7Skettenis 		HCLR4(sc, SDMMC_INTMASK, SDMMC_RINTSTS_SDIO);
564d5957df7Skettenis 		sdmmc_card_intr(sc->sc_sdmmc);
565d5957df7Skettenis 		handled = 1;
566d5957df7Skettenis 	}
567d5957df7Skettenis 
568d5957df7Skettenis 	return handled;
569d5957df7Skettenis }
570d5957df7Skettenis 
571d5957df7Skettenis void
572d5957df7Skettenis dwmmc_card_intr_mask(sdmmc_chipset_handle_t sch, int enable)
573d5957df7Skettenis {
574d5957df7Skettenis 	struct dwmmc_softc *sc = sch;
575d5957df7Skettenis 
576d5957df7Skettenis 	if (enable)
577d5957df7Skettenis 		HSET4(sc, SDMMC_INTMASK, SDMMC_RINTSTS_SDIO);
578d5957df7Skettenis 	else
579d5957df7Skettenis 		HCLR4(sc, SDMMC_INTMASK, SDMMC_RINTSTS_SDIO);
580d5957df7Skettenis }
581d5957df7Skettenis 
582d5957df7Skettenis void
583d5957df7Skettenis dwmmc_card_intr_ack(sdmmc_chipset_handle_t sch)
584d5957df7Skettenis {
585d5957df7Skettenis 	struct dwmmc_softc *sc = sch;
586d5957df7Skettenis 
587d5957df7Skettenis 	HSET4(sc, SDMMC_INTMASK, SDMMC_RINTSTS_SDIO);
58824225f53Skettenis }
58924225f53Skettenis 
59024225f53Skettenis int
59124225f53Skettenis dwmmc_host_reset(sdmmc_chipset_handle_t sch)
59224225f53Skettenis {
59324225f53Skettenis 	printf("%s\n", __func__);
59424225f53Skettenis 	return 0;
59524225f53Skettenis }
59624225f53Skettenis 
59724225f53Skettenis uint32_t
59824225f53Skettenis dwmmc_host_ocr(sdmmc_chipset_handle_t sch)
59924225f53Skettenis {
60024225f53Skettenis 	return MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V;
60124225f53Skettenis }
60224225f53Skettenis 
60324225f53Skettenis int
60424225f53Skettenis dwmmc_host_maxblklen(sdmmc_chipset_handle_t sch)
60524225f53Skettenis {
60624225f53Skettenis 	return 512;
60724225f53Skettenis }
60824225f53Skettenis 
60924225f53Skettenis int
61024225f53Skettenis dwmmc_card_detect(sdmmc_chipset_handle_t sch)
61124225f53Skettenis {
61224225f53Skettenis 	struct dwmmc_softc *sc = sch;
61324225f53Skettenis 	uint32_t cdetect;
61424225f53Skettenis 
615721f294eSkettenis 	/* XXX treat broken-cd as non-removable */
616721f294eSkettenis 	if (OF_getproplen(sc->sc_node, "non-removable") == 0 ||
617721f294eSkettenis 	    OF_getproplen(sc->sc_node, "broken-cd") == 0)
618f8fb77f5Skettenis 		return 1;
619f8fb77f5Skettenis 
620f8fb77f5Skettenis 	if (sc->sc_gpio[0]) {
621f8fb77f5Skettenis 		int inverted, val;
622f8fb77f5Skettenis 
623f8fb77f5Skettenis 		val = gpio_controller_get_pin(sc->sc_gpio);
624f8fb77f5Skettenis 
625f8fb77f5Skettenis 		inverted = (OF_getproplen(sc->sc_node, "cd-inverted") == 0);
626f8fb77f5Skettenis 		return inverted ? !val : val;
627f8fb77f5Skettenis 	}
628f8fb77f5Skettenis 
62924225f53Skettenis 	cdetect = HREAD4(sc, SDMMC_CDETECT);
63024225f53Skettenis 	return !(cdetect & SDMMC_CDETECT_CARD_DETECT_0);
63124225f53Skettenis }
63224225f53Skettenis 
63324225f53Skettenis int
63424225f53Skettenis dwmmc_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr)
63524225f53Skettenis {
63624225f53Skettenis 	struct dwmmc_softc *sc = sch;
637d5957df7Skettenis 	uint32_t vdd = 0;
638d5957df7Skettenis 
639d5957df7Skettenis 	if (ISSET(ocr, MMC_OCR_3_2V_3_3V|MMC_OCR_3_3V_3_4V))
640d5957df7Skettenis 		vdd = 3300000;
641d5957df7Skettenis 
642d5957df7Skettenis 	if (sc->sc_vdd == 0 && vdd > 0)
643d5957df7Skettenis 		dwmmc_pwrseq_pre(sc->sc_pwrseq);
64424225f53Skettenis 
64524225f53Skettenis 	if (ISSET(ocr, MMC_OCR_3_2V_3_3V|MMC_OCR_3_3V_3_4V))
64624225f53Skettenis 		HSET4(sc, SDMMC_PWREN, 1);
64724225f53Skettenis 	else
64824225f53Skettenis 		HCLR4(sc, SDMMC_PWREN, 0);
64924225f53Skettenis 
650d5957df7Skettenis 	if (sc->sc_vdd == 0 && vdd > 0)
651d5957df7Skettenis 		dwmmc_pwrseq_post(sc->sc_pwrseq);
652d5957df7Skettenis 
653d5957df7Skettenis 	sc->sc_vdd = vdd;
65424225f53Skettenis 	return 0;
65524225f53Skettenis }
65624225f53Skettenis 
65724225f53Skettenis int
65824225f53Skettenis dwmmc_bus_clock(sdmmc_chipset_handle_t sch, int freq, int timing)
65924225f53Skettenis {
66024225f53Skettenis 	struct dwmmc_softc *sc = sch;
66124225f53Skettenis 	int div = 0, timeout;
662d5957df7Skettenis 	uint32_t clkena;
66324225f53Skettenis 
66424225f53Skettenis 	HWRITE4(sc, SDMMC_CLKENA, 0);
66524225f53Skettenis 	HWRITE4(sc, SDMMC_CLKSRC, 0);
66624225f53Skettenis 
66724225f53Skettenis 	if (freq == 0)
66824225f53Skettenis 		return 0;
66924225f53Skettenis 
67024225f53Skettenis 	if (sc->sc_clkbase / 1000 > freq) {
67124225f53Skettenis 		for (div = 1; div < 256; div++)
67224225f53Skettenis 			if (sc->sc_clkbase / (2 * 1000 * div) <= freq)
67324225f53Skettenis 				break;
67424225f53Skettenis 	}
67524225f53Skettenis 	HWRITE4(sc, SDMMC_CLKDIV, div);
67624225f53Skettenis 
67724225f53Skettenis 	/* Update clock. */
67824225f53Skettenis 	HWRITE4(sc, SDMMC_CMD, SDMMC_CMD_START_CMD |
67924225f53Skettenis 	    SDMMC_CMD_WAIT_PRVDATA_COMPLETE |
68024225f53Skettenis 	    SDMMC_CMD_UPDATE_CLOCK_REGISTERS_ONLY);
68124225f53Skettenis 	for (timeout = 1000; timeout > 0; timeout--) {
68224225f53Skettenis 		if ((HREAD4(sc, SDMMC_CMD) & SDMMC_CMD_START_CMD) == 0)
68324225f53Skettenis 			break;
68424225f53Skettenis 	}
68524225f53Skettenis 	if (timeout == 0) {
68624225f53Skettenis 		printf("%s: timeout\n", __func__);
68724225f53Skettenis 		return ETIMEDOUT;
68824225f53Skettenis 	}
68924225f53Skettenis 
690d5957df7Skettenis 	/* Enable clock; low power mode only for memory mode. */
691d5957df7Skettenis 	clkena = SDMMC_CLKENA_CCLK_ENABLE;
692d5957df7Skettenis 	if (!sc->sc_sdio_irq)
693d5957df7Skettenis 		clkena |= SDMMC_CLKENA_CCLK_LOW_POWER;
694d5957df7Skettenis 	HWRITE4(sc, SDMMC_CLKENA, clkena);
69524225f53Skettenis 
69624225f53Skettenis 	/* Update clock again. */
69724225f53Skettenis 	HWRITE4(sc, SDMMC_CMD, SDMMC_CMD_START_CMD |
69824225f53Skettenis 	    SDMMC_CMD_WAIT_PRVDATA_COMPLETE |
69924225f53Skettenis 	    SDMMC_CMD_UPDATE_CLOCK_REGISTERS_ONLY);
70024225f53Skettenis 	for (timeout = 1000; timeout > 0; timeout--) {
70124225f53Skettenis 		if ((HREAD4(sc, SDMMC_CMD) & SDMMC_CMD_START_CMD) == 0)
70224225f53Skettenis 			break;
70324225f53Skettenis 	}
70424225f53Skettenis 	if (timeout == 0) {
70524225f53Skettenis 		printf("%s: timeout\n", __func__);
70624225f53Skettenis 		return ETIMEDOUT;
70724225f53Skettenis 	}
70824225f53Skettenis 
70924225f53Skettenis 	delay(1000000);
71024225f53Skettenis 
71124225f53Skettenis 	return 0;
71224225f53Skettenis }
71324225f53Skettenis 
71424225f53Skettenis int
71524225f53Skettenis dwmmc_bus_width(sdmmc_chipset_handle_t sch, int width)
71624225f53Skettenis {
71724225f53Skettenis 	struct dwmmc_softc *sc = sch;
71824225f53Skettenis 
71924225f53Skettenis 	switch (width) {
72024225f53Skettenis 	case 1:
72124225f53Skettenis 		HCLR4(sc, SDMMC_CTYPE, SDMMC_CTYPE_8BIT|SDMMC_CTYPE_4BIT);
72224225f53Skettenis 		break;
72324225f53Skettenis 	case 4:
72424225f53Skettenis 		HSET4(sc, SDMMC_CTYPE, SDMMC_CTYPE_4BIT);
72524225f53Skettenis 		HCLR4(sc, SDMMC_CTYPE, SDMMC_CTYPE_8BIT);
72624225f53Skettenis 		break;
72724225f53Skettenis 	case 8:
72824225f53Skettenis 		HSET4(sc, SDMMC_CTYPE, SDMMC_CTYPE_8BIT);
72924225f53Skettenis 		break;
73024225f53Skettenis 	default:
73124225f53Skettenis 		return EINVAL;
73224225f53Skettenis 	}
73324225f53Skettenis 
73424225f53Skettenis 	return 0;
73524225f53Skettenis }
73624225f53Skettenis 
7373704a61cSkettenis void
7383704a61cSkettenis dwmmc_pio_mode(struct dwmmc_softc *sc)
7393704a61cSkettenis {
7403704a61cSkettenis 	/* Disable DMA. */
7413704a61cSkettenis 	HCLR4(sc, SDMMC_CTRL, SDMMC_CTRL_USE_INTERNAL_DMAC |
7423704a61cSkettenis 	    SDMMC_CTRL_DMA_ENABLE);
7433704a61cSkettenis 
7443704a61cSkettenis 	/* Set FIFO thresholds. */
7453704a61cSkettenis 	HWRITE4(sc, SDMMC_FIFOTH, 2 << SDMMC_FIFOTH_MSIZE_SHIFT |
7463704a61cSkettenis 	    (sc->sc_fifo_depth / 2 - 1) << SDMMC_FIFOTH_RXWM_SHIFT |
7473704a61cSkettenis 	    (sc->sc_fifo_depth / 2) << SDMMC_FIFOTH_TXWM_SHIFT);
7483704a61cSkettenis 
7493704a61cSkettenis 	sc->sc_dmamode = 0;
7503704a61cSkettenis 	sc->sc_blklen = 0;
7513704a61cSkettenis }
7523704a61cSkettenis 
7533704a61cSkettenis void
7543704a61cSkettenis dwmmc_dma_mode(struct dwmmc_softc *sc)
7553704a61cSkettenis {
7563704a61cSkettenis 	int timeout;
7573704a61cSkettenis 
7583704a61cSkettenis 	/* Reset DMA. */
7593704a61cSkettenis 	HSET4(sc, SDMMC_BMOD, SDMMC_BMOD_SWR);
7603704a61cSkettenis 	for (timeout = 1000; timeout > 0; timeout--) {
7613704a61cSkettenis 		if ((HREAD4(sc, SDMMC_BMOD) & SDMMC_BMOD_SWR) == 0)
7623704a61cSkettenis 			break;
7633704a61cSkettenis 		delay(100);
7643704a61cSkettenis 	}
7653704a61cSkettenis 	if (timeout == 0)
7663704a61cSkettenis 		printf("%s: DMA reset failed\n", sc->sc_dev.dv_xname);
7673704a61cSkettenis 
7683704a61cSkettenis 	/* Enable DMA. */
7693704a61cSkettenis 	HSET4(sc, SDMMC_CTRL, SDMMC_CTRL_USE_INTERNAL_DMAC |
7703704a61cSkettenis 	    SDMMC_CTRL_DMA_ENABLE);
7713704a61cSkettenis 	HSET4(sc, SDMMC_BMOD, SDMMC_BMOD_FB | SDMMC_BMOD_DE);
7723704a61cSkettenis 
7733704a61cSkettenis 	sc->sc_dmamode = 1;
7743704a61cSkettenis }
7753704a61cSkettenis 
7763704a61cSkettenis void
7774df26cf2Skettenis dwmmc_dma_setup32(struct dwmmc_softc *sc, struct sdmmc_command *cmd)
778b9e8b644Skettenis {
7794df26cf2Skettenis 	struct dwmmc_desc32 *desc = (void *)sc->sc_desc;
780b9e8b644Skettenis 	uint32_t flags;
781b9e8b644Skettenis 	int seg;
782b9e8b644Skettenis 
783b9e8b644Skettenis 	flags = DES0_OWN | DES0_FS | DES0_CH | DES0_DIC;
784b9e8b644Skettenis 	for (seg = 0; seg < cmd->c_dmamap->dm_nsegs; seg++) {
785b9e8b644Skettenis 		bus_addr_t addr = cmd->c_dmamap->dm_segs[seg].ds_addr;
786b9e8b644Skettenis 		bus_size_t len = cmd->c_dmamap->dm_segs[seg].ds_len;
787b9e8b644Skettenis 
788b9e8b644Skettenis 		if (seg == cmd->c_dmamap->dm_nsegs - 1) {
789b9e8b644Skettenis 			flags |= DES0_LD;
790b9e8b644Skettenis 			flags &= ~DES0_DIC;
791b9e8b644Skettenis 		}
792b9e8b644Skettenis 
793b9e8b644Skettenis 		KASSERT((desc[seg].des[0] & DES0_OWN) == 0);
794b9e8b644Skettenis 		desc[seg].des[0] = flags;
795b9e8b644Skettenis 		desc[seg].des[1] = DES1_BS1(len);
796b9e8b644Skettenis 		desc[seg].des[2] = addr;
797b9e8b644Skettenis 		flags &= ~DES0_FS;
798b9e8b644Skettenis 	}
7994df26cf2Skettenis }
8004df26cf2Skettenis 
8014df26cf2Skettenis void
8024df26cf2Skettenis dwmmc_dma_setup64(struct dwmmc_softc *sc, struct sdmmc_command *cmd)
8034df26cf2Skettenis {
8044df26cf2Skettenis 	struct dwmmc_desc64 *desc = (void *)sc->sc_desc;
8054df26cf2Skettenis 	uint32_t flags;
8064df26cf2Skettenis 	int seg;
8074df26cf2Skettenis 
8084df26cf2Skettenis 	flags = DES0_OWN | DES0_FS | DES0_CH | DES0_DIC;
8094df26cf2Skettenis 	for (seg = 0; seg < cmd->c_dmamap->dm_nsegs; seg++) {
8104df26cf2Skettenis 		bus_addr_t addr = cmd->c_dmamap->dm_segs[seg].ds_addr;
8114df26cf2Skettenis 		bus_size_t len = cmd->c_dmamap->dm_segs[seg].ds_len;
8124df26cf2Skettenis 
8134df26cf2Skettenis 		if (seg == cmd->c_dmamap->dm_nsegs - 1) {
8144df26cf2Skettenis 			flags |= DES0_LD;
8154df26cf2Skettenis 			flags &= ~DES0_DIC;
8164df26cf2Skettenis 		}
8174df26cf2Skettenis 
8184df26cf2Skettenis 		KASSERT((desc[seg].des[0] & DES0_OWN) == 0);
8194df26cf2Skettenis 		desc[seg].des[0] = flags;
8204df26cf2Skettenis 		desc[seg].des[2] = DES2_BS1(len);
8214df26cf2Skettenis 		desc[seg].des[4] = addr;
8224df26cf2Skettenis 		desc[seg].des[5] = (uint64_t)addr >> 32;
8234df26cf2Skettenis 		flags &= ~DES0_FS;
8244df26cf2Skettenis 	}
8254df26cf2Skettenis }
8264df26cf2Skettenis 
8274df26cf2Skettenis void
8284df26cf2Skettenis dwmmc_dma_setup(struct dwmmc_softc *sc, struct sdmmc_command *cmd)
8294df26cf2Skettenis {
8304df26cf2Skettenis 	if (sc->sc_dma64)
8314df26cf2Skettenis 		dwmmc_dma_setup64(sc, cmd);
8324df26cf2Skettenis 	else
8334df26cf2Skettenis 		dwmmc_dma_setup32(sc, cmd);
834b9e8b644Skettenis 
835b9e8b644Skettenis 	bus_dmamap_sync(sc->sc_dmat, sc->sc_desc_map, 0, PAGE_SIZE,
836b9e8b644Skettenis 	    BUS_DMASYNC_PREWRITE);
837b9e8b644Skettenis 
838b9e8b644Skettenis 	sc->sc_idsts = 0;
8393704a61cSkettenis }
8403704a61cSkettenis 
8413704a61cSkettenis void
8423704a61cSkettenis dwmmc_dma_reset(struct dwmmc_softc *sc, struct sdmmc_command *cmd)
8433704a61cSkettenis {
8443704a61cSkettenis 	int timeout;
8453704a61cSkettenis 
8463704a61cSkettenis 	/* Reset DMA unit. */
8473704a61cSkettenis 	HSET4(sc, SDMMC_BMOD, SDMMC_BMOD_SWR);
8483704a61cSkettenis 	for (timeout = 1000; timeout > 0; timeout--) {
8493704a61cSkettenis 		if ((HREAD4(sc, SDMMC_BMOD) &
8503704a61cSkettenis 		    SDMMC_BMOD_SWR) == 0)
8513704a61cSkettenis 			break;
8523704a61cSkettenis 		delay(100);
8533704a61cSkettenis 	}
8543704a61cSkettenis 
8553704a61cSkettenis 	dwmmc_pio_mode(sc);
8563704a61cSkettenis 
8573704a61cSkettenis 	/* Clear descriptors that were in use, */
8583704a61cSkettenis 	memset(sc->sc_desc, 0, PAGE_SIZE);
8593704a61cSkettenis 	dwmmc_init_descriptors(sc);
8603704a61cSkettenis }
8613704a61cSkettenis 
8623704a61cSkettenis void
8633704a61cSkettenis dwmmc_fifo_setup(struct dwmmc_softc *sc, int blklen)
8643704a61cSkettenis {
8653704a61cSkettenis 	int blkdepth = blklen / sc->sc_fifo_width;
8663704a61cSkettenis 	int txwm = sc->sc_fifo_depth / 2;
8673704a61cSkettenis 	int rxwm, msize = 0;
8683704a61cSkettenis 
8693704a61cSkettenis 	/*
8703704a61cSkettenis 	 * Bursting is only possible of the block size is a multiple of
8713704a61cSkettenis 	 * the FIFO width.
8723704a61cSkettenis 	 */
8733704a61cSkettenis 	if (blklen % sc->sc_fifo_width == 0)
8743704a61cSkettenis 		msize = 7;
8753704a61cSkettenis 
8763704a61cSkettenis 	/* Magic to calculate the maximum burst size. */
8773704a61cSkettenis 	while (msize > 0) {
8783704a61cSkettenis 		if (blkdepth % (2 << msize) == 0 &&
8793704a61cSkettenis 		    (sc->sc_fifo_depth - txwm) % (2 << msize) == 0)
8803704a61cSkettenis 			break;
8813704a61cSkettenis 		msize--;
8823704a61cSkettenis 	}
8833704a61cSkettenis 	rxwm = (2 << msize) - 1;
8843704a61cSkettenis 
8853704a61cSkettenis 	HWRITE4(sc, SDMMC_FIFOTH,
8863704a61cSkettenis 	    msize << SDMMC_FIFOTH_MSIZE_SHIFT |
8873704a61cSkettenis 	    rxwm << SDMMC_FIFOTH_RXWM_SHIFT |
8883704a61cSkettenis 	    txwm << SDMMC_FIFOTH_TXWM_SHIFT);
8893704a61cSkettenis 
8903704a61cSkettenis 	sc->sc_blklen = blklen;
891b9e8b644Skettenis }
892b9e8b644Skettenis 
89324225f53Skettenis void
89424225f53Skettenis dwmmc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd)
89524225f53Skettenis {
89624225f53Skettenis 	struct dwmmc_softc *sc = sch;
89724225f53Skettenis 	uint32_t cmdval = SDMMC_CMD_START_CMD | SDMMC_CMD_USE_HOLD_REG;
89824225f53Skettenis 	uint32_t status;
899b9e8b644Skettenis 	int error, timeout;
90024225f53Skettenis 	int s;
90124225f53Skettenis 
90224225f53Skettenis #if 0
90324225f53Skettenis 	printf("%s: cmd %d arg 0x%x flags 0x%x data %p datalen %d blklen %d\n",
90424225f53Skettenis 	    sc->sc_dev.dv_xname, cmd->c_opcode, cmd->c_arg, cmd->c_flags,
90524225f53Skettenis 	    cmd->c_data, cmd->c_datalen, cmd->c_blklen);
90624225f53Skettenis #endif
90724225f53Skettenis 
90824225f53Skettenis 	s = splbio();
90924225f53Skettenis 
910e036655fSkettenis 	for (timeout = 10000; timeout > 0; timeout--) {
91124225f53Skettenis 		status = HREAD4(sc, SDMMC_STATUS);
91224225f53Skettenis 		if ((status & SDMMC_STATUS_DATA_BUSY) == 0)
91324225f53Skettenis 			break;
91424225f53Skettenis 		delay(100);
91524225f53Skettenis 	}
91624225f53Skettenis 	if (timeout == 0) {
91724225f53Skettenis 		printf("%s: timeout on data busy\n", sc->sc_dev.dv_xname);
91824225f53Skettenis 		goto done;
91924225f53Skettenis 	}
92024225f53Skettenis 
92124225f53Skettenis 	if (cmd->c_opcode == MMC_STOP_TRANSMISSION)
92224225f53Skettenis 		cmdval |= SDMMC_CMD_STOP_ABORT_CMD;
92324225f53Skettenis 	else if (cmd->c_opcode != MMC_SEND_STATUS)
92424225f53Skettenis 		cmdval |= SDMMC_CMD_WAIT_PRVDATA_COMPLETE;
92524225f53Skettenis 
92624225f53Skettenis 	if (cmd->c_opcode == 0)
92724225f53Skettenis 		cmdval |= SDMMC_CMD_SEND_INITIALIZATION;
92824225f53Skettenis 	if (cmd->c_flags & SCF_RSP_PRESENT)
92924225f53Skettenis 		cmdval |= SDMMC_CMD_RESPONSE_EXPECT;
93024225f53Skettenis 	if (cmd->c_flags & SCF_RSP_136)
93124225f53Skettenis 		cmdval |= SDMMC_CMD_RESPONSE_LENGTH;
93224225f53Skettenis 	if (cmd->c_flags & SCF_RSP_CRC)
93324225f53Skettenis 		cmdval |= SDMMC_CMD_CHECK_REPONSE_CRC;
93424225f53Skettenis 
93524225f53Skettenis 	if (cmd->c_datalen > 0) {
93624225f53Skettenis 		HWRITE4(sc, SDMMC_TMOUT, 0xffffffff);
93724225f53Skettenis 		HWRITE4(sc, SDMMC_BYTCNT, cmd->c_datalen);
93824225f53Skettenis 		HWRITE4(sc, SDMMC_BLKSIZ, cmd->c_blklen);
93924225f53Skettenis 
940d5957df7Skettenis 		if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
941d5957df7Skettenis 			/* Set card read threshold to the size of a block. */
942d5957df7Skettenis 			HWRITE4(sc, SDMMC_CARDTHRCTL,
943d5957df7Skettenis 			    cmd->c_blklen << SDMMC_CARDTHRCTL_RDTHR_SHIFT |
944d5957df7Skettenis 			    SDMMC_CARDTHRCTL_RDTHREN);
945d5957df7Skettenis 		}
946d5957df7Skettenis 
94724225f53Skettenis 		cmdval |= SDMMC_CMD_DATA_EXPECTED;
94824225f53Skettenis 		if (!ISSET(cmd->c_flags, SCF_CMD_READ))
94924225f53Skettenis 			cmdval |= SDMMC_CMD_WR;
9503704a61cSkettenis 		if (cmd->c_datalen > cmd->c_blklen &&
9513704a61cSkettenis 		    cmd->c_opcode != SD_IO_RW_EXTENDED)
9523704a61cSkettenis 			cmdval |= SDMMC_CMD_SEND_AUTO_STOP;
95324225f53Skettenis 	}
95424225f53Skettenis 
955b9e8b644Skettenis 	if (cmd->c_datalen > 0 && !cmd->c_dmamap) {
956b9e8b644Skettenis 		HSET4(sc, SDMMC_CTRL, SDMMC_CTRL_FIFO_RESET);
957b9e8b644Skettenis 		for (timeout = 1000; timeout > 0; timeout--) {
958b9e8b644Skettenis 			if ((HREAD4(sc, SDMMC_CTRL) &
959b9e8b644Skettenis 			    SDMMC_CTRL_FIFO_RESET) == 0)
960b9e8b644Skettenis 				break;
961b9e8b644Skettenis 			delay(100);
962b9e8b644Skettenis 		}
963b9e8b644Skettenis 		if (timeout == 0)
964b9e8b644Skettenis 			printf("%s: FIFO reset failed\n", sc->sc_dev.dv_xname);
965b9e8b644Skettenis 
9663704a61cSkettenis 		/* Disable DMA if we are switching back to PIO. */
9673704a61cSkettenis 		if (sc->sc_dmamode)
9683704a61cSkettenis 			dwmmc_pio_mode(sc);
969b9e8b644Skettenis 	}
970b9e8b644Skettenis 
971b9e8b644Skettenis 	if (cmd->c_datalen > 0 && cmd->c_dmamap) {
972b9e8b644Skettenis 		dwmmc_dma_setup(sc, cmd);
9733704a61cSkettenis 		HWRITE4(sc, SDMMC_PLDMND, 1);
974b9e8b644Skettenis 
9754b1a56afSjsg 		/* Enable DMA if we did PIO before. */
9763704a61cSkettenis 		if (!sc->sc_dmamode)
9773704a61cSkettenis 			dwmmc_dma_mode(sc);
978b9e8b644Skettenis 
9793704a61cSkettenis 		/* Reconfigure FIFO thresholds if block size changed. */
9803704a61cSkettenis 		if (cmd->c_blklen != sc->sc_blklen)
9813704a61cSkettenis 			dwmmc_fifo_setup(sc, cmd->c_blklen);
982b9e8b644Skettenis 	}
983b9e8b644Skettenis 
984d5957df7Skettenis 	HWRITE4(sc, SDMMC_RINTSTS, ~SDMMC_RINTSTS_SDIO);
98524225f53Skettenis 
98624225f53Skettenis 	HWRITE4(sc, SDMMC_CMDARG, cmd->c_arg);
98724225f53Skettenis 	HWRITE4(sc, SDMMC_CMD, cmdval | cmd->c_opcode);
98824225f53Skettenis 
98924225f53Skettenis 	for (timeout = 1000; timeout > 0; timeout--) {
99024225f53Skettenis 		status = HREAD4(sc, SDMMC_RINTSTS);
99124225f53Skettenis 		if (status & SDMMC_RINTSTS_CD)
99224225f53Skettenis 			break;
99324225f53Skettenis 		delay(100);
99424225f53Skettenis 	}
99524225f53Skettenis 	if (timeout == 0 || status & SDMMC_RINTSTS_RTO) {
99624225f53Skettenis 		cmd->c_error = ETIMEDOUT;
9973704a61cSkettenis 		dwmmc_dma_reset(sc, cmd);
99824225f53Skettenis 		goto done;
99924225f53Skettenis 	}
100024225f53Skettenis 
100124225f53Skettenis 	if (cmd->c_flags & SCF_RSP_PRESENT) {
100224225f53Skettenis 		if (cmd->c_flags & SCF_RSP_136) {
100324225f53Skettenis 			cmd->c_resp[0] = HREAD4(sc, SDMMC_RESP0);
100424225f53Skettenis 			cmd->c_resp[1] = HREAD4(sc, SDMMC_RESP1);
100524225f53Skettenis 			cmd->c_resp[2] = HREAD4(sc, SDMMC_RESP2);
100624225f53Skettenis 			cmd->c_resp[3] = HREAD4(sc, SDMMC_RESP3);
100724225f53Skettenis 			if (cmd->c_flags & SCF_RSP_CRC) {
100824225f53Skettenis 				cmd->c_resp[0] = (cmd->c_resp[0] >> 8) |
100924225f53Skettenis 				    (cmd->c_resp[1] << 24);
101024225f53Skettenis 				cmd->c_resp[1] = (cmd->c_resp[1] >> 8) |
101124225f53Skettenis 				    (cmd->c_resp[2] << 24);
101224225f53Skettenis 				cmd->c_resp[2] = (cmd->c_resp[2] >> 8) |
101324225f53Skettenis 				    (cmd->c_resp[3] << 24);
101424225f53Skettenis 				cmd->c_resp[3] = (cmd->c_resp[3] >> 8);
101524225f53Skettenis 			}
101624225f53Skettenis 		} else {
101724225f53Skettenis 			cmd->c_resp[0] = HREAD4(sc, SDMMC_RESP0);
101824225f53Skettenis 		}
101924225f53Skettenis 	}
102024225f53Skettenis 
1021b9e8b644Skettenis 	if (cmd->c_datalen > 0 && !cmd->c_dmamap)
102224225f53Skettenis 		dwmmc_transfer_data(sc, cmd);
102324225f53Skettenis 
1024b9e8b644Skettenis 	if (cmd->c_datalen > 0 && cmd->c_dmamap) {
1025b9e8b644Skettenis 		while (sc->sc_idsts == 0) {
10268544fed6Smpi 			error = tsleep_nsec(&sc->sc_idsts, PWAIT, "idsts",
10278544fed6Smpi 			    SEC_TO_NSEC(1));
1028b9e8b644Skettenis 			if (error) {
1029b9e8b644Skettenis 				cmd->c_error = error;
10303704a61cSkettenis 				dwmmc_dma_reset(sc, cmd);
1031b9e8b644Skettenis 				goto done;
1032b9e8b644Skettenis 			}
1033b9e8b644Skettenis 		}
1034b9e8b644Skettenis 
1035b9e8b644Skettenis 		for (timeout = 10000; timeout > 0; timeout--) {
1036b9e8b644Skettenis 			status = HREAD4(sc, SDMMC_RINTSTS);
1037b9e8b644Skettenis 			if (status & SDMMC_RINTSTS_DTO)
1038b9e8b644Skettenis 				break;
1039b9e8b644Skettenis 			delay(100);
1040b9e8b644Skettenis 		}
1041b9e8b644Skettenis 		if (timeout == 0) {
1042b9e8b644Skettenis 			cmd->c_error = ETIMEDOUT;
10433704a61cSkettenis 			dwmmc_dma_reset(sc, cmd);
1044b9e8b644Skettenis 			goto done;
1045b9e8b644Skettenis 		}
1046b9e8b644Skettenis 	}
1047b9e8b644Skettenis 
104824225f53Skettenis 	if (cmdval & SDMMC_CMD_SEND_AUTO_STOP) {
104924225f53Skettenis 		for (timeout = 10000; timeout > 0; timeout--) {
105024225f53Skettenis 			status = HREAD4(sc, SDMMC_RINTSTS);
10511f08c36fSkettenis 			if (status & SDMMC_RINTSTS_ACD)
105224225f53Skettenis 				break;
105324225f53Skettenis 			delay(10);
105424225f53Skettenis 		}
105524225f53Skettenis 		if (timeout == 0) {
105624225f53Skettenis 			cmd->c_error = ETIMEDOUT;
10573704a61cSkettenis 			dwmmc_dma_reset(sc, cmd);
105824225f53Skettenis 			goto done;
105924225f53Skettenis 		}
106024225f53Skettenis 	}
106124225f53Skettenis 
106224225f53Skettenis done:
106324225f53Skettenis 	cmd->c_flags |= SCF_ITSDONE;
106424225f53Skettenis #if 0
106524225f53Skettenis 	printf("%s: err %d rintsts 0x%x\n", sc->sc_dev.dv_xname, cmd->c_error,
106624225f53Skettenis 	    HREAD4(sc, SDMMC_RINTSTS));
106724225f53Skettenis #endif
106824225f53Skettenis 	splx(s);
106924225f53Skettenis }
107024225f53Skettenis 
107124225f53Skettenis void
107224225f53Skettenis dwmmc_transfer_data(struct dwmmc_softc *sc, struct sdmmc_command *cmd)
107324225f53Skettenis {
107424225f53Skettenis 	int datalen = cmd->c_datalen;
107524225f53Skettenis 	u_char *datap = cmd->c_data;
107624225f53Skettenis 	uint32_t status;
107724225f53Skettenis 	int count, timeout;
107870864666Skettenis 	int fifodr = SDMMC_RINTSTS_DTO | SDMMC_RINTSTS_HTO;
107924225f53Skettenis 
108024225f53Skettenis 	if (ISSET(cmd->c_flags, SCF_CMD_READ))
108124225f53Skettenis 		fifodr |= SDMMC_RINTSTS_RXDR;
108224225f53Skettenis 	else
108324225f53Skettenis 		fifodr |= SDMMC_RINTSTS_TXDR;
108424225f53Skettenis 
108524225f53Skettenis 	while (datalen > 0) {
108624225f53Skettenis 		status = HREAD4(sc, SDMMC_RINTSTS);
108724225f53Skettenis 		if (status & SDMMC_RINTSTS_DATA_ERR) {
108824225f53Skettenis 			cmd->c_error = EIO;
108924225f53Skettenis 			return;
109024225f53Skettenis 		}
109170864666Skettenis 		if (status & SDMMC_RINTSTS_DRTO) {
109224225f53Skettenis 			cmd->c_error = ETIMEDOUT;
109324225f53Skettenis 			return;
109424225f53Skettenis 		}
109524225f53Skettenis 
109624225f53Skettenis 		for (timeout = 10000; timeout > 0; timeout--) {
109724225f53Skettenis 			status = HREAD4(sc, SDMMC_RINTSTS);
109824225f53Skettenis 			if (status & fifodr)
109924225f53Skettenis 				break;
110024225f53Skettenis 			delay(100);
110124225f53Skettenis 		}
110224225f53Skettenis 		if (timeout == 0) {
110324225f53Skettenis 			cmd->c_error = ETIMEDOUT;
110424225f53Skettenis 			return;
110524225f53Skettenis 		}
110624225f53Skettenis 
110724225f53Skettenis 		count = SDMMC_STATUS_FIFO_COUNT(HREAD4(sc, SDMMC_STATUS));
110824225f53Skettenis 		if (!ISSET(cmd->c_flags, SCF_CMD_READ))
110924225f53Skettenis 		    count = sc->sc_fifo_depth - count;
111024225f53Skettenis 
111170864666Skettenis 		count = MIN(datalen, count * sc->sc_fifo_width);
111224225f53Skettenis 		if (ISSET(cmd->c_flags, SCF_CMD_READ))
111370864666Skettenis 			sc->sc_read_data(sc, datap, count);
111424225f53Skettenis 		else
111570864666Skettenis 			sc->sc_write_data(sc, datap, count);
111624225f53Skettenis 
111724225f53Skettenis 		datap += count;
111824225f53Skettenis 		datalen -= count;
111924225f53Skettenis 	}
112024225f53Skettenis 
1121e036655fSkettenis 	for (timeout = 10000; timeout > 0; timeout--) {
112224225f53Skettenis 		status = HREAD4(sc, SDMMC_RINTSTS);
112324225f53Skettenis 		if (status & SDMMC_RINTSTS_DTO)
112424225f53Skettenis 			break;
112524225f53Skettenis 		delay(100);
112624225f53Skettenis 	}
112724225f53Skettenis 	if (timeout == 0)
112824225f53Skettenis 		cmd->c_error = ETIMEDOUT;
112924225f53Skettenis }
113024225f53Skettenis 
113124225f53Skettenis void
113270864666Skettenis dwmmc_read_data32(struct dwmmc_softc *sc, u_char *datap, int datalen)
113324225f53Skettenis {
113424225f53Skettenis 	while (datalen > 3) {
113524225f53Skettenis 		*(uint32_t *)datap = HREAD4(sc, SDMMC_FIFO_BASE);
113624225f53Skettenis 		datap += 4;
113724225f53Skettenis 		datalen -= 4;
113824225f53Skettenis 	}
113924225f53Skettenis 	if (datalen > 0) {
114024225f53Skettenis 		uint32_t rv = HREAD4(sc, SDMMC_FIFO_BASE);
114124225f53Skettenis 		do {
114224225f53Skettenis 			*datap++ = rv & 0xff;
114324225f53Skettenis 			rv = rv >> 8;
114424225f53Skettenis 		} while (--datalen > 0);
114524225f53Skettenis 	}
114624225f53Skettenis 	HWRITE4(sc, SDMMC_RINTSTS, SDMMC_RINTSTS_RXDR);
114724225f53Skettenis }
114824225f53Skettenis 
114924225f53Skettenis void
115070864666Skettenis dwmmc_write_data32(struct dwmmc_softc *sc, u_char *datap, int datalen)
115124225f53Skettenis {
115224225f53Skettenis 	while (datalen > 3) {
115324225f53Skettenis 		HWRITE4(sc, SDMMC_FIFO_BASE, *((uint32_t *)datap));
115424225f53Skettenis 		datap += 4;
115524225f53Skettenis 		datalen -= 4;
115624225f53Skettenis 	}
115724225f53Skettenis 	if (datalen > 0) {
115824225f53Skettenis 		uint32_t rv = *datap++;
115924225f53Skettenis 		if (datalen > 1)
116024225f53Skettenis 			rv |= *datap++ << 8;
116124225f53Skettenis 		if (datalen > 2)
116224225f53Skettenis 			rv |= *datap++ << 16;
116324225f53Skettenis 		HWRITE4(sc, SDMMC_FIFO_BASE, rv);
116424225f53Skettenis 	}
116524225f53Skettenis 	HWRITE4(sc, SDMMC_RINTSTS, SDMMC_RINTSTS_TXDR);
116624225f53Skettenis }
116770864666Skettenis 
116870864666Skettenis void
116970864666Skettenis dwmmc_read_data64(struct dwmmc_softc *sc, u_char *datap, int datalen)
117070864666Skettenis {
117170864666Skettenis 	while (datalen > 7) {
117270864666Skettenis 		*(uint32_t *)datap = HREAD4(sc, SDMMC_FIFO_BASE);
117370864666Skettenis 		datap += 4;
117470864666Skettenis 		datalen -= 4;
117570864666Skettenis 		*(uint32_t *)datap = HREAD4(sc, SDMMC_FIFO_BASE + 4);
117670864666Skettenis 		datap += 4;
117770864666Skettenis 		datalen -= 4;
117870864666Skettenis 	}
117970864666Skettenis 	if (datalen > 0) {
118070864666Skettenis 		uint64_t rv = HREAD4(sc, SDMMC_FIFO_BASE) |
118170864666Skettenis 		    ((uint64_t)HREAD4(sc, SDMMC_FIFO_BASE + 4) << 32);
118270864666Skettenis 		do {
118370864666Skettenis 			*datap++ = rv & 0xff;
118470864666Skettenis 			rv = rv >> 8;
118570864666Skettenis 		} while (--datalen > 0);
118670864666Skettenis 	}
118770864666Skettenis 	HWRITE4(sc, SDMMC_RINTSTS, SDMMC_RINTSTS_RXDR);
118870864666Skettenis }
118970864666Skettenis 
119070864666Skettenis void
119170864666Skettenis dwmmc_write_data64(struct dwmmc_softc *sc, u_char *datap, int datalen)
119270864666Skettenis {
119370864666Skettenis 	while (datalen > 7) {
119470864666Skettenis 		HWRITE4(sc, SDMMC_FIFO_BASE, *((uint32_t *)datap));
119570864666Skettenis 		datap += 4;
119670864666Skettenis 		datalen -= 4;
119770864666Skettenis 		HWRITE4(sc, SDMMC_FIFO_BASE + 4, *((uint32_t *)datap));
119870864666Skettenis 		datap += 4;
119970864666Skettenis 		datalen -= 4;
120070864666Skettenis 	}
120170864666Skettenis 	if (datalen > 0) {
120270864666Skettenis 		uint32_t rv = *datap++;
120370864666Skettenis 		if (datalen > 1)
120470864666Skettenis 			rv |= *datap++ << 8;
120570864666Skettenis 		if (datalen > 2)
120670864666Skettenis 			rv |= *datap++ << 16;
120770864666Skettenis 		if (datalen > 3)
120870864666Skettenis 			rv |= *datap++ << 24;
120970864666Skettenis 		HWRITE4(sc, SDMMC_FIFO_BASE, rv);
121070864666Skettenis 		if (datalen > 4)
121170864666Skettenis 			rv = *datap++;
121270864666Skettenis 		if (datalen > 5)
121370864666Skettenis 			rv |= *datap++ << 8;
121470864666Skettenis 		if (datalen > 6)
121570864666Skettenis 			rv |= *datap++ << 16;
121670864666Skettenis 		HWRITE4(sc, SDMMC_FIFO_BASE + 4, rv);
121770864666Skettenis 	}
121870864666Skettenis 	HWRITE4(sc, SDMMC_RINTSTS, SDMMC_RINTSTS_TXDR);
121970864666Skettenis }
1220d5957df7Skettenis 
1221d5957df7Skettenis void
1222d5957df7Skettenis dwmmc_pwrseq_pre(uint32_t phandle)
1223d5957df7Skettenis {
1224d5957df7Skettenis 	uint32_t *gpios, *gpio;
1225d5957df7Skettenis 	int node;
1226d5957df7Skettenis 	int len;
1227d5957df7Skettenis 
1228d5957df7Skettenis 	node = OF_getnodebyphandle(phandle);
1229d5957df7Skettenis 	if (node == 0)
1230d5957df7Skettenis 		return;
1231d5957df7Skettenis 
1232d5957df7Skettenis 	if (!OF_is_compatible(node, "mmc-pwrseq-simple"))
1233d5957df7Skettenis 		return;
1234d5957df7Skettenis 
1235d5957df7Skettenis 	pinctrl_byname(node, "default");
1236d5957df7Skettenis 
1237d5957df7Skettenis 	clock_enable(node, "ext_clock");
1238d5957df7Skettenis 
1239d5957df7Skettenis 	len = OF_getproplen(node, "reset-gpios");
1240d5957df7Skettenis 	if (len <= 0)
1241d5957df7Skettenis 		return;
1242d5957df7Skettenis 
1243d5957df7Skettenis 	gpios = malloc(len, M_TEMP, M_WAITOK);
1244d5957df7Skettenis 	OF_getpropintarray(node, "reset-gpios", gpios, len);
1245d5957df7Skettenis 
1246d5957df7Skettenis 	gpio = gpios;
1247d5957df7Skettenis 	while (gpio && gpio < gpios + (len / sizeof(uint32_t))) {
1248d5957df7Skettenis 		gpio_controller_config_pin(gpio, GPIO_CONFIG_OUTPUT);
1249d5957df7Skettenis 		gpio_controller_set_pin(gpio, 1);
1250d5957df7Skettenis 		gpio = gpio_controller_next_pin(gpio);
1251d5957df7Skettenis 	}
1252d5957df7Skettenis 
1253d5957df7Skettenis 	free(gpios, M_TEMP, len);
1254d5957df7Skettenis }
1255d5957df7Skettenis 
1256d5957df7Skettenis void
1257d5957df7Skettenis dwmmc_pwrseq_post(uint32_t phandle)
1258d5957df7Skettenis {
1259d5957df7Skettenis 	uint32_t *gpios, *gpio;
12604c69420eSkettenis 	int post_delay;
1261d5957df7Skettenis 	int node;
1262d5957df7Skettenis 	int len;
1263d5957df7Skettenis 
1264d5957df7Skettenis 	node = OF_getnodebyphandle(phandle);
1265d5957df7Skettenis 	if (node == 0)
1266d5957df7Skettenis 		return;
1267d5957df7Skettenis 
1268d5957df7Skettenis 	if (!OF_is_compatible(node, "mmc-pwrseq-simple"))
1269d5957df7Skettenis 		return;
1270d5957df7Skettenis 
1271d5957df7Skettenis 	len = OF_getproplen(node, "reset-gpios");
1272d5957df7Skettenis 	if (len <= 0)
1273d5957df7Skettenis 		return;
1274d5957df7Skettenis 
1275d5957df7Skettenis 	gpios = malloc(len, M_TEMP, M_WAITOK);
1276d5957df7Skettenis 	OF_getpropintarray(node, "reset-gpios", gpios, len);
1277d5957df7Skettenis 
1278d5957df7Skettenis 	gpio = gpios;
1279d5957df7Skettenis 	while (gpio && gpio < gpios + (len / sizeof(uint32_t))) {
1280d5957df7Skettenis 		gpio_controller_set_pin(gpio, 0);
1281d5957df7Skettenis 		gpio = gpio_controller_next_pin(gpio);
1282d5957df7Skettenis 	}
1283d5957df7Skettenis 
12844c69420eSkettenis 	post_delay = OF_getpropint(node, "post-power-on-delay-ms", 0);
12854c69420eSkettenis 	if (post_delay)
12864c69420eSkettenis 		delay(post_delay * 1000);
12874c69420eSkettenis 
1288d5957df7Skettenis 	free(gpios, M_TEMP, len);
1289d5957df7Skettenis }
1290*98e9fa9cSpatrick 
1291*98e9fa9cSpatrick int
1292*98e9fa9cSpatrick dwmmc_signal_voltage(sdmmc_chipset_handle_t sch, int signal_voltage)
1293*98e9fa9cSpatrick {
1294*98e9fa9cSpatrick 	struct dwmmc_softc *sc = sch;
1295*98e9fa9cSpatrick 	uint32_t vccq;
1296*98e9fa9cSpatrick 
1297*98e9fa9cSpatrick 	if (sc->sc_vqmmc == 0)
1298*98e9fa9cSpatrick 		return ENODEV;
1299*98e9fa9cSpatrick 
1300*98e9fa9cSpatrick 	switch (signal_voltage) {
1301*98e9fa9cSpatrick 	case SDMMC_SIGNAL_VOLTAGE_180:
1302*98e9fa9cSpatrick 		vccq = 1800000;
1303*98e9fa9cSpatrick 		break;
1304*98e9fa9cSpatrick 	case SDMMC_SIGNAL_VOLTAGE_330:
1305*98e9fa9cSpatrick 		vccq = 3300000;
1306*98e9fa9cSpatrick 		break;
1307*98e9fa9cSpatrick 	default:
1308*98e9fa9cSpatrick 		return EINVAL;
1309*98e9fa9cSpatrick 	}
1310*98e9fa9cSpatrick 
1311*98e9fa9cSpatrick 	if (regulator_get_voltage(sc->sc_vqmmc) == vccq)
1312*98e9fa9cSpatrick 		return 0;
1313*98e9fa9cSpatrick 
1314*98e9fa9cSpatrick 	return regulator_set_voltage(sc->sc_vqmmc, vccq);
1315*98e9fa9cSpatrick }
1316