Merge git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6
[pandora-kernel.git] / drivers / mfd / timberdale.c
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
53 struct timberdale_device {
54         resource_size_t         ctl_mapbase;
55         unsigned char __iomem   *ctl_membase;
56         struct {
57                 u32 major;
58                 u32 minor;
59                 u32 config;
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,
74                 .irq = IRQ_TIMBERDALE_TSC_INT
75         },
76 };
77
78 static __devinitdata struct xiic_i2c_platform_data
79 timberdale_xiic_platform_data = {
80         .devices = timberdale_i2c_board_info,
81         .num_devices = ARRAY_SIZE(timberdale_i2c_board_info)
82 };
83
84 static __devinitdata struct ocores_i2c_platform_data
85 timberdale_ocores_platform_data = {
86         .regstep = 4,
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
118 const struct max7301_platform_data timberdale_max7301_platform_data = {
119         .base = 200
120 };
121
122 const struct mc33880_platform_data timberdale_mc33880_platform_data = {
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
167 static __devinitdata struct ks8842_platform_data
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,
181                 .end    = IRQ_TIMBERDALE_ETHSW_IF,
182                 .flags  = IORESOURCE_IRQ,
183         },
184 };
185
186 static __devinitdata struct timbgpio_platform_data
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,
219                 .end    = 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,
245                 .end    = 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),
253         .irq = IRQ_TIMBERDALE_ADV7180
254 };
255
256 static __devinitdata struct timb_video_platform_data
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
287 static __devinitdata struct timb_radio_platform_data
288         timberdale_radio_platform_data = {
289         .i2c_adapter = 0,
290         .tuner = {
291                 .info = &timberdale_tef6868_i2c_board_info
292         },
293         .dsp = {
294                 .info = &timberdale_saa7706_i2c_board_info
295         }
296 };
297
298 static const __devinitconst struct resource timberdale_video_resources[] = {
299         {
300                 .start  = LOGIWOFFSET,
301                 .end    = LOGIWEND,
302                 .flags  = IORESOURCE_MEM,
303         },
304         /*
305         note that the "frame buffer" is located in DMA area
306         starting at 0x1200000
307         */
308 };
309
310 static __devinitdata struct timb_dma_platform_data timb_dma_platform_data = {
311         .nr_channels = 10,
312         .channels = {
313                 {
314                         /* UART RX */
315                         .rx = true,
316                         .descriptors = 2,
317                         .descriptor_elements = 1
318                 },
319                 {
320                         /* UART TX */
321                         .rx = false,
322                         .descriptors = 2,
323                         .descriptor_elements = 1
324                 },
325                 {
326                         /* MLB RX */
327                         .rx = true,
328                         .descriptors = 2,
329                         .descriptor_elements = 1
330                 },
331                 {
332                         /* MLB TX */
333                         .rx = false,
334                         .descriptors = 2,
335                         .descriptor_elements = 1
336                 },
337                 {
338                         /* Video RX */
339                         .rx = true,
340                         .bytes_per_line = 1440,
341                         .descriptors = 2,
342                         .descriptor_elements = 16
343                 },
344                 {
345                         /* Video framedrop */
346                 },
347                 {
348                         /* SDHCI RX */
349                         .rx = true,
350                 },
351                 {
352                         /* SDHCI TX */
353                 },
354                 {
355                         /* ETH RX */
356                         .rx = true,
357                         .descriptors = 2,
358                         .descriptor_elements = 1
359                 },
360                 {
361                         /* ETH TX */
362                         .rx = false,
363                         .descriptors = 2,
364                         .descriptor_elements = 1
365                 },
366         }
367 };
368
369 static const __devinitconst struct resource timberdale_dma_resources[] = {
370         {
371                 .start  = DMAOFFSET,
372                 .end    = DMAEND,
373                 .flags  = IORESOURCE_MEM,
374         },
375         {
376                 .start  = IRQ_TIMBERDALE_DMA,
377                 .end    = IRQ_TIMBERDALE_DMA,
378                 .flags  = IORESOURCE_IRQ,
379         },
380 };
381
382 static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = {
383         {
384                 .name = "timb-dma",
385                 .num_resources = ARRAY_SIZE(timberdale_dma_resources),
386                 .resources = timberdale_dma_resources,
387                 .mfd_data = &timb_dma_platform_data,
388         },
389         {
390                 .name = "timb-uart",
391                 .num_resources = ARRAY_SIZE(timberdale_uart_resources),
392                 .resources = timberdale_uart_resources,
393         },
394         {
395                 .name = "xiic-i2c",
396                 .num_resources = ARRAY_SIZE(timberdale_xiic_resources),
397                 .resources = timberdale_xiic_resources,
398                 .mfd_data = &timberdale_xiic_platform_data,
399         },
400         {
401                 .name = "timb-gpio",
402                 .num_resources = ARRAY_SIZE(timberdale_gpio_resources),
403                 .resources = timberdale_gpio_resources,
404                 .mfd_data = &timberdale_gpio_platform_data,
405         },
406         {
407                 .name = "timb-video",
408                 .num_resources = ARRAY_SIZE(timberdale_video_resources),
409                 .resources = timberdale_video_resources,
410                 .mfd_data = &timberdale_video_platform_data,
411         },
412         {
413                 .name = "timb-radio",
414                 .num_resources = ARRAY_SIZE(timberdale_radio_resources),
415                 .resources = timberdale_radio_resources,
416                 .mfd_data = &timberdale_radio_platform_data,
417         },
418         {
419                 .name = "xilinx_spi",
420                 .num_resources = ARRAY_SIZE(timberdale_spi_resources),
421                 .resources = timberdale_spi_resources,
422                 .mfd_data = &timberdale_xspi_platform_data,
423         },
424         {
425                 .name = "ks8842",
426                 .num_resources = ARRAY_SIZE(timberdale_eth_resources),
427                 .resources = timberdale_eth_resources,
428                 .mfd_data = &timberdale_ks8842_platform_data,
429         },
430 };
431
432 static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = {
433         {
434                 .name = "timb-dma",
435                 .num_resources = ARRAY_SIZE(timberdale_dma_resources),
436                 .resources = timberdale_dma_resources,
437                 .mfd_data = &timb_dma_platform_data,
438         },
439         {
440                 .name = "timb-uart",
441                 .num_resources = ARRAY_SIZE(timberdale_uart_resources),
442                 .resources = timberdale_uart_resources,
443         },
444         {
445                 .name = "uartlite",
446                 .num_resources = ARRAY_SIZE(timberdale_uartlite_resources),
447                 .resources = timberdale_uartlite_resources,
448         },
449         {
450                 .name = "xiic-i2c",
451                 .num_resources = ARRAY_SIZE(timberdale_xiic_resources),
452                 .resources = timberdale_xiic_resources,
453                 .mfd_data = &timberdale_xiic_platform_data,
454         },
455         {
456                 .name = "timb-gpio",
457                 .num_resources = ARRAY_SIZE(timberdale_gpio_resources),
458                 .resources = timberdale_gpio_resources,
459                 .mfd_data = &timberdale_gpio_platform_data,
460         },
461         {
462                 .name = "timb-mlogicore",
463                 .num_resources = ARRAY_SIZE(timberdale_mlogicore_resources),
464                 .resources = timberdale_mlogicore_resources,
465         },
466         {
467                 .name = "timb-video",
468                 .num_resources = ARRAY_SIZE(timberdale_video_resources),
469                 .resources = timberdale_video_resources,
470                 .mfd_data = &timberdale_video_platform_data,
471         },
472         {
473                 .name = "timb-radio",
474                 .num_resources = ARRAY_SIZE(timberdale_radio_resources),
475                 .resources = timberdale_radio_resources,
476                 .mfd_data = &timberdale_radio_platform_data,
477         },
478         {
479                 .name = "xilinx_spi",
480                 .num_resources = ARRAY_SIZE(timberdale_spi_resources),
481                 .resources = timberdale_spi_resources,
482                 .mfd_data = &timberdale_xspi_platform_data,
483         },
484         {
485                 .name = "ks8842",
486                 .num_resources = ARRAY_SIZE(timberdale_eth_resources),
487                 .resources = timberdale_eth_resources,
488                 .mfd_data = &timberdale_ks8842_platform_data,
489         },
490 };
491
492 static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = {
493         {
494                 .name = "timb-dma",
495                 .num_resources = ARRAY_SIZE(timberdale_dma_resources),
496                 .resources = timberdale_dma_resources,
497                 .mfd_data = &timb_dma_platform_data,
498         },
499         {
500                 .name = "timb-uart",
501                 .num_resources = ARRAY_SIZE(timberdale_uart_resources),
502                 .resources = timberdale_uart_resources,
503         },
504         {
505                 .name = "xiic-i2c",
506                 .num_resources = ARRAY_SIZE(timberdale_xiic_resources),
507                 .resources = timberdale_xiic_resources,
508                 .mfd_data = &timberdale_xiic_platform_data,
509         },
510         {
511                 .name = "timb-gpio",
512                 .num_resources = ARRAY_SIZE(timberdale_gpio_resources),
513                 .resources = timberdale_gpio_resources,
514                 .mfd_data = &timberdale_gpio_platform_data,
515         },
516         {
517                 .name = "timb-video",
518                 .num_resources = ARRAY_SIZE(timberdale_video_resources),
519                 .resources = timberdale_video_resources,
520                 .mfd_data = &timberdale_video_platform_data,
521         },
522         {
523                 .name = "timb-radio",
524                 .num_resources = ARRAY_SIZE(timberdale_radio_resources),
525                 .resources = timberdale_radio_resources,
526                 .mfd_data = &timberdale_radio_platform_data,
527         },
528         {
529                 .name = "xilinx_spi",
530                 .num_resources = ARRAY_SIZE(timberdale_spi_resources),
531                 .resources = timberdale_spi_resources,
532                 .mfd_data = &timberdale_xspi_platform_data,
533         },
534 };
535
536 static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = {
537         {
538                 .name = "timb-dma",
539                 .num_resources = ARRAY_SIZE(timberdale_dma_resources),
540                 .resources = timberdale_dma_resources,
541                 .mfd_data = &timb_dma_platform_data,
542         },
543         {
544                 .name = "timb-uart",
545                 .num_resources = ARRAY_SIZE(timberdale_uart_resources),
546                 .resources = timberdale_uart_resources,
547         },
548         {
549                 .name = "ocores-i2c",
550                 .num_resources = ARRAY_SIZE(timberdale_ocores_resources),
551                 .resources = timberdale_ocores_resources,
552                 .mfd_data = &timberdale_ocores_platform_data,
553         },
554         {
555                 .name = "timb-gpio",
556                 .num_resources = ARRAY_SIZE(timberdale_gpio_resources),
557                 .resources = timberdale_gpio_resources,
558                 .mfd_data = &timberdale_gpio_platform_data,
559         },
560         {
561                 .name = "timb-video",
562                 .num_resources = ARRAY_SIZE(timberdale_video_resources),
563                 .resources = timberdale_video_resources,
564                 .mfd_data = &timberdale_video_platform_data,
565         },
566         {
567                 .name = "timb-radio",
568                 .num_resources = ARRAY_SIZE(timberdale_radio_resources),
569                 .resources = timberdale_radio_resources,
570                 .mfd_data = &timberdale_radio_platform_data,
571         },
572         {
573                 .name = "xilinx_spi",
574                 .num_resources = ARRAY_SIZE(timberdale_spi_resources),
575                 .resources = timberdale_spi_resources,
576                 .mfd_data = &timberdale_xspi_platform_data,
577         },
578         {
579                 .name = "ks8842",
580                 .num_resources = ARRAY_SIZE(timberdale_eth_resources),
581                 .resources = timberdale_eth_resources,
582                 .mfd_data = &timberdale_ks8842_platform_data,
583         },
584 };
585
586 static const __devinitconst struct resource timberdale_sdhc_resources[] = {
587         /* located in bar 1 and bar 2 */
588         {
589                 .start  = SDHC0OFFSET,
590                 .end    = SDHC0END,
591                 .flags  = IORESOURCE_MEM,
592         },
593         {
594                 .start  = IRQ_TIMBERDALE_SDHC,
595                 .end    = IRQ_TIMBERDALE_SDHC,
596                 .flags  = IORESOURCE_IRQ,
597         },
598 };
599
600 static __devinitdata struct mfd_cell timberdale_cells_bar1[] = {
601         {
602                 .name = "sdhci",
603                 .num_resources = ARRAY_SIZE(timberdale_sdhc_resources),
604                 .resources = timberdale_sdhc_resources,
605         },
606 };
607
608 static __devinitdata struct mfd_cell timberdale_cells_bar2[] = {
609         {
610                 .name = "sdhci",
611                 .num_resources = ARRAY_SIZE(timberdale_sdhc_resources),
612                 .resources = timberdale_sdhc_resources,
613         },
614 };
615
616 static ssize_t show_fw_ver(struct device *dev, struct device_attribute *attr,
617         char *buf)
618 {
619         struct pci_dev *pdev = to_pci_dev(dev);
620         struct timberdale_device *priv = pci_get_drvdata(pdev);
621
622         return sprintf(buf, "%d.%d.%d\n", priv->fw.major, priv->fw.minor,
623                 priv->fw.config);
624 }
625
626 static DEVICE_ATTR(fw_ver, S_IRUGO, show_fw_ver, NULL);
627
628 /*--------------------------------------------------------------------------*/
629
630 static int __devinit timb_probe(struct pci_dev *dev,
631         const struct pci_device_id *id)
632 {
633         struct timberdale_device *priv;
634         int err, i;
635         resource_size_t mapbase;
636         struct msix_entry *msix_entries = NULL;
637         u8 ip_setup;
638
639         priv = kzalloc(sizeof(*priv), GFP_KERNEL);
640         if (!priv)
641                 return -ENOMEM;
642
643         pci_set_drvdata(dev, priv);
644
645         err = pci_enable_device(dev);
646         if (err)
647                 goto err_enable;
648
649         mapbase = pci_resource_start(dev, 0);
650         if (!mapbase) {
651                 dev_err(&dev->dev, "No resource\n");
652                 goto err_start;
653         }
654
655         /* create a resource for the PCI master register */
656         priv->ctl_mapbase = mapbase + CHIPCTLOFFSET;
657         if (!request_mem_region(priv->ctl_mapbase, CHIPCTLSIZE, "timb-ctl")) {
658                 dev_err(&dev->dev, "Failed to request ctl mem\n");
659                 goto err_request;
660         }
661
662         priv->ctl_membase = ioremap(priv->ctl_mapbase, CHIPCTLSIZE);
663         if (!priv->ctl_membase) {
664                 dev_err(&dev->dev, "ioremap failed for ctl mem\n");
665                 goto err_ioremap;
666         }
667
668         /* read the HW config */
669         priv->fw.major = ioread32(priv->ctl_membase + TIMB_REV_MAJOR);
670         priv->fw.minor = ioread32(priv->ctl_membase + TIMB_REV_MINOR);
671         priv->fw.config = ioread32(priv->ctl_membase + TIMB_HW_CONFIG);
672
673         if (priv->fw.major > TIMB_SUPPORTED_MAJOR) {
674                 dev_err(&dev->dev, "The driver supports an older "
675                         "version of the FPGA, please update the driver to "
676                         "support %d.%d\n", priv->fw.major, priv->fw.minor);
677                 goto err_ioremap;
678         }
679         if (priv->fw.major < TIMB_SUPPORTED_MAJOR ||
680                 priv->fw.minor < TIMB_REQUIRED_MINOR) {
681                 dev_err(&dev->dev, "The FPGA image is too old (%d.%d), "
682                         "please upgrade the FPGA to at least: %d.%d\n",
683                         priv->fw.major, priv->fw.minor,
684                         TIMB_SUPPORTED_MAJOR, TIMB_REQUIRED_MINOR);
685                 goto err_ioremap;
686         }
687
688         msix_entries = kzalloc(TIMBERDALE_NR_IRQS * sizeof(*msix_entries),
689                 GFP_KERNEL);
690         if (!msix_entries)
691                 goto err_ioremap;
692
693         for (i = 0; i < TIMBERDALE_NR_IRQS; i++)
694                 msix_entries[i].entry = i;
695
696         err = pci_enable_msix(dev, msix_entries, TIMBERDALE_NR_IRQS);
697         if (err) {
698                 dev_err(&dev->dev,
699                         "MSI-X init failed: %d, expected entries: %d\n",
700                         err, TIMBERDALE_NR_IRQS);
701                 goto err_msix;
702         }
703
704         err = device_create_file(&dev->dev, &dev_attr_fw_ver);
705         if (err)
706                 goto err_create_file;
707
708         /* Reset all FPGA PLB peripherals */
709         iowrite32(0x1, priv->ctl_membase + TIMB_SW_RST);
710
711         /* update IRQ offsets in I2C board info */
712         for (i = 0; i < ARRAY_SIZE(timberdale_i2c_board_info); i++)
713                 timberdale_i2c_board_info[i].irq =
714                         msix_entries[timberdale_i2c_board_info[i].irq].vector;
715
716         /* Update the SPI configuration depending on the HW (8 or 16 bit) */
717         if (priv->fw.config & TIMB_HW_CONFIG_SPI_8BIT) {
718                 timberdale_xspi_platform_data.bits_per_word = 8;
719                 timberdale_xspi_platform_data.devices =
720                         timberdale_spi_8bit_board_info;
721                 timberdale_xspi_platform_data.num_devices =
722                         ARRAY_SIZE(timberdale_spi_8bit_board_info);
723         } else {
724                 timberdale_xspi_platform_data.bits_per_word = 16;
725                 timberdale_xspi_platform_data.devices =
726                         timberdale_spi_16bit_board_info;
727                 timberdale_xspi_platform_data.num_devices =
728                         ARRAY_SIZE(timberdale_spi_16bit_board_info);
729         }
730
731         ip_setup = priv->fw.config & TIMB_HW_VER_MASK;
732         switch (ip_setup) {
733         case TIMB_HW_VER0:
734                 err = mfd_add_devices(&dev->dev, -1,
735                         timberdale_cells_bar0_cfg0,
736                         ARRAY_SIZE(timberdale_cells_bar0_cfg0),
737                         &dev->resource[0], msix_entries[0].vector);
738                 break;
739         case TIMB_HW_VER1:
740                 err = mfd_add_devices(&dev->dev, -1,
741                         timberdale_cells_bar0_cfg1,
742                         ARRAY_SIZE(timberdale_cells_bar0_cfg1),
743                         &dev->resource[0], msix_entries[0].vector);
744                 break;
745         case TIMB_HW_VER2:
746                 err = mfd_add_devices(&dev->dev, -1,
747                         timberdale_cells_bar0_cfg2,
748                         ARRAY_SIZE(timberdale_cells_bar0_cfg2),
749                         &dev->resource[0], msix_entries[0].vector);
750                 break;
751         case TIMB_HW_VER3:
752                 err = mfd_add_devices(&dev->dev, -1,
753                         timberdale_cells_bar0_cfg3,
754                         ARRAY_SIZE(timberdale_cells_bar0_cfg3),
755                         &dev->resource[0], msix_entries[0].vector);
756                 break;
757         default:
758                 dev_err(&dev->dev, "Uknown IP setup: %d.%d.%d\n",
759                         priv->fw.major, priv->fw.minor, ip_setup);
760                 err = -ENODEV;
761                 goto err_mfd;
762                 break;
763         }
764
765         if (err) {
766                 dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
767                 goto err_mfd;
768         }
769
770         err = mfd_add_devices(&dev->dev, 0,
771                 timberdale_cells_bar1, ARRAY_SIZE(timberdale_cells_bar1),
772                 &dev->resource[1], msix_entries[0].vector);
773         if (err) {
774                 dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
775                 goto err_mfd2;
776         }
777
778         /* only version 0 and 3 have the iNand routed to SDHCI */
779         if (((priv->fw.config & TIMB_HW_VER_MASK) == TIMB_HW_VER0) ||
780                 ((priv->fw.config & TIMB_HW_VER_MASK) == TIMB_HW_VER3)) {
781                 err = mfd_add_devices(&dev->dev, 1, timberdale_cells_bar2,
782                         ARRAY_SIZE(timberdale_cells_bar2),
783                         &dev->resource[2], msix_entries[0].vector);
784                 if (err) {
785                         dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
786                         goto err_mfd2;
787                 }
788         }
789
790         kfree(msix_entries);
791
792         dev_info(&dev->dev,
793                 "Found Timberdale Card. Rev: %d.%d, HW config: 0x%02x\n",
794                 priv->fw.major, priv->fw.minor, priv->fw.config);
795
796         return 0;
797
798 err_mfd2:
799         mfd_remove_devices(&dev->dev);
800 err_mfd:
801         device_remove_file(&dev->dev, &dev_attr_fw_ver);
802 err_create_file:
803         pci_disable_msix(dev);
804 err_msix:
805         iounmap(priv->ctl_membase);
806 err_ioremap:
807         release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE);
808 err_request:
809         pci_set_drvdata(dev, NULL);
810 err_start:
811         pci_disable_device(dev);
812 err_enable:
813         kfree(msix_entries);
814         kfree(priv);
815         pci_set_drvdata(dev, NULL);
816         return -ENODEV;
817 }
818
819 static void __devexit timb_remove(struct pci_dev *dev)
820 {
821         struct timberdale_device *priv = pci_get_drvdata(dev);
822
823         mfd_remove_devices(&dev->dev);
824
825         device_remove_file(&dev->dev, &dev_attr_fw_ver);
826
827         iounmap(priv->ctl_membase);
828         release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE);
829
830         pci_disable_msix(dev);
831         pci_disable_device(dev);
832         pci_set_drvdata(dev, NULL);
833         kfree(priv);
834 }
835
836 static struct pci_device_id timberdale_pci_tbl[] = {
837         { PCI_DEVICE(PCI_VENDOR_ID_TIMB, PCI_DEVICE_ID_TIMB) },
838         { 0 }
839 };
840 MODULE_DEVICE_TABLE(pci, timberdale_pci_tbl);
841
842 static struct pci_driver timberdale_pci_driver = {
843         .name = DRIVER_NAME,
844         .id_table = timberdale_pci_tbl,
845         .probe = timb_probe,
846         .remove = __devexit_p(timb_remove),
847 };
848
849 static int __init timberdale_init(void)
850 {
851         int err;
852
853         err = pci_register_driver(&timberdale_pci_driver);
854         if (err < 0) {
855                 printk(KERN_ERR
856                         "Failed to register PCI driver for %s device.\n",
857                         timberdale_pci_driver.name);
858                 return -ENODEV;
859         }
860
861         printk(KERN_INFO "Driver for %s has been successfully registered.\n",
862                 timberdale_pci_driver.name);
863
864         return 0;
865 }
866
867 static void __exit timberdale_exit(void)
868 {
869         pci_unregister_driver(&timberdale_pci_driver);
870
871         printk(KERN_INFO "Driver for %s has been successfully unregistered.\n",
872                 timberdale_pci_driver.name);
873 }
874
875 module_init(timberdale_init);
876 module_exit(timberdale_exit);
877
878 MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
879 MODULE_VERSION(DRV_VERSION);
880 MODULE_LICENSE("GPL v2");