Commit 7fe0b14b authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'spi-3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/misc

Pull spi updates from Mark Brown:
 "No framework work here, only a bunch of driver updates of varying
  sizes:

   - Factoring out of the core hardware support from the MXS MMC driver
     by Marek Vasut to allow the hardware to also be used for SPI.
   - Lots of error handling cleanups from Guenter Roeck
   - Removal of the existing Tegra driver which is quite comprehensively
     broken as detailed in the changelog for the removal.
   - DT suppport for the PL022 and GPIO drivers.
   - pinctrl support for OMAP and PL022."

Pulling from Mark Brown as Grant Likely is still busy moving.

* tag 'spi-3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/misc: (53 commits)
  spi: remove completely broken Tegra driver
  spi/imx: set the inactive state of the clock according to the clock polarity
  spi/pl022: get/put resources on suspend/resume
  spi/pl022: use more managed resources
  spi/pl022: Devicetree support w/o platform data
  spi/s3c64xx: Don't free controller_data on non-dt platforms
  spi: omap2-mcspi: add pinctrl support
  spi/pl022: adopt pinctrl support
  spi: omap2-mcspi: Cleanup the omap2_mcspi_txrx_dma function
  spi/gpio: Fix stub for spi_gpio_probe_dt()
  spi/mxs: Make the SPI block clock speed configurable via DT
  spi: spi-sh-hspi: drop frees of devm_ alloc'd data
  spi/pl022: Fix chipselects pointer computation
  spi: spi-tle62x0: Use module_spi_driver macro
  mxs/spi: Rework the mxs_ssp_timeout to be more readable
  mxs/spi: Decrement the DMA/PIO border
  mxs/spi: Increment the transfer length only if transfer succeeded
  mxs/spi: Fix issues when doing long continuous transfer
  spi: spi-gpio: Add DT bindings
  spi: spi-gpio: store chipselect information in private structure
  ...
