Merge remote-tracking branches 'spi/topic/pxa', 'spi/topic/rb4xx', 'spi/topic/rspi', 'spi/topic/s3c64xx' and 'spi/topic/sh-msiof' into spi-next

This commit is contained in:
Mark Brown 2015-06-18 00:19:53 +01:00
11 changed files with 329 additions and 586 deletions

View File

@ -395,16 +395,9 @@ config SPI_PPC4xx
help help
This selects a driver for the PPC4xx SPI Controller. This selects a driver for the PPC4xx SPI Controller.
config SPI_PXA2XX_PXADMA
bool "PXA2xx SSP legacy PXA DMA API support"
depends on SPI_PXA2XX && ARCH_PXA
help
Enable PXA private legacy DMA API support. Note that this is
deprecated in favor of generic DMA engine API.
config SPI_PXA2XX_DMA config SPI_PXA2XX_DMA
def_bool y def_bool y
depends on SPI_PXA2XX && !SPI_PXA2XX_PXADMA depends on SPI_PXA2XX
config SPI_PXA2XX config SPI_PXA2XX
tristate "PXA2xx SSP SPI master" tristate "PXA2xx SSP SPI master"
@ -430,6 +423,12 @@ config SPI_ROCKCHIP
The main usecase of this controller is to use spi flash as boot The main usecase of this controller is to use spi flash as boot
device. device.
config SPI_RB4XX
tristate "Mikrotik RB4XX SPI master"
depends on SPI_MASTER && ATH79
help
SPI controller driver for the Mikrotik RB4xx series boards.
config SPI_RSPI config SPI_RSPI
tristate "Renesas RSPI/QSPI controller" tristate "Renesas RSPI/QSPI controller"
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST

View File

@ -60,12 +60,12 @@ obj-$(CONFIG_SPI_ORION) += spi-orion.o
obj-$(CONFIG_SPI_PL022) += spi-pl022.o obj-$(CONFIG_SPI_PL022) += spi-pl022.o
obj-$(CONFIG_SPI_PPC4xx) += spi-ppc4xx.o obj-$(CONFIG_SPI_PPC4xx) += spi-ppc4xx.o
spi-pxa2xx-platform-objs := spi-pxa2xx.o spi-pxa2xx-platform-objs := spi-pxa2xx.o
spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_PXADMA) += spi-pxa2xx-pxadma.o
spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o
obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o
obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o
obj-$(CONFIG_SPI_QUP) += spi-qup.o obj-$(CONFIG_SPI_QUP) += spi-qup.o
obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o
obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o
obj-$(CONFIG_SPI_RSPI) += spi-rspi.o obj-$(CONFIG_SPI_RSPI) += spi-rspi.o
obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o
spi-s3c24xx-hw-y := spi-s3c24xx.o spi-s3c24xx-hw-y := spi-s3c24xx.o

View File

