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