Skip to content

Commit

Permalink
spi/pl022: Add chip select handling via GPIO
Browse files Browse the repository at this point in the history
This patch adds the ability for the driver to control the chip select directly.
This enables independence from cs_control callbacks.  Configurable via
platform_data, to be extended as DT in the following patch.

Based on the initial patch by Alexandre Pereira da Silva <aletes.xgr@gmail.com>

Signed-off-by: Roland Stigge <stigge@antcom.de>
Acked-by: Alexandre Pereira da Silva <aletes.xgr@gmail.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
  • Loading branch information
Roland Stigge authored and Mark Brown committed Aug 22, 2012
1 parent 3ce8859 commit f6f46de
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 14 deletions.
48 changes: 34 additions & 14 deletions drivers/spi/spi-pl022.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/pm_runtime.h>
#include <linux/gpio.h>

/*
* This macro is used to define some register default values.
Expand Down Expand Up @@ -356,6 +357,8 @@ struct vendor_data {
* @sgt_rx: scattertable for the RX transfer
* @sgt_tx: scattertable for the TX transfer
* @dummypage: a dummy page used for driving data on the bus with DMA
* @cur_cs: current chip select (gpio)
* @chipselects: list of chipselects (gpios)
*/
struct pl022 {
struct amba_device *adev;
Expand Down Expand Up @@ -389,6 +392,8 @@ struct pl022 {
char *dummypage;
bool dma_running;
#endif
int cur_cs;
int *chipselects;
};

/**
Expand Down Expand Up @@ -433,6 +438,14 @@ static void null_cs_control(u32 command)
pr_debug("pl022: dummy chip select control, CS=0x%x\n", command);
}

static void pl022_cs_control(struct pl022 *pl022, u32 command)
{
if (gpio_is_valid(pl022->cur_cs))
gpio_set_value(pl022->cur_cs, command);
else
pl022->cur_chip->cs_control(command);
}

/**
* giveback - current spi_message is over, schedule next message and call
* callback of this message. Assumes that caller already
Expand Down Expand Up @@ -479,7 +492,7 @@ static void giveback(struct pl022 *pl022)
if (next_msg && next_msg->spi != pl022->cur_msg->spi)
next_msg = NULL;
if (!next_msg || pl022->cur_msg->state == STATE_ERROR)
pl022->cur_chip->cs_control(SSP_CHIP_DESELECT);
pl022_cs_control(pl022, SSP_CHIP_DESELECT);
else
pl022->next_msg_cs_active = true;

Expand Down Expand Up @@ -818,8 +831,7 @@ static void dma_callback(void *data)
/* Update total bytes transferred */
msg->actual_length += pl022->cur_transfer->len;
if (pl022->cur_transfer->cs_change)
pl022->cur_chip->
cs_control(SSP_CHIP_DESELECT);
pl022_cs_control(pl022, SSP_CHIP_DESELECT);

/* Move to next transfer */
msg->state = next_transfer(pl022);
Expand Down Expand Up @@ -1252,8 +1264,7 @@ static irqreturn_t pl022_interrupt_handler(int irq, void *dev_id)
/* Update total bytes transferred */
msg->actual_length += pl022->cur_transfer->len;
if (pl022->cur_transfer->cs_change)
pl022->cur_chip->
cs_control(SSP_CHIP_DESELECT);
pl022_cs_control(pl022, SSP_CHIP_DESELECT);
/* Move to next transfer */
msg->state = next_transfer(pl022);
tasklet_schedule(&pl022->pump_transfers);
Expand Down Expand Up @@ -1338,7 +1349,7 @@ static void pump_transfers(unsigned long data)

/* Reselect chip select only if cs_change was requested */
if (previous->cs_change)
pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
pl022_cs_control(pl022, SSP_CHIP_SELECT);
} else {
/* STATE_START */
message->state = STATE_RUNNING;
Expand Down Expand Up @@ -1377,7 +1388,7 @@ static void do_interrupt_dma_transfer(struct pl022 *pl022)

/* Enable target chip, if not already active */
if (!pl022->next_msg_cs_active)
pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
pl022_cs_control(pl022, SSP_CHIP_SELECT);

if (set_up_next_transfer(pl022, pl022->cur_transfer)) {
/* Error path */
Expand Down Expand Up @@ -1429,12 +1440,12 @@ static void do_polling_transfer(struct pl022 *pl022)
if (previous->delay_usecs)
udelay(previous->delay_usecs);
if (previous->cs_change)
pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
pl022_cs_control(pl022, SSP_CHIP_SELECT);
} else {
/* STATE_START */
message->state = STATE_RUNNING;
if (!pl022->next_msg_cs_active)
pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
pl022_cs_control(pl022, SSP_CHIP_SELECT);
}

/* Configuration Changing Per Transfer */
Expand Down Expand Up @@ -1466,7 +1477,7 @@ static void do_polling_transfer(struct pl022 *pl022)
/* Update total byte transferred */
message->actual_length += pl022->cur_transfer->len;
if (pl022->cur_transfer->cs_change)
pl022->cur_chip->cs_control(SSP_CHIP_DESELECT);
pl022_cs_control(pl022, SSP_CHIP_DESELECT);
/* Move to next transfer */
message->state = next_transfer(pl022);
}
Expand Down Expand Up @@ -1495,6 +1506,7 @@ static int pl022_transfer_one_message(struct spi_master *master,

/* Setup the SPI using the per chip configuration */
pl022->cur_chip = spi_get_ctldata(msg->spi);
pl022->cur_cs = pl022->chipselects[msg->spi->chip_select];

restore_state(pl022);
flush(pl022);
Expand Down Expand Up @@ -1840,8 +1852,9 @@ static int pl022_setup(struct spi_device *spi)
chip->xfer_type = chip_info->com_mode;
if (!chip_info->cs_control) {
chip->cs_control = null_cs_control;
dev_warn(&spi->dev,
"chip select function is NULL for this chip\n");
if (!gpio_is_valid(pl022->chipselects[spi->chip_select]))
dev_warn(&spi->dev,
"invalid chip select\n");
} else
chip->cs_control = chip_info->cs_control;

Expand Down Expand Up @@ -1993,7 +2006,7 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
struct pl022_ssp_controller *platform_info = adev->dev.platform_data;
struct spi_master *master;
struct pl022 *pl022 = NULL; /*Data for this driver */
int status = 0;
int status = 0, i;

dev_info(&adev->dev,
"ARM PL022 driver, device ID: 0x%08x\n", adev->periphid);
Expand All @@ -2004,7 +2017,8 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
}