@ -62,7 +62,7 @@ static struct pxa_spi_info spi_info_configs[] = {
.max_clk_rate = 3686400, .max_clk_rate = 3686400,
}, },
[PORT_BYT] = { [PORT_BYT] = {
.type = LPSS_SSP, .type = LPSS_BYT_SSP,
.port_id = 0, .port_id = 0,
.num_chipselect = 1, .num_chipselect = 1,
.max_clk_rate = 50000000, .max_clk_rate = 50000000,
@ -70,7 +70,7 @@ static struct pxa_spi_info spi_info_configs[] = {
.rx_param = &byt_rx_param, .rx_param = &byt_rx_param,
}, },
[PORT_BSW0] = { [PORT_BSW0] = {
.type = LPSS_SSP, .type = LPSS_BYT_SSP,
.port_id = 0, .port_id = 0,
.num_chipselect = 1, .num_chipselect = 1,
.max_clk_rate = 50000000, .max_clk_rate = 50000000,
@ -78,7 +78,7 @@ static struct pxa_spi_info spi_info_configs[] = {
.rx_param = &bsw0_rx_param, .rx_param = &bsw0_rx_param,
}, },
[PORT_BSW1] = { [PORT_BSW1] = {
.type = LPSS_SSP, .type = LPSS_BYT_SSP,
.port_id = 1, .port_id = 1,
.num_chipselect = 1, .num_chipselect = 1,
.max_clk_rate = 50000000, .max_clk_rate = 50000000,
@ -86,7 +86,7 @@ static struct pxa_spi_info spi_info_configs[] = {
.rx_param = &bsw1_rx_param, .rx_param = &bsw1_rx_param,
}, },
[PORT_BSW2] = { [PORT_BSW2] = {
.type = LPSS_SSP, .type = LPSS_BYT_SSP,
.port_id = 2, .port_id = 2,
.num_chipselect = 1, .num_chipselect = 1,
.max_clk_rate = 50000000, .max_clk_rate = 50000000,

View File

@ -1,487 +0,0 @@
/*
* PXA2xx SPI private DMA support.
*
* Copyright (C) 2005 Stephen Street / StreetFire Sound Labs
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; 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/delay.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/pxa2xx_ssp.h>
#include <linux/spi/spi.h>
#include <linux/spi/pxa2xx_spi.h>
#include <mach/dma.h>
#include "spi-pxa2xx.h"
#define DMA_INT_MASK (DCSR_ENDINTR | DCSR_STARTINTR | DCSR_BUSERR)
#define RESET_DMA_CHANNEL (DCSR_NODESC | DMA_INT_MASK)
bool pxa2xx_spi_dma_is_possible(size_t len)
{
/* Try to map dma buffer and do a dma transfer if successful, but
* only if the length is non-zero and less than MAX_DMA_LEN.
*
* Zero-length non-descriptor DMA is illegal on PXA2xx; force use
* of PIO instead. Care is needed above because the transfer may
* have have been passed with buffers that are already dma mapped.
* A zero-length transfer in PIO mode will not try to write/read
* to/from the buffers
*
* REVISIT large transfers are exactly where we most want to be
* using DMA. If this happens much, split those transfers into
* multiple DMA segments rather than forcing PIO.
*/
return len > 0 && len <= MAX_DMA_LEN;
}
int pxa2xx_spi_map_dma_buffers(struct driver_data *drv_data)
{
struct spi_message *msg = drv_data->cur_msg;
struct device *dev = &msg->spi->dev;
if (!drv_data->cur_chip->enable_dma)
return 0;
if (msg->is_dma_mapped)
return drv_data->rx_dma && drv_data->tx_dma;
if (!IS_DMA_ALIGNED(drv_data->rx) || !IS_DMA_ALIGNED(drv_data->tx))
return 0;
/* Modify setup if rx buffer is null */
if (drv_data->rx == NULL) {
*drv_data->null_dma_buf = 0;
drv_data->rx = drv_data->null_dma_buf;
drv_data->rx_map_len = 4;
} else
drv_data->rx_map_len = drv_data->len;
/* Modify setup if tx buffer is null */
if (drv_data->tx == NULL) {
*drv_data->null_dma_buf = 0;
drv_data->tx = drv_data->null_dma_buf;
drv_data->tx_map_len = 4;
} else
drv_data->tx_map_len = drv_data->len;
/* Stream map the tx buffer. Always do DMA_TO_DEVICE first
* so we flush the cache *before* invalidating it, in case
* the tx and rx buffers overlap.
*/
drv_data->tx_dma = dma_map_single(dev, drv_data->tx,
drv_data->tx_map_len, DMA_TO_DEVICE);
if (dma_mapping_error(dev, drv_data->tx_dma))
return 0;
/* Stream map the rx buffer */
drv_data->rx_dma = dma_map_single(dev, drv_data->rx,
drv_data->rx_map_len, DMA_FROM_DEVICE);
if (dma_mapping_error(dev, drv_data->rx_dma)) {
dma_unmap_single(dev, drv_data->tx_dma,
drv_data->tx_map_len, DMA_TO_DEVICE);
return 0;
}
return 1;
}
static void pxa2xx_spi_unmap_dma_buffers(struct driver_data *drv_data)
{
struct device *dev;
if (!drv_data->dma_mapped)
return;
if (!drv_data->cur_msg->is_dma_mapped) {
dev = &drv_data->cur_msg->spi->dev;
dma_unmap_single(dev, drv_data->rx_dma,
drv_data->rx_map_len, DMA_FROM_DEVICE);
dma_unmap_single(dev, drv_data->tx_dma,
drv_data->tx_map_len, DMA_TO_DEVICE);
}
drv_data->dma_mapped = 0;
}
static int wait_ssp_rx_stall(struct driver_data *drv_data)
{
unsigned long limit = loops_per_jiffy << 1;
while ((pxa2xx_spi_read(drv_data, SSSR) & SSSR_BSY) && --limit)
cpu_relax();
return limit;
}
static int wait_dma_channel_stop(int channel)
{
unsigned long limit = loops_per_jiffy << 1;
while (!(DCSR(channel) & DCSR_STOPSTATE) && --limit)
cpu_relax();
return limit;
}
static void pxa2xx_spi_dma_error_stop(struct driver_data *drv_data,
const char *msg)
{
/* Stop and reset */
DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
write_SSSR_CS(drv_data, drv_data->clear_sr);
pxa2xx_spi_write(drv_data, SSCR1,
pxa2xx_spi_read(drv_data, SSCR1)
& ~drv_data->dma_cr1);
if (!pxa25x_ssp_comp(drv_data))
pxa2xx_spi_write(drv_data, SSTO, 0);
pxa2xx_spi_flush(drv_data);
pxa2xx_spi_write(drv_data, SSCR0,
pxa2xx_spi_read(drv_data, SSCR0) & ~SSCR0_SSE);
pxa2xx_spi_unmap_dma_buffers(drv_data);
dev_err(&drv_data->pdev->dev, "%s\n", msg);
drv_data->cur_msg->state = ERROR_STATE;
tasklet_schedule(&drv_data->pump_transfers);
}
static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data)
{
struct spi_message *msg = drv_data->cur_msg;
/* Clear and disable interrupts on SSP and DMA channels*/
pxa2xx_spi_write(drv_data, SSCR1,
pxa2xx_spi_read(drv_data, SSCR1)
& ~drv_data->dma_cr1);
write_SSSR_CS(drv_data, drv_data->clear_sr);
DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
if (wait_dma_channel_stop(drv_data->rx_channel) == 0)
dev_err(&drv_data->pdev->dev,
"dma_handler: dma rx channel stop failed\n");
if (wait_ssp_rx_stall(drv_data->ioaddr) == 0)
dev_err(&drv_data->pdev->dev,
"dma_transfer: ssp rx stall failed\n");
pxa2xx_spi_unmap_dma_buffers(drv_data);
/* update the buffer pointer for the amount completed in dma */
drv_data->rx += drv_data->len -
(DCMD(drv_data->rx_channel) & DCMD_LENGTH);
/* read trailing data from fifo, it does not matter how many
* bytes are in the fifo just read until buffer is full
* or fifo is empty, which ever occurs first */
drv_data->read(drv_data);
/* return count of what was actually read */
msg->actual_length += drv_data->len -
(drv_data->rx_end - drv_data->rx);
/* Transfer delays and chip select release are
* handled in pump_transfers or giveback
*/
/* Move to next transfer */
msg->state = pxa2xx_spi_next_transfer(drv_data);
/* Schedule transfer tasklet */
tasklet_schedule(&drv_data->pump_transfers);
}
void pxa2xx_spi_dma_handler(int channel, void *data)
{
struct driver_data *drv_data = data;
u32 irq_status = DCSR(channel) & DMA_INT_MASK;
if (irq_status & DCSR_BUSERR) {
if (channel == drv_data->tx_channel)
pxa2xx_spi_dma_error_stop(drv_data,
"dma_handler: bad bus address on tx channel");
else
pxa2xx_spi_dma_error_stop(drv_data,
"dma_handler: bad bus address on rx channel");
return;
}
/* PXA255x_SSP has no timeout interrupt, wait for tailing bytes */
if ((channel == drv_data->tx_channel)
&& (irq_status & DCSR_ENDINTR)
&& (drv_data->ssp_type == PXA25x_SSP)) {
/* Wait for rx to stall */
if (wait_ssp_rx_stall(drv_data) == 0)
dev_err(&drv_data->pdev->dev,
"dma_handler: ssp rx stall failed\n");
/* finish this transfer, start the next */
pxa2xx_spi_dma_transfer_complete(drv_data);
}
}
irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data)
{
u32 irq_status;
irq_status = pxa2xx_spi_read(drv_data, SSSR) & drv_data->mask_sr;
if (irq_status & SSSR_ROR) {
pxa2xx_spi_dma_error_stop(drv_data,
"dma_transfer: fifo overrun");
return IRQ_HANDLED;
}
/* Check for false positive timeout */
if ((irq_status & SSSR_TINT)
&& (DCSR(drv_data->tx_channel) & DCSR_RUN)) {
pxa2xx_spi_write(drv_data, SSSR, SSSR_TINT);
return IRQ_HANDLED;
}
if (irq_status & SSSR_TINT || drv_data->rx == drv_data->rx_end) {
/* Clear and disable timeout interrupt, do the rest in
* dma_transfer_complete */
if (!pxa25x_ssp_comp(drv_data))
pxa2xx_spi_write(drv_data, SSTO, 0);
/* finish this transfer, start the next */
pxa2xx_spi_dma_transfer_complete(drv_data);
return IRQ_HANDLED;
}
/* Opps problem detected */
return IRQ_NONE;
}
int pxa2xx_spi_dma_prepare(struct driver_data *drv_data, u32 dma_burst)
{
u32 dma_width;
switch (drv_data->n_bytes) {
case 1:
dma_width = DCMD_WIDTH1;
break;
case 2:
dma_width = DCMD_WIDTH2;
break;
default:
dma_width = DCMD_WIDTH4;
break;
}
/* Setup rx DMA Channel */
DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
DSADR(drv_data->rx_channel) = drv_data->ssdr_physical;
DTADR(drv_data->rx_channel) = drv_data->rx_dma;
if (drv_data->rx == drv_data->null_dma_buf)
/* No target address increment */
DCMD(drv_data->rx_channel) = DCMD_FLOWSRC
| dma_width
| dma_burst
| drv_data->len;
else
DCMD(drv_data->rx_channel) = DCMD_INCTRGADDR
| DCMD_FLOWSRC
| dma_width
| dma_burst
| drv_data->len;
/* Setup tx DMA Channel */
DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
DSADR(drv_data->tx_channel) = drv_data->tx_dma;
DTADR(drv_data->tx_channel) = drv_data->ssdr_physical;
if (drv_data->tx == drv_data->null_dma_buf)
/* No source address increment */
DCMD(drv_data->tx_channel) = DCMD_FLOWTRG
| dma_width
| dma_burst
| drv_data->len;
else
DCMD(drv_data->tx_channel) = DCMD_INCSRCADDR
| DCMD_FLOWTRG
| dma_width
| dma_burst
| drv_data->len;
/* Enable dma end irqs on SSP to detect end of transfer */
if (drv_data->ssp_type == PXA25x_SSP)
DCMD(drv_data->tx_channel) |= DCMD_ENDIRQEN;
return 0;
}
void pxa2xx_spi_dma_start(struct driver_data *drv_data)
{
DCSR(drv_data->rx_channel) |= DCSR_RUN;
DCSR(drv_data->tx_channel) |= DCSR_RUN;
}
int pxa2xx_spi_dma_setup(struct driver_data *drv_data)
{
struct device *dev = &drv_data->pdev->dev;
struct ssp_device *ssp = drv_data->ssp;
/* Get two DMA channels (rx and tx) */
drv_data->rx_channel = pxa_request_dma("pxa2xx_spi_ssp_rx",
DMA_PRIO_HIGH,
pxa2xx_spi_dma_handler,
drv_data);
if (drv_data->rx_channel < 0) {
dev_err(dev, "problem (%d) requesting rx channel\n",
drv_data->rx_channel);
return -ENODEV;
}
drv_data->tx_channel = pxa_request_dma("pxa2xx_spi_ssp_tx",
DMA_PRIO_MEDIUM,
pxa2xx_spi_dma_handler,
drv_data);
if (drv_data->tx_channel < 0) {
dev_err(dev, "problem (%d) requesting tx channel\n",
drv_data->tx_channel);
pxa_free_dma(drv_data->rx_channel);
return -ENODEV;
}
DRCMR(ssp->drcmr_rx) = DRCMR_MAPVLD | drv_data->rx_channel;
DRCMR(ssp->drcmr_tx) = DRCMR_MAPVLD | drv_data->tx_channel;
return 0;
}
void pxa2xx_spi_dma_release(struct driver_data *drv_data)
{
struct ssp_device *ssp = drv_data->ssp;
DRCMR(ssp->drcmr_rx) = 0;
DRCMR(ssp->drcmr_tx) = 0;
if (drv_data->tx_channel != 0)
pxa_free_dma(drv_data->tx_channel);
if (drv_data->rx_channel != 0)
pxa_free_dma(drv_data->rx_channel);
}
void pxa2xx_spi_dma_resume(struct driver_data *drv_data)
{
if (drv_data->rx_channel != -1)
DRCMR(drv_data->ssp->drcmr_rx) =
DRCMR_MAPVLD | drv_data->rx_channel;
if (drv_data->tx_channel != -1)
DRCMR(drv_data->ssp->drcmr_tx) =
DRCMR_MAPVLD | drv_data->tx_channel;
}
int pxa2xx_spi_set_dma_burst_and_threshold(struct chip_data *chip,
struct spi_device *spi,
u8 bits_per_word, u32 *burst_code,
u32 *threshold)
{
struct pxa2xx_spi_chip *chip_info =
(struct pxa2xx_spi_chip *)spi->controller_data;
int bytes_per_word;
int burst_bytes;
int thresh_words;
int req_burst_size;
int retval = 0;
/* Set the threshold (in registers) to equal the same amount of data
* as represented by burst size (in bytes). The computation below
* is (burst_size rounded up to nearest 8 byte, word or long word)
* divided by (bytes/register); the tx threshold is the inverse of
* the rx, so that there will always be enough data in the rx fifo
* to satisfy a burst, and there will always be enough space in the
* tx fifo to accept a burst (a tx burst will overwrite the fifo if
* there is not enough space), there must always remain enough empty
* space in the rx fifo for any data loaded to the tx fifo.
* Whenever burst_size (in bytes) equals bits/word, the fifo threshold
* will be 8, or half the fifo;
* The threshold can only be set to 2, 4 or 8, but not 16, because
* to burst 16 to the tx fifo, the fifo would have to be empty;
* however, the minimum fifo trigger level is 1, and the tx will
* request service when the fifo is at this level, with only 15 spaces.
*/
/* find bytes/word */
if (bits_per_word <= 8)
bytes_per_word = 1;
else if (bits_per_word <= 16)
bytes_per_word = 2;
else
bytes_per_word = 4;
/* use struct pxa2xx_spi_chip->dma_burst_size if available */
if (chip_info)
req_burst_size = chip_info->dma_burst_size;
else {
switch (chip->dma_burst_size) {
default:
/* if the default burst size is not set,
* do it now */
chip->dma_burst_size = DCMD_BURST8;
case DCMD_BURST8:
req_burst_size = 8;
break;
case DCMD_BURST16:
req_burst_size = 16;
break;
case DCMD_BURST32:
req_burst_size = 32;
break;
}
}
if (req_burst_size <= 8) {
*burst_code = DCMD_BURST8;
burst_bytes = 8;
} else if (req_burst_size <= 16) {
if (bytes_per_word == 1) {
/* don't burst more than 1/2 the fifo */
*burst_code = DCMD_BURST8;
burst_bytes = 8;
retval = 1;
} else {
*burst_code = DCMD_BURST16;
burst_bytes = 16;
}
} else {
if (bytes_per_word == 1) {
/* don't burst more than 1/2 the fifo */
*burst_code = DCMD_BURST8;
burst_bytes = 8;
retval = 1;
} else if (bytes_per_word == 2) {
/* don't burst more than 1/2 the fifo */
*burst_code = DCMD_BURST16;
burst_bytes = 16;
retval = 1;
} else {
*burst_code = DCMD_BURST32;
burst_bytes = 32;
}
}
thresh_words = burst_bytes / bytes_per_word;
/* thresh_words will be between 2 and 8 */
*threshold = (SSCR1_RxTresh(thresh_words) & SSCR1_RFT)
| (SSCR1_TxTresh(16-thresh_words) & SSCR1_TFT);
return retval;
}

View File

@ -60,21 +60,60 @@ MODULE_ALIAS("platform:pxa2xx-spi");
| QUARK_X1000_SSCR1_TFT \ | QUARK_X1000_SSCR1_TFT \
| SSCR1_SPH | SSCR1_SPO | SSCR1_LBM) | SSCR1_SPH | SSCR1_SPO | SSCR1_LBM)
#define LPSS_RX_THRESH_DFLT 64
#define LPSS_TX_LOTHRESH_DFLT 160
#define LPSS_TX_HITHRESH_DFLT 224
/* Offset from drv_data->lpss_base */
#define GENERAL_REG 0x08
#define GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24) #define GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24)
#define SSP_REG 0x0c
#define SPI_CS_CONTROL 0x18
#define SPI_CS_CONTROL_SW_MODE BIT(0) #define SPI_CS_CONTROL_SW_MODE BIT(0)
#define SPI_CS_CONTROL_CS_HIGH BIT(1) #define SPI_CS_CONTROL_CS_HIGH BIT(1)
struct lpss_config {
/* LPSS offset from drv_data->ioaddr */
unsigned offset;
/* Register offsets from drv_data->lpss_base or -1 */
int reg_general;
int reg_ssp;
int reg_cs_ctrl;
/* FIFO thresholds */
u32 rx_threshold;
u32 tx_threshold_lo;
u32 tx_threshold_hi;
};
/* Keep these sorted with enum pxa_ssp_type */
static const struct lpss_config lpss_platforms[] = {
{ /* LPSS_LPT_SSP */
.offset = 0x800,
.reg_general = 0x08,
.reg_ssp = 0x0c,
.reg_cs_ctrl = 0x18,
.rx_threshold = 64,
.tx_threshold_lo = 160,
.tx_threshold_hi = 224,
},
{ /* LPSS_BYT_SSP */
.offset = 0x400,
.reg_general = 0x08,
.reg_ssp = 0x0c,
.reg_cs_ctrl = 0x18,
.rx_threshold = 64,
.tx_threshold_lo = 160,
.tx_threshold_hi = 224,
},
};
static inline const struct lpss_config
*lpss_get_config(const struct driver_data *drv_data)
{
return &lpss_platforms[drv_data->ssp_type - LPSS_LPT_SSP];
}
static bool is_lpss_ssp(const struct driver_data *drv_data) static bool is_lpss_ssp(const struct driver_data *drv_data)
{ {
return drv_data->ssp_type == LPSS_SSP; switch (drv_data->ssp_type) {
case LPSS_LPT_SSP:
case LPSS_BYT_SSP:
return true;
default:
return false;
}
} }
static bool is_quark_x1000_ssp(const struct driver_data *drv_data) static bool is_quark_x1000_ssp(const struct driver_data *drv_data)
@ -192,63 +231,43 @@ static void __lpss_ssp_write_priv(struct driver_data *drv_data,
*/ */
static void lpss_ssp_setup(struct driver_data *drv_data) static void lpss_ssp_setup(struct driver_data *drv_data)
{ {
unsigned offset = 0x400; const struct lpss_config *config;
u32 value, orig; u32 value;
/* config = lpss_get_config(drv_data);
* Perform auto-detection of the LPSS SSP private registers. They drv_data->lpss_base = drv_data->ioaddr + config->offset;
* can be either at 1k or 2k offset from the base address.
*/
orig = readl(drv_data->ioaddr + offset + SPI_CS_CONTROL);
/* Test SPI_CS_CONTROL_SW_MODE bit enabling */
value = orig | SPI_CS_CONTROL_SW_MODE;
writel(value, drv_data->ioaddr + offset + SPI_CS_CONTROL);
value = readl(drv_data->ioaddr + offset + SPI_CS_CONTROL);
if (value != (orig | SPI_CS_CONTROL_SW_MODE)) {
offset = 0x800;
goto detection_done;
}
orig = readl(drv_data->ioaddr + offset + SPI_CS_CONTROL);
/* Test SPI_CS_CONTROL_SW_MODE bit disabling */
value = orig & ~SPI_CS_CONTROL_SW_MODE;
writel(value, drv_data->ioaddr + offset + SPI_CS_CONTROL);
value = readl(drv_data->ioaddr + offset + SPI_CS_CONTROL);
if (value != (orig & ~SPI_CS_CONTROL_SW_MODE)) {
offset = 0x800;
goto detection_done;
}
detection_done:
/* Now set the LPSS base */
drv_data->lpss_base = drv_data->ioaddr + offset;
/* Enable software chip select control */ /* Enable software chip select control */
value = SPI_CS_CONTROL_SW_MODE | SPI_CS_CONTROL_CS_HIGH; value = SPI_CS_CONTROL_SW_MODE | SPI_CS_CONTROL_CS_HIGH;
__lpss_ssp_write_priv(drv_data, SPI_CS_CONTROL, value); __lpss_ssp_write_priv(drv_data, config->reg_cs_ctrl, value);
/* Enable multiblock DMA transfers */ /* Enable multiblock DMA transfers */
if (drv_data->master_info->enable_dma) { if (drv_data->master_info->enable_dma) {
__lpss_ssp_write_priv(drv_data, SSP_REG, 1); __lpss_ssp_write_priv(drv_data, config->reg_ssp, 1);
value = __lpss_ssp_read_priv(drv_data, GENERAL_REG); if (config->reg_general >= 0) {
value |= GENERAL_REG_RXTO_HOLDOFF_DISABLE; value = __lpss_ssp_read_priv(drv_data,
__lpss_ssp_write_priv(drv_data, GENERAL_REG, value); config->reg_general);
value |= GENERAL_REG_RXTO_HOLDOFF_DISABLE;
__lpss_ssp_write_priv(drv_data,
config->reg_general, value);
}
} }
} }
static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable) static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable)
{ {
const struct lpss_config *config;
u32 value; u32 value;
value = __lpss_ssp_read_priv(drv_data, SPI_CS_CONTROL); config = lpss_get_config(drv_data);
value = __lpss_ssp_read_priv(drv_data, config->reg_cs_ctrl);
if (enable) if (enable)
value &= ~SPI_CS_CONTROL_CS_HIGH; value &= ~SPI_CS_CONTROL_CS_HIGH;
else else
value |= SPI_CS_CONTROL_CS_HIGH; value |= SPI_CS_CONTROL_CS_HIGH;
__lpss_ssp_write_priv(drv_data, SPI_CS_CONTROL, value); __lpss_ssp_write_priv(drv_data, config->reg_cs_ctrl, value);
} }
static void cs_assert(struct driver_data *drv_data) static void cs_assert(struct driver_data *drv_data)
@ -1075,6 +1094,7 @@ static int setup(struct spi_device *spi)
{ {
struct pxa2xx_spi_chip *chip_info = NULL; struct pxa2xx_spi_chip *chip_info = NULL;
struct chip_data *chip; struct chip_data *chip;
const struct lpss_config *config;
struct driver_data *drv_data = spi_master_get_devdata(spi->master); struct driver_data *drv_data = spi_master_get_devdata(spi->master);
unsigned int clk_div; unsigned int clk_div;
uint tx_thres, tx_hi_thres, rx_thres; uint tx_thres, tx_hi_thres, rx_thres;
@ -1085,10 +1105,12 @@ static int setup(struct spi_device *spi)
tx_hi_thres = 0; tx_hi_thres = 0;
rx_thres = RX_THRESH_QUARK_X1000_DFLT; rx_thres = RX_THRESH_QUARK_X1000_DFLT;
break; break;
case LPSS_SSP: case LPSS_LPT_SSP:
tx_thres = LPSS_TX_LOTHRESH_DFLT; case LPSS_BYT_SSP:
tx_hi_thres = LPSS_TX_HITHRESH_DFLT; config = lpss_get_config(drv_data);
rx_thres = LPSS_RX_THRESH_DFLT; tx_thres = config->tx_threshold_lo;
tx_hi_thres = config->tx_threshold_hi;
rx_thres = config->rx_threshold;
break; break;
default: default:
tx_thres = TX_THRESH_DFLT; tx_thres = TX_THRESH_DFLT;
@ -1242,6 +1264,18 @@ static void cleanup(struct spi_device *spi)
} }
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
static const struct acpi_device_id pxa2xx_spi_acpi_match[] = {
{ "INT33C0", LPSS_LPT_SSP },
{ "INT33C1", LPSS_LPT_SSP },
{ "INT3430", LPSS_LPT_SSP },
{ "INT3431", LPSS_LPT_SSP },
{ "80860F0E", LPSS_BYT_SSP },
{ "8086228E", LPSS_BYT_SSP },
{ },
};
MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match);
static struct pxa2xx_spi_master * static struct pxa2xx_spi_master *
pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev)
{ {
@ -1249,12 +1283,19 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev)
struct acpi_device *adev; struct acpi_device *adev;
struct ssp_device *ssp; struct ssp_device *ssp;
struct resource *res; struct resource *res;
int devid; const struct acpi_device_id *id;
int devid, type;
if (!ACPI_HANDLE(&pdev->dev) || if (!ACPI_HANDLE(&pdev->dev) ||
acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev)) acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev))
return NULL; return NULL;
id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev);
if (id)
type = (int)id->driver_data;
else
return NULL;
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) if (!pdata)
return NULL; return NULL;
@ -1272,7 +1313,7 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev)
ssp->clk = devm_clk_get(&pdev->dev, NULL); ssp->clk = devm_clk_get(&pdev->dev, NULL);
ssp->irq = platform_get_irq(pdev, 0); ssp->irq = platform_get_irq(pdev, 0);
ssp->type = LPSS_SSP; ssp->type = type;
ssp->pdev = pdev; ssp->pdev = pdev;
ssp->port_id = -1; ssp->port_id = -1;
@ -1285,16 +1326,6 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev)
return pdata; return pdata;
} }
static struct acpi_device_id pxa2xx_spi_acpi_match[] = {
{ "INT33C0", 0 },
{ "INT33C1", 0 },
{ "INT3430", 0 },
{ "INT3431", 0 },
{ "80860F0E", 0 },
{ "8086228E", 0 },
{ },
};
MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match);
#else #else
static inline struct pxa2xx_spi_master * static inline struct pxa2xx_spi_master *
pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev)

