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", ®size); 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", ®shift); 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