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

* 'spi/next' of git://git.secretlab.ca/git/linux-2.6: (34 commits)
  spi/dw_spi: move dw_spi.h into drivers/spi
  spi/dw_spi: Fix missing header
  gpio/langwell: Clear edge bit before handling
  gpio/langwell: Simplify demux loop
  gpio/langwell: Convert irq name space
  gpio/langwell: Fix broken irq_eoi change.
  gpio; Make Intel chipset gpio drivers depend on x86
  gpio/cs5535-gpio: Fix section mismatch
  spi/rtc-{ds1390,ds3234,m41t94}: Use spi_get_drvdata() for SPI devices
  spi/davinci: Support DMA transfers larger than 65535 words
  spi/davinci: Use correct length parameter to dma_map_single calls
  gpio: Use __devexit at necessary places
  gpio: add MODULE_DEVICE_TABLE to pch_gpio and ml_ioh_gpio
  gpio/mcp23s08: support mcp23s17 variant
  of_mmc_spi: add card detect irq support
  spi/omap_mcspi: catch xfers of non-multiple SPI word size
  spi/omap_mcspi: Off-by-one error in finding the right divisor
  gpio/pca953x: Fix wrong pointer type
  spi/pl022: rid dangling labels
  spi: add support for SuperH SPI
  ...
This commit is contained in:
Linus Torvalds 2011-03-18 10:56:02 -07:00
commit b061c59c27
35 changed files with 1920 additions and 261 deletions

View file

@ -53,6 +53,12 @@ if SPI_MASTER
comment "SPI Master Controller Drivers"
config SPI_ALTERA
tristate "Altera SPI Controller"
select SPI_BITBANG
help
This is the driver for the Altera SPI Controller.
config SPI_ATH79
tristate "Atheros AR71XX/AR724X/AR913X SPI controller driver"
depends on ATH79 && GENERIC_GPIO
@ -231,6 +237,13 @@ config SPI_FSL_ESPI
From MPC8536, 85xx platform uses the controller, and all P10xx,
P20xx, P30xx,P40xx, P50xx uses this controller.
config SPI_OC_TINY
tristate "OpenCores tiny SPI"
depends on GENERIC_GPIO
select SPI_BITBANG
help
This is the driver for OpenCores tiny SPI master controller.
config SPI_OMAP_UWIRE
tristate "OMAP1 MicroWire"
depends on ARCH_OMAP1
@ -330,6 +343,12 @@ config SPI_SH_MSIOF
help
SPI driver for SuperH MSIOF blocks.
config SPI_SH
tristate "SuperH SPI controller"
depends on SUPERH
help
SPI driver for SuperH SPI blocks.
config SPI_SH_SCI
tristate "SuperH SCI SPI controller"
depends on SUPERH

View file

@ -9,6 +9,7 @@ ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG
obj-$(CONFIG_SPI_MASTER) += spi.o
# SPI master controller drivers (bus)
obj-$(CONFIG_SPI_ALTERA) += spi_altera.o
obj-$(CONFIG_SPI_ATMEL) += atmel_spi.o
obj-$(CONFIG_SPI_ATH79) += ath79_spi.o
obj-$(CONFIG_SPI_BFIN) += spi_bfin5xx.o
@ -27,6 +28,7 @@ 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_PXA2XX_PCI) += pxa2xx_spi_pci.o
obj-$(CONFIG_SPI_OC_TINY) += spi_oc_tiny.o
obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o
obj-$(CONFIG_SPI_OMAP24XX) += omap2_mcspi.o
obj-$(CONFIG_SPI_OMAP_100K) += omap_spi_100k.o
@ -47,6 +49,7 @@ obj-$(CONFIG_SPI_TI_SSP) += ti-ssp-spi.o
obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi_topcliff_pch.o
obj-$(CONFIG_SPI_TXX9) += spi_txx9.o
obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o
obj-$(CONFIG_SPI_SH) += spi_sh.o
obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o
obj-$(CONFIG_SPI_SH_MSIOF) += spi_sh_msiof.o
obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o

View file

@ -329,15 +329,16 @@ struct vendor_data {
/**
* struct pl022 - This is the private SSP driver data structure
* @adev: AMBA device model hookup
* @vendor: Vendor data for the IP block
* @phybase: The physical memory where the SSP device resides
* @virtbase: The virtual memory where the SSP is mapped
* @vendor: vendor data for the IP block
* @phybase: the physical memory where the SSP device resides
* @virtbase: the virtual memory where the SSP is mapped
* @clk: outgoing clock "SPICLK" for the SPI bus
* @master: SPI framework hookup
* @master_info: controller-specific data from machine setup
* @regs: SSP controller register's virtual address
* @pump_messages: Work struct for scheduling work to the workqueue
* @lock: spinlock to syncronise access to driver data
* @workqueue: a workqueue on which any spi_message request is queued
* @pump_messages: work struct for scheduling work to the workqueue
* @queue_lock: spinlock to syncronise access to message queue
* @queue: message queue
* @busy: workqueue is busy
* @running: workqueue is running
* @pump_transfers: Tasklet used in Interrupt Transfer mode
@ -348,8 +349,14 @@ struct vendor_data {
* @tx_end: end position in TX buffer to be read
* @rx: current position in RX buffer to be written
* @rx_end: end position in RX buffer to be written
* @readingtype: the type of read currently going on
* @writingtype: the type or write currently going on
* @read: the type of read currently going on
* @write: the type of write currently going on
* @exp_fifo_level: expected FIFO level
* @dma_rx_channel: optional channel for RX DMA
* @dma_tx_channel: optional channel for TX DMA
* @sgt_rx: scattertable for the RX transfer
* @sgt_tx: scattertable for the TX transfer
* @dummypage: a dummy page used for driving data on the bus with DMA
*/
struct pl022 {
struct amba_device *adev;
@ -397,8 +404,8 @@ struct pl022 {
* @cpsr: Value of Clock prescale register
* @n_bytes: how many bytes(power of 2) reqd for a given data width of client
* @enable_dma: Whether to enable DMA or not
* @write: function ptr to be used to write when doing xfer for this chip
* @read: function ptr to be used to read when doing xfer for this chip
* @write: function ptr to be used to write when doing xfer for this chip
* @cs_control: chip select callback provided by chip
* @xfer_type: polling/interrupt/DMA
*
@ -508,9 +515,10 @@ static void giveback(struct pl022 *pl022)
msg->state = NULL;
if (msg->complete)
msg->complete(msg->context);
/* This message is completed, so let's turn off the clocks! */
/* This message is completed, so let's turn off the clocks & power */
clk_disable(pl022->clk);
amba_pclk_disable(pl022->adev);
amba_vcore_disable(pl022->adev);
}
/**
@ -917,7 +925,6 @@ static int configure_dma(struct pl022 *pl022)
struct dma_chan *txchan = pl022->dma_tx_channel;
struct dma_async_tx_descriptor *rxdesc;
struct dma_async_tx_descriptor *txdesc;
dma_cookie_t cookie;
/* Check that the channels are available */
if (!rxchan || !txchan)
@ -962,10 +969,8 @@ static int configure_dma(struct pl022 *pl022)
tx_conf.dst_addr_width = rx_conf.src_addr_width;
BUG_ON(rx_conf.src_addr_width != tx_conf.dst_addr_width);
rxchan->device->device_control(rxchan, DMA_SLAVE_CONFIG,
(unsigned long) &rx_conf);
txchan->device->device_control(txchan, DMA_SLAVE_CONFIG,
(unsigned long) &tx_conf);
dmaengine_slave_config(rxchan, &rx_conf);
dmaengine_slave_config(txchan, &tx_conf);
/* Create sglists for the transfers */
pages = (pl022->cur_transfer->len >> PAGE_SHIFT) + 1;
@ -1018,23 +1023,17 @@ static int configure_dma(struct pl022 *pl022)
rxdesc->callback_param = pl022;
/* Submit and fire RX and TX with TX last so we're ready to read! */
cookie = rxdesc->tx_submit(rxdesc);
if (dma_submit_error(cookie))
goto err_submit_rx;
cookie = txdesc->tx_submit(txdesc);
if (dma_submit_error(cookie))
goto err_submit_tx;
rxchan->device->device_issue_pending(rxchan);
txchan->device->device_issue_pending(txchan);
dmaengine_submit(rxdesc);
dmaengine_submit(txdesc);
dma_async_issue_pending(rxchan);
dma_async_issue_pending(txchan);
return 0;
err_submit_tx:
err_submit_rx:
err_txdesc:
txchan->device->device_control(txchan, DMA_TERMINATE_ALL, 0);
dmaengine_terminate_all(txchan);
err_rxdesc:
rxchan->device->device_control(rxchan, DMA_TERMINATE_ALL, 0);
dmaengine_terminate_all(rxchan);
dma_unmap_sg(txchan->device->dev, pl022->sgt_tx.sgl,
pl022->sgt_tx.nents, DMA_TO_DEVICE);
err_tx_sgmap:
@ -1101,8 +1100,8 @@ static void terminate_dma(struct pl022 *pl022)
struct dma_chan *rxchan = pl022->dma_rx_channel;
struct dma_chan *txchan = pl022->dma_tx_channel;
rxchan->device->device_control(rxchan, DMA_TERMINATE_ALL, 0);
txchan->device->device_control(txchan, DMA_TERMINATE_ALL, 0);
dmaengine_terminate_all(rxchan);
dmaengine_terminate_all(txchan);
unmap_free_dma_scatter(pl022);
}
@ -1482,9 +1481,11 @@ static void pump_messages(struct work_struct *work)
/* Setup the SPI using the per chip configuration */
pl022->cur_chip = spi_get_ctldata(pl022->cur_msg->spi);
/*
* We enable the clocks here, then the clocks will be disabled when
* giveback() is called in each method (poll/interrupt/DMA)
* We enable the core voltage and clocks here, then the clocks
* and core will be disabled when giveback() is called in each method
* (poll/interrupt/DMA)
*/
amba_vcore_enable(pl022->adev);
amba_pclk_enable(pl022->adev);
clk_enable(pl022->clk);
restore_state(pl022);
@ -1910,8 +1911,6 @@ static int pl022_setup(struct spi_device *spi)
&& ((pl022->master_info)->enable_dma)) {
chip->enable_dma = true;
dev_dbg(&spi->dev, "DMA mode set in controller state\n");
if (status < 0)
goto err_config_params;
SSP_WRITE_BITS(chip->dmacr, SSP_DMA_ENABLED,
SSP_DMACR_MASK_RXDMAE, 0);
SSP_WRITE_BITS(chip->dmacr, SSP_DMA_ENABLED,
@ -2130,8 +2129,12 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
goto err_spi_register;
}
dev_dbg(dev, "probe succeded\n");
/* Disable the silicon block pclk and clock it when needed */
/*
* Disable the silicon block pclk and any voltage domain and just
* power it up and clock it when it's needed
*/
amba_pclk_disable(adev);
amba_vcore_disable(adev);
return 0;
err_spi_register:
@ -2196,9 +2199,11 @@ static int pl022_suspend(struct amba_device *adev, pm_message_t state)
return status;
}
amba_vcore_enable(adev);
amba_pclk_enable(adev);
load_ssp_default_config(pl022);
amba_pclk_disable(adev);
amba_vcore_disable(adev);
dev_dbg(&adev->dev, "suspended\n");
return 0;
}

