[U-Boot] [PATCH] Create a new driver for Atmel at91/avr MCI adapters that uses the u-boot mmc framework.

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@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@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); +}

Rob Emanuele schrieb:
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
<snip>
Dear Rob Emanuele,
this patch applies to current TOT, however it does not work flawlessly:
TOP9000> fatls mmc 0:1 gen_atmel_mci: CMDR 0x00001050 (16) ARGR 0x00000000 (SR: 0x00000000) COMMAND Dbg Msg gen_atmel_mci: CMDR 0x00051051 (17) ARGR 0x00000000 (SR: 0x00000000) COMMAND Dbg Msg
*** system hangs here ***
But, if preceeded by a mmc read command it works (except for some odd clock related messages):
TOP9000> mmc read 0 21000000 0 1
MMC read: dev # 0, block # 0, count 1 ... mmc: setting clock 150000 Hz, block size 512 mmc: clock 150000 too low; setting CLKDIV to 255 mmc: setting clock 0 Hz, block size 512 mmc: clock 0 too low; setting CLKDIV to 255 mmc: setting clock 194000 Hz, block size 512 gen_atmel_mci: CMDR 0x00001048 ( 8) ARGR 0x000001aa (SR: 0x0010c0e5) COMMAND Failed mmc: setting clock 194000 Hz, block size 512 mmc: setting clock 25000000 Hz, block size 512 1 blocks read: OK TOP9000> fatls mmc 0:1 28521 blaue berge.jpg
1 file(s), 0 dir(s)
Which hardware(CPU) did you test it, and which u-boot version?
Best Regards, Reinhard Meyer

Reinhard Meyer schrieb:
TOP9000> fatls mmc 0:1 gen_atmel_mci: CMDR 0x00001050 (16) ARGR 0x00000000 (SR: 0x00000000) COMMAND Dbg Msg gen_atmel_mci: CMDR 0x00051051 (17) ARGR 0x00000000 (SR: 0x00000000) COMMAND Dbg Msg
*** system hangs here ***
But, if preceeded by a mmc read command it works (except for some odd clock related messages):
Ok, the problem is that fatls does not call any init functions, its first call is a read...
I added a check for initialized so read will fail if MCI has not been initialized.
The command "mmcinfo" does the initialisation.
TOP9000> fatls mmc 0:1 MCI not initialized! ** Can't read from device 0 **
** Unable to use mmc 0:1 for fatls ** TOP9000> mmci mmc: setting clock 150000 Hz, block size 512 mmc: clock 150000 too low; setting CLKDIV to 255 mmc: setting clock 0 Hz, block size 512 mmc: clock 0 too low; setting CLKDIV to 255 mmc: setting clock 194000 Hz, block size 512 mmc: setting clock 194000 Hz, block size 512 mmc: setting clock 25000000 Hz, block size 512 Device: Atmel MCI Manufacturer ID: 89 OEM: 303 Name: NCard Tran Speed: 25000000 Rd Block Len: 512 SD version 2.0 High Capacity: No Capacity: 2006974464 Bus Width: 4-bit TOP9000> fatls mmc 0:1 windows/ programme/
0 file(s), 2 dir(s)
I'll rework this driver to use struct SoC access and present it as a patch soon.
Reinhard
participants (2)
-
Reinhard Meyer
-
Rob Emanuele