forked from Minki/linux
spi: Updates for v3.7
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. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIcBAABAgAGBQJQarn8AAoJEOoSHmUN5Tg49U4QAKeAPTxlKBSwHuJUpvKsCFF7 rfN9wsViOigcItsk065C1g20wViw6t8pSFkNmRzmbsIdSofqCYZlJapgWM5j2cXp BXs8z7za0JmK62ftS6C3IydacJPRDY/zd0fnSqXnhQOG7Z3YxDYJrM3Ugc97ZE6+ dg9NUn6rw9ZSwHEwpl08H8tLYqNkd8A+wayPYKr0GSWX7EAqS8cJEWjS0DtvDAm1 LxMx4BQHCorR6ydUZJrfhG6fLsubhIuMnXI4Ajhv/Ictl1qHH1Zx3mIAB+BNVV/B RlaQxPycr0eSikZ9UPMNST2o8vCRvamSfguPfBHMzRdRBp8M3TOKkNM2PmEGzCJW pgpDsMNAkEfktGqQfMTBJJMbAPLux1W5mPjfcjt7vOS+q6lkFPtecGdKbynnmLX6 Vf6nImwp1vyNIvcCip0YZId+j/LrQimuDfMQOH99mwxbkNqMMA401bsxEt8ELmut Qt1XEFoS6Xxj8n2BhTS/lMgEzz8zOuH9uOJI0hpNtctOfue6vqPm76GaefVC1i/B jpMpUwexwsFc6IhekVFG0MAkolMhFmhlTwk9N24yfgArH8D1pa3emBFN762BQUHE Ba7pghcwZADm8rT5SFNoxblcyekT/hCSRSiQ55MHUpqdH77/eVA1Lah4PaHiOqxz b0mu9Ae98K077vGP3Z6r =ld23 -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
7fe0b14b72
22
Documentation/devicetree/bindings/spi/mxs-spi.txt
Normal file
22
Documentation/devicetree/bindings/spi/mxs-spi.txt
Normal file
@ -0,0 +1,22 @@
|
||||
* 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
|
||||
chip selects. Individual drivers can define additional properties to
|
||||
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
|
||||
contain the following properties.
|
||||
- reg - (required) chip select address of device.
|
||||
|
29
Documentation/devicetree/bindings/spi/spi-gpio.txt
Normal file
29
Documentation/devicetree/bindings/spi/spi-gpio.txt
Normal file
@ -0,0 +1,29 @@
|
||||
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 */
|
||||
};
|
||||
|
23
Documentation/devicetree/bindings/spi/spi-sc18is602.txt
Normal file
23
Documentation/devicetree/bindings/spi/spi-sc18is602.txt
Normal file
@ -0,0 +1,23 @@
|
||||
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:
|
||||
- interrupts : Should contain SPI controller interrupt
|
||||
|
||||
Optional properties:
|
||||
- num-cs : total number of chipselects
|
||||
- cs-gpios : should specify GPIOs used for chipselects.
|
||||
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.
|
||||
- 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
|
||||
|
||||
|
36
Documentation/spi/spi-sc18is602
Normal file
36
Documentation/spi/spi-sc18is602
Normal file
@ -0,0 +1,36 @@
|
||||
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[] = {
|
||||
{
|
||||
.dev = &uart0_device.dev,
|
||||
},
|
||||
{
|
||||
.dev = &pl022_device.dev,
|
||||
},
|
||||
{
|
||||
.dev = &mmcsd_device.dev,
|
||||
},
|
||||
|
@ -2,7 +2,7 @@
|
||||
# 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_IMX28) += clk-imx28.o
|
||||
|
62
drivers/clk/mxs/clk-ssp.c
Normal file
62
drivers/clk/mxs/clk-ssp.c
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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);
|
@ -41,91 +41,13 @@
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/fsl/mxs-dma.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/stmp_device.h>
|
||||
#include <linux/mmc/mxs-mmc.h>
|
||||
#include <linux/spi/mxs-spi.h>
|
||||
|
||||
#define DRIVER_NAME "mxs-mmc"
|
||||
|
||||
/* card detect polling timeout */
|
||||
#define MXS_MMC_DETECT_TIMEOUT (HZ/2)
|
||||
|
||||
#define ssp_is_old(host) ((host)->devid == IMX23_MMC)
|
||||
|
||||
/* 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_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_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 BP_SSP_TIMING_CLOCK_RATE (0)
|
||||
#define BM_SSP_TIMING_CLOCK_RATE (0xff)
|
||||
#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_POLARITY (1 << 9)
|
||||
#define BP_SSP_CTRL1_WORD_LENGTH (4)
|
||||
#define BM_SSP_CTRL1_WORD_LENGTH (0xf << 4)
|
||||
#define BP_SSP_CTRL1_SSP_MODE (0)
|
||||
#define BM_SSP_CTRL1_SSP_MODE (0xf)
|
||||
#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 BF_SSP(value, field) (((value) << BP_SSP_##field) & BM_SSP_##field)
|
||||
|
||||
#define MXS_MMC_IRQ_BITS (BM_SSP_CTRL1_SDIO_IRQ | \
|
||||
BM_SSP_CTRL1_RESP_ERR_IRQ | \
|
||||
BM_SSP_CTRL1_RESP_TIMEOUT_IRQ | \
|
||||
@ -135,31 +57,17 @@
|
||||
BM_SSP_CTRL1_RECV_TIMEOUT_IRQ | \
|
||||
BM_SSP_CTRL1_FIFO_OVERRUN_IRQ)
|
||||
|
||||
#define SSP_PIO_NUM 3
|
||||
|
||||
enum mxs_mmc_id {
|
||||
IMX23_MMC,
|
||||
IMX28_MMC,
|
||||
};
|
||||
/* card detect polling timeout */
|
||||
#define MXS_MMC_DETECT_TIMEOUT (HZ/2)
|
||||
|
||||
struct mxs_mmc_host {
|
||||
struct mxs_ssp ssp;
|
||||
|
||||
struct mmc_host *mmc;
|
||||
struct mmc_request *mrq;
|
||||
struct mmc_command *cmd;
|
||||
struct mmc_data *data;
|
||||
|
||||
void __iomem *base;
|
||||
int dma_channel;
|
||||
struct clk *clk;
|
||||
unsigned int clk_rate;
|
||||
|
||||
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];
|
||||
|
||||
enum mxs_mmc_id devid;
|
||||
unsigned char bus_width;
|
||||
spinlock_t lock;
|
||||
int sdio_irq_en;
|
||||
@ -186,16 +94,18 @@ static int mxs_mmc_get_ro(struct mmc_host *mmc)
|
||||
static int mxs_mmc_get_cd(struct mmc_host *mmc)
|
||||
{
|
||||
struct mxs_mmc_host *host = mmc_priv(mmc);
|
||||
struct mxs_ssp *ssp = &host->ssp;
|
||||
|
||||
return !(readl(host->base + HW_SSP_STATUS(host)) &
|
||||
return !(readl(ssp->base + HW_SSP_STATUS(ssp)) &
|
||||
BM_SSP_STATUS_CARD_DETECT);
|
||||
}
|
||||
|
||||
static void mxs_mmc_reset(struct mxs_mmc_host *host)
|
||||
{
|
||||
struct mxs_ssp *ssp = &host->ssp;
|
||||
u32 ctrl0, ctrl1;
|
||||
|
||||
stmp_reset_block(host->base);
|
||||
stmp_reset_block(ssp->base);
|
||||
|
||||
ctrl0 = BM_SSP_CTRL0_IGNORE_CRC;
|
||||
ctrl1 = BF_SSP(0x3, CTRL1_SSP_MODE) |
|
||||
@ -211,15 +121,15 @@ static void mxs_mmc_reset(struct mxs_mmc_host *host)
|
||||
writel(BF_SSP(0xffff, TIMING_TIMEOUT) |
|
||||
BF_SSP(2, TIMING_CLOCK_DIVIDE) |
|
||||
BF_SSP(0, TIMING_CLOCK_RATE),
|
||||
host->base + HW_SSP_TIMING(host));
|
||||
ssp->base + HW_SSP_TIMING(ssp));
|
||||
|
||||
if (host->sdio_irq_en) {
|
||||
ctrl0 |= BM_SSP_CTRL0_SDIO_IRQ_CHECK;
|
||||
ctrl1 |= BM_SSP_CTRL1_SDIO_IRQ_EN;
|
||||
}
|
||||
|
||||
writel(ctrl0, host->base + HW_SSP_CTRL0);
|
||||
writel(ctrl1, host->base + HW_SSP_CTRL1(host));
|
||||
writel(ctrl0, ssp->base + HW_SSP_CTRL0);
|
||||
writel(ctrl1, ssp->base + HW_SSP_CTRL1(ssp));
|
||||
}
|
||||
|
||||
static void mxs_mmc_start_cmd(struct mxs_mmc_host *host,
|
||||
@ -230,21 +140,22 @@ static void mxs_mmc_request_done(struct mxs_mmc_host *host)
|
||||
struct mmc_command *cmd = host->cmd;
|
||||
struct mmc_data *data = host->data;
|
||||
struct mmc_request *mrq = host->mrq;
|
||||
struct mxs_ssp *ssp = &host->ssp;
|
||||
|
||||
if (mmc_resp_type(cmd) & MMC_RSP_PRESENT) {
|
||||
if (mmc_resp_type(cmd) & MMC_RSP_136) {
|
||||
cmd->resp[3] = readl(host->base + HW_SSP_SDRESP0(host));
|
||||
cmd->resp[2] = readl(host->base + HW_SSP_SDRESP1(host));
|
||||
cmd->resp[1] = readl(host->base + HW_SSP_SDRESP2(host));
|
||||
cmd->resp[0] = readl(host->base + HW_SSP_SDRESP3(host));
|
||||
cmd->resp[3] = readl(ssp->base + HW_SSP_SDRESP0(ssp));
|
||||
cmd->resp[2] = readl(ssp->base + HW_SSP_SDRESP1(ssp));
|
||||
cmd->resp[1] = readl(ssp->base + HW_SSP_SDRESP2(ssp));
|
||||
cmd->resp[0] = readl(ssp->base + HW_SSP_SDRESP3(ssp));
|
||||
} else {
|
||||
cmd->resp[0] = readl(host->base + HW_SSP_SDRESP0(host));
|
||||
cmd->resp[0] = readl(ssp->base + HW_SSP_SDRESP0(ssp));
|
||||
}
|
||||
}
|
||||
|
||||
if (data) {
|
||||
dma_unmap_sg(mmc_dev(host->mmc), data->sg,
|
||||
data->sg_len, host->dma_dir);
|
||||
data->sg_len, ssp->dma_dir);
|
||||
/*
|
||||
* If there was an error on any block, we mark all
|
||||
* data blocks as being in error.
|
||||
@ -277,13 +188,14 @@ static irqreturn_t mxs_mmc_irq_handler(int irq, void *dev_id)
|
||||
struct mxs_mmc_host *host = dev_id;
|
||||
struct mmc_command *cmd = host->cmd;
|
||||
struct mmc_data *data = host->data;
|
||||
struct mxs_ssp *ssp = &host->ssp;
|
||||
u32 stat;
|
||||
|
||||
spin_lock(&host->lock);
|
||||
|
||||
stat = readl(host->base + HW_SSP_CTRL1(host));
|
||||
stat = readl(ssp->base + HW_SSP_CTRL1(ssp));
|
||||
writel(stat & MXS_MMC_IRQ_BITS,
|
||||
host->base + HW_SSP_CTRL1(host) + STMP_OFFSET_REG_CLR);
|
||||
ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_CLR);
|
||||
|
||||
spin_unlock(&host->lock);
|
||||
|
||||
@ -312,6 +224,7 @@ static irqreturn_t mxs_mmc_irq_handler(int irq, void *dev_id)
|
||||
static struct dma_async_tx_descriptor *mxs_mmc_prep_dma(
|
||||
struct mxs_mmc_host *host, unsigned long flags)
|
||||
{
|
||||
struct mxs_ssp *ssp = &host->ssp;
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
struct mmc_data *data = host->data;
|
||||
struct scatterlist * sgl;
|
||||
@ -320,24 +233,24 @@ static struct dma_async_tx_descriptor *mxs_mmc_prep_dma(
|
||||
if (data) {
|
||||
/* data */
|
||||
dma_map_sg(mmc_dev(host->mmc), data->sg,
|
||||
data->sg_len, host->dma_dir);
|
||||
data->sg_len, ssp->dma_dir);
|
||||
sgl = data->sg;
|
||||
sg_len = data->sg_len;
|
||||
} else {
|
||||
/* pio */
|
||||
sgl = (struct scatterlist *) host->ssp_pio_words;
|
||||
sgl = (struct scatterlist *) ssp->ssp_pio_words;
|
||||
sg_len = SSP_PIO_NUM;
|
||||
}
|
||||
|
||||
desc = dmaengine_prep_slave_sg(host->dmach,
|
||||
sgl, sg_len, host->slave_dirn, flags);
|
||||
desc = dmaengine_prep_slave_sg(ssp->dmach,
|
||||
sgl, sg_len, ssp->slave_dirn, flags);
|
||||
if (desc) {
|
||||
desc->callback = mxs_mmc_dma_irq_callback;
|
||||
desc->callback_param = host;
|
||||
} else {
|
||||
if (data)
|
||||
dma_unmap_sg(mmc_dev(host->mmc), data->sg,
|
||||
data->sg_len, host->dma_dir);
|
||||
data->sg_len, ssp->dma_dir);
|
||||
}
|
||||
|
||||
return desc;
|
||||
@ -345,6 +258,7 @@ static struct dma_async_tx_descriptor *mxs_mmc_prep_dma(
|
||||
|
||||
static void mxs_mmc_bc(struct mxs_mmc_host *host)
|
||||
{
|
||||
struct mxs_ssp *ssp = &host->ssp;
|
||||
struct mmc_command *cmd = host->cmd;
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
u32 ctrl0, cmd0, cmd1;
|
||||
@ -358,17 +272,17 @@ static void mxs_mmc_bc(struct mxs_mmc_host *host)
|
||||
cmd0 |= BM_SSP_CMD0_CONT_CLKING_EN | BM_SSP_CMD0_SLOW_CLKING_EN;
|
||||
}
|
||||
|
||||
host->ssp_pio_words[0] = ctrl0;
|
||||
host->ssp_pio_words[1] = cmd0;
|
||||
host->ssp_pio_words[2] = cmd1;
|
||||
host->dma_dir = DMA_NONE;
|
||||
host->slave_dirn = DMA_TRANS_NONE;
|
||||
ssp->ssp_pio_words[0] = ctrl0;
|
||||
ssp->ssp_pio_words[1] = cmd0;
|
||||
ssp->ssp_pio_words[2] = cmd1;
|
||||
ssp->dma_dir = DMA_NONE;
|
||||
ssp->slave_dirn = DMA_TRANS_NONE;
|
||||
desc = mxs_mmc_prep_dma(host, DMA_CTRL_ACK);
|
||||
if (!desc)
|
||||
goto out;
|
||||
|
||||
dmaengine_submit(desc);
|
||||
dma_async_issue_pending(host->dmach);
|
||||
dma_async_issue_pending(ssp->dmach);
|
||||
return;
|
||||
|
||||
out:
|
||||
@ -378,6 +292,7 @@ out:
|
||||
|
||||
static void mxs_mmc_ac(struct mxs_mmc_host *host)
|
||||
{
|
||||
struct mxs_ssp *ssp = &host->ssp;
|
||||
struct mmc_command *cmd = host->cmd;
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
u32 ignore_crc, get_resp, long_resp;
|
||||
@ -399,17 +314,17 @@ static void mxs_mmc_ac(struct mxs_mmc_host *host)
|
||||
cmd0 |= BM_SSP_CMD0_CONT_CLKING_EN | BM_SSP_CMD0_SLOW_CLKING_EN;
|
||||
}
|
||||
|
||||
host->ssp_pio_words[0] = ctrl0;
|
||||
host->ssp_pio_words[1] = cmd0;
|
||||
host->ssp_pio_words[2] = cmd1;
|
||||
host->dma_dir = DMA_NONE;
|
||||
host->slave_dirn = DMA_TRANS_NONE;
|
||||
ssp->ssp_pio_words[0] = ctrl0;
|
||||
ssp->ssp_pio_words[1] = cmd0;
|
||||
ssp->ssp_pio_words[2] = cmd1;
|
||||
ssp->dma_dir = DMA_NONE;
|
||||
ssp->slave_dirn = DMA_TRANS_NONE;
|
||||
desc = mxs_mmc_prep_dma(host, DMA_CTRL_ACK);
|
||||
if (!desc)
|
||||
goto out;
|
||||
|
||||
dmaengine_submit(desc);
|
||||
dma_async_issue_pending(host->dmach);
|
||||
dma_async_issue_pending(ssp->dmach);
|
||||
return;
|
||||
|
||||
out:
|
||||
@ -447,6 +362,8 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
|
||||
unsigned int data_size = 0, log2_blksz;
|
||||
unsigned int blocks = data->blocks;
|
||||
|
||||
struct mxs_ssp *ssp = &host->ssp;
|
||||
|
||||
u32 ignore_crc, get_resp, long_resp, read;
|
||||
u32 ctrl0, cmd0, cmd1, val;
|
||||
|
||||
@ -489,15 +406,15 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
|
||||
blocks = 1;
|
||||
|
||||
/* xfer count, block size and count need to be set differently */
|
||||
if (ssp_is_old(host)) {
|
||||
if (ssp_is_old(ssp)) {
|
||||
ctrl0 |= BF_SSP(data_size, CTRL0_XFER_COUNT);
|
||||
cmd0 |= BF_SSP(log2_blksz, CMD0_BLOCK_SIZE) |
|
||||
BF_SSP(blocks - 1, CMD0_BLOCK_COUNT);
|
||||
} else {
|
||||
writel(data_size, host->base + HW_SSP_XFER_SIZE);
|
||||
writel(data_size, ssp->base + HW_SSP_XFER_SIZE);
|
||||
writel(BF_SSP(log2_blksz, BLOCK_SIZE_BLOCK_SIZE) |
|
||||
BF_SSP(blocks - 1, BLOCK_SIZE_BLOCK_COUNT),
|
||||
host->base + HW_SSP_BLOCK_SIZE);
|
||||
ssp->base + HW_SSP_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
if ((cmd->opcode == MMC_STOP_TRANSMISSION) ||
|
||||
@ -512,18 +429,18 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
|
||||
}
|
||||
|
||||
/* set the timeout count */
|
||||
timeout = mxs_ns_to_ssp_ticks(host->clk_rate, data->timeout_ns);
|
||||
val = readl(host->base + HW_SSP_TIMING(host));
|
||||
timeout = mxs_ns_to_ssp_ticks(ssp->clk_rate, data->timeout_ns);
|
||||
val = readl(ssp->base + HW_SSP_TIMING(ssp));
|
||||
val &= ~(BM_SSP_TIMING_TIMEOUT);
|
||||
val |= BF_SSP(timeout, TIMING_TIMEOUT);
|
||||
writel(val, host->base + HW_SSP_TIMING(host));
|
||||
writel(val, ssp->base + HW_SSP_TIMING(ssp));
|
||||
|
||||
/* pio */
|
||||
host->ssp_pio_words[0] = ctrl0;
|
||||
host->ssp_pio_words[1] = cmd0;
|
||||
host->ssp_pio_words[2] = cmd1;
|
||||
host->dma_dir = DMA_NONE;
|
||||
host->slave_dirn = DMA_TRANS_NONE;
|
||||
ssp->ssp_pio_words[0] = ctrl0;
|
||||
ssp->ssp_pio_words[1] = cmd0;
|
||||
ssp->ssp_pio_words[2] = cmd1;
|
||||
ssp->dma_dir = DMA_NONE;
|
||||
ssp->slave_dirn = DMA_TRANS_NONE;
|
||||
desc = mxs_mmc_prep_dma(host, 0);
|
||||
if (!desc)
|
||||
goto out;
|
||||
@ -531,14 +448,14 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
|
||||
/* append data sg */
|
||||
WARN_ON(host->data != NULL);
|
||||
host->data = data;
|
||||
host->dma_dir = dma_data_dir;
|
||||
host->slave_dirn = slave_dirn;
|
||||
ssp->dma_dir = dma_data_dir;
|
||||
ssp->slave_dirn = slave_dirn;
|
||||
desc = mxs_mmc_prep_dma(host, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (!desc)
|
||||
goto out;
|
||||
|
||||
dmaengine_submit(desc);
|
||||
dma_async_issue_pending(host->dmach);
|
||||
dma_async_issue_pending(ssp->dmach);
|
||||
return;
|
||||
out:
|
||||
dev_warn(mmc_dev(host->mmc),
|
||||
@ -579,42 +496,6 @@ static void mxs_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
mxs_mmc_start_cmd(host, mrq->cmd);
|
||||
}
|
||||
|
||||
static void mxs_mmc_set_clk_rate(struct mxs_mmc_host *host, unsigned int rate)
|
||||
{
|
||||
unsigned int ssp_clk, ssp_sck;
|
||||
u32 clock_divide, clock_rate;
|
||||
u32 val;
|
||||
|
||||
ssp_clk = clk_get_rate(host->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(mmc_dev(host->mmc),
|
||||
"%s: cannot set clock to %d\n", __func__, rate);
|
||||
return;
|
||||
}
|
||||
|
||||
ssp_sck = ssp_clk / clock_divide / (1 + clock_rate);
|
||||
|
||||
val = readl(host->base + HW_SSP_TIMING(host));
|
||||
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, host->base + HW_SSP_TIMING(host));
|
||||
|
||||
host->clk_rate = ssp_sck;
|
||||
|
||||
dev_dbg(mmc_dev(host->mmc),
|
||||
"%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);
|
||||
}
|
||||
|
||||
static void mxs_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
{
|
||||
struct mxs_mmc_host *host = mmc_priv(mmc);
|
||||
@ -627,12 +508,13 @@ static void mxs_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
host->bus_width = 0;
|
||||
|
||||
if (ios->clock)
|
||||
mxs_mmc_set_clk_rate(host, ios->clock);
|
||||
mxs_ssp_set_clk_rate(&host->ssp, ios->clock);
|
||||
}
|
||||
|
||||
static void mxs_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
||||
{
|
||||
struct mxs_mmc_host *host = mmc_priv(mmc);
|
||||
struct mxs_ssp *ssp = &host->ssp;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
@ -641,19 +523,19 @@ static void mxs_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
||||
|
||||
if (enable) {
|
||||
writel(BM_SSP_CTRL0_SDIO_IRQ_CHECK,
|
||||
host->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
writel(BM_SSP_CTRL1_SDIO_IRQ_EN,
|
||||
host->base + HW_SSP_CTRL1(host) + STMP_OFFSET_REG_SET);
|
||||
} else {
|
||||
writel(BM_SSP_CTRL0_SDIO_IRQ_CHECK,
|
||||
host->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
writel(BM_SSP_CTRL1_SDIO_IRQ_EN,
|
||||
host->base + HW_SSP_CTRL1(host) + STMP_OFFSET_REG_CLR);
|
||||
ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_CLR);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
if (enable && readl(host->base + HW_SSP_STATUS(host)) &
|
||||
if (enable && readl(ssp->base + HW_SSP_STATUS(ssp)) &
|
||||
BM_SSP_STATUS_SDIO_IRQ)
|
||||
mmc_signal_sdio_irq(host->mmc);
|
||||
|
||||
@ -670,34 +552,35 @@ static const struct mmc_host_ops mxs_mmc_ops = {
|
||||
static bool mxs_mmc_dma_filter(struct dma_chan *chan, void *param)
|
||||
{
|
||||
struct mxs_mmc_host *host = param;
|
||||
struct mxs_ssp *ssp = &host->ssp;
|
||||
|
||||
if (!mxs_dma_is_apbh(chan))
|
||||
return false;
|
||||
|
||||
if (chan->chan_id != host->dma_channel)
|
||||
if (chan->chan_id != ssp->dma_channel)
|
||||
return false;
|
||||
|
||||
chan->private = &host->dma_data;
|
||||
chan->private = &ssp->dma_data;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct platform_device_id mxs_mmc_ids[] = {
|
||||
static struct platform_device_id mxs_ssp_ids[] = {
|
||||
{
|
||||
.name = "imx23-mmc",
|
||||
.driver_data = IMX23_MMC,
|
||||
.driver_data = IMX23_SSP,
|
||||
}, {
|
||||
.name = "imx28-mmc",
|
||||
.driver_data = IMX28_MMC,
|
||||
.driver_data = IMX28_SSP,
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, mxs_mmc_ids);
|
||||
MODULE_DEVICE_TABLE(platform, mxs_ssp_ids);
|
||||
|
||||
static const struct of_device_id mxs_mmc_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx23-mmc", .data = (void *) IMX23_MMC, },
|
||||
{ .compatible = "fsl,imx28-mmc", .data = (void *) IMX28_MMC, },
|
||||
{ .compatible = "fsl,imx23-mmc", .data = (void *) IMX23_SSP, },
|
||||
{ .compatible = "fsl,imx28-mmc", .data = (void *) IMX28_SSP, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mxs_mmc_dt_ids);
|
||||
@ -716,6 +599,7 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
||||
dma_cap_mask_t mask;
|
||||
struct regulator *reg_vmmc;
|
||||
enum of_gpio_flags flags;
|
||||
struct mxs_ssp *ssp;
|
||||
|
||||
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
@ -729,28 +613,30 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
host = mmc_priv(mmc);
|
||||
host->base = devm_request_and_ioremap(&pdev->dev, iores);
|
||||
if (!host->base) {
|
||||
ssp = &host->ssp;
|
||||
ssp->dev = &pdev->dev;
|
||||
ssp->base = devm_request_and_ioremap(&pdev->dev, iores);
|
||||
if (!ssp->base) {
|
||||
ret = -EADDRNOTAVAIL;
|
||||
goto out_mmc_free;
|
||||
}
|
||||
|
||||
if (np) {
|
||||
host->devid = (enum mxs_mmc_id) of_id->data;
|
||||
ssp->devid = (enum mxs_ssp_id) of_id->data;
|
||||
/*
|
||||
* TODO: This is a temporary solution and should be changed
|
||||
* to use generic DMA binding later when the helpers get in.
|
||||
*/
|
||||
ret = of_property_read_u32(np, "fsl,ssp-dma-channel",
|
||||
&host->dma_channel);
|
||||
&ssp->dma_channel);
|
||||
if (ret) {
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"failed to get dma channel\n");
|
||||
goto out_mmc_free;
|
||||
}
|
||||
} else {
|
||||
host->devid = pdev->id_entry->driver_data;
|
||||
host->dma_channel = dmares->start;
|
||||
ssp->devid = pdev->id_entry->driver_data;
|
||||
ssp->dma_channel = dmares->start;
|
||||
}
|
||||
|
||||
host->mmc = mmc;
|
||||
@ -772,20 +658,20 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
||||
goto out_mmc_free;
|
||||
}
|
||||
|
||||
host->clk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(host->clk)) {
|
||||
ret = PTR_ERR(host->clk);
|
||||
ssp->clk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(ssp->clk)) {
|
||||
ret = PTR_ERR(ssp->clk);
|
||||
goto out_mmc_free;
|
||||
}
|
||||
clk_prepare_enable(host->clk);
|
||||
clk_prepare_enable(ssp->clk);
|
||||
|
||||
mxs_mmc_reset(host);
|
||||
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
host->dma_data.chan_irq = irq_dma;
|
||||
host->dmach = dma_request_channel(mask, mxs_mmc_dma_filter, host);
|
||||
if (!host->dmach) {
|
||||
ssp->dma_data.chan_irq = irq_dma;
|
||||
ssp->dmach = dma_request_channel(mask, mxs_mmc_dma_filter, host);
|
||||
if (!ssp->dmach) {
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"%s: failed to request dma\n", __func__);
|
||||
goto out_clk_put;
|
||||
@ -822,9 +708,9 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
||||
|
||||
mmc->max_segs = 52;
|
||||
mmc->max_blk_size = 1 << 0xf;
|
||||
mmc->max_blk_count = (ssp_is_old(host)) ? 0xff : 0xffffff;
|
||||
mmc->max_req_size = (ssp_is_old(host)) ? 0xffff : 0xffffffff;
|
||||
mmc->max_seg_size = dma_get_max_seg_size(host->dmach->device->dev);
|
||||
mmc->max_blk_count = (ssp_is_old(ssp)) ? 0xff : 0xffffff;
|
||||
mmc->max_req_size = (ssp_is_old(ssp)) ? 0xffff : 0xffffffff;
|
||||
mmc->max_seg_size = dma_get_max_seg_size(ssp->dmach->device->dev);
|
||||
|
||||
platform_set_drvdata(pdev, mmc);
|
||||
|
||||
@ -844,11 +730,11 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
|
||||
out_free_dma:
|
||||
if (host->dmach)
|
||||
dma_release_channel(host->dmach);
|
||||
if (ssp->dmach)
|
||||
dma_release_channel(ssp->dmach);
|
||||
out_clk_put:
|
||||
clk_disable_unprepare(host->clk);
|
||||
clk_put(host->clk);
|
||||
clk_disable_unprepare(ssp->clk);
|
||||
clk_put(ssp->clk);
|
||||
out_mmc_free:
|
||||
mmc_free_host(mmc);
|
||||
return ret;
|
||||
@ -858,16 +744,17 @@ static int mxs_mmc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mmc_host *mmc = platform_get_drvdata(pdev);
|
||||
struct mxs_mmc_host *host = mmc_priv(mmc);
|
||||
struct mxs_ssp *ssp = &host->ssp;
|
||||
|
||||
mmc_remove_host(mmc);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
if (host->dmach)
|
||||
dma_release_channel(host->dmach);
|
||||
if (ssp->dmach)
|
||||
dma_release_channel(ssp->dmach);
|
||||
|
||||
clk_disable_unprepare(host->clk);
|
||||
clk_put(host->clk);
|
||||
clk_disable_unprepare(ssp->clk);
|
||||
clk_put(ssp->clk);
|
||||
|
||||
mmc_free_host(mmc);
|
||||
|
||||
@ -879,11 +766,12 @@ static int mxs_mmc_suspend(struct device *dev)
|
||||
{
|
||||
struct mmc_host *mmc = dev_get_drvdata(dev);
|
||||
struct mxs_mmc_host *host = mmc_priv(mmc);
|
||||
struct mxs_ssp *ssp = &host->ssp;
|
||||
int ret = 0;
|
||||
|
||||
ret = mmc_suspend_host(mmc);
|
||||
|
||||
clk_disable_unprepare(host->clk);
|
||||
clk_disable_unprepare(ssp->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -892,9 +780,10 @@ static int mxs_mmc_resume(struct device *dev)
|
||||
{
|
||||
struct mmc_host *mmc = dev_get_drvdata(dev);
|
||||
struct mxs_mmc_host *host = mmc_priv(mmc);
|
||||
struct mxs_ssp *ssp = &host->ssp;
|
||||
int ret = 0;
|
||||
|
||||
clk_prepare_enable(host->clk);
|
||||
clk_prepare_enable(ssp->clk);
|
||||
|
||||
ret = mmc_resume_host(mmc);
|
||||
|
||||
@ -910,7 +799,7 @@ static const struct dev_pm_ops mxs_mmc_pm_ops = {
|
||||
static struct platform_driver mxs_mmc_driver = {
|
||||
.probe = mxs_mmc_probe,
|
||||
.remove = mxs_mmc_remove,
|
||||
.id_table = mxs_mmc_ids,
|
||||
.id_table = mxs_ssp_ids,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
|
@ -325,6 +325,12 @@ config SPI_S3C64XX
|
||||
help
|
||||
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
|
||||
tristate "SuperH MSIOF SPI controller"
|
||||
depends on SUPERH && HAVE_CLK
|
||||
@ -364,11 +370,12 @@ config SPI_STMP3XXX
|
||||
help
|
||||
SPI driver for Freescale STMP37xx/378x SoC SSP interface
|
||||
|
||||
config SPI_TEGRA
|
||||
tristate "Nvidia Tegra SPI controller"
|
||||
depends on ARCH_TEGRA && TEGRA20_APB_DMA
|
||||
config SPI_MXS
|
||||
tristate "Freescale MXS SPI controller"
|
||||
depends on ARCH_MXS
|
||||
select STMP_DEVICE
|
||||
help
|
||||
SPI driver for NVidia Tegra SoCs
|
||||
SPI driver for Freescale MXS devices.
|
||||
|
||||
config SPI_TI_SSP
|
||||
tristate "TI Sequencer Serial Port - SPI Support"
|
||||
|
@ -36,6 +36,7 @@ obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o
|
||||
obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mpc512x-psc.o
|
||||
obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.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_OC_TINY) += spi-oc-tiny.o
|
||||
obj-$(CONFIG_SPI_OMAP_UWIRE) += spi-omap-uwire.o
|
||||
@ -51,13 +52,13 @@ obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o
|
||||
spi-s3c24xx-hw-y := spi-s3c24xx.o
|
||||
spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.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_HSPI) += spi-sh-hspi.o
|
||||
obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.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_TEGRA) += spi-tegra.o
|
||||
obj-$(CONFIG_SPI_TI_SSP) += spi-ti-ssp.o
|
||||
obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o
|
||||
obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o
|
||||
|
@ -307,8 +307,6 @@ static const struct of_device_id altera_spi_match[] = {
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, altera_spi_match);
|
||||
#else /* CONFIG_OF */
|
||||
#define altera_spi_match NULL
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
static struct platform_driver altera_spi_driver = {
|
||||
@ -318,7 +316,7 @@ static struct platform_driver altera_spi_driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = NULL,
|
||||
.of_match_table = altera_spi_match,
|
||||
.of_match_table = of_match_ptr(altera_spi_match),
|
||||
},
|
||||
};
|
||||
module_platform_driver(altera_spi_driver);
|
||||
|
@ -22,6 +22,8 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi_bitbang.h>
|
||||
@ -46,6 +48,7 @@ struct spi_gpio {
|
||||
struct spi_bitbang bitbang;
|
||||
struct spi_gpio_platform_data pdata;
|
||||
struct platform_device *pdev;
|
||||
int cs_gpios[0];
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
@ -89,15 +92,21 @@ struct spi_gpio {
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
static inline const struct spi_gpio_platform_data * __pure
|
||||
spi_to_pdata(const struct spi_device *spi)
|
||||
static inline struct spi_gpio * __pure
|
||||
spi_to_spi_gpio(const struct spi_device *spi)
|
||||
{
|
||||
const struct spi_bitbang *bang;
|
||||
const struct spi_gpio *spi_gpio;
|
||||
struct spi_gpio *spi_gpio;
|
||||
|
||||
bang = spi_master_get_devdata(spi->master);
|
||||
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 */
|
||||
@ -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)
|
||||
{
|
||||
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 */
|
||||
if (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)
|
||||
{
|
||||
unsigned long cs = (unsigned long) spi->controller_data;
|
||||
int status = 0;
|
||||
unsigned int cs;
|
||||
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)
|
||||
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 (cs != SPI_GPIO_NO_CHIPSELECT) {
|
||||
status = gpio_request(cs, dev_name(&spi->dev));
|
||||
@ -239,8 +264,12 @@ static int spi_gpio_setup(struct spi_device *spi)
|
||||
!(spi->mode & SPI_CS_HIGH));
|
||||
}
|
||||
}
|
||||
if (!status)
|
||||
if (!status) {
|
||||
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 (!spi->controller_state && cs != SPI_GPIO_NO_CHIPSELECT)
|
||||
gpio_free(cs);
|
||||
@ -250,7 +279,8 @@ static int spi_gpio_setup(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)
|
||||
gpio_free(cs);
|
||||
@ -313,6 +343,55 @@ done:
|
||||
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)
|
||||
{
|
||||
int status;
|
||||
@ -320,6 +399,13 @@ static int __devinit spi_gpio_probe(struct platform_device *pdev)
|
||||
struct spi_gpio *spi_gpio;
|
||||
struct spi_gpio_platform_data *pdata;
|
||||
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;
|
||||
#ifdef GENERIC_BITBANG
|
||||
@ -331,7 +417,8 @@ static int __devinit spi_gpio_probe(struct platform_device *pdev)
|
||||
if (status < 0)
|
||||
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) {
|
||||
status = -ENOMEM;
|
||||
goto gpio_free;
|
||||
@ -348,6 +435,23 @@ static int __devinit spi_gpio_probe(struct platform_device *pdev)
|
||||
master->num_chipselect = SPI_N_CHIPSEL;
|
||||
master->setup = spi_gpio_setup;
|
||||
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.chipselect = spi_gpio_chipselect;
|
||||
@ -408,8 +512,11 @@ static int __devexit spi_gpio_remove(struct platform_device *pdev)
|
||||
MODULE_ALIAS("platform:" DRIVER_NAME);
|
||||
|
||||
static struct platform_driver spi_gpio_driver = {
|
||||
.driver.name = DRIVER_NAME,
|
||||
.driver.owner = THIS_MODULE,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(spi_gpio_dt_ids),
|
||||
},
|
||||
.probe = spi_gpio_probe,
|
||||
.remove = __devexit_p(spi_gpio_remove),
|
||||
};
|
||||
|
@ -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_SBBCTRL(cs) (1 << ((cs) + 8))
|
||||
#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_TEEN (1 << 0)
|
||||
@ -287,9 +288,10 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
|
||||
if (config->mode & SPI_CPHA)
|
||||
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_SCLKCTL(config->cs);
|
||||
}
|
||||
if (config->mode & SPI_CS_HIGH)
|
||||
cfg |= MX51_ECSPI_CONFIG_SSBPOL(config->cs);
|
||||
|
||||
|
@ -494,7 +494,7 @@ free_master:
|
||||
|
||||
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);
|
||||
|
||||
flush_workqueue(mps->workqueue);
|
||||
@ -503,6 +503,7 @@ static int __devexit mpc512x_psc_spi_do_remove(struct device *dev)
|
||||
free_irq(mps->irq, mps);
|
||||
if (mps->psc)
|
||||
iounmap(mps->psc);
|
||||
spi_master_put(master);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
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);
|
||||
|
||||
flush_workqueue(mps->workqueue);
|
||||
@ -490,6 +490,7 @@ static int __devexit mpc52xx_psc_spi_of_remove(struct platform_device *op)
|
||||
free_irq(mps->irq, mps);
|
||||
if (mps->psc)
|
||||
iounmap(mps->psc);
|
||||
spi_master_put(master);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -454,7 +454,7 @@ static int __devinit mpc52xx_spi_probe(struct platform_device *op)
|
||||
GFP_KERNEL);
|
||||
if (!ms->gpio_cs) {
|
||||
rc = -ENOMEM;
|
||||
goto err_alloc;
|
||||
goto err_alloc_gpio;
|
||||
}
|
||||
|
||||
for (i = 0; i < ms->gpio_cs_count; i++) {
|
||||
@ -514,12 +514,13 @@ static int __devinit mpc52xx_spi_probe(struct platform_device *op)
|
||||
|
||||
err_register:
|
||||
dev_err(&ms->master->dev, "initialization failed\n");
|
||||
spi_master_put(master);
|
||||
err_gpio:
|
||||
while (i-- > 0)
|
||||
gpio_free(ms->gpio_cs[i]);
|
||||
|
||||
kfree(ms->gpio_cs);
|
||||
err_alloc_gpio:
|
||||
spi_master_put(master);
|
||||
err_alloc:
|
||||
err_init:
|
||||
iounmap(regs);
|
||||
@ -528,7 +529,7 @@ static int __devinit mpc52xx_spi_probe(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);
|
||||
int i;
|
||||
|
||||
@ -540,8 +541,8 @@ static int __devexit mpc52xx_spi_remove(struct platform_device *op)
|
||||
|
||||
kfree(ms->gpio_cs);
|
||||
spi_unregister_master(master);
|
||||
spi_master_put(master);
|
||||
iounmap(ms->regs);
|
||||
spi_master_put(master);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
674
drivers/spi/spi-mxs.c
Normal file
674
drivers/spi/spi-mxs.c
Normal file
@ -0,0 +1,674 @@
|
||||
/*
|
||||
* Freescale MXS SPI master driver
|
||||
*
|
||||
* Copyright 2012 DENX Software Engineering, GmbH.
|
||||
* Copyright 2012 Freescale Semiconductor, Inc.
|
||||
* Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
|
||||
*
|
||||
* Rework and transition to new API by:
|
||||
* Marek Vasut <marex@denx.de>
|
||||
*
|
||||
* Based on previous attempt by:
|
||||
* Fabio Estevam <fabio.estevam@freescale.com>
|
||||
*
|
||||
* Based on code from U-Boot bootloader by:
|
||||
* Marek Vasut <marex@denx.de>
|
||||
*
|
||||
* Based on spi-stmp.c, which is:
|
||||
* Author: Dmitry Pervushin <dimka@embeddedalley.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/stmp_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/mxs-spi.h>
|
||||
|
||||
#define DRIVER_NAME "mxs-spi"
|
||||
|
||||
/* Use 10S timeout for very long transfers, it should suffice. */
|
||||
#define SSP_TIMEOUT 10000
|
||||
|
||||
#define SG_MAXLEN 0xff00
|
||||
|
||||
struct mxs_spi {
|
||||
struct mxs_ssp ssp;
|
||||
struct completion c;
|
||||
};
|
||||
|
||||
static int mxs_spi_setup_transfer(struct spi_device *dev,
|
||||
struct spi_transfer *t)
|
||||
{
|
||||
struct mxs_spi *spi = spi_master_get_devdata(dev->master);
|
||||
struct mxs_ssp *ssp = &spi->ssp;
|
||||
uint8_t bits_per_word;
|
||||
uint32_t hz = 0;
|
||||
|
||||
bits_per_word = dev->bits_per_word;
|
||||
if (t && t->bits_per_word)
|
||||
bits_per_word = t->bits_per_word;
|
||||
|
||||
if (bits_per_word != 8) {
|
||||
dev_err(&dev->dev, "%s, unsupported bits_per_word=%d\n",
|
||||
__func__, bits_per_word);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hz = dev->max_speed_hz;
|
||||
if (t && t->speed_hz)
|
||||
hz = min(hz, t->speed_hz);
|
||||
if (hz == 0) {
|
||||
dev_err(&dev->dev, "Cannot continue with zero clock\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mxs_ssp_set_clk_rate(ssp, hz);
|
||||
|
||||
writel(BF_SSP_CTRL1_SSP_MODE(BV_SSP_CTRL1_SSP_MODE__SPI) |
|
||||
BF_SSP_CTRL1_WORD_LENGTH
|
||||
(BV_SSP_CTRL1_WORD_LENGTH__EIGHT_BITS) |
|
||||
((dev->mode & SPI_CPOL) ? BM_SSP_CTRL1_POLARITY : 0) |
|
||||
((dev->mode & SPI_CPHA) ? BM_SSP_CTRL1_PHASE : 0),
|
||||
ssp->base + HW_SSP_CTRL1(ssp));
|
||||
|
||||
writel(0x0, ssp->base + HW_SSP_CMD0);
|
||||
writel(0x0, ssp->base + HW_SSP_CMD1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxs_spi_setup(struct spi_device *dev)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (!dev->bits_per_word)
|
||||
dev->bits_per_word = 8;
|
||||
|
||||
if (dev->mode & ~(SPI_CPOL | SPI_CPHA))
|
||||
return -EINVAL;
|
||||
|
||||
err = mxs_spi_setup_transfer(dev, NULL);
|
||||
if (err) {
|
||||
dev_err(&dev->dev,
|
||||
"Failed to setup transfer, error = %d\n", err);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static uint32_t mxs_spi_cs_to_reg(unsigned cs)
|
||||
{
|
||||
uint32_t select = 0;
|
||||
|
||||
/*
|
||||
* i.MX28 Datasheet: 17.10.1: HW_SSP_CTRL0
|
||||
*
|
||||
* The bits BM_SSP_CTRL0_WAIT_FOR_CMD and BM_SSP_CTRL0_WAIT_FOR_IRQ
|
||||
* in HW_SSP_CTRL0 register do have multiple usage, please refer to
|
||||
* the datasheet for further details. In SPI mode, they are used to
|
||||
* toggle the chip-select lines (nCS pins).
|
||||
*/
|
||||
if (cs & 1)
|
||||
select |= BM_SSP_CTRL0_WAIT_FOR_CMD;
|
||||
if (cs & 2)
|
||||
select |= BM_SSP_CTRL0_WAIT_FOR_IRQ;
|
||||
|
||||
return select;
|
||||
}
|
||||
|
||||
static void mxs_spi_set_cs(struct mxs_spi *spi, unsigned cs)
|
||||
{
|
||||
const uint32_t mask =
|
||||
BM_SSP_CTRL0_WAIT_FOR_CMD | BM_SSP_CTRL0_WAIT_FOR_IRQ;
|
||||
uint32_t select;
|
||||
struct mxs_ssp *ssp = &spi->ssp;
|
||||
|
||||
writel(mask, ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
select = mxs_spi_cs_to_reg(cs);
|
||||
writel(select, ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
}
|
||||
|
||||
static inline void mxs_spi_enable(struct mxs_spi *spi)
|
||||
{
|
||||
struct mxs_ssp *ssp = &spi->ssp;
|
||||
|
||||
writel(BM_SSP_CTRL0_LOCK_CS,
|
||||
ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
writel(BM_SSP_CTRL0_IGNORE_CRC,
|
||||
ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
}
|
||||
|
||||
static inline void mxs_spi_disable(struct mxs_spi *spi)
|
||||
{
|
||||
struct mxs_ssp *ssp = &spi->ssp;
|
||||
|
||||
writel(BM_SSP_CTRL0_LOCK_CS,
|
||||
ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
writel(BM_SSP_CTRL0_IGNORE_CRC,
|
||||
ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
}
|
||||
|
||||
static int mxs_ssp_wait(struct mxs_spi *spi, int offset, int mask, bool set)
|
||||
{
|
||||
const unsigned long timeout = jiffies + msecs_to_jiffies(SSP_TIMEOUT);
|
||||
struct mxs_ssp *ssp = &spi->ssp;
|
||||
uint32_t reg;
|
||||
|
||||
do {
|
||||
reg = readl_relaxed(ssp->base + offset);
|
||||
|
||||
if (!set)
|
||||
reg = ~reg;
|
||||
|
||||
reg &= mask;
|
||||
|
||||
if (reg == mask)
|
||||
return 0;
|
||||
} while (time_before(jiffies, timeout));
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static void mxs_ssp_dma_irq_callback(void *param)
|
||||
{
|
||||
struct mxs_spi *spi = param;
|
||||
complete(&spi->c);
|
||||
}
|
||||
|
||||
static irqreturn_t mxs_ssp_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct mxs_ssp *ssp = dev_id;
|
||||
dev_err(ssp->dev, "%s[%i] CTRL1=%08x STATUS=%08x\n",
|
||||
__func__, __LINE__,
|
||||
readl(ssp->base + HW_SSP_CTRL1(ssp)),
|
||||
readl(ssp->base + HW_SSP_STATUS(ssp)));
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int mxs_spi_txrx_dma(struct mxs_spi *spi, int cs,
|
||||
unsigned char *buf, int len,
|
||||
int *first, int *last, int write)
|
||||
{
|
||||
struct mxs_ssp *ssp = &spi->ssp;
|
||||
struct dma_async_tx_descriptor *desc = NULL;
|
||||
const bool vmalloced_buf = is_vmalloc_addr(buf);
|
||||
const int desc_len = vmalloced_buf ? PAGE_SIZE : SG_MAXLEN;
|
||||
const int sgs = DIV_ROUND_UP(len, desc_len);
|
||||
int sg_count;
|
||||
int min, ret;
|
||||
uint32_t ctrl0;
|
||||
struct page *vm_page;
|
||||
void *sg_buf;
|
||||
struct {
|
||||
uint32_t pio[4];
|
||||
struct scatterlist sg;
|
||||
} *dma_xfer;
|
||||
|
||||
if (!len)
|
||||
return -EINVAL;
|
||||
|
||||
dma_xfer = kzalloc(sizeof(*dma_xfer) * sgs, GFP_KERNEL);
|
||||
if (!dma_xfer)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_COMPLETION(spi->c);
|
||||
|
||||
ctrl0 = readl(ssp->base + HW_SSP_CTRL0);
|
||||
ctrl0 |= BM_SSP_CTRL0_DATA_XFER | mxs_spi_cs_to_reg(cs);
|
||||
|
||||
if (*first)
|
||||
ctrl0 |= BM_SSP_CTRL0_LOCK_CS;
|
||||
if (!write)
|
||||
ctrl0 |= BM_SSP_CTRL0_READ;
|
||||
|
||||
/* Queue the DMA data transfer. */
|
||||
for (sg_count = 0; sg_count < sgs; sg_count++) {
|
||||
min = min(len, desc_len);
|
||||
|
||||
/* Prepare the transfer descriptor. */
|
||||
if ((sg_count + 1 == sgs) && *last)
|
||||
ctrl0 |= BM_SSP_CTRL0_IGNORE_CRC;
|
||||
|
||||
if (ssp->devid == IMX23_SSP)
|
||||
ctrl0 |= min;
|
||||
|
||||
dma_xfer[sg_count].pio[0] = ctrl0;
|
||||
dma_xfer[sg_count].pio[3] = min;
|
||||
|
||||
if (vmalloced_buf) {
|
||||
vm_page = vmalloc_to_page(buf);
|
||||
if (!vm_page) {
|
||||
ret = -ENOMEM;
|
||||
goto err_vmalloc;
|
||||
}
|
||||
sg_buf = page_address(vm_page) +
|
||||
((size_t)buf & ~PAGE_MASK);
|
||||
} else {
|
||||
sg_buf = buf;
|
||||
}
|
||||
|
||||
sg_init_one(&dma_xfer[sg_count].sg, sg_buf, min);
|
||||
ret = dma_map_sg(ssp->dev, &dma_xfer[sg_count].sg, 1,
|
||||
write ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
|
||||
len -= min;
|
||||
buf += min;
|
||||
|
||||
/* Queue the PIO register write transfer. */
|
||||
desc = dmaengine_prep_slave_sg(ssp->dmach,
|
||||
(struct scatterlist *)dma_xfer[sg_count].pio,
|
||||
(ssp->devid == IMX23_SSP) ? 1 : 4,
|
||||
DMA_TRANS_NONE,
|
||||
sg_count ? DMA_PREP_INTERRUPT : 0);
|
||||
if (!desc) {
|
||||
dev_err(ssp->dev,
|
||||
"Failed to get PIO reg. write descriptor.\n");
|
||||
ret = -EINVAL;
|
||||
goto err_mapped;
|
||||
}
|
||||
|
||||
desc = dmaengine_prep_slave_sg(ssp->dmach,
|
||||
&dma_xfer[sg_count].sg, 1,
|
||||
write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
|
||||
if (!desc) {
|
||||
dev_err(ssp->dev,
|
||||
"Failed to get DMA data write descriptor.\n");
|
||||
ret = -EINVAL;
|
||||
goto err_mapped;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The last descriptor must have this callback,
|
||||
* to finish the DMA transaction.
|
||||
*/
|
||||
desc->callback = mxs_ssp_dma_irq_callback;
|
||||
desc->callback_param = spi;
|
||||
|
||||
/* Start the transfer. */
|
||||
dmaengine_submit(desc);
|
||||
dma_async_issue_pending(ssp->dmach);
|
||||
|
||||
ret = wait_for_completion_timeout(&spi->c,
|
||||
msecs_to_jiffies(SSP_TIMEOUT));
|
||||
if (!ret) {
|
||||
dev_err(ssp->dev, "DMA transfer timeout\n");
|
||||
ret = -ETIMEDOUT;
|
||||
goto err_vmalloc;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
err_vmalloc:
|
||||
while (--sg_count >= 0) {
|
||||
err_mapped:
|
||||
dma_unmap_sg(ssp->dev, &dma_xfer[sg_count].sg, 1,
|
||||
write ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
}
|
||||
|
||||
kfree(dma_xfer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxs_spi_txrx_pio(struct mxs_spi *spi, int cs,
|
||||
unsigned char *buf, int len,
|
||||
int *first, int *last, int write)
|
||||
{
|
||||
struct mxs_ssp *ssp = &spi->ssp;
|
||||
|
||||
if (*first)
|
||||
mxs_spi_enable(spi);
|
||||
|
||||
mxs_spi_set_cs(spi, cs);
|
||||
|
||||
while (len--) {
|
||||
if (*last && len == 0)
|
||||
mxs_spi_disable(spi);
|
||||
|
||||
if (ssp->devid == IMX23_SSP) {
|
||||
writel(BM_SSP_CTRL0_XFER_COUNT,
|
||||
ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
writel(1,
|
||||
ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
} else {
|
||||
writel(1, ssp->base + HW_SSP_XFER_SIZE);
|
||||
}
|
||||
|
||||
if (write)
|
||||
writel(BM_SSP_CTRL0_READ,
|
||||
ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
else
|
||||
writel(BM_SSP_CTRL0_READ,
|
||||
ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
|
||||
writel(BM_SSP_CTRL0_RUN,
|
||||
ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
|
||||
if (mxs_ssp_wait(spi, HW_SSP_CTRL0, BM_SSP_CTRL0_RUN, 1))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
if (write)
|
||||
writel(*buf, ssp->base + HW_SSP_DATA(ssp));
|
||||
|
||||
writel(BM_SSP_CTRL0_DATA_XFER,
|
||||
ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
|
||||
if (!write) {
|
||||
if (mxs_ssp_wait(spi, HW_SSP_STATUS(ssp),
|
||||
BM_SSP_STATUS_FIFO_EMPTY, 0))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
*buf = (readl(ssp->base + HW_SSP_DATA(ssp)) & 0xff);
|
||||
}
|
||||
|
||||
if (mxs_ssp_wait(spi, HW_SSP_CTRL0, BM_SSP_CTRL0_RUN, 0))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
buf++;
|
||||
}
|
||||
|
||||
if (len <= 0)
|
||||
return 0;
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int mxs_spi_transfer_one(struct spi_master *master,
|
||||
struct spi_message *m)
|
||||
{
|
||||
struct mxs_spi *spi = spi_master_get_devdata(master);
|
||||
struct mxs_ssp *ssp = &spi->ssp;
|
||||
int first, last;
|
||||
struct spi_transfer *t, *tmp_t;
|
||||
int status = 0;
|
||||
int cs;
|
||||
|
||||
first = last = 0;
|
||||
|
||||
cs = m->spi->chip_select;
|
||||
|
||||
list_for_each_entry_safe(t, tmp_t, &m->transfers, transfer_list) {
|
||||
|
||||
status = mxs_spi_setup_transfer(m->spi, t);
|
||||
if (status)
|
||||
break;
|
||||
|
||||
if (&t->transfer_list == m->transfers.next)
|
||||
first = 1;
|
||||
if (&t->transfer_list == m->transfers.prev)
|
||||
last = 1;
|
||||
if ((t->rx_buf && t->tx_buf) || (t->rx_dma && t->tx_dma)) {
|
||||
dev_err(ssp->dev,
|
||||
"Cannot send and receive simultaneously\n");
|
||||
status = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Small blocks can be transfered via PIO.
|
||||
* Measured by empiric means:
|
||||
*
|
||||
* dd if=/dev/mtdblock0 of=/dev/null bs=1024k count=1
|
||||
*
|
||||
* DMA only: 2.164808 seconds, 473.0KB/s
|
||||
* Combined: 1.676276 seconds, 610.9KB/s
|
||||
*/
|
||||
if (t->len < 32) {
|
||||
writel(BM_SSP_CTRL1_DMA_ENABLE,
|
||||
ssp->base + HW_SSP_CTRL1(ssp) +
|
||||
STMP_OFFSET_REG_CLR);
|
||||
|
||||
if (t->tx_buf)
|
||||
status = mxs_spi_txrx_pio(spi, cs,
|
||||
(void *)t->tx_buf,
|
||||
t->len, &first, &last, 1);
|
||||
if (t->rx_buf)
|
||||
status = mxs_spi_txrx_pio(spi, cs,
|
||||
t->rx_buf, t->len,
|
||||
&first, &last, 0);
|
||||
} else {
|
||||
writel(BM_SSP_CTRL1_DMA_ENABLE,
|
||||
ssp->base + HW_SSP_CTRL1(ssp) +
|
||||
STMP_OFFSET_REG_SET);
|
||||
|
||||
if (t->tx_buf)
|
||||
status = mxs_spi_txrx_dma(spi, cs,
|
||||
(void *)t->tx_buf, t->len,
|
||||
&first, &last, 1);
|
||||
if (t->rx_buf)
|
||||
status = mxs_spi_txrx_dma(spi, cs,
|
||||
t->rx_buf, t->len,
|
||||
&first, &last, 0);
|
||||
}
|
||||
|
||||
if (status) {
|
||||
stmp_reset_block(ssp->base);
|
||||
break;
|
||||
}
|
||||
|
||||
m->actual_length += t->len;
|
||||
first = last = 0;
|
||||
}
|
||||
|
||||
m->status = 0;
|
||||
spi_finalize_current_message(master);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static bool mxs_ssp_dma_filter(struct dma_chan *chan, void *param)
|
||||
{
|
||||
struct mxs_ssp *ssp = param;
|
||||
|
||||
if (!mxs_dma_is_apbh(chan))
|
||||
return false;
|
||||
|
||||
if (chan->chan_id != ssp->dma_channel)
|
||||
return false;
|
||||
|
||||
chan->private = &ssp->dma_data;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct of_device_id mxs_spi_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx23-spi", .data = (void *) IMX23_SSP, },
|
||||
{ .compatible = "fsl,imx28-spi", .data = (void *) IMX28_SSP, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mxs_spi_dt_ids);
|
||||
|
||||
static int __devinit mxs_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *of_id =
|
||||
of_match_device(mxs_spi_dt_ids, &pdev->dev);
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct spi_master *master;
|
||||
struct mxs_spi *spi;
|
||||
struct mxs_ssp *ssp;
|
||||
struct resource *iores, *dmares;
|
||||
struct pinctrl *pinctrl;
|
||||
struct clk *clk;
|
||||
void __iomem *base;
|
||||
int devid, dma_channel, clk_freq;
|
||||
int ret = 0, irq_err, irq_dma;
|
||||
dma_cap_mask_t mask;
|
||||
|
||||
/*
|
||||
* Default clock speed for the SPI core. 160MHz seems to
|
||||
* work reasonably well with most SPI flashes, so use this
|
||||
* as a default. Override with "clock-frequency" DT prop.
|
||||
*/
|
||||
const int clk_freq_default = 160000000;
|
||||
|
||||
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
irq_err = platform_get_irq(pdev, 0);
|
||||
irq_dma = platform_get_irq(pdev, 1);
|
||||
if (!iores || irq_err < 0 || irq_dma < 0)
|
||||
return -EINVAL;
|
||||
|
||||
base = devm_request_and_ioremap(&pdev->dev, iores);
|
||||
if (!base)
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
|
||||
if (IS_ERR(pinctrl))
|
||||
return PTR_ERR(pinctrl);
|
||||
|
||||
clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
if (np) {
|
||||
devid = (enum mxs_ssp_id) of_id->data;
|
||||
/*
|
||||
* TODO: This is a temporary solution and should be changed
|
||||
* to use generic DMA binding later when the helpers get in.
|
||||
*/
|
||||
ret = of_property_read_u32(np, "fsl,ssp-dma-channel",
|
||||
&dma_channel);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to get DMA channel\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "clock-frequency",
|
||||
&clk_freq);
|
||||
if (ret)
|
||||
clk_freq = clk_freq_default;
|
||||
} else {
|
||||
dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
if (!dmares)
|
||||
return -EINVAL;
|
||||
devid = pdev->id_entry->driver_data;
|
||||
dma_channel = dmares->start;
|
||||
clk_freq = clk_freq_default;
|
||||
}
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(*spi));
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
|
||||
master->transfer_one_message = mxs_spi_transfer_one;
|
||||
master->setup = mxs_spi_setup;
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA;
|
||||
master->num_chipselect = 3;
|
||||
master->dev.of_node = np;
|
||||
master->flags = SPI_MASTER_HALF_DUPLEX;
|
||||
|
||||
spi = spi_master_get_devdata(master);
|
||||
ssp = &spi->ssp;
|
||||
ssp->dev = &pdev->dev;
|
||||
ssp->clk = clk;
|
||||
ssp->base = base;
|
||||
ssp->devid = devid;
|
||||
ssp->dma_channel = dma_channel;
|
||||
|
||||
init_completion(&spi->c);
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq_err, mxs_ssp_irq_handler, 0,
|
||||
DRIVER_NAME, ssp);
|
||||
if (ret)
|
||||
goto out_master_free;
|
||||
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
ssp->dma_data.chan_irq = irq_dma;
|
||||
ssp->dmach = dma_request_channel(mask, mxs_ssp_dma_filter, ssp);
|
||||
if (!ssp->dmach) {
|
||||
dev_err(ssp->dev, "Failed to request DMA\n");
|
||||
goto out_master_free;
|
||||
}
|
||||
|
||||
clk_prepare_enable(ssp->clk);
|
||||
clk_set_rate(ssp->clk, clk_freq);
|
||||
ssp->clk_rate = clk_get_rate(ssp->clk) / 1000;
|
||||
|
||||
stmp_reset_block(ssp->base);
|
||||
|
||||
platform_set_drvdata(pdev, master);
|
||||
|
||||
ret = spi_register_master(master);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Cannot register SPI master, %d\n", ret);
|
||||
goto out_free_dma;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_dma:
|
||||
dma_release_channel(ssp->dmach);
|
||||
clk_disable_unprepare(ssp->clk);
|
||||
out_master_free:
|
||||
spi_master_put(master);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit mxs_spi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master;
|
||||
struct mxs_spi *spi;
|
||||
struct mxs_ssp *ssp;
|
||||
|
||||
master = spi_master_get(platform_get_drvdata(pdev));
|
||||
spi = spi_master_get_devdata(master);
|
||||
ssp = &spi->ssp;
|
||||
|
||||
spi_unregister_master(master);
|
||||
|
||||
dma_release_channel(ssp->dmach);
|
||||
|
||||
clk_disable_unprepare(ssp->clk);
|
||||
|
||||
spi_master_put(master);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver mxs_spi_driver = {
|
||||
.probe = mxs_spi_probe,
|
||||
.remove = __devexit_p(mxs_spi_remove),
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = mxs_spi_dt_ids,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(mxs_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
|
||||
MODULE_DESCRIPTION("MXS SPI master driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:mxs-spi");
|
@ -38,6 +38,8 @@
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
@ -140,13 +142,6 @@ struct omap2_mcspi_cs {
|
||||
u32 chconf0;
|
||||
};
|
||||
|
||||
#define MOD_REG_BIT(val, mask, set) do { \
|
||||
if (set) \
|
||||
val |= mask; \
|
||||
else \
|
||||
val &= ~mask; \
|
||||
} while (0)
|
||||
|
||||
static inline void mcspi_write_reg(struct spi_master *master,
|
||||
int idx, u32 val)
|
||||
{
|
||||
@ -205,7 +200,11 @@ static void omap2_mcspi_set_dma_req(const struct spi_device *spi,
|
||||
else
|
||||
rw = OMAP2_MCSPI_CHCONF_DMAW;
|
||||
|
||||
MOD_REG_BIT(l, rw, enable);
|
||||
if (enable)
|
||||
l |= rw;
|
||||
else
|
||||
l &= ~rw;
|
||||
|
||||
mcspi_write_chconf0(spi, l);
|
||||
}
|
||||
|
||||
@ -224,7 +223,11 @@ static void omap2_mcspi_force_cs(struct spi_device *spi, int cs_active)
|
||||
u32 l;
|
||||
|
||||
l = mcspi_cached_chconf0(spi);
|
||||
MOD_REG_BIT(l, OMAP2_MCSPI_CHCONF_FORCE, cs_active);
|
||||
if (cs_active)
|
||||
l |= OMAP2_MCSPI_CHCONF_FORCE;
|
||||
else
|
||||
l &= ~OMAP2_MCSPI_CHCONF_FORCE;
|
||||
|
||||
mcspi_write_chconf0(spi, l);
|
||||
}
|
||||
|
||||
@ -239,9 +242,8 @@ static void omap2_mcspi_set_master_mode(struct spi_master *master)
|
||||
* to single-channel master mode
|
||||
*/
|
||||
l = mcspi_read_reg(master, OMAP2_MCSPI_MODULCTRL);
|
||||
MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_STEST, 0);
|
||||
MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_MS, 0);
|
||||
MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_SINGLE, 1);
|
||||
l &= ~(OMAP2_MCSPI_MODULCTRL_STEST | OMAP2_MCSPI_MODULCTRL_MS);
|
||||
l |= OMAP2_MCSPI_MODULCTRL_SINGLE;
|
||||
mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, l);
|
||||
|
||||
ctx->modulctrl = l;
|
||||
@ -260,16 +262,6 @@ static void omap2_mcspi_restore_ctx(struct omap2_mcspi *mcspi)
|
||||
list_for_each_entry(cs, &ctx->cs, node)
|
||||
__raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0);
|
||||
}
|
||||
static void omap2_mcspi_disable_clocks(struct omap2_mcspi *mcspi)
|
||||
{
|
||||
pm_runtime_mark_last_busy(mcspi->dev);
|
||||
pm_runtime_put_autosuspend(mcspi->dev);
|
||||
}
|
||||
|
||||
static int omap2_mcspi_enable_clocks(struct omap2_mcspi *mcspi)
|
||||
{
|
||||
return pm_runtime_get_sync(mcspi->dev);
|
||||
}
|
||||
|
||||
static int omap2_prepare_transfer(struct spi_master *master)
|
||||
{
|
||||
@ -325,6 +317,169 @@ static void omap2_mcspi_tx_callback(void *data)
|
||||
omap2_mcspi_set_dma_req(spi, 0, 0);
|
||||
}
|
||||
|
||||
static void omap2_mcspi_tx_dma(struct spi_device *spi,
|
||||
struct spi_transfer *xfer,
|
||||
struct dma_slave_config cfg)
|
||||
{
|
||||
struct omap2_mcspi *mcspi;
|
||||
struct omap2_mcspi_dma *mcspi_dma;
|
||||
unsigned int count;
|
||||
u8 * rx;
|
||||
const u8 * tx;
|
||||
void __iomem *chstat_reg;
|
||||
struct omap2_mcspi_cs *cs = spi->controller_state;
|
||||
|
||||
mcspi = spi_master_get_devdata(spi->master);
|
||||
mcspi_dma = &mcspi->dma_channels[spi->chip_select];
|
||||
count = xfer->len;
|
||||
|
||||
rx = xfer->rx_buf;
|
||||
tx = xfer->tx_buf;
|
||||
chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0;
|
||||
|
||||
if (mcspi_dma->dma_tx) {
|
||||
struct dma_async_tx_descriptor *tx;
|
||||
struct scatterlist sg;
|
||||
|
||||
dmaengine_slave_config(mcspi_dma->dma_tx, &cfg);
|
||||
|
||||
sg_init_table(&sg, 1);
|
||||
sg_dma_address(&sg) = xfer->tx_dma;
|
||||
sg_dma_len(&sg) = xfer->len;
|
||||
|
||||
tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, &sg, 1,
|
||||
DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (tx) {
|
||||
tx->callback = omap2_mcspi_tx_callback;
|
||||
tx->callback_param = spi;
|
||||
dmaengine_submit(tx);
|
||||
} else {
|
||||
/* FIXME: fall back to PIO? */
|
||||
}
|
||||
}
|
||||
dma_async_issue_pending(mcspi_dma->dma_tx);
|
||||
omap2_mcspi_set_dma_req(spi, 0, 1);
|
||||
|
||||
wait_for_completion(&mcspi_dma->dma_tx_completion);
|
||||
dma_unmap_single(mcspi->dev, xfer->tx_dma, count,
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
/* for TX_ONLY mode, be sure all words have shifted out */
|
||||
if (rx == NULL) {
|
||||
if (mcspi_wait_for_reg_bit(chstat_reg,
|
||||
OMAP2_MCSPI_CHSTAT_TXS) < 0)
|
||||
dev_err(&spi->dev, "TXS timed out\n");
|
||||
else if (mcspi_wait_for_reg_bit(chstat_reg,
|
||||
OMAP2_MCSPI_CHSTAT_EOT) < 0)
|
||||
dev_err(&spi->dev, "EOT timed out\n");
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned
|
||||
omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer,
|
||||
struct dma_slave_config cfg,
|
||||
unsigned es)
|
||||
{
|
||||
struct omap2_mcspi *mcspi;
|
||||
struct omap2_mcspi_dma *mcspi_dma;
|
||||
unsigned int count;
|
||||
u32 l;
|
||||
int elements = 0;
|
||||
int word_len, element_count;
|
||||
struct omap2_mcspi_cs *cs = spi->controller_state;
|
||||
mcspi = spi_master_get_devdata(spi->master);
|
||||
mcspi_dma = &mcspi->dma_channels[spi->chip_select];
|
||||
count = xfer->len;
|
||||
word_len = cs->word_len;
|
||||
l = mcspi_cached_chconf0(spi);
|
||||
|
||||
if (word_len <= 8)
|
||||
element_count = count;
|
||||
else if (word_len <= 16)
|
||||
element_count = count >> 1;
|
||||
else /* word_len <= 32 */
|
||||
element_count = count >> 2;
|
||||
|
||||
if (mcspi_dma->dma_rx) {
|
||||
struct dma_async_tx_descriptor *tx;
|
||||
struct scatterlist sg;
|
||||
size_t len = xfer->len - es;
|
||||
|
||||
dmaengine_slave_config(mcspi_dma->dma_rx, &cfg);
|
||||
|
||||
if (l & OMAP2_MCSPI_CHCONF_TURBO)
|
||||
len -= es;
|
||||
|
||||
sg_init_table(&sg, 1);
|
||||
sg_dma_address(&sg) = xfer->rx_dma;
|
||||
sg_dma_len(&sg) = len;
|
||||
|
||||
tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, &sg, 1,
|
||||
DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT |
|
||||
DMA_CTRL_ACK);
|
||||
if (tx) {
|
||||
tx->callback = omap2_mcspi_rx_callback;
|
||||
tx->callback_param = spi;
|
||||
dmaengine_submit(tx);
|
||||
} else {
|
||||
/* FIXME: fall back to PIO? */
|
||||
}
|
||||
}
|
||||
|
||||
dma_async_issue_pending(mcspi_dma->dma_rx);
|
||||
omap2_mcspi_set_dma_req(spi, 1, 1);
|
||||
|
||||
wait_for_completion(&mcspi_dma->dma_rx_completion);
|
||||
dma_unmap_single(mcspi->dev, xfer->rx_dma, count,
|
||||
DMA_FROM_DEVICE);
|
||||
omap2_mcspi_set_enable(spi, 0);
|
||||
|
||||
elements = element_count - 1;
|
||||
|
||||
if (l & OMAP2_MCSPI_CHCONF_TURBO) {
|
||||
elements--;
|
||||
|
||||
if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0)
|
||||
& OMAP2_MCSPI_CHSTAT_RXS)) {
|
||||
u32 w;
|
||||
|
||||
w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0);
|
||||
if (word_len <= 8)
|
||||
((u8 *)xfer->rx_buf)[elements++] = w;
|
||||
else if (word_len <= 16)
|
||||
((u16 *)xfer->rx_buf)[elements++] = w;
|
||||
else /* word_len <= 32 */
|
||||
((u32 *)xfer->rx_buf)[elements++] = w;
|
||||
} else {
|
||||
dev_err(&spi->dev, "DMA RX penultimate word empty");
|
||||
count -= (word_len <= 8) ? 2 :
|
||||
(word_len <= 16) ? 4 :
|
||||
/* word_len <= 32 */ 8;
|
||||
omap2_mcspi_set_enable(spi, 1);
|
||||
return count;
|
||||
}
|
||||
}
|
||||
if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0)
|
||||
& OMAP2_MCSPI_CHSTAT_RXS)) {
|
||||
u32 w;
|
||||
|
||||
w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0);
|
||||
if (word_len <= 8)
|
||||
((u8 *)xfer->rx_buf)[elements] = w;
|
||||
else if (word_len <= 16)
|
||||
((u16 *)xfer->rx_buf)[elements] = w;
|
||||
else /* word_len <= 32 */
|
||||
((u32 *)xfer->rx_buf)[elements] = w;
|
||||
} else {
|
||||
dev_err(&spi->dev, "DMA RX last word empty");
|
||||
count -= (word_len <= 8) ? 1 :
|
||||
(word_len <= 16) ? 2 :
|
||||
/* word_len <= 32 */ 4;
|
||||
}
|
||||
omap2_mcspi_set_enable(spi, 1);
|
||||
return count;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
{
|
||||
@ -332,12 +487,9 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
struct omap2_mcspi_cs *cs = spi->controller_state;
|
||||
struct omap2_mcspi_dma *mcspi_dma;
|
||||
unsigned int count;
|
||||
int word_len, element_count;
|
||||
int elements = 0;
|
||||
u32 l;
|
||||
u8 * rx;
|
||||
const u8 * tx;
|
||||
void __iomem *chstat_reg;
|
||||
u8 *rx;
|
||||
const u8 *tx;
|
||||
struct dma_slave_config cfg;
|
||||
enum dma_slave_buswidth width;
|
||||
unsigned es;
|
||||
@ -346,7 +498,6 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
mcspi_dma = &mcspi->dma_channels[spi->chip_select];
|
||||
l = mcspi_cached_chconf0(spi);
|
||||
|
||||
chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0;
|
||||
|
||||
if (cs->word_len <= 8) {
|
||||
width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||
@ -367,144 +518,17 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
cfg.src_maxburst = 1;
|
||||
cfg.dst_maxburst = 1;
|
||||
|
||||
if (xfer->tx_buf && mcspi_dma->dma_tx) {
|
||||
struct dma_async_tx_descriptor *tx;
|
||||
struct scatterlist sg;
|
||||
|
||||
dmaengine_slave_config(mcspi_dma->dma_tx, &cfg);
|
||||
|
||||
sg_init_table(&sg, 1);
|
||||
sg_dma_address(&sg) = xfer->tx_dma;
|
||||
sg_dma_len(&sg) = xfer->len;
|
||||
|
||||
tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, &sg, 1,
|
||||
DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (tx) {
|
||||
tx->callback = omap2_mcspi_tx_callback;
|
||||
tx->callback_param = spi;
|
||||
dmaengine_submit(tx);
|
||||
} else {
|
||||
/* FIXME: fall back to PIO? */
|
||||
}
|
||||
}
|
||||
|
||||
if (xfer->rx_buf && mcspi_dma->dma_rx) {
|
||||
struct dma_async_tx_descriptor *tx;
|
||||
struct scatterlist sg;
|
||||
size_t len = xfer->len - es;
|
||||
|
||||
dmaengine_slave_config(mcspi_dma->dma_rx, &cfg);
|
||||
|
||||
if (l & OMAP2_MCSPI_CHCONF_TURBO)
|
||||
len -= es;
|
||||
|
||||
sg_init_table(&sg, 1);
|
||||
sg_dma_address(&sg) = xfer->rx_dma;
|
||||
sg_dma_len(&sg) = len;
|
||||
|
||||
tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, &sg, 1,
|
||||
DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (tx) {
|
||||
tx->callback = omap2_mcspi_rx_callback;
|
||||
tx->callback_param = spi;
|
||||
dmaengine_submit(tx);
|
||||
} else {
|
||||
/* FIXME: fall back to PIO? */
|
||||
}
|
||||
}
|
||||
|
||||
count = xfer->len;
|
||||
word_len = cs->word_len;
|
||||
|
||||
rx = xfer->rx_buf;
|
||||
tx = xfer->tx_buf;
|
||||
|
||||
if (word_len <= 8) {
|
||||
element_count = count;
|
||||
} else if (word_len <= 16) {
|
||||
element_count = count >> 1;
|
||||
} else /* word_len <= 32 */ {
|
||||
element_count = count >> 2;
|
||||
}
|
||||
count = xfer->len;
|
||||
|
||||
if (tx != NULL) {
|
||||
dma_async_issue_pending(mcspi_dma->dma_tx);
|
||||
omap2_mcspi_set_dma_req(spi, 0, 1);
|
||||
}
|
||||
if (tx != NULL)
|
||||
omap2_mcspi_tx_dma(spi, xfer, cfg);
|
||||
|
||||
if (rx != NULL) {
|
||||
dma_async_issue_pending(mcspi_dma->dma_rx);
|
||||
omap2_mcspi_set_dma_req(spi, 1, 1);
|
||||
}
|
||||
if (rx != NULL)
|
||||
return omap2_mcspi_rx_dma(spi, xfer, cfg, es);
|
||||
|
||||
if (tx != NULL) {
|
||||
wait_for_completion(&mcspi_dma->dma_tx_completion);
|
||||
dma_unmap_single(mcspi->dev, xfer->tx_dma, count,
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
/* for TX_ONLY mode, be sure all words have shifted out */
|
||||
if (rx == NULL) {
|
||||
if (mcspi_wait_for_reg_bit(chstat_reg,
|
||||
OMAP2_MCSPI_CHSTAT_TXS) < 0)
|
||||
dev_err(&spi->dev, "TXS timed out\n");
|
||||
else if (mcspi_wait_for_reg_bit(chstat_reg,
|
||||
OMAP2_MCSPI_CHSTAT_EOT) < 0)
|
||||
dev_err(&spi->dev, "EOT timed out\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (rx != NULL) {
|
||||
wait_for_completion(&mcspi_dma->dma_rx_completion);
|
||||
dma_unmap_single(mcspi->dev, xfer->rx_dma, count,
|
||||
DMA_FROM_DEVICE);
|
||||
omap2_mcspi_set_enable(spi, 0);
|
||||
|
||||
elements = element_count - 1;
|
||||
|
||||
if (l & OMAP2_MCSPI_CHCONF_TURBO) {
|
||||
elements--;
|
||||
|
||||
if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0)
|
||||
& OMAP2_MCSPI_CHSTAT_RXS)) {
|
||||
u32 w;
|
||||
|
||||
w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0);
|
||||
if (word_len <= 8)
|
||||
((u8 *)xfer->rx_buf)[elements++] = w;
|
||||
else if (word_len <= 16)
|
||||
((u16 *)xfer->rx_buf)[elements++] = w;
|
||||
else /* word_len <= 32 */
|
||||
((u32 *)xfer->rx_buf)[elements++] = w;
|
||||
} else {
|
||||
dev_err(&spi->dev,
|
||||
"DMA RX penultimate word empty");
|
||||
count -= (word_len <= 8) ? 2 :
|
||||
(word_len <= 16) ? 4 :
|
||||
/* word_len <= 32 */ 8;
|
||||
omap2_mcspi_set_enable(spi, 1);
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0)
|
||||
& OMAP2_MCSPI_CHSTAT_RXS)) {
|
||||
u32 w;
|
||||
|
||||
w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0);
|
||||
if (word_len <= 8)
|
||||
((u8 *)xfer->rx_buf)[elements] = w;
|
||||
else if (word_len <= 16)
|
||||
((u16 *)xfer->rx_buf)[elements] = w;
|
||||
else /* word_len <= 32 */
|
||||
((u32 *)xfer->rx_buf)[elements] = w;
|
||||
} else {
|
||||
dev_err(&spi->dev, "DMA RX last word empty");
|
||||
count -= (word_len <= 8) ? 1 :
|
||||
(word_len <= 16) ? 2 :
|
||||
/* word_len <= 32 */ 4;
|
||||
}
|
||||
omap2_mcspi_set_enable(spi, 1);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -848,12 +872,13 @@ static int omap2_mcspi_setup(struct spi_device *spi)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = omap2_mcspi_enable_clocks(mcspi);
|
||||
ret = pm_runtime_get_sync(mcspi->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = omap2_mcspi_setup_transfer(spi, NULL);
|
||||
omap2_mcspi_disable_clocks(mcspi);
|
||||
pm_runtime_mark_last_busy(mcspi->dev);
|
||||
pm_runtime_put_autosuspend(mcspi->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -1067,7 +1092,7 @@ static int __devinit omap2_mcspi_master_setup(struct omap2_mcspi *mcspi)
|
||||
struct omap2_mcspi_regs *ctx = &mcspi->ctx;
|
||||
int ret = 0;
|
||||
|
||||
ret = omap2_mcspi_enable_clocks(mcspi);
|
||||
ret = pm_runtime_get_sync(mcspi->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -1076,7 +1101,8 @@ static int __devinit omap2_mcspi_master_setup(struct omap2_mcspi *mcspi)
|
||||
ctx->wakeupenable = OMAP2_MCSPI_WAKEUPENABLE_WKEN;
|
||||
|
||||
omap2_mcspi_set_master_mode(master);
|
||||
omap2_mcspi_disable_clocks(mcspi);
|
||||
pm_runtime_mark_last_busy(mcspi->dev);
|
||||
pm_runtime_put_autosuspend(mcspi->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1124,6 +1150,7 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev)
|
||||
static int bus_num = 1;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
const struct of_device_id *match;
|
||||
struct pinctrl *pinctrl;
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof *mcspi);
|
||||
if (master == NULL) {
|
||||
@ -1219,6 +1246,11 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev)
|
||||
if (status < 0)
|
||||
goto dma_chnl_free;
|
||||
|
||||
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
|
||||
if (IS_ERR(pinctrl))
|
||||
dev_warn(&pdev->dev,
|
||||
"pins are not configured from the driver\n");
|
||||
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
@ -1238,7 +1270,6 @@ dma_chnl_free:
|
||||
kfree(mcspi->dma_channels);
|
||||
free_master:
|
||||
spi_master_put(master);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -1252,12 +1283,11 @@ static int __devexit omap2_mcspi_remove(struct platform_device *pdev)
|
||||
mcspi = spi_master_get_devdata(master);
|
||||
dma_channels = mcspi->dma_channels;
|
||||
|
||||
omap2_mcspi_disable_clocks(mcspi);
|
||||
pm_runtime_put_sync(mcspi->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
spi_unregister_master(master);
|
||||
kfree(dma_channels);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1278,20 +1308,21 @@ static int omap2_mcspi_resume(struct device *dev)
|
||||
struct omap2_mcspi_regs *ctx = &mcspi->ctx;
|
||||
struct omap2_mcspi_cs *cs;
|
||||
|
||||
omap2_mcspi_enable_clocks(mcspi);
|
||||
pm_runtime_get_sync(mcspi->dev);
|
||||
list_for_each_entry(cs, &ctx->cs, node) {
|
||||
if ((cs->chconf0 & OMAP2_MCSPI_CHCONF_FORCE) == 0) {
|
||||
/*
|
||||
* We need to toggle CS state for OMAP take this
|
||||
* change in account.
|
||||
*/
|
||||
MOD_REG_BIT(cs->chconf0, OMAP2_MCSPI_CHCONF_FORCE, 1);
|
||||
cs->chconf0 |= OMAP2_MCSPI_CHCONF_FORCE;
|
||||
__raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0);
|
||||
MOD_REG_BIT(cs->chconf0, OMAP2_MCSPI_CHCONF_FORCE, 0);
|
||||
cs->chconf0 &= ~OMAP2_MCSPI_CHCONF_FORCE;
|
||||
__raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0);
|
||||
}
|
||||
}
|
||||
omap2_mcspi_disable_clocks(mcspi);
|
||||
pm_runtime_mark_last_busy(mcspi->dev);
|
||||
pm_runtime_put_autosuspend(mcspi->dev);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
|
@ -36,12 +36,6 @@
|
||||
#define ORION_SPI_CLK_PRESCALE_MASK 0x1F
|
||||
|
||||
struct orion_spi {
|
||||
struct work_struct work;
|
||||
|
||||
/* Lock access to transfer list. */
|
||||
spinlock_t lock;
|
||||
|
||||
struct list_head msg_queue;
|
||||
struct spi_master *master;
|
||||
void __iomem *base;
|
||||
unsigned int max_speed;
|
||||
@ -49,8 +43,6 @@ struct orion_spi {
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static struct workqueue_struct *orion_spi_wq;
|
||||
|
||||
static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg)
|
||||
{
|
||||
return orion_spi->base + reg;
|
||||
@ -277,73 +269,78 @@ out:
|
||||
}
|
||||
|
||||
|
||||
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 =
|
||||
container_of(work, struct orion_spi, work);
|
||||
struct orion_spi *orion_spi = spi_master_get_devdata(master);
|
||||
struct spi_device *spi = m->spi;
|
||||
struct spi_transfer *t = NULL;
|
||||
int par_override = 0;
|
||||
int status = 0;
|
||||
int cs_active = 0;
|
||||
|
||||
spin_lock_irq(&orion_spi->lock);
|
||||
while (!list_empty(&orion_spi->msg_queue)) {
|
||||
struct spi_message *m;
|
||||
struct spi_device *spi;
|
||||
struct spi_transfer *t = NULL;
|
||||
int par_override = 0;
|
||||
int status = 0;
|
||||
int cs_active = 0;
|
||||
/* Load defaults */
|
||||
status = orion_spi_setup_transfer(spi, NULL);
|
||||
|
||||
m = container_of(orion_spi->msg_queue.next, struct spi_message,
|
||||
queue);
|
||||
if (status < 0)
|
||||
goto msg_done;
|
||||
|
||||
list_del_init(&m->queue);
|
||||
spin_unlock_irq(&orion_spi->lock);
|
||||
|
||||
spi = m->spi;
|
||||
|
||||
/* Load defaults */
|
||||
status = orion_spi_setup_transfer(spi, NULL);
|
||||
|
||||
if (status < 0)
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
/* 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;
|
||||
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
if (par_override || t->speed_hz || t->bits_per_word) {
|
||||
par_override = 1;
|
||||
status = orion_spi_setup_transfer(spi, t);
|
||||
if (status < 0)
|
||||
break;
|
||||
if (!t->speed_hz && !t->bits_per_word)
|
||||
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 (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;
|
||||
}
|
||||
|
||||
if (par_override || t->speed_hz || t->bits_per_word) {
|
||||
par_override = 1;
|
||||
status = orion_spi_setup_transfer(spi, t);
|
||||
if (status < 0)
|
||||
break;
|
||||
if (!t->speed_hz && !t->bits_per_word)
|
||||
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);
|
||||
|
||||
m->status = status;
|
||||
m->complete(m->context);
|
||||
|
||||
spin_lock_irq(&orion_spi->lock);
|
||||
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)
|
||||
@ -376,75 +373,6 @@ static int orion_spi_setup(struct spi_device *spi)
|
||||
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)
|
||||
{
|
||||
struct spi_master *master;
|
||||
@ -474,7 +402,7 @@ static int __init orion_spi_probe(struct platform_device *pdev)
|
||||
master->mode_bits = 0;
|
||||
|
||||
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;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, master);
|
||||
@ -507,11 +435,6 @@ static int __init orion_spi_probe(struct platform_device *pdev)
|
||||
}
|
||||
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)
|
||||
goto out_rel_mem;
|
||||
|
||||
@ -536,14 +459,12 @@ out:
|
||||
static int __exit orion_spi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master;
|
||||
struct orion_spi *spi;
|
||||
struct resource *r;
|
||||
struct orion_spi *spi;
|
||||
|
||||
master = dev_get_drvdata(&pdev->dev);
|
||||
spi = spi_master_get_devdata(master);
|
||||
|
||||
cancel_work_sync(&spi->work);
|
||||
|
||||
clk_disable_unprepare(spi->clk);
|
||||
clk_put(spi->clk);
|
||||
|
||||
@ -574,21 +495,13 @@ static struct platform_driver orion_spi_driver = {
|
||||
|
||||
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);
|
||||
}
|
||||
module_init(orion_spi_init);
|
||||
|
||||
static void __exit orion_spi_exit(void)
|
||||
{
|
||||
flush_workqueue(orion_spi_wq);
|
||||
platform_driver_unregister(&orion_spi_driver);
|
||||
|
||||
destroy_workqueue(orion_spi_wq);
|
||||
}
|
||||
module_exit(orion_spi_exit);
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* A driver for the ARM PL022 PrimeCell SSP/SPI bus master.
|
||||
*
|
||||
* Copyright (C) 2008-2009 ST-Ericsson AB
|
||||
* Copyright (C) 2008-2012 ST-Ericsson AB
|
||||
* Copyright (C) 2006 STMicroelectronics Pvt. Ltd.
|
||||
*
|
||||
* Author: Linus Walleij <linus.walleij@stericsson.com>
|
||||
@ -40,6 +40,9 @@
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
|
||||
/*
|
||||
* This macro is used to define some register default values.
|
||||
@ -356,6 +359,8 @@ struct vendor_data {
|
||||
* @sgt_rx: scattertable for the RX transfer
|
||||
* @sgt_tx: scattertable for the TX transfer
|
||||
* @dummypage: a dummy page used for driving data on the bus with DMA
|
||||
* @cur_cs: current chip select (gpio)
|
||||
* @chipselects: list of chipselects (gpios)
|
||||
*/
|
||||
struct pl022 {
|
||||
struct amba_device *adev;
|
||||
@ -363,6 +368,10 @@ struct pl022 {
|
||||
resource_size_t phybase;
|
||||
void __iomem *virtbase;
|
||||
struct clk *clk;
|
||||
/* Two optional pin states - default & sleep */
|
||||
struct pinctrl *pinctrl;
|
||||
struct pinctrl_state *pins_default;
|
||||
struct pinctrl_state *pins_sleep;
|
||||
struct spi_master *master;
|
||||
struct pl022_ssp_controller *master_info;
|
||||
/* Message per-transfer pump */
|
||||
@ -389,6 +398,8 @@ struct pl022 {
|
||||
char *dummypage;
|
||||
bool dma_running;
|
||||
#endif
|
||||
int cur_cs;
|
||||
int *chipselects;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -433,6 +444,14 @@ static void null_cs_control(u32 command)
|
||||
pr_debug("pl022: dummy chip select control, CS=0x%x\n", command);
|
||||
}
|
||||
|
||||
static void pl022_cs_control(struct pl022 *pl022, u32 command)
|
||||
{
|
||||
if (gpio_is_valid(pl022->cur_cs))
|
||||
gpio_set_value(pl022->cur_cs, command);
|
||||
else
|
||||
pl022->cur_chip->cs_control(command);
|
||||
}
|
||||
|
||||
/**
|
||||
* giveback - current spi_message is over, schedule next message and call
|
||||
* callback of this message. Assumes that caller already
|
||||
@ -479,7 +498,7 @@ static void giveback(struct pl022 *pl022)
|
||||
if (next_msg && next_msg->spi != pl022->cur_msg->spi)
|
||||
next_msg = NULL;
|
||||
if (!next_msg || pl022->cur_msg->state == STATE_ERROR)
|
||||
pl022->cur_chip->cs_control(SSP_CHIP_DESELECT);
|
||||
pl022_cs_control(pl022, SSP_CHIP_DESELECT);
|
||||
else
|
||||
pl022->next_msg_cs_active = true;
|
||||
|
||||
@ -818,8 +837,7 @@ static void dma_callback(void *data)
|
||||
/* Update total bytes transferred */
|
||||
msg->actual_length += pl022->cur_transfer->len;
|
||||
if (pl022->cur_transfer->cs_change)
|
||||
pl022->cur_chip->
|
||||
cs_control(SSP_CHIP_DESELECT);
|
||||
pl022_cs_control(pl022, SSP_CHIP_DESELECT);
|
||||
|
||||
/* Move to next transfer */
|
||||
msg->state = next_transfer(pl022);
|
||||
@ -1252,8 +1270,7 @@ static irqreturn_t pl022_interrupt_handler(int irq, void *dev_id)
|
||||
/* Update total bytes transferred */
|
||||
msg->actual_length += pl022->cur_transfer->len;
|
||||
if (pl022->cur_transfer->cs_change)
|
||||
pl022->cur_chip->
|
||||
cs_control(SSP_CHIP_DESELECT);
|
||||
pl022_cs_control(pl022, SSP_CHIP_DESELECT);
|
||||
/* Move to next transfer */
|
||||
msg->state = next_transfer(pl022);
|
||||
tasklet_schedule(&pl022->pump_transfers);
|
||||
@ -1338,7 +1355,7 @@ static void pump_transfers(unsigned long data)
|
||||
|
||||
/* Reselect chip select only if cs_change was requested */
|
||||
if (previous->cs_change)
|
||||
pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
|
||||
pl022_cs_control(pl022, SSP_CHIP_SELECT);
|
||||
} else {
|
||||
/* STATE_START */
|
||||
message->state = STATE_RUNNING;
|
||||
@ -1377,7 +1394,7 @@ static void do_interrupt_dma_transfer(struct pl022 *pl022)
|
||||
|
||||
/* Enable target chip, if not already active */
|
||||
if (!pl022->next_msg_cs_active)
|
||||
pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
|
||||
pl022_cs_control(pl022, SSP_CHIP_SELECT);
|
||||
|
||||
if (set_up_next_transfer(pl022, pl022->cur_transfer)) {
|
||||
/* Error path */
|
||||
@ -1429,12 +1446,12 @@ static void do_polling_transfer(struct pl022 *pl022)
|
||||
if (previous->delay_usecs)
|
||||
udelay(previous->delay_usecs);
|
||||
if (previous->cs_change)
|
||||
pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
|
||||
pl022_cs_control(pl022, SSP_CHIP_SELECT);
|
||||
} else {
|
||||
/* STATE_START */
|
||||
message->state = STATE_RUNNING;
|
||||
if (!pl022->next_msg_cs_active)
|
||||
pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
|
||||
pl022_cs_control(pl022, SSP_CHIP_SELECT);
|
||||
}
|
||||
|
||||
/* Configuration Changing Per Transfer */
|
||||
@ -1466,7 +1483,7 @@ static void do_polling_transfer(struct pl022 *pl022)
|
||||
/* Update total byte transferred */
|
||||
message->actual_length += pl022->cur_transfer->len;
|
||||
if (pl022->cur_transfer->cs_change)
|
||||
pl022->cur_chip->cs_control(SSP_CHIP_DESELECT);
|
||||
pl022_cs_control(pl022, SSP_CHIP_DESELECT);
|
||||
/* Move to next transfer */
|
||||
message->state = next_transfer(pl022);
|
||||
}
|
||||
@ -1495,6 +1512,7 @@ static int pl022_transfer_one_message(struct spi_master *master,
|
||||
|
||||
/* Setup the SPI using the per chip configuration */
|
||||
pl022->cur_chip = spi_get_ctldata(msg->spi);
|
||||
pl022->cur_cs = pl022->chipselects[msg->spi->chip_select];
|
||||
|
||||
restore_state(pl022);
|
||||
flush(pl022);
|
||||
@ -1766,12 +1784,14 @@ static const struct pl022_config_chip pl022_default_chip_info = {
|
||||
static int pl022_setup(struct spi_device *spi)
|
||||
{
|
||||
struct pl022_config_chip const *chip_info;
|
||||
struct pl022_config_chip chip_info_dt;
|
||||
struct chip_data *chip;
|
||||
struct ssp_clock_params clk_freq = { .cpsdvsr = 0, .scr = 0};
|
||||
int status = 0;
|
||||
struct pl022 *pl022 = spi_master_get_devdata(spi->master);
|
||||
unsigned int bits = spi->bits_per_word;
|
||||
u32 tmp;
|
||||
struct device_node *np = spi->dev.of_node;
|
||||
|
||||
if (!spi->max_speed_hz)
|
||||
return -EINVAL;
|
||||
@ -1794,10 +1814,32 @@ static int pl022_setup(struct spi_device *spi)
|
||||
chip_info = spi->controller_data;
|
||||
|
||||
if (chip_info == NULL) {
|
||||
chip_info = &pl022_default_chip_info;
|
||||
/* spi_board_info.controller_data not is supplied */
|
||||
dev_dbg(&spi->dev,
|
||||
"using default controller_data settings\n");
|
||||
if (np) {
|
||||
chip_info_dt = pl022_default_chip_info;
|
||||
|
||||
chip_info_dt.hierarchy = SSP_MASTER;
|
||||
of_property_read_u32(np, "pl022,interface",
|
||||
&chip_info_dt.iface);
|
||||
of_property_read_u32(np, "pl022,com-mode",
|
||||
&chip_info_dt.com_mode);
|
||||
of_property_read_u32(np, "pl022,rx-level-trig",
|
||||
&chip_info_dt.rx_lev_trig);
|
||||
of_property_read_u32(np, "pl022,tx-level-trig",
|
||||
&chip_info_dt.tx_lev_trig);
|
||||
of_property_read_u32(np, "pl022,ctrl-len",
|
||||
&chip_info_dt.ctrl_len);
|
||||
of_property_read_u32(np, "pl022,wait-state",
|
||||
&chip_info_dt.wait_state);
|
||||
of_property_read_u32(np, "pl022,duplex",
|
||||
&chip_info_dt.duplex);
|
||||
|
||||
chip_info = &chip_info_dt;
|
||||
} else {
|
||||
chip_info = &pl022_default_chip_info;
|
||||
/* spi_board_info.controller_data not is supplied */
|
||||
dev_dbg(&spi->dev,
|
||||
"using default controller_data settings\n");
|
||||
}
|
||||
} else
|
||||
dev_dbg(&spi->dev,
|
||||
"using user supplied controller_data settings\n");
|
||||
@ -1840,8 +1882,9 @@ static int pl022_setup(struct spi_device *spi)
|
||||
chip->xfer_type = chip_info->com_mode;
|
||||
if (!chip_info->cs_control) {
|
||||
chip->cs_control = null_cs_control;
|
||||
dev_warn(&spi->dev,
|
||||
"chip select function is NULL for this chip\n");
|
||||
if (!gpio_is_valid(pl022->chipselects[spi->chip_select]))
|
||||
dev_warn(&spi->dev,
|
||||
"invalid chip select\n");
|
||||
} else
|
||||
chip->cs_control = chip_info->cs_control;
|
||||
|
||||
@ -1986,6 +2029,34 @@ static void pl022_cleanup(struct spi_device *spi)
|
||||
kfree(chip);
|
||||
}
|
||||
|
||||
static struct pl022_ssp_controller *
|
||||
pl022_platform_data_dt_get(struct device *dev)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
struct pl022_ssp_controller *pd;
|
||||
u32 tmp;
|
||||
|
||||
if (!np) {
|
||||
dev_err(dev, "no dt node defined\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pd = devm_kzalloc(dev, sizeof(struct pl022_ssp_controller), GFP_KERNEL);
|
||||
if (!pd) {
|
||||
dev_err(dev, "cannot allocate platform data memory\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pd->bus_id = -1;
|
||||
of_property_read_u32(np, "num-cs", &tmp);
|
||||
pd->num_chipselect = tmp;
|
||||
of_property_read_u32(np, "pl022,autosuspend-delay",
|
||||
&pd->autosuspend_delay);
|
||||
pd->rt = of_property_read_bool(np, "pl022,rt");
|
||||
|
||||
return pd;
|
||||
}
|
||||
|
||||
static int __devinit
|
||||
pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
{
|
||||
@ -1993,22 +2064,31 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
struct pl022_ssp_controller *platform_info = adev->dev.platform_data;
|
||||
struct spi_master *master;
|
||||
struct pl022 *pl022 = NULL; /*Data for this driver */
|
||||
int status = 0;
|
||||
struct device_node *np = adev->dev.of_node;
|
||||
int status = 0, i, num_cs;
|
||||
|
||||
dev_info(&adev->dev,
|
||||
"ARM PL022 driver, device ID: 0x%08x\n", adev->periphid);
|
||||
if (platform_info == NULL) {
|
||||
dev_err(&adev->dev, "probe - no platform data supplied\n");
|
||||
status = -ENODEV;
|
||||
goto err_no_pdata;
|
||||
if (!platform_info && IS_ENABLED(CONFIG_OF))
|
||||
platform_info = pl022_platform_data_dt_get(dev);
|
||||
|
||||
if (!platform_info) {
|
||||
dev_err(dev, "probe: no platform data defined\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (platform_info->num_chipselect) {
|
||||
num_cs = platform_info->num_chipselect;
|
||||
} else {
|
||||
dev_err(dev, "probe: no chip select defined\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Allocate master with space for data */
|
||||
master = spi_alloc_master(dev, sizeof(struct pl022));
|
||||
if (master == NULL) {
|
||||
dev_err(&adev->dev, "probe - cannot alloc SPI master\n");
|
||||
status = -ENOMEM;
|
||||
goto err_no_master;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pl022 = spi_master_get_devdata(master);
|
||||
@ -2016,19 +2096,71 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
pl022->master_info = platform_info;
|
||||
pl022->adev = adev;
|
||||
pl022->vendor = id->data;
|
||||
pl022->chipselects = devm_kzalloc(dev, num_cs * sizeof(int),
|
||||
GFP_KERNEL);
|
||||
|
||||
pl022->pinctrl = devm_pinctrl_get(dev);
|
||||
if (IS_ERR(pl022->pinctrl)) {
|
||||
status = PTR_ERR(pl022->pinctrl);
|
||||
goto err_no_pinctrl;
|
||||
}
|
||||
|
||||
pl022->pins_default = pinctrl_lookup_state(pl022->pinctrl,
|
||||
PINCTRL_STATE_DEFAULT);
|
||||
/* enable pins to be muxed in and configured */
|
||||
if (!IS_ERR(pl022->pins_default)) {
|
||||
status = pinctrl_select_state(pl022->pinctrl,
|
||||
pl022->pins_default);
|
||||
if (status)
|
||||
dev_err(dev, "could not set default pins\n");
|
||||
} else
|
||||
dev_err(dev, "could not get default pinstate\n");
|
||||
|
||||
pl022->pins_sleep = pinctrl_lookup_state(pl022->pinctrl,
|
||||
PINCTRL_STATE_SLEEP);
|
||||
if (IS_ERR(pl022->pins_sleep))
|
||||
dev_dbg(dev, "could not get sleep pinstate\n");
|
||||
|
||||
/*
|
||||
* Bus Number Which has been Assigned to this SSP controller
|
||||
* on this board
|
||||
*/
|
||||
master->bus_num = platform_info->bus_id;
|
||||
master->num_chipselect = platform_info->num_chipselect;
|
||||
master->num_chipselect = num_cs;
|
||||
master->cleanup = pl022_cleanup;
|
||||
master->setup = pl022_setup;
|
||||
master->prepare_transfer_hardware = pl022_prepare_transfer_hardware;
|
||||
master->transfer_one_message = pl022_transfer_one_message;
|
||||
master->unprepare_transfer_hardware = pl022_unprepare_transfer_hardware;
|
||||
master->rt = platform_info->rt;
|
||||
master->dev.of_node = dev->of_node;
|
||||
|
||||
if (platform_info->num_chipselect && platform_info->chipselects) {
|
||||
for (i = 0; i < num_cs; i++)
|
||||
pl022->chipselects[i] = platform_info->chipselects[i];
|
||||
} else if (IS_ENABLED(CONFIG_OF)) {
|
||||
for (i = 0; i < num_cs; i++) {
|
||||
int cs_gpio = of_get_named_gpio(np, "cs-gpios", i);
|
||||
|
||||
if (cs_gpio == -EPROBE_DEFER) {
|
||||
status = -EPROBE_DEFER;
|
||||
goto err_no_gpio;
|
||||
}
|
||||
|
||||
pl022->chipselects[i] = cs_gpio;
|
||||
|
||||
if (gpio_is_valid(cs_gpio)) {
|
||||
if (devm_gpio_request(dev, cs_gpio, "ssp-pl022"))
|
||||
dev_err(&adev->dev,
|
||||
"could not request %d gpio\n",
|
||||
cs_gpio);
|
||||
else if (gpio_direction_output(cs_gpio, 1))
|
||||
dev_err(&adev->dev,
|
||||
"could set gpio %d as output\n",
|
||||
cs_gpio);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Supports mode 0-3, loopback, and active low CS. Transfers are
|
||||
@ -2045,7 +2177,8 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
goto err_no_ioregion;
|
||||
|
||||
pl022->phybase = adev->res.start;
|
||||
pl022->virtbase = ioremap(adev->res.start, resource_size(&adev->res));
|
||||
pl022->virtbase = devm_ioremap(dev, adev->res.start,
|
||||
resource_size(&adev->res));
|
||||
if (pl022->virtbase == NULL) {
|
||||
status = -ENOMEM;
|
||||
goto err_no_ioremap;
|
||||
@ -2055,7 +2188,7 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
|
||||
pm_runtime_resume(dev);
|
||||
|
||||
pl022->clk = clk_get(&adev->dev, NULL);
|
||||
pl022->clk = devm_clk_get(&adev->dev, NULL);
|
||||
if (IS_ERR(pl022->clk)) {
|
||||
status = PTR_ERR(pl022->clk);
|
||||
dev_err(&adev->dev, "could not retrieve SSP/SPI bus clock\n");
|
||||
@ -2083,8 +2216,8 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
SSP_CR1(pl022->virtbase));
|
||||
load_ssp_default_config(pl022);
|
||||
|
||||
status = request_irq(adev->irq[0], pl022_interrupt_handler, 0, "pl022",
|
||||
pl022);
|
||||
status = devm_request_irq(dev, adev->irq[0], pl022_interrupt_handler,
|
||||
0, "pl022", pl022);
|
||||
if (status < 0) {
|
||||
dev_err(&adev->dev, "probe - cannot get IRQ (%d)\n", status);
|
||||
goto err_no_irq;
|
||||
@ -2124,22 +2257,18 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
err_spi_register:
|
||||
if (platform_info->enable_dma)
|
||||
pl022_dma_remove(pl022);
|
||||
|
||||
free_irq(adev->irq[0], pl022);
|
||||
err_no_irq:
|
||||
clk_disable(pl022->clk);
|
||||
err_no_clk_en:
|
||||
clk_unprepare(pl022->clk);
|
||||
err_clk_prep:
|
||||
clk_put(pl022->clk);
|
||||
err_no_clk:
|
||||
iounmap(pl022->virtbase);
|
||||
err_no_ioremap:
|
||||
amba_release_regions(adev);
|
||||
err_no_ioregion:
|
||||
err_no_gpio:
|
||||
err_no_pinctrl:
|
||||
spi_master_put(master);
|
||||
err_no_master:
|
||||
err_no_pdata:
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -2161,20 +2290,55 @@ pl022_remove(struct amba_device *adev)
|
||||
if (pl022->master_info->enable_dma)
|
||||
pl022_dma_remove(pl022);
|
||||
|
||||
free_irq(adev->irq[0], pl022);
|
||||
clk_disable(pl022->clk);
|
||||
clk_unprepare(pl022->clk);
|
||||
clk_put(pl022->clk);
|
||||
pm_runtime_disable(&adev->dev);
|
||||
iounmap(pl022->virtbase);
|
||||
amba_release_regions(adev);
|
||||
tasklet_disable(&pl022->pump_transfers);
|
||||
spi_unregister_master(pl022->master);
|
||||
spi_master_put(pl022->master);
|
||||
amba_set_drvdata(adev, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_SUSPEND) || defined(CONFIG_PM_RUNTIME)
|
||||
/*
|
||||
* These two functions are used from both suspend/resume and
|
||||
* the runtime counterparts to handle external resources like
|
||||
* clocks, pins and regulators when going to sleep.
|
||||
*/
|
||||
static void pl022_suspend_resources(struct pl022 *pl022)
|
||||
{
|
||||
int ret;
|
||||
|
||||
clk_disable(pl022->clk);
|
||||
|
||||
/* Optionally let pins go into sleep states */
|
||||
if (!IS_ERR(pl022->pins_sleep)) {
|
||||
ret = pinctrl_select_state(pl022->pinctrl,
|
||||
pl022->pins_sleep);
|
||||
if (ret)
|
||||
dev_err(&pl022->adev->dev,
|
||||
"could not set pins to sleep state\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void pl022_resume_resources(struct pl022 *pl022)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Optionaly enable pins to be muxed in and configured */
|
||||
if (!IS_ERR(pl022->pins_default)) {
|
||||
ret = pinctrl_select_state(pl022->pinctrl,
|
||||
pl022->pins_default);
|
||||
if (ret)
|
||||
dev_err(&pl022->adev->dev,
|
||||
"could not set default pins\n");
|
||||
}
|
||||
|
||||
clk_enable(pl022->clk);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SUSPEND
|
||||
static int pl022_suspend(struct device *dev)
|
||||
{
|
||||
@ -2186,6 +2350,7 @@ static int pl022_suspend(struct device *dev)
|
||||
dev_warn(dev, "cannot suspend master\n");
|
||||
return ret;
|
||||
}
|
||||
pl022_suspend_resources(pl022);
|
||||
|
||||
dev_dbg(dev, "suspended\n");
|
||||
return 0;
|
||||
@ -2196,6 +2361,8 @@ static int pl022_resume(struct device *dev)
|
||||
struct pl022 *pl022 = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
pl022_resume_resources(pl022);
|
||||
|
||||
/* Start the queue running */
|
||||
ret = spi_master_resume(pl022->master);
|
||||
if (ret)
|
||||
@ -2212,8 +2379,7 @@ static int pl022_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct pl022 *pl022 = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable(pl022->clk);
|
||||
|
||||
pl022_suspend_resources(pl022);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2221,8 +2387,7 @@ static int pl022_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct pl022 *pl022 = dev_get_drvdata(dev);
|
||||
|
||||
clk_enable(pl022->clk);
|
||||
|
||||
pl022_resume_resources(pl022);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
@ -611,6 +611,7 @@ static int __devinit s3c24xx_spi_probe(struct platform_device *pdev)
|
||||
if (!pdata->set_cs) {
|
||||
if (pdata->pin_cs < 0) {
|
||||
dev_err(&pdev->dev, "No chipselect pin\n");
|
||||
err = -EINVAL;
|
||||
goto err_register;
|
||||
}
|
||||
|
||||
|
@ -976,7 +976,8 @@ err_msgq:
|
||||
spi_set_ctldata(spi, NULL);
|
||||
|
||||
err_gpio_req:
|
||||
kfree(cs);
|
||||
if (spi->dev.of_node)
|
||||
kfree(cs);
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -1409,7 +1410,7 @@ static int s3c64xx_spi_remove(struct platform_device *pdev)
|
||||
#ifdef CONFIG_PM
|
||||
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);
|
||||
|
||||
spi_master_suspend(master);
|
||||
@ -1428,7 +1429,7 @@ static int s3c64xx_spi_suspend(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_info *sci = sdd->cntrlr_info;
|
||||
|
||||
@ -1452,7 +1453,7 @@ static int s3c64xx_spi_resume(struct device *dev)
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
static int s3c64xx_spi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
|
||||
|
||||
clk_disable(sdd->clk);
|
||||
@ -1463,7 +1464,7 @@ static int s3c64xx_spi_runtime_suspend(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);
|
||||
|
||||
clk_enable(sdd->src_clk);
|
||||
|
364
drivers/spi/spi-sc18is602.c
Normal file
364
drivers/spi/spi-sc18is602.c
Normal file
@ -0,0 +1,364 @@
|
||||
/*
|
||||
* 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)
|
||||
ret = spi_register_master(master);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "spi_register_master error.\n");
|
||||
goto error2;
|
||||
goto error1;
|
||||
}
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
@ -292,8 +292,6 @@ static int __devinit hspi_probe(struct platform_device *pdev)
|
||||
|
||||
return 0;
|
||||
|
||||
error2:
|
||||
devm_iounmap(hspi->dev, hspi->addr);
|
||||
error1:
|
||||
clk_put(clk);
|
||||
error0:
|
||||
@ -310,7 +308,6 @@ static int __devexit hspi_remove(struct platform_device *pdev)
|
||||
|
||||
clk_put(hspi->clk);
|
||||
spi_unregister_master(hspi->master);
|
||||
devm_iounmap(hspi->dev, hspi->addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -594,9 +594,7 @@ static int __devexit stmp_spi_remove(struct platform_device *dev)
|
||||
struct stmp_spi *ss;
|
||||
struct spi_master *master;
|
||||
|
||||
master = platform_get_drvdata(dev);
|
||||
if (master == NULL)
|
||||
goto out0;
|
||||
master = spi_master_get(platform_get_drvdata(dev));
|
||||
ss = spi_master_get_devdata(master);
|
||||
|
||||
spi_unregister_master(master);
|
||||
@ -609,8 +607,6 @@ static int __devexit stmp_spi_remove(struct platform_device *dev)
|
||||
destroy_workqueue(ss->workqueue);
|
||||
iounmap(ss->regs);
|
||||
spi_master_put(master);
|
||||
platform_set_drvdata(dev, NULL);
|
||||
out0:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1,647 +0,0 @@
|
||||
/*
|
||||
* Driver for Nvidia TEGRA spi controller.
|
||||
*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
*
|
||||
* Author:
|
||||
* Erik Gilling <konkers@android.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/dmaengine.h>
|
||||
|
||||
#include <mach/dma.h>
|
||||
|
||||
#define SLINK_COMMAND 0x000
|
||||
#define SLINK_BIT_LENGTH(x) (((x) & 0x1f) << 0)
|
||||
#define SLINK_WORD_SIZE(x) (((x) & 0x1f) << 5)
|
||||
#define SLINK_BOTH_EN (1 << 10)
|
||||
#define SLINK_CS_SW (1 << 11)
|
||||
#define SLINK_CS_VALUE (1 << 12)
|
||||
#define SLINK_CS_POLARITY (1 << 13)
|
||||
#define SLINK_IDLE_SDA_DRIVE_LOW (0 << 16)
|
||||
#define SLINK_IDLE_SDA_DRIVE_HIGH (1 << 16)
|
||||
#define SLINK_IDLE_SDA_PULL_LOW (2 << 16)
|
||||
#define SLINK_IDLE_SDA_PULL_HIGH (3 << 16)
|
||||
#define SLINK_IDLE_SDA_MASK (3 << 16)
|
||||
#define SLINK_CS_POLARITY1 (1 << 20)
|
||||
#define SLINK_CK_SDA (1 << 21)
|
||||
#define SLINK_CS_POLARITY2 (1 << 22)
|
||||
#define SLINK_CS_POLARITY3 (1 << 23)
|
||||
#define SLINK_IDLE_SCLK_DRIVE_LOW (0 << 24)
|
||||
#define SLINK_IDLE_SCLK_DRIVE_HIGH (1 << 24)
|
||||
#define SLINK_IDLE_SCLK_PULL_LOW (2 << 24)
|
||||
#define SLINK_IDLE_SCLK_PULL_HIGH (3 << 24)
|
||||
#define SLINK_IDLE_SCLK_MASK (3 << 24)
|
||||
#define SLINK_M_S (1 << 28)
|
||||
#define SLINK_WAIT (1 << 29)
|
||||
#define SLINK_GO (1 << 30)
|
||||
#define SLINK_ENB (1 << 31)
|
||||
|
||||
#define SLINK_COMMAND2 0x004
|
||||
#define SLINK_LSBFE (1 << 0)
|
||||
#define SLINK_SSOE (1 << 1)
|
||||
#define SLINK_SPIE (1 << 4)
|
||||
#define SLINK_BIDIROE (1 << 6)
|
||||
#define SLINK_MODFEN (1 << 7)
|
||||
#define SLINK_INT_SIZE(x) (((x) & 0x1f) << 8)
|
||||
#define SLINK_CS_ACTIVE_BETWEEN (1 << 17)
|
||||
#define SLINK_SS_EN_CS(x) (((x) & 0x3) << 18)
|
||||
#define SLINK_SS_SETUP(x) (((x) & 0x3) << 20)
|
||||
#define SLINK_FIFO_REFILLS_0 (0 << 22)
|
||||
#define SLINK_FIFO_REFILLS_1 (1 << 22)
|
||||
#define SLINK_FIFO_REFILLS_2 (2 << 22)
|
||||
#define SLINK_FIFO_REFILLS_3 (3 << 22)
|
||||
#define SLINK_FIFO_REFILLS_MASK (3 << 22)
|
||||
#define SLINK_WAIT_PACK_INT(x) (((x) & 0x7) << 26)
|
||||
#define SLINK_SPC0 (1 << 29)
|
||||
#define SLINK_TXEN (1 << 30)
|
||||
#define SLINK_RXEN (1 << 31)
|
||||
|
||||
#define SLINK_STATUS 0x008
|
||||
#define SLINK_COUNT(val) (((val) >> 0) & 0x1f)
|
||||
#define SLINK_WORD(val) (((val) >> 5) & 0x1f)
|
||||
#define SLINK_BLK_CNT(val) (((val) >> 0) & 0xffff)
|
||||
#define SLINK_MODF (1 << 16)
|
||||
#define SLINK_RX_UNF (1 << 18)
|
||||
#define SLINK_TX_OVF (1 << 19)
|
||||
#define SLINK_TX_FULL (1 << 20)
|
||||
#define SLINK_TX_EMPTY (1 << 21)
|
||||
#define SLINK_RX_FULL (1 << 22)
|
||||
#define SLINK_RX_EMPTY (1 << 23)
|
||||
#define SLINK_TX_UNF (1 << 24)
|
||||
#define SLINK_RX_OVF (1 << 25)
|
||||
#define SLINK_TX_FLUSH (1 << 26)
|
||||
#define SLINK_RX_FLUSH (1 << 27)
|
||||
#define SLINK_SCLK (1 << 28)
|
||||
#define SLINK_ERR (1 << 29)
|
||||
#define SLINK_RDY (1 << 30)
|
||||
#define SLINK_BSY (1 << 31)
|
||||
|
||||
#define SLINK_MAS_DATA 0x010
|
||||
#define SLINK_SLAVE_DATA 0x014
|
||||
|
||||
#define SLINK_DMA_CTL 0x018
|
||||
#define SLINK_DMA_BLOCK_SIZE(x) (((x) & 0xffff) << 0)
|
||||
#define SLINK_TX_TRIG_1 (0 << 16)
|
||||
#define SLINK_TX_TRIG_4 (1 << 16)
|
||||
#define SLINK_TX_TRIG_8 (2 << 16)
|
||||
#define SLINK_TX_TRIG_16 (3 << 16)
|
||||
#define SLINK_TX_TRIG_MASK (3 << 16)
|
||||
#define SLINK_RX_TRIG_1 (0 << 18)
|
||||
#define SLINK_RX_TRIG_4 (1 << 18)
|
||||
#define SLINK_RX_TRIG_8 (2 << 18)
|
||||
#define SLINK_RX_TRIG_16 (3 << 18)
|
||||
#define SLINK_RX_TRIG_MASK (3 << 18)
|
||||
#define SLINK_PACKED (1 << 20)
|
||||
#define SLINK_PACK_SIZE_4 (0 << 21)
|
||||
#define SLINK_PACK_SIZE_8 (1 << 21)
|
||||
#define SLINK_PACK_SIZE_16 (2 << 21)
|
||||
#define SLINK_PACK_SIZE_32 (3 << 21)
|
||||
#define SLINK_PACK_SIZE_MASK (3 << 21)
|
||||
#define SLINK_IE_TXC (1 << 26)
|
||||
#define SLINK_IE_RXC (1 << 27)
|
||||
#define SLINK_DMA_EN (1 << 31)
|
||||
|
||||
#define SLINK_STATUS2 0x01c
|
||||
#define SLINK_TX_FIFO_EMPTY_COUNT(val) (((val) & 0x3f) >> 0)
|
||||
#define SLINK_RX_FIFO_FULL_COUNT(val) (((val) & 0x3f) >> 16)
|
||||
|
||||
#define SLINK_TX_FIFO 0x100
|
||||
#define SLINK_RX_FIFO 0x180
|
||||
|
||||
static const unsigned long spi_tegra_req_sels[] = {
|
||||
TEGRA_DMA_REQ_SEL_SL2B1,
|
||||
TEGRA_DMA_REQ_SEL_SL2B2,
|
||||
TEGRA_DMA_REQ_SEL_SL2B3,
|
||||
TEGRA_DMA_REQ_SEL_SL2B4,
|
||||
};
|
||||
|
||||
#define BB_LEN 32
|
||||
|
||||
struct spi_tegra_data {
|
||||
struct spi_master *master;
|
||||
struct platform_device *pdev;
|
||||
spinlock_t lock;
|
||||
|
||||
struct clk *clk;
|
||||
void __iomem *base;
|
||||
unsigned long phys;
|
||||
|
||||
u32 cur_speed;
|
||||
|
||||
struct list_head queue;
|
||||
struct spi_transfer *cur;
|
||||
unsigned cur_pos;
|
||||
unsigned cur_len;
|
||||
unsigned cur_bytes_per_word;
|
||||
|
||||
/* The tegra spi controller has a bug which causes the first word
|
||||
* in PIO transactions to be garbage. Since packed DMA transactions
|
||||
* require transfers to be 4 byte aligned we need a bounce buffer
|
||||
* for the generic case.
|
||||
*/
|
||||
int dma_req_len;
|
||||
struct dma_chan *rx_dma;
|
||||
struct dma_slave_config sconfig;
|
||||
struct dma_async_tx_descriptor *rx_dma_desc;
|
||||
dma_cookie_t rx_cookie;
|
||||
u32 *rx_bb;
|
||||
dma_addr_t rx_bb_phys;
|
||||
};
|
||||
|
||||
static void tegra_spi_rx_dma_complete(void *args);
|
||||
static inline unsigned long spi_tegra_readl(struct spi_tegra_data *tspi,
|
||||
unsigned long reg)
|
||||
{
|
||||
return readl(tspi->base + reg);
|
||||
}
|
||||
|
||||
static inline void spi_tegra_writel(struct spi_tegra_data *tspi,
|
||||
unsigned long val,
|
||||
unsigned long reg)
|
||||
{
|
||||
writel(val, tspi->base + reg);
|
||||
}
|
||||
|
||||
static void spi_tegra_go(struct spi_tegra_data *tspi)
|
||||
{
|
||||
unsigned long val;
|
||||
|
||||
wmb();
|
||||
|
||||
val = spi_tegra_readl(tspi, SLINK_DMA_CTL);
|
||||
val &= ~SLINK_DMA_BLOCK_SIZE(~0) & ~SLINK_DMA_EN;
|
||||
val |= SLINK_DMA_BLOCK_SIZE(tspi->dma_req_len / 4 - 1);
|
||||
spi_tegra_writel(tspi, val, SLINK_DMA_CTL);
|
||||
tspi->rx_dma_desc = dmaengine_prep_slave_single(tspi->rx_dma,
|
||||
tspi->rx_bb_phys, tspi->dma_req_len,
|
||||
DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
|
||||
if (!tspi->rx_dma_desc) {
|
||||
dev_err(&tspi->pdev->dev, "dmaengine slave prep failed\n");
|
||||
return;
|
||||
}
|
||||
tspi->rx_dma_desc->callback = tegra_spi_rx_dma_complete;
|
||||
tspi->rx_dma_desc->callback_param = tspi;
|
||||
tspi->rx_cookie = dmaengine_submit(tspi->rx_dma_desc);
|
||||
dma_async_issue_pending(tspi->rx_dma);
|
||||
|
||||
val |= SLINK_DMA_EN;
|
||||
spi_tegra_writel(tspi, val, SLINK_DMA_CTL);
|
||||
}
|
||||
|
||||
static unsigned spi_tegra_fill_tx_fifo(struct spi_tegra_data *tspi,
|
||||
struct spi_transfer *t)
|
||||
{
|
||||
unsigned len = min(t->len - tspi->cur_pos, BB_LEN *
|
||||
tspi->cur_bytes_per_word);
|
||||
u8 *tx_buf = (u8 *)t->tx_buf + tspi->cur_pos;
|
||||
int i, j;
|
||||
unsigned long val;
|
||||
|
||||
val = spi_tegra_readl(tspi, SLINK_COMMAND);
|
||||
val &= ~SLINK_WORD_SIZE(~0);
|
||||
val |= SLINK_WORD_SIZE(len / tspi->cur_bytes_per_word - 1);
|
||||
spi_tegra_writel(tspi, val, SLINK_COMMAND);
|
||||
|
||||
for (i = 0; i < len; i += tspi->cur_bytes_per_word) {
|
||||
val = 0;
|
||||
for (j = 0; j < tspi->cur_bytes_per_word; j++)
|
||||
val |= tx_buf[i + j] << j * 8;
|
||||
|
||||
spi_tegra_writel(tspi, val, SLINK_TX_FIFO);
|
||||
}
|
||||
|
||||
tspi->dma_req_len = len / tspi->cur_bytes_per_word * 4;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static unsigned spi_tegra_drain_rx_fifo(struct spi_tegra_data *tspi,
|
||||
struct spi_transfer *t)
|
||||
{
|
||||
unsigned len = tspi->cur_len;
|
||||
u8 *rx_buf = (u8 *)t->rx_buf + tspi->cur_pos;
|
||||
int i, j;
|
||||
unsigned long val;
|
||||
|
||||
for (i = 0; i < len; i += tspi->cur_bytes_per_word) {
|
||||
val = tspi->rx_bb[i / tspi->cur_bytes_per_word];
|
||||
for (j = 0; j < tspi->cur_bytes_per_word; j++)
|
||||
rx_buf[i + j] = (val >> (j * 8)) & 0xff;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static void spi_tegra_start_transfer(struct spi_device *spi,
|
||||
struct spi_transfer *t)
|
||||
{
|
||||
struct spi_tegra_data *tspi = spi_master_get_devdata(spi->master);
|
||||
u32 speed;
|
||||
u8 bits_per_word;
|
||||
unsigned long val;
|
||||
|
||||
speed = t->speed_hz ? t->speed_hz : spi->max_speed_hz;
|
||||
bits_per_word = t->bits_per_word ? t->bits_per_word :
|
||||
spi->bits_per_word;
|
||||
|
||||
tspi->cur_bytes_per_word = (bits_per_word - 1) / 8 + 1;
|
||||
|
||||
if (speed != tspi->cur_speed)
|
||||
clk_set_rate(tspi->clk, speed);
|
||||
|
||||
if (tspi->cur_speed == 0)
|
||||
clk_prepare_enable(tspi->clk);
|
||||
|
||||
tspi->cur_speed = speed;
|
||||
|
||||
val = spi_tegra_readl(tspi, SLINK_COMMAND2);
|
||||
val &= ~SLINK_SS_EN_CS(~0) | SLINK_RXEN | SLINK_TXEN;
|
||||
if (t->rx_buf)
|
||||
val |= SLINK_RXEN;
|
||||
if (t->tx_buf)
|
||||
val |= SLINK_TXEN;
|
||||
val |= SLINK_SS_EN_CS(spi->chip_select);
|
||||
val |= SLINK_SPIE;
|
||||
spi_tegra_writel(tspi, val, SLINK_COMMAND2);
|
||||
|
||||
val = spi_tegra_readl(tspi, SLINK_COMMAND);
|
||||
val &= ~SLINK_BIT_LENGTH(~0);
|
||||
val |= SLINK_BIT_LENGTH(bits_per_word - 1);
|
||||
|
||||
/* FIXME: should probably control CS manually so that we can be sure
|
||||
* it does not go low between transfer and to support delay_usecs
|
||||
* correctly.
|
||||
*/
|
||||
val &= ~SLINK_IDLE_SCLK_MASK & ~SLINK_CK_SDA & ~SLINK_CS_SW;
|
||||
|
||||
if (spi->mode & SPI_CPHA)
|
||||
val |= SLINK_CK_SDA;
|
||||
|
||||
if (spi->mode & SPI_CPOL)
|
||||
val |= SLINK_IDLE_SCLK_DRIVE_HIGH;
|
||||
else
|
||||
val |= SLINK_IDLE_SCLK_DRIVE_LOW;
|
||||
|
||||
val |= SLINK_M_S;
|
||||
|
||||
spi_tegra_writel(tspi, val, SLINK_COMMAND);
|
||||
|
||||
spi_tegra_writel(tspi, SLINK_RX_FLUSH | SLINK_TX_FLUSH, SLINK_STATUS);
|
||||
|
||||
tspi->cur = t;
|
||||
tspi->cur_pos = 0;
|
||||
tspi->cur_len = spi_tegra_fill_tx_fifo(tspi, t);
|
||||
|
||||
spi_tegra_go(tspi);
|
||||
}
|
||||
|
||||
static void spi_tegra_start_message(struct spi_device *spi,
|
||||
struct spi_message *m)
|
||||
{
|
||||
struct spi_transfer *t;
|
||||
|
||||
m->actual_length = 0;
|
||||
m->status = 0;
|
||||
|
||||
t = list_first_entry(&m->transfers, struct spi_transfer, transfer_list);
|
||||
spi_tegra_start_transfer(spi, t);
|
||||
}
|
||||
|
||||
static void handle_spi_rx_dma_complete(struct spi_tegra_data *tspi)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct spi_message *m;
|
||||
struct spi_device *spi;
|
||||
int timeout = 0;
|
||||
unsigned long val;
|
||||
|
||||
/* the SPI controller may come back with both the BSY and RDY bits
|
||||
* set. In this case we need to wait for the BSY bit to clear so
|
||||
* that we are sure the DMA is finished. 1000 reads was empirically
|
||||
* determined to be long enough.
|
||||
*/
|
||||
while (timeout++ < 1000) {
|
||||
if (!(spi_tegra_readl(tspi, SLINK_STATUS) & SLINK_BSY))
|
||||
break;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&tspi->lock, flags);
|
||||
|
||||
val = spi_tegra_readl(tspi, SLINK_STATUS);
|
||||
val |= SLINK_RDY;
|
||||
spi_tegra_writel(tspi, val, SLINK_STATUS);
|
||||
|
||||
m = list_first_entry(&tspi->queue, struct spi_message, queue);
|
||||
|
||||
if (timeout >= 1000)
|
||||
m->status = -EIO;
|
||||
|
||||
spi = m->state;
|
||||
|
||||
tspi->cur_pos += spi_tegra_drain_rx_fifo(tspi, tspi->cur);
|
||||
m->actual_length += tspi->cur_pos;
|
||||
|
||||
if (tspi->cur_pos < tspi->cur->len) {
|
||||
tspi->cur_len = spi_tegra_fill_tx_fifo(tspi, tspi->cur);
|
||||
spi_tegra_go(tspi);
|
||||
} else if (!list_is_last(&tspi->cur->transfer_list,
|
||||
&m->transfers)) {
|
||||
tspi->cur = list_first_entry(&tspi->cur->transfer_list,
|
||||
struct spi_transfer,
|
||||
transfer_list);
|
||||
spi_tegra_start_transfer(spi, tspi->cur);
|
||||
} else {
|
||||
list_del(&m->queue);
|
||||
|
||||
m->complete(m->context);
|
||||
|
||||
if (!list_empty(&tspi->queue)) {
|
||||
m = list_first_entry(&tspi->queue, struct spi_message,
|
||||
queue);
|
||||
spi = m->state;
|
||||
spi_tegra_start_message(spi, m);
|
||||
} else {
|
||||
clk_disable_unprepare(tspi->clk);
|
||||
tspi->cur_speed = 0;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&tspi->lock, flags);
|
||||
}
|
||||
|
||||
static void tegra_spi_rx_dma_complete(void *args)
|
||||
{
|
||||
struct spi_tegra_data *tspi = args;
|
||||
handle_spi_rx_dma_complete(tspi);
|
||||
}
|
||||
|
||||
static int spi_tegra_setup(struct spi_device *spi)
|
||||
{
|
||||
struct spi_tegra_data *tspi = spi_master_get_devdata(spi->master);
|
||||
unsigned long cs_bit;
|
||||
unsigned long val;
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(&spi->dev, "setup %d bpw, %scpol, %scpha, %dHz\n",
|
||||
spi->bits_per_word,
|
||||
spi->mode & SPI_CPOL ? "" : "~",
|
||||
spi->mode & SPI_CPHA ? "" : "~",
|
||||
spi->max_speed_hz);
|
||||
|
||||
|
||||
switch (spi->chip_select) {
|
||||
case 0:
|
||||
cs_bit = SLINK_CS_POLARITY;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
cs_bit = SLINK_CS_POLARITY1;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
cs_bit = SLINK_CS_POLARITY2;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
cs_bit = SLINK_CS_POLARITY3;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&tspi->lock, flags);
|
||||
|
||||
val = spi_tegra_readl(tspi, SLINK_COMMAND);
|
||||
if (spi->mode & SPI_CS_HIGH)
|
||||
val |= cs_bit;
|
||||
else
|
||||
val &= ~cs_bit;
|
||||
spi_tegra_writel(tspi, val, SLINK_COMMAND);
|
||||
|
||||
spin_unlock_irqrestore(&tspi->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_tegra_transfer(struct spi_device *spi, struct spi_message *m)
|
||||
{
|
||||
struct spi_tegra_data *tspi = spi_master_get_devdata(spi->master);
|
||||
struct spi_transfer *t;
|
||||
unsigned long flags;
|
||||
int was_empty;
|
||||
|
||||
if (list_empty(&m->transfers) || !m->complete)
|
||||
return -EINVAL;
|
||||
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
if (t->bits_per_word < 0 || t->bits_per_word > 32)
|
||||
return -EINVAL;
|
||||
|
||||
if (t->len == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!t->rx_buf && !t->tx_buf)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
m->state = spi;
|
||||
|
||||
spin_lock_irqsave(&tspi->lock, flags);
|
||||
was_empty = list_empty(&tspi->queue);
|
||||
list_add_tail(&m->queue, &tspi->queue);
|
||||
|
||||
if (was_empty)
|
||||
spi_tegra_start_message(spi, m);
|
||||
|
||||
spin_unlock_irqrestore(&tspi->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit spi_tegra_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master;
|
||||
struct spi_tegra_data *tspi;
|
||||
struct resource *r;
|
||||
int ret;
|
||||
dma_cap_mask_t mask;
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof *tspi);
|
||||
if (master == NULL) {
|
||||
dev_err(&pdev->dev, "master allocation failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* the spi->mode bits understood by this driver: */
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
||||
|
||||
master->bus_num = pdev->id;
|
||||
|
||||
master->setup = spi_tegra_setup;
|
||||
master->transfer = spi_tegra_transfer;
|
||||
master->num_chipselect = 4;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, master);
|
||||
tspi = spi_master_get_devdata(master);
|
||||
tspi->master = master;
|
||||
tspi->pdev = pdev;
|
||||
spin_lock_init(&tspi->lock);
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (r == NULL) {
|
||||
ret = -ENODEV;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
if (!request_mem_region(r->start, resource_size(r),
|
||||
dev_name(&pdev->dev))) {
|
||||
ret = -EBUSY;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
tspi->phys = r->start;
|
||||
tspi->base = ioremap(r->start, resource_size(r));
|
||||
if (!tspi->base) {
|
||||
dev_err(&pdev->dev, "can't ioremap iomem\n");
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
tspi->clk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(tspi->clk)) {
|
||||
dev_err(&pdev->dev, "can not get clock\n");
|
||||
ret = PTR_ERR(tspi->clk);
|
||||
goto err2;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&tspi->queue);
|
||||
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
tspi->rx_dma = dma_request_channel(mask, NULL, NULL);
|
||||
if (!tspi->rx_dma) {
|
||||
dev_err(&pdev->dev, "can not allocate rx dma channel\n");
|
||||
ret = -ENODEV;
|
||||
goto err3;
|
||||
}
|
||||
|
||||
tspi->rx_bb = dma_alloc_coherent(&pdev->dev, sizeof(u32) * BB_LEN,
|
||||
&tspi->rx_bb_phys, GFP_KERNEL);
|
||||
if (!tspi->rx_bb) {
|
||||
dev_err(&pdev->dev, "can not allocate rx bounce buffer\n");
|
||||
ret = -ENOMEM;
|
||||
goto err4;
|
||||
}
|
||||
|
||||
/* Dmaengine Dma slave config */
|
||||
tspi->sconfig.src_addr = tspi->phys + SLINK_RX_FIFO;
|
||||
tspi->sconfig.dst_addr = tspi->phys + SLINK_RX_FIFO;
|
||||
tspi->sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
tspi->sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
tspi->sconfig.slave_id = spi_tegra_req_sels[pdev->id];
|
||||
tspi->sconfig.src_maxburst = 1;
|
||||
tspi->sconfig.dst_maxburst = 1;
|
||||
ret = dmaengine_device_control(tspi->rx_dma,
|
||||
DMA_SLAVE_CONFIG, (unsigned long) &tspi->sconfig);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "can not do slave configure for dma %d\n",
|
||||
ret);
|
||||
goto err4;
|
||||
}
|
||||
|
||||
master->dev.of_node = pdev->dev.of_node;
|
||||
ret = spi_register_master(master);
|
||||
|
||||
if (ret < 0)
|
||||
goto err5;
|
||||
|
||||
return ret;
|
||||
|
||||
err5:
|
||||
dma_free_coherent(&pdev->dev, sizeof(u32) * BB_LEN,
|
||||
tspi->rx_bb, tspi->rx_bb_phys);
|
||||
err4:
|
||||
dma_release_channel(tspi->rx_dma);
|
||||
err3:
|
||||
clk_put(tspi->clk);
|
||||
err2:
|
||||
iounmap(tspi->base);
|
||||
err1:
|
||||
release_mem_region(r->start, resource_size(r));
|
||||
err0:
|
||||
spi_master_put(master);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit spi_tegra_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master;
|
||||
struct spi_tegra_data *tspi;
|
||||
struct resource *r;
|
||||
|
||||
master = dev_get_drvdata(&pdev->dev);
|
||||
tspi = spi_master_get_devdata(master);
|
||||
|
||||
spi_unregister_master(master);
|
||||
dma_release_channel(tspi->rx_dma);
|
||||
dma_free_coherent(&pdev->dev, sizeof(u32) * BB_LEN,
|
||||
tspi->rx_bb, tspi->rx_bb_phys);
|
||||
|
||||
clk_put(tspi->clk);
|
||||
iounmap(tspi->base);
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
release_mem_region(r->start, resource_size(r));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_ALIAS("platform:spi_tegra");
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id spi_tegra_of_match_table[] __devinitdata = {
|
||||
{ .compatible = "nvidia,tegra20-spi", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, spi_tegra_of_match_table);
|
||||
#else /* CONFIG_OF */
|
||||
#define spi_tegra_of_match_table NULL
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
static struct platform_driver spi_tegra_driver = {
|
||||
.driver = {
|
||||
.name = "spi_tegra",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = spi_tegra_of_match_table,
|
||||
},
|
||||
.probe = spi_tegra_probe,
|
||||
.remove = __devexit_p(spi_tegra_remove),
|
||||
};
|
||||
module_platform_driver(spi_tegra_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
@ -316,18 +316,7 @@ static struct spi_driver tle62x0_driver = {
|
||||
.remove = __devexit_p(tle62x0_remove),
|
||||
};
|
||||
|
||||
static __init int tle62x0_init(void)
|
||||
{
|
||||
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_spi_driver(tle62x0_driver);
|
||||
|
||||
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
|
||||
MODULE_DESCRIPTION("TLE62x0 SPI driver");
|
||||
|
@ -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);
|
||||
spi_unregister_master(data->master);
|
||||
spi_master_put(data->master);
|
||||
platform_set_drvdata(plat_dev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -244,6 +244,7 @@ struct dma_chan;
|
||||
* 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.
|
||||
* @chipselects: list of <num_chipselects> chip select gpios
|
||||
*/
|
||||
struct pl022_ssp_controller {
|
||||
u16 bus_id;
|
||||
@ -254,6 +255,7 @@ struct pl022_ssp_controller {
|
||||
void *dma_tx_param;
|
||||
int autosuspend_delay;
|
||||
bool rt;
|
||||
int *chipselects;
|
||||
};
|
||||
|
||||
/**
|
||||
|
19
include/linux/platform_data/sc18is602.h
Normal file
19
include/linux/platform_data/sc18is602.h
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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;
|
||||
};
|
150
include/linux/spi/mxs-spi.h
Normal file
150
include/linux/spi/mxs-spi.h
Normal file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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__ */
|
Loading…
Reference in New Issue
Block a user