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