xref: /openbmc/linux/drivers/mfd/timberdale.c (revision df2634f43f5106947f3735a0b61a6527a4b278cd)
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 __devinitdata 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 __devinitdata struct ocores_i2c_platform_data
85 timberdale_ocores_platform_data = {
86 	.regstep = 4,
87 	.clock_khz = 62500,
88 	.devices = timberdale_i2c_board_info,
89 	.num_devices = ARRAY_SIZE(timberdale_i2c_board_info)
90 };
91 
92 static const __devinitconst 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 __devinitconst 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 __devinitdata 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 __devinitconst 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 __devinitdata 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 __devinitconst 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 __devinitdata 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 __devinitconst 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 __devinitconst 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 __devinitconst 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 __devinitconst 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 __devinitdata 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 __devinitdata 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 __devinitconst 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 __devinitdata struct i2c_board_info timberdale_tef6868_i2c_board_info = {
280 	I2C_BOARD_INFO("tef6862", 0x60)
281 };
282 
283 static __devinitdata struct i2c_board_info timberdale_saa7706_i2c_board_info = {
284 	I2C_BOARD_INFO("saa7706h", 0x1C)
285 };
286 
287 static __devinitdata struct timb_radio_platform_data
288 	timberdale_radio_platform_data = {
289 	.i2c_adapter = 0,
290 	.tuner = {
291 		.info = &timberdale_tef6868_i2c_board_info
292 	},
293 	.dsp = {
294 		.info = &timberdale_saa7706_i2c_board_info
295 	}
296 };
297 
298 static const __devinitconst struct resource timberdale_video_resources[] = {
299 	{
300 		.start	= LOGIWOFFSET,
301 		.end	= LOGIWEND,
302 		.flags	= IORESOURCE_MEM,
303 	},
304 	/*
305 	note that the "frame buffer" is located in DMA area
306 	starting at 0x1200000
307 	*/
308 };
309 
310 static __devinitdata struct timb_dma_platform_data timb_dma_platform_data = {
311 	.nr_channels = 10,
312 	.channels = {
313 		{
314 			/* UART RX */
315 			.rx = true,
316 			.descriptors = 2,
317 			.descriptor_elements = 1
318 		},
319 		{
320 			/* UART TX */
321 			.rx = false,
322 			.descriptors = 2,
323 			.descriptor_elements = 1
324 		},
325 		{
326 			/* MLB RX */
327 			.rx = true,
328 			.descriptors = 2,
329 			.descriptor_elements = 1
330 		},
331 		{
332 			/* MLB TX */
333 			.rx = false,
334 			.descriptors = 2,
335 			.descriptor_elements = 1
336 		},
337 		{
338 			/* Video RX */
339 			.rx = true,
340 			.bytes_per_line = 1440,
341 			.descriptors = 2,
342 			.descriptor_elements = 16
343 		},
344 		{
345 			/* Video framedrop */
346 		},
347 		{
348 			/* SDHCI RX */
349 			.rx = true,
350 		},
351 		{
352 			/* SDHCI TX */
353 		},
354 		{
355 			/* ETH RX */
356 			.rx = true,
357 			.descriptors = 2,
358 			.descriptor_elements = 1
359 		},
360 		{
361 			/* ETH TX */
362 			.rx = false,
363 			.descriptors = 2,
364 			.descriptor_elements = 1
365 		},
366 	}
367 };
368 
369 static const __devinitconst struct resource timberdale_dma_resources[] = {
370 	{
371 		.start	= DMAOFFSET,
372 		.end	= DMAEND,
373 		.flags	= IORESOURCE_MEM,
374 	},
375 	{
376 		.start	= IRQ_TIMBERDALE_DMA,
377 		.end	= IRQ_TIMBERDALE_DMA,
378 		.flags	= IORESOURCE_IRQ,
379 	},
380 };
381 
382 static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = {
383 	{
384 		.name = "timb-dma",
385 		.num_resources = ARRAY_SIZE(timberdale_dma_resources),
386 		.resources = timberdale_dma_resources,
387 		.platform_data = &timb_dma_platform_data,
388 		.data_size = sizeof(timb_dma_platform_data),
389 	},
390 	{
391 		.name = "timb-uart",
392 		.num_resources = ARRAY_SIZE(timberdale_uart_resources),
393 		.resources = timberdale_uart_resources,
394 	},
395 	{
396 		.name = "xiic-i2c",
397 		.num_resources = ARRAY_SIZE(timberdale_xiic_resources),
398 		.resources = timberdale_xiic_resources,
399 		.platform_data = &timberdale_xiic_platform_data,
400 		.data_size = sizeof(timberdale_xiic_platform_data),
401 	},
402 	{
403 		.name = "timb-gpio",
404 		.num_resources = ARRAY_SIZE(timberdale_gpio_resources),
405 		.resources = timberdale_gpio_resources,
406 		.platform_data = &timberdale_gpio_platform_data,
407 		.data_size = sizeof(timberdale_gpio_platform_data),
408 	},
409 	{
410 		.name = "timb-video",
411 		.num_resources = ARRAY_SIZE(timberdale_video_resources),
412 		.resources = timberdale_video_resources,
413 		.platform_data = &timberdale_video_platform_data,
414 		.data_size = sizeof(timberdale_video_platform_data),
415 	},
416 	{
417 		.name = "timb-radio",
418 		.num_resources = ARRAY_SIZE(timberdale_radio_resources),
419 		.resources = timberdale_radio_resources,
420 		.platform_data = &timberdale_radio_platform_data,
421 		.data_size = sizeof(timberdale_radio_platform_data),
422 	},
423 	{
424 		.name = "xilinx_spi",
425 		.num_resources = ARRAY_SIZE(timberdale_spi_resources),
426 		.resources = timberdale_spi_resources,
427 		.platform_data = &timberdale_xspi_platform_data,
428 		.data_size = sizeof(timberdale_xspi_platform_data),
429 	},
430 	{
431 		.name = "ks8842",
432 		.num_resources = ARRAY_SIZE(timberdale_eth_resources),
433 		.resources = timberdale_eth_resources,
434 		.platform_data = &timberdale_ks8842_platform_data,
435 		.data_size = sizeof(timberdale_ks8842_platform_data)
436 	},
437 };
438 
439 static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = {
440 	{
441 		.name = "timb-dma",
442 		.num_resources = ARRAY_SIZE(timberdale_dma_resources),
443 		.resources = timberdale_dma_resources,
444 		.platform_data = &timb_dma_platform_data,
445 		.data_size = sizeof(timb_dma_platform_data),
446 	},
447 	{
448 		.name = "timb-uart",
449 		.num_resources = ARRAY_SIZE(timberdale_uart_resources),
450 		.resources = timberdale_uart_resources,
451 	},
452 	{
453 		.name = "uartlite",
454 		.num_resources = ARRAY_SIZE(timberdale_uartlite_resources),
455 		.resources = timberdale_uartlite_resources,
456 	},
457 	{
458 		.name = "xiic-i2c",
459 		.num_resources = ARRAY_SIZE(timberdale_xiic_resources),
460 		.resources = timberdale_xiic_resources,
461 		.platform_data = &timberdale_xiic_platform_data,
462 		.data_size = sizeof(timberdale_xiic_platform_data),
463 	},
464 	{
465 		.name = "timb-gpio",
466 		.num_resources = ARRAY_SIZE(timberdale_gpio_resources),
467 		.resources = timberdale_gpio_resources,
468 		.platform_data = &timberdale_gpio_platform_data,
469 		.data_size = sizeof(timberdale_gpio_platform_data),
470 	},
471 	{
472 		.name = "timb-mlogicore",
473 		.num_resources = ARRAY_SIZE(timberdale_mlogicore_resources),
474 		.resources = timberdale_mlogicore_resources,
475 	},
476 	{
477 		.name = "timb-video",
478 		.num_resources = ARRAY_SIZE(timberdale_video_resources),
479 		.resources = timberdale_video_resources,
480 		.platform_data = &timberdale_video_platform_data,
481 		.data_size = sizeof(timberdale_video_platform_data),
482 	},
483 	{
484 		.name = "timb-radio",
485 		.num_resources = ARRAY_SIZE(timberdale_radio_resources),
486 		.resources = timberdale_radio_resources,
487 		.platform_data = &timberdale_radio_platform_data,
488 		.data_size = sizeof(timberdale_radio_platform_data),
489 	},
490 	{
491 		.name = "xilinx_spi",
492 		.num_resources = ARRAY_SIZE(timberdale_spi_resources),
493 		.resources = timberdale_spi_resources,
494 		.platform_data = &timberdale_xspi_platform_data,
495 		.data_size = sizeof(timberdale_xspi_platform_data),
496 	},
497 	{
498 		.name = "ks8842",
499 		.num_resources = ARRAY_SIZE(timberdale_eth_resources),
500 		.resources = timberdale_eth_resources,
501 		.platform_data = &timberdale_ks8842_platform_data,
502 		.data_size = sizeof(timberdale_ks8842_platform_data)
503 	},
504 };
505 
506 static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = {
507 	{
508 		.name = "timb-dma",
509 		.num_resources = ARRAY_SIZE(timberdale_dma_resources),
510 		.resources = timberdale_dma_resources,
511 		.platform_data = &timb_dma_platform_data,
512 		.data_size = sizeof(timb_dma_platform_data),
513 	},
514 	{
515 		.name = "timb-uart",
516 		.num_resources = ARRAY_SIZE(timberdale_uart_resources),
517 		.resources = timberdale_uart_resources,
518 	},
519 	{
520 		.name = "xiic-i2c",
521 		.num_resources = ARRAY_SIZE(timberdale_xiic_resources),
522 		.resources = timberdale_xiic_resources,
523 		.platform_data = &timberdale_xiic_platform_data,
524 		.data_size = sizeof(timberdale_xiic_platform_data),
525 	},
526 	{
527 		.name = "timb-gpio",
528 		.num_resources = ARRAY_SIZE(timberdale_gpio_resources),
529 		.resources = timberdale_gpio_resources,
530 		.platform_data = &timberdale_gpio_platform_data,
531 		.data_size = sizeof(timberdale_gpio_platform_data),
532 	},
533 	{
534 		.name = "timb-video",
535 		.num_resources = ARRAY_SIZE(timberdale_video_resources),
536 		.resources = timberdale_video_resources,
537 		.platform_data = &timberdale_video_platform_data,
538 		.data_size = sizeof(timberdale_video_platform_data),
539 	},
540 	{
541 		.name = "timb-radio",
542 		.num_resources = ARRAY_SIZE(timberdale_radio_resources),
543 		.resources = timberdale_radio_resources,
544 		.platform_data = &timberdale_radio_platform_data,
545 		.data_size = sizeof(timberdale_radio_platform_data),
546 	},
547 	{
548 		.name = "xilinx_spi",
549 		.num_resources = ARRAY_SIZE(timberdale_spi_resources),
550 		.resources = timberdale_spi_resources,
551 		.platform_data = &timberdale_xspi_platform_data,
552 		.data_size = sizeof(timberdale_xspi_platform_data),
553 	},
554 };
555 
556 static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = {
557 	{
558 		.name = "timb-dma",
559 		.num_resources = ARRAY_SIZE(timberdale_dma_resources),
560 		.resources = timberdale_dma_resources,
561 		.platform_data = &timb_dma_platform_data,
562 		.data_size = sizeof(timb_dma_platform_data),
563 	},
564 	{
565 		.name = "timb-uart",
566 		.num_resources = ARRAY_SIZE(timberdale_uart_resources),
567 		.resources = timberdale_uart_resources,
568 	},
569 	{
570 		.name = "ocores-i2c",
571 		.num_resources = ARRAY_SIZE(timberdale_ocores_resources),
572 		.resources = timberdale_ocores_resources,
573 		.platform_data = &timberdale_ocores_platform_data,
574 		.data_size = sizeof(timberdale_ocores_platform_data),
575 	},
576 	{
577 		.name = "timb-gpio",
578 		.num_resources = ARRAY_SIZE(timberdale_gpio_resources),
579 		.resources = timberdale_gpio_resources,
580 		.platform_data = &timberdale_gpio_platform_data,
581 		.data_size = sizeof(timberdale_gpio_platform_data),
582 	},
583 	{
584 		.name = "timb-video",
585 		.num_resources = ARRAY_SIZE(timberdale_video_resources),
586 		.resources = timberdale_video_resources,
587 		.platform_data = &timberdale_video_platform_data,
588 		.data_size = sizeof(timberdale_video_platform_data),
589 	},
590 	{
591 		.name = "timb-radio",
592 		.num_resources = ARRAY_SIZE(timberdale_radio_resources),
593 		.resources = timberdale_radio_resources,
594 		.platform_data = &timberdale_radio_platform_data,
595 		.data_size = sizeof(timberdale_radio_platform_data),
596 	},
597 	{
598 		.name = "xilinx_spi",
599 		.num_resources = ARRAY_SIZE(timberdale_spi_resources),
600 		.resources = timberdale_spi_resources,
601 		.platform_data = &timberdale_xspi_platform_data,
602 		.data_size = sizeof(timberdale_xspi_platform_data),
603 	},
604 	{
605 		.name = "ks8842",
606 		.num_resources = ARRAY_SIZE(timberdale_eth_resources),
607 		.resources = timberdale_eth_resources,
608 		.platform_data = &timberdale_ks8842_platform_data,
609 		.data_size = sizeof(timberdale_ks8842_platform_data)
610 	},
611 };
612 
613 static const __devinitconst struct resource timberdale_sdhc_resources[] = {
614 	/* located in bar 1 and bar 2 */
615 	{
616 		.start	= SDHC0OFFSET,
617 		.end	= SDHC0END,
618 		.flags	= IORESOURCE_MEM,
619 	},
620 	{
621 		.start	= IRQ_TIMBERDALE_SDHC,
622 		.end	= IRQ_TIMBERDALE_SDHC,
623 		.flags	= IORESOURCE_IRQ,
624 	},
625 };
626 
627 static __devinitdata struct mfd_cell timberdale_cells_bar1[] = {
628 	{
629 		.name = "sdhci",
630 		.num_resources = ARRAY_SIZE(timberdale_sdhc_resources),
631 		.resources = timberdale_sdhc_resources,
632 	},
633 };
634 
635 static __devinitdata struct mfd_cell timberdale_cells_bar2[] = {
636 	{
637 		.name = "sdhci",
638 		.num_resources = ARRAY_SIZE(timberdale_sdhc_resources),
639 		.resources = timberdale_sdhc_resources,
640 	},
641 };
642 
643 static ssize_t show_fw_ver(struct device *dev, struct device_attribute *attr,
644 	char *buf)
645 {
646 	struct pci_dev *pdev = to_pci_dev(dev);
647 	struct timberdale_device *priv = pci_get_drvdata(pdev);
648 
649 	return sprintf(buf, "%d.%d.%d\n", priv->fw.major, priv->fw.minor,
650 		priv->fw.config);
651 }
652 
653 static DEVICE_ATTR(fw_ver, S_IRUGO, show_fw_ver, NULL);
654 
655 /*--------------------------------------------------------------------------*/
656 
657 static int __devinit timb_probe(struct pci_dev *dev,
658 	const struct pci_device_id *id)
659 {
660 	struct timberdale_device *priv;
661 	int err, i;
662 	resource_size_t mapbase;
663 	struct msix_entry *msix_entries = NULL;
664 	u8 ip_setup;
665 
666 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
667 	if (!priv)
668 		return -ENOMEM;
669 
670 	pci_set_drvdata(dev, priv);
671 
672 	err = pci_enable_device(dev);
673 	if (err)
674 		goto err_enable;
675 
676 	mapbase = pci_resource_start(dev, 0);
677 	if (!mapbase) {
678 		dev_err(&dev->dev, "No resource\n");
679 		goto err_start;
680 	}
681 
682 	/* create a resource for the PCI master register */
683 	priv->ctl_mapbase = mapbase + CHIPCTLOFFSET;
684 	if (!request_mem_region(priv->ctl_mapbase, CHIPCTLSIZE, "timb-ctl")) {
685 		dev_err(&dev->dev, "Failed to request ctl mem\n");
686 		goto err_request;
687 	}
688 
689 	priv->ctl_membase = ioremap(priv->ctl_mapbase, CHIPCTLSIZE);
690 	if (!priv->ctl_membase) {
691 		dev_err(&dev->dev, "ioremap failed for ctl mem\n");
692 		goto err_ioremap;
693 	}
694 
695 	/* read the HW config */
696 	priv->fw.major = ioread32(priv->ctl_membase + TIMB_REV_MAJOR);
697 	priv->fw.minor = ioread32(priv->ctl_membase + TIMB_REV_MINOR);
698 	priv->fw.config = ioread32(priv->ctl_membase + TIMB_HW_CONFIG);
699 
700 	if (priv->fw.major > TIMB_SUPPORTED_MAJOR) {
701 		dev_err(&dev->dev, "The driver supports an older "
702 			"version of the FPGA, please update the driver to "
703 			"support %d.%d\n", priv->fw.major, priv->fw.minor);
704 		goto err_ioremap;
705 	}
706 	if (priv->fw.major < TIMB_SUPPORTED_MAJOR ||
707 		priv->fw.minor < TIMB_REQUIRED_MINOR) {
708 		dev_err(&dev->dev, "The FPGA image is too old (%d.%d), "
709 			"please upgrade the FPGA to at least: %d.%d\n",
710 			priv->fw.major, priv->fw.minor,
711 			TIMB_SUPPORTED_MAJOR, TIMB_REQUIRED_MINOR);
712 		goto err_ioremap;
713 	}
714 
715 	msix_entries = kzalloc(TIMBERDALE_NR_IRQS * sizeof(*msix_entries),
716 		GFP_KERNEL);
717 	if (!msix_entries)
718 		goto err_ioremap;
719 
720 	for (i = 0; i < TIMBERDALE_NR_IRQS; i++)
721 		msix_entries[i].entry = i;
722 
723 	err = pci_enable_msix(dev, msix_entries, TIMBERDALE_NR_IRQS);
724 	if (err) {
725 		dev_err(&dev->dev,
726 			"MSI-X init failed: %d, expected entries: %d\n",
727 			err, TIMBERDALE_NR_IRQS);
728 		goto err_msix;
729 	}
730 
731 	err = device_create_file(&dev->dev, &dev_attr_fw_ver);
732 	if (err)
733 		goto err_create_file;
734 
735 	/* Reset all FPGA PLB peripherals */
736 	iowrite32(0x1, priv->ctl_membase + TIMB_SW_RST);
737 
738 	/* update IRQ offsets in I2C board info */
739 	for (i = 0; i < ARRAY_SIZE(timberdale_i2c_board_info); i++)
740 		timberdale_i2c_board_info[i].irq =
741 			msix_entries[timberdale_i2c_board_info[i].irq].vector;
742 
743 	/* Update the SPI configuration depending on the HW (8 or 16 bit) */
744 	if (priv->fw.config & TIMB_HW_CONFIG_SPI_8BIT) {
745 		timberdale_xspi_platform_data.bits_per_word = 8;
746 		timberdale_xspi_platform_data.devices =
747 			timberdale_spi_8bit_board_info;
748 		timberdale_xspi_platform_data.num_devices =
749 			ARRAY_SIZE(timberdale_spi_8bit_board_info);
750 	} else {
751 		timberdale_xspi_platform_data.bits_per_word = 16;
752 		timberdale_xspi_platform_data.devices =
753 			timberdale_spi_16bit_board_info;
754 		timberdale_xspi_platform_data.num_devices =
755 			ARRAY_SIZE(timberdale_spi_16bit_board_info);
756 	}
757 
758 	ip_setup = priv->fw.config & TIMB_HW_VER_MASK;
759 	switch (ip_setup) {
760 	case TIMB_HW_VER0:
761 		err = mfd_add_devices(&dev->dev, -1,
762 			timberdale_cells_bar0_cfg0,
763 			ARRAY_SIZE(timberdale_cells_bar0_cfg0),
764 			&dev->resource[0], msix_entries[0].vector);
765 		break;
766 	case TIMB_HW_VER1:
767 		err = mfd_add_devices(&dev->dev, -1,
768 			timberdale_cells_bar0_cfg1,
769 			ARRAY_SIZE(timberdale_cells_bar0_cfg1),
770 			&dev->resource[0], msix_entries[0].vector);
771 		break;
772 	case TIMB_HW_VER2:
773 		err = mfd_add_devices(&dev->dev, -1,
774 			timberdale_cells_bar0_cfg2,
775 			ARRAY_SIZE(timberdale_cells_bar0_cfg2),
776 			&dev->resource[0], msix_entries[0].vector);
777 		break;
778 	case TIMB_HW_VER3:
779 		err = mfd_add_devices(&dev->dev, -1,
780 			timberdale_cells_bar0_cfg3,
781 			ARRAY_SIZE(timberdale_cells_bar0_cfg3),
782 			&dev->resource[0], msix_entries[0].vector);
783 		break;
784 	default:
785 		dev_err(&dev->dev, "Uknown IP setup: %d.%d.%d\n",
786 			priv->fw.major, priv->fw.minor, ip_setup);
787 		err = -ENODEV;
788 		goto err_mfd;
789 		break;
790 	}
791 
792 	if (err) {
793 		dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
794 		goto err_mfd;
795 	}
796 
797 	err = mfd_add_devices(&dev->dev, 0,
798 		timberdale_cells_bar1, ARRAY_SIZE(timberdale_cells_bar1),
799 		&dev->resource[1], msix_entries[0].vector);
800 	if (err) {
801 		dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
802 		goto err_mfd2;
803 	}
804 
805 	/* only version 0 and 3 have the iNand routed to SDHCI */
806 	if (((priv->fw.config & TIMB_HW_VER_MASK) == TIMB_HW_VER0) ||
807 		((priv->fw.config & TIMB_HW_VER_MASK) == TIMB_HW_VER3)) {
808 		err = mfd_add_devices(&dev->dev, 1, timberdale_cells_bar2,
809 			ARRAY_SIZE(timberdale_cells_bar2),
810 			&dev->resource[2], msix_entries[0].vector);
811 		if (err) {
812 			dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
813 			goto err_mfd2;
814 		}
815 	}
816 
817 	kfree(msix_entries);
818 
819 	dev_info(&dev->dev,
820 		"Found Timberdale Card. Rev: %d.%d, HW config: 0x%02x\n",
821 		priv->fw.major, priv->fw.minor, priv->fw.config);
822 
823 	return 0;
824 
825 err_mfd2:
826 	mfd_remove_devices(&dev->dev);
827 err_mfd:
828 	device_remove_file(&dev->dev, &dev_attr_fw_ver);
829 err_create_file:
830 	pci_disable_msix(dev);
831 err_msix:
832 	iounmap(priv->ctl_membase);
833 err_ioremap:
834 	release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE);
835 err_request:
836 	pci_set_drvdata(dev, NULL);
837 err_start:
838 	pci_disable_device(dev);
839 err_enable:
840 	kfree(msix_entries);
841 	kfree(priv);
842 	pci_set_drvdata(dev, NULL);
843 	return -ENODEV;
844 }
845 
846 static void __devexit timb_remove(struct pci_dev *dev)
847 {
848 	struct timberdale_device *priv = pci_get_drvdata(dev);
849 
850 	mfd_remove_devices(&dev->dev);
851 
852 	device_remove_file(&dev->dev, &dev_attr_fw_ver);
853 
854 	iounmap(priv->ctl_membase);
855 	release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE);
856 
857 	pci_disable_msix(dev);
858 	pci_disable_device(dev);
859 	pci_set_drvdata(dev, NULL);
860 	kfree(priv);
861 }
862 
863 static struct pci_device_id timberdale_pci_tbl[] = {
864 	{ PCI_DEVICE(PCI_VENDOR_ID_TIMB, PCI_DEVICE_ID_TIMB) },
865 	{ 0 }
866 };
867 MODULE_DEVICE_TABLE(pci, timberdale_pci_tbl);
868 
869 static struct pci_driver timberdale_pci_driver = {
870 	.name = DRIVER_NAME,
871 	.id_table = timberdale_pci_tbl,
872 	.probe = timb_probe,
873 	.remove = __devexit_p(timb_remove),
874 };
875 
876 static int __init timberdale_init(void)
877 {
878 	int err;
879 
880 	err = pci_register_driver(&timberdale_pci_driver);
881 	if (err < 0) {
882 		printk(KERN_ERR
883 			"Failed to register PCI driver for %s device.\n",
884 			timberdale_pci_driver.name);
885 		return -ENODEV;
886 	}
887 
888 	printk(KERN_INFO "Driver for %s has been successfully registered.\n",
889 		timberdale_pci_driver.name);
890 
891 	return 0;
892 }
893 
894 static void __exit timberdale_exit(void)
895 {
896 	pci_unregister_driver(&timberdale_pci_driver);
897 
898 	printk(KERN_INFO "Driver for %s has been successfully unregistered.\n",
899 		timberdale_pci_driver.name);
900 }
901 
902 module_init(timberdale_init);
903 module_exit(timberdale_exit);
904 
905 MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
906 MODULE_VERSION(DRV_VERSION);
907 MODULE_LICENSE("GPL v2");
908