diff options
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/Kconfig | 38 | ||||
-rw-r--r-- | drivers/spi/Makefile | 3 | ||||
-rw-r--r-- | drivers/spi/at25.c | 381 | ||||
-rw-r--r-- | drivers/spi/omap_uwire.c | 572 | ||||
-rw-r--r-- | drivers/spi/pxa2xx_spi.c | 4 | ||||
-rw-r--r-- | drivers/spi/spi.c | 2 | ||||
-rw-r--r-- | drivers/spi/spi_bitbang.c | 11 | ||||
-rw-r--r-- | drivers/spi/spi_imx.c | 1769 |
8 files changed, 2765 insertions, 15 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index d895a1a..9052f4c 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -75,6 +75,13 @@ config SPI_BUTTERFLY inexpensive battery powered microcontroller evaluation board. This same cable can be used to flash new firmware. +config SPI_IMX + tristate "Freescale iMX SPI controller" + depends on SPI_MASTER && ARCH_IMX && EXPERIMENTAL + help + This enables using the Freescale iMX SPI controller in master + mode. + config SPI_MPC83xx tristate "Freescale MPC83xx SPI controller" depends on SPI_MASTER && PPC_83xx && EXPERIMENTAL @@ -87,6 +94,14 @@ config SPI_MPC83xx family of PowerPC processors. The MPC83xx uses a simple set of shift registers for data (opposed to the CPM based descriptor model). +config SPI_OMAP_UWIRE + tristate "OMAP1 MicroWire" + depends on SPI_MASTER && ARCH_OMAP1 + select SPI_BITBANG + help + This hooks up to the MicroWire controller on OMAP1 chips. + + config SPI_PXA2XX tristate "PXA2xx SSP SPI master" depends on SPI_MASTER && ARCH_PXA && EXPERIMENTAL @@ -95,6 +110,12 @@ config SPI_PXA2XX The driver can be configured to use any SSP port and additional documentation can be found a Documentation/spi/pxa2xx. +config SPI_S3C24XX + tristate "Samsung S3C24XX series SPI" + depends on SPI_MASTER && ARCH_S3C2410 && EXPERIMENTAL + help + SPI driver for Samsung S3C24XX series ARM SoCs + config SPI_S3C24XX_GPIO tristate "Samsung S3C24XX series SPI by GPIO" depends on SPI_MASTER && ARCH_S3C2410 && SPI_BITBANG && EXPERIMENTAL @@ -107,13 +128,6 @@ config SPI_S3C24XX_GPIO # Add new SPI master controllers in alphabetical order above this line # - -config SPI_S3C24XX - tristate "Samsung S3C24XX series SPI" - depends on SPI_MASTER && ARCH_S3C2410 && EXPERIMENTAL - help - SPI driver for Samsung S3C24XX series ARM SoCs - # # There are lots of SPI device types, with sensors and memory # being probably the most widely used ones. @@ -121,6 +135,16 @@ config SPI_S3C24XX comment "SPI Protocol Masters" depends on SPI_MASTER +config SPI_AT25 + tristate "SPI EEPROMs from most vendors" + depends on SPI_MASTER && SYSFS + help + Enable this driver to get read/write support to most SPI EEPROMs, + after you configure the board init code to know about each eeprom + on your target board. + + This driver can also be built as a module. If so, the module + will be called at25. # # Add new SPI protocol masters in alphabetical order above this line diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 8f4cb67..bf271fe 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -13,13 +13,16 @@ obj-$(CONFIG_SPI_MASTER) += spi.o # SPI master controller drivers (bus) obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o +obj-$(CONFIG_SPI_IMX) += spi_imx.o obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o +obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o obj-$(CONFIG_SPI_MPC83xx) += spi_mpc83xx.o obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o # ... add above this line ... # SPI protocol drivers (device/link on bus) +obj-$(CONFIG_SPI_AT25) += at25.o # ... add above this line ... # SPI slave controller drivers (upstream link) diff --git a/drivers/spi/at25.c b/drivers/spi/at25.c new file mode 100644 index 0000000..48e4f48 --- /dev/null +++ b/drivers/spi/at25.c @@ -0,0 +1,381 @@ +/* + * at25.c -- support most SPI EEPROMs, such as Atmel AT25 models + * + * Copyright (C) 2006 David Brownell + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/sched.h> + +#include <linux/spi/spi.h> +#include <linux/spi/eeprom.h> + + +struct at25_data { + struct spi_device *spi; + struct mutex lock; + struct spi_eeprom chip; + struct bin_attribute bin; + unsigned addrlen; +}; + +#define AT25_WREN 0x06 /* latch the write enable */ +#define AT25_WRDI 0x04 /* reset the write enable */ +#define AT25_RDSR 0x05 /* read status register */ +#define AT25_WRSR 0x01 /* write status register */ +#define AT25_READ 0x03 /* read byte(s) */ +#define AT25_WRITE 0x02 /* write byte(s)/sector */ + +#define AT25_SR_nRDY 0x01 /* nRDY = write-in-progress */ +#define AT25_SR_WEN 0x02 /* write enable (latched) */ +#define AT25_SR_BP0 0x04 /* BP for software writeprotect */ +#define AT25_SR_BP1 0x08 +#define AT25_SR_WPEN 0x80 /* writeprotect enable */ + + +#define EE_MAXADDRLEN 3 /* 24 bit addresses, up to 2 MBytes */ + +/* Specs often allow 5 msec for a page write, sometimes 20 msec; + * it's important to recover from write timeouts. + */ +#define EE_TIMEOUT 25 + +/*-------------------------------------------------------------------------*/ + +#define io_limit PAGE_SIZE /* bytes */ + +static ssize_t +at25_ee_read( + struct at25_data *at25, + char *buf, + unsigned offset, + size_t count +) +{ + u8 command[EE_MAXADDRLEN + 1]; + u8 *cp; + ssize_t status; + struct spi_transfer t[2]; + struct spi_message m; + + cp = command; + *cp++ = AT25_READ; + + /* 8/16/24-bit address is written MSB first */ + switch (at25->addrlen) { + default: /* case 3 */ + *cp++ = offset >> 16; + case 2: + *cp++ = offset >> 8; + case 1: + case 0: /* can't happen: for better codegen */ + *cp++ = offset >> 0; + } + + spi_message_init(&m); + memset(t, 0, sizeof t); + + t[0].tx_buf = command; + t[0].len = at25->addrlen + 1; + spi_message_add_tail(&t[0], &m); + + t[1].rx_buf = buf; + t[1].len = count; + spi_message_add_tail(&t[1], &m); + + mutex_lock(&at25->lock); + + /* Read it all at once. + * + * REVISIT that's potentially a problem with large chips, if + * other devices on the bus need to be accessed regularly or + * this chip is clocked very slowly + */ + status = spi_sync(at25->spi, &m); + dev_dbg(&at25->spi->dev, + "read %Zd bytes at %d --> %d\n", + count, offset, (int) status); + + mutex_unlock(&at25->lock); + return status ? status : count; +} + +static ssize_t +at25_bin_read(struct kobject *kobj, char *buf, loff_t off, size_t count) +{ + struct device *dev; + struct at25_data *at25; + + dev = container_of(kobj, struct device, kobj); + at25 = dev_get_drvdata(dev); + + if (unlikely(off >= at25->bin.size)) + return 0; + if ((off + count) > at25->bin.size) + count = at25->bin.size - off; + if (unlikely(!count)) + return count; + + return at25_ee_read(at25, buf, off, count); +} + + +static ssize_t +at25_ee_write(struct at25_data *at25, char *buf, loff_t off, size_t count) +{ + ssize_t status = 0; + unsigned written = 0; + unsigned buf_size; + u8 *bounce; + + /* Temp buffer starts with command and address */ + buf_size = at25->chip.page_size; + if (buf_size > io_limit) + buf_size = io_limit; + bounce = kmalloc(buf_size + at25->addrlen + 1, GFP_KERNEL); + if (!bounce) + return -ENOMEM; + + /* For write, rollover is within the page ... so we write at + * most one page, then manually roll over to the next page. + */ + bounce[0] = AT25_WRITE; + mutex_lock(&at25->lock); + do { + unsigned long timeout, retries; + unsigned segment; + unsigned offset = (unsigned) off; + u8 *cp = bounce + 1; + + *cp = AT25_WREN; + status = spi_write(at25->spi, cp, 1); + if (status < 0) { + dev_dbg(&at25->spi->dev, "WREN --> %d\n", + (int) status); + break; + } + + /* 8/16/24-bit address is written MSB first */ + switch (at25->addrlen) { + default: /* case 3 */ + *cp++ = offset >> 16; + case 2: + *cp++ = offset >> 8; + case 1: + case 0: /* can't happen: for better codegen */ + *cp++ = offset >> 0; + } + + /* Write as much of a page as we can */ + segment = buf_size - (offset % buf_size); + if (segment > count) + segment = count; + memcpy(cp, buf, segment); + status = spi_write(at25->spi, bounce, + segment + at25->addrlen + 1); + dev_dbg(&at25->spi->dev, + "write %u bytes at %u --> %d\n", + segment, offset, (int) status); + if (status < 0) + break; + + /* REVISIT this should detect (or prevent) failed writes + * to readonly sections of the EEPROM... + */ + + /* Wait for non-busy status */ + timeout = jiffies + msecs_to_jiffies(EE_TIMEOUT); + retries = 0; + do { + int sr; + + sr = spi_w8r8(at25->spi, AT25_RDSR); + if (sr < 0 || (sr & AT25_SR_nRDY)) { + dev_dbg(&at25->spi->dev, + "rdsr --> %d (%02x)\n", sr, sr); + /* at HZ=100, this is sloooow */ + msleep(1); + continue; + } + if (!(sr & AT25_SR_nRDY)) + break; + } while (retries++ < 3 || time_before_eq(jiffies, timeout)); + + if (time_after(jiffies, timeout)) { + dev_err(&at25->spi->dev, + "write %d bytes offset %d, " + "timeout after %u msecs\n", + segment, offset, + jiffies_to_msecs(jiffies - + (timeout - EE_TIMEOUT))); + status = -ETIMEDOUT; + break; + } + + off += segment; + buf += segment; + count -= segment; + written += segment; + + } while (count > 0); + + mutex_unlock(&at25->lock); + + kfree(bounce); + return written ? written : status; +} + +static ssize_t +at25_bin_write(struct kobject *kobj, char *buf, loff_t off, size_t count) +{ + struct device *dev; + struct at25_data *at25; + + dev = container_of(kobj, struct device, kobj); + at25 = dev_get_drvdata(dev); + + if (unlikely(off >= at25->bin.size)) + return -EFBIG; + if ((off + count) > at25->bin.size) + count = at25->bin.size - off; + if (unlikely(!count)) + return count; + + return at25_ee_write(at25, buf, off, count); +} + +/*-------------------------------------------------------------------------*/ + +static int at25_probe(struct spi_device *spi) +{ + struct at25_data *at25 = NULL; + const struct spi_eeprom *chip; + int err; + int sr; + int addrlen; + + /* Chip description */ + chip = spi->dev.platform_data; + if (!chip) { + dev_dbg(&spi->dev, "no chip description\n"); + err = -ENODEV; + goto fail; + } + + /* For now we only support 8/16/24 bit addressing */ + if (chip->flags & EE_ADDR1) + addrlen = 1; + else if (chip->flags & EE_ADDR2) + addrlen = 2; + else if (chip->flags & EE_ADDR3) + addrlen = 3; + else { + dev_dbg(&spi->dev, "unsupported address type\n"); + err = -EINVAL; + goto fail; + } + + /* Ping the chip ... the status register is pretty portable, + * unlike probing manufacturer IDs. We do expect that system + * firmware didn't write it in the past few milliseconds! + */ + sr = spi_w8r8(spi, AT25_RDSR); + if (sr < 0 || sr & AT25_SR_nRDY) { + dev_dbg(&at25->spi->dev, "rdsr --> %d (%02x)\n", sr, sr); + err = -ENXIO; + goto fail; + } + + if (!(at25 = kzalloc(sizeof *at25, GFP_KERNEL))) { + err = -ENOMEM; + goto fail; + } + + mutex_init(&at25->lock); + at25->chip = *chip; + at25->spi = spi_dev_get(spi); + dev_set_drvdata(&spi->dev, at25); + at25->addrlen = addrlen; + + /* Export the EEPROM bytes through sysfs, since that's convenient. + * Default to root-only access to the data; EEPROMs often hold data + * that's sensitive for read and/or write, like ethernet addresses, + * security codes, board-specific manufacturing calibrations, etc. + */ + at25->bin.attr.name = "eeprom"; + at25->bin.attr.mode = S_IRUSR; + at25->bin.attr.owner = THIS_MODULE; + at25->bin.read = at25_bin_read; + + at25->bin.size = at25->chip.byte_len; + if (!(chip->flags & EE_READONLY)) { + at25->bin.write = at25_bin_write; + at25->bin.attr.mode |= S_IWUSR; + } + + err = sysfs_create_bin_file(&spi->dev.kobj, &at25->bin); + if (err) + goto fail; + + dev_info(&spi->dev, "%Zd %s %s eeprom%s, pagesize %u\n", + (at25->bin.size < 1024) + ? at25->bin.size + : (at25->bin.size / 1024), + (at25->bin.size < 1024) ? "Byte" : "KByte", + at25->chip.name, + (chip->flags & EE_READONLY) ? " (readonly)" : "", + at25->chip.page_size); + return 0; +fail: + dev_dbg(&spi->dev, "probe err %d\n", err); + kfree(at25); + return err; +} + +static int __devexit at25_remove(struct spi_device *spi) +{ + struct at25_data *at25; + + at25 = dev_get_drvdata(&spi->dev); + sysfs_remove_bin_file(&spi->dev.kobj, &at25->bin); + kfree(at25); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static struct spi_driver at25_driver = { + .driver = { + .name = "at25", + .owner = THIS_MODULE, + }, + .probe = at25_probe, + .remove = __devexit_p(at25_remove), +}; + +static int __init at25_init(void) +{ + return spi_register_driver(&at25_driver); +} +module_init(at25_init); + +static void __exit at25_exit(void) +{ + spi_unregister_driver(&at25_driver); +} +module_exit(at25_exit); + +MODULE_DESCRIPTION("Driver for most SPI EEPROMs"); +MODULE_AUTHOR("David Brownell"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/spi/omap_uwire.c b/drivers/spi/omap_uwire.c new file mode 100644 index 0000000..366af49 --- /dev/null +++ b/drivers/spi/omap_uwire.c @@ -0,0 +1,572 @@ +/* + * omap_uwire.c -- MicroWire interface driver for OMAP + * + * Copyright 2003 MontaVista Software Inc. <source@mvista.com> + * + * Ported to 2.6 OMAP uwire interface. + * Copyright (C) 2004 Texas Instruments. + * + * Generalization patches by Juha Yrjola <juha.yrjola@nokia.com> + * + * Copyright (C) 2005 David Brownell (ported to 2.6 SPI interface) + * Copyright (C) 2006 Nokia + * + * Many updates by Imre Deak <imre.deak@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 SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/workqueue.h> +#include <linux/interrupt.h> +#include <linux/err.h> +#include <linux/clk.h> + +#include <linux/spi/spi.h> +#include <linux/spi/spi_bitbang.h> + +#include <asm/system.h> +#include <asm/irq.h> +#include <asm/hardware.h> +#include <asm/io.h> +#include <asm/mach-types.h> + +#include <asm/arch/mux.h> +#include <asm/arch/omap730.h> /* OMAP730_IO_CONF registers */ + + +/* FIXME address is now a platform device resource, + * and irqs should show there too... + */ +#define UWIRE_BASE_PHYS 0xFFFB3000 +#define UWIRE_BASE ((void *__iomem)IO_ADDRESS(UWIRE_BASE_PHYS)) + +/* uWire Registers: */ +#define UWIRE_IO_SIZE 0x20 +#define UWIRE_TDR 0x00 +#define UWIRE_RDR 0x00 +#define UWIRE_CSR 0x01 +#define UWIRE_SR1 0x02 +#define UWIRE_SR2 0x03 +#define UWIRE_SR3 0x04 +#define UWIRE_SR4 0x05 +#define UWIRE_SR5 0x06 + +/* CSR bits */ +#define RDRB (1 << 15) +#define CSRB (1 << 14) +#define START (1 << 13) +#define CS_CMD (1 << 12) + +/* SR1 or SR2 bits */ +#define UWIRE_READ_FALLING_EDGE 0x0001 +#define UWIRE_READ_RISING_EDGE 0x0000 +#define UWIRE_WRITE_FALLING_EDGE 0x0000 +#define UWIRE_WRITE_RISING_EDGE 0x0002 +#define UWIRE_CS_ACTIVE_LOW 0x0000 +#define UWIRE_CS_ACTIVE_HIGH 0x0004 +#define UWIRE_FREQ_DIV_2 0x0000 +#define UWIRE_FREQ_DIV_4 0x0008 +#define UWIRE_FREQ_DIV_8 0x0010 +#define UWIRE_CHK_READY 0x0020 +#define UWIRE_CLK_INVERTED 0x0040 + + +struct uwire_spi { + struct spi_bitbang bitbang; + struct clk *ck; +}; + +struct uwire_state { + unsigned bits_per_word; + unsigned div1_idx; +}; + +/* REVISIT compile time constant for idx_shift? */ +static unsigned int uwire_idx_shift; + +static inline void uwire_write_reg(int idx, u16 val) +{ + __raw_writew(val, UWIRE_BASE + (idx << uwire_idx_shift)); +} + +static inline u16 uwire_read_reg(int idx) +{ + return __raw_readw(UWIRE_BASE + (idx << uwire_idx_shift)); +} + +static inline void omap_uwire_configure_mode(u8 cs, unsigned long flags) +{ + u16 w, val = 0; + int shift, reg; + + if (flags & UWIRE_CLK_INVERTED) + val ^= 0x03; + val = flags & 0x3f; + if (cs & 1) + shift = 6; + else + shift = 0; + if (cs <= 1) + reg = UWIRE_SR1; + else + reg = UWIRE_SR2; + + w = uwire_read_reg(reg); + w &= ~(0x3f << shift); + w |= val << shift; + uwire_write_reg(reg, w); +} + +static int wait_uwire_csr_flag(u16 mask, u16 val, int might_not_catch) +{ + u16 w; + int c = 0; + unsigned long max_jiffies = jiffies + HZ; + + for (;;) { + w = uwire_read_reg(UWIRE_CSR); + if ((w & mask) == val) + break; + if (time_after(jiffies, max_jiffies)) { + printk(KERN_ERR "%s: timeout. reg=%#06x " + "mask=%#06x val=%#06x\n", + __FUNCTION__, w, mask, val); + return -1; + } + c++; + if (might_not_catch && c > 64) + break; + } + return 0; +} + +static void uwire_set_clk1_div(int div1_idx) +{ + u16 w; + + w = uwire_read_reg(UWIRE_SR3); + w &= ~(0x03 << 1); + w |= div1_idx << 1; + uwire_write_reg(UWIRE_SR3, w); +} + +static void uwire_chipselect(struct spi_device *spi, int value) +{ + struct uwire_state *ust = spi->controller_state; + u16 w; + int old_cs; + + + BUG_ON(wait_uwire_csr_flag(CSRB, 0, 0)); + + w = uwire_read_reg(UWIRE_CSR); + old_cs = (w >> 10) & 0x03; + if (value == BITBANG_CS_INACTIVE || old_cs != spi->chip_select) { + /* Deselect this CS, or the previous CS */ + w &= ~CS_CMD; + uwire_write_reg(UWIRE_CSR, w); + } + /* activate specfied chipselect */ + if (value == BITBANG_CS_ACTIVE) { + uwire_set_clk1_div(ust->div1_idx); + /* invert clock? */ + if (spi->mode & SPI_CPOL) + uwire_write_reg(UWIRE_SR4, 1); + else + uwire_write_reg(UWIRE_SR4, 0); + + w = spi->chip_select << 10; + w |= CS_CMD; + uwire_write_reg(UWIRE_CSR, w); + } +} + +static int uwire_txrx(struct spi_device *spi, struct spi_transfer *t) +{ + struct uwire_state *ust = spi->controller_state; + unsigned len = t->len; + unsigned bits = ust->bits_per_word; + unsigned bytes; + u16 val, w; + int status = 0;; + + if (!t->tx_buf && !t->rx_buf) + return 0; + + /* Microwire doesn't read and write concurrently */ + if (t->tx_buf && t->rx_buf) + return -EPERM; + + w = spi->chip_select << 10; + w |= CS_CMD; + + if (t->tx_buf) { + const u8 *buf = t->tx_buf; + + /* NOTE: DMA could be used for TX transfers */ + + /* write one or two bytes at a time */ + while (len >= 1) { + /* tx bit 15 is first sent; we byteswap multibyte words + * (msb-first) on the way out from memory. + */ + val = *buf++; + if (bits > 8) { + bytes = 2; + val |= *buf++ << 8; + } else + bytes = 1; + val <<= 16 - bits; + +#ifdef VERBOSE + pr_debug("%s: write-%d =%04x\n", + spi->dev.bus_id, bits, val); +#endif + if (wait_uwire_csr_flag(CSRB, 0, 0)) + goto eio; + + uwire_write_reg(UWIRE_TDR, val); + + /* start write */ + val = START | w | (bits << 5); + + uwire_write_reg(UWIRE_CSR, val); + len -= bytes; + + /* Wait till write actually starts. + * This is needed with MPU clock 60+ MHz. + * REVISIT: we may not have time to catch it... + */ + if (wait_uwire_csr_flag(CSRB, CSRB, 1)) + goto eio; + + status += bytes; + } + + /* REVISIT: save this for later to get more i/o overlap */ + if (wait_uwire_csr_flag(CSRB, 0, 0)) + goto eio; + + } else if (t->rx_buf) { + u8 *buf = t->rx_buf; + + /* read one or two bytes at a time */ + while (len) { + if (bits > 8) { + bytes = 2; + } else + bytes = 1; + + /* start read */ + val = START | w | (bits << 0); + uwire_write_reg(UWIRE_CSR, val); + len -= bytes; + + /* Wait till read actually starts */ + (void) wait_uwire_csr_flag(CSRB, CSRB, 1); + + if (wait_uwire_csr_flag(RDRB | CSRB, + RDRB, 0)) + goto eio; + + /* rx bit 0 is last received; multibyte words will + * be properly byteswapped on the way to memory. + */ + val = uwire_read_reg(UWIRE_RDR); + val &= (1 << bits) - 1; + *buf++ = (u8) val; + if (bytes == 2) + *buf++ = val >> 8; + status += bytes; +#ifdef VERBOSE + pr_debug("%s: read-%d =%04x\n", + spi->dev.bus_id, bits, val); +#endif + + } + } + return status; +eio: + return -EIO; +} + +static int uwire_setup_transfer(struct spi_device *spi, struct spi_transfer *t) +{ + struct uwire_state *ust = spi->controller_state; + struct uwire_spi *uwire; + unsigned flags = 0; + unsigned bits; + unsigned hz; + unsigned long rate; + int div1_idx; + int div1; + int div2; + int status; + + uwire = spi_master_get_devdata(spi->master); + + if (spi->chip_select > 3) { + pr_debug("%s: cs%d?\n", spi->dev.bus_id, spi->chip_select); + status = -ENODEV; + goto done; + } + + bits = spi->bits_per_word; + if (t != NULL && t->bits_per_word) + bits = t->bits_per_word; + if (!bits) + bits = 8; + + if (bits > 16) { + pr_debug("%s: wordsize %d?\n", spi->dev.bus_id, bits); + status = -ENODEV; + goto done; + } + ust->bits_per_word = bits; + + /* mode 0..3, clock inverted separately; + * standard nCS signaling; + * don't treat DI=high as "not ready" + */ + if (spi->mode & SPI_CS_HIGH) + flags |= UWIRE_CS_ACTIVE_HIGH; + + if (spi->mode & SPI_CPOL) + flags |= UWIRE_CLK_INVERTED; + + switch (spi->mode & (SPI_CPOL | SPI_CPHA)) { + case SPI_MODE_0: + case SPI_MODE_3: + flags |= UWIRE_WRITE_RISING_EDGE | UWIRE_READ_FALLING_EDGE; + break; + case SPI_MODE_1: + case SPI_MODE_2: + flags |= UWIRE_WRITE_FALLING_EDGE | UWIRE_READ_RISING_EDGE; + break; + } + + /* assume it's already enabled */ + rate = clk_get_rate(uwire->ck); + + hz = spi->max_speed_hz; + if (t != NULL && t->speed_hz) + hz = t->speed_hz; + + if (!hz) { + pr_debug("%s: zero speed?\n", spi->dev.bus_id); + status = -EINVAL; + goto done; + } + + /* F_INT = mpu_xor_clk / DIV1 */ + for (div1_idx = 0; div1_idx < 4; div1_idx++) { + switch (div1_idx) { + case 0: + div1 = 2; + break; + case 1: + div1 = 4; + break; + case 2: + div1 = 7; + break; + default: + case 3: + div1 = 10; + break; + } + div2 = (rate / div1 + hz - 1) / hz; + if (div2 <= 8) + break; + } + if (div1_idx == 4) { + pr_debug("%s: lowest clock %ld, need %d\n", + spi->dev.bus_id, rate / 10 / 8, hz); + status = -EDOM; + goto done; + } + + /* we have to cache this and reset in uwire_chipselect as this is a + * global parameter and another uwire device can change it under + * us */ + ust->div1_idx = div1_idx; + uwire_set_clk1_div(div1_idx); + + rate /= div1; + + switch (div2) { + case 0: + case 1: + case 2: + flags |= UWIRE_FREQ_DIV_2; + rate /= 2; + break; + case 3: + case 4: + flags |= UWIRE_FREQ_DIV_4; + rate /= 4; + break; + case 5: + case 6: + case 7: + case 8: + flags |= UWIRE_FREQ_DIV_8; + rate /= 8; + break; + } + omap_uwire_configure_mode(spi->chip_select, flags); + pr_debug("%s: uwire flags %02x, armxor %lu KHz, SCK %lu KHz\n", + __FUNCTION__, flags, + clk_get_rate(uwire->ck) / 1000, + rate / 1000); + status = 0; +done: + return status; +} + +static int uwire_setup(struct spi_device *spi) +{ + struct uwire_state *ust = spi->controller_state; + + if (ust == NULL) { + ust = kzalloc(sizeof(*ust), GFP_KERNEL); + if (ust == NULL) + return -ENOMEM; + spi->controller_state = ust; + } + + return uwire_setup_transfer(spi, NULL); +} + +static void uwire_cleanup(const struct spi_device *spi) +{ + kfree(spi->controller_state); +} + +static void uwire_off(struct uwire_spi *uwire) +{ + uwire_write_reg(UWIRE_SR3, 0); + clk_disable(uwire->ck); + clk_put(uwire->ck); + spi_master_put(uwire->bitbang.master); +} + +static int uwire_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct uwire_spi *uwire; + int status; + + master = spi_alloc_master(&pdev->dev, sizeof *uwire); + if (!master) + return -ENODEV; + + uwire = spi_master_get_devdata(master); + dev_set_drvdata(&pdev->dev, uwire); + + uwire->ck = clk_get(&pdev->dev, "armxor_ck"); + if (!uwire->ck || IS_ERR(uwire->ck)) { + dev_dbg(&pdev->dev, "no mpu_xor_clk ?\n"); + spi_master_put(master); + return -ENODEV; + } + clk_enable(uwire->ck); + + if (cpu_is_omap730()) + uwire_idx_shift = 1; + else + uwire_idx_shift = 2; + + uwire_write_reg(UWIRE_SR3, 1); + + master->bus_num = 2; /* "official" */ + master->num_chipselect = 4; + master->setup = uwire_setup; + master->cleanup = uwire_cleanup; + + uwire->bitbang.master = master; + uwire->bitbang.chipselect = uwire_chipselect; + uwire->bitbang.setup_transfer = uwire_setup_transfer; + uwire->bitbang.txrx_bufs = uwire_txrx; + + status = spi_bitbang_start(&uwire->bitbang); + if (status < 0) + uwire_off(uwire); + return status; +} + +static int uwire_remove(struct platform_device *pdev) +{ + struct uwire_spi *uwire = dev_get_drvdata(&pdev->dev); + int status; + + // FIXME remove all child devices, somewhere ... + + status = spi_bitbang_stop(&uwire->bitbang); + uwire_off(uwire); + return status; +} + +static struct platform_driver uwire_driver = { + .driver = { + .name = "omap_uwire", + .bus = &platform_bus_type, + .owner = THIS_MODULE, + }, + .probe = uwire_probe, + .remove = uwire_remove, + // suspend ... unuse ck + // resume ... use ck +}; + +static int __init omap_uwire_init(void) +{ + /* FIXME move these into the relevant board init code. also, include + * H3 support; it uses tsc2101 like H2 (on a different chipselect). + */ + + if (machine_is_omap_h2()) { + /* defaults: W21 SDO, U18 SDI, V19 SCL */ + omap_cfg_reg(N14_1610_UWIRE_CS0); + omap_cfg_reg(N15_1610_UWIRE_CS1); + } + if (machine_is_omap_perseus2()) { + /* configure pins: MPU_UW_nSCS1, MPU_UW_SDO, MPU_UW_SCLK */ + int val = omap_readl(OMAP730_IO_CONF_9) & ~0x00EEE000; + omap_writel(val | 0x00AAA000, OMAP730_IO_CONF_9); + } + + return platform_driver_register(&uwire_driver); +} + +static void __exit omap_uwire_exit(void) +{ + platform_driver_unregister(&uwire_driver); +} + +subsys_initcall(omap_uwire_init); +module_exit(omap_uwire_exit); + +MODULE_LICENSE("GPL"); + diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index 8b41f9c..9f2c887 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -1214,9 +1214,9 @@ static int setup(struct spi_device *spi) return 0; } -static void cleanup(const struct spi_device *spi) +static void cleanup(struct spi_device *spi) { - struct chip_data *chip = spi_get_ctldata((struct spi_device *)spi); + struct chip_data *chip = spi_get_ctldata(spi); kfree(chip); } diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 6307428..2328128 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -32,7 +32,7 @@ */ static void spidev_release(struct device *dev) { - const struct spi_device *spi = to_spi_device(dev); + struct spi_device *spi = to_spi_device(dev); /* spi masters may cleanup for released devices */ if (spi->master->cleanup) diff --git a/drivers/spi/spi_bitbang.c b/drivers/spi/spi_bitbang.c index 57289b6..24a330d 100644 --- a/drivers/spi/spi_bitbang.c +++ b/drivers/spi/spi_bitbang.c @@ -210,7 +210,7 @@ int spi_bitbang_setup(struct spi_device *spi) if (!cs->txrx_word) return -EINVAL; - retval = spi_bitbang_setup_transfer(spi, NULL); + retval = bitbang->setup_transfer(spi, NULL); if (retval < 0) return retval; @@ -238,7 +238,7 @@ EXPORT_SYMBOL_GPL(spi_bitbang_setup); /** * spi_bitbang_cleanup - default cleanup for per-word I/O loops */ -void spi_bitbang_cleanup(const struct spi_device *spi) +void spi_bitbang_cleanup(struct spi_device *spi) { kfree(spi->controller_state); } @@ -442,9 +442,10 @@ EXPORT_SYMBOL_GPL(spi_bitbang_transfer); * hardware that basically exposes a shift register) or per-spi_transfer * (which takes better advantage of hardware like fifos or DMA engines). * - * Drivers using per-word I/O loops should use (or call) spi_bitbang_setup and - * spi_bitbang_cleanup to handle those spi master methods. Those methods are - * the defaults if the bitbang->txrx_bufs routine isn't initialized. + * Drivers using per-word I/O loops should use (or call) spi_bitbang_setup, + * spi_bitbang_cleanup and spi_bitbang_setup_transfer to handle those spi + * master methods. Those methods are the defaults if the bitbang->txrx_bufs + * routine isn't initialized. * * This routine registers the spi_master, which will process requests in a * dedicated task, keeping IRQs unblocked most of the time. To stop diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c new file mode 100644 index 0000000..6ccf8a1 --- /dev/null +++ b/drivers/spi/spi_imx.c @@ -0,0 +1,1769 @@ +/* + * drivers/spi/spi_imx.c + * + * Copyright (C) 2006 SWAPP + * Andrea Paterniani <a.paterniani@swapp-eng.it> + * + * Initial version inspired by: + * linux-2.6.17-rc3-mm1/drivers/spi/pxa2xx_spi.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/ioport.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/spi/spi.h> +#include <linux/workqueue.h> +#include <linux/delay.h> + +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/hardware.h> +#include <asm/delay.h> + +#include <asm/arch/hardware.h> +#include <asm/arch/imx-dma.h> +#include <asm/arch/spi_imx.h> + +/*-------------------------------------------------------------------------*/ +/* SPI Registers offsets from peripheral base address */ +#define SPI_RXDATA (0x00) +#define SPI_TXDATA (0x04) +#define SPI_CONTROL (0x08) +#define SPI_INT_STATUS (0x0C) +#define SPI_TEST (0x10) +#define SPI_PERIOD (0x14) +#define SPI_DMA (0x18) +#define SPI_RESET (0x1C) + +/* SPI Control Register Bit Fields & Masks */ +#define SPI_CONTROL_BITCOUNT_MASK (0xF) /* Bit Count Mask */ +#define SPI_CONTROL_BITCOUNT(n) (((n) - 1) & SPI_CONTROL_BITCOUNT_MASK) +#define SPI_CONTROL_POL (0x1 << 4) /* Clock Polarity Mask */ +#define SPI_CONTROL_POL_ACT_HIGH (0x0 << 4) /* Active high pol. (0=idle) */ +#define SPI_CONTROL_POL_ACT_LOW (0x1 << 4) /* Active low pol. (1=idle) */ +#define SPI_CONTROL_PHA (0x1 << 5) /* Clock Phase Mask */ +#define SPI_CONTROL_PHA_0 (0x0 << 5) /* Clock Phase 0 */ +#define SPI_CONTROL_PHA_1 (0x1 << 5) /* Clock Phase 1 */ +#define SPI_CONTROL_SSCTL (0x1 << 6) /* /SS Waveform Select Mask */ +#define SPI_CONTROL_SSCTL_0 (0x0 << 6) /* Master: /SS stays low between SPI burst + Slave: RXFIFO advanced by BIT_COUNT */ +#define SPI_CONTROL_SSCTL_1 (0x1 << 6) /* Master: /SS insert pulse between SPI burst + Slave: RXFIFO advanced by /SS rising edge */ +#define SPI_CONTROL_SSPOL (0x1 << 7) /* /SS Polarity Select Mask */ +#define SPI_CONTROL_SSPOL_ACT_LOW (0x0 << 7) /* /SS Active low */ +#define SPI_CONTROL_SSPOL_ACT_HIGH (0x1 << 7) /* /SS Active high */ +#define SPI_CONTROL_XCH (0x1 << 8) /* Exchange */ +#define SPI_CONTROL_SPIEN (0x1 << 9) /* SPI Module Enable */ +#define SPI_CONTROL_MODE (0x1 << 10) /* SPI Mode Select Mask */ +#define SPI_CONTROL_MODE_SLAVE (0x0 << 10) /* SPI Mode Slave */ +#define SPI_CONTROL_MODE_MASTER (0x1 << 10) /* SPI Mode Master */ +#define SPI_CONTROL_DRCTL (0x3 << 11) /* /SPI_RDY Control Mask */ +#define SPI_CONTROL_DRCTL_0 (0x0 << 11) /* Ignore /SPI_RDY */ +#define SPI_CONTROL_DRCTL_1 (0x1 << 11) /* /SPI_RDY falling edge triggers input */ +#define SPI_CONTROL_DRCTL_2 (0x2 << 11) /* /SPI_RDY active low level triggers input */ +#define SPI_CONTROL_DATARATE (0x7 << 13) /* Data Rate Mask */ +#define SPI_PERCLK2_DIV_MIN (0) /* PERCLK2:4 */ +#define SPI_PERCLK2_DIV_MAX (7) /* PERCLK2:512 */ +#define SPI_CONTROL_DATARATE_MIN (SPI_PERCLK2_DIV_MAX << 13) +#define SPI_CONTROL_DATARATE_MAX (SPI_PERCLK2_DIV_MIN << 13) +#define SPI_CONTROL_DATARATE_BAD (SPI_CONTROL_DATARATE_MIN + 1) + +/* SPI Interrupt/Status Register Bit Fields & Masks */ +#define SPI_STATUS_TE (0x1 << 0) /* TXFIFO Empty Status */ +#define SPI_STATUS_TH (0x1 << 1) /* TXFIFO Half Status */ +#define SPI_STATUS_TF (0x1 << 2) /* TXFIFO Full Status */ +#define SPI_STATUS_RR (0x1 << 3) /* RXFIFO Data Ready Status */ +#define SPI_STATUS_RH (0x1 << 4) /* RXFIFO Half Status */ +#define SPI_STATUS_RF (0x1 << 5) /* RXFIFO Full Status */ +#define SPI_STATUS_RO (0x1 << 6) /* RXFIFO Overflow */ +#define SPI_STATUS_BO (0x1 << 7) /* Bit Count Overflow */ +#define SPI_STATUS (0xFF) /* SPI Status Mask */ +#define SPI_INTEN_TE (0x1 << 8) /* TXFIFO Empty Interrupt Enable */ +#define SPI_INTEN_TH (0x1 << 9) /* TXFIFO Half Interrupt Enable */ +#define SPI_INTEN_TF (0x1 << 10) /* TXFIFO Full Interrupt Enable */ +#define SPI_INTEN_RE (0x1 << 11) /* RXFIFO Data Ready Interrupt Enable */ +#define SPI_INTEN_RH (0x1 << 12) /* RXFIFO Half Interrupt Enable */ +#define SPI_INTEN_RF (0x1 << 13) /* RXFIFO Full Interrupt Enable */ +#define SPI_INTEN_RO (0x1 << 14) /* RXFIFO Overflow Interrupt Enable */ +#define SPI_INTEN_BO (0x1 << 15) /* Bit Count Overflow Interrupt Enable */ +#define SPI_INTEN (0xFF << 8) /* SPI Interrupt Enable Mask */ + +/* SPI Test Register Bit Fields & Masks */ +#define SPI_TEST_TXCNT (0xF << 0) /* TXFIFO Counter */ +#define SPI_TEST_RXCNT_LSB (4) /* RXFIFO Counter LSB */ +#define SPI_TEST_RXCNT (0xF << 4) /* RXFIFO Counter */ +#define SPI_TEST_SSTATUS (0xF << 8) /* State Machine Status */ +#define SPI_TEST_LBC (0x1 << 14) /* Loop Back Control */ + +/* SPI Period Register Bit Fields & Masks */ +#define SPI_PERIOD_WAIT (0x7FFF << 0) /* Wait Between Transactions */ +#define SPI_PERIOD_MAX_WAIT (0x7FFF) /* Max Wait Between + Transactions */ +#define SPI_PERIOD_CSRC (0x1 << 15) /* Period Clock Source Mask */ +#define SPI_PERIOD_CSRC_BCLK (0x0 << 15) /* Period Clock Source is + Bit Clock */ +#define SPI_PERIOD_CSRC_32768 (0x1 << 15) /* Period Clock Source is + 32.768 KHz Clock */ + +/* SPI DMA Register Bit Fields & Masks */ +#define SPI_DMA_RHDMA (0xF << 4) /* RXFIFO Half Status */ +#define SPI_DMA_RFDMA (0x1 << 5) /* RXFIFO Full Status */ +#define SPI_DMA_TEDMA (0x1 << 6) /* TXFIFO Empty Status */ +#define SPI_DMA_THDMA (0x1 << 7) /* TXFIFO Half Status */ +#define SPI_DMA_RHDEN (0x1 << 12) /* RXFIFO Half DMA Request Enable */ +#define SPI_DMA_RFDEN (0x1 << 13) /* RXFIFO Full DMA Request Enable */ +#define SPI_DMA_TEDEN (0x1 << 14) /* TXFIFO Empty DMA Request Enable */ +#define SPI_DMA_THDEN (0x1 << 15) /* TXFIFO Half DMA Request Enable */ + +/* SPI Soft Reset Register Bit Fields & Masks */ +#define SPI_RESET_START (0x1) /* Start */ + +/* Default SPI configuration values */ +#define SPI_DEFAULT_CONTROL \ +( \ + SPI_CONTROL_BITCOUNT(16) | \ + SPI_CONTROL_POL_ACT_HIGH | \ + SPI_CONTROL_PHA_0 | \ + SPI_CONTROL_SPIEN | \ + SPI_CONTROL_SSCTL_1 | \ + SPI_CONTROL_MODE_MASTER | \ + SPI_CONTROL_DRCTL_0 | \ + SPI_CONTROL_DATARATE_MIN \ +) +#define SPI_DEFAULT_ENABLE_LOOPBACK (0) +#define SPI_DEFAULT_ENABLE_DMA (0) +#define SPI_DEFAULT_PERIOD_WAIT (8) +/*-------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------*/ +/* TX/RX SPI FIFO size */ +#define SPI_FIFO_DEPTH (8) +#define SPI_FIFO_BYTE_WIDTH (2) +#define SPI_FIFO_OVERFLOW_MARGIN (2) + +/* DMA burst lenght for half full/empty request trigger */ +#define SPI_DMA_BLR (SPI_FIFO_DEPTH * SPI_FIFO_BYTE_WIDTH / 2) + +/* Dummy char output to achieve reads. + Choosing something different from all zeroes may help pattern recogition + for oscilloscope analysis, but may break some drivers. */ +#define SPI_DUMMY_u8 0 +#define SPI_DUMMY_u16 ((SPI_DUMMY_u8 << 8) | SPI_DUMMY_u8) +#define SPI_DUMMY_u32 ((SPI_DUMMY_u16 << 16) | SPI_DUMMY_u16) + +/** + * Macro to change a u32 field: + * @r : register to edit + * @m : bit mask + * @v : new value for the field correctly bit-alligned +*/ +#define u32_EDIT(r, m, v) r = (r & ~(m)) | (v) + +/* Message state */ +#define START_STATE ((void*)0) +#define RUNNING_STATE ((void*)1) +#define DONE_STATE ((void*)2) +#define ERROR_STATE ((void*)-1) + +/* Queue state */ +#define QUEUE_RUNNING (0) +#define QUEUE_STOPPED (1) + +#define IS_DMA_ALIGNED(x) (((u32)(x) & 0x03) == 0) +/*-------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------*/ +/* Driver data structs */ + +/* Context */ +struct driver_data { + /* Driver model hookup */ + struct platform_device *pdev; + + /* SPI framework hookup */ + struct spi_master *master; + + /* IMX hookup */ + struct spi_imx_master *master_info; + + /* Memory resources and SPI regs virtual address */ + struct resource *ioarea; + void __iomem *regs; + + /* SPI RX_DATA physical address */ + dma_addr_t rd_data_phys; + + /* Driver message queue */ + struct workqueue_struct *workqueue; + struct work_struct work; + spinlock_t lock; + struct list_head queue; + int busy; + int run; + + /* Message Transfer pump */ + struct tasklet_struct pump_transfers; + + /* Current message, transfer and state */ + struct spi_message *cur_msg; + struct spi_transfer *cur_transfer; + struct chip_data *cur_chip; + + /* Rd / Wr buffers pointers */ + size_t len; + void *tx; + void *tx_end; + void *rx; + void *rx_end; + + u8 rd_only; + u8 n_bytes; + int cs_change; + + /* Function pointers */ + irqreturn_t (*transfer_handler)(struct driver_data *drv_data); + void (*cs_control)(u32 command); + + /* DMA setup */ + int rx_channel; + int tx_channel; + dma_addr_t rx_dma; + dma_addr_t tx_dma; + int rx_dma_needs_unmap; + int tx_dma_needs_unmap; + size_t tx_map_len; + u32 dummy_dma_buf ____cacheline_aligned; +}; + +/* Runtime state */ +struct chip_data { + u32 control; + u32 period; + u32 test; + + u8 enable_dma:1; + u8 bits_per_word; + u8 n_bytes; + u32 max_speed_hz; + + void (*cs_control)(u32 command); +}; +/*-------------------------------------------------------------------------*/ + + +static void pump_messages(struct work_struct *work); + +static int flush(struct driver_data *drv_data) +{ + unsigned long limit = loops_per_jiffy << 1; + void __iomem *regs = drv_data->regs; + volatile u32 d; + + dev_dbg(&drv_data->pdev->dev, "flush\n"); + do { + while (readl(regs + SPI_INT_STATUS) & SPI_STATUS_RR) + d = readl(regs + SPI_RXDATA); + } while ((readl(regs + SPI_CONTROL) & SPI_CONTROL_XCH) && limit--); + + return limit; +} + +static void restore_state(struct driver_data *drv_data) +{ + void __iomem *regs = drv_data->regs; + struct chip_data *chip = drv_data->cur_chip; + + /* Load chip registers */ + dev_dbg(&drv_data->pdev->dev, + "restore_state\n" + " test = 0x%08X\n" + " control = 0x%08X\n", + chip->test, + chip->control); + writel(chip->test, regs + SPI_TEST); + writel(chip->period, regs + SPI_PERIOD); + writel(0, regs + SPI_INT_STATUS); + writel(chip->control, regs + SPI_CONTROL); +} + +static void null_cs_control(u32 command) +{ +} + +static inline u32 data_to_write(struct driver_data *drv_data) +{ + return ((u32)(drv_data->tx_end - drv_data->tx)) / drv_data->n_bytes; +} + +static inline u32 data_to_read(struct driver_data *drv_data) +{ + return ((u32)(drv_data->rx_end - drv_data->rx)) / drv_data->n_bytes; +} + +static int write(struct driver_data *drv_data) +{ + void __iomem *regs = drv_data->regs; + void *tx = drv_data->tx; + void *tx_end = drv_data->tx_end; + u8 n_bytes = drv_data->n_bytes; + u32 remaining_writes; + u32 fifo_avail_space; + u32 n; + u16 d; + + /* Compute how many fifo writes to do */ + remaining_writes = (u32)(tx_end - tx) / n_bytes; + fifo_avail_space = SPI_FIFO_DEPTH - + (readl(regs + SPI_TEST) & SPI_TEST_TXCNT); + if (drv_data->rx && (fifo_avail_space > SPI_FIFO_OVERFLOW_MARGIN)) + /* Fix misunderstood receive overflow */ + fifo_avail_space -= SPI_FIFO_OVERFLOW_MARGIN; + n = min(remaining_writes, fifo_avail_space); + + dev_dbg(&drv_data->pdev->dev, + "write type %s\n" + " remaining writes = %d\n" + " fifo avail space = %d\n" + " fifo writes = %d\n", + (n_bytes == 1) ? "u8" : "u16", + remaining_writes, + fifo_avail_space, + n); + + if (n > 0) { + /* Fill SPI TXFIFO */ + if (drv_data->rd_only) { + tx += n * n_bytes; + while (n--) + writel(SPI_DUMMY_u16, regs + SPI_TXDATA); + } else { + if (n_bytes == 1) { + while (n--) { + d = *(u8*)tx; + writel(d, regs + SPI_TXDATA); + tx += 1; + } + } else { + while (n--) { + d = *(u16*)tx; + writel(d, regs + SPI_TXDATA); + tx += 2; + } + } + } + + /* Trigger transfer */ + writel(readl(regs + SPI_CONTROL) | SPI_CONTROL_XCH, + regs + SPI_CONTROL); + + /* Update tx pointer */ + drv_data->tx = tx; + } + + return (tx >= tx_end); +} + +static int read(struct driver_data *drv_data) +{ + void __iomem *regs = drv_data->regs; + void *rx = drv_data->rx; + void *rx_end = drv_data->rx_end; + u8 n_bytes = drv_data->n_bytes; + u32 remaining_reads; + u32 fifo_rxcnt; + u32 n; + u16 d; + + /* Compute how many fifo reads to do */ + remaining_reads = (u32)(rx_end - rx) / n_bytes; + fifo_rxcnt = (readl(regs + SPI_TEST) & SPI_TEST_RXCNT) >> + SPI_TEST_RXCNT_LSB; + n = min(remaining_reads, fifo_rxcnt); + + dev_dbg(&drv_data->pdev->dev, + "read type %s\n" + " remaining reads = %d\n" + " fifo rx count = %d\n" + " fifo reads = %d\n", + (n_bytes == 1) ? "u8" : "u16", + remaining_reads, + fifo_rxcnt, + n); + + if (n > 0) { + /* Read SPI RXFIFO */ + if (n_bytes == 1) { + while (n--) { + d = readl(regs + SPI_RXDATA); + *((u8*)rx) = d; + rx += 1; + } + } else { + while (n--) { + d = readl(regs + SPI_RXDATA); + *((u16*)rx) = d; + rx += 2; + } + } + + /* Update rx pointer */ + drv_data->rx = rx; + } + + return (rx >= rx_end); +} + +static void *next_transfer(struct driver_data *drv_data) +{ + struct spi_message *msg = drv_data->cur_msg; + struct spi_transfer *trans = drv_data->cur_transfer; + + /* Move to next transfer */ + if (trans->transfer_list.next != &msg->transfers) { + drv_data->cur_transfer = + list_entry(trans->transfer_list.next, + struct spi_transfer, + transfer_list); + return RUNNING_STATE; + } + + return DONE_STATE; +} + +static int map_dma_buffers(struct driver_data *drv_data) +{ + struct spi_message *msg; + struct device *dev; + void *buf; + + drv_data->rx_dma_needs_unmap = 0; + drv_data->tx_dma_needs_unmap = 0; + + if (!drv_data->master_info->enable_dma || + !drv_data->cur_chip->enable_dma) + return -1; + + msg = drv_data->cur_msg; + dev = &msg->spi->dev; + if (msg->is_dma_mapped) { + if (drv_data->tx_dma) + /* The caller provided at least dma and cpu virtual + address for write; pump_transfers() will consider the + transfer as write only if cpu rx virtual address is + NULL */ + return 0; + + if (drv_data->rx_dma) { + /* The caller provided dma and cpu virtual address to + performe read only transfer --> + use drv_data->dummy_dma_buf for dummy writes to + achive reads */ + buf = &drv_data->dummy_dma_buf; + drv_data->tx_map_len = sizeof(drv_data->dummy_dma_buf); + drv_data->tx_dma = dma_map_single(dev, + buf, + drv_data->tx_map_len, + DMA_TO_DEVICE); + if (dma_mapping_error(drv_data->tx_dma)) + return -1; + + drv_data->tx_dma_needs_unmap = 1; + + /* Flags transfer as rd_only for pump_transfers() DMA + regs programming (should be redundant) */ + drv_data->tx = NULL; + + return 0; + } + } + + if (!IS_DMA_ALIGNED(drv_data->rx) || !IS_DMA_ALIGNED(drv_data->tx)) + return -1; + + /* NULL rx means write-only transfer and no map needed + since rx DMA will not be used */ + if (drv_data->rx) { + buf = drv_data->rx; + drv_data->rx_dma = dma_map_single( + dev, + buf, + drv_data->len, + DMA_FROM_DEVICE); + if (dma_mapping_error(drv_data->rx_dma)) + return -1; + drv_data->rx_dma_needs_unmap = 1; + } + + if (drv_data->tx == NULL) { + /* Read only message --> use drv_data->dummy_dma_buf for dummy + writes to achive reads */ + buf = &drv_data->dummy_dma_buf; + drv_data->tx_map_len = sizeof(drv_data->dummy_dma_buf); + } else { + buf = drv_data->tx; + drv_data->tx_map_len = drv_data->len; + } + drv_data->tx_dma = dma_map_single(dev, + buf, + drv_data->tx_map_len, + DMA_TO_DEVICE); + if (dma_mapping_error(drv_data->tx_dma)) { + if (drv_data->rx_dma) { + dma_unmap_single(dev, + drv_data->rx_dma, + drv_data->len, + DMA_FROM_DEVICE); + drv_data->rx_dma_needs_unmap = 0; + } + return -1; + } + drv_data->tx_dma_needs_unmap = 1; + + return 0; +} + +static void unmap_dma_buffers(struct driver_data *drv_data) +{ + struct spi_message *msg = drv_data->cur_msg; + struct device *dev = &msg->spi->dev; + + if (drv_data->rx_dma_needs_unmap) { + dma_unmap_single(dev, + drv_data->rx_dma, + drv_data->len, + DMA_FROM_DEVICE); + drv_data->rx_dma_needs_unmap = 0; + } + if (drv_data->tx_dma_needs_unmap) { + dma_unmap_single(dev, + drv_data->tx_dma, + drv_data->tx_map_len, + DMA_TO_DEVICE); + drv_data->tx_dma_needs_unmap = 0; + } +} + +/* Caller already set message->status (dma is already blocked) */ +static void giveback(struct spi_message *message, struct driver_data *drv_data) +{ + void __iomem *regs = drv_data->regs; + + /* Bring SPI to sleep; restore_state() and pump_transfer() + will do new setup */ + writel(0, regs + SPI_INT_STATUS); + writel(0, regs + SPI_DMA); + + drv_data->cs_control(SPI_CS_DEASSERT); + + message->state = NULL; + if (message->complete) + message->complete(message->context); + + drv_data->cur_msg = NULL; + drv_data->cur_transfer = NULL; + drv_data->cur_chip = NULL; + queue_work(drv_data->workqueue, &drv_data->work); +} + +static void dma_err_handler(int channel, void *data, int errcode) +{ + struct driver_data *drv_data = data; + struct spi_message *msg = drv_data->cur_msg; + + dev_dbg(&drv_data->pdev->dev, "dma_err_handler\n"); + + /* Disable both rx and tx dma channels */ + imx_dma_disable(drv_data->rx_channel); + imx_dma_disable(drv_data->tx_channel); + + if (flush(drv_data) == 0) + dev_err(&drv_data->pdev->dev, + "dma_err_handler - flush failed\n"); + + unmap_dma_buffers(drv_data); + + msg->state = ERROR_STATE; + tasklet_schedule(&drv_data->pump_transfers); +} + +static void dma_tx_handler(int channel, void *data) +{ + struct driver_data *drv_data = data; + + dev_dbg(&drv_data->pdev->dev, "dma_tx_handler\n"); + + imx_dma_disable(channel); + + /* Now waits for TX FIFO empty */ + writel(readl(drv_data->regs + SPI_INT_STATUS) | SPI_INTEN_TE, + drv_data->regs + SPI_INT_STATUS); +} + +static irqreturn_t dma_transfer(struct driver_data *drv_data) +{ + u32 status; + struct spi_message *msg = drv_data->cur_msg; + void __iomem *regs = drv_data->regs; + unsigned long limit; + + status = readl(regs + SPI_INT_STATUS); + + if ((status & SPI_INTEN_RO) && (status & SPI_STATUS_RO)) { + writel(status & ~SPI_INTEN, regs + SPI_INT_STATUS); + + imx_dma_disable(drv_data->rx_channel); + unmap_dma_buffers(drv_data); + + if (flush(drv_data) == 0) + dev_err(&drv_data->pdev->dev, + "dma_transfer - flush failed\n"); + + dev_warn(&drv_data->pdev->dev, + "dma_transfer - fifo overun\n"); + + msg->state = ERROR_STATE; + tasklet_schedule(&drv_data->pump_transfers); + + return IRQ_HANDLED; + } + + if (status & SPI_STATUS_TE) { + writel(status & ~SPI_INTEN_TE, regs + SPI_INT_STATUS); + + if (drv_data->rx) { + /* Wait end of transfer before read trailing data */ + limit = loops_per_jiffy << 1; + while ((readl(regs + SPI_CONTROL) & SPI_CONTROL_XCH) && + limit--); + + if (limit == 0) + dev_err(&drv_data->pdev->dev, + "dma_transfer - end of tx failed\n"); + else + dev_dbg(&drv_data->pdev->dev, + "dma_transfer - end of tx\n"); + + imx_dma_disable(drv_data->rx_channel); + unmap_dma_buffers(drv_data); + + /* Calculate number of trailing data and read them */ + dev_dbg(&drv_data->pdev->dev, + "dma_transfer - test = 0x%08X\n", + readl(regs + SPI_TEST)); + drv_data->rx = drv_data->rx_end - + ((readl(regs + SPI_TEST) & + SPI_TEST_RXCNT) >> + SPI_TEST_RXCNT_LSB)*drv_data->n_bytes; + read(drv_data); + } else { + /* Write only transfer */ + unmap_dma_buffers(drv_data); + + if (flush(drv_data) == 0) + dev_err(&drv_data->pdev->dev, + "dma_transfer - flush failed\n"); + } + + /* End of transfer, update total byte transfered */ + msg->actual_length += drv_data->len; + + /* Release chip select if requested, transfer delays are + handled in pump_transfers() */ + if (drv_data->cs_change) + drv_data->cs_control(SPI_CS_DEASSERT); + + /* Move to next transfer */ + msg->state = next_transfer(drv_data); + + /* Schedule transfer tasklet */ + tasklet_schedule(&drv_data->pump_transfers); + + return IRQ_HANDLED; + } + + /* Opps problem detected */ + return IRQ_NONE; +} + +static irqreturn_t interrupt_wronly_transfer(struct driver_data *drv_data) +{ + struct spi_message *msg = drv_data->cur_msg; + void __iomem *regs = drv_data->regs; + u32 status; + irqreturn_t handled = IRQ_NONE; + + status = readl(regs + SPI_INT_STATUS); + + while (status & SPI_STATUS_TH) { + dev_dbg(&drv_data->pdev->dev, + "interrupt_wronly_transfer - status = 0x%08X\n", status); + + /* Pump data */ + if (write(drv_data)) { + writel(readl(regs + SPI_INT_STATUS) & ~SPI_INTEN, + regs + SPI_INT_STATUS); + + dev_dbg(&drv_data->pdev->dev, + "interrupt_wronly_transfer - end of tx\n"); + + if (flush(drv_data) == 0) + dev_err(&drv_data->pdev->dev, + "interrupt_wronly_transfer - " + "flush failed\n"); + + /* End of transfer, update total byte transfered */ + msg->actual_length += drv_data->len; + + /* Release chip select if requested, transfer delays are + handled in pump_transfers */ + if (drv_data->cs_change) + drv_data->cs_control(SPI_CS_DEASSERT); + + /* Move to next transfer */ + msg->state = next_transfer(drv_data); + + /* Schedule transfer tasklet */ + tasklet_schedule(&drv_data->pump_transfers); + + return IRQ_HANDLED; + } + + status = readl(regs + SPI_INT_STATUS); + + /* We did something */ + handled = IRQ_HANDLED; + } + + return handled; +} + +static irqreturn_t interrupt_transfer(struct driver_data *drv_data) +{ + struct spi_message *msg = drv_data->cur_msg; + void __iomem *regs = drv_data->regs; + u32 status; + irqreturn_t handled = IRQ_NONE; + unsigned long limit; + + status = readl(regs + SPI_INT_STATUS); + + while (status & (SPI_STATUS_TH | SPI_STATUS_RO)) { + dev_dbg(&drv_data->pdev->dev, + "interrupt_transfer - status = 0x%08X\n", status); + + if (status & SPI_STATUS_RO) { + writel(readl(regs + SPI_INT_STATUS) & ~SPI_INTEN, + regs + SPI_INT_STATUS); + + dev_warn(&drv_data->pdev->dev, + "interrupt_transfer - fifo overun\n" + " data not yet written = %d\n" + " data not yet read = %d\n", + data_to_write(drv_data), + data_to_read(drv_data)); + + if (flush(drv_data) == 0) + dev_err(&drv_data->pdev->dev, + "interrupt_transfer - flush failed\n"); + + msg->state = ERROR_STATE; + tasklet_schedule(&drv_data->pump_transfers); + + return IRQ_HANDLED; + } + + /* Pump data */ + read(drv_data); + if (write(drv_data)) { + writel(readl(regs + SPI_INT_STATUS) & ~SPI_INTEN, + regs + SPI_INT_STATUS); + + dev_dbg(&drv_data->pdev->dev, + "interrupt_transfer - end of tx\n"); + + /* Read trailing bytes */ + limit = loops_per_jiffy << 1; + while ((read(drv_data) == 0) && limit--); + + if (limit == 0) + dev_err(&drv_data->pdev->dev, + "interrupt_transfer - " + "trailing byte read failed\n"); + else + dev_dbg(&drv_data->pdev->dev, + "interrupt_transfer - end of rx\n"); + + /* End of transfer, update total byte transfered */ + msg->actual_length += drv_data->len; + + /* Release chip select if requested, transfer delays are + handled in pump_transfers */ + if (drv_data->cs_change) + drv_data->cs_control(SPI_CS_DEASSERT); + + /* Move to next transfer */ + msg->state = next_transfer(drv_data); + + /* Schedule transfer tasklet */ + tasklet_schedule(&drv_data->pump_transfers); + + return IRQ_HANDLED; + } + + status = readl(regs + SPI_INT_STATUS); + + /* We did something */ + handled = IRQ_HANDLED; + } + + return handled; +} + +static irqreturn_t spi_int(int irq, void *dev_id) +{ + struct driver_data *drv_data = (struct driver_data *)dev_id; + + if (!drv_data->cur_msg) { + dev_err(&drv_data->pdev->dev, + "spi_int - bad message state\n"); + /* Never fail */ + return IRQ_HANDLED; + } + + return drv_data->transfer_handler(drv_data); +} + +static inline u32 spi_speed_hz(u32 data_rate) +{ + return imx_get_perclk2() / (4 << ((data_rate) >> 13)); +} + +static u32 spi_data_rate(u32 speed_hz) +{ + u32 div; + u32 quantized_hz = imx_get_perclk2() >> 2; + + for (div = SPI_PERCLK2_DIV_MIN; + div <= SPI_PERCLK2_DIV_MAX; + div++, quantized_hz >>= 1) { + if (quantized_hz <= speed_hz) + /* Max available speed LEQ required speed */ + return div << 13; + } + return SPI_CONTROL_DATARATE_BAD; +} + +static void pump_transfers(unsigned long data) +{ + struct driver_data *drv_data = (struct driver_data *)data; + struct spi_message *message; + struct spi_transfer *transfer, *previous; + struct chip_data *chip; + void __iomem *regs; + u32 tmp, control; + + dev_dbg(&drv_data->pdev->dev, "pump_transfer\n"); + + message = drv_data->cur_msg; + + /* Handle for abort */ + if (message->state == ERROR_STATE) { + message->status = -EIO; + giveback(message, drv_data); + return; + } + + /* Handle end of message */ + if (message->state == DONE_STATE) { + message->status = 0; + giveback(message, drv_data); + return; + } + + chip = drv_data->cur_chip; + + /* Delay if requested at end of transfer*/ + transfer = drv_data->cur_transfer; + if (message->state == RUNNING_STATE) { + previous = list_entry(transfer->transfer_list.prev, + struct spi_transfer, + transfer_list); + if (previous->delay_usecs) + udelay(previous->delay_usecs); + } else { + /* START_STATE */ + message->state = RUNNING_STATE; + drv_data->cs_control = chip->cs_control; + } + + transfer = drv_data->cur_transfer; + drv_data->tx = (void *)transfer->tx_buf; + drv_data->tx_end = drv_data->tx + transfer->len; + drv_data->rx = transfer->rx_buf; + drv_data->rx_end = drv_data->rx + transfer->len; + drv_data->rx_dma = transfer->rx_dma; + drv_data->tx_dma = transfer->tx_dma; + drv_data->len = transfer->len; + drv_data->cs_change = transfer->cs_change; + drv_data->rd_only = (drv_data->tx == NULL); + + regs = drv_data->regs; + control = readl(regs + SPI_CONTROL); + + /* Bits per word setup */ + tmp = transfer->bits_per_word; + if (tmp == 0) { + /* Use device setup */ + tmp = chip->bits_per_word; + drv_data->n_bytes = chip->n_bytes; + } else + /* Use per-transfer setup */ + drv_data->n_bytes = (tmp <= 8) ? 1 : 2; + u32_EDIT(control, SPI_CONTROL_BITCOUNT_MASK, tmp - 1); + + /* Speed setup (surely valid because already checked) */ + tmp = transfer->speed_hz; + if (tmp == 0) + tmp = chip->max_speed_hz; + tmp = spi_data_rate(tmp); + u32_EDIT(control, SPI_CONTROL_DATARATE, tmp); + + writel(control, regs + SPI_CONTROL); + + /* Assert device chip-select */ + drv_data->cs_control(SPI_CS_ASSERT); + + /* DMA cannot read/write SPI FIFOs other than 16 bits at a time; hence + if bits_per_word is less or equal 8 PIO transfers are performed. + Moreover DMA is convinient for transfer length bigger than FIFOs + byte size. */ + if ((drv_data->n_bytes == 2) && + (drv_data->len > SPI_FIFO_DEPTH*SPI_FIFO_BYTE_WIDTH) && + (map_dma_buffers(drv_data) == 0)) { + dev_dbg(&drv_data->pdev->dev, + "pump dma transfer\n" + " tx = %p\n" + " tx_dma = %08X\n" + " rx = %p\n" + " rx_dma = %08X\n" + " len = %d\n", + drv_data->tx, + (unsigned int)drv_data->tx_dma, + drv_data->rx, + (unsigned int)drv_data->rx_dma, + drv_data->len); + + /* Ensure we have the correct interrupt handler */ + drv_data->transfer_handler = dma_transfer; + + /* Trigger transfer */ + writel(readl(regs + SPI_CONTROL) | SPI_CONTROL_XCH, + regs + SPI_CONTROL); + + /* Setup tx DMA */ + if (drv_data->tx) + /* Linear source address */ + CCR(drv_data->tx_channel) = + CCR_DMOD_FIFO | + CCR_SMOD_LINEAR | + CCR_SSIZ_32 | CCR_DSIZ_16 | + CCR_REN; + else + /* Read only transfer -> fixed source address for + dummy write to achive read */ + CCR(drv_data->tx_channel) = + CCR_DMOD_FIFO | + CCR_SMOD_FIFO | + CCR_SSIZ_32 | CCR_DSIZ_16 | + CCR_REN; + + imx_dma_setup_single( + drv_data->tx_channel, + drv_data->tx_dma, + drv_data->len, + drv_data->rd_data_phys + 4, + DMA_MODE_WRITE); + + if (drv_data->rx) { + /* Setup rx DMA for linear destination address */ + CCR(drv_data->rx_channel) = + CCR_DMOD_LINEAR | + CCR_SMOD_FIFO | + CCR_DSIZ_32 | CCR_SSIZ_16 | + CCR_REN; + imx_dma_setup_single( + drv_data->rx_channel, + drv_data->rx_dma, + drv_data->len, + drv_data->rd_data_phys, + DMA_MODE_READ); + imx_dma_enable(drv_data->rx_channel); + + /* Enable SPI interrupt */ + writel(SPI_INTEN_RO, regs + SPI_INT_STATUS); + + /* Set SPI to request DMA service on both + Rx and Tx half fifo watermark */ + writel(SPI_DMA_RHDEN | SPI_DMA_THDEN, regs + SPI_DMA); + } else + /* Write only access -> set SPI to request DMA + service on Tx half fifo watermark */ + writel(SPI_DMA_THDEN, regs + SPI_DMA); + + imx_dma_enable(drv_data->tx_channel); + } else { + dev_dbg(&drv_data->pdev->dev, + "pump pio transfer\n" + " tx = %p\n" + " rx = %p\n" + " len = %d\n", + drv_data->tx, + drv_data->rx, + drv_data->len); + + /* Ensure we have the correct interrupt handler */ + if (drv_data->rx) + drv_data->transfer_handler = interrupt_transfer; + else + drv_data->transfer_handler = interrupt_wronly_transfer; + + /* Enable SPI interrupt */ + if (drv_data->rx) + writel(SPI_INTEN_TH | SPI_INTEN_RO, + regs + SPI_INT_STATUS); + else + writel(SPI_INTEN_TH, regs + SPI_INT_STATUS); + } +} + +static void pump_messages(struct work_struct *work) +{ + struct driver_data *drv_data = + container_of(work, struct driver_data, work); + unsigned long flags; + + /* Lock queue and check for queue work */ + spin_lock_irqsave(&drv_data->lock, flags); + if (list_empty(&drv_data->queue) || drv_data->run == QUEUE_STOPPED) { + drv_data->busy = 0; + spin_unlock_irqrestore(&drv_data->lock, flags); + return; + } + + /* Make sure we are not already running a message */ + if (drv_data->cur_msg) { + spin_unlock_irqrestore(&drv_data->lock, flags); + return; + } + + /* Extract head of queue */ + drv_data->cur_msg = list_entry(drv_data->queue.next, + struct spi_message, queue); + list_del_init(&drv_data->cur_msg->queue); + drv_data->busy = 1; + spin_unlock_irqrestore(&drv_data->lock, flags); + + /* Initial message state */ + drv_data->cur_msg->state = START_STATE; + drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next, + struct spi_transfer, + transfer_list); + + /* Setup the SPI using the per chip configuration */ + drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi); + restore_state(drv_data); + + /* Mark as busy and launch transfers */ + tasklet_schedule(&drv_data->pump_transfers); +} + +static int transfer(struct spi_device *spi, struct spi_message *msg) +{ + struct driver_data *drv_data = spi_master_get_devdata(spi->master); + u32 min_speed_hz, max_speed_hz, tmp; + struct spi_transfer *trans; + unsigned long flags; + + msg->actual_length = 0; + + /* Per transfer setup check */ + min_speed_hz = spi_speed_hz(SPI_CONTROL_DATARATE_MIN); + max_speed_hz = spi->max_speed_hz; + list_for_each_entry(trans, &msg->transfers, transfer_list) { + tmp = trans->bits_per_word; + if (tmp > 16) { + dev_err(&drv_data->pdev->dev, + "message rejected : " + "invalid transfer bits_per_word (%d bits)\n", + tmp); + goto msg_rejected; + } + tmp = trans->speed_hz; + if (tmp) { + if (tmp < min_speed_hz) { + dev_err(&drv_data->pdev->dev, + "message rejected : " + "device min speed (%d Hz) exceeds " + "required transfer speed (%d Hz)\n", + min_speed_hz, + tmp); + goto msg_rejected; + } else if (tmp > max_speed_hz) { + dev_err(&drv_data->pdev->dev, + "message rejected : " + "transfer speed (%d Hz) exceeds " + "device max speed (%d Hz)\n", + tmp, + max_speed_hz); + goto msg_rejected; + } + } + } + + /* Message accepted */ + msg->status = -EINPROGRESS; + msg->state = START_STATE; + + spin_lock_irqsave(&drv_data->lock, flags); + if (drv_data->run == QUEUE_STOPPED) { + spin_unlock_irqrestore(&drv_data->lock, flags); + return -ESHUTDOWN; + } + + list_add_tail(&msg->queue, &drv_data->queue); + if (drv_data->run == QUEUE_RUNNING && !drv_data->busy) + queue_work(drv_data->workqueue, &drv_data->work); + + spin_unlock_irqrestore(&drv_data->lock, flags); + return 0; + +msg_rejected: + /* Message rejected and not queued */ + msg->status = -EINVAL; + msg->state = ERROR_STATE; + if (msg->complete) + msg->complete(msg->context); + return -EINVAL; +} + +/* On first setup bad values must free chip_data memory since will cause + spi_new_device to fail. Bad value setup from protocol driver are simply not + applied and notified to the calling driver. */ +static int setup(struct spi_device *spi) +{ + struct spi_imx_chip *chip_info; + struct chip_data *chip; + int first_setup = 0; + u32 tmp; + int status = 0; + + /* Get controller data */ + chip_info = spi->controller_data; + + /* Get controller_state */ + chip = spi_get_ctldata(spi); + if (chip == NULL) { + first_setup = 1; + + chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL); + if (!chip) { + dev_err(&spi->dev, + "setup - cannot allocate controller state"); + return -ENOMEM; + } + chip->control = SPI_DEFAULT_CONTROL; + + if (chip_info == NULL) { + /* spi_board_info.controller_data not is supplied */ + chip_info = kzalloc(sizeof(struct spi_imx_chip), + GFP_KERNEL); + if (!chip_info) { + dev_err(&spi->dev, + "setup - " + "cannot allocate controller data"); + status = -ENOMEM; + goto err_first_setup; + } + /* Set controller data default value */ + chip_info->enable_loopback = + SPI_DEFAULT_ENABLE_LOOPBACK; + chip_info->enable_dma = SPI_DEFAULT_ENABLE_DMA; + chip_info->ins_ss_pulse = 1; + chip_info->bclk_wait = SPI_DEFAULT_PERIOD_WAIT; + chip_info->cs_control = null_cs_control; + } + } + + /* Now set controller state based on controller data */ + + if (first_setup) { + /* SPI loopback */ + if (chip_info->enable_loopback) + chip->test = SPI_TEST_LBC; + else + chip->test = 0; + + /* SPI dma driven */ + chip->enable_dma = chip_info->enable_dma; + + /* SPI /SS pulse between spi burst */ + if (chip_info->ins_ss_pulse) + u32_EDIT(chip->control, + SPI_CONTROL_SSCTL, SPI_CONTROL_SSCTL_1); + else + u32_EDIT(chip->control, + SPI_CONTROL_SSCTL, SPI_CONTROL_SSCTL_0); + + /* SPI bclk waits between each bits_per_word spi burst */ + if (chip_info->bclk_wait > SPI_PERIOD_MAX_WAIT) { + dev_err(&spi->dev, + "setup - " + "bclk_wait exceeds max allowed (%d)\n", + SPI_PERIOD_MAX_WAIT); + goto err_first_setup; + } + chip->period = SPI_PERIOD_CSRC_BCLK | + (chip_info->bclk_wait & SPI_PERIOD_WAIT); + } + + /* SPI mode */ + tmp = spi->mode; + if (tmp & SPI_LSB_FIRST) { + status = -EINVAL; + if (first_setup) { + dev_err(&spi->dev, + "setup - " + "HW doesn't support LSB first transfer\n"); + goto err_first_setup; + } else { + dev_err(&spi->dev, + "setup - " + "HW doesn't support LSB first transfer, " + "default to MSB first\n"); + spi->mode &= ~SPI_LSB_FIRST; + } + } + if (tmp & SPI_CS_HIGH) { + u32_EDIT(chip->control, + SPI_CONTROL_SSPOL, SPI_CONTROL_SSPOL_ACT_HIGH); + } + switch (tmp & SPI_MODE_3) { + case SPI_MODE_0: + tmp = 0; + break; + case SPI_MODE_1: + tmp = SPI_CONTROL_PHA_1; + break; + case SPI_MODE_2: + tmp = SPI_CONTROL_POL_ACT_LOW; + break; + default: + /* SPI_MODE_3 */ + tmp = SPI_CONTROL_PHA_1 | SPI_CONTROL_POL_ACT_LOW; + break; + } + u32_EDIT(chip->control, SPI_CONTROL_POL | SPI_CONTROL_PHA, tmp); + + /* SPI word width */ + tmp = spi->bits_per_word; + if (tmp == 0) { + tmp = 8; + spi->bits_per_word = 8; + } else if (tmp > 16) { + status = -EINVAL; + dev_err(&spi->dev, + "setup - " + "invalid bits_per_word (%d)\n", + tmp); + if (first_setup) + goto err_first_setup; + else { + /* Undo setup using chip as backup copy */ + tmp = chip->bits_per_word; + spi->bits_per_word = tmp; + } + } + chip->bits_per_word = tmp; + u32_EDIT(chip->control, SPI_CONTROL_BITCOUNT_MASK, tmp - 1); + chip->n_bytes = (tmp <= 8) ? 1 : 2; + + /* SPI datarate */ + tmp = spi_data_rate(spi->max_speed_hz); + if (tmp == SPI_CONTROL_DATARATE_BAD) { + status = -EINVAL; + dev_err(&spi->dev, + "setup - " + "HW min speed (%d Hz) exceeds required " + "max speed (%d Hz)\n", + spi_speed_hz(SPI_CONTROL_DATARATE_MIN), + spi->max_speed_hz); + if (first_setup) + goto err_first_setup; + else + /* Undo setup using chip as backup copy */ + spi->max_speed_hz = chip->max_speed_hz; + } else { + u32_EDIT(chip->control, SPI_CONTROL_DATARATE, tmp); + /* Actual rounded max_speed_hz */ + tmp = spi_speed_hz(tmp); + spi->max_speed_hz = tmp; + chip->max_speed_hz = tmp; + } + + /* SPI chip-select management */ + if (chip_info->cs_control) + chip->cs_control = chip_info->cs_control; + else + chip->cs_control = null_cs_control; + + /* Save controller_state */ + spi_set_ctldata(spi, chip); + + /* Summary */ + dev_dbg(&spi->dev, + "setup succeded\n" + " loopback enable = %s\n" + " dma enable = %s\n" + " insert /ss pulse = %s\n" + " period wait = %d\n" + " mode = %d\n" + " bits per word = %d\n" + " min speed = %d Hz\n" + " rounded max speed = %d Hz\n", + chip->test & SPI_TEST_LBC ? "Yes" : "No", + chip->enable_dma ? "Yes" : "No", + chip->control & SPI_CONTROL_SSCTL ? "Yes" : "No", + chip->period & SPI_PERIOD_WAIT, + spi->mode, + spi->bits_per_word, + spi_speed_hz(SPI_CONTROL_DATARATE_MIN), + spi->max_speed_hz); + +err_first_setup: + kfree(chip); + return status; +} + +static void cleanup(const struct spi_device *spi) +{ + struct chip_data *chip = spi_get_ctldata((struct spi_device *)spi); + kfree(chip); +} + +static int init_queue(struct driver_data *drv_data) +{ + INIT_LIST_HEAD(&drv_data->queue); + spin_lock_init(&drv_data->lock); + + drv_data->run = QUEUE_STOPPED; + drv_data->busy = 0; + + tasklet_init(&drv_data->pump_transfers, + pump_transfers, (unsigned long)drv_data); + + INIT_WORK(&drv_data->work, pump_messages); + drv_data->workqueue = create_singlethread_workqueue( + drv_data->master->cdev.dev->bus_id); + if (drv_data->workqueue == NULL) + return -EBUSY; + + return 0; +} + +static int start_queue(struct driver_data *drv_data) +{ + unsigned long flags; + + spin_lock_irqsave(&drv_data->lock, flags); + + if (drv_data->run == QUEUE_RUNNING || drv_data->busy) { + spin_unlock_irqrestore(&drv_data->lock, flags); + return -EBUSY; + } + + drv_data->run = QUEUE_RUNNING; + drv_data->cur_msg = NULL; + drv_data->cur_transfer = NULL; + drv_data->cur_chip = NULL; + spin_unlock_irqrestore(&drv_data->lock, flags); + + queue_work(drv_data->workqueue, &drv_data->work); + + return 0; +} + +static int stop_queue(struct driver_data *drv_data) +{ + unsigned long flags; + unsigned limit = 500; + int status = 0; + + spin_lock_irqsave(&drv_data->lock, flags); + + /* This is a bit lame, but is optimized for the common execution path. + * A wait_queue on the drv_data->busy could be used, but then the common + * execution path (pump_messages) would be required to call wake_up or + * friends on every SPI message. Do this instead */ + drv_data->run = QUEUE_STOPPED; + while (!list_empty(&drv_data->queue) && drv_data->busy && limit--) { + spin_unlock_irqrestore(&drv_data->lock, flags); + msleep(10); + spin_lock_irqsave(&drv_data->lock, flags); + } + + if (!list_empty(&drv_data->queue) || drv_data->busy) + status = -EBUSY; + + spin_unlock_irqrestore(&drv_data->lock, flags); + + return status; +} + +static int destroy_queue(struct driver_data *drv_data) +{ + int status; + + status = stop_queue(drv_data); + if (status != 0) + return status; + + if (drv_data->workqueue) + destroy_workqueue(drv_data->workqueue); + + return 0; +} + +static int spi_imx_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct spi_imx_master *platform_info; + struct spi_master *master; + struct driver_data *drv_data = NULL; + struct resource *res; + int irq, status = 0; + + platform_info = dev->platform_data; + if (platform_info == NULL) { + dev_err(&pdev->dev, "probe - no platform data supplied\n"); + status = -ENODEV; + goto err_no_pdata; + } + + /* Allocate master with space for drv_data */ + master = spi_alloc_master(dev, sizeof(struct driver_data)); + if (!master) { + dev_err(&pdev->dev, "probe - cannot alloc spi_master\n"); + status = -ENOMEM; + goto err_no_mem; + } + drv_data = spi_master_get_devdata(master); + drv_data->master = master; + drv_data->master_info = platform_info; + drv_data->pdev = pdev; + + master->bus_num = pdev->id; + master->num_chipselect = platform_info->num_chipselect; + master->cleanup = cleanup; + master->setup = setup; + master->transfer = transfer; + + drv_data->dummy_dma_buf = SPI_DUMMY_u32; + + /* Find and map resources */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "probe - MEM resources not defined\n"); + status = -ENODEV; + goto err_no_iores; + } + drv_data->ioarea = request_mem_region(res->start, + res->end - res->start + 1, + pdev->name); + if (drv_data->ioarea == NULL) { + dev_err(&pdev->dev, "probe - cannot reserve region\n"); + status = -ENXIO; + goto err_no_iores; + } + drv_data->regs = ioremap(res->start, res->end - res->start + 1); + if (drv_data->regs == NULL) { + dev_err(&pdev->dev, "probe - cannot map IO\n"); + status = -ENXIO; + goto err_no_iomap; + } + drv_data->rd_data_phys = (dma_addr_t)res->start; + + /* Attach to IRQ */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "probe - IRQ resource not defined\n"); + status = -ENODEV; + goto err_no_irqres; + } + status = request_irq(irq, spi_int, IRQF_DISABLED, dev->bus_id, drv_data); + if (status < 0) { + dev_err(&pdev->dev, "probe - cannot get IRQ (%d)\n", status); + goto err_no_irqres; + } + + /* Setup DMA if requested */ + drv_data->tx_channel = -1; + drv_data->rx_channel = -1; + if (platform_info->enable_dma) { + /* Get rx DMA channel */ + status = imx_dma_request_by_prio(&drv_data->rx_channel, + "spi_imx_rx", DMA_PRIO_HIGH); + if (status < 0) { + dev_err(dev, + "probe - problem (%d) requesting rx channel\n", + status); + goto err_no_rxdma; + } else + imx_dma_setup_handlers(drv_data->rx_channel, NULL, + dma_err_handler, drv_data); + + /* Get tx DMA channel */ + status = imx_dma_request_by_prio(&drv_data->tx_channel, + "spi_imx_tx", DMA_PRIO_MEDIUM); + if (status < 0) { + dev_err(dev, + "probe - problem (%d) requesting tx channel\n", + status); + imx_dma_free(drv_data->rx_channel); + goto err_no_txdma; + } else + imx_dma_setup_handlers(drv_data->tx_channel, + dma_tx_handler, dma_err_handler, + drv_data); + + /* Set request source and burst length for allocated channels */ + switch (drv_data->pdev->id) { + case 1: + /* Using SPI1 */ + RSSR(drv_data->rx_channel) = DMA_REQ_SPI1_R; + RSSR(drv_data->tx_channel) = DMA_REQ_SPI1_T; + break; + case 2: + /* Using SPI2 */ + RSSR(drv_data->rx_channel) = DMA_REQ_SPI2_R; + RSSR(drv_data->tx_channel) = DMA_REQ_SPI2_T; + break; + default: + dev_err(dev, "probe - bad SPI Id\n"); + imx_dma_free(drv_data->rx_channel); + imx_dma_free(drv_data->tx_channel); + status = -ENODEV; + goto err_no_devid; + } + BLR(drv_data->rx_channel) = SPI_DMA_BLR; + BLR(drv_data->tx_channel) = SPI_DMA_BLR; + } + + /* Load default SPI configuration */ + writel(SPI_RESET_START, drv_data->regs + SPI_RESET); + writel(0, drv_data->regs + SPI_RESET); + writel(SPI_DEFAULT_CONTROL, drv_data->regs + SPI_CONTROL); + + /* Initial and start queue */ + status = init_queue(drv_data); + if (status != 0) { + dev_err(&pdev->dev, "probe - problem initializing queue\n"); + goto err_init_queue; + } + status = start_queue(drv_data); + if (status != 0) { + dev_err(&pdev->dev, "probe - problem starting queue\n"); + goto err_start_queue; + } + + /* Register with the SPI framework */ + platform_set_drvdata(pdev, drv_data); + status = spi_register_master(master); + if (status != 0) { + dev_err(&pdev->dev, "probe - problem registering spi master\n"); + goto err_spi_register; + } + + dev_dbg(dev, "probe succeded\n"); + return 0; + +err_init_queue: +err_start_queue: +err_spi_register: + destroy_queue(drv_data); + +err_no_rxdma: +err_no_txdma: +err_no_devid: + free_irq(irq, drv_data); + +err_no_irqres: + iounmap(drv_data->regs); + +err_no_iomap: + release_resource(drv_data->ioarea); + kfree(drv_data->ioarea); + +err_no_iores: + spi_master_put(master); + +err_no_pdata: +err_no_mem: + return status; +} + +static int __devexit spi_imx_remove(struct platform_device *pdev) +{ + struct driver_data *drv_data = platform_get_drvdata(pdev); + int irq; + int status = 0; + + if (!drv_data) + return 0; + + tasklet_kill(&drv_data->pump_transfers); + + /* Remove the queue */ + status = destroy_queue(drv_data); + if (status != 0) { + dev_err(&pdev->dev, "queue remove failed (%d)\n", status); + return status; + } + + /* Reset SPI */ + writel(SPI_RESET_START, drv_data->regs + SPI_RESET); + writel(0, drv_data->regs + SPI_RESET); + + /* Release DMA */ + if (drv_data->master_info->enable_dma) { + RSSR(drv_data->rx_channel) = 0; + RSSR(drv_data->tx_channel) = 0; + imx_dma_free(drv_data->tx_channel); + imx_dma_free(drv_data->rx_channel); + } + + /* Release IRQ */ + irq = platform_get_irq(pdev, 0); + if (irq >= 0) + free_irq(irq, drv_data); + + /* Release map resources */ + iounmap(drv_data->regs); + release_resource(drv_data->ioarea); + kfree(drv_data->ioarea); + + /* Disconnect from the SPI framework */ + spi_unregister_master(drv_data->master); + spi_master_put(drv_data->master); + + /* Prevent double remove */ + platform_set_drvdata(pdev, NULL); + + dev_dbg(&pdev->dev, "remove succeded\n"); + + return 0; +} + +static void spi_imx_shutdown(struct platform_device *pdev) +{ + struct driver_data *drv_data = platform_get_drvdata(pdev); + + /* Reset SPI */ + writel(SPI_RESET_START, drv_data->regs + SPI_RESET); + writel(0, drv_data->regs + SPI_RESET); + + dev_dbg(&pdev->dev, "shutdown succeded\n"); +} + +#ifdef CONFIG_PM +static int suspend_devices(struct device *dev, void *pm_message) +{ + pm_message_t *state = pm_message; + + if (dev->power.power_state.event != state->event) { + dev_warn(dev, "pm state does not match request\n"); + return -1; + } + + return 0; +} + +static int spi_imx_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct driver_data *drv_data = platform_get_drvdata(pdev); + int status = 0; + + status = stop_queue(drv_data); + if (status != 0) { + dev_warn(&pdev->dev, "suspend cannot stop queue\n"); + return status; + } + + dev_dbg(&pdev->dev, "suspended\n"); + + return 0; +} + +static int spi_imx_resume(struct platform_device *pdev) +{ + struct driver_data *drv_data = platform_get_drvdata(pdev); + int status = 0; + + /* Start the queue running */ + status = start_queue(drv_data); + if (status != 0) + dev_err(&pdev->dev, "problem starting queue (%d)\n", status); + else + dev_dbg(&pdev->dev, "resumed\n"); + + return status; +} +#else +#define spi_imx_suspend NULL +#define spi_imx_resume NULL +#endif /* CONFIG_PM */ + +static struct platform_driver driver = { + .driver = { + .name = "imx-spi", + .bus = &platform_bus_type, + .owner = THIS_MODULE, + }, + .probe = spi_imx_probe, + .remove = __devexit_p(spi_imx_remove), + .shutdown = spi_imx_shutdown, + .suspend = spi_imx_suspend, + .resume = spi_imx_resume, +}; + +static int __init spi_imx_init(void) +{ + return platform_driver_register(&driver); +} +module_init(spi_imx_init); + +static void __exit spi_imx_exit(void) +{ + platform_driver_unregister(&driver); +} +module_exit(spi_imx_exit); + +MODULE_AUTHOR("Andrea Paterniani, <a.paterniani@swapp-eng.it>"); +MODULE_DESCRIPTION("iMX SPI Contoller Driver"); +MODULE_LICENSE("GPL"); |