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
  ...
This commit is contained in:
Linus Torvalds 2011-03-18 10:56:02 -07:00
commit b061c59c27
35 changed files with 1921 additions and 262 deletions

View file

@ -7,8 +7,13 @@ Required properties:
- voltage-ranges : two cells are required, first cell specifies minimum
slot voltage (mV), second cell specifies maximum slot voltage (mV).
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.
- interrupts : the interrupt of a card detect interrupt.
- interrupt-parent : the phandle for the interrupt controller that
services interrupts for this device.
Example:
@ -20,4 +25,6 @@ Example:
&qe_pio_d 15 0>;
voltage-ranges = <3300 3300>;
spi-max-frequency = <50000000>;
interrupts = <42>;
interrupt-parent = <&PIC>;
};

View file

@ -0,0 +1,4 @@
Altera SPI
Required properties:
- compatible : should be "ALTR,spi-1.0".

View file

@ -0,0 +1,12 @@
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.

View file

@ -133,7 +133,7 @@ static int __devinit gen_74x164_probe(struct spi_device *spi)
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;
int ret;

View file

@ -101,7 +101,7 @@ config GPIO_VR41XX
config GPIO_SCH
tristate "Intel SCH GPIO"
depends on GPIOLIB && PCI
depends on GPIOLIB && PCI && X86
select MFD_CORE
select LPC_SCH
help
@ -321,13 +321,13 @@ config GPIO_BT8XX
config GPIO_LANGWELL
bool "Intel Langwell/Penwell GPIO support"
depends on PCI
depends on PCI && X86
help
Say Y here to support Intel Langwell/Penwell GPIO.
config GPIO_PCH
tristate "PCH GPIO of Intel Topcliff"
depends on PCI
depends on PCI && X86
help
This driver is for PCH(Platform controller Hub) GPIO of Intel Topcliff
which is an IOH(Input/Output Hub) for x86 embedded processor.
@ -368,11 +368,11 @@ config GPIO_MAX7301
GPIO driver for Maxim MAX7301 SPI-based GPIO expander.
config GPIO_MCP23S08
tristate "Microchip MCP23S08 I/O expander"
tristate "Microchip MCP23Sxx I/O expander"
depends on SPI_MASTER
help
SPI driver for Microchip MCP23S08 I/O expander. This provides
a GPIO interface supporting inputs and outputs.
SPI driver for Microchip MCP23S08/MPC23S17 I/O expanders.
This provides a GPIO interface supporting inputs and outputs.
config GPIO_MC33880
tristate "Freescale MC33880 high-side/low-side switch"

View file

@ -373,7 +373,7 @@ static int __devexit cs5535_gpio_remove(struct platform_device *pdev)
return 0;
}
static struct platform_driver cs5535_gpio_drv = {
static struct platform_driver cs5535_gpio_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
@ -384,12 +384,12 @@ static struct platform_driver cs5535_gpio_drv = {
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)
{
platform_driver_unregister(&cs5535_gpio_drv);
platform_driver_unregister(&cs5535_gpio_driver);
}
module_init(cs5535_gpio_init);

View file

@ -187,31 +187,28 @@ MODULE_DEVICE_TABLE(pci, lnw_gpio_ids);
static void lnw_irq_handler(unsigned irq, struct irq_desc *desc)
{
struct lnw_gpio *lnw = get_irq_data(irq);
u32 base, gpio;
struct irq_data *data = irq_desc_get_irq_data(desc);
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;
u32 gedr_v;
/* check GPIO controller to check which pin triggered the interrupt */
for (base = 0; base < lnw->chip.ngpio; base += 32) {
gedr = gpio_reg(&lnw->chip, base, GEDR);
gedr_v = readl(gedr);
if (!gedr_v)
continue;
for (gpio = base; gpio < base + 32; gpio++)
if (gedr_v & BIT(gpio % 32)) {
pr_debug("pin %d triggered\n", gpio);
generic_handle_irq(lnw->irq_base + gpio);
}
/* clear the edge detect status bit */
writel(gedr_v, gedr);
pending = readl(gedr);
while (pending) {
gpio = __ffs(pending) - 1;
mask = BIT(gpio);
pending &= ~mask;
/* Clear before handling so we can't lose an edge */
writel(mask, gedr);
generic_handle_irq(lnw->irq_base + base + gpio);
}
}
if (desc->chip->irq_eoi)
desc->chip->irq_eoi(irq_get_irq_data(irq));
else
dev_warn(lnw->chip.dev, "missing EOI handler for irq %d\n", irq);
chip->irq_eoi(data);
}
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);
goto err5;
}
set_irq_data(pdev->irq, lnw);
set_irq_chained_handler(pdev->irq, lnw_irq_handler);
irq_set_handler_data(pdev->irq, lnw);
irq_set_chained_handler(pdev->irq, lnw_irq_handler);
for (i = 0; i < lnw->chip.ngpio; i++) {
set_irq_chip_and_handler_name(i + lnw->irq_base, &lnw_irqchip,
handle_simple_irq, "demux");
set_irq_chip_data(i + lnw->irq_base, lnw);
irq_set_chip_and_handler_name(i + lnw->irq_base, &lnw_irqchip,
handle_simple_irq, "demux");
irq_set_chip_data(i + lnw->irq_base, lnw);
}
spin_lock_init(&lnw->lock);

