diff options
author | Steve French <sfrench@us.ibm.com> | 2007-07-19 04:38:57 +0400 |
---|---|---|
committer | Steve French <sfrench@us.ibm.com> | 2007-07-19 04:38:57 +0400 |
commit | 1ff8392c32a2645d2665ca779ecb91bb29361c13 (patch) | |
tree | 860b95e9a499ade4060848740fc6ce1fbb4e4e8d /drivers/spi | |
parent | 70b315b0dd3879cb3ab8aadffb14f10b2d19b9c3 (diff) | |
parent | 5bae7ac9feba925fd0099057f6b23d7be80b7b41 (diff) | |
download | linux-1ff8392c32a2645d2665ca779ecb91bb29361c13.tar.xz |
Merge branch 'master' of /pub/scm/linux/kernel/git/torvalds/linux-2.6
Conflicts:
fs/cifs/export.c
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/Kconfig | 45 | ||||
-rw-r--r-- | drivers/spi/Makefile | 5 | ||||
-rw-r--r-- | drivers/spi/at25.c | 7 | ||||
-rw-r--r-- | drivers/spi/atmel_spi.c | 185 | ||||
-rw-r--r-- | drivers/spi/au1550_spi.c | 9 | ||||
-rw-r--r-- | drivers/spi/mpc52xx_psc_spi.c | 9 | ||||
-rw-r--r-- | drivers/spi/omap2_mcspi.c | 1081 | ||||
-rw-r--r-- | drivers/spi/omap_uwire.c | 9 | ||||
-rw-r--r-- | drivers/spi/pxa2xx_spi.c | 9 | ||||
-rw-r--r-- | drivers/spi/spi.c | 11 | ||||
-rw-r--r-- | drivers/spi/spi_bitbang.c | 8 | ||||
-rw-r--r-- | drivers/spi/spi_imx.c | 24 | ||||
-rw-r--r-- | drivers/spi/spi_lm70llp.c | 361 | ||||
-rw-r--r-- | drivers/spi/spi_mpc83xx.c | 47 | ||||
-rw-r--r-- | drivers/spi/spi_s3c24xx.c | 8 | ||||
-rw-r--r-- | drivers/spi/spi_txx9.c | 474 | ||||
-rw-r--r-- | drivers/spi/spidev.c | 6 | ||||
-rw-r--r-- | drivers/spi/tle62x0.c | 328 | ||||
-rw-r--r-- | drivers/spi/xilinx_spi.c | 434 |
19 files changed, 2989 insertions, 71 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 5e3f748f2693..b91571122daa 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -107,6 +107,15 @@ config SPI_IMX This enables using the Freescale iMX SPI controller in master mode. +config SPI_LM70_LLP + tristate "Parallel port adapter for LM70 eval board (DEVELOPMENT)" + depends on SPI_MASTER && PARPORT && EXPERIMENTAL + select SPI_BITBANG + help + This driver supports the NS LM70 LLP Evaluation Board, + which interfaces to an LM70 temperature sensor using + a parallel port. + config SPI_MPC52xx_PSC tristate "Freescale MPC52xx PSC SPI controller" depends on SPI_MASTER && PPC_MPC52xx && EXPERIMENTAL @@ -133,6 +142,12 @@ config SPI_OMAP_UWIRE help This hooks up to the MicroWire controller on OMAP1 chips. +config SPI_OMAP24XX + tristate "McSPI driver for OMAP24xx" + depends on SPI_MASTER && ARCH_OMAP24XX + help + SPI master controller for OMAP24xx Multichannel SPI + (McSPI) modules. config SPI_PXA2XX tristate "PXA2xx SSP SPI master" @@ -145,17 +160,36 @@ config SPI_PXA2XX config SPI_S3C24XX tristate "Samsung S3C24XX series SPI" depends on SPI_MASTER && ARCH_S3C2410 && EXPERIMENTAL + select SPI_BITBANG 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 + depends on SPI_MASTER && ARCH_S3C2410 && EXPERIMENTAL + select SPI_BITBANG help SPI driver for Samsung S3C24XX series ARM SoCs using GPIO lines to provide the SPI bus. This can be used where the inbuilt hardware cannot provide the transfer mode, or where the board is using non hardware connected pins. + +config SPI_TXX9 + tristate "Toshiba TXx9 SPI controller" + depends on SPI_MASTER && GENERIC_GPIO && CPU_TX49XX + help + SPI driver for Toshiba TXx9 MIPS SoCs + +config SPI_XILINX + tristate "Xilinx SPI controller" + depends on SPI_MASTER && XILINX_VIRTEX && EXPERIMENTAL + select SPI_BITBANG + 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. + # # Add new SPI master controllers in alphabetical order above this line # @@ -187,6 +221,15 @@ config SPI_SPIDEV Note that this application programming interface is EXPERIMENTAL and hence SUBJECT TO CHANGE WITHOUT NOTICE while it stabilizes. +config SPI_TLE62X0 + tristate "Infineon TLE62X0 (for power switching)" + depends on SPI_MASTER && SYSFS + help + SPI driver for Infineon TLE62X0 series line driver chips, + such as the TLE6220, TLE6230 and TLE6240. This provides a + sysfs interface, with each line presented as a kind of GPIO + exposing both switch control and diagnostic feedback. + # # Add new SPI protocol masters in alphabetical order above this line # diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 5788d867de84..41fbac45c323 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -17,17 +17,22 @@ obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o obj-$(CONFIG_SPI_AU1550) += au1550_spi.o obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o obj-$(CONFIG_SPI_IMX) += spi_imx.o +obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o +obj-$(CONFIG_SPI_OMAP24XX) += omap2_mcspi.o obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o obj-$(CONFIG_SPI_MPC83xx) += spi_mpc83xx.o 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 # ... add above this line ... # SPI protocol drivers (device/link on bus) obj-$(CONFIG_SPI_AT25) += at25.o obj-$(CONFIG_SPI_SPIDEV) += spidev.o +obj-$(CONFIG_SPI_TLE62X0) += tle62x0.o # ... add above this line ... # SPI slave controller drivers (upstream link) diff --git a/drivers/spi/at25.c b/drivers/spi/at25.c index 8efa07e8b8c2..e007833cca59 100644 --- a/drivers/spi/at25.c +++ b/drivers/spi/at25.c @@ -111,7 +111,8 @@ at25_ee_read( } static ssize_t -at25_bin_read(struct kobject *kobj, char *buf, loff_t off, size_t count) +at25_bin_read(struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct device *dev; struct at25_data *at25; @@ -236,7 +237,8 @@ at25_ee_write(struct at25_data *at25, char *buf, loff_t off, size_t count) } static ssize_t -at25_bin_write(struct kobject *kobj, char *buf, loff_t off, size_t count) +at25_bin_write(struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct device *dev; struct at25_data *at25; @@ -314,7 +316,6 @@ static int at25_probe(struct spi_device *spi) */ 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; diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index 8b2601de3630..ad144054da30 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -46,6 +46,7 @@ struct atmel_spi { struct clk *clk; struct platform_device *pdev; unsigned new_1:1; + struct spi_device *stay; u8 stopping; struct list_head queue; @@ -62,29 +63,62 @@ struct atmel_spi { /* * Earlier SPI controllers (e.g. on at91rm9200) have a design bug whereby * they assume that spi slave device state will not change on deselect, so - * that automagic deselection is OK. Not so! Workaround uses nCSx pins - * as GPIOs; or newer controllers have CSAAT and friends. + * that automagic deselection is OK. ("NPCSx rises if no data is to be + * transmitted") Not so! Workaround uses nCSx pins as GPIOs; or newer + * controllers have CSAAT and friends. * - * Since the CSAAT functionality is a bit weird on newer controllers - * as well, we use GPIO to control nCSx pins on all controllers. + * Since the CSAAT functionality is a bit weird on newer controllers as + * well, we use GPIO to control nCSx pins on all controllers, updating + * MR.PCS to avoid confusing the controller. Using GPIOs also lets us + * support active-high chipselects despite the controller's belief that + * only active-low devices/systems exists. + * + * However, at91rm9200 has a second erratum whereby nCS0 doesn't work + * right when driven with GPIO. ("Mode Fault does not allow more than one + * Master on Chip Select 0.") No workaround exists for that ... so for + * nCS0 on that chip, we (a) don't use the GPIO, (b) can't support CS_HIGH, + * and (c) will trigger that first erratum in some cases. */ -static inline void cs_activate(struct spi_device *spi) +static void cs_activate(struct atmel_spi *as, struct spi_device *spi) { unsigned gpio = (unsigned) spi->controller_data; unsigned active = spi->mode & SPI_CS_HIGH; + u32 mr; + + mr = spi_readl(as, MR); + mr = SPI_BFINS(PCS, ~(1 << spi->chip_select), mr); - dev_dbg(&spi->dev, "activate %u%s\n", gpio, active ? " (high)" : ""); - gpio_set_value(gpio, active); + dev_dbg(&spi->dev, "activate %u%s, mr %08x\n", + gpio, active ? " (high)" : "", + mr); + + if (!(cpu_is_at91rm9200() && spi->chip_select == 0)) + gpio_set_value(gpio, active); + spi_writel(as, MR, mr); } -static inline void cs_deactivate(struct spi_device *spi) +static void cs_deactivate(struct atmel_spi *as, struct spi_device *spi) { unsigned gpio = (unsigned) spi->controller_data; unsigned active = spi->mode & SPI_CS_HIGH; + u32 mr; - dev_dbg(&spi->dev, "DEactivate %u%s\n", gpio, active ? " (low)" : ""); - gpio_set_value(gpio, !active); + /* only deactivate *this* device; sometimes transfers to + * another device may be active when this routine is called. + */ + mr = spi_readl(as, MR); + if (~SPI_BFEXT(PCS, mr) & (1 << spi->chip_select)) { + mr = SPI_BFINS(PCS, 0xf, mr); + spi_writel(as, MR, mr); + } + + dev_dbg(&spi->dev, "DEactivate %u%s, mr %08x\n", + gpio, active ? " (low)" : "", + mr); + + if (!(cpu_is_at91rm9200() && spi->chip_select == 0)) + gpio_set_value(gpio, !active); } /* @@ -140,6 +174,7 @@ static void atmel_spi_next_xfer(struct spi_master *master, /* REVISIT: when xfer->delay_usecs == 0, the PDC "next transfer" * mechanism might help avoid the IRQ latency between transfers + * (and improve the nCS0 errata handling on at91rm9200 chips) * * We're also waiting for ENDRX before we start the next * transfer because we need to handle some difficult timing @@ -169,33 +204,62 @@ static void atmel_spi_next_message(struct spi_master *master) { struct atmel_spi *as = spi_master_get_devdata(master); struct spi_message *msg; - u32 mr; + struct spi_device *spi; BUG_ON(as->current_transfer); msg = list_entry(as->queue.next, struct spi_message, queue); + spi = msg->spi; - /* Select the chip */ - mr = spi_readl(as, MR); - mr = SPI_BFINS(PCS, ~(1 << msg->spi->chip_select), mr); - spi_writel(as, MR, mr); - cs_activate(msg->spi); + dev_dbg(master->cdev.dev, "start message %p for %s\n", + msg, spi->dev.bus_id); + + /* select chip if it's not still active */ + if (as->stay) { + if (as->stay != spi) { + cs_deactivate(as, as->stay); + cs_activate(as, spi); + } + as->stay = NULL; + } else + cs_activate(as, spi); atmel_spi_next_xfer(master, msg); } -static void +/* + * For DMA, tx_buf/tx_dma have the same relationship as rx_buf/rx_dma: + * - The buffer is either valid for CPU access, else NULL + * - If the buffer is valid, so is its DMA addresss + * + * This driver manages the dma addresss unless message->is_dma_mapped. + */ +static int atmel_spi_dma_map_xfer(struct atmel_spi *as, struct spi_transfer *xfer) { + struct device *dev = &as->pdev->dev; + xfer->tx_dma = xfer->rx_dma = INVALID_DMA_ADDRESS; - if (xfer->tx_buf) - xfer->tx_dma = dma_map_single(&as->pdev->dev, + if (xfer->tx_buf) { + xfer->tx_dma = dma_map_single(dev, (void *) xfer->tx_buf, xfer->len, DMA_TO_DEVICE); - if (xfer->rx_buf) - xfer->rx_dma = dma_map_single(&as->pdev->dev, + if (dma_mapping_error(xfer->tx_dma)) + return -ENOMEM; + } + if (xfer->rx_buf) { + xfer->rx_dma = dma_map_single(dev, xfer->rx_buf, xfer->len, DMA_FROM_DEVICE); + if (dma_mapping_error(xfer->tx_dma)) { + if (xfer->tx_buf) + dma_unmap_single(dev, + xfer->tx_dma, xfer->len, + DMA_TO_DEVICE); + return -ENOMEM; + } + } + return 0; } static void atmel_spi_dma_unmap_xfer(struct spi_master *master, @@ -211,9 +275,13 @@ static void atmel_spi_dma_unmap_xfer(struct spi_master *master, static void atmel_spi_msg_done(struct spi_master *master, struct atmel_spi *as, - struct spi_message *msg, int status) + struct spi_message *msg, int status, int stay) { - cs_deactivate(msg->spi); + if (!stay || status < 0) + cs_deactivate(as, msg->spi); + else + as->stay = msg->spi; + list_del(&msg->queue); msg->status = status; @@ -303,7 +371,7 @@ atmel_spi_interrupt(int irq, void *dev_id) /* Clear any overrun happening while cleaning up */ spi_readl(as, SR); - atmel_spi_msg_done(master, as, msg, -EIO); + atmel_spi_msg_done(master, as, msg, -EIO, 0); } else if (pending & SPI_BIT(ENDRX)) { ret = IRQ_HANDLED; @@ -321,12 +389,13 @@ atmel_spi_interrupt(int irq, void *dev_id) if (msg->transfers.prev == &xfer->transfer_list) { /* report completed message */ - atmel_spi_msg_done(master, as, msg, 0); + atmel_spi_msg_done(master, as, msg, 0, + xfer->cs_change); } else { if (xfer->cs_change) { - cs_deactivate(msg->spi); + cs_deactivate(as, msg->spi); udelay(1); - cs_activate(msg->spi); + cs_activate(as, msg->spi); } /* @@ -350,6 +419,7 @@ atmel_spi_interrupt(int irq, void *dev_id) return ret; } +/* the spi->mode bits understood by this driver: */ #define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH) static int atmel_spi_setup(struct spi_device *spi) @@ -388,6 +458,14 @@ static int atmel_spi_setup(struct spi_device *spi) return -EINVAL; } + /* see notes above re chipselect */ + if (cpu_is_at91rm9200() + && spi->chip_select == 0 + && (spi->mode & SPI_CS_HIGH)) { + dev_dbg(&spi->dev, "setup: can't be active-high\n"); + return -EINVAL; + } + /* speed zero convention is used by some upper layers */ bus_hz = clk_get_rate(as->clk); if (spi->max_speed_hz) { @@ -397,8 +475,9 @@ static int atmel_spi_setup(struct spi_device *spi) scbr = ((bus_hz + spi->max_speed_hz - 1) / spi->max_speed_hz); if (scbr >= (1 << SPI_SCBR_SIZE)) { - dev_dbg(&spi->dev, "setup: %d Hz too slow, scbr %u\n", - spi->max_speed_hz, scbr); + dev_dbg(&spi->dev, + "setup: %d Hz too slow, scbr %u; min %ld Hz\n", + spi->max_speed_hz, scbr, bus_hz/255); return -EINVAL; } } else @@ -423,6 +502,14 @@ static int atmel_spi_setup(struct spi_device *spi) return ret; spi->controller_state = (void *)npcs_pin; gpio_direction_output(npcs_pin, !(spi->mode & SPI_CS_HIGH)); + } else { + unsigned long flags; + + spin_lock_irqsave(&as->lock, flags); + if (as->stay == spi) + as->stay = NULL; + cs_deactivate(as, spi); + spin_unlock_irqrestore(&as->lock, flags); } dev_dbg(&spi->dev, @@ -464,14 +551,22 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg) dev_dbg(&spi->dev, "no protocol options yet\n"); return -ENOPROTOOPT; } - } - /* scrub dcache "early" */ - if (!msg->is_dma_mapped) { - list_for_each_entry(xfer, &msg->transfers, transfer_list) - atmel_spi_dma_map_xfer(as, xfer); + /* + * DMA map early, for performance (empties dcache ASAP) and + * better fault reporting. This is a DMA-only driver. + * + * NOTE that if dma_unmap_single() ever starts to do work on + * platforms supported by this driver, we would need to clean + * up mappings for previously-mapped transfers. + */ + if (!msg->is_dma_mapped) { + if (atmel_spi_dma_map_xfer(as, xfer) < 0) + return -ENOMEM; + } } +#ifdef VERBOSE list_for_each_entry(xfer, &msg->transfers, transfer_list) { dev_dbg(controller, " xfer %p: len %u tx %p/%08x rx %p/%08x\n", @@ -479,6 +574,7 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg) xfer->tx_buf, xfer->tx_dma, xfer->rx_buf, xfer->rx_dma); } +#endif msg->status = -EINPROGRESS; msg->actual_length = 0; @@ -494,8 +590,21 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg) static void atmel_spi_cleanup(struct spi_device *spi) { - if (spi->controller_state) - gpio_free((unsigned int)spi->controller_data); + struct atmel_spi *as = spi_master_get_devdata(spi->master); + unsigned gpio = (unsigned) spi->controller_data; + unsigned long flags; + + if (!spi->controller_state) + return; + + spin_lock_irqsave(&as->lock, flags); + if (as->stay == spi) { + as->stay = NULL; + cs_deactivate(as, spi); + } + spin_unlock_irqrestore(&as->lock, flags); + + gpio_free(gpio); } /*-------------------------------------------------------------------------*/ @@ -536,6 +645,10 @@ static int __init atmel_spi_probe(struct platform_device *pdev) as = spi_master_get_devdata(master); + /* + * Scratch buffer is used for throwaway rx and tx data. + * It's coherent to minimize dcache pollution. + */ as->buffer = dma_alloc_coherent(&pdev->dev, BUFFER_SIZE, &as->buffer_dma, GFP_KERNEL); if (!as->buffer) diff --git a/drivers/spi/au1550_spi.c b/drivers/spi/au1550_spi.c index ae2b1af0dba4..c47a650183a1 100644 --- a/drivers/spi/au1550_spi.c +++ b/drivers/spi/au1550_spi.c @@ -280,6 +280,9 @@ static int au1550_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t) return 0; } +/* the spi->mode bits understood by this driver: */ +#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST) + static int au1550_spi_setup(struct spi_device *spi) { struct au1550_spi *hw = spi_master_get_devdata(spi->master); @@ -292,6 +295,12 @@ static int au1550_spi_setup(struct spi_device *spi) return -EINVAL; } + if (spi->mode & ~MODEBITS) { + dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n", + spi->mode & ~MODEBITS); + return -EINVAL; + } + if (spi->max_speed_hz == 0) spi->max_speed_hz = hw->freq_max; if (spi->max_speed_hz > hw->freq_max diff --git a/drivers/spi/mpc52xx_psc_spi.c b/drivers/spi/mpc52xx_psc_spi.c index 11f36bef3057..d2a4b2bdb07b 100644 --- a/drivers/spi/mpc52xx_psc_spi.c +++ b/drivers/spi/mpc52xx_psc_spi.c @@ -270,6 +270,9 @@ static void mpc52xx_psc_spi_work(struct work_struct *work) spin_unlock_irq(&mps->lock); } +/* the spi->mode bits understood by this driver: */ +#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST) + static int mpc52xx_psc_spi_setup(struct spi_device *spi) { struct mpc52xx_psc_spi *mps = spi_master_get_devdata(spi->master); @@ -279,6 +282,12 @@ static int mpc52xx_psc_spi_setup(struct spi_device *spi) if (spi->bits_per_word%8) return -EINVAL; + if (spi->mode & ~MODEBITS) { + dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n", + spi->mode & ~MODEBITS); + return -EINVAL; + } + if (!cs) { cs = kzalloc(sizeof *cs, GFP_KERNEL); if (!cs) diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c new file mode 100644 index 000000000000..6b357cdb9ea3 --- /dev/null +++ b/drivers/spi/omap2_mcspi.c @@ -0,0 +1,1081 @@ +/* + * OMAP2 McSPI controller 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/dma-mapping.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include <linux/spi/spi.h> + +#include <asm/arch/dma.h> +#include <asm/arch/clock.h> + + +#define OMAP2_MCSPI_MAX_FREQ 48000000 + +#define OMAP2_MCSPI_REVISION 0x00 +#define OMAP2_MCSPI_SYSCONFIG 0x10 +#define OMAP2_MCSPI_SYSSTATUS 0x14 +#define OMAP2_MCSPI_IRQSTATUS 0x18 +#define OMAP2_MCSPI_IRQENABLE 0x1c +#define OMAP2_MCSPI_WAKEUPENABLE 0x20 +#define OMAP2_MCSPI_SYST 0x24 +#define OMAP2_MCSPI_MODULCTRL 0x28 + +/* per-channel banks, 0x14 bytes each, first is: */ +#define OMAP2_MCSPI_CHCONF0 0x2c +#define OMAP2_MCSPI_CHSTAT0 0x30 +#define OMAP2_MCSPI_CHCTRL0 0x34 +#define OMAP2_MCSPI_TX0 0x38 +#define OMAP2_MCSPI_RX0 0x3c + +/* per-register bitmasks: */ + +#define OMAP2_MCSPI_SYSCONFIG_AUTOIDLE (1 << 0) +#define OMAP2_MCSPI_SYSCONFIG_SOFTRESET (1 << 1) + +#define OMAP2_MCSPI_SYSSTATUS_RESETDONE (1 << 0) + +#define OMAP2_MCSPI_MODULCTRL_SINGLE (1 << 0) +#define OMAP2_MCSPI_MODULCTRL_MS (1 << 2) +#define OMAP2_MCSPI_MODULCTRL_STEST (1 << 3) + +#define OMAP2_MCSPI_CHCONF_PHA (1 << 0) +#define OMAP2_MCSPI_CHCONF_POL (1 << 1) +#define OMAP2_MCSPI_CHCONF_CLKD_MASK (0x0f << 2) +#define OMAP2_MCSPI_CHCONF_EPOL (1 << 6) +#define OMAP2_MCSPI_CHCONF_WL_MASK (0x1f << 7) +#define OMAP2_MCSPI_CHCONF_TRM_RX_ONLY (0x01 << 12) +#define OMAP2_MCSPI_CHCONF_TRM_TX_ONLY (0x02 << 12) +#define OMAP2_MCSPI_CHCONF_TRM_MASK (0x03 << 12) +#define OMAP2_MCSPI_CHCONF_DMAW (1 << 14) +#define OMAP2_MCSPI_CHCONF_DMAR (1 << 15) +#define OMAP2_MCSPI_CHCONF_DPE0 (1 << 16) +#define OMAP2_MCSPI_CHCONF_DPE1 (1 << 17) +#define OMAP2_MCSPI_CHCONF_IS (1 << 18) +#define OMAP2_MCSPI_CHCONF_TURBO (1 << 19) +#define OMAP2_MCSPI_CHCONF_FORCE (1 << 20) + +#define OMAP2_MCSPI_CHSTAT_RXS (1 << 0) +#define OMAP2_MCSPI_CHSTAT_TXS (1 << 1) +#define OMAP2_MCSPI_CHSTAT_EOT (1 << 2) + +#define OMAP2_MCSPI_CHCTRL_EN (1 << 0) + + +/* We have 2 DMA channels per CS, one for RX and one for TX */ +struct omap2_mcspi_dma { + int dma_tx_channel; + int dma_rx_channel; + + int dma_tx_sync_dev; + int dma_rx_sync_dev; + + struct completion dma_tx_completion; + struct completion dma_rx_completion; +}; + +/* use PIO for small transfers, avoiding DMA setup/teardown overhead and + * cache operations; better heuristics consider wordsize and bitrate. + */ +#define DMA_MIN_BYTES 8 + + +struct omap2_mcspi { + 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; + /* SPI1 has 4 channels, while SPI2 has 2 */ + struct omap2_mcspi_dma *dma_channels; +}; + +struct omap2_mcspi_cs { + void __iomem *base; + int word_len; +}; + +static struct workqueue_struct *omap2_mcspi_wq; + +#define MOD_REG_BIT(val, mask, set) do { \ + if (set) \ + val |= mask; \ + else \ + val &= ~mask; \ +} while (0) + +static inline void mcspi_write_reg(struct spi_master *master, + int idx, u32 val) +{ + struct omap2_mcspi *mcspi = spi_master_get_devdata(master); + + __raw_writel(val, mcspi->base + idx); +} + +static inline u32 mcspi_read_reg(struct spi_master *master, int idx) +{ + struct omap2_mcspi *mcspi = spi_master_get_devdata(master); + + return __raw_readl(mcspi->base + idx); +} + +static inline void mcspi_write_cs_reg(const struct spi_device *spi, + int idx, u32 val) +{ + struct omap2_mcspi_cs *cs = spi->controller_state; + + __raw_writel(val, cs->base + idx); +} + +static inline u32 mcspi_read_cs_reg(const struct spi_device *spi, int idx) +{ + struct omap2_mcspi_cs *cs = spi->controller_state; + + return __raw_readl(cs->base + idx); +} + +static void omap2_mcspi_set_dma_req(const struct spi_device *spi, + int is_read, int enable) +{ + u32 l, rw; + + l = mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0); + + if (is_read) /* 1 is read, 0 write */ + rw = OMAP2_MCSPI_CHCONF_DMAR; + else + rw = OMAP2_MCSPI_CHCONF_DMAW; + + MOD_REG_BIT(l, rw, enable); + mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCONF0, l); +} + +static void omap2_mcspi_set_enable(const struct spi_device *spi, int enable) +{ + u32 l; + + l = enable ? OMAP2_MCSPI_CHCTRL_EN : 0; + mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCTRL0, l); +} + +static void omap2_mcspi_force_cs(struct spi_device *spi, int cs_active) +{ + u32 l; + + l = mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0); + MOD_REG_BIT(l, OMAP2_MCSPI_CHCONF_FORCE, cs_active); + mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCONF0, l); +} + +static void omap2_mcspi_set_master_mode(struct spi_master *master) +{ + u32 l; + + /* setup when switching from (reset default) slave mode + * to single-channel master mode + */ + l = mcspi_read_reg(master, OMAP2_MCSPI_MODULCTRL); + MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_STEST, 0); + MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_MS, 0); + MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_SINGLE, 1); + mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, l); +} + +static unsigned +omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) +{ + struct omap2_mcspi *mcspi; + struct omap2_mcspi_cs *cs = spi->controller_state; + struct omap2_mcspi_dma *mcspi_dma; + unsigned int count, c; + unsigned long base, tx_reg, rx_reg; + int word_len, data_type, element_count; + u8 * rx; + const u8 * tx; + + mcspi = spi_master_get_devdata(spi->master); + mcspi_dma = &mcspi->dma_channels[spi->chip_select]; + + count = xfer->len; + c = count; + word_len = cs->word_len; + + base = (unsigned long) io_v2p(cs->base); + tx_reg = base + OMAP2_MCSPI_TX0; + rx_reg = base + OMAP2_MCSPI_RX0; + rx = xfer->rx_buf; + tx = xfer->tx_buf; + + if (word_len <= 8) { + data_type = OMAP_DMA_DATA_TYPE_S8; + element_count = count; + } else if (word_len <= 16) { + data_type = OMAP_DMA_DATA_TYPE_S16; + element_count = count >> 1; + } else /* word_len <= 32 */ { + data_type = OMAP_DMA_DATA_TYPE_S32; + element_count = count >> 2; + } + + if (tx != NULL) { + omap_set_dma_transfer_params(mcspi_dma->dma_tx_channel, + data_type, element_count, 1, + OMAP_DMA_SYNC_ELEMENT, + mcspi_dma->dma_tx_sync_dev, 0); + + omap_set_dma_dest_params(mcspi_dma->dma_tx_channel, 0, + OMAP_DMA_AMODE_CONSTANT, + tx_reg, 0, 0); + + omap_set_dma_src_params(mcspi_dma->dma_tx_channel, 0, + OMAP_DMA_AMODE_POST_INC, + xfer->tx_dma, 0, 0); + } + + if (rx != NULL) { + omap_set_dma_transfer_params(mcspi_dma->dma_rx_channel, + data_type, element_count, 1, + OMAP_DMA_SYNC_ELEMENT, + mcspi_dma->dma_rx_sync_dev, 1); + + omap_set_dma_src_params(mcspi_dma->dma_rx_channel, 0, + OMAP_DMA_AMODE_CONSTANT, + rx_reg, 0, 0); + + omap_set_dma_dest_params(mcspi_dma->dma_rx_channel, 0, + OMAP_DMA_AMODE_POST_INC, + xfer->rx_dma, 0, 0); + } + + if (tx != NULL) { + omap_start_dma(mcspi_dma->dma_tx_channel); + omap2_mcspi_set_dma_req(spi, 0, 1); + } + + if (rx != NULL) { + omap_start_dma(mcspi_dma->dma_rx_channel); + omap2_mcspi_set_dma_req(spi, 1, 1); + } + + if (tx != NULL) { + wait_for_completion(&mcspi_dma->dma_tx_completion); + dma_unmap_single(NULL, xfer->tx_dma, count, DMA_TO_DEVICE); + } + + if (rx != NULL) { + wait_for_completion(&mcspi_dma->dma_rx_completion); + dma_unmap_single(NULL, xfer->rx_dma, count, DMA_FROM_DEVICE); + } + return count; +} + +static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit) +{ + unsigned long timeout; + + timeout = jiffies + msecs_to_jiffies(1000); + while (!(__raw_readl(reg) & bit)) { + if (time_after(jiffies, timeout)) + return -1; + cpu_relax(); + } + return 0; +} + +static unsigned +omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) +{ + struct omap2_mcspi *mcspi; + struct omap2_mcspi_cs *cs = spi->controller_state; + unsigned int count, c; + u32 l; + void __iomem *base = cs->base; + void __iomem *tx_reg; + void __iomem *rx_reg; + void __iomem *chstat_reg; + int word_len; + + mcspi = spi_master_get_devdata(spi->master); + count = xfer->len; + c = count; + word_len = cs->word_len; + + l = mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0); + l &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; + + /* We store the pre-calculated register addresses on stack to speed + * up the transfer loop. */ + tx_reg = base + OMAP2_MCSPI_TX0; + rx_reg = base + OMAP2_MCSPI_RX0; + chstat_reg = base + OMAP2_MCSPI_CHSTAT0; + + if (word_len <= 8) { + u8 *rx; + const u8 *tx; + + rx = xfer->rx_buf; + tx = xfer->tx_buf; + + do { + if (tx != NULL) { + if (mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_TXS) < 0) { + dev_err(&spi->dev, "TXS timed out\n"); + goto out; + } +#ifdef VERBOSE + dev_dbg(&spi->dev, "write-%d %02x\n", + word_len, *tx); +#endif + __raw_writel(*tx++, tx_reg); + } + if (rx != NULL) { + if (mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_RXS) < 0) { + dev_err(&spi->dev, "RXS timed out\n"); + goto out; + } + /* prevent last RX_ONLY read from triggering + * more word i/o: switch to rx+tx + */ + if (c == 0 && tx == NULL) + mcspi_write_cs_reg(spi, + OMAP2_MCSPI_CHCONF0, l); + *rx++ = __raw_readl(rx_reg); +#ifdef VERBOSE + dev_dbg(&spi->dev, "read-%d %02x\n", + word_len, *(rx - 1)); +#endif + } + c -= 1; + } while (c); + } else if (word_len <= 16) { + u16 *rx; + const u16 *tx; + + rx = xfer->rx_buf; + tx = xfer->tx_buf; + do { + if (tx != NULL) { + if (mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_TXS) < 0) { + dev_err(&spi->dev, "TXS timed out\n"); + goto out; + } +#ifdef VERBOSE + dev_dbg(&spi->dev, "write-%d %04x\n", + word_len, *tx); +#endif + __raw_writel(*tx++, tx_reg); + } + if (rx != NULL) { + if (mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_RXS) < 0) { + dev_err(&spi->dev, "RXS timed out\n"); + goto out; + } + /* prevent last RX_ONLY read from triggering + * more word i/o: switch to rx+tx + */ + if (c == 0 && tx == NULL) + mcspi_write_cs_reg(spi, + OMAP2_MCSPI_CHCONF0, l); + *rx++ = __raw_readl(rx_reg); +#ifdef VERBOSE + dev_dbg(&spi->dev, "read-%d %04x\n", + word_len, *(rx - 1)); +#endif + } + c -= 2; + } while (c); + } else if (word_len <= 32) { + u32 *rx; + const u32 *tx; + + rx = xfer->rx_buf; + tx = xfer->tx_buf; + do { + if (tx != NULL) { + if (mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_TXS) < 0) { + dev_err(&spi->dev, "TXS timed out\n"); + goto out; + } +#ifdef VERBOSE + dev_dbg(&spi->dev, "write-%d %04x\n", + word_len, *tx); +#endif + __raw_writel(*tx++, tx_reg); + } + if (rx != NULL) { + if (mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_RXS) < 0) { + dev_err(&spi->dev, "RXS timed out\n"); + goto out; + } + /* prevent last RX_ONLY read from triggering + * more word i/o: switch to rx+tx + */ + if (c == 0 && tx == NULL) + mcspi_write_cs_reg(spi, + OMAP2_MCSPI_CHCONF0, l); + *rx++ = __raw_readl(rx_reg); +#ifdef VERBOSE + dev_dbg(&spi->dev, "read-%d %04x\n", + word_len, *(rx - 1)); +#endif + } + c -= 4; + } while (c); + } + + /* for TX_ONLY mode, be sure all words have shifted out */ + if (xfer->rx_buf == NULL) { + if (mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_TXS) < 0) { + dev_err(&spi->dev, "TXS timed out\n"); + } else if (mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_EOT) < 0) + dev_err(&spi->dev, "EOT timed out\n"); + } +out: + return count - c; +} + +/* called only when no transfer is active to this device */ +static int omap2_mcspi_setup_transfer(struct spi_device *spi, + struct spi_transfer *t) +{ + struct omap2_mcspi_cs *cs = spi->controller_state; + struct omap2_mcspi *mcspi; + u32 l = 0, div = 0; + u8 word_len = spi->bits_per_word; + + mcspi = spi_master_get_devdata(spi->master); + + if (t != NULL && t->bits_per_word) + word_len = t->bits_per_word; + + cs->word_len = word_len; + + if (spi->max_speed_hz) { + while (div <= 15 && (OMAP2_MCSPI_MAX_FREQ / (1 << div)) + > spi->max_speed_hz) + div++; + } else + div = 15; + + l = mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0); + + /* standard 4-wire master mode: SCK, MOSI/out, MISO/in, nCS + * REVISIT: this controller could support SPI_3WIRE mode. + */ + l &= ~(OMAP2_MCSPI_CHCONF_IS|OMAP2_MCSPI_CHCONF_DPE1); + l |= OMAP2_MCSPI_CHCONF_DPE0; + + /* wordlength */ + l &= ~OMAP2_MCSPI_CHCONF_WL_MASK; + l |= (word_len - 1) << 7; + + /* set chipselect polarity; manage with FORCE */ + if (!(spi->mode & SPI_CS_HIGH)) + l |= OMAP2_MCSPI_CHCONF_EPOL; /* active-low; normal */ + else + l &= ~OMAP2_MCSPI_CHCONF_EPOL; + + /* set clock divisor */ + l &= ~OMAP2_MCSPI_CHCONF_CLKD_MASK; + l |= div << 2; + + /* set SPI mode 0..3 */ + if (spi->mode & SPI_CPOL) + l |= OMAP2_MCSPI_CHCONF_POL; + else + l &= ~OMAP2_MCSPI_CHCONF_POL; + if (spi->mode & SPI_CPHA) + l |= OMAP2_MCSPI_CHCONF_PHA; + else + l &= ~OMAP2_MCSPI_CHCONF_PHA; + + mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCONF0, l); + + dev_dbg(&spi->dev, "setup: speed %d, sample %s edge, clk %s\n", + OMAP2_MCSPI_MAX_FREQ / (1 << div), + (spi->mode & SPI_CPHA) ? "trailing" : "leading", + (spi->mode & SPI_CPOL) ? "inverted" : "normal"); + + return 0; +} + +static void omap2_mcspi_dma_rx_callback(int lch, u16 ch_status, void *data) +{ + struct spi_device *spi = data; + struct omap2_mcspi *mcspi; + struct omap2_mcspi_dma *mcspi_dma; + + mcspi = spi_master_get_devdata(spi->master); + mcspi_dma = &(mcspi->dma_channels[spi->chip_select]); + + complete(&mcspi_dma->dma_rx_completion); + + /* We must disable the DMA RX request */ + omap2_mcspi_set_dma_req(spi, 1, 0); +} + +static void omap2_mcspi_dma_tx_callback(int lch, u16 ch_status, void *data) +{ + struct spi_device *spi = data; + struct omap2_mcspi *mcspi; + struct omap2_mcspi_dma *mcspi_dma; + + mcspi = spi_master_get_devdata(spi->master); + mcspi_dma = &(mcspi->dma_channels[spi->chip_select]); + + complete(&mcspi_dma->dma_tx_completion); + + /* We must disable the DMA TX request */ + omap2_mcspi_set_dma_req(spi, 0, 0); +} + +static int omap2_mcspi_request_dma(struct spi_device *spi) +{ + struct spi_master *master = spi->master; + struct omap2_mcspi *mcspi; + struct omap2_mcspi_dma *mcspi_dma; + + mcspi = spi_master_get_devdata(master); + mcspi_dma = mcspi->dma_channels + spi->chip_select; + + if (omap_request_dma(mcspi_dma->dma_rx_sync_dev, "McSPI RX", + omap2_mcspi_dma_rx_callback, spi, + &mcspi_dma->dma_rx_channel)) { + dev_err(&spi->dev, "no RX DMA channel for McSPI\n"); + return -EAGAIN; + } + + if (omap_request_dma(mcspi_dma->dma_tx_sync_dev, "McSPI TX", + omap2_mcspi_dma_tx_callback, spi, + &mcspi_dma->dma_tx_channel)) { + omap_free_dma(mcspi_dma->dma_rx_channel); + mcspi_dma->dma_rx_channel = -1; + dev_err(&spi->dev, "no TX DMA channel for McSPI\n"); + return -EAGAIN; + } + + init_completion(&mcspi_dma->dma_rx_completion); + init_completion(&mcspi_dma->dma_tx_completion); + + return 0; +} + +/* the spi->mode bits understood by this driver: */ +#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH) + +static int omap2_mcspi_setup(struct spi_device *spi) +{ + int ret; + struct omap2_mcspi *mcspi; + struct omap2_mcspi_dma *mcspi_dma; + struct omap2_mcspi_cs *cs = spi->controller_state; + + if (spi->mode & ~MODEBITS) { + dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n", + spi->mode & ~MODEBITS); + return -EINVAL; + } + + if (spi->bits_per_word == 0) + spi->bits_per_word = 8; + else 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; + } + + mcspi = spi_master_get_devdata(spi->master); + mcspi_dma = &mcspi->dma_channels[spi->chip_select]; + + if (!cs) { + cs = kzalloc(sizeof *cs, GFP_KERNEL); + if (!cs) + return -ENOMEM; + cs->base = mcspi->base + spi->chip_select * 0x14; + spi->controller_state = cs; + } + + if (mcspi_dma->dma_rx_channel == -1 + || mcspi_dma->dma_tx_channel == -1) { + ret = omap2_mcspi_request_dma(spi); + if (ret < 0) + return ret; + } + + clk_enable(mcspi->ick); + clk_enable(mcspi->fck); + ret = omap2_mcspi_setup_transfer(spi, NULL); + clk_disable(mcspi->fck); + clk_disable(mcspi->ick); + + return ret; +} + +static void omap2_mcspi_cleanup(struct spi_device *spi) +{ + struct omap2_mcspi *mcspi; + struct omap2_mcspi_dma *mcspi_dma; + + mcspi = spi_master_get_devdata(spi->master); + mcspi_dma = &mcspi->dma_channels[spi->chip_select]; + + kfree(spi->controller_state); + + if (mcspi_dma->dma_rx_channel != -1) { + omap_free_dma(mcspi_dma->dma_rx_channel); + mcspi_dma->dma_rx_channel = -1; + } + if (mcspi_dma->dma_tx_channel != -1) { + omap_free_dma(mcspi_dma->dma_tx_channel); + mcspi_dma->dma_tx_channel = -1; + } +} + +static void omap2_mcspi_work(struct work_struct *work) +{ + struct omap2_mcspi *mcspi; + + mcspi = container_of(work, struct omap2_mcspi, work); + spin_lock_irq(&mcspi->lock); + + clk_enable(mcspi->ick); + clk_enable(mcspi->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(&mcspi->msg_queue)) { + struct spi_message *m; + struct spi_device *spi; + struct spi_transfer *t = NULL; + int cs_active = 0; + struct omap2_mcspi_device_config *conf; + struct omap2_mcspi_cs *cs; + int par_override = 0; + int status = 0; + u32 chconf; + + m = container_of(mcspi->msg_queue.next, struct spi_message, + queue); + + list_del_init(&m->queue); + spin_unlock_irq(&mcspi->lock); + + spi = m->spi; + conf = spi->controller_data; + cs = spi->controller_state; + + omap2_mcspi_set_enable(spi, 1); + 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 = omap2_mcspi_setup_transfer(spi, t); + if (status < 0) + break; + if (!t->speed_hz && !t->bits_per_word) + par_override = 0; + } + + if (!cs_active) { + omap2_mcspi_force_cs(spi, 1); + cs_active = 1; + } + + chconf = mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0); + chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; + if (t->tx_buf == NULL) + chconf |= OMAP2_MCSPI_CHCONF_TRM_RX_ONLY; + else if (t->rx_buf == NULL) + chconf |= OMAP2_MCSPI_CHCONF_TRM_TX_ONLY; + mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCONF0, chconf); + + if (t->len) { + unsigned count; + + /* RX_ONLY mode needs dummy data in TX reg */ + if (t->tx_buf == NULL) + __raw_writel(0, cs->base + + OMAP2_MCSPI_TX0); + + if (m->is_dma_mapped || t->len >= DMA_MIN_BYTES) + count = omap2_mcspi_txrx_dma(spi, t); + else + count = omap2_mcspi_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) { + omap2_mcspi_force_cs(spi, 0); + cs_active = 0; + } + } + + /* Restore defaults if they were overriden */ + if (par_override) { + par_override = 0; + status = omap2_mcspi_setup_transfer(spi, NULL); + } + + if (cs_active) + omap2_mcspi_force_cs(spi, 0); + + omap2_mcspi_set_enable(spi, 0); + + m->status = status; + m->complete(m->context); + + spin_lock_irq(&mcspi->lock); + } + + clk_disable(mcspi->fck); + clk_disable(mcspi->ick); + + spin_unlock_irq(&mcspi->lock); +} + +static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m) +{ + struct omap2_mcspi *mcspi; + unsigned long flags; + struct spi_transfer *t; + + m->actual_length = 0; + m->status = 0; + + /* 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 > OMAP2_MCSPI_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 < OMAP2_MCSPI_MAX_FREQ/(1<<16)) { + dev_dbg(&spi->dev, "%d Hz max exceeds %d\n", + t->speed_hz, + OMAP2_MCSPI_MAX_FREQ/(1<<16)); + return -EINVAL; + } + + if (m->is_dma_mapped || len < DMA_MIN_BYTES) + continue; + + /* Do DMA mapping "early" for better error reporting and + * dcache use. Note that if dma_unmap_single() ever starts + * to do real work on ARM, we'd need to clean up mappings + * for previous transfers on *ALL* exits of this loop... + */ + if (tx_buf != NULL) { + t->tx_dma = dma_map_single(&spi->dev, (void *) tx_buf, + len, DMA_TO_DEVICE); + if (dma_mapping_error(t->tx_dma)) { + dev_dbg(&spi->dev, "dma %cX %d bytes error\n", + 'T', len); + return -EINVAL; + } + } + if (rx_buf != NULL) { + t->rx_dma = dma_map_single(&spi->dev, rx_buf, t->len, + DMA_FROM_DEVICE); + if (dma_mapping_error(t->rx_dma)) { + dev_dbg(&spi->dev, "dma %cX %d bytes error\n", + 'R', len); + if (tx_buf != NULL) + dma_unmap_single(NULL, t->tx_dma, + len, DMA_TO_DEVICE); + return -EINVAL; + } + } + } + + mcspi = spi_master_get_devdata(spi->master); + + spin_lock_irqsave(&mcspi->lock, flags); + list_add_tail(&m->queue, &mcspi->msg_queue); + queue_work(omap2_mcspi_wq, &mcspi->work); + spin_unlock_irqrestore(&mcspi->lock, flags); + + return 0; +} + +static int __init omap2_mcspi_reset(struct omap2_mcspi *mcspi) +{ + struct spi_master *master = mcspi->master; + u32 tmp; + + clk_enable(mcspi->ick); + clk_enable(mcspi->fck); + + mcspi_write_reg(master, OMAP2_MCSPI_SYSCONFIG, + OMAP2_MCSPI_SYSCONFIG_SOFTRESET); + do { + tmp = mcspi_read_reg(master, OMAP2_MCSPI_SYSSTATUS); + } while (!(tmp & OMAP2_MCSPI_SYSSTATUS_RESETDONE)); + + mcspi_write_reg(master, OMAP2_MCSPI_SYSCONFIG, + /* (3 << 8) | (2 << 3) | */ + OMAP2_MCSPI_SYSCONFIG_AUTOIDLE); + + omap2_mcspi_set_master_mode(master); + + clk_disable(mcspi->fck); + clk_disable(mcspi->ick); + return 0; +} + +static u8 __initdata spi1_rxdma_id [] = { + OMAP24XX_DMA_SPI1_RX0, + OMAP24XX_DMA_SPI1_RX1, + OMAP24XX_DMA_SPI1_RX2, + OMAP24XX_DMA_SPI1_RX3, +}; + +static u8 __initdata spi1_txdma_id [] = { + OMAP24XX_DMA_SPI1_TX0, + OMAP24XX_DMA_SPI1_TX1, + OMAP24XX_DMA_SPI1_TX2, + OMAP24XX_DMA_SPI1_TX3, +}; + +static u8 __initdata spi2_rxdma_id[] = { + OMAP24XX_DMA_SPI2_RX0, + OMAP24XX_DMA_SPI2_RX1, +}; + +static u8 __initdata spi2_txdma_id[] = { + OMAP24XX_DMA_SPI2_TX0, + OMAP24XX_DMA_SPI2_TX1, +}; + +static int __init omap2_mcspi_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct omap2_mcspi *mcspi; + struct resource *r; + int status = 0, i; + const u8 *rxdma_id, *txdma_id; + unsigned num_chipselect; + + switch (pdev->id) { + case 1: + rxdma_id = spi1_rxdma_id; + txdma_id = spi1_txdma_id; + num_chipselect = 4; + break; + case 2: + rxdma_id = spi2_rxdma_id; + txdma_id = spi2_txdma_id; + num_chipselect = 2; + break; + /* REVISIT omap2430 has a third McSPI ... */ + default: + return -EINVAL; + } + + master = spi_alloc_master(&pdev->dev, sizeof *mcspi); + if (master == NULL) { + dev_dbg(&pdev->dev, "master allocation failed\n"); + return -ENOMEM; + } + + if (pdev->id != -1) + master->bus_num = pdev->id; + + master->setup = omap2_mcspi_setup; + master->transfer = omap2_mcspi_transfer; + master->cleanup = omap2_mcspi_cleanup; + master->num_chipselect = num_chipselect; + + dev_set_drvdata(&pdev->dev, master); + + mcspi = spi_master_get_devdata(master); + mcspi->master = master; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (r == NULL) { + status = -ENODEV; + goto err1; + } + if (!request_mem_region(r->start, (r->end - r->start) + 1, + pdev->dev.bus_id)) { + status = -EBUSY; + goto err1; + } + + mcspi->base = (void __iomem *) io_p2v(r->start); + + INIT_WORK(&mcspi->work, omap2_mcspi_work); + + spin_lock_init(&mcspi->lock); + INIT_LIST_HEAD(&mcspi->msg_queue); + + mcspi->ick = clk_get(&pdev->dev, "mcspi_ick"); + if (IS_ERR(mcspi->ick)) { + dev_dbg(&pdev->dev, "can't get mcspi_ick\n"); + status = PTR_ERR(mcspi->ick); + goto err1a; + } + mcspi->fck = clk_get(&pdev->dev, "mcspi_fck"); + if (IS_ERR(mcspi->fck)) { + dev_dbg(&pdev->dev, "can't get mcspi_fck\n"); + status = PTR_ERR(mcspi->fck); + goto err2; + } + + mcspi->dma_channels = kcalloc(master->num_chipselect, + sizeof(struct omap2_mcspi_dma), + GFP_KERNEL); + + if (mcspi->dma_channels == NULL) + goto err3; + + for (i = 0; i < num_chipselect; i++) { + mcspi->dma_channels[i].dma_rx_channel = -1; + mcspi->dma_channels[i].dma_rx_sync_dev = rxdma_id[i]; + mcspi->dma_channels[i].dma_tx_channel = -1; + mcspi->dma_channels[i].dma_tx_sync_dev = txdma_id[i]; + } + + if (omap2_mcspi_reset(mcspi) < 0) + goto err4; + + status = spi_register_master(master); + if (status < 0) + goto err4; + + return status; + +err4: + kfree(mcspi->dma_channels); +err3: + clk_put(mcspi->fck); +err2: + clk_put(mcspi->ick); +err1a: + release_mem_region(r->start, (r->end - r->start) + 1); +err1: + spi_master_put(master); + return status; +} + +static int __exit omap2_mcspi_remove(struct platform_device *pdev) +{ + struct spi_master *master; + struct omap2_mcspi *mcspi; + struct omap2_mcspi_dma *dma_channels; + struct resource *r; + + master = dev_get_drvdata(&pdev->dev); + mcspi = spi_master_get_devdata(master); + dma_channels = mcspi->dma_channels; + + clk_put(mcspi->fck); + clk_put(mcspi->ick); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(r->start, (r->end - r->start) + 1); + + spi_unregister_master(master); + kfree(dma_channels); + + return 0; +} + +static struct platform_driver omap2_mcspi_driver = { + .driver = { + .name = "omap2_mcspi", + .owner = THIS_MODULE, + }, + .remove = __exit_p(omap2_mcspi_remove), +}; + + +static int __init omap2_mcspi_init(void) +{ + omap2_mcspi_wq = create_singlethread_workqueue( + omap2_mcspi_driver.driver.name); + if (omap2_mcspi_wq == NULL) + return -1; + return platform_driver_probe(&omap2_mcspi_driver, omap2_mcspi_probe); +} +subsys_initcall(omap2_mcspi_init); + +static void __exit omap2_mcspi_exit(void) +{ + platform_driver_unregister(&omap2_mcspi_driver); + + destroy_workqueue(omap2_mcspi_wq); +} +module_exit(omap2_mcspi_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/omap_uwire.c b/drivers/spi/omap_uwire.c index 95183e1df525..d275c615a73e 100644 --- a/drivers/spi/omap_uwire.c +++ b/drivers/spi/omap_uwire.c @@ -445,10 +445,19 @@ done: return status; } +/* the spi->mode bits understood by this driver: */ +#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH) + static int uwire_setup(struct spi_device *spi) { struct uwire_state *ust = spi->controller_state; + if (spi->mode & ~MODEBITS) { + dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n", + spi->mode & ~MODEBITS); + return -EINVAL; + } + if (ust == NULL) { ust = kzalloc(sizeof(*ust), GFP_KERNEL); if (ust == NULL) diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index 9f2c887ffa04..e51311b2da0b 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -1067,6 +1067,9 @@ static int transfer(struct spi_device *spi, struct spi_message *msg) return 0; } +/* the spi->mode bits understood by this driver: */ +#define MODEBITS (SPI_CPOL | SPI_CPHA) + static int setup(struct spi_device *spi) { struct pxa2xx_spi_chip *chip_info = NULL; @@ -1093,6 +1096,12 @@ static int setup(struct spi_device *spi) return -EINVAL; } + if (spi->mode & ~MODEBITS) { + dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n", + spi->mode & ~MODEBITS); + return -EINVAL; + } + /* Only alloc on first setup */ chip = spi_get_ctldata(spi); if (!chip) { diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 4831edbae2d5..018884d7a5fa 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -23,6 +23,7 @@ #include <linux/device.h> #include <linux/init.h> #include <linux/cache.h> +#include <linux/mutex.h> #include <linux/spi/spi.h> @@ -185,7 +186,7 @@ struct boardinfo { }; static LIST_HEAD(board_list); -static DECLARE_MUTEX(board_lock); +static DEFINE_MUTEX(board_lock); /** @@ -292,9 +293,9 @@ spi_register_board_info(struct spi_board_info const *info, unsigned n) bi->n_board_info = n; memcpy(bi->board_info, info, n * sizeof *info); - down(&board_lock); + mutex_lock(&board_lock); list_add_tail(&bi->list, &board_list); - up(&board_lock); + mutex_unlock(&board_lock); return 0; } @@ -308,7 +309,7 @@ scan_boardinfo(struct spi_master *master) struct boardinfo *bi; struct device *dev = master->cdev.dev; - down(&board_lock); + mutex_lock(&board_lock); list_for_each_entry(bi, &board_list, list) { struct spi_board_info *chip = bi->board_info; unsigned n; @@ -330,7 +331,7 @@ scan_boardinfo(struct spi_master *master) (void) spi_new_device(master, chip); } } - up(&board_lock); + mutex_unlock(&board_lock); } /*-------------------------------------------------------------------------*/ diff --git a/drivers/spi/spi_bitbang.c b/drivers/spi/spi_bitbang.c index 88425e1af4d3..0c85c984ccb4 100644 --- a/drivers/spi/spi_bitbang.c +++ b/drivers/spi/spi_bitbang.c @@ -187,12 +187,10 @@ int spi_bitbang_setup(struct spi_device *spi) bitbang = spi_master_get_devdata(spi->master); - /* REVISIT: some systems will want to support devices using lsb-first - * bit encodings on the wire. In pure software that would be trivial, - * just bitbang_txrx_le_cphaX() routines shifting the other way, and - * some hardware controllers also have this support. + /* Bitbangers can support SPI_CS_HIGH, SPI_3WIRE, and so on; + * add those to master->flags, and provide the other support. */ - if ((spi->mode & SPI_LSB_FIRST) != 0) + if ((spi->mode & ~(SPI_CPOL|SPI_CPHA|bitbang->flags)) != 0) return -EINVAL; if (!cs) { diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c index 656be4a5094a..aee9ad6f633c 100644 --- a/drivers/spi/spi_imx.c +++ b/drivers/spi/spi_imx.c @@ -1163,6 +1163,9 @@ msg_rejected: return -EINVAL; } +/* the spi->mode bits understood by this driver: */ +#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH) + /* 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. */ @@ -1174,6 +1177,12 @@ static int setup(struct spi_device *spi) u32 tmp; int status = 0; + if (spi->mode & ~MODEBITS) { + dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n", + spi->mode & ~MODEBITS); + return -EINVAL; + } + /* Get controller data */ chip_info = spi->controller_data; @@ -1245,21 +1254,6 @@ static int setup(struct spi_device *spi) /* 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); diff --git a/drivers/spi/spi_lm70llp.c b/drivers/spi/spi_lm70llp.c new file mode 100644 index 000000000000..4ea68ac16115 --- /dev/null +++ b/drivers/spi/spi_lm70llp.c @@ -0,0 +1,361 @@ +/* + * spi_lm70llp.c - driver for lm70llp eval board for the LM70 sensor + * + * Copyright (C) 2006 Kaiwan N Billimoria <kaiwan@designergraphix.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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/parport.h> +#include <linux/sysfs.h> +#include <linux/workqueue.h> + + +#include <linux/spi/spi.h> +#include <linux/spi/spi_bitbang.h> + + +/* + * The LM70 communicates with a host processor using a 3-wire variant of + * the SPI/Microwire bus interface. This driver specifically supports an + * NS LM70 LLP Evaluation Board, interfacing to a PC using its parallel + * port to bitbang an SPI-parport bridge. Accordingly, this is an SPI + * master controller driver. The hwmon/lm70 driver is a "SPI protocol + * driver", layered on top of this one and usable without the lm70llp. + * + * The LM70 is a temperature sensor chip from National Semiconductor; its + * datasheet is available at http://www.national.com/pf/LM/LM70.html + * + * Also see Documentation/spi/spi-lm70llp. The SPI<->parport code here is + * (heavily) based on spi-butterfly by David Brownell. + * + * The LM70 LLP connects to the PC parallel port in the following manner: + * + * Parallel LM70 LLP + * Port Direction JP2 Header + * ----------- --------- ------------ + * D0 2 - - + * D1 3 --> V+ 5 + * D2 4 --> V+ 5 + * D3 5 --> V+ 5 + * D4 6 --> V+ 5 + * D5 7 --> nCS 8 + * D6 8 --> SCLK 3 + * D7 9 --> SI/O 5 + * GND 25 - GND 7 + * Select 13 <-- SI/O 1 + * + * Note that parport pin 13 actually gets inverted by the transistor + * arrangement which lets either the parport or the LM70 drive the + * SI/SO signal. + */ + +#define DRVNAME "spi-lm70llp" + +#define lm70_INIT 0xBE +#define SIO 0x10 +#define nCS 0x20 +#define SCLK 0x40 + +/*-------------------------------------------------------------------------*/ + +struct spi_lm70llp { + struct spi_bitbang bitbang; + struct parport *port; + struct pardevice *pd; + struct spi_device *spidev_lm70; + struct spi_board_info info; + struct class_device *cdev; +}; + +/* REVISIT : ugly global ; provides "exclusive open" facility */ +static struct spi_lm70llp *lm70llp; + + +/*-------------------------------------------------------------------*/ + +static inline struct spi_lm70llp *spidev_to_pp(struct spi_device *spi) +{ + return spi->controller_data; +} + +/*---------------------- LM70 LLP eval board-specific inlines follow */ + +/* NOTE: we don't actually need to reread the output values, since they'll + * still be what we wrote before. Plus, going through parport builds in + * a ~1ms/operation delay; these SPI transfers could easily be faster. + */ + +static inline void deassertCS(struct spi_lm70llp *pp) +{ + u8 data = parport_read_data(pp->port); + parport_write_data(pp->port, data | nCS); +} + +static inline void assertCS(struct spi_lm70llp *pp) +{ + u8 data = parport_read_data(pp->port); + parport_write_data(pp->port, data & ~nCS); +} + +static inline void clkHigh(struct spi_lm70llp *pp) +{ + u8 data = parport_read_data(pp->port); + parport_write_data(pp->port, data | SCLK); +} + +static inline void clkLow(struct spi_lm70llp *pp) +{ + u8 data = parport_read_data(pp->port); + parport_write_data(pp->port, data & ~SCLK); +} + +/*------------------------- SPI-LM70-specific inlines ----------------------*/ + +static inline void spidelay(unsigned d) +{ + udelay(d); +} + +static inline void setsck(struct spi_device *s, int is_on) +{ + struct spi_lm70llp *pp = spidev_to_pp(s); + + if (is_on) + clkHigh(pp); + else + clkLow(pp); +} + +static inline void setmosi(struct spi_device *s, int is_on) +{ + /* FIXME update D7 ... this way we can put the chip + * into shutdown mode and read the manufacturer ID, + * but we can't put it back into operational mode. + */ +} + +/* + * getmiso: + * Why do we return 0 when the SIO line is high and vice-versa? + * The fact is, the lm70 eval board from NS (which this driver drives), + * is wired in just such a way : when the lm70's SIO goes high, a transistor + * switches it to low reflecting this on the parport (pin 13), and vice-versa. + */ +static inline int getmiso(struct spi_device *s) +{ + struct spi_lm70llp *pp = spidev_to_pp(s); + return ((SIO == (parport_read_status(pp->port) & SIO)) ? 0 : 1 ); +} +/*--------------------------------------------------------------------*/ + +#define EXPAND_BITBANG_TXRX 1 +#include <linux/spi/spi_bitbang.h> + +static void lm70_chipselect(struct spi_device *spi, int value) +{ + struct spi_lm70llp *pp = spidev_to_pp(spi); + + if (value) + assertCS(pp); + else + deassertCS(pp); +} + +/* + * Our actual bitbanger routine. + */ +static u32 lm70_txrx(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits) +{ + static u32 sio=0; + static int first_time=1; + + /* First time: perform SPI bitbang and return the LSB of + * the result of the SPI call. + */ + if (first_time) { + sio = bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits); + first_time=0; + return (sio & 0x00ff); + } + /* Return the MSB of the result of the SPI call */ + else { + first_time=1; + return (sio >> 8); + } +} + +static void spi_lm70llp_attach(struct parport *p) +{ + struct pardevice *pd; + struct spi_lm70llp *pp; + struct spi_master *master; + int status; + + if (lm70llp) { + printk(KERN_WARNING + "%s: spi_lm70llp instance already loaded. Aborting.\n", + DRVNAME); + return; + } + + /* TODO: this just _assumes_ a lm70 is there ... no probe; + * the lm70 driver could verify it, reading the manf ID. + */ + + master = spi_alloc_master(p->physport->dev, sizeof *pp); + if (!master) { + status = -ENOMEM; + goto out_fail; + } + pp = spi_master_get_devdata(master); + + master->bus_num = -1; /* dynamic alloc of a bus number */ + master->num_chipselect = 1; + + /* + * SPI and bitbang hookup. + */ + pp->bitbang.master = spi_master_get(master); + pp->bitbang.chipselect = lm70_chipselect; + pp->bitbang.txrx_word[SPI_MODE_0] = lm70_txrx; + pp->bitbang.flags = SPI_3WIRE; + + /* + * Parport hookup + */ + pp->port = p; + pd = parport_register_device(p, DRVNAME, + NULL, NULL, NULL, + PARPORT_FLAG_EXCL, pp); + if (!pd) { + status = -ENOMEM; + goto out_free_master; + } + pp->pd = pd; + + status = parport_claim(pd); + if (status < 0) + goto out_parport_unreg; + + /* + * Start SPI ... + */ + status = spi_bitbang_start(&pp->bitbang); + if (status < 0) { + printk(KERN_WARNING + "%s: spi_bitbang_start failed with status %d\n", + DRVNAME, status); + goto out_off_and_release; + } + + /* + * The modalias name MUST match the device_driver name + * for the bus glue code to match and subsequently bind them. + * We are binding to the generic drivers/hwmon/lm70.c device + * driver. + */ + strcpy(pp->info.modalias, "lm70"); + pp->info.max_speed_hz = 6 * 1000 * 1000; + pp->info.chip_select = 0; + pp->info.mode = SPI_3WIRE | SPI_MODE_0; + + /* power up the chip, and let the LM70 control SI/SO */ + parport_write_data(pp->port, lm70_INIT); + + /* Enable access to our primary data structure via + * the board info's (void *)controller_data. + */ + pp->info.controller_data = pp; + pp->spidev_lm70 = spi_new_device(pp->bitbang.master, &pp->info); + if (pp->spidev_lm70) + dev_dbg(&pp->spidev_lm70->dev, "spidev_lm70 at %s\n", + pp->spidev_lm70->dev.bus_id); + else { + printk(KERN_WARNING "%s: spi_new_device failed\n", DRVNAME); + status = -ENODEV; + goto out_bitbang_stop; + } + pp->spidev_lm70->bits_per_word = 16; + + lm70llp = pp; + + return; + +out_bitbang_stop: + spi_bitbang_stop(&pp->bitbang); +out_off_and_release: + /* power down */ + parport_write_data(pp->port, 0); + mdelay(10); + parport_release(pp->pd); +out_parport_unreg: + parport_unregister_device(pd); +out_free_master: + (void) spi_master_put(master); +out_fail: + pr_info("%s: spi_lm70llp probe fail, status %d\n", DRVNAME, status); +} + +static void spi_lm70llp_detach(struct parport *p) +{ + struct spi_lm70llp *pp; + + if (!lm70llp || lm70llp->port != p) + return; + + pp = lm70llp; + spi_bitbang_stop(&pp->bitbang); + + /* power down */ + parport_write_data(pp->port, 0); + msleep(10); + + parport_release(pp->pd); + parport_unregister_device(pp->pd); + + (void) spi_master_put(pp->bitbang.master); + + lm70llp = NULL; +} + + +static struct parport_driver spi_lm70llp_drv = { + .name = DRVNAME, + .attach = spi_lm70llp_attach, + .detach = spi_lm70llp_detach, +}; + +static int __init init_spi_lm70llp(void) +{ + return parport_register_driver(&spi_lm70llp_drv); +} +module_init(init_spi_lm70llp); + +static void __exit cleanup_spi_lm70llp(void) +{ + parport_unregister_driver(&spi_lm70llp_drv); +} +module_exit(cleanup_spi_lm70llp); + +MODULE_AUTHOR("Kaiwan N Billimoria <kaiwan@designergraphix.com>"); +MODULE_DESCRIPTION( + "Parport adapter for the National Semiconductor LM70 LLP eval board"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi_mpc83xx.c b/drivers/spi/spi_mpc83xx.c index e9798bf7b8c6..3295cfcc9f20 100644 --- a/drivers/spi/spi_mpc83xx.c +++ b/drivers/spi/spi_mpc83xx.c @@ -47,6 +47,7 @@ struct mpc83xx_spi_reg { #define SPMODE_ENABLE (1 << 24) #define SPMODE_LEN(x) ((x) << 20) #define SPMODE_PM(x) ((x) << 16) +#define SPMODE_OP (1 << 14) /* * Default for SPI Mode: @@ -85,6 +86,11 @@ struct mpc83xx_spi { unsigned nsecs; /* (clock cycle time)/2 */ u32 sysclk; + u32 rx_shift; /* RX data reg shift when in qe mode */ + u32 tx_shift; /* TX data reg shift when in qe mode */ + + bool qe_mode; + void (*activate_cs) (u8 cs, u8 polarity); void (*deactivate_cs) (u8 cs, u8 polarity); }; @@ -103,7 +109,7 @@ static inline u32 mpc83xx_spi_read_reg(__be32 __iomem * reg) void mpc83xx_spi_rx_buf_##type(u32 data, struct mpc83xx_spi *mpc83xx_spi) \ { \ type * rx = mpc83xx_spi->rx; \ - *rx++ = (type)data; \ + *rx++ = (type)(data >> mpc83xx_spi->rx_shift); \ mpc83xx_spi->rx = rx; \ } @@ -114,7 +120,7 @@ u32 mpc83xx_spi_tx_buf_##type(struct mpc83xx_spi *mpc83xx_spi) \ const type * tx = mpc83xx_spi->tx; \ if (!tx) \ return 0; \ - data = *tx++; \ + data = *tx++ << mpc83xx_spi->tx_shift; \ mpc83xx_spi->tx = tx; \ return data; \ } @@ -158,6 +164,12 @@ static void mpc83xx_spi_chipselect(struct spi_device *spi, int value) if ((mpc83xx_spi->sysclk / spi->max_speed_hz) >= 64) { u8 pm = mpc83xx_spi->sysclk / (spi->max_speed_hz * 64); + if (pm > 0x0f) { + printk(KERN_WARNING "MPC83xx SPI: SPICLK can't be less then a SYSCLK/1024!\n" + "Requested SPICLK is %d Hz. Will use %d Hz instead.\n", + spi->max_speed_hz, mpc83xx_spi->sysclk / 1024); + pm = 0x0f; + } regval |= SPMODE_PM(pm) | SPMODE_DIV16; } else { u8 pm = mpc83xx_spi->sysclk / (spi->max_speed_hz * 4); @@ -197,12 +209,22 @@ int mpc83xx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) || ((bits_per_word > 16) && (bits_per_word != 32))) return -EINVAL; + mpc83xx_spi->rx_shift = 0; + mpc83xx_spi->tx_shift = 0; if (bits_per_word <= 8) { mpc83xx_spi->get_rx = mpc83xx_spi_rx_buf_u8; mpc83xx_spi->get_tx = mpc83xx_spi_tx_buf_u8; + if (mpc83xx_spi->qe_mode) { + mpc83xx_spi->rx_shift = 16; + mpc83xx_spi->tx_shift = 24; + } } else if (bits_per_word <= 16) { mpc83xx_spi->get_rx = mpc83xx_spi_rx_buf_u16; mpc83xx_spi->get_tx = mpc83xx_spi_tx_buf_u16; + if (mpc83xx_spi->qe_mode) { + mpc83xx_spi->rx_shift = 16; + mpc83xx_spi->tx_shift = 16; + } } else if (bits_per_word <= 32) { mpc83xx_spi->get_rx = mpc83xx_spi_rx_buf_u32; mpc83xx_spi->get_tx = mpc83xx_spi_tx_buf_u32; @@ -232,12 +254,21 @@ int mpc83xx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) return 0; } +/* the spi->mode bits understood by this driver: */ +#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH) + static int mpc83xx_spi_setup(struct spi_device *spi) { struct spi_bitbang *bitbang; struct mpc83xx_spi *mpc83xx_spi; int retval; + if (spi->mode & ~MODEBITS) { + dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n", + spi->mode & ~MODEBITS); + return -EINVAL; + } + if (!spi->max_speed_hz) return -EINVAL; @@ -371,7 +402,6 @@ static int __init mpc83xx_spi_probe(struct platform_device *dev) ret = -ENODEV; goto free_master; } - mpc83xx_spi = spi_master_get_devdata(master); mpc83xx_spi->bitbang.master = spi_master_get(master); mpc83xx_spi->bitbang.chipselect = mpc83xx_spi_chipselect; @@ -380,9 +410,17 @@ static int __init mpc83xx_spi_probe(struct platform_device *dev) mpc83xx_spi->sysclk = pdata->sysclk; mpc83xx_spi->activate_cs = pdata->activate_cs; mpc83xx_spi->deactivate_cs = pdata->deactivate_cs; + mpc83xx_spi->qe_mode = pdata->qe_mode; mpc83xx_spi->get_rx = mpc83xx_spi_rx_buf_u8; mpc83xx_spi->get_tx = mpc83xx_spi_tx_buf_u8; + mpc83xx_spi->rx_shift = 0; + mpc83xx_spi->tx_shift = 0; + if (mpc83xx_spi->qe_mode) { + mpc83xx_spi->rx_shift = 16; + mpc83xx_spi->tx_shift = 24; + } + mpc83xx_spi->bitbang.master->setup = mpc83xx_spi_setup; init_completion(&mpc83xx_spi->done); @@ -417,6 +455,9 @@ static int __init mpc83xx_spi_probe(struct platform_device *dev) /* Enable SPI interface */ regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE; + if (pdata->qe_mode) + regval |= SPMODE_OP; + mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, regval); ret = spi_bitbang_start(&mpc83xx_spi->bitbang); diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi_s3c24xx.c index d5a710f6e445..7071ff8da63e 100644 --- a/drivers/spi/spi_s3c24xx.c +++ b/drivers/spi/spi_s3c24xx.c @@ -146,6 +146,9 @@ static int s3c24xx_spi_setupxfer(struct spi_device *spi, return 0; } +/* the spi->mode bits understood by this driver: */ +#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH) + static int s3c24xx_spi_setup(struct spi_device *spi) { int ret; @@ -153,8 +156,11 @@ static int s3c24xx_spi_setup(struct spi_device *spi) if (!spi->bits_per_word) spi->bits_per_word = 8; - if ((spi->mode & SPI_LSB_FIRST) != 0) + if (spi->mode & ~MODEBITS) { + dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n", + spi->mode & ~MODEBITS); return -EINVAL; + } ret = s3c24xx_spi_setupxfer(spi, NULL); if (ret < 0) { diff --git a/drivers/spi/spi_txx9.c b/drivers/spi/spi_txx9.c new file mode 100644 index 000000000000..08e981c40646 --- /dev/null +++ b/drivers/spi/spi_txx9.c @@ -0,0 +1,474 @@ +/* + * spi_txx9.c - TXx9 SPI controller driver. + * + * Based on linux/arch/mips/tx4938/toshiba_rbtx4938/spi_txx9.c + * Copyright (C) 2000-2001 Toshiba Corporation + * + * 2003-2005 (c) MontaVista Software, Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is + * licensed "as is" without any warranty of any kind, whether express + * or implied. + * + * Support for TX4938 in 2.6 - Manish Lachwani (mlachwani@mvista.com) + * + * Convert to generic SPI framework - Atsushi Nemoto (anemo@mba.ocn.ne.jp) + */ +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/sched.h> +#include <linux/spinlock.h> +#include <linux/workqueue.h> +#include <linux/spi/spi.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <asm/gpio.h> + + +#define SPI_FIFO_SIZE 4 + +#define TXx9_SPMCR 0x00 +#define TXx9_SPCR0 0x04 +#define TXx9_SPCR1 0x08 +#define TXx9_SPFS 0x0c +#define TXx9_SPSR 0x14 +#define TXx9_SPDR 0x18 + +/* SPMCR : SPI Master Control */ +#define TXx9_SPMCR_OPMODE 0xc0 +#define TXx9_SPMCR_CONFIG 0x40 +#define TXx9_SPMCR_ACTIVE 0x80 +#define TXx9_SPMCR_SPSTP 0x02 +#define TXx9_SPMCR_BCLR 0x01 + +/* SPCR0 : SPI Control 0 */ +#define TXx9_SPCR0_TXIFL_MASK 0xc000 +#define TXx9_SPCR0_RXIFL_MASK 0x3000 +#define TXx9_SPCR0_SIDIE 0x0800 +#define TXx9_SPCR0_SOEIE 0x0400 +#define TXx9_SPCR0_RBSIE 0x0200 +#define TXx9_SPCR0_TBSIE 0x0100 +#define TXx9_SPCR0_IFSPSE 0x0010 +#define TXx9_SPCR0_SBOS 0x0004 +#define TXx9_SPCR0_SPHA 0x0002 +#define TXx9_SPCR0_SPOL 0x0001 + +/* SPSR : SPI Status */ +#define TXx9_SPSR_TBSI 0x8000 +#define TXx9_SPSR_RBSI 0x4000 +#define TXx9_SPSR_TBS_MASK 0x3800 +#define TXx9_SPSR_RBS_MASK 0x0700 +#define TXx9_SPSR_SPOE 0x0080 +#define TXx9_SPSR_IFSD 0x0008 +#define TXx9_SPSR_SIDLE 0x0004 +#define TXx9_SPSR_STRDY 0x0002 +#define TXx9_SPSR_SRRDY 0x0001 + + +struct txx9spi { + struct workqueue_struct *workqueue; + struct work_struct work; + spinlock_t lock; /* protect 'queue' */ + struct list_head queue; + wait_queue_head_t waitq; + void __iomem *membase; + int irq; + int baseclk; + struct clk *clk; + u32 max_speed_hz, min_speed_hz; + int last_chipselect; + int last_chipselect_val; +}; + +static u32 txx9spi_rd(struct txx9spi *c, int reg) +{ + return __raw_readl(c->membase + reg); +} +static void txx9spi_wr(struct txx9spi *c, u32 val, int reg) +{ + __raw_writel(val, c->membase + reg); +} + +static void txx9spi_cs_func(struct spi_device *spi, struct txx9spi *c, + int on, unsigned int cs_delay) +{ + int val = (spi->mode & SPI_CS_HIGH) ? on : !on; + if (on) { + /* deselect the chip with cs_change hint in last transfer */ + if (c->last_chipselect >= 0) + gpio_set_value(c->last_chipselect, + !c->last_chipselect_val); + c->last_chipselect = spi->chip_select; + c->last_chipselect_val = val; + } else { + c->last_chipselect = -1; + ndelay(cs_delay); /* CS Hold Time */ + } + gpio_set_value(spi->chip_select, val); + ndelay(cs_delay); /* CS Setup Time / CS Recovery Time */ +} + +/* the spi->mode bits understood by this driver: */ +#define MODEBITS (SPI_CS_HIGH|SPI_CPOL|SPI_CPHA) + +static int txx9spi_setup(struct spi_device *spi) +{ + struct txx9spi *c = spi_master_get_devdata(spi->master); + u8 bits_per_word; + + if (spi->mode & ~MODEBITS) + return -EINVAL; + + if (!spi->max_speed_hz + || spi->max_speed_hz > c->max_speed_hz + || spi->max_speed_hz < c->min_speed_hz) + return -EINVAL; + + bits_per_word = spi->bits_per_word ? : 8; + if (bits_per_word != 8 && bits_per_word != 16) + return -EINVAL; + + if (gpio_direction_output(spi->chip_select, + !(spi->mode & SPI_CS_HIGH))) { + dev_err(&spi->dev, "Cannot setup GPIO for chipselect.\n"); + return -EINVAL; + } + + /* deselect chip */ + spin_lock(&c->lock); + txx9spi_cs_func(spi, c, 0, (NSEC_PER_SEC / 2) / spi->max_speed_hz); + spin_unlock(&c->lock); + + return 0; +} + +static irqreturn_t txx9spi_interrupt(int irq, void *dev_id) +{ + struct txx9spi *c = dev_id; + + /* disable rx intr */ + txx9spi_wr(c, txx9spi_rd(c, TXx9_SPCR0) & ~TXx9_SPCR0_RBSIE, + TXx9_SPCR0); + wake_up(&c->waitq); + return IRQ_HANDLED; +} + +static void txx9spi_work_one(struct txx9spi *c, struct spi_message *m) +{ + struct spi_device *spi = m->spi; + struct spi_transfer *t; + unsigned int cs_delay; + unsigned int cs_change = 1; + int status = 0; + u32 mcr; + u32 prev_speed_hz = 0; + u8 prev_bits_per_word = 0; + + /* CS setup/hold/recovery time in nsec */ + cs_delay = 100 + (NSEC_PER_SEC / 2) / spi->max_speed_hz; + + mcr = txx9spi_rd(c, TXx9_SPMCR); + if (unlikely((mcr & TXx9_SPMCR_OPMODE) == TXx9_SPMCR_ACTIVE)) { + dev_err(&spi->dev, "Bad mode.\n"); + status = -EIO; + goto exit; + } + mcr &= ~(TXx9_SPMCR_OPMODE | TXx9_SPMCR_SPSTP | TXx9_SPMCR_BCLR); + + /* enter config mode */ + txx9spi_wr(c, mcr | TXx9_SPMCR_CONFIG | TXx9_SPMCR_BCLR, TXx9_SPMCR); + txx9spi_wr(c, TXx9_SPCR0_SBOS + | ((spi->mode & SPI_CPOL) ? TXx9_SPCR0_SPOL : 0) + | ((spi->mode & SPI_CPHA) ? TXx9_SPCR0_SPHA : 0) + | 0x08, + TXx9_SPCR0); + + list_for_each_entry (t, &m->transfers, transfer_list) { + const void *txbuf = t->tx_buf; + void *rxbuf = t->rx_buf; + u32 data; + unsigned int len = t->len; + unsigned int wsize; + u32 speed_hz = t->speed_hz ? : spi->max_speed_hz; + u8 bits_per_word = t->bits_per_word ? : spi->bits_per_word; + + bits_per_word = bits_per_word ? : 8; + wsize = bits_per_word >> 3; /* in bytes */ + + if (prev_speed_hz != speed_hz + || prev_bits_per_word != bits_per_word) { + u32 n = (c->baseclk + speed_hz - 1) / speed_hz; + if (n < 1) + n = 1; + else if (n > 0xff) + n = 0xff; + /* enter config mode */ + txx9spi_wr(c, mcr | TXx9_SPMCR_CONFIG | TXx9_SPMCR_BCLR, + TXx9_SPMCR); + txx9spi_wr(c, (n << 8) | bits_per_word, TXx9_SPCR1); + /* enter active mode */ + txx9spi_wr(c, mcr | TXx9_SPMCR_ACTIVE, TXx9_SPMCR); + + prev_speed_hz = speed_hz; + prev_bits_per_word = bits_per_word; + } + + if (cs_change) + txx9spi_cs_func(spi, c, 1, cs_delay); + cs_change = t->cs_change; + while (len) { + unsigned int count = SPI_FIFO_SIZE; + int i; + u32 cr0; + + if (len < count * wsize) + count = len / wsize; + /* now tx must be idle... */ + while (!(txx9spi_rd(c, TXx9_SPSR) & TXx9_SPSR_SIDLE)) + cpu_relax(); + cr0 = txx9spi_rd(c, TXx9_SPCR0); + cr0 &= ~TXx9_SPCR0_RXIFL_MASK; + cr0 |= (count - 1) << 12; + /* enable rx intr */ + cr0 |= TXx9_SPCR0_RBSIE; + txx9spi_wr(c, cr0, TXx9_SPCR0); + /* send */ + for (i = 0; i < count; i++) { + if (txbuf) { + data = (wsize == 1) + ? *(const u8 *)txbuf + : *(const u16 *)txbuf; + txx9spi_wr(c, data, TXx9_SPDR); + txbuf += wsize; + } else + txx9spi_wr(c, 0, TXx9_SPDR); + } + /* wait all rx data */ + wait_event(c->waitq, + txx9spi_rd(c, TXx9_SPSR) & TXx9_SPSR_RBSI); + /* receive */ + for (i = 0; i < count; i++) { + data = txx9spi_rd(c, TXx9_SPDR); + if (rxbuf) { + if (wsize == 1) + *(u8 *)rxbuf = data; + else + *(u16 *)rxbuf = data; + rxbuf += wsize; + } + } + len -= count * wsize; + } + m->actual_length += t->len; + if (t->delay_usecs) + udelay(t->delay_usecs); + + if (!cs_change) + continue; + if (t->transfer_list.next == &m->transfers) + break; + /* sometimes a short mid-message deselect of the chip + * may be needed to terminate a mode or command + */ + txx9spi_cs_func(spi, c, 0, cs_delay); + } + +exit: + m->status = status; + m->complete(m->context); + + /* normally deactivate chipselect ... unless no error and + * cs_change has hinted that the next message will probably + * be for this chip too. + */ + if (!(status == 0 && cs_change)) + txx9spi_cs_func(spi, c, 0, cs_delay); + + /* enter config mode */ + txx9spi_wr(c, mcr | TXx9_SPMCR_CONFIG | TXx9_SPMCR_BCLR, TXx9_SPMCR); +} + +static void txx9spi_work(struct work_struct *work) +{ + struct txx9spi *c = container_of(work, struct txx9spi, work); + unsigned long flags; + + spin_lock_irqsave(&c->lock, flags); + while (!list_empty(&c->queue)) { + struct spi_message *m; + + m = container_of(c->queue.next, struct spi_message, queue); + list_del_init(&m->queue); + spin_unlock_irqrestore(&c->lock, flags); + + txx9spi_work_one(c, m); + + spin_lock_irqsave(&c->lock, flags); + } + spin_unlock_irqrestore(&c->lock, flags); +} + +static int txx9spi_transfer(struct spi_device *spi, struct spi_message *m) +{ + struct spi_master *master = spi->master; + struct txx9spi *c = spi_master_get_devdata(master); + struct spi_transfer *t; + unsigned long flags; + + m->actual_length = 0; + + /* check each transfer's parameters */ + list_for_each_entry (t, &m->transfers, transfer_list) { + u32 speed_hz = t->speed_hz ? : spi->max_speed_hz; + u8 bits_per_word = t->bits_per_word ? : spi->bits_per_word; + + bits_per_word = bits_per_word ? : 8; + if (!t->tx_buf && !t->rx_buf && t->len) + return -EINVAL; + if (bits_per_word != 8 && bits_per_word != 16) + return -EINVAL; + if (t->len & ((bits_per_word >> 3) - 1)) + return -EINVAL; + if (speed_hz < c->min_speed_hz || speed_hz > c->max_speed_hz) + return -EINVAL; + } + + spin_lock_irqsave(&c->lock, flags); + list_add_tail(&m->queue, &c->queue); + queue_work(c->workqueue, &c->work); + spin_unlock_irqrestore(&c->lock, flags); + + return 0; +} + +static int __init txx9spi_probe(struct platform_device *dev) +{ + struct spi_master *master; + struct txx9spi *c; + struct resource *res; + int ret = -ENODEV; + u32 mcr; + + master = spi_alloc_master(&dev->dev, sizeof(*c)); + if (!master) + return ret; + c = spi_master_get_devdata(master); + c->irq = -1; + platform_set_drvdata(dev, master); + + INIT_WORK(&c->work, txx9spi_work); + spin_lock_init(&c->lock); + INIT_LIST_HEAD(&c->queue); + init_waitqueue_head(&c->waitq); + + c->clk = clk_get(&dev->dev, "spi-baseclk"); + if (IS_ERR(c->clk)) { + ret = PTR_ERR(c->clk); + c->clk = NULL; + goto exit; + } + ret = clk_enable(c->clk); + if (ret) { + clk_put(c->clk); + c->clk = NULL; + goto exit; + } + c->baseclk = clk_get_rate(c->clk); + c->min_speed_hz = (c->baseclk + 0xff - 1) / 0xff; + c->max_speed_hz = c->baseclk; + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res) + goto exit; + c->membase = ioremap(res->start, res->end - res->start + 1); + if (!c->membase) + goto exit; + + /* enter config mode */ + mcr = txx9spi_rd(c, TXx9_SPMCR); + mcr &= ~(TXx9_SPMCR_OPMODE | TXx9_SPMCR_SPSTP | TXx9_SPMCR_BCLR); + txx9spi_wr(c, mcr | TXx9_SPMCR_CONFIG | TXx9_SPMCR_BCLR, TXx9_SPMCR); + + c->irq = platform_get_irq(dev, 0); + if (c->irq < 0) + goto exit; + ret = request_irq(c->irq, txx9spi_interrupt, 0, dev->name, c); + if (ret) { + c->irq = -1; + goto exit; + } + + c->workqueue = create_singlethread_workqueue(master->cdev.dev->bus_id); + if (!c->workqueue) + goto exit; + c->last_chipselect = -1; + + dev_info(&dev->dev, "at %#llx, irq %d, %dMHz\n", + (unsigned long long)res->start, c->irq, + (c->baseclk + 500000) / 1000000); + + master->bus_num = dev->id; + master->setup = txx9spi_setup; + master->transfer = txx9spi_transfer; + master->num_chipselect = (u16)UINT_MAX; /* any GPIO numbers */ + + ret = spi_register_master(master); + if (ret) + goto exit; + return 0; +exit: + if (c->workqueue) + destroy_workqueue(c->workqueue); + if (c->irq >= 0) + free_irq(c->irq, c); + if (c->membase) + iounmap(c->membase); + if (c->clk) { + clk_disable(c->clk); + clk_put(c->clk); + } + platform_set_drvdata(dev, NULL); + spi_master_put(master); + return ret; +} + +static int __exit txx9spi_remove(struct platform_device *dev) +{ + struct spi_master *master = spi_master_get(platform_get_drvdata(dev)); + struct txx9spi *c = spi_master_get_devdata(master); + + spi_unregister_master(master); + platform_set_drvdata(dev, NULL); + destroy_workqueue(c->workqueue); + free_irq(c->irq, c); + iounmap(c->membase); + clk_disable(c->clk); + clk_put(c->clk); + spi_master_put(master); + return 0; +} + +static struct platform_driver txx9spi_driver = { + .remove = __exit_p(txx9spi_remove), + .driver = { + .name = "txx9spi", + .owner = THIS_MODULE, + }, +}; + +static int __init txx9spi_init(void) +{ + return platform_driver_probe(&txx9spi_driver, txx9spi_probe); +} +subsys_initcall(txx9spi_init); + +static void __exit txx9spi_exit(void) +{ + platform_driver_unregister(&txx9spi_driver); +} +module_exit(txx9spi_exit); + +MODULE_DESCRIPTION("TXx9 SPI Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index d04242aee40d..38b60ad0eda0 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -181,7 +181,8 @@ static int spidev_message(struct spidev_data *spidev, } if (u_tmp->tx_buf) { k_tmp->tx_buf = buf; - if (copy_from_user(buf, (const u8 __user *)u_tmp->tx_buf, + if (copy_from_user(buf, (const u8 __user *) + (ptrdiff_t) u_tmp->tx_buf, u_tmp->len)) goto done; } @@ -213,7 +214,8 @@ static int spidev_message(struct spidev_data *spidev, buf = spidev->buffer; for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) { if (u_tmp->rx_buf) { - if (__copy_to_user((u8 __user *)u_tmp->rx_buf, buf, + if (__copy_to_user((u8 __user *) + (ptrdiff_t) u_tmp->rx_buf, buf, u_tmp->len)) { status = -EFAULT; goto done; diff --git a/drivers/spi/tle62x0.c b/drivers/spi/tle62x0.c new file mode 100644 index 000000000000..6da58ca48b33 --- /dev/null +++ b/drivers/spi/tle62x0.c @@ -0,0 +1,328 @@ +/* + * tle62x0.c -- support Infineon TLE62x0 driver chips + * + * Copyright (c) 2007 Simtec Electronics + * Ben Dooks, <ben@simtec.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/device.h> +#include <linux/kernel.h> + +#include <linux/spi/spi.h> +#include <linux/spi/tle62x0.h> + + +#define CMD_READ 0x00 +#define CMD_SET 0xff + +#define DIAG_NORMAL 0x03 +#define DIAG_OVERLOAD 0x02 +#define DIAG_OPEN 0x01 +#define DIAG_SHORTGND 0x00 + +struct tle62x0_state { + struct spi_device *us; + struct mutex lock; + unsigned int nr_gpio; + unsigned int gpio_state; + + unsigned char tx_buff[4]; + unsigned char rx_buff[4]; +}; + +static int to_gpio_num(struct device_attribute *attr); + +static inline int tle62x0_write(struct tle62x0_state *st) +{ + unsigned char *buff = st->tx_buff; + unsigned int gpio_state = st->gpio_state; + + buff[0] = CMD_SET; + + if (st->nr_gpio == 16) { + buff[1] = gpio_state >> 8; + buff[2] = gpio_state; + } else { + buff[1] = gpio_state; + } + + dev_dbg(&st->us->dev, "buff %02x,%02x,%02x\n", + buff[0], buff[1], buff[2]); + + return spi_write(st->us, buff, (st->nr_gpio == 16) ? 3 : 2); +} + +static inline int tle62x0_read(struct tle62x0_state *st) +{ + unsigned char *txbuff = st->tx_buff; + struct spi_transfer xfer = { + .tx_buf = txbuff, + .rx_buf = st->rx_buff, + .len = (st->nr_gpio * 2) / 8, + }; + struct spi_message msg; + + txbuff[0] = CMD_READ; + txbuff[1] = 0x00; + txbuff[2] = 0x00; + txbuff[3] = 0x00; + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + return spi_sync(st->us, &msg); +} + +static unsigned char *decode_fault(unsigned int fault_code) +{ + fault_code &= 3; + + switch (fault_code) { + case DIAG_NORMAL: + return "N"; + case DIAG_OVERLOAD: + return "V"; + case DIAG_OPEN: + return "O"; + case DIAG_SHORTGND: + return "G"; + } + + return "?"; +} + +static ssize_t tle62x0_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tle62x0_state *st = dev_get_drvdata(dev); + char *bp = buf; + unsigned char *buff = st->rx_buff; + unsigned long fault = 0; + int ptr; + int ret; + + mutex_lock(&st->lock); + ret = tle62x0_read(st); + + dev_dbg(dev, "tle62x0_read() returned %d\n", ret); + + for (ptr = 0; ptr < (st->nr_gpio * 2)/8; ptr += 1) { + fault <<= 8; + fault |= ((unsigned long)buff[ptr]); + + dev_dbg(dev, "byte %d is %02x\n", ptr, buff[ptr]); + } + + for (ptr = 0; ptr < st->nr_gpio; ptr++) { + bp += sprintf(bp, "%s ", decode_fault(fault >> (ptr * 2))); + } + + *bp++ = '\n'; + + mutex_unlock(&st->lock); + return bp - buf; +} + +static DEVICE_ATTR(status_show, S_IRUGO, tle62x0_status_show, NULL); + +static ssize_t tle62x0_gpio_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tle62x0_state *st = dev_get_drvdata(dev); + int gpio_num = to_gpio_num(attr); + int value; + + mutex_lock(&st->lock); + value = (st->gpio_state >> gpio_num) & 1; + mutex_unlock(&st->lock); + + return snprintf(buf, PAGE_SIZE, "%d", value); +} + +static ssize_t tle62x0_gpio_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct tle62x0_state *st = dev_get_drvdata(dev); + int gpio_num = to_gpio_num(attr); + unsigned long val; + char *endp; + + val = simple_strtoul(buf, &endp, 0); + if (buf == endp) + return -EINVAL; + + dev_dbg(dev, "setting gpio %d to %ld\n", gpio_num, val); + + mutex_lock(&st->lock); + + if (val) + st->gpio_state |= 1 << gpio_num; + else + st->gpio_state &= ~(1 << gpio_num); + + tle62x0_write(st); + mutex_unlock(&st->lock); + + return len; +} + +static DEVICE_ATTR(gpio1, S_IWUSR|S_IRUGO, + tle62x0_gpio_show, tle62x0_gpio_store); +static DEVICE_ATTR(gpio2, S_IWUSR|S_IRUGO, + tle62x0_gpio_show, tle62x0_gpio_store); +static DEVICE_ATTR(gpio3, S_IWUSR|S_IRUGO, + tle62x0_gpio_show, tle62x0_gpio_store); +static DEVICE_ATTR(gpio4, S_IWUSR|S_IRUGO, + tle62x0_gpio_show, tle62x0_gpio_store); +static DEVICE_ATTR(gpio5, S_IWUSR|S_IRUGO, + tle62x0_gpio_show, tle62x0_gpio_store); +static DEVICE_ATTR(gpio6, S_IWUSR|S_IRUGO, + tle62x0_gpio_show, tle62x0_gpio_store); +static DEVICE_ATTR(gpio7, S_IWUSR|S_IRUGO, + tle62x0_gpio_show, tle62x0_gpio_store); +static DEVICE_ATTR(gpio8, S_IWUSR|S_IRUGO, + tle62x0_gpio_show, tle62x0_gpio_store); +static DEVICE_ATTR(gpio9, S_IWUSR|S_IRUGO, + tle62x0_gpio_show, tle62x0_gpio_store); +static DEVICE_ATTR(gpio10, S_IWUSR|S_IRUGO, + tle62x0_gpio_show, tle62x0_gpio_store); +static DEVICE_ATTR(gpio11, S_IWUSR|S_IRUGO, + tle62x0_gpio_show, tle62x0_gpio_store); +static DEVICE_ATTR(gpio12, S_IWUSR|S_IRUGO, + tle62x0_gpio_show, tle62x0_gpio_store); +static DEVICE_ATTR(gpio13, S_IWUSR|S_IRUGO, + tle62x0_gpio_show, tle62x0_gpio_store); +static DEVICE_ATTR(gpio14, S_IWUSR|S_IRUGO, + tle62x0_gpio_show, tle62x0_gpio_store); +static DEVICE_ATTR(gpio15, S_IWUSR|S_IRUGO, + tle62x0_gpio_show, tle62x0_gpio_store); +static DEVICE_ATTR(gpio16, S_IWUSR|S_IRUGO, + tle62x0_gpio_show, tle62x0_gpio_store); + +static struct device_attribute *gpio_attrs[] = { + [0] = &dev_attr_gpio1, + [1] = &dev_attr_gpio2, + [2] = &dev_attr_gpio3, + [3] = &dev_attr_gpio4, + [4] = &dev_attr_gpio5, + [5] = &dev_attr_gpio6, + [6] = &dev_attr_gpio7, + [7] = &dev_attr_gpio8, + [8] = &dev_attr_gpio9, + [9] = &dev_attr_gpio10, + [10] = &dev_attr_gpio11, + [11] = &dev_attr_gpio12, + [12] = &dev_attr_gpio13, + [13] = &dev_attr_gpio14, + [14] = &dev_attr_gpio15, + [15] = &dev_attr_gpio16 +}; + +static int to_gpio_num(struct device_attribute *attr) +{ + int ptr; + + for (ptr = 0; ptr < ARRAY_SIZE(gpio_attrs); ptr++) { + if (gpio_attrs[ptr] == attr) + return ptr; + } + + return -1; +} + +static int __devinit tle62x0_probe(struct spi_device *spi) +{ + struct tle62x0_state *st; + struct tle62x0_pdata *pdata; + int ptr; + int ret; + + pdata = spi->dev.platform_data; + if (pdata == NULL) { + dev_err(&spi->dev, "no device data specified\n"); + return -EINVAL; + } + + st = kzalloc(sizeof(struct tle62x0_state), GFP_KERNEL); + if (st == NULL) { + dev_err(&spi->dev, "no memory for device state\n"); + return -ENOMEM; + } + + st->us = spi; + st->nr_gpio = pdata->gpio_count; + st->gpio_state = pdata->init_state; + + mutex_init(&st->lock); + + ret = device_create_file(&spi->dev, &dev_attr_status_show); + if (ret) { + dev_err(&spi->dev, "cannot create status attribute\n"); + goto err_status; + } + + for (ptr = 0; ptr < pdata->gpio_count; ptr++) { + ret = device_create_file(&spi->dev, gpio_attrs[ptr]); + if (ret) { + dev_err(&spi->dev, "cannot create gpio attribute\n"); + goto err_gpios; + } + } + + /* tle62x0_write(st); */ + spi_set_drvdata(spi, st); + return 0; + + err_gpios: + for (; ptr > 0; ptr--) + device_remove_file(&spi->dev, gpio_attrs[ptr]); + + device_remove_file(&spi->dev, &dev_attr_status_show); + + err_status: + kfree(st); + return ret; +} + +static int __devexit tle62x0_remove(struct spi_device *spi) +{ + struct tle62x0_state *st = spi_get_drvdata(spi); + int ptr; + + for (ptr = 0; ptr < st->nr_gpio; ptr++) + device_remove_file(&spi->dev, gpio_attrs[ptr]); + + kfree(st); + return 0; +} + +static struct spi_driver tle62x0_driver = { + .driver = { + .name = "tle62x0", + .owner = THIS_MODULE, + }, + .probe = tle62x0_probe, + .remove = __devexit_p(tle62x0_remove), +}; + +static __init int tle62x0_init(void) +{ + return spi_register_driver(&tle62x0_driver); +} + +static __exit void tle62x0_exit(void) +{ + spi_unregister_driver(&tle62x0_driver); +} + +module_init(tle62x0_init); +module_exit(tle62x0_exit); + +MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); +MODULE_DESCRIPTION("TLE62x0 SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c new file mode 100644 index 000000000000..f0bf9a68e96b --- /dev/null +++ b/drivers/spi/xilinx_spi.c @@ -0,0 +1,434 @@ +/* + * xilinx_spi.c + * + * Xilinx SPI controller driver (master mode only) + * + * Author: MontaVista Software, Inc. + * source@mvista.com + * + * 2002-2007 (c) MontaVista Software, Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is licensed + * "as is" without any warranty of any kind, whether express or implied. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/spi/spi_bitbang.h> +#include <linux/io.h> + +#include <syslib/virtex_devices.h> + +#define XILINX_SPI_NAME "xspi" + +/* Register definitions as per "OPB Serial Peripheral Interface (SPI) (v1.00e) + * Product Specification", DS464 + */ +#define XSPI_CR_OFFSET 0x62 /* 16-bit Control Register */ + +#define XSPI_CR_ENABLE 0x02 +#define XSPI_CR_MASTER_MODE 0x04 +#define XSPI_CR_CPOL 0x08 +#define XSPI_CR_CPHA 0x10 +#define XSPI_CR_MODE_MASK (XSPI_CR_CPHA | XSPI_CR_CPOL) +#define XSPI_CR_TXFIFO_RESET 0x20 +#define XSPI_CR_RXFIFO_RESET 0x40 +#define XSPI_CR_MANUAL_SSELECT 0x80 +#define XSPI_CR_TRANS_INHIBIT 0x100 + +#define XSPI_SR_OFFSET 0x67 /* 8-bit Status Register */ + +#define XSPI_SR_RX_EMPTY_MASK 0x01 /* Receive FIFO is empty */ +#define XSPI_SR_RX_FULL_MASK 0x02 /* Receive FIFO is full */ +#define XSPI_SR_TX_EMPTY_MASK 0x04 /* Transmit FIFO is empty */ +#define XSPI_SR_TX_FULL_MASK 0x08 /* Transmit FIFO is full */ +#define XSPI_SR_MODE_FAULT_MASK 0x10 /* Mode fault error */ + +#define XSPI_TXD_OFFSET 0x6b /* 8-bit Data Transmit Register */ +#define XSPI_RXD_OFFSET 0x6f /* 8-bit Data Receive Register */ + +#define XSPI_SSR_OFFSET 0x70 /* 32-bit Slave Select Register */ + +/* Register definitions as per "OPB IPIF (v3.01c) Product Specification", DS414 + * IPIF registers are 32 bit + */ +#define XIPIF_V123B_DGIER_OFFSET 0x1c /* IPIF global int enable reg */ +#define XIPIF_V123B_GINTR_ENABLE 0x80000000 + +#define XIPIF_V123B_IISR_OFFSET 0x20 /* IPIF interrupt status reg */ +#define XIPIF_V123B_IIER_OFFSET 0x28 /* IPIF interrupt enable reg */ + +#define XSPI_INTR_MODE_FAULT 0x01 /* Mode fault error */ +#define XSPI_INTR_SLAVE_MODE_FAULT 0x02 /* Selected as slave while + * disabled */ +#define XSPI_INTR_TX_EMPTY 0x04 /* TxFIFO is empty */ +#define XSPI_INTR_TX_UNDERRUN 0x08 /* TxFIFO was underrun */ +#define XSPI_INTR_RX_FULL 0x10 /* RxFIFO is full */ +#define XSPI_INTR_RX_OVERRUN 0x20 /* RxFIFO was overrun */ + +#define XIPIF_V123B_RESETR_OFFSET 0x40 /* IPIF reset register */ +#define XIPIF_V123B_RESET_MASK 0x0a /* the value to write */ + +struct xilinx_spi { + /* bitbang has to be first */ + struct spi_bitbang bitbang; + struct completion done; + + void __iomem *regs; /* virt. address of the control registers */ + + u32 irq; + + u32 speed_hz; /* SCK has a fixed frequency of speed_hz Hz */ + + u8 *rx_ptr; /* pointer in the Tx buffer */ + const u8 *tx_ptr; /* pointer in the Rx buffer */ + int remaining_bytes; /* the number of bytes left to transfer */ +}; + +static void xspi_init_hw(void __iomem *regs_base) +{ + /* Reset the SPI device */ + out_be32(regs_base + XIPIF_V123B_RESETR_OFFSET, + XIPIF_V123B_RESET_MASK); + /* Disable all the interrupts just in case */ + out_be32(regs_base + XIPIF_V123B_IIER_OFFSET, 0); + /* Enable the global IPIF interrupt */ + out_be32(regs_base + XIPIF_V123B_DGIER_OFFSET, + XIPIF_V123B_GINTR_ENABLE); + /* Deselect the slave on the SPI bus */ + out_be32(regs_base + XSPI_SSR_OFFSET, 0xffff); + /* Disable the transmitter, enable Manual Slave Select Assertion, + * put SPI controller into master mode, and enable it */ + out_be16(regs_base + XSPI_CR_OFFSET, + XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT + | XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE); +} + +static void xilinx_spi_chipselect(struct spi_device *spi, int is_on) +{ + struct xilinx_spi *xspi = spi_master_get_devdata(spi->master); + + if (is_on == BITBANG_CS_INACTIVE) { + /* Deselect the slave on the SPI bus */ + out_be32(xspi->regs + XSPI_SSR_OFFSET, 0xffff); + } else if (is_on == BITBANG_CS_ACTIVE) { + /* Set the SPI clock phase and polarity */ + u16 cr = in_be16(xspi->regs + XSPI_CR_OFFSET) + & ~XSPI_CR_MODE_MASK; + if (spi->mode & SPI_CPHA) + cr |= XSPI_CR_CPHA; + if (spi->mode & SPI_CPOL) + cr |= XSPI_CR_CPOL; + out_be16(xspi->regs + XSPI_CR_OFFSET, cr); + + /* We do not check spi->max_speed_hz here as the SPI clock + * frequency is not software programmable (the IP block design + * parameter) + */ + + /* Activate the chip select */ + out_be32(xspi->regs + XSPI_SSR_OFFSET, + ~(0x0001 << spi->chip_select)); + } +} + +/* spi_bitbang requires custom setup_transfer() to be defined if there is a + * custom txrx_bufs(). We have nothing to setup here as the SPI IP block + * supports just 8 bits per word, and SPI clock can't be changed in software. + * Check for 8 bits per word. Chip select delay calculations could be + * added here as soon as bitbang_work() can be made aware of the delay value. + */ +static int xilinx_spi_setup_transfer(struct spi_device *spi, + struct spi_transfer *t) +{ + u8 bits_per_word; + u32 hz; + struct xilinx_spi *xspi = spi_master_get_devdata(spi->master); + + bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word; + hz = (t) ? t->speed_hz : spi->max_speed_hz; + if (bits_per_word != 8) { + dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", + __FUNCTION__, bits_per_word); + return -EINVAL; + } + + if (hz && xspi->speed_hz > hz) { + dev_err(&spi->dev, "%s, unsupported clock rate %uHz\n", + __FUNCTION__, hz); + return -EINVAL; + } + + return 0; +} + +/* the spi->mode bits understood by this driver: */ +#define MODEBITS (SPI_CPOL | SPI_CPHA) + +static int xilinx_spi_setup(struct spi_device *spi) +{ + struct spi_bitbang *bitbang; + struct xilinx_spi *xspi; + int retval; + + xspi = spi_master_get_devdata(spi->master); + bitbang = &xspi->bitbang; + + if (!spi->bits_per_word) + spi->bits_per_word = 8; + + if (spi->mode & ~MODEBITS) { + dev_err(&spi->dev, "%s, unsupported mode bits %x\n", + __FUNCTION__, spi->mode & ~MODEBITS); + return -EINVAL; + } + + retval = xilinx_spi_setup_transfer(spi, NULL); + if (retval < 0) + return retval; + + dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec/bit\n", + __FUNCTION__, spi->mode & MODEBITS, spi->bits_per_word, 0); + + return 0; +} + +static void xilinx_spi_fill_tx_fifo(struct xilinx_spi *xspi) +{ + u8 sr; + + /* Fill the Tx FIFO with as many bytes as possible */ + sr = in_8(xspi->regs + XSPI_SR_OFFSET); + while ((sr & XSPI_SR_TX_FULL_MASK) == 0 && xspi->remaining_bytes > 0) { + if (xspi->tx_ptr) { + out_8(xspi->regs + XSPI_TXD_OFFSET, *xspi->tx_ptr++); + } else { + out_8(xspi->regs + XSPI_TXD_OFFSET, 0); + } + xspi->remaining_bytes--; + sr = in_8(xspi->regs + XSPI_SR_OFFSET); + } +} + +static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) +{ + struct xilinx_spi *xspi = spi_master_get_devdata(spi->master); + u32 ipif_ier; + u16 cr; + + /* We get here with transmitter inhibited */ + + xspi->tx_ptr = t->tx_buf; + xspi->rx_ptr = t->rx_buf; + xspi->remaining_bytes = t->len; + INIT_COMPLETION(xspi->done); + + xilinx_spi_fill_tx_fifo(xspi); + + /* Enable the transmit empty interrupt, which we use to determine + * progress on the transmission. + */ + ipif_ier = in_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET); + out_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET, + ipif_ier | XSPI_INTR_TX_EMPTY); + + /* Start the transfer by not inhibiting the transmitter any longer */ + cr = in_be16(xspi->regs + XSPI_CR_OFFSET) & ~XSPI_CR_TRANS_INHIBIT; + out_be16(xspi->regs + XSPI_CR_OFFSET, cr); + + wait_for_completion(&xspi->done); + + /* Disable the transmit empty interrupt */ + out_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET, ipif_ier); + + return t->len - xspi->remaining_bytes; +} + + +/* This driver supports single master mode only. Hence Tx FIFO Empty + * is the only interrupt we care about. + * Receive FIFO Overrun, Transmit FIFO Underrun, Mode Fault, and Slave Mode + * Fault are not to happen. + */ +static irqreturn_t xilinx_spi_irq(int irq, void *dev_id) +{ + struct xilinx_spi *xspi = dev_id; + u32 ipif_isr; + + /* Get the IPIF interrupts, and clear them immediately */ + ipif_isr = in_be32(xspi->regs + XIPIF_V123B_IISR_OFFSET); + out_be32(xspi->regs + XIPIF_V123B_IISR_OFFSET, ipif_isr); + + if (ipif_isr & XSPI_INTR_TX_EMPTY) { /* Transmission completed */ + u16 cr; + u8 sr; + + /* A transmit has just completed. Process received data and + * check for more data to transmit. Always inhibit the + * transmitter while the Isr refills the transmit register/FIFO, + * or make sure it is stopped if we're done. + */ + cr = in_be16(xspi->regs + XSPI_CR_OFFSET); + out_be16(xspi->regs + XSPI_CR_OFFSET, + cr | XSPI_CR_TRANS_INHIBIT); + + /* Read out all the data from the Rx FIFO */ + sr = in_8(xspi->regs + XSPI_SR_OFFSET); + while ((sr & XSPI_SR_RX_EMPTY_MASK) == 0) { + u8 data; + + data = in_8(xspi->regs + XSPI_RXD_OFFSET); + if (xspi->rx_ptr) { + *xspi->rx_ptr++ = data; + } + sr = in_8(xspi->regs + XSPI_SR_OFFSET); + } + + /* See if there is more data to send */ + if (xspi->remaining_bytes > 0) { + xilinx_spi_fill_tx_fifo(xspi); + /* Start the transfer by not inhibiting the + * transmitter any longer + */ + out_be16(xspi->regs + XSPI_CR_OFFSET, cr); + } else { + /* No more data to send. + * Indicate the transfer is completed. + */ + complete(&xspi->done); + } + } + + return IRQ_HANDLED; +} + +static int __init xilinx_spi_probe(struct platform_device *dev) +{ + int ret = 0; + struct spi_master *master; + struct xilinx_spi *xspi; + struct xspi_platform_data *pdata; + struct resource *r; + + /* Get resources(memory, IRQ) associated with the device */ + master = spi_alloc_master(&dev->dev, sizeof(struct xilinx_spi)); + + if (master == NULL) { + return -ENOMEM; + } + + platform_set_drvdata(dev, master); + pdata = dev->dev.platform_data; + + if (pdata == NULL) { + ret = -ENODEV; + goto put_master; + } + + r = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (r == NULL) { + ret = -ENODEV; + goto put_master; + } + + xspi = spi_master_get_devdata(master); + xspi->bitbang.master = spi_master_get(master); + xspi->bitbang.chipselect = xilinx_spi_chipselect; + xspi->bitbang.setup_transfer = xilinx_spi_setup_transfer; + xspi->bitbang.txrx_bufs = xilinx_spi_txrx_bufs; + xspi->bitbang.master->setup = xilinx_spi_setup; + init_completion(&xspi->done); + + if (!request_mem_region(r->start, + r->end - r->start + 1, XILINX_SPI_NAME)) { + ret = -ENXIO; + goto put_master; + } + + xspi->regs = ioremap(r->start, r->end - r->start + 1); + if (xspi->regs == NULL) { + ret = -ENOMEM; + goto put_master; + } + + xspi->irq = platform_get_irq(dev, 0); + if (xspi->irq < 0) { + ret = -ENXIO; + goto unmap_io; + } + + master->bus_num = pdata->bus_num; + master->num_chipselect = pdata->num_chipselect; + xspi->speed_hz = pdata->speed_hz; + + /* SPI controller initializations */ + xspi_init_hw(xspi->regs); + + /* Register for SPI Interrupt */ + ret = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi); + if (ret != 0) + goto unmap_io; + + ret = spi_bitbang_start(&xspi->bitbang); + if (ret != 0) { + dev_err(&dev->dev, "spi_bitbang_start FAILED\n"); + goto free_irq; + } + + dev_info(&dev->dev, "at 0x%08X mapped to 0x%08X, irq=%d\n", + r->start, (u32)xspi->regs, xspi->irq); + + return ret; + +free_irq: + free_irq(xspi->irq, xspi); +unmap_io: + iounmap(xspi->regs); +put_master: + spi_master_put(master); + return ret; +} + +static int __devexit xilinx_spi_remove(struct platform_device *dev) +{ + struct xilinx_spi *xspi; + struct spi_master *master; + + master = platform_get_drvdata(dev); + xspi = spi_master_get_devdata(master); + + spi_bitbang_stop(&xspi->bitbang); + free_irq(xspi->irq, xspi); + iounmap(xspi->regs); + platform_set_drvdata(dev, 0); + spi_master_put(xspi->bitbang.master); + + return 0; +} + +static struct platform_driver xilinx_spi_driver = { + .probe = xilinx_spi_probe, + .remove = __devexit_p(xilinx_spi_remove), + .driver = { + .name = XILINX_SPI_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init xilinx_spi_init(void) +{ + return platform_driver_register(&xilinx_spi_driver); +} +module_init(xilinx_spi_init); + +static void __exit xilinx_spi_exit(void) +{ + platform_driver_unregister(&xilinx_spi_driver); +} +module_exit(xilinx_spi_exit); + +MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>"); +MODULE_DESCRIPTION("Xilinx SPI driver"); +MODULE_LICENSE("GPL"); |