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 <acpi/acpi_bus.h> 36 #include <acpi/acpi_drivers.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 }; 62 63 #define to_acpi_ac(x) container_of(x, struct acpi_ac, charger) 64 65 /* -------------------------------------------------------------------------- 66 AC Adapter Management 67 -------------------------------------------------------------------------- */ 68 69 static int acpi_ac_get_state(struct acpi_ac *ac) 70 { 71 acpi_status status; 72 acpi_handle handle = ACPI_HANDLE(&ac->pdev->dev); 73 74 status = acpi_evaluate_integer(handle, "_PSR", NULL, 75 &ac->state); 76 if (ACPI_FAILURE(status)) { 77 ACPI_EXCEPTION((AE_INFO, status, 78 "Error reading AC Adapter state")); 79 ac->state = ACPI_AC_STATUS_UNKNOWN; 80 return -ENODEV; 81 } 82 83 return 0; 84 } 85 86 /* -------------------------------------------------------------------------- 87 sysfs I/F 88 -------------------------------------------------------------------------- */ 89 static int get_ac_property(struct power_supply *psy, 90 enum power_supply_property psp, 91 union power_supply_propval *val) 92 { 93 struct acpi_ac *ac = to_acpi_ac(psy); 94 95 if (!ac) 96 return -ENODEV; 97 98 if (acpi_ac_get_state(ac)) 99 return -ENODEV; 100 101 switch (psp) { 102 case POWER_SUPPLY_PROP_ONLINE: 103 val->intval = ac->state; 104 break; 105 default: 106 return -EINVAL; 107 } 108 return 0; 109 } 110 111 static enum power_supply_property ac_props[] = { 112 POWER_SUPPLY_PROP_ONLINE, 113 }; 114 115 /* -------------------------------------------------------------------------- 116 Driver Model 117 -------------------------------------------------------------------------- */ 118 119 static void acpi_ac_notify_handler(acpi_handle handle, u32 event, void *data) 120 { 121 struct acpi_ac *ac = data; 122 struct acpi_device *adev; 123 124 if (!ac) 125 return; 126 127 switch (event) { 128 default: 129 ACPI_DEBUG_PRINT((ACPI_DB_INFO, 130 "Unsupported event [0x%x]\n", event)); 131 case ACPI_AC_NOTIFY_STATUS: 132 case ACPI_NOTIFY_BUS_CHECK: 133 case ACPI_NOTIFY_DEVICE_CHECK: 134 /* 135 * A buggy BIOS may notify AC first and then sleep for 136 * a specific time before doing actual operations in the 137 * EC event handler (_Qxx). This will cause the AC state 138 * reported by the ACPI event to be incorrect, so wait for a 139 * specific time for the EC event handler to make progress. 140 */ 141 if (ac_sleep_before_get_state_ms > 0) 142 msleep(ac_sleep_before_get_state_ms); 143 144 acpi_ac_get_state(ac); 145 adev = ACPI_COMPANION(&ac->pdev->dev); 146 acpi_bus_generate_netlink_event(adev->pnp.device_class, 147 dev_name(&ac->pdev->dev), 148 event, (u32) ac->state); 149 acpi_notifier_call_chain(adev, event, (u32) ac->state); 150 kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE); 151 } 152 153 return; 154 } 155 156 static int thinkpad_e530_quirk(const struct dmi_system_id *d) 157 { 158 ac_sleep_before_get_state_ms = 1000; 159 return 0; 160 } 161 162 static struct dmi_system_id ac_dmi_table[] = { 163 { 164 .callback = thinkpad_e530_quirk, 165 .ident = "thinkpad e530", 166 .matches = { 167 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 168 DMI_MATCH(DMI_PRODUCT_NAME, "32597CG"), 169 }, 170 }, 171 {}, 172 }; 173 174 static int acpi_ac_probe(struct platform_device *pdev) 175 { 176 int result = 0; 177 struct acpi_ac *ac = NULL; 178 struct acpi_device *adev; 179 180 if (!pdev) 181 return -EINVAL; 182 183 adev = ACPI_COMPANION(&pdev->dev); 184 if (!adev) 185 return -ENODEV; 186 187 ac = kzalloc(sizeof(struct acpi_ac), GFP_KERNEL); 188 if (!ac) 189 return -ENOMEM; 190 191 strcpy(acpi_device_name(adev), ACPI_AC_DEVICE_NAME); 192 strcpy(acpi_device_class(adev), ACPI_AC_CLASS); 193 ac->pdev = pdev; 194 platform_set_drvdata(pdev, ac); 195 196 result = acpi_ac_get_state(ac); 197 if (result) 198 goto end; 199 200 ac->charger.name = acpi_device_bid(adev); 201 ac->charger.type = POWER_SUPPLY_TYPE_MAINS; 202 ac->charger.properties = ac_props; 203 ac->charger.num_properties = ARRAY_SIZE(ac_props); 204 ac->charger.get_property = get_ac_property; 205 result = power_supply_register(&pdev->dev, &ac->charger); 206 if (result) 207 goto end; 208 209 result = acpi_install_notify_handler(ACPI_HANDLE(&pdev->dev), 210 ACPI_DEVICE_NOTIFY, acpi_ac_notify_handler, ac); 211 if (result) { 212 power_supply_unregister(&ac->charger); 213 goto end; 214 } 215 printk(KERN_INFO PREFIX "%s [%s] (%s)\n", 216 acpi_device_name(adev), acpi_device_bid(adev), 217 ac->state ? "on-line" : "off-line"); 218 219 end: 220 if (result) 221 kfree(ac); 222 223 dmi_check_system(ac_dmi_table); 224 return result; 225 } 226 227 #ifdef CONFIG_PM_SLEEP 228 static int acpi_ac_resume(struct device *dev) 229 { 230 struct acpi_ac *ac; 231 unsigned old_state; 232 233 if (!dev) 234 return -EINVAL; 235 236 ac = platform_get_drvdata(to_platform_device(dev)); 237 if (!ac) 238 return -EINVAL; 239 240 old_state = ac->state; 241 if (acpi_ac_get_state(ac)) 242 return 0; 243 if (old_state != ac->state) 244 kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE); 245 return 0; 246 } 247 #endif 248 static SIMPLE_DEV_PM_OPS(acpi_ac_pm_ops, NULL, acpi_ac_resume); 249 250 static int acpi_ac_remove(struct platform_device *pdev) 251 { 252 struct acpi_ac *ac; 253 254 if (!pdev) 255 return -EINVAL; 256 257 acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev), 258 ACPI_DEVICE_NOTIFY, acpi_ac_notify_handler); 259 260 ac = platform_get_drvdata(pdev); 261 if (ac->charger.dev) 262 power_supply_unregister(&ac->charger); 263 264 kfree(ac); 265 266 return 0; 267 } 268 269 static const struct acpi_device_id acpi_ac_match[] = { 270 { "ACPI0003", 0 }, 271 { } 272 }; 273 MODULE_DEVICE_TABLE(acpi, acpi_ac_match); 274 275 static struct platform_driver acpi_ac_driver = { 276 .probe = acpi_ac_probe, 277 .remove = acpi_ac_remove, 278 .driver = { 279 .name = "acpi-ac", 280 .owner = THIS_MODULE, 281 .pm = &acpi_ac_pm_ops, 282 .acpi_match_table = ACPI_PTR(acpi_ac_match), 283 }, 284 }; 285 286 static int __init acpi_ac_init(void) 287 { 288 int result; 289 290 if (acpi_disabled) 291 return -ENODEV; 292 293 result = platform_driver_register(&acpi_ac_driver); 294 if (result < 0) 295 return -ENODEV; 296 297 return 0; 298 } 299 300 static void __exit acpi_ac_exit(void) 301 { 302 platform_driver_unregister(&acpi_ac_driver); 303 } 304 module_init(acpi_ac_init); 305 module_exit(acpi_ac_exit); 306