View file

@ -146,7 +146,7 @@ static int __devinit mc33880_probe(struct spi_device *spi)
return ret;
}
static int mc33880_remove(struct spi_device *spi)
static int __devexit mc33880_remove(struct spi_device *spi)
{
struct mc33880 *mc;
int ret;

View file

@ -10,7 +10,13 @@
#include <linux/spi/spi.h>
#include <linux/spi/mcp23s08.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.
*
@ -35,27 +41,38 @@
#define MCP_GPIO 0x09
#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 spi_device *spi;
u8 addr;
u8 cache[11];
u16 cache[11];
/* lock protects the cached values */
struct mutex lock;
struct gpio_chip chip;
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
* (e.g. chips #0 and #3 might be populated, but not #1 or $2).
* Driver data holds all the per-chip data.
*/
struct mcp23s08_driver_data {
unsigned ngpio;
struct mcp23s08 *mcp[4];
struct mcp23s08 *mcp[8];
struct mcp23s08 chip[];
};
@ -70,7 +87,7 @@ static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg)
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];
@ -81,17 +98,81 @@ static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, u8 val)
}
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)
return -EINVAL;
tx[0] = mcp->addr | 0x01;
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)
@ -101,7 +182,7 @@ static int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset)
mutex_lock(&mcp->lock);
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);
return status;
}
@ -114,7 +195,7 @@ static int mcp23s08_get(struct gpio_chip *chip, unsigned offset)
mutex_lock(&mcp->lock);
/* REVISIT reading this clears any IRQ ... */
status = mcp23s08_read(mcp, MCP_GPIO);
status = mcp->ops->read(mcp, MCP_GPIO);
if (status < 0)
status = 0;
else {
@ -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)
{
u8 olat = mcp->cache[MCP_OLAT];
unsigned olat = mcp->cache[MCP_OLAT];
if (value)
olat |= mask;
else
olat &= ~mask;
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)
{
struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip);
u8 mask = 1 << offset;
unsigned mask = 1 << offset;
mutex_lock(&mcp->lock);
__mcp23s08_set(mcp, mask, value);
@ -151,14 +232,14 @@ static int
mcp23s08_direction_output(struct gpio_chip *chip, unsigned offset, int value)
{
struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip);
u8 mask = 1 << offset;
unsigned mask = 1 << offset;
int status;
mutex_lock(&mcp->lock);
status = __mcp23s08_set(mcp, mask, value);
if (status == 0) {
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);
return status;
@ -184,16 +265,16 @@ static void mcp23s08_dbg_show(struct seq_file *s, struct gpio_chip *chip)
mcp = container_of(chip, struct mcp23s08, chip);
/* NOTE: we only handle one bank for now ... */
bank = '0' + ((mcp->addr >> 1) & 0x3);
bank = '0' + ((mcp->addr >> 1) & 0x7);
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) {
seq_printf(s, " I/O ERROR %d\n", t);
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;
label = gpiochip_is_requested(chip, t);
@ -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,
unsigned base, unsigned pullups)
unsigned type, unsigned base, unsigned pullups)
{
struct mcp23s08_driver_data *data = spi_get_drvdata(spi);
struct mcp23s08 *mcp = data->mcp[addr];
int status;
int do_update = 0;
mutex_init(&mcp->lock);
mcp->spi = spi;
mcp->addr = 0x40 | (addr << 1);
mcp->chip.label = "mcp23s08",
mcp->chip.direction_input = mcp23s08_direction_input;
mcp->chip.get = mcp23s08_get;
mcp->chip.direction_output = mcp23s08_direction_output;
mcp->chip.set = mcp23s08_set;
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.ngpio = 8;
mcp->chip.can_sleep = 1;
mcp->chip.dev = &spi->dev;
mcp->chip.owner = THIS_MODULE;
@ -248,45 +334,39 @@ static int mcp23s08_probe_one(struct spi_device *spi, unsigned addr,
/* verify MCP_IOCON.SEQOP = 0, so sequential reads work,
* 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)
goto fail;
if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN)) {
status &= ~IOCON_SEQOP;
status |= IOCON_HAEN;
status = mcp23s08_write(mcp, MCP_IOCON, (u8) status);
/* mcp23s17 has IOCON twice, make sure they are in sync */
status &= ~(IOCON_SEQOP | (IOCON_SEQOP << 8));
status |= IOCON_HAEN | (IOCON_HAEN << 8);
status = mcp->ops->write(mcp, MCP_IOCON, status);
if (status < 0)
goto fail;
}
/* configure ~100K pullups */
status = mcp23s08_write(mcp, MCP_GPPU, pullups);
status = mcp->ops->write(mcp, MCP_GPPU, pullups);
if (status < 0)
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)
goto fail;
/* disable inverter on input */
if (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 */
if (mcp->cache[MCP_GPINTEN] != 0) {
mcp->cache[MCP_GPINTEN] = 0;
do_update = 1;
}
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);
status = mcp->ops->write(mcp, MCP_GPINTEN, 0);
if (status < 0)
goto fail;
}
@ -305,19 +385,26 @@ static int mcp23s08_probe(struct spi_device *spi)
unsigned addr;
unsigned chips = 0;
struct mcp23s08_driver_data *data;
int status;
int status, type;
unsigned base;
type = spi_get_device_id(spi)->driver_data;
pdata = spi->dev.platform_data;
if (!pdata || !gpio_is_valid(pdata->base)) {
dev_dbg(&spi->dev, "invalid or missing platform data\n");
return -EINVAL;
}
for (addr = 0; addr < 4; addr++) {
for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) {
if (!pdata->chip[addr].is_present)
continue;
chips++;
if ((type == MCP_TYPE_S08) && (addr > 3)) {
dev_err(&spi->dev,
"mcp23s08 only supports address 0..3\n");
return -EINVAL;
}
}
if (!chips)
return -ENODEV;
@ -329,16 +416,17 @@ static int mcp23s08_probe(struct spi_device *spi)
spi_set_drvdata(spi, data);
base = pdata->base;
for (addr = 0; addr < 4; addr++) {
for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) {
if (!pdata->chip[addr].is_present)
continue;
chips--;
data->mcp[addr] = &data->chip[chips];
status = mcp23s08_probe_one(spi, addr, base,
pdata->chip[addr].pullups);
status = mcp23s08_probe_one(spi, addr, type, base,
pdata->chip[addr].pullups);
if (status < 0)
goto fail;
base += 8;
base += (type == MCP_TYPE_S17) ? 16 : 8;
}
data->ngpio = base - pdata->base;
@ -358,7 +446,7 @@ static int mcp23s08_probe(struct spi_device *spi)
return 0;
fail:
for (addr = 0; addr < 4; addr++) {
for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) {
int tmp;
if (!data->mcp[addr])
@ -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;
if (!data->mcp[addr])
@ -405,9 +493,17 @@ static int mcp23s08_remove(struct spi_device *spi)
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 = {
.probe = mcp23s08_probe,
.remove = mcp23s08_remove,
.id_table = mcp23s08_ids,
.driver = {
.name = "mcp23s08",
.owner = THIS_MODULE,
@ -432,4 +528,3 @@ static void __exit mcp23s08_exit(void)
module_exit(mcp23s08_exit);
MODULE_LICENSE("GPL");
MODULE_ALIAS("spi:mcp23s08");

View file

@ -462,7 +462,8 @@ pca953x_get_alt_pdata(struct i2c_client *client)
{
struct pca953x_platform_data *pdata;
struct device_node *node;
const uint16_t *val;
const __be32 *val;
int size;
node = client->dev.of_node;
if (node == NULL)
@ -475,13 +476,13 @@ pca953x_get_alt_pdata(struct i2c_client *client)
}
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 < 0)
dev_warn(&client->dev,
"invalid gpio-base in device tree\n");
if (size != sizeof(*val))
dev_warn(&client->dev, "%s: wrong linux,gpio-base\n",
node->full_name);
else
pdata->gpio_base = *val;
pdata->gpio_base = be32_to_cpup(val);
}
val = of_get_property(node, "polarity", NULL);

View file

@ -25,6 +25,8 @@
#include <linux/workqueue.h>
#include <linux/i2c/sx150x.h>
#define NO_UPDATE_PENDING -1
struct sx150x_device_data {
u8 reg_pullup;
u8 reg_pulldn;
@ -47,8 +49,11 @@ struct sx150x_chip {
const struct sx150x_device_data *dev_cfg;
int irq_summary;
int irq_base;
int irq_update;
u32 irq_sense;
unsigned long irq_set_type_pending;
u32 irq_masked;
u32 dev_sense;
u32 dev_masked;
struct irq_chip irq_chip;
struct mutex lock;
};
@ -312,9 +317,8 @@ static void sx150x_irq_mask(struct irq_data *d)
chip = container_of(ic, struct sx150x_chip, irq_chip);
n = d->irq - chip->irq_base;
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);
chip->irq_masked |= (1 << n);
chip->irq_update = n;
}
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);
n = d->irq - chip->irq_base;
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));
chip->irq_masked &= ~(1 << n);
chip->irq_update = n;
}
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 |= val << (n * 2);
chip->irq_set_type_pending |= BIT(n);
chip->irq_update = n;
return 0;
}
@ -404,15 +407,29 @@ static void sx150x_irq_bus_sync_unlock(struct irq_data *d)
chip = container_of(ic, struct sx150x_chip, irq_chip);
while (chip->irq_set_type_pending) {
n = __ffs(chip->irq_set_type_pending);
chip->irq_set_type_pending &= ~BIT(n);
if (!(irq_to_desc(n + chip->irq_base)->status & IRQ_MASKED))
sx150x_write_cfg(chip, n, 2,
chip->dev_cfg->reg_sense,
chip->irq_sense >> (n * 2));
}
if (chip->irq_update == NO_UPDATE_PENDING)
goto out;
n = chip->irq_update;
chip->irq_update = NO_UPDATE_PENDING;
/* 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);
}
@ -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_summary = -1;
chip->irq_base = -1;
chip->irq_masked = ~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)

View file

@ -34,6 +34,7 @@ enum {
struct of_mmc_spi {
int gpios[NUM_GPIOS];
bool alow_gpios[NUM_GPIOS];
int detect_irq;
struct mmc_spi_platform_data pdata;
};
@ -61,6 +62,22 @@ static int of_mmc_spi_get_ro(struct device *dev)
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 device *dev = &spi->dev;
@ -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]))
oms->pdata.get_ro = of_mmc_spi_get_ro;
/* We don't support interrupts yet, let's poll. */
oms->pdata.caps |= MMC_CAP_NEEDS_POLL;
oms->detect_irq = irq_of_parse_and_map(np, 0);
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;
return dev->platform_data;

