summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorimp <imp@FreeBSD.org>2006-10-20 06:44:04 +0000
committerimp <imp@FreeBSD.org>2006-10-20 06:44:04 +0000
commit3434ef1d273ef1358e9f6b56ebf004aa09612a95 (patch)
tree9439fcc2d2fc1db3ce647fa4d097dc1436a425f4 /sys
parent30743982cadbd960ddcea071a6305c7b9a367999 (diff)
downloadFreeBSD-src-3434ef1d273ef1358e9f6b56ebf004aa09612a95.zip
FreeBSD-src-3434ef1d273ef1358e9f6b56ebf004aa09612a95.tar.gz
MMC/SD bridge driver (host adapter) for AT91RM9200's MCI interface.
This interface also appears in the AT91SAM9260 and '61 as well as the AVR32 based micros from Atmel. We don't yet support write protect or hot-swap in this bridge driver.
Diffstat (limited to 'sys')
-rw-r--r--sys/arm/at91/at91_mci.c725
-rw-r--r--sys/arm/at91/at91_mcireg.h128
2 files changed, 853 insertions, 0 deletions
diff --git a/sys/arm/at91/at91_mci.c b/sys/arm/at91/at91_mci.c
new file mode 100644
index 0000000..56f3383
--- /dev/null
+++ b/sys/arm/at91/at91_mci.c
@@ -0,0 +1,725 @@
+/*-
+ * Copyright (c) 2006 Bernd Walter. All rights reserved.
+ * Copyright (c) 2006 M. Warner Losh. 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 ``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 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$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bio.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/time.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/cpufunc.h>
+#include <machine/resource.h>
+#include <machine/frame.h>
+#include <machine/intr.h>
+#include <arm/at91/at91rm92reg.h>
+#include <arm/at91/at91var.h>
+#include <arm/at91/at91_mcireg.h>
+#include <arm/at91/at91_pdcreg.h>
+#include <dev/mmc/bridge.h>
+#include <dev/mmc/mmcreg.h>
+#include <dev/mmc/mmcbrvar.h>
+
+#include "mmcbr_if.h"
+
+#define BBSZ 512
+
+struct at91_mci_softc {
+ void *intrhand; /* Interrupt handle */
+ device_t dev;
+ int flags;
+#define CMD_STARTED 1
+#define STOP_STARTED 2
+ struct resource *irq_res; /* IRQ resource */
+ struct resource *mem_res; /* Memory resource */
+ struct mtx sc_mtx;
+ bus_dma_tag_t dmatag;
+ bus_dmamap_t map;
+ int mapped;
+ struct mmc_host host;
+ int wire4;
+ int bus_busy;
+ struct mmc_request *req;
+ struct mmc_command *curcmd;
+ char bounce_buffer[BBSZ];
+};
+
+static inline uint32_t
+RD4(struct at91_mci_softc *sc, bus_size_t off)
+{
+ return bus_read_4(sc->mem_res, off);
+}
+
+static inline void
+WR4(struct at91_mci_softc *sc, bus_size_t off, uint32_t val)
+{
+ bus_write_4(sc->mem_res, off, val);
+}
+
+/* bus entry points */
+static int at91_mci_probe(device_t dev);
+static int at91_mci_attach(device_t dev);
+static int at91_mci_detach(device_t dev);
+static void at91_mci_intr(void *);
+
+/* helper routines */
+static int at91_mci_activate(device_t dev);
+static void at91_mci_deactivate(device_t dev);
+
+#define AT91_MCI_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define AT91_MCI_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define AT91_MCI_LOCK_INIT(_sc) \
+ mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \
+ "mci", MTX_DEF)
+#define AT91_MCI_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx);
+#define AT91_MCI_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED);
+#define AT91_MCI_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
+
+static void
+at91_mci_pdc_disable(struct at91_mci_softc *sc)
+{
+ WR4(sc, PDC_PTCR, PDC_PTCR_TXTDIS | PDC_PTCR_RXTDIS);
+ WR4(sc, PDC_RPR, 0);
+ WR4(sc, PDC_RCR, 0);
+ WR4(sc, PDC_RNPR, 0);
+ WR4(sc, PDC_RNCR, 0);
+ WR4(sc, PDC_TPR, 0);
+ WR4(sc, PDC_TCR, 0);
+ WR4(sc, PDC_TNPR, 0);
+ WR4(sc, PDC_TNCR, 0);
+}
+
+static void
+at91_mci_init(device_t dev)
+{
+ struct at91_mci_softc *sc = device_get_softc(dev);
+
+ WR4(sc, MCI_CR, MCI_CR_MCIEN); /* Enable controller */
+ WR4(sc, MCI_IDR, 0xffffffff); /* Turn off interrupts */
+ WR4(sc, MCI_DTOR, MCI_DTOR_DTOMUL_1M | 1);
+ WR4(sc, MCI_MR, 0x834a); // XXX GROSS HACK FROM LINUX
+ WR4(sc, MCI_SDCR, 0); /* SLOT A, 1 bit bus */
+}
+
+static void
+at91_mci_fini(device_t dev)
+{
+ struct at91_mci_softc *sc = device_get_softc(dev);
+
+ WR4(sc, MCI_IDR, 0xffffffff); /* Turn off interrupts */
+ at91_mci_pdc_disable(sc);
+ WR4(sc, MCI_CR, MCI_CR_MCIDIS | MCI_CR_SWRST); /* Put the device into reset */
+}
+
+static int
+at91_mci_probe(device_t dev)
+{
+
+ device_set_desc(dev, "MCI mmc/sd host bridge");
+ return (0);
+}
+
+static int
+at91_mci_attach(device_t dev)
+{
+ struct at91_mci_softc *sc = device_get_softc(dev);
+ int err;
+ device_t child;
+
+ sc->dev = dev;
+ err = at91_mci_activate(dev);
+ if (err)
+ goto out;
+
+ AT91_MCI_LOCK_INIT(sc);
+
+ /*
+ * Allocate DMA tags and maps
+ */
+ err = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT,
+ BUS_SPACE_MAXADDR, NULL, NULL, MAXPHYS, 1, MAXPHYS,
+ BUS_DMA_ALLOCNOW, NULL, NULL, &sc->dmatag);
+ if (err != 0)
+ goto out;
+
+ err = bus_dmamap_create(sc->dmatag, 0, &sc->map);
+ if (err != 0)
+ goto out;
+
+ at91_mci_fini(dev);
+ at91_mci_init(dev);
+
+ /*
+ * Activate the interrupt
+ */
+ err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ at91_mci_intr, sc, &sc->intrhand);
+ if (err) {
+ AT91_MCI_LOCK_DESTROY(sc);
+ goto out;
+ }
+ sc->host.f_min = 375000;
+ sc->host.f_max = 30000000;
+ sc->host.host_ocr = MMC_OCR_320_330 | MMC_OCR_330_340;
+ sc->host.caps = MMC_CAP_4_BIT_DATA;
+ child = device_add_child(dev, "mmc", 0);
+ device_set_ivars(dev, &sc->host);
+ err = bus_generic_attach(dev);
+out:;
+ if (err)
+ at91_mci_deactivate(dev);
+ return (err);
+}
+
+static int
+at91_mci_detach(device_t dev)
+{
+ at91_mci_fini(dev);
+ at91_mci_deactivate(dev);
+ return (EBUSY); /* XXX */
+}
+
+static int
+at91_mci_activate(device_t dev)
+{
+ struct at91_mci_softc *sc;
+ int rid;
+
+ sc = device_get_softc(dev);
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL)
+ goto errout;
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (sc->irq_res == NULL)
+ goto errout;
+ return (0);
+errout:
+ at91_mci_deactivate(dev);
+ return (ENOMEM);
+}
+
+static void
+at91_mci_deactivate(device_t dev)
+{
+ struct at91_mci_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (sc->intrhand)
+ bus_teardown_intr(dev, sc->irq_res, sc->intrhand);
+ sc->intrhand = 0;
+ bus_generic_detach(sc->dev);
+ if (sc->mem_res)
+ bus_release_resource(dev, SYS_RES_IOPORT,
+ rman_get_rid(sc->mem_res), sc->mem_res);
+ sc->mem_res = 0;
+ if (sc->irq_res)
+ bus_release_resource(dev, SYS_RES_IRQ,
+ rman_get_rid(sc->irq_res), sc->irq_res);
+ sc->irq_res = 0;
+ return;
+}
+
+static void
+at91_mci_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
+{
+ if (error != 0)
+ return;
+ *(bus_addr_t *)arg = segs[0].ds_addr;
+}
+
+static int
+at91_mci_update_ios(device_t brdev, device_t reqdev)
+{
+ uint32_t at91_master_clock = AT91C_MASTER_CLOCK;
+ struct at91_mci_softc *sc;
+ struct mmc_host *host;
+ struct mmc_ios *ios;
+ uint32_t clkdiv;
+
+ sc = device_get_softc(brdev);
+ host = &sc->host;
+ ios = &host->ios;
+ // bus mode?
+ if (ios->clock == 0) {
+ WR4(sc, MCI_CR, MCI_CR_MCIDIS);
+ clkdiv = 0;
+ } else {
+ WR4(sc, MCI_CR, MCI_CR_MCIEN);
+ if ((at91_master_clock % (ios->clock * 2)) == 0)
+ clkdiv = ((at91_master_clock / ios->clock) / 2) - 1;
+ else
+ clkdiv = (at91_master_clock / ios->clock) / 2;
+ }
+ if (ios->bus_width == bus_width_4 && sc->wire4) {
+ device_printf(sc->dev, "Setting controller bus width to 4\n");
+ WR4(sc, MCI_SDCR, RD4(sc, MCI_SDCR) | MCI_SDCR_SDCBUS);
+ } else {
+ device_printf(sc->dev, "Setting controller bus width to 1\n");
+ WR4(sc, MCI_SDCR, RD4(sc, MCI_SDCR) & ~MCI_SDCR_SDCBUS);
+ }
+ WR4(sc, MCI_MR, (RD4(sc, MCI_MR) & ~MCI_MR_CLKDIV) | clkdiv);
+#if 0
+ if (sc->vcc_pin) {
+ if (sc->power_mode == MMC_POWER_OFF)
+ gpio_set(sc->vcc_pin, 0);
+ else
+ gpio_set(sc->vcc_pin, 1);
+ }
+#endif
+ return (0);
+}
+
+static void
+at91_mci_start_cmd(struct at91_mci_softc *sc, struct mmc_command *cmd)
+{
+ uint32_t cmdr, ier = 0, mr;
+ uint32_t *src, *dst;
+ int i;
+ struct mmc_data *data;
+ struct mmc_request *req;
+ size_t block_size = 1 << 9; // Fixed, per mmc/sd spec for 2GB cards
+ void *vaddr;
+ bus_addr_t paddr;
+
+ sc->curcmd = cmd;
+ data = cmd->data;
+ cmdr = cmd->opcode;
+ req = cmd->mrq;
+ if (MMC_RSP(cmd->flags) == MMC_RSP_NONE)
+ cmdr |= MCI_CMDR_RSPTYP_NO;
+ else {
+ /* Allow big timeout for responses */
+ cmdr |= MCI_CMDR_MAXLAT;
+ if (cmd->flags & MMC_RSP_136)
+ cmdr |= MCI_CMDR_RSPTYP_136;
+ else
+ cmdr |= MCI_CMDR_RSPTYP_48;
+ }
+ if (cmd->opcode == MMC_STOP_TRANSMISSION)
+ cmdr |= MCI_CMDR_TRCMD_STOP;
+ if (sc->host.ios.bus_mode == opendrain)
+ cmdr |= MCI_CMDR_OPDCMD;
+ if (!data) {
+ // The no data case is fairly simple
+ at91_mci_pdc_disable(sc);
+// printf("CMDR %x ARGR %x\n", cmdr, cmd->arg);
+ WR4(sc, MCI_ARGR, cmd->arg);
+ WR4(sc, MCI_CMDR, cmdr);
+ WR4(sc, MCI_IER, MCI_SR_ERROR | MCI_SR_CMDRDY);
+ return;
+ }
+ if (data->flags & MMC_DATA_READ)
+ cmdr |= MCI_CMDR_TRDIR;
+ if (data->flags & (MMC_DATA_READ | MMC_DATA_WRITE))
+ cmdr |= MCI_CMDR_TRCMD_START;
+ if (data->flags & MMC_DATA_STREAM)
+ cmdr |= MCI_CMDR_TRTYP_STREAM;
+ if (data->flags & MMC_DATA_MULTI)
+ cmdr |= MCI_CMDR_TRTYP_MULTIPLE;
+ // Set block size and turn on PDC mode for dma xfer and disable
+ // PDC until we're ready.
+ mr = RD4(sc, MCI_MR) & ~MCI_MR_BLKLEN;
+ WR4(sc, MCI_MR, mr | (block_size << 16) | MCI_MR_PDCMODE);
+ WR4(sc, PDC_PTCR, PDC_PTCR_RXTDIS | PDC_PTCR_TXTDIS);
+ if (cmdr & MCI_CMDR_TRCMD_START) {
+ if (cmdr & MCI_CMDR_TRDIR)
+ vaddr = cmd->data->data;
+ else {
+ if (data->len != BBSZ)
+ panic("Write multiblock write support");
+ vaddr = sc->bounce_buffer;
+ src = (uint32_t *)cmd->data->data;
+ dst = (uint32_t *)vaddr;
+ for (i = 0; i < data->len / 4; i++)
+ dst[i] = bswap32(src[i]);
+ }
+ data->xfer_len = 0;
+ if (bus_dmamap_load(sc->dmatag, sc->map, vaddr, data->len,
+ at91_mci_getaddr, &paddr, 0) != 0) {
+ if (req->cmd->flags & STOP_STARTED)
+ req->stop->error = MMC_ERR_NO_MEMORY;
+ else
+ req->cmd->error = MMC_ERR_NO_MEMORY;
+ sc->req = NULL;
+ sc->curcmd = NULL;
+ req->done(req);
+ return;
+ }
+ sc->mapped++;
+ if (cmdr & MCI_CMDR_TRDIR) {
+ bus_dmamap_sync(sc->dmatag, sc->map, BUS_DMASYNC_PREREAD);
+ WR4(sc, PDC_RPR, paddr);
+ WR4(sc, PDC_RCR, data->len / 4);
+ ier = MCI_SR_ENDRX;
+ } else {
+ bus_dmamap_sync(sc->dmatag, sc->map, BUS_DMASYNC_PREWRITE);
+ WR4(sc, PDC_TPR, paddr);
+ WR4(sc, PDC_TCR, data->len / 4);
+ ier = MCI_SR_TXBUFE;
+ }
+ }
+// printf("CMDR %x ARGR %x with data\n", cmdr, cmd->arg);
+ WR4(sc, MCI_ARGR, cmd->arg);
+ WR4(sc, MCI_CMDR, cmdr);
+ if (cmdr & MCI_CMDR_TRCMD_START) {
+ if (cmdr & MCI_CMDR_TRDIR)
+ WR4(sc, PDC_PTCR, PDC_PTCR_RXTEN);
+ else
+ WR4(sc, PDC_PTCR, PDC_PTCR_TXTEN);
+ }
+ WR4(sc, MCI_IER, MCI_SR_ERROR | ier);
+}
+
+static void
+at91_mci_start(struct at91_mci_softc *sc)
+{
+ struct mmc_request *req;
+
+ req = sc->req;
+ if (req == NULL)
+ return;
+ // assert locked
+ if (!(sc->flags & CMD_STARTED)) {
+ sc->flags |= CMD_STARTED;
+// printf("Starting CMD\n");
+ at91_mci_start_cmd(sc, req->cmd);
+ return;
+ }
+ if (!(sc->flags & STOP_STARTED) && req->stop) {
+// printf("Starting Stop\n");
+ sc->flags |= STOP_STARTED;
+ at91_mci_start_cmd(sc, req->stop);
+ return;
+ }
+ /* We must be done -- bad idea to do this while locked? */
+ sc->req = NULL;
+ sc->curcmd = NULL;
+ req->done(req);
+}
+
+static int
+at91_mci_request(device_t brdev, device_t reqdev, struct mmc_request *req)
+{
+ struct at91_mci_softc *sc = device_get_softc(brdev);
+
+ AT91_MCI_LOCK(sc);
+ // XXX do we want to be able to queue up multiple commands?
+ // XXX sounds like a good idea, but all protocols are sync, so
+ // XXX maybe the idea is naive...
+ if (sc->req != NULL) {
+ AT91_MCI_UNLOCK(sc);
+ return EBUSY;
+ }
+ sc->req = req;
+ sc->flags = 0;
+ at91_mci_start(sc);
+ AT91_MCI_UNLOCK(sc);
+ return (0);
+}
+
+static int
+at91_mci_get_ro(device_t brdev, device_t reqdev)
+{
+ return (-1);
+}
+
+static int
+at91_mci_acquire_host(device_t brdev, device_t reqdev)
+{
+ struct at91_mci_softc *sc = device_get_softc(brdev);
+ int err = 0;
+
+ AT91_MCI_LOCK(sc);
+ while (sc->bus_busy)
+ msleep(sc, &sc->sc_mtx, PZERO, "mciah", hz / 5);
+ sc->bus_busy++;
+ AT91_MCI_UNLOCK(sc);
+ return (err);
+}
+
+static int
+at91_mci_release_host(device_t brdev, device_t reqdev)
+{
+ struct at91_mci_softc *sc = device_get_softc(brdev);
+
+ AT91_MCI_LOCK(sc);
+ sc->bus_busy--;
+ wakeup(sc);
+ AT91_MCI_UNLOCK(sc);
+ return (0);
+}
+
+static void
+at91_mci_read_done(struct at91_mci_softc *sc)
+{
+ uint32_t *walker;
+ struct mmc_command *cmd;
+ int i, len;
+
+ cmd = sc->curcmd;
+ bus_dmamap_sync(sc->dmatag, sc->map, BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(sc->dmatag, sc->map);
+ sc->mapped--;
+ walker = (uint32_t *)cmd->data->data;
+ len = cmd->data->len / 4;
+ for (i = 0; i < len; i++)
+ walker[i] = bswap32(walker[i]);
+ // Finish up the sequence...
+ WR4(sc, MCI_IDR, MCI_SR_ENDRX);
+ WR4(sc, MCI_IER, MCI_SR_RXBUFF);
+ WR4(sc, PDC_PTCR, PDC_PTCR_RXTDIS | PDC_PTCR_TXTDIS);
+}
+
+static void
+at91_mci_xmit_done(struct at91_mci_softc *sc)
+{
+ // Finish up the sequence...
+ WR4(sc, PDC_PTCR, PDC_PTCR_RXTDIS | PDC_PTCR_TXTDIS);
+ WR4(sc, MCI_IDR, MCI_SR_TXBUFE);
+ WR4(sc, MCI_IER, MCI_SR_NOTBUSY);
+ bus_dmamap_sync(sc->dmatag, sc->map, BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->dmatag, sc->map);
+ sc->mapped--;
+}
+
+static void
+at91_mci_intr(void *arg)
+{
+ struct at91_mci_softc *sc = (struct at91_mci_softc*)arg;
+ uint32_t sr;
+ int i, done = 0;
+ struct mmc_command *cmd;
+
+ AT91_MCI_LOCK(sc);
+ sr = RD4(sc, MCI_SR) & RD4(sc, MCI_IMR);
+// printf("i 0x%x\n", sr);
+ cmd = sc->curcmd;
+ if (sr & MCI_SR_ERROR) {
+ // Ignore CRC errors on CMD2 and ACMD47, per relevant standards
+ if ((sr & MCI_SR_RCRCE) && (cmd->opcode == MMC_SEND_OP_COND ||
+ cmd->opcode == ACMD_SD_SEND_OP_COND))
+ cmd->error = MMC_ERR_NONE;
+ else if (sr & (MCI_SR_RTOE | MCI_SR_DTOE))
+ cmd->error = MMC_ERR_TIMEOUT;
+ else if (sr & (MCI_SR_RCRCE | MCI_SR_DCRCE))
+ cmd->error = MMC_ERR_BADCRC;
+ else if (sr & (MCI_SR_OVRE | MCI_SR_UNRE))
+ cmd->error = MMC_ERR_FIFO;
+ else
+ cmd->error = MMC_ERR_FAILED;
+ done = 1;
+ if (sc->mapped && cmd->error) {
+ bus_dmamap_unload(sc->dmatag, sc->map);
+ sc->mapped--;
+ }
+ } else {
+ if (sr & MCI_SR_TXBUFE) {
+// printf("TXBUFE\n");
+ at91_mci_xmit_done(sc);
+ }
+ if (sr & MCI_SR_RXBUFF) {
+// printf("RXBUFF\n");
+ WR4(sc, MCI_IDR, MCI_SR_RXBUFF);
+ WR4(sc, MCI_IER, MCI_SR_CMDRDY);
+ }
+ if (sr & MCI_SR_ENDTX) {
+// printf("ENDTX\n");
+ }
+ if (sr & MCI_SR_ENDRX) {
+// printf("ENDRX\n");
+ at91_mci_read_done(sc);
+ }
+ if (sr & MCI_SR_NOTBUSY) {
+// printf("NOTBUSY\n");
+ WR4(sc, MCI_IDR, MCI_SR_NOTBUSY);
+ WR4(sc, MCI_IER, MCI_SR_CMDRDY);
+ }
+ if (sr & MCI_SR_DTIP) {
+// printf("Data transfer in progress\n");
+ }
+ if (sr & MCI_SR_BLKE) {
+// printf("Block transfer end\n");
+ }
+ if (sr & MCI_SR_TXRDY) {
+// printf("Ready to transmit\n");
+ }
+ if (sr & MCI_SR_RXRDY) {
+// printf("Ready to receive\n");
+ }
+ if (sr & MCI_SR_CMDRDY) {
+// printf("Command ready\n");
+ done = 1;
+ cmd->error = MMC_ERR_NONE;
+ }
+ }
+ if (done) {
+ WR4(sc, MCI_IDR, 0xffffffff);
+ if (cmd != NULL && (cmd->flags & MMC_RSP_PRESENT)) {
+ for (i = 0; i < ((cmd->flags & MMC_RSP_136) ? 4 : 1);
+ i++) {
+ cmd->resp[i] = RD4(sc, MCI_RSPR + i * 4);
+// printf("RSPR[%d] = %x\n", i, cmd->resp[i]);
+ }
+ }
+ at91_mci_start(sc);
+ }
+ AT91_MCI_UNLOCK(sc);
+}
+
+static int
+at91_mci_read_ivar(device_t bus, device_t child, int which, u_char *result)
+{
+ struct at91_mci_softc *sc = device_get_softc(bus);
+
+ switch (which) {
+ default:
+ return (EINVAL);
+ case MMCBR_IVAR_BUS_MODE:
+ *(int *)result = sc->host.ios.bus_mode;
+ break;
+ case MMCBR_IVAR_BUS_WIDTH:
+ *(int *)result = sc->host.ios.bus_width;
+ break;
+ case MMCBR_IVAR_CHIP_SELECT:
+ *(int *)result = sc->host.ios.chip_select;
+ break;
+ case MMCBR_IVAR_CLOCK:
+ *(int *)result = sc->host.ios.clock;
+ break;
+ case MMCBR_IVAR_F_MIN:
+ *(int *)result = sc->host.f_min;
+ break;
+ case MMCBR_IVAR_F_MAX:
+ *(int *)result = sc->host.f_max;
+ break;
+ case MMCBR_IVAR_HOST_OCR:
+ *(int *)result = sc->host.host_ocr;
+ break;
+ case MMCBR_IVAR_MODE:
+ *(int *)result = sc->host.mode;
+ break;
+ case MMCBR_IVAR_OCR:
+ *(int *)result = sc->host.ocr;
+ break;
+ case MMCBR_IVAR_POWER_MODE:
+ *(int *)result = sc->host.ios.power_mode;
+ break;
+ case MMCBR_IVAR_VDD:
+ *(int *)result = sc->host.ios.vdd;
+ break;
+ }
+ return (0);
+}
+
+static int
+at91_mci_write_ivar(device_t bus, device_t child, int which, uintptr_t value)
+{
+ struct at91_mci_softc *sc = device_get_softc(bus);
+
+ switch (which) {
+ default:
+ return (EINVAL);
+ case MMCBR_IVAR_BUS_MODE:
+ sc->host.ios.bus_mode = value;
+ break;
+ case MMCBR_IVAR_BUS_WIDTH:
+ sc->host.ios.bus_width = value;
+ break;
+ case MMCBR_IVAR_CHIP_SELECT:
+ sc->host.ios.chip_select = value;
+ break;
+ case MMCBR_IVAR_CLOCK:
+ sc->host.ios.clock = value;
+ break;
+ case MMCBR_IVAR_MODE:
+ sc->host.mode = value;
+ break;
+ case MMCBR_IVAR_OCR:
+ sc->host.ocr = value;
+ break;
+ case MMCBR_IVAR_POWER_MODE:
+ sc->host.ios.power_mode = value;
+ break;
+ case MMCBR_IVAR_VDD:
+ sc->host.ios.vdd = value;
+ break;
+ case MMCBR_IVAR_HOST_OCR:
+ case MMCBR_IVAR_F_MIN:
+ case MMCBR_IVAR_F_MAX:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+static device_method_t at91_mci_methods[] = {
+ /* device_if */
+ DEVMETHOD(device_probe, at91_mci_probe),
+ DEVMETHOD(device_attach, at91_mci_attach),
+ DEVMETHOD(device_detach, at91_mci_detach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_read_ivar, at91_mci_read_ivar),
+ DEVMETHOD(bus_write_ivar, at91_mci_write_ivar),
+
+ /* mmcbr_if */
+ DEVMETHOD(mmcbr_update_ios, at91_mci_update_ios),
+ DEVMETHOD(mmcbr_request, at91_mci_request),
+ DEVMETHOD(mmcbr_get_ro, at91_mci_get_ro),
+ DEVMETHOD(mmcbr_acquire_host, at91_mci_acquire_host),
+ DEVMETHOD(mmcbr_release_host, at91_mci_release_host),
+
+ {0, 0},
+};
+
+static driver_t at91_mci_driver = {
+ "at91_mci",
+ at91_mci_methods,
+ sizeof(struct at91_mci_softc),
+};
+static devclass_t at91_mci_devclass;
+
+
+DRIVER_MODULE(at91_mci, atmelarm, at91_mci_driver, at91_mci_devclass, 0, 0);
diff --git a/sys/arm/at91/at91_mcireg.h b/sys/arm/at91/at91_mcireg.h
new file mode 100644
index 0000000..458dfbc
--- /dev/null
+++ b/sys/arm/at91/at91_mcireg.h
@@ -0,0 +1,128 @@
+/*-
+ * Copyright (c) 2006 Berndt Walter. All rights reserved.
+ * Copyright (c) 2006 M. Warner Losh. 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 ``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 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 ARM_AT91_AT91QDMMCREG_H
+#define ARM_AT91_AT91QDMMCREG_H
+
+#define MMC_MAX 30
+
+#define MCI_CR 0x00 /* MCI Control Register */
+#define MCI_MR 0x04 /* MCI Mode Register */
+#define MCI_DTOR 0x08 /* MCI Data Timeout Register */
+#define MCI_SDCR 0x0c /* MCI SD Card Register */
+#define MCI_ARGR 0x10 /* MCI Argument Register */
+#define MCI_CMDR 0x14 /* MCI Command Register */
+#define MCI_RSPR 0x20 /* MCI Response Registers - 4 of them */
+#define MCI_RDR 0x30 /* MCI Receive Data Register */
+#define MCI_TDR 0x34 /* MCI Transmit Data Register */
+#define MCI_SR 0x40 /* MCI Status Register */
+#define MCI_IER 0x44 /* MCI Interrupt Enable Register */
+#define MCI_IDR 0x48 /* MCI Interrupt Disable Register */
+#define MCI_IMR 0x4c /* MCI Interrupt Mask Register */
+
+/* -------- MCI_CR : (MCI Offset: 0x0) MCI Control Register -------- */
+#define MCI_CR_MCIEN (0x1u << 0) /* (MCI) Multimedia Interface Enable */
+#define MCI_CR_MCIDIS (0x1u << 1) /* (MCI) Multimedia Interface Disable */
+#define MCI_CR_PWSEN (0x1u << 2) /* (MCI) Power Save Mode Enable */
+#define MCI_CR_PWSDIS (0x1u << 3) /* (MCI) Power Save Mode Disable */
+#define MCI_CR_SWRST (0x1u << 7) /* (MCI) Software Reset */
+/* -------- MCI_MR : (MCI Offset: 0x4) MCI Mode Register -------- */
+#define MCI_MR_CLKDIV (0xffu << 0) /* (MCI) Clock Divider */
+#define MCI_MR_PWSDIV (0x3fu << 8) /* (MCI) Power Saving Divider */
+#define MCI_MR_PDCPADV (0x1u << 14) /* (MCI) PDC Padding Value */
+#define MCI_MR_PDCMODE (0x1u << 15) /* (MCI) PDC Oriented Mode */
+#define MCI_MR_BLKLEN 0x3fff0000ul /* (MCI) Data Block Length */
+/* -------- MCI_DTOR : (MCI Offset: 0x8) MCI Data Timeout Register -------- */
+#define MCI_DTOR_DTOCYC (0xfu << 0) /* (MCI) Data Timeout Cycle Number */
+#define MCI_DTOR_DTOMUL (0x7u << 4) /* (MCI) Data Timeout Multiplier */
+#define MCI_DTOR_DTOMUL_1 (0x0u << 4) /* (MCI) DTOCYC x 1 */
+#define MCI_DTOR_DTOMUL_16 (0x1u << 4) /* (MCI) DTOCYC x 16 */
+#define MCI_DTOR_DTOMUL_128 (0x2u << 4) /* (MCI) DTOCYC x 128 */
+#define MCI_DTOR_DTOMUL_256 (0x3u << 4) /* (MCI) DTOCYC x 256 */
+#define MCI_DTOR_DTOMUL_1k (0x4u << 4) /* (MCI) DTOCYC x 1024 */
+#define MCI_DTOR_DTOMUL_4k (0x5u << 4) /* (MCI) DTOCYC x 4096 */
+#define MCI_DTOR_DTOMUL_64k (0x6u << 4) /* (MCI) DTOCYC x 65536 */
+#define MCI_DTOR_DTOMUL_1M (0x7u << 4) /* (MCI) DTOCYC x 1048576 */
+/* -------- MCI_SDCR : (MCI Offset: 0xc) MCI SD Card Register -------- */
+#define MCI_SDCR_SDCSEL (0x1u << 0) /* (MCI) SD Card Selector */
+#define MCI_SDCR_SDCBUS (0x1u << 7) /* (MCI) SD Card Bus Width */
+/* -------- MCI_CMDR : (MCI Offset: 0x14) MCI Command Register -------- */
+#define MCI_CMDR_CMDNB (0x1Fu << 0) /* (MCI) Command Number */
+#define MCI_CMDR_RSPTYP (0x3u << 6) /* (MCI) Response Type */
+#define MCI_CMDR_RSPTYP_NO (0x0u << 6) /* (MCI) No response */
+#define MCI_CMDR_RSPTYP_48 (0x1u << 6) /* (MCI) 48-bit response */
+#define MCI_CMDR_RSPTYP_136 (0x2u << 6) /* (MCI) 136-bit response */
+#define MCI_CMDR_SPCMD (0x7u << 8) /* (MCI) Special CMD */
+#define MCI_CMDR_SPCMD_NONE (0x0u << 8) /* (MCI) Not a special CMD */
+#define MCI_CMDR_SPCMD_INIT (0x1u << 8) /* (MCI) Initialization CMD */
+#define MCI_CMDR_SPCMD_SYNC (0x2u << 8) /* (MCI) Synchronized CMD */
+#define MCI_CMDR_SPCMD_IT_CMD (0x4u << 8) /* (MCI) Interrupt command */
+#define MCI_CMDR_SPCMD_IT_REP (0x5u << 8) /* (MCI) Interrupt response */
+#define MCI_CMDR_OPDCMD (0x1u << 11) /* (MCI) Open Drain Command */
+#define MCI_CMDR_MAXLAT (0x1u << 12) /* (MCI) Maximum Latency for Command to respond */
+#define MCI_CMDR_TRCMD (0x3u << 16) /* (MCI) Transfer CMD */
+#define MCI_CMDR_TRCMD_NO (0x0u << 16) /* (MCI) No transfer */
+#define MCI_CMDR_TRCMD_START (0x1u << 16) /* (MCI) Start transfer */
+#define MCI_CMDR_TRCMD_STOP (0x2u << 16) /* (MCI) Stop transfer */
+#define MCI_CMDR_TRDIR (0x1u << 18) /* (MCI) Transfer Direction */
+#define MCI_CMDR_TRTYP (0x3u << 19) /* (MCI) Transfer Type */
+#define MCI_CMDR_TRTYP_BLOCK (0x0u << 19) /* (MCI) Block Transfer type */
+#define MCI_CMDR_TRTYP_MULTIPLE (0x1u << 19) /* (MCI) Multiple Block transfer type */
+#define MCI_CMDR_TRTYP_STREAM (0x2u << 19) /* (MCI) Stream transfer type */
+/* -------- MCI_SR : (MCI Offset: 0x40) MCI Status Register -------- */
+#define MCI_SR_CMDRDY (0x1u << 0) /* (MCI) Command Ready flag */
+#define MCI_SR_RXRDY (0x1u << 1) /* (MCI) RX Ready flag */
+#define MCI_SR_TXRDY (0x1u << 2) /* (MCI) TX Ready flag */
+#define MCI_SR_BLKE (0x1u << 3) /* (MCI) Data Block Transfer Ended flag */
+#define MCI_SR_DTIP (0x1u << 4) /* (MCI) Data Transfer in Progress flag */
+#define MCI_SR_NOTBUSY (0x1u << 5) /* (MCI) Data Line Not Busy flag */
+#define MCI_SR_ENDRX (0x1u << 6) /* (MCI) End of RX Buffer flag */
+#define MCI_SR_ENDTX (0x1u << 7) /* (MCI) End of TX Buffer flag */
+#define MCI_SR_RXBUFF (0x1u << 14) /* (MCI) RX Buffer Full flag */
+#define MCI_SR_TXBUFE (0x1u << 15) /* (MCI) TX Buffer Empty flag */
+#define MCI_SR_RINDE (0x1u << 16) /* (MCI) Response Index Error flag */
+#define MCI_SR_RDIRE (0x1u << 17) /* (MCI) Response Direction Error flag */
+#define MCI_SR_RCRCE (0x1u << 18) /* (MCI) Response CRC Error flag */
+#define MCI_SR_RENDE (0x1u << 19) /* (MCI) Response End Bit Error flag */
+#define MCI_SR_RTOE (0x1u << 20) /* (MCI) Response Time-out Error flag */
+#define MCI_SR_DCRCE (0x1u << 21) /* (MCI) data CRC Error flag */
+#define MCI_SR_DTOE (0x1u << 22) /* (MCI) Data timeout Error flag */
+#define MCI_SR_OVRE (0x1u << 30) /* (MCI) Overrun flag */
+#define MCI_SR_UNRE (0x1u << 31) /* (MCI) Underrun flag */
+
+/* -------- MCI_IER : (MCI Offset: 0x44) MCI Interrupt Enable Register -------- */
+/* -------- MCI_IDR : (MCI Offset: 0x48) MCI Interrupt Disable Register -------- */
+/* -------- MCI_IMR : (MCI Offset: 0x4c) MCI Interrupt Mask Register -------- */
+
+#define MCI_SR_ERROR (MCI_SR_UNRE | MCI_SR_OVRE | MCI_SR_DTOE | \
+ MCI_SR_DCRCE | MCI_SR_RTOE | MCI_SR_RENDE | \
+ MCI_SR_RCRCE | MCI_SR_RDIRE | MCI_SR_RINDE)
+
+#define AT91C_BUS_WIDTH_1BIT 0x00
+#define AT91C_BUS_WIDTH_4BITS 0x02
+
+#endif /* ARM_AT91_AT91QDMMCREG_H */
OpenPOWER on IntegriCloud