Skip to content

Commit

Permalink
spi: pxa2xx_spi: introduce chipselect GPIO to simplify the common cases
Browse files Browse the repository at this point in the history
Most SPI peripherals use GPIOs as their chip selects, introduce .gpio_cs
for this.

Signed-off-by: Eric Miao <[email protected]>
Cc: David Brownell <[email protected]>
Cc: Russell King <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Eric Miao authored and torvalds committed Apr 7, 2009
1 parent c8fc657 commit a7bb390
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 18 deletions.
1 change: 1 addition & 0 deletions arch/arm/mach-pxa/include/mach/pxa2xx_spi.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ struct pxa2xx_spi_chip {
u8 dma_burst_size;
u32 timeout;
u8 enable_loopback;
int gpio_cs;
void (*cs_control)(u32 command);
};

Expand Down
96 changes: 78 additions & 18 deletions drivers/spi/pxa2xx_spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/gpio.h>

#include <asm/io.h>
#include <asm/irq.h>
Expand Down Expand Up @@ -166,13 +167,41 @@ struct chip_data {
u8 enable_dma;
u8 bits_per_word;
u32 speed_hz;
int gpio_cs;
int gpio_cs_inverted;
int (*write)(struct driver_data *drv_data);
int (*read)(struct driver_data *drv_data);
void (*cs_control)(u32 command);
};

static void pump_messages(struct work_struct *work);

static void cs_assert(struct driver_data *drv_data)
{
struct chip_data *chip = drv_data->cur_chip;

if (chip->cs_control) {
chip->cs_control(PXA2XX_CS_ASSERT);
return;
}

if (gpio_is_valid(chip->gpio_cs))
gpio_set_value(chip->gpio_cs, chip->gpio_cs_inverted);
}

static void cs_deassert(struct driver_data *drv_data)
{
struct chip_data *chip = drv_data->cur_chip;

if (chip->cs_control) {
chip->cs_control(PXA2XX_CS_ASSERT);
return;
}

if (gpio_is_valid(chip->gpio_cs))
gpio_set_value(chip->gpio_cs, !chip->gpio_cs_inverted);
}

static int flush(struct driver_data *drv_data)
{
unsigned long limit = loops_per_jiffy << 1;
Expand All @@ -189,10 +218,6 @@ static int flush(struct driver_data *drv_data)
return limit;
}

static void null_cs_control(u32 command)
{
}

static int null_writer(struct driver_data *drv_data)
{
void __iomem *reg = drv_data->ioaddr;
Expand Down Expand Up @@ -400,7 +425,6 @@ static void giveback(struct driver_data *drv_data)
msg = drv_data->cur_msg;
drv_data->cur_msg = NULL;
drv_data->cur_transfer = NULL;
drv_data->cur_chip = NULL;
queue_work(drv_data->workqueue, &drv_data->pump_messages);
spin_unlock_irqrestore(&drv_data->lock, flags);

Expand All @@ -416,7 +440,7 @@ static void giveback(struct driver_data *drv_data)
* a message with an error, or next message is for another chip
*/
if (!last_transfer->cs_change)
drv_data->cs_control(PXA2XX_CS_DEASSERT);
cs_deassert(drv_data);
else {
struct spi_message *next_msg;

Expand Down Expand Up @@ -445,12 +469,14 @@ static void giveback(struct driver_data *drv_data)
if (next_msg && next_msg->spi != msg->spi)
next_msg = NULL;
if (!next_msg || msg->state == ERROR_STATE)
drv_data->cs_control(PXA2XX_CS_DEASSERT);
cs_deassert(drv_data);
}

msg->state = NULL;
if (msg->complete)
msg->complete(msg->context);

drv_data->cur_chip = NULL;
}

static int wait_ssp_rx_stall(void const __iomem *ioaddr)
Expand Down Expand Up @@ -887,7 +913,7 @@ static void pump_transfers(unsigned long data)