View file

@ -158,7 +158,7 @@ static int __devinit ds1390_probe(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);
kfree(chip);

View file

@ -158,7 +158,7 @@ static int __devinit ds3234_probe(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);
return 0;

View file

@ -136,7 +136,7 @@ static int __devinit m41t94_probe(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)
rtc_device_unregister(rtc);

View file

@ -53,6 +53,12 @@ if SPI_MASTER
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
tristate "Atheros AR71XX/AR724X/AR913X SPI controller driver"
depends on ATH79 && GENERIC_GPIO
@ -231,6 +237,13 @@ config SPI_FSL_ESPI
From MPC8536, 85xx platform uses the controller, and all P10xx,
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
tristate "OMAP1 MicroWire"
depends on ARCH_OMAP1
@ -330,6 +343,12 @@ config SPI_SH_MSIOF
help
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
tristate "SuperH SCI SPI controller"
depends on SUPERH

View file

@ -9,6 +9,7 @@ ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG
obj-$(CONFIG_SPI_MASTER) += spi.o
# SPI master controller drivers (bus)
obj-$(CONFIG_SPI_ALTERA) += spi_altera.o
obj-$(CONFIG_SPI_ATMEL) += atmel_spi.o
obj-$(CONFIG_SPI_ATH79) += ath79_spi.o
obj-$(CONFIG_SPI_BFIN) += spi_bfin5xx.o
@ -27,6 +28,7 @@ obj-$(CONFIG_SPI_IMX) += spi_imx.o
obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o
obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.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_OMAP24XX) += omap2_mcspi.o
obj-$(CONFIG_SPI_OMAP_100K) += omap_spi_100k.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_TXX9) += spi_txx9.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_MSIOF) += spi_sh_msiof.o
obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o

