1 // SPDX-License-Identifier: GPL-2.0-only
3 * timberdale.c timberdale FPGA MFD driver
4 * Copyright (c) 2009 Intel Corporation
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/pci.h>
14 #include <linux/mfd/core.h>
15 #include <linux/property.h>
16 #include <linux/slab.h>
18 #include <linux/timb_gpio.h>
20 #include <linux/i2c.h>
21 #include <linux/platform_data/i2c-ocores.h>
22 #include <linux/platform_data/i2c-xiic.h>
24 #include <linux/spi/spi.h>
25 #include <linux/spi/xilinx_spi.h>
26 #include <linux/spi/max7301.h>
27 #include <linux/spi/mc33880.h>
29 #include <linux/platform_data/media/timb_radio.h>
30 #include <linux/platform_data/media/timb_video.h>
32 #include <linux/timb_dma.h>
34 #include <linux/ks8842.h>
36 #include "timberdale.h"
38 #define DRIVER_NAME "timberdale"
40 struct timberdale_device
{
41 resource_size_t ctl_mapbase
;
42 unsigned char __iomem
*ctl_membase
;
50 /*--------------------------------------------------------------------------*/
52 static const struct property_entry timberdale_tsc2007_properties
[] = {
53 PROPERTY_ENTRY_U32("ti,x-plate-ohms", 100),
57 static const struct software_node timberdale_tsc2007_node
= {
59 .properties
= timberdale_tsc2007_properties
,
62 static struct i2c_board_info timberdale_i2c_board_info
[] = {
64 I2C_BOARD_INFO("tsc2007", 0x48),
65 .irq
= IRQ_TIMBERDALE_TSC_INT
,
66 .swnode
= &timberdale_tsc2007_node
,
70 static struct xiic_i2c_platform_data
71 timberdale_xiic_platform_data
= {
72 .devices
= timberdale_i2c_board_info
,
73 .num_devices
= ARRAY_SIZE(timberdale_i2c_board_info
)
76 static struct ocores_i2c_platform_data
77 timberdale_ocores_platform_data
= {
80 .devices
= timberdale_i2c_board_info
,
81 .num_devices
= ARRAY_SIZE(timberdale_i2c_board_info
)
84 static const struct resource timberdale_xiic_resources
[] = {
88 .flags
= IORESOURCE_MEM
,
91 .start
= IRQ_TIMBERDALE_I2C
,
92 .end
= IRQ_TIMBERDALE_I2C
,
93 .flags
= IORESOURCE_IRQ
,
97 static const struct resource timberdale_ocores_resources
[] = {
99 .start
= OCORESOFFSET
,
101 .flags
= IORESOURCE_MEM
,
104 .start
= IRQ_TIMBERDALE_I2C
,
105 .end
= IRQ_TIMBERDALE_I2C
,
106 .flags
= IORESOURCE_IRQ
,
110 static const struct max7301_platform_data timberdale_max7301_platform_data
= {
114 static const struct mc33880_platform_data timberdale_mc33880_platform_data
= {
118 static struct spi_board_info timberdale_spi_16bit_board_info
[] = {
120 .modalias
= "max7301",
121 .max_speed_hz
= 26000,
124 .platform_data
= &timberdale_max7301_platform_data
128 static struct spi_board_info timberdale_spi_8bit_board_info
[] = {
130 .modalias
= "mc33880",
131 .max_speed_hz
= 4000,
134 .platform_data
= &timberdale_mc33880_platform_data
138 static struct xspi_platform_data timberdale_xspi_platform_data
= {
140 /* bits per word and devices will be filled in runtime depending
145 static const struct resource timberdale_spi_resources
[] = {
149 .flags
= IORESOURCE_MEM
,
152 .start
= IRQ_TIMBERDALE_SPI
,
153 .end
= IRQ_TIMBERDALE_SPI
,
154 .flags
= IORESOURCE_IRQ
,
158 static struct ks8842_platform_data
159 timberdale_ks8842_platform_data
= {
160 .rx_dma_channel
= DMA_ETH_RX
,
161 .tx_dma_channel
= DMA_ETH_TX
164 static const struct resource timberdale_eth_resources
[] = {
168 .flags
= IORESOURCE_MEM
,
171 .start
= IRQ_TIMBERDALE_ETHSW_IF
,
172 .end
= IRQ_TIMBERDALE_ETHSW_IF
,
173 .flags
= IORESOURCE_IRQ
,
177 static struct timbgpio_platform_data
178 timberdale_gpio_platform_data
= {
180 .nr_pins
= GPIO_NR_PINS
,
184 static const struct resource timberdale_gpio_resources
[] = {
188 .flags
= IORESOURCE_MEM
,
191 .start
= IRQ_TIMBERDALE_GPIO
,
192 .end
= IRQ_TIMBERDALE_GPIO
,
193 .flags
= IORESOURCE_IRQ
,
197 static const struct resource timberdale_mlogicore_resources
[] = {
199 .start
= MLCOREOFFSET
,
201 .flags
= IORESOURCE_MEM
,
204 .start
= IRQ_TIMBERDALE_MLCORE
,
205 .end
= IRQ_TIMBERDALE_MLCORE
,
206 .flags
= IORESOURCE_IRQ
,
209 .start
= IRQ_TIMBERDALE_MLCORE_BUF
,
210 .end
= IRQ_TIMBERDALE_MLCORE_BUF
,
211 .flags
= IORESOURCE_IRQ
,
215 static const struct resource timberdale_uart_resources
[] = {
219 .flags
= IORESOURCE_MEM
,
222 .start
= IRQ_TIMBERDALE_UART
,
223 .end
= IRQ_TIMBERDALE_UART
,
224 .flags
= IORESOURCE_IRQ
,
228 static const struct resource timberdale_uartlite_resources
[] = {
230 .start
= UARTLITEOFFSET
,
232 .flags
= IORESOURCE_MEM
,
235 .start
= IRQ_TIMBERDALE_UARTLITE
,
236 .end
= IRQ_TIMBERDALE_UARTLITE
,
237 .flags
= IORESOURCE_IRQ
,
241 static struct i2c_board_info timberdale_adv7180_i2c_board_info
= {
242 /* Requires jumper JP9 to be off */
243 I2C_BOARD_INFO("adv7180", 0x42 >> 1),
244 .irq
= IRQ_TIMBERDALE_ADV7180
247 static struct timb_video_platform_data
248 timberdale_video_platform_data
= {
249 .dma_channel
= DMA_VIDEO_RX
,
252 .info
= &timberdale_adv7180_i2c_board_info
256 static const struct resource
257 timberdale_radio_resources
[] = {
261 .flags
= IORESOURCE_MEM
,
264 .start
= IRQ_TIMBERDALE_RDS
,
265 .end
= IRQ_TIMBERDALE_RDS
,
266 .flags
= IORESOURCE_IRQ
,
270 static struct i2c_board_info timberdale_tef6868_i2c_board_info
= {
271 I2C_BOARD_INFO("tef6862", 0x60)
274 static struct i2c_board_info timberdale_saa7706_i2c_board_info
= {
275 I2C_BOARD_INFO("saa7706h", 0x1C)
278 static struct timb_radio_platform_data
279 timberdale_radio_platform_data
= {
281 .tuner
= &timberdale_tef6868_i2c_board_info
,
282 .dsp
= &timberdale_saa7706_i2c_board_info
285 static const struct resource timberdale_video_resources
[] = {
287 .start
= LOGIWOFFSET
,
289 .flags
= IORESOURCE_MEM
,
292 note that the "frame buffer" is located in DMA area
293 starting at 0x1200000
297 static struct timb_dma_platform_data timb_dma_platform_data
= {
304 .descriptor_elements
= 1
310 .descriptor_elements
= 1
316 .descriptor_elements
= 1
322 .descriptor_elements
= 1
327 .bytes_per_line
= 1440,
329 .descriptor_elements
= 16
332 /* Video framedrop */
345 .descriptor_elements
= 1
351 .descriptor_elements
= 1
356 static const struct resource timberdale_dma_resources
[] = {
360 .flags
= IORESOURCE_MEM
,
363 .start
= IRQ_TIMBERDALE_DMA
,
364 .end
= IRQ_TIMBERDALE_DMA
,
365 .flags
= IORESOURCE_IRQ
,
369 static const struct mfd_cell timberdale_cells_bar0_cfg0
[] = {
372 .num_resources
= ARRAY_SIZE(timberdale_dma_resources
),
373 .resources
= timberdale_dma_resources
,
374 .platform_data
= &timb_dma_platform_data
,
375 .pdata_size
= sizeof(timb_dma_platform_data
),
379 .num_resources
= ARRAY_SIZE(timberdale_uart_resources
),
380 .resources
= timberdale_uart_resources
,
384 .num_resources
= ARRAY_SIZE(timberdale_xiic_resources
),
385 .resources
= timberdale_xiic_resources
,
386 .platform_data
= &timberdale_xiic_platform_data
,
387 .pdata_size
= sizeof(timberdale_xiic_platform_data
),
391 .num_resources
= ARRAY_SIZE(timberdale_gpio_resources
),
392 .resources
= timberdale_gpio_resources
,
393 .platform_data
= &timberdale_gpio_platform_data
,
394 .pdata_size
= sizeof(timberdale_gpio_platform_data
),
397 .name
= "timb-video",
398 .num_resources
= ARRAY_SIZE(timberdale_video_resources
),
399 .resources
= timberdale_video_resources
,
400 .platform_data
= &timberdale_video_platform_data
,
401 .pdata_size
= sizeof(timberdale_video_platform_data
),
404 .name
= "timb-radio",
405 .num_resources
= ARRAY_SIZE(timberdale_radio_resources
),
406 .resources
= timberdale_radio_resources
,
407 .platform_data
= &timberdale_radio_platform_data
,
408 .pdata_size
= sizeof(timberdale_radio_platform_data
),
411 .name
= "xilinx_spi",
412 .num_resources
= ARRAY_SIZE(timberdale_spi_resources
),
413 .resources
= timberdale_spi_resources
,
414 .platform_data
= &timberdale_xspi_platform_data
,
415 .pdata_size
= sizeof(timberdale_xspi_platform_data
),
419 .num_resources
= ARRAY_SIZE(timberdale_eth_resources
),
420 .resources
= timberdale_eth_resources
,
421 .platform_data
= &timberdale_ks8842_platform_data
,
422 .pdata_size
= sizeof(timberdale_ks8842_platform_data
),
426 static const struct mfd_cell timberdale_cells_bar0_cfg1
[] = {
429 .num_resources
= ARRAY_SIZE(timberdale_dma_resources
),
430 .resources
= timberdale_dma_resources
,
431 .platform_data
= &timb_dma_platform_data
,
432 .pdata_size
= sizeof(timb_dma_platform_data
),
436 .num_resources
= ARRAY_SIZE(timberdale_uart_resources
),
437 .resources
= timberdale_uart_resources
,
441 .num_resources
= ARRAY_SIZE(timberdale_uartlite_resources
),
442 .resources
= timberdale_uartlite_resources
,
446 .num_resources
= ARRAY_SIZE(timberdale_xiic_resources
),
447 .resources
= timberdale_xiic_resources
,
448 .platform_data
= &timberdale_xiic_platform_data
,
449 .pdata_size
= sizeof(timberdale_xiic_platform_data
),
453 .num_resources
= ARRAY_SIZE(timberdale_gpio_resources
),
454 .resources
= timberdale_gpio_resources
,
455 .platform_data
= &timberdale_gpio_platform_data
,
456 .pdata_size
= sizeof(timberdale_gpio_platform_data
),
459 .name
= "timb-mlogicore",
460 .num_resources
= ARRAY_SIZE(timberdale_mlogicore_resources
),
461 .resources
= timberdale_mlogicore_resources
,
464 .name
= "timb-video",
465 .num_resources
= ARRAY_SIZE(timberdale_video_resources
),
466 .resources
= timberdale_video_resources
,
467 .platform_data
= &timberdale_video_platform_data
,
468 .pdata_size
= sizeof(timberdale_video_platform_data
),
471 .name
= "timb-radio",
472 .num_resources
= ARRAY_SIZE(timberdale_radio_resources
),
473 .resources
= timberdale_radio_resources
,
474 .platform_data
= &timberdale_radio_platform_data
,
475 .pdata_size
= sizeof(timberdale_radio_platform_data
),
478 .name
= "xilinx_spi",
479 .num_resources
= ARRAY_SIZE(timberdale_spi_resources
),
480 .resources
= timberdale_spi_resources
,
481 .platform_data
= &timberdale_xspi_platform_data
,
482 .pdata_size
= sizeof(timberdale_xspi_platform_data
),
486 .num_resources
= ARRAY_SIZE(timberdale_eth_resources
),
487 .resources
= timberdale_eth_resources
,
488 .platform_data
= &timberdale_ks8842_platform_data
,
489 .pdata_size
= sizeof(timberdale_ks8842_platform_data
),
493 static const struct mfd_cell timberdale_cells_bar0_cfg2
[] = {
496 .num_resources
= ARRAY_SIZE(timberdale_dma_resources
),
497 .resources
= timberdale_dma_resources
,
498 .platform_data
= &timb_dma_platform_data
,
499 .pdata_size
= sizeof(timb_dma_platform_data
),
503 .num_resources
= ARRAY_SIZE(timberdale_uart_resources
),
504 .resources
= timberdale_uart_resources
,
508 .num_resources
= ARRAY_SIZE(timberdale_xiic_resources
),
509 .resources
= timberdale_xiic_resources
,
510 .platform_data
= &timberdale_xiic_platform_data
,
511 .pdata_size
= sizeof(timberdale_xiic_platform_data
),
515 .num_resources
= ARRAY_SIZE(timberdale_gpio_resources
),
516 .resources
= timberdale_gpio_resources
,
517 .platform_data
= &timberdale_gpio_platform_data
,
518 .pdata_size
= sizeof(timberdale_gpio_platform_data
),
521 .name
= "timb-video",
522 .num_resources
= ARRAY_SIZE(timberdale_video_resources
),
523 .resources
= timberdale_video_resources
,
524 .platform_data
= &timberdale_video_platform_data
,
525 .pdata_size
= sizeof(timberdale_video_platform_data
),
528 .name
= "timb-radio",
529 .num_resources
= ARRAY_SIZE(timberdale_radio_resources
),
530 .resources
= timberdale_radio_resources
,
531 .platform_data
= &timberdale_radio_platform_data
,
532 .pdata_size
= sizeof(timberdale_radio_platform_data
),
535 .name
= "xilinx_spi",
536 .num_resources
= ARRAY_SIZE(timberdale_spi_resources
),
537 .resources
= timberdale_spi_resources
,
538 .platform_data
= &timberdale_xspi_platform_data
,
539 .pdata_size
= sizeof(timberdale_xspi_platform_data
),
543 static const struct mfd_cell timberdale_cells_bar0_cfg3
[] = {
546 .num_resources
= ARRAY_SIZE(timberdale_dma_resources
),
547 .resources
= timberdale_dma_resources
,
548 .platform_data
= &timb_dma_platform_data
,
549 .pdata_size
= sizeof(timb_dma_platform_data
),
553 .num_resources
= ARRAY_SIZE(timberdale_uart_resources
),
554 .resources
= timberdale_uart_resources
,
557 .name
= "ocores-i2c",
558 .num_resources
= ARRAY_SIZE(timberdale_ocores_resources
),
559 .resources
= timberdale_ocores_resources
,
560 .platform_data
= &timberdale_ocores_platform_data
,
561 .pdata_size
= sizeof(timberdale_ocores_platform_data
),
565 .num_resources
= ARRAY_SIZE(timberdale_gpio_resources
),
566 .resources
= timberdale_gpio_resources
,
567 .platform_data
= &timberdale_gpio_platform_data
,
568 .pdata_size
= sizeof(timberdale_gpio_platform_data
),
571 .name
= "timb-video",
572 .num_resources
= ARRAY_SIZE(timberdale_video_resources
),
573 .resources
= timberdale_video_resources
,
574 .platform_data
= &timberdale_video_platform_data
,
575 .pdata_size
= sizeof(timberdale_video_platform_data
),
578 .name
= "timb-radio",
579 .num_resources
= ARRAY_SIZE(timberdale_radio_resources
),
580 .resources
= timberdale_radio_resources
,
581 .platform_data
= &timberdale_radio_platform_data
,
582 .pdata_size
= sizeof(timberdale_radio_platform_data
),
585 .name
= "xilinx_spi",
586 .num_resources
= ARRAY_SIZE(timberdale_spi_resources
),
587 .resources
= timberdale_spi_resources
,
588 .platform_data
= &timberdale_xspi_platform_data
,
589 .pdata_size
= sizeof(timberdale_xspi_platform_data
),
593 .num_resources
= ARRAY_SIZE(timberdale_eth_resources
),
594 .resources
= timberdale_eth_resources
,
595 .platform_data
= &timberdale_ks8842_platform_data
,
596 .pdata_size
= sizeof(timberdale_ks8842_platform_data
),
600 static const struct resource timberdale_sdhc_resources
[] = {
601 /* located in bar 1 and bar 2 */
603 .start
= SDHC0OFFSET
,
605 .flags
= IORESOURCE_MEM
,
608 .start
= IRQ_TIMBERDALE_SDHC
,
609 .end
= IRQ_TIMBERDALE_SDHC
,
610 .flags
= IORESOURCE_IRQ
,
614 static const struct mfd_cell timberdale_cells_bar1
[] = {
617 .num_resources
= ARRAY_SIZE(timberdale_sdhc_resources
),
618 .resources
= timberdale_sdhc_resources
,
622 static const struct mfd_cell timberdale_cells_bar2
[] = {
625 .num_resources
= ARRAY_SIZE(timberdale_sdhc_resources
),
626 .resources
= timberdale_sdhc_resources
,
630 static ssize_t
fw_ver_show(struct device
*dev
,
631 struct device_attribute
*attr
, char *buf
)
633 struct timberdale_device
*priv
= dev_get_drvdata(dev
);
635 return sprintf(buf
, "%d.%d.%d\n", priv
->fw
.major
, priv
->fw
.minor
,
639 static DEVICE_ATTR_RO(fw_ver
);
641 /*--------------------------------------------------------------------------*/
643 static int timb_probe(struct pci_dev
*dev
,
644 const struct pci_device_id
*id
)
646 struct timberdale_device
*priv
;
648 resource_size_t mapbase
;
649 struct msix_entry
*msix_entries
= NULL
;
652 priv
= kzalloc(sizeof(*priv
), GFP_KERNEL
);
656 pci_set_drvdata(dev
, priv
);
658 err
= pci_enable_device(dev
);
662 mapbase
= pci_resource_start(dev
, 0);
664 dev_err(&dev
->dev
, "No resource\n");
668 /* create a resource for the PCI master register */
669 priv
->ctl_mapbase
= mapbase
+ CHIPCTLOFFSET
;
670 if (!request_mem_region(priv
->ctl_mapbase
, CHIPCTLSIZE
, "timb-ctl")) {
671 dev_err(&dev
->dev
, "Failed to request ctl mem\n");
675 priv
->ctl_membase
= ioremap(priv
->ctl_mapbase
, CHIPCTLSIZE
);
676 if (!priv
->ctl_membase
) {
677 dev_err(&dev
->dev
, "ioremap failed for ctl mem\n");
681 /* read the HW config */
682 priv
->fw
.major
= ioread32(priv
->ctl_membase
+ TIMB_REV_MAJOR
);
683 priv
->fw
.minor
= ioread32(priv
->ctl_membase
+ TIMB_REV_MINOR
);
684 priv
->fw
.config
= ioread32(priv
->ctl_membase
+ TIMB_HW_CONFIG
);
686 if (priv
->fw
.major
> TIMB_SUPPORTED_MAJOR
) {
687 dev_err(&dev
->dev
, "The driver supports an older "
688 "version of the FPGA, please update the driver to "
689 "support %d.%d\n", priv
->fw
.major
, priv
->fw
.minor
);
692 if (priv
->fw
.major
< TIMB_SUPPORTED_MAJOR
||
693 priv
->fw
.minor
< TIMB_REQUIRED_MINOR
) {
694 dev_err(&dev
->dev
, "The FPGA image is too old (%d.%d), "
695 "please upgrade the FPGA to at least: %d.%d\n",
696 priv
->fw
.major
, priv
->fw
.minor
,
697 TIMB_SUPPORTED_MAJOR
, TIMB_REQUIRED_MINOR
);
701 msix_entries
= kcalloc(TIMBERDALE_NR_IRQS
, sizeof(*msix_entries
),
706 for (i
= 0; i
< TIMBERDALE_NR_IRQS
; i
++)
707 msix_entries
[i
].entry
= i
;
709 err
= pci_enable_msix_exact(dev
, msix_entries
, TIMBERDALE_NR_IRQS
);
712 "MSI-X init failed: %d, expected entries: %d\n",
713 err
, TIMBERDALE_NR_IRQS
);
717 err
= device_create_file(&dev
->dev
, &dev_attr_fw_ver
);
719 goto err_create_file
;
721 /* Reset all FPGA PLB peripherals */
722 iowrite32(0x1, priv
->ctl_membase
+ TIMB_SW_RST
);
724 /* update IRQ offsets in I2C board info */
725 for (i
= 0; i
< ARRAY_SIZE(timberdale_i2c_board_info
); i
++)
726 timberdale_i2c_board_info
[i
].irq
=
727 msix_entries
[timberdale_i2c_board_info
[i
].irq
].vector
;
729 /* Update the SPI configuration depending on the HW (8 or 16 bit) */
730 if (priv
->fw
.config
& TIMB_HW_CONFIG_SPI_8BIT
) {
731 timberdale_xspi_platform_data
.bits_per_word
= 8;
732 timberdale_xspi_platform_data
.devices
=
733 timberdale_spi_8bit_board_info
;
734 timberdale_xspi_platform_data
.num_devices
=
735 ARRAY_SIZE(timberdale_spi_8bit_board_info
);
737 timberdale_xspi_platform_data
.bits_per_word
= 16;
738 timberdale_xspi_platform_data
.devices
=
739 timberdale_spi_16bit_board_info
;
740 timberdale_xspi_platform_data
.num_devices
=
741 ARRAY_SIZE(timberdale_spi_16bit_board_info
);
744 ip_setup
= priv
->fw
.config
& TIMB_HW_VER_MASK
;
747 err
= mfd_add_devices(&dev
->dev
, -1,
748 timberdale_cells_bar0_cfg0
,
749 ARRAY_SIZE(timberdale_cells_bar0_cfg0
),
750 &dev
->resource
[0], msix_entries
[0].vector
, NULL
);
753 err
= mfd_add_devices(&dev
->dev
, -1,
754 timberdale_cells_bar0_cfg1
,
755 ARRAY_SIZE(timberdale_cells_bar0_cfg1
),
756 &dev
->resource
[0], msix_entries
[0].vector
, NULL
);
759 err
= mfd_add_devices(&dev
->dev
, -1,
760 timberdale_cells_bar0_cfg2
,
761 ARRAY_SIZE(timberdale_cells_bar0_cfg2
),
762 &dev
->resource
[0], msix_entries
[0].vector
, NULL
);
765 err
= mfd_add_devices(&dev
->dev
, -1,
766 timberdale_cells_bar0_cfg3
,
767 ARRAY_SIZE(timberdale_cells_bar0_cfg3
),
768 &dev
->resource
[0], msix_entries
[0].vector
, NULL
);
771 dev_err(&dev
->dev
, "Unknown IP setup: %d.%d.%d\n",
772 priv
->fw
.major
, priv
->fw
.minor
, ip_setup
);
777 dev_err(&dev
->dev
, "mfd_add_devices failed: %d\n", err
);
781 err
= mfd_add_devices(&dev
->dev
, 0,
782 timberdale_cells_bar1
, ARRAY_SIZE(timberdale_cells_bar1
),
783 &dev
->resource
[1], msix_entries
[0].vector
, NULL
);
785 dev_err(&dev
->dev
, "mfd_add_devices failed: %d\n", err
);
789 /* only version 0 and 3 have the iNand routed to SDHCI */
790 if (((priv
->fw
.config
& TIMB_HW_VER_MASK
) == TIMB_HW_VER0
) ||
791 ((priv
->fw
.config
& TIMB_HW_VER_MASK
) == TIMB_HW_VER3
)) {
792 err
= mfd_add_devices(&dev
->dev
, 1, timberdale_cells_bar2
,
793 ARRAY_SIZE(timberdale_cells_bar2
),
794 &dev
->resource
[2], msix_entries
[0].vector
, NULL
);
796 dev_err(&dev
->dev
, "mfd_add_devices failed: %d\n", err
);
804 "Found Timberdale Card. Rev: %d.%d, HW config: 0x%02x\n",
805 priv
->fw
.major
, priv
->fw
.minor
, priv
->fw
.config
);
810 mfd_remove_devices(&dev
->dev
);
812 device_remove_file(&dev
->dev
, &dev_attr_fw_ver
);
814 pci_disable_msix(dev
);
818 iounmap(priv
->ctl_membase
);
820 release_mem_region(priv
->ctl_mapbase
, CHIPCTLSIZE
);
822 pci_disable_device(dev
);
828 static void timb_remove(struct pci_dev
*dev
)
830 struct timberdale_device
*priv
= pci_get_drvdata(dev
);
832 mfd_remove_devices(&dev
->dev
);
834 device_remove_file(&dev
->dev
, &dev_attr_fw_ver
);
836 iounmap(priv
->ctl_membase
);
837 release_mem_region(priv
->ctl_mapbase
, CHIPCTLSIZE
);
839 pci_disable_msix(dev
);
840 pci_disable_device(dev
);
844 static const struct pci_device_id timberdale_pci_tbl
[] = {
845 { PCI_DEVICE(PCI_VENDOR_ID_TIMB
, PCI_DEVICE_ID_TIMB
) },
848 MODULE_DEVICE_TABLE(pci
, timberdale_pci_tbl
);
850 static struct pci_driver timberdale_pci_driver
= {
852 .id_table
= timberdale_pci_tbl
,
854 .remove
= timb_remove
,
857 module_pci_driver(timberdale_pci_driver
);
859 MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
860 MODULE_VERSION(DRV_VERSION
);
861 MODULE_DESCRIPTION("Timberdale FPGA MFD driver");
862 MODULE_LICENSE("GPL v2");