Message ID | 1415778065.431549.108606915276.1.gpush@pablo (mailing list archive) |
---|---|
State | Accepted |
Delegated to: | Michael Ellerman |
Headers | show |
This looks good. Can this go into the IPMI tree now, or does it need work done in the PowerPC tree first? Thanks, -corey On 11/12/2014 01:41 AM, Jeremy Kerr wrote: > This change adds an initial IPMI driver for powerpc OPAL firmware. The > interface is exposed entirely through firmware: we have two functions to > send and receive IPMI messages, and an interrupt notification from the > firmware to signify that a message is available. > > Signed-off-by: Jeremy Kerr <jk@ozlabs.org> > > --- > v2: Update for ipmi/for-next tree, add copyright header > > --- > drivers/char/ipmi/Kconfig | 6 > drivers/char/ipmi/Makefile | 1 > drivers/char/ipmi/ipmi_powernv.c | 307 +++++++++++++++++++++++++++++++ > 3 files changed, 314 insertions(+) > > diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig > index c1fccf4..65fb008 100644 > --- a/drivers/char/ipmi/Kconfig > +++ b/drivers/char/ipmi/Kconfig > @@ -72,6 +72,12 @@ config IPMI_SSIF > have a driver that must be accessed over an I2C bus instead of a > standard interface. This module requires I2C support. > > +config IPMI_POWERNV > + depends on PPC_POWERNV > + tristate 'POWERNV (OPAL firmware) IPMI interface' > + help > + Provides a driver for OPAL firmware-based IPMI interfaces. > + > config IPMI_WATCHDOG > tristate 'IPMI Watchdog Timer' > help > diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile > index 115c08d..f3ffde1 100644 > --- a/drivers/char/ipmi/Makefile > +++ b/drivers/char/ipmi/Makefile > @@ -8,5 +8,6 @@ obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o > obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o > obj-$(CONFIG_IPMI_SI) += ipmi_si.o > obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o > +obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o > obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o > obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o > diff --git a/drivers/char/ipmi/ipmi_powernv.c b/drivers/char/ipmi/ipmi_powernv.c > new file mode 100644 > index 0000000..50134ec > --- /dev/null > +++ b/drivers/char/ipmi/ipmi_powernv.c > @@ -0,0 +1,307 @@ > +/* > + * PowerNV OPAL IPMI driver > + * > + * Copyright 2014 IBM Corp. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License as published by the Free > + * Software Foundation; either version 2 of the License, or (at your option) > + * any later version. > + */ > + > +#define pr_fmt(fmt) "ipmi-powernv: " fmt > + > +#include <linux/ipmi_smi.h> > +#include <linux/list.h> > +#include <linux/module.h> > +#include <linux/of.h> > + > +#include <asm/opal.h> > + > + > +struct ipmi_smi_powernv { > + u64 interface_id; > + struct ipmi_device_id ipmi_id; > + ipmi_smi_t intf; > + u64 event; > + struct notifier_block event_nb; > + > + /** > + * We assume that there can only be one outstanding request, so > + * keep the pending message in cur_msg. We protect this from concurrent > + * updates through send & recv calls, (and consequently opal_msg, which > + * is in-use when cur_msg is set) with msg_lock > + */ > + spinlock_t msg_lock; > + struct ipmi_smi_msg *cur_msg; > + struct opal_ipmi_msg *opal_msg; > +}; > + > +static int ipmi_powernv_start_processing(void *send_info, ipmi_smi_t intf) > +{ > + struct ipmi_smi_powernv *smi = send_info; > + smi->intf = intf; > + return 0; > +} > + > +static void send_error_reply(struct ipmi_smi_powernv *smi, > + struct ipmi_smi_msg *msg, u8 completion_code) > +{ > + msg->rsp[0] = msg->data[0] | 0x4; > + msg->rsp[1] = msg->data[1]; > + msg->rsp[2] = completion_code; > + msg->rsp_size = 3; > + ipmi_smi_msg_received(smi->intf, msg); > +} > + > +static void ipmi_powernv_send(void *send_info, struct ipmi_smi_msg *msg) > +{ > + struct ipmi_smi_powernv *smi = send_info; > + struct opal_ipmi_msg *opal_msg; > + unsigned long flags; > + int comp, rc; > + size_t size; > + > + /* ensure data_len will fit in the opal_ipmi_msg buffer... */ > + if (msg->data_size > IPMI_MAX_MSG_LENGTH) { > + comp = IPMI_REQ_LEN_EXCEEDED_ERR; > + goto err; > + } > + > + /* ... and that we at least have netfn and cmd bytes */ > + if (msg->data_size < 2) { > + comp = IPMI_REQ_LEN_INVALID_ERR; > + goto err; > + } > + > + spin_lock_irqsave(&smi->msg_lock, flags); > + > + if (smi->cur_msg) { > + comp = IPMI_NODE_BUSY_ERR; > + goto err_unlock; > + } > + > + /* format our data for the OPAL API */ > + opal_msg = smi->opal_msg; > + opal_msg->version = OPAL_IPMI_MSG_FORMAT_VERSION_1; > + opal_msg->netfn = msg->data[0]; > + opal_msg->cmd = msg->data[1]; > + if (msg->data_size > 2) > + memcpy(opal_msg->data, msg->data + 2, msg->data_size - 2); > + > + /* data_size already includes the netfn and cmd bytes */ > + size = sizeof(*opal_msg) + msg->data_size - 2; > + > + pr_devel("%s: opal_ipmi_send(0x%llx, %p, %ld)\n", __func__, > + smi->interface_id, opal_msg, size); > + rc = opal_ipmi_send(smi->interface_id, opal_msg, size); > + pr_devel("%s: -> %d\n", __func__, rc); > + > + if (!rc) { > + smi->cur_msg = msg; > + spin_unlock_irqrestore(&smi->msg_lock, flags); > + return; > + } > + > + comp = IPMI_ERR_UNSPECIFIED; > +err_unlock: > + spin_unlock_irqrestore(&smi->msg_lock, flags); > +err: > + send_error_reply(smi, msg, comp); > +} > + > +static int ipmi_powernv_recv(struct ipmi_smi_powernv *smi) > +{ > + struct opal_ipmi_msg *opal_msg; > + struct ipmi_smi_msg *msg; > + unsigned long flags; > + uint64_t size; > + int rc; > + > + pr_devel("%s: opal_ipmi_recv(%llx, msg, sz)\n", __func__, > + smi->interface_id); > + > + spin_lock_irqsave(&smi->msg_lock, flags); > + > + if (!smi->cur_msg) { > + pr_warn("no current message?\n"); > + return 0; > + } > + > + msg = smi->cur_msg; > + opal_msg = smi->opal_msg; > + > + size = cpu_to_be64(sizeof(*opal_msg) + IPMI_MAX_MSG_LENGTH); > + > + rc = opal_ipmi_recv(smi->interface_id, > + opal_msg, > + &size); > + size = be64_to_cpu(size); > + pr_devel("%s: -> %d (size %lld)\n", __func__, > + rc, rc == 0 ? size : 0); > + if (rc) { > + spin_unlock_irqrestore(&smi->msg_lock, flags); > + ipmi_free_smi_msg(msg); > + return 0; > + } > + > + if (size < sizeof(*opal_msg)) { > + spin_unlock_irqrestore(&smi->msg_lock, flags); > + pr_warn("unexpected IPMI message size %lld\n", size); > + return 0; > + } > + > + if (opal_msg->version != OPAL_IPMI_MSG_FORMAT_VERSION_1) { > + spin_unlock_irqrestore(&smi->msg_lock, flags); > + pr_warn("unexpected IPMI message format (version %d)\n", > + opal_msg->version); > + return 0; > + } > + > + msg->rsp[0] = opal_msg->netfn; > + msg->rsp[1] = opal_msg->cmd; > + if (size > sizeof(*opal_msg)) > + memcpy(&msg->rsp[2], opal_msg->data, size - sizeof(*opal_msg)); > + msg->rsp_size = 2 + size - sizeof(*opal_msg); > + > + smi->cur_msg = NULL; > + spin_unlock_irqrestore(&smi->msg_lock, flags); > + ipmi_smi_msg_received(smi->intf, msg); > + return 0; > +} > + > +static void ipmi_powernv_request_events(void *send_info) > +{ > +} > + > +static void ipmi_powernv_set_run_to_completion(void *send_info, > + bool run_to_completion) > +{ > +} > + > +static void ipmi_powernv_poll(void *send_info) > +{ > + struct ipmi_smi_powernv *smi = send_info; > + ipmi_powernv_recv(smi); > +} > + > +static struct ipmi_smi_handlers ipmi_powernv_smi_handlers = { > + .owner = THIS_MODULE, > + .start_processing = ipmi_powernv_start_processing, > + .sender = ipmi_powernv_send, > + .request_events = ipmi_powernv_request_events, > + .set_run_to_completion = ipmi_powernv_set_run_to_completion, > + .poll = ipmi_powernv_poll, > +}; > + > +static int ipmi_opal_event(struct notifier_block *nb, > + unsigned long events, void *change) > +{ > + struct ipmi_smi_powernv *smi = container_of(nb, > + struct ipmi_smi_powernv, event_nb); > + > + if (events & smi->event) > + ipmi_powernv_recv(smi); > + return 0; > +} > + > +static int ipmi_powernv_probe(struct platform_device *pdev) > +{ > + struct ipmi_smi_powernv *ipmi; > + struct device *dev; > + u32 prop; > + int rc; > + > + if (!pdev || !pdev->dev.of_node) > + return -ENODEV; > + > + dev = &pdev->dev; > + > + ipmi = devm_kzalloc(dev, sizeof(*ipmi), GFP_KERNEL); > + if (!ipmi) > + return -ENOMEM; > + > + spin_lock_init(&ipmi->msg_lock); > + > + rc = of_property_read_u32(dev->of_node, "ibm,ipmi-interface-id", > + &prop); > + if (rc) { > + dev_warn(dev, "No interface ID property\n"); > + goto err_free; > + } > + ipmi->interface_id = prop; > + > + rc = of_property_read_u32(dev->of_node, "interrupts", &prop); > + if (rc) { > + dev_warn(dev, "No interrupts property\n"); > + goto err_free; > + } > + > + ipmi->event = 1ull << prop; > + ipmi->event_nb.notifier_call = ipmi_opal_event; > + > + rc = opal_notifier_register(&ipmi->event_nb); > + if (rc) { > + dev_warn(dev, "OPAL notifier registration failed (%d)\n", rc); > + goto err_free; > + } > + > + ipmi->opal_msg = devm_kmalloc(dev, > + sizeof(*ipmi->opal_msg) + IPMI_MAX_MSG_LENGTH, > + GFP_KERNEL); > + if (!ipmi->opal_msg) { > + rc = -ENOMEM; > + goto err_unregister; > + } > + > + /* todo: query actual ipmi_device_id */ > + rc = ipmi_register_smi(&ipmi_powernv_smi_handlers, ipmi, > + &ipmi->ipmi_id, dev, 0); > + if (rc) { > + dev_warn(dev, "IPMI SMI registration failed (%d)\n", rc); > + goto err_free_msg; > + } > + > + dev_set_drvdata(dev, ipmi); > + return 0; > + > +err_free_msg: > + devm_kfree(dev, ipmi->opal_msg); > +err_unregister: > + opal_notifier_unregister(&ipmi->event_nb); > +err_free: > + devm_kfree(dev, ipmi); > + return rc; > +} > + > +static int ipmi_powernv_remove(struct platform_device *pdev) > +{ > + struct ipmi_smi_powernv *smi = dev_get_drvdata(&pdev->dev); > + ipmi_unregister_smi(smi->intf); > + opal_notifier_unregister(&smi->event_nb); > + return 0; > +} > + > +static const struct of_device_id ipmi_powernv_match[] = { > + { .compatible = "ibm,opal-ipmi" }, > + { }, > +}; > + > + > +static struct platform_driver powernv_ipmi_driver = { > + .driver = { > + .name = "ipmi-powernv", > + .owner = THIS_MODULE, > + .of_match_table = ipmi_powernv_match, > + }, > + .probe = ipmi_powernv_probe, > + .remove = ipmi_powernv_remove, > +}; > + > + > +module_platform_driver(powernv_ipmi_driver); > + > +MODULE_DEVICE_TABLE(of, ipmi_powernv_match); > +MODULE_DESCRIPTION("powernv IPMI driver"); > +MODULE_AUTHOR("Jeremy Kerr <jk@ozlabs.org>"); > +MODULE_LICENSE("GPL");
Hi Corey, > This looks good. Can this go into the IPMI tree now, or does it need > work done in the PowerPC tree first? The 1/2 patch is already in the powerpc tree - but to ensure that we hit Linus' tree in the right order, Michael has suggested that you merge his topic branch (which only has that one change): https://git.kernel.org/cgit/linux/kernel/git/mpe/linux.git/log/?h=topic/opal-ipmi - then apply the patch. This way, whichever tree Linus pulls first will have the changes in the correct order. Cheers, Jeremy
On Thu, 2014-11-13 at 19:42 -0600, Corey Minyard wrote: > This looks good. Can this go into the IPMI tree now, or does it need > work done in the PowerPC tree first? You need to merge this first: https://git.kernel.org/cgit/linux/kernel/git/mpe/linux.git/log/?h=topic/opal-ipmi ie. $ git fetch git://git.kernel.org/pub/scm/linux/kernel/git/mpe/linux.git topic/opal-ipmi $ git merge FETCH_HEAD cheers
diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index c1fccf4..65fb008 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -72,6 +72,12 @@ config IPMI_SSIF have a driver that must be accessed over an I2C bus instead of a standard interface. This module requires I2C support. +config IPMI_POWERNV + depends on PPC_POWERNV + tristate 'POWERNV (OPAL firmware) IPMI interface' + help + Provides a driver for OPAL firmware-based IPMI interfaces. + config IPMI_WATCHDOG tristate 'IPMI Watchdog Timer' help diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile index 115c08d..f3ffde1 100644 --- a/drivers/char/ipmi/Makefile +++ b/drivers/char/ipmi/Makefile @@ -8,5 +8,6 @@ obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o obj-$(CONFIG_IPMI_SI) += ipmi_si.o obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o +obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o diff --git a/drivers/char/ipmi/ipmi_powernv.c b/drivers/char/ipmi/ipmi_powernv.c new file mode 100644 index 0000000..50134ec --- /dev/null +++ b/drivers/char/ipmi/ipmi_powernv.c @@ -0,0 +1,307 @@ +/* + * PowerNV OPAL IPMI driver + * + * Copyright 2014 IBM Corp. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#define pr_fmt(fmt) "ipmi-powernv: " fmt + +#include <linux/ipmi_smi.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/of.h> + +#include <asm/opal.h> + + +struct ipmi_smi_powernv { + u64 interface_id; + struct ipmi_device_id ipmi_id; + ipmi_smi_t intf; + u64 event; + struct notifier_block event_nb; + + /** + * We assume that there can only be one outstanding request, so + * keep the pending message in cur_msg. We protect this from concurrent + * updates through send & recv calls, (and consequently opal_msg, which + * is in-use when cur_msg is set) with msg_lock + */ + spinlock_t msg_lock; + struct ipmi_smi_msg *cur_msg; + struct opal_ipmi_msg *opal_msg; +}; + +static int ipmi_powernv_start_processing(void *send_info, ipmi_smi_t intf) +{ + struct ipmi_smi_powernv *smi = send_info; + smi->intf = intf; + return 0; +} + +static void send_error_reply(struct ipmi_smi_powernv *smi, + struct ipmi_smi_msg *msg, u8 completion_code) +{ + msg->rsp[0] = msg->data[0] | 0x4; + msg->rsp[1] = msg->data[1]; + msg->rsp[2] = completion_code; + msg->rsp_size = 3; + ipmi_smi_msg_received(smi->intf, msg); +} + +static void ipmi_powernv_send(void *send_info, struct ipmi_smi_msg *msg) +{ + struct ipmi_smi_powernv *smi = send_info; + struct opal_ipmi_msg *opal_msg; + unsigned long flags; + int comp, rc; + size_t size; + + /* ensure data_len will fit in the opal_ipmi_msg buffer... */ + if (msg->data_size > IPMI_MAX_MSG_LENGTH) { + comp = IPMI_REQ_LEN_EXCEEDED_ERR; + goto err; + } + + /* ... and that we at least have netfn and cmd bytes */ + if (msg->data_size < 2) { + comp = IPMI_REQ_LEN_INVALID_ERR; + goto err; + } + + spin_lock_irqsave(&smi->msg_lock, flags); + + if (smi->cur_msg) { + comp = IPMI_NODE_BUSY_ERR; + goto err_unlock; + } + + /* format our data for the OPAL API */ + opal_msg = smi->opal_msg; + opal_msg->version = OPAL_IPMI_MSG_FORMAT_VERSION_1; + opal_msg->netfn = msg->data[0]; + opal_msg->cmd = msg->data[1]; + if (msg->data_size > 2) + memcpy(opal_msg->data, msg->data + 2, msg->data_size - 2); + + /* data_size already includes the netfn and cmd bytes */ + size = sizeof(*opal_msg) + msg->data_size - 2; + + pr_devel("%s: opal_ipmi_send(0x%llx, %p, %ld)\n", __func__, + smi->interface_id, opal_msg, size); + rc = opal_ipmi_send(smi->interface_id, opal_msg, size); + pr_devel("%s: -> %d\n", __func__, rc); + + if (!rc) { + smi->cur_msg = msg; + spin_unlock_irqrestore(&smi->msg_lock, flags); + return; + } + + comp = IPMI_ERR_UNSPECIFIED; +err_unlock: + spin_unlock_irqrestore(&smi->msg_lock, flags); +err: + send_error_reply(smi, msg, comp); +} + +static int ipmi_powernv_recv(struct ipmi_smi_powernv *smi) +{ + struct opal_ipmi_msg *opal_msg; + struct ipmi_smi_msg *msg; + unsigned long flags; + uint64_t size; + int rc; + + pr_devel("%s: opal_ipmi_recv(%llx, msg, sz)\n", __func__, + smi->interface_id); + + spin_lock_irqsave(&smi->msg_lock, flags); + + if (!smi->cur_msg) { + pr_warn("no current message?\n"); + return 0; + } + + msg = smi->cur_msg; + opal_msg = smi->opal_msg; + + size = cpu_to_be64(sizeof(*opal_msg) + IPMI_MAX_MSG_LENGTH); + + rc = opal_ipmi_recv(smi->interface_id, + opal_msg, + &size); + size = be64_to_cpu(size); + pr_devel("%s: -> %d (size %lld)\n", __func__, + rc, rc == 0 ? size : 0); + if (rc) { + spin_unlock_irqrestore(&smi->msg_lock, flags); + ipmi_free_smi_msg(msg); + return 0; + } + + if (size < sizeof(*opal_msg)) { + spin_unlock_irqrestore(&smi->msg_lock, flags); + pr_warn("unexpected IPMI message size %lld\n", size); + return 0; + } + + if (opal_msg->version != OPAL_IPMI_MSG_FORMAT_VERSION_1) { + spin_unlock_irqrestore(&smi->msg_lock, flags); + pr_warn("unexpected IPMI message format (version %d)\n", + opal_msg->version); + return 0; + } + + msg->rsp[0] = opal_msg->netfn; + msg->rsp[1] = opal_msg->cmd; + if (size > sizeof(*opal_msg)) + memcpy(&msg->rsp[2], opal_msg->data, size - sizeof(*opal_msg)); + msg->rsp_size = 2 + size - sizeof(*opal_msg); + + smi->cur_msg = NULL; + spin_unlock_irqrestore(&smi->msg_lock, flags); + ipmi_smi_msg_received(smi->intf, msg); + return 0; +} + +static void ipmi_powernv_request_events(void *send_info) +{ +} + +static void ipmi_powernv_set_run_to_completion(void *send_info, + bool run_to_completion) +{ +} + +static void ipmi_powernv_poll(void *send_info) +{ + struct ipmi_smi_powernv *smi = send_info; + ipmi_powernv_recv(smi); +} + +static struct ipmi_smi_handlers ipmi_powernv_smi_handlers = { + .owner = THIS_MODULE, + .start_processing = ipmi_powernv_start_processing, + .sender = ipmi_powernv_send, + .request_events = ipmi_powernv_request_events, + .set_run_to_completion = ipmi_powernv_set_run_to_completion, + .poll = ipmi_powernv_poll, +}; + +static int ipmi_opal_event(struct notifier_block *nb, + unsigned long events, void *change) +{ + struct ipmi_smi_powernv *smi = container_of(nb, + struct ipmi_smi_powernv, event_nb); + + if (events & smi->event) + ipmi_powernv_recv(smi); + return 0; +} + +static int ipmi_powernv_probe(struct platform_device *pdev) +{ + struct ipmi_smi_powernv *ipmi; + struct device *dev; + u32 prop; + int rc; + + if (!pdev || !pdev->dev.of_node) + return -ENODEV; + + dev = &pdev->dev; + + ipmi = devm_kzalloc(dev, sizeof(*ipmi), GFP_KERNEL); + if (!ipmi) + return -ENOMEM; + + spin_lock_init(&ipmi->msg_lock); + + rc = of_property_read_u32(dev->of_node, "ibm,ipmi-interface-id", + &prop); + if (rc) { + dev_warn(dev, "No interface ID property\n"); + goto err_free; + } + ipmi->interface_id = prop; + + rc = of_property_read_u32(dev->of_node, "interrupts", &prop); + if (rc) { + dev_warn(dev, "No interrupts property\n"); + goto err_free; + } + + ipmi->event = 1ull << prop; + ipmi->event_nb.notifier_call = ipmi_opal_event; + + rc = opal_notifier_register(&ipmi->event_nb); + if (rc) { + dev_warn(dev, "OPAL notifier registration failed (%d)\n", rc); + goto err_free; + } + + ipmi->opal_msg = devm_kmalloc(dev, + sizeof(*ipmi->opal_msg) + IPMI_MAX_MSG_LENGTH, + GFP_KERNEL); + if (!ipmi->opal_msg) { + rc = -ENOMEM; + goto err_unregister; + } + + /* todo: query actual ipmi_device_id */ + rc = ipmi_register_smi(&ipmi_powernv_smi_handlers, ipmi, + &ipmi->ipmi_id, dev, 0); + if (rc) { + dev_warn(dev, "IPMI SMI registration failed (%d)\n", rc); + goto err_free_msg; + } + + dev_set_drvdata(dev, ipmi); + return 0; + +err_free_msg: + devm_kfree(dev, ipmi->opal_msg); +err_unregister: + opal_notifier_unregister(&ipmi->event_nb); +err_free: + devm_kfree(dev, ipmi); + return rc; +} + +static int ipmi_powernv_remove(struct platform_device *pdev) +{ + struct ipmi_smi_powernv *smi = dev_get_drvdata(&pdev->dev); + ipmi_unregister_smi(smi->intf); + opal_notifier_unregister(&smi->event_nb); + return 0; +} + +static const struct of_device_id ipmi_powernv_match[] = { + { .compatible = "ibm,opal-ipmi" }, + { }, +}; + + +static struct platform_driver powernv_ipmi_driver = { + .driver = { + .name = "ipmi-powernv", + .owner = THIS_MODULE, + .of_match_table = ipmi_powernv_match, + }, + .probe = ipmi_powernv_probe, + .remove = ipmi_powernv_remove, +}; + + +module_platform_driver(powernv_ipmi_driver); + +MODULE_DEVICE_TABLE(of, ipmi_powernv_match); +MODULE_DESCRIPTION("powernv IPMI driver"); +MODULE_AUTHOR("Jeremy Kerr <jk@ozlabs.org>"); +MODULE_LICENSE("GPL");
This change adds an initial IPMI driver for powerpc OPAL firmware. The interface is exposed entirely through firmware: we have two functions to send and receive IPMI messages, and an interrupt notification from the firmware to signify that a message is available. Signed-off-by: Jeremy Kerr <jk@ozlabs.org> --- v2: Update for ipmi/for-next tree, add copyright header --- drivers/char/ipmi/Kconfig | 6 drivers/char/ipmi/Makefile | 1 drivers/char/ipmi/ipmi_powernv.c | 307 +++++++++++++++++++++++++++++++ 3 files changed, 314 insertions(+)