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