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