1 /* 2 * Support for OLPC XO-1.5 System Control Interrupts (SCI) 3 * 4 * Copyright (C) 2009-2010 One Laptop per Child 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 */ 11 12 #include <linux/device.h> 13 #include <linux/slab.h> 14 #include <linux/workqueue.h> 15 #include <linux/power_supply.h> 16 #include <linux/olpc-ec.h> 17 18 #include <acpi/acpi_bus.h> 19 #include <acpi/acpi_drivers.h> 20 #include <asm/olpc.h> 21 22 #define DRV_NAME "olpc-xo15-sci" 23 #define PFX DRV_NAME ": " 24 #define XO15_SCI_CLASS DRV_NAME 25 #define XO15_SCI_DEVICE_NAME "OLPC XO-1.5 SCI" 26 27 static unsigned long xo15_sci_gpe; 28 static bool lid_wake_on_close; 29 30 /* 31 * The normal ACPI LID wakeup behavior is wake-on-open, but not 32 * wake-on-close. This is implemented as standard by the XO-1.5 DSDT. 33 * 34 * We provide here a sysfs attribute that will additionally enable 35 * wake-on-close behavior. This is useful (e.g.) when we oportunistically 36 * suspend with the display running; if the lid is then closed, we want to 37 * wake up to turn the display off. 38 * 39 * This is controlled through a custom method in the XO-1.5 DSDT. 40 */ 41 static int set_lid_wake_behavior(bool wake_on_close) 42 { 43 acpi_status status; 44 45 status = acpi_execute_simple_method(NULL, "\\_SB.PCI0.LID.LIDW", wake_on_close); 46 if (ACPI_FAILURE(status)) { 47 pr_warning(PFX "failed to set lid behavior\n"); 48 return 1; 49 } 50 51 lid_wake_on_close = wake_on_close; 52 53 return 0; 54 } 55 56 static ssize_t 57 lid_wake_on_close_show(struct kobject *s, struct kobj_attribute *attr, char *buf) 58 { 59 return sprintf(buf, "%u\n", lid_wake_on_close); 60 } 61 62 static ssize_t lid_wake_on_close_store(struct kobject *s, 63 struct kobj_attribute *attr, 64 const char *buf, size_t n) 65 { 66 unsigned int val; 67 68 if (sscanf(buf, "%u", &val) != 1) 69 return -EINVAL; 70 71 set_lid_wake_behavior(!!val); 72 73 return n; 74 } 75 76 static struct kobj_attribute lid_wake_on_close_attr = 77 __ATTR(lid_wake_on_close, 0644, 78 lid_wake_on_close_show, 79 lid_wake_on_close_store); 80 81 static void battery_status_changed(void) 82 { 83 struct power_supply *psy = power_supply_get_by_name("olpc-battery"); 84 85 if (psy) { 86 power_supply_changed(psy); 87 put_device(psy->dev); 88 } 89 } 90 91 static void ac_status_changed(void) 92 { 93 struct power_supply *psy = power_supply_get_by_name("olpc-ac"); 94 95 if (psy) { 96 power_supply_changed(psy); 97 put_device(psy->dev); 98 } 99 } 100 101 static void process_sci_queue(void) 102 { 103 u16 data; 104 int r; 105 106 do { 107 r = olpc_ec_sci_query(&data); 108 if (r || !data) 109 break; 110 111 pr_debug(PFX "SCI 0x%x received\n", data); 112 113 switch (data) { 114 case EC_SCI_SRC_BATERR: 115 case EC_SCI_SRC_BATSOC: 116 case EC_SCI_SRC_BATTERY: 117 case EC_SCI_SRC_BATCRIT: 118 battery_status_changed(); 119 break; 120 case EC_SCI_SRC_ACPWR: 121 ac_status_changed(); 122 break; 123 } 124 } while (data); 125 126 if (r) 127 pr_err(PFX "Failed to clear SCI queue"); 128 } 129 130 static void process_sci_queue_work(struct work_struct *work) 131 { 132 process_sci_queue(); 133 } 134 135 static DECLARE_WORK(sci_work, process_sci_queue_work); 136 137 static u32 xo15_sci_gpe_handler(acpi_handle gpe_device, u32 gpe, void *context) 138 { 139 schedule_work(&sci_work); 140 return ACPI_INTERRUPT_HANDLED | ACPI_REENABLE_GPE; 141 } 142 143 static int xo15_sci_add(struct acpi_device *device) 144 { 145 unsigned long long tmp; 146 acpi_status status; 147 int r; 148 149 if (!device) 150 return -EINVAL; 151 152 strcpy(acpi_device_name(device), XO15_SCI_DEVICE_NAME); 153 strcpy(acpi_device_class(device), XO15_SCI_CLASS); 154 155 /* Get GPE bit assignment (EC events). */ 156 status = acpi_evaluate_integer(device->handle, "_GPE", NULL, &tmp); 157 if (ACPI_FAILURE(status)) 158 return -EINVAL; 159 160 xo15_sci_gpe = tmp; 161 status = acpi_install_gpe_handler(NULL, xo15_sci_gpe, 162 ACPI_GPE_EDGE_TRIGGERED, 163 xo15_sci_gpe_handler, device); 164 if (ACPI_FAILURE(status)) 165 return -ENODEV; 166 167 dev_info(&device->dev, "Initialized, GPE = 0x%lx\n", xo15_sci_gpe); 168 169 r = sysfs_create_file(&device->dev.kobj, &lid_wake_on_close_attr.attr); 170 if (r) 171 goto err_sysfs; 172 173 /* Flush queue, and enable all SCI events */ 174 process_sci_queue(); 175 olpc_ec_mask_write(EC_SCI_SRC_ALL); 176 177 acpi_enable_gpe(NULL, xo15_sci_gpe); 178 179 /* Enable wake-on-EC */ 180 if (device->wakeup.flags.valid) 181 device_init_wakeup(&device->dev, true); 182 183 return 0; 184 185 err_sysfs: 186 acpi_remove_gpe_handler(NULL, xo15_sci_gpe, xo15_sci_gpe_handler); 187 cancel_work_sync(&sci_work); 188 return r; 189 } 190 191 static int xo15_sci_remove(struct acpi_device *device) 192 { 193 acpi_disable_gpe(NULL, xo15_sci_gpe); 194 acpi_remove_gpe_handler(NULL, xo15_sci_gpe, xo15_sci_gpe_handler); 195 cancel_work_sync(&sci_work); 196 sysfs_remove_file(&device->dev.kobj, &lid_wake_on_close_attr.attr); 197 return 0; 198 } 199 200 static int xo15_sci_resume(struct device *dev) 201 { 202 /* Enable all EC events */ 203 olpc_ec_mask_write(EC_SCI_SRC_ALL); 204 205 /* Power/battery status might have changed */ 206 battery_status_changed(); 207 ac_status_changed(); 208 209 return 0; 210 } 211 212 static SIMPLE_DEV_PM_OPS(xo15_sci_pm, NULL, xo15_sci_resume); 213 214 static const struct acpi_device_id xo15_sci_device_ids[] = { 215 {"XO15EC", 0}, 216 {"", 0}, 217 }; 218 219 static struct acpi_driver xo15_sci_drv = { 220 .name = DRV_NAME, 221 .class = XO15_SCI_CLASS, 222 .ids = xo15_sci_device_ids, 223 .ops = { 224 .add = xo15_sci_add, 225 .remove = xo15_sci_remove, 226 }, 227 .drv.pm = &xo15_sci_pm, 228 }; 229 230 static int __init xo15_sci_init(void) 231 { 232 return acpi_bus_register_driver(&xo15_sci_drv); 233 } 234 device_initcall(xo15_sci_init); 235