Commit 5f0e685f authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'spi-for-linus' of git://git.secretlab.ca/git/linux-2.6

Pull SPI changes for v3.4 from Grant Likely:
 "Mostly a bunch of new drivers and driver bug fixes; but this also
  includes a few patches that create a core message queue infrastructure
  for the spi subsystem instead of making each driver open code it."

* tag 'spi-for-linus' of git://git.secretlab.ca/git/linux-2.6: (34 commits)
  spi/fsl-espi: Make sure pm is within 2..32
  spi/fsl-espi: make the clock computation easier to read
  spi: sh-hspi: modify write/read method
  spi: sh-hspi: control spi clock more correctly
  spi: sh-hspi: convert to using core message queue
  spi: s3c64xx: Fix build
  spi: s3c64xx: remove unnecessary callback msg->complete
  spi: remove redundant variable assignment
  spi: release lock on error path in spi_pump_messages()
  spi: Compatibility with direction which is used in samsung DMA operation
  spi-topcliff-pch: add recovery processing in case wait-event timeout
  spi-topcliff-pch: supports a spi mode setup and bit order setup by IO control
  spi-topcliff-pch: Fix issue for transmitting over 4KByte
  spi-topcliff-pch: Modify pci-bus number dynamically to get DMA device info
  spi/imx: simplify error handling to free gpios
  spi: Convert to DEFINE_PCI_DEVICE_TABLE
  spi: add Broadcom BCM63xx SPI controller driver
  SPI: add CSR SiRFprimaII SPI controller driver
  spi-topcliff-pch: fix -Wuninitialized warning
  spi: Mark spi_register_board_info() __devinit
  ...
