1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2ca632f55SGrant Likely /*
3ca632f55SGrant Likely * SPI controller driver for the Atheros AR71XX/AR724X/AR913X SoCs
4ca632f55SGrant Likely *
5ca632f55SGrant Likely * Copyright (C) 2009-2011 Gabor Juhos <juhosg@openwrt.org>
6ca632f55SGrant Likely *
7ca632f55SGrant Likely * This driver has been based on the spi-gpio.c:
8ca632f55SGrant Likely * Copyright (C) 2006,2008 David Brownell
9ca632f55SGrant Likely */
10ca632f55SGrant Likely
11ca632f55SGrant Likely #include <linux/kernel.h>
12807cc4b1SGabor Juhos #include <linux/module.h>
13ca632f55SGrant Likely #include <linux/delay.h>
14ca632f55SGrant Likely #include <linux/spinlock.h>
15ca632f55SGrant Likely #include <linux/platform_device.h>
16ca632f55SGrant Likely #include <linux/io.h>
17ca632f55SGrant Likely #include <linux/spi/spi.h>
18d08de025SLuiz Angelo Daros de Luca #include <linux/spi/spi-mem.h>
19ca632f55SGrant Likely #include <linux/spi/spi_bitbang.h>
20ca632f55SGrant Likely #include <linux/bitops.h>
21440114fdSGabor Juhos #include <linux/clk.h>
22440114fdSGabor Juhos #include <linux/err.h>
23ca632f55SGrant Likely
24ca632f55SGrant Likely #define DRV_NAME "ath79-spi"
25ca632f55SGrant Likely
26440114fdSGabor Juhos #define ATH79_SPI_RRW_DELAY_FACTOR 12000
27440114fdSGabor Juhos #define MHZ (1000 * 1000)
28440114fdSGabor Juhos
29b172fd0cSAlban Bedel #define AR71XX_SPI_REG_FS 0x00 /* Function Select */
30b172fd0cSAlban Bedel #define AR71XX_SPI_REG_CTRL 0x04 /* SPI Control */
31b172fd0cSAlban Bedel #define AR71XX_SPI_REG_IOC 0x08 /* SPI I/O Control */
32b172fd0cSAlban Bedel #define AR71XX_SPI_REG_RDS 0x0c /* Read Data Shift */
33b172fd0cSAlban Bedel
34b172fd0cSAlban Bedel #define AR71XX_SPI_FS_GPIO BIT(0) /* Enable GPIO mode */
35b172fd0cSAlban Bedel
36b172fd0cSAlban Bedel #define AR71XX_SPI_IOC_DO BIT(0) /* Data Out pin */
37b172fd0cSAlban Bedel #define AR71XX_SPI_IOC_CLK BIT(8) /* CLK pin */
38b172fd0cSAlban Bedel #define AR71XX_SPI_IOC_CS(n) BIT(16 + (n))
39b172fd0cSAlban Bedel
40ca632f55SGrant Likely struct ath79_spi {
41ca632f55SGrant Likely struct spi_bitbang bitbang;
42ca632f55SGrant Likely u32 ioc_base;
43ca632f55SGrant Likely u32 reg_ctrl;
44ca632f55SGrant Likely void __iomem *base;
45440114fdSGabor Juhos struct clk *clk;
46da470d6aSAravind Thokala unsigned int rrw_delay;
47ca632f55SGrant Likely };
48ca632f55SGrant Likely
ath79_spi_rr(struct ath79_spi * sp,unsigned int reg)49da470d6aSAravind Thokala static inline u32 ath79_spi_rr(struct ath79_spi *sp, unsigned int reg)
50ca632f55SGrant Likely {
51ca632f55SGrant Likely return ioread32(sp->base + reg);
52ca632f55SGrant Likely }
53ca632f55SGrant Likely
ath79_spi_wr(struct ath79_spi * sp,unsigned int reg,u32 val)54da470d6aSAravind Thokala static inline void ath79_spi_wr(struct ath79_spi *sp, unsigned int reg, u32 val)
55ca632f55SGrant Likely {
56ca632f55SGrant Likely iowrite32(val, sp->base + reg);
57ca632f55SGrant Likely }
58ca632f55SGrant Likely
ath79_spidev_to_sp(struct spi_device * spi)59ca632f55SGrant Likely static inline struct ath79_spi *ath79_spidev_to_sp(struct spi_device *spi)
60ca632f55SGrant Likely {
615aede90aSYang Yingliang return spi_controller_get_devdata(spi->controller);
62ca632f55SGrant Likely }
63ca632f55SGrant Likely
ath79_spi_delay(struct ath79_spi * sp,unsigned int nsecs)64da470d6aSAravind Thokala static inline void ath79_spi_delay(struct ath79_spi *sp, unsigned int nsecs)
65440114fdSGabor Juhos {
66440114fdSGabor Juhos if (nsecs > sp->rrw_delay)
67440114fdSGabor Juhos ndelay(nsecs - sp->rrw_delay);
68440114fdSGabor Juhos }
69440114fdSGabor Juhos
ath79_spi_chipselect(struct spi_device * spi,int is_active)70ca632f55SGrant Likely static void ath79_spi_chipselect(struct spi_device *spi, int is_active)
71ca632f55SGrant Likely {
72ca632f55SGrant Likely struct ath79_spi *sp = ath79_spidev_to_sp(spi);
73ca632f55SGrant Likely int cs_high = (spi->mode & SPI_CS_HIGH) ? is_active : !is_active;
749e264f3fSAmit Kumar Mahapatra via Alsa-devel u32 cs_bit = AR71XX_SPI_IOC_CS(spi_get_chipselect(spi, 0));
7522c76326SFelix Fietkau
76ca632f55SGrant Likely if (cs_high)
7722c76326SFelix Fietkau sp->ioc_base |= cs_bit;
78ca632f55SGrant Likely else
7922c76326SFelix Fietkau sp->ioc_base &= ~cs_bit;
80ca632f55SGrant Likely
81ca632f55SGrant Likely ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base);
82ca632f55SGrant Likely }
83ca632f55SGrant Likely
ath79_spi_enable(struct ath79_spi * sp)84c4a31f43SGabor Juhos static void ath79_spi_enable(struct ath79_spi *sp)
85ca632f55SGrant Likely {
86ca632f55SGrant Likely /* enable GPIO mode */
87ca632f55SGrant Likely ath79_spi_wr(sp, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO);
88ca632f55SGrant Likely
89ca632f55SGrant Likely /* save CTRL register */
90ca632f55SGrant Likely sp->reg_ctrl = ath79_spi_rr(sp, AR71XX_SPI_REG_CTRL);
91ca632f55SGrant Likely sp->ioc_base = ath79_spi_rr(sp, AR71XX_SPI_REG_IOC);
92ca632f55SGrant Likely
93797622d7SAlban Bedel /* clear clk and mosi in the base state */
94797622d7SAlban Bedel sp->ioc_base &= ~(AR71XX_SPI_IOC_DO | AR71XX_SPI_IOC_CLK);
95797622d7SAlban Bedel
96ca632f55SGrant Likely /* TODO: setup speed? */
97ca632f55SGrant Likely ath79_spi_wr(sp, AR71XX_SPI_REG_CTRL, 0x43);
98c4a31f43SGabor Juhos }
99c4a31f43SGabor Juhos
ath79_spi_disable(struct ath79_spi * sp)100c4a31f43SGabor Juhos static void ath79_spi_disable(struct ath79_spi *sp)
101c4a31f43SGabor Juhos {
102c4a31f43SGabor Juhos /* restore CTRL register */
103c4a31f43SGabor Juhos ath79_spi_wr(sp, AR71XX_SPI_REG_CTRL, sp->reg_ctrl);
104c4a31f43SGabor Juhos /* disable GPIO mode */
105c4a31f43SGabor Juhos ath79_spi_wr(sp, AR71XX_SPI_REG_FS, 0);
106c4a31f43SGabor Juhos }
107c4a31f43SGabor Juhos
ath79_spi_txrx_mode0(struct spi_device * spi,unsigned int nsecs,u32 word,u8 bits,unsigned flags)108da470d6aSAravind Thokala static u32 ath79_spi_txrx_mode0(struct spi_device *spi, unsigned int nsecs,
109304d3436SLorenzo Bianconi u32 word, u8 bits, unsigned flags)
110ca632f55SGrant Likely {
111ca632f55SGrant Likely struct ath79_spi *sp = ath79_spidev_to_sp(spi);
112ca632f55SGrant Likely u32 ioc = sp->ioc_base;
113ca632f55SGrant Likely
114ca632f55SGrant Likely /* clock starts at inactive polarity */
115ca632f55SGrant Likely for (word <<= (32 - bits); likely(bits); bits--) {
116ca632f55SGrant Likely u32 out;
117ca632f55SGrant Likely
118ca632f55SGrant Likely if (word & (1 << 31))
119ca632f55SGrant Likely out = ioc | AR71XX_SPI_IOC_DO;
120ca632f55SGrant Likely else
121ca632f55SGrant Likely out = ioc & ~AR71XX_SPI_IOC_DO;
122ca632f55SGrant Likely
1235aede90aSYang Yingliang /* setup MSB (to target) on trailing edge */
124ca632f55SGrant Likely ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, out);
125440114fdSGabor Juhos ath79_spi_delay(sp, nsecs);
126ca632f55SGrant Likely ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, out | AR71XX_SPI_IOC_CLK);
127440114fdSGabor Juhos ath79_spi_delay(sp, nsecs);
12872611db0SGabor Juhos if (bits == 1)
12972611db0SGabor Juhos ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, out);
130ca632f55SGrant Likely
131ca632f55SGrant Likely word <<= 1;
132ca632f55SGrant Likely }
133ca632f55SGrant Likely
134ca632f55SGrant Likely return ath79_spi_rr(sp, AR71XX_SPI_REG_RDS);
135ca632f55SGrant Likely }
136ca632f55SGrant Likely
ath79_exec_mem_op(struct spi_mem * mem,const struct spi_mem_op * op)137d08de025SLuiz Angelo Daros de Luca static int ath79_exec_mem_op(struct spi_mem *mem,
138d08de025SLuiz Angelo Daros de Luca const struct spi_mem_op *op)
139d08de025SLuiz Angelo Daros de Luca {
140d08de025SLuiz Angelo Daros de Luca struct ath79_spi *sp = ath79_spidev_to_sp(mem->spi);
141d08de025SLuiz Angelo Daros de Luca
142d08de025SLuiz Angelo Daros de Luca /* Ensures that reading is performed on device connected to hardware cs0 */
1439e264f3fSAmit Kumar Mahapatra via Alsa-devel if (spi_get_chipselect(mem->spi, 0) || spi_get_csgpiod(mem->spi, 0))
144d08de025SLuiz Angelo Daros de Luca return -ENOTSUPP;
145d08de025SLuiz Angelo Daros de Luca
146d08de025SLuiz Angelo Daros de Luca /* Only use for fast-read op. */
147d08de025SLuiz Angelo Daros de Luca if (op->cmd.opcode != 0x0b || op->data.dir != SPI_MEM_DATA_IN ||
148d08de025SLuiz Angelo Daros de Luca op->addr.nbytes != 3 || op->dummy.nbytes != 1)
149d08de025SLuiz Angelo Daros de Luca return -ENOTSUPP;
150d08de025SLuiz Angelo Daros de Luca
151d08de025SLuiz Angelo Daros de Luca /* disable GPIO mode */
152d08de025SLuiz Angelo Daros de Luca ath79_spi_wr(sp, AR71XX_SPI_REG_FS, 0);
153d08de025SLuiz Angelo Daros de Luca
154d08de025SLuiz Angelo Daros de Luca memcpy_fromio(op->data.buf.in, sp->base + op->addr.val, op->data.nbytes);
155d08de025SLuiz Angelo Daros de Luca
156d08de025SLuiz Angelo Daros de Luca /* enable GPIO mode */
157d08de025SLuiz Angelo Daros de Luca ath79_spi_wr(sp, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO);
158d08de025SLuiz Angelo Daros de Luca
159d08de025SLuiz Angelo Daros de Luca /* restore IOC register */
160d08de025SLuiz Angelo Daros de Luca ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base);
161d08de025SLuiz Angelo Daros de Luca
162d08de025SLuiz Angelo Daros de Luca return 0;
163d08de025SLuiz Angelo Daros de Luca }
164d08de025SLuiz Angelo Daros de Luca
165d08de025SLuiz Angelo Daros de Luca static const struct spi_controller_mem_ops ath79_mem_ops = {
166d08de025SLuiz Angelo Daros de Luca .exec_op = ath79_exec_mem_op,
167d08de025SLuiz Angelo Daros de Luca };
168d08de025SLuiz Angelo Daros de Luca
ath79_spi_probe(struct platform_device * pdev)169fd4a319bSGrant Likely static int ath79_spi_probe(struct platform_device *pdev)
170ca632f55SGrant Likely {
1715aede90aSYang Yingliang struct spi_controller *host;
172ca632f55SGrant Likely struct ath79_spi *sp;
173440114fdSGabor Juhos unsigned long rate;
174ca632f55SGrant Likely int ret;
175ca632f55SGrant Likely
1765aede90aSYang Yingliang host = spi_alloc_host(&pdev->dev, sizeof(*sp));
1775aede90aSYang Yingliang if (host == NULL) {
1785aede90aSYang Yingliang dev_err(&pdev->dev, "failed to allocate spi host\n");
179ca632f55SGrant Likely return -ENOMEM;
180ca632f55SGrant Likely }
181ca632f55SGrant Likely
1825aede90aSYang Yingliang sp = spi_controller_get_devdata(host);
1835aede90aSYang Yingliang host->dev.of_node = pdev->dev.of_node;
184ca632f55SGrant Likely platform_set_drvdata(pdev, sp);
185ca632f55SGrant Likely
1865aede90aSYang Yingliang host->use_gpio_descriptors = true;
1875aede90aSYang Yingliang host->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
188*82238d2cSAndy Shevchenko host->flags = SPI_CONTROLLER_GPIO_SS;
1895aede90aSYang Yingliang host->num_chipselect = 3;
1905aede90aSYang Yingliang host->mem_ops = &ath79_mem_ops;
191ca632f55SGrant Likely
1925aede90aSYang Yingliang sp->bitbang.master = host;
193ca632f55SGrant Likely sp->bitbang.chipselect = ath79_spi_chipselect;
194ca632f55SGrant Likely sp->bitbang.txrx_word[SPI_MODE_0] = ath79_spi_txrx_mode0;
195ca632f55SGrant Likely sp->bitbang.flags = SPI_CS_HIGH;
196ca632f55SGrant Likely
197bf348419SYueHaibing sp->base = devm_platform_ioremap_resource(pdev, 0);
198b7a2a1c0SHeiner Kallweit if (IS_ERR(sp->base)) {
199b7a2a1c0SHeiner Kallweit ret = PTR_ERR(sp->base);
2005aede90aSYang Yingliang goto err_put_host;
201ca632f55SGrant Likely }
202ca632f55SGrant Likely
203a6f4c8e0SJingoo Han sp->clk = devm_clk_get(&pdev->dev, "ahb");
204440114fdSGabor Juhos if (IS_ERR(sp->clk)) {
205440114fdSGabor Juhos ret = PTR_ERR(sp->clk);
2065aede90aSYang Yingliang goto err_put_host;
207440114fdSGabor Juhos }
208440114fdSGabor Juhos
2093e19acdcSAlban Bedel ret = clk_prepare_enable(sp->clk);
210440114fdSGabor Juhos if (ret)
2115aede90aSYang Yingliang goto err_put_host;
212440114fdSGabor Juhos
213440114fdSGabor Juhos rate = DIV_ROUND_UP(clk_get_rate(sp->clk), MHZ);
214440114fdSGabor Juhos if (!rate) {
215440114fdSGabor Juhos ret = -EINVAL;
216440114fdSGabor Juhos goto err_clk_disable;
217440114fdSGabor Juhos }
218440114fdSGabor Juhos
219440114fdSGabor Juhos sp->rrw_delay = ATH79_SPI_RRW_DELAY_FACTOR / rate;
220440114fdSGabor Juhos dev_dbg(&pdev->dev, "register read/write delay is %u nsecs\n",
221440114fdSGabor Juhos sp->rrw_delay);
222440114fdSGabor Juhos
223c4a31f43SGabor Juhos ath79_spi_enable(sp);
224ca632f55SGrant Likely ret = spi_bitbang_start(&sp->bitbang);
225ca632f55SGrant Likely if (ret)
226c4a31f43SGabor Juhos goto err_disable;
227ca632f55SGrant Likely
228ca632f55SGrant Likely return 0;
229ca632f55SGrant Likely
230c4a31f43SGabor Juhos err_disable:
231c4a31f43SGabor Juhos ath79_spi_disable(sp);
232440114fdSGabor Juhos err_clk_disable:
2333e19acdcSAlban Bedel clk_disable_unprepare(sp->clk);
2345aede90aSYang Yingliang err_put_host:
2355aede90aSYang Yingliang spi_controller_put(host);
236ca632f55SGrant Likely
237ca632f55SGrant Likely return ret;
238ca632f55SGrant Likely }
239ca632f55SGrant Likely
ath79_spi_remove(struct platform_device * pdev)24048c42f97SUwe Kleine-König static void ath79_spi_remove(struct platform_device *pdev)
241ca632f55SGrant Likely {
242ca632f55SGrant Likely struct ath79_spi *sp = platform_get_drvdata(pdev);
243ca632f55SGrant Likely
244ca632f55SGrant Likely spi_bitbang_stop(&sp->bitbang);
245c4a31f43SGabor Juhos ath79_spi_disable(sp);
2463e19acdcSAlban Bedel clk_disable_unprepare(sp->clk);
2475aede90aSYang Yingliang spi_controller_put(sp->bitbang.master);
248ca632f55SGrant Likely }
249ca632f55SGrant Likely
ath79_spi_shutdown(struct platform_device * pdev)2507410e848SGabor Juhos static void ath79_spi_shutdown(struct platform_device *pdev)
2517410e848SGabor Juhos {
2527410e848SGabor Juhos ath79_spi_remove(pdev);
2537410e848SGabor Juhos }
2547410e848SGabor Juhos
25585f62476SAlban Bedel static const struct of_device_id ath79_spi_of_match[] = {
25685f62476SAlban Bedel { .compatible = "qca,ar7100-spi", },
25785f62476SAlban Bedel { },
25885f62476SAlban Bedel };
259d7a32394SJavier Martinez Canillas MODULE_DEVICE_TABLE(of, ath79_spi_of_match);
26085f62476SAlban Bedel
261ca632f55SGrant Likely static struct platform_driver ath79_spi_driver = {
262ca632f55SGrant Likely .probe = ath79_spi_probe,
26348c42f97SUwe Kleine-König .remove_new = ath79_spi_remove,
2647410e848SGabor Juhos .shutdown = ath79_spi_shutdown,
265ca632f55SGrant Likely .driver = {
266ca632f55SGrant Likely .name = DRV_NAME,
26785f62476SAlban Bedel .of_match_table = ath79_spi_of_match,
268ca632f55SGrant Likely },
269ca632f55SGrant Likely };
270940ab889SGrant Likely module_platform_driver(ath79_spi_driver);
271ca632f55SGrant Likely
272ca632f55SGrant Likely MODULE_DESCRIPTION("SPI controller driver for Atheros AR71XX/AR724X/AR913X");
273ca632f55SGrant Likely MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
274ca632f55SGrant Likely MODULE_LICENSE("GPL v2");
275ca632f55SGrant Likely MODULE_ALIAS("platform:" DRV_NAME);
276