u8500: Separating mmc config parameters from driver

Configuration in vexpress and u8500.v1 is different from what
is needed in u8500.v2.  As such, card configuration specifics need
to reside in the board file rather than the driver.

Signed-off-by: John Rigby <john.rigby@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Signed-off-by: Tom Rini <trini@ti.com>
This commit is contained in:
John Rigby 2012-07-31 08:59:31 +00:00 committed by Albert ARIBAUD
parent 75dfe964cb
commit 10ed93dcdd
6 changed files with 213 additions and 88 deletions

View File

@ -33,6 +33,8 @@
* MA 02111-1307 USA * MA 02111-1307 USA
*/ */
#include <common.h> #include <common.h>
#include <malloc.h>
#include <errno.h>
#include <netdev.h> #include <netdev.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/arch/systimer.h> #include <asm/arch/systimer.h>
@ -90,8 +92,25 @@ int board_eth_init(bd_t *bis)
int cpu_mmc_init(bd_t *bis) int cpu_mmc_init(bd_t *bis)
{ {
int rc = 0; int rc = 0;
(void) bis;
#ifdef CONFIG_ARM_PL180_MMCI #ifdef CONFIG_ARM_PL180_MMCI
rc = arm_pl180_mmci_init(); struct pl180_mmc_host *host;
host = malloc(sizeof(struct pl180_mmc_host));
if (!host)
return -ENOMEM;
memset(host, 0, sizeof(*host));
strcpy(host->name, "MMC");
host->base = (struct sdi_registers *)CONFIG_ARM_PL180_MMCI_BASE;
host->pwr_init = INIT_PWR;
host->clkdiv_init = SDI_CLKCR_CLKDIV_INIT_V1 | SDI_CLKCR_CLKEN;
host->voltages = VOLTAGE_WINDOW_MMC;
host->caps = 0;
host->clock_in = ARM_MCLK;
host->clock_min = ARM_MCLK / (2 * (SDI_CLKCR_CLKDIV_INIT_V1 + 1));
host->clock_max = CONFIG_ARM_PL180_MMCI_CLOCK_FREQ;
rc = arm_pl180_mmci_init(host);
#endif #endif
return rc; return rc;
} }

View File

