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