File Index
Files and directories
Sign in
FILE

ipmi_si_platform.c

File preview with syntax highlighting.

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