@ -29,6 +29,9 @@
#include <asm/arch/hardware.h> #include <asm/arch/hardware.h>
#include <asm/arch/sys_proto.h> #include <asm/arch/sys_proto.h>
#ifdef CONFIG_MMC
#include "../../../drivers/mmc/arm_pl180_mmci.h"
#endif
#include "db8500_pins.h" #include "db8500_pins.h"
/* /*
@ -250,9 +253,96 @@ int board_late_init(void)
if ((raise_ab8500_gpio16() < 0)) if ((raise_ab8500_gpio16() < 0))
printf("error: cant' raise GPIO16\n"); printf("error: cant' raise GPIO16\n");
return 0;
}
#ifdef CONFIG_MMC #ifdef CONFIG_MMC
/*
* emmc_host_init - initialize the emmc controller.
* Configure GPIO settings, set initial clock and power for emmc slot.
* Initialize mmc struct and register with mmc framework.
*/
static int emmc_host_init(void)
{
struct pl180_mmc_host *host;
host = malloc(sizeof(struct pl180_mmc_host));
if (!host)
return -ENOMEM;
memset(host, 0, sizeof(*host));
host->base = (struct sdi_registers *)CFG_EMMC_BASE;
host->pwr_init = SDI_PWR_OPD | SDI_PWR_PWRCTRL_ON;
host->clkdiv_init = SDI_CLKCR_CLKDIV_INIT_V2 |
SDI_CLKCR_CLKEN | SDI_CLKCR_HWFC_EN;
strcpy(host->name, "EMMC");
host->caps = MMC_MODE_8BIT | MMC_MODE_HS | MMC_MODE_HS_52MHz;
host->voltages = VOLTAGE_WINDOW_MMC;
host->clock_min = ARM_MCLK / (2 + SDI_CLKCR_CLKDIV_INIT_V2);
host->clock_max = ARM_MCLK / 2;
host->clock_in = ARM_MCLK;
host->version2 = 1;
return arm_pl180_mmci_init(host);
}
/*
* mmc_host_init - initialize the external mmc controller.
* Configure GPIO settings, set initial clock and power for mmc slot.
* Initialize mmc struct and register with mmc framework.
*/
static int mmc_host_init(void)
{
struct pl180_mmc_host *host;
u32 sdi_u32;
host = malloc(sizeof(struct pl180_mmc_host));
if (!host)
return -ENOMEM;
memset(host, 0, sizeof(*host));
host->base = (struct sdi_registers *)CFG_MMC_BASE;
sdi_u32 = 0xBF;
writel(sdi_u32, &host->base->power);
host->pwr_init = 0xBF;
host->clkdiv_init = SDI_CLKCR_CLKDIV_INIT_V2 |
SDI_CLKCR_CLKEN | SDI_CLKCR_HWFC_EN;
strcpy(host->name, "MMC");
host->caps = MMC_MODE_8BIT;
host->b_max = 0;
host->voltages = VOLTAGE_WINDOW_SD;
host->clock_min = ARM_MCLK / (2 + SDI_CLKCR_CLKDIV_INIT_V2);
host->clock_max = ARM_MCLK / 2;
host->clock_in = ARM_MCLK;
host->version2 = 1;
return arm_pl180_mmci_init(host);
}
/*
* board_mmc_init - initialize all the mmc/sd host controllers.
* Called by generic mmc framework.
*/
int board_mmc_init(bd_t *bis)
{
int error;
(void) bis;
error = emmc_host_init();
if (error) {
printf("emmc_host_init() %d\n", error);
return -1;
}
u8500_mmc_power_init(); u8500_mmc_power_init();
#endif /* CONFIG_MMC */
error = mmc_host_init();
if (error) {
printf("mmc_host_init() %d\n", error);
return -1;
}
return 0; return 0;
} }
#endif /* CONFIG_MMC */

View File

@ -18,6 +18,7 @@
#include <config.h> #include <config.h>
#include <common.h> #include <common.h>
#include <malloc.h>
#include <i2c.h> #include <i2c.h>
#include <asm/types.h> #include <asm/types.h>
#include <asm/io.h> #include <asm/io.h>
@ -374,12 +375,27 @@ static int u8500_mmci_board_init(void)
int board_mmc_init(bd_t *bd) int board_mmc_init(bd_t *bd)
{ {
struct pl180_mmc_host *host;
if (u8500_mmci_board_init()) if (u8500_mmci_board_init())
return -ENODEV; return -ENODEV;
if (arm_pl180_mmci_init()) host = malloc(sizeof(struct pl180_mmc_host));
return -ENODEV; if (!host)
return 0; return -ENOMEM;
memset(host, 0, sizeof(*host));
strcpy(host->name, "MMC");
host->base = (struct sdi_registers *)CONFIG_ARM_PL180_MMCI_BASE;
host->pwr_init = INIT_PWR;
host->clkdiv_init = SDI_CLKCR_CLKDIV_INIT_V1 | SDI_CLKCR_CLKEN;
host->voltages = VOLTAGE_WINDOW_MMC;
host->caps = 0;
host->clock_in = ARM_MCLK;
host->clock_min = ARM_MCLK / (2 * (SDI_CLKCR_CLKDIV_INIT_V1 + 1));
host->clock_max = CONFIG_ARM_PL180_MMCI_CLOCK_FREQ;
return arm_pl180_mmci_init(host);
} }
#endif #endif

View File

