Merge branches 'spi-drivers' and 'spi-mxs' into spi-next
This commit is contained in:
commit
f4b81dd83e
|
@ -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
|
chip selects. Individual drivers can define additional properties to
|
||||||
support describing the chip select layout.
|
support describing the chip select layout.
|
||||||
|
|
||||||
|
Optional property:
|
||||||
|
- num-cs : total number of chipselects
|
||||||
|
|
||||||
SPI slave nodes must be children of the SPI master node and can
|
SPI slave nodes must be children of the SPI master node and can
|
||||||
contain the following properties.
|
contain the following properties.
|
||||||
- reg - (required) chip select address of device.
|
- reg - (required) chip select address of device.
|
||||||
|
|
|
@ -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 */
|
||||||
|
};
|
||||||
|
|
|
@ -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
|
- interrupts : Should contain SPI controller interrupt
|
||||||
|
|
||||||
Optional properties:
|
Optional properties:
|
||||||
|
- num-cs : total number of chipselects
|
||||||
- cs-gpios : should specify GPIOs used for chipselects.
|
- cs-gpios : should specify GPIOs used for chipselects.
|
||||||
The gpios will be referred to as reg = <index> in the SPI child nodes.
|
The gpios will be referred to as reg = <index> in the SPI child nodes.
|
||||||
If unspecified, a single SPI device without a chip select can be used.
|
If unspecified, a single SPI device without a chip select can be used.
|
||||||
|
- pl022,autosuspend-delay : delay in ms following transfer completion before
|
||||||
|
the runtime power management system suspends the
|
||||||
|
device. A setting of 0 indicates no delay and the
|
||||||
|
device will be suspended immediately
|
||||||
|
- pl022,rt : indicates the controller should run the message pump with realtime
|
||||||
|
priority to minimise the transfer latency on the bus (boolean)
|
||||||
|
|
||||||
|
|
||||||
|
SPI slave nodes must be children of the SPI master node and can
|
||||||
|
contain the following properties.
|
||||||
|
|
||||||
|
- pl022,interface : interface type:
|
||||||
|
0: SPI
|
||||||
|
1: Texas Instruments Synchronous Serial Frame Format
|
||||||
|
2: Microwire (Half Duplex)
|
||||||
|
- pl022,com-mode : polling, interrupt or dma
|
||||||
|
- pl022,rx-level-trig : Rx FIFO watermark level
|
||||||
|
- pl022,tx-level-trig : Tx FIFO watermark level
|
||||||
|
- pl022,ctrl-len : Microwire interface: Control length
|
||||||
|
- pl022,wait-state : Microwire interface: Wait state
|
||||||
|
- pl022,duplex : Microwire interface: Full/Half duplex
|
||||||
|
|
||||||
|
|
|
@ -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.
|
|
@ -1604,9 +1604,6 @@ static struct u300_mux_hog u300_mux_hogs[] = {
|
||||||
{
|
{
|
||||||
.dev = &uart0_device.dev,
|
.dev = &uart0_device.dev,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
.dev = &pl022_device.dev,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
.dev = &mmcsd_device.dev,
|
.dev = &mmcsd_device.dev,
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# Makefile for mxs specific clk
|
# Makefile for mxs specific clk
|
||||||
#
|
#
|
||||||
|
|
||||||
obj-y += clk.o clk-pll.o clk-ref.o clk-div.o clk-frac.o
|
obj-y += clk.o clk-pll.o clk-ref.o clk-div.o clk-frac.o clk-ssp.o
|
||||||
|
|
||||||
obj-$(CONFIG_SOC_IMX23) += clk-imx23.o
|
obj-$(CONFIG_SOC_IMX23) += clk-imx23.o
|
||||||
obj-$(CONFIG_SOC_IMX28) += clk-imx28.o
|
obj-$(CONFIG_SOC_IMX28) += clk-imx28.o
|
||||||
|
|
|
@ -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/gpio.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/fsl/mxs-dma.h>
|
|
||||||
#include <linux/pinctrl/consumer.h>
|
#include <linux/pinctrl/consumer.h>
|
||||||
#include <linux/stmp_device.h>
|
#include <linux/stmp_device.h>
|
||||||
#include <linux/mmc/mxs-mmc.h>
|
#include <linux/mmc/mxs-mmc.h>
|
||||||
|
#include <linux/spi/mxs-spi.h>
|
||||||
|
|
||||||
#define DRIVER_NAME "mxs-mmc"
|
#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 | \
|
#define MXS_MMC_IRQ_BITS (BM_SSP_CTRL1_SDIO_IRQ | \
|
||||||
BM_SSP_CTRL1_RESP_ERR_IRQ | \
|
BM_SSP_CTRL1_RESP_ERR_IRQ | \
|
||||||
BM_SSP_CTRL1_RESP_TIMEOUT_IRQ | \
|
BM_SSP_CTRL1_RESP_TIMEOUT_IRQ | \
|
||||||
|
@ -135,31 +57,17 @@
|
||||||
BM_SSP_CTRL1_RECV_TIMEOUT_IRQ | \
|
BM_SSP_CTRL1_RECV_TIMEOUT_IRQ | \
|
||||||
BM_SSP_CTRL1_FIFO_OVERRUN_IRQ)
|
BM_SSP_CTRL1_FIFO_OVERRUN_IRQ)
|
||||||
|
|
||||||
#define SSP_PIO_NUM 3
|
/* card detect polling timeout */
|
||||||
|
#define MXS_MMC_DETECT_TIMEOUT (HZ/2)
|
||||||
enum mxs_mmc_id {
|
|
||||||
IMX23_MMC,
|
|
||||||
IMX28_MMC,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mxs_mmc_host {
|
struct mxs_mmc_host {
|
||||||
|
struct mxs_ssp ssp;
|
||||||
|
|
||||||
struct mmc_host *mmc;
|
struct mmc_host *mmc;
|
||||||
struct mmc_request *mrq;
|
struct mmc_request *mrq;
|
||||||
struct mmc_command *cmd;
|
struct mmc_command *cmd;
|
||||||
struct mmc_data *data;
|
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;
|
unsigned char bus_width;
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
int sdio_irq_en;
|
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)
|
static int mxs_mmc_get_cd(struct mmc_host *mmc)
|
||||||
{
|
{
|
||||||
struct mxs_mmc_host *host = mmc_priv(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);
|
BM_SSP_STATUS_CARD_DETECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mxs_mmc_reset(struct mxs_mmc_host *host)
|
static void mxs_mmc_reset(struct mxs_mmc_host *host)
|
||||||
{
|
{
|
||||||
|
struct mxs_ssp *ssp = &host->ssp;
|
||||||
u32 ctrl0, ctrl1;
|
u32 ctrl0, ctrl1;
|
||||||
|
|
||||||
stmp_reset_block(host->base);
|
stmp_reset_block(ssp->base);
|
||||||
|
|
||||||
ctrl0 = BM_SSP_CTRL0_IGNORE_CRC;
|
ctrl0 = BM_SSP_CTRL0_IGNORE_CRC;
|
||||||
ctrl1 = BF_SSP(0x3, CTRL1_SSP_MODE) |
|
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) |
|
writel(BF_SSP(0xffff, TIMING_TIMEOUT) |
|
||||||
BF_SSP(2, TIMING_CLOCK_DIVIDE) |
|
BF_SSP(2, TIMING_CLOCK_DIVIDE) |
|
||||||
BF_SSP(0, TIMING_CLOCK_RATE),
|
BF_SSP(0, TIMING_CLOCK_RATE),
|
||||||
host->base + HW_SSP_TIMING(host));
|
ssp->base + HW_SSP_TIMING(ssp));
|
||||||
|
|
||||||
if (host->sdio_irq_en) {
|
if (host->sdio_irq_en) {
|
||||||
ctrl0 |= BM_SSP_CTRL0_SDIO_IRQ_CHECK;
|
ctrl0 |= BM_SSP_CTRL0_SDIO_IRQ_CHECK;
|
||||||
ctrl1 |= BM_SSP_CTRL1_SDIO_IRQ_EN;
|
ctrl1 |= BM_SSP_CTRL1_SDIO_IRQ_EN;
|
||||||
}
|
}
|
||||||
|
|
||||||
writel(ctrl0, host->base + HW_SSP_CTRL0);
|
writel(ctrl0, ssp->base + HW_SSP_CTRL0);
|
||||||
writel(ctrl1, host->base + HW_SSP_CTRL1(host));
|
writel(ctrl1, ssp->base + HW_SSP_CTRL1(ssp));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mxs_mmc_start_cmd(struct mxs_mmc_host *host,
|
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_command *cmd = host->cmd;
|
||||||
struct mmc_data *data = host->data;
|
struct mmc_data *data = host->data;
|
||||||
struct mmc_request *mrq = host->mrq;
|
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_PRESENT) {
|
||||||
if (mmc_resp_type(cmd) & MMC_RSP_136) {
|
if (mmc_resp_type(cmd) & MMC_RSP_136) {
|
||||||
cmd->resp[3] = readl(host->base + HW_SSP_SDRESP0(host));
|
cmd->resp[3] = readl(ssp->base + HW_SSP_SDRESP0(ssp));
|
||||||
cmd->resp[2] = readl(host->base + HW_SSP_SDRESP1(host));
|
cmd->resp[2] = readl(ssp->base + HW_SSP_SDRESP1(ssp));
|
||||||
cmd->resp[1] = readl(host->base + HW_SSP_SDRESP2(host));
|
cmd->resp[1] = readl(ssp->base + HW_SSP_SDRESP2(ssp));
|
||||||
cmd->resp[0] = readl(host->base + HW_SSP_SDRESP3(host));
|
cmd->resp[0] = readl(ssp->base + HW_SSP_SDRESP3(ssp));
|
||||||
} else {
|
} else {
|
||||||
cmd->resp[0] = readl(host->base + HW_SSP_SDRESP0(host));
|
cmd->resp[0] = readl(ssp->base + HW_SSP_SDRESP0(ssp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
dma_unmap_sg(mmc_dev(host->mmc), data->sg,
|
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
|
* If there was an error on any block, we mark all
|
||||||
* data blocks as being in error.
|
* 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 mxs_mmc_host *host = dev_id;
|
||||||
struct mmc_command *cmd = host->cmd;
|
struct mmc_command *cmd = host->cmd;
|
||||||
struct mmc_data *data = host->data;
|
struct mmc_data *data = host->data;
|
||||||
|
struct mxs_ssp *ssp = &host->ssp;
|
||||||
u32 stat;
|
u32 stat;
|
||||||
|
|
||||||
spin_lock(&host->lock);
|
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,
|
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);
|
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(
|
static struct dma_async_tx_descriptor *mxs_mmc_prep_dma(
|
||||||
struct mxs_mmc_host *host, unsigned long flags)
|
struct mxs_mmc_host *host, unsigned long flags)
|
||||||
{
|
{
|
||||||
|
struct mxs_ssp *ssp = &host->ssp;
|
||||||
struct dma_async_tx_descriptor *desc;
|
struct dma_async_tx_descriptor *desc;
|
||||||
struct mmc_data *data = host->data;
|
struct mmc_data *data = host->data;
|
||||||
struct scatterlist * sgl;
|
struct scatterlist * sgl;
|
||||||
|
@ -320,24 +233,24 @@ static struct dma_async_tx_descriptor *mxs_mmc_prep_dma(
|
||||||
if (data) {
|
if (data) {
|
||||||
/* data */
|
/* data */
|
||||||
dma_map_sg(mmc_dev(host->mmc), data->sg,
|
dma_map_sg(mmc_dev(host->mmc), data->sg,
|
||||||
data->sg_len, host->dma_dir);
|
data->sg_len, ssp->dma_dir);
|
||||||
sgl = data->sg;
|
sgl = data->sg;
|
||||||
sg_len = data->sg_len;
|
sg_len = data->sg_len;
|
||||||
} else {
|
} else {
|
||||||
/* pio */
|
/* pio */
|
||||||
sgl = (struct scatterlist *) host->ssp_pio_words;
|
sgl = (struct scatterlist *) ssp->ssp_pio_words;
|
||||||
sg_len = SSP_PIO_NUM;
|
sg_len = SSP_PIO_NUM;
|
||||||
}
|
}
|
||||||
|
|
||||||
desc = dmaengine_prep_slave_sg(host->dmach,
|
desc = dmaengine_prep_slave_sg(ssp->dmach,
|
||||||
sgl, sg_len, host->slave_dirn, flags);
|
sgl, sg_len, ssp->slave_dirn, flags);
|
||||||
if (desc) {
|
if (desc) {
|
||||||
desc->callback = mxs_mmc_dma_irq_callback;
|
desc->callback = mxs_mmc_dma_irq_callback;
|
||||||
desc->callback_param = host;
|
desc->callback_param = host;
|
||||||
} else {
|
} else {
|
||||||
if (data)
|
if (data)
|
||||||
dma_unmap_sg(mmc_dev(host->mmc), data->sg,
|
dma_unmap_sg(mmc_dev(host->mmc), data->sg,
|
||||||
data->sg_len, host->dma_dir);
|
data->sg_len, ssp->dma_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
return desc;
|
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)
|
static void mxs_mmc_bc(struct mxs_mmc_host *host)
|
||||||
{
|
{
|
||||||
|
struct mxs_ssp *ssp = &host->ssp;
|
||||||
struct mmc_command *cmd = host->cmd;
|
struct mmc_command *cmd = host->cmd;
|
||||||
struct dma_async_tx_descriptor *desc;
|
struct dma_async_tx_descriptor *desc;
|
||||||
u32 ctrl0, cmd0, cmd1;
|
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;
|
cmd0 |= BM_SSP_CMD0_CONT_CLKING_EN | BM_SSP_CMD0_SLOW_CLKING_EN;
|
||||||
}
|
}
|
||||||
|
|
||||||
host->ssp_pio_words[0] = ctrl0;
|
ssp->ssp_pio_words[0] = ctrl0;
|
||||||
host->ssp_pio_words[1] = cmd0;
|
ssp->ssp_pio_words[1] = cmd0;
|
||||||
host->ssp_pio_words[2] = cmd1;
|
ssp->ssp_pio_words[2] = cmd1;
|
||||||
host->dma_dir = DMA_NONE;
|
ssp->dma_dir = DMA_NONE;
|
||||||
host->slave_dirn = DMA_TRANS_NONE;
|
ssp->slave_dirn = DMA_TRANS_NONE;
|
||||||
desc = mxs_mmc_prep_dma(host, DMA_CTRL_ACK);
|
desc = mxs_mmc_prep_dma(host, DMA_CTRL_ACK);
|
||||||
if (!desc)
|
if (!desc)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
dmaengine_submit(desc);
|
dmaengine_submit(desc);
|
||||||
dma_async_issue_pending(host->dmach);
|
dma_async_issue_pending(ssp->dmach);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
@ -378,6 +292,7 @@ out:
|
||||||
|
|
||||||
static void mxs_mmc_ac(struct mxs_mmc_host *host)
|
static void mxs_mmc_ac(struct mxs_mmc_host *host)
|
||||||
{
|
{
|
||||||
|
struct mxs_ssp *ssp = &host->ssp;
|
||||||
struct mmc_command *cmd = host->cmd;
|
struct mmc_command *cmd = host->cmd;
|
||||||
struct dma_async_tx_descriptor *desc;
|
struct dma_async_tx_descriptor *desc;
|
||||||
u32 ignore_crc, get_resp, long_resp;
|
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;
|
cmd0 |= BM_SSP_CMD0_CONT_CLKING_EN | BM_SSP_CMD0_SLOW_CLKING_EN;
|
||||||
}
|
}
|
||||||
|
|
||||||
host->ssp_pio_words[0] = ctrl0;
|
ssp->ssp_pio_words[0] = ctrl0;
|
||||||
host->ssp_pio_words[1] = cmd0;
|
ssp->ssp_pio_words[1] = cmd0;
|
||||||
host->ssp_pio_words[2] = cmd1;
|
ssp->ssp_pio_words[2] = cmd1;
|
||||||
host->dma_dir = DMA_NONE;
|
ssp->dma_dir = DMA_NONE;
|
||||||
host->slave_dirn = DMA_TRANS_NONE;
|
ssp->slave_dirn = DMA_TRANS_NONE;
|
||||||
desc = mxs_mmc_prep_dma(host, DMA_CTRL_ACK);
|
desc = mxs_mmc_prep_dma(host, DMA_CTRL_ACK);
|
||||||
if (!desc)
|
if (!desc)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
dmaengine_submit(desc);
|
dmaengine_submit(desc);
|
||||||
dma_async_issue_pending(host->dmach);
|
dma_async_issue_pending(ssp->dmach);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
@ -447,6 +362,8 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
|
||||||
unsigned int data_size = 0, log2_blksz;
|
unsigned int data_size = 0, log2_blksz;
|
||||||
unsigned int blocks = data->blocks;
|
unsigned int blocks = data->blocks;
|
||||||
|
|
||||||
|
struct mxs_ssp *ssp = &host->ssp;
|
||||||
|
|
||||||
u32 ignore_crc, get_resp, long_resp, read;
|
u32 ignore_crc, get_resp, long_resp, read;
|
||||||
u32 ctrl0, cmd0, cmd1, val;
|
u32 ctrl0, cmd0, cmd1, val;
|
||||||
|
|
||||||
|
@ -489,15 +406,15 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
|
||||||
blocks = 1;
|
blocks = 1;
|
||||||
|
|
||||||
/* xfer count, block size and count need to be set differently */
|
/* 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);
|
ctrl0 |= BF_SSP(data_size, CTRL0_XFER_COUNT);
|
||||||
cmd0 |= BF_SSP(log2_blksz, CMD0_BLOCK_SIZE) |
|
cmd0 |= BF_SSP(log2_blksz, CMD0_BLOCK_SIZE) |
|
||||||
BF_SSP(blocks - 1, CMD0_BLOCK_COUNT);
|
BF_SSP(blocks - 1, CMD0_BLOCK_COUNT);
|
||||||
} else {
|
} 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) |
|
writel(BF_SSP(log2_blksz, BLOCK_SIZE_BLOCK_SIZE) |
|
||||||
BF_SSP(blocks - 1, BLOCK_SIZE_BLOCK_COUNT),
|
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) ||
|
if ((cmd->opcode == MMC_STOP_TRANSMISSION) ||
|
||||||
|
@ -512,18 +429,18 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set the timeout count */
|
/* set the timeout count */
|
||||||
timeout = mxs_ns_to_ssp_ticks(host->clk_rate, data->timeout_ns);
|
timeout = mxs_ns_to_ssp_ticks(ssp->clk_rate, data->timeout_ns);
|
||||||
val = readl(host->base + HW_SSP_TIMING(host));
|
val = readl(ssp->base + HW_SSP_TIMING(ssp));
|
||||||
val &= ~(BM_SSP_TIMING_TIMEOUT);
|
val &= ~(BM_SSP_TIMING_TIMEOUT);
|
||||||
val |= BF_SSP(timeout, 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 */
|
/* pio */
|
||||||
host->ssp_pio_words[0] = ctrl0;
|
ssp->ssp_pio_words[0] = ctrl0;
|
||||||
host->ssp_pio_words[1] = cmd0;
|
ssp->ssp_pio_words[1] = cmd0;
|
||||||
host->ssp_pio_words[2] = cmd1;
|
ssp->ssp_pio_words[2] = cmd1;
|
||||||
host->dma_dir = DMA_NONE;
|
ssp->dma_dir = DMA_NONE;
|
||||||
host->slave_dirn = DMA_TRANS_NONE;
|
ssp->slave_dirn = DMA_TRANS_NONE;
|
||||||
desc = mxs_mmc_prep_dma(host, 0);
|
desc = mxs_mmc_prep_dma(host, 0);
|
||||||
if (!desc)
|
if (!desc)
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -531,14 +448,14 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
|
||||||
/* append data sg */
|
/* append data sg */
|
||||||
WARN_ON(host->data != NULL);
|
WARN_ON(host->data != NULL);
|
||||||
host->data = data;
|
host->data = data;
|
||||||
host->dma_dir = dma_data_dir;
|
ssp->dma_dir = dma_data_dir;
|
||||||
host->slave_dirn = slave_dirn;
|
ssp->slave_dirn = slave_dirn;
|
||||||
desc = mxs_mmc_prep_dma(host, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
desc = mxs_mmc_prep_dma(host, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||||
if (!desc)
|
if (!desc)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
dmaengine_submit(desc);
|
dmaengine_submit(desc);
|
||||||
dma_async_issue_pending(host->dmach);
|
dma_async_issue_pending(ssp->dmach);
|
||||||
return;
|
return;
|
||||||
out:
|
out:
|
||||||
dev_warn(mmc_dev(host->mmc),
|
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);
|
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)
|
static void mxs_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
{
|
{
|
||||||
struct mxs_mmc_host *host = mmc_priv(mmc);
|
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;
|
host->bus_width = 0;
|
||||||
|
|
||||||
if (ios->clock)
|
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)
|
static void mxs_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
||||||
{
|
{
|
||||||
struct mxs_mmc_host *host = mmc_priv(mmc);
|
struct mxs_mmc_host *host = mmc_priv(mmc);
|
||||||
|
struct mxs_ssp *ssp = &host->ssp;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
spin_lock_irqsave(&host->lock, 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) {
|
if (enable) {
|
||||||
writel(BM_SSP_CTRL0_SDIO_IRQ_CHECK,
|
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,
|
writel(BM_SSP_CTRL1_SDIO_IRQ_EN,
|
||||||
host->base + HW_SSP_CTRL1(host) + STMP_OFFSET_REG_SET);
|
host->base + HW_SSP_CTRL1(host) + STMP_OFFSET_REG_SET);
|
||||||
} else {
|
} else {
|
||||||
writel(BM_SSP_CTRL0_SDIO_IRQ_CHECK,
|
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,
|
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);
|
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)
|
BM_SSP_STATUS_SDIO_IRQ)
|
||||||
mmc_signal_sdio_irq(host->mmc);
|
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)
|
static bool mxs_mmc_dma_filter(struct dma_chan *chan, void *param)
|
||||||
{
|
{
|
||||||
struct mxs_mmc_host *host = param;
|
struct mxs_mmc_host *host = param;
|
||||||
|
struct mxs_ssp *ssp = &host->ssp;
|
||||||
|
|
||||||
if (!mxs_dma_is_apbh(chan))
|
if (!mxs_dma_is_apbh(chan))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (chan->chan_id != host->dma_channel)
|
if (chan->chan_id != ssp->dma_channel)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
chan->private = &host->dma_data;
|
chan->private = &ssp->dma_data;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_device_id mxs_mmc_ids[] = {
|
static struct platform_device_id mxs_ssp_ids[] = {
|
||||||
{
|
{
|
||||||
.name = "imx23-mmc",
|
.name = "imx23-mmc",
|
||||||
.driver_data = IMX23_MMC,
|
.driver_data = IMX23_SSP,
|
||||||
}, {
|
}, {
|
||||||
.name = "imx28-mmc",
|
.name = "imx28-mmc",
|
||||||
.driver_data = IMX28_MMC,
|
.driver_data = IMX28_SSP,
|
||||||
}, {
|
}, {
|
||||||
/* sentinel */
|
/* 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[] = {
|
static const struct of_device_id mxs_mmc_dt_ids[] = {
|
||||||
{ .compatible = "fsl,imx23-mmc", .data = (void *) IMX23_MMC, },
|
{ .compatible = "fsl,imx23-mmc", .data = (void *) IMX23_SSP, },
|
||||||
{ .compatible = "fsl,imx28-mmc", .data = (void *) IMX28_MMC, },
|
{ .compatible = "fsl,imx28-mmc", .data = (void *) IMX28_SSP, },
|
||||||
{ /* sentinel */ }
|
{ /* sentinel */ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, mxs_mmc_dt_ids);
|
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;
|
dma_cap_mask_t mask;
|
||||||
struct regulator *reg_vmmc;
|
struct regulator *reg_vmmc;
|
||||||
enum of_gpio_flags flags;
|
enum of_gpio_flags flags;
|
||||||
|
struct mxs_ssp *ssp;
|
||||||
|
|
||||||
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||||
|
@ -729,28 +613,30 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
host = mmc_priv(mmc);
|
host = mmc_priv(mmc);
|
||||||
host->base = devm_request_and_ioremap(&pdev->dev, iores);
|
ssp = &host->ssp;
|
||||||
if (!host->base) {
|
ssp->dev = &pdev->dev;
|
||||||
|
ssp->base = devm_request_and_ioremap(&pdev->dev, iores);
|
||||||
|
if (!ssp->base) {
|
||||||
ret = -EADDRNOTAVAIL;
|
ret = -EADDRNOTAVAIL;
|
||||||
goto out_mmc_free;
|
goto out_mmc_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (np) {
|
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
|
* TODO: This is a temporary solution and should be changed
|
||||||
* to use generic DMA binding later when the helpers get in.
|
* to use generic DMA binding later when the helpers get in.
|
||||||
*/
|
*/
|
||||||
ret = of_property_read_u32(np, "fsl,ssp-dma-channel",
|
ret = of_property_read_u32(np, "fsl,ssp-dma-channel",
|
||||||
&host->dma_channel);
|
&ssp->dma_channel);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(mmc_dev(host->mmc),
|
dev_err(mmc_dev(host->mmc),
|
||||||
"failed to get dma channel\n");
|
"failed to get dma channel\n");
|
||||||
goto out_mmc_free;
|
goto out_mmc_free;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
host->devid = pdev->id_entry->driver_data;
|
ssp->devid = pdev->id_entry->driver_data;
|
||||||
host->dma_channel = dmares->start;
|
ssp->dma_channel = dmares->start;
|
||||||
}
|
}
|
||||||
|
|
||||||
host->mmc = mmc;
|
host->mmc = mmc;
|
||||||
|
@ -772,20 +658,20 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
||||||
goto out_mmc_free;
|
goto out_mmc_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
host->clk = clk_get(&pdev->dev, NULL);
|
ssp->clk = clk_get(&pdev->dev, NULL);
|
||||||
if (IS_ERR(host->clk)) {
|
if (IS_ERR(ssp->clk)) {
|
||||||
ret = PTR_ERR(host->clk);
|
ret = PTR_ERR(ssp->clk);
|
||||||
goto out_mmc_free;
|
goto out_mmc_free;
|
||||||
}
|
}
|
||||||
clk_prepare_enable(host->clk);
|
clk_prepare_enable(ssp->clk);
|
||||||
|
|
||||||
mxs_mmc_reset(host);
|
mxs_mmc_reset(host);
|
||||||
|
|
||||||
dma_cap_zero(mask);
|
dma_cap_zero(mask);
|
||||||
dma_cap_set(DMA_SLAVE, mask);
|
dma_cap_set(DMA_SLAVE, mask);
|
||||||
host->dma_data.chan_irq = irq_dma;
|
ssp->dma_data.chan_irq = irq_dma;
|
||||||
host->dmach = dma_request_channel(mask, mxs_mmc_dma_filter, host);
|
ssp->dmach = dma_request_channel(mask, mxs_mmc_dma_filter, host);
|
||||||
if (!host->dmach) {
|
if (!ssp->dmach) {
|
||||||
dev_err(mmc_dev(host->mmc),
|
dev_err(mmc_dev(host->mmc),
|
||||||
"%s: failed to request dma\n", __func__);
|
"%s: failed to request dma\n", __func__);
|
||||||
goto out_clk_put;
|
goto out_clk_put;
|
||||||
|
@ -822,9 +708,9 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
mmc->max_segs = 52;
|
mmc->max_segs = 52;
|
||||||
mmc->max_blk_size = 1 << 0xf;
|
mmc->max_blk_size = 1 << 0xf;
|
||||||
mmc->max_blk_count = (ssp_is_old(host)) ? 0xff : 0xffffff;
|
mmc->max_blk_count = (ssp_is_old(ssp)) ? 0xff : 0xffffff;
|
||||||
mmc->max_req_size = (ssp_is_old(host)) ? 0xffff : 0xffffffff;
|
mmc->max_req_size = (ssp_is_old(ssp)) ? 0xffff : 0xffffffff;
|
||||||
mmc->max_seg_size = dma_get_max_seg_size(host->dmach->device->dev);
|
mmc->max_seg_size = dma_get_max_seg_size(ssp->dmach->device->dev);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, mmc);
|
platform_set_drvdata(pdev, mmc);
|
||||||
|
|
||||||
|
@ -844,11 +730,11 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_free_dma:
|
out_free_dma:
|
||||||
if (host->dmach)
|
if (ssp->dmach)
|
||||||
dma_release_channel(host->dmach);
|
dma_release_channel(ssp->dmach);
|
||||||
out_clk_put:
|
out_clk_put:
|
||||||
clk_disable_unprepare(host->clk);
|
clk_disable_unprepare(ssp->clk);
|
||||||
clk_put(host->clk);
|
clk_put(ssp->clk);
|
||||||
out_mmc_free:
|
out_mmc_free:
|
||||||
mmc_free_host(mmc);
|
mmc_free_host(mmc);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -858,16 +744,17 @@ static int mxs_mmc_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct mmc_host *mmc = platform_get_drvdata(pdev);
|
struct mmc_host *mmc = platform_get_drvdata(pdev);
|
||||||
struct mxs_mmc_host *host = mmc_priv(mmc);
|
struct mxs_mmc_host *host = mmc_priv(mmc);
|
||||||
|
struct mxs_ssp *ssp = &host->ssp;
|
||||||
|
|
||||||
mmc_remove_host(mmc);
|
mmc_remove_host(mmc);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, NULL);
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
|
||||||
if (host->dmach)
|
if (ssp->dmach)
|
||||||
dma_release_channel(host->dmach);
|
dma_release_channel(ssp->dmach);
|
||||||
|
|
||||||
clk_disable_unprepare(host->clk);
|
clk_disable_unprepare(ssp->clk);
|
||||||
clk_put(host->clk);
|
clk_put(ssp->clk);
|
||||||
|
|
||||||
mmc_free_host(mmc);
|
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 mmc_host *mmc = dev_get_drvdata(dev);
|
||||||
struct mxs_mmc_host *host = mmc_priv(mmc);
|
struct mxs_mmc_host *host = mmc_priv(mmc);
|
||||||
|
struct mxs_ssp *ssp = &host->ssp;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
ret = mmc_suspend_host(mmc);
|
ret = mmc_suspend_host(mmc);
|
||||||
|
|
||||||
clk_disable_unprepare(host->clk);
|
clk_disable_unprepare(ssp->clk);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -892,9 +780,10 @@ static int mxs_mmc_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct mmc_host *mmc = dev_get_drvdata(dev);
|
struct mmc_host *mmc = dev_get_drvdata(dev);
|
||||||
struct mxs_mmc_host *host = mmc_priv(mmc);
|
struct mxs_mmc_host *host = mmc_priv(mmc);
|
||||||
|
struct mxs_ssp *ssp = &host->ssp;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
clk_prepare_enable(host->clk);
|
clk_prepare_enable(ssp->clk);
|
||||||
|
|
||||||
ret = mmc_resume_host(mmc);
|
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 = {
|
static struct platform_driver mxs_mmc_driver = {
|
||||||
.probe = mxs_mmc_probe,
|
.probe = mxs_mmc_probe,
|
||||||
.remove = mxs_mmc_remove,
|
.remove = mxs_mmc_remove,
|
||||||
.id_table = mxs_mmc_ids,
|
.id_table = mxs_ssp_ids,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRIVER_NAME,
|
.name = DRIVER_NAME,
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
|
|
|
@ -325,6 +325,12 @@ config SPI_S3C64XX
|
||||||
help
|
help
|
||||||
SPI driver for Samsung S3C64XX and newer SoCs.
|
SPI driver for Samsung S3C64XX and newer SoCs.
|
||||||
|
|
||||||
|
config SPI_SC18IS602
|
||||||
|
tristate "NXP SC18IS602/602B/603 I2C to SPI bridge"
|
||||||
|
depends on I2C
|
||||||
|
help
|
||||||
|
SPI driver for NXP SC18IS602/602B/603 I2C to SPI bridge.
|
||||||
|
|
||||||
config SPI_SH_MSIOF
|
config SPI_SH_MSIOF
|
||||||
tristate "SuperH MSIOF SPI controller"
|
tristate "SuperH MSIOF SPI controller"
|
||||||
depends on SUPERH && HAVE_CLK
|
depends on SUPERH && HAVE_CLK
|
||||||
|
@ -364,6 +370,13 @@ config SPI_STMP3XXX
|
||||||
help
|
help
|
||||||
SPI driver for Freescale STMP37xx/378x SoC SSP interface
|
SPI driver for Freescale STMP37xx/378x SoC SSP interface
|
||||||
|
|
||||||
|
config SPI_MXS
|
||||||
|
tristate "Freescale MXS SPI controller"
|
||||||
|
depends on ARCH_MXS
|
||||||
|
select STMP_DEVICE
|
||||||
|
help
|
||||||
|
SPI driver for Freescale MXS devices.
|
||||||
|
|
||||||
config SPI_TEGRA
|
config SPI_TEGRA
|
||||||
tristate "Nvidia Tegra SPI controller"
|
tristate "Nvidia Tegra SPI controller"
|
||||||
depends on ARCH_TEGRA && (TEGRA_SYSTEM_DMA || TEGRA20_APB_DMA)
|
depends on ARCH_TEGRA && (TEGRA_SYSTEM_DMA || TEGRA20_APB_DMA)
|
||||||
|
|
|
@ -36,6 +36,7 @@ obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o
|
||||||
obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mpc512x-psc.o
|
obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mpc512x-psc.o
|
||||||
obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o
|
obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o
|
||||||
obj-$(CONFIG_SPI_MPC52xx) += spi-mpc52xx.o
|
obj-$(CONFIG_SPI_MPC52xx) += spi-mpc52xx.o
|
||||||
|
obj-$(CONFIG_SPI_MXS) += spi-mxs.o
|
||||||
obj-$(CONFIG_SPI_NUC900) += spi-nuc900.o
|
obj-$(CONFIG_SPI_NUC900) += spi-nuc900.o
|
||||||
obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o
|
obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o
|
||||||
obj-$(CONFIG_SPI_OMAP_UWIRE) += spi-omap-uwire.o
|
obj-$(CONFIG_SPI_OMAP_UWIRE) += spi-omap-uwire.o
|
||||||
|
@ -51,6 +52,7 @@ obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o
|
||||||
spi-s3c24xx-hw-y := spi-s3c24xx.o
|
spi-s3c24xx-hw-y := spi-s3c24xx.o
|
||||||
spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o
|
spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o
|
||||||
obj-$(CONFIG_SPI_S3C64XX) += spi-s3c64xx.o
|
obj-$(CONFIG_SPI_S3C64XX) += spi-s3c64xx.o
|
||||||
|
obj-$(CONFIG_SPI_SC18IS602) += spi-sc18is602.o
|
||||||
obj-$(CONFIG_SPI_SH) += spi-sh.o
|
obj-$(CONFIG_SPI_SH) += spi-sh.o
|
||||||
obj-$(CONFIG_SPI_SH_HSPI) += spi-sh-hspi.o
|
obj-$(CONFIG_SPI_SH_HSPI) += spi-sh-hspi.o
|
||||||
obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o
|
obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o
|
||||||
|
|
|
@ -307,8 +307,6 @@ static const struct of_device_id altera_spi_match[] = {
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, altera_spi_match);
|
MODULE_DEVICE_TABLE(of, altera_spi_match);
|
||||||
#else /* CONFIG_OF */
|
|
||||||
#define altera_spi_match NULL
|
|
||||||
#endif /* CONFIG_OF */
|
#endif /* CONFIG_OF */
|
||||||
|
|
||||||
static struct platform_driver altera_spi_driver = {
|
static struct platform_driver altera_spi_driver = {
|
||||||
|
@ -318,7 +316,7 @@ static struct platform_driver altera_spi_driver = {
|
||||||
.name = DRV_NAME,
|
.name = DRV_NAME,
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.pm = NULL,
|
.pm = NULL,
|
||||||
.of_match_table = altera_spi_match,
|
.of_match_table = of_match_ptr(altera_spi_match),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
module_platform_driver(altera_spi_driver);
|
module_platform_driver(altera_spi_driver);
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/of_gpio.h>
|
||||||
|
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/spi/spi_bitbang.h>
|
#include <linux/spi/spi_bitbang.h>
|
||||||
|
@ -46,6 +48,7 @@ struct spi_gpio {
|
||||||
struct spi_bitbang bitbang;
|
struct spi_bitbang bitbang;
|
||||||
struct spi_gpio_platform_data pdata;
|
struct spi_gpio_platform_data pdata;
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
|
int cs_gpios[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
/*----------------------------------------------------------------------*/
|
/*----------------------------------------------------------------------*/
|
||||||
|
@ -89,15 +92,21 @@ struct spi_gpio {
|
||||||
|
|
||||||
/*----------------------------------------------------------------------*/
|
/*----------------------------------------------------------------------*/
|
||||||
|
|
||||||
static inline const struct spi_gpio_platform_data * __pure
|
static inline struct spi_gpio * __pure
|
||||||
spi_to_pdata(const struct spi_device *spi)
|
spi_to_spi_gpio(const struct spi_device *spi)
|
||||||
{
|
{
|
||||||
const struct spi_bitbang *bang;
|
const struct spi_bitbang *bang;
|
||||||
const struct spi_gpio *spi_gpio;
|
struct spi_gpio *spi_gpio;
|
||||||
|
|
||||||
bang = spi_master_get_devdata(spi->master);
|
bang = spi_master_get_devdata(spi->master);
|
||||||
spi_gpio = container_of(bang, struct spi_gpio, bitbang);
|
spi_gpio = container_of(bang, struct spi_gpio, bitbang);
|
||||||
return &spi_gpio->pdata;
|
return spi_gpio;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct spi_gpio_platform_data * __pure
|
||||||
|
spi_to_pdata(const struct spi_device *spi)
|
||||||
|
{
|
||||||
|
return &spi_to_spi_gpio(spi)->pdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this is #defined to avoid unused-variable warnings when inlining */
|
/* this is #defined to avoid unused-variable warnings when inlining */
|
||||||
|
@ -210,7 +219,8 @@ static u32 spi_gpio_spec_txrx_word_mode3(struct spi_device *spi,
|
||||||
|
|
||||||
static void spi_gpio_chipselect(struct spi_device *spi, int is_active)
|
static void spi_gpio_chipselect(struct spi_device *spi, int is_active)
|
||||||
{
|
{
|
||||||
unsigned long cs = (unsigned long) spi->controller_data;
|
struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi);
|
||||||
|
unsigned int cs = spi_gpio->cs_gpios[spi->chip_select];
|
||||||
|
|
||||||
/* set initial clock polarity */
|
/* set initial clock polarity */
|
||||||
if (is_active)
|
if (is_active)
|
||||||
|
@ -224,12 +234,27 @@ static void spi_gpio_chipselect(struct spi_device *spi, int is_active)
|
||||||
|
|
||||||
static int spi_gpio_setup(struct spi_device *spi)
|
static int spi_gpio_setup(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
unsigned long cs = (unsigned long) spi->controller_data;
|
unsigned int cs;
|
||||||
int status = 0;
|
int status = 0;
|
||||||
|
struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi);
|
||||||
|
struct device_node *np = spi->master->dev.of_node;
|
||||||
|
|
||||||
if (spi->bits_per_word > 32)
|
if (spi->bits_per_word > 32)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (np) {
|
||||||
|
/*
|
||||||
|
* In DT environments, the CS GPIOs have already been
|
||||||
|
* initialized from the "cs-gpios" property of the node.
|
||||||
|
*/
|
||||||
|
cs = spi_gpio->cs_gpios[spi->chip_select];
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* ... otherwise, take it from spi->controller_data
|
||||||
|
*/
|
||||||
|
cs = (unsigned int) spi->controller_data;
|
||||||
|
}
|
||||||
|
|
||||||
if (!spi->controller_state) {
|
if (!spi->controller_state) {
|
||||||
if (cs != SPI_GPIO_NO_CHIPSELECT) {
|
if (cs != SPI_GPIO_NO_CHIPSELECT) {
|
||||||
status = gpio_request(cs, dev_name(&spi->dev));
|
status = gpio_request(cs, dev_name(&spi->dev));
|
||||||
|
@ -239,8 +264,12 @@ static int spi_gpio_setup(struct spi_device *spi)
|
||||||
!(spi->mode & SPI_CS_HIGH));
|
!(spi->mode & SPI_CS_HIGH));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!status)
|
if (!status) {
|
||||||
status = spi_bitbang_setup(spi);
|
status = spi_bitbang_setup(spi);
|
||||||
|
/* in case it was initialized from static board data */
|
||||||
|
spi_gpio->cs_gpios[spi->chip_select] = cs;
|
||||||
|
}
|
||||||
|
|
||||||
if (status) {
|
if (status) {
|
||||||
if (!spi->controller_state && cs != SPI_GPIO_NO_CHIPSELECT)
|
if (!spi->controller_state && cs != SPI_GPIO_NO_CHIPSELECT)
|
||||||
gpio_free(cs);
|
gpio_free(cs);
|
||||||
|
@ -250,7 +279,8 @@ static int spi_gpio_setup(struct spi_device *spi)
|
||||||
|
|
||||||
static void spi_gpio_cleanup(struct spi_device *spi)
|
static void spi_gpio_cleanup(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
unsigned long cs = (unsigned long) spi->controller_data;
|
struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi);
|
||||||
|
unsigned int cs = spi_gpio->cs_gpios[spi->chip_select];
|
||||||
|
|
||||||
if (cs != SPI_GPIO_NO_CHIPSELECT)
|
if (cs != SPI_GPIO_NO_CHIPSELECT)
|
||||||
gpio_free(cs);
|
gpio_free(cs);
|
||||||
|
@ -313,6 +343,55 @@ done:
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_OF
|
||||||
|
static struct of_device_id spi_gpio_dt_ids[] = {
|
||||||
|
{ .compatible = "spi-gpio" },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, spi_gpio_dt_ids);
|
||||||
|
|
||||||
|
static int spi_gpio_probe_dt(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
u32 tmp;
|
||||||
|
struct spi_gpio_platform_data *pdata;
|
||||||
|
struct device_node *np = pdev->dev.of_node;
|
||||||
|
const struct of_device_id *of_id =
|
||||||
|
of_match_device(spi_gpio_dt_ids, &pdev->dev);
|
||||||
|
|
||||||
|
if (!of_id)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||||
|
if (!pdata)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
pdata->sck = of_get_named_gpio(np, "gpio-sck", 0);
|
||||||
|
pdata->miso = of_get_named_gpio(np, "gpio-miso", 0);
|
||||||
|
pdata->mosi = of_get_named_gpio(np, "gpio-mosi", 0);
|
||||||
|
|
||||||
|
ret = of_property_read_u32(np, "num-chipselects", &tmp);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&pdev->dev, "num-chipselects property not found\n");
|
||||||
|
goto error_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdata->num_chipselect = tmp;
|
||||||
|
pdev->dev.platform_data = pdata;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
error_free:
|
||||||
|
devm_kfree(&pdev->dev, pdata);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static inline int spi_gpio_probe_dt(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int __devinit spi_gpio_probe(struct platform_device *pdev)
|
static int __devinit spi_gpio_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
|
@ -320,6 +399,13 @@ static int __devinit spi_gpio_probe(struct platform_device *pdev)
|
||||||
struct spi_gpio *spi_gpio;
|
struct spi_gpio *spi_gpio;
|
||||||
struct spi_gpio_platform_data *pdata;
|
struct spi_gpio_platform_data *pdata;
|
||||||
u16 master_flags = 0;
|
u16 master_flags = 0;
|
||||||
|
bool use_of = 0;
|
||||||
|
|
||||||
|
status = spi_gpio_probe_dt(pdev);
|
||||||
|
if (status < 0)
|
||||||
|
return status;
|
||||||
|
if (status > 0)
|
||||||
|
use_of = 1;
|
||||||
|
|
||||||
pdata = pdev->dev.platform_data;
|
pdata = pdev->dev.platform_data;
|
||||||
#ifdef GENERIC_BITBANG
|
#ifdef GENERIC_BITBANG
|
||||||
|
@ -331,7 +417,8 @@ static int __devinit spi_gpio_probe(struct platform_device *pdev)
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
master = spi_alloc_master(&pdev->dev, sizeof *spi_gpio);
|
master = spi_alloc_master(&pdev->dev, sizeof(*spi_gpio) +
|
||||||
|
(sizeof(int) * SPI_N_CHIPSEL));
|
||||||
if (!master) {
|
if (!master) {
|
||||||
status = -ENOMEM;
|
status = -ENOMEM;
|
||||||
goto gpio_free;
|
goto gpio_free;
|
||||||
|
@ -348,6 +435,23 @@ static int __devinit spi_gpio_probe(struct platform_device *pdev)
|
||||||
master->num_chipselect = SPI_N_CHIPSEL;
|
master->num_chipselect = SPI_N_CHIPSEL;
|
||||||
master->setup = spi_gpio_setup;
|
master->setup = spi_gpio_setup;
|
||||||
master->cleanup = spi_gpio_cleanup;
|
master->cleanup = spi_gpio_cleanup;
|
||||||
|
#ifdef CONFIG_OF
|
||||||
|
master->dev.of_node = pdev->dev.of_node;
|
||||||
|
|
||||||
|
if (use_of) {
|
||||||
|
int i;
|
||||||
|
struct device_node *np = pdev->dev.of_node;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In DT environments, take the CS GPIO from the "cs-gpios"
|
||||||
|
* property of the node.
|
||||||
|
*/
|
||||||
|
|
||||||
|
for (i = 0; i < SPI_N_CHIPSEL; i++)
|
||||||
|
spi_gpio->cs_gpios[i] =
|
||||||
|
of_get_named_gpio(np, "cs-gpios", i);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
spi_gpio->bitbang.master = spi_master_get(master);
|
spi_gpio->bitbang.master = spi_master_get(master);
|
||||||
spi_gpio->bitbang.chipselect = spi_gpio_chipselect;
|
spi_gpio->bitbang.chipselect = spi_gpio_chipselect;
|
||||||
|
@ -408,8 +512,11 @@ static int __devexit spi_gpio_remove(struct platform_device *pdev)
|
||||||
MODULE_ALIAS("platform:" DRIVER_NAME);
|
MODULE_ALIAS("platform:" DRIVER_NAME);
|
||||||
|
|
||||||
static struct platform_driver spi_gpio_driver = {
|
static struct platform_driver spi_gpio_driver = {
|
||||||
.driver.name = DRIVER_NAME,
|
.driver = {
|
||||||
.driver.owner = THIS_MODULE,
|
.name = DRIVER_NAME,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.of_match_table = of_match_ptr(spi_gpio_dt_ids),
|
||||||
|
},
|
||||||
.probe = spi_gpio_probe,
|
.probe = spi_gpio_probe,
|
||||||
.remove = __devexit_p(spi_gpio_remove),
|
.remove = __devexit_p(spi_gpio_remove),
|
||||||
};
|
};
|
||||||
|
|
|
@ -197,6 +197,7 @@ static unsigned int spi_imx_clkdiv_2(unsigned int fin,
|
||||||
#define MX51_ECSPI_CONFIG_SCLKPOL(cs) (1 << ((cs) + 4))
|
#define MX51_ECSPI_CONFIG_SCLKPOL(cs) (1 << ((cs) + 4))
|
||||||
#define MX51_ECSPI_CONFIG_SBBCTRL(cs) (1 << ((cs) + 8))
|
#define MX51_ECSPI_CONFIG_SBBCTRL(cs) (1 << ((cs) + 8))
|
||||||
#define MX51_ECSPI_CONFIG_SSBPOL(cs) (1 << ((cs) + 12))
|
#define MX51_ECSPI_CONFIG_SSBPOL(cs) (1 << ((cs) + 12))
|
||||||
|
#define MX51_ECSPI_CONFIG_SCLKCTL(cs) (1 << ((cs) + 20))
|
||||||
|
|
||||||
#define MX51_ECSPI_INT 0x10
|
#define MX51_ECSPI_INT 0x10
|
||||||
#define MX51_ECSPI_INT_TEEN (1 << 0)
|
#define MX51_ECSPI_INT_TEEN (1 << 0)
|
||||||
|
@ -287,9 +288,10 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
|
||||||
if (config->mode & SPI_CPHA)
|
if (config->mode & SPI_CPHA)
|
||||||
cfg |= MX51_ECSPI_CONFIG_SCLKPHA(config->cs);
|
cfg |= MX51_ECSPI_CONFIG_SCLKPHA(config->cs);
|
||||||
|
|
||||||
if (config->mode & SPI_CPOL)
|
if (config->mode & SPI_CPOL) {
|
||||||
cfg |= MX51_ECSPI_CONFIG_SCLKPOL(config->cs);
|
cfg |= MX51_ECSPI_CONFIG_SCLKPOL(config->cs);
|
||||||
|
cfg |= MX51_ECSPI_CONFIG_SCLKCTL(config->cs);
|
||||||
|
}
|
||||||
if (config->mode & SPI_CS_HIGH)
|
if (config->mode & SPI_CS_HIGH)
|
||||||
cfg |= MX51_ECSPI_CONFIG_SSBPOL(config->cs);
|
cfg |= MX51_ECSPI_CONFIG_SSBPOL(config->cs);
|
||||||
|
|
||||||
|
|
|
@ -494,7 +494,7 @@ free_master:
|
||||||
|
|
||||||
static int __devexit mpc512x_psc_spi_do_remove(struct device *dev)
|
static int __devexit mpc512x_psc_spi_do_remove(struct device *dev)
|
||||||
{
|
{
|
||||||
struct spi_master *master = dev_get_drvdata(dev);
|
struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
|
||||||
struct mpc512x_psc_spi *mps = spi_master_get_devdata(master);
|
struct mpc512x_psc_spi *mps = spi_master_get_devdata(master);
|
||||||
|
|
||||||
flush_workqueue(mps->workqueue);
|
flush_workqueue(mps->workqueue);
|
||||||
|
@ -503,6 +503,7 @@ static int __devexit mpc512x_psc_spi_do_remove(struct device *dev)
|
||||||
free_irq(mps->irq, mps);
|
free_irq(mps->irq, mps);
|
||||||
if (mps->psc)
|
if (mps->psc)
|
||||||
iounmap(mps->psc);
|
iounmap(mps->psc);
|
||||||
|
spi_master_put(master);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -481,7 +481,7 @@ static int __devinit mpc52xx_psc_spi_of_probe(struct platform_device *op)
|
||||||
|
|
||||||
static int __devexit mpc52xx_psc_spi_of_remove(struct platform_device *op)
|
static int __devexit mpc52xx_psc_spi_of_remove(struct platform_device *op)
|
||||||
{
|
{
|
||||||
struct spi_master *master = dev_get_drvdata(&op->dev);
|
struct spi_master *master = spi_master_get(dev_get_drvdata(&op->dev));
|
||||||
struct mpc52xx_psc_spi *mps = spi_master_get_devdata(master);
|
struct mpc52xx_psc_spi *mps = spi_master_get_devdata(master);
|
||||||
|
|
||||||
flush_workqueue(mps->workqueue);
|
flush_workqueue(mps->workqueue);
|
||||||
|
@ -490,6 +490,7 @@ static int __devexit mpc52xx_psc_spi_of_remove(struct platform_device *op)
|
||||||
free_irq(mps->irq, mps);
|
free_irq(mps->irq, mps);
|
||||||
if (mps->psc)
|
if (mps->psc)
|
||||||
iounmap(mps->psc);
|
iounmap(mps->psc);
|
||||||
|
spi_master_put(master);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -454,7 +454,7 @@ static int __devinit mpc52xx_spi_probe(struct platform_device *op)
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!ms->gpio_cs) {
|
if (!ms->gpio_cs) {
|
||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
goto err_alloc;
|
goto err_alloc_gpio;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < ms->gpio_cs_count; i++) {
|
for (i = 0; i < ms->gpio_cs_count; i++) {
|
||||||
|
@ -514,12 +514,13 @@ static int __devinit mpc52xx_spi_probe(struct platform_device *op)
|
||||||
|
|
||||||
err_register:
|
err_register:
|
||||||
dev_err(&ms->master->dev, "initialization failed\n");
|
dev_err(&ms->master->dev, "initialization failed\n");
|
||||||
spi_master_put(master);
|
|
||||||
err_gpio:
|
err_gpio:
|
||||||
while (i-- > 0)
|
while (i-- > 0)
|
||||||
gpio_free(ms->gpio_cs[i]);
|
gpio_free(ms->gpio_cs[i]);
|
||||||
|
|
||||||
kfree(ms->gpio_cs);
|
kfree(ms->gpio_cs);
|
||||||
|
err_alloc_gpio:
|
||||||
|
spi_master_put(master);
|
||||||
err_alloc:
|
err_alloc:
|
||||||
err_init:
|
err_init:
|
||||||
iounmap(regs);
|
iounmap(regs);
|
||||||
|
@ -528,7 +529,7 @@ static int __devinit mpc52xx_spi_probe(struct platform_device *op)
|
||||||
|
|
||||||
static int __devexit mpc52xx_spi_remove(struct platform_device *op)
|
static int __devexit mpc52xx_spi_remove(struct platform_device *op)
|
||||||
{
|
{
|
||||||
struct spi_master *master = dev_get_drvdata(&op->dev);
|
struct spi_master *master = spi_master_get(dev_get_drvdata(&op->dev));
|
||||||
struct mpc52xx_spi *ms = spi_master_get_devdata(master);
|
struct mpc52xx_spi *ms = spi_master_get_devdata(master);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -540,8 +541,8 @@ static int __devexit mpc52xx_spi_remove(struct platform_device *op)
|
||||||
|
|
||||||
kfree(ms->gpio_cs);
|
kfree(ms->gpio_cs);
|
||||||
spi_unregister_master(master);
|
spi_unregister_master(master);
|
||||||
spi_master_put(master);
|
|
||||||
iounmap(ms->regs);
|
iounmap(ms->regs);
|
||||||
|
spi_master_put(master);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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/pm_runtime.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/pinctrl/consumer.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
|
|
||||||
|
@ -140,13 +142,6 @@ struct omap2_mcspi_cs {
|
||||||
u32 chconf0;
|
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,
|
static inline void mcspi_write_reg(struct spi_master *master,
|
||||||
int idx, u32 val)
|
int idx, u32 val)
|
||||||
{
|
{
|
||||||
|
@ -205,7 +200,11 @@ static void omap2_mcspi_set_dma_req(const struct spi_device *spi,
|
||||||
else
|
else
|
||||||
rw = OMAP2_MCSPI_CHCONF_DMAW;
|
rw = OMAP2_MCSPI_CHCONF_DMAW;
|
||||||
|
|
||||||
MOD_REG_BIT(l, rw, enable);
|
if (enable)
|
||||||
|
l |= rw;
|
||||||
|
else
|
||||||
|
l &= ~rw;
|
||||||
|
|
||||||
mcspi_write_chconf0(spi, l);
|
mcspi_write_chconf0(spi, l);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,7 +223,11 @@ static void omap2_mcspi_force_cs(struct spi_device *spi, int cs_active)
|
||||||
u32 l;
|
u32 l;
|
||||||
|
|
||||||
l = mcspi_cached_chconf0(spi);
|
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);
|
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
|
* to single-channel master mode
|
||||||
*/
|
*/
|
||||||
l = mcspi_read_reg(master, OMAP2_MCSPI_MODULCTRL);
|
l = mcspi_read_reg(master, OMAP2_MCSPI_MODULCTRL);
|
||||||
MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_STEST, 0);
|
l &= ~(OMAP2_MCSPI_MODULCTRL_STEST | OMAP2_MCSPI_MODULCTRL_MS);
|
||||||
MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_MS, 0);
|
l |= OMAP2_MCSPI_MODULCTRL_SINGLE;
|
||||||
MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_SINGLE, 1);
|
|
||||||
mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, l);
|
mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, l);
|
||||||
|
|
||||||
ctx->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)
|
list_for_each_entry(cs, &ctx->cs, node)
|
||||||
__raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0);
|
__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)
|
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);
|
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
|
static unsigned
|
||||||
omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
|
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_cs *cs = spi->controller_state;
|
||||||
struct omap2_mcspi_dma *mcspi_dma;
|
struct omap2_mcspi_dma *mcspi_dma;
|
||||||
unsigned int count;
|
unsigned int count;
|
||||||
int word_len, element_count;
|
|
||||||
int elements = 0;
|
|
||||||
u32 l;
|
u32 l;
|
||||||
u8 * rx;
|
u8 *rx;
|
||||||
const u8 * tx;
|
const u8 *tx;
|
||||||
void __iomem *chstat_reg;
|
|
||||||
struct dma_slave_config cfg;
|
struct dma_slave_config cfg;
|
||||||
enum dma_slave_buswidth width;
|
enum dma_slave_buswidth width;
|
||||||
unsigned es;
|
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];
|
mcspi_dma = &mcspi->dma_channels[spi->chip_select];
|
||||||
l = mcspi_cached_chconf0(spi);
|
l = mcspi_cached_chconf0(spi);
|
||||||
|
|
||||||
chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0;
|
|
||||||
|
|
||||||
if (cs->word_len <= 8) {
|
if (cs->word_len <= 8) {
|
||||||
width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
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.src_maxburst = 1;
|
||||||
cfg.dst_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;
|
rx = xfer->rx_buf;
|
||||||
tx = xfer->tx_buf;
|
tx = xfer->tx_buf;
|
||||||
|
|
||||||
if (word_len <= 8) {
|
count = xfer->len;
|
||||||
element_count = count;
|
|
||||||
} else if (word_len <= 16) {
|
|
||||||
element_count = count >> 1;
|
|
||||||
} else /* word_len <= 32 */ {
|
|
||||||
element_count = count >> 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tx != NULL) {
|
if (tx != NULL)
|
||||||
dma_async_issue_pending(mcspi_dma->dma_tx);
|
omap2_mcspi_tx_dma(spi, xfer, cfg);
|
||||||
omap2_mcspi_set_dma_req(spi, 0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rx != NULL) {
|
if (rx != NULL)
|
||||||
dma_async_issue_pending(mcspi_dma->dma_rx);
|
return omap2_mcspi_rx_dma(spi, xfer, cfg, es);
|
||||||
omap2_mcspi_set_dma_req(spi, 1, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -848,12 +872,13 @@ static int omap2_mcspi_setup(struct spi_device *spi)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = omap2_mcspi_enable_clocks(mcspi);
|
ret = pm_runtime_get_sync(mcspi->dev);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = omap2_mcspi_setup_transfer(spi, NULL);
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1067,7 +1092,7 @@ static int __devinit omap2_mcspi_master_setup(struct omap2_mcspi *mcspi)
|
||||||
struct omap2_mcspi_regs *ctx = &mcspi->ctx;
|
struct omap2_mcspi_regs *ctx = &mcspi->ctx;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
ret = omap2_mcspi_enable_clocks(mcspi);
|
ret = pm_runtime_get_sync(mcspi->dev);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -1076,7 +1101,8 @@ static int __devinit omap2_mcspi_master_setup(struct omap2_mcspi *mcspi)
|
||||||
ctx->wakeupenable = OMAP2_MCSPI_WAKEUPENABLE_WKEN;
|
ctx->wakeupenable = OMAP2_MCSPI_WAKEUPENABLE_WKEN;
|
||||||
|
|
||||||
omap2_mcspi_set_master_mode(master);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1124,6 +1150,7 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev)
|
||||||
static int bus_num = 1;
|
static int bus_num = 1;
|
||||||
struct device_node *node = pdev->dev.of_node;
|
struct device_node *node = pdev->dev.of_node;
|
||||||
const struct of_device_id *match;
|
const struct of_device_id *match;
|
||||||
|
struct pinctrl *pinctrl;
|
||||||
|
|
||||||
master = spi_alloc_master(&pdev->dev, sizeof *mcspi);
|
master = spi_alloc_master(&pdev->dev, sizeof *mcspi);
|
||||||
if (master == NULL) {
|
if (master == NULL) {
|
||||||
|
@ -1219,6 +1246,11 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev)
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
goto dma_chnl_free;
|
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_use_autosuspend(&pdev->dev);
|
||||||
pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT);
|
pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT);
|
||||||
pm_runtime_enable(&pdev->dev);
|
pm_runtime_enable(&pdev->dev);
|
||||||
|
@ -1238,7 +1270,6 @@ dma_chnl_free:
|
||||||
kfree(mcspi->dma_channels);
|
kfree(mcspi->dma_channels);
|
||||||
free_master:
|
free_master:
|
||||||
spi_master_put(master);
|
spi_master_put(master);
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1252,12 +1283,11 @@ static int __devexit omap2_mcspi_remove(struct platform_device *pdev)
|
||||||
mcspi = spi_master_get_devdata(master);
|
mcspi = spi_master_get_devdata(master);
|
||||||
dma_channels = mcspi->dma_channels;
|
dma_channels = mcspi->dma_channels;
|
||||||
|
|
||||||
omap2_mcspi_disable_clocks(mcspi);
|
pm_runtime_put_sync(mcspi->dev);
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
|
||||||
spi_unregister_master(master);
|
spi_unregister_master(master);
|
||||||
kfree(dma_channels);
|
kfree(dma_channels);
|
||||||
platform_set_drvdata(pdev, NULL);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1278,20 +1308,21 @@ static int omap2_mcspi_resume(struct device *dev)
|
||||||
struct omap2_mcspi_regs *ctx = &mcspi->ctx;
|
struct omap2_mcspi_regs *ctx = &mcspi->ctx;
|
||||||
struct omap2_mcspi_cs *cs;
|
struct omap2_mcspi_cs *cs;
|
||||||
|
|
||||||
omap2_mcspi_enable_clocks(mcspi);
|
pm_runtime_get_sync(mcspi->dev);
|
||||||
list_for_each_entry(cs, &ctx->cs, node) {
|
list_for_each_entry(cs, &ctx->cs, node) {
|
||||||
if ((cs->chconf0 & OMAP2_MCSPI_CHCONF_FORCE) == 0) {
|
if ((cs->chconf0 & OMAP2_MCSPI_CHCONF_FORCE) == 0) {
|
||||||
/*
|
/*
|
||||||
* We need to toggle CS state for OMAP take this
|
* We need to toggle CS state for OMAP take this
|
||||||
* change in account.
|
* 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);
|
__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);
|
__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;
|
return 0;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -36,12 +36,6 @@
|
||||||
#define ORION_SPI_CLK_PRESCALE_MASK 0x1F
|
#define ORION_SPI_CLK_PRESCALE_MASK 0x1F
|
||||||
|
|
||||||
struct orion_spi {
|
struct orion_spi {
|
||||||
struct work_struct work;
|
|
||||||
|
|
||||||
/* Lock access to transfer list. */
|
|
||||||
spinlock_t lock;
|
|
||||||
|
|
||||||
struct list_head msg_queue;
|
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
unsigned int max_speed;
|
unsigned int max_speed;
|
||||||
|
@ -49,8 +43,6 @@ struct orion_spi {
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct workqueue_struct *orion_spi_wq;
|
|
||||||
|
|
||||||
static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg)
|
static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg)
|
||||||
{
|
{
|
||||||
return orion_spi->base + reg;
|
return orion_spi->base + reg;
|
||||||
|
@ -277,73 +269,78 @@ 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 =
|
struct orion_spi *orion_spi = spi_master_get_devdata(master);
|
||||||
container_of(work, struct orion_spi, work);
|
struct spi_device *spi = m->spi;
|
||||||
|
struct spi_transfer *t = NULL;
|
||||||
|
int par_override = 0;
|
||||||
|
int status = 0;
|
||||||
|
int cs_active = 0;
|
||||||
|
|
||||||
spin_lock_irq(&orion_spi->lock);
|
/* Load defaults */
|
||||||
while (!list_empty(&orion_spi->msg_queue)) {
|
status = orion_spi_setup_transfer(spi, NULL);
|
||||||
struct spi_message *m;
|
|
||||||
struct spi_device *spi;
|
|
||||||
struct spi_transfer *t = NULL;
|
|
||||||
int par_override = 0;
|
|
||||||
int status = 0;
|
|
||||||
int cs_active = 0;
|
|
||||||
|
|
||||||
m = container_of(orion_spi->msg_queue.next, struct spi_message,
|
if (status < 0)
|
||||||
queue);
|
goto msg_done;
|
||||||
|
|
||||||
list_del_init(&m->queue);
|
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||||
spin_unlock_irq(&orion_spi->lock);
|
/* make sure buffer length is even when working in 16
|
||||||
|
* bit mode*/
|
||||||
spi = m->spi;
|
if ((t->bits_per_word == 16) && (t->len & 1)) {
|
||||||
|
dev_err(&spi->dev,
|
||||||
/* Load defaults */
|
"message rejected : "
|
||||||
status = orion_spi_setup_transfer(spi, NULL);
|
"odd data length %d while in 16 bit mode\n",
|
||||||
|
t->len);
|
||||||
if (status < 0)
|
status = -EIO;
|
||||||
goto msg_done;
|
goto msg_done;
|
||||||
|
|
||||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
|
||||||
if (par_override || t->speed_hz || t->bits_per_word) {
|
|
||||||
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 (t->speed_hz && t->speed_hz < orion_spi->min_speed) {
|
||||||
if (cs_active)
|
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);
|
orion_spi_set_cs(orion_spi, 0);
|
||||||
|
cs_active = 0;
|
||||||
m->status = status;
|
}
|
||||||
m->complete(m->context);
|
|
||||||
|
|
||||||
spin_lock_irq(&orion_spi->lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_unlock_irq(&orion_spi->lock);
|
msg_done:
|
||||||
|
if (cs_active)
|
||||||
|
orion_spi_set_cs(orion_spi, 0);
|
||||||
|
|
||||||
|
m->status = status;
|
||||||
|
spi_finalize_current_message(master);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init orion_spi_reset(struct orion_spi *orion_spi)
|
static int __init orion_spi_reset(struct orion_spi *orion_spi)
|
||||||
|
@ -376,75 +373,6 @@ static int orion_spi_setup(struct spi_device *spi)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int orion_spi_transfer(struct spi_device *spi, struct spi_message *m)
|
|
||||||
{
|
|
||||||
struct orion_spi *orion_spi;
|
|
||||||
struct spi_transfer *t = NULL;
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
m->actual_length = 0;
|
|
||||||
m->status = 0;
|
|
||||||
|
|
||||||
/* reject invalid messages and transfers */
|
|
||||||
if (list_empty(&m->transfers) || !m->complete)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
orion_spi = spi_master_get_devdata(spi->master);
|
|
||||||
|
|
||||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
|
||||||
unsigned int bits_per_word = spi->bits_per_word;
|
|
||||||
|
|
||||||
if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) {
|
|
||||||
dev_err(&spi->dev,
|
|
||||||
"message rejected : "
|
|
||||||
"invalid transfer data buffers\n");
|
|
||||||
goto msg_rejected;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t->bits_per_word)
|
|
||||||
bits_per_word = t->bits_per_word;
|
|
||||||
|
|
||||||
if ((bits_per_word != 8) && (bits_per_word != 16)) {
|
|
||||||
dev_err(&spi->dev,
|
|
||||||
"message rejected : "
|
|
||||||
"invalid transfer bits_per_word (%d bits)\n",
|
|
||||||
bits_per_word);
|
|
||||||
goto msg_rejected;
|
|
||||||
}
|
|
||||||
/*make sure buffer length is even when working in 16 bit mode*/
|
|
||||||
if ((t->bits_per_word == 16) && (t->len & 1)) {
|
|
||||||
dev_err(&spi->dev,
|
|
||||||
"message rejected : "
|
|
||||||
"odd data length (%d) while in 16 bit mode\n",
|
|
||||||
t->len);
|
|
||||||
goto msg_rejected;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t->speed_hz && t->speed_hz < orion_spi->min_speed) {
|
|
||||||
dev_err(&spi->dev,
|
|
||||||
"message rejected : "
|
|
||||||
"device min speed (%d Hz) exceeds "
|
|
||||||
"required transfer speed (%d Hz)\n",
|
|
||||||
orion_spi->min_speed, t->speed_hz);
|
|
||||||
goto msg_rejected;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
spin_lock_irqsave(&orion_spi->lock, flags);
|
|
||||||
list_add_tail(&m->queue, &orion_spi->msg_queue);
|
|
||||||
queue_work(orion_spi_wq, &orion_spi->work);
|
|
||||||
spin_unlock_irqrestore(&orion_spi->lock, flags);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
msg_rejected:
|
|
||||||
/* Message rejected and not queued */
|
|
||||||
m->status = -EINVAL;
|
|
||||||
if (m->complete)
|
|
||||||
m->complete(m->context);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __init orion_spi_probe(struct platform_device *pdev)
|
static int __init orion_spi_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
|
@ -474,7 +402,7 @@ static int __init orion_spi_probe(struct platform_device *pdev)
|
||||||
master->mode_bits = 0;
|
master->mode_bits = 0;
|
||||||
|
|
||||||
master->setup = orion_spi_setup;
|
master->setup = orion_spi_setup;
|
||||||
master->transfer = orion_spi_transfer;
|
master->transfer_one_message = orion_spi_transfer_one_message;
|
||||||
master->num_chipselect = ORION_NUM_CHIPSELECTS;
|
master->num_chipselect = ORION_NUM_CHIPSELECTS;
|
||||||
|
|
||||||
dev_set_drvdata(&pdev->dev, master);
|
dev_set_drvdata(&pdev->dev, master);
|
||||||
|
@ -507,11 +435,6 @@ static int __init orion_spi_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
spi->base = ioremap(r->start, SZ_1K);
|
spi->base = ioremap(r->start, SZ_1K);
|
||||||
|
|
||||||
INIT_WORK(&spi->work, orion_spi_work);
|
|
||||||
|
|
||||||
spin_lock_init(&spi->lock);
|
|
||||||
INIT_LIST_HEAD(&spi->msg_queue);
|
|
||||||
|
|
||||||
if (orion_spi_reset(spi) < 0)
|
if (orion_spi_reset(spi) < 0)
|
||||||
goto out_rel_mem;
|
goto out_rel_mem;
|
||||||
|
|
||||||
|
@ -536,14 +459,12 @@ out:
|
||||||
static int __exit orion_spi_remove(struct platform_device *pdev)
|
static int __exit orion_spi_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
struct orion_spi *spi;
|
|
||||||
struct resource *r;
|
struct resource *r;
|
||||||
|
struct orion_spi *spi;
|
||||||
|
|
||||||
master = dev_get_drvdata(&pdev->dev);
|
master = dev_get_drvdata(&pdev->dev);
|
||||||
spi = spi_master_get_devdata(master);
|
spi = spi_master_get_devdata(master);
|
||||||
|
|
||||||
cancel_work_sync(&spi->work);
|
|
||||||
|
|
||||||
clk_disable_unprepare(spi->clk);
|
clk_disable_unprepare(spi->clk);
|
||||||
clk_put(spi->clk);
|
clk_put(spi->clk);
|
||||||
|
|
||||||
|
@ -574,21 +495,13 @@ static struct platform_driver orion_spi_driver = {
|
||||||
|
|
||||||
static int __init orion_spi_init(void)
|
static int __init orion_spi_init(void)
|
||||||
{
|
{
|
||||||
orion_spi_wq = create_singlethread_workqueue(
|
|
||||||
orion_spi_driver.driver.name);
|
|
||||||
if (orion_spi_wq == NULL)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
return platform_driver_probe(&orion_spi_driver, orion_spi_probe);
|
return platform_driver_probe(&orion_spi_driver, orion_spi_probe);
|
||||||
}
|
}
|
||||||
module_init(orion_spi_init);
|
module_init(orion_spi_init);
|
||||||
|
|
||||||
static void __exit orion_spi_exit(void)
|
static void __exit orion_spi_exit(void)
|
||||||
{
|
{
|
||||||
flush_workqueue(orion_spi_wq);
|
|
||||||
platform_driver_unregister(&orion_spi_driver);
|
platform_driver_unregister(&orion_spi_driver);
|
||||||
|
|
||||||
destroy_workqueue(orion_spi_wq);
|
|
||||||
}
|
}
|
||||||
module_exit(orion_spi_exit);
|
module_exit(orion_spi_exit);
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* A driver for the ARM PL022 PrimeCell SSP/SPI bus master.
|
* 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.
|
* Copyright (C) 2006 STMicroelectronics Pvt. Ltd.
|
||||||
*
|
*
|
||||||
* Author: Linus Walleij <linus.walleij@stericsson.com>
|
* Author: Linus Walleij <linus.walleij@stericsson.com>
|
||||||
|
@ -40,6 +40,9 @@
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
#include <linux/pm_runtime.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.
|
* 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_rx: scattertable for the RX transfer
|
||||||
* @sgt_tx: scattertable for the TX transfer
|
* @sgt_tx: scattertable for the TX transfer
|
||||||
* @dummypage: a dummy page used for driving data on the bus with DMA
|
* @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 pl022 {
|
||||||
struct amba_device *adev;
|
struct amba_device *adev;
|
||||||
|
@ -363,6 +368,10 @@ struct pl022 {
|
||||||
resource_size_t phybase;
|
resource_size_t phybase;
|
||||||
void __iomem *virtbase;
|
void __iomem *virtbase;
|
||||||
struct clk *clk;
|
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 spi_master *master;
|
||||||
struct pl022_ssp_controller *master_info;
|
struct pl022_ssp_controller *master_info;
|
||||||
/* Message per-transfer pump */
|
/* Message per-transfer pump */
|
||||||
|
@ -389,6 +398,8 @@ struct pl022 {
|
||||||
char *dummypage;
|
char *dummypage;
|
||||||
bool dma_running;
|
bool dma_running;
|
||||||
#endif
|
#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);
|
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
|
* giveback - current spi_message is over, schedule next message and call
|
||||||
* callback of this message. Assumes that caller already
|
* 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)
|
if (next_msg && next_msg->spi != pl022->cur_msg->spi)
|
||||||
next_msg = NULL;
|
next_msg = NULL;
|
||||||
if (!next_msg || pl022->cur_msg->state == STATE_ERROR)
|
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
|
else
|
||||||
pl022->next_msg_cs_active = true;
|
pl022->next_msg_cs_active = true;
|
||||||
|
|
||||||
|
@ -818,8 +837,7 @@ static void dma_callback(void *data)
|
||||||
/* Update total bytes transferred */
|
/* Update total bytes transferred */
|
||||||
msg->actual_length += pl022->cur_transfer->len;
|
msg->actual_length += pl022->cur_transfer->len;
|
||||||
if (pl022->cur_transfer->cs_change)
|
if (pl022->cur_transfer->cs_change)
|
||||||
pl022->cur_chip->
|
pl022_cs_control(pl022, SSP_CHIP_DESELECT);
|
||||||
cs_control(SSP_CHIP_DESELECT);
|
|
||||||
|
|
||||||
/* Move to next transfer */
|
/* Move to next transfer */
|
||||||
msg->state = next_transfer(pl022);
|
msg->state = next_transfer(pl022);
|
||||||
|
@ -1252,8 +1270,7 @@ static irqreturn_t pl022_interrupt_handler(int irq, void *dev_id)
|
||||||
/* Update total bytes transferred */
|
/* Update total bytes transferred */
|
||||||
msg->actual_length += pl022->cur_transfer->len;
|
msg->actual_length += pl022->cur_transfer->len;
|
||||||
if (pl022->cur_transfer->cs_change)
|
if (pl022->cur_transfer->cs_change)
|
||||||
pl022->cur_chip->
|
pl022_cs_control(pl022, SSP_CHIP_DESELECT);
|
||||||
cs_control(SSP_CHIP_DESELECT);
|
|
||||||
/* Move to next transfer */
|
/* Move to next transfer */
|
||||||
msg->state = next_transfer(pl022);
|
msg->state = next_transfer(pl022);
|
||||||
tasklet_schedule(&pl022->pump_transfers);
|
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 */
|
/* Reselect chip select only if cs_change was requested */
|
||||||
if (previous->cs_change)
|
if (previous->cs_change)
|
||||||
pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
|
pl022_cs_control(pl022, SSP_CHIP_SELECT);
|
||||||
} else {
|
} else {
|
||||||
/* STATE_START */
|
/* STATE_START */
|
||||||
message->state = STATE_RUNNING;
|
message->state = STATE_RUNNING;
|
||||||
|
@ -1377,7 +1394,7 @@ static void do_interrupt_dma_transfer(struct pl022 *pl022)
|
||||||
|
|
||||||
/* Enable target chip, if not already active */
|
/* Enable target chip, if not already active */
|
||||||
if (!pl022->next_msg_cs_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)) {
|
if (set_up_next_transfer(pl022, pl022->cur_transfer)) {
|
||||||
/* Error path */
|
/* Error path */
|
||||||
|
@ -1429,12 +1446,12 @@ static void do_polling_transfer(struct pl022 *pl022)
|
||||||
if (previous->delay_usecs)
|
if (previous->delay_usecs)
|
||||||
udelay(previous->delay_usecs);
|
udelay(previous->delay_usecs);
|
||||||
if (previous->cs_change)
|
if (previous->cs_change)
|
||||||
pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
|
pl022_cs_control(pl022, SSP_CHIP_SELECT);
|
||||||
} else {
|
} else {
|
||||||
/* STATE_START */
|
/* STATE_START */
|
||||||
message->state = STATE_RUNNING;
|
message->state = STATE_RUNNING;
|
||||||
if (!pl022->next_msg_cs_active)
|
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 */
|
/* Configuration Changing Per Transfer */
|
||||||
|
@ -1466,7 +1483,7 @@ static void do_polling_transfer(struct pl022 *pl022)
|
||||||
/* Update total byte transferred */
|
/* Update total byte transferred */
|
||||||
message->actual_length += pl022->cur_transfer->len;
|
message->actual_length += pl022->cur_transfer->len;
|
||||||
if (pl022->cur_transfer->cs_change)
|
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 */
|
/* Move to next transfer */
|
||||||
message->state = next_transfer(pl022);
|
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 */
|
/* Setup the SPI using the per chip configuration */
|
||||||
pl022->cur_chip = spi_get_ctldata(msg->spi);
|
pl022->cur_chip = spi_get_ctldata(msg->spi);
|
||||||
|
pl022->cur_cs = pl022->chipselects[msg->spi->chip_select];
|
||||||
|
|
||||||
restore_state(pl022);
|
restore_state(pl022);
|
||||||
flush(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)
|
static int pl022_setup(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct pl022_config_chip const *chip_info;
|
struct pl022_config_chip const *chip_info;
|
||||||
|
struct pl022_config_chip chip_info_dt;
|
||||||
struct chip_data *chip;
|
struct chip_data *chip;
|
||||||
struct ssp_clock_params clk_freq = { .cpsdvsr = 0, .scr = 0};
|
struct ssp_clock_params clk_freq = { .cpsdvsr = 0, .scr = 0};
|
||||||
int status = 0;
|
int status = 0;
|
||||||
struct pl022 *pl022 = spi_master_get_devdata(spi->master);
|
struct pl022 *pl022 = spi_master_get_devdata(spi->master);
|
||||||
unsigned int bits = spi->bits_per_word;
|
unsigned int bits = spi->bits_per_word;
|
||||||
u32 tmp;
|
u32 tmp;
|
||||||
|
struct device_node *np = spi->dev.of_node;
|
||||||
|
|
||||||
if (!spi->max_speed_hz)
|
if (!spi->max_speed_hz)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -1794,10 +1814,32 @@ static int pl022_setup(struct spi_device *spi)
|
||||||
chip_info = spi->controller_data;
|
chip_info = spi->controller_data;
|
||||||
|
|
||||||
if (chip_info == NULL) {
|
if (chip_info == NULL) {
|
||||||
chip_info = &pl022_default_chip_info;
|
if (np) {
|
||||||
/* spi_board_info.controller_data not is supplied */
|
chip_info_dt = pl022_default_chip_info;
|
||||||
dev_dbg(&spi->dev,
|
|
||||||
"using default controller_data settings\n");
|
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
|
} else
|
||||||
dev_dbg(&spi->dev,
|
dev_dbg(&spi->dev,
|
||||||
"using user supplied controller_data settings\n");
|
"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;
|
chip->xfer_type = chip_info->com_mode;
|
||||||
if (!chip_info->cs_control) {
|
if (!chip_info->cs_control) {
|
||||||
chip->cs_control = null_cs_control;
|
chip->cs_control = null_cs_control;
|
||||||
dev_warn(&spi->dev,
|
if (!gpio_is_valid(pl022->chipselects[spi->chip_select]))
|
||||||
"chip select function is NULL for this chip\n");
|
dev_warn(&spi->dev,
|
||||||
|
"invalid chip select\n");
|
||||||
} else
|
} else
|
||||||
chip->cs_control = chip_info->cs_control;
|
chip->cs_control = chip_info->cs_control;
|
||||||
|
|
||||||
|
@ -1986,6 +2029,34 @@ static void pl022_cleanup(struct spi_device *spi)
|
||||||
kfree(chip);
|
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
|
static int __devinit
|
||||||
pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
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 pl022_ssp_controller *platform_info = adev->dev.platform_data;
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
struct pl022 *pl022 = NULL; /*Data for this driver */
|
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,
|
dev_info(&adev->dev,
|
||||||
"ARM PL022 driver, device ID: 0x%08x\n", adev->periphid);
|
"ARM PL022 driver, device ID: 0x%08x\n", adev->periphid);
|
||||||
if (platform_info == NULL) {
|
if (!platform_info && IS_ENABLED(CONFIG_OF))
|
||||||
dev_err(&adev->dev, "probe - no platform data supplied\n");
|
platform_info = pl022_platform_data_dt_get(dev);
|
||||||
status = -ENODEV;
|
|
||||||
goto err_no_pdata;
|
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 */
|
/* Allocate master with space for data */
|
||||||
master = spi_alloc_master(dev, sizeof(struct pl022));
|
master = spi_alloc_master(dev, sizeof(struct pl022));
|
||||||
if (master == NULL) {
|
if (master == NULL) {
|
||||||
dev_err(&adev->dev, "probe - cannot alloc SPI master\n");
|
dev_err(&adev->dev, "probe - cannot alloc SPI master\n");
|
||||||
status = -ENOMEM;
|
return -ENOMEM;
|
||||||
goto err_no_master;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pl022 = spi_master_get_devdata(master);
|
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->master_info = platform_info;
|
||||||
pl022->adev = adev;
|
pl022->adev = adev;
|
||||||
pl022->vendor = id->data;
|
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
|
* Bus Number Which has been Assigned to this SSP controller
|
||||||
* on this board
|
* on this board
|
||||||
*/
|
*/
|
||||||
master->bus_num = platform_info->bus_id;
|
master->bus_num = platform_info->bus_id;
|
||||||
master->num_chipselect = platform_info->num_chipselect;
|
master->num_chipselect = num_cs;
|
||||||
master->cleanup = pl022_cleanup;
|
master->cleanup = pl022_cleanup;
|
||||||
master->setup = pl022_setup;
|
master->setup = pl022_setup;
|
||||||
master->prepare_transfer_hardware = pl022_prepare_transfer_hardware;
|
master->prepare_transfer_hardware = pl022_prepare_transfer_hardware;
|
||||||
master->transfer_one_message = pl022_transfer_one_message;
|
master->transfer_one_message = pl022_transfer_one_message;
|
||||||
master->unprepare_transfer_hardware = pl022_unprepare_transfer_hardware;
|
master->unprepare_transfer_hardware = pl022_unprepare_transfer_hardware;
|
||||||
master->rt = platform_info->rt;
|
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
|
* 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;
|
goto err_no_ioregion;
|
||||||
|
|
||||||
pl022->phybase = adev->res.start;
|
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) {
|
if (pl022->virtbase == NULL) {
|
||||||
status = -ENOMEM;
|
status = -ENOMEM;
|
||||||
goto err_no_ioremap;
|
goto err_no_ioremap;
|
||||||
|
@ -2055,7 +2188,7 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
||||||
|
|
||||||
pm_runtime_resume(dev);
|
pm_runtime_resume(dev);
|
||||||
|
|
||||||
pl022->clk = clk_get(&adev->dev, NULL);
|
pl022->clk = devm_clk_get(&adev->dev, NULL);
|
||||||
if (IS_ERR(pl022->clk)) {
|
if (IS_ERR(pl022->clk)) {
|
||||||
status = PTR_ERR(pl022->clk);
|
status = PTR_ERR(pl022->clk);
|
||||||
dev_err(&adev->dev, "could not retrieve SSP/SPI bus clock\n");
|
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));
|
SSP_CR1(pl022->virtbase));
|
||||||
load_ssp_default_config(pl022);
|
load_ssp_default_config(pl022);
|
||||||
|
|
||||||
status = request_irq(adev->irq[0], pl022_interrupt_handler, 0, "pl022",
|
status = devm_request_irq(dev, adev->irq[0], pl022_interrupt_handler,
|
||||||
pl022);
|
0, "pl022", pl022);
|
||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
dev_err(&adev->dev, "probe - cannot get IRQ (%d)\n", status);
|
dev_err(&adev->dev, "probe - cannot get IRQ (%d)\n", status);
|
||||||
goto err_no_irq;
|
goto err_no_irq;
|
||||||
|
@ -2124,22 +2257,18 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
||||||
err_spi_register:
|
err_spi_register:
|
||||||
if (platform_info->enable_dma)
|
if (platform_info->enable_dma)
|
||||||
pl022_dma_remove(pl022);
|
pl022_dma_remove(pl022);
|
||||||
|
|
||||||
free_irq(adev->irq[0], pl022);
|
|
||||||
err_no_irq:
|
err_no_irq:
|
||||||
clk_disable(pl022->clk);
|
clk_disable(pl022->clk);
|
||||||
err_no_clk_en:
|
err_no_clk_en:
|
||||||
clk_unprepare(pl022->clk);
|
clk_unprepare(pl022->clk);
|
||||||
err_clk_prep:
|
err_clk_prep:
|
||||||
clk_put(pl022->clk);
|
|
||||||
err_no_clk:
|
err_no_clk:
|
||||||
iounmap(pl022->virtbase);
|
|
||||||
err_no_ioremap:
|
err_no_ioremap:
|
||||||
amba_release_regions(adev);
|
amba_release_regions(adev);
|
||||||
err_no_ioregion:
|
err_no_ioregion:
|
||||||
|
err_no_gpio:
|
||||||
|
err_no_pinctrl:
|
||||||
spi_master_put(master);
|
spi_master_put(master);
|
||||||
err_no_master:
|
|
||||||
err_no_pdata:
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2161,20 +2290,55 @@ pl022_remove(struct amba_device *adev)
|
||||||
if (pl022->master_info->enable_dma)
|
if (pl022->master_info->enable_dma)
|
||||||
pl022_dma_remove(pl022);
|
pl022_dma_remove(pl022);
|
||||||
|
|
||||||
free_irq(adev->irq[0], pl022);
|
|
||||||
clk_disable(pl022->clk);
|
clk_disable(pl022->clk);
|
||||||
clk_unprepare(pl022->clk);
|
clk_unprepare(pl022->clk);
|
||||||
clk_put(pl022->clk);
|
|
||||||
pm_runtime_disable(&adev->dev);
|
pm_runtime_disable(&adev->dev);
|
||||||
iounmap(pl022->virtbase);
|
|
||||||
amba_release_regions(adev);
|
amba_release_regions(adev);
|
||||||
tasklet_disable(&pl022->pump_transfers);
|
tasklet_disable(&pl022->pump_transfers);
|
||||||
spi_unregister_master(pl022->master);
|
spi_unregister_master(pl022->master);
|
||||||
spi_master_put(pl022->master);
|
|
||||||
amba_set_drvdata(adev, NULL);
|
amba_set_drvdata(adev, NULL);
|
||||||
return 0;
|
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
|
#ifdef CONFIG_SUSPEND
|
||||||
static int pl022_suspend(struct device *dev)
|
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");
|
dev_warn(dev, "cannot suspend master\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
pl022_suspend_resources(pl022);
|
||||||
|
|
||||||
dev_dbg(dev, "suspended\n");
|
dev_dbg(dev, "suspended\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -2196,6 +2361,8 @@ static int pl022_resume(struct device *dev)
|
||||||
struct pl022 *pl022 = dev_get_drvdata(dev);
|
struct pl022 *pl022 = dev_get_drvdata(dev);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
pl022_resume_resources(pl022);
|
||||||
|
|
||||||
/* Start the queue running */
|
/* Start the queue running */
|
||||||
ret = spi_master_resume(pl022->master);
|
ret = spi_master_resume(pl022->master);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -2212,8 +2379,7 @@ static int pl022_runtime_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct pl022 *pl022 = dev_get_drvdata(dev);
|
struct pl022 *pl022 = dev_get_drvdata(dev);
|
||||||
|
|
||||||
clk_disable(pl022->clk);
|
pl022_suspend_resources(pl022);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2221,8 +2387,7 @@ static int pl022_runtime_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct pl022 *pl022 = dev_get_drvdata(dev);
|
struct pl022 *pl022 = dev_get_drvdata(dev);
|
||||||
|
|
||||||
clk_enable(pl022->clk);
|
pl022_resume_resources(pl022);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -611,6 +611,7 @@ static int __devinit s3c24xx_spi_probe(struct platform_device *pdev)
|
||||||
if (!pdata->set_cs) {
|
if (!pdata->set_cs) {
|
||||||
if (pdata->pin_cs < 0) {
|
if (pdata->pin_cs < 0) {
|
||||||
dev_err(&pdev->dev, "No chipselect pin\n");
|
dev_err(&pdev->dev, "No chipselect pin\n");
|
||||||
|
err = -EINVAL;
|
||||||
goto err_register;
|
goto err_register;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -976,7 +976,8 @@ err_msgq:
|
||||||
spi_set_ctldata(spi, NULL);
|
spi_set_ctldata(spi, NULL);
|
||||||
|
|
||||||
err_gpio_req:
|
err_gpio_req:
|
||||||
kfree(cs);
|
if (spi->dev.of_node)
|
||||||
|
kfree(cs);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -1409,7 +1410,7 @@ static int s3c64xx_spi_remove(struct platform_device *pdev)
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
static int s3c64xx_spi_suspend(struct device *dev)
|
static int s3c64xx_spi_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
|
struct spi_master *master = dev_get_drvdata(dev);
|
||||||
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
|
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
|
||||||
|
|
||||||
spi_master_suspend(master);
|
spi_master_suspend(master);
|
||||||
|
@ -1428,7 +1429,7 @@ static int s3c64xx_spi_suspend(struct device *dev)
|
||||||
|
|
||||||
static int s3c64xx_spi_resume(struct device *dev)
|
static int s3c64xx_spi_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
|
struct spi_master *master = dev_get_drvdata(dev);
|
||||||
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
|
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
|
||||||
struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
|
struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
|
||||||
|
|
||||||
|
@ -1452,7 +1453,7 @@ static int s3c64xx_spi_resume(struct device *dev)
|
||||||
#ifdef CONFIG_PM_RUNTIME
|
#ifdef CONFIG_PM_RUNTIME
|
||||||
static int s3c64xx_spi_runtime_suspend(struct device *dev)
|
static int s3c64xx_spi_runtime_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
|
struct spi_master *master = dev_get_drvdata(dev);
|
||||||
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
|
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
|
||||||
|
|
||||||
clk_disable(sdd->clk);
|
clk_disable(sdd->clk);
|
||||||
|
@ -1463,7 +1464,7 @@ static int s3c64xx_spi_runtime_suspend(struct device *dev)
|
||||||
|
|
||||||
static int s3c64xx_spi_runtime_resume(struct device *dev)
|
static int s3c64xx_spi_runtime_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
|
struct spi_master *master = dev_get_drvdata(dev);
|
||||||
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
|
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
|
||||||
|
|
||||||
clk_enable(sdd->src_clk);
|
clk_enable(sdd->src_clk);
|
||||||
|
|
|
@ -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);
|
ret = spi_register_master(master);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "spi_register_master error.\n");
|
dev_err(&pdev->dev, "spi_register_master error.\n");
|
||||||
goto error2;
|
goto error1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pm_runtime_enable(&pdev->dev);
|
pm_runtime_enable(&pdev->dev);
|
||||||
|
@ -292,8 +292,6 @@ static int __devinit hspi_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error2:
|
|
||||||
devm_iounmap(hspi->dev, hspi->addr);
|
|
||||||
error1:
|
error1:
|
||||||
clk_put(clk);
|
clk_put(clk);
|
||||||
error0:
|
error0:
|
||||||
|
@ -310,7 +308,6 @@ static int __devexit hspi_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
clk_put(hspi->clk);
|
clk_put(hspi->clk);
|
||||||
spi_unregister_master(hspi->master);
|
spi_unregister_master(hspi->master);
|
||||||
devm_iounmap(hspi->dev, hspi->addr);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -594,9 +594,7 @@ static int __devexit stmp_spi_remove(struct platform_device *dev)
|
||||||
struct stmp_spi *ss;
|
struct stmp_spi *ss;
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
|
|
||||||
master = platform_get_drvdata(dev);
|
master = spi_master_get(platform_get_drvdata(dev));
|
||||||
if (master == NULL)
|
|
||||||
goto out0;
|
|
||||||
ss = spi_master_get_devdata(master);
|
ss = spi_master_get_devdata(master);
|
||||||
|
|
||||||
spi_unregister_master(master);
|
spi_unregister_master(master);
|
||||||
|
@ -609,8 +607,6 @@ static int __devexit stmp_spi_remove(struct platform_device *dev)
|
||||||
destroy_workqueue(ss->workqueue);
|
destroy_workqueue(ss->workqueue);
|
||||||
iounmap(ss->regs);
|
iounmap(ss->regs);
|
||||||
spi_master_put(master);
|
spi_master_put(master);
|
||||||
platform_set_drvdata(dev, NULL);
|
|
||||||
out0:
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -652,7 +652,7 @@ static int __devexit spi_tegra_remove(struct platform_device *pdev)
|
||||||
struct spi_tegra_data *tspi;
|
struct spi_tegra_data *tspi;
|
||||||
struct resource *r;
|
struct resource *r;
|
||||||
|
|
||||||
master = dev_get_drvdata(&pdev->dev);
|
master = spi_master_get(dev_get_drvdata(&pdev->dev));
|
||||||
tspi = spi_master_get_devdata(master);
|
tspi = spi_master_get_devdata(master);
|
||||||
|
|
||||||
spi_unregister_master(master);
|
spi_unregister_master(master);
|
||||||
|
@ -668,6 +668,8 @@ static int __devexit spi_tegra_remove(struct platform_device *pdev)
|
||||||
clk_put(tspi->clk);
|
clk_put(tspi->clk);
|
||||||
iounmap(tspi->base);
|
iounmap(tspi->base);
|
||||||
|
|
||||||
|
spi_master_put(master);
|
||||||
|
|
||||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
release_mem_region(r->start, resource_size(r));
|
release_mem_region(r->start, resource_size(r));
|
||||||
|
|
||||||
|
|
|
@ -316,18 +316,7 @@ static struct spi_driver tle62x0_driver = {
|
||||||
.remove = __devexit_p(tle62x0_remove),
|
.remove = __devexit_p(tle62x0_remove),
|
||||||
};
|
};
|
||||||
|
|
||||||
static __init int tle62x0_init(void)
|
module_spi_driver(tle62x0_driver);
|
||||||
{
|
|
||||||
return spi_register_driver(&tle62x0_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
static __exit void tle62x0_exit(void)
|
|
||||||
{
|
|
||||||
spi_unregister_driver(&tle62x0_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
module_init(tle62x0_init);
|
|
||||||
module_exit(tle62x0_exit);
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
|
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
|
||||||
MODULE_DESCRIPTION("TLE62x0 SPI driver");
|
MODULE_DESCRIPTION("TLE62x0 SPI driver");
|
||||||
|
|
|
@ -1536,8 +1536,6 @@ static int __devexit pch_spi_pd_remove(struct platform_device *plat_dev)
|
||||||
|
|
||||||
pci_iounmap(board_dat->pdev, data->io_remap_addr);
|
pci_iounmap(board_dat->pdev, data->io_remap_addr);
|
||||||
spi_unregister_master(data->master);
|
spi_unregister_master(data->master);
|
||||||
spi_master_put(data->master);
|
|
||||||
platform_set_drvdata(plat_dev, NULL);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -244,6 +244,7 @@ struct dma_chan;
|
||||||
* indicates no delay and the device will be suspended immediately.
|
* indicates no delay and the device will be suspended immediately.
|
||||||
* @rt: indicates the controller should run the message pump with realtime
|
* @rt: indicates the controller should run the message pump with realtime
|
||||||
* priority to minimise the transfer latency on the bus.
|
* priority to minimise the transfer latency on the bus.
|
||||||
|
* @chipselects: list of <num_chipselects> chip select gpios
|
||||||
*/
|
*/
|
||||||
struct pl022_ssp_controller {
|
struct pl022_ssp_controller {
|
||||||
u16 bus_id;
|
u16 bus_id;
|
||||||
|
@ -254,6 +255,7 @@ struct pl022_ssp_controller {
|
||||||
void *dma_tx_param;
|
void *dma_tx_param;
|
||||||
int autosuspend_delay;
|
int autosuspend_delay;
|
||||||
bool rt;
|
bool rt;
|
||||||
|
int *chipselects;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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;
|
||||||
|
};
|
|
@ -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