parents f8974cb7 87bf5ab8
OMAP2+ McSPI device
Required properties:
- compatible :
- "ti,omap2-spi" for OMAP2 & OMAP3.
- "ti,omap4-spi" for OMAP4+.
- ti,spi-num-cs : Number of chipselect supported by the instance.
- ti,hwmods: Name of the hwmod associated to the McSPI
Example:
mcspi1: mcspi@1 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "ti,omap4-mcspi";
ti,hwmods = "mcspi1";
ti,spi-num-cs = <4>;
};
Overview of Linux kernel SPI support Overview of Linux kernel SPI support
==================================== ====================================
21-May-2007 02-Feb-2012
What is SPI? What is SPI?
------------ ------------
...@@ -483,9 +483,9 @@ also initialize its own internal state. (See below about bus numbering ...@@ -483,9 +483,9 @@ also initialize its own internal state. (See below about bus numbering
and those methods.) and those methods.)
After you initialize the spi_master, then use spi_register_master() to After you initialize the spi_master, then use spi_register_master() to
publish it to the rest of the system. At that time, device nodes for publish it to the rest of the system. At that time, device nodes for the
the controller and any predeclared spi devices will be made available, controller and any predeclared spi devices will be made available, and
and the driver model core will take care of binding them to drivers. the driver model core will take care of binding them to drivers.
If you need to remove your SPI controller driver, spi_unregister_master() If you need to remove your SPI controller driver, spi_unregister_master()
will reverse the effect of spi_register_master(). will reverse the effect of spi_register_master().
...@@ -521,21 +521,53 @@ SPI MASTER METHODS ...@@ -521,21 +521,53 @@ SPI MASTER METHODS
** When you code setup(), ASSUME that the controller ** When you code setup(), ASSUME that the controller
** is actively processing transfers for another device. ** is actively processing transfers for another device.
master->transfer(struct spi_device *spi, struct spi_message *message)
This must not sleep. Its responsibility is arrange that the
transfer happens and its complete() callback is issued. The two
will normally happen later, after other transfers complete, and
if the controller is idle it will need to be kickstarted.
master->cleanup(struct spi_device *spi) master->cleanup(struct spi_device *spi)
Your controller driver may use spi_device.controller_state to hold Your controller driver may use spi_device.controller_state to hold
state it dynamically associates with that device. If you do that, state it dynamically associates with that device. If you do that,
be sure to provide the cleanup() method to free that state. be sure to provide the cleanup() method to free that state.
master->prepare_transfer_hardware(struct spi_master *master)
This will be called by the queue mechanism to signal to the driver
that a message is coming in soon, so the subsystem requests the
driver to prepare the transfer hardware by issuing this call.
This may sleep.
master->unprepare_transfer_hardware(struct spi_master *master)
This will be called by the queue mechanism to signal to the driver
that there are no more messages pending in the queue and it may
relax the hardware (e.g. by power management calls). This may sleep.
master->transfer_one_message(struct spi_master *master,
struct spi_message *mesg)
The subsystem calls the driver to transfer a single message while
queuing transfers that arrive in the meantime. When the driver is
finished with this message, it must call
spi_finalize_current_message() so the subsystem can issue the next
transfer. This may sleep.
DEPRECATED METHODS
master->transfer(struct spi_device *spi, struct spi_message *message)
This must not sleep. Its responsibility is arrange that the
transfer happens and its complete() callback is issued. The two
will normally happen later, after other transfers complete, and
if the controller is idle it will need to be kickstarted. This
method is not used on queued controllers and must be NULL if
transfer_one_message() and (un)prepare_transfer_hardware() are
implemented.
SPI MESSAGE QUEUE SPI MESSAGE QUEUE
The bulk of the driver will be managing the I/O queue fed by transfer(). If you are happy with the standard queueing mechanism provided by the
SPI subsystem, just implement the queued methods specified above. Using
the message queue has the upside of centralizing a lot of code and
providing pure process-context execution of methods. The message queue
can also be elevated to realtime priority on high-priority SPI traffic.
Unless the queueing mechanism in the SPI subsystem is selected, the bulk
of the driver will be managing the I/O queue fed by the now deprecated
function transfer().
That queue could be purely conceptual. For example, a driver used only That queue could be purely conceptual. For example, a driver used only
for low-frequency sensor access might be fine using synchronous PIO. for low-frequency sensor access might be fine using synchronous PIO.
...@@ -561,4 +593,6 @@ Stephen Street ...@@ -561,4 +593,6 @@ Stephen Street
Mark Underwood Mark Underwood
Andrew Victor Andrew Victor
Vitaly Wool Vitaly Wool
Grant Likely
Mark Brown
Linus Walleij
...@@ -94,6 +94,12 @@ config SPI_AU1550 ...@@ -94,6 +94,12 @@ config SPI_AU1550
If you say yes to this option, support will be included for the If you say yes to this option, support will be included for the
PSC SPI controller found on Au1550, Au1200 and Au1300 series. PSC SPI controller found on Au1550, Au1200 and Au1300 series.
config SPI_BCM63XX
tristate "Broadcom BCM63xx SPI controller"
depends on BCM63XX
help
Enable support for the SPI controller on the Broadcom BCM63xx SoCs.
config SPI_BITBANG config SPI_BITBANG
tristate "Utilities for Bitbanging SPI masters" tristate "Utilities for Bitbanging SPI masters"
help help
...@@ -126,7 +132,7 @@ config SPI_COLDFIRE_QSPI ...@@ -126,7 +132,7 @@ config SPI_COLDFIRE_QSPI
config SPI_DAVINCI config SPI_DAVINCI
tristate "Texas Instruments DaVinci/DA8x/OMAP-L/AM1x SoC SPI controller" tristate "Texas Instruments DaVinci/DA8x/OMAP-L/AM1x SoC SPI controller"
depends on SPI_MASTER && ARCH_DAVINCI depends on ARCH_DAVINCI
select SPI_BITBANG select SPI_BITBANG
help help
SPI master controller for DaVinci/DA8x/OMAP-L/AM1x SPI modules. SPI master controller for DaVinci/DA8x/OMAP-L/AM1x SPI modules.
...@@ -188,7 +194,7 @@ config SPI_MPC52xx_PSC ...@@ -188,7 +194,7 @@ config SPI_MPC52xx_PSC
config SPI_MPC512x_PSC config SPI_MPC512x_PSC
tristate "Freescale MPC512x PSC SPI controller" tristate "Freescale MPC512x PSC SPI controller"
depends on SPI_MASTER && PPC_MPC512x depends on PPC_MPC512x
help help
This enables using the Freescale MPC5121 Programmable Serial This enables using the Freescale MPC5121 Programmable Serial
Controller in SPI master mode. Controller in SPI master mode.
...@@ -238,7 +244,7 @@ config SPI_OMAP24XX ...@@ -238,7 +244,7 @@ config SPI_OMAP24XX
config SPI_OMAP_100K config SPI_OMAP_100K
tristate "OMAP SPI 100K" tristate "OMAP SPI 100K"
depends on SPI_MASTER && (ARCH_OMAP850 || ARCH_OMAP730) depends on ARCH_OMAP850 || ARCH_OMAP730
help help
OMAP SPI 100K master controller for omap7xx boards. OMAP SPI 100K master controller for omap7xx boards.
...@@ -262,7 +268,7 @@ config SPI_PL022 ...@@ -262,7 +268,7 @@ config SPI_PL022
config SPI_PPC4xx config SPI_PPC4xx
tristate "PPC4xx SPI Controller" tristate "PPC4xx SPI Controller"
depends on PPC32 && 4xx && SPI_MASTER depends on PPC32 && 4xx
select SPI_BITBANG select SPI_BITBANG
help help
This selects a driver for the PPC4xx SPI Controller. This selects a driver for the PPC4xx SPI Controller.
...@@ -279,6 +285,12 @@ config SPI_PXA2XX ...@@ -279,6 +285,12 @@ config SPI_PXA2XX
config SPI_PXA2XX_PCI config SPI_PXA2XX_PCI
def_bool SPI_PXA2XX && X86_32 && PCI def_bool SPI_PXA2XX && X86_32 && PCI
config SPI_RSPI
tristate "Renesas RSPI controller"
depends on SUPERH
help
SPI driver for Renesas RSPI blocks.
config SPI_S3C24XX config SPI_S3C24XX
tristate "Samsung S3C24XX series SPI" tristate "Samsung S3C24XX series SPI"
depends on ARCH_S3C2410 && EXPERIMENTAL depends on ARCH_S3C2410 && EXPERIMENTAL
...@@ -324,9 +336,22 @@ config SPI_SH_SCI ...@@ -324,9 +336,22 @@ config SPI_SH_SCI
help help
SPI driver for SuperH SCI blocks. SPI driver for SuperH SCI blocks.
config SPI_SH_HSPI
tristate "SuperH HSPI controller"
depends on ARCH_SHMOBILE
help
SPI driver for SuperH HSPI blocks.
config SPI_SIRF
tristate "CSR SiRFprimaII SPI controller"
depends on ARCH_PRIMA2
select SPI_BITBANG
help
SPI driver for CSR SiRFprimaII SoCs
config SPI_STMP3XXX config SPI_STMP3XXX
tristate "Freescale STMP37xx/378x SPI/SSP controller" tristate "Freescale STMP37xx/378x SPI/SSP controller"
depends on ARCH_STMP3XXX && SPI_MASTER depends on ARCH_STMP3XXX
help help
SPI driver for Freescale STMP37xx/378x SoC SSP interface SPI driver for Freescale STMP37xx/378x SoC SSP interface
...@@ -384,7 +409,6 @@ config SPI_NUC900 ...@@ -384,7 +409,6 @@ config SPI_NUC900
config SPI_DESIGNWARE config SPI_DESIGNWARE
tristate "DesignWare SPI controller core support" tristate "DesignWare SPI controller core support"
depends on SPI_MASTER
help help
general driver for SPI controller core from DesignWare general driver for SPI controller core from DesignWare
......
...@@ -14,6 +14,7 @@ obj-$(CONFIG_SPI_ALTERA) += spi-altera.o ...@@ -14,6 +14,7 @@ obj-$(CONFIG_SPI_ALTERA) += spi-altera.o
obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o
obj-$(CONFIG_SPI_ATH79) += spi-ath79.o obj-$(CONFIG_SPI_ATH79) += spi-ath79.o
obj-$(CONFIG_SPI_AU1550) += spi-au1550.o obj-$(CONFIG_SPI_AU1550) += spi-au1550.o
obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o
obj-$(CONFIG_SPI_BFIN) += spi-bfin5xx.o obj-$(CONFIG_SPI_BFIN) += spi-bfin5xx.o
obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o
obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o
...@@ -44,13 +45,16 @@ obj-$(CONFIG_SPI_PL022) += spi-pl022.o ...@@ -44,13 +45,16 @@ obj-$(CONFIG_SPI_PL022) += spi-pl022.o
obj-$(CONFIG_SPI_PPC4xx) += spi-ppc4xx.o obj-$(CONFIG_SPI_PPC4xx) += spi-ppc4xx.o
obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx.o obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx.o
obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o
obj-$(CONFIG_SPI_RSPI) += spi-rspi.o
obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o
spi-s3c24xx-hw-y := spi-s3c24xx.o spi-s3c24xx-hw-y := spi-s3c24xx.o
spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o
obj-$(CONFIG_SPI_S3C64XX) += spi-s3c64xx.o obj-$(CONFIG_SPI_S3C64XX) += spi-s3c64xx.o
obj-$(CONFIG_SPI_SH) += spi-sh.o obj-$(CONFIG_SPI_SH) += spi-sh.o
obj-$(CONFIG_SPI_SH_HSPI) += spi-sh-hspi.o
obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o
obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o
obj-$(CONFIG_SPI_SIRF) += spi-sirf.o
obj-$(CONFIG_SPI_STMP3XXX) += spi-stmp.o obj-$(CONFIG_SPI_STMP3XXX) += spi-stmp.o
obj-$(CONFIG_SPI_TEGRA) += spi-tegra.o obj-$(CONFIG_SPI_TEGRA) += spi-tegra.o
obj-$(CONFIG_SPI_TI_SSP) += spi-ti-ssp.o obj-$(CONFIG_SPI_TI_SSP) += spi-ti-ssp.o
......
/*
* Broadcom BCM63xx SPI controller support
*
* Copyright (C) 2009-2011 Florian Fainelli <florian@openwrt.org>
* Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/spi/spi.h>
#include <linux/completion.h>
#include <linux/err.h>
#include <bcm63xx_dev_spi.h>
#define PFX KBUILD_MODNAME
#define DRV_VER "0.1.2"
struct bcm63xx_spi {
spinlock_t lock;
int stopping;
struct completion done;
void __iomem *regs;
int irq;
/* Platform data */
u32 speed_hz;
unsigned fifo_size;
/* Data buffers */
const unsigned char *tx_ptr;
unsigned char *rx_ptr;
/* data iomem */
u8 __iomem *tx_io;
const u8 __iomem *rx_io;
int remaining_bytes;
struct clk *clk;
struct platform_device *pdev;
};
static inline u8 bcm_spi_readb(struct bcm63xx_spi *bs,
unsigned int offset)
{
return bcm_readb(bs->regs + bcm63xx_spireg(offset));
}
static inline u16 bcm_spi_readw(struct bcm63xx_spi *bs,
unsigned int offset)
{
return bcm_readw(bs->regs + bcm63xx_spireg(offset));
}
static inline void bcm_spi_writeb(struct bcm63xx_spi *bs,
u8 value, unsigned int offset)
{
bcm_writeb(value, bs->regs + bcm63xx_spireg(offset));
}
static inline void bcm_spi_writew(struct bcm63xx_spi *bs,
u16 value, unsigned int offset)
{
bcm_writew(value, bs->regs + bcm63xx_spireg(offset));
}
static const unsigned bcm63xx_spi_freq_table[SPI_CLK_MASK][2] = {
{ 20000000, SPI_CLK_20MHZ },
{ 12500000, SPI_CLK_12_50MHZ },
{ 6250000, SPI_CLK_6_250MHZ },
{ 3125000, SPI_CLK_3_125MHZ },
{ 1563000, SPI_CLK_1_563MHZ },
{ 781000, SPI_CLK_0_781MHZ },
{ 391000, SPI_CLK_0_391MHZ }
};
static int bcm63xx_spi_setup_transfer(struct spi_device *spi,
struct spi_transfer *t)
{
struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
u8 bits_per_word;
u8 clk_cfg, reg;
u32 hz;
int i;
bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word;
hz = (t) ? t->speed_hz : spi->max_speed_hz;
if (bits_per_word != 8) {
dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n",
__func__, bits_per_word);
return -EINVAL;
}
if (spi->chip_select > spi->master->num_chipselect) {
dev_err(&spi->dev, "%s, unsupported slave %d\n",
__func__, spi->chip_select);
return -EINVAL;
}
/* Find the closest clock configuration */
for (i = 0; i < SPI_CLK_MASK; i++) {
if (hz <= bcm63xx_spi_freq_table[i][0]) {
clk_cfg = bcm63xx_spi_freq_table[i][1];
break;
}
}
/* No matching configuration found, default to lowest */
if (i == SPI_CLK_MASK)
clk_cfg = SPI_CLK_0_391MHZ;
/* clear existing clock configuration bits of the register */
reg = bcm_spi_readb(bs, SPI_CLK_CFG);
reg &= ~SPI_CLK_MASK;
reg |= clk_cfg;
bcm_spi_writeb(bs, reg, SPI_CLK_CFG);
dev_dbg(&spi->dev, "Setting clock register to %02x (hz %d)\n",
clk_cfg, hz);
return 0;
}
/* the spi->mode bits understood by this driver: */
#define MODEBITS (SPI_CPOL | SPI_CPHA)
static int bcm63xx_spi_setup(struct spi_device *spi)
{
struct bcm63xx_spi *bs;
int ret;
bs = spi_master_get_devdata(spi->master);
if (bs->stopping)
return -ESHUTDOWN;
if (!spi->bits_per_word)
spi->bits_per_word = 8;
if (spi->mode & ~MODEBITS) {
dev_err(&spi->dev, "%s, unsupported mode bits %x\n",
__func__, spi->mode & ~MODEBITS);
return -EINVAL;
}
ret = bcm63xx_spi_setup_transfer(spi, NULL);
if (ret < 0) {
dev_err(&spi->dev, "setup: unsupported mode bits %x\n",
spi->mode & ~MODEBITS);
return ret;
}
dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec/bit\n",
__func__, spi->mode & MODEBITS, spi->bits_per_word, 0);
return 0;
}
/* Fill the TX FIFO with as many bytes as possible */
static void bcm63xx_spi_fill_tx_fifo(struct bcm63xx_spi *bs)
{
u8 size;
/* Fill the Tx FIFO with as many bytes as possible */
size = bs->remaining_bytes < bs->fifo_size ? bs->remaining_bytes :
bs->fifo_size;
memcpy_toio(bs->tx_io, bs->tx_ptr, size);
bs->remaining_bytes -= size;
}
static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
{
struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
u16 msg_ctl;
u16 cmd;
dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
t->tx_buf, t->rx_buf, t->len);
/* Transmitter is inhibited */
bs->tx_ptr = t->tx_buf;
bs->rx_ptr = t->rx_buf;
init_completion(&bs->done);
if (t->tx_buf) {
bs->remaining_bytes = t->len;
bcm63xx_spi_fill_tx_fifo(bs);
}
/* Enable the command done interrupt which
* we use to determine completion of a command */
bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK);
/* Fill in the Message control register */
msg_ctl = (t->len << SPI_BYTE_CNT_SHIFT);
if (t->rx_buf && t->tx_buf)
msg_ctl |= (SPI_FD_RW << SPI_MSG_TYPE_SHIFT);
else if (t->rx_buf)
msg_ctl |= (SPI_HD_R << SPI_MSG_TYPE_SHIFT);
else if (t->tx_buf)
msg_ctl |= (SPI_HD_W << SPI_MSG_TYPE_SHIFT);
bcm_spi_writew(bs, msg_ctl, SPI_MSG_CTL);
/* Issue the transfer */
cmd = SPI_CMD_START_IMMEDIATE;
cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT);
cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT);
bcm_spi_writew(bs, cmd, SPI_CMD);
wait_for_completion(&bs->done);
/* Disable the CMD_DONE interrupt */
bcm_spi_writeb(bs, 0, SPI_INT_MASK);
return t->len - bs->remaining_bytes;
}
static int bcm63xx_transfer(struct spi_device *spi, struct spi_message *m)
{
struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
struct spi_transfer *t;
int ret = 0;
if (unlikely(list_empty(&m->transfers)))
return -EINVAL;
if (bs->stopping)
return -ESHUTDOWN;
list_for_each_entry(t, &m->transfers, transfer_list) {
ret += bcm63xx_txrx_bufs(spi, t);
}
m->complete(m->context);
return ret;
}
/* This driver supports single master mode only. Hence
* CMD_DONE is the only interrupt we care about
*/
static irqreturn_t bcm63xx_spi_interrupt(int irq, void *dev_id)
{
struct spi_master *master = (struct spi_master *)dev_id;
struct bcm63xx_spi *bs = spi_master_get_devdata(master);
u8 intr;
u16 cmd;
/* Read interupts and clear them immediately */
intr = bcm_spi_readb(bs, SPI_INT_STATUS);
bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS);
bcm_spi_writeb(bs, 0, SPI_INT_MASK);
/* A tansfer completed */
if (intr & SPI_INTR_CMD_DONE) {
u8 rx_tail;
rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL);
/* Read out all the data */
if (rx_tail)
memcpy_fromio(bs->rx_ptr, bs->rx_io, rx_tail);
/* See if there is more data to send */
if (bs->remaining_bytes > 0) {
bcm63xx_spi_fill_tx_fifo(bs);
/* Start the transfer */
bcm_spi_writew(bs, SPI_HD_W << SPI_MSG_TYPE_SHIFT,
SPI_MSG_CTL);
cmd = bcm_spi_readw(bs, SPI_CMD);
cmd |= SPI_CMD_START_IMMEDIATE;
cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT);
bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK);
bcm_spi_writew(bs, cmd, SPI_CMD);
} else {
complete(&bs->done);
}
}
return IRQ_HANDLED;
}
static int __devinit bcm63xx_spi_probe(struct platform_device *pdev)
{
struct resource *r;
struct device *dev = &pdev->dev;
struct bcm63xx_spi_pdata *pdata = pdev->dev.platform_data;
int irq;
struct spi_master *master;
struct clk *clk;
struct bcm63xx_spi *bs;
int ret;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
dev_err(dev, "no iomem\n");
ret = -ENXIO;
goto out;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(dev, "no irq\n");
ret = -ENXIO;
goto out;
}
clk = clk_get(dev, "spi");
if (IS_ERR(clk)) {
dev_err(dev, "no clock for device\n");
ret = PTR_ERR(clk);
goto out;
}
master = spi_alloc_master(dev, sizeof(*bs));
if (!master) {
dev_err(dev, "out of memory\n");
ret = -ENOMEM;
goto out_clk;
}
bs = spi_master_get_devdata(master);
init_completion(&bs->done);
platform_set_drvdata(pdev, master);
bs->pdev = pdev;
if (!devm_request_mem_region(&pdev->dev, r->start,
resource_size(r), PFX)) {
dev_err(dev, "iomem request failed\n");
ret = -ENXIO;
goto out_err;
}
bs->regs = devm_ioremap_nocache(&pdev->dev, r->start,
resource_size(r));
if (!bs->regs) {
dev_err(dev, "unable to ioremap regs\n");
ret = -ENOMEM;
goto out_err;
}
bs->irq = irq;
bs->clk = clk;
bs->fifo_size = pdata->fifo_size;
ret = devm_request_irq(&pdev->dev, irq, bcm63xx_spi_interrupt, 0,
pdev->name, master);
if (ret) {
dev_err(dev, "unable to request irq\n");
goto out_err;
}
master->bus_num = pdata->bus_num;
master->num_chipselect = pdata->num_chipselect;
master->setup = bcm63xx_spi_setup;
master->transfer = bcm63xx_transfer;
bs->speed_hz = pdata->speed_hz;
bs->stopping = 0;
bs->tx_io = (u8 *)(bs->regs + bcm63xx_spireg(SPI_MSG_DATA));
bs->rx_io = (const u8 *)(bs->regs + bcm63xx_spireg(SPI_RX_DATA));
spin_lock_init(&bs->lock);
/* Initialize hardware */
clk_enable(bs->clk);
bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS);
/* register and we are done */
ret = spi_register_master(master);
if (ret) {
dev_err(dev, "spi register failed\n");
goto out_clk_disable;
}
dev_info(dev, "at 0x%08x (irq %d, FIFOs size %d) v%s\n",
r->start, irq, bs->fifo_size, DRV_VER);
return 0;
out_clk_disable:
clk_disable(clk);
out_err:
platform_set_drvdata(pdev, NULL);
spi_master_put(master);
out_clk:
clk_put(clk);
out:
return ret;
}
static int __devexit bcm63xx_spi_remove(struct platform_device *pdev)
{
struct spi_master *master = platform_get_drvdata(pdev);
struct bcm63xx_spi *bs = spi_master_get_devdata(master);
/* reset spi block */
bcm_spi_writeb(bs, 0, SPI_INT_MASK);
spin_lock(&bs->lock);
bs->stopping = 1;
/* HW shutdown */
clk_disable(bs->clk);
clk_put(bs->clk);
spin_unlock(&bs->lock);
platform_set_drvdata(pdev, 0);
spi_unregister_master(master);
return 0;
}
#ifdef CONFIG_PM
static int bcm63xx_spi_suspend(struct device *dev)
{
struct spi_master *master =
platform_get_drvdata(to_platform_device(dev));
struct bcm63xx_spi *bs = spi_master_get_devdata(master);
clk_disable(bs->clk);
return 0;
}
static int bcm63xx_spi_resume(struct device *dev)
{
struct spi_master *master =
platform_get_drvdata(to_platform_device(dev));
struct bcm63xx_spi *bs = spi_master_get_devdata(master);
clk_enable(bs->clk);
return 0;
}
static const struct dev_pm_ops bcm63xx_spi_pm_ops = {
.suspend = bcm63xx_spi_suspend,
.resume = bcm63xx_spi_resume,
};
#define BCM63XX_SPI_PM_OPS (&bcm63xx_spi_pm_ops)
#else
#define BCM63XX_SPI_PM_OPS NULL
#endif
static struct platform_driver bcm63xx_spi_driver = {
.driver = {
.name = "bcm63xx-spi",
.owner = THIS_MODULE,
.pm = BCM63XX_SPI_PM_OPS,
},
.probe = bcm63xx_spi_probe,
.remove = __devexit_p(bcm63xx_spi_remove),
};
module_platform_driver(bcm63xx_spi_driver);
MODULE_ALIAS("platform:bcm63xx_spi");
MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
MODULE_AUTHOR("Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>");
MODULE_DESCRIPTION("Broadcom BCM63xx SPI Controller driver");
MODULE_LICENSE("GPL");
...@@ -149,7 +149,7 @@ static int spi_resume(struct pci_dev *pdev) ...@@ -149,7 +149,7 @@ static int spi_resume(struct pci_dev *pdev)
#define spi_resume NULL #define spi_resume NULL
#endif #endif
static const struct pci_device_id pci_ids[] __devinitdata = { static DEFINE_PCI_DEVICE_TABLE(pci_ids) = {
/* Intel MID platform SPI controller 0 */ /* Intel MID platform SPI controller 0 */
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0800) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0800) },
{}, {},
......
...@@ -180,18 +180,20 @@ static int fsl_espi_setup_transfer(struct spi_device *spi, ...@@ -180,18 +180,20 @@ static int fsl_espi_setup_transfer(struct spi_device *spi,
if ((mpc8xxx_spi->spibrg / hz) > 64) { if ((mpc8xxx_spi->spibrg / hz) > 64) {
cs->hw_mode |= CSMODE_DIV16; cs->hw_mode |= CSMODE_DIV16;
pm = (mpc8xxx_spi->spibrg - 1) / (hz * 64) + 1; pm = DIV_ROUND_UP(mpc8xxx_spi->spibrg, hz * 16 * 4);
WARN_ONCE(pm > 16, "%s: Requested speed is too low: %d Hz. " WARN_ONCE(pm > 33, "%s: Requested speed is too low: %d Hz. "
"Will use %d Hz instead.\n", dev_name(&spi->dev), "Will use %d Hz instead.\n", dev_name(&spi->dev),
hz, mpc8xxx_spi->spibrg / 1024); hz, mpc8xxx_spi->spibrg / (4 * 16 * (32 + 1)));
if (pm > 16) if (pm > 33)
pm = 16; pm = 33;
} else { } else {
pm = (mpc8xxx_spi->spibrg - 1) / (hz * 4) + 1; pm = DIV_ROUND_UP(mpc8xxx_spi->spibrg, hz * 4);
} }
if (pm) if (pm)
pm--; pm--;
if (pm < 2)
pm = 2;
cs->hw_mode |= CSMODE_PM(pm); cs->hw_mode |= CSMODE_PM(pm);
......
...@@ -793,13 +793,8 @@ static int __devinit spi_imx_probe(struct platform_device *pdev) ...@@ -793,13 +793,8 @@ static int __devinit spi_imx_probe(struct platform_device *pdev)
ret = gpio_request(spi_imx->chipselect[i], DRIVER_NAME); ret = gpio_request(spi_imx->chipselect[i], DRIVER_NAME);
if (ret) { if (ret) {
while (i > 0) {
i--;
if (spi_imx->chipselect[i] >= 0)
gpio_free(spi_imx->chipselect[i]);
}
dev_err(&pdev->dev, "can't get cs gpios\n"); dev_err(&pdev->dev, "can't get cs gpios\n");
goto out_master_put; goto out_gpio_free;
} }
} }
...@@ -881,10 +876,10 @@ static int __devinit spi_imx_probe(struct platform_device *pdev) ...@@ -881,10 +876,10 @@ static int __devinit spi_imx_probe(struct platform_device *pdev)
out_release_mem: out_release_mem:
release_mem_region(res->start, resource_size(res)); release_mem_region(res->start, resource_size(res));
out_gpio_free: out_gpio_free:
for (i = 0; i < master->num_chipselect; i++) while (--i >= 0) {
if (spi_imx->chipselect[i] >= 0) if (spi_imx->chipselect[i] >= 0)
gpio_free(spi_imx->chipselect[i]); gpio_free(spi_imx->chipselect[i]);
out_master_put: }
spi_master_put(master); spi_master_put(master);
kfree(master); kfree(master);
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
......
...@@ -360,8 +360,6 @@ static int __devinit nuc900_spi_probe(struct platform_device *pdev) ...@@ -360,8 +360,6 @@ static int __devinit nuc900_spi_probe(struct platform_device *pdev)
} }
hw = spi_master_get_devdata(master); hw = spi_master_get_devdata(master);
memset(hw, 0, sizeof(struct nuc900_spi));
hw->master = spi_master_get(master); hw->master = spi_master_get(master);
hw->pdata = pdev->dev.platform_data; hw->pdata = pdev->dev.platform_data;
hw->dev = &pdev->dev; hw->dev = &pdev->dev;
......
...@@ -34,6 +34,8 @@ ...@@ -34,6 +34,8 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
...@@ -1079,15 +1081,39 @@ static int omap_mcspi_runtime_resume(struct device *dev) ...@@ -1079,15 +1081,39 @@ static int omap_mcspi_runtime_resume(struct device *dev)
return 0; return 0;
} }
static struct omap2_mcspi_platform_config omap2_pdata = {
.regs_offset = 0,
};
static struct omap2_mcspi_platform_config omap4_pdata = {
.regs_offset = OMAP4_MCSPI_REG_OFFSET,
};
static const struct of_device_id omap_mcspi_of_match[] = {
{
.compatible = "ti,omap2-mcspi",
.data = &omap2_pdata,
},
{
.compatible = "ti,omap4-mcspi",
.data = &omap4_pdata,
},
{ },
};
MODULE_DEVICE_TABLE(of, omap_mcspi_of_match);
static int __init omap2_mcspi_probe(struct platform_device *pdev) static int __init omap2_mcspi_probe(struct platform_device *pdev)
{ {
struct spi_master *master; struct spi_master *master;
struct omap2_mcspi_platform_config *pdata = pdev->dev.platform_data; struct omap2_mcspi_platform_config *pdata;
struct omap2_mcspi *mcspi; struct omap2_mcspi *mcspi;
struct resource *r; struct resource *r;
int status = 0, i; int status = 0, i;
char wq_name[20]; char wq_name[20];
u32 regs_offset = 0;
static int bus_num = 1;
struct device_node *node = pdev->dev.of_node;
const struct of_device_id *match;
master = spi_alloc_master(&pdev->dev, sizeof *mcspi); master = spi_alloc_master(&pdev->dev, sizeof *mcspi);
if (master == NULL) { if (master == NULL) {
...@@ -1098,13 +1124,26 @@ static int __init omap2_mcspi_probe(struct platform_device *pdev) ...@@ -1098,13 +1124,26 @@ static int __init omap2_mcspi_probe(struct platform_device *pdev)
/* the spi->mode bits understood by this driver: */ /* the spi->mode bits understood by this driver: */
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
if (pdev->id != -1)
master->bus_num = pdev->id;
master->setup = omap2_mcspi_setup; master->setup = omap2_mcspi_setup;
master->transfer = omap2_mcspi_transfer; master->transfer = omap2_mcspi_transfer;
master->cleanup = omap2_mcspi_cleanup; master->cleanup = omap2_mcspi_cleanup;
master->dev.of_node = node;
match = of_match_device(omap_mcspi_of_match, &pdev->dev);
if (match) {
u32 num_cs = 1; /* default number of chipselect */
pdata = match->data;
of_property_read_u32(node, "ti,spi-num-cs", &num_cs);
master->num_chipselect = num_cs;
master->bus_num = bus_num++;
} else {
pdata = pdev->dev.platform_data;
master->num_chipselect = pdata->num_cs; master->num_chipselect = pdata->num_cs;
if (pdev->id != -1)
master->bus_num = pdev->id;
}
regs_offset = pdata->regs_offset;
dev_set_drvdata(&pdev->dev, master); dev_set_drvdata(&pdev->dev, master);
...@@ -1124,8 +1163,8 @@ static int __init omap2_mcspi_probe(struct platform_device *pdev) ...@@ -1124,8 +1163,8 @@ static int __init omap2_mcspi_probe(struct platform_device *pdev)
goto free_master; goto free_master;
} }
r->start += pdata->regs_offset; r->start += regs_offset;
r->end += pdata->regs_offset; r->end += regs_offset;
mcspi->phys = r->start; mcspi->phys = r->start;
if (!request_mem_region(r->start, resource_size(r), if (!request_mem_region(r->start, resource_size(r),
dev_name(&pdev->dev))) { dev_name(&pdev->dev))) {
...@@ -1285,7 +1324,8 @@ static struct platform_driver omap2_mcspi_driver = { ...@@ -1285,7 +1324,8 @@ static struct platform_driver omap2_mcspi_driver = {
.driver = { .driver = {
.name = "omap2_mcspi", .name = "omap2_mcspi",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &omap2_mcspi_pm_ops .pm = &omap2_mcspi_pm_ops,
.of_match_table = omap_mcspi_of_match,
}, },
.remove = __exit_p(omap2_mcspi_remove), .remove = __exit_p(omap2_mcspi_remove),
}; };
......
...@@ -29,7 +29,6 @@ ...@@ -29,7 +29,6 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/workqueue.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/err.h> #include <linux/err.h>
...@@ -330,12 +329,13 @@ struct vendor_data { ...@@ -330,12 +329,13 @@ struct vendor_data {
* @clk: outgoing clock "SPICLK" for the SPI bus * @clk: outgoing clock "SPICLK" for the SPI bus
* @master: SPI framework hookup * @master: SPI framework hookup
* @master_info: controller-specific data from machine setup * @master_info: controller-specific data from machine setup
* @workqueue: a workqueue on which any spi_message request is queued * @kworker: thread struct for message pump
* @pump_messages: work struct for scheduling work to the workqueue * @kworker_task: pointer to task for message pump kworker thread
* @pump_messages: work struct for scheduling work to the message pump
* @queue_lock: spinlock to syncronise access to message queue * @queue_lock: spinlock to syncronise access to message queue
* @queue: message queue * @queue: message queue
* @busy: workqueue is busy * @busy: message pump is busy
* @running: workqueue is running * @running: message pump is running
* @pump_transfers: Tasklet used in Interrupt Transfer mode * @pump_transfers: Tasklet used in Interrupt Transfer mode
* @cur_msg: Pointer to current spi_message being processed * @cur_msg: Pointer to current spi_message being processed
* @cur_transfer: Pointer to current spi_transfer * @cur_transfer: Pointer to current spi_transfer
...@@ -365,14 +365,7 @@ struct pl022 { ...@@ -365,14 +365,7 @@ struct pl022 {
struct clk *clk; struct clk *clk;
struct spi_master *master; struct spi_master *master;
struct pl022_ssp_controller *master_info; struct pl022_ssp_controller *master_info;
/* Driver message queue */ /* Message per-transfer pump */
struct workqueue_struct *workqueue;
struct work_struct pump_messages;
spinlock_t queue_lock;
struct list_head queue;
bool busy;
bool running;
/* Message transfer pump */
struct tasklet_struct pump_transfers; struct tasklet_struct pump_transfers;
struct spi_message *cur_msg; struct spi_message *cur_msg;
struct spi_transfer *cur_transfer; struct spi_transfer *cur_transfer;
...@@ -394,6 +387,7 @@ struct pl022 { ...@@ -394,6 +387,7 @@ struct pl022 {
struct sg_table sgt_rx; struct sg_table sgt_rx;
struct sg_table sgt_tx; struct sg_table sgt_tx;
char *dummypage; char *dummypage;
bool dma_running;
#endif #endif
}; };
...@@ -448,8 +442,6 @@ static void null_cs_control(u32 command) ...@@ -448,8 +442,6 @@ static void null_cs_control(u32 command)
static void giveback(struct pl022 *pl022) static void giveback(struct pl022 *pl022)
{ {
struct spi_transfer *last_transfer; struct spi_transfer *last_transfer;
unsigned long flags;
struct spi_message *msg;
pl022->next_msg_cs_active = false; pl022->next_msg_cs_active = false;
last_transfer = list_entry(pl022->cur_msg->transfers.prev, last_transfer = list_entry(pl022->cur_msg->transfers.prev,
...@@ -477,15 +469,8 @@ static void giveback(struct pl022 *pl022) ...@@ -477,15 +469,8 @@ static void giveback(struct pl022 *pl022)
* sent the current message could be unloaded, which * sent the current message could be unloaded, which
* could invalidate the cs_control() callback... * could invalidate the cs_control() callback...
*/ */
/* get a pointer to the next message, if any */ /* get a pointer to the next message, if any */
spin_lock_irqsave(&pl022->queue_lock, flags); next_msg = spi_get_next_queued_message(pl022->master);
if (list_empty(&pl022->queue))
next_msg = NULL;
else
next_msg = list_entry(pl022->queue.next,
struct spi_message, queue);
spin_unlock_irqrestore(&pl022->queue_lock, flags);
/* /*
* see if the next and current messages point * see if the next and current messages point
...@@ -497,19 +482,13 @@ static void giveback(struct pl022 *pl022) ...@@ -497,19 +482,13 @@ static void giveback(struct pl022 *pl022)
pl022->cur_chip->cs_control(SSP_CHIP_DESELECT); pl022->cur_chip->cs_control(SSP_CHIP_DESELECT);
else else
pl022->next_msg_cs_active = true; pl022->next_msg_cs_active = true;
} }
spin_lock_irqsave(&pl022->queue_lock, flags);
msg = pl022->cur_msg;
pl022->cur_msg = NULL; pl022->cur_msg = NULL;
pl022->cur_transfer = NULL; pl022->cur_transfer = NULL;
pl022->cur_chip = NULL; pl022->cur_chip = NULL;
queue_work(pl022->workqueue, &pl022->pump_messages); spi_finalize_current_message(pl022->master);
spin_unlock_irqrestore(&pl022->queue_lock, flags);
msg->state = NULL;
if (msg->complete)
msg->complete(msg->context);
} }
/** /**
...@@ -1063,6 +1042,7 @@ static int configure_dma(struct pl022 *pl022) ...@@ -1063,6 +1042,7 @@ static int configure_dma(struct pl022 *pl022)
dmaengine_submit(txdesc); dmaengine_submit(txdesc);
dma_async_issue_pending(rxchan); dma_async_issue_pending(rxchan);
dma_async_issue_pending(txchan); dma_async_issue_pending(txchan);
pl022->dma_running = true;
return 0; return 0;
...@@ -1141,11 +1121,12 @@ static void terminate_dma(struct pl022 *pl022) ...@@ -1141,11 +1121,12 @@ static void terminate_dma(struct pl022 *pl022)
dmaengine_terminate_all(rxchan); dmaengine_terminate_all(rxchan);
dmaengine_terminate_all(txchan); dmaengine_terminate_all(txchan);
unmap_free_dma_scatter(pl022); unmap_free_dma_scatter(pl022);
pl022->dma_running = false;
} }
static void pl022_dma_remove(struct pl022 *pl022) static void pl022_dma_remove(struct pl022 *pl022)
{ {
if (pl022->busy) if (pl022->dma_running)
terminate_dma(pl022); terminate_dma(pl022);
if (pl022->dma_tx_channel) if (pl022->dma_tx_channel)
dma_release_channel(pl022->dma_tx_channel); dma_release_channel(pl022->dma_tx_channel);
...@@ -1493,73 +1474,20 @@ static void do_polling_transfer(struct pl022 *pl022) ...@@ -1493,73 +1474,20 @@ static void do_polling_transfer(struct pl022 *pl022)
return; return;
} }
/** static int pl022_transfer_one_message(struct spi_master *master,
* pump_messages - Workqueue function which processes spi message queue struct spi_message *msg)
* @data: pointer to private data of SSP driver
*
* This function checks if there is any spi message in the queue that
* needs processing and delegate control to appropriate function
* do_polling_transfer()/do_interrupt_dma_transfer()
* based on the kind of the transfer
*
*/
static void pump_messages(struct work_struct *work)
{ {
struct pl022 *pl022 = struct pl022 *pl022 = spi_master_get_devdata(master);
container_of(work, struct pl022, pump_messages);
unsigned long flags;
bool was_busy = false;
/* Lock queue and check for queue work */
spin_lock_irqsave(&pl022->queue_lock, flags);
if (list_empty(&pl022->queue) || !pl022->running) {
if (pl022->busy) {
/* nothing more to do - disable spi/ssp and power off */
writew((readw(SSP_CR1(pl022->virtbase)) &
(~SSP_CR1_MASK_SSE)), SSP_CR1(pl022->virtbase));
if (pl022->master_info->autosuspend_delay > 0) {
pm_runtime_mark_last_busy(&pl022->adev->dev);
pm_runtime_put_autosuspend(&pl022->adev->dev);
} else {
pm_runtime_put(&pl022->adev->dev);
}
}
pl022->busy = false;
spin_unlock_irqrestore(&pl022->queue_lock, flags);
return;
}
/* Make sure we are not already running a message */
if (pl022->cur_msg) {
spin_unlock_irqrestore(&pl022->queue_lock, flags);
return;
}
/* Extract head of queue */
pl022->cur_msg =
list_entry(pl022->queue.next, struct spi_message, queue);
list_del_init(&pl022->cur_msg->queue);
if (pl022->busy)
was_busy = true;
else
pl022->busy = true;
spin_unlock_irqrestore(&pl022->queue_lock, flags);
/* Initial message state */ /* Initial message state */
pl022->cur_msg->state = STATE_START; pl022->cur_msg = msg;
pl022->cur_transfer = list_entry(pl022->cur_msg->transfers.next, msg->state = STATE_START;
pl022->cur_transfer = list_entry(msg->transfers.next,
struct spi_transfer, transfer_list); struct spi_transfer, transfer_list);
/* Setup the SPI using the per chip configuration */ /* Setup the SPI using the per chip configuration */
pl022->cur_chip = spi_get_ctldata(pl022->cur_msg->spi); pl022->cur_chip = spi_get_ctldata(msg->spi);
if (!was_busy)
/*
* We enable the core voltage and clocks here, then the clocks
* and core will be disabled when this workqueue is run again
* and there is no more work to be done.
*/
pm_runtime_get_sync(&pl022->adev->dev);
restore_state(pl022); restore_state(pl022);
flush(pl022); flush(pl022);
...@@ -1568,95 +1496,37 @@ static void pump_messages(struct work_struct *work) ...@@ -1568,95 +1496,37 @@ static void pump_messages(struct work_struct *work)
do_polling_transfer(pl022); do_polling_transfer(pl022);
else else
do_interrupt_dma_transfer(pl022); do_interrupt_dma_transfer(pl022);
}
static int __init init_queue(struct pl022 *pl022)
{
INIT_LIST_HEAD(&pl022->queue);
spin_lock_init(&pl022->queue_lock);
pl022->running = false;
pl022->busy = false;
tasklet_init(&pl022->pump_transfers, pump_transfers,
(unsigned long)pl022);
INIT_WORK(&pl022->pump_messages, pump_messages);
pl022->workqueue = create_singlethread_workqueue(
dev_name(pl022->master->dev.parent));
if (pl022->workqueue == NULL)
return -EBUSY;
return 0; return 0;
} }
static int start_queue(struct pl022 *pl022) static int pl022_prepare_transfer_hardware(struct spi_master *master)
{ {
unsigned long flags; struct pl022 *pl022 = spi_master_get_devdata(master);
spin_lock_irqsave(&pl022->queue_lock, flags);
if (pl022->running || pl022->busy) {
spin_unlock_irqrestore(&pl022->queue_lock, flags);
return -EBUSY;
}
pl022->running = true;
pl022->cur_msg = NULL;
pl022->cur_transfer = NULL;
pl022->cur_chip = NULL;
pl022->next_msg_cs_active = false;
spin_unlock_irqrestore(&pl022->queue_lock, flags);
queue_work(pl022->workqueue, &pl022->pump_messages);
/*
* Just make sure we have all we need to run the transfer by syncing
* with the runtime PM framework.
*/
pm_runtime_get_sync(&pl022->adev->dev);
return 0; return 0;
} }
static int stop_queue(struct pl022 *pl022) static int pl022_unprepare_transfer_hardware(struct spi_master *master)
{ {
unsigned long flags; struct pl022 *pl022 = spi_master_get_devdata(master);
unsigned limit = 500;
int status = 0;
spin_lock_irqsave(&pl022->queue_lock, flags); /* nothing more to do - disable spi/ssp and power off */
writew((readw(SSP_CR1(pl022->virtbase)) &
(~SSP_CR1_MASK_SSE)), SSP_CR1(pl022->virtbase));
/* This is a bit lame, but is optimized for the common execution path. if (pl022->master_info->autosuspend_delay > 0) {
* A wait_queue on the pl022->busy could be used, but then the common pm_runtime_mark_last_busy(&pl022->adev->dev);
* execution path (pump_messages) would be required to call wake_up or pm_runtime_put_autosuspend(&pl022->adev->dev);
* friends on every SPI message. Do this instead */ } else {
while ((!list_empty(&pl022->queue) || pl022->busy) && limit--) { pm_runtime_put(&pl022->adev->dev);
spin_unlock_irqrestore(&pl022->queue_lock, flags);
msleep(10);
spin_lock_irqsave(&pl022->queue_lock, flags);
} }
if (!list_empty(&pl022->queue) || pl022->busy)
status = -EBUSY;
else
pl022->running = false;
spin_unlock_irqrestore(&pl022->queue_lock, flags);
return status;
}
static int destroy_queue(struct pl022 *pl022)
{
int status;
status = stop_queue(pl022);
/* we are unloading the module or failing to load (only two calls
* to this routine), and neither call can handle a return value.
* However, destroy_workqueue calls flush_workqueue, and that will
* block until all work is done. If the reason that stop_queue
* timed out is that the work will never finish, then it does no
* good to call destroy_workqueue, so return anyway. */
if (status != 0)
return status;
destroy_workqueue(pl022->workqueue);
return 0; return 0;
} }
...@@ -1776,38 +1646,6 @@ static int verify_controller_parameters(struct pl022 *pl022, ...@@ -1776,38 +1646,6 @@ static int verify_controller_parameters(struct pl022 *pl022,
return 0; return 0;
} }
/**
* pl022_transfer - transfer function registered to SPI master framework
* @spi: spi device which is requesting transfer
* @msg: spi message which is to handled is queued to driver queue
*
* This function is registered to the SPI framework for this SPI master
* controller. It will queue the spi_message in the queue of driver if
* the queue is not stopped and return.
*/
static int pl022_transfer(struct spi_device *spi, struct spi_message *msg)
{
struct pl022 *pl022 = spi_master_get_devdata(spi->master);
unsigned long flags;
spin_lock_irqsave(&pl022->queue_lock, flags);
if (!pl022->running) {
spin_unlock_irqrestore(&pl022->queue_lock, flags);
return -ESHUTDOWN;
}
msg->actual_length = 0;
msg->status = -EINPROGRESS;
msg->state = STATE_START;
list_add_tail(&msg->queue, &pl022->queue);
if (pl022->running && !pl022->busy)
queue_work(pl022->workqueue, &pl022->pump_messages);
spin_unlock_irqrestore(&pl022->queue_lock, flags);
return 0;
}
static inline u32 spi_rate(u32 rate, u16 cpsdvsr, u16 scr) static inline u32 spi_rate(u32 rate, u16 cpsdvsr, u16 scr)
{ {
return rate / (cpsdvsr * (1 + scr)); return rate / (cpsdvsr * (1 + scr));
...@@ -2170,7 +2008,10 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) ...@@ -2170,7 +2008,10 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
master->num_chipselect = platform_info->num_chipselect; master->num_chipselect = platform_info->num_chipselect;
master->cleanup = pl022_cleanup; master->cleanup = pl022_cleanup;
master->setup = pl022_setup; master->setup = pl022_setup;
master->transfer = pl022_transfer; master->prepare_transfer_hardware = pl022_prepare_transfer_hardware;
master->transfer_one_message = pl022_transfer_one_message;
master->unprepare_transfer_hardware = pl022_unprepare_transfer_hardware;
master->rt = platform_info->rt;
/* /*
* Supports mode 0-3, loopback, and active low CS. Transfers are * Supports mode 0-3, loopback, and active low CS. Transfers are
...@@ -2214,6 +2055,10 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) ...@@ -2214,6 +2055,10 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
goto err_no_clk_en; goto err_no_clk_en;
} }
/* Initialize transfer pump */
tasklet_init(&pl022->pump_transfers, pump_transfers,
(unsigned long)pl022);
/* Disable SSP */ /* Disable SSP */
writew((readw(SSP_CR1(pl022->virtbase)) & (~SSP_CR1_MASK_SSE)), writew((readw(SSP_CR1(pl022->virtbase)) & (~SSP_CR1_MASK_SSE)),
SSP_CR1(pl022->virtbase)); SSP_CR1(pl022->virtbase));
...@@ -2233,17 +2078,6 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) ...@@ -2233,17 +2078,6 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
platform_info->enable_dma = 0; platform_info->enable_dma = 0;
} }
/* Initialize and start queue */
status = init_queue(pl022);
if (status != 0) {
dev_err(&adev->dev, "probe - problem initializing queue\n");
goto err_init_queue;
}
status = start_queue(pl022);
if (status != 0) {
dev_err(&adev->dev, "probe - problem starting queue\n");
goto err_start_queue;
}
/* Register with the SPI framework */ /* Register with the SPI framework */
amba_set_drvdata(adev, pl022); amba_set_drvdata(adev, pl022);
status = spi_register_master(master); status = spi_register_master(master);
...@@ -2269,9 +2103,6 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) ...@@ -2269,9 +2103,6 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
return 0; return 0;
err_spi_register: err_spi_register:
err_start_queue:
err_init_queue:
destroy_queue(pl022);
if (platform_info->enable_dma) if (platform_info->enable_dma)
pl022_dma_remove(pl022); pl022_dma_remove(pl022);
...@@ -2307,9 +2138,6 @@ pl022_remove(struct amba_device *adev) ...@@ -2307,9 +2138,6 @@ pl022_remove(struct amba_device *adev)
*/ */
pm_runtime_get_noresume(&adev->dev); pm_runtime_get_noresume(&adev->dev);
/* Remove the queue */
if (destroy_queue(pl022) != 0)
dev_err(&adev->dev, "queue remove failed\n");
load_ssp_default_config(pl022); load_ssp_default_config(pl022);
if (pl022->master_info->enable_dma) if (pl022->master_info->enable_dma)
pl022_dma_remove(pl022); pl022_dma_remove(pl022);
...@@ -2331,12 +2159,12 @@ pl022_remove(struct amba_device *adev) ...@@ -2331,12 +2159,12 @@ pl022_remove(struct amba_device *adev)
static int pl022_suspend(struct device *dev) static int pl022_suspend(struct device *dev)
{ {
struct pl022 *pl022 = dev_get_drvdata(dev); struct pl022 *pl022 = dev_get_drvdata(dev);
int status = 0; int ret;
status = stop_queue(pl022); ret = spi_master_suspend(pl022->master);
if (status) { if (ret) {
dev_warn(dev, "suspend cannot stop queue\n"); dev_warn(dev, "cannot suspend master\n");
return status; return ret;
} }
dev_dbg(dev, "suspended\n"); dev_dbg(dev, "suspended\n");
...@@ -2346,16 +2174,16 @@ static int pl022_suspend(struct device *dev) ...@@ -2346,16 +2174,16 @@ static int pl022_suspend(struct device *dev)
static int pl022_resume(struct device *dev) static int pl022_resume(struct device *dev)
{ {
struct pl022 *pl022 = dev_get_drvdata(dev); struct pl022 *pl022 = dev_get_drvdata(dev);
int status = 0; int ret;
/* Start the queue running */ /* Start the queue running */
status = start_queue(pl022); ret = spi_master_resume(pl022->master);
if (status) if (ret)
dev_err(dev, "problem starting queue (%d)\n", status); dev_err(dev, "problem starting queue (%d)\n", ret);
else else
dev_dbg(dev, "resumed\n"); dev_dbg(dev, "resumed\n");
return status; return ret;
} }
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */
......
...@@ -151,7 +151,7 @@ static void __devexit ce4100_spi_remove(struct pci_dev *dev) ...@@ -151,7 +151,7 @@ static void __devexit ce4100_spi_remove(struct pci_dev *dev)
kfree(spi_info); kfree(spi_info);
} }
static struct pci_device_id ce4100_spi_devices[] __devinitdata = { static DEFINE_PCI_DEVICE_TABLE(ce4100_spi_devices) = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2e6a) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2e6a) },
{ }, { },
}; };
......
/*
* SH RSPI driver
*
* Copyright (C) 2012 Renesas Solutions Corp.
*
* Based on spi-sh.c:
* Copyright (C) 2011 Renesas Solutions Corp.
*
* 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/list.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/spi/spi.h>
#define RSPI_SPCR 0x00
#define RSPI_SSLP 0x01
#define RSPI_SPPCR 0x02
#define RSPI_SPSR 0x03
#define RSPI_SPDR 0x04
#define RSPI_SPSCR 0x08
#define RSPI_SPSSR 0x09
#define RSPI_SPBR 0x0a
#define RSPI_SPDCR 0x0b
#define RSPI_SPCKD 0x0c
#define RSPI_SSLND 0x0d
#define RSPI_SPND 0x0e
#define RSPI_SPCR2 0x0f
#define RSPI_SPCMD0 0x10
#define RSPI_SPCMD1 0x12
#define RSPI_SPCMD2 0x14
#define RSPI_SPCMD3 0x16
#define RSPI_SPCMD4 0x18
#define RSPI_SPCMD5 0x1a
#define RSPI_SPCMD6 0x1c
#define RSPI_SPCMD7 0x1e
/* SPCR */
#define SPCR_SPRIE 0x80
#define SPCR_SPE 0x40
#define SPCR_SPTIE 0x20
#define SPCR_SPEIE 0x10
#define SPCR_MSTR 0x08
#define SPCR_MODFEN 0x04
#define SPCR_TXMD 0x02
#define SPCR_SPMS 0x01
/* SSLP */
#define SSLP_SSL1P 0x02
#define SSLP_SSL0P 0x01
/* SPPCR */
#define SPPCR_MOIFE 0x20
#define SPPCR_MOIFV 0x10
#define SPPCR_SPOM 0x04
#define SPPCR_SPLP2 0x02
#define SPPCR_SPLP 0x01
/* SPSR */
#define SPSR_SPRF 0x80
#define SPSR_SPTEF 0x20
#define SPSR_PERF 0x08
#define SPSR_MODF 0x04
#define SPSR_IDLNF 0x02
#define SPSR_OVRF 0x01
/* SPSCR */
#define SPSCR_SPSLN_MASK 0x07
/* SPSSR */
#define SPSSR_SPECM_MASK 0x70
#define SPSSR_SPCP_MASK 0x07
/* SPDCR */
#define SPDCR_SPLW 0x20
#define SPDCR_SPRDTD 0x10
#define SPDCR_SLSEL1 0x08
#define SPDCR_SLSEL0 0x04
#define SPDCR_SLSEL_MASK 0x0c
#define SPDCR_SPFC1 0x02
#define SPDCR_SPFC0 0x01
/* SPCKD */
#define SPCKD_SCKDL_MASK 0x07
/* SSLND */
#define SSLND_SLNDL_MASK 0x07
/* SPND */
#define SPND_SPNDL_MASK 0x07
/* SPCR2 */
#define SPCR2_PTE 0x08
#define SPCR2_SPIE 0x04
#define SPCR2_SPOE 0x02
#define SPCR2_SPPE 0x01
/* SPCMDn */
#define SPCMD_SCKDEN 0x8000
#define SPCMD_SLNDEN 0x4000
#define SPCMD_SPNDEN 0x2000
#define SPCMD_LSBF 0x1000
#define SPCMD_SPB_MASK 0x0f00
#define SPCMD_SPB_8_TO_16(bit) (((bit - 1) << 8) & SPCMD_SPB_MASK)
#define SPCMD_SPB_20BIT 0x0000
#define SPCMD_SPB_24BIT 0x0100
#define SPCMD_SPB_32BIT 0x0200
#define SPCMD_SSLKP 0x0080
#define SPCMD_SSLA_MASK 0x0030
#define SPCMD_BRDV_MASK 0x000c
#define SPCMD_CPOL 0x0002
#define SPCMD_CPHA 0x0001
struct rspi_data {
void __iomem *addr;
u32 max_speed_hz;
struct spi_master *master;
struct list_head queue;
struct work_struct ws;
wait_queue_head_t wait;
spinlock_t lock;
struct clk *clk;
unsigned char spsr;
};
static void rspi_write8(struct rspi_data *rspi, u8 data, u16 offset)
{
iowrite8(data, rspi->addr + offset);
}
static void rspi_write16(struct rspi_data *rspi, u16 data, u16 offset)
{
iowrite16(data, rspi->addr + offset);
}
static u8 rspi_read8(struct rspi_data *rspi, u16 offset)
{
return ioread8(rspi->addr + offset);
}
static u16 rspi_read16(struct rspi_data *rspi, u16 offset)
{
return ioread16(rspi->addr + offset);
}
static unsigned char rspi_calc_spbr(struct rspi_data *rspi)
{
int tmp;
unsigned char spbr;
tmp = clk_get_rate(rspi->clk) / (2 * rspi->max_speed_hz) - 1;
spbr = clamp(tmp, 0, 255);
return spbr;
}
static void rspi_enable_irq(struct rspi_data *rspi, u8 enable)
{
rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) | enable, RSPI_SPCR);
}
static void rspi_disable_irq(struct rspi_data *rspi, u8 disable)
{
rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) & ~disable, RSPI_SPCR);
}
static int rspi_wait_for_interrupt(struct rspi_data *rspi, u8 wait_mask,
u8 enable_bit)
{
int ret;
rspi->spsr = rspi_read8(rspi, RSPI_SPSR);
rspi_enable_irq(rspi, enable_bit);
ret = wait_event_timeout(rspi->wait, rspi->spsr & wait_mask, HZ);
if (ret == 0 && !(rspi->spsr & wait_mask))
return -ETIMEDOUT;
return 0;
}
static void rspi_assert_ssl(struct rspi_data *rspi)
{
rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) | SPCR_SPE, RSPI_SPCR);
}
static void rspi_negate_ssl(struct rspi_data *rspi)
{
rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) & ~SPCR_SPE, RSPI_SPCR);
}
static int rspi_set_config_register(struct rspi_data *rspi, int access_size)
{
/* Sets output mode(CMOS) and MOSI signal(from previous transfer) */
rspi_write8(rspi, 0x00, RSPI_SPPCR);
/* Sets transfer bit rate */
rspi_write8(rspi, rspi_calc_spbr(rspi), RSPI_SPBR);
/* Sets number of frames to be used: 1 frame */
rspi_write8(rspi, 0x00, RSPI_SPDCR);
/* Sets RSPCK, SSL, next-access delay value */
rspi_write8(rspi, 0x00, RSPI_SPCKD);
rspi_write8(rspi, 0x00, RSPI_SSLND);
rspi_write8(rspi, 0x00, RSPI_SPND);
/* Sets parity, interrupt mask */
rspi_write8(rspi, 0x00, RSPI_SPCR2);
/* Sets SPCMD */
rspi_write16(rspi, SPCMD_SPB_8_TO_16(access_size) | SPCMD_SSLKP,
RSPI_SPCMD0);
/* Sets RSPI mode */
rspi_write8(rspi, SPCR_MSTR, RSPI_SPCR);
return 0;
}
static int rspi_send_pio(struct rspi_data *rspi, struct spi_message *mesg,
struct spi_transfer *t)
{
int remain = t->len;
u8 *data;
data = (u8 *)t->tx_buf;
while (remain > 0) {
rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) | SPCR_TXMD,
RSPI_SPCR);
if (rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE) < 0) {
dev_err(&rspi->master->dev,
"%s: tx empty timeout\n", __func__);
return -ETIMEDOUT;
}
rspi_write16(rspi, *data, RSPI_SPDR);
data++;
remain--;
}
/* Waiting for the last transmition */
rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE);
return 0;
}
static int rspi_receive_pio(struct rspi_data *rspi, struct spi_message *mesg,
struct spi_transfer *t)
{
int remain = t->len;
u8 *data;
unsigned char spsr;
spsr = rspi_read8(rspi, RSPI_SPSR);
if (spsr & SPSR_SPRF)
rspi_read16(rspi, RSPI_SPDR); /* dummy read */
if (spsr & SPSR_OVRF)
rspi_write8(rspi, rspi_read8(rspi, RSPI_SPSR) & ~SPSR_OVRF,
RSPI_SPCR);
data = (u8 *)t->rx_buf;
while (remain > 0) {
rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) & ~SPCR_TXMD,
RSPI_SPCR);
if (rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE) < 0) {
dev_err(&rspi->master->dev,
"%s: tx empty timeout\n", __func__);
return -ETIMEDOUT;
}
/* dummy write for generate clock */
rspi_write16(rspi, 0x00, RSPI_SPDR);
if (rspi_wait_for_interrupt(rspi, SPSR_SPRF, SPCR_SPRIE) < 0) {
dev_err(&rspi->master->dev,
"%s: receive timeout\n", __func__);
return -ETIMEDOUT;
}
/* SPDR allows 16 or 32-bit access only */
*data = (u8)rspi_read16(rspi, RSPI_SPDR);
data++;
remain--;
}
return 0;
}
static void rspi_work(struct work_struct *work)
{
struct rspi_data *rspi = container_of(work, struct rspi_data, ws);
struct spi_message *mesg;
struct spi_transfer *t;
unsigned long flags;
int ret;
spin_lock_irqsave(&rspi->lock, flags);
while (!list_empty(&rspi->queue)) {
mesg = list_entry(rspi->queue.next, struct spi_message, queue);
list_del_init(&mesg->queue);
spin_unlock_irqrestore(&rspi->lock, flags);
rspi_assert_ssl(rspi);
list_for_each_entry(t, &mesg->transfers, transfer_list) {
if (t->tx_buf) {
ret = rspi_send_pio(rspi, mesg, t);
if (ret < 0)
goto error;
}
if (t->rx_buf) {
ret = rspi_receive_pio(rspi, mesg, t);
if (ret < 0)
goto error;
}
mesg->actual_length += t->len;
}
rspi_negate_ssl(rspi);
mesg->status = 0;
mesg->complete(mesg->context);
spin_lock_irqsave(&rspi->lock, flags);
}
return;
error:
mesg->status = ret;
mesg->complete(mesg->context);
}
static int rspi_setup(struct spi_device *spi)
{
struct rspi_data *rspi = spi_master_get_devdata(spi->master);
if (!spi->bits_per_word)
spi->bits_per_word = 8;
rspi->max_speed_hz = spi->max_speed_hz;
rspi_set_config_register(rspi, 8);
return 0;
}
static int rspi_transfer(struct spi_device *spi, struct spi_message *mesg)
{
struct rspi_data *rspi = spi_master_get_devdata(spi->master);
unsigned long flags;
mesg->actual_length = 0;
mesg->status = -EINPROGRESS;
spin_lock_irqsave(&rspi->lock, flags);
list_add_tail(&mesg->queue, &rspi->queue);
schedule_work(&rspi->ws);
spin_unlock_irqrestore(&rspi->lock, flags);
return 0;
}
static void rspi_cleanup(struct spi_device *spi)
{
}
static irqreturn_t rspi_irq(int irq, void *_sr)
{
struct rspi_data *rspi = (struct rspi_data *)_sr;
unsigned long spsr;
irqreturn_t ret = IRQ_NONE;
unsigned char disable_irq = 0;
rspi->spsr = spsr = rspi_read8(rspi, RSPI_SPSR);
if (spsr & SPSR_SPRF)
disable_irq |= SPCR_SPRIE;
if (spsr & SPSR_SPTEF)
disable_irq |= SPCR_SPTIE;
if (disable_irq) {
ret = IRQ_HANDLED;
rspi_disable_irq(rspi, disable_irq);
wake_up(&rspi->wait);
}
return ret;
}
static int __devexit rspi_remove(struct platform_device *pdev)
{
struct rspi_data *rspi = dev_get_drvdata(&pdev->dev);
spi_unregister_master(rspi->master);
free_irq(platform_get_irq(pdev, 0), rspi);
clk_put(rspi->clk);
iounmap(rspi->addr);
spi_master_put(rspi->master);
return 0;
}
static int __devinit rspi_probe(struct platform_device *pdev)
{
struct resource *res;
struct spi_master *master;
struct rspi_data *rspi;
int ret, irq;
char clk_name[16];
/* 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 rspi_data));
if (master == NULL) {
dev_err(&pdev->dev, "spi_alloc_master error.\n");
return -ENOMEM;
}
rspi = spi_master_get_devdata(master);
dev_set_drvdata(&pdev->dev, rspi);
rspi->master = master;
rspi->addr = ioremap(res->start, resource_size(res));
if (rspi->addr == NULL) {
dev_err(&pdev->dev, "ioremap error.\n");
ret = -ENOMEM;
goto error1;
}
snprintf(clk_name, sizeof(clk_name), "rspi%d", pdev->id);
rspi->clk = clk_get(&pdev->dev, clk_name);
if (IS_ERR(rspi->clk)) {
dev_err(&pdev->dev, "cannot get clock\n");
ret = PTR_ERR(rspi->clk);
goto error2;
}
clk_enable(rspi->clk);
INIT_LIST_HEAD(&rspi->queue);
spin_lock_init(&rspi->lock);
INIT_WORK(&rspi->ws, rspi_work);
init_waitqueue_head(&rspi->wait);
master->num_chipselect = 2;
master->bus_num = pdev->id;
master->setup = rspi_setup;
master->transfer = rspi_transfer;
master->cleanup = rspi_cleanup;
ret = request_irq(irq, rspi_irq, 0, dev_name(&pdev->dev), rspi);
if (ret < 0) {
dev_err(&pdev->dev, "request_irq error\n");
goto error3;
}
ret = spi_register_master(master);
if (ret < 0) {
dev_err(&pdev->dev, "spi_register_master error.\n");
goto error4;
}
dev_info(&pdev->dev, "probed\n");
return 0;
error4:
free_irq(irq, rspi);
error3:
clk_put(rspi->clk);
error2:
iounmap(rspi->addr);
error1:
spi_master_put(master);
return ret;
}
static struct platform_driver rspi_driver = {
.probe = rspi_probe,
.remove = __devexit_p(rspi_remove),
.driver = {
.name = "rspi",
.owner = THIS_MODULE,
},
};
module_platform_driver(rspi_driver);
MODULE_DESCRIPTION("Renesas RSPI bus driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Yoshihiro Shimoda");
MODULE_ALIAS("platform:rspi");
...@@ -20,10 +20,12 @@ ...@@ -20,10 +20,12 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <mach/dma.h> #include <mach/dma.h>
...@@ -126,8 +128,6 @@ ...@@ -126,8 +128,6 @@
#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
#define SUSPND (1<<0)
#define SPIBUSY (1<<1)
#define RXBUSY (1<<2) #define RXBUSY (1<<2)
#define TXBUSY (1<<3) #define TXBUSY (1<<3)
...@@ -142,10 +142,8 @@ struct s3c64xx_spi_dma_data { ...@@ -142,10 +142,8 @@ struct s3c64xx_spi_dma_data {
* @clk: Pointer to the spi clock. * @clk: Pointer to the spi clock.
* @src_clk: Pointer to the clock used to generate SPI signals. * @src_clk: Pointer to the clock used to generate SPI signals.
* @master: Pointer to the SPI Protocol master. * @master: Pointer to the SPI Protocol master.
* @workqueue: Work queue for the SPI xfer requests.
* @cntrlr_info: Platform specific data for the controller this driver manages. * @cntrlr_info: Platform specific data for the controller this driver manages.
* @tgl_spi: Pointer to the last CS left untoggled by the cs_change hint. * @tgl_spi: Pointer to the last CS left untoggled by the cs_change hint.
* @work: Work
* @queue: To log SPI xfer requests. * @queue: To log SPI xfer requests.
* @lock: Controller specific lock. * @lock: Controller specific lock.
* @state: Set of FLAGS to indicate status. * @state: Set of FLAGS to indicate status.
...@@ -153,6 +151,7 @@ struct s3c64xx_spi_dma_data { ...@@ -153,6 +151,7 @@ struct s3c64xx_spi_dma_data {
* @tx_dmach: Controller's DMA channel for Tx. * @tx_dmach: Controller's DMA channel for Tx.
* @sfr_start: BUS address of SPI controller regs. * @sfr_start: BUS address of SPI controller regs.
* @regs: Pointer to ioremap'ed controller registers. * @regs: Pointer to ioremap'ed controller registers.
* @irq: interrupt
* @xfer_completion: To indicate completion of xfer task. * @xfer_completion: To indicate completion of xfer task.
* @cur_mode: Stores the active configuration of the controller. * @cur_mode: Stores the active configuration of the controller.
* @cur_bpw: Stores the active bits per word settings. * @cur_bpw: Stores the active bits per word settings.
...@@ -164,10 +163,8 @@ struct s3c64xx_spi_driver_data { ...@@ -164,10 +163,8 @@ struct s3c64xx_spi_driver_data {
struct clk *src_clk; struct clk *src_clk;
struct platform_device *pdev; struct platform_device *pdev;
struct spi_master *master; struct spi_master *master;
struct workqueue_struct *workqueue;
struct s3c64xx_spi_info *cntrlr_info; struct s3c64xx_spi_info *cntrlr_info;
struct spi_device *tgl_spi; struct spi_device *tgl_spi;
struct work_struct work;
struct list_head queue; struct list_head queue;
spinlock_t lock; spinlock_t lock;
unsigned long sfr_start; unsigned long sfr_start;
...@@ -239,7 +236,7 @@ static void s3c64xx_spi_dmacb(void *data) ...@@ -239,7 +236,7 @@ static void s3c64xx_spi_dmacb(void *data)
struct s3c64xx_spi_dma_data *dma = data; struct s3c64xx_spi_dma_data *dma = data;
unsigned long flags; unsigned long flags;
if (dma->direction == DMA_FROM_DEVICE) if (dma->direction == DMA_DEV_TO_MEM)
sdd = container_of(data, sdd = container_of(data,
struct s3c64xx_spi_driver_data, rx_dma); struct s3c64xx_spi_driver_data, rx_dma);
else else
...@@ -248,7 +245,7 @@ static void s3c64xx_spi_dmacb(void *data) ...@@ -248,7 +245,7 @@ static void s3c64xx_spi_dmacb(void *data)
spin_lock_irqsave(&sdd->lock, flags); spin_lock_irqsave(&sdd->lock, flags);
if (dma->direction == DMA_FROM_DEVICE) { if (dma->direction == DMA_DEV_TO_MEM) {
sdd->state &= ~RXBUSY; sdd->state &= ~RXBUSY;
if (!(sdd->state & TXBUSY)) if (!(sdd->state & TXBUSY))
complete(&sdd->xfer_completion); complete(&sdd->xfer_completion);
...@@ -267,7 +264,7 @@ static void prepare_dma(struct s3c64xx_spi_dma_data *dma, ...@@ -267,7 +264,7 @@ static void prepare_dma(struct s3c64xx_spi_dma_data *dma,
struct s3c64xx_spi_driver_data *sdd; struct s3c64xx_spi_driver_data *sdd;
struct samsung_dma_prep_info info; struct samsung_dma_prep_info info;
if (dma->direction == DMA_FROM_DEVICE) if (dma->direction == DMA_DEV_TO_MEM)
sdd = container_of((void *)dma, sdd = container_of((void *)dma,
struct s3c64xx_spi_driver_data, rx_dma); struct s3c64xx_spi_driver_data, rx_dma);
else else
...@@ -634,9 +631,10 @@ static void s3c64xx_spi_unmap_mssg(struct s3c64xx_spi_driver_data *sdd, ...@@ -634,9 +631,10 @@ static void s3c64xx_spi_unmap_mssg(struct s3c64xx_spi_driver_data *sdd,
} }
} }
static void handle_msg(struct s3c64xx_spi_driver_data *sdd, static int s3c64xx_spi_transfer_one_message(struct spi_master *master,
struct spi_message *msg) struct spi_message *msg)
{ {
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
struct s3c64xx_spi_info *sci = sdd->cntrlr_info; struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
struct spi_device *spi = msg->spi; struct spi_device *spi = msg->spi;
struct s3c64xx_spi_csinfo *cs = spi->controller_data; struct s3c64xx_spi_csinfo *cs = spi->controller_data;
...@@ -766,73 +764,33 @@ static void handle_msg(struct s3c64xx_spi_driver_data *sdd, ...@@ -766,73 +764,33 @@ static void handle_msg(struct s3c64xx_spi_driver_data *sdd,
msg->status = status; msg->status = status;
if (msg->complete) spi_finalize_current_message(master);
msg->complete(msg->context);
return 0;
} }
static void s3c64xx_spi_work(struct work_struct *work) static int s3c64xx_spi_prepare_transfer(struct spi_master *spi)
{ {
struct s3c64xx_spi_driver_data *sdd = container_of(work, struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi);
struct s3c64xx_spi_driver_data, work);
unsigned long flags;
/* Acquire DMA channels */ /* Acquire DMA channels */
while (!acquire_dma(sdd)) while (!acquire_dma(sdd))
msleep(10); msleep(10);
spin_lock_irqsave(&sdd->lock, flags); pm_runtime_get_sync(&sdd->pdev->dev);
while (!list_empty(&sdd->queue)
&& !(sdd->state & SUSPND)) {
struct spi_message *msg;
msg = container_of(sdd->queue.next, struct spi_message, queue);
list_del_init(&msg->queue);
/* Set Xfer busy flag */
sdd->state |= SPIBUSY;
spin_unlock_irqrestore(&sdd->lock, flags);
handle_msg(sdd, msg); return 0;
}
spin_lock_irqsave(&sdd->lock, flags);
sdd->state &= ~SPIBUSY;
}
spin_unlock_irqrestore(&sdd->lock, flags); static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi)
{
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi);
/* Free DMA channels */ /* Free DMA channels */
sdd->ops->release(sdd->rx_dma.ch, &s3c64xx_spi_dma_client); sdd->ops->release(sdd->rx_dma.ch, &s3c64xx_spi_dma_client);
sdd->ops->release(sdd->tx_dma.ch, &s3c64xx_spi_dma_client); sdd->ops->release(sdd->tx_dma.ch, &s3c64xx_spi_dma_client);
}
static int s3c64xx_spi_transfer(struct spi_device *spi,
struct spi_message *msg)
{
struct s3c64xx_spi_driver_data *sdd;
unsigned long flags;
sdd = spi_master_get_devdata(spi->master);
spin_lock_irqsave(&sdd->lock, flags); pm_runtime_put(&sdd->pdev->dev);
if (sdd->state & SUSPND) {
spin_unlock_irqrestore(&sdd->lock, flags);
return -ESHUTDOWN;
}
msg->status = -EINPROGRESS;
msg->actual_length = 0;
list_add_tail(&msg->queue, &sdd->queue);
queue_work(sdd->workqueue, &sdd->work);
spin_unlock_irqrestore(&sdd->lock, flags);
return 0; return 0;
} }
...@@ -872,13 +830,6 @@ static int s3c64xx_spi_setup(struct spi_device *spi) ...@@ -872,13 +830,6 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
} }
} }
if (sdd->state & SUSPND) {
spin_unlock_irqrestore(&sdd->lock, flags);
dev_err(&spi->dev,
"setup: SPI-%d not active!\n", spi->master->bus_num);
return -ESHUTDOWN;
}
spin_unlock_irqrestore(&sdd->lock, flags); spin_unlock_irqrestore(&sdd->lock, flags);
if (spi->bits_per_word != 8 if (spi->bits_per_word != 8
...@@ -890,6 +841,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi) ...@@ -890,6 +841,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
goto setup_exit; goto setup_exit;
} }
pm_runtime_get_sync(&sdd->pdev->dev);
/* Check if we can provide the requested rate */ /* Check if we can provide the requested rate */
if (!sci->clk_from_cmu) { if (!sci->clk_from_cmu) {
u32 psr, speed; u32 psr, speed;
...@@ -922,6 +875,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi) ...@@ -922,6 +875,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
err = -EINVAL; err = -EINVAL;
} }
pm_runtime_put(&sdd->pdev->dev);
setup_exit: setup_exit:
/* setup() returns with device de-selected */ /* setup() returns with device de-selected */
...@@ -930,6 +885,33 @@ static int s3c64xx_spi_setup(struct spi_device *spi) ...@@ -930,6 +885,33 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
return err; return err;
} }
static irqreturn_t s3c64xx_spi_irq(int irq, void *data)
{
struct s3c64xx_spi_driver_data *sdd = data;
struct spi_master *spi = sdd->master;
unsigned int val;
val = readl(sdd->regs + S3C64XX_SPI_PENDING_CLR);
val &= S3C64XX_SPI_PND_RX_OVERRUN_CLR |
S3C64XX_SPI_PND_RX_UNDERRUN_CLR |
S3C64XX_SPI_PND_TX_OVERRUN_CLR |
S3C64XX_SPI_PND_TX_UNDERRUN_CLR;
writel(val, sdd->regs + S3C64XX_SPI_PENDING_CLR);
if (val & S3C64XX_SPI_PND_RX_OVERRUN_CLR)
dev_err(&spi->dev, "RX overrun\n");
if (val & S3C64XX_SPI_PND_RX_UNDERRUN_CLR)
dev_err(&spi->dev, "RX underrun\n");
if (val & S3C64XX_SPI_PND_TX_OVERRUN_CLR)
dev_err(&spi->dev, "TX overrun\n");
if (val & S3C64XX_SPI_PND_TX_UNDERRUN_CLR)
dev_err(&spi->dev, "TX underrun\n");
return IRQ_HANDLED;
}
static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel) static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel)
{ {
struct s3c64xx_spi_info *sci = sdd->cntrlr_info; struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
...@@ -970,7 +952,7 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) ...@@ -970,7 +952,7 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
struct s3c64xx_spi_driver_data *sdd; struct s3c64xx_spi_driver_data *sdd;
struct s3c64xx_spi_info *sci; struct s3c64xx_spi_info *sci;
struct spi_master *master; struct spi_master *master;
int ret; int ret, irq;
char clk_name[16]; char clk_name[16];
if (pdev->id < 0) { if (pdev->id < 0) {
...@@ -1006,6 +988,12 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) ...@@ -1006,6 +988,12 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
return -ENXIO; return -ENXIO;
} }
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_warn(&pdev->dev, "Failed to get IRQ: %d\n", irq);
return irq;
}
master = spi_alloc_master(&pdev->dev, master = spi_alloc_master(&pdev->dev,
sizeof(struct s3c64xx_spi_driver_data)); sizeof(struct s3c64xx_spi_driver_data));
if (master == NULL) { if (master == NULL) {
...@@ -1021,15 +1009,17 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) ...@@ -1021,15 +1009,17 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
sdd->pdev = pdev; sdd->pdev = pdev;
sdd->sfr_start = mem_res->start; sdd->sfr_start = mem_res->start;
sdd->tx_dma.dmach = dmatx_res->start; sdd->tx_dma.dmach = dmatx_res->start;
sdd->tx_dma.direction = DMA_TO_DEVICE; sdd->tx_dma.direction = DMA_MEM_TO_DEV;
sdd->rx_dma.dmach = dmarx_res->start; sdd->rx_dma.dmach = dmarx_res->start;
sdd->rx_dma.direction = DMA_FROM_DEVICE; sdd->rx_dma.direction = DMA_DEV_TO_MEM;
sdd->cur_bpw = 8; sdd->cur_bpw = 8;
master->bus_num = pdev->id; master->bus_num = pdev->id;
master->setup = s3c64xx_spi_setup; master->setup = s3c64xx_spi_setup;
master->transfer = s3c64xx_spi_transfer; master->prepare_transfer_hardware = s3c64xx_spi_prepare_transfer;
master->transfer_one_message = s3c64xx_spi_transfer_one_message;
master->unprepare_transfer_hardware = s3c64xx_spi_unprepare_transfer;
master->num_chipselect = sci->num_cs; master->num_chipselect = sci->num_cs;
master->dma_alignment = 8; master->dma_alignment = 8;
/* the spi->mode bits understood by this driver: */ /* the spi->mode bits understood by this driver: */
...@@ -1084,22 +1074,24 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) ...@@ -1084,22 +1074,24 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
goto err6; goto err6;
} }
sdd->workqueue = create_singlethread_workqueue(
dev_name(master->dev.parent));
if (sdd->workqueue == NULL) {
dev_err(&pdev->dev, "Unable to create workqueue\n");
ret = -ENOMEM;
goto err7;
}
/* Setup Deufult Mode */ /* Setup Deufult Mode */
s3c64xx_spi_hwinit(sdd, pdev->id); s3c64xx_spi_hwinit(sdd, pdev->id);
spin_lock_init(&sdd->lock); spin_lock_init(&sdd->lock);
init_completion(&sdd->xfer_completion); init_completion(&sdd->xfer_completion);
INIT_WORK(&sdd->work, s3c64xx_spi_work);
INIT_LIST_HEAD(&sdd->queue); INIT_LIST_HEAD(&sdd->queue);
ret = request_irq(irq, s3c64xx_spi_irq, 0, "spi-s3c64xx", sdd);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request IRQ %d: %d\n",
irq, ret);
goto err7;
}
writel(S3C64XX_SPI_INT_RX_OVERRUN_EN | S3C64XX_SPI_INT_RX_UNDERRUN_EN |
S3C64XX_SPI_INT_TX_OVERRUN_EN | S3C64XX_SPI_INT_TX_UNDERRUN_EN,
sdd->regs + S3C64XX_SPI_INT_EN);
if (spi_register_master(master)) { if (spi_register_master(master)) {
dev_err(&pdev->dev, "cannot register SPI master\n"); dev_err(&pdev->dev, "cannot register SPI master\n");
ret = -EBUSY; ret = -EBUSY;
...@@ -1113,10 +1105,12 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) ...@@ -1113,10 +1105,12 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
mem_res->end, mem_res->start, mem_res->end, mem_res->start,
sdd->rx_dma.dmach, sdd->tx_dma.dmach); sdd->rx_dma.dmach, sdd->tx_dma.dmach);
pm_runtime_enable(&pdev->dev);
return 0; return 0;
err8: err8:
destroy_workqueue(sdd->workqueue); free_irq(irq, sdd);
err7: err7:
clk_disable(sdd->src_clk); clk_disable(sdd->src_clk);
err6: err6:
...@@ -1142,18 +1136,14 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) ...@@ -1142,18 +1136,14 @@ static int s3c64xx_spi_remove(struct platform_device *pdev)
struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
struct resource *mem_res; struct resource *mem_res;
unsigned long flags;
spin_lock_irqsave(&sdd->lock, flags);
sdd->state |= SUSPND;
spin_unlock_irqrestore(&sdd->lock, flags);
while (sdd->state & SPIBUSY) pm_runtime_disable(&pdev->dev);
msleep(10);
spi_unregister_master(master); spi_unregister_master(master);
destroy_workqueue(sdd->workqueue); writel(0, sdd->regs + S3C64XX_SPI_INT_EN);
free_irq(platform_get_irq(pdev, 0), sdd);
clk_disable(sdd->src_clk); clk_disable(sdd->src_clk);
clk_put(sdd->src_clk); clk_put(sdd->src_clk);
...@@ -1174,18 +1164,12 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) ...@@ -1174,18 +1164,12 @@ static int s3c64xx_spi_remove(struct platform_device *pdev)
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int s3c64xx_spi_suspend(struct platform_device *pdev, pm_message_t state) static int s3c64xx_spi_suspend(struct device *dev)
{ {
struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
unsigned long flags;
spin_lock_irqsave(&sdd->lock, flags);
sdd->state |= SUSPND;
spin_unlock_irqrestore(&sdd->lock, flags);
while (sdd->state & SPIBUSY) spi_master_suspend(master);
msleep(10);
/* Disable the clock */ /* Disable the clock */
clk_disable(sdd->src_clk); clk_disable(sdd->src_clk);
...@@ -1196,12 +1180,12 @@ static int s3c64xx_spi_suspend(struct platform_device *pdev, pm_message_t state) ...@@ -1196,12 +1180,12 @@ static int s3c64xx_spi_suspend(struct platform_device *pdev, pm_message_t state)
return 0; return 0;
} }
static int s3c64xx_spi_resume(struct platform_device *pdev) static int s3c64xx_spi_resume(struct device *dev)
{ {
struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); struct platform_device *pdev = to_platform_device(dev);
struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
struct s3c64xx_spi_info *sci = sdd->cntrlr_info; struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
unsigned long flags;
sci->cfg_gpio(pdev); sci->cfg_gpio(pdev);
...@@ -1211,25 +1195,49 @@ static int s3c64xx_spi_resume(struct platform_device *pdev) ...@@ -1211,25 +1195,49 @@ static int s3c64xx_spi_resume(struct platform_device *pdev)
s3c64xx_spi_hwinit(sdd, pdev->id); s3c64xx_spi_hwinit(sdd, pdev->id);
spin_lock_irqsave(&sdd->lock, flags); spi_master_resume(master);
sdd->state &= ~SUSPND;
spin_unlock_irqrestore(&sdd->lock, flags);
return 0; return 0;
} }
#else
#define s3c64xx_spi_suspend NULL
#define s3c64xx_spi_resume NULL
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */
#ifdef CONFIG_PM_RUNTIME
static int s3c64xx_spi_runtime_suspend(struct device *dev)
{
struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
clk_disable(sdd->clk);
clk_disable(sdd->src_clk);
return 0;
}
static int s3c64xx_spi_runtime_resume(struct device *dev)
{
struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
clk_enable(sdd->src_clk);
clk_enable(sdd->clk);
return 0;
}
#endif /* CONFIG_PM_RUNTIME */
static const struct dev_pm_ops s3c64xx_spi_pm = {
SET_SYSTEM_SLEEP_PM_OPS(s3c64xx_spi_suspend, s3c64xx_spi_resume)
SET_RUNTIME_PM_OPS(s3c64xx_spi_runtime_suspend,
s3c64xx_spi_runtime_resume, NULL)
};
static struct platform_driver s3c64xx_spi_driver = { static struct platform_driver s3c64xx_spi_driver = {
.driver = { .driver = {
.name = "s3c64xx-spi", .name = "s3c64xx-spi",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &s3c64xx_spi_pm,
}, },
.remove = s3c64xx_spi_remove, .remove = s3c64xx_spi_remove,
.suspend = s3c64xx_spi_suspend,
.resume = s3c64xx_spi_resume,
}; };
MODULE_ALIAS("platform:s3c64xx-spi"); MODULE_ALIAS("platform:s3c64xx-spi");
......
/*
* SuperH HSPI bus driver
*
* Copyright (C) 2011 Kuninori Morimoto
*
* Based on spi-sh.c:
* Based on pxa2xx_spi.c:
* Copyright (C) 2011 Renesas Solutions Corp.
* 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/clk.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/io.h>
#include <linux/spi/spi.h>
#include <linux/spi/sh_hspi.h>
#define SPCR 0x00
#define SPSR 0x04
#define SPSCR 0x08
#define SPTBR 0x0C
#define SPRBR 0x10
#define SPCR2 0x14
/* SPSR */
#define RXFL (1 << 2)
#define hspi2info(h) (h->dev->platform_data)
struct hspi_priv {
void __iomem *addr;
struct spi_master *master;
struct device *dev;
struct clk *clk;
};
/*
* basic function
*/
static void hspi_write(struct hspi_priv *hspi, int reg, u32 val)
{
iowrite32(val, hspi->addr + reg);
}
static u32 hspi_read(struct hspi_priv *hspi, int reg)
{
return ioread32(hspi->addr + reg);
}
/*
* transfer function
*/
static int hspi_status_check_timeout(struct hspi_priv *hspi, u32 mask, u32 val)
{
int t = 256;
while (t--) {
if ((mask & hspi_read(hspi, SPSR)) == val)
return 0;
msleep(20);
}
dev_err(hspi->dev, "timeout\n");
return -ETIMEDOUT;
}
/*
* spi master function
*/
static int hspi_prepare_transfer(struct spi_master *master)
{
struct hspi_priv *hspi = spi_master_get_devdata(master);
pm_runtime_get_sync(hspi->dev);
return 0;
}
static int hspi_unprepare_transfer(struct spi_master *master)
{
struct hspi_priv *hspi = spi_master_get_devdata(master);
pm_runtime_put_sync(hspi->dev);
return 0;
}
static void hspi_hw_setup(struct hspi_priv *hspi,
struct spi_message *msg,
struct spi_transfer *t)
{
struct spi_device *spi = msg->spi;
struct device *dev = hspi->dev;
u32 target_rate;
u32 spcr, idiv_clk;
u32 rate, best_rate, min, tmp;
target_rate = t ? t->speed_hz : 0;
if (!target_rate)
target_rate = spi->max_speed_hz;
/*
* find best IDIV/CLKCx settings
*/
min = ~0;
best_rate = 0;
spcr = 0;
for (idiv_clk = 0x00; idiv_clk <= 0x3F; idiv_clk++) {
rate = clk_get_rate(hspi->clk);
/* IDIV calculation */
if (idiv_clk & (1 << 5))
rate /= 128;
else
rate /= 16;
/* CLKCx calculation */
rate /= (((idiv_clk & 0x1F) + 1) * 2) ;
/* save best settings */
tmp = abs(target_rate - rate);
if (tmp < min) {
min = tmp;
spcr = idiv_clk;
best_rate = rate;
}
}
if (spi->mode & SPI_CPHA)
spcr |= 1 << 7;
if (spi->mode & SPI_CPOL)
spcr |= 1 << 6;
dev_dbg(dev, "speed %d/%d\n", target_rate, best_rate);
hspi_write(hspi, SPCR, spcr);
hspi_write(hspi, SPSR, 0x0);
hspi_write(hspi, SPSCR, 0x1); /* master mode */
}
static int hspi_transfer_one_message(struct spi_master *master,
struct spi_message *msg)
{
struct hspi_priv *hspi = spi_master_get_devdata(master);
struct spi_transfer *t;
u32 tx;
u32 rx;
int ret, i;
dev_dbg(hspi->dev, "%s\n", __func__);
ret = 0;
list_for_each_entry(t, &msg->transfers, transfer_list) {
hspi_hw_setup(hspi, msg, t);
for (i = 0; i < t->len; i++) {
/* wait remains */
ret = hspi_status_check_timeout(hspi, 0x1, 0);
if (ret < 0)
break;
tx = 0;
if (t->tx_buf)
tx = (u32)((u8 *)t->tx_buf)[i];
hspi_write(hspi, SPTBR, tx);
/* wait recive */
ret = hspi_status_check_timeout(hspi, 0x4, 0x4);
if (ret < 0)
break;
rx = hspi_read(hspi, SPRBR);
if (t->rx_buf)
((u8 *)t->rx_buf)[i] = (u8)rx;
}
msg->actual_length += t->len;
}
msg->status = ret;
spi_finalize_current_message(master);
return ret;
}
static int hspi_setup(struct spi_device *spi)
{
struct hspi_priv *hspi = spi_master_get_devdata(spi->master);
struct device *dev = hspi->dev;
if (8 != spi->bits_per_word) {
dev_err(dev, "bits_per_word should be 8\n");
return -EIO;
}
dev_dbg(dev, "%s setup\n", spi->modalias);
return 0;
}
static void hspi_cleanup(struct spi_device *spi)
{
struct hspi_priv *hspi = spi_master_get_devdata(spi->master);
struct device *dev = hspi->dev;
dev_dbg(dev, "%s cleanup\n", spi->modalias);
}
static int __devinit hspi_probe(struct platform_device *pdev)
{
struct resource *res;
struct spi_master *master;
struct hspi_priv *hspi;
struct clk *clk;
int ret;
/* get base addr */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "invalid resource\n");
return -EINVAL;
}
master = spi_alloc_master(&pdev->dev, sizeof(*hspi));
if (!master) {
dev_err(&pdev->dev, "spi_alloc_master error.\n");
return -ENOMEM;
}
clk = clk_get(NULL, "shyway_clk");
if (!clk) {
dev_err(&pdev->dev, "shyway_clk is required\n");
ret = -EINVAL;
goto error0;
}
hspi = spi_master_get_devdata(master);
dev_set_drvdata(&pdev->dev, hspi);
/* init hspi */
hspi->master = master;
hspi->dev = &pdev->dev;
hspi->clk = clk;
hspi->addr = devm_ioremap(hspi->dev,
res->start, resource_size(res));
if (!hspi->addr) {
dev_err(&pdev->dev, "ioremap error.\n");
ret = -ENOMEM;
goto error1;
}
master->num_chipselect = 1;
master->bus_num = pdev->id;
master->setup = hspi_setup;
master->cleanup = hspi_cleanup;
master->mode_bits = SPI_CPOL | SPI_CPHA;
master->prepare_transfer_hardware = hspi_prepare_transfer;
master->transfer_one_message = hspi_transfer_one_message;
master->unprepare_transfer_hardware = hspi_unprepare_transfer;
ret = spi_register_master(master);
if (ret < 0) {
dev_err(&pdev->dev, "spi_register_master error.\n");
goto error2;
}
pm_runtime_enable(&pdev->dev);
dev_info(&pdev->dev, "probed\n");
return 0;
error2:
devm_iounmap(hspi->dev, hspi->addr);
error1:
clk_put(clk);
error0:
spi_master_put(master);
return ret;
}
static int __devexit hspi_remove(struct platform_device *pdev)
{
struct hspi_priv *hspi = dev_get_drvdata(&pdev->dev);
pm_runtime_disable(&pdev->dev);
clk_put(hspi->clk);
spi_unregister_master(hspi->master);
devm_iounmap(hspi->dev, hspi->addr);
return 0;
}
static struct platform_driver hspi_driver = {
.probe = hspi_probe,
.remove = __devexit_p(hspi_remove),
.driver = {
.name = "sh-hspi",
.owner = THIS_MODULE,
},
};
module_platform_driver(hspi_driver);
MODULE_DESCRIPTION("SuperH HSPI bus driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
MODULE_ALIAS("platform:sh_spi");
...@@ -92,17 +92,26 @@ struct spi_sh_data { ...@@ -92,17 +92,26 @@ struct spi_sh_data {
unsigned long cr1; unsigned long cr1;
wait_queue_head_t wait; wait_queue_head_t wait;
spinlock_t lock; spinlock_t lock;
int width;
}; };
static void spi_sh_write(struct spi_sh_data *ss, unsigned long data, static void spi_sh_write(struct spi_sh_data *ss, unsigned long data,
unsigned long offset) unsigned long offset)
{ {
writel(data, ss->addr + offset); if (ss->width == 8)
iowrite8(data, ss->addr + (offset >> 2));
else if (ss->width == 32)
iowrite32(data, ss->addr + offset);
} }
static unsigned long spi_sh_read(struct spi_sh_data *ss, unsigned long offset) static unsigned long spi_sh_read(struct spi_sh_data *ss, unsigned long offset)
{ {
return readl(ss->addr + offset); if (ss->width == 8)
return ioread8(ss->addr + (offset >> 2));
else if (ss->width == 32)
return ioread32(ss->addr + offset);
else
return 0;
} }
static void spi_sh_set_bit(struct spi_sh_data *ss, unsigned long val, static void spi_sh_set_bit(struct spi_sh_data *ss, unsigned long val,
...@@ -464,6 +473,18 @@ static int __devinit spi_sh_probe(struct platform_device *pdev) ...@@ -464,6 +473,18 @@ static int __devinit spi_sh_probe(struct platform_device *pdev)
ss = spi_master_get_devdata(master); ss = spi_master_get_devdata(master);
dev_set_drvdata(&pdev->dev, ss); dev_set_drvdata(&pdev->dev, ss);
switch (res->flags & IORESOURCE_MEM_TYPE_MASK) {
case IORESOURCE_MEM_8BIT:
ss->width = 8;
break;
case IORESOURCE_MEM_32BIT:
ss->width = 32;
break;
default:
dev_err(&pdev->dev, "No support width\n");
ret = -ENODEV;
goto error1;
}
ss->irq = irq; ss->irq = irq;
ss->master = master; ss->master = master;
ss->addr = ioremap(res->start, resource_size(res)); ss->addr = ioremap(res->start, resource_size(res));
......
/*
* SPI bus driver for CSR SiRFprimaII
*
* Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
*
* Licensed under GPLv2 or later.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/pinctrl/pinmux.h>
#define DRIVER_NAME "sirfsoc_spi"
#define SIRFSOC_SPI_CTRL 0x0000
#define SIRFSOC_SPI_CMD 0x0004
#define SIRFSOC_SPI_TX_RX_EN 0x0008
#define SIRFSOC_SPI_INT_EN 0x000C
#define SIRFSOC_SPI_INT_STATUS 0x0010
#define SIRFSOC_SPI_TX_DMA_IO_CTRL 0x0100
#define SIRFSOC_SPI_TX_DMA_IO_LEN 0x0104
#define SIRFSOC_SPI_TXFIFO_CTRL 0x0108
#define SIRFSOC_SPI_TXFIFO_LEVEL_CHK 0x010C
#define SIRFSOC_SPI_TXFIFO_OP 0x0110
#define SIRFSOC_SPI_TXFIFO_STATUS 0x0114
#define SIRFSOC_SPI_TXFIFO_DATA 0x0118
#define SIRFSOC_SPI_RX_DMA_IO_CTRL 0x0120
#define SIRFSOC_SPI_RX_DMA_IO_LEN 0x0124
#define SIRFSOC_SPI_RXFIFO_CTRL 0x0128
#define SIRFSOC_SPI_RXFIFO_LEVEL_CHK 0x012C
#define SIRFSOC_SPI_RXFIFO_OP 0x0130
#define SIRFSOC_SPI_RXFIFO_STATUS 0x0134
#define SIRFSOC_SPI_RXFIFO_DATA 0x0138
#define SIRFSOC_SPI_DUMMY_DELAY_CTL 0x0144
/* SPI CTRL register defines */
#define SIRFSOC_SPI_SLV_MODE BIT(16)
#define SIRFSOC_SPI_CMD_MODE BIT(17)
#define SIRFSOC_SPI_CS_IO_OUT BIT(18)
#define SIRFSOC_SPI_CS_IO_MODE BIT(19)
#define SIRFSOC_SPI_CLK_IDLE_STAT BIT(20)
#define SIRFSOC_SPI_CS_IDLE_STAT BIT(21)
#define SIRFSOC_SPI_TRAN_MSB BIT(22)
#define SIRFSOC_SPI_DRV_POS_EDGE BIT(23)
#define SIRFSOC_SPI_CS_HOLD_TIME BIT(24)
#define SIRFSOC_SPI_CLK_SAMPLE_MODE BIT(25)
#define SIRFSOC_SPI_TRAN_DAT_FORMAT_8 (0 << 26)
#define SIRFSOC_SPI_TRAN_DAT_FORMAT_12 (1 << 26)
#define SIRFSOC_SPI_TRAN_DAT_FORMAT_16 (2 << 26)
#define SIRFSOC_SPI_TRAN_DAT_FORMAT_32 (3 << 26)
#define SIRFSOC_SPI_CMD_BYTE_NUM(x) ((x & 3) << 28)
#define SIRFSOC_SPI_ENA_AUTO_CLR BIT(30)
#define SIRFSOC_SPI_MUL_DAT_MODE BIT(31)
/* Interrupt Enable */
#define SIRFSOC_SPI_RX_DONE_INT_EN BIT(0)
#define SIRFSOC_SPI_TX_DONE_INT_EN BIT(1)
#define SIRFSOC_SPI_RX_OFLOW_INT_EN BIT(2)
#define SIRFSOC_SPI_TX_UFLOW_INT_EN BIT(3)
#define SIRFSOC_SPI_RX_IO_DMA_INT_EN BIT(4)
#define SIRFSOC_SPI_TX_IO_DMA_INT_EN BIT(5)
#define SIRFSOC_SPI_RXFIFO_FULL_INT_EN BIT(6)
#define SIRFSOC_SPI_TXFIFO_EMPTY_INT_EN BIT(7)
#define SIRFSOC_SPI_RXFIFO_THD_INT_EN BIT(8)
#define SIRFSOC_SPI_TXFIFO_THD_INT_EN BIT(9)
#define SIRFSOC_SPI_FRM_END_INT_EN BIT(10)
#define SIRFSOC_SPI_INT_MASK_ALL 0x1FFF
/* Interrupt status */
#define SIRFSOC_SPI_RX_DONE BIT(0)
#define SIRFSOC_SPI_TX_DONE BIT(1)
#define SIRFSOC_SPI_RX_OFLOW BIT(2)
#define SIRFSOC_SPI_TX_UFLOW BIT(3)
#define SIRFSOC_SPI_RX_FIFO_FULL BIT(6)
#define SIRFSOC_SPI_TXFIFO_EMPTY BIT(7)
#define SIRFSOC_SPI_RXFIFO_THD_REACH BIT(8)
#define SIRFSOC_SPI_TXFIFO_THD_REACH BIT(9)
#define SIRFSOC_SPI_FRM_END BIT(10)
/* TX RX enable */
#define SIRFSOC_SPI_RX_EN BIT(0)
#define SIRFSOC_SPI_TX_EN BIT(1)
#define SIRFSOC_SPI_CMD_TX_EN BIT(2)
#define SIRFSOC_SPI_IO_MODE_SEL BIT(0)
#define SIRFSOC_SPI_RX_DMA_FLUSH BIT(2)
/* FIFO OPs */
#define SIRFSOC_SPI_FIFO_RESET BIT(0)
#define SIRFSOC_SPI_FIFO_START BIT(1)
/* FIFO CTRL */
#define SIRFSOC_SPI_FIFO_WIDTH_BYTE (0 << 0)
#define SIRFSOC_SPI_FIFO_WIDTH_WORD (1 << 0)
#define SIRFSOC_SPI_FIFO_WIDTH_DWORD (2 << 0)
/* FIFO Status */
#define SIRFSOC_SPI_FIFO_LEVEL_MASK 0xFF
#define SIRFSOC_SPI_FIFO_FULL BIT(8)
#define SIRFSOC_SPI_FIFO_EMPTY BIT(9)
/* 256 bytes rx/tx FIFO */
#define SIRFSOC_SPI_FIFO_SIZE 256
#define SIRFSOC_SPI_DAT_FRM_LEN_MAX (64 * 1024)
#define SIRFSOC_SPI_FIFO_SC(x) ((x) & 0x3F)
#define SIRFSOC_SPI_FIFO_LC(x) (((x) & 0x3F) << 10)
#define SIRFSOC_SPI_FIFO_HC(x) (((x) & 0x3F) << 20)
#define SIRFSOC_SPI_FIFO_THD(x) (((x) & 0xFF) << 2)
struct sirfsoc_spi {
struct spi_bitbang bitbang;
struct completion done;
void __iomem *base;
u32 ctrl_freq; /* SPI controller clock speed */
struct clk *clk;
struct pinmux *pmx;
/* rx & tx bufs from the spi_transfer */
const void *tx;
void *rx;
/* place received word into rx buffer */
void (*rx_word) (struct sirfsoc_spi *);
/* get word from tx buffer for sending */
void (*tx_word) (struct sirfsoc_spi *);
/* number of words left to be tranmitted/received */
unsigned int left_tx_cnt;
unsigned int left_rx_cnt;
/* tasklet to push tx msg into FIFO */
struct tasklet_struct tasklet_tx;
int chipselect[0];
};
static void spi_sirfsoc_rx_word_u8(struct sirfsoc_spi *sspi)
{
u32 data;
u8 *rx = sspi->rx;
data = readl(sspi->base + SIRFSOC_SPI_RXFIFO_DATA);
if (rx) {
*rx++ = (u8) data;
sspi->rx = rx;
}
sspi->left_rx_cnt--;
}
static void spi_sirfsoc_tx_word_u8(struct sirfsoc_spi *sspi)
{
u32 data = 0;
const u8 *tx = sspi->tx;
if (tx) {
data = *tx++;
sspi->tx = tx;
}
writel(data, sspi->base + SIRFSOC_SPI_TXFIFO_DATA);
sspi->left_tx_cnt--;
}
static void spi_sirfsoc_rx_word_u16(struct sirfsoc_spi *sspi)
{
u32 data;
u16 *rx = sspi->rx;
data = readl(sspi->base + SIRFSOC_SPI_RXFIFO_DATA);
if (rx) {
*rx++ = (u16) data;
sspi->rx = rx;
}
sspi->left_rx_cnt--;
}
static void spi_sirfsoc_tx_word_u16(struct sirfsoc_spi *sspi)
{
u32 data = 0;
const u16 *tx = sspi->tx;
if (tx) {
data = *tx++;
sspi->tx = tx;
}
writel(data, sspi->base + SIRFSOC_SPI_TXFIFO_DATA);
sspi->left_tx_cnt--;
}
static void spi_sirfsoc_rx_word_u32(struct sirfsoc_spi *sspi)
{
u32 data;
u32 *rx = sspi->rx;
data = readl(sspi->base + SIRFSOC_SPI_RXFIFO_DATA);
if (rx) {
*rx++ = (u32) data;
sspi->rx = rx;
}
sspi->left_rx_cnt--;
}
static void spi_sirfsoc_tx_word_u32(struct sirfsoc_spi *sspi)
{
u32 data = 0;
const u32 *tx = sspi->tx;
if (tx) {
data = *tx++;
sspi->tx = tx;
}
writel(data, sspi->base + SIRFSOC_SPI_TXFIFO_DATA);
sspi->left_tx_cnt--;
}
static void spi_sirfsoc_tasklet_tx(unsigned long arg)
{
struct sirfsoc_spi *sspi = (struct sirfsoc_spi *)arg;
/* Fill Tx FIFO while there are left words to be transmitted */
while (!((readl(sspi->base + SIRFSOC_SPI_TXFIFO_STATUS) &
SIRFSOC_SPI_FIFO_FULL)) &&
sspi->left_tx_cnt)
sspi->tx_word(sspi);
}
static irqreturn_t spi_sirfsoc_irq(int irq, void *dev_id)
{
struct sirfsoc_spi *sspi = dev_id;
u32 spi_stat = readl(sspi->base + SIRFSOC_SPI_INT_STATUS);
writel(spi_stat, sspi->base + SIRFSOC_SPI_INT_STATUS);
/* Error Conditions */
if (spi_stat & SIRFSOC_SPI_RX_OFLOW ||
spi_stat & SIRFSOC_SPI_TX_UFLOW) {
complete(&sspi->done);
writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN);
}
if (spi_stat & SIRFSOC_SPI_FRM_END) {
while (!((readl(sspi->base + SIRFSOC_SPI_RXFIFO_STATUS)
& SIRFSOC_SPI_FIFO_EMPTY)) &&
sspi->left_rx_cnt)
sspi->rx_word(sspi);
/* Received all words */
if ((sspi->left_rx_cnt == 0) && (sspi->left_tx_cnt == 0)) {
complete(&sspi->done);
writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN);
}
}
if (spi_stat & SIRFSOC_SPI_RXFIFO_THD_REACH ||
spi_stat & SIRFSOC_SPI_TXFIFO_THD_REACH ||
spi_stat & SIRFSOC_SPI_RX_FIFO_FULL ||
spi_stat & SIRFSOC_SPI_TXFIFO_EMPTY)
tasklet_schedule(&sspi->tasklet_tx);
return IRQ_HANDLED;
}
static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t)
{
struct sirfsoc_spi *sspi;
int timeout = t->len * 10;
sspi = spi_master_get_devdata(spi->master);
sspi->tx = t->tx_buf;
sspi->rx = t->rx_buf;
sspi->left_tx_cnt = sspi->left_rx_cnt = t->len;
INIT_COMPLETION(sspi->done);
writel(SIRFSOC_SPI_INT_MASK_ALL, sspi->base + SIRFSOC_SPI_INT_STATUS);
if (t->len == 1) {
writel(readl(sspi->base + SIRFSOC_SPI_CTRL) |
SIRFSOC_SPI_ENA_AUTO_CLR,
sspi->base + SIRFSOC_SPI_CTRL);
writel(0, sspi->base + SIRFSOC_SPI_TX_DMA_IO_LEN);
writel(0, sspi->base + SIRFSOC_SPI_RX_DMA_IO_LEN);
} else if ((t->len > 1) && (t->len < SIRFSOC_SPI_DAT_FRM_LEN_MAX)) {
writel(readl(sspi->base + SIRFSOC_SPI_CTRL) |
SIRFSOC_SPI_MUL_DAT_MODE |
SIRFSOC_SPI_ENA_AUTO_CLR,
sspi->base + SIRFSOC_SPI_CTRL);
writel(t->len - 1, sspi->base + SIRFSOC_SPI_TX_DMA_IO_LEN);
writel(t->len - 1, sspi->base + SIRFSOC_SPI_RX_DMA_IO_LEN);
} else {
writel(readl(sspi->base + SIRFSOC_SPI_CTRL),
sspi->base + SIRFSOC_SPI_CTRL);
writel(0, sspi->base + SIRFSOC_SPI_TX_DMA_IO_LEN);
writel(0, sspi->base + SIRFSOC_SPI_RX_DMA_IO_LEN);
}
writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
/* Send the first word to trigger the whole tx/rx process */
sspi->tx_word(sspi);
writel(SIRFSOC_SPI_RX_OFLOW_INT_EN | SIRFSOC_SPI_TX_UFLOW_INT_EN |
SIRFSOC_SPI_RXFIFO_THD_INT_EN | SIRFSOC_SPI_TXFIFO_THD_INT_EN |
SIRFSOC_SPI_FRM_END_INT_EN | SIRFSOC_SPI_RXFIFO_FULL_INT_EN |
SIRFSOC_SPI_TXFIFO_EMPTY_INT_EN, sspi->base + SIRFSOC_SPI_INT_EN);
writel(SIRFSOC_SPI_RX_EN | SIRFSOC_SPI_TX_EN, sspi->base + SIRFSOC_SPI_TX_RX_EN);
if (wait_for_completion_timeout(&sspi->done, timeout) == 0)
dev_err(&spi->dev, "transfer timeout\n");
/* TX, RX FIFO stop */
writel(0, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
writel(0, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
writel(0, sspi->base + SIRFSOC_SPI_TX_RX_EN);
writel(0, sspi->base + SIRFSOC_SPI_INT_EN);
return t->len - sspi->left_rx_cnt;
}
static void spi_sirfsoc_chipselect(struct spi_device *spi, int value)
{
struct sirfsoc_spi *sspi = spi_master_get_devdata(spi->master);
if (sspi->chipselect[spi->chip_select] == 0) {
u32 regval = readl(sspi->base + SIRFSOC_SPI_CTRL);
regval |= SIRFSOC_SPI_CS_IO_OUT;
switch (value) {
case BITBANG_CS_ACTIVE:
if (spi->mode & SPI_CS_HIGH)
regval |= SIRFSOC_SPI_CS_IO_OUT;
else
regval &= ~SIRFSOC_SPI_CS_IO_OUT;
break;
case BITBANG_CS_INACTIVE:
if (spi->mode & SPI_CS_HIGH)
regval &= ~SIRFSOC_SPI_CS_IO_OUT;
else
regval |= SIRFSOC_SPI_CS_IO_OUT;
break;
}
writel(regval, sspi->base + SIRFSOC_SPI_CTRL);
} else {
int gpio = sspi->chipselect[spi->chip_select];
gpio_direction_output(gpio, spi->mode & SPI_CS_HIGH ? 0 : 1);
}
}
static int
spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
{
struct sirfsoc_spi *sspi;
u8 bits_per_word = 0;
int hz = 0;
u32 regval;
u32 txfifo_ctrl, rxfifo_ctrl;
u32 fifo_size = SIRFSOC_SPI_FIFO_SIZE / 4;
sspi = spi_master_get_devdata(spi->master);
bits_per_word = t && t->bits_per_word ? t->bits_per_word :
spi->bits_per_word;
hz = t && t->speed_hz ? t->speed_hz : spi->max_speed_hz;
/* Enable IO mode for RX, TX */
writel(SIRFSOC_SPI_IO_MODE_SEL, sspi->base + SIRFSOC_SPI_TX_DMA_IO_CTRL);
writel(SIRFSOC_SPI_IO_MODE_SEL, sspi->base + SIRFSOC_SPI_RX_DMA_IO_CTRL);
regval = (sspi->ctrl_freq / (2 * hz)) - 1;
if (regval > 0xFFFF || regval < 0) {
dev_err(&spi->dev, "Speed %d not supported\n", hz);
return -EINVAL;
}
switch (bits_per_word) {
case 8:
regval |= SIRFSOC_SPI_TRAN_DAT_FORMAT_8;
sspi->rx_word = spi_sirfsoc_rx_word_u8;
sspi->tx_word = spi_sirfsoc_tx_word_u8;
txfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
SIRFSOC_SPI_FIFO_WIDTH_BYTE;
rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
SIRFSOC_SPI_FIFO_WIDTH_BYTE;
break;
case 12:
case 16:
regval |= (bits_per_word == 12) ? SIRFSOC_SPI_TRAN_DAT_FORMAT_12 :
SIRFSOC_SPI_TRAN_DAT_FORMAT_16;
sspi->rx_word = spi_sirfsoc_rx_word_u16;
sspi->tx_word = spi_sirfsoc_tx_word_u16;
txfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
SIRFSOC_SPI_FIFO_WIDTH_WORD;
rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
SIRFSOC_SPI_FIFO_WIDTH_WORD;
break;
case 32:
regval |= SIRFSOC_SPI_TRAN_DAT_FORMAT_32;
sspi->rx_word = spi_sirfsoc_rx_word_u32;
sspi->tx_word = spi_sirfsoc_tx_word_u32;
txfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
SIRFSOC_SPI_FIFO_WIDTH_DWORD;
rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
SIRFSOC_SPI_FIFO_WIDTH_DWORD;
break;
default:
dev_err(&spi->dev, "Bits per word %d not supported\n",
bits_per_word);
return -EINVAL;
}
if (!(spi->mode & SPI_CS_HIGH))
regval |= SIRFSOC_SPI_CS_IDLE_STAT;
if (!(spi->mode & SPI_LSB_FIRST))
regval |= SIRFSOC_SPI_TRAN_MSB;
if (spi->mode & SPI_CPOL)
regval |= SIRFSOC_SPI_CLK_IDLE_STAT;
/*
* Data should be driven at least 1/2 cycle before the fetch edge to make
* sure that data gets stable at the fetch edge.
*/
if (((spi->mode & SPI_CPOL) && (spi->mode & SPI_CPHA)) ||
(!(spi->mode & SPI_CPOL) && !(spi->mode & SPI_CPHA)))
regval &= ~SIRFSOC_SPI_DRV_POS_EDGE;
else
regval |= SIRFSOC_SPI_DRV_POS_EDGE;
writel(SIRFSOC_SPI_FIFO_SC(fifo_size - 2) |
SIRFSOC_SPI_FIFO_LC(fifo_size / 2) |
SIRFSOC_SPI_FIFO_HC(2),
sspi->base + SIRFSOC_SPI_TXFIFO_LEVEL_CHK);
writel(SIRFSOC_SPI_FIFO_SC(2) |
SIRFSOC_SPI_FIFO_LC(fifo_size / 2) |
SIRFSOC_SPI_FIFO_HC(fifo_size - 2),
sspi->base + SIRFSOC_SPI_RXFIFO_LEVEL_CHK);
writel(txfifo_ctrl, sspi->base + SIRFSOC_SPI_TXFIFO_CTRL);
writel(rxfifo_ctrl, sspi->base + SIRFSOC_SPI_RXFIFO_CTRL);
writel(regval, sspi->base + SIRFSOC_SPI_CTRL);
return 0;
}
static int spi_sirfsoc_setup(struct spi_device *spi)
{
struct sirfsoc_spi *sspi;
if (!spi->max_speed_hz)
return -EINVAL;
sspi = spi_master_get_devdata(spi->master);
if (!spi->bits_per_word)
spi->bits_per_word = 8;
return spi_sirfsoc_setup_transfer(spi, NULL);
}
static int __devinit spi_sirfsoc_probe(struct platform_device *pdev)
{
struct sirfsoc_spi *sspi;
struct spi_master *master;
struct resource *mem_res;
int num_cs, cs_gpio, irq;
int i;
int ret;
ret = of_property_read_u32(pdev->dev.of_node,
"sirf,spi-num-chipselects", &num_cs);
if (ret < 0) {
dev_err(&pdev->dev, "Unable to get chip select number\n");
goto err_cs;
}
master = spi_alloc_master(&pdev->dev, sizeof(*sspi) + sizeof(int) * num_cs);
if (!master) {
dev_err(&pdev->dev, "Unable to allocate SPI master\n");
return -ENOMEM;
}
platform_set_drvdata(pdev, master);
sspi = spi_master_get_devdata(master);
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem_res) {
dev_err(&pdev->dev, "Unable to get IO resource\n");
ret = -ENODEV;
goto free_master;
}
master->num_chipselect = num_cs;
for (i = 0; i < master->num_chipselect; i++) {
cs_gpio = of_get_named_gpio(pdev->dev.of_node, "cs-gpios", i);
if (cs_gpio < 0) {
dev_err(&pdev->dev, "can't get cs gpio from DT\n");
ret = -ENODEV;
goto free_master;
}
sspi->chipselect[i] = cs_gpio;
if (cs_gpio == 0)
continue; /* use cs from spi controller */
ret = gpio_request(cs_gpio, DRIVER_NAME);
if (ret) {
while (i > 0) {
i--;
if (sspi->chipselect[i] > 0)
gpio_free(sspi->chipselect[i]);
}
dev_err(&pdev->dev, "fail to request cs gpios\n");
goto free_master;
}
}
sspi->base = devm_request_and_ioremap(&pdev->dev, mem_res);
if (!sspi->base) {
dev_err(&pdev->dev, "IO remap failed!\n");
ret = -ENOMEM;
goto free_master;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
ret = -ENXIO;
goto free_master;
}
ret = devm_request_irq(&pdev->dev, irq, spi_sirfsoc_irq, 0,
DRIVER_NAME, sspi);
if (ret)
goto free_master;
sspi->bitbang.master = spi_master_get(master);
sspi->bitbang.chipselect = spi_sirfsoc_chipselect;
sspi->bitbang.setup_transfer = spi_sirfsoc_setup_transfer;
sspi->bitbang.txrx_bufs = spi_sirfsoc_transfer;
sspi->bitbang.master->setup = spi_sirfsoc_setup;
master->bus_num = pdev->id;
sspi->bitbang.master->dev.of_node = pdev->dev.of_node;
sspi->pmx = pinmux_get(&pdev->dev, NULL);
ret = IS_ERR(sspi->pmx);
if (ret)
goto free_master;
pinmux_enable(sspi->pmx);
sspi->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(sspi->clk)) {
ret = -EINVAL;
goto free_pmx;
}
clk_enable(sspi->clk);
sspi->ctrl_freq = clk_get_rate(sspi->clk);
init_completion(&sspi->done);
tasklet_init(&sspi->tasklet_tx, spi_sirfsoc_tasklet_tx,
(unsigned long)sspi);
writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
/* We are not using dummy delay between command and data */
writel(0, sspi->base + SIRFSOC_SPI_DUMMY_DELAY_CTL);
ret = spi_bitbang_start(&sspi->bitbang);
if (ret)
goto free_clk;
dev_info(&pdev->dev, "registerred, bus number = %d\n", master->bus_num);
return 0;
free_clk:
clk_disable(sspi->clk);
clk_put(sspi->clk);
free_pmx:
pinmux_disable(sspi->pmx);
pinmux_put(sspi->pmx);
free_master:
spi_master_put(master);
err_cs:
return ret;
}
static int __devexit spi_sirfsoc_remove(struct platform_device *pdev)
{
struct spi_master *master;
struct sirfsoc_spi *sspi;
int i;
master = platform_get_drvdata(pdev);
sspi = spi_master_get_devdata(master);
spi_bitbang_stop(&sspi->bitbang);
for (i = 0; i < master->num_chipselect; i++) {
if (sspi->chipselect[i] > 0)
gpio_free(sspi->chipselect[i]);
}
clk_disable(sspi->clk);
clk_put(sspi->clk);
pinmux_disable(sspi->pmx);
pinmux_put(sspi->pmx);
spi_master_put(master);
return 0;
}
#ifdef CONFIG_PM
static int spi_sirfsoc_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct spi_master *master = platform_get_drvdata(pdev);
struct sirfsoc_spi *sspi = spi_master_get_devdata(master);
clk_disable(sspi->clk);
return 0;
}
static int spi_sirfsoc_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct spi_master *master = platform_get_drvdata(pdev);
struct sirfsoc_spi *sspi = spi_master_get_devdata(master);
clk_enable(sspi->clk);
writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
return 0;
}
static const struct dev_pm_ops spi_sirfsoc_pm_ops = {
.suspend = spi_sirfsoc_suspend,
.resume = spi_sirfsoc_resume,
};
#endif
static const struct of_device_id spi_sirfsoc_of_match[] = {
{ .compatible = "sirf,prima2-spi", },
{}
};
MODULE_DEVICE_TABLE(of, sirfsoc_spi_of_match);
static struct platform_driver spi_sirfsoc_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
#ifdef CONFIG_PM
.pm = &spi_sirfsoc_pm_ops,
#endif
.of_match_table = spi_sirfsoc_of_match,
},
.probe = spi_sirfsoc_probe,
.remove = __devexit_p(spi_sirfsoc_remove),
};
module_platform_driver(spi_sirfsoc_driver);
MODULE_DESCRIPTION("SiRF SoC SPI master driver");
MODULE_AUTHOR("Zhiwu Song <Zhiwu.Song@csr.com>, "
"Barry Song <Baohua.Song@csr.com>");
MODULE_LICENSE("GPL v2");
...@@ -196,6 +196,7 @@ struct pch_spi_data { ...@@ -196,6 +196,7 @@ struct pch_spi_data {
struct pch_spi_dma_ctrl dma; struct pch_spi_dma_ctrl dma;
int use_dma; int use_dma;
u8 irq_reg_sts; u8 irq_reg_sts;
int save_total_len;
}; };
/** /**
...@@ -216,7 +217,7 @@ struct pch_pd_dev_save { ...@@ -216,7 +217,7 @@ struct pch_pd_dev_save {
struct pch_spi_board_data *board_dat; struct pch_spi_board_data *board_dat;
}; };
static struct pci_device_id pch_spi_pcidev_id[] = { static DEFINE_PCI_DEVICE_TABLE(pch_spi_pcidev_id) = {
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_GE_SPI), 1, }, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_GE_SPI), 1, },
{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_SPI), 2, }, { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_SPI), 2, },
{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_SPI), 1, }, { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_SPI), 1, },
...@@ -318,13 +319,12 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val, ...@@ -318,13 +319,12 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val,
data->tx_index = tx_index; data->tx_index = tx_index;
data->rx_index = rx_index; data->rx_index = rx_index;
}
/* if transfer complete interrupt */ /* if transfer complete interrupt */
if (reg_spsr_val & SPSR_FI_BIT) { if (reg_spsr_val & SPSR_FI_BIT) {
if ((tx_index == bpw_len) && (rx_index == tx_index)) { if ((tx_index == bpw_len) && (rx_index == tx_index)) {
/* disable interrupts */ /* disable interrupts */
pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL); pch_spi_setclr_reg(data->master, PCH_SPCR, 0,
PCH_ALL);
/* transfer is completed; /* transfer is completed;
inform pch_spi_process_messages */ inform pch_spi_process_messages */
...@@ -333,7 +333,9 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val, ...@@ -333,7 +333,9 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val,
wake_up(&data->wait); wake_up(&data->wait);
} else { } else {
dev_err(&data->master->dev, dev_err(&data->master->dev,
"%s : Transfer is not completed", __func__); "%s : Transfer is not completed",
__func__);
}
} }
} }
} }
...@@ -822,11 +824,13 @@ static void pch_spi_copy_rx_data_for_dma(struct pch_spi_data *data, int bpw) ...@@ -822,11 +824,13 @@ static void pch_spi_copy_rx_data_for_dma(struct pch_spi_data *data, int bpw)
rx_dma_buf = data->dma.rx_buf_virt; rx_dma_buf = data->dma.rx_buf_virt;
for (j = 0; j < data->bpw_len; j++) for (j = 0; j < data->bpw_len; j++)
*rx_buf++ = *rx_dma_buf++ & 0xFF; *rx_buf++ = *rx_dma_buf++ & 0xFF;
data->cur_trans->rx_buf = rx_buf;
} else { } else {
rx_sbuf = data->cur_trans->rx_buf; rx_sbuf = data->cur_trans->rx_buf;
rx_dma_sbuf = data->dma.rx_buf_virt; rx_dma_sbuf = data->dma.rx_buf_virt;
for (j = 0; j < data->bpw_len; j++) for (j = 0; j < data->bpw_len; j++)
*rx_sbuf++ = *rx_dma_sbuf++; *rx_sbuf++ = *rx_dma_sbuf++;
data->cur_trans->rx_buf = rx_sbuf;
} }
} }
...@@ -852,6 +856,9 @@ static int pch_spi_start_transfer(struct pch_spi_data *data) ...@@ -852,6 +856,9 @@ static int pch_spi_start_transfer(struct pch_spi_data *data)
rtn = wait_event_interruptible_timeout(data->wait, rtn = wait_event_interruptible_timeout(data->wait,
data->transfer_complete, data->transfer_complete,
msecs_to_jiffies(2 * HZ)); msecs_to_jiffies(2 * HZ));
if (!rtn)
dev_err(&data->master->dev,
"%s wait-event timeout\n", __func__);
dma_sync_sg_for_cpu(&data->master->dev, dma->sg_rx_p, dma->nent, dma_sync_sg_for_cpu(&data->master->dev, dma->sg_rx_p, dma->nent,
DMA_FROM_DEVICE); DMA_FROM_DEVICE);
...@@ -923,7 +930,8 @@ static void pch_spi_request_dma(struct pch_spi_data *data, int bpw) ...@@ -923,7 +930,8 @@ static void pch_spi_request_dma(struct pch_spi_data *data, int bpw)
dma_cap_set(DMA_SLAVE, mask); dma_cap_set(DMA_SLAVE, mask);
/* Get DMA's dev information */ /* Get DMA's dev information */
dma_dev = pci_get_bus_and_slot(2, PCI_DEVFN(12, 0)); dma_dev = pci_get_bus_and_slot(data->board_dat->pdev->bus->number,
PCI_DEVFN(12, 0));
/* Set Tx DMA */ /* Set Tx DMA */
param = &dma->param_tx; param = &dma->param_tx;
...@@ -987,6 +995,7 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw) ...@@ -987,6 +995,7 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw)
int i; int i;
int size; int size;
int rem; int rem;
int head;
unsigned long flags; unsigned long flags;
struct pch_spi_dma_ctrl *dma; struct pch_spi_dma_ctrl *dma;
...@@ -1015,6 +1024,11 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw) ...@@ -1015,6 +1024,11 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw)
} }
data->bpw_len = data->cur_trans->len / (*bpw / 8); data->bpw_len = data->cur_trans->len / (*bpw / 8);
if (data->bpw_len > PCH_BUF_SIZE) {
data->bpw_len = PCH_BUF_SIZE;
data->cur_trans->len -= PCH_BUF_SIZE;
}
/* copy Tx Data */ /* copy Tx Data */
if (data->cur_trans->tx_buf != NULL) { if (data->cur_trans->tx_buf != NULL) {
if (*bpw == 8) { if (*bpw == 8) {
...@@ -1029,10 +1043,17 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw) ...@@ -1029,10 +1043,17 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw)
*tx_dma_sbuf++ = *tx_sbuf++; *tx_dma_sbuf++ = *tx_sbuf++;
} }
} }
/* Calculate Rx parameter for DMA transmitting */
if (data->bpw_len > PCH_DMA_TRANS_SIZE) { if (data->bpw_len > PCH_DMA_TRANS_SIZE) {
if (data->bpw_len % PCH_DMA_TRANS_SIZE) {
num = data->bpw_len / PCH_DMA_TRANS_SIZE + 1; num = data->bpw_len / PCH_DMA_TRANS_SIZE + 1;
size = PCH_DMA_TRANS_SIZE;
rem = data->bpw_len % PCH_DMA_TRANS_SIZE; rem = data->bpw_len % PCH_DMA_TRANS_SIZE;
} else {
num = data->bpw_len / PCH_DMA_TRANS_SIZE;
rem = PCH_DMA_TRANS_SIZE;
}
size = PCH_DMA_TRANS_SIZE;
} else { } else {
num = 1; num = 1;
size = data->bpw_len; size = data->bpw_len;
...@@ -1092,15 +1113,23 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw) ...@@ -1092,15 +1113,23 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw)
dma->nent = num; dma->nent = num;
dma->desc_rx = desc_rx; dma->desc_rx = desc_rx;
/* TX */ /* Calculate Tx parameter for DMA transmitting */
if (data->bpw_len > PCH_DMA_TRANS_SIZE) { if (data->bpw_len > PCH_MAX_FIFO_DEPTH) {
head = PCH_MAX_FIFO_DEPTH - PCH_DMA_TRANS_SIZE;
if (data->bpw_len % PCH_DMA_TRANS_SIZE > 4) {
num = data->bpw_len / PCH_DMA_TRANS_SIZE + 1;
rem = data->bpw_len % PCH_DMA_TRANS_SIZE - head;
} else {
num = data->bpw_len / PCH_DMA_TRANS_SIZE; num = data->bpw_len / PCH_DMA_TRANS_SIZE;
rem = data->bpw_len % PCH_DMA_TRANS_SIZE +
PCH_DMA_TRANS_SIZE - head;
}
size = PCH_DMA_TRANS_SIZE; size = PCH_DMA_TRANS_SIZE;
rem = 16;
} else { } else {
num = 1; num = 1;
size = data->bpw_len; size = data->bpw_len;
rem = data->bpw_len; rem = data->bpw_len;
head = 0;
} }
dma->sg_tx_p = kzalloc(sizeof(struct scatterlist)*num, GFP_ATOMIC); dma->sg_tx_p = kzalloc(sizeof(struct scatterlist)*num, GFP_ATOMIC);
...@@ -1110,11 +1139,17 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw) ...@@ -1110,11 +1139,17 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw)
for (i = 0; i < num; i++, sg++) { for (i = 0; i < num; i++, sg++) {
if (i == 0) { if (i == 0) {
sg->offset = 0; sg->offset = 0;
sg_set_page(sg, virt_to_page(dma->tx_buf_virt), size + head,
sg->offset);
sg_dma_len(sg) = size + head;
} else if (i == (num - 1)) {
sg->offset = head + size * i;
sg->offset = sg->offset * (*bpw / 8);
sg_set_page(sg, virt_to_page(dma->tx_buf_virt), rem, sg_set_page(sg, virt_to_page(dma->tx_buf_virt), rem,
sg->offset); sg->offset);
sg_dma_len(sg) = rem; sg_dma_len(sg) = rem;
} else { } else {
sg->offset = rem + size * (i - 1); sg->offset = head + size * i;
sg->offset = sg->offset * (*bpw / 8); sg->offset = sg->offset * (*bpw / 8);
sg_set_page(sg, virt_to_page(dma->tx_buf_virt), size, sg_set_page(sg, virt_to_page(dma->tx_buf_virt), size,
sg->offset); sg->offset);
...@@ -1202,6 +1237,7 @@ static void pch_spi_process_messages(struct work_struct *pwork) ...@@ -1202,6 +1237,7 @@ static void pch_spi_process_messages(struct work_struct *pwork)
data->current_msg->spi->bits_per_word); data->current_msg->spi->bits_per_word);
pch_spi_writereg(data->master, PCH_SSNXCR, SSN_NO_CONTROL); pch_spi_writereg(data->master, PCH_SSNXCR, SSN_NO_CONTROL);
do { do {
int cnt;
/* If we are already processing a message get the next /* If we are already processing a message get the next
transfer structure from the message otherwise retrieve transfer structure from the message otherwise retrieve
the 1st transfer request from the message. */ the 1st transfer request from the message. */
...@@ -1221,11 +1257,28 @@ static void pch_spi_process_messages(struct work_struct *pwork) ...@@ -1221,11 +1257,28 @@ static void pch_spi_process_messages(struct work_struct *pwork)
} }
spin_unlock(&data->lock); spin_unlock(&data->lock);
if (!data->cur_trans->len)
goto out;
cnt = (data->cur_trans->len - 1) / PCH_BUF_SIZE + 1;
data->save_total_len = data->cur_trans->len;
if (data->use_dma) { if (data->use_dma) {
int i;
char *save_rx_buf = data->cur_trans->rx_buf;
for (i = 0; i < cnt; i ++) {
pch_spi_handle_dma(data, &bpw); pch_spi_handle_dma(data, &bpw);
if (!pch_spi_start_transfer(data)) if (!pch_spi_start_transfer(data)) {
data->transfer_complete = true;
data->current_msg->status = -EIO;
data->current_msg->complete
(data->current_msg->context);
data->bcurrent_msg_processing = false;
data->current_msg = NULL;
data->cur_trans = NULL;
goto out; goto out;
}
pch_spi_copy_rx_data_for_dma(data, bpw); pch_spi_copy_rx_data_for_dma(data, bpw);
}
data->cur_trans->rx_buf = save_rx_buf;
} else { } else {
pch_spi_set_tx(data, &bpw); pch_spi_set_tx(data, &bpw);
pch_spi_set_ir(data); pch_spi_set_ir(data);
...@@ -1236,6 +1289,7 @@ static void pch_spi_process_messages(struct work_struct *pwork) ...@@ -1236,6 +1289,7 @@ static void pch_spi_process_messages(struct work_struct *pwork)
data->pkt_tx_buff = NULL; data->pkt_tx_buff = NULL;
} }
/* increment message count */ /* increment message count */
data->cur_trans->len = data->save_total_len;
data->current_msg->actual_length += data->cur_trans->len; data->current_msg->actual_length += data->cur_trans->len;
dev_dbg(&data->master->dev, dev_dbg(&data->master->dev,
...@@ -1388,6 +1442,7 @@ static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev) ...@@ -1388,6 +1442,7 @@ static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
master->num_chipselect = PCH_MAX_CS; master->num_chipselect = PCH_MAX_CS;
master->setup = pch_spi_setup; master->setup = pch_spi_setup;
master->transfer = pch_spi_transfer; master->transfer = pch_spi_transfer;
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
data->board_dat = board_dat; data->board_dat = board_dat;
data->plat_dev = plat_dev; data->plat_dev = plat_dev;
......
...@@ -30,6 +30,9 @@ ...@@ -30,6 +30,9 @@
#include <linux/of_spi.h> #include <linux/of_spi.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/kthread.h>
static void spidev_release(struct device *dev) static void spidev_release(struct device *dev)
{ {
...@@ -481,7 +484,7 @@ static void spi_match_master_to_boardinfo(struct spi_master *master, ...@@ -481,7 +484,7 @@ static void spi_match_master_to_boardinfo(struct spi_master *master,
* The board info passed can safely be __initdata ... but be careful of * The board info passed can safely be __initdata ... but be careful of
* any embedded pointers (platform_data, etc), they're copied as-is. * any embedded pointers (platform_data, etc), they're copied as-is.
*/ */
int __init int __devinit
spi_register_board_info(struct spi_board_info const *info, unsigned n) spi_register_board_info(struct spi_board_info const *info, unsigned n)
{ {
struct boardinfo *bi; struct boardinfo *bi;
...@@ -507,6 +510,294 @@ spi_register_board_info(struct spi_board_info const *info, unsigned n) ...@@ -507,6 +510,294 @@ spi_register_board_info(struct spi_board_info const *info, unsigned n)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
/**
* spi_pump_messages - kthread work function which processes spi message queue
* @work: pointer to kthread work struct contained in the master struct
*
* This function checks if there is any spi message in the queue that
* needs processing and if so call out to the driver to initialize hardware
* and transfer each message.
*
*/
static void spi_pump_messages(struct kthread_work *work)
{
struct spi_master *master =
container_of(work, struct spi_master, pump_messages);
unsigned long flags;
bool was_busy = false;
int ret;
/* Lock queue and check for queue work */
spin_lock_irqsave(&master->queue_lock, flags);
if (list_empty(&master->queue) || !master->running) {
if (master->busy) {
ret = master->unprepare_transfer_hardware(master);
if (ret) {
spin_unlock_irqrestore(&master->queue_lock, flags);
dev_err(&master->dev,
"failed to unprepare transfer hardware\n");
return;
}
}
master->busy = false;
spin_unlock_irqrestore(&master->queue_lock, flags);
return;
}
/* Make sure we are not already running a message */
if (master->cur_msg) {
spin_unlock_irqrestore(&master->queue_lock, flags);
return;
}
/* Extract head of queue */
master->cur_msg =
list_entry(master->queue.next, struct spi_message, queue);
list_del_init(&master->cur_msg->queue);
if (master->busy)
was_busy = true;
else
master->busy = true;
spin_unlock_irqrestore(&master->queue_lock, flags);
if (!was_busy) {
ret = master->prepare_transfer_hardware(master);
if (ret) {
dev_err(&master->dev,
"failed to prepare transfer hardware\n");
return;
}
}
ret = master->transfer_one_message(master, master->cur_msg);
if (ret) {
dev_err(&master->dev,
"failed to transfer one message from queue\n");
return;
}
}
static int spi_init_queue(struct spi_master *master)
{
struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 };
INIT_LIST_HEAD(&master->queue);
spin_lock_init(&master->queue_lock);
master->running = false;
master->busy = false;
init_kthread_worker(&master->kworker);
master->kworker_task = kthread_run(kthread_worker_fn,
&master->kworker,
dev_name(&master->dev));
if (IS_ERR(master->kworker_task)) {
dev_err(&master->dev, "failed to create message pump task\n");
return -ENOMEM;
}
init_kthread_work(&master->pump_messages, spi_pump_messages);
/*
* Master config will indicate if this controller should run the
* message pump with high (realtime) priority to reduce the transfer
* latency on the bus by minimising the delay between a transfer
* request and the scheduling of the message pump thread. Without this
* setting the message pump thread will remain at default priority.
*/
if (master->rt) {
dev_info(&master->dev,
"will run message pump with realtime priority\n");
sched_setscheduler(master->kworker_task, SCHED_FIFO, &param);
}
return 0;
}
/**
* spi_get_next_queued_message() - called by driver to check for queued
* messages
* @master: the master to check for queued messages
*
* If there are more messages in the queue, the next message is returned from
* this call.
*/
struct spi_message *spi_get_next_queued_message(struct spi_master *master)
{
struct spi_message *next;
unsigned long flags;
/* get a pointer to the next message, if any */
spin_lock_irqsave(&master->queue_lock, flags);
if (list_empty(&master->queue))
next = NULL;
else
next = list_entry(master->queue.next,
struct spi_message, queue);
spin_unlock_irqrestore(&master->queue_lock, flags);
return next;
}
EXPORT_SYMBOL_GPL(spi_get_next_queued_message);
/**
* spi_finalize_current_message() - the current message is complete
* @master: the master to return the message to
*
* Called by the driver to notify the core that the message in the front of the
* queue is complete and can be removed from the queue.
*/
void spi_finalize_current_message(struct spi_master *master)
{
struct spi_message *mesg;
unsigned long flags;
spin_lock_irqsave(&master->queue_lock, flags);
mesg = master->cur_msg;
master->cur_msg = NULL;
queue_kthread_work(&master->kworker, &master->pump_messages);
spin_unlock_irqrestore(&master->queue_lock, flags);
mesg->state = NULL;
if (mesg->complete)
mesg->complete(mesg->context);
}
EXPORT_SYMBOL_GPL(spi_finalize_current_message);
static int spi_start_queue(struct spi_master *master)
{
unsigned long flags;
spin_lock_irqsave(&master->queue_lock, flags);
if (master->running || master->busy) {
spin_unlock_irqrestore(&master->queue_lock, flags);
return -EBUSY;
}
master->running = true;
master->cur_msg = NULL;
spin_unlock_irqrestore(&master->queue_lock, flags);
queue_kthread_work(&master->kworker, &master->pump_messages);
return 0;
}
static int spi_stop_queue(struct spi_master *master)
{
unsigned long flags;
unsigned limit = 500;
int ret = 0;
spin_lock_irqsave(&master->queue_lock, flags);
/*
* This is a bit lame, but is optimized for the common execution path.
* A wait_queue on the master->busy could be used, but then the common
* execution path (pump_messages) would be required to call wake_up or
* friends on every SPI message. Do this instead.
*/
while ((!list_empty(&master->queue) || master->busy) && limit--) {
spin_unlock_irqrestore(&master->queue_lock, flags);
msleep(10);
spin_lock_irqsave(&master->queue_lock, flags);
}
if (!list_empty(&master->queue) || master->busy)
ret = -EBUSY;
else
master->running = false;
spin_unlock_irqrestore(&master->queue_lock, flags);
if (ret) {
dev_warn(&master->dev,
"could not stop message queue\n");
return ret;
}
return ret;
}
static int spi_destroy_queue(struct spi_master *master)
{
int ret;
ret = spi_stop_queue(master);
/*
* flush_kthread_worker will block until all work is done.
* If the reason that stop_queue timed out is that the work will never
* finish, then it does no good to call flush/stop thread, so
* return anyway.
*/
if (ret) {
dev_err(&master->dev, "problem destroying queue\n");
return ret;
}
flush_kthread_worker(&master->kworker);
kthread_stop(master->kworker_task);
return 0;
}
/**
* spi_queued_transfer - transfer function for queued transfers
* @spi: spi device which is requesting transfer
* @msg: spi message which is to handled is queued to driver queue
*/
static int spi_queued_transfer(struct spi_device *spi, struct spi_message *msg)
{
struct spi_master *master = spi->master;
unsigned long flags;
spin_lock_irqsave(&master->queue_lock, flags);
if (!master->running) {
spin_unlock_irqrestore(&master->queue_lock, flags);
return -ESHUTDOWN;
}
msg->actual_length = 0;
msg->status = -EINPROGRESS;
list_add_tail(&msg->queue, &master->queue);
if (master->running && !master->busy)
queue_kthread_work(&master->kworker, &master->pump_messages);
spin_unlock_irqrestore(&master->queue_lock, flags);
return 0;
}
static int spi_master_initialize_queue(struct spi_master *master)
{
int ret;
master->queued = true;
master->transfer = spi_queued_transfer;
/* Initialize and start queue */
ret = spi_init_queue(master);
if (ret) {
dev_err(&master->dev, "problem initializing queue\n");
goto err_init_queue;
}
ret = spi_start_queue(master);
if (ret) {
dev_err(&master->dev, "problem starting queue\n");
goto err_start_queue;
}
return 0;
err_start_queue:
err_init_queue:
spi_destroy_queue(master);
return ret;
}
/*-------------------------------------------------------------------------*/
static void spi_master_release(struct device *dev) static void spi_master_release(struct device *dev)
{ {
struct spi_master *master; struct spi_master *master;
...@@ -522,6 +813,7 @@ static struct class spi_master_class = { ...@@ -522,6 +813,7 @@ static struct class spi_master_class = {
}; };
/** /**
* spi_alloc_master - allocate SPI master controller * spi_alloc_master - allocate SPI master controller
* @dev: the controller, possibly using the platform_bus * @dev: the controller, possibly using the platform_bus
...@@ -539,7 +831,8 @@ static struct class spi_master_class = { ...@@ -539,7 +831,8 @@ static struct class spi_master_class = {
* *
* The caller is responsible for assigning the bus number and initializing * The caller is responsible for assigning the bus number and initializing
* the master's methods before calling spi_register_master(); and (after errors * the master's methods before calling spi_register_master(); and (after errors
* adding the device) calling spi_master_put() to prevent a memory leak. * adding the device) calling spi_master_put() and kfree() to prevent a memory
* leak.
*/ */
struct spi_master *spi_alloc_master(struct device *dev, unsigned size) struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
{ {
...@@ -621,14 +914,23 @@ int spi_register_master(struct spi_master *master) ...@@ -621,14 +914,23 @@ int spi_register_master(struct spi_master *master)
dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev), dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),
dynamic ? " (dynamic)" : ""); dynamic ? " (dynamic)" : "");
/* If we're using a queued driver, start the queue */
if (master->transfer)
dev_info(dev, "master is unqueued, this is deprecated\n");
else {
status = spi_master_initialize_queue(master);
if (status) {
device_unregister(&master->dev);
goto done;
}
}
mutex_lock(&board_lock); mutex_lock(&board_lock);
list_add_tail(&master->list, &spi_master_list); list_add_tail(&master->list, &spi_master_list);
list_for_each_entry(bi, &board_list, list) list_for_each_entry(bi, &board_list, list)
spi_match_master_to_boardinfo(master, &bi->board_info); spi_match_master_to_boardinfo(master, &bi->board_info);
mutex_unlock(&board_lock); mutex_unlock(&board_lock);
status = 0;
/* Register devices from the device tree */ /* Register devices from the device tree */
of_register_spi_devices(master); of_register_spi_devices(master);
done: done:
...@@ -636,7 +938,6 @@ int spi_register_master(struct spi_master *master) ...@@ -636,7 +938,6 @@ int spi_register_master(struct spi_master *master)
} }
EXPORT_SYMBOL_GPL(spi_register_master); EXPORT_SYMBOL_GPL(spi_register_master);
static int __unregister(struct device *dev, void *null) static int __unregister(struct device *dev, void *null)
{ {
spi_unregister_device(to_spi_device(dev)); spi_unregister_device(to_spi_device(dev));
...@@ -657,6 +958,11 @@ void spi_unregister_master(struct spi_master *master) ...@@ -657,6 +958,11 @@ void spi_unregister_master(struct spi_master *master)
{ {
int dummy; int dummy;
if (master->queued) {
if (spi_destroy_queue(master))
dev_err(&master->dev, "queue remove failed\n");
}
mutex_lock(&board_lock); mutex_lock(&board_lock);
list_del(&master->list); list_del(&master->list);
mutex_unlock(&board_lock); mutex_unlock(&board_lock);
...@@ -666,6 +972,37 @@ void spi_unregister_master(struct spi_master *master) ...@@ -666,6 +972,37 @@ void spi_unregister_master(struct spi_master *master)
} }
EXPORT_SYMBOL_GPL(spi_unregister_master); EXPORT_SYMBOL_GPL(spi_unregister_master);
int spi_master_suspend(struct spi_master *master)
{
int ret;
/* Basically no-ops for non-queued masters */
if (!master->queued)
return 0;
ret = spi_stop_queue(master);
if (ret)
dev_err(&master->dev, "queue stop failed\n");
return ret;
}
EXPORT_SYMBOL_GPL(spi_master_suspend);
int spi_master_resume(struct spi_master *master)
{
int ret;
if (!master->queued)
return 0;
ret = spi_start_queue(master);
if (ret)
dev_err(&master->dev, "queue restart failed\n");
return ret;
}
EXPORT_SYMBOL_GPL(spi_master_resume);
static int __spi_master_match(struct device *dev, void *data) static int __spi_master_match(struct device *dev, void *data)
{ {
struct spi_master *m; struct spi_master *m;
......
...@@ -241,6 +241,8 @@ struct dma_chan; ...@@ -241,6 +241,8 @@ struct dma_chan;
* @autosuspend_delay: delay in ms following transfer completion before the * @autosuspend_delay: delay in ms following transfer completion before the
* runtime power management system suspends the device. A setting of 0 * runtime power management system suspends the device. A setting of 0
* indicates no delay and the device will be suspended immediately. * indicates no delay and the device will be suspended immediately.
* @rt: indicates the controller should run the message pump with realtime
* priority to minimise the transfer latency on the bus.
*/ */
struct pl022_ssp_controller { struct pl022_ssp_controller {
u16 bus_id; u16 bus_id;
...@@ -250,6 +252,7 @@ struct pl022_ssp_controller { ...@@ -250,6 +252,7 @@ struct pl022_ssp_controller {
void *dma_rx_param; void *dma_rx_param;
void *dma_tx_param; void *dma_tx_param;
int autosuspend_delay; int autosuspend_delay;
bool rt;
}; };
/** /**
......
/*
* Copyright (C) 2011 Kuninori Morimoto
*
* 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
*/
#ifndef SH_HSPI_H
#define SH_HSPI_H
struct sh_hspi_info {
};
#endif
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/mod_devicetable.h> #include <linux/mod_devicetable.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/kthread.h>
/* /*
* INTERFACES between SPI master-side drivers and SPI infrastructure. * INTERFACES between SPI master-side drivers and SPI infrastructure.
...@@ -235,6 +236,27 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) ...@@ -235,6 +236,27 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
* the device whose settings are being modified. * the device whose settings are being modified.
* @transfer: adds a message to the controller's transfer queue. * @transfer: adds a message to the controller's transfer queue.
* @cleanup: frees controller-specific state * @cleanup: frees controller-specific state
* @queued: whether this master is providing an internal message queue
* @kworker: thread struct for message pump
* @kworker_task: pointer to task for message pump kworker thread
* @pump_messages: work struct for scheduling work to the message pump
* @queue_lock: spinlock to syncronise access to message queue
* @queue: message queue
* @cur_msg: the currently in-flight message
* @busy: message pump is busy
* @running: message pump is running
* @rt: whether this queue is set to run as a realtime task
* @prepare_transfer_hardware: a message will soon arrive from the queue
* so the subsystem requests the driver to prepare the transfer hardware
* by issuing this call
* @transfer_one_message: the subsystem calls the driver to transfer a single
* message while queuing transfers that arrive in the meantime. When the
* driver is finished with this message, it must call
* spi_finalize_current_message() so the subsystem can issue the next
* transfer
* @prepare_transfer_hardware: there are currently no more messages on the
* queue so the subsystem notifies the driver that it may relax the
* hardware by issuing this call
* *
* Each SPI master controller can communicate with one or more @spi_device * Each SPI master controller can communicate with one or more @spi_device
* children. These make a small bus, sharing MOSI, MISO and SCK signals * children. These make a small bus, sharing MOSI, MISO and SCK signals
...@@ -318,6 +340,28 @@ struct spi_master { ...@@ -318,6 +340,28 @@ struct spi_master {
/* called on release() to free memory provided by spi_master */ /* called on release() to free memory provided by spi_master */
void (*cleanup)(struct spi_device *spi); void (*cleanup)(struct spi_device *spi);
/*
* These hooks are for drivers that want to use the generic
* master transfer queueing mechanism. If these are used, the
* transfer() function above must NOT be specified by the driver.
* Over time we expect SPI drivers to be phased over to this API.
*/
bool queued;
struct kthread_worker kworker;
struct task_struct *kworker_task;
struct kthread_work pump_messages;
spinlock_t queue_lock;
struct list_head queue;
struct spi_message *cur_msg;
bool busy;
bool running;
bool rt;
int (*prepare_transfer_hardware)(struct spi_master *master);
int (*transfer_one_message)(struct spi_master *master,
struct spi_message *mesg);
int (*unprepare_transfer_hardware)(struct spi_master *master);
}; };
static inline void *spi_master_get_devdata(struct spi_master *master) static inline void *spi_master_get_devdata(struct spi_master *master)
...@@ -343,6 +387,13 @@ static inline void spi_master_put(struct spi_master *master) ...@@ -343,6 +387,13 @@ static inline void spi_master_put(struct spi_master *master)
put_device(&master->dev); put_device(&master->dev);
} }
/* PM calls that need to be issued by the driver */
extern int spi_master_suspend(struct spi_master *master);
extern int spi_master_resume(struct spi_master *master);
/* Calls the driver make to interact with the message queue */
extern struct spi_message *spi_get_next_queued_message(struct spi_master *master);
extern void spi_finalize_current_message(struct spi_master *master);
/* the spi driver core manages memory for the spi_master classdev */ /* the spi driver core manages memory for the spi_master classdev */
extern struct spi_master * extern struct spi_master *
...@@ -549,7 +600,7 @@ static inline struct spi_message *spi_message_alloc(unsigned ntrans, gfp_t flags ...@@ -549,7 +600,7 @@ static inline struct spi_message *spi_message_alloc(unsigned ntrans, gfp_t flags
+ ntrans * sizeof(struct spi_transfer), + ntrans * sizeof(struct spi_transfer),
flags); flags);
if (m) { if (m) {
int i; unsigned i;
struct spi_transfer *t = (struct spi_transfer *)(m + 1); struct spi_transfer *t = (struct spi_transfer *)(m + 1);
INIT_LIST_HEAD(&m->transfers); INIT_LIST_HEAD(&m->transfers);
......
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