1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * OLPC XO-1.5 ebook switch driver |
4 | * (based on generic ACPI button driver) |
5 | * |
6 | * Copyright (C) 2009 Paul Fox <pgf@laptop.org> |
7 | * Copyright (C) 2010 One Laptop per Child |
8 | */ |
9 | |
10 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
11 | |
12 | #include <linux/kernel.h> |
13 | #include <linux/module.h> |
14 | #include <linux/init.h> |
15 | #include <linux/types.h> |
16 | #include <linux/input.h> |
17 | #include <linux/acpi.h> |
18 | |
19 | #define MODULE_NAME "xo15-ebook" |
20 | |
21 | #define XO15_EBOOK_CLASS MODULE_NAME |
22 | #define XO15_EBOOK_TYPE_UNKNOWN 0x00 |
23 | #define XO15_EBOOK_NOTIFY_STATUS 0x80 |
24 | |
25 | #define XO15_EBOOK_SUBCLASS "ebook" |
26 | #define XO15_EBOOK_HID "XO15EBK" |
27 | #define XO15_EBOOK_DEVICE_NAME "EBook Switch" |
28 | |
29 | MODULE_DESCRIPTION("OLPC XO-1.5 ebook switch driver" ); |
30 | MODULE_LICENSE("GPL" ); |
31 | |
32 | static const struct acpi_device_id ebook_device_ids[] = { |
33 | { XO15_EBOOK_HID, 0 }, |
34 | { "" , 0 }, |
35 | }; |
36 | MODULE_DEVICE_TABLE(acpi, ebook_device_ids); |
37 | |
38 | struct ebook_switch { |
39 | struct input_dev *input; |
40 | char phys[32]; /* for input device */ |
41 | }; |
42 | |
43 | static int ebook_send_state(struct acpi_device *device) |
44 | { |
45 | struct ebook_switch *button = acpi_driver_data(d: device); |
46 | unsigned long long state; |
47 | acpi_status status; |
48 | |
49 | status = acpi_evaluate_integer(handle: device->handle, pathname: "EBK" , NULL, data: &state); |
50 | if (ACPI_FAILURE(status)) |
51 | return -EIO; |
52 | |
53 | /* input layer checks if event is redundant */ |
54 | input_report_switch(dev: button->input, SW_TABLET_MODE, value: !state); |
55 | input_sync(dev: button->input); |
56 | return 0; |
57 | } |
58 | |
59 | static void ebook_switch_notify(struct acpi_device *device, u32 event) |
60 | { |
61 | switch (event) { |
62 | case ACPI_FIXED_HARDWARE_EVENT: |
63 | case XO15_EBOOK_NOTIFY_STATUS: |
64 | ebook_send_state(device); |
65 | break; |
66 | default: |
67 | acpi_handle_debug(device->handle, |
68 | "Unsupported event [0x%x]\n" , event); |
69 | break; |
70 | } |
71 | } |
72 | |
73 | #ifdef CONFIG_PM_SLEEP |
74 | static int ebook_switch_resume(struct device *dev) |
75 | { |
76 | return ebook_send_state(to_acpi_device(dev)); |
77 | } |
78 | #endif |
79 | |
80 | static SIMPLE_DEV_PM_OPS(ebook_switch_pm, NULL, ebook_switch_resume); |
81 | |
82 | static int ebook_switch_add(struct acpi_device *device) |
83 | { |
84 | const struct acpi_device_id *id; |
85 | struct ebook_switch *button; |
86 | struct input_dev *input; |
87 | char *name, *class; |
88 | int error; |
89 | |
90 | button = kzalloc(size: sizeof(struct ebook_switch), GFP_KERNEL); |
91 | if (!button) |
92 | return -ENOMEM; |
93 | |
94 | device->driver_data = button; |
95 | |
96 | button->input = input = input_allocate_device(); |
97 | if (!input) { |
98 | error = -ENOMEM; |
99 | goto err_free_button; |
100 | } |
101 | |
102 | name = acpi_device_name(device); |
103 | class = acpi_device_class(device); |
104 | |
105 | id = acpi_match_acpi_device(ids: ebook_device_ids, adev: device); |
106 | if (!id) { |
107 | dev_err(&device->dev, "Unsupported hid\n" ); |
108 | error = -ENODEV; |
109 | goto err_free_input; |
110 | } |
111 | |
112 | strcpy(p: name, XO15_EBOOK_DEVICE_NAME); |
113 | sprintf(buf: class, fmt: "%s/%s" , XO15_EBOOK_CLASS, XO15_EBOOK_SUBCLASS); |
114 | |
115 | snprintf(buf: button->phys, size: sizeof(button->phys), fmt: "%s/button/input0" , id->id); |
116 | |
117 | input->name = name; |
118 | input->phys = button->phys; |
119 | input->id.bustype = BUS_HOST; |
120 | input->dev.parent = &device->dev; |
121 | |
122 | input->evbit[0] = BIT_MASK(EV_SW); |
123 | set_bit(SW_TABLET_MODE, addr: input->swbit); |
124 | |
125 | error = input_register_device(input); |
126 | if (error) |
127 | goto err_free_input; |
128 | |
129 | ebook_send_state(device); |
130 | |
131 | if (device->wakeup.flags.valid) { |
132 | /* Button's GPE is run-wake GPE */ |
133 | acpi_enable_gpe(gpe_device: device->wakeup.gpe_device, |
134 | gpe_number: device->wakeup.gpe_number); |
135 | device_set_wakeup_enable(dev: &device->dev, enable: true); |
136 | } |
137 | |
138 | return 0; |
139 | |
140 | err_free_input: |
141 | input_free_device(dev: input); |
142 | err_free_button: |
143 | kfree(objp: button); |
144 | return error; |
145 | } |
146 | |
147 | static void ebook_switch_remove(struct acpi_device *device) |
148 | { |
149 | struct ebook_switch *button = acpi_driver_data(d: device); |
150 | |
151 | input_unregister_device(button->input); |
152 | kfree(objp: button); |
153 | } |
154 | |
155 | static struct acpi_driver xo15_ebook_driver = { |
156 | .name = MODULE_NAME, |
157 | .class = XO15_EBOOK_CLASS, |
158 | .ids = ebook_device_ids, |
159 | .ops = { |
160 | .add = ebook_switch_add, |
161 | .remove = ebook_switch_remove, |
162 | .notify = ebook_switch_notify, |
163 | }, |
164 | .drv.pm = &ebook_switch_pm, |
165 | }; |
166 | module_acpi_driver(xo15_ebook_driver); |
167 | |