File Index
Files and directories
Sign in
FILE

spi-rb4xx.c

File preview with syntax highlighting.

linux-6.19.14/drivers/spi/spi-rb4xx.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * SPI controller driver for the Mikrotik RB4xx boards
  4 *
  5 * Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org>
  6 * Copyright (C) 2015 Bert Vermeulen <bert@biot.com>
  7 *
  8 * This file was based on the patches for Linux 2.6.27.39 published by
  9 * MikroTik for their RouterBoard 4xx series devices.
 10 */
 11
 12#include <linux/kernel.h>
 13#include <linux/module.h>
 14#include <linux/platform_device.h>
 15#include <linux/clk.h>
 16#include <linux/spi/spi.h>
 17#include <linux/of.h>
 18
 19#define AR71XX_SPI_REG_FS		0x00	/* Function Select */
 20#define AR71XX_SPI_REG_CTRL		0x04	/* SPI Control */
 21#define AR71XX_SPI_REG_IOC		0x08	/* SPI I/O Control */
 22#define AR71XX_SPI_REG_RDS		0x0c	/* Read Data Shift */
 23
 24#define AR71XX_SPI_FS_GPIO		BIT(0)	/* Enable GPIO mode */
 25
 26#define AR71XX_SPI_IOC_DO		BIT(0)	/* Data Out pin */
 27#define AR71XX_SPI_IOC_CLK		BIT(8)	/* CLK pin */
 28#define AR71XX_SPI_IOC_CS(n)		BIT(16 + (n))
 29
 30struct rb4xx_spi {
 31	void __iomem *base;
 32	struct clk *clk;
 33};
 34
 35static inline u32 rb4xx_read(struct rb4xx_spi *rbspi, u32 reg)
 36{
 37	return __raw_readl(rbspi->base + reg);
 38}
 39
 40static inline void rb4xx_write(struct rb4xx_spi *rbspi, u32 reg, u32 value)
 41{
 42	__raw_writel(value, rbspi->base + reg);
 43}
 44
 45static inline void do_spi_clk(struct rb4xx_spi *rbspi, u32 spi_ioc, int value)
 46{
 47	u32 regval;
 48
 49	regval = spi_ioc;
 50	if (value & BIT(0))
 51		regval |= AR71XX_SPI_IOC_DO;
 52
 53	rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval);
 54	rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval | AR71XX_SPI_IOC_CLK);
 55}
 56
 57static void do_spi_byte(struct rb4xx_spi *rbspi, u32 spi_ioc, u8 byte)
 58{
 59	int i;
 60
 61	for (i = 7; i >= 0; i--)
 62		do_spi_clk(rbspi, spi_ioc, byte >> i);
 63}
 64
 65/* The CS2 pin is used to clock in a second bit per clock cycle. */
 66static inline void do_spi_clk_two(struct rb4xx_spi *rbspi, u32 spi_ioc,
 67				   u8 value)
 68{
 69	u32 regval;
 70
 71	regval = spi_ioc;
 72	if (value & BIT(1))
 73		regval |= AR71XX_SPI_IOC_DO;
 74	if (value & BIT(0))
 75		regval |= AR71XX_SPI_IOC_CS(2);
 76
 77	rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval);
 78	rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval | AR71XX_SPI_IOC_CLK);
 79}
 80
 81/* Two bits at a time, msb first */
 82static void do_spi_byte_two(struct rb4xx_spi *rbspi, u32 spi_ioc, u8 byte)
 83{
 84	do_spi_clk_two(rbspi, spi_ioc, byte >> 6);
 85	do_spi_clk_two(rbspi, spi_ioc, byte >> 4);
 86	do_spi_clk_two(rbspi, spi_ioc, byte >> 2);
 87	do_spi_clk_two(rbspi, spi_ioc, byte >> 0);
 88}
 89
 90static void rb4xx_set_cs(struct spi_device *spi, bool enable)
 91{
 92	struct rb4xx_spi *rbspi = spi_controller_get_devdata(spi->controller);
 93
 94	/*
 95	 * Setting CS is done along with bitbanging the actual values,
 96	 * since it's all on the same hardware register. However the
 97	 * CPLD needs CS deselected after every command.
 98	 */
 99	if (enable)
100		rb4xx_write(rbspi, AR71XX_SPI_REG_IOC,
101			    AR71XX_SPI_IOC_CS(0) | AR71XX_SPI_IOC_CS(1));
102}
103
104static int rb4xx_transfer_one(struct spi_controller *host,
105			      struct spi_device *spi, struct spi_transfer *t)
106{
107	struct rb4xx_spi *rbspi = spi_controller_get_devdata(host);
108	int i;
109	u32 spi_ioc;
110	u8 *rx_buf;
111	const u8 *tx_buf;
112
113	/*
114	 * Prime the SPI register with the SPI device selected. The m25p80 boot
115	 * flash and CPLD share the CS0 pin. This works because the CPLD's
116	 * command set was designed to almost not clash with that of the
117	 * boot flash.
118	 */
119	if (spi_get_chipselect(spi, 0) == 2)
120		/* MMC */
121		spi_ioc = AR71XX_SPI_IOC_CS(0);
122	else
123		/* Boot flash and CPLD */
124		spi_ioc = AR71XX_SPI_IOC_CS(1);
125
126	tx_buf = t->tx_buf;
127	rx_buf = t->rx_buf;
128	for (i = 0; i < t->len; ++i) {
129		if (t->tx_nbits == SPI_NBITS_DUAL)
130			/* CPLD can use two-wire transfers */
131			do_spi_byte_two(rbspi, spi_ioc, tx_buf[i]);
132		else
133			do_spi_byte(rbspi, spi_ioc, tx_buf[i]);
134		if (!rx_buf)
135			continue;
136		rx_buf[i] = rb4xx_read(rbspi, AR71XX_SPI_REG_RDS);
137	}
138	spi_finalize_current_transfer(host);
139
140	return 0;
141}
142
143static int rb4xx_spi_probe(struct platform_device *pdev)
144{
145	struct spi_controller *host;
146	struct clk *ahb_clk;
147	struct rb4xx_spi *rbspi;
148	int err;
149	void __iomem *spi_base;
150
151	spi_base = devm_platform_ioremap_resource(pdev, 0);
152	if (IS_ERR(spi_base))
153		return PTR_ERR(spi_base);
154
155	host = devm_spi_alloc_host(&pdev->dev, sizeof(*rbspi));
156	if (!host)
157		return -ENOMEM;
158
159	ahb_clk = devm_clk_get_enabled(&pdev->dev, "ahb");
160	if (IS_ERR(ahb_clk))
161		return PTR_ERR(ahb_clk);
162
163	host->dev.of_node = pdev->dev.of_node;
164	host->bus_num = 0;
165	host->num_chipselect = 3;
166	host->mode_bits = SPI_TX_DUAL;
167	host->bits_per_word_mask = SPI_BPW_MASK(8);
168	host->flags = SPI_CONTROLLER_MUST_TX;
169	host->transfer_one = rb4xx_transfer_one;
170	host->set_cs = rb4xx_set_cs;
171
172	rbspi = spi_controller_get_devdata(host);
173	rbspi->base = spi_base;
174	rbspi->clk = ahb_clk;
175
176	err = devm_spi_register_controller(&pdev->dev, host);
177	if (err) {
178		dev_err(&pdev->dev, "failed to register SPI host\n");
179		return err;
180	}
181
182	/* Enable SPI */
183	rb4xx_write(rbspi, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO);
184
185	return 0;
186}
187
188static const struct of_device_id rb4xx_spi_dt_match[] = {
189	{ .compatible = "mikrotik,rb4xx-spi" },
190	{ },
191};
192MODULE_DEVICE_TABLE(of, rb4xx_spi_dt_match);
193
194static struct platform_driver rb4xx_spi_drv = {
195	.probe = rb4xx_spi_probe,
196	.driver = {
197		.name = "rb4xx-spi",
198		.of_match_table = rb4xx_spi_dt_match,
199	},
200};
201
202module_platform_driver(rb4xx_spi_drv);
203
204MODULE_DESCRIPTION("Mikrotik RB4xx SPI controller driver");
205MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
206MODULE_AUTHOR("Bert Vermeulen <bert@biot.com>");
207MODULE_LICENSE("GPL v2");
~/ Read-only file preview.