GNU Linux-libre 5.10.219-gnu1
[releases.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 timberdale_device *priv = dev_get_drvdata(dev);
630
631         return sprintf(buf, "%d.%d.%d\n", priv->fw.major, priv->fw.minor,
632                 priv->fw.config);
633 }
634
635 static DEVICE_ATTR(fw_ver, S_IRUGO, show_fw_ver, NULL);
636
637 /*--------------------------------------------------------------------------*/
638
639 static int timb_probe(struct pci_dev *dev,
640         const struct pci_device_id *id)
641 {
642         struct timberdale_device *priv;
643         int err, i;
644         resource_size_t mapbase;
645         struct msix_entry *msix_entries = NULL;
646         u8 ip_setup;
647
648         priv = kzalloc(sizeof(*priv), GFP_KERNEL);
649         if (!priv)
650                 return -ENOMEM;
651
652         pci_set_drvdata(dev, priv);
653
654         err = pci_enable_device(dev);
655         if (err)
656                 goto err_enable;
657
658         mapbase = pci_resource_start(dev, 0);
659         if (!mapbase) {
660                 dev_err(&dev->dev, "No resource\n");
661                 goto err_start;
662         }
663
664         /* create a resource for the PCI master register */
665         priv->ctl_mapbase = mapbase + CHIPCTLOFFSET;
666         if (!request_mem_region(priv->ctl_mapbase, CHIPCTLSIZE, "timb-ctl")) {
667                 dev_err(&dev->dev, "Failed to request ctl mem\n");
668                 goto err_start;
669         }
670
671         priv->ctl_membase = ioremap(priv->ctl_mapbase, CHIPCTLSIZE);
672         if (!priv->ctl_membase) {
673                 dev_err(&dev->dev, "ioremap failed for ctl mem\n");
674                 goto err_ioremap;
675         }
676
677         /* read the HW config */
678         priv->fw.major = ioread32(priv->ctl_membase + TIMB_REV_MAJOR);
679         priv->fw.minor = ioread32(priv->ctl_membase + TIMB_REV_MINOR);
680         priv->fw.config = ioread32(priv->ctl_membase + TIMB_HW_CONFIG);
681
682         if (priv->fw.major > TIMB_SUPPORTED_MAJOR) {
683                 dev_err(&dev->dev, "The driver supports an older "
684                         "version of the FPGA, please update the driver to "
685                         "support %d.%d\n", priv->fw.major, priv->fw.minor);
686                 goto err_config;
687         }
688         if (priv->fw.major < TIMB_SUPPORTED_MAJOR ||
689                 priv->fw.minor < TIMB_REQUIRED_MINOR) {
690                 dev_err(&dev->dev, "The FPGA image is too old (%d.%d), "
691                         "please upgrade the FPGA to at least: %d.%d\n",
692                         priv->fw.major, priv->fw.minor,
693                         TIMB_SUPPORTED_MAJOR, TIMB_REQUIRED_MINOR);
694                 goto err_config;
695         }
696
697         msix_entries = kcalloc(TIMBERDALE_NR_IRQS, sizeof(*msix_entries),
698                                GFP_KERNEL);
699         if (!msix_entries)
700                 goto err_config;
701
702         for (i = 0; i < TIMBERDALE_NR_IRQS; i++)
703                 msix_entries[i].entry = i;
704
705         err = pci_enable_msix_exact(dev, msix_entries, TIMBERDALE_NR_IRQS);
706         if (err) {
707                 dev_err(&dev->dev,
708                         "MSI-X init failed: %d, expected entries: %d\n",
709                         err, TIMBERDALE_NR_IRQS);
710                 goto err_msix;
711         }
712
713         err = device_create_file(&dev->dev, &dev_attr_fw_ver);
714         if (err)
715                 goto err_create_file;
716
717         /* Reset all FPGA PLB peripherals */
718         iowrite32(0x1, priv->ctl_membase + TIMB_SW_RST);
719
720         /* update IRQ offsets in I2C board info */
721         for (i = 0; i < ARRAY_SIZE(timberdale_i2c_board_info); i++)
722                 timberdale_i2c_board_info[i].irq =
723                         msix_entries[timberdale_i2c_board_info[i].irq].vector;
724
725         /* Update the SPI configuration depending on the HW (8 or 16 bit) */
726         if (priv->fw.config & TIMB_HW_CONFIG_SPI_8BIT) {
727                 timberdale_xspi_platform_data.bits_per_word = 8;
728                 timberdale_xspi_platform_data.devices =
729                         timberdale_spi_8bit_board_info;
730                 timberdale_xspi_platform_data.num_devices =
731                         ARRAY_SIZE(timberdale_spi_8bit_board_info);
732         } else {
733                 timberdale_xspi_platform_data.bits_per_word = 16;
734                 timberdale_xspi_platform_data.devices =
735                         timberdale_spi_16bit_board_info;
736                 timberdale_xspi_platform_data.num_devices =
737                         ARRAY_SIZE(timberdale_spi_16bit_board_info);
738         }
739
740         ip_setup = priv->fw.config & TIMB_HW_VER_MASK;
741         switch (ip_setup) {
742         case TIMB_HW_VER0:
743                 err = mfd_add_devices(&dev->dev, -1,
744                         timberdale_cells_bar0_cfg0,
745                         ARRAY_SIZE(timberdale_cells_bar0_cfg0),
746                         &dev->resource[0], msix_entries[0].vector, NULL);
747                 break;
748         case TIMB_HW_VER1:
749                 err = mfd_add_devices(&dev->dev, -1,
750                         timberdale_cells_bar0_cfg1,
751                         ARRAY_SIZE(timberdale_cells_bar0_cfg1),
752                         &dev->resource[0], msix_entries[0].vector, NULL);
753                 break;
754         case TIMB_HW_VER2:
755                 err = mfd_add_devices(&dev->dev, -1,
756                         timberdale_cells_bar0_cfg2,
757                         ARRAY_SIZE(timberdale_cells_bar0_cfg2),
758                         &dev->resource[0], msix_entries[0].vector, NULL);
759                 break;
760         case TIMB_HW_VER3:
761                 err = mfd_add_devices(&dev->dev, -1,
762                         timberdale_cells_bar0_cfg3,
763                         ARRAY_SIZE(timberdale_cells_bar0_cfg3),
764                         &dev->resource[0], msix_entries[0].vector, NULL);
765                 break;
766         default:
767                 dev_err(&dev->dev, "Unknown IP setup: %d.%d.%d\n",
768                         priv->fw.major, priv->fw.minor, ip_setup);
769                 err = -ENODEV;
770                 goto err_mfd;
771         }
772
773         if (err) {
774                 dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
775                 goto err_mfd;
776         }
777
778         err = mfd_add_devices(&dev->dev, 0,
779                 timberdale_cells_bar1, ARRAY_SIZE(timberdale_cells_bar1),
780                 &dev->resource[1], msix_entries[0].vector, NULL);
781         if (err) {
782                 dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
783                 goto err_mfd2;
784         }
785
786         /* only version 0 and 3 have the iNand routed to SDHCI */
787         if (((priv->fw.config & TIMB_HW_VER_MASK) == TIMB_HW_VER0) ||
788                 ((priv->fw.config & TIMB_HW_VER_MASK) == TIMB_HW_VER3)) {
789                 err = mfd_add_devices(&dev->dev, 1, timberdale_cells_bar2,
790                         ARRAY_SIZE(timberdale_cells_bar2),
791                         &dev->resource[2], msix_entries[0].vector, NULL);
792                 if (err) {
793                         dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
794                         goto err_mfd2;
795                 }
796         }
797
798         kfree(msix_entries);
799
800         dev_info(&dev->dev,
801                 "Found Timberdale Card. Rev: %d.%d, HW config: 0x%02x\n",
802                 priv->fw.major, priv->fw.minor, priv->fw.config);
803
804         return 0;
805
806 err_mfd2:
807         mfd_remove_devices(&dev->dev);
808 err_mfd:
809         device_remove_file(&dev->dev, &dev_attr_fw_ver);
810 err_create_file:
811         pci_disable_msix(dev);
812 err_msix:
813         kfree(msix_entries);
814 err_config:
815         iounmap(priv->ctl_membase);
816 err_ioremap:
817         release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE);
818 err_start:
819         pci_disable_device(dev);
820 err_enable:
821         kfree(priv);
822         return -ENODEV;
823 }
824
825 static void timb_remove(struct pci_dev *dev)
826 {
827         struct timberdale_device *priv = pci_get_drvdata(dev);
828
829         mfd_remove_devices(&dev->dev);
830
831         device_remove_file(&dev->dev, &dev_attr_fw_ver);
832
833         iounmap(priv->ctl_membase);
834         release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE);
835
836         pci_disable_msix(dev);
837         pci_disable_device(dev);
838         kfree(priv);
839 }
840
841 static const struct pci_device_id timberdale_pci_tbl[] = {
842         { PCI_DEVICE(PCI_VENDOR_ID_TIMB, PCI_DEVICE_ID_TIMB) },
843         { 0 }
844 };
845 MODULE_DEVICE_TABLE(pci, timberdale_pci_tbl);
846
847 static struct pci_driver timberdale_pci_driver = {
848         .name = DRIVER_NAME,
849         .id_table = timberdale_pci_tbl,
850         .probe = timb_probe,
851         .remove = timb_remove,
852 };
853
854 module_pci_driver(timberdale_pci_driver);
855
856 MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
857 MODULE_VERSION(DRV_VERSION);
858 MODULE_LICENSE("GPL v2");