1 /* 2 * acpi_ac.c - ACPI AC Adapter Driver ($Revision: 27 $) 3 * 4 * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> 5 * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> 6 * 7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or (at 12 * your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, but 15 * WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License along 20 * with this program; if not, write to the Free Software Foundation, Inc., 21 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 22 * 23 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 24 */ 25 26 #include <linux/kernel.h> 27 #include <linux/module.h> 28 #include <linux/slab.h> 29 #include <linux/init.h> 30 #include <linux/types.h> 31 #include <linux/dmi.h> 32 #include <linux/delay.h> 33 #include <linux/platform_device.h> 34 #include <linux/power_supply.h> 35 #include <linux/acpi.h> 36 #include "battery.h" 37 38 #define PREFIX "ACPI: " 39 40 #define ACPI_AC_CLASS "ac_adapter" 41 #define ACPI_AC_DEVICE_NAME "AC Adapter" 42 #define ACPI_AC_FILE_STATE "state" 43 #define ACPI_AC_NOTIFY_STATUS 0x80 44 #define ACPI_AC_STATUS_OFFLINE 0x00 45 #define ACPI_AC_STATUS_ONLINE 0x01 46 #define ACPI_AC_STATUS_UNKNOWN 0xFF 47 48 #define _COMPONENT ACPI_AC_COMPONENT 49 ACPI_MODULE_NAME("ac"); 50 51 MODULE_AUTHOR("Paul Diefenbaugh"); 52 MODULE_DESCRIPTION("ACPI AC Adapter Driver"); 53 MODULE_LICENSE("GPL"); 54 55 static int ac_sleep_before_get_state_ms; 56 57 struct acpi_ac { 58 struct power_supply charger; 59 struct platform_device *pdev; 60 unsigned long long state; 61 struct notifier_block battery_nb; 62 }; 63 64 #define to_acpi_ac(x) container_of(x, struct acpi_ac, charger) 65 66 /* -------------------------------------------------------------------------- 67 AC Adapter Management 68 -------------------------------------------------------------------------- */ 69 70 static int acpi_ac_get_state(struct acpi_ac *ac) 71 { 72 acpi_status status; 73 acpi_handle handle = ACPI_HANDLE(&ac->pdev->dev); 74 75 status = acpi_evaluate_integer(handle, "_PSR", NULL, 76 &ac->state); 77 if (ACPI_FAILURE(status)) { 78 ACPI_EXCEPTION((AE_INFO, status, 79 "Error reading AC Adapter state")); 80 ac->state = ACPI_AC_STATUS_UNKNOWN; 81 return -ENODEV; 82 } 83 84 return 0; 85 } 86 87 /* -------------------------------------------------------------------------- 88 sysfs I/F 89 -------------------------------------------------------------------------- */ 90 static int get_ac_property(struct power_supply *psy, 91 enum power_supply_property psp, 92 union power_supply_propval *val) 93 { 94 struct acpi_ac *ac = to_acpi_ac(psy); 95 96 if (!ac) 97 return -ENODEV; 98 99 if (acpi_ac_get_state(ac)) 100 return -ENODEV; 101 102 switch (psp) { 103 case POWER_SUPPLY_PROP_ONLINE: 104 val->intval = ac->state; 105 break; 106 default: 107 return -EINVAL; 108 } 109 return 0; 110 } 111 112 static enum power_supply_property ac_props[] = { 113 POWER_SUPPLY_PROP_ONLINE, 114 }; 115 116 /* -------------------------------------------------------------------------- 117 Driver Model 118 -------------------------------------------------------------------------- */ 119 120 static void acpi_ac_notify_handler(acpi_handle handle, u32 event, void *data) 121 { 122 struct acpi_ac *ac = data; 123 struct acpi_device *adev; 124 125 if (!ac) 126 return; 127 128 switch (event) { 129 default: 130 ACPI_DEBUG_PRINT((ACPI_DB_INFO, 131 "Unsupported event [0x%x]\n", event)); 132 case ACPI_AC_NOTIFY_STATUS: 133 case ACPI_NOTIFY_BUS_CHECK: 134 case ACPI_NOTIFY_DEVICE_CHECK: 135 /* 136 * A buggy BIOS may notify AC first and then sleep for 137 * a specific time before doing actual operations in the 138 * EC event handler (_Qxx). This will cause the AC state 139 * reported by the ACPI event to be incorrect, so wait for a 140 * specific time for the EC event handler to make progress. 141 */ 142 if (ac_sleep_before_get_state_ms > 0) 143 msleep(ac_sleep_before_get_state_ms); 144 145 acpi_ac_get_state(ac); 146 adev = ACPI_COMPANION(&ac->pdev->dev); 147 acpi_bus_generate_netlink_event(adev->pnp.device_class, 148 dev_name(&ac->pdev->dev), 149 event, (u32) ac->state); 150 acpi_notifier_call_chain(adev, event, (u32) ac->state); 151 kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE); 152 } 153 154 return; 155 } 156 157 static int acpi_ac_battery_notify(struct notifier_block *nb, 158 unsigned long action, void *data) 159 { 160 struct acpi_ac *ac = container_of(nb, struct acpi_ac, battery_nb); 161 struct acpi_bus_event *event = (struct acpi_bus_event *)data; 162 163 /* 164 * On HP Pavilion dv6-6179er AC status notifications aren't triggered 165 * when adapter is plugged/unplugged. However, battery status 166 * notifcations are triggered when battery starts charging or 167 * discharging. Re-reading AC status triggers lost AC notifications, 168 * if AC status has changed. 169 */ 170 if (strcmp(event->device_class, ACPI_BATTERY_CLASS) == 0 && 171 event->type == ACPI_BATTERY_NOTIFY_STATUS) 172 acpi_ac_get_state(ac); 173 174 return NOTIFY_OK; 175 } 176 177 static int thinkpad_e530_quirk(const struct dmi_system_id *d) 178 { 179 ac_sleep_before_get_state_ms = 1000; 180 return 0; 181 } 182 183 static struct dmi_system_id ac_dmi_table[] = { 184 { 185 .callback = thinkpad_e530_quirk, 186 .ident = "thinkpad e530", 187 .matches = { 188 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 189 DMI_MATCH(DMI_PRODUCT_NAME, "32597CG"), 190 }, 191 }, 192 {}, 193 }; 194 195 static int acpi_ac_probe(struct platform_device *pdev) 196 { 197 int result = 0; 198 struct acpi_ac *ac = NULL; 199 struct acpi_device *adev; 200 201 if (!pdev) 202 return -EINVAL; 203 204 adev = ACPI_COMPANION(&pdev->dev); 205 if (!adev) 206 return -ENODEV; 207 208 ac = kzalloc(sizeof(struct acpi_ac), GFP_KERNEL); 209 if (!ac) 210 return -ENOMEM; 211 212 strcpy(acpi_device_name(adev), ACPI_AC_DEVICE_NAME); 213 strcpy(acpi_device_class(adev), ACPI_AC_CLASS); 214 ac->pdev = pdev; 215 platform_set_drvdata(pdev, ac); 216 217 result = acpi_ac_get_state(ac); 218 if (result) 219 goto end; 220 221 ac->charger.name = acpi_device_bid(adev); 222 ac->charger.type = POWER_SUPPLY_TYPE_MAINS; 223 ac->charger.properties = ac_props; 224 ac->charger.num_properties = ARRAY_SIZE(ac_props); 225 ac->charger.get_property = get_ac_property; 226 result = power_supply_register(&pdev->dev, &ac->charger); 227 if (result) 228 goto end; 229 230 result = acpi_install_notify_handler(ACPI_HANDLE(&pdev->dev), 231 ACPI_ALL_NOTIFY, acpi_ac_notify_handler, ac); 232 if (result) { 233 power_supply_unregister(&ac->charger); 234 goto end; 235 } 236 printk(KERN_INFO PREFIX "%s [%s] (%s)\n", 237 acpi_device_name(adev), acpi_device_bid(adev), 238 ac->state ? "on-line" : "off-line"); 239 240 ac->battery_nb.notifier_call = acpi_ac_battery_notify; 241 register_acpi_notifier(&ac->battery_nb); 242 end: 243 if (result) 244 kfree(ac); 245 246 dmi_check_system(ac_dmi_table); 247 return result; 248 } 249 250 #ifdef CONFIG_PM_SLEEP 251 static int acpi_ac_resume(struct device *dev) 252 { 253 struct acpi_ac *ac; 254 unsigned old_state; 255 256 if (!dev) 257 return -EINVAL; 258 259 ac = platform_get_drvdata(to_platform_device(dev)); 260 if (!ac) 261 return -EINVAL; 262 263 old_state = ac->state; 264 if (acpi_ac_get_state(ac)) 265 return 0; 266 if (old_state != ac->state) 267 kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE); 268 return 0; 269 } 270 #else 271 #define acpi_ac_resume NULL 272 #endif 273 static SIMPLE_DEV_PM_OPS(acpi_ac_pm_ops, NULL, acpi_ac_resume); 274 275 static int acpi_ac_remove(struct platform_device *pdev) 276 { 277 struct acpi_ac *ac; 278 279 if (!pdev) 280 return -EINVAL; 281 282 acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev), 283 ACPI_ALL_NOTIFY, acpi_ac_notify_handler); 284 285 ac = platform_get_drvdata(pdev); 286 if (ac->charger.dev) 287 power_supply_unregister(&ac->charger); 288 unregister_acpi_notifier(&ac->battery_nb); 289 290 kfree(ac); 291 292 return 0; 293 } 294 295 static const struct acpi_device_id acpi_ac_match[] = { 296 { "ACPI0003", 0 }, 297 { } 298 }; 299 MODULE_DEVICE_TABLE(acpi, acpi_ac_match); 300 301 static struct platform_driver acpi_ac_driver = { 302 .probe = acpi_ac_probe, 303 .remove = acpi_ac_remove, 304 .driver = { 305 .name = "acpi-ac", 306 .owner = THIS_MODULE, 307 .pm = &acpi_ac_pm_ops, 308 .acpi_match_table = ACPI_PTR(acpi_ac_match), 309 }, 310 }; 311 312 static int __init acpi_ac_init(void) 313 { 314 int result; 315 316 if (acpi_disabled) 317 return -ENODEV; 318 319 result = platform_driver_register(&acpi_ac_driver); 320 if (result < 0) 321 return -ENODEV; 322 323 return 0; 324 } 325 326 static void __exit acpi_ac_exit(void) 327 { 328 platform_driver_unregister(&acpi_ac_driver); 329 } 330 module_init(acpi_ac_init); 331 module_exit(acpi_ac_exit); 332