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 = { 291 .info = &timberdale_tef6868_i2c_board_info 292 }, 293 .dsp = { 294 .info = &timberdale_saa7706_i2c_board_info 295 } 296 }; 297 298 static const __devinitconst struct resource timberdale_video_resources[] = { 299 { 300 .start = LOGIWOFFSET, 301 .end = LOGIWEND, 302 .flags = IORESOURCE_MEM, 303 }, 304 /* 305 note that the "frame buffer" is located in DMA area 306 starting at 0x1200000 307 */ 308 }; 309 310 static __devinitdata struct timb_dma_platform_data timb_dma_platform_data = { 311 .nr_channels = 10, 312 .channels = { 313 { 314 /* UART RX */ 315 .rx = true, 316 .descriptors = 2, 317 .descriptor_elements = 1 318 }, 319 { 320 /* UART TX */ 321 .rx = false, 322 .descriptors = 2, 323 .descriptor_elements = 1 324 }, 325 { 326 /* MLB RX */ 327 .rx = true, 328 .descriptors = 2, 329 .descriptor_elements = 1 330 }, 331 { 332 /* MLB TX */ 333 .rx = false, 334 .descriptors = 2, 335 .descriptor_elements = 1 336 }, 337 { 338 /* Video RX */ 339 .rx = true, 340 .bytes_per_line = 1440, 341 .descriptors = 2, 342 .descriptor_elements = 16 343 }, 344 { 345 /* Video framedrop */ 346 }, 347 { 348 /* SDHCI RX */ 349 .rx = true, 350 }, 351 { 352 /* SDHCI TX */ 353 }, 354 { 355 /* ETH RX */ 356 .rx = true, 357 .descriptors = 2, 358 .descriptor_elements = 1 359 }, 360 { 361 /* ETH TX */ 362 .rx = false, 363 .descriptors = 2, 364 .descriptor_elements = 1 365 }, 366 } 367 }; 368 369 static const __devinitconst struct resource timberdale_dma_resources[] = { 370 { 371 .start = DMAOFFSET, 372 .end = DMAEND, 373 .flags = IORESOURCE_MEM, 374 }, 375 { 376 .start = IRQ_TIMBERDALE_DMA, 377 .end = IRQ_TIMBERDALE_DMA, 378 .flags = IORESOURCE_IRQ, 379 }, 380 }; 381 382 static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = { 383 { 384 .name = "timb-dma", 385 .num_resources = ARRAY_SIZE(timberdale_dma_resources), 386 .resources = timberdale_dma_resources, 387 .platform_data = &timb_dma_platform_data, 388 .data_size = sizeof(timb_dma_platform_data), 389 }, 390 { 391 .name = "timb-uart", 392 .num_resources = ARRAY_SIZE(timberdale_uart_resources), 393 .resources = timberdale_uart_resources, 394 }, 395 { 396 .name = "xiic-i2c", 397 .num_resources = ARRAY_SIZE(timberdale_xiic_resources), 398 .resources = timberdale_xiic_resources, 399 .platform_data = &timberdale_xiic_platform_data, 400 .data_size = sizeof(timberdale_xiic_platform_data), 401 }, 402 { 403 .name = "timb-gpio", 404 .num_resources = ARRAY_SIZE(timberdale_gpio_resources), 405 .resources = timberdale_gpio_resources, 406 .platform_data = &timberdale_gpio_platform_data, 407 .data_size = sizeof(timberdale_gpio_platform_data), 408 }, 409 { 410 .name = "timb-video", 411 .num_resources = ARRAY_SIZE(timberdale_video_resources), 412 .resources = timberdale_video_resources, 413 .platform_data = &timberdale_video_platform_data, 414 .data_size = sizeof(timberdale_video_platform_data), 415 }, 416 { 417 .name = "timb-radio", 418 .num_resources = ARRAY_SIZE(timberdale_radio_resources), 419 .resources = timberdale_radio_resources, 420 .platform_data = &timberdale_radio_platform_data, 421 .data_size = sizeof(timberdale_radio_platform_data), 422 }, 423 { 424 .name = "xilinx_spi", 425 .num_resources = ARRAY_SIZE(timberdale_spi_resources), 426 .resources = timberdale_spi_resources, 427 .platform_data = &timberdale_xspi_platform_data, 428 .data_size = sizeof(timberdale_xspi_platform_data), 429 }, 430 { 431 .name = "ks8842", 432 .num_resources = ARRAY_SIZE(timberdale_eth_resources), 433 .resources = timberdale_eth_resources, 434 .platform_data = &timberdale_ks8842_platform_data, 435 .data_size = sizeof(timberdale_ks8842_platform_data) 436 }, 437 }; 438 439 static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = { 440 { 441 .name = "timb-dma", 442 .num_resources = ARRAY_SIZE(timberdale_dma_resources), 443 .resources = timberdale_dma_resources, 444 .platform_data = &timb_dma_platform_data, 445 .data_size = sizeof(timb_dma_platform_data), 446 }, 447 { 448 .name = "timb-uart", 449 .num_resources = ARRAY_SIZE(timberdale_uart_resources), 450 .resources = timberdale_uart_resources, 451 }, 452 { 453 .name = "uartlite", 454 .num_resources = ARRAY_SIZE(timberdale_uartlite_resources), 455 .resources = timberdale_uartlite_resources, 456 }, 457 { 458 .name = "xiic-i2c", 459 .num_resources = ARRAY_SIZE(timberdale_xiic_resources), 460 .resources = timberdale_xiic_resources, 461 .platform_data = &timberdale_xiic_platform_data, 462 .data_size = sizeof(timberdale_xiic_platform_data), 463 }, 464 { 465 .name = "timb-gpio", 466 .num_resources = ARRAY_SIZE(timberdale_gpio_resources), 467 .resources = timberdale_gpio_resources, 468 .platform_data = &timberdale_gpio_platform_data, 469 .data_size = sizeof(timberdale_gpio_platform_data), 470 }, 471 { 472 .name = "timb-mlogicore", 473 .num_resources = ARRAY_SIZE(timberdale_mlogicore_resources), 474 .resources = timberdale_mlogicore_resources, 475 }, 476 { 477 .name = "timb-video", 478 .num_resources = ARRAY_SIZE(timberdale_video_resources), 479 .resources = timberdale_video_resources, 480 .platform_data = &timberdale_video_platform_data, 481 .data_size = sizeof(timberdale_video_platform_data), 482 }, 483 { 484 .name = "timb-radio", 485 .num_resources = ARRAY_SIZE(timberdale_radio_resources), 486 .resources = timberdale_radio_resources, 487 .platform_data = &timberdale_radio_platform_data, 488 .data_size = sizeof(timberdale_radio_platform_data), 489 }, 490 { 491 .name = "xilinx_spi", 492 .num_resources = ARRAY_SIZE(timberdale_spi_resources), 493 .resources = timberdale_spi_resources, 494 .platform_data = &timberdale_xspi_platform_data, 495 .data_size = sizeof(timberdale_xspi_platform_data), 496 }, 497 { 498 .name = "ks8842", 499 .num_resources = ARRAY_SIZE(timberdale_eth_resources), 500 .resources = timberdale_eth_resources, 501 .platform_data = &timberdale_ks8842_platform_data, 502 .data_size = sizeof(timberdale_ks8842_platform_data) 503 }, 504 }; 505 506 static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = { 507 { 508 .name = "timb-dma", 509 .num_resources = ARRAY_SIZE(timberdale_dma_resources), 510 .resources = timberdale_dma_resources, 511 .platform_data = &timb_dma_platform_data, 512 .data_size = sizeof(timb_dma_platform_data), 513 }, 514 { 515 .name = "timb-uart", 516 .num_resources = ARRAY_SIZE(timberdale_uart_resources), 517 .resources = timberdale_uart_resources, 518 }, 519 { 520 .name = "xiic-i2c", 521 .num_resources = ARRAY_SIZE(timberdale_xiic_resources), 522 .resources = timberdale_xiic_resources, 523 .platform_data = &timberdale_xiic_platform_data, 524 .data_size = sizeof(timberdale_xiic_platform_data), 525 }, 526 { 527 .name = "timb-gpio", 528 .num_resources = ARRAY_SIZE(timberdale_gpio_resources), 529 .resources = timberdale_gpio_resources, 530 .platform_data = &timberdale_gpio_platform_data, 531 .data_size = sizeof(timberdale_gpio_platform_data), 532 }, 533 { 534 .name = "timb-video", 535 .num_resources = ARRAY_SIZE(timberdale_video_resources), 536 .resources = timberdale_video_resources, 537 .platform_data = &timberdale_video_platform_data, 538 .data_size = sizeof(timberdale_video_platform_data), 539 }, 540 { 541 .name = "timb-radio", 542 .num_resources = ARRAY_SIZE(timberdale_radio_resources), 543 .resources = timberdale_radio_resources, 544 .platform_data = &timberdale_radio_platform_data, 545 .data_size = sizeof(timberdale_radio_platform_data), 546 }, 547 { 548 .name = "xilinx_spi", 549 .num_resources = ARRAY_SIZE(timberdale_spi_resources), 550 .resources = timberdale_spi_resources, 551 .platform_data = &timberdale_xspi_platform_data, 552 .data_size = sizeof(timberdale_xspi_platform_data), 553 }, 554 }; 555 556 static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = { 557 { 558 .name = "timb-dma", 559 .num_resources = ARRAY_SIZE(timberdale_dma_resources), 560 .resources = timberdale_dma_resources, 561 .platform_data = &timb_dma_platform_data, 562 .data_size = sizeof(timb_dma_platform_data), 563 }, 564 { 565 .name = "timb-uart", 566 .num_resources = ARRAY_SIZE(timberdale_uart_resources), 567 .resources = timberdale_uart_resources, 568 }, 569 { 570 .name = "ocores-i2c", 571 .num_resources = ARRAY_SIZE(timberdale_ocores_resources), 572 .resources = timberdale_ocores_resources, 573 .platform_data = &timberdale_ocores_platform_data, 574 .data_size = sizeof(timberdale_ocores_platform_data), 575 }, 576 { 577 .name = "timb-gpio", 578 .num_resources = ARRAY_SIZE(timberdale_gpio_resources), 579 .resources = timberdale_gpio_resources, 580 .platform_data = &timberdale_gpio_platform_data, 581 .data_size = sizeof(timberdale_gpio_platform_data), 582 }, 583 { 584 .name = "timb-video", 585 .num_resources = ARRAY_SIZE(timberdale_video_resources), 586 .resources = timberdale_video_resources, 587 .platform_data = &timberdale_video_platform_data, 588 .data_size = sizeof(timberdale_video_platform_data), 589 }, 590 { 591 .name = "timb-radio", 592 .num_resources = ARRAY_SIZE(timberdale_radio_resources), 593 .resources = timberdale_radio_resources, 594 .platform_data = &timberdale_radio_platform_data, 595 .data_size = sizeof(timberdale_radio_platform_data), 596 }, 597 { 598 .name = "xilinx_spi", 599 .num_resources = ARRAY_SIZE(timberdale_spi_resources), 600 .resources = timberdale_spi_resources, 601 .platform_data = &timberdale_xspi_platform_data, 602 .data_size = sizeof(timberdale_xspi_platform_data), 603 }, 604 { 605 .name = "ks8842", 606 .num_resources = ARRAY_SIZE(timberdale_eth_resources), 607 .resources = timberdale_eth_resources, 608 .platform_data = &timberdale_ks8842_platform_data, 609 .data_size = sizeof(timberdale_ks8842_platform_data) 610 }, 611 }; 612 613 static const __devinitconst struct resource timberdale_sdhc_resources[] = { 614 /* located in bar 1 and bar 2 */ 615 { 616 .start = SDHC0OFFSET, 617 .end = SDHC0END, 618 .flags = IORESOURCE_MEM, 619 }, 620 { 621 .start = IRQ_TIMBERDALE_SDHC, 622 .end = IRQ_TIMBERDALE_SDHC, 623 .flags = IORESOURCE_IRQ, 624 }, 625 }; 626 627 static __devinitdata struct mfd_cell timberdale_cells_bar1[] = { 628 { 629 .name = "sdhci", 630 .num_resources = ARRAY_SIZE(timberdale_sdhc_resources), 631 .resources = timberdale_sdhc_resources, 632 }, 633 }; 634 635 static __devinitdata struct mfd_cell timberdale_cells_bar2[] = { 636 { 637 .name = "sdhci", 638 .num_resources = ARRAY_SIZE(timberdale_sdhc_resources), 639 .resources = timberdale_sdhc_resources, 640 }, 641 }; 642 643 static ssize_t show_fw_ver(struct device *dev, struct device_attribute *attr, 644 char *buf) 645 { 646 struct pci_dev *pdev = to_pci_dev(dev); 647 struct timberdale_device *priv = pci_get_drvdata(pdev); 648 649 return sprintf(buf, "%d.%d.%d\n", priv->fw.major, priv->fw.minor, 650 priv->fw.config); 651 } 652 653 static DEVICE_ATTR(fw_ver, S_IRUGO, show_fw_ver, NULL); 654 655 /*--------------------------------------------------------------------------*/ 656 657 static int __devinit timb_probe(struct pci_dev *dev, 658 const struct pci_device_id *id) 659 { 660 struct timberdale_device *priv; 661 int err, i; 662 resource_size_t mapbase; 663 struct msix_entry *msix_entries = NULL; 664 u8 ip_setup; 665 666 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 667 if (!priv) 668 return -ENOMEM; 669 670 pci_set_drvdata(dev, priv); 671 672 err = pci_enable_device(dev); 673 if (err) 674 goto err_enable; 675 676 mapbase = pci_resource_start(dev, 0); 677 if (!mapbase) { 678 dev_err(&dev->dev, "No resource\n"); 679 goto err_start; 680 } 681 682 /* create a resource for the PCI master register */ 683 priv->ctl_mapbase = mapbase + CHIPCTLOFFSET; 684 if (!request_mem_region(priv->ctl_mapbase, CHIPCTLSIZE, "timb-ctl")) { 685 dev_err(&dev->dev, "Failed to request ctl mem\n"); 686 goto err_request; 687 } 688 689 priv->ctl_membase = ioremap(priv->ctl_mapbase, CHIPCTLSIZE); 690 if (!priv->ctl_membase) { 691 dev_err(&dev->dev, "ioremap failed for ctl mem\n"); 692 goto err_ioremap; 693 } 694 695 /* read the HW config */ 696 priv->fw.major = ioread32(priv->ctl_membase + TIMB_REV_MAJOR); 697 priv->fw.minor = ioread32(priv->ctl_membase + TIMB_REV_MINOR); 698 priv->fw.config = ioread32(priv->ctl_membase + TIMB_HW_CONFIG); 699 700 if (priv->fw.major > TIMB_SUPPORTED_MAJOR) { 701 dev_err(&dev->dev, "The driver supports an older " 702 "version of the FPGA, please update the driver to " 703 "support %d.%d\n", priv->fw.major, priv->fw.minor); 704 goto err_ioremap; 705 } 706 if (priv->fw.major < TIMB_SUPPORTED_MAJOR || 707 priv->fw.minor < TIMB_REQUIRED_MINOR) { 708 dev_err(&dev->dev, "The FPGA image is too old (%d.%d), " 709 "please upgrade the FPGA to at least: %d.%d\n", 710 priv->fw.major, priv->fw.minor, 711 TIMB_SUPPORTED_MAJOR, TIMB_REQUIRED_MINOR); 712 goto err_ioremap; 713 } 714 715 msix_entries = kzalloc(TIMBERDALE_NR_IRQS * sizeof(*msix_entries), 716 GFP_KERNEL); 717 if (!msix_entries) 718 goto err_ioremap; 719 720 for (i = 0; i < TIMBERDALE_NR_IRQS; i++) 721 msix_entries[i].entry = i; 722 723 err = pci_enable_msix(dev, msix_entries, TIMBERDALE_NR_IRQS); 724 if (err) { 725 dev_err(&dev->dev, 726 "MSI-X init failed: %d, expected entries: %d\n", 727 err, TIMBERDALE_NR_IRQS); 728 goto err_msix; 729 } 730 731 err = device_create_file(&dev->dev, &dev_attr_fw_ver); 732 if (err) 733 goto err_create_file; 734 735 /* Reset all FPGA PLB peripherals */ 736 iowrite32(0x1, priv->ctl_membase + TIMB_SW_RST); 737 738 /* update IRQ offsets in I2C board info */ 739 for (i = 0; i < ARRAY_SIZE(timberdale_i2c_board_info); i++) 740 timberdale_i2c_board_info[i].irq = 741 msix_entries[timberdale_i2c_board_info[i].irq].vector; 742 743 /* Update the SPI configuration depending on the HW (8 or 16 bit) */ 744 if (priv->fw.config & TIMB_HW_CONFIG_SPI_8BIT) { 745 timberdale_xspi_platform_data.bits_per_word = 8; 746 timberdale_xspi_platform_data.devices = 747 timberdale_spi_8bit_board_info; 748 timberdale_xspi_platform_data.num_devices = 749 ARRAY_SIZE(timberdale_spi_8bit_board_info); 750 } else { 751 timberdale_xspi_platform_data.bits_per_word = 16; 752 timberdale_xspi_platform_data.devices = 753 timberdale_spi_16bit_board_info; 754 timberdale_xspi_platform_data.num_devices = 755 ARRAY_SIZE(timberdale_spi_16bit_board_info); 756 } 757 758 ip_setup = priv->fw.config & TIMB_HW_VER_MASK; 759 switch (ip_setup) { 760 case TIMB_HW_VER0: 761 err = mfd_add_devices(&dev->dev, -1, 762 timberdale_cells_bar0_cfg0, 763 ARRAY_SIZE(timberdale_cells_bar0_cfg0), 764 &dev->resource[0], msix_entries[0].vector); 765 break; 766 case TIMB_HW_VER1: 767 err = mfd_add_devices(&dev->dev, -1, 768 timberdale_cells_bar0_cfg1, 769 ARRAY_SIZE(timberdale_cells_bar0_cfg1), 770 &dev->resource[0], msix_entries[0].vector); 771 break; 772 case TIMB_HW_VER2: 773 err = mfd_add_devices(&dev->dev, -1, 774 timberdale_cells_bar0_cfg2, 775 ARRAY_SIZE(timberdale_cells_bar0_cfg2), 776 &dev->resource[0], msix_entries[0].vector); 777 break; 778 case TIMB_HW_VER3: 779 err = mfd_add_devices(&dev->dev, -1, 780 timberdale_cells_bar0_cfg3, 781 ARRAY_SIZE(timberdale_cells_bar0_cfg3), 782 &dev->resource[0], msix_entries[0].vector); 783 break; 784 default: 785 dev_err(&dev->dev, "Uknown IP setup: %d.%d.%d\n", 786 priv->fw.major, priv->fw.minor, ip_setup); 787 err = -ENODEV; 788 goto err_mfd; 789 break; 790 } 791 792 if (err) { 793 dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err); 794 goto err_mfd; 795 } 796 797 err = mfd_add_devices(&dev->dev, 0, 798 timberdale_cells_bar1, ARRAY_SIZE(timberdale_cells_bar1), 799 &dev->resource[1], msix_entries[0].vector); 800 if (err) { 801 dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err); 802 goto err_mfd2; 803 } 804 805 /* only version 0 and 3 have the iNand routed to SDHCI */ 806 if (((priv->fw.config & TIMB_HW_VER_MASK) == TIMB_HW_VER0) || 807 ((priv->fw.config & TIMB_HW_VER_MASK) == TIMB_HW_VER3)) { 808 err = mfd_add_devices(&dev->dev, 1, timberdale_cells_bar2, 809 ARRAY_SIZE(timberdale_cells_bar2), 810 &dev->resource[2], msix_entries[0].vector); 811 if (err) { 812 dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err); 813 goto err_mfd2; 814 } 815 } 816 817 kfree(msix_entries); 818 819 dev_info(&dev->dev, 820 "Found Timberdale Card. Rev: %d.%d, HW config: 0x%02x\n", 821 priv->fw.major, priv->fw.minor, priv->fw.config); 822 823 return 0; 824 825 err_mfd2: 826 mfd_remove_devices(&dev->dev); 827 err_mfd: 828 device_remove_file(&dev->dev, &dev_attr_fw_ver); 829 err_create_file: 830 pci_disable_msix(dev); 831 err_msix: 832 iounmap(priv->ctl_membase); 833 err_ioremap: 834 release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE); 835 err_request: 836 pci_set_drvdata(dev, NULL); 837 err_start: 838 pci_disable_device(dev); 839 err_enable: 840 kfree(msix_entries); 841 kfree(priv); 842 pci_set_drvdata(dev, NULL); 843 return -ENODEV; 844 } 845 846 static void __devexit timb_remove(struct pci_dev *dev) 847 { 848 struct timberdale_device *priv = pci_get_drvdata(dev); 849 850 mfd_remove_devices(&dev->dev); 851 852 device_remove_file(&dev->dev, &dev_attr_fw_ver); 853 854 iounmap(priv->ctl_membase); 855 release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE); 856 857 pci_disable_msix(dev); 858 pci_disable_device(dev); 859 pci_set_drvdata(dev, NULL); 860 kfree(priv); 861 } 862 863 static struct pci_device_id timberdale_pci_tbl[] = { 864 { PCI_DEVICE(PCI_VENDOR_ID_TIMB, PCI_DEVICE_ID_TIMB) }, 865 { 0 } 866 }; 867 MODULE_DEVICE_TABLE(pci, timberdale_pci_tbl); 868 869 static struct pci_driver timberdale_pci_driver = { 870 .name = DRIVER_NAME, 871 .id_table = timberdale_pci_tbl, 872 .probe = timb_probe, 873 .remove = __devexit_p(timb_remove), 874 }; 875 876 static int __init timberdale_init(void) 877 { 878 int err; 879 880 err = pci_register_driver(&timberdale_pci_driver); 881 if (err < 0) { 882 printk(KERN_ERR 883 "Failed to register PCI driver for %s device.\n", 884 timberdale_pci_driver.name); 885 return -ENODEV; 886 } 887 888 printk(KERN_INFO "Driver for %s has been successfully registered.\n", 889 timberdale_pci_driver.name); 890 891 return 0; 892 } 893 894 static void __exit timberdale_exit(void) 895 { 896 pci_unregister_driver(&timberdale_pci_driver); 897 898 printk(KERN_INFO "Driver for %s has been successfully unregistered.\n", 899 timberdale_pci_driver.name); 900 } 901 902 module_init(timberdale_init); 903 module_exit(timberdale_exit); 904 905 MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>"); 906 MODULE_VERSION(DRV_VERSION); 907 MODULE_LICENSE("GPL v2"); 908