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