aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-09-09 17:07:47 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-09-09 17:07:47 -0400
commit11b88ee275ec8590a373396888c2460ee89364d6 (patch)
tree686b3511a75150080b3b91d18b8fbc87ddfa57ba /drivers/acpi
parent464c114717ae221202ebdbd9aa216035b4626f18 (diff)
ACPI / bind: Prefer device objects with _STA to those without it
As reported at https://bugzilla.kernel.org/show_bug.cgi?id=60829, there still are cases in which do_find_child() doesn't choose the ACPI device object it is "expected" to choose if there are more such objects matching one PCI device present. This particular problem may be worked around by making do_find_child() return device obejcts witn _STA whose result indicates that the device is enabled before device objects without _STA if there's more than one device object to choose from. This change doesn't affect the case in which there's only one matching ACPI device object per PCI device. References: https://bugzilla.kernel.org/show_bug.cgi?id=60829 Reported-by: Peter Wu <lekensteyn@gmail.com> Tested-by: Felix Lisczyk <felix.lisczyk@gmail.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/acpi')
-rw-r--r--drivers/acpi/glue.c35
1 files changed, 24 insertions, 11 deletions
diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c
index 94672297e1b1..10f0f40587bb 100644
--- a/drivers/acpi/glue.c
+++ b/drivers/acpi/glue.c
@@ -79,6 +79,9 @@ static struct acpi_bus_type *acpi_get_bus_type(struct device *dev)
79 return ret; 79 return ret;
80} 80}
81 81
82#define FIND_CHILD_MIN_SCORE 1
83#define FIND_CHILD_MAX_SCORE 2
84
82static acpi_status acpi_dev_present(acpi_handle handle, u32 lvl_not_used, 85static acpi_status acpi_dev_present(acpi_handle handle, u32 lvl_not_used,
83 void *not_used, void **ret_p) 86 void *not_used, void **ret_p)
84{ 87{
@@ -92,14 +95,17 @@ static acpi_status acpi_dev_present(acpi_handle handle, u32 lvl_not_used,
92 return AE_OK; 95 return AE_OK;
93} 96}
94 97
95static bool acpi_extra_checks_passed(acpi_handle handle, bool is_bridge) 98static int do_find_child_checks(acpi_handle handle, bool is_bridge)
96{ 99{
100 bool sta_present = true;
97 unsigned long long sta; 101 unsigned long long sta;
98 acpi_status status; 102 acpi_status status;
99 103
100 status = acpi_bus_get_status_handle(handle, &sta); 104 status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
101 if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_ENABLED)) 105 if (status == AE_NOT_FOUND)
102 return false; 106 sta_present = false;
107 else if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_ENABLED))
108 return -ENODEV;
103 109
104 if (is_bridge) { 110 if (is_bridge) {
105 void *test = NULL; 111 void *test = NULL;
@@ -107,16 +113,17 @@ static bool acpi_extra_checks_passed(acpi_handle handle, bool is_bridge)
107 /* Check if this object has at least one child device. */ 113 /* Check if this object has at least one child device. */
108 acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, 114 acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
109 acpi_dev_present, NULL, NULL, &test); 115 acpi_dev_present, NULL, NULL, &test);
110 return !!test; 116 if (!test)
117 return -ENODEV;
111 } 118 }
112 return true; 119 return sta_present ? FIND_CHILD_MAX_SCORE : FIND_CHILD_MIN_SCORE;
113} 120}
114 121
115struct find_child_context { 122struct find_child_context {
116 u64 addr; 123 u64 addr;
117 bool is_bridge; 124 bool is_bridge;
118 acpi_handle ret; 125 acpi_handle ret;
119 bool ret_checked; 126 int ret_score;
120}; 127};
121 128
122static acpi_status do_find_child(acpi_handle handle, u32 lvl_not_used, 129static acpi_status do_find_child(acpi_handle handle, u32 lvl_not_used,
@@ -125,6 +132,7 @@ static acpi_status do_find_child(acpi_handle handle, u32 lvl_not_used,
125 struct find_child_context *context = data; 132 struct find_child_context *context = data;
126 unsigned long long addr; 133 unsigned long long addr;
127 acpi_status status; 134 acpi_status status;
135 int score;
128 136
129 status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &addr); 137 status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &addr);
130 if (ACPI_FAILURE(status) || addr != context->addr) 138 if (ACPI_FAILURE(status) || addr != context->addr)
@@ -144,15 +152,20 @@ static acpi_status do_find_child(acpi_handle handle, u32 lvl_not_used,
144 * its handle if so. Second, check the same for the object that we've 152 * its handle if so. Second, check the same for the object that we've
145 * just found. 153 * just found.
146 */ 154 */
147 if (!context->ret_checked) { 155 if (!context->ret_score) {
148 if (acpi_extra_checks_passed(context->ret, context->is_bridge)) 156 score = do_find_child_checks(context->ret, context->is_bridge);
157 if (score == FIND_CHILD_MAX_SCORE)
149 return AE_CTRL_TERMINATE; 158 return AE_CTRL_TERMINATE;
150 else 159 else
151 context->ret_checked = true; 160 context->ret_score = score;
152 } 161 }
153 if (acpi_extra_checks_passed(handle, context->is_bridge)) { 162 score = do_find_child_checks(handle, context->is_bridge);
163 if (score == FIND_CHILD_MAX_SCORE) {
154 context->ret = handle; 164 context->ret = handle;
155 return AE_CTRL_TERMINATE; 165 return AE_CTRL_TERMINATE;
166 } else if (score > context->ret_score) {
167 context->ret = handle;
168 context->ret_score = score;
156 } 169 }
157 return AE_OK; 170 return AE_OK;
158} 171}