863 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * timberdale.c timberdale FPGA MFD driver4 * Copyright (c) 2009 Intel Corporation5 */6 7/* Supports:8 * Timberdale FPGA9 */10 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>17 18#include <linux/timb_gpio.h>19 20#include <linux/i2c.h>21#include <linux/platform_data/i2c-ocores.h>22#include <linux/platform_data/i2c-xiic.h>23 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>28 29#include <linux/platform_data/media/timb_radio.h>30#include <linux/platform_data/media/timb_video.h>31 32#include <linux/timb_dma.h>33 34#include <linux/ks8842.h>35 36#include "timberdale.h"37 38#define DRIVER_NAME "timberdale"39 40struct timberdale_device {41 resource_size_t ctl_mapbase;42 unsigned char __iomem *ctl_membase;43 struct {44 u32 major;45 u32 minor;46 u32 config;47 } fw;48};49 50/*--------------------------------------------------------------------------*/51 52static const struct property_entry timberdale_tsc2007_properties[] = {53 PROPERTY_ENTRY_U32("ti,x-plate-ohms", 100),54 { }55};56 57static const struct software_node timberdale_tsc2007_node = {58 .name = "tsc2007",59 .properties = timberdale_tsc2007_properties,60};61 62static struct i2c_board_info timberdale_i2c_board_info[] = {63 {64 I2C_BOARD_INFO("tsc2007", 0x48),65 .irq = IRQ_TIMBERDALE_TSC_INT,66 .swnode = &timberdale_tsc2007_node,67 },68};69 70static struct xiic_i2c_platform_data71timberdale_xiic_platform_data = {72 .devices = timberdale_i2c_board_info,73 .num_devices = ARRAY_SIZE(timberdale_i2c_board_info)74};75 76static struct ocores_i2c_platform_data77timberdale_ocores_platform_data = {78 .reg_shift = 2,79 .clock_khz = 62500,80 .devices = timberdale_i2c_board_info,81 .num_devices = ARRAY_SIZE(timberdale_i2c_board_info)82};83 84static const struct resource timberdale_xiic_resources[] = {85 {86 .start = XIICOFFSET,87 .end = XIICEND,88 .flags = IORESOURCE_MEM,89 },90 {91 .start = IRQ_TIMBERDALE_I2C,92 .end = IRQ_TIMBERDALE_I2C,93 .flags = IORESOURCE_IRQ,94 },95};96 97static const struct resource timberdale_ocores_resources[] = {98 {99 .start = OCORESOFFSET,100 .end = OCORESEND,101 .flags = IORESOURCE_MEM,102 },103 {104 .start = IRQ_TIMBERDALE_I2C,105 .end = IRQ_TIMBERDALE_I2C,106 .flags = IORESOURCE_IRQ,107 },108};109 110static const struct max7301_platform_data timberdale_max7301_platform_data = {111 .base = 200112};113 114static const struct mc33880_platform_data timberdale_mc33880_platform_data = {115 .base = 100116};117 118static struct spi_board_info timberdale_spi_16bit_board_info[] = {119 {120 .modalias = "max7301",121 .max_speed_hz = 26000,122 .chip_select = 2,123 .mode = SPI_MODE_0,124 .platform_data = &timberdale_max7301_platform_data125 },126};127 128static struct spi_board_info timberdale_spi_8bit_board_info[] = {129 {130 .modalias = "mc33880",131 .max_speed_hz = 4000,132 .chip_select = 1,133 .mode = SPI_MODE_1,134 .platform_data = &timberdale_mc33880_platform_data135 },136};137 138static struct xspi_platform_data timberdale_xspi_platform_data = {139 .num_chipselect = 3,140 /* bits per word and devices will be filled in runtime depending141 * on the HW config142 */143};144 145static const struct resource timberdale_spi_resources[] = {146 {147 .start = SPIOFFSET,148 .end = SPIEND,149 .flags = IORESOURCE_MEM,150 },151 {152 .start = IRQ_TIMBERDALE_SPI,153 .end = IRQ_TIMBERDALE_SPI,154 .flags = IORESOURCE_IRQ,155 },156};157 158static struct ks8842_platform_data159 timberdale_ks8842_platform_data = {160 .rx_dma_channel = DMA_ETH_RX,161 .tx_dma_channel = DMA_ETH_TX162};163 164static const struct resource timberdale_eth_resources[] = {165 {166 .start = ETHOFFSET,167 .end = ETHEND,168 .flags = IORESOURCE_MEM,169 },170 {171 .start = IRQ_TIMBERDALE_ETHSW_IF,172 .end = IRQ_TIMBERDALE_ETHSW_IF,173 .flags = IORESOURCE_IRQ,174 },175};176 177static struct timbgpio_platform_data178 timberdale_gpio_platform_data = {179 .gpio_base = 0,180 .nr_pins = GPIO_NR_PINS,181 .irq_base = 200,182};183 184static const struct resource timberdale_gpio_resources[] = {185 {186 .start = GPIOOFFSET,187 .end = GPIOEND,188 .flags = IORESOURCE_MEM,189 },190 {191 .start = IRQ_TIMBERDALE_GPIO,192 .end = IRQ_TIMBERDALE_GPIO,193 .flags = IORESOURCE_IRQ,194 },195};196 197static const struct resource timberdale_mlogicore_resources[] = {198 {199 .start = MLCOREOFFSET,200 .end = MLCOREEND,201 .flags = IORESOURCE_MEM,202 },203 {204 .start = IRQ_TIMBERDALE_MLCORE,205 .end = IRQ_TIMBERDALE_MLCORE,206 .flags = IORESOURCE_IRQ,207 },208 {209 .start = IRQ_TIMBERDALE_MLCORE_BUF,210 .end = IRQ_TIMBERDALE_MLCORE_BUF,211 .flags = IORESOURCE_IRQ,212 },213};214 215static const struct resource timberdale_uart_resources[] = {216 {217 .start = UARTOFFSET,218 .end = UARTEND,219 .flags = IORESOURCE_MEM,220 },221 {222 .start = IRQ_TIMBERDALE_UART,223 .end = IRQ_TIMBERDALE_UART,224 .flags = IORESOURCE_IRQ,225 },226};227 228static const struct resource timberdale_uartlite_resources[] = {229 {230 .start = UARTLITEOFFSET,231 .end = UARTLITEEND,232 .flags = IORESOURCE_MEM,233 },234 {235 .start = IRQ_TIMBERDALE_UARTLITE,236 .end = IRQ_TIMBERDALE_UARTLITE,237 .flags = IORESOURCE_IRQ,238 },239};240 241static 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_ADV7180245};246 247static struct timb_video_platform_data248 timberdale_video_platform_data = {249 .dma_channel = DMA_VIDEO_RX,250 .i2c_adapter = 0,251 .encoder = {252 .info = &timberdale_adv7180_i2c_board_info253 }254};255 256static const struct resource257timberdale_radio_resources[] = {258 {259 .start = RDSOFFSET,260 .end = RDSEND,261 .flags = IORESOURCE_MEM,262 },263 {264 .start = IRQ_TIMBERDALE_RDS,265 .end = IRQ_TIMBERDALE_RDS,266 .flags = IORESOURCE_IRQ,267 },268};269 270static struct i2c_board_info timberdale_tef6868_i2c_board_info = {271 I2C_BOARD_INFO("tef6862", 0x60)272};273 274static struct i2c_board_info timberdale_saa7706_i2c_board_info = {275 I2C_BOARD_INFO("saa7706h", 0x1C)276};277 278static struct timb_radio_platform_data279 timberdale_radio_platform_data = {280 .i2c_adapter = 0,281 .tuner = &timberdale_tef6868_i2c_board_info,282 .dsp = &timberdale_saa7706_i2c_board_info283};284 285static const struct resource timberdale_video_resources[] = {286 {287 .start = LOGIWOFFSET,288 .end = LOGIWEND,289 .flags = IORESOURCE_MEM,290 },291 /*292 note that the "frame buffer" is located in DMA area293 starting at 0x1200000294 */295};296 297static struct timb_dma_platform_data timb_dma_platform_data = {298 .nr_channels = 10,299 .channels = {300 {301 /* UART RX */302 .rx = true,303 .descriptors = 2,304 .descriptor_elements = 1305 },306 {307 /* UART TX */308 .rx = false,309 .descriptors = 2,310 .descriptor_elements = 1311 },312 {313 /* MLB RX */314 .rx = true,315 .descriptors = 2,316 .descriptor_elements = 1317 },318 {319 /* MLB TX */320 .rx = false,321 .descriptors = 2,322 .descriptor_elements = 1323 },324 {325 /* Video RX */326 .rx = true,327 .bytes_per_line = 1440,328 .descriptors = 2,329 .descriptor_elements = 16330 },331 {332 /* Video framedrop */333 },334 {335 /* SDHCI RX */336 .rx = true,337 },338 {339 /* SDHCI TX */340 },341 {342 /* ETH RX */343 .rx = true,344 .descriptors = 2,345 .descriptor_elements = 1346 },347 {348 /* ETH TX */349 .rx = false,350 .descriptors = 2,351 .descriptor_elements = 1352 },353 }354};355 356static const struct resource timberdale_dma_resources[] = {357 {358 .start = DMAOFFSET,359 .end = DMAEND,360 .flags = IORESOURCE_MEM,361 },362 {363 .start = IRQ_TIMBERDALE_DMA,364 .end = IRQ_TIMBERDALE_DMA,365 .flags = IORESOURCE_IRQ,366 },367};368 369static const struct mfd_cell timberdale_cells_bar0_cfg0[] = {370 {371 .name = "timb-dma",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),376 },377 {378 .name = "timb-uart",379 .num_resources = ARRAY_SIZE(timberdale_uart_resources),380 .resources = timberdale_uart_resources,381 },382 {383 .name = "xiic-i2c",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),388 },389 {390 .name = "timb-gpio",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),395 },396 {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),402 },403 {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),409 },410 {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),416 },417 {418 .name = "ks8842",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),423 },424};425 426static const struct mfd_cell timberdale_cells_bar0_cfg1[] = {427 {428 .name = "timb-dma",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),433 },434 {435 .name = "timb-uart",436 .num_resources = ARRAY_SIZE(timberdale_uart_resources),437 .resources = timberdale_uart_resources,438 },439 {440 .name = "uartlite",441 .num_resources = ARRAY_SIZE(timberdale_uartlite_resources),442 .resources = timberdale_uartlite_resources,443 },444 {445 .name = "xiic-i2c",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),450 },451 {452 .name = "timb-gpio",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),457 },458 {459 .name = "timb-mlogicore",460 .num_resources = ARRAY_SIZE(timberdale_mlogicore_resources),461 .resources = timberdale_mlogicore_resources,462 },463 {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),469 },470 {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),476 },477 {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),483 },484 {485 .name = "ks8842",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),490 },491};492 493static const struct mfd_cell timberdale_cells_bar0_cfg2[] = {494 {495 .name = "timb-dma",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),500 },501 {502 .name = "timb-uart",503 .num_resources = ARRAY_SIZE(timberdale_uart_resources),504 .resources = timberdale_uart_resources,505 },506 {507 .name = "xiic-i2c",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),512 },513 {514 .name = "timb-gpio",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),519 },520 {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),526 },527 {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),533 },534 {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),540 },541};542 543static const struct mfd_cell timberdale_cells_bar0_cfg3[] = {544 {545 .name = "timb-dma",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),550 },551 {552 .name = "timb-uart",553 .num_resources = ARRAY_SIZE(timberdale_uart_resources),554 .resources = timberdale_uart_resources,555 },556 {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),562 },563 {564 .name = "timb-gpio",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),569 },570 {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),576 },577 {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),583 },584 {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),590 },591 {592 .name = "ks8842",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),597 },598};599 600static const struct resource timberdale_sdhc_resources[] = {601 /* located in bar 1 and bar 2 */602 {603 .start = SDHC0OFFSET,604 .end = SDHC0END,605 .flags = IORESOURCE_MEM,606 },607 {608 .start = IRQ_TIMBERDALE_SDHC,609 .end = IRQ_TIMBERDALE_SDHC,610 .flags = IORESOURCE_IRQ,611 },612};613 614static const struct mfd_cell timberdale_cells_bar1[] = {615 {616 .name = "sdhci",617 .num_resources = ARRAY_SIZE(timberdale_sdhc_resources),618 .resources = timberdale_sdhc_resources,619 },620};621 622static const struct mfd_cell timberdale_cells_bar2[] = {623 {624 .name = "sdhci",625 .num_resources = ARRAY_SIZE(timberdale_sdhc_resources),626 .resources = timberdale_sdhc_resources,627 },628};629 630static ssize_t fw_ver_show(struct device *dev,631 struct device_attribute *attr, char *buf)632{633 struct timberdale_device *priv = dev_get_drvdata(dev);634 635 return sprintf(buf, "%d.%d.%d\n", priv->fw.major, priv->fw.minor,636 priv->fw.config);637}638 639static DEVICE_ATTR_RO(fw_ver);640 641/*--------------------------------------------------------------------------*/642 643static int timb_probe(struct pci_dev *dev,644 const struct pci_device_id *id)645{646 struct timberdale_device *priv;647 int err, i;648 resource_size_t mapbase;649 struct msix_entry *msix_entries = NULL;650 u8 ip_setup;651 652 priv = kzalloc(sizeof(*priv), GFP_KERNEL);653 if (!priv)654 return -ENOMEM;655 656 pci_set_drvdata(dev, priv);657 658 err = pci_enable_device(dev);659 if (err)660 goto err_enable;661 662 mapbase = pci_resource_start(dev, 0);663 if (!mapbase) {664 dev_err(&dev->dev, "No resource\n");665 goto err_start;666 }667 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");672 goto err_start;673 }674 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");678 goto err_ioremap;679 }680 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);685 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);690 goto err_config;691 }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);698 goto err_config;699 }700 701 msix_entries = kcalloc(TIMBERDALE_NR_IRQS, sizeof(*msix_entries),702 GFP_KERNEL);703 if (!msix_entries)704 goto err_config;705 706 for (i = 0; i < TIMBERDALE_NR_IRQS; i++)707 msix_entries[i].entry = i;708 709 err = pci_enable_msix_exact(dev, msix_entries, TIMBERDALE_NR_IRQS);710 if (err) {711 dev_err(&dev->dev,712 "MSI-X init failed: %d, expected entries: %d\n",713 err, TIMBERDALE_NR_IRQS);714 goto err_msix;715 }716 717 err = device_create_file(&dev->dev, &dev_attr_fw_ver);718 if (err)719 goto err_create_file;720 721 /* Reset all FPGA PLB peripherals */722 iowrite32(0x1, priv->ctl_membase + TIMB_SW_RST);723 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;728 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);736 } else {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);742 }743 744 ip_setup = priv->fw.config & TIMB_HW_VER_MASK;745 switch (ip_setup) {746 case TIMB_HW_VER0: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);751 break;752 case TIMB_HW_VER1: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);757 break;758 case TIMB_HW_VER2: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);763 break;764 case TIMB_HW_VER3: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);769 break;770 default:771 dev_err(&dev->dev, "Unknown IP setup: %d.%d.%d\n",772 priv->fw.major, priv->fw.minor, ip_setup);773 goto err_mfd;774 }775 776 if (err) {777 dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);778 goto err_mfd;779 }780 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);784 if (err) {785 dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);786 goto err_mfd2;787 }788 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);795 if (err) {796 dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);797 goto err_mfd2;798 }799 }800 801 kfree(msix_entries);802 803 dev_info(&dev->dev,804 "Found Timberdale Card. Rev: %d.%d, HW config: 0x%02x\n",805 priv->fw.major, priv->fw.minor, priv->fw.config);806 807 return 0;808 809err_mfd2:810 mfd_remove_devices(&dev->dev);811err_mfd:812 device_remove_file(&dev->dev, &dev_attr_fw_ver);813err_create_file:814 pci_disable_msix(dev);815err_msix:816 kfree(msix_entries);817err_config:818 iounmap(priv->ctl_membase);819err_ioremap:820 release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE);821err_start:822 pci_disable_device(dev);823err_enable:824 kfree(priv);825 return -ENODEV;826}827 828static void timb_remove(struct pci_dev *dev)829{830 struct timberdale_device *priv = pci_get_drvdata(dev);831 832 mfd_remove_devices(&dev->dev);833 834 device_remove_file(&dev->dev, &dev_attr_fw_ver);835 836 iounmap(priv->ctl_membase);837 release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE);838 839 pci_disable_msix(dev);840 pci_disable_device(dev);841 kfree(priv);842}843 844static const struct pci_device_id timberdale_pci_tbl[] = {845 { PCI_DEVICE(PCI_VENDOR_ID_TIMB, PCI_DEVICE_ID_TIMB) },846 { 0 }847};848MODULE_DEVICE_TABLE(pci, timberdale_pci_tbl);849 850static struct pci_driver timberdale_pci_driver = {851 .name = DRIVER_NAME,852 .id_table = timberdale_pci_tbl,853 .probe = timb_probe,854 .remove = timb_remove,855};856 857module_pci_driver(timberdale_pci_driver);858 859MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");860MODULE_VERSION(DRV_VERSION);861MODULE_DESCRIPTION("Timberdale FPGA MFD driver");862MODULE_LICENSE("GPL v2");863