Linux Audio

Check our new training course

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