summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Makefile2
-rw-r--r--drivers/ata/Kconfig41
-rw-r--r--drivers/ata/Makefile5
-rw-r--r--drivers/ata/disk_ata_drive.c618
-rw-r--r--drivers/ata/disk_bios_drive.c (renamed from drivers/ata/bios.c)102
-rw-r--r--drivers/ata/disk_drive.c248
-rw-r--r--drivers/ata/intf_platform_ide.c130
-rw-r--r--drivers/base/driver.c29
-rw-r--r--drivers/mci/Kconfig23
-rw-r--r--drivers/mci/Makefile1
-rw-r--r--drivers/mci/atmel_mci.c2
-rw-r--r--drivers/mci/imx-esdhc.c41
-rw-r--r--drivers/mci/mci-core.c264
-rw-r--r--drivers/mci/mci_spi.c430
-rw-r--r--drivers/mtd/nand/nand_base.c8
-rw-r--r--drivers/net/macb.c13
-rw-r--r--drivers/serial/Kconfig4
-rw-r--r--drivers/serial/Makefile1
-rw-r--r--drivers/serial/serial_pxa.c201
-rw-r--r--drivers/spi/altera_spi.c27
-rw-r--r--drivers/usb/gadget/Kconfig7
-rw-r--r--drivers/usb/gadget/Makefile1
-rw-r--r--drivers/usb/gadget/dfu.c2
-rw-r--r--drivers/usb/gadget/f_serial.c5
-rw-r--r--drivers/usb/gadget/pxa27x_udc.c1524
-rw-r--r--drivers/usb/gadget/pxa27x_udc.h393
-rw-r--r--drivers/usb/gadget/u_serial.c27
-rw-r--r--drivers/usb/storage/Kconfig1
-rw-r--r--drivers/usb/storage/usb.c130
-rw-r--r--drivers/usb/storage/usb.h8
30 files changed, 3790 insertions, 498 deletions
diff --git a/drivers/Makefile b/drivers/Makefile
index 16b3bb129f..592c39e6d9 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -4,7 +4,7 @@ obj-y += serial/
obj-y += mtd/
obj-y += nor/
obj-y += usb/
-obj-$(CONFIG_ATA) += ata/
+obj-$(CONFIG_DISK) += ata/
obj-$(CONFIG_SPI) += spi/
obj-$(CONFIG_I2C) += i2c/
obj-$(CONFIG_MCI) += mci/
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index d7f4dcbfe3..fe6f5e6f5d 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -1,25 +1,21 @@
-menuconfig ATA
+menuconfig DISK
select BLOCK
- bool "ATA "
+ select PARTITION
+ select PARTITION_DISK
+ bool "Disk support "
help
- Add support for ATA types of drives like harddisks and CDROMs.
+ Add support for disk like drives like harddisks, CDROMs, SD cards and
+ CF cards.
-if ATA
+if DISK
-comment "drive types"
-
-config ATA_WRITE
+config DISK_WRITE
select BLOCK_WRITE
- bool "support writing to ATA drives"
-
-config ATA_DISK
- bool "disk drives"
- help
- Add support for regular disk drives
+ bool "support writing to disk drives"
-comment "interface types"
+comment "drive types"
-config ATA_BIOS
+config DISK_BIOS
bool "BIOS based"
depends on X86_BIOS_BRINGUP
help
@@ -28,4 +24,19 @@ config ATA_BIOS
media to work on. Disadvantage is: Due to its 16 bit nature it is
slow.
+config DISK_ATA
+ bool "ATA type drives"
+ select DISK_DRIVE
+ help
+ Support for native ATA/IDE drives
+
+comment "interface types"
+
+config DISK_INTF_PLATFORM_IDE
+ bool "Platform IDE"
+ select DISK_ATA
+ help
+ Generic platform driver for simple IDE like interfaces to a connected
+ ATA device.
+
endif
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 30d15ccb02..2560076d26 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -1,7 +1,8 @@
# drive types
-obj-$(CONFIG_ATA_DISK) += disk_drive.o
+obj-$(CONFIG_DISK_BIOS) += disk_bios_drive.o
+obj-$(CONFIG_DISK_ATA) += disk_ata_drive.o
# interface types
-obj-$(CONFIG_ATA_BIOS) += bios.o
+obj-$(CONFIG_DISK_INTF_PLATFORM_IDE) += intf_platform_ide.o
diff --git a/drivers/ata/disk_ata_drive.c b/drivers/ata/disk_ata_drive.c
new file mode 100644
index 0000000000..4602af3c0d
--- /dev/null
+++ b/drivers/ata/disk_ata_drive.c
@@ -0,0 +1,618 @@
+/*
+ * Copyright (C) 2011 Juergen Beisert, Pengutronix
+ *
+ * Inspired from various soures like http://wiki.osdev.org/ATA_PIO_Mode,
+ * u-boot and the linux kernel
+ *
+ * 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.
+ */
+
+#include <common.h>
+#include <xfuncs.h>
+#include <io.h>
+#include <malloc.h>
+#include <errno.h>
+#include <clock.h>
+#include <block.h>
+#include <ata_drive.h>
+#include <disks.h>
+
+#define ATA_CMD_ID_DEVICE 0xEC
+#define ATA_CMD_RD_CONF 0x40
+#define ATA_CMD_RD 0x20
+#define ATA_CMD_WR 0x30
+
+#define DISK_MASTER 0
+#define DISK_SLAVE 1
+
+/* max timeout for a rotating disk in [ms] */
+#define MAX_TIMEOUT 5000
+
+/**
+ * Collection of data we need to know about this drive
+ */
+struct ata_drive_access {
+ struct block_device blk; /**< the main device */
+ struct ata_ioports *io; /**< register file */
+ uint16_t id[(SECTOR_SIZE / sizeof(uint16_t))];
+};
+
+#define to_ata_drive_access(x) container_of((x), struct ata_drive_access, blk)
+
+#define ata_id_u32(id,n) \
+ (((uint32_t) (id)[(n) + 1] << 16) | ((uint32_t) (id)[(n)]))
+#define ata_id_u64(id,n) \
+ ( ((uint64_t) (id)[(n) + 3] << 48) | \
+ ((uint64_t) (id)[(n) + 2] << 32) | \
+ ((uint64_t) (id)[(n) + 1] << 16) | \
+ ((uint64_t) (id)[(n) + 0]) )
+
+#define ata_id_has_lba(id) ((id)[49] & (1 << 9))
+
+/* drive's status flags */
+#define ATA_STATUS_BUSY (1 << 7)
+#define ATA_STATUS_READY (1 << 6)
+#define ATA_STATUS_WR_FLT (1 << 5)
+#define ATA_STATUS_DRQ (1 << 4)
+#define ATA_STATUS_CORR (1 << 3)
+#define ATA_STATUS_ERROR (1 << 1)
+/* command flags */
+#define LBA_FLAG (1 << 6)
+#define ATA_DEVCTL_SOFT_RESET (1 << 2)
+#define ATA_DEVCTL_INTR_DISABLE (1 << 1)
+
+enum {
+ ATA_ID_SERNO = 10,
+#define ATA_ID_SERNO_LEN 20
+ ATA_ID_FW_REV = 23,
+#define ATA_ID_FW_REV_LEN 8
+ ATA_ID_PROD = 27,
+#define ATA_ID_PROD_LEN 40
+ ATA_ID_CAPABILITY = 49,
+ ATA_ID_FIELD_VALID = 53,
+ ATA_ID_LBA_CAPACITY = 60,
+ ATA_ID_MWDMA_MODES = 63,
+ ATA_ID_PIO_MODES = 64,
+ ATA_ID_QUEUE_DEPTH = 75,
+ ATA_ID_MAJOR_VER = 80,
+ ATA_ID_COMMAND_SET_1 = 82,
+ ATA_ID_COMMAND_SET_2 = 83,
+ ATA_ID_CFSSE = 84,
+ ATA_ID_CFS_ENABLE_1 = 85,
+ ATA_ID_CFS_ENABLE_2 = 86,
+ ATA_ID_CSF_DEFAULT = 87,
+ ATA_ID_UDMA_MODES = 88,
+ ATA_ID_HW_CONFIG = 93,
+ ATA_ID_LBA_CAPACITY_2 = 100,
+};
+
+static int ata_id_is_valid(const uint16_t *id)
+{
+ if ((id[ATA_ID_FIELD_VALID] & 1) == 0) {
+ pr_debug("Drive's ID seems invalid\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static inline int ata_id_has_lba48(const uint16_t *id)
+{
+ if ((id[ATA_ID_COMMAND_SET_2] & 0xC000) != 0x4000)
+ return 0;
+ if (!ata_id_u64(id, ATA_ID_LBA_CAPACITY_2))
+ return 0;
+ return id[ATA_ID_COMMAND_SET_2] & (1 << 10);
+}
+
+static uint64_t ata_id_n_sectors(uint16_t *id)
+{
+ if (ata_id_has_lba(id)) {
+ if (ata_id_has_lba48(id))
+ return ata_id_u64(id, ATA_ID_LBA_CAPACITY_2);
+ else
+ return ata_id_u32(id, ATA_ID_LBA_CAPACITY);
+ }
+
+ return 0;
+}
+
+static void ata_id_string(const uint16_t *id, unsigned char *s,
+ unsigned ofs, unsigned len)
+{
+ unsigned c;
+
+ while (len > 0) {
+ c = id[ofs] >> 8;
+ *s = c;
+ s++;
+
+ c = id[ofs] & 0xff;
+ *s = c;
+ s++;
+
+ ofs++;
+ len -= 2;
+ }
+}
+
+static void ata_id_c_string(const uint16_t *id, unsigned char *s,
+ unsigned ofs, unsigned len)
+{
+ unsigned char *p;
+
+ ata_id_string(id, s, ofs, len - 1);
+
+ p = s + strnlen((char *)s, len - 1);
+ while (p > s && p[-1] == ' ')
+ p--;
+ *p = '\0';
+}
+
+static void __maybe_unused ata_dump_id(uint16_t *id)
+{
+ unsigned char serial[ATA_ID_SERNO_LEN + 1];
+ unsigned char firmware[ATA_ID_FW_REV_LEN + 1];
+ unsigned char product[ATA_ID_PROD_LEN + 1];
+ uint64_t n_sectors;
+
+ /* Serial number */
+ ata_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial));
+ printf("S/N: %s\n\r", serial);
+
+ /* Firmware version */
+ ata_id_c_string(id, firmware, ATA_ID_FW_REV, sizeof(firmware));
+ printf("Firmware version: %s\n\r", firmware);
+
+ /* Product model */
+ ata_id_c_string(id, product, ATA_ID_PROD, sizeof(product));
+ printf("Product model number: %s\n\r", product);
+
+ /* Total sectors of device */
+ n_sectors = ata_id_n_sectors(id);
+ printf("Capablity: %lld sectors\n\r", n_sectors);
+
+ printf ("id[49]: capabilities = 0x%04x\n"
+ "id[53]: field valid = 0x%04x\n"
+ "id[63]: mwdma = 0x%04x\n"
+ "id[64]: pio = 0x%04x\n"
+ "id[75]: queue depth = 0x%04x\n",
+ id[ATA_ID_CAPABILITY],
+ id[ATA_ID_FIELD_VALID],
+ id[ATA_ID_MWDMA_MODES],
+ id[ATA_ID_PIO_MODES],
+ id[ATA_ID_QUEUE_DEPTH]);
+
+ printf ("id[76]: sata capablity = 0x%04x\n"
+ "id[78]: sata features supported = 0x%04x\n"
+ "id[79]: sata features enable = 0x%04x\n",
+ id[76], /* FIXME */
+ id[78], /* FIXME */
+ id[79]); /* FIXME */
+
+ printf ("id[80]: major version = 0x%04x\n"
+ "id[81]: minor version = 0x%04x\n"
+ "id[82]: command set supported 1 = 0x%04x\n"
+ "id[83]: command set supported 2 = 0x%04x\n"
+ "id[84]: command set extension = 0x%04x\n",
+ id[ATA_ID_MAJOR_VER],
+ id[81], /* FIXME */
+ id[ATA_ID_COMMAND_SET_1],
+ id[ATA_ID_COMMAND_SET_2],
+ id[ATA_ID_CFSSE]);
+ printf ("id[85]: command set enable 1 = 0x%04x\n"
+ "id[86]: command set enable 2 = 0x%04x\n"
+ "id[87]: command set default = 0x%04x\n"
+ "id[88]: udma = 0x%04x\n"
+ "id[93]: hardware reset result = 0x%04x\n",
+ id[ATA_ID_CFS_ENABLE_1],
+ id[ATA_ID_CFS_ENABLE_2],
+ id[ATA_ID_CSF_DEFAULT],
+ id[ATA_ID_UDMA_MODES],
+ id[ATA_ID_HW_CONFIG]);
+}
+
+/**
+ * Swap little endian data on demand
+ * @param buf Buffer with little endian word data
+ * @param wds 16 bit word count
+ *
+ * ATA disks report their ID data in little endian notation on a 16 bit word
+ * base. So swap the buffer content if the running CPU differs in their
+ * endiaeness.
+ */
+static void ata_fix_endianess(uint16_t *buf, unsigned wds)
+{
+#if __BYTE_ORDER == __BIG_ENDIAN
+ unsigned u;
+
+ for (u = 0; u < wds; u++)
+ buf[u] = le16_to_cpu(buf[u]);
+#endif
+}
+
+/**
+ * Read the status register of the ATA drive
+ * @param io Register file
+ * @return Register's content
+ */
+static uint8_t ata_rd_status(struct ata_ioports *io)
+{
+ return readb(io->status_addr);
+}
+
+/**
+ * Wait until the disk is busy or time out
+ * @param io Register file
+ * @param timeout Timeout in [ms]
+ * @return 0 on success, -ETIMEDOUT else
+ */
+static int ata_wait_busy(struct ata_ioports *io, unsigned timeout)
+{
+ uint8_t status;
+ uint64_t start = get_time_ns();
+ uint64_t toffs = timeout * 1000 * 1000;
+
+ do {
+ status = ata_rd_status(io);
+ if (status & ATA_STATUS_BUSY)
+ return 0;
+ } while (!is_timeout(start, toffs));
+
+ return -ETIMEDOUT;
+}
+
+/**
+ * Wait until the disk is ready again or time out
+ * @param io Register file
+ * @param timeout Timeout in [ms]
+ * @return 0 on success, -ETIMEDOUT else
+ *
+ * This function is useful to check if the disk has accepted a command.
+ */
+static int ata_wait_ready(struct ata_ioports *io, unsigned timeout)
+{
+ uint8_t status;
+ uint64_t start = get_time_ns();
+ uint64_t toffs = timeout * 1000 * 1000;
+
+ do {
+ status = ata_rd_status(io);
+ if (!(status & ATA_STATUS_BUSY)) {
+ if (status & ATA_STATUS_READY)
+ return 0;
+ }
+ } while (!is_timeout(start, toffs));
+
+ return -ETIMEDOUT;
+}
+
+/**
+ * Setup the sector number in LBA notation (LBA28)
+ * @param io Register file
+ * @param drive 0 master drive, 1 slave drive
+ * @param num Sector number
+ *
+ * @todo LBA48 support
+ */
+static int ata_set_lba_sector(struct ata_ioports *io, unsigned drive, uint64_t num)
+{
+ if (num > 0x0FFFFFFF || drive > 1)
+ return -EINVAL;
+
+ writeb(0xA0 | LBA_FLAG | drive << 4 | num >> 24, io->device_addr);
+ writeb(0x00, io->error_addr);
+ writeb(0x01, io->nsect_addr);
+ writeb(num, io->lbal_addr); /* 0 ... 7 */
+ writeb(num >> 8, io->lbam_addr); /* 8 ... 15 */
+ writeb(num >> 16, io->lbah_addr); /* 16 ... 23 */
+
+ return 0;
+}
+
+/**
+ * Write an ATA command into the disk
+ * @param io Register file
+ * @param cmd Command to write
+ * @return 0 on success
+ */
+static int ata_wr_cmd(struct ata_ioports *io, uint8_t cmd)
+{
+ int rc;
+
+ rc = ata_wait_ready(io, MAX_TIMEOUT);
+ if (rc != 0)
+ return rc;
+
+ writeb(cmd, io->command_addr);
+ return 0;
+}
+
+/**
+ * Write a new value into the "device control register"
+ * @param io Register file
+ * @param val Value to write
+ */
+static void ata_wr_dev_ctrl(struct ata_ioports *io, uint8_t val)
+{
+ writeb(val, io->ctl_addr);
+}
+
+/**
+ * Read one sector from the drive (always SECTOR_SIZE bytes at once)
+ * @param io Register file
+ * @param buf Buffer to read the data into
+ */
+static void ata_rd_sector(struct ata_ioports *io, void *buf)
+{
+ unsigned u = SECTOR_SIZE / sizeof(uint16_t);
+ uint16_t *b = buf;
+
+ if (io->dataif_be) {
+ for (; u > 0; u--)
+ *b++ = be16_to_cpu(readw(io->data_addr));
+ } else {
+ for (; u > 0; u--)
+ *b++ = le16_to_cpu(readw(io->data_addr));
+ }
+}
+
+/**
+ * Write one sector into the drive
+ * @param io Register file
+ * @param buf Buffer to read the data from
+ */
+static void ata_wr_sector(struct ata_ioports *io, const void *buf)
+{
+ unsigned u = SECTOR_SIZE / sizeof(uint16_t);
+ const uint16_t *b = buf;
+
+ if (io->dataif_be) {
+ for (; u > 0; u--)
+ writew(cpu_to_be16(*b++), io->data_addr);
+ } else {
+ for (; u > 0; u--)
+ writew(cpu_to_le16(*b++), io->data_addr);
+ }
+}
+
+/**
+ * Read the ATA disk's description info
+ * @param d All we need to know about the disk
+ * @return 0 on success
+ */
+static int ata_get_id(struct ata_drive_access *d)
+{
+ int rc;
+
+ writeb(0xA0, d->io->device_addr); /* FIXME drive */
+ writeb(0x00, d->io->lbal_addr);
+ writeb(0x00, d->io->lbam_addr);
+ writeb(0x00, d->io->lbah_addr);
+
+ rc = ata_wr_cmd(d->io, ATA_CMD_ID_DEVICE);
+ if (rc != 0)
+ return rc;
+
+ rc = ata_wait_ready(d->io, MAX_TIMEOUT);
+ if (rc != 0)
+ return rc;
+
+ ata_rd_sector(d->io, &d->id);
+
+ ata_fix_endianess(d->id, SECTOR_SIZE / sizeof(uint16_t));
+
+ return ata_id_is_valid(d->id);
+}
+
+static int ata_reset(struct ata_ioports *io)
+{
+ int rc;
+ uint8_t reg;
+
+ /* try a hard reset first (if available) */
+ if (io->reset != NULL) {
+ pr_debug("%s: Resetting drive...\n", __func__);
+ io->reset(1);
+ rc = ata_wait_busy(io, 500);
+ io->reset(0);
+ if (rc == 0) {
+ rc = ata_wait_ready(io, MAX_TIMEOUT);
+ if (rc != 0)
+ return rc;
+ } else {
+ pr_debug("%s: Drive does not respond to RESET line. Ignored\n",
+ __func__);
+ }
+ }
+
+ /* try a soft reset */
+ ata_wr_dev_ctrl(io, ATA_DEVCTL_SOFT_RESET | ATA_DEVCTL_INTR_DISABLE);
+ rc = ata_wait_busy(io, MAX_TIMEOUT); /* does the drive accept the command? */
+ if (rc != 0) {
+ pr_debug("%s: Drive fails on soft reset\n", __func__);
+ return rc;
+ }
+ ata_wr_dev_ctrl(io, ATA_DEVCTL_INTR_DISABLE);
+ rc = ata_wait_ready(io, MAX_TIMEOUT);
+ if (rc != 0) {
+ pr_debug("%s: Drive fails after soft reset\n", __func__);
+ return rc;
+ }
+
+ reg = ata_rd_status(io) & 0xf;
+
+ if (reg == 0xf) {
+ pr_debug("%s: Seems no drive connected!\n", __func__);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/**
+ * Read a chunk of sectors from the drive
+ * @param blk All info about the block device we need
+ * @param buffer Buffer to read into
+ * @param block Sector's LBA number to start read from
+ * @param num_blocks Sector count to read
+ * @return 0 on success, anything else on failure
+ *
+ * This routine expects the buffer has the correct size to store all data!
+ *
+ * @note Due to 'block' is of type 'int' only small disks can be handled!
+ * @todo Optimize the read loop
+ */
+static int ata_read(struct block_device *blk, void *buffer, int block,
+ int num_blocks)
+{
+ int rc;
+ uint64_t sector = block;
+ struct ata_drive_access *drv = to_ata_drive_access(blk);
+
+ while (num_blocks) {
+ rc = ata_set_lba_sector(drv->io, DISK_MASTER, sector);
+ if (rc != 0)
+ return rc;
+ rc = ata_wr_cmd(drv->io, ATA_CMD_RD);
+ if (rc != 0)
+ return rc;
+ rc = ata_wait_ready(drv->io, MAX_TIMEOUT);
+ if (rc != 0)
+ return rc;
+ ata_rd_sector(drv->io, buffer);
+ num_blocks--;
+ sector++;
+ buffer += SECTOR_SIZE;
+ }
+
+ return 0;
+}
+
+/**
+ * Write a chunk of sectors into the drive
+ * @param blk All info about the block device we need
+ * @param buffer Buffer to write from
+ * @param block Sector's number to start write to
+ * @param num_blocks Sector count to write
+ * @return 0 on success, anything else on failure
+ *
+ * This routine expects the buffer has the correct size to read all data!
+ *
+ * @note Due to 'block' is of type 'int' only small disks can be handled!
+ * @todo Optimize the write loop
+ */
+static int __maybe_unused ata_write(struct block_device *blk,
+ const void *buffer, int block, int num_blocks)
+{
+ int rc;
+ uint64_t sector = block;
+ struct ata_drive_access *drv = to_ata_drive_access(blk);
+
+ while (num_blocks) {
+ rc = ata_set_lba_sector(drv->io, DISK_MASTER, sector);
+ if (rc != 0)
+ return rc;
+ rc = ata_wr_cmd(drv->io, ATA_CMD_WR);
+ if (rc != 0)
+ return rc;
+ ata_wr_sector(drv->io, buffer);
+ num_blocks--;
+ sector++;
+ buffer += SECTOR_SIZE;
+ }
+
+ return 0;
+}
+
+static struct block_device_ops ata_ops = {
+ .read = ata_read,
+#ifdef CONFIG_BLOCK_WRITE
+ .write = ata_write,
+#endif
+};
+
+/* until Barebox can handle 64 bit offsets */
+static int limit_disk_size(uint64_t val)
+{
+ if (val > (__INT_MAX__ / SECTOR_SIZE))
+ return (__INT_MAX__ / SECTOR_SIZE);
+ return (int)val;
+}
+
+/**
+ * Register an ATA drive behind an IDE like interface
+ * @param dev The interface device
+ * @param io ATA register file description
+ * @return 0 on success
+ */
+int register_ata_drive(struct device_d *dev, struct ata_ioports *io)
+{
+ int rc;
+ struct ata_drive_access *drive;
+
+ drive = xzalloc(sizeof(struct ata_drive_access));
+
+ drive->io = io;
+ drive->blk.dev = dev;
+ drive->blk.ops = &ata_ops;
+
+ rc = ata_reset(io);
+ if (rc) {
+ dev_dbg(dev, "Resetting failed\n");
+ goto on_error;
+ }
+
+ rc = ata_get_id(drive);
+ if (rc != 0) {
+ dev_dbg(dev, "Reading ID failed\n");
+ goto on_error;
+ }
+
+#ifdef DEBUG
+ ata_dump_id(drive->id);
+#endif
+ rc = cdev_find_free_index("disk");
+ if (rc == -1)
+ pr_err("Cannot find a free index for the disk node\n");
+
+ drive->blk.num_blocks = limit_disk_size(ata_id_n_sectors(drive->id));
+ drive->blk.cdev.name = asprintf("disk%d", rc);
+ drive->blk.blockbits = SECTOR_SHIFT;
+
+ rc = blockdevice_register(&drive->blk);
+ if (rc != 0) {
+ dev_err(dev, "Failed to register blockdevice\n");
+ goto on_error;
+ }
+
+ /* create partitions on demand */
+ rc = parse_partition_table(&drive->blk);
+ if (rc != 0)
+ dev_warn(dev, "No partition table found\n");
+
+ return 0;
+
+on_error:
+ free(drive);
+ return rc;
+}
+
+/**
+ * @file
+ * @brief Generic ATA disk drive support
+ *
+ * Please be aware: This driver covers only a subset of the available ATA drives
+ *
+ * @todo Support for disks larger than 4 GiB
+ * @todo LBA48
+ * @todo CHS
+ */
diff --git a/drivers/ata/bios.c b/drivers/ata/disk_bios_drive.c
index 6e2377c7ea..78672454c0 100644
--- a/drivers/ata/bios.c
+++ b/drivers/ata/disk_bios_drive.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 Juergen Beisert, Pengutronix
+ * Copyright (C) 2009...2011 Juergen Beisert, Pengutronix
*
* Mostly stolen from the GRUB2 project
* Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008 Free Software Foundation, Inc.
@@ -43,15 +43,13 @@
* Note: This driver does only support LBA addressing. Currently no CHS!
*/
-#include <stdio.h>
-#include <linux/types.h>
+#include <common.h>
#include <init.h>
-#include <driver.h>
-#include <string.h>
-#include <xfuncs.h>
#include <asm/syslib.h>
-#include <ata.h>
#include <errno.h>
+#include <block.h>
+#include <disks.h>
+#include <malloc.h>
/**
* Sector count handled in one count
@@ -61,9 +59,6 @@
*/
#define SECTORS_AT_ONCE 64
-/** Size of one sector in bytes */
-#define SECTOR_SIZE 512
-
/** Command to read sectors from media */
#define BIOS_READ_CMD 0
@@ -89,10 +84,13 @@ struct DAPS
* Collection of data we need to know about the connected drive
*/
struct media_access {
+ struct block_device blk; /**< the main device */
int drive_no; /**< drive number used by the BIOS */
int is_cdrom; /**< drive is a CDROM e.g. no write support */
};
+#define to_media_access(x) container_of((x), struct media_access, blk)
+
/**
* Scratch memory for BIOS communication to handle data in chunks of 32 kiB
*
@@ -146,19 +144,23 @@ static int biosdisk_bios_call(struct media_access *media, int cmd, uint64_t sect
/**
* Read a chunk of sectors from media
- * @param dev our data we need to do the access
- * @param sector_start Sector's LBA number to start read from
- * @param sector_count Sectors to read
+ * @param blk All info about the block device we need
* @param buffer Buffer to read into
+ * @param block Sector's LBA number to start read from
+ * @param num_blocks Sector count to read
* @return 0 on success, anything else on failure
*
* This routine expects the buffer has the correct size to store all data!
+ *
+ * @note Due to 'block' is of type 'int' only small disks can be handled!
*/
-static int biosdisk_read(struct device_d *dev, uint64_t sector_start, unsigned sector_count, void *buffer)
+static int biosdisk_read(struct block_device *blk, void *buffer, int block,
+ int num_blocks)
{
int rc;
- struct ata_interface *intf = dev->platform_data;
- struct media_access *media = intf->priv;
+ uint64_t sector_start = block;
+ unsigned sector_count = num_blocks;
+ struct media_access *media = to_media_access(blk);
while (sector_count >= SECTORS_AT_ONCE) {
rc = biosdisk_bios_call(media, BIOS_READ_CMD, sector_start, SECTORS_AT_ONCE, scratch_buffer);
@@ -182,19 +184,23 @@ static int biosdisk_read(struct device_d *dev, uint64_t sector_start, unsigned s
/**
* Write a chunk of sectors to media
- * @param dev our data we need to do the access
- * @param sector_start Sector's LBA number to start write to
- * @param sector_count Sectors to write
+ * @param blk All info about the block device we need
* @param buffer Buffer to write from
+ * @param block Sector's LBA number to start write to
+ * @param num_blocks Sector count to write
* @return 0 on success, anything else on failure
*
* This routine expects the buffer has the correct size to read all data!
+ *
+ * @note Due to 'block' is of type 'int' only small disks can be handled!
*/
-static int biosdisk_write(struct device_d *dev, uint64_t sector_start, unsigned sector_count, const void *buffer)
+static int __maybe_unused biosdisk_write(struct block_device *blk,
+ const void *buffer, int block, int num_blocks)
{
int rc;
- struct ata_interface *intf = dev->platform_data;
- struct media_access *media = intf->priv;
+ uint64_t sector_start = block;
+ unsigned sector_count = num_blocks;
+ struct media_access *media = to_media_access(blk);
while (sector_count >= SECTORS_AT_ONCE) {
__builtin_memcpy(scratch_buffer, buffer, sizeof(scratch_buffer));
@@ -216,6 +222,13 @@ static int biosdisk_write(struct device_d *dev, uint64_t sector_start, unsigned
return rc;
}
+static struct block_device_ops bios_ata = {
+ .read = biosdisk_read,
+#ifdef CONFIG_BLOCK_WRITE
+ .write = biosdisk_write,
+#endif
+};
+
/**
* Probe for connected drives and register them
*
@@ -228,8 +241,6 @@ static int biosdisk_probe(struct device_d *dev)
{
int drive, rc;
struct media_access media, *m;
- struct device_d *drive_dev;
- struct ata_interface *p;
for (drive = 0x80; drive < 0x90; drive++) {
media.drive_no = drive;
@@ -240,27 +251,32 @@ static int biosdisk_probe(struct device_d *dev)
printf("BIOSdrive %d seems valid. Registering...\n", media.drive_no);
- drive_dev = xzalloc(sizeof(struct device_d) + sizeof(struct media_access) + sizeof(struct ata_interface));
- if (drive_dev == NULL) {
- dev_err(dev, "Out of memory\n");
- return -1;
+ m = xzalloc(sizeof(struct media_access));
+
+ m->blk.dev = dev;
+ m->blk.ops = &bios_ata;
+ /*
+ * keep the 'blk.num_blocks' member 0, as we don't know
+ * the size of this disk yet!
+ */
+ rc = cdev_find_free_index("disk");
+ if (rc < 0)
+ pr_err("Cannot find a free number for the disk node\n");
+ m->blk.cdev.name = asprintf("disk%d", rc);
+ m->blk.blockbits = SECTOR_SHIFT;
+
+ rc = blockdevice_register(&m->blk);
+ if (rc != 0) {
+ dev_err(dev, "Cannot register BIOSdrive %d\n",
+ media.drive_no);
+ free(m);
+ return rc;
}
- m = (struct media_access*)&drive_dev[1];
- p = (struct ata_interface*)&m[1];
-
- m->drive_no = drive;
- m->is_cdrom = 0;
-
- p->write = biosdisk_write;
- p->read = biosdisk_read;
- p->priv = m;
- strcpy(drive_dev->name, "biosdisk");
- drive_dev->id = drive - 0x80;
- drive_dev->resource[0].start = 0;
- drive_dev->platform_data = p;
-
- register_device(drive_dev);
+ /* create partitions on demand */
+ rc = parse_partition_table(&m->blk);
+ if (rc != 0)
+ dev_warn(dev, "No partition table found\n");
}
return 0;
diff --git a/drivers/ata/disk_drive.c b/drivers/ata/disk_drive.c
deleted file mode 100644
index a9d0deefe4..0000000000
--- a/drivers/ata/disk_drive.c
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (C) 2009 Juergen Beisert, Pengutronix
- *
- * 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 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>
-#include <init.h>
-#include <driver.h>
-#include <types.h>
-#include <ata.h>
-#include <xfuncs.h>
-#include <errno.h>
-#include <string.h>
-#include <linux/kernel.h>
-#include <malloc.h>
-#include <common.h>
-#include <block.h>
-#include <asm/unaligned.h>
-
-/**
- * Description of one partition table entry (D*S type)
- */
-struct partition_entry {
- uint8_t boot_indicator;
- uint8_t chs_begin[3];
- uint8_t type;
- uint8_t chs_end[3];
- uint32_t partition_start;
- uint32_t partition_size;
-} __attribute__ ((packed));
-
-/** one for all */
-#define SECTOR_SIZE 512
-
-/**
- * Guess the size of the disk, based on the partition table entries
- * @param dev device to create partitions for
- * @param table partition table
- * @return size in sectors
- */
-#ifdef CONFIG_ATA_BIOS
-static unsigned long disk_guess_size(struct device_d *dev, struct partition_entry *table)
-{
- int part_order[4] = {0, 1, 2, 3};
- unsigned long size = 0;
- int i;
-
- /* TODO order the partitions */
-
- for (i = 0; i < 4; i++) {
- if (table[part_order[i]].partition_start != 0) {
- size += table[part_order[i]].partition_start - size; /* the gap */
- size += table[part_order[i]].partition_size;
- }
- }
-#if 1
-/* limit disk sizes we can't handle due to 32 bit limits */
- if (size > 0x7fffff) {
- dev_warn(dev, "Warning: Size limited due to 32 bit contraints\n");
- size = 0x7fffff;
- }
-#endif
- return size;
-}
-#endif
-
-/**
- * Register partitions found on the drive
- * @param dev device to create partitions for
- * @param table partition table
- * @return 0 on success
- */
-static int disk_register_partitions(struct device_d *dev, struct partition_entry *table)
-{
- int part_order[4] = {0, 1, 2, 3};
- int i, rc;
- char drive_name[16], partition_name[19];
- u32 partition_start, partition_size;
-
- /* TODO order the partitions */
-
- for (i = 0; i < 4; i++) {
- partition_start = get_unaligned(&table[part_order[i]].partition_start);
- partition_size = get_unaligned(&table[part_order[i]].partition_size);
-
- sprintf(drive_name, "%s%d", dev->name, dev->id);
- sprintf(partition_name, "%s%d.%d", dev->name, dev->id, i);
- if (partition_start != 0) {
-#if 1
-/* ignore partitions we can't handle due to 32 bit limits */
- if (partition_start > 0x7fffff)
- continue;
- if (partition_size > 0x7fffff)
- continue;
-#endif
- dev_dbg(dev, "Registering partition %s to drive %s\n",
- partition_name, drive_name);
- rc = devfs_add_partition(drive_name,
- partition_start * SECTOR_SIZE,
- partition_size * SECTOR_SIZE,
- DEVFS_PARTITION_FIXED, partition_name);
- if (rc != 0)
- dev_err(dev, "Failed to register partition %s (%d)\n", partition_name, rc);
- }
- }
-
- return 0;
-}
-
-struct ata_block_device {
- struct block_device blk;
- struct device_d *dev;
- struct ata_interface *intf;
-};
-
-static int atablk_read(struct block_device *blk, void *buf, int block,
- int num_blocks)
-{
- struct ata_block_device *atablk = container_of(blk, struct ata_block_device, blk);
-
- return atablk->intf->read(atablk->dev, block, num_blocks, buf);
-}
-
-#ifdef CONFIG_ATA_WRITE
-static int atablk_write(struct block_device *blk, const void *buf, int block,
- int num_blocks)
-{
- struct ata_block_device *atablk = container_of(blk, struct ata_block_device, blk);
-
- return atablk->intf->write(atablk->dev, block, num_blocks, buf);
-}
-#endif
-
-static struct block_device_ops ataops = {
- .read = atablk_read,
-#ifdef CONFIG_ATA_WRITE
- .write = atablk_write,
-#endif
-};
-
-/**
- * Probe the connected disk drive
- */
-static int disk_probe(struct device_d *dev)
-{
- uint8_t *sector;
- int rc;
- struct ata_interface *intf = dev->platform_data;
- struct ata_block_device *atablk = xzalloc(sizeof(*atablk));
-
- sector = xmalloc(SECTOR_SIZE);
-
- rc = intf->read(dev, 0, 1, sector);
- if (rc != 0) {
- dev_err(dev, "Cannot read MBR of this device\n");
- rc = -ENODEV;
- goto on_error;
- }
-
- /*
- * BIOS based disks needs special handling. Not the driver can
- * enumerate the hardware, the BIOS did it already. To show the user
- * the drive ordering must not correspond to the Linux drive order,
- * use the 'biosdisk' name instead.
- */
-#ifdef CONFIG_ATA_BIOS
- if (strcmp(dev->driver->name, "biosdisk") == 0)
- atablk->blk.cdev.name = asprintf("biosdisk%d", dev->id);
- else
-#endif
- atablk->blk.cdev.name = asprintf("disk%d", dev->id);
-
-#ifdef CONFIG_ATA_BIOS
- /* On x86, BIOS based disks are coming without a valid .size field */
- if (dev->resource[0].size == 0) {
- /* guess the size of this drive if not otherwise given */
- dev->resource[0].size = disk_guess_size(dev,
- (struct partition_entry*)&sector[446]) * SECTOR_SIZE;
- dev_info(dev, "Drive size guessed to %u kiB\n", dev->resource[0].size / 1024);
- }
-#endif
- atablk->blk.num_blocks = dev->resource[0].size / SECTOR_SIZE;
- atablk->blk.ops = &ataops;
- atablk->blk.blockbits = 9;
- atablk->blk.dev = dev;
- atablk->dev = dev;
- atablk->intf = intf;
- blockdevice_register(&atablk->blk);
-
- if ((sector[510] != 0x55) || (sector[511] != 0xAA)) {
- dev_info(dev, "No partition table found\n");
- rc = 0;
- goto on_error;
- }
-
-
- rc = disk_register_partitions(dev, (struct partition_entry*)&sector[446]);
-
-on_error:
- free(sector);
- return rc;
-}
-
-#ifdef CONFIG_ATA_BIOS
-static struct driver_d biosdisk_driver = {
- .name = "biosdisk",
- .probe = disk_probe,
-};
-#endif
-
-static struct driver_d disk_driver = {
- .name = "disk",
- .probe = disk_probe,
-};
-
-static int disk_init(void)
-{
-#ifdef CONFIG_ATA_BIOS
- register_driver(&biosdisk_driver);
-#endif
- register_driver(&disk_driver);
- return 0;
-}
-
-device_initcall(disk_init);
diff --git a/drivers/ata/intf_platform_ide.c b/drivers/ata/intf_platform_ide.c
new file mode 100644
index 0000000000..1955a9cb6c
--- /dev/null
+++ b/drivers/ata/intf_platform_ide.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2011 Juergen Beisert, Pengutronix
+ *
+ * Derived from the Linux kernel: Generic platform device PATA driver
+ * Copyright (C) 2006 - 2007 Paul Mundt
+ * Based on pata_pcmcia:
+ * Copyright 2005-2006 Red Hat Inc, all rights reserved.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * 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.
+ */
+
+#include <common.h>
+#include <init.h>
+#include <xfuncs.h>
+#include <malloc.h>
+#include <errno.h>
+#include <ata_drive.h>
+#include <platform_ide.h>
+
+/**
+ * Setup the register specific addresses for an ATA like divice
+ * @param reg_base Base address of the standard ATA like registers
+ * @param alt_base Base address of the alternate ATA like registers
+ * @param ioaddr Register file structure to setup
+ * @param shift Shift offset between registers
+ *
+ * Some of the registers have different names but use the same offset. This is
+ * due to the fact read or write access at the same offset reaches different
+ * registers.
+ */
+static void platform_ide_setup_port(void *reg_base, void *alt_base,
+ struct ata_ioports *ioaddr, unsigned shift)
+{
+ /* standard registers (0 ... 7) */
+ ioaddr->cmd_addr = reg_base;
+
+ ioaddr->data_addr = reg_base + (IDE_REG_DATA << shift);
+
+ ioaddr->error_addr = reg_base + (IDE_REG_ERR << shift);
+ ioaddr->feature_addr = reg_base + (IDE_REG_FEATURE << shift);
+
+ ioaddr->nsect_addr = reg_base + (IDE_REG_NSECT << shift);
+
+ ioaddr->lbal_addr = reg_base + (IDE_REG_LBAL << shift);
+
+ ioaddr->lbam_addr = reg_base + (IDE_REG_LBAM << shift);
+
+ ioaddr->lbah_addr = reg_base + (IDE_REG_LBAH << shift);
+
+ ioaddr->device_addr = reg_base + (IDE_REG_DEVICE << shift);
+
+ ioaddr->status_addr = reg_base + (IDE_REG_STATUS << shift);
+ ioaddr->command_addr = reg_base + (IDE_REG_CMD << shift);
+
+ ioaddr->altstatus_addr = alt_base + (IDE_REG_ALT_STATUS << shift);
+ ioaddr->ctl_addr = alt_base + (IDE_REG_DEV_CTL << shift);
+
+ ioaddr->alt_dev_addr = alt_base + (IDE_REG_DRV_ADDR << shift);
+}
+
+static int platform_ide_probe(struct device_d *dev)
+{
+ int rc;
+ struct ide_port_info *pdata = dev->platform_data;
+ struct ata_ioports *io;
+ void *reg_base, *alt_base;
+
+ if (pdata == NULL) {
+ dev_err(dev, "No platform data. Cannot continue\n");
+ return -EINVAL;
+ }
+
+ io = xzalloc(sizeof(struct ata_ioports));
+ reg_base = dev_request_mem_region(dev, 0);
+ alt_base = dev_request_mem_region(dev, 1);
+ platform_ide_setup_port(reg_base, alt_base, io, pdata->ioport_shift);
+ io->reset = pdata->reset;
+ io->dataif_be = pdata->dataif_be;
+
+ rc = register_ata_drive(dev, io);
+ if (rc != 0) {
+ dev_err(dev, "Cannot register IDE interface\n");
+ free(io);
+ }
+
+ return rc;
+}
+
+static struct driver_d platform_ide_driver = {
+ .name = "ide_intf",
+ .probe = platform_ide_probe,
+};
+
+static int platform_ide_init(void)
+{
+ return register_driver(&platform_ide_driver);
+}
+
+device_initcall(platform_ide_init);
+
+/**
+ * @file
+ * @brief Interface driver for an ATA drive behind an IDE like interface
+ *
+ * This communication driver does all accesses to the drive via memory IO
+ * instructions.
+ *
+ * This kind of interface is also useable for regular bus like access, when
+ * there is no dedicated IDE available.
+ *
+ * "IDE like" means the platform data defines addresses to the register file
+ * of the attached ATA drive.
+ *
+ * This driver does not change any access timings due to the fact it has no idea
+ * how to do so. So, do not expect an impressive data throughput.
+ *
+ * @todo Support also the IO port access method, the x86 architecture is using
+ */
diff --git a/drivers/base/driver.c b/drivers/base/driver.c
index 0b80103144..0132e7dcf8 100644
--- a/drivers/base/driver.c
+++ b/drivers/base/driver.c
@@ -214,7 +214,7 @@ int register_driver(struct driver_d *drv)
}
EXPORT_SYMBOL(register_driver);
-void __iomem *dev_get_mem_region(struct device_d *dev, int num)
+static struct resource *dev_get_resource(struct device_d *dev, int num)
{
int i, n = 0;
@@ -222,15 +222,40 @@ void __iomem *dev_get_mem_region(struct device_d *dev, int num)
struct resource *res = &dev->resource[i];
if (resource_type(res) == IORESOURCE_MEM) {
if (n == num)
- return (void __force __iomem *)res->start;
+ return res;
n++;
}
}
return NULL;
}
+
+void __iomem *dev_get_mem_region(struct device_d *dev, int num)
+{
+ struct resource *res;
+
+ res = dev_get_resource(dev, num);
+ if (!res)
+ return res;
+
+ return (void __force __iomem *)res->start;
+}
EXPORT_SYMBOL(dev_get_mem_region);
+void __iomem *dev_request_mem_region(struct device_d *dev, int num)
+{
+ struct resource *res;
+
+ res = dev_get_resource(dev, num);
+ if (!res)
+ return NULL;
+
+ res = request_iomem_region(dev_name(dev), res->start, res->size);
+
+ return (void __force __iomem *)res->start;
+}
+EXPORT_SYMBOL(dev_request_mem_region);
+
int dev_protect(struct device_d *dev, size_t count, unsigned long offset, int prot)
{
printf("%s: currently broken\n", __func__);
diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
index 0d5a0e0814..6ed21cd2a4 100644
--- a/drivers/mci/Kconfig
+++ b/drivers/mci/Kconfig
@@ -1,7 +1,6 @@
menuconfig MCI
bool "MCI drivers "
- select ATA
- select ATA_DISK
+ select DISK
help
Add support for MCI drivers, used to handle MMC and SD cards
@@ -28,7 +27,7 @@ config MCI_INFO
config MCI_WRITE
bool "Support writing to MCI cards"
default y
- select ATA_WRITE
+ select DISK_WRITE
comment "--- MCI host drivers ---"
@@ -80,4 +79,22 @@ config MCI_ATMEL
Enable this entry to add support to read and write SD cards on a
Atmel AT91.
+config MCI_SPI
+ bool "MMC/SD over SPI"
+ depends on SPI
+ help
+ Some systems access MMC/SD/SDIO cards using a SPI controller
+ instead of using a "native" MMC/SD/SDIO controller. This has a
+ disadvantage of being relatively high overhead, but a compensating
+ advantage of working on many systems without dedicated MMC/SD/SDIO
+ controllers.
+
+config MMC_SPI_CRC_ON
+ bool "Enable CRC protection for transfers"
+ select CRC7
+ select CRC16
+ depends on MCI_SPI
+ help
+ Enable CRC protection for transfers
+
endif
diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile
index 4fc00461e9..d7482dc8a2 100644
--- a/drivers/mci/Makefile
+++ b/drivers/mci/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_MCI_IMX) += imx.o
obj-$(CONFIG_MCI_IMX_ESDHC) += imx-esdhc.o
obj-$(CONFIG_MCI_OMAP_HSMMC) += omap_hsmmc.o
obj-$(CONFIG_MCI_ATMEL) += atmel_mci.o
+obj-$(CONFIG_MCI_SPI) += mci_spi.o
diff --git a/drivers/mci/atmel_mci.c b/drivers/mci/atmel_mci.c
index 4a29cad18f..15668fc78b 100644
--- a/drivers/mci/atmel_mci.c
+++ b/drivers/mci/atmel_mci.c
@@ -369,6 +369,8 @@ static void mci_set_ios(struct mci_host *mci, struct device_d *mci_dev,
atmel_mci_writel(host, AT91_MCI_SDCR, AT91_MCI_SDCBUS_1BIT);
break;
}
+ atmel_mci_writel(host, AT91_MCI_SDCR, atmel_mci_readl(host, AT91_MCI_SDCR)
+ | host->hw_dev->id);
if (clock) {
atmel_set_clk_rate(host, clock);
diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c
index a0e61f0d01..fe55697638 100644
--- a/drivers/mci/imx-esdhc.c
+++ b/drivers/mci/imx-esdhc.c
@@ -36,6 +36,8 @@
#include <asm/mmu.h>
#include <mach/clock.h>
#include <mach/generic.h>
+#include <mach/esdhc.h>
+#include <gpio.h>
#include "imx-esdhc.h"
@@ -107,7 +109,8 @@ u32 esdhc_xfertyp(struct mci_cmd *cmd, struct mci_data *data)
xfertyp |= XFERTYP_RSPTYP_48_BUSY;
else if (cmd->resp_type & MMC_RSP_PRESENT)
xfertyp |= XFERTYP_RSPTYP_48;
- if (cpu_is_mx53() && cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
+ if ((cpu_is_mx51() || cpu_is_mx53()) &&
+ cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
xfertyp |= SDHCI_CMD_ABORTCMD;
return XFERTYP_CMD(cmd->cmdidx) | xfertyp;
@@ -391,6 +394,31 @@ static void esdhc_set_ios(struct mci_host *mci, struct device_d *dev,
}
+static int esdhc_card_detect(struct fsl_esdhc_host *host)
+{
+ struct fsl_esdhc *regs = host->regs;
+ struct esdhc_platform_data *pdata = host->dev->platform_data;
+ int ret;
+
+ if (!pdata)
+ return 1;
+
+ switch (pdata->cd_type) {
+ case ESDHC_CD_NONE:
+ case ESDHC_CD_PERMANENT:
+ return 1;
+ case ESDHC_CD_CONTROLLER:
+ return !(esdhc_read32(&regs->prsstat) & PRSSTAT_WPSPL);
+ case ESDHC_CD_GPIO:
+ ret = gpio_direction_input(pdata->cd_gpio);
+ if (ret)
+ return ret;
+ return gpio_get_value(pdata->cd_gpio) ? 0 : 1;
+ }
+
+ return 0;
+}
+
static int esdhc_init(struct mci_host *mci, struct device_d *dev)
{
struct fsl_esdhc_host *host = to_fsl_esdhc(mci);
@@ -398,6 +426,16 @@ static int esdhc_init(struct mci_host *mci, struct device_d *dev)
int timeout = 1000;
int ret = 0;
+ ret = esdhc_card_detect(host);
+
+ if (ret == 0)
+ return -ENODEV;
+
+ if (ret < 0)
+ return ret;
+
+ ret = 0;
+
/* Enable cache snooping */
if (host && !host->no_snoop)
esdhc_write32(&regs->scr, 0x00000040);
@@ -484,7 +522,6 @@ static int fsl_esdhc_probe(struct device_d *dev)
host->mci.send_cmd = esdhc_send_cmd;
host->mci.set_ios = esdhc_set_ios;
host->mci.init = esdhc_init;
- host->mci.host_caps = MMC_MODE_4BIT;
host->mci.hw_dev = dev;
host->mci.voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c
index 09f7e29dc1..e9fe87ca40 100644
--- a/drivers/mci/mci-core.c
+++ b/drivers/mci/mci-core.c
@@ -36,7 +36,8 @@
#include <errno.h>
#include <asm-generic/div64.h>
#include <asm/byteorder.h>
-#include <ata.h>
+#include <block.h>
+#include <disks.h>
#define MAX_BUFFER_NUMBER 0xffffffff
@@ -104,15 +105,23 @@ static void *sector_buf;
* @param mci_dev MCI instance
* @param src Where to read from to write to the card
* @param blocknum Block number to write
+ * @param blocks Block count to write
* @return Transaction status (0 on success)
*/
-#ifdef CONFIG_MCI_WRITE
-static int mci_block_write(struct device_d *mci_dev, const void *src, unsigned blocknum)
+static int mci_block_write(struct device_d *mci_dev, const void *src, int blocknum,
+ int blocks)
{
struct mci *mci = GET_MCI_DATA(mci_dev);
struct mci_cmd cmd;
struct mci_data data;
const void *buf;
+ unsigned mmccmd;
+ int ret;
+
+ if (blocks > 1)
+ mmccmd = MMC_CMD_WRITE_MULTIPLE_BLOCK;
+ else
+ mmccmd = MMC_CMD_WRITE_SINGLE_BLOCK;
if ((unsigned long)src & 0x3) {
memcpy(sector_buf, src, 512);
@@ -122,18 +131,30 @@ static int mci_block_write(struct device_d *mci_dev, const void *src, unsigned b
}
mci_setup_cmd(&cmd,
- MMC_CMD_WRITE_SINGLE_BLOCK,
+ mmccmd,
mci->high_capacity != 0 ? blocknum : blocknum * mci->write_bl_len,
MMC_RSP_R1);
data.src = buf;
- data.blocks = 1;
+ data.blocks = blocks;
data.blocksize = mci->write_bl_len;
data.flags = MMC_DATA_WRITE;
- return mci_send_cmd(mci_dev, &cmd, &data);
+ ret = mci_send_cmd(mci_dev, &cmd, &data);
+ if (ret)
+ return ret;
+
+ if (blocks > 1) {
+ mci_setup_cmd(&cmd,
+ MMC_CMD_STOP_TRANSMISSION,
+ 0, MMC_RSP_R1b);
+ ret = mci_send_cmd(mci_dev, &cmd, NULL);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
}
-#endif
/**
* Read one block of data from the card
@@ -142,7 +163,7 @@ static int mci_block_write(struct device_d *mci_dev, const void *src, unsigned b
* @param blocknum Block number to read
* @param blocks number of blocks to read
*/
-static int mci_read_block(struct device_d *mci_dev, void *dst, unsigned blocknum,
+static int mci_read_block(struct device_d *mci_dev, void *dst, int blocknum,
int blocks)
{
struct mci *mci = GET_MCI_DATA(mci_dev);
@@ -218,6 +239,7 @@ static int sd_send_op_cond(struct device_d *mci_dev)
int timeout = 1000;
int err;
unsigned voltages;
+ unsigned busy;
/*
* Most cards do not answer if some reserved bits
@@ -237,7 +259,7 @@ static int sd_send_op_cond(struct device_d *mci_dev)
}
mci_setup_cmd(&cmd, SD_CMD_APP_SEND_OP_COND,
- voltages | (mci->version == SD_VERSION_2 ? OCR_HCS : 0),
+ mmc_host_is_spi(host) ? 0 : (voltages | (mci->version == SD_VERSION_2 ? OCR_HCS : 0)),
MMC_RSP_R3);
err = mci_send_cmd(mci_dev, &cmd, NULL);
if (err) {
@@ -245,7 +267,13 @@ static int sd_send_op_cond(struct device_d *mci_dev)
return err;
}
udelay(1000);
- } while ((!(cmd.response[0] & OCR_BUSY)) && timeout--);
+
+ if (mmc_host_is_spi(host))
+ busy = cmd.response[0] & R1_SPI_IDLE;
+ else
+ busy = !(cmd.response[0] & OCR_BUSY);
+
+ } while (busy && timeout--);
if (timeout <= 0) {
pr_debug("SD operation condition set timed out\n");
@@ -255,6 +283,13 @@ static int sd_send_op_cond(struct device_d *mci_dev)
if (mci->version != SD_VERSION_2)
mci->version = SD_VERSION_1_0;
+ if (mmc_host_is_spi(host)) { /* read OCR for spi */
+ mci_setup_cmd(&cmd, MMC_CMD_SPI_READ_OCR, 0, MMC_RSP_R3);
+ err = mci_send_cmd(mci_dev, &cmd, NULL);
+ if (err)
+ return err;
+ }
+
mci->ocr = cmd.response[0];
mci->high_capacity = ((mci->ocr & OCR_HCS) == OCR_HCS);
@@ -453,11 +488,17 @@ 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;
+#ifdef CONFIG_MCI_SPI
+ struct mci_host *host = GET_MCI_PDATA(mci_dev);
+#endif
uint32_t *switch_status = sector_buf;
uint32_t *scr = sector_buf;
int timeout;
int err;
+ if (mmc_host_is_spi(host))
+ return 0;
+
pr_debug("Changing transfer frequency\n");
mci->card_caps = 0;
@@ -748,10 +789,23 @@ static int mci_startup(struct device_d *mci_dev)
struct mci_cmd cmd;
int err;
+#ifdef CONFIG_MMC_SPI_CRC_ON
+ if (mmc_host_is_spi(host)) { /* enable CRC check for spi */
+
+ mci_setup_cmd(&cmd, MMC_CMD_SPI_CRC_ON_OFF, 1, MMC_RSP_R1);
+ err = mci_send_cmd(mci_dev, &cmd, NULL);
+
+ if (err) {
+ pr_debug("Can't enable CRC check : %d\n", err);
+ return err;
+ }
+ }
+#endif
+
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);
+ mci_setup_cmd(&cmd, mmc_host_is_spi(host) ? MMC_CMD_SEND_CID : 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);
@@ -768,12 +822,14 @@ static int mci_startup(struct device_d *mci_dev)
* 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 (!mmc_host_is_spi(host)) { /* cmd not supported in spi */
+ 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))
@@ -800,27 +856,29 @@ static int mci_startup(struct device_d *mci_dev)
mci_extract_card_capacity_from_csd(mci_dev);
/* sanitiy? */
- if (mci->read_bl_len > 512) {
- mci->read_bl_len = 512;
+ if (mci->read_bl_len > SECTOR_SIZE) {
+ mci->read_bl_len = SECTOR_SIZE;
pr_debug("Limiting max. read block size down to %u\n",
mci->read_bl_len);
}
- if (mci->write_bl_len > 512) {
- mci->write_bl_len = 512;
+ if (mci->write_bl_len > SECTOR_SIZE) {
+ mci->write_bl_len = SECTOR_SIZE;
pr_debug("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 (!mmc_host_is_spi(host)) { /* cmd not supported in spi */
+ 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))
@@ -936,77 +994,68 @@ static int sd_send_if_cond(struct device_d *mci_dev)
return 0;
}
-/* ------------------ attach to the ATA API --------------------------- */
+/* ------------------ attach to the blocklayer --------------------------- */
/**
* 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 blk All info about the block device we need
* @param buffer Buffer to write from
+ * @param block Sector's number to start write to
+ * @param num_blocks Sector count to write
* @return 0 on success, anything else on failure
*
* This routine expects the buffer has the correct size to read all data!
*/
-#ifdef CONFIG_MCI_WRITE
-static int mci_sd_write(struct device_d *disk_dev, uint64_t sector_start,
- unsigned sector_count, const void *buffer)
+static int __maybe_unused mci_sd_write(struct block_device *blk,
+ const void *buffer, int block, int num_blocks)
{
- struct ata_interface *intf = disk_dev->platform_data;
- struct device_d *mci_dev = intf->priv;
+ struct device_d *mci_dev = blk->dev;
struct mci *mci = GET_MCI_DATA(mci_dev);
int rc;
- pr_debug("%s: Write %u block(s), starting at %u\n",
- __func__, sector_count, (unsigned)sector_start);
+ pr_debug("%s: Write %d block(s), starting at %d\n",
+ __func__, num_blocks, block);
- if (mci->write_bl_len != 512) {
- pr_debug("MMC/SD block size is not 512 bytes (its %u bytes instead)\n",
- mci->read_bl_len);
+ if (mci->write_bl_len != SECTOR_SIZE) {
+ pr_debug("MMC/SD block size is not %d bytes (its %u bytes instead)\n",
+ SECTOR_SIZE, 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_debug("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_debug("Writing block %u failed with %d\n", (unsigned)sector_start, rc);
- return rc;
- }
- sector_count--;
- buffer += mci->write_bl_len;
- sector_start++;
+ /* size of the block number field in the MMC/SD command is 32 bit only */
+ if (block > MAX_BUFFER_NUMBER) {
+ pr_debug("Cannot handle block number %d. Too large!\n", block);
+ return -EINVAL;
+ }
+
+ rc = mci_block_write(mci_dev, buffer, block, num_blocks);
+ if (rc != 0) {
+ pr_debug("Writing block %d failed with %d\n", block, rc);
+ return rc;
}
return 0;
}
-#endif
/**
- * 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
+ * Read a chunk of sectors from the drive
+ * @param blk All info about the block device we need
* @param buffer Buffer to read into
+ * @param block Sector's LBA number to start read from
+ * @param num_blocks Sector count to read
* @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)
+static int mci_sd_read(struct block_device *blk, void *buffer, int block,
+ int num_blocks)
{
- struct ata_interface *intf = disk_dev->platform_data;
- struct device_d *mci_dev = intf->priv;
+ struct device_d *mci_dev = blk->dev;
struct mci *mci = GET_MCI_DATA(mci_dev);
int rc;
- pr_debug("%s: Read %u block(s), starting at %u\n",
- __func__, sector_count, (unsigned)sector_start);
+ pr_debug("%s: Read %d block(s), starting at %d\n",
+ __func__, num_blocks, block);
if (mci->read_bl_len != 512) {
pr_debug("MMC/SD block size is not 512 bytes (its %u bytes instead)\n",
@@ -1014,21 +1063,15 @@ static int mci_sd_read(struct device_d *disk_dev, uint64_t sector_start,
return -EINVAL;
}
- while (sector_count) {
- int now = min(sector_count, 32U);
- if (sector_start > MAX_BUFFER_NUMBER) {
- pr_err("Cannot handle block number %u. Too large!\n",
- (unsigned)sector_start);
- return -EINVAL;
- }
- rc = mci_read_block(mci_dev, buffer, (unsigned)sector_start, now);
- if (rc != 0) {
- pr_debug("Reading block %u failed with %d\n", (unsigned)sector_start, rc);
- return rc;
- }
- sector_count -= now;
- buffer += mci->read_bl_len * now;
- sector_start += now;
+ if (block > MAX_BUFFER_NUMBER) {
+ pr_err("Cannot handle block number %d. Too large!\n", block);
+ return -EINVAL;
+ }
+
+ rc = mci_read_block(mci_dev, buffer, block, num_blocks);
+ if (rc != 0) {
+ pr_debug("Reading block %d failed with %d\n", block, rc);
+ return rc;
}
return 0;
@@ -1166,6 +1209,25 @@ static int mci_check_if_already_initialized(struct device_d *mci_dev)
return 0;
}
+static int mci_calc_blk_cnt(uint64_t cap, unsigned shift)
+{
+ unsigned ret = cap >> shift;
+
+ if (ret > 0x7fffffff) {
+ pr_warn("Limiting card size due to 31 bit contraints\n");
+ return 0x7fffffff;
+ }
+
+ return (int)ret;
+}
+
+static struct block_device_ops mci_ops = {
+ .read = mci_sd_read,
+#ifdef CONFIG_BLOCK_WRITE
+ .write = mci_sd_write,
+#endif
+};
+
/**
* Probe an MCI card at the given host interface
* @param mci_dev MCI device instance
@@ -1175,9 +1237,7 @@ 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 ata_interface *p;
int rc;
- struct device_d *dev;
/* start with a host interface reset */
rc = (host->init)(host, mci_dev);
@@ -1221,16 +1281,29 @@ static int mci_card_probe(struct device_d *mci_dev)
* An MMC/SD card acts like an ordinary disk.
* So, re-use the disk driver to gain access to this media
*/
- p = xzalloc(sizeof(struct ata_interface));
+ mci->blk.dev = mci_dev;
+ mci->blk.ops = &mci_ops;
-#ifdef CONFIG_MCI_WRITE
- p->write = mci_sd_write;
-#endif
- p->read = mci_sd_read;
- p->priv = mci_dev;
+ rc = cdev_find_free_index("disk");
+ if (rc == -1)
+ pr_err("Cannot find a free number for the disk node\n");
- dev = add_generic_device("disk", -1, NULL, 0, mci->capacity, IORESOURCE_MEM, p);
- dev_add_child(&host->dev, dev);
+ mci->blk.cdev.name = asprintf("disk%d", rc);
+ mci->blk.blockbits = SECTOR_SHIFT;
+ mci->blk.num_blocks = mci_calc_blk_cnt(mci->capacity, mci->blk.blockbits);
+
+ rc = blockdevice_register(&mci->blk);
+ if (rc != 0) {
+ dev_err(mci_dev, "Failed to register MCI/SD blockdevice\n");
+ goto on_error;
+ }
+
+ /* create partitions on demand */
+ rc = parse_partition_table(&mci->blk);
+ if (rc != 0) {
+ dev_warn(mci_dev, "No partition table found\n");
+ rc = 0; /* it's not a failure */
+ }
pr_debug("SD Card successfully added\n");
@@ -1307,7 +1380,7 @@ static int mci_probe(struct device_d *mci_dev)
#ifdef CONFIG_MCI_STARTUP
/* if enabled, probe the attached card immediately */
rc = mci_card_probe(mci_dev);
- if (rc == -ENODEV) {
+ if (rc) {
/*
* If it fails, add the 'probe' parameter to give the user
* a chance to insert a card and try again. Note: This may fail
@@ -1361,8 +1434,9 @@ device_initcall(mci_init);
*/
int mci_register(struct mci_host *host)
{
- struct device_d *mci_dev = &host->dev;
+ struct device_d *mci_dev = xzalloc(sizeof(struct device_d));
+ mci_dev->id = -1;
strcpy(mci_dev->name, mci_driver.name);
mci_dev->platform_data = (void*)host;
dev_add_child(host->hw_dev, mci_dev);
diff --git a/drivers/mci/mci_spi.c b/drivers/mci/mci_spi.c
new file mode 100644
index 0000000000..653910bf7f
--- /dev/null
+++ b/drivers/mci/mci_spi.c
@@ -0,0 +1,430 @@
+/*
+ * (C) Copyright 2011 - Franck JULLIEN <elec4fun@gmail.com>
+ *
+ * This code was inspired from u-boot mmc_spi.c:
+ * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
+ *
+ * and linux mmc_spi.c:
+ * (C) Copyright 2005, Intec Automation,
+ * Mike Lavender (mike@steroidmicros)
+ * (C) Copyright 2006-2007, David Brownell
+ * (C) Copyright 2007, Axis Communications,
+ * Hans-Peter Nilsson (hp@axis.com)
+ * (C) Copyright 2007, ATRON electronic GmbH,
+ * Jan Nikitenko <jan.nikitenko@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <common.h>
+#include <init.h>
+#include <errno.h>
+#include <clock.h>
+#include <asm/io.h>
+#include <driver.h>
+#include <spi/spi.h>
+#include <mci.h>
+#include <crc.h>
+#include <crc7.h>
+
+#define to_spi_host(mci) container_of(mci, struct mmc_spi_host, mci)
+#define spi_setup(spi) spi->master->setup(spi)
+
+/* Response tokens used to ack each block written: */
+#define SPI_MMC_RESPONSE_CODE(x) ((x) & 0x1f)
+#define SPI_RESPONSE_ACCEPTED ((2 << 1)|1)
+
+/* Read and write blocks start with these tokens and end with crc;
+ * on error, read tokens act like a subset of R2_SPI_* values.
+ */
+#define SPI_TOKEN_SINGLE 0xFE /* single block r/w, multiblock read */
+#define SPI_TOKEN_MULTI_WRITE 0xFC /* multiblock write */
+#define SPI_TOKEN_STOP_TRAN 0xFD /* terminate multiblock write */
+
+/* MMC SPI commands start with a start bit "0" and a transmit bit "1" */
+#define MMC_SPI_CMD(x) (0x40 | (x & 0x3F))
+
+#define MMC_SPI_BLOCKSIZE 512
+
+/* timeout value */
+#define CTOUT 8
+#define RTOUT 3000000 /* 1 sec */
+#define WTOUT 3000000 /* 1 sec */
+
+struct mmc_spi_host {
+ struct mci_host mci;
+ struct spi_device *spi;
+ struct device_d *dev;
+
+ /* for bulk data transfers */
+ struct spi_transfer t_tx;
+ struct spi_message m_tx;
+
+ /* for status readback */
+ struct spi_transfer t_rx;
+ struct spi_message m_rx;
+
+ void *ones;
+};
+
+static char *maptype(struct mci_cmd *cmd)
+{
+ switch (cmd->resp_type) {
+ case MMC_RSP_NONE: return "NONE";
+ case MMC_RSP_R1: return "R1";
+ case MMC_RSP_R1b: return "R1B";
+ case MMC_RSP_R2: return "R2/R5";
+ case MMC_RSP_R3: return "R3/R4/R7";
+ default: return "?";
+ }
+}
+
+static inline int mmc_cs_off(struct mmc_spi_host *host)
+{
+ /* chipselect will always be inactive after setup() */
+ return spi_setup(host->spi);
+}
+
+static int
+mmc_spi_readbytes(struct mmc_spi_host *host, unsigned len, void *data)
+{
+ int status;
+
+ host->t_rx.len = len;
+ host->t_rx.rx_buf = data;
+
+ status = spi_sync(host->spi, &host->m_rx);
+
+ return status;
+}
+
+static int
+mmc_spi_writebytes(struct mmc_spi_host *host, unsigned len, void *data)
+{
+ int status;
+
+ host->t_tx.len = len;
+ host->t_tx.tx_buf = data;
+
+ status = spi_sync(host->spi, &host->m_tx);
+
+ return status;
+}
+
+/*
+ * Note that while the CRC, in general, is ignored in SPI mode, the very first
+ * command must be followed by a valid CRC, since the card is not yet in SPI mode.
+ * The CRC byte for a CMD0 command with a zero argument is a constant 0x4A. For
+ * simplicity, this CRC byte is always sent with every command.
+ */
+#define MMC_SPI_CMD0_CRC ((0x4a << 1) | 0x1)
+
+static int mmc_spi_command_send(struct mmc_spi_host *host, struct mci_cmd *cmd)
+{
+ uint8_t r1;
+ uint8_t command[7];
+ int i;
+
+ command[0] = 0xff;
+ command[1] = MMC_SPI_CMD(cmd->cmdidx);
+ command[2] = cmd->cmdarg >> 24;
+ command[3] = cmd->cmdarg >> 16;
+ command[4] = cmd->cmdarg >> 8;
+ command[5] = cmd->cmdarg;
+#ifdef CONFIG_MMC_SPI_CRC_ON
+ command[6] = (crc7(0, &command[1], 5) << 1) | 0x01;
+#else
+ command[6] = MMC_SPI_CMD0_CRC;
+#endif
+
+ mmc_spi_writebytes(host, 7, command);
+
+ for (i = 0; i < CTOUT; i++) {
+ mmc_spi_readbytes(host, 1, &r1);
+ if (i && ((r1 & 0x80) == 0)) { /* r1 response */
+ dev_dbg(host->dev, "%s: CMD%d, TRY %d, RESP %x\n", __func__, cmd->cmdidx, i, r1);
+ break;
+ }
+ }
+
+ return r1;
+}
+
+static uint mmc_spi_readdata(struct mmc_spi_host *host, void *xbuf,
+ uint32_t bcnt, uint32_t bsize)
+{
+ uint8_t *buf = xbuf;
+ uint8_t r1;
+ uint16_t crc;
+ int i;
+
+ while (bcnt--) {
+ for (i = 0; i < RTOUT; i++) {
+ mmc_spi_readbytes(host, 1, &r1);
+ if (r1 != 0xff) /* data token */
+ break;
+ }
+ if (r1 == SPI_TOKEN_SINGLE) {
+ mmc_spi_readbytes(host, bsize, buf);
+ mmc_spi_readbytes(host, 2, &crc);
+#ifdef CONFIG_MMC_SPI_CRC_ON
+ if (swab16(cyg_crc16(buf, bsize)) != crc) {
+ dev_dbg(host->dev, "%s: CRC error\n", __func__);
+ r1 = R1_SPI_COM_CRC;
+ break;
+ }
+#endif
+ r1 = 0;
+ } else {
+ r1 = R1_SPI_ERROR;
+ break;
+ }
+ buf += bsize;
+ }
+
+ return r1;
+}
+
+static uint mmc_spi_writedata(struct mmc_spi_host *host, const void *xbuf,
+ uint32_t bcnt, uint32_t bsize, int multi)
+{
+ const uint8_t *buf = xbuf;
+ uint8_t r1;
+ uint16_t crc = 0;
+ uint8_t tok[2];
+ int i;
+
+ tok[0] = 0xff;
+ tok[1] = multi ? SPI_TOKEN_MULTI_WRITE : SPI_TOKEN_SINGLE;
+
+ while (bcnt--) {
+#ifdef CONFIG_MMC_SPI_CRC_ON
+ crc = swab16(cyg_crc16((u8 *)buf, bsize));
+#endif
+ mmc_spi_writebytes(host, 2, tok);
+ mmc_spi_writebytes(host, bsize, (void *)buf);
+ mmc_spi_writebytes(host, 2, &crc);
+
+ for (i = 0; i < CTOUT; i++) {
+ mmc_spi_readbytes(host, 1, &r1);
+ if ((r1 & 0x11) == 0x01) /* response token */
+ break;
+ }
+
+ dev_dbg(host->dev,"%s : TOKEN%d RESP 0x%X\n", __func__, i, r1);
+ if (SPI_MMC_RESPONSE_CODE(r1) == SPI_RESPONSE_ACCEPTED) {
+ for (i = 0; i < WTOUT; i++) { /* wait busy */
+ mmc_spi_readbytes(host, 1, &r1);
+ if (i && r1 == 0xff) {
+ r1 = 0;
+ break;
+ }
+ }
+ if (i == WTOUT) {
+ dev_dbg(host->dev, "%s: wtout %x\n", __func__, r1);
+ r1 = R1_SPI_ERROR;
+ break;
+ }
+ } else {
+ dev_dbg(host->dev, "%s: err %x\n", __func__, r1);
+ r1 = R1_SPI_COM_CRC;
+ break;
+ }
+ buf += bsize;
+ }
+
+ if (multi && bcnt == -1) { /* stop multi write */
+ tok[1] = SPI_TOKEN_STOP_TRAN;
+ mmc_spi_writebytes(host, 2, tok);
+ for (i = 0; i < WTOUT; i++) { /* wait busy */
+ mmc_spi_readbytes(host, 1, &r1);
+ if (i && r1 == 0xff) {
+ r1 = 0;
+ break;
+ }
+ }
+ if (i == WTOUT) {
+ dev_dbg(host->dev, "%s: wstop %x\n", __func__, r1);
+ r1 = R1_SPI_ERROR;
+ }
+ }
+
+ return r1;
+}
+
+static int mmc_spi_request(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
+{
+ struct mmc_spi_host *host = to_spi_host(mci);
+ uint8_t r1;
+ int i;
+ int ret = 0;
+
+ dev_dbg(host->dev, "%s : CMD%02d, RESP %s, ARG 0x%X\n", __func__,
+ cmd->cmdidx, maptype(cmd), cmd->cmdarg);
+
+ r1 = mmc_spi_command_send(host, cmd);
+
+ cmd->response[0] = r1;
+
+ if (r1 == 0xff) { /* no response */
+ ret = -ETIME;
+ goto done;
+ } else if (r1 & R1_SPI_COM_CRC) {
+ ret = -ECOMM;
+ goto done;
+ } else if (r1 & ~R1_SPI_IDLE) { /* other errors */
+ ret = -ETIME;
+ goto done;
+ } else if (cmd->resp_type == MMC_RSP_R2) {
+ r1 = mmc_spi_readdata(host, cmd->response, 1, 16);
+ for (i = 0; i < 4; i++)
+ cmd->response[i] = swab32(cmd->response[i]);
+ dev_dbg(host->dev, "MMC_RSP_R2 -> %x %x %x %x\n", cmd->response[0], cmd->response[1],
+ cmd->response[2], cmd->response[3]);
+ } else if (!data) {
+ switch (cmd->cmdidx) {
+ case SD_CMD_SEND_IF_COND:
+ case MMC_CMD_SPI_READ_OCR:
+ mmc_spi_readbytes(host, 4, cmd->response);
+ cmd->response[0] = swab32(cmd->response[0]);
+ break;
+ }
+ } else {
+ if (data->flags == MMC_DATA_READ) {
+ dev_dbg(host->dev, "%s : DATA READ, %x blocks, bsize = 0x%X\n", __func__,
+ data->blocks, data->blocksize);
+ r1 = mmc_spi_readdata(host, data->dest,
+ data->blocks, data->blocksize);
+ } else if (data->flags == MMC_DATA_WRITE) {
+ dev_dbg(host->dev, "%s : DATA WRITE, %x blocks, bsize = 0x%X\n", __func__,
+ data->blocks, data->blocksize);
+ r1 = mmc_spi_writedata(host, data->src,
+ data->blocks, data->blocksize,
+ (cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK));
+ }
+ if (r1 & R1_SPI_COM_CRC)
+ ret = -ECOMM;
+ else if (r1)
+ ret = -ETIME;
+ }
+
+done:
+ mmc_cs_off(host);
+ return ret;
+
+return 0;
+
+}
+
+static void mmc_spi_set_ios(struct mci_host *mci, struct device_d *mci_dev,
+ unsigned bus_width, unsigned clock)
+{
+ struct mmc_spi_host *host = to_spi_host(mci);
+
+ spi_setup(host->spi);
+}
+
+static int mmc_spi_init(struct mci_host *mci, struct device_d *mci_dev)
+{
+ struct mmc_spi_host *host = to_spi_host(mci);
+ mmc_spi_readbytes(host, 10, NULL);
+
+ /*
+ * Do a burst with chipselect active-high. We need to do this to
+ * meet the requirement of 74 clock cycles with both chipselect
+ * and CMD (MOSI) high before CMD0 ... after the card has been
+ * powered up to Vdd(min), and so is ready to take commands.
+ *
+ * Some cards are particularly needy of this (e.g. Viking "SD256")
+ * while most others don't seem to care.
+ *
+ * Note that this is one of the places MMC/SD plays games with the
+ * SPI protocol. Another is that when chipselect is released while
+ * the card returns BUSY status, the clock must issue several cycles
+ * with chipselect high before the card will stop driving its output.
+ */
+
+ host->spi->mode |= SPI_CS_HIGH;
+ if (spi_setup(host->spi) != 0) {
+ /* Just warn; most cards work without it. */
+ dev_warn(&host->spi->dev,
+ "can't change chip-select polarity\n");
+ host->spi->mode &= ~SPI_CS_HIGH;
+ } else {
+ mmc_spi_readbytes(host, 18, NULL);
+
+ host->spi->mode &= ~SPI_CS_HIGH;
+ if (spi_setup(host->spi) != 0) {
+ /* Wot, we can't get the same setup we had before? */
+ dev_err(&host->spi->dev,
+ "can't restore chip-select polarity\n");
+ }
+ }
+
+ return 0;
+}
+
+static int spi_mci_probe(struct device_d *dev)
+{
+ struct spi_device *spi = (struct spi_device *)dev->type_data;
+ struct mmc_spi_host *host;
+ void *ones;
+
+ host = xzalloc(sizeof(*host));
+ host->mci.send_cmd = mmc_spi_request;
+ host->mci.set_ios = mmc_spi_set_ios;
+ host->mci.init = mmc_spi_init;
+
+ host->dev = dev;
+ host->spi = spi;
+ dev->priv = host;
+
+ ones = xmalloc(MMC_SPI_BLOCKSIZE);
+ memset(ones, 0xff, MMC_SPI_BLOCKSIZE);
+
+ host->ones = ones;
+
+ spi_message_init(&host->m_tx);
+ spi_message_init(&host->m_rx);
+
+ spi_message_add_tail(&host->t_tx, &host->m_tx);
+ spi_message_add_tail(&host->t_rx, &host->m_rx);
+
+ host->t_rx.tx_buf = host->ones;
+ host->t_rx.cs_change = 1;
+
+ host->t_tx.cs_change = 1;
+
+ host->mci.voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+ host->mci.host_caps = MMC_CAP_SPI;
+
+ mci_register(&host->mci);
+
+ return 0;
+}
+
+static struct driver_d spi_mci_driver = {
+ .name = "spi_mci",
+ .probe = spi_mci_probe,
+};
+
+static int spi_mci_init_driver(void)
+{
+ register_driver(&spi_mci_driver);
+ return 0;
+}
+
+device_initcall(spi_mci_init_driver);
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 2710f6a309..ec0f4a3e11 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -1067,7 +1067,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
tmp_id = chip->read_byte(mtd);
if (tmp_manf != *maf_id || tmp_id != dev_id) {
- printk(KERN_INFO "%s: second ID read did not match "
+ printk(KERN_ERR "%s: second ID read did not match "
"%02x,%02x against %02x,%02x\n", __func__,
*maf_id, dev_id, tmp_manf, tmp_id);
return ERR_PTR(-ENODEV);
@@ -1081,8 +1081,10 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
}
}
- if (!type)
+ if (!type) {
+ printk(KERN_ERR "NAND type unknown: %02x,%02x\n", *maf_id, dev_id);
return ERR_PTR(-ENODEV);
+ }
if (!mtd->name)
mtd->name = type->name;
@@ -1209,7 +1211,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips)
type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id);
if (IS_ERR(type)) {
- printk(KERN_WARNING "No NAND device found!!!\n");
+ printk(KERN_WARNING "No NAND device found (%ld)!\n", PTR_ERR(type));
chip->select_chip(mtd, -1);
return PTR_ERR(type);
}
diff --git a/drivers/net/macb.c b/drivers/net/macb.c
index c1f7f68484..e1cfd85d68 100644
--- a/drivers/net/macb.c
+++ b/drivers/net/macb.c
@@ -302,18 +302,14 @@ static int macb_phy_read(struct mii_device *mdev, int addr, int reg)
unsigned long netctl;
unsigned long netstat;
unsigned long frame;
- int iflag;
int value;
uint64_t start;
debug("%s\n", __func__);
- iflag = disable_interrupts();
netctl = readl(macb->regs + MACB_NCR);
netctl |= MACB_BIT(MPE);
writel(netctl, macb->regs + MACB_NCR);
- if (iflag)
- enable_interrupts();
frame = (MACB_BF(SOF, 1)
| MACB_BF(RW, 2)
@@ -334,12 +330,9 @@ static int macb_phy_read(struct mii_device *mdev, int addr, int reg)
frame = readl(macb->regs + MACB_MAN);
value = MACB_BFEXT(DATA, frame);
- iflag = disable_interrupts();
netctl = readl(macb->regs + MACB_NCR);
netctl &= ~MACB_BIT(MPE);
writel(netctl, macb->regs + MACB_NCR);
- if (iflag)
- enable_interrupts();
return value;
}
@@ -355,12 +348,9 @@ static int macb_phy_write(struct mii_device *mdev, int addr, int reg, int value)
debug("%s\n", __func__);
- iflag = disable_interrupts();
netctl = readl(macb->regs + MACB_NCR);
netctl |= MACB_BIT(MPE);
writel(netctl, macb->regs + MACB_NCR);
- if (iflag)
- enable_interrupts();
frame = (MACB_BF(SOF, 1)
| MACB_BF(RW, 1)
@@ -374,12 +364,9 @@ static int macb_phy_write(struct mii_device *mdev, int addr, int reg, int value)
netstat = readl(macb->regs + MACB_NSR);
} while (!(netstat & MACB_BIT(IDLE)));
- iflag = disable_interrupts();
netctl = readl(macb->regs + MACB_NCR);
netctl &= ~MACB_BIT(MPE);
writel(netctl, macb->regs + MACB_NCR);
- if (iflag)
- enable_interrupts();
return 0;
}
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index c2bee79bb4..592d543508 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -92,4 +92,8 @@ config DRIVER_SERIAL_S3C24X0_AUTOSYNC
Say Y here if you want to use the auto flow feature of this
UART. RTS and CTS will be handled by the hardware when enabled.
+config DRIVER_SERIAL_PXA
+ bool "PXA serial driver"
+ depends on ARCH_PXA
+
endmenu
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 411b8c535d..a7021252f7 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -18,3 +18,4 @@ obj-$(CONFIG_DRIVER_SERIAL_PL010) += serial_pl010.o
obj-$(CONFIG_DRIVER_SERIAL_S3C24X0) += serial_s3c24x0.o
obj-$(CONFIG_DRIVER_SERIAL_ALTERA) += serial_altera.o
obj-$(CONFIG_DRIVER_SERIAL_ALTERA_JTAG) += serial_altera_jtag.o
+obj-$(CONFIG_DRIVER_SERIAL_PXA) += serial_pxa.o
diff --git a/drivers/serial/serial_pxa.c b/drivers/serial/serial_pxa.c
new file mode 100644
index 0000000000..d295235ee3
--- /dev/null
+++ b/drivers/serial/serial_pxa.c
@@ -0,0 +1,201 @@
+/*
+ * (c) 2009 Sascha Hauer <s.hauer@pengutronix.de>
+ * 2010 by Marc Kleine-Budde <kernel@pengutronix.de>
+ *
+ * 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.
+ *
+ */
+
+#include <common.h>
+#include <driver.h>
+#include <init.h>
+#include <malloc.h>
+
+#include <mach/clock.h>
+#include <asm/io.h>
+
+#define RBR 0x00 /* Receive Buffer Register (read only) */
+#define THR 0x00 /* Transmit Holding Register (write only) */
+#define IER 0x04 /* Interrupt Enable Register (read/write) */
+#define IIR 0x08 /* Interrupt ID Register (read only) */
+#define FCR 0x08 /* FIFO Control Register (write only) */
+#define LCR 0x0c /* Line Control Register (read/write) */
+#define MCR 0x10 /* Modem Control Register (read/write) */
+#define LSR 0x14 /* Line Status Register (read only) */
+#define MSR 0x18 /* Modem Status Register (read only) */
+#define SPR 0x1c /* Scratch Pad Register (read/write) */
+#define ISR 0x20 /* Infrared Selection Register (read/write) */
+#define DLL 0x00 /* Divisor Latch Low Register (DLAB = 1) (read/write) */
+#define DLH 0x04 /* Divisor Latch High Register (DLAB = 1) (read/write) */
+
+#define IER_DMAE (1 << 7) /* DMA Requests Enable */
+#define IER_UUE (1 << 6) /* UART Unit Enable */
+#define IER_NRZE (1 << 5) /* NRZ coding Enable */
+#define IER_RTIOE (1 << 4) /* Receiver Time Out Interrupt Enable */
+#define IER_MIE (1 << 3) /* Modem Interrupt Enable */
+#define IER_RLSE (1 << 2) /* Receiver Line Status Interrupt Enable */
+#define IER_TIE (1 << 1) /* Transmit Data request Interrupt Enable */
+#define IER_RAVIE (1 << 0) /* Receiver Data Available Interrupt Enable */
+
+#define IIR_FIFOES1 (1 << 7) /* FIFO Mode Enable Status */
+#define IIR_FIFOES0 (1 << 6) /* FIFO Mode Enable Status */
+#define IIR_TOD (1 << 3) /* Time Out Detected */
+#define IIR_IID2 (1 << 2) /* Interrupt Source Encoded */
+#define IIR_IID1 (1 << 1) /* Interrupt Source Encoded */
+#define IIR_IP (1 << 0) /* Interrupt Pending (active low) */
+
+#define FCR_ITL2 (1 << 7) /* Interrupt Trigger Level */
+#define FCR_ITL1 (1 << 6) /* Interrupt Trigger Level */
+#define FCR_RESETTF (1 << 2) /* Reset Transmitter FIFO */
+#define FCR_RESETRF (1 << 1) /* Reset Receiver FIFO */
+#define FCR_TRFIFOE (1 << 0) /* Transmit and Receive FIFO Enable */
+#define FCR_ITL_1 (0)
+#define FCR_ITL_8 (FCR_ITL1)
+#define FCR_ITL_16 (FCR_ITL2)
+#define FCR_ITL_32 (FCR_ITL2 | F CR_ITL1)
+
+#define LCR_DLAB (1 << 7) /* Divisor Latch Access Bit */
+#define LCR_SB (1 << 6) /* Set Break */
+#define LCR_STKYP (1 << 5) /* Sticky Parity */
+#define LCR_EPS (1 << 4) /* Even Parity Select */
+#define LCR_PEN (1 << 3) /* Parity Enable */
+#define LCR_STB (1 << 2) /* Stop Bit */
+#define LCR_WLS1 (1 << 1) /* Word Length Select */
+#define LCR_WLS0 (1 << 0) /* Word Length Select */
+#define LCR_WLEN8 (LCR_WLS1 | LCR_WLS0)
+ /* Wordlength: 8 bits */
+
+#define LSR_FIFOE (1 << 7) /* FIFO Error Status */
+#define LSR_TEMT (1 << 6) /* Transmitter Empty */
+#define LSR_TDRQ (1 << 5) /* Transmit Data Request */
+#define LSR_BI (1 << 4) /* Break Interrupt */
+#define LSR_FE (1 << 3) /* Framing Error */
+#define LSR_PE (1 << 2) /* Parity Error */
+#define LSR_OE (1 << 1) /* Overrun Error */
+#define LSR_DR (1 << 0) /* Data Ready */
+
+#define MCR_LOOP (1 << 4) /* */
+#define MCR_OUT2 (1 << 3) /* force MSR_DCD in loopback mode */
+#define MCR_OUT1 (1 << 2) /* force MSR_RI in loopback mode */
+#define MCR_RTS (1 << 1) /* Request to Send */
+#define MCR_DTR (1 << 0) /* Data Terminal Ready */
+
+#define MSR_DCD (1 << 7) /* Data Carrier Detect */
+#define MSR_RI (1 << 6) /* Ring Indicator */
+#define MSR_DSR (1 << 5) /* Data Set Ready */
+#define MSR_CTS (1 << 4) /* Clear To Send */
+#define MSR_DDCD (1 << 3) /* Delta Data Carrier Detect */
+#define MSR_TERI (1 << 2) /* Trailing Edge Ring Indicator */
+#define MSR_DDSR (1 << 1) /* Delta Data Set Ready */
+#define MSR_DCTS (1 << 0) /* Delta Clear To Send */
+
+struct pxa_serial_priv {
+ void __iomem *regs;
+ struct console_device cdev;
+};
+
+static void __iomem *to_regs(struct console_device *cdev)
+{
+ struct pxa_serial_priv *priv =
+ container_of(cdev, struct pxa_serial_priv, cdev);
+ return priv->regs;
+}
+
+static void pxa_serial_putc(struct console_device *cdev, char c)
+{
+ while (!(readl(to_regs(cdev) + LSR) & LSR_TEMT));
+
+ writel(c, to_regs(cdev) + THR);
+}
+
+static int pxa_serial_tstc(struct console_device *cdev)
+{
+ return readl(to_regs(cdev) + LSR) & LSR_DR;
+}
+
+static int pxa_serial_getc(struct console_device *cdev)
+{
+ while (!(readl(to_regs(cdev) + LSR) & LSR_DR));
+
+ return readl(to_regs(cdev) + RBR) & 0xff;
+}
+
+static void pxa_serial_flush(struct console_device *cdev)
+{
+}
+
+static int pxa_serial_setbaudrate(struct console_device *cdev, int baudrate)
+{
+ unsigned char cval = LCR_WLEN8; /* 8N1 */
+ unsigned int quot;
+
+ /* enable uart */
+ writel(IER_UUE, to_regs(cdev) + IER);
+
+ /* write divisor */
+ quot = (pxa_get_uartclk() + (8 * baudrate)) / (16 * baudrate);
+
+ writel(cval | LCR_DLAB, to_regs(cdev) + LCR); /* set DLAB */
+ writel(quot & 0xff, to_regs(cdev) + DLL);
+ /*
+ * work around Errata #75 according to Intel(R) PXA27x
+ * Processor Family Specification Update (Nov 2005)
+ */
+ readl(to_regs(cdev) + DLL);
+ writel(quot >> 8, to_regs(cdev) + DLH);
+ writel(cval, to_regs(cdev) + LCR); /* reset DLAB */
+
+ /* enable fifos */
+ writel(FCR_TRFIFOE, to_regs(cdev) + FCR);
+
+ return 0;
+}
+
+static int pxa_serial_probe(struct device_d *dev)
+{
+ struct console_device *cdev;
+ struct pxa_serial_priv *priv;
+
+ priv = xzalloc(sizeof(*priv));
+ cdev = &priv->cdev;
+ priv->regs = dev_request_mem_region(dev, 0);
+
+ dev->type_data = cdev;
+ cdev->dev = dev;
+ cdev->f_caps = CONSOLE_STDIN | CONSOLE_STDOUT | CONSOLE_STDERR;
+ cdev->tstc = pxa_serial_tstc;
+ cdev->putc = pxa_serial_putc;
+ cdev->getc = pxa_serial_getc;
+ cdev->flush = pxa_serial_flush;
+ cdev->setbrg = pxa_serial_setbaudrate;
+
+ console_register(cdev);
+
+ return 0;
+}
+
+static void pxa_serial_remove(struct device_d *dev)
+{
+ free(dev->type_data);
+}
+
+static struct driver_d pxa_serial_driver = {
+ .name = "pxa_serial",
+ .probe = pxa_serial_probe,
+ .remove = pxa_serial_remove,
+};
+
+static int pxa_serial_init(void)
+{
+ return register_driver(&pxa_serial_driver);
+}
+
+console_initcall(pxa_serial_init);
diff --git a/drivers/spi/altera_spi.c b/drivers/spi/altera_spi.c
index 4fd82fb905..6cf9942d9d 100644
--- a/drivers/spi/altera_spi.c
+++ b/drivers/spi/altera_spi.c
@@ -25,6 +25,9 @@
#include <io.h>
#include <asm/spi.h>
#include <asm/nios2-io.h>
+#include <clock.h>
+
+static void altera_spi_cs_inactive(struct spi_device *spi);
static int altera_spi_setup(struct spi_device *spi)
{
@@ -49,6 +52,8 @@ static int altera_spi_setup(struct spi_device *spi)
return -1;
}
+ altera_spi_cs_inactive(spi);
+
dev_dbg(master->dev, " mode 0x%08x, bits_per_word: %d, speed: %d\n",
spi->mode, spi->bits_per_word, altera_spi->speed);
@@ -167,19 +172,38 @@ static int altera_spi_transfer(struct spi_device *spi, struct spi_message *mesg)
struct altera_spi *altera_spi = container_of(spi->master, struct altera_spi, master);
struct nios_spi *nios_spi = altera_spi->regs;
struct spi_transfer *t;
+ unsigned int cs_change;
+ const int nsecs = 50;
altera_spi_cs_active(spi);
+ cs_change = 0;
+
mesg->actual_length = 0;
list_for_each_entry(t, &mesg->transfers, transfer_list) {
+
+ if (cs_change) {
+ ndelay(nsecs);
+ altera_spi_cs_inactive(spi);
+ ndelay(nsecs);
+ altera_spi_cs_active(spi);
+ }
+
+ cs_change = t->cs_change;
+
mesg->actual_length += altera_spi_do_xfer(spi, t);
+
+ if (cs_change) {
+ altera_spi_cs_active(spi);
+ }
}
/* Wait the end of any pending transfer */
while ((readl(&nios_spi->status) & NIOS_SPI_TMT) == 0);
- altera_spi_cs_inactive(spi);
+ if (!cs_change)
+ altera_spi_cs_inactive(spi);
return 0;
}
@@ -199,6 +223,7 @@ static int altera_spi_probe(struct device_d *dev)
master->setup = altera_spi_setup;
master->transfer = altera_spi_transfer;
master->num_chipselect = pdata->num_chipselect;
+ master->bus_num = pdata->bus_num;
altera_spi->regs = dev_request_mem_region(dev, 0);
altera_spi->databits = pdata->databits;
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index b8375c661e..472c591c35 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -14,6 +14,13 @@ config USB_GADGET_DRIVER_ARC
prompt "Arc OTG device core"
depends on ARCH_IMX
select USB_GADGET_DUALSPEED
+
+config USB_GADGET_DRIVER_PXA27X
+ bool
+ prompt "PXA27x gadget driver"
+ depends on ARCH_PXA
+ select USB_GADGET_DUALSPEED
+ select POLLER
endchoice
choice
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 804bb914bc..e034786dc8 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -3,3 +3,4 @@ obj-$(CONFIG_USB_GADGET) += composite.o config.o usbstring.o epautoconf.o
obj-$(CONFIG_USB_GADGET_SERIAL) += u_serial.o serial.o f_serial.o f_acm.o
obj-$(CONFIG_USB_GADGET_DFU) += dfu.o
obj-$(CONFIG_USB_GADGET_DRIVER_ARC) += fsl_udc.o
+obj-$(CONFIG_USB_GADGET_DRIVER_PXA27X) += pxa27x_udc.o
diff --git a/drivers/usb/gadget/dfu.c b/drivers/usb/gadget/dfu.c
index 1387c6da22..0a0d244106 100644
--- a/drivers/usb/gadget/dfu.c
+++ b/drivers/usb/gadget/dfu.c
@@ -259,7 +259,7 @@ static int handle_dnload(struct usb_function *f, const struct usb_ctrlrequest *c
ret = -EINVAL;
goto err_out;
}
- ret = copy_file(DFU_TEMPFILE, dfu_devs[dfualt].dev);
+ ret = copy_file(DFU_TEMPFILE, dfu_devs[dfualt].dev, 0);
if (ret) {
printf("copy file failed\n");
ret = -EINVAL;
diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c
index 5b7eb2c1c5..b93310573d 100644
--- a/drivers/usb/gadget/f_serial.c
+++ b/drivers/usb/gadget/f_serial.c
@@ -16,7 +16,6 @@
#include "gadget_chips.h"
#include "u_serial.h"
-
/*
* This function packages a simple "generic serial" port with no real
* control mechanisms, just raw data transfer over two bulk endpoints.
@@ -140,6 +139,7 @@ static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
gser->hs.in, gser->fs.in);
gser->port.out_desc = ep_choose(cdev->gadget,
gser->hs.out, gser->fs.out);
+ gserial_connect(&gser->port, gser->port_num);
}
return 0;
@@ -147,7 +147,10 @@ static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
static void gser_disable(struct usb_function *f)
{
+ struct f_gser *gser = func_to_gser(f);
+
DBG(cdev, "generic ttyGS%d deactivated\n", gser->port_num);
+ gserial_disconnect(&gser->port);
}
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c
new file mode 100644
index 0000000000..d0dbee9824
--- /dev/null
+++ b/drivers/usb/gadget/pxa27x_udc.c
@@ -0,0 +1,1524 @@
+/*
+ * Handles the Intel 27x USB Device Controller (UDC)
+ *
+ * Inspired by original driver by Frank Becker, David Brownell, and others.
+ * Copyright (C) 2008 Robert Jarzmik
+ *
+ * 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
+ *
+ * Taken from linux-2.6 kernel and adapted to barebox.
+ */
+#include <common.h>
+#include <errno.h>
+#include <clock.h>
+#include <io.h>
+#include <gpio.h>
+#include <init.h>
+#include <poller.h>
+
+#include <usb/ch9.h>
+#include <usb/gadget.h>
+
+#include "pxa27x_udc.h"
+#include <mach/udc_pxa2xx.h>
+#include <mach/pxa-regs.h>
+
+#define DRIVER_VERSION "2008-04-18"
+#define DRIVER_DESC "PXA 27x USB Device Controller driver"
+
+static const char driver_name[] = "pxa27x_udc";
+static struct pxa_udc *the_controller;
+
+static void handle_ep(struct pxa_ep *ep);
+
+static int is_match_usb_pxa(struct udc_usb_ep *udc_usb_ep, struct pxa_ep *ep,
+ int config, int interface, int altsetting)
+{
+ if (usb_endpoint_num(&udc_usb_ep->desc) != ep->addr)
+ return 0;
+ if (usb_endpoint_dir_in(&udc_usb_ep->desc) != ep->dir_in)
+ return 0;
+ if (usb_endpoint_type(&udc_usb_ep->desc) != ep->type)
+ return 0;
+ if ((ep->config != config) || (ep->interface != interface)
+ || (ep->alternate != altsetting))
+ return 0;
+ return 1;
+}
+
+static struct pxa_ep *find_pxa_ep(struct pxa_udc *udc,
+ struct udc_usb_ep *udc_usb_ep)
+{
+ int i;
+ struct pxa_ep *ep;
+ int cfg = udc->config;
+ int iface = udc->last_interface;
+ int alt = udc->last_alternate;
+
+ if (udc_usb_ep == &udc->udc_usb_ep[0])
+ return &udc->pxa_ep[0];
+
+ for (i = 1; i < NR_PXA_ENDPOINTS; i++) {
+ ep = &udc->pxa_ep[i];
+ if (is_match_usb_pxa(udc_usb_ep, ep, cfg, iface, alt))
+ return ep;
+ }
+ return NULL;
+}
+
+static void update_pxa_ep_matches(struct pxa_udc *udc)
+{
+ int i;
+ struct udc_usb_ep *udc_usb_ep;
+
+ for (i = 1; i < NR_USB_ENDPOINTS; i++) {
+ udc_usb_ep = &udc->udc_usb_ep[i];
+ if (udc_usb_ep->pxa_ep)
+ udc_usb_ep->pxa_ep = find_pxa_ep(udc, udc_usb_ep);
+ }
+}
+
+static void pio_irq_enable(struct pxa_ep *ep)
+{
+ struct pxa_udc *udc = ep->dev;
+ int index = EPIDX(ep);
+ u32 udcicr0 = udc_readl(udc, UDCICR0);
+ u32 udcicr1 = udc_readl(udc, UDCICR1);
+
+ if (index < 16)
+ udc_writel(udc, UDCICR0, udcicr0 | (3 << (index * 2)));
+ else
+ udc_writel(udc, UDCICR1, udcicr1 | (3 << ((index - 16) * 2)));
+}
+
+static void pio_irq_disable(struct pxa_ep *ep)
+{
+ struct pxa_udc *udc = ep->dev;
+ int index = EPIDX(ep);
+ u32 udcicr0 = udc_readl(udc, UDCICR0);
+ u32 udcicr1 = udc_readl(udc, UDCICR1);
+
+ if (index < 16)
+ udc_writel(udc, UDCICR0, udcicr0 & ~(3 << (index * 2)));
+ else
+ udc_writel(udc, UDCICR1, udcicr1 & ~(3 << ((index - 16) * 2)));
+}
+
+static inline void udc_set_mask_UDCCR(struct pxa_udc *udc, int mask)
+{
+ u32 udccr = udc_readl(udc, UDCCR);
+ udc_writel(udc, UDCCR,
+ (udccr & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS));
+}
+
+static inline void udc_clear_mask_UDCCR(struct pxa_udc *udc, int mask)
+{
+ u32 udccr = udc_readl(udc, UDCCR);
+ udc_writel(udc, UDCCR,
+ (udccr & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS));
+}
+
+static inline void ep_write_UDCCSR(struct pxa_ep *ep, int mask)
+{
+ if (is_ep0(ep))
+ mask |= UDCCSR0_ACM;
+ udc_ep_writel(ep, UDCCSR, mask);
+}
+
+static int ep_count_bytes_remain(struct pxa_ep *ep)
+{
+ if (ep->dir_in)
+ return -EOPNOTSUPP;
+ return udc_ep_readl(ep, UDCBCR) & 0x3ff;
+}
+
+static int ep_is_empty(struct pxa_ep *ep)
+{
+ int ret;
+
+ if (!is_ep0(ep) && ep->dir_in)
+ return -EOPNOTSUPP;
+ if (is_ep0(ep))
+ ret = !(udc_ep_readl(ep, UDCCSR) & UDCCSR0_RNE);
+ else
+ ret = !(udc_ep_readl(ep, UDCCSR) & UDCCSR_BNE);
+ return ret;
+}
+
+static int ep_is_full(struct pxa_ep *ep)
+{
+ if (is_ep0(ep))
+ return udc_ep_readl(ep, UDCCSR) & UDCCSR0_IPR;
+ if (!ep->dir_in)
+ return -EOPNOTSUPP;
+ return !(udc_ep_readl(ep, UDCCSR) & UDCCSR_BNF);
+}
+
+static int epout_has_pkt(struct pxa_ep *ep)
+{
+ if (!is_ep0(ep) && ep->dir_in)
+ return -EOPNOTSUPP;
+ if (is_ep0(ep))
+ return udc_ep_readl(ep, UDCCSR) & UDCCSR0_OPC;
+ return udc_ep_readl(ep, UDCCSR) & UDCCSR_PC;
+}
+
+static void set_ep0state(struct pxa_udc *udc, int state)
+{
+ struct pxa_ep *ep = &udc->pxa_ep[0];
+ char *old_stname = EP0_STNAME(udc);
+
+ udc->ep0state = state;
+ ep_dbg(ep, "state=%s->%s, udccsr0=0x%03x, udcbcr=%d\n", old_stname,
+ EP0_STNAME(udc), udc_ep_readl(ep, UDCCSR),
+ udc_ep_readl(ep, UDCBCR));
+}
+
+static void ep0_idle(struct pxa_udc *dev)
+{
+ set_ep0state(dev, WAIT_FOR_SETUP);
+}
+
+static __init void pxa_ep_setup(struct pxa_ep *ep)
+{
+ u32 new_udccr;
+
+ new_udccr = ((ep->config << UDCCONR_CN_S) & UDCCONR_CN)
+ | ((ep->interface << UDCCONR_IN_S) & UDCCONR_IN)
+ | ((ep->alternate << UDCCONR_AISN_S) & UDCCONR_AISN)
+ | ((EPADDR(ep) << UDCCONR_EN_S) & UDCCONR_EN)
+ | ((EPXFERTYPE(ep) << UDCCONR_ET_S) & UDCCONR_ET)
+ | ((ep->dir_in) ? UDCCONR_ED : 0)
+ | ((ep->fifo_size << UDCCONR_MPS_S) & UDCCONR_MPS)
+ | UDCCONR_EE;
+
+ udc_ep_writel(ep, UDCCR, new_udccr);
+}
+
+static __init void pxa_eps_setup(struct pxa_udc *dev)
+{
+ unsigned int i;
+
+ dev_dbg(dev->dev, "%s: dev=%p\n", __func__, dev);
+
+ for (i = 1; i < NR_PXA_ENDPOINTS; i++)
+ pxa_ep_setup(&dev->pxa_ep[i]);
+}
+
+static struct usb_request *pxa_ep_alloc_request(struct usb_ep *_ep)
+{
+ struct pxa27x_request *req;
+
+ req = xzalloc(sizeof *req);
+ if (!req)
+ return NULL;
+
+ INIT_LIST_HEAD(&req->queue);
+ req->in_use = 0;
+ req->udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep);
+
+ return &req->req;
+}
+
+static void pxa_ep_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct pxa27x_request *req;
+
+ req = container_of(_req, struct pxa27x_request, req);
+ WARN_ON(!list_empty(&req->queue));
+ kfree(req);
+}
+
+static void ep_add_request(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+ if (unlikely(!req))
+ return;
+ ep_vdbg(ep, "req:%p, lg=%d, udccsr=0x%03x\n", req,
+ req->req.length, udc_ep_readl(ep, UDCCSR));
+
+ req->in_use = 1;
+ list_add_tail(&req->queue, &ep->queue);
+ pio_irq_enable(ep);
+}
+
+static void ep_del_request(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+ if (unlikely(!req))
+ return;
+ ep_vdbg(ep, "req:%p, lg=%d, udccsr=0x%03x\n", req,
+ req->req.length, udc_ep_readl(ep, UDCCSR));
+
+ list_del_init(&req->queue);
+ req->in_use = 0;
+ if (!is_ep0(ep) && list_empty(&ep->queue))
+ pio_irq_disable(ep);
+}
+
+static void req_done(struct pxa_ep *ep, struct pxa27x_request *req, int status)
+{
+ ep_del_request(ep, req);
+ if (likely(req->req.status == -EINPROGRESS))
+ req->req.status = status;
+ else
+ status = req->req.status;
+
+ if (status && status != -ESHUTDOWN)
+ ep_dbg(ep, "complete req %p stat %d len %u/%u\n",
+ &req->req, status,
+ req->req.actual, req->req.length);
+
+ req->req.complete(&req->udc_usb_ep->usb_ep, &req->req);
+}
+
+static void ep_end_out_req(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+ req_done(ep, req, 0);
+}
+
+static void ep0_end_out_req(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+ set_ep0state(ep->dev, OUT_STATUS_STAGE);
+ ep_end_out_req(ep, req);
+ ep0_idle(ep->dev);
+}
+
+static void ep_end_in_req(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+ req_done(ep, req, 0);
+}
+
+static void ep0_end_in_req(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+ set_ep0state(ep->dev, IN_STATUS_STAGE);
+ ep_end_in_req(ep, req);
+}
+
+static void nuke(struct pxa_ep *ep, int status)
+{
+ struct pxa27x_request *req;
+
+ while (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct pxa27x_request, queue);
+ req_done(ep, req, status);
+ }
+}
+
+static int read_packet(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+ u32 *buf;
+ int bytes_ep, bufferspace, count, i;
+
+ bytes_ep = ep_count_bytes_remain(ep);
+ bufferspace = req->req.length - req->req.actual;
+
+ buf = (u32 *)(req->req.buf + req->req.actual);
+
+ if (likely(!ep_is_empty(ep)))
+ count = min(bytes_ep, bufferspace);
+ else /* zlp */
+ count = 0;
+
+ for (i = count; i > 0; i -= 4)
+ *buf++ = udc_ep_readl(ep, UDCDR);
+ req->req.actual += count;
+
+ ep_write_UDCCSR(ep, UDCCSR_PC);
+
+ return count;
+}
+
+static int write_packet(struct pxa_ep *ep, struct pxa27x_request *req,
+ unsigned int max)
+{
+ int length, count, remain, i;
+ u32 *buf;
+ u8 *buf_8;
+
+ buf = (u32 *)(req->req.buf + req->req.actual);
+ prefetch(buf);
+
+ length = min(req->req.length - req->req.actual, max);
+ req->req.actual += length;
+
+ remain = length & 0x3;
+ count = length & ~(0x3);
+ for (i = count; i > 0 ; i -= 4)
+ udc_ep_writel(ep, UDCDR, *buf++);
+
+ buf_8 = (u8 *)buf;
+ for (i = remain; i > 0; i--)
+ udc_ep_writeb(ep, UDCDR, *buf_8++);
+
+ ep_vdbg(ep, "length=%d+%d, udccsr=0x%03x\n", count, remain,
+ udc_ep_readl(ep, UDCCSR));
+
+ return length;
+}
+
+static int read_fifo(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+ int count, is_short, completed = 0;
+
+ while (epout_has_pkt(ep)) {
+ count = read_packet(ep, req);
+
+ is_short = (count < ep->fifo_size);
+ ep_dbg(ep, "read udccsr:%03x, count:%d bytes%s req %p %d/%d\n",
+ udc_ep_readl(ep, UDCCSR), count, is_short ? "/S" : "",
+ &req->req, req->req.actual, req->req.length);
+
+ /* completion */
+ if (is_short || req->req.actual == req->req.length) {
+ completed = 1;
+ break;
+ }
+ /* finished that packet. the next one may be waiting... */
+ }
+ return completed;
+}
+
+static int write_fifo(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+ unsigned max;
+ int count, is_short, is_last = 0, completed = 0, totcount = 0;
+ u32 udccsr;
+
+ max = ep->fifo_size;
+ do {
+ is_short = 0;
+
+ udccsr = udc_ep_readl(ep, UDCCSR);
+ if (udccsr & UDCCSR_PC) {
+ ep_vdbg(ep, "Clearing Transmit Complete, udccsr=%x\n",
+ udccsr);
+ ep_write_UDCCSR(ep, UDCCSR_PC);
+ }
+ if (udccsr & UDCCSR_TRN) {
+ ep_vdbg(ep, "Clearing Underrun on, udccsr=%x\n",
+ udccsr);
+ ep_write_UDCCSR(ep, UDCCSR_TRN);
+ }
+
+ count = write_packet(ep, req, max);
+ totcount += count;
+
+ /* last packet is usually short (or a zlp) */
+ if (unlikely(count < max)) {
+ is_last = 1;
+ is_short = 1;
+ } else {
+ if (likely(req->req.length > req->req.actual)
+ || req->req.zero)
+ is_last = 0;
+ else
+ is_last = 1;
+ /* interrupt/iso maxpacket may not fill the fifo */
+ is_short = unlikely(max < ep->fifo_size);
+ }
+
+ if (is_short)
+ ep_write_UDCCSR(ep, UDCCSR_SP);
+
+ /* requests complete when all IN data is in the FIFO */
+ if (is_last) {
+ completed = 1;
+ break;
+ }
+ } while (!ep_is_full(ep));
+
+ ep_dbg(ep, "wrote count:%d bytes%s%s, left:%d req=%p\n",
+ totcount, is_last ? "/L" : "", is_short ? "/S" : "",
+ req->req.length - req->req.actual, &req->req);
+
+ return completed;
+}
+
+static int read_ep0_fifo(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+ int count, is_short, completed = 0;
+
+ while (epout_has_pkt(ep)) {
+ count = read_packet(ep, req);
+ ep_write_UDCCSR(ep, UDCCSR0_OPC);
+
+ is_short = (count < ep->fifo_size);
+ ep_dbg(ep, "read udccsr:%03x, count:%d bytes%s req %p %d/%d\n",
+ udc_ep_readl(ep, UDCCSR), count, is_short ? "/S" : "",
+ &req->req, req->req.actual, req->req.length);
+
+ if (is_short || req->req.actual >= req->req.length) {
+ completed = 1;
+ break;
+ }
+ }
+
+ return completed;
+}
+
+static int write_ep0_fifo(struct pxa_ep *ep, struct pxa27x_request *req)
+{
+ unsigned count;
+ int is_last, is_short;
+
+ count = write_packet(ep, req, EP0_FIFO_SIZE);
+
+ is_short = (count < EP0_FIFO_SIZE);
+ is_last = ((count == 0) || (count < EP0_FIFO_SIZE));
+
+ /* Sends either a short packet or a 0 length packet */
+ if (unlikely(is_short))
+ ep_write_UDCCSR(ep, UDCCSR0_IPR);
+
+ ep_dbg(ep, "in %d bytes%s%s, %d left, req=%p, udccsr0=0x%03x\n",
+ count, is_short ? "/S" : "", is_last ? "/L" : "",
+ req->req.length - req->req.actual,
+ &req->req, udc_ep_readl(ep, UDCCSR));
+
+ return is_last;
+}
+
+static int pxa_ep_queue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct udc_usb_ep *udc_usb_ep;
+ struct pxa_ep *ep;
+ struct pxa27x_request *req;
+ struct pxa_udc *dev;
+ int rc = 0;
+ int is_first_req;
+ unsigned length;
+
+ req = container_of(_req, struct pxa27x_request, req);
+ udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep);
+
+ if (unlikely(!_req || !_req->complete || !_req->buf))
+ return -EINVAL;
+
+ if (unlikely(!_ep))
+ return -EINVAL;
+
+ dev = udc_usb_ep->dev;
+ ep = udc_usb_ep->pxa_ep;
+ if (unlikely(!ep))
+ return -EINVAL;
+
+ dev = ep->dev;
+ if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) {
+ ep_dbg(ep, "bogus device state\n");
+ return -ESHUTDOWN;
+ }
+
+ /* iso is always one packet per request, that's the only way
+ * we can report per-packet status. that also helps with dma.
+ */
+ if (unlikely(EPXFERTYPE_is_ISO(ep)
+ && req->req.length > ep->fifo_size))
+ return -EMSGSIZE;
+
+ is_first_req = list_empty(&ep->queue);
+ ep_dbg(ep, "queue req %p(first=%s), len %d buf %p\n",
+ _req, is_first_req ? "yes" : "no",
+ _req->length, _req->buf);
+
+ if (!ep->enabled) {
+ _req->status = -ESHUTDOWN;
+ rc = -ESHUTDOWN;
+ goto out_locked;
+ }
+
+ if (req->in_use) {
+ ep_err(ep, "refusing to queue req %p (already queued)\n", req);
+ goto out_locked;
+ }
+
+ length = _req->length;
+ _req->status = -EINPROGRESS;
+ _req->actual = 0;
+
+ ep_add_request(ep, req);
+
+ if (is_ep0(ep)) {
+ switch (dev->ep0state) {
+ case WAIT_ACK_SET_CONF_INTERF:
+ if (length == 0) {
+ ep_end_in_req(ep, req);
+ } else {
+ ep_err(ep, "got a request of %d bytes while"
+ "in state WAIT_ACK_SET_CONF_INTERF\n",
+ length);
+ ep_del_request(ep, req);
+ rc = -EL2HLT;
+ }
+ ep0_idle(ep->dev);
+ break;
+ case IN_DATA_STAGE:
+ if (!ep_is_full(ep))
+ if (write_ep0_fifo(ep, req))
+ ep0_end_in_req(ep, req);
+ break;
+ case OUT_DATA_STAGE:
+ if ((length == 0) || !epout_has_pkt(ep))
+ if (read_ep0_fifo(ep, req))
+ ep0_end_out_req(ep, req);
+ break;
+ default:
+ ep_err(ep, "odd state %s to send me a request\n",
+ EP0_STNAME(ep->dev));
+ ep_del_request(ep, req);
+ rc = -EL2HLT;
+ break;
+ }
+ } else {
+ handle_ep(ep);
+ }
+
+out:
+ return rc;
+out_locked:
+ goto out;
+}
+
+static int pxa_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct pxa_ep *ep;
+ struct udc_usb_ep *udc_usb_ep;
+ struct pxa27x_request *req;
+ int rc = -EINVAL;
+
+ if (!_ep)
+ return rc;
+ udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep);
+ ep = udc_usb_ep->pxa_ep;
+ if (!ep || is_ep0(ep))
+ return rc;
+
+ /* make sure it's actually queued on this endpoint */
+ list_for_each_entry(req, &ep->queue, queue) {
+ if (&req->req == _req) {
+ rc = 0;
+ break;
+ }
+ }
+
+ if (!rc)
+ req_done(ep, req, -ECONNRESET);
+ return rc;
+}
+
+static int pxa_ep_set_halt(struct usb_ep *_ep, int value)
+{
+ struct pxa_ep *ep;
+ struct udc_usb_ep *udc_usb_ep;
+ int rc;
+
+
+ if (!_ep)
+ return -EINVAL;
+ udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep);
+ ep = udc_usb_ep->pxa_ep;
+ if (!ep || is_ep0(ep))
+ return -EINVAL;
+
+ if (value == 0) {
+ /*
+ * This path (reset toggle+halt) is needed to implement
+ * SET_INTERFACE on normal hardware. but it can't be
+ * done from software on the PXA UDC, and the hardware
+ * forgets to do it as part of SET_INTERFACE automagic.
+ */
+ ep_dbg(ep, "only host can clear halt\n");
+ return -EROFS;
+ }
+
+ rc = -EAGAIN;
+ if (ep->dir_in && (ep_is_full(ep) || !list_empty(&ep->queue)))
+ goto out;
+
+ /* FST, FEF bits are the same for control and non control endpoints */
+ rc = 0;
+ ep_write_UDCCSR(ep, UDCCSR_FST | UDCCSR_FEF);
+ if (is_ep0(ep))
+ set_ep0state(ep->dev, STALL);
+
+out:
+ return rc;
+}
+
+static int pxa_ep_fifo_status(struct usb_ep *_ep)
+{
+ struct pxa_ep *ep;
+ struct udc_usb_ep *udc_usb_ep;
+
+ if (!_ep)
+ return -ENODEV;
+ udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep);
+ ep = udc_usb_ep->pxa_ep;
+ if (!ep || is_ep0(ep))
+ return -ENODEV;
+
+ if (ep->dir_in)
+ return -EOPNOTSUPP;
+ if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN || ep_is_empty(ep))
+ return 0;
+ else
+ return ep_count_bytes_remain(ep) + 1;
+}
+
+static void pxa_ep_fifo_flush(struct usb_ep *_ep)
+{
+ struct pxa_ep *ep;
+ struct udc_usb_ep *udc_usb_ep;
+
+ if (!_ep)
+ return;
+ udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep);
+ ep = udc_usb_ep->pxa_ep;
+ if (!ep || is_ep0(ep))
+ return;
+
+ if (unlikely(!list_empty(&ep->queue)))
+ ep_dbg(ep, "called while queue list not empty\n");
+ ep_dbg(ep, "called\n");
+
+ /* for OUT, just read and discard the FIFO contents. */
+ if (!ep->dir_in) {
+ while (!ep_is_empty(ep))
+ udc_ep_readl(ep, UDCDR);
+ } else {
+ /* most IN status is the same, but ISO can't stall */
+ ep_write_UDCCSR(ep,
+ UDCCSR_PC | UDCCSR_FEF | UDCCSR_TRN
+ | (EPXFERTYPE_is_ISO(ep) ? 0 : UDCCSR_SST));
+ }
+}
+
+static int pxa_ep_enable(struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct pxa_ep *ep;
+ struct udc_usb_ep *udc_usb_ep;
+ struct pxa_udc *udc;
+
+ if (!_ep || !desc)
+ return -EINVAL;
+
+ udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep);
+ if (udc_usb_ep->pxa_ep) {
+ ep = udc_usb_ep->pxa_ep;
+ ep_warn(ep, "usb_ep %s already enabled, doing nothing\n",
+ _ep->name);
+ } else {
+ ep = find_pxa_ep(udc_usb_ep->dev, udc_usb_ep);
+ }
+
+ if (!ep || is_ep0(ep)) {
+ dev_err(udc_usb_ep->dev->dev,
+ "unable to match pxa_ep for ep %s\n",
+ _ep->name);
+ return -EINVAL;
+ }
+
+ if ((desc->bDescriptorType != USB_DT_ENDPOINT)
+ || (ep->type != usb_endpoint_type(desc))) {
+ ep_err(ep, "type mismatch\n");
+ return -EINVAL;
+ }
+
+ if (ep->fifo_size < le16_to_cpu(desc->wMaxPacketSize)) {
+ ep_err(ep, "bad maxpacket\n");
+ return -ERANGE;
+ }
+
+ udc_usb_ep->pxa_ep = ep;
+ udc = ep->dev;
+
+ if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) {
+ ep_err(ep, "bogus device state\n");
+ return -ESHUTDOWN;
+ }
+
+ ep->enabled = 1;
+
+ /* flush fifo (mostly for OUT buffers) */
+ pxa_ep_fifo_flush(_ep);
+
+ ep_dbg(ep, "enabled\n");
+ return 0;
+}
+
+static int pxa_ep_disable(struct usb_ep *_ep)
+{
+ struct pxa_ep *ep;
+ struct udc_usb_ep *udc_usb_ep;
+
+ if (!_ep)
+ return -EINVAL;
+
+ udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep);
+ ep = udc_usb_ep->pxa_ep;
+ if (!ep || is_ep0(ep) || !list_empty(&ep->queue))
+ return -EINVAL;
+
+ ep->enabled = 0;
+ nuke(ep, -ESHUTDOWN);
+
+ pxa_ep_fifo_flush(_ep);
+ udc_usb_ep->pxa_ep = NULL;
+
+ ep_dbg(ep, "disabled\n");
+ return 0;
+}
+
+static struct usb_ep_ops pxa_ep_ops = {
+ .enable = pxa_ep_enable,
+ .disable = pxa_ep_disable,
+
+ .alloc_request = pxa_ep_alloc_request,
+ .free_request = pxa_ep_free_request,
+
+ .queue = pxa_ep_queue,
+ .dequeue = pxa_ep_dequeue,
+
+ .set_halt = pxa_ep_set_halt,
+ .fifo_status = pxa_ep_fifo_status,
+ .fifo_flush = pxa_ep_fifo_flush,
+};
+
+static void dplus_pullup(struct pxa_udc *udc, int on)
+{
+ if (on) {
+ if (udc->mach->gpio_pullup > 0)
+ gpio_set_value(udc->mach->gpio_pullup,
+ !udc->mach->gpio_pullup_inverted);
+ if (udc->mach->udc_command)
+ udc->mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
+ } else {
+ if (udc->mach->gpio_pullup > 0)
+ gpio_set_value(udc->mach->gpio_pullup,
+ udc->mach->gpio_pullup_inverted);
+ if (udc->mach->udc_command)
+ udc->mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
+ }
+ udc->pullup_on = on;
+}
+
+/**
+ * pxa_udc_get_frame - Returns usb frame number
+ * @_gadget: usb gadget
+ */
+static int pxa_udc_get_frame(struct usb_gadget *_gadget)
+{
+ struct pxa_udc *udc = to_gadget_udc(_gadget);
+
+ return udc_readl(udc, UDCFNR) & 0x7ff;
+}
+
+static int pxa_udc_wakeup(struct usb_gadget *_gadget)
+{
+ struct pxa_udc *udc = to_gadget_udc(_gadget);
+
+ /* host may not have enabled remote wakeup */
+ if ((udc_readl(udc, UDCCR) & UDCCR_DWRE) == 0)
+ return -EHOSTUNREACH;
+ udc_set_mask_UDCCR(udc, UDCCR_UDR);
+ return 0;
+}
+
+static void udc_enable(struct pxa_udc *udc);
+static void udc_disable(struct pxa_udc *udc);
+
+static int should_enable_udc(struct pxa_udc *udc)
+{
+ int put_on;
+
+ put_on = ((udc->pullup_on) && (udc->driver));
+ put_on &= udc->vbus_sensed;
+ return put_on;
+}
+
+static int should_disable_udc(struct pxa_udc *udc)
+{
+ int put_off;
+
+ put_off = ((!udc->pullup_on) || (!udc->driver));
+ put_off |= !udc->vbus_sensed;
+ return put_off;
+}
+
+static int pxa_udc_pullup(struct usb_gadget *_gadget, int is_active)
+{
+ struct pxa_udc *udc = to_gadget_udc(_gadget);
+
+ if ((udc->mach->gpio_pullup < 0) && !udc->mach->udc_command)
+ return -EOPNOTSUPP;
+
+ dplus_pullup(udc, is_active);
+
+ if (should_enable_udc(udc))
+ udc_enable(udc);
+ if (should_disable_udc(udc))
+ udc_disable(udc);
+ return 0;
+}
+
+static int pxa_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
+{
+ struct pxa_udc *udc = to_gadget_udc(_gadget);
+
+ udc->vbus_sensed = is_active;
+ if (should_enable_udc(udc))
+ udc_enable(udc);
+ if (should_disable_udc(udc))
+ udc_disable(udc);
+
+ return 0;
+}
+
+static const struct usb_gadget_ops pxa_udc_ops = {
+ .get_frame = pxa_udc_get_frame,
+ .wakeup = pxa_udc_wakeup,
+ .pullup = pxa_udc_pullup,
+ .vbus_session = pxa_udc_vbus_session,
+};
+
+static void clk_enable(void)
+{
+ CKEN |= CKEN_USB;
+}
+
+static void clk_disable(void)
+{
+ CKEN &= ~CKEN_USB;
+}
+
+static void udc_disable(struct pxa_udc *udc)
+{
+ if (!udc->enabled)
+ return;
+
+ udc_writel(udc, UDCICR0, 0);
+ udc_writel(udc, UDCICR1, 0);
+
+ udc_clear_mask_UDCCR(udc, UDCCR_UDE);
+ clk_disable();
+
+ ep0_idle(udc);
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+
+ udc->enabled = 0;
+}
+
+static __init void udc_init_data(struct pxa_udc *dev)
+{
+ int i;
+ struct pxa_ep *ep;
+
+ /* device/ep0 records init */
+ INIT_LIST_HEAD(&dev->gadget.ep_list);
+ INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+ dev->udc_usb_ep[0].pxa_ep = &dev->pxa_ep[0];
+ ep0_idle(dev);
+
+ /* PXA endpoints init */
+ for (i = 0; i < NR_PXA_ENDPOINTS; i++) {
+ ep = &dev->pxa_ep[i];
+
+ ep->enabled = is_ep0(ep);
+ INIT_LIST_HEAD(&ep->queue);
+ }
+
+ /* USB endpoints init */
+ for (i = 1; i < NR_USB_ENDPOINTS; i++)
+ list_add_tail(&dev->udc_usb_ep[i].usb_ep.ep_list,
+ &dev->gadget.ep_list);
+}
+
+static void udc_enable(struct pxa_udc *udc)
+{
+ if (udc->enabled)
+ return;
+
+ udc_writel(udc, UDCICR0, 0);
+ udc_writel(udc, UDCICR1, 0);
+ udc_clear_mask_UDCCR(udc, UDCCR_UDE);
+
+ clk_enable();
+
+ ep0_idle(udc);
+ udc->gadget.speed = USB_SPEED_FULL;
+ memset(&udc->stats, 0, sizeof(udc->stats));
+
+ udc_set_mask_UDCCR(udc, UDCCR_UDE);
+ ep_write_UDCCSR(&udc->pxa_ep[0], UDCCSR0_ACM);
+ udelay(2);
+ if (udc_readl(udc, UDCCR) & UDCCR_EMCE)
+ dev_err(udc->dev, "Configuration errors, udc disabled\n");
+
+ /*
+ * Caller must be able to sleep in order to cope with startup transients
+ */
+ mdelay(100);
+
+ /* enable suspend/resume and reset irqs */
+ udc_writel(udc, UDCICR1,
+ UDCICR1_IECC | UDCICR1_IERU
+ | UDCICR1_IESU | UDCICR1_IERS);
+
+ pio_irq_enable(&udc->pxa_ep[0]);
+ udc->enabled = 1;
+}
+
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+ struct pxa_udc *udc = the_controller;
+ int retval;
+
+ if (!driver || driver->speed < USB_SPEED_FULL || !driver->bind
+ || !driver->disconnect || !driver->setup)
+ return -EINVAL;
+ if (!udc)
+ return -ENODEV;
+ if (udc->driver)
+ return -EBUSY;
+
+ /* first hook up the driver ... */
+ udc->driver = driver;
+ dplus_pullup(udc, 1);
+
+ retval = driver->bind(&udc->gadget);
+ if (retval) {
+ dev_err(udc->dev, "bind to function %s --> error %d\n",
+ driver->function, retval);
+ goto bind_fail;
+ }
+ dev_dbg(udc->dev, "registered gadget function '%s'\n",
+ driver->function);
+
+ if (should_enable_udc(udc))
+ udc_enable(udc);
+ return 0;
+
+bind_fail:
+ return retval;
+}
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+static void stop_activity(struct pxa_udc *udc, struct usb_gadget_driver *driver)
+{
+ int i;
+
+ /* don't disconnect drivers more than once */
+ if (udc->gadget.speed == USB_SPEED_UNKNOWN)
+ driver = NULL;
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+
+ for (i = 0; i < NR_USB_ENDPOINTS; i++)
+ pxa_ep_disable(&udc->udc_usb_ep[i].usb_ep);
+
+ if (driver)
+ driver->disconnect(&udc->gadget);
+}
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ struct pxa_udc *udc = the_controller;
+
+ if (!udc)
+ return -ENODEV;
+ if (!driver || driver != udc->driver || !driver->unbind)
+ return -EINVAL;
+
+ stop_activity(udc, driver);
+ udc_disable(udc);
+ dplus_pullup(udc, 0);
+
+ driver->disconnect(&udc->gadget);
+ driver->unbind(&udc->gadget);
+ udc->driver = NULL;
+
+ /*
+ dev_info(udc->dev, "unregistered gadget driver '%s'\n",
+ driver->driver.name);
+ */
+ return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+static void handle_ep0_ctrl_req(struct pxa_udc *udc,
+ struct pxa27x_request *req)
+{
+ struct pxa_ep *ep = &udc->pxa_ep[0];
+ union {
+ struct usb_ctrlrequest r;
+ u32 word[2];
+ } u;
+ int i;
+ int have_extrabytes = 0;
+
+ nuke(ep, -EPROTO);
+
+ /*
+ * In the PXA320 manual, in the section about Back-to-Back setup
+ * packets, it describes this situation. The solution is to set OPC to
+ * get rid of the status packet, and then continue with the setup
+ * packet. Generalize to pxa27x CPUs.
+ */
+ if (epout_has_pkt(ep) && (ep_count_bytes_remain(ep) == 0))
+ ep_write_UDCCSR(ep, UDCCSR0_OPC);
+
+ /* read SETUP packet */
+ for (i = 0; i < 2; i++) {
+ if (unlikely(ep_is_empty(ep)))
+ goto stall;
+ u.word[i] = udc_ep_readl(ep, UDCDR);
+ }
+
+ have_extrabytes = !ep_is_empty(ep);
+ while (!ep_is_empty(ep)) {
+ i = udc_ep_readl(ep, UDCDR);
+ ep_err(ep, "wrong to have extra bytes for setup : 0x%08x\n", i);
+ }
+
+ ep_dbg(ep, "SETUP %02x.%02x v%04x i%04x l%04x\n",
+ u.r.bRequestType, u.r.bRequest,
+ le16_to_cpu(u.r.wValue), le16_to_cpu(u.r.wIndex),
+ le16_to_cpu(u.r.wLength));
+ if (unlikely(have_extrabytes))
+ goto stall;
+
+ if (u.r.bRequestType & USB_DIR_IN)
+ set_ep0state(udc, IN_DATA_STAGE);
+ else
+ set_ep0state(udc, OUT_DATA_STAGE);
+
+ /* Tell UDC to enter Data Stage */
+ ep_write_UDCCSR(ep, UDCCSR0_SA | UDCCSR0_OPC);
+
+ i = udc->driver->setup(&udc->gadget, &u.r);
+ if (i < 0)
+ goto stall;
+out:
+ return;
+stall:
+ ep_dbg(ep, "protocol STALL, udccsr0=%03x err %d\n",
+ udc_ep_readl(ep, UDCCSR), i);
+ ep_write_UDCCSR(ep, UDCCSR0_FST | UDCCSR0_FTF);
+ set_ep0state(udc, STALL);
+ goto out;
+}
+
+static void handle_ep0(struct pxa_udc *udc, int fifo_irq, int opc_irq)
+{
+ u32 udccsr0;
+ struct pxa_ep *ep = &udc->pxa_ep[0];
+ struct pxa27x_request *req = NULL;
+ int completed = 0;
+
+ if (!list_empty(&ep->queue))
+ req = list_entry(ep->queue.next, struct pxa27x_request, queue);
+
+ udccsr0 = udc_ep_readl(ep, UDCCSR);
+ ep_dbg(ep, "state=%s, req=%p, udccsr0=0x%03x, udcbcr=%d, irq_msk=%x\n",
+ EP0_STNAME(udc), req, udccsr0, udc_ep_readl(ep, UDCBCR),
+ (fifo_irq << 1 | opc_irq));
+
+ if (udccsr0 & UDCCSR0_SST) {
+ ep_dbg(ep, "clearing stall status\n");
+ nuke(ep, -EPIPE);
+ ep_write_UDCCSR(ep, UDCCSR0_SST);
+ ep0_idle(udc);
+ }
+
+ if (udccsr0 & UDCCSR0_SA) {
+ nuke(ep, 0);
+ set_ep0state(udc, SETUP_STAGE);
+ }
+
+ switch (udc->ep0state) {
+ case WAIT_FOR_SETUP:
+ /*
+ * Hardware bug : beware, we cannot clear OPC, since we would
+ * miss a potential OPC irq for a setup packet.
+ * So, we only do ... nothing, and hope for a next irq with
+ * UDCCSR0_SA set.
+ */
+ break;
+ case SETUP_STAGE:
+ udccsr0 &= UDCCSR0_CTRL_REQ_MASK;
+ if (likely(udccsr0 == UDCCSR0_CTRL_REQ_MASK))
+ handle_ep0_ctrl_req(udc, req);
+ break;
+ case IN_DATA_STAGE: /* GET_DESCRIPTOR */
+ if (epout_has_pkt(ep))
+ ep_write_UDCCSR(ep, UDCCSR0_OPC);
+ if (req && !ep_is_full(ep))
+ completed = write_ep0_fifo(ep, req);
+ if (completed)
+ ep0_end_in_req(ep, req);
+ break;
+ case OUT_DATA_STAGE: /* SET_DESCRIPTOR */
+ if (epout_has_pkt(ep) && req)
+ completed = read_ep0_fifo(ep, req);
+ if (completed)
+ ep0_end_out_req(ep, req);
+ break;
+ case STALL:
+ ep_write_UDCCSR(ep, UDCCSR0_FST);
+ break;
+ case IN_STATUS_STAGE:
+ /*
+ * Hardware bug : beware, we cannot clear OPC, since we would
+ * miss a potential PC irq for a setup packet.
+ * So, we only put the ep0 into WAIT_FOR_SETUP state.
+ */
+ if (opc_irq)
+ ep0_idle(udc);
+ break;
+ case OUT_STATUS_STAGE:
+ case WAIT_ACK_SET_CONF_INTERF:
+ ep_warn(ep, "should never get in %s state here!!!\n",
+ EP0_STNAME(ep->dev));
+ ep0_idle(udc);
+ break;
+ }
+}
+
+static void handle_ep(struct pxa_ep *ep)
+{
+ struct pxa27x_request *req;
+ int completed;
+ u32 udccsr;
+ int is_in = ep->dir_in;
+ int loop = 0;
+
+ do {
+ completed = 0;
+ udccsr = udc_ep_readl(ep, UDCCSR);
+
+ if (likely(!list_empty(&ep->queue)))
+ req = list_entry(ep->queue.next,
+ struct pxa27x_request, queue);
+ else
+ req = NULL;
+
+ ep_dbg(ep, "req:%p, udccsr 0x%03x loop=%d\n",
+ req, udccsr, loop++);
+
+ if (unlikely(udccsr & (UDCCSR_SST | UDCCSR_TRN)))
+ udc_ep_writel(ep, UDCCSR,
+ udccsr & (UDCCSR_SST | UDCCSR_TRN));
+ if (!req)
+ break;
+
+ if (unlikely(is_in)) {
+ if (likely(!ep_is_full(ep)))
+ completed = write_fifo(ep, req);
+ } else {
+ if (likely(epout_has_pkt(ep)))
+ completed = read_fifo(ep, req);
+ }
+
+ if (completed) {
+ if (is_in)
+ ep_end_in_req(ep, req);
+ else
+ ep_end_out_req(ep, req);
+ }
+ } while (completed);
+
+ return;
+}
+
+static void pxa27x_change_configuration(struct pxa_udc *udc, int config)
+{
+ struct usb_ctrlrequest req ;
+
+ dev_dbg(udc->dev, "config=%d\n", config);
+
+ udc->config = config;
+ udc->last_interface = 0;
+ udc->last_alternate = 0;
+
+ req.bRequestType = 0;
+ req.bRequest = USB_REQ_SET_CONFIGURATION;
+ req.wValue = config;
+ req.wIndex = 0;
+ req.wLength = 0;
+
+ set_ep0state(udc, WAIT_ACK_SET_CONF_INTERF);
+ udc->driver->setup(&udc->gadget, &req);
+}
+
+static void pxa27x_change_interface(struct pxa_udc *udc, int iface, int alt)
+{
+ struct usb_ctrlrequest req;
+
+ dev_dbg(udc->dev, "interface=%d, alternate setting=%d\n", iface, alt);
+
+ udc->last_interface = iface;
+ udc->last_alternate = alt;
+
+ req.bRequestType = USB_RECIP_INTERFACE;
+ req.bRequest = USB_REQ_SET_INTERFACE;
+ req.wValue = alt;
+ req.wIndex = iface;
+ req.wLength = 0;
+
+ set_ep0state(udc, WAIT_ACK_SET_CONF_INTERF);
+ ep_write_UDCCSR(&udc->pxa_ep[0], UDCCSR0_AREN);
+ udc->driver->setup(&udc->gadget, &req);
+}
+
+static void irq_handle_data(struct pxa_udc *udc)
+{
+ int i;
+ struct pxa_ep *ep;
+ u32 udcisr0 = udc_readl(udc, UDCISR0) & UDCCISR0_EP_MASK;
+ u32 udcisr1 = udc_readl(udc, UDCISR1) & UDCCISR1_EP_MASK;
+
+ if (udcisr0 & UDCISR_INT_MASK) {
+ udc_writel(udc, UDCISR0, UDCISR_INT(0, UDCISR_INT_MASK));
+ handle_ep0(udc, !!(udcisr0 & UDCICR_FIFOERR),
+ !!(udcisr0 & UDCICR_PKTCOMPL));
+ }
+
+ udcisr0 >>= 2;
+ for (i = 1; udcisr0 != 0 && i < 16; udcisr0 >>= 2, i++) {
+ if (!(udcisr0 & UDCISR_INT_MASK))
+ continue;
+
+ udc_writel(udc, UDCISR0, UDCISR_INT(i, UDCISR_INT_MASK));
+
+ WARN_ON(i >= ARRAY_SIZE(udc->pxa_ep));
+ if (i < ARRAY_SIZE(udc->pxa_ep)) {
+ ep = &udc->pxa_ep[i];
+ if (ep->enabled)
+ handle_ep(ep);
+ }
+ }
+
+ for (i = 16; udcisr1 != 0 && i < 24; udcisr1 >>= 2, i++) {
+ udc_writel(udc, UDCISR1, UDCISR_INT(i - 16, UDCISR_INT_MASK));
+ if (!(udcisr1 & UDCISR_INT_MASK))
+ continue;
+
+ WARN_ON(i >= ARRAY_SIZE(udc->pxa_ep));
+ if (i < ARRAY_SIZE(udc->pxa_ep)) {
+ ep = &udc->pxa_ep[i];
+ if (ep->enabled)
+ handle_ep(ep);
+ }
+ }
+
+}
+
+static void irq_udc_suspend(struct pxa_udc *udc)
+{
+ udc_writel(udc, UDCISR1, UDCISR1_IRSU);
+
+ if (udc->gadget.speed != USB_SPEED_UNKNOWN
+ && udc->driver && udc->driver->suspend)
+ udc->driver->suspend(&udc->gadget);
+ ep0_idle(udc);
+}
+
+static void irq_udc_resume(struct pxa_udc *udc)
+{
+ udc_writel(udc, UDCISR1, UDCISR1_IRRU);
+
+ if (udc->gadget.speed != USB_SPEED_UNKNOWN
+ && udc->driver && udc->driver->resume)
+ udc->driver->resume(&udc->gadget);
+}
+
+static void irq_udc_reconfig(struct pxa_udc *udc)
+{
+ unsigned config, interface, alternate, config_change;
+ u32 udccr = udc_readl(udc, UDCCR);
+
+ udc_writel(udc, UDCISR1, UDCISR1_IRCC);
+
+ config = (udccr & UDCCR_ACN) >> UDCCR_ACN_S;
+ config_change = (config != udc->config);
+ if (config_change)
+ update_pxa_ep_matches(udc);
+ udc_set_mask_UDCCR(udc, UDCCR_SMAC);
+ ep_write_UDCCSR(&udc->pxa_ep[0], UDCCSR0_AREN);
+
+ pxa27x_change_configuration(udc, config);
+ interface = (udccr & UDCCR_AIN) >> UDCCR_AIN_S;
+ alternate = (udccr & UDCCR_AAISN) >> UDCCR_AAISN_S;
+
+ /*
+ * barebox specific: do not call change interface, as change_config has
+ * already setup the gadget.
+ * pxa27x_change_interface(udc, interface, alternate);
+ */
+}
+
+static void irq_udc_reset(struct pxa_udc *udc)
+{
+ u32 udccr = udc_readl(udc, UDCCR);
+ struct pxa_ep *ep = &udc->pxa_ep[0];
+
+ dev_info(udc->dev, "USB reset\n");
+ udc_writel(udc, UDCISR1, UDCISR1_IRRS);
+
+ if ((udccr & UDCCR_UDA) == 0) {
+ dev_dbg(udc->dev, "USB reset start\n");
+ stop_activity(udc, udc->driver);
+ }
+ udc->gadget.speed = USB_SPEED_FULL;
+
+ nuke(ep, -EPROTO);
+ ep_write_UDCCSR(ep, UDCCSR0_FTF | UDCCSR0_OPC);
+ ep0_idle(udc);
+}
+
+int usb_gadget_poll(void)
+{
+ struct pxa_udc *udc = the_controller;
+ u32 udcisr0 = udc_readl(udc, UDCISR0);
+ u32 udcisr1 = udc_readl(udc, UDCISR1);
+ u32 udccr = udc_readl(udc, UDCCR);
+ u32 udcisr1_spec;
+ int ret = 0;
+
+ udc->vbus_sensed = udc->mach->udc_is_connected();
+ if (should_enable_udc(udc))
+ udc_enable(udc);
+ if (should_disable_udc(udc)) {
+ stop_activity(udc, udc->driver);
+ udc_disable(udc);
+ }
+
+ if (!udc->enabled)
+ return -EIO;
+
+ dev_dbg(udc->dev, "Interrupt, UDCISR0:0x%08x, UDCISR1:0x%08x, "
+ "UDCCR:0x%08x\n", udcisr0, udcisr1, udccr);
+
+ udcisr1_spec = udcisr1 & 0xf8000000;
+ if (unlikely(udcisr1_spec & UDCISR1_IRSU))
+ irq_udc_suspend(udc);
+ if (unlikely(udcisr1_spec & UDCISR1_IRRU))
+ irq_udc_resume(udc);
+ if (unlikely(udcisr1_spec & UDCISR1_IRCC))
+ irq_udc_reconfig(udc);
+ if (unlikely(udcisr1_spec & UDCISR1_IRRS))
+ irq_udc_reset(udc);
+ if (udcisr1_spec)
+ ret = 1;
+
+ if ((udcisr0 & UDCCISR0_EP_MASK) | (udcisr1 & UDCCISR1_EP_MASK)) {
+ irq_handle_data(udc);
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static struct pxa_udc memory = {
+ .gadget = {
+ .ops = &pxa_udc_ops,
+ .ep0 = &memory.udc_usb_ep[0].usb_ep,
+ .name = driver_name,
+ },
+
+ .udc_usb_ep = {
+ USB_EP_CTRL,
+ USB_EP_OUT_BULK(1),
+ USB_EP_IN_BULK(2),
+ USB_EP_IN_ISO(3),
+ USB_EP_OUT_ISO(4),
+ USB_EP_IN_INT(5),
+ },
+
+ .pxa_ep = {
+ PXA_EP_CTRL,
+ /* Endpoints for gadget zero */
+ PXA_EP_OUT_BULK(1, 1, 3, 0, 0),
+ PXA_EP_IN_BULK(2, 2, 3, 0, 0),
+ /* Endpoints for ether gadget, file storage gadget */
+ PXA_EP_OUT_BULK(3, 1, 1, 0, 0),
+ PXA_EP_IN_BULK(4, 2, 1, 0, 0),
+ PXA_EP_IN_ISO(5, 3, 1, 0, 0),
+ PXA_EP_OUT_ISO(6, 4, 1, 0, 0),
+ PXA_EP_IN_INT(7, 5, 1, 0, 0),
+ /* Endpoints for RNDIS, serial */
+ PXA_EP_OUT_BULK(8, 1, 2, 0, 0),
+ PXA_EP_IN_BULK(9, 2, 2, 0, 0),
+ PXA_EP_IN_INT(10, 5, 2, 0, 0),
+ /*
+ * All the following endpoints are only for completion. They
+ * won't never work, as multiple interfaces are really broken on
+ * the pxa.
+ */
+ PXA_EP_OUT_BULK(11, 1, 2, 1, 0),
+ PXA_EP_IN_BULK(12, 2, 2, 1, 0),
+ /* Endpoint for CDC Ether */
+ PXA_EP_OUT_BULK(13, 1, 1, 1, 1),
+ PXA_EP_IN_BULK(14, 2, 1, 1, 1),
+ }
+};
+
+static int __init pxa_udc_probe(struct device_d *dev)
+{
+ struct pxa_udc *udc = &memory;
+ int gpio;
+
+ udc->regs = dev_request_mem_region(dev, 0);
+ if (!udc->regs)
+ return -ENXIO;
+
+ udc->dev = dev;
+ udc->mach = dev->platform_data;
+
+ gpio = udc->mach->gpio_pullup;
+ if (gpio >= 0) {
+ gpio_direction_output(gpio,
+ udc->mach->gpio_pullup_inverted);
+ }
+
+ udc->vbus_sensed = 0;
+
+ the_controller = udc;
+ udc_init_data(udc);
+ pxa_eps_setup(udc);
+ return 0;
+}
+
+#define pxa27x_clear_otgph() do {} while (0)
+
+static struct driver_d udc_driver = {
+ .name = "pxa27x-udc",
+ .probe = pxa_udc_probe,
+};
+
+static int pxa27x_udc_poller(struct poller_struct *poller)
+{
+ return usb_gadget_poll();
+}
+static struct poller_struct poller = {
+ .func = pxa27x_udc_poller
+};
+
+static int __init pxa27x_udc_init(void)
+{
+ register_driver(&udc_driver);
+ poller_register(&poller);
+ return 0;
+}
+
+device_initcall(pxa27x_udc_init);
diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/pxa27x_udc.h
new file mode 100644
index 0000000000..eace2e4055
--- /dev/null
+++ b/drivers/usb/gadget/pxa27x_udc.h
@@ -0,0 +1,393 @@
+/*
+ * linux/drivers/usb/gadget/pxa27x_udc.h
+ * Intel PXA27x on-chip full speed USB device controller
+ *
+ * Inspired by original driver by Frank Becker, David Brownell, and others.
+ * Copyright (C) 2008 Robert Jarzmik
+ *
+ * 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
+ *
+ * Taken from linux-2.6 kernel and adapted to barebox.
+ */
+
+#ifndef __LINUX_USB_GADGET_PXA27X_H
+#define __LINUX_USB_GADGET_PXA27X_H
+
+/*
+ * Register definitions
+ */
+/* Offsets */
+#define UDCCR 0x0000 /* UDC Control Register */
+#define UDCICR0 0x0004 /* UDC Interrupt Control Register0 */
+#define UDCICR1 0x0008 /* UDC Interrupt Control Register1 */
+#define UDCISR0 0x000C /* UDC Interrupt Status Register 0 */
+#define UDCISR1 0x0010 /* UDC Interrupt Status Register 1 */
+#define UDCFNR 0x0014 /* UDC Frame Number Register */
+#define UDCOTGICR 0x0018 /* UDC On-The-Go interrupt control */
+#define UP2OCR 0x0020 /* USB Port 2 Output Control register */
+#define UP3OCR 0x0024 /* USB Port 3 Output Control register */
+#define UDCCSRn(x) (0x0100 + ((x)<<2)) /* UDC Control/Status register */
+#define UDCBCRn(x) (0x0200 + ((x)<<2)) /* UDC Byte Count Register */
+#define UDCDRn(x) (0x0300 + ((x)<<2)) /* UDC Data Register */
+#define UDCCRn(x) (0x0400 + ((x)<<2)) /* UDC Control Register */
+
+#define UDCCR_OEN (1 << 31) /* On-the-Go Enable */
+#define UDCCR_AALTHNP (1 << 30) /* A-device Alternate Host Negotiation
+ Protocol Port Support */
+#define UDCCR_AHNP (1 << 29) /* A-device Host Negotiation Protocol
+ Support */
+#define UDCCR_BHNP (1 << 28) /* B-device Host Negotiation Protocol
+ Enable */
+#define UDCCR_DWRE (1 << 16) /* Device Remote Wake-up Enable */
+#define UDCCR_ACN (0x03 << 11) /* Active UDC configuration Number */
+#define UDCCR_ACN_S 11
+#define UDCCR_AIN (0x07 << 8) /* Active UDC interface Number */
+#define UDCCR_AIN_S 8
+#define UDCCR_AAISN (0x07 << 5) /* Active UDC Alternate Interface
+ Setting Number */
+#define UDCCR_AAISN_S 5
+#define UDCCR_SMAC (1 << 4) /* Switch Endpoint Memory to Active
+ Configuration */
+#define UDCCR_EMCE (1 << 3) /* Endpoint Memory Configuration
+ Error */
+#define UDCCR_UDR (1 << 2) /* UDC Resume */
+#define UDCCR_UDA (1 << 1) /* UDC Active */
+#define UDCCR_UDE (1 << 0) /* UDC Enable */
+
+#define UDCICR_INT(n, intr) (((intr) & 0x03) << (((n) & 0x0F) * 2))
+#define UDCICR1_IECC (1 << 31) /* IntEn - Configuration Change */
+#define UDCICR1_IESOF (1 << 30) /* IntEn - Start of Frame */
+#define UDCICR1_IERU (1 << 29) /* IntEn - Resume */
+#define UDCICR1_IESU (1 << 28) /* IntEn - Suspend */
+#define UDCICR1_IERS (1 << 27) /* IntEn - Reset */
+#define UDCICR_FIFOERR (1 << 1) /* FIFO Error interrupt for EP */
+#define UDCICR_PKTCOMPL (1 << 0) /* Packet Complete interrupt for EP */
+#define UDCICR_INT_MASK (UDCICR_FIFOERR | UDCICR_PKTCOMPL)
+
+#define UDCISR_INT(n, intr) (((intr) & 0x03) << (((n) & 0x0F) * 2))
+#define UDCISR1_IRCC (1 << 31) /* IntReq - Configuration Change */
+#define UDCISR1_IRSOF (1 << 30) /* IntReq - Start of Frame */
+#define UDCISR1_IRRU (1 << 29) /* IntReq - Resume */
+#define UDCISR1_IRSU (1 << 28) /* IntReq - Suspend */
+#define UDCISR1_IRRS (1 << 27) /* IntReq - Reset */
+#define UDCISR_INT_MASK (UDCICR_FIFOERR | UDCICR_PKTCOMPL)
+
+#define UDCOTGICR_IESF (1 << 24) /* OTG SET_FEATURE command recvd */
+#define UDCOTGICR_IEXR (1 << 17) /* Extra Transceiver Interrupt
+ Rising Edge Interrupt Enable */
+#define UDCOTGICR_IEXF (1 << 16) /* Extra Transceiver Interrupt
+ Falling Edge Interrupt Enable */
+#define UDCOTGICR_IEVV40R (1 << 9) /* OTG Vbus Valid 4.0V Rising Edge
+ Interrupt Enable */
+#define UDCOTGICR_IEVV40F (1 << 8) /* OTG Vbus Valid 4.0V Falling Edge
+ Interrupt Enable */
+#define UDCOTGICR_IEVV44R (1 << 7) /* OTG Vbus Valid 4.4V Rising Edge
+ Interrupt Enable */
+#define UDCOTGICR_IEVV44F (1 << 6) /* OTG Vbus Valid 4.4V Falling Edge
+ Interrupt Enable */
+#define UDCOTGICR_IESVR (1 << 5) /* OTG Session Valid Rising Edge
+ Interrupt Enable */
+#define UDCOTGICR_IESVF (1 << 4) /* OTG Session Valid Falling Edge
+ Interrupt Enable */
+#define UDCOTGICR_IESDR (1 << 3) /* OTG A-Device SRP Detect Rising
+ Edge Interrupt Enable */
+#define UDCOTGICR_IESDF (1 << 2) /* OTG A-Device SRP Detect Falling
+ Edge Interrupt Enable */
+#define UDCOTGICR_IEIDR (1 << 1) /* OTG ID Change Rising Edge
+ Interrupt Enable */
+#define UDCOTGICR_IEIDF (1 << 0) /* OTG ID Change Falling Edge
+ Interrupt Enable */
+
+/* Host Port 2 field bits */
+#define UP2OCR_CPVEN (1 << 0) /* Charge Pump Vbus Enable */
+#define UP2OCR_CPVPE (1 << 1) /* Charge Pump Vbus Pulse Enable */
+ /* Transceiver enablers */
+#define UP2OCR_DPPDE (1 << 2) /* D+ Pull Down Enable */
+#define UP2OCR_DMPDE (1 << 3) /* D- Pull Down Enable */
+#define UP2OCR_DPPUE (1 << 4) /* D+ Pull Up Enable */
+#define UP2OCR_DMPUE (1 << 5) /* D- Pull Up Enable */
+#define UP2OCR_DPPUBE (1 << 6) /* D+ Pull Up Bypass Enable */
+#define UP2OCR_DMPUBE (1 << 7) /* D- Pull Up Bypass Enable */
+#define UP2OCR_EXSP (1 << 8) /* External Transceiver Speed Control */
+#define UP2OCR_EXSUS (1 << 9) /* External Transceiver Speed Enable */
+#define UP2OCR_IDON (1 << 10) /* OTG ID Read Enable */
+#define UP2OCR_HXS (1 << 16) /* Transceiver Output Select */
+#define UP2OCR_HXOE (1 << 17) /* Transceiver Output Enable */
+#define UP2OCR_SEOS (1 << 24) /* Single-Ended Output Select */
+
+#define UDCCSR0_ACM (1 << 9) /* Ack Control Mode */
+#define UDCCSR0_AREN (1 << 8) /* Ack Response Enable */
+#define UDCCSR0_SA (1 << 7) /* Setup Active */
+#define UDCCSR0_RNE (1 << 6) /* Receive FIFO Not Empty */
+#define UDCCSR0_FST (1 << 5) /* Force Stall */
+#define UDCCSR0_SST (1 << 4) /* Sent Stall */
+#define UDCCSR0_DME (1 << 3) /* DMA Enable */
+#define UDCCSR0_FTF (1 << 2) /* Flush Transmit FIFO */
+#define UDCCSR0_IPR (1 << 1) /* IN Packet Ready */
+#define UDCCSR0_OPC (1 << 0) /* OUT Packet Complete */
+
+#define UDCCSR_DPE (1 << 9) /* Data Packet Error */
+#define UDCCSR_FEF (1 << 8) /* Flush Endpoint FIFO */
+#define UDCCSR_SP (1 << 7) /* Short Packet Control/Status */
+#define UDCCSR_BNE (1 << 6) /* Buffer Not Empty (IN endpoints) */
+#define UDCCSR_BNF (1 << 6) /* Buffer Not Full (OUT endpoints) */
+#define UDCCSR_FST (1 << 5) /* Force STALL */
+#define UDCCSR_SST (1 << 4) /* Sent STALL */
+#define UDCCSR_DME (1 << 3) /* DMA Enable */
+#define UDCCSR_TRN (1 << 2) /* Tx/Rx NAK */
+#define UDCCSR_PC (1 << 1) /* Packet Complete */
+#define UDCCSR_FS (1 << 0) /* FIFO needs service */
+
+#define UDCCONR_CN (0x03 << 25) /* Configuration Number */
+#define UDCCONR_CN_S 25
+#define UDCCONR_IN (0x07 << 22) /* Interface Number */
+#define UDCCONR_IN_S 22
+#define UDCCONR_AISN (0x07 << 19) /* Alternate Interface Number */
+#define UDCCONR_AISN_S 19
+#define UDCCONR_EN (0x0f << 15) /* Endpoint Number */
+#define UDCCONR_EN_S 15
+#define UDCCONR_ET (0x03 << 13) /* Endpoint Type: */
+#define UDCCONR_ET_S 13
+#define UDCCONR_ET_INT (0x03 << 13) /* Interrupt */
+#define UDCCONR_ET_BULK (0x02 << 13) /* Bulk */
+#define UDCCONR_ET_ISO (0x01 << 13) /* Isochronous */
+#define UDCCONR_ET_NU (0x00 << 13) /* Not used */
+#define UDCCONR_ED (1 << 12) /* Endpoint Direction */
+#define UDCCONR_MPS (0x3ff << 2) /* Maximum Packet Size */
+#define UDCCONR_MPS_S 2
+#define UDCCONR_DE (1 << 1) /* Double Buffering Enable */
+#define UDCCONR_EE (1 << 0) /* Endpoint Enable */
+
+#define UDCCR_MASK_BITS (UDCCR_OEN | UDCCR_SMAC | UDCCR_UDR | UDCCR_UDE)
+#define UDCCSR_WR_MASK (UDCCSR_DME | UDCCSR_FST)
+#define UDC_FNR_MASK (0x7ff)
+#define UDC_BCR_MASK (0x3ff)
+
+/*
+ * UDCCR = UDC Endpoint Configuration Registers
+ * UDCCSR = UDC Control/Status Register for this EP
+ * UDCBCR = UDC Byte Count Remaining (contents of OUT fifo)
+ * UDCDR = UDC Endpoint Data Register (the fifo)
+ */
+#define ofs_UDCCR(ep) (UDCCRn(ep->idx))
+#define ofs_UDCCSR(ep) (UDCCSRn(ep->idx))
+#define ofs_UDCBCR(ep) (UDCBCRn(ep->idx))
+#define ofs_UDCDR(ep) (UDCDRn(ep->idx))
+
+/* Register access macros */
+#define udc_ep_readl(ep, reg) \
+ readl((ep)->dev->regs + ofs_##reg(ep))
+#define udc_ep_writel(ep, reg, value) \
+ writel((value), ep->dev->regs + ofs_##reg(ep))
+#define udc_ep_readb(ep, reg) \
+ readb((ep)->dev->regs + ofs_##reg(ep))
+#define udc_ep_writeb(ep, reg, value) \
+ writeb((value), ep->dev->regs + ofs_##reg(ep))
+#define udc_readl(dev, reg) \
+ readl((dev)->regs + (reg))
+#define udc_writel(udc, reg, value) \
+ writel((value), (udc)->regs + (reg))
+
+#define UDCCSR_MASK (UDCCSR_FST | UDCCSR_DME)
+#define UDCCISR0_EP_MASK (~0)
+#define UDCCISR1_EP_MASK 0xffff
+#define UDCCSR0_CTRL_REQ_MASK (UDCCSR0_OPC | UDCCSR0_SA | UDCCSR0_RNE)
+
+#define EPIDX(ep) (ep->idx)
+#define EPADDR(ep) (ep->addr)
+#define EPXFERTYPE(ep) (ep->type)
+#define EPNAME(ep) (ep->name)
+#define is_ep0(ep) (!ep->idx)
+#define EPXFERTYPE_is_ISO(ep) (EPXFERTYPE(ep) == USB_ENDPOINT_XFER_ISOC)
+
+/*
+ * Endpoint definition helpers
+ */
+#define USB_EP_DEF(addr, bname, dir, type, maxpkt) \
+{ .usb_ep = { .name = bname, .ops = &pxa_ep_ops, .maxpacket = maxpkt, }, \
+ .desc = { .bEndpointAddress = addr | (dir ? USB_DIR_IN : 0), \
+ .bmAttributes = type, \
+ .wMaxPacketSize = maxpkt, }, \
+ .dev = &memory \
+}
+#define USB_EP_BULK(addr, bname, dir) \
+ USB_EP_DEF(addr, bname, dir, USB_ENDPOINT_XFER_BULK, BULK_FIFO_SIZE)
+#define USB_EP_ISO(addr, bname, dir) \
+ USB_EP_DEF(addr, bname, dir, USB_ENDPOINT_XFER_ISOC, ISO_FIFO_SIZE)
+#define USB_EP_INT(addr, bname, dir) \
+ USB_EP_DEF(addr, bname, dir, USB_ENDPOINT_XFER_INT, INT_FIFO_SIZE)
+#define USB_EP_IN_BULK(n) USB_EP_BULK(n, "ep" #n "in-bulk", 1)
+#define USB_EP_OUT_BULK(n) USB_EP_BULK(n, "ep" #n "out-bulk", 0)
+#define USB_EP_IN_ISO(n) USB_EP_ISO(n, "ep" #n "in-iso", 1)
+#define USB_EP_OUT_ISO(n) USB_EP_ISO(n, "ep" #n "out-iso", 0)
+#define USB_EP_IN_INT(n) USB_EP_INT(n, "ep" #n "in-int", 1)
+#define USB_EP_CTRL USB_EP_DEF(0, "ep0", 0, 0, EP0_FIFO_SIZE)
+
+#define PXA_EP_DEF(_idx, _addr, dir, _type, maxpkt, _config, iface, altset) \
+{ \
+ .dev = &memory, \
+ .name = "ep" #_idx, \
+ .idx = _idx, .enabled = 0, \
+ .dir_in = dir, .addr = _addr, \
+ .config = _config, .interface = iface, .alternate = altset, \
+ .type = _type, .fifo_size = maxpkt, \
+}
+#define PXA_EP_BULK(_idx, addr, dir, config, iface, alt) \
+ PXA_EP_DEF(_idx, addr, dir, USB_ENDPOINT_XFER_BULK, BULK_FIFO_SIZE, \
+ config, iface, alt)
+#define PXA_EP_ISO(_idx, addr, dir, config, iface, alt) \
+ PXA_EP_DEF(_idx, addr, dir, USB_ENDPOINT_XFER_ISOC, ISO_FIFO_SIZE, \
+ config, iface, alt)
+#define PXA_EP_INT(_idx, addr, dir, config, iface, alt) \
+ PXA_EP_DEF(_idx, addr, dir, USB_ENDPOINT_XFER_INT, INT_FIFO_SIZE, \
+ config, iface, alt)
+#define PXA_EP_IN_BULK(i, adr, c, f, a) PXA_EP_BULK(i, adr, 1, c, f, a)
+#define PXA_EP_OUT_BULK(i, adr, c, f, a) PXA_EP_BULK(i, adr, 0, c, f, a)
+#define PXA_EP_IN_ISO(i, adr, c, f, a) PXA_EP_ISO(i, adr, 1, c, f, a)
+#define PXA_EP_OUT_ISO(i, adr, c, f, a) PXA_EP_ISO(i, adr, 0, c, f, a)
+#define PXA_EP_IN_INT(i, adr, c, f, a) PXA_EP_INT(i, adr, 1, c, f, a)
+#define PXA_EP_CTRL PXA_EP_DEF(0, 0, 0, 0, EP0_FIFO_SIZE, 0, 0, 0)
+
+struct pxa27x_udc;
+
+struct stats {
+ unsigned long in_ops;
+ unsigned long out_ops;
+ unsigned long in_bytes;
+ unsigned long out_bytes;
+ unsigned long irqs;
+};
+
+/**
+ * struct udc_usb_ep - container of each usb_ep structure
+ * @usb_ep: usb endpoint
+ * @desc: usb descriptor, especially type and address
+ * @dev: udc managing this endpoint
+ * @pxa_ep: matching pxa_ep (cache of find_pxa_ep() call)
+ */
+struct udc_usb_ep {
+ struct usb_ep usb_ep;
+ struct usb_endpoint_descriptor desc;
+ struct pxa_udc *dev;
+ struct pxa_ep *pxa_ep;
+};
+
+struct pxa_ep {
+ struct pxa_udc *dev;
+
+ struct list_head queue;
+ unsigned enabled:1;
+
+ unsigned idx:5;
+ char *name;
+
+ /*
+ * Specific pxa endpoint data, needed for hardware initialization
+ */
+ unsigned dir_in:1;
+ unsigned addr:4;
+ unsigned config:2;
+ unsigned interface:3;
+ unsigned alternate:3;
+ unsigned fifo_size;
+ unsigned type;
+};
+
+struct pxa27x_request {
+ struct usb_request req;
+ struct udc_usb_ep *udc_usb_ep;
+ unsigned in_use:1;
+ struct list_head queue;
+};
+
+enum ep0_state {
+ WAIT_FOR_SETUP,
+ SETUP_STAGE,
+ IN_DATA_STAGE,
+ OUT_DATA_STAGE,
+ IN_STATUS_STAGE,
+ OUT_STATUS_STAGE,
+ STALL,
+ WAIT_ACK_SET_CONF_INTERF
+};
+
+static char *ep0_state_name[] = {
+ "WAIT_FOR_SETUP", "SETUP_STAGE", "IN_DATA_STAGE", "OUT_DATA_STAGE",
+ "IN_STATUS_STAGE", "OUT_STATUS_STAGE", "STALL",
+ "WAIT_ACK_SET_CONF_INTERF"
+};
+#define EP0_STNAME(udc) ep0_state_name[(udc)->ep0state]
+
+#define EP0_FIFO_SIZE 16U
+#define BULK_FIFO_SIZE 64U
+#define ISO_FIFO_SIZE 256U
+#define INT_FIFO_SIZE 16U
+
+struct udc_stats {
+ unsigned long irqs_reset;
+ unsigned long irqs_suspend;
+ unsigned long irqs_resume;
+ unsigned long irqs_reconfig;
+};
+
+#define NR_USB_ENDPOINTS (1 + 5) /* ep0 + ep1in-bulk + .. + ep3in-iso */
+#define NR_PXA_ENDPOINTS (1 + 14) /* ep0 + epA + epB + .. + epX */
+
+struct pxa_udc {
+ void __iomem *regs;
+ int irq;
+ struct clk *clk;
+ struct device_d *dev;
+
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *driver;
+ struct pxa2xx_udc_mach_info *mach;
+
+ enum ep0_state ep0state;
+ struct udc_stats stats;
+
+ struct udc_usb_ep udc_usb_ep[NR_USB_ENDPOINTS];
+ struct pxa_ep pxa_ep[NR_PXA_ENDPOINTS];
+
+ unsigned enabled:1;
+ unsigned pullup_on:1;
+ unsigned pullup_resume:1;
+ unsigned vbus_sensed:1;
+ unsigned config:2;
+ unsigned last_interface:3;
+ unsigned last_alternate:3;
+
+};
+
+static inline struct pxa_udc *to_gadget_udc(struct usb_gadget *gadget)
+{
+ return container_of(gadget, struct pxa_udc, gadget);
+}
+
+/*
+ * Debugging/message support
+ */
+#define ep_dbg(ep, fmt, arg...) \
+ dev_dbg(ep->dev->dev, "%s:%s: " fmt, EPNAME(ep), __func__, ## arg)
+#define ep_vdbg(ep, fmt, arg...) \
+ dev_dbg(ep->dev->dev, "%s:%s: " fmt, EPNAME(ep), __func__, ## arg)
+#define ep_err(ep, fmt, arg...) \
+ dev_err(ep->dev->dev, "%s:%s: " fmt, EPNAME(ep), __func__, ## arg)
+#define ep_info(ep, fmt, arg...) \
+ dev_info(ep->dev->dev, "%s:%s: " fmt, EPNAME(ep), __func__, ## arg)
+#define ep_warn(ep, fmt, arg...) \
+ dev_warn(ep->dev->dev, "%s:%s:" fmt, EPNAME(ep), __func__, ## arg)
+
+#endif /* __LINUX_USB_GADGET_PXA27X_H */
diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c
index d3baed74de..49aedc23ef 100644
--- a/drivers/usb/gadget/u_serial.c
+++ b/drivers/usb/gadget/u_serial.c
@@ -367,12 +367,13 @@ static void serial_putc(struct console_device *cdev, char c)
struct gs_port *port = container_of(cdev,
struct gs_port, cdev);
struct list_head *pool = &port->write_pool;
- struct usb_ep *in = port->port_usb->in;
+ struct usb_ep *in;
struct usb_request *req;
int status;
if (list_empty(pool))
return;
+ in = port->port_usb->in;
req = list_entry(pool->next, struct usb_request, list);
req->length = 1;
@@ -381,8 +382,8 @@ static void serial_putc(struct console_device *cdev, char c)
*(unsigned char *)req->buf = c;
status = usb_ep_queue(in, req);
- while (list_empty(pool))
- fsl_udc_irq();
+ while (status >= 0 && list_empty(pool))
+ status = usb_gadget_poll();
}
static int serial_tstc(struct console_device *cdev)
@@ -399,7 +400,10 @@ static int serial_getc(struct console_device *cdev)
struct gs_port, cdev);
unsigned char ch;
- while (kfifo_getc(port->recv_fifo, &ch));
+ if (!port->port_usb)
+ return -EIO;
+ while (kfifo_getc(port->recv_fifo, &ch))
+ usb_gadget_poll();
return ch;
}
@@ -421,6 +425,10 @@ int gserial_connect(struct gserial *gser, u8 port_num)
/* we "know" gserial_cleanup() hasn't been called */
port = ports[port_num].port;
+ /* In case of multiple activation (ie. multiple SET_INTERFACE) */
+ if (port->port_usb)
+ return 0;
+
/* activate the endpoints */
status = usb_ep_enable(gser->in, gser->in_desc);
if (status < 0)
@@ -475,7 +483,7 @@ static int do_mycdev(struct command *cmdtp, int argc, char *argv[])
printf("%c", i);
mdelay(500);
for (j = 0; j < 100; j++)
- fsl_udc_irq();
+ usb_gadget_poll();
}
return 0;
}
@@ -498,7 +506,9 @@ BAREBOX_CMD_END
void gserial_disconnect(struct gserial *gser)
{
struct gs_port *port = gser->ioport;
-printf("%s\n", __func__);
+ struct console_device *cdev;
+
+ printf("%s\n", __func__);
if (!port)
return;
@@ -518,8 +528,9 @@ printf("%s\n", __func__);
gser->in->driver_data = NULL;
/* finally, free any unused/unusable I/O buffers */
-
gs_free_requests(gser->out, &port->read_pool);
gs_free_requests(gser->in, &port->write_pool);
-}
+ cdev = &port->cdev;
+ console_unregister(cdev);
+}
diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig
index f6c8c06b1e..b80c039117 100644
--- a/drivers/usb/storage/Kconfig
+++ b/drivers/usb/storage/Kconfig
@@ -1,2 +1,3 @@
config USB_STORAGE
tristate "USB Mass Storage support"
+ select DISK
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index d033b291c2..865ba8ec47 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -96,8 +96,8 @@ static int usb_stor_test_unit_ready(ccb *srb, struct us_data *us)
retries = 10;
do {
US_DEBUGP("SCSI_TST_U_RDY\n");
- memset(&srb->cmd[0], 0, 6);
- srb->cmdlen = 6;
+ memset(&srb->cmd[0], 0, 12);
+ srb->cmdlen = 12;
srb->cmd[0] = SCSI_TST_U_RDY;
srb->datalen = 0;
result = us->transport(srb, us);
@@ -193,35 +193,34 @@ static int usb_stor_write_10(ccb *srb, struct us_data *us,
* Disk driver interface
***********************************************************************/
-#define US_MAX_IO_BLK 32U
+#define US_MAX_IO_BLK 32
+
+#define to_usb_mass_storage(x) container_of((x), struct us_blk_dev, blk)
enum { io_rd, io_wr };
/* Read / write a chunk of sectors on media */
-static int usb_stor_blk_io(int io_op, struct device_d *disk_dev,
- uint64_t sector_start, unsigned sector_count,
- void *buffer)
+static int usb_stor_blk_io(int io_op, struct block_device *disk_dev,
+ int sector_start, int sector_count, void *buffer)
{
- struct ata_interface *pata_if = disk_dev->platform_data;
- struct us_blk_dev *pblk_dev = (struct us_blk_dev *)pata_if->priv;
+ struct us_blk_dev *pblk_dev = to_usb_mass_storage(disk_dev);
struct us_data *us = pblk_dev->us;
ccb us_ccb;
- ushort const sector_size = 512;
unsigned sectors_done;
if (sector_count == 0)
return 0;
/* check for unsupported block size */
- if (pblk_dev->blksz != sector_size) {
- US_DEBUGP("%s: unsupported block size %lu\n",
- __func__, pblk_dev->blksz);
+ if (pblk_dev->blk.blockbits != SECTOR_SHIFT) {
+ US_DEBUGP("%s: unsupported block shift %d\n",
+ __func__, pblk_dev->blk.blockbits);
return -EINVAL;
}
/* check for invalid sector_start */
- if (sector_start >= pblk_dev->blknum || sector_start > (ulong)-1) {
- US_DEBUGP("%s: start sector %llu too large\n",
+ if (sector_start >= pblk_dev->blk.num_blocks || sector_start > (ulong)-1) {
+ US_DEBUGP("%s: start sector %d too large\n",
__func__, sector_start);
return -EINVAL;
}
@@ -242,21 +241,21 @@ static int usb_stor_blk_io(int io_op, struct device_d *disk_dev,
sector_count = INT_MAX;
US_DEBUGP("Restricting I/O to %u blocks\n", sector_count);
}
- if (sector_start + sector_count > pblk_dev->blknum) {
- sector_count = pblk_dev->blknum - sector_start;
+ if (sector_start + sector_count > pblk_dev->blk.num_blocks) {
+ sector_count = pblk_dev->blk.num_blocks - sector_start;
US_DEBUGP("Restricting I/O to %u blocks\n", sector_count);
}
/* read / write the requested data */
- US_DEBUGP("%s %u block(s), starting from %llu\n",
+ US_DEBUGP("%s %u block(s), starting from %d\n",
((io_op == io_rd) ? "Read" : "Write"),
sector_count, sector_start);
sectors_done = 0;
while (sector_count > 0) {
int result;
- ushort n = (ushort)min(sector_count, US_MAX_IO_BLK);
- us_ccb.pdata = buffer + sectors_done * sector_size;
- us_ccb.datalen = n * (ulong)sector_size;
+ unsigned n = min(sector_count, US_MAX_IO_BLK);
+ us_ccb.pdata = buffer + (sectors_done * SECTOR_SIZE);
+ us_ccb.datalen = n * SECTOR_SIZE;
if (io_op == io_rd)
result = usb_stor_read_10(&us_ccb, us,
(ulong)sector_start, n);
@@ -264,7 +263,7 @@ static int usb_stor_blk_io(int io_op, struct device_d *disk_dev,
result = usb_stor_write_10(&us_ccb, us,
(ulong)sector_start, n);
if (result != 0) {
- US_DEBUGP("I/O error at sector %llu\n", sector_start);
+ US_DEBUGP("I/O error at sector %d\n", sector_start);
break;
}
sector_start += n;
@@ -274,27 +273,31 @@ static int usb_stor_blk_io(int io_op, struct device_d *disk_dev,
usb_disable_asynch(0);
- US_DEBUGP("Successful I/O of %u blocks\n", sectors_done);
+ US_DEBUGP("Successful I/O of %d blocks\n", sectors_done);
return (sector_count != 0) ? -EIO : 0;
}
/* Write a chunk of sectors to media */
-static int usb_stor_blk_write(struct device_d *disk_dev, uint64_t sector_start,
- unsigned sector_count, const void *buffer)
+static int __maybe_unused usb_stor_blk_write(struct block_device *blk,
+ const void *buffer, int block, int num_blocks)
{
- return usb_stor_blk_io(io_wr, disk_dev, sector_start, sector_count,
- (void *)buffer);
+ return usb_stor_blk_io(io_wr, blk, block, num_blocks, (void *)buffer);
}
/* Read a chunk of sectors from media */
-static int usb_stor_blk_read(struct device_d *disk_dev, uint64_t sector_start,
- unsigned sector_count, void *buffer)
+static int usb_stor_blk_read(struct block_device *blk, void *buffer, int block,
+ int num_blocks)
{
- return usb_stor_blk_io(io_rd, disk_dev, sector_start, sector_count,
- buffer);
+ return usb_stor_blk_io(io_rd, blk, block, num_blocks, buffer);
}
+static struct block_device_ops usb_mass_storage_ops = {
+ .read = usb_stor_blk_read,
+#ifdef CONFIG_BLOCK_WRITE
+ .write = usb_stor_blk_write,
+#endif
+};
/***********************************************************************
* Block device routines
@@ -302,6 +305,16 @@ static int usb_stor_blk_read(struct device_d *disk_dev, uint64_t sector_start,
static unsigned char us_io_buf[512];
+static int usb_limit_blk_cnt(unsigned cnt)
+{
+ if (cnt > 0x7fffffff) {
+ pr_warn("Limiting device size due to 31 bit contraints\n");
+ return 0x7fffffff;
+ }
+
+ return (int)cnt;
+}
+
/* Prepare a disk device */
static int usb_stor_init_blkdev(struct us_blk_dev *pblk_dev)
{
@@ -313,7 +326,7 @@ static int usb_stor_init_blkdev(struct us_blk_dev *pblk_dev)
us_ccb.pdata = us_io_buf;
us_ccb.lun = pblk_dev->lun;
- pblk_dev->blknum = 0;
+ pblk_dev->blk.num_blocks = 0;
usb_disable_asynch(1);
/* get device info */
@@ -350,11 +363,12 @@ static int usb_stor_init_blkdev(struct us_blk_dev *pblk_dev)
}
pcap = (unsigned long *)us_ccb.pdata;
US_DEBUGP("Read Capacity returns: 0x%lx, 0x%lx\n", pcap[0], pcap[1]);
- pblk_dev->blknum = be32_to_cpu(pcap[0]);
- pblk_dev->blksz = be32_to_cpu(pcap[1]);
- pblk_dev->blknum++;
- US_DEBUGP("Capacity = 0x%llx, blocksz = 0x%lx\n",
- pblk_dev->blknum, pblk_dev->blksz);
+ pblk_dev->blk.num_blocks = usb_limit_blk_cnt(be32_to_cpu(pcap[0]) + 1);
+ if (be32_to_cpu(pcap[1]) != SECTOR_SIZE)
+ pr_warn("Support only %d bytes sectors\n", SECTOR_SIZE);
+ pblk_dev->blk.blockbits = SECTOR_SHIFT;
+ US_DEBUGP("Capacity = 0x%x, blockshift = 0x%x\n",
+ pblk_dev->blk.num_blocks, pblk_dev->blk.blockbits);
Exit:
usb_disable_asynch(0);
@@ -362,39 +376,45 @@ Exit:
}
/* Create and register a disk device for the specified LUN */
-static int usb_stor_add_blkdev(struct us_data *us, unsigned char lun)
+static int usb_stor_add_blkdev(struct us_data *us, struct device_d *dev,
+ unsigned char lun)
{
struct us_blk_dev *pblk_dev;
- struct device_d *pdev;
- struct ata_interface *pata_if;
int result;
- /* allocate blk dev data */
- pblk_dev = (struct us_blk_dev *)malloc(sizeof(struct us_blk_dev));
- if (!pblk_dev)
- return -ENOMEM;
- memset(pblk_dev, 0, sizeof(struct us_blk_dev));
+ /* allocate a new USB block device */
+ pblk_dev = xzalloc(sizeof(struct us_blk_dev));
/* initialize blk dev data */
+ pblk_dev->blk.dev = dev;
+ pblk_dev->blk.ops = &usb_mass_storage_ops;
pblk_dev->us = us;
pblk_dev->lun = lun;
- pata_if = &pblk_dev->ata_if;
- pata_if->read = &usb_stor_blk_read;
- pata_if->write = &usb_stor_blk_write;
- pata_if->priv = pblk_dev;
- pdev = &pblk_dev->dev;
- strcpy(pdev->name, "disk");
- pdev->platform_data = pata_if;
/* read some info and get the unit ready */
result = usb_stor_init_blkdev(pblk_dev);
if (result < 0)
goto BadDevice;
- /* register disk device */
- result = register_device(pdev);
- if (result < 0)
+ result = cdev_find_free_index("disk");
+ if (result == -1)
+ pr_err("Cannot find a free number for the disk node\n");
+ pr_info("Using index %d for the new disk\n", result);
+
+ pblk_dev->blk.cdev.name = asprintf("disk%d", result);
+ pblk_dev->blk.blockbits = SECTOR_SHIFT;
+
+ result = blockdevice_register(&pblk_dev->blk);
+ if (result != 0) {
+ dev_err(dev, "Failed to register blockdevice\n");
goto BadDevice;
+ }
+
+ /* create partitions on demand */
+ result = parse_partition_table(&pblk_dev->blk);
+ if (result != 0)
+ dev_warn(dev, "No partition table found\n");
+
list_add_tail(&pblk_dev->list, &us_blkdev_list);
US_DEBUGP("USB disk device successfully added\n");
@@ -485,7 +505,7 @@ static int usb_stor_scan(struct usb_device *usbdev, struct us_data *us)
/* register a disk device for each active LUN */
for (lun=0; lun<=us->max_lun; lun++) {
- if (usb_stor_add_blkdev(us, lun) == 0)
+ if (usb_stor_add_blkdev(us, &usbdev->dev, lun) == 0)
num_devs++;
}
diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h
index 17a1e1263e..59423934dd 100644
--- a/drivers/usb/storage/usb.h
+++ b/drivers/usb/storage/usb.h
@@ -26,7 +26,8 @@
#define _STORAGE_USB_H_
#include <usb/usb.h>
-#include <ata.h>
+#include <block.h>
+#include <disks.h>
#include <scsi.h>
#include <linux/list.h>
@@ -85,10 +86,7 @@ struct us_data {
/* one us_blk_dev object allocated per LUN */
struct us_blk_dev {
struct us_data *us; /* LUN's enclosing dev */
- struct device_d dev; /* intf to generic driver */
- struct ata_interface ata_if; /* intf to "disk" driver */
- uint64_t blknum; /* capacity */
- unsigned long blksz; /* block size */
+ struct block_device blk; /* the blockdevice for the dev */
unsigned char lun; /* the LUN of this blk dev */
struct list_head list; /* siblings */
};