xref: /linux/drivers/spi/spi-oc-tiny.c (revision ca55b2fef3a9373fcfc30f82fd26bc7fccbda732)
1 /*
2  * OpenCores tiny SPI master driver
3  *
4  * http://opencores.org/project,tiny_spi
5  *
6  * Copyright (C) 2011 Thomas Chou <thomas@wytron.com.tw>
7  *
8  * Based on spi_s3c24xx.c, which is:
9  * Copyright (c) 2006 Ben Dooks
10  * Copyright (c) 2006 Simtec Electronics
11  *	Ben Dooks <ben@simtec.co.uk>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License version 2 as
15  * published by the Free Software Foundation.
16  */
17 
18 #include <linux/interrupt.h>
19 #include <linux/errno.h>
20 #include <linux/module.h>
21 #include <linux/platform_device.h>
22 #include <linux/spi/spi.h>
23 #include <linux/spi/spi_bitbang.h>
24 #include <linux/spi/spi_oc_tiny.h>
25 #include <linux/io.h>
26 #include <linux/gpio.h>
27 #include <linux/of.h>
28 
29 #define DRV_NAME "spi_oc_tiny"
30 
31 #define TINY_SPI_RXDATA 0
32 #define TINY_SPI_TXDATA 4
33 #define TINY_SPI_STATUS 8
34 #define TINY_SPI_CONTROL 12
35 #define TINY_SPI_BAUD 16
36 
37 #define TINY_SPI_STATUS_TXE 0x1
38 #define TINY_SPI_STATUS_TXR 0x2
39 
40 struct tiny_spi {
41 	/* bitbang has to be first */
42 	struct spi_bitbang bitbang;
43 	struct completion done;
44 
45 	void __iomem *base;
46 	int irq;
47 	unsigned int freq;
48 	unsigned int baudwidth;
49 	unsigned int baud;
50 	unsigned int speed_hz;
51 	unsigned int mode;
52 	unsigned int len;
53 	unsigned int txc, rxc;
54 	const u8 *txp;
55 	u8 *rxp;
56 	int gpio_cs_count;
57 	int *gpio_cs;
58 };
59 
60 static inline struct tiny_spi *tiny_spi_to_hw(struct spi_device *sdev)
61 {
62 	return spi_master_get_devdata(sdev->master);
63 }
64 
65 static unsigned int tiny_spi_baud(struct spi_device *spi, unsigned int hz)
66 {
67 	struct tiny_spi *hw = tiny_spi_to_hw(spi);
68 
69 	return min(DIV_ROUND_UP(hw->freq, hz * 2), (1U << hw->baudwidth)) - 1;
70 }
71 
72 static void tiny_spi_chipselect(struct spi_device *spi, int is_active)
73 {
74 	struct tiny_spi *hw = tiny_spi_to_hw(spi);
75 
76 	if (hw->gpio_cs_count > 0) {
77 		gpio_set_value(hw->gpio_cs[spi->chip_select],
78 			(spi->mode & SPI_CS_HIGH) ? is_active : !is_active);
79 	}
80 }
81 
82 static int tiny_spi_setup_transfer(struct spi_device *spi,
83 				   struct spi_transfer *t)
84 {
85 	struct tiny_spi *hw = tiny_spi_to_hw(spi);
86 	unsigned int baud = hw->baud;
87 
88 	if (t) {
89 		if (t->speed_hz && t->speed_hz != hw->speed_hz)
90 			baud = tiny_spi_baud(spi, t->speed_hz);
91 	}
92 	writel(baud, hw->base + TINY_SPI_BAUD);
93 	writel(hw->mode, hw->base + TINY_SPI_CONTROL);
94 	return 0;
95 }
96 
97 static int tiny_spi_setup(struct spi_device *spi)
98 {
99 	struct tiny_spi *hw = tiny_spi_to_hw(spi);
100 
101 	if (spi->max_speed_hz != hw->speed_hz) {
102 		hw->speed_hz = spi->max_speed_hz;
103 		hw->baud = tiny_spi_baud(spi, hw->speed_hz);
104 	}
105 	hw->mode = spi->mode & (SPI_CPOL | SPI_CPHA);
106 	return 0;
107 }
108 
109 static inline void tiny_spi_wait_txr(struct tiny_spi *hw)
110 {
111 	while (!(readb(hw->base + TINY_SPI_STATUS) &
112 		 TINY_SPI_STATUS_TXR))
113 		cpu_relax();
114 }
115 
116 static inline void tiny_spi_wait_txe(struct tiny_spi *hw)
117 {
118 	while (!(readb(hw->base + TINY_SPI_STATUS) &
119 		 TINY_SPI_STATUS_TXE))
120 		cpu_relax();
121 }
122 
123 static int tiny_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
124 {
125 	struct tiny_spi *hw = tiny_spi_to_hw(spi);
126 	const u8 *txp = t->tx_buf;
127 	u8 *rxp = t->rx_buf;
128 	unsigned int i;
129 
130 	if (hw->irq >= 0) {
131 		/* use interrupt driven data transfer */
132 		hw->len = t->len;
133 		hw->txp = t->tx_buf;
134 		hw->rxp = t->rx_buf;
135 		hw->txc = 0;
136 		hw->rxc = 0;
137 
138 		/* send the first byte */
139 		if (t->len > 1) {
140 			writeb(hw->txp ? *hw->txp++ : 0,
141 			       hw->base + TINY_SPI_TXDATA);
142 			hw->txc++;
143 			writeb(hw->txp ? *hw->txp++ : 0,
144 			       hw->base + TINY_SPI_TXDATA);
145 			hw->txc++;
146 			writeb(TINY_SPI_STATUS_TXR, hw->base + TINY_SPI_STATUS);
147 		} else {
148 			writeb(hw->txp ? *hw->txp++ : 0,
149 			       hw->base + TINY_SPI_TXDATA);
150 			hw->txc++;
151 			writeb(TINY_SPI_STATUS_TXE, hw->base + TINY_SPI_STATUS);
152 		}
153 
154 		wait_for_completion(&hw->done);
155 	} else {
156 		/* we need to tighten the transfer loop */
157 		writeb(txp ? *txp++ : 0, hw->base + TINY_SPI_TXDATA);
158 		for (i = 1; i < t->len; i++) {
159 			writeb(txp ? *txp++ : 0, hw->base + TINY_SPI_TXDATA);
160 
161 			if (rxp || (i != t->len - 1))
162 				tiny_spi_wait_txr(hw);
163 			if (rxp)
164 				*rxp++ = readb(hw->base + TINY_SPI_TXDATA);
165 		}
166 		tiny_spi_wait_txe(hw);
167 		if (rxp)
168 			*rxp++ = readb(hw->base + TINY_SPI_RXDATA);
169 	}
170 
171 	return t->len;
172 }
173 
174 static irqreturn_t tiny_spi_irq(int irq, void *dev)
175 {
176 	struct tiny_spi *hw = dev;
177 
178 	writeb(0, hw->base + TINY_SPI_STATUS);
179 	if (hw->rxc + 1 == hw->len) {
180 		if (hw->rxp)
181 			*hw->rxp++ = readb(hw->base + TINY_SPI_RXDATA);
182 		hw->rxc++;
183 		complete(&hw->done);
184 	} else {
185 		if (hw->rxp)
186 			*hw->rxp++ = readb(hw->base + TINY_SPI_TXDATA);
187 		hw->rxc++;
188 		if (hw->txc < hw->len) {
189 			writeb(hw->txp ? *hw->txp++ : 0,
190 			       hw->base + TINY_SPI_TXDATA);
191 			hw->txc++;
192 			writeb(TINY_SPI_STATUS_TXR,
193 			       hw->base + TINY_SPI_STATUS);
194 		} else {
195 			writeb(TINY_SPI_STATUS_TXE,
196 			       hw->base + TINY_SPI_STATUS);
197 		}
198 	}
199 	return IRQ_HANDLED;
200 }
201 
202 #ifdef CONFIG_OF
203 #include <linux/of_gpio.h>
204 
205 static int tiny_spi_of_probe(struct platform_device *pdev)
206 {
207 	struct tiny_spi *hw = platform_get_drvdata(pdev);
208 	struct device_node *np = pdev->dev.of_node;
209 	unsigned int i;
210 	const __be32 *val;
211 	int len;
212 
213 	if (!np)
214 		return 0;
215 	hw->gpio_cs_count = of_gpio_count(np);
216 	if (hw->gpio_cs_count > 0) {
217 		hw->gpio_cs = devm_kzalloc(&pdev->dev,
218 				hw->gpio_cs_count * sizeof(unsigned int),
219 				GFP_KERNEL);
220 		if (!hw->gpio_cs)
221 			return -ENOMEM;
222 	}
223 	for (i = 0; i < hw->gpio_cs_count; i++) {
224 		hw->gpio_cs[i] = of_get_gpio_flags(np, i, NULL);
225 		if (hw->gpio_cs[i] < 0)
226 			return -ENODEV;
227 	}
228 	hw->bitbang.master->dev.of_node = pdev->dev.of_node;
229 	val = of_get_property(pdev->dev.of_node,
230 			      "clock-frequency", &len);
231 	if (val && len >= sizeof(__be32))
232 		hw->freq = be32_to_cpup(val);
233 	val = of_get_property(pdev->dev.of_node, "baud-width", &len);
234 	if (val && len >= sizeof(__be32))
235 		hw->baudwidth = be32_to_cpup(val);
236 	return 0;
237 }
238 #else /* !CONFIG_OF */
239 static int tiny_spi_of_probe(struct platform_device *pdev)
240 {
241 	return 0;
242 }
243 #endif /* CONFIG_OF */
244 
245 static int tiny_spi_probe(struct platform_device *pdev)
246 {
247 	struct tiny_spi_platform_data *platp = dev_get_platdata(&pdev->dev);
248 	struct tiny_spi *hw;
249 	struct spi_master *master;
250 	struct resource *res;
251 	unsigned int i;
252 	int err = -ENODEV;
253 
254 	master = spi_alloc_master(&pdev->dev, sizeof(struct tiny_spi));
255 	if (!master)
256 		return err;
257 
258 	/* setup the master state. */
259 	master->bus_num = pdev->id;
260 	master->num_chipselect = 255;
261 	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
262 	master->setup = tiny_spi_setup;
263 
264 	hw = spi_master_get_devdata(master);
265 	platform_set_drvdata(pdev, hw);
266 
267 	/* setup the state for the bitbang driver */
268 	hw->bitbang.master = master;
269 	hw->bitbang.setup_transfer = tiny_spi_setup_transfer;
270 	hw->bitbang.chipselect = tiny_spi_chipselect;
271 	hw->bitbang.txrx_bufs = tiny_spi_txrx_bufs;
272 
273 	/* find and map our resources */
274 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
275 	hw->base = devm_ioremap_resource(&pdev->dev, res);
276 	if (IS_ERR(hw->base)) {
277 		err = PTR_ERR(hw->base);
278 		goto exit;
279 	}
280 	/* irq is optional */
281 	hw->irq = platform_get_irq(pdev, 0);
282 	if (hw->irq >= 0) {
283 		init_completion(&hw->done);
284 		err = devm_request_irq(&pdev->dev, hw->irq, tiny_spi_irq, 0,
285 				       pdev->name, hw);
286 		if (err)
287 			goto exit;
288 	}
289 	/* find platform data */
290 	if (platp) {
291 		hw->gpio_cs_count = platp->gpio_cs_count;
292 		hw->gpio_cs = platp->gpio_cs;
293 		if (platp->gpio_cs_count && !platp->gpio_cs) {
294 			err = -EBUSY;
295 			goto exit;
296 		}
297 		hw->freq = platp->freq;
298 		hw->baudwidth = platp->baudwidth;
299 	} else {
300 		err = tiny_spi_of_probe(pdev);
301 		if (err)
302 			goto exit;
303 	}
304 	for (i = 0; i < hw->gpio_cs_count; i++) {
305 		err = gpio_request(hw->gpio_cs[i], dev_name(&pdev->dev));
306 		if (err)
307 			goto exit_gpio;
308 		gpio_direction_output(hw->gpio_cs[i], 1);
309 	}
310 	hw->bitbang.master->num_chipselect = max(1, hw->gpio_cs_count);
311 
312 	/* register our spi controller */
313 	err = spi_bitbang_start(&hw->bitbang);
314 	if (err)
315 		goto exit;
316 	dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq);
317 
318 	return 0;
319 
320 exit_gpio:
321 	while (i-- > 0)
322 		gpio_free(hw->gpio_cs[i]);
323 exit:
324 	spi_master_put(master);
325 	return err;
326 }
327 
328 static int tiny_spi_remove(struct platform_device *pdev)
329 {
330 	struct tiny_spi *hw = platform_get_drvdata(pdev);
331 	struct spi_master *master = hw->bitbang.master;
332 	unsigned int i;
333 
334 	spi_bitbang_stop(&hw->bitbang);
335 	for (i = 0; i < hw->gpio_cs_count; i++)
336 		gpio_free(hw->gpio_cs[i]);
337 	spi_master_put(master);
338 	return 0;
339 }
340 
341 #ifdef CONFIG_OF
342 static const struct of_device_id tiny_spi_match[] = {
343 	{ .compatible = "opencores,tiny-spi-rtlsvn2", },
344 	{},
345 };
346 MODULE_DEVICE_TABLE(of, tiny_spi_match);
347 #endif /* CONFIG_OF */
348 
349 static struct platform_driver tiny_spi_driver = {
350 	.probe = tiny_spi_probe,
351 	.remove = tiny_spi_remove,
352 	.driver = {
353 		.name = DRV_NAME,
354 		.pm = NULL,
355 		.of_match_table = of_match_ptr(tiny_spi_match),
356 	},
357 };
358 module_platform_driver(tiny_spi_driver);
359 
360 MODULE_DESCRIPTION("OpenCores tiny SPI driver");
361 MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
362 MODULE_LICENSE("GPL");
363 MODULE_ALIAS("platform:" DRV_NAME);
364