1d6644679SSascha Wildner /*-
2d6644679SSascha Wildner * Copyright (c) 2008 Alexander Motin <mav@FreeBSD.org>
3d6644679SSascha Wildner * All rights reserved.
4d6644679SSascha Wildner *
5d6644679SSascha Wildner * Redistribution and use in source and binary forms, with or without
6d6644679SSascha Wildner * modification, are permitted provided that the following conditions
7d6644679SSascha Wildner * are met:
8d6644679SSascha Wildner * 1. Redistributions of source code must retain the above copyright
9d6644679SSascha Wildner * notice, this list of conditions and the following disclaimer.
10d6644679SSascha Wildner * 2. Redistributions in binary form must reproduce the above copyright
11d6644679SSascha Wildner * notice, this list of conditions and the following disclaimer in the
12d6644679SSascha Wildner * documentation and/or other materials provided with the distribution.
13d6644679SSascha Wildner *
14d6644679SSascha Wildner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15d6644679SSascha Wildner * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16d6644679SSascha Wildner * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17d6644679SSascha Wildner * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18d6644679SSascha Wildner * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19d6644679SSascha Wildner * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20d6644679SSascha Wildner * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21d6644679SSascha Wildner * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22d6644679SSascha Wildner * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23d6644679SSascha Wildner * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24d6644679SSascha Wildner *
25d6644679SSascha Wildner * $FreeBSD: src/sys/dev/sdhci/sdhci.c,v 1.8 2009/02/17 19:12:15 mav Exp $
26d6644679SSascha Wildner */
27d6644679SSascha Wildner
28d6644679SSascha Wildner #include <sys/param.h>
29d6644679SSascha Wildner #include <sys/systm.h>
30d6644679SSascha Wildner #include <sys/bus.h>
31e355a444SMarkus Pfeiffer #include <sys/callout.h>
32d6644679SSascha Wildner #include <sys/conf.h>
33d6644679SSascha Wildner #include <sys/kernel.h>
34d6644679SSascha Wildner #include <sys/lock.h>
35d6644679SSascha Wildner #include <sys/module.h>
36d6644679SSascha Wildner #include <sys/spinlock.h>
37d6644679SSascha Wildner #include <sys/resource.h>
38d6644679SSascha Wildner #include <sys/rman.h>
39d6644679SSascha Wildner #include <sys/sysctl.h>
40d6644679SSascha Wildner #include <sys/taskqueue.h>
41d6644679SSascha Wildner
42d6644679SSascha Wildner #include <bus/mmc/bridge.h>
43d6644679SSascha Wildner #include <bus/mmc/mmcreg.h>
44d6644679SSascha Wildner #include <bus/mmc/mmcbrvar.h>
45d6644679SSascha Wildner
46d6644679SSascha Wildner #include "mmcbr_if.h"
47d6644679SSascha Wildner #include "sdhci.h"
48e355a444SMarkus Pfeiffer #include "sdhci_if.h"
49d6644679SSascha Wildner
50d6644679SSascha Wildner SYSCTL_NODE(_hw, OID_AUTO, sdhci, CTLFLAG_RD, 0, "sdhci driver");
51d6644679SSascha Wildner
52e355a444SMarkus Pfeiffer int sdhci_debug = 0;
53d6644679SSascha Wildner TUNABLE_INT("hw.sdhci.debug", &sdhci_debug);
54d6644679SSascha Wildner SYSCTL_INT(_hw_sdhci, OID_AUTO, debug, CTLFLAG_RW, &sdhci_debug, 0, "Debug level");
55d6644679SSascha Wildner
564f67f60cSImre Vadász static int sdhci_sdma_disable = 0;
574f67f60cSImre Vadász TUNABLE_INT("hw.sdhci.sdma_disable", &sdhci_sdma_disable);
584f67f60cSImre Vadász
594f67f60cSImre Vadász static int sdhci_adma2_disable = 0;
604f67f60cSImre Vadász TUNABLE_INT("hw.sdhci.adma2_disable", &sdhci_adma2_disable);
614f67f60cSImre Vadász
624f67f60cSImre Vadász static int sdhci_adma2_test = 0;
634f67f60cSImre Vadász TUNABLE_INT("hw.sdhci.adma2_test", &sdhci_adma2_test);
644f67f60cSImre Vadász
657ba10b88SImre Vadász u_int sdhci_quirk_clear = 0;
667ba10b88SImre Vadász TUNABLE_INT("hw.sdhci.quirk_clear", &sdhci_quirk_clear);
677ba10b88SImre Vadász u_int sdhci_quirk_set = 0;
687ba10b88SImre Vadász TUNABLE_INT("hw.sdhci.quirk_set", &sdhci_quirk_set);
697ba10b88SImre Vadász
70e355a444SMarkus Pfeiffer #define RD1(slot, off) SDHCI_READ_1((slot)->bus, (slot), (off))
71e355a444SMarkus Pfeiffer #define RD2(slot, off) SDHCI_READ_2((slot)->bus, (slot), (off))
72e355a444SMarkus Pfeiffer #define RD4(slot, off) SDHCI_READ_4((slot)->bus, (slot), (off))
73e355a444SMarkus Pfeiffer #define RD_MULTI_4(slot, off, ptr, count) \
74e355a444SMarkus Pfeiffer SDHCI_READ_MULTI_4((slot)->bus, (slot), (off), (ptr), (count))
75e355a444SMarkus Pfeiffer #define WR1(slot, off, val) SDHCI_WRITE_1((slot)->bus, (slot), (off), (val))
76e355a444SMarkus Pfeiffer #define WR2(slot, off, val) SDHCI_WRITE_2((slot)->bus, (slot), (off), (val))
77e355a444SMarkus Pfeiffer #define WR4(slot, off, val) SDHCI_WRITE_4((slot)->bus, (slot), (off), (val))
78e355a444SMarkus Pfeiffer #define WR_MULTI_4(slot, off, ptr, count) \
79e355a444SMarkus Pfeiffer SDHCI_WRITE_MULTI_4((slot)->bus, (slot), (off), (ptr), (count))
80d6644679SSascha Wildner
81d6644679SSascha Wildner static void sdhci_set_clock(struct sdhci_slot *slot, uint32_t clock);
82d6644679SSascha Wildner static void sdhci_start(struct sdhci_slot *slot);
83d6644679SSascha Wildner static void sdhci_start_data(struct sdhci_slot *slot, struct mmc_data *data);
84d6644679SSascha Wildner
85d6644679SSascha Wildner static void sdhci_card_task(void *, int);
86d6644679SSascha Wildner
874013580eSImre Vadász static int sdhci_dma_alloc(struct sdhci_slot *slot);
884013580eSImre Vadász static void sdhci_dmamem_free(bus_dmamem_t *mem);
894013580eSImre Vadász static void sdhci_dma_free(struct sdhci_slot *slot);
904013580eSImre Vadász static void sdhci_adma2_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs,
914013580eSImre Vadász int error);
924013580eSImre Vadász
93d6644679SSascha Wildner /* helper routines */
947ba10b88SImre Vadász static void sdhci_dumpregs(struct sdhci_slot *slot);
957ba10b88SImre Vadász static int slot_printf(struct sdhci_slot *slot, const char * fmt, ...)
967ba10b88SImre Vadász __printflike(2, 3);
977ba10b88SImre Vadász
98d6644679SSascha Wildner #define SDHCI_LOCK(_slot) lockmgr(&(_slot)->lock, LK_EXCLUSIVE)
99d6644679SSascha Wildner #define SDHCI_UNLOCK(_slot) lockmgr(&(_slot)->lock, LK_RELEASE)
100d6644679SSascha Wildner #define SDHCI_LOCK_INIT(_slot) lockinit(&(_slot)->lock, "sdhci", 0, LK_CANRECURSE)
101d6644679SSascha Wildner #define SDHCI_LOCK_DESTROY(_slot) lockuninit(&(_slot)->lock);
102d6644679SSascha Wildner #define SDHCI_ASSERT_LOCKED(_slot) KKASSERT(lockstatus(&(_slot)->lock, curthread) != 0);
103d6644679SSascha Wildner #define SDHCI_ASSERT_UNLOCKED(_slot) KKASSERT(lockstatus(&(_slot)->lock, curthread) == 0);
104d6644679SSascha Wildner
105e355a444SMarkus Pfeiffer #define SDHCI_DEFAULT_MAX_FREQ 50
106d6644679SSascha Wildner
107e355a444SMarkus Pfeiffer #define SDHCI_200_MAX_DIVIDER 256
108e355a444SMarkus Pfeiffer #define SDHCI_300_MAX_DIVIDER 2046
109d6644679SSascha Wildner
1104f29f02bSImre Vadász /*
1114f29f02bSImre Vadász * Broadcom BCM577xx Controller Constants
1124f29f02bSImre Vadász */
1134f29f02bSImre Vadász #define BCM577XX_DEFAULT_MAX_DIVIDER 256 /* Maximum divider supported by the default clock source. */
1144f29f02bSImre Vadász #define BCM577XX_ALT_CLOCK_BASE 63000000 /* Alternative clock's base frequency. */
1154f29f02bSImre Vadász
1164f29f02bSImre Vadász #define BCM577XX_HOST_CONTROL 0x198
1174f29f02bSImre Vadász #define BCM577XX_CTRL_CLKSEL_MASK 0xFFFFCFFF
1184f29f02bSImre Vadász #define BCM577XX_CTRL_CLKSEL_SHIFT 12
1194f29f02bSImre Vadász #define BCM577XX_CTRL_CLKSEL_DEFAULT 0x0
1204f29f02bSImre Vadász #define BCM577XX_CTRL_CLKSEL_64MHZ 0x3
1214f29f02bSImre Vadász
122e355a444SMarkus Pfeiffer static int
slot_printf(struct sdhci_slot * slot,const char * fmt,...)123e355a444SMarkus Pfeiffer slot_printf(struct sdhci_slot *slot, const char * fmt, ...)
124e355a444SMarkus Pfeiffer {
125d26841ecSSascha Wildner __va_list ap;
126e355a444SMarkus Pfeiffer int retval;
127e355a444SMarkus Pfeiffer
128e355a444SMarkus Pfeiffer retval = kprintf("%s-slot%d: ",
129e355a444SMarkus Pfeiffer device_get_nameunit(slot->bus), slot->num);
130e355a444SMarkus Pfeiffer
131d26841ecSSascha Wildner __va_start(ap, fmt);
132e355a444SMarkus Pfeiffer retval += kvprintf(fmt, ap);
133d26841ecSSascha Wildner __va_end(ap);
134e355a444SMarkus Pfeiffer return (retval);
135e355a444SMarkus Pfeiffer }
136e355a444SMarkus Pfeiffer
137d6644679SSascha Wildner static void
sdhci_dumpregs(struct sdhci_slot * slot)138d6644679SSascha Wildner sdhci_dumpregs(struct sdhci_slot *slot)
139d6644679SSascha Wildner {
140d6644679SSascha Wildner slot_printf(slot,
141d6644679SSascha Wildner "============== REGISTER DUMP ==============\n");
142d6644679SSascha Wildner
14322caf486SImre Vadász slot_printf(slot, "SDMA addr: 0x%08x | Version: 0x%08x\n",
14422caf486SImre Vadász RD4(slot, SDHCI_SDMA_ADDRESS), RD2(slot, SDHCI_HOST_VERSION));
145d6644679SSascha Wildner slot_printf(slot, "Blk size: 0x%08x | Blk cnt: 0x%08x\n",
146d6644679SSascha Wildner RD2(slot, SDHCI_BLOCK_SIZE), RD2(slot, SDHCI_BLOCK_COUNT));
147d6644679SSascha Wildner slot_printf(slot, "Argument: 0x%08x | Trn mode: 0x%08x\n",
148d6644679SSascha Wildner RD4(slot, SDHCI_ARGUMENT), RD2(slot, SDHCI_TRANSFER_MODE));
149d6644679SSascha Wildner slot_printf(slot, "Present: 0x%08x | Host ctl: 0x%08x\n",
150d6644679SSascha Wildner RD4(slot, SDHCI_PRESENT_STATE), RD1(slot, SDHCI_HOST_CONTROL));
151d6644679SSascha Wildner slot_printf(slot, "Power: 0x%08x | Blk gap: 0x%08x\n",
152d6644679SSascha Wildner RD1(slot, SDHCI_POWER_CONTROL), RD1(slot, SDHCI_BLOCK_GAP_CONTROL));
153d6644679SSascha Wildner slot_printf(slot, "Wake-up: 0x%08x | Clock: 0x%08x\n",
154d6644679SSascha Wildner RD1(slot, SDHCI_WAKE_UP_CONTROL), RD2(slot, SDHCI_CLOCK_CONTROL));
155d6644679SSascha Wildner slot_printf(slot, "Timeout: 0x%08x | Int stat: 0x%08x\n",
156d6644679SSascha Wildner RD1(slot, SDHCI_TIMEOUT_CONTROL), RD4(slot, SDHCI_INT_STATUS));
157d6644679SSascha Wildner slot_printf(slot, "Int enab: 0x%08x | Sig enab: 0x%08x\n",
158d6644679SSascha Wildner RD4(slot, SDHCI_INT_ENABLE), RD4(slot, SDHCI_SIGNAL_ENABLE));
1599e3c8f63SImre Vadász slot_printf(slot, "AC12 err: 0x%08x | Host ctl2: 0x%08x\n",
1609e3c8f63SImre Vadász RD2(slot, SDHCI_ACMD12_ERR), RD2(slot, SDHCI_HOST_CONTROL2));
1619e3c8f63SImre Vadász slot_printf(slot, "Caps: 0x%08x | Caps2: 0x%08x\n",
1629e3c8f63SImre Vadász RD4(slot, SDHCI_CAPABILITIES), RD4(slot, SDHCI_CAPABILITIES2));
1639e3c8f63SImre Vadász slot_printf(slot, "Max curr: 0x%08x | ADMA err: 0x%08x\n",
1649e3c8f63SImre Vadász RD4(slot, SDHCI_MAX_CURRENT), RD1(slot, SDHCI_ADMA_ERR));
1659e3c8f63SImre Vadász slot_printf(slot, "ADMA addr: 0x%08x | Slot int: 0x%08x\n",
1669e3c8f63SImre Vadász RD4(slot, SDHCI_ADMA_ADDRESS_LOW), RD2(slot, SDHCI_SLOT_INT_STATUS));
167d6644679SSascha Wildner
168d6644679SSascha Wildner slot_printf(slot,
169d6644679SSascha Wildner "===========================================\n");
170d6644679SSascha Wildner }
171d6644679SSascha Wildner
172d6644679SSascha Wildner static void
sdhci_reset(struct sdhci_slot * slot,uint8_t mask)173d6644679SSascha Wildner sdhci_reset(struct sdhci_slot *slot, uint8_t mask)
174d6644679SSascha Wildner {
175d6644679SSascha Wildner int timeout;
1764d3ae590SImre Vadász uint32_t clock;
177d6644679SSascha Wildner
178e355a444SMarkus Pfeiffer if (slot->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
1797f03b76bSImre Vadász if (!SDHCI_GET_CARD_PRESENT(slot->bus, slot))
180d6644679SSascha Wildner return;
181d6644679SSascha Wildner }
182d6644679SSascha Wildner
183d6644679SSascha Wildner /* Some controllers need this kick or reset won't work. */
184d6644679SSascha Wildner if ((mask & SDHCI_RESET_ALL) == 0 &&
185e355a444SMarkus Pfeiffer (slot->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET)) {
186d6644679SSascha Wildner /* This is to force an update */
187d6644679SSascha Wildner clock = slot->clock;
188d6644679SSascha Wildner slot->clock = 0;
189d6644679SSascha Wildner sdhci_set_clock(slot, clock);
190d6644679SSascha Wildner }
191d6644679SSascha Wildner
192d6644679SSascha Wildner if (mask & SDHCI_RESET_ALL) {
193d6644679SSascha Wildner slot->clock = 0;
194d6644679SSascha Wildner slot->power = 0;
195d6644679SSascha Wildner }
196d6644679SSascha Wildner
1974f29f02bSImre Vadász WR1(slot, SDHCI_SOFTWARE_RESET, mask);
1984f29f02bSImre Vadász
1994f29f02bSImre Vadász if (slot->quirks & SDHCI_QUIRK_WAITFOR_RESET_ASSERTED) {
2004f29f02bSImre Vadász /*
2014f29f02bSImre Vadász * Resets on TI OMAPs and AM335x are incompatible with SDHCI
2024f29f02bSImre Vadász * specification. The reset bit has internal propagation delay,
2034f29f02bSImre Vadász * so a fast read after write returns 0 even if reset process is
2044f29f02bSImre Vadász * in progress. The workaround is to poll for 1 before polling
2054f29f02bSImre Vadász * for 0. In the worst case, if we miss seeing it asserted the
2064f29f02bSImre Vadász * time we spent waiting is enough to ensure the reset finishes.
2074f29f02bSImre Vadász */
2084f29f02bSImre Vadász timeout = 10000;
2094f29f02bSImre Vadász while ((RD1(slot, SDHCI_SOFTWARE_RESET) & mask) != mask) {
2104f29f02bSImre Vadász if (timeout <= 0)
2114f29f02bSImre Vadász break;
2124f29f02bSImre Vadász timeout--;
2134f29f02bSImre Vadász DELAY(1);
2144f29f02bSImre Vadász }
2154f29f02bSImre Vadász }
2164f29f02bSImre Vadász
217d6644679SSascha Wildner /* Wait max 100 ms */
2184f29f02bSImre Vadász timeout = 10000;
219d6644679SSascha Wildner /* Controller clears the bits when it's done */
2204f29f02bSImre Vadász while (RD1(slot, SDHCI_SOFTWARE_RESET) & mask) {
2214f29f02bSImre Vadász if (timeout <= 0) {
2224f29f02bSImre Vadász slot_printf(slot, "Reset 0x%x never completed.\n",
2234f29f02bSImre Vadász mask);
224d6644679SSascha Wildner sdhci_dumpregs(slot);
225d6644679SSascha Wildner return;
226d6644679SSascha Wildner }
227d6644679SSascha Wildner timeout--;
2284f29f02bSImre Vadász DELAY(10);
229d6644679SSascha Wildner }
230d6644679SSascha Wildner }
231d6644679SSascha Wildner
232d6644679SSascha Wildner static void
sdhci_init(struct sdhci_slot * slot)233d6644679SSascha Wildner sdhci_init(struct sdhci_slot *slot)
234d6644679SSascha Wildner {
235d6644679SSascha Wildner
236d6644679SSascha Wildner sdhci_reset(slot, SDHCI_RESET_ALL);
237d6644679SSascha Wildner
238d6644679SSascha Wildner /* Enable interrupts. */
239d6644679SSascha Wildner slot->intmask = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT |
240d6644679SSascha Wildner SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX |
241d6644679SSascha Wildner SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT |
242d6644679SSascha Wildner SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL |
243d6644679SSascha Wildner SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE |
2446bdaa92fSImre Vadász SDHCI_INT_ACMD12ERR | SDHCI_INT_ADMAERR;
24565704a46SImre Vadász if (!(slot->opt & SDHCI_SLOT_EMBEDDED))
24665704a46SImre Vadász slot->intmask |= SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT;
247d6644679SSascha Wildner WR4(slot, SDHCI_INT_ENABLE, slot->intmask);
248d6644679SSascha Wildner WR4(slot, SDHCI_SIGNAL_ENABLE, slot->intmask);
249d6644679SSascha Wildner }
250d6644679SSascha Wildner
251d6644679SSascha Wildner static void
sdhci_set_clock(struct sdhci_slot * slot,uint32_t clock)252d6644679SSascha Wildner sdhci_set_clock(struct sdhci_slot *slot, uint32_t clock)
253d6644679SSascha Wildner {
2544f29f02bSImre Vadász uint32_t clk_base;
2554f29f02bSImre Vadász uint32_t clk_sel;
256d6644679SSascha Wildner uint32_t res;
257d6644679SSascha Wildner uint16_t clk;
258e355a444SMarkus Pfeiffer uint16_t div;
259d6644679SSascha Wildner int timeout;
260d6644679SSascha Wildner
261d6644679SSascha Wildner if (clock == slot->clock)
262d6644679SSascha Wildner return;
263d6644679SSascha Wildner slot->clock = clock;
264d6644679SSascha Wildner
265d6644679SSascha Wildner /* Turn off the clock. */
266e355a444SMarkus Pfeiffer clk = RD2(slot, SDHCI_CLOCK_CONTROL);
267e355a444SMarkus Pfeiffer WR2(slot, SDHCI_CLOCK_CONTROL, clk & ~SDHCI_CLOCK_CARD_EN);
2684d3ae590SImre Vadász /* If no clock requested - leave it so. */
269d6644679SSascha Wildner if (clock == 0)
270d6644679SSascha Wildner return;
271e355a444SMarkus Pfeiffer
2724f29f02bSImre Vadász /* Determine the clock base frequency */
2734f29f02bSImre Vadász clk_base = slot->max_clk;
2744f29f02bSImre Vadász if (slot->quirks & SDHCI_QUIRK_BCM577XX_400KHZ_CLKSRC) {
2754f29f02bSImre Vadász clk_sel = RD2(slot, BCM577XX_HOST_CONTROL) & BCM577XX_CTRL_CLKSEL_MASK;
2764f29f02bSImre Vadász
2774f29f02bSImre Vadász /* Select clock source appropriate for the requested frequency. */
2784f29f02bSImre Vadász if ((clk_base / BCM577XX_DEFAULT_MAX_DIVIDER) > clock) {
2794f29f02bSImre Vadász clk_base = BCM577XX_ALT_CLOCK_BASE;
2804f29f02bSImre Vadász clk_sel |= (BCM577XX_CTRL_CLKSEL_64MHZ << BCM577XX_CTRL_CLKSEL_SHIFT);
2814f29f02bSImre Vadász } else {
2824f29f02bSImre Vadász clk_sel |= (BCM577XX_CTRL_CLKSEL_DEFAULT << BCM577XX_CTRL_CLKSEL_SHIFT);
2834f29f02bSImre Vadász }
2844f29f02bSImre Vadász
2854f29f02bSImre Vadász WR2(slot, BCM577XX_HOST_CONTROL, clk_sel);
2864f29f02bSImre Vadász }
2874f29f02bSImre Vadász
288e355a444SMarkus Pfeiffer /* Recalculate timeout clock frequency based on the new sd clock. */
289e355a444SMarkus Pfeiffer if (slot->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)
290e355a444SMarkus Pfeiffer slot->timeout_clk = slot->clock / 1000;
291e355a444SMarkus Pfeiffer
292e355a444SMarkus Pfeiffer if (slot->version < SDHCI_SPEC_300) {
293d6644679SSascha Wildner /* Looking for highest freq <= clock. */
2944f29f02bSImre Vadász res = clk_base;
295e355a444SMarkus Pfeiffer for (div = 1; div < SDHCI_200_MAX_DIVIDER; div <<= 1) {
296d6644679SSascha Wildner if (res <= clock)
297d6644679SSascha Wildner break;
298d6644679SSascha Wildner res >>= 1;
299d6644679SSascha Wildner }
300d6644679SSascha Wildner /* Divider 1:1 is 0x00, 2:1 is 0x01, 256:1 is 0x80 ... */
301e355a444SMarkus Pfeiffer div >>= 1;
30270a02aadSImre Vadász } else {
303e355a444SMarkus Pfeiffer /* Version 3.0 divisors are multiples of two up to 1023 * 2 */
3044f29f02bSImre Vadász if (clock >= clk_base)
305e355a444SMarkus Pfeiffer div = 0;
306e355a444SMarkus Pfeiffer else {
307e355a444SMarkus Pfeiffer for (div = 2; div < SDHCI_300_MAX_DIVIDER; div += 2) {
3084f29f02bSImre Vadász if ((clk_base / div) <= clock)
309e355a444SMarkus Pfeiffer break;
310e355a444SMarkus Pfeiffer }
311e355a444SMarkus Pfeiffer }
312e355a444SMarkus Pfeiffer div >>= 1;
313e355a444SMarkus Pfeiffer }
314e355a444SMarkus Pfeiffer
315e355a444SMarkus Pfeiffer if (bootverbose || sdhci_debug)
3164f29f02bSImre Vadász slot_printf(slot, "Divider %d for freq %d (base %d)\n",
3174f29f02bSImre Vadász div, clock, clk_base);
318e355a444SMarkus Pfeiffer
319d6644679SSascha Wildner /* Now we have got divider, set it. */
320e355a444SMarkus Pfeiffer clk = (div & SDHCI_DIVIDER_MASK) << SDHCI_DIVIDER_SHIFT;
321e355a444SMarkus Pfeiffer clk |= ((div >> SDHCI_DIVIDER_MASK_LEN) & SDHCI_DIVIDER_HI_MASK)
322e355a444SMarkus Pfeiffer << SDHCI_DIVIDER_HI_SHIFT;
323e355a444SMarkus Pfeiffer
324d6644679SSascha Wildner WR2(slot, SDHCI_CLOCK_CONTROL, clk);
325d6644679SSascha Wildner /* Enable clock. */
326d6644679SSascha Wildner clk |= SDHCI_CLOCK_INT_EN;
327d6644679SSascha Wildner WR2(slot, SDHCI_CLOCK_CONTROL, clk);
328d6644679SSascha Wildner /* Wait up to 10 ms until it stabilize. */
329d6644679SSascha Wildner timeout = 10;
330d6644679SSascha Wildner while (!((clk = RD2(slot, SDHCI_CLOCK_CONTROL))
331d6644679SSascha Wildner & SDHCI_CLOCK_INT_STABLE)) {
332d6644679SSascha Wildner if (timeout == 0) {
333d6644679SSascha Wildner slot_printf(slot,
334d6644679SSascha Wildner "Internal clock never stabilised.\n");
335d6644679SSascha Wildner sdhci_dumpregs(slot);
336d6644679SSascha Wildner return;
337d6644679SSascha Wildner }
338d6644679SSascha Wildner timeout--;
339d6644679SSascha Wildner DELAY(1000);
340d6644679SSascha Wildner }
341d6644679SSascha Wildner /* Pass clock signal to the bus. */
342d6644679SSascha Wildner clk |= SDHCI_CLOCK_CARD_EN;
343d6644679SSascha Wildner WR2(slot, SDHCI_CLOCK_CONTROL, clk);
344d6644679SSascha Wildner }
345d6644679SSascha Wildner
346d6644679SSascha Wildner static void
sdhci_set_power(struct sdhci_slot * slot,u_char power)347d6644679SSascha Wildner sdhci_set_power(struct sdhci_slot *slot, u_char power)
348d6644679SSascha Wildner {
349d6644679SSascha Wildner uint8_t pwr;
350d6644679SSascha Wildner
351d6644679SSascha Wildner if (slot->power == power)
352d6644679SSascha Wildner return;
353e355a444SMarkus Pfeiffer
354d6644679SSascha Wildner slot->power = power;
355d6644679SSascha Wildner
356d6644679SSascha Wildner /* Turn off the power. */
357d6644679SSascha Wildner pwr = 0;
358d6644679SSascha Wildner WR1(slot, SDHCI_POWER_CONTROL, pwr);
3594d3ae590SImre Vadász /* If power down requested - leave it so. */
360d6644679SSascha Wildner if (power == 0)
361d6644679SSascha Wildner return;
362d6644679SSascha Wildner /* Set voltage. */
363d6644679SSascha Wildner switch (1 << power) {
364d6644679SSascha Wildner case MMC_OCR_LOW_VOLTAGE:
365d6644679SSascha Wildner pwr |= SDHCI_POWER_180;
366d6644679SSascha Wildner break;
367d6644679SSascha Wildner case MMC_OCR_290_300:
368d6644679SSascha Wildner case MMC_OCR_300_310:
369d6644679SSascha Wildner pwr |= SDHCI_POWER_300;
370d6644679SSascha Wildner break;
371d6644679SSascha Wildner case MMC_OCR_320_330:
372d6644679SSascha Wildner case MMC_OCR_330_340:
373d6644679SSascha Wildner pwr |= SDHCI_POWER_330;
374d6644679SSascha Wildner break;
375d6644679SSascha Wildner }
376d6644679SSascha Wildner WR1(slot, SDHCI_POWER_CONTROL, pwr);
377d6644679SSascha Wildner /* Turn on the power. */
378d6644679SSascha Wildner pwr |= SDHCI_POWER_ON;
379d6644679SSascha Wildner WR1(slot, SDHCI_POWER_CONTROL, pwr);
380d6644679SSascha Wildner }
381d6644679SSascha Wildner
382d6644679SSascha Wildner static void
sdhci_read_block_pio(struct sdhci_slot * slot)383d6644679SSascha Wildner sdhci_read_block_pio(struct sdhci_slot *slot)
384d6644679SSascha Wildner {
385d6644679SSascha Wildner uint32_t data;
386d6644679SSascha Wildner char *buffer;
387d6644679SSascha Wildner size_t left;
388d6644679SSascha Wildner
389d6644679SSascha Wildner buffer = slot->curcmd->data->data;
390d6644679SSascha Wildner buffer += slot->offset;
391d6644679SSascha Wildner /* Transfer one block at a time. */
392d6644679SSascha Wildner left = min(512, slot->curcmd->data->len - slot->offset);
393d6644679SSascha Wildner slot->offset += left;
394d6644679SSascha Wildner
395d6644679SSascha Wildner /* If we are too fast, broken controllers return zeroes. */
396e355a444SMarkus Pfeiffer if (slot->quirks & SDHCI_QUIRK_BROKEN_TIMINGS)
397d6644679SSascha Wildner DELAY(10);
398e355a444SMarkus Pfeiffer /* Handle unaligned and aligned buffer cases. */
399d6644679SSascha Wildner if ((intptr_t)buffer & 3) {
400d6644679SSascha Wildner while (left > 3) {
401d6644679SSascha Wildner data = RD4(slot, SDHCI_BUFFER);
402d6644679SSascha Wildner buffer[0] = data;
403d6644679SSascha Wildner buffer[1] = (data >> 8);
404d6644679SSascha Wildner buffer[2] = (data >> 16);
405d6644679SSascha Wildner buffer[3] = (data >> 24);
406d6644679SSascha Wildner buffer += 4;
407d6644679SSascha Wildner left -= 4;
408d6644679SSascha Wildner }
409d6644679SSascha Wildner } else {
410e355a444SMarkus Pfeiffer RD_MULTI_4(slot, SDHCI_BUFFER,
411d6644679SSascha Wildner (uint32_t *)buffer, left >> 2);
412d6644679SSascha Wildner left &= 3;
413d6644679SSascha Wildner }
414d6644679SSascha Wildner /* Handle uneven size case. */
415d6644679SSascha Wildner if (left > 0) {
416d6644679SSascha Wildner data = RD4(slot, SDHCI_BUFFER);
417d6644679SSascha Wildner while (left > 0) {
418d6644679SSascha Wildner *(buffer++) = data;
419d6644679SSascha Wildner data >>= 8;
420d6644679SSascha Wildner left--;
421d6644679SSascha Wildner }
422d6644679SSascha Wildner }
423d6644679SSascha Wildner }
424d6644679SSascha Wildner
425d6644679SSascha Wildner static void
sdhci_write_block_pio(struct sdhci_slot * slot)426d6644679SSascha Wildner sdhci_write_block_pio(struct sdhci_slot *slot)
427d6644679SSascha Wildner {
428d6644679SSascha Wildner uint32_t data = 0;
429d6644679SSascha Wildner char *buffer;
430d6644679SSascha Wildner size_t left;
431d6644679SSascha Wildner
432d6644679SSascha Wildner buffer = slot->curcmd->data->data;
433d6644679SSascha Wildner buffer += slot->offset;
434d6644679SSascha Wildner /* Transfer one block at a time. */
435d6644679SSascha Wildner left = min(512, slot->curcmd->data->len - slot->offset);
436d6644679SSascha Wildner slot->offset += left;
437d6644679SSascha Wildner
438e355a444SMarkus Pfeiffer /* Handle unaligned and aligned buffer cases. */
439d6644679SSascha Wildner if ((intptr_t)buffer & 3) {
440d6644679SSascha Wildner while (left > 3) {
441d6644679SSascha Wildner data = buffer[0] +
442d6644679SSascha Wildner (buffer[1] << 8) +
443d6644679SSascha Wildner (buffer[2] << 16) +
444d6644679SSascha Wildner (buffer[3] << 24);
445d6644679SSascha Wildner left -= 4;
446d6644679SSascha Wildner buffer += 4;
447d6644679SSascha Wildner WR4(slot, SDHCI_BUFFER, data);
448d6644679SSascha Wildner }
449d6644679SSascha Wildner } else {
450e355a444SMarkus Pfeiffer WR_MULTI_4(slot, SDHCI_BUFFER,
451d6644679SSascha Wildner (uint32_t *)buffer, left >> 2);
452d6644679SSascha Wildner left &= 3;
453d6644679SSascha Wildner }
454d6644679SSascha Wildner /* Handle uneven size case. */
455d6644679SSascha Wildner if (left > 0) {
456d6644679SSascha Wildner while (left > 0) {
457d6644679SSascha Wildner data <<= 8;
458d6644679SSascha Wildner data += *(buffer++);
459d6644679SSascha Wildner left--;
460d6644679SSascha Wildner }
461d6644679SSascha Wildner WR4(slot, SDHCI_BUFFER, data);
462d6644679SSascha Wildner }
463d6644679SSascha Wildner }
464d6644679SSascha Wildner
465d6644679SSascha Wildner static void
sdhci_transfer_pio(struct sdhci_slot * slot)466d6644679SSascha Wildner sdhci_transfer_pio(struct sdhci_slot *slot)
467d6644679SSascha Wildner {
468d6644679SSascha Wildner
469d6644679SSascha Wildner /* Read as many blocks as possible. */
470d6644679SSascha Wildner if (slot->curcmd->data->flags & MMC_DATA_READ) {
471d6644679SSascha Wildner while (RD4(slot, SDHCI_PRESENT_STATE) &
472d6644679SSascha Wildner SDHCI_DATA_AVAILABLE) {
473d6644679SSascha Wildner sdhci_read_block_pio(slot);
474d6644679SSascha Wildner if (slot->offset >= slot->curcmd->data->len)
475d6644679SSascha Wildner break;
476d6644679SSascha Wildner }
477d6644679SSascha Wildner } else {
478d6644679SSascha Wildner while (RD4(slot, SDHCI_PRESENT_STATE) &
479d6644679SSascha Wildner SDHCI_SPACE_AVAILABLE) {
480d6644679SSascha Wildner sdhci_write_block_pio(slot);
481d6644679SSascha Wildner if (slot->offset >= slot->curcmd->data->len)
482d6644679SSascha Wildner break;
483d6644679SSascha Wildner }
484d6644679SSascha Wildner }
485d6644679SSascha Wildner }
486d6644679SSascha Wildner
487d6644679SSascha Wildner static void
sdhci_card_delay(void * arg)488d6644679SSascha Wildner sdhci_card_delay(void *arg)
489d6644679SSascha Wildner {
490d6644679SSascha Wildner struct sdhci_slot *slot = arg;
491d6644679SSascha Wildner
492b40601faSMatthew Dillon taskqueue_enqueue(taskqueue_swi_mp, &slot->card_task);
493d6644679SSascha Wildner }
494d6644679SSascha Wildner
495d6644679SSascha Wildner static void
sdhci_card_task(void * arg,int pending)496d6644679SSascha Wildner sdhci_card_task(void *arg, int pending)
497d6644679SSascha Wildner {
498d6644679SSascha Wildner struct sdhci_slot *slot = arg;
499d6644679SSascha Wildner
500d6644679SSascha Wildner SDHCI_LOCK(slot);
5017f03b76bSImre Vadász if (SDHCI_GET_CARD_PRESENT(slot->bus, slot)) {
502d6644679SSascha Wildner if (slot->dev == NULL) {
503d6644679SSascha Wildner /* If card is present - attach mmc bus. */
504e355a444SMarkus Pfeiffer slot->dev = device_add_child(slot->bus, "mmc", -1);
505d6644679SSascha Wildner device_set_ivars(slot->dev, slot);
506d6644679SSascha Wildner SDHCI_UNLOCK(slot);
507d6644679SSascha Wildner device_probe_and_attach(slot->dev);
508d6644679SSascha Wildner } else
509d6644679SSascha Wildner SDHCI_UNLOCK(slot);
510d6644679SSascha Wildner } else {
511d6644679SSascha Wildner if (slot->dev != NULL) {
512d6644679SSascha Wildner /* If no card present - detach mmc bus. */
513d6644679SSascha Wildner device_t d = slot->dev;
514d6644679SSascha Wildner slot->dev = NULL;
515d6644679SSascha Wildner SDHCI_UNLOCK(slot);
516e355a444SMarkus Pfeiffer device_delete_child(slot->bus, d);
517d6644679SSascha Wildner } else
518d6644679SSascha Wildner SDHCI_UNLOCK(slot);
519d6644679SSascha Wildner }
520d6644679SSascha Wildner }
521d6644679SSascha Wildner
5224013580eSImre Vadász static int
sdhci_dma_alloc(struct sdhci_slot * slot)5234013580eSImre Vadász sdhci_dma_alloc(struct sdhci_slot *slot)
5244013580eSImre Vadász {
5254013580eSImre Vadász int err;
5264013580eSImre Vadász
5274013580eSImre Vadász /* Allocate DMA memory for SDMA. */
5284013580eSImre Vadász err = bus_dmamem_coherent(bus_get_dma_tag(slot->bus),
5294013580eSImre Vadász DMA_BLOCK_SIZE, 0, BUS_SPACE_MAXADDR_32BIT,
5304013580eSImre Vadász BUS_SPACE_MAXADDR, DMA_BLOCK_SIZE, BUS_DMA_NOWAIT,
5314013580eSImre Vadász &slot->sdma_mem);
5324013580eSImre Vadász if (err != 0) {
5334013580eSImre Vadász device_printf(slot->bus, "Can't alloc DMA memory for SDMA\n");
5344013580eSImre Vadász goto done;
5354013580eSImre Vadász }
5364013580eSImre Vadász
5374013580eSImre Vadász /* Allocate DMA memory for 32bit ADMA2 descriptors. */
5384013580eSImre Vadász err = bus_dmamem_coherent(bus_get_dma_tag(slot->bus),
5394013580eSImre Vadász 4, 0, BUS_SPACE_MAXADDR_32BIT,
5404013580eSImre Vadász BUS_SPACE_MAXADDR, SDHCI_ADMA2_DESCBUF_SIZE, BUS_DMA_NOWAIT,
5414013580eSImre Vadász &slot->adma2_descs);
5424013580eSImre Vadász if (err != 0) {
5434013580eSImre Vadász device_printf(slot->bus,
5444013580eSImre Vadász "Can't alloc DMA memory for ADMA2 descriptors\n");
5454013580eSImre Vadász goto error1;
5464013580eSImre Vadász }
5474013580eSImre Vadász
5484013580eSImre Vadász /* Allocate DMA tag for 32bit ADMA2 data buffer */
5494013580eSImre Vadász err = bus_dma_tag_create(bus_get_dma_tag(slot->bus),
550*030b0c8cSMichael Neumann 4, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
5514013580eSImre Vadász MAXPHYS, SDHCI_ADMA2_DESC_COUNT,
5524013580eSImre Vadász MIN(MAXPHYS, SDHCI_ADMA2_MAX_SEGSIZE),
5534013580eSImre Vadász BUS_DMA_ALLOCNOW | BUS_DMA_ALLOCALL,
5544013580eSImre Vadász &slot->adma2_tag);
5554013580eSImre Vadász if (err != 0) {
5564013580eSImre Vadász device_printf(slot->bus, "Can't create DMA tag for ADMA2\n");
5574013580eSImre Vadász goto error2;
5584013580eSImre Vadász }
5594013580eSImre Vadász
5604013580eSImre Vadász /* Allocate DMA map for ADMA2 data buffer */
5614013580eSImre Vadász err = bus_dmamap_create(slot->adma2_tag, BUS_DMA_NOWAIT,
5624013580eSImre Vadász &slot->adma2_map);
5634013580eSImre Vadász if (err != 0) {
5644013580eSImre Vadász device_printf(slot->bus, "Can't create DMA map for ADMA2\n");
5654013580eSImre Vadász goto error3;
5664013580eSImre Vadász }
5674013580eSImre Vadász
5684013580eSImre Vadász return (0);
5694013580eSImre Vadász
5704013580eSImre Vadász error3:
5714013580eSImre Vadász bus_dma_tag_destroy(slot->adma2_tag);
5724013580eSImre Vadász error2:
5734013580eSImre Vadász sdhci_dmamem_free(&slot->adma2_descs);
5744013580eSImre Vadász error1:
5754013580eSImre Vadász sdhci_dmamem_free(&slot->sdma_mem);
5764013580eSImre Vadász done:
5774013580eSImre Vadász return (err);
5784013580eSImre Vadász }
5794013580eSImre Vadász
5804013580eSImre Vadász static void
sdhci_dmamem_free(bus_dmamem_t * dma)5814013580eSImre Vadász sdhci_dmamem_free(bus_dmamem_t *dma)
5824013580eSImre Vadász {
5834013580eSImre Vadász bus_dmamap_unload(dma->dmem_tag, dma->dmem_map);
5844013580eSImre Vadász bus_dmamem_free(dma->dmem_tag, dma->dmem_addr, dma->dmem_map);
5854013580eSImre Vadász bus_dma_tag_destroy(dma->dmem_tag);
5864013580eSImre Vadász }
5874013580eSImre Vadász
5884013580eSImre Vadász static void
sdhci_dma_free(struct sdhci_slot * slot)5894013580eSImre Vadász sdhci_dma_free(struct sdhci_slot *slot)
5904013580eSImre Vadász {
5914013580eSImre Vadász bus_dmamap_destroy(slot->adma2_tag, slot->adma2_map);
5924013580eSImre Vadász bus_dma_tag_destroy(slot->adma2_tag);
5934013580eSImre Vadász sdhci_dmamem_free(&slot->sdma_mem);
5944013580eSImre Vadász sdhci_dmamem_free(&slot->adma2_descs);
5954013580eSImre Vadász }
5964013580eSImre Vadász
5974013580eSImre Vadász static void
sdhci_adma2_getaddr(void * arg,bus_dma_segment_t * segs,int nsegs,int error)5984013580eSImre Vadász sdhci_adma2_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
5994013580eSImre Vadász {
6004013580eSImre Vadász struct sdhci_slot *slot = arg;
6014013580eSImre Vadász bus_dmamem_t *descmem = &slot->adma2_descs;
6024013580eSImre Vadász struct sdhci_adma2_desc32 *descs = (void *)descmem->dmem_addr;
6034013580eSImre Vadász int i;
6044013580eSImre Vadász
6054013580eSImre Vadász if (error != 0) {
6064013580eSImre Vadász /* This signals, that loading was unsuccessful */
6074013580eSImre Vadász memset(&descs[0], 0, sizeof(*descs));
6084013580eSImre Vadász return;
6094013580eSImre Vadász }
6104013580eSImre Vadász
6114013580eSImre Vadász for (i = 0; i < nsegs; i++) {
6124013580eSImre Vadász descs[i].address = segs[i].ds_addr;
6134013580eSImre Vadász /*
6144013580eSImre Vadász * The 65536 segment length case is broken in some sdhc host
6154013580eSImre Vadász * controllers, so we actually use a maximum segment length
6164013580eSImre Vadász * of 32768 for the DMA mapping and ds_len should be at most
6174013580eSImre Vadász * 32768 here.
6184013580eSImre Vadász */
6194013580eSImre Vadász if (segs[i].ds_len == 65536)
6204013580eSImre Vadász descs[i].length = 0;
6214013580eSImre Vadász else
6224013580eSImre Vadász descs[i].length = segs[i].ds_len;
6234013580eSImre Vadász descs[i].attribute =
6244013580eSImre Vadász SDHCI_ADMA2_ATTR_VALID | SDHCI_ADMA2_ATTR_OP_TRAN;
6254013580eSImre Vadász }
6264013580eSImre Vadász descs[nsegs-1].attribute |= SDHCI_ADMA2_ATTR_END;
6274013580eSImre Vadász /* If there is room left, explicitly add an invalid descriptor. */
6284013580eSImre Vadász if (nsegs < SDHCI_ADMA2_DESC_COUNT)
6294013580eSImre Vadász memset(&descs[nsegs], 0, sizeof(*descs));
6304013580eSImre Vadász }
6314013580eSImre Vadász
632e355a444SMarkus Pfeiffer int
sdhci_init_slot(device_t dev,struct sdhci_slot * slot,int num)633e355a444SMarkus Pfeiffer sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num)
634d6644679SSascha Wildner {
6357ba10b88SImre Vadász uint32_t caps, caps2, freq, host_caps;
636e355a444SMarkus Pfeiffer int err;
637d6644679SSascha Wildner
638d6644679SSascha Wildner SDHCI_LOCK_INIT(slot);
639e355a444SMarkus Pfeiffer slot->num = num;
640e355a444SMarkus Pfeiffer slot->bus = dev;
641e355a444SMarkus Pfeiffer
6424013580eSImre Vadász err = sdhci_dma_alloc(slot);
643d6644679SSascha Wildner if (err != 0) {
644d6644679SSascha Wildner SDHCI_LOCK_DESTROY(slot);
645e355a444SMarkus Pfeiffer return (err);
646d6644679SSascha Wildner }
647e355a444SMarkus Pfeiffer
648d6644679SSascha Wildner /* Initialize slot. */
649d6644679SSascha Wildner sdhci_init(slot);
650e355a444SMarkus Pfeiffer slot->version = (RD2(slot, SDHCI_HOST_VERSION)
651e355a444SMarkus Pfeiffer >> SDHCI_SPEC_VER_SHIFT) & SDHCI_SPEC_VER_MASK;
6527ba10b88SImre Vadász if (slot->quirks & SDHCI_QUIRK_MISSING_CAPS) {
653e355a444SMarkus Pfeiffer caps = slot->caps;
6547ba10b88SImre Vadász caps2 = slot->caps2;
6557ba10b88SImre Vadász } else {
656d6644679SSascha Wildner caps = RD4(slot, SDHCI_CAPABILITIES);
6577ba10b88SImre Vadász if (slot->version >= SDHCI_SPEC_300)
6587ba10b88SImre Vadász caps2 = RD4(slot, SDHCI_CAPABILITIES2);
6597ba10b88SImre Vadász else
6607ba10b88SImre Vadász caps2 = 0;
6617ba10b88SImre Vadász }
66265704a46SImre Vadász if (slot->version >= SDHCI_SPEC_300) {
66365704a46SImre Vadász if ((caps & SDHCI_SLOTTYPE_MASK) != SDHCI_SLOTTYPE_REMOVABLE &&
66465704a46SImre Vadász (caps & SDHCI_SLOTTYPE_MASK) != SDHCI_SLOTTYPE_EMBEDDED) {
66565704a46SImre Vadász device_printf(dev,
66665704a46SImre Vadász "Driver doesn't support shared bus slots\n");
66765704a46SImre Vadász sdhci_dma_free(slot);
66865704a46SImre Vadász SDHCI_LOCK_DESTROY(slot);
66965704a46SImre Vadász return (1);
67065704a46SImre Vadász } else if ((caps & SDHCI_SLOTTYPE_MASK) ==
67165704a46SImre Vadász SDHCI_SLOTTYPE_EMBEDDED) {
67265704a46SImre Vadász slot->opt |= SDHCI_SLOT_EMBEDDED;
67365704a46SImre Vadász }
67465704a46SImre Vadász }
675d6644679SSascha Wildner /* Calculate base clock frequency. */
676e355a444SMarkus Pfeiffer if (slot->version >= SDHCI_SPEC_300)
677e355a444SMarkus Pfeiffer freq = (caps & SDHCI_CLOCK_V3_BASE_MASK) >>
678e355a444SMarkus Pfeiffer SDHCI_CLOCK_BASE_SHIFT;
679e355a444SMarkus Pfeiffer else
680e355a444SMarkus Pfeiffer freq = (caps & SDHCI_CLOCK_BASE_MASK) >>
681e355a444SMarkus Pfeiffer SDHCI_CLOCK_BASE_SHIFT;
682e355a444SMarkus Pfeiffer if (freq != 0)
683e355a444SMarkus Pfeiffer slot->max_clk = freq * 1000000;
684e355a444SMarkus Pfeiffer /*
685e355a444SMarkus Pfeiffer * If the frequency wasn't in the capabilities and the hardware driver
686e355a444SMarkus Pfeiffer * hasn't already set max_clk we're probably not going to work right
687e355a444SMarkus Pfeiffer * with an assumption, so complain about it.
688e355a444SMarkus Pfeiffer */
689d6644679SSascha Wildner if (slot->max_clk == 0) {
690e355a444SMarkus Pfeiffer slot->max_clk = SDHCI_DEFAULT_MAX_FREQ * 1000000;
691d6644679SSascha Wildner device_printf(dev, "Hardware doesn't specify base clock "
692e355a444SMarkus Pfeiffer "frequency, using %dMHz as default.\n", SDHCI_DEFAULT_MAX_FREQ);
693d6644679SSascha Wildner }
694d6644679SSascha Wildner /* Calculate timeout clock frequency. */
695e355a444SMarkus Pfeiffer if (slot->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK) {
696e355a444SMarkus Pfeiffer slot->timeout_clk = slot->max_clk / 1000;
697e355a444SMarkus Pfeiffer } else {
698d6644679SSascha Wildner slot->timeout_clk =
699d6644679SSascha Wildner (caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;
700d6644679SSascha Wildner if (caps & SDHCI_TIMEOUT_CLK_UNIT)
701d6644679SSascha Wildner slot->timeout_clk *= 1000;
702e355a444SMarkus Pfeiffer }
703e355a444SMarkus Pfeiffer /*
704e355a444SMarkus Pfeiffer * If the frequency wasn't in the capabilities and the hardware driver
705e355a444SMarkus Pfeiffer * hasn't already set timeout_clk we'll probably work okay using the
706e355a444SMarkus Pfeiffer * max timeout, but still mention it.
707e355a444SMarkus Pfeiffer */
708e355a444SMarkus Pfeiffer if (slot->timeout_clk == 0) {
709e355a444SMarkus Pfeiffer device_printf(dev, "Hardware doesn't specify timeout clock "
710e355a444SMarkus Pfeiffer "frequency, setting BROKEN_TIMEOUT quirk.\n");
711e355a444SMarkus Pfeiffer slot->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
712e355a444SMarkus Pfeiffer }
713d6644679SSascha Wildner
714e355a444SMarkus Pfeiffer slot->host.f_min = SDHCI_MIN_FREQ(slot->bus, slot);
715d6644679SSascha Wildner slot->host.f_max = slot->max_clk;
716d6644679SSascha Wildner slot->host.host_ocr = 0;
717d6644679SSascha Wildner if (caps & SDHCI_CAN_VDD_330)
718d6644679SSascha Wildner slot->host.host_ocr |= MMC_OCR_320_330 | MMC_OCR_330_340;
719d6644679SSascha Wildner if (caps & SDHCI_CAN_VDD_300)
720d6644679SSascha Wildner slot->host.host_ocr |= MMC_OCR_290_300 | MMC_OCR_300_310;
7217ba10b88SImre Vadász /* 1.8V VDD is not supposed to be used for removable cards */
722e32f8658SSascha Wildner if ((caps & SDHCI_CAN_VDD_180) && (slot->opt & SDHCI_SLOT_EMBEDDED))
723d6644679SSascha Wildner slot->host.host_ocr |= MMC_OCR_LOW_VOLTAGE;
724d6644679SSascha Wildner if (slot->host.host_ocr == 0) {
725d6644679SSascha Wildner device_printf(dev, "Hardware doesn't report any "
726d6644679SSascha Wildner "support voltages.\n");
727d6644679SSascha Wildner }
7287ba10b88SImre Vadász host_caps = MMC_CAP_4_BIT_DATA;
7294f29f02bSImre Vadász if (caps & SDHCI_CAN_DO_8BITBUS)
7307ba10b88SImre Vadász host_caps |= MMC_CAP_8_BIT_DATA;
731d6644679SSascha Wildner if (caps & SDHCI_CAN_DO_HISPD)
7327ba10b88SImre Vadász host_caps |= MMC_CAP_HSPEED;
73385ccd313SImre Vadász if (slot->quirks & SDHCI_QUIRK_WAIT_WHILE_BUSY)
7347ba10b88SImre Vadász host_caps |= MMC_CAP_WAIT_WHILE_BUSY;
7357ba10b88SImre Vadász if (caps2 & (SDHCI_CAN_SDR50 | SDHCI_CAN_SDR104 | SDHCI_CAN_DDR50))
7367ba10b88SImre Vadász host_caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
7377ba10b88SImre Vadász if (caps2 & SDHCI_CAN_SDR104) {
7387ba10b88SImre Vadász host_caps |= MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50;
7397ba10b88SImre Vadász if (!(slot->quirks & SDHCI_QUIRK_BROKEN_MMC_HS200))
7407ba10b88SImre Vadász host_caps |= MMC_CAP_MMC_HS200;
7417ba10b88SImre Vadász } else if (caps2 & SDHCI_CAN_SDR50) {
7427ba10b88SImre Vadász host_caps |= MMC_CAP_UHS_SDR50;
7437ba10b88SImre Vadász }
7447ba10b88SImre Vadász if ((caps2 & SDHCI_CAN_DDR50) &&
7457ba10b88SImre Vadász !(slot->quirks & SDHCI_QUIRK_BROKEN_UHS_DDR50))
7467ba10b88SImre Vadász host_caps |= MMC_CAP_UHS_DDR50;
7477ba10b88SImre Vadász if (slot->quirks & SDHCI_QUIRK_MMC_DDR52)
7487ba10b88SImre Vadász host_caps |= MMC_CAP_MMC_DDR52;
7497ba10b88SImre Vadász if (slot->quirks & SDHCI_QUIRK_CAPS_BIT63_FOR_MMC_HS400 &&
7507ba10b88SImre Vadász caps2 & SDHCI_CAN_MMC_HS400)
7517ba10b88SImre Vadász host_caps |= MMC_CAP_MMC_HS400;
7527ba10b88SImre Vadász host_caps |= MMC_CAP_SIGNALING_330;
7537ba10b88SImre Vadász if (host_caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
7547ba10b88SImre Vadász MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50 |
7557ba10b88SImre Vadász MMC_CAP_MMC_DDR52_180 | MMC_CAP_MMC_HS200_180 |
7567ba10b88SImre Vadász MMC_CAP_MMC_HS400_180))
7577ba10b88SImre Vadász host_caps |= MMC_CAP_SIGNALING_180;
7587ba10b88SImre Vadász if (caps2 & SDHCI_CAN_DRIVE_TYPE_A)
7597ba10b88SImre Vadász host_caps |= MMC_CAP_DRIVER_TYPE_A;
7607ba10b88SImre Vadász if (caps2 & SDHCI_CAN_DRIVE_TYPE_C)
7617ba10b88SImre Vadász host_caps |= MMC_CAP_DRIVER_TYPE_C;
7627ba10b88SImre Vadász if (caps2 & SDHCI_CAN_DRIVE_TYPE_D)
7637ba10b88SImre Vadász host_caps |= MMC_CAP_DRIVER_TYPE_D;
7647ba10b88SImre Vadász slot->host.caps = host_caps;
7657ba10b88SImre Vadász
766d6644679SSascha Wildner /* Decide if we have usable DMA. */
767d6644679SSascha Wildner if (caps & SDHCI_CAN_DO_DMA)
76822caf486SImre Vadász slot->opt |= SDHCI_HAVE_SDMA;
7694013580eSImre Vadász if (caps & SDHCI_CAN_DO_ADMA2)
7704013580eSImre Vadász slot->opt |= SDHCI_HAVE_ADMA2;
771e355a444SMarkus Pfeiffer
7724f67f60cSImre Vadász /* Use ADMA2 only on whitelisted models, or when explicitly enabled. */
7734f67f60cSImre Vadász if (sdhci_adma2_test == 0 &&
7744f67f60cSImre Vadász (slot->quirks & SDHCI_QUIRK_WHITELIST_ADMA2) == 0) {
7754f67f60cSImre Vadász slot->opt &= ~SDHCI_HAVE_ADMA2;
7764f67f60cSImre Vadász }
7774f67f60cSImre Vadász
7784013580eSImre Vadász if (slot->quirks & SDHCI_QUIRK_BROKEN_DMA) {
77922caf486SImre Vadász slot->opt &= ~SDHCI_HAVE_SDMA;
7804013580eSImre Vadász slot->opt &= ~SDHCI_HAVE_ADMA2;
7814013580eSImre Vadász }
78222caf486SImre Vadász if (slot->quirks & SDHCI_QUIRK_FORCE_SDMA)
78322caf486SImre Vadász slot->opt |= SDHCI_HAVE_SDMA;
784d6644679SSascha Wildner
7854f67f60cSImre Vadász if (sdhci_sdma_disable)
7864f67f60cSImre Vadász slot->opt &= ~SDHCI_HAVE_SDMA;
7874f67f60cSImre Vadász if (sdhci_adma2_disable)
7884f67f60cSImre Vadász slot->opt &= ~SDHCI_HAVE_ADMA2;
7894f67f60cSImre Vadász
790e355a444SMarkus Pfeiffer /*
791e355a444SMarkus Pfeiffer * Use platform-provided transfer backend
792e355a444SMarkus Pfeiffer * with PIO as a fallback mechanism
793e355a444SMarkus Pfeiffer */
7944013580eSImre Vadász if (slot->opt & SDHCI_PLATFORM_TRANSFER) {
79522caf486SImre Vadász slot->opt &= ~SDHCI_HAVE_SDMA;
7964013580eSImre Vadász slot->opt &= ~SDHCI_HAVE_ADMA2;
7974013580eSImre Vadász }
798e355a444SMarkus Pfeiffer
799d6644679SSascha Wildner if (bootverbose || sdhci_debug) {
8007ba10b88SImre Vadász slot_printf(slot,
8017ba10b88SImre Vadász "%uMHz%s %s VDD:%s%s%s VCCQ: 3.3V%s%s DRV: B%s%s%s %s%s\n",
802d6644679SSascha Wildner slot->max_clk / 1000000,
803d6644679SSascha Wildner (caps & SDHCI_CAN_DO_HISPD) ? " HS" : "",
8047ba10b88SImre Vadász (host_caps & MMC_CAP_8_BIT_DATA) ? "8bits" :
8057ba10b88SImre Vadász ((host_caps & MMC_CAP_4_BIT_DATA) ? "4bits" : "1bit"),
806d6644679SSascha Wildner (caps & SDHCI_CAN_VDD_330) ? " 3.3V" : "",
807d6644679SSascha Wildner (caps & SDHCI_CAN_VDD_300) ? " 3.0V" : "",
8087ba10b88SImre Vadász ((caps & SDHCI_CAN_VDD_180) &&
8097ba10b88SImre Vadász (slot->opt & SDHCI_SLOT_EMBEDDED)) ? " 1.8V" : "",
8107ba10b88SImre Vadász (host_caps & MMC_CAP_SIGNALING_180) ? " 1.8V" : "",
8117ba10b88SImre Vadász (host_caps & MMC_CAP_SIGNALING_120) ? " 1.2V" : "",
8127ba10b88SImre Vadász (caps2 & SDHCI_CAN_DRIVE_TYPE_A) ? "A" : "",
8137ba10b88SImre Vadász (caps2 & SDHCI_CAN_DRIVE_TYPE_C) ? "C" : "",
8147ba10b88SImre Vadász (caps2 & SDHCI_CAN_DRIVE_TYPE_D) ? "D" : "",
8154013580eSImre Vadász (slot->opt & SDHCI_HAVE_ADMA2) ? "ADMA2" :
81665704a46SImre Vadász (slot->opt & SDHCI_HAVE_SDMA) ? "SDMA" : "PIO",
81765704a46SImre Vadász (slot->version < SDHCI_SPEC_300) ? "" :
81865704a46SImre Vadász (slot->opt & SDHCI_SLOT_EMBEDDED) ? " (embedded)" :
81965704a46SImre Vadász " (removable)");
8207ba10b88SImre Vadász if (host_caps & (MMC_CAP_MMC_DDR52 | MMC_CAP_MMC_HS200 |
8217ba10b88SImre Vadász MMC_CAP_MMC_HS400 | MMC_CAP_MMC_ENH_STROBE))
8227ba10b88SImre Vadász slot_printf(slot, "eMMC:%s%s%s%s\n",
8237ba10b88SImre Vadász (host_caps & MMC_CAP_MMC_DDR52) ? " DDR52" : "",
8247ba10b88SImre Vadász (host_caps & MMC_CAP_MMC_HS200) ? " HS200" : "",
8257ba10b88SImre Vadász (host_caps & MMC_CAP_MMC_HS400) ? " HS400" : "",
8267ba10b88SImre Vadász ((host_caps &
8277ba10b88SImre Vadász (MMC_CAP_MMC_HS400 | MMC_CAP_MMC_ENH_STROBE)) ==
8287ba10b88SImre Vadász (MMC_CAP_MMC_HS400 | MMC_CAP_MMC_ENH_STROBE)) ?
8297ba10b88SImre Vadász " HS400ES" : "");
8307ba10b88SImre Vadász if (host_caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
8317ba10b88SImre Vadász MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104))
8327ba10b88SImre Vadász slot_printf(slot, "UHS-I:%s%s%s%s%s\n",
8337ba10b88SImre Vadász (host_caps & MMC_CAP_UHS_SDR12) ? " SDR12" : "",
8347ba10b88SImre Vadász (host_caps & MMC_CAP_UHS_SDR25) ? " SDR25" : "",
8357ba10b88SImre Vadász (host_caps & MMC_CAP_UHS_SDR50) ? " SDR50" : "",
8367ba10b88SImre Vadász (host_caps & MMC_CAP_UHS_SDR104) ? " SDR104" : "",
8377ba10b88SImre Vadász (host_caps & MMC_CAP_UHS_DDR50) ? " DDR50" : "");
838d6644679SSascha Wildner sdhci_dumpregs(slot);
839d6644679SSascha Wildner }
840d6644679SSascha Wildner
8414f29f02bSImre Vadász slot->timeout = 10;
8421019f171SMatthew Dillon slot->failures = 0;
8434f29f02bSImre Vadász SYSCTL_ADD_INT(device_get_sysctl_ctx(slot->bus),
8444f29f02bSImre Vadász SYSCTL_CHILDREN(device_get_sysctl_tree(slot->bus)), OID_AUTO,
8454f29f02bSImre Vadász "timeout", CTLFLAG_RW, &slot->timeout, 0,
8464f29f02bSImre Vadász "Maximum timeout for SDHCI transfers (in secs)");
847d6644679SSascha Wildner TASK_INIT(&slot->card_task, 0, sdhci_card_task, slot);
848b40601faSMatthew Dillon callout_init_mp(&slot->card_callout);
8494f29f02bSImre Vadász callout_init_lk(&slot->timeout_callout, &slot->lock);
850d6644679SSascha Wildner return (0);
851d6644679SSascha Wildner }
852d6644679SSascha Wildner
853e355a444SMarkus Pfeiffer void
sdhci_start_slot(struct sdhci_slot * slot)854e355a444SMarkus Pfeiffer sdhci_start_slot(struct sdhci_slot *slot)
855d6644679SSascha Wildner {
856e355a444SMarkus Pfeiffer sdhci_card_task(slot, 0);
857e355a444SMarkus Pfeiffer }
858d6644679SSascha Wildner
859e355a444SMarkus Pfeiffer int
sdhci_cleanup_slot(struct sdhci_slot * slot)860e355a444SMarkus Pfeiffer sdhci_cleanup_slot(struct sdhci_slot *slot)
861e355a444SMarkus Pfeiffer {
862d6644679SSascha Wildner device_t d;
863d6644679SSascha Wildner
864e355a444SMarkus Pfeiffer callout_drain(&slot->timeout_callout);
865e355a444SMarkus Pfeiffer callout_drain(&slot->card_callout);
866b40601faSMatthew Dillon taskqueue_drain(taskqueue_swi_mp, &slot->card_task);
867d6644679SSascha Wildner
868d6644679SSascha Wildner SDHCI_LOCK(slot);
869d6644679SSascha Wildner d = slot->dev;
870d6644679SSascha Wildner slot->dev = NULL;
871d6644679SSascha Wildner SDHCI_UNLOCK(slot);
872d6644679SSascha Wildner if (d != NULL)
873e355a444SMarkus Pfeiffer device_delete_child(slot->bus, d);
874d6644679SSascha Wildner
875d6644679SSascha Wildner SDHCI_LOCK(slot);
876d6644679SSascha Wildner sdhci_reset(slot, SDHCI_RESET_ALL);
877d6644679SSascha Wildner SDHCI_UNLOCK(slot);
87822caf486SImre Vadász
8794013580eSImre Vadász sdhci_dma_free(slot);
880e355a444SMarkus Pfeiffer
881d6644679SSascha Wildner SDHCI_LOCK_DESTROY(slot);
882e355a444SMarkus Pfeiffer
883d6644679SSascha Wildner return (0);
884d6644679SSascha Wildner }
885d6644679SSascha Wildner
886e355a444SMarkus Pfeiffer int
sdhci_generic_suspend(struct sdhci_slot * slot)887e355a444SMarkus Pfeiffer sdhci_generic_suspend(struct sdhci_slot *slot)
888d6644679SSascha Wildner {
889e355a444SMarkus Pfeiffer sdhci_reset(slot, SDHCI_RESET_ALL);
890d6644679SSascha Wildner
891d6644679SSascha Wildner return (0);
892d6644679SSascha Wildner }
893d6644679SSascha Wildner
894e355a444SMarkus Pfeiffer int
sdhci_generic_resume(struct sdhci_slot * slot)895e355a444SMarkus Pfeiffer sdhci_generic_resume(struct sdhci_slot *slot)
896d6644679SSascha Wildner {
897e355a444SMarkus Pfeiffer sdhci_init(slot);
898d6644679SSascha Wildner
899e355a444SMarkus Pfeiffer return (0);
900d6644679SSascha Wildner }
901d6644679SSascha Wildner
902e355a444SMarkus Pfeiffer uint32_t
sdhci_generic_min_freq(device_t brdev __unused,struct sdhci_slot * slot)9034d3ae590SImre Vadász sdhci_generic_min_freq(device_t brdev __unused, struct sdhci_slot *slot)
904e355a444SMarkus Pfeiffer {
905e355a444SMarkus Pfeiffer if (slot->version >= SDHCI_SPEC_300)
906e355a444SMarkus Pfeiffer return (slot->max_clk / SDHCI_300_MAX_DIVIDER);
907e355a444SMarkus Pfeiffer else
908e355a444SMarkus Pfeiffer return (slot->max_clk / SDHCI_200_MAX_DIVIDER);
909e355a444SMarkus Pfeiffer }
910e355a444SMarkus Pfeiffer
9117f03b76bSImre Vadász boolean_t
sdhci_generic_get_card_present(device_t brdev __unused,struct sdhci_slot * slot)9124d3ae590SImre Vadász sdhci_generic_get_card_present(device_t brdev __unused, struct sdhci_slot *slot)
9137f03b76bSImre Vadász {
91465704a46SImre Vadász if (slot->opt & SDHCI_SLOT_EMBEDDED)
91565704a46SImre Vadász return 1;
9167f03b76bSImre Vadász
9177f03b76bSImre Vadász return (RD4(slot, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT);
9187f03b76bSImre Vadász }
9197f03b76bSImre Vadász
9207ba10b88SImre Vadász void
sdhci_generic_set_uhs_timing(device_t brdev __unused,struct sdhci_slot * slot)9217ba10b88SImre Vadász sdhci_generic_set_uhs_timing(device_t brdev __unused, struct sdhci_slot *slot)
9227ba10b88SImre Vadász {
9237ba10b88SImre Vadász struct mmc_ios *ios;
9247ba10b88SImre Vadász uint16_t hostctrl2;
9257ba10b88SImre Vadász
9267ba10b88SImre Vadász if (slot->version < SDHCI_SPEC_300)
9277ba10b88SImre Vadász return;
9287ba10b88SImre Vadász
9297ba10b88SImre Vadász ios = &slot->host.ios;
9307ba10b88SImre Vadász sdhci_set_clock(slot, 0);
9317ba10b88SImre Vadász hostctrl2 = RD2(slot, SDHCI_HOST_CONTROL2);
9327ba10b88SImre Vadász hostctrl2 &= ~SDHCI_CTRL2_UHS_MASK;
9337ba10b88SImre Vadász if (ios->timing == bus_timing_mmc_hs400 ||
9347ba10b88SImre Vadász ios->timing == bus_timing_mmc_hs400es)
9357ba10b88SImre Vadász hostctrl2 |= SDHCI_CTRL2_MMC_HS400;
9367ba10b88SImre Vadász else if (ios->clock > SD_SDR50_MAX)
9377ba10b88SImre Vadász hostctrl2 |= SDHCI_CTRL2_UHS_SDR104;
9387ba10b88SImre Vadász else if (ios->clock > SD_SDR25_MAX)
9397ba10b88SImre Vadász hostctrl2 |= SDHCI_CTRL2_UHS_SDR50;
9407ba10b88SImre Vadász else if (ios->clock > SD_SDR12_MAX) {
9417ba10b88SImre Vadász if (ios->timing == bus_timing_uhs_ddr50 ||
9427ba10b88SImre Vadász ios->timing == bus_timing_mmc_ddr52)
9437ba10b88SImre Vadász hostctrl2 |= SDHCI_CTRL2_UHS_DDR50;
9447ba10b88SImre Vadász else
9457ba10b88SImre Vadász hostctrl2 |= SDHCI_CTRL2_UHS_SDR25;
9467ba10b88SImre Vadász } else if (ios->clock > SD_MMC_CARD_ID_FREQUENCY)
9477ba10b88SImre Vadász hostctrl2 |= SDHCI_CTRL2_UHS_SDR12;
9487ba10b88SImre Vadász WR2(slot, SDHCI_HOST_CONTROL2, hostctrl2);
9497ba10b88SImre Vadász sdhci_set_clock(slot, ios->clock);
9507ba10b88SImre Vadász }
9517ba10b88SImre Vadász
952e355a444SMarkus Pfeiffer int
sdhci_generic_update_ios(device_t brdev,device_t reqdev)953e355a444SMarkus Pfeiffer sdhci_generic_update_ios(device_t brdev, device_t reqdev)
954d6644679SSascha Wildner {
955d6644679SSascha Wildner struct sdhci_slot *slot = device_get_ivars(reqdev);
956d6644679SSascha Wildner struct mmc_ios *ios = &slot->host.ios;
957d6644679SSascha Wildner
958d6644679SSascha Wildner SDHCI_LOCK(slot);
959d6644679SSascha Wildner /* Do full reset on bus power down to clear from any state. */
960d6644679SSascha Wildner if (ios->power_mode == power_off) {
961d6644679SSascha Wildner WR4(slot, SDHCI_SIGNAL_ENABLE, 0);
962d6644679SSascha Wildner sdhci_init(slot);
963d6644679SSascha Wildner }
964d6644679SSascha Wildner /* Configure the bus. */
965d6644679SSascha Wildner sdhci_set_clock(slot, ios->clock);
966d6644679SSascha Wildner sdhci_set_power(slot, (ios->power_mode == power_off) ? 0 : ios->vdd);
9674f29f02bSImre Vadász if (ios->bus_width == bus_width_8) {
9684f29f02bSImre Vadász slot->hostctrl |= SDHCI_CTRL_8BITBUS;
969d6644679SSascha Wildner slot->hostctrl &= ~SDHCI_CTRL_4BITBUS;
9704f29f02bSImre Vadász } else if (ios->bus_width == bus_width_4) {
9714f29f02bSImre Vadász slot->hostctrl &= ~SDHCI_CTRL_8BITBUS;
9724f29f02bSImre Vadász slot->hostctrl |= SDHCI_CTRL_4BITBUS;
9734f29f02bSImre Vadász } else if (ios->bus_width == bus_width_1) {
9744f29f02bSImre Vadász slot->hostctrl &= ~SDHCI_CTRL_8BITBUS;
9754f29f02bSImre Vadász slot->hostctrl &= ~SDHCI_CTRL_4BITBUS;
9764f29f02bSImre Vadász } else {
9774f29f02bSImre Vadász panic("Invalid bus width: %d", ios->bus_width);
9784f29f02bSImre Vadász }
9797ba10b88SImre Vadász if (ios->clock > SD_SDR12_MAX &&
9804f29f02bSImre Vadász !(slot->quirks & SDHCI_QUIRK_DONT_SET_HISPD_BIT))
981d6644679SSascha Wildner slot->hostctrl |= SDHCI_CTRL_HISPD;
982d6644679SSascha Wildner else
983d6644679SSascha Wildner slot->hostctrl &= ~SDHCI_CTRL_HISPD;
984d6644679SSascha Wildner WR1(slot, SDHCI_HOST_CONTROL, slot->hostctrl);
9857ba10b88SImre Vadász SDHCI_SET_UHS_TIMING(brdev, slot);
986d6644679SSascha Wildner /* Some controllers like reset after bus changes. */
987e355a444SMarkus Pfeiffer if (slot->quirks & SDHCI_QUIRK_RESET_ON_IOS)
988d6644679SSascha Wildner sdhci_reset(slot, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
989d6644679SSascha Wildner
990d6644679SSascha Wildner SDHCI_UNLOCK(slot);
991d6644679SSascha Wildner return (0);
992d6644679SSascha Wildner }
993d6644679SSascha Wildner
9947ba10b88SImre Vadász int
sdhci_generic_switch_vccq(device_t brdev __unused,device_t reqdev)9957ba10b88SImre Vadász sdhci_generic_switch_vccq(device_t brdev __unused, device_t reqdev)
9967ba10b88SImre Vadász {
9977ba10b88SImre Vadász struct sdhci_slot *slot = device_get_ivars(reqdev);
9987ba10b88SImre Vadász enum mmc_vccq vccq;
9997ba10b88SImre Vadász int err;
10007ba10b88SImre Vadász uint16_t hostctrl2;
10017ba10b88SImre Vadász
10027ba10b88SImre Vadász if (slot->version < SDHCI_SPEC_300)
10037ba10b88SImre Vadász return (0);
10047ba10b88SImre Vadász
10057ba10b88SImre Vadász err = 0;
10067ba10b88SImre Vadász vccq = slot->host.ios.vccq;
10077ba10b88SImre Vadász SDHCI_LOCK(slot);
10087ba10b88SImre Vadász sdhci_set_clock(slot, 0);
10097ba10b88SImre Vadász hostctrl2 = RD2(slot, SDHCI_HOST_CONTROL2);
10107ba10b88SImre Vadász switch (vccq) {
10117ba10b88SImre Vadász case vccq_330:
10127ba10b88SImre Vadász if (!(hostctrl2 & SDHCI_CTRL2_S18_ENABLE))
10137ba10b88SImre Vadász goto done;
10147ba10b88SImre Vadász hostctrl2 &= ~SDHCI_CTRL2_S18_ENABLE;
10157ba10b88SImre Vadász WR2(slot, SDHCI_HOST_CONTROL2, hostctrl2);
10167ba10b88SImre Vadász DELAY(5000);
10177ba10b88SImre Vadász hostctrl2 = RD2(slot, SDHCI_HOST_CONTROL2);
10187ba10b88SImre Vadász if (!(hostctrl2 & SDHCI_CTRL2_S18_ENABLE))
10197ba10b88SImre Vadász goto done;
10207ba10b88SImre Vadász err = EAGAIN;
10217ba10b88SImre Vadász break;
10227ba10b88SImre Vadász case vccq_180:
10237ba10b88SImre Vadász if (!(slot->host.caps & MMC_CAP_SIGNALING_180)) {
10247ba10b88SImre Vadász err = EINVAL;
10257ba10b88SImre Vadász goto done;
10267ba10b88SImre Vadász }
10277ba10b88SImre Vadász if (hostctrl2 & SDHCI_CTRL2_S18_ENABLE)
10287ba10b88SImre Vadász goto done;
10297ba10b88SImre Vadász hostctrl2 |= SDHCI_CTRL2_S18_ENABLE;
10307ba10b88SImre Vadász WR2(slot, SDHCI_HOST_CONTROL2, hostctrl2);
10317ba10b88SImre Vadász DELAY(5000);
10327ba10b88SImre Vadász hostctrl2 = RD2(slot, SDHCI_HOST_CONTROL2);
10337ba10b88SImre Vadász if (hostctrl2 & SDHCI_CTRL2_S18_ENABLE)
10347ba10b88SImre Vadász goto done;
10357ba10b88SImre Vadász err = EAGAIN;
10367ba10b88SImre Vadász break;
10377ba10b88SImre Vadász default:
10387ba10b88SImre Vadász slot_printf(slot,
10397ba10b88SImre Vadász "Attempt to set unsupported signaling voltage\n");
10407ba10b88SImre Vadász err = EINVAL;
10417ba10b88SImre Vadász break;
10427ba10b88SImre Vadász }
10437ba10b88SImre Vadász done:
10447ba10b88SImre Vadász sdhci_set_clock(slot, slot->host.ios.clock);
10457ba10b88SImre Vadász SDHCI_UNLOCK(slot);
10467ba10b88SImre Vadász return (err);
10477ba10b88SImre Vadász }
10487ba10b88SImre Vadász
1049d6644679SSascha Wildner static void
sdhci_req_done(struct sdhci_slot * slot)1050e355a444SMarkus Pfeiffer sdhci_req_done(struct sdhci_slot *slot)
1051e355a444SMarkus Pfeiffer {
1052e355a444SMarkus Pfeiffer struct mmc_request *req;
1053e355a444SMarkus Pfeiffer
1054e355a444SMarkus Pfeiffer if (slot->req != NULL && slot->curcmd != NULL) {
1055e355a444SMarkus Pfeiffer callout_stop(&slot->timeout_callout);
10561019f171SMatthew Dillon if (slot->curcmd->error != MMC_ERR_TIMEOUT)
10571019f171SMatthew Dillon slot->failures = 0;
1058e355a444SMarkus Pfeiffer req = slot->req;
1059e355a444SMarkus Pfeiffer slot->req = NULL;
1060e355a444SMarkus Pfeiffer slot->curcmd = NULL;
1061e355a444SMarkus Pfeiffer req->done(req);
1062e355a444SMarkus Pfeiffer }
1063e355a444SMarkus Pfeiffer }
1064e355a444SMarkus Pfeiffer
1065e355a444SMarkus Pfeiffer static void
sdhci_timeout(void * arg)1066e355a444SMarkus Pfeiffer sdhci_timeout(void *arg)
1067e355a444SMarkus Pfeiffer {
1068e355a444SMarkus Pfeiffer struct sdhci_slot *slot = arg;
1069e355a444SMarkus Pfeiffer
1070e355a444SMarkus Pfeiffer if (slot->curcmd != NULL) {
10714f29f02bSImre Vadász slot_printf(slot, " Controller timeout\n");
10724f29f02bSImre Vadász sdhci_dumpregs(slot);
1073e355a444SMarkus Pfeiffer sdhci_reset(slot, SDHCI_RESET_CMD|SDHCI_RESET_DATA);
1074e355a444SMarkus Pfeiffer slot->curcmd->error = MMC_ERR_TIMEOUT;
1075e355a444SMarkus Pfeiffer sdhci_req_done(slot);
10764f29f02bSImre Vadász } else {
10774f29f02bSImre Vadász slot_printf(slot, " Spurious timeout - no active command\n");
1078e355a444SMarkus Pfeiffer }
1079e355a444SMarkus Pfeiffer }
1080e355a444SMarkus Pfeiffer
1081e355a444SMarkus Pfeiffer static void
sdhci_set_transfer_mode(struct sdhci_slot * slot,struct mmc_data * data)10824d3ae590SImre Vadász sdhci_set_transfer_mode(struct sdhci_slot *slot, struct mmc_data *data)
1083d6644679SSascha Wildner {
1084d6644679SSascha Wildner uint16_t mode;
1085d6644679SSascha Wildner
1086d6644679SSascha Wildner if (data == NULL)
1087d6644679SSascha Wildner return;
1088d6644679SSascha Wildner
1089d6644679SSascha Wildner mode = SDHCI_TRNS_BLK_CNT_EN;
1090d6644679SSascha Wildner if (data->len > 512)
1091d6644679SSascha Wildner mode |= SDHCI_TRNS_MULTI;
1092d6644679SSascha Wildner if (data->flags & MMC_DATA_READ)
1093d6644679SSascha Wildner mode |= SDHCI_TRNS_READ;
1094d6644679SSascha Wildner if (slot->req->stop)
1095d6644679SSascha Wildner mode |= SDHCI_TRNS_ACMD12;
10964013580eSImre Vadász if (slot->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA2))
1097d6644679SSascha Wildner mode |= SDHCI_TRNS_DMA;
1098d6644679SSascha Wildner
1099d6644679SSascha Wildner WR2(slot, SDHCI_TRANSFER_MODE, mode);
1100d6644679SSascha Wildner }
1101d6644679SSascha Wildner
1102d6644679SSascha Wildner static void
sdhci_start_command(struct sdhci_slot * slot,struct mmc_command * cmd)1103d6644679SSascha Wildner sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
1104d6644679SSascha Wildner {
1105d6644679SSascha Wildner int flags, timeout;
110605475029SImre Vadász uint32_t mask;
1107d6644679SSascha Wildner
1108d6644679SSascha Wildner slot->curcmd = cmd;
1109d6644679SSascha Wildner slot->cmd_done = 0;
1110d6644679SSascha Wildner
1111d6644679SSascha Wildner cmd->error = MMC_ERR_NONE;
1112d6644679SSascha Wildner
1113d6644679SSascha Wildner /* This flags combination is not supported by controller. */
1114d6644679SSascha Wildner if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
1115d6644679SSascha Wildner slot_printf(slot, "Unsupported response type!\n");
1116d6644679SSascha Wildner cmd->error = MMC_ERR_FAILED;
1117e355a444SMarkus Pfeiffer sdhci_req_done(slot);
1118d6644679SSascha Wildner return;
1119d6644679SSascha Wildner }
1120d6644679SSascha Wildner
11214d3ae590SImre Vadász /*
11224d3ae590SImre Vadász * Do not issue command if there is no card, clock or power.
11234d3ae590SImre Vadász * Controller will not detect timeout without clock active.
11244d3ae590SImre Vadász */
11257f03b76bSImre Vadász if (!SDHCI_GET_CARD_PRESENT(slot->bus, slot) ||
1126d6644679SSascha Wildner slot->power == 0 ||
1127d6644679SSascha Wildner slot->clock == 0) {
1128d6644679SSascha Wildner cmd->error = MMC_ERR_FAILED;
1129e355a444SMarkus Pfeiffer sdhci_req_done(slot);
1130d6644679SSascha Wildner return;
1131d6644679SSascha Wildner }
1132d6644679SSascha Wildner /* Always wait for free CMD bus. */
1133d6644679SSascha Wildner mask = SDHCI_CMD_INHIBIT;
1134d6644679SSascha Wildner /* Wait for free DAT if we have data or busy signal. */
1135d6644679SSascha Wildner if (cmd->data || (cmd->flags & MMC_RSP_BUSY))
1136d6644679SSascha Wildner mask |= SDHCI_DAT_INHIBIT;
1137d6644679SSascha Wildner /* We shouldn't wait for DAT for stop commands. */
1138d6644679SSascha Wildner if (cmd == slot->req->stop)
1139d6644679SSascha Wildner mask &= ~SDHCI_DAT_INHIBIT;
1140e355a444SMarkus Pfeiffer /*
1141e355a444SMarkus Pfeiffer * Wait for bus no more then 250 ms. Typically there will be no wait
1142e355a444SMarkus Pfeiffer * here at all, but when writing a crash dump we may be bypassing the
1143e355a444SMarkus Pfeiffer * host platform's interrupt handler, and in some cases that handler
1144e355a444SMarkus Pfeiffer * may be working around hardware quirks such as not respecting r1b
1145e355a444SMarkus Pfeiffer * busy indications. In those cases, this wait-loop serves the purpose
1146e355a444SMarkus Pfeiffer * of waiting for the prior command and data transfers to be done, and
1147e355a444SMarkus Pfeiffer * SD cards are allowed to take up to 250ms for write and erase ops.
1148e355a444SMarkus Pfeiffer * (It's usually more like 20-30ms in the real world.)
1149e355a444SMarkus Pfeiffer */
1150e355a444SMarkus Pfeiffer timeout = 250;
115105475029SImre Vadász while (mask & RD4(slot, SDHCI_PRESENT_STATE)) {
1152d6644679SSascha Wildner if (timeout == 0) {
1153d6644679SSascha Wildner slot_printf(slot, "Controller never released "
1154d6644679SSascha Wildner "inhibit bit(s).\n");
1155d6644679SSascha Wildner sdhci_dumpregs(slot);
1156d6644679SSascha Wildner cmd->error = MMC_ERR_FAILED;
1157e355a444SMarkus Pfeiffer sdhci_req_done(slot);
1158d6644679SSascha Wildner return;
1159d6644679SSascha Wildner }
1160d6644679SSascha Wildner timeout--;
1161d6644679SSascha Wildner DELAY(1000);
1162d6644679SSascha Wildner }
1163d6644679SSascha Wildner
1164d6644679SSascha Wildner /* Prepare command flags. */
1165d6644679SSascha Wildner if (!(cmd->flags & MMC_RSP_PRESENT))
1166d6644679SSascha Wildner flags = SDHCI_CMD_RESP_NONE;
1167d6644679SSascha Wildner else if (cmd->flags & MMC_RSP_136)
1168d6644679SSascha Wildner flags = SDHCI_CMD_RESP_LONG;
1169d6644679SSascha Wildner else if (cmd->flags & MMC_RSP_BUSY)
1170d6644679SSascha Wildner flags = SDHCI_CMD_RESP_SHORT_BUSY;
1171d6644679SSascha Wildner else
1172d6644679SSascha Wildner flags = SDHCI_CMD_RESP_SHORT;
1173d6644679SSascha Wildner if (cmd->flags & MMC_RSP_CRC)
1174d6644679SSascha Wildner flags |= SDHCI_CMD_CRC;
1175d6644679SSascha Wildner if (cmd->flags & MMC_RSP_OPCODE)
1176d6644679SSascha Wildner flags |= SDHCI_CMD_INDEX;
1177d6644679SSascha Wildner if (cmd->data)
1178d6644679SSascha Wildner flags |= SDHCI_CMD_DATA;
1179d6644679SSascha Wildner if (cmd->opcode == MMC_STOP_TRANSMISSION)
1180d6644679SSascha Wildner flags |= SDHCI_CMD_TYPE_ABORT;
1181d6644679SSascha Wildner /* Prepare data. */
1182d6644679SSascha Wildner sdhci_start_data(slot, cmd->data);
1183d6644679SSascha Wildner /*
1184d6644679SSascha Wildner * Interrupt aggregation: To reduce total number of interrupts
1185d6644679SSascha Wildner * group response interrupt with data interrupt when possible.
118685ccd313SImre Vadász * If there is going to be a data interrupt, mask the response one.
1187d6644679SSascha Wildner */
1188d6644679SSascha Wildner if (slot->data_done == 0) {
1189d6644679SSascha Wildner WR4(slot, SDHCI_SIGNAL_ENABLE,
1190d6644679SSascha Wildner slot->intmask &= ~SDHCI_INT_RESPONSE);
1191d6644679SSascha Wildner }
1192d6644679SSascha Wildner /* Set command argument. */
1193d6644679SSascha Wildner WR4(slot, SDHCI_ARGUMENT, cmd->arg);
1194d6644679SSascha Wildner /* Set data transfer mode. */
1195d6644679SSascha Wildner sdhci_set_transfer_mode(slot, cmd->data);
1196d6644679SSascha Wildner /* Start command. */
1197e355a444SMarkus Pfeiffer WR2(slot, SDHCI_COMMAND_FLAGS, (cmd->opcode << 8) | (flags & 0xff));
11981019f171SMatthew Dillon
11991019f171SMatthew Dillon /*
12001019f171SMatthew Dillon * Start timeout callout. Timeout is dropped to 2 seconds with
12011019f171SMatthew Dillon * repeated controller timeouts.
12021019f171SMatthew Dillon */
12031019f171SMatthew Dillon if (slot->failures)
12041019f171SMatthew Dillon timeout = slot->timeout / 5;
12051019f171SMatthew Dillon else
12061019f171SMatthew Dillon timeout = slot->timeout;
12071019f171SMatthew Dillon if (timeout < 2)
12081019f171SMatthew Dillon timeout = 2;
12091019f171SMatthew Dillon callout_reset(&slot->timeout_callout, timeout * hz,
12104f29f02bSImre Vadász sdhci_timeout, slot);
1211d6644679SSascha Wildner }
1212d6644679SSascha Wildner
1213d6644679SSascha Wildner static void
sdhci_finish_command(struct sdhci_slot * slot)1214d6644679SSascha Wildner sdhci_finish_command(struct sdhci_slot *slot)
1215d6644679SSascha Wildner {
1216d6644679SSascha Wildner int i;
1217d6644679SSascha Wildner
1218d6644679SSascha Wildner slot->cmd_done = 1;
1219d6644679SSascha Wildner /* Interrupt aggregation: Restore command interrupt.
1220d6644679SSascha Wildner * Main restore point for the case when command interrupt
1221d6644679SSascha Wildner * happened first. */
1222d6644679SSascha Wildner WR4(slot, SDHCI_SIGNAL_ENABLE, slot->intmask |= SDHCI_INT_RESPONSE);
1223d6644679SSascha Wildner /* In case of error - reset host and return. */
1224d6644679SSascha Wildner if (slot->curcmd->error) {
1225d6644679SSascha Wildner sdhci_reset(slot, SDHCI_RESET_CMD);
1226d6644679SSascha Wildner sdhci_reset(slot, SDHCI_RESET_DATA);
1227d6644679SSascha Wildner sdhci_start(slot);
1228d6644679SSascha Wildner return;
1229d6644679SSascha Wildner }
1230d6644679SSascha Wildner /* If command has response - fetch it. */
1231d6644679SSascha Wildner if (slot->curcmd->flags & MMC_RSP_PRESENT) {
1232d6644679SSascha Wildner if (slot->curcmd->flags & MMC_RSP_136) {
1233d6644679SSascha Wildner /* CRC is stripped so we need one byte shift. */
1234d6644679SSascha Wildner uint8_t extra = 0;
1235d6644679SSascha Wildner for (i = 0; i < 4; i++) {
1236d6644679SSascha Wildner uint32_t val = RD4(slot, SDHCI_RESPONSE + i * 4);
1237e355a444SMarkus Pfeiffer if (slot->quirks & SDHCI_QUIRK_DONT_SHIFT_RESPONSE) {
1238e355a444SMarkus Pfeiffer slot->curcmd->resp[3 - i] = val;
1239e355a444SMarkus Pfeiffer } else {
1240e355a444SMarkus Pfeiffer slot->curcmd->resp[3 - i] =
1241e355a444SMarkus Pfeiffer (val << 8) | extra;
1242d6644679SSascha Wildner extra = val >> 24;
1243d6644679SSascha Wildner }
1244e355a444SMarkus Pfeiffer }
1245e355a444SMarkus Pfeiffer } else {
1246d6644679SSascha Wildner slot->curcmd->resp[0] = RD4(slot, SDHCI_RESPONSE);
1247d6644679SSascha Wildner }
1248e355a444SMarkus Pfeiffer }
1249d6644679SSascha Wildner /* If data ready - finish. */
1250d6644679SSascha Wildner if (slot->data_done)
1251d6644679SSascha Wildner sdhci_start(slot);
1252d6644679SSascha Wildner }
1253d6644679SSascha Wildner
1254d6644679SSascha Wildner static void
sdhci_start_data(struct sdhci_slot * slot,struct mmc_data * data)1255d6644679SSascha Wildner sdhci_start_data(struct sdhci_slot *slot, struct mmc_data *data)
1256d6644679SSascha Wildner {
1257d6644679SSascha Wildner uint32_t target_timeout, current_timeout;
1258d6644679SSascha Wildner uint8_t div;
1259d6644679SSascha Wildner
1260d6644679SSascha Wildner if (data == NULL && (slot->curcmd->flags & MMC_RSP_BUSY) == 0) {
1261d6644679SSascha Wildner slot->data_done = 1;
1262d6644679SSascha Wildner return;
1263d6644679SSascha Wildner }
1264d6644679SSascha Wildner
1265d6644679SSascha Wildner slot->data_done = 0;
1266d6644679SSascha Wildner
1267d6644679SSascha Wildner /* Calculate and set data timeout.*/
1268d6644679SSascha Wildner /* XXX: We should have this from mmc layer, now assume 1 sec. */
1269e355a444SMarkus Pfeiffer if (slot->quirks & SDHCI_QUIRK_BROKEN_TIMEOUT_VAL) {
1270e355a444SMarkus Pfeiffer div = 0xe;
1271e355a444SMarkus Pfeiffer } else {
1272d6644679SSascha Wildner target_timeout = 1000000;
1273d6644679SSascha Wildner div = 0;
1274d6644679SSascha Wildner current_timeout = (1 << 13) * 1000 / slot->timeout_clk;
1275e355a444SMarkus Pfeiffer while (current_timeout < target_timeout && div < 0xE) {
1276e355a444SMarkus Pfeiffer ++div;
1277d6644679SSascha Wildner current_timeout <<= 1;
1278d6644679SSascha Wildner }
1279d6644679SSascha Wildner /* Compensate for an off-by-one error in the CaFe chip.*/
1280e355a444SMarkus Pfeiffer if (div < 0xE &&
1281e355a444SMarkus Pfeiffer (slot->quirks & SDHCI_QUIRK_INCR_TIMEOUT_CONTROL)) {
1282e355a444SMarkus Pfeiffer ++div;
1283e355a444SMarkus Pfeiffer }
1284d6644679SSascha Wildner }
1285d6644679SSascha Wildner WR1(slot, SDHCI_TIMEOUT_CONTROL, div);
1286d6644679SSascha Wildner
1287d6644679SSascha Wildner if (data == NULL)
1288d6644679SSascha Wildner return;
1289d6644679SSascha Wildner
12904013580eSImre Vadász /* Use DMA if possible. Prefer ADMA2 over SDMA. */
12914013580eSImre Vadász if ((slot->opt & SDHCI_HAVE_ADMA2)) {
12924013580eSImre Vadász slot->flags |= SDHCI_USE_ADMA2;
12934013580eSImre Vadász slot->flags &= ~SDHCI_USE_SDMA;
12944013580eSImre Vadász } else if ((slot->opt & SDHCI_HAVE_SDMA)) {
129522caf486SImre Vadász slot->flags |= SDHCI_USE_SDMA;
12964013580eSImre Vadász slot->flags &= ~SDHCI_USE_ADMA2;
12974013580eSImre Vadász }
129822caf486SImre Vadász /* If data is small, broken DMA may return zeroes instead of data. */
1299e355a444SMarkus Pfeiffer if ((slot->quirks & SDHCI_QUIRK_BROKEN_TIMINGS) &&
13004013580eSImre Vadász (data->len <= 512)) {
130122caf486SImre Vadász slot->flags &= ~SDHCI_USE_SDMA;
13024013580eSImre Vadász slot->flags &= ~SDHCI_USE_ADMA2;
13034013580eSImre Vadász }
1304d6644679SSascha Wildner /* Some controllers require even block sizes. */
1305e355a444SMarkus Pfeiffer if ((slot->quirks & SDHCI_QUIRK_32BIT_DMA_SIZE) &&
13064013580eSImre Vadász ((data->len) & 0x3)) {
130722caf486SImre Vadász slot->flags &= ~SDHCI_USE_SDMA;
13084013580eSImre Vadász slot->flags &= ~SDHCI_USE_ADMA2;
13094013580eSImre Vadász }
1310d6644679SSascha Wildner /* Load DMA buffer. */
13114013580eSImre Vadász if (slot->flags & SDHCI_USE_ADMA2) {
13124013580eSImre Vadász bus_dmamem_t *descmem = &slot->adma2_descs;
13134013580eSImre Vadász struct sdhci_adma2_desc32 *descs = (void *)descmem->dmem_addr;
13144013580eSImre Vadász int err;
13154013580eSImre Vadász
13164013580eSImre Vadász /* It shouldn't really be possible for this to fail */
13174013580eSImre Vadász err = bus_dmamap_load(slot->adma2_tag, slot->adma2_map,
13184013580eSImre Vadász data->data, data->len, sdhci_adma2_getaddr, slot,
13194013580eSImre Vadász dumping ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK);
13204013580eSImre Vadász if (err != 0) {
13214013580eSImre Vadász device_printf(slot->bus,
13224013580eSImre Vadász "Dma load for ADMA2 fail: %d\n", err);
13234013580eSImre Vadász } else if (descs[0].address == 0) {
13244013580eSImre Vadász device_printf(slot->bus,
13254013580eSImre Vadász "Dma load for ADMA2 fail, segment constraints\n");
13264013580eSImre Vadász }
13274013580eSImre Vadász if (err != 0 || descs[0].address == 0) {
13284013580eSImre Vadász /* fallback to PIO for this request */
13294013580eSImre Vadász slot->flags &= ~SDHCI_USE_ADMA2;
13304013580eSImre Vadász goto pio_fallback;
13314013580eSImre Vadász }
13324013580eSImre Vadász /* sync dma descriptors */
13334013580eSImre Vadász bus_dmamap_sync(descmem->dmem_tag, descmem->dmem_map,
13344013580eSImre Vadász BUS_DMASYNC_PREWRITE);
13354013580eSImre Vadász /* sync data buffers */
13364013580eSImre Vadász if (data->flags & MMC_DATA_READ) {
13374013580eSImre Vadász bus_dmamap_sync(slot->adma2_tag, slot->adma2_map,
13384013580eSImre Vadász BUS_DMASYNC_PREREAD);
13394013580eSImre Vadász } else {
13404013580eSImre Vadász bus_dmamap_sync(slot->adma2_tag, slot->adma2_map,
13414013580eSImre Vadász BUS_DMASYNC_PREWRITE);
13424013580eSImre Vadász }
13434013580eSImre Vadász WR4(slot, SDHCI_ADMA_ADDRESS_LOW, descmem->dmem_busaddr);
13444013580eSImre Vadász if ((slot->hostctrl & SDHCI_CTRL_DMA_MASK) !=
13454013580eSImre Vadász SDHCI_CTRL_ADMA2) {
13464013580eSImre Vadász slot->hostctrl &= ~SDHCI_CTRL_DMA_MASK;
13474013580eSImre Vadász slot->hostctrl |= SDHCI_CTRL_ADMA2;
13484013580eSImre Vadász WR1(slot, SDHCI_HOST_CONTROL, slot->hostctrl);
13494013580eSImre Vadász }
13504013580eSImre Vadász /* We don't expect any DMA_END interrupts with ADMA2 */
13514013580eSImre Vadász WR4(slot, SDHCI_SIGNAL_ENABLE,
13524013580eSImre Vadász slot->intmask &= ~SDHCI_INT_DMA_END);
13534013580eSImre Vadász } else if (slot->flags & SDHCI_USE_SDMA) {
135422caf486SImre Vadász bus_dmamem_t *sdma = &slot->sdma_mem;
135522caf486SImre Vadász
1356d6644679SSascha Wildner if (data->flags & MMC_DATA_READ) {
135722caf486SImre Vadász bus_dmamap_sync(sdma->dmem_tag, sdma->dmem_map,
13584f29f02bSImre Vadász BUS_DMASYNC_PREREAD);
1359d6644679SSascha Wildner } else {
136022caf486SImre Vadász memcpy(sdma->dmem_addr, data->data,
13614f29f02bSImre Vadász (data->len < DMA_BLOCK_SIZE) ?
13624f29f02bSImre Vadász data->len : DMA_BLOCK_SIZE);
136322caf486SImre Vadász bus_dmamap_sync(sdma->dmem_tag, sdma->dmem_map,
13644f29f02bSImre Vadász BUS_DMASYNC_PREWRITE);
1365d6644679SSascha Wildner }
136622caf486SImre Vadász WR4(slot, SDHCI_SDMA_ADDRESS, sdma->dmem_busaddr);
13674013580eSImre Vadász if ((slot->hostctrl & SDHCI_CTRL_DMA_MASK) !=
13684013580eSImre Vadász SDHCI_CTRL_SDMA) {
13694013580eSImre Vadász slot->hostctrl &= ~SDHCI_CTRL_DMA_MASK;
13704013580eSImre Vadász slot->hostctrl |= SDHCI_CTRL_SDMA;
13714013580eSImre Vadász WR1(slot, SDHCI_HOST_CONTROL, slot->hostctrl);
13724013580eSImre Vadász }
1373d6644679SSascha Wildner /* Interrupt aggregation: Mask border interrupt
1374d6644679SSascha Wildner * for the last page and unmask else. */
1375d6644679SSascha Wildner if (data->len == DMA_BLOCK_SIZE)
1376d6644679SSascha Wildner slot->intmask &= ~SDHCI_INT_DMA_END;
1377d6644679SSascha Wildner else
1378d6644679SSascha Wildner slot->intmask |= SDHCI_INT_DMA_END;
1379d6644679SSascha Wildner WR4(slot, SDHCI_SIGNAL_ENABLE, slot->intmask);
1380d6644679SSascha Wildner }
13814013580eSImre Vadász pio_fallback:
13824013580eSImre Vadász /* Current data offset for both PIO and SDMA. */
1383d6644679SSascha Wildner slot->offset = 0;
13844013580eSImre Vadász /* Set block size and for SDMA request IRQ on 4K border. */
1385d6644679SSascha Wildner WR2(slot, SDHCI_BLOCK_SIZE,
1386d6644679SSascha Wildner SDHCI_MAKE_BLKSZ(DMA_BOUNDARY, (data->len < 512)?data->len:512));
1387d6644679SSascha Wildner /* Set block count. */
1388d6644679SSascha Wildner WR2(slot, SDHCI_BLOCK_COUNT, (data->len + 511) / 512);
1389d6644679SSascha Wildner }
1390d6644679SSascha Wildner
1391e355a444SMarkus Pfeiffer void
sdhci_finish_data(struct sdhci_slot * slot)1392d6644679SSascha Wildner sdhci_finish_data(struct sdhci_slot *slot)
1393d6644679SSascha Wildner {
1394d6644679SSascha Wildner struct mmc_data *data = slot->curcmd->data;
1395d6644679SSascha Wildner
1396d6644679SSascha Wildner /* Interrupt aggregation: Restore command interrupt.
1397e355a444SMarkus Pfeiffer * Auxiliary restore point for the case when data interrupt
1398d6644679SSascha Wildner * happened first. */
1399d6644679SSascha Wildner if (!slot->cmd_done) {
1400d6644679SSascha Wildner WR4(slot, SDHCI_SIGNAL_ENABLE,
1401d6644679SSascha Wildner slot->intmask |= SDHCI_INT_RESPONSE);
1402d6644679SSascha Wildner }
1403d6644679SSascha Wildner /* Unload rest of data from DMA buffer. */
14044013580eSImre Vadász if (!slot->data_done && (slot->flags & SDHCI_USE_ADMA2)) {
14054013580eSImre Vadász bus_dmamem_t *descmem = &slot->adma2_descs;
14064013580eSImre Vadász
14074013580eSImre Vadász bus_dmamap_sync(descmem->dmem_tag, descmem->dmem_map,
14084013580eSImre Vadász BUS_DMASYNC_POSTWRITE);
14094013580eSImre Vadász if (data->flags & MMC_DATA_READ) {
14104013580eSImre Vadász bus_dmamap_sync(slot->adma2_tag, slot->adma2_map,
14114013580eSImre Vadász BUS_DMASYNC_POSTREAD);
14124013580eSImre Vadász } else {
14134013580eSImre Vadász bus_dmamap_sync(slot->adma2_tag, slot->adma2_map,
14144013580eSImre Vadász BUS_DMASYNC_POSTWRITE);
14154013580eSImre Vadász }
14164013580eSImre Vadász bus_dmamap_unload(slot->adma2_tag, slot->adma2_map);
14174013580eSImre Vadász } else if (!slot->data_done && (slot->flags & SDHCI_USE_SDMA)) {
141822caf486SImre Vadász bus_dmamem_t *sdma = &slot->sdma_mem;
141922caf486SImre Vadász
1420d6644679SSascha Wildner if (data->flags & MMC_DATA_READ) {
1421d6644679SSascha Wildner size_t left = data->len - slot->offset;
142222caf486SImre Vadász bus_dmamap_sync(sdma->dmem_tag, sdma->dmem_map,
1423e355a444SMarkus Pfeiffer BUS_DMASYNC_POSTREAD);
142422caf486SImre Vadász memcpy((u_char*)data->data + slot->offset,
142522caf486SImre Vadász sdma->dmem_addr,
1426d6644679SSascha Wildner (left < DMA_BLOCK_SIZE)?left:DMA_BLOCK_SIZE);
14274013580eSImre Vadász } else {
142822caf486SImre Vadász bus_dmamap_sync(sdma->dmem_tag, sdma->dmem_map,
1429e355a444SMarkus Pfeiffer BUS_DMASYNC_POSTWRITE);
1430d6644679SSascha Wildner }
14314013580eSImre Vadász }
14324f29f02bSImre Vadász slot->data_done = 1;
14334013580eSImre Vadász /* If there was an error - reset the host. */
1434d6644679SSascha Wildner if (slot->curcmd->error) {
1435d6644679SSascha Wildner sdhci_reset(slot, SDHCI_RESET_CMD);
1436d6644679SSascha Wildner sdhci_reset(slot, SDHCI_RESET_DATA);
1437d6644679SSascha Wildner sdhci_start(slot);
1438d6644679SSascha Wildner return;
1439d6644679SSascha Wildner }
1440d6644679SSascha Wildner /* If we already have command response - finish. */
1441d6644679SSascha Wildner if (slot->cmd_done)
1442d6644679SSascha Wildner sdhci_start(slot);
1443d6644679SSascha Wildner }
1444d6644679SSascha Wildner
1445d6644679SSascha Wildner static void
sdhci_start(struct sdhci_slot * slot)1446d6644679SSascha Wildner sdhci_start(struct sdhci_slot *slot)
1447d6644679SSascha Wildner {
1448d6644679SSascha Wildner struct mmc_request *req;
1449d6644679SSascha Wildner
1450d6644679SSascha Wildner req = slot->req;
1451d6644679SSascha Wildner if (req == NULL)
1452d6644679SSascha Wildner return;
1453d6644679SSascha Wildner
1454d6644679SSascha Wildner if (!(slot->flags & CMD_STARTED)) {
1455d6644679SSascha Wildner slot->flags |= CMD_STARTED;
1456d6644679SSascha Wildner sdhci_start_command(slot, req->cmd);
1457d6644679SSascha Wildner return;
1458d6644679SSascha Wildner }
1459d6644679SSascha Wildner /* We don't need this until using Auto-CMD12 feature
1460d6644679SSascha Wildner if (!(slot->flags & STOP_STARTED) && req->stop) {
1461d6644679SSascha Wildner slot->flags |= STOP_STARTED;
1462d6644679SSascha Wildner sdhci_start_command(slot, req->stop);
1463d6644679SSascha Wildner return;
1464d6644679SSascha Wildner }
1465d6644679SSascha Wildner */
1466d6644679SSascha Wildner if (sdhci_debug > 1)
1467d6644679SSascha Wildner slot_printf(slot, "result: %d\n", req->cmd->error);
1468d6644679SSascha Wildner if (!req->cmd->error &&
1469e355a444SMarkus Pfeiffer (slot->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)) {
1470d6644679SSascha Wildner sdhci_reset(slot, SDHCI_RESET_CMD);
1471d6644679SSascha Wildner sdhci_reset(slot, SDHCI_RESET_DATA);
1472d6644679SSascha Wildner }
1473d6644679SSascha Wildner
1474e355a444SMarkus Pfeiffer sdhci_req_done(slot);
1475d6644679SSascha Wildner }
1476d6644679SSascha Wildner
1477e355a444SMarkus Pfeiffer int
sdhci_generic_request(device_t brdev __unused,device_t reqdev,struct mmc_request * req)14784d3ae590SImre Vadász sdhci_generic_request(device_t brdev __unused, device_t reqdev,
14794d3ae590SImre Vadász struct mmc_request *req)
1480d6644679SSascha Wildner {
1481d6644679SSascha Wildner struct sdhci_slot *slot = device_get_ivars(reqdev);
1482d6644679SSascha Wildner
1483d6644679SSascha Wildner SDHCI_LOCK(slot);
1484d6644679SSascha Wildner if (slot->req != NULL) {
1485d6644679SSascha Wildner SDHCI_UNLOCK(slot);
1486d6644679SSascha Wildner return (EBUSY);
1487d6644679SSascha Wildner }
1488d6644679SSascha Wildner if (sdhci_debug > 1) {
1489d6644679SSascha Wildner slot_printf(slot, "CMD%u arg %#x flags %#x dlen %u dflags %#x\n",
1490d6644679SSascha Wildner req->cmd->opcode, req->cmd->arg, req->cmd->flags,
1491d6644679SSascha Wildner (req->cmd->data)?(u_int)req->cmd->data->len:0,
1492d6644679SSascha Wildner (req->cmd->data)?req->cmd->data->flags:0);
1493d6644679SSascha Wildner }
1494d6644679SSascha Wildner slot->req = req;
1495d6644679SSascha Wildner slot->flags = 0;
1496d6644679SSascha Wildner sdhci_start(slot);
1497d6644679SSascha Wildner SDHCI_UNLOCK(slot);
1498d6644679SSascha Wildner if (dumping) {
1499d6644679SSascha Wildner while (slot->req != NULL) {
1500e355a444SMarkus Pfeiffer sdhci_generic_intr(slot);
1501d6644679SSascha Wildner DELAY(10);
1502d6644679SSascha Wildner }
1503d6644679SSascha Wildner }
1504d6644679SSascha Wildner return (0);
1505d6644679SSascha Wildner }
1506d6644679SSascha Wildner
1507e355a444SMarkus Pfeiffer int
sdhci_generic_get_ro(device_t brdev __unused,device_t reqdev)15084d3ae590SImre Vadász sdhci_generic_get_ro(device_t brdev __unused, device_t reqdev)
1509d6644679SSascha Wildner {
1510d6644679SSascha Wildner struct sdhci_slot *slot = device_get_ivars(reqdev);
1511d6644679SSascha Wildner uint32_t val;
1512d6644679SSascha Wildner
1513d6644679SSascha Wildner SDHCI_LOCK(slot);
1514d6644679SSascha Wildner val = RD4(slot, SDHCI_PRESENT_STATE);
1515d6644679SSascha Wildner SDHCI_UNLOCK(slot);
1516d6644679SSascha Wildner return (!(val & SDHCI_WRITE_PROTECT));
1517d6644679SSascha Wildner }
1518d6644679SSascha Wildner
1519e355a444SMarkus Pfeiffer int
sdhci_generic_acquire_host(device_t brdev __unused,device_t reqdev)15204d3ae590SImre Vadász sdhci_generic_acquire_host(device_t brdev __unused, device_t reqdev)
1521d6644679SSascha Wildner {
1522d6644679SSascha Wildner struct sdhci_slot *slot = device_get_ivars(reqdev);
1523d6644679SSascha Wildner int err = 0;
1524d6644679SSascha Wildner
1525d6644679SSascha Wildner SDHCI_LOCK(slot);
1526d6644679SSascha Wildner while (slot->bus_busy)
1527d6644679SSascha Wildner lksleep(slot, &slot->lock, 0, "sdhciah", 0);
1528d6644679SSascha Wildner slot->bus_busy++;
1529d6644679SSascha Wildner /* Activate led. */
1530d6644679SSascha Wildner WR1(slot, SDHCI_HOST_CONTROL, slot->hostctrl |= SDHCI_CTRL_LED);
1531d6644679SSascha Wildner SDHCI_UNLOCK(slot);
1532d6644679SSascha Wildner return (err);
1533d6644679SSascha Wildner }
1534d6644679SSascha Wildner
1535e355a444SMarkus Pfeiffer int
sdhci_generic_release_host(device_t brdev __unused,device_t reqdev)15364d3ae590SImre Vadász sdhci_generic_release_host(device_t brdev __unused, device_t reqdev)
1537d6644679SSascha Wildner {
1538d6644679SSascha Wildner struct sdhci_slot *slot = device_get_ivars(reqdev);
1539d6644679SSascha Wildner
1540d6644679SSascha Wildner SDHCI_LOCK(slot);
1541d6644679SSascha Wildner /* Deactivate led. */
1542d6644679SSascha Wildner WR1(slot, SDHCI_HOST_CONTROL, slot->hostctrl &= ~SDHCI_CTRL_LED);
1543d6644679SSascha Wildner slot->bus_busy--;
1544d6644679SSascha Wildner SDHCI_UNLOCK(slot);
1545d6644679SSascha Wildner wakeup(slot);
1546d6644679SSascha Wildner return (0);
1547d6644679SSascha Wildner }
1548d6644679SSascha Wildner
1549d6644679SSascha Wildner static void
sdhci_cmd_irq(struct sdhci_slot * slot,uint32_t intmask)1550d6644679SSascha Wildner sdhci_cmd_irq(struct sdhci_slot *slot, uint32_t intmask)
1551d6644679SSascha Wildner {
1552d6644679SSascha Wildner
1553d6644679SSascha Wildner if (!slot->curcmd) {
1554d6644679SSascha Wildner slot_printf(slot, "Got command interrupt 0x%08x, but "
1555d6644679SSascha Wildner "there is no active command.\n", intmask);
1556d6644679SSascha Wildner sdhci_dumpregs(slot);
1557d6644679SSascha Wildner return;
1558d6644679SSascha Wildner }
1559d6644679SSascha Wildner if (intmask & SDHCI_INT_TIMEOUT)
1560d6644679SSascha Wildner slot->curcmd->error = MMC_ERR_TIMEOUT;
1561d6644679SSascha Wildner else if (intmask & SDHCI_INT_CRC)
1562d6644679SSascha Wildner slot->curcmd->error = MMC_ERR_BADCRC;
1563d6644679SSascha Wildner else if (intmask & (SDHCI_INT_END_BIT | SDHCI_INT_INDEX))
1564d6644679SSascha Wildner slot->curcmd->error = MMC_ERR_FIFO;
1565d6644679SSascha Wildner
1566d6644679SSascha Wildner sdhci_finish_command(slot);
1567d6644679SSascha Wildner }
1568d6644679SSascha Wildner
1569d6644679SSascha Wildner static void
sdhci_data_irq(struct sdhci_slot * slot,uint32_t intmask)1570d6644679SSascha Wildner sdhci_data_irq(struct sdhci_slot *slot, uint32_t intmask)
1571d6644679SSascha Wildner {
1572d6644679SSascha Wildner
1573d6644679SSascha Wildner if (!slot->curcmd) {
1574d6644679SSascha Wildner slot_printf(slot, "Got data interrupt 0x%08x, but "
1575d6644679SSascha Wildner "there is no active command.\n", intmask);
1576d6644679SSascha Wildner sdhci_dumpregs(slot);
1577d6644679SSascha Wildner return;
1578d6644679SSascha Wildner }
1579d6644679SSascha Wildner if (slot->curcmd->data == NULL &&
1580d6644679SSascha Wildner (slot->curcmd->flags & MMC_RSP_BUSY) == 0) {
1581d6644679SSascha Wildner slot_printf(slot, "Got data interrupt 0x%08x, but "
1582d6644679SSascha Wildner "there is no active data operation.\n",
1583d6644679SSascha Wildner intmask);
1584d6644679SSascha Wildner sdhci_dumpregs(slot);
1585d6644679SSascha Wildner return;
1586d6644679SSascha Wildner }
1587d6644679SSascha Wildner if (intmask & SDHCI_INT_DATA_TIMEOUT)
1588d6644679SSascha Wildner slot->curcmd->error = MMC_ERR_TIMEOUT;
1589d6644679SSascha Wildner else if (intmask & (SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_END_BIT))
1590d6644679SSascha Wildner slot->curcmd->error = MMC_ERR_BADCRC;
1591d6644679SSascha Wildner if (slot->curcmd->data == NULL &&
1592d6644679SSascha Wildner (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL |
1593d6644679SSascha Wildner SDHCI_INT_DMA_END))) {
1594d6644679SSascha Wildner slot_printf(slot, "Got data interrupt 0x%08x, but "
1595d6644679SSascha Wildner "there is busy-only command.\n", intmask);
1596d6644679SSascha Wildner sdhci_dumpregs(slot);
1597d6644679SSascha Wildner slot->curcmd->error = MMC_ERR_INVALID;
1598d6644679SSascha Wildner }
1599d6644679SSascha Wildner if (slot->curcmd->error) {
1600d6644679SSascha Wildner /* No need to continue after any error. */
16014f29f02bSImre Vadász goto done;
1602d6644679SSascha Wildner }
1603d6644679SSascha Wildner
1604d6644679SSascha Wildner /* Handle PIO interrupt. */
1605e355a444SMarkus Pfeiffer if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)) {
1606e355a444SMarkus Pfeiffer if ((slot->opt & SDHCI_PLATFORM_TRANSFER) &&
1607e355a444SMarkus Pfeiffer SDHCI_PLATFORM_WILL_HANDLE(slot->bus, slot)) {
1608e355a444SMarkus Pfeiffer SDHCI_PLATFORM_START_TRANSFER(slot->bus, slot, &intmask);
1609e355a444SMarkus Pfeiffer slot->flags |= PLATFORM_DATA_STARTED;
1610e355a444SMarkus Pfeiffer } else
1611d6644679SSascha Wildner sdhci_transfer_pio(slot);
1612e355a444SMarkus Pfeiffer }
1613d6644679SSascha Wildner /* Handle DMA border. */
1614d6644679SSascha Wildner if (intmask & SDHCI_INT_DMA_END) {
1615d6644679SSascha Wildner struct mmc_data *data = slot->curcmd->data;
161622caf486SImre Vadász bus_dmamem_t *sdma = &slot->sdma_mem;
1617d6644679SSascha Wildner size_t left;
1618d6644679SSascha Wildner
1619d6644679SSascha Wildner /* Unload DMA buffer ... */
1620d6644679SSascha Wildner left = data->len - slot->offset;
1621d6644679SSascha Wildner if (data->flags & MMC_DATA_READ) {
162222caf486SImre Vadász bus_dmamap_sync(sdma->dmem_tag, sdma->dmem_map,
1623d6644679SSascha Wildner BUS_DMASYNC_POSTREAD);
162422caf486SImre Vadász memcpy((u_char*)data->data + slot->offset,
162522caf486SImre Vadász sdma->dmem_addr,
1626d6644679SSascha Wildner (left < DMA_BLOCK_SIZE)?left:DMA_BLOCK_SIZE);
1627d6644679SSascha Wildner } else {
162822caf486SImre Vadász bus_dmamap_sync(sdma->dmem_tag, sdma->dmem_map,
1629d6644679SSascha Wildner BUS_DMASYNC_POSTWRITE);
1630d6644679SSascha Wildner }
1631d6644679SSascha Wildner /* ... and reload it again. */
1632d6644679SSascha Wildner slot->offset += DMA_BLOCK_SIZE;
1633d6644679SSascha Wildner left = data->len - slot->offset;
1634d6644679SSascha Wildner if (data->flags & MMC_DATA_READ) {
163522caf486SImre Vadász bus_dmamap_sync(sdma->dmem_tag, sdma->dmem_map,
1636d6644679SSascha Wildner BUS_DMASYNC_PREREAD);
1637d6644679SSascha Wildner } else {
163822caf486SImre Vadász memcpy(sdma->dmem_addr,
163922caf486SImre Vadász (u_char*)data->data + slot->offset,
1640d6644679SSascha Wildner (left < DMA_BLOCK_SIZE)?left:DMA_BLOCK_SIZE);
164122caf486SImre Vadász bus_dmamap_sync(sdma->dmem_tag, sdma->dmem_map,
1642d6644679SSascha Wildner BUS_DMASYNC_PREWRITE);
1643d6644679SSascha Wildner }
1644d6644679SSascha Wildner /* Interrupt aggregation: Mask border interrupt
1645d6644679SSascha Wildner * for the last page. */
1646d6644679SSascha Wildner if (left == DMA_BLOCK_SIZE) {
1647d6644679SSascha Wildner slot->intmask &= ~SDHCI_INT_DMA_END;
1648d6644679SSascha Wildner WR4(slot, SDHCI_SIGNAL_ENABLE, slot->intmask);
1649d6644679SSascha Wildner }
1650d6644679SSascha Wildner /* Restart DMA. */
165122caf486SImre Vadász WR4(slot, SDHCI_SDMA_ADDRESS, sdma->dmem_busaddr);
1652d6644679SSascha Wildner }
1653d6644679SSascha Wildner /* We have got all data. */
1654e355a444SMarkus Pfeiffer if (intmask & SDHCI_INT_DATA_END) {
1655e355a444SMarkus Pfeiffer if (slot->flags & PLATFORM_DATA_STARTED) {
1656e355a444SMarkus Pfeiffer slot->flags &= ~PLATFORM_DATA_STARTED;
1657e355a444SMarkus Pfeiffer SDHCI_PLATFORM_FINISH_TRANSFER(slot->bus, slot);
1658e355a444SMarkus Pfeiffer } else {
1659d6644679SSascha Wildner sdhci_finish_data(slot);
1660d6644679SSascha Wildner }
1661e355a444SMarkus Pfeiffer }
16624f29f02bSImre Vadász done:
16634f29f02bSImre Vadász if (slot->curcmd != NULL && slot->curcmd->error != 0) {
16644f29f02bSImre Vadász if (slot->flags & PLATFORM_DATA_STARTED) {
16654f29f02bSImre Vadász slot->flags &= ~PLATFORM_DATA_STARTED;
16664f29f02bSImre Vadász SDHCI_PLATFORM_FINISH_TRANSFER(slot->bus, slot);
16674013580eSImre Vadász } else {
16684f29f02bSImre Vadász sdhci_finish_data(slot);
16694013580eSImre Vadász }
16704f29f02bSImre Vadász return;
16714f29f02bSImre Vadász }
1672e355a444SMarkus Pfeiffer }
1673d6644679SSascha Wildner
1674d6644679SSascha Wildner static void
sdhci_acmd_irq(struct sdhci_slot * slot)1675d6644679SSascha Wildner sdhci_acmd_irq(struct sdhci_slot *slot)
1676d6644679SSascha Wildner {
1677d6644679SSascha Wildner uint16_t err;
1678d6644679SSascha Wildner
1679d6644679SSascha Wildner err = RD4(slot, SDHCI_ACMD12_ERR);
1680d6644679SSascha Wildner if (!slot->curcmd) {
1681d6644679SSascha Wildner slot_printf(slot, "Got AutoCMD12 error 0x%04x, but "
1682d6644679SSascha Wildner "there is no active command.\n", err);
1683d6644679SSascha Wildner sdhci_dumpregs(slot);
1684d6644679SSascha Wildner return;
1685d6644679SSascha Wildner }
1686d6644679SSascha Wildner slot_printf(slot, "Got AutoCMD12 error 0x%04x\n", err);
1687d6644679SSascha Wildner sdhci_reset(slot, SDHCI_RESET_CMD);
1688d6644679SSascha Wildner }
1689d6644679SSascha Wildner
16906bdaa92fSImre Vadász static void
sdhci_adma_irq(struct sdhci_slot * slot)16916bdaa92fSImre Vadász sdhci_adma_irq(struct sdhci_slot *slot)
16926bdaa92fSImre Vadász {
16936bdaa92fSImre Vadász bus_dmamem_t *descmem = &slot->adma2_descs;
16946bdaa92fSImre Vadász struct sdhci_adma2_desc32 *desc;
16956bdaa92fSImre Vadász bus_addr_t addr = 0;
16966bdaa92fSImre Vadász uint8_t err, adma_state;
16976bdaa92fSImre Vadász
16986bdaa92fSImre Vadász err = RD1(slot, SDHCI_ADMA_ERR);
16996bdaa92fSImre Vadász if (slot->curcmd && (slot->flags & SDHCI_USE_ADMA2)) {
17006bdaa92fSImre Vadász slot_printf(slot, "Got ADMA2 error 0x%02x\n", err);
17016bdaa92fSImre Vadász } else {
17026bdaa92fSImre Vadász slot_printf(slot, "Got ADMA2 error 0x%02x, but "
17036bdaa92fSImre Vadász "there is no active command.\n", err);
17046bdaa92fSImre Vadász sdhci_dumpregs(slot);
17056bdaa92fSImre Vadász }
17066bdaa92fSImre Vadász
17076bdaa92fSImre Vadász /* Try to print the erronous ADMA2 descriptor */
17086bdaa92fSImre Vadász adma_state = err & SDHCI_ADMA_ERR_STATE_MASK;
17096bdaa92fSImre Vadász if (adma_state == SDHCI_ADMA_ERR_STATE_STOP) {
17106bdaa92fSImre Vadász addr = RD4(slot, SDHCI_ADMA_ADDRESS_LOW);
17116bdaa92fSImre Vadász if (addr > sizeof(*desc))
17126bdaa92fSImre Vadász addr -= sizeof(*desc);
17136bdaa92fSImre Vadász else
17146bdaa92fSImre Vadász addr = 0;
17156bdaa92fSImre Vadász } else if (adma_state == SDHCI_ADMA_ERR_STATE_FDS) {
17166bdaa92fSImre Vadász addr = RD4(slot, SDHCI_ADMA_ADDRESS_LOW);
17176bdaa92fSImre Vadász } else if (adma_state == SDHCI_ADMA_ERR_STATE_TFR) {
17186bdaa92fSImre Vadász addr = RD4(slot, SDHCI_ADMA_ADDRESS_LOW);
17196bdaa92fSImre Vadász if (addr > sizeof(*desc))
17206bdaa92fSImre Vadász addr -= sizeof(*desc);
17216bdaa92fSImre Vadász else
17226bdaa92fSImre Vadász addr = 0;
17236bdaa92fSImre Vadász } else {
17246bdaa92fSImre Vadász slot_printf(slot, "Invalid ADMA2 state 0x%02x\n", adma_state);
17256bdaa92fSImre Vadász }
17266bdaa92fSImre Vadász if (addr >= descmem->dmem_busaddr &&
17276bdaa92fSImre Vadász addr < descmem->dmem_busaddr + SDHCI_ADMA2_DESCBUF_SIZE) {
17286bdaa92fSImre Vadász desc = (void *) ((char *)descmem->dmem_addr +
17296bdaa92fSImre Vadász (addr - descmem->dmem_busaddr));
17306bdaa92fSImre Vadász slot_printf(slot,
17316bdaa92fSImre Vadász "Descriptor: Addr=0x%08x Length=0x%04x Attr=0x%04x\n",
17326bdaa92fSImre Vadász desc->address, desc->length, desc->attribute);
17336bdaa92fSImre Vadász }
17346bdaa92fSImre Vadász
17356bdaa92fSImre Vadász if (slot->curcmd && (slot->flags & SDHCI_USE_ADMA2)) {
17366bdaa92fSImre Vadász sdhci_reset(slot, SDHCI_RESET_CMD);
17376bdaa92fSImre Vadász }
17386bdaa92fSImre Vadász }
17396bdaa92fSImre Vadász
1740e355a444SMarkus Pfeiffer void
sdhci_generic_intr(struct sdhci_slot * slot)1741e355a444SMarkus Pfeiffer sdhci_generic_intr(struct sdhci_slot *slot)
1742d6644679SSascha Wildner {
1743d6644679SSascha Wildner uint32_t intmask;
1744d6644679SSascha Wildner
1745d6644679SSascha Wildner SDHCI_LOCK(slot);
1746d6644679SSascha Wildner /* Read slot interrupt status. */
1747d6644679SSascha Wildner intmask = RD4(slot, SDHCI_INT_STATUS);
1748d6644679SSascha Wildner if (intmask == 0 || intmask == 0xffffffff) {
1749d6644679SSascha Wildner SDHCI_UNLOCK(slot);
1750e355a444SMarkus Pfeiffer return;
1751d6644679SSascha Wildner }
1752d6644679SSascha Wildner if (sdhci_debug > 2)
1753d6644679SSascha Wildner slot_printf(slot, "Interrupt %#x\n", intmask);
1754d6644679SSascha Wildner
1755d6644679SSascha Wildner /* Handle card presence interrupts. */
1756d6644679SSascha Wildner if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
1757d6644679SSascha Wildner WR4(slot, SDHCI_INT_STATUS, intmask &
1758d6644679SSascha Wildner (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE));
1759d6644679SSascha Wildner
1760d6644679SSascha Wildner if (intmask & SDHCI_INT_CARD_REMOVE) {
1761d6644679SSascha Wildner if (bootverbose || sdhci_debug)
1762d6644679SSascha Wildner slot_printf(slot, "Card removed\n");
1763d6644679SSascha Wildner callout_stop(&slot->card_callout);
1764b40601faSMatthew Dillon taskqueue_enqueue(taskqueue_swi_mp, &slot->card_task);
1765d6644679SSascha Wildner }
1766d6644679SSascha Wildner if (intmask & SDHCI_INT_CARD_INSERT) {
1767d6644679SSascha Wildner if (bootverbose || sdhci_debug)
1768d6644679SSascha Wildner slot_printf(slot, "Card inserted\n");
1769d6644679SSascha Wildner callout_reset(&slot->card_callout, hz / 2,
1770d6644679SSascha Wildner sdhci_card_delay, slot);
1771d6644679SSascha Wildner }
1772d6644679SSascha Wildner intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
1773d6644679SSascha Wildner }
1774d6644679SSascha Wildner /* Handle command interrupts. */
1775d6644679SSascha Wildner if (intmask & SDHCI_INT_CMD_MASK) {
1776d6644679SSascha Wildner WR4(slot, SDHCI_INT_STATUS, intmask & SDHCI_INT_CMD_MASK);
1777d6644679SSascha Wildner sdhci_cmd_irq(slot, intmask & SDHCI_INT_CMD_MASK);
1778d6644679SSascha Wildner }
1779d6644679SSascha Wildner /* Handle data interrupts. */
1780d6644679SSascha Wildner if (intmask & SDHCI_INT_DATA_MASK) {
1781d6644679SSascha Wildner WR4(slot, SDHCI_INT_STATUS, intmask & SDHCI_INT_DATA_MASK);
17824f29f02bSImre Vadász /* Dont call data_irq in case of errored command */
17834f29f02bSImre Vadász if ((intmask & SDHCI_INT_CMD_ERROR_MASK) == 0)
1784d6644679SSascha Wildner sdhci_data_irq(slot, intmask & SDHCI_INT_DATA_MASK);
1785d6644679SSascha Wildner }
1786d6644679SSascha Wildner /* Handle AutoCMD12 error interrupt. */
1787d6644679SSascha Wildner if (intmask & SDHCI_INT_ACMD12ERR) {
1788d6644679SSascha Wildner WR4(slot, SDHCI_INT_STATUS, SDHCI_INT_ACMD12ERR);
1789d6644679SSascha Wildner sdhci_acmd_irq(slot);
1790d6644679SSascha Wildner }
17916bdaa92fSImre Vadász /* Handle ADMA2 error interrupt. */
17926bdaa92fSImre Vadász if (intmask & SDHCI_INT_ADMAERR) {
17936bdaa92fSImre Vadász WR4(slot, SDHCI_INT_STATUS, SDHCI_INT_ADMAERR);
17946bdaa92fSImre Vadász sdhci_adma_irq(slot);
17956bdaa92fSImre Vadász }
1796d6644679SSascha Wildner intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK);
1797d6644679SSascha Wildner intmask &= ~SDHCI_INT_ACMD12ERR;
17986bdaa92fSImre Vadász intmask &= ~SDHCI_INT_ADMAERR;
1799d6644679SSascha Wildner intmask &= ~SDHCI_INT_ERROR;
1800d6644679SSascha Wildner /* Handle bus power interrupt. */
1801d6644679SSascha Wildner if (intmask & SDHCI_INT_BUS_POWER) {
1802d6644679SSascha Wildner WR4(slot, SDHCI_INT_STATUS, SDHCI_INT_BUS_POWER);
1803d6644679SSascha Wildner slot_printf(slot,
1804d6644679SSascha Wildner "Card is consuming too much power!\n");
1805d6644679SSascha Wildner intmask &= ~SDHCI_INT_BUS_POWER;
1806d6644679SSascha Wildner }
1807d6644679SSascha Wildner /* The rest is unknown. */
1808d6644679SSascha Wildner if (intmask) {
1809d6644679SSascha Wildner WR4(slot, SDHCI_INT_STATUS, intmask);
1810d6644679SSascha Wildner slot_printf(slot, "Unexpected interrupt 0x%08x.\n",
1811d6644679SSascha Wildner intmask);
1812d6644679SSascha Wildner sdhci_dumpregs(slot);
1813d6644679SSascha Wildner }
1814d6644679SSascha Wildner
1815d6644679SSascha Wildner SDHCI_UNLOCK(slot);
1816d6644679SSascha Wildner }
1817d6644679SSascha Wildner
1818e355a444SMarkus Pfeiffer int
sdhci_generic_read_ivar(device_t bus,device_t child,int which,uintptr_t * result)1819e355a444SMarkus Pfeiffer sdhci_generic_read_ivar(device_t bus, device_t child, int which, uintptr_t *result)
1820d6644679SSascha Wildner {
1821d6644679SSascha Wildner struct sdhci_slot *slot = device_get_ivars(child);
1822d6644679SSascha Wildner
1823d6644679SSascha Wildner switch (which) {
1824d6644679SSascha Wildner default:
1825d6644679SSascha Wildner return (EINVAL);
1826d6644679SSascha Wildner case MMCBR_IVAR_BUS_MODE:
1827d6644679SSascha Wildner *(int *)result = slot->host.ios.bus_mode;
1828d6644679SSascha Wildner break;
1829d6644679SSascha Wildner case MMCBR_IVAR_BUS_WIDTH:
1830d6644679SSascha Wildner *(int *)result = slot->host.ios.bus_width;
1831d6644679SSascha Wildner break;
1832d6644679SSascha Wildner case MMCBR_IVAR_CHIP_SELECT:
1833d6644679SSascha Wildner *(int *)result = slot->host.ios.chip_select;
1834d6644679SSascha Wildner break;
1835d6644679SSascha Wildner case MMCBR_IVAR_CLOCK:
1836d6644679SSascha Wildner *(int *)result = slot->host.ios.clock;
1837d6644679SSascha Wildner break;
1838d6644679SSascha Wildner case MMCBR_IVAR_F_MIN:
1839d6644679SSascha Wildner *(int *)result = slot->host.f_min;
1840d6644679SSascha Wildner break;
1841d6644679SSascha Wildner case MMCBR_IVAR_F_MAX:
1842d6644679SSascha Wildner *(int *)result = slot->host.f_max;
1843d6644679SSascha Wildner break;
1844d6644679SSascha Wildner case MMCBR_IVAR_HOST_OCR:
1845d6644679SSascha Wildner *(int *)result = slot->host.host_ocr;
1846d6644679SSascha Wildner break;
1847d6644679SSascha Wildner case MMCBR_IVAR_MODE:
1848d6644679SSascha Wildner *(int *)result = slot->host.mode;
1849d6644679SSascha Wildner break;
1850d6644679SSascha Wildner case MMCBR_IVAR_OCR:
1851d6644679SSascha Wildner *(int *)result = slot->host.ocr;
1852d6644679SSascha Wildner break;
1853d6644679SSascha Wildner case MMCBR_IVAR_POWER_MODE:
1854d6644679SSascha Wildner *(int *)result = slot->host.ios.power_mode;
1855d6644679SSascha Wildner break;
1856d6644679SSascha Wildner case MMCBR_IVAR_VDD:
1857d6644679SSascha Wildner *(int *)result = slot->host.ios.vdd;
1858d6644679SSascha Wildner break;
18597ba10b88SImre Vadász case MMCBR_IVAR_VCCQ:
18607ba10b88SImre Vadász *result = slot->host.ios.vccq;
18617ba10b88SImre Vadász break;
1862d6644679SSascha Wildner case MMCBR_IVAR_CAPS:
1863d6644679SSascha Wildner *(int *)result = slot->host.caps;
1864d6644679SSascha Wildner break;
1865d6644679SSascha Wildner case MMCBR_IVAR_TIMING:
1866d6644679SSascha Wildner *(int *)result = slot->host.ios.timing;
1867d6644679SSascha Wildner break;
1868d6644679SSascha Wildner case MMCBR_IVAR_MAX_DATA:
1869d6644679SSascha Wildner *(int *)result = 65535;
1870d6644679SSascha Wildner break;
187185ccd313SImre Vadász case MMCBR_IVAR_MAX_BUSY_TIMEOUT:
187285ccd313SImre Vadász /*
187385ccd313SImre Vadász * Currently, sdhci_start_data() hardcodes 1 s for all CMDs.
187485ccd313SImre Vadász */
187585ccd313SImre Vadász *result = 1000000;
187685ccd313SImre Vadász break;
1877d6644679SSascha Wildner }
1878d6644679SSascha Wildner return (0);
1879d6644679SSascha Wildner }
1880d6644679SSascha Wildner
1881e355a444SMarkus Pfeiffer int
sdhci_generic_write_ivar(device_t bus,device_t child,int which,uintptr_t value)1882e355a444SMarkus Pfeiffer sdhci_generic_write_ivar(device_t bus, device_t child, int which, uintptr_t value)
1883d6644679SSascha Wildner {
1884d6644679SSascha Wildner struct sdhci_slot *slot = device_get_ivars(child);
18854d3ae590SImre Vadász uint32_t clock, max_clock;
18864d3ae590SImre Vadász int i;
1887d6644679SSascha Wildner
1888d6644679SSascha Wildner switch (which) {
1889d6644679SSascha Wildner default:
1890d6644679SSascha Wildner return (EINVAL);
1891d6644679SSascha Wildner case MMCBR_IVAR_BUS_MODE:
1892d6644679SSascha Wildner slot->host.ios.bus_mode = value;
1893d6644679SSascha Wildner break;
1894d6644679SSascha Wildner case MMCBR_IVAR_BUS_WIDTH:
1895d6644679SSascha Wildner slot->host.ios.bus_width = value;
1896d6644679SSascha Wildner break;
1897d6644679SSascha Wildner case MMCBR_IVAR_CHIP_SELECT:
1898d6644679SSascha Wildner slot->host.ios.chip_select = value;
1899d6644679SSascha Wildner break;
1900d6644679SSascha Wildner case MMCBR_IVAR_CLOCK:
1901d6644679SSascha Wildner if (value > 0) {
1902e355a444SMarkus Pfeiffer max_clock = slot->max_clk;
1903e355a444SMarkus Pfeiffer clock = max_clock;
1904e355a444SMarkus Pfeiffer
1905e355a444SMarkus Pfeiffer if (slot->version < SDHCI_SPEC_300) {
1906e355a444SMarkus Pfeiffer for (i = 0; i < SDHCI_200_MAX_DIVIDER;
1907e355a444SMarkus Pfeiffer i <<= 1) {
1908d6644679SSascha Wildner if (clock <= value)
1909d6644679SSascha Wildner break;
1910d6644679SSascha Wildner clock >>= 1;
1911d6644679SSascha Wildner }
19124d3ae590SImre Vadász } else {
1913e355a444SMarkus Pfeiffer for (i = 0; i < SDHCI_300_MAX_DIVIDER;
1914e355a444SMarkus Pfeiffer i += 2) {
1915e355a444SMarkus Pfeiffer if (clock <= value)
1916e355a444SMarkus Pfeiffer break;
1917e355a444SMarkus Pfeiffer clock = max_clock / (i + 2);
1918e355a444SMarkus Pfeiffer }
1919e355a444SMarkus Pfeiffer }
1920e355a444SMarkus Pfeiffer
1921d6644679SSascha Wildner slot->host.ios.clock = clock;
1922d6644679SSascha Wildner } else
1923d6644679SSascha Wildner slot->host.ios.clock = 0;
1924d6644679SSascha Wildner break;
1925d6644679SSascha Wildner case MMCBR_IVAR_MODE:
1926d6644679SSascha Wildner slot->host.mode = value;
1927d6644679SSascha Wildner break;
1928d6644679SSascha Wildner case MMCBR_IVAR_OCR:
1929d6644679SSascha Wildner slot->host.ocr = value;
1930d6644679SSascha Wildner break;
1931d6644679SSascha Wildner case MMCBR_IVAR_POWER_MODE:
1932d6644679SSascha Wildner slot->host.ios.power_mode = value;
1933d6644679SSascha Wildner break;
1934d6644679SSascha Wildner case MMCBR_IVAR_VDD:
1935d6644679SSascha Wildner slot->host.ios.vdd = value;
1936d6644679SSascha Wildner break;
19377ba10b88SImre Vadász case MMCBR_IVAR_VCCQ:
19387ba10b88SImre Vadász slot->host.ios.vccq = value;
19397ba10b88SImre Vadász break;
1940d6644679SSascha Wildner case MMCBR_IVAR_TIMING:
1941d6644679SSascha Wildner slot->host.ios.timing = value;
1942d6644679SSascha Wildner break;
1943d6644679SSascha Wildner case MMCBR_IVAR_CAPS:
1944d6644679SSascha Wildner case MMCBR_IVAR_HOST_OCR:
1945d6644679SSascha Wildner case MMCBR_IVAR_F_MIN:
1946d6644679SSascha Wildner case MMCBR_IVAR_F_MAX:
1947d6644679SSascha Wildner case MMCBR_IVAR_MAX_DATA:
1948d6644679SSascha Wildner return (EINVAL);
1949d6644679SSascha Wildner }
1950d6644679SSascha Wildner return (0);
1951d6644679SSascha Wildner }
1952d6644679SSascha Wildner
1953e355a444SMarkus Pfeiffer MODULE_VERSION(sdhci, 1);
1954