diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/ata/ide-sff.c | 98 | ||||
-rw-r--r-- | drivers/ata/intf_platform_ide.c | 37 | ||||
-rw-r--r-- | drivers/base/driver.c | 16 | ||||
-rw-r--r-- | drivers/gpio/gpio-generic.c | 4 | ||||
-rw-r--r-- | drivers/gpio/gpio-imx.c | 10 | ||||
-rw-r--r-- | drivers/mfd/syscon.c | 2 | ||||
-rw-r--r-- | drivers/misc/sram.c | 2 | ||||
-rw-r--r-- | drivers/mtd/core.c | 3 | ||||
-rw-r--r-- | drivers/mtd/nand/nand-bb.c | 161 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_mxs.c | 5 | ||||
-rw-r--r-- | drivers/of/barebox.c | 19 | ||||
-rw-r--r-- | drivers/of/partition.c | 3 | ||||
-rw-r--r-- | drivers/serial/serial_ns16550.c | 127 | ||||
-rw-r--r-- | drivers/spi/Kconfig | 6 | ||||
-rw-r--r-- | drivers/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spi/ath79_spi.c | 299 | ||||
-rw-r--r-- | drivers/spi/imx_spi.c | 22 | ||||
-rw-r--r-- | drivers/spi/spi-bitbang-txrx.h | 95 |
18 files changed, 755 insertions, 155 deletions
diff --git a/drivers/ata/ide-sff.c b/drivers/ata/ide-sff.c index 3d5932e278..a8f2b26c55 100644 --- a/drivers/ata/ide-sff.c +++ b/drivers/ata/ide-sff.c @@ -15,13 +15,74 @@ #define DISK_SLAVE 1 /** + * Read a byte from the ATA controller + * @param ide IDE port structure + * @param addr Register adress + * @return Register's content + */ +static inline uint8_t ata_rd_byte(struct ide_port *ide, void __iomem *addr) +{ + if (ide->io.mmio) + return readb(addr); + else + return (uint8_t) inb((int) addr); +} + +/** + * Write a byte to the ATA controller + * @param ide IDE port structure + * @param value Value to write + * @param addr Register adress + * @return Register's content + */ +static inline void ata_wr_byte(struct ide_port *ide, uint8_t value, + void __iomem *addr) +{ + if (ide->io.mmio) + writeb(value, addr); + else + outb(value, (int) addr); +} + +/** + * Read a word from the ATA controller + * @param ide IDE port structure + * @param addr Register adress + * @return Register's content + */ +static inline uint16_t ata_rd_word(struct ide_port *ide, + void __iomem *addr) +{ + if (ide->io.mmio) + return readw(addr); + else + return (uint16_t) inw((int) addr); +} + +/** + * Write a word to the ATA controller + * @param ide IDE port structure + * @param value Value to write + * @param addr Register adress + * @return Register's content + */ +static inline void ata_wr_word(struct ide_port *ide, uint16_t value, + void __iomem *addr) +{ + if (ide->io.mmio) + writew(value, addr); + else + outw(value, (int) addr); +} + +/** * Read the status register of the ATA drive * @param io Register file * @return Register's content */ static uint8_t ata_rd_status(struct ide_port *ide) { - return readb(ide->io.status_addr); + return ata_rd_byte(ide, ide->io.status_addr); } /** @@ -83,12 +144,13 @@ static int ata_set_lba_sector(struct ide_port *ide, unsigned drive, uint64_t num if (num > 0x0FFFFFFF || drive > 1) return -EINVAL; - writeb(0xA0 | LBA_FLAG | drive << 4 | num >> 24, ide->io.device_addr); - writeb(0x00, ide->io.error_addr); - writeb(0x01, ide->io.nsect_addr); - writeb(num, ide->io.lbal_addr); /* 0 ... 7 */ - writeb(num >> 8, ide->io.lbam_addr); /* 8 ... 15 */ - writeb(num >> 16, ide->io.lbah_addr); /* 16 ... 23 */ + ata_wr_byte(ide, 0xA0 | LBA_FLAG | drive << 4 | num >> 24, + ide->io.device_addr); + ata_wr_byte(ide, 0x00, ide->io.error_addr); + ata_wr_byte(ide, 0x01, ide->io.nsect_addr); + ata_wr_byte(ide, num, ide->io.lbal_addr); /* 0 ... 7 */ + ata_wr_byte(ide, num >> 8, ide->io.lbam_addr); /* 8 ... 15 */ + ata_wr_byte(ide, num >> 16, ide->io.lbah_addr); /* 16 ... 23 */ return 0; } @@ -107,7 +169,7 @@ static int ata_wr_cmd(struct ide_port *ide, uint8_t cmd) if (rc != 0) return rc; - writeb(cmd, ide->io.command_addr); + ata_wr_byte(ide, cmd, ide->io.command_addr); return 0; } @@ -118,7 +180,7 @@ static int ata_wr_cmd(struct ide_port *ide, uint8_t cmd) */ static void ata_wr_dev_ctrl(struct ide_port *ide, uint8_t val) { - writeb(val, ide->io.ctl_addr); + ata_wr_byte(ide, val, ide->io.ctl_addr); } /** @@ -133,10 +195,10 @@ static void ata_rd_sector(struct ide_port *ide, void *buf) if (ide->io.dataif_be) { for (; u > 0; u--) - *b++ = be16_to_cpu(readw(ide->io.data_addr)); + *b++ = be16_to_cpu(ata_rd_word(ide, ide->io.data_addr)); } else { for (; u > 0; u--) - *b++ = le16_to_cpu(readw(ide->io.data_addr)); + *b++ = le16_to_cpu(ata_rd_word(ide, ide->io.data_addr)); } } @@ -152,10 +214,10 @@ static void ata_wr_sector(struct ide_port *ide, const void *buf) if (ide->io.dataif_be) { for (; u > 0; u--) - writew(cpu_to_be16(*b++), ide->io.data_addr); + ata_wr_word(ide, cpu_to_be16(*b++), ide->io.data_addr); } else { for (; u > 0; u--) - writew(cpu_to_le16(*b++), ide->io.data_addr); + ata_wr_word(ide, cpu_to_le16(*b++), ide->io.data_addr); } } @@ -169,10 +231,10 @@ static int ide_read_id(struct ata_port *port, void *buf) struct ide_port *ide = to_ata_drive_access(port); int rc; - writeb(0xA0, ide->io.device_addr); /* FIXME drive */ - writeb(0x00, ide->io.lbal_addr); - writeb(0x00, ide->io.lbam_addr); - writeb(0x00, ide->io.lbah_addr); + ata_wr_byte(ide, 0xA0, ide->io.device_addr); /* FIXME drive */ + ata_wr_byte(ide, 0x00, ide->io.lbal_addr); + ata_wr_byte(ide, 0x00, ide->io.lbam_addr); + ata_wr_byte(ide, 0x00, ide->io.lbah_addr); rc = ata_wr_cmd(ide, ATA_CMD_ID_ATA); if (rc != 0) @@ -327,6 +389,8 @@ int ide_port_register(struct ide_port *ide) ide->port.ops = &ide_ops; ret = ata_port_register(&ide->port); + if (!ret) + ata_port_detect(&ide->port); if (ret) free(ide); diff --git a/drivers/ata/intf_platform_ide.c b/drivers/ata/intf_platform_ide.c index 8ae0f054c3..ecc2546d84 100644 --- a/drivers/ata/intf_platform_ide.c +++ b/drivers/ata/intf_platform_ide.c @@ -82,15 +82,46 @@ static int platform_ide_probe(struct device_d *dev) struct ide_port_info *pdata = dev->platform_data; struct ide_port *ide; void *reg_base, *alt_base; + struct resource *reg, *alt; + int mmio; if (pdata == NULL) { dev_err(dev, "No platform data. Cannot continue\n"); return -EINVAL; } + alt = NULL; + reg = dev_get_resource(dev, IORESOURCE_MEM, 0); + mmio = (reg != NULL); + if (reg != NULL) { + reg = request_iomem_region(dev_name(dev), reg->start, + reg->end); + alt = dev_get_resource(dev, IORESOURCE_MEM, 1); + if (alt != NULL) + alt = request_iomem_region(dev_name(dev), alt->start, + alt->end); + } else { + reg = dev_get_resource(dev, IORESOURCE_IO, 0); + if (reg != NULL) { + reg = request_ioport_region(dev_name(dev), reg->start, + reg->end); + alt = dev_get_resource(dev, IORESOURCE_IO, 1); + if (alt != NULL) + alt = request_ioport_region(dev_name(dev), + alt->start, + alt->end); + } + } + + reg_base = (reg != NULL ? (void __force __iomem *) reg->start : NULL); + alt_base = (alt != NULL ? (void __force __iomem *) alt->start : NULL); + + if (!reg_base) + return -ENODEV; + ide = xzalloc(sizeof(*ide)); - reg_base = dev_request_mem_region(dev, 0); - alt_base = dev_request_mem_region(dev, 1); + ide->io.mmio = mmio; + platform_ide_setup_port(reg_base, alt_base, &ide->io, pdata->ioport_shift); ide->io.reset = pdata->reset; ide->io.dataif_be = pdata->dataif_be; @@ -125,6 +156,4 @@ device_platform_driver(platform_ide_driver); * * 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 37560fd46f..2cf3ee6892 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -241,13 +241,14 @@ int register_driver(struct driver_d *drv) } EXPORT_SYMBOL(register_driver); -struct resource *dev_get_resource(struct device_d *dev, int num) +struct resource *dev_get_resource(struct device_d *dev, unsigned long type, + int num) { int i, n = 0; for (i = 0; i < dev->num_resources; i++) { struct resource *res = &dev->resource[i]; - if (resource_type(res) == IORESOURCE_MEM) { + if (resource_type(res) == type) { if (n == num) return res; n++; @@ -261,7 +262,7 @@ void *dev_get_mem_region(struct device_d *dev, int num) { struct resource *res; - res = dev_get_resource(dev, num); + res = dev_get_resource(dev, IORESOURCE_MEM, num); if (!res) return NULL; @@ -270,13 +271,14 @@ void *dev_get_mem_region(struct device_d *dev, int num) EXPORT_SYMBOL(dev_get_mem_region); struct resource *dev_get_resource_by_name(struct device_d *dev, + unsigned long type, const char *name) { int i; for (i = 0; i < dev->num_resources; i++) { struct resource *res = &dev->resource[i]; - if (resource_type(res) != IORESOURCE_MEM) + if (resource_type(res) != type) continue; if (!res->name) continue; @@ -291,7 +293,7 @@ void *dev_get_mem_region_by_name(struct device_d *dev, const char *name) { struct resource *res; - res = dev_get_resource_by_name(dev, name); + res = dev_get_resource_by_name(dev, IORESOURCE_MEM, name); if (!res) return NULL; @@ -303,7 +305,7 @@ void __iomem *dev_request_mem_region_by_name(struct device_d *dev, const char *n { struct resource *res; - res = dev_get_resource_by_name(dev, name); + res = dev_get_resource_by_name(dev, IORESOURCE_MEM, name); if (!res) return NULL; @@ -319,7 +321,7 @@ void __iomem *dev_request_mem_region(struct device_d *dev, int num) { struct resource *res; - res = dev_get_resource(dev, num); + res = dev_get_resource(dev, IORESOURCE_MEM, num); if (!res) return NULL; diff --git a/drivers/gpio/gpio-generic.c b/drivers/gpio/gpio-generic.c index a2fc400431..5c46282045 100644 --- a/drivers/gpio/gpio-generic.c +++ b/drivers/gpio/gpio-generic.c @@ -310,7 +310,7 @@ static void __iomem *bgpio_map(struct device_d *dev, const char *name, *err = 0; - r = dev_get_resource_by_name(dev, name); + r = dev_get_resource_by_name(dev, IORESOURCE_MEM, name); if (!r) return NULL; @@ -342,7 +342,7 @@ static int bgpio_dev_probe(struct device_d *dev) struct bgpio_chip *bgc; struct bgpio_pdata *pdata = dev->platform_data; - r = dev_get_resource_by_name(dev, "dat"); + r = dev_get_resource_by_name(dev, IORESOURCE_MEM, "dat"); if (!r) return -EINVAL; diff --git a/drivers/gpio/gpio-imx.c b/drivers/gpio/gpio-imx.c index a71492a3c3..d32638cf0b 100644 --- a/drivers/gpio/gpio-imx.c +++ b/drivers/gpio/gpio-imx.c @@ -113,11 +113,21 @@ static int imx_gpio_get_value(struct gpio_chip *chip, unsigned gpio) return val & (1 << gpio) ? 1 : 0; } +static int imx_get_direction(struct gpio_chip *chip, unsigned offset) +{ + struct imx_gpio_chip *imxgpio = container_of(chip, struct imx_gpio_chip, chip); + void __iomem *base = imxgpio->base; + u32 val = readl(base + imxgpio->regs->gdir); + + return (val & (1 << offset)) ? GPIOF_DIR_OUT : GPIOF_DIR_IN; +} + static struct gpio_ops imx_gpio_ops = { .direction_input = imx_gpio_direction_input, .direction_output = imx_gpio_direction_output, .get = imx_gpio_get_value, .set = imx_gpio_set_value, + .get_direction = imx_get_direction, }; static int imx_gpio_probe(struct device_d *dev) diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index 8fc84c34d8..e6722e1916 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -70,7 +70,7 @@ static int syscon_probe(struct device_d *dev) if (!syscon) return -ENOMEM; - res = dev_get_resource(dev, 0); + res = dev_get_resource(dev, IORESOURCE_MEM, 0); if (!res) { free(syscon); return -ENOENT; diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index 7ea23b7b0c..0466a15289 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -47,7 +47,7 @@ static int sram_probe(struct device_d *dev) sram->cdev.name = asprintf("sram%d", cdev_find_free_index("sram")); - res = dev_get_resource(dev, 0); + res = dev_get_resource(dev, IORESOURCE_MEM, 0); sram->cdev.size = (unsigned long)resource_size(res); sram->cdev.ops = &memops; diff --git a/drivers/mtd/core.c b/drivers/mtd/core.c index c97c8c1403..d954f72886 100644 --- a/drivers/mtd/core.c +++ b/drivers/mtd/core.c @@ -409,6 +409,9 @@ int add_mtd_device(struct mtd_info *mtd, char *devname, int device_id) devfs_create(&mtd->cdev); + if (mtd_can_have_bb(mtd)) + mtd->cdev_bb = mtd_add_bb(mtd, NULL); + if (mtd->parent && !mtd->master) of_parse_partitions(&mtd->cdev, mtd->parent->device_node); diff --git a/drivers/mtd/nand/nand-bb.c b/drivers/mtd/nand/nand-bb.c index f387ef687a..d888f903e0 100644 --- a/drivers/mtd/nand/nand-bb.c +++ b/drivers/mtd/nand/nand-bb.c @@ -25,22 +25,19 @@ #include <init.h> #include <ioctl.h> #include <nand.h> -#include <linux/mtd/mtd-abi.h> +#include <linux/mtd/mtd.h> #include <fcntl.h> #include <libgen.h> #include <linux/list.h> +#include <linux/err.h> struct nand_bb { - char cdevname[MAX_DRIVER_NAME]; - struct cdev *cdev_parent; char *name; int open; int needs_write; - struct mtd_info_user info; + struct mtd_info *mtd; - loff_t raw_size; - loff_t size; loff_t offset; unsigned long flags; void *writebuf; @@ -54,31 +51,28 @@ static ssize_t nand_bb_read(struct cdev *cdev, void *buf, size_t count, loff_t offset, ulong flags) { struct nand_bb *bb = cdev->priv; - struct cdev *parent = bb->cdev_parent; + size_t retlen; int ret, bytes = 0, now; debug("%s 0x%08llx %d\n", __func__, offset, count); - while(count) { - ret = cdev_ioctl(parent, MEMGETBADBLOCK, &bb->offset); - if (ret < 0) - return ret; - - if (ret) { + while (count) { + if (mtd_block_isbad(bb->mtd, offset)) { printf("skipping bad block at 0x%08llx\n", bb->offset); - bb->offset += bb->info.erasesize; + bb->offset += bb->mtd->erasesize; continue; } - now = min(count, (size_t)(bb->info.erasesize - - ((size_t)bb->offset % bb->info.erasesize))); - ret = cdev_read(parent, buf, now, bb->offset, 0); + now = min(count, (size_t)(bb->mtd->erasesize - + ((size_t)bb->offset % bb->mtd->erasesize))); + + ret = mtd_read(bb->mtd, bb->offset, now, &retlen, buf); if (ret < 0) return ret; - buf += now; - count -= now; - bb->offset += now; - bytes += now; + buf += retlen; + count -= retlen; + bb->offset += retlen; + bytes += retlen; }; return bytes; @@ -91,29 +85,25 @@ static ssize_t nand_bb_read(struct cdev *cdev, void *buf, size_t count, static int nand_bb_write_buf(struct nand_bb *bb, size_t count) { int ret, now; - struct cdev *parent = bb->cdev_parent; + size_t retlen; void *buf = bb->writebuf; loff_t cur_ofs = bb->offset & ~(BB_WRITEBUF_SIZE - 1); while (count) { - ret = cdev_ioctl(parent, MEMGETBADBLOCK, &cur_ofs); - if (ret < 0) - return ret; - - if (ret) { + if (mtd_block_isbad(bb->mtd, cur_ofs)) { debug("skipping bad block at 0x%08llx\n", cur_ofs); - bb->offset += bb->info.erasesize; - cur_ofs += bb->info.erasesize; + bb->offset += bb->mtd->erasesize; + cur_ofs += bb->mtd->erasesize; continue; } - now = min(count, (size_t)(bb->info.erasesize)); - ret = cdev_write(parent, buf, now, cur_ofs, 0); + now = min(count, (size_t)(bb->mtd->erasesize)); + ret = mtd_write(bb->mtd, cur_ofs, now, &retlen, buf); if (ret < 0) return ret; - buf += now; - count -= now; - cur_ofs += now; + buf += retlen; + count -= retlen; + cur_ofs += retlen; }; return 0; @@ -152,13 +142,17 @@ static ssize_t nand_bb_write(struct cdev *cdev, const void *buf, size_t count, static int nand_bb_erase(struct cdev *cdev, size_t count, loff_t offset) { struct nand_bb *bb = cdev->priv; + struct erase_info erase = {}; if (offset != 0) { printf("can only erase from beginning of device\n"); return -EINVAL; } - return cdev_erase(bb->cdev_parent, bb->raw_size, 0); + erase.addr = 0; + erase.len = bb->mtd->size; + + return mtd_erase(bb->mtd, &erase); } #endif @@ -195,16 +189,12 @@ static int nand_bb_close(struct cdev *cdev) static int nand_bb_calc_size(struct nand_bb *bb) { loff_t pos = 0; - int ret; - while (pos < bb->raw_size) { - ret = cdev_ioctl(bb->cdev_parent, MEMGETBADBLOCK, &pos); - if (ret < 0) - return ret; - if (!ret) - bb->cdev.size += bb->info.erasesize; + while (pos < bb->mtd->size) { + if (!mtd_block_isbad(bb->mtd, pos)) + bb->cdev.size += bb->mtd->erasesize; - pos += bb->info.erasesize; + pos += bb->mtd->erasesize; } return 0; @@ -215,22 +205,18 @@ static loff_t nand_bb_lseek(struct cdev *cdev, loff_t __offset) struct nand_bb *bb = cdev->priv; loff_t raw_pos = 0; uint32_t offset = __offset; - int ret; /* lseek only in readonly mode */ if (bb->flags & O_ACCMODE) return -ENOSYS; - while (raw_pos < bb->raw_size) { - off_t now = min(offset, bb->info.erasesize); + while (raw_pos < bb->mtd->size) { + off_t now = min(offset, bb->mtd->erasesize); - ret = cdev_ioctl(bb->cdev_parent, MEMGETBADBLOCK, &raw_pos); - if (ret < 0) - return ret; - if (!ret) { + if (mtd_block_isbad(bb->mtd, raw_pos)) { + raw_pos += bb->mtd->erasesize; + } else { offset -= now; raw_pos += now; - } else { - raw_pos += bb->info.erasesize; } if (!offset) { @@ -255,37 +241,18 @@ static struct file_operations nand_bb_ops = { static LIST_HEAD(bb_list); -/** - * Add a bad block aware device ontop of another (NAND) device - * @param[in] dev The device to add a partition on - * @param[in] name Partition name (can be obtained with devinfo command) - * @return The device representing the new partition. - */ -int dev_add_bb_dev(const char *path, const char *name) +struct cdev *mtd_add_bb(struct mtd_info *mtd, const char *name) { struct nand_bb *bb; - int ret = -ENOMEM; + int ret; bb = xzalloc(sizeof(*bb)); + bb->mtd = mtd; - bb->cdev_parent = cdev_open(path, O_RDWR); - if (!bb->cdev_parent) - goto out1; - - if (name) { - strcpy(bb->cdevname, name); - } else { - strcpy(bb->cdevname, path); - strcat(bb->cdevname, ".bb"); - } - - bb->cdev.name = bb->cdevname; - - bb->raw_size = bb->cdev_parent->size; - - ret = cdev_ioctl(bb->cdev_parent, MEMGETINFO, &bb->info); - if (ret) - goto out4; + if (name) + bb->cdev.name = xstrdup(name); + else + bb->cdev.name = asprintf("%s.bb", mtd->cdev.name); nand_bb_calc_size(bb); bb->cdev.ops = &nand_bb_ops; @@ -293,28 +260,48 @@ int dev_add_bb_dev(const char *path, const char *name) ret = devfs_create(&bb->cdev); if (ret) - goto out4; + goto err; list_add_tail(&bb->list, &bb_list); - return 0; + return &bb->cdev; -out4: - cdev_close(bb->cdev_parent); -out1: +err: free(bb); - return ret; + return ERR_PTR(ret); +} + +/** + * Add a bad block aware device ontop of another (NAND) device + * @param[in] dev The device to add a partition on + * @param[in] name Partition name (can be obtained with devinfo command) + * @return The device representing the new partition. + */ +int dev_add_bb_dev(const char *path, const char *name) +{ + struct cdev *parent, *cdev; + + parent = cdev_by_name(path); + if (!parent) + return -ENODEV; + + if (!parent->mtd) + return -EINVAL; + + cdev = mtd_add_bb(parent->mtd, name); + + return PTR_ERR(cdev); } int dev_remove_bb_dev(const char *name) { - struct nand_bb *bb; + struct nand_bb *bb, *tmp; - list_for_each_entry(bb, &bb_list, list) { + list_for_each_entry_safe(bb, tmp, &bb_list, list) { if (!strcmp(bb->cdev.name, name)) { devfs_remove(&bb->cdev); - cdev_close(bb->cdev_parent); list_del_init(&bb->list); + free(bb->name); free(bb); return 0; } diff --git a/drivers/mtd/nand/nand_mxs.c b/drivers/mtd/nand/nand_mxs.c index b06b558b08..d5428bc485 100644 --- a/drivers/mtd/nand/nand_mxs.c +++ b/drivers/mtd/nand/nand_mxs.c @@ -661,6 +661,7 @@ static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand, uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip; uint32_t corrected = 0, failed = 0; uint8_t *status; + unsigned int max_bitflips = 0; int i, ret; /* Compile the DMA descriptor - wait for ready. */ @@ -766,6 +767,7 @@ static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand, } corrected += status[i]; + max_bitflips = max_t(unsigned int, max_bitflips, status[i]); } /* Propagate ECC status to the owning MTD. */ @@ -787,10 +789,11 @@ static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand, memcpy(buf, nand_info->data_buf, mtd->writesize); + ret = 0; rtn: mxs_nand_return_dma_descs(nand_info); - return ret; + return ret ? ret : max_bitflips; } /* diff --git a/drivers/of/barebox.c b/drivers/of/barebox.c index 44ec820ec5..8c05924be3 100644 --- a/drivers/of/barebox.c +++ b/drivers/of/barebox.c @@ -24,6 +24,7 @@ #include <malloc.h> #include <partition.h> #include <envfs.h> +#include <linux/mtd/mtd.h> struct of_partition { struct list_head list; @@ -57,6 +58,24 @@ static int environment_probe(struct device_d *dev) if (ret) return ret; + /* + * The environment support is not bad block aware, hence we + * have to use the .bb device. Test if we have a nand device + * and if yes, append .bb to the filename. + */ + if (!strncmp(path, "/dev/", 5)) { + struct cdev *cdev; + char *cdevname; + + cdevname = path + 5; + cdev = cdev_by_name(cdevname); + if (cdev && cdev->mtd && mtd_can_have_bb(cdev->mtd)) { + char *bbpath = asprintf("%s.bb", path); + free(path); + path = bbpath; + } + } + dev_info(dev, "setting default environment path to %s\n", path); default_environment_path_set(path); diff --git a/drivers/of/partition.c b/drivers/of/partition.c index 5ed44a84d2..074be090e6 100644 --- a/drivers/of/partition.c +++ b/drivers/of/partition.c @@ -61,9 +61,6 @@ struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node) new = devfs_add_partition(cdev->name, offset, size, flags, filename); - if (cdev->mtd && cdev->mtd->type == MTD_NANDFLASH) - dev_add_bb_dev(filename, NULL); - free(filename); return new; diff --git a/drivers/serial/serial_ns16550.c b/drivers/serial/serial_ns16550.c index 0c00eb182c..709f704cb4 100644 --- a/drivers/serial/serial_ns16550.c +++ b/drivers/serial/serial_ns16550.c @@ -47,6 +47,7 @@ struct ns16550_priv { struct console_device cdev; struct NS16550_plat plat; int access_width; + int mmio; struct clk *clk; uint32_t fcrval; }; @@ -62,6 +63,90 @@ struct ns16550_drvdata { }; /** + * @brief read system i/o (byte) + * @param[in] addr address to read + * @param[in] mmio memory i/o space or i/o port space + */ +static inline uint8_t ns16550_sys_readb(void __iomem *addr, int mmio) +{ + if (mmio) + return readb(addr); + else + return (uint8_t) inb((int) addr); +} + +/** + * @brief read system i/o (word) + * @param[in] addr address to read + * @param[in] mmio memory i/o space or i/o port space + */ +static inline uint16_t ns16550_sys_readw(void __iomem *addr, int mmio) +{ + if (mmio) + return readw(addr); + else + return (uint16_t) inw((int) addr); +} + +/** + * @brief read system i/o (dword) + * @param[in] addr address to read + * @param[in] mmio memory i/o space or i/o port space + */ +static inline uint32_t ns16550_sys_readl(void __iomem *addr, int mmio) +{ + if (mmio) + return readl(addr); + else + return (uint32_t) inl((int) addr); +} + +/** + * @brief write system i/o (byte) + * @param[in] val data to write + * @param[in] addr address to write to + * @param[in] mmio memory i/o space or i/o port space + */ +static inline void ns16550_sys_writeb(uint8_t val, void __iomem *addr, + int mmio) +{ + if (mmio) + writeb(val, addr); + else + outb(val, (int) addr); +} + +/** + * @brief read system i/o (word) + * @param[in] val data to write + * @param[in] addr address to write to + * @param[in] mmio memory i/o space or i/o port space + */ +static inline void ns16550_sys_writew(uint16_t val, void __iomem *addr, + int mmio) +{ + if (mmio) + writew(val, addr); + else + outw(val, (int) addr); +} + +/** + * @brief read system i/o (dword) + * @param[in] val data to write + * @param[in] addr address to write to + * @param[in] mmio memory i/o space or i/o port space + */ +static inline void ns16550_sys_writel(uint32_t val, void __iomem *addr, + int mmio) +{ + if (mmio) + writel(val, addr); + else + outl(val, (int) addr); +} + +/** * @brief read register * * @param[in] cdev pointer to console device @@ -78,16 +163,13 @@ static uint32_t ns16550_read(struct console_device *cdev, uint32_t off) off <<= plat->shift; - if (plat->reg_read) - return plat->reg_read((unsigned long)dev->priv, off); - switch (width) { case IORESOURCE_MEM_8BIT: - return readb(dev->priv + off); + return ns16550_sys_readb(dev->priv + off, priv->mmio); case IORESOURCE_MEM_16BIT: - return readw(dev->priv + off); + return ns16550_sys_readw(dev->priv + off, priv->mmio); case IORESOURCE_MEM_32BIT: - return readl(dev->priv + off); + return ns16550_sys_readl(dev->priv + off, priv->mmio); } return -1; } @@ -109,20 +191,15 @@ static void ns16550_write(struct console_device *cdev, uint32_t val, off <<= plat->shift; - if (plat->reg_write) { - plat->reg_write(val, (unsigned long)dev->priv, off); - return; - } - switch (width) { case IORESOURCE_MEM_8BIT: - writeb(val & 0xff, dev->priv + off); + ns16550_sys_writeb(val & 0xff, dev->priv + off, priv->mmio); break; case IORESOURCE_MEM_16BIT: - writew(val & 0xffff, dev->priv + off); + ns16550_sys_writew(val & 0xffff, dev->priv + off, priv->mmio); break; case IORESOURCE_MEM_32BIT: - writel(val, dev->priv + off); + ns16550_sys_writel(val, dev->priv + off, priv->mmio); break; } } @@ -194,6 +271,10 @@ static void ns16450_serial_init_port(struct console_device *cdev) static void ns16550_omap_init_port(struct console_device *cdev) { + struct ns16550_priv *priv = to_ns16550_priv(cdev); + + priv->plat.shift = 2; + ns16550_serial_init_port(cdev); ns16550_write(cdev, 0x07, omap_mdr1); /* Disable */ @@ -293,16 +374,30 @@ static int ns16550_probe(struct device_d *dev) struct console_device *cdev; struct NS16550_plat *plat = (struct NS16550_plat *)dev->platform_data; struct ns16550_drvdata *devtype; + struct resource *res; int ret; ret = dev_get_drvdata(dev, (unsigned long *)&devtype); if (ret) devtype = &ns16550_drvdata; - dev->priv = dev_request_mem_region(dev, 0); - priv = xzalloc(sizeof(*priv)); + res = dev_get_resource(dev, IORESOURCE_MEM, 0); + priv->mmio = (res != NULL); + if (res) { + res = request_iomem_region(dev_name(dev), res->start, res->end); + } else { + res = dev_get_resource(dev, IORESOURCE_IO, 0); + if (res) + res = request_ioport_region(dev_name(dev), res->start, + res->end); + } + if (!res) + goto err; + dev->priv = (void __force __iomem *) res->start; + + if (plat) priv->plat = *plat; else diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 422693ccd5..8a9bbd7917 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -10,6 +10,10 @@ config DRIVER_SPI_ALTERA bool "Altera SPI Master driver" depends on NIOS2 +config DRIVER_SPI_ATH79 + bool "Atheros AR71XX/AR724X/AR913X/AR933X SPI controller driver" + depends on MACH_MIPS_ATH79 + config DRIVER_SPI_ATMEL bool "Atmel (AT91) SPI Master driver" depends on ARCH_AT91 @@ -25,7 +29,7 @@ config DRIVER_SPI_IMX_0_0 config DRIVER_SPI_IMX_0_7 bool - depends on ARCH_IMX25 || ARCH_IMX35 + depends on ARCH_IMX25 || ARCH_IMX35 || ARCH_IMX53 default y config DRIVER_SPI_IMX_2_3 diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 1036f8fbc7..7469479c31 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_SPI) += spi.o +obj-$(CONFIG_DRIVER_SPI_ATH79) += ath79_spi.o obj-$(CONFIG_DRIVER_SPI_IMX) += imx_spi.o obj-$(CONFIG_DRIVER_SPI_MVEBU) += mvebu_spi.o obj-$(CONFIG_DRIVER_SPI_MXS) += mxs_spi.o diff --git a/drivers/spi/ath79_spi.c b/drivers/spi/ath79_spi.c new file mode 100644 index 0000000000..d9ab269da7 --- /dev/null +++ b/drivers/spi/ath79_spi.c @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2013, 2014 Antony Pavlov <antonynpavlov@gmail.com> + * + * This file is part of barebox. + * See file CREDITS for list of people who contributed to this project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <common.h> +#include <init.h> +#include <driver.h> +#include <spi/spi.h> +#include <io.h> +#include <clock.h> + +struct ath79_spi { + struct spi_master master; + void __iomem *regs; + u32 val; + u32 reg_ctrl; +}; + +#define AR71XX_SPI_REG_FS 0x00 /* Function Select */ +#define AR71XX_SPI_REG_CTRL 0x04 /* SPI Control */ +#define AR71XX_SPI_REG_IOC 0x08 /* SPI I/O Control */ +#define AR71XX_SPI_REG_RDS 0x0c /* Read Data Shift */ + +#define AR71XX_SPI_FS_GPIO BIT(0) /* Enable GPIO mode */ + +#define AR71XX_SPI_IOC_DO BIT(0) /* Data Out pin */ +#define AR71XX_SPI_IOC_CLK BIT(8) /* CLK pin */ +#define AR71XX_SPI_IOC_CS(n) BIT(16 + (n)) +#define AR71XX_SPI_IOC_CS0 AR71XX_SPI_IOC_CS(0) +#define AR71XX_SPI_IOC_CS1 AR71XX_SPI_IOC_CS(1) +#define AR71XX_SPI_IOC_CS2 AR71XX_SPI_IOC_CS(2) +#define AR71XX_SPI_IOC_CS_ALL (AR71XX_SPI_IOC_CS0 | AR71XX_SPI_IOC_CS1 | \ + AR71XX_SPI_IOC_CS2) + +static inline u32 ath79_spi_rr(struct ath79_spi *sp, int reg) +{ + return cpu_readl(sp->regs + reg); +} + +static inline void ath79_spi_wr(struct ath79_spi *sp, u32 val, int reg) +{ + cpu_writel(val, sp->regs + reg); +} + +static inline void setbits(struct ath79_spi *sp, int bits, int on) +{ + /* + * We are the only user of SCSPTR so no locking is required. + * Reading bit 2 and 0 in SCSPTR gives pin state as input. + * Writing the same bits sets the output value. + * This makes regular read-modify-write difficult so we + * use sp->val to keep track of the latest register value. + */ + + if (on) + sp->val |= bits; + else + sp->val &= ~bits; + + ath79_spi_wr(sp, sp->val, AR71XX_SPI_REG_IOC); +} + +static inline struct ath79_spi *ath79_spidev_to_sp(struct spi_device *spi) +{ + return container_of(spi->master, struct ath79_spi, master); +} + +static inline void setsck(struct spi_device *spi, int on) +{ + struct ath79_spi *sc = ath79_spidev_to_sp(spi); + + setbits(sc, AR71XX_SPI_IOC_CLK, on); +} + +static inline void setmosi(struct spi_device *spi, int on) +{ + struct ath79_spi *sc = ath79_spidev_to_sp(spi); + + setbits(sc, AR71XX_SPI_IOC_DO, on); +} + +static inline u32 getmiso(struct spi_device *spi) +{ + struct ath79_spi *sc = ath79_spidev_to_sp(spi); + + return !!((ath79_spi_rr(sc, AR71XX_SPI_REG_RDS) & 1)); +} + +#include "spi-bitbang-txrx.h" + +static inline void ath79_spi_chipselect(struct ath79_spi *sp, int chipselect) +{ + int off_bits; + + off_bits = 0xffffffff; + + switch (chipselect) { + case 0: + off_bits &= ~AR71XX_SPI_IOC_CS0; + break; + + case 1: + off_bits &= ~AR71XX_SPI_IOC_CS1; + break; + + case 2: + off_bits &= ~AR71XX_SPI_IOC_CS2; + break; + + case 3: + break; + } + + /* by default inactivate chip selects */ + sp->val |= AR71XX_SPI_IOC_CS_ALL; + sp->val &= off_bits; + + ath79_spi_wr(sp, sp->val, AR71XX_SPI_REG_IOC); +} + +static int ath79_spi_setup(struct spi_device *spi) +{ + struct spi_master *master = spi->master; + struct device_d spi_dev = spi->dev; + + if (spi->bits_per_word != 8) { + dev_err(master->dev, "master doesn't support %d bits per word requested by %s\n", + spi->bits_per_word, spi_dev.name); + return -EINVAL; + } + + if ((spi->mode & (SPI_CPHA | SPI_CPOL)) != SPI_MODE_0) { + dev_err(master->dev, "master doesn't support SPI_MODE%d requested by %s\n", + spi->mode & (SPI_CPHA | SPI_CPOL), spi_dev.name); + return -EINVAL; + } + + return 0; +} + +static int ath79_spi_read(struct spi_device *spi, void *buf, size_t nbyte) +{ + ssize_t cnt = 0; + u8 *rxf_buf = buf; + + while (cnt < nbyte) { + *rxf_buf = bitbang_txrx_be_cpha1(spi, 1000, 1, 0, 8); + rxf_buf++; + cnt++; + } + + return cnt; +} + +static int ath79_spi_write(struct spi_device *spi, + const void *buf, size_t nbyte) +{ + ssize_t cnt = 0; + const u8 *txf_buf = buf; + + while (cnt < nbyte) { + bitbang_txrx_be_cpha1(spi, 1000, 1, (u32)*txf_buf, 8); + txf_buf++; + cnt++; + } + + return 0; +} + +static int ath79_spi_transfer(struct spi_device *spi, struct spi_message *mesg) +{ + struct ath79_spi *sc = ath79_spidev_to_sp(spi); + struct spi_transfer *t; + + mesg->actual_length = 0; + + /* activate chip select signal */ + ath79_spi_chipselect(sc, spi->chip_select); + + list_for_each_entry(t, &mesg->transfers, transfer_list) { + + if (t->tx_buf) + ath79_spi_write(spi, t->tx_buf, t->len); + + if (t->rx_buf) + ath79_spi_read(spi, t->rx_buf, t->len); + + mesg->actual_length += t->len; + } + + /* inactivate chip select signal */ + ath79_spi_chipselect(sc, -1); + + return 0; +} + +static void ath79_spi_enable(struct ath79_spi *sp) +{ + /* enable GPIO mode */ + ath79_spi_wr(sp, AR71XX_SPI_FS_GPIO, AR71XX_SPI_REG_FS); + + /* save CTRL register */ + sp->reg_ctrl = ath79_spi_rr(sp, AR71XX_SPI_REG_CTRL); + sp->val = ath79_spi_rr(sp, AR71XX_SPI_REG_IOC); + + /* TODO: setup speed? */ + ath79_spi_wr(sp, 0x43, AR71XX_SPI_REG_CTRL); +} + +static void ath79_spi_disable(struct ath79_spi *sp) +{ + /* restore CTRL register */ + ath79_spi_wr(sp, sp->reg_ctrl, AR71XX_SPI_REG_CTRL); + /* disable GPIO mode */ + ath79_spi_wr(sp, 0, AR71XX_SPI_REG_FS); +} + +static int ath79_spi_probe(struct device_d *dev) +{ + struct spi_master *master; + struct ath79_spi *ath79_spi; + + ath79_spi = xzalloc(sizeof(*ath79_spi)); + dev->priv = ath79_spi; + + master = &ath79_spi->master; + master->dev = dev; + + master->bus_num = dev->id; + master->setup = ath79_spi_setup; + master->transfer = ath79_spi_transfer; + master->num_chipselect = 3; + + if (IS_ENABLED(CONFIG_OFDEVICE)) { + struct device_node *node = dev->device_node; + u32 num_cs; + int ret; + + ret = of_property_read_u32(node, "num-chipselects", &num_cs); + if (ret) + num_cs = 3; + + if (num_cs > 3) { + dev_err(dev, "master doesn't support num-chipselects > 3\n"); + } + + master->num_chipselect = num_cs; + } + + ath79_spi->regs = dev_request_mem_region(dev, 0); + + /* enable gpio mode */ + ath79_spi_enable(ath79_spi); + + /* inactivate chip select signal */ + ath79_spi_chipselect(ath79_spi, -1); + + spi_register_master(master); + + return 0; +} + +static void ath79_spi_remove(struct device_d *dev) +{ + struct ath79_spi *sp = dev->priv; + + ath79_spi_disable(sp); +} + +static __maybe_unused struct of_device_id ath79_spi_dt_ids[] = { + { + .compatible = "qca,ath79-spi", + }, + { + /* sentinel */ + } +}; + +static struct driver_d ath79_spi_driver = { + .name = "ath79-spi", + .probe = ath79_spi_probe, + .remove = ath79_spi_remove, + .of_compatible = DRV_OF_COMPAT(ath79_spi_dt_ids), +}; +device_platform_driver(ath79_spi_driver); diff --git a/drivers/spi/imx_spi.c b/drivers/spi/imx_spi.c index 7a347f911d..e07cf1a95a 100644 --- a/drivers/spi/imx_spi.c +++ b/drivers/spi/imx_spi.c @@ -67,6 +67,8 @@ #define CSPI_0_0_TEST_LBC (1 << 14) +#define CSPI_0_0_RESET_START (1 << 0) + #define CSPI_0_7_RXDATA 0x00 #define CSPI_0_7_TXDATA 0x04 #define CSPI_0_7_CTRL 0x08 @@ -115,7 +117,6 @@ struct imx_spi { unsigned int (*xchg_single)(struct imx_spi *imx, u32 data); void (*chipselect)(struct spi_device *spi, int active); - void (*init)(struct imx_spi *imx); }; struct spi_imx_devtype_data { @@ -197,13 +198,9 @@ static void cspi_0_0_init(struct imx_spi *imx) { void __iomem *base = imx->regs; - writel(CSPI_0_0_CTRL_ENABLE | CSPI_0_0_CTRL_MASTER, - base + CSPI_0_0_CTRL); - writel(CSPI_0_0_PERIOD_32KHZ, - base + CSPI_0_0_PERIOD); - while (readl(base + CSPI_0_0_INT) & CSPI_0_0_STAT_RR) - readl(base + CSPI_0_0_RXDATA); - writel(0, base + CSPI_0_0_INT); + writel(CSPI_0_0_RESET_START, base + CSPI_0_0_RESET); + do { + } while (readl(base + CSPI_0_0_RESET) & CSPI_0_0_RESET_START); } static unsigned int cspi_0_7_xchg_single(struct imx_spi *imx, unsigned int data) @@ -385,10 +382,6 @@ static void cspi_2_3_chipselect(struct spi_device *spi, int is_active) gpio_set_value(gpio, gpio_cs); } -static void cspi_2_3_init(struct imx_spi *imx) -{ -} - static void imx_spi_do_transfer(struct spi_device *spi, struct spi_transfer *t) { struct imx_spi *imx = container_of(spi->master, struct imx_spi, master); @@ -461,7 +454,6 @@ static __maybe_unused struct spi_imx_devtype_data spi_imx_devtype_data_0_7 = { static __maybe_unused struct spi_imx_devtype_data spi_imx_devtype_data_2_3 = { .chipselect = cspi_2_3_chipselect, .xchg_single = cspi_2_3_xchg_single, - .init = cspi_2_3_init, }; static int imx_spi_dt_probe(struct imx_spi *imx) @@ -525,10 +517,10 @@ static int imx_spi_probe(struct device_d *dev) imx->chipselect = devdata->chipselect; imx->xchg_single = devdata->xchg_single; - imx->init = devdata->init; imx->regs = dev_request_mem_region(dev, 0); - imx->init(imx); + if (devdata->init) + devdata->init(imx); spi_register_master(master); diff --git a/drivers/spi/spi-bitbang-txrx.h b/drivers/spi/spi-bitbang-txrx.h new file mode 100644 index 0000000000..4c74d4e0c5 --- /dev/null +++ b/drivers/spi/spi-bitbang-txrx.h @@ -0,0 +1,95 @@ +/* + * Mix this utility code with some glue code to get one of several types of + * simple SPI master driver. Two do polled word-at-a-time I/O: + * + * - GPIO/parport bitbangers. Provide chipselect() and txrx_word[](), + * expanding the per-word routines from the inline templates below. + * + * - Drivers for controllers resembling bare shift registers. Provide + * chipselect() and txrx_word[](), with custom setup()/cleanup() methods + * that use your controller's clock and chipselect registers. + * + * Some hardware works well with requests at spi_transfer scope: + * + * - Drivers leveraging smarter hardware, with fifos or DMA; or for half + * duplex (MicroWire) controllers. Provide chipselect() and txrx_bufs(), + * and custom setup()/cleanup() methods. + */ + +/* + * The code that knows what GPIO pins do what should have declared four + * functions, ideally as inlines, before including this header: + * + * void setsck(struct spi_device *, int is_on); + * void setmosi(struct spi_device *, int is_on); + * int getmiso(struct spi_device *); + * void spidelay(unsigned); + * + * setsck()'s is_on parameter is a zero/nonzero boolean. + * + * setmosi()'s is_on parameter is a zero/nonzero boolean. + * + * getmiso() is required to return 0 or 1 only. Any other value is invalid + * and will result in improper operation. + * + * A non-inlined routine would call bitbang_txrx_*() routines. The + * main loop could easily compile down to a handful of instructions, + * especially if the delay is a NOP (to run at peak speed). + * + * Since this is software, the timings may not be exactly what your board's + * chips need ... there may be several reasons you'd need to tweak timings + * in these routines, not just to make it faster or slower to match a + * particular CPU clock rate. + */ + +#define spidelay(nsecs) udelay(nsecs/1000) + +static inline u32 +bitbang_txrx_be_cpha0(struct spi_device *spi, + unsigned nsecs, unsigned cpol, + u32 word, u8 bits) +{ + /* if (cpol == 0) this is SPI_MODE_0; else this is SPI_MODE_2 */ + + /* clock starts at inactive polarity */ + for (word <<= (32 - bits); likely(bits); bits--) { + + /* setup MSB (to slave) on trailing edge */ + setmosi(spi, word & (1 << 31)); + spidelay(nsecs); /* T(setup) */ + + setsck(spi, !cpol); + spidelay(nsecs); + + /* sample MSB (from slave) on leading edge */ + word <<= 1; + word |= getmiso(spi); + setsck(spi, cpol); + } + return word; +} + +static inline u32 +bitbang_txrx_be_cpha1(struct spi_device *spi, + unsigned nsecs, unsigned cpol, + u32 word, u8 bits) +{ + /* if (cpol == 0) this is SPI_MODE_1; else this is SPI_MODE_3 */ + + /* clock starts at inactive polarity */ + for (word <<= (32 - bits); likely(bits); bits--) { + + /* setup MSB (to slave) on leading edge */ + setsck(spi, !cpol); + setmosi(spi, word & (1 << 31)); + spidelay(nsecs); /* T(setup) */ + + setsck(spi, cpol); + spidelay(nsecs); + + /* sample MSB (from slave) on trailing edge */ + word <<= 1; + word |= getmiso(spi); + } + return word; +} |