#include <linux/module.h>
#include <linux/delay.h>
#include <linux/device.h>
+#include <linux/slab.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/irq.h>
MODULE_DESCRIPTION(DRV_DESC);
MODULE_LICENSE("GPL");
-#define IS_DMA_ALIGNED(x) (((u32)(x)&0x07) == 0)
-
#define START_STATE ((void *)0)
#define RUNNING_STATE ((void *)1)
#define DONE_STATE ((void *)2)
#define QUEUE_RUNNING 0
#define QUEUE_STOPPED 1
+/* Value to send if no TX value is supplied */
+#define SPI_IDLE_TXVAL 0x0000
+
struct driver_data {
/* Driver model hookup */
struct platform_device *pdev;
u8 bits_per_word; /* 8 or 16 */
u8 cs_change_per_word;
u16 cs_chg_udelay; /* Some devices require > 255usec delay */
+ u32 cs_gpio;
+ u16 idle_tx_val;
void (*write) (struct driver_data *);
void (*read) (struct driver_data *);
void (*duplex) (struct driver_data *);
unsigned long limit = loops_per_jiffy << 1;
/* wait for stop and clear stat */
- while (!(read_STAT(drv_data) & BIT_STAT_SPIF) && limit--)
+ while (!(read_STAT(drv_data) & BIT_STAT_SPIF) && --limit)
cpu_relax();
write_STAT(drv_data, BIT_STAT_CLR);
/* Chip select operation functions for cs_change flag */
static void bfin_spi_cs_active(struct driver_data *drv_data, struct chip_data *chip)
{
- u16 flag = read_FLAG(drv_data);
+ if (likely(chip->chip_select_num)) {
+ u16 flag = read_FLAG(drv_data);
- flag |= chip->flag;
- flag &= ~(chip->flag << 8);
+ flag |= chip->flag;
+ flag &= ~(chip->flag << 8);
- write_FLAG(drv_data, flag);
+ write_FLAG(drv_data, flag);
+ } else {
+ gpio_set_value(chip->cs_gpio, 0);
+ }
}
static void bfin_spi_cs_deactive(struct driver_data *drv_data, struct chip_data *chip)
{
- u16 flag = read_FLAG(drv_data);
+ if (likely(chip->chip_select_num)) {
+ u16 flag = read_FLAG(drv_data);
- flag &= ~chip->flag;
- flag |= (chip->flag << 8);
+ flag &= ~chip->flag;
+ flag |= (chip->flag << 8);
- write_FLAG(drv_data, flag);
+ write_FLAG(drv_data, flag);
+ } else {
+ gpio_set_value(chip->cs_gpio, 1);
+ }
/* Move delay here for consistency */
if (chip->cs_chg_udelay)
bfin_spi_cs_active(drv_data, chip);
}
-/* used to kick off transfer in rx mode */
-static unsigned short bfin_spi_dummy_read(struct driver_data *drv_data)
+/* used to kick off transfer in rx mode and read unwanted RX data */
+static inline void bfin_spi_dummy_read(struct driver_data *drv_data)
{
- unsigned short tmp;
- tmp = read_RDBR(drv_data);
- return tmp;
+ (void) read_RDBR(drv_data);
}
static void bfin_spi_null_writer(struct driver_data *drv_data)
{
u8 n_bytes = drv_data->n_bytes;
+ u16 tx_val = drv_data->cur_chip->idle_tx_val;
+
+ /* clear RXS (we check for RXS inside the loop) */
+ bfin_spi_dummy_read(drv_data);
while (drv_data->tx < drv_data->tx_end) {
- write_TDBR(drv_data, 0);
- while ((read_STAT(drv_data) & BIT_STAT_TXS))
- cpu_relax();
+ write_TDBR(drv_data, tx_val);
drv_data->tx += n_bytes;
+ /* wait until transfer finished.
+ checking SPIF or TXS may not guarantee transfer completion */
+ while (!(read_STAT(drv_data) & BIT_STAT_RXS))
+ cpu_relax();
+ /* discard RX data and clear RXS */
+ bfin_spi_dummy_read(drv_data);
}
}
static void bfin_spi_null_reader(struct driver_data *drv_data)
{
u8 n_bytes = drv_data->n_bytes;
+ u16 tx_val = drv_data->cur_chip->idle_tx_val;
+
+ /* discard old RX data and clear RXS */
bfin_spi_dummy_read(drv_data);
while (drv_data->rx < drv_data->rx_end) {
+ write_TDBR(drv_data, tx_val);
+ drv_data->rx += n_bytes;
while (!(read_STAT(drv_data) & BIT_STAT_RXS))
cpu_relax();
bfin_spi_dummy_read(drv_data);
- drv_data->rx += n_bytes;
}
}
static void bfin_spi_u8_writer(struct driver_data *drv_data)
{
- dev_dbg(&drv_data->pdev->dev,
- "cr8-s is 0x%x\n", read_STAT(drv_data));
+ /* clear RXS (we check for RXS inside the loop) */
+ bfin_spi_dummy_read(drv_data);
while (drv_data->tx < drv_data->tx_end) {
- write_TDBR(drv_data, (*(u8 *) (drv_data->tx)));
- while (read_STAT(drv_data) & BIT_STAT_TXS)
+ write_TDBR(drv_data, (*(u8 *) (drv_data->tx++)));
+ /* wait until transfer finished.
+ checking SPIF or TXS may not guarantee transfer completion */
+ while (!(read_STAT(drv_data) & BIT_STAT_RXS))
cpu_relax();
- ++drv_data->tx;
+ /* discard RX data and clear RXS */
+ bfin_spi_dummy_read(drv_data);
}
-
- /* poll for SPI completion before return */
- while (!(read_STAT(drv_data) & BIT_STAT_SPIF))
- cpu_relax();
}
static void bfin_spi_u8_cs_chg_writer(struct driver_data *drv_data)
{
struct chip_data *chip = drv_data->cur_chip;
+ /* clear RXS (we check for RXS inside the loop) */
+ bfin_spi_dummy_read(drv_data);
+
while (drv_data->tx < drv_data->tx_end) {
bfin_spi_cs_active(drv_data, chip);
-
- write_TDBR(drv_data, (*(u8 *) (drv_data->tx)));
- while (read_STAT(drv_data) & BIT_STAT_TXS)
- cpu_relax();
- while (!(read_STAT(drv_data) & BIT_STAT_SPIF))
+ write_TDBR(drv_data, (*(u8 *) (drv_data->tx++)));
+ /* make sure transfer finished before deactiving CS */
+ while (!(read_STAT(drv_data) & BIT_STAT_RXS))
cpu_relax();
-
+ bfin_spi_dummy_read(drv_data);
bfin_spi_cs_deactive(drv_data, chip);
-
- ++drv_data->tx;
}
}
static void bfin_spi_u8_reader(struct driver_data *drv_data)
{
- dev_dbg(&drv_data->pdev->dev,
- "cr-8 is 0x%x\n", read_STAT(drv_data));
-
- /* poll for SPI completion before start */
- while (!(read_STAT(drv_data) & BIT_STAT_SPIF))
- cpu_relax();
-
- /* clear TDBR buffer before read(else it will be shifted out) */
- write_TDBR(drv_data, 0xFFFF);
+ u16 tx_val = drv_data->cur_chip->idle_tx_val;
+ /* discard old RX data and clear RXS */
bfin_spi_dummy_read(drv_data);
- while (drv_data->rx < drv_data->rx_end - 1) {
+ while (drv_data->rx < drv_data->rx_end) {
+ write_TDBR(drv_data, tx_val);
while (!(read_STAT(drv_data) & BIT_STAT_RXS))
cpu_relax();
- *(u8 *) (drv_data->rx) = read_RDBR(drv_data);
- ++drv_data->rx;
+ *(u8 *) (drv_data->rx++) = read_RDBR(drv_data);
}
-
- while (!(read_STAT(drv_data) & BIT_STAT_RXS))
- cpu_relax();
- *(u8 *) (drv_data->rx) = read_SHAW(drv_data);
- ++drv_data->rx;
}
static void bfin_spi_u8_cs_chg_reader(struct driver_data *drv_data)
{
struct chip_data *chip = drv_data->cur_chip;
+ u16 tx_val = chip->idle_tx_val;
+
+ /* discard old RX data and clear RXS */
+ bfin_spi_dummy_read(drv_data);
while (drv_data->rx < drv_data->rx_end) {
bfin_spi_cs_active(drv_data, chip);
- read_RDBR(drv_data); /* kick off */
-
+ write_TDBR(drv_data, tx_val);
while (!(read_STAT(drv_data) & BIT_STAT_RXS))
cpu_relax();
- while (!(read_STAT(drv_data) & BIT_STAT_SPIF))
- cpu_relax();
-
- *(u8 *) (drv_data->rx) = read_SHAW(drv_data);
+ *(u8 *) (drv_data->rx++) = read_RDBR(drv_data);
bfin_spi_cs_deactive(drv_data, chip);
-
- ++drv_data->rx;
}
}
static void bfin_spi_u8_duplex(struct driver_data *drv_data)
{
- /* in duplex mode, clk is triggered by writing of TDBR */
+ /* discard old RX data and clear RXS */
+ bfin_spi_dummy_read(drv_data);
+
while (drv_data->rx < drv_data->rx_end) {
- write_TDBR(drv_data, (*(u8 *) (drv_data->tx)));
- while (!(read_STAT(drv_data) & BIT_STAT_SPIF))
- cpu_relax();
+ write_TDBR(drv_data, (*(u8 *) (drv_data->tx++)));
while (!(read_STAT(drv_data) & BIT_STAT_RXS))
cpu_relax();
- *(u8 *) (drv_data->rx) = read_RDBR(drv_data);
- ++drv_data->rx;
- ++drv_data->tx;
+ *(u8 *) (drv_data->rx++) = read_RDBR(drv_data);
}
}
{
struct chip_data *chip = drv_data->cur_chip;
+ /* discard old RX data and clear RXS */
+ bfin_spi_dummy_read(drv_data);
+
while (drv_data->rx < drv_data->rx_end) {
bfin_spi_cs_active(drv_data, chip);
-
- write_TDBR(drv_data, (*(u8 *) (drv_data->tx)));
-
- while (!(read_STAT(drv_data) & BIT_STAT_SPIF))
- cpu_relax();
+ write_TDBR(drv_data, (*(u8 *) (drv_data->tx++)));
while (!(read_STAT(drv_data) & BIT_STAT_RXS))
cpu_relax();
- *(u8 *) (drv_data->rx) = read_RDBR(drv_data);
-
+ *(u8 *) (drv_data->rx++) = read_RDBR(drv_data);
bfin_spi_cs_deactive(drv_data, chip);
-
- ++drv_data->rx;
- ++drv_data->tx;
}
}
static void bfin_spi_u16_writer(struct driver_data *drv_data)
{
- dev_dbg(&drv_data->pdev->dev,
- "cr16 is 0x%x\n", read_STAT(drv_data));
+ /* clear RXS (we check for RXS inside the loop) */
+ bfin_spi_dummy_read(drv_data);
while (drv_data->tx < drv_data->tx_end) {
write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));
- while ((read_STAT(drv_data) & BIT_STAT_TXS))
- cpu_relax();
drv_data->tx += 2;
+ /* wait until transfer finished.
+ checking SPIF or TXS may not guarantee transfer completion */
+ while (!(read_STAT(drv_data) & BIT_STAT_RXS))
+ cpu_relax();
+ /* discard RX data and clear RXS */
+ bfin_spi_dummy_read(drv_data);
}
-
- /* poll for SPI completion before return */
- while (!(read_STAT(drv_data) & BIT_STAT_SPIF))
- cpu_relax();
}
static void bfin_spi_u16_cs_chg_writer(struct driver_data *drv_data)
{
struct chip_data *chip = drv_data->cur_chip;
+ /* clear RXS (we check for RXS inside the loop) */
+ bfin_spi_dummy_read(drv_data);
+
while (drv_data->tx < drv_data->tx_end) {
bfin_spi_cs_active(drv_data, chip);
-
write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));
- while ((read_STAT(drv_data) & BIT_STAT_TXS))
- cpu_relax();
- while (!(read_STAT(drv_data) & BIT_STAT_SPIF))
+ drv_data->tx += 2;
+ /* make sure transfer finished before deactiving CS */
+ while (!(read_STAT(drv_data) & BIT_STAT_RXS))
cpu_relax();
-
+ bfin_spi_dummy_read(drv_data);
bfin_spi_cs_deactive(drv_data, chip);
-
- drv_data->tx += 2;
}
}
static void bfin_spi_u16_reader(struct driver_data *drv_data)
{
- dev_dbg(&drv_data->pdev->dev,
- "cr-16 is 0x%x\n", read_STAT(drv_data));
-
- /* poll for SPI completion before start */
- while (!(read_STAT(drv_data) & BIT_STAT_SPIF))
- cpu_relax();
-
- /* clear TDBR buffer before read(else it will be shifted out) */
- write_TDBR(drv_data, 0xFFFF);
+ u16 tx_val = drv_data->cur_chip->idle_tx_val;
+ /* discard old RX data and clear RXS */
bfin_spi_dummy_read(drv_data);
- while (drv_data->rx < (drv_data->rx_end - 2)) {
+ while (drv_data->rx < drv_data->rx_end) {
+ write_TDBR(drv_data, tx_val);
while (!(read_STAT(drv_data) & BIT_STAT_RXS))
cpu_relax();
*(u16 *) (drv_data->rx) = read_RDBR(drv_data);
drv_data->rx += 2;
}
-
- while (!(read_STAT(drv_data) & BIT_STAT_RXS))
- cpu_relax();
- *(u16 *) (drv_data->rx) = read_SHAW(drv_data);
- drv_data->rx += 2;
}
static void bfin_spi_u16_cs_chg_reader(struct driver_data *drv_data)
{
struct chip_data *chip = drv_data->cur_chip;
+ u16 tx_val = chip->idle_tx_val;
- /* poll for SPI completion before start */
- while (!(read_STAT(drv_data) & BIT_STAT_SPIF))
- cpu_relax();
-
- /* clear TDBR buffer before read(else it will be shifted out) */
- write_TDBR(drv_data, 0xFFFF);
-
- bfin_spi_cs_active(drv_data, chip);
+ /* discard old RX data and clear RXS */
bfin_spi_dummy_read(drv_data);
- while (drv_data->rx < drv_data->rx_end - 2) {
- bfin_spi_cs_deactive(drv_data, chip);
-
+ while (drv_data->rx < drv_data->rx_end) {
+ bfin_spi_cs_active(drv_data, chip);
+ write_TDBR(drv_data, tx_val);
while (!(read_STAT(drv_data) & BIT_STAT_RXS))
cpu_relax();
- bfin_spi_cs_active(drv_data, chip);
*(u16 *) (drv_data->rx) = read_RDBR(drv_data);
drv_data->rx += 2;
+ bfin_spi_cs_deactive(drv_data, chip);
}
- bfin_spi_cs_deactive(drv_data, chip);
-
- while (!(read_STAT(drv_data) & BIT_STAT_RXS))
- cpu_relax();
- *(u16 *) (drv_data->rx) = read_SHAW(drv_data);
- drv_data->rx += 2;
}
static void bfin_spi_u16_duplex(struct driver_data *drv_data)
{
- /* in duplex mode, clk is triggered by writing of TDBR */
- while (drv_data->tx < drv_data->tx_end) {
+ /* discard old RX data and clear RXS */
+ bfin_spi_dummy_read(drv_data);
+
+ while (drv_data->rx < drv_data->rx_end) {
write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));
- while (!(read_STAT(drv_data) & BIT_STAT_SPIF))
- cpu_relax();
+ drv_data->tx += 2;
while (!(read_STAT(drv_data) & BIT_STAT_RXS))
cpu_relax();
*(u16 *) (drv_data->rx) = read_RDBR(drv_data);
drv_data->rx += 2;
- drv_data->tx += 2;
}
}
{
struct chip_data *chip = drv_data->cur_chip;
- while (drv_data->tx < drv_data->tx_end) {
- bfin_spi_cs_active(drv_data, chip);
+ /* discard old RX data and clear RXS */
+ bfin_spi_dummy_read(drv_data);
+ while (drv_data->rx < drv_data->rx_end) {
+ bfin_spi_cs_active(drv_data, chip);
write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));
- while (!(read_STAT(drv_data) & BIT_STAT_SPIF))
- cpu_relax();
+ drv_data->tx += 2;
while (!(read_STAT(drv_data) & BIT_STAT_RXS))
cpu_relax();
*(u16 *) (drv_data->rx) = read_RDBR(drv_data);
-
- bfin_spi_cs_deactive(drv_data, chip);
-
drv_data->rx += 2;
- drv_data->tx += 2;
+ bfin_spi_cs_deactive(drv_data, chip);
}
}
return;
}
+ if (transfer->len == 0) {
+ /* Move to next transfer of this msg */
+ message->state = bfin_spi_next_transfer(drv_data);
+ /* Schedule next transfer tasklet */
+ tasklet_schedule(&drv_data->pump_transfers);
+ }
+
if (transfer->tx_buf != NULL) {
drv_data->tx = (void *)transfer->tx_buf;
drv_data->tx_end = drv_data->tx + transfer->len;
drv_data->rx, drv_data->len_in_bytes);
/* invalidate caches, if needed */
- if (bfin_addr_dcachable((unsigned long) drv_data->rx))
+ if (bfin_addr_dcacheable((unsigned long) drv_data->rx))
invalidate_dcache_range((unsigned long) drv_data->rx,
(unsigned long) (drv_data->rx +
drv_data->len_in_bytes));
- /* clear tx reg soformer data is not shifted out */
- write_TDBR(drv_data, 0xFFFF);
-
dma_config |= WNR;
dma_start_addr = (unsigned long)drv_data->rx;
cr |= BIT_CTL_TIMOD_DMA_RX | BIT_CTL_SENDOPT;
dev_dbg(&drv_data->pdev->dev, "doing DMA out.\n");
/* flush caches, if needed */
- if (bfin_addr_dcachable((unsigned long) drv_data->tx))
+ if (bfin_addr_dcacheable((unsigned long) drv_data->tx))
flush_dcache_range((unsigned long) drv_data->tx,
(unsigned long) (drv_data->tx +
drv_data->len_in_bytes));
/* IO mode write then read */
dev_dbg(&drv_data->pdev->dev, "doing IO transfer\n");
+ /* we always use SPI_WRITE mode. SPI_READ mode
+ seems to have problems with setting up the
+ output value in TDBR prior to the transfer. */
+ write_CTRL(drv_data, (cr | CFG_SPI_WRITE));
+
if (full_duplex) {
/* full duplex mode */
BUG_ON((drv_data->tx_end - drv_data->tx) !=
dev_dbg(&drv_data->pdev->dev,
"IO duplex: cr is 0x%x\n", cr);
- /* set SPI transfer mode */
- write_CTRL(drv_data, (cr | CFG_SPI_WRITE));
-
drv_data->duplex(drv_data);
if (drv_data->tx != drv_data->tx_end)
dev_dbg(&drv_data->pdev->dev,
"IO write: cr is 0x%x\n", cr);
- /* set SPI transfer mode */
- write_CTRL(drv_data, (cr | CFG_SPI_WRITE));
-
drv_data->write(drv_data);
if (drv_data->tx != drv_data->tx_end)
dev_dbg(&drv_data->pdev->dev,
"IO read: cr is 0x%x\n", cr);
- /* set SPI transfer mode */
- write_CTRL(drv_data, (cr | CFG_SPI_READ));
-
drv_data->read(drv_data);
if (drv_data->rx != drv_data->rx_end)
tranf_success = 0;
}
/* Schedule next transfer tasklet */
tasklet_schedule(&drv_data->pump_transfers);
-
}
}
struct bfin5xx_spi_chip *chip_info = NULL;
struct chip_data *chip;
struct driver_data *drv_data = spi_master_get_devdata(spi->master);
-
- /* Abort device setup if requested features are not supported */
- if (spi->mode & ~(SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST)) {
- dev_err(&spi->dev, "requested mode not fully supported\n");
- return -EINVAL;
- }
-
- /* Zero (the default) here means 8 bits */
- if (!spi->bits_per_word)
- spi->bits_per_word = 8;
+ int ret;
if (spi->bits_per_word != 8 && spi->bits_per_word != 16)
return -EINVAL;
chip->bits_per_word = chip_info->bits_per_word;
chip->cs_change_per_word = chip_info->cs_change_per_word;
chip->cs_chg_udelay = chip_info->cs_chg_udelay;
+ chip->cs_gpio = chip_info->cs_gpio;
+ chip->idle_tx_val = chip_info->idle_tx_val;
}
/* translate common spi framework into our register */
chip->flag = 1 << (spi->chip_select);
chip->chip_select_num = spi->chip_select;
+ if (chip->chip_select_num == 0) {
+ ret = gpio_request(chip->cs_gpio, spi->modalias);
+ if (ret) {
+ if (drv_data->dma_requested)
+ free_dma(drv_data->dma_channel);
+ return ret;
+ }
+ gpio_direction_output(chip->cs_gpio, 1);
+ }
+
switch (chip->bits_per_word) {
case 8:
chip->n_bytes = 1;
{
struct chip_data *chip = spi_get_ctldata(spi);
+ if (!chip)
+ return;
+
if ((chip->chip_select_num > 0)
&& (chip->chip_select_num <= spi->master->num_chipselect))
peripheral_free(ssel[spi->master->bus_num]
[chip->chip_select_num-1]);
+ if (chip->chip_select_num == 0)
+ gpio_free(chip->cs_gpio);
+
kfree(chip);
}
drv_data->pdev = pdev;
drv_data->pin_req = platform_info->pin_req;
+ /* the spi->mode bits supported by this driver: */
+ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
+
master->bus_num = pdev->id;
master->num_chipselect = platform_info->num_chipselect;
master->cleanup = bfin_spi_cleanup;
goto out_error_get_res;
}
- drv_data->regs_base = ioremap(res->start, (res->end - res->start + 1));
+ drv_data->regs_base = ioremap(res->start, resource_size(res));
if (drv_data->regs_base == NULL) {
dev_err(dev, "Cannot map IO\n");
status = -ENXIO;