Merge branches 'spi-drivers' and 'spi-mxs' into spi-next

This commit is contained in:
Mark Brown 2012-09-28 14:05:29 +01:00
commit f4b81dd83e
33 changed files with 2148 additions and 638 deletions

View File

@ -0,0 +1,22 @@
* Freescale MX233/MX28 SSP/SPI
Required properties:
- compatible: Should be "fsl,<soc>-spi", where soc is "imx23" or "imx28"
- reg: Offset and length of the register set for the device
- interrupts: Should contain SSP interrupts (error irq first, dma irq second)
- fsl,ssp-dma-channel: APBX DMA channel for the SSP
Optional properties:
- clock-frequency : Input clock frequency to the SPI block in Hz.
Default is 160000000 Hz.
Example:
ssp0: ssp@80010000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx28-spi";
reg = <0x80010000 0x2000>;
interrupts = <96 82>;
fsl,ssp-dma-channel = <0>;
};

View File

@ -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.

View File

@ -0,0 +1,29 @@
SPI-GPIO devicetree bindings
Required properties:
- compatible: should be set to "spi-gpio"
- #address-cells: should be set to <0x1>
- ranges
- gpio-sck: GPIO spec for the SCK line to use
- gpio-miso: GPIO spec for the MISO line to use
- gpio-mosi: GPIO spec for the MOSI line to use
- cs-gpios: GPIOs to use for chipselect lines
- num-chipselects: number of chipselect lines
Example:
spi {
compatible = "spi-gpio";
#address-cells = <0x1>;
ranges;
gpio-sck = <&gpio 95 0>;
gpio-miso = <&gpio 98 0>;
gpio-mosi = <&gpio 97 0>;
cs-gpios = <&gpio 125 0>;
num-chipselects = <1>;
/* clients */
};

View File

@ -0,0 +1,23 @@
NXP SC18IS602/SCIS603
Required properties:
- compatible : Should be one of
"nxp,sc18is602"
"nxp,sc18is602b"
"nxp,sc18is603"
- reg: I2C bus address
Optional properties:
- clock-frequency : external oscillator clock frequency. If not
specified, the SC18IS602 default frequency (7372000) will be used.
The clock-frequency property is relevant and needed only if the chip has an
external oscillator (SC18IS603).
Example:
sc18is603@28 {
compatible = "nxp,sc18is603";
reg = <0x28>;
clock-frequency = <14744000>;
}

View File

@ -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

View File

@ -0,0 +1,36 @@
Kernel driver spi-sc18is602
===========================
Supported chips:
* NXP SI18IS602/602B/603
Datasheet: http://www.nxp.com/documents/data_sheet/SC18IS602_602B_603.pdf
Author:
Guenter Roeck <linux@roeck-us.net>
Description
-----------
This driver provides connects a NXP SC18IS602/603 I2C-bus to SPI bridge to the
kernel's SPI core subsystem.
The driver does not probe for supported chips, since the SI18IS602/603 does not
support Chip ID registers. You will have to instantiate the devices explicitly.
Please see Documentation/i2c/instantiating-devices for details.
Usage Notes
-----------
This driver requires the I2C adapter driver to support raw I2C messages. I2C
adapter drivers which can only handle the SMBus protocol are not supported.
The maximum SPI message size supported by SC18IS602/603 is 200 bytes. Attempts
to initiate longer transfers will fail with -EINVAL. EEPROM read operations and
similar large accesses have to be split into multiple chunks of no more than
200 bytes per SPI message (128 bytes of data per message is recommended). This
means that programs such as "cp" or "od", which automatically use large block
sizes to access a device, can not be used directly to read data from EEPROM.
Programs such as dd, where the block size can be specified, should be used
instead.

View File

@ -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,
}, },

View File

@ -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

62
drivers/clk/mxs/clk-ssp.c Normal file
View File

