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