View file

@ -329,15 +329,16 @@ struct vendor_data {
/**
* struct pl022 - This is the private SSP driver data structure
* @adev: AMBA device model hookup
* @vendor: Vendor data for the IP block
* @phybase: The physical memory where the SSP device resides
* @virtbase: The virtual memory where the SSP is mapped
* @vendor: vendor data for the IP block
* @phybase: the physical memory where the SSP device resides
* @virtbase: the virtual memory where the SSP is mapped
* @clk: outgoing clock "SPICLK" for the SPI bus
* @master: SPI framework hookup
* @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
* @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
* @running: workqueue is running
* @pump_transfers: Tasklet used in Interrupt Transfer mode
@ -348,8 +349,14 @@ struct vendor_data {
* @tx_end: end position in TX buffer to be read
* @rx: current 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
* @writingtype: the type or write currently going on
* @read: the type of read 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 amba_device *adev;
@ -397,8 +404,8 @@ struct pl022 {
* @cpsr: Value of Clock prescale register
* @n_bytes: how many bytes(power of 2) reqd for a given data width of client
* @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
* @write: function ptr to be used to write when doing xfer for this chip
* @cs_control: chip select callback provided by chip
* @xfer_type: polling/interrupt/DMA
*
@ -508,9 +515,10 @@ static void giveback(struct pl022 *pl022)
msg->state = NULL;
if (msg->complete)
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);
amba_pclk_disable(pl022->adev);
amba_vcore_disable(pl022->adev);
}
/**
@ -917,7 +925,6 @@ static int configure_dma(struct pl022 *pl022)
struct dma_chan *txchan = pl022->dma_tx_channel;
struct dma_async_tx_descriptor *rxdesc;
struct dma_async_tx_descriptor *txdesc;
dma_cookie_t cookie;
/* Check that the channels are available */
if (!rxchan || !txchan)
@ -962,10 +969,8 @@ static int configure_dma(struct pl022 *pl022)
tx_conf.dst_addr_width = rx_conf.src_addr_width;
BUG_ON(rx_conf.src_addr_width != tx_conf.dst_addr_width);
rxchan->device->device_control(rxchan, DMA_SLAVE_CONFIG,
(unsigned long) &rx_conf);
txchan->device->device_control(txchan, DMA_SLAVE_CONFIG,
(unsigned long) &tx_conf);
dmaengine_slave_config(rxchan, &rx_conf);
dmaengine_slave_config(txchan, &tx_conf);
/* Create sglists for the transfers */
pages = (pl022->cur_transfer->len >> PAGE_SHIFT) + 1;
@ -1018,23 +1023,17 @@ static int configure_dma(struct pl022 *pl022)
rxdesc->callback_param = pl022;
/* Submit and fire RX and TX with TX last so we're ready to read! */
cookie = rxdesc->tx_submit(rxdesc);
if (dma_submit_error(cookie))
goto err_submit_rx;
cookie = txdesc->tx_submit(txdesc);
if (dma_submit_error(cookie))
goto err_submit_tx;
rxchan->device->device_issue_pending(rxchan);
txchan->device->device_issue_pending(txchan);
dmaengine_submit(rxdesc);
dmaengine_submit(txdesc);
dma_async_issue_pending(rxchan);
dma_async_issue_pending(txchan);
return 0;
err_submit_tx:
err_submit_rx:
err_txdesc:
txchan->device->device_control(txchan, DMA_TERMINATE_ALL, 0);
dmaengine_terminate_all(txchan);
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,
pl022->sgt_tx.nents, DMA_TO_DEVICE);
err_tx_sgmap:
@ -1101,8 +1100,8 @@ static void terminate_dma(struct pl022 *pl022)
struct dma_chan *rxchan = pl022->dma_rx_channel;
struct dma_chan *txchan = pl022->dma_tx_channel;
rxchan->device->device_control(rxchan, DMA_TERMINATE_ALL, 0);
txchan->device->device_control(txchan, DMA_TERMINATE_ALL, 0);
dmaengine_terminate_all(rxchan);
dmaengine_terminate_all(txchan);
unmap_free_dma_scatter(pl022);
}
@ -1482,9 +1481,11 @@ static void pump_messages(struct work_struct *work)
/* Setup the SPI using the per chip configuration */
pl022->cur_chip = spi_get_ctldata(pl022->cur_msg->spi);
/*
* We enable the clocks here, then the clocks will be disabled when
* giveback() is called in each method (poll/interrupt/DMA)
* We enable the core voltage and clocks here, then the clocks
* 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);
clk_enable(pl022->clk);
restore_state(pl022);
@ -1910,8 +1911,6 @@ static int pl022_setup(struct spi_device *spi)
&& ((pl022->master_info)->enable_dma)) {
chip->enable_dma = true;
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_DMACR_MASK_RXDMAE, 0);
SSP_WRITE_BITS(chip->dmacr, SSP_DMA_ENABLED,
@ -2130,8 +2129,12 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
goto err_spi_register;
}
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_vcore_disable(adev);
return 0;
err_spi_register:
@ -2196,9 +2199,11 @@ static int pl022_suspend(struct amba_device *adev, pm_message_t state)
return status;
}
amba_vcore_enable(adev);
amba_pclk_enable(adev);
load_ssp_default_config(pl022);
amba_pclk_disable(adev);
amba_vcore_disable(adev);
dev_dbg(&adev->dev, "suspended\n");
return 0;
}

View file

@ -571,6 +571,7 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
unsigned long tx_reg, rx_reg;
struct edmacc_param param;
void *rx_buf;
int b, c;
dma = &dspi->dma;
@ -591,22 +592,38 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
if (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)) {
dev_dbg(sdev, "Unable to DMA map %d bytes"
"TX buffer\n", dspi->wcount);
"TX buffer\n", t->len);
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.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.src_dst_bidx = t->tx_buf ? data_type : 0;
param.link_bcntrld = 0xffff;
param.src_dst_cidx = 0;
param.ccnt = 1;
param.link_bcntrld = 0xffffffff;
param.src_dst_cidx = t->tx_buf ? data_type : 0;
param.ccnt = c;
edma_write_slot(dma->tx_channel, &param);
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)
if (t->rx_buf) {
rx_buf = t->rx_buf;
rx_buf_count = dspi->rcount;
rx_buf_count = t->len;
} else {
rx_buf = 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)
dev_dbg(sdev, "Couldn't DMA map a %d bytes RX buffer\n",
rx_buf_count);
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);
return -ENOMEM;
}
param.opt = TCINTEN | EDMA_TCC(dma->rx_channel);
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.src_dst_bidx = (t->rx_buf ? data_type : 0) << 16;
param.link_bcntrld = 0xffff;
param.src_dst_cidx = 0;
param.ccnt = 1;
param.link_bcntrld = 0xffffffff;
param.src_dst_cidx = (t->rx_buf ? data_type : 0) << 16;
param.ccnt = c;
edma_write_slot(dma->rx_channel, &param);
if (pdata->cshold_bug)
@ -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 (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_unmap_single(NULL, t->rx_dma, rx_buf_count,

View file

@ -22,10 +22,10 @@
#include <linux/highmem.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/spi/dw_spi.h>
#include <linux/spi/spi.h>
#include "dw_spi.h"
#ifdef CONFIG_DEBUG_FS
#include <linux/debugfs.h>
#endif

View file

@ -2,6 +2,7 @@
#define DW_SPI_HEADER_H
#include <linux/io.h>
#include <linux/scatterlist.h>
/* Bit fields in CTRLR0 */
#define SPI_DFS_OFFSET 0