@ -0,0 +1,62 @@
/*
* Copyright 2012 DENX Software Engineering, GmbH
*
* Pulled from code:
* Portions copyright (C) 2003 Russell King, PXA MMCI Driver
* Portions copyright (C) 2004-2005 Pierre Ossman, W83L51xD SD/MMC driver
*
* Copyright 2008 Embedded Alley Solutions, Inc.
* Copyright 2009-2011 Freescale Semiconductor, Inc.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/spi/mxs-spi.h>
void mxs_ssp_set_clk_rate(struct mxs_ssp *ssp, unsigned int rate)
{
unsigned int ssp_clk, ssp_sck;
u32 clock_divide, clock_rate;
u32 val;
ssp_clk = clk_get_rate(ssp->clk);
for (clock_divide = 2; clock_divide <= 254; clock_divide += 2) {
clock_rate = DIV_ROUND_UP(ssp_clk, rate * clock_divide);
clock_rate = (clock_rate > 0) ? clock_rate - 1 : 0;
if (clock_rate <= 255)
break;
}
if (clock_divide > 254) {
dev_err(ssp->dev,
"%s: cannot set clock to %d\n", __func__, rate);
return;
}
ssp_sck = ssp_clk / clock_divide / (1 + clock_rate);
val = readl(ssp->base + HW_SSP_TIMING(ssp));
val &= ~(BM_SSP_TIMING_CLOCK_DIVIDE | BM_SSP_TIMING_CLOCK_RATE);
val |= BF_SSP(clock_divide, TIMING_CLOCK_DIVIDE);
val |= BF_SSP(clock_rate, TIMING_CLOCK_RATE);
writel(val, ssp->base + HW_SSP_TIMING(ssp));
ssp->clk_rate = ssp_sck;
dev_dbg(ssp->dev,
"%s: clock_divide %d, clock_rate %d, ssp_clk %d, rate_actual %d, rate_requested %d\n",
__func__, clock_divide, clock_rate, ssp_clk, ssp_sck, rate);
}
EXPORT_SYMBOL_GPL(mxs_ssp_set_clk_rate);

View File

@ -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,

View File

@ -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)

View File

@ -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

View File

@ -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);

View File

@ -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),
}; };

View File

@ -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);

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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;
} }

674
drivers/spi/spi-mxs.c Normal file
View File

@ -0,0 +1,674 @@
/*
* Freescale MXS SPI master driver
*
* Copyright 2012 DENX Software Engineering, GmbH.
* Copyright 2012 Freescale Semiconductor, Inc.
* Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
*
* Rework and transition to new API by:
* Marek Vasut <marex@denx.de>
*
* Based on previous attempt by:
* Fabio Estevam <fabio.estevam@freescale.com>
*
* Based on code from U-Boot bootloader by:
* Marek Vasut <marex@denx.de>
*
* Based on spi-stmp.c, which is:
* Author: Dmitry Pervushin <dimka@embeddedalley.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/highmem.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/completion.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/module.h>
#include <linux/pinctrl/consumer.h>
#include <linux/stmp_device.h>
#include <linux/spi/spi.h>
#include <linux/spi/mxs-spi.h>
#define DRIVER_NAME "mxs-spi"
/* Use 10S timeout for very long transfers, it should suffice. */
#define SSP_TIMEOUT 10000
#define SG_MAXLEN 0xff00
struct mxs_spi {
struct mxs_ssp ssp;
struct completion c;
};
static int mxs_spi_setup_transfer(struct spi_device *dev,
struct spi_transfer *t)
{
struct mxs_spi *spi = spi_master_get_devdata(dev->master);
struct mxs_ssp *ssp = &spi->ssp;
uint8_t bits_per_word;
uint32_t hz = 0;
bits_per_word = dev->bits_per_word;
if (t && t->bits_per_word)
bits_per_word = t->bits_per_word;
if (bits_per_word != 8) {
dev_err(&dev->dev, "%s, unsupported bits_per_word=%d\n",
__func__, bits_per_word);
return -EINVAL;
}
hz = dev->max_speed_hz;
if (t && t->speed_hz)
hz = min(hz, t->speed_hz);
if (hz == 0) {
dev_err(&dev->dev, "Cannot continue with zero clock\n");
return -EINVAL;
}
mxs_ssp_set_clk_rate(ssp, hz);
writel(BF_SSP_CTRL1_SSP_MODE(BV_SSP_CTRL1_SSP_MODE__SPI) |
BF_SSP_CTRL1_WORD_LENGTH
(BV_SSP_CTRL1_WORD_LENGTH__EIGHT_BITS) |
((dev->mode & SPI_CPOL) ? BM_SSP_CTRL1_POLARITY : 0) |
((dev->mode & SPI_CPHA) ? BM_SSP_CTRL1_PHASE : 0),
ssp->base + HW_SSP_CTRL1(ssp));
writel(0x0, ssp->base + HW_SSP_CMD0);
writel(0x0, ssp->base + HW_SSP_CMD1);
return 0;
}
static int mxs_spi_setup(struct spi_device *dev)
{
int err = 0;
if (!dev->bits_per_word)
dev->bits_per_word = 8;
if (dev->mode & ~(SPI_CPOL | SPI_CPHA))
return -EINVAL;
err = mxs_spi_setup_transfer(dev, NULL);
if (err) {
dev_err(&dev->dev,
"Failed to setup transfer, error = %d\n", err);
}
return err;
}
static uint32_t mxs_spi_cs_to_reg(unsigned cs)
{
uint32_t select = 0;
/*
* i.MX28 Datasheet: 17.10.1: HW_SSP_CTRL0
*
* The bits BM_SSP_CTRL0_WAIT_FOR_CMD and BM_SSP_CTRL0_WAIT_FOR_IRQ
* in HW_SSP_CTRL0 register do have multiple usage, please refer to
* the datasheet for further details. In SPI mode, they are used to
* toggle the chip-select lines (nCS pins).
*/
if (cs & 1)
select |= BM_SSP_CTRL0_WAIT_FOR_CMD;
if (cs & 2)
select |= BM_SSP_CTRL0_WAIT_FOR_IRQ;
return select;
}
static void mxs_spi_set_cs(struct mxs_spi *spi, unsigned cs)
{
const uint32_t mask =
BM_SSP_CTRL0_WAIT_FOR_CMD | BM_SSP_CTRL0_WAIT_FOR_IRQ;
uint32_t select;
struct mxs_ssp *ssp = &spi->ssp;
writel(mask, ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR);
select = mxs_spi_cs_to_reg(cs);
writel(select, ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
}
static inline void mxs_spi_enable(struct mxs_spi *spi)
{
struct mxs_ssp *ssp = &spi->ssp;
writel(BM_SSP_CTRL0_LOCK_CS,
ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
writel(BM_SSP_CTRL0_IGNORE_CRC,
ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR);
}
static inline void mxs_spi_disable(struct mxs_spi *spi)
{
struct mxs_ssp *ssp = &spi->ssp;
writel(BM_SSP_CTRL0_LOCK_CS,
ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR);
writel(BM_SSP_CTRL0_IGNORE_CRC,
ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
}
static int mxs_ssp_wait(struct mxs_spi *spi, int offset, int mask, bool set)
{
const unsigned long timeout = jiffies + msecs_to_jiffies(SSP_TIMEOUT);
struct mxs_ssp *ssp = &spi->ssp;
uint32_t reg;
do {
reg = readl_relaxed(ssp->base + offset);
if (!set)
reg = ~reg;
reg &= mask;
if (reg == mask)
return 0;
} while (time_before(jiffies, timeout));
return -ETIMEDOUT;
}
static void mxs_ssp_dma_irq_callback(void *param)
{
struct mxs_spi *spi = param;
complete(&spi->c);
}
static irqreturn_t mxs_ssp_irq_handler(int irq, void *dev_id)
{
struct mxs_ssp *ssp = dev_id;
dev_err(ssp->dev, "%s[%i] CTRL1=%08x STATUS=%08x\n",
__func__, __LINE__,
readl(ssp->base + HW_SSP_CTRL1(ssp)),
readl(ssp->base + HW_SSP_STATUS(ssp)));
return IRQ_HANDLED;
}
static int mxs_spi_txrx_dma(struct mxs_spi *spi, int cs,
unsigned char *buf, int len,
int *first, int *last, int write)
{
struct mxs_ssp *ssp = &spi->ssp;
struct dma_async_tx_descriptor *desc = NULL;
const bool vmalloced_buf = is_vmalloc_addr(buf);
const int desc_len = vmalloced_buf ? PAGE_SIZE : SG_MAXLEN;
const int sgs = DIV_ROUND_UP(len, desc_len);
int sg_count;
int min, ret;
uint32_t ctrl0;
struct page *vm_page;
void *sg_buf;
struct {
uint32_t pio[4];
struct scatterlist sg;
} *dma_xfer;
if (!len)
return -EINVAL;
dma_xfer = kzalloc(sizeof(*dma_xfer) * sgs, GFP_KERNEL);
if (!dma_xfer)
return -ENOMEM;
INIT_COMPLETION(spi->c);
ctrl0 = readl(ssp->base + HW_SSP_CTRL0);
ctrl0 |= BM_SSP_CTRL0_DATA_XFER | mxs_spi_cs_to_reg(cs);
if (*first)
ctrl0 |= BM_SSP_CTRL0_LOCK_CS;
if (!write)
ctrl0 |= BM_SSP_CTRL0_READ;
/* Queue the DMA data transfer. */
for (sg_count = 0; sg_count < sgs; sg_count++) {
min = min(len, desc_len);
/* Prepare the transfer descriptor. */
if ((sg_count + 1 == sgs) && *last)
ctrl0 |= BM_SSP_CTRL0_IGNORE_CRC;
if (ssp->devid == IMX23_SSP)
ctrl0 |= min;
dma_xfer[sg_count].pio[0] = ctrl0;
dma_xfer[sg_count].pio[3] = min;
if (vmalloced_buf) {
vm_page = vmalloc_to_page(buf);
if (!vm_page) {
ret = -ENOMEM;
goto err_vmalloc;
}
sg_buf = page_address(vm_page) +
((size_t)buf & ~PAGE_MASK);
} else {
sg_buf = buf;
}
sg_init_one(&dma_xfer[sg_count].sg, sg_buf, min);
ret = dma_map_sg(ssp->dev, &dma_xfer[sg_count].sg, 1,
write ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
len -= min;
buf += min;
/* Queue the PIO register write transfer. */
desc = dmaengine_prep_slave_sg(ssp->dmach,
(struct scatterlist *)dma_xfer[sg_count].pio,
(ssp->devid == IMX23_SSP) ? 1 : 4,
DMA_TRANS_NONE,
sg_count ? DMA_PREP_INTERRUPT : 0);
if (!desc) {
dev_err(ssp->dev,
"Failed to get PIO reg. write descriptor.\n");
ret = -EINVAL;
goto err_mapped;
}
desc = dmaengine_prep_slave_sg(ssp->dmach,
&dma_xfer[sg_count].sg, 1,
write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
dev_err(ssp->dev,
"Failed to get DMA data write descriptor.\n");
ret = -EINVAL;
goto err_mapped;
}
}
/*
* The last descriptor must have this callback,
* to finish the DMA transaction.
*/
desc->callback = mxs_ssp_dma_irq_callback;
desc->callback_param = spi;
/* Start the transfer. */
dmaengine_submit(desc);
dma_async_issue_pending(ssp->dmach);
ret = wait_for_completion_timeout(&spi->c,
msecs_to_jiffies(SSP_TIMEOUT));
if (!ret) {
dev_err(ssp->dev, "DMA transfer timeout\n");
ret = -ETIMEDOUT;
goto err_vmalloc;
}
ret = 0;
err_vmalloc:
while (--sg_count >= 0) {
err_mapped:
dma_unmap_sg(ssp->dev, &dma_xfer[sg_count].sg, 1,
write ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
}
kfree(dma_xfer);
return ret;
}
static int mxs_spi_txrx_pio(struct mxs_spi *spi, int cs,
unsigned char *buf, int len,
int *first, int *last, int write)
{
struct mxs_ssp *ssp = &spi->ssp;
if (*first)
mxs_spi_enable(spi);
mxs_spi_set_cs(spi, cs);
while (len--) {
if (*last && len == 0)
mxs_spi_disable(spi);
if (ssp->devid == IMX23_SSP) {
writel(BM_SSP_CTRL0_XFER_COUNT,
ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR);
writel(1,
ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
} else {
writel(1, ssp->base + HW_SSP_XFER_SIZE);
}
if (write)
writel(BM_SSP_CTRL0_READ,
ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR);
else
writel(BM_SSP_CTRL0_READ,
ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
writel(BM_SSP_CTRL0_RUN,
ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
if (mxs_ssp_wait(spi, HW_SSP_CTRL0, BM_SSP_CTRL0_RUN, 1))
return -ETIMEDOUT;
if (write)
writel(*buf, ssp->base + HW_SSP_DATA(ssp));
writel(BM_SSP_CTRL0_DATA_XFER,
ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
if (!write) {
if (mxs_ssp_wait(spi, HW_SSP_STATUS(ssp),
BM_SSP_STATUS_FIFO_EMPTY, 0))
return -ETIMEDOUT;
*buf = (readl(ssp->base + HW_SSP_DATA(ssp)) & 0xff);
}
if (mxs_ssp_wait(spi, HW_SSP_CTRL0, BM_SSP_CTRL0_RUN, 0))
return -ETIMEDOUT;
buf++;
}
if (len <= 0)
return 0;
return -ETIMEDOUT;
}
static int mxs_spi_transfer_one(struct spi_master *master,
struct spi_message *m)
{
struct mxs_spi *spi = spi_master_get_devdata(master);
struct mxs_ssp *ssp = &spi->ssp;
int first, last;
struct spi_transfer *t, *tmp_t;
int status = 0;
int cs;
first = last = 0;
cs = m->spi->chip_select;
list_for_each_entry_safe(t, tmp_t, &m->transfers, transfer_list) {
status = mxs_spi_setup_transfer(m->spi, t);
if (status)
break;
if (&t->transfer_list == m->transfers.next)
first = 1;
if (&t->transfer_list == m->transfers.prev)
last = 1;
if ((t->rx_buf && t->tx_buf) || (t->rx_dma && t->tx_dma)) {
dev_err(ssp->dev,
"Cannot send and receive simultaneously\n");
status = -EINVAL;
break;
}
/*
* Small blocks can be transfered via PIO.
* Measured by empiric means:
*
* dd if=/dev/mtdblock0 of=/dev/null bs=1024k count=1
*
* DMA only: 2.164808 seconds, 473.0KB/s
* Combined: 1.676276 seconds, 610.9KB/s
*/
if (t->len < 32) {
writel(BM_SSP_CTRL1_DMA_ENABLE,
ssp->base + HW_SSP_CTRL1(ssp) +
STMP_OFFSET_REG_CLR);
if (t->tx_buf)
status = mxs_spi_txrx_pio(spi, cs,
(void *)t->tx_buf,
t->len, &first, &last, 1);
if (t->rx_buf)
status = mxs_spi_txrx_pio(spi, cs,
t->rx_buf, t->len,
&first, &last, 0);
} else {
writel(BM_SSP_CTRL1_DMA_ENABLE,
ssp->base + HW_SSP_CTRL1(ssp) +
STMP_OFFSET_REG_SET);
if (t->tx_buf)
status = mxs_spi_txrx_dma(spi, cs,
(void *)t->tx_buf, t->len,
&first, &last, 1);
if (t->rx_buf)
status = mxs_spi_txrx_dma(spi, cs,
t->rx_buf, t->len,
&first, &last, 0);
}
if (status) {
stmp_reset_block(ssp->base);
break;
}
m->actual_length += t->len;
first = last = 0;
}
m->status = 0;
spi_finalize_current_message(master);
return status;
}
static bool mxs_ssp_dma_filter(struct dma_chan *chan, void *param)
{
struct mxs_ssp *ssp = param;
if (!mxs_dma_is_apbh(chan))
return false;
if (chan->chan_id != ssp->dma_channel)
return false;
chan->private = &ssp->dma_data;
return true;
}
static const struct of_device_id mxs_spi_dt_ids[] = {
{ .compatible = "fsl,imx23-spi", .data = (void *) IMX23_SSP, },
{ .compatible = "fsl,imx28-spi", .data = (void *) IMX28_SSP, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mxs_spi_dt_ids);
static int __devinit mxs_spi_probe(struct platform_device *pdev)
{
const struct of_device_id *of_id =
of_match_device(mxs_spi_dt_ids, &pdev->dev);
struct device_node *np = pdev->dev.of_node;
struct spi_master *master;
struct mxs_spi *spi;
struct mxs_ssp *ssp;
struct resource *iores, *dmares;
struct pinctrl *pinctrl;
struct clk *clk;
void __iomem *base;
int devid, dma_channel, clk_freq;
int ret = 0, irq_err, irq_dma;
dma_cap_mask_t mask;
/*
* Default clock speed for the SPI core. 160MHz seems to
* work reasonably well with most SPI flashes, so use this
* as a default. Override with "clock-frequency" DT prop.
*/
const int clk_freq_default = 160000000;
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq_err = platform_get_irq(pdev, 0);
irq_dma = platform_get_irq(pdev, 1);
if (!iores || irq_err < 0 || irq_dma < 0)
return -EINVAL;
base = devm_request_and_ioremap(&pdev->dev, iores);
if (!base)
return -EADDRNOTAVAIL;
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
if (IS_ERR(pinctrl))
return PTR_ERR(pinctrl);
clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(clk))
return PTR_ERR(clk);
if (np) {
devid = (enum mxs_ssp_id) of_id->data;
/*
* TODO: This is a temporary solution and should be changed
* to use generic DMA binding later when the helpers get in.
*/
ret = of_property_read_u32(np, "fsl,ssp-dma-channel",
&dma_channel);
if (ret) {
dev_err(&pdev->dev,
"Failed to get DMA channel\n");
return -EINVAL;
}
ret = of_property_read_u32(np, "clock-frequency",
&clk_freq);
if (ret)
clk_freq = clk_freq_default;
} else {
dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!dmares)
return -EINVAL;
devid = pdev->id_entry->driver_data;
dma_channel = dmares->start;
clk_freq = clk_freq_default;
}
master = spi_alloc_master(&pdev->dev, sizeof(*spi));
if (!master)
return -ENOMEM;
master->transfer_one_message = mxs_spi_transfer_one;
master->setup = mxs_spi_setup;
master->mode_bits = SPI_CPOL | SPI_CPHA;
master->num_chipselect = 3;
master->dev.of_node = np;
master->flags = SPI_MASTER_HALF_DUPLEX;
spi = spi_master_get_devdata(master);
ssp = &spi->ssp;
ssp->dev = &pdev->dev;
ssp->clk = clk;
ssp->base = base;
ssp->devid = devid;
ssp->dma_channel = dma_channel;
init_completion(&spi->c);
ret = devm_request_irq(&pdev->dev, irq_err, mxs_ssp_irq_handler, 0,
DRIVER_NAME, ssp);
if (ret)
goto out_master_free;
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
ssp->dma_data.chan_irq = irq_dma;
ssp->dmach = dma_request_channel(mask, mxs_ssp_dma_filter, ssp);
if (!ssp->dmach) {
dev_err(ssp->dev, "Failed to request DMA\n");
goto out_master_free;
}
clk_prepare_enable(ssp->clk);
clk_set_rate(ssp->clk, clk_freq);
ssp->clk_rate = clk_get_rate(ssp->clk) / 1000;
stmp_reset_block(ssp->base);
platform_set_drvdata(pdev, master);
ret = spi_register_master(master);
if (ret) {
dev_err(&pdev->dev, "Cannot register SPI master, %d\n", ret);
goto out_free_dma;
}
return 0;
out_free_dma:
dma_release_channel(ssp->dmach);
clk_disable_unprepare(ssp->clk);
out_master_free:
spi_master_put(master);
return ret;
}
static int __devexit mxs_spi_remove(struct platform_device *pdev)
{
struct spi_master *master;
struct mxs_spi *spi;
struct mxs_ssp *ssp;
master = spi_master_get(platform_get_drvdata(pdev));
spi = spi_master_get_devdata(master);
ssp = &spi->ssp;
spi_unregister_master(master);
dma_release_channel(ssp->dmach);
clk_disable_unprepare(ssp->clk);
spi_master_put(master);
return 0;
}
static struct platform_driver mxs_spi_driver = {
.probe = mxs_spi_probe,
.remove = __devexit_p(mxs_spi_remove),
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = mxs_spi_dt_ids,
},
};
module_platform_driver(mxs_spi_driver);
MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
MODULE_DESCRIPTION("MXS SPI master driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:mxs-spi");

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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;
} }

View File

@ -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);

364
drivers/spi/spi-sc18is602.c Normal file
View File

@ -0,0 +1,364 @@
/*
* NXP SC18IS602/603 SPI driver
*
* Copyright (C) Guenter Roeck <linux@roeck-us.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/platform_data/sc18is602.h>
enum chips { sc18is602, sc18is602b, sc18is603 };
#define SC18IS602_BUFSIZ 200
#define SC18IS602_CLOCK 7372000
#define SC18IS602_MODE_CPHA BIT(2)
#define SC18IS602_MODE_CPOL BIT(3)
#define SC18IS602_MODE_LSB_FIRST BIT(5)
#define SC18IS602_MODE_CLOCK_DIV_4 0x0
#define SC18IS602_MODE_CLOCK_DIV_16 0x1
#define SC18IS602_MODE_CLOCK_DIV_64 0x2
#define SC18IS602_MODE_CLOCK_DIV_128 0x3
struct sc18is602 {
struct spi_master *master;
struct device *dev;
u8 ctrl;
u32 freq;
u32 speed;
/* I2C data */
struct i2c_client *client;
enum chips id;
u8 buffer[SC18IS602_BUFSIZ + 1];
int tlen; /* Data queued for tx in buffer */
int rindex; /* Receive data index in buffer */
};
static int sc18is602_wait_ready(struct sc18is602 *hw, int len)
{
int i, err;
int usecs = 1000000 * len / hw->speed + 1;
u8 dummy[1];
for (i = 0; i < 10; i++) {
err = i2c_master_recv(hw->client, dummy, 1);
if (err >= 0)
return 0;
usleep_range(usecs, usecs * 2);
}
return -ETIMEDOUT;
}
static int sc18is602_txrx(struct sc18is602 *hw, struct spi_message *msg,
struct spi_transfer *t, bool do_transfer)
{
unsigned int len = t->len;
int ret;
if (hw->tlen == 0) {
/* First byte (I2C command) is chip select */
hw->buffer[0] = 1 << msg->spi->chip_select;
hw->tlen = 1;
hw->rindex = 0;
}
/*
* We can not immediately send data to the chip, since each I2C message
* resembles a full SPI message (from CS active to CS inactive).
* Enqueue messages up to the first read or until do_transfer is true.
*/
if (t->tx_buf) {
memcpy(&hw->buffer[hw->tlen], t->tx_buf, len);
hw->tlen += len;
if (t->rx_buf)
do_transfer = true;
else
hw->rindex = hw->tlen - 1;
} else if (t->rx_buf) {
/*
* For receive-only transfers we still need to perform a dummy
* write to receive data from the SPI chip.
* Read data starts at the end of transmit data (minus 1 to
* account for CS).
*/
hw->rindex = hw->tlen - 1;
memset(&hw->buffer[hw->tlen], 0, len);
hw->tlen += len;
do_transfer = true;
}
if (do_transfer && hw->tlen > 1) {
ret = sc18is602_wait_ready(hw, SC18IS602_BUFSIZ);
if (ret < 0)
return ret;
ret = i2c_master_send(hw->client, hw->buffer, hw->tlen);
if (ret < 0)
return ret;
if (ret != hw->tlen)
return -EIO;
if (t->rx_buf) {
int rlen = hw->rindex + len;
ret = sc18is602_wait_ready(hw, hw->tlen);
if (ret < 0)
return ret;
ret = i2c_master_recv(hw->client, hw->buffer, rlen);
if (ret < 0)
return ret;
if (ret != rlen)
return -EIO;
memcpy(t->rx_buf, &hw->buffer[hw->rindex], len);
}
hw->tlen = 0;
}
return len;
}
static int sc18is602_setup_transfer(struct sc18is602 *hw, u32 hz, u8 mode)
{
u8 ctrl = 0;
int ret;
if (mode & SPI_CPHA)
ctrl |= SC18IS602_MODE_CPHA;
if (mode & SPI_CPOL)
ctrl |= SC18IS602_MODE_CPOL;
if (mode & SPI_LSB_FIRST)
ctrl |= SC18IS602_MODE_LSB_FIRST;
/* Find the closest clock speed */
if (hz >= hw->freq / 4) {
ctrl |= SC18IS602_MODE_CLOCK_DIV_4;
hw->speed = hw->freq / 4;
} else if (hz >= hw->freq / 16) {
ctrl |= SC18IS602_MODE_CLOCK_DIV_16;
hw->speed = hw->freq / 16;
} else if (hz >= hw->freq / 64) {
ctrl |= SC18IS602_MODE_CLOCK_DIV_64;
hw->speed = hw->freq / 64;
} else {
ctrl |= SC18IS602_MODE_CLOCK_DIV_128;
hw->speed = hw->freq / 128;
}
/*
* Don't do anything if the control value did not change. The initial
* value of 0xff for hw->ctrl ensures that the correct mode will be set
* with the first call to this function.
*/
if (ctrl == hw->ctrl)
return 0;
ret = i2c_smbus_write_byte_data(hw->client, 0xf0, ctrl);
if (ret < 0)
return ret;
hw->ctrl = ctrl;
return 0;
}
static int sc18is602_check_transfer(struct spi_device *spi,
struct spi_transfer *t, int tlen)
{
int bpw;
uint32_t hz;
if (t && t->len + tlen > SC18IS602_BUFSIZ)
return -EINVAL;
bpw = spi->bits_per_word;
if (t && t->bits_per_word)
bpw = t->bits_per_word;
if (bpw != 8)
return -EINVAL;
hz = spi->max_speed_hz;
if (t && t->speed_hz)
hz = t->speed_hz;
if (hz == 0)
return -EINVAL;
return 0;
}
static int sc18is602_transfer_one(struct spi_master *master,
struct spi_message *m)
{
struct sc18is602 *hw = spi_master_get_devdata(master);
struct spi_device *spi = m->spi;
struct spi_transfer *t;
int status = 0;
/* SC18IS602 does not support CS2 */
if (hw->id == sc18is602 && spi->chip_select == 2) {
status = -ENXIO;
goto error;
}
hw->tlen = 0;
list_for_each_entry(t, &m->transfers, transfer_list) {
u32 hz = t->speed_hz ? : spi->max_speed_hz;
bool do_transfer;
status = sc18is602_check_transfer(spi, t, hw->tlen);
if (status < 0)
break;
status = sc18is602_setup_transfer(hw, hz, spi->mode);
if (status < 0)
break;
do_transfer = t->cs_change || list_is_last(&t->transfer_list,
&m->transfers);
if (t->len) {
status = sc18is602_txrx(hw, m, t, do_transfer);
if (status < 0)
break;
m->actual_length += status;
}
status = 0;
if (t->delay_usecs)
udelay(t->delay_usecs);
}
error:
m->status = status;
spi_finalize_current_message(master);
return status;
}
static int sc18is602_setup(struct spi_device *spi)
{
if (!spi->bits_per_word)
spi->bits_per_word = 8;
if (spi->mode & ~(SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST))
return -EINVAL;
return sc18is602_check_transfer(spi, NULL, 0);
}
static int sc18is602_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct device_node *np = dev->of_node;
struct sc18is602_platform_data *pdata = dev_get_platdata(dev);
struct sc18is602 *hw;
struct spi_master *master;
int error;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
return -EINVAL;
master = spi_alloc_master(dev, sizeof(struct sc18is602));
if (!master)
return -ENOMEM;
hw = spi_master_get_devdata(master);
i2c_set_clientdata(client, hw);
hw->master = master;
hw->client = client;
hw->dev = dev;
hw->ctrl = 0xff;
hw->id = id->driver_data;
switch (hw->id) {
case sc18is602:
case sc18is602b:
master->num_chipselect = 4;
hw->freq = SC18IS602_CLOCK;
break;
case sc18is603:
master->num_chipselect = 2;
if (pdata) {
hw->freq = pdata->clock_frequency;
} else {
const __be32 *val;
int len;
val = of_get_property(np, "clock-frequency", &len);
if (val && len >= sizeof(__be32))
hw->freq = be32_to_cpup(val);
}
if (!hw->freq)
hw->freq = SC18IS602_CLOCK;
break;
}
master->bus_num = client->adapter->nr;
master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST;
master->setup = sc18is602_setup;
master->transfer_one_message = sc18is602_transfer_one;
master->dev.of_node = np;
error = spi_register_master(master);
if (error)
goto error_reg;
return 0;
error_reg:
spi_master_put(master);
return error;
}
static int sc18is602_remove(struct i2c_client *client)
{
struct sc18is602 *hw = i2c_get_clientdata(client);
struct spi_master *master = hw->master;
spi_unregister_master(master);
return 0;
}
static const struct i2c_device_id sc18is602_id[] = {
{ "sc18is602", sc18is602 },
{ "sc18is602b", sc18is602b },
{ "sc18is603", sc18is603 },
{ }
};
MODULE_DEVICE_TABLE(i2c, sc18is602_id);
static struct i2c_driver sc18is602_driver = {
.driver = {
.name = "sc18is602",
},
.probe = sc18is602_probe,
.remove = sc18is602_remove,
.id_table = sc18is602_id,
};
module_i2c_driver(sc18is602_driver);
MODULE_DESCRIPTION("SC18IC602/603 SPI Master Driver");
MODULE_AUTHOR("Guenter Roeck");
MODULE_LICENSE("GPL");

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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));

