Page MenuHomeFreeBSD

D8407.id21866.diff
No OneTemporary

D8407.id21866.diff

Index: sys/arm/freescale/imx/files.imx5
===================================================================
--- sys/arm/freescale/imx/files.imx5
+++ sys/arm/freescale/imx/files.imx5
@@ -32,7 +32,7 @@
dev/ata/chipsets/ata-fsl.c optional imxata
# SDHCI/MMC
-arm/freescale/imx/imx_sdhci.c optional sdhci
+dev/sdhci/fsl_sdhci.c optional sdhci
# USB OH3 controller (1 OTG, 3 EHCI)
arm/freescale/imx/imx_nop_usbphy.c optional ehci
Index: sys/arm/freescale/imx/files.imx6
===================================================================
--- sys/arm/freescale/imx/files.imx6
+++ sys/arm/freescale/imx/files.imx6
@@ -32,7 +32,7 @@
#
# Optional devices.
#
-arm/freescale/imx/imx_sdhci.c optional sdhci
+dev/sdhci/fsl_sdhci.c optional sdhci
arm/freescale/imx/imx_wdog.c optional imxwdt
Index: sys/arm/freescale/imx/imx_sdhci.c
===================================================================
--- sys/arm/freescale/imx/imx_sdhci.c
+++ /dev/null
@@ -1,917 +0,0 @@
-/*-
- * Copyright (c) 2013 Ian Lepore <ian@freebsd.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- */
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-/*
- * SDHCI driver glue for Freescale i.MX SoC family.
- *
- * This supports both eSDHC (earlier SoCs) and uSDHC (more recent SoCs).
- */
-
-#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/types.h>
-#include <sys/bus.h>
-#include <sys/callout.h>
-#include <sys/kernel.h>
-#include <sys/libkern.h>
-#include <sys/lock.h>
-#include <sys/malloc.h>
-#include <sys/module.h>
-#include <sys/mutex.h>
-#include <sys/resource.h>
-#include <sys/rman.h>
-#include <sys/sysctl.h>
-#include <sys/taskqueue.h>
-#include <sys/time.h>
-
-#include <machine/bus.h>
-#include <machine/resource.h>
-#include <machine/intr.h>
-
-#include <arm/freescale/imx/imx_ccmvar.h>
-
-#include <dev/ofw/ofw_bus.h>
-#include <dev/ofw/ofw_bus_subr.h>
-
-#include <dev/mmc/bridge.h>
-#include <dev/mmc/mmcreg.h>
-#include <dev/mmc/mmcbrvar.h>
-
-#include <dev/sdhci/sdhci.h>
-#include "sdhci_if.h"
-
-struct imx_sdhci_softc {
- device_t dev;
- struct resource * mem_res;
- struct resource * irq_res;
- void * intr_cookie;
- struct sdhci_slot slot;
- struct callout r1bfix_callout;
- sbintime_t r1bfix_timeout_at;
- uint32_t baseclk_hz;
- uint32_t cmd_and_mode;
- uint32_t r1bfix_intmask;
- boolean_t force_card_present;
- uint16_t sdclockreg_freq_bits;
- uint8_t r1bfix_type;
- uint8_t hwtype;
-};
-
-#define R1BFIX_NONE 0 /* No fix needed at next interrupt. */
-#define R1BFIX_NODATA 1 /* Synthesize DATA_END for R1B w/o data. */
-#define R1BFIX_AC12 2 /* Wait for busy after auto command 12. */
-
-#define HWTYPE_NONE 0 /* Hardware not recognized/supported. */
-#define HWTYPE_ESDHC 1 /* imx5x and earlier. */
-#define HWTYPE_USDHC 2 /* imx6. */
-
-/*
- * Freescale-specific registers, or in some cases the layout of bits within the
- * sdhci-defined register is different on Freescale. These names all begin with
- * SDHC_ (not SDHCI_).
- */
-
-#define SDHC_WTMK_LVL 0x44 /* Watermark Level register. */
-#define USDHC_MIX_CONTROL 0x48 /* Mix(ed) Control register. */
-#define SDHC_VEND_SPEC 0xC0 /* Vendor-specific register. */
-#define SDHC_VEND_FRC_SDCLK_ON (1 << 8)
-#define SDHC_VEND_IPGEN (1 << 11)
-#define SDHC_VEND_HCKEN (1 << 12)
-#define SDHC_VEND_PEREN (1 << 13)
-
-#define SDHC_PRES_STATE 0x24
-#define SDHC_PRES_CIHB (1 << 0)
-#define SDHC_PRES_CDIHB (1 << 1)
-#define SDHC_PRES_DLA (1 << 2)
-#define SDHC_PRES_SDSTB (1 << 3)
-#define SDHC_PRES_IPGOFF (1 << 4)
-#define SDHC_PRES_HCKOFF (1 << 5)
-#define SDHC_PRES_PEROFF (1 << 6)
-#define SDHC_PRES_SDOFF (1 << 7)
-#define SDHC_PRES_WTA (1 << 8)
-#define SDHC_PRES_RTA (1 << 9)
-#define SDHC_PRES_BWEN (1 << 10)
-#define SDHC_PRES_BREN (1 << 11)
-#define SDHC_PRES_RTR (1 << 12)
-#define SDHC_PRES_CINST (1 << 16)
-#define SDHC_PRES_CDPL (1 << 18)
-#define SDHC_PRES_WPSPL (1 << 19)
-#define SDHC_PRES_CLSL (1 << 23)
-#define SDHC_PRES_DLSL_SHIFT 24
-#define SDHC_PRES_DLSL_MASK (0xffU << SDHC_PRES_DLSL_SHIFT)
-
-#define SDHC_PROT_CTRL 0x28
-#define SDHC_PROT_LED (1 << 0)
-#define SDHC_PROT_WIDTH_1BIT (0 << 1)
-#define SDHC_PROT_WIDTH_4BIT (1 << 1)
-#define SDHC_PROT_WIDTH_8BIT (2 << 1)
-#define SDHC_PROT_WIDTH_MASK (3 << 1)
-#define SDHC_PROT_D3CD (1 << 3)
-#define SDHC_PROT_EMODE_BIG (0 << 4)
-#define SDHC_PROT_EMODE_HALF (1 << 4)
-#define SDHC_PROT_EMODE_LITTLE (2 << 4)
-#define SDHC_PROT_EMODE_MASK (3 << 4)
-#define SDHC_PROT_SDMA (0 << 8)
-#define SDHC_PROT_ADMA1 (1 << 8)
-#define SDHC_PROT_ADMA2 (2 << 8)
-#define SDHC_PROT_ADMA264 (3 << 8)
-#define SDHC_PROT_DMA_MASK (3 << 8)
-#define SDHC_PROT_CDTL (1 << 6)
-#define SDHC_PROT_CDSS (1 << 7)
-
-#define SDHC_SYS_CTRL 0x2c
-#define SDHC_INT_STATUS 0x30
-
-/*
- * The clock enable bits exist in different registers for ESDHC vs USDHC, but
- * they are the same bits in both cases. The divisor values go into the
- * standard sdhci clock register, but in different bit positions and meanings
- than the sdhci spec values.
- */
-#define SDHC_CLK_IPGEN (1 << 0)
-#define SDHC_CLK_HCKEN (1 << 1)
-#define SDHC_CLK_PEREN (1 << 2)
-#define SDHC_CLK_SDCLKEN (1 << 3)
-#define SDHC_CLK_ENABLE_MASK 0x0000000f
-#define SDHC_CLK_DIVISOR_MASK 0x000000f0
-#define SDHC_CLK_DIVISOR_SHIFT 4
-#define SDHC_CLK_PRESCALE_MASK 0x0000ff00
-#define SDHC_CLK_PRESCALE_SHIFT 8
-
-static struct ofw_compat_data compat_data[] = {
- {"fsl,imx6q-usdhc", HWTYPE_USDHC},
- {"fsl,imx6sl-usdhc", HWTYPE_USDHC},
- {"fsl,imx53-esdhc", HWTYPE_ESDHC},
- {"fsl,imx51-esdhc", HWTYPE_ESDHC},
- {NULL, HWTYPE_NONE},
-};
-
-static uint16_t imx_sdhc_get_clock(struct imx_sdhci_softc *sc);
-static void imx_sdhc_set_clock(struct imx_sdhci_softc *sc, uint16_t val);
-static void imx_sdhci_r1bfix_func(void *arg);
-
-static inline uint32_t
-RD4(struct imx_sdhci_softc *sc, bus_size_t off)
-{
-
- return (bus_read_4(sc->mem_res, off));
-}
-
-static inline void
-WR4(struct imx_sdhci_softc *sc, bus_size_t off, uint32_t val)
-{
-
- bus_write_4(sc->mem_res, off, val);
-}
-
-static uint8_t
-imx_sdhci_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off)
-{
- struct imx_sdhci_softc *sc = device_get_softc(dev);
- uint32_t val32, wrk32;
-
- /*
- * Most of the things in the standard host control register are in the
- * hardware's wider protocol control register, but some of the bits are
- * moved around.
- */
- if (off == SDHCI_HOST_CONTROL) {
- wrk32 = RD4(sc, SDHC_PROT_CTRL);
- val32 = wrk32 & (SDHCI_CTRL_LED | SDHCI_CTRL_CARD_DET |
- SDHCI_CTRL_FORCE_CARD);
- switch (wrk32 & SDHC_PROT_WIDTH_MASK) {
- case SDHC_PROT_WIDTH_1BIT:
- /* Value is already 0. */
- break;
- case SDHC_PROT_WIDTH_4BIT:
- val32 |= SDHCI_CTRL_4BITBUS;
- break;
- case SDHC_PROT_WIDTH_8BIT:
- val32 |= SDHCI_CTRL_8BITBUS;
- break;
- }
- switch (wrk32 & SDHC_PROT_DMA_MASK) {
- case SDHC_PROT_SDMA:
- /* Value is already 0. */
- break;
- case SDHC_PROT_ADMA1:
- /* This value is deprecated, should never appear. */
- break;
- case SDHC_PROT_ADMA2:
- val32 |= SDHCI_CTRL_ADMA2;
- break;
- case SDHC_PROT_ADMA264:
- val32 |= SDHCI_CTRL_ADMA264;
- break;
- }
- return val32;
- }
-
- /*
- * XXX can't find the bus power on/off knob. For now we have to say the
- * power is always on and always set to the same voltage.
- */
- if (off == SDHCI_POWER_CONTROL) {
- return (SDHCI_POWER_ON | SDHCI_POWER_300);
- }
-
-
- return ((RD4(sc, off & ~3) >> (off & 3) * 8) & 0xff);
-}
-
-static uint16_t
-imx_sdhci_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off)
-{
- struct imx_sdhci_softc *sc = device_get_softc(dev);
- uint32_t val32;
-
- if (sc->hwtype == HWTYPE_USDHC) {
- /*
- * The USDHC hardware has nothing in the version register, but
- * it's v3 compatible with all our translation code.
- */
- if (off == SDHCI_HOST_VERSION) {
- return (SDHCI_SPEC_300 << SDHCI_SPEC_VER_SHIFT);
- }
- /*
- * The USDHC hardware moved the transfer mode bits to the mixed
- * control register, fetch them from there.
- */
- if (off == SDHCI_TRANSFER_MODE)
- return (RD4(sc, USDHC_MIX_CONTROL) & 0x37);
-
- } else if (sc->hwtype == HWTYPE_ESDHC) {
-
- /*
- * The ESDHC hardware has the typical 32-bit combined "command
- * and mode" register that we have to cache so that command
- * isn't written until after mode. On a read, just retrieve the
- * cached values last written.
- */
- if (off == SDHCI_TRANSFER_MODE) {
- return (sc->cmd_and_mode & 0x0000ffff);
- } else if (off == SDHCI_COMMAND_FLAGS) {
- return (sc->cmd_and_mode >> 16);
- }
- }
-
- /*
- * This hardware only manages one slot. Synthesize a slot interrupt
- * status register... if there are any enabled interrupts active they
- * must be coming from our one and only slot.
- */
- if (off == SDHCI_SLOT_INT_STATUS) {
- val32 = RD4(sc, SDHCI_INT_STATUS);
- val32 &= RD4(sc, SDHCI_SIGNAL_ENABLE);
- return (val32 ? 1 : 0);
- }
-
- /*
- * Clock bits are scattered into various registers which differ by
- * hardware type, complex enough to have their own function.
- */
- if (off == SDHCI_CLOCK_CONTROL) {
- return (imx_sdhc_get_clock(sc));
- }
-
- return ((RD4(sc, off & ~3) >> (off & 3) * 8) & 0xffff);
-}
-
-static uint32_t
-imx_sdhci_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off)
-{
- struct imx_sdhci_softc *sc = device_get_softc(dev);
- uint32_t val32, wrk32;
-
- val32 = RD4(sc, off);
-
- /*
- * The hardware leaves the base clock frequency out of the capabilities
- * register, but we filled it in by setting slot->max_clk at attach time
- * rather than here, because we can't represent frequencies above 63MHz
- * in an sdhci 2.0 capabliities register. The timeout clock is the same
- * as the active output sdclock; we indicate that with a quirk setting
- * so don't populate the timeout frequency bits.
- *
- * XXX Turn off (for now) features the hardware can do but this driver
- * doesn't yet handle (1.8v, suspend/resume, etc).
- */
- if (off == SDHCI_CAPABILITIES) {
- val32 &= ~SDHCI_CAN_VDD_180;
- val32 &= ~SDHCI_CAN_DO_SUSPEND;
- val32 |= SDHCI_CAN_DO_8BITBUS;
- return (val32);
- }
-
- /*
- * The hardware moves bits around in the present state register to make
- * room for all 8 data line state bits. To translate, mask out all the
- * bits which are not in the same position in both registers (this also
- * masks out some Freescale-specific bits in locations defined as
- * reserved by sdhci), then shift the data line and retune request bits
- * down to their standard locations.
- */
- if (off == SDHCI_PRESENT_STATE) {
- wrk32 = val32;
- val32 &= 0x000F0F07;
- val32 |= (wrk32 >> 4) & SDHCI_STATE_DAT_MASK;
- val32 |= (wrk32 >> 9) & SDHCI_RETUNE_REQUEST;
- if (sc->force_card_present)
- val32 |= SDHCI_CARD_PRESENT;
- return (val32);
- }
-
- /*
- * imx_sdhci_intr() can synthesize a DATA_END interrupt following a
- * command with an R1B response, mix it into the hardware status.
- */
- if (off == SDHCI_INT_STATUS) {
- return (val32 | sc->r1bfix_intmask);
- }
-
- return val32;
-}
-
-static void
-imx_sdhci_read_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
- uint32_t *data, bus_size_t count)
-{
- struct imx_sdhci_softc *sc = device_get_softc(dev);
-
- bus_read_multi_4(sc->mem_res, off, data, count);
-}
-
-static void
-imx_sdhci_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint8_t val)
-{
- struct imx_sdhci_softc *sc = device_get_softc(dev);
- uint32_t val32;
-
- /*
- * Most of the things in the standard host control register are in the
- * hardware's wider protocol control register, but some of the bits are
- * moved around.
- */
- if (off == SDHCI_HOST_CONTROL) {
- val32 = RD4(sc, SDHC_PROT_CTRL);
- val32 &= ~(SDHC_PROT_LED | SDHC_PROT_DMA_MASK |
- SDHC_PROT_WIDTH_MASK | SDHC_PROT_CDTL | SDHC_PROT_CDSS);
- val32 |= (val & SDHCI_CTRL_LED);
- if (val & SDHCI_CTRL_8BITBUS)
- val32 |= SDHC_PROT_WIDTH_8BIT;
- else
- val32 |= (val & SDHCI_CTRL_4BITBUS);
- val32 |= (val & (SDHCI_CTRL_SDMA | SDHCI_CTRL_ADMA2)) << 4;
- val32 |= (val & (SDHCI_CTRL_CARD_DET | SDHCI_CTRL_FORCE_CARD));
- WR4(sc, SDHC_PROT_CTRL, val32);
- return;
- }
-
- /* XXX I can't find the bus power on/off knob; do nothing. */
- if (off == SDHCI_POWER_CONTROL) {
- return;
- }
-
- val32 = RD4(sc, off & ~3);
- val32 &= ~(0xff << (off & 3) * 8);
- val32 |= (val << (off & 3) * 8);
-
- WR4(sc, off & ~3, val32);
-}
-
-static void
-imx_sdhci_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint16_t val)
-{
- struct imx_sdhci_softc *sc = device_get_softc(dev);
- uint32_t val32;
-
- /*
- * The clock control stuff is complex enough to have its own function
- * that can handle the ESDHC versus USDHC differences.
- */
- if (off == SDHCI_CLOCK_CONTROL) {
- imx_sdhc_set_clock(sc, val);
- return;
- }
-
- /*
- * Figure out whether we need to check the DAT0 line for busy status at
- * interrupt time. The controller should be doing this, but for some
- * reason it doesn't. There are two cases:
- * - R1B response with no data transfer should generate a DATA_END (aka
- * TRANSFER_COMPLETE) interrupt after waiting for busy, but if
- * there's no data transfer there's no DATA_END interrupt. This is
- * documented; they seem to think it's a feature.
- * - R1B response after Auto-CMD12 appears to not work, even though
- * there's a control bit for it (bit 3) in the vendor register.
- * When we're starting a command that needs a manual DAT0 line check at
- * interrupt time, we leave ourselves a note in r1bfix_type so that we
- * can do the extra work in imx_sdhci_intr().
- */
- if (off == SDHCI_COMMAND_FLAGS) {
- if (val & SDHCI_CMD_DATA) {
- const uint32_t MBAUTOCMD = SDHCI_TRNS_ACMD12 | SDHCI_TRNS_MULTI;
- val32 = RD4(sc, USDHC_MIX_CONTROL);
- if ((val32 & MBAUTOCMD) == MBAUTOCMD)
- sc->r1bfix_type = R1BFIX_AC12;
- } else {
- if ((val & SDHCI_CMD_RESP_MASK) == SDHCI_CMD_RESP_SHORT_BUSY) {
- WR4(sc, SDHCI_INT_ENABLE, slot->intmask | SDHCI_INT_RESPONSE);
- WR4(sc, SDHCI_SIGNAL_ENABLE, slot->intmask | SDHCI_INT_RESPONSE);
- sc->r1bfix_type = R1BFIX_NODATA;
- }
- }
- }
-
- /*
- * The USDHC hardware moved the transfer mode bits to mixed control; we
- * just write them there and we're done. The ESDHC hardware has the
- * typical combined cmd-and-mode register that allows only 32-bit
- * access, so when writing the mode bits just save them, then later when
- * writing the command bits, add in the saved mode bits.
- */
- if (sc->hwtype == HWTYPE_USDHC) {
- if (off == SDHCI_TRANSFER_MODE) {
- val32 = RD4(sc, USDHC_MIX_CONTROL);
- val32 &= ~0x3f;
- val32 |= val & 0x37;
- // XXX acmd23 not supported here (or by sdhci driver)
- WR4(sc, USDHC_MIX_CONTROL, val32);
- return;
- }
- } else if (sc->hwtype == HWTYPE_ESDHC) {
- if (off == SDHCI_TRANSFER_MODE) {
- sc->cmd_and_mode =
- (sc->cmd_and_mode & 0xffff0000) | val;
- return;
- } else if (off == SDHCI_COMMAND_FLAGS) {
- sc->cmd_and_mode =
- (sc->cmd_and_mode & 0xffff) | (val << 16);
- WR4(sc, SDHCI_TRANSFER_MODE, sc->cmd_and_mode);
- return;
- }
- }
-
- val32 = RD4(sc, off & ~3);
- val32 &= ~(0xffff << (off & 3) * 8);
- val32 |= ((val & 0xffff) << (off & 3) * 8);
- WR4(sc, off & ~3, val32);
-}
-
-static void
-imx_sdhci_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t val)
-{
- struct imx_sdhci_softc *sc = device_get_softc(dev);
-
- /* Clear synthesized interrupts, then pass the value to the hardware. */
- if (off == SDHCI_INT_STATUS) {
- sc->r1bfix_intmask &= ~val;
- }
-
- WR4(sc, off, val);
-}
-
-static void
-imx_sdhci_write_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
- uint32_t *data, bus_size_t count)
-{
- struct imx_sdhci_softc *sc = device_get_softc(dev);
-
- bus_write_multi_4(sc->mem_res, off, data, count);
-}
-
-static uint16_t
-imx_sdhc_get_clock(struct imx_sdhci_softc *sc)
-{
- uint16_t val;
-
- /*
- * Whenever the sdhci driver writes the clock register we save a
- * snapshot of just the frequency bits, so that we can play them back
- * here on a register read without recalculating the frequency from the
- * prescalar and divisor bits in the real register. We'll start with
- * those bits, and mix in the clock status and enable bits that come
- * from different places depending on which hardware we've got.
- */
- val = sc->sdclockreg_freq_bits;
-
- /*
- * The internal clock is always enabled (actually, the hardware manages
- * it). Whether the internal clock is stable yet after a frequency
- * change comes from the present-state register on both hardware types.
- */
- val |= SDHCI_CLOCK_INT_EN;
- if (RD4(sc, SDHC_PRES_STATE) & SDHC_PRES_SDSTB)
- val |= SDHCI_CLOCK_INT_STABLE;
-
- /*
- * On ESDHC hardware the card bus clock enable is in the usual sdhci
- * register but it's a different bit, so transcribe it (note the
- * difference between standard SDHCI_ and Freescale SDHC_ prefixes
- * here). On USDHC hardware there is a force-on bit, but no force-off
- * for the card bus clock (the hardware runs the clock when transfers
- * are active no matter what), so we always say the clock is on.
- * XXX Maybe we should say it's in whatever state the sdhci driver last
- * set it to.
- */
- if (sc->hwtype == HWTYPE_ESDHC) {
- if (RD4(sc, SDHC_SYS_CTRL) & SDHC_CLK_SDCLKEN)
- val |= SDHCI_CLOCK_CARD_EN;
- } else {
- val |= SDHCI_CLOCK_CARD_EN;
- }
-
- return (val);
-}
-
-static void
-imx_sdhc_set_clock(struct imx_sdhci_softc *sc, uint16_t val)
-{
- uint32_t divisor, freq, prescale, val32;
-
- val32 = RD4(sc, SDHCI_CLOCK_CONTROL);
-
- /*
- * Save the frequency-setting bits in SDHCI format so that we can play
- * them back in get_clock without complex decoding of hardware regs,
- * then deal with the freqency part of the value based on hardware type.
- */
- sc->sdclockreg_freq_bits = val & SDHCI_DIVIDERS_MASK;
- if (sc->hwtype == HWTYPE_ESDHC) {
- /*
- * The ESDHC hardware requires the driver to manually start and
- * stop the sd bus clock. If the enable bit is not set, turn
- * off the clock in hardware and we're done, otherwise decode
- * the requested frequency. ESDHC hardware is sdhci 2.0; the
- * sdhci driver will use the original 8-bit divisor field and
- * the "base / 2^N" divisor scheme.
- */
- if ((val & SDHCI_CLOCK_CARD_EN) == 0) {
- WR4(sc, SDHCI_CLOCK_CONTROL, val32 & ~SDHC_CLK_SDCLKEN);
- return;
-
- }
- divisor = (val >> SDHCI_DIVIDER_SHIFT) & SDHCI_DIVIDER_MASK;
- freq = sc->baseclk_hz >> ffs(divisor);
- } else {
- /*
- * The USDHC hardware provides only "force always on" control
- * over the sd bus clock, but no way to turn it off. (If a cmd
- * or data transfer is in progress the clock is on, otherwise it
- * is off.) If the clock is being disabled, we can just return
- * now, otherwise we decode the requested frequency. USDHC
- * hardware is sdhci 3.0; the sdhci driver will use a 10-bit
- * divisor using the "base / 2*N" divisor scheme.
- */
- if ((val & SDHCI_CLOCK_CARD_EN) == 0)
- return;
- divisor = ((val >> SDHCI_DIVIDER_SHIFT) & SDHCI_DIVIDER_MASK) |
- ((val >> SDHCI_DIVIDER_HI_SHIFT) & SDHCI_DIVIDER_HI_MASK) <<
- SDHCI_DIVIDER_MASK_LEN;
- if (divisor == 0)
- freq = sc->baseclk_hz;
- else
- freq = sc->baseclk_hz / (2 * divisor);
- }
-
- /*
- * Get a prescaler and final divisor to achieve the desired frequency.
- */
- for (prescale = 2; freq < sc->baseclk_hz / (prescale * 16);)
- prescale <<= 1;
-
- for (divisor = 1; freq < sc->baseclk_hz / (prescale * divisor);)
- ++divisor;
-
-#ifdef DEBUG
- device_printf(sc->dev,
- "desired SD freq: %d, actual: %d; base %d prescale %d divisor %d\n",
- freq, sc->baseclk_hz / (prescale * divisor), sc->baseclk_hz,
- prescale, divisor);
-#endif
-
- /*
- * Adjust to zero-based values, and store them to the hardware.
- */
- prescale >>= 1;
- divisor -= 1;
-
- val32 &= ~(SDHC_CLK_DIVISOR_MASK | SDHC_CLK_PRESCALE_MASK);
- val32 |= divisor << SDHC_CLK_DIVISOR_SHIFT;
- val32 |= prescale << SDHC_CLK_PRESCALE_SHIFT;
- WR4(sc, SDHCI_CLOCK_CONTROL, val32);
-}
-
-static boolean_t
-imx_sdhci_r1bfix_is_wait_done(struct imx_sdhci_softc *sc)
-{
- uint32_t inhibit;
-
- mtx_assert(&sc->slot.mtx, MA_OWNED);
-
- /*
- * Check the DAT0 line status using both the DLA (data line active) and
- * CDIHB (data inhibit) bits in the present state register. In theory
- * just DLA should do the trick, but in practice it takes both. If the
- * DAT0 line is still being held and we're not yet beyond the timeout
- * point, just schedule another callout to check again later.
- */
- inhibit = RD4(sc, SDHC_PRES_STATE) & (SDHC_PRES_DLA | SDHC_PRES_CDIHB);
-
- if (inhibit && getsbinuptime() < sc->r1bfix_timeout_at) {
- callout_reset_sbt(&sc->r1bfix_callout, SBT_1MS, 0,
- imx_sdhci_r1bfix_func, sc, 0);
- return (false);
- }
-
- /*
- * If we reach this point with the inhibit bits still set, we've got a
- * timeout, synthesize a DATA_TIMEOUT interrupt. Otherwise the DAT0
- * line has been released, and we synthesize a DATA_END, and if the type
- * of fix needed was on a command-without-data we also now add in the
- * original INT_RESPONSE that we suppressed earlier.
- */
- if (inhibit)
- sc->r1bfix_intmask |= SDHCI_INT_DATA_TIMEOUT;
- else {
- sc->r1bfix_intmask |= SDHCI_INT_DATA_END;
- if (sc->r1bfix_type == R1BFIX_NODATA)
- sc->r1bfix_intmask |= SDHCI_INT_RESPONSE;
- }
-
- sc->r1bfix_type = R1BFIX_NONE;
- return (true);
-}
-
-static void
-imx_sdhci_r1bfix_func(void * arg)
-{
- struct imx_sdhci_softc *sc = arg;
- boolean_t r1bwait_done;
-
- mtx_lock(&sc->slot.mtx);
- r1bwait_done = imx_sdhci_r1bfix_is_wait_done(sc);
- mtx_unlock(&sc->slot.mtx);
- if (r1bwait_done)
- sdhci_generic_intr(&sc->slot);
-}
-
-static void
-imx_sdhci_intr(void *arg)
-{
- struct imx_sdhci_softc *sc = arg;
- uint32_t intmask;
-
- mtx_lock(&sc->slot.mtx);
-
- /*
- * Manually check the DAT0 line for R1B response types that the
- * controller fails to handle properly. The controller asserts the done
- * interrupt while the card is still asserting busy with the DAT0 line.
- *
- * We check DAT0 immediately because most of the time, especially on a
- * read, the card will actually be done by time we get here. If it's
- * not, then the wait_done routine will schedule a callout to re-check
- * periodically until it is done. In that case we clear the interrupt
- * out of the hardware now so that we can present it later when the DAT0
- * line is released.
- *
- * If we need to wait for the DAT0 line to be released, we set up a
- * timeout point 250ms in the future. This number comes from the SD
- * spec, which allows a command to take that long. In the real world,
- * cards tend to take 10-20ms for a long-running command such as a write
- * or erase that spans two pages.
- */
- switch (sc->r1bfix_type) {
- case R1BFIX_NODATA:
- intmask = RD4(sc, SDHC_INT_STATUS) & SDHCI_INT_RESPONSE;
- break;
- case R1BFIX_AC12:
- intmask = RD4(sc, SDHC_INT_STATUS) & SDHCI_INT_DATA_END;
- break;
- default:
- intmask = 0;
- break;
- }
- if (intmask) {
- sc->r1bfix_timeout_at = getsbinuptime() + 250 * SBT_1MS;
- if (!imx_sdhci_r1bfix_is_wait_done(sc)) {
- WR4(sc, SDHC_INT_STATUS, intmask);
- bus_barrier(sc->mem_res, SDHC_INT_STATUS, 4,
- BUS_SPACE_BARRIER_WRITE);
- }
- }
-
- mtx_unlock(&sc->slot.mtx);
- sdhci_generic_intr(&sc->slot);
-}
-
-static int
-imx_sdhci_get_ro(device_t bus, device_t child)
-{
-
- return (false);
-}
-
-static int
-imx_sdhci_detach(device_t dev)
-{
-
- return (EBUSY);
-}
-
-static int
-imx_sdhci_attach(device_t dev)
-{
- struct imx_sdhci_softc *sc = device_get_softc(dev);
- int rid, err;
- phandle_t node;
-
- sc->dev = dev;
-
- sc->hwtype = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
- if (sc->hwtype == HWTYPE_NONE)
- panic("Impossible: not compatible in imx_sdhci_attach()");
-
- rid = 0;
- sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
- RF_ACTIVE);
- if (!sc->mem_res) {
- device_printf(dev, "cannot allocate memory window\n");
- err = ENXIO;
- goto fail;
- }
-
- rid = 0;
- sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
- RF_ACTIVE);
- if (!sc->irq_res) {
- device_printf(dev, "cannot allocate interrupt\n");
- err = ENXIO;
- goto fail;
- }
-
- if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
- NULL, imx_sdhci_intr, sc, &sc->intr_cookie)) {
- device_printf(dev, "cannot setup interrupt handler\n");
- err = ENXIO;
- goto fail;
- }
-
- sc->slot.quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK;
-
- /*
- * DMA is not really broken, I just haven't implemented it yet.
- */
- sc->slot.quirks |= SDHCI_QUIRK_BROKEN_DMA;
-
- /*
- * Set the buffer watermark level to 128 words (512 bytes) for both read
- * and write. The hardware has a restriction that when the read or
- * write ready status is asserted, that means you can read exactly the
- * number of words set in the watermark register before you have to
- * re-check the status and potentially wait for more data. The main
- * sdhci driver provides no hook for doing status checking on less than
- * a full block boundary, so we set the watermark level to be a full
- * block. Reads and writes where the block size is less than the
- * watermark size will work correctly too, no need to change the
- * watermark for different size blocks. However, 128 is the maximum
- * allowed for the watermark, so PIO is limitted to 512 byte blocks
- * (which works fine for SD cards, may be a problem for SDIO some day).
- *
- * XXX need named constants for this stuff.
- */
- WR4(sc, SDHC_WTMK_LVL, 0x08800880);
-
- sc->baseclk_hz = imx_ccm_sdhci_hz();
- sc->slot.max_clk = sc->baseclk_hz;
-
- /*
- * If the slot is flagged with the non-removable property, set our flag
- * to always force the SDHCI_CARD_PRESENT bit on.
- *
- * XXX Workaround for gpio-based card detect...
- *
- * We don't have gpio support yet. If there's a cd-gpios property just
- * force the SDHCI_CARD_PRESENT bit on for now. If there isn't really a
- * card there it will fail to probe at the mmc layer and nothing bad
- * happens except instantiating an mmcN device for an empty slot.
- */
- node = ofw_bus_get_node(dev);
- if (OF_hasprop(node, "non-removable"))
- sc->force_card_present = true;
- else if (OF_hasprop(node, "cd-gpios")) {
- /* XXX put real gpio hookup here. */
- sc->force_card_present = true;
- }
-
- callout_init(&sc->r1bfix_callout, 1);
- sdhci_init_slot(dev, &sc->slot, 0);
-
- bus_generic_probe(dev);
- bus_generic_attach(dev);
-
- sdhci_start_slot(&sc->slot);
-
- return (0);
-
-fail:
- if (sc->intr_cookie)
- bus_teardown_intr(dev, sc->irq_res, sc->intr_cookie);
- if (sc->irq_res)
- bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
- if (sc->mem_res)
- bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
-
- return (err);
-}
-
-static int
-imx_sdhci_probe(device_t dev)
-{
-
- if (!ofw_bus_status_okay(dev))
- return (ENXIO);
-
- switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) {
- case HWTYPE_ESDHC:
- device_set_desc(dev, "Freescale eSDHC controller");
- return (BUS_PROBE_DEFAULT);
- case HWTYPE_USDHC:
- device_set_desc(dev, "Freescale uSDHC controller");
- return (BUS_PROBE_DEFAULT);
- default:
- break;
- }
- return (ENXIO);
-}
-
-static device_method_t imx_sdhci_methods[] = {
- /* Device interface */
- DEVMETHOD(device_probe, imx_sdhci_probe),
- DEVMETHOD(device_attach, imx_sdhci_attach),
- DEVMETHOD(device_detach, imx_sdhci_detach),
-
- /* Bus interface */
- DEVMETHOD(bus_read_ivar, sdhci_generic_read_ivar),
- DEVMETHOD(bus_write_ivar, sdhci_generic_write_ivar),
- DEVMETHOD(bus_print_child, bus_generic_print_child),
-
- /* MMC bridge interface */
- DEVMETHOD(mmcbr_update_ios, sdhci_generic_update_ios),
- DEVMETHOD(mmcbr_request, sdhci_generic_request),
- DEVMETHOD(mmcbr_get_ro, imx_sdhci_get_ro),
- DEVMETHOD(mmcbr_acquire_host, sdhci_generic_acquire_host),
- DEVMETHOD(mmcbr_release_host, sdhci_generic_release_host),
-
- /* SDHCI registers accessors */
- DEVMETHOD(sdhci_read_1, imx_sdhci_read_1),
- DEVMETHOD(sdhci_read_2, imx_sdhci_read_2),
- DEVMETHOD(sdhci_read_4, imx_sdhci_read_4),
- DEVMETHOD(sdhci_read_multi_4, imx_sdhci_read_multi_4),
- DEVMETHOD(sdhci_write_1, imx_sdhci_write_1),
- DEVMETHOD(sdhci_write_2, imx_sdhci_write_2),
- DEVMETHOD(sdhci_write_4, imx_sdhci_write_4),
- DEVMETHOD(sdhci_write_multi_4, imx_sdhci_write_multi_4),
-
- { 0, 0 }
-};
-
-static devclass_t imx_sdhci_devclass;
-
-static driver_t imx_sdhci_driver = {
- "sdhci_imx",
- imx_sdhci_methods,
- sizeof(struct imx_sdhci_softc),
-};
-
-DRIVER_MODULE(sdhci_imx, simplebus, imx_sdhci_driver, imx_sdhci_devclass, 0, 0);
-MODULE_DEPEND(sdhci_imx, sdhci, 1, 1, 1);
-DRIVER_MODULE(mmc, sdhci_imx, mmc_driver, mmc_devclass, NULL, NULL);
-MODULE_DEPEND(sdhci_imx, mmc, 1, 1, 1);
Index: sys/conf/files.powerpc
===================================================================
--- sys/conf/files.powerpc
+++ sys/conf/files.powerpc
@@ -63,6 +63,7 @@
dev/powermac_nvram/powermac_nvram.c optional powermac_nvram powermac
dev/quicc/quicc_bfe_fdt.c optional quicc mpc85xx
dev/scc/scc_bfe_macio.c optional scc powermac
+dev/sdhci/fsl_sdhc.c optional mpc85xx sdhci
dev/sec/sec.c optional sec mpc85xx
dev/sound/macio/aoa.c optional snd_davbus | snd_ai2s powermac
dev/sound/macio/davbus.c optional snd_davbus powermac
@@ -137,7 +138,6 @@
powerpc/mpc85xx/ds1553_bus_fdt.c optional ds1553 fdt
powerpc/mpc85xx/ds1553_core.c optional ds1553
powerpc/mpc85xx/fsl_diu.c optional mpc85xx diu
-powerpc/mpc85xx/fsl_sdhc.c optional mpc85xx sdhc
powerpc/mpc85xx/i2c.c optional iicbus fdt
powerpc/mpc85xx/isa.c optional mpc85xx isa
powerpc/mpc85xx/lbc.c optional mpc85xx
Index: sys/dev/sdhci/fsl_sdhci.c
===================================================================
--- /dev/null
+++ sys/dev/sdhci/fsl_sdhci.c
@@ -0,0 +1,1001 @@
+/*-
+ * Copyright (c) 2013 Ian Lepore <ian@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * SDHCI driver glue for Freescale i.MX SoC and QorIQ families.
+ *
+ * This supports both eSDHC (earlier SoCs) and uSDHC (more recent SoCs).
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/bus.h>
+#include <sys/callout.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/libkern.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
+#include <sys/time.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#ifdef __arm__
+#include <machine/intr.h>
+
+#include <arm/freescale/imx/imx_ccmvar.h>
+#endif
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/mmc/bridge.h>
+#include <dev/mmc/mmcreg.h>
+#include <dev/mmc/mmcbrvar.h>
+
+#include <dev/sdhci/sdhci.h>
+#include "sdhci_if.h"
+
+struct fsl_sdhci_softc {
+ device_t dev;
+ struct resource * mem_res;
+ struct resource * irq_res;
+ void * intr_cookie;
+ struct sdhci_slot slot;
+ struct callout r1bfix_callout;
+ sbintime_t r1bfix_timeout_at;
+ uint32_t baseclk_hz;
+ uint32_t cmd_and_mode;
+ uint32_t r1bfix_intmask;
+ boolean_t force_card_present;
+ uint16_t sdclockreg_freq_bits;
+ uint8_t r1bfix_type;
+ uint8_t hwtype;
+};
+
+#define R1BFIX_NONE 0 /* No fix needed at next interrupt. */
+#define R1BFIX_NODATA 1 /* Synthesize DATA_END for R1B w/o data. */
+#define R1BFIX_AC12 2 /* Wait for busy after auto command 12. */
+
+#define HWTYPE_NONE 0 /* Hardware not recognized/supported. */
+#define HWTYPE_ESDHC 1 /* fsl5x and earlier. */
+#define HWTYPE_USDHC 2 /* fsl6. */
+
+/*
+ * Freescale-specific registers, or in some cases the layout of bits within the
+ * sdhci-defined register is different on Freescale. These names all begin with
+ * SDHC_ (not SDHCI_).
+ */
+
+#define SDHC_WTMK_LVL 0x44 /* Watermark Level register. */
+#define USDHC_MIX_CONTROL 0x48 /* Mix(ed) Control register. */
+#define SDHC_VEND_SPEC 0xC0 /* Vendor-specific register. */
+#define SDHC_VEND_FRC_SDCLK_ON (1 << 8)
+#define SDHC_VEND_IPGEN (1 << 11)
+#define SDHC_VEND_HCKEN (1 << 12)
+#define SDHC_VEND_PEREN (1 << 13)
+
+#define SDHC_PRES_STATE 0x24
+#define SDHC_PRES_CIHB (1 << 0)
+#define SDHC_PRES_CDIHB (1 << 1)
+#define SDHC_PRES_DLA (1 << 2)
+#define SDHC_PRES_SDSTB (1 << 3)
+#define SDHC_PRES_IPGOFF (1 << 4)
+#define SDHC_PRES_HCKOFF (1 << 5)
+#define SDHC_PRES_PEROFF (1 << 6)
+#define SDHC_PRES_SDOFF (1 << 7)
+#define SDHC_PRES_WTA (1 << 8)
+#define SDHC_PRES_RTA (1 << 9)
+#define SDHC_PRES_BWEN (1 << 10)
+#define SDHC_PRES_BREN (1 << 11)
+#define SDHC_PRES_RTR (1 << 12)
+#define SDHC_PRES_CINST (1 << 16)
+#define SDHC_PRES_CDPL (1 << 18)
+#define SDHC_PRES_WPSPL (1 << 19)
+#define SDHC_PRES_CLSL (1 << 23)
+#define SDHC_PRES_DLSL_SHIFT 24
+#define SDHC_PRES_DLSL_MASK (0xffU << SDHC_PRES_DLSL_SHIFT)
+
+#define SDHC_PROT_CTRL 0x28
+#define SDHC_PROT_LED (1 << 0)
+#define SDHC_PROT_WIDTH_1BIT (0 << 1)
+#define SDHC_PROT_WIDTH_4BIT (1 << 1)
+#define SDHC_PROT_WIDTH_8BIT (2 << 1)
+#define SDHC_PROT_WIDTH_MASK (3 << 1)
+#define SDHC_PROT_D3CD (1 << 3)
+#define SDHC_PROT_EMODE_BIG (0 << 4)
+#define SDHC_PROT_EMODE_HALF (1 << 4)
+#define SDHC_PROT_EMODE_LITTLE (2 << 4)
+#define SDHC_PROT_EMODE_MASK (3 << 4)
+#define SDHC_PROT_SDMA (0 << 8)
+#define SDHC_PROT_ADMA1 (1 << 8)
+#define SDHC_PROT_ADMA2 (2 << 8)
+#define SDHC_PROT_ADMA264 (3 << 8)
+#define SDHC_PROT_DMA_MASK (3 << 8)
+#define SDHC_PROT_CDTL (1 << 6)
+#define SDHC_PROT_CDSS (1 << 7)
+
+#define SDHC_SYS_CTRL 0x2c
+
+/*
+ * The clock enable bits exist in different registers for ESDHC vs USDHC, but
+ * they are the same bits in both cases. The divisor values go into the
+ * standard sdhci clock register, but in different bit positions and meanings
+ than the sdhci spec values.
+ */
+#define SDHC_CLK_IPGEN (1 << 0)
+#define SDHC_CLK_HCKEN (1 << 1)
+#define SDHC_CLK_PEREN (1 << 2)
+#define SDHC_CLK_SDCLKEN (1 << 3)
+#define SDHC_CLK_ENABLE_MASK 0x0000000f
+#define SDHC_CLK_DIVISOR_MASK 0x000000f0
+#define SDHC_CLK_DIVISOR_SHIFT 4
+#define SDHC_CLK_PRESCALE_MASK 0x0000ff00
+#define SDHC_CLK_PRESCALE_SHIFT 8
+
+static struct ofw_compat_data compat_data[] = {
+ {"fsl,imx6q-usdhc", HWTYPE_USDHC},
+ {"fsl,imx6sl-usdhc", HWTYPE_USDHC},
+ {"fsl,imx53-esdhc", HWTYPE_ESDHC},
+ {"fsl,imx51-esdhc", HWTYPE_ESDHC},
+ {"fsl,esdhc", HWTYPE_ESDHC},
+ {NULL, HWTYPE_NONE},
+};
+
+static uint16_t fsl_sdhc_get_clock(struct fsl_sdhci_softc *sc);
+static void fsl_sdhc_set_clock(struct fsl_sdhci_softc *sc, uint16_t val);
+static void fsl_sdhci_r1bfix_func(void *arg);
+
+static inline uint32_t
+RD4(struct fsl_sdhci_softc *sc, bus_size_t off)
+{
+
+ return (bus_read_4(sc->mem_res, off));
+}
+
+static inline void
+WR4(struct fsl_sdhci_softc *sc, bus_size_t off, uint32_t val)
+{
+
+ bus_write_4(sc->mem_res, off, val);
+}
+
+static uint8_t
+fsl_sdhci_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off)
+{
+ struct fsl_sdhci_softc *sc = device_get_softc(dev);
+ uint32_t val32, wrk32;
+
+ /*
+ * Most of the things in the standard host control register are in the
+ * hardware's wider protocol control register, but some of the bits are
+ * moved around.
+ */
+ if (off == SDHCI_HOST_CONTROL) {
+ wrk32 = RD4(sc, SDHC_PROT_CTRL);
+ val32 = wrk32 & (SDHCI_CTRL_LED | SDHCI_CTRL_CARD_DET |
+ SDHCI_CTRL_FORCE_CARD);
+ switch (wrk32 & SDHC_PROT_WIDTH_MASK) {
+ case SDHC_PROT_WIDTH_1BIT:
+ /* Value is already 0. */
+ break;
+ case SDHC_PROT_WIDTH_4BIT:
+ val32 |= SDHCI_CTRL_4BITBUS;
+ break;
+ case SDHC_PROT_WIDTH_8BIT:
+ val32 |= SDHCI_CTRL_8BITBUS;
+ break;
+ }
+ switch (wrk32 & SDHC_PROT_DMA_MASK) {
+ case SDHC_PROT_SDMA:
+ /* Value is already 0. */
+ break;
+ case SDHC_PROT_ADMA1:
+ /* This value is deprecated, should never appear. */
+ break;
+ case SDHC_PROT_ADMA2:
+ val32 |= SDHCI_CTRL_ADMA2;
+ break;
+ case SDHC_PROT_ADMA264:
+ val32 |= SDHCI_CTRL_ADMA264;
+ break;
+ }
+ return val32;
+ }
+
+ /*
+ * XXX can't find the bus power on/off knob. For now we have to say the
+ * power is always on and always set to the same voltage.
+ */
+ if (off == SDHCI_POWER_CONTROL) {
+ return (SDHCI_POWER_ON | SDHCI_POWER_300);
+ }
+
+
+ return ((RD4(sc, off & ~3) >> (off & 3) * 8) & 0xff);
+}
+
+static uint16_t
+fsl_sdhci_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off)
+{
+ struct fsl_sdhci_softc *sc = device_get_softc(dev);
+ uint32_t val32;
+
+ if (sc->hwtype == HWTYPE_USDHC) {
+ /*
+ * The USDHC hardware has nothing in the version register, but
+ * it's v3 compatible with all our translation code.
+ */
+ if (off == SDHCI_HOST_VERSION) {
+ return (SDHCI_SPEC_300 << SDHCI_SPEC_VER_SHIFT);
+ }
+ /*
+ * The USDHC hardware moved the transfer mode bits to the mixed
+ * control register, fetch them from there.
+ */
+ if (off == SDHCI_TRANSFER_MODE)
+ return (RD4(sc, USDHC_MIX_CONTROL) & 0x37);
+
+ } else if (sc->hwtype == HWTYPE_ESDHC) {
+
+ /*
+ * The ESDHC hardware has the typical 32-bit combined "command
+ * and mode" register that we have to cache so that command
+ * isn't written until after mode. On a read, just retrieve the
+ * cached values last written.
+ */
+ if (off == SDHCI_TRANSFER_MODE) {
+ return (sc->cmd_and_mode & 0x0000ffff);
+ } else if (off == SDHCI_COMMAND_FLAGS) {
+ return (sc->cmd_and_mode >> 16);
+ }
+ }
+
+ /*
+ * This hardware only manages one slot. Synthesize a slot interrupt
+ * status register... if there are any enabled interrupts active they
+ * must be coming from our one and only slot.
+ */
+ if (off == SDHCI_SLOT_INT_STATUS) {
+ val32 = RD4(sc, SDHCI_INT_STATUS);
+ val32 &= RD4(sc, SDHCI_SIGNAL_ENABLE);
+ return (val32 ? 1 : 0);
+ }
+
+ /*
+ * Clock bits are scattered into various registers which differ by
+ * hardware type, complex enough to have their own function.
+ */
+ if (off == SDHCI_CLOCK_CONTROL) {
+ return (fsl_sdhc_get_clock(sc));
+ }
+
+ return ((RD4(sc, off & ~3) >> (off & 3) * 8) & 0xffff);
+}
+
+static uint32_t
+fsl_sdhci_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off)
+{
+ struct fsl_sdhci_softc *sc = device_get_softc(dev);
+ uint32_t val32, wrk32;
+
+ val32 = RD4(sc, off);
+
+ /*
+ * The hardware leaves the base clock frequency out of the capabilities
+ * register, but we filled it in by setting slot->max_clk at attach time
+ * rather than here, because we can't represent frequencies above 63MHz
+ * in an sdhci 2.0 capabliities register. The timeout clock is the same
+ * as the active output sdclock; we indicate that with a quirk setting
+ * so don't populate the timeout frequency bits.
+ *
+ * XXX Turn off (for now) features the hardware can do but this driver
+ * doesn't yet handle (1.8v, suspend/resume, etc).
+ */
+ if (off == SDHCI_CAPABILITIES) {
+ val32 &= ~SDHCI_CAN_VDD_180;
+ val32 &= ~SDHCI_CAN_DO_SUSPEND;
+ val32 |= SDHCI_CAN_DO_8BITBUS;
+ return (val32);
+ }
+
+ /*
+ * The hardware moves bits around in the present state register to make
+ * room for all 8 data line state bits. To translate, mask out all the
+ * bits which are not in the same position in both registers (this also
+ * masks out some Freescale-specific bits in locations defined as
+ * reserved by sdhci), then shift the data line and retune request bits
+ * down to their standard locations.
+ */
+ if (off == SDHCI_PRESENT_STATE) {
+ wrk32 = val32;
+ val32 &= 0x000F0F07;
+ val32 |= (wrk32 >> 4) & SDHCI_STATE_DAT_MASK;
+ val32 |= (wrk32 >> 9) & SDHCI_RETUNE_REQUEST;
+ if (sc->force_card_present)
+ val32 |= SDHCI_CARD_PRESENT;
+ return (val32);
+ }
+
+ /*
+ * fsl_sdhci_intr() can synthesize a DATA_END interrupt following a
+ * command with an R1B response, mix it into the hardware status.
+ */
+ if (off == SDHCI_INT_STATUS) {
+ return (val32 | sc->r1bfix_intmask);
+ }
+
+ return val32;
+}
+
+static void
+fsl_sdhci_read_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
+ uint32_t *data, bus_size_t count)
+{
+ struct fsl_sdhci_softc *sc = device_get_softc(dev);
+
+ bus_read_multi_4(sc->mem_res, off, data, count);
+}
+
+static void
+fsl_sdhci_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint8_t val)
+{
+ struct fsl_sdhci_softc *sc = device_get_softc(dev);
+ uint32_t val32;
+
+ /*
+ * Most of the things in the standard host control register are in the
+ * hardware's wider protocol control register, but some of the bits are
+ * moved around.
+ */
+ if (off == SDHCI_HOST_CONTROL) {
+ val32 = RD4(sc, SDHC_PROT_CTRL);
+ val32 &= ~(SDHC_PROT_LED | SDHC_PROT_DMA_MASK |
+ SDHC_PROT_WIDTH_MASK | SDHC_PROT_CDTL | SDHC_PROT_CDSS);
+ val32 |= (val & SDHCI_CTRL_LED);
+ if (val & SDHCI_CTRL_8BITBUS)
+ val32 |= SDHC_PROT_WIDTH_8BIT;
+ else
+ val32 |= (val & SDHCI_CTRL_4BITBUS);
+ val32 |= (val & (SDHCI_CTRL_SDMA | SDHCI_CTRL_ADMA2)) << 4;
+ val32 |= (val & (SDHCI_CTRL_CARD_DET | SDHCI_CTRL_FORCE_CARD));
+ WR4(sc, SDHC_PROT_CTRL, val32);
+ return;
+ }
+
+ /* XXX I can't find the bus power on/off knob; do nothing. */
+ if (off == SDHCI_POWER_CONTROL) {
+ return;
+ }
+#ifdef __powerpc__
+ /* XXX Reset doesn't seem to work as expected. Do nothing for now. */
+ if (off == SDHCI_SOFTWARE_RESET)
+ return;
+#endif
+
+ val32 = RD4(sc, off & ~3);
+ val32 &= ~(0xff << (off & 3) * 8);
+ val32 |= (val << (off & 3) * 8);
+
+ WR4(sc, off & ~3, val32);
+}
+
+static void
+fsl_sdhci_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint16_t val)
+{
+ struct fsl_sdhci_softc *sc = device_get_softc(dev);
+ uint32_t val32;
+
+ /*
+ * The clock control stuff is complex enough to have its own function
+ * that can handle the ESDHC versus USDHC differences.
+ */
+ if (off == SDHCI_CLOCK_CONTROL) {
+ fsl_sdhc_set_clock(sc, val);
+ return;
+ }
+
+ /*
+ * Figure out whether we need to check the DAT0 line for busy status at
+ * interrupt time. The controller should be doing this, but for some
+ * reason it doesn't. There are two cases:
+ * - R1B response with no data transfer should generate a DATA_END (aka
+ * TRANSFER_COMPLETE) interrupt after waiting for busy, but if
+ * there's no data transfer there's no DATA_END interrupt. This is
+ * documented; they seem to think it's a feature.
+ * - R1B response after Auto-CMD12 appears to not work, even though
+ * there's a control bit for it (bit 3) in the vendor register.
+ * When we're starting a command that needs a manual DAT0 line check at
+ * interrupt time, we leave ourselves a note in r1bfix_type so that we
+ * can do the extra work in fsl_sdhci_intr().
+ */
+ if (off == SDHCI_COMMAND_FLAGS) {
+ if (val & SDHCI_CMD_DATA) {
+ const uint32_t MBAUTOCMD = SDHCI_TRNS_ACMD12 | SDHCI_TRNS_MULTI;
+ val32 = RD4(sc, USDHC_MIX_CONTROL);
+ if ((val32 & MBAUTOCMD) == MBAUTOCMD)
+ sc->r1bfix_type = R1BFIX_AC12;
+ } else {
+ if ((val & SDHCI_CMD_RESP_MASK) == SDHCI_CMD_RESP_SHORT_BUSY) {
+ WR4(sc, SDHCI_INT_ENABLE, slot->intmask | SDHCI_INT_RESPONSE);
+ WR4(sc, SDHCI_SIGNAL_ENABLE, slot->intmask | SDHCI_INT_RESPONSE);
+ sc->r1bfix_type = R1BFIX_NODATA;
+ }
+ }
+ }
+
+ /*
+ * The USDHC hardware moved the transfer mode bits to mixed control; we
+ * just write them there and we're done. The ESDHC hardware has the
+ * typical combined cmd-and-mode register that allows only 32-bit
+ * access, so when writing the mode bits just save them, then later when
+ * writing the command bits, add in the saved mode bits.
+ */
+ if (sc->hwtype == HWTYPE_USDHC) {
+ if (off == SDHCI_TRANSFER_MODE) {
+ val32 = RD4(sc, USDHC_MIX_CONTROL);
+ val32 &= ~0x3f;
+ val32 |= val & 0x37;
+ // XXX acmd23 not supported here (or by sdhci driver)
+ WR4(sc, USDHC_MIX_CONTROL, val32);
+ return;
+ }
+ } else if (sc->hwtype == HWTYPE_ESDHC) {
+ if (off == SDHCI_TRANSFER_MODE) {
+ sc->cmd_and_mode =
+ (sc->cmd_and_mode & 0xffff0000) | val;
+ return;
+ } else if (off == SDHCI_COMMAND_FLAGS) {
+ sc->cmd_and_mode =
+ (sc->cmd_and_mode & 0xffff) | (val << 16);
+ WR4(sc, SDHCI_TRANSFER_MODE, sc->cmd_and_mode);
+ return;
+ }
+ }
+
+ val32 = RD4(sc, off & ~3);
+ val32 &= ~(0xffff << (off & 3) * 8);
+ val32 |= ((val & 0xffff) << (off & 3) * 8);
+ WR4(sc, off & ~3, val32);
+}
+
+static void
+fsl_sdhci_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t val)
+{
+ struct fsl_sdhci_softc *sc = device_get_softc(dev);
+
+ /* Clear synthesized interrupts, then pass the value to the hardware. */
+ if (off == SDHCI_INT_STATUS) {
+ sc->r1bfix_intmask &= ~val;
+ }
+
+ WR4(sc, off, val);
+}
+
+static void
+fsl_sdhci_write_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
+ uint32_t *data, bus_size_t count)
+{
+ struct fsl_sdhci_softc *sc = device_get_softc(dev);
+
+ bus_write_multi_4(sc->mem_res, off, data, count);
+}
+
+static uint16_t
+fsl_sdhc_get_clock(struct fsl_sdhci_softc *sc)
+{
+ uint16_t val;
+
+ /*
+ * Whenever the sdhci driver writes the clock register we save a
+ * snapshot of just the frequency bits, so that we can play them back
+ * here on a register read without recalculating the frequency from the
+ * prescalar and divisor bits in the real register. We'll start with
+ * those bits, and mix in the clock status and enable bits that come
+ * from different places depending on which hardware we've got.
+ */
+ val = sc->sdclockreg_freq_bits;
+
+ /*
+ * The internal clock is always enabled (actually, the hardware manages
+ * it). Whether the internal clock is stable yet after a frequency
+ * change comes from the present-state register on both hardware types.
+ */
+ val |= SDHCI_CLOCK_INT_EN;
+ if (RD4(sc, SDHC_PRES_STATE) & SDHC_PRES_SDSTB)
+ val |= SDHCI_CLOCK_INT_STABLE;
+
+ /*
+ * On i.MX ESDHC hardware the card bus clock enable is in the usual
+ * sdhci register but it's a different bit, so transcribe it (note the
+ * difference between standard SDHCI_ and Freescale SDHC_ prefixes
+ * here). On USDHC and QorIQ ESDHC hardware there is a force-on bit, but
+ * no force-off for the card bus clock (the hardware runs the clock when
+ * transfers are active no matter what), so we always say the clock is
+ * on.
+ * XXX Maybe we should say it's in whatever state the sdhci driver last
+ * set it to.
+ */
+ if (sc->hwtype == HWTYPE_ESDHC) {
+#ifdef __arm__
+ if (RD4(sc, SDHC_SYS_CTRL) & SDHC_CLK_SDCLKEN)
+#endif
+ val |= SDHCI_CLOCK_CARD_EN;
+ } else {
+ val |= SDHCI_CLOCK_CARD_EN;
+ }
+
+ return (val);
+}
+
+static void
+fsl_sdhc_set_clock(struct fsl_sdhci_softc *sc, uint16_t val)
+{
+ uint32_t divisor, freq, prescale, val32;
+
+ val32 = RD4(sc, SDHCI_CLOCK_CONTROL);
+
+ /*
+ * Save the frequency-setting bits in SDHCI format so that we can play
+ * them back in get_clock without complex decoding of hardware regs,
+ * then deal with the freqency part of the value based on hardware type.
+ */
+ sc->sdclockreg_freq_bits = val & SDHCI_DIVIDERS_MASK;
+ if (sc->hwtype == HWTYPE_ESDHC) {
+ /*
+ * The i.MX5 ESDHC hardware requires the driver to manually
+ * start and stop the sd bus clock. If the enable bit is not
+ * set, turn off the clock in hardware and we're done, otherwise
+ * decode the requested frequency. ESDHC hardware is sdhci 2.0;
+ * the sdhci driver will use the original 8-bit divisor field
+ * and the "base / 2^N" divisor scheme.
+ */
+ if ((val & SDHCI_CLOCK_CARD_EN) == 0) {
+#ifdef __arm__
+ /* On QorIQ, this is a reserved bit. */
+ WR4(sc, SDHCI_CLOCK_CONTROL, val32 & ~SDHC_CLK_SDCLKEN);
+#endif
+ return;
+
+ }
+ divisor = (val >> SDHCI_DIVIDER_SHIFT) & SDHCI_DIVIDER_MASK;
+ freq = sc->baseclk_hz >> ffs(divisor);
+ } else {
+ /*
+ * The USDHC hardware provides only "force always on" control
+ * over the sd bus clock, but no way to turn it off. (If a cmd
+ * or data transfer is in progress the clock is on, otherwise it
+ * is off.) If the clock is being disabled, we can just return
+ * now, otherwise we decode the requested frequency. USDHC
+ * hardware is sdhci 3.0; the sdhci driver will use a 10-bit
+ * divisor using the "base / 2*N" divisor scheme.
+ */
+ if ((val & SDHCI_CLOCK_CARD_EN) == 0)
+ return;
+ divisor = ((val >> SDHCI_DIVIDER_SHIFT) & SDHCI_DIVIDER_MASK) |
+ ((val >> SDHCI_DIVIDER_HI_SHIFT) & SDHCI_DIVIDER_HI_MASK) <<
+ SDHCI_DIVIDER_MASK_LEN;
+ if (divisor == 0)
+ freq = sc->baseclk_hz;
+ else
+ freq = sc->baseclk_hz / (2 * divisor);
+ }
+
+ /*
+ * Get a prescaler and final divisor to achieve the desired frequency.
+ */
+ for (prescale = 2; freq < sc->baseclk_hz / (prescale * 16);)
+ prescale <<= 1;
+
+ for (divisor = 1; freq < sc->baseclk_hz / (prescale * divisor);)
+ ++divisor;
+
+#ifdef DEBUG
+ device_printf(sc->dev,
+ "desired SD freq: %d, actual: %d; base %d prescale %d divisor %d\n",
+ freq, sc->baseclk_hz / (prescale * divisor), sc->baseclk_hz,
+ prescale, divisor);
+#endif
+
+ /*
+ * Adjust to zero-based values, and store them to the hardware.
+ */
+ prescale >>= 1;
+ divisor -= 1;
+
+ val32 &= ~(SDHC_CLK_DIVISOR_MASK | SDHC_CLK_PRESCALE_MASK);
+ val32 |= divisor << SDHC_CLK_DIVISOR_SHIFT;
+ val32 |= prescale << SDHC_CLK_PRESCALE_SHIFT;
+ val32 |= SDHC_CLK_IPGEN;
+ WR4(sc, SDHCI_CLOCK_CONTROL, val32);
+}
+
+static boolean_t
+fsl_sdhci_r1bfix_is_wait_done(struct fsl_sdhci_softc *sc)
+{
+ uint32_t inhibit;
+
+ mtx_assert(&sc->slot.mtx, MA_OWNED);
+
+ /*
+ * Check the DAT0 line status using both the DLA (data line active) and
+ * CDIHB (data inhibit) bits in the present state register. In theory
+ * just DLA should do the trick, but in practice it takes both. If the
+ * DAT0 line is still being held and we're not yet beyond the timeout
+ * point, just schedule another callout to check again later.
+ */
+ inhibit = RD4(sc, SDHC_PRES_STATE) & (SDHC_PRES_DLA | SDHC_PRES_CDIHB);
+
+ if (inhibit && getsbinuptime() < sc->r1bfix_timeout_at) {
+ callout_reset_sbt(&sc->r1bfix_callout, SBT_1MS, 0,
+ fsl_sdhci_r1bfix_func, sc, 0);
+ return (false);
+ }
+
+ /*
+ * If we reach this point with the inhibit bits still set, we've got a
+ * timeout, synthesize a DATA_TIMEOUT interrupt. Otherwise the DAT0
+ * line has been released, and we synthesize a DATA_END, and if the type
+ * of fix needed was on a command-without-data we also now add in the
+ * original INT_RESPONSE that we suppressed earlier.
+ */
+ if (inhibit)
+ sc->r1bfix_intmask |= SDHCI_INT_DATA_TIMEOUT;
+ else {
+ sc->r1bfix_intmask |= SDHCI_INT_DATA_END;
+ if (sc->r1bfix_type == R1BFIX_NODATA)
+ sc->r1bfix_intmask |= SDHCI_INT_RESPONSE;
+ }
+
+ sc->r1bfix_type = R1BFIX_NONE;
+ return (true);
+}
+
+static void
+fsl_sdhci_r1bfix_func(void * arg)
+{
+ struct fsl_sdhci_softc *sc = arg;
+ boolean_t r1bwait_done;
+
+ mtx_lock(&sc->slot.mtx);
+ r1bwait_done = fsl_sdhci_r1bfix_is_wait_done(sc);
+ mtx_unlock(&sc->slot.mtx);
+ if (r1bwait_done)
+ sdhci_generic_intr(&sc->slot);
+}
+
+static void
+fsl_sdhci_intr(void *arg)
+{
+ struct fsl_sdhci_softc *sc = arg;
+ uint32_t intmask;
+
+ mtx_lock(&sc->slot.mtx);
+
+ /*
+ * Manually check the DAT0 line for R1B response types that the
+ * controller fails to handle properly. The controller asserts the done
+ * interrupt while the card is still asserting busy with the DAT0 line.
+ *
+ * We check DAT0 immediately because most of the time, especially on a
+ * read, the card will actually be done by time we get here. If it's
+ * not, then the wait_done routine will schedule a callout to re-check
+ * periodically until it is done. In that case we clear the interrupt
+ * out of the hardware now so that we can present it later when the DAT0
+ * line is released.
+ *
+ * If we need to wait for the DAT0 line to be released, we set up a
+ * timeout point 250ms in the future. This number comes from the SD
+ * spec, which allows a command to take that long. In the real world,
+ * cards tend to take 10-20ms for a long-running command such as a write
+ * or erase that spans two pages.
+ */
+ switch (sc->r1bfix_type) {
+ case R1BFIX_NODATA:
+ intmask = RD4(sc, SDHCI_INT_STATUS) & SDHCI_INT_RESPONSE;
+ break;
+ case R1BFIX_AC12:
+ intmask = RD4(sc, SDHCI_INT_STATUS) & SDHCI_INT_DATA_END;
+ break;
+ default:
+ intmask = 0;
+ break;
+ }
+ if (intmask) {
+ sc->r1bfix_timeout_at = getsbinuptime() + 250 * SBT_1MS;
+ if (!fsl_sdhci_r1bfix_is_wait_done(sc)) {
+ WR4(sc, SDHCI_INT_STATUS, intmask);
+ bus_barrier(sc->mem_res, SDHCI_INT_STATUS, 4,
+ BUS_SPACE_BARRIER_WRITE);
+ }
+ }
+
+ mtx_unlock(&sc->slot.mtx);
+ sdhci_generic_intr(&sc->slot);
+}
+
+static int
+fsl_sdhci_get_ro(device_t bus, device_t child)
+{
+ struct fsl_sdhci_softc *sc = device_get_softc(bus);
+
+ if (RD4(sc, SDHCI_PRESENT_STATE) & SDHC_PRES_WPSPL)
+ return (false);
+ return (true);
+}
+
+#ifdef __powerpc__
+static uint32_t
+fsl_sdhci_get_platform_clock(device_t dev)
+{
+ device_t parent;
+ phandle_t node;
+ uint32_t clock;
+
+ node = ofw_bus_get_node(dev);
+
+ /* Get sdhci node properties */
+ if((OF_getprop(node, "clock-frequency", (void *)&clock,
+ sizeof(clock)) <= 0) || (clock == 0)) {
+
+ /*
+ * Trying to get clock from parent device (soc) if correct
+ * clock cannot be acquired from sdhci node.
+ */
+ parent = device_get_parent(dev);
+ node = ofw_bus_get_node(parent);
+
+ /* Get soc properties */
+ if ((OF_getprop(node, "bus-frequency", (void *)&clock,
+ sizeof(clock)) <= 0) || (clock == 0)) {
+ device_printf(dev,"Cannot acquire correct sdhci "
+ "frequency from DTS.\n");
+
+ return (0);
+ }
+ /* eSDHC clock is 1/2 platform clock. */
+ clock /= 2;
+ }
+
+ if (bootverbose)
+ device_printf(dev, "Acquired clock: %d from DTS\n", clock);
+
+ return (clock);
+}
+#endif
+
+
+static int
+fsl_sdhci_detach(device_t dev)
+{
+
+ return (EBUSY);
+}
+
+static int
+fsl_sdhci_attach(device_t dev)
+{
+ struct fsl_sdhci_softc *sc = device_get_softc(dev);
+ int rid, err;
+ phandle_t node;
+ uint32_t protctl;
+
+ sc->dev = dev;
+
+ sc->hwtype = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+ if (sc->hwtype == HWTYPE_NONE)
+ panic("Impossible: not compatible in fsl_sdhci_attach()");
+
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->mem_res) {
+ device_printf(dev, "cannot allocate memory window\n");
+ err = ENXIO;
+ goto fail;
+ }
+
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (!sc->irq_res) {
+ device_printf(dev, "cannot allocate interrupt\n");
+ err = ENXIO;
+ goto fail;
+ }
+
+ if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, fsl_sdhci_intr, sc, &sc->intr_cookie)) {
+ device_printf(dev, "cannot setup interrupt handler\n");
+ err = ENXIO;
+ goto fail;
+ }
+
+ sc->slot.quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK;
+
+ /*
+ * DMA is not really broken, I just haven't implemented it yet.
+ */
+ sc->slot.quirks |= SDHCI_QUIRK_BROKEN_DMA;
+
+ /*
+ * Set the buffer watermark level to 128 words (512 bytes) for both read
+ * and write. The hardware has a restriction that when the read or
+ * write ready status is asserted, that means you can read exactly the
+ * number of words set in the watermark register before you have to
+ * re-check the status and potentially wait for more data. The main
+ * sdhci driver provides no hook for doing status checking on less than
+ * a full block boundary, so we set the watermark level to be a full
+ * block. Reads and writes where the block size is less than the
+ * watermark size will work correctly too, no need to change the
+ * watermark for different size blocks. However, 128 is the maximum
+ * allowed for the watermark, so PIO is limitted to 512 byte blocks
+ * (which works fine for SD cards, may be a problem for SDIO some day).
+ *
+ * XXX need named constants for this stuff.
+ */
+#ifdef __powerpc__
+ /* P1022 has the '*_BRST_LEN' fields as reserved, always reading 0x10 */
+ if ((SVR_VER(mfspr(SPR_SVR)) & 0xfff6) == SVR_P1022 )
+ WR4(sc, SDHC_WTMK_LVL, 0x10801080);
+ else
+#endif
+ WR4(sc, SDHC_WTMK_LVL, 0x08800880);
+
+ /*
+ * We read in native byte order in the main driver, but the register
+ * defaults to little endian.
+ */
+#ifdef __powerpc__
+ sc->baseclk_hz = fsl_sdhci_get_platform_clock(dev);
+#else
+ sc->baseclk_hz = imx_ccm_sdhci_hz();
+#endif
+ sc->slot.max_clk = sc->baseclk_hz;
+
+ /*
+ * If the slot is flagged with the non-removable property, set our flag
+ * to always force the SDHCI_CARD_PRESENT bit on.
+ *
+ * XXX Workaround for gpio-based card detect...
+ *
+ * We don't have gpio support yet. If there's a cd-gpios property just
+ * force the SDHCI_CARD_PRESENT bit on for now. If there isn't really a
+ * card there it will fail to probe at the mmc layer and nothing bad
+ * happens except instantiating an mmcN device for an empty slot.
+ */
+ node = ofw_bus_get_node(dev);
+ if (OF_hasprop(node, "non-removable"))
+ sc->force_card_present = true;
+ else if (OF_hasprop(node, "cd-gpios")) {
+ /* XXX put real gpio hookup here. */
+ sc->force_card_present = true;
+ }
+#ifdef __powerpc__
+ /* Default to big-endian on powerpc */
+ protctl = RD4(sc, SDHC_PROT_CTRL);
+ protctl &= ~SDHC_PROT_EMODE_MASK;
+ if (OF_hasprop(node, "little-endian"))
+ protctl |= SDHC_PROT_EMODE_LITTLE;
+ else
+ protctl |= SDHC_PROT_EMODE_BIG;
+ WR4(sc, SDHC_PROT_CTRL, protctl);
+#endif
+
+ callout_init(&sc->r1bfix_callout, 1);
+ sdhci_init_slot(dev, &sc->slot, 0);
+
+ bus_generic_probe(dev);
+ bus_generic_attach(dev);
+
+ sdhci_start_slot(&sc->slot);
+
+ return (0);
+
+fail:
+ if (sc->intr_cookie)
+ bus_teardown_intr(dev, sc->irq_res, sc->intr_cookie);
+ if (sc->irq_res)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+ if (sc->mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+
+ return (err);
+}
+
+static int
+fsl_sdhci_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) {
+ case HWTYPE_ESDHC:
+ device_set_desc(dev, "Freescale eSDHC controller");
+ return (BUS_PROBE_DEFAULT);
+ case HWTYPE_USDHC:
+ device_set_desc(dev, "Freescale uSDHC controller");
+ return (BUS_PROBE_DEFAULT);
+ default:
+ break;
+ }
+ return (ENXIO);
+}
+
+static device_method_t fsl_sdhci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, fsl_sdhci_probe),
+ DEVMETHOD(device_attach, fsl_sdhci_attach),
+ DEVMETHOD(device_detach, fsl_sdhci_detach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_read_ivar, sdhci_generic_read_ivar),
+ DEVMETHOD(bus_write_ivar, sdhci_generic_write_ivar),
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+
+ /* MMC bridge interface */
+ DEVMETHOD(mmcbr_update_ios, sdhci_generic_update_ios),
+ DEVMETHOD(mmcbr_request, sdhci_generic_request),
+ DEVMETHOD(mmcbr_get_ro, fsl_sdhci_get_ro),
+ DEVMETHOD(mmcbr_acquire_host, sdhci_generic_acquire_host),
+ DEVMETHOD(mmcbr_release_host, sdhci_generic_release_host),
+
+ /* SDHCI registers accessors */
+ DEVMETHOD(sdhci_read_1, fsl_sdhci_read_1),
+ DEVMETHOD(sdhci_read_2, fsl_sdhci_read_2),
+ DEVMETHOD(sdhci_read_4, fsl_sdhci_read_4),
+ DEVMETHOD(sdhci_read_multi_4, fsl_sdhci_read_multi_4),
+ DEVMETHOD(sdhci_write_1, fsl_sdhci_write_1),
+ DEVMETHOD(sdhci_write_2, fsl_sdhci_write_2),
+ DEVMETHOD(sdhci_write_4, fsl_sdhci_write_4),
+ DEVMETHOD(sdhci_write_multi_4, fsl_sdhci_write_multi_4),
+
+ { 0, 0 }
+};
+
+static devclass_t fsl_sdhci_devclass;
+
+static driver_t fsl_sdhci_driver = {
+ "sdhci_fsl",
+ fsl_sdhci_methods,
+ sizeof(struct fsl_sdhci_softc),
+};
+
+DRIVER_MODULE(sdhci_fsl, simplebus, fsl_sdhci_driver, fsl_sdhci_devclass, 0, 0);
+MODULE_DEPEND(sdhci_fsl, sdhci, 1, 1, 1);
+DRIVER_MODULE(mmc, sdhci_fsl, mmc_driver, mmc_devclass, NULL, NULL);
+MODULE_DEPEND(sdhci_fsl, mmc, 1, 1, 1);
Index: sys/dev/sdhci/sdhci.c
===================================================================
--- sys/dev/sdhci/sdhci.c
+++ sys/dev/sdhci/sdhci.c
@@ -1309,7 +1309,7 @@
void
sdhci_generic_intr(struct sdhci_slot *slot)
{
- uint32_t intmask;
+ uint32_t intmask, present;
SDHCI_LOCK(slot);
/* Read slot interrupt status. */
@@ -1323,6 +1323,13 @@
/* Handle card presence interrupts. */
if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
+ present = RD4(slot, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT;
+ slot->intmask &=
+ ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
+ slot->intmask |= present ? SDHCI_INT_CARD_REMOVE :
+ SDHCI_INT_CARD_INSERT;
+ WR4(slot, SDHCI_INT_ENABLE, slot->intmask);
+ WR4(slot, SDHCI_SIGNAL_ENABLE, slot->intmask);
WR4(slot, SDHCI_INT_STATUS, intmask &
(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE));
Index: sys/powerpc/conf/MPC85XX
===================================================================
--- sys/powerpc/conf/MPC85XX
+++ sys/powerpc/conf/MPC85XX
@@ -81,6 +81,7 @@
device loop
device md
device miibus
+device nand
device pass
device pci
device quicc
@@ -88,6 +89,7 @@
#device rl
device scbus
device scc
+device sdhci
device sec
device tsec
device dpaa
Index: sys/powerpc/conf/MPC85XXSPE
===================================================================
--- sys/powerpc/conf/MPC85XXSPE
+++ sys/powerpc/conf/MPC85XXSPE
@@ -81,6 +81,7 @@
device loop
device md
device miibus
+device nand
device pass
device pci
device quicc
@@ -88,6 +89,7 @@
#device rl
device scbus
device scc
+device sdhci
device sec
device tsec
device dpaa
Index: sys/powerpc/mpc85xx/fsl_sdhc.h
===================================================================
--- sys/powerpc/mpc85xx/fsl_sdhc.h
+++ /dev/null
@@ -1,297 +0,0 @@
-/*-
- * Copyright (c) 2011-2012 Semihalf
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * $FreeBSD$
- */
-
-#ifndef FSL_SDHC_H_
-#define FSL_SDHC_H_
-
-#include <sys/cdefs.h>
-
-#include <sys/param.h>
-#include <sys/bus.h>
-#include <sys/kernel.h>
-#include <sys/lock.h>
-#include <sys/module.h>
-#include <sys/mutex.h>
-#include <sys/rman.h>
-#include <sys/sysctl.h>
-#include <sys/systm.h>
-#include <sys/taskqueue.h>
-
-#include <machine/bus.h>
-
-#include <dev/mmc/bridge.h>
-#include <dev/mmc/mmcreg.h>
-#include <dev/mmc/mmcvar.h>
-#include <dev/mmc/mmcbrvar.h>
-
-#include "mmcbr_if.h"
-
-
-/*****************************************************************************
- * Private defines
- *****************************************************************************/
-struct slot {
- uint32_t clock;
-};
-
-struct fsl_sdhc_softc {
- device_t self;
- device_t child;
-
- bus_space_handle_t bsh;
- bus_space_tag_t bst;
-
- struct resource *mem_resource;
- int mem_rid;
- struct resource *irq_resource;
- int irq_rid;
- void *ihl;
-
- bus_dma_tag_t dma_tag;
- bus_dmamap_t dma_map;
- uint32_t* dma_mem;
- bus_addr_t dma_phys;
-
- struct mtx mtx;
-
- struct task card_detect_task;
- struct callout card_detect_callout;
-
- struct mmc_host mmc_host;
-
- struct slot slot;
- uint32_t bus_busy;
- uint32_t platform_clock;
-
- struct mmc_request *request;
- int data_done;
- int command_done;
- int use_dma;
- uint32_t* data_ptr;
- uint32_t data_offset;
-};
-
-#define FSL_SDHC_RESET_DELAY 50
-
-#define FSL_SDHC_BASE_CLOCK_DIV (2)
-#define FSL_SDHC_MAX_DIV (FSL_SDHC_BASE_CLOCK_DIV * 256 * 16)
-#define FSL_SDHC_MIN_DIV (FSL_SDHC_BASE_CLOCK_DIV * 2)
-#define FSL_SDHC_MAX_CLOCK (50000000)
-
-#define FSL_SDHC_MAX_BLOCK_COUNT (65535)
-#define FSL_SDHC_MAX_BLOCK_SIZE (4096)
-
-#define FSL_SDHC_FIFO_BUF_SIZE (64) /* Water-mark level. */
-#define FSL_SDHC_FIFO_BUF_WORDS (FSL_SDHC_FIFO_BUF_SIZE / 4)
-
-#define FSL_SDHC_DMA_SEGMENT_SIZE (1024)
-#define FSL_SDHC_DMA_ALIGNMENT (4)
-#define FSL_SDHC_DMA_BLOCK_SIZE FSL_SDHC_MAX_BLOCK_SIZE
-
-
-/*
- * Offsets of SD HC registers
- */
-enum sdhc_reg_off {
- SDHC_DSADDR = 0x000,
- SDHC_BLKATTR = 0x004,
- SDHC_CMDARG = 0x008,
- SDHC_XFERTYP = 0x00c,
- SDHC_CMDRSP0 = 0x010,
- SDHC_CMDRSP1 = 0x014,
- SDHC_CMDRSP2 = 0x018,
- SDHC_CMDRSP3 = 0x01c,
- SDHC_DATPORT = 0x020,
- SDHC_PRSSTAT = 0x024,
- SDHC_PROCTL = 0x028,
- SDHC_SYSCTL = 0x02c,
- SDHC_IRQSTAT = 0x030,
- SDHC_IRQSTATEN = 0x034,
- SDHC_IRQSIGEN = 0x038,
- SDHC_AUTOC12ERR = 0x03c,
- SDHC_HOSTCAPBLT = 0x040,
- SDHC_WML = 0x044,
- SDHC_FEVT = 0x050,
- SDHC_HOSTVER = 0x0fc,
- SDHC_DCR = 0x40c
-};
-
-enum sysctl_bit {
- SYSCTL_INITA = 0x08000000,
- SYSCTL_RSTD = 0x04000000,
- SYSCTL_RSTC = 0x02000000,
- SYSCTL_RSTA = 0x01000000,
- SYSCTL_DTOCV = 0x000f0000,
- SYSCTL_SDCLKFS = 0x0000ff00,
- SYSCTL_DVS = 0x000000f0,
- SYSCTL_PEREN = 0x00000004,
- SYSCTL_HCKEN = 0x00000002,
- SYSCTL_IPGEN = 0x00000001
-};
-
-#define HEX_LEFT_SHIFT(x) (4 * x)
-enum sysctl_shift {
- SHIFT_DTOCV = HEX_LEFT_SHIFT(4),
- SHIFT_SDCLKFS = HEX_LEFT_SHIFT(2),
- SHIFT_DVS = HEX_LEFT_SHIFT(1)
-};
-
-enum proctl_bit {
- PROCTL_WECRM = 0x04000000,
- PROCTL_WECINS = 0x02000000,
- PROCTL_WECINT = 0x01000000,
- PROCTL_RWCTL = 0x00040000,
- PROCTL_CREQ = 0x00020000,
- PROCTL_SABGREQ = 0x00010000,
- PROCTL_CDSS = 0x00000080,
- PROCTL_CDTL = 0x00000040,
- PROCTL_EMODE = 0x00000030,
- PROCTL_D3CD = 0x00000008,
- PROCTL_DTW = 0x00000006
-};
-
-enum dtw {
- DTW_1 = 0x00000000,
- DTW_4 = 0x00000002,
- DTW_8 = 0x00000004
-};
-
-enum prsstat_bit {
- PRSSTAT_DLSL = 0xff000000,
- PRSSTAT_CLSL = 0x00800000,
- PRSSTAT_WPSPL = 0x00080000,
- PRSSTAT_CDPL = 0x00040000,
- PRSSTAT_CINS = 0x00010000,
- PRSSTAT_BREN = 0x00000800,
- PRSSTAT_BWEN = 0x00000400,
- PRSSTAT_RTA = 0x00000200,
- PRSSTAT_WTA = 0x00000100,
- PRSSTAT_SDOFF = 0x00000080,
- PRSSTAT_PEROFF = 0x00000040,
- PRSSTAT_HCKOFF = 0x00000020,
- PRSSTAT_IPGOFF = 0x00000010,
- PRSSTAT_DLA = 0x00000004,
- PRSSTAT_CDIHB = 0x00000002,
- PRSSTAT_CIHB = 0x00000001
-
-};
-
-enum irq_bits {
- IRQ_DMAE = 0x10000000,
- IRQ_AC12E = 0x01000000,
- IRQ_DEBE = 0x00400000,
- IRQ_DCE = 0x00200000,
- IRQ_DTOE = 0x00100000,
- IRQ_CIE = 0x00080000,
- IRQ_CEBE = 0x00040000,
- IRQ_CCE = 0x00020000,
- IRQ_CTOE = 0x00010000,
- IRQ_CINT = 0x00000100,
- IRQ_CRM = 0x00000080,
- IRQ_CINS = 0x00000040,
- IRQ_BRR = 0x00000020,
- IRQ_BWR = 0x00000010,
- IRQ_DINT = 0x00000008,
- IRQ_BGE = 0x00000004,
- IRQ_TC = 0x00000002,
- IRQ_CC = 0x00000001
-};
-
-enum irq_masks {
- IRQ_ERROR_DATA_MASK = IRQ_DMAE | IRQ_DEBE | IRQ_DCE | IRQ_DTOE,
- IRQ_ERROR_CMD_MASK = IRQ_AC12E | IRQ_CIE | IRQ_CTOE | IRQ_CCE |
- IRQ_CEBE
-};
-
-enum dcr_bits {
- DCR_PRI = 0x0000c000,
- DCR_SNOOP = 0x00000040,
- DCR_AHB2MAG_BYPASS = 0x00000020,
- DCR_RD_SAFE = 0x00000004,
- DCR_RD_PFE = 0x00000002,
- DCR_RD_PF_SIZE = 0x00000001
-};
-
-#define DCR_PRI_SHIFT (14)
-
-enum xfertyp_bits {
- XFERTYP_CMDINX = 0x3f000000,
- XFERTYP_CMDTYP = 0x00c00000,
- XFERTYP_DPSEL = 0x00200000,
- XFERTYP_CICEN = 0x00100000,
- XFERTYP_CCCEN = 0x00080000,
- XFERTYP_RSPTYP = 0x00030000,
- XFERTYP_MSBSEL = 0x00000020,
- XFERTYP_DTDSEL = 0x00000010,
- XFERTYP_AC12EN = 0x00000004,
- XFERTYP_BCEN = 0x00000002,
- XFERTYP_DMAEN = 0x00000001
-};
-
-#define CMDINX_SHIFT (24)
-
-enum xfertyp_cmdtyp {
- CMDTYP_NORMAL = 0x00000000,
- CMDYTP_SUSPEND = 0x00400000,
- CMDTYP_RESUME = 0x00800000,
- CMDTYP_ABORT = 0x00c00000
-};
-
-enum xfertyp_rsptyp {
- RSPTYP_NONE = 0x00000000,
- RSPTYP_136 = 0x00010000,
- RSPTYP_48 = 0x00020000,
- RSPTYP_48_BUSY = 0x00030000
-};
-
-enum blkattr_bits {
- BLKATTR_BLKSZE = 0x00001fff,
- BLKATTR_BLKCNT = 0xffff0000
-};
-#define BLKATTR_BLOCK_COUNT(x) (x << 16)
-
-enum wml_bits {
- WR_WML = 0x00ff0000,
- RD_WML = 0x000000ff,
-};
-
-enum sdhc_bit_mask {
- MASK_CLOCK_CONTROL = 0x0000ffff,
- MASK_IRQ_ALL = IRQ_DMAE | IRQ_AC12E | IRQ_DEBE | IRQ_DCE |
- IRQ_DTOE | IRQ_CIE | IRQ_CEBE | IRQ_CCE |
- IRQ_CTOE | IRQ_CINT | IRQ_CRM | IRQ_CINS |
- IRQ_BRR | IRQ_BWR | IRQ_DINT | IRQ_BGE |
- IRQ_TC | IRQ_CC,
-};
-
-enum sdhc_line {
- SDHC_DAT_LINE = 0x2,
- SDHC_CMD_LINE = 0x1
-};
-
-#endif /* FSL_SDHC_H_ */
Index: sys/powerpc/mpc85xx/fsl_sdhc.c
===================================================================
--- sys/powerpc/mpc85xx/fsl_sdhc.c
+++ /dev/null
@@ -1,1303 +0,0 @@
-/*-
- * Copyright (c) 2011-2012 Semihalf
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/*
- * Driver for Freescale integrated eSDHC controller.
- * Limitations:
- * - No support for multi-block transfers.
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include <sys/param.h>
-#include <sys/bus.h>
-#include <sys/kernel.h>
-#include <sys/lock.h>
-#include <sys/module.h>
-#include <sys/mutex.h>
-#include <sys/rman.h>
-#include <sys/sysctl.h>
-#include <sys/systm.h>
-#include <sys/taskqueue.h>
-
-#include <machine/bus.h>
-#include <machine/vmparam.h>
-
-#include <dev/fdt/fdt_common.h>
-#include <dev/ofw/ofw_bus.h>
-#include <dev/ofw/ofw_bus_subr.h>
-
-#include <dev/mmc/bridge.h>
-#include <dev/mmc/mmcreg.h>
-#include <dev/mmc/mmcvar.h>
-#include <dev/mmc/mmcbrvar.h>
-
-#include <powerpc/mpc85xx/mpc85xx.h>
-
-#include "opt_platform.h"
-
-#include "mmcbr_if.h"
-
-#include "fsl_sdhc.h"
-
-#ifdef DEBUG
-#define DPRINTF(fmt, arg...) printf("DEBUG %s(): " fmt, __FUNCTION__, ##arg)
-#else
-#define DPRINTF(fmt, arg...)
-#endif
-
-
-/*****************************************************************************
- * Register the driver
- *****************************************************************************/
-/* Forward declarations */
-static int fsl_sdhc_probe(device_t);
-static int fsl_sdhc_attach(device_t);
-static int fsl_sdhc_detach(device_t);
-
-static int fsl_sdhc_read_ivar(device_t, device_t, int, uintptr_t *);
-static int fsl_sdhc_write_ivar(device_t, device_t, int, uintptr_t);
-
-static int fsl_sdhc_update_ios(device_t, device_t);
-static int fsl_sdhc_request(device_t, device_t, struct mmc_request *);
-static int fsl_sdhc_get_ro(device_t, device_t);
-static int fsl_sdhc_acquire_host(device_t, device_t);
-static int fsl_sdhc_release_host(device_t, device_t);
-
-static device_method_t fsl_sdhc_methods[] = {
- /* device_if */
- DEVMETHOD(device_probe, fsl_sdhc_probe),
- DEVMETHOD(device_attach, fsl_sdhc_attach),
- DEVMETHOD(device_detach, fsl_sdhc_detach),
-
- /* Bus interface */
- DEVMETHOD(bus_read_ivar, fsl_sdhc_read_ivar),
- DEVMETHOD(bus_write_ivar, fsl_sdhc_write_ivar),
-
- /* OFW bus interface */
- DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat),
- DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model),
- DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name),
- DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node),
- DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type),
-
- /* mmcbr_if */
- DEVMETHOD(mmcbr_update_ios, fsl_sdhc_update_ios),
- DEVMETHOD(mmcbr_request, fsl_sdhc_request),
- DEVMETHOD(mmcbr_get_ro, fsl_sdhc_get_ro),
- DEVMETHOD(mmcbr_acquire_host, fsl_sdhc_acquire_host),
- DEVMETHOD(mmcbr_release_host, fsl_sdhc_release_host),
-
- {0, 0},
-};
-
-/* kobj_class definition */
-static driver_t fsl_sdhc_driver = {
- "sdhci_fsl",
- fsl_sdhc_methods,
- sizeof(struct fsl_sdhc_softc)
-};
-
-static devclass_t fsl_sdhc_devclass;
-
-DRIVER_MODULE(sdhci_fsl, simplebus, fsl_sdhc_driver, fsl_sdhc_devclass, 0, 0);
-DRIVER_MODULE(mmc, sdhci_fsl, mmc_driver, mmc_devclass, NULL, NULL);
-MODULE_DEPEND(sdhci_fsl, mmc, 1, 1, 1);
-
-/*****************************************************************************
- * Private methods
- *****************************************************************************/
-static inline int
-read4(struct fsl_sdhc_softc *sc, unsigned int offset)
-{
-
- return bus_space_read_4(sc->bst, sc->bsh, offset);
-}
-
-static inline void
-write4(struct fsl_sdhc_softc *sc, unsigned int offset, int value)
-{
-
- bus_space_write_4(sc->bst, sc->bsh, offset, value);
-}
-
-static inline void
-set_bit(struct fsl_sdhc_softc *sc, uint32_t offset, uint32_t mask)
-{
- uint32_t x = read4(sc, offset);
-
- write4(sc, offset, x | mask);
-}
-
-static inline void
-clear_bit(struct fsl_sdhc_softc *sc, uint32_t offset, uint32_t mask)
-{
- uint32_t x = read4(sc, offset);
-
- write4(sc, offset, x & ~mask);
-}
-
-static int
-wait_for_bit_clear(struct fsl_sdhc_softc *sc, enum sdhc_reg_off reg,
- uint32_t bit)
-{
- uint32_t timeout = 10;
- uint32_t stat;
-
- stat = read4(sc, reg);
- while (stat & bit) {
- if (timeout == 0) {
- return (-1);
- }
- --timeout;
- DELAY(1000);
- stat = read4(sc, reg);
- }
-
- return (0);
-}
-
-static int
-wait_for_free_line(struct fsl_sdhc_softc *sc, enum sdhc_line line)
-{
- uint32_t timeout = 100;
- uint32_t stat;
-
- stat = read4(sc, SDHC_PRSSTAT);
- while (stat & line) {
- if (timeout == 0) {
- return (-1);
- }
- --timeout;
- DELAY(1000);
- stat = read4(sc, SDHC_PRSSTAT);
- }
-
- return (0);
-}
-
-static uint32_t
-get_platform_clock(struct fsl_sdhc_softc *sc)
-{
- device_t self, parent;
- phandle_t node;
- uint32_t clock;
-
- self = sc->self;
- node = ofw_bus_get_node(self);
-
- /* Get sdhci node properties */
- if((OF_getprop(node, "clock-frequency", (void *)&clock,
- sizeof(clock)) <= 0) || (clock == 0)) {
-
- /*
- * Trying to get clock from parent device (soc) if correct
- * clock cannot be acquired from sdhci node.
- */
- parent = device_get_parent(self);
- node = ofw_bus_get_node(parent);
-
- /* Get soc properties */
- if ((OF_getprop(node, "bus-frequency", (void *)&clock,
- sizeof(clock)) <= 0) || (clock == 0)) {
- device_printf(self,"Cannot acquire correct sdhci "
- "frequency from DTS.\n");
-
- return (0);
- }
- }
-
- DPRINTF("Acquired clock: %d from DTS\n", clock);
-
- return (clock);
-}
-
-/**
- * Set clock driving card.
- * @param sc
- * @param clock Desired clock frequency in Hz
- */
-static void
-set_clock(struct fsl_sdhc_softc *sc, uint32_t clock)
-{
- uint32_t base_clock;
- uint32_t divisor, prescaler = 1;
- uint32_t round = 0;
-
- if (clock == sc->slot.clock)
- return;
-
- if (clock == 0) {
- clear_bit(sc, SDHC_SYSCTL, MASK_CLOCK_CONTROL | SYSCTL_PEREN |
- SYSCTL_HCKEN | SYSCTL_IPGEN);
- return;
- }
-
- base_clock = sc->platform_clock;
- round = base_clock & 0x2;
- base_clock >>= 2;
- base_clock += round;
- round = 0;
-
- /* SD specification 1.1 doesn't allow frequences above 50 MHz */
- if (clock > FSL_SDHC_MAX_CLOCK)
- clock = FSL_SDHC_MAX_CLOCK;
-
- /*
- * divisor = ceil(base_clock / clock)
- * TODO: Reconsider symmetric rounding here instead of ceiling.
- */
- divisor = howmany(base_clock, clock);
-
- while (divisor > 16) {
- round = divisor & 0x1;
- divisor >>= 1;
-
- prescaler <<= 1;
- }
- divisor += round - 1;
-
- /* Turn off the clock. */
- clear_bit(sc, SDHC_SYSCTL, MASK_CLOCK_CONTROL);
-
- /* Write clock settings. */
- set_bit(sc, SDHC_SYSCTL, (prescaler << SHIFT_SDCLKFS) |
- (divisor << SHIFT_DVS));
-
- /*
- * Turn on clocks.
- * TODO: This actually disables clock automatic gating off feature of
- * the controller which eventually should be enabled but as for now
- * it prevents controller from generating card insertion/removal
- * interrupts correctly.
- */
- set_bit(sc, SDHC_SYSCTL, SYSCTL_PEREN | SYSCTL_HCKEN | SYSCTL_IPGEN);
-
- sc->slot.clock = clock;
-
- DPRINTF("given clock = %d, computed clock = %d\n", clock,
- (base_clock / prescaler) / (divisor + 1));
-}
-
-static inline void
-send_80_clock_ticks(struct fsl_sdhc_softc *sc)
-{
- int err;
-
- err = wait_for_free_line(sc, SDHC_CMD_LINE | SDHC_DAT_LINE);
- if (err != 0) {
- device_printf(sc->self, "Can't acquire data/cmd lines\n");
- return;
- }
-
- set_bit(sc, SDHC_SYSCTL, SYSCTL_INITA);
- err = wait_for_bit_clear(sc, SDHC_SYSCTL, SYSCTL_INITA);
- if (err != 0) {
- device_printf(sc->self, "Can't send 80 clocks to the card.\n");
- }
-}
-
-static void
-set_bus_width(struct fsl_sdhc_softc *sc, enum mmc_bus_width width)
-{
-
- DPRINTF("setting bus width to %d\n", width);
- switch (width) {
- case bus_width_1:
- set_bit(sc, SDHC_PROCTL, DTW_1);
- break;
- case bus_width_4:
- set_bit(sc, SDHC_PROCTL, DTW_4);
- break;
- case bus_width_8:
- set_bit(sc, SDHC_PROCTL, DTW_8);
- break;
- default:
- device_printf(sc->self, "Unsupported bus width\n");
- }
-}
-
-static void
-reset_controller_all(struct fsl_sdhc_softc *sc)
-{
- uint32_t count = 5;
-
- set_bit(sc, SDHC_SYSCTL, SYSCTL_RSTA);
- while (read4(sc, SDHC_SYSCTL) & SYSCTL_RSTA) {
- DELAY(FSL_SDHC_RESET_DELAY);
- --count;
- if (count == 0) {
- device_printf(sc->self,
- "Can't reset the controller\n");
- return;
- }
- }
-}
-
-static void
-reset_controller_dat_cmd(struct fsl_sdhc_softc *sc)
-{
- int err;
-
- set_bit(sc, SDHC_SYSCTL, SYSCTL_RSTD | SYSCTL_RSTC);
- err = wait_for_bit_clear(sc, SDHC_SYSCTL, SYSCTL_RSTD | SYSCTL_RSTC);
- if (err != 0) {
- device_printf(sc->self, "Can't reset data & command part!\n");
- return;
- }
-}
-
-static void
-init_controller(struct fsl_sdhc_softc *sc)
-{
-
- /* Enable interrupts. */
-#ifdef FSL_SDHC_NO_DMA
- write4(sc, SDHC_IRQSTATEN, MASK_IRQ_ALL & ~IRQ_DINT & ~IRQ_DMAE);
- write4(sc, SDHC_IRQSIGEN, MASK_IRQ_ALL & ~IRQ_DINT & ~IRQ_DMAE);
-#else
- write4(sc, SDHC_IRQSTATEN, MASK_IRQ_ALL & ~IRQ_BRR & ~IRQ_BWR);
- write4(sc, SDHC_IRQSIGEN, MASK_IRQ_ALL & ~IRQ_BRR & ~IRQ_BWR);
-
- /* Write DMA address */
- write4(sc, SDHC_DSADDR, sc->dma_phys);
-
- /* Enable snooping and fix for AHB2MAG bypass. */
- write4(sc, SDHC_DCR, DCR_SNOOP | DCR_AHB2MAG_BYPASS);
-#endif
- /* Set data timeout. */
- set_bit(sc, SDHC_SYSCTL, 0xe << SHIFT_DTOCV);
-
- /* Set water-mark levels (FIFO buffer size). */
- write4(sc, SDHC_WML, (FSL_SDHC_FIFO_BUF_WORDS << 16) |
- FSL_SDHC_FIFO_BUF_WORDS);
-}
-
-static void
-init_mmc_host_struct(struct fsl_sdhc_softc *sc)
-{
- struct mmc_host *host = &sc->mmc_host;
-
- /* Clear host structure. */
- bzero(host, sizeof(struct mmc_host));
-
- /* Calculate minimum and maximum operating frequencies. */
- host->f_min = sc->platform_clock / FSL_SDHC_MAX_DIV;
- host->f_max = FSL_SDHC_MAX_CLOCK;
-
- /* Set operation conditions (voltage). */
- host->host_ocr = MMC_OCR_320_330 | MMC_OCR_330_340;
-
- /* Set additional host controller capabilities. */
- host->caps = MMC_CAP_4_BIT_DATA;
-
- /* Set mode. */
- host->mode = mode_sd;
-}
-
-static void
-card_detect_task(void *arg, int pending)
-{
- struct fsl_sdhc_softc *sc = (struct fsl_sdhc_softc *)arg;
- int err;
- int insert;
-
- insert = read4(sc, SDHC_PRSSTAT) & PRSSTAT_CINS;
-
- mtx_lock(&sc->mtx);
-
- if (insert) {
- if (sc->child != NULL) {
- mtx_unlock(&sc->mtx);
- return;
- }
-
- sc->child = device_add_child(sc->self, "mmc", -1);
- if (sc->child == NULL) {
- device_printf(sc->self, "Couldn't add MMC bus!\n");
- mtx_unlock(&sc->mtx);
- return;
- }
-
- /* Initialize MMC bus host structure. */
- init_mmc_host_struct(sc);
- device_set_ivars(sc->child, &sc->mmc_host);
-
- } else {
- if (sc->child == NULL) {
- mtx_unlock(&sc->mtx);
- return;
- }
- }
-
- mtx_unlock(&sc->mtx);
-
- if (insert) {
- if ((err = device_probe_and_attach(sc->child)) != 0) {
- device_printf(sc->self, "MMC bus failed on probe "
- "and attach! error %d\n", err);
- device_delete_child(sc->self, sc->child);
- sc->child = NULL;
- }
- } else {
- if (device_delete_child(sc->self, sc->child) != 0)
- device_printf(sc->self, "Could not delete MMC bus!\n");
- sc->child = NULL;
- }
-}
-
-static void
-card_detect_delay(void *arg)
-{
- struct fsl_sdhc_softc *sc = arg;
-
- taskqueue_enqueue(taskqueue_swi_giant, &sc->card_detect_task);
-}
-
-static void
-finalize_request(struct fsl_sdhc_softc *sc)
-{
-
- DPRINTF("finishing request %p\n", sc->request);
-
- sc->request->done(sc->request);
- sc->request = NULL;
-}
-
-/**
- * Read response from card.
- * @todo Implement Auto-CMD responses being held in R3 for multi-block xfers.
- * @param sc
- */
-static void
-get_response(struct fsl_sdhc_softc *sc)
-{
- struct mmc_command *cmd = sc->request->cmd;
- int i;
- uint32_t val;
- uint8_t ext = 0;
-
- if (cmd->flags & MMC_RSP_136) {
- /* CRC is stripped, need to shift one byte left. */
- for (i = 0; i < 4; i++) {
- val = read4(sc, SDHC_CMDRSP0 + i * 4);
- cmd->resp[3 - i] = (val << 8) + ext;
- ext = val >> 24;
- }
- } else {
- cmd->resp[0] = read4(sc, SDHC_CMDRSP0);
- }
-}
-
-#ifdef FSL_SDHC_NO_DMA
-/**
- * Read all content of a fifo buffer.
- * @warning Assumes data buffer is 32-bit aligned.
- * @param sc
- */
-static void
-read_block_pio(struct fsl_sdhc_softc *sc)
-{
- struct mmc_data *data = sc->request->cmd->data;
- size_t left = min(FSL_SDHC_FIFO_BUF_SIZE, data->len);
- uint8_t *buf = data->data;
- uint32_t word;
-
- buf += sc->data_offset;
- bus_space_read_multi_4(sc->bst, sc->bsh, SDHC_DATPORT, (uint32_t *)buf,
- left >> 2);
-
- sc->data_offset += left;
-
- /* Handle 32-bit unaligned size case. */
- left &= 0x3;
- if (left > 0) {
- buf = (uint8_t *)data->data + (sc->data_offset & ~0x3);
- word = read4(sc, SDHC_DATPORT);
- while (left > 0) {
- *(buf++) = word;
- word >>= 8;
- --left;
- }
- }
-}
-
-/**
- * Write a fifo buffer.
- * @warning Assumes data buffer size is 32-bit aligned.
- * @param sc
- */
-static void
-write_block_pio(struct fsl_sdhc_softc *sc)
-{
- struct mmc_data *data = sc->request->cmd->data;
- size_t left = min(FSL_SDHC_FIFO_BUF_SIZE, data->len);
- uint8_t *buf = data->data;
- uint32_t word = 0;
-
- DPRINTF("sc->data_offset %d\n", sc->data_offset);
-
- buf += sc->data_offset;
- bus_space_write_multi_4(sc->bst, sc->bsh, SDHC_DATPORT, (uint32_t *)buf,
- left >> 2);
-
- sc->data_offset += left;
-
- /* Handle 32-bit unaligned size case. */
- left &= 0x3;
- if (left > 0) {
- buf = (uint8_t *)data->data + (sc->data_offset & ~0x3);
- while (left > 0) {
- word += *(buf++);
- word <<= 8;
- --left;
- }
- write4(sc, SDHC_DATPORT, word);
- }
-}
-
-static void
-pio_read_transfer(struct fsl_sdhc_softc *sc)
-{
-
- while (read4(sc, SDHC_PRSSTAT) & PRSSTAT_BREN) {
- read_block_pio(sc);
-
- /*
- * TODO: should we check here whether data_offset >= data->len?
- */
- }
-}
-
-static void
-pio_write_transfer(struct fsl_sdhc_softc *sc)
-{
-
- while (read4(sc, SDHC_PRSSTAT) & PRSSTAT_BWEN) {
- write_block_pio(sc);
-
- /*
- * TODO: should we check here whether data_offset >= data->len?
- */
- }
-}
-#endif /* FSL_SDHC_USE_DMA */
-
-static inline void
-handle_command_intr(struct fsl_sdhc_softc *sc, uint32_t irq_stat)
-{
- struct mmc_command *cmd = sc->request->cmd;
-
- /* Handle errors. */
- if (irq_stat & IRQ_CTOE) {
- cmd->error = MMC_ERR_TIMEOUT;
- } else if (irq_stat & IRQ_CCE) {
- cmd->error = MMC_ERR_BADCRC;
- } else if (irq_stat & (IRQ_CEBE | IRQ_CIE)) {
- cmd->error = MMC_ERR_FIFO;
- }
-
- if (cmd->error) {
- device_printf(sc->self, "Error interrupt occured\n");
- reset_controller_dat_cmd(sc);
- return;
- }
-
- if (sc->command_done)
- return;
-
- if (irq_stat & IRQ_CC) {
- sc->command_done = 1;
-
- if (cmd->flags & MMC_RSP_PRESENT)
- get_response(sc);
- }
-}
-
-static inline void
-handle_data_intr(struct fsl_sdhc_softc *sc, uint32_t irq_stat)
-{
- struct mmc_command *cmd = sc->request->cmd;
-
- /* Handle errors. */
- if (irq_stat & IRQ_DTOE) {
- cmd->error = MMC_ERR_TIMEOUT;
- } else if (irq_stat & (IRQ_DCE | IRQ_DEBE)) {
- cmd->error = MMC_ERR_BADCRC;
- } else if (irq_stat & IRQ_ERROR_DATA_MASK) {
- cmd->error = MMC_ERR_FAILED;
- }
-
- if (cmd->error) {
- device_printf(sc->self, "Error interrupt occured\n");
- sc->data_done = 1;
- reset_controller_dat_cmd(sc);
- return;
- }
-
- if (sc->data_done)
- return;
-
-#ifdef FSL_SDHC_NO_DMA
- if (irq_stat & IRQ_BRR) {
- pio_read_transfer(sc);
- }
-
- if (irq_stat & IRQ_BWR) {
- pio_write_transfer(sc);
- }
-#else
- if (irq_stat & IRQ_DINT) {
- struct mmc_data *data = sc->request->cmd->data;
-
- /* Synchronize DMA. */
- if (data->flags & MMC_DATA_READ) {
- bus_dmamap_sync(sc->dma_tag, sc->dma_map,
- BUS_DMASYNC_POSTREAD);
- memcpy(data->data, sc->dma_mem, data->len);
- } else {
- bus_dmamap_sync(sc->dma_tag, sc->dma_map,
- BUS_DMASYNC_POSTWRITE);
- }
-
- /*
- * TODO: For multiple block transfers, address of dma memory
- * in DSADDR register should be set to the beginning of the
- * segment here. Also offset to data pointer should be handled.
- */
- }
-#endif
-
- if (irq_stat & IRQ_TC)
- sc->data_done = 1;
-}
-
-static void
-interrupt_handler(void *arg)
-{
- struct fsl_sdhc_softc *sc = (struct fsl_sdhc_softc *)arg;
- uint32_t irq_stat;
-
- mtx_lock(&sc->mtx);
-
- irq_stat = read4(sc, SDHC_IRQSTAT);
-
- /* Card interrupt. */
- if (irq_stat & IRQ_CINT) {
- DPRINTF("Card interrupt recievied\n");
-
- }
-
- /* Card insertion interrupt. */
- if (irq_stat & IRQ_CINS) {
- clear_bit(sc, SDHC_IRQSIGEN, IRQ_CINS);
- clear_bit(sc, SDHC_IRQSTATEN, IRQ_CINS);
- set_bit(sc, SDHC_IRQSIGEN, IRQ_CRM);
- set_bit(sc, SDHC_IRQSTATEN, IRQ_CRM);
-
- callout_reset(&sc->card_detect_callout, hz / 2,
- card_detect_delay, sc);
- }
-
- /* Card removal interrupt. */
- if (irq_stat & IRQ_CRM) {
- clear_bit(sc, SDHC_IRQSIGEN, IRQ_CRM);
- clear_bit(sc, SDHC_IRQSTATEN, IRQ_CRM);
- set_bit(sc, SDHC_IRQSIGEN, IRQ_CINS);
- set_bit(sc, SDHC_IRQSTATEN, IRQ_CINS);
-
- callout_stop(&sc->card_detect_callout);
- taskqueue_enqueue(taskqueue_swi_giant, &sc->card_detect_task);
- }
-
- /* Handle request interrupts. */
- if (sc->request) {
- handle_command_intr(sc, irq_stat);
- handle_data_intr(sc, irq_stat);
-
- /*
- * Finalize request when transfer is done successfully
- * or was interrupted due to error.
- */
- if ((sc->data_done && sc->command_done) ||
- (sc->request->cmd->error))
- finalize_request(sc);
- }
-
- /* Clear status register. */
- write4(sc, SDHC_IRQSTAT, irq_stat);
-
- mtx_unlock(&sc->mtx);
-}
-
-#ifndef FSL_SDHC_NO_DMA
-static void
-dma_get_phys_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
-{
-
- if (error != 0)
- return;
-
- /* Get first segment's physical address. */
- *(bus_addr_t *)arg = segs->ds_addr;
-}
-
-static int
-init_dma(struct fsl_sdhc_softc *sc)
-{
- device_t self = sc->self;
- int err;
-
- err = bus_dma_tag_create(bus_get_dma_tag(self),
- FSL_SDHC_DMA_BLOCK_SIZE, 0, BUS_SPACE_MAXADDR_32BIT,
- BUS_SPACE_MAXADDR, NULL, NULL, FSL_SDHC_DMA_BLOCK_SIZE, 1,
- FSL_SDHC_DMA_BLOCK_SIZE, BUS_DMA_ALLOCNOW, NULL, NULL,
- &sc->dma_tag);
-
- if (err) {
- device_printf(self, "Could not create DMA tag!\n");
- return (-1);
- }
-
- err = bus_dmamem_alloc(sc->dma_tag, (void **)&(sc->dma_mem),
- BUS_DMA_NOWAIT | BUS_DMA_NOCACHE, &sc->dma_map);
- if (err) {
- device_printf(self, "Could not allocate DMA memory!\n");
- goto fail1;
- }
-
- err = bus_dmamap_load(sc->dma_tag, sc->dma_map, (void *)sc->dma_mem,
- FSL_SDHC_DMA_BLOCK_SIZE, dma_get_phys_addr, &sc->dma_phys, 0);
- if (err) {
- device_printf(self, "Could not load DMA map!\n");
- goto fail2;
- }
-
- return (0);
-
-fail2:
- bus_dmamem_free(sc->dma_tag, sc->dma_mem, sc->dma_map);
-fail1:
- bus_dma_tag_destroy(sc->dma_tag);
-
- return (-1);
-}
-#endif /* FSL_SDHC_NO_DMA */
-
-static uint32_t
-set_xfertyp_register(const struct mmc_command *cmd)
-{
- uint32_t xfertyp = 0;
-
- /* Set command index. */
- xfertyp |= cmd->opcode << CMDINX_SHIFT;
-
- /* Set command type. */
- if (cmd->opcode == MMC_STOP_TRANSMISSION)
- xfertyp |= CMDTYP_ABORT;
-
- /* Set data preset select. */
- if (cmd->data) {
- xfertyp |= XFERTYP_DPSEL;
-
- /* Set transfer direction. */
- if (cmd->data->flags & MMC_DATA_READ)
- xfertyp |= XFERTYP_DTDSEL;
- }
-
- /* Set command index check. */
- if (cmd->flags & MMC_RSP_OPCODE)
- xfertyp |= XFERTYP_CICEN;
-
- /* Set command CRC check. */
- if (cmd->flags & MMC_RSP_CRC)
- xfertyp |= XFERTYP_CCCEN;
-
- /* Set response type */
- if (!(cmd->flags & MMC_RSP_PRESENT))
- xfertyp |= RSPTYP_NONE;
- else if (cmd->flags & MMC_RSP_136)
- xfertyp |= RSPTYP_136;
- else if (cmd->flags & MMC_RSP_BUSY)
- xfertyp |= RSPTYP_48_BUSY;
- else
- xfertyp |= RSPTYP_48;
-
-#ifndef FSL_SDHC_NO_DMA
- /* Enable DMA */
- xfertyp |= XFERTYP_DMAEN;
-#endif
-
- return (xfertyp);
-}
-
-static uint32_t
-set_blkattr_register(const struct mmc_data *data)
-{
-
- if (data->len <= FSL_SDHC_MAX_BLOCK_SIZE) {
- /* One block transfer. */
- return (BLKATTR_BLOCK_COUNT(1) | ((data->len) &
- BLKATTR_BLKSZE));
- }
-
- /* TODO: Write code here for multi-block transfers. */
- return (0);
-}
-
-/**
- * Initiate data transfer. Interrupt handler will finalize it.
- * @todo Implement multi-block transfers.
- * @param sc
- * @param cmd
- */
-static int
-start_data(struct fsl_sdhc_softc *sc, struct mmc_data *data)
-{
- uint32_t reg;
-
- if ((uint32_t)data->data & 0x3) {
- device_printf(sc->self, "32-bit unaligned data pointer in "
- "request\n");
- return (-1);
- }
-
- sc->data_done = 0;
-
-#ifdef FSL_SDHC_NO_DMA
- sc->data_ptr = data->data;
- sc->data_offset = 0;
-#else
- /* Write DMA address register. */
- write4(sc, SDHC_DSADDR, sc->dma_phys);
-
- /* Synchronize DMA. */
- if (data->flags & MMC_DATA_READ) {
- bus_dmamap_sync(sc->dma_tag, sc->dma_map,
- BUS_DMASYNC_PREREAD);
- } else {
- memcpy(sc->dma_mem, data->data, data->len);
- bus_dmamap_sync(sc->dma_tag, sc->dma_map,
- BUS_DMASYNC_PREWRITE);
- }
-#endif
- /* Set block size and count. */
- reg = set_blkattr_register(data);
- if (reg == 0) {
- device_printf(sc->self, "Requested unsupported multi-block "
- "transfer.\n");
- return (-1);
- }
- write4(sc, SDHC_BLKATTR, reg);
-
- return (0);
-}
-
-static int
-start_command(struct fsl_sdhc_softc *sc, struct mmc_command *cmd)
-{
- struct mmc_request *req = sc->request;
- uint32_t mask;
- uint32_t xfertyp;
- int err;
-
- DPRINTF("opcode %d, flags 0x%08x\n", cmd->opcode, cmd->flags);
- DPRINTF("PRSSTAT = 0x%08x\n", read4(sc, SDHC_PRSSTAT));
-
- sc->command_done = 0;
-
- cmd->error = MMC_ERR_NONE;
-
- /* TODO: should we check here for card presence and clock settings? */
-
- /* Always wait for free CMD line. */
- mask = SDHC_CMD_LINE;
- /* Wait for free DAT if we have data or busy signal. */
- if (cmd->data || (cmd->flags & MMC_RSP_BUSY))
- mask |= SDHC_DAT_LINE;
- /* We shouldn't wait for DAT for stop commands. */
- if (cmd == req->stop)
- mask &= ~SDHC_DAT_LINE;
- err = wait_for_free_line(sc, mask);
- if (err != 0) {
- device_printf(sc->self, "Controller never released inhibit "
- "bit(s).\n");
- reset_controller_dat_cmd(sc);
- cmd->error = MMC_ERR_FAILED;
- sc->request = NULL;
- req->done(req);
- return (-1);
- }
-
- xfertyp = set_xfertyp_register(cmd);
-
- if (cmd->data != NULL) {
- err = start_data(sc, cmd->data);
- if (err != 0) {
- device_printf(sc->self,
- "Data transfer request failed\n");
- reset_controller_dat_cmd(sc);
- cmd->error = MMC_ERR_FAILED;
- sc->request = NULL;
- req->done(req);
- return (-1);
- }
- }
-
- write4(sc, SDHC_CMDARG, cmd->arg);
- write4(sc, SDHC_XFERTYP, xfertyp);
-
- DPRINTF("XFERTYP = 0x%08x\n", xfertyp);
- DPRINTF("CMDARG = 0x%08x\n", cmd->arg);
-
- return (0);
-}
-
-#ifdef DEBUG
-static void
-dump_registers(struct fsl_sdhc_softc *sc)
-{
- printf("PRSSTAT = 0x%08x\n", read4(sc, SDHC_PRSSTAT));
- printf("PROCTL = 0x%08x\n", read4(sc, SDHC_PROCTL));
- printf("HOSTCAPBLT = 0x%08x\n", read4(sc, SDHC_HOSTCAPBLT));
- printf("IRQSTAT = 0x%08x\n", read4(sc, SDHC_IRQSTAT));
- printf("IRQSTATEN = 0x%08x\n", read4(sc, SDHC_IRQSTATEN));
- printf("IRQSIGEN = 0x%08x\n", read4(sc, SDHC_IRQSIGEN));
- printf("WML = 0x%08x\n", read4(sc, SDHC_WML));
- printf("DSADDR = 0x%08x\n", read4(sc, SDHC_DSADDR));
- printf("XFERTYP = 0x%08x\n", read4(sc, SDHC_XFERTYP));
- printf("DCR = 0x%08x\n", read4(sc, SDHC_DCR));
-}
-#endif
-
-/*****************************************************************************
- * Public methods
- *****************************************************************************/
-/*
- * Device interface methods.
- */
-static int
-fsl_sdhc_probe(device_t self)
-{
- static const char *desc =
- "Freescale Enhanced Secure Digital Host Controller";
-
- if (!ofw_bus_is_compatible(self, "fsl,p2020-esdhc") &&
- !ofw_bus_is_compatible(self, "fsl,esdhc"))
- return (ENXIO);
-
- device_set_desc(self, desc);
-
- return (BUS_PROBE_VENDOR);
-}
-
-static int
-fsl_sdhc_attach(device_t self)
-{
- struct fsl_sdhc_softc *sc;
-
- sc = device_get_softc(self);
-
- sc->self = self;
-
- mtx_init(&sc->mtx, device_get_nameunit(self), NULL, MTX_DEF);
-
- /* Setup memory resource */
- sc->mem_rid = 0;
- sc->mem_resource = bus_alloc_resource_any(self, SYS_RES_MEMORY,
- &sc->mem_rid, RF_ACTIVE);
- if (sc->mem_resource == NULL) {
- device_printf(self, "Could not allocate memory.\n");
- goto fail;
- }
- sc->bst = rman_get_bustag(sc->mem_resource);
- sc->bsh = rman_get_bushandle(sc->mem_resource);
-
- /* Setup interrupt resource. */
- sc->irq_rid = 0;
- sc->irq_resource = bus_alloc_resource_any(self, SYS_RES_IRQ,
- &sc->irq_rid, RF_ACTIVE);
- if (sc->irq_resource == NULL) {
- device_printf(self, "Could not allocate interrupt.\n");
- goto fail;
- }
- if (bus_setup_intr(self, sc->irq_resource, INTR_TYPE_MISC |
- INTR_MPSAFE, NULL, interrupt_handler, sc, &sc->ihl) != 0) {
- device_printf(self, "Could not setup interrupt.\n");
- goto fail;
- }
-
- /* Setup DMA. */
-#ifndef FSL_SDHC_NO_DMA
- if (init_dma(sc) != 0) {
- device_printf(self, "Could not setup DMA\n");
- }
-#endif
- sc->bus_busy = 0;
- sc->platform_clock = get_platform_clock(sc);
- if (sc->platform_clock == 0) {
- device_printf(self, "Could not get platform clock.\n");
- goto fail;
- }
- sc->command_done = 1;
- sc->data_done = 1;
-
- /* Init card detection task. */
- TASK_INIT(&sc->card_detect_task, 0, card_detect_task, sc);
- callout_init(&sc->card_detect_callout, 1);
-
- reset_controller_all(sc);
- init_controller(sc);
- set_clock(sc, 400000);
- send_80_clock_ticks(sc);
-
-#ifdef DEBUG
- dump_registers(sc);
-#endif
-
- return (0);
-
-fail:
- fsl_sdhc_detach(self);
- return (ENXIO);
-}
-
-static int
-fsl_sdhc_detach(device_t self)
-{
- struct fsl_sdhc_softc *sc = device_get_softc(self);
- int err;
-
- if (sc->child)
- device_delete_child(self, sc->child);
-
- taskqueue_drain(taskqueue_swi_giant, &sc->card_detect_task);
-
-#ifndef FSL_SDHC_NO_DMA
- bus_dmamap_unload(sc->dma_tag, sc->dma_map);
- bus_dmamem_free(sc->dma_tag, sc->dma_mem, sc->dma_map);
- bus_dma_tag_destroy(sc->dma_tag);
-#endif
-
- if (sc->ihl != NULL) {
- err = bus_teardown_intr(self, sc->irq_resource, sc->ihl);
- if (err)
- return (err);
- }
- if (sc->irq_resource != NULL) {
- err = bus_release_resource(self, SYS_RES_IRQ, sc->irq_rid,
- sc->irq_resource);
- if (err)
- return (err);
-
- }
- if (sc->mem_resource != NULL) {
- err = bus_release_resource(self, SYS_RES_MEMORY, sc->mem_rid,
- sc->mem_resource);
- if (err)
- return (err);
- }
-
- mtx_destroy(&sc->mtx);
-
- return (0);
-}
-
-
-/*
- * Bus interface methods.
- */
-static int
-fsl_sdhc_read_ivar(device_t self, device_t child, int index,
- uintptr_t *result)
-{
- struct mmc_host *host = device_get_ivars(child);
-
- switch (index) {
- case MMCBR_IVAR_BUS_MODE:
- *(int *)result = host->ios.bus_mode;
- break;
- case MMCBR_IVAR_BUS_WIDTH:
- *(int *)result = host->ios.bus_width;
- break;
- case MMCBR_IVAR_CHIP_SELECT:
- *(int *)result = host->ios.chip_select;
- break;
- case MMCBR_IVAR_CLOCK:
- *(int *)result = host->ios.clock;
- break;
- case MMCBR_IVAR_F_MIN:
- *(int *)result = host->f_min;
- break;
- case MMCBR_IVAR_F_MAX:
- *(int *)result = host->f_max;
- break;
- case MMCBR_IVAR_HOST_OCR:
- *(int *)result = host->host_ocr;
- break;
- case MMCBR_IVAR_MODE:
- *(int *)result = host->mode;
- break;
- case MMCBR_IVAR_OCR:
- *(int *)result = host->ocr;
- break;
- case MMCBR_IVAR_POWER_MODE:
- *(int *)result = host->ios.power_mode;
- break;
- case MMCBR_IVAR_VDD:
- *(int *)result = host->ios.vdd;
- break;
- default:
- return (EINVAL);
- }
-
- return (0);
-}
-
-static int
-fsl_sdhc_write_ivar(device_t self, device_t child, int index,
- uintptr_t value)
-{
- struct mmc_host *host = device_get_ivars(child);
-
- switch (index) {
- case MMCBR_IVAR_BUS_MODE:
- host->ios.bus_mode = value;
- break;
- case MMCBR_IVAR_BUS_WIDTH:
- host->ios.bus_width = value;
- break;
- case MMCBR_IVAR_CHIP_SELECT:
- host->ios.chip_select = value;
- break;
- case MMCBR_IVAR_CLOCK:
- host->ios.clock = value;
- break;
- case MMCBR_IVAR_MODE:
- host->mode = value;
- break;
- case MMCBR_IVAR_OCR:
- host->ocr = value;
- break;
- case MMCBR_IVAR_POWER_MODE:
- host->ios.power_mode = value;
- break;
- case MMCBR_IVAR_VDD:
- host->ios.vdd = value;
- break;
- case MMCBR_IVAR_HOST_OCR:
- case MMCBR_IVAR_F_MIN:
- case MMCBR_IVAR_F_MAX:
- default:
- /* Instance variable not writable. */
- return (EINVAL);
- }
-
- return (0);
-}
-
-
-/*
- * MMC bridge methods.
- */
-static int
-fsl_sdhc_update_ios(device_t self, device_t reqdev)
-{
- struct fsl_sdhc_softc *sc = device_get_softc(self);
- struct mmc_host *host = device_get_ivars(reqdev);
- struct mmc_ios *ios = &host->ios;
-
- mtx_lock(&sc->mtx);
-
- /* Full reset on bus power down to clear from any state. */
- if (ios->power_mode == power_off) {
- reset_controller_all(sc);
- init_controller(sc);
- }
-
- set_clock(sc, ios->clock);
- set_bus_width(sc, ios->bus_width);
-
- mtx_unlock(&sc->mtx);
-
- return (0);
-}
-
-static int
-fsl_sdhc_request(device_t self, device_t reqdev, struct mmc_request *req)
-{
- struct fsl_sdhc_softc *sc = device_get_softc(self);
- int err;
-
- mtx_lock(&sc->mtx);
-
- sc->request = req;
- err = start_command(sc, req->cmd);
-
- mtx_unlock(&sc->mtx);
-
- return (err);
-}
-
-static int
-fsl_sdhc_get_ro(device_t self, device_t reqdev)
-{
- struct fsl_sdhc_softc *sc = device_get_softc(self);
-
- /* Wouldn't it be faster using branching (if {}) ?? */
- return (((read4(sc, SDHC_PRSSTAT) & PRSSTAT_WPSPL) >> 19) ^ 0x1);
-}
-
-static int
-fsl_sdhc_acquire_host(device_t self, device_t reqdev)
-{
- struct fsl_sdhc_softc *sc = device_get_softc(self);
- int retval = 0;
-
- mtx_lock(&sc->mtx);
-
- while (sc->bus_busy)
- retval = mtx_sleep(sc, &sc->mtx, PZERO, "sdhcah", 0);
- ++(sc->bus_busy);
-
- mtx_unlock(&sc->mtx);
-
- return (retval);
-}
-
-static int
-fsl_sdhc_release_host(device_t self, device_t reqdev)
-{
- struct fsl_sdhc_softc *sc = device_get_softc(self);
-
- mtx_lock(&sc->mtx);
- --(sc->bus_busy);
- mtx_unlock(&sc->mtx);
- wakeup(sc);
-
- return (0);
-}

File Metadata

Mime Type
text/plain
Expires
Wed, Jun 17, 9:26 AM (21 h, 51 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34018790
Default Alt Text
D8407.id21866.diff (101 KB)

Event Timeline