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