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:
Коммит
66b5a337d0
|
@ -648,6 +648,13 @@ config SPI_TEGRA20_SLINK
|
|||
help
|
||||
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
|
||||
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) SPI"
|
||||
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_SLINK) += spi-tegra20-slink.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_TXX9) += spi-txx9.o
|
||||
obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o
|
||||
|
|
|
@ -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
|
||||
#define __SPI_CAVIUM_H
|
||||
|
||||
#include <linux/clk.h>
|
||||
|
||||
#define OCTEON_SPI_MAX_BYTES 9
|
||||
#define OCTEON_SPI_MAX_CLOCK_HZ 16000000
|
||||
|
||||
|
@ -17,6 +19,7 @@ struct octeon_spi {
|
|||
u64 cs_enax;
|
||||
int sys_freq;
|
||||
struct octeon_spi_regs regs;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
#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;
|
||||
|
||||
rdesc = list_first_entry(&sqi->bd_list_free, struct ring_desc, list);
|
||||
list_del(&rdesc->list);
|
||||
list_add_tail(&rdesc->list, &sqi->bd_list_used);
|
||||
list_move_tail(&rdesc->list, &sqi->bd_list_used);
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
static void ring_desc_put(struct pic32_sqi *sqi, struct ring_desc *rdesc)
|
||||
{
|
||||
list_del(&rdesc->list);
|
||||
list_add(&rdesc->list, &sqi->bd_list_free);
|
||||
list_move(&rdesc->list, &sqi->bd_list_free);
|
||||
}
|
||||
|
||||
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,
|
||||
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
|
||||
|
@ -76,7 +76,8 @@ static struct dma_async_tx_descriptor *
|
|||
pxa2xx_spi_dma_prepare_one(struct driver_data *drv_data,
|
||||
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;
|
||||
enum dma_slave_buswidth width;
|
||||
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)
|
||||
{
|
||||
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);
|
||||
if (!tx_desc) {
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <linux/spi/spi.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
@ -62,6 +63,13 @@ MODULE_ALIAS("platform:pxa2xx-spi");
|
|||
| QUARK_X1000_SSCR1_TFT \
|
||||
| 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_CS_CONTROL_SW_MODE BIT(0)
|
||||
#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) {
|
||||
case QUARK_X1000_SSP:
|
||||
return QUARK_X1000_SSCR1_CHANGE_MASK;
|
||||
case CE4100_SSP:
|
||||
return CE4100_SSCR1_CHANGE_MASK;
|
||||
default:
|
||||
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) {
|
||||
case QUARK_X1000_SSP:
|
||||
return RX_THRESH_QUARK_X1000_DFLT;
|
||||
case CE4100_SSP:
|
||||
return RX_THRESH_CE4100_DFLT;
|
||||
default:
|
||||
return RX_THRESH_DFLT;
|
||||
}
|
||||
|
@ -199,6 +211,9 @@ static bool pxa2xx_spi_txfifo_full(const struct driver_data *drv_data)
|
|||
case QUARK_X1000_SSP:
|
||||
mask = QUARK_X1000_SSSR_TFL_MASK;
|
||||
break;
|
||||
case CE4100_SSP:
|
||||
mask = CE4100_SSSR_TFL_MASK;
|
||||
break;
|
||||
default:
|
||||
mask = SSSR_TFL_MASK;
|
||||
break;
|
||||
|
@ -216,6 +231,9 @@ static void pxa2xx_spi_clear_rx_thre(const struct driver_data *drv_data,
|
|||
case QUARK_X1000_SSP:
|
||||
mask = QUARK_X1000_SSCR1_RFT;
|
||||
break;
|
||||
case CE4100_SSP:
|
||||
mask = CE4100_SSCR1_RFT;
|
||||
break;
|
||||
default:
|
||||
mask = SSCR1_RFT;
|
||||
break;
|
||||
|
@ -230,6 +248,9 @@ static void pxa2xx_spi_set_rx_thre(const struct driver_data *drv_data,
|
|||
case QUARK_X1000_SSP:
|
||||
*sccr1_reg |= QUARK_X1000_SSCR1_RxTresh(threshold);
|
||||
break;
|
||||
case CE4100_SSP:
|
||||
*sccr1_reg |= CE4100_SSCR1_RxTresh(threshold);
|
||||
break;
|
||||
default:
|
||||
*sccr1_reg |= SSCR1_RxTresh(threshold);
|
||||
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);
|
||||
|
||||
cs = drv_data->cur_msg->spi->chip_select;
|
||||
cs = drv_data->master->cur_msg->spi->chip_select;
|
||||
cs <<= config->cs_sel_shift;
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
pxa2xx_spi_write(drv_data, SSSR, drv_data->cur_chip->frm);
|
||||
pxa2xx_spi_write(drv_data, SSSR, chip->frm);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -378,7 +400,8 @@ static void cs_assert(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)
|
||||
return;
|
||||
|
@ -508,7 +531,7 @@ static int u32_reader(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;
|
||||
|
||||
/* Move to next transfer */
|
||||
|
@ -529,8 +552,7 @@ static void giveback(struct driver_data *drv_data)
|
|||
struct spi_message *msg;
|
||||
unsigned long timeout;
|
||||
|
||||
msg = drv_data->cur_msg;
|
||||
drv_data->cur_msg = NULL;
|
||||
msg = drv_data->master->cur_msg;
|
||||
drv_data->cur_transfer = NULL;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
drv_data->cur_chip = NULL;
|
||||
spi_finalize_current_message(drv_data->master);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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:
|
||||
sccr1_reg &= ~QUARK_X1000_SSCR1_RFT;
|
||||
break;
|
||||
case CE4100_SSP:
|
||||
sccr1_reg &= ~CE4100_SSCR1_RFT;
|
||||
break;
|
||||
default:
|
||||
sccr1_reg &= ~SSCR1_RFT;
|
||||
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);
|
||||
|
||||
drv_data->cur_msg->state = ERROR_STATE;
|
||||
drv_data->master->cur_msg->state = ERROR_STATE;
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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 */
|
||||
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 */
|
||||
tasklet_schedule(&drv_data->pump_transfers);
|
||||
|
@ -746,7 +771,7 @@ static irqreturn_t ssp_int(int irq, void *dev_id)
|
|||
if (!(status & mask))
|
||||
return IRQ_NONE;
|
||||
|
||||
if (!drv_data->cur_msg) {
|
||||
if (!drv_data->master->cur_msg) {
|
||||
|
||||
pxa2xx_spi_write(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,
|
||||
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;
|
||||
|
||||
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 spi_master *master = drv_data->master;
|
||||
struct spi_message *message = NULL;
|
||||
struct spi_transfer *transfer = NULL;
|
||||
struct spi_transfer *previous = NULL;
|
||||
struct chip_data *chip = NULL;
|
||||
u32 clk_div = 0;
|
||||
u8 bits = 0;
|
||||
u32 speed = 0;
|
||||
struct spi_message *message = master->cur_msg;
|
||||
struct chip_data *chip = spi_get_ctldata(message->spi);
|
||||
u32 dma_thresh = chip->dma_threshold;
|
||||
u32 dma_burst = chip->dma_burst_size;
|
||||
u32 change_mask = pxa2xx_spi_get_ssrc1_change_mask(drv_data);
|
||||
struct spi_transfer *transfer;
|
||||
struct spi_transfer *previous;
|
||||
u32 clk_div;
|
||||
u8 bits;
|
||||
u32 speed;
|
||||
u32 cr0;
|
||||
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 dma_mapped;
|
||||
|
||||
/* Get current state information */
|
||||
message = drv_data->cur_msg;
|
||||
transfer = drv_data->cur_transfer;
|
||||
chip = drv_data->cur_chip;
|
||||
|
||||
/* Handle for abort */
|
||||
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);
|
||||
|
||||
drv_data->cur_msg = msg;
|
||||
/* Initial message state*/
|
||||
drv_data->cur_msg->state = START_STATE;
|
||||
drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next,
|
||||
msg->state = START_STATE;
|
||||
drv_data->cur_transfer = list_entry(msg->transfers.next,
|
||||
struct spi_transfer,
|
||||
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 */
|
||||
tasklet_schedule(&drv_data->pump_transfers);
|
||||
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,
|
||||
struct pxa2xx_spi_chip *chip_info)
|
||||
{
|
||||
struct driver_data *drv_data = spi_master_get_devdata(spi->master);
|
||||
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;
|
||||
|
||||
/* 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)
|
||||
{
|
||||
struct pxa2xx_spi_chip *chip_info = NULL;
|
||||
struct pxa2xx_spi_chip *chip_info;
|
||||
struct chip_data *chip;
|
||||
const struct lpss_config *config;
|
||||
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;
|
||||
rx_thres = RX_THRESH_QUARK_X1000_DFLT;
|
||||
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_BYT_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_TFT);
|
||||
break;
|
||||
case CE4100_SSP:
|
||||
chip->threshold = (CE4100_SSCR1_RxTresh(rx_thres) & CE4100_SSCR1_RFT) |
|
||||
(CE4100_SSCR1_TxTresh(tx_thres) & CE4100_SSCR1_TFT);
|
||||
break;
|
||||
default:
|
||||
chip->threshold = (SSCR1_RxTresh(rx_thres) & SSCR1_RFT) |
|
||||
(SSCR1_TxTresh(tx_thres) & SSCR1_TFT);
|
||||
|
@ -1352,7 +1397,8 @@ static void cleanup(struct spi_device *spi)
|
|||
if (!chip)
|
||||
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);
|
||||
|
||||
kfree(chip);
|
||||
|
@ -1530,7 +1576,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
|||
struct driver_data *drv_data;
|
||||
struct ssp_device *ssp;
|
||||
const struct lpss_config *config;
|
||||
int status;
|
||||
int status, count;
|
||||
u32 tmp;
|
||||
|
||||
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);
|
||||
switch (drv_data->ssp_type) {
|
||||
case QUARK_X1000_SSP:
|
||||
tmp = QUARK_X1000_SSCR1_RxTresh(RX_THRESH_QUARK_X1000_DFLT)
|
||||
| QUARK_X1000_SSCR1_TxTresh(TX_THRESH_QUARK_X1000_DFLT);
|
||||
tmp = QUARK_X1000_SSCR1_RxTresh(RX_THRESH_QUARK_X1000_DFLT) |
|
||||
QUARK_X1000_SSCR1_TxTresh(TX_THRESH_QUARK_X1000_DFLT);
|
||||
pxa2xx_spi_write(drv_data, SSCR1, tmp);
|
||||
|
||||
/* using the Motorola SPI protocol and use 8 bit frame */
|
||||
pxa2xx_spi_write(drv_data, SSCR0,
|
||||
QUARK_X1000_SSCR0_Motorola
|
||||
| QUARK_X1000_SSCR0_DataSize(8));
|
||||
tmp = QUARK_X1000_SSCR0_Motorola | QUARK_X1000_SSCR0_DataSize(8);
|
||||
pxa2xx_spi_write(drv_data, SSCR0, tmp);
|
||||
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:
|
||||
tmp = SSCR1_RxTresh(RX_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;
|
||||
|
||||
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,
|
||||
(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 ssp_device *ssp = drv_data->ssp;
|
||||
int status = 0;
|
||||
int status;
|
||||
|
||||
status = spi_master_suspend(drv_data->master);
|
||||
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 ssp_device *ssp = drv_data->ssp;
|
||||
int status = 0;
|
||||
int status;
|
||||
|
||||
/* Enable the SSP clock */
|
||||
if (!pm_runtime_suspended(dev))
|
||||
|
|
|
@ -53,9 +53,7 @@ struct driver_data {
|
|||
atomic_t dma_running;
|
||||
|
||||
/* Current message transfer state info */
|
||||
struct spi_message *cur_msg;
|
||||
struct spi_transfer *cur_transfer;
|
||||
struct chip_data *cur_chip;
|
||||
size_t len;
|
||||
void *tx;
|
||||
void *tx_end;
|
||||
|
@ -68,6 +66,9 @@ struct driver_data {
|
|||
void (*cs_control)(u32 command);
|
||||
|
||||
void __iomem *lpss_base;
|
||||
|
||||
/* GPIOs for chip selects */
|
||||
struct gpio_desc **cs_gpiods;
|
||||
};
|
||||
|
||||
struct chip_data {
|
||||
|
|
|
@ -982,8 +982,10 @@ static int spi_qup_suspend(struct device *device)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
clk_disable_unprepare(controller->cclk);
|
||||
clk_disable_unprepare(controller->iclk);
|
||||
if (!pm_runtime_suspended(device)) {
|
||||
clk_disable_unprepare(controller->cclk);
|
||||
clk_disable_unprepare(controller->iclk);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -83,7 +83,6 @@
|
|||
#define SSSR_RFS (1 << 6) /* Receive FIFO Service Request */
|
||||
#define SSSR_ROR (1 << 7) /* Receive FIFO Overrun */
|
||||
|
||||
#ifdef CONFIG_ARCH_PXA
|
||||
#define RX_THRESH_DFLT 8
|
||||
#define TX_THRESH_DFLT 8
|
||||
|
||||
|
@ -95,19 +94,16 @@
|
|||
#define SSCR1_RFT (0x00003c00) /* Receive FIFO Threshold (mask) */
|
||||
#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 TX_THRESH_DFLT 2
|
||||
#define CE4100_SSSR_TFL_MASK (0x3 << 8) /* Transmit FIFO Level mask */
|
||||
#define CE4100_SSSR_RFL_MASK (0x3 << 12) /* Receive FIFO Level mask */
|
||||
|
||||
#define SSSR_TFL_MASK (0x3 << 8) /* Transmit FIFO Level mask */
|
||||
#define SSSR_RFL_MASK (0x3 << 12) /* Receive FIFO Level mask */
|
||||
|
||||
#define SSCR1_TFT (0x000000c0) /* Transmit FIFO Threshold (mask) */
|
||||
#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
|
||||
#define CE4100_SSCR1_TFT (0x000000c0) /* Transmit FIFO Threshold (mask) */
|
||||
#define CE4100_SSCR1_TxTresh(x) (((x) - 1) << 6) /* level [1..4] */
|
||||
#define CE4100_SSCR1_RFT (0x00000c00) /* Receive FIFO Threshold (mask) */
|
||||
#define CE4100_SSCR1_RxTresh(x) (((x) - 1) << 10) /* level [1..4] */
|
||||
|
||||
/* QUARK_X1000 SSCR0 bit definition */
|
||||
#define QUARK_X1000_SSCR0_DSS (0x1F) /* Data Size Select (mask) */
|
||||
|
|
Загрузка…
Ссылка в новой задаче