View file

@ -22,7 +22,8 @@
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/spi/dw_spi.h>
#include "dw_spi.h"
#ifdef CONFIG_SPI_DW_MID_DMA
#include <linux/intel_mid_dma.h>

View file

@ -13,8 +13,10 @@
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spi/dw_spi.h>
#include <linux/spi/spi.h>
#include <linux/scatterlist.h>
#include "dw_spi.h"
#define DRIVER_NAME "dw_spi_mmio"

View file

@ -20,9 +20,10 @@
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/spi/dw_spi.h>
#include <linux/spi/spi.h>
#include "dw_spi.h"
#define DRIVER_NAME "dw_spi_pci"
struct dw_spi_pci {

View file

@ -467,6 +467,9 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
rx_reg = base + OMAP2_MCSPI_RX0;
chstat_reg = base + OMAP2_MCSPI_CHSTAT0;
if (c < (word_len>>3))
return 0;
if (word_len <= 8) {
u8 *rx;
const u8 *tx;
@ -514,7 +517,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
dev_vdbg(&spi->dev, "read-%d %02x\n",
word_len, *(rx - 1));
}
} while (c);
} while (c > (word_len>>3));
} else if (word_len <= 16) {
u16 *rx;
const u16 *tx;
@ -561,7 +564,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
dev_vdbg(&spi->dev, "read-%d %04x\n",
word_len, *(rx - 1));
}
} while (c);
} while (c > (word_len>>3));
} else if (word_len <= 32) {
u32 *rx;
const u32 *tx;
@ -608,7 +611,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
dev_vdbg(&spi->dev, "read-%d %08x\n",
word_len, *(rx - 1));
}
} while (c);
} while (c > (word_len>>3));
}
/* 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)
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 */
static int omap2_mcspi_setup_transfer(struct spi_device *spi,
struct spi_transfer *t)
@ -653,12 +667,8 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
if (t && t->speed_hz)
speed_hz = t->speed_hz;
if (speed_hz) {
while (div <= 15 && (OMAP2_MCSPI_MAX_FREQ / (1 << div))
> speed_hz)
div++;
} else
div = 15;
speed_hz = min_t(u32, speed_hz, OMAP2_MCSPI_MAX_FREQ);
div = omap2_mcspi_calc_divisor(speed_hz);
l = mcspi_cached_chconf0(spi);
@ -695,7 +705,7 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
mcspi_write_chconf0(spi, l);
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_CPOL) ? "inverted" : "normal");
@ -996,10 +1006,10 @@ static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m)
t->bits_per_word);
return -EINVAL;
}
if (t->speed_hz && t->speed_hz < OMAP2_MCSPI_MAX_FREQ/(1<<16)) {
dev_dbg(&spi->dev, "%d Hz max exceeds %d\n",
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, "speed_hz %d below minimum %d Hz\n",
t->speed_hz,
OMAP2_MCSPI_MAX_FREQ >> 15);
return -EINVAL;
}

