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