xref: /linux/drivers/spi/spi-oc-tiny.c (revision 1ccd4b7bfdcfcc8cc7ffc4a9c11d3ac5b6da8ca0)
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/init.h>
19 #include <linux/interrupt.h>
20 #include <linux/errno.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 	unsigned 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) {
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 intrrupt 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 if (txp && rxp) {
156 		/* we need to tighten the transfer loop */
157 		writeb(*txp++, hw->base + TINY_SPI_TXDATA);
158 		if (t->len > 1) {
159 			writeb(*txp++, hw->base + TINY_SPI_TXDATA);
160 			for (i = 2; i < t->len; i++) {
161 				u8 rx, tx = *txp++;
162 				tiny_spi_wait_txr(hw);
163 				rx = readb(hw->base + TINY_SPI_TXDATA);
164 				writeb(tx, hw->base + TINY_SPI_TXDATA);
165 				*rxp++ = rx;
166 			}
167 			tiny_spi_wait_txr(hw);
168 			*rxp++ = readb(hw->base + TINY_SPI_TXDATA);
169 		}
170 		tiny_spi_wait_txe(hw);
171 		*rxp++ = readb(hw->base + TINY_SPI_RXDATA);
172 	} else if (rxp) {
173 		writeb(0, hw->base + TINY_SPI_TXDATA);
174 		if (t->len > 1) {
175 			writeb(0,
176 			       hw->base + TINY_SPI_TXDATA);
177 			for (i = 2; i < t->len; i++) {
178 				u8 rx;
179 				tiny_spi_wait_txr(hw);
180 				rx = readb(hw->base + TINY_SPI_TXDATA);
181 				writeb(0, hw->base + TINY_SPI_TXDATA);
182 				*rxp++ = rx;
183 			}
184 			tiny_spi_wait_txr(hw);
185 			*rxp++ = readb(hw->base + TINY_SPI_TXDATA);
186 		}
187 		tiny_spi_wait_txe(hw);
188 		*rxp++ = readb(hw->base + TINY_SPI_RXDATA);
189 	} else if (txp) {
190 		writeb(*txp++, hw->base + TINY_SPI_TXDATA);
191 		if (t->len > 1) {
192 			writeb(*txp++, hw->base + TINY_SPI_TXDATA);
193 			for (i = 2; i < t->len; i++) {
194 				u8 tx = *txp++;
195 				tiny_spi_wait_txr(hw);
196 				writeb(tx, hw->base + TINY_SPI_TXDATA);
197 			}
198 		}
199 		tiny_spi_wait_txe(hw);
200 	} else {
201 		writeb(0, hw->base + TINY_SPI_TXDATA);
202 		if (t->len > 1) {
203 			writeb(0, hw->base + TINY_SPI_TXDATA);
204 			for (i = 2; i < t->len; i++) {
205 				tiny_spi_wait_txr(hw);
206 				writeb(0, hw->base + TINY_SPI_TXDATA);
207 			}
208 		}
209 		tiny_spi_wait_txe(hw);
210 	}
211 	return t->len;
212 }
213 
214 static irqreturn_t tiny_spi_irq(int irq, void *dev)
215 {
216 	struct tiny_spi *hw = dev;
217 
218 	writeb(0, hw->base + TINY_SPI_STATUS);
219 	if (hw->rxc + 1 == hw->len) {
220 		if (hw->rxp)
221 			*hw->rxp++ = readb(hw->base + TINY_SPI_RXDATA);
222 		hw->rxc++;
223 		complete(&hw->done);
224 	} else {
225 		if (hw->rxp)
226 			*hw->rxp++ = readb(hw->base + TINY_SPI_TXDATA);
227 		hw->rxc++;
228 		if (hw->txc < hw->len) {
229 			writeb(hw->txp ? *hw->txp++ : 0,
230 			       hw->base + TINY_SPI_TXDATA);
231 			hw->txc++;
232 			writeb(TINY_SPI_STATUS_TXR,
233 			       hw->base + TINY_SPI_STATUS);
234 		} else {
235 			writeb(TINY_SPI_STATUS_TXE,
236 			       hw->base + TINY_SPI_STATUS);
237 		}
238 	}
239 	return IRQ_HANDLED;
240 }
241 
242 #ifdef CONFIG_OF
243 #include <linux/of_gpio.h>
244 
245 static int __devinit tiny_spi_of_probe(struct platform_device *pdev)
246 {
247 	struct tiny_spi *hw = platform_get_drvdata(pdev);
248 	struct device_node *np = pdev->dev.of_node;
249 	unsigned int i;
250 	const __be32 *val;
251 	int len;
252 
253 	if (!np)
254 		return 0;
255 	hw->gpio_cs_count = of_gpio_count(np);
256 	if (hw->gpio_cs_count) {
257 		hw->gpio_cs = devm_kzalloc(&pdev->dev,
258 				hw->gpio_cs_count * sizeof(unsigned int),
259 				GFP_KERNEL);
260 		if (!hw->gpio_cs)
261 			return -ENOMEM;
262 	}
263 	for (i = 0; i < hw->gpio_cs_count; i++) {
264 		hw->gpio_cs[i] = of_get_gpio_flags(np, i, NULL);
265 		if (hw->gpio_cs[i] < 0)
266 			return -ENODEV;
267 	}
268 	hw->bitbang.master->dev.of_node = pdev->dev.of_node;
269 	val = of_get_property(pdev->dev.of_node,
270 			      "clock-frequency", &len);
271 	if (val && len >= sizeof(__be32))
272 		hw->freq = be32_to_cpup(val);
273 	val = of_get_property(pdev->dev.of_node, "baud-width", &len);
274 	if (val && len >= sizeof(__be32))
275 		hw->baudwidth = be32_to_cpup(val);
276 	return 0;
277 }
278 #else /* !CONFIG_OF */
279 static int __devinit tiny_spi_of_probe(struct platform_device *pdev)
280 {
281 	return 0;
282 }
283 #endif /* CONFIG_OF */
284 
285 static int __devinit tiny_spi_probe(struct platform_device *pdev)
286 {
287 	struct tiny_spi_platform_data *platp = pdev->dev.platform_data;
288 	struct tiny_spi *hw;
289 	struct spi_master *master;
290 	struct resource *res;
291 	unsigned int i;
292 	int err = -ENODEV;
293 
294 	master = spi_alloc_master(&pdev->dev, sizeof(struct tiny_spi));
295 	if (!master)
296 		return err;
297 
298 	/* setup the master state. */
299 	master->bus_num = pdev->id;
300 	master->num_chipselect = 255;
301 	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
302 	master->setup = tiny_spi_setup;
303 
304 	hw = spi_master_get_devdata(master);
305 	platform_set_drvdata(pdev, hw);
306 
307 	/* setup the state for the bitbang driver */
308 	hw->bitbang.master = spi_master_get(master);
309 	if (!hw->bitbang.master)
310 		return err;
311 	hw->bitbang.setup_transfer = tiny_spi_setup_transfer;
312 	hw->bitbang.chipselect = tiny_spi_chipselect;
313 	hw->bitbang.txrx_bufs = tiny_spi_txrx_bufs;
314 
315 	/* find and map our resources */
316 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
317 	if (!res)
318 		goto exit_busy;
319 	if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
320 				     pdev->name))
321 		goto exit_busy;
322 	hw->base = devm_ioremap_nocache(&pdev->dev, res->start,
323 					resource_size(res));
324 	if (!hw->base)
325 		goto exit_busy;
326 	/* irq is optional */
327 	hw->irq = platform_get_irq(pdev, 0);
328 	if (hw->irq >= 0) {
329 		init_completion(&hw->done);
330 		err = devm_request_irq(&pdev->dev, hw->irq, tiny_spi_irq, 0,
331 				       pdev->name, hw);
332 		if (err)
333 			goto exit;
334 	}
335 	/* find platform data */
336 	if (platp) {
337 		hw->gpio_cs_count = platp->gpio_cs_count;
338 		hw->gpio_cs = platp->gpio_cs;
339 		if (platp->gpio_cs_count && !platp->gpio_cs)
340 			goto exit_busy;
341 		hw->freq = platp->freq;
342 		hw->baudwidth = platp->baudwidth;
343 	} else {
344 		err = tiny_spi_of_probe(pdev);
345 		if (err)
346 			goto exit;
347 	}
348 	for (i = 0; i < hw->gpio_cs_count; i++) {
349 		err = gpio_request(hw->gpio_cs[i], dev_name(&pdev->dev));
350 		if (err)
351 			goto exit_gpio;
352 		gpio_direction_output(hw->gpio_cs[i], 1);
353 	}
354 	hw->bitbang.master->num_chipselect = max(1U, hw->gpio_cs_count);
355 
356 	/* register our spi controller */
357 	err = spi_bitbang_start(&hw->bitbang);
358 	if (err)
359 		goto exit;
360 	dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq);
361 
362 	return 0;
363 
364 exit_gpio:
365 	while (i-- > 0)
366 		gpio_free(hw->gpio_cs[i]);
367 exit_busy:
368 	err = -EBUSY;
369 exit:
370 	platform_set_drvdata(pdev, NULL);
371 	spi_master_put(master);
372 	return err;
373 }
374 
375 static int __devexit tiny_spi_remove(struct platform_device *pdev)
376 {
377 	struct tiny_spi *hw = platform_get_drvdata(pdev);
378 	struct spi_master *master = hw->bitbang.master;
379 	unsigned int i;
380 
381 	spi_bitbang_stop(&hw->bitbang);
382 	for (i = 0; i < hw->gpio_cs_count; i++)
383 		gpio_free(hw->gpio_cs[i]);
384 	platform_set_drvdata(pdev, NULL);
385 	spi_master_put(master);
386 	return 0;
387 }
388 
389 #ifdef CONFIG_OF
390 static const struct of_device_id tiny_spi_match[] = {
391 	{ .compatible = "opencores,tiny-spi-rtlsvn2", },
392 	{},
393 };
394 MODULE_DEVICE_TABLE(of, tiny_spi_match);
395 #else /* CONFIG_OF */
396 #define tiny_spi_match NULL
397 #endif /* CONFIG_OF */
398 
399 static struct platform_driver tiny_spi_driver = {
400 	.probe = tiny_spi_probe,
401 	.remove = __devexit_p(tiny_spi_remove),
402 	.driver = {
403 		.name = DRV_NAME,
404 		.owner = THIS_MODULE,
405 		.pm = NULL,
406 		.of_match_table = tiny_spi_match,
407 	},
408 };
409 
410 static int __init tiny_spi_init(void)
411 {
412 	return platform_driver_register(&tiny_spi_driver);
413 }
414 module_init(tiny_spi_init);
415 
416 static void __exit tiny_spi_exit(void)
417 {
418 	platform_driver_unregister(&tiny_spi_driver);
419 }
420 module_exit(tiny_spi_exit);
421 
422 MODULE_DESCRIPTION("OpenCores tiny SPI driver");
423 MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
424 MODULE_LICENSE("GPL");
425 MODULE_ALIAS("platform:" DRV_NAME);
426