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