root/arch/x86/platform/olpc/olpc-xo15-sci.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. set_lid_wake_behavior
  2. lid_wake_on_close_show
  3. lid_wake_on_close_store
  4. battery_status_changed
  5. ac_status_changed
  6. process_sci_queue
  7. process_sci_queue_work
  8. xo15_sci_gpe_handler
  9. xo15_sci_add
  10. xo15_sci_remove
  11. xo15_sci_resume
  12. xo15_sci_init

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

/* [<][>][^][v][top][bottom][index][help] */