/* Allocate master with space for data */
master = spi_alloc_master(dev, sizeof(struct pl022));
master = spi_alloc_master(dev, sizeof(struct pl022) + sizeof(int) *
platform_info->num_chipselect);
if (master == NULL) {
dev_err(&adev->dev, "probe - cannot alloc SPI master\n");
status = -ENOMEM;
Expand All @@ -2016,6 +2030,8 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
pl022->master_info = platform_info;
pl022->adev = adev;
pl022->vendor = id->data;
/* Point chipselects to allocated memory beyond the main struct */
pl022->chipselects = (int *) pl022 + sizeof(struct pl022);

/*
* Bus Number Which has been Assigned to this SSP controller
Expand All @@ -2030,6 +2046,10 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
master->unprepare_transfer_hardware = pl022_unprepare_transfer_hardware;
master->rt = platform_info->rt;

if (platform_info->num_chipselect && platform_info->chipselects)
for (i = 0; i < platform_info->num_chipselect; i++)
pl022->chipselects[i] = platform_info->chipselects[i];

/*
* Supports mode 0-3, loopback, and active low CS. Transfers are
* always MS bit first on the original pl022.
Expand Down
2 changes: 2 additions & 0 deletions include/linux/amba/pl022.h
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ struct dma_chan;
* indicates no delay and the device will be suspended immediately.
* @rt: indicates the controller should run the message pump with realtime
* priority to minimise the transfer latency on the bus.
* @chipselects: list of <num_chipselects> chip select gpios
*/
struct pl022_ssp_controller {
u16 bus_id;
Expand All @@ -254,6 +255,7 @@ struct pl022_ssp_controller {
void *dma_tx_param;
int autosuspend_delay;
bool rt;
int *chipselects;
};

/**
Expand Down

0 comments on commit f6f46de

Please sign in to comment.