xref: /freebsd-src/sys/dev/mmc/host/dwmmc.c (revision 3ddaf8200bc90b1410755ebac7b5c979ea90a2f6)
141709d23SRuslan Bukin /*-
23ee93b74SRuslan Bukin  * Copyright (c) 2014-2019 Ruslan Bukin <br@bsdpad.com>
341709d23SRuslan Bukin  * All rights reserved.
441709d23SRuslan Bukin  *
541709d23SRuslan Bukin  * This software was developed by SRI International and the University of
641709d23SRuslan Bukin  * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
741709d23SRuslan Bukin  * ("CTSRD"), as part of the DARPA CRASH research programme.
841709d23SRuslan Bukin  *
941709d23SRuslan Bukin  * Redistribution and use in source and binary forms, with or without
1041709d23SRuslan Bukin  * modification, are permitted provided that the following conditions
1141709d23SRuslan Bukin  * are met:
1241709d23SRuslan Bukin  * 1. Redistributions of source code must retain the above copyright
1341709d23SRuslan Bukin  *    notice, this list of conditions and the following disclaimer.
1441709d23SRuslan Bukin  * 2. Redistributions in binary form must reproduce the above copyright
1541709d23SRuslan Bukin  *    notice, this list of conditions and the following disclaimer in the
1641709d23SRuslan Bukin  *    documentation and/or other materials provided with the distribution.
1741709d23SRuslan Bukin  *
1841709d23SRuslan Bukin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1941709d23SRuslan Bukin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2041709d23SRuslan Bukin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2141709d23SRuslan Bukin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2241709d23SRuslan Bukin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2341709d23SRuslan Bukin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2441709d23SRuslan Bukin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2541709d23SRuslan Bukin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2641709d23SRuslan Bukin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2741709d23SRuslan Bukin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2841709d23SRuslan Bukin  * SUCH DAMAGE.
2941709d23SRuslan Bukin  */
3041709d23SRuslan Bukin 
3141709d23SRuslan Bukin /*
3241709d23SRuslan Bukin  * Synopsys DesignWare Mobile Storage Host Controller
3341709d23SRuslan Bukin  * Chapter 14, Altera Cyclone V Device Handbook (CV-5V2 2014.07.22)
3441709d23SRuslan Bukin  */
3541709d23SRuslan Bukin 
3641709d23SRuslan Bukin #include <sys/param.h>
3741709d23SRuslan Bukin #include <sys/systm.h>
3844682688SAndriy Gapon #include <sys/conf.h>
3941709d23SRuslan Bukin #include <sys/bus.h>
4041709d23SRuslan Bukin #include <sys/kernel.h>
41e2e050c8SConrad Meyer #include <sys/lock.h>
4241709d23SRuslan Bukin #include <sys/module.h>
4341709d23SRuslan Bukin #include <sys/malloc.h>
44e2e050c8SConrad Meyer #include <sys/mutex.h>
4544682688SAndriy Gapon #include <sys/proc.h>
4641709d23SRuslan Bukin #include <sys/rman.h>
4787d4212bSEmmanuel Vadot #include <sys/queue.h>
4887d4212bSEmmanuel Vadot #include <sys/taskqueue.h>
4941709d23SRuslan Bukin 
5041709d23SRuslan Bukin #include <dev/mmc/bridge.h>
5141709d23SRuslan Bukin #include <dev/mmc/mmcbrvar.h>
52a1af70e5SEmmanuel Vadot #include <dev/mmc/mmc_fdt_helpers.h>
5341709d23SRuslan Bukin 
5441709d23SRuslan Bukin #include <dev/fdt/fdt_common.h>
5541709d23SRuslan Bukin #include <dev/ofw/openfirm.h>
5641709d23SRuslan Bukin #include <dev/ofw/ofw_bus.h>
5741709d23SRuslan Bukin #include <dev/ofw/ofw_bus_subr.h>
5841709d23SRuslan Bukin 
5941709d23SRuslan Bukin #include <machine/bus.h>
6041709d23SRuslan Bukin #include <machine/cpu.h>
6141709d23SRuslan Bukin #include <machine/intr.h>
6241709d23SRuslan Bukin 
63be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
64dd198e86SEmmanuel Vadot 
65755eb18fSAndrew Turner #include <dev/mmc/host/dwmmc_reg.h>
66fa6ea996SAndrew Turner #include <dev/mmc/host/dwmmc_var.h>
6741709d23SRuslan Bukin 
6841c653beSEmmanuel Vadot #include "opt_mmccam.h"
6941c653beSEmmanuel Vadot 
7041c653beSEmmanuel Vadot #ifdef MMCCAM
7141c653beSEmmanuel Vadot #include <cam/cam.h>
7241c653beSEmmanuel Vadot #include <cam/cam_ccb.h>
7341c653beSEmmanuel Vadot #include <cam/cam_debug.h>
7441c653beSEmmanuel Vadot #include <cam/cam_sim.h>
7541c653beSEmmanuel Vadot #include <cam/cam_xpt_sim.h>
76f1cc48e5SEmmanuel Vadot 
77f1cc48e5SEmmanuel Vadot #include "mmc_sim_if.h"
7841c653beSEmmanuel Vadot #endif
7941c653beSEmmanuel Vadot 
8041709d23SRuslan Bukin #include "mmcbr_if.h"
8141709d23SRuslan Bukin 
8241c653beSEmmanuel Vadot #ifdef DEBUG
8341c653beSEmmanuel Vadot #define dprintf(fmt, args...) printf(fmt, ##args)
8441c653beSEmmanuel Vadot #else
8541709d23SRuslan Bukin #define dprintf(x, arg...)
8641c653beSEmmanuel Vadot #endif
8741709d23SRuslan Bukin 
8841709d23SRuslan Bukin #define	READ4(_sc, _reg) \
8941709d23SRuslan Bukin 	bus_read_4((_sc)->res[0], _reg)
9041709d23SRuslan Bukin #define	WRITE4(_sc, _reg, _val) \
9141709d23SRuslan Bukin 	bus_write_4((_sc)->res[0], _reg, _val)
9241709d23SRuslan Bukin 
93057b4402SPedro F. Giffuni #define	DIV_ROUND_UP(n, d)		howmany(n, d)
9441709d23SRuslan Bukin 
9541709d23SRuslan Bukin #define	DWMMC_LOCK(_sc)			mtx_lock(&(_sc)->sc_mtx)
9641709d23SRuslan Bukin #define	DWMMC_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_mtx)
9741709d23SRuslan Bukin #define	DWMMC_LOCK_INIT(_sc) \
9841709d23SRuslan Bukin 	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \
9941709d23SRuslan Bukin 	    "dwmmc", MTX_DEF)
10041709d23SRuslan Bukin #define	DWMMC_LOCK_DESTROY(_sc)		mtx_destroy(&_sc->sc_mtx);
10141709d23SRuslan Bukin #define	DWMMC_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_OWNED);
10241709d23SRuslan Bukin #define	DWMMC_ASSERT_UNLOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
10341709d23SRuslan Bukin 
10441709d23SRuslan Bukin #define	PENDING_CMD	0x01
10541709d23SRuslan Bukin #define	PENDING_STOP	0x02
10641709d23SRuslan Bukin #define	CARD_INIT_DONE	0x04
10741709d23SRuslan Bukin 
10841709d23SRuslan Bukin #define	DWMMC_DATA_ERR_FLAGS	(SDMMC_INTMASK_DRT | SDMMC_INTMASK_DCRC \
1098727c174SMichal Meloun 				|SDMMC_INTMASK_SBE | SDMMC_INTMASK_EBE)
11041709d23SRuslan Bukin #define	DWMMC_CMD_ERR_FLAGS	(SDMMC_INTMASK_RTO | SDMMC_INTMASK_RCRC \
11141709d23SRuslan Bukin 				|SDMMC_INTMASK_RE)
11241709d23SRuslan Bukin #define	DWMMC_ERR_FLAGS		(DWMMC_DATA_ERR_FLAGS | DWMMC_CMD_ERR_FLAGS \
11341709d23SRuslan Bukin 				|SDMMC_INTMASK_HLE)
11441709d23SRuslan Bukin 
115c30e9bebSEmmanuel Vadot #define	DES0_DIC	(1 << 1)	/* Disable Interrupt on Completion */
116c30e9bebSEmmanuel Vadot #define	DES0_LD		(1 << 2)	/* Last Descriptor */
117c30e9bebSEmmanuel Vadot #define	DES0_FS		(1 << 3)	/* First Descriptor */
118c30e9bebSEmmanuel Vadot #define	DES0_CH		(1 << 4)	/* second address CHained */
119c30e9bebSEmmanuel Vadot #define	DES0_ER		(1 << 5)	/* End of Ring */
120c30e9bebSEmmanuel Vadot #define	DES0_CES	(1 << 30)	/* Card Error Summary */
121c30e9bebSEmmanuel Vadot #define	DES0_OWN	(1 << 31)	/* OWN */
12241709d23SRuslan Bukin 
123c30e9bebSEmmanuel Vadot #define	DES1_BS1_MASK	0x1fff
12441709d23SRuslan Bukin 
12541709d23SRuslan Bukin struct idmac_desc {
12641709d23SRuslan Bukin 	uint32_t	des0;	/* control */
12741709d23SRuslan Bukin 	uint32_t	des1;	/* bufsize */
12841709d23SRuslan Bukin 	uint32_t	des2;	/* buf1 phys addr */
12941709d23SRuslan Bukin 	uint32_t	des3;	/* buf2 phys addr or next descr */
13041709d23SRuslan Bukin };
13141709d23SRuslan Bukin 
132c30e9bebSEmmanuel Vadot #define	IDMAC_DESC_SEGS	(PAGE_SIZE / (sizeof(struct idmac_desc)))
133c30e9bebSEmmanuel Vadot #define	IDMAC_DESC_SIZE	(sizeof(struct idmac_desc) * IDMAC_DESC_SEGS)
13441709d23SRuslan Bukin #define	DEF_MSIZE	0x2	/* Burst size of multiple transaction */
1358727c174SMichal Meloun /*
1368727c174SMichal Meloun  * Size field in DMA descriptor is 13 bits long (up to 4095 bytes),
1378727c174SMichal Meloun  * but must be a multiple of the data bus size.Additionally, we must ensure
1388727c174SMichal Meloun  * that bus_dmamap_load() doesn't additionally fragments buffer (because it
1398727c174SMichal Meloun  * is processed with page size granularity). Thus limit fragment size to half
1408727c174SMichal Meloun  * of page.
1418727c174SMichal Meloun  * XXX switch descriptor format to array and use second buffer pointer for
1428727c174SMichal Meloun  * second half of page
1438727c174SMichal Meloun  */
1448727c174SMichal Meloun #define	IDMAC_MAX_SIZE	2048
145dfb73602SMichal Meloun /*
146dfb73602SMichal Meloun  * Busdma may bounce buffers, so we must reserve 2 descriptors
147dfb73602SMichal Meloun  * (on start and on end) for bounced fragments.
148dfb73602SMichal Meloun  */
149dfb73602SMichal Meloun #define DWMMC_MAX_DATA	(IDMAC_MAX_SIZE * (IDMAC_DESC_SEGS - 2)) / MMC_SECTOR_SIZE
15041709d23SRuslan Bukin 
15141709d23SRuslan Bukin static void dwmmc_next_operation(struct dwmmc_softc *);
15241709d23SRuslan Bukin static int dwmmc_setup_bus(struct dwmmc_softc *, int);
15341709d23SRuslan Bukin static int dma_done(struct dwmmc_softc *, struct mmc_command *);
15441709d23SRuslan Bukin static int dma_stop(struct dwmmc_softc *);
155e2763dcaSGanbold Tsagaankhuu static void pio_read(struct dwmmc_softc *, struct mmc_command *);
156e2763dcaSGanbold Tsagaankhuu static void pio_write(struct dwmmc_softc *, struct mmc_command *);
15787d4212bSEmmanuel Vadot static void dwmmc_handle_card_present(struct dwmmc_softc *sc, bool is_present);
15841709d23SRuslan Bukin 
15941709d23SRuslan Bukin static struct resource_spec dwmmc_spec[] = {
16041709d23SRuslan Bukin 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
16141709d23SRuslan Bukin 	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
16241709d23SRuslan Bukin 	{ -1, 0 }
16341709d23SRuslan Bukin };
16441709d23SRuslan Bukin 
16541709d23SRuslan Bukin #define	HWTYPE_MASK		(0x0000ffff)
16641709d23SRuslan Bukin #define	HWFLAG_MASK		(0xffff << 16)
16741709d23SRuslan Bukin 
16841709d23SRuslan Bukin static void
16941709d23SRuslan Bukin dwmmc_get1paddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
17041709d23SRuslan Bukin {
17141709d23SRuslan Bukin 
1728727c174SMichal Meloun 	if (nsegs != 1)
1738727c174SMichal Meloun 		panic("%s: nsegs != 1 (%d)\n", __func__, nsegs);
17441709d23SRuslan Bukin 	if (error != 0)
1758727c174SMichal Meloun 		panic("%s: error != 0 (%d)\n", __func__, error);
1768727c174SMichal Meloun 
17741709d23SRuslan Bukin 	*(bus_addr_t *)arg = segs[0].ds_addr;
17841709d23SRuslan Bukin }
17941709d23SRuslan Bukin 
18041709d23SRuslan Bukin static void
18141709d23SRuslan Bukin dwmmc_ring_setup(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
18241709d23SRuslan Bukin {
18341709d23SRuslan Bukin 	struct dwmmc_softc *sc;
18441709d23SRuslan Bukin 	int idx;
18541709d23SRuslan Bukin 
18641709d23SRuslan Bukin 	sc = arg;
18741709d23SRuslan Bukin 	dprintf("nsegs %d seg0len %lu\n", nsegs, segs[0].ds_len);
1888727c174SMichal Meloun 	if (error != 0)
1898727c174SMichal Meloun 		panic("%s: error != 0 (%d)\n", __func__, error);
19041709d23SRuslan Bukin 
19141709d23SRuslan Bukin 	for (idx = 0; idx < nsegs; idx++) {
1928727c174SMichal Meloun 		sc->desc_ring[idx].des0 = DES0_DIC | DES0_CH;
193c30e9bebSEmmanuel Vadot 		sc->desc_ring[idx].des1 = segs[idx].ds_len & DES1_BS1_MASK;
19441709d23SRuslan Bukin 		sc->desc_ring[idx].des2 = segs[idx].ds_addr;
19541709d23SRuslan Bukin 
19641709d23SRuslan Bukin 		if (idx == 0)
19741709d23SRuslan Bukin 			sc->desc_ring[idx].des0 |= DES0_FS;
19841709d23SRuslan Bukin 
19941709d23SRuslan Bukin 		if (idx == (nsegs - 1)) {
20041709d23SRuslan Bukin 			sc->desc_ring[idx].des0 &= ~(DES0_DIC | DES0_CH);
20141709d23SRuslan Bukin 			sc->desc_ring[idx].des0 |= DES0_LD;
20241709d23SRuslan Bukin 		}
2038727c174SMichal Meloun 		wmb();
2048727c174SMichal Meloun 		sc->desc_ring[idx].des0 |= DES0_OWN;
20541709d23SRuslan Bukin 	}
20641709d23SRuslan Bukin }
20741709d23SRuslan Bukin 
20841709d23SRuslan Bukin static int
20941709d23SRuslan Bukin dwmmc_ctrl_reset(struct dwmmc_softc *sc, int reset_bits)
21041709d23SRuslan Bukin {
21141709d23SRuslan Bukin 	int reg;
21241709d23SRuslan Bukin 	int i;
21341709d23SRuslan Bukin 
21441709d23SRuslan Bukin 	reg = READ4(sc, SDMMC_CTRL);
21541709d23SRuslan Bukin 	reg |= (reset_bits);
21641709d23SRuslan Bukin 	WRITE4(sc, SDMMC_CTRL, reg);
21741709d23SRuslan Bukin 
21841709d23SRuslan Bukin 	/* Wait reset done */
21941709d23SRuslan Bukin 	for (i = 0; i < 100; i++) {
22041709d23SRuslan Bukin 		if (!(READ4(sc, SDMMC_CTRL) & reset_bits))
22141709d23SRuslan Bukin 			return (0);
22241709d23SRuslan Bukin 		DELAY(10);
22374b8d63dSPedro F. Giffuni 	}
22441709d23SRuslan Bukin 
22541709d23SRuslan Bukin 	device_printf(sc->dev, "Reset failed\n");
22641709d23SRuslan Bukin 
22741709d23SRuslan Bukin 	return (1);
22841709d23SRuslan Bukin }
22941709d23SRuslan Bukin 
23041709d23SRuslan Bukin static int
23141709d23SRuslan Bukin dma_setup(struct dwmmc_softc *sc)
23241709d23SRuslan Bukin {
23341709d23SRuslan Bukin 	int error;
23441709d23SRuslan Bukin 	int nidx;
23541709d23SRuslan Bukin 	int idx;
23641709d23SRuslan Bukin 
23741709d23SRuslan Bukin 	/*
23841709d23SRuslan Bukin 	 * Set up TX descriptor ring, descriptors, and dma maps.
23941709d23SRuslan Bukin 	 */
24041709d23SRuslan Bukin 	error = bus_dma_tag_create(
24141709d23SRuslan Bukin 	    bus_get_dma_tag(sc->dev),	/* Parent tag. */
24241709d23SRuslan Bukin 	    4096, 0,			/* alignment, boundary */
24341709d23SRuslan Bukin 	    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
24441709d23SRuslan Bukin 	    BUS_SPACE_MAXADDR,		/* highaddr */
24541709d23SRuslan Bukin 	    NULL, NULL,			/* filter, filterarg */
246c30e9bebSEmmanuel Vadot 	    IDMAC_DESC_SIZE, 1,		/* maxsize, nsegments */
247c30e9bebSEmmanuel Vadot 	    IDMAC_DESC_SIZE,		/* maxsegsize */
24841709d23SRuslan Bukin 	    0,				/* flags */
24941709d23SRuslan Bukin 	    NULL, NULL,			/* lockfunc, lockarg */
25041709d23SRuslan Bukin 	    &sc->desc_tag);
25141709d23SRuslan Bukin 	if (error != 0) {
25241709d23SRuslan Bukin 		device_printf(sc->dev,
25341709d23SRuslan Bukin 		    "could not create ring DMA tag.\n");
25441709d23SRuslan Bukin 		return (1);
25541709d23SRuslan Bukin 	}
25641709d23SRuslan Bukin 
25741709d23SRuslan Bukin 	error = bus_dmamem_alloc(sc->desc_tag, (void**)&sc->desc_ring,
25841709d23SRuslan Bukin 	    BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO,
25941709d23SRuslan Bukin 	    &sc->desc_map);
26041709d23SRuslan Bukin 	if (error != 0) {
26141709d23SRuslan Bukin 		device_printf(sc->dev,
26241709d23SRuslan Bukin 		    "could not allocate descriptor ring.\n");
26341709d23SRuslan Bukin 		return (1);
26441709d23SRuslan Bukin 	}
26541709d23SRuslan Bukin 
26641709d23SRuslan Bukin 	error = bus_dmamap_load(sc->desc_tag, sc->desc_map,
267c30e9bebSEmmanuel Vadot 	    sc->desc_ring, IDMAC_DESC_SIZE, dwmmc_get1paddr,
26841709d23SRuslan Bukin 	    &sc->desc_ring_paddr, 0);
26941709d23SRuslan Bukin 	if (error != 0) {
27041709d23SRuslan Bukin 		device_printf(sc->dev,
27141709d23SRuslan Bukin 		    "could not load descriptor ring map.\n");
27241709d23SRuslan Bukin 		return (1);
27341709d23SRuslan Bukin 	}
27441709d23SRuslan Bukin 
275c30e9bebSEmmanuel Vadot 	for (idx = 0; idx < IDMAC_DESC_SEGS; idx++) {
27641709d23SRuslan Bukin 		sc->desc_ring[idx].des0 = DES0_CH;
27741709d23SRuslan Bukin 		sc->desc_ring[idx].des1 = 0;
278c30e9bebSEmmanuel Vadot 		nidx = (idx + 1) % IDMAC_DESC_SEGS;
27941709d23SRuslan Bukin 		sc->desc_ring[idx].des3 = sc->desc_ring_paddr + \
28041709d23SRuslan Bukin 		    (nidx * sizeof(struct idmac_desc));
28141709d23SRuslan Bukin 	}
282c30e9bebSEmmanuel Vadot 	sc->desc_ring[idx - 1].des3 = sc->desc_ring_paddr;
283c30e9bebSEmmanuel Vadot 	sc->desc_ring[idx - 1].des0 |= DES0_ER;
28441709d23SRuslan Bukin 
28541709d23SRuslan Bukin 	error = bus_dma_tag_create(
28641709d23SRuslan Bukin 	    bus_get_dma_tag(sc->dev),	/* Parent tag. */
2878727c174SMichal Meloun 	    8, 0,			/* alignment, boundary */
28841709d23SRuslan Bukin 	    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
28941709d23SRuslan Bukin 	    BUS_SPACE_MAXADDR,		/* highaddr */
29041709d23SRuslan Bukin 	    NULL, NULL,			/* filter, filterarg */
291c30e9bebSEmmanuel Vadot 	    IDMAC_MAX_SIZE * IDMAC_DESC_SEGS,	/* maxsize */
292c30e9bebSEmmanuel Vadot 	    IDMAC_DESC_SEGS,		/* nsegments */
293c30e9bebSEmmanuel Vadot 	    IDMAC_MAX_SIZE,		/* maxsegsize */
29441709d23SRuslan Bukin 	    0,				/* flags */
29541709d23SRuslan Bukin 	    NULL, NULL,			/* lockfunc, lockarg */
29641709d23SRuslan Bukin 	    &sc->buf_tag);
29741709d23SRuslan Bukin 	if (error != 0) {
29841709d23SRuslan Bukin 		device_printf(sc->dev,
29941709d23SRuslan Bukin 		    "could not create ring DMA tag.\n");
30041709d23SRuslan Bukin 		return (1);
30141709d23SRuslan Bukin 	}
30241709d23SRuslan Bukin 
30341709d23SRuslan Bukin 	error = bus_dmamap_create(sc->buf_tag, 0,
30441709d23SRuslan Bukin 	    &sc->buf_map);
30541709d23SRuslan Bukin 	if (error != 0) {
30641709d23SRuslan Bukin 		device_printf(sc->dev,
30741709d23SRuslan Bukin 		    "could not create TX buffer DMA map.\n");
30841709d23SRuslan Bukin 		return (1);
30941709d23SRuslan Bukin 	}
31041709d23SRuslan Bukin 
31141709d23SRuslan Bukin 	return (0);
31241709d23SRuslan Bukin }
31341709d23SRuslan Bukin 
31441709d23SRuslan Bukin static void
31541709d23SRuslan Bukin dwmmc_cmd_done(struct dwmmc_softc *sc)
31641709d23SRuslan Bukin {
31741709d23SRuslan Bukin 	struct mmc_command *cmd;
31841c653beSEmmanuel Vadot #ifdef MMCCAM
31941c653beSEmmanuel Vadot 	union ccb *ccb;
32041c653beSEmmanuel Vadot #endif
32141709d23SRuslan Bukin 
32241c653beSEmmanuel Vadot #ifdef MMCCAM
32341c653beSEmmanuel Vadot 	ccb = sc->ccb;
32441c653beSEmmanuel Vadot 	if (ccb == NULL)
32541c653beSEmmanuel Vadot 		return;
32641c653beSEmmanuel Vadot 	cmd = &ccb->mmcio.cmd;
32741c653beSEmmanuel Vadot #else
32841709d23SRuslan Bukin 	cmd = sc->curcmd;
32941c653beSEmmanuel Vadot #endif
33041709d23SRuslan Bukin 	if (cmd == NULL)
33141709d23SRuslan Bukin 		return;
33241709d23SRuslan Bukin 
33341709d23SRuslan Bukin 	if (cmd->flags & MMC_RSP_PRESENT) {
33441709d23SRuslan Bukin 		if (cmd->flags & MMC_RSP_136) {
33541709d23SRuslan Bukin 			cmd->resp[3] = READ4(sc, SDMMC_RESP0);
33641709d23SRuslan Bukin 			cmd->resp[2] = READ4(sc, SDMMC_RESP1);
33741709d23SRuslan Bukin 			cmd->resp[1] = READ4(sc, SDMMC_RESP2);
33841709d23SRuslan Bukin 			cmd->resp[0] = READ4(sc, SDMMC_RESP3);
33941709d23SRuslan Bukin 		} else {
34041709d23SRuslan Bukin 			cmd->resp[3] = 0;
34141709d23SRuslan Bukin 			cmd->resp[2] = 0;
34241709d23SRuslan Bukin 			cmd->resp[1] = 0;
34341709d23SRuslan Bukin 			cmd->resp[0] = READ4(sc, SDMMC_RESP0);
34441709d23SRuslan Bukin 		}
34541709d23SRuslan Bukin 	}
34641709d23SRuslan Bukin }
34741709d23SRuslan Bukin 
34841709d23SRuslan Bukin static void
34941709d23SRuslan Bukin dwmmc_tasklet(struct dwmmc_softc *sc)
35041709d23SRuslan Bukin {
35141709d23SRuslan Bukin 	struct mmc_command *cmd;
35241709d23SRuslan Bukin 
35341709d23SRuslan Bukin 	cmd = sc->curcmd;
35441709d23SRuslan Bukin 	if (cmd == NULL)
35541709d23SRuslan Bukin 		return;
35641709d23SRuslan Bukin 
357fdbf76c3SRuslan Bukin 	if (!sc->cmd_done)
358fdbf76c3SRuslan Bukin 		return;
359fdbf76c3SRuslan Bukin 
360fdbf76c3SRuslan Bukin 	if (cmd->error != MMC_ERR_NONE || !cmd->data) {
36141709d23SRuslan Bukin 		dwmmc_next_operation(sc);
36241709d23SRuslan Bukin 	} else if (cmd->data && sc->dto_rcvd) {
36341709d23SRuslan Bukin 		if ((cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK ||
36441709d23SRuslan Bukin 		     cmd->opcode == MMC_READ_MULTIPLE_BLOCK) &&
36541709d23SRuslan Bukin 		     sc->use_auto_stop) {
36641709d23SRuslan Bukin 			if (sc->acd_rcvd)
36741709d23SRuslan Bukin 				dwmmc_next_operation(sc);
36841709d23SRuslan Bukin 		} else {
36941709d23SRuslan Bukin 			dwmmc_next_operation(sc);
37041709d23SRuslan Bukin 		}
37141709d23SRuslan Bukin 	}
37241709d23SRuslan Bukin }
37341709d23SRuslan Bukin 
37441709d23SRuslan Bukin static void
37541709d23SRuslan Bukin dwmmc_intr(void *arg)
37641709d23SRuslan Bukin {
37741709d23SRuslan Bukin 	struct mmc_command *cmd;
37841709d23SRuslan Bukin 	struct dwmmc_softc *sc;
37941709d23SRuslan Bukin 	uint32_t reg;
38041709d23SRuslan Bukin 
38141709d23SRuslan Bukin 	sc = arg;
38241709d23SRuslan Bukin 
38341709d23SRuslan Bukin 	DWMMC_LOCK(sc);
38441709d23SRuslan Bukin 
38541709d23SRuslan Bukin 	cmd = sc->curcmd;
38641709d23SRuslan Bukin 
38741709d23SRuslan Bukin 	/* First handle SDMMC controller interrupts */
38841709d23SRuslan Bukin 	reg = READ4(sc, SDMMC_MINTSTS);
38941709d23SRuslan Bukin 	if (reg) {
39041709d23SRuslan Bukin 		dprintf("%s 0x%08x\n", __func__, reg);
39141709d23SRuslan Bukin 
39241709d23SRuslan Bukin 		if (reg & DWMMC_CMD_ERR_FLAGS) {
39341709d23SRuslan Bukin 			dprintf("cmd err 0x%08x cmd 0x%08x\n",
39441709d23SRuslan Bukin 				reg, cmd->opcode);
39541709d23SRuslan Bukin 			cmd->error = MMC_ERR_TIMEOUT;
39641709d23SRuslan Bukin 		}
39741709d23SRuslan Bukin 
39841709d23SRuslan Bukin 		if (reg & DWMMC_DATA_ERR_FLAGS) {
39941709d23SRuslan Bukin 			dprintf("data err 0x%08x cmd 0x%08x\n",
40041709d23SRuslan Bukin 				reg, cmd->opcode);
40141709d23SRuslan Bukin 			cmd->error = MMC_ERR_FAILED;
402e2763dcaSGanbold Tsagaankhuu 			if (!sc->use_pio) {
40341709d23SRuslan Bukin 				dma_done(sc, cmd);
40441709d23SRuslan Bukin 				dma_stop(sc);
40541709d23SRuslan Bukin 			}
406e2763dcaSGanbold Tsagaankhuu 		}
40741709d23SRuslan Bukin 
40841709d23SRuslan Bukin 		if (reg & SDMMC_INTMASK_CMD_DONE) {
40941709d23SRuslan Bukin 			dwmmc_cmd_done(sc);
41041709d23SRuslan Bukin 			sc->cmd_done = 1;
41141709d23SRuslan Bukin 		}
41241709d23SRuslan Bukin 
413dd198e86SEmmanuel Vadot 		if (reg & SDMMC_INTMASK_ACD)
41441709d23SRuslan Bukin 			sc->acd_rcvd = 1;
41541709d23SRuslan Bukin 
416dd198e86SEmmanuel Vadot 		if (reg & SDMMC_INTMASK_DTO)
41741709d23SRuslan Bukin 			sc->dto_rcvd = 1;
41841709d23SRuslan Bukin 
41941709d23SRuslan Bukin 		if (reg & SDMMC_INTMASK_CD) {
42087d4212bSEmmanuel Vadot 			dwmmc_handle_card_present(sc,
42187d4212bSEmmanuel Vadot 			    READ4(sc, SDMMC_CDETECT) == 0 ? true : false);
42241709d23SRuslan Bukin 		}
42341709d23SRuslan Bukin 	}
42441709d23SRuslan Bukin 
425dd198e86SEmmanuel Vadot 	/* Ack interrupts */
426dd198e86SEmmanuel Vadot 	WRITE4(sc, SDMMC_RINTSTS, reg);
427dd198e86SEmmanuel Vadot 
428e2763dcaSGanbold Tsagaankhuu 	if (sc->use_pio) {
429e2763dcaSGanbold Tsagaankhuu 		if (reg & (SDMMC_INTMASK_RXDR|SDMMC_INTMASK_DTO)) {
430e2763dcaSGanbold Tsagaankhuu 			pio_read(sc, cmd);
431e2763dcaSGanbold Tsagaankhuu 		}
432e2763dcaSGanbold Tsagaankhuu 		if (reg & (SDMMC_INTMASK_TXDR|SDMMC_INTMASK_DTO)) {
433e2763dcaSGanbold Tsagaankhuu 			pio_write(sc, cmd);
434e2763dcaSGanbold Tsagaankhuu 		}
435e2763dcaSGanbold Tsagaankhuu 	} else {
43641709d23SRuslan Bukin 		/* Now handle DMA interrupts */
43741709d23SRuslan Bukin 		reg = READ4(sc, SDMMC_IDSTS);
43841709d23SRuslan Bukin 		if (reg) {
43941709d23SRuslan Bukin 			dprintf("dma intr 0x%08x\n", reg);
44041709d23SRuslan Bukin 			if (reg & (SDMMC_IDINTEN_TI | SDMMC_IDINTEN_RI)) {
44141709d23SRuslan Bukin 				WRITE4(sc, SDMMC_IDSTS, (SDMMC_IDINTEN_TI |
44241709d23SRuslan Bukin 							 SDMMC_IDINTEN_RI));
44341709d23SRuslan Bukin 				WRITE4(sc, SDMMC_IDSTS, SDMMC_IDINTEN_NI);
44441709d23SRuslan Bukin 				dma_done(sc, cmd);
44541709d23SRuslan Bukin 			}
44641709d23SRuslan Bukin 		}
447e2763dcaSGanbold Tsagaankhuu 	}
44841709d23SRuslan Bukin 
44941709d23SRuslan Bukin 	dwmmc_tasklet(sc);
45041709d23SRuslan Bukin 
45141709d23SRuslan Bukin 	DWMMC_UNLOCK(sc);
45241709d23SRuslan Bukin }
45341709d23SRuslan Bukin 
45487d4212bSEmmanuel Vadot static void
45587d4212bSEmmanuel Vadot dwmmc_handle_card_present(struct dwmmc_softc *sc, bool is_present)
45687d4212bSEmmanuel Vadot {
45787d4212bSEmmanuel Vadot 	bool was_present;
45887d4212bSEmmanuel Vadot 
45944682688SAndriy Gapon 	if (dumping || SCHEDULER_STOPPED())
46044682688SAndriy Gapon 		return;
46144682688SAndriy Gapon 
46287d4212bSEmmanuel Vadot 	was_present = sc->child != NULL;
46387d4212bSEmmanuel Vadot 
46487d4212bSEmmanuel Vadot 	if (!was_present && is_present) {
46587d4212bSEmmanuel Vadot 		taskqueue_enqueue_timeout(taskqueue_swi_giant,
46687d4212bSEmmanuel Vadot 		  &sc->card_delayed_task, -(hz / 2));
46787d4212bSEmmanuel Vadot 	} else if (was_present && !is_present) {
46887d4212bSEmmanuel Vadot 		taskqueue_enqueue(taskqueue_swi_giant, &sc->card_task);
46987d4212bSEmmanuel Vadot 	}
47087d4212bSEmmanuel Vadot }
47187d4212bSEmmanuel Vadot 
47287d4212bSEmmanuel Vadot static void
47387d4212bSEmmanuel Vadot dwmmc_card_task(void *arg, int pending __unused)
47487d4212bSEmmanuel Vadot {
47587d4212bSEmmanuel Vadot 	struct dwmmc_softc *sc = arg;
47687d4212bSEmmanuel Vadot 
47741c653beSEmmanuel Vadot #ifdef MMCCAM
478f1cc48e5SEmmanuel Vadot 	mmc_cam_sim_discover(&sc->mmc_sim);
47941c653beSEmmanuel Vadot #else
48087d4212bSEmmanuel Vadot 	DWMMC_LOCK(sc);
48187d4212bSEmmanuel Vadot 
4824c1ecf55SRuslan Bukin 	if (READ4(sc, SDMMC_CDETECT) == 0 ||
4834c1ecf55SRuslan Bukin 	    (sc->mmc_helper.props & MMC_PROP_BROKEN_CD)) {
48487d4212bSEmmanuel Vadot 		if (sc->child == NULL) {
48587d4212bSEmmanuel Vadot 			if (bootverbose)
48687d4212bSEmmanuel Vadot 				device_printf(sc->dev, "Card inserted\n");
48787d4212bSEmmanuel Vadot 
4885b56413dSWarner Losh 			sc->child = device_add_child(sc->dev, "mmc", DEVICE_UNIT_ANY);
48987d4212bSEmmanuel Vadot 			DWMMC_UNLOCK(sc);
49087d4212bSEmmanuel Vadot 			if (sc->child) {
49187d4212bSEmmanuel Vadot 				device_set_ivars(sc->child, sc);
49287d4212bSEmmanuel Vadot 				(void)device_probe_and_attach(sc->child);
49387d4212bSEmmanuel Vadot 			}
49487d4212bSEmmanuel Vadot 		} else
49587d4212bSEmmanuel Vadot 			DWMMC_UNLOCK(sc);
49687d4212bSEmmanuel Vadot 	} else {
49787d4212bSEmmanuel Vadot 		/* Card isn't present, detach if necessary */
49887d4212bSEmmanuel Vadot 		if (sc->child != NULL) {
49987d4212bSEmmanuel Vadot 			if (bootverbose)
50087d4212bSEmmanuel Vadot 				device_printf(sc->dev, "Card removed\n");
50187d4212bSEmmanuel Vadot 
50287d4212bSEmmanuel Vadot 			DWMMC_UNLOCK(sc);
50387d4212bSEmmanuel Vadot 			device_delete_child(sc->dev, sc->child);
50487d4212bSEmmanuel Vadot 			sc->child = NULL;
50587d4212bSEmmanuel Vadot 		} else
50687d4212bSEmmanuel Vadot 			DWMMC_UNLOCK(sc);
50787d4212bSEmmanuel Vadot 	}
50841c653beSEmmanuel Vadot #endif /* MMCCAM */
50987d4212bSEmmanuel Vadot }
51087d4212bSEmmanuel Vadot 
51141709d23SRuslan Bukin static int
51241709d23SRuslan Bukin parse_fdt(struct dwmmc_softc *sc)
51341709d23SRuslan Bukin {
51441709d23SRuslan Bukin 	pcell_t dts_value[3];
51541709d23SRuslan Bukin 	phandle_t node;
516a1af70e5SEmmanuel Vadot 	uint32_t bus_hz = 0;
51741709d23SRuslan Bukin 	int len;
518dd198e86SEmmanuel Vadot 	int error;
51941709d23SRuslan Bukin 
52041709d23SRuslan Bukin 	if ((node = ofw_bus_get_node(sc->dev)) == -1)
52141709d23SRuslan Bukin 		return (ENXIO);
52241709d23SRuslan Bukin 
523a1af70e5SEmmanuel Vadot 	/* Set some defaults for freq and supported mode */
524a1af70e5SEmmanuel Vadot 	sc->host.f_min = 400000;
525e82ba2c5SEmmanuel Vadot 	sc->host.f_max = 200000000;
526a1af70e5SEmmanuel Vadot 	sc->host.host_ocr = MMC_OCR_320_330 | MMC_OCR_330_340;
527a1af70e5SEmmanuel Vadot 	sc->host.caps = MMC_CAP_HSPEED | MMC_CAP_SIGNALING_330;
528a1af70e5SEmmanuel Vadot 	mmc_fdt_parse(sc->dev, node, &sc->mmc_helper, &sc->host);
529dd198e86SEmmanuel Vadot 
53041709d23SRuslan Bukin 	/* fifo-depth */
531488d593aSAndrew Turner 	if ((len = OF_getproplen(node, "fifo-depth")) > 0) {
53241709d23SRuslan Bukin 		OF_getencprop(node, "fifo-depth", dts_value, len);
53341709d23SRuslan Bukin 		sc->fifo_depth = dts_value[0];
534488d593aSAndrew Turner 	}
53541709d23SRuslan Bukin 
536dd198e86SEmmanuel Vadot 	/* num-slots (Deprecated) */
537488d593aSAndrew Turner 	sc->num_slots = 1;
538488d593aSAndrew Turner 	if ((len = OF_getproplen(node, "num-slots")) > 0) {
539dd198e86SEmmanuel Vadot 		device_printf(sc->dev, "num-slots property is deprecated\n");
54041709d23SRuslan Bukin 		OF_getencprop(node, "num-slots", dts_value, len);
54141709d23SRuslan Bukin 		sc->num_slots = dts_value[0];
542488d593aSAndrew Turner 	}
54341709d23SRuslan Bukin 
544dd198e86SEmmanuel Vadot 	/* clock-frequency */
545dd198e86SEmmanuel Vadot 	if ((len = OF_getproplen(node, "clock-frequency")) > 0) {
546dd198e86SEmmanuel Vadot 		OF_getencprop(node, "clock-frequency", dts_value, len);
547dd198e86SEmmanuel Vadot 		bus_hz = dts_value[0];
548dd198e86SEmmanuel Vadot 	}
549dd198e86SEmmanuel Vadot 
550e088e853SMichal Meloun 	/* IP block reset is optional */
551e088e853SMichal Meloun 	error = hwreset_get_by_ofw_name(sc->dev, 0, "reset", &sc->hwreset);
552a53a43d9SRuslan Bukin 	if (error != 0 &&
553a53a43d9SRuslan Bukin 	    error != ENOENT &&
554a53a43d9SRuslan Bukin 	    error != ENODEV) {
555e088e853SMichal Meloun 		device_printf(sc->dev, "Cannot get reset\n");
556a53a43d9SRuslan Bukin 		goto fail;
557a53a43d9SRuslan Bukin 	}
558e088e853SMichal Meloun 
559e088e853SMichal Meloun 	/* vmmc regulator is optional */
560e088e853SMichal Meloun 	error = regulator_get_by_ofw_property(sc->dev, 0, "vmmc-supply",
561e088e853SMichal Meloun 	     &sc->vmmc);
562a53a43d9SRuslan Bukin 	if (error != 0 &&
563a53a43d9SRuslan Bukin 	    error != ENOENT &&
564a53a43d9SRuslan Bukin 	    error != ENODEV) {
565e088e853SMichal Meloun 		device_printf(sc->dev, "Cannot get regulator 'vmmc-supply'\n");
566a53a43d9SRuslan Bukin 		goto fail;
567a53a43d9SRuslan Bukin 	}
568e088e853SMichal Meloun 
569e088e853SMichal Meloun 	/* vqmmc regulator is optional */
570e088e853SMichal Meloun 	error = regulator_get_by_ofw_property(sc->dev, 0, "vqmmc-supply",
571e088e853SMichal Meloun 	     &sc->vqmmc);
572a53a43d9SRuslan Bukin 	if (error != 0 &&
573a53a43d9SRuslan Bukin 	    error != ENOENT &&
574a53a43d9SRuslan Bukin 	    error != ENODEV) {
575e088e853SMichal Meloun 		device_printf(sc->dev, "Cannot get regulator 'vqmmc-supply'\n");
576a53a43d9SRuslan Bukin 		goto fail;
577a53a43d9SRuslan Bukin 	}
578e088e853SMichal Meloun 
579e088e853SMichal Meloun 	/* Assert reset first */
580e088e853SMichal Meloun 	if (sc->hwreset != NULL) {
581e088e853SMichal Meloun 		error = hwreset_assert(sc->hwreset);
582e088e853SMichal Meloun 		if (error != 0) {
583e088e853SMichal Meloun 			device_printf(sc->dev, "Cannot assert reset\n");
584e088e853SMichal Meloun 			goto fail;
585e088e853SMichal Meloun 		}
586e088e853SMichal Meloun 	}
587e088e853SMichal Meloun 
588dd198e86SEmmanuel Vadot 	/* BIU (Bus Interface Unit clock) is optional */
589dd198e86SEmmanuel Vadot 	error = clk_get_by_ofw_name(sc->dev, 0, "biu", &sc->biu);
590a53a43d9SRuslan Bukin 	if (error != 0 &&
591a53a43d9SRuslan Bukin 	    error != ENOENT &&
592a53a43d9SRuslan Bukin 	    error != ENODEV) {
593e088e853SMichal Meloun 		device_printf(sc->dev, "Cannot get 'biu' clock\n");
594a53a43d9SRuslan Bukin 		goto fail;
595a53a43d9SRuslan Bukin 	}
5963ee93b74SRuslan Bukin 
597dd198e86SEmmanuel Vadot 	if (sc->biu) {
598dd198e86SEmmanuel Vadot 		error = clk_enable(sc->biu);
599dd198e86SEmmanuel Vadot 		if (error != 0) {
600dd198e86SEmmanuel Vadot 			device_printf(sc->dev, "cannot enable biu clock\n");
601dd198e86SEmmanuel Vadot 			goto fail;
602dd198e86SEmmanuel Vadot 		}
603dd198e86SEmmanuel Vadot 	}
604dd198e86SEmmanuel Vadot 
60541709d23SRuslan Bukin 	/*
606dd198e86SEmmanuel Vadot 	 * CIU (Controller Interface Unit clock) is mandatory
607dd198e86SEmmanuel Vadot 	 * if no clock-frequency property is given
60841709d23SRuslan Bukin 	 */
609dd198e86SEmmanuel Vadot 	error = clk_get_by_ofw_name(sc->dev, 0, "ciu", &sc->ciu);
610a53a43d9SRuslan Bukin 	if (error != 0 &&
611a53a43d9SRuslan Bukin 	    error != ENOENT &&
612a53a43d9SRuslan Bukin 	    error != ENODEV) {
613e088e853SMichal Meloun 		device_printf(sc->dev, "Cannot get 'ciu' clock\n");
614a53a43d9SRuslan Bukin 		goto fail;
615a53a43d9SRuslan Bukin 	}
6163ee93b74SRuslan Bukin 
617e088e853SMichal Meloun 	if (sc->ciu) {
618dd198e86SEmmanuel Vadot 		if (bus_hz != 0) {
619dd198e86SEmmanuel Vadot 			error = clk_set_freq(sc->ciu, bus_hz, 0);
620dd198e86SEmmanuel Vadot 			if (error != 0)
621dd198e86SEmmanuel Vadot 				device_printf(sc->dev,
622dd198e86SEmmanuel Vadot 				    "cannot set ciu clock to %u\n", bus_hz);
623dd198e86SEmmanuel Vadot 		}
624e088e853SMichal Meloun 		error = clk_enable(sc->ciu);
625e088e853SMichal Meloun 		if (error != 0) {
626e088e853SMichal Meloun 			device_printf(sc->dev, "cannot enable ciu clock\n");
627e088e853SMichal Meloun 			goto fail;
628e088e853SMichal Meloun 		}
629dd198e86SEmmanuel Vadot 		clk_get_freq(sc->ciu, &sc->bus_hz);
630dd198e86SEmmanuel Vadot 	}
631e088e853SMichal Meloun 
63287dc015dSEmmanuel Vadot 	/* Enable regulators */
63387dc015dSEmmanuel Vadot 	if (sc->vmmc != NULL) {
63487dc015dSEmmanuel Vadot 		error = regulator_enable(sc->vmmc);
63587dc015dSEmmanuel Vadot 		if (error != 0) {
63687dc015dSEmmanuel Vadot 			device_printf(sc->dev, "Cannot enable vmmc regulator\n");
63787dc015dSEmmanuel Vadot 			goto fail;
63887dc015dSEmmanuel Vadot 		}
63987dc015dSEmmanuel Vadot 	}
64087dc015dSEmmanuel Vadot 	if (sc->vqmmc != NULL) {
64187dc015dSEmmanuel Vadot 		error = regulator_enable(sc->vqmmc);
64287dc015dSEmmanuel Vadot 		if (error != 0) {
64387dc015dSEmmanuel Vadot 			device_printf(sc->dev, "Cannot enable vqmmc regulator\n");
64487dc015dSEmmanuel Vadot 			goto fail;
64587dc015dSEmmanuel Vadot 		}
64687dc015dSEmmanuel Vadot 	}
64787dc015dSEmmanuel Vadot 
648e088e853SMichal Meloun 	/* Take dwmmc out of reset */
649e088e853SMichal Meloun 	if (sc->hwreset != NULL) {
650e088e853SMichal Meloun 		error = hwreset_deassert(sc->hwreset);
651e088e853SMichal Meloun 		if (error != 0) {
652e088e853SMichal Meloun 			device_printf(sc->dev, "Cannot deassert reset\n");
653e088e853SMichal Meloun 			goto fail;
654e088e853SMichal Meloun 		}
655e088e853SMichal Meloun 	}
656dd198e86SEmmanuel Vadot 
657725b72d5SAndrew Turner 	if (sc->bus_hz == 0) {
658dd198e86SEmmanuel Vadot 		device_printf(sc->dev, "No bus speed provided\n");
659dd198e86SEmmanuel Vadot 		goto fail;
660725b72d5SAndrew Turner 	}
66141709d23SRuslan Bukin 
66241709d23SRuslan Bukin 	return (0);
663dd198e86SEmmanuel Vadot 
664dd198e86SEmmanuel Vadot fail:
665dd198e86SEmmanuel Vadot 	return (ENXIO);
66641709d23SRuslan Bukin }
66741709d23SRuslan Bukin 
668fa6ea996SAndrew Turner int
66941709d23SRuslan Bukin dwmmc_attach(device_t dev)
67041709d23SRuslan Bukin {
67141709d23SRuslan Bukin 	struct dwmmc_softc *sc;
67241709d23SRuslan Bukin 	int error;
67341709d23SRuslan Bukin 
67441709d23SRuslan Bukin 	sc = device_get_softc(dev);
67541709d23SRuslan Bukin 
67641709d23SRuslan Bukin 	sc->dev = dev;
67741709d23SRuslan Bukin 
67841709d23SRuslan Bukin 	/* Why not to use Auto Stop? It save a hundred of irq per second */
67941709d23SRuslan Bukin 	sc->use_auto_stop = 1;
68041709d23SRuslan Bukin 
68141709d23SRuslan Bukin 	error = parse_fdt(sc);
68241709d23SRuslan Bukin 	if (error != 0) {
68341709d23SRuslan Bukin 		device_printf(dev, "Can't get FDT property.\n");
68441709d23SRuslan Bukin 		return (ENXIO);
68541709d23SRuslan Bukin 	}
68641709d23SRuslan Bukin 
68741709d23SRuslan Bukin 	DWMMC_LOCK_INIT(sc);
68841709d23SRuslan Bukin 
68941709d23SRuslan Bukin 	if (bus_alloc_resources(dev, dwmmc_spec, sc->res)) {
69041709d23SRuslan Bukin 		device_printf(dev, "could not allocate resources\n");
69141709d23SRuslan Bukin 		return (ENXIO);
69241709d23SRuslan Bukin 	}
69341709d23SRuslan Bukin 
69441709d23SRuslan Bukin 	/* Setup interrupt handler. */
69541709d23SRuslan Bukin 	error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_NET | INTR_MPSAFE,
69641709d23SRuslan Bukin 	    NULL, dwmmc_intr, sc, &sc->intr_cookie);
69741709d23SRuslan Bukin 	if (error != 0) {
69841709d23SRuslan Bukin 		device_printf(dev, "could not setup interrupt handler.\n");
69941709d23SRuslan Bukin 		return (ENXIO);
70041709d23SRuslan Bukin 	}
70141709d23SRuslan Bukin 
70241709d23SRuslan Bukin 	device_printf(dev, "Hardware version ID is %04x\n",
70341709d23SRuslan Bukin 		READ4(sc, SDMMC_VERID) & 0xffff);
70441709d23SRuslan Bukin 
70541709d23SRuslan Bukin 	/* Reset all */
70641709d23SRuslan Bukin 	if (dwmmc_ctrl_reset(sc, (SDMMC_CTRL_RESET |
70741709d23SRuslan Bukin 				  SDMMC_CTRL_FIFO_RESET |
70841709d23SRuslan Bukin 				  SDMMC_CTRL_DMA_RESET)))
70941709d23SRuslan Bukin 		return (ENXIO);
71041709d23SRuslan Bukin 
71141709d23SRuslan Bukin 	dwmmc_setup_bus(sc, sc->host.f_min);
71241709d23SRuslan Bukin 
713488d593aSAndrew Turner 	if (sc->fifo_depth == 0) {
714488d593aSAndrew Turner 		sc->fifo_depth = 1 +
715488d593aSAndrew Turner 		    ((READ4(sc, SDMMC_FIFOTH) >> SDMMC_FIFOTH_RXWMARK_S) & 0xfff);
716488d593aSAndrew Turner 		device_printf(dev, "No fifo-depth, using FIFOTH %x\n",
717488d593aSAndrew Turner 		    sc->fifo_depth);
718488d593aSAndrew Turner 	}
719488d593aSAndrew Turner 
720e2763dcaSGanbold Tsagaankhuu 	if (!sc->use_pio) {
721e3014a57SEmmanuel Vadot 		dma_stop(sc);
72241709d23SRuslan Bukin 		if (dma_setup(sc))
72341709d23SRuslan Bukin 			return (ENXIO);
72441709d23SRuslan Bukin 
72541709d23SRuslan Bukin 		/* Install desc base */
72641709d23SRuslan Bukin 		WRITE4(sc, SDMMC_DBADDR, sc->desc_ring_paddr);
72741709d23SRuslan Bukin 
72841709d23SRuslan Bukin 		/* Enable DMA interrupts */
72941709d23SRuslan Bukin 		WRITE4(sc, SDMMC_IDSTS, SDMMC_IDINTEN_MASK);
73041709d23SRuslan Bukin 		WRITE4(sc, SDMMC_IDINTEN, (SDMMC_IDINTEN_NI |
73141709d23SRuslan Bukin 					   SDMMC_IDINTEN_RI |
73241709d23SRuslan Bukin 					   SDMMC_IDINTEN_TI));
733e2763dcaSGanbold Tsagaankhuu 	}
73441709d23SRuslan Bukin 
73541709d23SRuslan Bukin 	/* Clear and disable interrups for a while */
73641709d23SRuslan Bukin 	WRITE4(sc, SDMMC_RINTSTS, 0xffffffff);
73741709d23SRuslan Bukin 	WRITE4(sc, SDMMC_INTMASK, 0);
73841709d23SRuslan Bukin 
73941709d23SRuslan Bukin 	/* Maximum timeout */
74041709d23SRuslan Bukin 	WRITE4(sc, SDMMC_TMOUT, 0xffffffff);
74141709d23SRuslan Bukin 
74241709d23SRuslan Bukin 	/* Enable interrupts */
74341709d23SRuslan Bukin 	WRITE4(sc, SDMMC_RINTSTS, 0xffffffff);
74441709d23SRuslan Bukin 	WRITE4(sc, SDMMC_INTMASK, (SDMMC_INTMASK_CMD_DONE |
74541709d23SRuslan Bukin 				   SDMMC_INTMASK_DTO |
74641709d23SRuslan Bukin 				   SDMMC_INTMASK_ACD |
74741709d23SRuslan Bukin 				   SDMMC_INTMASK_TXDR |
74841709d23SRuslan Bukin 				   SDMMC_INTMASK_RXDR |
74941709d23SRuslan Bukin 				   DWMMC_ERR_FLAGS |
75041709d23SRuslan Bukin 				   SDMMC_INTMASK_CD));
75141709d23SRuslan Bukin 	WRITE4(sc, SDMMC_CTRL, SDMMC_CTRL_INT_ENABLE);
75241709d23SRuslan Bukin 
75387d4212bSEmmanuel Vadot 	TASK_INIT(&sc->card_task, 0, dwmmc_card_task, sc);
75487d4212bSEmmanuel Vadot 	TIMEOUT_TASK_INIT(taskqueue_swi_giant, &sc->card_delayed_task, 0,
75587d4212bSEmmanuel Vadot 		dwmmc_card_task, sc);
75687d4212bSEmmanuel Vadot 
75741c653beSEmmanuel Vadot #ifdef MMCCAM
75841c653beSEmmanuel Vadot 	sc->ccb = NULL;
759f1cc48e5SEmmanuel Vadot 	if (mmc_cam_sim_alloc(dev, "dw_mmc", &sc->mmc_sim) != 0) {
760f1cc48e5SEmmanuel Vadot 		device_printf(dev, "cannot alloc cam sim\n");
761f1cc48e5SEmmanuel Vadot 		dwmmc_detach(dev);
762f1cc48e5SEmmanuel Vadot 		return (ENXIO);
76341c653beSEmmanuel Vadot 	}
76441c653beSEmmanuel Vadot #endif
76587d4212bSEmmanuel Vadot 	/*
76687d4212bSEmmanuel Vadot 	 * Schedule a card detection as we won't get an interrupt
76787d4212bSEmmanuel Vadot 	 * if the card is inserted when we attach
76887d4212bSEmmanuel Vadot 	 */
76987d4212bSEmmanuel Vadot 	dwmmc_card_task(sc, 0);
77087d4212bSEmmanuel Vadot 	return (0);
77141709d23SRuslan Bukin }
77241709d23SRuslan Bukin 
77387dc015dSEmmanuel Vadot int
77487dc015dSEmmanuel Vadot dwmmc_detach(device_t dev)
77587dc015dSEmmanuel Vadot {
77687dc015dSEmmanuel Vadot 	struct dwmmc_softc *sc;
77787dc015dSEmmanuel Vadot 	int ret;
77887dc015dSEmmanuel Vadot 
77987dc015dSEmmanuel Vadot 	sc = device_get_softc(dev);
78087dc015dSEmmanuel Vadot 
781*3ddaf820SJohn Baldwin 	ret = bus_generic_detach(dev);
78287dc015dSEmmanuel Vadot 	if (ret != 0)
78387dc015dSEmmanuel Vadot 		return (ret);
78487dc015dSEmmanuel Vadot 
78587d4212bSEmmanuel Vadot 	taskqueue_drain(taskqueue_swi_giant, &sc->card_task);
78687d4212bSEmmanuel Vadot 	taskqueue_drain_timeout(taskqueue_swi_giant, &sc->card_delayed_task);
78787d4212bSEmmanuel Vadot 
78887dc015dSEmmanuel Vadot 	if (sc->intr_cookie != NULL) {
78987dc015dSEmmanuel Vadot 		ret = bus_teardown_intr(dev, sc->res[1], sc->intr_cookie);
79087dc015dSEmmanuel Vadot 		if (ret != 0)
79187dc015dSEmmanuel Vadot 			return (ret);
79287dc015dSEmmanuel Vadot 	}
79387dc015dSEmmanuel Vadot 	bus_release_resources(dev, dwmmc_spec, sc->res);
79487dc015dSEmmanuel Vadot 
79587d4212bSEmmanuel Vadot 	DWMMC_LOCK_DESTROY(sc);
79687d4212bSEmmanuel Vadot 
79787dc015dSEmmanuel Vadot 	if (sc->hwreset != NULL && hwreset_deassert(sc->hwreset) != 0)
79887dc015dSEmmanuel Vadot 		device_printf(sc->dev, "cannot deassert reset\n");
79987dc015dSEmmanuel Vadot 	if (sc->biu != NULL && clk_disable(sc->biu) != 0)
80087dc015dSEmmanuel Vadot 		device_printf(sc->dev, "cannot disable biu clock\n");
80187dc015dSEmmanuel Vadot 	if (sc->ciu != NULL && clk_disable(sc->ciu) != 0)
80287dc015dSEmmanuel Vadot 			device_printf(sc->dev, "cannot disable ciu clock\n");
80387dc015dSEmmanuel Vadot 
80487dc015dSEmmanuel Vadot 	if (sc->vmmc && regulator_disable(sc->vmmc) != 0)
80587dc015dSEmmanuel Vadot 		device_printf(sc->dev, "Cannot disable vmmc regulator\n");
80687dc015dSEmmanuel Vadot 	if (sc->vqmmc && regulator_disable(sc->vqmmc) != 0)
80787dc015dSEmmanuel Vadot 		device_printf(sc->dev, "Cannot disable vqmmc regulator\n");
80887dc015dSEmmanuel Vadot 
809f1cc48e5SEmmanuel Vadot #ifdef MMCCAM
810f1cc48e5SEmmanuel Vadot 	mmc_cam_sim_free(&sc->mmc_sim);
811f1cc48e5SEmmanuel Vadot #endif
812f1cc48e5SEmmanuel Vadot 
81387dc015dSEmmanuel Vadot 	return (0);
81487dc015dSEmmanuel Vadot }
81587dc015dSEmmanuel Vadot 
81641709d23SRuslan Bukin static int
81741709d23SRuslan Bukin dwmmc_setup_bus(struct dwmmc_softc *sc, int freq)
81841709d23SRuslan Bukin {
81941709d23SRuslan Bukin 	int tout;
82041709d23SRuslan Bukin 	int div;
82141709d23SRuslan Bukin 
82241709d23SRuslan Bukin 	if (freq == 0) {
82341709d23SRuslan Bukin 		WRITE4(sc, SDMMC_CLKENA, 0);
82441709d23SRuslan Bukin 		WRITE4(sc, SDMMC_CMD, (SDMMC_CMD_WAIT_PRVDATA |
82541709d23SRuslan Bukin 			SDMMC_CMD_UPD_CLK_ONLY | SDMMC_CMD_START));
82641709d23SRuslan Bukin 
82741709d23SRuslan Bukin 		tout = 1000;
82841709d23SRuslan Bukin 		do {
82941709d23SRuslan Bukin 			if (tout-- < 0) {
83041709d23SRuslan Bukin 				device_printf(sc->dev, "Failed update clk\n");
83141709d23SRuslan Bukin 				return (1);
83241709d23SRuslan Bukin 			}
83341709d23SRuslan Bukin 		} while (READ4(sc, SDMMC_CMD) & SDMMC_CMD_START);
83441709d23SRuslan Bukin 
83541709d23SRuslan Bukin 		return (0);
83641709d23SRuslan Bukin 	}
83741709d23SRuslan Bukin 
83841709d23SRuslan Bukin 	WRITE4(sc, SDMMC_CLKENA, 0);
83941709d23SRuslan Bukin 	WRITE4(sc, SDMMC_CLKSRC, 0);
84041709d23SRuslan Bukin 
84141709d23SRuslan Bukin 	div = (sc->bus_hz != freq) ? DIV_ROUND_UP(sc->bus_hz, 2 * freq) : 0;
84241709d23SRuslan Bukin 
84341709d23SRuslan Bukin 	WRITE4(sc, SDMMC_CLKDIV, div);
84441709d23SRuslan Bukin 	WRITE4(sc, SDMMC_CMD, (SDMMC_CMD_WAIT_PRVDATA |
84541709d23SRuslan Bukin 			SDMMC_CMD_UPD_CLK_ONLY | SDMMC_CMD_START));
84641709d23SRuslan Bukin 
84741709d23SRuslan Bukin 	tout = 1000;
84841709d23SRuslan Bukin 	do {
84941709d23SRuslan Bukin 		if (tout-- < 0) {
8507cbdf8a0SEmmanuel Vadot 			device_printf(sc->dev, "Failed to update clk\n");
85141709d23SRuslan Bukin 			return (1);
85241709d23SRuslan Bukin 		}
85341709d23SRuslan Bukin 	} while (READ4(sc, SDMMC_CMD) & SDMMC_CMD_START);
85441709d23SRuslan Bukin 
85541709d23SRuslan Bukin 	WRITE4(sc, SDMMC_CLKENA, (SDMMC_CLKENA_CCLK_EN | SDMMC_CLKENA_LP));
85641709d23SRuslan Bukin 	WRITE4(sc, SDMMC_CMD, SDMMC_CMD_WAIT_PRVDATA |
85741709d23SRuslan Bukin 			SDMMC_CMD_UPD_CLK_ONLY | SDMMC_CMD_START);
85841709d23SRuslan Bukin 
85941709d23SRuslan Bukin 	tout = 1000;
86041709d23SRuslan Bukin 	do {
86141709d23SRuslan Bukin 		if (tout-- < 0) {
86241709d23SRuslan Bukin 			device_printf(sc->dev, "Failed to enable clk\n");
86341709d23SRuslan Bukin 			return (1);
86441709d23SRuslan Bukin 		}
86541709d23SRuslan Bukin 	} while (READ4(sc, SDMMC_CMD) & SDMMC_CMD_START);
86641709d23SRuslan Bukin 
86741709d23SRuslan Bukin 	return (0);
86841709d23SRuslan Bukin }
86941709d23SRuslan Bukin 
87041709d23SRuslan Bukin static int
87141709d23SRuslan Bukin dwmmc_update_ios(device_t brdev, device_t reqdev)
87241709d23SRuslan Bukin {
87341709d23SRuslan Bukin 	struct dwmmc_softc *sc;
87441709d23SRuslan Bukin 	struct mmc_ios *ios;
875dd198e86SEmmanuel Vadot 	uint32_t reg;
876dd198e86SEmmanuel Vadot 	int ret = 0;
87741709d23SRuslan Bukin 
87841709d23SRuslan Bukin 	sc = device_get_softc(brdev);
87941709d23SRuslan Bukin 	ios = &sc->host.ios;
88041709d23SRuslan Bukin 
881ea7f45d3SAndriy Gapon 	dprintf("Setting up clk %u bus_width %d, timing: %d\n",
8828727c174SMichal Meloun 		ios->clock, ios->bus_width, ios->timing);
88341709d23SRuslan Bukin 
884af32e2ccSEmmanuel Vadot 	switch (ios->power_mode) {
885af32e2ccSEmmanuel Vadot 	case power_on:
886af32e2ccSEmmanuel Vadot 		break;
887af32e2ccSEmmanuel Vadot 	case power_off:
888af32e2ccSEmmanuel Vadot 		WRITE4(sc, SDMMC_PWREN, 0);
889af32e2ccSEmmanuel Vadot 		break;
890af32e2ccSEmmanuel Vadot 	case power_up:
891af32e2ccSEmmanuel Vadot 		WRITE4(sc, SDMMC_PWREN, 1);
892af32e2ccSEmmanuel Vadot 		break;
893af32e2ccSEmmanuel Vadot 	}
894af32e2ccSEmmanuel Vadot 
895ce41765cSEmmanuel Vadot 	mmc_fdt_set_power(&sc->mmc_helper, ios->power_mode);
896ce41765cSEmmanuel Vadot 
89741709d23SRuslan Bukin 	if (ios->bus_width == bus_width_8)
89841709d23SRuslan Bukin 		WRITE4(sc, SDMMC_CTYPE, SDMMC_CTYPE_8BIT);
89941709d23SRuslan Bukin 	else if (ios->bus_width == bus_width_4)
90041709d23SRuslan Bukin 		WRITE4(sc, SDMMC_CTYPE, SDMMC_CTYPE_4BIT);
90141709d23SRuslan Bukin 	else
90241709d23SRuslan Bukin 		WRITE4(sc, SDMMC_CTYPE, 0);
90341709d23SRuslan Bukin 
90441709d23SRuslan Bukin 	if ((sc->hwtype & HWTYPE_MASK) == HWTYPE_EXYNOS) {
90541709d23SRuslan Bukin 		/* XXX: take care about DDR or SDR use here */
90641709d23SRuslan Bukin 		WRITE4(sc, SDMMC_CLKSEL, sc->sdr_timing);
90741709d23SRuslan Bukin 	}
90841709d23SRuslan Bukin 
909dd198e86SEmmanuel Vadot 	/* Set DDR mode */
910dd198e86SEmmanuel Vadot 	reg = READ4(sc, SDMMC_UHS_REG);
911dd198e86SEmmanuel Vadot 	if (ios->timing == bus_timing_uhs_ddr50 ||
912dd198e86SEmmanuel Vadot 	    ios->timing == bus_timing_mmc_ddr52 ||
913dd198e86SEmmanuel Vadot 	    ios->timing == bus_timing_mmc_hs400)
914dd198e86SEmmanuel Vadot 		reg |= (SDMMC_UHS_REG_DDR);
915dd198e86SEmmanuel Vadot 	else
916dd198e86SEmmanuel Vadot 		reg &= ~(SDMMC_UHS_REG_DDR);
917dd198e86SEmmanuel Vadot 	WRITE4(sc, SDMMC_UHS_REG, reg);
91841709d23SRuslan Bukin 
919dd198e86SEmmanuel Vadot 	if (sc->update_ios)
920dd198e86SEmmanuel Vadot 		ret = sc->update_ios(sc, ios);
921dd198e86SEmmanuel Vadot 
922dd198e86SEmmanuel Vadot 	dwmmc_setup_bus(sc, ios->clock);
923dd198e86SEmmanuel Vadot 
924dd198e86SEmmanuel Vadot 	return (ret);
92541709d23SRuslan Bukin }
92641709d23SRuslan Bukin 
92741709d23SRuslan Bukin static int
92841709d23SRuslan Bukin dma_done(struct dwmmc_softc *sc, struct mmc_command *cmd)
92941709d23SRuslan Bukin {
93041709d23SRuslan Bukin 	struct mmc_data *data;
93141709d23SRuslan Bukin 
93241709d23SRuslan Bukin 	data = cmd->data;
93341709d23SRuslan Bukin 
93441709d23SRuslan Bukin 	if (data->flags & MMC_DATA_WRITE)
93541709d23SRuslan Bukin 		bus_dmamap_sync(sc->buf_tag, sc->buf_map,
93641709d23SRuslan Bukin 			BUS_DMASYNC_POSTWRITE);
93741709d23SRuslan Bukin 	else
93841709d23SRuslan Bukin 		bus_dmamap_sync(sc->buf_tag, sc->buf_map,
93941709d23SRuslan Bukin 			BUS_DMASYNC_POSTREAD);
94041709d23SRuslan Bukin 
941f3ad8ea0SAndrew Turner 	bus_dmamap_sync(sc->desc_tag, sc->desc_map,
942f3ad8ea0SAndrew Turner 	    BUS_DMASYNC_POSTWRITE);
943f3ad8ea0SAndrew Turner 
94441709d23SRuslan Bukin 	bus_dmamap_unload(sc->buf_tag, sc->buf_map);
94541709d23SRuslan Bukin 
94641709d23SRuslan Bukin 	return (0);
94741709d23SRuslan Bukin }
94841709d23SRuslan Bukin 
94941709d23SRuslan Bukin static int
95041709d23SRuslan Bukin dma_stop(struct dwmmc_softc *sc)
95141709d23SRuslan Bukin {
95241709d23SRuslan Bukin 	int reg;
95341709d23SRuslan Bukin 
95441709d23SRuslan Bukin 	reg = READ4(sc, SDMMC_CTRL);
95541709d23SRuslan Bukin 	reg &= ~(SDMMC_CTRL_USE_IDMAC);
95641709d23SRuslan Bukin 	reg |= (SDMMC_CTRL_DMA_RESET);
95741709d23SRuslan Bukin 	WRITE4(sc, SDMMC_CTRL, reg);
95841709d23SRuslan Bukin 
95941709d23SRuslan Bukin 	reg = READ4(sc, SDMMC_BMOD);
96041709d23SRuslan Bukin 	reg &= ~(SDMMC_BMOD_DE | SDMMC_BMOD_FB);
96141709d23SRuslan Bukin 	reg |= (SDMMC_BMOD_SWR);
96241709d23SRuslan Bukin 	WRITE4(sc, SDMMC_BMOD, reg);
96341709d23SRuslan Bukin 
96441709d23SRuslan Bukin 	return (0);
96541709d23SRuslan Bukin }
96641709d23SRuslan Bukin 
96741709d23SRuslan Bukin static int
96841709d23SRuslan Bukin dma_prepare(struct dwmmc_softc *sc, struct mmc_command *cmd)
96941709d23SRuslan Bukin {
97041709d23SRuslan Bukin 	struct mmc_data *data;
97141709d23SRuslan Bukin 	int err;
97241709d23SRuslan Bukin 	int reg;
97341709d23SRuslan Bukin 
97441709d23SRuslan Bukin 	data = cmd->data;
97541709d23SRuslan Bukin 
97641709d23SRuslan Bukin 	reg = READ4(sc, SDMMC_INTMASK);
97741709d23SRuslan Bukin 	reg &= ~(SDMMC_INTMASK_TXDR | SDMMC_INTMASK_RXDR);
97841709d23SRuslan Bukin 	WRITE4(sc, SDMMC_INTMASK, reg);
9798727c174SMichal Meloun 	dprintf("%s: bus_dmamap_load size: %zu\n", __func__, data->len);
98041709d23SRuslan Bukin 	err = bus_dmamap_load(sc->buf_tag, sc->buf_map,
98141709d23SRuslan Bukin 		data->data, data->len, dwmmc_ring_setup,
98241709d23SRuslan Bukin 		sc, BUS_DMA_NOWAIT);
98341709d23SRuslan Bukin 	if (err != 0)
98441709d23SRuslan Bukin 		panic("dmamap_load failed\n");
98541709d23SRuslan Bukin 
986f3ad8ea0SAndrew Turner 	/* Ensure the device can see the desc */
987f3ad8ea0SAndrew Turner 	bus_dmamap_sync(sc->desc_tag, sc->desc_map,
988f3ad8ea0SAndrew Turner 	    BUS_DMASYNC_PREWRITE);
989f3ad8ea0SAndrew Turner 
99041709d23SRuslan Bukin 	if (data->flags & MMC_DATA_WRITE)
99141709d23SRuslan Bukin 		bus_dmamap_sync(sc->buf_tag, sc->buf_map,
99241709d23SRuslan Bukin 			BUS_DMASYNC_PREWRITE);
99341709d23SRuslan Bukin 	else
99441709d23SRuslan Bukin 		bus_dmamap_sync(sc->buf_tag, sc->buf_map,
99541709d23SRuslan Bukin 			BUS_DMASYNC_PREREAD);
99641709d23SRuslan Bukin 
99741709d23SRuslan Bukin 	reg = (DEF_MSIZE << SDMMC_FIFOTH_MSIZE_S);
99841709d23SRuslan Bukin 	reg |= ((sc->fifo_depth / 2) - 1) << SDMMC_FIFOTH_RXWMARK_S;
99941709d23SRuslan Bukin 	reg |= (sc->fifo_depth / 2) << SDMMC_FIFOTH_TXWMARK_S;
100041709d23SRuslan Bukin 
100141709d23SRuslan Bukin 	WRITE4(sc, SDMMC_FIFOTH, reg);
100241709d23SRuslan Bukin 	wmb();
100341709d23SRuslan Bukin 
100441709d23SRuslan Bukin 	reg = READ4(sc, SDMMC_CTRL);
100541709d23SRuslan Bukin 	reg |= (SDMMC_CTRL_USE_IDMAC | SDMMC_CTRL_DMA_ENABLE);
100641709d23SRuslan Bukin 	WRITE4(sc, SDMMC_CTRL, reg);
100741709d23SRuslan Bukin 	wmb();
100841709d23SRuslan Bukin 
100941709d23SRuslan Bukin 	reg = READ4(sc, SDMMC_BMOD);
101041709d23SRuslan Bukin 	reg |= (SDMMC_BMOD_DE | SDMMC_BMOD_FB);
101141709d23SRuslan Bukin 	WRITE4(sc, SDMMC_BMOD, reg);
101241709d23SRuslan Bukin 
101341709d23SRuslan Bukin 	/* Start */
101441709d23SRuslan Bukin 	WRITE4(sc, SDMMC_PLDMND, 1);
101541709d23SRuslan Bukin 
101641709d23SRuslan Bukin 	return (0);
101741709d23SRuslan Bukin }
101841709d23SRuslan Bukin 
1019e2763dcaSGanbold Tsagaankhuu static int
1020e2763dcaSGanbold Tsagaankhuu pio_prepare(struct dwmmc_softc *sc, struct mmc_command *cmd)
1021e2763dcaSGanbold Tsagaankhuu {
1022e2763dcaSGanbold Tsagaankhuu 	struct mmc_data *data;
1023e2763dcaSGanbold Tsagaankhuu 	int reg;
1024e2763dcaSGanbold Tsagaankhuu 
1025e2763dcaSGanbold Tsagaankhuu 	data = cmd->data;
1026e2763dcaSGanbold Tsagaankhuu 	data->xfer_len = 0;
1027e2763dcaSGanbold Tsagaankhuu 
1028e2763dcaSGanbold Tsagaankhuu 	reg = (DEF_MSIZE << SDMMC_FIFOTH_MSIZE_S);
1029e2763dcaSGanbold Tsagaankhuu 	reg |= ((sc->fifo_depth / 2) - 1) << SDMMC_FIFOTH_RXWMARK_S;
1030e2763dcaSGanbold Tsagaankhuu 	reg |= (sc->fifo_depth / 2) << SDMMC_FIFOTH_TXWMARK_S;
1031e2763dcaSGanbold Tsagaankhuu 
1032e2763dcaSGanbold Tsagaankhuu 	WRITE4(sc, SDMMC_FIFOTH, reg);
1033e2763dcaSGanbold Tsagaankhuu 	wmb();
1034e2763dcaSGanbold Tsagaankhuu 
1035e2763dcaSGanbold Tsagaankhuu 	return (0);
1036e2763dcaSGanbold Tsagaankhuu }
1037e2763dcaSGanbold Tsagaankhuu 
1038e2763dcaSGanbold Tsagaankhuu static void
1039e2763dcaSGanbold Tsagaankhuu pio_read(struct dwmmc_softc *sc, struct mmc_command *cmd)
1040e2763dcaSGanbold Tsagaankhuu {
1041e2763dcaSGanbold Tsagaankhuu 	struct mmc_data *data;
1042e2763dcaSGanbold Tsagaankhuu 	uint32_t *p, status;
1043e2763dcaSGanbold Tsagaankhuu 
1044e2763dcaSGanbold Tsagaankhuu 	if (cmd == NULL || cmd->data == NULL)
1045e2763dcaSGanbold Tsagaankhuu 		return;
1046e2763dcaSGanbold Tsagaankhuu 
1047e2763dcaSGanbold Tsagaankhuu 	data = cmd->data;
1048e2763dcaSGanbold Tsagaankhuu 	if ((data->flags & MMC_DATA_READ) == 0)
1049e2763dcaSGanbold Tsagaankhuu 		return;
1050e2763dcaSGanbold Tsagaankhuu 
1051e2763dcaSGanbold Tsagaankhuu 	KASSERT((data->xfer_len & 3) == 0, ("xfer_len not aligned"));
1052e2763dcaSGanbold Tsagaankhuu 	p = (uint32_t *)data->data + (data->xfer_len >> 2);
1053e2763dcaSGanbold Tsagaankhuu 
1054e2763dcaSGanbold Tsagaankhuu 	while (data->xfer_len < data->len) {
1055e2763dcaSGanbold Tsagaankhuu 		status = READ4(sc, SDMMC_STATUS);
1056e2763dcaSGanbold Tsagaankhuu 		if (status & SDMMC_STATUS_FIFO_EMPTY)
1057e2763dcaSGanbold Tsagaankhuu 			break;
1058e2763dcaSGanbold Tsagaankhuu 		*p++ = READ4(sc, SDMMC_DATA);
1059e2763dcaSGanbold Tsagaankhuu 		data->xfer_len += 4;
1060e2763dcaSGanbold Tsagaankhuu 	}
1061e2763dcaSGanbold Tsagaankhuu 
1062e2763dcaSGanbold Tsagaankhuu 	WRITE4(sc, SDMMC_RINTSTS, SDMMC_INTMASK_RXDR);
1063e2763dcaSGanbold Tsagaankhuu }
1064e2763dcaSGanbold Tsagaankhuu 
1065e2763dcaSGanbold Tsagaankhuu static void
1066e2763dcaSGanbold Tsagaankhuu pio_write(struct dwmmc_softc *sc, struct mmc_command *cmd)
1067e2763dcaSGanbold Tsagaankhuu {
1068e2763dcaSGanbold Tsagaankhuu 	struct mmc_data *data;
1069e2763dcaSGanbold Tsagaankhuu 	uint32_t *p, status;
1070e2763dcaSGanbold Tsagaankhuu 
1071e2763dcaSGanbold Tsagaankhuu 	if (cmd == NULL || cmd->data == NULL)
1072e2763dcaSGanbold Tsagaankhuu 		return;
1073e2763dcaSGanbold Tsagaankhuu 
1074e2763dcaSGanbold Tsagaankhuu 	data = cmd->data;
1075e2763dcaSGanbold Tsagaankhuu 	if ((data->flags & MMC_DATA_WRITE) == 0)
1076e2763dcaSGanbold Tsagaankhuu 		return;
1077e2763dcaSGanbold Tsagaankhuu 
1078e2763dcaSGanbold Tsagaankhuu 	KASSERT((data->xfer_len & 3) == 0, ("xfer_len not aligned"));
1079e2763dcaSGanbold Tsagaankhuu 	p = (uint32_t *)data->data + (data->xfer_len >> 2);
1080e2763dcaSGanbold Tsagaankhuu 
1081e2763dcaSGanbold Tsagaankhuu 	while (data->xfer_len < data->len) {
1082e2763dcaSGanbold Tsagaankhuu 		status = READ4(sc, SDMMC_STATUS);
1083e2763dcaSGanbold Tsagaankhuu 		if (status & SDMMC_STATUS_FIFO_FULL)
1084e2763dcaSGanbold Tsagaankhuu 			break;
1085e2763dcaSGanbold Tsagaankhuu 		WRITE4(sc, SDMMC_DATA, *p++);
1086e2763dcaSGanbold Tsagaankhuu 		data->xfer_len += 4;
1087e2763dcaSGanbold Tsagaankhuu 	}
1088e2763dcaSGanbold Tsagaankhuu 
1089e2763dcaSGanbold Tsagaankhuu 	WRITE4(sc, SDMMC_RINTSTS, SDMMC_INTMASK_TXDR);
1090e2763dcaSGanbold Tsagaankhuu }
1091e2763dcaSGanbold Tsagaankhuu 
109241709d23SRuslan Bukin static void
109341709d23SRuslan Bukin dwmmc_start_cmd(struct dwmmc_softc *sc, struct mmc_command *cmd)
109441709d23SRuslan Bukin {
109541709d23SRuslan Bukin 	struct mmc_data *data;
109641709d23SRuslan Bukin 	uint32_t blksz;
109741709d23SRuslan Bukin 	uint32_t cmdr;
109841709d23SRuslan Bukin 
109941c653beSEmmanuel Vadot 	dprintf("%s\n", __func__);
110041709d23SRuslan Bukin 	sc->curcmd = cmd;
110141709d23SRuslan Bukin 	data = cmd->data;
110241709d23SRuslan Bukin 
110341c653beSEmmanuel Vadot #ifndef MMCCAM
110441709d23SRuslan Bukin 	/* XXX Upper layers don't always set this */
110541709d23SRuslan Bukin 	cmd->mrq = sc->req;
110641c653beSEmmanuel Vadot #endif
110741709d23SRuslan Bukin 	/* Begin setting up command register. */
110841709d23SRuslan Bukin 
110941709d23SRuslan Bukin 	cmdr = cmd->opcode;
111041709d23SRuslan Bukin 
111141709d23SRuslan Bukin 	dprintf("cmd->opcode 0x%08x\n", cmd->opcode);
111241709d23SRuslan Bukin 
111341709d23SRuslan Bukin 	if (cmd->opcode == MMC_STOP_TRANSMISSION ||
111441709d23SRuslan Bukin 	    cmd->opcode == MMC_GO_IDLE_STATE ||
111541709d23SRuslan Bukin 	    cmd->opcode == MMC_GO_INACTIVE_STATE)
111641709d23SRuslan Bukin 		cmdr |= SDMMC_CMD_STOP_ABORT;
111741709d23SRuslan Bukin 	else if (cmd->opcode != MMC_SEND_STATUS && data)
111841709d23SRuslan Bukin 		cmdr |= SDMMC_CMD_WAIT_PRVDATA;
111941709d23SRuslan Bukin 
112041709d23SRuslan Bukin 	/* Set up response handling. */
112141709d23SRuslan Bukin 	if (MMC_RSP(cmd->flags) != MMC_RSP_NONE) {
112241709d23SRuslan Bukin 		cmdr |= SDMMC_CMD_RESP_EXP;
112341709d23SRuslan Bukin 		if (cmd->flags & MMC_RSP_136)
112441709d23SRuslan Bukin 			cmdr |= SDMMC_CMD_RESP_LONG;
112541709d23SRuslan Bukin 	}
112641709d23SRuslan Bukin 
112741709d23SRuslan Bukin 	if (cmd->flags & MMC_RSP_CRC)
112841709d23SRuslan Bukin 		cmdr |= SDMMC_CMD_RESP_CRC;
112941709d23SRuslan Bukin 
113041709d23SRuslan Bukin 	/*
113141709d23SRuslan Bukin 	 * XXX: Not all platforms want this.
113241709d23SRuslan Bukin 	 */
113341709d23SRuslan Bukin 	cmdr |= SDMMC_CMD_USE_HOLD_REG;
113441709d23SRuslan Bukin 
113541709d23SRuslan Bukin 	if ((sc->flags & CARD_INIT_DONE) == 0) {
113641709d23SRuslan Bukin 		sc->flags |= (CARD_INIT_DONE);
113741709d23SRuslan Bukin 		cmdr |= SDMMC_CMD_SEND_INIT;
113841709d23SRuslan Bukin 	}
113941709d23SRuslan Bukin 
114041709d23SRuslan Bukin 	if (data) {
114141709d23SRuslan Bukin 		if ((cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK ||
114241709d23SRuslan Bukin 		     cmd->opcode == MMC_READ_MULTIPLE_BLOCK) &&
114341709d23SRuslan Bukin 		     sc->use_auto_stop)
114441709d23SRuslan Bukin 			cmdr |= SDMMC_CMD_SEND_ASTOP;
114541709d23SRuslan Bukin 
114641709d23SRuslan Bukin 		cmdr |= SDMMC_CMD_DATA_EXP;
114741709d23SRuslan Bukin 		if (data->flags & MMC_DATA_STREAM)
114841709d23SRuslan Bukin 			cmdr |= SDMMC_CMD_MODE_STREAM;
114941709d23SRuslan Bukin 		if (data->flags & MMC_DATA_WRITE)
115041709d23SRuslan Bukin 			cmdr |= SDMMC_CMD_DATA_WRITE;
115141709d23SRuslan Bukin 
115241709d23SRuslan Bukin 		WRITE4(sc, SDMMC_TMOUT, 0xffffffff);
1153990a1dbfSEmmanuel Vadot #ifdef MMCCAM
1154990a1dbfSEmmanuel Vadot 		if (cmd->data->flags & MMC_DATA_BLOCK_SIZE) {
1155990a1dbfSEmmanuel Vadot 			WRITE4(sc, SDMMC_BLKSIZ, cmd->data->block_size);
1156990a1dbfSEmmanuel Vadot 			WRITE4(sc, SDMMC_BYTCNT, cmd->data->len);
1157990a1dbfSEmmanuel Vadot 		} else
1158990a1dbfSEmmanuel Vadot #endif
1159990a1dbfSEmmanuel Vadot 		{
116041709d23SRuslan Bukin 			WRITE4(sc, SDMMC_BYTCNT, data->len);
116141709d23SRuslan Bukin 			blksz = (data->len < MMC_SECTOR_SIZE) ? \
116241709d23SRuslan Bukin 				data->len : MMC_SECTOR_SIZE;
116341709d23SRuslan Bukin 			WRITE4(sc, SDMMC_BLKSIZ, blksz);
1164990a1dbfSEmmanuel Vadot 		}
116541709d23SRuslan Bukin 
1166e2763dcaSGanbold Tsagaankhuu 		if (sc->use_pio) {
1167e2763dcaSGanbold Tsagaankhuu 			pio_prepare(sc, cmd);
1168e2763dcaSGanbold Tsagaankhuu 		} else {
116941709d23SRuslan Bukin 			dma_prepare(sc, cmd);
1170e2763dcaSGanbold Tsagaankhuu 		}
117141709d23SRuslan Bukin 		wmb();
117241709d23SRuslan Bukin 	}
117341709d23SRuslan Bukin 
117441709d23SRuslan Bukin 	dprintf("cmdr 0x%08x\n", cmdr);
117541709d23SRuslan Bukin 
117641709d23SRuslan Bukin 	WRITE4(sc, SDMMC_CMDARG, cmd->arg);
117741709d23SRuslan Bukin 	wmb();
117841709d23SRuslan Bukin 	WRITE4(sc, SDMMC_CMD, cmdr | SDMMC_CMD_START);
117941709d23SRuslan Bukin };
118041709d23SRuslan Bukin 
118141709d23SRuslan Bukin static void
118241709d23SRuslan Bukin dwmmc_next_operation(struct dwmmc_softc *sc)
118341709d23SRuslan Bukin {
118441c653beSEmmanuel Vadot 	struct mmc_command *cmd;
118541c653beSEmmanuel Vadot 	dprintf("%s\n", __func__);
118641c653beSEmmanuel Vadot #ifdef MMCCAM
118741c653beSEmmanuel Vadot 	union ccb *ccb;
118841c653beSEmmanuel Vadot 
118941c653beSEmmanuel Vadot 	ccb = sc->ccb;
119041c653beSEmmanuel Vadot 	if (ccb == NULL)
119141c653beSEmmanuel Vadot 		return;
119241c653beSEmmanuel Vadot 	cmd = &ccb->mmcio.cmd;
119341c653beSEmmanuel Vadot #else
119441709d23SRuslan Bukin 	struct mmc_request *req;
119541709d23SRuslan Bukin 
119641709d23SRuslan Bukin 	req = sc->req;
119741709d23SRuslan Bukin 	if (req == NULL)
119841709d23SRuslan Bukin 		return;
119941c653beSEmmanuel Vadot 	cmd = req->cmd;
120041c653beSEmmanuel Vadot #endif
120141709d23SRuslan Bukin 
120241709d23SRuslan Bukin 	sc->acd_rcvd = 0;
120341709d23SRuslan Bukin 	sc->dto_rcvd = 0;
120441709d23SRuslan Bukin 	sc->cmd_done = 0;
120541709d23SRuslan Bukin 
120641709d23SRuslan Bukin 	/*
120741709d23SRuslan Bukin 	 * XXX: Wait until card is still busy.
120841709d23SRuslan Bukin 	 * We do need this to prevent data timeouts,
120941709d23SRuslan Bukin 	 * mostly caused by multi-block write command
121041709d23SRuslan Bukin 	 * followed by single-read.
121141709d23SRuslan Bukin 	 */
121241709d23SRuslan Bukin 	while(READ4(sc, SDMMC_STATUS) & (SDMMC_STATUS_DATA_BUSY))
121341709d23SRuslan Bukin 		continue;
121441709d23SRuslan Bukin 
121541709d23SRuslan Bukin 	if (sc->flags & PENDING_CMD) {
121641709d23SRuslan Bukin 		sc->flags &= ~PENDING_CMD;
121741c653beSEmmanuel Vadot 		dwmmc_start_cmd(sc, cmd);
121841709d23SRuslan Bukin 		return;
121941709d23SRuslan Bukin 	} else if (sc->flags & PENDING_STOP && !sc->use_auto_stop) {
122041709d23SRuslan Bukin 		sc->flags &= ~PENDING_STOP;
122141c653beSEmmanuel Vadot 		/// XXX: What to do with this?
122241c653beSEmmanuel Vadot 		//dwmmc_start_cmd(sc, req->stop);
122341709d23SRuslan Bukin 		return;
122441709d23SRuslan Bukin 	}
122541709d23SRuslan Bukin 
122641c653beSEmmanuel Vadot #ifdef MMCCAM
122741c653beSEmmanuel Vadot 	sc->ccb = NULL;
122841c653beSEmmanuel Vadot 	sc->curcmd = NULL;
122941c653beSEmmanuel Vadot 	ccb->ccb_h.status =
123041c653beSEmmanuel Vadot 		(ccb->mmcio.cmd.error == 0 ? CAM_REQ_CMP : CAM_REQ_CMP_ERR);
123141c653beSEmmanuel Vadot 	xpt_done(ccb);
123241c653beSEmmanuel Vadot #else
123341709d23SRuslan Bukin 	sc->req = NULL;
123441709d23SRuslan Bukin 	sc->curcmd = NULL;
123541709d23SRuslan Bukin 	req->done(req);
123641c653beSEmmanuel Vadot #endif
123741709d23SRuslan Bukin }
123841709d23SRuslan Bukin 
123941709d23SRuslan Bukin static int
124041709d23SRuslan Bukin dwmmc_request(device_t brdev, device_t reqdev, struct mmc_request *req)
124141709d23SRuslan Bukin {
124241709d23SRuslan Bukin 	struct dwmmc_softc *sc;
124341709d23SRuslan Bukin 
124441709d23SRuslan Bukin 	sc = device_get_softc(brdev);
124541709d23SRuslan Bukin 
124641709d23SRuslan Bukin 	dprintf("%s\n", __func__);
124741709d23SRuslan Bukin 
124841709d23SRuslan Bukin 	DWMMC_LOCK(sc);
124941709d23SRuslan Bukin 
125041c653beSEmmanuel Vadot #ifdef MMCCAM
125141c653beSEmmanuel Vadot 	sc->flags |= PENDING_CMD;
125241c653beSEmmanuel Vadot #else
125341709d23SRuslan Bukin 	if (sc->req != NULL) {
125441709d23SRuslan Bukin 		DWMMC_UNLOCK(sc);
125541709d23SRuslan Bukin 		return (EBUSY);
125641709d23SRuslan Bukin 	}
125741709d23SRuslan Bukin 
125841709d23SRuslan Bukin 	sc->req = req;
125941709d23SRuslan Bukin 	sc->flags |= PENDING_CMD;
126041709d23SRuslan Bukin 	if (sc->req->stop)
126141709d23SRuslan Bukin 		sc->flags |= PENDING_STOP;
126241c653beSEmmanuel Vadot #endif
126341709d23SRuslan Bukin 	dwmmc_next_operation(sc);
126441709d23SRuslan Bukin 
126541709d23SRuslan Bukin 	DWMMC_UNLOCK(sc);
126641709d23SRuslan Bukin 	return (0);
126741709d23SRuslan Bukin }
126841709d23SRuslan Bukin 
1269f1cc48e5SEmmanuel Vadot #ifndef MMCCAM
127041709d23SRuslan Bukin static int
127141709d23SRuslan Bukin dwmmc_get_ro(device_t brdev, device_t reqdev)
127241709d23SRuslan Bukin {
127341709d23SRuslan Bukin 
127441709d23SRuslan Bukin 	dprintf("%s\n", __func__);
127541709d23SRuslan Bukin 
127641709d23SRuslan Bukin 	return (0);
127741709d23SRuslan Bukin }
127841709d23SRuslan Bukin 
127941709d23SRuslan Bukin static int
128041709d23SRuslan Bukin dwmmc_acquire_host(device_t brdev, device_t reqdev)
128141709d23SRuslan Bukin {
128241709d23SRuslan Bukin 	struct dwmmc_softc *sc;
128341709d23SRuslan Bukin 
128441709d23SRuslan Bukin 	sc = device_get_softc(brdev);
128541709d23SRuslan Bukin 
128641709d23SRuslan Bukin 	DWMMC_LOCK(sc);
128741709d23SRuslan Bukin 	while (sc->bus_busy)
128841709d23SRuslan Bukin 		msleep(sc, &sc->sc_mtx, PZERO, "dwmmcah", hz / 5);
128941709d23SRuslan Bukin 	sc->bus_busy++;
129041709d23SRuslan Bukin 	DWMMC_UNLOCK(sc);
129141709d23SRuslan Bukin 	return (0);
129241709d23SRuslan Bukin }
129341709d23SRuslan Bukin 
129441709d23SRuslan Bukin static int
129541709d23SRuslan Bukin dwmmc_release_host(device_t brdev, device_t reqdev)
129641709d23SRuslan Bukin {
129741709d23SRuslan Bukin 	struct dwmmc_softc *sc;
129841709d23SRuslan Bukin 
129941709d23SRuslan Bukin 	sc = device_get_softc(brdev);
130041709d23SRuslan Bukin 
130141709d23SRuslan Bukin 	DWMMC_LOCK(sc);
130241709d23SRuslan Bukin 	sc->bus_busy--;
130341709d23SRuslan Bukin 	wakeup(sc);
130441709d23SRuslan Bukin 	DWMMC_UNLOCK(sc);
130541709d23SRuslan Bukin 	return (0);
130641709d23SRuslan Bukin }
1307f1cc48e5SEmmanuel Vadot #endif	/* !MMCCAM */
130841709d23SRuslan Bukin 
130941709d23SRuslan Bukin static int
131041709d23SRuslan Bukin dwmmc_read_ivar(device_t bus, device_t child, int which, uintptr_t *result)
131141709d23SRuslan Bukin {
131241709d23SRuslan Bukin 	struct dwmmc_softc *sc;
131341709d23SRuslan Bukin 
131441709d23SRuslan Bukin 	sc = device_get_softc(bus);
131541709d23SRuslan Bukin 
131641709d23SRuslan Bukin 	switch (which) {
131741709d23SRuslan Bukin 	default:
131841709d23SRuslan Bukin 		return (EINVAL);
131941709d23SRuslan Bukin 	case MMCBR_IVAR_BUS_MODE:
132041709d23SRuslan Bukin 		*(int *)result = sc->host.ios.bus_mode;
132141709d23SRuslan Bukin 		break;
132241709d23SRuslan Bukin 	case MMCBR_IVAR_BUS_WIDTH:
132341709d23SRuslan Bukin 		*(int *)result = sc->host.ios.bus_width;
132441709d23SRuslan Bukin 		break;
132541709d23SRuslan Bukin 	case MMCBR_IVAR_CHIP_SELECT:
132641709d23SRuslan Bukin 		*(int *)result = sc->host.ios.chip_select;
132741709d23SRuslan Bukin 		break;
132841709d23SRuslan Bukin 	case MMCBR_IVAR_CLOCK:
132941709d23SRuslan Bukin 		*(int *)result = sc->host.ios.clock;
133041709d23SRuslan Bukin 		break;
133141709d23SRuslan Bukin 	case MMCBR_IVAR_F_MIN:
133241709d23SRuslan Bukin 		*(int *)result = sc->host.f_min;
133341709d23SRuslan Bukin 		break;
133441709d23SRuslan Bukin 	case MMCBR_IVAR_F_MAX:
133541709d23SRuslan Bukin 		*(int *)result = sc->host.f_max;
133641709d23SRuslan Bukin 		break;
133741709d23SRuslan Bukin 	case MMCBR_IVAR_HOST_OCR:
133841709d23SRuslan Bukin 		*(int *)result = sc->host.host_ocr;
133941709d23SRuslan Bukin 		break;
134041709d23SRuslan Bukin 	case MMCBR_IVAR_MODE:
134141709d23SRuslan Bukin 		*(int *)result = sc->host.mode;
134241709d23SRuslan Bukin 		break;
134341709d23SRuslan Bukin 	case MMCBR_IVAR_OCR:
134441709d23SRuslan Bukin 		*(int *)result = sc->host.ocr;
134541709d23SRuslan Bukin 		break;
134641709d23SRuslan Bukin 	case MMCBR_IVAR_POWER_MODE:
134741709d23SRuslan Bukin 		*(int *)result = sc->host.ios.power_mode;
134841709d23SRuslan Bukin 		break;
134941709d23SRuslan Bukin 	case MMCBR_IVAR_VDD:
135041709d23SRuslan Bukin 		*(int *)result = sc->host.ios.vdd;
135141709d23SRuslan Bukin 		break;
13524924bcd3SWarner Losh 	case MMCBR_IVAR_VCCQ:
13534924bcd3SWarner Losh 		*(int *)result = sc->host.ios.vccq;
13544924bcd3SWarner Losh 		break;
135541709d23SRuslan Bukin 	case MMCBR_IVAR_CAPS:
135641709d23SRuslan Bukin 		*(int *)result = sc->host.caps;
135741709d23SRuslan Bukin 		break;
135841709d23SRuslan Bukin 	case MMCBR_IVAR_MAX_DATA:
1359dfb73602SMichal Meloun 		*(int *)result = DWMMC_MAX_DATA;
1360a4e0b5a4SEmmanuel Vadot 		break;
1361a4e0b5a4SEmmanuel Vadot 	case MMCBR_IVAR_TIMING:
1362a4e0b5a4SEmmanuel Vadot 		*(int *)result = sc->host.ios.timing;
1363a4e0b5a4SEmmanuel Vadot 		break;
136441709d23SRuslan Bukin 	}
136541709d23SRuslan Bukin 	return (0);
136641709d23SRuslan Bukin }
136741709d23SRuslan Bukin 
136841709d23SRuslan Bukin static int
136941709d23SRuslan Bukin dwmmc_write_ivar(device_t bus, device_t child, int which, uintptr_t value)
137041709d23SRuslan Bukin {
137141709d23SRuslan Bukin 	struct dwmmc_softc *sc;
137241709d23SRuslan Bukin 
137341709d23SRuslan Bukin 	sc = device_get_softc(bus);
137441709d23SRuslan Bukin 
137541709d23SRuslan Bukin 	switch (which) {
137641709d23SRuslan Bukin 	default:
137741709d23SRuslan Bukin 		return (EINVAL);
137841709d23SRuslan Bukin 	case MMCBR_IVAR_BUS_MODE:
137941709d23SRuslan Bukin 		sc->host.ios.bus_mode = value;
138041709d23SRuslan Bukin 		break;
138141709d23SRuslan Bukin 	case MMCBR_IVAR_BUS_WIDTH:
138241709d23SRuslan Bukin 		sc->host.ios.bus_width = value;
138341709d23SRuslan Bukin 		break;
138441709d23SRuslan Bukin 	case MMCBR_IVAR_CHIP_SELECT:
138541709d23SRuslan Bukin 		sc->host.ios.chip_select = value;
138641709d23SRuslan Bukin 		break;
138741709d23SRuslan Bukin 	case MMCBR_IVAR_CLOCK:
138841709d23SRuslan Bukin 		sc->host.ios.clock = value;
138941709d23SRuslan Bukin 		break;
139041709d23SRuslan Bukin 	case MMCBR_IVAR_MODE:
139141709d23SRuslan Bukin 		sc->host.mode = value;
139241709d23SRuslan Bukin 		break;
139341709d23SRuslan Bukin 	case MMCBR_IVAR_OCR:
139441709d23SRuslan Bukin 		sc->host.ocr = value;
139541709d23SRuslan Bukin 		break;
139641709d23SRuslan Bukin 	case MMCBR_IVAR_POWER_MODE:
139741709d23SRuslan Bukin 		sc->host.ios.power_mode = value;
139841709d23SRuslan Bukin 		break;
139941709d23SRuslan Bukin 	case MMCBR_IVAR_VDD:
140041709d23SRuslan Bukin 		sc->host.ios.vdd = value;
140141709d23SRuslan Bukin 		break;
1402a4e0b5a4SEmmanuel Vadot 	case MMCBR_IVAR_TIMING:
1403a4e0b5a4SEmmanuel Vadot 		sc->host.ios.timing = value;
1404a4e0b5a4SEmmanuel Vadot 		break;
1405a4e0b5a4SEmmanuel Vadot 	case MMCBR_IVAR_VCCQ:
1406fbcd7187SWarner Losh 		sc->host.ios.vccq = value;
1407a4e0b5a4SEmmanuel Vadot 		break;
140841709d23SRuslan Bukin 	/* These are read-only */
140941709d23SRuslan Bukin 	case MMCBR_IVAR_CAPS:
141041709d23SRuslan Bukin 	case MMCBR_IVAR_HOST_OCR:
141141709d23SRuslan Bukin 	case MMCBR_IVAR_F_MIN:
141241709d23SRuslan Bukin 	case MMCBR_IVAR_F_MAX:
141341709d23SRuslan Bukin 	case MMCBR_IVAR_MAX_DATA:
141441709d23SRuslan Bukin 		return (EINVAL);
141541709d23SRuslan Bukin 	}
141641709d23SRuslan Bukin 	return (0);
141741709d23SRuslan Bukin }
141841709d23SRuslan Bukin 
1419f4e5e045SEmmanuel Vadot #ifdef MMCCAM
142041c653beSEmmanuel Vadot /* Note: this function likely belongs to the specific driver impl */
142141c653beSEmmanuel Vadot static int
142241c653beSEmmanuel Vadot dwmmc_switch_vccq(device_t dev, device_t child)
142341c653beSEmmanuel Vadot {
142441c653beSEmmanuel Vadot 	device_printf(dev, "This is a default impl of switch_vccq() that always fails\n");
142541c653beSEmmanuel Vadot 	return EINVAL;
142641c653beSEmmanuel Vadot }
142741c653beSEmmanuel Vadot 
1428f1cc48e5SEmmanuel Vadot static int
1429f1cc48e5SEmmanuel Vadot dwmmc_get_tran_settings(device_t dev, struct ccb_trans_settings_mmc *cts)
143041c653beSEmmanuel Vadot {
143141c653beSEmmanuel Vadot 	struct dwmmc_softc *sc;
143241c653beSEmmanuel Vadot 
1433f1cc48e5SEmmanuel Vadot 	sc = device_get_softc(dev);
143441c653beSEmmanuel Vadot 
1435f1cc48e5SEmmanuel Vadot 	cts->host_ocr = sc->host.host_ocr;
1436f1cc48e5SEmmanuel Vadot 	cts->host_f_min = sc->host.f_min;
1437f1cc48e5SEmmanuel Vadot 	cts->host_f_max = sc->host.f_max;
1438f1cc48e5SEmmanuel Vadot 	cts->host_caps = sc->host.caps;
1439dfb73602SMichal Meloun 	cts->host_max_data = DWMMC_MAX_DATA;
1440f1cc48e5SEmmanuel Vadot 	memcpy(&cts->ios, &sc->host.ios, sizeof(struct mmc_ios));
144141c653beSEmmanuel Vadot 
1442f1cc48e5SEmmanuel Vadot 	return (0);
144341c653beSEmmanuel Vadot }
144441c653beSEmmanuel Vadot 
144541c653beSEmmanuel Vadot static int
1446f1cc48e5SEmmanuel Vadot dwmmc_set_tran_settings(device_t dev, struct ccb_trans_settings_mmc *cts)
144741c653beSEmmanuel Vadot {
1448f1cc48e5SEmmanuel Vadot 	struct dwmmc_softc *sc;
144941c653beSEmmanuel Vadot 	struct mmc_ios *ios;
145041c653beSEmmanuel Vadot 	struct mmc_ios *new_ios;
145141c653beSEmmanuel Vadot 	int res;
145241c653beSEmmanuel Vadot 
1453f1cc48e5SEmmanuel Vadot 	sc = device_get_softc(dev);
145441c653beSEmmanuel Vadot 	ios = &sc->host.ios;
145541c653beSEmmanuel Vadot 
145641c653beSEmmanuel Vadot 	new_ios = &cts->ios;
145741c653beSEmmanuel Vadot 
145841c653beSEmmanuel Vadot 	/* Update only requested fields */
145941c653beSEmmanuel Vadot 	if (cts->ios_valid & MMC_CLK) {
146041c653beSEmmanuel Vadot 		ios->clock = new_ios->clock;
146141c653beSEmmanuel Vadot 		if (bootverbose)
146241c653beSEmmanuel Vadot 			device_printf(sc->dev, "Clock => %d\n", ios->clock);
146341c653beSEmmanuel Vadot 	}
146441c653beSEmmanuel Vadot 	if (cts->ios_valid & MMC_VDD) {
146541c653beSEmmanuel Vadot 		ios->vdd = new_ios->vdd;
146641c653beSEmmanuel Vadot 		if (bootverbose)
146741c653beSEmmanuel Vadot 			device_printf(sc->dev, "VDD => %d\n", ios->vdd);
146841c653beSEmmanuel Vadot 	}
146941c653beSEmmanuel Vadot 	if (cts->ios_valid & MMC_CS) {
147041c653beSEmmanuel Vadot 		ios->chip_select = new_ios->chip_select;
147141c653beSEmmanuel Vadot 		if (bootverbose)
147241c653beSEmmanuel Vadot 			device_printf(sc->dev, "CS => %d\n", ios->chip_select);
147341c653beSEmmanuel Vadot 	}
147441c653beSEmmanuel Vadot 	if (cts->ios_valid & MMC_BW) {
147541c653beSEmmanuel Vadot 		ios->bus_width = new_ios->bus_width;
147641c653beSEmmanuel Vadot 		if (bootverbose)
147741c653beSEmmanuel Vadot 			device_printf(sc->dev, "Bus width => %d\n", ios->bus_width);
147841c653beSEmmanuel Vadot 	}
147941c653beSEmmanuel Vadot 	if (cts->ios_valid & MMC_PM) {
148041c653beSEmmanuel Vadot 		ios->power_mode = new_ios->power_mode;
148141c653beSEmmanuel Vadot 		if (bootverbose)
148241c653beSEmmanuel Vadot 			device_printf(sc->dev, "Power mode => %d\n", ios->power_mode);
148341c653beSEmmanuel Vadot 	}
148441c653beSEmmanuel Vadot 	if (cts->ios_valid & MMC_BT) {
148541c653beSEmmanuel Vadot 		ios->timing = new_ios->timing;
148641c653beSEmmanuel Vadot 		if (bootverbose)
148741c653beSEmmanuel Vadot 			device_printf(sc->dev, "Timing => %d\n", ios->timing);
148841c653beSEmmanuel Vadot 	}
148941c653beSEmmanuel Vadot 	if (cts->ios_valid & MMC_BM) {
149041c653beSEmmanuel Vadot 		ios->bus_mode = new_ios->bus_mode;
149141c653beSEmmanuel Vadot 		if (bootverbose)
149241c653beSEmmanuel Vadot 			device_printf(sc->dev, "Bus mode => %d\n", ios->bus_mode);
149341c653beSEmmanuel Vadot 	}
149441c653beSEmmanuel Vadot 	if (cts->ios_valid & MMC_VCCQ) {
149541c653beSEmmanuel Vadot 		ios->vccq = new_ios->vccq;
149641c653beSEmmanuel Vadot 		if (bootverbose)
149741c653beSEmmanuel Vadot 			device_printf(sc->dev, "VCCQ => %d\n", ios->vccq);
149841c653beSEmmanuel Vadot 		res = dwmmc_switch_vccq(sc->dev, NULL);
149941c653beSEmmanuel Vadot 		device_printf(sc->dev, "VCCQ switch result: %d\n", res);
150041c653beSEmmanuel Vadot 	}
150141c653beSEmmanuel Vadot 
150241c653beSEmmanuel Vadot 	return (dwmmc_update_ios(sc->dev, NULL));
150341c653beSEmmanuel Vadot }
150441c653beSEmmanuel Vadot 
150541c653beSEmmanuel Vadot static int
1506f1cc48e5SEmmanuel Vadot dwmmc_cam_request(device_t dev, union ccb *ccb)
150741c653beSEmmanuel Vadot {
1508f1cc48e5SEmmanuel Vadot 	struct dwmmc_softc *sc;
150941c653beSEmmanuel Vadot 	struct ccb_mmcio *mmcio;
151041c653beSEmmanuel Vadot 
1511f1cc48e5SEmmanuel Vadot 	sc = device_get_softc(dev);
151241c653beSEmmanuel Vadot 	mmcio = &ccb->mmcio;
151341c653beSEmmanuel Vadot 
151441c653beSEmmanuel Vadot 	DWMMC_LOCK(sc);
151541c653beSEmmanuel Vadot 
151641c653beSEmmanuel Vadot #ifdef DEBUG
151741c653beSEmmanuel Vadot 	if (__predict_false(bootverbose)) {
151841c653beSEmmanuel Vadot 		device_printf(sc->dev, "CMD%u arg %#x flags %#x dlen %u dflags %#x\n",
151941c653beSEmmanuel Vadot 			    mmcio->cmd.opcode, mmcio->cmd.arg, mmcio->cmd.flags,
152041c653beSEmmanuel Vadot 			    mmcio->cmd.data != NULL ? (unsigned int) mmcio->cmd.data->len : 0,
152141c653beSEmmanuel Vadot 			    mmcio->cmd.data != NULL ? mmcio->cmd.data->flags: 0);
152241c653beSEmmanuel Vadot 	}
152341c653beSEmmanuel Vadot #endif
152441c653beSEmmanuel Vadot 	if (mmcio->cmd.data != NULL) {
152541c653beSEmmanuel Vadot 		if (mmcio->cmd.data->len == 0 || mmcio->cmd.data->flags == 0)
152641c653beSEmmanuel Vadot 			panic("data->len = %d, data->flags = %d -- something is b0rked",
152741c653beSEmmanuel Vadot 			      (int)mmcio->cmd.data->len, mmcio->cmd.data->flags);
152841c653beSEmmanuel Vadot 	}
152941c653beSEmmanuel Vadot 	if (sc->ccb != NULL) {
153041c653beSEmmanuel Vadot 		device_printf(sc->dev, "Controller still has an active command\n");
153141c653beSEmmanuel Vadot 		return (EBUSY);
153241c653beSEmmanuel Vadot 	}
153341c653beSEmmanuel Vadot 	sc->ccb = ccb;
153441c653beSEmmanuel Vadot 	DWMMC_UNLOCK(sc);
153541c653beSEmmanuel Vadot 	dwmmc_request(sc->dev, NULL, NULL);
153641c653beSEmmanuel Vadot 
153741c653beSEmmanuel Vadot 	return (0);
153841c653beSEmmanuel Vadot }
153944682688SAndriy Gapon 
154044682688SAndriy Gapon static void
154144682688SAndriy Gapon dwmmc_cam_poll(device_t dev)
154244682688SAndriy Gapon {
154344682688SAndriy Gapon 	struct dwmmc_softc *sc;
154444682688SAndriy Gapon 
154544682688SAndriy Gapon 	sc = device_get_softc(dev);
154644682688SAndriy Gapon 	dwmmc_intr(sc);
154744682688SAndriy Gapon }
1548f1cc48e5SEmmanuel Vadot #endif /* MMCCAM */
154941c653beSEmmanuel Vadot 
155041709d23SRuslan Bukin static device_method_t dwmmc_methods[] = {
155141709d23SRuslan Bukin 	/* Bus interface */
155241709d23SRuslan Bukin 	DEVMETHOD(bus_read_ivar,	dwmmc_read_ivar),
155341709d23SRuslan Bukin 	DEVMETHOD(bus_write_ivar,	dwmmc_write_ivar),
155441709d23SRuslan Bukin 
1555f1cc48e5SEmmanuel Vadot #ifndef MMCCAM
155641709d23SRuslan Bukin 	/* mmcbr_if */
155741709d23SRuslan Bukin 	DEVMETHOD(mmcbr_update_ios,	dwmmc_update_ios),
155841709d23SRuslan Bukin 	DEVMETHOD(mmcbr_request,	dwmmc_request),
155941709d23SRuslan Bukin 	DEVMETHOD(mmcbr_get_ro,		dwmmc_get_ro),
156041709d23SRuslan Bukin 	DEVMETHOD(mmcbr_acquire_host,	dwmmc_acquire_host),
156141709d23SRuslan Bukin 	DEVMETHOD(mmcbr_release_host,	dwmmc_release_host),
1562f1cc48e5SEmmanuel Vadot #endif
1563f1cc48e5SEmmanuel Vadot 
1564f1cc48e5SEmmanuel Vadot #ifdef MMCCAM
1565f1cc48e5SEmmanuel Vadot 	/* MMCCAM interface */
1566f1cc48e5SEmmanuel Vadot 	DEVMETHOD(mmc_sim_get_tran_settings,	dwmmc_get_tran_settings),
1567f1cc48e5SEmmanuel Vadot 	DEVMETHOD(mmc_sim_set_tran_settings,	dwmmc_set_tran_settings),
1568f1cc48e5SEmmanuel Vadot 	DEVMETHOD(mmc_sim_cam_request,		dwmmc_cam_request),
156944682688SAndriy Gapon 	DEVMETHOD(mmc_sim_cam_poll,		dwmmc_cam_poll),
1570c99d887cSEmmanuel Vadot 
1571c99d887cSEmmanuel Vadot 	DEVMETHOD(bus_add_child,		bus_generic_add_child),
1572f1cc48e5SEmmanuel Vadot #endif
157341709d23SRuslan Bukin 
157441709d23SRuslan Bukin 	DEVMETHOD_END
157541709d23SRuslan Bukin };
157641709d23SRuslan Bukin 
157774e0613eSEmmanuel Vadot DEFINE_CLASS_0(dwmmc, dwmmc_driver, dwmmc_methods,
157874e0613eSEmmanuel Vadot     sizeof(struct dwmmc_softc));
1579