|
@@ -28,11 +28,6 @@
|
|
|
#include <linux/debugfs.h>
|
|
|
#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 */
|
|
|
struct chip_data {
|
|
|
u16 cr0;
|
|
@@ -143,6 +138,19 @@ static inline void dw_spi_debugfs_remove(struct dw_spi *dws)
|
|
|
}
|
|
|
#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 */
|
|
|
static inline u32 tx_max(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
|
|
|
* a dma-capable memory, and this func just need translate
|
|
|
* 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->cur_chip->enable_dma
|
|
|
+ || !chip->enable_dma
|
|
|
|| !dws->dma_ops)
|
|
|
return 0;
|
|
|
|
|
|
- if (dws->cur_transfer->tx_dma)
|
|
|
- dws->tx_dma = dws->cur_transfer->tx_dma;
|
|
|
+ if (transfer->tx_dma)
|
|
|
+ dws->tx_dma = transfer->tx_dma;
|
|
|
|
|
|
- if (dws->cur_transfer->rx_dma)
|
|
|
- dws->rx_dma = dws->cur_transfer->rx_dma;
|
|
|
+ if (transfer->rx_dma)
|
|
|
+ dws->rx_dma = transfer->rx_dma;
|
|
|
|
|
|
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)
|
|
|
{
|
|
|
spi_reset_chip(dws);
|
|
|
|
|
|
dev_err(&dws->master->dev, "%s\n", msg);
|
|
|
- dws->cur_msg->state = ERROR_STATE;
|
|
|
- tasklet_schedule(&dws->pump_transfers);
|
|
|
+ dws->master->cur_msg->status = -EIO;
|
|
|
+ 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)
|
|
|
{
|
|
|
u16 irq_status = dw_readw(dws, DW_SPI_ISR);
|
|
@@ -312,7 +268,7 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws)
|
|
|
dw_reader(dws);
|
|
|
if (dws->rx_end == dws->rx) {
|
|
|
spi_mask_intr(dws, SPI_INT_TXEI);
|
|
|
- dw_spi_xfer_done(dws);
|
|
|
+ spi_finalize_current_transfer(dws->master);
|
|
|
return IRQ_HANDLED;
|
|
|
}
|
|
|
if (irq_status & SPI_INT_TXEI) {
|
|
@@ -327,13 +283,14 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws)
|
|
|
|
|
|
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;
|
|
|
|
|
|
if (!irq_status)
|
|
|
return IRQ_NONE;
|
|
|
|
|
|
- if (!dws->cur_msg) {
|
|
|
+ if (!master->cur_msg) {
|
|
|
spi_mask_intr(dws, SPI_INT_TXEI);
|
|
|
return IRQ_HANDLED;
|
|
|
}
|
|
@@ -342,7 +299,7 @@ static irqreturn_t dw_spi_irq(int irq, void *dev_id)
|
|
|
}
|
|
|
|
|
|
/* Must be called inside pump_transfers() */
|
|
|
-static void poll_transfer(struct dw_spi *dws)
|
|
|
+static int poll_transfer(struct dw_spi *dws)
|
|
|
{
|
|
|
do {
|
|
|
dw_writer(dws);
|
|
@@ -350,17 +307,14 @@ static void poll_transfer(struct dw_spi *dws)
|
|
|
cpu_relax();
|
|
|
} 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 spi_message *message = NULL;
|
|
|
- struct spi_transfer *transfer = NULL;
|
|
|
- struct spi_transfer *previous = NULL;
|
|
|
- struct spi_device *spi = NULL;
|
|
|
- struct chip_data *chip = NULL;
|
|
|
+ struct dw_spi *dws = spi_master_get_devdata(master);
|
|
|
+ struct chip_data *chip = spi_get_ctldata(spi);
|
|
|
u8 bits = 0;
|
|
|
u8 imask = 0;
|
|
|
u8 cs_change = 0;
|
|
@@ -369,35 +323,8 @@ static void pump_transfers(unsigned long data)
|
|
|
u32 speed = 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->dma_width = chip->dma_width;
|
|
|
- dws->cs_control = chip->cs_control;
|
|
|
|
|
|
dws->rx_dma = transfer->rx_dma;
|
|
|
dws->tx_dma = transfer->tx_dma;
|
|
@@ -405,7 +332,7 @@ static void pump_transfers(unsigned long data)
|
|
|
dws->tx_end = dws->tx + transfer->len;
|
|
|
dws->rx = transfer->rx_buf;
|
|
|
dws->rx_end = dws->rx + transfer->len;
|
|
|
- dws->len = dws->cur_transfer->len;
|
|
|
+ dws->len = transfer->len;
|
|
|
if (chip != dws->prev_chip)
|
|
|
cs_change = 1;
|
|
|
|
|
@@ -437,13 +364,12 @@ static void pump_transfers(unsigned long data)
|
|
|
| (spi->mode << SPI_MODE_OFFSET)
|
|
|
| (chip->tmode << SPI_TMOD_OFFSET);
|
|
|
}
|
|
|
- message->state = RUNNING_STATE;
|
|
|
|
|
|
/*
|
|
|
* Adjust transfer mode if necessary. Requires platform dependent
|
|
|
* chipselect mechanism.
|
|
|
*/
|
|
|
- if (dws->cs_control) {
|
|
|
+ if (chip->cs_control) {
|
|
|
if (dws->rx && dws->tx)
|
|
|
chip->tmode = SPI_TMOD_TR;
|
|
|
else if (dws->rx)
|
|
@@ -456,10 +382,9 @@ static void pump_transfers(unsigned long data)
|
|
|
}
|
|
|
|
|
|
dw_writew(dws, DW_SPI_CTRL0, cr0);
|
|
|
- spi_chip_sel(dws, spi, 1);
|
|
|
|
|
|
/* 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 */
|
|
|
spi_mask_intr(dws, 0xff);
|
|
@@ -489,31 +414,17 @@ static void pump_transfers(unsigned long data)
|
|
|
dws->dma_ops->dma_transfer(dws, cs_change);
|
|
|
|
|
|
if (chip->poll_mode)
|
|
|
- poll_transfer(dws);
|
|
|
+ return poll_transfer(dws);
|
|
|
|
|
|
- return;
|
|
|
-
|
|
|
-early_exit:
|
|
|
- giveback(dws);
|
|
|
+ return 1;
|
|
|
}
|
|
|
|
|
|
-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 dw_spi *dws = spi_master_get_devdata(master);
|
|
|
|
|
|
- dws->cur_msg = msg;
|
|
|
- /* 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;
|
|
|
+ spi_reset_chip(dws);
|
|
|
}
|
|
|
|
|
|
/* 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)
|
|
|
snprintf(dws->name, sizeof(dws->name), "dw_spi%d", dws->bus_num);
|
|
|
|
|
|
ret = devm_request_irq(dev, dws->irq, dw_spi_irq, IRQF_SHARED,
|
|
|
- dws->name, dws);
|
|
|
+ dws->name, master);
|
|
|
if (ret < 0) {
|
|
|
dev_err(&master->dev, "can not get IRQ\n");
|
|
|
goto err_free_master;
|
|
@@ -649,7 +560,9 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
|
|
|
master->num_chipselect = dws->num_cs;
|
|
|
master->setup = dw_spi_setup;
|
|
|
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->dev.of_node = dev->of_node;
|
|
|
|
|
@@ -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);
|
|
|
ret = devm_spi_register_master(dev, master);
|
|
|
if (ret) {
|