Blame view

arch/powerpc/sysdev/pmi.c 6.6 KB
0e8266437   Christian Krafft   [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   Tejun Heo   include cleanup: ...
28
  #include <linux/slab.h>
0e8266437   Christian Krafft   [POWERPC] Add PMI...
29
30
  #include <linux/completion.h>
  #include <linux/spinlock.h>
7dfe293cf   Paul Gortmaker   powerpc: Fix up m...
31
  #include <linux/module.h>
0e8266437   Christian Krafft   [POWERPC] Add PMI...
32
  #include <linux/workqueue.h>
eb8f27634   Jon Loeliger   [POWERPC] Clean o...
33
34
  #include <linux/of_device.h>
  #include <linux/of_platform.h>
0e8266437   Christian Krafft   [POWERPC] Add PMI...
35

0e8266437   Christian Krafft   [POWERPC] Add PMI...
36
37
  #include <asm/io.h>
  #include <asm/pmi.h>
6bf05fd77   Christian Krafft   [POWERPC] add of_...
38
  #include <asm/prom.h>
0e8266437   Christian Krafft   [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   Grant Likely   powerpc: remove r...
47
  	struct platform_device	*dev;
0e8266437   Christian Krafft   [POWERPC] Add PMI...
48
49
50
51
  	int			irq;
  	u8 __iomem		*pmi_reg;
  	struct work_struct	work;
  };
813f90728   Christian Krafft   [CELL] pmi: remov...
52
  static struct pmi_data *data;
0e8266437   Christian Krafft   [POWERPC] Add PMI...
53

fb2474491   Stephen Rothwell   powerpc/pmi: Irq ...
54
  static irqreturn_t pmi_irq_handler(int irq, void *dev_id)
0e8266437   Christian Krafft   [POWERPC] Add PMI...
55
  {
0e8266437   Christian Krafft   [POWERPC] Add PMI...
56
57
  	u8 type;
  	int rc;
0e8266437   Christian Krafft   [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   Christian Krafft   [POWERPC] pmi pro...
107
  	{ .type = "ibm,pmi" },
0e8266437   Christian Krafft   [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   Christian Krafft   [POWERPC] Add PMI...
115
  	struct pmi_handler *handler;
0e8266437   Christian Krafft   [POWERPC] Add PMI...
116
117
  	spin_lock(&data->handler_spinlock);
  	list_for_each_entry(handler, &data->handler, node) {
689fd14ae   Joe Perches   powerpc: Remove p...
118
119
  		pr_debug("pmi: notifying handler %p
  ", handler);
0e8266437   Christian Krafft   [POWERPC] Add PMI...
120
  		if (handler->type == data->msg.type)
813f90728   Christian Krafft   [CELL] pmi: remov...
121
  			handler->handle_pmi_message(data->msg);
0e8266437   Christian Krafft   [POWERPC] Add PMI...
122
123
124
  	}
  	spin_unlock(&data->handler_spinlock);
  }
000061245   Grant Likely   dt/powerpc: Elimi...
125
  static int pmi_of_probe(struct platform_device *dev)
0e8266437   Christian Krafft   [POWERPC] Add PMI...
126
  {
61c7a080a   Grant Likely   of: Always use 's...
127
  	struct device_node *np = dev->dev.of_node;
0e8266437   Christian Krafft   [POWERPC] Add PMI...
128
  	int rc;
813f90728   Christian Krafft   [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   Christian Krafft   [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   Christian Krafft   [POWERPC] add of_...
142
  	data->pmi_reg = of_iomap(np, 0);
0e8266437   Christian Krafft   [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   Christian Krafft   [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   Christian Krafft   [CELL] pmi: remov...
166
  	rc = request_irq(data->irq, pmi_irq_handler, 0, "pmi", NULL);
0e8266437   Christian Krafft   [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   Grant Likely   powerpc: remove r...
188
  static int pmi_of_remove(struct platform_device *dev)
0e8266437   Christian Krafft   [POWERPC] Add PMI...
189
  {
0e8266437   Christian Krafft   [POWERPC] Add PMI...
190
  	struct pmi_handler *handler, *tmp;
813f90728   Christian Krafft   [CELL] pmi: remov...
191
  	free_irq(data->irq, NULL);
0e8266437   Christian Krafft   [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   Christian Krafft   [CELL] pmi: remov...
200
201
  	kfree(data);
  	data = NULL;
0e8266437   Christian Krafft   [POWERPC] Add PMI...
202
203
204
  
  	return 0;
  }
000061245   Grant Likely   dt/powerpc: Elimi...
205
  static struct platform_driver pmi_of_platform_driver = {
0e8266437   Christian Krafft   [POWERPC] Add PMI...
206
  	.probe		= pmi_of_probe,
84dd4676f   Stephen Rothwell   [POWERPC] Move of...
207
  	.remove		= pmi_of_remove,
4018294b5   Grant Likely   of: Remove duplic...
208
209
210
211
  	.driver = {
  		.name = "pmi",
  		.owner = THIS_MODULE,
  		.of_match_table = pmi_match,
84dd4676f   Stephen Rothwell   [POWERPC] Move of...
212
  	},
0e8266437   Christian Krafft   [POWERPC] Add PMI...
213
214
215
216
  };
  
  static int __init pmi_module_init(void)
  {
000061245   Grant Likely   dt/powerpc: Elimi...
217
  	return platform_driver_register(&pmi_of_platform_driver);
0e8266437   Christian Krafft   [POWERPC] Add PMI...
218
219
220
221
222
  }
  module_init(pmi_module_init);
  
  static void __exit pmi_module_exit(void)
  {
000061245   Grant Likely   dt/powerpc: Elimi...
223
  	platform_driver_unregister(&pmi_of_platform_driver);
0e8266437   Christian Krafft   [POWERPC] Add PMI...
224
225
  }
  module_exit(pmi_module_exit);
813f90728   Christian Krafft   [CELL] pmi: remov...
226
  int pmi_send_message(pmi_message_t msg)
0e8266437   Christian Krafft   [POWERPC] Add PMI...
227
  {
0e8266437   Christian Krafft   [POWERPC] Add PMI...
228
229
  	unsigned long flags;
  	DECLARE_COMPLETION_ONSTACK(completion);
813f90728   Christian Krafft   [CELL] pmi: remov...
230
231
  	if (!data)
  		return -ENODEV;
0e8266437   Christian Krafft   [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   Christian Krafft   [CELL] pmi: remov...
257
258
  
  	return 0;
0e8266437   Christian Krafft   [POWERPC] Add PMI...
259
260
  }
  EXPORT_SYMBOL_GPL(pmi_send_message);
813f90728   Christian Krafft   [CELL] pmi: remov...
261
  int pmi_register_handler(struct pmi_handler *handler)
0e8266437   Christian Krafft   [POWERPC] Add PMI...
262
  {
79baf4a60   Christian Krafft   [POWERPC] add che...
263
  	if (!data)
813f90728   Christian Krafft   [CELL] pmi: remov...
264
  		return -ENODEV;
79baf4a60   Christian Krafft   [POWERPC] add che...
265

0e8266437   Christian Krafft   [POWERPC] Add PMI...
266
267
268
  	spin_lock(&data->handler_spinlock);
  	list_add_tail(&handler->node, &data->handler);
  	spin_unlock(&data->handler_spinlock);
813f90728   Christian Krafft   [CELL] pmi: remov...
269
270
  
  	return 0;
0e8266437   Christian Krafft   [POWERPC] Add PMI...
271
272
  }
  EXPORT_SYMBOL_GPL(pmi_register_handler);
813f90728   Christian Krafft   [CELL] pmi: remov...
273
  void pmi_unregister_handler(struct pmi_handler *handler)
0e8266437   Christian Krafft   [POWERPC] Add PMI...
274
  {
79baf4a60   Christian Krafft   [POWERPC] add che...
275
276
  	if (!data)
  		return;
0e8266437   Christian Krafft   [POWERPC] Add PMI...
277

79baf4a60   Christian Krafft   [POWERPC] add che...
278
279
  	pr_debug("pmi: unregistering handler %p
  ", handler);
0e8266437   Christian Krafft   [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");