summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/ata/ide-sff.c98
-rw-r--r--drivers/ata/intf_platform_ide.c37
-rw-r--r--drivers/base/driver.c16
-rw-r--r--drivers/gpio/gpio-generic.c4
-rw-r--r--drivers/gpio/gpio-imx.c10
-rw-r--r--drivers/mfd/syscon.c2
-rw-r--r--drivers/misc/sram.c2
-rw-r--r--drivers/mtd/core.c3
-rw-r--r--drivers/mtd/nand/nand-bb.c161
-rw-r--r--drivers/mtd/nand/nand_mxs.c5
-rw-r--r--drivers/of/barebox.c19
-rw-r--r--drivers/of/partition.c3
-rw-r--r--drivers/serial/serial_ns16550.c127
-rw-r--r--drivers/spi/Kconfig6
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/ath79_spi.c299
-rw-r--r--drivers/spi/imx_spi.c22
-rw-r--r--drivers/spi/spi-bitbang-txrx.h95
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;
+}