1 // SPDX-License-Identifier: GPL-2.0+
5 * Handling for dynamically adding/removing IPMI devices through
6 * a module parameter (and thus sysfs).
9 #define pr_fmt(fmt) "ipmi_hotmod: " fmt
11 #include <linux/moduleparam.h>
12 #include <linux/ipmi.h>
13 #include <linux/atomic.h>
15 #include "ipmi_plat_data.h"
17 static int hotmod_handler(const char *val
, const struct kernel_param
*kp
);
19 module_param_call(hotmod
, hotmod_handler
, NULL
, NULL
, 0200);
20 MODULE_PARM_DESC(hotmod
, "Add and remove interfaces. See"
21 " Documentation/driver-api/ipmi.rst in the kernel sources for the"
25 * Parms come in as <op1>[:op2[:op3...]]. ops are:
26 * add|remove,kcs|bt|smic,mem|i/o,<address>[,<opt1>[,<opt2>[,...]]]
34 enum hotmod_op
{ HM_ADD
, HM_REMOVE
};
40 static const struct hotmod_vals hotmod_ops
[] = {
42 { "remove", HM_REMOVE
},
46 static const struct hotmod_vals hotmod_si
[] = {
53 static const struct hotmod_vals hotmod_as
[] = {
54 { "mem", IPMI_MEM_ADDR_SPACE
},
55 { "i/o", IPMI_IO_ADDR_SPACE
},
59 static int parse_str(const struct hotmod_vals
*v
, unsigned int *val
, char *name
,
65 s
= strchr(*curr
, ',');
67 pr_warn("No hotmod %s given\n", name
);
72 for (i
= 0; v
[i
].name
; i
++) {
73 if (strcmp(*curr
, v
[i
].name
) == 0) {
80 pr_warn("Invalid hotmod %s '%s'\n", name
, *curr
);
84 static int check_hotmod_int_op(const char *curr
, const char *option
,
85 const char *name
, unsigned int *val
)
89 if (strcmp(curr
, name
) == 0) {
91 pr_warn("No option given for '%s'\n", curr
);
94 *val
= simple_strtoul(option
, &n
, 0);
95 if ((*n
!= '\0') || (*option
== '\0')) {
96 pr_warn("Bad option given for '%s'\n", curr
);
104 static int parse_hotmod_str(const char *curr
, enum hotmod_op
*op
,
105 struct ipmi_plat_data
*h
)
111 h
->iftype
= IPMI_PLAT_IF_SI
;
112 rv
= parse_str(hotmod_ops
, &ival
, "operation", &curr
);
117 rv
= parse_str(hotmod_si
, &ival
, "interface type", &curr
);
122 rv
= parse_str(hotmod_as
, &ival
, "address space", &curr
);
127 s
= strchr(curr
, ',');
132 rv
= kstrtoul(curr
, 0, &h
->addr
);
134 pr_warn("Invalid hotmod address '%s': %d\n", curr
, rv
);
140 s
= strchr(curr
, ',');
145 o
= strchr(curr
, '=');
150 rv
= check_hotmod_int_op(curr
, o
, "rsp", &h
->regspacing
);
155 rv
= check_hotmod_int_op(curr
, o
, "rsi", &h
->regsize
);
160 rv
= check_hotmod_int_op(curr
, o
, "rsh", &h
->regshift
);
165 rv
= check_hotmod_int_op(curr
, o
, "irq", &h
->irq
);
170 rv
= check_hotmod_int_op(curr
, o
, "ipmb", &h
->slave_addr
);
176 pr_warn("Invalid hotmod option '%s'\n", curr
);
180 h
->addr_source
= SI_HOTMOD
;
184 static atomic_t hotmod_nr
;
186 static int hotmod_handler(const char *val
, const struct kernel_param
*kp
)
188 char *str
= kstrdup(val
, GFP_KERNEL
), *curr
, *next
;
190 struct ipmi_plat_data h
;
197 /* Kill any trailing spaces, as we can get a "\n" from echo. */
200 while ((ival
>= 0) && isspace(str
[ival
])) {
205 for (curr
= str
; curr
; curr
= next
) {
208 next
= strchr(curr
, ':');
214 memset(&h
, 0, sizeof(h
));
215 rv
= parse_hotmod_str(curr
, &op
, &h
);
220 ipmi_platform_add("hotmod-ipmi-si",
221 atomic_inc_return(&hotmod_nr
),
226 dev
= ipmi_si_remove_by_data(h
.space
, h
.type
, h
.addr
);
227 if (dev
&& dev_is_platform(dev
)) {
228 struct platform_device
*pdev
;
230 pdev
= to_platform_device(dev
);
231 if (strcmp(pdev
->name
, "hotmod-ipmi-si") == 0)
232 platform_device_unregister(pdev
);
244 void ipmi_si_hotmod_exit(void)
246 ipmi_remove_platform_device_by_name("hotmod-ipmi-si");