Commit bf931a01 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'next-spi' of git://git.secretlab.ca/git/linux-2.6

* 'next-spi' of git://git.secretlab.ca/git/linux-2.6:
  spi: spi_txx9.c: use resource_size()
  spi: spi_sh_sci.c: use resource_size()
  spi: spi_mpc8xxx.c: use resource_size()
  spi: spi_bfin5xx.c: use resource_size()
  spi: atmel_spi.c: use resource_size()
  spi: Add s3c64xx SPI Controller driver
  atmel_spi: fix dma addr calculation for len > BUFFER_SIZE
  spi_s3c24xx: add FIQ pseudo-DMA support
  spi: controller driver for Designware SPI core
  spidev: add proper section markers
  spidev: use DECLARE_BITMAP instead of declaring the array
parents 4e46aa08 d53342bf
...@@ -18,6 +18,8 @@ struct s3c2410_spi_info { ...@@ -18,6 +18,8 @@ struct s3c2410_spi_info {
unsigned int num_cs; /* total chipselects */ unsigned int num_cs; /* total chipselects */
int bus_num; /* bus number to use. */ int bus_num; /* bus number to use. */
unsigned int use_fiq:1; /* use fiq */
void (*gpio_setup)(struct s3c2410_spi_info *spi, int enable); void (*gpio_setup)(struct s3c2410_spi_info *spi, int enable);
void (*set_cs)(struct s3c2410_spi_info *spi, int cs, int pol); void (*set_cs)(struct s3c2410_spi_info *spi, int cs, int pol);
}; };
......
...@@ -216,6 +216,17 @@ config SPI_S3C24XX ...@@ -216,6 +216,17 @@ config SPI_S3C24XX
help help
SPI driver for Samsung S3C24XX series ARM SoCs SPI driver for Samsung S3C24XX series ARM SoCs
config SPI_S3C24XX_FIQ
bool "S3C24XX driver with FIQ pseudo-DMA"
depends on SPI_S3C24XX
select FIQ
help
Enable FIQ support for the S3C24XX SPI driver to provide pseudo
DMA by using the fast-interrupt request framework, This allows
the driver to get DMA-like performance when there are either
no free DMA channels, or when doing transfers that required both
TX and RX data paths.
config SPI_S3C24XX_GPIO config SPI_S3C24XX_GPIO
tristate "Samsung S3C24XX series SPI by GPIO" tristate "Samsung S3C24XX series SPI by GPIO"
depends on ARCH_S3C2410 && EXPERIMENTAL depends on ARCH_S3C2410 && EXPERIMENTAL
...@@ -226,6 +237,13 @@ config SPI_S3C24XX_GPIO ...@@ -226,6 +237,13 @@ config SPI_S3C24XX_GPIO
the inbuilt hardware cannot provide the transfer mode, or the inbuilt hardware cannot provide the transfer mode, or
where the board is using non hardware connected pins. where the board is using non hardware connected pins.
config SPI_S3C64XX
tristate "Samsung S3C64XX series type SPI"
depends on ARCH_S3C64XX && EXPERIMENTAL
select S3C64XX_DMA
help
SPI driver for Samsung S3C64XX and newer SoCs.
config SPI_SH_MSIOF config SPI_SH_MSIOF
tristate "SuperH MSIOF SPI controller" tristate "SuperH MSIOF SPI controller"
depends on SUPERH && HAVE_CLK depends on SUPERH && HAVE_CLK
...@@ -289,6 +307,16 @@ config SPI_NUC900 ...@@ -289,6 +307,16 @@ config SPI_NUC900
# Add new SPI master controllers in alphabetical order above this line # Add new SPI master controllers in alphabetical order above this line
# #
config SPI_DESIGNWARE
bool "DesignWare SPI controller core support"
depends on SPI_MASTER
help
general driver for SPI controller core from DesignWare
config SPI_DW_PCI
tristate "PCI interface driver for DW SPI core"
depends on SPI_DESIGNWARE && PCI
# #
# There are lots of SPI device types, with sensors and memory # There are lots of SPI device types, with sensors and memory
# being probably the most widely used ones. # being probably the most widely used ones.
......
...@@ -16,6 +16,8 @@ obj-$(CONFIG_SPI_BFIN) += spi_bfin5xx.o ...@@ -16,6 +16,8 @@ obj-$(CONFIG_SPI_BFIN) += spi_bfin5xx.o
obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o
obj-$(CONFIG_SPI_AU1550) += au1550_spi.o obj-$(CONFIG_SPI_AU1550) += au1550_spi.o
obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o
obj-$(CONFIG_SPI_DESIGNWARE) += dw_spi.o
obj-$(CONFIG_SPI_DW_PCI) += dw_spi_pci.o
obj-$(CONFIG_SPI_GPIO) += spi_gpio.o obj-$(CONFIG_SPI_GPIO) += spi_gpio.o
obj-$(CONFIG_SPI_IMX) += spi_imx.o obj-$(CONFIG_SPI_IMX) += spi_imx.o
obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o
...@@ -30,7 +32,8 @@ obj-$(CONFIG_SPI_MPC52xx) += mpc52xx_spi.o ...@@ -30,7 +32,8 @@ obj-$(CONFIG_SPI_MPC52xx) += mpc52xx_spi.o
obj-$(CONFIG_SPI_MPC8xxx) += spi_mpc8xxx.o obj-$(CONFIG_SPI_MPC8xxx) += spi_mpc8xxx.o
obj-$(CONFIG_SPI_PPC4xx) += spi_ppc4xx.o obj-$(CONFIG_SPI_PPC4xx) += spi_ppc4xx.o
obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o
obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx_hw.o
obj-$(CONFIG_SPI_S3C64XX) += spi_s3c64xx.o
obj-$(CONFIG_SPI_TXX9) += spi_txx9.o obj-$(CONFIG_SPI_TXX9) += spi_txx9.o
obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o
obj-$(CONFIG_SPI_XILINX_OF) += xilinx_spi_of.o obj-$(CONFIG_SPI_XILINX_OF) += xilinx_spi_of.o
...@@ -39,6 +42,11 @@ obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o ...@@ -39,6 +42,11 @@ obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o
obj-$(CONFIG_SPI_SH_MSIOF) += spi_sh_msiof.o obj-$(CONFIG_SPI_SH_MSIOF) += spi_sh_msiof.o
obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o
obj-$(CONFIG_SPI_NUC900) += spi_nuc900.o obj-$(CONFIG_SPI_NUC900) += spi_nuc900.o
# special build for s3c24xx spi driver with fiq support
spi_s3c24xx_hw-y := spi_s3c24xx.o
spi_s3c24xx_hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi_s3c24xx_fiq.o
# ... add above this line ... # ... add above this line ...
# SPI protocol drivers (device/link on bus) # SPI protocol drivers (device/link on bus)
......
...@@ -189,14 +189,14 @@ static void atmel_spi_next_xfer_data(struct spi_master *master, ...@@ -189,14 +189,14 @@ static void atmel_spi_next_xfer_data(struct spi_master *master,
/* use scratch buffer only when rx or tx data is unspecified */ /* use scratch buffer only when rx or tx data is unspecified */
if (xfer->rx_buf) if (xfer->rx_buf)
*rx_dma = xfer->rx_dma + xfer->len - len; *rx_dma = xfer->rx_dma + xfer->len - *plen;
else { else {
*rx_dma = as->buffer_dma; *rx_dma = as->buffer_dma;
if (len > BUFFER_SIZE) if (len > BUFFER_SIZE)
len = BUFFER_SIZE; len = BUFFER_SIZE;
} }
if (xfer->tx_buf) if (xfer->tx_buf)
*tx_dma = xfer->tx_dma + xfer->len - len; *tx_dma = xfer->tx_dma + xfer->len - *plen;
else { else {
*tx_dma = as->buffer_dma; *tx_dma = as->buffer_dma;
if (len > BUFFER_SIZE) if (len > BUFFER_SIZE)
...@@ -788,7 +788,7 @@ static int __init atmel_spi_probe(struct platform_device *pdev) ...@@ -788,7 +788,7 @@ static int __init atmel_spi_probe(struct platform_device *pdev)
spin_lock_init(&as->lock); spin_lock_init(&as->lock);
INIT_LIST_HEAD(&as->queue); INIT_LIST_HEAD(&as->queue);
as->pdev = pdev; as->pdev = pdev;
as->regs = ioremap(regs->start, (regs->end - regs->start) + 1); as->regs = ioremap(regs->start, resource_size(regs));
if (!as->regs) if (!as->regs)
goto out_free_buffer; goto out_free_buffer;
as->irq = irq; as->irq = irq;
......
This diff is collapsed.
/*
* mrst_spi_pci.c - PCI interface driver for DW SPI Core
*
* Copyright (c) 2009, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/spi/dw_spi.h>
#include <linux/spi/spi.h>
#define DRIVER_NAME "dw_spi_pci"
struct dw_spi_pci {
struct pci_dev *pdev;
struct dw_spi dws;
};
static int __devinit spi_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct dw_spi_pci *dwpci;
struct dw_spi *dws;
int pci_bar = 0;
int ret;
printk(KERN_INFO "DW: found PCI SPI controller(ID: %04x:%04x)\n",
pdev->vendor, pdev->device);
ret = pci_enable_device(pdev);
if (ret)
return ret;
dwpci = kzalloc(sizeof(struct dw_spi_pci), GFP_KERNEL);
if (!dwpci) {
ret = -ENOMEM;
goto err_disable;
}
dwpci->pdev = pdev;
dws = &dwpci->dws;
/* Get basic io resource and map it */
dws->paddr = pci_resource_start(pdev, pci_bar);
dws->iolen = pci_resource_len(pdev, pci_bar);
ret = pci_request_region(pdev, pci_bar, dev_name(&pdev->dev));
if (ret)
goto err_kfree;
dws->regs = ioremap_nocache((unsigned long)dws->paddr,
pci_resource_len(pdev, pci_bar));
if (!dws->regs) {
ret = -ENOMEM;
goto err_release_reg;
}
dws->parent_dev = &pdev->dev;
dws->bus_num = 0;
dws->num_cs = 4;
dws->max_freq = 25000000; /* for Moorestwon */
dws->irq = pdev->irq;
ret = dw_spi_add_host(dws);
if (ret)
goto err_unmap;
/* PCI hook and SPI hook use the same drv data */
pci_set_drvdata(pdev, dwpci);
return 0;
err_unmap:
iounmap(dws->regs);
err_release_reg:
pci_release_region(pdev, pci_bar);
err_kfree:
kfree(dwpci);
err_disable:
pci_disable_device(pdev);
return ret;
}
static void __devexit spi_pci_remove(struct pci_dev *pdev)
{
struct dw_spi_pci *dwpci = pci_get_drvdata(pdev);
pci_set_drvdata(pdev, NULL);
iounmap(dwpci->dws.regs);
pci_release_region(pdev, 0);
kfree(dwpci);
pci_disable_device(pdev);
}
#ifdef CONFIG_PM
static int spi_suspend(struct pci_dev *pdev, pm_message_t state)
{
struct dw_spi_pci *dwpci = pci_get_drvdata(pdev);
int ret;
ret = dw_spi_suspend_host(&dwpci->dws);
if (ret)
return ret;
pci_save_state(pdev);
pci_disable_device(pdev);
pci_set_power_state(pdev, pci_choose_state(pdev, state));
return ret;
}
static int spi_resume(struct pci_dev *pdev)
{
struct dw_spi_pci *dwpci = pci_get_drvdata(pdev);
int ret;
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
ret = pci_enable_device(pdev);
if (ret)
return ret;
return dw_spi_resume_host(&dwpci->dws);
}
#else
#define spi_suspend NULL
#define spi_resume NULL
#endif
static const struct pci_device_id pci_ids[] __devinitdata = {
/* Intel Moorestown platform SPI controller 0 */
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0800) },
{},
};
static struct pci_driver dw_spi_driver = {
.name = DRIVER_NAME,
.id_table = pci_ids,
.probe = spi_pci_probe,
.remove = __devexit_p(spi_pci_remove),
.suspend = spi_suspend,
.resume = spi_resume,
};
static int __init mrst_spi_init(void)
{
return pci_register_driver(&dw_spi_driver);
}
static void __exit mrst_spi_exit(void)
{
pci_unregister_driver(&dw_spi_driver);
}
module_init(mrst_spi_init);
module_exit(mrst_spi_exit);
MODULE_AUTHOR("Feng Tang <feng.tang@intel.com>");
MODULE_DESCRIPTION("PCI interface driver for DW SPI Core");
MODULE_LICENSE("GPL v2");
...@@ -1294,7 +1294,7 @@ static int __init bfin_spi_probe(struct platform_device *pdev) ...@@ -1294,7 +1294,7 @@ static int __init bfin_spi_probe(struct platform_device *pdev)
goto out_error_get_res; goto out_error_get_res;
} }
drv_data->regs_base = ioremap(res->start, (res->end - res->start + 1)); drv_data->regs_base = ioremap(res->start, resource_size(res));
if (drv_data->regs_base == NULL) { if (drv_data->regs_base == NULL) {
dev_err(dev, "Cannot map IO\n"); dev_err(dev, "Cannot map IO\n");
status = -ENXIO; status = -ENXIO;
......
...@@ -1013,7 +1013,7 @@ mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq) ...@@ -1013,7 +1013,7 @@ mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
init_completion(&mpc8xxx_spi->done); init_completion(&mpc8xxx_spi->done);
mpc8xxx_spi->base = ioremap(mem->start, mem->end - mem->start + 1); mpc8xxx_spi->base = ioremap(mem->start, resource_size(mem));
if (mpc8xxx_spi->base == NULL) { if (mpc8xxx_spi->base == NULL) {
ret = -ENOMEM; ret = -ENOMEM;
goto err_ioremap; goto err_ioremap;
......
/* linux/drivers/spi/spi_s3c24xx.c /* linux/drivers/spi/spi_s3c24xx.c
* *
* Copyright (c) 2006 Ben Dooks * Copyright (c) 2006 Ben Dooks
* Copyright (c) 2006 Simtec Electronics * Copyright 2006-2009 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk> * Ben Dooks <ben@simtec.co.uk>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -28,6 +28,11 @@ ...@@ -28,6 +28,11 @@
#include <plat/regs-spi.h> #include <plat/regs-spi.h>
#include <mach/spi.h> #include <mach/spi.h>
#include <plat/fiq.h>
#include <asm/fiq.h>
#include "spi_s3c24xx_fiq.h"
/** /**
* s3c24xx_spi_devstate - per device data * s3c24xx_spi_devstate - per device data
* @hz: Last frequency calculated for @sppre field. * @hz: Last frequency calculated for @sppre field.
...@@ -42,6 +47,13 @@ struct s3c24xx_spi_devstate { ...@@ -42,6 +47,13 @@ struct s3c24xx_spi_devstate {
u8 sppre; u8 sppre;
}; };
enum spi_fiq_mode {
FIQ_MODE_NONE = 0,
FIQ_MODE_TX = 1,
FIQ_MODE_RX = 2,
FIQ_MODE_TXRX = 3,
};
struct s3c24xx_spi { struct s3c24xx_spi {
/* bitbang has to be first */ /* bitbang has to be first */
struct spi_bitbang bitbang; struct spi_bitbang bitbang;
...@@ -52,6 +64,11 @@ struct s3c24xx_spi { ...@@ -52,6 +64,11 @@ struct s3c24xx_spi {
int len; int len;
int count; int count;
struct fiq_handler fiq_handler;
enum spi_fiq_mode fiq_mode;
unsigned char fiq_inuse;
unsigned char fiq_claimed;
void (*set_cs)(struct s3c2410_spi_info *spi, void (*set_cs)(struct s3c2410_spi_info *spi,
int cs, int pol); int cs, int pol);
...@@ -67,6 +84,7 @@ struct s3c24xx_spi { ...@@ -67,6 +84,7 @@ struct s3c24xx_spi {
struct s3c2410_spi_info *pdata; struct s3c2410_spi_info *pdata;
}; };
#define SPCON_DEFAULT (S3C2410_SPCON_MSTR | S3C2410_SPCON_SMOD_INT) #define SPCON_DEFAULT (S3C2410_SPCON_MSTR | S3C2410_SPCON_SMOD_INT)
#define SPPIN_DEFAULT (S3C2410_SPPIN_KEEP) #define SPPIN_DEFAULT (S3C2410_SPPIN_KEEP)
...@@ -127,7 +145,7 @@ static int s3c24xx_spi_update_state(struct spi_device *spi, ...@@ -127,7 +145,7 @@ static int s3c24xx_spi_update_state(struct spi_device *spi,
} }
if (spi->mode != cs->mode) { if (spi->mode != cs->mode) {
u8 spcon = SPCON_DEFAULT; u8 spcon = SPCON_DEFAULT | S3C2410_SPCON_ENSCK;
if (spi->mode & SPI_CPHA) if (spi->mode & SPI_CPHA)
spcon |= S3C2410_SPCON_CPHA_FMTB; spcon |= S3C2410_SPCON_CPHA_FMTB;
...@@ -214,13 +232,196 @@ static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count) ...@@ -214,13 +232,196 @@ static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count)
return hw->tx ? hw->tx[count] : 0; return hw->tx ? hw->tx[count] : 0;
} }
#ifdef CONFIG_SPI_S3C24XX_FIQ
/* Support for FIQ based pseudo-DMA to improve the transfer speed.
*
* This code uses the assembly helper in spi_s3c24xx_spi.S which is
* used by the FIQ core to move data between main memory and the peripheral
* block. Since this is code running on the processor, there is no problem
* with cache coherency of the buffers, so we can use any buffer we like.
*/
/**
* struct spi_fiq_code - FIQ code and header
* @length: The length of the code fragment, excluding this header.
* @ack_offset: The offset from @data to the word to place the IRQ ACK bit at.
* @data: The code itself to install as a FIQ handler.
*/
struct spi_fiq_code {
u32 length;
u32 ack_offset;
u8 data[0];
};
extern struct spi_fiq_code s3c24xx_spi_fiq_txrx;
extern struct spi_fiq_code s3c24xx_spi_fiq_tx;
extern struct spi_fiq_code s3c24xx_spi_fiq_rx;
/**
* ack_bit - turn IRQ into IRQ acknowledgement bit
* @irq: The interrupt number
*
* Returns the bit to write to the interrupt acknowledge register.
*/
static inline u32 ack_bit(unsigned int irq)
{
return 1 << (irq - IRQ_EINT0);
}
/**
* s3c24xx_spi_tryfiq - attempt to claim and setup FIQ for transfer
* @hw: The hardware state.
*
* Claim the FIQ handler (only one can be active at any one time) and
* then setup the correct transfer code for this transfer.
*
* This call updates all the necessary state information if sucessful,
* so the caller does not need to do anything more than start the transfer
* as normal, since the IRQ will have been re-routed to the FIQ handler.
*/
void s3c24xx_spi_tryfiq(struct s3c24xx_spi *hw)
{
struct pt_regs regs;
enum spi_fiq_mode mode;
struct spi_fiq_code *code;
int ret;
if (!hw->fiq_claimed) {
/* try and claim fiq if we haven't got it, and if not
* then return and simply use another transfer method */
ret = claim_fiq(&hw->fiq_handler);
if (ret)
return;
}
if (hw->tx && !hw->rx)
mode = FIQ_MODE_TX;
else if (hw->rx && !hw->tx)
mode = FIQ_MODE_RX;
else
mode = FIQ_MODE_TXRX;
regs.uregs[fiq_rspi] = (long)hw->regs;
regs.uregs[fiq_rrx] = (long)hw->rx;
regs.uregs[fiq_rtx] = (long)hw->tx + 1;
regs.uregs[fiq_rcount] = hw->len - 1;
regs.uregs[fiq_rirq] = (long)S3C24XX_VA_IRQ;
set_fiq_regs(&regs);
if (hw->fiq_mode != mode) {
u32 *ack_ptr;
hw->fiq_mode = mode;
switch (mode) {
case FIQ_MODE_TX:
code = &s3c24xx_spi_fiq_tx;
break;
case FIQ_MODE_RX:
code = &s3c24xx_spi_fiq_rx;
break;
case FIQ_MODE_TXRX:
code = &s3c24xx_spi_fiq_txrx;
break;
default:
code = NULL;
}
BUG_ON(!code);
ack_ptr = (u32 *)&code->data[code->ack_offset];
*ack_ptr = ack_bit(hw->irq);
set_fiq_handler(&code->data, code->length);
}
s3c24xx_set_fiq(hw->irq, true);
hw->fiq_mode = mode;
hw->fiq_inuse = 1;
}
/**
* s3c24xx_spi_fiqop - FIQ core code callback
* @pw: Data registered with the handler
* @release: Whether this is a release or a return.
*
* Called by the FIQ code when another module wants to use the FIQ, so
* return whether we are currently using this or not and then update our
* internal state.
*/
static int s3c24xx_spi_fiqop(void *pw, int release)
{
struct s3c24xx_spi *hw = pw;
int ret = 0;
if (release) {
if (hw->fiq_inuse)
ret = -EBUSY;
/* note, we do not need to unroute the FIQ, as the FIQ
* vector code de-routes it to signal the end of transfer */
hw->fiq_mode = FIQ_MODE_NONE;
hw->fiq_claimed = 0;
} else {
hw->fiq_claimed = 1;
}
return ret;
}
/**
* s3c24xx_spi_initfiq - setup the information for the FIQ core
* @hw: The hardware state.
*
* Setup the fiq_handler block to pass to the FIQ core.
*/
static inline void s3c24xx_spi_initfiq(struct s3c24xx_spi *hw)
{
hw->fiq_handler.dev_id = hw;
hw->fiq_handler.name = dev_name(hw->dev);
hw->fiq_handler.fiq_op = s3c24xx_spi_fiqop;
}
/**
* s3c24xx_spi_usefiq - return if we should be using FIQ.
* @hw: The hardware state.
*
* Return true if the platform data specifies whether this channel is
* allowed to use the FIQ.
*/
static inline bool s3c24xx_spi_usefiq(struct s3c24xx_spi *hw)
{
return hw->pdata->use_fiq;
}
/**
* s3c24xx_spi_usingfiq - return if channel is using FIQ
* @spi: The hardware state.
*
* Return whether the channel is currently using the FIQ (separate from
* whether the FIQ is claimed).
*/
static inline bool s3c24xx_spi_usingfiq(struct s3c24xx_spi *spi)
{
return spi->fiq_inuse;
}
#else
static inline void s3c24xx_spi_initfiq(struct s3c24xx_spi *s) { }
static inline void s3c24xx_spi_tryfiq(struct s3c24xx_spi *s) { }
static inline bool s3c24xx_spi_usefiq(struct s3c24xx_spi *s) { return false; }
static inline bool s3c24xx_spi_usingfiq(struct s3c24xx_spi *s) { return false; }
#endif /* CONFIG_SPI_S3C24XX_FIQ */
static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t) static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
{ {
struct s3c24xx_spi *hw = to_hw(spi); struct s3c24xx_spi *hw = to_hw(spi);
dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
t->tx_buf, t->rx_buf, t->len);
hw->tx = t->tx_buf; hw->tx = t->tx_buf;
hw->rx = t->rx_buf; hw->rx = t->rx_buf;
hw->len = t->len; hw->len = t->len;
...@@ -228,11 +429,14 @@ static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t) ...@@ -228,11 +429,14 @@ static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
init_completion(&hw->done); init_completion(&hw->done);
hw->fiq_inuse = 0;
if (s3c24xx_spi_usefiq(hw) && t->len >= 3)
s3c24xx_spi_tryfiq(hw);
/* send the first byte */ /* send the first byte */
writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT); writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT);
wait_for_completion(&hw->done); wait_for_completion(&hw->done);
return hw->count; return hw->count;
} }
...@@ -254,17 +458,27 @@ static irqreturn_t s3c24xx_spi_irq(int irq, void *dev) ...@@ -254,17 +458,27 @@ static irqreturn_t s3c24xx_spi_irq(int irq, void *dev)
goto irq_done; goto irq_done;
} }
hw->count++; if (!s3c24xx_spi_usingfiq(hw)) {
hw->count++;
if (hw->rx) if (hw->rx)
hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT); hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT);
count++; count++;
if (count < hw->len)
writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);
else
complete(&hw->done);
} else {
hw->count = hw->len;
hw->fiq_inuse = 0;
if (hw->rx)
hw->rx[hw->len-1] = readb(hw->regs + S3C2410_SPRDAT);
if (count < hw->len)
writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);
else
complete(&hw->done); complete(&hw->done);
}
irq_done: irq_done:
return IRQ_HANDLED; return IRQ_HANDLED;
...@@ -322,6 +536,10 @@ static int __init s3c24xx_spi_probe(struct platform_device *pdev) ...@@ -322,6 +536,10 @@ static int __init s3c24xx_spi_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, hw); platform_set_drvdata(pdev, hw);
init_completion(&hw->done); init_completion(&hw->done);
/* initialise fiq handler */
s3c24xx_spi_initfiq(hw);
/* setup the master state. */ /* setup the master state. */
/* the spi->mode bits understood by this driver: */ /* the spi->mode bits understood by this driver: */
......
/* linux/drivers/spi/spi_s3c24xx_fiq.S
*
* Copyright 2009 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
*
* S3C24XX SPI - FIQ pseudo-DMA transfer code
*
* 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/linkage.h>
#include <asm/assembler.h>
#include <mach/map.h>
#include <mach/regs-irq.h>
#include <plat/regs-spi.h>
#include "spi_s3c24xx_fiq.h"
.text
@ entry to these routines is as follows, with the register names
@ defined in fiq.h so that they can be shared with the C files which
@ setup the calling registers.
@
@ fiq_rirq The base of the IRQ registers to find S3C2410_SRCPND
@ fiq_rtmp Temporary register to hold tx/rx data
@ fiq_rspi The base of the SPI register block
@ fiq_rtx The tx buffer pointer
@ fiq_rrx The rx buffer pointer
@ fiq_rcount The number of bytes to move
@ each entry starts with a word entry of how long it is
@ and an offset to the irq acknowledgment word
ENTRY(s3c24xx_spi_fiq_rx)
s3c24xx_spi_fix_rx:
.word fiq_rx_end - fiq_rx_start
.word fiq_rx_irq_ack - fiq_rx_start
fiq_rx_start:
ldr fiq_rtmp, fiq_rx_irq_ack
str fiq_rtmp, [ fiq_rirq, # S3C2410_SRCPND - S3C24XX_VA_IRQ ]
ldrb fiq_rtmp, [ fiq_rspi, # S3C2410_SPRDAT ]
strb fiq_rtmp, [ fiq_rrx ], #1
mov fiq_rtmp, #0xff
strb fiq_rtmp, [ fiq_rspi, # S3C2410_SPTDAT ]
subs fiq_rcount, fiq_rcount, #1
subnes pc, lr, #4 @@ return, still have work to do
@@ set IRQ controller so that next op will trigger IRQ
mov fiq_rtmp, #0
str fiq_rtmp, [ fiq_rirq, # S3C2410_INTMOD - S3C24XX_VA_IRQ ]
subs pc, lr, #4
fiq_rx_irq_ack:
.word 0
fiq_rx_end:
ENTRY(s3c24xx_spi_fiq_txrx)
s3c24xx_spi_fiq_txrx:
.word fiq_txrx_end - fiq_txrx_start
.word fiq_txrx_irq_ack - fiq_txrx_start
fiq_txrx_start:
ldrb fiq_rtmp, [ fiq_rspi, # S3C2410_SPRDAT ]
strb fiq_rtmp, [ fiq_rrx ], #1
ldr fiq_rtmp, fiq_txrx_irq_ack
str fiq_rtmp, [ fiq_rirq, # S3C2410_SRCPND - S3C24XX_VA_IRQ ]
ldrb fiq_rtmp, [ fiq_rtx ], #1
strb fiq_rtmp, [ fiq_rspi, # S3C2410_SPTDAT ]
subs fiq_rcount, fiq_rcount, #1
subnes pc, lr, #4 @@ return, still have work to do
mov fiq_rtmp, #0
str fiq_rtmp, [ fiq_rirq, # S3C2410_INTMOD - S3C24XX_VA_IRQ ]
subs pc, lr, #4
fiq_txrx_irq_ack:
.word 0
fiq_txrx_end:
ENTRY(s3c24xx_spi_fiq_tx)
s3c24xx_spi_fix_tx:
.word fiq_tx_end - fiq_tx_start
.word fiq_tx_irq_ack - fiq_tx_start
fiq_tx_start:
ldrb fiq_rtmp, [ fiq_rspi, # S3C2410_SPRDAT ]
ldr fiq_rtmp, fiq_tx_irq_ack
str fiq_rtmp, [ fiq_rirq, # S3C2410_SRCPND - S3C24XX_VA_IRQ ]
ldrb fiq_rtmp, [ fiq_rtx ], #1
strb fiq_rtmp, [ fiq_rspi, # S3C2410_SPTDAT ]
subs fiq_rcount, fiq_rcount, #1
subnes pc, lr, #4 @@ return, still have work to do
mov fiq_rtmp, #0
str fiq_rtmp, [ fiq_rirq, # S3C2410_INTMOD - S3C24XX_VA_IRQ ]
subs pc, lr, #4
fiq_tx_irq_ack:
.word 0
fiq_tx_end:
.end
/* linux/drivers/spi/spi_s3c24xx_fiq.h
*
* Copyright 2009 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
*
* S3C24XX SPI - FIQ pseudo-DMA transfer support
*
* 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.
*/
/* We have R8 through R13 to play with */
#ifdef __ASSEMBLY__
#define __REG_NR(x) r##x
#else
#define __REG_NR(x) (x)
#endif
#define fiq_rspi __REG_NR(8)
#define fiq_rtmp __REG_NR(9)
#define fiq_rrx __REG_NR(10)
#define fiq_rtx __REG_NR(11)
#define fiq_rcount __REG_NR(12)
#define fiq_rirq __REG_NR(13)
This diff is collapsed.
...@@ -148,7 +148,7 @@ static int sh_sci_spi_probe(struct platform_device *dev) ...@@ -148,7 +148,7 @@ static int sh_sci_spi_probe(struct platform_device *dev)
ret = -ENOENT; ret = -ENOENT;
goto err1; goto err1;
} }
sp->membase = ioremap(r->start, r->end - r->start + 1); sp->membase = ioremap(r->start, resource_size(r));
if (!sp->membase) { if (!sp->membase) {
ret = -ENXIO; ret = -ENXIO;
goto err1; goto err1;
......
...@@ -375,12 +375,10 @@ static int __init txx9spi_probe(struct platform_device *dev) ...@@ -375,12 +375,10 @@ static int __init txx9spi_probe(struct platform_device *dev)
res = platform_get_resource(dev, IORESOURCE_MEM, 0); res = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (!res) if (!res)
goto exit_busy; goto exit_busy;
if (!devm_request_mem_region(&dev->dev, if (!devm_request_mem_region(&dev->dev, res->start, resource_size(res),
res->start, res->end - res->start + 1,
"spi_txx9")) "spi_txx9"))
goto exit_busy; goto exit_busy;
c->membase = devm_ioremap(&dev->dev, c->membase = devm_ioremap(&dev->dev, res->start, resource_size(res));
res->start, res->end - res->start + 1);
if (!c->membase) if (!c->membase)
goto exit_busy; goto exit_busy;
......
...@@ -53,7 +53,7 @@ ...@@ -53,7 +53,7 @@
#define SPIDEV_MAJOR 153 /* assigned */ #define SPIDEV_MAJOR 153 /* assigned */
#define N_SPI_MINORS 32 /* ... up to 256 */ #define N_SPI_MINORS 32 /* ... up to 256 */
static unsigned long minors[N_SPI_MINORS / BITS_PER_LONG]; static DECLARE_BITMAP(minors, N_SPI_MINORS);
/* Bit masks for spi_device.mode management. Note that incorrect /* Bit masks for spi_device.mode management. Note that incorrect
...@@ -558,7 +558,7 @@ static struct class *spidev_class; ...@@ -558,7 +558,7 @@ static struct class *spidev_class;
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
static int spidev_probe(struct spi_device *spi) static int __devinit spidev_probe(struct spi_device *spi)
{ {
struct spidev_data *spidev; struct spidev_data *spidev;
int status; int status;
...@@ -607,7 +607,7 @@ static int spidev_probe(struct spi_device *spi) ...@@ -607,7 +607,7 @@ static int spidev_probe(struct spi_device *spi)
return status; return status;
} }
static int spidev_remove(struct spi_device *spi) static int __devexit spidev_remove(struct spi_device *spi)
{ {
struct spidev_data *spidev = spi_get_drvdata(spi); struct spidev_data *spidev = spi_get_drvdata(spi);
...@@ -629,7 +629,7 @@ static int spidev_remove(struct spi_device *spi) ...@@ -629,7 +629,7 @@ static int spidev_remove(struct spi_device *spi)
return 0; return 0;
} }
static struct spi_driver spidev_spi = { static struct spi_driver spidev_spi_driver = {
.driver = { .driver = {
.name = "spidev", .name = "spidev",
.owner = THIS_MODULE, .owner = THIS_MODULE,
...@@ -661,14 +661,14 @@ static int __init spidev_init(void) ...@@ -661,14 +661,14 @@ static int __init spidev_init(void)
spidev_class = class_create(THIS_MODULE, "spidev"); spidev_class = class_create(THIS_MODULE, "spidev");
if (IS_ERR(spidev_class)) { if (IS_ERR(spidev_class)) {
unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
return PTR_ERR(spidev_class); return PTR_ERR(spidev_class);
} }
status = spi_register_driver(&spidev_spi); status = spi_register_driver(&spidev_spi_driver);
if (status < 0) { if (status < 0) {
class_destroy(spidev_class); class_destroy(spidev_class);
unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
} }
return status; return status;
} }
...@@ -676,9 +676,9 @@ module_init(spidev_init); ...@@ -676,9 +676,9 @@ module_init(spidev_init);
static void __exit spidev_exit(void) static void __exit spidev_exit(void)
{ {
spi_unregister_driver(&spidev_spi); spi_unregister_driver(&spidev_spi_driver);
class_destroy(spidev_class); class_destroy(spidev_class);
unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
} }
module_exit(spidev_exit); module_exit(spidev_exit);
......
#ifndef DW_SPI_HEADER_H
#define DW_SPI_HEADER_H
#include <linux/io.h>
/* Bit fields in CTRLR0 */
#define SPI_DFS_OFFSET 0
#define SPI_FRF_OFFSET 4
#define SPI_FRF_SPI 0x0
#define SPI_FRF_SSP 0x1
#define SPI_FRF_MICROWIRE 0x2
#define SPI_FRF_RESV 0x3
#define SPI_MODE_OFFSET 6
#define SPI_SCPH_OFFSET 6
#define SPI_SCOL_OFFSET 7
#define SPI_TMOD_OFFSET 8
#define SPI_TMOD_TR 0x0 /* xmit & recv */
#define SPI_TMOD_TO 0x1 /* xmit only */
#define SPI_TMOD_RO 0x2 /* recv only */
#define SPI_TMOD_EPROMREAD 0x3 /* eeprom read mode */
#define SPI_SLVOE_OFFSET 10
#define SPI_SRL_OFFSET 11
#define SPI_CFS_OFFSET 12
/* Bit fields in SR, 7 bits */
#define SR_MASK 0x7f /* cover 7 bits */
#define SR_BUSY (1 << 0)
#define SR_TF_NOT_FULL (1 << 1)
#define SR_TF_EMPT (1 << 2)
#define SR_RF_NOT_EMPT (1 << 3)
#define SR_RF_FULL (1 << 4)
#define SR_TX_ERR (1 << 5)
#define SR_DCOL (1 << 6)
/* Bit fields in ISR, IMR, RISR, 7 bits */
#define SPI_INT_TXEI (1 << 0)
#define SPI_INT_TXOI (1 << 1)
#define SPI_INT_RXUI (1 << 2)
#define SPI_INT_RXOI (1 << 3)
#define SPI_INT_RXFI (1 << 4)
#define SPI_INT_MSTI (1 << 5)
/* TX RX interrupt level threshhold, max can be 256 */
#define SPI_INT_THRESHOLD 32
enum dw_ssi_type {
SSI_MOTO_SPI = 0,
SSI_TI_SSP,
SSI_NS_MICROWIRE,
};
struct dw_spi_reg {
u32 ctrl0;
u32 ctrl1;
u32 ssienr;
u32 mwcr;
u32 ser;
u32 baudr;
u32 txfltr;
u32 rxfltr;
u32 txflr;
u32 rxflr;
u32 sr;
u32 imr;
u32 isr;
u32 risr;
u32 txoicr;
u32 rxoicr;
u32 rxuicr;
u32 msticr;
u32 icr;
u32 dmacr;
u32 dmatdlr;
u32 dmardlr;
u32 idr;
u32 version;
u32 dr; /* Currently oper as 32 bits,
though only low 16 bits matters */
} __packed;
struct dw_spi {
struct spi_master *master;
struct spi_device *cur_dev;
struct device *parent_dev;
enum dw_ssi_type type;
void __iomem *regs;
unsigned long paddr;
u32 iolen;
int irq;
u32 max_freq; /* max bus freq supported */
u16 bus_num;
u16 num_cs; /* supported slave numbers */
/* Driver message queue */
struct workqueue_struct *workqueue;
struct work_struct pump_messages;
spinlock_t lock;
struct list_head queue;
int busy;
int run;
/* Message Transfer pump */
struct tasklet_struct pump_transfers;
/* Current message transfer state info */
struct spi_message *cur_msg;
struct spi_transfer *cur_transfer;
struct chip_data *cur_chip;
struct chip_data *prev_chip;
size_t len;
void *tx;
void *tx_end;
void *rx;
void *rx_end;
int dma_mapped;
dma_addr_t rx_dma;
dma_addr_t tx_dma;
size_t rx_map_len;
size_t tx_map_len;
u8 n_bytes; /* current is a 1/2 bytes op */
u8 max_bits_per_word; /* maxim is 16b */
u32 dma_width;
int cs_change;
int (*write)(struct dw_spi *dws);
int (*read)(struct dw_spi *dws);
irqreturn_t (*transfer_handler)(struct dw_spi *dws);
void (*cs_control)(u32 command);
/* Dma info */
int dma_inited;
struct dma_chan *txchan;
struct dma_chan *rxchan;
int txdma_done;
int rxdma_done;
u64 tx_param;
u64 rx_param;
struct device *dma_dev;
dma_addr_t dma_addr;
/* Bus interface info */
void *priv;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs;
#endif
};
#define dw_readl(dw, name) \
__raw_readl(&(((struct dw_spi_reg *)dw->regs)->name))
#define dw_writel(dw, name, val) \
__raw_writel((val), &(((struct dw_spi_reg *)dw->regs)->name))
#define dw_readw(dw, name) \
__raw_readw(&(((struct dw_spi_reg *)dw->regs)->name))
#define dw_writew(dw, name, val) \
__raw_writew((val), &(((struct dw_spi_reg *)dw->regs)->name))
static inline void spi_enable_chip(struct dw_spi *dws, int enable)
{
dw_writel(dws, ssienr, (enable ? 1 : 0));
}
static inline void spi_set_clk(struct dw_spi *dws, u16 div)
{
dw_writel(dws, baudr, div);
}
static inline void spi_chip_sel(struct dw_spi *dws, u16 cs)
{
if (cs > dws->num_cs)
return;
dw_writel(dws, ser, 1 << cs);
}
/* Disable IRQ bits */
static inline void spi_mask_intr(struct dw_spi *dws, u32 mask)
{
u32 new_mask;
new_mask = dw_readl(dws, imr) & ~mask;
dw_writel(dws, imr, new_mask);
}
/* Enable IRQ bits */
static inline void spi_umask_intr(struct dw_spi *dws, u32 mask)
{
u32 new_mask;
new_mask = dw_readl(dws, imr) | mask;
dw_writel(dws, imr, new_mask);
}
/*
* Each SPI slave device to work with dw_api controller should
* has such a structure claiming its working mode (PIO/DMA etc),
* which can be save in the "controller_data" member of the
* struct spi_device
*/
struct dw_spi_chip {
u8 poll_mode; /* 0 for contoller polling mode */
u8 type; /* SPI/SSP/Micrwire */
u8 enable_dma;
void (*cs_control)(u32 command);
};
extern int dw_spi_add_host(struct dw_spi *dws);
extern void dw_spi_remove_host(struct dw_spi *dws);
extern int dw_spi_suspend_host(struct dw_spi *dws);
extern int dw_spi_resume_host(struct dw_spi *dws);
#endif /* DW_SPI_HEADER_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment