| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Driver for Broadcom BCM2835 auxiliary SPI Controllers |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 7 | 8 | * Based on: spi-bcm2835.c |
|---|
| 8 | 9 | * |
|---|
| 9 | 10 | * Copyright (C) 2015 Martin Sperl |
|---|
| 10 | | - * |
|---|
| 11 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 12 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 13 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 14 | | - * (at your option) any later version. |
|---|
| 15 | | - * |
|---|
| 16 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 17 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 18 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 19 | | - * GNU General Public License for more details. |
|---|
| 20 | 11 | */ |
|---|
| 21 | 12 | |
|---|
| 22 | 13 | #include <linux/clk.h> |
|---|
| 23 | 14 | #include <linux/completion.h> |
|---|
| 15 | +#include <linux/debugfs.h> |
|---|
| 24 | 16 | #include <linux/delay.h> |
|---|
| 25 | 17 | #include <linux/err.h> |
|---|
| 26 | 18 | #include <linux/interrupt.h> |
|---|
| .. | .. |
|---|
| 35 | 27 | #include <linux/regmap.h> |
|---|
| 36 | 28 | #include <linux/spi/spi.h> |
|---|
| 37 | 29 | #include <linux/spinlock.h> |
|---|
| 30 | + |
|---|
| 31 | +/* define polling limits */ |
|---|
| 32 | +static unsigned int polling_limit_us = 30; |
|---|
| 33 | +module_param(polling_limit_us, uint, 0664); |
|---|
| 34 | +MODULE_PARM_DESC(polling_limit_us, |
|---|
| 35 | + "time in us to run a transfer in polling mode - if zero no polling is used\n"); |
|---|
| 38 | 36 | |
|---|
| 39 | 37 | /* |
|---|
| 40 | 38 | * spi register defines |
|---|
| .. | .. |
|---|
| 88 | 86 | #define BCM2835_AUX_SPI_STAT_BUSY 0x00000040 |
|---|
| 89 | 87 | #define BCM2835_AUX_SPI_STAT_BITCOUNT 0x0000003F |
|---|
| 90 | 88 | |
|---|
| 91 | | -/* timeout values */ |
|---|
| 92 | | -#define BCM2835_AUX_SPI_POLLING_LIMIT_US 30 |
|---|
| 93 | | -#define BCM2835_AUX_SPI_POLLING_JIFFIES 2 |
|---|
| 94 | | - |
|---|
| 95 | 89 | struct bcm2835aux_spi { |
|---|
| 96 | 90 | void __iomem *regs; |
|---|
| 97 | 91 | struct clk *clk; |
|---|
| .. | .. |
|---|
| 102 | 96 | int tx_len; |
|---|
| 103 | 97 | int rx_len; |
|---|
| 104 | 98 | int pending; |
|---|
| 99 | + |
|---|
| 100 | + u64 count_transfer_polling; |
|---|
| 101 | + u64 count_transfer_irq; |
|---|
| 102 | + u64 count_transfer_irq_after_poll; |
|---|
| 103 | + |
|---|
| 104 | + struct dentry *debugfs_dir; |
|---|
| 105 | 105 | }; |
|---|
| 106 | + |
|---|
| 107 | +#if defined(CONFIG_DEBUG_FS) |
|---|
| 108 | +static void bcm2835aux_debugfs_create(struct bcm2835aux_spi *bs, |
|---|
| 109 | + const char *dname) |
|---|
| 110 | +{ |
|---|
| 111 | + char name[64]; |
|---|
| 112 | + struct dentry *dir; |
|---|
| 113 | + |
|---|
| 114 | + /* get full name */ |
|---|
| 115 | + snprintf(name, sizeof(name), "spi-bcm2835aux-%s", dname); |
|---|
| 116 | + |
|---|
| 117 | + /* the base directory */ |
|---|
| 118 | + dir = debugfs_create_dir(name, NULL); |
|---|
| 119 | + bs->debugfs_dir = dir; |
|---|
| 120 | + |
|---|
| 121 | + /* the counters */ |
|---|
| 122 | + debugfs_create_u64("count_transfer_polling", 0444, dir, |
|---|
| 123 | + &bs->count_transfer_polling); |
|---|
| 124 | + debugfs_create_u64("count_transfer_irq", 0444, dir, |
|---|
| 125 | + &bs->count_transfer_irq); |
|---|
| 126 | + debugfs_create_u64("count_transfer_irq_after_poll", 0444, dir, |
|---|
| 127 | + &bs->count_transfer_irq_after_poll); |
|---|
| 128 | +} |
|---|
| 129 | + |
|---|
| 130 | +static void bcm2835aux_debugfs_remove(struct bcm2835aux_spi *bs) |
|---|
| 131 | +{ |
|---|
| 132 | + debugfs_remove_recursive(bs->debugfs_dir); |
|---|
| 133 | + bs->debugfs_dir = NULL; |
|---|
| 134 | +} |
|---|
| 135 | +#else |
|---|
| 136 | +static void bcm2835aux_debugfs_create(struct bcm2835aux_spi *bs, |
|---|
| 137 | + const char *dname) |
|---|
| 138 | +{ |
|---|
| 139 | +} |
|---|
| 140 | + |
|---|
| 141 | +static void bcm2835aux_debugfs_remove(struct bcm2835aux_spi *bs) |
|---|
| 142 | +{ |
|---|
| 143 | +} |
|---|
| 144 | +#endif /* CONFIG_DEBUG_FS */ |
|---|
| 106 | 145 | |
|---|
| 107 | 146 | static inline u32 bcm2835aux_rd(struct bcm2835aux_spi *bs, unsigned reg) |
|---|
| 108 | 147 | { |
|---|
| .. | .. |
|---|
| 123 | 162 | data = bcm2835aux_rd(bs, BCM2835_AUX_SPI_IO); |
|---|
| 124 | 163 | if (bs->rx_buf) { |
|---|
| 125 | 164 | switch (count) { |
|---|
| 126 | | - case 4: |
|---|
| 127 | | - *bs->rx_buf++ = (data >> 24) & 0xff; |
|---|
| 128 | | - /* fallthrough */ |
|---|
| 129 | 165 | case 3: |
|---|
| 130 | 166 | *bs->rx_buf++ = (data >> 16) & 0xff; |
|---|
| 131 | | - /* fallthrough */ |
|---|
| 167 | + fallthrough; |
|---|
| 132 | 168 | case 2: |
|---|
| 133 | 169 | *bs->rx_buf++ = (data >> 8) & 0xff; |
|---|
| 134 | | - /* fallthrough */ |
|---|
| 170 | + fallthrough; |
|---|
| 135 | 171 | case 1: |
|---|
| 136 | 172 | *bs->rx_buf++ = (data >> 0) & 0xff; |
|---|
| 137 | 173 | /* fallthrough - no default */ |
|---|
| .. | .. |
|---|
| 245 | 281 | { |
|---|
| 246 | 282 | struct bcm2835aux_spi *bs = spi_master_get_devdata(master); |
|---|
| 247 | 283 | |
|---|
| 284 | + /* update statistics */ |
|---|
| 285 | + bs->count_transfer_irq++; |
|---|
| 286 | + |
|---|
| 248 | 287 | /* fill in registers and fifos before enabling interrupts */ |
|---|
| 249 | 288 | bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL1, bs->cntl[1]); |
|---|
| 250 | 289 | bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL0, bs->cntl[0]); |
|---|
| .. | .. |
|---|
| 268 | 307 | struct bcm2835aux_spi *bs = spi_master_get_devdata(master); |
|---|
| 269 | 308 | unsigned long timeout; |
|---|
| 270 | 309 | |
|---|
| 310 | + /* update statistics */ |
|---|
| 311 | + bs->count_transfer_polling++; |
|---|
| 312 | + |
|---|
| 271 | 313 | /* configure spi */ |
|---|
| 272 | 314 | bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL1, bs->cntl[1]); |
|---|
| 273 | 315 | bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL0, bs->cntl[0]); |
|---|
| 274 | 316 | |
|---|
| 275 | | - /* set the timeout */ |
|---|
| 276 | | - timeout = jiffies + BCM2835_AUX_SPI_POLLING_JIFFIES; |
|---|
| 317 | + /* set the timeout to at least 2 jiffies */ |
|---|
| 318 | + timeout = jiffies + 2 + HZ * polling_limit_us / 1000000; |
|---|
| 277 | 319 | |
|---|
| 278 | 320 | /* loop until finished the transfer */ |
|---|
| 279 | 321 | while (bs->rx_len) { |
|---|
| .. | .. |
|---|
| 288 | 330 | jiffies - timeout, |
|---|
| 289 | 331 | bs->tx_len, bs->rx_len); |
|---|
| 290 | 332 | /* forward to interrupt handler */ |
|---|
| 333 | + bs->count_transfer_irq_after_poll++; |
|---|
| 291 | 334 | return __bcm2835aux_spi_transfer_one_irq(master, |
|---|
| 292 | 335 | spi, tfr); |
|---|
| 293 | 336 | } |
|---|
| .. | .. |
|---|
| 303 | 346 | { |
|---|
| 304 | 347 | struct bcm2835aux_spi *bs = spi_master_get_devdata(master); |
|---|
| 305 | 348 | unsigned long spi_hz, clk_hz, speed; |
|---|
| 306 | | - unsigned long spi_used_hz; |
|---|
| 349 | + unsigned long hz_per_byte, byte_limit; |
|---|
| 307 | 350 | |
|---|
| 308 | 351 | /* calculate the registers to handle |
|---|
| 309 | 352 | * |
|---|
| .. | .. |
|---|
| 331 | 374 | /* set the new speed */ |
|---|
| 332 | 375 | bs->cntl[0] |= speed << BCM2835_AUX_SPI_CNTL0_SPEED_SHIFT; |
|---|
| 333 | 376 | |
|---|
| 334 | | - spi_used_hz = clk_hz / (2 * (speed + 1)); |
|---|
| 377 | + tfr->effective_speed_hz = clk_hz / (2 * (speed + 1)); |
|---|
| 335 | 378 | |
|---|
| 336 | 379 | /* set transmit buffers and length */ |
|---|
| 337 | 380 | bs->tx_buf = tfr->tx_buf; |
|---|
| .. | .. |
|---|
| 347 | 390 | * of Hz per byte per polling limit. E.g., we can transfer 1 byte in |
|---|
| 348 | 391 | * 30 µs per 300,000 Hz of bus clock. |
|---|
| 349 | 392 | */ |
|---|
| 350 | | -#define HZ_PER_BYTE ((9 * 1000000) / BCM2835_AUX_SPI_POLLING_LIMIT_US) |
|---|
| 393 | + hz_per_byte = polling_limit_us ? (9 * 1000000) / polling_limit_us : 0; |
|---|
| 394 | + byte_limit = hz_per_byte ? tfr->effective_speed_hz / hz_per_byte : 1; |
|---|
| 395 | + |
|---|
| 351 | 396 | /* run in polling mode for short transfers */ |
|---|
| 352 | | - if (tfr->len < spi_used_hz / HZ_PER_BYTE) |
|---|
| 397 | + if (tfr->len < byte_limit) |
|---|
| 353 | 398 | return bcm2835aux_spi_transfer_one_poll(master, spi, tfr); |
|---|
| 354 | 399 | |
|---|
| 355 | 400 | /* run in interrupt mode for all others */ |
|---|
| 356 | 401 | return bcm2835aux_spi_transfer_one_irq(master, spi, tfr); |
|---|
| 357 | | -#undef HZ_PER_BYTE |
|---|
| 358 | 402 | } |
|---|
| 359 | 403 | |
|---|
| 360 | 404 | static int bcm2835aux_spi_prepare_message(struct spi_master *master, |
|---|
| .. | .. |
|---|
| 399 | 443 | bcm2835aux_spi_reset_hw(bs); |
|---|
| 400 | 444 | } |
|---|
| 401 | 445 | |
|---|
| 446 | +static int bcm2835aux_spi_setup(struct spi_device *spi) |
|---|
| 447 | +{ |
|---|
| 448 | + int ret; |
|---|
| 449 | + |
|---|
| 450 | + /* sanity check for native cs */ |
|---|
| 451 | + if (spi->mode & SPI_NO_CS) |
|---|
| 452 | + return 0; |
|---|
| 453 | + if (gpio_is_valid(spi->cs_gpio)) { |
|---|
| 454 | + /* with gpio-cs set the GPIO to the correct level |
|---|
| 455 | + * and as output (in case the dt has the gpio not configured |
|---|
| 456 | + * as output but native cs) |
|---|
| 457 | + */ |
|---|
| 458 | + ret = gpio_direction_output(spi->cs_gpio, |
|---|
| 459 | + (spi->mode & SPI_CS_HIGH) ? 0 : 1); |
|---|
| 460 | + if (ret) |
|---|
| 461 | + dev_err(&spi->dev, |
|---|
| 462 | + "could not set gpio %i as output: %i\n", |
|---|
| 463 | + spi->cs_gpio, ret); |
|---|
| 464 | + |
|---|
| 465 | + return ret; |
|---|
| 466 | + } |
|---|
| 467 | + |
|---|
| 468 | + /* for dt-backwards compatibility: only support native on CS0 |
|---|
| 469 | + * known things not supported with broken native CS: |
|---|
| 470 | + * * multiple chip-selects: cs0-cs2 are all |
|---|
| 471 | + * simultaniously asserted whenever there is a transfer |
|---|
| 472 | + * this even includes SPI_NO_CS |
|---|
| 473 | + * * SPI_CS_HIGH: cs are always asserted low |
|---|
| 474 | + * * cs_change: cs is deasserted after each spi_transfer |
|---|
| 475 | + * * cs_delay_usec: cs is always deasserted one SCK cycle |
|---|
| 476 | + * after the last transfer |
|---|
| 477 | + * probably more... |
|---|
| 478 | + */ |
|---|
| 479 | + dev_warn(&spi->dev, |
|---|
| 480 | + "Native CS is not supported - please configure cs-gpio in device-tree\n"); |
|---|
| 481 | + |
|---|
| 482 | + if (spi->chip_select == 0) |
|---|
| 483 | + return 0; |
|---|
| 484 | + |
|---|
| 485 | + dev_warn(&spi->dev, "Native CS is not working for cs > 0\n"); |
|---|
| 486 | + |
|---|
| 487 | + return -EINVAL; |
|---|
| 488 | +} |
|---|
| 489 | + |
|---|
| 402 | 490 | static int bcm2835aux_spi_probe(struct platform_device *pdev) |
|---|
| 403 | 491 | { |
|---|
| 404 | 492 | struct spi_master *master; |
|---|
| 405 | 493 | struct bcm2835aux_spi *bs; |
|---|
| 406 | | - struct resource *res; |
|---|
| 407 | 494 | unsigned long clk_hz; |
|---|
| 408 | 495 | int err; |
|---|
| 409 | 496 | |
|---|
| 410 | 497 | master = devm_spi_alloc_master(&pdev->dev, sizeof(*bs)); |
|---|
| 411 | | - if (!master) { |
|---|
| 412 | | - dev_err(&pdev->dev, "spi_alloc_master() failed\n"); |
|---|
| 498 | + if (!master) |
|---|
| 413 | 499 | return -ENOMEM; |
|---|
| 414 | | - } |
|---|
| 415 | 500 | |
|---|
| 416 | 501 | platform_set_drvdata(pdev, master); |
|---|
| 417 | 502 | master->mode_bits = (SPI_CPOL | SPI_CS_HIGH | SPI_NO_CS); |
|---|
| .. | .. |
|---|
| 428 | 513 | * a spi_transfer |
|---|
| 429 | 514 | */ |
|---|
| 430 | 515 | master->num_chipselect = 1; |
|---|
| 516 | + master->setup = bcm2835aux_spi_setup; |
|---|
| 431 | 517 | master->transfer_one = bcm2835aux_spi_transfer_one; |
|---|
| 432 | 518 | master->handle_err = bcm2835aux_spi_handle_err; |
|---|
| 433 | 519 | master->prepare_message = bcm2835aux_spi_prepare_message; |
|---|
| .. | .. |
|---|
| 437 | 523 | bs = spi_master_get_devdata(master); |
|---|
| 438 | 524 | |
|---|
| 439 | 525 | /* the main area */ |
|---|
| 440 | | - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
|---|
| 441 | | - bs->regs = devm_ioremap_resource(&pdev->dev, res); |
|---|
| 526 | + bs->regs = devm_platform_ioremap_resource(pdev, 0); |
|---|
| 442 | 527 | if (IS_ERR(bs->regs)) |
|---|
| 443 | 528 | return PTR_ERR(bs->regs); |
|---|
| 444 | 529 | |
|---|
| 445 | 530 | bs->clk = devm_clk_get(&pdev->dev, NULL); |
|---|
| 446 | | - if ((!bs->clk) || (IS_ERR(bs->clk))) { |
|---|
| 531 | + if (IS_ERR(bs->clk)) { |
|---|
| 447 | 532 | err = PTR_ERR(bs->clk); |
|---|
| 448 | 533 | dev_err(&pdev->dev, "could not get clk: %d\n", err); |
|---|
| 449 | 534 | return err; |
|---|
| 450 | 535 | } |
|---|
| 451 | 536 | |
|---|
| 452 | 537 | bs->irq = platform_get_irq(pdev, 0); |
|---|
| 453 | | - if (bs->irq <= 0) { |
|---|
| 454 | | - dev_err(&pdev->dev, "could not get IRQ: %d\n", bs->irq); |
|---|
| 538 | + if (bs->irq <= 0) |
|---|
| 455 | 539 | return bs->irq ? bs->irq : -ENODEV; |
|---|
| 456 | | - } |
|---|
| 457 | 540 | |
|---|
| 458 | 541 | /* this also enables the HW block */ |
|---|
| 459 | 542 | err = clk_prepare_enable(bs->clk); |
|---|
| .. | .. |
|---|
| 488 | 571 | goto out_clk_disable; |
|---|
| 489 | 572 | } |
|---|
| 490 | 573 | |
|---|
| 574 | + bcm2835aux_debugfs_create(bs, dev_name(&pdev->dev)); |
|---|
| 575 | + |
|---|
| 491 | 576 | return 0; |
|---|
| 492 | 577 | |
|---|
| 493 | 578 | out_clk_disable: |
|---|
| .. | .. |
|---|
| 499 | 584 | { |
|---|
| 500 | 585 | struct spi_master *master = platform_get_drvdata(pdev); |
|---|
| 501 | 586 | struct bcm2835aux_spi *bs = spi_master_get_devdata(master); |
|---|
| 587 | + |
|---|
| 588 | + bcm2835aux_debugfs_remove(bs); |
|---|
| 502 | 589 | |
|---|
| 503 | 590 | spi_unregister_master(master); |
|---|
| 504 | 591 | |
|---|
| .. | .. |
|---|
| 528 | 615 | |
|---|
| 529 | 616 | MODULE_DESCRIPTION("SPI controller driver for Broadcom BCM2835 aux"); |
|---|
| 530 | 617 | MODULE_AUTHOR("Martin Sperl <kernel@martin.sperl.org>"); |
|---|
| 531 | | -MODULE_LICENSE("GPL v2"); |
|---|
| 618 | +MODULE_LICENSE("GPL"); |
|---|