View File

@ -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");

View File

@ -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;
} }

View File

@ -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;
}; };
/** /**

View File

@ -0,0 +1,19 @@
/*
* Platform data for NXP SC18IS602/603
*
* Copyright (C) 2012 Guenter Roeck <linux@roeck-us.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* For further information, see the Documentation/spi/sc18is602 file.
*/
/**
* struct sc18is602_platform_data - sc18is602 info
* @clock_frequency SC18IS603 oscillator frequency
*/
struct sc18is602_platform_data {
u32 clock_frequency;
};

150
include/linux/spi/mxs-spi.h Normal file
View File

@ -0,0 +1,150 @@
/*
* include/linux/spi/mxs-spi.h
*
* Freescale i.MX233/i.MX28 SPI controller register definition
*
* Copyright 2008 Embedded Alley Solutions, Inc.
* Copyright 2009-2011 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __LINUX_SPI_MXS_SPI_H__
#define __LINUX_SPI_MXS_SPI_H__
#include <linux/fsl/mxs-dma.h>
#define ssp_is_old(host) ((host)->devid == IMX23_SSP)
/* SSP registers */
#define HW_SSP_CTRL0 0x000
#define BM_SSP_CTRL0_RUN (1 << 29)
#define BM_SSP_CTRL0_SDIO_IRQ_CHECK (1 << 28)
#define BM_SSP_CTRL0_LOCK_CS (1 << 27)
#define BM_SSP_CTRL0_IGNORE_CRC (1 << 26)
#define BM_SSP_CTRL0_READ (1 << 25)
#define BM_SSP_CTRL0_DATA_XFER (1 << 24)
#define BP_SSP_CTRL0_BUS_WIDTH 22
#define BM_SSP_CTRL0_BUS_WIDTH (0x3 << 22)
#define BM_SSP_CTRL0_WAIT_FOR_IRQ (1 << 21)
#define BM_SSP_CTRL0_WAIT_FOR_CMD (1 << 20)
#define BM_SSP_CTRL0_LONG_RESP (1 << 19)
#define BM_SSP_CTRL0_GET_RESP (1 << 17)
#define BM_SSP_CTRL0_ENABLE (1 << 16)
#define BP_SSP_CTRL0_XFER_COUNT 0
#define BM_SSP_CTRL0_XFER_COUNT 0xffff
#define HW_SSP_CMD0 0x010
#define BM_SSP_CMD0_DBL_DATA_RATE_EN (1 << 25)
#define BM_SSP_CMD0_SLOW_CLKING_EN (1 << 22)
#define BM_SSP_CMD0_CONT_CLKING_EN (1 << 21)
#define BM_SSP_CMD0_APPEND_8CYC (1 << 20)
#define BP_SSP_CMD0_BLOCK_SIZE 16
#define BM_SSP_CMD0_BLOCK_SIZE (0xf << 16)
#define BP_SSP_CMD0_BLOCK_COUNT 8
#define BM_SSP_CMD0_BLOCK_COUNT (0xff << 8)
#define BP_SSP_CMD0_CMD 0
#define BM_SSP_CMD0_CMD 0xff
#define HW_SSP_CMD1 0x020
#define HW_SSP_XFER_SIZE 0x030
#define HW_SSP_BLOCK_SIZE 0x040
#define BP_SSP_BLOCK_SIZE_BLOCK_COUNT 4
#define BM_SSP_BLOCK_SIZE_BLOCK_COUNT (0xffffff << 4)
#define BP_SSP_BLOCK_SIZE_BLOCK_SIZE 0
#define BM_SSP_BLOCK_SIZE_BLOCK_SIZE 0xf
#define HW_SSP_TIMING(h) (ssp_is_old(h) ? 0x050 : 0x070)
#define BP_SSP_TIMING_TIMEOUT 16
#define BM_SSP_TIMING_TIMEOUT (0xffff << 16)
#define BP_SSP_TIMING_CLOCK_DIVIDE 8
#define BM_SSP_TIMING_CLOCK_DIVIDE (0xff << 8)
#define BF_SSP_TIMING_CLOCK_DIVIDE(v) \
(((v) << 8) & BM_SSP_TIMING_CLOCK_DIVIDE)
#define BP_SSP_TIMING_CLOCK_RATE 0
#define BM_SSP_TIMING_CLOCK_RATE 0xff
#define BF_SSP_TIMING_CLOCK_RATE(v) \
(((v) << 0) & BM_SSP_TIMING_CLOCK_RATE)
#define HW_SSP_CTRL1(h) (ssp_is_old(h) ? 0x060 : 0x080)
#define BM_SSP_CTRL1_SDIO_IRQ (1 << 31)
#define BM_SSP_CTRL1_SDIO_IRQ_EN (1 << 30)
#define BM_SSP_CTRL1_RESP_ERR_IRQ (1 << 29)
#define BM_SSP_CTRL1_RESP_ERR_IRQ_EN (1 << 28)
#define BM_SSP_CTRL1_RESP_TIMEOUT_IRQ (1 << 27)
#define BM_SSP_CTRL1_RESP_TIMEOUT_IRQ_EN (1 << 26)
#define BM_SSP_CTRL1_DATA_TIMEOUT_IRQ (1 << 25)
#define BM_SSP_CTRL1_DATA_TIMEOUT_IRQ_EN (1 << 24)
#define BM_SSP_CTRL1_DATA_CRC_IRQ (1 << 23)
#define BM_SSP_CTRL1_DATA_CRC_IRQ_EN (1 << 22)
#define BM_SSP_CTRL1_FIFO_UNDERRUN_IRQ (1 << 21)
#define BM_SSP_CTRL1_FIFO_UNDERRUN_IRQ_EN (1 << 20)
#define BM_SSP_CTRL1_RECV_TIMEOUT_IRQ (1 << 17)
#define BM_SSP_CTRL1_RECV_TIMEOUT_IRQ_EN (1 << 16)
#define BM_SSP_CTRL1_FIFO_OVERRUN_IRQ (1 << 15)
#define BM_SSP_CTRL1_FIFO_OVERRUN_IRQ_EN (1 << 14)
#define BM_SSP_CTRL1_DMA_ENABLE (1 << 13)
#define BM_SSP_CTRL1_PHASE (1 << 10)
#define BM_SSP_CTRL1_POLARITY (1 << 9)
#define BP_SSP_CTRL1_WORD_LENGTH 4
#define BM_SSP_CTRL1_WORD_LENGTH (0xf << 4)
#define BF_SSP_CTRL1_WORD_LENGTH(v) \
(((v) << 4) & BM_SSP_CTRL1_WORD_LENGTH)
#define BV_SSP_CTRL1_WORD_LENGTH__FOUR_BITS 0x3
#define BV_SSP_CTRL1_WORD_LENGTH__EIGHT_BITS 0x7
#define BV_SSP_CTRL1_WORD_LENGTH__SIXTEEN_BITS 0xF
#define BP_SSP_CTRL1_SSP_MODE 0
#define BM_SSP_CTRL1_SSP_MODE 0xf
#define BF_SSP_CTRL1_SSP_MODE(v) \
(((v) << 0) & BM_SSP_CTRL1_SSP_MODE)
#define BV_SSP_CTRL1_SSP_MODE__SPI 0x0
#define BV_SSP_CTRL1_SSP_MODE__SSI 0x1
#define BV_SSP_CTRL1_SSP_MODE__SD_MMC 0x3
#define BV_SSP_CTRL1_SSP_MODE__MS 0x4
#define HW_SSP_DATA(h) (ssp_is_old(h) ? 0x070 : 0x090)
#define HW_SSP_SDRESP0(h) (ssp_is_old(h) ? 0x080 : 0x0a0)
#define HW_SSP_SDRESP1(h) (ssp_is_old(h) ? 0x090 : 0x0b0)
#define HW_SSP_SDRESP2(h) (ssp_is_old(h) ? 0x0a0 : 0x0c0)
#define HW_SSP_SDRESP3(h) (ssp_is_old(h) ? 0x0b0 : 0x0d0)
#define HW_SSP_STATUS(h) (ssp_is_old(h) ? 0x0c0 : 0x100)
#define BM_SSP_STATUS_CARD_DETECT (1 << 28)
#define BM_SSP_STATUS_SDIO_IRQ (1 << 17)
#define BM_SSP_STATUS_FIFO_EMPTY (1 << 5)
#define BF_SSP(value, field) (((value) << BP_SSP_##field) & BM_SSP_##field)
#define SSP_PIO_NUM 3
enum mxs_ssp_id {
IMX23_SSP,
IMX28_SSP,
};
struct mxs_ssp {
struct device *dev;
void __iomem *base;
struct clk *clk;
unsigned int clk_rate;
enum mxs_ssp_id devid;
int dma_channel;
struct dma_chan *dmach;
struct mxs_dma_data dma_data;
unsigned int dma_dir;
enum dma_transfer_direction slave_dirn;
u32 ssp_pio_words[SSP_PIO_NUM];
};
void mxs_ssp_set_clk_rate(struct mxs_ssp *ssp, unsigned int rate);
#endif /* __LINUX_SPI_MXS_SPI_H__ */