Second try sending Intel SSP slave controller driver. Signed-off-by: Ken Mills <[email protected]> --- drivers/spi/Kconfig | 6 + drivers/spi/Makefile | 1 + drivers/spi/mrst_spi_slave.c | 1263 ++++++++++++++++++++++++++++++++++++ include/linux/spi/mrst_spi_slave.h | 143 ++++ 4 files changed, 1413 insertions(+), 0 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 83a185d..f34528c 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -256,4 +256,10 @@ 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 + endif # SPI diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 5d04519..8475072 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -38,6 +38,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..429f6ea --- /dev/null +++ b/drivers/spi/mrst_spi_slave.c @@ -0,0 +1,1263 @@ +/* + * mrst_spi_slave.c - Moorestown SPI slave controller driver + * based on pxa2xx_spi.c + * + * Copyright (C) Intel 2009 + * Ken Mills <[email protected]> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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: + * + * 1. Cuurently supports interrupt programmed I/O and + * non-interrupt polled transfers. + */ + +#include <linux/delay.h> +#include <linux/highmem.h> +#include <linux/pci.h> + +#ifdef CONFIG_SPI_MRST_SLAVE_DMA +#include <linux/dma-mapping.h> +#include <linux/lnw_dma.h> +#endif + +#include <linux/spi/spi.h> +#include <linux/spi/mrst_spi_slave.h> + +#define DRIVER_NAME "mrst_spi_slave" + +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; + struct pci_dev *dmac1; + + /* DMA setup stuff */ + int rx_channel; + int tx_channel; + u32 *null_dma_buf; + + /* 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; + u32 dma_width; + int cs_change; + 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 dma_inited; + int txdma_done; + int rxdma_done; +#endif +}; + +struct chip_data { + u32 cr0; + u32 cr1; + u32 psp; + u32 timeout; + u8 n_bytes; + u32 dma_width; + u32 dma_burst_size; + u32 threshold; + u32 dma_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 dma_transfer_complete(struct driver_data *drv_data) +{ + void *reg = drv_data->ioaddr; + + /* Stop SSP */ + write_SSSR(drv_data->clear_sr, reg); + write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg); + 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 mrst_dma_done(void *arg) +{ + u64 *param = arg; + struct driver_data *drv_data; + + drv_data = (struct driver_data *)(u32)(*param >> 32); + dma_transfer_complete(drv_data); + return; +} + +static void mrst_spi_dma_init(struct driver_data *drv_data) +{ + struct lnw_dma_slave *rxs, *txs; + dma_cap_mask_t mask; + int myparam = 100; /* Temp only */ + + drv_data->txchan = NULL; + drv_data->rxchan = NULL; + + /* 1. init rx channel */ + rxs = &drv_data->dmas_rx; + + rxs->dirn = DMA_FROM_DEVICE; + rxs->hs_mode = LNW_DMA_SW_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_16; + rxs->dst_msize = LNW_DMA_MSIZE_16; + rxs->rx_reg = (dma_addr_t)(drv_data->paddr + 0x10); + rxs->tx_reg = (dma_addr_t)0; + + rxs->callback = mrst_dma_done; + rxs->callback_param = &myparam; + + 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) { + printk(KERN_ERR "SPI-Slave: Could not get Rx channel\n"); + 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_SW_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_16; + txs->dst_msize = LNW_DMA_MSIZE_16; + txs->tx_reg = (dma_addr_t)(drv_data->paddr + 0x10); + txs->rx_reg = (dma_addr_t)0; + + txs->callback = mrst_dma_done; + txs->callback_param = &myparam; + + dma_cap_set(DMA_SLAVE, mask); + dma_cap_set(DMA_MEMCPY, mask); + + drv_data->txchan = dma_request_channel(mask, chan_filter, + &drv_data->txchan); + if (!drv_data->txchan) { + printk(KERN_ERR "SPI-Slave: Could not get Tx channel\n"); + goto free_rxchan; + } + drv_data->txchan->private = txs; + + /* set the dma done bit to 1 */ + drv_data->dma_inited = 1; + drv_data->txdma_done = 1; + drv_data->rxdma_done = 1; + + return; + +free_rxchan: + dma_release_channel(drv_data->rxchan); +err_exit: + printk(KERN_ERR "SPI-Slave: Could not get rx channel\n"); + return; +} + +static void mrst_spi_dma_exit(struct driver_data *drv_data) +{ + dma_release_channel(drv_data->txchan); + dma_release_channel(drv_data->rxchan); +} + +static void dma_transfer(struct driver_data *drv_data) +{ + struct dma_async_tx_descriptor *txdesc = NULL, *rxdesc = NULL; + struct dma_chan *txchan, *rxchan; + enum dma_ctrl_flags flag; + static u64 rx_param, tx_param; + + printk(KERN_ERR "SPI-SLAVE: entering dma-transfer\n"); + + 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->tx_dma) { + txdesc = txchan->device->device_prep_dma_memcpy(txchan, + drv_data->dmas_tx.tx_reg, drv_data->tx_dma, + drv_data->len, flag); + + txdesc->callback = mrst_dma_done; + tx_param = ((u64)(u32)drv_data << 32) + | (u32)(&drv_data->txdma_done); + txdesc->callback_param = &tx_param; + } + + /* 3. start the RX dma transfer */ + if (drv_data->rx_dma) { + rxdesc = rxchan->device->device_prep_dma_memcpy(rxchan, + drv_data->rx_dma, drv_data->dmas_rx.rx_reg, + drv_data->len, flag); + + rxdesc->callback = mrst_dma_done; + rx_param = ((u64)(u32)drv_data << 32) + | (u32)(&drv_data->rxdma_done); + rxdesc->callback_param = &rx_param; + } + + if (rxdesc) + rxdesc->tx_submit(rxdesc); + if (txdesc) + txdesc->tx_submit(txdesc); + + /* + txchan->device->device_issue_pending(txchan); + rxchan->device->device_issue_pending(rxchan); + */ +} +#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); + write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg); + + 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; + + /* Stop SSP */ + write_SSSR(drv_data->clear_sr, reg); + write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg); + 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; + + /* just return if this is not our interrupt */ + if (!(read_SSSR(reg) & drv_data->mask_sr)) + return IRQ_NONE; + + if (!drv_data->cur_msg) { + printk(KERN_ERR "SPI-Slave: bad message: %d\n", + (int)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; + + /* Handle for abort */ + + /* Check transfer length */ + if (transfer->len > 8191) { + dev_warn(&drv_data->pdev->dev, "SPI-SLAVE: transfer " + "length greater than 8191\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->dma_width = chip->dma_width; + 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; + drv_data->cs_change = transfer->cs_change; + + /* 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->dma_width = 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->dma_width = 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->dma_width = 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 + + if (chip->enable_dma) { + drv_data->dma_mapped = 0; + + if (drv_data->tx) + drv_data->tx_dma = (dma_addr_t)drv_data->tx; + + if (drv_data->rx) + drv_data->rx_dma = (dma_addr_t)drv_data->rx; + + if (drv_data->tx || drv_data->rx) + drv_data->dma_mapped = 1; + } +#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); + + /* see if we need to reload the config registers */ + + if ((read_SSCR0(reg) != cr0) + || (read_SSCR1(reg) & SSCR1_CHANGE_MASK) != + (cr1 & SSCR1_CHANGE_MASK)) { + + write_SSCR0(cr0 & ~SSCR0_SSE, reg); + write_SSPSP(0x02010007, reg); + write_SSTO(chip->timeout, reg); + write_SSCR1(cr1, reg); + 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(cr1, reg); + } + + /* transfer using DMA */ + if (drv_data->dma_mapped) { + cr1 = cr1 | drv_data->dma_cr1; + cr1 = cr1 & (~drv_data->int_cr1); + + write_SSCR1(cr1, reg); + +#ifdef CONFIG_SPI_MRST_SLAVE_DMA + 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 = 0; + chip->poll_mode = 1; + chip->timeout = 1000; + chip->threshold = SSCR1_RxTresh(1) | SSCR1_TxTresh(1); + chip->dma_burst_size = 0; + chip->dma_threshold = 0; + } + + /* + * 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->dma_width = 1; + chip->read = u8_reader; + chip->write = u8_writer; + } else if (spi->bits_per_word <= 16) { + chip->n_bytes = 2; + chip->dma_width = 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->dma_width = 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 */ + .dma_burst_size = 8, /* Byte wide transfers used so 8 byte bursts */ + .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); + printk(KERN_INFO "SPI-Slave: paddr = : %08x\n", (int)drv_data->paddr); + 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_TINTE; + 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 */ + drv_data->tx_channel = -1; + drv_data->rx_channel = -1; + + /* 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 + drv_data->dmac1 = pci_get_device(PCI_VENDOR_ID_INTEL, 0x0814, NULL); + 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 + + /* Disable the SSP at the peripheral and SOC level */ + write_SSCR0(0, drv_data->ioaddr); + + /* 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); + write_SSCR0(0, drv_data->ioaddr); + + 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) +{ + printk(KERN_ERR "SPI-Slave: mrst_spi_init()\n"); + 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/include/linux/spi/mrst_spi_slave.h b/include/linux/spi/mrst_spi_slave.h new file mode 100644 index 0000000..a5f4a90 --- /dev/null +++ b/include/linux/spi/mrst_spi_slave.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) Intel 2009 + * Ken Mills <[email protected]> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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_*/ -- 1.5.4.3 ------------------------------------------------------------------------------ Crystal Reports - New Free Runtime and 30 Day Trial Check out the new simplified licensing option that enables unlimited royalty-free distribution of the report engine for externally facing server and web deployment. http://p.sf.net/sfu/businessobjects _______________________________________________ spi-devel-general mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/spi-devel-general
