xref: /linux/drivers/char/ipmi/ipmi_si_hotmod.c (revision 8be98d2f2a0a262f8bf8a0bc1fdf522b3c7aab17)
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);
20*07cbd87bSAndy Shevchenko MODULE_PARM_DESC(hotmod,
21*07cbd87bSAndy Shevchenko 		 "Add and remove interfaces.  See Documentation/driver-api/ipmi.rst in the kernel sources for the gory details.");
2244814ec9SCorey Minyard 
2344814ec9SCorey Minyard /*
2444814ec9SCorey Minyard  * Parms come in as <op1>[:op2[:op3...]].  ops are:
2544814ec9SCorey Minyard  *   add|remove,kcs|bt|smic,mem|i/o,<address>[,<opt1>[,<opt2>[,...]]]
2644814ec9SCorey Minyard  * Options are:
2744814ec9SCorey Minyard  *   rsp=<regspacing>
2844814ec9SCorey Minyard  *   rsi=<regsize>
2944814ec9SCorey Minyard  *   rsh=<regshift>
3044814ec9SCorey Minyard  *   irq=<irq>
3144814ec9SCorey Minyard  *   ipmb=<ipmb addr>
3244814ec9SCorey Minyard  */
3344814ec9SCorey Minyard enum hotmod_op { HM_ADD, HM_REMOVE };
3444814ec9SCorey Minyard struct hotmod_vals {
3544814ec9SCorey Minyard 	const char *name;
3644814ec9SCorey Minyard 	const int  val;
3744814ec9SCorey Minyard };
3844814ec9SCorey Minyard 
3944814ec9SCorey Minyard static const struct hotmod_vals hotmod_ops[] = {
4044814ec9SCorey Minyard 	{ "add",	HM_ADD },
4144814ec9SCorey Minyard 	{ "remove",	HM_REMOVE },
4244814ec9SCorey Minyard 	{ NULL }
4344814ec9SCorey Minyard };
4444814ec9SCorey Minyard 
4544814ec9SCorey Minyard static const struct hotmod_vals hotmod_si[] = {
4644814ec9SCorey Minyard 	{ "kcs",	SI_KCS },
4744814ec9SCorey Minyard 	{ "smic",	SI_SMIC },
4844814ec9SCorey Minyard 	{ "bt",		SI_BT },
4944814ec9SCorey Minyard 	{ NULL }
5044814ec9SCorey Minyard };
5144814ec9SCorey Minyard 
5244814ec9SCorey Minyard static const struct hotmod_vals hotmod_as[] = {
5344814ec9SCorey Minyard 	{ "mem",	IPMI_MEM_ADDR_SPACE },
5444814ec9SCorey Minyard 	{ "i/o",	IPMI_IO_ADDR_SPACE },
5544814ec9SCorey Minyard 	{ NULL }
5644814ec9SCorey Minyard };
5744814ec9SCorey Minyard 
parse_str(const struct hotmod_vals * v,unsigned int * val,char * name,const char ** curr)583bb8ea40SCorey Minyard static int parse_str(const struct hotmod_vals *v, unsigned int *val, char *name,
593bb8ea40SCorey Minyard 		     const char **curr)
6044814ec9SCorey Minyard {
6144814ec9SCorey Minyard 	char *s;
6244814ec9SCorey Minyard 	int  i;
6344814ec9SCorey Minyard 
6444814ec9SCorey Minyard 	s = strchr(*curr, ',');
6544814ec9SCorey Minyard 	if (!s) {
6625880f7dSJoe Perches 		pr_warn("No hotmod %s given\n", name);
6744814ec9SCorey Minyard 		return -EINVAL;
6844814ec9SCorey Minyard 	}
6944814ec9SCorey Minyard 	*s = '\0';
7044814ec9SCorey Minyard 	s++;
7144814ec9SCorey Minyard 	for (i = 0; v[i].name; i++) {
7244814ec9SCorey Minyard 		if (strcmp(*curr, v[i].name) == 0) {
7344814ec9SCorey Minyard 			*val = v[i].val;
7444814ec9SCorey Minyard 			*curr = s;
7544814ec9SCorey Minyard 			return 0;
7644814ec9SCorey Minyard 		}
7744814ec9SCorey Minyard 	}
7844814ec9SCorey Minyard 
7925880f7dSJoe Perches 	pr_warn("Invalid hotmod %s '%s'\n", name, *curr);
8044814ec9SCorey Minyard 	return -EINVAL;
8144814ec9SCorey Minyard }
8244814ec9SCorey Minyard 
check_hotmod_int_op(const char * curr,const char * option,const char * name,unsigned int * val)8344814ec9SCorey Minyard static int check_hotmod_int_op(const char *curr, const char *option,
843bb8ea40SCorey Minyard 			       const char *name, unsigned int *val)
8544814ec9SCorey Minyard {
8644814ec9SCorey Minyard 	char *n;
8744814ec9SCorey Minyard 
8844814ec9SCorey Minyard 	if (strcmp(curr, name) == 0) {
8944814ec9SCorey Minyard 		if (!option) {
9025880f7dSJoe Perches 			pr_warn("No option given for '%s'\n", curr);
9144814ec9SCorey Minyard 			return -EINVAL;
9244814ec9SCorey Minyard 		}
9344814ec9SCorey Minyard 		*val = simple_strtoul(option, &n, 0);
9444814ec9SCorey Minyard 		if ((*n != '\0') || (*option == '\0')) {
9525880f7dSJoe Perches 			pr_warn("Bad option given for '%s'\n", curr);
9644814ec9SCorey Minyard 			return -EINVAL;
9744814ec9SCorey Minyard 		}
9844814ec9SCorey Minyard 		return 1;
9944814ec9SCorey Minyard 	}
10044814ec9SCorey Minyard 	return 0;
10144814ec9SCorey Minyard }
10244814ec9SCorey Minyard 
parse_hotmod_str(const char * curr,enum hotmod_op * op,struct ipmi_plat_data * h)1033bb8ea40SCorey Minyard static int parse_hotmod_str(const char *curr, enum hotmod_op *op,
1043bb8ea40SCorey Minyard 			    struct ipmi_plat_data *h)
10544814ec9SCorey Minyard {
1063bb8ea40SCorey Minyard 	char *s, *o;
10744814ec9SCorey Minyard 	int rv;
1083bb8ea40SCorey Minyard 	unsigned int ival;
10944814ec9SCorey Minyard 
110d7323638SCorey Minyard 	h->iftype = IPMI_PLAT_IF_SI;
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 
hotmod_handler(const char * val,const struct kernel_param * kp)1853bb8ea40SCorey Minyard static int hotmod_handler(const char *val, const struct kernel_param *kp)
1863bb8ea40SCorey Minyard {
1873bb8ea40SCorey Minyard 	int  rv;
1883bb8ea40SCorey Minyard 	struct ipmi_plat_data h;
189d14ce8c7SAndy Shevchenko 	char *str, *curr, *next;
19044814ec9SCorey Minyard 
191d14ce8c7SAndy Shevchenko 	str = kstrdup(val, GFP_KERNEL);
1923bb8ea40SCorey Minyard 	if (!str)
1933bb8ea40SCorey Minyard 		return -ENOMEM;
1943bb8ea40SCorey Minyard 
1953bb8ea40SCorey Minyard 	/* Kill any trailing spaces, as we can get a "\n" from echo. */
196d14ce8c7SAndy Shevchenko 	for (curr = strstrip(str); curr; curr = next) {
1973bb8ea40SCorey Minyard 		enum hotmod_op op;
1983bb8ea40SCorey Minyard 
1993bb8ea40SCorey Minyard 		next = strchr(curr, ':');
2003bb8ea40SCorey Minyard 		if (next) {
2013bb8ea40SCorey Minyard 			*next = '\0';
2023bb8ea40SCorey Minyard 			next++;
2033bb8ea40SCorey Minyard 		}
2043bb8ea40SCorey Minyard 
2053bb8ea40SCorey Minyard 		memset(&h, 0, sizeof(h));
2063bb8ea40SCorey Minyard 		rv = parse_hotmod_str(curr, &op, &h);
20744814ec9SCorey Minyard 		if (rv)
20844814ec9SCorey Minyard 			goto out;
2093bb8ea40SCorey Minyard 
2103bb8ea40SCorey Minyard 		if (op == HM_ADD) {
2113bb8ea40SCorey Minyard 			ipmi_platform_add("hotmod-ipmi-si",
2123bb8ea40SCorey Minyard 					  atomic_inc_return(&hotmod_nr),
2133bb8ea40SCorey Minyard 					  &h);
21444814ec9SCorey Minyard 		} else {
215bdb57b7bSCorey Minyard 			struct device *dev;
216bdb57b7bSCorey Minyard 
217bdb57b7bSCorey Minyard 			dev = ipmi_si_remove_by_data(h.space, h.type, h.addr);
218bdb57b7bSCorey Minyard 			if (dev && dev_is_platform(dev)) {
219bdb57b7bSCorey Minyard 				struct platform_device *pdev;
220bdb57b7bSCorey Minyard 
221bdb57b7bSCorey Minyard 				pdev = to_platform_device(dev);
222bdb57b7bSCorey Minyard 				if (strcmp(pdev->name, "hotmod-ipmi-si") == 0)
223bdb57b7bSCorey Minyard 					platform_device_unregister(pdev);
224bdb57b7bSCorey Minyard 			}
225bdb57b7bSCorey Minyard 			put_device(dev);
22644814ec9SCorey Minyard 		}
22744814ec9SCorey Minyard 	}
228d14ce8c7SAndy Shevchenko 	rv = strlen(val);
22944814ec9SCorey Minyard out:
23044814ec9SCorey Minyard 	kfree(str);
23144814ec9SCorey Minyard 	return rv;
23244814ec9SCorey Minyard }
233bdb57b7bSCorey Minyard 
ipmi_si_hotmod_exit(void)234bdb57b7bSCorey Minyard void ipmi_si_hotmod_exit(void)
235bdb57b7bSCorey Minyard {
236e17c6571SCorey Minyard 	ipmi_remove_platform_device_by_name("hotmod-ipmi-si");
237bdb57b7bSCorey Minyard }
238