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