Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
spi-altera.c
Go to the documentation of this file.
1 /*
2  * Altera SPI driver
3  *
4  * Copyright (C) 2008 Thomas Chou <[email protected]>
5  *
6  * Based on spi_s3c24xx.c, which is:
7  * Copyright (c) 2006 Ben Dooks
8  * Copyright (c) 2006 Simtec Electronics
9  * Ben Dooks <[email protected]>
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 */
51  struct completion done;
52 
53  void __iomem *base;
54  int irq;
55  int len;
56  int count;
58  unsigned long imr;
59 
60  /* data buffers */
61  const unsigned char *tx;
62  unsigned char *rx;
63 };
64 
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 
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) {
77  writel(1 << spi->chip_select,
80  writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
81  break;
82 
83  case BITBANG_CS_ACTIVE:
85  writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
87  break;
88  }
89  } else {
90  switch (value) {
93  writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
94  break;
95 
96  case BITBANG_CS_ACTIVE:
97  writel(1 << spi->chip_select,
100  writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
101  break;
102  }
103  }
104 }
105 
106 static int altera_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t)
107 {
108  return 0;
109 }
110 
111 static int altera_spi_setup(struct spi_device *spi)
112 {
113  return 0;
114 }
115 
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 
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 */
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 
149  /* disable receive interrupt */
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) &
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 
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 
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 
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 
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 #endif /* CONFIG_OF */
311 
312 static struct platform_driver altera_spi_driver = {
313  .probe = altera_spi_probe,
314  .remove = __devexit_p(altera_spi_remove),
315  .driver = {
316  .name = DRV_NAME,
317  .owner = THIS_MODULE,
318  .pm = NULL,
319  .of_match_table = of_match_ptr(altera_spi_match),
320  },
321 };
322 module_platform_driver(altera_spi_driver);
323 
324 MODULE_DESCRIPTION("Altera SPI driver");
325 MODULE_AUTHOR("Thomas Chou <[email protected]>");
326 MODULE_LICENSE("GPL");
327 MODULE_ALIAS("platform:" DRV_NAME);