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