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,
21 "Add and remove interfaces. See Documentation/driver-api/ipmi.rst in the kernel sources for the gory details.");
24 * Parms come in as <op1>[:op2[:op3...]]. ops are:
25 * add|remove,kcs|bt|smic,mem|i/o,<address>[,<opt1>[,<opt2>[,...]]]
33 enum hotmod_op { HM_ADD, HM_REMOVE };
39 static const struct hotmod_vals hotmod_ops[] = {
41 { "remove", HM_REMOVE },
45 static const struct hotmod_vals hotmod_si[] = {
52 static const struct hotmod_vals hotmod_as[] = {
53 { "mem", IPMI_MEM_ADDR_SPACE },
54 { "i/o", IPMI_IO_ADDR_SPACE },
58 static int parse_str(const struct hotmod_vals *v, unsigned int *val, char *name,
64 s = strchr(*curr, ',');
66 pr_warn("No hotmod %s given\n", name);
71 for (i = 0; v[i].name; i++) {
72 if (strcmp(*curr, v[i].name) == 0) {
79 pr_warn("Invalid hotmod %s '%s'\n", name, *curr);
83 static int check_hotmod_int_op(const char *curr, const char *option,
84 const char *name, unsigned int *val)
88 if (strcmp(curr, name) == 0) {
90 pr_warn("No option given for '%s'\n", curr);
93 *val = simple_strtoul(option, &n, 0);
94 if ((*n != '\0') || (*option == '\0')) {
95 pr_warn("Bad option given for '%s'\n", curr);
103 static int parse_hotmod_str(const char *curr, enum hotmod_op *op,
104 struct ipmi_plat_data *h)
110 h->iftype = IPMI_PLAT_IF_SI;
111 rv = parse_str(hotmod_ops, &ival, "operation", &curr);
116 rv = parse_str(hotmod_si, &ival, "interface type", &curr);
121 rv = parse_str(hotmod_as, &ival, "address space", &curr);
126 s = strchr(curr, ',');
131 rv = kstrtoul(curr, 0, &h->addr);
133 pr_warn("Invalid hotmod address '%s': %d\n", curr, rv);
139 s = strchr(curr, ',');
144 o = strchr(curr, '=');
149 rv = check_hotmod_int_op(curr, o, "rsp", &h->regspacing);
154 rv = check_hotmod_int_op(curr, o, "rsi", &h->regsize);
159 rv = check_hotmod_int_op(curr, o, "rsh", &h->regshift);
164 rv = check_hotmod_int_op(curr, o, "irq", &h->irq);
169 rv = check_hotmod_int_op(curr, o, "ipmb", &h->slave_addr);
175 pr_warn("Invalid hotmod option '%s'\n", curr);
179 h->addr_source = SI_HOTMOD;
183 static atomic_t hotmod_nr;
185 static int hotmod_handler(const char *val, const struct kernel_param *kp)
188 struct ipmi_plat_data h;
189 char *str, *curr, *next;
191 str = kstrdup(val, GFP_KERNEL);
195 /* Kill any trailing spaces, as we can get a "\n" from echo. */
196 for (curr = strstrip(str); curr; curr = next) {
199 next = strchr(curr, ':');
205 memset(&h, 0, sizeof(h));
206 rv = parse_hotmod_str(curr, &op, &h);
211 ipmi_platform_add("hotmod-ipmi-si",
212 atomic_inc_return(&hotmod_nr),
217 dev = ipmi_si_remove_by_data(h.space, h.type, h.addr);
218 if (dev && dev_is_platform(dev)) {
219 struct platform_device *pdev;
221 pdev = to_platform_device(dev);
222 if (strcmp(pdev->name, "hotmod-ipmi-si") == 0)
223 platform_device_unregister(pdev);
234 void ipmi_si_hotmod_exit(void)
236 ipmi_remove_platform_device_by_name("hotmod-ipmi-si");