diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-02-22 09:19:08 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-02-22 09:19:08 -0800 |
| commit | a2590d69893f232cbb79d149dbbb456a1febca22 (patch) | |
| tree | 870326105cf0ff810fe9778764b276324fc6b1b9 /drivers/spi/spi-realtek-rtl.c | |
| parent | d6560052c2f73db59834e9a3c0aba20579aa7059 (diff) | |
| parent | eec262d179ff60e8d12298ab2f118661040e0bf5 (diff) | |
Merge tag 'spi-v5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
Pull spi updates from Mark Brown:
"The main focus of this release from a framework point of view has been
spi-mem where we've acquired support for a few new hardware features
which enable better performance on suitable hardware.
Otherwise mostly thanks to Arnd's cleanup efforts on old platforms
we've removed several obsolete drivers which just about balance out
the newer drivers we've added this cycle.
Summary:
- Allow drivers to flag if they are unidirectional.
- Support for DTR mode and hardware acceleration of dummy cycles in
spi-mem.
- Support for Allwinder H616, Intel Lightning Mountain, nVidia Tegra
QuadSPI, Realtek RTL838x and RTL839x.
- Removal of obsolete EFM32, Txx9 and SIRF Prima and Atlas drivers"
* tag 'spi-v5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (76 commits)
spi: Skip zero-length transfers in spi_transfer_one_message()
spi: dw: Avoid stack content exposure
spi: cadence-quadspi: Use spi_mem_dtr_supports_op()
spi: spi-mem: add spi_mem_dtr_supports_op()
spi: atmel-quadspi: Disable the QSPI IP at suspend()
spi: pxa2xx: Add IDs for the controllers found on Intel Lynxpoint
spi: pxa2xx: Fix the controller numbering for Wildcat Point
spi: Change provied to provided in the file spi.h
spi: mediatek: add set_cs_timing support
spi: support CS timing for HW & SW mode
spi: add power control when set_cs_timing
spi: stm32: make spurious and overrun interrupts visible
spi: stm32h7: replace private SPI_1HZ_NS with NSEC_PER_SEC
spi: stm32: defer probe for reset
spi: stm32: driver uses reset controller only at init
spi: stm32h7: ensure message are smaller than max size
spi: stm32: use bitfield macros
spi: stm32: do not mandate cs_gpio
spi: stm32: properly handle 0 byte transfer
spi: clps711xx: remove redundant white-space
...
Diffstat (limited to 'drivers/spi/spi-realtek-rtl.c')
| -rw-r--r-- | drivers/spi/spi-realtek-rtl.c | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/drivers/spi/spi-realtek-rtl.c b/drivers/spi/spi-realtek-rtl.c new file mode 100644 index 000000000000..866b0477dbd7 --- /dev/null +++ b/drivers/spi/spi-realtek-rtl.c @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/mod_devicetable.h> +#include <linux/spi/spi.h> + +struct rtspi { + void __iomem *base; +}; + +/* SPI Flash Configuration Register */ +#define RTL_SPI_SFCR 0x00 +#define RTL_SPI_SFCR_RBO BIT(28) +#define RTL_SPI_SFCR_WBO BIT(27) + +/* SPI Flash Control and Status Register */ +#define RTL_SPI_SFCSR 0x08 +#define RTL_SPI_SFCSR_CSB0 BIT(31) +#define RTL_SPI_SFCSR_CSB1 BIT(30) +#define RTL_SPI_SFCSR_RDY BIT(27) +#define RTL_SPI_SFCSR_CS BIT(24) +#define RTL_SPI_SFCSR_LEN_MASK ~(0x03 << 28) +#define RTL_SPI_SFCSR_LEN1 (0x00 << 28) +#define RTL_SPI_SFCSR_LEN4 (0x03 << 28) + +/* SPI Flash Data Register */ +#define RTL_SPI_SFDR 0x0c + +#define REG(x) (rtspi->base + x) + + +static void rt_set_cs(struct spi_device *spi, bool active) +{ + struct rtspi *rtspi = spi_controller_get_devdata(spi->controller); + u32 value; + + /* CS0 bit is active low */ + value = readl(REG(RTL_SPI_SFCSR)); + if (active) + value |= RTL_SPI_SFCSR_CSB0; + else + value &= ~RTL_SPI_SFCSR_CSB0; + writel(value, REG(RTL_SPI_SFCSR)); +} + +static void set_size(struct rtspi *rtspi, int size) +{ + u32 value; + + value = readl(REG(RTL_SPI_SFCSR)); + value &= RTL_SPI_SFCSR_LEN_MASK; + if (size == 4) + value |= RTL_SPI_SFCSR_LEN4; + else if (size == 1) + value |= RTL_SPI_SFCSR_LEN1; + writel(value, REG(RTL_SPI_SFCSR)); +} + +static inline void wait_ready(struct rtspi *rtspi) +{ + while (!(readl(REG(RTL_SPI_SFCSR)) & RTL_SPI_SFCSR_RDY)) + cpu_relax(); +} +static void send4(struct rtspi *rtspi, const u32 *buf) +{ + wait_ready(rtspi); + set_size(rtspi, 4); + writel(*buf, REG(RTL_SPI_SFDR)); +} + +static void send1(struct rtspi *rtspi, const u8 *buf) +{ + wait_ready(rtspi); + set_size(rtspi, 1); + writel(buf[0] << 24, REG(RTL_SPI_SFDR)); +} + +static void rcv4(struct rtspi *rtspi, u32 *buf) +{ + wait_ready(rtspi); + set_size(rtspi, 4); + *buf = readl(REG(RTL_SPI_SFDR)); +} + +static void rcv1(struct rtspi *rtspi, u8 *buf) +{ + wait_ready(rtspi); + set_size(rtspi, 1); + *buf = readl(REG(RTL_SPI_SFDR)) >> 24; +} + +static int transfer_one(struct spi_controller *ctrl, struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct rtspi *rtspi = spi_controller_get_devdata(ctrl); + void *rx_buf; + const void *tx_buf; + int cnt; + + tx_buf = xfer->tx_buf; + rx_buf = xfer->rx_buf; + cnt = xfer->len; + if (tx_buf) { + while (cnt >= 4) { + send4(rtspi, tx_buf); + tx_buf += 4; + cnt -= 4; + } + while (cnt) { + send1(rtspi, tx_buf); + tx_buf++; + cnt--; + } + } else if (rx_buf) { + while (cnt >= 4) { + rcv4(rtspi, rx_buf); + rx_buf += 4; + cnt -= 4; + } + while (cnt) { + rcv1(rtspi, rx_buf); + rx_buf++; + cnt--; + } + } + + spi_finalize_current_transfer(ctrl); + + return 0; +} + +static void init_hw(struct rtspi *rtspi) +{ + u32 value; + + /* Turn on big-endian byte ordering */ + value = readl(REG(RTL_SPI_SFCR)); + value |= RTL_SPI_SFCR_RBO | RTL_SPI_SFCR_WBO; + writel(value, REG(RTL_SPI_SFCR)); + + value = readl(REG(RTL_SPI_SFCSR)); + /* Permanently disable CS1, since it's never used */ + value |= RTL_SPI_SFCSR_CSB1; + /* Select CS0 for use */ + value &= RTL_SPI_SFCSR_CS; + writel(value, REG(RTL_SPI_SFCSR)); +} + +static int realtek_rtl_spi_probe(struct platform_device *pdev) +{ + struct spi_controller *ctrl; + struct rtspi *rtspi; + int err; + + ctrl = devm_spi_alloc_master(&pdev->dev, sizeof(*rtspi)); + if (!ctrl) { + dev_err(&pdev->dev, "Error allocating SPI controller\n"); + return -ENOMEM; + } + platform_set_drvdata(pdev, ctrl); + rtspi = spi_controller_get_devdata(ctrl); + + rtspi->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + if (IS_ERR(rtspi->base)) { + dev_err(&pdev->dev, "Could not map SPI register address"); + return -ENOMEM; + } + + init_hw(rtspi); + + ctrl->dev.of_node = pdev->dev.of_node; + ctrl->flags = SPI_CONTROLLER_HALF_DUPLEX; + ctrl->set_cs = rt_set_cs; + ctrl->transfer_one = transfer_one; + + err = devm_spi_register_controller(&pdev->dev, ctrl); + if (err) { + dev_err(&pdev->dev, "Could not register SPI controller\n"); + return -ENODEV; + } + + return 0; +} + + +static const struct of_device_id realtek_rtl_spi_of_ids[] = { + { .compatible = "realtek,rtl8380-spi" }, + { .compatible = "realtek,rtl8382-spi" }, + { .compatible = "realtek,rtl8391-spi" }, + { .compatible = "realtek,rtl8392-spi" }, + { .compatible = "realtek,rtl8393-spi" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, realtek_rtl_spi_of_ids); + +static struct platform_driver realtek_rtl_spi_driver = { + .probe = realtek_rtl_spi_probe, + .driver = { + .name = "realtek-rtl-spi", + .of_match_table = realtek_rtl_spi_of_ids, + }, +}; + +module_platform_driver(realtek_rtl_spi_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Bert Vermeulen <bert@biot.com>"); +MODULE_DESCRIPTION("Realtek RTL SPI driver"); |
