Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * timberdale.c timberdale FPGA MFD driver
0004  * Copyright (c) 2009 Intel Corporation
0005  */
0006 
0007 /* Supports:
0008  * Timberdale FPGA
0009  */
0010 
0011 #include <linux/kernel.h>
0012 #include <linux/module.h>
0013 #include <linux/pci.h>
0014 #include <linux/msi.h>
0015 #include <linux/mfd/core.h>
0016 #include <linux/slab.h>
0017 
0018 #include <linux/timb_gpio.h>
0019 
0020 #include <linux/i2c.h>
0021 #include <linux/platform_data/i2c-ocores.h>
0022 #include <linux/platform_data/i2c-xiic.h>
0023 
0024 #include <linux/spi/spi.h>
0025 #include <linux/spi/xilinx_spi.h>
0026 #include <linux/spi/max7301.h>
0027 #include <linux/spi/mc33880.h>
0028 
0029 #include <linux/platform_data/tsc2007.h>
0030 #include <linux/platform_data/media/timb_radio.h>
0031 #include <linux/platform_data/media/timb_video.h>
0032 
0033 #include <linux/timb_dma.h>
0034 
0035 #include <linux/ks8842.h>
0036 
0037 #include "timberdale.h"
0038 
0039 #define DRIVER_NAME "timberdale"
0040 
0041 struct timberdale_device {
0042     resource_size_t     ctl_mapbase;
0043     unsigned char __iomem   *ctl_membase;
0044     struct {
0045         u32 major;
0046         u32 minor;
0047         u32 config;
0048     } fw;
0049 };
0050 
0051 /*--------------------------------------------------------------------------*/
0052 
0053 static struct tsc2007_platform_data timberdale_tsc2007_platform_data = {
0054     .model = 2003,
0055     .x_plate_ohms = 100
0056 };
0057 
0058 static struct i2c_board_info timberdale_i2c_board_info[] = {
0059     {
0060         I2C_BOARD_INFO("tsc2007", 0x48),
0061         .platform_data = &timberdale_tsc2007_platform_data,
0062         .irq = IRQ_TIMBERDALE_TSC_INT
0063     },
0064 };
0065 
0066 static struct xiic_i2c_platform_data
0067 timberdale_xiic_platform_data = {
0068     .devices = timberdale_i2c_board_info,
0069     .num_devices = ARRAY_SIZE(timberdale_i2c_board_info)
0070 };
0071 
0072 static struct ocores_i2c_platform_data
0073 timberdale_ocores_platform_data = {
0074     .reg_shift = 2,
0075     .clock_khz = 62500,
0076     .devices = timberdale_i2c_board_info,
0077     .num_devices = ARRAY_SIZE(timberdale_i2c_board_info)
0078 };
0079 
0080 static const struct resource timberdale_xiic_resources[] = {
0081     {
0082         .start  = XIICOFFSET,
0083         .end    = XIICEND,
0084         .flags  = IORESOURCE_MEM,
0085     },
0086     {
0087         .start  = IRQ_TIMBERDALE_I2C,
0088         .end    = IRQ_TIMBERDALE_I2C,
0089         .flags  = IORESOURCE_IRQ,
0090     },
0091 };
0092 
0093 static const struct resource timberdale_ocores_resources[] = {
0094     {
0095         .start  = OCORESOFFSET,
0096         .end    = OCORESEND,
0097         .flags  = IORESOURCE_MEM,
0098     },
0099     {
0100         .start  = IRQ_TIMBERDALE_I2C,
0101         .end    = IRQ_TIMBERDALE_I2C,
0102         .flags  = IORESOURCE_IRQ,
0103     },
0104 };
0105 
0106 static const struct max7301_platform_data timberdale_max7301_platform_data = {
0107     .base = 200
0108 };
0109 
0110 static const struct mc33880_platform_data timberdale_mc33880_platform_data = {
0111     .base = 100
0112 };
0113 
0114 static struct spi_board_info timberdale_spi_16bit_board_info[] = {
0115     {
0116         .modalias = "max7301",
0117         .max_speed_hz = 26000,
0118         .chip_select = 2,
0119         .mode = SPI_MODE_0,
0120         .platform_data = &timberdale_max7301_platform_data
0121     },
0122 };
0123 
0124 static struct spi_board_info timberdale_spi_8bit_board_info[] = {
0125     {
0126         .modalias = "mc33880",
0127         .max_speed_hz = 4000,
0128         .chip_select = 1,
0129         .mode = SPI_MODE_1,
0130         .platform_data = &timberdale_mc33880_platform_data
0131     },
0132 };
0133 
0134 static struct xspi_platform_data timberdale_xspi_platform_data = {
0135     .num_chipselect = 3,
0136     /* bits per word and devices will be filled in runtime depending
0137      * on the HW config
0138      */
0139 };
0140 
0141 static const struct resource timberdale_spi_resources[] = {
0142     {
0143         .start  = SPIOFFSET,
0144         .end    = SPIEND,
0145         .flags  = IORESOURCE_MEM,
0146     },
0147     {
0148         .start  = IRQ_TIMBERDALE_SPI,
0149         .end    = IRQ_TIMBERDALE_SPI,
0150         .flags  = IORESOURCE_IRQ,
0151     },
0152 };
0153 
0154 static struct ks8842_platform_data
0155     timberdale_ks8842_platform_data = {
0156     .rx_dma_channel = DMA_ETH_RX,
0157     .tx_dma_channel = DMA_ETH_TX
0158 };
0159 
0160 static const struct resource timberdale_eth_resources[] = {
0161     {
0162         .start  = ETHOFFSET,
0163         .end    = ETHEND,
0164         .flags  = IORESOURCE_MEM,
0165     },
0166     {
0167         .start  = IRQ_TIMBERDALE_ETHSW_IF,
0168         .end    = IRQ_TIMBERDALE_ETHSW_IF,
0169         .flags  = IORESOURCE_IRQ,
0170     },
0171 };
0172 
0173 static struct timbgpio_platform_data
0174     timberdale_gpio_platform_data = {
0175     .gpio_base = 0,
0176     .nr_pins = GPIO_NR_PINS,
0177     .irq_base = 200,
0178 };
0179 
0180 static const struct resource timberdale_gpio_resources[] = {
0181     {
0182         .start  = GPIOOFFSET,
0183         .end    = GPIOEND,
0184         .flags  = IORESOURCE_MEM,
0185     },
0186     {
0187         .start  = IRQ_TIMBERDALE_GPIO,
0188         .end    = IRQ_TIMBERDALE_GPIO,
0189         .flags  = IORESOURCE_IRQ,
0190     },
0191 };
0192 
0193 static const struct resource timberdale_mlogicore_resources[] = {
0194     {
0195         .start  = MLCOREOFFSET,
0196         .end    = MLCOREEND,
0197         .flags  = IORESOURCE_MEM,
0198     },
0199     {
0200         .start  = IRQ_TIMBERDALE_MLCORE,
0201         .end    = IRQ_TIMBERDALE_MLCORE,
0202         .flags  = IORESOURCE_IRQ,
0203     },
0204     {
0205         .start  = IRQ_TIMBERDALE_MLCORE_BUF,
0206         .end    = IRQ_TIMBERDALE_MLCORE_BUF,
0207         .flags  = IORESOURCE_IRQ,
0208     },
0209 };
0210 
0211 static const struct resource timberdale_uart_resources[] = {
0212     {
0213         .start  = UARTOFFSET,
0214         .end    = UARTEND,
0215         .flags  = IORESOURCE_MEM,
0216     },
0217     {
0218         .start  = IRQ_TIMBERDALE_UART,
0219         .end    = IRQ_TIMBERDALE_UART,
0220         .flags  = IORESOURCE_IRQ,
0221     },
0222 };
0223 
0224 static const struct resource timberdale_uartlite_resources[] = {
0225     {
0226         .start  = UARTLITEOFFSET,
0227         .end    = UARTLITEEND,
0228         .flags  = IORESOURCE_MEM,
0229     },
0230     {
0231         .start  = IRQ_TIMBERDALE_UARTLITE,
0232         .end    = IRQ_TIMBERDALE_UARTLITE,
0233         .flags  = IORESOURCE_IRQ,
0234     },
0235 };
0236 
0237 static struct i2c_board_info timberdale_adv7180_i2c_board_info = {
0238     /* Requires jumper JP9 to be off */
0239     I2C_BOARD_INFO("adv7180", 0x42 >> 1),
0240     .irq = IRQ_TIMBERDALE_ADV7180
0241 };
0242 
0243 static struct timb_video_platform_data
0244     timberdale_video_platform_data = {
0245     .dma_channel = DMA_VIDEO_RX,
0246     .i2c_adapter = 0,
0247     .encoder = {
0248         .info = &timberdale_adv7180_i2c_board_info
0249     }
0250 };
0251 
0252 static const struct resource
0253 timberdale_radio_resources[] = {
0254     {
0255         .start  = RDSOFFSET,
0256         .end    = RDSEND,
0257         .flags  = IORESOURCE_MEM,
0258     },
0259     {
0260         .start  = IRQ_TIMBERDALE_RDS,
0261         .end    = IRQ_TIMBERDALE_RDS,
0262         .flags  = IORESOURCE_IRQ,
0263     },
0264 };
0265 
0266 static struct i2c_board_info timberdale_tef6868_i2c_board_info = {
0267     I2C_BOARD_INFO("tef6862", 0x60)
0268 };
0269 
0270 static struct i2c_board_info timberdale_saa7706_i2c_board_info = {
0271     I2C_BOARD_INFO("saa7706h", 0x1C)
0272 };
0273 
0274 static struct timb_radio_platform_data
0275     timberdale_radio_platform_data = {
0276     .i2c_adapter = 0,
0277     .tuner = &timberdale_tef6868_i2c_board_info,
0278     .dsp = &timberdale_saa7706_i2c_board_info
0279 };
0280 
0281 static const struct resource timberdale_video_resources[] = {
0282     {
0283         .start  = LOGIWOFFSET,
0284         .end    = LOGIWEND,
0285         .flags  = IORESOURCE_MEM,
0286     },
0287     /*
0288     note that the "frame buffer" is located in DMA area
0289     starting at 0x1200000
0290     */
0291 };
0292 
0293 static struct timb_dma_platform_data timb_dma_platform_data = {
0294     .nr_channels = 10,
0295     .channels = {
0296         {
0297             /* UART RX */
0298             .rx = true,
0299             .descriptors = 2,
0300             .descriptor_elements = 1
0301         },
0302         {
0303             /* UART TX */
0304             .rx = false,
0305             .descriptors = 2,
0306             .descriptor_elements = 1
0307         },
0308         {
0309             /* MLB RX */
0310             .rx = true,
0311             .descriptors = 2,
0312             .descriptor_elements = 1
0313         },
0314         {
0315             /* MLB TX */
0316             .rx = false,
0317             .descriptors = 2,
0318             .descriptor_elements = 1
0319         },
0320         {
0321             /* Video RX */
0322             .rx = true,
0323             .bytes_per_line = 1440,
0324             .descriptors = 2,
0325             .descriptor_elements = 16
0326         },
0327         {
0328             /* Video framedrop */
0329         },
0330         {
0331             /* SDHCI RX */
0332             .rx = true,
0333         },
0334         {
0335             /* SDHCI TX */
0336         },
0337         {
0338             /* ETH RX */
0339             .rx = true,
0340             .descriptors = 2,
0341             .descriptor_elements = 1
0342         },
0343         {
0344             /* ETH TX */
0345             .rx = false,
0346             .descriptors = 2,
0347             .descriptor_elements = 1
0348         },
0349     }
0350 };
0351 
0352 static const struct resource timberdale_dma_resources[] = {
0353     {
0354         .start  = DMAOFFSET,
0355         .end    = DMAEND,
0356         .flags  = IORESOURCE_MEM,
0357     },
0358     {
0359         .start  = IRQ_TIMBERDALE_DMA,
0360         .end    = IRQ_TIMBERDALE_DMA,
0361         .flags  = IORESOURCE_IRQ,
0362     },
0363 };
0364 
0365 static const struct mfd_cell timberdale_cells_bar0_cfg0[] = {
0366     {
0367         .name = "timb-dma",
0368         .num_resources = ARRAY_SIZE(timberdale_dma_resources),
0369         .resources = timberdale_dma_resources,
0370         .platform_data = &timb_dma_platform_data,
0371         .pdata_size = sizeof(timb_dma_platform_data),
0372     },
0373     {
0374         .name = "timb-uart",
0375         .num_resources = ARRAY_SIZE(timberdale_uart_resources),
0376         .resources = timberdale_uart_resources,
0377     },
0378     {
0379         .name = "xiic-i2c",
0380         .num_resources = ARRAY_SIZE(timberdale_xiic_resources),
0381         .resources = timberdale_xiic_resources,
0382         .platform_data = &timberdale_xiic_platform_data,
0383         .pdata_size = sizeof(timberdale_xiic_platform_data),
0384     },
0385     {
0386         .name = "timb-gpio",
0387         .num_resources = ARRAY_SIZE(timberdale_gpio_resources),
0388         .resources = timberdale_gpio_resources,
0389         .platform_data = &timberdale_gpio_platform_data,
0390         .pdata_size = sizeof(timberdale_gpio_platform_data),
0391     },
0392     {
0393         .name = "timb-video",
0394         .num_resources = ARRAY_SIZE(timberdale_video_resources),
0395         .resources = timberdale_video_resources,
0396         .platform_data = &timberdale_video_platform_data,
0397         .pdata_size = sizeof(timberdale_video_platform_data),
0398     },
0399     {
0400         .name = "timb-radio",
0401         .num_resources = ARRAY_SIZE(timberdale_radio_resources),
0402         .resources = timberdale_radio_resources,
0403         .platform_data = &timberdale_radio_platform_data,
0404         .pdata_size = sizeof(timberdale_radio_platform_data),
0405     },
0406     {
0407         .name = "xilinx_spi",
0408         .num_resources = ARRAY_SIZE(timberdale_spi_resources),
0409         .resources = timberdale_spi_resources,
0410         .platform_data = &timberdale_xspi_platform_data,
0411         .pdata_size = sizeof(timberdale_xspi_platform_data),
0412     },
0413     {
0414         .name = "ks8842",
0415         .num_resources = ARRAY_SIZE(timberdale_eth_resources),
0416         .resources = timberdale_eth_resources,
0417         .platform_data = &timberdale_ks8842_platform_data,
0418         .pdata_size = sizeof(timberdale_ks8842_platform_data),
0419     },
0420 };
0421 
0422 static const struct mfd_cell timberdale_cells_bar0_cfg1[] = {
0423     {
0424         .name = "timb-dma",
0425         .num_resources = ARRAY_SIZE(timberdale_dma_resources),
0426         .resources = timberdale_dma_resources,
0427         .platform_data = &timb_dma_platform_data,
0428         .pdata_size = sizeof(timb_dma_platform_data),
0429     },
0430     {
0431         .name = "timb-uart",
0432         .num_resources = ARRAY_SIZE(timberdale_uart_resources),
0433         .resources = timberdale_uart_resources,
0434     },
0435     {
0436         .name = "uartlite",
0437         .num_resources = ARRAY_SIZE(timberdale_uartlite_resources),
0438         .resources = timberdale_uartlite_resources,
0439     },
0440     {
0441         .name = "xiic-i2c",
0442         .num_resources = ARRAY_SIZE(timberdale_xiic_resources),
0443         .resources = timberdale_xiic_resources,
0444         .platform_data = &timberdale_xiic_platform_data,
0445         .pdata_size = sizeof(timberdale_xiic_platform_data),
0446     },
0447     {
0448         .name = "timb-gpio",
0449         .num_resources = ARRAY_SIZE(timberdale_gpio_resources),
0450         .resources = timberdale_gpio_resources,
0451         .platform_data = &timberdale_gpio_platform_data,
0452         .pdata_size = sizeof(timberdale_gpio_platform_data),
0453     },
0454     {
0455         .name = "timb-mlogicore",
0456         .num_resources = ARRAY_SIZE(timberdale_mlogicore_resources),
0457         .resources = timberdale_mlogicore_resources,
0458     },
0459     {
0460         .name = "timb-video",
0461         .num_resources = ARRAY_SIZE(timberdale_video_resources),
0462         .resources = timberdale_video_resources,
0463         .platform_data = &timberdale_video_platform_data,
0464         .pdata_size = sizeof(timberdale_video_platform_data),
0465     },
0466     {
0467         .name = "timb-radio",
0468         .num_resources = ARRAY_SIZE(timberdale_radio_resources),
0469         .resources = timberdale_radio_resources,
0470         .platform_data = &timberdale_radio_platform_data,
0471         .pdata_size = sizeof(timberdale_radio_platform_data),
0472     },
0473     {
0474         .name = "xilinx_spi",
0475         .num_resources = ARRAY_SIZE(timberdale_spi_resources),
0476         .resources = timberdale_spi_resources,
0477         .platform_data = &timberdale_xspi_platform_data,
0478         .pdata_size = sizeof(timberdale_xspi_platform_data),
0479     },
0480     {
0481         .name = "ks8842",
0482         .num_resources = ARRAY_SIZE(timberdale_eth_resources),
0483         .resources = timberdale_eth_resources,
0484         .platform_data = &timberdale_ks8842_platform_data,
0485         .pdata_size = sizeof(timberdale_ks8842_platform_data),
0486     },
0487 };
0488 
0489 static const struct mfd_cell timberdale_cells_bar0_cfg2[] = {
0490     {
0491         .name = "timb-dma",
0492         .num_resources = ARRAY_SIZE(timberdale_dma_resources),
0493         .resources = timberdale_dma_resources,
0494         .platform_data = &timb_dma_platform_data,
0495         .pdata_size = sizeof(timb_dma_platform_data),
0496     },
0497     {
0498         .name = "timb-uart",
0499         .num_resources = ARRAY_SIZE(timberdale_uart_resources),
0500         .resources = timberdale_uart_resources,
0501     },
0502     {
0503         .name = "xiic-i2c",
0504         .num_resources = ARRAY_SIZE(timberdale_xiic_resources),
0505         .resources = timberdale_xiic_resources,
0506         .platform_data = &timberdale_xiic_platform_data,
0507         .pdata_size = sizeof(timberdale_xiic_platform_data),
0508     },
0509     {
0510         .name = "timb-gpio",
0511         .num_resources = ARRAY_SIZE(timberdale_gpio_resources),
0512         .resources = timberdale_gpio_resources,
0513         .platform_data = &timberdale_gpio_platform_data,
0514         .pdata_size = sizeof(timberdale_gpio_platform_data),
0515     },
0516     {
0517         .name = "timb-video",
0518         .num_resources = ARRAY_SIZE(timberdale_video_resources),
0519         .resources = timberdale_video_resources,
0520         .platform_data = &timberdale_video_platform_data,
0521         .pdata_size = sizeof(timberdale_video_platform_data),
0522     },
0523     {
0524         .name = "timb-radio",
0525         .num_resources = ARRAY_SIZE(timberdale_radio_resources),
0526         .resources = timberdale_radio_resources,
0527         .platform_data = &timberdale_radio_platform_data,
0528         .pdata_size = sizeof(timberdale_radio_platform_data),
0529     },
0530     {
0531         .name = "xilinx_spi",
0532         .num_resources = ARRAY_SIZE(timberdale_spi_resources),
0533         .resources = timberdale_spi_resources,
0534         .platform_data = &timberdale_xspi_platform_data,
0535         .pdata_size = sizeof(timberdale_xspi_platform_data),
0536     },
0537 };
0538 
0539 static const struct mfd_cell timberdale_cells_bar0_cfg3[] = {
0540     {
0541         .name = "timb-dma",
0542         .num_resources = ARRAY_SIZE(timberdale_dma_resources),
0543         .resources = timberdale_dma_resources,
0544         .platform_data = &timb_dma_platform_data,
0545         .pdata_size = sizeof(timb_dma_platform_data),
0546     },
0547     {
0548         .name = "timb-uart",
0549         .num_resources = ARRAY_SIZE(timberdale_uart_resources),
0550         .resources = timberdale_uart_resources,
0551     },
0552     {
0553         .name = "ocores-i2c",
0554         .num_resources = ARRAY_SIZE(timberdale_ocores_resources),
0555         .resources = timberdale_ocores_resources,
0556         .platform_data = &timberdale_ocores_platform_data,
0557         .pdata_size = sizeof(timberdale_ocores_platform_data),
0558     },
0559     {
0560         .name = "timb-gpio",
0561         .num_resources = ARRAY_SIZE(timberdale_gpio_resources),
0562         .resources = timberdale_gpio_resources,
0563         .platform_data = &timberdale_gpio_platform_data,
0564         .pdata_size = sizeof(timberdale_gpio_platform_data),
0565     },
0566     {
0567         .name = "timb-video",
0568         .num_resources = ARRAY_SIZE(timberdale_video_resources),
0569         .resources = timberdale_video_resources,
0570         .platform_data = &timberdale_video_platform_data,
0571         .pdata_size = sizeof(timberdale_video_platform_data),
0572     },
0573     {
0574         .name = "timb-radio",
0575         .num_resources = ARRAY_SIZE(timberdale_radio_resources),
0576         .resources = timberdale_radio_resources,
0577         .platform_data = &timberdale_radio_platform_data,
0578         .pdata_size = sizeof(timberdale_radio_platform_data),
0579     },
0580     {
0581         .name = "xilinx_spi",
0582         .num_resources = ARRAY_SIZE(timberdale_spi_resources),
0583         .resources = timberdale_spi_resources,
0584         .platform_data = &timberdale_xspi_platform_data,
0585         .pdata_size = sizeof(timberdale_xspi_platform_data),
0586     },
0587     {
0588         .name = "ks8842",
0589         .num_resources = ARRAY_SIZE(timberdale_eth_resources),
0590         .resources = timberdale_eth_resources,
0591         .platform_data = &timberdale_ks8842_platform_data,
0592         .pdata_size = sizeof(timberdale_ks8842_platform_data),
0593     },
0594 };
0595 
0596 static const struct resource timberdale_sdhc_resources[] = {
0597     /* located in bar 1 and bar 2 */
0598     {
0599         .start  = SDHC0OFFSET,
0600         .end    = SDHC0END,
0601         .flags  = IORESOURCE_MEM,
0602     },
0603     {
0604         .start  = IRQ_TIMBERDALE_SDHC,
0605         .end    = IRQ_TIMBERDALE_SDHC,
0606         .flags  = IORESOURCE_IRQ,
0607     },
0608 };
0609 
0610 static const struct mfd_cell timberdale_cells_bar1[] = {
0611     {
0612         .name = "sdhci",
0613         .num_resources = ARRAY_SIZE(timberdale_sdhc_resources),
0614         .resources = timberdale_sdhc_resources,
0615     },
0616 };
0617 
0618 static const struct mfd_cell timberdale_cells_bar2[] = {
0619     {
0620         .name = "sdhci",
0621         .num_resources = ARRAY_SIZE(timberdale_sdhc_resources),
0622         .resources = timberdale_sdhc_resources,
0623     },
0624 };
0625 
0626 static ssize_t fw_ver_show(struct device *dev,
0627                struct device_attribute *attr, char *buf)
0628 {
0629     struct timberdale_device *priv = dev_get_drvdata(dev);
0630 
0631     return sprintf(buf, "%d.%d.%d\n", priv->fw.major, priv->fw.minor,
0632         priv->fw.config);
0633 }
0634 
0635 static DEVICE_ATTR_RO(fw_ver);
0636 
0637 /*--------------------------------------------------------------------------*/
0638 
0639 static int timb_probe(struct pci_dev *dev,
0640     const struct pci_device_id *id)
0641 {
0642     struct timberdale_device *priv;
0643     int err, i;
0644     resource_size_t mapbase;
0645     struct msix_entry *msix_entries = NULL;
0646     u8 ip_setup;
0647 
0648     priv = kzalloc(sizeof(*priv), GFP_KERNEL);
0649     if (!priv)
0650         return -ENOMEM;
0651 
0652     pci_set_drvdata(dev, priv);
0653 
0654     err = pci_enable_device(dev);
0655     if (err)
0656         goto err_enable;
0657 
0658     mapbase = pci_resource_start(dev, 0);
0659     if (!mapbase) {
0660         dev_err(&dev->dev, "No resource\n");
0661         goto err_start;
0662     }
0663 
0664     /* create a resource for the PCI master register */
0665     priv->ctl_mapbase = mapbase + CHIPCTLOFFSET;
0666     if (!request_mem_region(priv->ctl_mapbase, CHIPCTLSIZE, "timb-ctl")) {
0667         dev_err(&dev->dev, "Failed to request ctl mem\n");
0668         goto err_start;
0669     }
0670 
0671     priv->ctl_membase = ioremap(priv->ctl_mapbase, CHIPCTLSIZE);
0672     if (!priv->ctl_membase) {
0673         dev_err(&dev->dev, "ioremap failed for ctl mem\n");
0674         goto err_ioremap;
0675     }
0676 
0677     /* read the HW config */
0678     priv->fw.major = ioread32(priv->ctl_membase + TIMB_REV_MAJOR);
0679     priv->fw.minor = ioread32(priv->ctl_membase + TIMB_REV_MINOR);
0680     priv->fw.config = ioread32(priv->ctl_membase + TIMB_HW_CONFIG);
0681 
0682     if (priv->fw.major > TIMB_SUPPORTED_MAJOR) {
0683         dev_err(&dev->dev, "The driver supports an older "
0684             "version of the FPGA, please update the driver to "
0685             "support %d.%d\n", priv->fw.major, priv->fw.minor);
0686         goto err_config;
0687     }
0688     if (priv->fw.major < TIMB_SUPPORTED_MAJOR ||
0689         priv->fw.minor < TIMB_REQUIRED_MINOR) {
0690         dev_err(&dev->dev, "The FPGA image is too old (%d.%d), "
0691             "please upgrade the FPGA to at least: %d.%d\n",
0692             priv->fw.major, priv->fw.minor,
0693             TIMB_SUPPORTED_MAJOR, TIMB_REQUIRED_MINOR);
0694         goto err_config;
0695     }
0696 
0697     msix_entries = kcalloc(TIMBERDALE_NR_IRQS, sizeof(*msix_entries),
0698                    GFP_KERNEL);
0699     if (!msix_entries)
0700         goto err_config;
0701 
0702     for (i = 0; i < TIMBERDALE_NR_IRQS; i++)
0703         msix_entries[i].entry = i;
0704 
0705     err = pci_enable_msix_exact(dev, msix_entries, TIMBERDALE_NR_IRQS);
0706     if (err) {
0707         dev_err(&dev->dev,
0708             "MSI-X init failed: %d, expected entries: %d\n",
0709             err, TIMBERDALE_NR_IRQS);
0710         goto err_msix;
0711     }
0712 
0713     err = device_create_file(&dev->dev, &dev_attr_fw_ver);
0714     if (err)
0715         goto err_create_file;
0716 
0717     /* Reset all FPGA PLB peripherals */
0718     iowrite32(0x1, priv->ctl_membase + TIMB_SW_RST);
0719 
0720     /* update IRQ offsets in I2C board info */
0721     for (i = 0; i < ARRAY_SIZE(timberdale_i2c_board_info); i++)
0722         timberdale_i2c_board_info[i].irq =
0723             msix_entries[timberdale_i2c_board_info[i].irq].vector;
0724 
0725     /* Update the SPI configuration depending on the HW (8 or 16 bit) */
0726     if (priv->fw.config & TIMB_HW_CONFIG_SPI_8BIT) {
0727         timberdale_xspi_platform_data.bits_per_word = 8;
0728         timberdale_xspi_platform_data.devices =
0729             timberdale_spi_8bit_board_info;
0730         timberdale_xspi_platform_data.num_devices =
0731             ARRAY_SIZE(timberdale_spi_8bit_board_info);
0732     } else {
0733         timberdale_xspi_platform_data.bits_per_word = 16;
0734         timberdale_xspi_platform_data.devices =
0735             timberdale_spi_16bit_board_info;
0736         timberdale_xspi_platform_data.num_devices =
0737             ARRAY_SIZE(timberdale_spi_16bit_board_info);
0738     }
0739 
0740     ip_setup = priv->fw.config & TIMB_HW_VER_MASK;
0741     switch (ip_setup) {
0742     case TIMB_HW_VER0:
0743         err = mfd_add_devices(&dev->dev, -1,
0744             timberdale_cells_bar0_cfg0,
0745             ARRAY_SIZE(timberdale_cells_bar0_cfg0),
0746             &dev->resource[0], msix_entries[0].vector, NULL);
0747         break;
0748     case TIMB_HW_VER1:
0749         err = mfd_add_devices(&dev->dev, -1,
0750             timberdale_cells_bar0_cfg1,
0751             ARRAY_SIZE(timberdale_cells_bar0_cfg1),
0752             &dev->resource[0], msix_entries[0].vector, NULL);
0753         break;
0754     case TIMB_HW_VER2:
0755         err = mfd_add_devices(&dev->dev, -1,
0756             timberdale_cells_bar0_cfg2,
0757             ARRAY_SIZE(timberdale_cells_bar0_cfg2),
0758             &dev->resource[0], msix_entries[0].vector, NULL);
0759         break;
0760     case TIMB_HW_VER3:
0761         err = mfd_add_devices(&dev->dev, -1,
0762             timberdale_cells_bar0_cfg3,
0763             ARRAY_SIZE(timberdale_cells_bar0_cfg3),
0764             &dev->resource[0], msix_entries[0].vector, NULL);
0765         break;
0766     default:
0767         dev_err(&dev->dev, "Unknown IP setup: %d.%d.%d\n",
0768             priv->fw.major, priv->fw.minor, ip_setup);
0769         err = -ENODEV;
0770         goto err_mfd;
0771     }
0772 
0773     if (err) {
0774         dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
0775         goto err_mfd;
0776     }
0777 
0778     err = mfd_add_devices(&dev->dev, 0,
0779         timberdale_cells_bar1, ARRAY_SIZE(timberdale_cells_bar1),
0780         &dev->resource[1], msix_entries[0].vector, NULL);
0781     if (err) {
0782         dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
0783         goto err_mfd2;
0784     }
0785 
0786     /* only version 0 and 3 have the iNand routed to SDHCI */
0787     if (((priv->fw.config & TIMB_HW_VER_MASK) == TIMB_HW_VER0) ||
0788         ((priv->fw.config & TIMB_HW_VER_MASK) == TIMB_HW_VER3)) {
0789         err = mfd_add_devices(&dev->dev, 1, timberdale_cells_bar2,
0790             ARRAY_SIZE(timberdale_cells_bar2),
0791             &dev->resource[2], msix_entries[0].vector, NULL);
0792         if (err) {
0793             dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
0794             goto err_mfd2;
0795         }
0796     }
0797 
0798     kfree(msix_entries);
0799 
0800     dev_info(&dev->dev,
0801         "Found Timberdale Card. Rev: %d.%d, HW config: 0x%02x\n",
0802         priv->fw.major, priv->fw.minor, priv->fw.config);
0803 
0804     return 0;
0805 
0806 err_mfd2:
0807     mfd_remove_devices(&dev->dev);
0808 err_mfd:
0809     device_remove_file(&dev->dev, &dev_attr_fw_ver);
0810 err_create_file:
0811     pci_disable_msix(dev);
0812 err_msix:
0813     kfree(msix_entries);
0814 err_config:
0815     iounmap(priv->ctl_membase);
0816 err_ioremap:
0817     release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE);
0818 err_start:
0819     pci_disable_device(dev);
0820 err_enable:
0821     kfree(priv);
0822     return -ENODEV;
0823 }
0824 
0825 static void timb_remove(struct pci_dev *dev)
0826 {
0827     struct timberdale_device *priv = pci_get_drvdata(dev);
0828 
0829     mfd_remove_devices(&dev->dev);
0830 
0831     device_remove_file(&dev->dev, &dev_attr_fw_ver);
0832 
0833     iounmap(priv->ctl_membase);
0834     release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE);
0835 
0836     pci_disable_msix(dev);
0837     pci_disable_device(dev);
0838     kfree(priv);
0839 }
0840 
0841 static const struct pci_device_id timberdale_pci_tbl[] = {
0842     { PCI_DEVICE(PCI_VENDOR_ID_TIMB, PCI_DEVICE_ID_TIMB) },
0843     { 0 }
0844 };
0845 MODULE_DEVICE_TABLE(pci, timberdale_pci_tbl);
0846 
0847 static struct pci_driver timberdale_pci_driver = {
0848     .name = DRIVER_NAME,
0849     .id_table = timberdale_pci_tbl,
0850     .probe = timb_probe,
0851     .remove = timb_remove,
0852 };
0853 
0854 module_pci_driver(timberdale_pci_driver);
0855 
0856 MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
0857 MODULE_VERSION(DRV_VERSION);
0858 MODULE_LICENSE("GPL v2");