1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * ipmi_si_platform.c |
4 | * |
5 | * Handling for platform devices in IPMI (ACPI, OF, and things |
6 | * coming from the platform. |
7 | */ |
8 | |
9 | #define pr_fmt(fmt) "ipmi_platform: " fmt |
10 | #define dev_fmt pr_fmt |
11 | |
12 | #include <linux/types.h> |
13 | #include <linux/module.h> |
14 | #include <linux/of_device.h> |
15 | #include <linux/of_platform.h> |
16 | #include <linux/of_address.h> |
17 | #include <linux/of_irq.h> |
18 | #include <linux/acpi.h> |
19 | #include "ipmi_si.h" |
20 | #include "ipmi_dmi.h" |
21 | |
22 | static bool platform_registered; |
23 | static bool si_tryplatform = true; |
24 | #ifdef CONFIG_ACPI |
25 | static bool si_tryacpi = true; |
26 | #endif |
27 | #ifdef CONFIG_OF |
28 | static bool si_tryopenfirmware = true; |
29 | #endif |
30 | #ifdef CONFIG_DMI |
31 | static bool si_trydmi = true; |
32 | #else |
33 | static bool si_trydmi = false; |
34 | #endif |
35 | |
36 | module_param_named(tryplatform, si_tryplatform, bool, 0); |
37 | MODULE_PARM_DESC(tryplatform, |
38 | "Setting this to zero will disable the default scan of the interfaces identified via platform interfaces besides ACPI, OpenFirmware, and DMI" ); |
39 | #ifdef CONFIG_ACPI |
40 | module_param_named(tryacpi, si_tryacpi, bool, 0); |
41 | MODULE_PARM_DESC(tryacpi, |
42 | "Setting this to zero will disable the default scan of the interfaces identified via ACPI" ); |
43 | #endif |
44 | #ifdef CONFIG_OF |
45 | module_param_named(tryopenfirmware, si_tryopenfirmware, bool, 0); |
46 | MODULE_PARM_DESC(tryopenfirmware, |
47 | "Setting this to zero will disable the default scan of the interfaces identified via OpenFirmware" ); |
48 | #endif |
49 | #ifdef CONFIG_DMI |
50 | module_param_named(trydmi, si_trydmi, bool, 0); |
51 | MODULE_PARM_DESC(trydmi, |
52 | "Setting this to zero will disable the default scan of the interfaces identified via DMI" ); |
53 | #endif |
54 | |
55 | #ifdef CONFIG_ACPI |
56 | /* For GPE-type interrupts. */ |
57 | static u32 ipmi_acpi_gpe(acpi_handle gpe_device, |
58 | u32 gpe_number, void *context) |
59 | { |
60 | struct si_sm_io *io = context; |
61 | |
62 | ipmi_si_irq_handler(irq: io->irq, data: io->irq_handler_data); |
63 | return ACPI_INTERRUPT_HANDLED; |
64 | } |
65 | |
66 | static void acpi_gpe_irq_cleanup(struct si_sm_io *io) |
67 | { |
68 | if (!io->irq) |
69 | return; |
70 | |
71 | ipmi_irq_start_cleanup(io); |
72 | acpi_remove_gpe_handler(NULL, gpe_number: io->irq, address: &ipmi_acpi_gpe); |
73 | } |
74 | |
75 | static int acpi_gpe_irq_setup(struct si_sm_io *io) |
76 | { |
77 | acpi_status status; |
78 | |
79 | if (!io->irq) |
80 | return 0; |
81 | |
82 | status = acpi_install_gpe_handler(NULL, |
83 | gpe_number: io->irq, |
84 | ACPI_GPE_LEVEL_TRIGGERED, |
85 | address: &ipmi_acpi_gpe, |
86 | context: io); |
87 | if (ACPI_FAILURE(status)) { |
88 | dev_warn(io->dev, |
89 | "Unable to claim ACPI GPE %d, running polled\n" , |
90 | io->irq); |
91 | io->irq = 0; |
92 | return -EINVAL; |
93 | } |
94 | |
95 | io->irq_cleanup = acpi_gpe_irq_cleanup; |
96 | ipmi_irq_finish_setup(io); |
97 | dev_info(io->dev, "Using ACPI GPE %d\n" , io->irq); |
98 | return 0; |
99 | } |
100 | #endif |
101 | |
102 | static void ipmi_set_addr_data_and_space(struct resource *r, struct si_sm_io *io) |
103 | { |
104 | if (resource_type(res: r) == IORESOURCE_IO) |
105 | io->addr_space = IPMI_IO_ADDR_SPACE; |
106 | else |
107 | io->addr_space = IPMI_MEM_ADDR_SPACE; |
108 | io->addr_data = r->start; |
109 | } |
110 | |
111 | static struct resource * |
112 | ipmi_get_info_from_resources(struct platform_device *pdev, |
113 | struct si_sm_io *io) |
114 | { |
115 | struct resource *res, *res_second; |
116 | |
117 | res = platform_get_mem_or_io(pdev, 0); |
118 | if (!res) { |
119 | dev_err(&pdev->dev, "no I/O or memory address\n" ); |
120 | return NULL; |
121 | } |
122 | ipmi_set_addr_data_and_space(r: res, io); |
123 | |
124 | io->regspacing = DEFAULT_REGSPACING; |
125 | res_second = platform_get_mem_or_io(pdev, 1); |
126 | if (res_second && resource_type(res: res_second) == resource_type(res)) { |
127 | if (res_second->start > io->addr_data) |
128 | io->regspacing = res_second->start - io->addr_data; |
129 | } |
130 | |
131 | return res; |
132 | } |
133 | |
134 | static int platform_ipmi_probe(struct platform_device *pdev) |
135 | { |
136 | struct si_sm_io io; |
137 | u8 type, slave_addr, addr_source, regsize, regshift; |
138 | int rv; |
139 | |
140 | rv = device_property_read_u8(dev: &pdev->dev, propname: "addr-source" , val: &addr_source); |
141 | if (rv) |
142 | addr_source = SI_PLATFORM; |
143 | if (addr_source >= SI_LAST) |
144 | return -EINVAL; |
145 | |
146 | if (addr_source == SI_SMBIOS) { |
147 | if (!si_trydmi) |
148 | return -ENODEV; |
149 | } else if (addr_source != SI_HARDCODED) { |
150 | if (!si_tryplatform) |
151 | return -ENODEV; |
152 | } |
153 | |
154 | rv = device_property_read_u8(dev: &pdev->dev, propname: "ipmi-type" , val: &type); |
155 | if (rv) |
156 | return -ENODEV; |
157 | |
158 | memset(&io, 0, sizeof(io)); |
159 | io.addr_source = addr_source; |
160 | dev_info(&pdev->dev, "probing via %s\n" , |
161 | ipmi_addr_src_to_str(addr_source)); |
162 | |
163 | switch (type) { |
164 | case SI_KCS: |
165 | case SI_SMIC: |
166 | case SI_BT: |
167 | io.si_type = type; |
168 | break; |
169 | case SI_TYPE_INVALID: /* User disabled this in hardcode. */ |
170 | return -ENODEV; |
171 | default: |
172 | dev_err(&pdev->dev, "ipmi-type property is invalid\n" ); |
173 | return -EINVAL; |
174 | } |
175 | |
176 | io.regsize = DEFAULT_REGSIZE; |
177 | rv = device_property_read_u8(dev: &pdev->dev, propname: "reg-size" , val: ®size); |
178 | if (!rv) |
179 | io.regsize = regsize; |
180 | |
181 | io.regshift = 0; |
182 | rv = device_property_read_u8(dev: &pdev->dev, propname: "reg-shift" , val: ®shift); |
183 | if (!rv) |
184 | io.regshift = regshift; |
185 | |
186 | if (!ipmi_get_info_from_resources(pdev, io: &io)) |
187 | return -EINVAL; |
188 | |
189 | rv = device_property_read_u8(dev: &pdev->dev, propname: "slave-addr" , val: &slave_addr); |
190 | if (rv) |
191 | io.slave_addr = 0x20; |
192 | else |
193 | io.slave_addr = slave_addr; |
194 | |
195 | io.irq = platform_get_irq_optional(pdev, 0); |
196 | if (io.irq > 0) |
197 | io.irq_setup = ipmi_std_irq_setup; |
198 | else |
199 | io.irq = 0; |
200 | |
201 | io.dev = &pdev->dev; |
202 | |
203 | pr_info("ipmi_si: %s: %s %#lx regsize %d spacing %d irq %d\n" , |
204 | ipmi_addr_src_to_str(addr_source), |
205 | (io.addr_space == IPMI_IO_ADDR_SPACE) ? "io" : "mem" , |
206 | io.addr_data, io.regsize, io.regspacing, io.irq); |
207 | |
208 | ipmi_si_add_smi(io: &io); |
209 | |
210 | return 0; |
211 | } |
212 | |
213 | #ifdef CONFIG_OF |
214 | static const struct of_device_id of_ipmi_match[] = { |
215 | { .type = "ipmi" , .compatible = "ipmi-kcs" , |
216 | .data = (void *)(unsigned long) SI_KCS }, |
217 | { .type = "ipmi" , .compatible = "ipmi-smic" , |
218 | .data = (void *)(unsigned long) SI_SMIC }, |
219 | { .type = "ipmi" , .compatible = "ipmi-bt" , |
220 | .data = (void *)(unsigned long) SI_BT }, |
221 | {}, |
222 | }; |
223 | MODULE_DEVICE_TABLE(of, of_ipmi_match); |
224 | |
225 | static int of_ipmi_probe(struct platform_device *pdev) |
226 | { |
227 | const struct of_device_id *match; |
228 | struct si_sm_io io; |
229 | struct resource resource; |
230 | const __be32 *regsize, *regspacing, *regshift; |
231 | struct device_node *np = pdev->dev.of_node; |
232 | int ret; |
233 | int proplen; |
234 | |
235 | if (!si_tryopenfirmware) |
236 | return -ENODEV; |
237 | |
238 | dev_info(&pdev->dev, "probing via device tree\n" ); |
239 | |
240 | match = of_match_device(matches: of_ipmi_match, dev: &pdev->dev); |
241 | if (!match) |
242 | return -ENODEV; |
243 | |
244 | if (!of_device_is_available(device: np)) |
245 | return -EINVAL; |
246 | |
247 | ret = of_address_to_resource(dev: np, index: 0, r: &resource); |
248 | if (ret) { |
249 | dev_warn(&pdev->dev, "invalid address from OF\n" ); |
250 | return ret; |
251 | } |
252 | |
253 | regsize = of_get_property(node: np, name: "reg-size" , lenp: &proplen); |
254 | if (regsize && proplen != 4) { |
255 | dev_warn(&pdev->dev, "invalid regsize from OF\n" ); |
256 | return -EINVAL; |
257 | } |
258 | |
259 | regspacing = of_get_property(node: np, name: "reg-spacing" , lenp: &proplen); |
260 | if (regspacing && proplen != 4) { |
261 | dev_warn(&pdev->dev, "invalid regspacing from OF\n" ); |
262 | return -EINVAL; |
263 | } |
264 | |
265 | regshift = of_get_property(node: np, name: "reg-shift" , lenp: &proplen); |
266 | if (regshift && proplen != 4) { |
267 | dev_warn(&pdev->dev, "invalid regshift from OF\n" ); |
268 | return -EINVAL; |
269 | } |
270 | |
271 | memset(&io, 0, sizeof(io)); |
272 | io.si_type = (unsigned long) match->data; |
273 | io.addr_source = SI_DEVICETREE; |
274 | io.irq_setup = ipmi_std_irq_setup; |
275 | |
276 | ipmi_set_addr_data_and_space(r: &resource, io: &io); |
277 | |
278 | io.regsize = regsize ? be32_to_cpup(p: regsize) : DEFAULT_REGSIZE; |
279 | io.regspacing = regspacing ? be32_to_cpup(p: regspacing) : DEFAULT_REGSPACING; |
280 | io.regshift = regshift ? be32_to_cpup(p: regshift) : 0; |
281 | |
282 | io.irq = irq_of_parse_and_map(node: pdev->dev.of_node, index: 0); |
283 | io.dev = &pdev->dev; |
284 | |
285 | dev_dbg(&pdev->dev, "addr 0x%lx regsize %d spacing %d irq %d\n" , |
286 | io.addr_data, io.regsize, io.regspacing, io.irq); |
287 | |
288 | return ipmi_si_add_smi(io: &io); |
289 | } |
290 | #else |
291 | #define of_ipmi_match NULL |
292 | static int of_ipmi_probe(struct platform_device *dev) |
293 | { |
294 | return -ENODEV; |
295 | } |
296 | #endif |
297 | |
298 | #ifdef CONFIG_ACPI |
299 | static int find_slave_address(struct si_sm_io *io, int slave_addr) |
300 | { |
301 | #ifdef CONFIG_IPMI_DMI_DECODE |
302 | if (!slave_addr) |
303 | slave_addr = ipmi_dmi_get_slave_addr(si_type: io->si_type, |
304 | space: io->addr_space, |
305 | base_addr: io->addr_data); |
306 | #endif |
307 | |
308 | return slave_addr; |
309 | } |
310 | |
311 | static int acpi_ipmi_probe(struct platform_device *pdev) |
312 | { |
313 | struct device *dev = &pdev->dev; |
314 | struct si_sm_io io; |
315 | acpi_handle handle; |
316 | acpi_status status; |
317 | unsigned long long tmp; |
318 | struct resource *res; |
319 | |
320 | if (!si_tryacpi) |
321 | return -ENODEV; |
322 | |
323 | handle = ACPI_HANDLE(dev); |
324 | if (!handle) |
325 | return -ENODEV; |
326 | |
327 | memset(&io, 0, sizeof(io)); |
328 | io.addr_source = SI_ACPI; |
329 | dev_info(dev, "probing via ACPI\n" ); |
330 | |
331 | io.addr_info.acpi_info.acpi_handle = handle; |
332 | |
333 | /* _IFT tells us the interface type: KCS, BT, etc */ |
334 | status = acpi_evaluate_integer(handle, pathname: "_IFT" , NULL, data: &tmp); |
335 | if (ACPI_FAILURE(status)) { |
336 | dev_err(dev, "Could not find ACPI IPMI interface type\n" ); |
337 | return -EINVAL; |
338 | } |
339 | |
340 | switch (tmp) { |
341 | case 1: |
342 | io.si_type = SI_KCS; |
343 | break; |
344 | case 2: |
345 | io.si_type = SI_SMIC; |
346 | break; |
347 | case 3: |
348 | io.si_type = SI_BT; |
349 | break; |
350 | case 4: /* SSIF, just ignore */ |
351 | return -ENODEV; |
352 | default: |
353 | dev_info(dev, "unknown IPMI type %lld\n" , tmp); |
354 | return -EINVAL; |
355 | } |
356 | |
357 | io.dev = dev; |
358 | io.regsize = DEFAULT_REGSIZE; |
359 | io.regshift = 0; |
360 | |
361 | res = ipmi_get_info_from_resources(pdev, io: &io); |
362 | if (!res) |
363 | return -EINVAL; |
364 | |
365 | /* If _GPE exists, use it; otherwise use standard interrupts */ |
366 | status = acpi_evaluate_integer(handle, pathname: "_GPE" , NULL, data: &tmp); |
367 | if (ACPI_SUCCESS(status)) { |
368 | io.irq = tmp; |
369 | io.irq_setup = acpi_gpe_irq_setup; |
370 | } else { |
371 | int irq = platform_get_irq_optional(pdev, 0); |
372 | |
373 | if (irq > 0) { |
374 | io.irq = irq; |
375 | io.irq_setup = ipmi_std_irq_setup; |
376 | } |
377 | } |
378 | |
379 | io.slave_addr = find_slave_address(io: &io, slave_addr: io.slave_addr); |
380 | |
381 | dev_info(dev, "%pR regsize %d spacing %d irq %d\n" , |
382 | res, io.regsize, io.regspacing, io.irq); |
383 | |
384 | request_module_nowait("acpi_ipmi" ); |
385 | |
386 | return ipmi_si_add_smi(io: &io); |
387 | } |
388 | |
389 | static const struct acpi_device_id acpi_ipmi_match[] = { |
390 | { "IPI0001" , 0 }, |
391 | { }, |
392 | }; |
393 | MODULE_DEVICE_TABLE(acpi, acpi_ipmi_match); |
394 | #else |
395 | static int acpi_ipmi_probe(struct platform_device *dev) |
396 | { |
397 | return -ENODEV; |
398 | } |
399 | #endif |
400 | |
401 | static int ipmi_probe(struct platform_device *pdev) |
402 | { |
403 | if (pdev->dev.of_node && of_ipmi_probe(pdev) == 0) |
404 | return 0; |
405 | |
406 | if (acpi_ipmi_probe(pdev) == 0) |
407 | return 0; |
408 | |
409 | return platform_ipmi_probe(pdev); |
410 | } |
411 | |
412 | static int ipmi_remove(struct platform_device *pdev) |
413 | { |
414 | ipmi_si_remove_by_dev(dev: &pdev->dev); |
415 | |
416 | return 0; |
417 | } |
418 | |
419 | static int pdev_match_name(struct device *dev, const void *data) |
420 | { |
421 | struct platform_device *pdev = to_platform_device(dev); |
422 | const char *name = data; |
423 | |
424 | return strcmp(pdev->name, name) == 0; |
425 | } |
426 | |
427 | void ipmi_remove_platform_device_by_name(char *name) |
428 | { |
429 | struct device *dev; |
430 | |
431 | while ((dev = bus_find_device(bus: &platform_bus_type, NULL, data: name, |
432 | match: pdev_match_name))) { |
433 | struct platform_device *pdev = to_platform_device(dev); |
434 | |
435 | platform_device_unregister(pdev); |
436 | put_device(dev); |
437 | } |
438 | } |
439 | |
440 | static const struct platform_device_id si_plat_ids[] = { |
441 | { "dmi-ipmi-si" , 0 }, |
442 | { "hardcode-ipmi-si" , 0 }, |
443 | { "hotmod-ipmi-si" , 0 }, |
444 | { } |
445 | }; |
446 | |
447 | struct platform_driver ipmi_platform_driver = { |
448 | .driver = { |
449 | .name = SI_DEVICE_NAME, |
450 | .of_match_table = of_ipmi_match, |
451 | .acpi_match_table = ACPI_PTR(acpi_ipmi_match), |
452 | }, |
453 | .probe = ipmi_probe, |
454 | .remove = ipmi_remove, |
455 | .id_table = si_plat_ids |
456 | }; |
457 | |
458 | void ipmi_si_platform_init(void) |
459 | { |
460 | int rv = platform_driver_register(&ipmi_platform_driver); |
461 | if (rv) |
462 | pr_err("Unable to register driver: %d\n" , rv); |
463 | else |
464 | platform_registered = true; |
465 | } |
466 | |
467 | void ipmi_si_platform_shutdown(void) |
468 | { |
469 | if (platform_registered) |
470 | platform_driver_unregister(&ipmi_platform_driver); |
471 | } |
472 | |