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