View file

@ -571,6 +571,7 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
unsigned long tx_reg, rx_reg;
struct edmacc_param param;
void *rx_buf;
int b, c;
dma = &dspi->dma;
@ -591,22 +592,38 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
if (t->tx_buf) {
t->tx_dma = dma_map_single(&spi->dev, (void *)t->tx_buf,
dspi->wcount, DMA_TO_DEVICE);
t->len, DMA_TO_DEVICE);
if (dma_mapping_error(&spi->dev, t->tx_dma)) {
dev_dbg(sdev, "Unable to DMA map %d bytes"
"TX buffer\n", dspi->wcount);
"TX buffer\n", t->len);
return -ENOMEM;
}
}
/*
* If number of words is greater than 65535, then we need
* to configure a 3 dimension transfer. Use the BCNTRLD
* feature to allow for transfers that aren't even multiples
* of 65535 (or any other possible b size) by first transferring
* the remainder amount then grabbing the next N blocks of
* 65535 words.
*/
c = dspi->wcount / (SZ_64K - 1); /* N 65535 Blocks */
b = dspi->wcount - c * (SZ_64K - 1); /* Remainder */
if (b)
c++;
else
b = SZ_64K - 1;
param.opt = TCINTEN | EDMA_TCC(dma->tx_channel);
param.src = t->tx_buf ? t->tx_dma : tx_reg;
param.a_b_cnt = dspi->wcount << 16 | data_type;
param.a_b_cnt = b << 16 | data_type;
param.dst = tx_reg;
param.src_dst_bidx = t->tx_buf ? data_type : 0;
param.link_bcntrld = 0xffff;
param.src_dst_cidx = 0;
param.ccnt = 1;
param.link_bcntrld = 0xffffffff;
param.src_dst_cidx = t->tx_buf ? data_type : 0;
param.ccnt = c;
edma_write_slot(dma->tx_channel, &param);
edma_link(dma->tx_channel, dma->dummy_param_slot);
@ -624,7 +641,7 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
if (t->rx_buf) {
rx_buf = t->rx_buf;
rx_buf_count = dspi->rcount;
rx_buf_count = t->len;
} else {
rx_buf = dspi->rx_tmp_buf;
rx_buf_count = sizeof(dspi->rx_tmp_buf);
@ -636,19 +653,19 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
dev_dbg(sdev, "Couldn't DMA map a %d bytes RX buffer\n",
rx_buf_count);
if (t->tx_buf)
dma_unmap_single(NULL, t->tx_dma, dspi->wcount,
dma_unmap_single(NULL, t->tx_dma, t->len,
DMA_TO_DEVICE);
return -ENOMEM;
}
param.opt = TCINTEN | EDMA_TCC(dma->rx_channel);
param.src = rx_reg;
param.a_b_cnt = dspi->rcount << 16 | data_type;
param.a_b_cnt = b << 16 | data_type;
param.dst = t->rx_dma;
param.src_dst_bidx = (t->rx_buf ? data_type : 0) << 16;
param.link_bcntrld = 0xffff;
param.src_dst_cidx = 0;
param.ccnt = 1;
param.link_bcntrld = 0xffffffff;
param.src_dst_cidx = (t->rx_buf ? data_type : 0) << 16;
param.ccnt = c;
edma_write_slot(dma->rx_channel, &param);
if (pdata->cshold_bug)
@ -675,7 +692,7 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
if (spicfg->io_type == SPI_IO_TYPE_DMA) {
if (t->tx_buf)
dma_unmap_single(NULL, t->tx_dma, dspi->wcount,
dma_unmap_single(NULL, t->tx_dma, t->len,
DMA_TO_DEVICE);
dma_unmap_single(NULL, t->rx_dma, rx_buf_count,

View file

@ -22,10 +22,10 @@
#include <linux/highmem.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/spi/dw_spi.h>
#include <linux/spi/spi.h>
#include "dw_spi.h"
#ifdef CONFIG_DEBUG_FS
#include <linux/debugfs.h>
#endif

234
drivers/spi/dw_spi.h Normal file
View file

@ -0,0 +1,234 @@
#ifndef DW_SPI_HEADER_H
#define DW_SPI_HEADER_H
#include <linux/io.h>
#include <linux/scatterlist.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_MASK (0x3 << SPI_TMOD_OFFSET)
#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 dw_spi_dma_ops {
int (*dma_init)(struct dw_spi *dws);
void (*dma_exit)(struct dw_spi *dws);
int (*dma_transfer)(struct dw_spi *dws, int cs_change);
};
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 fifo_len; /* depth of the FIFO buffer */
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 scatterlist tx_sgl;
struct dma_chan *rxchan;
struct scatterlist rx_sgl;
int dma_chan_done;
struct device *dma_dev;
dma_addr_t dma_addr; /* phy address of the Data register */
struct dw_spi_dma_ops *dma_ops;
void *dma_priv; /* platform relate info */
struct pci_dev *dmac;
/* 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;
if (dws->cs_control)
dws->cs_control(1);
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);
extern void dw_spi_xfer_done(struct dw_spi *dws);
/* platform related setup */
extern int dw_spi_mid_init(struct dw_spi *dws); /* Intel MID platforms */
#endif /* DW_SPI_HEADER_H */

View file

@ -22,7 +22,8 @@
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/spi/dw_spi.h>
#include "dw_spi.h"
#ifdef CONFIG_SPI_DW_MID_DMA
#include <linux/intel_mid_dma.h>

View file

@ -13,8 +13,10 @@
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spi/dw_spi.h>
#include <linux/spi/spi.h>
#include <linux/scatterlist.h>
#include "dw_spi.h"
#define DRIVER_NAME "dw_spi_mmio"

View file

@ -20,9 +20,10 @@
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/spi/dw_spi.h>
#include <linux/spi/spi.h>
#include "dw_spi.h"
#define DRIVER_NAME "dw_spi_pci"
struct dw_spi_pci {

View file

@ -467,6 +467,9 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
rx_reg = base + OMAP2_MCSPI_RX0;
chstat_reg = base + OMAP2_MCSPI_CHSTAT0;
if (c < (word_len>>3))
return 0;
if (word_len <= 8) {
u8 *rx;
const u8 *tx;
@ -514,7 +517,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
dev_vdbg(&spi->dev, "read-%d %02x\n",
word_len, *(rx - 1));
}
} while (c);
} while (c > (word_len>>3));
} else if (word_len <= 16) {
u16 *rx;
const u16 *tx;
@ -561,7 +564,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
dev_vdbg(&spi->dev, "read-%d %04x\n",
word_len, *(rx - 1));
}
} while (c);
} while (c > (word_len>>3));
} else if (word_len <= 32) {
u32 *rx;
const u32 *tx;
@ -608,7 +611,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
dev_vdbg(&spi->dev, "read-%d %08x\n",
word_len, *(rx - 1));
}
} while (c);
} while (c > (word_len>>3));
}
/* for TX_ONLY mode, be sure all words have shifted out */
@ -631,6 +634,17 @@ out:
return count - c;
}
static u32 omap2_mcspi_calc_divisor(u32 speed_hz)
{
u32 div;
for (div = 0; div < 15; div++)
if (speed_hz >= (OMAP2_MCSPI_MAX_FREQ >> div))
return div;
return 15;
}
/* called only when no transfer is active to this device */
static int omap2_mcspi_setup_transfer(struct spi_device *spi,
struct spi_transfer *t)
@ -653,12 +667,8 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
if (t && t->speed_hz)
speed_hz = t->speed_hz;
if (speed_hz) {
while (div <= 15 && (OMAP2_MCSPI_MAX_FREQ / (1 << div))
> speed_hz)
div++;
} else
div = 15;
speed_hz = min_t(u32, speed_hz, OMAP2_MCSPI_MAX_FREQ);
div = omap2_mcspi_calc_divisor(speed_hz);
l = mcspi_cached_chconf0(spi);
@ -695,7 +705,7 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
mcspi_write_chconf0(spi, l);
dev_dbg(&spi->dev, "setup: speed %d, sample %s edge, clk %s\n",
OMAP2_MCSPI_MAX_FREQ / (1 << div),
OMAP2_MCSPI_MAX_FREQ >> div,
(spi->mode & SPI_CPHA) ? "trailing" : "leading",
(spi->mode & SPI_CPOL) ? "inverted" : "normal");
@ -996,10 +1006,10 @@ static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m)
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));
if (t->speed_hz && t->speed_hz < (OMAP2_MCSPI_MAX_FREQ >> 15)) {
dev_dbg(&spi->dev, "speed_hz %d below minimum %d Hz\n",
t->speed_hz,
OMAP2_MCSPI_MAX_FREQ >> 15);
return -EINVAL;
}

