1 /*
2  * Altera SPI driver
3  *
4  * Copyright (C) 2008 Thomas Chou <thomas@wytron.com.tw>
5  *
6  * Based on spi_s3c24xx.c, which is:
7  * Copyright (c) 2006 Ben Dooks
8  * Copyright (c) 2006 Simtec Electronics
9  *	Ben Dooks <ben@simtec.co.uk>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License version 2 as
13  * published by the Free Software Foundation.
14  */
15 
16 #include <linux/init.h>
17 #include <linux/interrupt.h>
18 #include <linux/errno.h>
19 #include <linux/module.h>
20 #include <linux/platform_device.h>
21 #include <linux/spi/spi.h>
22 #include <linux/spi/spi_bitbang.h>
23 #include <linux/io.h>
24 #include <linux/of.h>
25 
26 #define DRV_NAME "spi_altera"
27 
28 #define ALTERA_SPI_RXDATA	0
29 #define ALTERA_SPI_TXDATA	4
30 #define ALTERA_SPI_STATUS	8
31 #define ALTERA_SPI_CONTROL	12
32 #define ALTERA_SPI_SLAVE_SEL	20
33 
34 #define ALTERA_SPI_STATUS_ROE_MSK	0x8
35 #define ALTERA_SPI_STATUS_TOE_MSK	0x10
36 #define ALTERA_SPI_STATUS_TMT_MSK	0x20
37 #define ALTERA_SPI_STATUS_TRDY_MSK	0x40
38 #define ALTERA_SPI_STATUS_RRDY_MSK	0x80
39 #define ALTERA_SPI_STATUS_E_MSK		0x100
40 
41 #define ALTERA_SPI_CONTROL_IROE_MSK	0x8
42 #define ALTERA_SPI_CONTROL_ITOE_MSK	0x10
43 #define ALTERA_SPI_CONTROL_ITRDY_MSK	0x40
44 #define ALTERA_SPI_CONTROL_IRRDY_MSK	0x80
45 #define ALTERA_SPI_CONTROL_IE_MSK	0x100
46 #define ALTERA_SPI_CONTROL_SSO_MSK	0x400
47 
48 struct altera_spi {
49 	/* bitbang has to be first */
50 	struct spi_bitbang bitbang;
51 	struct completion done;
52 
53 	void __iomem *base;
54 	int irq;
55 	int len;
56 	int count;
57 	int bytes_per_word;
58 	unsigned long imr;
59 
60 	/* data buffers */
61 	const unsigned char *tx;
62 	unsigned char *rx;
63 };
64 
altera_spi_to_hw(struct spi_device * sdev)65 static inline struct altera_spi *altera_spi_to_hw(struct spi_device *sdev)
66 {
67 	return spi_master_get_devdata(sdev->master);
68 }
69 
altera_spi_chipsel(struct spi_device * spi,int value)70 static void altera_spi_chipsel(struct spi_device *spi, int value)
71 {
72 	struct altera_spi *hw = altera_spi_to_hw(spi);
73 
74 	if (spi->mode & SPI_CS_HIGH) {
75 		switch (value) {
76 		case BITBANG_CS_INACTIVE:
77 			writel(1 << spi->chip_select,
78 			       hw->base + ALTERA_SPI_SLAVE_SEL);
79 			hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK;
80 			writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
81 			break;
82 
83 		case BITBANG_CS_ACTIVE:
84 			hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK;
85 			writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
86 			writel(0, hw->base + ALTERA_SPI_SLAVE_SEL);
87 			break;
88 		}
89 	} else {
90 		switch (value) {
91 		case BITBANG_CS_INACTIVE:
92 			hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK;
93 			writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
94 			break;
95 
96 		case BITBANG_CS_ACTIVE:
97 			writel(1 << spi->chip_select,
98 			       hw->base + ALTERA_SPI_SLAVE_SEL);
99 			hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK;
100 			writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
101 			break;
102 		}
103 	}
104 }
105 
altera_spi_setupxfer(struct spi_device * spi,struct spi_transfer * t)106 static int altera_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t)
107 {
108 	return 0;
109 }
110 
altera_spi_setup(struct spi_device * spi)111 static int altera_spi_setup(struct spi_device *spi)
112 {
113 	return 0;
114 }
115 
hw_txbyte(struct altera_spi * hw,int count)116 static inline unsigned int hw_txbyte(struct altera_spi *hw, int count)
117 {
118 	if (hw->tx) {
119 		switch (hw->bytes_per_word) {
120 		case 1:
121 			return hw->tx[count];
122 		case 2:
123 			return (hw->tx[count * 2]
124 				| (hw->tx[count * 2 + 1] << 8));
125 		}
126 	}
127 	return 0;
128 }
129 
altera_spi_txrx(struct spi_device * spi,struct spi_transfer * t)130 static int altera_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
131 {
132 	struct altera_spi *hw = altera_spi_to_hw(spi);
133 
134 	hw->tx = t->tx_buf;
135 	hw->rx = t->rx_buf;
136 	hw->count = 0;
137 	hw->bytes_per_word = (t->bits_per_word ? : spi->bits_per_word) / 8;
138 	hw->len = t->len / hw->bytes_per_word;
139 
140 	if (hw->irq >= 0) {
141 		/* enable receive interrupt */
142 		hw->imr |= ALTERA_SPI_CONTROL_IRRDY_MSK;
143 		writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
144 
145 		/* send the first byte */
146 		writel(hw_txbyte(hw, 0), hw->base + ALTERA_SPI_TXDATA);
147 
148 		wait_for_completion(&hw->done);
149 		/* disable receive interrupt */
150 		hw->imr &= ~ALTERA_SPI_CONTROL_IRRDY_MSK;
151 		writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
152 	} else {
153 		/* send the first byte */
154 		writel(hw_txbyte(hw, 0), hw->base + ALTERA_SPI_TXDATA);
155 
156 		while (1) {
157 			unsigned int rxd;
158 
159 			while (!(readl(hw->base + ALTERA_SPI_STATUS) &
160 				 ALTERA_SPI_STATUS_RRDY_MSK))
161 				cpu_relax();
162 
163 			rxd = readl(hw->base + ALTERA_SPI_RXDATA);
164 			if (hw->rx) {
165 				switch (hw->bytes_per_word) {
166 				case 1:
167 					hw->rx[hw->count] = rxd;
168 					break;
169 				case 2:
170 					hw->rx[hw->count * 2] = rxd;
171 					hw->rx[hw->count * 2 + 1] = rxd >> 8;
172 					break;
173 				}
174 			}
175 
176 			hw->count++;
177 
178 			if (hw->count < hw->len)
179 				writel(hw_txbyte(hw, hw->count),
180 				       hw->base + ALTERA_SPI_TXDATA);
181 			else
182 				break;
183 		}
184 
185 	}
186 
187 	return hw->count * hw->bytes_per_word;
188 }
189 
altera_spi_irq(int irq,void * dev)190 static irqreturn_t altera_spi_irq(int irq, void *dev)
191 {
192 	struct altera_spi *hw = dev;
193 	unsigned int rxd;
194 
195 	rxd = readl(hw->base + ALTERA_SPI_RXDATA);
196 	if (hw->rx) {
197 		switch (hw->bytes_per_word) {
198 		case 1:
199 			hw->rx[hw->count] = rxd;
200 			break;
201 		case 2:
202 			hw->rx[hw->count * 2] = rxd;
203 			hw->rx[hw->count * 2 + 1] = rxd >> 8;
204 			break;
205 		}
206 	}
207 
208 	hw->count++;
209 
210 	if (hw->count < hw->len)
211 		writel(hw_txbyte(hw, hw->count), hw->base + ALTERA_SPI_TXDATA);
212 	else
213 		complete(&hw->done);
214 
215 	return IRQ_HANDLED;
216 }
217 
altera_spi_probe(struct platform_device * pdev)218 static int __devinit altera_spi_probe(struct platform_device *pdev)
219 {
220 	struct altera_spi_platform_data *platp = pdev->dev.platform_data;
221 	struct altera_spi *hw;
222 	struct spi_master *master;
223 	struct resource *res;
224 	int err = -ENODEV;
225 
226 	master = spi_alloc_master(&pdev->dev, sizeof(struct altera_spi));
227 	if (!master)
228 		return err;
229 
230 	/* setup the master state. */
231 	master->bus_num = pdev->id;
232 	master->num_chipselect = 16;
233 	master->mode_bits = SPI_CS_HIGH;
234 	master->setup = altera_spi_setup;
235 
236 	hw = spi_master_get_devdata(master);
237 	platform_set_drvdata(pdev, hw);
238 
239 	/* setup the state for the bitbang driver */
240 	hw->bitbang.master = spi_master_get(master);
241 	if (!hw->bitbang.master)
242 		return err;
243 	hw->bitbang.setup_transfer = altera_spi_setupxfer;
244 	hw->bitbang.chipselect = altera_spi_chipsel;
245 	hw->bitbang.txrx_bufs = altera_spi_txrx;
246 
247 	/* find and map our resources */
248 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
249 	if (!res)
250 		goto exit_busy;
251 	if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
252 				     pdev->name))
253 		goto exit_busy;
254 	hw->base = devm_ioremap_nocache(&pdev->dev, res->start,
255 					resource_size(res));
256 	if (!hw->base)
257 		goto exit_busy;
258 	/* program defaults into the registers */
259 	hw->imr = 0;		/* disable spi interrupts */
260 	writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
261 	writel(0, hw->base + ALTERA_SPI_STATUS);	/* clear status reg */
262 	if (readl(hw->base + ALTERA_SPI_STATUS) & ALTERA_SPI_STATUS_RRDY_MSK)
263 		readl(hw->base + ALTERA_SPI_RXDATA);	/* flush rxdata */
264 	/* irq is optional */
265 	hw->irq = platform_get_irq(pdev, 0);
266 	if (hw->irq >= 0) {
267 		init_completion(&hw->done);
268 		err = devm_request_irq(&pdev->dev, hw->irq, altera_spi_irq, 0,
269 				       pdev->name, hw);
270 		if (err)
271 			goto exit;
272 	}
273 	/* find platform data */
274 	if (!platp)
275 		hw->bitbang.master->dev.of_node = pdev->dev.of_node;
276 
277 	/* register our spi controller */
278 	err = spi_bitbang_start(&hw->bitbang);
279 	if (err)
280 		goto exit;
281 	dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq);
282 
283 	return 0;
284 
285 exit_busy:
286 	err = -EBUSY;
287 exit:
288 	platform_set_drvdata(pdev, NULL);
289 	spi_master_put(master);
290 	return err;
291 }
292 
altera_spi_remove(struct platform_device * dev)293 static int __devexit altera_spi_remove(struct platform_device *dev)
294 {
295 	struct altera_spi *hw = platform_get_drvdata(dev);
296 	struct spi_master *master = hw->bitbang.master;
297 
298 	spi_bitbang_stop(&hw->bitbang);
299 	platform_set_drvdata(dev, NULL);
300 	spi_master_put(master);
301 	return 0;
302 }
303 
304 #ifdef CONFIG_OF
305 static const struct of_device_id altera_spi_match[] = {
306 	{ .compatible = "ALTR,spi-1.0", },
307 	{},
308 };
309 MODULE_DEVICE_TABLE(of, altera_spi_match);
310 #else /* CONFIG_OF */
311 #define altera_spi_match NULL
312 #endif /* CONFIG_OF */
313 
314 static struct platform_driver altera_spi_driver = {
315 	.probe = altera_spi_probe,
316 	.remove = __devexit_p(altera_spi_remove),
317 	.driver = {
318 		.name = DRV_NAME,
319 		.owner = THIS_MODULE,
320 		.pm = NULL,
321 		.of_match_table = altera_spi_match,
322 	},
323 };
324 module_platform_driver(altera_spi_driver);
325 
326 MODULE_DESCRIPTION("Altera SPI driver");
327 MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
328 MODULE_LICENSE("GPL");
329 MODULE_ALIAS("platform:" DRV_NAME);
330