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
......
This diff is collapsed.
...@@ -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->num_chipselect = pdata->num_cs; 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;
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),
}; };
......
This diff is collapsed.
...@@ -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) },
{ }, { },
}; };
......
This diff is collapsed.
This diff is collapsed.
/*
* 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));
......
This diff is collapsed.
...@@ -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,22 +319,23 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val, ...@@ -318,22 +319,23 @@ 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 (reg_spsr_val & SPSR_FI_BIT) {
/* if transfer complete interrupt */ if ((tx_index == bpw_len) && (rx_index == tx_index)) {
if (reg_spsr_val & SPSR_FI_BIT) { /* disable interrupts */
if ((tx_index == bpw_len) && (rx_index == tx_index)) { pch_spi_setclr_reg(data->master, PCH_SPCR, 0,
/* disable interrupts */ 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 */ data->transfer_complete = true;
data->transfer_complete = true; data->transfer_active = false;
data->transfer_active = false; 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",
"%s : Transfer is not completed", __func__); __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) {
num = data->bpw_len / PCH_DMA_TRANS_SIZE + 1; if (data->bpw_len % PCH_DMA_TRANS_SIZE) {
num = data->bpw_len / PCH_DMA_TRANS_SIZE + 1;
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; size = PCH_DMA_TRANS_SIZE;
rem = data->bpw_len % 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) {
num = data->bpw_len / PCH_DMA_TRANS_SIZE; 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;
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) {
pch_spi_handle_dma(data, &bpw); int i;
if (!pch_spi_start_transfer(data)) char *save_rx_buf = data->cur_trans->rx_buf;
goto out; for (i = 0; i < cnt; i ++) {
pch_spi_copy_rx_data_for_dma(data, bpw); pch_spi_handle_dma(data, &bpw);
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;
}
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;
......
This diff is collapsed.
...@@ -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