339
drivers/spi/spi_altera.c Normal file
View file

@ -0,0 +1,339 @@
/*
* Altera SPI driver
*
* Copyright (C) 2008 Thomas Chou <thomas@wytron.com.tw>
*
* Based on spi_s3c24xx.c, which is:
* Copyright (c) 2006 Ben Dooks
* Copyright (c) 2006 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/init.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/io.h>
#include <linux/of.h>
#define DRV_NAME "spi_altera"
#define ALTERA_SPI_RXDATA 0
#define ALTERA_SPI_TXDATA 4
#define ALTERA_SPI_STATUS 8
#define ALTERA_SPI_CONTROL 12
#define ALTERA_SPI_SLAVE_SEL 20
#define ALTERA_SPI_STATUS_ROE_MSK 0x8
#define ALTERA_SPI_STATUS_TOE_MSK 0x10
#define ALTERA_SPI_STATUS_TMT_MSK 0x20
#define ALTERA_SPI_STATUS_TRDY_MSK 0x40
#define ALTERA_SPI_STATUS_RRDY_MSK 0x80
#define ALTERA_SPI_STATUS_E_MSK 0x100
#define ALTERA_SPI_CONTROL_IROE_MSK 0x8
#define ALTERA_SPI_CONTROL_ITOE_MSK 0x10
#define ALTERA_SPI_CONTROL_ITRDY_MSK 0x40
#define ALTERA_SPI_CONTROL_IRRDY_MSK 0x80
#define ALTERA_SPI_CONTROL_IE_MSK 0x100
#define ALTERA_SPI_CONTROL_SSO_MSK 0x400
struct altera_spi {
/* bitbang has to be first */
struct spi_bitbang bitbang;
struct completion done;
void __iomem *base;
int irq;
int len;
int count;
int bytes_per_word;
unsigned long imr;
/* data buffers */
const unsigned char *tx;
unsigned char *rx;
};
static inline struct altera_spi *altera_spi_to_hw(struct spi_device *sdev)
{
return spi_master_get_devdata(sdev->master);
}
static void altera_spi_chipsel(struct spi_device *spi, int value)
{
struct altera_spi *hw = altera_spi_to_hw(spi);
if (spi->mode & SPI_CS_HIGH) {
switch (value) {
case BITBANG_CS_INACTIVE:
writel(1 << spi->chip_select,
hw->base + ALTERA_SPI_SLAVE_SEL);
hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK;
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
break;
case BITBANG_CS_ACTIVE:
hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK;
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
writel(0, hw->base + ALTERA_SPI_SLAVE_SEL);
break;
}
} else {
switch (value) {
case BITBANG_CS_INACTIVE:
hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK;
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
break;
case BITBANG_CS_ACTIVE:
writel(1 << spi->chip_select,
hw->base + ALTERA_SPI_SLAVE_SEL);
hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK;
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
break;
}
}
}
static int altera_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t)
{
return 0;
}
static int altera_spi_setup(struct spi_device *spi)
{
return 0;
}
static inline unsigned int hw_txbyte(struct altera_spi *hw, int count)
{
if (hw->tx) {
switch (hw->bytes_per_word) {
case 1:
return hw->tx[count];
case 2:
return (hw->tx[count * 2]
| (hw->tx[count * 2 + 1] << 8));
}
}
return 0;
}
static int altera_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
{
struct altera_spi *hw = altera_spi_to_hw(spi);
hw->tx = t->tx_buf;
hw->rx = t->rx_buf;
hw->count = 0;
hw->bytes_per_word = (t->bits_per_word ? : spi->bits_per_word) / 8;
hw->len = t->len / hw->bytes_per_word;
if (hw->irq >= 0) {
/* enable receive interrupt */
hw->imr |= ALTERA_SPI_CONTROL_IRRDY_MSK;
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
/* send the first byte */
writel(hw_txbyte(hw, 0), hw->base + ALTERA_SPI_TXDATA);
wait_for_completion(&hw->done);
/* disable receive interrupt */
hw->imr &= ~ALTERA_SPI_CONTROL_IRRDY_MSK;
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
} else {
/* send the first byte */
writel(hw_txbyte(hw, 0), hw->base + ALTERA_SPI_TXDATA);
while (1) {
unsigned int rxd;
while (!(readl(hw->base + ALTERA_SPI_STATUS) &
ALTERA_SPI_STATUS_RRDY_MSK))
cpu_relax();
rxd = readl(hw->base + ALTERA_SPI_RXDATA);
if (hw->rx) {
switch (hw->bytes_per_word) {
case 1:
hw->rx[hw->count] = rxd;
break;
case 2:
hw->rx[hw->count * 2] = rxd;
hw->rx[hw->count * 2 + 1] = rxd >> 8;
break;
}
}
hw->count++;
if (hw->count < hw->len)
writel(hw_txbyte(hw, hw->count),
hw->base + ALTERA_SPI_TXDATA);
else
break;
}
}
return hw->count * hw->bytes_per_word;
}
static irqreturn_t altera_spi_irq(int irq, void *dev)
{
struct altera_spi *hw = dev;
unsigned int rxd;
rxd = readl(hw->base + ALTERA_SPI_RXDATA);
if (hw->rx) {
switch (hw->bytes_per_word) {
case 1:
hw->rx[hw->count] = rxd;
break;
case 2:
hw->rx[hw->count * 2] = rxd;
hw->rx[hw->count * 2 + 1] = rxd >> 8;
break;
}
}
hw->count++;
if (hw->count < hw->len)
writel(hw_txbyte(hw, hw->count), hw->base + ALTERA_SPI_TXDATA);
else
complete(&hw->done);
return IRQ_HANDLED;
}
static int __devinit altera_spi_probe(struct platform_device *pdev)
{
struct altera_spi_platform_data *platp = pdev->dev.platform_data;
struct altera_spi *hw;
struct spi_master *master;
struct resource *res;
int err = -ENODEV;
master = spi_alloc_master(&pdev->dev, sizeof(struct altera_spi));
if (!master)
return err;
/* setup the master state. */
master->bus_num = pdev->id;
master->num_chipselect = 16;
master->mode_bits = SPI_CS_HIGH;
master->setup = altera_spi_setup;
hw = spi_master_get_devdata(master);
platform_set_drvdata(pdev, hw);
/* setup the state for the bitbang driver */
hw->bitbang.master = spi_master_get(master);
if (!hw->bitbang.master)
return err;
hw->bitbang.setup_transfer = altera_spi_setupxfer;
hw->bitbang.chipselect = altera_spi_chipsel;
hw->bitbang.txrx_bufs = altera_spi_txrx;
/* find and map our resources */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
goto exit_busy;
if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
pdev->name))
goto exit_busy;
hw->base = devm_ioremap_nocache(&pdev->dev, res->start,
resource_size(res));
if (!hw->base)
goto exit_busy;
/* program defaults into the registers */
hw->imr = 0; /* disable spi interrupts */
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
writel(0, hw->base + ALTERA_SPI_STATUS); /* clear status reg */
if (readl(hw->base + ALTERA_SPI_STATUS) & ALTERA_SPI_STATUS_RRDY_MSK)
readl(hw->base + ALTERA_SPI_RXDATA); /* flush rxdata */
/* irq is optional */
hw->irq = platform_get_irq(pdev, 0);
if (hw->irq >= 0) {
init_completion(&hw->done);
err = devm_request_irq(&pdev->dev, hw->irq, altera_spi_irq, 0,
pdev->name, hw);
if (err)
goto exit;
}
/* find platform data */
if (!platp)
hw->bitbang.master->dev.of_node = pdev->dev.of_node;
/* register our spi controller */
err = spi_bitbang_start(&hw->bitbang);
if (err)
goto exit;
dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq);
return 0;
exit_busy:
err = -EBUSY;
exit:
platform_set_drvdata(pdev, NULL);
spi_master_put(master);
return err;
}
static int __devexit altera_spi_remove(struct platform_device *dev)
{
struct altera_spi *hw = platform_get_drvdata(dev);
struct spi_master *master = hw->bitbang.master;
spi_bitbang_stop(&hw->bitbang);
platform_set_drvdata(dev, NULL);
spi_master_put(master);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id altera_spi_match[] = {
{ .compatible = "ALTR,spi-1.0", },
{},
};
MODULE_DEVICE_TABLE(of, altera_spi_match);
#else /* CONFIG_OF */
#define altera_spi_match NULL
#endif /* CONFIG_OF */
static struct platform_driver altera_spi_driver = {
.probe = altera_spi_probe,
.remove = __devexit_p(altera_spi_remove),
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.pm = NULL,
.of_match_table = altera_spi_match,
},
};
static int __init altera_spi_init(void)
{
return platform_driver_register(&altera_spi_driver);
}
module_init(altera_spi_init);
static void __exit altera_spi_exit(void)
{
platform_driver_unregister(&altera_spi_driver);
}
module_exit(altera_spi_exit);
MODULE_DESCRIPTION("Altera SPI driver");
MODULE_AUTHOR("Thomas Chou <[email protected]>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);

View file

@ -425,6 +425,7 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id)
struct bfin_spi_slave_data *chip = drv_data->cur_chip;
struct spi_message *msg = drv_data->cur_msg;
int n_bytes = drv_data->n_bytes;
int loop = 0;
/* wait until transfer finished. */
while (!(read_STAT(drv_data) & BIT_STAT_RXS))
@ -435,10 +436,15 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id)
/* last read */
if (drv_data->rx) {
dev_dbg(&drv_data->pdev->dev, "last read\n");
if (n_bytes == 2)
*(u16 *) (drv_data->rx) = read_RDBR(drv_data);
else if (n_bytes == 1)
*(u8 *) (drv_data->rx) = read_RDBR(drv_data);
if (n_bytes % 2) {
u16 *buf = (u16 *)drv_data->rx;
for (loop = 0; loop < n_bytes / 2; loop++)
*buf++ = read_RDBR(drv_data);
} else {
u8 *buf = (u8 *)drv_data->rx;
for (loop = 0; loop < n_bytes; loop++)
*buf++ = read_RDBR(drv_data);
}
drv_data->rx += n_bytes;
}
@ -458,29 +464,53 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id)
if (drv_data->rx && drv_data->tx) {
/* duplex */
dev_dbg(&drv_data->pdev->dev, "duplex: write_TDBR\n");
if (drv_data->n_bytes == 2) {
*(u16 *) (drv_data->rx) = read_RDBR(drv_data);
write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));
} else if (drv_data->n_bytes == 1) {
*(u8 *) (drv_data->rx) = read_RDBR(drv_data);
write_TDBR(drv_data, (*(u8 *) (drv_data->tx)));
if (n_bytes % 2) {
u16 *buf = (u16 *)drv_data->rx;
u16 *buf2 = (u16 *)drv_data->tx;
for (loop = 0; loop < n_bytes / 2; loop++) {
*buf++ = read_RDBR(drv_data);
write_TDBR(drv_data, *buf2++);
}
} else {
u8 *buf = (u8 *)drv_data->rx;
u8 *buf2 = (u8 *)drv_data->tx;
for (loop = 0; loop < n_bytes; loop++) {
*buf++ = read_RDBR(drv_data);
write_TDBR(drv_data, *buf2++);
}
}
} else if (drv_data->rx) {
/* read */
dev_dbg(&drv_data->pdev->dev, "read: write_TDBR\n");
if (drv_data->n_bytes == 2)
*(u16 *) (drv_data->rx) = read_RDBR(drv_data);
else if (drv_data->n_bytes == 1)
*(u8 *) (drv_data->rx) = read_RDBR(drv_data);
write_TDBR(drv_data, chip->idle_tx_val);
if (n_bytes % 2) {
u16 *buf = (u16 *)drv_data->rx;
for (loop = 0; loop < n_bytes / 2; loop++) {
*buf++ = read_RDBR(drv_data);
write_TDBR(drv_data, chip->idle_tx_val);
}
} else {
u8 *buf = (u8 *)drv_data->rx;
for (loop = 0; loop < n_bytes; loop++) {
*buf++ = read_RDBR(drv_data);
write_TDBR(drv_data, chip->idle_tx_val);
}
}
} else if (drv_data->tx) {
/* write */
dev_dbg(&drv_data->pdev->dev, "write: write_TDBR\n");
bfin_spi_dummy_read(drv_data);
if (drv_data->n_bytes == 2)
write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));
else if (drv_data->n_bytes == 1)
write_TDBR(drv_data, (*(u8 *) (drv_data->tx)));
if (n_bytes % 2) {
u16 *buf = (u16 *)drv_data->tx;
for (loop = 0; loop < n_bytes / 2; loop++) {
read_RDBR(drv_data);
write_TDBR(drv_data, *buf++);
}
} else {
u8 *buf = (u8 *)drv_data->tx;
for (loop = 0; loop < n_bytes; loop++) {
read_RDBR(drv_data);
write_TDBR(drv_data, *buf++);
}
}
}
if (drv_data->tx)
@ -623,6 +653,7 @@ static void bfin_spi_pump_transfers(unsigned long data)
message->state = bfin_spi_next_transfer(drv_data);
/* Schedule next transfer tasklet */
tasklet_schedule(&drv_data->pump_transfers);
return;
}
if (transfer->tx_buf != NULL) {
@ -651,16 +682,16 @@ static void bfin_spi_pump_transfers(unsigned long data)
/* Bits per word setup */
bits_per_word = transfer->bits_per_word ? : message->spi->bits_per_word;
if (bits_per_word == 8) {
drv_data->n_bytes = 1;
drv_data->len = transfer->len;
cr_width = 0;
drv_data->ops = &bfin_bfin_spi_transfer_ops_u8;
} else if (bits_per_word == 16) {
drv_data->n_bytes = 2;
if ((bits_per_word > 0) && (bits_per_word % 16 == 0)) {
drv_data->n_bytes = bits_per_word/8;
drv_data->len = (transfer->len) >> 1;
cr_width = BIT_CTL_WORDSIZE;
drv_data->ops = &bfin_bfin_spi_transfer_ops_u16;
} else if ((bits_per_word > 0) && (bits_per_word % 8 == 0)) {
drv_data->n_bytes = bits_per_word/8;
drv_data->len = transfer->len;
cr_width = 0;
drv_data->ops = &bfin_bfin_spi_transfer_ops_u8;
} else {
dev_err(&drv_data->pdev->dev, "transfer: unsupported bits_per_word\n");
message->status = -EINVAL;
@ -815,10 +846,19 @@ static void bfin_spi_pump_transfers(unsigned long data)
if (drv_data->tx == NULL)
write_TDBR(drv_data, chip->idle_tx_val);
else {
if (bits_per_word == 8)
write_TDBR(drv_data, (*(u8 *) (drv_data->tx)));
else
write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));
int loop;
if (bits_per_word % 16 == 0) {
u16 *buf = (u16 *)drv_data->tx;
for (loop = 0; loop < bits_per_word / 16;
loop++) {
write_TDBR(drv_data, *buf++);
}
} else if (bits_per_word % 8 == 0) {
u8 *buf = (u8 *)drv_data->tx;
for (loop = 0; loop < bits_per_word / 8; loop++)
write_TDBR(drv_data, *buf++);
}
drv_data->tx += drv_data->n_bytes;
}
@ -1031,7 +1071,7 @@ static int bfin_spi_setup(struct spi_device *spi)
chip->ctl_reg &= bfin_ctl_reg;
}
if (spi->bits_per_word != 8 && spi->bits_per_word != 16) {
if (spi->bits_per_word % 8) {
dev_err(&spi->dev, "%d bits_per_word is not supported\n",
spi->bits_per_word);
goto error;

View file

@ -259,10 +259,6 @@ static void bitbang_work(struct work_struct *work)
struct spi_bitbang *bitbang =
container_of(work, struct spi_bitbang, work);
unsigned long flags;
int (*setup_transfer)(struct spi_device *,
struct spi_transfer *);
setup_transfer = bitbang->setup_transfer;
spin_lock_irqsave(&bitbang->lock, flags);
bitbang->busy = 1;
@ -300,11 +296,7 @@ static void bitbang_work(struct work_struct *work)
/* init (-1) or override (1) transfer params */
if (do_setup != 0) {
if (!setup_transfer) {
status = -ENOPROTOOPT;
break;
}
status = setup_transfer(spi, t);
status = bitbang->setup_transfer(spi, t);
if (status < 0)
break;
if (do_setup == -1)
@ -465,6 +457,9 @@ int spi_bitbang_start(struct spi_bitbang *bitbang)
}
} else if (!bitbang->master->setup)
return -EINVAL;
if (bitbang->master->transfer == spi_bitbang_transfer &&
!bitbang->setup_transfer)
return -EINVAL;
/* this task is the only thing to touch the SPI bits */
bitbang->busy = 0;

View file

@ -174,7 +174,7 @@ static unsigned int spi_imx_clkdiv_2(unsigned int fin,
#define SPI_IMX2_3_CTRL 0x08
#define SPI_IMX2_3_CTRL_ENABLE (1 << 0)
#define SPI_IMX2_3_CTRL_XCH (1 << 2)
#define SPI_IMX2_3_CTRL_MODE(cs) (1 << ((cs) + 4))
#define SPI_IMX2_3_CTRL_MODE_MASK (0xf << 4)
#define SPI_IMX2_3_CTRL_POSTDIV_OFFSET 8
#define SPI_IMX2_3_CTRL_PREDIV_OFFSET 12
#define SPI_IMX2_3_CTRL_CS(cs) ((cs) << 18)
@ -253,8 +253,14 @@ static int __maybe_unused spi_imx2_3_config(struct spi_imx_data *spi_imx,
{
u32 ctrl = SPI_IMX2_3_CTRL_ENABLE, cfg = 0;
/* set master mode */
ctrl |= SPI_IMX2_3_CTRL_MODE(config->cs);
/*
* The hardware seems to have a race condition when changing modes. The
* current assumption is that the selection of the channel arrives
* earlier in the hardware than the mode bits when they are written at
* the same time.
* So set master mode for all channels as we do not support slave mode.
*/
ctrl |= SPI_IMX2_3_CTRL_MODE_MASK;
/* set clock speed */
ctrl |= spi_imx2_3_clkdiv(spi_imx->spi_clk, config->speed_hz);

425
drivers/spi/spi_oc_tiny.c Normal file
View file

@ -0,0 +1,425 @@
/*
* OpenCores tiny SPI master driver
*
* http://opencores.org/project,tiny_spi
*
* Copyright (C) 2011 Thomas Chou <thomas@wytron.com.tw>
*
* Based on spi_s3c24xx.c, which is:
* Copyright (c) 2006 Ben Dooks
* Copyright (c) 2006 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/init.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/spi/spi_oc_tiny.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/of.h>
#define DRV_NAME "spi_oc_tiny"
#define TINY_SPI_RXDATA 0
#define TINY_SPI_TXDATA 4
#define TINY_SPI_STATUS 8
#define TINY_SPI_CONTROL 12
#define TINY_SPI_BAUD 16
#define TINY_SPI_STATUS_TXE 0x1
#define TINY_SPI_STATUS_TXR 0x2
struct tiny_spi {
/* bitbang has to be first */
struct spi_bitbang bitbang;
struct completion done;
void __iomem *base;
int irq;
unsigned int freq;
unsigned int baudwidth;
unsigned int baud;
unsigned int speed_hz;
unsigned int mode;
unsigned int len;
unsigned int txc, rxc;
const u8 *txp;
u8 *rxp;
unsigned int gpio_cs_count;
int *gpio_cs;
};
static inline struct tiny_spi *tiny_spi_to_hw(struct spi_device *sdev)
{
return spi_master_get_devdata(sdev->master);
}
static unsigned int tiny_spi_baud(struct spi_device *spi, unsigned int hz)
{
struct tiny_spi *hw = tiny_spi_to_hw(spi);
return min(DIV_ROUND_UP(hw->freq, hz * 2), (1U << hw->baudwidth)) - 1;
}
static void tiny_spi_chipselect(struct spi_device *spi, int is_active)
{
struct tiny_spi *hw = tiny_spi_to_hw(spi);
if (hw->gpio_cs_count) {
gpio_set_value(hw->gpio_cs[spi->chip_select],
(spi->mode & SPI_CS_HIGH) ? is_active : !is_active);
}
}
static int tiny_spi_setup_transfer(struct spi_device *spi,
struct spi_transfer *t)
{
struct tiny_spi *hw = tiny_spi_to_hw(spi);
unsigned int baud = hw->baud;
if (t) {
if (t->speed_hz && t->speed_hz != hw->speed_hz)
baud = tiny_spi_baud(spi, t->speed_hz);
}
writel(baud, hw->base + TINY_SPI_BAUD);
writel(hw->mode, hw->base + TINY_SPI_CONTROL);
return 0;
}
static int tiny_spi_setup(struct spi_device *spi)
{
struct tiny_spi *hw = tiny_spi_to_hw(spi);
if (spi->max_speed_hz != hw->speed_hz) {
hw->speed_hz = spi->max_speed_hz;
hw->baud = tiny_spi_baud(spi, hw->speed_hz);
}
hw->mode = spi->mode & (SPI_CPOL | SPI_CPHA);
return 0;
}
static inline void tiny_spi_wait_txr(struct tiny_spi *hw)
{
while (!(readb(hw->base + TINY_SPI_STATUS) &
TINY_SPI_STATUS_TXR))
cpu_relax();
}
static inline void tiny_spi_wait_txe(struct tiny_spi *hw)
{
while (!(readb(hw->base + TINY_SPI_STATUS) &
TINY_SPI_STATUS_TXE))
cpu_relax();
}
static int tiny_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
{
struct tiny_spi *hw = tiny_spi_to_hw(spi);
const u8 *txp = t->tx_buf;
u8 *rxp = t->rx_buf;
unsigned int i;
if (hw->irq >= 0) {
/* use intrrupt driven data transfer */
hw->len = t->len;
hw->txp = t->tx_buf;
hw->rxp = t->rx_buf;
hw->txc = 0;
hw->rxc = 0;
/* send the first byte */
if (t->len > 1) {
writeb(hw->txp ? *hw->txp++ : 0,
hw->base + TINY_SPI_TXDATA);
hw->txc++;
writeb(hw->txp ? *hw->txp++ : 0,
hw->base + TINY_SPI_TXDATA);
hw->txc++;
writeb(TINY_SPI_STATUS_TXR, hw->base + TINY_SPI_STATUS);
} else {
writeb(hw->txp ? *hw->txp++ : 0,
hw->base + TINY_SPI_TXDATA);
hw->txc++;
writeb(TINY_SPI_STATUS_TXE, hw->base + TINY_SPI_STATUS);
}
wait_for_completion(&hw->done);
} else if (txp && rxp) {
/* we need to tighten the transfer loop */
writeb(*txp++, hw->base + TINY_SPI_TXDATA);
if (t->len > 1) {
writeb(*txp++, hw->base + TINY_SPI_TXDATA);
for (i = 2; i < t->len; i++) {
u8 rx, tx = *txp++;
tiny_spi_wait_txr(hw);
rx = readb(hw->base + TINY_SPI_TXDATA);
writeb(tx, hw->base + TINY_SPI_TXDATA);
*rxp++ = rx;
}
tiny_spi_wait_txr(hw);
*rxp++ = readb(hw->base + TINY_SPI_TXDATA);
}
tiny_spi_wait_txe(hw);
*rxp++ = readb(hw->base + TINY_SPI_RXDATA);
} else if (rxp) {
writeb(0, hw->base + TINY_SPI_TXDATA);
if (t->len > 1) {
writeb(0,
hw->base + TINY_SPI_TXDATA);
for (i = 2; i < t->len; i++) {
u8 rx;
tiny_spi_wait_txr(hw);
rx = readb(hw->base + TINY_SPI_TXDATA);
writeb(0, hw->base + TINY_SPI_TXDATA);
*rxp++ = rx;
}
tiny_spi_wait_txr(hw);
*rxp++ = readb(hw->base + TINY_SPI_TXDATA);
}
tiny_spi_wait_txe(hw);
*rxp++ = readb(hw->base + TINY_SPI_RXDATA);
} else if (txp) {
writeb(*txp++, hw->base + TINY_SPI_TXDATA);
if (t->len > 1) {
writeb(*txp++, hw->base + TINY_SPI_TXDATA);
for (i = 2; i < t->len; i++) {
u8 tx = *txp++;
tiny_spi_wait_txr(hw);
writeb(tx, hw->base + TINY_SPI_TXDATA);
}
}
tiny_spi_wait_txe(hw);
} else {
writeb(0, hw->base + TINY_SPI_TXDATA);
if (t->len > 1) {
writeb(0, hw->base + TINY_SPI_TXDATA);
for (i = 2; i < t->len; i++) {
tiny_spi_wait_txr(hw);
writeb(0, hw->base + TINY_SPI_TXDATA);
}
}
tiny_spi_wait_txe(hw);
}
return t->len;
}
static irqreturn_t tiny_spi_irq(int irq, void *dev)
{
struct tiny_spi *hw = dev;
writeb(0, hw->base + TINY_SPI_STATUS);
if (hw->rxc + 1 == hw->len) {
if (hw->rxp)
*hw->rxp++ = readb(hw->base + TINY_SPI_RXDATA);
hw->rxc++;
complete(&hw->done);
} else {
if (hw->rxp)
*hw->rxp++ = readb(hw->base + TINY_SPI_TXDATA);
hw->rxc++;
if (hw->txc < hw->len) {
writeb(hw->txp ? *hw->txp++ : 0,
hw->base + TINY_SPI_TXDATA);
hw->txc++;
writeb(TINY_SPI_STATUS_TXR,
hw->base + TINY_SPI_STATUS);
} else {
writeb(TINY_SPI_STATUS_TXE,
hw->base + TINY_SPI_STATUS);
}
}
return IRQ_HANDLED;
}
#ifdef CONFIG_OF
#include <linux/of_gpio.h>
static int __devinit tiny_spi_of_probe(struct platform_device *pdev)
{
struct tiny_spi *hw = platform_get_drvdata(pdev);
struct device_node *np = pdev->dev.of_node;
unsigned int i;
const __be32 *val;
int len;
if (!np)
return 0;
hw->gpio_cs_count = of_gpio_count(np);
if (hw->gpio_cs_count) {
hw->gpio_cs = devm_kzalloc(&pdev->dev,
hw->gpio_cs_count * sizeof(unsigned int),
GFP_KERNEL);
if (!hw->gpio_cs)
return -ENOMEM;
}
for (i = 0; i < hw->gpio_cs_count; i++) {
hw->gpio_cs[i] = of_get_gpio_flags(np, i, NULL);
if (hw->gpio_cs[i] < 0)
return -ENODEV;
}
hw->bitbang.master->dev.of_node = pdev->dev.of_node;
val = of_get_property(pdev->dev.of_node,
"clock-frequency", &len);
if (val && len >= sizeof(__be32))
hw->freq = be32_to_cpup(val);
val = of_get_property(pdev->dev.of_node, "baud-width", &len);
if (val && len >= sizeof(__be32))
hw->baudwidth = be32_to_cpup(val);
return 0;
}
#else /* !CONFIG_OF */
static int __devinit tiny_spi_of_probe(struct platform_device *pdev)
{
return 0;
}
#endif /* CONFIG_OF */
static int __devinit tiny_spi_probe(struct platform_device *pdev)
{
struct tiny_spi_platform_data *platp = pdev->dev.platform_data;
struct tiny_spi *hw;
struct spi_master *master;
struct resource *res;
unsigned int i;
int err = -ENODEV;
master = spi_alloc_master(&pdev->dev, sizeof(struct tiny_spi));
if (!master)
return err;
/* setup the master state. */
master->bus_num = pdev->id;
master->num_chipselect = 255;
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
master->setup = tiny_spi_setup;
hw = spi_master_get_devdata(master);
platform_set_drvdata(pdev, hw);
/* setup the state for the bitbang driver */
hw->bitbang.master = spi_master_get(master);
if (!hw->bitbang.master)
return err;
hw->bitbang.setup_transfer = tiny_spi_setup_transfer;
hw->bitbang.chipselect = tiny_spi_chipselect;
hw->bitbang.txrx_bufs = tiny_spi_txrx_bufs;
/* find and map our resources */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
goto exit_busy;
if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
pdev->name))
goto exit_busy;
hw->base = devm_ioremap_nocache(&pdev->dev, res->start,
resource_size(res));
if (!hw->base)
goto exit_busy;
/* irq is optional */
hw->irq = platform_get_irq(pdev, 0);
if (hw->irq >= 0) {
init_completion(&hw->done);
err = devm_request_irq(&pdev->dev, hw->irq, tiny_spi_irq, 0,
pdev->name, hw);
if (err)
goto exit;
}
/* find platform data */
if (platp) {
hw->gpio_cs_count = platp->gpio_cs_count;
hw->gpio_cs = platp->gpio_cs;
if (platp->gpio_cs_count && !platp->gpio_cs)
goto exit_busy;
hw->freq = platp->freq;
hw->baudwidth = platp->baudwidth;
} else {
err = tiny_spi_of_probe(pdev);
if (err)
goto exit;
}
for (i = 0; i < hw->gpio_cs_count; i++) {
err = gpio_request(hw->gpio_cs[i], dev_name(&pdev->dev));
if (err)
goto exit_gpio;
gpio_direction_output(hw->gpio_cs[i], 1);
}
hw->bitbang.master->num_chipselect = max(1U, hw->gpio_cs_count);
/* register our spi controller */
err = spi_bitbang_start(&hw->bitbang);
if (err)
goto exit;
dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq);
return 0;
exit_gpio:
while (i-- > 0)
gpio_free(hw->gpio_cs[i]);
exit_busy:
err = -EBUSY;
exit:
platform_set_drvdata(pdev, NULL);
spi_master_put(master);
return err;
}
static int __devexit tiny_spi_remove(struct platform_device *pdev)
{
struct tiny_spi *hw = platform_get_drvdata(pdev);
struct spi_master *master = hw->bitbang.master;
unsigned int i;
spi_bitbang_stop(&hw->bitbang);
for (i = 0; i < hw->gpio_cs_count; i++)
gpio_free(hw->gpio_cs[i]);
platform_set_drvdata(pdev, NULL);
spi_master_put(master);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id tiny_spi_match[] = {
{ .compatible = "opencores,tiny-spi-rtlsvn2", },
{},
};
MODULE_DEVICE_TABLE(of, tiny_spi_match);
#else /* CONFIG_OF */
#define tiny_spi_match NULL
#endif /* CONFIG_OF */
static struct platform_driver tiny_spi_driver = {
.probe = tiny_spi_probe,
.remove = __devexit_p(tiny_spi_remove),
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.pm = NULL,
.of_match_table = tiny_spi_match,
},
};
static int __init tiny_spi_init(void)
{
return platform_driver_register(&tiny_spi_driver);
}
module_init(tiny_spi_init);
static void __exit tiny_spi_exit(void)
{
platform_driver_unregister(&tiny_spi_driver);
}
module_exit(tiny_spi_exit);
MODULE_DESCRIPTION("OpenCores tiny SPI driver");
MODULE_AUTHOR("Thomas Chou <[email protected]>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);

543
drivers/spi/spi_sh.c Normal file
View file

@ -0,0 +1,543 @@
/*
* SH SPI bus driver
*
* Copyright (C) 2011 Renesas Solutions Corp.
*
* Based on pxa2xx_spi.c:
* Copyright (C) 2005 Stephen Street / StreetFire Sound Labs
*
* 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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/list.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/spi/spi.h>
#define SPI_SH_TBR 0x00
#define SPI_SH_RBR 0x00
#define SPI_SH_CR1 0x08
#define SPI_SH_CR2 0x10
#define SPI_SH_CR3 0x18
#define SPI_SH_CR4 0x20
#define SPI_SH_CR5 0x28
/* CR1 */
#define SPI_SH_TBE 0x80
#define SPI_SH_TBF 0x40
#define SPI_SH_RBE 0x20
#define SPI_SH_RBF 0x10
#define SPI_SH_PFONRD 0x08
#define SPI_SH_SSDB 0x04
#define SPI_SH_SSD 0x02
#define SPI_SH_SSA 0x01
/* CR2 */
#define SPI_SH_RSTF 0x80
#define SPI_SH_LOOPBK 0x40
#define SPI_SH_CPOL 0x20
#define SPI_SH_CPHA 0x10
#define SPI_SH_L1M0 0x08
/* CR3 */
#define SPI_SH_MAX_BYTE 0xFF
/* CR4 */
#define SPI_SH_TBEI 0x80
#define SPI_SH_TBFI 0x40
#define SPI_SH_RBEI 0x20
#define SPI_SH_RBFI 0x10
#define SPI_SH_WPABRT 0x04
#define SPI_SH_SSS 0x01
/* CR8 */
#define SPI_SH_P1L0 0x80
#define SPI_SH_PP1L0 0x40
#define SPI_SH_MUXI 0x20
#define SPI_SH_MUXIRQ 0x10
#define SPI_SH_FIFO_SIZE 32
#define SPI_SH_SEND_TIMEOUT (3 * HZ)
#define SPI_SH_RECEIVE_TIMEOUT (HZ >> 3)
#undef DEBUG
struct spi_sh_data {
void __iomem *addr;
int irq;
struct spi_master *master;
struct list_head queue;
struct workqueue_struct *workqueue;
struct work_struct ws;
unsigned long cr1;
wait_queue_head_t wait;
spinlock_t lock;
};
static void spi_sh_write(struct spi_sh_data *ss, unsigned long data,
unsigned long offset)
{
writel(data, ss->addr + offset);
}
static unsigned long spi_sh_read(struct spi_sh_data *ss, unsigned long offset)
{
return readl(ss->addr + offset);
}
static void spi_sh_set_bit(struct spi_sh_data *ss, unsigned long val,
unsigned long offset)
{
unsigned long tmp;
tmp = spi_sh_read(ss, offset);
tmp |= val;
spi_sh_write(ss, tmp, offset);
}
static void spi_sh_clear_bit(struct spi_sh_data *ss, unsigned long val,
unsigned long offset)
{
unsigned long tmp;
tmp = spi_sh_read(ss, offset);
tmp &= ~val;
spi_sh_write(ss, tmp, offset);
}
static void clear_fifo(struct spi_sh_data *ss)
{
spi_sh_set_bit(ss, SPI_SH_RSTF, SPI_SH_CR2);
spi_sh_clear_bit(ss, SPI_SH_RSTF, SPI_SH_CR2);
}
static int spi_sh_wait_receive_buffer(struct spi_sh_data *ss)
{
int timeout = 100000;
while (spi_sh_read(ss, SPI_SH_CR1) & SPI_SH_RBE) {
udelay(10);
if (timeout-- < 0)
return -ETIMEDOUT;
}
return 0;
}
static int spi_sh_wait_write_buffer_empty(struct spi_sh_data *ss)
{
int timeout = 100000;
while (!(spi_sh_read(ss, SPI_SH_CR1) & SPI_SH_TBE)) {
udelay(10);
if (timeout-- < 0)
return -ETIMEDOUT;
}
return 0;
}
static int spi_sh_send(struct spi_sh_data *ss, struct spi_message *mesg,
struct spi_transfer *t)
{
int i, retval = 0;
int remain = t->len;
int cur_len;
unsigned char *data;
unsigned long tmp;
long ret;
if (t->len)
spi_sh_set_bit(ss, SPI_SH_SSA, SPI_SH_CR1);
data = (unsigned char *)t->tx_buf;
while (remain > 0) {
cur_len = min(SPI_SH_FIFO_SIZE, remain);
for (i = 0; i < cur_len &&
!(spi_sh_read(ss, SPI_SH_CR4) &
SPI_SH_WPABRT) &&
!(spi_sh_read(ss, SPI_SH_CR1) & SPI_SH_TBF);
i++)
spi_sh_write(ss, (unsigned long)data[i], SPI_SH_TBR);
if (spi_sh_read(ss, SPI_SH_CR4) & SPI_SH_WPABRT) {
/* Abort SPI operation */
spi_sh_set_bit(ss, SPI_SH_WPABRT, SPI_SH_CR4);
retval = -EIO;
break;
}
cur_len = i;
remain -= cur_len;
data += cur_len;
if (remain > 0) {
ss->cr1 &= ~SPI_SH_TBE;
spi_sh_set_bit(ss, SPI_SH_TBE, SPI_SH_CR4);
ret = wait_event_interruptible_timeout(ss->wait,
ss->cr1 & SPI_SH_TBE,
SPI_SH_SEND_TIMEOUT);
if (ret == 0 && !(ss->cr1 & SPI_SH_TBE)) {
printk(KERN_ERR "%s: timeout\n", __func__);
return -ETIMEDOUT;
}
}
}
if (list_is_last(&t->transfer_list, &mesg->transfers)) {
tmp = spi_sh_read(ss, SPI_SH_CR1);
tmp = tmp & ~(SPI_SH_SSD | SPI_SH_SSDB);
spi_sh_write(ss, tmp, SPI_SH_CR1);
spi_sh_set_bit(ss, SPI_SH_SSA, SPI_SH_CR1);
ss->cr1 &= ~SPI_SH_TBE;
spi_sh_set_bit(ss, SPI_SH_TBE, SPI_SH_CR4);
ret = wait_event_interruptible_timeout(ss->wait,
ss->cr1 & SPI_SH_TBE,
SPI_SH_SEND_TIMEOUT);
if (ret == 0 && (ss->cr1 & SPI_SH_TBE)) {
printk(KERN_ERR "%s: timeout\n", __func__);
return -ETIMEDOUT;
}
}
return retval;
}
static int spi_sh_receive(struct spi_sh_data *ss, struct spi_message *mesg,
struct spi_transfer *t)
{
int i;
int remain = t->len;
int cur_len;
unsigned char *data;
unsigned long tmp;
long ret;
if (t->len > SPI_SH_MAX_BYTE)
spi_sh_write(ss, SPI_SH_MAX_BYTE, SPI_SH_CR3);
else
spi_sh_write(ss, t->len, SPI_SH_CR3);
tmp = spi_sh_read(ss, SPI_SH_CR1);
tmp = tmp & ~(SPI_SH_SSD | SPI_SH_SSDB);
spi_sh_write(ss, tmp, SPI_SH_CR1);
spi_sh_set_bit(ss, SPI_SH_SSA, SPI_SH_CR1);
spi_sh_wait_write_buffer_empty(ss);
data = (unsigned char *)t->rx_buf;
while (remain > 0) {
if (remain >= SPI_SH_FIFO_SIZE) {
ss->cr1 &= ~SPI_SH_RBF;
spi_sh_set_bit(ss, SPI_SH_RBF, SPI_SH_CR4);
ret = wait_event_interruptible_timeout(ss->wait,
ss->cr1 & SPI_SH_RBF,
SPI_SH_RECEIVE_TIMEOUT);
if (ret == 0 &&
spi_sh_read(ss, SPI_SH_CR1) & SPI_SH_RBE) {
printk(KERN_ERR "%s: timeout\n", __func__);
return -ETIMEDOUT;
}
}
cur_len = min(SPI_SH_FIFO_SIZE, remain);
for (i = 0; i < cur_len; i++) {
if (spi_sh_wait_receive_buffer(ss))
break;
data[i] = (unsigned char)spi_sh_read(ss, SPI_SH_RBR);
}
remain -= cur_len;
data += cur_len;
}
/* deassert CS when SPI is receiving. */
if (t->len > SPI_SH_MAX_BYTE) {
clear_fifo(ss);
spi_sh_write(ss, 1, SPI_SH_CR3);
} else {
spi_sh_write(ss, 0, SPI_SH_CR3);
}
return 0;
}
static void spi_sh_work(struct work_struct *work)
{
struct spi_sh_data *ss = container_of(work, struct spi_sh_data, ws);
struct spi_message *mesg;
struct spi_transfer *t;
unsigned long flags;
int ret;
pr_debug("%s: enter\n", __func__);
spin_lock_irqsave(&ss->lock, flags);
while (!list_empty(&ss->queue)) {
mesg = list_entry(ss->queue.next, struct spi_message, queue);
list_del_init(&mesg->queue);
spin_unlock_irqrestore(&ss->lock, flags);
list_for_each_entry(t, &mesg->transfers, transfer_list) {
pr_debug("tx_buf = %p, rx_buf = %p\n",
t->tx_buf, t->rx_buf);
pr_debug("len = %d, delay_usecs = %d\n",
t->len, t->delay_usecs);
if (t->tx_buf) {
ret = spi_sh_send(ss, mesg, t);
if (ret < 0)
goto error;
}
if (t->rx_buf) {
ret = spi_sh_receive(ss, mesg, t);
if (ret < 0)
goto error;
}
mesg->actual_length += t->len;
}
spin_lock_irqsave(&ss->lock, flags);
mesg->status = 0;
mesg->complete(mesg->context);
}
clear_fifo(ss);
spi_sh_set_bit(ss, SPI_SH_SSD, SPI_SH_CR1);
udelay(100);
spi_sh_clear_bit(ss, SPI_SH_SSA | SPI_SH_SSDB | SPI_SH_SSD,
SPI_SH_CR1);
clear_fifo(ss);
spin_unlock_irqrestore(&ss->lock, flags);
return;
error:
mesg->status = ret;
mesg->complete(mesg->context);
spi_sh_clear_bit(ss, SPI_SH_SSA | SPI_SH_SSDB | SPI_SH_SSD,
SPI_SH_CR1);
clear_fifo(ss);
}
static int spi_sh_setup(struct spi_device *spi)
{
struct spi_sh_data *ss = spi_master_get_devdata(spi->master);
if (!spi->bits_per_word)
spi->bits_per_word = 8;
pr_debug("%s: enter\n", __func__);
spi_sh_write(ss, 0xfe, SPI_SH_CR1); /* SPI sycle stop */
spi_sh_write(ss, 0x00, SPI_SH_CR1); /* CR1 init */
spi_sh_write(ss, 0x00, SPI_SH_CR3); /* CR3 init */
clear_fifo(ss);
/* 1/8 clock */
spi_sh_write(ss, spi_sh_read(ss, SPI_SH_CR2) | 0x07, SPI_SH_CR2);
udelay(10);
return 0;
}
static int spi_sh_transfer(struct spi_device *spi, struct spi_message *mesg)
{
struct spi_sh_data *ss = spi_master_get_devdata(spi->master);
unsigned long flags;
pr_debug("%s: enter\n", __func__);
pr_debug("\tmode = %02x\n", spi->mode);
spin_lock_irqsave(&ss->lock, flags);
mesg->actual_length = 0;
mesg->status = -EINPROGRESS;
spi_sh_clear_bit(ss, SPI_SH_SSA, SPI_SH_CR1);
list_add_tail(&mesg->queue, &ss->queue);
queue_work(ss->workqueue, &ss->ws);
spin_unlock_irqrestore(&ss->lock, flags);
return 0;
}
static void spi_sh_cleanup(struct spi_device *spi)
{
struct spi_sh_data *ss = spi_master_get_devdata(spi->master);
pr_debug("%s: enter\n", __func__);
spi_sh_clear_bit(ss, SPI_SH_SSA | SPI_SH_SSDB | SPI_SH_SSD,
SPI_SH_CR1);
}
static irqreturn_t spi_sh_irq(int irq, void *_ss)
{
struct spi_sh_data *ss = (struct spi_sh_data *)_ss;
unsigned long cr1;
cr1 = spi_sh_read(ss, SPI_SH_CR1);
if (cr1 & SPI_SH_TBE)
ss->cr1 |= SPI_SH_TBE;
if (cr1 & SPI_SH_TBF)
ss->cr1 |= SPI_SH_TBF;
if (cr1 & SPI_SH_RBE)
ss->cr1 |= SPI_SH_RBE;
if (cr1 & SPI_SH_RBF)
ss->cr1 |= SPI_SH_RBF;
if (ss->cr1) {
spi_sh_clear_bit(ss, ss->cr1, SPI_SH_CR4);
wake_up(&ss->wait);
}
return IRQ_HANDLED;
}
static int __devexit spi_sh_remove(struct platform_device *pdev)
{
struct spi_sh_data *ss = dev_get_drvdata(&pdev->dev);
destroy_workqueue(ss->workqueue);
free_irq(ss->irq, ss);
iounmap(ss->addr);
spi_master_put(ss->master);
return 0;
}
static int __devinit spi_sh_probe(struct platform_device *pdev)
{
struct resource *res;
struct spi_master *master;
struct spi_sh_data *ss;
int ret, irq;
/* get base addr */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (unlikely(res == NULL)) {
dev_err(&pdev->dev, "invalid resource\n");
return -EINVAL;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "platform_get_irq error\n");
return -ENODEV;
}
master = spi_alloc_master(&pdev->dev, sizeof(struct spi_sh_data));
if (master == NULL) {
dev_err(&pdev->dev, "spi_alloc_master error.\n");
return -ENOMEM;
}
ss = spi_master_get_devdata(master);
dev_set_drvdata(&pdev->dev, ss);
ss->irq = irq;
ss->master = master;
ss->addr = ioremap(res->start, resource_size(res));
if (ss->addr == NULL) {
dev_err(&pdev->dev, "ioremap error.\n");
ret = -ENOMEM;
goto error1;
}
INIT_LIST_HEAD(&ss->queue);
spin_lock_init(&ss->lock);
INIT_WORK(&ss->ws, spi_sh_work);
init_waitqueue_head(&ss->wait);
ss->workqueue = create_singlethread_workqueue(
dev_name(master->dev.parent));
if (ss->workqueue == NULL) {
dev_err(&pdev->dev, "create workqueue error\n");
ret = -EBUSY;
goto error2;
}
ret = request_irq(irq, spi_sh_irq, IRQF_DISABLED, "spi_sh", ss);
if (ret < 0) {
dev_err(&pdev->dev, "request_irq error\n");
goto error3;
}
master->num_chipselect = 2;
master->bus_num = pdev->id;
master->setup = spi_sh_setup;
master->transfer = spi_sh_transfer;
master->cleanup = spi_sh_cleanup;
ret = spi_register_master(master);
if (ret < 0) {
printk(KERN_ERR "spi_register_master error.\n");
goto error4;
}
return 0;
error4:
free_irq(irq, ss);
error3:
destroy_workqueue(ss->workqueue);
error2:
iounmap(ss->addr);
error1:
spi_master_put(master);
return ret;
}
static struct platform_driver spi_sh_driver = {
.probe = spi_sh_probe,
.remove = __devexit_p(spi_sh_remove),
.driver = {
.name = "sh_spi",
.owner = THIS_MODULE,
},
};
static int __init spi_sh_init(void)
{
return platform_driver_register(&spi_sh_driver);
}
module_init(spi_sh_init);
static void __exit spi_sh_exit(void)
{
platform_driver_unregister(&spi_sh_driver);
}
module_exit(spi_sh_exit);
MODULE_DESCRIPTION("SH SPI bus driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yoshihiro Shimoda");
MODULE_ALIAS("platform:sh_spi");

