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