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