Blame view
arch/powerpc/sysdev/pmi.c
6.6 KB
0e8266437 [POWERPC] Add PMI... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
/* * pmi driver * * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 * * PMI (Platform Management Interrupt) is a way to communicate * with the BMC (Baseboard Management Controller) via interrupts. * Unlike IPMI it is bidirectional and has a low latency. * * Author: Christian Krafft <krafft@de.ibm.com> * * 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/interrupt.h> |
5a0e3ad6a include cleanup: ... |
28 |
#include <linux/slab.h> |
0e8266437 [POWERPC] Add PMI... |
29 30 |
#include <linux/completion.h> #include <linux/spinlock.h> |
7dfe293cf powerpc: Fix up m... |
31 |
#include <linux/module.h> |
0e8266437 [POWERPC] Add PMI... |
32 |
#include <linux/workqueue.h> |
eb8f27634 [POWERPC] Clean o... |
33 34 |
#include <linux/of_device.h> #include <linux/of_platform.h> |
0e8266437 [POWERPC] Add PMI... |
35 |
|
0e8266437 [POWERPC] Add PMI... |
36 37 |
#include <asm/io.h> #include <asm/pmi.h> |
6bf05fd77 [POWERPC] add of_... |
38 |
#include <asm/prom.h> |
0e8266437 [POWERPC] Add PMI... |
39 40 41 42 43 44 45 46 |
struct pmi_data { struct list_head handler; spinlock_t handler_spinlock; spinlock_t pmi_spinlock; struct mutex msg_mutex; pmi_message_t msg; struct completion *completion; |
a454dc505 powerpc: remove r... |
47 |
struct platform_device *dev; |
0e8266437 [POWERPC] Add PMI... |
48 49 50 51 |
int irq; u8 __iomem *pmi_reg; struct work_struct work; }; |
813f90728 [CELL] pmi: remov... |
52 |
static struct pmi_data *data; |
0e8266437 [POWERPC] Add PMI... |
53 |
|
fb2474491 powerpc/pmi: Irq ... |
54 |
static irqreturn_t pmi_irq_handler(int irq, void *dev_id) |
0e8266437 [POWERPC] Add PMI... |
55 |
{ |
0e8266437 [POWERPC] Add PMI... |
56 57 |
u8 type; int rc; |
0e8266437 [POWERPC] Add PMI... |
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
spin_lock(&data->pmi_spinlock); type = ioread8(data->pmi_reg + PMI_READ_TYPE); pr_debug("pmi: got message of type %d ", type); if (type & PMI_ACK && !data->completion) { printk(KERN_WARNING "pmi: got unexpected ACK message. "); rc = -EIO; goto unlock; } if (data->completion && !(type & PMI_ACK)) { printk(KERN_WARNING "pmi: expected ACK, but got %d ", type); rc = -EIO; goto unlock; } data->msg.type = type; data->msg.data0 = ioread8(data->pmi_reg + PMI_READ_DATA0); data->msg.data1 = ioread8(data->pmi_reg + PMI_READ_DATA1); data->msg.data2 = ioread8(data->pmi_reg + PMI_READ_DATA2); rc = 0; unlock: spin_unlock(&data->pmi_spinlock); if (rc == -EIO) { rc = IRQ_HANDLED; goto out; } if (data->msg.type & PMI_ACK) { complete(data->completion); rc = IRQ_HANDLED; goto out; } schedule_work(&data->work); rc = IRQ_HANDLED; out: return rc; } static struct of_device_id pmi_match[] = { { .type = "ibm,pmi", .name = "ibm,pmi" }, |
4a065f941 [POWERPC] pmi pro... |
107 |
{ .type = "ibm,pmi" }, |
0e8266437 [POWERPC] Add PMI... |
108 109 110 111 112 113 114 |
{}, }; MODULE_DEVICE_TABLE(of, pmi_match); static void pmi_notify_handlers(struct work_struct *work) { |
0e8266437 [POWERPC] Add PMI... |
115 |
struct pmi_handler *handler; |
0e8266437 [POWERPC] Add PMI... |
116 117 |
spin_lock(&data->handler_spinlock); list_for_each_entry(handler, &data->handler, node) { |
689fd14ae powerpc: Remove p... |
118 119 |
pr_debug("pmi: notifying handler %p ", handler); |
0e8266437 [POWERPC] Add PMI... |
120 |
if (handler->type == data->msg.type) |
813f90728 [CELL] pmi: remov... |
121 |
handler->handle_pmi_message(data->msg); |
0e8266437 [POWERPC] Add PMI... |
122 123 124 |
} spin_unlock(&data->handler_spinlock); } |
000061245 dt/powerpc: Elimi... |
125 |
static int pmi_of_probe(struct platform_device *dev) |
0e8266437 [POWERPC] Add PMI... |
126 |
{ |
61c7a080a of: Always use 's... |
127 |
struct device_node *np = dev->dev.of_node; |
0e8266437 [POWERPC] Add PMI... |
128 |
int rc; |
813f90728 [CELL] pmi: remov... |
129 130 131 132 133 134 |
if (data) { printk(KERN_ERR "pmi: driver has already been initialized. "); rc = -EBUSY; goto out; } |
0e8266437 [POWERPC] Add PMI... |
135 136 137 138 139 140 141 |
data = kzalloc(sizeof(struct pmi_data), GFP_KERNEL); if (!data) { printk(KERN_ERR "pmi: could not allocate memory. "); rc = -ENOMEM; goto out; } |
6bf05fd77 [POWERPC] add of_... |
142 |
data->pmi_reg = of_iomap(np, 0); |
0e8266437 [POWERPC] Add PMI... |
143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
if (!data->pmi_reg) { printk(KERN_ERR "pmi: invalid register address. "); rc = -EFAULT; goto error_cleanup_data; } INIT_LIST_HEAD(&data->handler); mutex_init(&data->msg_mutex); spin_lock_init(&data->pmi_spinlock); spin_lock_init(&data->handler_spinlock); INIT_WORK(&data->work, pmi_notify_handlers); |
0e8266437 [POWERPC] Add PMI... |
157 158 159 160 161 162 163 164 165 |
data->dev = dev; data->irq = irq_of_parse_and_map(np, 0); if (data->irq == NO_IRQ) { printk(KERN_ERR "pmi: invalid interrupt. "); rc = -EFAULT; goto error_cleanup_iomap; } |
813f90728 [CELL] pmi: remov... |
166 |
rc = request_irq(data->irq, pmi_irq_handler, 0, "pmi", NULL); |
0e8266437 [POWERPC] Add PMI... |
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
if (rc) { printk(KERN_ERR "pmi: can't request IRQ %d: returned %d ", data->irq, rc); goto error_cleanup_iomap; } printk(KERN_INFO "pmi: found pmi device at addr %p. ", data->pmi_reg); goto out; error_cleanup_iomap: iounmap(data->pmi_reg); error_cleanup_data: kfree(data); out: return rc; } |
a454dc505 powerpc: remove r... |
188 |
static int pmi_of_remove(struct platform_device *dev) |
0e8266437 [POWERPC] Add PMI... |
189 |
{ |
0e8266437 [POWERPC] Add PMI... |
190 |
struct pmi_handler *handler, *tmp; |
813f90728 [CELL] pmi: remov... |
191 |
free_irq(data->irq, NULL); |
0e8266437 [POWERPC] Add PMI... |
192 193 194 195 196 197 198 199 |
iounmap(data->pmi_reg); spin_lock(&data->handler_spinlock); list_for_each_entry_safe(handler, tmp, &data->handler, node) list_del(&handler->node); spin_unlock(&data->handler_spinlock); |
813f90728 [CELL] pmi: remov... |
200 201 |
kfree(data); data = NULL; |
0e8266437 [POWERPC] Add PMI... |
202 203 204 |
return 0; } |
000061245 dt/powerpc: Elimi... |
205 |
static struct platform_driver pmi_of_platform_driver = { |
0e8266437 [POWERPC] Add PMI... |
206 |
.probe = pmi_of_probe, |
84dd4676f [POWERPC] Move of... |
207 |
.remove = pmi_of_remove, |
4018294b5 of: Remove duplic... |
208 209 210 211 |
.driver = { .name = "pmi", .owner = THIS_MODULE, .of_match_table = pmi_match, |
84dd4676f [POWERPC] Move of... |
212 |
}, |
0e8266437 [POWERPC] Add PMI... |
213 214 215 216 |
}; static int __init pmi_module_init(void) { |
000061245 dt/powerpc: Elimi... |
217 |
return platform_driver_register(&pmi_of_platform_driver); |
0e8266437 [POWERPC] Add PMI... |
218 219 220 221 222 |
} module_init(pmi_module_init); static void __exit pmi_module_exit(void) { |
000061245 dt/powerpc: Elimi... |
223 |
platform_driver_unregister(&pmi_of_platform_driver); |
0e8266437 [POWERPC] Add PMI... |
224 225 |
} module_exit(pmi_module_exit); |
813f90728 [CELL] pmi: remov... |
226 |
int pmi_send_message(pmi_message_t msg) |
0e8266437 [POWERPC] Add PMI... |
227 |
{ |
0e8266437 [POWERPC] Add PMI... |
228 229 |
unsigned long flags; DECLARE_COMPLETION_ONSTACK(completion); |
813f90728 [CELL] pmi: remov... |
230 231 |
if (!data) return -ENODEV; |
0e8266437 [POWERPC] Add PMI... |
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
mutex_lock(&data->msg_mutex); data->msg = msg; pr_debug("pmi_send_message: msg is %08x ", *(u32*)&msg); data->completion = &completion; spin_lock_irqsave(&data->pmi_spinlock, flags); iowrite8(msg.data0, data->pmi_reg + PMI_WRITE_DATA0); iowrite8(msg.data1, data->pmi_reg + PMI_WRITE_DATA1); iowrite8(msg.data2, data->pmi_reg + PMI_WRITE_DATA2); iowrite8(msg.type, data->pmi_reg + PMI_WRITE_TYPE); spin_unlock_irqrestore(&data->pmi_spinlock, flags); pr_debug("pmi_send_message: wait for completion "); wait_for_completion_interruptible_timeout(data->completion, PMI_TIMEOUT); data->completion = NULL; mutex_unlock(&data->msg_mutex); |
813f90728 [CELL] pmi: remov... |
257 258 |
return 0; |
0e8266437 [POWERPC] Add PMI... |
259 260 |
} EXPORT_SYMBOL_GPL(pmi_send_message); |
813f90728 [CELL] pmi: remov... |
261 |
int pmi_register_handler(struct pmi_handler *handler) |
0e8266437 [POWERPC] Add PMI... |
262 |
{ |
79baf4a60 [POWERPC] add che... |
263 |
if (!data) |
813f90728 [CELL] pmi: remov... |
264 |
return -ENODEV; |
79baf4a60 [POWERPC] add che... |
265 |
|
0e8266437 [POWERPC] Add PMI... |
266 267 268 |
spin_lock(&data->handler_spinlock); list_add_tail(&handler->node, &data->handler); spin_unlock(&data->handler_spinlock); |
813f90728 [CELL] pmi: remov... |
269 270 |
return 0; |
0e8266437 [POWERPC] Add PMI... |
271 272 |
} EXPORT_SYMBOL_GPL(pmi_register_handler); |
813f90728 [CELL] pmi: remov... |
273 |
void pmi_unregister_handler(struct pmi_handler *handler) |
0e8266437 [POWERPC] Add PMI... |
274 |
{ |
79baf4a60 [POWERPC] add che... |
275 276 |
if (!data) return; |
0e8266437 [POWERPC] Add PMI... |
277 |
|
79baf4a60 [POWERPC] add che... |
278 279 |
pr_debug("pmi: unregistering handler %p ", handler); |
0e8266437 [POWERPC] Add PMI... |
280 281 282 283 284 285 286 287 288 289 |
spin_lock(&data->handler_spinlock); list_del(&handler->node); spin_unlock(&data->handler_spinlock); } EXPORT_SYMBOL_GPL(pmi_unregister_handler); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>"); MODULE_DESCRIPTION("IBM Platform Management Interrupt driver"); |