aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/acpi_ipmi.c
diff options
context:
space:
mode:
authorLv Zheng <lv.zheng@intel.com>2013-09-13 01:14:02 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-09-30 13:46:12 -0400
commite96a94edd7ae302168e17daa0198b9ef08b2109d (patch)
tree15ce848d8c90c9466011d6d0cc191dfbbc3b99f1 /drivers/acpi/acpi_ipmi.c
parenta1a69b297e4775298d6407357332ea1adc218396 (diff)
ACPI / IPMI: Use global IPMI operation region handler
It is found on a real machine, in its ACPI namespace, the IPMI OperationRegions (in the ACPI000D - ACPI power meter) are not defined under the IPMI system interface device (the IPI0001 with KCS type returned from _IFT control method): Device (PMI0) { Name (_HID, "ACPI000D") // _HID: Hardware ID OperationRegion (SYSI, IPMI, 0x0600, 0x0100) Field (SYSI, BufferAcc, Lock, Preserve) { AccessAs (BufferAcc, 0x01), Offset (0x58), SCMD, 8, GCMD, 8 } OperationRegion (POWR, IPMI, 0x3000, 0x0100) Field (POWR, BufferAcc, Lock, Preserve) { AccessAs (BufferAcc, 0x01), Offset (0xB3), GPMM, 8 } } Device (PCI0) { Device (ISA) { Device (NIPM) { Name (_HID, EisaId ("IPI0001")) // _HID: Hardware ID Method (_IFT, 0, NotSerialized) // _IFT: IPMI Interface Type { Return (0x01) } } } } Current ACPI_IPMI code registers IPMI operation region handler on a per-device basis, so for the above namespace the IPMI operation region handler is registered only under the scope of \_SB.PCI0.ISA.NIPM. Thus when an IPMI operation region field of \PMI0 is accessed, there are errors reported on such platform: ACPI Error: No handlers for Region [IPMI] ACPI Error: Region IPMI(7) has no handler The solution is to install an IPMI operation region handler from root node so that every object that defines IPMI OperationRegion can get an address space handler registered. When an IPMI operation region field is accessed, the Network Function (0x06 for SYSI and 0x30 for POWR) and the Command (SCMD, GCMD, GPMM) are passed to the operation region handler, there is no system interface specified by the BIOS. The patch tries to select one system interface by monitoring the system interface notification. IPMI messages passed from the ACPI codes are sent to this selected global IPMI system interface. The ACPI_IPMI will always select the first registered IPMI interface with an ACPI handle (i.e., defined in the ACPI namespace). It's hard to determine the selection when there are multiple IPMI system interfaces defined in the ACPI namespace. According to the IPMI specification: A BMC device may make available multiple system interfaces, but only one management controller is allowed to be 'active' BMC that provides BMC functionality for the system (in case of a 'partitioned' system, there can be only one active BMC per partition). Only the system interface(s) for the active BMC allowed to respond to the 'Get Device Id' command. According to the ipmi_si desigin: The ipmi_si registeration notifications can only happen after a successful "Get Device ID" command. Thus it should be OK for non-partitioned systems to do such selection. However, we do not have much knowledge on 'partitioned' systems. References: https://bugzilla.kernel.org/show_bug.cgi?id=46741 Signed-off-by: Lv Zheng <lv.zheng@intel.com> Reviewed-by: Huang Ying <ying.huang@intel.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/acpi/acpi_ipmi.c')
-rw-r--r--drivers/acpi/acpi_ipmi.c81
1 files changed, 34 insertions, 47 deletions
diff --git a/drivers/acpi/acpi_ipmi.c b/drivers/acpi/acpi_ipmi.c
index b285386eb37f..7ec4cd1e7245 100644
--- a/drivers/acpi/acpi_ipmi.c
+++ b/drivers/acpi/acpi_ipmi.c
@@ -46,7 +46,6 @@ MODULE_AUTHOR("Zhao Yakui");
46MODULE_DESCRIPTION("ACPI IPMI Opregion driver"); 46MODULE_DESCRIPTION("ACPI IPMI Opregion driver");
47MODULE_LICENSE("GPL"); 47MODULE_LICENSE("GPL");
48 48
49#define IPMI_FLAGS_HANDLER_INSTALL 0
50 49
51#define ACPI_IPMI_OK 0 50#define ACPI_IPMI_OK 0
52#define ACPI_IPMI_TIMEOUT 0x10 51#define ACPI_IPMI_TIMEOUT 0x10
@@ -66,7 +65,6 @@ struct acpi_ipmi_device {
66 ipmi_user_t user_interface; 65 ipmi_user_t user_interface;
67 int ipmi_ifnum; /* IPMI interface number */ 66 int ipmi_ifnum; /* IPMI interface number */
68 long curr_msgid; 67 long curr_msgid;
69 unsigned long flags;
70 struct ipmi_smi_info smi_data; 68 struct ipmi_smi_info smi_data;
71 bool dead; 69 bool dead;
72 struct kref kref; 70 struct kref kref;
@@ -77,6 +75,14 @@ struct ipmi_driver_data {
77 struct ipmi_smi_watcher bmc_events; 75 struct ipmi_smi_watcher bmc_events;
78 struct ipmi_user_hndl ipmi_hndlrs; 76 struct ipmi_user_hndl ipmi_hndlrs;
79 struct mutex ipmi_lock; 77 struct mutex ipmi_lock;
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;
80}; 86};
81 87
82struct acpi_ipmi_msg { 88struct acpi_ipmi_msg {
@@ -110,8 +116,6 @@ struct acpi_ipmi_buffer {
110static void ipmi_register_bmc(int iface, struct device *dev); 116static void ipmi_register_bmc(int iface, struct device *dev);
111static void ipmi_bmc_gone(int iface); 117static void ipmi_bmc_gone(int iface);
112static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data); 118static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);
113static int ipmi_install_space_handler(struct acpi_ipmi_device *ipmi);
114static void ipmi_remove_space_handler(struct acpi_ipmi_device *ipmi);
115 119
116static struct ipmi_driver_data driver_data = { 120static struct ipmi_driver_data driver_data = {
117 .ipmi_devices = LIST_HEAD_INIT(driver_data.ipmi_devices), 121 .ipmi_devices = LIST_HEAD_INIT(driver_data.ipmi_devices),
@@ -154,14 +158,12 @@ ipmi_dev_alloc(int iface, struct ipmi_smi_info *smi_data, acpi_handle handle)
154 return NULL; 158 return NULL;
155 } 159 }
156 ipmi_device->user_interface = user; 160 ipmi_device->user_interface = user;
157 ipmi_install_space_handler(ipmi_device);
158 161
159 return ipmi_device; 162 return ipmi_device;
160} 163}
161 164
162static void ipmi_dev_release(struct acpi_ipmi_device *ipmi_device) 165static void ipmi_dev_release(struct acpi_ipmi_device *ipmi_device)
163{ 166{
164 ipmi_remove_space_handler(ipmi_device);
165 ipmi_destroy_user(ipmi_device->user_interface); 167 ipmi_destroy_user(ipmi_device->user_interface);
166 put_device(ipmi_device->smi_data.dev); 168 put_device(ipmi_device->smi_data.dev);
167 kfree(ipmi_device); 169 kfree(ipmi_device);
@@ -178,6 +180,8 @@ static void ipmi_dev_release_kref(struct kref *kref)
178static void __ipmi_dev_kill(struct acpi_ipmi_device *ipmi_device) 180static void __ipmi_dev_kill(struct acpi_ipmi_device *ipmi_device)
179{ 181{
180 list_del(&ipmi_device->head); 182 list_del(&ipmi_device->head);
183 if (driver_data.selected_smi == ipmi_device)
184 driver_data.selected_smi = NULL;
181 /* 185 /*
182 * Always setting dead flag after deleting from the list or 186 * Always setting dead flag after deleting from the list or
183 * list_for_each_entry() codes must get changed. 187 * list_for_each_entry() codes must get changed.
@@ -185,17 +189,14 @@ static void __ipmi_dev_kill(struct acpi_ipmi_device *ipmi_device)
185 ipmi_device->dead = true; 189 ipmi_device->dead = true;
186} 190}
187 191
188static struct acpi_ipmi_device *acpi_ipmi_dev_get(int iface) 192static struct acpi_ipmi_device *acpi_ipmi_dev_get(void)
189{ 193{
190 struct acpi_ipmi_device *temp, *ipmi_device = NULL; 194 struct acpi_ipmi_device *ipmi_device = NULL;
191 195
192 mutex_lock(&driver_data.ipmi_lock); 196 mutex_lock(&driver_data.ipmi_lock);
193 list_for_each_entry(temp, &driver_data.ipmi_devices, head) { 197 if (driver_data.selected_smi) {
194 if (temp->ipmi_ifnum == iface) { 198 ipmi_device = driver_data.selected_smi;
195 ipmi_device = temp; 199 kref_get(&ipmi_device->kref);
196 kref_get(&ipmi_device->kref);
197 break;
198 }
199 } 200 }
200 mutex_unlock(&driver_data.ipmi_lock); 201 mutex_unlock(&driver_data.ipmi_lock);
201 202
@@ -416,6 +417,8 @@ static void ipmi_register_bmc(int iface, struct device *dev)
416 goto err_lock; 417 goto err_lock;
417 } 418 }
418 419
420 if (!driver_data.selected_smi)
421 driver_data.selected_smi = ipmi_device;
419 list_add_tail(&ipmi_device->head, &driver_data.ipmi_devices); 422 list_add_tail(&ipmi_device->head, &driver_data.ipmi_devices);
420 mutex_unlock(&driver_data.ipmi_lock); 423 mutex_unlock(&driver_data.ipmi_lock);
421 put_device(smi_data.dev); 424 put_device(smi_data.dev);
@@ -443,6 +446,10 @@ static void ipmi_bmc_gone(int iface)
443 break; 446 break;
444 } 447 }
445 } 448 }
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);
446 mutex_unlock(&driver_data.ipmi_lock); 453 mutex_unlock(&driver_data.ipmi_lock);
447 if (dev_found) { 454 if (dev_found) {
448 ipmi_flush_tx_msg(ipmi_device); 455 ipmi_flush_tx_msg(ipmi_device);
@@ -471,7 +478,6 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
471 void *handler_context, void *region_context) 478 void *handler_context, void *region_context)
472{ 479{
473 struct acpi_ipmi_msg *tx_msg; 480 struct acpi_ipmi_msg *tx_msg;
474 int iface = (long)handler_context;
475 struct acpi_ipmi_device *ipmi_device; 481 struct acpi_ipmi_device *ipmi_device;
476 int err; 482 int err;
477 acpi_status status; 483 acpi_status status;
@@ -485,7 +491,7 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
485 if ((function & ACPI_IO_MASK) == ACPI_READ) 491 if ((function & ACPI_IO_MASK) == ACPI_READ)
486 return AE_TYPE; 492 return AE_TYPE;
487 493
488 ipmi_device = acpi_ipmi_dev_get(iface); 494 ipmi_device = acpi_ipmi_dev_get();
489 if (!ipmi_device) 495 if (!ipmi_device)
490 return AE_NOT_EXIST; 496 return AE_NOT_EXIST;
491 497
@@ -534,47 +540,26 @@ out_ref:
534 return status; 540 return status;
535} 541}
536 542
537static void ipmi_remove_space_handler(struct acpi_ipmi_device *ipmi)
538{
539 if (!test_bit(IPMI_FLAGS_HANDLER_INSTALL, &ipmi->flags))
540 return;
541
542 acpi_remove_address_space_handler(ipmi->handle,
543 ACPI_ADR_SPACE_IPMI, &acpi_ipmi_space_handler);
544
545 clear_bit(IPMI_FLAGS_HANDLER_INSTALL, &ipmi->flags);
546}
547
548static int ipmi_install_space_handler(struct acpi_ipmi_device *ipmi)
549{
550 acpi_status status;
551
552 if (test_bit(IPMI_FLAGS_HANDLER_INSTALL, &ipmi->flags))
553 return 0;
554
555 status = acpi_install_address_space_handler(ipmi->handle,
556 ACPI_ADR_SPACE_IPMI, &acpi_ipmi_space_handler,
557 NULL, (void *)((long)ipmi->ipmi_ifnum));
558 if (ACPI_FAILURE(status)) {
559 struct pnp_dev *pnp_dev = ipmi->pnp_dev;
560 dev_warn(&pnp_dev->dev, "Can't register IPMI opregion space "
561 "handle\n");
562 return -EINVAL;
563 }
564 set_bit(IPMI_FLAGS_HANDLER_INSTALL, &ipmi->flags);
565 return 0;
566}
567
568static int __init acpi_ipmi_init(void) 543static int __init acpi_ipmi_init(void)
569{ 544{
570 int result = 0; 545 int result = 0;
546 acpi_status status;
571 547
572 if (acpi_disabled) 548 if (acpi_disabled)
573 return result; 549 return result;
574 550
575 mutex_init(&driver_data.ipmi_lock); 551 mutex_init(&driver_data.ipmi_lock);
576 552
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 }
577 result = ipmi_smi_watcher_register(&driver_data.bmc_events); 560 result = ipmi_smi_watcher_register(&driver_data.bmc_events);
561 if (result)
562 pr_err("Can't register IPMI system interface watcher\n");
578 563
579 return result; 564 return result;
580} 565}
@@ -608,6 +593,8 @@ static void __exit acpi_ipmi_exit(void)
608 mutex_lock(&driver_data.ipmi_lock); 593 mutex_lock(&driver_data.ipmi_lock);
609 } 594 }
610 mutex_unlock(&driver_data.ipmi_lock); 595 mutex_unlock(&driver_data.ipmi_lock);
596 acpi_remove_address_space_handler(ACPI_ROOT_OBJECT,
597 ACPI_ADR_SPACE_IPMI, &acpi_ipmi_space_handler);
611} 598}
612 599
613module_init(acpi_ipmi_init); 600module_init(acpi_ipmi_init);