@@ -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.
@@ -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)
+ * @chipselect: list of chipselects (gpios)
*/
struct pl022 {
struct amba_device *adev;
@@ -389,6 +392,8 @@ struct pl022 {
char *dummypage;
bool dma_running;
#endif
+ int cur_cs;
+ int *chipselect;
};
/**
@@ -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
@@ -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;
@@ -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);
@@ -1252,8 +1264,7 @@ static irqreturn_t pl022_interrupt_handl
/* 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);
@@ -1338,7 +1349,7 @@ static void pump_transfers(unsigned long
/* 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;
@@ -1377,7 +1388,7 @@ static void do_interrupt_dma_transfer(st
/* 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 */
@@ -1429,12 +1440,12 @@ static void do_polling_transfer(struct p
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 */
@@ -1466,7 +1477,7 @@ static void do_polling_transfer(struct p
/* 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);
}
@@ -1495,6 +1506,7 @@ static int pl022_transfer_one_message(st
/* Setup the SPI using the per chip configuration */
pl022->cur_chip = spi_get_ctldata(msg->spi);
+ pl022->cur_cs = pl022->chipselect[msg->spi->chip_select];
restore_state(pl022);
flush(pl022);
@@ -1840,8 +1852,9 @@ static int pl022_setup(struct spi_device
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->chipselect[spi->chip_select]))
+ dev_warn(&spi->dev,
+ "chip select function is NULL for this chip\n");
} else
chip->cs_control = chip_info->cs_control;
@@ -1993,7 +2006,7 @@ pl022_probe(struct amba_device *adev, co
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);
@@ -2004,7 +2017,8 @@ pl022_probe(struct amba_device *adev, co
}
/* 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;
@@ -2016,6 +2030,7 @@ pl022_probe(struct amba_device *adev, co
pl022->master_info = platform_info;
pl022->adev = adev;
pl022->vendor = id->data;
+ pl022->chipselect = (int *)&master[1];
/*
* Bus Number Which has been Assigned to this SSP controller
@@ -2030,6 +2045,10 @@ pl022_probe(struct amba_device *adev, co
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->chipselect[i] = platform_info->chipselects[i];
+
/*
* Supports mode 0-3, loopback, and active low CS. Transfers are
* always MS bit first on the original pl022.
@@ -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;
@@ -254,6 +255,7 @@ struct pl022_ssp_controller {
void *dma_tx_param;
int autosuspend_delay;
bool rt;
+ int *chipselects;
};
/**
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> --- Applies to v3.6-rc2 Patch set changes since v4: * Rename DT property: "pl022,num-chipselects" -> "num-cs" * Removed reference to Linux code in DT binding documentation * Removed property "pl022,hierarchy" - only support SPI master for now * Documented DT property pl022,interface * Removed property "pl022,slave-tx-disable" - not relevant in master mode * Added kerneldoc for cur_cs and chipselect list * Reorganized struct pl022 (int *chipselect) * Introduced int *chipselects to struct pl022_ssp_controller * Let platform data override DT data * Split patches into CS handling vs. DT support Changes since v3: * Proper use of IS_ENABLED Changes since v2: * Use IS_ENABLED instead of #ifdef * Remove bogus const change Changes since v1: * return EPROBE_DEFFER if gpios are not initialized yet Thanks Thierry Reding, Rob Herring and Linus Walleij for reviewing! drivers/spi/spi-pl022.c | 47 +++++++++++++++++++++++++++++++-------------- include/linux/amba/pl022.h | 2 + 2 files changed, 35 insertions(+), 14 deletions(-) ------------------------------------------------------------------------------ Live Security Virtual Conference Exclusive live event will cover all the ways today's security and threat landscape has changed and how IT managers can respond. Discussions will include endpoint security, mobile security and the latest in malware threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/