cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

timberdale.c (21766B)


      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 fw_ver_show(struct device *dev,
    627			   struct device_attribute *attr, 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_RO(fw_ver);
    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");