Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F106029497
D8407.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
46 KB
Referenced Files
None
Subscribers
None
D8407.diff
View Options
Index: head/sys/conf/files.powerpc
===================================================================
--- head/sys/conf/files.powerpc
+++ head/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_sdhci.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: head/sys/dev/sdhci/fsl_sdhci.c
===================================================================
--- head/sys/dev/sdhci/fsl_sdhci.c
+++ head/sys/dev/sdhci/fsl_sdhci.c
@@ -28,7 +28,7 @@
__FBSDID("$FreeBSD$");
/*
- * SDHCI driver glue for Freescale i.MX SoC family.
+ * SDHCI driver glue for Freescale i.MX SoC and QorIQ families.
*
* This supports both eSDHC (earlier SoCs) and uSDHC (more recent SoCs).
*/
@@ -38,6 +38,7 @@
#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>
@@ -52,9 +53,11 @@
#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>
@@ -146,7 +149,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
@@ -169,6 +171,7 @@
{"fsl,imx6sl-usdhc", HWTYPE_USDHC},
{"fsl,imx53-esdhc", HWTYPE_ESDHC},
{"fsl,imx51-esdhc", HWTYPE_ESDHC},
+ {"fsl,esdhc", HWTYPE_ESDHC},
{NULL, HWTYPE_NONE},
};
@@ -397,6 +400,11 @@
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);
@@ -531,17 +539,20 @@
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
+ * 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 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.
+ * 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;
@@ -565,15 +576,18 @@
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.
+ * 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;
}
@@ -625,6 +639,7 @@
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);
}
@@ -710,10 +725,10 @@
*/
switch (sc->r1bfix_type) {
case R1BFIX_NODATA:
- intmask = RD4(sc, SDHC_INT_STATUS) & SDHCI_INT_RESPONSE;
+ intmask = RD4(sc, SDHCI_INT_STATUS) & SDHCI_INT_RESPONSE;
break;
case R1BFIX_AC12:
- intmask = RD4(sc, SDHC_INT_STATUS) & SDHCI_INT_DATA_END;
+ intmask = RD4(sc, SDHCI_INT_STATUS) & SDHCI_INT_DATA_END;
break;
default:
intmask = 0;
@@ -722,8 +737,8 @@
if (intmask) {
sc->r1bfix_timeout_at = getsbinuptime() + 250 * SBT_1MS;
if (!fsl_sdhci_r1bfix_is_wait_done(sc)) {
- WR4(sc, SDHC_INT_STATUS, intmask);
- bus_barrier(sc->mem_res, SDHC_INT_STATUS, 4,
+ WR4(sc, SDHCI_INT_STATUS, intmask);
+ bus_barrier(sc->mem_res, SDHCI_INT_STATUS, 4,
BUS_SPACE_BARRIER_WRITE);
}
}
@@ -735,9 +750,53 @@
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);
- return (false);
+ /* 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)
@@ -752,6 +811,7 @@
struct fsl_sdhci_softc *sc = device_get_softc(dev);
int rid, err;
phandle_t node;
+ uint32_t protctl;
sc->dev = dev;
@@ -807,9 +867,23 @@
*
* XXX need named constants for this stuff.
*/
- WR4(sc, SDHC_WTMK_LVL, 0x08800880);
+#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;
/*
@@ -830,6 +904,16 @@
/* 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);
Index: head/sys/powerpc/mpc85xx/fsl_sdhc.h
===================================================================
--- head/sys/powerpc/mpc85xx/fsl_sdhc.h
+++ head/sys/powerpc/mpc85xx/fsl_sdhc.h
@@ -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: head/sys/powerpc/mpc85xx/fsl_sdhc.c
===================================================================
--- head/sys/powerpc/mpc85xx/fsl_sdhc.c
+++ head/sys/powerpc/mpc85xx/fsl_sdhc.c
@@ -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
Details
Attached
Mime Type
text/plain
Expires
Wed, Dec 25, 3:58 AM (8 h, 53 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15594084
Default Alt Text
D8407.diff (46 KB)
Attached To
Mode
D8407: Merge i.MX and PowerPC SDHCI drivers
Attached
Detach File
Event Timeline
Log In to Comment