339
drivers/spi/spi_altera.c Normal file
View file

@ -0,0 +1,339 @@
/*
* 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);

View file

@ -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 spi_message *msg = drv_data->cur_msg;
int n_bytes = drv_data->n_bytes;
int loop = 0;
/* wait until transfer finished. */
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)
/* last read */
if (drv_data->rx) {
dev_dbg(&drv_data->pdev->dev, "last read\n");
if (n_bytes == 2)
*(u16 *) (drv_data->rx) = read_RDBR(drv_data);
else if (n_bytes == 1)
*(u8 *) (drv_data->rx) = read_RDBR(drv_data);
if (n_bytes % 2) {
u16 *buf = (u16 *)drv_data->rx;
for (loop = 0; loop < n_bytes / 2; loop++)
*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;
}
@ -458,29 +464,53 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id)
if (drv_data->rx && drv_data->tx) {
/* duplex */
dev_dbg(&drv_data->pdev->dev, "duplex: write_TDBR\n");
if (drv_data->n_bytes == 2) {
*(u16 *) (drv_data->rx) = read_RDBR(drv_data);
write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));
} else if (drv_data->n_bytes == 1) {
*(u8 *) (drv_data->rx) = read_RDBR(drv_data);
write_TDBR(drv_data, (*(u8 *) (drv_data->tx)));
if (n_bytes % 2) {
u16 *buf = (u16 *)drv_data->rx;
u16 *buf2 = (u16 *)drv_data->tx;
for (loop = 0; loop < n_bytes / 2; loop++) {
*buf++ = read_RDBR(drv_data);
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) {
/* read */
dev_dbg(&drv_data->pdev->dev, "read: write_TDBR\n");
if (drv_data->n_bytes == 2)
*(u16 *) (drv_data->rx) = read_RDBR(drv_data);
else if (drv_data->n_bytes == 1)
*(u8 *) (drv_data->rx) = read_RDBR(drv_data);
write_TDBR(drv_data, chip->idle_tx_val);
if (n_bytes % 2) {
u16 *buf = (u16 *)drv_data->rx;
for (loop = 0; loop < n_bytes / 2; loop++) {
*buf++ = read_RDBR(drv_data);
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) {
/* write */
dev_dbg(&drv_data->pdev->dev, "write: write_TDBR\n");
bfin_spi_dummy_read(drv_data);
if (drv_data->n_bytes == 2)
write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));
else if (drv_data->n_bytes == 1)
write_TDBR(drv_data, (*(u8 *) (drv_data->tx)));
if (n_bytes % 2) {
u16 *buf = (u16 *)drv_data->tx;
for (loop = 0; loop < n_bytes / 2; loop++) {
read_RDBR(drv_data);
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)
@ -623,6 +653,7 @@ static void bfin_spi_pump_transfers(unsigned long data)
message->state = bfin_spi_next_transfer(drv_data);
/* Schedule next transfer tasklet */
tasklet_schedule(&drv_data->pump_transfers);
return;
}
if (transfer->tx_buf != NULL) {
@ -651,16 +682,16 @@ static void bfin_spi_pump_transfers(unsigned long data)
/* Bits per word setup */
bits_per_word = transfer->bits_per_word ? : message->spi->bits_per_word;
if (bits_per_word == 8) {
drv_data->n_bytes = 1;
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;
if ((bits_per_word > 0) && (bits_per_word % 16 == 0)) {
drv_data->n_bytes = bits_per_word/8;
drv_data->len = (transfer->len) >> 1;
cr_width = BIT_CTL_WORDSIZE;
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 {
dev_err(&drv_data->pdev->dev, "transfer: unsupported bits_per_word\n");
message->status = -EINVAL;
@ -815,10 +846,19 @@ static void bfin_spi_pump_transfers(unsigned long data)
if (drv_data->tx == NULL)
write_TDBR(drv_data, chip->idle_tx_val);
else {
if (bits_per_word == 8)
write_TDBR(drv_data, (*(u8 *) (drv_data->tx)));
else
write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));
int loop;
if (bits_per_word % 16 == 0) {
u16 *buf = (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;
}
@ -1031,7 +1071,7 @@ static int bfin_spi_setup(struct spi_device *spi)
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",
spi->bits_per_word);
goto error;

View file

@ -259,10 +259,6 @@ static void bitbang_work(struct work_struct *work)
struct spi_bitbang *bitbang =
container_of(work, struct spi_bitbang, work);
unsigned long flags;
int (*setup_transfer)(struct spi_device *,
struct spi_transfer *);
setup_transfer = bitbang->setup_transfer;
spin_lock_irqsave(&bitbang->lock, flags);
bitbang->busy = 1;
@ -300,11 +296,7 @@ static void bitbang_work(struct work_struct *work)
/* init (-1) or override (1) transfer params */
if (do_setup != 0) {
if (!setup_transfer) {
status = -ENOPROTOOPT;
break;
}
status = setup_transfer(spi, t);
status = bitbang->setup_transfer(spi, t);
if (status < 0)
break;
if (do_setup == -1)
@ -465,6 +457,9 @@ int spi_bitbang_start(struct spi_bitbang *bitbang)
}
} else if (!bitbang->master->setup)
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 */
bitbang->busy = 0;

View file

@ -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_ENABLE (1 << 0)
#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_PREDIV_OFFSET 12
#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,
{
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 */
ctrl |= spi_imx2_3_clkdiv(spi_imx->spi_clk, config->speed_hz);

425
drivers/spi/spi_oc_tiny.c Normal file
View file

@ -0,0 +1,425 @@
/*
* 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);

543
drivers/spi/spi_sh.c Normal file
View file

@ -0,0 +1,543 @@
/*
* 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");

View file

@ -9,22 +9,22 @@
*
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/completion.h>
#include <linux/pm_runtime.h>
#include <linux/gpio.h>
#include <linux/bitmap.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/spi/sh_msiof.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/spi/sh_msiof.h>
#include <asm/unaligned.h>
@ -67,7 +67,7 @@ struct sh_msiof_spi_priv {
#define STR_TEOF (1 << 23)
#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) {
case TSCR:
@ -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,
unsigned long value)
u32 value)
{
switch (reg_offs) {
case TSCR:
@ -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,
unsigned long clr, unsigned long set)
u32 clr, u32 set)
{
unsigned long mask = clr | set;
unsigned long data;
u32 mask = clr | set;
u32 data;
int k;
data = sh_msiof_read(p, CTR);
@ -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,
int cpol, int cpha,
int tx_hi_z, int lsb_first)
u32 cpol, u32 cpha,
u32 tx_hi_z, u32 lsb_first)
{
unsigned long tmp;
u32 tmp;
int edge;
/*
@ -187,7 +187,7 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p,
tmp |= cpol << 30; /* TSCKIZ */
tmp |= cpol << 28; /* RSCKIZ */
edge = cpol ? cpha : !cpha;
edge = cpol ^ !cpha;
tmp |= edge << 27; /* TEDG */
tmp |= edge << 26; /* REDG */
@ -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,
const void *tx_buf, void *rx_buf,
int bits, int words)
u32 bits, u32 words)
{
unsigned long dr2;
dr2 = ((bits - 1) << 24) | ((words - 1) << 16);
u32 dr2 = ((bits - 1) << 24) | ((words - 1) << 16);
if (tx_buf)
sh_msiof_write(p, TMDR2, dr2);
@ -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,
const void *tx_buf, int words, int fs)
{
const unsigned char *buf_8 = tx_buf;
const u8 *buf_8 = tx_buf;
int 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,
static void sh_msiof_spi_write_fifo_16(struct sh_msiof_spi_priv *p,
const void *tx_buf, int words, int fs)
{
const unsigned short *buf_16 = tx_buf;
const u16 *buf_16 = tx_buf;
int 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,
static void sh_msiof_spi_write_fifo_16u(struct sh_msiof_spi_priv *p,
const void *tx_buf, int words, int fs)
{
const unsigned short *buf_16 = tx_buf;
const u16 *buf_16 = tx_buf;
int 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,
static void sh_msiof_spi_write_fifo_32(struct sh_msiof_spi_priv *p,
const void *tx_buf, int words, int fs)
{
const unsigned int *buf_32 = tx_buf;
const u32 *buf_32 = tx_buf;
int 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,
static void sh_msiof_spi_write_fifo_32u(struct sh_msiof_spi_priv *p,
const void *tx_buf, int words, int fs)
{
const unsigned int *buf_32 = tx_buf;
const u32 *buf_32 = tx_buf;
int k;
for (k = 0; k < words; k++)
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,
void *rx_buf, int words, int fs)
{
unsigned char *buf_8 = rx_buf;
u8 *buf_8 = rx_buf;
int 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,
static void sh_msiof_spi_read_fifo_16(struct sh_msiof_spi_priv *p,
void *rx_buf, int words, int fs)
{
unsigned short *buf_16 = rx_buf;
u16 *buf_16 = rx_buf;
int 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,
static void sh_msiof_spi_read_fifo_16u(struct sh_msiof_spi_priv *p,
void *rx_buf, int words, int fs)
{
unsigned short *buf_16 = rx_buf;
u16 *buf_16 = rx_buf;
int 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,
static void sh_msiof_spi_read_fifo_32(struct sh_msiof_spi_priv *p,
void *rx_buf, int words, int fs)
{
unsigned int *buf_32 = rx_buf;
u32 *buf_32 = rx_buf;
int 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,
static void sh_msiof_spi_read_fifo_32u(struct sh_msiof_spi_priv *p,
void *rx_buf, int words, int fs)
{
unsigned int *buf_32 = rx_buf;
u32 *buf_32 = rx_buf;
int k;
for (k = 0; k < words; 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)
{
int bits;
bits = t ? t->bits_per_word : 0;
bits = bits ? bits : spi->bits_per_word;
if (!bits)
bits = spi->bits_per_word;
return bits;
}
@ -334,7 +373,8 @@ static unsigned long sh_msiof_spi_hz(struct spi_device *spi,
unsigned long hz;
hz = t ? t->speed_hz : 0;
hz = hz ? hz : spi->max_speed_hz;
if (!hz)
hz = spi->max_speed_hz;
return hz;
}
@ -468,9 +508,17 @@ static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
int bytes_done;
int words;
int n;
bool swab;
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 */
if (bits <= 8) {
bytes_per_word = 1;
@ -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;
else
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 {
bytes_per_word = 4;
if ((unsigned long)t->tx_buf & 0x03)

View file

@ -30,6 +30,7 @@
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/compat.h>
#include <linux/spi/spi.h>
#include <linux/spi/spidev.h>
@ -471,6 +472,16 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
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)
{
struct spidev_data *spidev;
@ -543,6 +554,7 @@ static const struct file_operations spidev_fops = {
.write = spidev_write,
.read = spidev_read,
.unlocked_ioctl = spidev_ioctl,
.compat_ioctl = spidev_compat_ioctl,
.open = spidev_open,
.release = spidev_release,
.llseek = no_llseek,

View file

@ -2,21 +2,24 @@
/* FIXME driver should be able to handle IRQs... */
struct mcp23s08_chip_info {
bool is_present; /* true iff populated */
u8 pullups; /* BIT(x) means enable pullup x */
bool is_present; /* true if populated */
unsigned pullups; /* BIT(x) means enable pullup x */
};
struct mcp23s08_platform_data {
/* Four slaves (numbered 0..3) can share one SPI chipselect, and
* will provide 8..32 GPIOs using 1..4 gpio_chip instances.
/* For mcp23s08, up to 4 slaves (numbered 0..3) can share one SPI
* 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
* not currently supported, and even if there are gaps in chip
* addressing the GPIO numbers are sequential .. so for example
* 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;

View file

@ -0,0 +1,20 @@
#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 */