]>
Commit | Line | Data |
---|---|---|
e92b297c ZY |
1 | /* |
2 | * acpi_ipmi.c - ACPI IPMI opregion | |
3 | * | |
a1a69b29 LZ |
4 | * Copyright (C) 2010, 2013 Intel Corporation |
5 | * Author: Zhao Yakui <yakui.zhao@intel.com> | |
6 | * Lv Zheng <lv.zheng@intel.com> | |
e92b297c ZY |
7 | * |
8 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License as published by | |
12 | * the Free Software Foundation; either version 2 of the License, or (at | |
13 | * your option) any later version. | |
14 | * | |
15 | * This program is distributed in the hope that it will be useful, but | |
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
18 | * General Public License for more details. | |
19 | * | |
20 | * You should have received a copy of the GNU General Public License along | |
21 | * with this program; if not, write to the Free Software Foundation, Inc., | |
22 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | |
23 | * | |
24 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
25 | */ | |
26 | ||
27 | #include <linux/kernel.h> | |
28 | #include <linux/module.h> | |
29 | #include <linux/init.h> | |
30 | #include <linux/types.h> | |
31 | #include <linux/delay.h> | |
32 | #include <linux/proc_fs.h> | |
33 | #include <linux/seq_file.h> | |
34 | #include <linux/interrupt.h> | |
35 | #include <linux/list.h> | |
36 | #include <linux/spinlock.h> | |
37 | #include <linux/io.h> | |
38 | #include <acpi/acpi_bus.h> | |
39 | #include <acpi/acpi_drivers.h> | |
40 | #include <linux/ipmi.h> | |
41 | #include <linux/device.h> | |
42 | #include <linux/pnp.h> | |
06a8566b | 43 | #include <linux/spinlock.h> |
e92b297c ZY |
44 | |
45 | MODULE_AUTHOR("Zhao Yakui"); | |
46 | MODULE_DESCRIPTION("ACPI IPMI Opregion driver"); | |
47 | MODULE_LICENSE("GPL"); | |
48 | ||
e92b297c ZY |
49 | |
50 | #define ACPI_IPMI_OK 0 | |
51 | #define ACPI_IPMI_TIMEOUT 0x10 | |
52 | #define ACPI_IPMI_UNKNOWN 0x07 | |
53 | /* the IPMI timeout is 5s */ | |
8584ec6a | 54 | #define IPMI_TIMEOUT (5000) |
6b68f03f | 55 | #define ACPI_IPMI_MAX_MSG_LENGTH 64 |
e92b297c ZY |
56 | |
57 | struct acpi_ipmi_device { | |
58 | /* the device list attached to driver_data.ipmi_devices */ | |
59 | struct list_head head; | |
60 | /* the IPMI request message list */ | |
61 | struct list_head tx_msg_list; | |
06a8566b | 62 | spinlock_t tx_msg_lock; |
e92b297c ZY |
63 | acpi_handle handle; |
64 | struct pnp_dev *pnp_dev; | |
65 | ipmi_user_t user_interface; | |
66 | int ipmi_ifnum; /* IPMI interface number */ | |
67 | long curr_msgid; | |
e92b297c | 68 | struct ipmi_smi_info smi_data; |
a1a69b29 LZ |
69 | bool dead; |
70 | struct kref kref; | |
e92b297c ZY |
71 | }; |
72 | ||
73 | struct ipmi_driver_data { | |
74 | struct list_head ipmi_devices; | |
75 | struct ipmi_smi_watcher bmc_events; | |
76 | struct ipmi_user_hndl ipmi_hndlrs; | |
77 | struct mutex ipmi_lock; | |
e96a94ed LZ |
78 | /* |
79 | * NOTE: IPMI System Interface Selection | |
80 | * There is no system interface specified by the IPMI operation | |
81 | * region access. We try to select one system interface with ACPI | |
82 | * handle set. IPMI messages passed from the ACPI codes are sent | |
83 | * to this selected global IPMI system interface. | |
84 | */ | |
85 | struct acpi_ipmi_device *selected_smi; | |
e92b297c ZY |
86 | }; |
87 | ||
88 | struct acpi_ipmi_msg { | |
89 | struct list_head head; | |
90 | /* | |
91 | * General speaking the addr type should be SI_ADDR_TYPE. And | |
92 | * the addr channel should be BMC. | |
93 | * In fact it can also be IPMB type. But we will have to | |
94 | * parse it from the Netfn command buffer. It is so complex | |
95 | * that it is skipped. | |
96 | */ | |
97 | struct ipmi_addr addr; | |
98 | long tx_msgid; | |
99 | /* it is used to track whether the IPMI message is finished */ | |
100 | struct completion tx_complete; | |
101 | struct kernel_ipmi_msg tx_message; | |
102 | int msg_done; | |
6b68f03f LZ |
103 | /* tx/rx data . And copy it from/to ACPI object buffer */ |
104 | u8 data[ACPI_IPMI_MAX_MSG_LENGTH]; | |
105 | u8 rx_len; | |
e92b297c ZY |
106 | struct acpi_ipmi_device *device; |
107 | }; | |
108 | ||
109 | /* IPMI request/response buffer per ACPI 4.0, sec 5.5.2.4.3.2 */ | |
110 | struct acpi_ipmi_buffer { | |
111 | u8 status; | |
112 | u8 length; | |
6b68f03f | 113 | u8 data[ACPI_IPMI_MAX_MSG_LENGTH]; |
e92b297c ZY |
114 | }; |
115 | ||
116 | static void ipmi_register_bmc(int iface, struct device *dev); | |
117 | static void ipmi_bmc_gone(int iface); | |
118 | static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data); | |
e92b297c ZY |
119 | |
120 | static struct ipmi_driver_data driver_data = { | |
121 | .ipmi_devices = LIST_HEAD_INIT(driver_data.ipmi_devices), | |
122 | .bmc_events = { | |
123 | .owner = THIS_MODULE, | |
124 | .new_smi = ipmi_register_bmc, | |
125 | .smi_gone = ipmi_bmc_gone, | |
126 | }, | |
127 | .ipmi_hndlrs = { | |
128 | .ipmi_recv_hndl = ipmi_msg_handler, | |
129 | }, | |
130 | }; | |
131 | ||
a1a69b29 LZ |
132 | static struct acpi_ipmi_device * |
133 | ipmi_dev_alloc(int iface, struct ipmi_smi_info *smi_data, acpi_handle handle) | |
134 | { | |
135 | struct acpi_ipmi_device *ipmi_device; | |
136 | int err; | |
137 | ipmi_user_t user; | |
138 | ||
139 | ipmi_device = kzalloc(sizeof(*ipmi_device), GFP_KERNEL); | |
140 | if (!ipmi_device) | |
141 | return NULL; | |
142 | ||
143 | kref_init(&ipmi_device->kref); | |
144 | INIT_LIST_HEAD(&ipmi_device->head); | |
145 | INIT_LIST_HEAD(&ipmi_device->tx_msg_list); | |
146 | spin_lock_init(&ipmi_device->tx_msg_lock); | |
147 | ||
148 | ipmi_device->handle = handle; | |
149 | ipmi_device->pnp_dev = to_pnp_dev(get_device(smi_data->dev)); | |
150 | memcpy(&ipmi_device->smi_data, smi_data, sizeof(struct ipmi_smi_info)); | |
151 | ipmi_device->ipmi_ifnum = iface; | |
152 | ||
153 | err = ipmi_create_user(iface, &driver_data.ipmi_hndlrs, | |
154 | ipmi_device, &user); | |
155 | if (err) { | |
156 | put_device(smi_data->dev); | |
157 | kfree(ipmi_device); | |
158 | return NULL; | |
159 | } | |
160 | ipmi_device->user_interface = user; | |
a1a69b29 LZ |
161 | |
162 | return ipmi_device; | |
163 | } | |
164 | ||
165 | static void ipmi_dev_release(struct acpi_ipmi_device *ipmi_device) | |
166 | { | |
a1a69b29 LZ |
167 | ipmi_destroy_user(ipmi_device->user_interface); |
168 | put_device(ipmi_device->smi_data.dev); | |
169 | kfree(ipmi_device); | |
170 | } | |
171 | ||
172 | static void ipmi_dev_release_kref(struct kref *kref) | |
173 | { | |
174 | struct acpi_ipmi_device *ipmi = | |
175 | container_of(kref, struct acpi_ipmi_device, kref); | |
176 | ||
177 | ipmi_dev_release(ipmi); | |
178 | } | |
179 | ||
180 | static void __ipmi_dev_kill(struct acpi_ipmi_device *ipmi_device) | |
181 | { | |
182 | list_del(&ipmi_device->head); | |
e96a94ed LZ |
183 | if (driver_data.selected_smi == ipmi_device) |
184 | driver_data.selected_smi = NULL; | |
a1a69b29 LZ |
185 | /* |
186 | * Always setting dead flag after deleting from the list or | |
187 | * list_for_each_entry() codes must get changed. | |
188 | */ | |
189 | ipmi_device->dead = true; | |
190 | } | |
191 | ||
e96a94ed | 192 | static struct acpi_ipmi_device *acpi_ipmi_dev_get(void) |
a1a69b29 | 193 | { |
e96a94ed | 194 | struct acpi_ipmi_device *ipmi_device = NULL; |
a1a69b29 LZ |
195 | |
196 | mutex_lock(&driver_data.ipmi_lock); | |
e96a94ed LZ |
197 | if (driver_data.selected_smi) { |
198 | ipmi_device = driver_data.selected_smi; | |
199 | kref_get(&ipmi_device->kref); | |
a1a69b29 LZ |
200 | } |
201 | mutex_unlock(&driver_data.ipmi_lock); | |
202 | ||
203 | return ipmi_device; | |
204 | } | |
205 | ||
206 | static void acpi_ipmi_dev_put(struct acpi_ipmi_device *ipmi_device) | |
207 | { | |
208 | kref_put(&ipmi_device->kref, ipmi_dev_release_kref); | |
209 | } | |
210 | ||
e92b297c ZY |
211 | static struct acpi_ipmi_msg *acpi_alloc_ipmi_msg(struct acpi_ipmi_device *ipmi) |
212 | { | |
213 | struct acpi_ipmi_msg *ipmi_msg; | |
214 | struct pnp_dev *pnp_dev = ipmi->pnp_dev; | |
215 | ||
216 | ipmi_msg = kzalloc(sizeof(struct acpi_ipmi_msg), GFP_KERNEL); | |
217 | if (!ipmi_msg) { | |
218 | dev_warn(&pnp_dev->dev, "Can't allocate memory for ipmi_msg\n"); | |
219 | return NULL; | |
220 | } | |
221 | init_completion(&ipmi_msg->tx_complete); | |
222 | INIT_LIST_HEAD(&ipmi_msg->head); | |
223 | ipmi_msg->device = ipmi; | |
8584ec6a | 224 | ipmi_msg->msg_done = ACPI_IPMI_UNKNOWN; |
e92b297c ZY |
225 | return ipmi_msg; |
226 | } | |
227 | ||
228 | #define IPMI_OP_RGN_NETFN(offset) ((offset >> 8) & 0xff) | |
229 | #define IPMI_OP_RGN_CMD(offset) (offset & 0xff) | |
6b68f03f | 230 | static int acpi_format_ipmi_request(struct acpi_ipmi_msg *tx_msg, |
e92b297c ZY |
231 | acpi_physical_address address, |
232 | acpi_integer *value) | |
233 | { | |
234 | struct kernel_ipmi_msg *msg; | |
235 | struct acpi_ipmi_buffer *buffer; | |
236 | struct acpi_ipmi_device *device; | |
06a8566b | 237 | unsigned long flags; |
e92b297c ZY |
238 | |
239 | msg = &tx_msg->tx_message; | |
240 | /* | |
241 | * IPMI network function and command are encoded in the address | |
242 | * within the IPMI OpRegion; see ACPI 4.0, sec 5.5.2.4.3. | |
243 | */ | |
244 | msg->netfn = IPMI_OP_RGN_NETFN(address); | |
245 | msg->cmd = IPMI_OP_RGN_CMD(address); | |
6b68f03f | 246 | msg->data = tx_msg->data; |
e92b297c ZY |
247 | /* |
248 | * value is the parameter passed by the IPMI opregion space handler. | |
249 | * It points to the IPMI request message buffer | |
250 | */ | |
251 | buffer = (struct acpi_ipmi_buffer *)value; | |
252 | /* copy the tx message data */ | |
6b68f03f LZ |
253 | if (buffer->length > ACPI_IPMI_MAX_MSG_LENGTH) { |
254 | dev_WARN_ONCE(&tx_msg->device->pnp_dev->dev, true, | |
255 | "Unexpected request (msg len %d).\n", | |
256 | buffer->length); | |
257 | return -EINVAL; | |
258 | } | |
e92b297c | 259 | msg->data_len = buffer->length; |
6b68f03f | 260 | memcpy(tx_msg->data, buffer->data, msg->data_len); |
e92b297c ZY |
261 | /* |
262 | * now the default type is SYSTEM_INTERFACE and channel type is BMC. | |
263 | * If the netfn is APP_REQUEST and the cmd is SEND_MESSAGE, | |
264 | * the addr type should be changed to IPMB. Then we will have to parse | |
265 | * the IPMI request message buffer to get the IPMB address. | |
266 | * If so, please fix me. | |
267 | */ | |
268 | tx_msg->addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; | |
269 | tx_msg->addr.channel = IPMI_BMC_CHANNEL; | |
270 | tx_msg->addr.data[0] = 0; | |
271 | ||
272 | /* Get the msgid */ | |
273 | device = tx_msg->device; | |
06a8566b | 274 | spin_lock_irqsave(&device->tx_msg_lock, flags); |
e92b297c ZY |
275 | device->curr_msgid++; |
276 | tx_msg->tx_msgid = device->curr_msgid; | |
06a8566b | 277 | spin_unlock_irqrestore(&device->tx_msg_lock, flags); |
6b68f03f | 278 | return 0; |
e92b297c ZY |
279 | } |
280 | ||
281 | static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg, | |
8584ec6a | 282 | acpi_integer *value) |
e92b297c ZY |
283 | { |
284 | struct acpi_ipmi_buffer *buffer; | |
285 | ||
286 | /* | |
287 | * value is also used as output parameter. It represents the response | |
288 | * IPMI message returned by IPMI command. | |
289 | */ | |
290 | buffer = (struct acpi_ipmi_buffer *)value; | |
e92b297c | 291 | /* |
8584ec6a LZ |
292 | * If the flag of msg_done is not set, it means that the IPMI command is |
293 | * not executed correctly. | |
e92b297c | 294 | */ |
8584ec6a LZ |
295 | buffer->status = msg->msg_done; |
296 | if (msg->msg_done != ACPI_IPMI_OK) | |
e92b297c | 297 | return; |
e92b297c ZY |
298 | /* |
299 | * If the IPMI response message is obtained correctly, the status code | |
300 | * will be ACPI_IPMI_OK | |
301 | */ | |
e92b297c | 302 | buffer->length = msg->rx_len; |
6b68f03f | 303 | memcpy(buffer->data, msg->data, msg->rx_len); |
e92b297c ZY |
304 | } |
305 | ||
306 | static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi) | |
307 | { | |
308 | struct acpi_ipmi_msg *tx_msg, *temp; | |
5ac557ef | 309 | unsigned long flags; |
e92b297c | 310 | |
a1a69b29 LZ |
311 | /* |
312 | * NOTE: On-going ipmi_recv_msg | |
313 | * ipmi_msg_handler() may still be invoked by ipmi_si after | |
314 | * flushing. But it is safe to do a fast flushing on module_exit() | |
315 | * without waiting for all ipmi_recv_msg(s) to complete from | |
316 | * ipmi_msg_handler() as it is ensured by ipmi_si that all | |
317 | * ipmi_recv_msg(s) are freed after invoking ipmi_destroy_user(). | |
318 | */ | |
5ac557ef | 319 | spin_lock_irqsave(&ipmi->tx_msg_lock, flags); |
e92b297c ZY |
320 | list_for_each_entry_safe(tx_msg, temp, &ipmi->tx_msg_list, head) { |
321 | /* wake up the sleep thread on the Tx msg */ | |
322 | complete(&tx_msg->tx_complete); | |
323 | } | |
5ac557ef | 324 | spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags); |
e92b297c ZY |
325 | } |
326 | ||
327 | static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) | |
328 | { | |
329 | struct acpi_ipmi_device *ipmi_device = user_msg_data; | |
330 | int msg_found = 0; | |
331 | struct acpi_ipmi_msg *tx_msg; | |
332 | struct pnp_dev *pnp_dev = ipmi_device->pnp_dev; | |
06a8566b | 333 | unsigned long flags; |
e92b297c ZY |
334 | |
335 | if (msg->user != ipmi_device->user_interface) { | |
336 | dev_warn(&pnp_dev->dev, "Unexpected response is returned. " | |
337 | "returned user %p, expected user %p\n", | |
338 | msg->user, ipmi_device->user_interface); | |
6b68f03f | 339 | goto out_msg; |
e92b297c | 340 | } |
06a8566b | 341 | spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags); |
e92b297c ZY |
342 | list_for_each_entry(tx_msg, &ipmi_device->tx_msg_list, head) { |
343 | if (msg->msgid == tx_msg->tx_msgid) { | |
344 | msg_found = 1; | |
345 | break; | |
346 | } | |
347 | } | |
348 | ||
e92b297c ZY |
349 | if (!msg_found) { |
350 | dev_warn(&pnp_dev->dev, "Unexpected response (msg id %ld) is " | |
351 | "returned.\n", msg->msgid); | |
5ac557ef | 352 | goto out_lock; |
e92b297c ZY |
353 | } |
354 | ||
6b68f03f LZ |
355 | /* copy the response data to Rx_data buffer */ |
356 | if (msg->msg.data_len > ACPI_IPMI_MAX_MSG_LENGTH) { | |
357 | dev_WARN_ONCE(&pnp_dev->dev, true, | |
358 | "Unexpected response (msg len %d).\n", | |
359 | msg->msg.data_len); | |
8584ec6a | 360 | goto out_comp; |
e92b297c | 361 | } |
8584ec6a LZ |
362 | /* response msg is an error msg */ |
363 | msg->recv_type = IPMI_RESPONSE_RECV_TYPE; | |
364 | if (msg->recv_type == IPMI_RESPONSE_RECV_TYPE && | |
365 | msg->msg.data_len == 1) { | |
366 | if (msg->msg.data[0] == IPMI_TIMEOUT_COMPLETION_CODE) { | |
367 | dev_WARN_ONCE(&pnp_dev->dev, true, | |
368 | "Unexpected response (timeout).\n"); | |
369 | tx_msg->msg_done = ACPI_IPMI_TIMEOUT; | |
370 | } | |
371 | goto out_comp; | |
372 | } | |
373 | tx_msg->rx_len = msg->msg.data_len; | |
374 | memcpy(tx_msg->data, msg->msg.data, tx_msg->rx_len); | |
375 | tx_msg->msg_done = ACPI_IPMI_OK; | |
376 | out_comp: | |
e92b297c | 377 | complete(&tx_msg->tx_complete); |
5ac557ef LZ |
378 | out_lock: |
379 | spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags); | |
6b68f03f | 380 | out_msg: |
e92b297c ZY |
381 | ipmi_free_recv_msg(msg); |
382 | }; | |
383 | ||
384 | static void ipmi_register_bmc(int iface, struct device *dev) | |
385 | { | |
386 | struct acpi_ipmi_device *ipmi_device, *temp; | |
387 | struct pnp_dev *pnp_dev; | |
e92b297c ZY |
388 | int err; |
389 | struct ipmi_smi_info smi_data; | |
390 | acpi_handle handle; | |
391 | ||
392 | err = ipmi_get_smi_info(iface, &smi_data); | |
393 | ||
394 | if (err) | |
395 | return; | |
396 | ||
a1a69b29 LZ |
397 | if (smi_data.addr_src != SI_ACPI) |
398 | goto err_ref; | |
e92b297c | 399 | handle = smi_data.addr_info.acpi_info.acpi_handle; |
a1a69b29 LZ |
400 | if (!handle) |
401 | goto err_ref; | |
402 | pnp_dev = to_pnp_dev(smi_data.dev); | |
403 | ||
404 | ipmi_device = ipmi_dev_alloc(iface, &smi_data, handle); | |
405 | if (!ipmi_device) { | |
406 | dev_warn(&pnp_dev->dev, "Can't create IPMI user interface\n"); | |
407 | goto err_ref; | |
408 | } | |
e92b297c ZY |
409 | |
410 | mutex_lock(&driver_data.ipmi_lock); | |
411 | list_for_each_entry(temp, &driver_data.ipmi_devices, head) { | |
412 | /* | |
413 | * if the corresponding ACPI handle is already added | |
414 | * to the device list, don't add it again. | |
415 | */ | |
416 | if (temp->handle == handle) | |
a1a69b29 | 417 | goto err_lock; |
e92b297c ZY |
418 | } |
419 | ||
e96a94ed LZ |
420 | if (!driver_data.selected_smi) |
421 | driver_data.selected_smi = ipmi_device; | |
a1a69b29 | 422 | list_add_tail(&ipmi_device->head, &driver_data.ipmi_devices); |
e92b297c | 423 | mutex_unlock(&driver_data.ipmi_lock); |
a1a69b29 | 424 | put_device(smi_data.dev); |
e92b297c ZY |
425 | return; |
426 | ||
a1a69b29 | 427 | err_lock: |
e92b297c | 428 | mutex_unlock(&driver_data.ipmi_lock); |
a1a69b29 LZ |
429 | ipmi_dev_release(ipmi_device); |
430 | err_ref: | |
e92b297c ZY |
431 | put_device(smi_data.dev); |
432 | return; | |
433 | } | |
434 | ||
435 | static void ipmi_bmc_gone(int iface) | |
436 | { | |
437 | struct acpi_ipmi_device *ipmi_device, *temp; | |
a1a69b29 | 438 | bool dev_found = false; |
e92b297c ZY |
439 | |
440 | mutex_lock(&driver_data.ipmi_lock); | |
441 | list_for_each_entry_safe(ipmi_device, temp, | |
442 | &driver_data.ipmi_devices, head) { | |
a1a69b29 LZ |
443 | if (ipmi_device->ipmi_ifnum != iface) { |
444 | dev_found = true; | |
445 | __ipmi_dev_kill(ipmi_device); | |
446 | break; | |
447 | } | |
e92b297c | 448 | } |
e96a94ed LZ |
449 | if (!driver_data.selected_smi) |
450 | driver_data.selected_smi = list_first_entry_or_null( | |
451 | &driver_data.ipmi_devices, | |
452 | struct acpi_ipmi_device, head); | |
e92b297c | 453 | mutex_unlock(&driver_data.ipmi_lock); |
a1a69b29 LZ |
454 | if (dev_found) { |
455 | ipmi_flush_tx_msg(ipmi_device); | |
456 | acpi_ipmi_dev_put(ipmi_device); | |
457 | } | |
e92b297c ZY |
458 | } |
459 | /* -------------------------------------------------------------------------- | |
460 | * Address Space Management | |
461 | * -------------------------------------------------------------------------- */ | |
462 | /* | |
463 | * This is the IPMI opregion space handler. | |
464 | * @function: indicates the read/write. In fact as the IPMI message is driven | |
465 | * by command, only write is meaningful. | |
466 | * @address: This contains the netfn/command of IPMI request message. | |
467 | * @bits : not used. | |
468 | * @value : it is an in/out parameter. It points to the IPMI message buffer. | |
469 | * Before the IPMI message is sent, it represents the actual request | |
470 | * IPMI message. After the IPMI message is finished, it represents | |
471 | * the response IPMI message returned by IPMI command. | |
472 | * @handler_context: IPMI device context. | |
473 | */ | |
474 | ||
475 | static acpi_status | |
476 | acpi_ipmi_space_handler(u32 function, acpi_physical_address address, | |
477 | u32 bits, acpi_integer *value, | |
478 | void *handler_context, void *region_context) | |
479 | { | |
480 | struct acpi_ipmi_msg *tx_msg; | |
a1a69b29 | 481 | struct acpi_ipmi_device *ipmi_device; |
8584ec6a | 482 | int err; |
e92b297c | 483 | acpi_status status; |
06a8566b | 484 | unsigned long flags; |
e92b297c ZY |
485 | /* |
486 | * IPMI opregion message. | |
487 | * IPMI message is firstly written to the BMC and system software | |
488 | * can get the respsonse. So it is unmeaningful for the read access | |
489 | * of IPMI opregion. | |
490 | */ | |
491 | if ((function & ACPI_IO_MASK) == ACPI_READ) | |
492 | return AE_TYPE; | |
493 | ||
e96a94ed | 494 | ipmi_device = acpi_ipmi_dev_get(); |
a1a69b29 | 495 | if (!ipmi_device) |
e92b297c ZY |
496 | return AE_NOT_EXIST; |
497 | ||
498 | tx_msg = acpi_alloc_ipmi_msg(ipmi_device); | |
a1a69b29 LZ |
499 | if (!tx_msg) { |
500 | status = AE_NO_MEMORY; | |
501 | goto out_ref; | |
502 | } | |
e92b297c | 503 | |
6b68f03f LZ |
504 | if (acpi_format_ipmi_request(tx_msg, address, value) != 0) { |
505 | status = AE_TYPE; | |
506 | goto out_msg; | |
507 | } | |
a1a69b29 LZ |
508 | mutex_lock(&driver_data.ipmi_lock); |
509 | /* Do not add a tx_msg that can not be flushed. */ | |
510 | if (ipmi_device->dead) { | |
511 | status = AE_NOT_EXIST; | |
512 | mutex_unlock(&driver_data.ipmi_lock); | |
513 | goto out_msg; | |
514 | } | |
06a8566b | 515 | spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags); |
e92b297c | 516 | list_add_tail(&tx_msg->head, &ipmi_device->tx_msg_list); |
06a8566b | 517 | spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags); |
a1a69b29 | 518 | mutex_unlock(&driver_data.ipmi_lock); |
e92b297c ZY |
519 | err = ipmi_request_settime(ipmi_device->user_interface, |
520 | &tx_msg->addr, | |
521 | tx_msg->tx_msgid, | |
522 | &tx_msg->tx_message, | |
8584ec6a | 523 | NULL, 0, 0, IPMI_TIMEOUT); |
e92b297c ZY |
524 | if (err) { |
525 | status = AE_ERROR; | |
6b68f03f | 526 | goto out_list; |
e92b297c | 527 | } |
8584ec6a LZ |
528 | wait_for_completion(&tx_msg->tx_complete); |
529 | acpi_format_ipmi_response(tx_msg, value); | |
e92b297c ZY |
530 | status = AE_OK; |
531 | ||
6b68f03f | 532 | out_list: |
06a8566b | 533 | spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags); |
e92b297c | 534 | list_del(&tx_msg->head); |
06a8566b | 535 | spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags); |
6b68f03f | 536 | out_msg: |
e92b297c | 537 | kfree(tx_msg); |
a1a69b29 LZ |
538 | out_ref: |
539 | acpi_ipmi_dev_put(ipmi_device); | |
e92b297c ZY |
540 | return status; |
541 | } | |
542 | ||
e92b297c ZY |
543 | static int __init acpi_ipmi_init(void) |
544 | { | |
545 | int result = 0; | |
e96a94ed | 546 | acpi_status status; |
e92b297c ZY |
547 | |
548 | if (acpi_disabled) | |
549 | return result; | |
550 | ||
551 | mutex_init(&driver_data.ipmi_lock); | |
552 | ||
e96a94ed LZ |
553 | status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT, |
554 | ACPI_ADR_SPACE_IPMI, &acpi_ipmi_space_handler, | |
555 | NULL, NULL); | |
556 | if (ACPI_FAILURE(status)) { | |
557 | pr_warn("Can't register IPMI opregion space handle\n"); | |
558 | return -EINVAL; | |
559 | } | |
e92b297c | 560 | result = ipmi_smi_watcher_register(&driver_data.bmc_events); |
e96a94ed LZ |
561 | if (result) |
562 | pr_err("Can't register IPMI system interface watcher\n"); | |
e92b297c ZY |
563 | |
564 | return result; | |
565 | } | |
566 | ||
567 | static void __exit acpi_ipmi_exit(void) | |
568 | { | |
a1a69b29 | 569 | struct acpi_ipmi_device *ipmi_device; |
e92b297c ZY |
570 | |
571 | if (acpi_disabled) | |
572 | return; | |
573 | ||
574 | ipmi_smi_watcher_unregister(&driver_data.bmc_events); | |
575 | ||
576 | /* | |
577 | * When one smi_watcher is unregistered, it is only deleted | |
578 | * from the smi_watcher list. But the smi_gone callback function | |
579 | * is not called. So explicitly uninstall the ACPI IPMI oregion | |
580 | * handler and free it. | |
581 | */ | |
582 | mutex_lock(&driver_data.ipmi_lock); | |
a1a69b29 LZ |
583 | while (!list_empty(&driver_data.ipmi_devices)) { |
584 | ipmi_device = list_first_entry(&driver_data.ipmi_devices, | |
585 | struct acpi_ipmi_device, | |
586 | head); | |
587 | __ipmi_dev_kill(ipmi_device); | |
588 | mutex_unlock(&driver_data.ipmi_lock); | |
589 | ||
590 | ipmi_flush_tx_msg(ipmi_device); | |
591 | acpi_ipmi_dev_put(ipmi_device); | |
592 | ||
593 | mutex_lock(&driver_data.ipmi_lock); | |
e92b297c ZY |
594 | } |
595 | mutex_unlock(&driver_data.ipmi_lock); | |
e96a94ed LZ |
596 | acpi_remove_address_space_handler(ACPI_ROOT_OBJECT, |
597 | ACPI_ADR_SPACE_IPMI, &acpi_ipmi_space_handler); | |
e92b297c ZY |
598 | } |
599 | ||
600 | module_init(acpi_ipmi_init); | |
601 | module_exit(acpi_ipmi_exit); |