1243ac210SCorey Minyard // SPDX-License-Identifier: GPL-2.0+ 244814ec9SCorey Minyard /* 344814ec9SCorey Minyard * ipmi_si_hotmod.c 444814ec9SCorey Minyard * 544814ec9SCorey Minyard * Handling for dynamically adding/removing IPMI devices through 644814ec9SCorey Minyard * a module parameter (and thus sysfs). 744814ec9SCorey Minyard */ 825880f7dSJoe Perches 925880f7dSJoe Perches #define pr_fmt(fmt) "ipmi_hotmod: " fmt 1025880f7dSJoe Perches 1144814ec9SCorey Minyard #include <linux/moduleparam.h> 1244814ec9SCorey Minyard #include <linux/ipmi.h> 133bb8ea40SCorey Minyard #include <linux/atomic.h> 1444814ec9SCorey Minyard #include "ipmi_si.h" 153bb8ea40SCorey Minyard #include "ipmi_plat_data.h" 1644814ec9SCorey Minyard 176297fabdSCorey Minyard static int hotmod_handler(const char *val, const struct kernel_param *kp); 1844814ec9SCorey Minyard 1944814ec9SCorey Minyard module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200); 2044814ec9SCorey Minyard MODULE_PARM_DESC(hotmod, "Add and remove interfaces. See" 2144814ec9SCorey Minyard " Documentation/IPMI.txt in the kernel sources for the" 2244814ec9SCorey Minyard " gory details."); 2344814ec9SCorey Minyard 2444814ec9SCorey Minyard /* 2544814ec9SCorey Minyard * Parms come in as <op1>[:op2[:op3...]]. ops are: 2644814ec9SCorey Minyard * add|remove,kcs|bt|smic,mem|i/o,<address>[,<opt1>[,<opt2>[,...]]] 2744814ec9SCorey Minyard * Options are: 2844814ec9SCorey Minyard * rsp=<regspacing> 2944814ec9SCorey Minyard * rsi=<regsize> 3044814ec9SCorey Minyard * rsh=<regshift> 3144814ec9SCorey Minyard * irq=<irq> 3244814ec9SCorey Minyard * ipmb=<ipmb addr> 3344814ec9SCorey Minyard */ 3444814ec9SCorey Minyard enum hotmod_op { HM_ADD, HM_REMOVE }; 3544814ec9SCorey Minyard struct hotmod_vals { 3644814ec9SCorey Minyard const char *name; 3744814ec9SCorey Minyard const int val; 3844814ec9SCorey Minyard }; 3944814ec9SCorey Minyard 4044814ec9SCorey Minyard static const struct hotmod_vals hotmod_ops[] = { 4144814ec9SCorey Minyard { "add", HM_ADD }, 4244814ec9SCorey Minyard { "remove", HM_REMOVE }, 4344814ec9SCorey Minyard { NULL } 4444814ec9SCorey Minyard }; 4544814ec9SCorey Minyard 4644814ec9SCorey Minyard static const struct hotmod_vals hotmod_si[] = { 4744814ec9SCorey Minyard { "kcs", SI_KCS }, 4844814ec9SCorey Minyard { "smic", SI_SMIC }, 4944814ec9SCorey Minyard { "bt", SI_BT }, 5044814ec9SCorey Minyard { NULL } 5144814ec9SCorey Minyard }; 5244814ec9SCorey Minyard 5344814ec9SCorey Minyard static const struct hotmod_vals hotmod_as[] = { 5444814ec9SCorey Minyard { "mem", IPMI_MEM_ADDR_SPACE }, 5544814ec9SCorey Minyard { "i/o", IPMI_IO_ADDR_SPACE }, 5644814ec9SCorey Minyard { NULL } 5744814ec9SCorey Minyard }; 5844814ec9SCorey Minyard 593bb8ea40SCorey Minyard static int parse_str(const struct hotmod_vals *v, unsigned int *val, char *name, 603bb8ea40SCorey Minyard const char **curr) 6144814ec9SCorey Minyard { 6244814ec9SCorey Minyard char *s; 6344814ec9SCorey Minyard int i; 6444814ec9SCorey Minyard 6544814ec9SCorey Minyard s = strchr(*curr, ','); 6644814ec9SCorey Minyard if (!s) { 6725880f7dSJoe Perches pr_warn("No hotmod %s given\n", name); 6844814ec9SCorey Minyard return -EINVAL; 6944814ec9SCorey Minyard } 7044814ec9SCorey Minyard *s = '\0'; 7144814ec9SCorey Minyard s++; 7244814ec9SCorey Minyard for (i = 0; v[i].name; i++) { 7344814ec9SCorey Minyard if (strcmp(*curr, v[i].name) == 0) { 7444814ec9SCorey Minyard *val = v[i].val; 7544814ec9SCorey Minyard *curr = s; 7644814ec9SCorey Minyard return 0; 7744814ec9SCorey Minyard } 7844814ec9SCorey Minyard } 7944814ec9SCorey Minyard 8025880f7dSJoe Perches pr_warn("Invalid hotmod %s '%s'\n", name, *curr); 8144814ec9SCorey Minyard return -EINVAL; 8244814ec9SCorey Minyard } 8344814ec9SCorey Minyard 8444814ec9SCorey Minyard static int check_hotmod_int_op(const char *curr, const char *option, 853bb8ea40SCorey Minyard const char *name, unsigned int *val) 8644814ec9SCorey Minyard { 8744814ec9SCorey Minyard char *n; 8844814ec9SCorey Minyard 8944814ec9SCorey Minyard if (strcmp(curr, name) == 0) { 9044814ec9SCorey Minyard if (!option) { 9125880f7dSJoe Perches pr_warn("No option given for '%s'\n", curr); 9244814ec9SCorey Minyard return -EINVAL; 9344814ec9SCorey Minyard } 9444814ec9SCorey Minyard *val = simple_strtoul(option, &n, 0); 9544814ec9SCorey Minyard if ((*n != '\0') || (*option == '\0')) { 9625880f7dSJoe Perches pr_warn("Bad option given for '%s'\n", curr); 9744814ec9SCorey Minyard return -EINVAL; 9844814ec9SCorey Minyard } 9944814ec9SCorey Minyard return 1; 10044814ec9SCorey Minyard } 10144814ec9SCorey Minyard return 0; 10244814ec9SCorey Minyard } 10344814ec9SCorey Minyard 1043bb8ea40SCorey Minyard static int parse_hotmod_str(const char *curr, enum hotmod_op *op, 1053bb8ea40SCorey Minyard struct ipmi_plat_data *h) 10644814ec9SCorey Minyard { 1073bb8ea40SCorey Minyard char *s, *o; 10844814ec9SCorey Minyard int rv; 1093bb8ea40SCorey Minyard unsigned int ival; 11044814ec9SCorey Minyard 11144814ec9SCorey Minyard rv = parse_str(hotmod_ops, &ival, "operation", &curr); 11244814ec9SCorey Minyard if (rv) 1133bb8ea40SCorey Minyard return rv; 1143bb8ea40SCorey Minyard *op = ival; 11544814ec9SCorey Minyard 11644814ec9SCorey Minyard rv = parse_str(hotmod_si, &ival, "interface type", &curr); 11744814ec9SCorey Minyard if (rv) 1183bb8ea40SCorey Minyard return rv; 1193bb8ea40SCorey Minyard h->type = ival; 12044814ec9SCorey Minyard 121f6296bdcSCorey Minyard rv = parse_str(hotmod_as, &ival, "address space", &curr); 12244814ec9SCorey Minyard if (rv) 1233bb8ea40SCorey Minyard return rv; 1243bb8ea40SCorey Minyard h->space = ival; 12544814ec9SCorey Minyard 12644814ec9SCorey Minyard s = strchr(curr, ','); 12744814ec9SCorey Minyard if (s) { 12844814ec9SCorey Minyard *s = '\0'; 12944814ec9SCorey Minyard s++; 13044814ec9SCorey Minyard } 1313bb8ea40SCorey Minyard rv = kstrtoul(curr, 0, &h->addr); 1323bb8ea40SCorey Minyard if (rv) { 1333bb8ea40SCorey Minyard pr_warn("Invalid hotmod address '%s': %d\n", curr, rv); 1343bb8ea40SCorey Minyard return rv; 13544814ec9SCorey Minyard } 13644814ec9SCorey Minyard 13744814ec9SCorey Minyard while (s) { 13844814ec9SCorey Minyard curr = s; 13944814ec9SCorey Minyard s = strchr(curr, ','); 14044814ec9SCorey Minyard if (s) { 14144814ec9SCorey Minyard *s = '\0'; 14244814ec9SCorey Minyard s++; 14344814ec9SCorey Minyard } 14444814ec9SCorey Minyard o = strchr(curr, '='); 14544814ec9SCorey Minyard if (o) { 14644814ec9SCorey Minyard *o = '\0'; 14744814ec9SCorey Minyard o++; 14844814ec9SCorey Minyard } 1493bb8ea40SCorey Minyard rv = check_hotmod_int_op(curr, o, "rsp", &h->regspacing); 15044814ec9SCorey Minyard if (rv < 0) 1513bb8ea40SCorey Minyard return rv; 15244814ec9SCorey Minyard else if (rv) 15344814ec9SCorey Minyard continue; 1543bb8ea40SCorey Minyard rv = check_hotmod_int_op(curr, o, "rsi", &h->regsize); 15544814ec9SCorey Minyard if (rv < 0) 1563bb8ea40SCorey Minyard return rv; 15744814ec9SCorey Minyard else if (rv) 15844814ec9SCorey Minyard continue; 1593bb8ea40SCorey Minyard rv = check_hotmod_int_op(curr, o, "rsh", &h->regshift); 16044814ec9SCorey Minyard if (rv < 0) 1613bb8ea40SCorey Minyard return rv; 16244814ec9SCorey Minyard else if (rv) 16344814ec9SCorey Minyard continue; 1643bb8ea40SCorey Minyard rv = check_hotmod_int_op(curr, o, "irq", &h->irq); 16544814ec9SCorey Minyard if (rv < 0) 1663bb8ea40SCorey Minyard return rv; 16744814ec9SCorey Minyard else if (rv) 16844814ec9SCorey Minyard continue; 1693bb8ea40SCorey Minyard rv = check_hotmod_int_op(curr, o, "ipmb", &h->slave_addr); 17044814ec9SCorey Minyard if (rv < 0) 1713bb8ea40SCorey Minyard return rv; 17244814ec9SCorey Minyard else if (rv) 17344814ec9SCorey Minyard continue; 17444814ec9SCorey Minyard 17525880f7dSJoe Perches pr_warn("Invalid hotmod option '%s'\n", curr); 1763bb8ea40SCorey Minyard return -EINVAL; 17744814ec9SCorey Minyard } 17844814ec9SCorey Minyard 1793bb8ea40SCorey Minyard h->addr_source = SI_HOTMOD; 1803bb8ea40SCorey Minyard return 0; 1813bb8ea40SCorey Minyard } 18244814ec9SCorey Minyard 1833bb8ea40SCorey Minyard static atomic_t hotmod_nr; 18444814ec9SCorey Minyard 1853bb8ea40SCorey Minyard static int hotmod_handler(const char *val, const struct kernel_param *kp) 1863bb8ea40SCorey Minyard { 1873bb8ea40SCorey Minyard char *str = kstrdup(val, GFP_KERNEL), *curr, *next; 1883bb8ea40SCorey Minyard int rv; 1893bb8ea40SCorey Minyard struct ipmi_plat_data h; 1903bb8ea40SCorey Minyard unsigned int len, ival; 19144814ec9SCorey Minyard 1923bb8ea40SCorey Minyard if (!str) 1933bb8ea40SCorey Minyard return -ENOMEM; 1943bb8ea40SCorey Minyard 1953bb8ea40SCorey Minyard /* Kill any trailing spaces, as we can get a "\n" from echo. */ 1963bb8ea40SCorey Minyard len = strlen(str); 1973bb8ea40SCorey Minyard ival = len - 1; 1983bb8ea40SCorey Minyard while ((ival >= 0) && isspace(str[ival])) { 1993bb8ea40SCorey Minyard str[ival] = '\0'; 2003bb8ea40SCorey Minyard ival--; 2013bb8ea40SCorey Minyard } 2023bb8ea40SCorey Minyard 2033bb8ea40SCorey Minyard for (curr = str; curr; curr = next) { 2043bb8ea40SCorey Minyard enum hotmod_op op; 2053bb8ea40SCorey Minyard 2063bb8ea40SCorey Minyard next = strchr(curr, ':'); 2073bb8ea40SCorey Minyard if (next) { 2083bb8ea40SCorey Minyard *next = '\0'; 2093bb8ea40SCorey Minyard next++; 2103bb8ea40SCorey Minyard } 2113bb8ea40SCorey Minyard 2123bb8ea40SCorey Minyard memset(&h, 0, sizeof(h)); 2133bb8ea40SCorey Minyard rv = parse_hotmod_str(curr, &op, &h); 21444814ec9SCorey Minyard if (rv) 21544814ec9SCorey Minyard goto out; 2163bb8ea40SCorey Minyard 2173bb8ea40SCorey Minyard if (op == HM_ADD) { 2183bb8ea40SCorey Minyard ipmi_platform_add("hotmod-ipmi-si", 2193bb8ea40SCorey Minyard atomic_inc_return(&hotmod_nr), 2203bb8ea40SCorey Minyard &h); 22144814ec9SCorey Minyard } else { 222*bdb57b7bSCorey Minyard struct device *dev; 223*bdb57b7bSCorey Minyard 224*bdb57b7bSCorey Minyard dev = ipmi_si_remove_by_data(h.space, h.type, h.addr); 225*bdb57b7bSCorey Minyard if (dev && dev_is_platform(dev)) { 226*bdb57b7bSCorey Minyard struct platform_device *pdev; 227*bdb57b7bSCorey Minyard 228*bdb57b7bSCorey Minyard pdev = to_platform_device(dev); 229*bdb57b7bSCorey Minyard if (strcmp(pdev->name, "hotmod-ipmi-si") == 0) 230*bdb57b7bSCorey Minyard platform_device_unregister(pdev); 231*bdb57b7bSCorey Minyard } 232*bdb57b7bSCorey Minyard if (dev) 233*bdb57b7bSCorey Minyard put_device(dev); 23444814ec9SCorey Minyard } 23544814ec9SCorey Minyard } 23644814ec9SCorey Minyard rv = len; 23744814ec9SCorey Minyard out: 23844814ec9SCorey Minyard kfree(str); 23944814ec9SCorey Minyard return rv; 24044814ec9SCorey Minyard } 241*bdb57b7bSCorey Minyard 242*bdb57b7bSCorey Minyard static int pdev_match_name(struct device *dev, void *data) 243*bdb57b7bSCorey Minyard { 244*bdb57b7bSCorey Minyard struct platform_device *pdev = to_platform_device(dev); 245*bdb57b7bSCorey Minyard 246*bdb57b7bSCorey Minyard return strcmp(pdev->name, "hotmod-ipmi-si") == 0; 247*bdb57b7bSCorey Minyard } 248*bdb57b7bSCorey Minyard 249*bdb57b7bSCorey Minyard void ipmi_si_hotmod_exit(void) 250*bdb57b7bSCorey Minyard { 251*bdb57b7bSCorey Minyard struct device *dev; 252*bdb57b7bSCorey Minyard 253*bdb57b7bSCorey Minyard while ((dev = bus_find_device(&platform_bus_type, NULL, NULL, 254*bdb57b7bSCorey Minyard pdev_match_name))) { 255*bdb57b7bSCorey Minyard struct platform_device *pdev = to_platform_device(dev); 256*bdb57b7bSCorey Minyard 257*bdb57b7bSCorey Minyard platform_device_unregister(pdev); 258*bdb57b7bSCorey Minyard } 259*bdb57b7bSCorey Minyard } 260