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