From owner-svn-src-all@FreeBSD.ORG Sat Dec 27 04:54:38 2014 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 807185FC; Sat, 27 Dec 2014 04:54:38 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 62E462DD9; Sat, 27 Dec 2014 04:54:38 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.9/8.14.9) with ESMTP id sBR4scTi049304; Sat, 27 Dec 2014 04:54:38 GMT (envelope-from ian@FreeBSD.org) Received: (from ian@localhost) by svn.freebsd.org (8.14.9/8.14.9/Submit) id sBR4sbaE049301; Sat, 27 Dec 2014 04:54:37 GMT (envelope-from ian@FreeBSD.org) Message-Id: <201412270454.sBR4sbaE049301@svn.freebsd.org> X-Authentication-Warning: svn.freebsd.org: ian set sender to ian@FreeBSD.org using -f From: Ian Lepore Date: Sat, 27 Dec 2014 04:54:37 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org Subject: svn commit: r276287 - in stable/10/sys: arm/ti dev/sdhci X-SVN-Group: stable-10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 27 Dec 2014 04:54:38 -0000 Author: ian Date: Sat Dec 27 04:54:36 2014 New Revision: 276287 URL: https://svnweb.freebsd.org/changeset/base/276287 Log: MFC r275944, r275946, r275949, r275950: Add code to set and reset open-drain mode on the bus when requested. When command and data interrupts have been aggregated together, don't do the data-completed processing if a command-error interrupt is also asserted. Add a new sdhci quirk, SDHCI_QUIRK_WAITFOR_RESET_ASSERTED, to work around TI OMAP controllers which will return the reset-in-progress bit as zero if you read the status register too fast after setting the reset bit. Modified: stable/10/sys/arm/ti/ti_sdhci.c stable/10/sys/dev/sdhci/sdhci.c stable/10/sys/dev/sdhci/sdhci.h Directory Properties: stable/10/ (props changed) Modified: stable/10/sys/arm/ti/ti_sdhci.c ============================================================================== --- stable/10/sys/arm/ti/ti_sdhci.c Sat Dec 27 04:51:21 2014 (r276286) +++ stable/10/sys/arm/ti/ti_sdhci.c Sat Dec 27 04:54:36 2014 (r276287) @@ -112,6 +112,7 @@ static struct ofw_compat_data compat_dat #define MMCHS_CON 0x02C #define MMCHS_CON_DW8 (1 << 5) #define MMCHS_CON_DVAL_8_4MS (3 << 9) +#define MMCHS_CON_OD (1 << 0) #define MMCHS_SYSCTL 0x12C #define MMCHS_SYSCTL_CLKD_MASK 0x3FF #define MMCHS_SYSCTL_CLKD_SHIFT 6 @@ -327,7 +328,7 @@ ti_sdhci_update_ios(device_t brdev, devi struct ti_sdhci_softc *sc = device_get_softc(brdev); struct sdhci_slot *slot; struct mmc_ios *ios; - uint32_t val32; + uint32_t val32, newval32; slot = device_get_ivars(reqdev); ios = &slot->host.ios; @@ -339,10 +340,20 @@ ti_sdhci_update_ios(device_t brdev, devi * requested, then let the standard driver handle everything else. */ val32 = ti_mmchs_read_4(sc, MMCHS_CON); + newval32 = val32; + if (ios->bus_width == bus_width_8) - ti_mmchs_write_4(sc, MMCHS_CON, val32 | MMCHS_CON_DW8); + newval32 |= MMCHS_CON_DW8; else - ti_mmchs_write_4(sc, MMCHS_CON, val32 & ~MMCHS_CON_DW8); + newval32 &= ~MMCHS_CON_DW8; + + if (ios->bus_mode == opendrain) + newval32 |= MMCHS_CON_OD; + else /* if (ios->bus_mode == pushpull) */ + newval32 &= ~MMCHS_CON_OD; + + if (newval32 != val32) + ti_mmchs_write_4(sc, MMCHS_CON, newval32); return (sdhci_generic_update_ios(brdev, reqdev)); } @@ -392,20 +403,42 @@ ti_sdhci_hw_init(device_t dev) /* Issue a softreset to the controller */ ti_mmchs_write_4(sc, MMCHS_SYSCONFIG, MMCHS_SYSCONFIG_RESET); timeout = 1000; - while (!(ti_mmchs_read_4(sc, MMCHS_SYSSTATUS) & MMCHS_SYSSTATUS_RESETDONE)) { + while (!(ti_mmchs_read_4(sc, MMCHS_SYSSTATUS) & + MMCHS_SYSSTATUS_RESETDONE)) { if (--timeout == 0) { - device_printf(dev, "Error: Controller reset operation timed out\n"); + device_printf(dev, + "Error: Controller reset operation timed out\n"); break; } DELAY(100); } - /* Reset both the command and data state machines */ + /* + * Reset the command and data state machines and also other aspects of + * the controller such as bus clock and power. + * + * If we read the software reset register too fast after writing it we + * can get back a zero that means the reset hasn't started yet rather + * than that the reset is complete. Per TI recommendations, work around + * it by reading until we see the reset bit asserted, then read until + * it's clear. We also set the SDHCI_QUIRK_WAITFOR_RESET_ASSERTED quirk + * so that the main sdhci driver uses this same logic in its resets. + */ ti_sdhci_write_1(dev, NULL, SDHCI_SOFTWARE_RESET, SDHCI_RESET_ALL); - timeout = 1000; - while ((ti_sdhci_read_1(dev, NULL, SDHCI_SOFTWARE_RESET) & SDHCI_RESET_ALL)) { + timeout = 10000; + while ((ti_sdhci_read_1(dev, NULL, SDHCI_SOFTWARE_RESET) & + SDHCI_RESET_ALL) != SDHCI_RESET_ALL) { if (--timeout == 0) { - device_printf(dev, "Error: Software reset operation timed out\n"); + break; + } + DELAY(1); + } + timeout = 10000; + while ((ti_sdhci_read_1(dev, NULL, SDHCI_SOFTWARE_RESET) & + SDHCI_RESET_ALL)) { + if (--timeout == 0) { + device_printf(dev, + "Error: Software reset operation timed out\n"); break; } DELAY(100); @@ -562,6 +595,12 @@ ti_sdhci_attach(device_t dev) sc->slot.quirks |= SDHCI_QUIRK_DONT_SHIFT_RESPONSE; /* + * Reset bits are broken, have to wait to see the bits asserted + * before waiting to see them de-asserted. + */ + sc->slot.quirks |= SDHCI_QUIRK_WAITFOR_RESET_ASSERTED; + + /* * DMA is not really broken, I just haven't implemented it yet. */ sc->slot.quirks |= SDHCI_QUIRK_BROKEN_DMA; Modified: stable/10/sys/dev/sdhci/sdhci.c ============================================================================== --- stable/10/sys/dev/sdhci/sdhci.c Sat Dec 27 04:51:21 2014 (r276286) +++ stable/10/sys/dev/sdhci/sdhci.c Sat Dec 27 04:54:36 2014 (r276287) @@ -150,7 +150,6 @@ static void sdhci_reset(struct sdhci_slot *slot, uint8_t mask) { int timeout; - uint8_t res; if (slot->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) { if (!(RD4(slot, SDHCI_PRESENT_STATE) & @@ -169,26 +168,43 @@ sdhci_reset(struct sdhci_slot *slot, uin sdhci_set_clock(slot, clock); } - WR1(slot, SDHCI_SOFTWARE_RESET, mask); - if (mask & SDHCI_RESET_ALL) { slot->clock = 0; slot->power = 0; } + WR1(slot, SDHCI_SOFTWARE_RESET, mask); + + if (slot->quirks & SDHCI_QUIRK_WAITFOR_RESET_ASSERTED) { + /* + * Resets on TI OMAPs and AM335x are incompatible with SDHCI + * specification. The reset bit has internal propagation delay, + * so a fast read after write returns 0 even if reset process is + * in progress. The workaround is to poll for 1 before polling + * for 0. In the worst case, if we miss seeing it asserted the + * time we spent waiting is enough to ensure the reset finishes. + */ + timeout = 10000; + while ((RD1(slot, SDHCI_SOFTWARE_RESET) & mask) != mask) { + if (timeout <= 0) + break; + timeout--; + DELAY(1); + } + } + /* Wait max 100 ms */ - timeout = 100; + timeout = 10000; /* Controller clears the bits when it's done */ - while ((res = RD1(slot, SDHCI_SOFTWARE_RESET)) & mask) { - if (timeout == 0) { - slot_printf(slot, - "Reset 0x%x never completed - 0x%x.\n", - (int)mask, (int)res); + while (RD1(slot, SDHCI_SOFTWARE_RESET) & mask) { + if (timeout <= 0) { + slot_printf(slot, "Reset 0x%x never completed.\n", + mask); sdhci_dumpregs(slot); return; } timeout--; - DELAY(1000); + DELAY(10); } } @@ -714,9 +730,13 @@ sdhci_timeout(void *arg) struct sdhci_slot *slot = arg; if (slot->curcmd != NULL) { + slot_printf(slot, " Controller timeout\n"); + sdhci_dumpregs(slot); sdhci_reset(slot, SDHCI_RESET_CMD|SDHCI_RESET_DATA); slot->curcmd->error = MMC_ERR_TIMEOUT; sdhci_req_done(slot); + } else { + slot_printf(slot, " Spurious timeout - no active command\n"); } } @@ -1275,7 +1295,9 @@ sdhci_generic_intr(struct sdhci_slot *sl /* Handle data interrupts. */ if (intmask & SDHCI_INT_DATA_MASK) { WR4(slot, SDHCI_INT_STATUS, intmask & SDHCI_INT_DATA_MASK); - sdhci_data_irq(slot, intmask & SDHCI_INT_DATA_MASK); + /* Dont call data_irq in case of errored command */ + if ((intmask & SDHCI_INT_CMD_ERROR_MASK) == 0) + sdhci_data_irq(slot, intmask & SDHCI_INT_DATA_MASK); } /* Handle AutoCMD12 error interrupt. */ if (intmask & SDHCI_INT_ACMD12ERR) { Modified: stable/10/sys/dev/sdhci/sdhci.h ============================================================================== --- stable/10/sys/dev/sdhci/sdhci.h Sat Dec 27 04:51:21 2014 (r276286) +++ stable/10/sys/dev/sdhci/sdhci.h Sat Dec 27 04:54:36 2014 (r276287) @@ -59,6 +59,8 @@ #define SDHCI_QUIRK_MISSING_CAPS (1<<12) /* Hardware shifts the 136-bit response, don't do it in software. */ #define SDHCI_QUIRK_DONT_SHIFT_RESPONSE (1<<13) +/* Wait to see reset bit asserted before waiting for de-asserted */ +#define SDHCI_QUIRK_WAITFOR_RESET_ASSERTED (1<<14) /* * Controller registers @@ -182,8 +184,11 @@ #define SDHCI_INT_NORMAL_MASK 0x00007FFF #define SDHCI_INT_ERROR_MASK 0xFFFF8000 -#define SDHCI_INT_CMD_MASK (SDHCI_INT_RESPONSE | SDHCI_INT_TIMEOUT | \ +#define SDHCI_INT_CMD_ERROR_MASK (SDHCI_INT_TIMEOUT | \ SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX) + +#define SDHCI_INT_CMD_MASK (SDHCI_INT_RESPONSE | SDHCI_INT_CMD_ERROR_MASK) + #define SDHCI_INT_DATA_MASK (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \ SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \ SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \