.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * A driver for the ARM PL022 PrimeCell SSP/SPI bus master. |
---|
3 | 4 | * |
---|
.. | .. |
---|
10 | 11 | * linux-2.6.17-rc3-mm1/drivers/spi/pxa2xx_spi.c |
---|
11 | 12 | * Initial adoption to PL022 by: |
---|
12 | 13 | * Sachin Verma <sachin.verma@st.com> |
---|
13 | | - * |
---|
14 | | - * This program is free software; you can redistribute it and/or modify |
---|
15 | | - * it under the terms of the GNU General Public License as published by |
---|
16 | | - * the Free Software Foundation; either version 2 of the License, or |
---|
17 | | - * (at your option) any later version. |
---|
18 | | - * |
---|
19 | | - * This program is distributed in the hope that it will be useful, |
---|
20 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
21 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
22 | | - * GNU General Public License for more details. |
---|
23 | 14 | */ |
---|
24 | 15 | |
---|
25 | 16 | #include <linux/init.h> |
---|
.. | .. |
---|
253 | 244 | #define STATE_RUNNING ((void *) 1) |
---|
254 | 245 | #define STATE_DONE ((void *) 2) |
---|
255 | 246 | #define STATE_ERROR ((void *) -1) |
---|
| 247 | +#define STATE_TIMEOUT ((void *) -2) |
---|
256 | 248 | |
---|
257 | 249 | /* |
---|
258 | 250 | * SSP State - Whether Enabled or Disabled |
---|
.. | .. |
---|
306 | 298 | READING_U32 |
---|
307 | 299 | }; |
---|
308 | 300 | |
---|
309 | | -/** |
---|
| 301 | +/* |
---|
310 | 302 | * The type of writing going on on this chip |
---|
311 | 303 | */ |
---|
312 | 304 | enum ssp_writing { |
---|
.. | .. |
---|
325 | 317 | * @extended_cr: 32 bit wide control register 0 with extra |
---|
326 | 318 | * features and extra features in CR1 as found in the ST variants |
---|
327 | 319 | * @pl023: supports a subset of the ST extensions called "PL023" |
---|
| 320 | + * @loopback: supports loopback mode |
---|
328 | 321 | * @internal_cs_ctrl: supports chip select control register |
---|
329 | 322 | */ |
---|
330 | 323 | struct vendor_data { |
---|
.. | .. |
---|
361 | 354 | * @read: the type of read currently going on |
---|
362 | 355 | * @write: the type of write currently going on |
---|
363 | 356 | * @exp_fifo_level: expected FIFO level |
---|
| 357 | + * @rx_lev_trig: receive FIFO watermark level which triggers IRQ |
---|
| 358 | + * @tx_lev_trig: transmit FIFO watermark level which triggers IRQ |
---|
364 | 359 | * @dma_rx_channel: optional channel for RX DMA |
---|
365 | 360 | * @dma_tx_channel: optional channel for TX DMA |
---|
366 | 361 | * @sgt_rx: scattertable for the RX transfer |
---|
367 | 362 | * @sgt_tx: scattertable for the TX transfer |
---|
368 | 363 | * @dummypage: a dummy page used for driving data on the bus with DMA |
---|
| 364 | + * @dma_running: indicates whether DMA is in operation |
---|
369 | 365 | * @cur_cs: current chip select (gpio) |
---|
370 | 366 | * @chipselects: list of chipselects (gpios) |
---|
371 | 367 | */ |
---|
.. | .. |
---|
493 | 489 | struct spi_transfer, transfer_list); |
---|
494 | 490 | |
---|
495 | 491 | /* Delay if requested before any change in chip select */ |
---|
496 | | - if (last_transfer->delay_usecs) |
---|
497 | | - /* |
---|
498 | | - * FIXME: This runs in interrupt context. |
---|
499 | | - * Is this really smart? |
---|
500 | | - */ |
---|
501 | | - udelay(last_transfer->delay_usecs); |
---|
| 492 | + /* |
---|
| 493 | + * FIXME: This runs in interrupt context. |
---|
| 494 | + * Is this really smart? |
---|
| 495 | + */ |
---|
| 496 | + spi_transfer_delay_exec(last_transfer); |
---|
502 | 497 | |
---|
503 | 498 | if (!last_transfer->cs_change) { |
---|
504 | 499 | struct spi_message *next_msg; |
---|
.. | .. |
---|
671 | 666 | writew(CLEAR_ALL_INTERRUPTS, SSP_ICR(pl022->virtbase)); |
---|
672 | 667 | } |
---|
673 | 668 | |
---|
674 | | -/** |
---|
| 669 | +/* |
---|
675 | 670 | * This will write to TX and read from RX according to the parameters |
---|
676 | 671 | * set in pl022. |
---|
677 | 672 | */ |
---|
.. | .. |
---|
861 | 856 | |
---|
862 | 857 | /* Update total bytes transferred */ |
---|
863 | 858 | msg->actual_length += pl022->cur_transfer->len; |
---|
864 | | - if (pl022->cur_transfer->cs_change) |
---|
865 | | - pl022_cs_control(pl022, SSP_CHIP_DESELECT); |
---|
866 | | - |
---|
867 | 859 | /* Move to next transfer */ |
---|
868 | 860 | msg->state = next_transfer(pl022); |
---|
| 861 | + if (msg->state != STATE_DONE && pl022->cur_transfer->cs_change) |
---|
| 862 | + pl022_cs_control(pl022, SSP_CHIP_DESELECT); |
---|
869 | 863 | tasklet_schedule(&pl022->pump_transfers); |
---|
870 | 864 | } |
---|
871 | 865 | |
---|
.. | .. |
---|
1168 | 1162 | int err; |
---|
1169 | 1163 | |
---|
1170 | 1164 | /* automatically configure DMA channels from platform, normally using DT */ |
---|
1171 | | - chan = dma_request_slave_channel_reason(dev, "rx"); |
---|
| 1165 | + chan = dma_request_chan(dev, "rx"); |
---|
1172 | 1166 | if (IS_ERR(chan)) { |
---|
1173 | 1167 | err = PTR_ERR(chan); |
---|
1174 | 1168 | goto err_no_rxchan; |
---|
.. | .. |
---|
1176 | 1170 | |
---|
1177 | 1171 | pl022->dma_rx_channel = chan; |
---|
1178 | 1172 | |
---|
1179 | | - chan = dma_request_slave_channel_reason(dev, "tx"); |
---|
| 1173 | + chan = dma_request_chan(dev, "tx"); |
---|
1180 | 1174 | if (IS_ERR(chan)) { |
---|
1181 | 1175 | err = PTR_ERR(chan); |
---|
1182 | 1176 | goto err_no_txchan; |
---|
.. | .. |
---|
1247 | 1241 | |
---|
1248 | 1242 | /** |
---|
1249 | 1243 | * pl022_interrupt_handler - Interrupt handler for SSP controller |
---|
| 1244 | + * @irq: IRQ number |
---|
| 1245 | + * @dev_id: Local device data |
---|
1250 | 1246 | * |
---|
1251 | 1247 | * This function handles interrupts generated for an interrupt based transfer. |
---|
1252 | 1248 | * If a receive overrun (ROR) interrupt is there then we disable SSP, flag the |
---|
.. | .. |
---|
1333 | 1329 | } |
---|
1334 | 1330 | /* Update total bytes transferred */ |
---|
1335 | 1331 | msg->actual_length += pl022->cur_transfer->len; |
---|
1336 | | - if (pl022->cur_transfer->cs_change) |
---|
1337 | | - pl022_cs_control(pl022, SSP_CHIP_DESELECT); |
---|
1338 | 1332 | /* Move to next transfer */ |
---|
1339 | 1333 | msg->state = next_transfer(pl022); |
---|
| 1334 | + if (msg->state != STATE_DONE && pl022->cur_transfer->cs_change) |
---|
| 1335 | + pl022_cs_control(pl022, SSP_CHIP_DESELECT); |
---|
1340 | 1336 | tasklet_schedule(&pl022->pump_transfers); |
---|
1341 | 1337 | return IRQ_HANDLED; |
---|
1342 | 1338 | } |
---|
.. | .. |
---|
1344 | 1340 | return IRQ_HANDLED; |
---|
1345 | 1341 | } |
---|
1346 | 1342 | |
---|
1347 | | -/** |
---|
| 1343 | +/* |
---|
1348 | 1344 | * This sets up the pointers to memory for the next message to |
---|
1349 | 1345 | * send out on the SPI bus. |
---|
1350 | 1346 | */ |
---|
.. | .. |
---|
1410 | 1406 | previous = list_entry(transfer->transfer_list.prev, |
---|
1411 | 1407 | struct spi_transfer, |
---|
1412 | 1408 | transfer_list); |
---|
1413 | | - if (previous->delay_usecs) |
---|
1414 | | - /* |
---|
1415 | | - * FIXME: This runs in interrupt context. |
---|
1416 | | - * Is this really smart? |
---|
1417 | | - */ |
---|
1418 | | - udelay(previous->delay_usecs); |
---|
| 1409 | + /* |
---|
| 1410 | + * FIXME: This runs in interrupt context. |
---|
| 1411 | + * Is this really smart? |
---|
| 1412 | + */ |
---|
| 1413 | + spi_transfer_delay_exec(previous); |
---|
1419 | 1414 | |
---|
1420 | 1415 | /* Reselect chip select only if cs_change was requested */ |
---|
1421 | 1416 | if (previous->cs_change) |
---|
.. | .. |
---|
1485 | 1480 | writew(irqflags, SSP_IMSC(pl022->virtbase)); |
---|
1486 | 1481 | } |
---|
1487 | 1482 | |
---|
| 1483 | +static void print_current_status(struct pl022 *pl022) |
---|
| 1484 | +{ |
---|
| 1485 | + u32 read_cr0; |
---|
| 1486 | + u16 read_cr1, read_dmacr, read_sr; |
---|
| 1487 | + |
---|
| 1488 | + if (pl022->vendor->extended_cr) |
---|
| 1489 | + read_cr0 = readl(SSP_CR0(pl022->virtbase)); |
---|
| 1490 | + else |
---|
| 1491 | + read_cr0 = readw(SSP_CR0(pl022->virtbase)); |
---|
| 1492 | + read_cr1 = readw(SSP_CR1(pl022->virtbase)); |
---|
| 1493 | + read_dmacr = readw(SSP_DMACR(pl022->virtbase)); |
---|
| 1494 | + read_sr = readw(SSP_SR(pl022->virtbase)); |
---|
| 1495 | + |
---|
| 1496 | + dev_warn(&pl022->adev->dev, "spi-pl022 CR0: %x\n", read_cr0); |
---|
| 1497 | + dev_warn(&pl022->adev->dev, "spi-pl022 CR1: %x\n", read_cr1); |
---|
| 1498 | + dev_warn(&pl022->adev->dev, "spi-pl022 DMACR: %x\n", read_dmacr); |
---|
| 1499 | + dev_warn(&pl022->adev->dev, "spi-pl022 SR: %x\n", read_sr); |
---|
| 1500 | + dev_warn(&pl022->adev->dev, |
---|
| 1501 | + "spi-pl022 exp_fifo_level/fifodepth: %u/%d\n", |
---|
| 1502 | + pl022->exp_fifo_level, |
---|
| 1503 | + pl022->vendor->fifodepth); |
---|
| 1504 | + |
---|
| 1505 | +} |
---|
| 1506 | + |
---|
1488 | 1507 | static void do_polling_transfer(struct pl022 *pl022) |
---|
1489 | 1508 | { |
---|
1490 | 1509 | struct spi_message *message = NULL; |
---|
1491 | 1510 | struct spi_transfer *transfer = NULL; |
---|
1492 | 1511 | struct spi_transfer *previous = NULL; |
---|
1493 | | - struct chip_data *chip; |
---|
1494 | 1512 | unsigned long time, timeout; |
---|
1495 | 1513 | |
---|
1496 | | - chip = pl022->cur_chip; |
---|
1497 | 1514 | message = pl022->cur_msg; |
---|
1498 | 1515 | |
---|
1499 | 1516 | while (message->state != STATE_DONE) { |
---|
.. | .. |
---|
1507 | 1524 | previous = |
---|
1508 | 1525 | list_entry(transfer->transfer_list.prev, |
---|
1509 | 1526 | struct spi_transfer, transfer_list); |
---|
1510 | | - if (previous->delay_usecs) |
---|
1511 | | - udelay(previous->delay_usecs); |
---|
| 1527 | + spi_transfer_delay_exec(previous); |
---|
1512 | 1528 | if (previous->cs_change) |
---|
1513 | 1529 | pl022_cs_control(pl022, SSP_CHIP_SELECT); |
---|
1514 | 1530 | } else { |
---|
.. | .. |
---|
1538 | 1554 | if (time_after(time, timeout)) { |
---|
1539 | 1555 | dev_warn(&pl022->adev->dev, |
---|
1540 | 1556 | "%s: timeout!\n", __func__); |
---|
1541 | | - message->state = STATE_ERROR; |
---|
| 1557 | + message->state = STATE_TIMEOUT; |
---|
| 1558 | + print_current_status(pl022); |
---|
1542 | 1559 | goto out; |
---|
1543 | 1560 | } |
---|
1544 | 1561 | cpu_relax(); |
---|
.. | .. |
---|
1546 | 1563 | |
---|
1547 | 1564 | /* Update total byte transferred */ |
---|
1548 | 1565 | message->actual_length += pl022->cur_transfer->len; |
---|
1549 | | - if (pl022->cur_transfer->cs_change) |
---|
1550 | | - pl022_cs_control(pl022, SSP_CHIP_DESELECT); |
---|
1551 | 1566 | /* Move to next transfer */ |
---|
1552 | 1567 | message->state = next_transfer(pl022); |
---|
| 1568 | + if (message->state != STATE_DONE |
---|
| 1569 | + && pl022->cur_transfer->cs_change) |
---|
| 1570 | + pl022_cs_control(pl022, SSP_CHIP_DESELECT); |
---|
1553 | 1571 | } |
---|
1554 | 1572 | out: |
---|
1555 | 1573 | /* Handle end of message */ |
---|
1556 | 1574 | if (message->state == STATE_DONE) |
---|
1557 | 1575 | message->status = 0; |
---|
| 1576 | + else if (message->state == STATE_TIMEOUT) |
---|
| 1577 | + message->status = -EAGAIN; |
---|
1558 | 1578 | else |
---|
1559 | 1579 | message->status = -EIO; |
---|
1560 | 1580 | |
---|
.. | .. |
---|
2295 | 2315 | return status; |
---|
2296 | 2316 | } |
---|
2297 | 2317 | |
---|
2298 | | -static int |
---|
| 2318 | +static void |
---|
2299 | 2319 | pl022_remove(struct amba_device *adev) |
---|
2300 | 2320 | { |
---|
2301 | 2321 | struct pl022 *pl022 = amba_get_drvdata(adev); |
---|
2302 | 2322 | |
---|
2303 | 2323 | if (!pl022) |
---|
2304 | | - return 0; |
---|
| 2324 | + return; |
---|
2305 | 2325 | |
---|
2306 | 2326 | /* |
---|
2307 | 2327 | * undo pm_runtime_put() in probe. I assume that we're not |
---|
.. | .. |
---|
2316 | 2336 | clk_disable_unprepare(pl022->clk); |
---|
2317 | 2337 | amba_release_regions(adev); |
---|
2318 | 2338 | tasklet_disable(&pl022->pump_transfers); |
---|
2319 | | - return 0; |
---|
2320 | 2339 | } |
---|
2321 | 2340 | |
---|
2322 | 2341 | #ifdef CONFIG_PM_SLEEP |
---|
.. | .. |
---|
2326 | 2345 | int ret; |
---|
2327 | 2346 | |
---|
2328 | 2347 | ret = spi_master_suspend(pl022->master); |
---|
2329 | | - if (ret) { |
---|
2330 | | - dev_warn(dev, "cannot suspend master\n"); |
---|
| 2348 | + if (ret) |
---|
2331 | 2349 | return ret; |
---|
2332 | | - } |
---|
2333 | 2350 | |
---|
2334 | 2351 | ret = pm_runtime_force_suspend(dev); |
---|
2335 | 2352 | if (ret) { |
---|
.. | .. |
---|
2354 | 2371 | |
---|
2355 | 2372 | /* Start the queue running */ |
---|
2356 | 2373 | ret = spi_master_resume(pl022->master); |
---|
2357 | | - if (ret) |
---|
2358 | | - dev_err(dev, "problem starting queue (%d)\n", ret); |
---|
2359 | | - else |
---|
| 2374 | + if (!ret) |
---|
2360 | 2375 | dev_dbg(dev, "resumed\n"); |
---|
2361 | 2376 | |
---|
2362 | 2377 | return ret; |
---|