root/arch/powerpc/sysdev/pmi.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pmi_irq_handler
  2. pmi_notify_handlers
  3. pmi_of_probe
  4. pmi_of_remove
  5. pmi_send_message
  6. pmi_register_handler
  7. pmi_unregister_handler

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * pmi driver
   4  *
   5  * (C) Copyright IBM Deutschland Entwicklung GmbH 2005
   6  *
   7  * PMI (Platform Management Interrupt) is a way to communicate
   8  * with the BMC (Baseboard Management Controller) via interrupts.
   9  * Unlike IPMI it is bidirectional and has a low latency.
  10  *
  11  * Author: Christian Krafft <krafft@de.ibm.com>
  12  */
  13 
  14 #include <linux/interrupt.h>
  15 #include <linux/slab.h>
  16 #include <linux/completion.h>
  17 #include <linux/spinlock.h>
  18 #include <linux/module.h>
  19 #include <linux/workqueue.h>
  20 #include <linux/of_device.h>
  21 #include <linux/of_platform.h>
  22 
  23 #include <asm/io.h>
  24 #include <asm/pmi.h>
  25 #include <asm/prom.h>
  26 
  27 struct pmi_data {
  28         struct list_head        handler;
  29         spinlock_t              handler_spinlock;
  30         spinlock_t              pmi_spinlock;
  31         struct mutex            msg_mutex;
  32         pmi_message_t           msg;
  33         struct completion       *completion;
  34         struct platform_device  *dev;
  35         int                     irq;
  36         u8 __iomem              *pmi_reg;
  37         struct work_struct      work;
  38 };
  39 
  40 static struct pmi_data *data;
  41 
  42 static irqreturn_t pmi_irq_handler(int irq, void *dev_id)
  43 {
  44         u8 type;
  45         int rc;
  46 
  47         spin_lock(&data->pmi_spinlock);
  48 
  49         type = ioread8(data->pmi_reg + PMI_READ_TYPE);
  50         pr_debug("pmi: got message of type %d\n", type);
  51 
  52         if (type & PMI_ACK && !data->completion) {
  53                 printk(KERN_WARNING "pmi: got unexpected ACK message.\n");
  54                 rc = -EIO;
  55                 goto unlock;
  56         }
  57 
  58         if (data->completion && !(type & PMI_ACK)) {
  59                 printk(KERN_WARNING "pmi: expected ACK, but got %d\n", type);
  60                 rc = -EIO;
  61                 goto unlock;
  62         }
  63 
  64         data->msg.type = type;
  65         data->msg.data0 = ioread8(data->pmi_reg + PMI_READ_DATA0);
  66         data->msg.data1 = ioread8(data->pmi_reg + PMI_READ_DATA1);
  67         data->msg.data2 = ioread8(data->pmi_reg + PMI_READ_DATA2);
  68         rc = 0;
  69 unlock:
  70         spin_unlock(&data->pmi_spinlock);
  71 
  72         if (rc == -EIO) {
  73                 rc = IRQ_HANDLED;
  74                 goto out;
  75         }
  76 
  77         if (data->msg.type & PMI_ACK) {
  78                 complete(data->completion);
  79                 rc = IRQ_HANDLED;
  80                 goto out;
  81         }
  82 
  83         schedule_work(&data->work);
  84 
  85         rc = IRQ_HANDLED;
  86 out:
  87         return rc;
  88 }
  89 
  90 
  91 static const struct of_device_id pmi_match[] = {
  92         { .type = "ibm,pmi", .name = "ibm,pmi" },
  93         { .type = "ibm,pmi" },
  94         {},
  95 };
  96 
  97 MODULE_DEVICE_TABLE(of, pmi_match);
  98 
  99 static void pmi_notify_handlers(struct work_struct *work)
 100 {
 101         struct pmi_handler *handler;
 102 
 103         spin_lock(&data->handler_spinlock);
 104         list_for_each_entry(handler, &data->handler, node) {
 105                 pr_debug("pmi: notifying handler %p\n", handler);
 106                 if (handler->type == data->msg.type)
 107                         handler->handle_pmi_message(data->msg);
 108         }
 109         spin_unlock(&data->handler_spinlock);
 110 }
 111 
 112 static int pmi_of_probe(struct platform_device *dev)
 113 {
 114         struct device_node *np = dev->dev.of_node;
 115         int rc;
 116 
 117         if (data) {
 118                 printk(KERN_ERR "pmi: driver has already been initialized.\n");
 119                 rc = -EBUSY;
 120                 goto out;
 121         }
 122 
 123         data = kzalloc(sizeof(struct pmi_data), GFP_KERNEL);
 124         if (!data) {
 125                 printk(KERN_ERR "pmi: could not allocate memory.\n");
 126                 rc = -ENOMEM;
 127                 goto out;
 128         }
 129 
 130         data->pmi_reg = of_iomap(np, 0);
 131         if (!data->pmi_reg) {
 132                 printk(KERN_ERR "pmi: invalid register address.\n");
 133                 rc = -EFAULT;
 134                 goto error_cleanup_data;
 135         }
 136 
 137         INIT_LIST_HEAD(&data->handler);
 138 
 139         mutex_init(&data->msg_mutex);
 140         spin_lock_init(&data->pmi_spinlock);
 141         spin_lock_init(&data->handler_spinlock);
 142 
 143         INIT_WORK(&data->work, pmi_notify_handlers);
 144 
 145         data->dev = dev;
 146 
 147         data->irq = irq_of_parse_and_map(np, 0);
 148         if (!data->irq) {
 149                 printk(KERN_ERR "pmi: invalid interrupt.\n");
 150                 rc = -EFAULT;
 151                 goto error_cleanup_iomap;
 152         }
 153 
 154         rc = request_irq(data->irq, pmi_irq_handler, 0, "pmi", NULL);
 155         if (rc) {
 156                 printk(KERN_ERR "pmi: can't request IRQ %d: returned %d\n",
 157                                 data->irq, rc);
 158                 goto error_cleanup_iomap;
 159         }
 160 
 161         printk(KERN_INFO "pmi: found pmi device at addr %p.\n", data->pmi_reg);
 162 
 163         goto out;
 164 
 165 error_cleanup_iomap:
 166         iounmap(data->pmi_reg);
 167 
 168 error_cleanup_data:
 169         kfree(data);
 170 
 171 out:
 172         return rc;
 173 }
 174 
 175 static int pmi_of_remove(struct platform_device *dev)
 176 {
 177         struct pmi_handler *handler, *tmp;
 178 
 179         free_irq(data->irq, NULL);
 180         iounmap(data->pmi_reg);
 181 
 182         spin_lock(&data->handler_spinlock);
 183 
 184         list_for_each_entry_safe(handler, tmp, &data->handler, node)
 185                 list_del(&handler->node);
 186 
 187         spin_unlock(&data->handler_spinlock);
 188 
 189         kfree(data);
 190         data = NULL;
 191 
 192         return 0;
 193 }
 194 
 195 static struct platform_driver pmi_of_platform_driver = {
 196         .probe          = pmi_of_probe,
 197         .remove         = pmi_of_remove,
 198         .driver = {
 199                 .name = "pmi",
 200                 .of_match_table = pmi_match,
 201         },
 202 };
 203 module_platform_driver(pmi_of_platform_driver);
 204 
 205 int pmi_send_message(pmi_message_t msg)
 206 {
 207         unsigned long flags;
 208         DECLARE_COMPLETION_ONSTACK(completion);
 209 
 210         if (!data)
 211                 return -ENODEV;
 212 
 213         mutex_lock(&data->msg_mutex);
 214 
 215         data->msg = msg;
 216         pr_debug("pmi_send_message: msg is %08x\n", *(u32*)&msg);
 217 
 218         data->completion = &completion;
 219 
 220         spin_lock_irqsave(&data->pmi_spinlock, flags);
 221         iowrite8(msg.data0, data->pmi_reg + PMI_WRITE_DATA0);
 222         iowrite8(msg.data1, data->pmi_reg + PMI_WRITE_DATA1);
 223         iowrite8(msg.data2, data->pmi_reg + PMI_WRITE_DATA2);
 224         iowrite8(msg.type, data->pmi_reg + PMI_WRITE_TYPE);
 225         spin_unlock_irqrestore(&data->pmi_spinlock, flags);
 226 
 227         pr_debug("pmi_send_message: wait for completion\n");
 228 
 229         wait_for_completion_interruptible_timeout(data->completion,
 230                                                   PMI_TIMEOUT);
 231 
 232         data->completion = NULL;
 233 
 234         mutex_unlock(&data->msg_mutex);
 235 
 236         return 0;
 237 }
 238 EXPORT_SYMBOL_GPL(pmi_send_message);
 239 
 240 int pmi_register_handler(struct pmi_handler *handler)
 241 {
 242         if (!data)
 243                 return -ENODEV;
 244 
 245         spin_lock(&data->handler_spinlock);
 246         list_add_tail(&handler->node, &data->handler);
 247         spin_unlock(&data->handler_spinlock);
 248 
 249         return 0;
 250 }
 251 EXPORT_SYMBOL_GPL(pmi_register_handler);
 252 
 253 void pmi_unregister_handler(struct pmi_handler *handler)
 254 {
 255         if (!data)
 256                 return;
 257 
 258         pr_debug("pmi: unregistering handler %p\n", handler);
 259 
 260         spin_lock(&data->handler_spinlock);
 261         list_del(&handler->node);
 262         spin_unlock(&data->handler_spinlock);
 263 }
 264 EXPORT_SYMBOL_GPL(pmi_unregister_handler);
 265 
 266 MODULE_LICENSE("GPL");
 267 MODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>");
 268 MODULE_DESCRIPTION("IBM Platform Management Interrupt driver");

/* [<][>][^][v][top][bottom][index][help] */