From f52838cd03de045aa67cc1a0c1614ea5bfb30fcc Mon Sep 17 00:00:00 2001 From: Alan Olsen Date: Wed, 11 Nov 2009 13:12:42 -0800 Subject: [PATCH 065/104] Moorestown SPI Slave Controller driver v1.1 consolidation patch This patch contains the following patches: Alpha2-1.0-1-1-mrst-SPI-Slave-Core-Driver-K29.patch [PATCH] SPI Slave Support Added to SPI Core Driver Signed-off-by: Pranav K. Sanghadia Alpha2-1.0-1-1-mrst-SPI-Slave-controller-driver.patch [PATCH] SPI slave controller driver for Moorestown platform This driver currently supports only programmed IO mode. Config settings are: CONFIG_SPI_MRST_SLAVE=y CONFIG_SPI_MRST_SLAVE_DMA is not set Signed-off-by: Ken Mills Alpha2-1.0-1-1-DMA-Support-added-in-SPI-Slave-Controller-Driver.patch [PATCH] This patch adds DMA support for SPI Slave Controller Driver. DMA provides highspeed data transfer between SPI-SSP and external Master mode device Signed-off-by: Pranav K. Sanghadia Alpha2-1.0-1-1-mrst-SPI-Slave-controller-fix-DMA-Issue.patch [PATCH] Alpha2-1.0-1-1-mrst-SPI-Slave-controller-fix-DMA-Issue.patch [PATCH] Optimized SSP clock bitbang routine Signed-off-by: Ken Mills Signed-off-by: Alan Olsen --- drivers/spi/Kconfig | 11 + drivers/spi/Makefile | 1 + drivers/spi/mrst_spi_slave.c | 1227 ++++++++++++++++++++++++++++++++++++ drivers/spi/spi.c | 403 +++++++++++- include/linux/spi/mrst_spi_slave.h | 143 +++++ include/linux/spi/spi.h | 98 +++- 6 files changed, 1862 insertions(+), 21 deletions(-) create mode 100644 drivers/spi/mrst_spi_slave.c create mode 100644 include/linux/spi/mrst_spi_slave.h diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 9d4ff53..b94445b 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -360,5 +360,16 @@ config SPI_TLE62X0 endif # SPI_MASTER # (slave support would go here) +config SPI_MRST_SLAVE + tristate "SPI slave controller driver for Intel Moorestown platform " + depends on SPI_MASTER + help + This is the SPI slave controller driver for Intel Moorestown platform + +config SPI_MRST_SLAVE_DMA + boolean "Enable DMA for MRST SPI Slave Controller" + depends on INTEL_LNW_DMAC1 + help + This has to be enabled after Moorestown DMAC1 driver is enabled endif # SPI diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c78cb77..8acdd96 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_SPI_TLE62X0) += tle62x0.o # ... add above this line ... # SPI slave controller drivers (upstream link) +obj-$(CONFIG_SPI_MRST_SLAVE) += mrst_spi_slave.o # ... add above this line ... # SPI slave drivers (protocol for that link) diff --git a/drivers/spi/mrst_spi_slave.c b/drivers/spi/mrst_spi_slave.c new file mode 100644 index 0000000..82a50b7 --- /dev/null +++ b/drivers/spi/mrst_spi_slave.c @@ -0,0 +1,1227 @@ +/* + * mrst_spi_slave.c - Moorestown SPI slave controller driver + * based on pxa2xx_spi.c + * + * Copyright (C) Intel 2009 + * Ken Mills + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +/* + * Note: + * + * Supports interrupt programmed I/O, DMA and non-interrupt polled transfers. + * + */ + +#include +#include +#include +#include + +#ifdef CONFIG_SPI_MRST_SLAVE_DMA +#include +#include +#endif + +#include +#include + + +#define DRIVER_NAME "mrst_spi_slave" + +#define SSP_NOT_SYNC 0x400000 + +MODULE_AUTHOR(""); +MODULE_DESCRIPTION("Moorestown SPI Slave Contoller"); +MODULE_LICENSE("GPL"); + +/* + * For testing SSCR1 changes that require SSP restart, basically + * everything except the service and interrupt enables + */ +#define SSCR1_CHANGE_MASK (SSCR1_TTELP | SSCR1_TTE | SSCR1_EBCEI | SSCR1_SCFR \ + | SSCR1_ECRA | SSCR1_ECRB | SSCR1_SCLKDIR \ + | SSCR1_SFRMDIR \ + | SSCR1_RWOT | SSCR1_TRAIL | SSCR1_PINTE \ + | SSCR1_STRF | SSCR1_EFWR | SSCR1_RFT \ + | SSCR1_TFT | SSCR1_SPH | SSCR1_SPO) + +#define DEFINE_SSP_REG(reg, off) \ +static inline u32 read_##reg(void *p) { return __raw_readl(p + (off)); } \ +static inline void write_##reg(u32 v, void *p) { __raw_writel(v, p + (off)); } + +DEFINE_SSP_REG(SSCR0, 0x00) +DEFINE_SSP_REG(SSCR1, 0x04) +DEFINE_SSP_REG(SSSR, 0x08) +DEFINE_SSP_REG(SSITR, 0x0c) +DEFINE_SSP_REG(SSDR, 0x10) +DEFINE_SSP_REG(SSTO, 0x28) +DEFINE_SSP_REG(SSPSP, 0x2c) + +DEFINE_SSP_REG(IPCCSR, 0x00); +DEFINE_SSP_REG(IPCPISR, 0x08); +DEFINE_SSP_REG(IPCPIMR, 0x10); + +DEFINE_SSP_REG(I2CCTRL, 0x00); +DEFINE_SSP_REG(I2CDATA, 0x04); + +DEFINE_SSP_REG(GPLR1, 0x04); +DEFINE_SSP_REG(GPDR1, 0x0c); +DEFINE_SSP_REG(GPSR1, 0x14); +DEFINE_SSP_REG(GPCR1, 0x1C); +DEFINE_SSP_REG(GAFR1_U, 0x44); + +#define START_STATE ((void *)0) +#define RUNNING_STATE ((void *)1) +#define DONE_STATE ((void *)2) +#define ERROR_STATE ((void *)-1) + +struct driver_data { + /* Driver model hookup */ + struct pci_dev *pdev; + + /* SPI framework hookup */ + struct spi_slave *slave; + + /* SSP register addresses */ + void *paddr; + void *ioaddr; + u32 iolen; + int irq; + + /* IPC registers */ + void *IPC_paddr; + void *IPC_ioaddr; + + /* I2C registers */ + void *I2C_paddr; + void *I2C_ioaddr; + + /* SSP masks*/ + u32 dma_cr1; + u32 int_cr1; + u32 clear_sr; + u32 mask_sr; + + struct tasklet_struct poll_transfer; + + spinlock_t lock; + int busy; + int run; + + /* Current message transfer state info */ + struct spi_message *cur_msg; + size_t len; + void *tx; + void *tx_end; + void *rx; + void *rx_end; + int dma_mapped; + dma_addr_t rx_dma; + dma_addr_t tx_dma; + size_t rx_map_len; + size_t tx_map_len; + u8 n_bytes; + int (*write)(struct driver_data *drv_data); + int (*read)(struct driver_data *drv_data); + irqreturn_t (*transfer_handler)(struct driver_data *drv_data); + void (*cs_control)(u32 command); + +#ifdef CONFIG_SPI_MRST_SLAVE_DMA + struct lnw_dma_slave dmas_tx; + struct lnw_dma_slave dmas_rx; + struct dma_chan *txchan; + struct dma_chan *rxchan; + + int txdma_done; + int rxdma_done; + u64 tx_param; + u64 rx_param; + struct pci_dev *dmac1; +#endif +}; + +struct chip_data { + u32 cr0; + u32 cr1; + u32 psp; + u32 timeout; + u8 n_bytes; + u32 threshold; + u8 enable_dma; + u8 poll_mode; /* 1 means use poll mode */ + u8 bits_per_word; + int (*write)(struct driver_data *drv_data); + int (*read)(struct driver_data *drv_data); +}; + +static void flush(struct driver_data *drv_data) +{ + void *reg = drv_data->ioaddr; + u32 sssr; + + /* If the transmit fifo is not empty, reset the interface. */ + sssr = read_SSSR(reg); + if ((sssr & 0xf00) || (sssr & SSSR_TNF) == 0) { + write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg); + return; + } + + while (read_SSSR(reg) & SSSR_RNE) + read_SSDR(reg); + + write_SSSR(SSSR_ROR, reg); + write_SSSR(SSSR_TUR, reg); + + return; +} + +static int null_writer(struct driver_data *drv_data) +{ + void *reg = drv_data->ioaddr; + u8 n_bytes = drv_data->n_bytes; + + if (((read_SSSR(reg) & 0x00000f00) == 0x00000f00) + || (drv_data->tx == drv_data->tx_end)) + return 0; + + write_SSDR(0, reg); + drv_data->tx += n_bytes; + + return 1; +} + +static int null_reader(struct driver_data *drv_data) +{ + void *reg = drv_data->ioaddr; + u8 n_bytes = drv_data->n_bytes; + + while ((read_SSSR(reg) & SSSR_RNE) + && (drv_data->rx < drv_data->rx_end)) { + read_SSDR(reg); + drv_data->rx += n_bytes; + } + + return drv_data->rx == drv_data->rx_end; +} + +static int u8_writer(struct driver_data *drv_data) +{ + void *reg = drv_data->ioaddr; + if (((read_SSSR(reg) & 0x00000f00) == 0x00000f00) + || (drv_data->tx == drv_data->tx_end)) + return 0; + + write_SSDR(*(u8 *)(drv_data->tx), reg); + ++drv_data->tx; + + return 1; +} + +static int u8_reader(struct driver_data *drv_data) +{ + void *reg = drv_data->ioaddr; + while ((read_SSSR(reg) & SSSR_RNE) + && (drv_data->rx < drv_data->rx_end)) { + *(u8 *)(drv_data->rx) = read_SSDR(reg); + ++drv_data->rx; + } + + return drv_data->rx == drv_data->rx_end; +} + +static int u16_writer(struct driver_data *drv_data) +{ + void *reg = drv_data->ioaddr; + if (((read_SSSR(reg) & 0x00000f00) == 0x00000f00) + || (drv_data->tx == drv_data->tx_end)) + return 0; + + write_SSDR(*(u16 *)(drv_data->tx), reg); + drv_data->tx += 2; + + return 1; +} + +static int u16_reader(struct driver_data *drv_data) +{ + void *reg = drv_data->ioaddr; + while ((read_SSSR(reg) & SSSR_RNE) + && (drv_data->rx < drv_data->rx_end)) { + *(u16 *)(drv_data->rx) = read_SSDR(reg); + drv_data->rx += 2; + } + + return drv_data->rx == drv_data->rx_end; +} + +static int u32_writer(struct driver_data *drv_data) +{ + void *reg = drv_data->ioaddr; + if (((read_SSSR(reg) & 0x00000f00) == 0x00000f00) + || (drv_data->tx == drv_data->tx_end)) + return 0; + + write_SSDR(*(u32 *)(drv_data->tx), reg); + drv_data->tx += 4; + + return 1; +} + +static int u32_reader(struct driver_data *drv_data) +{ + void *reg = drv_data->ioaddr; + while ((read_SSSR(reg) & SSSR_RNE) + && (drv_data->rx < drv_data->rx_end)) { + *(u32 *)(drv_data->rx) = read_SSDR(reg); + drv_data->rx += 4; + } + + return drv_data->rx == drv_data->rx_end; +} + + + +/* caller already set message->status; dma and pio irqs are blocked */ +static void giveback(struct driver_data *drv_data) +{ + struct spi_message *msg; + + msg = drv_data->cur_msg; + msg->state = NULL; + if (msg->complete) + msg->complete(msg->context); +} + +#ifdef CONFIG_SPI_MRST_SLAVE_DMA + +static bool chan_filter(struct dma_chan *chan, void *param) +{ + struct driver_data *drv_data = (struct driver_data *)param; + bool ret = false; + + if (!drv_data->dmac1) + return ret; + + if (chan->device->dev == &drv_data->dmac1->dev) + ret = true; + + return ret; +} + +static void int_transfer_complete(struct driver_data *drv_data); + +static void mrst_spi_dma_done(void *arg) +{ + u64 *param = arg; + struct driver_data *drv_data; + int *done; + + drv_data = (struct driver_data *)(u32)(*param >> 32); + done = (int *)(u32)(*param & 0xffffffff); + *done = 1; + + if (!drv_data->txdma_done || !drv_data->rxdma_done) + return; + int_transfer_complete(drv_data); +} + +static void mrst_spi_dma_init(struct driver_data *drv_data) +{ + struct lnw_dma_slave *rxs, *txs; + dma_cap_mask_t mask; + + /* Use DMAC1 */ + drv_data->dmac1 = pci_get_device(PCI_VENDOR_ID_INTEL, 0x0814, NULL); + if (!drv_data->dmac1) { + printk(KERN_WARNING "SPI Slave:Can't find DMAC1\n"); + return; + } + + /* 1. init rx channel */ + rxs = &drv_data->dmas_rx; + + rxs->dirn = DMA_FROM_DEVICE; + rxs->hs_mode = LNW_DMA_HW_HS; + rxs->cfg_mode = LNW_DMA_PER_TO_MEM; + rxs->src_width = LNW_DMA_WIDTH_16BIT; + rxs->dst_width = LNW_DMA_WIDTH_32BIT; + rxs->src_msize = LNW_DMA_MSIZE_8; + rxs->dst_msize = LNW_DMA_MSIZE_8; + + + dma_cap_zero(mask); + dma_cap_set(DMA_MEMCPY, mask); + dma_cap_set(DMA_SLAVE, mask); + + drv_data->rxchan = dma_request_channel(mask, chan_filter, drv_data); + if (!drv_data->rxchan) + goto err_exit; + + drv_data->rxchan->private = rxs; + + /* 2. init tx channel */ + txs = &drv_data->dmas_tx; + + txs->dirn = DMA_TO_DEVICE; + txs->hs_mode = LNW_DMA_HW_HS; + txs->cfg_mode = LNW_DMA_MEM_TO_PER; + txs->src_width = LNW_DMA_WIDTH_32BIT; + txs->dst_width = LNW_DMA_WIDTH_16BIT; + txs->src_msize = LNW_DMA_MSIZE_8; + txs->dst_msize = LNW_DMA_MSIZE_8; + + + dma_cap_set(DMA_SLAVE, mask); + dma_cap_set(DMA_MEMCPY, mask); + + drv_data->txchan = dma_request_channel(mask, chan_filter, drv_data); + if (!drv_data->txchan) + goto free_rxchan; + else + + drv_data->txchan->private = txs; + + /* set the dma done bit to 1 */ + drv_data->txdma_done = 1; + drv_data->rxdma_done = 1; + + drv_data->tx_param = ((u64)(u32)drv_data << 32) + | (u32)(&drv_data->txdma_done); + drv_data->rx_param = ((u64)(u32)drv_data << 32) + | (u32)(&drv_data->rxdma_done); + return; + +free_rxchan: + printk(KERN_ERR "SPI-Slave Error : DMA Channle Not available\n"); + dma_release_channel(drv_data->rxchan); +err_exit: + printk(KERN_ERR "SPI-Slave Error : DMA Channel Not available\n"); + pci_dev_put(drv_data->dmac1); + return; +} + +static void mrst_spi_dma_exit(struct driver_data *drv_data) +{ + dma_release_channel(drv_data->txchan); + dma_release_channel(drv_data->rxchan); + pci_dev_put(drv_data->dmac1); +} + +static void dma_transfer(struct driver_data *drv_data) +{ + dma_addr_t ssdr_addr; + struct dma_async_tx_descriptor *txdesc = NULL, *rxdesc = NULL; + struct dma_chan *txchan, *rxchan; + enum dma_ctrl_flags flag; + + /* get Data Read/Write address */ + ssdr_addr = (dma_addr_t)(u32)(drv_data->paddr + 0x10); + + if (drv_data->tx_dma) + drv_data->txdma_done = 0; + + if (drv_data->rx_dma) + drv_data->rxdma_done = 0; + + /* 2. start the TX dma transfer */ + txchan = drv_data->txchan; + rxchan = drv_data->rxchan; + + flag = DMA_PREP_INTERRUPT | DMA_CTRL_ACK; + + if (drv_data->rx_dma) { + rxdesc = rxchan->device->device_prep_dma_memcpy + (rxchan, /* DMA Channel */ + drv_data->rx_dma, /* DAR */ + ssdr_addr, /* SAR */ + drv_data->len, /* Data Length */ + flag); /* Flag */ + + rxdesc->callback = mrst_spi_dma_done; + rxdesc->callback_param = &drv_data->rx_param; + } + + /* 3. start the RX dma transfer */ + if (drv_data->tx_dma) { + txdesc = txchan->device->device_prep_dma_memcpy + (txchan, /* DMA Channel */ + ssdr_addr, /* DAR */ + drv_data->tx_dma, /* SAR */ + drv_data->len, /* Data Length */ + flag); /* Flag */ + + txdesc->callback = mrst_spi_dma_done; + txdesc->callback_param = &drv_data->tx_param; + } + + if (rxdesc) + rxdesc->tx_submit(rxdesc); + if (txdesc) + txdesc->tx_submit(txdesc); + +} + +static int map_dma_buffers(struct driver_data *drv_data) +{ + drv_data->rx_dma = (dma_addr_t) virt_to_phys(drv_data->rx); + drv_data->tx_dma = (dma_addr_t) virt_to_phys(drv_data->tx); + return 1; +} +#endif + +static void int_error_stop(struct driver_data *drv_data, const char* msg) +{ + void *reg = drv_data->ioaddr; + + /* Stop and reset SSP */ + write_SSSR(drv_data->clear_sr, reg); + write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg); + write_SSTO(0, reg); + flush(drv_data); + + dev_err(&drv_data->pdev->dev, "%s\n", msg); + + drv_data->cur_msg->state = ERROR_STATE; +} + +static void int_transfer_complete(struct driver_data *drv_data) +{ + void *reg = drv_data->ioaddr; + + /* Clear Status Register */ + write_SSSR(drv_data->clear_sr, reg); + +#ifdef CONFIG_SPI_MRST_SLAVE_DMA + /* Disable Triggers to DMA */ + write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg); +#else + /* Disable Interrupt */ + write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg); +#endif + /* Stop getting Time Outs */ + write_SSTO(0, reg); + + /* Update total byte transfered return count actual bytes read */ + drv_data->cur_msg->actual_length += drv_data->len - + (drv_data->rx_end - drv_data->rx); + + drv_data->cur_msg->status = 0; + giveback(drv_data); +} + +static void transfer_complete(struct driver_data *drv_data) +{ + /* Update total byte transfered return count actual bytes read */ + drv_data->cur_msg->actual_length += + drv_data->len - (drv_data->rx_end - drv_data->rx); + + drv_data->cur_msg->status = 0; + giveback(drv_data); +} + +static irqreturn_t interrupt_transfer(struct driver_data *drv_data) +{ + void *reg = drv_data->ioaddr; + u32 irq_mask = (read_SSCR1(reg) & SSCR1_TIE) ? + drv_data->mask_sr : drv_data->mask_sr & ~SSSR_TFS; + + u32 irq_status = read_SSSR(reg) & irq_mask; + if (irq_status & SSSR_ROR) { + int_error_stop(drv_data, "interrupt_transfer: fifo overrun"); + return IRQ_HANDLED; + } + + if (irq_status & SSSR_TINT) { + write_SSSR(SSSR_TINT, reg); + if (drv_data->read(drv_data)) { + int_transfer_complete(drv_data); + return IRQ_HANDLED; + } + } + + /* Drain rx fifo, Fill tx fifo and prevent overruns */ + do { + if (drv_data->read(drv_data)) { + int_transfer_complete(drv_data); + return IRQ_HANDLED; + } + } while (drv_data->write(drv_data)); + + if (drv_data->read(drv_data)) { + int_transfer_complete(drv_data); + return IRQ_HANDLED; + } + + if (drv_data->tx == drv_data->tx_end) + write_SSCR1(read_SSCR1(reg) & ~SSCR1_TIE, reg); + + return IRQ_HANDLED; +} + +static irqreturn_t ssp_int(int irq, void *dev_id) +{ + struct driver_data *drv_data = dev_id; + void *reg = drv_data->ioaddr; + u32 status = read_SSSR(reg); + + #ifdef CONFIG_SPI_MRST_SLAVE_DMA + if (status & SSSR_ROR || status & SSSR_TUR) { + printk(KERN_DEBUG "--- SPI ROR or TUR Occred : SSSR=%x\n", status); + write_SSSR(SSSR_ROR, reg); /* Clear ROR */ + write_SSSR(SSSR_TUR, reg); /* Clear TUR */ + return IRQ_HANDLED; + } + return IRQ_NONE; + #endif + /* just return if this is not our interrupt */ + if (!(read_SSSR(reg) & drv_data->mask_sr)) + return IRQ_NONE; + + if (!drv_data->cur_msg) { + write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg); + write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg); + write_SSSR(drv_data->clear_sr, reg); + + /* Never fail */ + return IRQ_HANDLED; + } + return drv_data->transfer_handler(drv_data); +} + +static void poll_transfer(unsigned long data) +{ + struct driver_data *drv_data = (struct driver_data *)data; + + if (drv_data->tx) + while (drv_data->tx != drv_data->tx_end) { + drv_data->write(drv_data); + drv_data->read(drv_data); + } + + while (!drv_data->read(drv_data)) + ; + + transfer_complete(drv_data); +} + +static int transfer(struct spi_device *spi, struct spi_message *msg) +{ + struct driver_data *drv_data = \ + spi_slave_get_devdata(spi->slave); + unsigned long flags; + struct chip_data *chip = NULL; + struct spi_transfer *transfer = NULL; + void *reg = drv_data->ioaddr; + void *i2cReg = drv_data->I2C_ioaddr; + u32 clk_div = 0; + u8 bits = 0; + u32 cr0; + u32 cr1; + u32 sssr; + + spin_lock_irqsave(&drv_data->lock, flags); + msg->actual_length = 0; + msg->status = -EINPROGRESS; + drv_data->cur_msg = msg; + /* Initial message state*/ + msg->state = START_STATE; + + /* We handle only one transfer message since the protocol module has to + control the out of band signaling. */ + transfer = list_entry(msg->transfers.next, + struct spi_transfer, + transfer_list); + + chip = spi_get_ctldata(msg->spi); + + drv_data->busy = 1; + + /* Check transfer length */ + if (transfer->len > 8192) { + dev_warn(&drv_data->pdev->dev, "SPI-SLAVE: transfer " + "length greater than 8192\n"); + msg->status = -EINVAL; + giveback(drv_data); + spin_unlock_irqrestore(&drv_data->lock, flags); + return 0; + } + + /* Setup the transfer state based on the type of transfer */ + flush(drv_data); + drv_data->n_bytes = chip->n_bytes; + drv_data->tx = (void *)transfer->tx_buf; + drv_data->tx_end = drv_data->tx + transfer->len; + drv_data->rx = transfer->rx_buf; + drv_data->rx_end = drv_data->rx + transfer->len; + drv_data->rx_dma = transfer->rx_dma; + drv_data->tx_dma = transfer->tx_dma; + drv_data->len = transfer->len; + drv_data->write = drv_data->tx ? chip->write : null_writer; + drv_data->read = drv_data->rx ? chip->read : null_reader; + + /* Change speed and bit per word on a per transfer */ + cr0 = chip->cr0; + if (transfer->bits_per_word) { + + bits = chip->bits_per_word; + + clk_div = 0x0; + + if (transfer->bits_per_word) + bits = transfer->bits_per_word; + + + if (bits <= 8) { + drv_data->n_bytes = 1; + drv_data->read = drv_data->read != null_reader ? + u8_reader : null_reader; + drv_data->write = drv_data->write != null_writer ? + u8_writer : null_writer; + } else if (bits <= 16) { + drv_data->n_bytes = 2; + drv_data->read = drv_data->read != null_reader ? + u16_reader : null_reader; + drv_data->write = drv_data->write != null_writer ? + u16_writer : null_writer; + } else if (bits <= 32) { + drv_data->n_bytes = 4; + drv_data->read = drv_data->read != null_reader ? + u32_reader : null_reader; + drv_data->write = drv_data->write != null_writer ? + u32_writer : null_writer; + } + + cr0 = clk_div + | SSCR0_Motorola + | SSCR0_DataSize(bits > 16 ? bits - 16 : bits) + | SSCR0_SSE + | SSCR0_TIM + | SSCR0_RIM + | (bits > 16 ? SSCR0_EDSS : 0); + } + + +#ifdef CONFIG_SPI_MRST_SLAVE_DMA + drv_data->dma_mapped = 0; + if (chip->enable_dma) + drv_data->dma_mapped = map_dma_buffers(drv_data); +#endif + + msg->state = RUNNING_STATE; + /* Ensure we have the correct interrupt handler */ + drv_data->transfer_handler = interrupt_transfer; + /* Clear status */ + cr1 = chip->cr1 | chip->threshold; + write_SSSR(drv_data->clear_sr, reg); + + /* Reload the config and do bitbanging only if SSP not-enable or not-synchronized */ + if( ( read_SSSR(reg) & SSP_NOT_SYNC ) || (!(read_SSCR0(reg) & SSCR0_SSE) ) ) { + + write_SSSR(drv_data->clear_sr, reg); /* clear status */ + write_SSCR0(cr0 & ~SSCR0_SSE, reg); + write_SSPSP(0x02010007, reg); + write_SSTO(chip->timeout, reg); + write_SSCR1(0x13001DC0, reg); /* TBD remove hardcoded value */ + write_SSCR0(cr0, reg); + + /* + * This routine uses the DFx block to override the SSP inputs + * and outputs allowing us to bit bang SSPSCLK. On Langwell, + * we have to generate the clock to clear busy. + */ + + write_I2CDATA(0x3, i2cReg); + udelay(10); + write_I2CCTRL(0x01070034, i2cReg); + udelay(10); + write_I2CDATA(0x00000099, i2cReg); + udelay(10); + write_I2CCTRL(0x01070038, i2cReg); + udelay(10); + sssr = read_SSSR(reg); + + /* Bit bang the clock until CSS clears */ + while (sssr & 0x400000) { + write_I2CDATA(0x2, i2cReg); + udelay(10); + write_I2CCTRL(0x01070034, i2cReg); + udelay(10); + write_I2CDATA(0x3, i2cReg); + udelay(10); + write_I2CCTRL(0x01070034, i2cReg); + udelay(10); + sssr = read_SSSR(reg); + } + + write_I2CDATA(0x0, i2cReg); + udelay(10); + write_I2CCTRL(0x01070038, i2cReg); + + } else { + write_SSTO(chip->timeout, reg); + write_SSCR1(0x13001DC0, reg); /* TBD: remove hardcoded value */ + } + + /* transfer using DMA */ + if (drv_data->dma_mapped) { +#ifdef CONFIG_SPI_MRST_SLAVE_DMA + cr1 = cr1 | drv_data->dma_cr1; + write_SSCR1(0x13701DC0, reg); /* TBD: remove hardcoded value */ + dma_transfer(drv_data); +#endif + } + + /* transfer using non interrupt polling */ + else if (chip->poll_mode) + tasklet_schedule(&drv_data->poll_transfer); + + /* transfer using interrupt driven programmed I/O */ + else { + cr1 = cr1 | drv_data->int_cr1; + write_SSCR1(cr1, reg); + } + + spin_unlock_irqrestore(&drv_data->lock, flags); + return 0; +} + +static int setup(struct spi_device *spi) +{ + struct mrst_spi_chip *chip_info = NULL; + struct chip_data *chip; + + if (!spi->bits_per_word) + spi->bits_per_word = 8; + + if ((spi->bits_per_word < 4 || spi->bits_per_word > 32)) + return -EINVAL; + + /* Only alloc on first setup */ + chip = spi_get_ctldata(spi); + if (!chip) { + chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL); + if (!chip) { + dev_err(&spi->dev, + "failed setup: can't allocate chip data\n"); + return -ENOMEM; + } + + chip->enable_dma = 1; + chip->poll_mode = 1; + chip->timeout = 1000; + chip->threshold = SSCR1_RxTresh(1) | SSCR1_TxTresh(1); + } + + /* + * protocol drivers may change the chip settings, so... + * if chip_info exists, use it + */ + chip_info = spi->controller_data; + + /* chip_info isn't always needed */ + chip->cr1 = 0; + if (chip_info) { + + chip->timeout = chip_info->timeout; + + chip->threshold = (SSCR1_RxTresh(chip_info->rx_threshold) & + SSCR1_RFT) | + (SSCR1_TxTresh(chip_info->tx_threshold) & + SSCR1_TFT); + + + if (chip_info->enable_loopback) + chip->cr1 = SSCR1_LBM; + } + + chip->cr0 = SSCR0_Motorola + | SSCR0_DataSize(spi->bits_per_word > 16 ? + spi->bits_per_word - 16 : spi->bits_per_word) + | SSCR0_SSE + | SSCR0_TIM + | SSCR0_RIM + | (spi->bits_per_word > 16 ? SSCR0_EDSS : 0); + chip->cr1 &= ~(SSCR1_SPO | SSCR1_SPH); + chip->cr1 |= (((spi->mode & SPI_CPHA) != 0) ? SSCR1_SPH : 0) + | (((spi->mode & SPI_CPOL) != 0) ? SSCR1_SPO : 0); + /* set slave mode */ + chip->cr1 |= SSCR1_SCLKDIR | SSCR1_SFRMDIR; + chip->cr1 |= SSCR1_SCFR; /* slave clock is not free running */ + dev_dbg(&spi->dev, "%d bits/word, mode %d\n", + spi->bits_per_word, + spi->mode & 0x3); + + if (spi->bits_per_word <= 8) { + chip->n_bytes = 1; + chip->read = u8_reader; + chip->write = u8_writer; + } else if (spi->bits_per_word <= 16) { + chip->n_bytes = 2; + chip->read = u16_reader; + chip->write = u16_writer; + } else if (spi->bits_per_word <= 32) { + chip->cr0 |= SSCR0_EDSS; + chip->n_bytes = 4; + chip->read = u32_reader; + chip->write = u32_writer; + } else { + dev_err(&spi->dev, "invalid wordsize\n"); + return -ENODEV; + } + chip->bits_per_word = spi->bits_per_word; + spi_set_ctldata(spi, chip); + + return 0; +} + +static void cleanup(struct spi_device *spi) +{ + struct chip_data *chip = spi_get_ctldata(spi); + + kfree(chip); +} + +static struct mrst_spi_chip spidev_chip_info = { + .tx_threshold = 8, /* SSP hardware FIFO threshold */ + .rx_threshold = 8, /* SSP hardware FIFO threshold */ + .timeout = 235, /* See Intel documentation */ +}; + +/* + * mrst_parse_spi_dib - mrst-ssp parse the spi device info block + * table + * @pdev: spi controller pci device structure + * @drv_data: spi controller driver data + * Context: can sleep + * + * ssp controller needs to parse the spi device info block table + * saved in PCI bar 1 and register them with the spi core subsystem. + */ +static void mrst_parse_spi_dib(struct pci_dev *pdev, + struct driver_data *drv_data) +{ + u32 dib_len; + void *dib_vaddr; + unsigned long dib_paddr; + struct spi_board_info info[1]; + struct spi_dib_header *header; + struct spi_dib *dib; + int info_num, i, j, dib_bar; + u16 *pval; + + dib_bar = 1; + dib_paddr = pci_resource_start(pdev, dib_bar); + dib_len = pci_resource_len(pdev, dib_bar); + + printk(KERN_INFO "SPI-Slave: %s() - paddr = 0x%08lx, " + "iolen = 0x%x\n", __func__, dib_paddr, dib_len); + + dib_vaddr = ioremap(dib_paddr, dib_len); + if (!dib_vaddr) { + dev_err(&pdev->dev, "%s(): ioremap failed\n", __func__); + goto err_ioremap; + } + + /* bar1 contains a pointer to the SPI DIB table */ + if (dib_len == 8) { + u32 *ptemp = (u32 *)dib_vaddr; + dib_len = *(ptemp + 1); + dib_vaddr = ioremap(*(unsigned long *)dib_vaddr, dib_len); + iounmap(ptemp); + } + + header = (struct spi_dib_header *)dib_vaddr; + info_num = (header->length - sizeof(*header)) / + sizeof(*dib); + dib = (struct spi_dib *)&header[1]; + + /* search for our dib entry. */ + for (i = 0; i < info_num; i++) + if (dib[i].host_num == 3) + break; + if (i == info_num) + return; + + strncpy(info[0].modalias, dib[i].name, SPI_DIB_NAME_LEN); + info[0].irq = dib[i].irq; + info[0].bus_num = dib[i].host_num; + info[0].chip_select = dib[i].cs; + info[0].mode = 0; + info[0].max_speed_hz = 0; + + printk(KERN_INFO "SPI-Slave: name = %s, irq = 0x%x, " + "bus = %d, cs = %d\n", info[0].modalias, info[0].irq, + info[0].bus_num, info[0].chip_select); + + pval = (u16 *)&(dib[i].dev_data[0]); + + info[0].controller_data = &spidev_chip_info; /* Slave chip config */ + + for (j = 0; j < 5; j++) { + spidev_chip_info.extra_data[j] = *pval; + pval++; + } + + spi_register_board_info(info, 1); + +err_ioremap: + pci_release_region(pdev, dib_bar); + + return; +} + +static int mrst_spi_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct device *dev = &pdev->dev; + struct spi_slave *slave; + struct driver_data *drv_data = 0; + int status = 0; + int pci_bar = 0; + + printk(KERN_INFO "SPI-Slave: found PCI SSP controller(ID: %04x:%04x)\n", + pdev->vendor, pdev->device); + + status = pci_enable_device(pdev); + if (status) + return status; + + /* Allocate Slave with space for drv_data and null dma buffer */ + slave = spi_alloc_slave(dev, sizeof(struct driver_data)); + + if (!slave) { + dev_err(&pdev->dev, "cannot alloc spi_slave\n"); + status = -ENOMEM; + goto err_free_slave0; + } + + drv_data = spi_slave_get_devdata(slave); + drv_data->slave = slave; + + drv_data->pdev = pdev; + spin_lock_init(&drv_data->lock); + + slave->bus_num = 3; + slave->num_chipselect = 1; + slave->cleanup = cleanup; + slave->setup = setup; + slave->transfer = transfer; + + /* get basic io resource and map it */ + drv_data->paddr = (void *)pci_resource_start(pdev, pci_bar); + drv_data->iolen = pci_resource_len(pdev, pci_bar); + + status = pci_request_region(pdev, pci_bar, dev_name(&pdev->dev)); + if (status) + goto err_free_slave1; + + drv_data->ioaddr = + ioremap_nocache((u32)drv_data->paddr, drv_data->iolen); + if (!drv_data->ioaddr) { + status = -ENOMEM; + goto err_free_slave2; + } + printk(KERN_INFO "SPI-Slave: ioaddr = : %08x\n", (int)drv_data->ioaddr); + printk(KERN_INFO "SPI-Slave: attaching to IRQ: %04x\n", pdev->irq); + + mrst_parse_spi_dib(pdev, drv_data); + + /* get base address of IPC registers */ + drv_data->IPC_paddr = (void *)0xffae8000; + drv_data->IPC_ioaddr = + ioremap_nocache((unsigned long)drv_data->IPC_paddr, 0x80); + if (!drv_data->IPC_ioaddr) { + status = -ENOMEM; + goto err_free_slave3; + } + /* get base address of I2C_Serbus registers */ + drv_data->I2C_paddr = (void *)0xff12b000; + drv_data->I2C_ioaddr = + ioremap_nocache((unsigned long)drv_data->I2C_paddr, 0x10); + if (!drv_data->I2C_ioaddr) { + status = -ENOMEM; + goto err_free_slave4; + } + + printk(KERN_INFO "SPI-Slave: IPC_ioaddr = : %08x\n", + (int)drv_data->IPC_ioaddr); + printk(KERN_INFO "SPI-Slave: IPCCSR = : %08x\n", + read_IPCCSR(drv_data->IPC_ioaddr)); + write_IPCCSR(0x802, drv_data->IPC_ioaddr); + printk(KERN_INFO "SPI-Slave: IPCCSR = : %08x\n", + read_IPCCSR(drv_data->IPC_ioaddr)); + + /* Attach to IRQ */ + drv_data->irq = pdev->irq; + status = request_irq(drv_data->irq, ssp_int, IRQF_SHARED, + "mrst_spi3", drv_data); + if (status < 0) { + dev_err(&pdev->dev, "can not get IRQ\n"); + goto err_free_slave5; + } + + drv_data->int_cr1 = SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE; + drv_data->dma_cr1 = SSCR1_TSRE | SSCR1_RSRE | SSCR1_TRAIL; + drv_data->clear_sr = SSSR_ROR | SSSR_TINT; + drv_data->mask_sr = SSSR_TINT | SSSR_RFS | SSSR_TFS | SSSR_ROR; + + tasklet_init(&drv_data->poll_transfer, + poll_transfer, (unsigned long)drv_data); + + /* Setup DMA if requested */ + + /* Load default SSP configuration */ + printk(KERN_INFO "SPI-Slave: setup default SSP configuration\n"); + write_SSCR0(0, drv_data->ioaddr); + write_SSCR1(SSCR1_RxTresh(4) | SSCR1_TxTresh(12), drv_data->ioaddr); + write_SSCR0(SSCR0_Motorola + | SSCR0_DataSize(8), + drv_data->ioaddr); + write_SSTO(0, drv_data->ioaddr); + write_SSPSP(0x02010007, drv_data->ioaddr); + + /* Register with the SPI framework */ + printk(KERN_INFO "SPI-Slave: register with SPI framework\n"); + + status = spi_register_slave(slave); + + if (status != 0) { + dev_err(&pdev->dev, "problem registering spi slave\n"); + goto err_free_slave6; + } + +#ifdef CONFIG_SPI_MRST_SLAVE_DMA + mrst_spi_dma_init(drv_data); +#endif + + pci_set_drvdata(pdev, drv_data); + + return status; + +err_free_slave6: + free_irq(drv_data->irq, drv_data); +err_free_slave5: + iounmap(drv_data->I2C_ioaddr); +err_free_slave4: + iounmap(drv_data->IPC_ioaddr); +err_free_slave3: + iounmap(drv_data->ioaddr); +err_free_slave2: + pci_release_region(pdev, pci_bar); +err_free_slave1: + spi_slave_put(slave); +err_free_slave0: + pci_disable_device(pdev); + + return status; +} + +static void __devexit mrst_spi_remove(struct pci_dev *pdev) +{ + struct driver_data *drv_data = pci_get_drvdata(pdev); + + if (!drv_data) + return; + + pci_set_drvdata(pdev, NULL); + +#ifdef CONFIG_SPI_MRST_SLAVE_DMA + mrst_spi_dma_exit(drv_data); + pci_dev_put(drv_data->dmac1); +#endif + + /* Release IRQ */ + free_irq(drv_data->irq, drv_data); + + iounmap(drv_data->ioaddr); + iounmap(drv_data->I2C_ioaddr); + iounmap(drv_data->IPC_ioaddr); + + pci_release_region(pdev, 0); + + /* disconnect from the SPI framework */ + spi_unregister_slave(drv_data->slave); + + pci_disable_device(pdev); + + return; +} + +#ifdef CONFIG_PM + +static int mrst_spi_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct driver_data *drv_data = pci_get_drvdata(pdev); + printk(KERN_ERR "spi-slave: suspend\n"); + + tasklet_disable(&drv_data->poll_transfer); + + return 0; +} + +static int mrst_spi_resume(struct pci_dev *pdev) +{ + struct driver_data *drv_data = pci_get_drvdata(pdev); + printk(KERN_ERR "spi-slave: resume\n"); + + tasklet_enable(&drv_data->poll_transfer); + + return 0; +} +#else +#define mrst_spi_suspend NULL +#define mrst_spi_resume NULL +#endif /* CONFIG_PM */ + + +static const struct pci_device_id pci_ids[] __devinitdata = { + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = 0x0815, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + {}, +}; + +static struct pci_driver mrst_spi_slave_driver = { + .name = DRIVER_NAME, + .id_table = pci_ids, + .probe = mrst_spi_probe, + .remove = __devexit_p(mrst_spi_remove), + .suspend = mrst_spi_suspend, + .resume = mrst_spi_resume, +}; + +static int __init mrst_spi_init(void) +{ + return pci_register_driver(&mrst_spi_slave_driver); +} + +late_initcall_sync(mrst_spi_init); + +static void __exit mrst_spi_exit(void) +{ + pci_unregister_driver(&mrst_spi_slave_driver); +} +module_exit(mrst_spi_exit); diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index b76f246..f58f8c3 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -27,10 +27,16 @@ #include -/* SPI bustype and spi_master class are registered after board init code - * provides the SPI device tables, ensuring that both are present by the - * time controller driver registration causes spi_devices to "enumerate". - */ +/* SPI bustype, spi_master and spi_slave class are registered after board +* init code provides the SPI device tables, ensuring that both are present +* by the time controller driver registration causes spi_devices +* to "enumerate". +*/ + +/* SPI Slave Support is added for new spi slave devices: It uses common APIs, +* apart from few new APIs and a spi_slave structure. +*/ + static void spidev_release(struct device *dev) { struct spi_device *spi = to_spi_device(dev); @@ -43,11 +49,22 @@ static void spidev_release(struct device *dev) kfree(dev); } +static void spidev_slave_release(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + + /* spi slave may cleanup */ + if (spi->slave->cleanup) + spi->slave->cleanup(spi); + + spi_slave_put(spi->slave); + kfree(dev); +} + static ssize_t modalias_show(struct device *dev, struct device_attribute *a, char *buf) { const struct spi_device *spi = to_spi_device(dev); - return sprintf(buf, "%s\n", spi->modalias); } @@ -177,10 +194,13 @@ int spi_register_driver(struct spi_driver *sdrv) sdrv->driver.bus = &spi_bus_type; if (sdrv->probe) sdrv->driver.probe = spi_drv_probe; + if (sdrv->remove) sdrv->driver.remove = spi_drv_remove; + if (sdrv->shutdown) sdrv->driver.shutdown = spi_drv_shutdown; + return driver_register(&sdrv->driver); } EXPORT_SYMBOL_GPL(spi_register_driver); @@ -201,6 +221,7 @@ struct boardinfo { static LIST_HEAD(board_list); static DEFINE_MUTEX(board_lock); +static DEFINE_MUTEX(slave_board_lock); /** * spi_alloc_device - Allocate a new SPI device @@ -221,28 +242,70 @@ static DEFINE_MUTEX(board_lock); */ struct spi_device *spi_alloc_device(struct spi_master *master) { - struct spi_device *spi; + struct spi_device *spi_m_dev; struct device *dev = master->dev.parent; if (!spi_master_get(master)) return NULL; - spi = kzalloc(sizeof *spi, GFP_KERNEL); - if (!spi) { + spi_m_dev = kzalloc(sizeof *spi_m_dev, GFP_KERNEL); + if (!spi_m_dev) { dev_err(dev, "cannot alloc spi_device\n"); spi_master_put(master); return NULL; } - spi->master = master; - spi->dev.parent = dev; - spi->dev.bus = &spi_bus_type; - spi->dev.release = spidev_release; - device_initialize(&spi->dev); - return spi; + spi_m_dev->master = master; + spi_m_dev->using_slave = 0; + spi_m_dev->dev.parent = dev; + spi_m_dev->dev.bus = &spi_bus_type; + spi_m_dev->dev.release = spidev_release; + device_initialize(&spi_m_dev->dev); + return spi_m_dev; } EXPORT_SYMBOL_GPL(spi_alloc_device); +/* +* spi_alloc_slave_device - Allocate a new SPI device +* @slave: Controller to which device is connected +* Context: can sleep +* +* Allows a driver to allocate and initialize a spi_device without +* registering it immediately. This allows a driver to directly +* fill the spi_device with device parameters before calling +* spi_add_slave_device() on it. +* +* Caller is responsible to call spi_add_slave_device() on the returned +* spi_device structure to add it to the SPI slave. If the caller +* needs to discard the spi_device without adding it, then it should +* call spi_dev_slave_put() on it. +* Returns a pointer to the new device, or NULL. +*/ +struct spi_device *spi_alloc_slave_device(struct spi_slave *slave) +{ + struct spi_device *spi_s; + struct device *dev = slave->dev.parent; + + if (!spi_slave_get(slave)) + return NULL; + + spi_s = kzalloc(sizeof *spi_s, GFP_KERNEL); + if (!spi_s) { + dev_err(dev, "cannot alloc spi_slave_device\n"); + spi_slave_put(slave); + return NULL; + } + + spi_s->slave = slave; + spi_s->using_slave = 9; + spi_s->dev.parent = dev; + spi_s->dev.bus = &spi_bus_type; + spi_s->dev.release = spidev_slave_release; + device_initialize(&spi_s->dev); + return spi_s; +} +EXPORT_SYMBOL_GPL(spi_alloc_slave_device); + /** * spi_add_device - Add spi_device allocated with spi_alloc_device * @spi: spi_device to register @@ -301,6 +364,7 @@ int spi_add_device(struct spi_device *spi) if (status < 0) dev_err(dev, "can't %s %s, status %d\n", "add", dev_name(&spi->dev), status); + else dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev)); @@ -311,6 +375,74 @@ done: EXPORT_SYMBOL_GPL(spi_add_device); /** +* spi_add_slave_device - Add spi_device allocated with spi_alloc_slave_device +* @spi: spi_device to register +* +* Companion function to spi_alloc_slave_device. Devices allocated with +* spi_alloc_slave_device can be added onto the spi bus with this function. +* +* Returns 0 on success; negative errno on failure +*/ +int spi_add_slave_device(struct spi_device *spi) +{ + static DEFINE_MUTEX(spi_slave_add_lock); + struct device *dev = spi->slave->dev.parent; + int status; + + /* Chipselects are numbered 0..max; validate. */ + if (spi->chip_select >= spi->slave->num_chipselect) { + dev_err(dev, "cs%d >= max %d\n", + spi->chip_select, + spi->slave->num_chipselect); + return -EINVAL; + } + + /* Set the bus ID string */ + dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->slave->dev), + spi->chip_select); + + + /* We need to make sure there's no other device with this + * chipselect **BEFORE** we call setup(), else we'll trash + * its configuration. Lock against concurrent add() calls. + */ + mutex_lock(&spi_slave_add_lock); + + if (bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev)) + != NULL) { + dev_err(dev, "chipselect %d already in use\n", + spi->chip_select); + status = -EBUSY; + goto done; + } + + /* Drivers may modify this initial i/o setup, but will + * normally rely on the device being setup. Devices + * using SPI_CS_HIGH can't coexist well otherwise... + */ + status = spi->slave->setup(spi); + if (status < 0) { + dev_err(dev, "can't %s %s, status %d\n", + "setup", dev_name(&spi->dev), status); + goto done; + } + + /* Device may be bound to an active driver when this returns */ + status = device_add(&spi->dev); + if (status < 0) + dev_err(dev, "can't %s %s, status %d\n", + "add", dev_name(&spi->dev), status); + else + dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev)); + +done: + mutex_unlock(&spi_slave_add_lock); + return status; +} +EXPORT_SYMBOL_GPL(spi_add_slave_device); + + +/** * spi_new_device - instantiate one new SPI device * @master: Controller to which device is connected * @chip: Describes the SPI device @@ -341,6 +473,8 @@ struct spi_device *spi_new_device(struct spi_master *master, if (!proxy) return NULL; + + WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias)); proxy->chip_select = chip->chip_select; @@ -363,6 +497,54 @@ struct spi_device *spi_new_device(struct spi_master *master, EXPORT_SYMBOL_GPL(spi_new_device); /** +* spi_slave_new_device - instantiate one new SPI device +* @slave: Controller to which device is connected +* @chip: Describes the SPI device +* Context: can sleep +* +* On typical mainboards, this is purely internal; and it's not needed +* after board init creates the hard-wired devices. Some development +* platforms may not be able to use spi_register_board_info though, and +* this is exported so that for example a USB or parport based adapter +* driver could add devices (which it would learn about out-of-band). +* +* Returns the new device, or NULL. +*/ +struct spi_device *spi_slave_new_device(struct spi_slave *slave, + struct spi_board_info *chip) +{ + struct spi_device *proxy_slave; + int status; + + proxy_slave = spi_alloc_slave_device(slave); + + if (!proxy_slave) + return NULL; + + WARN_ON(strlen(chip->modalias) >= sizeof(proxy_slave->modalias)); + + proxy_slave->chip_select = chip->chip_select; + proxy_slave->max_speed_hz = chip->max_speed_hz; + proxy_slave->mode = chip->mode; + proxy_slave->irq = chip->irq; + strlcpy(proxy_slave->modalias, chip->modalias, + sizeof(proxy_slave->modalias)); + proxy_slave->dev.platform_data = (void *) chip->platform_data; + proxy_slave->controller_data = chip->controller_data; + proxy_slave->controller_state = NULL; + + status = spi_add_slave_device(proxy_slave); + if (status < 0) { + spi_dev_put(proxy_slave); + return NULL; + } + + return proxy_slave; +} +EXPORT_SYMBOL_GPL(spi_slave_new_device); + + +/** * spi_register_board_info - register SPI devices for a given board * @info: array of chip descriptors * @n: how many descriptors are provided @@ -389,6 +571,7 @@ spi_register_board_info(struct spi_board_info const *info, unsigned n) bi = kmalloc(sizeof(*bi) + n * sizeof *info, GFP_KERNEL); if (!bi) return -ENOMEM; + bi->n_board_info = n; memcpy(bi->board_info, info, n * sizeof *info); @@ -398,6 +581,7 @@ spi_register_board_info(struct spi_board_info const *info, unsigned n) return 0; } + /* FIXME someone should add support for a __setup("spi", ...) that * creates board info from kernel command lines */ @@ -423,6 +607,28 @@ static void scan_boardinfo(struct spi_master *master) mutex_unlock(&board_lock); } +static void spi_slave_scan_boardinfo(struct spi_slave *slave) +{ + struct boardinfo *bi; + + mutex_lock(&slave_board_lock); + list_for_each_entry(bi, &board_list, list) { + struct spi_board_info *chip = bi->board_info; + unsigned n; + + for (n = bi->n_board_info; n > 0; n--, chip++) { + if (chip->bus_num != slave->bus_num) + continue; + /* NOTE: this relies on spi_new_device to + * issue diagnostics when given bogus inputs + */ + (void) spi_slave_new_device(slave, chip); + + } + } + mutex_unlock(&slave_board_lock); +} + /*-------------------------------------------------------------------------*/ static void spi_master_release(struct device *dev) @@ -439,6 +645,19 @@ static struct class spi_master_class = { .dev_release = spi_master_release, }; +static void spi_slave_release(struct device *dev) +{ + struct spi_slave *slave; + + slave = container_of(dev, struct spi_slave, dev); + kfree(slave); +} + +static struct class spi_slave_class = { + .name = "spi_slave", + .owner = THIS_MODULE, + .dev_release = spi_slave_release, +}; /** * spi_alloc_master - allocate SPI master controller @@ -480,6 +699,47 @@ struct spi_master *spi_alloc_master(struct device *dev, unsigned size) EXPORT_SYMBOL_GPL(spi_alloc_master); /** +* spi_alloc_slave - allocate SPI slave controller +* @dev: the controller, possibly using the platform_bus +* @size: how much zeroed driver-private data to allocate; the pointer to this +* memory is in the driver_data field of the returned device, +* accessible with spi_slave_get_devdata(). +* Context: can sleep +* +* This call is used only by SPI master controller drivers, which are the +* only ones directly touching chip registers. It's how they allocate +* an spi_master structure, prior to calling spi_register_slave(). +* +* This must be called from context that can sleep. It returns the SPI +* master structure on success, else NULL. +* +* The caller is responsible for assigning the bus number and initializing +* the master's methods before calling spi_register_slave(); and (after errors +* adding the device) calling spi_slave_put() to prevent a memory leak. +*/ +struct spi_slave *spi_alloc_slave(struct device *dev, unsigned size) +{ + struct spi_slave *slave; + + if (!dev) + return NULL; + + slave = kzalloc(size + sizeof *slave, GFP_KERNEL); + if (!slave) + return NULL; + + device_initialize(&slave->dev); + slave->dev.class = &spi_slave_class; + slave->dev.parent = get_device(dev); + spi_slave_set_devdata(slave, &slave[1]); + + return slave; +} +EXPORT_SYMBOL_GPL(spi_alloc_slave); + + + +/** * spi_register_master - register SPI master controller * @master: initialized master, originally from spi_alloc_master() * Context: can sleep @@ -531,7 +791,8 @@ int spi_register_master(struct spi_master *master) status = device_add(&master->dev); if (status < 0) goto done; - dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev), + + dev_dbg(dev, "spi_register_master() : %s%s\n", dev_name(&master->dev), dynamic ? " (dynamic)" : ""); /* populate children from any spi device tables */ @@ -542,6 +803,71 @@ done: } EXPORT_SYMBOL_GPL(spi_register_master); +/** +* spi_register_slave - register SPI slave controller +* @master: initialized master, originally from spi_alloc_slave() +* Context: can sleep +* +* SPI slave controllers connect to their drivers using some non-SPI bus, +* such as the platform bus. The final stage of probe() in that code +* includes calling spi_register_slave() to hook up to this SPI bus glue. +* +* SPI controllers use board specific (often SOC specific) bus numbers, +* and board-specific addressing for SPI devices combines those numbers +* with chip select numbers. Since SPI does not directly support dynamic +* device identification, boards need configuration tables telling which +* chip is at which address. +* +* This must be called from context that can sleep. It returns zero on +* success, else a negative error code (dropping the slave's refcount). +* After a successful return, the caller is responsible for calling +* spi_unregister_slave(). +*/ +int spi_register_slave(struct spi_slave *slave) +{ + static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1); + struct device *dev = slave->dev.parent; + int status = -ENODEV; + int dynamic = 0; + + if (!dev) + return -ENODEV; + + /* even if it's just one always-selected device, there must + * be at least one chipselect + */ + if (slave->num_chipselect == 0) + return -EINVAL; + + /* convention: dynamically assigned bus IDs count down from the max */ + if (slave->bus_num < 0) { + /* FIXME switch to an IDR based scheme, something like + * I2C now uses, so we can't run out of "dynamic" IDs + */ + slave->bus_num = atomic_dec_return(&dyn_bus_id); + dynamic = 1; + } + + /* register the device, then userspace will see it. + * registration fails if the bus ID is in use. + */ + dev_set_name(&slave->dev, "spi%u", slave->bus_num); + status = device_add(&slave->dev); + if (status < 0) + goto done; + + dev_dbg(dev, "registered slave %s%s\n", dev_name(&slave->dev), + dynamic ? " (dynamic)" : ""); + + /* populate children from any spi device tables */ + spi_slave_scan_boardinfo(slave); + status = 0; +done: + return status; +} +EXPORT_SYMBOL_GPL(spi_register_slave); + + static int __unregister(struct device *dev, void *master_dev) { @@ -571,6 +897,27 @@ void spi_unregister_master(struct spi_master *master) } EXPORT_SYMBOL_GPL(spi_unregister_master); +/** +* spi_unregister_slave - unregister SPI slave controller +* @master: the slave being unregistered +* Context: can sleep +* +* This call is used only by SPI slave controller drivers, which are the +* only ones directly touching chip registers. +* +* This must be called from context that can sleep. +*/ +void spi_unregister_slave(struct spi_slave *slave) +{ + int dummy; + + dummy = device_for_each_child(slave->dev.parent, &slave->dev, + __unregister); + device_unregister(&slave->dev); +} +EXPORT_SYMBOL_GPL(spi_unregister_slave); + + static int __spi_master_match(struct device *dev, void *data) { struct spi_master *m; @@ -718,7 +1065,12 @@ int spi_async(struct spi_device *spi, struct spi_message *message) message->spi = spi; message->status = -EINPROGRESS; - return master->transfer(spi, message); + + /* TODO: ugly*/ + if (spi->using_slave == 9) + return spi->slave->transfer(spi, message); /* Slave */ + else + return spi->master->transfer(spi, message); /* Master */ } EXPORT_SYMBOL_GPL(spi_async); @@ -773,6 +1125,18 @@ int spi_sync(struct spi_device *spi, struct spi_message *message) } EXPORT_SYMBOL_GPL(spi_sync); +/* spi_transfer_async - Wraper function to allow spi_async to expose to +* user protocol drivers for modem handshaking +*/ + +int spi_transfer_async(struct spi_device *spi, struct spi_message *message) +{ + int status; + status = spi_async(spi, message); + return status; +} +EXPORT_SYMBOL_GPL(spi_transfer_async); + /* portable code must never pass more than 32 bytes */ #define SPI_BUFSIZ max(32,SMP_CACHE_BYTES) @@ -871,6 +1235,12 @@ static int __init spi_init(void) status = class_register(&spi_master_class); if (status < 0) goto err2; + + status = class_register(&spi_slave_class); + + if (status < 0) + goto err2; + return 0; err2: @@ -890,4 +1260,3 @@ err0: * include needing to have boardinfo data structures be much more public. */ postcore_initcall(spi_init); - diff --git a/include/linux/spi/mrst_spi_slave.h b/include/linux/spi/mrst_spi_slave.h new file mode 100644 index 0000000..4d73f0e --- /dev/null +++ b/include/linux/spi/mrst_spi_slave.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) Intel 2009 + * Ken Mills + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ +#ifndef MRST_SSP_H_ +#define MRST_SSP_H_ + + +/* + * Langwell SSP serial port register definitions + */ + +#define SSCR0_DSS (0x0000000f) /* Data Size Select (mask) */ +#define SSCR0_DataSize(x) ((x) - 1) /* Data Size Select [4..16] */ +#define SSCR0_FRF (0x00000030) /* FRame Format (mask) */ +#define SSCR0_Motorola (0x0 << 4) /* Motorola's SPI mode */ +#define SSCR0_ECS (1 << 6) /* External clock select */ +#define SSCR0_SSE (1 << 7) /* Synchronous Serial Port Enable */ + + +#define SSCR0_SCR (0x000fff00) /* Serial Clock Rate (mask) */ +#define SSCR0_SerClkDiv(x) (((x) - 1) << 8) /* Divisor [1..4096] */ +#define SSCR0_EDSS (1 << 20) /* Extended data size select */ +#define SSCR0_NCS (1 << 21) /* Network clock select */ +#define SSCR0_RIM (1 << 22) /* Receive FIFO overrrun int mask */ +#define SSCR0_TUM (1 << 23) /* Transmit FIFO underrun int mask */ +#define SSCR0_FRDC (0x07000000) /* Frame rate divider control (mask) */ +#define SSCR0_SlotsPerFrm(x) (((x) - 1) << 24) /* Time slots per frame */ +#define SSCR0_ADC (1 << 30) /* Audio clock select */ +#define SSCR0_MOD (1 << 31) /* Mode (normal or network) */ + + +#define SSCR1_RIE (1 << 0) /* Receive FIFO Interrupt Enable */ +#define SSCR1_TIE (1 << 1) /* Transmit FIFO Interrupt Enable */ +#define SSCR1_LBM (1 << 2) /* Loop-Back Mode */ +#define SSCR1_SPO (1 << 3) /* SSPSCLK polarity setting */ +#define SSCR1_SPH (1 << 4) /* Motorola SPI SSPSCLK phase setting */ +#define SSCR1_MWDS (1 << 5) /* Microwire Transmit Data Size */ +#define SSCR1_TFT (0x000003c0) /* Transmit FIFO Threshold (mask) */ +#define SSCR1_TxTresh(x) (((x) - 1) << 6) /* level [1..16] */ +#define SSCR1_RFT (0x00003c00) /* Receive FIFO Threshold (mask) */ +#define SSCR1_RxTresh(x) (((x) - 1) << 10) /* level [1..16] */ + +#define SSSR_TNF (1 << 2) /* Transmit FIFO Not Full */ +#define SSSR_RNE (1 << 3) /* Receive FIFO Not Empty */ +#define SSSR_BSY (1 << 4) /* SSP Busy */ +#define SSSR_TFS (1 << 5) /* Transmit FIFO Service Request */ +#define SSSR_RFS (1 << 6) /* Receive FIFO Service Request */ +#define SSSR_ROR (1 << 7) /* Receive FIFO Overrun */ + +#define SSCR0_TIM (1 << 23) /* Transmit FIFO Under Run Int Mask */ +#define SSCR0_RIM (1 << 22) /* Receive FIFO Over Run int Mask */ +#define SSCR0_NCS (1 << 21) /* Network Clock Select */ +#define SSCR0_EDSS (1 << 20) /* Extended Data Size Select */ + +#define SSCR0_TISSP (1 << 4) /* TI Sync Serial Protocol */ +#define SSCR0_PSP (3 << 4) /* PSP - Programmable Serial Protocol */ +#define SSCR1_TTELP (1 << 31) /* TXD Tristate Enable Last Phase */ +#define SSCR1_TTE (1 << 30) /* TXD Tristate Enable */ +#define SSCR1_EBCEI (1 << 29) /* Enable Bit Count Error interrupt */ +#define SSCR1_SCFR (1 << 28) /* Slave Clock free Running */ +#define SSCR1_ECRA (1 << 27) /* Enable Clock Request A */ +#define SSCR1_ECRB (1 << 26) /* Enable Clock request B */ +#define SSCR1_SCLKDIR (1 << 25) /* Serial Bit Rate Clock Direction */ +#define SSCR1_SFRMDIR (1 << 24) /* Frame Direction */ +#define SSCR1_RWOT (1 << 23) /* Receive Without Transmit */ +#define SSCR1_TRAIL (1 << 22) /* Trailing Byte */ +#define SSCR1_TSRE (1 << 21) /* Transmit Service Request Enable */ +#define SSCR1_RSRE (1 << 20) /* Receive Service Request Enable */ +#define SSCR1_TINTE (1 << 19) /* Receiver Time-out Interrupt enable */ +#define SSCR1_PINTE (1 << 18) /* Trailing Byte Interupt Enable */ +#define SSCR1_STRF (1 << 15) /* Select FIFO or EFWR */ +#define SSCR1_EFWR (1 << 14) /* Enable FIFO Write/Read */ + +#define SSSR_BCE (1 << 23) /* Bit Count Error */ +#define SSSR_CSS (1 << 22) /* Clock Synchronisation Status */ +#define SSSR_TUR (1 << 21) /* Transmit FIFO Under Run */ +#define SSSR_EOC (1 << 20) /* End Of Chain */ +#define SSSR_TINT (1 << 19) /* Receiver Time-out Interrupt */ +#define SSSR_PINT (1 << 18) /* Peripheral Trailing Byte Interrupt */ + +#define SSPSP_FSRT (1 << 25) /* Frame Sync Relative Timing */ +#define SSPSP_DMYSTOP(x) ((x) << 23) /* Dummy Stop */ +#define SSPSP_SFRMWDTH(x) ((x) << 16) /* Serial Frame Width */ +#define SSPSP_SFRMDLY(x) ((x) << 9) /* Serial Frame Delay */ +#define SSPSP_DMYSTRT(x) ((x) << 7) /* Dummy Start */ +#define SSPSP_STRTDLY(x) ((x) << 4) /* Start Delay */ +#define SSPSP_ETDS (1 << 3) /* End of Transfer data State */ +#define SSPSP_SFRMP (1 << 2) /* Serial Frame Polarity */ +#define SSPSP_SCMODE(x) ((x) << 0) /* Serial Bit Rate Clock Mode */ + +/* spi_board_info.controller_data for SPI slave devices, + * copied to spi_device.platform_data ... mostly for dma tuning + */ +struct mrst_spi_chip { + u8 tx_threshold; + u8 rx_threshold; + u8 dma_burst_size; + u32 timeout; + u8 enable_loopback; + u16 extra_data[5]; +}; + + +#define SPI_DIB_NAME_LEN 16 +#define SPI_DIB_SPEC_INFO_LEN 10 + +struct spi_dib_header { + u32 signature; + u32 length; + u8 rev; + u8 checksum; + u8 dib[0]; +} __attribute__((packed)); + +struct spi_dib { + u16 host_num; + u16 cs; + u16 irq; + char name[SPI_DIB_NAME_LEN]; + u8 dev_data[SPI_DIB_SPEC_INFO_LEN]; +} __attribute__((packed)); + +#endif /*MRST_SSP_H_*/ diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 97b60b3..87b4d12 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -23,15 +23,19 @@ #include /* - * INTERFACES between SPI master-side drivers and SPI infrastructure. - * (There's no SPI slave support for Linux yet...) + * INTERFACES between SPI Master/Slave side drivers and + * SPI infrastructure. + * SPI Slave Support added : It uses few new APIs and + * a new spi_slave struct */ extern struct bus_type spi_bus_type; /** * struct spi_device - Master side proxy for an SPI slave device * @dev: Driver model representation of the device. - * @master: SPI controller used with the device. + * @master: SPI Master controller used with the device. + * @slave: SPI Slave Controller used with the device + * @using_slave: SPI Slave Flag used by spi_async() * @max_speed_hz: Maximum clock rate to be used with this chip * (on this board); may be changed by the device's driver. * The spi_transfer.speed_hz can override this for each transfer. @@ -68,6 +72,8 @@ extern struct bus_type spi_bus_type; struct spi_device { struct device dev; struct spi_master *master; + struct spi_slave *slave; + u8 using_slave; u32 max_speed_hz; u8 chip_select; u8 mode; @@ -143,7 +149,6 @@ static inline void *spi_get_drvdata(struct spi_device *spi) struct spi_message; - /** * struct spi_driver - Host side "protocol" driver * @id_table: List of SPI devices supported by this driver @@ -295,16 +300,56 @@ struct spi_master { void (*cleanup)(struct spi_device *spi); }; +/** + * struct spi_slave - interface to SPI Slave Controller + * @dev: device interface to this driver + * @bus_num: board-specific (and often SOC-specific) identifier for a + * given SPI controller. + * @num_chipselect: chipselects are used to distinguish individual + * SPI slaves, and are numbered from zero to num_chipselects. + * each slave has a chipselect signal, but it's common that not + * every chipselect is connected to a slave. + * @setup: updates the device mode and clocking records used by a + * device's SPI controller; protocol code may call this. This + * must fail if an unrecognized or unsupported mode is requested. + * It's always safe to call this unless transfers are pending on + * the device whose settings are being modified. + * @transfer: adds a message to the controller's transfer queue. + * @cleanup: frees controller-specific state + */ +struct spi_slave { + struct device dev; + s16 bus_num; + u16 num_chipselect; + + int (*setup)(struct spi_device *spi); + + int (*transfer)(struct spi_device *spi, + struct spi_message *mesg); + + void (*cleanup)(struct spi_device *spi); +}; + static inline void *spi_master_get_devdata(struct spi_master *master) { return dev_get_drvdata(&master->dev); } +static inline void *spi_slave_get_devdata(struct spi_slave *slave) +{ + return dev_get_drvdata(&slave->dev); +} + static inline void spi_master_set_devdata(struct spi_master *master, void *data) { dev_set_drvdata(&master->dev, data); } +static inline void spi_slave_set_devdata(struct spi_slave *slave, void *data) +{ + dev_set_drvdata(&slave->dev, data); +} + static inline struct spi_master *spi_master_get(struct spi_master *master) { if (!master || !get_device(&master->dev)) @@ -312,20 +357,42 @@ static inline struct spi_master *spi_master_get(struct spi_master *master) return master; } +static inline struct spi_slave *spi_slave_get(struct spi_slave *slave) +{ + if (!slave || !get_device(&slave->dev)) + return NULL; + return slave; +} + static inline void spi_master_put(struct spi_master *master) { if (master) put_device(&master->dev); } +static inline void spi_slave_put(struct spi_slave *slave) +{ + if (slave) + put_device(&slave->dev); +} + /* the spi driver core manages memory for the spi_master classdev */ extern struct spi_master * spi_alloc_master(struct device *host, unsigned size); +extern struct spi_slave * +spi_alloc_slave(struct device *host, unsigned size); + + extern int spi_register_master(struct spi_master *master); + +extern int spi_register_slave(struct spi_slave *slave); + extern void spi_unregister_master(struct spi_master *master); +extern void spi_unregister_slave(struct spi_slave *slave); + extern struct spi_master *spi_busnum_to_master(u16 busnum); /*---------------------------------------------------------------------------*/ @@ -551,6 +618,18 @@ extern int spi_async(struct spi_device *spi, struct spi_message *message); extern int spi_sync(struct spi_device *spi, struct spi_message *message); +static inline int +spi_slave_setup(struct spi_device *spi) +{ + return spi->slave->setup(spi); +} + + +/* spi_transfer_async() exposes spi_async() functionality */ +extern int spi_transfer_async(struct spi_device *spi, + struct spi_message *message); + + /** * spi_write - SPI synchronous write * @spi: device to which data will be written @@ -759,12 +838,23 @@ spi_register_board_info(struct spi_board_info const *info, unsigned n) extern struct spi_device * spi_alloc_device(struct spi_master *master); +extern struct spi_device * +spi_alloc_slave_device(struct spi_slave *slave); + extern int spi_add_device(struct spi_device *spi); +extern int +spi_add_slave_device(struct spi_device *spi); + + extern struct spi_device * spi_new_device(struct spi_master *, struct spi_board_info *); +extern struct spi_device * +spi_slave_new_device(struct spi_slave *, struct spi_board_info *); + + static inline void spi_unregister_device(struct spi_device *spi) { -- 1.6.2.5