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