xref: /OK3568_Linux_fs/kernel/drivers/char/ipmi/ipmi_si_hotmod.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * ipmi_si_hotmod.c
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Handling for dynamically adding/removing IPMI devices through
6*4882a593Smuzhiyun  * a module parameter (and thus sysfs).
7*4882a593Smuzhiyun  */
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun #define pr_fmt(fmt) "ipmi_hotmod: " fmt
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun #include <linux/moduleparam.h>
12*4882a593Smuzhiyun #include <linux/ipmi.h>
13*4882a593Smuzhiyun #include <linux/atomic.h>
14*4882a593Smuzhiyun #include "ipmi_si.h"
15*4882a593Smuzhiyun #include "ipmi_plat_data.h"
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun static int hotmod_handler(const char *val, const struct kernel_param *kp);
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200);
20*4882a593Smuzhiyun MODULE_PARM_DESC(hotmod, "Add and remove interfaces.  See"
21*4882a593Smuzhiyun 		 " Documentation/driver-api/ipmi.rst in the kernel sources for the"
22*4882a593Smuzhiyun 		 " gory details.");
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun /*
25*4882a593Smuzhiyun  * Parms come in as <op1>[:op2[:op3...]].  ops are:
26*4882a593Smuzhiyun  *   add|remove,kcs|bt|smic,mem|i/o,<address>[,<opt1>[,<opt2>[,...]]]
27*4882a593Smuzhiyun  * Options are:
28*4882a593Smuzhiyun  *   rsp=<regspacing>
29*4882a593Smuzhiyun  *   rsi=<regsize>
30*4882a593Smuzhiyun  *   rsh=<regshift>
31*4882a593Smuzhiyun  *   irq=<irq>
32*4882a593Smuzhiyun  *   ipmb=<ipmb addr>
33*4882a593Smuzhiyun  */
34*4882a593Smuzhiyun enum hotmod_op { HM_ADD, HM_REMOVE };
35*4882a593Smuzhiyun struct hotmod_vals {
36*4882a593Smuzhiyun 	const char *name;
37*4882a593Smuzhiyun 	const int  val;
38*4882a593Smuzhiyun };
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun static const struct hotmod_vals hotmod_ops[] = {
41*4882a593Smuzhiyun 	{ "add",	HM_ADD },
42*4882a593Smuzhiyun 	{ "remove",	HM_REMOVE },
43*4882a593Smuzhiyun 	{ NULL }
44*4882a593Smuzhiyun };
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun static const struct hotmod_vals hotmod_si[] = {
47*4882a593Smuzhiyun 	{ "kcs",	SI_KCS },
48*4882a593Smuzhiyun 	{ "smic",	SI_SMIC },
49*4882a593Smuzhiyun 	{ "bt",		SI_BT },
50*4882a593Smuzhiyun 	{ NULL }
51*4882a593Smuzhiyun };
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun static const struct hotmod_vals hotmod_as[] = {
54*4882a593Smuzhiyun 	{ "mem",	IPMI_MEM_ADDR_SPACE },
55*4882a593Smuzhiyun 	{ "i/o",	IPMI_IO_ADDR_SPACE },
56*4882a593Smuzhiyun 	{ NULL }
57*4882a593Smuzhiyun };
58*4882a593Smuzhiyun 
parse_str(const struct hotmod_vals * v,unsigned int * val,char * name,const char ** curr)59*4882a593Smuzhiyun static int parse_str(const struct hotmod_vals *v, unsigned int *val, char *name,
60*4882a593Smuzhiyun 		     const char **curr)
61*4882a593Smuzhiyun {
62*4882a593Smuzhiyun 	char *s;
63*4882a593Smuzhiyun 	int  i;
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun 	s = strchr(*curr, ',');
66*4882a593Smuzhiyun 	if (!s) {
67*4882a593Smuzhiyun 		pr_warn("No hotmod %s given\n", name);
68*4882a593Smuzhiyun 		return -EINVAL;
69*4882a593Smuzhiyun 	}
70*4882a593Smuzhiyun 	*s = '\0';
71*4882a593Smuzhiyun 	s++;
72*4882a593Smuzhiyun 	for (i = 0; v[i].name; i++) {
73*4882a593Smuzhiyun 		if (strcmp(*curr, v[i].name) == 0) {
74*4882a593Smuzhiyun 			*val = v[i].val;
75*4882a593Smuzhiyun 			*curr = s;
76*4882a593Smuzhiyun 			return 0;
77*4882a593Smuzhiyun 		}
78*4882a593Smuzhiyun 	}
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun 	pr_warn("Invalid hotmod %s '%s'\n", name, *curr);
81*4882a593Smuzhiyun 	return -EINVAL;
82*4882a593Smuzhiyun }
83*4882a593Smuzhiyun 
check_hotmod_int_op(const char * curr,const char * option,const char * name,unsigned int * val)84*4882a593Smuzhiyun static int check_hotmod_int_op(const char *curr, const char *option,
85*4882a593Smuzhiyun 			       const char *name, unsigned int *val)
86*4882a593Smuzhiyun {
87*4882a593Smuzhiyun 	char *n;
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	if (strcmp(curr, name) == 0) {
90*4882a593Smuzhiyun 		if (!option) {
91*4882a593Smuzhiyun 			pr_warn("No option given for '%s'\n", curr);
92*4882a593Smuzhiyun 			return -EINVAL;
93*4882a593Smuzhiyun 		}
94*4882a593Smuzhiyun 		*val = simple_strtoul(option, &n, 0);
95*4882a593Smuzhiyun 		if ((*n != '\0') || (*option == '\0')) {
96*4882a593Smuzhiyun 			pr_warn("Bad option given for '%s'\n", curr);
97*4882a593Smuzhiyun 			return -EINVAL;
98*4882a593Smuzhiyun 		}
99*4882a593Smuzhiyun 		return 1;
100*4882a593Smuzhiyun 	}
101*4882a593Smuzhiyun 	return 0;
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun 
parse_hotmod_str(const char * curr,enum hotmod_op * op,struct ipmi_plat_data * h)104*4882a593Smuzhiyun static int parse_hotmod_str(const char *curr, enum hotmod_op *op,
105*4882a593Smuzhiyun 			    struct ipmi_plat_data *h)
106*4882a593Smuzhiyun {
107*4882a593Smuzhiyun 	char *s, *o;
108*4882a593Smuzhiyun 	int rv;
109*4882a593Smuzhiyun 	unsigned int ival;
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun 	h->iftype = IPMI_PLAT_IF_SI;
112*4882a593Smuzhiyun 	rv = parse_str(hotmod_ops, &ival, "operation", &curr);
113*4882a593Smuzhiyun 	if (rv)
114*4882a593Smuzhiyun 		return rv;
115*4882a593Smuzhiyun 	*op = ival;
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 	rv = parse_str(hotmod_si, &ival, "interface type", &curr);
118*4882a593Smuzhiyun 	if (rv)
119*4882a593Smuzhiyun 		return rv;
120*4882a593Smuzhiyun 	h->type = ival;
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 	rv = parse_str(hotmod_as, &ival, "address space", &curr);
123*4882a593Smuzhiyun 	if (rv)
124*4882a593Smuzhiyun 		return rv;
125*4882a593Smuzhiyun 	h->space = ival;
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun 	s = strchr(curr, ',');
128*4882a593Smuzhiyun 	if (s) {
129*4882a593Smuzhiyun 		*s = '\0';
130*4882a593Smuzhiyun 		s++;
131*4882a593Smuzhiyun 	}
132*4882a593Smuzhiyun 	rv = kstrtoul(curr, 0, &h->addr);
133*4882a593Smuzhiyun 	if (rv) {
134*4882a593Smuzhiyun 		pr_warn("Invalid hotmod address '%s': %d\n", curr, rv);
135*4882a593Smuzhiyun 		return rv;
136*4882a593Smuzhiyun 	}
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun 	while (s) {
139*4882a593Smuzhiyun 		curr = s;
140*4882a593Smuzhiyun 		s = strchr(curr, ',');
141*4882a593Smuzhiyun 		if (s) {
142*4882a593Smuzhiyun 			*s = '\0';
143*4882a593Smuzhiyun 			s++;
144*4882a593Smuzhiyun 		}
145*4882a593Smuzhiyun 		o = strchr(curr, '=');
146*4882a593Smuzhiyun 		if (o) {
147*4882a593Smuzhiyun 			*o = '\0';
148*4882a593Smuzhiyun 			o++;
149*4882a593Smuzhiyun 		}
150*4882a593Smuzhiyun 		rv = check_hotmod_int_op(curr, o, "rsp", &h->regspacing);
151*4882a593Smuzhiyun 		if (rv < 0)
152*4882a593Smuzhiyun 			return rv;
153*4882a593Smuzhiyun 		else if (rv)
154*4882a593Smuzhiyun 			continue;
155*4882a593Smuzhiyun 		rv = check_hotmod_int_op(curr, o, "rsi", &h->regsize);
156*4882a593Smuzhiyun 		if (rv < 0)
157*4882a593Smuzhiyun 			return rv;
158*4882a593Smuzhiyun 		else if (rv)
159*4882a593Smuzhiyun 			continue;
160*4882a593Smuzhiyun 		rv = check_hotmod_int_op(curr, o, "rsh", &h->regshift);
161*4882a593Smuzhiyun 		if (rv < 0)
162*4882a593Smuzhiyun 			return rv;
163*4882a593Smuzhiyun 		else if (rv)
164*4882a593Smuzhiyun 			continue;
165*4882a593Smuzhiyun 		rv = check_hotmod_int_op(curr, o, "irq", &h->irq);
166*4882a593Smuzhiyun 		if (rv < 0)
167*4882a593Smuzhiyun 			return rv;
168*4882a593Smuzhiyun 		else if (rv)
169*4882a593Smuzhiyun 			continue;
170*4882a593Smuzhiyun 		rv = check_hotmod_int_op(curr, o, "ipmb", &h->slave_addr);
171*4882a593Smuzhiyun 		if (rv < 0)
172*4882a593Smuzhiyun 			return rv;
173*4882a593Smuzhiyun 		else if (rv)
174*4882a593Smuzhiyun 			continue;
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun 		pr_warn("Invalid hotmod option '%s'\n", curr);
177*4882a593Smuzhiyun 		return -EINVAL;
178*4882a593Smuzhiyun 	}
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun 	h->addr_source = SI_HOTMOD;
181*4882a593Smuzhiyun 	return 0;
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun static atomic_t hotmod_nr;
185*4882a593Smuzhiyun 
hotmod_handler(const char * val,const struct kernel_param * kp)186*4882a593Smuzhiyun static int hotmod_handler(const char *val, const struct kernel_param *kp)
187*4882a593Smuzhiyun {
188*4882a593Smuzhiyun 	char *str = kstrdup(val, GFP_KERNEL), *curr, *next;
189*4882a593Smuzhiyun 	int  rv;
190*4882a593Smuzhiyun 	struct ipmi_plat_data h;
191*4882a593Smuzhiyun 	unsigned int len;
192*4882a593Smuzhiyun 	int ival;
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 	if (!str)
195*4882a593Smuzhiyun 		return -ENOMEM;
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun 	/* Kill any trailing spaces, as we can get a "\n" from echo. */
198*4882a593Smuzhiyun 	len = strlen(str);
199*4882a593Smuzhiyun 	ival = len - 1;
200*4882a593Smuzhiyun 	while ((ival >= 0) && isspace(str[ival])) {
201*4882a593Smuzhiyun 		str[ival] = '\0';
202*4882a593Smuzhiyun 		ival--;
203*4882a593Smuzhiyun 	}
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	for (curr = str; curr; curr = next) {
206*4882a593Smuzhiyun 		enum hotmod_op op;
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun 		next = strchr(curr, ':');
209*4882a593Smuzhiyun 		if (next) {
210*4882a593Smuzhiyun 			*next = '\0';
211*4882a593Smuzhiyun 			next++;
212*4882a593Smuzhiyun 		}
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 		memset(&h, 0, sizeof(h));
215*4882a593Smuzhiyun 		rv = parse_hotmod_str(curr, &op, &h);
216*4882a593Smuzhiyun 		if (rv)
217*4882a593Smuzhiyun 			goto out;
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 		if (op == HM_ADD) {
220*4882a593Smuzhiyun 			ipmi_platform_add("hotmod-ipmi-si",
221*4882a593Smuzhiyun 					  atomic_inc_return(&hotmod_nr),
222*4882a593Smuzhiyun 					  &h);
223*4882a593Smuzhiyun 		} else {
224*4882a593Smuzhiyun 			struct device *dev;
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 			dev = ipmi_si_remove_by_data(h.space, h.type, h.addr);
227*4882a593Smuzhiyun 			if (dev && dev_is_platform(dev)) {
228*4882a593Smuzhiyun 				struct platform_device *pdev;
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 				pdev = to_platform_device(dev);
231*4882a593Smuzhiyun 				if (strcmp(pdev->name, "hotmod-ipmi-si") == 0)
232*4882a593Smuzhiyun 					platform_device_unregister(pdev);
233*4882a593Smuzhiyun 			}
234*4882a593Smuzhiyun 			if (dev)
235*4882a593Smuzhiyun 				put_device(dev);
236*4882a593Smuzhiyun 		}
237*4882a593Smuzhiyun 	}
238*4882a593Smuzhiyun 	rv = len;
239*4882a593Smuzhiyun out:
240*4882a593Smuzhiyun 	kfree(str);
241*4882a593Smuzhiyun 	return rv;
242*4882a593Smuzhiyun }
243*4882a593Smuzhiyun 
ipmi_si_hotmod_exit(void)244*4882a593Smuzhiyun void ipmi_si_hotmod_exit(void)
245*4882a593Smuzhiyun {
246*4882a593Smuzhiyun 	ipmi_remove_platform_device_by_name("hotmod-ipmi-si");
247*4882a593Smuzhiyun }
248