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