View File

@ -162,11 +162,7 @@ extern void *pxa2xx_spi_next_transfer(struct driver_data *drv_data);
/* /*
* Select the right DMA implementation. * Select the right DMA implementation.
*/ */
#if defined(CONFIG_SPI_PXA2XX_PXADMA) #if defined(CONFIG_SPI_PXA2XX_DMA)
#define SPI_PXA2XX_USE_DMA 1
#define MAX_DMA_LEN 8191
#define DEFAULT_DMA_CR1 (SSCR1_TSRE | SSCR1_RSRE | SSCR1_TINTE)
#elif defined(CONFIG_SPI_PXA2XX_DMA)
#define SPI_PXA2XX_USE_DMA 1 #define SPI_PXA2XX_USE_DMA 1
#define MAX_DMA_LEN SZ_64K #define MAX_DMA_LEN SZ_64K
#define DEFAULT_DMA_CR1 (SSCR1_TSRE | SSCR1_RSRE | SSCR1_TRAIL) #define DEFAULT_DMA_CR1 (SSCR1_TSRE | SSCR1_RSRE | SSCR1_TRAIL)

210
drivers/spi/spi-rb4xx.c Normal file
View File

@ -0,0 +1,210 @@
/*
* SPI controller driver for the Mikrotik RB4xx boards
*
* Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2015 Bert Vermeulen <bert@biot.com>
*
* This file was based on the patches for Linux 2.6.27.39 published by
* MikroTik for their RouterBoard 4xx series devices.
*
* 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.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/spi/spi.h>
#include <asm/mach-ath79/ar71xx_regs.h>
struct rb4xx_spi {
void __iomem *base;
struct clk *clk;
};
static inline u32 rb4xx_read(struct rb4xx_spi *rbspi, u32 reg)
{
return __raw_readl(rbspi->base + reg);
}
static inline void rb4xx_write(struct rb4xx_spi *rbspi, u32 reg, u32 value)
{
__raw_writel(value, rbspi->base + reg);
}
static inline void do_spi_clk(struct rb4xx_spi *rbspi, u32 spi_ioc, int value)
{
u32 regval;
regval = spi_ioc;
if (value & BIT(0))
regval |= AR71XX_SPI_IOC_DO;
rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval);
rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval | AR71XX_SPI_IOC_CLK);
}
static void do_spi_byte(struct rb4xx_spi *rbspi, u32 spi_ioc, u8 byte)
{
int i;
for (i = 7; i >= 0; i--)
do_spi_clk(rbspi, spi_ioc, byte >> i);
}
/* The CS2 pin is used to clock in a second bit per clock cycle. */
static inline void do_spi_clk_two(struct rb4xx_spi *rbspi, u32 spi_ioc,
u8 value)
{
u32 regval;
regval = spi_ioc;
if (value & BIT(1))
regval |= AR71XX_SPI_IOC_DO;
if (value & BIT(0))
regval |= AR71XX_SPI_IOC_CS2;
rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval);
rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval | AR71XX_SPI_IOC_CLK);
}
/* Two bits at a time, msb first */
static void do_spi_byte_two(struct rb4xx_spi *rbspi, u32 spi_ioc, u8 byte)
{
do_spi_clk_two(rbspi, spi_ioc, byte >> 6);
do_spi_clk_two(rbspi, spi_ioc, byte >> 4);
do_spi_clk_two(rbspi, spi_ioc, byte >> 2);
do_spi_clk_two(rbspi, spi_ioc, byte >> 0);
}
static void rb4xx_set_cs(struct spi_device *spi, bool enable)
{
struct rb4xx_spi *rbspi = spi_master_get_devdata(spi->master);
/*
* Setting CS is done along with bitbanging the actual values,
* since it's all on the same hardware register. However the
* CPLD needs CS deselected after every command.
*/
if (enable)
rb4xx_write(rbspi, AR71XX_SPI_REG_IOC,
AR71XX_SPI_IOC_CS0 | AR71XX_SPI_IOC_CS1);
}
static int rb4xx_transfer_one(struct spi_master *master,
struct spi_device *spi, struct spi_transfer *t)
{
struct rb4xx_spi *rbspi = spi_master_get_devdata(master);
int i;
u32 spi_ioc;
u8 *rx_buf;
const u8 *tx_buf;
/*
* Prime the SPI register with the SPI device selected. The m25p80 boot
* flash and CPLD share the CS0 pin. This works because the CPLD's
* command set was designed to almost not clash with that of the
* boot flash.
*/
if (spi->chip_select == 2)
/* MMC */
spi_ioc = AR71XX_SPI_IOC_CS0;
else
/* Boot flash and CPLD */
spi_ioc = AR71XX_SPI_IOC_CS1;
tx_buf = t->tx_buf;
rx_buf = t->rx_buf;
for (i = 0; i < t->len; ++i) {
if (t->tx_nbits == SPI_NBITS_DUAL)
/* CPLD can use two-wire transfers */
do_spi_byte_two(rbspi, spi_ioc, tx_buf[i]);
else
do_spi_byte(rbspi, spi_ioc, tx_buf[i]);
if (!rx_buf)
continue;
rx_buf[i] = rb4xx_read(rbspi, AR71XX_SPI_REG_RDS);
}
spi_finalize_current_transfer(master);
return 0;
}
static int rb4xx_spi_probe(struct platform_device *pdev)
{
struct spi_master *master;
struct clk *ahb_clk;
struct rb4xx_spi *rbspi;
struct resource *r;
int err;
void __iomem *spi_base;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
spi_base = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(spi_base))
return PTR_ERR(spi_base);
master = spi_alloc_master(&pdev->dev, sizeof(*rbspi));
if (!master)
return -ENOMEM;
ahb_clk = devm_clk_get(&pdev->dev, "ahb");
if (IS_ERR(ahb_clk))
return PTR_ERR(ahb_clk);
master->bus_num = 0;
master->num_chipselect = 3;
master->mode_bits = SPI_TX_DUAL;
master->bits_per_word_mask = BIT(7);
master->flags = SPI_MASTER_MUST_TX;
master->transfer_one = rb4xx_transfer_one;
master->set_cs = rb4xx_set_cs;
err = devm_spi_register_master(&pdev->dev, master);
if (err) {
dev_err(&pdev->dev, "failed to register SPI master\n");
return err;
}
err = clk_prepare_enable(ahb_clk);
if (err)
return err;
rbspi = spi_master_get_devdata(master);
rbspi->base = spi_base;
rbspi->clk = ahb_clk;
platform_set_drvdata(pdev, rbspi);
/* Enable SPI */
rb4xx_write(rbspi, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO);
return 0;
}
static int rb4xx_spi_remove(struct platform_device *pdev)
{
struct rb4xx_spi *rbspi = platform_get_drvdata(pdev);
clk_disable_unprepare(rbspi->clk);
return 0;
}
static struct platform_driver rb4xx_spi_drv = {
.probe = rb4xx_spi_probe,
.remove = rb4xx_spi_remove,
.driver = {
.name = "rb4xx-spi",
},
};
module_platform_driver(rb4xx_spi_drv);
MODULE_DESCRIPTION("Mikrotik RB4xx SPI controller driver");
MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
MODULE_AUTHOR("Bert Vermeulen <bert@biot.com>");
MODULE_LICENSE("GPL v2");

