Linux Audio

Check our new training course

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