View file

@ -9,22 +9,22 @@
*
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/completion.h>
#include <linux/pm_runtime.h>
#include <linux/gpio.h>
#include <linux/bitmap.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/spi/sh_msiof.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/spi/sh_msiof.h>
#include <asm/unaligned.h>
@ -67,7 +67,7 @@ struct sh_msiof_spi_priv {
#define STR_TEOF (1 << 23)
#define STR_REOF (1 << 7)
static unsigned long sh_msiof_read(struct sh_msiof_spi_priv *p, int reg_offs)
static u32 sh_msiof_read(struct sh_msiof_spi_priv *p, int reg_offs)
{
switch (reg_offs) {
case TSCR:
@ -79,7 +79,7 @@ static unsigned long sh_msiof_read(struct sh_msiof_spi_priv *p, int reg_offs)
}
static void sh_msiof_write(struct sh_msiof_spi_priv *p, int reg_offs,
unsigned long value)
u32 value)
{
switch (reg_offs) {
case TSCR:
@ -93,10 +93,10 @@ static void sh_msiof_write(struct sh_msiof_spi_priv *p, int reg_offs,
}
static int sh_msiof_modify_ctr_wait(struct sh_msiof_spi_priv *p,
unsigned long clr, unsigned long set)
u32 clr, u32 set)
{
unsigned long mask = clr | set;
unsigned long data;
u32 mask = clr | set;
u32 data;
int k;
data = sh_msiof_read(p, CTR);
@ -166,10 +166,10 @@ static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p,
}
static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p,
int cpol, int cpha,
int tx_hi_z, int lsb_first)
u32 cpol, u32 cpha,
u32 tx_hi_z, u32 lsb_first)
{
unsigned long tmp;
u32 tmp;
int edge;
/*
@ -187,7 +187,7 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p,
tmp |= cpol << 30; /* TSCKIZ */
tmp |= cpol << 28; /* RSCKIZ */
edge = cpol ? cpha : !cpha;
edge = cpol ^ !cpha;
tmp |= edge << 27; /* TEDG */
tmp |= edge << 26; /* REDG */
@ -197,11 +197,9 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p,
static void sh_msiof_spi_set_mode_regs(struct sh_msiof_spi_priv *p,
const void *tx_buf, void *rx_buf,
int bits, int words)
u32 bits, u32 words)
{
unsigned long dr2;
dr2 = ((bits - 1) << 24) | ((words - 1) << 16);
u32 dr2 = ((bits - 1) << 24) | ((words - 1) << 16);
if (tx_buf)
sh_msiof_write(p, TMDR2, dr2);
@ -222,7 +220,7 @@ static void sh_msiof_reset_str(struct sh_msiof_spi_priv *p)
static void sh_msiof_spi_write_fifo_8(struct sh_msiof_spi_priv *p,
const void *tx_buf, int words, int fs)
{
const unsigned char *buf_8 = tx_buf;
const u8 *buf_8 = tx_buf;
int k;
for (k = 0; k < words; k++)
@ -232,7 +230,7 @@ static void sh_msiof_spi_write_fifo_8(struct sh_msiof_spi_priv *p,
static void sh_msiof_spi_write_fifo_16(struct sh_msiof_spi_priv *p,
const void *tx_buf, int words, int fs)
{
const unsigned short *buf_16 = tx_buf;
const u16 *buf_16 = tx_buf;
int k;
for (k = 0; k < words; k++)
@ -242,7 +240,7 @@ static void sh_msiof_spi_write_fifo_16(struct sh_msiof_spi_priv *p,
static void sh_msiof_spi_write_fifo_16u(struct sh_msiof_spi_priv *p,
const void *tx_buf, int words, int fs)
{
const unsigned short *buf_16 = tx_buf;
const u16 *buf_16 = tx_buf;
int k;
for (k = 0; k < words; k++)
@ -252,7 +250,7 @@ static void sh_msiof_spi_write_fifo_16u(struct sh_msiof_spi_priv *p,
static void sh_msiof_spi_write_fifo_32(struct sh_msiof_spi_priv *p,
const void *tx_buf, int words, int fs)
{
const unsigned int *buf_32 = tx_buf;
const u32 *buf_32 = tx_buf;
int k;
for (k = 0; k < words; k++)
@ -262,17 +260,37 @@ static void sh_msiof_spi_write_fifo_32(struct sh_msiof_spi_priv *p,
static void sh_msiof_spi_write_fifo_32u(struct sh_msiof_spi_priv *p,
const void *tx_buf, int words, int fs)
{
const unsigned int *buf_32 = tx_buf;
const u32 *buf_32 = tx_buf;
int k;
for (k = 0; k < words; k++)
sh_msiof_write(p, TFDR, get_unaligned(&buf_32[k]) << fs);
}
static void sh_msiof_spi_write_fifo_s32(struct sh_msiof_spi_priv *p,
const void *tx_buf, int words, int fs)
{
const u32 *buf_32 = tx_buf;
int k;
for (k = 0; k < words; k++)
sh_msiof_write(p, TFDR, swab32(buf_32[k] << fs));
}
static void sh_msiof_spi_write_fifo_s32u(struct sh_msiof_spi_priv *p,
const void *tx_buf, int words, int fs)
{
const u32 *buf_32 = tx_buf;
int k;
for (k = 0; k < words; k++)
sh_msiof_write(p, TFDR, swab32(get_unaligned(&buf_32[k]) << fs));
}
static void sh_msiof_spi_read_fifo_8(struct sh_msiof_spi_priv *p,
void *rx_buf, int words, int fs)
{
unsigned char *buf_8 = rx_buf;
u8 *buf_8 = rx_buf;
int k;
for (k = 0; k < words; k++)
@ -282,7 +300,7 @@ static void sh_msiof_spi_read_fifo_8(struct sh_msiof_spi_priv *p,
static void sh_msiof_spi_read_fifo_16(struct sh_msiof_spi_priv *p,
void *rx_buf, int words, int fs)
{
unsigned short *buf_16 = rx_buf;
u16 *buf_16 = rx_buf;
int k;
for (k = 0; k < words; k++)
@ -292,7 +310,7 @@ static void sh_msiof_spi_read_fifo_16(struct sh_msiof_spi_priv *p,
static void sh_msiof_spi_read_fifo_16u(struct sh_msiof_spi_priv *p,
void *rx_buf, int words, int fs)
{
unsigned short *buf_16 = rx_buf;
u16 *buf_16 = rx_buf;
int k;
for (k = 0; k < words; k++)
@ -302,7 +320,7 @@ static void sh_msiof_spi_read_fifo_16u(struct sh_msiof_spi_priv *p,
static void sh_msiof_spi_read_fifo_32(struct sh_msiof_spi_priv *p,
void *rx_buf, int words, int fs)
{
unsigned int *buf_32 = rx_buf;
u32 *buf_32 = rx_buf;
int k;
for (k = 0; k < words; k++)
@ -312,19 +330,40 @@ static void sh_msiof_spi_read_fifo_32(struct sh_msiof_spi_priv *p,
static void sh_msiof_spi_read_fifo_32u(struct sh_msiof_spi_priv *p,
void *rx_buf, int words, int fs)
{
unsigned int *buf_32 = rx_buf;
u32 *buf_32 = rx_buf;
int k;
for (k = 0; k < words; k++)
put_unaligned(sh_msiof_read(p, RFDR) >> fs, &buf_32[k]);
}
static void sh_msiof_spi_read_fifo_s32(struct sh_msiof_spi_priv *p,
void *rx_buf, int words, int fs)
{
u32 *buf_32 = rx_buf;
int k;
for (k = 0; k < words; k++)
buf_32[k] = swab32(sh_msiof_read(p, RFDR) >> fs);
}
static void sh_msiof_spi_read_fifo_s32u(struct sh_msiof_spi_priv *p,
void *rx_buf, int words, int fs)
{
u32 *buf_32 = rx_buf;
int k;
for (k = 0; k < words; k++)
put_unaligned(swab32(sh_msiof_read(p, RFDR) >> fs), &buf_32[k]);
}
static int sh_msiof_spi_bits(struct spi_device *spi, struct spi_transfer *t)
{
int bits;
bits = t ? t->bits_per_word : 0;
bits = bits ? bits : spi->bits_per_word;
if (!bits)
bits = spi->bits_per_word;
return bits;
}
@ -334,7 +373,8 @@ static unsigned long sh_msiof_spi_hz(struct spi_device *spi,
unsigned long hz;
hz = t ? t->speed_hz : 0;
hz = hz ? hz : spi->max_speed_hz;
if (!hz)
hz = spi->max_speed_hz;
return hz;
}
@ -468,9 +508,17 @@ static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
int bytes_done;
int words;
int n;
bool swab;
bits = sh_msiof_spi_bits(spi, t);
if (bits <= 8 && t->len > 15 && !(t->len & 3)) {
bits = 32;
swab = true;
} else {
swab = false;
}
/* setup bytes per word and fifo read/write functions */
if (bits <= 8) {
bytes_per_word = 1;
@ -487,6 +535,17 @@ static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
rx_fifo = sh_msiof_spi_read_fifo_16u;
else
rx_fifo = sh_msiof_spi_read_fifo_16;
} else if (swab) {
bytes_per_word = 4;
if ((unsigned long)t->tx_buf & 0x03)
tx_fifo = sh_msiof_spi_write_fifo_s32u;
else
tx_fifo = sh_msiof_spi_write_fifo_s32;
if ((unsigned long)t->rx_buf & 0x03)
rx_fifo = sh_msiof_spi_read_fifo_s32u;
else
rx_fifo = sh_msiof_spi_read_fifo_s32;
} else {
bytes_per_word = 4;
if ((unsigned long)t->tx_buf & 0x03)

View file

@ -30,6 +30,7 @@
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/compat.h>
#include <linux/spi/spi.h>
#include <linux/spi/spidev.h>
@ -471,6 +472,16 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return retval;
}
#ifdef CONFIG_COMPAT
static long
spidev_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
return spidev_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
}
#else
#define spidev_compat_ioctl NULL
#endif /* CONFIG_COMPAT */
static int spidev_open(struct inode *inode, struct file *filp)
{
struct spidev_data *spidev;
@ -543,6 +554,7 @@ static const struct file_operations spidev_fops = {
.write = spidev_write,
.read = spidev_read,
.unlocked_ioctl = spidev_ioctl,
.compat_ioctl = spidev_compat_ioctl,
.open = spidev_open,
.release = spidev_release,
.llseek = no_llseek,