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