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