| .. | .. |
|---|
| 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; |
|---|