Linux Audio

Check our new training course

Loading...
v3.1
  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);
v3.5.6
  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
 48struct 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
 65static inline struct altera_spi *altera_spi_to_hw(struct spi_device *sdev)
 66{
 67	return spi_master_get_devdata(sdev->master);
 68}
 69
 70static 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
106static int altera_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t)
107{
108	return 0;
109}
110
111static int altera_spi_setup(struct spi_device *spi)
112{
113	return 0;
114}
115
116static 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
130static 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
190static 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
218static 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
285exit_busy:
286	err = -EBUSY;
287exit:
288	platform_set_drvdata(pdev, NULL);
289	spi_master_put(master);
290	return err;
291}
292
293static 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
305static const struct of_device_id altera_spi_match[] = {
306	{ .compatible = "ALTR,spi-1.0", },
307	{},
308};
309MODULE_DEVICE_TABLE(of, altera_spi_match);
310#else /* CONFIG_OF */
311#define altera_spi_match NULL
312#endif /* CONFIG_OF */
313
314static 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};
324module_platform_driver(altera_spi_driver);
 
 
 
 
 
 
 
 
 
 
 
325
326MODULE_DESCRIPTION("Altera SPI driver");
327MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
328MODULE_LICENSE("GPL");
329MODULE_ALIAS("platform:" DRV_NAME);