Merge remote-tracking branches 'spi/topic/octeon', 'spi/topic/pic32-sqi', 'spi/topic/pxa2xx' and 'spi/topic/qup' into spi-next
This commit is contained in:
@@ -648,6 +648,13 @@ config SPI_TEGRA20_SLINK
|
|||||||
help
|
help
|
||||||
SPI driver for Nvidia Tegra20/Tegra30 SLINK Controller interface.
|
SPI driver for Nvidia Tegra20/Tegra30 SLINK Controller interface.
|
||||||
|
|
||||||
|
config SPI_THUNDERX
|
||||||
|
tristate "Cavium ThunderX SPI controller"
|
||||||
|
depends on PCI && 64BIT && (ARM64 || COMPILE_TEST)
|
||||||
|
help
|
||||||
|
SPI host driver for the hardware found on Cavium ThunderX
|
||||||
|
SOCs.
|
||||||
|
|
||||||
config SPI_TOPCLIFF_PCH
|
config SPI_TOPCLIFF_PCH
|
||||||
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) SPI"
|
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) SPI"
|
||||||
depends on PCI && (X86_32 || MIPS || COMPILE_TEST)
|
depends on PCI && (X86_32 || MIPS || COMPILE_TEST)
|
||||||
|
@@ -93,6 +93,8 @@ obj-$(CONFIG_SPI_TEGRA114) += spi-tegra114.o
|
|||||||
obj-$(CONFIG_SPI_TEGRA20_SFLASH) += spi-tegra20-sflash.o
|
obj-$(CONFIG_SPI_TEGRA20_SFLASH) += spi-tegra20-sflash.o
|
||||||
obj-$(CONFIG_SPI_TEGRA20_SLINK) += spi-tegra20-slink.o
|
obj-$(CONFIG_SPI_TEGRA20_SLINK) += spi-tegra20-slink.o
|
||||||
obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o
|
obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o
|
||||||
|
spi-thunderx-objs := spi-cavium.o spi-cavium-thunderx.o
|
||||||
|
obj-$(CONFIG_SPI_THUNDERX) += spi-thunderx.o
|
||||||
obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o
|
obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o
|
||||||
obj-$(CONFIG_SPI_TXX9) += spi-txx9.o
|
obj-$(CONFIG_SPI_TXX9) += spi-txx9.o
|
||||||
obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o
|
obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o
|
||||||
|
120
drivers/spi/spi-cavium-thunderx.c
Normal file
120
drivers/spi/spi-cavium-thunderx.c
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* Cavium ThunderX SPI driver.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 Cavium Inc.
|
||||||
|
* Authors: Jan Glauber <jglauber@cavium.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/pci.h>
|
||||||
|
#include <linux/spi/spi.h>
|
||||||
|
|
||||||
|
#include "spi-cavium.h"
|
||||||
|
|
||||||
|
#define DRV_NAME "spi-thunderx"
|
||||||
|
|
||||||
|
#define SYS_FREQ_DEFAULT 700000000 /* 700 Mhz */
|
||||||
|
|
||||||
|
static int thunderx_spi_probe(struct pci_dev *pdev,
|
||||||
|
const struct pci_device_id *ent)
|
||||||
|
{
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct spi_master *master;
|
||||||
|
struct octeon_spi *p;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
master = spi_alloc_master(dev, sizeof(struct octeon_spi));
|
||||||
|
if (!master)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
p = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
ret = pcim_enable_device(pdev);
|
||||||
|
if (ret)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
ret = pci_request_regions(pdev, DRV_NAME);
|
||||||
|
if (ret)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
p->register_base = pcim_iomap(pdev, 0, pci_resource_len(pdev, 0));
|
||||||
|
if (!p->register_base) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
p->regs.config = 0x1000;
|
||||||
|
p->regs.status = 0x1008;
|
||||||
|
p->regs.tx = 0x1010;
|
||||||
|
p->regs.data = 0x1080;
|
||||||
|
|
||||||
|
p->clk = devm_clk_get(dev, NULL);
|
||||||
|
if (IS_ERR(p->clk)) {
|
||||||
|
ret = PTR_ERR(p->clk);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(p->clk);
|
||||||
|
if (ret)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
p->sys_freq = clk_get_rate(p->clk);
|
||||||
|
if (!p->sys_freq)
|
||||||
|
p->sys_freq = SYS_FREQ_DEFAULT;
|
||||||
|
dev_info(dev, "Set system clock to %u\n", p->sys_freq);
|
||||||
|
|
||||||
|
master->num_chipselect = 4;
|
||||||
|
master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH |
|
||||||
|
SPI_LSB_FIRST | SPI_3WIRE;
|
||||||
|
master->transfer_one_message = octeon_spi_transfer_one_message;
|
||||||
|
master->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||||
|
master->max_speed_hz = OCTEON_SPI_MAX_CLOCK_HZ;
|
||||||
|
master->dev.of_node = pdev->dev.of_node;
|
||||||
|
|
||||||
|
pci_set_drvdata(pdev, master);
|
||||||
|
|
||||||
|
ret = devm_spi_register_master(dev, master);
|
||||||
|
if (ret)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
clk_disable_unprepare(p->clk);
|
||||||
|
spi_master_put(master);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void thunderx_spi_remove(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
struct spi_master *master = pci_get_drvdata(pdev);
|
||||||
|
struct octeon_spi *p;
|
||||||
|
|
||||||
|
p = spi_master_get_devdata(master);
|
||||||
|
if (!p)
|
||||||
|
return;
|
||||||
|
|
||||||
|
clk_disable_unprepare(p->clk);
|
||||||
|
/* Put everything in a known state. */
|
||||||
|
writeq(0, p->register_base + OCTEON_SPI_CFG(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct pci_device_id thunderx_spi_pci_id_table[] = {
|
||||||
|
{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xa00b) },
|
||||||
|
{ 0, }
|
||||||
|
};
|
||||||
|
|
||||||
|
MODULE_DEVICE_TABLE(pci, thunderx_spi_pci_id_table);
|
||||||
|
|
||||||
|
static struct pci_driver thunderx_spi_driver = {
|
||||||
|
.name = DRV_NAME,
|
||||||
|
.id_table = thunderx_spi_pci_id_table,
|
||||||
|
.probe = thunderx_spi_probe,
|
||||||
|
.remove = thunderx_spi_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_pci_driver(thunderx_spi_driver);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("Cavium, Inc. ThunderX SPI bus driver");
|
||||||
|
MODULE_AUTHOR("Jan Glauber");
|
||||||
|
MODULE_LICENSE("GPL");
|
@@ -1,6 +1,8 @@
|
|||||||
#ifndef __SPI_CAVIUM_H
|
#ifndef __SPI_CAVIUM_H
|
||||||
#define __SPI_CAVIUM_H
|
#define __SPI_CAVIUM_H
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
|
|
||||||
#define OCTEON_SPI_MAX_BYTES 9
|
#define OCTEON_SPI_MAX_BYTES 9
|
||||||
#define OCTEON_SPI_MAX_CLOCK_HZ 16000000
|
#define OCTEON_SPI_MAX_CLOCK_HZ 16000000
|
||||||
|
|
||||||
@@ -17,6 +19,7 @@ struct octeon_spi {
|
|||||||
u64 cs_enax;
|
u64 cs_enax;
|
||||||
int sys_freq;
|
int sys_freq;
|
||||||
struct octeon_spi_regs regs;
|
struct octeon_spi_regs regs;
|
||||||
|
struct clk *clk;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define OCTEON_SPI_CFG(x) (x->regs.config)
|
#define OCTEON_SPI_CFG(x) (x->regs.config)
|
||||||
|
@@ -253,15 +253,13 @@ static struct ring_desc *ring_desc_get(struct pic32_sqi *sqi)
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
rdesc = list_first_entry(&sqi->bd_list_free, struct ring_desc, list);
|
rdesc = list_first_entry(&sqi->bd_list_free, struct ring_desc, list);
|
||||||
list_del(&rdesc->list);
|
list_move_tail(&rdesc->list, &sqi->bd_list_used);
|
||||||
list_add_tail(&rdesc->list, &sqi->bd_list_used);
|
|
||||||
return rdesc;
|
return rdesc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ring_desc_put(struct pic32_sqi *sqi, struct ring_desc *rdesc)
|
static void ring_desc_put(struct pic32_sqi *sqi, struct ring_desc *rdesc)
|
||||||
{
|
{
|
||||||
list_del(&rdesc->list);
|
list_move(&rdesc->list, &sqi->bd_list_free);
|
||||||
list_add(&rdesc->list, &sqi->bd_list_free);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pic32_sqi_one_transfer(struct pic32_sqi *sqi,
|
static int pic32_sqi_one_transfer(struct pic32_sqi *sqi,
|
||||||
|
@@ -23,7 +23,7 @@
|
|||||||
static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data,
|
static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data,
|
||||||
bool error)
|
bool error)
|
||||||
{
|
{
|
||||||
struct spi_message *msg = drv_data->cur_msg;
|
struct spi_message *msg = drv_data->master->cur_msg;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* It is possible that one CPU is handling ROR interrupt and other
|
* It is possible that one CPU is handling ROR interrupt and other
|
||||||
@@ -76,7 +76,8 @@ static struct dma_async_tx_descriptor *
|
|||||||
pxa2xx_spi_dma_prepare_one(struct driver_data *drv_data,
|
pxa2xx_spi_dma_prepare_one(struct driver_data *drv_data,
|
||||||
enum dma_transfer_direction dir)
|
enum dma_transfer_direction dir)
|
||||||
{
|
{
|
||||||
struct chip_data *chip = drv_data->cur_chip;
|
struct chip_data *chip =
|
||||||
|
spi_get_ctldata(drv_data->master->cur_msg->spi);
|
||||||
struct spi_transfer *xfer = drv_data->cur_transfer;
|
struct spi_transfer *xfer = drv_data->cur_transfer;
|
||||||
enum dma_slave_buswidth width;
|
enum dma_slave_buswidth width;
|
||||||
struct dma_slave_config cfg;
|
struct dma_slave_config cfg;
|
||||||
@@ -146,7 +147,7 @@ irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data)
|
|||||||
int pxa2xx_spi_dma_prepare(struct driver_data *drv_data, u32 dma_burst)
|
int pxa2xx_spi_dma_prepare(struct driver_data *drv_data, u32 dma_burst)
|
||||||
{
|
{
|
||||||
struct dma_async_tx_descriptor *tx_desc, *rx_desc;
|
struct dma_async_tx_descriptor *tx_desc, *rx_desc;
|
||||||
int err = 0;
|
int err;
|
||||||
|
|
||||||
tx_desc = pxa2xx_spi_dma_prepare_one(drv_data, DMA_MEM_TO_DEV);
|
tx_desc = pxa2xx_spi_dma_prepare_one(drv_data, DMA_MEM_TO_DEV);
|
||||||
if (!tx_desc) {
|
if (!tx_desc) {
|
||||||
|
@@ -28,6 +28,7 @@
|
|||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
@@ -62,6 +63,13 @@ 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 CE4100_SSCR1_CHANGE_MASK (SSCR1_TTELP | SSCR1_TTE | SSCR1_SCFR \
|
||||||
|
| SSCR1_ECRA | SSCR1_ECRB | SSCR1_SCLKDIR \
|
||||||
|
| SSCR1_SFRMDIR | SSCR1_RWOT | SSCR1_TRAIL \
|
||||||
|
| SSCR1_IFS | SSCR1_STRF | SSCR1_EFWR \
|
||||||
|
| CE4100_SSCR1_RFT | CE4100_SSCR1_TFT | SSCR1_MWDS \
|
||||||
|
| SSCR1_SPH | SSCR1_SPO | SSCR1_LBM)
|
||||||
|
|
||||||
#define LPSS_GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24)
|
#define LPSS_GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24)
|
||||||
#define LPSS_CS_CONTROL_SW_MODE BIT(0)
|
#define LPSS_CS_CONTROL_SW_MODE BIT(0)
|
||||||
#define LPSS_CS_CONTROL_CS_HIGH BIT(1)
|
#define LPSS_CS_CONTROL_CS_HIGH BIT(1)
|
||||||
@@ -175,6 +183,8 @@ static u32 pxa2xx_spi_get_ssrc1_change_mask(const struct driver_data *drv_data)
|
|||||||
switch (drv_data->ssp_type) {
|
switch (drv_data->ssp_type) {
|
||||||
case QUARK_X1000_SSP:
|
case QUARK_X1000_SSP:
|
||||||
return QUARK_X1000_SSCR1_CHANGE_MASK;
|
return QUARK_X1000_SSCR1_CHANGE_MASK;
|
||||||
|
case CE4100_SSP:
|
||||||
|
return CE4100_SSCR1_CHANGE_MASK;
|
||||||
default:
|
default:
|
||||||
return SSCR1_CHANGE_MASK;
|
return SSCR1_CHANGE_MASK;
|
||||||
}
|
}
|
||||||
@@ -186,6 +196,8 @@ pxa2xx_spi_get_rx_default_thre(const struct driver_data *drv_data)
|
|||||||
switch (drv_data->ssp_type) {
|
switch (drv_data->ssp_type) {
|
||||||
case QUARK_X1000_SSP:
|
case QUARK_X1000_SSP:
|
||||||
return RX_THRESH_QUARK_X1000_DFLT;
|
return RX_THRESH_QUARK_X1000_DFLT;
|
||||||
|
case CE4100_SSP:
|
||||||
|
return RX_THRESH_CE4100_DFLT;
|
||||||
default:
|
default:
|
||||||
return RX_THRESH_DFLT;
|
return RX_THRESH_DFLT;
|
||||||
}
|
}
|
||||||
@@ -199,6 +211,9 @@ static bool pxa2xx_spi_txfifo_full(const struct driver_data *drv_data)
|
|||||||
case QUARK_X1000_SSP:
|
case QUARK_X1000_SSP:
|
||||||
mask = QUARK_X1000_SSSR_TFL_MASK;
|
mask = QUARK_X1000_SSSR_TFL_MASK;
|
||||||
break;
|
break;
|
||||||
|
case CE4100_SSP:
|
||||||
|
mask = CE4100_SSSR_TFL_MASK;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
mask = SSSR_TFL_MASK;
|
mask = SSSR_TFL_MASK;
|
||||||
break;
|
break;
|
||||||
@@ -216,6 +231,9 @@ static void pxa2xx_spi_clear_rx_thre(const struct driver_data *drv_data,
|
|||||||
case QUARK_X1000_SSP:
|
case QUARK_X1000_SSP:
|
||||||
mask = QUARK_X1000_SSCR1_RFT;
|
mask = QUARK_X1000_SSCR1_RFT;
|
||||||
break;
|
break;
|
||||||
|
case CE4100_SSP:
|
||||||
|
mask = CE4100_SSCR1_RFT;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
mask = SSCR1_RFT;
|
mask = SSCR1_RFT;
|
||||||
break;
|
break;
|
||||||
@@ -230,6 +248,9 @@ static void pxa2xx_spi_set_rx_thre(const struct driver_data *drv_data,
|
|||||||
case QUARK_X1000_SSP:
|
case QUARK_X1000_SSP:
|
||||||
*sccr1_reg |= QUARK_X1000_SSCR1_RxTresh(threshold);
|
*sccr1_reg |= QUARK_X1000_SSCR1_RxTresh(threshold);
|
||||||
break;
|
break;
|
||||||
|
case CE4100_SSP:
|
||||||
|
*sccr1_reg |= CE4100_SSCR1_RxTresh(threshold);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
*sccr1_reg |= SSCR1_RxTresh(threshold);
|
*sccr1_reg |= SSCR1_RxTresh(threshold);
|
||||||
break;
|
break;
|
||||||
@@ -316,7 +337,7 @@ static void lpss_ssp_select_cs(struct driver_data *drv_data,
|
|||||||
|
|
||||||
value = __lpss_ssp_read_priv(drv_data, config->reg_cs_ctrl);
|
value = __lpss_ssp_read_priv(drv_data, config->reg_cs_ctrl);
|
||||||
|
|
||||||
cs = drv_data->cur_msg->spi->chip_select;
|
cs = drv_data->master->cur_msg->spi->chip_select;
|
||||||
cs <<= config->cs_sel_shift;
|
cs <<= config->cs_sel_shift;
|
||||||
if (cs != (value & config->cs_sel_mask)) {
|
if (cs != (value & config->cs_sel_mask)) {
|
||||||
/*
|
/*
|
||||||
@@ -355,10 +376,11 @@ static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable)
|
|||||||
|
|
||||||
static void cs_assert(struct driver_data *drv_data)
|
static void cs_assert(struct driver_data *drv_data)
|
||||||
{
|
{
|
||||||
struct chip_data *chip = drv_data->cur_chip;
|
struct chip_data *chip =
|
||||||
|
spi_get_ctldata(drv_data->master->cur_msg->spi);
|
||||||
|
|
||||||
if (drv_data->ssp_type == CE4100_SSP) {
|
if (drv_data->ssp_type == CE4100_SSP) {
|
||||||
pxa2xx_spi_write(drv_data, SSSR, drv_data->cur_chip->frm);
|
pxa2xx_spi_write(drv_data, SSSR, chip->frm);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,7 +400,8 @@ static void cs_assert(struct driver_data *drv_data)
|
|||||||
|
|
||||||
static void cs_deassert(struct driver_data *drv_data)
|
static void cs_deassert(struct driver_data *drv_data)
|
||||||
{
|
{
|
||||||
struct chip_data *chip = drv_data->cur_chip;
|
struct chip_data *chip =
|
||||||
|
spi_get_ctldata(drv_data->master->cur_msg->spi);
|
||||||
|
|
||||||
if (drv_data->ssp_type == CE4100_SSP)
|
if (drv_data->ssp_type == CE4100_SSP)
|
||||||
return;
|
return;
|
||||||
@@ -508,7 +531,7 @@ static int u32_reader(struct driver_data *drv_data)
|
|||||||
|
|
||||||
void *pxa2xx_spi_next_transfer(struct driver_data *drv_data)
|
void *pxa2xx_spi_next_transfer(struct driver_data *drv_data)
|
||||||
{
|
{
|
||||||
struct spi_message *msg = drv_data->cur_msg;
|
struct spi_message *msg = drv_data->master->cur_msg;
|
||||||
struct spi_transfer *trans = drv_data->cur_transfer;
|
struct spi_transfer *trans = drv_data->cur_transfer;
|
||||||
|
|
||||||
/* Move to next transfer */
|
/* Move to next transfer */
|
||||||
@@ -529,8 +552,7 @@ static void giveback(struct driver_data *drv_data)
|
|||||||
struct spi_message *msg;
|
struct spi_message *msg;
|
||||||
unsigned long timeout;
|
unsigned long timeout;
|
||||||
|
|
||||||
msg = drv_data->cur_msg;
|
msg = drv_data->master->cur_msg;
|
||||||
drv_data->cur_msg = NULL;
|
|
||||||
drv_data->cur_transfer = NULL;
|
drv_data->cur_transfer = NULL;
|
||||||
|
|
||||||
last_transfer = list_last_entry(&msg->transfers, struct spi_transfer,
|
last_transfer = list_last_entry(&msg->transfers, struct spi_transfer,
|
||||||
@@ -575,13 +597,13 @@ static void giveback(struct driver_data *drv_data)
|
|||||||
cs_deassert(drv_data);
|
cs_deassert(drv_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
drv_data->cur_chip = NULL;
|
|
||||||
spi_finalize_current_message(drv_data->master);
|
spi_finalize_current_message(drv_data->master);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void reset_sccr1(struct driver_data *drv_data)
|
static void reset_sccr1(struct driver_data *drv_data)
|
||||||
{
|
{
|
||||||
struct chip_data *chip = drv_data->cur_chip;
|
struct chip_data *chip =
|
||||||
|
spi_get_ctldata(drv_data->master->cur_msg->spi);
|
||||||
u32 sccr1_reg;
|
u32 sccr1_reg;
|
||||||
|
|
||||||
sccr1_reg = pxa2xx_spi_read(drv_data, SSCR1) & ~drv_data->int_cr1;
|
sccr1_reg = pxa2xx_spi_read(drv_data, SSCR1) & ~drv_data->int_cr1;
|
||||||
@@ -589,6 +611,9 @@ static void reset_sccr1(struct driver_data *drv_data)
|
|||||||
case QUARK_X1000_SSP:
|
case QUARK_X1000_SSP:
|
||||||
sccr1_reg &= ~QUARK_X1000_SSCR1_RFT;
|
sccr1_reg &= ~QUARK_X1000_SSCR1_RFT;
|
||||||
break;
|
break;
|
||||||
|
case CE4100_SSP:
|
||||||
|
sccr1_reg &= ~CE4100_SSCR1_RFT;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
sccr1_reg &= ~SSCR1_RFT;
|
sccr1_reg &= ~SSCR1_RFT;
|
||||||
break;
|
break;
|
||||||
@@ -610,7 +635,7 @@ static void int_error_stop(struct driver_data *drv_data, const char* msg)
|
|||||||
|
|
||||||
dev_err(&drv_data->pdev->dev, "%s\n", msg);
|
dev_err(&drv_data->pdev->dev, "%s\n", msg);
|
||||||
|
|
||||||
drv_data->cur_msg->state = ERROR_STATE;
|
drv_data->master->cur_msg->state = ERROR_STATE;
|
||||||
tasklet_schedule(&drv_data->pump_transfers);
|
tasklet_schedule(&drv_data->pump_transfers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -623,7 +648,7 @@ static void int_transfer_complete(struct driver_data *drv_data)
|
|||||||
pxa2xx_spi_write(drv_data, SSTO, 0);
|
pxa2xx_spi_write(drv_data, SSTO, 0);
|
||||||
|
|
||||||
/* Update total byte transferred return count actual bytes read */
|
/* Update total byte transferred return count actual bytes read */
|
||||||
drv_data->cur_msg->actual_length += drv_data->len -
|
drv_data->master->cur_msg->actual_length += drv_data->len -
|
||||||
(drv_data->rx_end - drv_data->rx);
|
(drv_data->rx_end - drv_data->rx);
|
||||||
|
|
||||||
/* Transfer delays and chip select release are
|
/* Transfer delays and chip select release are
|
||||||
@@ -631,7 +656,7 @@ static void int_transfer_complete(struct driver_data *drv_data)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* Move to next transfer */
|
/* Move to next transfer */
|
||||||
drv_data->cur_msg->state = pxa2xx_spi_next_transfer(drv_data);
|
drv_data->master->cur_msg->state = pxa2xx_spi_next_transfer(drv_data);
|
||||||
|
|
||||||
/* Schedule transfer tasklet */
|
/* Schedule transfer tasklet */
|
||||||
tasklet_schedule(&drv_data->pump_transfers);
|
tasklet_schedule(&drv_data->pump_transfers);
|
||||||
@@ -746,7 +771,7 @@ static irqreturn_t ssp_int(int irq, void *dev_id)
|
|||||||
if (!(status & mask))
|
if (!(status & mask))
|
||||||
return IRQ_NONE;
|
return IRQ_NONE;
|
||||||
|
|
||||||
if (!drv_data->cur_msg) {
|
if (!drv_data->master->cur_msg) {
|
||||||
|
|
||||||
pxa2xx_spi_write(drv_data, SSCR0,
|
pxa2xx_spi_write(drv_data, SSCR0,
|
||||||
pxa2xx_spi_read(drv_data, SSCR0)
|
pxa2xx_spi_read(drv_data, SSCR0)
|
||||||
@@ -905,7 +930,8 @@ static unsigned int ssp_get_clk_div(struct driver_data *drv_data, int rate)
|
|||||||
static unsigned int pxa2xx_ssp_get_clk_div(struct driver_data *drv_data,
|
static unsigned int pxa2xx_ssp_get_clk_div(struct driver_data *drv_data,
|
||||||
int rate)
|
int rate)
|
||||||
{
|
{
|
||||||
struct chip_data *chip = drv_data->cur_chip;
|
struct chip_data *chip =
|
||||||
|
spi_get_ctldata(drv_data->master->cur_msg->spi);
|
||||||
unsigned int clk_div;
|
unsigned int clk_div;
|
||||||
|
|
||||||
switch (drv_data->ssp_type) {
|
switch (drv_data->ssp_type) {
|
||||||
@@ -934,25 +960,23 @@ static void pump_transfers(unsigned long data)
|
|||||||
{
|
{
|
||||||
struct driver_data *drv_data = (struct driver_data *)data;
|
struct driver_data *drv_data = (struct driver_data *)data;
|
||||||
struct spi_master *master = drv_data->master;
|
struct spi_master *master = drv_data->master;
|
||||||
struct spi_message *message = NULL;
|
struct spi_message *message = master->cur_msg;
|
||||||
struct spi_transfer *transfer = NULL;
|
struct chip_data *chip = spi_get_ctldata(message->spi);
|
||||||
struct spi_transfer *previous = NULL;
|
u32 dma_thresh = chip->dma_threshold;
|
||||||
struct chip_data *chip = NULL;
|
u32 dma_burst = chip->dma_burst_size;
|
||||||
u32 clk_div = 0;
|
u32 change_mask = pxa2xx_spi_get_ssrc1_change_mask(drv_data);
|
||||||
u8 bits = 0;
|
struct spi_transfer *transfer;
|
||||||
u32 speed = 0;
|
struct spi_transfer *previous;
|
||||||
|
u32 clk_div;
|
||||||
|
u8 bits;
|
||||||
|
u32 speed;
|
||||||
u32 cr0;
|
u32 cr0;
|
||||||
u32 cr1;
|
u32 cr1;
|
||||||
u32 dma_thresh = drv_data->cur_chip->dma_threshold;
|
|
||||||
u32 dma_burst = drv_data->cur_chip->dma_burst_size;
|
|
||||||
u32 change_mask = pxa2xx_spi_get_ssrc1_change_mask(drv_data);
|
|
||||||
int err;
|
int err;
|
||||||
int dma_mapped;
|
int dma_mapped;
|
||||||
|
|
||||||
/* Get current state information */
|
/* Get current state information */
|
||||||
message = drv_data->cur_msg;
|
|
||||||
transfer = drv_data->cur_transfer;
|
transfer = drv_data->cur_transfer;
|
||||||
chip = drv_data->cur_chip;
|
|
||||||
|
|
||||||
/* Handle for abort */
|
/* Handle for abort */
|
||||||
if (message->state == ERROR_STATE) {
|
if (message->state == ERROR_STATE) {
|
||||||
@@ -1146,17 +1170,12 @@ static int pxa2xx_spi_transfer_one_message(struct spi_master *master,
|
|||||||
{
|
{
|
||||||
struct driver_data *drv_data = spi_master_get_devdata(master);
|
struct driver_data *drv_data = spi_master_get_devdata(master);
|
||||||
|
|
||||||
drv_data->cur_msg = msg;
|
|
||||||
/* Initial message state*/
|
/* Initial message state*/
|
||||||
drv_data->cur_msg->state = START_STATE;
|
msg->state = START_STATE;
|
||||||
drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next,
|
drv_data->cur_transfer = list_entry(msg->transfers.next,
|
||||||
struct spi_transfer,
|
struct spi_transfer,
|
||||||
transfer_list);
|
transfer_list);
|
||||||
|
|
||||||
/* prepare to setup the SSP, in pump_transfers, using the per
|
|
||||||
* chip configuration */
|
|
||||||
drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi);
|
|
||||||
|
|
||||||
/* Mark as busy and launch transfers */
|
/* Mark as busy and launch transfers */
|
||||||
tasklet_schedule(&drv_data->pump_transfers);
|
tasklet_schedule(&drv_data->pump_transfers);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1176,9 +1195,26 @@ static int pxa2xx_spi_unprepare_transfer(struct spi_master *master)
|
|||||||
static int setup_cs(struct spi_device *spi, struct chip_data *chip,
|
static int setup_cs(struct spi_device *spi, struct chip_data *chip,
|
||||||
struct pxa2xx_spi_chip *chip_info)
|
struct pxa2xx_spi_chip *chip_info)
|
||||||
{
|
{
|
||||||
|
struct driver_data *drv_data = spi_master_get_devdata(spi->master);
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
if (chip == NULL || chip_info == NULL)
|
if (chip == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (drv_data->cs_gpiods) {
|
||||||
|
struct gpio_desc *gpiod;
|
||||||
|
|
||||||
|
gpiod = drv_data->cs_gpiods[spi->chip_select];
|
||||||
|
if (gpiod) {
|
||||||
|
chip->gpio_cs = desc_to_gpio(gpiod);
|
||||||
|
chip->gpio_cs_inverted = spi->mode & SPI_CS_HIGH;
|
||||||
|
gpiod_set_value(gpiod, chip->gpio_cs_inverted);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chip_info == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* NOTE: setup() can be called multiple times, possibly with
|
/* NOTE: setup() can be called multiple times, possibly with
|
||||||
@@ -1213,7 +1249,7 @@ static int setup_cs(struct spi_device *spi, struct chip_data *chip,
|
|||||||
|
|
||||||
static int setup(struct spi_device *spi)
|
static int setup(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct pxa2xx_spi_chip *chip_info = NULL;
|
struct pxa2xx_spi_chip *chip_info;
|
||||||
struct chip_data *chip;
|
struct chip_data *chip;
|
||||||
const struct lpss_config *config;
|
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);
|
||||||
@@ -1225,6 +1261,11 @@ 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 CE4100_SSP:
|
||||||
|
tx_thres = TX_THRESH_CE4100_DFLT;
|
||||||
|
tx_hi_thres = 0;
|
||||||
|
rx_thres = RX_THRESH_CE4100_DFLT;
|
||||||
|
break;
|
||||||
case LPSS_LPT_SSP:
|
case LPSS_LPT_SSP:
|
||||||
case LPSS_BYT_SSP:
|
case LPSS_BYT_SSP:
|
||||||
case LPSS_BSW_SSP:
|
case LPSS_BSW_SSP:
|
||||||
@@ -1309,6 +1350,10 @@ static int setup(struct spi_device *spi)
|
|||||||
| (QUARK_X1000_SSCR1_TxTresh(tx_thres)
|
| (QUARK_X1000_SSCR1_TxTresh(tx_thres)
|
||||||
& QUARK_X1000_SSCR1_TFT);
|
& QUARK_X1000_SSCR1_TFT);
|
||||||
break;
|
break;
|
||||||
|
case CE4100_SSP:
|
||||||
|
chip->threshold = (CE4100_SSCR1_RxTresh(rx_thres) & CE4100_SSCR1_RFT) |
|
||||||
|
(CE4100_SSCR1_TxTresh(tx_thres) & CE4100_SSCR1_TFT);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
chip->threshold = (SSCR1_RxTresh(rx_thres) & SSCR1_RFT) |
|
chip->threshold = (SSCR1_RxTresh(rx_thres) & SSCR1_RFT) |
|
||||||
(SSCR1_TxTresh(tx_thres) & SSCR1_TFT);
|
(SSCR1_TxTresh(tx_thres) & SSCR1_TFT);
|
||||||
@@ -1352,7 +1397,8 @@ static void cleanup(struct spi_device *spi)
|
|||||||
if (!chip)
|
if (!chip)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (drv_data->ssp_type != CE4100_SSP && gpio_is_valid(chip->gpio_cs))
|
if (drv_data->ssp_type != CE4100_SSP && !drv_data->cs_gpiods &&
|
||||||
|
gpio_is_valid(chip->gpio_cs))
|
||||||
gpio_free(chip->gpio_cs);
|
gpio_free(chip->gpio_cs);
|
||||||
|
|
||||||
kfree(chip);
|
kfree(chip);
|
||||||
@@ -1530,7 +1576,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
|||||||
struct driver_data *drv_data;
|
struct driver_data *drv_data;
|
||||||
struct ssp_device *ssp;
|
struct ssp_device *ssp;
|
||||||
const struct lpss_config *config;
|
const struct lpss_config *config;
|
||||||
int status;
|
int status, count;
|
||||||
u32 tmp;
|
u32 tmp;
|
||||||
|
|
||||||
platform_info = dev_get_platdata(dev);
|
platform_info = dev_get_platdata(dev);
|
||||||
@@ -1630,15 +1676,20 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
|||||||
pxa2xx_spi_write(drv_data, SSCR0, 0);
|
pxa2xx_spi_write(drv_data, SSCR0, 0);
|
||||||
switch (drv_data->ssp_type) {
|
switch (drv_data->ssp_type) {
|
||||||
case QUARK_X1000_SSP:
|
case QUARK_X1000_SSP:
|
||||||
tmp = QUARK_X1000_SSCR1_RxTresh(RX_THRESH_QUARK_X1000_DFLT)
|
tmp = QUARK_X1000_SSCR1_RxTresh(RX_THRESH_QUARK_X1000_DFLT) |
|
||||||
| QUARK_X1000_SSCR1_TxTresh(TX_THRESH_QUARK_X1000_DFLT);
|
QUARK_X1000_SSCR1_TxTresh(TX_THRESH_QUARK_X1000_DFLT);
|
||||||
pxa2xx_spi_write(drv_data, SSCR1, tmp);
|
pxa2xx_spi_write(drv_data, SSCR1, tmp);
|
||||||
|
|
||||||
/* using the Motorola SPI protocol and use 8 bit frame */
|
/* using the Motorola SPI protocol and use 8 bit frame */
|
||||||
pxa2xx_spi_write(drv_data, SSCR0,
|
tmp = QUARK_X1000_SSCR0_Motorola | QUARK_X1000_SSCR0_DataSize(8);
|
||||||
QUARK_X1000_SSCR0_Motorola
|
pxa2xx_spi_write(drv_data, SSCR0, tmp);
|
||||||
| QUARK_X1000_SSCR0_DataSize(8));
|
|
||||||
break;
|
break;
|
||||||
|
case CE4100_SSP:
|
||||||
|
tmp = CE4100_SSCR1_RxTresh(RX_THRESH_CE4100_DFLT) |
|
||||||
|
CE4100_SSCR1_TxTresh(TX_THRESH_CE4100_DFLT);
|
||||||
|
pxa2xx_spi_write(drv_data, SSCR1, tmp);
|
||||||
|
tmp = SSCR0_SCR(2) | SSCR0_Motorola | SSCR0_DataSize(8);
|
||||||
|
pxa2xx_spi_write(drv_data, SSCR0, tmp);
|
||||||
default:
|
default:
|
||||||
tmp = SSCR1_RxTresh(RX_THRESH_DFLT) |
|
tmp = SSCR1_RxTresh(RX_THRESH_DFLT) |
|
||||||
SSCR1_TxTresh(TX_THRESH_DFLT);
|
SSCR1_TxTresh(TX_THRESH_DFLT);
|
||||||
@@ -1669,6 +1720,39 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
master->num_chipselect = platform_info->num_chipselect;
|
master->num_chipselect = platform_info->num_chipselect;
|
||||||
|
|
||||||
|
count = gpiod_count(&pdev->dev, "cs");
|
||||||
|
if (count > 0) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
master->num_chipselect = max_t(int, count,
|
||||||
|
master->num_chipselect);
|
||||||
|
|
||||||
|
drv_data->cs_gpiods = devm_kcalloc(&pdev->dev,
|
||||||
|
master->num_chipselect, sizeof(struct gpio_desc *),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!drv_data->cs_gpiods) {
|
||||||
|
status = -ENOMEM;
|
||||||
|
goto out_error_clock_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < master->num_chipselect; i++) {
|
||||||
|
struct gpio_desc *gpiod;
|
||||||
|
|
||||||
|
gpiod = devm_gpiod_get_index(dev, "cs", i,
|
||||||
|
GPIOD_OUT_HIGH);
|
||||||
|
if (IS_ERR(gpiod)) {
|
||||||
|
/* Means use native chip select */
|
||||||
|
if (PTR_ERR(gpiod) == -ENOENT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
status = (int)PTR_ERR(gpiod);
|
||||||
|
goto out_error_clock_enabled;
|
||||||
|
} else {
|
||||||
|
drv_data->cs_gpiods[i] = gpiod;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tasklet_init(&drv_data->pump_transfers, pump_transfers,
|
tasklet_init(&drv_data->pump_transfers, pump_transfers,
|
||||||
(unsigned long)drv_data);
|
(unsigned long)drv_data);
|
||||||
|
|
||||||
@@ -1742,7 +1826,7 @@ static int pxa2xx_spi_suspend(struct device *dev)
|
|||||||
{
|
{
|
||||||
struct driver_data *drv_data = dev_get_drvdata(dev);
|
struct driver_data *drv_data = dev_get_drvdata(dev);
|
||||||
struct ssp_device *ssp = drv_data->ssp;
|
struct ssp_device *ssp = drv_data->ssp;
|
||||||
int status = 0;
|
int status;
|
||||||
|
|
||||||
status = spi_master_suspend(drv_data->master);
|
status = spi_master_suspend(drv_data->master);
|
||||||
if (status != 0)
|
if (status != 0)
|
||||||
@@ -1759,7 +1843,7 @@ static int pxa2xx_spi_resume(struct device *dev)
|
|||||||
{
|
{
|
||||||
struct driver_data *drv_data = dev_get_drvdata(dev);
|
struct driver_data *drv_data = dev_get_drvdata(dev);
|
||||||
struct ssp_device *ssp = drv_data->ssp;
|
struct ssp_device *ssp = drv_data->ssp;
|
||||||
int status = 0;
|
int status;
|
||||||
|
|
||||||
/* Enable the SSP clock */
|
/* Enable the SSP clock */
|
||||||
if (!pm_runtime_suspended(dev))
|
if (!pm_runtime_suspended(dev))
|
||||||
|
@@ -53,9 +53,7 @@ struct driver_data {
|
|||||||
atomic_t dma_running;
|
atomic_t dma_running;
|
||||||
|
|
||||||
/* Current message transfer state info */
|
/* Current message transfer state info */
|
||||||
struct spi_message *cur_msg;
|
|
||||||
struct spi_transfer *cur_transfer;
|
struct spi_transfer *cur_transfer;
|
||||||
struct chip_data *cur_chip;
|
|
||||||
size_t len;
|
size_t len;
|
||||||
void *tx;
|
void *tx;
|
||||||
void *tx_end;
|
void *tx_end;
|
||||||
@@ -68,6 +66,9 @@ struct driver_data {
|
|||||||
void (*cs_control)(u32 command);
|
void (*cs_control)(u32 command);
|
||||||
|
|
||||||
void __iomem *lpss_base;
|
void __iomem *lpss_base;
|
||||||
|
|
||||||
|
/* GPIOs for chip selects */
|
||||||
|
struct gpio_desc **cs_gpiods;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct chip_data {
|
struct chip_data {
|
||||||
|
@@ -982,8 +982,10 @@ static int spi_qup_suspend(struct device *device)
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
if (!pm_runtime_suspended(device)) {
|
||||||
clk_disable_unprepare(controller->cclk);
|
clk_disable_unprepare(controller->cclk);
|
||||||
clk_disable_unprepare(controller->iclk);
|
clk_disable_unprepare(controller->iclk);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -83,7 +83,6 @@
|
|||||||
#define SSSR_RFS (1 << 6) /* Receive FIFO Service Request */
|
#define SSSR_RFS (1 << 6) /* Receive FIFO Service Request */
|
||||||
#define SSSR_ROR (1 << 7) /* Receive FIFO Overrun */
|
#define SSSR_ROR (1 << 7) /* Receive FIFO Overrun */
|
||||||
|
|
||||||
#ifdef CONFIG_ARCH_PXA
|
|
||||||
#define RX_THRESH_DFLT 8
|
#define RX_THRESH_DFLT 8
|
||||||
#define TX_THRESH_DFLT 8
|
#define TX_THRESH_DFLT 8
|
||||||
|
|
||||||
@@ -95,19 +94,16 @@
|
|||||||
#define SSCR1_RFT (0x00003c00) /* Receive FIFO Threshold (mask) */
|
#define SSCR1_RFT (0x00003c00) /* Receive FIFO Threshold (mask) */
|
||||||
#define SSCR1_RxTresh(x) (((x) - 1) << 10) /* level [1..16] */
|
#define SSCR1_RxTresh(x) (((x) - 1) << 10) /* level [1..16] */
|
||||||
|
|
||||||
#else
|
#define RX_THRESH_CE4100_DFLT 2
|
||||||
|
#define TX_THRESH_CE4100_DFLT 2
|
||||||
|
|
||||||
#define RX_THRESH_DFLT 2
|
#define CE4100_SSSR_TFL_MASK (0x3 << 8) /* Transmit FIFO Level mask */
|
||||||
#define TX_THRESH_DFLT 2
|
#define CE4100_SSSR_RFL_MASK (0x3 << 12) /* Receive FIFO Level mask */
|
||||||
|
|
||||||
#define SSSR_TFL_MASK (0x3 << 8) /* Transmit FIFO Level mask */
|
#define CE4100_SSCR1_TFT (0x000000c0) /* Transmit FIFO Threshold (mask) */
|
||||||
#define SSSR_RFL_MASK (0x3 << 12) /* Receive FIFO Level mask */
|
#define CE4100_SSCR1_TxTresh(x) (((x) - 1) << 6) /* level [1..4] */
|
||||||
|
#define CE4100_SSCR1_RFT (0x00000c00) /* Receive FIFO Threshold (mask) */
|
||||||
#define SSCR1_TFT (0x000000c0) /* Transmit FIFO Threshold (mask) */
|
#define CE4100_SSCR1_RxTresh(x) (((x) - 1) << 10) /* level [1..4] */
|
||||||
#define SSCR1_TxTresh(x) (((x) - 1) << 6) /* level [1..4] */
|
|
||||||
#define SSCR1_RFT (0x00000c00) /* Receive FIFO Threshold (mask) */
|
|
||||||
#define SSCR1_RxTresh(x) (((x) - 1) << 10) /* level [1..4] */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* QUARK_X1000 SSCR0 bit definition */
|
/* QUARK_X1000 SSCR0 bit definition */
|
||||||
#define QUARK_X1000_SSCR0_DSS (0x1F) /* Data Size Select (mask) */
|
#define QUARK_X1000_SSCR0_DSS (0x1F) /* Data Size Select (mask) */
|
||||||
|
Reference in New Issue
Block a user