/* Drop chip select only if cs_change is requested */
if (previous->cs_change)
drv_data->cs_control(PXA2XX_CS_DEASSERT);
cs_deassert(drv_data);
}

/* Check for transfers that need multiple DMA segments */
Expand Down Expand Up @@ -922,7 +948,6 @@ static void pump_transfers(unsigned long data)
}
drv_data->n_bytes = chip->n_bytes;
drv_data->dma_width = chip->dma_width;
drv_data->cs_control = chip->cs_control;
drv_data->tx = (void *)transfer->tx_buf;
drv_data->tx_end = drv_data->tx + transfer->len;
drv_data->rx = transfer->rx_buf;
Expand Down Expand Up @@ -1084,11 +1109,7 @@ static void pump_transfers(unsigned long data)
write_SSTO(chip->timeout, reg);
}

/* FIXME, need to handle cs polarity,
* this driver uses struct pxa2xx_spi_chip.cs_control to
* specify a CS handling function, and it ignores most
* struct spi_device.mode[s], including SPI_CS_HIGH */
drv_data->cs_control(PXA2XX_CS_ASSERT);
cs_assert(drv_data);

/* after chip select, release the data by enabling service
* requests and interrupts, without changing any mode bits */
Expand Down Expand Up @@ -1166,6 +1187,44 @@ static int transfer(struct spi_device *spi, struct spi_message *msg)
/* the spi->mode bits understood by this driver: */
#define MODEBITS (SPI_CPOL | SPI_CPHA)

static int setup_cs(struct spi_device *spi, struct chip_data *chip,
struct pxa2xx_spi_chip *chip_info)
{
int err = 0;

if (chip == NULL || chip_info == NULL)
return 0;

/* NOTE: setup() can be called multiple times, possibly with
* different chip_info, release previously requested GPIO
*/
if (gpio_is_valid(chip->gpio_cs))
gpio_free(chip->gpio_cs);

/* If (*cs_control) is provided, ignore GPIO chip select */
if (chip_info->cs_control) {
chip->cs_control = chip_info->cs_control;
return 0;
}

if (gpio_is_valid(chip_info->gpio_cs)) {
err = gpio_request(chip_info->gpio_cs, "SPI_CS");
if (err) {
dev_err(&spi->dev, "failed to request chip select "
"GPIO%d\n", chip_info->gpio_cs);
return err;
}

chip->gpio_cs = chip_info->gpio_cs;
chip->gpio_cs_inverted = spi->mode & SPI_CS_HIGH;

err = gpio_direction_output(chip->gpio_cs,
!chip->gpio_cs_inverted);
}

return err;
}

static int setup(struct spi_device *spi)
{
struct pxa2xx_spi_chip *chip_info = NULL;
Expand Down Expand Up @@ -1211,7 +1270,7 @@ static int setup(struct spi_device *spi)
return -ENOMEM;
}

chip->cs_control = null_cs_control;
chip->gpio_cs = -1;
chip->enable_dma = 0;
chip->timeout = TIMOUT_DFLT;
chip->dma_burst_size = drv_data->master_info->enable_dma ?
Expand All @@ -1225,8 +1284,6 @@ static int setup(struct spi_device *spi)
/* chip_info isn't always needed */
chip->cr1 = 0;
if (chip_info) {
if (chip_info->cs_control)
chip->cs_control = chip_info->cs_control;
if (chip_info->timeout)
chip->timeout = chip_info->timeout;
if (chip_info->tx_threshold)
Expand Down Expand Up @@ -1308,13 +1365,16 @@ static int setup(struct spi_device *spi)

spi_set_ctldata(spi, chip);

return 0;
return setup_cs(spi, chip, chip_info);
}

static void cleanup(struct spi_device *spi)
{
struct chip_data *chip = spi_get_ctldata(spi);

if (gpio_is_valid(chip->gpio_cs))
gpio_free(chip->gpio_cs);

kfree(chip);
}

Expand Down

0 comments on commit a7bb390

Please sign in to comment.