* [PATCH 1/3] Add MMC support to barebox
2010-10-07 12:28 [RFC] Add MMC support to barebox Juergen Beisert
@ 2010-10-07 12:28 ` Juergen Beisert
2010-10-07 12:28 ` [PATCH 2/3] Add i.MX23 MCI card support Juergen Beisert
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Juergen Beisert @ 2010-10-07 12:28 UTC (permalink / raw)
To: barebox
This adds the basic framework to handle MCI cards in barebox.
Signed-off-by: Juergen Beisert <jbe@pengutronix.de>
---
drivers/Kconfig | 1 +
drivers/Makefile | 1 +
drivers/mci/Kconfig | 31 ++
drivers/mci/Makefile | 1 +
drivers/mci/mci-core.c | 1308 ++++++++++++++++++++++++++++++++++++++++++++++++
include/mci.h | 226 +++++++++
6 files changed, 1568 insertions(+), 0 deletions(-)
create mode 100644 drivers/mci/Kconfig
create mode 100644 drivers/mci/Makefile
create mode 100644 drivers/mci/mci-core.c
create mode 100644 include/mci.h
diff --git a/drivers/Kconfig b/drivers/Kconfig
index f7154c6..13235f3 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -9,6 +9,7 @@ source "drivers/mtd/Kconfig"
source "drivers/ata/Kconfig"
source "drivers/usb/Kconfig"
source "drivers/video/Kconfig"
+source "drivers/mci/Kconfig"
source "drivers/clk/Kconfig"
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 706e1c8..71d34f9 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -6,5 +6,6 @@ obj-y += usb/
obj-$(CONFIG_ATA) += ata/
obj-$(CONFIG_SPI) += spi/
obj-$(CONFIG_I2C) += i2c/
+obj-$(CONFIG_MCI) += mci/
obj-$(CONFIG_VIDEO) += video/
obj-y += clk/
diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
new file mode 100644
index 0000000..28f3283
--- /dev/null
+++ b/drivers/mci/Kconfig
@@ -0,0 +1,31 @@
+menuconfig MCI
+ bool "MCI drivers "
+ select ATA
+ select ATA_DISK
+ help
+ Add support for MCI drivers, used to handle MMC and SD cards
+
+if MCI
+
+comment "--- Feature list ---"
+
+config MCI_STARTUP
+ bool "Probe on system start"
+ help
+ Say 'y' here if the MCI framework should probe for attached MCI cards
+ on system start up. This is required if the card carries barebox's
+ environment (for example on systems where the MCI card is the sole
+ bootmedia). Otherwise probing run on demand with "mci*.probe=1"
+
+config MCI_INFO
+ bool "MCI Info"
+ depends on CMD_DEVINFO
+ default y
+ help
+ This entry adds more info about the attached MCI card, when the
+ 'devinfo' command is used on the mci device.
+
+comment "--- MCI host drivers ---"
+
+endif
+
diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile
new file mode 100644
index 0000000..927618f
--- /dev/null
+++ b/drivers/mci/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_MCI) += mci-core.o
diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c
new file mode 100644
index 0000000..c6d98be
--- /dev/null
+++ b/drivers/mci/mci-core.c
@@ -0,0 +1,1308 @@
+/*
+ * (C) Copyright 2010 Juergen Beisert, Pengutronix
+ *
+ * This code is havily inspred and in parts based on:
+ *
+ * Copyright 2008, Freescale Semiconductor, Inc
+ * Andy Fleming
+ *
+ * Based vaguely on the Linux code
+ *
+ * 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
+ */
+
+/* #define DEBUG */
+
+#include <init.h>
+#include <common.h>
+#include <mci.h>
+#include <malloc.h>
+#include <errno.h>
+#include <asm-generic/div64.h>
+#include <asm/byteorder.h>
+#include <ata.h>
+
+#define MAX_BUFFER_NUMBER 0xffffffff
+
+/**
+ * @file
+ * @brief Memory Card framework
+ *
+ * Checked with the following cards:
+ * - old Canon SD 16 MiB, does not like the 0x08 command (SD_CMD_SEND_IF_COND) -> failed
+ * - Kingston 512 MiB
+ * - SanDisk 512 MiB
+ * - Transcend SD Ultra, 1 GiB (Industrial)
+ */
+
+/**
+ * Call the MMC/SD instance driver to run the command on the MMC/SD card
+ * @param mci_dev MCI instance
+ * @param cmd The information about the command to run
+ * @param data The data according to the command (can be NULL)
+ * @return Driver's answer (0 on success)
+ */
+static int mci_send_cmd(struct device_d *mci_dev, struct mci_cmd *cmd, struct mci_data *data)
+{
+ struct mci_platformdata *pdata = GET_MCI_PDATA(mci_dev);
+
+ return (pdata->send_cmd)(pdata->hw_dev, cmd, data);
+}
+
+/**
+ * @param p Command definition to setup
+ * @param cmd Valid SD/MMC command (refer MMC_CMD_* / SD_CMD_*)
+ * @param arg Argument for the command (optional)
+ * @param response Command's response type (refer MMC_RSP_*)
+ *
+ * Note: When calling, the 'response' must match command's requirements
+ */
+static void mci_setup_cmd(struct mci_cmd *p, unsigned cmd, unsigned arg, unsigned response)
+{
+ p->cmdidx = cmd;
+ p->cmdarg = arg;
+ p->resp_type = response;
+}
+
+/**
+ * Setup SD/MMC card's blocklength to be used for future transmitts
+ * @param mci_dev MCI instance
+ * @param len Blocklength in bytes
+ * @return Transaction status (0 on success)
+ */
+static int mci_set_blocklen(struct device_d *mci_dev, unsigned len)
+{
+ struct mci_cmd cmd;
+
+ mci_setup_cmd(&cmd, MMC_CMD_SET_BLOCKLEN, len, MMC_RSP_R1);
+ return mci_send_cmd(mci_dev, &cmd, NULL);
+}
+
+/**
+ * Write one block of data to the card
+ * @param mci_dev MCI instance
+ * @param src Where to read from to write to the card
+ * @param blocknum Block number to write
+ * @return Transaction status (0 on success)
+ */
+static int mci_block_write(struct device_d *mci_dev, const void *src, unsigned blocknum)
+{
+ struct mci *mci = GET_MCI_DATA(mci_dev);
+ struct mci_cmd cmd;
+ struct mci_data data;
+
+ mci_setup_cmd(&cmd,
+ MMC_CMD_WRITE_SINGLE_BLOCK,
+ mci->high_capacity != 0 ? blocknum : blocknum * mci->write_bl_len,
+ MMC_RSP_R1);
+
+ data.src = src;
+ data.blocks = 1;
+ data.blocksize = mci->write_bl_len;
+ data.flags = MMC_DATA_WRITE;
+
+ return mci_send_cmd(mci_dev, &cmd, &data);
+}
+
+/**
+ * Read one block of data from the card
+ * @param mci_dev MCI instance
+ * @param dst Where to store the data read from the card
+ * @param blocknum Block number to read
+ */
+static int mci_read_block(struct device_d *mci_dev, void *dst, unsigned blocknum)
+{
+ struct mci *mci = GET_MCI_DATA(mci_dev);
+ struct mci_cmd cmd;
+ struct mci_data data;
+
+ mci_setup_cmd(&cmd,
+ MMC_CMD_READ_SINGLE_BLOCK,
+ mci->high_capacity != 0 ? blocknum : blocknum * mci->read_bl_len,
+ MMC_RSP_R1);
+
+ data.dest = dst;
+ data.blocks = 1;
+ data.blocksize = mci->read_bl_len;
+ data.flags = MMC_DATA_READ;
+
+ return mci_send_cmd(mci_dev, &cmd, &data);
+}
+
+/**
+ * Reset the attached MMC/SD card
+ * @param mci_dev MCI instance
+ * @return Transaction status (0 on success)
+ */
+static int mci_go_idle(struct device_d *mci_dev)
+{
+ struct mci_cmd cmd;
+ int err;
+
+ udelay(1000);
+
+ mci_setup_cmd(&cmd, MMC_CMD_GO_IDLE_STATE, 0, MMC_RSP_NONE);
+ err = mci_send_cmd(mci_dev, &cmd, NULL);
+
+ if (err) {
+ pr_debug("Activating IDLE state failed: %d\n", err);
+ return err;
+ }
+
+ udelay(2000); /* WTF? */
+
+ return 0;
+}
+
+/**
+ * FIXME
+ * @param mci_dev MCI instance
+ * @return Transaction status (0 on success)
+ */
+static int sd_send_op_cond(struct device_d *mci_dev)
+{
+ struct mci *mci = GET_MCI_DATA(mci_dev);
+ struct mci_platformdata *pdata = GET_MCI_PDATA(mci_dev);
+ struct mci_cmd cmd;
+ int timeout = 1000;
+ int err;
+
+ do {
+ mci_setup_cmd(&cmd, MMC_CMD_APP_CMD, 0, MMC_RSP_R1);
+ err = mci_send_cmd(mci_dev, &cmd, NULL);
+ if (err) {
+ pr_debug("Preparing SD for operating conditions failed: %d\n", err);
+ return err;
+ }
+
+ mci_setup_cmd(&cmd, SD_CMD_APP_SEND_OP_COND,
+ pdata->voltages | (mci->version == SD_VERSION_2 ? OCR_HCS : 0),
+ MMC_RSP_R3);
+ err = mci_send_cmd(mci_dev, &cmd, NULL);
+ if (err) {
+ pr_debug("SD operation condition set failed: %d\n", err);
+ return err;
+ }
+ udelay(1000);
+ } while ((!(cmd.response[0] & OCR_BUSY)) && timeout--);
+
+ if (timeout <= 0) {
+ pr_debug("SD operation condition set timed out\n");
+ return -ENODEV;
+ }
+
+ if (mci->version != SD_VERSION_2)
+ mci->version = SD_VERSION_1_0;
+
+ mci->ocr = cmd.response[0];
+
+ mci->high_capacity = ((mci->ocr & OCR_HCS) == OCR_HCS);
+ mci->rca = 0;
+
+ return 0;
+}
+
+/**
+ * Setup the operation conditions to a MultiMediaCard
+ * @param mci_dev MCI instance
+ * @return Transaction status (0 on success)
+ */
+static int mmc_send_op_cond(struct device_d *mci_dev)
+{
+ struct mci *mci = GET_MCI_DATA(mci_dev);
+ struct mci_platformdata *pdata = GET_MCI_PDATA(mci_dev);
+ struct mci_cmd cmd;
+ int timeout = 1000;
+ int err;
+
+ /* Some cards seem to need this */
+ mci_go_idle(mci_dev);
+
+ do {
+ mci_setup_cmd(&cmd, MMC_CMD_SEND_OP_COND, OCR_HCS | pdata->voltages, MMC_RSP_R3);
+ err = mci_send_cmd(mci_dev, &cmd, NULL);
+
+ if (err) {
+ pr_debug("Preparing MMC for operating conditions failed: %d\n", err);
+ return err;
+ }
+
+ udelay(1000);
+ } while (!(cmd.response[0] & OCR_BUSY) && timeout--);
+
+ if (timeout <= 0) {
+ pr_debug("SD operation condition set timed out\n");
+ return -ENODEV;
+ }
+
+ mci->version = MMC_VERSION_UNKNOWN;
+ mci->ocr = cmd.response[0];
+
+ mci->high_capacity = ((mci->ocr & OCR_HCS) == OCR_HCS);
+ mci->rca = 0;
+
+ return 0;
+}
+
+/**
+ * FIXME
+ * @param mci_dev MCI instance
+ * @param ext_csd Buffer for a 512 byte sized extended CSD
+ * @return Transaction status (0 on success)
+ *
+ * Note: Only cards newer than Version 1.1 (Physical Layer Spec) support this command
+ */
+static int mci_send_ext_csd(struct device_d *mci_dev, char *ext_csd)
+{
+ struct mci_cmd cmd;
+ struct mci_data data;
+
+ /* Get the Card Status Register */
+ mci_setup_cmd(&cmd, MMC_CMD_SEND_EXT_CSD, 0, MMC_RSP_R1);
+
+ data.dest = ext_csd;
+ data.blocks = 1;
+ data.blocksize = 512;
+ data.flags = MMC_DATA_READ;
+
+ return mci_send_cmd(mci_dev, &cmd, &data);
+}
+
+/**
+ * FIXME
+ * @param mci_dev MCI instance
+ * @param set FIXME
+ * @param index FIXME
+ * @param value FIXME
+ * @return Transaction status (0 on success)
+ */
+static int mci_switch(struct device_d *mci_dev, unsigned set, unsigned index, unsigned value)
+{
+ struct mci_cmd cmd;
+
+ mci_setup_cmd(&cmd, MMC_CMD_SWITCH,
+ (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
+ (index << 16) |
+ (value << 8),
+ MMC_RSP_R1b);
+
+ return mci_send_cmd(mci_dev, &cmd, NULL);
+}
+
+/**
+ * Change transfer frequency foa a MMC card
+ * @param mci_dev MCI instance
+ * @return Transaction status (0 on success)
+ */
+static int mmc_change_freq(struct device_d *mci_dev)
+{
+ struct mci *mci = GET_MCI_DATA(mci_dev);
+ char ext_csd[512]; /* TODO why this works on ARM, while the same in the disk layer does not??? */
+ char cardtype;
+ int err;
+
+ mci->card_caps = 0;
+
+ /* Only version 4 supports high-speed */
+ if (mci->version < MMC_VERSION_4)
+ return 0;
+
+ mci->card_caps |= MMC_MODE_4BIT;
+
+ err = mci_send_ext_csd(mci_dev, ext_csd);
+ if (err) {
+ pr_debug("Preparing for frequency setup failed: %d\n", err);
+ return err;
+ }
+
+ if (ext_csd[212] || ext_csd[213] || ext_csd[214] || ext_csd[215])
+ mci->high_capacity = 1;
+
+ cardtype = ext_csd[196] & 0xf;
+
+ err = mci_switch(mci_dev, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1);
+
+ if (err) {
+ pr_debug("MMC frequency changing failed: %d\n", err);
+ return err;
+ }
+
+ /* Now check to see that it worked */
+ err = mci_send_ext_csd(mci_dev, ext_csd);
+
+ if (err) {
+ pr_debug("Verifying frequency change failed: %d\n", err);
+ return err;
+ }
+
+ /* No high-speed support */
+ if (!ext_csd[185])
+ return 0;
+
+ /* High Speed is set, there are two types: 52MHz and 26MHz */
+ if (cardtype & MMC_HS_52MHZ)
+ mci->card_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
+ else
+ mci->card_caps |= MMC_MODE_HS;
+
+ return 0;
+}
+
+/**
+ * FIXME
+ * @param mci_dev MCI instance
+ * @param mode FIXME
+ * @param group FIXME
+ * @param value FIXME
+ * @param resp FIXME
+ * @return Transaction status (0 on success)
+ */
+static int sd_switch(struct device_d *mci_dev, unsigned mode, unsigned group, unsigned value, uint8_t *resp)
+{
+ struct mci_cmd cmd;
+ struct mci_data data;
+ unsigned arg;
+
+ arg = (mode << 31) | 0xffffff;
+ arg &= ~(0xf << (group << 2));
+ arg |= value << (group << 2);
+
+ /* Switch the frequency */
+ mci_setup_cmd(&cmd, SD_CMD_SWITCH_FUNC, arg, MMC_RSP_R1);
+
+ data.dest = resp;
+ data.blocksize = 64;
+ data.blocks = 1;
+ data.flags = MMC_DATA_READ;
+
+ return mci_send_cmd(mci_dev, &cmd, &data);
+}
+
+/**
+ * FIXME
+ * @param mci_dev MCI instance
+ * @return Transaction status (0 on success)
+ */
+static int sd_change_freq(struct device_d *mci_dev)
+{
+ struct mci *mci = GET_MCI_DATA(mci_dev);
+ struct mci_cmd cmd;
+ struct mci_data data;
+ uint32_t switch_status[16];
+ uint scr[2];
+ int timeout;
+ int err;
+
+ pr_debug("Changing transfer frequency\n");
+ mci->card_caps = 0;
+
+ /* Read the SCR to find out if this card supports higher speeds */
+ mci_setup_cmd(&cmd, MMC_CMD_APP_CMD, mci->rca << 16, MMC_RSP_R1);
+ err = mci_send_cmd(mci_dev, &cmd, NULL);
+ if (err) {
+ pr_debug("Query SD card capabilities failed: %d\n", err);
+ return err;
+ }
+
+ mci_setup_cmd(&cmd, SD_CMD_APP_SEND_SCR, 0, MMC_RSP_R1);
+
+ timeout = 3;
+
+retry_scr:
+ pr_debug("Trying to read the SCR (try %d of %d)\n", 4 - timeout, 3);
+ data.dest = (char *)&scr;
+ data.blocksize = 8;
+ data.blocks = 1;
+ data.flags = MMC_DATA_READ;
+
+ err = mci_send_cmd(mci_dev, &cmd, &data);
+ if (err) {
+ pr_debug(" Catch error (%d)", err);
+ if (timeout--) {
+ pr_debug("-- retrying\n");
+ goto retry_scr;
+ }
+ pr_debug("-- giving up\n");
+ return err;
+ }
+
+ mci->scr[0] = __be32_to_cpu(scr[0]);
+ mci->scr[1] = __be32_to_cpu(scr[1]);
+
+ switch ((mci->scr[0] >> 24) & 0xf) {
+ case 0:
+ mci->version = SD_VERSION_1_0;
+ break;
+ case 1:
+ mci->version = SD_VERSION_1_10;
+ break;
+ case 2:
+ mci->version = SD_VERSION_2;
+ break;
+ default:
+ mci->version = SD_VERSION_1_0;
+ break;
+ }
+
+ /* Version 1.0 doesn't support switching */
+ if (mci->version == SD_VERSION_1_0)
+ return 0;
+
+ timeout = 4;
+ while (timeout--) {
+ err = sd_switch(mci_dev, SD_SWITCH_CHECK, 0, 1, (uint8_t*)&switch_status);
+ if (err) {
+ pr_debug("Checking SD transfer switch frequency feature failed: %d\n", err);
+ return err;
+ }
+
+ /* The high-speed function is busy. Try again */
+ if (!(__be32_to_cpu(switch_status[7]) & SD_HIGHSPEED_BUSY))
+ break;
+ }
+
+ if (mci->scr[0] & SD_DATA_4BIT)
+ mci->card_caps |= MMC_MODE_4BIT;
+
+ /* If high-speed isn't supported, we return */
+ if (!(__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED))
+ return 0;
+
+ err = sd_switch(mci_dev, SD_SWITCH_SWITCH, 0, 1, (uint8_t*)&switch_status);
+ if (err) {
+ pr_debug("Switching SD transfer frequency failed: %d\n", err);
+ return err;
+ }
+
+ if ((__be32_to_cpu(switch_status[4]) & 0x0f000000) == 0x01000000)
+ mci->card_caps |= MMC_MODE_HS;
+
+ return 0;
+}
+
+/**
+ * Setup host's interface bus width and transfer frequency
+ * @param mci_dev MCI instance
+ */
+static void mci_set_ios(struct device_d *mci_dev)
+{
+ struct mci_platformdata *pdata = GET_MCI_PDATA(mci_dev);
+
+ (pdata->set_ios)(pdata->hw_dev, mci_dev, pdata->bus_width, pdata->clock);
+}
+
+/**
+ * Setup host's interface transfer frequency
+ * @param mci_dev MCI instance
+ * @param clock New clock in Hz to set
+ */
+static void mci_set_clock(struct device_d *mci_dev, unsigned clock)
+{
+ struct mci_platformdata *pdata = GET_MCI_PDATA(mci_dev);
+
+ /* check against any given limits */
+ if (clock > pdata->f_max)
+ clock = pdata->f_max;
+
+ if (clock < pdata->f_min)
+ clock = pdata->f_min;
+
+ pdata->clock = clock; /* the new target frequency */
+ mci_set_ios(mci_dev);
+}
+
+/**
+ * Setup host's interface bus width
+ * @param mci_dev MCI instance
+ * @param width New interface bit width (1, 4 or 8)
+ */
+static void mci_set_bus_width(struct device_d *mci_dev, unsigned width)
+{
+ struct mci_platformdata *pdata = GET_MCI_PDATA(mci_dev);
+
+ pdata->bus_width = width; /* the new target bus width */
+ mci_set_ios(mci_dev);
+}
+
+/**
+ * Extract card's version from its CSD
+ * @param mci_dev MCI instance
+ * @return 0 on success
+ */
+static void mci_detect_version_from_csd(struct device_d *mci_dev)
+{
+ struct mci *mci = GET_MCI_DATA(mci_dev);
+ int version;
+
+ if (mci->version == MMC_VERSION_UNKNOWN) {
+ /* the version is coded in the bits 127:126 (left aligned) */
+ version = (mci->csd[0] >> 26) & 0xf; /* FIXME why other width? */
+
+ switch (version) {
+ case 0:
+ printf("Detecting a 1.2 revision card\n");
+ mci->version = MMC_VERSION_1_2;
+ break;
+ case 1:
+ printf("Detecting a 1.4 revision card\n");
+ mci->version = MMC_VERSION_1_4;
+ break;
+ case 2:
+ printf("Detecting a 2.2 revision card\n");
+ mci->version = MMC_VERSION_2_2;
+ break;
+ case 3:
+ printf("Detecting a 3.0 revision card\n");
+ mci->version = MMC_VERSION_3;
+ break;
+ case 4:
+ printf("Detecting a 4.0 revision card\n");
+ mci->version = MMC_VERSION_4;
+ break;
+ default:
+ printf("Unknow revision. Falling back to a 1.2 revision card\n");
+ mci->version = MMC_VERSION_1_2;
+ break;
+ }
+ }
+}
+
+/**
+ * meaning of the encoded 'unit' bits in the CSD's field 'TRAN_SPEED'
+ * (divided by 10 to be nice to platforms without floating point)
+ */
+static const unsigned tran_speed_unit[] = {
+ [0] = 10000, /* 100 kbit/s */
+ [1] = 100000, /* 1 Mbit/s */
+ [2] = 1000000, /* 10 Mbit/s */
+ [3] = 10000000, /* 100 Mbit/s */
+ /* [4]...[7] are reserved */
+};
+
+/**
+ * meaning of the 'time' bits in the CSD's field 'TRAN_SPEED'
+ * (multiplied by 10 to be nice to platforms without floating point)
+ */
+static const unsigned char tran_speed_time[] = {
+ 0, /* reserved */
+ 10, /* 1.0 ns */
+ 12, /* 1.2 ns */
+ 13,
+ 15,
+ 20,
+ 25,
+ 30,
+ 35,
+ 40,
+ 45,
+ 50,
+ 55,
+ 60,
+ 70, /* 7.0 ns */
+ 80, /* 8.0 ns */
+};
+
+/**
+ * Extract max. transfer speed from the CSD
+ * @param mci_dev MCI instance
+ *
+ * Encoded in bit 103:96 (103: reserved, 102:99: time, 98:96 unit)
+ */
+static void mci_extract_max_tran_speed_from_csd(struct device_d *mci_dev)
+{
+ struct mci *mci = GET_MCI_DATA(mci_dev);
+ unsigned unit, time;
+
+ unit = tran_speed_unit[(mci->csd[0] & 0x7)];
+ time = tran_speed_time[((mci->csd[0] >> 3) & 0xf)];
+ if ((unit == 0) || (time == 0)) {
+ pr_warning("Unsupported 'TRAN_SPEED' unit/time value. Can't calculate card's max. transfer speed\n");
+ return;
+ }
+
+ mci->tran_speed = time * unit;
+ pr_debug("Transfer speed: %u\n", mci->tran_speed);
+}
+
+/**
+ * Extract max read and write block length from the CSD
+ * @param mci_dev MCI instance
+ *
+ * Encoded in bit 83:80 (read) and 25:22 (write)
+ */
+static void mci_extract_block_lengths_from_csd(struct device_d *mci_dev)
+{
+ struct mci *mci = GET_MCI_DATA(mci_dev);
+
+ mci->read_bl_len = 1 << ((mci->csd[1] >> 16) & 0xf);
+
+ if (IS_SD(mci))
+ mci->write_bl_len = mci->read_bl_len; /* FIXME why? */
+ else
+ mci->write_bl_len = 1 << ((mci->csd[3] >> 22) & 0xf);
+
+ pr_debug("Max. block length are: Write=%u, Read=%u Bytes\n", mci->read_bl_len, mci->write_bl_len);
+}
+
+/**
+ * Extract card's capacitiy from the CSD
+ * @param mci_dev MCI instance
+ */
+static void mci_extract_card_capacity_from_csd(struct device_d *mci_dev)
+{
+ struct mci *mci = GET_MCI_DATA(mci_dev);
+ uint64_t csize, cmult;
+
+ if (mci->high_capacity) {
+ csize = (mci->csd[1] & 0x3f) << 16 | (mci->csd[2] & 0xffff0000) >> 16;
+ cmult = 8;
+ } else {
+ csize = (mci->csd[1] & 0x3ff) << 2 | (mci->csd[2] & 0xc0000000) >> 30;
+ cmult = (mci->csd[2] & 0x00038000) >> 15;
+ }
+
+ mci->capacity = (csize + 1) << (cmult + 2);
+ mci->capacity *= mci->read_bl_len;
+ pr_debug("Capacity: %u MiB\n", (unsigned)mci->capacity >> 20);
+}
+
+/**
+ * Scan the given host interfaces and detect connected MMC/SD cards
+ * @param mci_dev MCI instance
+ * @return 0 on success, negative value else
+ */
+static int mci_startup(struct device_d *mci_dev)
+{
+ struct mci *mci = GET_MCI_DATA(mci_dev);
+ struct mci_platformdata *pdata = GET_MCI_PDATA(mci_dev);
+ struct mci_cmd cmd;
+ int err;
+
+ pr_debug("Put the Card in Identify Mode\n");
+
+ /* Put the Card in Identify Mode */
+ mci_setup_cmd(&cmd, MMC_CMD_ALL_SEND_CID, 0, MMC_RSP_R2);
+ err = mci_send_cmd(mci_dev, &cmd, NULL);
+ if (err) {
+ pr_debug("Can't bring card into identify mode: %d\n", err);
+ return err;
+ }
+
+ memcpy(mci->cid, cmd.response, 16);
+
+ pr_debug("Card's identification data is: %08X-%08X-%08X-%08X\n", mci->cid[0], mci->cid[1], mci->cid[2], mci->cid[3]);
+
+ /*
+ * For MMC cards, set the Relative Address.
+ * For SD cards, get the Relatvie Address.
+ * This also puts the cards into Standby State
+ */
+ pr_debug("Get/Set relative address\n");
+ mci_setup_cmd(&cmd, SD_CMD_SEND_RELATIVE_ADDR, mci->rca << 16, MMC_RSP_R6);
+ err = mci_send_cmd(mci_dev, &cmd, NULL);
+ if (err) {
+ pr_debug("Get/Set relative address failed: %d\n", err);
+ return err;
+ }
+
+ if (IS_SD(mci))
+ mci->rca = (cmd.response[0] >> 16) & 0xffff;
+
+ pr_debug("Get card's specific data\n");
+ /* Get the Card-Specific Data */
+ mci_setup_cmd(&cmd, MMC_CMD_SEND_CSD, mci->rca << 16, MMC_RSP_R2);
+ err = mci_send_cmd(mci_dev, &cmd, NULL);
+ if (err) {
+ pr_debug("Getting card's specific data failed: %d\n", err);
+ return err;
+ }
+
+ /* CSD is of 128 bit */
+ memcpy(mci->csd, cmd.response, 16);
+
+ pr_debug("Card's specific data is: %08X-%08X-%08X-%08X\n", mci->csd[0], mci->csd[1], mci->csd[2], mci->csd[3]);
+
+ mci_detect_version_from_csd(mci_dev);
+ mci_extract_max_tran_speed_from_csd(mci_dev);
+ mci_extract_block_lengths_from_csd(mci_dev);
+ mci_extract_card_capacity_from_csd(mci_dev);
+
+ /* sanitiy? */
+ if (mci->read_bl_len > 512) {
+ mci->read_bl_len = 512;
+ pr_warning("Limiting max. read block size down to %u\n", mci->read_bl_len);
+ }
+
+ if (mci->write_bl_len > 512) {
+ mci->write_bl_len = 512;
+ pr_warning("Limiting max. write block size down to %u\n", mci->read_bl_len);
+ }
+ pr_debug("Read block length: %u, Write block length: %u\n", mci->read_bl_len, mci->write_bl_len);
+
+ pr_debug("Select the card, and put it into Transfer Mode\n");
+ /* Select the card, and put it into Transfer Mode */
+ mci_setup_cmd(&cmd, MMC_CMD_SELECT_CARD, mci->rca << 16, MMC_RSP_R1b);
+ err = mci_send_cmd(mci_dev, &cmd, NULL);
+ if (err) {
+ pr_debug("Putting in transfer mode failed: %d\n", err);
+ return err;
+ }
+
+ if (IS_SD(mci))
+ err = sd_change_freq(mci_dev);
+ else
+ err = mmc_change_freq(mci_dev);
+
+ if (err)
+ return err;
+
+ /* Restrict card's capabilities by what the host can do */
+ mci->card_caps &= pdata->host_caps;
+
+ if (IS_SD(mci)) {
+ if (mci->card_caps & MMC_MODE_4BIT) {
+ pr_debug("Prepare for bus width change\n");
+ mci_setup_cmd(&cmd, MMC_CMD_APP_CMD, mci->rca << 16, MMC_RSP_R1);
+ err = mci_send_cmd(mci_dev, &cmd, NULL);
+ if (err) {
+ pr_debug("Preparing SD for bus width change failed: %d\n", err);
+ return err;
+ }
+
+ pr_debug("Set SD bus width to 4 bit\n");
+ mci_setup_cmd(&cmd, SD_CMD_APP_SET_BUS_WIDTH, 2, MMC_RSP_R1);
+ err = mci_send_cmd(mci_dev, &cmd, NULL);
+ if (err) {
+ pr_debug("Changing SD bus width failed: %d\n", err);
+ /* TODO continue with 1 bit? */
+ return err;
+ }
+ mci_set_bus_width(mci_dev, 4);
+ }
+ /* if possible, speed up the transfer */
+ if (mci->card_caps & MMC_MODE_HS)
+ mci_set_clock(mci_dev, 50000000);
+ else
+ mci_set_clock(mci_dev, 25000000);
+ } else {
+ if (mci->card_caps & MMC_MODE_4BIT) {
+ pr_debug("Set MMC bus width to 4 bit\n");
+ /* Set the card to use 4 bit*/
+ err = mci_switch(mci_dev, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4);
+ if (err) {
+ pr_debug("Changing MMC bus width failed: %d\n", err);
+ return err;
+ }
+ mci_set_bus_width(mci_dev, 4);
+ } else if (mci->card_caps & MMC_MODE_8BIT) {
+ pr_debug("Set MMC bus width to 8 bit\n");
+ /* Set the card to use 8 bit*/
+ err = mci_switch(mci_dev, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_8);
+ if (err) {
+ pr_debug("Changing MMC bus width failed: %d\n", err);
+ return err;
+ }
+ mci_set_bus_width(mci_dev, 8);
+ }
+ /* if possible, speed up the transfer */
+ if (mci->card_caps & MMC_MODE_HS) {
+ if (mci->card_caps & MMC_MODE_HS_52MHz)
+ mci_set_clock(mci_dev, 52000000);
+ else
+ mci_set_clock(mci_dev, 26000000);
+ } else
+ mci_set_clock(mci_dev, 20000000);
+ }
+
+ /* we setup the blocklength only one times for all accesses to this media */
+ err = mci_set_blocklen(mci_dev, mci->read_bl_len);
+
+ return err;
+}
+
+/**
+ * Detect a SD 2.0 card and enable its features
+ * @param mci_dev MCI instance
+ * @return Transfer status (0 on success)
+ *
+ * By issuing the CMD8 command SDHC/SDXC cards realize that the host supports
+ * the Physical Layer Version 2.00 or later and the card can enable
+ * corresponding new functions.
+ *
+ * If this CMD8 command will end with a timeout it is a MultiMediaCard only.
+ */
+static int sd_send_if_cond(struct device_d *mci_dev)
+{
+ struct mci *mci = GET_MCI_DATA(mci_dev);
+ struct mci_platformdata *pdata = GET_MCI_PDATA(mci_dev);
+ struct mci_cmd cmd;
+ int err;
+
+ mci_setup_cmd(&cmd, SD_CMD_SEND_IF_COND,
+ /* We set the bit if the host supports voltages between 2.7 and 3.6 V */
+ ((pdata->voltages & 0x00ff8000) != 0) << 8 | 0xaa,
+ MMC_RSP_R7);
+ err = mci_send_cmd(mci_dev, &cmd, NULL);
+ if (err) {
+ pr_debug("Query interface conditions failed: %d\n", err);
+ return err;
+ }
+
+ if ((cmd.response[0] & 0xff) != 0xaa) {
+ pr_debug("Card cannot work with hosts supply voltages\n");
+ return -EINVAL;
+ } else {
+ pr_debug("SD Card Rev. 2.00 or later detected\n");
+ mci->version = SD_VERSION_2;
+ }
+
+ return 0;
+}
+
+/* ------------------ attach to the ATA API --------------------------- */
+
+/**
+ * Write a chunk of sectors to media
+ * @param disk_dev Disk device instance
+ * @param sector_start Sector's number to start write to
+ * @param sector_count Sectors to write
+ * @param buffer Buffer to write from
+ * @return 0 on success, anything else on failure
+ *
+ * This routine expects the buffer has the correct size to read all data!
+ */
+static int mci_sd_write(struct device_d *disk_dev, uint64_t sector_start, unsigned sector_count, const void *buffer)
+{
+ struct ata_interface *intf = disk_dev->platform_data;
+ struct device_d *mci_dev = intf->priv;
+ struct mci *mci = GET_MCI_DATA(mci_dev);
+ int rc;
+
+ pr_debug("%s called: Write %u block(s), starting at %lu", __func__, sector_count, (unsigned)sector_count);
+
+ if (mci->write_bl_len != 512) {
+ pr_warning("MMC/SD block size is not 512 bytes (its %u bytes instead)\n", mci->read_bl_len);
+ return -EINVAL;
+ }
+
+ while (sector_count) {
+ /* size of the block number field in the MMC/SD command is 32 bit only */
+ if (sector_start > MAX_BUFFER_NUMBER) {
+ pr_err("Cannot handle block number %llu. Too large!\n", sector_start);
+ return -EINVAL;
+ }
+ rc = mci_block_write(mci_dev, buffer, sector_start);
+ if (rc != 0) {
+ pr_err("Writing block %u failed\n", (unsigned)sector_start);
+ return rc;
+ }
+ sector_count--;
+ buffer += mci->write_bl_len;
+ sector_start++;
+ }
+
+ return 0;
+}
+
+/**
+ * Read a chunk of sectors from media
+ * @param disk_dev Disk device instance
+ * @param sector_start Sector's number to start read from
+ * @param sector_count Sectors to read
+ * @param buffer Buffer to read into
+ * @return 0 on success, anything else on failure
+ *
+ * This routine expects the buffer has the correct size to store all data!
+ */
+static int mci_sd_read(struct device_d *disk_dev, uint64_t sector_start, unsigned sector_count, void *buffer)
+{
+ struct ata_interface *intf = disk_dev->platform_data;
+ struct device_d *mci_dev = intf->priv;
+ struct mci *mci = GET_MCI_DATA(mci_dev);
+ int rc;
+
+ pr_debug("%s called: Read %u block(s), starting at %lu to %08X\n", __func__, sector_count, (unsigned)sector_start, buffer);
+
+ if (mci->read_bl_len != 512) {
+ pr_warning("MMC/SD block size is not 512 bytes (its %u bytes instead)\n", mci->read_bl_len);
+ return -EINVAL;
+ }
+
+ while (sector_count) {
+ if (sector_start > MAX_BUFFER_NUMBER) {
+ pr_err("Cannot handle block number %lu. Too large!\n", (unsigned)sector_start);
+ return -EINVAL;
+ }
+ rc = mci_read_block(mci_dev, buffer, (unsigned)sector_start);
+ if (rc != 0) {
+ pr_err("Reading block %lu failed\n", (unsigned)sector_start);
+ return rc;
+ }
+ sector_count--;
+ buffer += mci->read_bl_len; /* FIXME always 512 bytes? */
+ sector_start++;
+ }
+
+ return 0;
+}
+
+/* ------------------ attach to the device API --------------------------- */
+
+#ifdef CONFIG_MCI_INFO
+/**
+ * Extract the Manufacturer ID from the CID
+ * @param mci Instance data
+ *
+ * The 'MID' is encoded in bit 127:120 in the CID
+ */
+static unsigned extract_mid(struct mci *mci)
+{
+ return mci->cid[0] >> 24;
+}
+
+/**
+ * Extract the OEM/Application ID from the CID
+ * @param mci Instance data
+ *
+ * The 'OID' is encoded in bit 119:104 in the CID
+ */
+static unsigned extract_oid(struct mci *mci)
+{
+ return (mci->cid[0] >> 8) & 0xffff;
+}
+
+/**
+ * Extract the product revision from the CID
+ * @param mci Instance data
+ *
+ * The 'PRV' is encoded in bit 63:56 in the CID
+ */
+static unsigned extract_prv(struct mci *mci)
+{
+ return mci->cid[2] >> 24;
+}
+
+/**
+ * Extract the product serial number from the CID
+ * @param mci Instance data
+ *
+ * The 'PSN' is encoded in bit 55:24 in the CID
+ */
+static unsigned extract_psn(struct mci *mci)
+{
+ return (mci->cid[2] << 8) | (mci->cid[3] >> 24);
+}
+
+/**
+ * Extract the month of the manufacturing date from the CID
+ * @param mci Instance data
+ *
+ * The 'MTD' is encoded in bit 19:8 in the CID, month in 11:8
+ */
+static unsigned extract_mtd_month(struct mci *mci)
+{
+ return (mci->cid[3] >> 8) & 0xf;
+}
+
+/**
+ * Extract the year of the manufacturing date from the CID
+ * @param mci Instance data
+ *
+ * The 'MTD' is encoded in bit 19:8 in the CID, year in 19:12
+ * An encoded 0 means the year 2000
+ */
+static unsigned extract_mtd_year(struct mci *mci)
+{
+ return ((mci->cid[3] >> 12) & 0xff) + 2000U;
+}
+
+/**
+ * Output some valuable information when the user runs 'devinfo' on an MCI device
+ * @param mci_dev MCI device instance
+ */
+static void mci_info(struct device_d *mci_dev)
+{
+ struct mci *mci = GET_MCI_DATA(mci_dev);
+
+ if (mci->ready_for_use == 0) {
+ printf(" No information available:\n MCI card not probed yet\n");
+ return;
+ }
+
+ printf(" Card:\n");
+ if (mci->version < SD_VERSION_SD) {
+ printf(" Attached is a MultiMediaCard (Version: %u.%u)\n", (mci->version >> 4) & 0xf, mci->version & 0xf);
+ } else {
+ printf(" Attached is an SD Card (Version: %u.%u)\n", (mci->version >> 4) & 0xf, mci->version & 0xf);
+ }
+ printf(" Capacity: %u MiB\n", (unsigned)(mci->capacity >> 20));
+
+ if (mci->high_capacity)
+ printf(" High capacity card\n");
+ printf(" CID: %08X-%08X-%08X-%08X\n", mci->cid[0], mci->cid[1], mci->cid[2], mci->cid[3]);
+ printf(" CSD: %08X-%08X-%08X-%08X\n", mci->csd[0], mci->csd[1], mci->csd[2], mci->csd[3]);
+ printf(" Max. transfer speed: %u Hz\n", mci->tran_speed);
+ printf(" Manufacturer ID: %02X\n", extract_mid(mci));
+ printf(" OEM/Application ID: %04X\n", extract_oid(mci));
+ printf(" Product name: '%c%c%c%c%c'\n", mci->cid[0] & 0xff, (mci->cid[1] >> 24), (mci->cid[1] >> 16) & 0xff,
+ (mci->cid[1] >> 8) & 0xff, mci->cid[1] & 0xff);
+ printf(" Product revision: %u.%u\n", extract_prv(mci) >> 4, extract_prv(mci) & 0xf);
+ printf(" Serial no: %0u\n", extract_psn(mci));
+ printf(" Manufacturing date: %u.%u\n", extract_mtd_month(mci), extract_mtd_year(mci));
+}
+#endif
+
+/**
+ * Check if the MCI card is already probed
+ * @param mci_dev MCI device instance
+ * @return 0 when not probed yet, -EPERM if already probed
+ *
+ * @a barebox cannot really cope with hot plugging. So, probing an attached
+ * MCI card is a one time only job. If its already done, there is no way to
+ * return.
+ */
+static int mci_check_if_already_initialized(struct device_d *mci_dev)
+{
+ struct mci *mci = GET_MCI_DATA(mci_dev);
+
+ if (mci->ready_for_use != 0)
+ return -EPERM;
+
+ return 0;
+}
+
+/**
+ * Probe an MCI card at the given host interface
+ * @param mci_dev MCI device instance
+ * @return 0 on success, negative values else
+ */
+static int mci_card_probe(struct device_d *mci_dev)
+{
+ struct mci *mci = GET_MCI_DATA(mci_dev);
+ struct mci_platformdata *pdata = GET_MCI_PDATA(mci_dev);
+ struct device_d *disk_dev;
+ struct ata_interface *p;
+ int rc;
+
+ /* start with a host interface reset */
+ rc = (pdata->init)(pdata->hw_dev, mci_dev);
+ if (rc) {
+ pr_err("Cannot reset the SD/MMC interface\n");
+ return rc;
+ }
+
+ mci_set_bus_width(mci_dev, 1);
+ mci_set_clock(mci_dev, 1); /* set the lowest available clock */
+
+ /* reset the card */
+ rc = mci_go_idle(mci_dev);
+ if (rc) {
+ pr_warning("Cannot reset the SD/MMC card\n");
+ goto on_error;
+ }
+
+ /* Check if this card can handle the "SD Card Physical Layer Specification 2.0" */
+ rc = sd_send_if_cond(mci_dev);
+ if (rc) {
+ /* If the command timed out, we check for an MMC card */
+ if (rc == -ETIMEDOUT) {
+ pr_debug("Card seems to be a MultiMediaCard\n");
+ rc = mmc_send_op_cond(mci_dev);
+ if (rc) {
+ printf("MultiMediaCard did not respond to voltage select!\n");
+ rc = -ENODEV;
+ goto on_error;
+ }
+ } else
+ goto on_error;
+ } else {
+ /* Its a 2.xx card. Setup operation conditions */
+ rc = sd_send_op_cond(mci_dev);
+ if (rc) {
+ pr_debug("Cannot setup SD card's operation condition\n");
+ goto on_error;
+ }
+ }
+
+ rc = mci_startup(mci_dev);
+ if (rc) {
+ printf("Card's startup fails with %d\n", rc);
+ goto on_error;
+ }
+
+ pr_debug("Card is up and running now, registering as a disk\n");
+ mci->ready_for_use = 1; /* TODO now or later? */
+
+ /*
+ * An MMC/SD card acts like an ordinary disk.
+ * So, re-use the disk driver to gain access to this media
+ */
+ disk_dev = xzalloc(sizeof(struct device_d) + sizeof(struct ata_interface));
+ if (disk_dev == NULL) {
+ pr_err("Out of memory when registering the MMC/SD device\n");
+ rc = -ENOMEM;
+ goto on_error;
+ }
+ p = (struct ata_interface*)&disk_dev[1];
+
+ p->write = mci_sd_write;
+ p->read = mci_sd_read;
+ p->priv = mci_dev;
+
+ strcpy(disk_dev->name, "disk");
+ disk_dev->size = mci->capacity;
+ disk_dev->map_base = 0;
+ disk_dev->platform_data = p;
+
+ register_device(disk_dev);
+
+ pr_debug("SD Card successfully added\n");
+
+on_error:
+ if (rc != 0) {
+ pdata->clock = 0; /* disable the MCI clock */
+ mci_set_ios(mci_dev);
+ }
+
+ return rc;
+}
+
+/**
+ * Trigger probing of an attached MCI card
+ * @param mci_dev MCI device instance
+ * @param param FIXME
+ * @param val "0" does nothing, a "1" will probe for a MCI card
+ * @return 0 on success
+ */
+static int mci_set_probe(struct device_d *mci_dev, struct param_d *param, const char *val)
+{
+ int rc, probe;
+
+ rc = mci_check_if_already_initialized(mci_dev);
+ if (rc != 0)
+ return rc;
+
+ probe = simple_strtoul(val, NULL, 0);
+ if (probe != 0) {
+ rc = mci_card_probe(mci_dev);
+ if (rc != 0)
+ return rc;
+ }
+
+ return dev_param_set_generic(mci_dev, param, val);
+}
+
+/**
+ * Add parameter to the MCI device on demand
+ * @param mci_dev MCI device instance
+ * @return 0 on success
+ *
+ * This parameter is only available (or usefull) if MCI card probing is delayed
+ */
+static int add_mci_parameter(struct device_d *mci_dev)
+{
+ int rc;
+
+ /* provide a 'probing right now' parameter for the user */
+ rc = dev_add_param(mci_dev, "probe", mci_set_probe, NULL, 0);
+ if (rc != 0)
+ return rc;
+
+ return dev_set_param(mci_dev, "probe", "0");
+}
+
+/**
+ * Prepare for MCI card's usage
+ * @param mci_dev MCI device instance
+ * @return 0 on success
+ *
+ * This routine will probe an attached MCI card immediately or provide
+ * a parameter to do it later on user's demand.
+ */
+static int mci_probe(struct device_d *mci_dev)
+{
+ struct mci *mci;
+ int rc;
+
+ mci = xzalloc(sizeof(struct mci));
+ mci_dev->priv = mci;
+
+#ifdef CONFIG_MCI_STARTUP
+ /* if enabled, probe the attached card immediately */
+ rc = mci_card_probe(mci_dev);
+ if (rc == -ENODEV) {
+ /*
+ * If it fails, add the 'probe' parameter to give the user
+ * a chance to insert a card and try again. Note: This may fail
+ * systems that rely on the MCI card for startup (for the
+ * persistant environment for example)
+ */
+ rc = add_mci_parameter(mci_dev);
+ if (rc != 0) {
+ pr_err("Failed to add 'probe' parameter to the MCI device\n");
+ goto on_error;
+ }
+ }
+#endif
+
+#ifndef CONFIG_MCI_STARTUP
+ /* add params on demand */
+ rc = add_mci_parameter(mci_dev);
+ if (rc != 0) {
+ pr_err("Failed to add 'probe' parameter to the MCI device\n");
+ goto on_error;
+ }
+#endif
+
+ return rc;
+
+on_error:
+ free(mci);
+ return rc;
+}
+
+static struct driver_d mci_driver = {
+ .name = "mci",
+ .probe = mci_probe,
+ .info = mci_info,
+};
+
+static int mci_init(void)
+{
+ return register_driver(&mci_driver);
+}
+
+device_initcall(mci_init);
+
+/**
+ * Create a new mci device (for convenience)
+ * @param pdata MCI device's platform data for this MCI device
+ * @return 0 on success
+ */
+int mci_register(struct mci_platformdata *pdata)
+{
+ struct device_d *mci_dev;
+
+ mci_dev = xzalloc(sizeof(struct device_d));
+
+ strcpy(mci_dev->name, mci_driver.name);
+ mci_dev->platform_data = (void*)pdata;
+
+ return register_device(mci_dev);
+}
diff --git a/include/mci.h b/include/mci.h
new file mode 100644
index 0000000..ec04f7d
--- /dev/null
+++ b/include/mci.h
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2008, Freescale Semiconductor, Inc
+ * Andy Fleming
+ *
+ * Based (loosely) on the Linux code
+ *
+ * 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
+ */
+
+#ifndef _MCI_H_
+#define _MCI_H_
+
+#include <linux/list.h>
+
+/* Firmware revisions for SD cards */
+#define SD_VERSION_SD 0x20000
+#define SD_VERSION_2 (SD_VERSION_SD | 0x20)
+#define SD_VERSION_1_0 (SD_VERSION_SD | 0x10)
+#define SD_VERSION_1_10 (SD_VERSION_SD | 0x1a)
+
+/* Firmware revisions for MMC cards */
+#define MMC_VERSION_MMC 0x10000
+#define MMC_VERSION_UNKNOWN (MMC_VERSION_MMC)
+#define MMC_VERSION_1_2 (MMC_VERSION_MMC | 0x12)
+#define MMC_VERSION_1_4 (MMC_VERSION_MMC | 0x14)
+#define MMC_VERSION_2_2 (MMC_VERSION_MMC | 0x22)
+#define MMC_VERSION_3 (MMC_VERSION_MMC | 0x30)
+#define MMC_VERSION_4 (MMC_VERSION_MMC | 0x40)
+
+#define MMC_MODE_HS 0x001
+#define MMC_MODE_HS_52MHz 0x010
+#define MMC_MODE_4BIT 0x100
+#define MMC_MODE_8BIT 0x200
+
+#define SD_DATA_4BIT 0x00040000
+
+#define IS_SD(x) (x->version & SD_VERSION_SD)
+
+#define MMC_DATA_READ 1
+#define MMC_DATA_WRITE 2
+
+/* command list */
+#define MMC_CMD_GO_IDLE_STATE 0
+#define MMC_CMD_SEND_OP_COND 1
+#define MMC_CMD_ALL_SEND_CID 2
+#define MMC_CMD_SET_RELATIVE_ADDR 3
+#define MMC_CMD_SET_DSR 4
+#define MMC_CMD_SWITCH 6
+#define MMC_CMD_SELECT_CARD 7
+#define MMC_CMD_SEND_EXT_CSD 8
+#define MMC_CMD_SEND_CSD 9
+#define MMC_CMD_SEND_CID 10
+#define MMC_CMD_STOP_TRANSMISSION 12
+#define MMC_CMD_SEND_STATUS 13
+#define MMC_CMD_SET_BLOCKLEN 16
+#define MMC_CMD_READ_SINGLE_BLOCK 17
+#define MMC_CMD_READ_MULTIPLE_BLOCK 18
+#define MMC_CMD_WRITE_SINGLE_BLOCK 24
+#define MMC_CMD_WRITE_MULTIPLE_BLOCK 25
+#define MMC_CMD_APP_CMD 55
+
+#define SD_CMD_SEND_RELATIVE_ADDR 3
+#define SD_CMD_SWITCH_FUNC 6
+#define SD_CMD_SEND_IF_COND 8
+
+#define SD_CMD_APP_SET_BUS_WIDTH 6
+#define SD_CMD_APP_SEND_OP_COND 41
+#define SD_CMD_APP_SEND_SCR 51
+
+/* SCR definitions in different words */
+#define SD_HIGHSPEED_BUSY 0x00020000
+#define SD_HIGHSPEED_SUPPORTED 0x00020000
+
+#define MMC_HS_TIMING 0x00000100
+#define MMC_HS_52MHZ 0x2
+
+#define OCR_BUSY 0x80000000
+/** card's response in its OCR if it is a high capacity card */
+#define OCR_HCS 0x40000000
+
+#define MMC_VDD_165_195 0x00000080 /* VDD voltage 1.65 - 1.95 */
+#define MMC_VDD_20_21 0x00000100 /* VDD voltage 2.0 ~ 2.1 */
+#define MMC_VDD_21_22 0x00000200 /* VDD voltage 2.1 ~ 2.2 */
+#define MMC_VDD_22_23 0x00000400 /* VDD voltage 2.2 ~ 2.3 */
+#define MMC_VDD_23_24 0x00000800 /* VDD voltage 2.3 ~ 2.4 */
+#define MMC_VDD_24_25 0x00001000 /* VDD voltage 2.4 ~ 2.5 */
+#define MMC_VDD_25_26 0x00002000 /* VDD voltage 2.5 ~ 2.6 */
+#define MMC_VDD_26_27 0x00004000 /* VDD voltage 2.6 ~ 2.7 */
+#define MMC_VDD_27_28 0x00008000 /* VDD voltage 2.7 ~ 2.8 */
+#define MMC_VDD_28_29 0x00010000 /* VDD voltage 2.8 ~ 2.9 */
+#define MMC_VDD_29_30 0x00020000 /* VDD voltage 2.9 ~ 3.0 */
+#define MMC_VDD_30_31 0x00040000 /* VDD voltage 3.0 ~ 3.1 */
+#define MMC_VDD_31_32 0x00080000 /* VDD voltage 3.1 ~ 3.2 */
+#define MMC_VDD_32_33 0x00100000 /* VDD voltage 3.2 ~ 3.3 */
+#define MMC_VDD_33_34 0x00200000 /* VDD voltage 3.3 ~ 3.4 */
+#define MMC_VDD_34_35 0x00400000 /* VDD voltage 3.4 ~ 3.5 */
+#define MMC_VDD_35_36 0x00800000 /* VDD voltage 3.5 ~ 3.6 */
+
+#define MMC_SWITCH_MODE_CMD_SET 0x00 /** Change the command set */
+#define MMC_SWITCH_MODE_SET_BITS 0x01 /** Set bits in EXT_CSD byte addressed by index which are 1 in value field */
+#define MMC_SWITCH_MODE_CLEAR_BITS 0x02 /** Clear bits in EXT_CSD byte addressed by index, which are 1 in value field */
+#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /** Set target byte to value */
+
+#define SD_SWITCH_CHECK 0
+#define SD_SWITCH_SWITCH 1
+
+/*
+ * EXT_CSD fields
+ */
+
+#define EXT_CSD_BUS_WIDTH 183 /* R/W */
+#define EXT_CSD_HS_TIMING 185 /* R/W */
+#define EXT_CSD_CARD_TYPE 196 /* RO */
+#define EXT_CSD_REV 192 /* RO */
+#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */
+
+/*
+ * EXT_CSD field definitions
+ */
+
+#define EXT_CSD_CMD_SET_NORMAL (1<<0)
+#define EXT_CSD_CMD_SET_SECURE (1<<1)
+#define EXT_CSD_CMD_SET_CPSECURE (1<<2)
+
+#define EXT_CSD_CARD_TYPE_26 (1<<0) /* Card can run at 26MHz */
+#define EXT_CSD_CARD_TYPE_52 (1<<1) /* Card can run at 52MHz */
+
+#define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */
+#define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */
+#define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */
+
+#define R1_ILLEGAL_COMMAND (1 << 22)
+#define R1_APP_CMD (1 << 5)
+
+/* response types */
+#define MMC_RSP_PRESENT (1 << 0)
+#define MMC_RSP_136 (1 << 1) /* 136 bit response */
+#define MMC_RSP_CRC (1 << 2) /* expect valid crc */
+#define MMC_RSP_BUSY (1 << 3) /* card may send busy */
+#define MMC_RSP_OPCODE (1 << 4) /* response contains opcode */
+
+#define MMC_RSP_NONE (0)
+#define MMC_RSP_R1 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+#define MMC_RSP_R1b (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY)
+#define MMC_RSP_R2 (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC)
+#define MMC_RSP_R3 (MMC_RSP_PRESENT)
+#define MMC_RSP_R4 (MMC_RSP_PRESENT)
+#define MMC_RSP_R5 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+#define MMC_RSP_R6 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+#define MMC_RSP_R7 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+
+/** command information to be sent to the SD/MMC card */
+struct mci_cmd {
+ unsigned cmdidx; /**< Command to be sent to the SD/MMC card */
+ unsigned resp_type; /**< Type of expected response, refer MMC_RSP_* macros */
+ unsigned cmdarg; /**< Command's arguments */
+ unsigned response[4]; /**< card's response */
+};
+
+/** data information to be used with some SD/MMC commands */
+struct mci_data {
+ union {
+ uint8_t *dest;
+ const uint8_t *src; /**< src buffers don't get written to */
+ };
+ unsigned flags; /**< refer MMC_DATA_* to define direction */
+ unsigned blocks; /**< block count to handle in this command */
+ unsigned blocksize; /**< block size in bytes (mostly 512) */
+};
+
+/** host information */
+struct mci_platformdata {
+ struct device_d *hw_dev; /**< the host MCI hardware device */
+ unsigned voltages;
+ unsigned host_caps; /**< Host's interface capabilities, refer MMC_VDD_* and FIXME */
+ unsigned f_min; /**< host interface lower limit */
+ unsigned f_max; /**< host interface upper limit */
+ unsigned clock; /**< Current clock used to talk to the card */
+ unsigned bus_width; /**< used data bus width to the card */
+
+ int (*init)(struct device_d*, struct device_d*); /**< init the host interface */
+ void (*set_ios)(struct device_d*, struct device_d*, unsigned, unsigned); /**< change host interface settings */
+ int (*send_cmd)(struct device_d*, struct mci_cmd*, struct mci_data*); /**< handle a command */
+};
+
+/** MMC/SD and interface instance information */
+struct mci {
+ unsigned version;
+ int high_capacity; /**< != 0 when a high capacity card is connected (OCR -> OCR_HCS) */
+ unsigned card_caps; /**< Card's capabilities */
+ unsigned ocr; /**< card's "operation condition register" */
+ unsigned scr[2];
+ unsigned csd[4]; /**< card's "card specific data register" */
+ unsigned cid[4]; /**< card's "card identification register" */
+ unsigned short rca; /* FIXME */
+ unsigned tran_speed; /**< not yet used */
+ unsigned read_bl_len; /**< currently used data block length for read accesses */
+ unsigned write_bl_len; /**< currently used data block length for write accesses */
+ uint64_t capacity; /**< Card's data capacity in bytes */
+ int ready_for_use; /** true if already probed */
+};
+
+int mci_register(struct mci_platformdata*);
+
+#define GET_HOST_DATA(x) (x->priv)
+#define GET_HOST_PDATA(x) (x->platform_data)
+#define GET_MCI_DATA(x) (x->priv)
+#define GET_MCI_PDATA(x) (x->platform_data)
+
+#endif /* _MCI_H_ */
--
1.7.2.3
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 2/3] Add i.MX23 MCI card support
2010-10-07 12:28 [RFC] Add MMC support to barebox Juergen Beisert
2010-10-07 12:28 ` [PATCH 1/3] " Juergen Beisert
@ 2010-10-07 12:28 ` Juergen Beisert
2010-10-07 12:28 ` [PATCH 3/3] Add S3C2440 " Juergen Beisert
2010-10-07 12:33 ` [RFC] Add MMC support to barebox Juergen Beisert
3 siblings, 0 replies; 6+ messages in thread
From: Juergen Beisert @ 2010-10-07 12:28 UTC (permalink / raw)
To: barebox
[-- Attachment #1: Type: text/plain, Size: 22603 bytes --]
Adding MCI card support for STM378x/i.MX23 CPUs. This is for reference only,
as this architecture is currently not part of barebox (but will coming soon).
Its tested on the i.MX23 based ChumbyOne.
Signed-off-by: Juergen Beisert <jbe@pengutronix.de>
---
drivers/mci/Kconfig | 7 +
drivers/mci/Makefile | 1 +
drivers/mci/stm378x.c | 687 +++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 695 insertions(+), 0 deletions(-)
create mode 100644 drivers/mci/stm378x.c
diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
index 28f3283..d67e383 100644
--- a/drivers/mci/Kconfig
+++ b/drivers/mci/Kconfig
@@ -27,5 +27,12 @@ config MCI_INFO
comment "--- MCI host drivers ---"
+config MCI_STM378X
+ bool "i.MX23"
+ depends on ARCH_STM
+ help
+ Enable this entry to add support to read and write SD cards on a
+ i.MX23 based system.
+
endif
diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile
index 927618f..e5e19b0 100644
--- a/drivers/mci/Makefile
+++ b/drivers/mci/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_MCI) += mci-core.o
+obj-$(CONFIG_MCI_STM378X) += stm378x.o
diff --git a/drivers/mci/stm378x.c b/drivers/mci/stm378x.c
new file mode 100644
index 0000000..a8508a4
--- /dev/null
+++ b/drivers/mci/stm378x.c
@@ -0,0 +1,687 @@
+/*
+ * Copyright (C) 2010 Juergen Beisert, Pengutronix <jbe@pengutronix.de>
+ *
+ * This code is based on:
+ *
+ * Copyright (C) 2007 SigmaTel, Inc., Ioannis Kappas <ikappas@sigmatel.com>
+ *
+ * Portions copyright (C) 2003 Russell King, PXA MMCI Driver
+ * Portions copyright (C) 2004-2005 Pierre Ossman, W83L51xD SD/MMC driver
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * @brief MCI card host interface for i.MX23 CPU
+ */
+
+/* #define DEBUG */
+
+#include <common.h>
+#include <init.h>
+#include <mci.h>
+#include <errno.h>
+#include <clock.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <mach/imx-regs.h>
+#include <mach/mci.h>
+#include <mach/clock-imx23.h>
+
+#define CLOCKRATE_MIN (1 * 1000 * 1000)
+#define CLOCKRATE_MAX (480 * 1000 * 1000)
+
+#define HW_SSP_CTRL0 0x000
+# define SSP_CTRL0_SFTRST (1 << 31)
+# define SSP_CTRL0_CLKGATE (1 << 30)
+# define SSP_CTRL0_RUN (1 << 29)
+# define SSP_CTRL0_LOCK_CS (1 << 29)
+# define SSP_CTRL0_READ (1 << 25)
+# define SSP_CTRL0_IGNORE_CRC (1 << 26)
+# define SSP_CTRL0_DATA_XFER (1 << 24)
+# define SSP_CTRL0_BUS_WIDTH(x) (((x) & 0x3) << 22)
+# define SSP_CTRL0_WAIT_FOR_IRQ (1 << 21)
+# define SSP_CTRL0_LONG_RESP (1 << 19)
+# define SSP_CTRL0_GET_RESP (1 << 17)
+# define SSP_CTRL0_ENABLE (1 << 16)
+# define SSP_CTRL0_XFER_COUNT(x) ((x) & 0xffff)
+
+#define HW_SSP_CMD0 0x010
+# define SSP_CMD0_SLOW_CLK (1 << 22)
+# define SSP_CMD0_CONT_CLK (1 << 21)
+# define SSP_CMD0_APPEND_8CYC (1 << 20)
+# define SSP_CMD0_BLOCK_SIZE(x) (((x) & 0xf) << 16)
+# define SSP_CMD0_BLOCK_COUNT(x) (((x) & 0xff) << 8)
+# define SSP_CMD0_CMD(x) ((x) & 0xff)
+
+#define HW_SSP_CMD1 0x020
+#define HW_SSP_COMPREF 0x030
+#define HW_SSP_COMPMASK 0x040
+#define HW_SSP_TIMING 0x050
+# define SSP_TIMING_TIMEOUT_MASK (0xffff0000)
+# define SSP_TIMING_TIMEOUT(x) ((x) << 16)
+# define SSP_TIMING_CLOCK_DIVIDE(x) (((x) & 0xff) << 8)
+# define SSP_TIMING_CLOCK_RATE(x) ((x) & 0xff)
+
+#define HW_SSP_CTRL1 0x060
+# define SSP_CTRL1_POLARITY (1 << 9)
+# define SSP_CTRL1_WORD_LENGTH(x) (((x) & 0xf) << 4)
+# define SSP_CTRL1_SSP_MODE(x) ((x) & 0xf)
+
+#define HW_SSP_DATA 0x070
+#define HW_SSP_SDRESP0 0x080
+#define HW_SSP_SDRESP1 0x090
+#define HW_SSP_SDRESP2 0x0A0
+#define HW_SSP_SDRESP3 0x0B0
+
+#define HW_SSP_STATUS 0x0C0
+# define SSP_STATUS_PRESENT (1 << 31)
+# define SSP_STATUS_SD_PRESENT (1 << 29)
+# define SSP_STATUS_CARD_DETECT (1 << 28)
+# define SSP_STATUS_RESP_CRC_ERR (1 << 16)
+# define SSP_STATUS_RESP_ERR (1 << 15)
+# define SSP_STATUS_RESP_TIMEOUT (1 << 14)
+# define SSP_STATUS_DATA_CRC_ERR (1 << 13)
+# define SSP_STATUS_TIMEOUT (1 << 12)
+# define SSP_STATUS_FIFO_OVRFLW (1 << 9)
+# define SSP_STATUS_FIFO_FULL (1 << 8)
+# define SSP_STATUS_FIFO_EMPTY (1 << 5)
+# define SSP_STATUS_FIFO_UNDRFLW (1 << 4)
+# define SSP_STATUS_CMD_BUSY (1 << 3)
+# define SSP_STATUS_DATA_BUSY (1 << 2)
+# define SSP_STATUS_BUSY (1 << 0)
+# define SSP_STATUS_ERROR (SSP_STATUS_FIFO_OVRFLW | SSP_STATUS_FIFO_UNDRFLW | SSP_STATUS_RESP_CRC_ERR | SSP_STATUS_RESP_ERR | SSP_STATUS_RESP_TIMEOUT | SSP_STATUS_DATA_CRC_ERR | SSP_STATUS_TIMEOUT)
+
+#define HW_SSP_DEBUG 0x100
+#define HW_SSP_VERSION 0x110
+
+struct stm_mci_host {
+ unsigned clock; /* current clock speed in Hz ("0" if disabled) */
+#ifdef CONFIG_MCI_INFO
+ unsigned f_min;
+ unsigned f_max;
+#endif
+ int bus_width:2; /* 0 = 1 bit, 1 = 4 bit, 2 = 8 bit */
+};
+
+/**
+ * Get MCI cards response if defined for the type of command
+ * @param hw_dev Host interface device instance
+ * @param cmd Command description
+ * @return Response bytes count, -EINVAL for unsupported response types
+ */
+static int get_cards_response(struct device_d *hw_dev, struct mci_cmd *cmd)
+{
+ switch (cmd->resp_type) {
+ case MMC_RSP_NONE:
+ return 0;
+
+ case MMC_RSP_R1:
+ case MMC_RSP_R1b:
+ case MMC_RSP_R3:
+ cmd->response[0] = readl(hw_dev->map_base + HW_SSP_SDRESP0);
+ return 1;
+
+ case MMC_RSP_R2:
+ cmd->response[3] = readl(hw_dev->map_base + HW_SSP_SDRESP0);
+ cmd->response[2] = readl(hw_dev->map_base + HW_SSP_SDRESP1);
+ cmd->response[1] = readl(hw_dev->map_base + HW_SSP_SDRESP2);
+ cmd->response[0] = readl(hw_dev->map_base + HW_SSP_SDRESP3);
+ return 4;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * Finish a reques to the MCI card
+ * @param hw_dev Host interface device instance
+ *
+ * Can also stop the clock to save power
+ */
+static void finish_request(struct device_d *hw_dev)
+{
+ /* stop the engines (normaly already done) */
+ writel(SSP_CTRL0_RUN, hw_dev->map_base + HW_SSP_CTRL0 + 8);
+}
+
+/**
+ * Check if the last command failed and if, why it failed
+ * @param status HW_SSP_STATUS's content
+ * @return 0 if no error, negative values else
+ */
+static int get_cmd_error(unsigned status)
+{
+ if (status & SSP_STATUS_ERROR)
+ pr_debug("Status Reg reports %08X\n", status);
+
+ if (status & SSP_STATUS_TIMEOUT) {
+ pr_debug("CMD timeout\n");
+ return -ETIMEDOUT;
+ } else if (status & SSP_STATUS_RESP_TIMEOUT) {
+ pr_debug("RESP timeout\n");
+ return -ETIMEDOUT;
+ } else if (status & SSP_STATUS_RESP_CRC_ERR) {
+ pr_debug("CMD crc error\n");
+ return -EILSEQ;
+ } else if (status & SSP_STATUS_RESP_ERR) {
+ pr_debug("RESP error\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * Define the timout for the next command
+ * @param hw_dev Host interface device instance
+ * @param to Timeout value in MCI card's bus clocks
+ */
+static void stm_setup_timout(struct device_d *hw_dev, unsigned to)
+{
+ uint32_t reg;
+
+ reg = readl(hw_dev->map_base + HW_SSP_TIMING) & ~SSP_TIMING_TIMEOUT_MASK;
+ reg |= SSP_TIMING_TIMEOUT(to);
+ writel(reg, hw_dev->map_base + HW_SSP_TIMING);
+}
+
+/**
+ * Read data from the MCI card
+ * @param hw_dev Host interface device instance
+ * @param buffer To write data into
+ * @param length Count of bytes to read (must be multiples of 4)
+ * @return 0 on success, negative values else
+ *
+ * @note This routine uses PIO to read in the data bytes from the FIFO. This
+ * may fail whith high clock speeds. If you receive -EIO errors you can try
+ * again with reduced clock speeds.
+ */
+static int read_data(struct device_d *hw_dev, void *buffer, unsigned length)
+{
+ uint32_t *p = buffer;
+
+ if (length & 0x3) {
+ pr_debug("Cannot read data sizes not multiple of 4 (request for %u detected)\n", length);
+ return -EINVAL;
+ }
+
+ while ((length != 0) && ((readl(hw_dev->map_base + HW_SSP_STATUS) & SSP_STATUS_ERROR) == 0)) {
+ /* TODO sort out FIFO overflows and emit -EOI for this case */
+ if ((readl(hw_dev->map_base + HW_SSP_STATUS) & SSP_STATUS_FIFO_EMPTY) == 0) {
+ *p = readl(hw_dev->map_base + HW_SSP_DATA);
+ p++;
+ length -= 4;
+ }
+ }
+
+ if (length == 0)
+ return 0;
+
+ return -EINVAL;
+}
+
+
+/**
+ * Write data into the MCI card
+ * @param hw_dev Host interface device instance
+ * @param buffer To read the data from
+ * @param length Count of bytes to write (must be multiples of 4)
+ * @return 0 on success, negative values else
+ *
+ * @note This routine uses PIO to write the data bytes into the FIFO. This
+ * may fail with high clock speeds. If you receive -EIO errors you can try
+ * again with reduced clock speeds.
+ */
+static int write_data(struct device_d *hw_dev, const void *buffer, unsigned length)
+{
+ const uint32_t *p = buffer;
+
+ if (length & 0x3) {
+ pr_debug("Cannot write data sizes not multiple of 4 (request for %u detected)\n", length);
+ return -EINVAL;
+ }
+
+ while ((length != 0) && ((readl(hw_dev->map_base + HW_SSP_STATUS) & SSP_STATUS_ERROR) == 0)) {
+ /* TODO sort out FIFO overflows and emit -EOI for this case */
+ if ((readl(hw_dev->map_base + HW_SSP_STATUS) & SSP_STATUS_FIFO_FULL) == 0) {
+ writel(*p, hw_dev->map_base + HW_SSP_DATA);
+ p++;
+ length -= 4;
+ }
+ }
+ if (length == 0)
+ return 0;
+
+ return -EINVAL;
+}
+
+/**
+ * Start the transaction with or without data
+ * @param hw_dev Host interface device instance
+ * @param data Data transfer description (might be NULL)
+ * @return 0 on success
+ */
+static int transfer_data(struct device_d *hw_dev, struct mci_data *data)
+{
+ unsigned length;
+
+ if (data != NULL) {
+ length = data->blocks * data->blocksize;
+#if 0
+ /*
+ * For the records: When writing data with high clock speeds it
+ * could be a good idea to fill the FIFO prior starting the
+ * transaction.
+ * But last time I tried it, it failed badly. Don't know why yet
+ */
+ if (data->flags & MMC_DATA_WRITE) {
+ err = write_data(host, data->src, 16);
+ data->src += 16;
+ length -= 16;
+ }
+#endif
+ }
+
+ /*
+ * Everything is ready for the transaction now:
+ * - transfer configuration
+ * - command and its parameters
+ *
+ * Start the transaction right now
+ */
+ writel(SSP_CTRL0_RUN, hw_dev->map_base + HW_SSP_CTRL0 + 4);
+
+ if (data != NULL) {
+ if (data->flags & MMC_DATA_READ)
+ return read_data(hw_dev, data->dest, length);
+ else
+ return write_data(hw_dev, data->src, length);
+ }
+
+ return 0;
+}
+
+/**
+ * Configure the MCI hardware for the next transaction
+ * @param cmd_flags Command information
+ * @param data_flags Data information (may be 0)
+ * @return Corresponding setting for the SSP_CTRL0 register
+ */
+static uint32_t prepare_transfer_setup(unsigned cmd_flags, unsigned data_flags)
+{
+ uint32_t reg = 0;
+
+ if (cmd_flags & MMC_RSP_PRESENT)
+ reg |= SSP_CTRL0_GET_RESP;
+ if ((cmd_flags & MMC_RSP_CRC) == 0)
+ reg |= SSP_CTRL0_IGNORE_CRC;
+ if (cmd_flags & MMC_RSP_136)
+ reg |= SSP_CTRL0_LONG_RESP;
+ if (cmd_flags & MMC_RSP_BUSY)
+ reg |= SSP_CTRL0_WAIT_FOR_IRQ; /* FIXME correct? */
+#if 0
+ if (cmd_flags & MMC_RSP_OPCODE)
+ /* TODO */
+#endif
+ if (data_flags & MMC_DATA_READ)
+ reg |= SSP_CTRL0_READ;
+
+ return reg;
+}
+
+/**
+ * Handle MCI commands without data
+ * @param hw_dev Host interface device instance
+ * @param cmd The command to handle
+ * @return 0 on success
+ *
+ * This functions handles the following MCI commands:
+ * - "broadcast command (BC)" without a response
+ * - "broadcast commands with response (BCR)"
+ * - "addressed command (AC)" with response, but without data
+ */
+static int stm_mci_std_cmds(struct device_d *hw_dev, struct mci_cmd *cmd)
+{
+ int rc;
+
+ /* setup command and transfer parameters */
+ writel(prepare_transfer_setup(cmd->resp_type, 0) |
+ /* SSP_CTRL0_BUS_WIDTH(host->bus_width) | */ /* wird im Original-Treiber nicht gemacht???? wird nur beim Datenaustausch ben�tigt (Response z�hlt nicht dazu) */
+ SSP_CTRL0_ENABLE, hw_dev->map_base + HW_SSP_CTRL0);
+
+ /* prepare the command, when no response is expected add a few trailing clocks */
+ writel(SSP_CMD0_CMD(cmd->cmdidx) |
+ (cmd->resp_type & MMC_RSP_PRESENT ? 0 : SSP_CMD0_APPEND_8CYC),
+ hw_dev->map_base + HW_SSP_CMD0);
+
+ /* prepare command's arguments */
+ writel(cmd->cmdarg, hw_dev->map_base + HW_SSP_CMD1);
+
+ stm_setup_timout(hw_dev, 0xffff);
+
+ /* start the transfer */
+ writel(SSP_CTRL0_RUN, hw_dev->map_base + HW_SSP_CTRL0 + 4);
+
+ /* wait until finished */
+ while (readl(hw_dev->map_base + HW_SSP_CTRL0) & SSP_CTRL0_RUN)
+ ;
+
+ if (cmd->resp_type & MMC_RSP_PRESENT)
+ get_cards_response(hw_dev, cmd);
+
+ return get_cmd_error(readl(hw_dev->map_base + HW_SSP_STATUS));
+}
+
+/**
+ * Handle an "addressed data transfer command " with or without data
+ * @param hw_dev Host interface device instance
+ * @param cmd The command to handle
+ * @param data The data information (buffer, direction aso.) May be NULL
+ * @return 0 on success
+ */
+static int stm_mci_adtc(struct device_d *hw_dev, struct mci_cmd *cmd, struct mci_data *data)
+{
+ struct stm_mci_host *host_data = (struct stm_mci_host*)GET_HOST_DATA(hw_dev);
+ uint32_t xfer_cnt, log2blocksize, block_cnt;
+ int err;
+
+ /* Note: 'data' can be NULL! */
+ if (data != NULL) {
+ xfer_cnt = data->blocks * data->blocksize;
+ block_cnt = data->blocks - 1; /* can be 0 */
+ log2blocksize = find_first_bit((const unsigned long*)&data->blocksize, 32);
+ } else
+ xfer_cnt = log2blocksize = block_cnt = 0;
+
+ /* setup command and transfer parameters */
+ writel(prepare_transfer_setup(cmd->resp_type, data != NULL ? data->flags : 0) |
+ SSP_CTRL0_BUS_WIDTH(host_data->bus_width) |
+ (xfer_cnt != 0 ? SSP_CTRL0_DATA_XFER : 0) | /* command plus data */
+ SSP_CTRL0_ENABLE |
+ SSP_CTRL0_XFER_COUNT(xfer_cnt), /* byte count to be transfered */
+ hw_dev->map_base + HW_SSP_CTRL0);
+
+ /* prepare the command and the transfered data count */
+ writel(SSP_CMD0_CMD(cmd->cmdidx) |
+ SSP_CMD0_BLOCK_SIZE(log2blocksize) |
+ SSP_CMD0_BLOCK_COUNT(block_cnt) |
+ (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION ? SSP_CMD0_APPEND_8CYC : 0),
+ hw_dev->map_base + HW_SSP_CMD0);
+
+ /* prepare command's arguments */
+ writel(cmd->cmdarg, hw_dev->map_base + HW_SSP_CMD1);
+
+ stm_setup_timout(hw_dev, 0xffff);
+
+ err = transfer_data(hw_dev, data);
+ if (err != 0) {
+ pr_debug(" Transfering data failed\n");
+ return err;
+ }
+
+ /* wait until finished */
+ while (readl(hw_dev->map_base + HW_SSP_CTRL0) & SSP_CTRL0_RUN)
+ ;
+
+ get_cards_response(hw_dev, cmd);
+
+ return 0;
+}
+
+
+/**
+ * @param hw_dev Host interface device instance
+ * @param nc New Clock in [Hz] (may be 0 to disable the clock)
+ * @return The real clock frequency
+ *
+ * The SSP unit clock can base on the external 24 MHz or the internal 480 MHz
+ * Its unit clock value is derived from the io clock, from the SSP divider
+ * and at least the SSP bus clock itself is derived from the SSP unit's divider
+ *
+ * @code
+ * |------------------- generic -------------|-peripheral specific-|-----all SSPs-----|-per SSP unit-|
+ * 24 MHz ----------------------------
+ * \ \
+ * \ |----| FRAC |----IO CLK----| SSP unit DIV |---| SSP DIV |--- SSP output clock
+ * \- | PLL |--- 480 MHz ---/
+ * @endcode
+ *
+ * @note Up to "SSP unit DIV" the outer world must care. This routine only
+ * handles the "SSP DIV".
+ */
+static unsigned setup_clock_speed(struct device_d *hw_dev, unsigned nc)
+{
+ unsigned ssp, div1, div2, reg;
+
+ if (nc == 0U) {
+ /* TODO stop the clock */
+ return 0;
+ }
+
+ ssp = imx_get_sspclk() * 1000;
+
+ for (div1 = 2; div1 < 255; div1 += 2) {
+ div2 = ssp / nc / div1;
+ if (div2 <= 0x100)
+ break;
+ }
+ if (div1 >= 255) {
+ pr_warning("Cannot set clock to %d Hz\n", nc);
+ return 0;
+ }
+
+ reg = readl(hw_dev->map_base + HW_SSP_TIMING) & SSP_TIMING_TIMEOUT_MASK;
+ reg |= SSP_TIMING_CLOCK_DIVIDE(div1) | SSP_TIMING_CLOCK_RATE(div2 - 1);
+ writel(reg, hw_dev->map_base + HW_SSP_TIMING);
+
+ return ssp / div1 / div2;
+}
+
+/**
+ * Reset the MCI engine (the hard way)
+ * @param hw_dev Host interface instance
+ *
+ * This will reset everything in all registers of this unit! (FIXME)
+ */
+static void stm_mci_reset(struct device_d *hw_dev)
+{
+ writel(SSP_CTRL0_SFTRST, hw_dev->map_base + HW_SSP_CTRL0 + 8);
+ while (readl(hw_dev->map_base + HW_SSP_CTRL0) & SSP_CTRL0_SFTRST)
+ ;
+}
+
+/**
+ * Initialize the engine
+ * @param hw_dev Host interface instance
+ * @param mci_dev MCI device instance
+ *
+ * @todo Initialisierung dieses Taktes und seiner Umschaltung: siehe mx23_evk.c aus u-boot
+ * der Takt muss von extern laufen, sonst geht hier nix!
+ */
+static int stm_mci_initialize(struct device_d *hw_dev, struct device_d *mci_dev)
+{
+ struct mci_platformdata *mci_pdata = GET_MCI_PDATA(mci_dev);
+ struct stm_mci_host *host_data = (struct stm_mci_host*)GET_HOST_DATA(hw_dev);
+
+ /* enable the clock to this unit to be able to reset it */
+ writel(SSP_CTRL0_CLKGATE, hw_dev->map_base + HW_SSP_CTRL0 + 8);
+
+ /* reset the unit */
+ stm_mci_reset(hw_dev);
+
+ /* restore the last settings */
+ mci_pdata->clock = host_data->clock = setup_clock_speed(hw_dev, mci_pdata->clock);
+ stm_setup_timout(hw_dev, 0xffff);
+ writel(SSP_CTRL0_IGNORE_CRC |
+ SSP_CTRL0_BUS_WIDTH(host_data->bus_width), hw_dev->map_base + HW_SSP_CTRL0);
+ writel(SSP_CTRL1_POLARITY |
+ SSP_CTRL1_SSP_MODE(3) |
+ SSP_CTRL1_WORD_LENGTH(7), hw_dev->map_base + HW_SSP_CTRL1);
+
+ return 0;
+}
+
+/* ------------------------- MCI API -------------------------------------- */
+
+/**
+ * Keep the attached MMC/SD unit in a well know state
+ * @param hw_dev Host interface instance
+ * @param mci_dev MCI device instance
+ * @return 0 on success, negative value else
+ */
+static int mci_reset(struct device_d *hw_dev, struct device_d *mci_dev)
+{
+ return stm_mci_initialize(hw_dev, mci_dev);
+}
+
+/**
+ * Process one command to the MCI card
+ * @param hw_dev Host interface instance
+ * @param cmd The command to process
+ * @param data The data to handle in the command (can be NULL)
+ * @return 0 on success, negative value else
+ */
+static int mci_request(struct device_d *hw_dev, struct mci_cmd *cmd, struct mci_data *data)
+{
+ int rc;
+
+ if ((cmd->resp_type == 0) || (data == NULL))
+ rc = stm_mci_std_cmds(hw_dev, cmd);
+ else
+ rc = stm_mci_adtc(hw_dev, cmd, data); /* with response and data */
+
+ finish_request(hw_dev); /* TODO */
+ return rc;
+}
+
+/**
+ * Setup the bus width and IO speed
+ * @param hw_dev Host interface device instance
+ * @param mci_dev MCI device instance
+ * @param bus_width New bus width value (1, 4 or 8)
+ * @param clock New clock in Hz (can be '0' to disable the clock)
+ *
+ * Drivers currently realized values are stored in MCI's platformdata
+ */
+static void mci_set_ios(struct device_d *hw_dev, struct device_d *mci_dev, unsigned bus_width, unsigned clock)
+{
+ struct stm_mci_host *host_data = (struct stm_mci_host*)GET_HOST_DATA(hw_dev);
+ struct mci_platformdata *mci_pdata = GET_MCI_PDATA(mci_dev);
+
+ switch (bus_width) {
+ case 8:
+ host_data->bus_width = 2;
+ mci_pdata->bus_width = 8; /* 8 bit is possible */
+ break;
+ case 4:
+ host_data->bus_width = 1;
+ mci_pdata->bus_width = 4; /* 4 bit is possible */
+ break;
+ default:
+ host_data->bus_width = 0;
+ mci_pdata->bus_width = 1; /* 1 bit is possible */
+ break;
+ }
+
+ mci_pdata->clock = host_data->clock = setup_clock_speed(hw_dev, clock);
+ pr_debug("IO settings: bus width=%d, frequency=%u Hz\n", mci_pdata->bus_width, mci_pdata->clock);
+}
+
+/* ----------------------------------------------------------------------- */
+
+#ifdef CONFIG_MCI_INFO
+const unsigned char bus_width[3] = { 1, 4, 8 };
+
+static void stm_info(struct device_d *hw_dev)
+{
+ struct stm_mci_host *host_data = (struct stm_mci_host*)GET_HOST_DATA(hw_dev);
+
+ printf(" Interface\n");
+ printf(" Min. bus clock: %u Hz\n", host_data->f_min);
+ printf(" Max. bus clock: %u Hz\n", host_data->f_max);
+ printf(" Current bus clock: %u Hz\n", host_data->clock);
+ printf(" Bus width: %u bit\n", bus_width[host_data->bus_width]);
+ printf("\n");
+}
+#endif
+
+static int stm_mci_probe(struct device_d *hw_dev)
+{
+ struct stm_mci_platform_data *pd = (struct stm_mci_platform_data*)hw_dev->platform_data;
+ struct stm_mci_host *host_data;
+ struct mci_platformdata *mci_pdata;
+
+ if (hw_dev->platform_data == NULL) {
+ pr_err("Missing platform data\n");
+ return -EINVAL;
+ }
+
+ mci_pdata = xzalloc(sizeof(struct stm_mci_host) + sizeof(struct mci_platformdata));
+ host_data = (struct stm_mci_host*)&mci_pdata[1];
+
+ hw_dev->priv = host_data;
+ mci_pdata->hw_dev = hw_dev;
+ mci_pdata->send_cmd = mci_request,
+ mci_pdata->set_ios = mci_set_ios,
+ mci_pdata->init = mci_reset,
+
+ /* feed forward the platform specific values */
+ mci_pdata->voltages = pd->voltages;
+ mci_pdata->host_caps = pd->caps;
+
+ if (pd->f_min == 0) {
+ mci_pdata->f_min = imx_get_sspclk() / 254U / 256U * 1000U;
+ pr_debug("Min. frequency is %u Hz\n", mci_pdata->f_min);
+ } else {
+ mci_pdata->f_min = pd->f_min;
+ pr_debug("Min. frequency is %u Hz, could be %u Hz\n", mci_pdata->f_min, imx_get_sspclk() / 254U / 256U * 1000U);
+ }
+ if (pd->f_max == 0) {
+ mci_pdata->f_max = imx_get_sspclk() / 2U / 1U * 1000U;
+ pr_debug("Max. frequency is %u Hz\n", mci_pdata->f_max);
+ } else {
+ mci_pdata->f_max = pd->f_max;
+ pr_debug("Max. frequency is %u Hz, could be %u Hz\n", mci_pdata->f_max, imx_get_sspclk() / 2U / 1U * 1000U);
+ }
+
+#ifdef CONFIG_MCI_INFO
+ host_data->f_min = mci_pdata->f_min;
+ host_data->f_max = mci_pdata->f_max;
+#endif
+
+ return mci_register(mci_pdata);
+}
+
+static struct driver_d stm_mci_driver = {
+ .name = "stm_mci",
+ .probe = stm_mci_probe,
+#ifdef CONFIG_MCI_INFO
+ .info = stm_info,
+#endif
+};
+
+static int stm_mci_init_driver(void)
+{
+ register_driver(&stm_mci_driver);
+ return 0;
+}
+
+device_initcall(stm_mci_init_driver);
--
1.7.2.3
[-- Attachment #2: Type: text/plain, Size: 149 bytes --]
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 3/3] Add S3C2440 MCI card support
2010-10-07 12:28 [RFC] Add MMC support to barebox Juergen Beisert
2010-10-07 12:28 ` [PATCH 1/3] " Juergen Beisert
2010-10-07 12:28 ` [PATCH 2/3] Add i.MX23 MCI card support Juergen Beisert
@ 2010-10-07 12:28 ` Juergen Beisert
2010-10-07 12:42 ` Belisko Marek
2010-10-07 12:33 ` [RFC] Add MMC support to barebox Juergen Beisert
3 siblings, 1 reply; 6+ messages in thread
From: Juergen Beisert @ 2010-10-07 12:28 UTC (permalink / raw)
To: barebox; +Cc: Juergen Beisert
From: Juergen Beisert <juergen@kreuzholzen.de>
Adding MCI card support for S3C2440 CPUs. This is for reference only, as there
is currently no user in the barebox tree. Maybe one with access to the A9M2440
development kit can check it on his/her system.
Checked here with my own S3C2440 based system which is not in the barebox tree.
Signed-off-by: Juergen Beisert <juergen@kreuzholzen.de>
---
arch/arm/mach-s3c24xx/include/mach/mci.h | 42 ++
drivers/mci/Kconfig | 8 +-
drivers/mci/Makefile | 1 +
drivers/mci/s3c.c | 799 ++++++++++++++++++++++++++++++
4 files changed, 849 insertions(+), 1 deletions(-)
create mode 100644 arch/arm/mach-s3c24xx/include/mach/mci.h
create mode 100644 drivers/mci/s3c.c
diff --git a/arch/arm/mach-s3c24xx/include/mach/mci.h b/arch/arm/mach-s3c24xx/include/mach/mci.h
new file mode 100644
index 0000000..fc3a4d6
--- /dev/null
+++ b/arch/arm/mach-s3c24xx/include/mach/mci.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2008, Freescale Semiconductor, Inc
+ * Andy Fleming
+ *
+ * Based (loosely) on the Linux code
+ *
+ * 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
+ */
+
+#ifndef __MACH_MMC_H_
+#define __MACH_MMC_H_
+
+struct s3c_mci_platform_data {
+ unsigned caps; /**< supported operating modes (MMC_MODE_*) */
+ unsigned voltages; /**< supported voltage range (MMC_VDD_*) */
+ unsigned f_min; /**< min operating frequency in Hz (0 -> no limit) */
+ unsigned f_max; /**< max operating frequency in Hz (0 -> no limit) */
+ /* TODO */
+ /* function to modify the voltage */
+ /* function to switch the voltage */
+ /* function to detect the presence of a SD card in the socket */
+ unsigned gpio_detect;
+ unsigned detect_invert;
+};
+
+#endif /* __MACH_MMC_H_ */
diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
index d67e383..644c0a3 100644
--- a/drivers/mci/Kconfig
+++ b/drivers/mci/Kconfig
@@ -34,5 +34,11 @@ config MCI_STM378X
Enable this entry to add support to read and write SD cards on a
i.MX23 based system.
-endif
+config MCI_S3C
+ bool "S3C"
+ depends on ARCH_S3C24xx
+ help
+ Enable this entry to add support to read and write SD cards on a
+ Samsung S3C24xx based system.
+endif
diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile
index e5e19b0..be18446 100644
--- a/drivers/mci/Makefile
+++ b/drivers/mci/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_MCI) += mci-core.o
obj-$(CONFIG_MCI_STM378X) += stm378x.o
+obj-$(CONFIG_MCI_S3C) += s3c.o
diff --git a/drivers/mci/s3c.c b/drivers/mci/s3c.c
new file mode 100644
index 0000000..d164834
--- /dev/null
+++ b/drivers/mci/s3c.c
@@ -0,0 +1,799 @@
+/*
+ * Copyright (C) 2010 Juergen Beisert <juergen@kreuzholzen.de>
+ *
+ * This code is based on various Linux and u-boot sources:
+ * Copyright (C) 2004-2006 maintech GmbH, Thomas Kleffel <tk@maintech.de>
+ * Copyright (C) 2008 Simtec Electronics <ben-linux@fluff.org>
+ * (C) Copyright 2006 by OpenMoko, Inc.
+ * Author: Harald Welte <laforge@openmoko.org>
+ * based on u-boot pxa MMC driver and linux/drivers/mmc/s3c2410mci.c
+ * (C) 2005-2005 Thomas Kleffel
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * @brief MCI card host interface for S3C2440 CPU
+ */
+
+/* #define DEBUG */
+
+#include <common.h>
+#include <init.h>
+#include <mci.h>
+#include <errno.h>
+#include <clock.h>
+#include <asm/io.h>
+#include <mach/mci.h>
+#include <mach/s3c24xx-generic.h>
+#include <mach/s3c24x0-iomap.h>
+
+#define SDICON 0x0
+# define SDICON_SDRESET (1 << 8)
+# define SDICON_MMCCLOCK (1 << 5) /* this is a clock type SD or MMC style WTF? */
+# define SDICON_BYTEORDER (1 << 4)
+# define SDICON_SDIOIRQ (1 << 3)
+# define SDICON_RWAITEN (1 << 2)
+# define SDICON_FIFORESET (1 << 1) /* reserved bit on 2440 ????? */
+# define SDICON_CLKEN (1 << 0) /* enable/disable external clock */
+
+#define SDIPRE 0x4
+
+#define SDICMDARG 0x8
+
+#define SDICMDCON 0xc
+# define SDICMDCON_ABORT (1 << 12)
+# define SDICMDCON_WITHDATA (1 << 11)
+# define SDICMDCON_LONGRSP (1 << 10)
+# define SDICMDCON_WAITRSP (1 << 9)
+# define SDICMDCON_CMDSTART (1 << 8)
+# define SDICMDCON_SENDERHOST (1 << 6)
+# define SDICMDCON_INDEX (0x3f)
+
+#define SDICMDSTAT 0x10
+# define SDICMDSTAT_CRCFAIL (1 << 12)
+# define SDICMDSTAT_CMDSENT (1 << 11)
+# define SDICMDSTAT_CMDTIMEOUT (1 << 10)
+# define SDICMDSTAT_RSPFIN (1 << 9)
+# define SDICMDSTAT_XFERING (1 << 8)
+# define SDICMDSTAT_INDEX (0xff)
+
+#define SDIRSP0 0x14
+#define SDIRSP1 0x18
+#define SDIRSP2 0x1C
+#define SDIRSP3 0x20
+
+#define SDITIMER 0x24
+#define SDIBSIZE 0x28
+
+#define SDIDCON 0x2c
+# define SDIDCON_DS_BYTE (0 << 22)
+# define SDIDCON_DS_HALFWORD (1 << 22)
+# define SDIDCON_DS_WORD (2 << 22)
+# define SDIDCON_IRQPERIOD (1 << 21)
+# define SDIDCON_TXAFTERRESP (1 << 20)
+# define SDIDCON_RXAFTERCMD (1 << 19)
+# define SDIDCON_BUSYAFTERCMD (1 << 18)
+# define SDIDCON_BLOCKMODE (1 << 17)
+# define SDIDCON_WIDEBUS (1 << 16)
+# define SDIDCON_DMAEN (1 << 15)
+# define SDIDCON_STOP (0 << 14)
+# define SDIDCON_DATSTART (1 << 14)
+# define SDIDCON_DATMODE (3 << 12)
+# define SDIDCON_BLKNUM (0xfff)
+# define SDIDCON_XFER_READY (0 << 12)
+# define SDIDCON_XFER_CHKSTART (1 << 12)
+# define SDIDCON_XFER_RXSTART (2 << 12)
+# define SDIDCON_XFER_TXSTART (3 << 12)
+
+#define SDIDCNT 0x30
+# define SDIDCNT_BLKNUM_SHIFT 12
+
+#define SDIDSTA 0x34
+# define SDIDSTA_RDYWAITREQ (1 << 10)
+# define SDIDSTA_SDIOIRQDETECT (1 << 9)
+# define SDIDSTA_FIFOFAIL (1 << 8) /* reserved on 2440 */
+# define SDIDSTA_CRCFAIL (1 << 7)
+# define SDIDSTA_RXCRCFAIL (1 << 6)
+# define SDIDSTA_DATATIMEOUT (1 << 5)
+# define SDIDSTA_XFERFINISH (1 << 4)
+# define SDIDSTA_BUSYFINISH (1 << 3)
+# define SDIDSTA_SBITERR (1 << 2) /* reserved on 2410a/2440 */
+# define SDIDSTA_TXDATAON (1 << 1)
+# define SDIDSTA_RXDATAON (1 << 0)
+
+#define SDIFSTA 0x38
+# define SDIFSTA_FIFORESET (1<<16)
+# define SDIFSTA_FIFOFAIL (3<<14) /* 3 is correct (2 bits) */
+# define SDIFSTA_TFDET (1<<13)
+# define SDIFSTA_RFDET (1<<12)
+# define SDIFSTA_TFHALF (1<<11)
+# define SDIFSTA_TFEMPTY (1<<10)
+# define SDIFSTA_RFLAST (1<<9)
+# define SDIFSTA_RFFULL (1<<8)
+# define SDIFSTA_RFHALF (1<<7)
+# define SDIFSTA_COUNTMASK (0x7f)
+
+#define SDIIMSK 0x3C
+# define SDIIMSK_RESPONSECRC (1<<17)
+# define SDIIMSK_CMDSENT (1<<16)
+# define SDIIMSK_CMDTIMEOUT (1<<15)
+# define SDIIMSK_RESPONSEND (1<<14)
+# define SDIIMSK_READWAIT (1<<13)
+# define SDIIMSK_SDIOIRQ (1<<12)
+# define SDIIMSK_FIFOFAIL (1<<11)
+# define SDIIMSK_CRCSTATUS (1<<10)
+# define SDIIMSK_DATACRC (1<<9)
+# define SDIIMSK_DATATIMEOUT (1<<8)
+# define SDIIMSK_DATAFINISH (1<<7)
+# define SDIIMSK_BUSYFINISH (1<<6)
+# define SDIIMSK_SBITERR (1<<5) /* reserved 2440/2410a */
+# define SDIIMSK_TXFIFOHALF (1<<4)
+# define SDIIMSK_TXFIFOEMPTY (1<<3)
+# define SDIIMSK_RXFIFOLAST (1<<2)
+# define SDIIMSK_RXFIFOFULL (1<<1)
+# define SDIIMSK_RXFIFOHALF (1<<0)
+
+#define SDIDATA 0x40
+
+#define CLOCKRATE_MIN (1 * 1000 * 1000)
+#define CLOCKRATE_MAX (480 * 1000 * 1000)
+
+struct s3c_mci_host {
+ int bus_width:2; /* 0 = 1 bit, 1 = 4 bit, 2 = 8 bit */
+ unsigned clock; /* current clock in Hz */
+ unsigned data_size; /* data transfer in bytes */
+};
+
+/*
+ * There is only one host MCI hardware instance available.
+ * It makes no sense to dynamically allocate this data
+ */
+static struct s3c_mci_host host_data;
+
+/**
+ * Finish a request
+ * @param hw_dev Host interface instance
+ *
+ * Just a little bit paranoia.
+ */
+static void s3c_finish_request(struct device_d *hw_dev)
+{
+ /* TODO ensure the engines are stopped */
+}
+
+/* TODO GPIO feature is required for this architecture */
+static unsigned gpio_get_value(unsigned val)
+{
+ return 0;
+}
+
+/**
+ * Detect if a card is plugged in
+ * @param hw_dev Host interface instance
+ * @return 0 if a card is plugged in
+ *
+ * Note: If there is no GPIO registered to detect if a card is present, we
+ * assume a card _is_ present.
+ */
+static int s3c_mci_card_present(struct device_d *hw_dev)
+{
+ struct s3c_mci_platform_data *pd = (struct s3c_mci_platform_data*)GET_HOST_PDATA(hw_dev);
+ int ret;
+
+ if (pd->gpio_detect == 0)
+ return 0; /* assume the card is present */
+
+ ret = gpio_get_value(pd->gpio_detect) ? 0 : 1;
+ return ret ^ pd->detect_invert;
+}
+
+/**
+ * Setup a new clock frequency on this MCI bus
+ * @param hw_dev Host interface instance
+ * @param nc New clock value in Hz (can be 0)
+ * @return New clock value (may differ from 'nc')
+ */
+static unsigned s3c_setup_clock_speed(struct device_d *hw_dev, unsigned nc)
+{
+ unsigned clock;
+ uint32_t mci_psc;
+
+ if (nc == 0)
+ return 0;
+
+ clock = s3c24xx_get_pclk();
+ /* Calculate the required prescaler value to get the requested frequency */
+ mci_psc = (clock + (nc >> 2)) / nc;
+
+ if (mci_psc > 256) {
+ mci_psc = 256;
+ pr_warning("SD/MMC clock might be too high!\n");
+ }
+
+ writel(mci_psc - 1, hw_dev->map_base + SDIPRE);
+
+ return clock / mci_psc;
+}
+
+/**
+ * Reset the MCI engine (the hard way)
+ * @param hw_dev Host interface instance
+ *
+ * This will reset everything in all registers of this unit!
+ */
+static void s3c_mci_reset(struct device_d *hw_dev)
+{
+ /* reset the hardware */
+ writel(SDICON_SDRESET, hw_dev->map_base + SDICON);
+ /* wait until reset it finished */
+ while (readl(hw_dev->map_base + SDICON) & SDICON_SDRESET)
+ ;
+}
+
+/**
+ * Initialize hard and software
+ * @param hw_dev Host interface instance
+ * @param mci_dev MCI device instance (might be NULL)
+ */
+static int s3c_mci_initialize(struct device_d *hw_dev, struct device_d *mci_dev)
+{
+ struct s3c_mci_host *host_data = (struct s3c_mci_host*)GET_HOST_DATA(hw_dev);
+
+ s3c_mci_reset(hw_dev);
+
+ /* restore last settings */
+ host_data->clock = s3c_setup_clock_speed(hw_dev, host_data->clock);
+ writel(0x007FFFFF, hw_dev->map_base + SDITIMER);
+ writel(SDICON_MMCCLOCK, hw_dev->map_base + SDICON);
+ writel(512, hw_dev->map_base + SDIBSIZE);
+
+ return 0;
+}
+
+/**
+ * Prepare engine's bits for the next command transfer
+ * @param cmd_flags MCI's command flags
+ * @param data_flags MCI's data flags
+ * @return Register bits for this transfer
+ */
+static uint32_t s3c_prepare_command_setup(unsigned cmd_flags, unsigned data_flags)
+{
+ uint32_t reg;
+
+ /* source (=host) */
+ reg = SDICMDCON_SENDERHOST;
+
+ if (cmd_flags & MMC_RSP_PRESENT) {
+ reg |= SDICMDCON_WAITRSP;
+ pr_debug("Command with response\n");
+ }
+ if (cmd_flags & MMC_RSP_136) {
+ reg |= SDICMDCON_LONGRSP;
+ pr_debug("Command with long response\n");
+ }
+ if (cmd_flags & MMC_RSP_CRC)
+ ; /* FIXME */
+ if (cmd_flags & MMC_RSP_BUSY)
+ ; /* FIXME */
+ if (cmd_flags & MMC_RSP_OPCODE)
+ ; /* FIXME */
+ if (data_flags != 0)
+ reg |= SDICMDCON_WITHDATA;
+
+ return reg;
+}
+
+/**
+ * Prepare engine's bits for the next data transfer
+ * @param hw_dev Host interface device instance
+ * @param data_flags MCI's data flags
+ * @return Register bits for this transfer
+ */
+static uint32_t s3c_prepare_data_setup(struct device_d *hw_dev, unsigned data_flags)
+{
+ struct s3c_mci_host *host_data = (struct s3c_mci_host*)GET_HOST_DATA(hw_dev);
+ uint32_t reg = SDIDCON_BLOCKMODE; /* block mode only is supported */
+
+ if (host_data->bus_width == 1)
+ reg |= SDIDCON_WIDEBUS;
+
+ /* enable any kind of data transfers on demand only */
+ if (data_flags & MMC_DATA_WRITE)
+ reg |= SDIDCON_TXAFTERRESP | SDIDCON_XFER_TXSTART;
+
+ if (data_flags & MMC_DATA_READ)
+ reg |= SDIDCON_RXAFTERCMD | SDIDCON_XFER_RXSTART;
+
+ /* TODO: Support more than the 2440 CPU */
+ reg |= SDIDCON_DS_WORD | SDIDCON_DATSTART;
+
+ return reg;
+}
+
+/**
+ * Terminate a current running transfer
+ * @param hw_dev Host interface device instance
+ * @return 0 on success
+ *
+ * Note: Try to stop a running transfer. This should not happen, as all
+ * transfers must complete in this driver. But who knows... ;-)
+ */
+static int s3c_terminate_transfer(struct device_d *hw_dev)
+{
+ unsigned stoptries = 3;
+
+ while (readl(hw_dev->map_base + SDIDSTA) & (SDIDSTA_TXDATAON | SDIDSTA_RXDATAON)) {
+ pr_debug("Transfer still in progress.\n");
+
+ writel(SDIDCON_STOP, hw_dev->map_base + SDIDCON);
+ s3c_mci_initialize(hw_dev, NULL);
+
+ if ((stoptries--) == 0) {
+ pr_warning("Cannot stop the engine!\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Setup registers for data transfer
+ * @param hw_dev Host interface device instance
+ * @param data The data information (buffer, direction aso.)
+ * @return 0 on success
+ */
+static int s3c_prepare_data_transfer(struct device_d *hw_dev, struct mci_data *data)
+{
+ uint32_t reg;
+
+ writel(data->blocksize, hw_dev->map_base + SDIBSIZE);
+ reg = s3c_prepare_data_setup(hw_dev, data->flags);
+ reg |= data->blocks & SDIDCON_BLKNUM;
+ writel(reg, hw_dev->map_base + SDIDCON);
+ writel(0x007FFFFF, hw_dev->map_base + SDITIMER);
+
+ return 0;
+}
+
+/**
+ * Send a command and receive the response
+ * @param hw_dev Host interface device instance
+ * @param cmd The command to handle
+ * @param data The data information (buffer, direction aso.)
+ * @return 0 on success
+ */
+static int s3c_send_command(struct device_d *hw_dev, struct mci_cmd *cmd, struct mci_data *data)
+{
+ uint32_t reg, t1;
+ int rc;
+
+ writel(0x007FFFFF, hw_dev->map_base + SDITIMER);
+
+ /* setup argument */
+ writel(cmd->cmdarg, hw_dev->map_base + SDICMDARG);
+
+ /* setup command and transfer characteristic */
+ reg = s3c_prepare_command_setup(cmd->resp_type, data != NULL ? data->flags : 0);
+ reg |= cmd->cmdidx & SDICMDCON_INDEX;
+
+ /* run the command right now */
+ writel(reg | SDICMDCON_CMDSTART, hw_dev->map_base + SDICMDCON);
+ t1 = readl(hw_dev->map_base + SDICMDSTAT);
+ /* wait until command is done */
+ while (1) {
+ reg = readl(hw_dev->map_base + SDICMDSTAT);
+ /* done? */
+ if (cmd->resp_type & MMC_RSP_PRESENT) {
+ if (reg & SDICMDSTAT_RSPFIN) {
+ writel(SDICMDSTAT_RSPFIN, hw_dev->map_base + SDICMDSTAT); /* required??? */
+ rc = 0;
+ break;
+ }
+ } else {
+ if (reg & SDICMDSTAT_CMDSENT) {
+ writel(SDICMDSTAT_CMDSENT, hw_dev->map_base + SDICMDSTAT); /* required??? */
+ rc = 0;
+ break;
+ }
+ }
+ /* timeout? */
+ if (reg & SDICMDSTAT_CMDTIMEOUT) {
+ writel(SDICMDSTAT_CMDTIMEOUT, hw_dev->map_base + SDICMDSTAT); /* required??? */
+ rc = -ETIMEDOUT;
+ break;
+ }
+ }
+
+ if ((rc == 0) && (cmd->resp_type & MMC_RSP_PRESENT)) {
+ cmd->response[0] = readl(hw_dev->map_base + SDIRSP0);
+ cmd->response[1] = readl(hw_dev->map_base + SDIRSP1);
+ cmd->response[2] = readl(hw_dev->map_base + SDIRSP2);
+ cmd->response[3] = readl(hw_dev->map_base + SDIRSP3);
+ }
+ /* do not disable the clock! */
+ return rc;
+}
+
+/**
+ * Clear major registers prior a new transaction
+ * @param hw_dev Host interface device instance
+ * @return 0 on success
+ *
+ * FIFO clear is only necessary on 2440, but doesn't hurt on 2410
+ */
+static int s3c_prepare_engine(struct device_d *hw_dev)
+{
+ int rc;
+
+ rc = s3c_terminate_transfer(hw_dev);
+ if (rc != 0)
+ return rc;
+
+ writel(-1, hw_dev->map_base + SDICMDSTAT);
+ writel(-1, hw_dev->map_base + SDIDSTA);
+ writel(-1, hw_dev->map_base + SDIFSTA);
+
+ return 0;
+}
+
+/**
+ * Handle MCI commands without data
+ * @param hw_dev Host interface device instance
+ * @param cmd The command to handle
+ * @return 0 on success
+ *
+ * This functions handles the following MCI commands:
+ * - "broadcast command (BC)" without a response
+ * - "broadcast commands with response (BCR)"
+ * - "addressed command (AC)" with response, but without data
+ */
+static int s3c_mci_std_cmds(struct device_d *hw_dev, struct mci_cmd *cmd)
+{
+ int rc;
+
+ rc = s3c_prepare_engine(hw_dev);
+ if (rc != 0)
+ return 0;
+
+ return s3c_send_command(hw_dev, cmd, NULL);
+}
+
+/**
+ * Read one block of data from the FIFO
+ * @param hw_dev Host interface device instance
+ * @param data The data information (buffer, direction aso.)
+ * @return 0 on success
+ */
+static int s3c_mci_read_block(struct device_d *hw_dev, struct mci_data *data)
+{
+ uint32_t *p;
+ unsigned cnt, data_size;
+
+#define READ_REASON_TO_FAIL (SDIDSTA_CRCFAIL | SDIDSTA_RXCRCFAIL | SDIDSTA_DATATIMEOUT)
+
+ p = (uint32_t*)data->dest;
+ data_size = data->blocksize * data->blocks;
+
+ while (data_size > 0) {
+
+ /* serious error? */
+ if (readl(hw_dev->map_base + SDIDSTA) & READ_REASON_TO_FAIL) {
+ pr_err("Failed while reading data\n");
+ return -EIO;
+ }
+
+ /* now check the FIFO status */
+ if (readl(hw_dev->map_base + SDIFSTA) & SDIFSTA_FIFOFAIL) {
+ pr_err("Data loss due to FIFO overflow when reading\n");
+ return -EIO;
+ }
+
+ /* we only want to read full words */
+ cnt = (readl(hw_dev->map_base + SDIFSTA) & SDIFSTA_COUNTMASK) >> 2;
+
+ /* read one chunk of data from the FIFO */
+ while (cnt--) {
+ *p = readl(hw_dev->map_base + SDIDATA);
+ p++;
+ if (data_size >= 4)
+ data_size -= 4;
+ else {
+ data_size = 0;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Write one block of data into the FIFO
+ * @param hw_dev Host interface device instance
+ * @param cmd The command to handle
+ * @param data The data information (buffer, direction aso.)
+ * @return 0 on success
+ *
+ * We must ensure data in the FIFO when the command phase changes into the
+ * data phase. To ensure this, the FIFO gets filled first, then the command.
+ */
+static int s3c_mci_write_block(struct device_d *hw_dev, struct mci_cmd *cmd, struct mci_data *data)
+{
+ const uint32_t *p = (const uint32_t*)data->src;
+ unsigned cnt, data_size;
+ uint32_t reg;
+
+#define WRITE_REASON_TO_FAIL (SDIDSTA_CRCFAIL | SDIDSTA_DATATIMEOUT)
+
+ data_size = data->blocksize * data->blocks;
+ /*
+ * With high clock rates we must fill the FIFO as early as possible
+ * Its size is 16 words. We assume its empty, when this function is
+ * entered.
+ */
+ cnt = 16;
+ while (cnt--) {
+ writel(*p, hw_dev->map_base + SDIDATA);
+ p++;
+ if (data_size >= 4)
+ data_size -= 4;
+ else {
+ data_size = 0;
+ break;
+ }
+ }
+
+ /* data is now in place and waits for transmitt. Start the command right now */
+ s3c_send_command(hw_dev, cmd, data);
+
+ if ((reg = readl(hw_dev->map_base + SDIFSTA)) & SDIFSTA_FIFOFAIL) {
+ pr_err("Command fails immediatly due to FIFO underrun when writing %08X\n", reg);
+ return -EIO;
+ }
+
+ while (data_size > 0) {
+
+ if (readl(hw_dev->map_base + SDIDSTA) & WRITE_REASON_TO_FAIL) {
+ pr_err("Failed writing data\n");
+ return -EIO;
+ }
+
+ /* now check the FIFO status */
+ if ((reg = readl(hw_dev->map_base + SDIFSTA)) & SDIFSTA_FIFOFAIL) {
+ pr_err("Data loss due to FIFO underrun when writing %08X\n", reg);
+ return -EIO;
+ }
+
+ /* we only want to write full words */
+ cnt = 16 - (((readl(hw_dev->map_base + SDIFSTA) & SDIFSTA_COUNTMASK) + 3) >> 2);
+
+ /* fill the FIFO if it has free entries */
+ while (cnt--) {
+ writel(*p, hw_dev->map_base + SDIDATA);
+ p++;
+ if (data_size >= 4)
+ data_size -= 4;
+ else {
+ data_size = 0;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Handle MCI commands with or without data
+ * @param hw_dev Host interface device instance
+ * @param cmd The command to handle
+ * @param data The data information (buffer, direction aso.)
+ * @return 0 on success
+*/
+static int s3c_mci_adtc(struct device_d *hw_dev, struct mci_cmd *cmd, struct mci_data *data)
+{
+ int rc;
+
+ rc = s3c_prepare_engine(hw_dev);
+ if (rc != 0)
+ return rc;
+
+ rc = s3c_prepare_data_transfer(hw_dev, data);
+ if (rc != 0)
+ return rc;
+
+ if (data->flags & MMC_DATA_READ) {
+ s3c_send_command(hw_dev, cmd, data);
+ rc = s3c_mci_read_block(hw_dev, data);
+ if (rc == 0) {
+ while (!(readl(hw_dev->map_base + SDIDSTA) & SDIDSTA_XFERFINISH))
+ ;
+ } else
+ s3c_terminate_transfer(hw_dev);
+ }
+
+ if (data->flags & MMC_DATA_WRITE) {
+ rc = s3c_mci_write_block(hw_dev, cmd, data);
+ if (rc == 0) {
+ while (!(readl(hw_dev->map_base + SDIDSTA) & SDIDSTA_XFERFINISH))
+ ;
+ } else
+ s3c_terminate_transfer(hw_dev);
+ }
+ writel(0, hw_dev->map_base + SDIDCON);
+
+ return rc;
+}
+
+/* ------------------------- MCI API -------------------------------------- */
+
+/**
+ * Keep the attached MMC/SD unit in a well know state
+ * @param hw_dev Host interface instance
+ * @param mci_dev MCI device instance
+ * @return 0 on success, negative value else
+ */
+static int mci_reset(struct device_d *hw_dev, struct device_d *mci_dev)
+{
+ return s3c_mci_initialize(hw_dev, mci_dev);
+}
+
+/**
+ * Process one command to the MCI card
+ * @param hw_dev Host interface instance
+ * @param cmd The command to process
+ * @param data The data to handle in the command (can be NULL)
+ * @return 0 on success, negative value else
+ */
+static int mci_request(struct device_d *hw_dev, struct mci_cmd *cmd, struct mci_data *data)
+{
+ int rc;
+
+ /* enable clock */
+ writel(readl(hw_dev->map_base + SDICON) | SDICON_CLKEN, hw_dev->map_base + SDICON);
+
+ if ((cmd->resp_type == 0) || (data == NULL))
+ rc = s3c_mci_std_cmds(hw_dev, cmd);
+ else
+ rc = s3c_mci_adtc(hw_dev, cmd, data); /* with response and data */
+
+ s3c_finish_request(hw_dev);
+
+ /* disable clock */
+ writel(readl(hw_dev->map_base + SDICON) & ~SDICON_CLKEN, hw_dev->map_base + SDICON);
+ return rc;
+}
+
+/**
+ * Setup the bus width and IO speed
+ * @param hw_dev Host interface device instance
+ * @param mci_dev MCI device instance
+ * @param bus_width New bus width value (1, 4 or 8)
+ * @param clock New clock in Hz (can be '0' to disable the clock)
+ */
+static void mci_set_ios(struct device_d *hw_dev, struct device_d *mci_dev, unsigned bus_width, unsigned clock)
+{
+ struct s3c_mci_host *host_data = (struct s3c_mci_host*)GET_HOST_DATA(hw_dev);
+ struct mci_platformdata *mci_pdata = GET_MCI_PDATA(mci_dev);
+ uint32_t reg;
+
+ switch (bus_width) {
+ case 8: /* no 8 bit support, fall back to 4 bit */
+ case 4:
+ host_data->bus_width = 1;
+ mci_pdata->bus_width = 4; /* 4 bit is possible */
+ break;
+ default:
+ host_data->bus_width = 0;
+ mci_pdata->bus_width = 1; /* 1 bit is possible */
+ break;
+ }
+
+ reg = readl(hw_dev->map_base + SDICON);
+ if (clock) {
+ /* setup the IO clock frequency and enable it */
+ mci_pdata->clock = host_data->clock = s3c_setup_clock_speed(hw_dev, clock);
+ reg |= SDICON_CLKEN; /* enable the clock */
+ } else {
+ reg &= ~SDICON_CLKEN; /* disable the clock */
+ mci_pdata->clock = host_data->clock = 0;
+ }
+ writel(reg, hw_dev->map_base + SDICON);
+
+ pr_debug("IO settings: bus width=%d, frequency=%u Hz\n", mci_pdata->bus_width, mci_pdata->clock);
+}
+
+/* ----------------------------------------------------------------------- */
+
+#ifdef CONFIG_MCI_INFO
+static void s3c_info(struct device_d *hw_dev)
+{
+ struct s3c_mci_host *host = (struct s3c_mci_host*)hw_dev->priv;
+ struct s3c_mci_platform_data *pd = (struct s3c_mci_platform_data*)hw_dev->platform_data;
+
+ printf(" Bus data width: %d bit\n", host->bus_width == 1 ? 4 : 1);
+ printf(" Bus frequency: %u Hz\n", host->clock);
+ printf(" Frequency limits: ");
+ if (pd->f_min == 0)
+ printf("no lower limit ");
+ else
+ printf("%u Hz lower limit ", pd->f_min);
+ if (pd->f_max == 0)
+ printf("- no upper limit");
+ else
+ printf("- %u Hz upper limit", pd->f_max);
+ printf("\n Card detection support: %s\n", pd->gpio_detect != 0 ? "yes" : "no");
+}
+#endif
+
+/*
+ * There is only one host MCI hardware instance available.
+ * It makes no sense to dynamically allocate this data
+ */
+static struct mci_platformdata mci_pdata = {
+ .send_cmd = mci_request,
+ .set_ios = mci_set_ios,
+ .init = mci_reset,
+};
+
+static int s3c_mci_probe(struct device_d *hw_dev)
+{
+ struct s3c_mci_platform_data *pd = (struct s3c_mci_platform_data*)hw_dev->platform_data;
+
+ /* TODO replace by the global func: enable the SDI unit clock */
+ writel(readl(S3C24X0_CLOCK_POWER_BASE + 0x0c) | 0x200,S3C24X0_CLOCK_POWER_BASE + 0x0c);
+
+ if (pd == NULL) {
+ pr_err("Missing platform data\n");
+ return -EINVAL;
+ }
+
+ hw_dev->priv = &host_data;
+ mci_pdata.hw_dev = hw_dev;
+
+ /* feed forward the platform specific values */
+ mci_pdata.voltages = pd->voltages;
+ mci_pdata.host_caps = pd->caps;
+ mci_pdata.f_min = pd->f_min == 0 ? CLOCKRATE_MIN : pd->f_min;
+ mci_pdata.f_max = pd->f_max == 0 ? CLOCKRATE_MAX : pd->f_max;
+
+ /*
+ * Start the clock to let the engine and the card finishes its startup
+ */
+ host_data.clock = s3c_setup_clock_speed(hw_dev, mci_pdata.f_min);
+ writel(SDICON_FIFORESET | SDICON_MMCCLOCK, hw_dev->map_base + SDICON);
+
+ return mci_register(&mci_pdata);
+}
+
+static struct driver_d s3c_mci_driver = {
+ .name = "s3c_mci",
+ .probe = s3c_mci_probe,
+#ifdef CONFIG_MCI_INFO
+ .info = s3c_info,
+#endif
+};
+
+static int s3c_mci_init_driver(void)
+{
+ register_driver(&s3c_mci_driver);
+ return 0;
+}
+
+device_initcall(s3c_mci_init_driver);
--
1.7.2.3
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 6+ messages in thread