diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-12-14 10:22:11 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-12-14 10:22:11 -0800 |
commit | 478e4e9d7a618379676b17e64583ff3622f2fec5 (patch) | |
tree | 1f25c96499abbb0d0b47f75aa92134209fcc4ee8 /drivers/spi | |
parent | 2205afa7d13ec716935dfd4b8ff71059ee7aeb0c (diff) | |
parent | 965346e3b99e2c5f51bd1325ddd0257227000355 (diff) |
Merge branch 'next-spi' of git://git.secretlab.ca/git/linux-2.6
* 'next-spi' of git://git.secretlab.ca/git/linux-2.6: (23 commits)
spi: fix probe/remove section markings
Add OMAP spi100k driver
spi-imx: don't access struct device directly but use dev_get_platdata
spi-imx: Add mx25 support
spi-imx: use positive logic to distinguish cpu variants
spi-imx: correct check for platform_get_irq failing
ARM: NUC900: Add spi driver support for nuc900
spi: SuperH MSIOF SPI Master driver V2
spi: fix spidev compilation failure when VERBOSE is defined
spi/au1550_spi: fix setupxfer not to override cfg with zeros
spi/mpc8xxx: don't use __exit_p to wrap plat_mpc8xxx_spi_remove
spi/i.MX: fix broken error handling for gpio_request
spi/i.mx: drain MXC SPI transfer buffer when probing device
MAINTAINERS: add SPI co-maintainer.
spi/xilinx_spi: fix incorrect casting
spi/mpc52xx-spi: minor cleanups
xilinx_spi: add a platform driver using the xilinx_spi common module.
xilinx_spi: add support for the DS570 IP.
xilinx_spi: Switch to iomem functions and support little endian.
xilinx_spi: Split into of driver and generic part.
...
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/Kconfig | 40 | ||||
-rw-r--r-- | drivers/spi/Makefile | 5 | ||||
-rw-r--r-- | drivers/spi/au1550_spi.c | 10 | ||||
-rw-r--r-- | drivers/spi/mpc52xx_spi.c | 86 | ||||
-rw-r--r-- | drivers/spi/omap_spi_100k.c | 635 | ||||
-rw-r--r-- | drivers/spi/spi_imx.c | 35 | ||||
-rw-r--r-- | drivers/spi/spi_mpc8xxx.c | 2 | ||||
-rw-r--r-- | drivers/spi/spi_nuc900.c | 504 | ||||
-rw-r--r-- | drivers/spi/spi_sh_msiof.c | 691 | ||||
-rw-r--r-- | drivers/spi/spidev.c | 6 | ||||
-rw-r--r-- | drivers/spi/xilinx_spi.c | 355 | ||||
-rw-r--r-- | drivers/spi/xilinx_spi.h | 32 | ||||
-rw-r--r-- | drivers/spi/xilinx_spi_of.c | 134 | ||||
-rw-r--r-- | drivers/spi/xilinx_spi_pltfm.c | 102 |
14 files changed, 2418 insertions, 219 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 28fce65b859..2d9d7035936 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -169,6 +169,12 @@ config SPI_OMAP24XX SPI master controller for OMAP24xx/OMAP34xx Multichannel SPI (McSPI) modules. +config SPI_OMAP_100K + tristate "OMAP SPI 100K" + depends on SPI_MASTER && (ARCH_OMAP850 || ARCH_OMAP730) + help + OMAP SPI 100K master controller for omap7xx boards. + config SPI_ORION tristate "Orion SPI master (EXPERIMENTAL)" depends on PLAT_ORION && EXPERIMENTAL @@ -220,6 +226,13 @@ config SPI_S3C24XX_GPIO the inbuilt hardware cannot provide the transfer mode, or where the board is using non hardware connected pins. +config SPI_SH_MSIOF + tristate "SuperH MSIOF SPI controller" + depends on SUPERH && HAVE_CLK + select SPI_BITBANG + help + SPI driver for SuperH MSIOF blocks. + config SPI_SH_SCI tristate "SuperH SCI SPI controller" depends on SUPERH @@ -240,15 +253,38 @@ config SPI_TXX9 SPI driver for Toshiba TXx9 MIPS SoCs config SPI_XILINX - tristate "Xilinx SPI controller" - depends on (XILINX_VIRTEX || MICROBLAZE) && EXPERIMENTAL + tristate "Xilinx SPI controller common module" + depends on HAS_IOMEM && EXPERIMENTAL select SPI_BITBANG + select SPI_XILINX_OF if (XILINX_VIRTEX || MICROBLAZE) help This exposes the SPI controller IP from the Xilinx EDK. See the "OPB Serial Peripheral Interface (SPI) (v1.00e)" Product Specification document (DS464) for hardware details. + Or for the DS570, see "XPS Serial Peripheral Interface (SPI) (v2.00b)" + +config SPI_XILINX_OF + tristate "Xilinx SPI controller OF device" + depends on SPI_XILINX && (XILINX_VIRTEX || MICROBLAZE) + help + This is the OF driver for the SPI controller IP from the Xilinx EDK. + +config SPI_XILINX_PLTFM + tristate "Xilinx SPI controller platform device" + depends on SPI_XILINX + help + This is the platform driver for the SPI controller IP + from the Xilinx EDK. + +config SPI_NUC900 + tristate "Nuvoton NUC900 series SPI" + depends on ARCH_W90X900 && EXPERIMENTAL + select SPI_BITBANG + help + SPI driver for Nuvoton NUC900 series ARM SoCs + # # Add new SPI master controllers in alphabetical order above this line # diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index e3f092a9afa..ed8c1675b52 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.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 obj-$(CONFIG_SPI_ORION) += orion_spi.o obj-$(CONFIG_SPI_PL022) += amba-pl022.o obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o @@ -32,8 +33,12 @@ obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o obj-$(CONFIG_SPI_TXX9) += spi_txx9.o obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o +obj-$(CONFIG_SPI_XILINX_OF) += xilinx_spi_of.o +obj-$(CONFIG_SPI_XILINX_PLTFM) += xilinx_spi_pltfm.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 +obj-$(CONFIG_SPI_NUC900) += spi_nuc900.o # ... add above this line ... # SPI protocol drivers (device/link on bus) diff --git a/drivers/spi/au1550_spi.c b/drivers/spi/au1550_spi.c index 76cbc1a6659..cfd5ff9508f 100644 --- a/drivers/spi/au1550_spi.c +++ b/drivers/spi/au1550_spi.c @@ -237,8 +237,14 @@ static int au1550_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t) unsigned bpw, hz; u32 cfg, stat; - bpw = t ? t->bits_per_word : spi->bits_per_word; - hz = t ? t->speed_hz : spi->max_speed_hz; + bpw = spi->bits_per_word; + hz = spi->max_speed_hz; + if (t) { + if (t->bits_per_word) + bpw = t->bits_per_word; + if (t->speed_hz) + hz = t->speed_hz; + } if (bpw < 4 || bpw > 24) { dev_err(&spi->dev, "setupxfer: invalid bits_per_word=%d\n", diff --git a/drivers/spi/mpc52xx_spi.c b/drivers/spi/mpc52xx_spi.c index ef8379b2c17..45bfe645817 100644 --- a/drivers/spi/mpc52xx_spi.c +++ b/drivers/spi/mpc52xx_spi.c @@ -18,9 +18,9 @@ #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/spi/spi.h> -#include <linux/spi/mpc52xx_spi.h> #include <linux/of_spi.h> #include <linux/io.h> +#include <linux/of_gpio.h> #include <asm/time.h> #include <asm/mpc52xx.h> @@ -53,7 +53,7 @@ MODULE_LICENSE("GPL"); /* FSM state return values */ #define FSM_STOP 0 /* Nothing more for the state machine to */ /* do. If something interesting happens */ - /* then and IRQ will be received */ + /* then an IRQ will be received */ #define FSM_POLL 1 /* need to poll for completion, an IRQ is */ /* not expected */ #define FSM_CONTINUE 2 /* Keep iterating the state machine */ @@ -61,13 +61,12 @@ MODULE_LICENSE("GPL"); /* Driver internal data */ struct mpc52xx_spi { struct spi_master *master; - u32 sysclk; void __iomem *regs; int irq0; /* MODF irq */ int irq1; /* SPIF irq */ - int ipb_freq; + unsigned int ipb_freq; - /* Statistics */ + /* Statistics; not used now, but will be reintroduced for debugfs */ int msg_count; int wcol_count; int wcol_ticks; @@ -79,7 +78,6 @@ struct mpc52xx_spi { spinlock_t lock; struct work_struct work; - /* Details of current transfer (length, and buffer pointers) */ struct spi_message *message; /* current message */ struct spi_transfer *transfer; /* current transfer */ @@ -89,6 +87,8 @@ struct mpc52xx_spi { u8 *rx_buf; const u8 *tx_buf; int cs_change; + int gpio_cs_count; + unsigned int *gpio_cs; }; /* @@ -96,7 +96,13 @@ struct mpc52xx_spi { */ static void mpc52xx_spi_chipsel(struct mpc52xx_spi *ms, int value) { - out_8(ms->regs + SPI_PORTDATA, value ? 0 : 0x08); + int cs; + + if (ms->gpio_cs_count > 0) { + cs = ms->message->spi->chip_select; + gpio_set_value(ms->gpio_cs[cs], value ? 0 : 1); + } else + out_8(ms->regs + SPI_PORTDATA, value ? 0 : 0x08); } /* @@ -221,7 +227,7 @@ static int mpc52xx_spi_fsmstate_transfer(int irq, struct mpc52xx_spi *ms, ms->wcol_tx_timestamp = get_tbl(); data = 0; if (ms->tx_buf) - data = *(ms->tx_buf-1); + data = *(ms->tx_buf - 1); out_8(ms->regs + SPI_DATA, data); /* try again */ return FSM_CONTINUE; } else if (status & SPI_STATUS_MODF) { @@ -390,7 +396,9 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op, struct spi_master *master; struct mpc52xx_spi *ms; void __iomem *regs; - int rc; + u8 ctrl1; + int rc, i = 0; + int gpio_cs; /* MMIO registers */ dev_dbg(&op->dev, "probing mpc5200 SPI device\n"); @@ -399,7 +407,8 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op, return -ENODEV; /* initialize the device */ - out_8(regs+SPI_CTRL1, SPI_CTRL1_SPIE | SPI_CTRL1_SPE | SPI_CTRL1_MSTR); + ctrl1 = SPI_CTRL1_SPIE | SPI_CTRL1_SPE | SPI_CTRL1_MSTR; + out_8(regs + SPI_CTRL1, ctrl1); out_8(regs + SPI_CTRL2, 0x0); out_8(regs + SPI_DATADIR, 0xe); /* Set output pins */ out_8(regs + SPI_PORTDATA, 0x8); /* Deassert /SS signal */ @@ -409,6 +418,8 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op, * on the SPI bus. This fault will also occur if the SPI signals * are not connected to any pins (port_config setting) */ in_8(regs + SPI_STATUS); + out_8(regs + SPI_CTRL1, ctrl1); + in_8(regs + SPI_DATA); if (in_8(regs + SPI_STATUS) & SPI_STATUS_MODF) { dev_err(&op->dev, "mode fault; is port_config correct?\n"); @@ -422,10 +433,12 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op, rc = -ENOMEM; goto err_alloc; } + master->bus_num = -1; - master->num_chipselect = 1; master->setup = mpc52xx_spi_setup; master->transfer = mpc52xx_spi_transfer; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; + dev_set_drvdata(&op->dev, master); ms = spi_master_get_devdata(master); @@ -435,16 +448,51 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op, ms->irq1 = irq_of_parse_and_map(op->node, 1); ms->state = mpc52xx_spi_fsmstate_idle; ms->ipb_freq = mpc5xxx_get_bus_frequency(op->node); + ms->gpio_cs_count = of_gpio_count(op->node); + if (ms->gpio_cs_count > 0) { + master->num_chipselect = ms->gpio_cs_count; + ms->gpio_cs = kmalloc(ms->gpio_cs_count * sizeof(unsigned int), + GFP_KERNEL); + if (!ms->gpio_cs) { + rc = -ENOMEM; + goto err_alloc; + } + + for (i = 0; i < ms->gpio_cs_count; i++) { + gpio_cs = of_get_gpio(op->node, i); + if (gpio_cs < 0) { + dev_err(&op->dev, + "could not parse the gpio field " + "in oftree\n"); + rc = -ENODEV; + goto err_gpio; + } + + rc = gpio_request(gpio_cs, dev_name(&op->dev)); + if (rc) { + dev_err(&op->dev, + "can't request spi cs gpio #%d " + "on gpio line %d\n", i, gpio_cs); + goto err_gpio; + } + + gpio_direction_output(gpio_cs, 1); + ms->gpio_cs[i] = gpio_cs; + } + } else { + master->num_chipselect = 1; + } + spin_lock_init(&ms->lock); INIT_LIST_HEAD(&ms->queue); INIT_WORK(&ms->work, mpc52xx_spi_wq); /* Decide if interrupts can be used */ if (ms->irq0 && ms->irq1) { - rc = request_irq(ms->irq0, mpc52xx_spi_irq, IRQF_SAMPLE_RANDOM, + rc = request_irq(ms->irq0, mpc52xx_spi_irq, 0, "mpc5200-spi-modf", ms); - rc |= request_irq(ms->irq1, mpc52xx_spi_irq, IRQF_SAMPLE_RANDOM, - "mpc5200-spi-spiF", ms); + rc |= request_irq(ms->irq1, mpc52xx_spi_irq, 0, + "mpc5200-spi-spif", ms); if (rc) { free_irq(ms->irq0, ms); free_irq(ms->irq1, ms); @@ -471,6 +519,11 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op, err_register: dev_err(&ms->master->dev, "initialization failed\n"); spi_master_put(master); + err_gpio: + while (i-- > 0) + gpio_free(ms->gpio_cs[i]); + + kfree(ms->gpio_cs); err_alloc: err_init: iounmap(regs); @@ -481,10 +534,15 @@ static int __devexit mpc52xx_spi_remove(struct of_device *op) { struct spi_master *master = dev_get_drvdata(&op->dev); struct mpc52xx_spi *ms = spi_master_get_devdata(master); + int i; free_irq(ms->irq0, ms); free_irq(ms->irq1, ms); + for (i = 0; i < ms->gpio_cs_count; i++) + gpio_free(ms->gpio_cs[i]); + + kfree(ms->gpio_cs); spi_unregister_master(master); spi_master_put(master); iounmap(ms->regs); diff --git a/drivers/spi/omap_spi_100k.c b/drivers/spi/omap_spi_100k.c new file mode 100644 index 00000000000..5355d90d1be --- /dev/null +++ b/drivers/spi/omap_spi_100k.c @@ -0,0 +1,635 @@ +/* + * OMAP7xx SPI 100k controller driver + * Author: Fabrice Crohas <fcrohas@gmail.com> + * from original omap1_mcspi driver + * + * Copyright (C) 2005, 2006 Nokia Corporation + * Author: Samuel Ortiz <samuel.ortiz@nokia.com> and + * Juha Yrj�l� <juha.yrjola@nokia.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/gpio.h> + +#include <linux/spi/spi.h> + +#include <plat/clock.h> + +#define OMAP1_SPI100K_MAX_FREQ 48000000 + +#define ICR_SPITAS (OMAP7XX_ICR_BASE + 0x12) + +#define SPI_SETUP1 0x00 +#define SPI_SETUP2 0x02 +#define SPI_CTRL 0x04 +#define SPI_STATUS 0x06 +#define SPI_TX_LSB 0x08 +#define SPI_TX_MSB 0x0a +#define SPI_RX_LSB 0x0c +#define SPI_RX_MSB 0x0e + +#define SPI_SETUP1_INT_READ_ENABLE (1UL << 5) +#define SPI_SETUP1_INT_WRITE_ENABLE (1UL << 4) +#define SPI_SETUP1_CLOCK_DIVISOR(x) ((x) << 1) +#define SPI_SETUP1_CLOCK_ENABLE (1UL << 0) + +#define SPI_SETUP2_ACTIVE_EDGE_FALLING (0UL << 0) +#define SPI_SETUP2_ACTIVE_EDGE_RISING (1UL << 0) +#define SPI_SETUP2_NEGATIVE_LEVEL (0UL << 5) +#define SPI_SETUP2_POSITIVE_LEVEL (1UL << 5) +#define SPI_SETUP2_LEVEL_TRIGGER (0UL << 10) +#define SPI_SETUP2_EDGE_TRIGGER (1UL << 10) + +#define SPI_CTRL_SEN(x) ((x) << 7) +#define SPI_CTRL_WORD_SIZE(x) (((x) - 1) << 2) +#define SPI_CTRL_WR (1UL << 1) +#define SPI_CTRL_RD (1UL << 0) + +#define SPI_STATUS_WE (1UL << 1) +#define SPI_STATUS_RD (1UL << 0) + +#define WRITE 0 +#define READ 1 + + +/* use PIO for small transfers, avoiding DMA setup/teardown overhead and + * cache operations; better heuristics consider wordsize and bitrate. + */ +#define DMA_MIN_BYTES 8 + +#define SPI_RUNNING 0 +#define SPI_SHUTDOWN 1 + +struct omap1_spi100k { + struct work_struct work; + + /* lock protects queue and registers */ + spinlock_t lock; + struct list_head msg_queue; + struct spi_master *master; + struct clk *ick; + struct clk *fck; + + /* Virtual base address of the controller */ + void __iomem *base; + + /* State of the SPI */ + unsigned int state; +}; + +struct omap1_spi100k_cs { + void __iomem *base; + int word_len; +}; + +static struct workqueue_struct *omap1_spi100k_wq; + +#define MOD_REG_BIT(val, mask, set) do { \ + if (set) \ + val |= mask; \ + else \ + val &= ~mask; \ +} while (0) + +static void spi100k_enable_clock(struct spi_master *master) +{ + unsigned int val; + struct omap1_spi100k *spi100k = spi_master_get_devdata(master); + + /* enable SPI */ + val = readw(spi100k->base + SPI_SETUP1); + val |= SPI_SETUP1_CLOCK_ENABLE; + writew(val, spi100k->base + SPI_SETUP1); +} + +static void spi100k_disable_clock(struct spi_master *master) +{ + unsigned int val; + struct omap1_spi100k *spi100k = spi_master_get_devdata(master); + + /* disable SPI */ + val = readw(spi100k->base + SPI_SETUP1); + val &= ~SPI_SETUP1_CLOCK_ENABLE; + writew(val, spi100k->base + SPI_SETUP1); +} + +static void spi100k_write_data(struct spi_master *master, int len, int data) +{ + struct omap1_spi100k *spi100k = spi_master_get_devdata(master); + + /* write 16-bit word */ + spi100k_enable_clock(master); + writew( data , spi100k->base + SPI_TX_MSB); + + writew(SPI_CTRL_SEN(0) | + SPI_CTRL_WORD_SIZE(len) | + SPI_CTRL_WR, + spi100k->base + SPI_CTRL); + + /* Wait for bit ack send change */ + while((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_WE) != SPI_STATUS_WE); + udelay(1000); + + spi100k_disable_clock(master); +} + +static int spi100k_read_data(struct spi_master *master, int len) +{ + int dataH,dataL; + struct omap1_spi100k *spi100k = spi_master_get_devdata(master); + + spi100k_enable_clock(master); + writew(SPI_CTRL_SEN(0) | + SPI_CTRL_WORD_SIZE(len) | + SPI_CTRL_RD, + spi100k->base + SPI_CTRL); + + while((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_RD) != SPI_STATUS_RD); + udelay(1000); + + dataL = readw(spi100k->base + SPI_RX_LSB); + dataH = readw(spi100k->base + SPI_RX_MSB); + spi100k_disable_clock(master); + + return dataL; +} + +static void spi100k_open(struct spi_master *master) +{ + /* get control of SPI */ + struct omap1_spi100k *spi100k = spi_master_get_devdata(master); + + writew(SPI_SETUP1_INT_READ_ENABLE | + SPI_SETUP1_INT_WRITE_ENABLE | + SPI_SETUP1_CLOCK_DIVISOR(0), spi100k->base + SPI_SETUP1); + + /* configure clock and interrupts */ + writew(SPI_SETUP2_ACTIVE_EDGE_FALLING | + SPI_SETUP2_NEGATIVE_LEVEL | + SPI_SETUP2_LEVEL_TRIGGER, spi100k->base + SPI_SETUP2); +} + +static void omap1_spi100k_force_cs(struct omap1_spi100k *spi100k, int enable) +{ + if (enable) + writew(0x05fc, spi100k->base + SPI_CTRL); + else + writew(0x05fd, spi100k->base + SPI_CTRL); +} + +static unsigned +omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) +{ + struct omap1_spi100k *spi100k; + struct omap1_spi100k_cs *cs = spi->controller_state; + unsigned int count, c; + int word_len; + + spi100k = spi_master_get_devdata(spi->master); + count = xfer->len; + c = count; + word_len = cs->word_len; + + /* RX_ONLY mode needs dummy data in TX reg */ + if (xfer->tx_buf == NULL) + spi100k_write_data(spi->master,word_len, 0); + + if (word_len <= 8) { + u8 *rx; + const u8 *tx; + + rx = xfer->rx_buf; + tx = xfer->tx_buf; + do { + c-=1; + if (xfer->tx_buf != NULL) + spi100k_write_data(spi->master,word_len, *tx); + if (xfer->rx_buf != NULL) + *rx = spi100k_read_data(spi->master,word_len); + } while(c); + } else if (word_len <= 16) { + u16 *rx; + const u16 *tx; + + rx = xfer->rx_buf; + tx = xfer->tx_buf; + do { + c-=2; + if (xfer->tx_buf != NULL) + spi100k_write_data(spi->master,word_len, *tx++); + if (xfer->rx_buf != NULL) + *rx++ = spi100k_read_data(spi->master,word_len); + } while(c); + } else if (word_len <= 32) { + u32 *rx; + const u32 *tx; + + rx = xfer->rx_buf; + tx = xfer->tx_buf; + do { + c-=4; + if (xfer->tx_buf != NULL) + spi100k_write_data(spi->master,word_len, *tx); + if (xfer->rx_buf != NULL) + *rx = spi100k_read_data(spi->master,word_len); + } while(c); + } + return count - c; +} + +/* called only when no transfer is active to this device */ +static int omap1_spi100k_setup_transfer(struct spi_device *spi, + struct spi_transfer *t) +{ + struct omap1_spi100k *spi100k = spi_master_get_devdata(spi->master); + struct omap1_spi100k_cs *cs = spi->controller_state; + u8 word_len = spi->bits_per_word; + + if (t != NULL && t->bits_per_word) + word_len = t->bits_per_word; + if (!word_len) + word_len = 8; + + if (spi->bits_per_word > 32) + return -EINVAL; + cs->word_len = word_len; + + /* SPI init before transfer */ + writew(0x3e , spi100k->base + SPI_SETUP1); + writew(0x00 , spi100k->base + SPI_STATUS); + writew(0x3e , spi100k->base + SPI_CTRL); + + return 0; +} + +/* the spi->mode bits understood by this driver: */ +#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH) + +static int omap1_spi100k_setup(struct spi_device *spi) +{ + int ret; + struct omap1_spi100k *spi100k; + struct omap1_spi100k_cs *cs = spi->controller_state; + + if (spi->bits_per_word < 4 || spi->bits_per_word > 32) { + dev_dbg(&spi->dev, "setup: unsupported %d bit words\n", + spi->bits_per_word); + return -EINVAL; + } + + spi100k = spi_master_get_devdata(spi->master); + + if (!cs) { + cs = kzalloc(sizeof *cs, GFP_KERNEL); + if (!cs) + return -ENOMEM; + cs->base = spi100k->base + spi->chip_select * 0x14; + spi->controller_state = cs; + } + + spi100k_open(spi->master); + + clk_enable(spi100k->ick); + clk_enable(spi100k->fck); + + ret = omap1_spi100k_setup_transfer(spi, NULL); + + clk_disable(spi100k->ick); + clk_disable(spi100k->fck); + + return ret; +} + +static void omap1_spi100k_work(struct work_struct *work) +{ + struct omap1_spi100k *spi100k; + int status = 0; + + spi100k = container_of(work, struct omap1_spi100k, work); + spin_lock_irq(&spi100k->lock); + + clk_enable(spi100k->ick); + clk_enable(spi100k->fck); + + /* We only enable one channel at a time -- the one whose message is + * at the head of the queue -- although this controller would gladly + * arbitrate among multiple channels. This corresponds to "single + * channel" master mode. As a side effect, we need to manage the + * chipselect with the FORCE bit ... CS != channel enable. + */ + while (!list_empty(&spi100k->msg_queue)) { + struct spi_message *m; + struct spi_device *spi; + struct spi_transfer *t = NULL; + int cs_active = 0; + struct omap1_spi100k_cs *cs; + int par_override = 0; + + m = container_of(spi100k->msg_queue.next, struct spi_message, + queue); + + list_del_init(&m->queue); + spin_unlock_irq(&spi100k->lock); + + spi = m->spi; + cs = spi->controller_state; + + list_for_each_entry(t, &m->transfers, transfer_list) { + if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) { + status = -EINVAL; + break; + } + if (par_override || t->speed_hz || t->bits_per_word) { + par_override = 1; + status = omap1_spi100k_setup_transfer(spi, t); + if (status < 0) + break; + if (!t->speed_hz && !t->bits_per_word) + par_override = 0; + } + + if (!cs_active) { + omap1_spi100k_force_cs(spi100k, 1); + cs_active = 1; + } + + if (t->len) { + unsigned count; + + /* RX_ONLY mode needs dummy data in TX reg */ + if (t->tx_buf == NULL) + spi100k_write_data(spi->master, 8, 0); + + count = omap1_spi100k_txrx_pio(spi, t); + m->actual_length += count; + + if (count != t->len) { + status = -EIO; + break; + } + } + + if (t->delay_usecs) + udelay(t->delay_usecs); + + /* ignore the "leave it on after last xfer" hint */ + + if (t->cs_change) { + omap1_spi100k_force_cs(spi100k, 0); + cs_active = 0; + } + } + + /* Restore defaults if they were overriden */ + if (par_override) { + par_override = 0; + status = omap1_spi100k_setup_transfer(spi, NULL); + } + + if (cs_active) + omap1_spi100k_force_cs(spi100k, 0); + + m->status = status; + m->complete(m->context); + + spin_lock_irq(&spi100k->lock); + } + + clk_disable(spi100k->ick); + clk_disable(spi100k->fck); + spin_unlock_irq(&spi100k->lock); + + if (status < 0) + printk(KERN_WARNING "spi transfer failed with %d\n", status); +} + +static int omap1_spi100k_transfer(struct spi_device *spi, struct spi_message *m) +{ + struct omap1_spi100k *spi100k; + unsigned long flags; + struct spi_transfer *t; + + m->actual_length = 0; + m->status = -EINPROGRESS; + + spi100k = spi_master_get_devdata(spi->master); + + /* Don't accept new work if we're shutting down */ + if (spi100k->state == SPI_SHUTDOWN) + return -ESHUTDOWN; + + /* reject invalid messages and transfers */ + if (list_empty(&m->transfers) || !m->complete) + return -EINVAL; + + list_for_each_entry(t, &m->transfers, transfer_list) { + const void *tx_buf = t->tx_buf; + void *rx_buf = t->rx_buf; + unsigned len = t->len; + + if (t->speed_hz > OMAP1_SPI100K_MAX_FREQ + || (len && !(rx_buf || tx_buf)) + || (t->bits_per_word && + ( t->bits_per_word < 4 + || t->bits_per_word > 32))) { + dev_dbg(&spi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n", + t->speed_hz, + len, + tx_buf ? "tx" : "", + rx_buf ? "rx" : "", + t->bits_per_word); + return -EINVAL; + } + + if (t->speed_hz && t->speed_hz < OMAP1_SPI100K_MAX_FREQ/(1<<16)) { + dev_dbg(&spi->dev, "%d Hz max exceeds %d\n", + t->speed_hz, + OMAP1_SPI100K_MAX_FREQ/(1<<16)); + return -EINVAL; + } + + } + + spin_lock_irqsave(&spi100k->lock, flags); + list_add_tail(&m->queue, &spi100k->msg_queue); + queue_work(omap1_spi100k_wq, &spi100k->work); + spin_unlock_irqrestore(&spi100k->lock, flags); + + return 0; +} + +static int __init omap1_spi100k_reset(struct omap1_spi100k *spi100k) +{ + return 0; +} + +static int __devinit omap1_spi100k_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct omap1_spi100k *spi100k; + int status = 0; + + if (!pdev->id) + return -EINVAL; + + master = spi_alloc_master(&pdev->dev, sizeof *spi100k); + if (master == NULL) { + dev_dbg(&pdev->dev, "master allocation failed\n"); + return -ENOMEM; + } + + if (pdev->id != -1) + master->bus_num = pdev->id; + + master->setup = omap1_spi100k_setup; + master->transfer = omap1_spi100k_transfer; + master->cleanup = NULL; + master->num_chipselect = 2; + master->mode_bits = MODEBITS; + + dev_set_drvdata(&pdev->dev, master); + + spi100k = spi_master_get_devdata(master); + spi100k->master = master; + + /* + * The memory region base address is taken as the platform_data. + * You should allocate this with ioremap() before initializing + * the SPI. + */ + spi100k->base = (void __iomem *) pdev->dev.platform_data; + + INIT_WORK(&spi100k->work, omap1_spi100k_work); + + spin_lock_init(&spi100k->lock); + INIT_LIST_HEAD(&spi100k->msg_queue); + spi100k->ick = clk_get(&pdev->dev, "ick"); + if (IS_ERR(spi100k->ick)) { + dev_dbg(&pdev->dev, "can't get spi100k_ick\n"); + status = PTR_ERR(spi100k->ick); + goto err1; + } + + spi100k->fck = clk_get(&pdev->dev, "fck"); + if (IS_ERR(spi100k->fck)) { + dev_dbg(&pdev->dev, "can't get spi100k_fck\n"); + status = PTR_ERR(spi100k->fck); + goto err2; + } + + if (omap1_spi100k_reset(spi100k) < 0) + goto err3; + + status = spi_register_master(master); + if (status < 0) + goto err3; + + spi100k->state = SPI_RUNNING; + + return status; + +err3: + clk_put(spi100k->fck); +err2: + clk_put(spi100k->ick); +err1: + spi_master_put(master); + return status; +} + +static int __exit omap1_spi100k_remove(struct platform_device *pdev) +{ + struct spi_master *master; + struct omap1_spi100k *spi100k; + struct resource *r; + unsigned limit = 500; + unsigned long flags; + int status = 0; + + master = dev_get_drvdata(&pdev->dev); + spi100k = spi_master_get_devdata(master); + + spin_lock_irqsave(&spi100k->lock, flags); + + spi100k->state = SPI_SHUTDOWN; + while (!list_empty(&spi100k->msg_queue) && limit--) { + spin_unlock_irqrestore(&spi100k->lock, flags); + msleep(10); + spin_lock_irqsave(&spi100k->lock, flags); + } + + if (!list_empty(&spi100k->msg_queue)) + status = -EBUSY; + + spin_unlock_irqrestore(&spi100k->lock, flags); + + if (status != 0) + return status; + + clk_put(spi100k->fck); + clk_put(spi100k->ick); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + spi_unregister_master(master); + + return 0; +} + +static struct platform_driver omap1_spi100k_driver = { + .driver = { + .name = "omap1_spi100k", + .owner = THIS_MODULE, + }, + .remove = __exit_p(omap1_spi100k_remove), +}; + + +static int __init omap1_spi100k_init(void) +{ + omap1_spi100k_wq = create_singlethread_workqueue( + omap1_spi100k_driver.driver.name); + + if (omap1_spi100k_wq == NULL) + return -1; + + return platform_driver_probe(&omap1_spi100k_driver, omap1_spi100k_probe); +} + +static void __exit omap1_spi100k_exit(void) +{ + platform_driver_unregister(&omap1_spi100k_driver); + + destroy_workqueue(omap1_spi100k_wq); +} + +module_init(omap1_spi100k_init); +module_exit(omap1_spi100k_exit); + +MODULE_DESCRIPTION("OMAP7xx SPI 100k controller driver"); +MODULE_AUTHOR("Fabrice Crohas <fcrohas@gmail.com>"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c index 89c22efedfb..1893f1e96dc 100644 --- a/drivers/spi/spi_imx.c +++ b/drivers/spi/spi_imx.c @@ -44,6 +44,9 @@ #define MXC_CSPIINT 0x0c #define MXC_RESET 0x1c +#define MX3_CSPISTAT 0x14 +#define MX3_CSPISTAT_RR (1 << 3) + /* generic defines to abstract from the different register layouts */ #define MXC_INT_RR (1 << 0) /* Receive data ready interrupt */ #define MXC_INT_TE (1 << 1) /* Transmit FIFO empty interrupt */ @@ -205,7 +208,7 @@ static int mx31_config(struct spi_imx_data *spi_imx, if (cpu_is_mx31()) reg |= (config->bpw - 1) << MX31_CSPICTRL_BC_SHIFT; - else if (cpu_is_mx35()) { + else if (cpu_is_mx25() || cpu_is_mx35()) { reg |= (config->bpw - 1) << MX35_CSPICTRL_BL_SHIFT; reg |= MX31_CSPICTRL_SSCTL; } @@ -219,7 +222,7 @@ static int mx31_config(struct spi_imx_data *spi_imx, if (config->cs < 0) { if (cpu_is_mx31()) |