diff options
Diffstat (limited to 'drivers')
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*)§or[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*)§or[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(®s->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(®s->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 */ }; |