Commit 9a4506b6 authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branches 'spi/topic/pxa2xx', 'spi/topic/rockchip',...

Merge remote-tracking branches 'spi/topic/pxa2xx', 'spi/topic/rockchip', 'spi/topic/s3c64xx', 'spi/topic/sh' and 'spi/topic/sh-msiof' into spi-next
......@@ -6,10 +6,13 @@ and display controllers using the SPI communication interface.
Required Properties:
- compatible: should be one of the following.
"rockchip,rk3066-spi" for rk3066.
"rockchip,rk3188-spi", "rockchip,rk3066-spi" for rk3188.
"rockchip,rk3288-spi", "rockchip,rk3066-spi" for rk3288.
"rockchip,rk3399-spi", "rockchip,rk3066-spi" for rk3399.
"rockchip,rk3036-spi" for rk3036 SoCS.
"rockchip,rk3066-spi" for rk3066 SoCs.
"rockchip,rk3188-spi" for rk3188 SoCs.
"rockchip,rk3228-spi" for rk3228 SoCS.
"rockchip,rk3288-spi" for rk3288 SoCs.
"rockchip,rk3368-spi" for rk3368 SoCs.
"rockchip,rk3399-spi" for rk3399 SoCs.
- reg: physical base address of the controller and length of memory mapped
region.
- interrupts: The interrupt number to the cpu. The interrupt specifier format
......
......@@ -9,7 +9,8 @@ Required SoC Specific Properties:
- samsung,s3c2443-spi: for s3c2443, s3c2416 and s3c2450 platforms
- samsung,s3c6410-spi: for s3c6410 platforms
- samsung,s5pv210-spi: for s5pv210 and s5pc110 platforms
- samsung,exynos7-spi: for exynos7 platforms
- samsung,exynos5433-spi: for exynos5433 compatible controllers
- samsung,exynos7-spi: for exynos7 platforms <DEPRECATED>
- reg: physical base address of the controller and length of memory mapped
region.
......@@ -23,6 +24,15 @@ Required SoC Specific Properties:
- dma-names: Names for the dma channels. There must be at least one channel
named "tx" for transmit and named "rx" for receive.
- clocks: specifies the clock IDs provided to the SPI controller; they are
required for interacting with the controller itself, for synchronizing the bus
and as I/O clock (the latter is required by exynos5433 and exynos7).
- clock-names: string names of the clocks in the 'clocks' property; for all the
the devices the names must be "spi", "spi_busclkN" (where N is determined by
"samsung,spi-src-clk"), while Exynos5433 should specify a third clock
"spi_ioclk" for the I/O clock.
Required Board Specific Properties:
- #address-cells: should be 1.
......@@ -40,6 +50,9 @@ Optional Board Specific Properties:
- cs-gpios: should specify GPIOs used for chipselects (see spi-bus.txt)
- no-cs-readback: the CS line is disconnected, therefore the device should not
operate based on CS signalling.
SPI Controller specific data in SPI slave nodes:
- The spi slave nodes should provide the following information which is required
......
......@@ -20,79 +20,6 @@
#include "spi-pxa2xx.h"
static int pxa2xx_spi_map_dma_buffer(struct driver_data *drv_data,
enum dma_data_direction dir)
{
int i, nents, len = drv_data->len;
struct scatterlist *sg;
struct device *dmadev;
struct sg_table *sgt;
void *buf, *pbuf;
if (dir == DMA_TO_DEVICE) {
dmadev = drv_data->tx_chan->device->dev;
sgt = &drv_data->tx_sgt;
buf = drv_data->tx;
} else {
dmadev = drv_data->rx_chan->device->dev;
sgt = &drv_data->rx_sgt;
buf = drv_data->rx;
}
nents = DIV_ROUND_UP(len, SZ_2K);
if (nents != sgt->nents) {
int ret;
sg_free_table(sgt);
ret = sg_alloc_table(sgt, nents, GFP_ATOMIC);
if (ret)
return ret;
}
pbuf = buf;
for_each_sg(sgt->sgl, sg, sgt->nents, i) {
size_t bytes = min_t(size_t, len, SZ_2K);
sg_set_buf(sg, pbuf, bytes);
pbuf += bytes;
len -= bytes;
}
nents = dma_map_sg(dmadev, sgt->sgl, sgt->nents, dir);
if (!nents)
return -ENOMEM;
return nents;
}
static void pxa2xx_spi_unmap_dma_buffer(struct driver_data *drv_data,
enum dma_data_direction dir)
{
struct device *dmadev;
struct sg_table *sgt;
if (dir == DMA_TO_DEVICE) {
dmadev = drv_data->tx_chan->device->dev;
sgt = &drv_data->tx_sgt;
} else {
dmadev = drv_data->rx_chan->device->dev;
sgt = &drv_data->rx_sgt;
}
dma_unmap_sg(dmadev, sgt->sgl, sgt->nents, dir);
}
static void pxa2xx_spi_unmap_dma_buffers(struct driver_data *drv_data)
{
if (!drv_data->dma_mapped)
return;
pxa2xx_spi_unmap_dma_buffer(drv_data, DMA_FROM_DEVICE);
pxa2xx_spi_unmap_dma_buffer(drv_data, DMA_TO_DEVICE);
drv_data->dma_mapped = 0;
}
static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data,
bool error)
{
......@@ -125,8 +52,6 @@ static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data,
pxa2xx_spi_write(drv_data, SSTO, 0);
if (!error) {
pxa2xx_spi_unmap_dma_buffers(drv_data);
msg->actual_length += drv_data->len;
msg->state = pxa2xx_spi_next_transfer(drv_data);
} else {
......@@ -152,11 +77,12 @@ pxa2xx_spi_dma_prepare_one(struct driver_data *drv_data,
enum dma_transfer_direction dir)
{
struct chip_data *chip = drv_data->cur_chip;
struct spi_transfer *xfer = drv_data->cur_transfer;
enum dma_slave_buswidth width;
struct dma_slave_config cfg;
struct dma_chan *chan;
struct sg_table *sgt;
int nents, ret;
int ret;
switch (drv_data->n_bytes) {
case 1:
......@@ -178,17 +104,15 @@ pxa2xx_spi_dma_prepare_one(struct driver_data *drv_data,
cfg.dst_addr_width = width;
cfg.dst_maxburst = chip->dma_burst_size;
sgt = &drv_data->tx_sgt;
nents = drv_data->tx_nents;
chan = drv_data->tx_chan;
sgt = &xfer->tx_sg;
chan = drv_data->master->dma_tx;
} else {
cfg.src_addr = drv_data->ssdr_physical;
cfg.src_addr_width = width;
cfg.src_maxburst = chip->dma_burst_size;
sgt = &drv_data->rx_sgt;
nents = drv_data->rx_nents;
chan = drv_data->rx_chan;
sgt = &xfer->rx_sg;
chan = drv_data->master->dma_rx;
}
ret = dmaengine_slave_config(chan, &cfg);
......@@ -197,46 +121,10 @@ pxa2xx_spi_dma_prepare_one(struct driver_data *drv_data,
return NULL;
}
return dmaengine_prep_slave_sg(chan, sgt->sgl, nents, dir,
return dmaengine_prep_slave_sg(chan, sgt->sgl, sgt->nents, dir,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
}
bool pxa2xx_spi_dma_is_possible(size_t len)
{
return len <= MAX_DMA_LEN;
}
int pxa2xx_spi_map_dma_buffers(struct driver_data *drv_data)
{
const struct chip_data *chip = drv_data->cur_chip;
int ret;
if (!chip->enable_dma)
return 0;
/* Don't bother with DMA if we can't do even a single burst */
if (drv_data->len < chip->dma_burst_size)
return 0;
ret = pxa2xx_spi_map_dma_buffer(drv_data, DMA_TO_DEVICE);
if (ret <= 0) {
dev_warn(&drv_data->pdev->dev, "failed to DMA map TX\n");
return 0;
}
drv_data->tx_nents = ret;
ret = pxa2xx_spi_map_dma_buffer(drv_data, DMA_FROM_DEVICE);
if (ret <= 0) {
pxa2xx_spi_unmap_dma_buffer(drv_data, DMA_TO_DEVICE);
dev_warn(&drv_data->pdev->dev, "failed to DMA map RX\n");
return 0;
}
drv_data->rx_nents = ret;
return 1;
}
irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data)
{
u32 status;
......@@ -245,8 +133,8 @@ irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data)
if (status & SSSR_ROR) {
dev_err(&drv_data->pdev->dev, "FIFO overrun\n");
dmaengine_terminate_async(drv_data->rx_chan);
dmaengine_terminate_async(drv_data->tx_chan);
dmaengine_terminate_async(drv_data->master->dma_rx);
dmaengine_terminate_async(drv_data->master->dma_tx);
pxa2xx_spi_dma_transfer_complete(drv_data, true);
return IRQ_HANDLED;
......@@ -285,16 +173,15 @@ int pxa2xx_spi_dma_prepare(struct driver_data *drv_data, u32 dma_burst)
return 0;
err_rx:
dmaengine_terminate_async(drv_data->tx_chan);
dmaengine_terminate_async(drv_data->master->dma_tx);
err_tx:
pxa2xx_spi_unmap_dma_buffers(drv_data);
return err;
}
void pxa2xx_spi_dma_start(struct driver_data *drv_data)
{
dma_async_issue_pending(drv_data->rx_chan);
dma_async_issue_pending(drv_data->tx_chan);
dma_async_issue_pending(drv_data->master->dma_rx);
dma_async_issue_pending(drv_data->master->dma_tx);
atomic_set(&drv_data->dma_running, 1);
}
......@@ -303,21 +190,22 @@ int pxa2xx_spi_dma_setup(struct driver_data *drv_data)
{
struct pxa2xx_spi_master *pdata = drv_data->master_info;
struct device *dev = &drv_data->pdev->dev;
struct spi_master *master = drv_data->master;
dma_cap_mask_t mask;
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
drv_data->tx_chan = dma_request_slave_channel_compat(mask,
master->dma_tx = dma_request_slave_channel_compat(mask,
pdata->dma_filter, pdata->tx_param, dev, "tx");
if (!drv_data->tx_chan)
if (!master->dma_tx)
return -ENODEV;
drv_data->rx_chan = dma_request_slave_channel_compat(mask,
master->dma_rx = dma_request_slave_channel_compat(mask,
pdata->dma_filter, pdata->rx_param, dev, "rx");
if (!drv_data->rx_chan) {
dma_release_channel(drv_data->tx_chan);
drv_data->tx_chan = NULL;
if (!master->dma_rx) {
dma_release_channel(master->dma_tx);
master->dma_tx = NULL;
return -ENODEV;
}
......@@ -326,17 +214,17 @@ int pxa2xx_spi_dma_setup(struct driver_data *drv_data)
void pxa2xx_spi_dma_release(struct driver_data *drv_data)
{
if (drv_data->rx_chan) {
dmaengine_terminate_sync(drv_data->rx_chan);
dma_release_channel(drv_data->rx_chan);
sg_free_table(&drv_data->rx_sgt);
drv_data->rx_chan = NULL;
struct spi_master *master = drv_data->master;
if (master->dma_rx) {
dmaengine_terminate_sync(master->dma_rx);
dma_release_channel(master->dma_rx);
master->dma_rx = NULL;
}
if (drv_data->tx_chan) {
dmaengine_terminate_sync(drv_data->tx_chan);
dma_release_channel(drv_data->tx_chan);
sg_free_table(&drv_data->tx_sgt);
drv_data->tx_chan = NULL;
if (master->dma_tx) {
dmaengine_terminate_sync(master->dma_tx);
dma_release_channel(master->dma_tx);
master->dma_tx = NULL;
}
}
......
/*
* CE4100's SPI device is more or less the same one as found on PXA
*
* Copyright (C) 2016, Intel Corporation
*/
#include <linux/clk-provider.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/module.h>
#include <linux/spi/pxa2xx_spi.h>
#include <linux/clk-provider.h>
#include <linux/dmaengine.h>
#include <linux/platform_data/dma-dw.h>
enum {
PORT_CE4100,
PORT_QUARK_X1000,
PORT_BYT,
PORT_MRFLD,
PORT_BSW0,
PORT_BSW1,
PORT_BSW2,
PORT_QUARK_X1000,
PORT_CE4100,
PORT_LPT,
};
......@@ -29,8 +31,11 @@ struct pxa_spi_info {
unsigned long max_clk_rate;
/* DMA channel request parameters */
bool (*dma_filter)(struct dma_chan *chan, void *param);
void *tx_param;
void *rx_param;
int (*setup)(struct pci_dev *pdev, struct pxa_spi_info *c);
};
static struct dw_dma_slave byt_tx_param = { .dst_id = 0 };
......@@ -57,6 +62,56 @@ static bool lpss_dma_filter(struct dma_chan *chan, void *param)
return true;
}
static int lpss_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c)
{
struct pci_dev *dma_dev;
c->num_chipselect = 1;
c->max_clk_rate = 50000000;
dma_dev = pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
if (c->tx_param) {
struct dw_dma_slave *slave = c->tx_param;
slave->dma_dev = &dma_dev->dev;
slave->m_master = 0;
slave->p_master = 1;
}
if (c->rx_param) {
struct dw_dma_slave *slave = c->rx_param;
slave->dma_dev = &dma_dev->dev;
slave->m_master = 0;
slave->p_master = 1;
}
c->dma_filter = lpss_dma_filter;
return 0;
}
static int mrfld_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c)
{
switch (PCI_FUNC(dev->devfn)) {
case 0:
c->port_id = 3;
c->num_chipselect = 1;
break;
case 1:
c->port_id = 5;
c->num_chipselect = 4;
break;
case 2:
c->port_id = 6;
c->num_chipselect = 1;
break;
default:
return -ENODEV;
}
return 0;
}
static struct pxa_spi_info spi_info_configs[] = {
[PORT_CE4100] = {
.type = PXA25x_SSP,
......@@ -67,35 +122,36 @@ static struct pxa_spi_info spi_info_configs[] = {
[PORT_BYT] = {
.type = LPSS_BYT_SSP,
.port_id = 0,
.num_chipselect = 1,
.max_clk_rate = 50000000,
.setup = lpss_spi_setup,
.tx_param = &byt_tx_param,
.rx_param = &byt_rx_param,
},
[PORT_BSW0] = {
.type = LPSS_BYT_SSP,
.type = LPSS_BSW_SSP,
.port_id = 0,
.num_chipselect = 1,
.max_clk_rate = 50000000,
.setup = lpss_spi_setup,
.tx_param = &bsw0_tx_param,
.rx_param = &bsw0_rx_param,
},
[PORT_BSW1] = {
.type = LPSS_BYT_SSP,
.type = LPSS_BSW_SSP,
.port_id = 1,
.num_chipselect = 1,
.max_clk_rate = 50000000,
.setup = lpss_spi_setup,
.tx_param = &bsw1_tx_param,
.rx_param = &bsw1_rx_param,
},
[PORT_BSW2] = {
.type = LPSS_BYT_SSP,
.type = LPSS_BSW_SSP,
.port_id = 2,
.num_chipselect = 1,
.max_clk_rate = 50000000,
.setup = lpss_spi_setup,
.tx_param = &bsw2_tx_param,
.rx_param = &bsw2_rx_param,
},
[PORT_MRFLD] = {
.type = PXA27x_SSP,
.max_clk_rate = 25000000,
.setup = mrfld_spi_setup,
},
[PORT_QUARK_X1000] = {
.type = QUARK_X1000_SSP,
.port_id = -1,
......@@ -105,8 +161,7 @@ static struct pxa_spi_info spi_info_configs[] = {
[PORT_LPT] = {
.type = LPSS_LPT_SSP,
.port_id = 0,
.num_chipselect = 1,
.max_clk_rate = 50000000,
.setup = lpss_spi_setup,
.tx_param = &lpt_tx_param,
.rx_param = &lpt_rx_param,
},
......@@ -122,7 +177,6 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev,
struct ssp_device *ssp;
struct pxa_spi_info *c;
char buf[40];
struct pci_dev *dma_dev;
ret = pcim_enable_device(dev);
if (ret)
......@@ -133,30 +187,15 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev,
return ret;
c = &spi_info_configs[ent->driver_data];
memset(&spi_pdata, 0, sizeof(spi_pdata));
spi_pdata.num_chipselect = (c->num_chipselect > 0) ?
c->num_chipselect : dev->devfn;
dma_dev = pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
if (c->tx_param) {
struct dw_dma_slave *slave = c->tx_param;
slave->dma_dev = &dma_dev->dev;
slave->m_master = 0;
slave->p_master = 1;
if (c->setup) {
ret = c->setup(dev, c);
if (ret)
return ret;
}
if (c->rx_param) {
struct dw_dma_slave *slave = c->rx_param;
slave->dma_dev = &dma_dev->dev;
slave->m_master = 0;
slave->p_master = 1;
}
spi_pdata.dma_filter = lpss_dma_filter;
memset(&spi_pdata, 0, sizeof(spi_pdata));
spi_pdata.num_chipselect = (c->num_chipselect > 0) ? c->num_chipselect : dev->devfn;
spi_pdata.dma_filter = c->dma_filter;
spi_pdata.tx_param = c->tx_param;
spi_pdata.rx_param = c->rx_param;
spi_pdata.enable_dma = c->rx_param && c->tx_param;
......@@ -164,10 +203,6 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev,
ssp = &spi_pdata.ssp;
ssp->phys_base = pci_resource_start(dev, 0);
ssp->mmio_base = pcim_iomap_table(dev)[0];
if (!ssp->mmio_base) {
dev_err(&dev->dev, "failed to ioremap() registers\n");
return -EIO;
}
ssp->irq = dev->irq;
ssp->port_id = (c->port_id >= 0) ? c->port_id : dev->devfn;
ssp->type = c->type;
......@@ -208,12 +243,13 @@ static void pxa2xx_spi_pci_remove(struct pci_dev *dev)
}
static const struct pci_device_id pxa2xx_spi_pci_devices[] = {
{ PCI_VDEVICE(INTEL, 0x2e6a), PORT_CE4100 },
{ PCI_VDEVICE(INTEL, 0x0935), PORT_QUARK_X1000 },
{ PCI_VDEVICE(INTEL, 0x0f0e), PORT_BYT },
{ PCI_VDEVICE(INTEL, 0x1194), PORT_MRFLD },
{ PCI_VDEVICE(INTEL, 0x228e), PORT_BSW0 },
{ PCI_VDEVICE(INTEL, 0x2290), PORT_BSW1 },
{ PCI_VDEVICE(INTEL, 0x22ac), PORT_BSW2 },
{ PCI_VDEVICE(INTEL, 0x2e6a), PORT_CE4100 },
{ PCI_VDEVICE(INTEL, 0x9ce6), PORT_LPT },
{ },
};
......
......@@ -919,9 +919,21 @@ static unsigned int pxa2xx_ssp_get_clk_div(struct driver_data *drv_data,
return clk_div << 8;
}
static bool pxa2xx_spi_can_dma(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *xfer)
{
struct chip_data *chip = spi_get_ctldata(spi);
return chip->enable_dma &&
xfer->len <= MAX_DMA_LEN &&
xfer->len >= chip->dma_burst_size;
}
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;
......@@ -935,6 +947,7 @@ static void pump_transfers(unsigned long data)
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;
......@@ -969,7 +982,7 @@ static void pump_transfers(unsigned long data)
}
/* Check if we can DMA this transfer */
if (!pxa2xx_spi_dma_is_possible(transfer->len) && chip->enable_dma) {
if (transfer->len > MAX_DMA_LEN && chip->enable_dma) {
/* reject already-mapped transfers; PIO won't always work */
if (message->is_dma_mapped
......@@ -1046,10 +1059,10 @@ static void pump_transfers(unsigned long data)
message->state = RUNNING_STATE;
drv_data->dma_mapped = 0;
if (pxa2xx_spi_dma_is_possible(drv_data->len))
drv_data->dma_mapped = pxa2xx_spi_map_dma_buffers(drv_data);
if (drv_data->dma_mapped) {
dma_mapped = master->can_dma &&
master->can_dma(master, message->spi, transfer) &&
master->cur_msg_mapped;
if (dma_mapped) {
/* Ensure we have the correct interrupt handler */
drv_data->transfer_handler = pxa2xx_spi_dma_transfer;
......@@ -1079,14 +1092,14 @@ static void pump_transfers(unsigned long data)
cr0 = pxa2xx_configure_sscr0(drv_data, clk_div, bits);
if (!pxa25x_ssp_comp(drv_data))
dev_dbg(&message->spi->dev, "%u Hz actual, %s\n",
drv_data->master->max_speed_hz
master->max_speed_hz
/ (1 + ((cr0 & SSCR0_SCR(0xfff)) >> 8)),
drv_data->dma_mapped ? "DMA" : "PIO");
dma_mapped ? "DMA" : "PIO");
else
dev_dbg(&message->spi->dev, "%u Hz actual, %s\n",
drv_data->master->max_speed_hz / 2
master->max_speed_hz / 2
/ (1 + ((cr0 & SSCR0_SCR(0x0ff)) >> 8)),
drv_data->dma_mapped ? "DMA" : "PIO");
dma_mapped ? "DMA" : "PIO");
if (is_lpss_ssp(drv_data)) {
if ((pxa2xx_spi_read(drv_data, SSIRF) & 0xff)
......@@ -1247,7 +1260,7 @@ static int setup(struct spi_device *spi)
chip->frm = spi->chip_select;
} else
chip->gpio_cs = -1;
chip->enable_dma = 0;
chip->enable_dma = drv_data->master_info->enable_dma;
chip->timeout = TIMOUT_DFLT;
}
......@@ -1266,17 +1279,9 @@ static int setup(struct spi_device *spi)
tx_hi_thres = chip_info->tx_hi_threshold;
if (chip_info->rx_threshold)
rx_thres = chip_info->rx_threshold;
chip->enable_dma = drv_data->master_info->enable_dma;
chip->dma_threshold = 0;
if (chip_info->enable_loopback)
chip->cr1 = SSCR1_LBM;
} else if (ACPI_HANDLE(&spi->dev)) {
/*
* Slave devices enumerated from ACPI namespace don't
* usually have chip_info but we still might want to use
* DMA with them.
*/
chip->enable_dma = drv_data->master_info->enable_dma;
}
chip->lpss_rx_threshold = SSIRF_RxThresh(rx_thres);
......@@ -1396,6 +1401,9 @@ static const struct pci_device_id pxa2xx_spi_pci_compound_match[] = {
/* SPT-H */
{ PCI_VDEVICE(INTEL, 0xa129), LPSS_SPT_SSP },
{ PCI_VDEVICE(INTEL, 0xa12a), LPSS_SPT_SSP },
/* KBL-H */
{ PCI_VDEVICE(INTEL, 0xa2a9), LPSS_SPT_SSP },
{ PCI_VDEVICE(INTEL, 0xa2aa), LPSS_SPT_SSP },
/* BXT A-Step */
{ PCI_VDEVICE(INTEL, 0x0ac2), LPSS_BXT_SSP },
{ PCI_VDEVICE(INTEL, 0x0ac4), LPSS_BXT_SSP },
......@@ -1608,6 +1616,8 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
if (status) {
dev_dbg(dev, "no DMA channels available, using PIO\n");
platform_info->enable_dma = false;
} else {
master->can_dma = pxa2xx_spi_can_dma;
}
}
......
......@@ -50,12 +50,6 @@ struct driver_data {
struct tasklet_struct pump_transfers;
/* DMA engine support */
struct dma_chan *rx_chan;
struct dma_chan *tx_chan;
struct sg_table rx_sgt;
struct sg_table tx_sgt;
int rx_nents;
int tx_nents;
atomic_t dma_running;
/* Current message transfer state info */
......@@ -67,7 +61,6 @@ struct driver_data {
void *tx_end;
void *rx;
void *rx_end;
int dma_mapped;
u8 n_bytes;
int (*write)(struct driver_data *drv_data);
int (*read)(struct driver_data *drv_data);
......@@ -145,8 +138,6 @@ extern void *pxa2xx_spi_next_transfer(struct driver_data *drv_data);
#define MAX_DMA_LEN SZ_64K
#define DEFAULT_DMA_CR1 (SSCR1_TSRE | SSCR1_RSRE | SSCR1_TRAIL)
extern bool pxa2xx_spi_dma_is_possible(size_t len);
extern int pxa2xx_spi_map_dma_buffers(struct driver_data *drv_data);
extern irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data);
extern int pxa2xx_spi_dma_prepare(struct driver_data *drv_data, u32 dma_burst);
extern void pxa2xx_spi_dma_start(struct driver_data *drv_data);
......
......@@ -911,9 +911,12 @@ static const struct dev_pm_ops rockchip_spi_pm = {
};
static const struct of_device_id rockchip_spi_dt_match[] = {
{ .compatible = "rockchip,rk3036-spi", },
{ .compatible = "rockchip,rk3066-spi", },
{ .compatible = "rockchip,rk3188-spi", },
{ .compatible = "rockchip,rk3228-spi", },
{ .compatible = "rockchip,rk3288-spi", },
{ .compatible = "rockchip,rk3368-spi", },
{ .compatible = "rockchip,rk3399-spi", },
{ },
};
......
......@@ -156,12 +156,14 @@ struct s3c64xx_spi_port_config {
int quirks;
bool high_speed;
bool clk_from_cmu;
bool clk_ioclk;
};
/**
* struct s3c64xx_spi_driver_data - Runtime info holder for SPI driver.
* @clk: Pointer to the spi clock.
* @src_clk: Pointer to the clock used to generate SPI signals.
* @ioclk: Pointer to the i/o clock between master and slave
* @master: Pointer to the SPI Protocol master.
* @cntrlr_info: Platform specific data for the controller this driver manages.
* @tgl_spi: Pointer to the last CS left untoggled by the cs_change hint.
......@@ -181,6 +183,7 @@ struct s3c64xx_spi_driver_data {
void __iomem *regs;
struct clk *clk;
struct clk *src_clk;
struct clk *ioclk;
struct platform_device *pdev;
struct spi_master *master;
struct s3c64xx_spi_info *cntrlr_info;
......@@ -310,44 +313,63 @@ static void prepare_dma(struct s3c64xx_spi_dma_data *dma,
dma_async_issue_pending(dma->ch);
}
static void s3c64xx_spi_set_cs(struct spi_device *spi, bool enable)
{
struct s3c64xx_spi_driver_data *sdd =
spi_master_get_devdata(spi->master);
if (sdd->cntrlr_info->no_cs)
return;
if (enable) {
if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) {
writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
} else {
u32 ssel = readl(sdd->regs + S3C64XX_SPI_SLAVE_SEL);
ssel |= (S3C64XX_SPI_SLAVE_AUTO |
S3C64XX_SPI_SLAVE_NSC_CNT_2);
writel(ssel, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
}
} else {
if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO))
writel(S3C64XX_SPI_SLAVE_SIG_INACT,
sdd->regs + S3C64XX_SPI_SLAVE_SEL);
}
}
static int s3c64xx_spi_prepare_transfer(struct spi_master *spi)
{
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi);
dma_filter_fn filter = sdd->cntrlr_info->filter;
struct device *dev = &sdd->pdev->dev;
dma_cap_mask_t mask;
int ret;
if (!is_polling(sdd)) {
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
/* Acquire DMA channels */
sdd->rx_dma.ch = dma_request_slave_channel_compat(mask, filter,
sdd->cntrlr_info->dma_rx, dev, "rx");
if (!sdd->rx_dma.ch) {
dev_err(dev, "Failed to get RX DMA channel\n");
ret = -EBUSY;
goto out;
}
spi->dma_rx = sdd->rx_dma.ch;
sdd->tx_dma.ch = dma_request_slave_channel_compat(mask, filter,
sdd->cntrlr_info->dma_tx, dev, "tx");
if (!sdd->tx_dma.ch) {
dev_err(dev, "Failed to get TX DMA channel\n");
ret = -EBUSY;
goto out_rx;
}
spi->dma_tx = sdd->tx_dma.ch;
if (is_polling(sdd))
return 0;
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
/* Acquire DMA channels */
sdd->rx_dma.ch = dma_request_slave_channel_compat(mask, filter,
sdd->cntrlr_info->dma_rx, dev, "rx");
if (!sdd->rx_dma.ch) {
dev_err(dev, "Failed to get RX DMA channel\n");
return -EBUSY;
}
spi->dma_rx = sdd->rx_dma.ch;
return 0;
sdd->tx_dma.ch = dma_request_slave_channel_compat(mask, filter,
sdd->cntrlr_info->dma_tx, dev, "tx");
if (!sdd->tx_dma.ch) {
dev_err(dev, "Failed to get TX DMA channel\n");
dma_release_channel(sdd->rx_dma.ch);
return -EBUSY;
}
spi->dma_tx = sdd->tx_dma.ch;
out_rx:
dma_release_channel(sdd->rx_dma.ch);
out:
return ret;
return 0;
}
static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi)
......@@ -577,9 +599,7 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
u32 val;
/* Disable Clock */
if (sdd->port_conf->clk_from_cmu) {
clk_disable_unprepare(sdd->src_clk);
} else {
if (!sdd->port_conf->clk_from_cmu) {
val = readl(regs + S3C64XX_SPI_CLK_CFG);
val &= ~S3C64XX_SPI_ENCLK_ENABLE;
writel(val, regs + S3C64XX_SPI_CLK_CFG);
......@@ -622,11 +642,8 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
writel(val, regs + S3C64XX_SPI_MODE_CFG);
if (sdd->port_conf->clk_from_cmu) {
/* Configure Clock */
/* There is half-multiplier before the SPI */
/* The src_clk clock is divided internally by 2 */
clk_set_rate(sdd->src_clk, sdd->cur_speed * 2);
/* Enable Clock */
clk_prepare_enable(sdd->src_clk);
} else {
/* Configure Clock */
val = readl(regs + S3C64XX_SPI_CLK_CFG);
......@@ -651,16 +668,6 @@ static int s3c64xx_spi_prepare_message(struct spi_master *master,
struct spi_device *spi = msg->spi;
struct s3c64xx_spi_csinfo *cs = spi->controller_data;
/* If Master's(controller) state differs from that needed by Slave */
if (sdd->cur_speed != spi->max_speed_hz
|| sdd->cur_mode != spi->mode
|| sdd->cur_bpw != spi->bits_per_word) {
sdd->cur_bpw = spi->bits_per_word;
sdd->cur_speed = spi->max_speed_hz;
sdd->cur_mode = spi->mode;
s3c64xx_spi_config(sdd);
}
/* Configure feedback delay */
writel(cs->fb_delay & 0x3, sdd->regs + S3C64XX_SPI_FB_CLK);
......@@ -687,6 +694,7 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master,
if (bpw != sdd->cur_bpw || speed != sdd->cur_speed) {
sdd->cur_bpw = bpw;
sdd->cur_speed = speed;
sdd->cur_mode = spi->mode;
s3c64xx_spi_config(sdd);
}
......@@ -706,12 +714,7 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master,
enable_datapath(sdd, spi, xfer, use_dma);
/* Start the signals */
if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO))
writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
else
writel(readl(sdd->regs + S3C64XX_SPI_SLAVE_SEL)
| S3C64XX_SPI_SLAVE_AUTO | S3C64XX_SPI_SLAVE_NSC_CNT_2,
sdd->regs + S3C64XX_SPI_SLAVE_SEL);
s3c64xx_spi_set_cs(spi, true);
spin_unlock_irqrestore(&sdd->lock, flags);
......@@ -861,16 +864,15 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
pm_runtime_mark_last_busy(&sdd->pdev->dev);
pm_runtime_put_autosuspend(&sdd->pdev->dev);
if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO))
writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
s3c64xx_spi_set_cs(spi, false);
return 0;
setup_exit:
pm_runtime_mark_last_busy(&sdd->pdev->dev);
pm_runtime_put_autosuspend(&sdd->pdev->dev);
/* setup() returns with device de-selected */
if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO))
writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
s3c64xx_spi_set_cs(spi, false);
if (gpio_is_valid(spi->cs_gpio))
gpio_free(spi->cs_gpio);
......@@ -944,7 +946,9 @@ static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel)
sdd->cur_speed = 0;
if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO))
if (sci->no_cs)
writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
else if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO))
writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
/* Disable Interrupts - we use Polling if not DMA mode */
......@@ -999,6 +1003,8 @@ static struct s3c64xx_spi_info *s3c64xx_spi_parse_dt(struct device *dev)
sci->num_cs = temp;
}
sci->no_cs = of_property_read_bool(dev->of_node, "broken-cs");
return sci;
}
#else
......@@ -1076,7 +1082,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
if (ret < 0) {
dev_err(&pdev->dev, "failed to get alias id, errno %d\n",
ret);
goto err0;
goto err_deref_master;
}
sdd->port_id = ret;
} else {
......@@ -1114,13 +1120,13 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
sdd->regs = devm_ioremap_resource(&pdev->dev, mem_res);
if (IS_ERR(sdd->regs)) {
ret = PTR_ERR(sdd->regs);
goto err0;
goto err_deref_master;
}
if (sci->cfg_gpio && sci->cfg_gpio()) {
dev_err(&pdev->dev, "Unable to config gpio\n");
ret = -EBUSY;
goto err0;
goto err_deref_master;
}
/* Setup clocks */
......@@ -1128,13 +1134,13 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
if (IS_ERR(sdd->clk)) {
dev_err(&pdev->dev, "Unable to acquire clock 'spi'\n");
ret = PTR_ERR(sdd->clk);
goto err0;
goto err_deref_master;
}
if (clk_prepare_enable(sdd->clk)) {
ret = clk_prepare_enable(sdd->clk);
if (ret) {
dev_err(&pdev->dev, "Couldn't enable clock 'spi'\n");
ret = -EBUSY;
goto err0;
goto err_deref_master;
}
sprintf(clk_name, "spi_busclk%d", sci->src_clk_nr);
......@@ -1143,13 +1149,28 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
dev_err(&pdev->dev,
"Unable to acquire clock '%s'\n", clk_name);
ret = PTR_ERR(sdd->src_clk);
goto err2;
goto err_disable_clk;
}
if (clk_prepare_enable(sdd->src_clk)) {
ret = clk_prepare_enable(sdd->src_clk);
if (ret) {
dev_err(&pdev->dev, "Couldn't enable clock '%s'\n", clk_name);
ret = -EBUSY;
goto err2;
goto err_disable_clk;
}
if (sdd->port_conf->clk_ioclk) {
sdd->ioclk = devm_clk_get(&pdev->dev, "spi_ioclk");
if (IS_ERR(sdd->ioclk)) {
dev_err(&pdev->dev, "Unable to acquire 'ioclk'\n");
ret = PTR_ERR(sdd->ioclk);
goto err_disable_src_clk;
}
ret = clk_prepare_enable(sdd->ioclk);
if (ret) {
dev_err(&pdev->dev, "Couldn't enable clock 'ioclk'\n");
goto err_disable_src_clk;
}
}
pm_runtime_set_autosuspend_delay(&pdev->dev, AUTOSUSPEND_TIMEOUT);
......@@ -1169,7 +1190,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request IRQ %d: %d\n",
irq, ret);
goto err3;
goto err_pm_put;
}
writel(S3C64XX_SPI_INT_RX_OVERRUN_EN | S3C64XX_SPI_INT_RX_UNDERRUN_EN |
......@@ -1179,7 +1200,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
ret = devm_spi_register_master(&pdev->dev, master);
if (ret != 0) {
dev_err(&pdev->dev, "cannot register SPI master: %d\n", ret);
goto err3;
goto err_pm_put;
}
dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d with %d Slaves attached\n",
......@@ -1193,15 +1214,17 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
return 0;
err3:
err_pm_put:
pm_runtime_put_noidle(&pdev->dev);
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
clk_disable_unprepare(sdd->ioclk);
err_disable_src_clk:
clk_disable_unprepare(sdd->src_clk);
err2:
err_disable_clk:
clk_disable_unprepare(sdd->clk);
err0:
err_deref_master:
spi_master_put(master);
return ret;
......@@ -1209,13 +1232,15 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
static int s3c64xx_spi_remove(struct platform_device *pdev)
{
struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
struct spi_master *master = platform_get_drvdata(pdev);
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
pm_runtime_get_sync(&pdev->dev);
writel(0, sdd->regs + S3C64XX_SPI_INT_EN);
clk_disable_unprepare(sdd->ioclk);
clk_disable_unprepare(sdd->src_clk);
clk_disable_unprepare(sdd->clk);
......@@ -1274,6 +1299,7 @@ static int s3c64xx_spi_runtime_suspend(struct device *dev)
clk_disable_unprepare(sdd->clk);
clk_disable_unprepare(sdd->src_clk);
clk_disable_unprepare(sdd->ioclk);
return 0;
}
......@@ -1284,17 +1310,28 @@ static int s3c64xx_spi_runtime_resume(struct device *dev)
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
int ret;
if (sdd->port_conf->clk_ioclk) {
ret = clk_prepare_enable(sdd->ioclk);
if (ret != 0)
return ret;
}
ret = clk_prepare_enable(sdd->src_clk);
if (ret != 0)
return ret;
goto err_disable_ioclk;
ret = clk_prepare_enable(sdd->clk);
if (ret != 0) {
clk_disable_unprepare(sdd->src_clk);
return ret;
}
if (ret != 0)
goto err_disable_src_clk;
return 0;
err_disable_src_clk:
clk_disable_unprepare(sdd->src_clk);
err_disable_ioclk:
clk_disable_unprepare(sdd->ioclk);
return ret;
}
#endif /* CONFIG_PM */
......@@ -1350,6 +1387,16 @@ static struct s3c64xx_spi_port_config exynos7_spi_port_config = {
.quirks = S3C64XX_SPI_QUIRK_CS_AUTO,
};
static struct s3c64xx_spi_port_config exynos5433_spi_port_config = {
.fifo_lvl_mask = { 0x1ff, 0x7f, 0x7f, 0x7f, 0x7f, 0x1ff},
.rx_lvl_offset = 15,
.tx_st_done = 25,
.high_speed = true,
.clk_from_cmu = true,
.clk_ioclk = true,
.quirks = S3C64XX_SPI_QUIRK_CS_AUTO,
};
static const struct platform_device_id s3c64xx_spi_driver_ids[] = {
{
.name = "s3c2443-spi",
......@@ -1380,6 +1427,9 @@ static const struct of_device_id s3c64xx_spi_dt_match[] = {
{ .compatible = "samsung,exynos7-spi",
.data = (void *)&exynos7_spi_port_config,
},
{ .compatible = "samsung,exynos5433-spi",
.data = (void *)&exynos5433_spi_port_config,
},
{ },
};
MODULE_DEVICE_TABLE(of, s3c64xx_spi_dt_match);
......
......@@ -45,7 +45,6 @@ struct sh_msiof_spi_priv {
void __iomem *mapbase;
struct clk *clk;
struct platform_device *pdev;
const struct sh_msiof_chipdata *chipdata;
struct sh_msiof_spi_info *info;
struct completion done;
unsigned int tx_fifo_size;
......@@ -271,7 +270,7 @@ static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p,
scr = sh_msiof_spi_div_table[k].brdv | SCR_BRPS(brps);
sh_msiof_write(p, TSCR, scr);
if (!(p->chipdata->master_flags & SPI_MASTER_MUST_TX))
if (!(p->master->flags & SPI_MASTER_MUST_TX))
sh_msiof_write(p, RSCR, scr);
}
......@@ -336,7 +335,7 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p,
tmp |= lsb_first << MDR1_BITLSB_SHIFT;
tmp |= sh_msiof_spi_get_dtdl_and_syncdl(p);
sh_msiof_write(p, TMDR1, tmp | MDR1_TRMD | TMDR1_PCON);
if (p->chipdata->master_flags & SPI_MASTER_MUST_TX) {
if (p->master->flags & SPI_MASTER_MUST_TX) {
/* These bits are reserved if RX needs TX */
tmp &= ~0x0000ffff;
}
......@@ -360,7 +359,7 @@ static void sh_msiof_spi_set_mode_regs(struct sh_msiof_spi_priv *p,
{
u32 dr2 = MDR2_BITLEN1(bits) | MDR2_WDLEN1(words);
if (tx_buf || (p->chipdata->master_flags & SPI_MASTER_MUST_TX))
if (tx_buf || (p->master->flags & SPI_MASTER_MUST_TX))
sh_msiof_write(p, TMDR2, dr2);
else
sh_msiof_write(p, TMDR2, dr2 | MDR2_GRPMASK1);
......@@ -1152,6 +1151,7 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
{
struct resource *r;
struct spi_master *master;
const struct sh_msiof_chipdata *chipdata;
const struct of_device_id *of_id;
struct sh_msiof_spi_priv *p;
int i;
......@@ -1170,10 +1170,10 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
of_id = of_match_device(sh_msiof_match, &pdev->dev);
if (of_id) {
p->chipdata = of_id->data;
chipdata = of_id->data;
p->info = sh_msiof_spi_parse_dt(&pdev->dev);
} else {
p->chipdata = (const void *)pdev->id_entry->driver_data;
chipdata = (const void *)pdev->id_entry->driver_data;
p->info = dev_get_platdata(&pdev->dev);
}
......@@ -1217,8 +1217,8 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
/* Platform data may override FIFO sizes */
p->tx_fifo_size = p->chipdata->tx_fifo_size;
p->rx_fifo_size = p->chipdata->rx_fifo_size;
p->tx_fifo_size = chipdata->tx_fifo_size;
p->rx_fifo_size = chipdata->rx_fifo_size;
if (p->info->tx_fifo_override)
p->tx_fifo_size = p->info->tx_fifo_override;
if (p->info->rx_fifo_override)
......@@ -1227,7 +1227,7 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
/* init master code */
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
master->mode_bits |= SPI_LSB_FIRST | SPI_3WIRE;
master->flags = p->chipdata->master_flags;
master->flags = chipdata->master_flags;
master->bus_num = pdev->id;
master->dev.of_node = pdev->dev.of_node;
master->num_chipselect = p->info->num_chipselect;
......
......@@ -82,7 +82,6 @@ struct spi_sh_data {
int irq;
struct spi_master *master;
struct list_head queue;
struct workqueue_struct *workqueue;
struct work_struct ws;
unsigned long cr1;
wait_queue_head_t wait;
......@@ -380,7 +379,7 @@ static int spi_sh_transfer(struct spi_device *spi, struct spi_message *mesg)
spi_sh_clear_bit(ss, SPI_SH_SSA, SPI_SH_CR1);
list_add_tail(&mesg->queue, &ss->queue);
queue_work(ss->workqueue, &ss->ws);
schedule_work(&ss->ws);
spin_unlock_irqrestore(&ss->lock, flags);
......@@ -425,7 +424,7 @@ static int spi_sh_remove(struct platform_device *pdev)
struct spi_sh_data *ss = platform_get_drvdata(pdev);
spi_unregister_master(ss->master);
destroy_workqueue(ss->workqueue);
flush_work(&ss->ws);
free_irq(ss->irq, ss);
return 0;
......@@ -484,18 +483,11 @@ static int spi_sh_probe(struct platform_device *pdev)
spin_lock_init(&ss->lock);
INIT_WORK(&ss->ws, spi_sh_work);
init_waitqueue_head(&ss->wait);
ss->workqueue = create_singlethread_workqueue(
dev_name(master->dev.parent));
if (ss->workqueue == NULL) {
dev_err(&pdev->dev, "create workqueue error\n");
ret = -EBUSY;
goto error1;
}
ret = request_irq(irq, spi_sh_irq, 0, "spi_sh", ss);
if (ret < 0) {
dev_err(&pdev->dev, "request_irq error\n");
goto error2;
goto error1;
}
master->num_chipselect = 2;
......@@ -514,8 +506,6 @@ static int spi_sh_probe(struct platform_device *pdev)
error3:
free_irq(irq, ss);
error2:
destroy_workqueue(ss->workqueue);
error1:
spi_master_put(master);
......
......@@ -38,6 +38,7 @@ struct s3c64xx_spi_csinfo {
struct s3c64xx_spi_info {
int src_clk_nr;
int num_cs;
bool no_cs;
int (*cfg_gpio)(void);
dma_filter_fn filter;
void *dma_tx;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment