123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388 |
- /*
- * wcxb SPI library
- *
- * Copyright (C) 2013 Digium, Inc.
- *
- * All rights reserved.
- *
- */
- /*
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2 as published by the
- * Free Software Foundation. See the LICENSE file included with
- * this program for more details.
- */
- #define DEBUG
- #include <linux/kernel.h>
- #include <linux/list.h>
- #include <linux/device.h>
- #include <linux/completion.h>
- #include <linux/sched.h>
- #include <linux/delay.h>
- #include <linux/slab.h>
- #include <linux/io.h>
- #include <dahdi/kernel.h>
- #include "wcxb_spi.h"
- #define BBIT(n) (1UL << (31UL - (n)))
- #define SPISRR 0x40
- #define SPISRR_RESET 0x0000000a /* Resets Device */
- #define SPICR 0x60
- #define SPICR_LSB_FIRST BBIT(22) /* LSB First. 0=MSB first transfer */
- #define SPICR_MASTER_INHIBIT BBIT(23) /* Master Transaction Inhibit */
- #define SPICR_SLAVE_SELECT BBIT(24) /* Manual Slave Select Assert Enable */
- #define SPICR_RX_FIFO_RESET BBIT(25) /* Receive FIFO Reset */
- #define SPICR_TX_FIFO_RESET BBIT(26) /* Transmit FIFO Reset */
- #define SPICR_CPHA BBIT(27) /* Clock Phase */
- #define SPICR_CPOL BBIT(28) /* Clock Polarity 0=Active High */
- #define SPICR_MASTER BBIT(29) /* Master Enable */
- #define SPICR_SPE BBIT(30) /* SPI System Enable */
- #define SPICR_LOOP BBIT(31) /* Local Loopback Mode */
- #define SPICR_START_TRANSFER (SPICR_CPHA | SPICR_CPOL | \
- SPICR_MASTER | SPICR_SPE)
- #define SPICR_READY_TRANSFER (SPICR_MASTER_INHIBIT | SPICR_START_TRANSFER)
- #define SPISR 0x64 /* SPI Status Register */
- #define SPISR_SLAVE_MODE_SEL BBIT(26) /* Slave Mode Select Flag */
- #define SPISR_MODF BBIT(27) /* Mode-Fault Error Flag */
- #define SPISR_TX_FULL BBIT(28) /* Transmit FIFO Full */
- #define SPISR_TX_EMPTY BBIT(29) /* Transmit FIFO Empty */
- #define SPISR_RX_FULL BBIT(30) /* Receive FIFO Full */
- #define SPISR_RX_EMPTY BBIT(31) /* Receive FIFO Empty */
- #define SPIDTR 0x68 /* SPI Data Transmit Register */
- #define SPIDRR 0x6c /* SPI Data Receive Register */
- #define SPISSR 0x70 /* SPI Slave Select Register */
- #undef SUCCESS
- #define SUCCESS 0
- struct wcxb_spi_master {
- struct device *parent;
- struct list_head message_queue;
- spinlock_t lock;
- void __iomem *base;
- struct wcxb_spi_message *cur_msg;
- struct wcxb_spi_transfer *cur_transfer;
- u16 bytes_left;
- u16 bytes_in_fifo;
- const u8 *cur_tx_byte;
- u8 *cur_rx_byte;
- u16 auto_cs:1;
- };
- static inline void _wcxb_assert_chip_select(struct wcxb_spi_master *master,
- unsigned int cs)
- {
- const int cs_mask = ~(1UL << cs);
- iowrite32be(cs_mask, master->base + SPISSR);
- ioread32be(master->base + SPISSR);
- }
- static inline void _wcxb_clear_chip_select(struct wcxb_spi_master *master)
- {
- iowrite32be(~(0), master->base + SPISSR);
- ioread32(master->base + SPISSR);
- }
- static inline void wcxb_spi_reset_controller(struct wcxb_spi_master *master)
- {
- u32 spicr = SPICR_READY_TRANSFER;
- spicr |= (master->auto_cs) ? 0 : SPICR_SLAVE_SELECT;
- iowrite32be(SPISRR_RESET, master->base + SPISRR);
- iowrite32be(spicr, master->base + SPICR);
- iowrite32be(0xffffffff, master->base + SPISSR);
- }
- struct wcxb_spi_master *wcxb_spi_master_create(struct device *parent,
- void __iomem *membase,
- bool auto_cs)
- {
- struct wcxb_spi_master *master = NULL;
- master = kzalloc(sizeof(struct wcxb_spi_master), GFP_KERNEL);
- if (!master)
- goto error_exit;
- spin_lock_init(&master->lock);
- INIT_LIST_HEAD(&master->message_queue);
- master->base = membase;
- master->parent = parent;
- master->auto_cs = (auto_cs) ? 1 : 0;
- wcxb_spi_reset_controller(master);
- return master;
- error_exit:
- kfree(master);
- return NULL;
- }
- void wcxb_spi_master_destroy(struct wcxb_spi_master *master)
- {
- struct wcxb_spi_message *m;
- if (!master)
- return;
- while (!list_empty(&master->message_queue)) {
- m = list_first_entry(&master->message_queue,
- struct wcxb_spi_message, node);
- list_del(&m->node);
- if (m->complete)
- m->complete(m->arg);
- }
- kfree(master);
- return;
- }
- static inline bool is_txfifo_empty(const struct wcxb_spi_master *master)
- {
- return ((ioread32(master->base + SPISR) &
- cpu_to_be32(SPISR_TX_EMPTY)) > 0);
- }
- static const u8 DUMMY_TX = 0xff;
- static u8 DUMMY_RX;
- static void _wcxb_spi_transfer_to_fifo(struct wcxb_spi_master *master)
- {
- const unsigned int FIFO_SIZE = 16;
- u32 spicr;
- while (master->bytes_left && master->bytes_in_fifo < FIFO_SIZE) {
- iowrite32be(*master->cur_tx_byte, master->base + SPIDTR);
- master->bytes_in_fifo++;
- master->bytes_left--;
- if (&DUMMY_TX != master->cur_tx_byte)
- master->cur_tx_byte++;
- }
- spicr = (master->auto_cs) ? SPICR_START_TRANSFER :
- SPICR_START_TRANSFER | SPICR_SLAVE_SELECT;
- iowrite32be(spicr, master->base + SPICR);
- }
- static void _wcxb_spi_transfer_from_fifo(struct wcxb_spi_master *master)
- {
- u32 spicr;
- while (master->bytes_in_fifo) {
- *master->cur_rx_byte = ioread32be(master->base + SPIDRR);
- if (&DUMMY_RX != master->cur_rx_byte)
- master->cur_rx_byte++;
- --master->bytes_in_fifo;
- }
- spicr = SPICR_START_TRANSFER;
- spicr |= (master->auto_cs) ? 0 : SPICR_SLAVE_SELECT;
- iowrite32be(spicr | SPICR_MASTER_INHIBIT, master->base + SPICR);
- }
- static void _wcxb_spi_start_transfer(struct wcxb_spi_master *master,
- struct wcxb_spi_transfer *t)
- {
- #ifdef DEBUG
- if (!t || !master || (!t->tx_buf && !t->rx_buf) ||
- master->cur_transfer) {
- WARN_ON(1);
- return;
- }
- #endif
- master->cur_transfer = t;
- master->bytes_left = t->len;
- master->cur_tx_byte = (t->tx_buf) ?: &DUMMY_TX;
- master->cur_rx_byte = (t->rx_buf) ?: &DUMMY_RX;
- _wcxb_spi_transfer_to_fifo(master);
- }
- /**
- * _wcxb_spi_start_message - Start a new message transferring.
- *
- * Must be called with master->lock held.
- *
- */
- static int _wcxb_spi_start_message(struct wcxb_spi_master *master,
- struct wcxb_spi_message *message)
- {
- struct wcxb_spi_transfer *t;
- if (master->cur_msg) {
- /* There is already a message in progress. Queue for later. */
- list_add_tail(&message->node, &master->message_queue);
- return 0;
- }
- if (!message->spi) {
- dev_dbg(master->parent,
- "Queueing message without SPI device specified?\n");
- return -EINVAL;
- };
- master->cur_msg = message;
- _wcxb_assert_chip_select(master, message->spi->chip_select);
- t = list_first_entry(&message->transfers,
- struct wcxb_spi_transfer, node);
- _wcxb_spi_start_transfer(master, t);
- return 0;
- }
- /**
- * wcxb_spi_complete_message - Complete the current message.
- *
- * Called after all transfers in current message have been completed. This will
- * complete the current message and start the next queued message if there are
- * any.
- *
- * Must be called with the master->lock held.
- *
- */
- static void _wcxb_spi_complete_cur_msg(struct wcxb_spi_master *master)
- {
- struct wcxb_spi_message *message;
- if (!master->cur_msg)
- return;
- message = master->cur_msg;
- message->status = SUCCESS;
- _wcxb_clear_chip_select(master);
- master->cur_msg = NULL;
- if (!list_empty(&master->message_queue)) {
- message = list_first_entry(&master->message_queue,
- struct wcxb_spi_message, node);
- list_del(&message->node);
- _wcxb_spi_start_message(master, message);
- }
- return;
- }
- static inline bool
- _wcxb_spi_is_last_transfer(const struct wcxb_spi_transfer *t,
- const struct wcxb_spi_message *message)
- {
- return t->node.next == &message->transfers;
- }
- static inline struct wcxb_spi_transfer *
- _wcxb_spi_next_transfer(struct wcxb_spi_transfer *t)
- {
- return list_entry(t->node.next, struct wcxb_spi_transfer, node);
- }
- /**
- * wcxb_spi_handle_interrupt - Drives the transfers forward.
- *
- * Doesn't necessarily need to be called in the context of a real interrupt, but
- * should be called with interrupts disabled on the local CPU.
- *
- */
- void wcxb_spi_handle_interrupt(struct wcxb_spi_master *master)
- {
- struct wcxb_spi_message *msg;
- struct wcxb_spi_transfer *t;
- void (*complete)(void *arg) = NULL;
- unsigned long flags;
- /* Check if we're not in the middle of a transfer, or not finished with
- * a part of one. */
- spin_lock_irqsave(&master->lock, flags);
- t = master->cur_transfer;
- msg = master->cur_msg;
- if (!msg || !is_txfifo_empty(master))
- goto done;
- #ifdef DEBUG
- if (!t) {
- dev_dbg(master->parent,
- "No current transfer in %s\n", __func__);
- goto done;
- }
- #endif
- /* First read any data out of the receive FIFO into the current
- * transfer. */
- _wcxb_spi_transfer_from_fifo(master);
- if (master->bytes_left) {
- /* The current transfer isn't finished. */
- _wcxb_spi_transfer_to_fifo(master);
- goto done;
- }
- /* The current transfer is finished. Check for another transfer in this
- * message or complete it and look for another message to start. */
- master->cur_transfer = NULL;
- if (_wcxb_spi_is_last_transfer(t, msg)) {
- complete = msg->complete;
- _wcxb_spi_complete_cur_msg(master);
- } else {
- t = _wcxb_spi_next_transfer(t);
- _wcxb_spi_start_transfer(master, t);
- }
- done:
- spin_unlock_irqrestore(&master->lock, flags);
- /* Do not call the complete call back under the bus lock. */
- if (complete)
- complete(msg->arg);
- return;
- }
- int wcxb_spi_async(struct wcxb_spi_device *spi,
- struct wcxb_spi_message *message)
- {
- int res;
- unsigned long flags;
- WARN_ON(!spi || !message || !spi->master);
- if (list_empty(&message->transfers)) {
- /* No transfers in this message? */
- if (message->complete)
- message->complete(message->arg);
- message->status = -EINVAL;
- return 0;
- }
- message->status = -EINPROGRESS;
- message->spi = spi;
- spin_lock_irqsave(&spi->master->lock, flags);
- res = _wcxb_spi_start_message(spi->master, message);
- spin_unlock_irqrestore(&spi->master->lock, flags);
- return res;
- }
- static void wcxb_spi_complete_message(void *arg)
- {
- complete((struct completion *)arg);
- }
- int wcxb_spi_sync(struct wcxb_spi_device *spi, struct wcxb_spi_message *message)
- {
- DECLARE_COMPLETION_ONSTACK(done);
- WARN_ON(!spi || !spi->master);
- message->complete = wcxb_spi_complete_message;
- message->arg = &done;
- wcxb_spi_async(spi, message);
- /* TODO: There has got to be a better way to do this. */
- while (!try_wait_for_completion(&done)) {
- wcxb_spi_handle_interrupt(spi->master);
- cpu_relax();
- }
- return message->status;
- }
|