Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
timberdale.c
Go to the documentation of this file.
1 /*
2  * timberdale.c timberdale FPGA MFD driver
3  * Copyright (c) 2009 Intel Corporation
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 
19 /* Supports:
20  * Timberdale FPGA
21  */
22 
23 #include <linux/kernel.h>
24 #include <linux/module.h>
25 #include <linux/pci.h>
26 #include <linux/msi.h>
27 #include <linux/mfd/core.h>
28 #include <linux/slab.h>
29 
30 #include <linux/timb_gpio.h>
31 
32 #include <linux/i2c.h>
33 #include <linux/i2c-ocores.h>
34 #include <linux/i2c-xiic.h>
35 #include <linux/i2c/tsc2007.h>
36 
37 #include <linux/spi/spi.h>
38 #include <linux/spi/xilinx_spi.h>
39 #include <linux/spi/max7301.h>
40 #include <linux/spi/mc33880.h>
41 
42 #include <media/timb_radio.h>
43 #include <media/timb_video.h>
44 
45 #include <linux/timb_dma.h>
46 
47 #include <linux/ks8842.h>
48 
49 #include "timberdale.h"
50 
51 #define DRIVER_NAME "timberdale"
52 
55  unsigned char __iomem *ctl_membase;
56  struct {
60  } fw;
61 };
62 
63 /*--------------------------------------------------------------------------*/
64 
65 static struct tsc2007_platform_data timberdale_tsc2007_platform_data = {
66  .model = 2003,
67  .x_plate_ohms = 100
68 };
69 
70 static struct i2c_board_info timberdale_i2c_board_info[] = {
71  {
72  I2C_BOARD_INFO("tsc2007", 0x48),
73  .platform_data = &timberdale_tsc2007_platform_data,
75  },
76 };
77 
79 timberdale_xiic_platform_data = {
80  .devices = timberdale_i2c_board_info,
81  .num_devices = ARRAY_SIZE(timberdale_i2c_board_info)
82 };
83 
85 timberdale_ocores_platform_data = {
86  .reg_shift = 2,
87  .clock_khz = 62500,
88  .devices = timberdale_i2c_board_info,
89  .num_devices = ARRAY_SIZE(timberdale_i2c_board_info)
90 };
91 
92 static const __devinitconst struct resource timberdale_xiic_resources[] = {
93  {
94  .start = XIICOFFSET,
95  .end = XIICEND,
96  .flags = IORESOURCE_MEM,
97  },
98  {
99  .start = IRQ_TIMBERDALE_I2C,
100  .end = IRQ_TIMBERDALE_I2C,
101  .flags = IORESOURCE_IRQ,
102  },
103 };
104 
105 static const __devinitconst struct resource timberdale_ocores_resources[] = {
106  {
107  .start = OCORESOFFSET,
108  .end = OCORESEND,
109  .flags = IORESOURCE_MEM,
110  },
111  {
112  .start = IRQ_TIMBERDALE_I2C,
113  .end = IRQ_TIMBERDALE_I2C,
114  .flags = IORESOURCE_IRQ,
115  },
116 };
117 
119  .base = 200
120 };
121 
123  .base = 100
124 };
125 
126 static struct spi_board_info timberdale_spi_16bit_board_info[] = {
127  {
128  .modalias = "max7301",
129  .max_speed_hz = 26000,
130  .chip_select = 2,
131  .mode = SPI_MODE_0,
132  .platform_data = &timberdale_max7301_platform_data
133  },
134 };
135 
136 static struct spi_board_info timberdale_spi_8bit_board_info[] = {
137  {
138  .modalias = "mc33880",
139  .max_speed_hz = 4000,
140  .chip_select = 1,
141  .mode = SPI_MODE_1,
142  .platform_data = &timberdale_mc33880_platform_data
143  },
144 };
145 
146 static __devinitdata struct xspi_platform_data timberdale_xspi_platform_data = {
147  .num_chipselect = 3,
148  .little_endian = true,
149  /* bits per word and devices will be filled in runtime depending
150  * on the HW config
151  */
152 };
153 
154 static const __devinitconst struct resource timberdale_spi_resources[] = {
155  {
156  .start = SPIOFFSET,
157  .end = SPIEND,
158  .flags = IORESOURCE_MEM,
159  },
160  {
161  .start = IRQ_TIMBERDALE_SPI,
162  .end = IRQ_TIMBERDALE_SPI,
163  .flags = IORESOURCE_IRQ,
164  },
165 };
166 
168  timberdale_ks8842_platform_data = {
169  .rx_dma_channel = DMA_ETH_RX,
170  .tx_dma_channel = DMA_ETH_TX
171 };
172 
173 static const __devinitconst struct resource timberdale_eth_resources[] = {
174  {
175  .start = ETHOFFSET,
176  .end = ETHEND,
177  .flags = IORESOURCE_MEM,
178  },
179  {
180  .start = IRQ_TIMBERDALE_ETHSW_IF,
182  .flags = IORESOURCE_IRQ,
183  },
184 };
185 
187  timberdale_gpio_platform_data = {
188  .gpio_base = 0,
189  .nr_pins = GPIO_NR_PINS,
190  .irq_base = 200,
191 };
192 
193 static const __devinitconst struct resource timberdale_gpio_resources[] = {
194  {
195  .start = GPIOOFFSET,
196  .end = GPIOEND,
197  .flags = IORESOURCE_MEM,
198  },
199  {
200  .start = IRQ_TIMBERDALE_GPIO,
201  .end = IRQ_TIMBERDALE_GPIO,
202  .flags = IORESOURCE_IRQ,
203  },
204 };
205 
206 static const __devinitconst struct resource timberdale_mlogicore_resources[] = {
207  {
208  .start = MLCOREOFFSET,
209  .end = MLCOREEND,
210  .flags = IORESOURCE_MEM,
211  },
212  {
213  .start = IRQ_TIMBERDALE_MLCORE,
214  .end = IRQ_TIMBERDALE_MLCORE,
215  .flags = IORESOURCE_IRQ,
216  },
217  {
218  .start = IRQ_TIMBERDALE_MLCORE_BUF,
220  .flags = IORESOURCE_IRQ,
221  },
222 };
223 
224 static const __devinitconst struct resource timberdale_uart_resources[] = {
225  {
226  .start = UARTOFFSET,
227  .end = UARTEND,
228  .flags = IORESOURCE_MEM,
229  },
230  {
231  .start = IRQ_TIMBERDALE_UART,
232  .end = IRQ_TIMBERDALE_UART,
233  .flags = IORESOURCE_IRQ,
234  },
235 };
236 
237 static const __devinitconst struct resource timberdale_uartlite_resources[] = {
238  {
239  .start = UARTLITEOFFSET,
240  .end = UARTLITEEND,
241  .flags = IORESOURCE_MEM,
242  },
243  {
244  .start = IRQ_TIMBERDALE_UARTLITE,
246  .flags = IORESOURCE_IRQ,
247  },
248 };
249 
250 static __devinitdata struct i2c_board_info timberdale_adv7180_i2c_board_info = {
251  /* Requires jumper JP9 to be off */
252  I2C_BOARD_INFO("adv7180", 0x42 >> 1),
254 };
255 
257  timberdale_video_platform_data = {
258  .dma_channel = DMA_VIDEO_RX,
259  .i2c_adapter = 0,
260  .encoder = {
261  .info = &timberdale_adv7180_i2c_board_info
262  }
263 };
264 
265 static const __devinitconst struct resource
266 timberdale_radio_resources[] = {
267  {
268  .start = RDSOFFSET,
269  .end = RDSEND,
270  .flags = IORESOURCE_MEM,
271  },
272  {
273  .start = IRQ_TIMBERDALE_RDS,
274  .end = IRQ_TIMBERDALE_RDS,
275  .flags = IORESOURCE_IRQ,
276  },
277 };
278 
279 static __devinitdata struct i2c_board_info timberdale_tef6868_i2c_board_info = {
280  I2C_BOARD_INFO("tef6862", 0x60)
281 };
282 
283 static __devinitdata struct i2c_board_info timberdale_saa7706_i2c_board_info = {
284  I2C_BOARD_INFO("saa7706h", 0x1C)
285 };
286 
288  timberdale_radio_platform_data = {
289  .i2c_adapter = 0,
290  .tuner = &timberdale_tef6868_i2c_board_info,
291  .dsp = &timberdale_saa7706_i2c_board_info
292 };
293 
294 static const __devinitconst struct resource timberdale_video_resources[] = {
295  {
296  .start = LOGIWOFFSET,
297  .end = LOGIWEND,
298  .flags = IORESOURCE_MEM,
299  },
300  /*
301  note that the "frame buffer" is located in DMA area
302  starting at 0x1200000
303  */
304 };
305 
307  .nr_channels = 10,
308  .channels = {
309  {
310  /* UART RX */
311  .rx = true,
312  .descriptors = 2,
313  .descriptor_elements = 1
314  },
315  {
316  /* UART TX */
317  .rx = false,
318  .descriptors = 2,
319  .descriptor_elements = 1
320  },
321  {
322  /* MLB RX */
323  .rx = true,
324  .descriptors = 2,
325  .descriptor_elements = 1
326  },
327  {
328  /* MLB TX */
329  .rx = false,
330  .descriptors = 2,
331  .descriptor_elements = 1
332  },
333  {
334  /* Video RX */
335  .rx = true,
336  .bytes_per_line = 1440,
337  .descriptors = 2,
338  .descriptor_elements = 16
339  },
340  {
341  /* Video framedrop */
342  },
343  {
344  /* SDHCI RX */
345  .rx = true,
346  },
347  {
348  /* SDHCI TX */
349  },
350  {
351  /* ETH RX */
352  .rx = true,
353  .descriptors = 2,
354  .descriptor_elements = 1
355  },
356  {
357  /* ETH TX */
358  .rx = false,
359  .descriptors = 2,
360  .descriptor_elements = 1
361  },
362  }
363 };
364 
365 static const __devinitconst struct resource timberdale_dma_resources[] = {
366  {
367  .start = DMAOFFSET,
368  .end = DMAEND,
369  .flags = IORESOURCE_MEM,
370  },
371  {
372  .start = IRQ_TIMBERDALE_DMA,
373  .end = IRQ_TIMBERDALE_DMA,
374  .flags = IORESOURCE_IRQ,
375  },
376 };
377 
378 static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = {
379  {
380  .name = "timb-dma",
381  .num_resources = ARRAY_SIZE(timberdale_dma_resources),
382  .resources = timberdale_dma_resources,
383  .platform_data = &timb_dma_platform_data,
384  .pdata_size = sizeof(timb_dma_platform_data),
385  },
386  {
387  .name = "timb-uart",
388  .num_resources = ARRAY_SIZE(timberdale_uart_resources),
389  .resources = timberdale_uart_resources,
390  },
391  {
392  .name = "xiic-i2c",
393  .num_resources = ARRAY_SIZE(timberdale_xiic_resources),
394  .resources = timberdale_xiic_resources,
395  .platform_data = &timberdale_xiic_platform_data,
396  .pdata_size = sizeof(timberdale_xiic_platform_data),
397  },
398  {
399  .name = "timb-gpio",
400  .num_resources = ARRAY_SIZE(timberdale_gpio_resources),
401  .resources = timberdale_gpio_resources,
402  .platform_data = &timberdale_gpio_platform_data,
403  .pdata_size = sizeof(timberdale_gpio_platform_data),
404  },
405  {
406  .name = "timb-video",
407  .num_resources = ARRAY_SIZE(timberdale_video_resources),
408  .resources = timberdale_video_resources,
409  .platform_data = &timberdale_video_platform_data,
410  .pdata_size = sizeof(timberdale_video_platform_data),
411  },
412  {
413  .name = "timb-radio",
414  .num_resources = ARRAY_SIZE(timberdale_radio_resources),
415  .resources = timberdale_radio_resources,
416  .platform_data = &timberdale_radio_platform_data,
417  .pdata_size = sizeof(timberdale_radio_platform_data),
418  },
419  {
420  .name = "xilinx_spi",
421  .num_resources = ARRAY_SIZE(timberdale_spi_resources),
422  .resources = timberdale_spi_resources,
423  .platform_data = &timberdale_xspi_platform_data,
424  .pdata_size = sizeof(timberdale_xspi_platform_data),
425  },
426  {
427  .name = "ks8842",
428  .num_resources = ARRAY_SIZE(timberdale_eth_resources),
429  .resources = timberdale_eth_resources,
430  .platform_data = &timberdale_ks8842_platform_data,
431  .pdata_size = sizeof(timberdale_ks8842_platform_data),
432  },
433 };
434 
435 static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = {
436  {
437  .name = "timb-dma",
438  .num_resources = ARRAY_SIZE(timberdale_dma_resources),
439  .resources = timberdale_dma_resources,
440  .platform_data = &timb_dma_platform_data,
441  .pdata_size = sizeof(timb_dma_platform_data),
442  },
443  {
444  .name = "timb-uart",
445  .num_resources = ARRAY_SIZE(timberdale_uart_resources),
446  .resources = timberdale_uart_resources,
447  },
448  {
449  .name = "uartlite",
450  .num_resources = ARRAY_SIZE(timberdale_uartlite_resources),
451  .resources = timberdale_uartlite_resources,
452  },
453  {
454  .name = "xiic-i2c",
455  .num_resources = ARRAY_SIZE(timberdale_xiic_resources),
456  .resources = timberdale_xiic_resources,
457  .platform_data = &timberdale_xiic_platform_data,
458  .pdata_size = sizeof(timberdale_xiic_platform_data),
459  },
460  {
461  .name = "timb-gpio",
462  .num_resources = ARRAY_SIZE(timberdale_gpio_resources),
463  .resources = timberdale_gpio_resources,
464  .platform_data = &timberdale_gpio_platform_data,
465  .pdata_size = sizeof(timberdale_gpio_platform_data),
466  },
467  {
468  .name = "timb-mlogicore",
469  .num_resources = ARRAY_SIZE(timberdale_mlogicore_resources),
470  .resources = timberdale_mlogicore_resources,
471  },
472  {
473  .name = "timb-video",
474  .num_resources = ARRAY_SIZE(timberdale_video_resources),
475  .resources = timberdale_video_resources,
476  .platform_data = &timberdale_video_platform_data,
477  .pdata_size = sizeof(timberdale_video_platform_data),
478  },
479  {
480  .name = "timb-radio",
481  .num_resources = ARRAY_SIZE(timberdale_radio_resources),
482  .resources = timberdale_radio_resources,
483  .platform_data = &timberdale_radio_platform_data,
484  .pdata_size = sizeof(timberdale_radio_platform_data),
485  },
486  {
487  .name = "xilinx_spi",
488  .num_resources = ARRAY_SIZE(timberdale_spi_resources),
489  .resources = timberdale_spi_resources,
490  .platform_data = &timberdale_xspi_platform_data,
491  .pdata_size = sizeof(timberdale_xspi_platform_data),
492  },
493  {
494  .name = "ks8842",
495  .num_resources = ARRAY_SIZE(timberdale_eth_resources),
496  .resources = timberdale_eth_resources,
497  .platform_data = &timberdale_ks8842_platform_data,
498  .pdata_size = sizeof(timberdale_ks8842_platform_data),
499  },
500 };
501 
502 static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = {
503  {
504  .name = "timb-dma",
505  .num_resources = ARRAY_SIZE(timberdale_dma_resources),
506  .resources = timberdale_dma_resources,
507  .platform_data = &timb_dma_platform_data,
508  .pdata_size = sizeof(timb_dma_platform_data),
509  },
510  {
511  .name = "timb-uart",
512  .num_resources = ARRAY_SIZE(timberdale_uart_resources),
513  .resources = timberdale_uart_resources,
514  },
515  {
516  .name = "xiic-i2c",
517  .num_resources = ARRAY_SIZE(timberdale_xiic_resources),
518  .resources = timberdale_xiic_resources,
519  .platform_data = &timberdale_xiic_platform_data,
520  .pdata_size = sizeof(timberdale_xiic_platform_data),
521  },
522  {
523  .name = "timb-gpio",
524  .num_resources = ARRAY_SIZE(timberdale_gpio_resources),
525  .resources = timberdale_gpio_resources,
526  .platform_data = &timberdale_gpio_platform_data,
527  .pdata_size = sizeof(timberdale_gpio_platform_data),
528  },
529  {
530  .name = "timb-video",
531  .num_resources = ARRAY_SIZE(timberdale_video_resources),
532  .resources = timberdale_video_resources,
533  .platform_data = &timberdale_video_platform_data,
534  .pdata_size = sizeof(timberdale_video_platform_data),
535  },
536  {
537  .name = "timb-radio",
538  .num_resources = ARRAY_SIZE(timberdale_radio_resources),
539  .resources = timberdale_radio_resources,
540  .platform_data = &timberdale_radio_platform_data,
541  .pdata_size = sizeof(timberdale_radio_platform_data),
542  },
543  {
544  .name = "xilinx_spi",
545  .num_resources = ARRAY_SIZE(timberdale_spi_resources),
546  .resources = timberdale_spi_resources,
547  .platform_data = &timberdale_xspi_platform_data,
548  .pdata_size = sizeof(timberdale_xspi_platform_data),
549  },
550 };
551 
552 static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = {
553  {
554  .name = "timb-dma",
555  .num_resources = ARRAY_SIZE(timberdale_dma_resources),
556  .resources = timberdale_dma_resources,
557  .platform_data = &timb_dma_platform_data,
558  .pdata_size = sizeof(timb_dma_platform_data),
559  },
560  {
561  .name = "timb-uart",
562  .num_resources = ARRAY_SIZE(timberdale_uart_resources),
563  .resources = timberdale_uart_resources,
564  },
565  {
566  .name = "ocores-i2c",
567  .num_resources = ARRAY_SIZE(timberdale_ocores_resources),
568  .resources = timberdale_ocores_resources,
569  .platform_data = &timberdale_ocores_platform_data,
570  .pdata_size = sizeof(timberdale_ocores_platform_data),
571  },
572  {
573  .name = "timb-gpio",
574  .num_resources = ARRAY_SIZE(timberdale_gpio_resources),
575  .resources = timberdale_gpio_resources,
576  .platform_data = &timberdale_gpio_platform_data,
577  .pdata_size = sizeof(timberdale_gpio_platform_data),
578  },
579  {
580  .name = "timb-video",
581  .num_resources = ARRAY_SIZE(timberdale_video_resources),
582  .resources = timberdale_video_resources,
583  .platform_data = &timberdale_video_platform_data,
584  .pdata_size = sizeof(timberdale_video_platform_data),
585  },
586  {
587  .name = "timb-radio",
588  .num_resources = ARRAY_SIZE(timberdale_radio_resources),
589  .resources = timberdale_radio_resources,
590  .platform_data = &timberdale_radio_platform_data,
591  .pdata_size = sizeof(timberdale_radio_platform_data),
592  },
593  {
594  .name = "xilinx_spi",
595  .num_resources = ARRAY_SIZE(timberdale_spi_resources),
596  .resources = timberdale_spi_resources,
597  .platform_data = &timberdale_xspi_platform_data,
598  .pdata_size = sizeof(timberdale_xspi_platform_data),
599  },
600  {
601  .name = "ks8842",
602  .num_resources = ARRAY_SIZE(timberdale_eth_resources),
603  .resources = timberdale_eth_resources,
604  .platform_data = &timberdale_ks8842_platform_data,
605  .pdata_size = sizeof(timberdale_ks8842_platform_data),
606  },
607 };
608 
609 static const __devinitconst struct resource timberdale_sdhc_resources[] = {
610  /* located in bar 1 and bar 2 */
611  {
612  .start = SDHC0OFFSET,
613  .end = SDHC0END,
614  .flags = IORESOURCE_MEM,
615  },
616  {
617  .start = IRQ_TIMBERDALE_SDHC,
618  .end = IRQ_TIMBERDALE_SDHC,
619  .flags = IORESOURCE_IRQ,
620  },
621 };
622 
623 static __devinitdata struct mfd_cell timberdale_cells_bar1[] = {
624  {
625  .name = "sdhci",
626  .num_resources = ARRAY_SIZE(timberdale_sdhc_resources),
627  .resources = timberdale_sdhc_resources,
628  },
629 };
630 
631 static __devinitdata struct mfd_cell timberdale_cells_bar2[] = {
632  {
633  .name = "sdhci",
634  .num_resources = ARRAY_SIZE(timberdale_sdhc_resources),
635  .resources = timberdale_sdhc_resources,
636  },
637 };
638 
639 static ssize_t show_fw_ver(struct device *dev, struct device_attribute *attr,
640  char *buf)
641 {
642  struct pci_dev *pdev = to_pci_dev(dev);
643  struct timberdale_device *priv = pci_get_drvdata(pdev);
644 
645  return sprintf(buf, "%d.%d.%d\n", priv->fw.major, priv->fw.minor,
646  priv->fw.config);
647 }
648 
649 static DEVICE_ATTR(fw_ver, S_IRUGO, show_fw_ver, NULL);
650 
651 /*--------------------------------------------------------------------------*/
652 
653 static int __devinit timb_probe(struct pci_dev *dev,
654  const struct pci_device_id *id)
655 {
656  struct timberdale_device *priv;
657  int err, i;
658  resource_size_t mapbase;
659  struct msix_entry *msix_entries = NULL;
660  u8 ip_setup;
661 
662  priv = kzalloc(sizeof(*priv), GFP_KERNEL);
663  if (!priv)
664  return -ENOMEM;
665 
666  pci_set_drvdata(dev, priv);
667 
668  err = pci_enable_device(dev);
669  if (err)
670  goto err_enable;
671 
672  mapbase = pci_resource_start(dev, 0);
673  if (!mapbase) {
674  dev_err(&dev->dev, "No resource\n");
675  goto err_start;
676  }
677 
678  /* create a resource for the PCI master register */
679  priv->ctl_mapbase = mapbase + CHIPCTLOFFSET;
680  if (!request_mem_region(priv->ctl_mapbase, CHIPCTLSIZE, "timb-ctl")) {
681  dev_err(&dev->dev, "Failed to request ctl mem\n");
682  goto err_request;
683  }
684 
685  priv->ctl_membase = ioremap(priv->ctl_mapbase, CHIPCTLSIZE);
686  if (!priv->ctl_membase) {
687  dev_err(&dev->dev, "ioremap failed for ctl mem\n");
688  goto err_ioremap;
689  }
690 
691  /* read the HW config */
692  priv->fw.major = ioread32(priv->ctl_membase + TIMB_REV_MAJOR);
693  priv->fw.minor = ioread32(priv->ctl_membase + TIMB_REV_MINOR);
694  priv->fw.config = ioread32(priv->ctl_membase + TIMB_HW_CONFIG);
695 
696  if (priv->fw.major > TIMB_SUPPORTED_MAJOR) {
697  dev_err(&dev->dev, "The driver supports an older "
698  "version of the FPGA, please update the driver to "
699  "support %d.%d\n", priv->fw.major, priv->fw.minor);
700  goto err_config;
701  }
702  if (priv->fw.major < TIMB_SUPPORTED_MAJOR ||
703  priv->fw.minor < TIMB_REQUIRED_MINOR) {
704  dev_err(&dev->dev, "The FPGA image is too old (%d.%d), "
705  "please upgrade the FPGA to at least: %d.%d\n",
706  priv->fw.major, priv->fw.minor,
708  goto err_config;
709  }
710 
711  msix_entries = kzalloc(TIMBERDALE_NR_IRQS * sizeof(*msix_entries),
712  GFP_KERNEL);
713  if (!msix_entries)
714  goto err_config;
715 
716  for (i = 0; i < TIMBERDALE_NR_IRQS; i++)
717  msix_entries[i].entry = i;
718 
719  err = pci_enable_msix(dev, msix_entries, TIMBERDALE_NR_IRQS);
720  if (err) {
721  dev_err(&dev->dev,
722  "MSI-X init failed: %d, expected entries: %d\n",
723  err, TIMBERDALE_NR_IRQS);
724  goto err_msix;
725  }
726 
727  err = device_create_file(&dev->dev, &dev_attr_fw_ver);
728  if (err)
729  goto err_create_file;
730 
731  /* Reset all FPGA PLB peripherals */
732  iowrite32(0x1, priv->ctl_membase + TIMB_SW_RST);
733 
734  /* update IRQ offsets in I2C board info */
735  for (i = 0; i < ARRAY_SIZE(timberdale_i2c_board_info); i++)
736  timberdale_i2c_board_info[i].irq =
737  msix_entries[timberdale_i2c_board_info[i].irq].vector;
738 
739  /* Update the SPI configuration depending on the HW (8 or 16 bit) */
740  if (priv->fw.config & TIMB_HW_CONFIG_SPI_8BIT) {
741  timberdale_xspi_platform_data.bits_per_word = 8;
742  timberdale_xspi_platform_data.devices =
743  timberdale_spi_8bit_board_info;
744  timberdale_xspi_platform_data.num_devices =
745  ARRAY_SIZE(timberdale_spi_8bit_board_info);
746  } else {
747  timberdale_xspi_platform_data.bits_per_word = 16;
748  timberdale_xspi_platform_data.devices =
749  timberdale_spi_16bit_board_info;
750  timberdale_xspi_platform_data.num_devices =
751  ARRAY_SIZE(timberdale_spi_16bit_board_info);
752  }
753 
754  ip_setup = priv->fw.config & TIMB_HW_VER_MASK;
755  switch (ip_setup) {
756  case TIMB_HW_VER0:
757  err = mfd_add_devices(&dev->dev, -1,
758  timberdale_cells_bar0_cfg0,
759  ARRAY_SIZE(timberdale_cells_bar0_cfg0),
760  &dev->resource[0], msix_entries[0].vector, NULL);
761  break;
762  case TIMB_HW_VER1:
763  err = mfd_add_devices(&dev->dev, -1,
764  timberdale_cells_bar0_cfg1,
765  ARRAY_SIZE(timberdale_cells_bar0_cfg1),
766  &dev->resource[0], msix_entries[0].vector, NULL);
767  break;
768  case TIMB_HW_VER2:
769  err = mfd_add_devices(&dev->dev, -1,
770  timberdale_cells_bar0_cfg2,
771  ARRAY_SIZE(timberdale_cells_bar0_cfg2),
772  &dev->resource[0], msix_entries[0].vector, NULL);
773  break;
774  case TIMB_HW_VER3:
775  err = mfd_add_devices(&dev->dev, -1,
776  timberdale_cells_bar0_cfg3,
777  ARRAY_SIZE(timberdale_cells_bar0_cfg3),
778  &dev->resource[0], msix_entries[0].vector, NULL);
779  break;
780  default:
781  dev_err(&dev->dev, "Uknown IP setup: %d.%d.%d\n",
782  priv->fw.major, priv->fw.minor, ip_setup);
783  err = -ENODEV;
784  goto err_mfd;
785  break;
786  }
787 
788  if (err) {
789  dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
790  goto err_mfd;
791  }
792 
793  err = mfd_add_devices(&dev->dev, 0,
794  timberdale_cells_bar1, ARRAY_SIZE(timberdale_cells_bar1),
795  &dev->resource[1], msix_entries[0].vector, NULL);
796  if (err) {
797  dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
798  goto err_mfd2;
799  }
800 
801  /* only version 0 and 3 have the iNand routed to SDHCI */
802  if (((priv->fw.config & TIMB_HW_VER_MASK) == TIMB_HW_VER0) ||
803  ((priv->fw.config & TIMB_HW_VER_MASK) == TIMB_HW_VER3)) {
804  err = mfd_add_devices(&dev->dev, 1, timberdale_cells_bar2,
805  ARRAY_SIZE(timberdale_cells_bar2),
806  &dev->resource[2], msix_entries[0].vector, NULL);
807  if (err) {
808  dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
809  goto err_mfd2;
810  }
811  }
812 
813  kfree(msix_entries);
814 
815  dev_info(&dev->dev,
816  "Found Timberdale Card. Rev: %d.%d, HW config: 0x%02x\n",
817  priv->fw.major, priv->fw.minor, priv->fw.config);
818 
819  return 0;
820 
821 err_mfd2:
822  mfd_remove_devices(&dev->dev);
823 err_mfd:
824  device_remove_file(&dev->dev, &dev_attr_fw_ver);
825 err_create_file:
826  pci_disable_msix(dev);
827 err_msix:
828  kfree(msix_entries);
829 err_config:
830  iounmap(priv->ctl_membase);
831 err_ioremap:
833 err_request:
834  pci_set_drvdata(dev, NULL);
835 err_start:
836  pci_disable_device(dev);
837 err_enable:
838  kfree(priv);
839  pci_set_drvdata(dev, NULL);
840  return -ENODEV;
841 }
842 
843 static void __devexit timb_remove(struct pci_dev *dev)
844 {
845  struct timberdale_device *priv = pci_get_drvdata(dev);
846 
847  mfd_remove_devices(&dev->dev);
848 
849  device_remove_file(&dev->dev, &dev_attr_fw_ver);
850 
851  iounmap(priv->ctl_membase);
853 
854  pci_disable_msix(dev);
855  pci_disable_device(dev);
856  pci_set_drvdata(dev, NULL);
857  kfree(priv);
858 }
859 
860 static DEFINE_PCI_DEVICE_TABLE(timberdale_pci_tbl) = {
862  { 0 }
863 };
864 MODULE_DEVICE_TABLE(pci, timberdale_pci_tbl);
865 
866 static struct pci_driver timberdale_pci_driver = {
867  .name = DRIVER_NAME,
868  .id_table = timberdale_pci_tbl,
869  .probe = timb_probe,
870  .remove = __devexit_p(timb_remove),
871 };
872 
873 static int __init timberdale_init(void)
874 {
875  int err;
876 
877  err = pci_register_driver(&timberdale_pci_driver);
878  if (err < 0) {
880  "Failed to register PCI driver for %s device.\n",
881  timberdale_pci_driver.name);
882  return -ENODEV;
883  }
884 
885  printk(KERN_INFO "Driver for %s has been successfully registered.\n",
886  timberdale_pci_driver.name);
887 
888  return 0;
889 }
890 
891 static void __exit timberdale_exit(void)
892 {
893  pci_unregister_driver(&timberdale_pci_driver);
894 
895  printk(KERN_INFO "Driver for %s has been successfully unregistered.\n",
896  timberdale_pci_driver.name);
897 }
898 
899 module_init(timberdale_init);
900 module_exit(timberdale_exit);
901 
902 MODULE_AUTHOR("Mocean Laboratories <[email protected]>");
904 MODULE_LICENSE("GPL v2");