* [PATCH v2] Add MCI card support to barebox
@ 2010-10-08 16:30 Juergen Beisert
2010-10-08 16:30 ` [PATCH 01/10] x-functions do not return in case of failure Juergen Beisert
` (9 more replies)
0 siblings, 10 replies; 11+ messages in thread
From: Juergen Beisert @ 2010-10-08 16:30 UTC (permalink / raw)
To: barebox
This is the second series. Hopefully it include all suggestions made.
It also include Sascha's patches related to MCI.
This patch is based on some work Sascha made already. I tried to bring in some
light into the MCI framework routines as they are based on code from another
project. Bear with me, if I didn't add useful comments all over the code. I
just wasn't able to understand what happen... If someone has more knowledge
about MCI cards, please take a look at the FIXME/TODOs and make me understand
why the code does what it does.
There is only one regular user yet: The i.MX27 based PCA100 platform. The other
platform (S3C2440 based) I did the tests on, is not part of barebox (and will not)
and the i.MX23 based platform (ChumbyOne) is not yet part of barebox.
Comments are still welcome.
Regards,
Juergen
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 01/10] x-functions do not return in case of failure.
2010-10-08 16:30 [PATCH v2] Add MCI card support to barebox Juergen Beisert
@ 2010-10-08 16:30 ` Juergen Beisert
2010-10-08 16:30 ` [PATCH 02/10] Make the disk driver less noisy Juergen Beisert
` (8 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Juergen Beisert @ 2010-10-08 16:30 UTC (permalink / raw)
To: barebox
Remove error handling, because in case of failure the xzalloc() function
does not return.
Signed-off-by: Juergen Beisert <jbe@pengutronix.de>
---
drivers/ata/disk_drive.c | 4 ----
1 files changed, 0 insertions(+), 4 deletions(-)
diff --git a/drivers/ata/disk_drive.c b/drivers/ata/disk_drive.c
index 250dada..6d5c87a 100644
--- a/drivers/ata/disk_drive.c
+++ b/drivers/ata/disk_drive.c
@@ -281,10 +281,6 @@ static int disk_probe(struct device_d *dev)
/* It seems a valuable disk. Register it */
disk_cdev = xzalloc(sizeof(struct cdev));
- if (disk_cdev == NULL) {
- dev_err(dev, "Out of memory\n");
- return -ENOMEM;
- }
/*
* BIOS based disks needs special handling. Not the driver can
--
1.7.2.3
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 02/10] Make the disk driver less noisy
2010-10-08 16:30 [PATCH v2] Add MCI card support to barebox Juergen Beisert
2010-10-08 16:30 ` [PATCH 01/10] x-functions do not return in case of failure Juergen Beisert
@ 2010-10-08 16:30 ` Juergen Beisert
2010-10-08 16:30 ` [PATCH 03/10] Don't use a sector buffer on stack Juergen Beisert
` (7 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Juergen Beisert @ 2010-10-08 16:30 UTC (permalink / raw)
To: barebox
In real life this output is only a "nice to have", but most of the time
its useless and confusing. So, make it a debug feature for the interested
developer.
Note: This is in preparation to add MCI card support, which mostly come with
a partition table and can be handled like a disk drive.
Signed-off-by: Juergen Beisert <jbe@pengutronix.de>
---
drivers/ata/disk_drive.c | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/drivers/ata/disk_drive.c b/drivers/ata/disk_drive.c
index 6d5c87a..5b22e49 100644
--- a/drivers/ata/disk_drive.c
+++ b/drivers/ata/disk_drive.c
@@ -105,7 +105,8 @@ static int disk_register_partitions(struct device_d *dev, struct partition_entry
if (table[part_order[i]].partition_size > 0x7fffff)
continue;
#endif
- dev_info(dev, "Registering partition %s to drive %s\n", partition_name, drive_name);
+ dev_dbg(dev, "Registering partition %s to drive %s\n",
+ partition_name, drive_name);
rc = devfs_add_partition(drive_name,
table[part_order[i]].partition_start * SECTOR_SIZE,
table[part_order[i]].partition_size * SECTOR_SIZE,
--
1.7.2.3
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 03/10] Don't use a sector buffer on stack
2010-10-08 16:30 [PATCH v2] Add MCI card support to barebox Juergen Beisert
2010-10-08 16:30 ` [PATCH 01/10] x-functions do not return in case of failure Juergen Beisert
2010-10-08 16:30 ` [PATCH 02/10] Make the disk driver less noisy Juergen Beisert
@ 2010-10-08 16:30 ` Juergen Beisert
2010-10-08 16:30 ` [PATCH 04/10] Don't try to guess the size of a disk if its size value is already given Juergen Beisert
` (6 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Juergen Beisert @ 2010-10-08 16:30 UTC (permalink / raw)
To: barebox
Using a temp. buffer for a disk sector on the stack, seems not work. Doing so
lets the system run crazy (the stack seems to be destroyd). Don't know the
correct stack handling on ARM, but (IMHO) I also can exclude any writing across
the buffer boundaries.
Using a temp. buffer via malloc() runs also on ARM.
Note: This patch was required to add MCI card support, which can be handled
like a disk drive.
Signed-off-by: Juergen Beisert <jbe@pengutronix.de>
---
drivers/ata/disk_drive.c | 17 ++++++++++++-----
1 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/drivers/ata/disk_drive.c b/drivers/ata/disk_drive.c
index 5b22e49..15ef5f4 100644
--- a/drivers/ata/disk_drive.c
+++ b/drivers/ata/disk_drive.c
@@ -34,6 +34,7 @@
#include <errno.h>
#include <string.h>
#include <linux/kernel.h>
+#include <malloc.h>
/**
* Description of one partition table entry (D*S type)
@@ -269,15 +270,18 @@ static struct file_operations disk_ops = {
*/
static int disk_probe(struct device_d *dev)
{
- uint8_t sector[512];
+ uint8_t *sector;
int rc;
struct ata_interface *intf = dev->platform_data;
struct cdev *disk_cdev;
+ sector = xmalloc(SECTOR_SIZE);
+
rc = intf->read(dev, 0, 1, sector);
if (rc != 0) {
dev_err(dev, "Cannot read MBR of this device\n");
- return -1;
+ rc = -ENODEV;
+ goto on_error;
}
/* It seems a valuable disk. Register it */
@@ -306,7 +310,8 @@ static int disk_probe(struct device_d *dev)
if ((sector[510] != 0x55) || (sector[511] != 0xAA)) {
dev_info(dev, "No partition table found\n");
- return 0;
+ rc = 0;
+ goto on_error;
}
/* guess the size of this drive */
@@ -314,9 +319,11 @@ static int disk_probe(struct device_d *dev)
dev_info(dev, "Drive size guessed to %u kiB\n", dev->size / 1024);
disk_cdev->size = dev->size;
- disk_register_partitions(dev, (struct partition_entry*)§or[446]);
+ rc = disk_register_partitions(dev, (struct partition_entry*)§or[446]);
- return 0;
+on_error:
+ free(sector);
+ return rc;
}
#ifdef CONFIG_ATA_BIOS
--
1.7.2.3
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 04/10] Don't try to guess the size of a disk if its size value is already given
2010-10-08 16:30 [PATCH v2] Add MCI card support to barebox Juergen Beisert
` (2 preceding siblings ...)
2010-10-08 16:30 ` [PATCH 03/10] Don't use a sector buffer on stack Juergen Beisert
@ 2010-10-08 16:30 ` Juergen Beisert
2010-10-08 16:30 ` [PATCH 05/10] Add MCI card support to barebox Juergen Beisert
` (5 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Juergen Beisert @ 2010-10-08 16:30 UTC (permalink / raw)
To: barebox
Guessing the size of an attached harddisk (access via x86 BIOS) was needed
due to the fact, barebox can't query this information from the BIOS easily.
But with the SD/MMC cards, there will be a second user of the generic disk
handling routines. And with this media it is very easy to know its size.
This patch provides a workaround to keep the guessing feature if the size of
the registered disk is 0. If it is not 0, the given value will be used instead.
Note: This is in preparation to add MCI card support, which can be handled
like a disk drive.
Signed-off-by: Juergen Beisert <jbe@pengutronix.de>
---
arch/x86/boards/x86_generic/generic_pc.c | 2 +-
drivers/ata/disk_drive.c | 27 ++++++++++++++++++---------
2 files changed, 19 insertions(+), 10 deletions(-)
diff --git a/arch/x86/boards/x86_generic/generic_pc.c b/arch/x86/boards/x86_generic/generic_pc.c
index a6cd7e0..b9c31aa 100644
--- a/arch/x86/boards/x86_generic/generic_pc.c
+++ b/arch/x86/boards/x86_generic/generic_pc.c
@@ -46,7 +46,7 @@ static struct device_d sdram_dev = {
static struct device_d bios_disk_dev = {
.id = -1,
.name = "biosdrive",
- .size = 1,
+ .size = 0, /* auto guess */
};
/*
diff --git a/drivers/ata/disk_drive.c b/drivers/ata/disk_drive.c
index 15ef5f4..a54429a 100644
--- a/drivers/ata/disk_drive.c
+++ b/drivers/ata/disk_drive.c
@@ -23,6 +23,7 @@
* @brief Generic disk drive support
*
* @todo Support for disks larger than 4 GiB
+ * @todo Reliable size detection for BIOS based disks (on x86 only)
*/
#include <stdio.h>
@@ -299,11 +300,16 @@ static int disk_probe(struct device_d *dev)
else
#endif
disk_cdev->name = asprintf("disk%d", dev->id);
- /**
- * @todo we need the size of the drive, else its nearly impossible
- * to do anything with it (at least with the generic routines)
- */
- disk_cdev->size = 32; /* FIXME */
+
+ /* On x86, BIOS based disks are coming without a valid .size field */
+ if (dev->size == 0) {
+ /*
+ * We need always the size of the drive, else its nearly impossible
+ * to do anything with it (at least with the generic routines)
+ */
+ disk_cdev->size = 32;
+ } else
+ disk_cdev->size = dev->size;
disk_cdev->ops = &disk_ops;
disk_cdev->dev = dev;
devfs_create(disk_cdev);
@@ -314,10 +320,13 @@ static int disk_probe(struct device_d *dev)
goto on_error;
}
- /* guess the size of this drive */
- dev->size = disk_guess_size(dev, (struct partition_entry*)§or[446]) * SECTOR_SIZE;
- dev_info(dev, "Drive size guessed to %u kiB\n", dev->size / 1024);
- disk_cdev->size = dev->size;
+ if (dev->size == 0) {
+ /* guess the size of this drive if not otherwise given */
+ dev->size = disk_guess_size(dev,
+ (struct partition_entry*)§or[446]) * SECTOR_SIZE;
+ dev_info(dev, "Drive size guessed to %u kiB\n", dev->size / 1024);
+ disk_cdev->size = dev->size;
+ }
rc = disk_register_partitions(dev, (struct partition_entry*)§or[446]);
--
1.7.2.3
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 05/10] Add MCI card support to barebox
2010-10-08 16:30 [PATCH v2] Add MCI card support to barebox Juergen Beisert
` (3 preceding siblings ...)
2010-10-08 16:30 ` [PATCH 04/10] Don't try to guess the size of a disk if its size value is already given Juergen Beisert
@ 2010-10-08 16:30 ` Juergen Beisert
2010-10-08 16:30 ` [PATCH 06/10] Add i.MX23 MCI card support Juergen Beisert
` (4 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Juergen Beisert @ 2010-10-08 16:30 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 | 30 ++
drivers/mci/Makefile | 1 +
drivers/mci/mci-core.c | 1333 ++++++++++++++++++++++++++++++++++++++++++++++++
include/mci.h | 239 +++++++++
6 files changed, 1605 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..0abadb3
--- /dev/null
+++ b/drivers/mci/Kconfig
@@ -0,0 +1,30 @@
+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..f961e46
--- /dev/null
+++ b/drivers/mci/mci-core.c
@@ -0,0 +1,1333 @@
+/*
+ * (C) Copyright 2010 Juergen Beisert, Pengutronix
+ *
+ * This code is havily inspired and in parts from the u-boot project:
+ *
+ * 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_host *host = GET_MCI_PDATA(mci_dev);
+
+ return host->send_cmd(host, 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_host *host = 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,
+ host->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_host *host = 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 |
+ host->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 for an 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];
+ 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);
+}
+
+/**
+ * Change transfer frequency for an SD card
+ * @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_host *host = GET_MCI_PDATA(mci_dev);
+
+ host->set_ios(host, mci_dev, host->bus_width, host->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_host *host = GET_MCI_PDATA(mci_dev);
+
+ /* check against any given limits */
+ if (clock > host->f_max)
+ clock = host->f_max;
+
+ if (clock < host->f_min)
+ clock = host->f_min;
+
+ host->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_host *host = GET_MCI_PDATA(mci_dev);
+
+ host->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_host *host = 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 &= host->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_host *host = 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 */
+ ((host->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;
+ 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_host *host = GET_MCI_PDATA(mci_dev);
+ struct device_d *disk_dev;
+ struct ata_interface *p;
+ int rc;
+
+ /* start with a host interface reset */
+ rc = (host->init)(host, 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) {
+ pr_err("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));
+ 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) {
+ host->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_host *host)
+{
+ struct device_d *mci_dev;
+
+ mci_dev = xzalloc(sizeof(struct device_d));
+
+ strcpy(mci_dev->name, mci_driver.name);
+ mci_dev->platform_data = (void*)host;
+
+ return register_device(mci_dev);
+}
diff --git a/include/mci.h b/include/mci.h
new file mode 100644
index 0000000..8c669ca
--- /dev/null
+++ b/include/mci.h
@@ -0,0 +1,239 @@
+/*
+ * (C) Copyright 2010 Juergen Beisert, Pengutronix
+ *
+ * This code is partially based on u-boot code:
+ *
+ * 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 */
+ /** Set bits in EXT_CSD byte addressed by index which are 1 in value field */
+#define MMC_SWITCH_MODE_SET_BITS 0x01
+ /** Clear bits in EXT_CSD byte addressed by index, which are 1 in value field */
+#define MMC_SWITCH_MODE_CLEAR_BITS 0x02
+ /** Set target byte to value */
+#define MMC_SWITCH_MODE_WRITE_BYTE 0x03
+
+#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_host {
+ struct device_d *hw_dev; /**< the host MCI hardware device */
+ unsigned voltages;
+ unsigned host_caps; /**< Host's interface capabilities, refer MMC_VDD_* */
+ 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 */
+
+ /** init the host interface */
+ int (*init)(struct mci_host*, struct device_d*);
+ /** change host interface settings */
+ void (*set_ios)(struct mci_host*, struct device_d*, unsigned, unsigned);
+ /** handle a command */
+ int (*send_cmd)(struct mci_host*, struct mci_cmd*, struct mci_data*);
+};
+
+/** MMC/SD and interface instance information */
+struct mci {
+ unsigned version;
+ /** != 0 when a high capacity card is connected (OCR -> OCR_HCS) */
+ int high_capacity;
+ 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 */
+ /** currently used data block length for read accesses */
+ unsigned read_bl_len;
+ /** currently used data block length for write accesses */
+ unsigned write_bl_len;
+ uint64_t capacity; /**< Card's data capacity in bytes */
+ int ready_for_use; /** true if already probed */
+};
+
+int mci_register(struct mci_host*);
+
+#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] 11+ messages in thread
* [PATCH 06/10] Add i.MX23 MCI card support
2010-10-08 16:30 [PATCH v2] Add MCI card support to barebox Juergen Beisert
` (4 preceding siblings ...)
2010-10-08 16:30 ` [PATCH 05/10] Add MCI card support to barebox Juergen Beisert
@ 2010-10-08 16:30 ` Juergen Beisert
2010-10-08 16:30 ` [PATCH 07/10] Add S3C2440 " Juergen Beisert
` (3 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Juergen Beisert @ 2010-10-08 16:30 UTC (permalink / raw)
To: barebox
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 | 699 +++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 707 insertions(+), 0 deletions(-)
create mode 100644 drivers/mci/stm378x.c
diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
index 0abadb3..a24ae6d 100644
--- a/drivers/mci/Kconfig
+++ b/drivers/mci/Kconfig
@@ -27,4 +27,11 @@ 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..fbb9994
--- /dev/null
+++ b/drivers/mci/stm378x.c
@@ -0,0 +1,699 @@
+/*
+ * 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.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 request 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)
+{
+ /* setup command and transfer parameters */
+ writel(prepare_transfer_setup(cmd->resp_type, 0) |
+ 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
+ */
+static int stm_mci_initialize(struct device_d *hw_dev, struct device_d *mci_dev)
+{
+ struct mci_host *host = 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 */
+ host->clock = host_data->clock = setup_clock_speed(hw_dev, host->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 mci_pdata MCI platform data
+ * @param mci_dev MCI device instance
+ * @return 0 on success, negative value else
+ */
+static int mci_reset(struct mci_host *mci_pdata, struct device_d *mci_dev)
+{
+ struct device_d *hw_dev = mci_pdata->hw_dev;
+
+ return stm_mci_initialize(hw_dev, mci_dev);
+}
+
+/**
+ * Process one command to the MCI card
+ * @param mci_pdata MCI platform data
+ * @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 mci_host *mci_pdata, struct mci_cmd *cmd,
+ struct mci_data *data)
+{
+ struct device_d *hw_dev = mci_pdata->hw_dev;
+ 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 mci_pdata MCI platform data
+ * @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 mci_host *mci_pdata, struct device_d *mci_dev,
+ unsigned bus_width, unsigned clock)
+{
+ struct device_d *hw_dev = mci_pdata->hw_dev;
+ struct stm_mci_host *host_data = (struct stm_mci_host*)GET_HOST_DATA(hw_dev);
+ struct mci_host *host = GET_MCI_PDATA(mci_dev);
+
+ switch (bus_width) {
+ case 8:
+ host_data->bus_width = 2;
+ host->bus_width = 8; /* 8 bit is possible */
+ break;
+ case 4:
+ host_data->bus_width = 1;
+ host->bus_width = 4; /* 4 bit is possible */
+ break;
+ default:
+ host_data->bus_width = 0;
+ host->bus_width = 1; /* 1 bit is possible */
+ break;
+ }
+
+ host->clock = host_data->clock = setup_clock_speed(hw_dev, clock);
+ pr_debug("IO settings: bus width=%d, frequency=%u Hz\n", host->bus_width,
+ host->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 = 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 = hw_dev->platform_data;
+ struct stm_mci_host *host_data;
+ struct mci_host *host;
+
+ if (hw_dev->platform_data == NULL) {
+ pr_err("Missing platform data\n");
+ return -EINVAL;
+ }
+
+ host = xzalloc(sizeof(struct stm_mci_host) + sizeof(struct mci_host));
+ host_data = (struct stm_mci_host*)&host[1];
+
+ hw_dev->priv = host_data;
+ host->hw_dev = hw_dev;
+ host->send_cmd = mci_request,
+ host->set_ios = mci_set_ios,
+ host->init = mci_reset,
+
+ /* feed forward the platform specific values */
+ host->voltages = pd->voltages;
+ host->host_caps = pd->caps;
+
+ if (pd->f_min == 0) {
+ host->f_min = imx_get_sspclk() / 254U / 256U * 1000U;
+ pr_debug("Min. frequency is %u Hz\n", host->f_min);
+ } else {
+ host->f_min = pd->f_min;
+ pr_debug("Min. frequency is %u Hz, could be %u Hz\n",
+ host->f_min, imx_get_sspclk() / 254U / 256U * 1000U);
+ }
+ if (pd->f_max == 0) {
+ host->f_max = imx_get_sspclk() / 2U / 1U * 1000U;
+ pr_debug("Max. frequency is %u Hz\n", host->f_max);
+ } else {
+ host->f_max = pd->f_max;
+ pr_debug("Max. frequency is %u Hz, could be %u Hz\n",
+ host->f_max, imx_get_sspclk() / 2U / 1U * 1000U);
+ }
+
+#ifdef CONFIG_MCI_INFO
+ host_data->f_min = host->f_min;
+ host_data->f_max = host->f_max;
+#endif
+
+ return mci_register(host);
+}
+
+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
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 07/10] Add S3C2440 MCI card support
2010-10-08 16:30 [PATCH v2] Add MCI card support to barebox Juergen Beisert
` (5 preceding siblings ...)
2010-10-08 16:30 ` [PATCH 06/10] Add i.MX23 MCI card support Juergen Beisert
@ 2010-10-08 16:30 ` Juergen Beisert
2010-10-08 16:30 ` [PATCH 08/10] i.MX27: Add mmc clock support in order to add an i.MX MCI driver Juergen Beisert
` (2 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Juergen Beisert @ 2010-10-08 16:30 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 | 46 ++
drivers/mci/Kconfig | 7 +
drivers/mci/Makefile | 1 +
drivers/mci/s3c.c | 817 ++++++++++++++++++++++++++++++
4 files changed, 871 insertions(+), 0 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..6ba8961
--- /dev/null
+++ b/arch/arm/mach-s3c24xx/include/mach/mci.h
@@ -0,0 +1,46 @@
+/*
+ * (C) Copyright 2010 Juergen Beisert, Pengutronix
+ *
+ * This code is partially based on u-boot code:
+ *
+ * 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 a24ae6d..644c0a3 100644
--- a/drivers/mci/Kconfig
+++ b/drivers/mci/Kconfig
@@ -34,4 +34,11 @@ config MCI_STM378X
Enable this entry to add support to read and write SD cards on a
i.MX23 based system.
+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..9810683
--- /dev/null
+++ b/drivers/mci/s3c.c
@@ -0,0 +1,817 @@
+/*
+ * Copyright (C) 2010 Juergen Beisert <juergen@kreuzholzen.de>
+ *
+ * This code is partially based on u-boot code:
+ *
+ * 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
+
+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 = 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 = 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);
+ rc = 0;
+ break;
+ }
+ } else {
+ if (reg & SDICMDSTAT_CMDSENT) {
+ writel(SDICMDSTAT_CMDSENT,
+ hw_dev->map_base + SDICMDSTAT);
+ rc = 0;
+ break;
+ }
+ }
+ /* timeout? */
+ if (reg & SDICMDSTAT_CMDTIMEOUT) {
+ writel(SDICMDSTAT_CMDTIMEOUT,
+ hw_dev->map_base + SDICMDSTAT);
+ 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 mci_pdata MCI platform data
+ * @param mci_dev MCI device instance
+ * @return 0 on success, negative value else
+ */
+static int mci_reset(struct mci_host *mci_pdata, struct device_d *mci_dev)
+{
+ struct device_d *hw_dev = mci_pdata->hw_dev;
+
+ return s3c_mci_initialize(hw_dev, mci_dev);
+}
+
+/**
+ * Process one command to the MCI card
+ * @param mci_pdata MCI platform data
+ * @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 mci_host *mci_pdata, struct mci_cmd *cmd,
+ struct mci_data *data)
+{
+ struct device_d *hw_dev = mci_pdata->hw_dev;
+ 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 mci_pdata MCI platform data
+ * @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 mci_host *mci_pdata, struct device_d *mci_dev,
+ unsigned bus_width, unsigned clock)
+{
+ struct device_d *hw_dev = mci_pdata->hw_dev;
+ struct s3c_mci_host *host_data = GET_HOST_DATA(hw_dev);
+ struct mci_host *host = 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;
+ host->bus_width = 4; /* 4 bit is possible */
+ break;
+ default:
+ host_data->bus_width = 0;
+ host->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 */
+ host->clock = host_data->clock = s3c_setup_clock_speed(hw_dev, clock);
+ reg |= SDICON_CLKEN; /* enable the clock */
+ } else {
+ reg &= ~SDICON_CLKEN; /* disable the clock */
+ host->clock = host_data->clock = 0;
+ }
+ writel(reg, hw_dev->map_base + SDICON);
+
+ pr_debug("IO settings: bus width=%d, frequency=%u Hz\n",
+ host->bus_width, host->clock);
+}
+
+/* ----------------------------------------------------------------------- */
+
+#ifdef CONFIG_MCI_INFO
+static void s3c_info(struct device_d *hw_dev)
+{
+ struct s3c_mci_host *host = hw_dev->priv;
+ struct s3c_mci_platform_data *pd = 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_host 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 = 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 ? s3c24xx_get_pclk() / 256 : pd->f_min;
+ mci_pdata.f_max = pd->f_max == 0 ? s3c24xx_get_pclk() / 2 : 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] 11+ messages in thread
* [PATCH 08/10] i.MX27: Add mmc clock support in order to add an i.MX MCI driver
2010-10-08 16:30 [PATCH v2] Add MCI card support to barebox Juergen Beisert
` (6 preceding siblings ...)
2010-10-08 16:30 ` [PATCH 07/10] Add S3C2440 " Juergen Beisert
@ 2010-10-08 16:30 ` Juergen Beisert
2010-10-08 16:30 ` [PATCH 09/10] mci: Add i.MX27/31 driver Juergen Beisert
2010-10-08 16:30 ` [PATCH 10/10] ARM pca100: Add mci support Juergen Beisert
9 siblings, 0 replies; 11+ messages in thread
From: Juergen Beisert @ 2010-10-08 16:30 UTC (permalink / raw)
To: barebox
From: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Acked-by: Juergen Beisert <jbe@pengutronix.de>
---
arch/arm/mach-imx/include/mach/clock.h | 1 +
arch/arm/mach-imx/speed-imx27.c | 5 +++++
2 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-imx/include/mach/clock.h b/arch/arm/mach-imx/include/mach/clock.h
index 76ab4a5..bbe7a60 100644
--- a/arch/arm/mach-imx/include/mach/clock.h
+++ b/arch/arm/mach-imx/include/mach/clock.h
@@ -28,6 +28,7 @@ ulong imx_get_gptclk(void);
ulong imx_get_uartclk(void);
ulong imx_get_lcdclk(void);
ulong imx_get_i2cclk(void);
+ulong imx_get_mmcclk(void);
int imx_clko_set_div(int div);
void imx_clko_set_src(int src);
diff --git a/arch/arm/mach-imx/speed-imx27.c b/arch/arm/mach-imx/speed-imx27.c
index cdcd419..0a92d29 100644
--- a/arch/arm/mach-imx/speed-imx27.c
+++ b/arch/arm/mach-imx/speed-imx27.c
@@ -159,6 +159,11 @@ ulong imx_get_i2cclk(void)
return imx_get_ipgclk();
}
+ulong imx_get_mmcclk(void)
+{
+ return imx_get_perclk2();
+}
+
void imx_dump_clocks(void)
{
uint32_t cid = CID;
--
1.7.2.3
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 09/10] mci: Add i.MX27/31 driver
2010-10-08 16:30 [PATCH v2] Add MCI card support to barebox Juergen Beisert
` (7 preceding siblings ...)
2010-10-08 16:30 ` [PATCH 08/10] i.MX27: Add mmc clock support in order to add an i.MX MCI driver Juergen Beisert
@ 2010-10-08 16:30 ` Juergen Beisert
2010-10-08 16:30 ` [PATCH 10/10] ARM pca100: Add mci support Juergen Beisert
9 siblings, 0 replies; 11+ messages in thread
From: Juergen Beisert @ 2010-10-08 16:30 UTC (permalink / raw)
To: barebox
From: Sascha Hauer <s.hauer@pengutronix.de>
Add i.MX27/31 driver
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
drivers/mci/Kconfig | 7 +
drivers/mci/Makefile | 1 +
drivers/mci/imx.c | 520 ++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 528 insertions(+), 0 deletions(-)
create mode 100644 drivers/mci/imx.c
diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
index 644c0a3..0bc4254 100644
--- a/drivers/mci/Kconfig
+++ b/drivers/mci/Kconfig
@@ -41,4 +41,11 @@ config MCI_S3C
Enable this entry to add support to read and write SD cards on a
Samsung S3C24xx based system.
+config MCI_IMX
+ bool "i.MX"
+ depends on ARCH_IMX27 || ARCH_IMX31
+ help
+ Enable this entry to add support to read and write SD cards on a
+ Freescale i.MX based system.
+
endif
diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile
index be18446..f393e93 100644
--- a/drivers/mci/Makefile
+++ b/drivers/mci/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_MCI) += mci-core.o
obj-$(CONFIG_MCI_STM378X) += stm378x.o
obj-$(CONFIG_MCI_S3C) += s3c.o
+obj-$(CONFIG_MCI_IMX) += imx.o
diff --git a/drivers/mci/imx.c b/drivers/mci/imx.c
new file mode 100644
index 0000000..8525692
--- /dev/null
+++ b/drivers/mci/imx.c
@@ -0,0 +1,520 @@
+/*
+ * This is a driver for the SDHC controller found in Freescale MX2/MX3
+ * SoCs. It is basically the same hardware as found on MX1 (imxmmc.c).
+ * Unlike the hardware found on MX1, this hardware just works and does
+ * not need all the quirks found in imxmmc.c, hence the seperate driver.
+ *
+ * Copyright (C) 2009 Ilya Yanok, <yanok@emcraft.com>
+ * Copyright (C) 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ * Copyright (C) 2006 Pavel Pisa, PiKRON <ppisa@pikron.com>
+ *
+ * derived from pxamci.c by Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <config.h>
+#include <common.h>
+#include <command.h>
+#include <mci.h>
+#include <malloc.h>
+#include <errno.h>
+#include <clock.h>
+#include <init.h>
+#include <driver.h>
+#include <mach/clock.h>
+#include <asm/io.h>
+
+#define DRIVER_NAME "imx-mmc"
+
+struct mxcmci_regs {
+ u32 str_stp_clk;
+ u32 status;
+ u32 clk_rate;
+ u32 cmd_dat_cont;
+ u32 res_to;
+ u32 read_to;
+ u32 blk_len;
+ u32 nob;
+ u32 rev_no;
+ u32 int_cntr;
+ u32 cmd;
+ u32 arg;
+ u32 pad;
+ u32 res_fifo;
+ u32 buffer_access;
+};
+
+#define STR_STP_CLK_RESET (1 << 3)
+#define STR_STP_CLK_START_CLK (1 << 1)
+#define STR_STP_CLK_STOP_CLK (1 << 0)
+
+#define STATUS_CARD_INSERTION (1 << 31)
+#define STATUS_CARD_REMOVAL (1 << 30)
+#define STATUS_YBUF_EMPTY (1 << 29)
+#define STATUS_XBUF_EMPTY (1 << 28)
+#define STATUS_YBUF_FULL (1 << 27)
+#define STATUS_XBUF_FULL (1 << 26)
+#define STATUS_BUF_UND_RUN (1 << 25)
+#define STATUS_BUF_OVFL (1 << 24)
+#define STATUS_SDIO_INT_ACTIVE (1 << 14)
+#define STATUS_END_CMD_RESP (1 << 13)
+#define STATUS_WRITE_OP_DONE (1 << 12)
+#define STATUS_DATA_TRANS_DONE (1 << 11)
+#define STATUS_READ_OP_DONE (1 << 11)
+#define STATUS_WR_CRC_ERROR_CODE_MASK (3 << 10)
+#define STATUS_CARD_BUS_CLK_RUN (1 << 8)
+#define STATUS_BUF_READ_RDY (1 << 7)
+#define STATUS_BUF_WRITE_RDY (1 << 6)
+#define STATUS_RESP_CRC_ERR (1 << 5)
+#define STATUS_CRC_READ_ERR (1 << 3)
+#define STATUS_CRC_WRITE_ERR (1 << 2)
+#define STATUS_TIME_OUT_RESP (1 << 1)
+#define STATUS_TIME_OUT_READ (1 << 0)
+#define STATUS_ERR_MASK 0x2f
+
+#define CMD_DAT_CONT_CMD_RESP_LONG_OFF (1 << 12)
+#define CMD_DAT_CONT_STOP_READWAIT (1 << 11)
+#define CMD_DAT_CONT_START_READWAIT (1 << 10)
+#define CMD_DAT_CONT_BUS_WIDTH_4 (2 << 8)
+#define CMD_DAT_CONT_INIT (1 << 7)
+#define CMD_DAT_CONT_WRITE (1 << 4)
+#define CMD_DAT_CONT_DATA_ENABLE (1 << 3)
+#define CMD_DAT_CONT_RESPONSE_48BIT_CRC (1 << 0)
+#define CMD_DAT_CONT_RESPONSE_136BIT (2 << 0)
+#define CMD_DAT_CONT_RESPONSE_48BIT (3 << 0)
+
+#define INT_SDIO_INT_WKP_EN (1 << 18)
+#define INT_CARD_INSERTION_WKP_EN (1 << 17)
+#define INT_CARD_REMOVAL_WKP_EN (1 << 16)
+#define INT_CARD_INSERTION_EN (1 << 15)
+#define INT_CARD_REMOVAL_EN (1 << 14)
+#define INT_SDIO_IRQ_EN (1 << 13)
+#define INT_DAT0_EN (1 << 12)
+#define INT_BUF_READ_EN (1 << 4)
+#define INT_BUF_WRITE_EN (1 << 3)
+#define INT_END_CMD_RES_EN (1 << 2)
+#define INT_WRITE_OP_DONE_EN (1 << 1)
+#define INT_READ_OP_EN (1 << 0)
+
+struct mxcmci_host {
+ struct mci_host mci;
+ struct mxcmci_regs *base;
+ int irq;
+ int detect_irq;
+ int dma;
+ int do_dma;
+ unsigned int power_mode;
+
+ struct mci_cmd *cmd;
+ struct mci_data *data;
+
+ unsigned int dma_nents;
+ unsigned int datasize;
+ unsigned int dma_dir;
+
+ u16 rev_no;
+ unsigned int cmdat;
+
+ int clock;
+};
+
+#define to_mxcmci(mci) container_of(mci, struct mxcmci_host, mci)
+
+static void mxcmci_softreset(struct mxcmci_host *host)
+{
+ int i;
+
+ /* reset sequence */
+ writew(STR_STP_CLK_RESET, &host->base->str_stp_clk);
+ writew(STR_STP_CLK_RESET | STR_STP_CLK_START_CLK,
+ &host->base->str_stp_clk);
+
+ for (i = 0; i < 8; i++)
+ writew(STR_STP_CLK_START_CLK, &host->base->str_stp_clk);
+
+ writew(0xff, &host->base->res_to);
+}
+
+static void mxcmci_setup_data(struct mxcmci_host *host, struct mci_data *data)
+{
+ unsigned int nob = data->blocks;
+ unsigned int blksz = data->blocksize;
+ unsigned int datasize = nob * blksz;
+
+ host->data = data;
+
+ writew(nob, &host->base->nob);
+ writew(blksz, &host->base->blk_len);
+ host->datasize = datasize;
+}
+
+static int mxcmci_start_cmd(struct mxcmci_host *host, struct mci_cmd *cmd,
+ unsigned int cmdat)
+{
+ if (host->cmd != NULL)
+ printf("mxcmci: error!\n");
+ host->cmd = cmd;
+
+ switch (cmd->resp_type) {
+ case MMC_RSP_R1: /* short CRC, OPCODE */
+ case MMC_RSP_R1b:/* short CRC, OPCODE, BUSY */
+ cmdat |= CMD_DAT_CONT_RESPONSE_48BIT_CRC;
+ break;
+ case MMC_RSP_R2: /* long 136 bit + CRC */
+ cmdat |= CMD_DAT_CONT_RESPONSE_136BIT;
+ break;
+ case MMC_RSP_R3: /* short */
+ cmdat |= CMD_DAT_CONT_RESPONSE_48BIT;
+ break;
+ case MMC_RSP_NONE:
+ break;
+ default:
+ printf("mxcmci: unhandled response type 0x%x\n",
+ cmd->resp_type);
+ return -EINVAL;
+ }
+
+ writew(cmd->cmdidx, &host->base->cmd);
+ writel(cmd->cmdarg, &host->base->arg);
+ writew(cmdat, &host->base->cmd_dat_cont);
+
+ return 0;
+}
+
+static void mxcmci_finish_request(struct mxcmci_host *host,
+ struct mci_cmd *cmd, struct mci_data *data)
+{
+ host->cmd = NULL;
+ host->data = NULL;
+}
+
+static int mxcmci_finish_data(struct mxcmci_host *host, unsigned int stat)
+{
+ int data_error = 0;
+
+ if (stat & STATUS_ERR_MASK) {
+ printf("request failed. status: 0x%08x\n",
+ stat);
+ if (stat & STATUS_CRC_READ_ERR) {
+ data_error = -EILSEQ;
+ } else if (stat & STATUS_CRC_WRITE_ERR) {
+ u32 err_code = (stat >> 9) & 0x3;
+ if (err_code == 2) /* No CRC response */
+ data_error = -ETIMEDOUT;
+ else
+ data_error = -EILSEQ;
+ } else if (stat & STATUS_TIME_OUT_READ) {
+ data_error = -ETIMEDOUT;
+ } else {
+ data_error = -EIO;
+ }
+ }
+
+ host->data = NULL;
+
+ return data_error;
+}
+
+static int mxcmci_read_response(struct mxcmci_host *host, unsigned int stat)
+{
+ struct mci_cmd *cmd = host->cmd;
+ int i;
+ u32 a, b, c;
+ u32 *resp = (u32 *)cmd->response;
+
+ if (!cmd)
+ return 0;
+
+ if (stat & STATUS_TIME_OUT_RESP) {
+ printf("CMD TIMEOUT\n");
+ return -ETIMEDOUT;
+ } else if (stat & STATUS_RESP_CRC_ERR && cmd->resp_type & MMC_RSP_CRC) {
+ printf("cmd crc error\n");
+ return -EILSEQ;
+ }
+
+ if (cmd->resp_type & MMC_RSP_PRESENT) {
+ if (cmd->resp_type & MMC_RSP_136) {
+ for (i = 0; i < 4; i++) {
+ a = readw(&host->base->res_fifo);
+ b = readw(&host->base->res_fifo);
+ resp[i] = a << 16 | b;
+ }
+ } else {
+ a = readw(&host->base->res_fifo);
+ b = readw(&host->base->res_fifo);
+ c = readw(&host->base->res_fifo);
+ resp[0] = a << 24 | b << 8 | c >> 8;
+ }
+ }
+ return 0;
+}
+
+static int mxcmci_poll_status(struct mxcmci_host *host, u32 mask)
+{
+ u32 stat;
+ uint64_t start = get_time_ns();
+
+ do {
+ stat = readl(&host->base->status);
+ if (stat & STATUS_ERR_MASK)
+ return stat;
+ if (is_timeout(start, SECOND))
+ return STATUS_TIME_OUT_READ;
+ if (stat & mask)
+ return 0;
+ } while (1);
+}
+
+static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes)
+{
+ unsigned int stat;
+ u32 *buf = _buf;
+
+ while (bytes > 3) {
+ stat = mxcmci_poll_status(host,
+ STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE);
+ if (stat)
+ return stat;
+ *buf++ = readl(&host->base->buffer_access);
+ bytes -= 4;
+ }
+
+ if (bytes) {
+ u8 *b = (u8 *)buf;
+ u32 tmp;
+
+ stat = mxcmci_poll_status(host,
+ STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE);
+ if (stat)
+ return stat;
+ tmp = readl(&host->base->buffer_access);
+ memcpy(b, &tmp, bytes);
+ }
+
+ return 0;
+}
+
+static int mxcmci_push(struct mxcmci_host *host, const void *_buf, int bytes)
+{
+ unsigned int stat;
+ const u32 *buf = _buf;
+
+ while (bytes > 3) {
+ stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
+ if (stat)
+ return stat;
+ writel(*buf++, &host->base->buffer_access);
+ bytes -= 4;
+ }
+
+ if (bytes) {
+ const u8 *b = (u8 *)buf;
+ u32 tmp;
+
+ stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
+ if (stat)
+ return stat;
+
+ memcpy(&tmp, b, bytes);
+ writel(tmp, &host->base->buffer_access);
+ }
+
+ stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
+ if (stat)
+ return stat;
+
+ return 0;
+}
+
+static int mxcmci_transfer_data(struct mxcmci_host *host)
+{
+ struct mci_data *data = host->data;
+ int stat;
+ unsigned long length;
+
+ length = data->blocks * data->blocksize;
+ host->datasize = 0;
+
+ if (data->flags & MMC_DATA_READ) {
+ stat = mxcmci_pull(host, data->dest, length);
+ if (stat)
+ return stat;
+ host->datasize += length;
+ } else {
+ stat = mxcmci_push(host, (const void *)(data->src), length);
+ if (stat)
+ return stat;
+ host->datasize += length;
+ stat = mxcmci_poll_status(host, STATUS_WRITE_OP_DONE);
+ if (stat)
+ return stat;
+ }
+ return 0;
+}
+
+static int mxcmci_cmd_done(struct mxcmci_host *host, unsigned int stat)
+{
+ int datastat;
+ int ret;
+
+ ret = mxcmci_read_response(host, stat);
+
+ if (ret) {
+ mxcmci_finish_request(host, host->cmd, host->data);
+ return ret;
+ }
+
+ if (!host->data) {
+ mxcmci_finish_request(host, host->cmd, host->data);
+ return 0;
+ }
+
+ datastat = mxcmci_transfer_data(host);
+ ret = mxcmci_finish_data(host, datastat);
+ mxcmci_finish_request(host, host->cmd, host->data);
+ return ret;
+}
+
+static int mxcmci_request(struct mci_host *mci, struct mci_cmd *cmd,
+ struct mci_data *data)
+{
+ struct mxcmci_host *host = to_mxcmci(mci);
+ unsigned int cmdat = host->cmdat;
+ u32 stat;
+ int ret;
+
+ host->cmdat &= ~CMD_DAT_CONT_INIT;
+ if (data) {
+ mxcmci_setup_data(host, data);
+
+ cmdat |= CMD_DAT_CONT_DATA_ENABLE;
+
+ if (data->flags & MMC_DATA_WRITE)
+ cmdat |= CMD_DAT_CONT_WRITE;
+ }
+
+ if ((ret = mxcmci_start_cmd(host, cmd, cmdat))) {
+ mxcmci_finish_request(host, cmd, data);
+ return ret;
+ }
+
+ do {
+ stat = readl(&host->base->status);
+ writel(stat, &host->base->status);
+ } while (!(stat & STATUS_END_CMD_RESP));
+
+ return mxcmci_cmd_done(host, stat);
+}
+
+static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios)
+{
+ unsigned int divider;
+ int prescaler = 0;
+ unsigned long clk_in = imx_get_mmcclk();
+
+ while (prescaler <= 0x800) {
+ for (divider = 1; divider <= 0xF; divider++) {
+ int x;
+
+ x = (clk_in / (divider + 1));
+
+ if (prescaler)
+ x /= (prescaler * 2);
+
+ if (x <= clk_ios)
+ break;
+ }
+ if (divider < 0x10)
+ break;
+
+ if (prescaler == 0)
+ prescaler = 1;
+ else
+ prescaler <<= 1;
+ }
+
+ writew((prescaler << 4) | divider, &host->base->clk_rate);
+}
+
+static void mxcmci_set_ios(struct mci_host *mci, struct device_d *dev,
+ unsigned bus_width, unsigned clock)
+{
+ struct mxcmci_host *host = to_mxcmci(mci);
+
+ if (bus_width == 4)
+ host->cmdat |= CMD_DAT_CONT_BUS_WIDTH_4;
+ else
+ host->cmdat &= ~CMD_DAT_CONT_BUS_WIDTH_4;
+
+ if (clock) {
+ mxcmci_set_clk_rate(host, clock);
+ writew(STR_STP_CLK_START_CLK, &host->base->str_stp_clk);
+ } else {
+ writew(STR_STP_CLK_STOP_CLK, &host->base->str_stp_clk);
+ }
+
+ host->clock = clock;
+}
+
+static int mxcmci_init(struct mci_host *mci, struct device_d *dev)
+{
+ struct mxcmci_host *host = to_mxcmci(mci);
+
+ mxcmci_softreset(host);
+
+ host->rev_no = readw(&host->base->rev_no);
+ if (host->rev_no != 0x400) {
+ printf("wrong rev.no. 0x%08x. aborting.\n",
+ host->rev_no);
+ return -ENODEV;
+ }
+
+ /* recommended in data sheet */
+ writew(0x2db4, &host->base->read_to);
+
+ writel(0, &host->base->int_cntr);
+
+ return 0;
+}
+
+static int mxcmci_probe(struct device_d *dev)
+{
+ struct mxcmci_host *host;
+
+ host = xzalloc(sizeof(*host));
+
+ host->mci.send_cmd = mxcmci_request;
+ host->mci.set_ios = mxcmci_set_ios;
+ host->mci.init = mxcmci_init;
+ host->mci.host_caps = MMC_MODE_4BIT;
+
+ host->base = (struct mxcmci_regs *)dev->map_base;
+
+ host->mci.voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+
+ host->mci.f_min = imx_get_mmcclk() >> 7;
+ host->mci.f_max = imx_get_mmcclk() >> 1;
+
+ mci_register(&host->mci);
+
+ return 0;
+}
+
+static struct driver_d mxcmci_driver = {
+ .name = DRIVER_NAME,
+ .probe = mxcmci_probe,
+};
+
+static int mxcmci_init_driver(void)
+{
+ register_driver(&mxcmci_driver);
+ return 0;
+}
+
+device_initcall(mxcmci_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] 11+ messages in thread
* [PATCH 10/10] ARM pca100: Add mci support
2010-10-08 16:30 [PATCH v2] Add MCI card support to barebox Juergen Beisert
` (8 preceding siblings ...)
2010-10-08 16:30 ` [PATCH 09/10] mci: Add i.MX27/31 driver Juergen Beisert
@ 2010-10-08 16:30 ` Juergen Beisert
9 siblings, 0 replies; 11+ messages in thread
From: Juergen Beisert @ 2010-10-08 16:30 UTC (permalink / raw)
To: barebox
From: Sascha Hauer <s.hauer@pengutronix.de>
Add mci support to the PCA100 platform
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
arch/arm/boards/phycard-i.MX27/pca100.c | 15 +++++++++++++++
1 files changed, 15 insertions(+), 0 deletions(-)
diff --git a/arch/arm/boards/phycard-i.MX27/pca100.c b/arch/arm/boards/phycard-i.MX27/pca100.c
index 7328a6c..7460c00 100644
--- a/arch/arm/boards/phycard-i.MX27/pca100.c
+++ b/arch/arm/boards/phycard-i.MX27/pca100.c
@@ -109,6 +109,11 @@ static void pca100_usbh_init(void)
}
#endif
+static struct device_d mmc_dev = {
+ .name = "imx-mmc",
+ .map_base = 0x10014000,
+};
+
#ifdef CONFIG_MMU
static void pca100_mmu_init(void)
{
@@ -180,8 +185,17 @@ static int pca100_devices_init(void)
PD23_AF_USBH2_DATA2,
PD24_AF_USBH2_DATA1,
PD26_AF_USBH2_DATA5,
+ /* SDHC */
+ PB4_PF_SD2_D0,
+ PB5_PF_SD2_D1,
+ PB6_PF_SD2_D2,
+ PB7_PF_SD2_D3,
+ PB8_PF_SD2_CMD,
+ PB9_PF_SD2_CLK,
};
+ PCCR0 |= PCCR0_SDHC2_EN;
+
/* disable the usb phys */
imx_gpio_mode((GPIO_PORTB | 23) | GPIO_GPIO | GPIO_IN);
gpio_direction_output(GPIO_PORTB + 23, 1);
@@ -195,6 +209,7 @@ static int pca100_devices_init(void)
register_device(&nand_dev);
register_device(&sdram_dev);
register_device(&fec_dev);
+ register_device(&mmc_dev);
PCCR1 |= PCCR1_PERCLK2_EN;
--
1.7.2.3
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2010-10-08 16:31 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-10-08 16:30 [PATCH v2] Add MCI card support to barebox Juergen Beisert
2010-10-08 16:30 ` [PATCH 01/10] x-functions do not return in case of failure Juergen Beisert
2010-10-08 16:30 ` [PATCH 02/10] Make the disk driver less noisy Juergen Beisert
2010-10-08 16:30 ` [PATCH 03/10] Don't use a sector buffer on stack Juergen Beisert
2010-10-08 16:30 ` [PATCH 04/10] Don't try to guess the size of a disk if its size value is already given Juergen Beisert
2010-10-08 16:30 ` [PATCH 05/10] Add MCI card support to barebox Juergen Beisert
2010-10-08 16:30 ` [PATCH 06/10] Add i.MX23 MCI card support Juergen Beisert
2010-10-08 16:30 ` [PATCH 07/10] Add S3C2440 " Juergen Beisert
2010-10-08 16:30 ` [PATCH 08/10] i.MX27: Add mmc clock support in order to add an i.MX MCI driver Juergen Beisert
2010-10-08 16:30 ` [PATCH 09/10] mci: Add i.MX27/31 driver Juergen Beisert
2010-10-08 16:30 ` [PATCH 10/10] ARM pca100: Add mci support Juergen Beisert
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox