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