Commit c22c62db authored by Andy Shevchenko's avatar Andy Shevchenko Committed by Mark Brown

spi: dw: move to SPI core message handling

This patch removes a lot of duplicate code since SPI core provides a nice
message handling.
Signed-off-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 3e00803a
...@@ -110,7 +110,7 @@ static void dw_spi_dma_tx_done(void *arg) ...@@ -110,7 +110,7 @@ static void dw_spi_dma_tx_done(void *arg)
if (test_and_clear_bit(TX_BUSY, &dws->dma_chan_busy) & BIT(RX_BUSY)) if (test_and_clear_bit(TX_BUSY, &dws->dma_chan_busy) & BIT(RX_BUSY))
return; return;
dw_spi_xfer_done(dws); spi_finalize_current_transfer(dws->master);
} }
static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws) static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws)
...@@ -155,7 +155,7 @@ static void dw_spi_dma_rx_done(void *arg) ...@@ -155,7 +155,7 @@ static void dw_spi_dma_rx_done(void *arg)
if (test_and_clear_bit(RX_BUSY, &dws->dma_chan_busy) & BIT(TX_BUSY)) if (test_and_clear_bit(RX_BUSY, &dws->dma_chan_busy) & BIT(TX_BUSY))
return; return;
dw_spi_xfer_done(dws); spi_finalize_current_transfer(dws->master);
} }
static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws) static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws)
......
...@@ -28,11 +28,6 @@ ...@@ -28,11 +28,6 @@
#include <linux/debugfs.h> #include <linux/debugfs.h>
#endif #endif
#define START_STATE ((void *)0)
#define RUNNING_STATE ((void *)1)
#define DONE_STATE ((void *)2)
#define ERROR_STATE ((void *)-1)
/* Slave spi_dev related */ /* Slave spi_dev related */
struct chip_data { struct chip_data {
u16 cr0; u16 cr0;
...@@ -143,6 +138,19 @@ static inline void dw_spi_debugfs_remove(struct dw_spi *dws) ...@@ -143,6 +138,19 @@ static inline void dw_spi_debugfs_remove(struct dw_spi *dws)
} }
#endif /* CONFIG_DEBUG_FS */ #endif /* CONFIG_DEBUG_FS */
static void dw_spi_set_cs(struct spi_device *spi, bool enable)
{
struct dw_spi *dws = spi_master_get_devdata(spi->master);
struct chip_data *chip = spi_get_ctldata(spi);
/* Chip select logic is inverted from spi_set_cs() */
if (chip->cs_control)
chip->cs_control(!enable);
if (!enable)
dw_writel(dws, DW_SPI_SER, BIT(spi->chip_select));
}
/* Return the max entries we can fill into tx fifo */ /* Return the max entries we can fill into tx fifo */
static inline u32 tx_max(struct dw_spi *dws) static inline u32 tx_max(struct dw_spi *dws)
{ {
...@@ -209,93 +217,41 @@ static void dw_reader(struct dw_spi *dws) ...@@ -209,93 +217,41 @@ static void dw_reader(struct dw_spi *dws)
} }
} }
static void *next_transfer(struct dw_spi *dws)
{
struct spi_message *msg = dws->cur_msg;
struct spi_transfer *trans = dws->cur_transfer;
/* Move to next transfer */
if (trans->transfer_list.next != &msg->transfers) {
dws->cur_transfer =
list_entry(trans->transfer_list.next,
struct spi_transfer,
transfer_list);
return RUNNING_STATE;
}
return DONE_STATE;
}
/* /*
* Note: first step is the protocol driver prepares * Note: first step is the protocol driver prepares
* a dma-capable memory, and this func just need translate * a dma-capable memory, and this func just need translate
* the virt addr to physical * the virt addr to physical
*/ */
static int map_dma_buffers(struct dw_spi *dws) static int map_dma_buffers(struct spi_master *master,
struct spi_device *spi, struct spi_transfer *transfer)
{ {
if (!dws->cur_msg->is_dma_mapped struct dw_spi *dws = spi_master_get_devdata(master);
struct chip_data *chip = spi_get_ctldata(spi);
if (!master->cur_msg->is_dma_mapped
|| !dws->dma_inited || !dws->dma_inited
|| !dws->cur_chip->enable_dma || !chip->enable_dma
|| !dws->dma_ops) || !dws->dma_ops)
return 0; return 0;
if (dws->cur_transfer->tx_dma) if (transfer->tx_dma)
dws->tx_dma = dws->cur_transfer->tx_dma; dws->tx_dma = transfer->tx_dma;
if (dws->cur_transfer->rx_dma) if (transfer->rx_dma)
dws->rx_dma = dws->cur_transfer->rx_dma; dws->rx_dma = transfer->rx_dma;
return 1; return 1;
} }
/* Caller already set message->status; dma and pio irqs are blocked */
static void giveback(struct dw_spi *dws)
{
struct spi_transfer *last_transfer;
struct spi_message *msg;
msg = dws->cur_msg;
dws->cur_msg = NULL;
dws->cur_transfer = NULL;
dws->prev_chip = dws->cur_chip;
dws->cur_chip = NULL;
dws->dma_mapped = 0;
last_transfer = list_last_entry(&msg->transfers, struct spi_transfer,
transfer_list);
if (!last_transfer->cs_change)
spi_chip_sel(dws, msg->spi, 0);
spi_finalize_current_message(dws->master);
}
static void int_error_stop(struct dw_spi *dws, const char *msg) static void int_error_stop(struct dw_spi *dws, const char *msg)
{ {
spi_reset_chip(dws); spi_reset_chip(dws);
dev_err(&dws->master->dev, "%s\n", msg); dev_err(&dws->master->dev, "%s\n", msg);
dws->cur_msg->state = ERROR_STATE; dws->master->cur_msg->status = -EIO;
tasklet_schedule(&dws->pump_transfers); spi_finalize_current_transfer(dws->master);
} }
void dw_spi_xfer_done(struct dw_spi *dws)
{
/* Update total byte transferred return count actual bytes read */
dws->cur_msg->actual_length += dws->len;
/* Move to next transfer */
dws->cur_msg->state = next_transfer(dws);
/* Handle end of message */
if (dws->cur_msg->state == DONE_STATE) {
dws->cur_msg->status = 0;
giveback(dws);
} else
tasklet_schedule(&dws->pump_transfers);
}
EXPORT_SYMBOL_GPL(dw_spi_xfer_done);
static irqreturn_t interrupt_transfer(struct dw_spi *dws) static irqreturn_t interrupt_transfer(struct dw_spi *dws)
{ {
u16 irq_status = dw_readw(dws, DW_SPI_ISR); u16 irq_status = dw_readw(dws, DW_SPI_ISR);
...@@ -312,7 +268,7 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws) ...@@ -312,7 +268,7 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws)
dw_reader(dws); dw_reader(dws);
if (dws->rx_end == dws->rx) { if (dws->rx_end == dws->rx) {
spi_mask_intr(dws, SPI_INT_TXEI); spi_mask_intr(dws, SPI_INT_TXEI);
dw_spi_xfer_done(dws); spi_finalize_current_transfer(dws->master);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
if (irq_status & SPI_INT_TXEI) { if (irq_status & SPI_INT_TXEI) {
...@@ -327,13 +283,14 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws) ...@@ -327,13 +283,14 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws)
static irqreturn_t dw_spi_irq(int irq, void *dev_id) static irqreturn_t dw_spi_irq(int irq, void *dev_id)
{ {
struct dw_spi *dws = dev_id; struct spi_master *master = dev_id;
struct dw_spi *dws = spi_master_get_devdata(master);
u16 irq_status = dw_readw(dws, DW_SPI_ISR) & 0x3f; u16 irq_status = dw_readw(dws, DW_SPI_ISR) & 0x3f;
if (!irq_status) if (!irq_status)
return IRQ_NONE; return IRQ_NONE;
if (!dws->cur_msg) { if (!master->cur_msg) {
spi_mask_intr(dws, SPI_INT_TXEI); spi_mask_intr(dws, SPI_INT_TXEI);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -342,7 +299,7 @@ static irqreturn_t dw_spi_irq(int irq, void *dev_id) ...@@ -342,7 +299,7 @@ static irqreturn_t dw_spi_irq(int irq, void *dev_id)
} }
/* Must be called inside pump_transfers() */ /* Must be called inside pump_transfers() */
static void poll_transfer(struct dw_spi *dws) static int poll_transfer(struct dw_spi *dws)
{ {
do { do {
dw_writer(dws); dw_writer(dws);
...@@ -350,17 +307,14 @@ static void poll_transfer(struct dw_spi *dws) ...@@ -350,17 +307,14 @@ static void poll_transfer(struct dw_spi *dws)
cpu_relax(); cpu_relax();
} while (dws->rx_end > dws->rx); } while (dws->rx_end > dws->rx);
dw_spi_xfer_done(dws); return 0;
} }
static void pump_transfers(unsigned long data) static int dw_spi_transfer_one(struct spi_master *master,
struct spi_device *spi, struct spi_transfer *transfer)
{ {
struct dw_spi *dws = (struct dw_spi *)data; struct dw_spi *dws = spi_master_get_devdata(master);
struct spi_message *message = NULL; struct chip_data *chip = spi_get_ctldata(spi);
struct spi_transfer *transfer = NULL;
struct spi_transfer *previous = NULL;
struct spi_device *spi = NULL;
struct chip_data *chip = NULL;
u8 bits = 0; u8 bits = 0;
u8 imask = 0; u8 imask = 0;
u8 cs_change = 0; u8 cs_change = 0;
...@@ -369,35 +323,8 @@ static void pump_transfers(unsigned long data) ...@@ -369,35 +323,8 @@ static void pump_transfers(unsigned long data)
u32 speed = 0; u32 speed = 0;
u32 cr0 = 0; u32 cr0 = 0;
/* Get current state information */
message = dws->cur_msg;
transfer = dws->cur_transfer;
chip = dws->cur_chip;
spi = message->spi;
if (message->state == ERROR_STATE) {
message->status = -EIO;
goto early_exit;
}
/* Handle end of message */
if (message->state == DONE_STATE) {
message->status = 0;
goto early_exit;
}
/* Delay if requested at end of transfer */
if (message->state == RUNNING_STATE) {
previous = list_entry(transfer->transfer_list.prev,
struct spi_transfer,
transfer_list);
if (previous->delay_usecs)
udelay(previous->delay_usecs);
}
dws->n_bytes = chip->n_bytes; dws->n_bytes = chip->n_bytes;
dws->dma_width = chip->dma_width; dws->dma_width = chip->dma_width;
dws->cs_control = chip->cs_control;
dws->rx_dma = transfer->rx_dma; dws->rx_dma = transfer->rx_dma;
dws->tx_dma = transfer->tx_dma; dws->tx_dma = transfer->tx_dma;
...@@ -405,7 +332,7 @@ static void pump_transfers(unsigned long data) ...@@ -405,7 +332,7 @@ static void pump_transfers(unsigned long data)
dws->tx_end = dws->tx + transfer->len; dws->tx_end = dws->tx + transfer->len;
dws->rx = transfer->rx_buf; dws->rx = transfer->rx_buf;
dws->rx_end = dws->rx + transfer->len; dws->rx_end = dws->rx + transfer->len;
dws->len = dws->cur_transfer->len; dws->len = transfer->len;
if (chip != dws->prev_chip) if (chip != dws->prev_chip)
cs_change = 1; cs_change = 1;
...@@ -437,13 +364,12 @@ static void pump_transfers(unsigned long data) ...@@ -437,13 +364,12 @@ static void pump_transfers(unsigned long data)
| (spi->mode << SPI_MODE_OFFSET) | (spi->mode << SPI_MODE_OFFSET)
| (chip->tmode << SPI_TMOD_OFFSET); | (chip->tmode << SPI_TMOD_OFFSET);
} }
message->state = RUNNING_STATE;
/* /*
* Adjust transfer mode if necessary. Requires platform dependent * Adjust transfer mode if necessary. Requires platform dependent
* chipselect mechanism. * chipselect mechanism.
*/ */
if (dws->cs_control) { if (chip->cs_control) {
if (dws->rx && dws->tx) if (dws->rx && dws->tx)
chip->tmode = SPI_TMOD_TR; chip->tmode = SPI_TMOD_TR;
else if (dws->rx) else if (dws->rx)
...@@ -456,10 +382,9 @@ static void pump_transfers(unsigned long data) ...@@ -456,10 +382,9 @@ static void pump_transfers(unsigned long data)
} }
dw_writew(dws, DW_SPI_CTRL0, cr0); dw_writew(dws, DW_SPI_CTRL0, cr0);
spi_chip_sel(dws, spi, 1);
/* Check if current transfer is a DMA transaction */ /* Check if current transfer is a DMA transaction */
dws->dma_mapped = map_dma_buffers(dws); dws->dma_mapped = map_dma_buffers(master, spi, transfer);
/* For poll mode just disable all interrupts */ /* For poll mode just disable all interrupts */
spi_mask_intr(dws, 0xff); spi_mask_intr(dws, 0xff);
...@@ -489,31 +414,17 @@ static void pump_transfers(unsigned long data) ...@@ -489,31 +414,17 @@ static void pump_transfers(unsigned long data)
dws->dma_ops->dma_transfer(dws, cs_change); dws->dma_ops->dma_transfer(dws, cs_change);
if (chip->poll_mode) if (chip->poll_mode)
poll_transfer(dws); return poll_transfer(dws);
return; return 1;
early_exit:
giveback(dws);
} }
static int dw_spi_transfer_one_message(struct spi_master *master, static void dw_spi_handle_err(struct spi_master *master,
struct spi_message *msg) struct spi_message *msg)
{ {
struct dw_spi *dws = spi_master_get_devdata(master); struct dw_spi *dws = spi_master_get_devdata(master);
dws->cur_msg = msg; spi_reset_chip(dws);
/* Initial message state */
dws->cur_msg->state = START_STATE;
dws->cur_transfer = list_entry(dws->cur_msg->transfers.next,
struct spi_transfer,
transfer_list);
dws->cur_chip = spi_get_ctldata(dws->cur_msg->spi);
/* Launch transfers */
tasklet_schedule(&dws->pump_transfers);
return 0;
} }
/* This may be called twice for each spi dev */ /* This may be called twice for each spi dev */
...@@ -637,7 +548,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) ...@@ -637,7 +548,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
snprintf(dws->name, sizeof(dws->name), "dw_spi%d", dws->bus_num); snprintf(dws->name, sizeof(dws->name), "dw_spi%d", dws->bus_num);
ret = devm_request_irq(dev, dws->irq, dw_spi_irq, IRQF_SHARED, ret = devm_request_irq(dev, dws->irq, dw_spi_irq, IRQF_SHARED,
dws->name, dws); dws->name, master);
if (ret < 0) { if (ret < 0) {
dev_err(&master->dev, "can not get IRQ\n"); dev_err(&master->dev, "can not get IRQ\n");
goto err_free_master; goto err_free_master;
...@@ -649,7 +560,9 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) ...@@ -649,7 +560,9 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
master->num_chipselect = dws->num_cs; master->num_chipselect = dws->num_cs;
master->setup = dw_spi_setup; master->setup = dw_spi_setup;
master->cleanup = dw_spi_cleanup; master->cleanup = dw_spi_cleanup;
master->transfer_one_message = dw_spi_transfer_one_message; master->set_cs = dw_spi_set_cs;
master->transfer_one = dw_spi_transfer_one;
master->handle_err = dw_spi_handle_err;
master->max_speed_hz = dws->max_freq; master->max_speed_hz = dws->max_freq;
master->dev.of_node = dev->of_node; master->dev.of_node = dev->of_node;
...@@ -664,8 +577,6 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) ...@@ -664,8 +577,6 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
} }
} }
tasklet_init(&dws->pump_transfers, pump_transfers, (unsigned long)dws);
spi_master_set_devdata(master, dws); spi_master_set_devdata(master, dws);
ret = devm_spi_register_master(dev, master); ret = devm_spi_register_master(dev, master);
if (ret) { if (ret) {
......
...@@ -96,7 +96,6 @@ struct dw_spi_dma_ops { ...@@ -96,7 +96,6 @@ struct dw_spi_dma_ops {
struct dw_spi { struct dw_spi {
struct spi_master *master; struct spi_master *master;
struct spi_device *cur_dev;
enum dw_ssi_type type; enum dw_ssi_type type;
char name[16]; char name[16];
...@@ -109,13 +108,7 @@ struct dw_spi { ...@@ -109,13 +108,7 @@ struct dw_spi {
u16 bus_num; u16 bus_num;
u16 num_cs; /* supported slave numbers */ u16 num_cs; /* supported slave numbers */
/* Message Transfer pump */
struct tasklet_struct pump_transfers;
/* Current message transfer state info */ /* Current message transfer state info */
struct spi_message *cur_msg;
struct spi_transfer *cur_transfer;
struct chip_data *cur_chip;
struct chip_data *prev_chip; struct chip_data *prev_chip;
size_t len; size_t len;
void *tx; void *tx;
...@@ -128,10 +121,8 @@ struct dw_spi { ...@@ -128,10 +121,8 @@ struct dw_spi {
size_t rx_map_len; size_t rx_map_len;
size_t tx_map_len; size_t tx_map_len;
u8 n_bytes; /* current is a 1/2 bytes op */ u8 n_bytes; /* current is a 1/2 bytes op */
u8 max_bits_per_word; /* maxim is 16b */
u32 dma_width; u32 dma_width;
irqreturn_t (*transfer_handler)(struct dw_spi *dws); irqreturn_t (*transfer_handler)(struct dw_spi *dws);
void (*cs_control)(u32 command);
/* Dma info */ /* Dma info */
int dma_inited; int dma_inited;
...@@ -182,22 +173,6 @@ static inline void spi_set_clk(struct dw_spi *dws, u16 div) ...@@ -182,22 +173,6 @@ static inline void spi_set_clk(struct dw_spi *dws, u16 div)
dw_writel(dws, DW_SPI_BAUDR, div); dw_writel(dws, DW_SPI_BAUDR, div);
} }
static inline void spi_chip_sel(struct dw_spi *dws, struct spi_device *spi,
int active)
{
u16 cs = spi->chip_select;
int gpio_val = active ? (spi->mode & SPI_CS_HIGH) :
!(spi->mode & SPI_CS_HIGH);
if (dws->cs_control)
dws->cs_control(active);
if (gpio_is_valid(spi->cs_gpio))
gpio_set_value(spi->cs_gpio, gpio_val);
if (active)
dw_writel(dws, DW_SPI_SER, 1 << cs);
}
/* Disable IRQ bits */ /* Disable IRQ bits */
static inline void spi_mask_intr(struct dw_spi *dws, u32 mask) static inline void spi_mask_intr(struct dw_spi *dws, u32 mask)
{ {
...@@ -245,7 +220,6 @@ extern int dw_spi_add_host(struct device *dev, struct dw_spi *dws); ...@@ -245,7 +220,6 @@ extern int dw_spi_add_host(struct device *dev, struct dw_spi *dws);
extern void dw_spi_remove_host(struct dw_spi *dws); extern void dw_spi_remove_host(struct dw_spi *dws);
extern int dw_spi_suspend_host(struct dw_spi *dws); extern int dw_spi_suspend_host(struct dw_spi *dws);
extern int dw_spi_resume_host(struct dw_spi *dws); extern int dw_spi_resume_host(struct dw_spi *dws);
extern void dw_spi_xfer_done(struct dw_spi *dws);
/* platform related setup */ /* platform related setup */
extern int dw_spi_mid_init(struct dw_spi *dws); /* Intel MID platforms */ extern int dw_spi_mid_init(struct dw_spi *dws); /* Intel MID platforms */
......
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