Commit b061c59c authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'spi/next' of git://git.secretlab.ca/git/linux-2.6

* 'spi/next' of git://git.secretlab.ca/git/linux-2.6: (34 commits)
  spi/dw_spi: move dw_spi.h into drivers/spi
  spi/dw_spi: Fix missing header
  gpio/langwell: Clear edge bit before handling
  gpio/langwell: Simplify demux loop
  gpio/langwell: Convert irq name space
  gpio/langwell: Fix broken irq_eoi change.
  gpio; Make Intel chipset gpio drivers depend on x86
  gpio/cs5535-gpio: Fix section mismatch
  spi/rtc-{ds1390,ds3234,m41t94}: Use spi_get_drvdata() for SPI devices
  spi/davinci: Support DMA transfers larger than 65535 words
  spi/davinci: Use correct length parameter to dma_map_single calls
  gpio: Use __devexit at necessary places
  gpio: add MODULE_DEVICE_TABLE to pch_gpio and ml_ioh_gpio
  gpio/mcp23s08: support mcp23s17 variant
  of_mmc_spi: add card detect irq support
  spi/omap_mcspi: catch xfers of non-multiple SPI word size
  spi/omap_mcspi: Off-by-one error in finding the right divisor
  gpio/pca953x: Fix wrong pointer type
  spi/pl022: rid dangling labels
  spi: add support for SuperH SPI
  ...
parents 99f4065b 568a60ed
...@@ -7,8 +7,13 @@ Required properties: ...@@ -7,8 +7,13 @@ Required properties:
- voltage-ranges : two cells are required, first cell specifies minimum - voltage-ranges : two cells are required, first cell specifies minimum
slot voltage (mV), second cell specifies maximum slot voltage (mV). slot voltage (mV), second cell specifies maximum slot voltage (mV).
Several ranges could be specified. Several ranges could be specified.
- gpios : (optional) may specify GPIOs in this order: Card-Detect GPIO,
Optional properties:
- gpios : may specify GPIOs in this order: Card-Detect GPIO,
Write-Protect GPIO. Write-Protect GPIO.
- interrupts : the interrupt of a card detect interrupt.
- interrupt-parent : the phandle for the interrupt controller that
services interrupts for this device.
Example: Example:
...@@ -20,4 +25,6 @@ Example: ...@@ -20,4 +25,6 @@ Example:
&qe_pio_d 15 0>; &qe_pio_d 15 0>;
voltage-ranges = <3300 3300>; voltage-ranges = <3300 3300>;
spi-max-frequency = <50000000>; spi-max-frequency = <50000000>;
interrupts = <42>;
interrupt-parent = <&PIC>;
}; };
Altera SPI
Required properties:
- compatible : should be "ALTR,spi-1.0".
OpenCores tiny SPI
Required properties:
- compatible : should be "opencores,tiny-spi-rtlsvn2".
- gpios : should specify GPIOs used for chipselect.
Optional properties:
- clock-frequency : input clock frequency to the core.
- baud-width: width, in bits, of the programmable divider used to scale
the input clock to SCLK.
The clock-frequency and baud-width properties are needed only if the divider
is programmable. They are not needed if the divider is fixed.
...@@ -133,7 +133,7 @@ static int __devinit gen_74x164_probe(struct spi_device *spi) ...@@ -133,7 +133,7 @@ static int __devinit gen_74x164_probe(struct spi_device *spi)
return ret; return ret;
} }
static int gen_74x164_remove(struct spi_device *spi) static int __devexit gen_74x164_remove(struct spi_device *spi)
{ {
struct gen_74x164_chip *chip; struct gen_74x164_chip *chip;
int ret; int ret;
......
...@@ -101,7 +101,7 @@ config GPIO_VR41XX ...@@ -101,7 +101,7 @@ config GPIO_VR41XX
config GPIO_SCH config GPIO_SCH
tristate "Intel SCH GPIO" tristate "Intel SCH GPIO"
depends on GPIOLIB && PCI depends on GPIOLIB && PCI && X86
select MFD_CORE select MFD_CORE
select LPC_SCH select LPC_SCH
help help
...@@ -321,13 +321,13 @@ config GPIO_BT8XX ...@@ -321,13 +321,13 @@ config GPIO_BT8XX
config GPIO_LANGWELL config GPIO_LANGWELL
bool "Intel Langwell/Penwell GPIO support" bool "Intel Langwell/Penwell GPIO support"
depends on PCI depends on PCI && X86
help help
Say Y here to support Intel Langwell/Penwell GPIO. Say Y here to support Intel Langwell/Penwell GPIO.
config GPIO_PCH config GPIO_PCH
tristate "PCH GPIO of Intel Topcliff" tristate "PCH GPIO of Intel Topcliff"
depends on PCI depends on PCI && X86
help help
This driver is for PCH(Platform controller Hub) GPIO of Intel Topcliff This driver is for PCH(Platform controller Hub) GPIO of Intel Topcliff
which is an IOH(Input/Output Hub) for x86 embedded processor. which is an IOH(Input/Output Hub) for x86 embedded processor.
...@@ -368,11 +368,11 @@ config GPIO_MAX7301 ...@@ -368,11 +368,11 @@ config GPIO_MAX7301
GPIO driver for Maxim MAX7301 SPI-based GPIO expander. GPIO driver for Maxim MAX7301 SPI-based GPIO expander.
config GPIO_MCP23S08 config GPIO_MCP23S08
tristate "Microchip MCP23S08 I/O expander" tristate "Microchip MCP23Sxx I/O expander"
depends on SPI_MASTER depends on SPI_MASTER
help help
SPI driver for Microchip MCP23S08 I/O expander. This provides SPI driver for Microchip MCP23S08/MPC23S17 I/O expanders.
a GPIO interface supporting inputs and outputs. This provides a GPIO interface supporting inputs and outputs.
config GPIO_MC33880 config GPIO_MC33880
tristate "Freescale MC33880 high-side/low-side switch" tristate "Freescale MC33880 high-side/low-side switch"
......
...@@ -373,7 +373,7 @@ static int __devexit cs5535_gpio_remove(struct platform_device *pdev) ...@@ -373,7 +373,7 @@ static int __devexit cs5535_gpio_remove(struct platform_device *pdev)
return 0; return 0;
} }
static struct platform_driver cs5535_gpio_drv = { static struct platform_driver cs5535_gpio_driver = {
.driver = { .driver = {
.name = DRV_NAME, .name = DRV_NAME,
.owner = THIS_MODULE, .owner = THIS_MODULE,
...@@ -384,12 +384,12 @@ static struct platform_driver cs5535_gpio_drv = { ...@@ -384,12 +384,12 @@ static struct platform_driver cs5535_gpio_drv = {
static int __init cs5535_gpio_init(void) static int __init cs5535_gpio_init(void)
{ {
return platform_driver_register(&cs5535_gpio_drv); return platform_driver_register(&cs5535_gpio_driver);
} }
static void __exit cs5535_gpio_exit(void) static void __exit cs5535_gpio_exit(void)
{ {
platform_driver_unregister(&cs5535_gpio_drv); platform_driver_unregister(&cs5535_gpio_driver);
} }
module_init(cs5535_gpio_init); module_init(cs5535_gpio_init);
......
...@@ -187,31 +187,28 @@ MODULE_DEVICE_TABLE(pci, lnw_gpio_ids); ...@@ -187,31 +187,28 @@ MODULE_DEVICE_TABLE(pci, lnw_gpio_ids);
static void lnw_irq_handler(unsigned irq, struct irq_desc *desc) static void lnw_irq_handler(unsigned irq, struct irq_desc *desc)
{ {
struct lnw_gpio *lnw = get_irq_data(irq); struct irq_data *data = irq_desc_get_irq_data(desc);
u32 base, gpio; struct lnw_gpio *lnw = irq_data_get_irq_handler_data(data);
struct irq_chip *chip = irq_data_get_irq_chip(data);
u32 base, gpio, mask;
unsigned long pending;
void __iomem *gedr; void __iomem *gedr;
u32 gedr_v;
/* check GPIO controller to check which pin triggered the interrupt */ /* check GPIO controller to check which pin triggered the interrupt */
for (base = 0; base < lnw->chip.ngpio; base += 32) { for (base = 0; base < lnw->chip.ngpio; base += 32) {
gedr = gpio_reg(&lnw->chip, base, GEDR); gedr = gpio_reg(&lnw->chip, base, GEDR);
gedr_v = readl(gedr); pending = readl(gedr);
if (!gedr_v) while (pending) {
continue; gpio = __ffs(pending) - 1;
for (gpio = base; gpio < base + 32; gpio++) mask = BIT(gpio);
if (gedr_v & BIT(gpio % 32)) { pending &= ~mask;
pr_debug("pin %d triggered\n", gpio); /* Clear before handling so we can't lose an edge */
generic_handle_irq(lnw->irq_base + gpio); writel(mask, gedr);
} generic_handle_irq(lnw->irq_base + base + gpio);
/* clear the edge detect status bit */ }
writel(gedr_v, gedr);
} }
if (desc->chip->irq_eoi) chip->irq_eoi(data);
desc->chip->irq_eoi(irq_get_irq_data(irq));
else
dev_warn(lnw->chip.dev, "missing EOI handler for irq %d\n", irq);
} }
static int __devinit lnw_gpio_probe(struct pci_dev *pdev, static int __devinit lnw_gpio_probe(struct pci_dev *pdev,
...@@ -279,12 +276,12 @@ static int __devinit lnw_gpio_probe(struct pci_dev *pdev, ...@@ -279,12 +276,12 @@ static int __devinit lnw_gpio_probe(struct pci_dev *pdev,
dev_err(&pdev->dev, "langwell gpiochip_add error %d\n", retval); dev_err(&pdev->dev, "langwell gpiochip_add error %d\n", retval);
goto err5; goto err5;
} }
set_irq_data(pdev->irq, lnw); irq_set_handler_data(pdev->irq, lnw);
set_irq_chained_handler(pdev->irq, lnw_irq_handler); irq_set_chained_handler(pdev->irq, lnw_irq_handler);
for (i = 0; i < lnw->chip.ngpio; i++) { for (i = 0; i < lnw->chip.ngpio; i++) {
set_irq_chip_and_handler_name(i + lnw->irq_base, &lnw_irqchip, irq_set_chip_and_handler_name(i + lnw->irq_base, &lnw_irqchip,
handle_simple_irq, "demux"); handle_simple_irq, "demux");
set_irq_chip_data(i + lnw->irq_base, lnw); irq_set_chip_data(i + lnw->irq_base, lnw);
} }
spin_lock_init(&lnw->lock); spin_lock_init(&lnw->lock);
......
...@@ -146,7 +146,7 @@ static int __devinit mc33880_probe(struct spi_device *spi) ...@@ -146,7 +146,7 @@ static int __devinit mc33880_probe(struct spi_device *spi)
return ret; return ret;
} }
static int mc33880_remove(struct spi_device *spi) static int __devexit mc33880_remove(struct spi_device *spi)
{ {
struct mc33880 *mc; struct mc33880 *mc;
int ret; int ret;
......
...@@ -10,7 +10,13 @@ ...@@ -10,7 +10,13 @@
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/spi/mcp23s08.h> #include <linux/spi/mcp23s08.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <asm/byteorder.h>
/**
* MCP types supported by driver
*/
#define MCP_TYPE_S08 0
#define MCP_TYPE_S17 1
/* Registers are all 8 bits wide. /* Registers are all 8 bits wide.
* *
...@@ -35,27 +41,38 @@ ...@@ -35,27 +41,38 @@
#define MCP_GPIO 0x09 #define MCP_GPIO 0x09
#define MCP_OLAT 0x0a #define MCP_OLAT 0x0a
struct mcp23s08;
struct mcp23s08_ops {
int (*read)(struct mcp23s08 *mcp, unsigned reg);
int (*write)(struct mcp23s08 *mcp, unsigned reg, unsigned val);
int (*read_regs)(struct mcp23s08 *mcp, unsigned reg,
u16 *vals, unsigned n);
};
struct mcp23s08 { struct mcp23s08 {
struct spi_device *spi; struct spi_device *spi;
u8 addr; u8 addr;
u8 cache[11]; u16 cache[11];
/* lock protects the cached values */ /* lock protects the cached values */
struct mutex lock; struct mutex lock;
struct gpio_chip chip; struct gpio_chip chip;
struct work_struct work; struct work_struct work;
const struct mcp23s08_ops *ops;
}; };
/* A given spi_device can represent up to four mcp23s08 chips /* A given spi_device can represent up to eight mcp23sxx chips
* sharing the same chipselect but using different addresses * sharing the same chipselect but using different addresses
* (e.g. chips #0 and #3 might be populated, but not #1 or $2). * (e.g. chips #0 and #3 might be populated, but not #1 or $2).
* Driver data holds all the per-chip data. * Driver data holds all the per-chip data.
*/ */
struct mcp23s08_driver_data { struct mcp23s08_driver_data {
unsigned ngpio; unsigned ngpio;
struct mcp23s08 *mcp[4]; struct mcp23s08 *mcp[8];
struct mcp23s08 chip[]; struct mcp23s08 chip[];
}; };
...@@ -70,7 +87,7 @@ static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg) ...@@ -70,7 +87,7 @@ static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg)
return (status < 0) ? status : rx[0]; return (status < 0) ? status : rx[0];
} }
static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, u8 val) static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, unsigned val)
{ {
u8 tx[3]; u8 tx[3];
...@@ -81,17 +98,81 @@ static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, u8 val) ...@@ -81,17 +98,81 @@ static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, u8 val)
} }
static int static int
mcp23s08_read_regs(struct mcp23s08 *mcp, unsigned reg, u8 *vals, unsigned n) mcp23s08_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n)
{ {
u8 tx[2]; u8 tx[2], *tmp;
int status;
if ((n + reg) > sizeof mcp->cache) if ((n + reg) > sizeof mcp->cache)
return -EINVAL; return -EINVAL;
tx[0] = mcp->addr | 0x01; tx[0] = mcp->addr | 0x01;
tx[1] = reg; tx[1] = reg;
return spi_write_then_read(mcp->spi, tx, sizeof tx, vals, n);
tmp = (u8 *)vals;
status = spi_write_then_read(mcp->spi, tx, sizeof tx, tmp, n);
if (status >= 0) {
while (n--)
vals[n] = tmp[n]; /* expand to 16bit */
}
return status;
}
static int mcp23s17_read(struct mcp23s08 *mcp, unsigned reg)
{
u8 tx[2], rx[2];
int status;
tx[0] = mcp->addr | 0x01;
tx[1] = reg << 1;
status = spi_write_then_read(mcp->spi, tx, sizeof tx, rx, sizeof rx);
return (status < 0) ? status : (rx[0] | (rx[1] << 8));
}
static int mcp23s17_write(struct mcp23s08 *mcp, unsigned reg, unsigned val)
{
u8 tx[4];
tx[0] = mcp->addr;
tx[1] = reg << 1;
tx[2] = val;
tx[3] = val >> 8;
return spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0);
}
static int
mcp23s17_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n)
{
u8 tx[2];
int status;
if ((n + reg) > sizeof mcp->cache)
return -EINVAL;
tx[0] = mcp->addr | 0x01;
tx[1] = reg << 1;
status = spi_write_then_read(mcp->spi, tx, sizeof tx,
(u8 *)vals, n * 2);
if (status >= 0) {
while (n--)
vals[n] = __le16_to_cpu((__le16)vals[n]);
}
return status;
} }
static const struct mcp23s08_ops mcp23s08_ops = {
.read = mcp23s08_read,
.write = mcp23s08_write,
.read_regs = mcp23s08_read_regs,
};
static const struct mcp23s08_ops mcp23s17_ops = {
.read = mcp23s17_read,
.write = mcp23s17_write,
.read_regs = mcp23s17_read_regs,
};
/*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/
static int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset) static int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset)
...@@ -101,7 +182,7 @@ static int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset) ...@@ -101,7 +182,7 @@ static int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset)
mutex_lock(&mcp->lock); mutex_lock(&mcp->lock);
mcp->cache[MCP_IODIR] |= (1 << offset); mcp->cache[MCP_IODIR] |= (1 << offset);
status = mcp23s08_write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); status = mcp->ops->write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]);
mutex_unlock(&mcp->lock); mutex_unlock(&mcp->lock);
return status; return status;
} }
...@@ -114,7 +195,7 @@ static int mcp23s08_get(struct gpio_chip *chip, unsigned offset) ...@@ -114,7 +195,7 @@ static int mcp23s08_get(struct gpio_chip *chip, unsigned offset)
mutex_lock(&mcp->lock); mutex_lock(&mcp->lock);
/* REVISIT reading this clears any IRQ ... */ /* REVISIT reading this clears any IRQ ... */
status = mcp23s08_read(mcp, MCP_GPIO); status = mcp->ops->read(mcp, MCP_GPIO);
if (status < 0) if (status < 0)
status = 0; status = 0;
else { else {
...@@ -127,20 +208,20 @@ static int mcp23s08_get(struct gpio_chip *chip, unsigned offset) ...@@ -127,20 +208,20 @@ static int mcp23s08_get(struct gpio_chip *chip, unsigned offset)
static int __mcp23s08_set(struct mcp23s08 *mcp, unsigned mask, int value) static int __mcp23s08_set(struct mcp23s08 *mcp, unsigned mask, int value)
{ {
u8 olat = mcp->cache[MCP_OLAT]; unsigned olat = mcp->cache[MCP_OLAT];
if (value) if (value)
olat |= mask; olat |= mask;
else else
olat &= ~mask; olat &= ~mask;
mcp->cache[MCP_OLAT] = olat; mcp->cache[MCP_OLAT] = olat;
return mcp23s08_write(mcp, MCP_OLAT, olat); return mcp->ops->write(mcp, MCP_OLAT, olat);
} }
static void mcp23s08_set(struct gpio_chip *chip, unsigned offset, int value) static void mcp23s08_set(struct gpio_chip *chip, unsigned offset, int value)
{ {
struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip);
u8 mask = 1 << offset; unsigned mask = 1 << offset;
mutex_lock(&mcp->lock); mutex_lock(&mcp->lock);
__mcp23s08_set(mcp, mask, value); __mcp23s08_set(mcp, mask, value);
...@@ -151,14 +232,14 @@ static int ...@@ -151,14 +232,14 @@ static int
mcp23s08_direction_output(struct gpio_chip *chip, unsigned offset, int value) mcp23s08_direction_output(struct gpio_chip *chip, unsigned offset, int value)
{ {
struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip);
u8 mask = 1 << offset; unsigned mask = 1 << offset;
int status; int status;
mutex_lock(&mcp->lock); mutex_lock(&mcp->lock);
status = __mcp23s08_set(mcp, mask, value); status = __mcp23s08_set(mcp, mask, value);
if (status == 0) { if (status == 0) {
mcp->cache[MCP_IODIR] &= ~mask; mcp->cache[MCP_IODIR] &= ~mask;
status = mcp23s08_write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); status = mcp->ops->write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]);
} }
mutex_unlock(&mcp->lock); mutex_unlock(&mcp->lock);
return status; return status;
...@@ -184,16 +265,16 @@ static void mcp23s08_dbg_show(struct seq_file *s, struct gpio_chip *chip) ...@@ -184,16 +265,16 @@ static void mcp23s08_dbg_show(struct seq_file *s, struct gpio_chip *chip)
mcp = container_of(chip, struct mcp23s08, chip); mcp = container_of(chip, struct mcp23s08, chip);
/* NOTE: we only handle one bank for now ... */ /* NOTE: we only handle one bank for now ... */
bank = '0' + ((mcp->addr >> 1) & 0x3); bank = '0' + ((mcp->addr >> 1) & 0x7);
mutex_lock(&mcp->lock); mutex_lock(&mcp->lock);
t = mcp23s08_read_regs(mcp, 0, mcp->cache, sizeof mcp->cache); t = mcp->ops->read_regs(mcp, 0, mcp->cache, ARRAY_SIZE(mcp->cache));
if (t < 0) { if (t < 0) {
seq_printf(s, " I/O ERROR %d\n", t); seq_printf(s, " I/O ERROR %d\n", t);
goto done; goto done;
} }
for (t = 0, mask = 1; t < 8; t++, mask <<= 1) { for (t = 0, mask = 1; t < chip->ngpio; t++, mask <<= 1) {
const char *label; const char *label;
label = gpiochip_is_requested(chip, t); label = gpiochip_is_requested(chip, t);
...@@ -219,28 +300,33 @@ static void mcp23s08_dbg_show(struct seq_file *s, struct gpio_chip *chip) ...@@ -219,28 +300,33 @@ static void mcp23s08_dbg_show(struct seq_file *s, struct gpio_chip *chip)
/*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/
static int mcp23s08_probe_one(struct spi_device *spi, unsigned addr, static int mcp23s08_probe_one(struct spi_device *spi, unsigned addr,
unsigned base, unsigned pullups) unsigned type, unsigned base, unsigned pullups)
{ {
struct mcp23s08_driver_data *data = spi_get_drvdata(spi); struct mcp23s08_driver_data *data = spi_get_drvdata(spi);
struct mcp23s08 *mcp = data->mcp[addr]; struct mcp23s08 *mcp = data->mcp[addr];
int status; int status;
int do_update = 0;
mutex_init(&mcp->lock); mutex_init(&mcp->lock);
mcp->spi = spi; mcp->spi = spi;
mcp->addr = 0x40 | (addr << 1); mcp->addr = 0x40 | (addr << 1);
mcp->chip.label = "mcp23s08",
mcp->chip.direction_input = mcp23s08_direction_input; mcp->chip.direction_input = mcp23s08_direction_input;
mcp->chip.get = mcp23s08_get; mcp->chip.get = mcp23s08_get;
mcp->chip.direction_output = mcp23s08_direction_output; mcp->chip.direction_output = mcp23s08_direction_output;
mcp->chip.set = mcp23s08_set; mcp->chip.set = mcp23s08_set;
mcp->chip.dbg_show = mcp23s08_dbg_show; mcp->chip.dbg_show = mcp23s08_dbg_show;
if (type == MCP_TYPE_S17) {
mcp->ops = &mcp23s17_ops;
mcp->chip.ngpio = 16;
mcp->chip.label = "mcp23s17";
} else {
mcp->ops = &mcp23s08_ops;
mcp->chip.ngpio = 8;
mcp->chip.label = "mcp23s08";
}
mcp->chip.base = base; mcp->chip.base = base;
mcp->chip.ngpio = 8;
mcp->chip.can_sleep = 1; mcp->chip.can_sleep = 1;
mcp->chip.dev = &spi->dev; mcp->chip.dev = &spi->dev;
mcp->chip.owner = THIS_MODULE; mcp->chip.owner = THIS_MODULE;
...@@ -248,45 +334,39 @@ static int mcp23s08_probe_one(struct spi_device *spi, unsigned addr, ...@@ -248,45 +334,39 @@ static int mcp23s08_probe_one(struct spi_device *spi, unsigned addr,
/* verify MCP_IOCON.SEQOP = 0, so sequential reads work, /* verify MCP_IOCON.SEQOP = 0, so sequential reads work,
* and MCP_IOCON.HAEN = 1, so we work with all chips. * and MCP_IOCON.HAEN = 1, so we work with all chips.
*/ */
status = mcp23s08_read(mcp, MCP_IOCON); status = mcp->ops->read(mcp, MCP_IOCON);
if (status < 0) if (status < 0)
goto fail; goto fail;
if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN)) { if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN)) {
status &= ~IOCON_SEQOP; /* mcp23s17 has IOCON twice, make sure they are in sync */
status |= IOCON_HAEN; status &= ~(IOCON_SEQOP | (IOCON_SEQOP << 8));
status = mcp23s08_write(mcp, MCP_IOCON, (u8) status); status |= IOCON_HAEN | (IOCON_HAEN << 8);
status = mcp->ops->write(mcp, MCP_IOCON, status);
if (status < 0) if (status < 0)
goto fail; goto fail;
} }
/* configure ~100K pullups */ /* configure ~100K pullups */
status = mcp23s08_write(mcp, MCP_GPPU, pullups); status = mcp->ops->write(mcp, MCP_GPPU, pullups);
if (status < 0) if (status < 0)
goto fail; goto fail;
status = mcp23s08_read_regs(mcp, 0, mcp->cache, sizeof mcp->cache); status = mcp->ops->read_regs(mcp, 0, mcp->cache, ARRAY_SIZE(mcp->cache));
if (status < 0) if (status < 0)
goto fail; goto fail;
/* disable inverter on input */ /* disable inverter on input */
if (mcp->cache[MCP_IPOL] != 0) { if (mcp->cache[MCP_IPOL] != 0) {
mcp->cache[MCP_IPOL] = 0; mcp->cache[MCP_IPOL] = 0;
do_update = 1; status = mcp->ops->write(mcp, MCP_IPOL, 0);
if (status < 0)
goto fail;
} }
/* disable irqs */ /* disable irqs */
if (mcp->cache[MCP_GPINTEN] != 0) { if (mcp->cache[MCP_GPINTEN] != 0) {
mcp->cache[MCP_GPINTEN] = 0; mcp->cache[MCP_GPINTEN] = 0;
do_update = 1; status = mcp->ops->write(mcp, MCP_GPINTEN, 0);
}
if (do_update) {
u8 tx[4];
tx[0] = mcp->addr;
tx[1] = MCP_IPOL;
memcpy(&tx[2], &mcp->cache[MCP_IPOL], sizeof(tx) - 2);
status = spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0);
if (status < 0) if (status < 0)
goto fail; goto fail;
} }
...@@ -305,19 +385,26 @@ static int mcp23s08_probe(struct spi_device *spi) ...@@ -305,19 +385,26 @@ static int mcp23s08_probe(struct spi_device *spi)
unsigned addr; unsigned addr;
unsigned chips = 0; unsigned chips = 0;
struct mcp23s08_driver_data *data; struct mcp23s08_driver_data *data;
int status; int status, type;
unsigned base; unsigned base;
type = spi_get_device_id(spi)->driver_data;
pdata = spi->dev.platform_data; pdata = spi->dev.platform_data;
if (!pdata || !gpio_is_valid(pdata->base)) { if (!pdata || !gpio_is_valid(pdata->base)) {
dev_dbg(&spi->dev, "invalid or missing platform data\n"); dev_dbg(&spi->dev, "invalid or missing platform data\n");
return -EINVAL; return -EINVAL;
} }
for (addr = 0; addr < 4; addr++) { for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) {
if (!pdata->chip[addr].is_present) if (!pdata->chip[addr].is_present)
continue; continue;
chips++; chips++;
if ((type == MCP_TYPE_S08) && (addr > 3)) {
dev_err(&spi->dev,
"mcp23s08 only supports address 0..3\n");
return -EINVAL;
}
} }
if (!chips) if (!chips)
return -ENODEV; return -ENODEV;
...@@ -329,16 +416,17 @@ static int mcp23s08_probe(struct spi_device *spi) ...@@ -329,16 +416,17 @@ static int mcp23s08_probe(struct spi_device *spi)
spi_set_drvdata(spi, data); spi_set_drvdata(spi, data);
base = pdata->base; base = pdata->base;
for (addr = 0; addr < 4; addr++) { for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) {
if (!pdata->chip[addr].is_present) if (!pdata->chip[addr].is_present)
continue; continue;
chips--; chips--;
data->mcp[addr] = &data->chip[chips]; data->mcp[addr] = &data->chip[chips];
status = mcp23s08_probe_one(spi, addr, base, status = mcp23s08_probe_one(spi, addr, type, base,
pdata->chip[addr].pullups); pdata->chip[addr].pullups);
if (status < 0) if (status < 0)
goto fail; goto fail;
base += 8;
base += (type == MCP_TYPE_S17) ? 16 : 8;
} }
data->ngpio = base - pdata->base; data->ngpio = base - pdata->base;
...@@ -358,7 +446,7 @@ static int mcp23s08_probe(struct spi_device *spi) ...@@ -358,7 +446,7 @@ static int mcp23s08_probe(struct spi_device *spi)
return 0; return 0;
fail: fail:
for (addr = 0; addr < 4; addr++) { for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) {
int tmp; int tmp;
if (!data->mcp[addr]) if (!data->mcp[addr])
...@@ -388,7 +476,7 @@ static int mcp23s08_remove(struct spi_device *spi) ...@@ -388,7 +476,7 @@ static int mcp23s08_remove(struct spi_device *spi)
} }
} }
for (addr = 0; addr < 4; addr++) { for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) {
int tmp; int tmp;
if (!data->mcp[addr]) if (!data->mcp[addr])
...@@ -405,9 +493,17 @@ static int mcp23s08_remove(struct spi_device *spi) ...@@ -405,9 +493,17 @@ static int mcp23s08_remove(struct spi_device *spi)
return status; return status;
} }
static const struct spi_device_id mcp23s08_ids[] = {
{ "mcp23s08", MCP_TYPE_S08 },
{ "mcp23s17", MCP_TYPE_S17 },
{ },
};
MODULE_DEVICE_TABLE(spi, mcp23s08_ids);
static struct spi_driver mcp23s08_driver = { static struct spi_driver mcp23s08_driver = {
.probe = mcp23s08_probe, .probe = mcp23s08_probe,
.remove = mcp23s08_remove, .remove = mcp23s08_remove,
.id_table = mcp23s08_ids,
.driver = { .driver = {
.name = "mcp23s08", .name = "mcp23s08",
.owner = THIS_MODULE, .owner = THIS_MODULE,
...@@ -432,4 +528,3 @@ static void __exit mcp23s08_exit(void) ...@@ -432,4 +528,3 @@ static void __exit mcp23s08_exit(void)
module_exit(mcp23s08_exit); module_exit(mcp23s08_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_ALIAS("spi:mcp23s08");
...@@ -462,7 +462,8 @@ pca953x_get_alt_pdata(struct i2c_client *client) ...@@ -462,7 +462,8 @@ pca953x_get_alt_pdata(struct i2c_client *client)
{ {
struct pca953x_platform_data *pdata; struct pca953x_platform_data *pdata;
struct device_node *node; struct device_node *node;
const uint16_t *val; const __be32 *val;
int size;
node = client->dev.of_node; node = client->dev.of_node;
if (node == NULL) if (node == NULL)
...@@ -475,13 +476,13 @@ pca953x_get_alt_pdata(struct i2c_client *client) ...@@ -475,13 +476,13 @@ pca953x_get_alt_pdata(struct i2c_client *client)
} }
pdata->gpio_base = -1; pdata->gpio_base = -1;
val = of_get_property(node, "linux,gpio-base", NULL); val = of_get_property(node, "linux,gpio-base", &size);
if (val) { if (val) {
if (*val < 0) if (size != sizeof(*val))
dev_warn(&client->dev, dev_warn(&client->dev, "%s: wrong linux,gpio-base\n",
"invalid gpio-base in device tree\n"); node->full_name);
else else
pdata->gpio_base = *val; pdata->gpio_base = be32_to_cpup(val);
} }
val = of_get_property(node, "polarity", NULL); val = of_get_property(node, "polarity", NULL);
......
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/i2c/sx150x.h> #include <linux/i2c/sx150x.h>
#define NO_UPDATE_PENDING -1
struct sx150x_device_data { struct sx150x_device_data {
u8 reg_pullup; u8 reg_pullup;
u8 reg_pulldn; u8 reg_pulldn;
...@@ -47,8 +49,11 @@ struct sx150x_chip { ...@@ -47,8 +49,11 @@ struct sx150x_chip {
const struct sx150x_device_data *dev_cfg; const struct sx150x_device_data *dev_cfg;
int irq_summary; int irq_summary;
int irq_base; int irq_base;
int irq_update;
u32 irq_sense; u32 irq_sense;
unsigned long irq_set_type_pending; u32 irq_masked;
u32 dev_sense;
u32 dev_masked;
struct irq_chip irq_chip; struct irq_chip irq_chip;
struct mutex lock; struct mutex lock;
}; };
...@@ -312,9 +317,8 @@ static void sx150x_irq_mask(struct irq_data *d) ...@@ -312,9 +317,8 @@ static void sx150x_irq_mask(struct irq_data *d)
chip = container_of(ic, struct sx150x_chip, irq_chip); chip = container_of(ic, struct sx150x_chip, irq_chip);
n = d->irq - chip->irq_base; n = d->irq - chip->irq_base;
chip->irq_masked |= (1 << n);
sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 1); chip->irq_update = n;
sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense, 0);
} }
static void sx150x_irq_unmask(struct irq_data *d) static void sx150x_irq_unmask(struct irq_data *d)
...@@ -326,9 +330,8 @@ static void sx150x_irq_unmask(struct irq_data *d) ...@@ -326,9 +330,8 @@ static void sx150x_irq_unmask(struct irq_data *d)
chip = container_of(ic, struct sx150x_chip, irq_chip); chip = container_of(ic, struct sx150x_chip, irq_chip);
n = d->irq - chip->irq_base; n = d->irq - chip->irq_base;
sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 0); chip->irq_masked &= ~(1 << n);
sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense, chip->irq_update = n;
chip->irq_sense >> (n * 2));
} }
static int sx150x_irq_set_type(struct irq_data *d, unsigned int flow_type) static int sx150x_irq_set_type(struct irq_data *d, unsigned int flow_type)
...@@ -350,7 +353,7 @@ static int sx150x_irq_set_type(struct irq_data *d, unsigned int flow_type) ...@@ -350,7 +353,7 @@ static int sx150x_irq_set_type(struct irq_data *d, unsigned int flow_type)
chip->irq_sense &= ~(3UL << (n * 2)); chip->irq_sense &= ~(3UL << (n * 2));
chip->irq_sense |= val << (n * 2); chip->irq_sense |= val << (n * 2);
chip->irq_set_type_pending |= BIT(n); chip->irq_update = n;
return 0; return 0;
} }
...@@ -404,15 +407,29 @@ static void sx150x_irq_bus_sync_unlock(struct irq_data *d) ...@@ -404,15 +407,29 @@ static void sx150x_irq_bus_sync_unlock(struct irq_data *d)
chip = container_of(ic, struct sx150x_chip, irq_chip); chip = container_of(ic, struct sx150x_chip, irq_chip);
while (chip->irq_set_type_pending) { if (chip->irq_update == NO_UPDATE_PENDING)
n = __ffs(chip->irq_set_type_pending); goto out;
chip->irq_set_type_pending &= ~BIT(n);
if (!(irq_to_desc(n + chip->irq_base)->status & IRQ_MASKED)) n = chip->irq_update;
sx150x_write_cfg(chip, n, 2, chip->irq_update = NO_UPDATE_PENDING;
chip->dev_cfg->reg_sense,
chip->irq_sense >> (n * 2));
}
/* Avoid updates if nothing changed */
if (chip->dev_sense == chip->irq_sense &&
chip->dev_sense == chip->irq_masked)
goto out;
chip->dev_sense = chip->irq_sense;
chip->dev_masked = chip->irq_masked;
if (chip->irq_masked & (1 << n)) {
sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 1);
sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense, 0);
} else {
sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 0);
sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense,
chip->irq_sense >> (n * 2));
}
out:
mutex_unlock(&chip->lock); mutex_unlock(&chip->lock);
} }
...@@ -445,8 +462,11 @@ static void sx150x_init_chip(struct sx150x_chip *chip, ...@@ -445,8 +462,11 @@ static void sx150x_init_chip(struct sx150x_chip *chip,
chip->irq_chip.irq_bus_sync_unlock = sx150x_irq_bus_sync_unlock; chip->irq_chip.irq_bus_sync_unlock = sx150x_irq_bus_sync_unlock;
chip->irq_summary = -1; chip->irq_summary = -1;
chip->irq_base = -1; chip->irq_base = -1;
chip->irq_masked = ~0;
chip->irq_sense = 0; chip->irq_sense = 0;
chip->irq_set_type_pending = 0; chip->dev_masked = ~0;
chip->dev_sense = 0;
chip->irq_update = NO_UPDATE_PENDING;
} }
static int sx150x_init_io(struct sx150x_chip *chip, u8 base, u16 cfg) static int sx150x_init_io(struct sx150x_chip *chip, u8 base, u16 cfg)
......
...@@ -34,6 +34,7 @@ enum { ...@@ -34,6 +34,7 @@ enum {
struct of_mmc_spi { struct of_mmc_spi {
int gpios[NUM_GPIOS]; int gpios[NUM_GPIOS];
bool alow_gpios[NUM_GPIOS]; bool alow_gpios[NUM_GPIOS];
int detect_irq;
struct mmc_spi_platform_data pdata; struct mmc_spi_platform_data pdata;
}; };
...@@ -61,6 +62,22 @@ static int of_mmc_spi_get_ro(struct device *dev) ...@@ -61,6 +62,22 @@ static int of_mmc_spi_get_ro(struct device *dev)
return of_mmc_spi_read_gpio(dev, WP_GPIO); return of_mmc_spi_read_gpio(dev, WP_GPIO);
} }
static int of_mmc_spi_init(struct device *dev,
irqreturn_t (*irqhandler)(int, void *), void *mmc)
{
struct of_mmc_spi *oms = to_of_mmc_spi(dev);
return request_threaded_irq(oms->detect_irq, NULL, irqhandler, 0,
dev_name(dev), mmc);
}
static void of_mmc_spi_exit(struct device *dev, void *mmc)
{
struct of_mmc_spi *oms = to_of_mmc_spi(dev);
free_irq(oms->detect_irq, mmc);
}
struct mmc_spi_platform_data *mmc_spi_get_pdata(struct spi_device *spi) struct mmc_spi_platform_data *mmc_spi_get_pdata(struct spi_device *spi)
{ {
struct device *dev = &spi->dev; struct device *dev = &spi->dev;
...@@ -121,8 +138,13 @@ struct mmc_spi_platform_data *mmc_spi_get_pdata(struct spi_device *spi) ...@@ -121,8 +138,13 @@ struct mmc_spi_platform_data *mmc_spi_get_pdata(struct spi_device *spi)
if (gpio_is_valid(oms->gpios[WP_GPIO])) if (gpio_is_valid(oms->gpios[WP_GPIO]))
oms->pdata.get_ro = of_mmc_spi_get_ro; oms->pdata.get_ro = of_mmc_spi_get_ro;
/* We don't support interrupts yet, let's poll. */ oms->detect_irq = irq_of_parse_and_map(np, 0);
oms->pdata.caps |= MMC_CAP_NEEDS_POLL; if (oms->detect_irq != NO_IRQ) {
oms->pdata.init = of_mmc_spi_init;
oms->pdata.exit = of_mmc_spi_exit;
} else {
oms->pdata.caps |= MMC_CAP_NEEDS_POLL;
}
dev->platform_data = &oms->pdata; dev->platform_data = &oms->pdata;
return dev->platform_data; return dev->platform_data;
......
...@@ -158,7 +158,7 @@ static int __devinit ds1390_probe(struct spi_device *spi) ...@@ -158,7 +158,7 @@ static int __devinit ds1390_probe(struct spi_device *spi)
static int __devexit ds1390_remove(struct spi_device *spi) static int __devexit ds1390_remove(struct spi_device *spi)
{ {
struct ds1390 *chip = platform_get_drvdata(spi); struct ds1390 *chip = spi_get_drvdata(spi);
rtc_device_unregister(chip->rtc); rtc_device_unregister(chip->rtc);
kfree(chip); kfree(chip);
......
...@@ -158,7 +158,7 @@ static int __devinit ds3234_probe(struct spi_device *spi) ...@@ -158,7 +158,7 @@ static int __devinit ds3234_probe(struct spi_device *spi)
static int __devexit ds3234_remove(struct spi_device *spi) static int __devexit ds3234_remove(struct spi_device *spi)
{ {
struct rtc_device *rtc = platform_get_drvdata(spi); struct rtc_device *rtc = spi_get_drvdata(spi);
rtc_device_unregister(rtc); rtc_device_unregister(rtc);
return 0; return 0;
......
...@@ -136,7 +136,7 @@ static int __devinit m41t94_probe(struct spi_device *spi) ...@@ -136,7 +136,7 @@ static int __devinit m41t94_probe(struct spi_device *spi)
static int __devexit m41t94_remove(struct spi_device *spi) static int __devexit m41t94_remove(struct spi_device *spi)
{ {
struct rtc_device *rtc = platform_get_drvdata(spi); struct rtc_device *rtc = spi_get_drvdata(spi);
if (rtc) if (rtc)
rtc_device_unregister(rtc); rtc_device_unregister(rtc);
......
...@@ -53,6 +53,12 @@ if SPI_MASTER ...@@ -53,6 +53,12 @@ if SPI_MASTER
comment "SPI Master Controller Drivers" comment "SPI Master Controller Drivers"
config SPI_ALTERA
tristate "Altera SPI Controller"
select SPI_BITBANG
help
This is the driver for the Altera SPI Controller.
config SPI_ATH79 config SPI_ATH79
tristate "Atheros AR71XX/AR724X/AR913X SPI controller driver" tristate "Atheros AR71XX/AR724X/AR913X SPI controller driver"
depends on ATH79 && GENERIC_GPIO depends on ATH79 && GENERIC_GPIO
...@@ -231,6 +237,13 @@ config SPI_FSL_ESPI ...@@ -231,6 +237,13 @@ config SPI_FSL_ESPI
From MPC8536, 85xx platform uses the controller, and all P10xx, From MPC8536, 85xx platform uses the controller, and all P10xx,
P20xx, P30xx,P40xx, P50xx uses this controller. P20xx, P30xx,P40xx, P50xx uses this controller.
config SPI_OC_TINY
tristate "OpenCores tiny SPI"
depends on GENERIC_GPIO
select SPI_BITBANG
help
This is the driver for OpenCores tiny SPI master controller.
config SPI_OMAP_UWIRE config SPI_OMAP_UWIRE
tristate "OMAP1 MicroWire" tristate "OMAP1 MicroWire"
depends on ARCH_OMAP1 depends on ARCH_OMAP1
...@@ -330,6 +343,12 @@ config SPI_SH_MSIOF ...@@ -330,6 +343,12 @@ config SPI_SH_MSIOF
help help
SPI driver for SuperH MSIOF blocks. SPI driver for SuperH MSIOF blocks.
config SPI_SH
tristate "SuperH SPI controller"
depends on SUPERH
help
SPI driver for SuperH SPI blocks.
config SPI_SH_SCI config SPI_SH_SCI
tristate "SuperH SCI SPI controller" tristate "SuperH SCI SPI controller"
depends on SUPERH depends on SUPERH
......
...@@ -9,6 +9,7 @@ ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG ...@@ -9,6 +9,7 @@ ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG
obj-$(CONFIG_SPI_MASTER) += spi.o obj-$(CONFIG_SPI_MASTER) += spi.o
# SPI master controller drivers (bus) # SPI master controller drivers (bus)
obj-$(CONFIG_SPI_ALTERA) += spi_altera.o
obj-$(CONFIG_SPI_ATMEL) += atmel_spi.o obj-$(CONFIG_SPI_ATMEL) += atmel_spi.o
obj-$(CONFIG_SPI_ATH79) += ath79_spi.o obj-$(CONFIG_SPI_ATH79) += ath79_spi.o
obj-$(CONFIG_SPI_BFIN) += spi_bfin5xx.o obj-$(CONFIG_SPI_BFIN) += spi_bfin5xx.o
...@@ -27,6 +28,7 @@ obj-$(CONFIG_SPI_IMX) += spi_imx.o ...@@ -27,6 +28,7 @@ obj-$(CONFIG_SPI_IMX) += spi_imx.o
obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o
obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o
obj-$(CONFIG_SPI_PXA2XX_PCI) += pxa2xx_spi_pci.o obj-$(CONFIG_SPI_PXA2XX_PCI) += pxa2xx_spi_pci.o
obj-$(CONFIG_SPI_OC_TINY) += spi_oc_tiny.o
obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o
obj-$(CONFIG_SPI_OMAP24XX) += omap2_mcspi.o obj-$(CONFIG_SPI_OMAP24XX) += omap2_mcspi.o
obj-$(CONFIG_SPI_OMAP_100K) += omap_spi_100k.o obj-$(CONFIG_SPI_OMAP_100K) += omap_spi_100k.o
...@@ -47,6 +49,7 @@ obj-$(CONFIG_SPI_TI_SSP) += ti-ssp-spi.o ...@@ -47,6 +49,7 @@ obj-$(CONFIG_SPI_TI_SSP) += ti-ssp-spi.o
obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi_topcliff_pch.o obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi_topcliff_pch.o
obj-$(CONFIG_SPI_TXX9) += spi_txx9.o obj-$(CONFIG_SPI_TXX9) += spi_txx9.o
obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o
obj-$(CONFIG_SPI_SH) += spi_sh.o
obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o
obj-$(CONFIG_SPI_SH_MSIOF) += spi_sh_msiof.o obj-$(CONFIG_SPI_SH_MSIOF) += spi_sh_msiof.o
obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o
......
...@@ -329,15 +329,16 @@ struct vendor_data { ...@@ -329,15 +329,16 @@ struct vendor_data {
/** /**
* struct pl022 - This is the private SSP driver data structure * struct pl022 - This is the private SSP driver data structure
* @adev: AMBA device model hookup * @adev: AMBA device model hookup
* @vendor: Vendor data for the IP block * @vendor: vendor data for the IP block
* @phybase: The physical memory where the SSP device resides * @phybase: the physical memory where the SSP device resides
* @virtbase: The virtual memory where the SSP is mapped * @virtbase: the virtual memory where the SSP is mapped
* @clk: outgoing clock "SPICLK" for the SPI bus
* @master: SPI framework hookup * @master: SPI framework hookup
* @master_info: controller-specific data from machine setup * @master_info: controller-specific data from machine setup
* @regs: SSP controller register's virtual address
* @pump_messages: Work struct for scheduling work to the workqueue
* @lock: spinlock to syncronise access to driver data
* @workqueue: a workqueue on which any spi_message request is queued * @workqueue: a workqueue on which any spi_message request is queued
* @pump_messages: work struct for scheduling work to the workqueue
* @queue_lock: spinlock to syncronise access to message queue
* @queue: message queue
* @busy: workqueue is busy * @busy: workqueue is busy
* @running: workqueue is running * @running: workqueue is running
* @pump_transfers: Tasklet used in Interrupt Transfer mode * @pump_transfers: Tasklet used in Interrupt Transfer mode
...@@ -348,8 +349,14 @@ struct vendor_data { ...@@ -348,8 +349,14 @@ struct vendor_data {
* @tx_end: end position in TX buffer to be read * @tx_end: end position in TX buffer to be read
* @rx: current position in RX buffer to be written * @rx: current position in RX buffer to be written
* @rx_end: end position in RX buffer to be written * @rx_end: end position in RX buffer to be written
* @readingtype: the type of read currently going on * @read: the type of read currently going on
* @writingtype: the type or write currently going on * @write: the type of write currently going on
* @exp_fifo_level: expected FIFO level
* @dma_rx_channel: optional channel for RX DMA
* @dma_tx_channel: optional channel for TX DMA
* @sgt_rx: scattertable for the RX transfer
* @sgt_tx: scattertable for the TX transfer
* @dummypage: a dummy page used for driving data on the bus with DMA
*/ */
struct pl022 { struct pl022 {
struct amba_device *adev; struct amba_device *adev;
...@@ -397,8 +404,8 @@ struct pl022 { ...@@ -397,8 +404,8 @@ struct pl022 {
* @cpsr: Value of Clock prescale register * @cpsr: Value of Clock prescale register
* @n_bytes: how many bytes(power of 2) reqd for a given data width of client * @n_bytes: how many bytes(power of 2) reqd for a given data width of client
* @enable_dma: Whether to enable DMA or not * @enable_dma: Whether to enable DMA or not
* @write: function ptr to be used to write when doing xfer for this chip
* @read: function ptr to be used to read when doing xfer for this chip * @read: function ptr to be used to read when doing xfer for this chip
* @write: function ptr to be used to write when doing xfer for this chip
* @cs_control: chip select callback provided by chip * @cs_control: chip select callback provided by chip
* @xfer_type: polling/interrupt/DMA * @xfer_type: polling/interrupt/DMA
* *
...@@ -508,9 +515,10 @@ static void giveback(struct pl022 *pl022) ...@@ -508,9 +515,10 @@ static void giveback(struct pl022 *pl022)
msg->state = NULL; msg->state = NULL;
if (msg->complete) if (msg->complete)
msg->complete(msg->context); msg->complete(msg->context);
/* This message is completed, so let's turn off the clocks! */ /* This message is completed, so let's turn off the clocks & power */
clk_disable(pl022->clk); clk_disable(pl022->clk);
amba_pclk_disable(pl022->adev); amba_pclk_disable(pl022->adev);
amba_vcore_disable(pl022->adev);
} }
/** /**
...@@ -917,7 +925,6 @@ static int configure_dma(struct pl022 *pl022) ...@@ -917,7 +925,6 @@ static int configure_dma(struct pl022 *pl022)
struct dma_chan *txchan = pl022->dma_tx_channel; struct dma_chan *txchan = pl022->dma_tx_channel;
struct dma_async_tx_descriptor *rxdesc; struct dma_async_tx_descriptor *rxdesc;
struct dma_async_tx_descriptor *txdesc; struct dma_async_tx_descriptor *txdesc;
dma_cookie_t cookie;
/* Check that the channels are available */ /* Check that the channels are available */
if (!rxchan || !txchan) if (!rxchan || !txchan)
...@@ -962,10 +969,8 @@ static int configure_dma(struct pl022 *pl022) ...@@ -962,10 +969,8 @@ static int configure_dma(struct pl022 *pl022)
tx_conf.dst_addr_width = rx_conf.src_addr_width; tx_conf.dst_addr_width = rx_conf.src_addr_width;
BUG_ON(rx_conf.src_addr_width != tx_conf.dst_addr_width); BUG_ON(rx_conf.src_addr_width != tx_conf.dst_addr_width);
rxchan->device->device_control(rxchan, DMA_SLAVE_CONFIG, dmaengine_slave_config(rxchan, &rx_conf);
(unsigned long) &rx_conf); dmaengine_slave_config(txchan, &tx_conf);
txchan->device->device_control(txchan, DMA_SLAVE_CONFIG,
(unsigned long) &tx_conf);
/* Create sglists for the transfers */ /* Create sglists for the transfers */
pages = (pl022->cur_transfer->len >> PAGE_SHIFT) + 1; pages = (pl022->cur_transfer->len >> PAGE_SHIFT) + 1;
...@@ -1018,23 +1023,17 @@ static int configure_dma(struct pl022 *pl022) ...@@ -1018,23 +1023,17 @@ static int configure_dma(struct pl022 *pl022)
rxdesc->callback_param = pl022; rxdesc->callback_param = pl022;
/* Submit and fire RX and TX with TX last so we're ready to read! */ /* Submit and fire RX and TX with TX last so we're ready to read! */
cookie = rxdesc->tx_submit(rxdesc); dmaengine_submit(rxdesc);
if (dma_submit_error(cookie)) dmaengine_submit(txdesc);
goto err_submit_rx; dma_async_issue_pending(rxchan);
cookie = txdesc->tx_submit(txdesc); dma_async_issue_pending(txchan);
if (dma_submit_error(cookie))
goto err_submit_tx;
rxchan->device->device_issue_pending(rxchan);
txchan->device->device_issue_pending(txchan);
return 0; return 0;
err_submit_tx:
err_submit_rx:
err_txdesc: err_txdesc:
txchan->device->device_control(txchan, DMA_TERMINATE_ALL, 0); dmaengine_terminate_all(txchan);
err_rxdesc: err_rxdesc:
rxchan->device->device_control(rxchan, DMA_TERMINATE_ALL, 0); dmaengine_terminate_all(rxchan);
dma_unmap_sg(txchan->device->dev, pl022->sgt_tx.sgl, dma_unmap_sg(txchan->device->dev, pl022->sgt_tx.sgl,
pl022->sgt_tx.nents, DMA_TO_DEVICE); pl022->sgt_tx.nents, DMA_TO_DEVICE);
err_tx_sgmap: err_tx_sgmap:
...@@ -1101,8 +1100,8 @@ static void terminate_dma(struct pl022 *pl022) ...@@ -1101,8 +1100,8 @@ static void terminate_dma(struct pl022 *pl022)
struct dma_chan *rxchan = pl022->dma_rx_channel; struct dma_chan *rxchan = pl022->dma_rx_channel;
struct dma_chan *txchan = pl022->dma_tx_channel; struct dma_chan *txchan = pl022->dma_tx_channel;
rxchan->device->device_control(rxchan, DMA_TERMINATE_ALL, 0); dmaengine_terminate_all(rxchan);
txchan->device->device_control(txchan, DMA_TERMINATE_ALL, 0); dmaengine_terminate_all(txchan);
unmap_free_dma_scatter(pl022); unmap_free_dma_scatter(pl022);
} }
...@@ -1482,9 +1481,11 @@ static void pump_messages(struct work_struct *work) ...@@ -1482,9 +1481,11 @@ static void pump_messages(struct work_struct *work)
/* Setup the SPI using the per chip configuration */ /* Setup the SPI using the per chip configuration */
pl022->cur_chip = spi_get_ctldata(pl022->cur_msg->spi); pl022->cur_chip = spi_get_ctldata(pl022->cur_msg->spi);
/* /*
* We enable the clocks here, then the clocks will be disabled when * We enable the core voltage and clocks here, then the clocks
* giveback() is called in each method (poll/interrupt/DMA) * and core will be disabled when giveback() is called in each method
* (poll/interrupt/DMA)
*/ */
amba_vcore_enable(pl022->adev);
amba_pclk_enable(pl022->adev); amba_pclk_enable(pl022->adev);
clk_enable(pl022->clk); clk_enable(pl022->clk);
restore_state(pl022); restore_state(pl022);
...@@ -1910,8 +1911,6 @@ static int pl022_setup(struct spi_device *spi) ...@@ -1910,8 +1911,6 @@ static int pl022_setup(struct spi_device *spi)
&& ((pl022->master_info)->enable_dma)) { && ((pl022->master_info)->enable_dma)) {
chip->enable_dma = true; chip->enable_dma = true;
dev_dbg(&spi->dev, "DMA mode set in controller state\n"); dev_dbg(&spi->dev, "DMA mode set in controller state\n");
if (status < 0)
goto err_config_params;
SSP_WRITE_BITS(chip->dmacr, SSP_DMA_ENABLED, SSP_WRITE_BITS(chip->dmacr, SSP_DMA_ENABLED,
SSP_DMACR_MASK_RXDMAE, 0); SSP_DMACR_MASK_RXDMAE, 0);
SSP_WRITE_BITS(chip->dmacr, SSP_DMA_ENABLED, SSP_WRITE_BITS(chip->dmacr, SSP_DMA_ENABLED,
...@@ -2130,8 +2129,12 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) ...@@ -2130,8 +2129,12 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
goto err_spi_register; goto err_spi_register;
} }
dev_dbg(dev, "probe succeded\n"); dev_dbg(dev, "probe succeded\n");
/* Disable the silicon block pclk and clock it when needed */ /*
* Disable the silicon block pclk and any voltage domain and just
* power it up and clock it when it's needed
*/
amba_pclk_disable(adev); amba_pclk_disable(adev);
amba_vcore_disable(adev);
return 0; return 0;
err_spi_register: err_spi_register:
...@@ -2196,9 +2199,11 @@ static int pl022_suspend(struct amba_device *adev, pm_message_t state) ...@@ -2196,9 +2199,11 @@ static int pl022_suspend(struct amba_device *adev, pm_message_t state)
return status; return status;
} }
amba_vcore_enable(adev);
amba_pclk_enable(adev); amba_pclk_enable(adev);
load_ssp_default_config(pl022); load_ssp_default_config(pl022);
amba_pclk_disable(adev); amba_pclk_disable(adev);
amba_vcore_disable(adev);
dev_dbg(&adev->dev, "suspended\n"); dev_dbg(&adev->dev, "suspended\n");
return 0; return 0;
} }
......
...@@ -571,6 +571,7 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) ...@@ -571,6 +571,7 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
unsigned long tx_reg, rx_reg; unsigned long tx_reg, rx_reg;
struct edmacc_param param; struct edmacc_param param;
void *rx_buf; void *rx_buf;
int b, c;
dma = &dspi->dma; dma = &dspi->dma;
...@@ -591,22 +592,38 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) ...@@ -591,22 +592,38 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
if (t->tx_buf) { if (t->tx_buf) {
t->tx_dma = dma_map_single(&spi->dev, (void *)t->tx_buf, t->tx_dma = dma_map_single(&spi->dev, (void *)t->tx_buf,
dspi->wcount, DMA_TO_DEVICE); t->len, DMA_TO_DEVICE);
if (dma_mapping_error(&spi->dev, t->tx_dma)) { if (dma_mapping_error(&spi->dev, t->tx_dma)) {
dev_dbg(sdev, "Unable to DMA map %d bytes" dev_dbg(sdev, "Unable to DMA map %d bytes"
"TX buffer\n", dspi->wcount); "TX buffer\n", t->len);
return -ENOMEM; return -ENOMEM;
} }
} }
/*
* If number of words is greater than 65535, then we need
* to configure a 3 dimension transfer. Use the BCNTRLD
* feature to allow for transfers that aren't even multiples
* of 65535 (or any other possible b size) by first transferring
* the remainder amount then grabbing the next N blocks of
* 65535 words.
*/
c = dspi->wcount / (SZ_64K - 1); /* N 65535 Blocks */
b = dspi->wcount - c * (SZ_64K - 1); /* Remainder */
if (b)
c++;
else
b = SZ_64K - 1;
param.opt = TCINTEN | EDMA_TCC(dma->tx_channel); param.opt = TCINTEN | EDMA_TCC(dma->tx_channel);
param.src = t->tx_buf ? t->tx_dma : tx_reg; param.src = t->tx_buf ? t->tx_dma : tx_reg;
param.a_b_cnt = dspi->wcount << 16 | data_type; param.a_b_cnt = b << 16 | data_type;
param.dst = tx_reg; param.dst = tx_reg;
param.src_dst_bidx = t->tx_buf ? data_type : 0; param.src_dst_bidx = t->tx_buf ? data_type : 0;
param.link_bcntrld = 0xffff; param.link_bcntrld = 0xffffffff;
param.src_dst_cidx = 0; param.src_dst_cidx = t->tx_buf ? data_type : 0;
param.ccnt = 1; param.ccnt = c;
edma_write_slot(dma->tx_channel, &param); edma_write_slot(dma->tx_channel, &param);
edma_link(dma->tx_channel, dma->dummy_param_slot); edma_link(dma->tx_channel, dma->dummy_param_slot);
...@@ -624,7 +641,7 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) ...@@ -624,7 +641,7 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
if (t->rx_buf) { if (t->rx_buf) {
rx_buf = t->rx_buf; rx_buf = t->rx_buf;
rx_buf_count = dspi->rcount; rx_buf_count = t->len;
} else { } else {
rx_buf = dspi->rx_tmp_buf; rx_buf = dspi->rx_tmp_buf;
rx_buf_count = sizeof(dspi->rx_tmp_buf); rx_buf_count = sizeof(dspi->rx_tmp_buf);
...@@ -636,19 +653,19 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) ...@@ -636,19 +653,19 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
dev_dbg(sdev, "Couldn't DMA map a %d bytes RX buffer\n", dev_dbg(sdev, "Couldn't DMA map a %d bytes RX buffer\n",
rx_buf_count); rx_buf_count);
if (t->tx_buf) if (t->tx_buf)
dma_unmap_single(NULL, t->tx_dma, dspi->wcount, dma_unmap_single(NULL, t->tx_dma, t->len,
DMA_TO_DEVICE); DMA_TO_DEVICE);
return -ENOMEM; return -ENOMEM;
} }
param.opt = TCINTEN | EDMA_TCC(dma->rx_channel); param.opt = TCINTEN | EDMA_TCC(dma->rx_channel);
param.src = rx_reg; param.src = rx_reg;
param.a_b_cnt = dspi->rcount << 16 | data_type; param.a_b_cnt = b << 16 | data_type;
param.dst = t->rx_dma; param.dst = t->rx_dma;
param.src_dst_bidx = (t->rx_buf ? data_type : 0) << 16; param.src_dst_bidx = (t->rx_buf ? data_type : 0) << 16;
param.link_bcntrld = 0xffff; param.link_bcntrld = 0xffffffff;
param.src_dst_cidx = 0; param.src_dst_cidx = (t->rx_buf ? data_type : 0) << 16;
param.ccnt = 1; param.ccnt = c;
edma_write_slot(dma->rx_channel, &param); edma_write_slot(dma->rx_channel, &param);
if (pdata->cshold_bug) if (pdata->cshold_bug)
...@@ -675,7 +692,7 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) ...@@ -675,7 +692,7 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
if (spicfg->io_type == SPI_IO_TYPE_DMA) { if (spicfg->io_type == SPI_IO_TYPE_DMA) {
if (t->tx_buf) if (t->tx_buf)
dma_unmap_single(NULL, t->tx_dma, dspi->wcount, dma_unmap_single(NULL, t->tx_dma, t->len,
DMA_TO_DEVICE); DMA_TO_DEVICE);
dma_unmap_single(NULL, t->rx_dma, rx_buf_count, dma_unmap_single(NULL, t->rx_dma, rx_buf_count,
......
...@@ -22,10 +22,10 @@ ...@@ -22,10 +22,10 @@
#include <linux/highmem.h> #include <linux/highmem.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/spi/dw_spi.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include "dw_spi.h"
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
#include <linux/debugfs.h> #include <linux/debugfs.h>
#endif #endif
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#define DW_SPI_HEADER_H #define DW_SPI_HEADER_H
#include <linux/io.h> #include <linux/io.h>
#include <linux/scatterlist.h>
/* Bit fields in CTRLR0 */ /* Bit fields in CTRLR0 */
#define SPI_DFS_OFFSET 0 #define SPI_DFS_OFFSET 0
......
...@@ -22,7 +22,8 @@ ...@@ -22,7 +22,8 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/spi/dw_spi.h>
#include "dw_spi.h"
#ifdef CONFIG_SPI_DW_MID_DMA #ifdef CONFIG_SPI_DW_MID_DMA
#include <linux/intel_mid_dma.h> #include <linux/intel_mid_dma.h>
......
...@@ -13,8 +13,10 @@ ...@@ -13,8 +13,10 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/spi/dw_spi.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/scatterlist.h>
#include "dw_spi.h"
#define DRIVER_NAME "dw_spi_mmio" #define DRIVER_NAME "dw_spi_mmio"
......
...@@ -20,9 +20,10 @@ ...@@ -20,9 +20,10 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/spi/dw_spi.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include "dw_spi.h"
#define DRIVER_NAME "dw_spi_pci" #define DRIVER_NAME "dw_spi_pci"
struct dw_spi_pci { struct dw_spi_pci {
......
...@@ -467,6 +467,9 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) ...@@ -467,6 +467,9 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
rx_reg = base + OMAP2_MCSPI_RX0; rx_reg = base + OMAP2_MCSPI_RX0;
chstat_reg = base + OMAP2_MCSPI_CHSTAT0; chstat_reg = base + OMAP2_MCSPI_CHSTAT0;
if (c < (word_len>>3))
return 0;
if (word_len <= 8) { if (word_len <= 8) {
u8 *rx; u8 *rx;
const u8 *tx; const u8 *tx;
...@@ -514,7 +517,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) ...@@ -514,7 +517,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
dev_vdbg(&spi->dev, "read-%d %02x\n", dev_vdbg(&spi->dev, "read-%d %02x\n",
word_len, *(rx - 1)); word_len, *(rx - 1));
} }
} while (c); } while (c > (word_len>>3));
} else if (word_len <= 16) { } else if (word_len <= 16) {
u16 *rx; u16 *rx;
const u16 *tx; const u16 *tx;
...@@ -561,7 +564,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) ...@@ -561,7 +564,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
dev_vdbg(&spi->dev, "read-%d %04x\n", dev_vdbg(&spi->dev, "read-%d %04x\n",
word_len, *(rx - 1)); word_len, *(rx - 1));
} }
} while (c); } while (c > (word_len>>3));
} else if (word_len <= 32) { } else if (word_len <= 32) {
u32 *rx; u32 *rx;
const u32 *tx; const u32 *tx;
...@@ -608,7 +611,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) ...@@ -608,7 +611,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
dev_vdbg(&spi->dev, "read-%d %08x\n", dev_vdbg(&spi->dev, "read-%d %08x\n",
word_len, *(rx - 1)); word_len, *(rx - 1));
} }
} while (c); } while (c > (word_len>>3));
} }
/* for TX_ONLY mode, be sure all words have shifted out */ /* for TX_ONLY mode, be sure all words have shifted out */
...@@ -631,6 +634,17 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) ...@@ -631,6 +634,17 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
return count - c; return count - c;
} }
static u32 omap2_mcspi_calc_divisor(u32 speed_hz)
{
u32 div;
for (div = 0; div < 15; div++)
if (speed_hz >= (OMAP2_MCSPI_MAX_FREQ >> div))
return div;
return 15;
}
/* called only when no transfer is active to this device */ /* called only when no transfer is active to this device */
static int omap2_mcspi_setup_transfer(struct spi_device *spi, static int omap2_mcspi_setup_transfer(struct spi_device *spi,
struct spi_transfer *t) struct spi_transfer *t)
...@@ -653,12 +667,8 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, ...@@ -653,12 +667,8 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
if (t && t->speed_hz) if (t && t->speed_hz)
speed_hz = t->speed_hz; speed_hz = t->speed_hz;
if (speed_hz) { speed_hz = min_t(u32, speed_hz, OMAP2_MCSPI_MAX_FREQ);
while (div <= 15 && (OMAP2_MCSPI_MAX_FREQ / (1 << div)) div = omap2_mcspi_calc_divisor(speed_hz);
> speed_hz)
div++;
} else
div = 15;
l = mcspi_cached_chconf0(spi); l = mcspi_cached_chconf0(spi);
...@@ -695,7 +705,7 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, ...@@ -695,7 +705,7 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
mcspi_write_chconf0(spi, l); mcspi_write_chconf0(spi, l);
dev_dbg(&spi->dev, "setup: speed %d, sample %s edge, clk %s\n", dev_dbg(&spi->dev, "setup: speed %d, sample %s edge, clk %s\n",
OMAP2_MCSPI_MAX_FREQ / (1 << div), OMAP2_MCSPI_MAX_FREQ >> div,
(spi->mode & SPI_CPHA) ? "trailing" : "leading", (spi->mode & SPI_CPHA) ? "trailing" : "leading",
(spi->mode & SPI_CPOL) ? "inverted" : "normal"); (spi->mode & SPI_CPOL) ? "inverted" : "normal");
...@@ -996,10 +1006,10 @@ static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m) ...@@ -996,10 +1006,10 @@ static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m)
t->bits_per_word); t->bits_per_word);
return -EINVAL; return -EINVAL;
} }
if (t->speed_hz && t->speed_hz < OMAP2_MCSPI_MAX_FREQ/(1<<16)) { if (t->speed_hz && t->speed_hz < (OMAP2_MCSPI_MAX_FREQ >> 15)) {
dev_dbg(&spi->dev, "%d Hz max exceeds %d\n", dev_dbg(&spi->dev, "speed_hz %d below minimum %d Hz\n",
t->speed_hz, t->speed_hz,
OMAP2_MCSPI_MAX_FREQ/(1<<16)); OMAP2_MCSPI_MAX_FREQ >> 15);
return -EINVAL; return -EINVAL;
} }
......
/*
* Altera SPI driver
*
* Copyright (C) 2008 Thomas Chou <thomas@wytron.com.tw>
*
* Based on spi_s3c24xx.c, which is:
* Copyright (c) 2006 Ben Dooks
* Copyright (c) 2006 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/io.h>
#include <linux/of.h>
#define DRV_NAME "spi_altera"
#define ALTERA_SPI_RXDATA 0
#define ALTERA_SPI_TXDATA 4
#define ALTERA_SPI_STATUS 8
#define ALTERA_SPI_CONTROL 12
#define ALTERA_SPI_SLAVE_SEL 20
#define ALTERA_SPI_STATUS_ROE_MSK 0x8
#define ALTERA_SPI_STATUS_TOE_MSK 0x10
#define ALTERA_SPI_STATUS_TMT_MSK 0x20
#define ALTERA_SPI_STATUS_TRDY_MSK 0x40
#define ALTERA_SPI_STATUS_RRDY_MSK 0x80
#define ALTERA_SPI_STATUS_E_MSK 0x100
#define ALTERA_SPI_CONTROL_IROE_MSK 0x8
#define ALTERA_SPI_CONTROL_ITOE_MSK 0x10
#define ALTERA_SPI_CONTROL_ITRDY_MSK 0x40
#define ALTERA_SPI_CONTROL_IRRDY_MSK 0x80
#define ALTERA_SPI_CONTROL_IE_MSK 0x100
#define ALTERA_SPI_CONTROL_SSO_MSK 0x400
struct altera_spi {
/* bitbang has to be first */
struct spi_bitbang bitbang;
struct completion done;
void __iomem *base;
int irq;
int len;
int count;
int bytes_per_word;
unsigned long imr;
/* data buffers */
const unsigned char *tx;
unsigned char *rx;
};
static inline struct altera_spi *altera_spi_to_hw(struct spi_device *sdev)
{
return spi_master_get_devdata(sdev->master);
}
static void altera_spi_chipsel(struct spi_device *spi, int value)
{
struct altera_spi *hw = altera_spi_to_hw(spi);
if (spi->mode & SPI_CS_HIGH) {
switch (value) {
case BITBANG_CS_INACTIVE:
writel(1 << spi->chip_select,
hw->base + ALTERA_SPI_SLAVE_SEL);
hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK;
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
break;
case BITBANG_CS_ACTIVE:
hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK;
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
writel(0, hw->base + ALTERA_SPI_SLAVE_SEL);
break;
}
} else {
switch (value) {
case BITBANG_CS_INACTIVE:
hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK;
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
break;
case BITBANG_CS_ACTIVE:
writel(1 << spi->chip_select,
hw->base + ALTERA_SPI_SLAVE_SEL);
hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK;
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
break;
}
}
}
static int altera_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t)
{
return 0;
}
static int altera_spi_setup(struct spi_device *spi)
{
return 0;
}
static inline unsigned int hw_txbyte(struct altera_spi *hw, int count)
{
if (hw->tx) {
switch (hw->bytes_per_word) {
case 1:
return hw->tx[count];
case 2:
return (hw->tx[count * 2]
| (hw->tx[count * 2 + 1] << 8));
}
}
return 0;
}
static int altera_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
{
struct altera_spi *hw = altera_spi_to_hw(spi);
hw->tx = t->tx_buf;
hw->rx = t->rx_buf;
hw->count = 0;
hw->bytes_per_word = (t->bits_per_word ? : spi->bits_per_word) / 8;
hw->len = t->len / hw->bytes_per_word;
if (hw->irq >= 0) {
/* enable receive interrupt */
hw->imr |= ALTERA_SPI_CONTROL_IRRDY_MSK;
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
/* send the first byte */
writel(hw_txbyte(hw, 0), hw->base + ALTERA_SPI_TXDATA);
wait_for_completion(&hw->done);
/* disable receive interrupt */
hw->imr &= ~ALTERA_SPI_CONTROL_IRRDY_MSK;
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
} else {
/* send the first byte */
writel(hw_txbyte(hw, 0), hw->base + ALTERA_SPI_TXDATA);
while (1) {
unsigned int rxd;
while (!(readl(hw->base + ALTERA_SPI_STATUS) &
ALTERA_SPI_STATUS_RRDY_MSK))
cpu_relax();
rxd = readl(hw->base + ALTERA_SPI_RXDATA);
if (hw->rx) {
switch (hw->bytes_per_word) {
case 1:
hw->rx[hw->count] = rxd;
break;
case 2:
hw->rx[hw->count * 2] = rxd;
hw->rx[hw->count * 2 + 1] = rxd >> 8;
break;
}
}
hw->count++;
if (hw->count < hw->len)
writel(hw_txbyte(hw, hw->count),
hw->base + ALTERA_SPI_TXDATA);
else
break;
}
}
return hw->count * hw->bytes_per_word;
}
static irqreturn_t altera_spi_irq(int irq, void *dev)
{
struct altera_spi *hw = dev;
unsigned int rxd;
rxd = readl(hw->base + ALTERA_SPI_RXDATA);
if (hw->rx) {
switch (hw->bytes_per_word) {
case 1:
hw->rx[hw->count] = rxd;
break;
case 2:
hw->rx[hw->count * 2] = rxd;
hw->rx[hw->count * 2 + 1] = rxd >> 8;
break;
}
}
hw->count++;
if (hw->count < hw->len)
writel(hw_txbyte(hw, hw->count), hw->base + ALTERA_SPI_TXDATA);
else
complete(&hw->done);
return IRQ_HANDLED;
}
static int __devinit altera_spi_probe(struct platform_device *pdev)
{
struct altera_spi_platform_data *platp = pdev->dev.platform_data;
struct altera_spi *hw;
struct spi_master *master;
struct resource *res;
int err = -ENODEV;
master = spi_alloc_master(&pdev->dev, sizeof(struct altera_spi));
if (!master)
return err;
/* setup the master state. */
master->bus_num = pdev->id;
master->num_chipselect = 16;
master->mode_bits = SPI_CS_HIGH;
master->setup = altera_spi_setup;
hw = spi_master_get_devdata(master);
platform_set_drvdata(pdev, hw);
/* setup the state for the bitbang driver */
hw->bitbang.master = spi_master_get(master);
if (!hw->bitbang.master)
return err;
hw->bitbang.setup_transfer = altera_spi_setupxfer;
hw->bitbang.chipselect = altera_spi_chipsel;
hw->bitbang.txrx_bufs = altera_spi_txrx;
/* find and map our resources */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
goto exit_busy;
if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
pdev->name))
goto exit_busy;
hw->base = devm_ioremap_nocache(&pdev->dev, res->start,
resource_size(res));
if (!hw->base)
goto exit_busy;
/* program defaults into the registers */
hw->imr = 0; /* disable spi interrupts */
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
writel(0, hw->base + ALTERA_SPI_STATUS); /* clear status reg */
if (readl(hw->base + ALTERA_SPI_STATUS) & ALTERA_SPI_STATUS_RRDY_MSK)
readl(hw->base + ALTERA_SPI_RXDATA); /* flush rxdata */
/* irq is optional */
hw->irq = platform_get_irq(pdev, 0);
if (hw->irq >= 0) {
init_completion(&hw->done);
err = devm_request_irq(&pdev->dev, hw->irq, altera_spi_irq, 0,
pdev->name, hw);
if (err)
goto exit;
}
/* find platform data */
if (!platp)
hw->bitbang.master->dev.of_node = pdev->dev.of_node;
/* register our spi controller */
err = spi_bitbang_start(&hw->bitbang);
if (err)
goto exit;
dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq);
return 0;
exit_busy:
err = -EBUSY;
exit:
platform_set_drvdata(pdev, NULL);
spi_master_put(master);
return err;
}
static int __devexit altera_spi_remove(struct platform_device *dev)
{
struct altera_spi *hw = platform_get_drvdata(dev);
struct spi_master *master = hw->bitbang.master;
spi_bitbang_stop(&hw->bitbang);
platform_set_drvdata(dev, NULL);
spi_master_put(master);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id altera_spi_match[] = {
{ .compatible = "ALTR,spi-1.0", },
{},
};
MODULE_DEVICE_TABLE(of, altera_spi_match);
#else /* CONFIG_OF */
#define altera_spi_match NULL
#endif /* CONFIG_OF */
static struct platform_driver altera_spi_driver = {
.probe = altera_spi_probe,
.remove = __devexit_p(altera_spi_remove),
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.pm = NULL,
.of_match_table = altera_spi_match,
},
};
static int __init altera_spi_init(void)
{
return platform_driver_register(&altera_spi_driver);
}
module_init(altera_spi_init);
static void __exit altera_spi_exit(void)
{
platform_driver_unregister(&altera_spi_driver);
}
module_exit(altera_spi_exit);
MODULE_DESCRIPTION("Altera SPI driver");
MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);
...@@ -425,6 +425,7 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id) ...@@ -425,6 +425,7 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id)
struct bfin_spi_slave_data *chip = drv_data->cur_chip; struct bfin_spi_slave_data *chip = drv_data->cur_chip;
struct spi_message *msg = drv_data->cur_msg; struct spi_message *msg = drv_data->cur_msg;
int n_bytes = drv_data->n_bytes; int n_bytes = drv_data->n_bytes;
int loop = 0;
/* wait until transfer finished. */ /* wait until transfer finished. */
while (!(read_STAT(drv_data) & BIT_STAT_RXS)) while (!(read_STAT(drv_data) & BIT_STAT_RXS))
...@@ -435,10 +436,15 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id) ...@@ -435,10 +436,15 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id)
/* last read */ /* last read */
if (drv_data->rx) { if (drv_data->rx) {
dev_dbg(&drv_data->pdev->dev, "last read\n"); dev_dbg(&drv_data->pdev->dev, "last read\n");
if (n_bytes == 2) if (n_bytes % 2) {
*(u16 *) (drv_data->rx) = read_RDBR(drv_data); u16 *buf = (u16 *)drv_data->rx;
else if (n_bytes == 1) for (loop = 0; loop < n_bytes / 2; loop++)
*(u8 *) (drv_data->rx) = read_RDBR(drv_data); *buf++ = read_RDBR(drv_data);
} else {
u8 *buf = (u8 *)drv_data->rx;
for (loop = 0; loop < n_bytes; loop++)
*buf++ = read_RDBR(drv_data);
}
drv_data->rx += n_bytes; drv_data->rx += n_bytes;
} }
...@@ -458,29 +464,53 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id) ...@@ -458,29 +464,53 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id)
if (drv_data->rx && drv_data->tx) { if (drv_data->rx && drv_data->tx) {
/* duplex */ /* duplex */
dev_dbg(&drv_data->pdev->dev, "duplex: write_TDBR\n"); dev_dbg(&drv_data->pdev->dev, "duplex: write_TDBR\n");
if (drv_data->n_bytes == 2) { if (n_bytes % 2) {
*(u16 *) (drv_data->rx) = read_RDBR(drv_data); u16 *buf = (u16 *)drv_data->rx;
write_TDBR(drv_data, (*(u16 *) (drv_data->tx))); u16 *buf2 = (u16 *)drv_data->tx;
} else if (drv_data->n_bytes == 1) { for (loop = 0; loop < n_bytes / 2; loop++) {
*(u8 *) (drv_data->rx) = read_RDBR(drv_data); *buf++ = read_RDBR(drv_data);
write_TDBR(drv_data, (*(u8 *) (drv_data->tx))); write_TDBR(drv_data, *buf2++);
}
} else {
u8 *buf = (u8 *)drv_data->rx;
u8 *buf2 = (u8 *)drv_data->tx;
for (loop = 0; loop < n_bytes; loop++) {
*buf++ = read_RDBR(drv_data);
write_TDBR(drv_data, *buf2++);
}
} }
} else if (drv_data->rx) { } else if (drv_data->rx) {
/* read */ /* read */
dev_dbg(&drv_data->pdev->dev, "read: write_TDBR\n"); dev_dbg(&drv_data->pdev->dev, "read: write_TDBR\n");
if (drv_data->n_bytes == 2) if (n_bytes % 2) {
*(u16 *) (drv_data->rx) = read_RDBR(drv_data); u16 *buf = (u16 *)drv_data->rx;
else if (drv_data->n_bytes == 1) for (loop = 0; loop < n_bytes / 2; loop++) {
*(u8 *) (drv_data->rx) = read_RDBR(drv_data); *buf++ = read_RDBR(drv_data);
write_TDBR(drv_data, chip->idle_tx_val); write_TDBR(drv_data, chip->idle_tx_val);
}
} else {
u8 *buf = (u8 *)drv_data->rx;
for (loop = 0; loop < n_bytes; loop++) {
*buf++ = read_RDBR(drv_data);
write_TDBR(drv_data, chip->idle_tx_val);
}
}
} else if (drv_data->tx) { } else if (drv_data->tx) {
/* write */ /* write */
dev_dbg(&drv_data->pdev->dev, "write: write_TDBR\n"); dev_dbg(&drv_data->pdev->dev, "write: write_TDBR\n");
bfin_spi_dummy_read(drv_data); if (n_bytes % 2) {
if (drv_data->n_bytes == 2) u16 *buf = (u16 *)drv_data->tx;
write_TDBR(drv_data, (*(u16 *) (drv_data->tx))); for (loop = 0; loop < n_bytes / 2; loop++) {
else if (drv_data->n_bytes == 1) read_RDBR(drv_data);
write_TDBR(drv_data, (*(u8 *) (drv_data->tx))); write_TDBR(drv_data, *buf++);
}
} else {
u8 *buf = (u8 *)drv_data->tx;
for (loop = 0; loop < n_bytes; loop++) {
read_RDBR(drv_data);
write_TDBR(drv_data, *buf++);
}
}
} }
if (drv_data->tx) if (drv_data->tx)
...@@ -623,6 +653,7 @@ static void bfin_spi_pump_transfers(unsigned long data) ...@@ -623,6 +653,7 @@ static void bfin_spi_pump_transfers(unsigned long data)
message->state = bfin_spi_next_transfer(drv_data); message->state = bfin_spi_next_transfer(drv_data);
/* Schedule next transfer tasklet */ /* Schedule next transfer tasklet */
tasklet_schedule(&drv_data->pump_transfers); tasklet_schedule(&drv_data->pump_transfers);
return;
} }
if (transfer->tx_buf != NULL) { if (transfer->tx_buf != NULL) {
...@@ -651,16 +682,16 @@ static void bfin_spi_pump_transfers(unsigned long data) ...@@ -651,16 +682,16 @@ static void bfin_spi_pump_transfers(unsigned long data)
/* Bits per word setup */ /* Bits per word setup */
bits_per_word = transfer->bits_per_word ? : message->spi->bits_per_word; bits_per_word = transfer->bits_per_word ? : message->spi->bits_per_word;
if (bits_per_word == 8) { if ((bits_per_word > 0) && (bits_per_word % 16 == 0)) {
drv_data->n_bytes = 1; drv_data->n_bytes = bits_per_word/8;
drv_data->len = transfer->len;
cr_width = 0;
drv_data->ops = &bfin_bfin_spi_transfer_ops_u8;
} else if (bits_per_word == 16) {
drv_data->n_bytes = 2;
drv_data->len = (transfer->len) >> 1; drv_data->len = (transfer->len) >> 1;
cr_width = BIT_CTL_WORDSIZE; cr_width = BIT_CTL_WORDSIZE;
drv_data->ops = &bfin_bfin_spi_transfer_ops_u16; drv_data->ops = &bfin_bfin_spi_transfer_ops_u16;
} else if ((bits_per_word > 0) && (bits_per_word % 8 == 0)) {
drv_data->n_bytes = bits_per_word/8;
drv_data->len = transfer->len;
cr_width = 0;
drv_data->ops = &bfin_bfin_spi_transfer_ops_u8;
} else { } else {
dev_err(&drv_data->pdev->dev, "transfer: unsupported bits_per_word\n"); dev_err(&drv_data->pdev->dev, "transfer: unsupported bits_per_word\n");
message->status = -EINVAL; message->status = -EINVAL;
...@@ -815,10 +846,19 @@ static void bfin_spi_pump_transfers(unsigned long data) ...@@ -815,10 +846,19 @@ static void bfin_spi_pump_transfers(unsigned long data)
if (drv_data->tx == NULL) if (drv_data->tx == NULL)
write_TDBR(drv_data, chip->idle_tx_val); write_TDBR(drv_data, chip->idle_tx_val);
else { else {
if (bits_per_word == 8) int loop;
write_TDBR(drv_data, (*(u8 *) (drv_data->tx))); if (bits_per_word % 16 == 0) {
else u16 *buf = (u16 *)drv_data->tx;
write_TDBR(drv_data, (*(u16 *) (drv_data->tx))); for (loop = 0; loop < bits_per_word / 16;
loop++) {
write_TDBR(drv_data, *buf++);
}
} else if (bits_per_word % 8 == 0) {
u8 *buf = (u8 *)drv_data->tx;
for (loop = 0; loop < bits_per_word / 8; loop++)
write_TDBR(drv_data, *buf++);
}
drv_data->tx += drv_data->n_bytes; drv_data->tx += drv_data->n_bytes;
} }
...@@ -1031,7 +1071,7 @@ static int bfin_spi_setup(struct spi_device *spi) ...@@ -1031,7 +1071,7 @@ static int bfin_spi_setup(struct spi_device *spi)
chip->ctl_reg &= bfin_ctl_reg; chip->ctl_reg &= bfin_ctl_reg;
} }
if (spi->bits_per_word != 8 && spi->bits_per_word != 16) { if (spi->bits_per_word % 8) {
dev_err(&spi->dev, "%d bits_per_word is not supported\n", dev_err(&spi->dev, "%d bits_per_word is not supported\n",
spi->bits_per_word); spi->bits_per_word);
goto error; goto error;
......
...@@ -259,10 +259,6 @@ static void bitbang_work(struct work_struct *work) ...@@ -259,10 +259,6 @@ static void bitbang_work(struct work_struct *work)
struct spi_bitbang *bitbang = struct spi_bitbang *bitbang =
container_of(work, struct spi_bitbang, work); container_of(work, struct spi_bitbang, work);
unsigned long flags; unsigned long flags;
int (*setup_transfer)(struct spi_device *,
struct spi_transfer *);
setup_transfer = bitbang->setup_transfer;
spin_lock_irqsave(&bitbang->lock, flags); spin_lock_irqsave(&bitbang->lock, flags);
bitbang->busy = 1; bitbang->busy = 1;
...@@ -300,11 +296,7 @@ static void bitbang_work(struct work_struct *work) ...@@ -300,11 +296,7 @@ static void bitbang_work(struct work_struct *work)
/* init (-1) or override (1) transfer params */ /* init (-1) or override (1) transfer params */
if (do_setup != 0) { if (do_setup != 0) {
if (!setup_transfer) { status = bitbang->setup_transfer(spi, t);
status = -ENOPROTOOPT;
break;
}
status = setup_transfer(spi, t);
if (status < 0) if (status < 0)
break; break;
if (do_setup == -1) if (do_setup == -1)
...@@ -465,6 +457,9 @@ int spi_bitbang_start(struct spi_bitbang *bitbang) ...@@ -465,6 +457,9 @@ int spi_bitbang_start(struct spi_bitbang *bitbang)
} }
} else if (!bitbang->master->setup) } else if (!bitbang->master->setup)
return -EINVAL; return -EINVAL;
if (bitbang->master->transfer == spi_bitbang_transfer &&
!bitbang->setup_transfer)
return -EINVAL;
/* this task is the only thing to touch the SPI bits */ /* this task is the only thing to touch the SPI bits */
bitbang->busy = 0; bitbang->busy = 0;
......
...@@ -174,7 +174,7 @@ static unsigned int spi_imx_clkdiv_2(unsigned int fin, ...@@ -174,7 +174,7 @@ static unsigned int spi_imx_clkdiv_2(unsigned int fin,
#define SPI_IMX2_3_CTRL 0x08 #define SPI_IMX2_3_CTRL 0x08
#define SPI_IMX2_3_CTRL_ENABLE (1 << 0) #define SPI_IMX2_3_CTRL_ENABLE (1 << 0)
#define SPI_IMX2_3_CTRL_XCH (1 << 2) #define SPI_IMX2_3_CTRL_XCH (1 << 2)
#define SPI_IMX2_3_CTRL_MODE(cs) (1 << ((cs) + 4)) #define SPI_IMX2_3_CTRL_MODE_MASK (0xf << 4)
#define SPI_IMX2_3_CTRL_POSTDIV_OFFSET 8 #define SPI_IMX2_3_CTRL_POSTDIV_OFFSET 8
#define SPI_IMX2_3_CTRL_PREDIV_OFFSET 12 #define SPI_IMX2_3_CTRL_PREDIV_OFFSET 12
#define SPI_IMX2_3_CTRL_CS(cs) ((cs) << 18) #define SPI_IMX2_3_CTRL_CS(cs) ((cs) << 18)
...@@ -253,8 +253,14 @@ static int __maybe_unused spi_imx2_3_config(struct spi_imx_data *spi_imx, ...@@ -253,8 +253,14 @@ static int __maybe_unused spi_imx2_3_config(struct spi_imx_data *spi_imx,
{ {
u32 ctrl = SPI_IMX2_3_CTRL_ENABLE, cfg = 0; u32 ctrl = SPI_IMX2_3_CTRL_ENABLE, cfg = 0;
/* set master mode */ /*
ctrl |= SPI_IMX2_3_CTRL_MODE(config->cs); * The hardware seems to have a race condition when changing modes. The
* current assumption is that the selection of the channel arrives
* earlier in the hardware than the mode bits when they are written at
* the same time.
* So set master mode for all channels as we do not support slave mode.
*/
ctrl |= SPI_IMX2_3_CTRL_MODE_MASK;
/* set clock speed */ /* set clock speed */
ctrl |= spi_imx2_3_clkdiv(spi_imx->spi_clk, config->speed_hz); ctrl |= spi_imx2_3_clkdiv(spi_imx->spi_clk, config->speed_hz);
......
/*
* OpenCores tiny SPI master driver
*
* http://opencores.org/project,tiny_spi
*
* Copyright (C) 2011 Thomas Chou <thomas@wytron.com.tw>
*
* Based on spi_s3c24xx.c, which is:
* Copyright (c) 2006 Ben Dooks
* Copyright (c) 2006 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/spi/spi_oc_tiny.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/of.h>
#define DRV_NAME "spi_oc_tiny"
#define TINY_SPI_RXDATA 0
#define TINY_SPI_TXDATA 4
#define TINY_SPI_STATUS 8
#define TINY_SPI_CONTROL 12
#define TINY_SPI_BAUD 16
#define TINY_SPI_STATUS_TXE 0x1
#define TINY_SPI_STATUS_TXR 0x2
struct tiny_spi {
/* bitbang has to be first */
struct spi_bitbang bitbang;
struct completion done;
void __iomem *base;
int irq;
unsigned int freq;
unsigned int baudwidth;
unsigned int baud;
unsigned int speed_hz;
unsigned int mode;
unsigned int len;
unsigned int txc, rxc;
const u8 *txp;
u8 *rxp;
unsigned int gpio_cs_count;
int *gpio_cs;
};
static inline struct tiny_spi *tiny_spi_to_hw(struct spi_device *sdev)
{
return spi_master_get_devdata(sdev->master);
}
static unsigned int tiny_spi_baud(struct spi_device *spi, unsigned int hz)
{
struct tiny_spi *hw = tiny_spi_to_hw(spi);
return min(DIV_ROUND_UP(hw->freq, hz * 2), (1U << hw->baudwidth)) - 1;
}
static void tiny_spi_chipselect(struct spi_device *spi, int is_active)
{
struct tiny_spi *hw = tiny_spi_to_hw(spi);
if (hw->gpio_cs_count) {
gpio_set_value(hw->gpio_cs[spi->chip_select],
(spi->mode & SPI_CS_HIGH) ? is_active : !is_active);
}
}
static int tiny_spi_setup_transfer(struct spi_device *spi,
struct spi_transfer *t)
{
struct tiny_spi *hw = tiny_spi_to_hw(spi);
unsigned int baud = hw->baud;
if (t) {
if (t->speed_hz && t->speed_hz != hw->speed_hz)
baud = tiny_spi_baud(spi, t->speed_hz);
}
writel(baud, hw->base + TINY_SPI_BAUD);
writel(hw->mode, hw->base + TINY_SPI_CONTROL);
return 0;
}
static int tiny_spi_setup(struct spi_device *spi)
{
struct tiny_spi *hw = tiny_spi_to_hw(spi);
if (spi->max_speed_hz != hw->speed_hz) {
hw->speed_hz = spi->max_speed_hz;
hw->baud = tiny_spi_baud(spi, hw->speed_hz);
}
hw->mode = spi->mode & (SPI_CPOL | SPI_CPHA);
return 0;
}
static inline void tiny_spi_wait_txr(struct tiny_spi *hw)
{
while (!(readb(hw->base + TINY_SPI_STATUS) &
TINY_SPI_STATUS_TXR))
cpu_relax();
}
static inline void tiny_spi_wait_txe(struct tiny_spi *hw)
{
while (!(readb(hw->base + TINY_SPI_STATUS) &
TINY_SPI_STATUS_TXE))
cpu_relax();
}
static int tiny_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
{
struct tiny_spi *hw = tiny_spi_to_hw(spi);
const u8 *txp = t->tx_buf;
u8 *rxp = t->rx_buf;
unsigned int i;
if (hw->irq >= 0) {
/* use intrrupt driven data transfer */
hw->len = t->len;
hw->txp = t->tx_buf;
hw->rxp = t->rx_buf;
hw->txc = 0;
hw->rxc = 0;
/* send the first byte */
if (t->len > 1) {
writeb(hw->txp ? *hw->txp++ : 0,
hw->base + TINY_SPI_TXDATA);
hw->txc++;
writeb(hw->txp ? *hw->txp++ : 0,
hw->base + TINY_SPI_TXDATA);
hw->txc++;
writeb(TINY_SPI_STATUS_TXR, hw->base + TINY_SPI_STATUS);
} else {
writeb(hw->txp ? *hw->txp++ : 0,
hw->base + TINY_SPI_TXDATA);
hw->txc++;
writeb(TINY_SPI_STATUS_TXE, hw->base + TINY_SPI_STATUS);
}
wait_for_completion(&hw->done);
} else if (txp && rxp) {
/* we need to tighten the transfer loop */
writeb(*txp++, hw->base + TINY_SPI_TXDATA);
if (t->len > 1) {
writeb(*txp++, hw->base + TINY_SPI_TXDATA);
for (i = 2; i < t->len; i++) {
u8 rx, tx = *txp++;
tiny_spi_wait_txr(hw);
rx = readb(hw->base + TINY_SPI_TXDATA);
writeb(tx, hw->base + TINY_SPI_TXDATA);
*rxp++ = rx;
}
tiny_spi_wait_txr(hw);
*rxp++ = readb(hw->base + TINY_SPI_TXDATA);
}
tiny_spi_wait_txe(hw);
*rxp++ = readb(hw->base + TINY_SPI_RXDATA);
} else if (rxp) {
writeb(0, hw->base + TINY_SPI_TXDATA);
if (t->len > 1) {
writeb(0,
hw->base + TINY_SPI_TXDATA);
for (i = 2; i < t->len; i++) {
u8 rx;
tiny_spi_wait_txr(hw);
rx = readb(hw->base + TINY_SPI_TXDATA);
writeb(0, hw->base + TINY_SPI_TXDATA);
*rxp++ = rx;
}
tiny_spi_wait_txr(hw);
*rxp++ = readb(hw->base + TINY_SPI_TXDATA);
}
tiny_spi_wait_txe(hw);
*rxp++ = readb(hw->base + TINY_SPI_RXDATA);
} else if (txp) {
writeb(*txp++, hw->base + TINY_SPI_TXDATA);
if (t->len > 1) {
writeb(*txp++, hw->base + TINY_SPI_TXDATA);
for (i = 2; i < t->len; i++) {
u8 tx = *txp++;
tiny_spi_wait_txr(hw);
writeb(tx, hw->base + TINY_SPI_TXDATA);
}
}
tiny_spi_wait_txe(hw);
} else {
writeb(0, hw->base + TINY_SPI_TXDATA);
if (t->len > 1) {
writeb(0, hw->base + TINY_SPI_TXDATA);
for (i = 2; i < t->len; i++) {
tiny_spi_wait_txr(hw);
writeb(0, hw->base + TINY_SPI_TXDATA);
}
}
tiny_spi_wait_txe(hw);
}
return t->len;
}
static irqreturn_t tiny_spi_irq(int irq, void *dev)
{
struct tiny_spi *hw = dev;
writeb(0, hw->base + TINY_SPI_STATUS);
if (hw->rxc + 1 == hw->len) {
if (hw->rxp)
*hw->rxp++ = readb(hw->base + TINY_SPI_RXDATA);
hw->rxc++;
complete(&hw->done);
} else {
if (hw->rxp)
*hw->rxp++ = readb(hw->base + TINY_SPI_TXDATA);
hw->rxc++;
if (hw->txc < hw->len) {
writeb(hw->txp ? *hw->txp++ : 0,
hw->base + TINY_SPI_TXDATA);
hw->txc++;
writeb(TINY_SPI_STATUS_TXR,
hw->base + TINY_SPI_STATUS);
} else {
writeb(TINY_SPI_STATUS_TXE,
hw->base + TINY_SPI_STATUS);
}
}
return IRQ_HANDLED;
}
#ifdef CONFIG_OF
#include <linux/of_gpio.h>
static int __devinit tiny_spi_of_probe(struct platform_device *pdev)
{
struct tiny_spi *hw = platform_get_drvdata(pdev);
struct device_node *np = pdev->dev.of_node;
unsigned int i;
const __be32 *val;
int len;
if (!np)
return 0;
hw->gpio_cs_count = of_gpio_count(np);
if (hw->gpio_cs_count) {
hw->gpio_cs = devm_kzalloc(&pdev->dev,
hw->gpio_cs_count * sizeof(unsigned int),
GFP_KERNEL);
if (!hw->gpio_cs)
return -ENOMEM;
}
for (i = 0; i < hw->gpio_cs_count; i++) {
hw->gpio_cs[i] = of_get_gpio_flags(np, i, NULL);
if (hw->gpio_cs[i] < 0)
return -ENODEV;
}
hw->bitbang.master->dev.of_node = pdev->dev.of_node;
val = of_get_property(pdev->dev.of_node,
"clock-frequency", &len);
if (val && len >= sizeof(__be32))
hw->freq = be32_to_cpup(val);
val = of_get_property(pdev->dev.of_node, "baud-width", &len);
if (val && len >= sizeof(__be32))
hw->baudwidth = be32_to_cpup(val);
return 0;
}
#else /* !CONFIG_OF */
static int __devinit tiny_spi_of_probe(struct platform_device *pdev)
{
return 0;
}
#endif /* CONFIG_OF */
static int __devinit tiny_spi_probe(struct platform_device *pdev)
{
struct tiny_spi_platform_data *platp = pdev->dev.platform_data;
struct tiny_spi *hw;
struct spi_master *master;
struct resource *res;
unsigned int i;
int err = -ENODEV;
master = spi_alloc_master(&pdev->dev, sizeof(struct tiny_spi));
if (!master)
return err;
/* setup the master state. */
master->bus_num = pdev->id;
master->num_chipselect = 255;
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
master->setup = tiny_spi_setup;
hw = spi_master_get_devdata(master);
platform_set_drvdata(pdev, hw);
/* setup the state for the bitbang driver */
hw->bitbang.master = spi_master_get(master);
if (!hw->bitbang.master)
return err;
hw->bitbang.setup_transfer = tiny_spi_setup_transfer;
hw->bitbang.chipselect = tiny_spi_chipselect;
hw->bitbang.txrx_bufs = tiny_spi_txrx_bufs;
/* find and map our resources */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
goto exit_busy;
if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
pdev->name))
goto exit_busy;
hw->base = devm_ioremap_nocache(&pdev->dev, res->start,
resource_size(res));
if (!hw->base)
goto exit_busy;
/* irq is optional */
hw->irq = platform_get_irq(pdev, 0);
if (hw->irq >= 0) {
init_completion(&hw->done);
err = devm_request_irq(&pdev->dev, hw->irq, tiny_spi_irq, 0,
pdev->name, hw);
if (err)
goto exit;
}
/* find platform data */
if (platp) {
hw->gpio_cs_count = platp->gpio_cs_count;
hw->gpio_cs = platp->gpio_cs;
if (platp->gpio_cs_count && !platp->gpio_cs)
goto exit_busy;
hw->freq = platp->freq;
hw->baudwidth = platp->baudwidth;
} else {
err = tiny_spi_of_probe(pdev);
if (err)
goto exit;
}
for (i = 0; i < hw->gpio_cs_count; i++) {
err = gpio_request(hw->gpio_cs[i], dev_name(&pdev->dev));
if (err)
goto exit_gpio;
gpio_direction_output(hw->gpio_cs[i], 1);
}
hw->bitbang.master->num_chipselect = max(1U, hw->gpio_cs_count);
/* register our spi controller */
err = spi_bitbang_start(&hw->bitbang);
if (err)
goto exit;
dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq);
return 0;
exit_gpio:
while (i-- > 0)
gpio_free(hw->gpio_cs[i]);
exit_busy:
err = -EBUSY;
exit:
platform_set_drvdata(pdev, NULL);
spi_master_put(master);
return err;
}
static int __devexit tiny_spi_remove(struct platform_device *pdev)
{
struct tiny_spi *hw = platform_get_drvdata(pdev);
struct spi_master *master = hw->bitbang.master;
unsigned int i;
spi_bitbang_stop(&hw->bitbang);
for (i = 0; i < hw->gpio_cs_count; i++)
gpio_free(hw->gpio_cs[i]);
platform_set_drvdata(pdev, NULL);
spi_master_put(master);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id tiny_spi_match[] = {
{ .compatible = "opencores,tiny-spi-rtlsvn2", },
{},
};
MODULE_DEVICE_TABLE(of, tiny_spi_match);
#else /* CONFIG_OF */
#define tiny_spi_match NULL
#endif /* CONFIG_OF */
static struct platform_driver tiny_spi_driver = {
.probe = tiny_spi_probe,
.remove = __devexit_p(tiny_spi_remove),
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.pm = NULL,
.of_match_table = tiny_spi_match,
},
};
static int __init tiny_spi_init(void)
{
return platform_driver_register(&tiny_spi_driver);
}
module_init(tiny_spi_init);
static void __exit tiny_spi_exit(void)
{
platform_driver_unregister(&tiny_spi_driver);
}
module_exit(tiny_spi_exit);
MODULE_DESCRIPTION("OpenCores tiny SPI driver");
MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);
/*
* SH SPI bus driver
*
* Copyright (C) 2011 Renesas Solutions Corp.
*
* Based on pxa2xx_spi.c:
* Copyright (C) 2005 Stephen Street / StreetFire Sound Labs
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/list.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/spi/spi.h>
#define SPI_SH_TBR 0x00
#define SPI_SH_RBR 0x00
#define SPI_SH_CR1 0x08
#define SPI_SH_CR2 0x10
#define SPI_SH_CR3 0x18
#define SPI_SH_CR4 0x20
#define SPI_SH_CR5 0x28
/* CR1 */
#define SPI_SH_TBE 0x80
#define SPI_SH_TBF 0x40
#define SPI_SH_RBE 0x20
#define SPI_SH_RBF 0x10
#define SPI_SH_PFONRD 0x08
#define SPI_SH_SSDB 0x04
#define SPI_SH_SSD 0x02
#define SPI_SH_SSA 0x01
/* CR2 */
#define SPI_SH_RSTF 0x80
#define SPI_SH_LOOPBK 0x40
#define SPI_SH_CPOL 0x20
#define SPI_SH_CPHA 0x10
#define SPI_SH_L1M0 0x08
/* CR3 */
#define SPI_SH_MAX_BYTE 0xFF
/* CR4 */
#define SPI_SH_TBEI 0x80
#define SPI_SH_TBFI 0x40
#define SPI_SH_RBEI 0x20
#define SPI_SH_RBFI 0x10
#define SPI_SH_WPABRT 0x04
#define SPI_SH_SSS 0x01
/* CR8 */
#define SPI_SH_P1L0 0x80
#define SPI_SH_PP1L0 0x40
#define SPI_SH_MUXI 0x20
#define SPI_SH_MUXIRQ 0x10
#define SPI_SH_FIFO_SIZE 32
#define SPI_SH_SEND_TIMEOUT (3 * HZ)
#define SPI_SH_RECEIVE_TIMEOUT (HZ >> 3)
#undef DEBUG
struct spi_sh_data {
void __iomem *addr;
int irq;
struct spi_master *master;
struct list_head queue;
struct workqueue_struct *workqueue;
struct work_struct ws;
unsigned long cr1;
wait_queue_head_t wait;
spinlock_t lock;
};
static void spi_sh_write(struct spi_sh_data *ss, unsigned long data,
unsigned long offset)
{
writel(data, ss->addr + offset);
}
static unsigned long spi_sh_read(struct spi_sh_data *ss, unsigned long offset)
{
return readl(ss->addr + offset);
}
static void spi_sh_set_bit(struct spi_sh_data *ss, unsigned long val,
unsigned long offset)
{
unsigned long tmp;
tmp = spi_sh_read(ss, offset);
tmp |= val;
spi_sh_write(ss, tmp, offset);
}
static void spi_sh_clear_bit(struct spi_sh_data *ss, unsigned long val,
unsigned long offset)
{
unsigned long tmp;
tmp = spi_sh_read(ss, offset);
tmp &= ~val;
spi_sh_write(ss, tmp, offset);
}
static void clear_fifo(struct spi_sh_data *ss)
{
spi_sh_set_bit(ss, SPI_SH_RSTF, SPI_SH_CR2);
spi_sh_clear_bit(ss, SPI_SH_RSTF, SPI_SH_CR2);
}
static int spi_sh_wait_receive_buffer(struct spi_sh_data *ss)
{
int timeout = 100000;
while (spi_sh_read(ss, SPI_SH_CR1) & SPI_SH_RBE) {
udelay(10);
if (timeout-- < 0)
return -ETIMEDOUT;
}
return 0;
}
static int spi_sh_wait_write_buffer_empty(struct spi_sh_data *ss)
{
int timeout = 100000;
while (!(spi_sh_read(ss, SPI_SH_CR1) & SPI_SH_TBE)) {
udelay(10);
if (timeout-- < 0)
return -ETIMEDOUT;
}
return 0;
}
static int spi_sh_send(struct spi_sh_data *ss, struct spi_message *mesg,
struct spi_transfer *t)
{
int i, retval = 0;
int remain = t->len;
int cur_len;
unsigned char *data;
unsigned long tmp;
long ret;
if (t->len)
spi_sh_set_bit(ss, SPI_SH_SSA, SPI_SH_CR1);
data = (unsigned char *)t->tx_buf;
while (remain > 0) {
cur_len = min(SPI_SH_FIFO_SIZE, remain);
for (i = 0; i < cur_len &&
!(spi_sh_read(ss, SPI_SH_CR4) &
SPI_SH_WPABRT) &&
!(spi_sh_read(ss, SPI_SH_CR1) & SPI_SH_TBF);
i++)
spi_sh_write(ss, (unsigned long)data[i], SPI_SH_TBR);
if (spi_sh_read(ss, SPI_SH_CR4) & SPI_SH_WPABRT) {
/* Abort SPI operation */
spi_sh_set_bit(ss, SPI_SH_WPABRT, SPI_SH_CR4);
retval = -EIO;
break;
}
cur_len = i;
remain -= cur_len;
data += cur_len;
if (remain > 0) {
ss->cr1 &= ~SPI_SH_TBE;
spi_sh_set_bit(ss, SPI_SH_TBE, SPI_SH_CR4);
ret = wait_event_interruptible_timeout(ss->wait,
ss->cr1 & SPI_SH_TBE,
SPI_SH_SEND_TIMEOUT);
if (ret == 0 && !(ss->cr1 & SPI_SH_TBE)) {
printk(KERN_ERR "%s: timeout\n", __func__);
return -ETIMEDOUT;
}
}
}
if (list_is_last(&t->transfer_list, &mesg->transfers)) {
tmp = spi_sh_read(ss, SPI_SH_CR1);
tmp = tmp & ~(SPI_SH_SSD | SPI_SH_SSDB);
spi_sh_write(ss, tmp, SPI_SH_CR1);
spi_sh_set_bit(ss, SPI_SH_SSA, SPI_SH_CR1);
ss->cr1 &= ~SPI_SH_TBE;
spi_sh_set_bit(ss, SPI_SH_TBE, SPI_SH_CR4);
ret = wait_event_interruptible_timeout(ss->wait,
ss->cr1 & SPI_SH_TBE,
SPI_SH_SEND_TIMEOUT);
if (ret == 0 && (ss->cr1 & SPI_SH_TBE)) {
printk(KERN_ERR "%s: timeout\n", __func__);
return -ETIMEDOUT;
}
}
return retval;
}
static int spi_sh_receive(struct spi_sh_data *ss, struct spi_message *mesg,
struct spi_transfer *t)
{
int i;
int remain = t->len;
int cur_len;
unsigned char *data;
unsigned long tmp;
long ret;
if (t->len > SPI_SH_MAX_BYTE)
spi_sh_write(ss, SPI_SH_MAX_BYTE, SPI_SH_CR3);
else
spi_sh_write(ss, t->len, SPI_SH_CR3);
tmp = spi_sh_read(ss, SPI_SH_CR1);
tmp = tmp & ~(SPI_SH_SSD | SPI_SH_SSDB);
spi_sh_write(ss, tmp, SPI_SH_CR1);
spi_sh_set_bit(ss, SPI_SH_SSA, SPI_SH_CR1);
spi_sh_wait_write_buffer_empty(ss);
data = (unsigned char *)t->rx_buf;
while (remain > 0) {
if (remain >= SPI_SH_FIFO_SIZE) {
ss->cr1 &= ~SPI_SH_RBF;
spi_sh_set_bit(ss, SPI_SH_RBF, SPI_SH_CR4);
ret = wait_event_interruptible_timeout(ss->wait,
ss->cr1 & SPI_SH_RBF,
SPI_SH_RECEIVE_TIMEOUT);
if (ret == 0 &&
spi_sh_read(ss, SPI_SH_CR1) & SPI_SH_RBE) {
printk(KERN_ERR "%s: timeout\n", __func__);
return -ETIMEDOUT;
}
}
cur_len = min(SPI_SH_FIFO_SIZE, remain);
for (i = 0; i < cur_len; i++) {
if (spi_sh_wait_receive_buffer(ss))
break;
data[i] = (unsigned char)spi_sh_read(ss, SPI_SH_RBR);
}
remain -= cur_len;
data += cur_len;
}
/* deassert CS when SPI is receiving. */
if (t->len > SPI_SH_MAX_BYTE) {
clear_fifo(ss);
spi_sh_write(ss, 1, SPI_SH_CR3);
} else {
spi_sh_write(ss, 0, SPI_SH_CR3);
}
return 0;
}
static void spi_sh_work(struct work_struct *work)
{
struct spi_sh_data *ss = container_of(work, struct spi_sh_data, ws);
struct spi_message *mesg;
struct spi_transfer *t;
unsigned long flags;
int ret;
pr_debug("%s: enter\n", __func__);
spin_lock_irqsave(&ss->lock, flags);
while (!list_empty(&ss->queue)) {
mesg = list_entry(ss->queue.next, struct spi_message, queue);
list_del_init(&mesg->queue);
spin_unlock_irqrestore(&ss->lock, flags);
list_for_each_entry(t, &mesg->transfers, transfer_list) {
pr_debug("tx_buf = %p, rx_buf = %p\n",
t->tx_buf, t->rx_buf);
pr_debug("len = %d, delay_usecs = %d\n",
t->len, t->delay_usecs);
if (t->tx_buf) {
ret = spi_sh_send(ss, mesg, t);
if (ret < 0)
goto error;
}
if (t->rx_buf) {
ret = spi_sh_receive(ss, mesg, t);
if (ret < 0)
goto error;
}
mesg->actual_length += t->len;
}
spin_lock_irqsave(&ss->lock, flags);
mesg->status = 0;
mesg->complete(mesg->context);
}
clear_fifo(ss);
spi_sh_set_bit(ss, SPI_SH_SSD, SPI_SH_CR1);
udelay(100);
spi_sh_clear_bit(ss, SPI_SH_SSA | SPI_SH_SSDB | SPI_SH_SSD,
SPI_SH_CR1);
clear_fifo(ss);
spin_unlock_irqrestore(&ss->lock, flags);
return;
error:
mesg->status = ret;
mesg->complete(mesg->context);
spi_sh_clear_bit(ss, SPI_SH_SSA | SPI_SH_SSDB | SPI_SH_SSD,
SPI_SH_CR1);
clear_fifo(ss);
}
static int spi_sh_setup(struct spi_device *spi)
{
struct spi_sh_data *ss = spi_master_get_devdata(spi->master);
if (!spi->bits_per_word)
spi->bits_per_word = 8;
pr_debug("%s: enter\n", __func__);
spi_sh_write(ss, 0xfe, SPI_SH_CR1); /* SPI sycle stop */
spi_sh_write(ss, 0x00, SPI_SH_CR1); /* CR1 init */
spi_sh_write(ss, 0x00, SPI_SH_CR3); /* CR3 init */
clear_fifo(ss);
/* 1/8 clock */
spi_sh_write(ss, spi_sh_read(ss, SPI_SH_CR2) | 0x07, SPI_SH_CR2);
udelay(10);
return 0;
}
static int spi_sh_transfer(struct spi_device *spi, struct spi_message *mesg)
{
struct spi_sh_data *ss = spi_master_get_devdata(spi->master);
unsigned long flags;
pr_debug("%s: enter\n", __func__);
pr_debug("\tmode = %02x\n", spi->mode);
spin_lock_irqsave(&ss->lock, flags);
mesg->actual_length = 0;
mesg->status = -EINPROGRESS;
spi_sh_clear_bit(ss, SPI_SH_SSA, SPI_SH_CR1);
list_add_tail(&mesg->queue, &ss->queue);
queue_work(ss->workqueue, &ss->ws);
spin_unlock_irqrestore(&ss->lock, flags);
return 0;
}
static void spi_sh_cleanup(struct spi_device *spi)
{
struct spi_sh_data *ss = spi_master_get_devdata(spi->master);
pr_debug("%s: enter\n", __func__);
spi_sh_clear_bit(ss, SPI_SH_SSA | SPI_SH_SSDB | SPI_SH_SSD,
SPI_SH_CR1);
}
static irqreturn_t spi_sh_irq(int irq, void *_ss)
{
struct spi_sh_data *ss = (struct spi_sh_data *)_ss;
unsigned long cr1;
cr1 = spi_sh_read(ss, SPI_SH_CR1);
if (cr1 & SPI_SH_TBE)
ss->cr1 |= SPI_SH_TBE;
if (cr1 & SPI_SH_TBF)
ss->cr1 |= SPI_SH_TBF;
if (cr1 & SPI_SH_RBE)
ss->cr1 |= SPI_SH_RBE;
if (cr1 & SPI_SH_RBF)
ss->cr1 |= SPI_SH_RBF;
if (ss->cr1) {
spi_sh_clear_bit(ss, ss->cr1, SPI_SH_CR4);
wake_up(&ss->wait);
}
return IRQ_HANDLED;
}
static int __devexit spi_sh_remove(struct platform_device *pdev)
{
struct spi_sh_data *ss = dev_get_drvdata(&pdev->dev);
destroy_workqueue(ss->workqueue);
free_irq(ss->irq, ss);
iounmap(ss->addr);
spi_master_put(ss->master);
return 0;
}
static int __devinit spi_sh_probe(struct platform_device *pdev)
{
struct resource *res;
struct spi_master *master;
struct spi_sh_data *ss;
int ret, irq;
/* get base addr */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (unlikely(res == NULL)) {
dev_err(&pdev->dev, "invalid resource\n");
return -EINVAL;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "platform_get_irq error\n");
return -ENODEV;
}
master = spi_alloc_master(&pdev->dev, sizeof(struct spi_sh_data));
if (master == NULL) {
dev_err(&pdev->dev, "spi_alloc_master error.\n");
return -ENOMEM;
}
ss = spi_master_get_devdata(master);
dev_set_drvdata(&pdev->dev, ss);
ss->irq = irq;
ss->master = master;
ss->addr = ioremap(res->start, resource_size(res));
if (ss->addr == NULL) {
dev_err(&pdev->dev, "ioremap error.\n");
ret = -ENOMEM;
goto error1;
}
INIT_LIST_HEAD(&ss->queue);
spin_lock_init(&ss->lock);
INIT_WORK(&ss->ws, spi_sh_work);
init_waitqueue_head(&ss->wait);
ss->workqueue = create_singlethread_workqueue(
dev_name(master->dev.parent));
if (ss->workqueue == NULL) {
dev_err(&pdev->dev, "create workqueue error\n");
ret = -EBUSY;
goto error2;
}
ret = request_irq(irq, spi_sh_irq, IRQF_DISABLED, "spi_sh", ss);
if (ret < 0) {
dev_err(&pdev->dev, "request_irq error\n");
goto error3;
}
master->num_chipselect = 2;
master->bus_num = pdev->id;
master->setup = spi_sh_setup;
master->transfer = spi_sh_transfer;
master->cleanup = spi_sh_cleanup;
ret = spi_register_master(master);
if (ret < 0) {
printk(KERN_ERR "spi_register_master error.\n");
goto error4;
}
return 0;
error4:
free_irq(irq, ss);
error3:
destroy_workqueue(ss->workqueue);
error2:
iounmap(ss->addr);
error1:
spi_master_put(master);
return ret;
}
static struct platform_driver spi_sh_driver = {
.probe = spi_sh_probe,
.remove = __devexit_p(spi_sh_remove),
.driver = {
.name = "sh_spi",
.owner = THIS_MODULE,
},
};
static int __init spi_sh_init(void)
{
return platform_driver_register(&spi_sh_driver);
}
module_init(spi_sh_init);
static void __exit spi_sh_exit(void)
{
platform_driver_unregister(&spi_sh_driver);
}
module_exit(spi_sh_exit);
MODULE_DESCRIPTION("SH SPI bus driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yoshihiro Shimoda");
MODULE_ALIAS("platform:sh_spi");
...@@ -9,22 +9,22 @@ ...@@ -9,22 +9,22 @@
* *
*/ */
#include <linux/kernel.h> #include <linux/bitmap.h>
#include <linux/init.h> #include <linux/clk.h>
#include <linux/completion.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/completion.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/gpio.h>
#include <linux/bitmap.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/spi/sh_msiof.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h> #include <linux/spi/spi_bitbang.h>
#include <linux/spi/sh_msiof.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
...@@ -67,7 +67,7 @@ struct sh_msiof_spi_priv { ...@@ -67,7 +67,7 @@ struct sh_msiof_spi_priv {
#define STR_TEOF (1 << 23) #define STR_TEOF (1 << 23)
#define STR_REOF (1 << 7) #define STR_REOF (1 << 7)
static unsigned long sh_msiof_read(struct sh_msiof_spi_priv *p, int reg_offs) static u32 sh_msiof_read(struct sh_msiof_spi_priv *p, int reg_offs)
{ {
switch (reg_offs) { switch (reg_offs) {
case TSCR: case TSCR:
...@@ -79,7 +79,7 @@ static unsigned long sh_msiof_read(struct sh_msiof_spi_priv *p, int reg_offs) ...@@ -79,7 +79,7 @@ static unsigned long sh_msiof_read(struct sh_msiof_spi_priv *p, int reg_offs)
} }
static void sh_msiof_write(struct sh_msiof_spi_priv *p, int reg_offs, static void sh_msiof_write(struct sh_msiof_spi_priv *p, int reg_offs,
unsigned long value) u32 value)
{ {
switch (reg_offs) { switch (reg_offs) {
case TSCR: case TSCR:
...@@ -93,10 +93,10 @@ static void sh_msiof_write(struct sh_msiof_spi_priv *p, int reg_offs, ...@@ -93,10 +93,10 @@ static void sh_msiof_write(struct sh_msiof_spi_priv *p, int reg_offs,
} }
static int sh_msiof_modify_ctr_wait(struct sh_msiof_spi_priv *p, static int sh_msiof_modify_ctr_wait(struct sh_msiof_spi_priv *p,
unsigned long clr, unsigned long set) u32 clr, u32 set)
{ {
unsigned long mask = clr | set; u32 mask = clr | set;
unsigned long data; u32 data;
int k; int k;
data = sh_msiof_read(p, CTR); data = sh_msiof_read(p, CTR);
...@@ -166,10 +166,10 @@ static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p, ...@@ -166,10 +166,10 @@ static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p,
} }
static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p, static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p,
int cpol, int cpha, u32 cpol, u32 cpha,
int tx_hi_z, int lsb_first) u32 tx_hi_z, u32 lsb_first)
{ {
unsigned long tmp; u32 tmp;
int edge; int edge;
/* /*
...@@ -187,7 +187,7 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p, ...@@ -187,7 +187,7 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p,
tmp |= cpol << 30; /* TSCKIZ */ tmp |= cpol << 30; /* TSCKIZ */
tmp |= cpol << 28; /* RSCKIZ */ tmp |= cpol << 28; /* RSCKIZ */
edge = cpol ? cpha : !cpha; edge = cpol ^ !cpha;
tmp |= edge << 27; /* TEDG */ tmp |= edge << 27; /* TEDG */
tmp |= edge << 26; /* REDG */ tmp |= edge << 26; /* REDG */
...@@ -197,11 +197,9 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p, ...@@ -197,11 +197,9 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p,
static void sh_msiof_spi_set_mode_regs(struct sh_msiof_spi_priv *p, static void sh_msiof_spi_set_mode_regs(struct sh_msiof_spi_priv *p,
const void *tx_buf, void *rx_buf, const void *tx_buf, void *rx_buf,
int bits, int words) u32 bits, u32 words)
{ {
unsigned long dr2; u32 dr2 = ((bits - 1) << 24) | ((words - 1) << 16);
dr2 = ((bits - 1) << 24) | ((words - 1) << 16);
if (tx_buf) if (tx_buf)
sh_msiof_write(p, TMDR2, dr2); sh_msiof_write(p, TMDR2, dr2);
...@@ -222,7 +220,7 @@ static void sh_msiof_reset_str(struct sh_msiof_spi_priv *p) ...@@ -222,7 +220,7 @@ static void sh_msiof_reset_str(struct sh_msiof_spi_priv *p)
static void sh_msiof_spi_write_fifo_8(struct sh_msiof_spi_priv *p, static void sh_msiof_spi_write_fifo_8(struct sh_msiof_spi_priv *p,
const void *tx_buf, int words, int fs) const void *tx_buf, int words, int fs)
{ {
const unsigned char *buf_8 = tx_buf; const u8 *buf_8 = tx_buf;
int k; int k;
for (k = 0; k < words; k++) for (k = 0; k < words; k++)
...@@ -232,7 +230,7 @@ static void sh_msiof_spi_write_fifo_8(struct sh_msiof_spi_priv *p, ...@@ -232,7 +230,7 @@ static void sh_msiof_spi_write_fifo_8(struct sh_msiof_spi_priv *p,
static void sh_msiof_spi_write_fifo_16(struct sh_msiof_spi_priv *p, static void sh_msiof_spi_write_fifo_16(struct sh_msiof_spi_priv *p,
const void *tx_buf, int words, int fs) const void *tx_buf, int words, int fs)
{ {
const unsigned short *buf_16 = tx_buf; const u16 *buf_16 = tx_buf;
int k; int k;
for (k = 0; k < words; k++) for (k = 0; k < words; k++)
...@@ -242,7 +240,7 @@ static void sh_msiof_spi_write_fifo_16(struct sh_msiof_spi_priv *p, ...@@ -242,7 +240,7 @@ static void sh_msiof_spi_write_fifo_16(struct sh_msiof_spi_priv *p,
static void sh_msiof_spi_write_fifo_16u(struct sh_msiof_spi_priv *p, static void sh_msiof_spi_write_fifo_16u(struct sh_msiof_spi_priv *p,
const void *tx_buf, int words, int fs) const void *tx_buf, int words, int fs)
{ {
const unsigned short *buf_16 = tx_buf; const u16 *buf_16 = tx_buf;
int k; int k;
for (k = 0; k < words; k++) for (k = 0; k < words; k++)
...@@ -252,7 +250,7 @@ static void sh_msiof_spi_write_fifo_16u(struct sh_msiof_spi_priv *p, ...@@ -252,7 +250,7 @@ static void sh_msiof_spi_write_fifo_16u(struct sh_msiof_spi_priv *p,
static void sh_msiof_spi_write_fifo_32(struct sh_msiof_spi_priv *p, static void sh_msiof_spi_write_fifo_32(struct sh_msiof_spi_priv *p,
const void *tx_buf, int words, int fs) const void *tx_buf, int words, int fs)
{ {
const unsigned int *buf_32 = tx_buf; const u32 *buf_32 = tx_buf;
int k; int k;
for (k = 0; k < words; k++) for (k = 0; k < words; k++)
...@@ -262,17 +260,37 @@ static void sh_msiof_spi_write_fifo_32(struct sh_msiof_spi_priv *p, ...@@ -262,17 +260,37 @@ static void sh_msiof_spi_write_fifo_32(struct sh_msiof_spi_priv *p,
static void sh_msiof_spi_write_fifo_32u(struct sh_msiof_spi_priv *p, static void sh_msiof_spi_write_fifo_32u(struct sh_msiof_spi_priv *p,
const void *tx_buf, int words, int fs) const void *tx_buf, int words, int fs)
{ {
const unsigned int *buf_32 = tx_buf; const u32 *buf_32 = tx_buf;
int k; int k;
for (k = 0; k < words; k++) for (k = 0; k < words; k++)
sh_msiof_write(p, TFDR, get_unaligned(&buf_32[k]) << fs); sh_msiof_write(p, TFDR, get_unaligned(&buf_32[k]) << fs);
} }
static void sh_msiof_spi_write_fifo_s32(struct sh_msiof_spi_priv *p,
const void *tx_buf, int words, int fs)
{
const u32 *buf_32 = tx_buf;
int k;
for (k = 0; k < words; k++)
sh_msiof_write(p, TFDR, swab32(buf_32[k] << fs));
}
static void sh_msiof_spi_write_fifo_s32u(struct sh_msiof_spi_priv *p,
const void *tx_buf, int words, int fs)
{
const u32 *buf_32 = tx_buf;
int k;
for (k = 0; k < words; k++)
sh_msiof_write(p, TFDR, swab32(get_unaligned(&buf_32[k]) << fs));
}
static void sh_msiof_spi_read_fifo_8(struct sh_msiof_spi_priv *p, static void sh_msiof_spi_read_fifo_8(struct sh_msiof_spi_priv *p,
void *rx_buf, int words, int fs) void *rx_buf, int words, int fs)
{ {
unsigned char *buf_8 = rx_buf; u8 *buf_8 = rx_buf;
int k; int k;
for (k = 0; k < words; k++) for (k = 0; k < words; k++)
...@@ -282,7 +300,7 @@ static void sh_msiof_spi_read_fifo_8(struct sh_msiof_spi_priv *p, ...@@ -282,7 +300,7 @@ static void sh_msiof_spi_read_fifo_8(struct sh_msiof_spi_priv *p,
static void sh_msiof_spi_read_fifo_16(struct sh_msiof_spi_priv *p, static void sh_msiof_spi_read_fifo_16(struct sh_msiof_spi_priv *p,
void *rx_buf, int words, int fs) void *rx_buf, int words, int fs)
{ {
unsigned short *buf_16 = rx_buf; u16 *buf_16 = rx_buf;
int k; int k;
for (k = 0; k < words; k++) for (k = 0; k < words; k++)
...@@ -292,7 +310,7 @@ static void sh_msiof_spi_read_fifo_16(struct sh_msiof_spi_priv *p, ...@@ -292,7 +310,7 @@ static void sh_msiof_spi_read_fifo_16(struct sh_msiof_spi_priv *p,
static void sh_msiof_spi_read_fifo_16u(struct sh_msiof_spi_priv *p, static void sh_msiof_spi_read_fifo_16u(struct sh_msiof_spi_priv *p,
void *rx_buf, int words, int fs) void *rx_buf, int words, int fs)
{ {
unsigned short *buf_16 = rx_buf; u16 *buf_16 = rx_buf;
int k; int k;
for (k = 0; k < words; k++) for (k = 0; k < words; k++)
...@@ -302,7 +320,7 @@ static void sh_msiof_spi_read_fifo_16u(struct sh_msiof_spi_priv *p, ...@@ -302,7 +320,7 @@ static void sh_msiof_spi_read_fifo_16u(struct sh_msiof_spi_priv *p,
static void sh_msiof_spi_read_fifo_32(struct sh_msiof_spi_priv *p, static void sh_msiof_spi_read_fifo_32(struct sh_msiof_spi_priv *p,
void *rx_buf, int words, int fs) void *rx_buf, int words, int fs)
{ {
unsigned int *buf_32 = rx_buf; u32 *buf_32 = rx_buf;
int k; int k;
for (k = 0; k < words; k++) for (k = 0; k < words; k++)
...@@ -312,19 +330,40 @@ static void sh_msiof_spi_read_fifo_32(struct sh_msiof_spi_priv *p, ...@@ -312,19 +330,40 @@ static void sh_msiof_spi_read_fifo_32(struct sh_msiof_spi_priv *p,
static void sh_msiof_spi_read_fifo_32u(struct sh_msiof_spi_priv *p, static void sh_msiof_spi_read_fifo_32u(struct sh_msiof_spi_priv *p,
void *rx_buf, int words, int fs) void *rx_buf, int words, int fs)
{ {
unsigned int *buf_32 = rx_buf; u32 *buf_32 = rx_buf;
int k; int k;
for (k = 0; k < words; k++) for (k = 0; k < words; k++)
put_unaligned(sh_msiof_read(p, RFDR) >> fs, &buf_32[k]); put_unaligned(sh_msiof_read(p, RFDR) >> fs, &buf_32[k]);
} }
static void sh_msiof_spi_read_fifo_s32(struct sh_msiof_spi_priv *p,
void *rx_buf, int words, int fs)
{
u32 *buf_32 = rx_buf;
int k;
for (k = 0; k < words; k++)
buf_32[k] = swab32(sh_msiof_read(p, RFDR) >> fs);
}
static void sh_msiof_spi_read_fifo_s32u(struct sh_msiof_spi_priv *p,
void *rx_buf, int words, int fs)
{
u32 *buf_32 = rx_buf;
int k;
for (k = 0; k < words; k++)
put_unaligned(swab32(sh_msiof_read(p, RFDR) >> fs), &buf_32[k]);
}
static int sh_msiof_spi_bits(struct spi_device *spi, struct spi_transfer *t) static int sh_msiof_spi_bits(struct spi_device *spi, struct spi_transfer *t)
{ {
int bits; int bits;
bits = t ? t->bits_per_word : 0; bits = t ? t->bits_per_word : 0;
bits = bits ? bits : spi->bits_per_word; if (!bits)
bits = spi->bits_per_word;
return bits; return bits;
} }
...@@ -334,7 +373,8 @@ static unsigned long sh_msiof_spi_hz(struct spi_device *spi, ...@@ -334,7 +373,8 @@ static unsigned long sh_msiof_spi_hz(struct spi_device *spi,
unsigned long hz; unsigned long hz;
hz = t ? t->speed_hz : 0; hz = t ? t->speed_hz : 0;
hz = hz ? hz : spi->max_speed_hz; if (!hz)
hz = spi->max_speed_hz;
return hz; return hz;
} }
...@@ -468,9 +508,17 @@ static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t) ...@@ -468,9 +508,17 @@ static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
int bytes_done; int bytes_done;
int words; int words;
int n; int n;
bool swab;
bits = sh_msiof_spi_bits(spi, t); bits = sh_msiof_spi_bits(spi, t);
if (bits <= 8 && t->len > 15 && !(t->len & 3)) {
bits = 32;
swab = true;
} else {
swab = false;
}
/* setup bytes per word and fifo read/write functions */ /* setup bytes per word and fifo read/write functions */
if (bits <= 8) { if (bits <= 8) {
bytes_per_word = 1; bytes_per_word = 1;
...@@ -487,6 +535,17 @@ static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t) ...@@ -487,6 +535,17 @@ static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
rx_fifo = sh_msiof_spi_read_fifo_16u; rx_fifo = sh_msiof_spi_read_fifo_16u;
else else
rx_fifo = sh_msiof_spi_read_fifo_16; rx_fifo = sh_msiof_spi_read_fifo_16;
} else if (swab) {
bytes_per_word = 4;
if ((unsigned long)t->tx_buf & 0x03)
tx_fifo = sh_msiof_spi_write_fifo_s32u;
else
tx_fifo = sh_msiof_spi_write_fifo_s32;
if ((unsigned long)t->rx_buf & 0x03)
rx_fifo = sh_msiof_spi_read_fifo_s32u;
else
rx_fifo = sh_msiof_spi_read_fifo_s32;
} else { } else {
bytes_per_word = 4; bytes_per_word = 4;
if ((unsigned long)t->tx_buf & 0x03) if ((unsigned long)t->tx_buf & 0x03)
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/compat.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/spi/spidev.h> #include <linux/spi/spidev.h>
...@@ -471,6 +472,16 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ...@@ -471,6 +472,16 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return retval; return retval;
} }
#ifdef CONFIG_COMPAT
static long
spidev_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
return spidev_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
}
#else
#define spidev_compat_ioctl NULL
#endif /* CONFIG_COMPAT */
static int spidev_open(struct inode *inode, struct file *filp) static int spidev_open(struct inode *inode, struct file *filp)
{ {
struct spidev_data *spidev; struct spidev_data *spidev;
...@@ -543,6 +554,7 @@ static const struct file_operations spidev_fops = { ...@@ -543,6 +554,7 @@ static const struct file_operations spidev_fops = {
.write = spidev_write, .write = spidev_write,
.read = spidev_read, .read = spidev_read,
.unlocked_ioctl = spidev_ioctl, .unlocked_ioctl = spidev_ioctl,
.compat_ioctl = spidev_compat_ioctl,
.open = spidev_open, .open = spidev_open,
.release = spidev_release, .release = spidev_release,
.llseek = no_llseek, .llseek = no_llseek,
......
...@@ -2,21 +2,24 @@ ...@@ -2,21 +2,24 @@
/* FIXME driver should be able to handle IRQs... */ /* FIXME driver should be able to handle IRQs... */
struct mcp23s08_chip_info { struct mcp23s08_chip_info {
bool is_present; /* true iff populated */ bool is_present; /* true if populated */
u8 pullups; /* BIT(x) means enable pullup x */ unsigned pullups; /* BIT(x) means enable pullup x */
}; };
struct mcp23s08_platform_data { struct mcp23s08_platform_data {
/* Four slaves (numbered 0..3) can share one SPI chipselect, and /* For mcp23s08, up to 4 slaves (numbered 0..3) can share one SPI
* will provide 8..32 GPIOs using 1..4 gpio_chip instances. * chipselect, each providing 1 gpio_chip instance with 8 gpios.
* For mpc23s17, up to 8 slaves (numbered 0..7) can share one SPI
* chipselect, each providing 1 gpio_chip (port A + port B) with
* 16 gpios.
*/ */
struct mcp23s08_chip_info chip[4]; struct mcp23s08_chip_info chip[8];
/* "base" is the number of the first GPIO. Dynamic assignment is /* "base" is the number of the first GPIO. Dynamic assignment is
* not currently supported, and even if there are gaps in chip * not currently supported, and even if there are gaps in chip
* addressing the GPIO numbers are sequential .. so for example * addressing the GPIO numbers are sequential .. so for example
* if only slaves 0 and 3 are present, their GPIOs range from * if only slaves 0 and 3 are present, their GPIOs range from
* base to base+15. * base to base+15 (or base+31 for s17 variant).
*/ */
unsigned base; unsigned base;
......
#ifndef _LINUX_SPI_SPI_OC_TINY_H
#define _LINUX_SPI_SPI_OC_TINY_H
/**
* struct tiny_spi_platform_data - platform data of the OpenCores tiny SPI
* @freq: input clock freq to the core.
* @baudwidth: baud rate divider width of the core.
* @gpio_cs_count: number of gpio pins used for chipselect.
* @gpio_cs: array of gpio pins used for chipselect.
*
* freq and baudwidth are used only if the divider is programmable.
*/
struct tiny_spi_platform_data {
unsigned int freq;
unsigned int baudwidth;
unsigned int gpio_cs_count;
int *gpio_cs;
};
#endif /* _LINUX_SPI_SPI_OC_TINY_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment