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