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