diff options
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/Kconfig | 14 | ||||
-rw-r--r-- | drivers/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spi/atmel_spi.c | 173 | ||||
-rw-r--r-- | drivers/spi/mpc52xx_psc_spi.c | 77 | ||||
-rw-r--r-- | drivers/spi/omap2_mcspi.c | 43 | ||||
-rw-r--r-- | drivers/spi/pxa2xx_spi.c | 156 | ||||
-rw-r--r-- | drivers/spi/spi.c | 61 | ||||
-rw-r--r-- | drivers/spi/spi_bfin5xx.c | 131 | ||||
-rw-r--r-- | drivers/spi/spi_imx.c | 13 | ||||
-rw-r--r-- | drivers/spi/spi_mpc83xx.c | 6 | ||||
-rw-r--r-- | drivers/spi/spi_s3c24xx.c | 12 | ||||
-rw-r--r-- | drivers/spi/spi_s3c24xx_gpio.c | 12 | ||||
-rw-r--r-- | drivers/spi/spi_sh_sci.c | 205 |
13 files changed, 496 insertions, 408 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index abf05048c638..d8107890db15 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -144,15 +144,16 @@ config SPI_OMAP_UWIRE This hooks up to the MicroWire controller on OMAP1 chips. config SPI_OMAP24XX - tristate "McSPI driver for OMAP24xx" - depends on SPI_MASTER && ARCH_OMAP24XX + tristate "McSPI driver for OMAP24xx/OMAP34xx" + depends on SPI_MASTER && (ARCH_OMAP24XX || ARCH_OMAP34XX) help - SPI master controller for OMAP24xx Multichannel SPI + SPI master controller for OMAP24xx/OMAP34xx Multichannel SPI (McSPI) modules. config SPI_PXA2XX tristate "PXA2xx SSP SPI master" depends on SPI_MASTER && ARCH_PXA && EXPERIMENTAL + select PXA_SSP help This enables using a PXA2xx SSP port as a SPI master controller. The driver can be configured to use any SSP port and additional @@ -175,6 +176,13 @@ config SPI_S3C24XX_GPIO the inbuilt hardware cannot provide the transfer mode, or where the board is using non hardware connected pins. +config SPI_SH_SCI + tristate "SuperH SCI SPI controller" + depends on SPI_MASTER && SUPERH + select SPI_BITBANG + help + SPI driver for SuperH SCI blocks. + config SPI_TXX9 tristate "Toshiba TXx9 SPI controller" depends on SPI_MASTER && GENERIC_GPIO && CPU_TX49XX diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 41fbac45c323..7fca043ce723 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o obj-$(CONFIG_SPI_TXX9) += spi_txx9.o obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o +obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o # ... add above this line ... # SPI protocol drivers (device/link on bus) diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index ff10808183a3..293b7cab3e57 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -51,7 +51,9 @@ struct atmel_spi { u8 stopping; struct list_head queue; struct spi_transfer *current_transfer; - unsigned long remaining_bytes; + unsigned long current_remaining_bytes; + struct spi_transfer *next_transfer; + unsigned long next_remaining_bytes; void *buffer; dma_addr_t buffer_dma; @@ -121,6 +123,48 @@ static void cs_deactivate(struct atmel_spi *as, struct spi_device *spi) gpio_set_value(gpio, !active); } +static inline int atmel_spi_xfer_is_last(struct spi_message *msg, + struct spi_transfer *xfer) +{ + return msg->transfers.prev == &xfer->transfer_list; +} + +static inline int atmel_spi_xfer_can_be_chained(struct spi_transfer *xfer) +{ + return xfer->delay_usecs == 0 && !xfer->cs_change; +} + +static void atmel_spi_next_xfer_data(struct spi_master *master, + struct spi_transfer *xfer, + dma_addr_t *tx_dma, + dma_addr_t *rx_dma, + u32 *plen) +{ + struct atmel_spi *as = spi_master_get_devdata(master); + u32 len = *plen; + + /* use scratch buffer only when rx or tx data is unspecified */ + if (xfer->rx_buf) + *rx_dma = xfer->rx_dma + xfer->len - len; + else { + *rx_dma = as->buffer_dma; + if (len > BUFFER_SIZE) + len = BUFFER_SIZE; + } + if (xfer->tx_buf) + *tx_dma = xfer->tx_dma + xfer->len - len; + else { + *tx_dma = as->buffer_dma; + if (len > BUFFER_SIZE) + len = BUFFER_SIZE; + memset(as->buffer, 0, len); + dma_sync_single_for_device(&as->pdev->dev, + as->buffer_dma, len, DMA_TO_DEVICE); + } + + *plen = len; +} + /* * Submit next transfer for DMA. * lock is held, spi irq is blocked @@ -130,53 +174,78 @@ static void atmel_spi_next_xfer(struct spi_master *master, { struct atmel_spi *as = spi_master_get_devdata(master); struct spi_transfer *xfer; - u32 len; + u32 len, remaining, total; dma_addr_t tx_dma, rx_dma; - xfer = as->current_transfer; - if (!xfer || as->remaining_bytes == 0) { - if (xfer) - xfer = list_entry(xfer->transfer_list.next, - struct spi_transfer, transfer_list); - else - xfer = list_entry(msg->transfers.next, - struct spi_transfer, transfer_list); - as->remaining_bytes = xfer->len; - as->current_transfer = xfer; - } + if (!as->current_transfer) + xfer = list_entry(msg->transfers.next, + struct spi_transfer, transfer_list); + else if (!as->next_transfer) + xfer = list_entry(as->current_transfer->transfer_list.next, + struct spi_transfer, transfer_list); + else + xfer = NULL; - len = as->remaining_bytes; + if (xfer) { + len = xfer->len; + atmel_spi_next_xfer_data(master, xfer, &tx_dma, &rx_dma, &len); + remaining = xfer->len - len; - tx_dma = xfer->tx_dma + xfer->len - len; - rx_dma = xfer->rx_dma + xfer->len - len; + spi_writel(as, RPR, rx_dma); + spi_writel(as, TPR, tx_dma); - /* use scratch buffer only when rx or tx data is unspecified */ - if (!xfer->rx_buf) { - rx_dma = as->buffer_dma; - if (len > BUFFER_SIZE) - len = BUFFER_SIZE; - } - if (!xfer->tx_buf) { - tx_dma = as->buffer_dma; - if (len > BUFFER_SIZE) - len = BUFFER_SIZE; - memset(as->buffer, 0, len); - dma_sync_single_for_device(&as->pdev->dev, - as->buffer_dma, len, DMA_TO_DEVICE); + if (msg->spi->bits_per_word > 8) + len >>= 1; + spi_writel(as, RCR, len); + spi_writel(as, TCR, len); + + dev_dbg(&msg->spi->dev, + " start xfer %p: len %u tx %p/%08x rx %p/%08x\n", + xfer, xfer->len, xfer->tx_buf, xfer->tx_dma, + xfer->rx_buf, xfer->rx_dma); + } else { + xfer = as->next_transfer; + remaining = as->next_remaining_bytes; } - spi_writel(as, RPR, rx_dma); - spi_writel(as, TPR, tx_dma); + as->current_transfer = xfer; + as->current_remaining_bytes = remaining; - as->remaining_bytes -= len; - if (msg->spi->bits_per_word > 8) - len >>= 1; + if (remaining > 0) + len = remaining; + else if (!atmel_spi_xfer_is_last(msg, xfer) + && atmel_spi_xfer_can_be_chained(xfer)) { + xfer = list_entry(xfer->transfer_list.next, + struct spi_transfer, transfer_list); + len = xfer->len; + } else + xfer = NULL; - /* 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 + as->next_transfer = xfer; + + if (xfer) { + total = len; + atmel_spi_next_xfer_data(master, xfer, &tx_dma, &rx_dma, &len); + as->next_remaining_bytes = total - len; + + spi_writel(as, RNPR, rx_dma); + spi_writel(as, TNPR, tx_dma); + + if (msg->spi->bits_per_word > 8) + len >>= 1; + spi_writel(as, RNCR, len); + spi_writel(as, TNCR, len); + + dev_dbg(&msg->spi->dev, + " next xfer %p: len %u tx %p/%08x rx %p/%08x\n", + xfer, xfer->len, xfer->tx_buf, xfer->tx_dma, + xfer->rx_buf, xfer->rx_dma); + } else { + spi_writel(as, RNCR, 0); + spi_writel(as, TNCR, 0); + } + + /* REVISIT: We're waiting for ENDRX before we start the next * transfer because we need to handle some difficult timing * issues otherwise. If we wait for ENDTX in one transfer and * then starts waiting for ENDRX in the next, it's difficult @@ -186,17 +255,7 @@ static void atmel_spi_next_xfer(struct spi_master *master, * * It should be doable, though. Just not now... */ - spi_writel(as, TNCR, 0); - spi_writel(as, RNCR, 0); spi_writel(as, IER, SPI_BIT(ENDRX) | SPI_BIT(OVRES)); - - dev_dbg(&msg->spi->dev, - " start xfer %p: len %u tx %p/%08x rx %p/%08x imr %03x\n", - xfer, xfer->len, xfer->tx_buf, xfer->tx_dma, - xfer->rx_buf, xfer->rx_dma, spi_readl(as, IMR)); - - spi_writel(as, RCR, len); - spi_writel(as, TCR, len); spi_writel(as, PTCR, SPI_BIT(TXTEN) | SPI_BIT(RXTEN)); } @@ -294,6 +353,7 @@ atmel_spi_msg_done(struct spi_master *master, struct atmel_spi *as, spin_lock(&as->lock); as->current_transfer = NULL; + as->next_transfer = NULL; /* continue if needed */ if (list_empty(&as->queue) || as->stopping) @@ -377,7 +437,7 @@ atmel_spi_interrupt(int irq, void *dev_id) spi_writel(as, IDR, pending); - if (as->remaining_bytes == 0) { + if (as->current_remaining_bytes == 0) { msg->actual_length += xfer->len; if (!msg->is_dma_mapped) @@ -387,7 +447,7 @@ atmel_spi_interrupt(int irq, void *dev_id) if (xfer->delay_usecs) udelay(xfer->delay_usecs); - if (msg->transfers.prev == &xfer->transfer_list) { + if (atmel_spi_xfer_is_last(msg, xfer)) { /* report completed message */ atmel_spi_msg_done(master, as, msg, 0, xfer->cs_change); @@ -490,9 +550,14 @@ static int atmel_spi_setup(struct spi_device *spi) if (!(spi->mode & SPI_CPHA)) csr |= SPI_BIT(NCPHA); - /* TODO: DLYBS and DLYBCT */ - csr |= SPI_BF(DLYBS, 10); - csr |= SPI_BF(DLYBCT, 10); + /* DLYBS is mostly irrelevant since we manage chipselect using GPIOs. + * + * DLYBCT would add delays between words, slowing down transfers. + * It could potentially be useful to cope with DMA bottlenecks, but + * in those cases it's probably best to just use a lower bitrate. + */ + csr |= SPI_BF(DLYBS, 0); + csr |= SPI_BF(DLYBCT, 0); /* chipselect must have been muxed as GPIO (e.g. in board setup) */ npcs_pin = (unsigned int)spi->controller_data; diff --git a/drivers/spi/mpc52xx_psc_spi.c b/drivers/spi/mpc52xx_psc_spi.c index 7051e6c5edc3..253ed5682a6d 100644 --- a/drivers/spi/mpc52xx_psc_spi.c +++ b/drivers/spi/mpc52xx_psc_spi.c @@ -330,75 +330,13 @@ static void mpc52xx_psc_spi_cleanup(struct spi_device *spi) static int mpc52xx_psc_spi_port_config(int psc_id, struct mpc52xx_psc_spi *mps) { - struct mpc52xx_cdm __iomem *cdm; - struct mpc52xx_gpio __iomem *gpio; struct mpc52xx_psc __iomem *psc = mps->psc; - u32 ul; u32 mclken_div; int ret = 0; -#if defined(CONFIG_PPC_MERGE) - cdm = mpc52xx_find_and_map("mpc5200-cdm"); - gpio = mpc52xx_find_and_map("mpc5200-gpio"); -#else - cdm = ioremap(MPC52xx_PA(MPC52xx_CDM_OFFSET), MPC52xx_CDM_SIZE); - gpio = ioremap(MPC52xx_PA(MPC52xx_GPIO_OFFSET), MPC52xx_GPIO_SIZE); -#endif - if (!cdm || !gpio) { - printk(KERN_ERR "Error mapping CDM/GPIO\n"); - ret = -EFAULT; - goto unmap_regs; - } - /* default sysclk is 512MHz */ - mclken_div = 0x8000 | - (((mps->sysclk ? mps->sysclk : 512000000) / MCLK) & 0x1FF); - - switch (psc_id) { - case 1: - ul = in_be32(&gpio->port_config); - ul &= 0xFFFFFFF8; - ul |= 0x00000006; - out_be32(&gpio->port_config, ul); - out_be16(&cdm->mclken_div_psc1, mclken_div); - ul = in_be32(&cdm->clk_enables); - ul |= 0x00000020; - out_be32(&cdm->clk_enables, ul); - break; - case 2: - ul = in_be32(&gpio->port_config); - ul &= 0xFFFFFF8F; - ul |= 0x00000060; - out_be32(&gpio->port_config, ul); - out_be16(&cdm->mclken_div_psc2, mclken_div); - ul = in_be32(&cdm->clk_enables); - ul |= 0x00000040; - out_be32(&cdm->clk_enables, ul); - break; - case 3: - ul = in_be32(&gpio->port_config); - ul &= 0xFFFFF0FF; - ul |= 0x00000600; - out_be32(&gpio->port_config, ul); - out_be16(&cdm->mclken_div_psc3, mclken_div); - ul = in_be32(&cdm->clk_enables); - ul |= 0x00000080; - out_be32(&cdm->clk_enables, ul); - break; - case 6: - ul = in_be32(&gpio->port_config); - ul &= 0xFF8FFFFF; - ul |= 0x00700000; - out_be32(&gpio->port_config, ul); - out_be16(&cdm->mclken_div_psc6, mclken_div); - ul = in_be32(&cdm->clk_enables); - ul |= 0x00000010; - out_be32(&cdm->clk_enables, ul); - break; - default: - ret = -EINVAL; - goto unmap_regs; - } + mclken_div = (mps->sysclk ? mps->sysclk : 512000000) / MCLK; + mpc52xx_set_psc_clkdiv(psc_id, mclken_div); /* Reset the PSC into a known state */ out_8(&psc->command, MPC52xx_PSC_RST_RX); @@ -422,12 +360,6 @@ static int mpc52xx_psc_spi_port_config(int psc_id, struct mpc52xx_psc_spi *mps) mps->bits_per_word = 8; -unmap_regs: - if (cdm) - iounmap(cdm); - if (gpio) - iounmap(gpio); - return ret; } @@ -623,8 +555,9 @@ static int __exit mpc52xx_psc_spi_of_remove(struct of_device *op) } static struct of_device_id mpc52xx_psc_spi_of_match[] = { - { .type = "spi", .compatible = "mpc5200-psc-spi", }, - {}, + { .compatible = "fsl,mpc5200-psc-spi", }, + { .compatible = "mpc5200-psc-spi", }, /* old */ + {} }; MODULE_DEVICE_TABLE(of, mpc52xx_psc_spi_of_match); diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index 3cdab131c4a9..a6ba11afb03f 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c @@ -350,6 +350,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) tx = xfer->tx_buf; do { + c -= 1; if (tx != NULL) { if (mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_TXS) < 0) { @@ -380,7 +381,6 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) word_len, *(rx - 1)); #endif } - c -= 1; } while (c); } else if (word_len <= 16) { u16 *rx; @@ -389,6 +389,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) rx = xfer->rx_buf; tx = xfer->tx_buf; do { + c -= 2; if (tx != NULL) { if (mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_TXS) < 0) { @@ -419,7 +420,6 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) word_len, *(rx - 1)); #endif } - c -= 2; } while (c); } else if (word_len <= 32) { u32 *rx; @@ -428,6 +428,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) rx = xfer->rx_buf; tx = xfer->tx_buf; do { + c -= 4; if (tx != NULL) { if (mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_TXS) < 0) { @@ -458,7 +459,6 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) word_len, *(rx - 1)); #endif } - c -= 4; } while (c); } @@ -915,6 +915,28 @@ static u8 __initdata spi2_txdma_id[] = { OMAP24XX_DMA_SPI2_TX1, }; +#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX) +static u8 __initdata spi3_rxdma_id[] = { + OMAP24XX_DMA_SPI3_RX0, + OMAP24XX_DMA_SPI3_RX1, +}; + +static u8 __initdata spi3_txdma_id[] = { + OMAP24XX_DMA_SPI3_TX0, + OMAP24XX_DMA_SPI3_TX1, +}; +#endif + +#ifdef CONFIG_ARCH_OMAP3 +static u8 __initdata spi4_rxdma_id[] = { + OMAP34XX_DMA_SPI4_RX0, +}; + +static u8 __initdata spi4_txdma_id[] = { + OMAP34XX_DMA_SPI4_TX0, +}; +#endif + static int __init omap2_mcspi_probe(struct platform_device *pdev) { struct spi_master *master; @@ -935,7 +957,20 @@ static int __init omap2_mcspi_probe(struct platform_device *pdev) txdma_id = spi2_txdma_id; num_chipselect = 2; break; - /* REVISIT omap2430 has a third McSPI ... */ +#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) + case 3: + rxdma_id = spi3_rxdma_id; + txdma_id = spi3_txdma_id; + num_chipselect = 2; + break; +#endif +#ifdef CONFIG_ARCH_OMAP3 + case 4: + rxdma_id = spi4_rxdma_id; + txdma_id = spi4_txdma_id; + num_chipselect = 1; + break; +#endif default: return -EINVAL; } diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index 1c2ab541d37d..365e0e355aea 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -27,6 +27,7 @@ #include <linux/spi/spi.h> #include <linux/workqueue.h> #include <linux/delay.h> +#include <linux/clk.h> #include <asm/io.h> #include <asm/irq.h> @@ -36,6 +37,8 @@ #include <asm/arch/hardware.h> #include <asm/arch/pxa-regs.h> +#include <asm/arch/regs-ssp.h> +#include <asm/arch/ssp.h> #include <asm/arch/pxa2xx_spi.h> MODULE_AUTHOR("Stephen Street"); @@ -80,6 +83,9 @@ struct driver_data { /* Driver model hookup */ struct platform_device *pdev; + /* SSP Info */ + struct ssp_device *ssp; + /* SPI framework hookup */ enum pxa_ssp_type ssp_type; struct spi_master *master; @@ -778,6 +784,16 @@ int set_dma_burst_and_threshold(struct chip_data *chip, struct spi_device *spi, return retval; } +static unsigned int ssp_get_clk_div(struct ssp_device *ssp, int rate) +{ + unsigned long ssp_clk = clk_get_rate(ssp->clk); + + if (ssp->type == PXA25x_SSP) + return ((ssp_clk / (2 * rate) - 1) & 0xff) << 8; + else + return ((ssp_clk / rate - 1) & 0xfff) << 8; +} + static void pump_transfers(unsigned long data) { struct driver_data *drv_data = (struct driver_data *)data; @@ -785,6 +801,7 @@ static void pump_transfers(unsigned long data) struct spi_transfer *transfer = NULL; struct spi_transfer *previous = NULL; struct chip_data *chip = NULL; + struct ssp_device *ssp = drv_data->ssp; void *reg = drv_data->ioaddr; u32 clk_div = 0; u8 bits = 0; @@ -866,12 +883,7 @@ static void pump_transfers(unsigned long data) if (transfer->bits_per_word) bits = transfer->bits_per_word; - if (reg == SSP1_VIRT) - clk_div = SSP1_SerClkDiv(speed); - else if (reg == SSP2_VIRT) - clk_div = SSP2_SerClkDiv(speed); - else if (reg == SSP3_VIRT) - clk_div = SSP3_SerClkDiv(speed); + clk_div = ssp_get_clk_div(ssp, speed); if (bits <= 8) { drv_data->n_bytes = 1; @@ -1074,6 +1086,7 @@ static int setup(struct spi_device *spi) struct pxa2xx_spi_chip *chip_info = NULL; struct chip_data *chip; struct driver_data *drv_data = spi_master_get_devdata(spi->master); + struct ssp_device *ssp = drv_data->ssp; unsigned int clk_div; if (!spi->bits_per_word) @@ -1157,18 +1170,7 @@ static int setup(struct spi_device *spi) } } - if (drv_data->ioaddr == SSP1_VIRT) - clk_div = SSP1_SerClkDiv(spi->max_speed_hz); - else if (drv_data->ioaddr == SSP2_VIRT) - clk_div = SSP2_SerClkDiv(spi->max_speed_hz); - else if (drv_data->ioaddr == SSP3_VIRT) - clk_div = SSP3_SerClkDiv(spi->max_speed_hz); - else - { - dev_err(&spi->dev, "failed setup: unknown IO address=0x%p\n", - drv_data->ioaddr); - return -ENODEV; - } + clk_div = ssp_get_clk_div(ssp, spi->max_speed_hz); chip->speed_hz = spi->max_speed_hz; chip->cr0 = clk_div @@ -1183,15 +1185,15 @@ static int setup(struct spi_device *spi) /* NOTE: PXA25x_SSP _could_ use external clocking ... */ if (drv_data->ssp_type != PXA25x_SSP) - dev_dbg(&spi->dev, "%d bits/word, %d Hz, mode %d\n", + dev_dbg(&spi->dev, "%d bits/word, %ld Hz, mode %d\n", spi->bits_per_word, - (CLOCK_SPEED_HZ) + clk_get_rate(ssp->clk) / (1 + ((chip->cr0 & SSCR0_SCR) >> 8)), spi->mode & 0x3); else - dev_dbg(&spi->dev, "%d bits/word, %d Hz, mode %d\n", + dev_dbg(&spi->dev, "%d bits/word, %ld Hz, mode %d\n", spi->bits_per_word, - (CLOCK_SPEED_HZ/2) + clk_get_rate(ssp->clk) / (1 + ((chip->cr0 & SSCR0_SCR) >> 8)), spi->mode & 0x3); @@ -1323,14 +1325,14 @@ static int __init pxa2xx_spi_probe(struct platform_device *pdev) struct pxa2xx_spi_master *platform_info; struct spi_master *master; struct driver_data *drv_data = 0; - struct resource *memory_resource; - int irq; + struct ssp_device *ssp; int status = 0; platform_info = dev->platform_data; - if (platform_info->ssp_type == SSP_UNDEFINED) { - dev_err(&pdev->dev, "undefined SSP\n"); + ssp = ssp_request(pdev->id, pdev->name); + if (ssp == NULL) { + dev_err(&pdev->dev, "failed to request SSP%d\n", pdev->id); return -ENODEV; } @@ -1338,12 +1340,14 @@ static int __init pxa2xx_spi_probe(struct platform_device *pdev) master = spi_alloc_master(dev, sizeof(struct driver_data) + 16); if (!master) { dev_err(&pdev->dev, "can not alloc spi_master\n"); + ssp_free(ssp); return -ENOMEM; } drv_data = spi_master_get_devdata(master); drv_data->master = master; drv_data->master_info = platform_info; drv_data->pdev = pdev; + drv_data->ssp = ssp; master->bus_num = pdev->id; master->num_chipselect = platform_info->num_chipselect; @@ -1351,21 +1355,13 @@ static int __init pxa2xx_spi_probe(struct platform_device *pdev) master->setup = setup; master->transfer = transfer; - drv_data->ssp_type = platform_info->ssp_type; + drv_data->ssp_type = ssp->type; drv_data->null_dma_buf = (u32 *)ALIGN((u32)(drv_data + sizeof(struct driver_data)), 8); - /* Setup register addresses */ - memory_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!memory_resource) { - dev_err(&pdev->dev, "memory resources not defined\n"); - status = -ENODEV; - goto out_error_master_alloc; - } - - drv_data->ioaddr = (void *)io_p2v((unsigned long)(memory_resource->start)); - drv_data->ssdr_physical = memory_resource->start + 0x00000010; - if (platform_info->ssp_type == PXA25x_SSP) { + drv_data->ioaddr = ssp->mmio_base; + drv_data->ssdr_physical = ssp->phys_base + SSDR; + if (ssp->type == PXA25x_SSP) { drv_data->int_cr1 = SSCR1_TIE | SSCR1_RIE; drv_data->dma_cr1 = 0; drv_data->clear_sr = SSSR_ROR; @@ -1377,15 +1373,7 @@ static int __init pxa2xx_spi_probe(struct platform_device *pdev) drv_data->mask_sr = SSSR_TINT | SSSR_RFS | SSSR_TFS | SSSR_ROR; } - /* Attach to IRQ */ - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "irq resource not defined\n"); - status = -ENODEV; - goto out_error_master_alloc; - } - - status = request_irq(irq, ssp_int, 0, dev->bus_id, drv_data); + status = request_irq(ssp->irq, ssp_int, 0, dev->bus_id, drv_data); if (status < 0) { dev_err(&pdev->dev, "can not get IRQ\n"); goto out_error_master_alloc; @@ -1418,29 +1406,12 @@ static int __init pxa2xx_spi_probe(struct platform_device *pdev) goto out_error_dma_alloc; } - if (drv_data->ioaddr == SSP1_VIRT) { - DRCMRRXSSDR = DRCMR_MAPVLD - | drv_data->rx_channel; - DRCMRTXSSDR = DRCMR_MAPVLD - | drv_data->tx_channel; - } else if (drv_data->ioaddr == SSP2_VIRT) { - DRCMRRXSS2DR = DRCMR_MAPVLD - | drv_data->rx_channel; - DRCMRTXSS2DR = DRCMR_MAPVLD - | drv_data->tx_channel; - } else if (drv_data->ioaddr == SSP3_VIRT) { - DRCMRRXSS3DR = DRCMR_MAPVLD - | drv_data->rx_channel; - DRCMRTXSS3DR = DRCMR_MAPVLD - | drv_data->tx_channel; - } else { - dev_err(dev, "bad SSP type\n"); - goto out_error_dma_alloc; - } + DRCMR(ssp->drcmr_rx) = DRCMR_MAPVLD | drv_data->rx_channel; + DRCMR(ssp->drcmr_tx) = DRCMR_MAPVLD | drv_data->tx_channel; } /* Enable SOC clock */ - pxa_set_cken(platform_info->clock_enable, 1); + clk_enable(ssp->clk); /* Load default SSP configuration */ write_SSCR0(0, drv_data->ioaddr); @@ -1479,7 +1450,7 @@ out_error_queue_alloc: destroy_queue(drv_data); out_error_clock_enabled: - pxa_set_cken(platform_info->clock_enable, 0); + clk_disable(ssp->clk); out_error_dma_alloc: if (drv_data->tx_channel != -1) @@ -1488,17 +1459,18 @@ out_error_dma_alloc: pxa_free_dma(drv_data->rx_channel); out_error_irq_alloc: - free_irq(irq, drv_data); + free_irq(ssp->irq, drv_data); out_error_master_alloc: spi_master_put(master); + ssp_free(ssp); return status; } static int pxa2xx_spi_remove(struct platform_device *pdev) { struct driver_data *drv_data = platform_get_drvdata(pdev); - int irq; + struct ssp_device *ssp = drv_data->ssp; int status = 0; if (!drv_data) @@ -1520,28 +1492,21 @@ static int pxa2xx_spi_remove(struct platform_device *pdev) /* Disable the SSP at the peripheral and SOC level */ write_SSCR0(0, drv_data->ioaddr); - pxa_set_cken(drv_data->master_info->clock_enable, 0); + clk_disable(ssp->clk); /* Release DMA */ if (drv_data->master_info->enable_dma) { - if (drv_data->ioaddr == SSP1_VIRT) { - DRCMRRXSSDR = 0; - DRCMRTXSSDR = 0; - } else if (drv_data->ioaddr == SSP2_VIRT) { - DRCMRRXSS2DR = 0; - DRCMRTXSS2DR = 0; - } else if (drv_data->ioaddr == SSP3_VIRT) { - DRCMRRXSS3DR = 0; - DRCMRTXSS3DR = 0; - } + DRCMR(ssp->drcmr_rx) = 0; + DRCMR(ssp->drcmr_tx) = 0; pxa_free_dma(drv_data->tx_channel); pxa_free_dma(drv_data->rx_channel); } /* Release IRQ */ - irq = platform_get_irq(pdev, 0); - if (irq >= 0) - free_irq(irq, drv_data); + free_irq(ssp->irq, drv_data); + + /* Release SSP */ + ssp_free(ssp); /* Disconnect from the SPI framework */ spi_unregister_master(drv_data->master); @@ -1561,34 +1526,18 @@ static void pxa2xx_spi_shutdown(struct platform_device *pdev) } #ifdef CONFIG_PM -static int suspend_devices(struct device *dev, void *pm_message) -{ - pm_message_t *state = pm_message; - - if (dev->power.power_state.event != state->event) { - dev_warn(dev, "pm state does not match request\n"); - return -1; - } - - return 0; -} static int pxa2xx_spi_suspend(struct platform_device *pdev, pm_message_t state) { struct driver_data *drv_data = platform_get_drvdata(pdev); + struct ssp_device *ssp = drv_data->ssp; int status = 0; - /* Check all childern for current power state */ - if (device_for_each_child(&pdev->dev, &state, suspend_devices) != 0) { - dev_warn(&pdev->dev, "suspend aborted\n"); - return -1; - } - status = stop_queue(drv_data); if (status != 0) return status; write_SSCR0(0, drv_data->ioaddr); - pxa_set_cken(drv_data->master_info->clock_enable, 0); + clk_disable(ssp->clk); return 0; } @@ -1596,10 +1545,11 @@ static int pxa2xx_spi_suspend(struct platform_device *pdev, pm_message_t state) static int pxa2xx_spi_resume(struct platform_device *pdev) { struct driver_data *drv_data = platform_get_drvdata(pdev); + struct ssp_device *ssp = drv_data->ssp; int status = 0; /* Enable the SSP clock */ - pxa_set_cken(drv_data->master_info->clock_enable, 1); + clk_disable(ssp->clk); /* Start the queue running */ status = start_queue(drv_data); diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 93e9de46977a..1ad12afc6ba0 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -18,7 +18,6 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <linux/autoconf.h> #include <linux/kernel.h> #include <linux/device.h> #include <linux/init.h> @@ -77,39 +76,33 @@ static int spi_uevent(struct device *dev, struct kobj_uevent_env *env) #ifdef CONFIG_PM -/* - * NOTE: the suspend() method for an spi_master controller driver - * should verify that all its child devices are marked as suspended; - * suspend requests delivered through sysfs power/state files don't - * enforce such constraints. - */ static int spi_suspend(struct device *dev, pm_message_t message) { - int value; + int value = 0; struct spi_driver *drv = to_spi_driver(dev->driver); - if (!drv || !drv->suspend) - return 0; - /* suspend will stop irqs and dma; no more i/o */ - value = drv->suspend(to_spi_device(dev), message); - if (value == 0) - dev->power.power_state = message; + if (drv) { + if (drv->suspend) + value = drv->suspend(to_spi_device(dev), message); + else + dev_dbg(dev, "... can't suspend\n"); + } return value; } static int spi_resume(struct device *dev) { - int value; + int value = 0; struct spi_driver *drv = to_spi_driver(dev->driver); - if (!drv || !drv->resume) - return 0; - /* resume may restart the i/o queue */ - value = drv->resume(to_spi_device(dev)); - if (value == 0) - dev->power.power_state = PMSG_ON; + if (drv) { + if (drv->resume) + value = drv->resume(to_spi_device(dev)); + else + dev_dbg(dev, "... can't resume\n"); + } return value; } @@ -485,6 +478,15 @@ void spi_unregister_master(struct spi_master *master) } EXPORT_SYMBOL_GPL(spi_unregister_master); +static int __spi_master_match(struct device *dev, void *data) +{ + struct spi_master *m; + u16 *bus_num = data; + + m = container_of(dev, struct spi_master, dev); + return m->bus_num == *bus_num; +} + /** * spi_busnum_to_master - look up master associated with bus_num * @bus_num: the master's bus number @@ -499,17 +501,12 @@ struct spi_master *spi_busnum_to_master(u16 bus_num) { struct device *dev; struct spi_master *master = NULL; - struct spi_master *m; - - down(&spi_master_class.sem); - list_for_each_entry(dev, &spi_master_class.children, node) { - m = container_of(dev, struct spi_master, dev); - if (m->bus_num == bus_num) { - master = spi_master_get(m); - break; - } - } - up(&spi_master_class.sem); + + dev = class_find_device(&spi_master_class, &bus_num, + __spi_master_match); + if (dev) + master = container_of(dev, struct spi_master, dev); + /* reference got in class_find_device */ return master; } EXPORT_SYMBOL_GPL(spi_busnum_to_master); diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c index 7ef39a6e8c06..d853fceb6bf0 100644 --- a/drivers/spi/spi_bfin5xx.c +++ b/drivers/spi/spi_bfin5xx.c @@ -1,37 +1,11 @@ /* - * File: drivers/spi/bfin5xx_spi.c - * Maintainer: - * Bryan Wu <bryan.wu@analog.com> - * Original Author: - * Luke Yang (Analog Devices Inc.) - * - * Created: March. 10th 2006 - * Description: SPI controller driver for Blackfin BF5xx - * Bugs: Enter bugs at http://blackfin.uclinux.org/ - * - * Modified: - * March 10, 2006 bfin5xx_spi.c Created. (Luke Yang) - * August 7, 2006 added full duplex mode (Axel Weiss & Luke Yang) - * July 17, 2007 add support for BF54x SPI0 controller (Bryan Wu) - * July 30, 2007 add platfrom_resource interface to support multi-port - * SPI controller (Bryan Wu) + * Blackfin On-Chip SPI Driver * * Copyright 2004-2007 Analog Devices Inc. * - * 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, 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. + * Enter bugs at http://blackfin.uclinux.org/ * - * You should have received a copy of the GNU General Public License - * along with this program ; see the file COPYING. - * If not, write to the Free Software Foundation, - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Licensed under the GPL-2 or later. */ #include <linux/init.h> @@ -223,10 +197,9 @@ static void cs_deactive(struct driver_data *drv_data, struct chip_data *chip) #define MAX_SPI_SSEL 7 /* stop controller and re-config current chip*/ -static int restore_state(struct driver_data *drv_data) +static void restore_state(struct driver_data *drv_data) { struct chip_data *chip = drv_data->cur_chip; - int ret = 0; /* Clear status and disable clock */ write_STAT(drv_data, BIT_STAT_CLR); @@ -239,13 +212,6 @@ static int restore_state(struct driver_data *drv_data) bfin_spi_enable(drv_data); cs_active(drv_data, chip); - - if (ret) - dev_dbg(&drv_data->pdev->dev, - ": request chip select number %d failed\n", - chip->chip_select_num); - - return ret; } /* used to kick off transfer in rx mode */ @@ -286,32 +252,30 @@ static void u8_writer(struct driver_data *drv_data) dev_dbg(&drv_data->pdev->dev, "cr8-s is 0x%x\n", read_STAT(drv_data)); - /* poll for SPI completion before start */ - while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) - cpu_relax(); - while (drv_data->tx < drv_data->tx_end) { write_TDBR(drv_data, (*(u8 *) (drv_data->tx))); while (read_STAT(drv_data) & BIT_STAT_TXS) cpu_relax(); ++drv_data->tx; } + + /* poll for SPI completion before return */ + while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) + cpu_relax(); } static void u8_cs_chg_writer(struct driver_data *drv_data) { struct chip_data *chip = drv_data->cur_chip; - /* poll for SPI completion before start */ - while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) - cpu_relax(); - while (drv_data->tx < drv_data->tx_end) { cs_active(drv_data, chip); write_TDBR(drv_data, (*(u8 *) (drv_data->tx))); while (read_STAT(drv_data) & BIT_STAT_TXS) cpu_relax(); + while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) + cpu_relax(); cs_deactive(drv_data, chip); @@ -350,43 +314,28 @@ static void u8_cs_chg_reader(struct driver_data *drv_data) { struct chip_data *chip = drv_data->cur_chip; - /* poll for SPI completion before start */ - while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) - cpu_relax(); - - /* clear TDBR buffer before read(else it will be shifted out) */ - write_TDBR(drv_data, 0xFFFF); + while (drv_data->rx < drv_data->rx_end) { + cs_active(drv_data, chip); + read_RDBR(drv_data); /* kick off */ - cs_active(drv_data, chip); - dummy_read(drv_data); + while (!(read_STAT(drv_data) & BIT_STAT_RXS)) + cpu_relax(); + while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) + cpu_relax(); - while (drv_data->rx < drv_data->rx_end - 1) { + *(u8 *) (drv_data->rx) = read_SHAW(drv_data); cs_deactive(drv_data, chip); - while (!(read_STAT(drv_data) & BIT_STAT_RXS)) - cpu_relax(); - cs_active(drv_data, chip); - *(u8 *) (drv_data->rx) = read_RDBR(drv_data); ++drv_data->rx; } - cs_deactive(drv_data, chip); - - while (!(read_STAT(drv_data) & BIT_STAT_RXS)) - cpu_relax(); - *(u8 *) (drv_data->rx) = read_SHAW(drv_data); - ++drv_data->rx; } static void u8_duplex(struct driver_data *drv_data) { - /* poll for SPI completion before start */ - while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) - cpu_relax(); - /* in duplex mode, clk is triggered by writing of TDBR */ while (drv_data->rx < drv_data->rx_end) { write_TDBR(drv_data, (*(u8 *) (drv_data->tx))); - while (read_STAT(drv_data) & BIT_STAT_TXS) + while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) cpu_relax(); while (!(read_STAT(drv_data) & BIT_STAT_RXS)) cpu_relax(); @@ -400,15 +349,12 @@ static void u8_cs_chg_duplex(struct driver_data *drv_data) { struct chip_data *chip = drv_data->cur_chip; - /* poll for SPI completion before start */ - while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) - cpu_relax(); - while (drv_data->rx < drv_data->rx_end) { cs_active(drv_data, chip); write_TDBR(drv_data, (*(u8 *) (drv_data->tx))); - while (read_STAT(drv_data) & BIT_STAT_TXS) + + while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) cpu_relax(); while (!(read_STAT(drv_data) & BIT_STAT_RXS)) cpu_relax(); @@ -426,32 +372,30 @@ static void u16_writer(struct driver_data *drv_data) dev_dbg(&drv_data->pdev->dev, "cr16 is 0x%x\n", read_STAT(drv_data)); - /* poll for SPI completion before start */ - while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) - cpu_relax(); - while (drv_data->tx < drv_data->tx_end) { write_TDBR(drv_data, (*(u16 *) (drv_data->tx))); while ((read_STAT(drv_data) & BIT_STAT_TXS)) cpu_relax(); drv_data->tx += 2; } + + /* poll for SPI completion before return */ + while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) + cpu_relax(); } static void u16_cs_chg_writer(struct driver_data *drv_data) { struct chip_data *chip = drv_data->cur_chip; - /* poll for SPI completion before start */ - while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) - cpu_relax(); - while (drv_data->tx < drv_data->tx_end) { cs_active(drv_data, chip); write_TDBR(drv_data, (*(u16 *) (drv_data->tx))); while ((read_STAT(drv_data) & BIT_STAT_TXS)) cpu_relax(); + while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) + cpu_relax(); cs_deactive(drv_data, chip); @@ -519,14 +463,10 @@ static void u16_cs_chg_reader(struct driver_data *drv_data) static void u16_duplex(struct driver_data *drv_data) { - /* poll for SPI completion before start */ - while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) - cpu_relax(); - /* in duplex mode, clk is triggered by writing of TDBR */ while (drv_data->tx < drv_data->tx_end) { write_TDBR(drv_data, (*(u16 *) (drv_data->tx))); - while (read_STAT(drv_data) & BIT_STAT_TXS) + while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) cpu_relax(); while (!(read_STAT(drv_data) & BIT_STAT_RXS)) cpu_relax(); @@ -540,15 +480,11 @@ static void u16_cs_chg_duplex(struct driver_data *drv_data) { struct chip_data *chip = drv_data->cur_chip; - /* poll for SPI completion before start */ - while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) - cpu_relax(); - while (drv_data->tx < drv_data->tx_end) { cs_active(drv_data, chip); write_TDBR(drv_data, (*(u16 *) (drv_data->tx))); - while (read_STAT(drv_data) & BIT_STAT_TXS) + while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) cpu_relax(); while (!(read_STAT(drv_data) & BIT_STAT_RXS)) cpu_relax(); @@ -616,7 +552,7 @@ static void giveback(struct driver_data *drv_data) static irqreturn_t dma_irq_handler(int irq, void *dev_id) { - struct driver_data *drv_data = (struct driver_data *)dev_id; + struct driver_data *drv_data = dev_id; struct chip_data *chip = drv_data->cur_chip; struct spi_message *msg = drv_data->cur_msg; @@ -978,10 +914,7 @@ static void pump_messages(struct work_struct *work) /* Setup the SSP using the per chip configuration */ drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi); - if (restore_state(drv_data)) { - spin_unlock_irqrestore(&drv_data->lock, flags); - return; - }; + restore_state(drv_data); list_del_init(&drv_data->cur_msg->queue); @@ -1187,7 +1120,7 @@ static int setup(struct spi_device *spi) if ((chip->chip_select_num > 0) && (chip->chip_select_num <= spi->master->num_chipselect)) peripheral_request(ssel[spi->master->bus_num] - [chip->chip_select_num-1], DRV_NAME); + [chip->chip_select_num-1], spi->modalias); cs_deactive(drv_data, chip); diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c index 2cd8573fb09c..1b0647124933 100644 --- a/drivers/spi/spi_imx.c +++ b/drivers/spi/spi_imx.c @@ -157,7 +157,7 @@ #define SPI_FIFO_BYTE_WIDTH (2) #define SPI_FIFO_OVERFLOW_MARGIN (2) -/* DMA burst lenght for half full/empty request trigger */ +/* DMA burst length for half full/empty request trigger */ #define SPI_DMA_BLR (SPI_FIFO_DEPTH * SPI_FIFO_BYTE_WIDTH / 2) /* Dummy char output to achieve reads. @@ -1686,17 +1686,6 @@ static void spi_imx_shutdown(struct platform_device *pdev) } #ifdef CONFIG_PM -static int suspend_devices(struct device *dev, void *pm_message) -{ - pm_message_t *state = pm_message; - - if (dev->power.power_state.event != state->event) { - dev_warn(dev, "pm state does not match request\n"); - return -1; - } - - return 0; -} static int spi_imx_suspend(struct platform_device *pdev, pm_message_t state) { diff --git a/drivers/spi/spi_mpc83xx.c b/drivers/spi/spi_mpc83xx.c index 4580b9cf625d..04f7cd9fc261 100644 --- a/drivers/spi/spi_mpc83xx.c +++ b/drivers/spi/spi_mpc83xx.c @@ -436,11 +436,7 @@ static int __init mpc83xx_spi_probe(struct platform_device *dev) 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; - - if (mpc83xx_spi->qe_mode) - mpc83xx_spi->spibrg = pdata->sysclk / 2; - else - mpc83xx_spi->spibrg = pdata->sysclk; + mpc83xx_spi->spibrg = pdata->sysclk; mpc83xx_spi->rx_shift = 0; mpc83xx_spi->tx_shift = 0; diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi_s3c24xx.c index 89d6685a5ca4..6e834b8b9d27 100644 --- a/drivers/spi/spi_s3c24xx.c +++ b/drivers/spi/spi_s3c24xx.c @@ -237,10 +237,8 @@ static int __init s3c24xx_spi_probe(struct platform_device *pdev) { struct s3c24xx_spi *hw; struct spi_master *master; - struct spi_board_info *bi; struct resource *res; int err = 0; - int i; master = spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi)); if (master == NULL) { @@ -348,16 +346,6 @@ static int __init s3c24xx_spi_probe(struct platform_device *pdev) goto err_register; } - /* register all the devices associated */ - - bi = &hw->pdata->board_info[0]; - for (i = 0; i < hw->pdata->board_size; i++, bi++) { - dev_info(hw->dev, "registering %s\n", bi->modalias); - - bi->controller_data = hw; - spi_new_device(master, bi); - } - return 0; err_register: diff --git a/drivers/spi/spi_s3c24xx_gpio.c b/drivers/spi/spi_s3c24xx_gpio.c index 109d82c1abc0..82ae7d7eca38 100644 --- a/drivers/spi/spi_s3c24xx_gpio.c +++ b/drivers/spi/spi_s3c24xx_gpio.c @@ -100,7 +100,6 @@ static int s3c2410_spigpio_probe(struct platform_device *dev) struct spi_master *master; struct s3c2410_spigpio *sp; int ret; - int i; master = spi_alloc_master(&dev->dev, sizeof(struct s3c2410_spigpio)); if (master == NULL) { @@ -143,17 +142,6 @@ static int s3c2410_spigpio_probe(struct platform_device *dev) if (ret) goto err_no_bitbang; - /* register the chips to go with the board */ - - for (i = 0; i < sp->info->board_size; i++) { - dev_info(&dev->dev, "registering %p: %s\n", - &sp->info->board_info[i], - sp->info->board_info[i].modalias); - - sp->info->board_info[i].controller_data = sp; - spi_new_device(master, sp->info->board_info + i); - } - return 0; err_no_bitbang: diff --git a/drivers/spi/spi_sh_sci.c b/drivers/spi/spi_sh_sci.c new file mode 100644 index 000000000000..3dbe71b16d60 --- /dev/null +++ b/drivers/spi/spi_sh_sci.c @@ -0,0 +1,205 @@ +/* + * SH SCI SPI interface + * + * Copyright (c) 2008 Magnus Damm + * + * Based on S3C24XX GPIO based SPI driver, which is: + * Copyright (c) 2006 Ben Dooks + * Copyright (c) 2006 Simtec Electronics + * + * 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/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/spinlock.h> +#include <linux/workqueue.h> +#include <linux/platform_device.h> + +#include <linux/spi/spi.h> +#include <linux/spi/spi_bitbang.h> + +#include <asm/spi.h> +#include <asm/io.h> + +struct sh_sci_spi { + struct spi_bitbang bitbang; + + void __iomem *membase; + unsigned char val; + struct sh_spi_info *info; + struct platform_device *dev; +}; + +#define SCSPTR(sp) (sp->membase + 0x1c) +#define PIN_SCK (1 << 2) +#define PIN_TXD (1 << 0) +#define PIN_RXD PIN_TXD +#define PIN_INIT ((1 << 1) | (1 << 3) | PIN_SCK | PIN_TXD) + +static inline void setbits(struct sh_sci_spi *sp, int bits, int on) +{ + /* + * We are the only user of SCSPTR so no locking is required. + * Reading bit 2 and 0 in SCSPTR gives pin state as input. + * Writing the same bits sets the output value. + * This makes regular read-modify-write difficult so we + * use sp->val to keep track of the latest register value. + */ + + if (on) + sp->val |= bits; + else + sp->val &= ~bits; + + iowrite8(sp->val, SCSPTR(sp)); +} + +static inline void setsck(struct spi_device *dev, int on) +{ + setbits(spi_master_get_devdata(dev->master), PIN_SCK, on); +} + +static inline void setmosi(struct spi_device *dev, int on) +{ + setbits(spi_master_get_devdata(dev->master), PIN_TXD, on); +} + +static inline u32 getmiso(struct spi_device *dev) +{ + struct sh_sci_spi *sp = spi_master_get_devdata(dev->master); + + return (ioread8(SCSPTR(sp)) & PIN_RXD) ? 1 : 0; +} + +#define spidelay(x) ndelay(x) + +#define EXPAND_BITBANG_TXRX +#include <linux/spi/spi_bitbang.h> + +static u32 sh_sci_spi_txrx_mode0(struct spi_device *spi, + unsigned nsecs, u32 word, u8 bits) +{ + return bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits); +} + +static u32 sh_sci_spi_txrx_mode1(struct spi_device *spi, + unsigned nsecs, u32 word, u8 bits) +{ + return bitbang_txrx_be_cpha1(spi, nsecs, 0, word, bits); +} + +static u32 sh_sci_spi_txrx_mode2(struct spi_device *spi, + unsigned nsecs, u32 word, u8 bits) +{ + return bitbang_txrx_be_cpha0(spi, nsecs, 1, word, bits); +} + +static u32 sh_sci_spi_txrx_mode3(struct spi_device *spi, + unsigned nsecs, u32 word, u8 bits) +{ + return bitbang_txrx_be_cpha1(spi, nsecs, 1, word, bits); +} + +static void sh_sci_spi_chipselect(struct spi_device *dev, int value) +{ + struct sh_sci_spi *sp = spi_master_get_devdata(dev->master); + + if (sp->info && sp->info->chip_select) + (sp->info->chip_select)(sp->info, dev->chip_select, value); +} + +static int sh_sci_spi_probe(struct platform_device *dev) +{ + struct resource *r; + struct spi_master *master; + struct sh_sci_spi *sp; + int ret; + + master = spi_alloc_master(&dev->dev, sizeof(struct sh_sci_spi)); + if (master == NULL) { + dev_err(&dev->dev, "failed to allocate spi master\n"); + ret = -ENOMEM; + goto err0; + } + + sp = spi_master_get_devdata(master); + + platform_set_drvdata(dev, sp); + sp->info = dev->dev.platform_data; + + /* setup spi bitbang adaptor */ + sp->bitbang.master = spi_master_get(master); + sp->bitbang.master->bus_num = sp->info->bus_num; + sp->bitbang.master->num_chipselect = sp->info->num_chipselect; + sp->bitbang.chipselect = sh_sci_spi_chipselect; + + sp->bitbang.txrx_word[SPI_MODE_0] = sh_sci_spi_txrx_mode0; + sp->bitbang.txrx_word[SPI_MODE_1] = sh_sci_spi_txrx_mode1; + sp->bitbang.txrx_word[SPI_MODE_2] = sh_sci_spi_txrx_mode2; + sp->bitbang.txrx_word[SPI_MODE_3] = sh_sci_spi_txrx_mode3; + + r = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (r == NULL) { + ret = -ENOENT; + goto err1; + } + sp->membase = ioremap(r->start, r->end - r->start + 1); + if (!sp->membase) { + ret = -ENXIO; + goto err1; + } + sp->val = ioread8(SCSPTR(sp)); + setbits(sp, PIN_INIT, 1); + + ret = spi_bitbang_start(&sp->bitbang); + if (!ret) + return 0; + + setbits(sp, PIN_INIT, 0); + iounmap(sp->membase); + err1: + spi_master_put(sp->bitbang.master); + err0: + return ret; +} + +static int sh_sci_spi_remove(struct platform_device *dev) +{ + struct sh_sci_spi *sp = platform_get_drvdata(dev); + + iounmap(sp->membase); + setbits(sp, PIN_INIT, 0); + spi_bitbang_stop(&sp->bitbang); + spi_master_put(sp->bitbang.master); + return 0; +} + +static struct platform_driver sh_sci_spi_drv = { + .probe = sh_sci_spi_probe, + .remove = sh_sci_spi_remove, + .driver = { + .name = "spi_sh_sci", + .owner = THIS_MODULE, + }, +}; + +static int __init sh_sci_spi_init(void) +{ + return platform_driver_register(&sh_sci_spi_drv); +} +module_init(sh_sci_spi_init); + +static void __exit sh_sci_spi_exit(void) +{ + platform_driver_unregister(&sh_sci_spi_drv); +} +module_exit(sh_sci_spi_exit); + +MODULE_DESCRIPTION("SH SCI SPI Driver"); +MODULE_AUTHOR("Magnus Damm <damm@opensource.se>"); +MODULE_LICENSE("GPL"); |