1 /* 2 * pnpacpi -- PnP ACPI driver 3 * 4 * Copyright (c) 2004 Matthieu Castet <castet.matthieu@free.fr> 5 * Copyright (c) 2004 Li Shaohua <shaohua.li@intel.com> 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the 9 * Free Software Foundation; either version 2, or (at your option) any 10 * later version. 11 * 12 * This program is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21 22 #include <linux/export.h> 23 #include <linux/acpi.h> 24 #include <linux/pnp.h> 25 #include <linux/slab.h> 26 #include <linux/mod_devicetable.h> 27 28 #include "../base.h" 29 #include "pnpacpi.h" 30 31 static int num; 32 33 /* 34 * Compatible Device IDs 35 */ 36 #define TEST_HEX(c) \ 37 if (!(('0' <= (c) && (c) <= '9') || ('A' <= (c) && (c) <= 'F'))) \ 38 return 0 39 #define TEST_ALPHA(c) \ 40 if (!('A' <= (c) && (c) <= 'Z')) \ 41 return 0 42 static int __init ispnpidacpi(const char *id) 43 { 44 TEST_ALPHA(id[0]); 45 TEST_ALPHA(id[1]); 46 TEST_ALPHA(id[2]); 47 TEST_HEX(id[3]); 48 TEST_HEX(id[4]); 49 TEST_HEX(id[5]); 50 TEST_HEX(id[6]); 51 if (id[7] != '\0') 52 return 0; 53 return 1; 54 } 55 56 static int pnpacpi_get_resources(struct pnp_dev *dev) 57 { 58 pnp_dbg(&dev->dev, "get resources\n"); 59 return pnpacpi_parse_allocated_resource(dev); 60 } 61 62 static int pnpacpi_set_resources(struct pnp_dev *dev) 63 { 64 struct acpi_device *acpi_dev; 65 acpi_handle handle; 66 int ret = 0; 67 68 pnp_dbg(&dev->dev, "set resources\n"); 69 70 handle = ACPI_HANDLE(&dev->dev); 71 if (!handle || acpi_bus_get_device(handle, &acpi_dev)) { 72 dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__); 73 return -ENODEV; 74 } 75 76 if (WARN_ON_ONCE(acpi_dev != dev->data)) 77 dev->data = acpi_dev; 78 79 if (acpi_has_method(handle, METHOD_NAME__SRS)) { 80 struct acpi_buffer buffer; 81 82 ret = pnpacpi_build_resource_template(dev, &buffer); 83 if (ret) 84 return ret; 85 86 ret = pnpacpi_encode_resources(dev, &buffer); 87 if (!ret) { 88 acpi_status status; 89 90 status = acpi_set_current_resources(handle, &buffer); 91 if (ACPI_FAILURE(status)) 92 ret = -EIO; 93 } 94 kfree(buffer.pointer); 95 } 96 if (!ret && acpi_bus_power_manageable(handle)) 97 ret = acpi_bus_set_power(handle, ACPI_STATE_D0); 98 99 return ret; 100 } 101 102 static int pnpacpi_disable_resources(struct pnp_dev *dev) 103 { 104 struct acpi_device *acpi_dev; 105 acpi_handle handle; 106 acpi_status status; 107 108 dev_dbg(&dev->dev, "disable resources\n"); 109 110 handle = ACPI_HANDLE(&dev->dev); 111 if (!handle || acpi_bus_get_device(handle, &acpi_dev)) { 112 dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__); 113 return 0; 114 } 115 116 /* acpi_unregister_gsi(pnp_irq(dev, 0)); */ 117 if (acpi_bus_power_manageable(handle)) 118 acpi_bus_set_power(handle, ACPI_STATE_D3_COLD); 119 120 /* continue even if acpi_bus_set_power() fails */ 121 status = acpi_evaluate_object(handle, "_DIS", NULL, NULL); 122 if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) 123 return -ENODEV; 124 125 return 0; 126 } 127 128 #ifdef CONFIG_ACPI_SLEEP 129 static bool pnpacpi_can_wakeup(struct pnp_dev *dev) 130 { 131 struct acpi_device *acpi_dev; 132 acpi_handle handle; 133 134 handle = ACPI_HANDLE(&dev->dev); 135 if (!handle || acpi_bus_get_device(handle, &acpi_dev)) { 136 dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__); 137 return false; 138 } 139 140 return acpi_bus_can_wakeup(handle); 141 } 142 143 static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state) 144 { 145 struct acpi_device *acpi_dev; 146 acpi_handle handle; 147 int error = 0; 148 149 handle = ACPI_HANDLE(&dev->dev); 150 if (!handle || acpi_bus_get_device(handle, &acpi_dev)) { 151 dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__); 152 return 0; 153 } 154 155 if (device_can_wakeup(&dev->dev)) { 156 error = acpi_pm_device_sleep_wake(&dev->dev, 157 device_may_wakeup(&dev->dev)); 158 if (error) 159 return error; 160 } 161 162 if (acpi_bus_power_manageable(handle)) { 163 int power_state = acpi_pm_device_sleep_state(&dev->dev, NULL, 164 ACPI_STATE_D3_COLD); 165 if (power_state < 0) 166 power_state = (state.event == PM_EVENT_ON) ? 167 ACPI_STATE_D0 : ACPI_STATE_D3_COLD; 168 169 /* 170 * acpi_bus_set_power() often fails (keyboard port can't be 171 * powered-down?), and in any case, our return value is ignored 172 * by pnp_bus_suspend(). Hence we don't revert the wakeup 173 * setting if the set_power fails. 174 */ 175 error = acpi_bus_set_power(handle, power_state); 176 } 177 178 return error; 179 } 180 181 static int pnpacpi_resume(struct pnp_dev *dev) 182 { 183 struct acpi_device *acpi_dev; 184 acpi_handle handle = ACPI_HANDLE(&dev->dev); 185 int error = 0; 186 187 if (!handle || acpi_bus_get_device(handle, &acpi_dev)) { 188 dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__); 189 return -ENODEV; 190 } 191 192 if (device_may_wakeup(&dev->dev)) 193 acpi_pm_device_sleep_wake(&dev->dev, false); 194 195 if (acpi_bus_power_manageable(handle)) 196 error = acpi_bus_set_power(handle, ACPI_STATE_D0); 197 198 return error; 199 } 200 #endif 201 202 struct pnp_protocol pnpacpi_protocol = { 203 .name = "Plug and Play ACPI", 204 .get = pnpacpi_get_resources, 205 .set = pnpacpi_set_resources, 206 .disable = pnpacpi_disable_resources, 207 #ifdef CONFIG_ACPI_SLEEP 208 .can_wakeup = pnpacpi_can_wakeup, 209 .suspend = pnpacpi_suspend, 210 .resume = pnpacpi_resume, 211 #endif 212 }; 213 EXPORT_SYMBOL(pnpacpi_protocol); 214 215 static char *__init pnpacpi_get_id(struct acpi_device *device) 216 { 217 struct acpi_hardware_id *id; 218 219 list_for_each_entry(id, &device->pnp.ids, list) { 220 if (ispnpidacpi(id->id)) 221 return id->id; 222 } 223 224 return NULL; 225 } 226 227 static int __init pnpacpi_add_device(struct acpi_device *device) 228 { 229 struct pnp_dev *dev; 230 char *pnpid; 231 struct acpi_hardware_id *id; 232 int error; 233 234 /* Skip devices that are already bound */ 235 if (device->physical_node_count) 236 return 0; 237 238 /* 239 * If a PnPacpi device is not present , the device 240 * driver should not be loaded. 241 */ 242 if (!acpi_has_method(device->handle, "_CRS")) 243 return 0; 244 245 pnpid = pnpacpi_get_id(device); 246 if (!pnpid) 247 return 0; 248 249 if (!device->status.present) 250 return 0; 251 252 dev = pnp_alloc_dev(&pnpacpi_protocol, num, pnpid); 253 if (!dev) 254 return -ENOMEM; 255 256 dev->data = device; 257 /* .enabled means the device can decode the resources */ 258 dev->active = device->status.enabled; 259 if (acpi_has_method(device->handle, "_SRS")) 260 dev->capabilities |= PNP_CONFIGURABLE; 261 dev->capabilities |= PNP_READ; 262 if (device->flags.dynamic_status && (dev->capabilities & PNP_CONFIGURABLE)) 263 dev->capabilities |= PNP_WRITE; 264 if (device->flags.removable) 265 dev->capabilities |= PNP_REMOVABLE; 266 if (acpi_has_method(device->handle, "_DIS")) 267 dev->capabilities |= PNP_DISABLE; 268 269 if (strlen(acpi_device_name(device))) 270 strncpy(dev->name, acpi_device_name(device), sizeof(dev->name)); 271 else 272 strncpy(dev->name, acpi_device_bid(device), sizeof(dev->name)); 273 274 if (dev->active) 275 pnpacpi_parse_allocated_resource(dev); 276 277 if (dev->capabilities & PNP_CONFIGURABLE) 278 pnpacpi_parse_resource_option_data(dev); 279 280 list_for_each_entry(id, &device->pnp.ids, list) { 281 if (!strcmp(id->id, pnpid)) 282 continue; 283 if (!ispnpidacpi(id->id)) 284 continue; 285 pnp_add_id(dev, id->id); 286 } 287 288 /* clear out the damaged flags */ 289 if (!dev->active) 290 pnp_init_resources(dev); 291 292 error = pnp_add_device(dev); 293 if (error) { 294 put_device(&dev->dev); 295 return error; 296 } 297 298 num++; 299 300 return 0; 301 } 302 303 static acpi_status __init pnpacpi_add_device_handler(acpi_handle handle, 304 u32 lvl, void *context, 305 void **rv) 306 { 307 struct acpi_device *device; 308 309 if (acpi_bus_get_device(handle, &device)) 310 return AE_CTRL_DEPTH; 311 if (acpi_is_pnp_device(device)) 312 pnpacpi_add_device(device); 313 return AE_OK; 314 } 315 316 static int __init acpi_pnp_match(struct device *dev, void *_pnp) 317 { 318 struct acpi_device *acpi = to_acpi_device(dev); 319 struct pnp_dev *pnp = _pnp; 320 321 /* true means it matched */ 322 return !acpi->physical_node_count 323 && compare_pnp_id(pnp->id, acpi_device_hid(acpi)); 324 } 325 326 static struct acpi_device * __init acpi_pnp_find_companion(struct device *dev) 327 { 328 dev = bus_find_device(&acpi_bus_type, NULL, to_pnp_dev(dev), 329 acpi_pnp_match); 330 if (!dev) 331 return NULL; 332 333 put_device(dev); 334 return to_acpi_device(dev); 335 } 336 337 /* complete initialization of a PNPACPI device includes having 338 * pnpdev->dev.archdata.acpi_handle point to its ACPI sibling. 339 */ 340 static bool acpi_pnp_bus_match(struct device *dev) 341 { 342 return dev->bus == &pnp_bus_type; 343 } 344 345 static struct acpi_bus_type __initdata acpi_pnp_bus = { 346 .name = "PNP", 347 .match = acpi_pnp_bus_match, 348 .find_companion = acpi_pnp_find_companion, 349 }; 350 351 int pnpacpi_disabled __initdata; 352 static int __init pnpacpi_init(void) 353 { 354 if (acpi_disabled || pnpacpi_disabled) { 355 printk(KERN_INFO "pnp: PnP ACPI: disabled\n"); 356 return 0; 357 } 358 printk(KERN_INFO "pnp: PnP ACPI init\n"); 359 pnp_register_protocol(&pnpacpi_protocol); 360 register_acpi_bus_type(&acpi_pnp_bus); 361 acpi_get_devices(NULL, pnpacpi_add_device_handler, NULL, NULL); 362 printk(KERN_INFO "pnp: PnP ACPI: found %d devices\n", num); 363 unregister_acpi_bus_type(&acpi_pnp_bus); 364 pnp_platform_devices = 1; 365 return 0; 366 } 367 368 fs_initcall(pnpacpi_init); 369 370 static int __init pnpacpi_setup(char *str) 371 { 372 if (str == NULL) 373 return 1; 374 if (!strncmp(str, "off", 3)) 375 pnpacpi_disabled = 1; 376 return 1; 377 } 378 379 __setup("pnpacpi=", pnpacpi_setup); 380