// 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_host(&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");