xref: /linux/arch/x86/platform/olpc/olpc-xo15-sci.c (revision f2ee442115c9b6219083c019939a9cc0c9abb2f8)
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 
17 #include <acpi/acpi_bus.h>
18 #include <acpi/acpi_drivers.h>
19 #include <asm/olpc.h>
20 
21 #define DRV_NAME			"olpc-xo15-sci"
22 #define PFX				DRV_NAME ": "
23 #define XO15_SCI_CLASS			DRV_NAME
24 #define XO15_SCI_DEVICE_NAME		"OLPC XO-1.5 SCI"
25 
26 static unsigned long xo15_sci_gpe;
27 
28 static void battery_status_changed(void)
29 {
30 	struct power_supply *psy = power_supply_get_by_name("olpc-battery");
31 
32 	if (psy) {
33 		power_supply_changed(psy);
34 		put_device(psy->dev);
35 	}
36 }
37 
38 static void ac_status_changed(void)
39 {
40 	struct power_supply *psy = power_supply_get_by_name("olpc-ac");
41 
42 	if (psy) {
43 		power_supply_changed(psy);
44 		put_device(psy->dev);
45 	}
46 }
47 
48 static void process_sci_queue(void)
49 {
50 	u16 data;
51 	int r;
52 
53 	do {
54 		r = olpc_ec_sci_query(&data);
55 		if (r || !data)
56 			break;
57 
58 		pr_debug(PFX "SCI 0x%x received\n", data);
59 
60 		switch (data) {
61 		case EC_SCI_SRC_BATERR:
62 		case EC_SCI_SRC_BATSOC:
63 		case EC_SCI_SRC_BATTERY:
64 		case EC_SCI_SRC_BATCRIT:
65 			battery_status_changed();
66 			break;
67 		case EC_SCI_SRC_ACPWR:
68 			ac_status_changed();
69 			break;
70 		}
71 	} while (data);
72 
73 	if (r)
74 		pr_err(PFX "Failed to clear SCI queue");
75 }
76 
77 static void process_sci_queue_work(struct work_struct *work)
78 {
79 	process_sci_queue();
80 }
81 
82 static DECLARE_WORK(sci_work, process_sci_queue_work);
83 
84 static u32 xo15_sci_gpe_handler(acpi_handle gpe_device, u32 gpe, void *context)
85 {
86 	schedule_work(&sci_work);
87 	return ACPI_INTERRUPT_HANDLED | ACPI_REENABLE_GPE;
88 }
89 
90 static int xo15_sci_add(struct acpi_device *device)
91 {
92 	unsigned long long tmp;
93 	acpi_status status;
94 
95 	if (!device)
96 		return -EINVAL;
97 
98 	strcpy(acpi_device_name(device), XO15_SCI_DEVICE_NAME);
99 	strcpy(acpi_device_class(device), XO15_SCI_CLASS);
100 
101 	/* Get GPE bit assignment (EC events). */
102 	status = acpi_evaluate_integer(device->handle, "_GPE", NULL, &tmp);
103 	if (ACPI_FAILURE(status))
104 		return -EINVAL;
105 
106 	xo15_sci_gpe = tmp;
107 	status = acpi_install_gpe_handler(NULL, xo15_sci_gpe,
108 					  ACPI_GPE_EDGE_TRIGGERED,
109 					  xo15_sci_gpe_handler, device);
110 	if (ACPI_FAILURE(status))
111 		return -ENODEV;
112 
113 	dev_info(&device->dev, "Initialized, GPE = 0x%lx\n", xo15_sci_gpe);
114 
115 	/* Flush queue, and enable all SCI events */
116 	process_sci_queue();
117 	olpc_ec_mask_write(EC_SCI_SRC_ALL);
118 
119 	acpi_enable_gpe(NULL, xo15_sci_gpe);
120 
121 	/* Enable wake-on-EC */
122 	if (device->wakeup.flags.valid)
123 		device_init_wakeup(&device->dev, true);
124 
125 	return 0;
126 }
127 
128 static int xo15_sci_remove(struct acpi_device *device, int type)
129 {
130 	acpi_disable_gpe(NULL, xo15_sci_gpe);
131 	acpi_remove_gpe_handler(NULL, xo15_sci_gpe, xo15_sci_gpe_handler);
132 	cancel_work_sync(&sci_work);
133 	return 0;
134 }
135 
136 static int xo15_sci_resume(struct acpi_device *device)
137 {
138 	/* Enable all EC events */
139 	olpc_ec_mask_write(EC_SCI_SRC_ALL);
140 
141 	/* Power/battery status might have changed */
142 	battery_status_changed();
143 	ac_status_changed();
144 
145 	return 0;
146 }
147 
148 static const struct acpi_device_id xo15_sci_device_ids[] = {
149 	{"XO15EC", 0},
150 	{"", 0},
151 };
152 
153 static struct acpi_driver xo15_sci_drv = {
154 	.name = DRV_NAME,
155 	.class = XO15_SCI_CLASS,
156 	.ids = xo15_sci_device_ids,
157 	.ops = {
158 		.add = xo15_sci_add,
159 		.remove = xo15_sci_remove,
160 		.resume = xo15_sci_resume,
161 	},
162 };
163 
164 static int __init xo15_sci_init(void)
165 {
166 	return acpi_bus_register_driver(&xo15_sci_drv);
167 }
168 device_initcall(xo15_sci_init);
169