View File

@ -665,15 +665,12 @@ static bool rspi_can_dma(struct spi_master *master, struct spi_device *spi,
static int rspi_dma_check_then_transfer(struct rspi_data *rspi, static int rspi_dma_check_then_transfer(struct rspi_data *rspi,
struct spi_transfer *xfer) struct spi_transfer *xfer)
{ {
if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) { if (!rspi->master->can_dma || !__rspi_can_dma(rspi, xfer))
/* rx_buf can be NULL on RSPI on SH in TX-only Mode */ return -EAGAIN;
int ret = rspi_dma_transfer(rspi, &xfer->tx_sg,
xfer->rx_buf ? &xfer->rx_sg : NULL);
if (ret != -EAGAIN)
return 0;
}
return -EAGAIN; /* rx_buf can be NULL on RSPI on SH in TX-only Mode */
return rspi_dma_transfer(rspi, &xfer->tx_sg,
xfer->rx_buf ? &xfer->rx_sg : NULL);
} }
static int rspi_common_transfer(struct rspi_data *rspi, static int rspi_common_transfer(struct rspi_data *rspi,
@ -724,7 +721,7 @@ static int rspi_rz_transfer_one(struct spi_master *master,
return rspi_common_transfer(rspi, xfer); return rspi_common_transfer(rspi, xfer);
} }
static int qspi_trigger_transfer_out_int(struct rspi_data *rspi, const u8 *tx, static int qspi_trigger_transfer_out_in(struct rspi_data *rspi, const u8 *tx,
u8 *rx, unsigned int len) u8 *rx, unsigned int len)
{ {
int i, n, ret; int i, n, ret;
@ -771,12 +768,8 @@ static int qspi_transfer_out_in(struct rspi_data *rspi,
if (ret != -EAGAIN) if (ret != -EAGAIN)
return ret; return ret;
ret = qspi_trigger_transfer_out_int(rspi, xfer->tx_buf, return qspi_trigger_transfer_out_in(rspi, xfer->tx_buf,
xfer->rx_buf, xfer->len); xfer->rx_buf, xfer->len);
if (ret < 0)
return ret;
return 0;
} }
static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer) static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer)
@ -1300,7 +1293,7 @@ error1:
return ret; return ret;
} }
static struct platform_device_id spi_driver_ids[] = { static const struct platform_device_id spi_driver_ids[] = {
{ "rspi", (kernel_ulong_t)&rspi_ops }, { "rspi", (kernel_ulong_t)&rspi_ops },
{ "rspi-rz", (kernel_ulong_t)&rspi_rz_ops }, { "rspi-rz", (kernel_ulong_t)&rspi_rz_ops },
{ "qspi", (kernel_ulong_t)&qspi_ops }, { "qspi", (kernel_ulong_t)&qspi_ops },

View File

@ -1347,7 +1347,7 @@ static struct s3c64xx_spi_port_config exynos7_spi_port_config = {
.quirks = S3C64XX_SPI_QUIRK_CS_AUTO, .quirks = S3C64XX_SPI_QUIRK_CS_AUTO,
}; };
static struct platform_device_id s3c64xx_spi_driver_ids[] = { static const struct platform_device_id s3c64xx_spi_driver_ids[] = {
{ {
.name = "s3c2443-spi", .name = "s3c2443-spi",
.driver_data = (kernel_ulong_t)&s3c2443_spi_port_config, .driver_data = (kernel_ulong_t)&s3c2443_spi_port_config,

View File

@ -1263,7 +1263,7 @@ static int sh_msiof_spi_remove(struct platform_device *pdev)
return 0; return 0;
} }
static struct platform_device_id spi_driver_ids[] = { static const struct platform_device_id spi_driver_ids[] = {
{ "spi_sh_msiof", (kernel_ulong_t)&sh_data }, { "spi_sh_msiof", (kernel_ulong_t)&sh_data },
{ "spi_r8a7790_msiof", (kernel_ulong_t)&r8a779x_data }, { "spi_r8a7790_msiof", (kernel_ulong_t)&r8a779x_data },
{ "spi_r8a7791_msiof", (kernel_ulong_t)&r8a779x_data }, { "spi_r8a7791_msiof", (kernel_ulong_t)&r8a779x_data },

View File

@ -194,8 +194,9 @@ enum pxa_ssp_type {
PXA168_SSP, PXA168_SSP,
PXA910_SSP, PXA910_SSP,
CE4100_SSP, CE4100_SSP,
LPSS_SSP,
QUARK_X1000_SSP, QUARK_X1000_SSP,
LPSS_LPT_SSP, /* Keep LPSS types sorted with lpss_platforms[] */
LPSS_BYT_SSP,
}; };
struct ssp_device { struct ssp_device {