diff options
Diffstat (limited to 'arch/x86/platform/intel-mid/sfi.c')
-rw-r--r-- | arch/x86/platform/intel-mid/sfi.c | 488 |
1 files changed, 488 insertions, 0 deletions
diff --git a/arch/x86/platform/intel-mid/sfi.c b/arch/x86/platform/intel-mid/sfi.c new file mode 100644 index 000000000000..c84c1ca396bf --- /dev/null +++ b/arch/x86/platform/intel-mid/sfi.c | |||
@@ -0,0 +1,488 @@ | |||
1 | /* | ||
2 | * intel_mid_sfi.c: Intel MID SFI initialization code | ||
3 | * | ||
4 | * (C) Copyright 2013 Intel Corporation | ||
5 | * Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License | ||
9 | * as published by the Free Software Foundation; version 2 | ||
10 | * of the License. | ||
11 | */ | ||
12 | |||
13 | #include <linux/init.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/interrupt.h> | ||
16 | #include <linux/scatterlist.h> | ||
17 | #include <linux/sfi.h> | ||
18 | #include <linux/intel_pmic_gpio.h> | ||
19 | #include <linux/spi/spi.h> | ||
20 | #include <linux/i2c.h> | ||
21 | #include <linux/skbuff.h> | ||
22 | #include <linux/gpio.h> | ||
23 | #include <linux/gpio_keys.h> | ||
24 | #include <linux/input.h> | ||
25 | #include <linux/platform_device.h> | ||
26 | #include <linux/irq.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/notifier.h> | ||
29 | #include <linux/mmc/core.h> | ||
30 | #include <linux/mmc/card.h> | ||
31 | #include <linux/blkdev.h> | ||
32 | |||
33 | #include <asm/setup.h> | ||
34 | #include <asm/mpspec_def.h> | ||
35 | #include <asm/hw_irq.h> | ||
36 | #include <asm/apic.h> | ||
37 | #include <asm/io_apic.h> | ||
38 | #include <asm/intel-mid.h> | ||
39 | #include <asm/intel_mid_vrtc.h> | ||
40 | #include <asm/io.h> | ||
41 | #include <asm/i8259.h> | ||
42 | #include <asm/intel_scu_ipc.h> | ||
43 | #include <asm/apb_timer.h> | ||
44 | #include <asm/reboot.h> | ||
45 | |||
46 | #define SFI_SIG_OEM0 "OEM0" | ||
47 | #define MAX_IPCDEVS 24 | ||
48 | #define MAX_SCU_SPI 24 | ||
49 | #define MAX_SCU_I2C 24 | ||
50 | |||
51 | static struct platform_device *ipc_devs[MAX_IPCDEVS]; | ||
52 | static struct spi_board_info *spi_devs[MAX_SCU_SPI]; | ||
53 | static struct i2c_board_info *i2c_devs[MAX_SCU_I2C]; | ||
54 | static struct sfi_gpio_table_entry *gpio_table; | ||
55 | static struct sfi_timer_table_entry sfi_mtimer_array[SFI_MTMR_MAX_NUM]; | ||
56 | static int ipc_next_dev; | ||
57 | static int spi_next_dev; | ||
58 | static int i2c_next_dev; | ||
59 | static int i2c_bus[MAX_SCU_I2C]; | ||
60 | static int gpio_num_entry; | ||
61 | static u32 sfi_mtimer_usage[SFI_MTMR_MAX_NUM]; | ||
62 | int sfi_mrtc_num; | ||
63 | int sfi_mtimer_num; | ||
64 | |||
65 | struct sfi_rtc_table_entry sfi_mrtc_array[SFI_MRTC_MAX]; | ||
66 | EXPORT_SYMBOL_GPL(sfi_mrtc_array); | ||
67 | |||
68 | struct blocking_notifier_head intel_scu_notifier = | ||
69 | BLOCKING_NOTIFIER_INIT(intel_scu_notifier); | ||
70 | EXPORT_SYMBOL_GPL(intel_scu_notifier); | ||
71 | |||
72 | #define intel_mid_sfi_get_pdata(dev, priv) \ | ||
73 | ((dev)->get_platform_data ? (dev)->get_platform_data(priv) : NULL) | ||
74 | |||
75 | /* parse all the mtimer info to a static mtimer array */ | ||
76 | int __init sfi_parse_mtmr(struct sfi_table_header *table) | ||
77 | { | ||
78 | struct sfi_table_simple *sb; | ||
79 | struct sfi_timer_table_entry *pentry; | ||
80 | struct mpc_intsrc mp_irq; | ||
81 | int totallen; | ||
82 | |||
83 | sb = (struct sfi_table_simple *)table; | ||
84 | if (!sfi_mtimer_num) { | ||
85 | sfi_mtimer_num = SFI_GET_NUM_ENTRIES(sb, | ||
86 | struct sfi_timer_table_entry); | ||
87 | pentry = (struct sfi_timer_table_entry *) sb->pentry; | ||
88 | totallen = sfi_mtimer_num * sizeof(*pentry); | ||
89 | memcpy(sfi_mtimer_array, pentry, totallen); | ||
90 | } | ||
91 | |||
92 | pr_debug("SFI MTIMER info (num = %d):\n", sfi_mtimer_num); | ||
93 | pentry = sfi_mtimer_array; | ||
94 | for (totallen = 0; totallen < sfi_mtimer_num; totallen++, pentry++) { | ||
95 | pr_debug("timer[%d]: paddr = 0x%08x, freq = %dHz, irq = %d\n", | ||
96 | totallen, (u32)pentry->phys_addr, | ||
97 | pentry->freq_hz, pentry->irq); | ||
98 | if (!pentry->irq) | ||
99 | continue; | ||
100 | mp_irq.type = MP_INTSRC; | ||
101 | mp_irq.irqtype = mp_INT; | ||
102 | /* triggering mode edge bit 2-3, active high polarity bit 0-1 */ | ||
103 | mp_irq.irqflag = 5; | ||
104 | mp_irq.srcbus = MP_BUS_ISA; | ||
105 | mp_irq.srcbusirq = pentry->irq; /* IRQ */ | ||
106 | mp_irq.dstapic = MP_APIC_ALL; | ||
107 | mp_irq.dstirq = pentry->irq; | ||
108 | mp_save_irq(&mp_irq); | ||
109 | } | ||
110 | |||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | struct sfi_timer_table_entry *sfi_get_mtmr(int hint) | ||
115 | { | ||
116 | int i; | ||
117 | if (hint < sfi_mtimer_num) { | ||
118 | if (!sfi_mtimer_usage[hint]) { | ||
119 | pr_debug("hint taken for timer %d irq %d\n", | ||
120 | hint, sfi_mtimer_array[hint].irq); | ||
121 | sfi_mtimer_usage[hint] = 1; | ||
122 | return &sfi_mtimer_array[hint]; | ||
123 | } | ||
124 | } | ||
125 | /* take the first timer available */ | ||
126 | for (i = 0; i < sfi_mtimer_num;) { | ||
127 | if (!sfi_mtimer_usage[i]) { | ||
128 | sfi_mtimer_usage[i] = 1; | ||
129 | return &sfi_mtimer_array[i]; | ||
130 | } | ||
131 | i++; | ||
132 | } | ||
133 | return NULL; | ||
134 | } | ||
135 | |||
136 | void sfi_free_mtmr(struct sfi_timer_table_entry *mtmr) | ||
137 | { | ||
138 | int i; | ||
139 | for (i = 0; i < sfi_mtimer_num;) { | ||
140 | if (mtmr->irq == sfi_mtimer_array[i].irq) { | ||
141 | sfi_mtimer_usage[i] = 0; | ||
142 | return; | ||
143 | } | ||
144 | i++; | ||
145 | } | ||
146 | } | ||
147 | |||
148 | /* parse all the mrtc info to a global mrtc array */ | ||
149 | int __init sfi_parse_mrtc(struct sfi_table_header *table) | ||
150 | { | ||
151 | struct sfi_table_simple *sb; | ||
152 | struct sfi_rtc_table_entry *pentry; | ||
153 | struct mpc_intsrc mp_irq; | ||
154 | |||
155 | int totallen; | ||
156 | |||
157 | sb = (struct sfi_table_simple *)table; | ||
158 | if (!sfi_mrtc_num) { | ||
159 | sfi_mrtc_num = SFI_GET_NUM_ENTRIES(sb, | ||
160 | struct sfi_rtc_table_entry); | ||
161 | pentry = (struct sfi_rtc_table_entry *)sb->pentry; | ||
162 | totallen = sfi_mrtc_num * sizeof(*pentry); | ||
163 | memcpy(sfi_mrtc_array, pentry, totallen); | ||
164 | } | ||
165 | |||
166 | pr_debug("SFI RTC info (num = %d):\n", sfi_mrtc_num); | ||
167 | pentry = sfi_mrtc_array; | ||
168 | for (totallen = 0; totallen < sfi_mrtc_num; totallen++, pentry++) { | ||
169 | pr_debug("RTC[%d]: paddr = 0x%08x, irq = %d\n", | ||
170 | totallen, (u32)pentry->phys_addr, pentry->irq); | ||
171 | mp_irq.type = MP_INTSRC; | ||
172 | mp_irq.irqtype = mp_INT; | ||
173 | mp_irq.irqflag = 0xf; /* level trigger and active low */ | ||
174 | mp_irq.srcbus = MP_BUS_ISA; | ||
175 | mp_irq.srcbusirq = pentry->irq; /* IRQ */ | ||
176 | mp_irq.dstapic = MP_APIC_ALL; | ||
177 | mp_irq.dstirq = pentry->irq; | ||
178 | mp_save_irq(&mp_irq); | ||
179 | } | ||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | |||
184 | /* | ||
185 | * Parsing GPIO table first, since the DEVS table will need this table | ||
186 | * to map the pin name to the actual pin. | ||
187 | */ | ||
188 | static int __init sfi_parse_gpio(struct sfi_table_header *table) | ||
189 | { | ||
190 | struct sfi_table_simple *sb; | ||
191 | struct sfi_gpio_table_entry *pentry; | ||
192 | int num, i; | ||
193 | |||
194 | if (gpio_table) | ||
195 | return 0; | ||
196 | sb = (struct sfi_table_simple *)table; | ||
197 | num = SFI_GET_NUM_ENTRIES(sb, struct sfi_gpio_table_entry); | ||
198 | pentry = (struct sfi_gpio_table_entry *)sb->pentry; | ||
199 | |||
200 | gpio_table = kmalloc(num * sizeof(*pentry), GFP_KERNEL); | ||
201 | if (!gpio_table) | ||
202 | return -1; | ||
203 | memcpy(gpio_table, pentry, num * sizeof(*pentry)); | ||
204 | gpio_num_entry = num; | ||
205 | |||
206 | pr_debug("GPIO pin info:\n"); | ||
207 | for (i = 0; i < num; i++, pentry++) | ||
208 | pr_debug("info[%2d]: controller = %16.16s, pin_name = %16.16s," | ||
209 | " pin = %d\n", i, | ||
210 | pentry->controller_name, | ||
211 | pentry->pin_name, | ||
212 | pentry->pin_no); | ||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | int get_gpio_by_name(const char *name) | ||
217 | { | ||
218 | struct sfi_gpio_table_entry *pentry = gpio_table; | ||
219 | int i; | ||
220 | |||
221 | if (!pentry) | ||
222 | return -1; | ||
223 | for (i = 0; i < gpio_num_entry; i++, pentry++) { | ||
224 | if (!strncmp(name, pentry->pin_name, SFI_NAME_LEN)) | ||
225 | return pentry->pin_no; | ||
226 | } | ||
227 | return -1; | ||
228 | } | ||
229 | |||
230 | void __init intel_scu_device_register(struct platform_device *pdev) | ||
231 | { | ||
232 | if (ipc_next_dev == MAX_IPCDEVS) | ||
233 | pr_err("too many SCU IPC devices"); | ||
234 | else | ||
235 | ipc_devs[ipc_next_dev++] = pdev; | ||
236 | } | ||
237 | |||
238 | static void __init intel_scu_spi_device_register(struct spi_board_info *sdev) | ||
239 | { | ||
240 | struct spi_board_info *new_dev; | ||
241 | |||
242 | if (spi_next_dev == MAX_SCU_SPI) { | ||
243 | pr_err("too many SCU SPI devices"); | ||
244 | return; | ||
245 | } | ||
246 | |||
247 | new_dev = kzalloc(sizeof(*sdev), GFP_KERNEL); | ||
248 | if (!new_dev) { | ||
249 | pr_err("failed to alloc mem for delayed spi dev %s\n", | ||
250 | sdev->modalias); | ||
251 | return; | ||
252 | } | ||
253 | memcpy(new_dev, sdev, sizeof(*sdev)); | ||
254 | |||
255 | spi_devs[spi_next_dev++] = new_dev; | ||
256 | } | ||
257 | |||
258 | static void __init intel_scu_i2c_device_register(int bus, | ||
259 | struct i2c_board_info *idev) | ||
260 | { | ||
261 | struct i2c_board_info *new_dev; | ||
262 | |||
263 | if (i2c_next_dev == MAX_SCU_I2C) { | ||
264 | pr_err("too many SCU I2C devices"); | ||
265 | return; | ||
266 | } | ||
267 | |||
268 | new_dev = kzalloc(sizeof(*idev), GFP_KERNEL); | ||
269 | if (!new_dev) { | ||
270 | pr_err("failed to alloc mem for delayed i2c dev %s\n", | ||
271 | idev->type); | ||
272 | return; | ||
273 | } | ||
274 | memcpy(new_dev, idev, sizeof(*idev)); | ||
275 | |||
276 | i2c_bus[i2c_next_dev] = bus; | ||
277 | i2c_devs[i2c_next_dev++] = new_dev; | ||
278 | } | ||
279 | |||
280 | /* Called by IPC driver */ | ||
281 | void intel_scu_devices_create(void) | ||
282 | { | ||
283 | int i; | ||
284 | |||
285 | for (i = 0; i < ipc_next_dev; i++) | ||
286 | platform_device_add(ipc_devs[i]); | ||
287 | |||
288 | for (i = 0; i < spi_next_dev; i++) | ||
289 | spi_register_board_info(spi_devs[i], 1); | ||
290 | |||
291 | for (i = 0; i < i2c_next_dev; i++) { | ||
292 | struct i2c_adapter *adapter; | ||
293 | struct i2c_client *client; | ||
294 | |||
295 | adapter = i2c_get_adapter(i2c_bus[i]); | ||
296 | if (adapter) { | ||
297 | client = i2c_new_device(adapter, i2c_devs[i]); | ||
298 | if (!client) | ||
299 | pr_err("can't create i2c device %s\n", | ||
300 | i2c_devs[i]->type); | ||
301 | } else | ||
302 | i2c_register_board_info(i2c_bus[i], i2c_devs[i], 1); | ||
303 | } | ||
304 | intel_scu_notifier_post(SCU_AVAILABLE, NULL); | ||
305 | } | ||
306 | EXPORT_SYMBOL_GPL(intel_scu_devices_create); | ||
307 | |||
308 | /* Called by IPC driver */ | ||
309 | void intel_scu_devices_destroy(void) | ||
310 | { | ||
311 | int i; | ||
312 | |||
313 | intel_scu_notifier_post(SCU_DOWN, NULL); | ||
314 | |||
315 | for (i = 0; i < ipc_next_dev; i++) | ||
316 | platform_device_del(ipc_devs[i]); | ||
317 | } | ||
318 | EXPORT_SYMBOL_GPL(intel_scu_devices_destroy); | ||
319 | |||
320 | static void __init install_irq_resource(struct platform_device *pdev, int irq) | ||
321 | { | ||
322 | /* Single threaded */ | ||
323 | static struct resource res __initdata = { | ||
324 | .name = "IRQ", | ||
325 | .flags = IORESOURCE_IRQ, | ||
326 | }; | ||
327 | res.start = irq; | ||
328 | platform_device_add_resources(pdev, &res, 1); | ||
329 | } | ||
330 | |||
331 | static void __init sfi_handle_ipc_dev(struct sfi_device_table_entry *pentry, | ||
332 | struct devs_id *dev) | ||
333 | { | ||
334 | struct platform_device *pdev; | ||
335 | void *pdata = NULL; | ||
336 | |||
337 | pr_debug("IPC bus, name = %16.16s, irq = 0x%2x\n", | ||
338 | pentry->name, pentry->irq); | ||
339 | pdata = intel_mid_sfi_get_pdata(dev, pentry); | ||
340 | |||
341 | pdev = platform_device_alloc(pentry->name, 0); | ||
342 | if (pdev == NULL) { | ||
343 | pr_err("out of memory for SFI platform device '%s'.\n", | ||
344 | pentry->name); | ||
345 | return; | ||
346 | } | ||
347 | install_irq_resource(pdev, pentry->irq); | ||
348 | |||
349 | pdev->dev.platform_data = pdata; | ||
350 | platform_device_add(pdev); | ||
351 | } | ||
352 | |||
353 | static void __init sfi_handle_spi_dev(struct sfi_device_table_entry *pentry, | ||
354 | struct devs_id *dev) | ||
355 | { | ||
356 | struct spi_board_info spi_info; | ||
357 | void *pdata = NULL; | ||
358 | |||
359 | memset(&spi_info, 0, sizeof(spi_info)); | ||
360 | strncpy(spi_info.modalias, pentry->name, SFI_NAME_LEN); | ||
361 | spi_info.irq = ((pentry->irq == (u8)0xff) ? 0 : pentry->irq); | ||
362 | spi_info.bus_num = pentry->host_num; | ||
363 | spi_info.chip_select = pentry->addr; | ||
364 | spi_info.max_speed_hz = pentry->max_freq; | ||
365 | pr_debug("SPI bus=%d, name=%16.16s, irq=0x%2x, max_freq=%d, cs=%d\n", | ||
366 | spi_info.bus_num, | ||
367 | spi_info.modalias, | ||
368 | spi_info.irq, | ||
369 | spi_info.max_speed_hz, | ||
370 | spi_info.chip_select); | ||
371 | |||
372 | pdata = intel_mid_sfi_get_pdata(dev, &spi_info); | ||
373 | |||
374 | spi_info.platform_data = pdata; | ||
375 | if (dev->delay) | ||
376 | intel_scu_spi_device_register(&spi_info); | ||
377 | else | ||
378 | spi_register_board_info(&spi_info, 1); | ||
379 | } | ||
380 | |||
381 | static void __init sfi_handle_i2c_dev(struct sfi_device_table_entry *pentry, | ||
382 | struct devs_id *dev) | ||
383 | { | ||
384 | struct i2c_board_info i2c_info; | ||
385 | void *pdata = NULL; | ||
386 | |||
387 | memset(&i2c_info, 0, sizeof(i2c_info)); | ||
388 | strncpy(i2c_info.type, pentry->name, SFI_NAME_LEN); | ||
389 | i2c_info.irq = ((pentry->irq == (u8)0xff) ? 0 : pentry->irq); | ||
390 | i2c_info.addr = pentry->addr; | ||
391 | pr_debug("I2C bus = %d, name = %16.16s, irq = 0x%2x, addr = 0x%x\n", | ||
392 | pentry->host_num, | ||
393 | i2c_info.type, | ||
394 | i2c_info.irq, | ||
395 | i2c_info.addr); | ||
396 | pdata = intel_mid_sfi_get_pdata(dev, &i2c_info); | ||
397 | i2c_info.platform_data = pdata; | ||
398 | |||
399 | if (dev->delay) | ||
400 | intel_scu_i2c_device_register(pentry->host_num, &i2c_info); | ||
401 | else | ||
402 | i2c_register_board_info(pentry->host_num, &i2c_info, 1); | ||
403 | } | ||
404 | |||
405 | extern struct devs_id *const __x86_intel_mid_dev_start[], | ||
406 | *const __x86_intel_mid_dev_end[]; | ||
407 | |||
408 | static struct devs_id __init *get_device_id(u8 type, char *name) | ||
409 | { | ||
410 | struct devs_id *const *dev_table; | ||
411 | |||
412 | for (dev_table = __x86_intel_mid_dev_start; | ||
413 | dev_table < __x86_intel_mid_dev_end; dev_table++) { | ||
414 | struct devs_id *dev = *dev_table; | ||
415 | if (dev->type == type && | ||
416 | !strncmp(dev->name, name, SFI_NAME_LEN)) { | ||
417 | return dev; | ||
418 | } | ||
419 | } | ||
420 | |||
421 | return NULL; | ||
422 | } | ||
423 | |||
424 | static int __init sfi_parse_devs(struct sfi_table_header *table) | ||
425 | { | ||
426 | struct sfi_table_simple *sb; | ||
427 | struct sfi_device_table_entry *pentry; | ||
428 | struct devs_id *dev = NULL; | ||
429 | int num, i; | ||
430 | int ioapic; | ||
431 | struct io_apic_irq_attr irq_attr; | ||
432 | |||
433 | sb = (struct sfi_table_simple *)table; | ||
434 | num = SFI_GET_NUM_ENTRIES(sb, struct sfi_device_table_entry); | ||
435 | pentry = (struct sfi_device_table_entry *)sb->pentry; | ||
436 | |||
437 | for (i = 0; i < num; i++, pentry++) { | ||
438 | int irq = pentry->irq; | ||
439 | |||
440 | if (irq != (u8)0xff) { /* native RTE case */ | ||
441 | /* these SPI2 devices are not exposed to system as PCI | ||
442 | * devices, but they have separate RTE entry in IOAPIC | ||
443 | * so we have to enable them one by one here | ||
444 | */ | ||
445 | ioapic = mp_find_ioapic(irq); | ||
446 | irq_attr.ioapic = ioapic; | ||
447 | irq_attr.ioapic_pin = irq; | ||
448 | irq_attr.trigger = 1; | ||
449 | irq_attr.polarity = 1; | ||
450 | io_apic_set_pci_routing(NULL, irq, &irq_attr); | ||
451 | } else | ||
452 | irq = 0; /* No irq */ | ||
453 | |||
454 | dev = get_device_id(pentry->type, pentry->name); | ||
455 | |||
456 | if (!dev) | ||
457 | continue; | ||
458 | |||
459 | if (dev->device_handler) { | ||
460 | dev->device_handler(pentry, dev); | ||
461 | } else { | ||
462 | switch (pentry->type) { | ||
463 | case SFI_DEV_TYPE_IPC: | ||
464 | sfi_handle_ipc_dev(pentry, dev); | ||
465 | break; | ||
466 | case SFI_DEV_TYPE_SPI: | ||
467 | sfi_handle_spi_dev(pentry, dev); | ||
468 | break; | ||
469 | case SFI_DEV_TYPE_I2C: | ||
470 | sfi_handle_i2c_dev(pentry, dev); | ||
471 | break; | ||
472 | case SFI_DEV_TYPE_UART: | ||
473 | case SFI_DEV_TYPE_HSI: | ||
474 | default: | ||
475 | break; | ||
476 | } | ||
477 | } | ||
478 | } | ||
479 | return 0; | ||
480 | } | ||
481 | |||
482 | static int __init intel_mid_platform_init(void) | ||
483 | { | ||
484 | sfi_table_parse(SFI_SIG_GPIO, NULL, NULL, sfi_parse_gpio); | ||
485 | sfi_table_parse(SFI_SIG_DEVS, NULL, NULL, sfi_parse_devs); | ||
486 | return 0; | ||
487 | } | ||
488 | arch_initcall(intel_mid_platform_init); | ||