xref: /openbmc/linux/drivers/spi/spi-ath79.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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