linux/drivers/mfd/timberdale.c
<<
>>
Prefs
   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
  53struct 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
  65static struct tsc2007_platform_data timberdale_tsc2007_platform_data = {
  66        .model = 2003,
  67        .x_plate_ohms = 100
  68};
  69
  70static 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
  78static __devinitdata struct xiic_i2c_platform_data
  79timberdale_xiic_platform_data = {
  80        .devices = timberdale_i2c_board_info,
  81        .num_devices = ARRAY_SIZE(timberdale_i2c_board_info)
  82};
  83
  84static __devinitdata struct ocores_i2c_platform_data
  85timberdale_ocores_platform_data = {
  86        .reg_shift = 2,
  87        .clock_khz = 62500,
  88        .devices = timberdale_i2c_board_info,
  89        .num_devices = ARRAY_SIZE(timberdale_i2c_board_info)
  90};
  91
  92static 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
 105static 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
 118const struct max7301_platform_data timberdale_max7301_platform_data = {
 119        .base = 200
 120};
 121
 122const struct mc33880_platform_data timberdale_mc33880_platform_data = {
 123        .base = 100
 124};
 125
 126static 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
 136static 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
 146static __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
 154static 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
 167static __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
 173static 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
 186static __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
 193static 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
 206static 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
 224static 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
 237static 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
 250static __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
 256static __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
 265static const __devinitconst struct resource
 266timberdale_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
 279static __devinitdata struct i2c_board_info timberdale_tef6868_i2c_board_info = {
 280        I2C_BOARD_INFO("tef6862", 0x60)
 281};
 282
 283static __devinitdata struct i2c_board_info timberdale_saa7706_i2c_board_info = {
 284        I2C_BOARD_INFO("saa7706h", 0x1C)
 285};
 286
 287static __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
 294static 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
 306static __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
 365static 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
 378static __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
 435static __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
 502static __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
 552static __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
 609static 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
 623static __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
 631static __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
 639static 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
 649static DEVICE_ATTR(fw_ver, S_IRUGO, show_fw_ver, NULL);
 650
 651/*--------------------------------------------------------------------------*/
 652
 653static 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, NULL);
 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, NULL);
 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, NULL);
 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, NULL);
 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, NULL);
 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, NULL);
 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
 821err_mfd2:
 822        mfd_remove_devices(&dev->dev);
 823err_mfd:
 824        device_remove_file(&dev->dev, &dev_attr_fw_ver);
 825err_create_file:
 826        pci_disable_msix(dev);
 827err_msix:
 828        kfree(msix_entries);
 829err_config:
 830        iounmap(priv->ctl_membase);
 831err_ioremap:
 832        release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE);
 833err_request:
 834        pci_set_drvdata(dev, NULL);
 835err_start:
 836        pci_disable_device(dev);
 837err_enable:
 838        kfree(priv);
 839        pci_set_drvdata(dev, NULL);
 840        return -ENODEV;
 841}
 842
 843static 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
 860static DEFINE_PCI_DEVICE_TABLE(timberdale_pci_tbl) = {
 861        { PCI_DEVICE(PCI_VENDOR_ID_TIMB, PCI_DEVICE_ID_TIMB) },
 862        { 0 }
 863};
 864MODULE_DEVICE_TABLE(pci, timberdale_pci_tbl);
 865
 866static 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
 873static 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
 891static 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
 899module_init(timberdale_init);
 900module_exit(timberdale_exit);
 901
 902MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
 903MODULE_VERSION(DRV_VERSION);
 904MODULE_LICENSE("GPL v2");
 905
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.