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:
commit
b061c59c27
35 changed files with 1921 additions and 262 deletions
|
@ -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>;
|
||||
};
|
||||
|
|
4
Documentation/devicetree/bindings/spi/spi_altera.txt
Normal file
4
Documentation/devicetree/bindings/spi/spi_altera.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
Altera SPI
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "ALTR,spi-1.0".
|
12
Documentation/devicetree/bindings/spi/spi_oc_tiny.txt
Normal file
12
Documentation/devicetree/bindings/spi/spi_oc_tiny.txt
Normal 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.
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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, ¶m);
|
||||
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, ¶m);
|
||||
|
||||
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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
339
drivers/spi/spi_altera.c
Normal 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);
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
425
drivers/spi/spi_oc_tiny.c
Normal 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
543
drivers/spi/spi_sh.c
Normal 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");
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
20
include/linux/spi/spi_oc_tiny.h
Normal file
20
include/linux/spi/spi_oc_tiny.h
Normal 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 */
|
Loading…
Reference in a new issue