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