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