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