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