[U-Boot] [PATCH] Create a new driver for Atmel at91/avr MCI adapters that uses the u-boot mmc framework.
Rob Emanuele
rob at emanuele.us
Wed Apr 28 22:10:53 CEST 2010
To use it on your platform add defines like these:
#define CONFIG_MMC 1
#define CONFIG_GENERIC_MMC 1
#define CONFIG_GENERIC_ATMEL_MCI 1
/*change this for your cpu */
#define MMCI_BASE 0xFFF80000
Then create an init routine in your platform code:
/* this is a weak define that we are overriding */
int board_mmc_init(bd_t *bd)
{
/* This calls your code to clock the interface
* and set the pins up, etc.
*/
mmc_hw_init();
/* This calls the atmel_mmc_init in gen_atmel_mci.c */
return atmel_mmc_init(bd);
}
Signed-off-by: Rob Emanuele <rob at emanuele.us>
---
drivers/mmc/Makefile | 1 +
drivers/mmc/gen_atmel_mci.c | 330 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 331 insertions(+), 0 deletions(-)
create mode 100644 drivers/mmc/gen_atmel_mci.c
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 6fa04b8..1785253 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -26,6 +26,7 @@ include $(TOPDIR)/config.mk
LIB := $(obj)libmmc.a
COBJS-$(CONFIG_GENERIC_MMC) += mmc.o
+COBJS-$(CONFIG_GENERIC_ATMEL_MCI) += gen_atmel_mci.o
COBJS-$(CONFIG_ATMEL_MCI) += atmel_mci.o
COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o
COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o
diff --git a/drivers/mmc/gen_atmel_mci.c b/drivers/mmc/gen_atmel_mci.c
new file mode 100644
index 0000000..9e3430a
--- /dev/null
+++ b/drivers/mmc/gen_atmel_mci.c
@@ -0,0 +1,330 @@
+/*
+ * Copyright 2010, Rob Emanuele <rob at emanuele.us>
+ *
+ * Original Driver:
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <config.h>
+#include <common.h>
+#include <command.h>
+#include <hwconfig.h>
+#include <mmc.h>
+#include <part.h>
+#include <malloc.h>
+#include <mmc.h>
+#include <asm/io.h>
+#include <asm/errno.h>
+#include <asm/byteorder.h>
+#include <asm/arch/clk.h>
+#include <asm/arch/memory-map.h>
+#include "atmel_mci.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#ifndef CONFIG_SYS_MMC_CLK_OD
+#define CONFIG_SYS_MMC_CLK_OD 150000
+#endif
+#define MMC_DEFAULT_BLKLEN 512
+
+void dump_cmd(uint cmdr, uint arg, uint status, const char* msg)
+{
+ printf("gen_atmel_mci: CMDR 0x%08x (%2u) ARGR 0x%08x (SR: 0x%08x) %s\n",
+ cmdr, cmdr&0x3F, arg, status, msg);
+}
+
+/* Setup for MCI Clock and Block Size */
+static void mci_set_mode(unsigned long hz, unsigned long blklen)
+{
+ unsigned long bus_hz;
+ unsigned long clkdiv;
+
+ if (hz > 0) {
+ bus_hz = get_mck_clk_rate();
+ clkdiv = (bus_hz / hz) / 2 - 1;
+ } else {
+ clkdiv = ~0UL;
+ }
+
+ printf("mmc: setting clock %lu Hz, block size %lu\n",
+ hz, blklen);
+
+ if (clkdiv & ~255UL) {
+ clkdiv = 255;
+ printf("mmc: clock %lu too low; setting CLKDIV to 255\n",
+ hz);
+ }
+
+ blklen &= 0xfffc;
+ /* On some platforms RDPROOF and WRPROOF are ignored */
+ mmci_writel(MR, (MMCI_BF(CLKDIV, clkdiv)
+ | MMCI_BF(BLKLEN, blklen)
+ | MMCI_BIT(RDPROOF)
+ | MMCI_BIT(WRPROOF)));
+}
+
+/* Return the CMDR with flags for a given command and data packet */
+static uint atmel_encode_cmd(struct mmc_cmd *cmd, struct mmc_data *data, uint* error_flags)
+{
+ uint cmdr = 0;
+ /* Default Flags for Errors */
+ *error_flags |= (MMCI_BIT(DTOE) | MMCI_BIT(RDIRE) | MMCI_BIT(RENDE) | MMCI_BIT(RINDE) | MMCI_BIT(RTOE));
+
+ /* Default Flags for the Command */
+ cmdr |= MMCI_BIT(MAXLAT);
+
+ if (data) {
+ cmdr |= MMCI_BF(TRCMD,1);
+
+ if (data->blocks > 1) {
+ cmdr |= MMCI_BF(TRTYP,1);
+ }
+
+ if (data->flags & MMC_DATA_READ)
+ cmdr |= MMCI_BIT(TRDIR);
+ }
+
+ if (cmd->resp_type & MMC_RSP_CRC)
+ *error_flags |= MMCI_BIT(RCRCE);
+
+ if (cmd->resp_type & MMC_RSP_136)
+ cmdr |= MMCI_BF(RSPTYP,2);
+ else if (cmd->resp_type & MMC_RSP_BUSY)
+ cmdr |= MMCI_BF(RSPTYP,3);
+ else if (cmd->resp_type & MMC_RSP_PRESENT)
+ cmdr |= MMCI_BF(RSPTYP,1);
+
+ return cmdr | MMCI_BF(CMDNB,cmd->cmdidx);
+}
+
+static uint atmel_data_read(uint* data, uint error_flags)
+{
+ uint status;
+ do {
+ status = mmci_readl(SR);
+ if (status & (error_flags | MMCI_BIT(OVRE)))
+ goto io_fail;
+ } while (!(status & MMCI_BIT(RXRDY)));
+
+ if (status & MMCI_BIT(RXRDY)) {
+ *data = mmci_readl(RDR);
+ status = 0;
+ }
+io_fail:
+ return status;
+}
+
+static uint atmel_data_write(uint* data, uint error_flags)
+{
+ uint status;
+ do {
+ status = mmci_readl(SR);
+ if (status & (error_flags | MMCI_BIT(UNRE)))
+ goto io_fail;
+ } while (!(status & MMCI_BIT(TXRDY)));
+
+ if (status & MMCI_BIT(TXRDY)) {
+ mmci_writel(TDR,*data);
+ status = 0;
+ }
+io_fail:
+ return status;
+}
+
+/*
+ * Sends a command out on the bus and deals with the block data.
+ * Takes the mmc pointer, a command pointer, and an optional data pointer.
+ */
+static int
+atmel_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
+{
+ uint cmdr;
+ uint error_flags = 0;
+ uint status;
+
+ /* Figure out the transfer arguments */
+ cmdr = atmel_encode_cmd(cmd, data, &error_flags);
+
+ /* Send the command */
+ mmci_writel(ARGR,cmd->cmdarg);
+ mmci_writel(CMDR, cmdr);
+
+#if GEN_ATMEL_MCI_DEBUG
+ dump_cmd(cmdr, cmd->cmdarg, 0, "COMMAND Dbg Msg");
+#endif
+
+ /* Wait for the command to complete */
+ while (!((status = mmci_readl(SR)) & MMCI_BIT(CMDRDY)));
+
+ if (status & error_flags) {
+ dump_cmd(cmdr, cmd->cmdarg, status, "COMMAND Failed");
+ return COMM_ERR;
+ }
+
+ /* Copy the response to the response buffer */
+ if (cmd->resp_type & MMC_RSP_136) {
+ cmd->response[0] = mmci_readl(RSPR);
+ cmd->response[1] = mmci_readl(RSPR1);
+ cmd->response[2] = mmci_readl(RSPR2);
+ cmd->response[3] = mmci_readl(RSPR3);
+ } else
+ cmd->response[0] = mmci_readl(RSPR);
+
+ /* transfer all of the blocks */
+ if (data) {
+ uint word_count, block_count;
+ uint* ioptr;
+ uint sys_blocksize, dummy, i;
+ uint (*atmel_data_op)(uint* data, uint error_flags);
+
+ if (data->flags & MMC_DATA_READ) {
+ atmel_data_op = atmel_data_read;
+ sys_blocksize = mmc->read_bl_len;
+ ioptr = (uint*)data->dest;
+ } else {
+ atmel_data_op = atmel_data_write;
+ sys_blocksize = mmc->write_bl_len;
+ ioptr = (uint*)data->src;
+ }
+
+ status = 0;
+ for(block_count = 0; block_count < data->blocks && !status; block_count++) {
+ word_count = 0;
+ do {
+ status = atmel_data_op(ioptr,error_flags);
+ word_count++;
+ ioptr++;
+ } while(!status && word_count < (data->blocksize / 4));
+#if GEN_ATMEL_MCI_DEBUG
+ if (data->flags & MMC_DATA_READ)
+ {
+ char *mem_args[3];
+ char mem_addr_str[16];
+ char mem_len_str[16];
+ printf("Command caused a block read for %u bytes (read %u bytes)\n", data->blocksize, word_count*4);
+ mem_args[0] = "md.l";
+ sprintf(mem_addr_str,"0x%08x",(uint)(data->dest));
+ mem_args[1] = mem_addr_str;
+ sprintf(mem_len_str,"0x%x",word_count);
+ mem_args[2] = mem_len_str;
+ printf("Dumping with: %s %s %s\n", mem_args[0], mem_args[1], mem_args[2]);
+ do_mem_md(NULL, 0, 3, mem_args);
+ }
+#endif
+#if GEN_ATMEL_MCI_DEBUG
+ if (!status && word_count < (sys_blocksize / 4))
+ printf("sponging....\n");
+#endif
+ /* sponge the rest of a full block */
+ while (!status && word_count < (sys_blocksize / 4)) {
+ status = atmel_data_op(&dummy,error_flags);
+ word_count++;
+ }
+ if (status) {
+ dump_cmd(cmdr, cmd->cmdarg, status, "XFER Failed");
+ return COMM_ERR;
+ }
+ }
+
+ /* Wait for Transfer End */
+ i = 0;
+ do {
+ status = mmci_readl(SR);
+
+ if (status & error_flags) {
+ dump_cmd(cmdr, cmd->cmdarg, status, "XFER DTIP Wait Failed");
+ return COMM_ERR;
+ }
+ i++;
+ } while ((status & MMCI_BIT(DTIP)) && i < 10000);
+ if (status & MMCI_BIT(DTIP)) {
+ dump_cmd(cmdr, cmd->cmdarg, status, "XFER DTIP never unset, ignoring");
+ }
+ }
+
+ return 0;
+}
+
+static void atmel_set_ios(struct mmc *mmc)
+{
+ /* Set the clock speed */
+ mci_set_mode(mmc->clock, MMC_DEFAULT_BLKLEN);
+
+ /* set the bus width and select slot A for this interface
+ * there is no capability for multiple slots on the same interface yet
+ */
+ /* Bitfield SCDBUS needs to be expanded to 2 bits for 8-bit buses
+ */
+ if (mmc->bus_width == 4)
+ mmci_writel(SDCR, MMCI_BF(SCDBUS,0x1)|MMCI_BF(SCDSEL,0x0));
+ else
+ mmci_writel(SDCR, MMCI_BF(SCDBUS,0x0)|MMCI_BF(SCDSEL,0x0));
+}
+
+static int atmel_init(struct mmc *mmc)
+{
+ /* Initialize controller */
+ mmci_writel(CR, MMCI_BIT(SWRST)); /* soft reset */
+ mmci_writel(CR, MMCI_BIT(PWSDIS)); /* disable power save */
+ mmci_writel(CR, MMCI_BIT(MCIEN)); /* enable mci */
+
+ /* Initial Time-outs */
+ mmci_writel(DTOR, 0x5f);
+ /* Disable Interrupts */
+ mmci_writel(IDR, ~0UL);
+
+ /* Set defualt clocks and blocklen */
+ mci_set_mode(CONFIG_SYS_MMC_CLK_OD, MMC_DEFAULT_BLKLEN);
+
+ return 0;
+}
+
+static int atmel_initialize(bd_t *bis)
+{
+ struct mmc *mmc;
+
+ mmc = malloc(sizeof(struct mmc));
+
+ sprintf(mmc->name, "Atmel MCI");
+ /* We set this but atmel_mci.h makes use of MMCI_BASE directly */
+ mmc->priv = (void *)MMCI_BASE;
+ mmc->send_cmd = atmel_send_cmd;
+ mmc->set_ios = atmel_set_ios;
+ mmc->init = atmel_init;
+
+ /* need to be able to pass these in on a baord by board basis */
+ mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+ mmc->host_caps = MMC_MODE_4BIT;
+
+ /* High Speed Support? */
+
+ mmc->f_min = get_mck_clk_rate() / (2*(0xFF+1));
+ mmc->f_max = get_mck_clk_rate() / (2);
+
+ mmc_register(mmc);
+
+ return 0;
+}
+
+int atmel_mmc_init(bd_t *bis)
+{
+ return atmel_initialize(bis);
+}
--
1.6.3.3
More information about the U-Boot
mailing list