@ -32,14 +32,10 @@
#include "arm_pl180_mmci.h" #include "arm_pl180_mmci.h"
#include <malloc.h> #include <malloc.h>
struct mmc_host {
struct sdi_registers *base;
};
static int wait_for_command_end(struct mmc *dev, struct mmc_cmd *cmd) static int wait_for_command_end(struct mmc *dev, struct mmc_cmd *cmd)
{ {
u32 hoststatus, statusmask; u32 hoststatus, statusmask;
struct mmc_host *host = dev->priv; struct pl180_mmc_host *host = dev->priv;
statusmask = SDI_STA_CTIMEOUT | SDI_STA_CCRCFAIL; statusmask = SDI_STA_CTIMEOUT | SDI_STA_CCRCFAIL;
if ((cmd->resp_type & MMC_RSP_PRESENT)) if ((cmd->resp_type & MMC_RSP_PRESENT))
@ -53,8 +49,8 @@ static int wait_for_command_end(struct mmc *dev, struct mmc_cmd *cmd)
writel(statusmask, &host->base->status_clear); writel(statusmask, &host->base->status_clear);
if (hoststatus & SDI_STA_CTIMEOUT) { if (hoststatus & SDI_STA_CTIMEOUT) {
printf("CMD%d time out\n", cmd->cmdidx); debug("CMD%d time out\n", cmd->cmdidx);
return -ETIMEDOUT; return TIMEOUT;
} else if ((hoststatus & SDI_STA_CCRCFAIL) && } else if ((hoststatus & SDI_STA_CCRCFAIL) &&
(cmd->flags & MMC_RSP_CRC)) { (cmd->flags & MMC_RSP_CRC)) {
printf("CMD%d CRC error\n", cmd->cmdidx); printf("CMD%d CRC error\n", cmd->cmdidx);
@ -80,7 +76,7 @@ static int do_command(struct mmc *dev, struct mmc_cmd *cmd)
{ {
int result; int result;
u32 sdi_cmd = 0; u32 sdi_cmd = 0;
struct mmc_host *host = dev->priv; struct pl180_mmc_host *host = dev->priv;
sdi_cmd = ((cmd->cmdidx & SDI_CMD_CMDINDEX_MASK) | SDI_CMD_CPSMEN); sdi_cmd = ((cmd->cmdidx & SDI_CMD_CMDINDEX_MASK) | SDI_CMD_CPSMEN);
@ -112,7 +108,7 @@ static int read_bytes(struct mmc *dev, u32 *dest, u32 blkcount, u32 blksize)
{ {
u32 *tempbuff = dest; u32 *tempbuff = dest;
u64 xfercount = blkcount * blksize; u64 xfercount = blkcount * blksize;
struct mmc_host *host = dev->priv; struct pl180_mmc_host *host = dev->priv;
u32 status, status_err; u32 status, status_err;
debug("read_bytes: blkcount=%u blksize=%u\n", blkcount, blksize); debug("read_bytes: blkcount=%u blksize=%u\n", blkcount, blksize);
@ -168,7 +164,7 @@ static int write_bytes(struct mmc *dev, u32 *src, u32 blkcount, u32 blksize)
u32 *tempbuff = src; u32 *tempbuff = src;
int i; int i;
u64 xfercount = blkcount * blksize; u64 xfercount = blkcount * blksize;
struct mmc_host *host = dev->priv; struct pl180_mmc_host *host = dev->priv;
u32 status, status_err; u32 status, status_err;
debug("write_bytes: blkcount=%u blksize=%u\n", blkcount, blksize); debug("write_bytes: blkcount=%u blksize=%u\n", blkcount, blksize);
@ -227,14 +223,19 @@ static int do_data_transfer(struct mmc *dev,
struct mmc_data *data) struct mmc_data *data)
{ {
int error = -ETIMEDOUT; int error = -ETIMEDOUT;
struct mmc_host *host = dev->priv; struct pl180_mmc_host *host = dev->priv;
u32 blksz = 0; u32 blksz = 0;
u32 data_ctrl = 0; u32 data_ctrl = 0;
u32 data_len = (u32) (data->blocks * data->blocksize); u32 data_len = (u32) (data->blocks * data->blocksize);
blksz = (ffs(data->blocksize) - 1); if (!host->version2) {
data_ctrl |= ((blksz << 4) & SDI_DCTRL_DBLKSIZE_MASK); blksz = (ffs(data->blocksize) - 1);
data_ctrl |= SDI_DCTRL_DTEN; data_ctrl |= ((blksz << 4) & SDI_DCTRL_DBLKSIZE_MASK);
} else {
blksz = data->blocksize;
data_ctrl |= (blksz << SDI_DCTRL_DBLOCKSIZE_V2_SHIFT);
}
data_ctrl |= SDI_DCTRL_DTEN | SDI_DCTRL_BUSYMODE;
writel(SDI_DTIMER_DEFAULT, &host->base->datatimer); writel(SDI_DTIMER_DEFAULT, &host->base->datatimer);
writel(data_len, &host->base->datalength); writel(data_len, &host->base->datalength);
@ -257,7 +258,7 @@ static int do_data_transfer(struct mmc *dev,
writel(data_ctrl, &host->base->datactrl); writel(data_ctrl, &host->base->datactrl);
error = write_bytes(dev, (u32 *)data->src, (u32)data->blocks, error = write_bytes(dev, (u32 *)data->src, (u32)data->blocks,
(u32)data->blocksize); (u32)data->blocksize);
} }
return error; return error;
@ -280,17 +281,16 @@ static int host_request(struct mmc *dev,
/* MMC uses open drain drivers in the enumeration phase */ /* MMC uses open drain drivers in the enumeration phase */
static int mmc_host_reset(struct mmc *dev) static int mmc_host_reset(struct mmc *dev)
{ {
struct mmc_host *host = dev->priv; struct pl180_mmc_host *host = dev->priv;
u32 sdi_u32 = SDI_PWR_OPD | SDI_PWR_PWRCTRL_ON;
writel(sdi_u32, &host->base->power); writel(host->pwr_init, &host->base->power);
return 0; return 0;
} }
static void host_set_ios(struct mmc *dev) static void host_set_ios(struct mmc *dev)
{ {
struct mmc_host *host = dev->priv; struct pl180_mmc_host *host = dev->priv;
u32 sdi_clkcr; u32 sdi_clkcr;
sdi_clkcr = readl(&host->base->clock); sdi_clkcr = readl(&host->base->clock);
@ -298,15 +298,26 @@ static void host_set_ios(struct mmc *dev)
/* Ramp up the clock rate */ /* Ramp up the clock rate */
if (dev->clock) { if (dev->clock) {
u32 clkdiv = 0; u32 clkdiv = 0;
u32 tmp_clock;
if (dev->clock >= dev->f_max) if (dev->clock >= dev->f_max) {
clkdiv = 0;
dev->clock = dev->f_max; dev->clock = dev->f_max;
} else {
clkdiv = (host->clock_in / dev->clock) - 2;
}
clkdiv = ((ARM_MCLK / dev->clock) / 2) - 1; tmp_clock = host->clock_in / (clkdiv + 2);
while (tmp_clock > dev->clock) {
clkdiv++;
tmp_clock = host->clock_in / (clkdiv + 2);
}
if (clkdiv > SDI_CLKCR_CLKDIV_MASK) if (clkdiv > SDI_CLKCR_CLKDIV_MASK)
clkdiv = SDI_CLKCR_CLKDIV_MASK; clkdiv = SDI_CLKCR_CLKDIV_MASK;
tmp_clock = host->clock_in / (clkdiv + 2);
dev->clock = tmp_clock;
sdi_clkcr &= ~(SDI_CLKCR_CLKDIV_MASK); sdi_clkcr &= ~(SDI_CLKCR_CLKDIV_MASK);
sdi_clkcr |= clkdiv; sdi_clkcr |= clkdiv;
} }
@ -322,8 +333,11 @@ static void host_set_ios(struct mmc *dev)
case 4: case 4:
buswidth |= SDI_CLKCR_WIDBUS_4; buswidth |= SDI_CLKCR_WIDBUS_4;
break; break;
case 8:
buswidth |= SDI_CLKCR_WIDBUS_8;
break;
default: default:
printf("Invalid bus width\n"); printf("Invalid bus width: %d\n", dev->bus_width);
break; break;
} }
sdi_clkcr &= ~(SDI_CLKCR_WIDBUS_MASK); sdi_clkcr &= ~(SDI_CLKCR_WIDBUS_MASK);
@ -334,83 +348,40 @@ static void host_set_ios(struct mmc *dev)
udelay(CLK_CHANGE_DELAY); udelay(CLK_CHANGE_DELAY);
} }
struct mmc *alloc_mmc_struct(void)
{
struct mmc_host *host = NULL;
struct mmc *mmc_device = NULL;
host = malloc(sizeof(struct mmc_host));
if (!host)
return NULL;
mmc_device = malloc(sizeof(struct mmc));
if (!mmc_device)
goto err;
mmc_device->priv = host;
return mmc_device;
err:
free(host);
return NULL;
}
/* /*
* mmc_host_init - initialize the mmc controller. * mmc_host_init - initialize the mmc controller.
* Set initial clock and power for mmc slot. * Set initial clock and power for mmc slot.
* Initialize mmc struct and register with mmc framework. * Initialize mmc struct and register with mmc framework.
*/ */
static int arm_pl180_mmci_host_init(struct mmc *dev) int arm_pl180_mmci_init(struct pl180_mmc_host *host)
{ {
struct mmc_host *host = dev->priv; struct mmc *dev;
u32 sdi_u32; u32 sdi_u32;
host->base = (struct sdi_registers *)CONFIG_ARM_PL180_MMCI_BASE; dev = malloc(sizeof(struct mmc));
if (!dev)
return -ENOMEM;
/* Initially set power-on, full voltage & MMCI read */ memset(dev, 0, sizeof(struct mmc));
sdi_u32 = INIT_PWR; dev->priv = host;
writel(sdi_u32, &host->base->power);
/* setting clk freq 505KHz */ writel(host->pwr_init, &host->base->power);
sdi_u32 = SDI_CLKCR_CLKDIV_INIT | SDI_CLKCR_CLKEN; writel(host->clkdiv_init, &host->base->clock);
writel(sdi_u32, &host->base->clock);
udelay(CLK_CHANGE_DELAY); udelay(CLK_CHANGE_DELAY);
/* Disable mmc interrupts */ /* Disable mmc interrupts */
sdi_u32 = readl(&host->base->mask0) & ~SDI_MASK0_MASK; sdi_u32 = readl(&host->base->mask0) & ~SDI_MASK0_MASK;
writel(sdi_u32, &host->base->mask0); writel(sdi_u32, &host->base->mask0);
strncpy(dev->name, host->name, sizeof(dev->name));
sprintf(dev->name, "MMC");
dev->clock = ARM_MCLK / (2 * (SDI_CLKCR_CLKDIV_INIT + 1));
dev->send_cmd = host_request; dev->send_cmd = host_request;
dev->set_ios = host_set_ios; dev->set_ios = host_set_ios;
dev->init = mmc_host_reset; dev->init = mmc_host_reset;
dev->getcd = NULL; dev->getcd = NULL;
dev->host_caps = 0; dev->host_caps = host->caps;
dev->voltages = VOLTAGE_WINDOW_MMC; dev->voltages = host->voltages;
dev->f_min = dev->clock; dev->f_min = host->clock_min;
dev->f_max = CONFIG_ARM_PL180_MMCI_CLOCK_FREQ; dev->f_max = host->clock_max;
dev->b_max = host->b_max;
return 0;
}
int arm_pl180_mmci_init(void)
{
int error;
struct mmc *dev;
dev = alloc_mmc_struct();
if (!dev)
return -1;
error = arm_pl180_mmci_host_init(dev);
if (error) {
printf("mmci_host_init error - %d\n", error);
return -1;
}
dev->b_max = 0;
mmc_register(dev); mmc_register(dev);
debug("registered mmc interface number is:%d\n", dev->block_dev.dev); debug("registered mmc interface number is:%d\n", dev->block_dev.dev);

View File

@ -26,8 +26,6 @@
#ifndef __ARM_PL180_MMCI_H__ #ifndef __ARM_PL180_MMCI_H__
#define __ARM_PL180_MMCI_H__ #define __ARM_PL180_MMCI_H__
int arm_pl180_mmci_init(void);
#define COMMAND_REG_DELAY 300 #define COMMAND_REG_DELAY 300
#define DATA_REG_DELAY 1000 #define DATA_REG_DELAY 1000
#define CLK_CHANGE_DELAY 2000 #define CLK_CHANGE_DELAY 2000
@ -59,8 +57,13 @@ int arm_pl180_mmci_init(void);
#define SDI_CLKCR_WIDBUS_MASK 0x00001800 #define SDI_CLKCR_WIDBUS_MASK 0x00001800
#define SDI_CLKCR_WIDBUS_1 0x00000000 #define SDI_CLKCR_WIDBUS_1 0x00000000
#define SDI_CLKCR_WIDBUS_4 0x00000800 #define SDI_CLKCR_WIDBUS_4 0x00000800
/* V2 only */
#define SDI_CLKCR_WIDBUS_8 0x00001000
#define SDI_CLKCR_NEDGE 0x00002000
#define SDI_CLKCR_HWFC_EN 0x00004000
#define SDI_CLKCR_CLKDIV_INIT 0x000000C6 /* MCLK/(2*(0xC6+1)) => 505KHz */ #define SDI_CLKCR_CLKDIV_INIT_V1 0x000000C6 /* MCLK/(2*(0xC6+1)) => 505KHz */
#define SDI_CLKCR_CLKDIV_INIT_V2 0x000000FD
/* SDI command register bits */ /* SDI command register bits */
#define SDI_CMD_CMDINDEX_MASK 0x000000FF #define SDI_CMD_CMDINDEX_MASK 0x000000FF
@ -144,6 +147,8 @@ int arm_pl180_mmci_init(void);
#define SDI_DCTRL_DBOOTMODEEN 0x00002000 #define SDI_DCTRL_DBOOTMODEEN 0x00002000
#define SDI_DCTRL_BUSYMODE 0x00004000 #define SDI_DCTRL_BUSYMODE 0x00004000
#define SDI_DCTRL_DDR_MODE 0x00008000 #define SDI_DCTRL_DDR_MODE 0x00008000
#define SDI_DCTRL_DBLOCKSIZE_V2_MASK 0x7fff0000
#define SDI_DCTRL_DBLOCKSIZE_V2_SHIFT 16
#define SDI_FIFO_BURST_SIZE 8 #define SDI_FIFO_BURST_SIZE 8
@ -180,4 +185,20 @@ struct sdi_registers {
u32 pcell_id3; /* 0xFFC*/ u32 pcell_id3; /* 0xFFC*/
}; };
struct pl180_mmc_host {
struct sdi_registers *base;
char name[32];
unsigned int b_max;
unsigned int voltages;
unsigned int caps;
unsigned int clock_in;
unsigned int clock_min;
unsigned int clock_max;
unsigned int clkdiv_init;
unsigned int pwr_init;
int version2;
};
int arm_pl180_mmci_init(struct pl180_mmc_host *);
#endif #endif

View File

@ -228,6 +228,14 @@
#define CONFIG_SYS_TEXT_BASE 0x0 #define CONFIG_SYS_TEXT_BASE 0x0
#endif #endif
/*
* MMC related configs
*/
#define CONFIG_ARM_PL180_MMCI
#define MMC_BLOCK_SIZE 512
#define CFG_EMMC_BASE 0x80114000
#define CFG_MMC_BASE 0x80126000
/* /*
* FLASH and environment organization * FLASH and environment organization
*/ */