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