parents 7a9a2970 536a53a3
* Freescale MX233/MX28 SSP/SPI
Required properties:
- compatible: Should be "fsl,<soc>-spi", where soc is "imx23" or "imx28"
- reg: Offset and length of the register set for the device
- interrupts: Should contain SSP interrupts (error irq first, dma irq second)
- fsl,ssp-dma-channel: APBX DMA channel for the SSP
Optional properties:
- clock-frequency : Input clock frequency to the SPI block in Hz.
Default is 160000000 Hz.
Example:
ssp0: ssp@80010000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx28-spi";
reg = <0x80010000 0x2000>;
interrupts = <96 82>;
fsl,ssp-dma-channel = <0>;
};
...@@ -21,6 +21,9 @@ assumption that board specific platform code will be used to manage ...@@ -21,6 +21,9 @@ assumption that board specific platform code will be used to manage
chip selects. Individual drivers can define additional properties to chip selects. Individual drivers can define additional properties to
support describing the chip select layout. support describing the chip select layout.
Optional property:
- num-cs : total number of chipselects
SPI slave nodes must be children of the SPI master node and can SPI slave nodes must be children of the SPI master node and can
contain the following properties. contain the following properties.
- reg - (required) chip select address of device. - reg - (required) chip select address of device.
......
SPI-GPIO devicetree bindings
Required properties:
- compatible: should be set to "spi-gpio"
- #address-cells: should be set to <0x1>
- ranges
- gpio-sck: GPIO spec for the SCK line to use
- gpio-miso: GPIO spec for the MISO line to use
- gpio-mosi: GPIO spec for the MOSI line to use
- cs-gpios: GPIOs to use for chipselect lines
- num-chipselects: number of chipselect lines
Example:
spi {
compatible = "spi-gpio";
#address-cells = <0x1>;
ranges;
gpio-sck = <&gpio 95 0>;
gpio-miso = <&gpio 98 0>;
gpio-mosi = <&gpio 97 0>;
cs-gpios = <&gpio 125 0>;
num-chipselects = <1>;
/* clients */
};
NXP SC18IS602/SCIS603
Required properties:
- compatible : Should be one of
"nxp,sc18is602"
"nxp,sc18is602b"
"nxp,sc18is603"
- reg: I2C bus address
Optional properties:
- clock-frequency : external oscillator clock frequency. If not
specified, the SC18IS602 default frequency (7372000) will be used.
The clock-frequency property is relevant and needed only if the chip has an
external oscillator (SC18IS603).
Example:
sc18is603@28 {
compatible = "nxp,sc18is603";
reg = <0x28>;
clock-frequency = <14744000>;
}
...@@ -6,7 +6,29 @@ Required properties: ...@@ -6,7 +6,29 @@ Required properties:
- interrupts : Should contain SPI controller interrupt - interrupts : Should contain SPI controller interrupt
Optional properties: Optional properties:
- num-cs : total number of chipselects
- cs-gpios : should specify GPIOs used for chipselects. - cs-gpios : should specify GPIOs used for chipselects.
The gpios will be referred to as reg = <index> in the SPI child nodes. The gpios will be referred to as reg = <index> in the SPI child nodes.
If unspecified, a single SPI device without a chip select can be used. If unspecified, a single SPI device without a chip select can be used.
- pl022,autosuspend-delay : delay in ms following transfer completion before
the runtime power management system suspends the
device. A setting of 0 indicates no delay and the
device will be suspended immediately
- pl022,rt : indicates the controller should run the message pump with realtime
priority to minimise the transfer latency on the bus (boolean)
SPI slave nodes must be children of the SPI master node and can
contain the following properties.
- pl022,interface : interface type:
0: SPI
1: Texas Instruments Synchronous Serial Frame Format
2: Microwire (Half Duplex)
- pl022,com-mode : polling, interrupt or dma
- pl022,rx-level-trig : Rx FIFO watermark level
- pl022,tx-level-trig : Tx FIFO watermark level
- pl022,ctrl-len : Microwire interface: Control length
- pl022,wait-state : Microwire interface: Wait state
- pl022,duplex : Microwire interface: Full/Half duplex
Kernel driver spi-sc18is602
===========================
Supported chips:
* NXP SI18IS602/602B/603
Datasheet: http://www.nxp.com/documents/data_sheet/SC18IS602_602B_603.pdf
Author:
Guenter Roeck <linux@roeck-us.net>
Description
-----------
This driver provides connects a NXP SC18IS602/603 I2C-bus to SPI bridge to the
kernel's SPI core subsystem.
The driver does not probe for supported chips, since the SI18IS602/603 does not
support Chip ID registers. You will have to instantiate the devices explicitly.
Please see Documentation/i2c/instantiating-devices for details.
Usage Notes
-----------
This driver requires the I2C adapter driver to support raw I2C messages. I2C
adapter drivers which can only handle the SMBus protocol are not supported.
The maximum SPI message size supported by SC18IS602/603 is 200 bytes. Attempts
to initiate longer transfers will fail with -EINVAL. EEPROM read operations and
similar large accesses have to be split into multiple chunks of no more than
200 bytes per SPI message (128 bytes of data per message is recommended). This
means that programs such as "cp" or "od", which automatically use large block
sizes to access a device, can not be used directly to read data from EEPROM.
Programs such as dd, where the block size can be specified, should be used
instead.
...@@ -1556,9 +1556,6 @@ static struct u300_mux_hog u300_mux_hogs[] = { ...@@ -1556,9 +1556,6 @@ static struct u300_mux_hog u300_mux_hogs[] = {
{ {
.dev = &uart0_device.dev, .dev = &uart0_device.dev,
}, },
{
.dev = &pl022_device.dev,
},
{ {
.dev = &mmcsd_device.dev, .dev = &mmcsd_device.dev,
}, },
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# Makefile for mxs specific clk # Makefile for mxs specific clk
# #
obj-y += clk.o clk-pll.o clk-ref.o clk-div.o clk-frac.o obj-y += clk.o clk-pll.o clk-ref.o clk-div.o clk-frac.o clk-ssp.o
obj-$(CONFIG_SOC_IMX23) += clk-imx23.o obj-$(CONFIG_SOC_IMX23) += clk-imx23.o
obj-$(CONFIG_SOC_IMX28) += clk-imx28.o obj-$(CONFIG_SOC_IMX28) += clk-imx28.o
/*
* Copyright 2012 DENX Software Engineering, GmbH
*
* Pulled from code:
* Portions copyright (C) 2003 Russell King, PXA MMCI Driver
* Portions copyright (C) 2004-2005 Pierre Ossman, W83L51xD SD/MMC driver
*
* Copyright 2008 Embedded Alley Solutions, Inc.
* Copyright 2009-2011 Freescale Semiconductor, Inc.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/spi/mxs-spi.h>
void mxs_ssp_set_clk_rate(struct mxs_ssp *ssp, unsigned int rate)
{
unsigned int ssp_clk, ssp_sck;
u32 clock_divide, clock_rate;
u32 val;
ssp_clk = clk_get_rate(ssp->clk);
for (clock_divide = 2; clock_divide <= 254; clock_divide += 2) {
clock_rate = DIV_ROUND_UP(ssp_clk, rate * clock_divide);
clock_rate = (clock_rate > 0) ? clock_rate - 1 : 0;
if (clock_rate <= 255)
break;
}
if (clock_divide > 254) {
dev_err(ssp->dev,
"%s: cannot set clock to %d\n", __func__, rate);
return;
}
ssp_sck = ssp_clk / clock_divide / (1 + clock_rate);
val = readl(ssp->base + HW_SSP_TIMING(ssp));
val &= ~(BM_SSP_TIMING_CLOCK_DIVIDE | BM_SSP_TIMING_CLOCK_RATE);
val |= BF_SSP(clock_divide, TIMING_CLOCK_DIVIDE);
val |= BF_SSP(clock_rate, TIMING_CLOCK_RATE);
writel(val, ssp->base + HW_SSP_TIMING(ssp));
ssp->clk_rate = ssp_sck;
dev_dbg(ssp->dev,
"%s: clock_divide %d, clock_rate %d, ssp_clk %d, rate_actual %d, rate_requested %d\n",
__func__, clock_divide, clock_rate, ssp_clk, ssp_sck, rate);
}
EXPORT_SYMBOL_GPL(mxs_ssp_set_clk_rate);
This diff is collapsed.
...@@ -325,6 +325,12 @@ config SPI_S3C64XX ...@@ -325,6 +325,12 @@ config SPI_S3C64XX
help help
SPI driver for Samsung S3C64XX and newer SoCs. SPI driver for Samsung S3C64XX and newer SoCs.
config SPI_SC18IS602
tristate "NXP SC18IS602/602B/603 I2C to SPI bridge"
depends on I2C
help
SPI driver for NXP SC18IS602/602B/603 I2C to SPI bridge.
config SPI_SH_MSIOF config SPI_SH_MSIOF
tristate "SuperH MSIOF SPI controller" tristate "SuperH MSIOF SPI controller"
depends on SUPERH && HAVE_CLK depends on SUPERH && HAVE_CLK
...@@ -364,11 +370,12 @@ config SPI_STMP3XXX ...@@ -364,11 +370,12 @@ config SPI_STMP3XXX
help help
SPI driver for Freescale STMP37xx/378x SoC SSP interface SPI driver for Freescale STMP37xx/378x SoC SSP interface
config SPI_TEGRA config SPI_MXS
tristate "Nvidia Tegra SPI controller" tristate "Freescale MXS SPI controller"
depends on ARCH_TEGRA && TEGRA20_APB_DMA depends on ARCH_MXS
select STMP_DEVICE
help help
SPI driver for NVidia Tegra SoCs SPI driver for Freescale MXS devices.
config SPI_TI_SSP config SPI_TI_SSP
tristate "TI Sequencer Serial Port - SPI Support" tristate "TI Sequencer Serial Port - SPI Support"
......
...@@ -36,6 +36,7 @@ obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o ...@@ -36,6 +36,7 @@ obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o
obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mpc512x-psc.o obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mpc512x-psc.o
obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o
obj-$(CONFIG_SPI_MPC52xx) += spi-mpc52xx.o obj-$(CONFIG_SPI_MPC52xx) += spi-mpc52xx.o
obj-$(CONFIG_SPI_MXS) += spi-mxs.o
obj-$(CONFIG_SPI_NUC900) += spi-nuc900.o obj-$(CONFIG_SPI_NUC900) += spi-nuc900.o
obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o
obj-$(CONFIG_SPI_OMAP_UWIRE) += spi-omap-uwire.o obj-$(CONFIG_SPI_OMAP_UWIRE) += spi-omap-uwire.o
...@@ -51,13 +52,13 @@ obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o ...@@ -51,13 +52,13 @@ 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_SC18IS602) += spi-sc18is602.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_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_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_TI_SSP) += spi-ti-ssp.o obj-$(CONFIG_SPI_TI_SSP) += spi-ti-ssp.o
obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o
obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o
......
...@@ -307,8 +307,6 @@ static const struct of_device_id altera_spi_match[] = { ...@@ -307,8 +307,6 @@ static const struct of_device_id altera_spi_match[] = {
{}, {},
}; };
MODULE_DEVICE_TABLE(of, altera_spi_match); MODULE_DEVICE_TABLE(of, altera_spi_match);
#else /* CONFIG_OF */
#define altera_spi_match NULL
#endif /* CONFIG_OF */ #endif /* CONFIG_OF */
static struct platform_driver altera_spi_driver = { static struct platform_driver altera_spi_driver = {
...@@ -318,7 +316,7 @@ static struct platform_driver altera_spi_driver = { ...@@ -318,7 +316,7 @@ static struct platform_driver altera_spi_driver = {
.name = DRV_NAME, .name = DRV_NAME,
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = NULL, .pm = NULL,
.of_match_table = altera_spi_match, .of_match_table = of_match_ptr(altera_spi_match),
}, },
}; };
module_platform_driver(altera_spi_driver); module_platform_driver(altera_spi_driver);
......
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h> #include <linux/spi/spi_bitbang.h>
...@@ -46,6 +48,7 @@ struct spi_gpio { ...@@ -46,6 +48,7 @@ struct spi_gpio {
struct spi_bitbang bitbang; struct spi_bitbang bitbang;
struct spi_gpio_platform_data pdata; struct spi_gpio_platform_data pdata;
struct platform_device *pdev; struct platform_device *pdev;
int cs_gpios[0];
}; };
/*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/
...@@ -89,15 +92,21 @@ struct spi_gpio { ...@@ -89,15 +92,21 @@ struct spi_gpio {
/*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/
static inline const struct spi_gpio_platform_data * __pure static inline struct spi_gpio * __pure
spi_to_pdata(const struct spi_device *spi) spi_to_spi_gpio(const struct spi_device *spi)
{ {
const struct spi_bitbang *bang; const struct spi_bitbang *bang;
const struct spi_gpio *spi_gpio; struct spi_gpio *spi_gpio;
bang = spi_master_get_devdata(spi->master); bang = spi_master_get_devdata(spi->master);
spi_gpio = container_of(bang, struct spi_gpio, bitbang); spi_gpio = container_of(bang, struct spi_gpio, bitbang);
return &spi_gpio->pdata; return spi_gpio;
}
static inline struct spi_gpio_platform_data * __pure
spi_to_pdata(const struct spi_device *spi)
{
return &spi_to_spi_gpio(spi)->pdata;
} }
/* this is #defined to avoid unused-variable warnings when inlining */ /* this is #defined to avoid unused-variable warnings when inlining */
...@@ -210,7 +219,8 @@ static u32 spi_gpio_spec_txrx_word_mode3(struct spi_device *spi, ...@@ -210,7 +219,8 @@ static u32 spi_gpio_spec_txrx_word_mode3(struct spi_device *spi,
static void spi_gpio_chipselect(struct spi_device *spi, int is_active) static void spi_gpio_chipselect(struct spi_device *spi, int is_active)
{ {
unsigned long cs = (unsigned long) spi->controller_data; struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi);
unsigned int cs = spi_gpio->cs_gpios[spi->chip_select];
/* set initial clock polarity */ /* set initial clock polarity */
if (is_active) if (is_active)
...@@ -224,12 +234,27 @@ static void spi_gpio_chipselect(struct spi_device *spi, int is_active) ...@@ -224,12 +234,27 @@ static void spi_gpio_chipselect(struct spi_device *spi, int is_active)
static int spi_gpio_setup(struct spi_device *spi) static int spi_gpio_setup(struct spi_device *spi)
{ {
unsigned long cs = (unsigned long) spi->controller_data; unsigned int cs;
int status = 0; int status = 0;
struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi);
struct device_node *np = spi->master->dev.of_node;
if (spi->bits_per_word > 32) if (spi->bits_per_word > 32)
return -EINVAL; return -EINVAL;
if (np) {
/*
* In DT environments, the CS GPIOs have already been
* initialized from the "cs-gpios" property of the node.
*/
cs = spi_gpio->cs_gpios[spi->chip_select];
} else {
/*
* ... otherwise, take it from spi->controller_data
*/
cs = (unsigned int) spi->controller_data;
}
if (!spi->controller_state) { if (!spi->controller_state) {
if (cs != SPI_GPIO_NO_CHIPSELECT) { if (cs != SPI_GPIO_NO_CHIPSELECT) {
status = gpio_request(cs, dev_name(&spi->dev)); status = gpio_request(cs, dev_name(&spi->dev));
...@@ -239,8 +264,12 @@ static int spi_gpio_setup(struct spi_device *spi) ...@@ -239,8 +264,12 @@ static int spi_gpio_setup(struct spi_device *spi)
!(spi->mode & SPI_CS_HIGH)); !(spi->mode & SPI_CS_HIGH));
} }
} }
if (!status) if (!status) {
status = spi_bitbang_setup(spi); status = spi_bitbang_setup(spi);
/* in case it was initialized from static board data */
spi_gpio->cs_gpios[spi->chip_select] = cs;
}
if (status) { if (status) {
if (!spi->controller_state && cs != SPI_GPIO_NO_CHIPSELECT) if (!spi->controller_state && cs != SPI_GPIO_NO_CHIPSELECT)
gpio_free(cs); gpio_free(cs);
...@@ -250,7 +279,8 @@ static int spi_gpio_setup(struct spi_device *spi) ...@@ -250,7 +279,8 @@ static int spi_gpio_setup(struct spi_device *spi)
static void spi_gpio_cleanup(struct spi_device *spi) static void spi_gpio_cleanup(struct spi_device *spi)
{ {
unsigned long cs = (unsigned long) spi->controller_data; struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi);
unsigned int cs = spi_gpio->cs_gpios[spi->chip_select];
if (cs != SPI_GPIO_NO_CHIPSELECT) if (cs != SPI_GPIO_NO_CHIPSELECT)
gpio_free(cs); gpio_free(cs);
...@@ -313,6 +343,55 @@ spi_gpio_request(struct spi_gpio_platform_data *pdata, const char *label, ...@@ -313,6 +343,55 @@ spi_gpio_request(struct spi_gpio_platform_data *pdata, const char *label,
return value; return value;
} }
#ifdef CONFIG_OF
static struct of_device_id spi_gpio_dt_ids[] = {
{ .compatible = "spi-gpio" },
{}
};
MODULE_DEVICE_TABLE(of, spi_gpio_dt_ids);
static int spi_gpio_probe_dt(struct platform_device *pdev)
{
int ret;
u32 tmp;
struct spi_gpio_platform_data *pdata;
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *of_id =
of_match_device(spi_gpio_dt_ids, &pdev->dev);
if (!of_id)
return 0;
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
pdata->sck = of_get_named_gpio(np, "gpio-sck", 0);
pdata->miso = of_get_named_gpio(np, "gpio-miso", 0);
pdata->mosi = of_get_named_gpio(np, "gpio-mosi", 0);
ret = of_property_read_u32(np, "num-chipselects", &tmp);
if (ret < 0) {
dev_err(&pdev->dev, "num-chipselects property not found\n");
goto error_free;
}
pdata->num_chipselect = tmp;
pdev->dev.platform_data = pdata;
return 1;
error_free:
devm_kfree(&pdev->dev, pdata);
return ret;
}
#else
static inline int spi_gpio_probe_dt(struct platform_device *pdev)
{
return 0;
}
#endif
static int __devinit spi_gpio_probe(struct platform_device *pdev) static int __devinit spi_gpio_probe(struct platform_device *pdev)
{ {
int status; int status;
...@@ -320,6 +399,13 @@ static int __devinit spi_gpio_probe(struct platform_device *pdev) ...@@ -320,6 +399,13 @@ static int __devinit spi_gpio_probe(struct platform_device *pdev)
struct spi_gpio *spi_gpio; struct spi_gpio *spi_gpio;
struct spi_gpio_platform_data *pdata; struct spi_gpio_platform_data *pdata;
u16 master_flags = 0; u16 master_flags = 0;
bool use_of = 0;
status = spi_gpio_probe_dt(pdev);
if (status < 0)
return status;
if (status > 0)
use_of = 1;
pdata = pdev->dev.platform_data; pdata = pdev->dev.platform_data;
#ifdef GENERIC_BITBANG #ifdef GENERIC_BITBANG
...@@ -331,7 +417,8 @@ static int __devinit spi_gpio_probe(struct platform_device *pdev) ...@@ -331,7 +417,8 @@ static int __devinit spi_gpio_probe(struct platform_device *pdev)
if (status < 0) if (status < 0)
return status; return status;
master = spi_alloc_master(&pdev->dev, sizeof *spi_gpio); master = spi_alloc_master(&pdev->dev, sizeof(*spi_gpio) +
(sizeof(int) * SPI_N_CHIPSEL));
if (!master) { if (!master) {
status = -ENOMEM; status = -ENOMEM;
goto gpio_free; goto gpio_free;
...@@ -348,6 +435,23 @@ static int __devinit spi_gpio_probe(struct platform_device *pdev) ...@@ -348,6 +435,23 @@ static int __devinit spi_gpio_probe(struct platform_device *pdev)
master->num_chipselect = SPI_N_CHIPSEL; master->num_chipselect = SPI_N_CHIPSEL;
master->setup = spi_gpio_setup; master->setup = spi_gpio_setup;
master->cleanup = spi_gpio_cleanup; master->cleanup = spi_gpio_cleanup;
#ifdef CONFIG_OF
master->dev.of_node = pdev->dev.of_node;
if (use_of) {
int i;
struct device_node *np = pdev->dev.of_node;
/*
* In DT environments, take the CS GPIO from the "cs-gpios"
* property of the node.
*/
for (i = 0; i < SPI_N_CHIPSEL; i++)
spi_gpio->cs_gpios[i] =
of_get_named_gpio(np, "cs-gpios", i);
}
#endif
spi_gpio->bitbang.master = spi_master_get(master); spi_gpio->bitbang.master = spi_master_get(master);
spi_gpio->bitbang.chipselect = spi_gpio_chipselect; spi_gpio->bitbang.chipselect = spi_gpio_chipselect;
...@@ -408,8 +512,11 @@ static int __devexit spi_gpio_remove(struct platform_device *pdev) ...@@ -408,8 +512,11 @@ static int __devexit spi_gpio_remove(struct platform_device *pdev)
MODULE_ALIAS("platform:" DRIVER_NAME); MODULE_ALIAS("platform:" DRIVER_NAME);
static struct platform_driver spi_gpio_driver = { static struct platform_driver spi_gpio_driver = {
.driver.name = DRIVER_NAME, .driver = {
.driver.owner = THIS_MODULE, .name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(spi_gpio_dt_ids),
},
.probe = spi_gpio_probe, .probe = spi_gpio_probe,
.remove = __devexit_p(spi_gpio_remove), .remove = __devexit_p(spi_gpio_remove),
}; };
......
...@@ -197,6 +197,7 @@ static unsigned int spi_imx_clkdiv_2(unsigned int fin, ...@@ -197,6 +197,7 @@ static unsigned int spi_imx_clkdiv_2(unsigned int fin,
#define MX51_ECSPI_CONFIG_SCLKPOL(cs) (1 << ((cs) + 4)) #define MX51_ECSPI_CONFIG_SCLKPOL(cs) (1 << ((cs) + 4))
#define MX51_ECSPI_CONFIG_SBBCTRL(cs) (1 << ((cs) + 8)) #define MX51_ECSPI_CONFIG_SBBCTRL(cs) (1 << ((cs) + 8))
#define MX51_ECSPI_CONFIG_SSBPOL(cs) (1 << ((cs) + 12)) #define MX51_ECSPI_CONFIG_SSBPOL(cs) (1 << ((cs) + 12))
#define MX51_ECSPI_CONFIG_SCLKCTL(cs) (1 << ((cs) + 20))
#define MX51_ECSPI_INT 0x10 #define MX51_ECSPI_INT 0x10
#define MX51_ECSPI_INT_TEEN (1 << 0) #define MX51_ECSPI_INT_TEEN (1 << 0)
...@@ -287,9 +288,10 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx, ...@@ -287,9 +288,10 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
if (config->mode & SPI_CPHA) if (config->mode & SPI_CPHA)
cfg |= MX51_ECSPI_CONFIG_SCLKPHA(config->cs); cfg |= MX51_ECSPI_CONFIG_SCLKPHA(config->cs);
if (config->mode & SPI_CPOL) if (config->mode & SPI_CPOL) {
cfg |= MX51_ECSPI_CONFIG_SCLKPOL(config->cs); cfg |= MX51_ECSPI_CONFIG_SCLKPOL(config->cs);
cfg |= MX51_ECSPI_CONFIG_SCLKCTL(config->cs);
}
if (config->mode & SPI_CS_HIGH) if (config->mode & SPI_CS_HIGH)
cfg |= MX51_ECSPI_CONFIG_SSBPOL(config->cs); cfg |= MX51_ECSPI_CONFIG_SSBPOL(config->cs);
......
...@@ -494,7 +494,7 @@ static int __devinit mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr, ...@@ -494,7 +494,7 @@ static int __devinit mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr,
static int __devexit mpc512x_psc_spi_do_remove(struct device *dev) static int __devexit mpc512x_psc_spi_do_remove(struct device *dev)
{ {
struct spi_master *master = dev_get_drvdata(dev); struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
struct mpc512x_psc_spi *mps = spi_master_get_devdata(master); struct mpc512x_psc_spi *mps = spi_master_get_devdata(master);
flush_workqueue(mps->workqueue); flush_workqueue(mps->workqueue);
...@@ -503,6 +503,7 @@ static int __devexit mpc512x_psc_spi_do_remove(struct device *dev) ...@@ -503,6 +503,7 @@ static int __devexit mpc512x_psc_spi_do_remove(struct device *dev)
free_irq(mps->irq, mps); free_irq(mps->irq, mps);
if (mps->psc) if (mps->psc)
iounmap(mps->psc); iounmap(mps->psc);
spi_master_put(master);
return 0; return 0;
} }
......
...@@ -481,7 +481,7 @@ static int __devinit mpc52xx_psc_spi_of_probe(struct platform_device *op) ...@@ -481,7 +481,7 @@ static int __devinit mpc52xx_psc_spi_of_probe(struct platform_device *op)
static int __devexit mpc52xx_psc_spi_of_remove(struct platform_device *op) static int __devexit mpc52xx_psc_spi_of_remove(struct platform_device *op)
{ {
struct spi_master *master = dev_get_drvdata(&op->dev); struct spi_master *master = spi_master_get(dev_get_drvdata(&op->dev));
struct mpc52xx_psc_spi *mps = spi_master_get_devdata(master); struct mpc52xx_psc_spi *mps = spi_master_get_devdata(master);
flush_workqueue(mps->workqueue); flush_workqueue(mps->workqueue);
...@@ -490,6 +490,7 @@ static int __devexit mpc52xx_psc_spi_of_remove(struct platform_device *op) ...@@ -490,6 +490,7 @@ static int __devexit mpc52xx_psc_spi_of_remove(struct platform_device *op)
free_irq(mps->irq, mps); free_irq(mps->irq, mps);
if (mps->psc) if (mps->psc)
iounmap(mps->psc); iounmap(mps->psc);
spi_master_put(master);
return 0; return 0;
} }
......
...@@ -454,7 +454,7 @@ static int __devinit mpc52xx_spi_probe(struct platform_device *op) ...@@ -454,7 +454,7 @@ static int __devinit mpc52xx_spi_probe(struct platform_device *op)
GFP_KERNEL); GFP_KERNEL);
if (!ms->gpio_cs) { if (!ms->gpio_cs) {
rc = -ENOMEM; rc = -ENOMEM;
goto err_alloc; goto err_alloc_gpio;
} }
for (i = 0; i < ms->gpio_cs_count; i++) { for (i = 0; i < ms->gpio_cs_count; i++) {
...@@ -514,12 +514,13 @@ static int __devinit mpc52xx_spi_probe(struct platform_device *op) ...@@ -514,12 +514,13 @@ static int __devinit mpc52xx_spi_probe(struct platform_device *op)
err_register: err_register:
dev_err(&ms->master->dev, "initialization failed\n"); dev_err(&ms->master->dev, "initialization failed\n");
spi_master_put(master);
err_gpio: err_gpio:
while (i-- > 0) while (i-- > 0)
gpio_free(ms->gpio_cs[i]); gpio_free(ms->gpio_cs[i]);
kfree(ms->gpio_cs); kfree(ms->gpio_cs);
err_alloc_gpio:
spi_master_put(master);
err_alloc: err_alloc:
err_init: err_init:
iounmap(regs); iounmap(regs);
...@@ -528,7 +529,7 @@ static int __devinit mpc52xx_spi_probe(struct platform_device *op) ...@@ -528,7 +529,7 @@ static int __devinit mpc52xx_spi_probe(struct platform_device *op)
static int __devexit mpc52xx_spi_remove(struct platform_device *op) static int __devexit mpc52xx_spi_remove(struct platform_device *op)
{ {
struct spi_master *master = dev_get_drvdata(&op->dev); struct spi_master *master = spi_master_get(dev_get_drvdata(&op->dev));
struct mpc52xx_spi *ms = spi_master_get_devdata(master); struct mpc52xx_spi *ms = spi_master_get_devdata(master);
int i; int i;
...@@ -540,8 +541,8 @@ static int __devexit mpc52xx_spi_remove(struct platform_device *op) ...@@ -540,8 +541,8 @@ static int __devexit mpc52xx_spi_remove(struct platform_device *op)
kfree(ms->gpio_cs); kfree(ms->gpio_cs);
spi_unregister_master(master); spi_unregister_master(master);
spi_master_put(master);
iounmap(ms->regs); iounmap(ms->regs);
spi_master_put(master);
return 0; return 0;
} }
......
This diff is collapsed.
This diff is collapsed.
...@@ -36,12 +36,6 @@ ...@@ -36,12 +36,6 @@
#define ORION_SPI_CLK_PRESCALE_MASK 0x1F #define ORION_SPI_CLK_PRESCALE_MASK 0x1F
struct orion_spi { struct orion_spi {
struct work_struct work;
/* Lock access to transfer list. */
spinlock_t lock;
struct list_head msg_queue;
struct spi_master *master; struct spi_master *master;
void __iomem *base; void __iomem *base;
unsigned int max_speed; unsigned int max_speed;
...@@ -49,8 +43,6 @@ struct orion_spi { ...@@ -49,8 +43,6 @@ struct orion_spi {
struct clk *clk; struct clk *clk;
}; };
static struct workqueue_struct *orion_spi_wq;
static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg) static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg)
{ {
return orion_spi->base + reg; return orion_spi->base + reg;
...@@ -277,73 +269,78 @@ orion_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer) ...@@ -277,73 +269,78 @@ orion_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
} }
static void orion_spi_work(struct work_struct *work) static int orion_spi_transfer_one_message(struct spi_master *master,
struct spi_message *m)
{ {
struct orion_spi *orion_spi = struct orion_spi *orion_spi = spi_master_get_devdata(master);
container_of(work, struct orion_spi, work); struct spi_device *spi = m->spi;
struct spi_transfer *t = NULL;
spin_lock_irq(&orion_spi->lock); int par_override = 0;
while (!list_empty(&orion_spi->msg_queue)) { int status = 0;
struct spi_message *m; int cs_active = 0;
struct spi_device *spi;
struct spi_transfer *t = NULL;
int par_override = 0;
int status = 0;
int cs_active = 0;
m = container_of(orion_spi->msg_queue.next, struct spi_message,
queue);
list_del_init(&m->queue); /* Load defaults */
spin_unlock_irq(&orion_spi->lock); status = orion_spi_setup_transfer(spi, NULL);
spi = m->spi; if (status < 0)
goto msg_done;
/* Load defaults */ list_for_each_entry(t, &m->transfers, transfer_list) {
status = orion_spi_setup_transfer(spi, NULL); /* make sure buffer length is even when working in 16
* bit mode*/
if ((t->bits_per_word == 16) && (t->len & 1)) {
dev_err(&spi->dev,
"message rejected : "
"odd data length %d while in 16 bit mode\n",
t->len);
status = -EIO;
goto msg_done;
}
if (status < 0) if (t->speed_hz && t->speed_hz < orion_spi->min_speed) {
dev_err(&spi->dev,
"message rejected : "
"device min speed (%d Hz) exceeds "
"required transfer speed (%d Hz)\n",
orion_spi->min_speed, t->speed_hz);
status = -EIO;
goto msg_done; goto msg_done;
}
list_for_each_entry(t, &m->transfers, transfer_list) { if (par_override || t->speed_hz || t->bits_per_word) {
if (par_override || t->speed_hz || t->bits_per_word) { par_override = 1;
par_override = 1; status = orion_spi_setup_transfer(spi, t);
status = orion_spi_setup_transfer(spi, t); if (status < 0)
if (status < 0) break;
break; if (!t->speed_hz && !t->bits_per_word)
if (!t->speed_hz && !t->bits_per_word) par_override = 0;
par_override = 0;
}
if (!cs_active) {
orion_spi_set_cs(orion_spi, 1);
cs_active = 1;
}
if (t->len)
m->actual_length +=
orion_spi_write_read(spi, t);
if (t->delay_usecs)
udelay(t->delay_usecs);
if (t->cs_change) {
orion_spi_set_cs(orion_spi, 0);
cs_active = 0;
}
} }
msg_done: if (!cs_active) {
if (cs_active) orion_spi_set_cs(orion_spi, 1);
orion_spi_set_cs(orion_spi, 0); cs_active = 1;
}
m->status = status; if (t->len)
m->complete(m->context); m->actual_length += orion_spi_write_read(spi, t);
spin_lock_irq(&orion_spi->lock); if (t->delay_usecs)
udelay(t->delay_usecs);
if (t->cs_change) {
orion_spi_set_cs(orion_spi, 0);
cs_active = 0;
}
} }
spin_unlock_irq(&orion_spi->lock); msg_done:
if (cs_active)
orion_spi_set_cs(orion_spi, 0);
m->status = status;
spi_finalize_current_message(master);
return 0;
} }
static int __init orion_spi_reset(struct orion_spi *orion_spi) static int __init orion_spi_reset(struct orion_spi *orion_spi)
...@@ -376,75 +373,6 @@ static int orion_spi_setup(struct spi_device *spi) ...@@ -376,75 +373,6 @@ static int orion_spi_setup(struct spi_device *spi)
return 0; return 0;
} }
static int orion_spi_transfer(struct spi_device *spi, struct spi_message *m)
{
struct orion_spi *orion_spi;
struct spi_transfer *t = NULL;
unsigned long flags;
m->actual_length = 0;
m->status = 0;
/* reject invalid messages and transfers */
if (list_empty(&m->transfers) || !m->complete)
return -EINVAL;
orion_spi = spi_master_get_devdata(spi->master);
list_for_each_entry(t, &m->transfers, transfer_list) {
unsigned int bits_per_word = spi->bits_per_word;
if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) {
dev_err(&spi->dev,
"message rejected : "
"invalid transfer data buffers\n");
goto msg_rejected;
}
if (t->bits_per_word)
bits_per_word = t->bits_per_word;
if ((bits_per_word != 8) && (bits_per_word != 16)) {
dev_err(&spi->dev,
"message rejected : "
"invalid transfer bits_per_word (%d bits)\n",
bits_per_word);
goto msg_rejected;
}
/*make sure buffer length is even when working in 16 bit mode*/
if ((t->bits_per_word == 16) && (t->len & 1)) {
dev_err(&spi->dev,
"message rejected : "
"odd data length (%d) while in 16 bit mode\n",
t->len);
goto msg_rejected;
}
if (t->speed_hz && t->speed_hz < orion_spi->min_speed) {
dev_err(&spi->dev,
"message rejected : "
"device min speed (%d Hz) exceeds "
"required transfer speed (%d Hz)\n",
orion_spi->min_speed, t->speed_hz);
goto msg_rejected;
}
}
spin_lock_irqsave(&orion_spi->lock, flags);
list_add_tail(&m->queue, &orion_spi->msg_queue);
queue_work(orion_spi_wq, &orion_spi->work);
spin_unlock_irqrestore(&orion_spi->lock, flags);
return 0;
msg_rejected:
/* Message rejected and not queued */
m->status = -EINVAL;
if (m->complete)
m->complete(m->context);
return -EINVAL;
}
static int __init orion_spi_probe(struct platform_device *pdev) static int __init orion_spi_probe(struct platform_device *pdev)
{ {
struct spi_master *master; struct spi_master *master;
...@@ -474,7 +402,7 @@ static int __init orion_spi_probe(struct platform_device *pdev) ...@@ -474,7 +402,7 @@ static int __init orion_spi_probe(struct platform_device *pdev)
master->mode_bits = 0; master->mode_bits = 0;
master->setup = orion_spi_setup; master->setup = orion_spi_setup;
master->transfer = orion_spi_transfer; master->transfer_one_message = orion_spi_transfer_one_message;
master->num_chipselect = ORION_NUM_CHIPSELECTS; master->num_chipselect = ORION_NUM_CHIPSELECTS;
dev_set_drvdata(&pdev->dev, master); dev_set_drvdata(&pdev->dev, master);
...@@ -507,11 +435,6 @@ static int __init orion_spi_probe(struct platform_device *pdev) ...@@ -507,11 +435,6 @@ static int __init orion_spi_probe(struct platform_device *pdev)
} }
spi->base = ioremap(r->start, SZ_1K); spi->base = ioremap(r->start, SZ_1K);
INIT_WORK(&spi->work, orion_spi_work);
spin_lock_init(&spi->lock);
INIT_LIST_HEAD(&spi->msg_queue);
if (orion_spi_reset(spi) < 0) if (orion_spi_reset(spi) < 0)
goto out_rel_mem; goto out_rel_mem;
...@@ -536,14 +459,12 @@ static int __init orion_spi_probe(struct platform_device *pdev) ...@@ -536,14 +459,12 @@ static int __init orion_spi_probe(struct platform_device *pdev)
static int __exit orion_spi_remove(struct platform_device *pdev) static int __exit orion_spi_remove(struct platform_device *pdev)
{ {
struct spi_master *master; struct spi_master *master;
struct orion_spi *spi;
struct resource *r; struct resource *r;
struct orion_spi *spi;
master = dev_get_drvdata(&pdev->dev); master = dev_get_drvdata(&pdev->dev);
spi = spi_master_get_devdata(master); spi = spi_master_get_devdata(master);
cancel_work_sync(&spi->work);
clk_disable_unprepare(spi->clk); clk_disable_unprepare(spi->clk);
clk_put(spi->clk); clk_put(spi->clk);
...@@ -574,21 +495,13 @@ static struct platform_driver orion_spi_driver = { ...@@ -574,21 +495,13 @@ static struct platform_driver orion_spi_driver = {
static int __init orion_spi_init(void) static int __init orion_spi_init(void)
{ {
orion_spi_wq = create_singlethread_workqueue(
orion_spi_driver.driver.name);
if (orion_spi_wq == NULL)
return -ENOMEM;
return platform_driver_probe(&orion_spi_driver, orion_spi_probe); return platform_driver_probe(&orion_spi_driver, orion_spi_probe);
} }
module_init(orion_spi_init); module_init(orion_spi_init);
static void __exit orion_spi_exit(void) static void __exit orion_spi_exit(void)
{ {
flush_workqueue(orion_spi_wq);
platform_driver_unregister(&orion_spi_driver); platform_driver_unregister(&orion_spi_driver);
destroy_workqueue(orion_spi_wq);
} }
module_exit(orion_spi_exit); module_exit(orion_spi_exit);
......
This diff is collapsed.
...@@ -611,6 +611,7 @@ static int __devinit s3c24xx_spi_probe(struct platform_device *pdev) ...@@ -611,6 +611,7 @@ static int __devinit s3c24xx_spi_probe(struct platform_device *pdev)
if (!pdata->set_cs) { if (!pdata->set_cs) {
if (pdata->pin_cs < 0) { if (pdata->pin_cs < 0) {
dev_err(&pdev->dev, "No chipselect pin\n"); dev_err(&pdev->dev, "No chipselect pin\n");
err = -EINVAL;
goto err_register; goto err_register;
} }
......
...@@ -976,7 +976,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi) ...@@ -976,7 +976,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
spi_set_ctldata(spi, NULL); spi_set_ctldata(spi, NULL);
err_gpio_req: err_gpio_req:
kfree(cs); if (spi->dev.of_node)
kfree(cs);
return err; return err;
} }
...@@ -1409,7 +1410,7 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) ...@@ -1409,7 +1410,7 @@ static int s3c64xx_spi_remove(struct platform_device *pdev)
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int s3c64xx_spi_suspend(struct device *dev) static int s3c64xx_spi_suspend(struct device *dev)
{ {
struct spi_master *master = spi_master_get(dev_get_drvdata(dev)); struct spi_master *master = 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);
spi_master_suspend(master); spi_master_suspend(master);
...@@ -1428,7 +1429,7 @@ static int s3c64xx_spi_suspend(struct device *dev) ...@@ -1428,7 +1429,7 @@ static int s3c64xx_spi_suspend(struct device *dev)
static int s3c64xx_spi_resume(struct device *dev) static int s3c64xx_spi_resume(struct device *dev)
{ {
struct spi_master *master = spi_master_get(dev_get_drvdata(dev)); struct spi_master *master = 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;
...@@ -1452,7 +1453,7 @@ static int s3c64xx_spi_resume(struct device *dev) ...@@ -1452,7 +1453,7 @@ static int s3c64xx_spi_resume(struct device *dev)
#ifdef CONFIG_PM_RUNTIME #ifdef CONFIG_PM_RUNTIME
static int s3c64xx_spi_runtime_suspend(struct device *dev) static int s3c64xx_spi_runtime_suspend(struct device *dev)
{ {
struct spi_master *master = spi_master_get(dev_get_drvdata(dev)); struct spi_master *master = 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);
clk_disable(sdd->clk); clk_disable(sdd->clk);
...@@ -1463,7 +1464,7 @@ static int s3c64xx_spi_runtime_suspend(struct device *dev) ...@@ -1463,7 +1464,7 @@ static int s3c64xx_spi_runtime_suspend(struct device *dev)
static int s3c64xx_spi_runtime_resume(struct device *dev) static int s3c64xx_spi_runtime_resume(struct device *dev)
{ {
struct spi_master *master = spi_master_get(dev_get_drvdata(dev)); struct spi_master *master = 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);
clk_enable(sdd->src_clk); clk_enable(sdd->src_clk);
......
/*
* NXP SC18IS602/603 SPI driver
*
* Copyright (C) Guenter Roeck <linux@roeck-us.net>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/platform_data/sc18is602.h>
enum chips { sc18is602, sc18is602b, sc18is603 };
#define SC18IS602_BUFSIZ 200
#define SC18IS602_CLOCK 7372000
#define SC18IS602_MODE_CPHA BIT(2)
#define SC18IS602_MODE_CPOL BIT(3)
#define SC18IS602_MODE_LSB_FIRST BIT(5)
#define SC18IS602_MODE_CLOCK_DIV_4 0x0
#define SC18IS602_MODE_CLOCK_DIV_16 0x1
#define SC18IS602_MODE_CLOCK_DIV_64 0x2
#define SC18IS602_MODE_CLOCK_DIV_128 0x3
struct sc18is602 {
struct spi_master *master;
struct device *dev;
u8 ctrl;
u32 freq;
u32 speed;
/* I2C data */
struct i2c_client *client;
enum chips id;
u8 buffer[SC18IS602_BUFSIZ + 1];
int tlen; /* Data queued for tx in buffer */
int rindex; /* Receive data index in buffer */
};
static int sc18is602_wait_ready(struct sc18is602 *hw, int len)
{
int i, err;
int usecs = 1000000 * len / hw->speed + 1;
u8 dummy[1];
for (i = 0; i < 10; i++) {
err = i2c_master_recv(hw->client, dummy, 1);
if (err >= 0)
return 0;
usleep_range(usecs, usecs * 2);
}
return -ETIMEDOUT;
}
static int sc18is602_txrx(struct sc18is602 *hw, struct spi_message *msg,
struct spi_transfer *t, bool do_transfer)
{
unsigned int len = t->len;
int ret;
if (hw->tlen == 0) {
/* First byte (I2C command) is chip select */
hw->buffer[0] = 1 << msg->spi->chip_select;
hw->tlen = 1;
hw->rindex = 0;
}
/*
* We can not immediately send data to the chip, since each I2C message
* resembles a full SPI message (from CS active to CS inactive).
* Enqueue messages up to the first read or until do_transfer is true.
*/
if (t->tx_buf) {
memcpy(&hw->buffer[hw->tlen], t->tx_buf, len);
hw->tlen += len;
if (t->rx_buf)
do_transfer = true;
else
hw->rindex = hw->tlen - 1;
} else if (t->rx_buf) {
/*
* For receive-only transfers we still need to perform a dummy
* write to receive data from the SPI chip.
* Read data starts at the end of transmit data (minus 1 to
* account for CS).
*/
hw->rindex = hw->tlen - 1;
memset(&hw->buffer[hw->tlen], 0, len);
hw->tlen += len;
do_transfer = true;
}
if (do_transfer && hw->tlen > 1) {
ret = sc18is602_wait_ready(hw, SC18IS602_BUFSIZ);
if (ret < 0)
return ret;
ret = i2c_master_send(hw->client, hw->buffer, hw->tlen);
if (ret < 0)
return ret;
if (ret != hw->tlen)
return -EIO;
if (t->rx_buf) {
int rlen = hw->rindex + len;
ret = sc18is602_wait_ready(hw, hw->tlen);
if (ret < 0)
return ret;
ret = i2c_master_recv(hw->client, hw->buffer, rlen);
if (ret < 0)
return ret;
if (ret != rlen)
return -EIO;
memcpy(t->rx_buf, &hw->buffer[hw->rindex], len);
}
hw->tlen = 0;
}
return len;
}
static int sc18is602_setup_transfer(struct sc18is602 *hw, u32 hz, u8 mode)
{
u8 ctrl = 0;
int ret;
if (mode & SPI_CPHA)
ctrl |= SC18IS602_MODE_CPHA;
if (mode & SPI_CPOL)
ctrl |= SC18IS602_MODE_CPOL;
if (mode & SPI_LSB_FIRST)
ctrl |= SC18IS602_MODE_LSB_FIRST;
/* Find the closest clock speed */
if (hz >= hw->freq / 4) {
ctrl |= SC18IS602_MODE_CLOCK_DIV_4;
hw->speed = hw->freq / 4;
} else if (hz >= hw->freq / 16) {
ctrl |= SC18IS602_MODE_CLOCK_DIV_16;
hw->speed = hw->freq / 16;
} else if (hz >= hw->freq / 64) {
ctrl |= SC18IS602_MODE_CLOCK_DIV_64;
hw->speed = hw->freq / 64;
} else {
ctrl |= SC18IS602_MODE_CLOCK_DIV_128;
hw->speed = hw->freq / 128;
}
/*
* Don't do anything if the control value did not change. The initial
* value of 0xff for hw->ctrl ensures that the correct mode will be set
* with the first call to this function.
*/
if (ctrl == hw->ctrl)
return 0;
ret = i2c_smbus_write_byte_data(hw->client, 0xf0, ctrl);
if (ret < 0)
return ret;
hw->ctrl = ctrl;
return 0;
}
static int sc18is602_check_transfer(struct spi_device *spi,
struct spi_transfer *t, int tlen)
{
int bpw;
uint32_t hz;
if (t && t->len + tlen > SC18IS602_BUFSIZ)
return -EINVAL;
bpw = spi->bits_per_word;
if (t && t->bits_per_word)
bpw = t->bits_per_word;
if (bpw != 8)
return -EINVAL;
hz = spi->max_speed_hz;
if (t && t->speed_hz)
hz = t->speed_hz;
if (hz == 0)
return -EINVAL;
return 0;
}
static int sc18is602_transfer_one(struct spi_master *master,
struct spi_message *m)
{
struct sc18is602 *hw = spi_master_get_devdata(master);
struct spi_device *spi = m->spi;
struct spi_transfer *t;
int status = 0;
/* SC18IS602 does not support CS2 */
if (hw->id == sc18is602 && spi->chip_select == 2) {
status = -ENXIO;
goto error;
}
hw->tlen = 0;
list_for_each_entry(t, &m->transfers, transfer_list) {
u32 hz = t->speed_hz ? : spi->max_speed_hz;
bool do_transfer;
status = sc18is602_check_transfer(spi, t, hw->tlen);
if (status < 0)
break;
status = sc18is602_setup_transfer(hw, hz, spi->mode);
if (status < 0)
break;
do_transfer = t->cs_change || list_is_last(&t->transfer_list,
&m->transfers);
if (t->len) {
status = sc18is602_txrx(hw, m, t, do_transfer);
if (status < 0)
break;
m->actual_length += status;
}
status = 0;
if (t->delay_usecs)
udelay(t->delay_usecs);
}
error:
m->status = status;
spi_finalize_current_message(master);
return status;
}
static int sc18is602_setup(struct spi_device *spi)
{
if (!spi->bits_per_word)
spi->bits_per_word = 8;
if (spi->mode & ~(SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST))
return -EINVAL;
return sc18is602_check_transfer(spi, NULL, 0);
}
static int sc18is602_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct device_node *np = dev->of_node;
struct sc18is602_platform_data *pdata = dev_get_platdata(dev);
struct sc18is602 *hw;
struct spi_master *master;
int error;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
return -EINVAL;
master = spi_alloc_master(dev, sizeof(struct sc18is602));
if (!master)
return -ENOMEM;
hw = spi_master_get_devdata(master);
i2c_set_clientdata(client, hw);
hw->master = master;
hw->client = client;
hw->dev = dev;
hw->ctrl = 0xff;
hw->id = id->driver_data;
switch (hw->id) {
case sc18is602:
case sc18is602b:
master->num_chipselect = 4;
hw->freq = SC18IS602_CLOCK;
break;
case sc18is603:
master->num_chipselect = 2;
if (pdata) {
hw->freq = pdata->clock_frequency;
} else {
const __be32 *val;
int len;
val = of_get_property(np, "clock-frequency", &len);
if (val && len >= sizeof(__be32))
hw->freq = be32_to_cpup(val);
}
if (!hw->freq)
hw->freq = SC18IS602_CLOCK;
break;
}
master->bus_num = client->adapter->nr;
master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST;
master->setup = sc18is602_setup;
master->transfer_one_message = sc18is602_transfer_one;
master->dev.of_node = np;
error = spi_register_master(master);
if (error)
goto error_reg;
return 0;
error_reg:
spi_master_put(master);
return error;
}
static int sc18is602_remove(struct i2c_client *client)
{
struct sc18is602 *hw = i2c_get_clientdata(client);
struct spi_master *master = hw->master;
spi_unregister_master(master);
return 0;
}
static const struct i2c_device_id sc18is602_id[] = {
{ "sc18is602", sc18is602 },
{ "sc18is602b", sc18is602b },
{ "sc18is603", sc18is603 },
{ }
};
MODULE_DEVICE_TABLE(i2c, sc18is602_id);
static struct i2c_driver sc18is602_driver = {
.driver = {
.name = "sc18is602",
},
.probe = sc18is602_probe,
.remove = sc18is602_remove,
.id_table = sc18is602_id,
};
module_i2c_driver(sc18is602_driver);
MODULE_DESCRIPTION("SC18IC602/603 SPI Master Driver");
MODULE_AUTHOR("Guenter Roeck");
MODULE_LICENSE("GPL");
...@@ -283,7 +283,7 @@ static int __devinit hspi_probe(struct platform_device *pdev) ...@@ -283,7 +283,7 @@ static int __devinit hspi_probe(struct platform_device *pdev)
ret = spi_register_master(master); ret = spi_register_master(master);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "spi_register_master error.\n"); dev_err(&pdev->dev, "spi_register_master error.\n");
goto error2; goto error1;
} }
pm_runtime_enable(&pdev->dev); pm_runtime_enable(&pdev->dev);
...@@ -292,8 +292,6 @@ static int __devinit hspi_probe(struct platform_device *pdev) ...@@ -292,8 +292,6 @@ static int __devinit hspi_probe(struct platform_device *pdev)
return 0; return 0;
error2:
devm_iounmap(hspi->dev, hspi->addr);
error1: error1:
clk_put(clk); clk_put(clk);
error0: error0:
...@@ -310,7 +308,6 @@ static int __devexit hspi_remove(struct platform_device *pdev) ...@@ -310,7 +308,6 @@ static int __devexit hspi_remove(struct platform_device *pdev)
clk_put(hspi->clk); clk_put(hspi->clk);
spi_unregister_master(hspi->master); spi_unregister_master(hspi->master);
devm_iounmap(hspi->dev, hspi->addr);
return 0; return 0;
} }
......
...@@ -594,9 +594,7 @@ static int __devexit stmp_spi_remove(struct platform_device *dev) ...@@ -594,9 +594,7 @@ static int __devexit stmp_spi_remove(struct platform_device *dev)
struct stmp_spi *ss; struct stmp_spi *ss;
struct spi_master *master; struct spi_master *master;
master = platform_get_drvdata(dev); master = spi_master_get(platform_get_drvdata(dev));
if (master == NULL)
goto out0;
ss = spi_master_get_devdata(master); ss = spi_master_get_devdata(master);
spi_unregister_master(master); spi_unregister_master(master);
...@@ -609,8 +607,6 @@ static int __devexit stmp_spi_remove(struct platform_device *dev) ...@@ -609,8 +607,6 @@ static int __devexit stmp_spi_remove(struct platform_device *dev)
destroy_workqueue(ss->workqueue); destroy_workqueue(ss->workqueue);
iounmap(ss->regs); iounmap(ss->regs);
spi_master_put(master); spi_master_put(master);
platform_set_drvdata(dev, NULL);
out0:
return 0; return 0;
} }
......
This diff is collapsed.
...@@ -316,18 +316,7 @@ static struct spi_driver tle62x0_driver = { ...@@ -316,18 +316,7 @@ static struct spi_driver tle62x0_driver = {
.remove = __devexit_p(tle62x0_remove), .remove = __devexit_p(tle62x0_remove),
}; };
static __init int tle62x0_init(void) module_spi_driver(tle62x0_driver);
{
return spi_register_driver(&tle62x0_driver);
}
static __exit void tle62x0_exit(void)
{
spi_unregister_driver(&tle62x0_driver);
}
module_init(tle62x0_init);
module_exit(tle62x0_exit);
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
MODULE_DESCRIPTION("TLE62x0 SPI driver"); MODULE_DESCRIPTION("TLE62x0 SPI driver");
......
...@@ -1536,8 +1536,6 @@ static int __devexit pch_spi_pd_remove(struct platform_device *plat_dev) ...@@ -1536,8 +1536,6 @@ static int __devexit pch_spi_pd_remove(struct platform_device *plat_dev)
pci_iounmap(board_dat->pdev, data->io_remap_addr); pci_iounmap(board_dat->pdev, data->io_remap_addr);
spi_unregister_master(data->master); spi_unregister_master(data->master);
spi_master_put(data->master);
platform_set_drvdata(plat_dev, NULL);
return 0; return 0;
} }
......
...@@ -244,6 +244,7 @@ struct dma_chan; ...@@ -244,6 +244,7 @@ struct dma_chan;
* 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 * @rt: indicates the controller should run the message pump with realtime
* priority to minimise the transfer latency on the bus. * priority to minimise the transfer latency on the bus.
* @chipselects: list of <num_chipselects> chip select gpios
*/ */
struct pl022_ssp_controller { struct pl022_ssp_controller {
u16 bus_id; u16 bus_id;
...@@ -254,6 +255,7 @@ struct pl022_ssp_controller { ...@@ -254,6 +255,7 @@ struct pl022_ssp_controller {
void *dma_tx_param; void *dma_tx_param;
int autosuspend_delay; int autosuspend_delay;
bool rt; bool rt;
int *chipselects;
}; };
/** /**
......
/*
* Platform data for NXP SC18IS602/603
*
* Copyright (C) 2012 Guenter Roeck <linux@roeck-us.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* For further information, see the Documentation/spi/sc18is602 file.
*/
/**
* struct sc18is602_platform_data - sc18is602 info
* @clock_frequency SC18IS603 oscillator frequency
*/
struct sc18is602_platform_data {
u32 clock_frequency;
};
/*
* include/linux/spi/mxs-spi.h
*
* Freescale i.MX233/i.MX28 SPI controller register definition
*
* Copyright 2008 Embedded Alley Solutions, Inc.
* Copyright 2009-2011 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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, Boston, MA 02110-1301 USA.
*/
#ifndef __LINUX_SPI_MXS_SPI_H__
#define __LINUX_SPI_MXS_SPI_H__
#include <linux/fsl/mxs-dma.h>
#define ssp_is_old(host) ((host)->devid == IMX23_SSP)
/* SSP registers */
#define HW_SSP_CTRL0 0x000
#define BM_SSP_CTRL0_RUN (1 << 29)
#define BM_SSP_CTRL0_SDIO_IRQ_CHECK (1 << 28)
#define BM_SSP_CTRL0_LOCK_CS (1 << 27)
#define BM_SSP_CTRL0_IGNORE_CRC (1 << 26)
#define BM_SSP_CTRL0_READ (1 << 25)
#define BM_SSP_CTRL0_DATA_XFER (1 << 24)
#define BP_SSP_CTRL0_BUS_WIDTH 22
#define BM_SSP_CTRL0_BUS_WIDTH (0x3 << 22)
#define BM_SSP_CTRL0_WAIT_FOR_IRQ (1 << 21)
#define BM_SSP_CTRL0_WAIT_FOR_CMD (1 << 20)
#define BM_SSP_CTRL0_LONG_RESP (1 << 19)
#define BM_SSP_CTRL0_GET_RESP (1 << 17)
#define BM_SSP_CTRL0_ENABLE (1 << 16)
#define BP_SSP_CTRL0_XFER_COUNT 0
#define BM_SSP_CTRL0_XFER_COUNT 0xffff
#define HW_SSP_CMD0 0x010
#define BM_SSP_CMD0_DBL_DATA_RATE_EN (1 << 25)
#define BM_SSP_CMD0_SLOW_CLKING_EN (1 << 22)
#define BM_SSP_CMD0_CONT_CLKING_EN (1 << 21)
#define BM_SSP_CMD0_APPEND_8CYC (1 << 20)
#define BP_SSP_CMD0_BLOCK_SIZE 16
#define BM_SSP_CMD0_BLOCK_SIZE (0xf << 16)
#define BP_SSP_CMD0_BLOCK_COUNT 8
#define BM_SSP_CMD0_BLOCK_COUNT (0xff << 8)
#define BP_SSP_CMD0_CMD 0
#define BM_SSP_CMD0_CMD 0xff
#define HW_SSP_CMD1 0x020
#define HW_SSP_XFER_SIZE 0x030
#define HW_SSP_BLOCK_SIZE 0x040
#define BP_SSP_BLOCK_SIZE_BLOCK_COUNT 4
#define BM_SSP_BLOCK_SIZE_BLOCK_COUNT (0xffffff << 4)
#define BP_SSP_BLOCK_SIZE_BLOCK_SIZE 0
#define BM_SSP_BLOCK_SIZE_BLOCK_SIZE 0xf
#define HW_SSP_TIMING(h) (ssp_is_old(h) ? 0x050 : 0x070)
#define BP_SSP_TIMING_TIMEOUT 16
#define BM_SSP_TIMING_TIMEOUT (0xffff << 16)
#define BP_SSP_TIMING_CLOCK_DIVIDE 8
#define BM_SSP_TIMING_CLOCK_DIVIDE (0xff << 8)
#define BF_SSP_TIMING_CLOCK_DIVIDE(v) \
(((v) << 8) & BM_SSP_TIMING_CLOCK_DIVIDE)
#define BP_SSP_TIMING_CLOCK_RATE 0
#define BM_SSP_TIMING_CLOCK_RATE 0xff
#define BF_SSP_TIMING_CLOCK_RATE(v) \
(((v) << 0) & BM_SSP_TIMING_CLOCK_RATE)
#define HW_SSP_CTRL1(h) (ssp_is_old(h) ? 0x060 : 0x080)
#define BM_SSP_CTRL1_SDIO_IRQ (1 << 31)
#define BM_SSP_CTRL1_SDIO_IRQ_EN (1 << 30)
#define BM_SSP_CTRL1_RESP_ERR_IRQ (1 << 29)
#define BM_SSP_CTRL1_RESP_ERR_IRQ_EN (1 << 28)
#define BM_SSP_CTRL1_RESP_TIMEOUT_IRQ (1 << 27)
#define BM_SSP_CTRL1_RESP_TIMEOUT_IRQ_EN (1 << 26)
#define BM_SSP_CTRL1_DATA_TIMEOUT_IRQ (1 << 25)
#define BM_SSP_CTRL1_DATA_TIMEOUT_IRQ_EN (1 << 24)
#define BM_SSP_CTRL1_DATA_CRC_IRQ (1 << 23)
#define BM_SSP_CTRL1_DATA_CRC_IRQ_EN (1 << 22)
#define BM_SSP_CTRL1_FIFO_UNDERRUN_IRQ (1 << 21)
#define BM_SSP_CTRL1_FIFO_UNDERRUN_IRQ_EN (1 << 20)
#define BM_SSP_CTRL1_RECV_TIMEOUT_IRQ (1 << 17)
#define BM_SSP_CTRL1_RECV_TIMEOUT_IRQ_EN (1 << 16)
#define BM_SSP_CTRL1_FIFO_OVERRUN_IRQ (1 << 15)
#define BM_SSP_CTRL1_FIFO_OVERRUN_IRQ_EN (1 << 14)
#define BM_SSP_CTRL1_DMA_ENABLE (1 << 13)
#define BM_SSP_CTRL1_PHASE (1 << 10)
#define BM_SSP_CTRL1_POLARITY (1 << 9)
#define BP_SSP_CTRL1_WORD_LENGTH 4
#define BM_SSP_CTRL1_WORD_LENGTH (0xf << 4)
#define BF_SSP_CTRL1_WORD_LENGTH(v) \
(((v) << 4) & BM_SSP_CTRL1_WORD_LENGTH)
#define BV_SSP_CTRL1_WORD_LENGTH__FOUR_BITS 0x3
#define BV_SSP_CTRL1_WORD_LENGTH__EIGHT_BITS 0x7
#define BV_SSP_CTRL1_WORD_LENGTH__SIXTEEN_BITS 0xF
#define BP_SSP_CTRL1_SSP_MODE 0
#define BM_SSP_CTRL1_SSP_MODE 0xf
#define BF_SSP_CTRL1_SSP_MODE(v) \
(((v) << 0) & BM_SSP_CTRL1_SSP_MODE)
#define BV_SSP_CTRL1_SSP_MODE__SPI 0x0
#define BV_SSP_CTRL1_SSP_MODE__SSI 0x1
#define BV_SSP_CTRL1_SSP_MODE__SD_MMC 0x3
#define BV_SSP_CTRL1_SSP_MODE__MS 0x4
#define HW_SSP_DATA(h) (ssp_is_old(h) ? 0x070 : 0x090)
#define HW_SSP_SDRESP0(h) (ssp_is_old(h) ? 0x080 : 0x0a0)
#define HW_SSP_SDRESP1(h) (ssp_is_old(h) ? 0x090 : 0x0b0)
#define HW_SSP_SDRESP2(h) (ssp_is_old(h) ? 0x0a0 : 0x0c0)
#define HW_SSP_SDRESP3(h) (ssp_is_old(h) ? 0x0b0 : 0x0d0)
#define HW_SSP_STATUS(h) (ssp_is_old(h) ? 0x0c0 : 0x100)
#define BM_SSP_STATUS_CARD_DETECT (1 << 28)
#define BM_SSP_STATUS_SDIO_IRQ (1 << 17)
#define BM_SSP_STATUS_FIFO_EMPTY (1 << 5)
#define BF_SSP(value, field) (((value) << BP_SSP_##field) & BM_SSP_##field)
#define SSP_PIO_NUM 3
enum mxs_ssp_id {
IMX23_SSP,
IMX28_SSP,
};
struct mxs_ssp {
struct device *dev;
void __iomem *base;
struct clk *clk;
unsigned int clk_rate;
enum mxs_ssp_id devid;
int dma_channel;
struct dma_chan *dmach;
struct mxs_dma_data dma_data;
unsigned int dma_dir;
enum dma_transfer_direction slave_dirn;
u32 ssp_pio_words[SSP_PIO_NUM];
};
void mxs_ssp_set_clk_rate(struct mxs_ssp *ssp, unsigned int rate);
#endif /* __LINUX_SPI_MXS_SPI_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment