diff options
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/ipmi/ipmi_si_hotmod.c | 208 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_si_intf.c | 2 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_si_platform.c | 1 |
3 files changed, 98 insertions, 113 deletions
diff --git a/drivers/char/ipmi/ipmi_si_hotmod.c b/drivers/char/ipmi/ipmi_si_hotmod.c index f3d455bbf3cc..f0728be00bd2 100644 --- a/drivers/char/ipmi/ipmi_si_hotmod.c +++ b/drivers/char/ipmi/ipmi_si_hotmod.c @@ -10,7 +10,9 @@ #include <linux/moduleparam.h> #include <linux/ipmi.h> +#include <linux/atomic.h> #include "ipmi_si.h" +#include "ipmi_plat_data.h" static int hotmod_handler(const char *val, const struct kernel_param *kp); @@ -54,8 +56,8 @@ static const struct hotmod_vals hotmod_as[] = { { NULL } }; -static int parse_str(const struct hotmod_vals *v, int *val, char *name, - char **curr) +static int parse_str(const struct hotmod_vals *v, unsigned int *val, char *name, + const char **curr) { char *s; int i; @@ -80,7 +82,7 @@ static int parse_str(const struct hotmod_vals *v, int *val, char *name, } static int check_hotmod_int_op(const char *curr, const char *option, - const char *name, int *val) + const char *name, unsigned int *val) { char *n; @@ -99,22 +101,93 @@ static int check_hotmod_int_op(const char *curr, const char *option, return 0; } +static int parse_hotmod_str(const char *curr, enum hotmod_op *op, + struct ipmi_plat_data *h) +{ + char *s, *o; + int rv; + unsigned int ival; + + rv = parse_str(hotmod_ops, &ival, "operation", &curr); + if (rv) + return rv; + *op = ival; + + rv = parse_str(hotmod_si, &ival, "interface type", &curr); + if (rv) + return rv; + h->type = ival; + + rv = parse_str(hotmod_as, &ival, "address space", &curr); + if (rv) + return rv; + h->space = ival; + + s = strchr(curr, ','); + if (s) { + *s = '\0'; + s++; + } + rv = kstrtoul(curr, 0, &h->addr); + if (rv) { + pr_warn("Invalid hotmod address '%s': %d\n", curr, rv); + return rv; + } + + while (s) { + curr = s; + s = strchr(curr, ','); + if (s) { + *s = '\0'; + s++; + } + o = strchr(curr, '='); + if (o) { + *o = '\0'; + o++; + } + rv = check_hotmod_int_op(curr, o, "rsp", &h->regspacing); + if (rv < 0) + return rv; + else if (rv) + continue; + rv = check_hotmod_int_op(curr, o, "rsi", &h->regsize); + if (rv < 0) + return rv; + else if (rv) + continue; + rv = check_hotmod_int_op(curr, o, "rsh", &h->regshift); + if (rv < 0) + return rv; + else if (rv) + continue; + rv = check_hotmod_int_op(curr, o, "irq", &h->irq); + if (rv < 0) + return rv; + else if (rv) + continue; + rv = check_hotmod_int_op(curr, o, "ipmb", &h->slave_addr); + if (rv < 0) + return rv; + else if (rv) + continue; + + pr_warn("Invalid hotmod option '%s'\n", curr); + return -EINVAL; + } + + h->addr_source = SI_HOTMOD; + return 0; +} + +static atomic_t hotmod_nr; + static int hotmod_handler(const char *val, const struct kernel_param *kp) { - char *str = kstrdup(val, GFP_KERNEL); + char *str = kstrdup(val, GFP_KERNEL), *curr, *next; int rv; - char *next, *curr, *s, *n, *o; - enum hotmod_op op; - enum si_type si_type; - enum ipmi_addr_space addr_space; - unsigned long addr; - int regspacing; - int regsize; - int regshift; - int irq; - int ipmb; - int ival; - int len; + struct ipmi_plat_data h; + unsigned int len, ival; if (!str) return -ENOMEM; @@ -128,11 +201,7 @@ static int hotmod_handler(const char *val, const struct kernel_param *kp) } for (curr = str; curr; curr = next) { - regspacing = 1; - regsize = 1; - regshift = 0; - irq = 0; - ipmb = 0; /* Choose the default if not specified */ + enum hotmod_op op; next = strchr(curr, ':'); if (next) { @@ -140,102 +209,17 @@ static int hotmod_handler(const char *val, const struct kernel_param *kp) next++; } - rv = parse_str(hotmod_ops, &ival, "operation", &curr); - if (rv) - break; - op = ival; - - rv = parse_str(hotmod_si, &ival, "interface type", &curr); + memset(&h, 0, sizeof(h)); + rv = parse_hotmod_str(curr, &op, &h); if (rv) - break; - si_type = ival; - - rv = parse_str(hotmod_as, &ival, "address space", &curr); - if (rv) - break; - addr_space = ival; - - s = strchr(curr, ','); - if (s) { - *s = '\0'; - s++; - } - addr = simple_strtoul(curr, &n, 0); - if ((*n != '\0') || (*curr == '\0')) { - pr_warn("Invalid hotmod address '%s'\n", curr); - break; - } - - while (s) { - curr = s; - s = strchr(curr, ','); - if (s) { - *s = '\0'; - s++; - } - o = strchr(curr, '='); - if (o) { - *o = '\0'; - o++; - } - rv = check_hotmod_int_op(curr, o, "rsp", ®spacing); - if (rv < 0) - goto out; - else if (rv) - continue; - rv = check_hotmod_int_op(curr, o, "rsi", ®size); - if (rv < 0) - goto out; - else if (rv) - continue; - rv = check_hotmod_int_op(curr, o, "rsh", ®shift); - if (rv < 0) - goto out; - else if (rv) - continue; - rv = check_hotmod_int_op(curr, o, "irq", &irq); - if (rv < 0) - goto out; - else if (rv) - continue; - rv = check_hotmod_int_op(curr, o, "ipmb", &ipmb); - if (rv < 0) - goto out; - else if (rv) - continue; - - rv = -EINVAL; - pr_warn("Invalid hotmod option '%s'\n", curr); goto out; - } if (op == HM_ADD) { - struct si_sm_io io; - - memset(&io, 0, sizeof(io)); - io.addr_source = SI_HOTMOD; - io.si_type = si_type; - io.addr_data = addr; - io.addr_space = addr_space; - - io.addr = NULL; - io.regspacing = regspacing; - if (!io.regspacing) - io.regspacing = DEFAULT_REGSPACING; - io.regsize = regsize; - if (!io.regsize) - io.regsize = DEFAULT_REGSIZE; - io.regshift = regshift; - io.irq = irq; - if (io.irq) - io.irq_setup = ipmi_std_irq_setup; - io.slave_addr = ipmb; - - rv = ipmi_si_add_smi(&io); - if (rv) - goto out; + ipmi_platform_add("hotmod-ipmi-si", + atomic_inc_return(&hotmod_nr), + &h); } else { - ipmi_si_remove_by_data(addr_space, si_type, addr); + ipmi_si_remove_by_data(h.space, h.type, h.addr); } } rv = len; diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 54e3b4f2c024..c8af06dfa244 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -1870,7 +1870,7 @@ int ipmi_si_add_smi(struct si_sm_io *io) * address, they presumably want us to use it and not what is * in the firmware. */ - if (io->addr_source != SI_HARDCODED && + if (io->addr_source != SI_HARDCODED && io->addr_source != SI_HOTMOD && ipmi_si_hardcode_match(io->addr_space, io->addr_data)) { dev_info(io->dev, "Hard-coded device at this address already exists"); diff --git a/drivers/char/ipmi/ipmi_si_platform.c b/drivers/char/ipmi/ipmi_si_platform.c index f2b5ac0986f2..0f493fe83701 100644 --- a/drivers/char/ipmi/ipmi_si_platform.c +++ b/drivers/char/ipmi/ipmi_si_platform.c @@ -431,6 +431,7 @@ static int ipmi_remove(struct platform_device *pdev) static const struct platform_device_id si_plat_ids[] = { { "dmi-ipmi-si", 0 }, { "hardcode-ipmi-si", 0 }, + { "hotmod-ipmi-si", 0 }, { } }; |