Commit f6f46de1063c8829713cd9d5b960dd8cb66cde8b
Committed by
Mark Brown
1 parent
3ce8859e2e
spi/pl022: Add chip select handling via GPIO
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>
Showing 2 changed files with 36 additions and 14 deletions Side-by-side Diff
drivers/spi/spi-pl022.c
... | ... | @@ -40,6 +40,7 @@ |
40 | 40 | #include <linux/dma-mapping.h> |
41 | 41 | #include <linux/scatterlist.h> |
42 | 42 | #include <linux/pm_runtime.h> |
43 | +#include <linux/gpio.h> | |
43 | 44 | |
44 | 45 | /* |
45 | 46 | * This macro is used to define some register default values. |
... | ... | @@ -356,6 +357,8 @@ |
356 | 357 | * @sgt_rx: scattertable for the RX transfer |
357 | 358 | * @sgt_tx: scattertable for the TX transfer |
358 | 359 | * @dummypage: a dummy page used for driving data on the bus with DMA |
360 | + * @cur_cs: current chip select (gpio) | |
361 | + * @chipselects: list of chipselects (gpios) | |
359 | 362 | */ |
360 | 363 | struct pl022 { |
361 | 364 | struct amba_device *adev; |
... | ... | @@ -389,6 +392,8 @@ |
389 | 392 | char *dummypage; |
390 | 393 | bool dma_running; |
391 | 394 | #endif |
395 | + int cur_cs; | |
396 | + int *chipselects; | |
392 | 397 | }; |
393 | 398 | |
394 | 399 | /** |
... | ... | @@ -433,6 +438,14 @@ |
433 | 438 | pr_debug("pl022: dummy chip select control, CS=0x%x\n", command); |
434 | 439 | } |
435 | 440 | |
441 | +static void pl022_cs_control(struct pl022 *pl022, u32 command) | |
442 | +{ | |
443 | + if (gpio_is_valid(pl022->cur_cs)) | |
444 | + gpio_set_value(pl022->cur_cs, command); | |
445 | + else | |
446 | + pl022->cur_chip->cs_control(command); | |
447 | +} | |
448 | + | |
436 | 449 | /** |
437 | 450 | * giveback - current spi_message is over, schedule next message and call |
438 | 451 | * callback of this message. Assumes that caller already |
... | ... | @@ -479,7 +492,7 @@ |
479 | 492 | if (next_msg && next_msg->spi != pl022->cur_msg->spi) |
480 | 493 | next_msg = NULL; |
481 | 494 | if (!next_msg || pl022->cur_msg->state == STATE_ERROR) |
482 | - pl022->cur_chip->cs_control(SSP_CHIP_DESELECT); | |
495 | + pl022_cs_control(pl022, SSP_CHIP_DESELECT); | |
483 | 496 | else |
484 | 497 | pl022->next_msg_cs_active = true; |
485 | 498 | |
... | ... | @@ -818,8 +831,7 @@ |
818 | 831 | /* Update total bytes transferred */ |
819 | 832 | msg->actual_length += pl022->cur_transfer->len; |
820 | 833 | if (pl022->cur_transfer->cs_change) |
821 | - pl022->cur_chip-> | |
822 | - cs_control(SSP_CHIP_DESELECT); | |
834 | + pl022_cs_control(pl022, SSP_CHIP_DESELECT); | |
823 | 835 | |
824 | 836 | /* Move to next transfer */ |
825 | 837 | msg->state = next_transfer(pl022); |
... | ... | @@ -1252,8 +1264,7 @@ |
1252 | 1264 | /* Update total bytes transferred */ |
1253 | 1265 | msg->actual_length += pl022->cur_transfer->len; |
1254 | 1266 | if (pl022->cur_transfer->cs_change) |
1255 | - pl022->cur_chip-> | |
1256 | - cs_control(SSP_CHIP_DESELECT); | |
1267 | + pl022_cs_control(pl022, SSP_CHIP_DESELECT); | |
1257 | 1268 | /* Move to next transfer */ |
1258 | 1269 | msg->state = next_transfer(pl022); |
1259 | 1270 | tasklet_schedule(&pl022->pump_transfers); |
... | ... | @@ -1338,7 +1349,7 @@ |
1338 | 1349 | |
1339 | 1350 | /* Reselect chip select only if cs_change was requested */ |
1340 | 1351 | if (previous->cs_change) |
1341 | - pl022->cur_chip->cs_control(SSP_CHIP_SELECT); | |
1352 | + pl022_cs_control(pl022, SSP_CHIP_SELECT); | |
1342 | 1353 | } else { |
1343 | 1354 | /* STATE_START */ |
1344 | 1355 | message->state = STATE_RUNNING; |
... | ... | @@ -1377,7 +1388,7 @@ |
1377 | 1388 | |
1378 | 1389 | /* Enable target chip, if not already active */ |
1379 | 1390 | if (!pl022->next_msg_cs_active) |
1380 | - pl022->cur_chip->cs_control(SSP_CHIP_SELECT); | |
1391 | + pl022_cs_control(pl022, SSP_CHIP_SELECT); | |
1381 | 1392 | |
1382 | 1393 | if (set_up_next_transfer(pl022, pl022->cur_transfer)) { |
1383 | 1394 | /* Error path */ |
1384 | 1395 | |
... | ... | @@ -1429,12 +1440,12 @@ |
1429 | 1440 | if (previous->delay_usecs) |
1430 | 1441 | udelay(previous->delay_usecs); |
1431 | 1442 | if (previous->cs_change) |
1432 | - pl022->cur_chip->cs_control(SSP_CHIP_SELECT); | |
1443 | + pl022_cs_control(pl022, SSP_CHIP_SELECT); | |
1433 | 1444 | } else { |
1434 | 1445 | /* STATE_START */ |
1435 | 1446 | message->state = STATE_RUNNING; |
1436 | 1447 | if (!pl022->next_msg_cs_active) |
1437 | - pl022->cur_chip->cs_control(SSP_CHIP_SELECT); | |
1448 | + pl022_cs_control(pl022, SSP_CHIP_SELECT); | |
1438 | 1449 | } |
1439 | 1450 | |
1440 | 1451 | /* Configuration Changing Per Transfer */ |
... | ... | @@ -1466,7 +1477,7 @@ |
1466 | 1477 | /* Update total byte transferred */ |
1467 | 1478 | message->actual_length += pl022->cur_transfer->len; |
1468 | 1479 | if (pl022->cur_transfer->cs_change) |
1469 | - pl022->cur_chip->cs_control(SSP_CHIP_DESELECT); | |
1480 | + pl022_cs_control(pl022, SSP_CHIP_DESELECT); | |
1470 | 1481 | /* Move to next transfer */ |
1471 | 1482 | message->state = next_transfer(pl022); |
1472 | 1483 | } |
... | ... | @@ -1495,6 +1506,7 @@ |
1495 | 1506 | |
1496 | 1507 | /* Setup the SPI using the per chip configuration */ |
1497 | 1508 | pl022->cur_chip = spi_get_ctldata(msg->spi); |
1509 | + pl022->cur_cs = pl022->chipselects[msg->spi->chip_select]; | |
1498 | 1510 | |
1499 | 1511 | restore_state(pl022); |
1500 | 1512 | flush(pl022); |
... | ... | @@ -1840,8 +1852,9 @@ |
1840 | 1852 | chip->xfer_type = chip_info->com_mode; |
1841 | 1853 | if (!chip_info->cs_control) { |
1842 | 1854 | chip->cs_control = null_cs_control; |
1843 | - dev_warn(&spi->dev, | |
1844 | - "chip select function is NULL for this chip\n"); | |
1855 | + if (!gpio_is_valid(pl022->chipselects[spi->chip_select])) | |
1856 | + dev_warn(&spi->dev, | |
1857 | + "invalid chip select\n"); | |
1845 | 1858 | } else |
1846 | 1859 | chip->cs_control = chip_info->cs_control; |
1847 | 1860 | |
... | ... | @@ -1993,7 +2006,7 @@ |
1993 | 2006 | struct pl022_ssp_controller *platform_info = adev->dev.platform_data; |
1994 | 2007 | struct spi_master *master; |
1995 | 2008 | struct pl022 *pl022 = NULL; /*Data for this driver */ |
1996 | - int status = 0; | |
2009 | + int status = 0, i; | |
1997 | 2010 | |
1998 | 2011 | dev_info(&adev->dev, |
1999 | 2012 | "ARM PL022 driver, device ID: 0x%08x\n", adev->periphid); |
... | ... | @@ -2004,7 +2017,8 @@ |
2004 | 2017 | } |
2005 | 2018 | |
2006 | 2019 | /* Allocate master with space for data */ |
2007 | - master = spi_alloc_master(dev, sizeof(struct pl022)); | |
2020 | + master = spi_alloc_master(dev, sizeof(struct pl022) + sizeof(int) * | |
2021 | + platform_info->num_chipselect); | |
2008 | 2022 | if (master == NULL) { |
2009 | 2023 | dev_err(&adev->dev, "probe - cannot alloc SPI master\n"); |
2010 | 2024 | status = -ENOMEM; |
... | ... | @@ -2016,6 +2030,8 @@ |
2016 | 2030 | pl022->master_info = platform_info; |
2017 | 2031 | pl022->adev = adev; |
2018 | 2032 | pl022->vendor = id->data; |
2033 | + /* Point chipselects to allocated memory beyond the main struct */ | |
2034 | + pl022->chipselects = (int *) pl022 + sizeof(struct pl022); | |
2019 | 2035 | |
2020 | 2036 | /* |
2021 | 2037 | * Bus Number Which has been Assigned to this SSP controller |
... | ... | @@ -2029,6 +2045,10 @@ |
2029 | 2045 | master->transfer_one_message = pl022_transfer_one_message; |
2030 | 2046 | master->unprepare_transfer_hardware = pl022_unprepare_transfer_hardware; |
2031 | 2047 | master->rt = platform_info->rt; |
2048 | + | |
2049 | + if (platform_info->num_chipselect && platform_info->chipselects) | |
2050 | + for (i = 0; i < platform_info->num_chipselect; i++) | |
2051 | + pl022->chipselects[i] = platform_info->chipselects[i]; | |
2032 | 2052 | |
2033 | 2053 | /* |
2034 | 2054 | * Supports mode 0-3, loopback, and active low CS. Transfers are |
include/linux/amba/pl022.h
... | ... | @@ -244,6 +244,7 @@ |
244 | 244 | * indicates no delay and the device will be suspended immediately. |
245 | 245 | * @rt: indicates the controller should run the message pump with realtime |
246 | 246 | * priority to minimise the transfer latency on the bus. |
247 | + * @chipselects: list of <num_chipselects> chip select gpios | |
247 | 248 | */ |
248 | 249 | struct pl022_ssp_controller { |
249 | 250 | u16 bus_id; |
... | ... | @@ -254,6 +255,7 @@ |
254 | 255 | void *dma_tx_param; |
255 | 256 | int autosuspend_delay; |
256 | 257 | bool rt; |
258 | + int *chipselects; | |
257 | 259 | }; |
258 | 260 | |
259 | 261 | /** |