18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * ipmi_si_platform.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Handling for platform devices in IPMI (ACPI, OF, and things
68c2ecf20Sopenharmony_ci * coming from the platform.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "ipmi_platform: " fmt
108c2ecf20Sopenharmony_ci#define dev_fmt pr_fmt
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/types.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/of_device.h>
158c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
168c2ecf20Sopenharmony_ci#include <linux/of_address.h>
178c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
188c2ecf20Sopenharmony_ci#include <linux/acpi.h>
198c2ecf20Sopenharmony_ci#include "ipmi_si.h"
208c2ecf20Sopenharmony_ci#include "ipmi_dmi.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic bool platform_registered;
238c2ecf20Sopenharmony_cistatic bool si_tryplatform = true;
248c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI
258c2ecf20Sopenharmony_cistatic bool          si_tryacpi = true;
268c2ecf20Sopenharmony_ci#endif
278c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
288c2ecf20Sopenharmony_cistatic bool          si_tryopenfirmware = true;
298c2ecf20Sopenharmony_ci#endif
308c2ecf20Sopenharmony_ci#ifdef CONFIG_DMI
318c2ecf20Sopenharmony_cistatic bool          si_trydmi = true;
328c2ecf20Sopenharmony_ci#else
338c2ecf20Sopenharmony_cistatic bool          si_trydmi = false;
348c2ecf20Sopenharmony_ci#endif
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cimodule_param_named(tryplatform, si_tryplatform, bool, 0);
378c2ecf20Sopenharmony_ciMODULE_PARM_DESC(tryplatform, "Setting this to zero will disable the"
388c2ecf20Sopenharmony_ci		 " default scan of the interfaces identified via platform"
398c2ecf20Sopenharmony_ci		 " interfaces besides ACPI, OpenFirmware, and DMI");
408c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI
418c2ecf20Sopenharmony_cimodule_param_named(tryacpi, si_tryacpi, bool, 0);
428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(tryacpi, "Setting this to zero will disable the"
438c2ecf20Sopenharmony_ci		 " default scan of the interfaces identified via ACPI");
448c2ecf20Sopenharmony_ci#endif
458c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
468c2ecf20Sopenharmony_cimodule_param_named(tryopenfirmware, si_tryopenfirmware, bool, 0);
478c2ecf20Sopenharmony_ciMODULE_PARM_DESC(tryopenfirmware, "Setting this to zero will disable the"
488c2ecf20Sopenharmony_ci		 " default scan of the interfaces identified via OpenFirmware");
498c2ecf20Sopenharmony_ci#endif
508c2ecf20Sopenharmony_ci#ifdef CONFIG_DMI
518c2ecf20Sopenharmony_cimodule_param_named(trydmi, si_trydmi, bool, 0);
528c2ecf20Sopenharmony_ciMODULE_PARM_DESC(trydmi, "Setting this to zero will disable the"
538c2ecf20Sopenharmony_ci		 " default scan of the interfaces identified via DMI");
548c2ecf20Sopenharmony_ci#endif
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI
578c2ecf20Sopenharmony_ci/* For GPE-type interrupts. */
588c2ecf20Sopenharmony_cistatic u32 ipmi_acpi_gpe(acpi_handle gpe_device,
598c2ecf20Sopenharmony_ci	u32 gpe_number, void *context)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	struct si_sm_io *io = context;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	ipmi_si_irq_handler(io->irq, io->irq_handler_data);
648c2ecf20Sopenharmony_ci	return ACPI_INTERRUPT_HANDLED;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic void acpi_gpe_irq_cleanup(struct si_sm_io *io)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	if (!io->irq)
708c2ecf20Sopenharmony_ci		return;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	ipmi_irq_start_cleanup(io);
738c2ecf20Sopenharmony_ci	acpi_remove_gpe_handler(NULL, io->irq, &ipmi_acpi_gpe);
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic int acpi_gpe_irq_setup(struct si_sm_io *io)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	acpi_status status;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	if (!io->irq)
818c2ecf20Sopenharmony_ci		return 0;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	status = acpi_install_gpe_handler(NULL,
848c2ecf20Sopenharmony_ci					  io->irq,
858c2ecf20Sopenharmony_ci					  ACPI_GPE_LEVEL_TRIGGERED,
868c2ecf20Sopenharmony_ci					  &ipmi_acpi_gpe,
878c2ecf20Sopenharmony_ci					  io);
888c2ecf20Sopenharmony_ci	if (status != AE_OK) {
898c2ecf20Sopenharmony_ci		dev_warn(io->dev,
908c2ecf20Sopenharmony_ci			 "Unable to claim ACPI GPE %d, running polled\n",
918c2ecf20Sopenharmony_ci			 io->irq);
928c2ecf20Sopenharmony_ci		io->irq = 0;
938c2ecf20Sopenharmony_ci		return -EINVAL;
948c2ecf20Sopenharmony_ci	} else {
958c2ecf20Sopenharmony_ci		io->irq_cleanup = acpi_gpe_irq_cleanup;
968c2ecf20Sopenharmony_ci		ipmi_irq_finish_setup(io);
978c2ecf20Sopenharmony_ci		dev_info(io->dev, "Using ACPI GPE %d\n", io->irq);
988c2ecf20Sopenharmony_ci		return 0;
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci#endif
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic struct resource *
1048c2ecf20Sopenharmony_ciipmi_get_info_from_resources(struct platform_device *pdev,
1058c2ecf20Sopenharmony_ci			     struct si_sm_io *io)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	struct resource *res, *res_second;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
1108c2ecf20Sopenharmony_ci	if (res) {
1118c2ecf20Sopenharmony_ci		io->addr_space = IPMI_IO_ADDR_SPACE;
1128c2ecf20Sopenharmony_ci	} else {
1138c2ecf20Sopenharmony_ci		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1148c2ecf20Sopenharmony_ci		if (res)
1158c2ecf20Sopenharmony_ci			io->addr_space = IPMI_MEM_ADDR_SPACE;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci	if (!res) {
1188c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "no I/O or memory address\n");
1198c2ecf20Sopenharmony_ci		return NULL;
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci	io->addr_data = res->start;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	io->regspacing = DEFAULT_REGSPACING;
1248c2ecf20Sopenharmony_ci	res_second = platform_get_resource(pdev,
1258c2ecf20Sopenharmony_ci			       (io->addr_space == IPMI_IO_ADDR_SPACE) ?
1268c2ecf20Sopenharmony_ci					IORESOURCE_IO : IORESOURCE_MEM,
1278c2ecf20Sopenharmony_ci			       1);
1288c2ecf20Sopenharmony_ci	if (res_second) {
1298c2ecf20Sopenharmony_ci		if (res_second->start > io->addr_data)
1308c2ecf20Sopenharmony_ci			io->regspacing = res_second->start - io->addr_data;
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	return res;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic int platform_ipmi_probe(struct platform_device *pdev)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	struct si_sm_io io;
1398c2ecf20Sopenharmony_ci	u8 type, slave_addr, addr_source, regsize, regshift;
1408c2ecf20Sopenharmony_ci	int rv;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	rv = device_property_read_u8(&pdev->dev, "addr-source", &addr_source);
1438c2ecf20Sopenharmony_ci	if (rv)
1448c2ecf20Sopenharmony_ci		addr_source = SI_PLATFORM;
1458c2ecf20Sopenharmony_ci	if (addr_source >= SI_LAST)
1468c2ecf20Sopenharmony_ci		return -EINVAL;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	if (addr_source == SI_SMBIOS) {
1498c2ecf20Sopenharmony_ci		if (!si_trydmi)
1508c2ecf20Sopenharmony_ci			return -ENODEV;
1518c2ecf20Sopenharmony_ci	} else if (addr_source != SI_HARDCODED) {
1528c2ecf20Sopenharmony_ci		if (!si_tryplatform)
1538c2ecf20Sopenharmony_ci			return -ENODEV;
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	rv = device_property_read_u8(&pdev->dev, "ipmi-type", &type);
1578c2ecf20Sopenharmony_ci	if (rv)
1588c2ecf20Sopenharmony_ci		return -ENODEV;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	memset(&io, 0, sizeof(io));
1618c2ecf20Sopenharmony_ci	io.addr_source = addr_source;
1628c2ecf20Sopenharmony_ci	dev_info(&pdev->dev, "probing via %s\n",
1638c2ecf20Sopenharmony_ci		 ipmi_addr_src_to_str(addr_source));
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	switch (type) {
1668c2ecf20Sopenharmony_ci	case SI_KCS:
1678c2ecf20Sopenharmony_ci	case SI_SMIC:
1688c2ecf20Sopenharmony_ci	case SI_BT:
1698c2ecf20Sopenharmony_ci		io.si_type = type;
1708c2ecf20Sopenharmony_ci		break;
1718c2ecf20Sopenharmony_ci	case SI_TYPE_INVALID: /* User disabled this in hardcode. */
1728c2ecf20Sopenharmony_ci		return -ENODEV;
1738c2ecf20Sopenharmony_ci	default:
1748c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "ipmi-type property is invalid\n");
1758c2ecf20Sopenharmony_ci		return -EINVAL;
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	io.regsize = DEFAULT_REGSIZE;
1798c2ecf20Sopenharmony_ci	rv = device_property_read_u8(&pdev->dev, "reg-size", &regsize);
1808c2ecf20Sopenharmony_ci	if (!rv)
1818c2ecf20Sopenharmony_ci		io.regsize = regsize;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	io.regshift = 0;
1848c2ecf20Sopenharmony_ci	rv = device_property_read_u8(&pdev->dev, "reg-shift", &regshift);
1858c2ecf20Sopenharmony_ci	if (!rv)
1868c2ecf20Sopenharmony_ci		io.regshift = regshift;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	if (!ipmi_get_info_from_resources(pdev, &io))
1898c2ecf20Sopenharmony_ci		return -EINVAL;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	rv = device_property_read_u8(&pdev->dev, "slave-addr", &slave_addr);
1928c2ecf20Sopenharmony_ci	if (rv)
1938c2ecf20Sopenharmony_ci		io.slave_addr = 0x20;
1948c2ecf20Sopenharmony_ci	else
1958c2ecf20Sopenharmony_ci		io.slave_addr = slave_addr;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	io.irq = platform_get_irq_optional(pdev, 0);
1988c2ecf20Sopenharmony_ci	if (io.irq > 0)
1998c2ecf20Sopenharmony_ci		io.irq_setup = ipmi_std_irq_setup;
2008c2ecf20Sopenharmony_ci	else
2018c2ecf20Sopenharmony_ci		io.irq = 0;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	io.dev = &pdev->dev;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	pr_info("ipmi_si: %s: %s %#lx regsize %d spacing %d irq %d\n",
2068c2ecf20Sopenharmony_ci		ipmi_addr_src_to_str(addr_source),
2078c2ecf20Sopenharmony_ci		(io.addr_space == IPMI_IO_ADDR_SPACE) ? "io" : "mem",
2088c2ecf20Sopenharmony_ci		io.addr_data, io.regsize, io.regspacing, io.irq);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	ipmi_si_add_smi(&io);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	return 0;
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
2168c2ecf20Sopenharmony_cistatic const struct of_device_id of_ipmi_match[] = {
2178c2ecf20Sopenharmony_ci	{ .type = "ipmi", .compatible = "ipmi-kcs",
2188c2ecf20Sopenharmony_ci	  .data = (void *)(unsigned long) SI_KCS },
2198c2ecf20Sopenharmony_ci	{ .type = "ipmi", .compatible = "ipmi-smic",
2208c2ecf20Sopenharmony_ci	  .data = (void *)(unsigned long) SI_SMIC },
2218c2ecf20Sopenharmony_ci	{ .type = "ipmi", .compatible = "ipmi-bt",
2228c2ecf20Sopenharmony_ci	  .data = (void *)(unsigned long) SI_BT },
2238c2ecf20Sopenharmony_ci	{},
2248c2ecf20Sopenharmony_ci};
2258c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_ipmi_match);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic int of_ipmi_probe(struct platform_device *pdev)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	const struct of_device_id *match;
2308c2ecf20Sopenharmony_ci	struct si_sm_io io;
2318c2ecf20Sopenharmony_ci	struct resource resource;
2328c2ecf20Sopenharmony_ci	const __be32 *regsize, *regspacing, *regshift;
2338c2ecf20Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
2348c2ecf20Sopenharmony_ci	int ret;
2358c2ecf20Sopenharmony_ci	int proplen;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	if (!si_tryopenfirmware)
2388c2ecf20Sopenharmony_ci		return -ENODEV;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	dev_info(&pdev->dev, "probing via device tree\n");
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	match = of_match_device(of_ipmi_match, &pdev->dev);
2438c2ecf20Sopenharmony_ci	if (!match)
2448c2ecf20Sopenharmony_ci		return -ENODEV;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	if (!of_device_is_available(np))
2478c2ecf20Sopenharmony_ci		return -EINVAL;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	ret = of_address_to_resource(np, 0, &resource);
2508c2ecf20Sopenharmony_ci	if (ret) {
2518c2ecf20Sopenharmony_ci		dev_warn(&pdev->dev, "invalid address from OF\n");
2528c2ecf20Sopenharmony_ci		return ret;
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	regsize = of_get_property(np, "reg-size", &proplen);
2568c2ecf20Sopenharmony_ci	if (regsize && proplen != 4) {
2578c2ecf20Sopenharmony_ci		dev_warn(&pdev->dev, "invalid regsize from OF\n");
2588c2ecf20Sopenharmony_ci		return -EINVAL;
2598c2ecf20Sopenharmony_ci	}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	regspacing = of_get_property(np, "reg-spacing", &proplen);
2628c2ecf20Sopenharmony_ci	if (regspacing && proplen != 4) {
2638c2ecf20Sopenharmony_ci		dev_warn(&pdev->dev, "invalid regspacing from OF\n");
2648c2ecf20Sopenharmony_ci		return -EINVAL;
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	regshift = of_get_property(np, "reg-shift", &proplen);
2688c2ecf20Sopenharmony_ci	if (regshift && proplen != 4) {
2698c2ecf20Sopenharmony_ci		dev_warn(&pdev->dev, "invalid regshift from OF\n");
2708c2ecf20Sopenharmony_ci		return -EINVAL;
2718c2ecf20Sopenharmony_ci	}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	memset(&io, 0, sizeof(io));
2748c2ecf20Sopenharmony_ci	io.si_type	= (enum si_type) match->data;
2758c2ecf20Sopenharmony_ci	io.addr_source	= SI_DEVICETREE;
2768c2ecf20Sopenharmony_ci	io.irq_setup	= ipmi_std_irq_setup;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	if (resource.flags & IORESOURCE_IO)
2798c2ecf20Sopenharmony_ci		io.addr_space = IPMI_IO_ADDR_SPACE;
2808c2ecf20Sopenharmony_ci	else
2818c2ecf20Sopenharmony_ci		io.addr_space = IPMI_MEM_ADDR_SPACE;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	io.addr_data	= resource.start;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	io.regsize	= regsize ? be32_to_cpup(regsize) : DEFAULT_REGSIZE;
2868c2ecf20Sopenharmony_ci	io.regspacing	= regspacing ? be32_to_cpup(regspacing) : DEFAULT_REGSPACING;
2878c2ecf20Sopenharmony_ci	io.regshift	= regshift ? be32_to_cpup(regshift) : 0;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	io.irq		= irq_of_parse_and_map(pdev->dev.of_node, 0);
2908c2ecf20Sopenharmony_ci	io.dev		= &pdev->dev;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "addr 0x%lx regsize %d spacing %d irq %d\n",
2938c2ecf20Sopenharmony_ci		io.addr_data, io.regsize, io.regspacing, io.irq);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	return ipmi_si_add_smi(&io);
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci#else
2988c2ecf20Sopenharmony_ci#define of_ipmi_match NULL
2998c2ecf20Sopenharmony_cistatic int of_ipmi_probe(struct platform_device *dev)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	return -ENODEV;
3028c2ecf20Sopenharmony_ci}
3038c2ecf20Sopenharmony_ci#endif
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI
3068c2ecf20Sopenharmony_cistatic int find_slave_address(struct si_sm_io *io, int slave_addr)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci#ifdef CONFIG_IPMI_DMI_DECODE
3098c2ecf20Sopenharmony_ci	if (!slave_addr)
3108c2ecf20Sopenharmony_ci		slave_addr = ipmi_dmi_get_slave_addr(io->si_type,
3118c2ecf20Sopenharmony_ci						     io->addr_space,
3128c2ecf20Sopenharmony_ci						     io->addr_data);
3138c2ecf20Sopenharmony_ci#endif
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	return slave_addr;
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic int acpi_ipmi_probe(struct platform_device *pdev)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	struct si_sm_io io;
3218c2ecf20Sopenharmony_ci	acpi_handle handle;
3228c2ecf20Sopenharmony_ci	acpi_status status;
3238c2ecf20Sopenharmony_ci	unsigned long long tmp;
3248c2ecf20Sopenharmony_ci	struct resource *res;
3258c2ecf20Sopenharmony_ci	int rv = -EINVAL;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	if (!si_tryacpi)
3288c2ecf20Sopenharmony_ci		return -ENODEV;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	handle = ACPI_HANDLE(&pdev->dev);
3318c2ecf20Sopenharmony_ci	if (!handle)
3328c2ecf20Sopenharmony_ci		return -ENODEV;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	memset(&io, 0, sizeof(io));
3358c2ecf20Sopenharmony_ci	io.addr_source = SI_ACPI;
3368c2ecf20Sopenharmony_ci	dev_info(&pdev->dev, "probing via ACPI\n");
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	io.addr_info.acpi_info.acpi_handle = handle;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	/* _IFT tells us the interface type: KCS, BT, etc */
3418c2ecf20Sopenharmony_ci	status = acpi_evaluate_integer(handle, "_IFT", NULL, &tmp);
3428c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status)) {
3438c2ecf20Sopenharmony_ci		dev_err(&pdev->dev,
3448c2ecf20Sopenharmony_ci			"Could not find ACPI IPMI interface type\n");
3458c2ecf20Sopenharmony_ci		goto err_free;
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	switch (tmp) {
3498c2ecf20Sopenharmony_ci	case 1:
3508c2ecf20Sopenharmony_ci		io.si_type = SI_KCS;
3518c2ecf20Sopenharmony_ci		break;
3528c2ecf20Sopenharmony_ci	case 2:
3538c2ecf20Sopenharmony_ci		io.si_type = SI_SMIC;
3548c2ecf20Sopenharmony_ci		break;
3558c2ecf20Sopenharmony_ci	case 3:
3568c2ecf20Sopenharmony_ci		io.si_type = SI_BT;
3578c2ecf20Sopenharmony_ci		break;
3588c2ecf20Sopenharmony_ci	case 4: /* SSIF, just ignore */
3598c2ecf20Sopenharmony_ci		rv = -ENODEV;
3608c2ecf20Sopenharmony_ci		goto err_free;
3618c2ecf20Sopenharmony_ci	default:
3628c2ecf20Sopenharmony_ci		dev_info(&pdev->dev, "unknown IPMI type %lld\n", tmp);
3638c2ecf20Sopenharmony_ci		goto err_free;
3648c2ecf20Sopenharmony_ci	}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	io.regsize = DEFAULT_REGSIZE;
3678c2ecf20Sopenharmony_ci	io.regshift = 0;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	res = ipmi_get_info_from_resources(pdev, &io);
3708c2ecf20Sopenharmony_ci	if (!res) {
3718c2ecf20Sopenharmony_ci		rv = -EINVAL;
3728c2ecf20Sopenharmony_ci		goto err_free;
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	/* If _GPE exists, use it; otherwise use standard interrupts */
3768c2ecf20Sopenharmony_ci	status = acpi_evaluate_integer(handle, "_GPE", NULL, &tmp);
3778c2ecf20Sopenharmony_ci	if (ACPI_SUCCESS(status)) {
3788c2ecf20Sopenharmony_ci		io.irq = tmp;
3798c2ecf20Sopenharmony_ci		io.irq_setup = acpi_gpe_irq_setup;
3808c2ecf20Sopenharmony_ci	} else {
3818c2ecf20Sopenharmony_ci		int irq = platform_get_irq_optional(pdev, 0);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci		if (irq > 0) {
3848c2ecf20Sopenharmony_ci			io.irq = irq;
3858c2ecf20Sopenharmony_ci			io.irq_setup = ipmi_std_irq_setup;
3868c2ecf20Sopenharmony_ci		}
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	io.slave_addr = find_slave_address(&io, io.slave_addr);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	io.dev = &pdev->dev;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	dev_info(io.dev, "%pR regsize %d spacing %d irq %d\n",
3948c2ecf20Sopenharmony_ci		 res, io.regsize, io.regspacing, io.irq);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	request_module("acpi_ipmi");
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	return ipmi_si_add_smi(&io);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cierr_free:
4018c2ecf20Sopenharmony_ci	return rv;
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic const struct acpi_device_id acpi_ipmi_match[] = {
4058c2ecf20Sopenharmony_ci	{ "IPI0001", 0 },
4068c2ecf20Sopenharmony_ci	{ },
4078c2ecf20Sopenharmony_ci};
4088c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, acpi_ipmi_match);
4098c2ecf20Sopenharmony_ci#else
4108c2ecf20Sopenharmony_cistatic int acpi_ipmi_probe(struct platform_device *dev)
4118c2ecf20Sopenharmony_ci{
4128c2ecf20Sopenharmony_ci	return -ENODEV;
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci#endif
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_cistatic int ipmi_probe(struct platform_device *pdev)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	if (pdev->dev.of_node && of_ipmi_probe(pdev) == 0)
4198c2ecf20Sopenharmony_ci		return 0;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	if (acpi_ipmi_probe(pdev) == 0)
4228c2ecf20Sopenharmony_ci		return 0;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	return platform_ipmi_probe(pdev);
4258c2ecf20Sopenharmony_ci}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cistatic int ipmi_remove(struct platform_device *pdev)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	return ipmi_si_remove_by_dev(&pdev->dev);
4308c2ecf20Sopenharmony_ci}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_cistatic int pdev_match_name(struct device *dev, const void *data)
4338c2ecf20Sopenharmony_ci{
4348c2ecf20Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
4358c2ecf20Sopenharmony_ci	const char *name = data;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	return strcmp(pdev->name, name) == 0;
4388c2ecf20Sopenharmony_ci}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_civoid ipmi_remove_platform_device_by_name(char *name)
4418c2ecf20Sopenharmony_ci{
4428c2ecf20Sopenharmony_ci	struct device *dev;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	while ((dev = bus_find_device(&platform_bus_type, NULL, name,
4458c2ecf20Sopenharmony_ci				      pdev_match_name))) {
4468c2ecf20Sopenharmony_ci		struct platform_device *pdev = to_platform_device(dev);
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci		platform_device_unregister(pdev);
4498c2ecf20Sopenharmony_ci		put_device(dev);
4508c2ecf20Sopenharmony_ci	}
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_cistatic const struct platform_device_id si_plat_ids[] = {
4548c2ecf20Sopenharmony_ci	{ "dmi-ipmi-si", 0 },
4558c2ecf20Sopenharmony_ci	{ "hardcode-ipmi-si", 0 },
4568c2ecf20Sopenharmony_ci	{ "hotmod-ipmi-si", 0 },
4578c2ecf20Sopenharmony_ci	{ }
4588c2ecf20Sopenharmony_ci};
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_cistruct platform_driver ipmi_platform_driver = {
4618c2ecf20Sopenharmony_ci	.driver = {
4628c2ecf20Sopenharmony_ci		.name = SI_DEVICE_NAME,
4638c2ecf20Sopenharmony_ci		.of_match_table = of_ipmi_match,
4648c2ecf20Sopenharmony_ci		.acpi_match_table = ACPI_PTR(acpi_ipmi_match),
4658c2ecf20Sopenharmony_ci	},
4668c2ecf20Sopenharmony_ci	.probe		= ipmi_probe,
4678c2ecf20Sopenharmony_ci	.remove		= ipmi_remove,
4688c2ecf20Sopenharmony_ci	.id_table       = si_plat_ids
4698c2ecf20Sopenharmony_ci};
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_civoid ipmi_si_platform_init(void)
4728c2ecf20Sopenharmony_ci{
4738c2ecf20Sopenharmony_ci	int rv = platform_driver_register(&ipmi_platform_driver);
4748c2ecf20Sopenharmony_ci	if (rv)
4758c2ecf20Sopenharmony_ci		pr_err("Unable to register driver: %d\n", rv);
4768c2ecf20Sopenharmony_ci	else
4778c2ecf20Sopenharmony_ci		platform_registered = true;
4788c2ecf20Sopenharmony_ci}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_civoid ipmi_si_platform_shutdown(void)
4818c2ecf20Sopenharmony_ci{
4828c2ecf20Sopenharmony_ci	if (platform_registered)
4838c2ecf20Sopenharmony_ci		platform_driver_unregister(&ipmi_platform_driver);
4848c2ecf20Sopenharmony_ci}
485