diff options
-rw-r--r-- | MAINTAINERS | 3 | ||||
-rw-r--r-- | drivers/mfd/cros_ec.c | 13 | ||||
-rw-r--r-- | drivers/platform/chrome/Kconfig | 14 | ||||
-rw-r--r-- | drivers/platform/chrome/Makefile | 7 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_debugfs.c | 401 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_debugfs.h | 27 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_dev.c | 40 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_dev.h | 6 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_lightbar.c | 197 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_lpc.c | 168 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_lpc_mec.c | 140 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_lpc_reg.c | 133 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_proto.c | 116 | ||||
-rw-r--r-- | include/linux/mfd/cros_ec.h | 19 | ||||
-rw-r--r-- | include/linux/mfd/cros_ec_commands.h | 42 | ||||
-rw-r--r-- | include/linux/mfd/cros_ec_lpc_mec.h | 90 | ||||
-rw-r--r-- | include/linux/mfd/cros_ec_lpc_reg.h | 61 |
17 files changed, 1393 insertions, 84 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index d8eab9322ba2..f6ec1be48609 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -3319,9 +3319,10 @@ F: Documentation/devicetree/bindings/input/touchscreen/chipone_icn8318.txt | |||
3319 | F: drivers/input/touchscreen/chipone_icn8318.c | 3319 | F: drivers/input/touchscreen/chipone_icn8318.c |
3320 | 3320 | ||
3321 | CHROME HARDWARE PLATFORM SUPPORT | 3321 | CHROME HARDWARE PLATFORM SUPPORT |
3322 | M: Benson Leung <bleung@chromium.org> | ||
3322 | M: Olof Johansson <olof@lixom.net> | 3323 | M: Olof Johansson <olof@lixom.net> |
3323 | S: Maintained | 3324 | S: Maintained |
3324 | T: git git://git.kernel.org/pub/scm/linux/kernel/git/olof/chrome-platform.git | 3325 | T: git git://git.kernel.org/pub/scm/linux/kernel/git/bleung/chrome-platform.git |
3325 | F: drivers/platform/chrome/ | 3326 | F: drivers/platform/chrome/ |
3326 | 3327 | ||
3327 | CISCO VIC ETHERNET NIC DRIVER | 3328 | CISCO VIC ETHERNET NIC DRIVER |
diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index dc6ce9091694..b0ca5a4c841e 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c | |||
@@ -54,12 +54,19 @@ static const struct mfd_cell ec_pd_cell = { | |||
54 | static irqreturn_t ec_irq_thread(int irq, void *data) | 54 | static irqreturn_t ec_irq_thread(int irq, void *data) |
55 | { | 55 | { |
56 | struct cros_ec_device *ec_dev = data; | 56 | struct cros_ec_device *ec_dev = data; |
57 | bool wake_event = true; | ||
57 | int ret; | 58 | int ret; |
58 | 59 | ||
59 | if (device_may_wakeup(ec_dev->dev)) | 60 | ret = cros_ec_get_next_event(ec_dev, &wake_event); |
61 | |||
62 | /* | ||
63 | * Signal only if wake host events or any interrupt if | ||
64 | * cros_ec_get_next_event() returned an error (default value for | ||
65 | * wake_event is true) | ||
66 | */ | ||
67 | if (wake_event && device_may_wakeup(ec_dev->dev)) | ||
60 | pm_wakeup_event(ec_dev->dev, 0); | 68 | pm_wakeup_event(ec_dev->dev, 0); |
61 | 69 | ||
62 | ret = cros_ec_get_next_event(ec_dev); | ||
63 | if (ret > 0) | 70 | if (ret > 0) |
64 | blocking_notifier_call_chain(&ec_dev->event_notifier, | 71 | blocking_notifier_call_chain(&ec_dev->event_notifier, |
65 | 0, ec_dev); | 72 | 0, ec_dev); |
@@ -224,7 +231,7 @@ EXPORT_SYMBOL(cros_ec_suspend); | |||
224 | 231 | ||
225 | static void cros_ec_drain_events(struct cros_ec_device *ec_dev) | 232 | static void cros_ec_drain_events(struct cros_ec_device *ec_dev) |
226 | { | 233 | { |
227 | while (cros_ec_get_next_event(ec_dev) > 0) | 234 | while (cros_ec_get_next_event(ec_dev, NULL) > 0) |
228 | blocking_notifier_call_chain(&ec_dev->event_notifier, | 235 | blocking_notifier_call_chain(&ec_dev->event_notifier, |
229 | 1, ec_dev); | 236 | 1, ec_dev); |
230 | } | 237 | } |
diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index 76bdae1a93bb..0ad6e290bbda 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig | |||
@@ -49,7 +49,7 @@ config CROS_EC_CHARDEV | |||
49 | 49 | ||
50 | config CROS_EC_LPC | 50 | config CROS_EC_LPC |
51 | tristate "ChromeOS Embedded Controller (LPC)" | 51 | tristate "ChromeOS Embedded Controller (LPC)" |
52 | depends on MFD_CROS_EC && (X86 || COMPILE_TEST) | 52 | depends on MFD_CROS_EC && ACPI && (X86 || COMPILE_TEST) |
53 | help | 53 | help |
54 | If you say Y here, you get support for talking to the ChromeOS EC | 54 | If you say Y here, you get support for talking to the ChromeOS EC |
55 | over an LPC bus. This uses a simple byte-level protocol with a | 55 | over an LPC bus. This uses a simple byte-level protocol with a |
@@ -59,6 +59,18 @@ config CROS_EC_LPC | |||
59 | To compile this driver as a module, choose M here: the | 59 | To compile this driver as a module, choose M here: the |
60 | module will be called cros_ec_lpc. | 60 | module will be called cros_ec_lpc. |
61 | 61 | ||
62 | config CROS_EC_LPC_MEC | ||
63 | bool "ChromeOS Embedded Controller LPC Microchip EC (MEC) variant" | ||
64 | depends on CROS_EC_LPC | ||
65 | default n | ||
66 | help | ||
67 | If you say Y here, a variant LPC protocol for the Microchip EC | ||
68 | will be used. Note that this variant is not backward compatible | ||
69 | with non-Microchip ECs. | ||
70 | |||
71 | If you have a ChromeOS Embedded Controller Microchip EC variant | ||
72 | choose Y here. | ||
73 | |||
62 | config CROS_EC_PROTO | 74 | config CROS_EC_PROTO |
63 | bool | 75 | bool |
64 | help | 76 | help |
diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile index 4f3462783a3c..66c345ca35fc 100644 --- a/drivers/platform/chrome/Makefile +++ b/drivers/platform/chrome/Makefile | |||
@@ -2,8 +2,11 @@ | |||
2 | obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o | 2 | obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o |
3 | obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o | 3 | obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o |
4 | cros_ec_devs-objs := cros_ec_dev.o cros_ec_sysfs.o \ | 4 | cros_ec_devs-objs := cros_ec_dev.o cros_ec_sysfs.o \ |
5 | cros_ec_lightbar.o cros_ec_vbc.o | 5 | cros_ec_lightbar.o cros_ec_vbc.o \ |
6 | cros_ec_debugfs.o | ||
6 | obj-$(CONFIG_CROS_EC_CHARDEV) += cros_ec_devs.o | 7 | obj-$(CONFIG_CROS_EC_CHARDEV) += cros_ec_devs.o |
7 | obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpc.o | 8 | cros_ec_lpcs-objs := cros_ec_lpc.o cros_ec_lpc_reg.o |
9 | cros_ec_lpcs-$(CONFIG_CROS_EC_LPC_MEC) += cros_ec_lpc_mec.o | ||
10 | obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpcs.o | ||
8 | obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o | 11 | obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o |
9 | obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT) += cros_kbd_led_backlight.o | 12 | obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT) += cros_kbd_led_backlight.o |
diff --git a/drivers/platform/chrome/cros_ec_debugfs.c b/drivers/platform/chrome/cros_ec_debugfs.c new file mode 100644 index 000000000000..4cc66f405760 --- /dev/null +++ b/drivers/platform/chrome/cros_ec_debugfs.c | |||
@@ -0,0 +1,401 @@ | |||
1 | /* | ||
2 | * cros_ec_debugfs - debug logs for Chrome OS EC | ||
3 | * | ||
4 | * Copyright 2015 Google, Inc. | ||
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 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
18 | */ | ||
19 | |||
20 | #include <linux/circ_buf.h> | ||
21 | #include <linux/debugfs.h> | ||
22 | #include <linux/delay.h> | ||
23 | #include <linux/fs.h> | ||
24 | #include <linux/mfd/cros_ec.h> | ||
25 | #include <linux/mfd/cros_ec_commands.h> | ||
26 | #include <linux/mutex.h> | ||
27 | #include <linux/poll.h> | ||
28 | #include <linux/sched.h> | ||
29 | #include <linux/slab.h> | ||
30 | #include <linux/wait.h> | ||
31 | |||
32 | #include "cros_ec_dev.h" | ||
33 | #include "cros_ec_debugfs.h" | ||
34 | |||
35 | #define LOG_SHIFT 14 | ||
36 | #define LOG_SIZE (1 << LOG_SHIFT) | ||
37 | #define LOG_POLL_SEC 10 | ||
38 | |||
39 | #define CIRC_ADD(idx, size, value) (((idx) + (value)) & ((size) - 1)) | ||
40 | |||
41 | /* struct cros_ec_debugfs - ChromeOS EC debugging information | ||
42 | * | ||
43 | * @ec: EC device this debugfs information belongs to | ||
44 | * @dir: dentry for debugfs files | ||
45 | * @log_buffer: circular buffer for console log information | ||
46 | * @read_msg: preallocated EC command and buffer to read console log | ||
47 | * @log_mutex: mutex to protect circular buffer | ||
48 | * @log_wq: waitqueue for log readers | ||
49 | * @log_poll_work: recurring task to poll EC for new console log data | ||
50 | * @panicinfo_blob: panicinfo debugfs blob | ||
51 | */ | ||
52 | struct cros_ec_debugfs { | ||
53 | struct cros_ec_dev *ec; | ||
54 | struct dentry *dir; | ||
55 | /* EC log */ | ||
56 | struct circ_buf log_buffer; | ||
57 | struct cros_ec_command *read_msg; | ||
58 | struct mutex log_mutex; | ||
59 | wait_queue_head_t log_wq; | ||
60 | struct delayed_work log_poll_work; | ||
61 | /* EC panicinfo */ | ||
62 | struct debugfs_blob_wrapper panicinfo_blob; | ||
63 | }; | ||
64 | |||
65 | /* | ||
66 | * We need to make sure that the EC log buffer on the UART is large enough, | ||
67 | * so that it is unlikely enough to overlow within LOG_POLL_SEC. | ||
68 | */ | ||
69 | static void cros_ec_console_log_work(struct work_struct *__work) | ||
70 | { | ||
71 | struct cros_ec_debugfs *debug_info = | ||
72 | container_of(to_delayed_work(__work), | ||
73 | struct cros_ec_debugfs, | ||
74 | log_poll_work); | ||
75 | struct cros_ec_dev *ec = debug_info->ec; | ||
76 | struct circ_buf *cb = &debug_info->log_buffer; | ||
77 | struct cros_ec_command snapshot_msg = { | ||
78 | .command = EC_CMD_CONSOLE_SNAPSHOT + ec->cmd_offset, | ||
79 | }; | ||
80 | |||
81 | struct ec_params_console_read_v1 *read_params = | ||
82 | (struct ec_params_console_read_v1 *)debug_info->read_msg->data; | ||
83 | uint8_t *ec_buffer = (uint8_t *)debug_info->read_msg->data; | ||
84 | int idx; | ||
85 | int buf_space; | ||
86 | int ret; | ||
87 | |||
88 | ret = cros_ec_cmd_xfer(ec->ec_dev, &snapshot_msg); | ||
89 | if (ret < 0) { | ||
90 | dev_err(ec->dev, "EC communication failed\n"); | ||
91 | goto resched; | ||
92 | } | ||
93 | if (snapshot_msg.result != EC_RES_SUCCESS) { | ||
94 | dev_err(ec->dev, "EC failed to snapshot the console log\n"); | ||
95 | goto resched; | ||
96 | } | ||
97 | |||
98 | /* Loop until we have read everything, or there's an error. */ | ||
99 | mutex_lock(&debug_info->log_mutex); | ||
100 | buf_space = CIRC_SPACE(cb->head, cb->tail, LOG_SIZE); | ||
101 | |||
102 | while (1) { | ||
103 | if (!buf_space) { | ||
104 | dev_info_once(ec->dev, | ||
105 | "Some logs may have been dropped...\n"); | ||
106 | break; | ||
107 | } | ||
108 | |||
109 | memset(read_params, '\0', sizeof(*read_params)); | ||
110 | read_params->subcmd = CONSOLE_READ_RECENT; | ||
111 | ret = cros_ec_cmd_xfer(ec->ec_dev, debug_info->read_msg); | ||
112 | if (ret < 0) { | ||
113 | dev_err(ec->dev, "EC communication failed\n"); | ||
114 | break; | ||
115 | } | ||
116 | if (debug_info->read_msg->result != EC_RES_SUCCESS) { | ||
117 | dev_err(ec->dev, | ||
118 | "EC failed to read the console log\n"); | ||
119 | break; | ||
120 | } | ||
121 | |||
122 | /* If the buffer is empty, we're done here. */ | ||
123 | if (ret == 0 || ec_buffer[0] == '\0') | ||
124 | break; | ||
125 | |||
126 | idx = 0; | ||
127 | while (idx < ret && ec_buffer[idx] != '\0' && buf_space > 0) { | ||
128 | cb->buf[cb->head] = ec_buffer[idx]; | ||
129 | cb->head = CIRC_ADD(cb->head, LOG_SIZE, 1); | ||
130 | idx++; | ||
131 | buf_space--; | ||
132 | } | ||
133 | |||
134 | wake_up(&debug_info->log_wq); | ||
135 | } | ||
136 | |||
137 | mutex_unlock(&debug_info->log_mutex); | ||
138 | |||
139 | resched: | ||
140 | schedule_delayed_work(&debug_info->log_poll_work, | ||
141 | msecs_to_jiffies(LOG_POLL_SEC * 1000)); | ||
142 | } | ||
143 | |||
144 | static int cros_ec_console_log_open(struct inode *inode, struct file *file) | ||
145 | { | ||
146 | file->private_data = inode->i_private; | ||
147 | |||
148 | return nonseekable_open(inode, file); | ||
149 | } | ||
150 | |||
151 | static ssize_t cros_ec_console_log_read(struct file *file, char __user *buf, | ||
152 | size_t count, loff_t *ppos) | ||
153 | { | ||
154 | struct cros_ec_debugfs *debug_info = file->private_data; | ||
155 | struct circ_buf *cb = &debug_info->log_buffer; | ||
156 | ssize_t ret; | ||
157 | |||
158 | mutex_lock(&debug_info->log_mutex); | ||
159 | |||
160 | while (!CIRC_CNT(cb->head, cb->tail, LOG_SIZE)) { | ||
161 | if (file->f_flags & O_NONBLOCK) { | ||
162 | ret = -EAGAIN; | ||
163 | goto error; | ||
164 | } | ||
165 | |||
166 | mutex_unlock(&debug_info->log_mutex); | ||
167 | |||
168 | ret = wait_event_interruptible(debug_info->log_wq, | ||
169 | CIRC_CNT(cb->head, cb->tail, LOG_SIZE)); | ||
170 | if (ret < 0) | ||
171 | return ret; | ||
172 | |||
173 | mutex_lock(&debug_info->log_mutex); | ||
174 | } | ||
175 | |||
176 | /* Only copy until the end of the circular buffer, and let userspace | ||
177 | * retry to get the rest of the data. | ||
178 | */ | ||
179 | ret = min_t(size_t, CIRC_CNT_TO_END(cb->head, cb->tail, LOG_SIZE), | ||
180 | count); | ||
181 | |||
182 | if (copy_to_user(buf, cb->buf + cb->tail, ret)) { | ||
183 | ret = -EFAULT; | ||
184 | goto error; | ||
185 | } | ||
186 | |||
187 | cb->tail = CIRC_ADD(cb->tail, LOG_SIZE, ret); | ||
188 | |||
189 | error: | ||
190 | mutex_unlock(&debug_info->log_mutex); | ||
191 | return ret; | ||
192 | } | ||
193 | |||
194 | static unsigned int cros_ec_console_log_poll(struct file *file, | ||
195 | poll_table *wait) | ||
196 | { | ||
197 | struct cros_ec_debugfs *debug_info = file->private_data; | ||
198 | unsigned int mask = 0; | ||
199 | |||
200 | poll_wait(file, &debug_info->log_wq, wait); | ||
201 | |||
202 | mutex_lock(&debug_info->log_mutex); | ||
203 | if (CIRC_CNT(debug_info->log_buffer.head, | ||
204 | debug_info->log_buffer.tail, | ||
205 | LOG_SIZE)) | ||
206 | mask |= POLLIN | POLLRDNORM; | ||
207 | mutex_unlock(&debug_info->log_mutex); | ||
208 | |||
209 | return mask; | ||
210 | } | ||
211 | |||
212 | static int cros_ec_console_log_release(struct inode *inode, struct file *file) | ||
213 | { | ||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | const struct file_operations cros_ec_console_log_fops = { | ||
218 | .owner = THIS_MODULE, | ||
219 | .open = cros_ec_console_log_open, | ||
220 | .read = cros_ec_console_log_read, | ||
221 | .llseek = no_llseek, | ||
222 | .poll = cros_ec_console_log_poll, | ||
223 | .release = cros_ec_console_log_release, | ||
224 | }; | ||
225 | |||
226 | static int ec_read_version_supported(struct cros_ec_dev *ec) | ||
227 | { | ||
228 | struct ec_params_get_cmd_versions_v1 *params; | ||
229 | struct ec_response_get_cmd_versions *response; | ||
230 | int ret; | ||
231 | |||
232 | struct cros_ec_command *msg; | ||
233 | |||
234 | msg = kzalloc(sizeof(*msg) + max(sizeof(*params), sizeof(*response)), | ||
235 | GFP_KERNEL); | ||
236 | if (!msg) | ||
237 | return 0; | ||
238 | |||
239 | msg->command = EC_CMD_GET_CMD_VERSIONS + ec->cmd_offset; | ||
240 | msg->outsize = sizeof(*params); | ||
241 | msg->insize = sizeof(*response); | ||
242 | |||
243 | params = (struct ec_params_get_cmd_versions_v1 *)msg->data; | ||
244 | params->cmd = EC_CMD_CONSOLE_READ; | ||
245 | response = (struct ec_response_get_cmd_versions *)msg->data; | ||
246 | |||
247 | ret = cros_ec_cmd_xfer(ec->ec_dev, msg) >= 0 && | ||
248 | msg->result == EC_RES_SUCCESS && | ||
249 | (response->version_mask & EC_VER_MASK(1)); | ||
250 | |||
251 | kfree(msg); | ||
252 | |||
253 | return ret; | ||
254 | } | ||
255 | |||
256 | static int cros_ec_create_console_log(struct cros_ec_debugfs *debug_info) | ||
257 | { | ||
258 | struct cros_ec_dev *ec = debug_info->ec; | ||
259 | char *buf; | ||
260 | int read_params_size; | ||
261 | int read_response_size; | ||
262 | |||
263 | if (!ec_read_version_supported(ec)) { | ||
264 | dev_warn(ec->dev, | ||
265 | "device does not support reading the console log\n"); | ||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | buf = devm_kzalloc(ec->dev, LOG_SIZE, GFP_KERNEL); | ||
270 | if (!buf) | ||
271 | return -ENOMEM; | ||
272 | |||
273 | read_params_size = sizeof(struct ec_params_console_read_v1); | ||
274 | read_response_size = ec->ec_dev->max_response; | ||
275 | debug_info->read_msg = devm_kzalloc(ec->dev, | ||
276 | sizeof(*debug_info->read_msg) + | ||
277 | max(read_params_size, read_response_size), GFP_KERNEL); | ||
278 | if (!debug_info->read_msg) | ||
279 | return -ENOMEM; | ||
280 | |||
281 | debug_info->read_msg->version = 1; | ||
282 | debug_info->read_msg->command = EC_CMD_CONSOLE_READ + ec->cmd_offset; | ||
283 | debug_info->read_msg->outsize = read_params_size; | ||
284 | debug_info->read_msg->insize = read_response_size; | ||
285 | |||
286 | debug_info->log_buffer.buf = buf; | ||
287 | debug_info->log_buffer.head = 0; | ||
288 | debug_info->log_buffer.tail = 0; | ||
289 | |||
290 | mutex_init(&debug_info->log_mutex); | ||
291 | init_waitqueue_head(&debug_info->log_wq); | ||
292 | |||
293 | if (!debugfs_create_file("console_log", | ||
294 | S_IFREG | S_IRUGO, | ||
295 | debug_info->dir, | ||
296 | debug_info, | ||
297 | &cros_ec_console_log_fops)) | ||
298 | return -ENOMEM; | ||
299 | |||
300 | INIT_DELAYED_WORK(&debug_info->log_poll_work, | ||
301 | cros_ec_console_log_work); | ||
302 | schedule_delayed_work(&debug_info->log_poll_work, 0); | ||
303 | |||
304 | return 0; | ||
305 | } | ||
306 | |||
307 | static void cros_ec_cleanup_console_log(struct cros_ec_debugfs *debug_info) | ||
308 | { | ||
309 | if (debug_info->log_buffer.buf) { | ||
310 | cancel_delayed_work_sync(&debug_info->log_poll_work); | ||
311 | mutex_destroy(&debug_info->log_mutex); | ||
312 | } | ||
313 | } | ||
314 | |||
315 | static int cros_ec_create_panicinfo(struct cros_ec_debugfs *debug_info) | ||
316 | { | ||
317 | struct cros_ec_device *ec_dev = debug_info->ec->ec_dev; | ||
318 | int ret; | ||
319 | struct cros_ec_command *msg; | ||
320 | int insize; | ||
321 | |||
322 | insize = ec_dev->max_response; | ||
323 | |||
324 | msg = devm_kzalloc(debug_info->ec->dev, | ||
325 | sizeof(*msg) + insize, GFP_KERNEL); | ||
326 | if (!msg) | ||
327 | return -ENOMEM; | ||
328 | |||
329 | msg->command = EC_CMD_GET_PANIC_INFO; | ||
330 | msg->insize = insize; | ||
331 | |||
332 | ret = cros_ec_cmd_xfer(ec_dev, msg); | ||
333 | if (ret < 0) { | ||
334 | dev_warn(debug_info->ec->dev, "Cannot read panicinfo.\n"); | ||
335 | ret = 0; | ||
336 | goto free; | ||
337 | } | ||
338 | |||
339 | /* No panic data */ | ||
340 | if (ret == 0) | ||
341 | goto free; | ||
342 | |||
343 | debug_info->panicinfo_blob.data = msg->data; | ||
344 | debug_info->panicinfo_blob.size = ret; | ||
345 | |||
346 | if (!debugfs_create_blob("panicinfo", | ||
347 | S_IFREG | S_IRUGO, | ||
348 | debug_info->dir, | ||
349 | &debug_info->panicinfo_blob)) { | ||
350 | ret = -ENOMEM; | ||
351 | goto free; | ||
352 | } | ||
353 | |||
354 | return 0; | ||
355 | |||
356 | free: | ||
357 | devm_kfree(debug_info->ec->dev, msg); | ||
358 | return ret; | ||
359 | } | ||
360 | |||
361 | int cros_ec_debugfs_init(struct cros_ec_dev *ec) | ||
362 | { | ||
363 | struct cros_ec_platform *ec_platform = dev_get_platdata(ec->dev); | ||
364 | const char *name = ec_platform->ec_name; | ||
365 | struct cros_ec_debugfs *debug_info; | ||
366 | int ret; | ||
367 | |||
368 | debug_info = devm_kzalloc(ec->dev, sizeof(*debug_info), GFP_KERNEL); | ||
369 | if (!debug_info) | ||
370 | return -ENOMEM; | ||
371 | |||
372 | debug_info->ec = ec; | ||
373 | debug_info->dir = debugfs_create_dir(name, NULL); | ||
374 | if (!debug_info->dir) | ||
375 | return -ENOMEM; | ||
376 | |||
377 | ret = cros_ec_create_panicinfo(debug_info); | ||
378 | if (ret) | ||
379 | goto remove_debugfs; | ||
380 | |||
381 | ret = cros_ec_create_console_log(debug_info); | ||
382 | if (ret) | ||
383 | goto remove_debugfs; | ||
384 | |||
385 | ec->debug_info = debug_info; | ||
386 | |||
387 | return 0; | ||
388 | |||
389 | remove_debugfs: | ||
390 | debugfs_remove_recursive(debug_info->dir); | ||
391 | return ret; | ||
392 | } | ||
393 | |||
394 | void cros_ec_debugfs_remove(struct cros_ec_dev *ec) | ||
395 | { | ||
396 | if (!ec->debug_info) | ||
397 | return; | ||
398 | |||
399 | debugfs_remove_recursive(ec->debug_info->dir); | ||
400 | cros_ec_cleanup_console_log(ec->debug_info); | ||
401 | } | ||
diff --git a/drivers/platform/chrome/cros_ec_debugfs.h b/drivers/platform/chrome/cros_ec_debugfs.h new file mode 100644 index 000000000000..1ff3a50aa1b8 --- /dev/null +++ b/drivers/platform/chrome/cros_ec_debugfs.h | |||
@@ -0,0 +1,27 @@ | |||
1 | /* | ||
2 | * Copyright 2015 Google, Inc. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | #ifndef _DRV_CROS_EC_DEBUGFS_H_ | ||
19 | #define _DRV_CROS_EC_DEBUGFS_H_ | ||
20 | |||
21 | #include "cros_ec_dev.h" | ||
22 | |||
23 | /* debugfs stuff */ | ||
24 | int cros_ec_debugfs_init(struct cros_ec_dev *ec); | ||
25 | void cros_ec_debugfs_remove(struct cros_ec_dev *ec); | ||
26 | |||
27 | #endif /* _DRV_CROS_EC_DEBUGFS_H_ */ | ||
diff --git a/drivers/platform/chrome/cros_ec_dev.c b/drivers/platform/chrome/cros_ec_dev.c index 6aa120cd0574..cf6c4f0846b8 100644 --- a/drivers/platform/chrome/cros_ec_dev.c +++ b/drivers/platform/chrome/cros_ec_dev.c | |||
@@ -21,9 +21,11 @@ | |||
21 | #include <linux/mfd/core.h> | 21 | #include <linux/mfd/core.h> |
22 | #include <linux/module.h> | 22 | #include <linux/module.h> |
23 | #include <linux/platform_device.h> | 23 | #include <linux/platform_device.h> |
24 | #include <linux/pm.h> | ||
24 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
25 | #include <linux/uaccess.h> | 26 | #include <linux/uaccess.h> |
26 | 27 | ||
28 | #include "cros_ec_debugfs.h" | ||
27 | #include "cros_ec_dev.h" | 29 | #include "cros_ec_dev.h" |
28 | 30 | ||
29 | /* Device variables */ | 31 | /* Device variables */ |
@@ -427,10 +429,16 @@ static int ec_device_probe(struct platform_device *pdev) | |||
427 | goto failed; | 429 | goto failed; |
428 | } | 430 | } |
429 | 431 | ||
432 | if (cros_ec_debugfs_init(ec)) | ||
433 | dev_warn(dev, "failed to create debugfs directory\n"); | ||
434 | |||
430 | /* check whether this EC is a sensor hub. */ | 435 | /* check whether this EC is a sensor hub. */ |
431 | if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE)) | 436 | if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE)) |
432 | cros_ec_sensors_register(ec); | 437 | cros_ec_sensors_register(ec); |
433 | 438 | ||
439 | /* Take control of the lightbar from the EC. */ | ||
440 | lb_manual_suspend_ctrl(ec, 1); | ||
441 | |||
434 | return 0; | 442 | return 0; |
435 | 443 | ||
436 | failed: | 444 | failed: |
@@ -441,6 +449,12 @@ failed: | |||
441 | static int ec_device_remove(struct platform_device *pdev) | 449 | static int ec_device_remove(struct platform_device *pdev) |
442 | { | 450 | { |
443 | struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev); | 451 | struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev); |
452 | |||
453 | /* Let the EC take over the lightbar again. */ | ||
454 | lb_manual_suspend_ctrl(ec, 0); | ||
455 | |||
456 | cros_ec_debugfs_remove(ec); | ||
457 | |||
444 | cdev_del(&ec->cdev); | 458 | cdev_del(&ec->cdev); |
445 | device_unregister(&ec->class_dev); | 459 | device_unregister(&ec->class_dev); |
446 | return 0; | 460 | return 0; |
@@ -452,9 +466,35 @@ static const struct platform_device_id cros_ec_id[] = { | |||
452 | }; | 466 | }; |
453 | MODULE_DEVICE_TABLE(platform, cros_ec_id); | 467 | MODULE_DEVICE_TABLE(platform, cros_ec_id); |
454 | 468 | ||
469 | static __maybe_unused int ec_device_suspend(struct device *dev) | ||
470 | { | ||
471 | struct cros_ec_dev *ec = dev_get_drvdata(dev); | ||
472 | |||
473 | lb_suspend(ec); | ||
474 | |||
475 | return 0; | ||
476 | } | ||
477 | |||
478 | static __maybe_unused int ec_device_resume(struct device *dev) | ||
479 | { | ||
480 | struct cros_ec_dev *ec = dev_get_drvdata(dev); | ||
481 | |||
482 | lb_resume(ec); | ||
483 | |||
484 | return 0; | ||
485 | } | ||
486 | |||
487 | static const struct dev_pm_ops cros_ec_dev_pm_ops = { | ||
488 | #ifdef CONFIG_PM_SLEEP | ||
489 | .suspend = ec_device_suspend, | ||
490 | .resume = ec_device_resume, | ||
491 | #endif | ||
492 | }; | ||
493 | |||
455 | static struct platform_driver cros_ec_dev_driver = { | 494 | static struct platform_driver cros_ec_dev_driver = { |
456 | .driver = { | 495 | .driver = { |
457 | .name = "cros-ec-ctl", | 496 | .name = "cros-ec-ctl", |
497 | .pm = &cros_ec_dev_pm_ops, | ||
458 | }, | 498 | }, |
459 | .probe = ec_device_probe, | 499 | .probe = ec_device_probe, |
460 | .remove = ec_device_remove, | 500 | .remove = ec_device_remove, |
diff --git a/drivers/platform/chrome/cros_ec_dev.h b/drivers/platform/chrome/cros_ec_dev.h index bfd2c84c3571..45e9453608c5 100644 --- a/drivers/platform/chrome/cros_ec_dev.h +++ b/drivers/platform/chrome/cros_ec_dev.h | |||
@@ -43,4 +43,10 @@ struct cros_ec_readmem { | |||
43 | #define CROS_EC_DEV_IOCXCMD _IOWR(CROS_EC_DEV_IOC, 0, struct cros_ec_command) | 43 | #define CROS_EC_DEV_IOCXCMD _IOWR(CROS_EC_DEV_IOC, 0, struct cros_ec_command) |
44 | #define CROS_EC_DEV_IOCRDMEM _IOWR(CROS_EC_DEV_IOC, 1, struct cros_ec_readmem) | 44 | #define CROS_EC_DEV_IOCRDMEM _IOWR(CROS_EC_DEV_IOC, 1, struct cros_ec_readmem) |
45 | 45 | ||
46 | /* Lightbar utilities */ | ||
47 | extern bool ec_has_lightbar(struct cros_ec_dev *ec); | ||
48 | extern int lb_manual_suspend_ctrl(struct cros_ec_dev *ec, uint8_t enable); | ||
49 | extern int lb_suspend(struct cros_ec_dev *ec); | ||
50 | extern int lb_resume(struct cros_ec_dev *ec); | ||
51 | |||
46 | #endif /* _CROS_EC_DEV_H_ */ | 52 | #endif /* _CROS_EC_DEV_H_ */ |
diff --git a/drivers/platform/chrome/cros_ec_lightbar.c b/drivers/platform/chrome/cros_ec_lightbar.c index 8df3d447cacf..fd2b047a2748 100644 --- a/drivers/platform/chrome/cros_ec_lightbar.c +++ b/drivers/platform/chrome/cros_ec_lightbar.c | |||
@@ -38,6 +38,13 @@ | |||
38 | /* Rate-limit the lightbar interface to prevent DoS. */ | 38 | /* Rate-limit the lightbar interface to prevent DoS. */ |
39 | static unsigned long lb_interval_jiffies = 50 * HZ / 1000; | 39 | static unsigned long lb_interval_jiffies = 50 * HZ / 1000; |
40 | 40 | ||
41 | /* | ||
42 | * Whether or not we have given userspace control of the lightbar. | ||
43 | * If this is true, we won't do anything during suspend/resume. | ||
44 | */ | ||
45 | static bool userspace_control; | ||
46 | static struct cros_ec_dev *ec_with_lightbar; | ||
47 | |||
41 | static ssize_t interval_msec_show(struct device *dev, | 48 | static ssize_t interval_msec_show(struct device *dev, |
42 | struct device_attribute *attr, char *buf) | 49 | struct device_attribute *attr, char *buf) |
43 | { | 50 | { |
@@ -295,7 +302,8 @@ exit: | |||
295 | 302 | ||
296 | static char const *seqname[] = { | 303 | static char const *seqname[] = { |
297 | "ERROR", "S5", "S3", "S0", "S5S3", "S3S0", | 304 | "ERROR", "S5", "S3", "S0", "S5S3", "S3S0", |
298 | "S0S3", "S3S5", "STOP", "RUN", "PULSE", "TEST", "KONAMI", | 305 | "S0S3", "S3S5", "STOP", "RUN", "KONAMI", |
306 | "TAP", "PROGRAM", | ||
299 | }; | 307 | }; |
300 | 308 | ||
301 | static ssize_t sequence_show(struct device *dev, | 309 | static ssize_t sequence_show(struct device *dev, |
@@ -340,6 +348,89 @@ exit: | |||
340 | return ret; | 348 | return ret; |
341 | } | 349 | } |
342 | 350 | ||
351 | static int lb_send_empty_cmd(struct cros_ec_dev *ec, uint8_t cmd) | ||
352 | { | ||
353 | struct ec_params_lightbar *param; | ||
354 | struct cros_ec_command *msg; | ||
355 | int ret; | ||
356 | |||
357 | msg = alloc_lightbar_cmd_msg(ec); | ||
358 | if (!msg) | ||
359 | return -ENOMEM; | ||
360 | |||
361 | param = (struct ec_params_lightbar *)msg->data; | ||
362 | param->cmd = cmd; | ||
363 | |||
364 | ret = lb_throttle(); | ||
365 | if (ret) | ||
366 | goto error; | ||
367 | |||
368 | ret = cros_ec_cmd_xfer(ec->ec_dev, msg); | ||
369 | if (ret < 0) | ||
370 | goto error; | ||
371 | if (msg->result != EC_RES_SUCCESS) { | ||
372 | ret = -EINVAL; | ||
373 | goto error; | ||
374 | } | ||
375 | ret = 0; | ||
376 | error: | ||
377 | kfree(msg); | ||
378 | |||
379 | return ret; | ||
380 | } | ||
381 | |||
382 | int lb_manual_suspend_ctrl(struct cros_ec_dev *ec, uint8_t enable) | ||
383 | { | ||
384 | struct ec_params_lightbar *param; | ||
385 | struct cros_ec_command *msg; | ||
386 | int ret; | ||
387 | |||
388 | if (ec != ec_with_lightbar) | ||
389 | return 0; | ||
390 | |||
391 | msg = alloc_lightbar_cmd_msg(ec); | ||
392 | if (!msg) | ||
393 | return -ENOMEM; | ||
394 | |||
395 | param = (struct ec_params_lightbar *)msg->data; | ||
396 | |||
397 | param->cmd = LIGHTBAR_CMD_MANUAL_SUSPEND_CTRL; | ||
398 | param->manual_suspend_ctrl.enable = enable; | ||
399 | |||
400 | ret = lb_throttle(); | ||
401 | if (ret) | ||
402 | goto error; | ||
403 | |||
404 | ret = cros_ec_cmd_xfer(ec->ec_dev, msg); | ||
405 | if (ret < 0) | ||
406 | goto error; | ||
407 | if (msg->result != EC_RES_SUCCESS) { | ||
408 | ret = -EINVAL; | ||
409 | goto error; | ||
410 | } | ||
411 | ret = 0; | ||
412 | error: | ||
413 | kfree(msg); | ||
414 | |||
415 | return ret; | ||
416 | } | ||
417 | |||
418 | int lb_suspend(struct cros_ec_dev *ec) | ||
419 | { | ||
420 | if (userspace_control || ec != ec_with_lightbar) | ||
421 | return 0; | ||
422 | |||
423 | return lb_send_empty_cmd(ec, LIGHTBAR_CMD_SUSPEND); | ||
424 | } | ||
425 | |||
426 | int lb_resume(struct cros_ec_dev *ec) | ||
427 | { | ||
428 | if (userspace_control || ec != ec_with_lightbar) | ||
429 | return 0; | ||
430 | |||
431 | return lb_send_empty_cmd(ec, LIGHTBAR_CMD_RESUME); | ||
432 | } | ||
433 | |||
343 | static ssize_t sequence_store(struct device *dev, struct device_attribute *attr, | 434 | static ssize_t sequence_store(struct device *dev, struct device_attribute *attr, |
344 | const char *buf, size_t count) | 435 | const char *buf, size_t count) |
345 | { | 436 | { |
@@ -390,6 +481,93 @@ exit: | |||
390 | return ret; | 481 | return ret; |
391 | } | 482 | } |
392 | 483 | ||
484 | static ssize_t program_store(struct device *dev, struct device_attribute *attr, | ||
485 | const char *buf, size_t count) | ||
486 | { | ||
487 | int extra_bytes, max_size, ret; | ||
488 | struct ec_params_lightbar *param; | ||
489 | struct cros_ec_command *msg; | ||
490 | struct cros_ec_dev *ec = container_of(dev, struct cros_ec_dev, | ||
491 | class_dev); | ||
492 | |||
493 | /* | ||
494 | * We might need to reject the program for size reasons. The EC | ||
495 | * enforces a maximum program size, but we also don't want to try | ||
496 | * and send a program that is too big for the protocol. In order | ||
497 | * to ensure the latter, we also need to ensure we have extra bytes | ||
498 | * to represent the rest of the packet. | ||
499 | */ | ||
500 | extra_bytes = sizeof(*param) - sizeof(param->set_program.data); | ||
501 | max_size = min(EC_LB_PROG_LEN, ec->ec_dev->max_request - extra_bytes); | ||
502 | if (count > max_size) { | ||
503 | dev_err(dev, "Program is %u bytes, too long to send (max: %u)", | ||
504 | (unsigned int)count, max_size); | ||
505 | |||
506 | return -EINVAL; | ||
507 | } | ||
508 | |||
509 | msg = alloc_lightbar_cmd_msg(ec); | ||
510 | if (!msg) | ||
511 | return -ENOMEM; | ||
512 | |||
513 | ret = lb_throttle(); | ||
514 | if (ret) | ||
515 | goto exit; | ||
516 | |||
517 | dev_info(dev, "Copying %zu byte program to EC", count); | ||
518 | |||
519 | param = (struct ec_params_lightbar *)msg->data; | ||
520 | param->cmd = LIGHTBAR_CMD_SET_PROGRAM; | ||
521 | |||
522 | param->set_program.size = count; | ||
523 | memcpy(param->set_program.data, buf, count); | ||
524 | |||
525 | /* | ||
526 | * We need to set the message size manually or else it will use | ||
527 | * EC_LB_PROG_LEN. This might be too long, and the program | ||
528 | * is unlikely to use all of the space. | ||
529 | */ | ||
530 | msg->outsize = count + extra_bytes; | ||
531 | |||
532 | ret = cros_ec_cmd_xfer(ec->ec_dev, msg); | ||
533 | if (ret < 0) | ||
534 | goto exit; | ||
535 | if (msg->result != EC_RES_SUCCESS) { | ||
536 | ret = -EINVAL; | ||
537 | goto exit; | ||
538 | } | ||
539 | |||
540 | ret = count; | ||
541 | exit: | ||
542 | kfree(msg); | ||
543 | |||
544 | return ret; | ||
545 | } | ||
546 | |||
547 | static ssize_t userspace_control_show(struct device *dev, | ||
548 | struct device_attribute *attr, | ||
549 | char *buf) | ||
550 | { | ||
551 | return scnprintf(buf, PAGE_SIZE, "%d\n", userspace_control); | ||
552 | } | ||
553 | |||
554 | static ssize_t userspace_control_store(struct device *dev, | ||
555 | struct device_attribute *attr, | ||
556 | const char *buf, | ||
557 | size_t count) | ||
558 | { | ||
559 | bool enable; | ||
560 | int ret; | ||
561 | |||
562 | ret = strtobool(buf, &enable); | ||
563 | if (ret < 0) | ||
564 | return ret; | ||
565 | |||
566 | userspace_control = enable; | ||
567 | |||
568 | return count; | ||
569 | } | ||
570 | |||
393 | /* Module initialization */ | 571 | /* Module initialization */ |
394 | 572 | ||
395 | static DEVICE_ATTR_RW(interval_msec); | 573 | static DEVICE_ATTR_RW(interval_msec); |
@@ -397,15 +575,25 @@ static DEVICE_ATTR_RO(version); | |||
397 | static DEVICE_ATTR_WO(brightness); | 575 | static DEVICE_ATTR_WO(brightness); |
398 | static DEVICE_ATTR_WO(led_rgb); | 576 | static DEVICE_ATTR_WO(led_rgb); |
399 | static DEVICE_ATTR_RW(sequence); | 577 | static DEVICE_ATTR_RW(sequence); |
578 | static DEVICE_ATTR_WO(program); | ||
579 | static DEVICE_ATTR_RW(userspace_control); | ||
580 | |||
400 | static struct attribute *__lb_cmds_attrs[] = { | 581 | static struct attribute *__lb_cmds_attrs[] = { |
401 | &dev_attr_interval_msec.attr, | 582 | &dev_attr_interval_msec.attr, |
402 | &dev_attr_version.attr, | 583 | &dev_attr_version.attr, |
403 | &dev_attr_brightness.attr, | 584 | &dev_attr_brightness.attr, |
404 | &dev_attr_led_rgb.attr, | 585 | &dev_attr_led_rgb.attr, |
405 | &dev_attr_sequence.attr, | 586 | &dev_attr_sequence.attr, |
587 | &dev_attr_program.attr, | ||
588 | &dev_attr_userspace_control.attr, | ||
406 | NULL, | 589 | NULL, |
407 | }; | 590 | }; |
408 | 591 | ||
592 | bool ec_has_lightbar(struct cros_ec_dev *ec) | ||
593 | { | ||
594 | return !!get_lightbar_version(ec, NULL, NULL); | ||
595 | } | ||
596 | |||
409 | static umode_t cros_ec_lightbar_attrs_are_visible(struct kobject *kobj, | 597 | static umode_t cros_ec_lightbar_attrs_are_visible(struct kobject *kobj, |
410 | struct attribute *a, int n) | 598 | struct attribute *a, int n) |
411 | { | 599 | { |
@@ -422,10 +610,11 @@ static umode_t cros_ec_lightbar_attrs_are_visible(struct kobject *kobj, | |||
422 | return 0; | 610 | return 0; |
423 | 611 | ||
424 | /* Only instantiate this stuff if the EC has a lightbar */ | 612 | /* Only instantiate this stuff if the EC has a lightbar */ |
425 | if (get_lightbar_version(ec, NULL, NULL)) | 613 | if (ec_has_lightbar(ec)) { |
614 | ec_with_lightbar = ec; | ||
426 | return a->mode; | 615 | return a->mode; |
427 | else | 616 | } |
428 | return 0; | 617 | return 0; |
429 | } | 618 | } |
430 | 619 | ||
431 | struct attribute_group cros_ec_lightbar_attr_group = { | 620 | struct attribute_group cros_ec_lightbar_attr_group = { |
diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c index f9a245465fd0..2b6436d1b6a4 100644 --- a/drivers/platform/chrome/cros_ec_lpc.c +++ b/drivers/platform/chrome/cros_ec_lpc.c | |||
@@ -21,24 +21,29 @@ | |||
21 | * expensive. | 21 | * expensive. |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #include <linux/acpi.h> | ||
24 | #include <linux/dmi.h> | 25 | #include <linux/dmi.h> |
25 | #include <linux/delay.h> | 26 | #include <linux/delay.h> |
26 | #include <linux/io.h> | 27 | #include <linux/io.h> |
27 | #include <linux/mfd/cros_ec.h> | 28 | #include <linux/mfd/cros_ec.h> |
28 | #include <linux/mfd/cros_ec_commands.h> | 29 | #include <linux/mfd/cros_ec_commands.h> |
30 | #include <linux/mfd/cros_ec_lpc_reg.h> | ||
29 | #include <linux/module.h> | 31 | #include <linux/module.h> |
30 | #include <linux/platform_device.h> | 32 | #include <linux/platform_device.h> |
31 | #include <linux/printk.h> | 33 | #include <linux/printk.h> |
32 | 34 | ||
33 | #define DRV_NAME "cros_ec_lpc" | 35 | #define DRV_NAME "cros_ec_lpcs" |
36 | #define ACPI_DRV_NAME "GOOG0004" | ||
34 | 37 | ||
35 | static int ec_response_timed_out(void) | 38 | static int ec_response_timed_out(void) |
36 | { | 39 | { |
37 | unsigned long one_second = jiffies + HZ; | 40 | unsigned long one_second = jiffies + HZ; |
41 | u8 data; | ||
38 | 42 | ||
39 | usleep_range(200, 300); | 43 | usleep_range(200, 300); |
40 | do { | 44 | do { |
41 | if (!(inb(EC_LPC_ADDR_HOST_CMD) & EC_LPC_STATUS_BUSY_MASK)) | 45 | if (!(cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_CMD, 1, &data) & |
46 | EC_LPC_STATUS_BUSY_MASK)) | ||
42 | return 0; | 47 | return 0; |
43 | usleep_range(100, 200); | 48 | usleep_range(100, 200); |
44 | } while (time_before(jiffies, one_second)); | 49 | } while (time_before(jiffies, one_second)); |
@@ -51,21 +56,20 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec, | |||
51 | { | 56 | { |
52 | struct ec_host_request *request; | 57 | struct ec_host_request *request; |
53 | struct ec_host_response response; | 58 | struct ec_host_response response; |
54 | u8 sum = 0; | 59 | u8 sum; |
55 | int i; | ||
56 | int ret = 0; | 60 | int ret = 0; |
57 | u8 *dout; | 61 | u8 *dout; |
58 | 62 | ||
59 | ret = cros_ec_prepare_tx(ec, msg); | 63 | ret = cros_ec_prepare_tx(ec, msg); |
60 | 64 | ||
61 | /* Write buffer */ | 65 | /* Write buffer */ |
62 | for (i = 0; i < ret; i++) | 66 | cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_PACKET, ret, ec->dout); |
63 | outb(ec->dout[i], EC_LPC_ADDR_HOST_PACKET + i); | ||
64 | 67 | ||
65 | request = (struct ec_host_request *)ec->dout; | 68 | request = (struct ec_host_request *)ec->dout; |
66 | 69 | ||
67 | /* Here we go */ | 70 | /* Here we go */ |
68 | outb(EC_COMMAND_PROTOCOL_3, EC_LPC_ADDR_HOST_CMD); | 71 | sum = EC_COMMAND_PROTOCOL_3; |
72 | cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_CMD, 1, &sum); | ||
69 | 73 | ||
70 | if (ec_response_timed_out()) { | 74 | if (ec_response_timed_out()) { |
71 | dev_warn(ec->dev, "EC responsed timed out\n"); | 75 | dev_warn(ec->dev, "EC responsed timed out\n"); |
@@ -74,17 +78,15 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec, | |||
74 | } | 78 | } |
75 | 79 | ||
76 | /* Check result */ | 80 | /* Check result */ |
77 | msg->result = inb(EC_LPC_ADDR_HOST_DATA); | 81 | msg->result = cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_DATA, 1, &sum); |
78 | ret = cros_ec_check_result(ec, msg); | 82 | ret = cros_ec_check_result(ec, msg); |
79 | if (ret) | 83 | if (ret) |
80 | goto done; | 84 | goto done; |
81 | 85 | ||
82 | /* Read back response */ | 86 | /* Read back response */ |
83 | dout = (u8 *)&response; | 87 | dout = (u8 *)&response; |
84 | for (i = 0; i < sizeof(response); i++) { | 88 | sum = cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_PACKET, sizeof(response), |
85 | dout[i] = inb(EC_LPC_ADDR_HOST_PACKET + i); | 89 | dout); |
86 | sum += dout[i]; | ||
87 | } | ||
88 | 90 | ||
89 | msg->result = response.result; | 91 | msg->result = response.result; |
90 | 92 | ||
@@ -97,11 +99,9 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec, | |||
97 | } | 99 | } |
98 | 100 | ||
99 | /* Read response and process checksum */ | 101 | /* Read response and process checksum */ |
100 | for (i = 0; i < response.data_len; i++) { | 102 | sum += cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_PACKET + |
101 | msg->data[i] = | 103 | sizeof(response), response.data_len, |
102 | inb(EC_LPC_ADDR_HOST_PACKET + sizeof(response) + i); | 104 | msg->data); |
103 | sum += msg->data[i]; | ||
104 | } | ||
105 | 105 | ||
106 | if (sum) { | 106 | if (sum) { |
107 | dev_err(ec->dev, | 107 | dev_err(ec->dev, |
@@ -121,8 +121,7 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, | |||
121 | struct cros_ec_command *msg) | 121 | struct cros_ec_command *msg) |
122 | { | 122 | { |
123 | struct ec_lpc_host_args args; | 123 | struct ec_lpc_host_args args; |
124 | int csum; | 124 | u8 sum; |
125 | int i; | ||
126 | int ret = 0; | 125 | int ret = 0; |
127 | 126 | ||
128 | if (msg->outsize > EC_PROTO2_MAX_PARAM_SIZE || | 127 | if (msg->outsize > EC_PROTO2_MAX_PARAM_SIZE || |
@@ -139,24 +138,20 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, | |||
139 | args.data_size = msg->outsize; | 138 | args.data_size = msg->outsize; |
140 | 139 | ||
141 | /* Initialize checksum */ | 140 | /* Initialize checksum */ |
142 | csum = msg->command + args.flags + | 141 | sum = msg->command + args.flags + args.command_version + args.data_size; |
143 | args.command_version + args.data_size; | ||
144 | 142 | ||
145 | /* Copy data and update checksum */ | 143 | /* Copy data and update checksum */ |
146 | for (i = 0; i < msg->outsize; i++) { | 144 | sum += cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_PARAM, msg->outsize, |
147 | outb(msg->data[i], EC_LPC_ADDR_HOST_PARAM + i); | 145 | msg->data); |
148 | csum += msg->data[i]; | ||
149 | } | ||
150 | 146 | ||
151 | /* Finalize checksum and write args */ | 147 | /* Finalize checksum and write args */ |
152 | args.checksum = csum & 0xFF; | 148 | args.checksum = sum; |
153 | outb(args.flags, EC_LPC_ADDR_HOST_ARGS); | 149 | cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_ARGS, sizeof(args), |
154 | outb(args.command_version, EC_LPC_ADDR_HOST_ARGS + 1); | 150 | (u8 *)&args); |
155 | outb(args.data_size, EC_LPC_ADDR_HOST_ARGS + 2); | ||
156 | outb(args.checksum, EC_LPC_ADDR_HOST_ARGS + 3); | ||
157 | 151 | ||
158 | /* Here we go */ | 152 | /* Here we go */ |
159 | outb(msg->command, EC_LPC_ADDR_HOST_CMD); | 153 | sum = msg->command; |
154 | cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_CMD, 1, &sum); | ||
160 | 155 | ||
161 | if (ec_response_timed_out()) { | 156 | if (ec_response_timed_out()) { |
162 | dev_warn(ec->dev, "EC responsed timed out\n"); | 157 | dev_warn(ec->dev, "EC responsed timed out\n"); |
@@ -165,16 +160,14 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, | |||
165 | } | 160 | } |
166 | 161 | ||
167 | /* Check result */ | 162 | /* Check result */ |
168 | msg->result = inb(EC_LPC_ADDR_HOST_DATA); | 163 | msg->result = cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_DATA, 1, &sum); |
169 | ret = cros_ec_check_result(ec, msg); | 164 | ret = cros_ec_check_result(ec, msg); |
170 | if (ret) | 165 | if (ret) |
171 | goto done; | 166 | goto done; |
172 | 167 | ||
173 | /* Read back args */ | 168 | /* Read back args */ |
174 | args.flags = inb(EC_LPC_ADDR_HOST_ARGS); | 169 | cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_ARGS, sizeof(args), |
175 | args.command_version = inb(EC_LPC_ADDR_HOST_ARGS + 1); | 170 | (u8 *)&args); |
176 | args.data_size = inb(EC_LPC_ADDR_HOST_ARGS + 2); | ||
177 | args.checksum = inb(EC_LPC_ADDR_HOST_ARGS + 3); | ||
178 | 171 | ||
179 | if (args.data_size > msg->insize) { | 172 | if (args.data_size > msg->insize) { |
180 | dev_err(ec->dev, | 173 | dev_err(ec->dev, |
@@ -185,20 +178,17 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, | |||
185 | } | 178 | } |
186 | 179 | ||
187 | /* Start calculating response checksum */ | 180 | /* Start calculating response checksum */ |
188 | csum = msg->command + args.flags + | 181 | sum = msg->command + args.flags + args.command_version + args.data_size; |
189 | args.command_version + args.data_size; | ||
190 | 182 | ||
191 | /* Read response and update checksum */ | 183 | /* Read response and update checksum */ |
192 | for (i = 0; i < args.data_size; i++) { | 184 | sum += cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_PARAM, args.data_size, |
193 | msg->data[i] = inb(EC_LPC_ADDR_HOST_PARAM + i); | 185 | msg->data); |
194 | csum += msg->data[i]; | ||
195 | } | ||
196 | 186 | ||
197 | /* Verify checksum */ | 187 | /* Verify checksum */ |
198 | if (args.checksum != (csum & 0xFF)) { | 188 | if (args.checksum != sum) { |
199 | dev_err(ec->dev, | 189 | dev_err(ec->dev, |
200 | "bad packet checksum, expected %02x, got %02x\n", | 190 | "bad packet checksum, expected %02x, got %02x\n", |
201 | args.checksum, csum & 0xFF); | 191 | args.checksum, sum); |
202 | ret = -EBADMSG; | 192 | ret = -EBADMSG; |
203 | goto done; | 193 | goto done; |
204 | } | 194 | } |
@@ -222,14 +212,13 @@ static int cros_ec_lpc_readmem(struct cros_ec_device *ec, unsigned int offset, | |||
222 | 212 | ||
223 | /* fixed length */ | 213 | /* fixed length */ |
224 | if (bytes) { | 214 | if (bytes) { |
225 | for (; cnt < bytes; i++, s++, cnt++) | 215 | cros_ec_lpc_read_bytes(EC_LPC_ADDR_MEMMAP + offset, bytes, s); |
226 | *s = inb(EC_LPC_ADDR_MEMMAP + i); | 216 | return bytes; |
227 | return cnt; | ||
228 | } | 217 | } |
229 | 218 | ||
230 | /* string */ | 219 | /* string */ |
231 | for (; i < EC_MEMMAP_SIZE; i++, s++) { | 220 | for (; i < EC_MEMMAP_SIZE; i++, s++) { |
232 | *s = inb(EC_LPC_ADDR_MEMMAP + i); | 221 | cros_ec_lpc_read_bytes(EC_LPC_ADDR_MEMMAP + i, 1, s); |
233 | cnt++; | 222 | cnt++; |
234 | if (!*s) | 223 | if (!*s) |
235 | break; | 224 | break; |
@@ -238,10 +227,23 @@ static int cros_ec_lpc_readmem(struct cros_ec_device *ec, unsigned int offset, | |||
238 | return cnt; | 227 | return cnt; |
239 | } | 228 | } |
240 | 229 | ||
230 | static void cros_ec_lpc_acpi_notify(acpi_handle device, u32 value, void *data) | ||
231 | { | ||
232 | struct cros_ec_device *ec_dev = data; | ||
233 | |||
234 | if (ec_dev->mkbp_event_supported && | ||
235 | cros_ec_get_next_event(ec_dev, NULL) > 0) | ||
236 | blocking_notifier_call_chain(&ec_dev->event_notifier, 0, | ||
237 | ec_dev); | ||
238 | } | ||
239 | |||
241 | static int cros_ec_lpc_probe(struct platform_device *pdev) | 240 | static int cros_ec_lpc_probe(struct platform_device *pdev) |
242 | { | 241 | { |
243 | struct device *dev = &pdev->dev; | 242 | struct device *dev = &pdev->dev; |
243 | struct acpi_device *adev; | ||
244 | acpi_status status; | ||
244 | struct cros_ec_device *ec_dev; | 245 | struct cros_ec_device *ec_dev; |
246 | u8 buf[2]; | ||
245 | int ret; | 247 | int ret; |
246 | 248 | ||
247 | if (!devm_request_region(dev, EC_LPC_ADDR_MEMMAP, EC_MEMMAP_SIZE, | 249 | if (!devm_request_region(dev, EC_LPC_ADDR_MEMMAP, EC_MEMMAP_SIZE, |
@@ -250,8 +252,8 @@ static int cros_ec_lpc_probe(struct platform_device *pdev) | |||
250 | return -EBUSY; | 252 | return -EBUSY; |
251 | } | 253 | } |
252 | 254 | ||
253 | if ((inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID) != 'E') || | 255 | cros_ec_lpc_read_bytes(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID, 2, buf); |
254 | (inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID + 1) != 'C')) { | 256 | if (buf[0] != 'E' || buf[1] != 'C') { |
255 | dev_err(dev, "EC ID not detected\n"); | 257 | dev_err(dev, "EC ID not detected\n"); |
256 | return -ENODEV; | 258 | return -ENODEV; |
257 | } | 259 | } |
@@ -287,12 +289,33 @@ static int cros_ec_lpc_probe(struct platform_device *pdev) | |||
287 | return ret; | 289 | return ret; |
288 | } | 290 | } |
289 | 291 | ||
292 | /* | ||
293 | * Connect a notify handler to process MKBP messages if we have a | ||
294 | * companion ACPI device. | ||
295 | */ | ||
296 | adev = ACPI_COMPANION(dev); | ||
297 | if (adev) { | ||
298 | status = acpi_install_notify_handler(adev->handle, | ||
299 | ACPI_ALL_NOTIFY, | ||
300 | cros_ec_lpc_acpi_notify, | ||
301 | ec_dev); | ||
302 | if (ACPI_FAILURE(status)) | ||
303 | dev_warn(dev, "Failed to register notifier %08x\n", | ||
304 | status); | ||
305 | } | ||
306 | |||
290 | return 0; | 307 | return 0; |
291 | } | 308 | } |
292 | 309 | ||
293 | static int cros_ec_lpc_remove(struct platform_device *pdev) | 310 | static int cros_ec_lpc_remove(struct platform_device *pdev) |
294 | { | 311 | { |
295 | struct cros_ec_device *ec_dev; | 312 | struct cros_ec_device *ec_dev; |
313 | struct acpi_device *adev; | ||
314 | |||
315 | adev = ACPI_COMPANION(&pdev->dev); | ||
316 | if (adev) | ||
317 | acpi_remove_notify_handler(adev->handle, ACPI_ALL_NOTIFY, | ||
318 | cros_ec_lpc_acpi_notify); | ||
296 | 319 | ||
297 | ec_dev = platform_get_drvdata(pdev); | 320 | ec_dev = platform_get_drvdata(pdev); |
298 | cros_ec_remove(ec_dev); | 321 | cros_ec_remove(ec_dev); |
@@ -300,6 +323,12 @@ static int cros_ec_lpc_remove(struct platform_device *pdev) | |||
300 | return 0; | 323 | return 0; |
301 | } | 324 | } |
302 | 325 | ||
326 | static const struct acpi_device_id cros_ec_lpc_acpi_device_ids[] = { | ||
327 | { ACPI_DRV_NAME, 0 }, | ||
328 | { } | ||
329 | }; | ||
330 | MODULE_DEVICE_TABLE(acpi, cros_ec_lpc_acpi_device_ids); | ||
331 | |||
303 | static struct dmi_system_id cros_ec_lpc_dmi_table[] __initdata = { | 332 | static struct dmi_system_id cros_ec_lpc_dmi_table[] __initdata = { |
304 | { | 333 | { |
305 | /* | 334 | /* |
@@ -337,18 +366,36 @@ static struct dmi_system_id cros_ec_lpc_dmi_table[] __initdata = { | |||
337 | }; | 366 | }; |
338 | MODULE_DEVICE_TABLE(dmi, cros_ec_lpc_dmi_table); | 367 | MODULE_DEVICE_TABLE(dmi, cros_ec_lpc_dmi_table); |
339 | 368 | ||
369 | #ifdef CONFIG_PM_SLEEP | ||
370 | static int cros_ec_lpc_suspend(struct device *dev) | ||
371 | { | ||
372 | struct cros_ec_device *ec_dev = dev_get_drvdata(dev); | ||
373 | |||
374 | return cros_ec_suspend(ec_dev); | ||
375 | } | ||
376 | |||
377 | static int cros_ec_lpc_resume(struct device *dev) | ||
378 | { | ||
379 | struct cros_ec_device *ec_dev = dev_get_drvdata(dev); | ||
380 | |||
381 | return cros_ec_resume(ec_dev); | ||
382 | } | ||
383 | #endif | ||
384 | |||
385 | const struct dev_pm_ops cros_ec_lpc_pm_ops = { | ||
386 | SET_LATE_SYSTEM_SLEEP_PM_OPS(cros_ec_lpc_suspend, cros_ec_lpc_resume) | ||
387 | }; | ||
388 | |||
340 | static struct platform_driver cros_ec_lpc_driver = { | 389 | static struct platform_driver cros_ec_lpc_driver = { |
341 | .driver = { | 390 | .driver = { |
342 | .name = DRV_NAME, | 391 | .name = DRV_NAME, |
392 | .acpi_match_table = cros_ec_lpc_acpi_device_ids, | ||
393 | .pm = &cros_ec_lpc_pm_ops, | ||
343 | }, | 394 | }, |
344 | .probe = cros_ec_lpc_probe, | 395 | .probe = cros_ec_lpc_probe, |
345 | .remove = cros_ec_lpc_remove, | 396 | .remove = cros_ec_lpc_remove, |
346 | }; | 397 | }; |
347 | 398 | ||
348 | static struct platform_device cros_ec_lpc_device = { | ||
349 | .name = DRV_NAME | ||
350 | }; | ||
351 | |||
352 | static int __init cros_ec_lpc_init(void) | 399 | static int __init cros_ec_lpc_init(void) |
353 | { | 400 | { |
354 | int ret; | 401 | int ret; |
@@ -358,18 +405,13 @@ static int __init cros_ec_lpc_init(void) | |||
358 | return -ENODEV; | 405 | return -ENODEV; |
359 | } | 406 | } |
360 | 407 | ||
408 | cros_ec_lpc_reg_init(); | ||
409 | |||
361 | /* Register the driver */ | 410 | /* Register the driver */ |
362 | ret = platform_driver_register(&cros_ec_lpc_driver); | 411 | ret = platform_driver_register(&cros_ec_lpc_driver); |
363 | if (ret) { | 412 | if (ret) { |
364 | pr_err(DRV_NAME ": can't register driver: %d\n", ret); | 413 | pr_err(DRV_NAME ": can't register driver: %d\n", ret); |
365 | return ret; | 414 | cros_ec_lpc_reg_destroy(); |
366 | } | ||
367 | |||
368 | /* Register the device, and it'll get hooked up automatically */ | ||
369 | ret = platform_device_register(&cros_ec_lpc_device); | ||
370 | if (ret) { | ||
371 | pr_err(DRV_NAME ": can't register device: %d\n", ret); | ||
372 | platform_driver_unregister(&cros_ec_lpc_driver); | ||
373 | return ret; | 415 | return ret; |
374 | } | 416 | } |
375 | 417 | ||
@@ -378,8 +420,8 @@ static int __init cros_ec_lpc_init(void) | |||
378 | 420 | ||
379 | static void __exit cros_ec_lpc_exit(void) | 421 | static void __exit cros_ec_lpc_exit(void) |
380 | { | 422 | { |
381 | platform_device_unregister(&cros_ec_lpc_device); | ||
382 | platform_driver_unregister(&cros_ec_lpc_driver); | 423 | platform_driver_unregister(&cros_ec_lpc_driver); |
424 | cros_ec_lpc_reg_destroy(); | ||
383 | } | 425 | } |
384 | 426 | ||
385 | module_init(cros_ec_lpc_init); | 427 | module_init(cros_ec_lpc_init); |
diff --git a/drivers/platform/chrome/cros_ec_lpc_mec.c b/drivers/platform/chrome/cros_ec_lpc_mec.c new file mode 100644 index 000000000000..2eda2c2fc210 --- /dev/null +++ b/drivers/platform/chrome/cros_ec_lpc_mec.c | |||
@@ -0,0 +1,140 @@ | |||
1 | /* | ||
2 | * cros_ec_lpc_mec - LPC variant I/O for Microchip EC | ||
3 | * | ||
4 | * Copyright (C) 2016 Google, Inc | ||
5 | * | ||
6 | * This software is licensed under the terms of the GNU General Public | ||
7 | * License version 2, as published by the Free Software Foundation, and | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * This driver uses the Chrome OS EC byte-level message-based protocol for | ||
16 | * communicating the keyboard state (which keys are pressed) from a keyboard EC | ||
17 | * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing, | ||
18 | * but everything else (including deghosting) is done here. The main | ||
19 | * motivation for this is to keep the EC firmware as simple as possible, since | ||
20 | * it cannot be easily upgraded and EC flash/IRAM space is relatively | ||
21 | * expensive. | ||
22 | */ | ||
23 | |||
24 | #include <linux/delay.h> | ||
25 | #include <linux/io.h> | ||
26 | #include <linux/mfd/cros_ec_commands.h> | ||
27 | #include <linux/mfd/cros_ec_lpc_mec.h> | ||
28 | #include <linux/mutex.h> | ||
29 | #include <linux/types.h> | ||
30 | |||
31 | /* | ||
32 | * This mutex must be held while accessing the EMI unit. We can't rely on the | ||
33 | * EC mutex because memmap data may be accessed without it being held. | ||
34 | */ | ||
35 | static struct mutex io_mutex; | ||
36 | |||
37 | /* | ||
38 | * cros_ec_lpc_mec_emi_write_address | ||
39 | * | ||
40 | * Initialize EMI read / write at a given address. | ||
41 | * | ||
42 | * @addr: Starting read / write address | ||
43 | * @access_type: Type of access, typically 32-bit auto-increment | ||
44 | */ | ||
45 | static void cros_ec_lpc_mec_emi_write_address(u16 addr, | ||
46 | enum cros_ec_lpc_mec_emi_access_mode access_type) | ||
47 | { | ||
48 | /* Address relative to start of EMI range */ | ||
49 | addr -= MEC_EMI_RANGE_START; | ||
50 | outb((addr & 0xfc) | access_type, MEC_EMI_EC_ADDRESS_B0); | ||
51 | outb((addr >> 8) & 0x7f, MEC_EMI_EC_ADDRESS_B1); | ||
52 | } | ||
53 | |||
54 | /* | ||
55 | * cros_ec_lpc_io_bytes_mec - Read / write bytes to MEC EMI port | ||
56 | * | ||
57 | * @io_type: MEC_IO_READ or MEC_IO_WRITE, depending on request | ||
58 | * @offset: Base read / write address | ||
59 | * @length: Number of bytes to read / write | ||
60 | * @buf: Destination / source buffer | ||
61 | * | ||
62 | * @return 8-bit checksum of all bytes read / written | ||
63 | */ | ||
64 | u8 cros_ec_lpc_io_bytes_mec(enum cros_ec_lpc_mec_io_type io_type, | ||
65 | unsigned int offset, unsigned int length, | ||
66 | u8 *buf) | ||
67 | { | ||
68 | int i = 0; | ||
69 | int io_addr; | ||
70 | u8 sum = 0; | ||
71 | enum cros_ec_lpc_mec_emi_access_mode access, new_access; | ||
72 | |||
73 | /* | ||
74 | * Long access cannot be used on misaligned data since reading B0 loads | ||
75 | * the data register and writing B3 flushes. | ||
76 | */ | ||
77 | if (offset & 0x3 || length < 4) | ||
78 | access = ACCESS_TYPE_BYTE; | ||
79 | else | ||
80 | access = ACCESS_TYPE_LONG_AUTO_INCREMENT; | ||
81 | |||
82 | mutex_lock(&io_mutex); | ||
83 | |||
84 | /* Initialize I/O at desired address */ | ||
85 | cros_ec_lpc_mec_emi_write_address(offset, access); | ||
86 | |||
87 | /* Skip bytes in case of misaligned offset */ | ||
88 | io_addr = MEC_EMI_EC_DATA_B0 + (offset & 0x3); | ||
89 | while (i < length) { | ||
90 | while (io_addr <= MEC_EMI_EC_DATA_B3) { | ||
91 | if (io_type == MEC_IO_READ) | ||
92 | buf[i] = inb(io_addr++); | ||
93 | else | ||
94 | outb(buf[i], io_addr++); | ||
95 | |||
96 | sum += buf[i++]; | ||
97 | offset++; | ||
98 | |||
99 | /* Extra bounds check in case of misaligned length */ | ||
100 | if (i == length) | ||
101 | goto done; | ||
102 | } | ||
103 | |||
104 | /* | ||
105 | * Use long auto-increment access except for misaligned write, | ||
106 | * since writing B3 triggers the flush. | ||
107 | */ | ||
108 | if (length - i < 4 && io_type == MEC_IO_WRITE) | ||
109 | new_access = ACCESS_TYPE_BYTE; | ||
110 | else | ||
111 | new_access = ACCESS_TYPE_LONG_AUTO_INCREMENT; | ||
112 | |||
113 | if (new_access != access || | ||
114 | access != ACCESS_TYPE_LONG_AUTO_INCREMENT) { | ||
115 | access = new_access; | ||
116 | cros_ec_lpc_mec_emi_write_address(offset, access); | ||
117 | } | ||
118 | |||
119 | /* Access [B0, B3] on each loop pass */ | ||
120 | io_addr = MEC_EMI_EC_DATA_B0; | ||
121 | } | ||
122 | |||
123 | done: | ||
124 | mutex_unlock(&io_mutex); | ||
125 | |||
126 | return sum; | ||
127 | } | ||
128 | EXPORT_SYMBOL(cros_ec_lpc_io_bytes_mec); | ||
129 | |||
130 | void cros_ec_lpc_mec_init(void) | ||
131 | { | ||
132 | mutex_init(&io_mutex); | ||
133 | } | ||
134 | EXPORT_SYMBOL(cros_ec_lpc_mec_init); | ||
135 | |||
136 | void cros_ec_lpc_mec_destroy(void) | ||
137 | { | ||
138 | mutex_destroy(&io_mutex); | ||
139 | } | ||
140 | EXPORT_SYMBOL(cros_ec_lpc_mec_destroy); | ||
diff --git a/drivers/platform/chrome/cros_ec_lpc_reg.c b/drivers/platform/chrome/cros_ec_lpc_reg.c new file mode 100644 index 000000000000..dcc7a3e30604 --- /dev/null +++ b/drivers/platform/chrome/cros_ec_lpc_reg.c | |||
@@ -0,0 +1,133 @@ | |||
1 | /* | ||
2 | * cros_ec_lpc_reg - LPC access to the Chrome OS Embedded Controller | ||
3 | * | ||
4 | * Copyright (C) 2016 Google, Inc | ||
5 | * | ||
6 | * This software is licensed under the terms of the GNU General Public | ||
7 | * License version 2, as published by the Free Software Foundation, and | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * This driver uses the Chrome OS EC byte-level message-based protocol for | ||
16 | * communicating the keyboard state (which keys are pressed) from a keyboard EC | ||
17 | * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing, | ||
18 | * but everything else (including deghosting) is done here. The main | ||
19 | * motivation for this is to keep the EC firmware as simple as possible, since | ||
20 | * it cannot be easily upgraded and EC flash/IRAM space is relatively | ||
21 | * expensive. | ||
22 | */ | ||
23 | |||
24 | #include <linux/io.h> | ||
25 | #include <linux/mfd/cros_ec.h> | ||
26 | #include <linux/mfd/cros_ec_commands.h> | ||
27 | #include <linux/mfd/cros_ec_lpc_mec.h> | ||
28 | |||
29 | static u8 lpc_read_bytes(unsigned int offset, unsigned int length, u8 *dest) | ||
30 | { | ||
31 | int i; | ||
32 | int sum = 0; | ||
33 | |||
34 | for (i = 0; i < length; ++i) { | ||
35 | dest[i] = inb(offset + i); | ||
36 | sum += dest[i]; | ||
37 | } | ||
38 | |||
39 | /* Return checksum of all bytes read */ | ||
40 | return sum; | ||
41 | } | ||
42 | |||
43 | static u8 lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg) | ||
44 | { | ||
45 | int i; | ||
46 | int sum = 0; | ||
47 | |||
48 | for (i = 0; i < length; ++i) { | ||
49 | outb(msg[i], offset + i); | ||
50 | sum += msg[i]; | ||
51 | } | ||
52 | |||
53 | /* Return checksum of all bytes written */ | ||
54 | return sum; | ||
55 | } | ||
56 | |||
57 | #ifdef CONFIG_CROS_EC_LPC_MEC | ||
58 | |||
59 | u8 cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length, u8 *dest) | ||
60 | { | ||
61 | if (length == 0) | ||
62 | return 0; | ||
63 | |||
64 | /* Access desired range through EMI interface */ | ||
65 | if (offset >= MEC_EMI_RANGE_START && offset <= MEC_EMI_RANGE_END) { | ||
66 | /* Ensure we don't straddle EMI region */ | ||
67 | if (WARN_ON(offset + length - 1 > MEC_EMI_RANGE_END)) | ||
68 | return 0; | ||
69 | |||
70 | return cros_ec_lpc_io_bytes_mec(MEC_IO_READ, offset, length, | ||
71 | dest); | ||
72 | } | ||
73 | |||
74 | if (WARN_ON(offset + length > MEC_EMI_RANGE_START && | ||
75 | offset < MEC_EMI_RANGE_START)) | ||
76 | return 0; | ||
77 | |||
78 | return lpc_read_bytes(offset, length, dest); | ||
79 | } | ||
80 | |||
81 | u8 cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg) | ||
82 | { | ||
83 | if (length == 0) | ||
84 | return 0; | ||
85 | |||
86 | /* Access desired range through EMI interface */ | ||
87 | if (offset >= MEC_EMI_RANGE_START && offset <= MEC_EMI_RANGE_END) { | ||
88 | /* Ensure we don't straddle EMI region */ | ||
89 | if (WARN_ON(offset + length - 1 > MEC_EMI_RANGE_END)) | ||
90 | return 0; | ||
91 | |||
92 | return cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, offset, length, | ||
93 | msg); | ||
94 | } | ||
95 | |||
96 | if (WARN_ON(offset + length > MEC_EMI_RANGE_START && | ||
97 | offset < MEC_EMI_RANGE_START)) | ||
98 | return 0; | ||
99 | |||
100 | return lpc_write_bytes(offset, length, msg); | ||
101 | } | ||
102 | |||
103 | void cros_ec_lpc_reg_init(void) | ||
104 | { | ||
105 | cros_ec_lpc_mec_init(); | ||
106 | } | ||
107 | |||
108 | void cros_ec_lpc_reg_destroy(void) | ||
109 | { | ||
110 | cros_ec_lpc_mec_destroy(); | ||
111 | } | ||
112 | |||
113 | #else /* CONFIG_CROS_EC_LPC_MEC */ | ||
114 | |||
115 | u8 cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length, u8 *dest) | ||
116 | { | ||
117 | return lpc_read_bytes(offset, length, dest); | ||
118 | } | ||
119 | |||
120 | u8 cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg) | ||
121 | { | ||
122 | return lpc_write_bytes(offset, length, msg); | ||
123 | } | ||
124 | |||
125 | void cros_ec_lpc_reg_init(void) | ||
126 | { | ||
127 | } | ||
128 | |||
129 | void cros_ec_lpc_reg_destroy(void) | ||
130 | { | ||
131 | } | ||
132 | |||
133 | #endif /* CONFIG_CROS_EC_LPC_MEC */ | ||
diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c index ed5dee744c74..8dfa7fcb1248 100644 --- a/drivers/platform/chrome/cros_ec_proto.c +++ b/drivers/platform/chrome/cros_ec_proto.c | |||
@@ -150,6 +150,40 @@ int cros_ec_check_result(struct cros_ec_device *ec_dev, | |||
150 | } | 150 | } |
151 | EXPORT_SYMBOL(cros_ec_check_result); | 151 | EXPORT_SYMBOL(cros_ec_check_result); |
152 | 152 | ||
153 | /* | ||
154 | * cros_ec_get_host_event_wake_mask | ||
155 | * | ||
156 | * Get the mask of host events that cause wake from suspend. | ||
157 | * | ||
158 | * @ec_dev: EC device to call | ||
159 | * @msg: message structure to use | ||
160 | * @mask: result when function returns >=0. | ||
161 | * | ||
162 | * LOCKING: | ||
163 | * the caller has ec_dev->lock mutex, or the caller knows there is | ||
164 | * no other command in progress. | ||
165 | */ | ||
166 | static int cros_ec_get_host_event_wake_mask(struct cros_ec_device *ec_dev, | ||
167 | struct cros_ec_command *msg, | ||
168 | uint32_t *mask) | ||
169 | { | ||
170 | struct ec_response_host_event_mask *r; | ||
171 | int ret; | ||
172 | |||
173 | msg->command = EC_CMD_HOST_EVENT_GET_WAKE_MASK; | ||
174 | msg->version = 0; | ||
175 | msg->outsize = 0; | ||
176 | msg->insize = sizeof(*r); | ||
177 | |||
178 | ret = send_command(ec_dev, msg); | ||
179 | if (ret > 0) { | ||
180 | r = (struct ec_response_host_event_mask *)msg->data; | ||
181 | *mask = r->mask; | ||
182 | } | ||
183 | |||
184 | return ret; | ||
185 | } | ||
186 | |||
153 | static int cros_ec_host_command_proto_query(struct cros_ec_device *ec_dev, | 187 | static int cros_ec_host_command_proto_query(struct cros_ec_device *ec_dev, |
154 | int devidx, | 188 | int devidx, |
155 | struct cros_ec_command *msg) | 189 | struct cros_ec_command *msg) |
@@ -235,6 +269,22 @@ static int cros_ec_host_command_proto_query_v2(struct cros_ec_device *ec_dev) | |||
235 | return ret; | 269 | return ret; |
236 | } | 270 | } |
237 | 271 | ||
272 | /* | ||
273 | * cros_ec_get_host_command_version_mask | ||
274 | * | ||
275 | * Get the version mask of a given command. | ||
276 | * | ||
277 | * @ec_dev: EC device to call | ||
278 | * @msg: message structure to use | ||
279 | * @cmd: command to get the version of. | ||
280 | * @mask: result when function returns 0. | ||
281 | * | ||
282 | * @return 0 on success, error code otherwise | ||
283 | * | ||
284 | * LOCKING: | ||
285 | * the caller has ec_dev->lock mutex or the caller knows there is | ||
286 | * no other command in progress. | ||
287 | */ | ||
238 | static int cros_ec_get_host_command_version_mask(struct cros_ec_device *ec_dev, | 288 | static int cros_ec_get_host_command_version_mask(struct cros_ec_device *ec_dev, |
239 | u16 cmd, u32 *mask) | 289 | u16 cmd, u32 *mask) |
240 | { | 290 | { |
@@ -256,7 +306,7 @@ static int cros_ec_get_host_command_version_mask(struct cros_ec_device *ec_dev, | |||
256 | pver = (struct ec_params_get_cmd_versions *)msg->data; | 306 | pver = (struct ec_params_get_cmd_versions *)msg->data; |
257 | pver->cmd = cmd; | 307 | pver->cmd = cmd; |
258 | 308 | ||
259 | ret = cros_ec_cmd_xfer(ec_dev, msg); | 309 | ret = send_command(ec_dev, msg); |
260 | if (ret > 0) { | 310 | if (ret > 0) { |
261 | rver = (struct ec_response_get_cmd_versions *)msg->data; | 311 | rver = (struct ec_response_get_cmd_versions *)msg->data; |
262 | *mask = rver->version_mask; | 312 | *mask = rver->version_mask; |
@@ -371,6 +421,17 @@ int cros_ec_query_all(struct cros_ec_device *ec_dev) | |||
371 | else | 421 | else |
372 | ec_dev->mkbp_event_supported = 1; | 422 | ec_dev->mkbp_event_supported = 1; |
373 | 423 | ||
424 | /* | ||
425 | * Get host event wake mask, assume all events are wake events | ||
426 | * if unavailable. | ||
427 | */ | ||
428 | ret = cros_ec_get_host_event_wake_mask(ec_dev, proto_msg, | ||
429 | &ec_dev->host_event_wake_mask); | ||
430 | if (ret < 0) | ||
431 | ec_dev->host_event_wake_mask = U32_MAX; | ||
432 | |||
433 | ret = 0; | ||
434 | |||
374 | exit: | 435 | exit: |
375 | kfree(proto_msg); | 436 | kfree(proto_msg); |
376 | return ret; | 437 | return ret; |
@@ -486,11 +547,54 @@ static int get_keyboard_state_event(struct cros_ec_device *ec_dev) | |||
486 | return ec_dev->event_size; | 547 | return ec_dev->event_size; |
487 | } | 548 | } |
488 | 549 | ||
489 | int cros_ec_get_next_event(struct cros_ec_device *ec_dev) | 550 | int cros_ec_get_next_event(struct cros_ec_device *ec_dev, bool *wake_event) |
490 | { | 551 | { |
491 | if (ec_dev->mkbp_event_supported) | 552 | u32 host_event; |
492 | return get_next_event(ec_dev); | 553 | int ret; |
493 | else | 554 | |
494 | return get_keyboard_state_event(ec_dev); | 555 | if (!ec_dev->mkbp_event_supported) { |
556 | ret = get_keyboard_state_event(ec_dev); | ||
557 | if (ret < 0) | ||
558 | return ret; | ||
559 | |||
560 | if (wake_event) | ||
561 | *wake_event = true; | ||
562 | |||
563 | return ret; | ||
564 | } | ||
565 | |||
566 | ret = get_next_event(ec_dev); | ||
567 | if (ret < 0) | ||
568 | return ret; | ||
569 | |||
570 | if (wake_event) { | ||
571 | host_event = cros_ec_get_host_event(ec_dev); | ||
572 | |||
573 | /* Consider non-host_event as wake event */ | ||
574 | *wake_event = !host_event || | ||
575 | !!(host_event & ec_dev->host_event_wake_mask); | ||
576 | } | ||
577 | |||
578 | return ret; | ||
495 | } | 579 | } |
496 | EXPORT_SYMBOL(cros_ec_get_next_event); | 580 | EXPORT_SYMBOL(cros_ec_get_next_event); |
581 | |||
582 | u32 cros_ec_get_host_event(struct cros_ec_device *ec_dev) | ||
583 | { | ||
584 | u32 host_event; | ||
585 | |||
586 | BUG_ON(!ec_dev->mkbp_event_supported); | ||
587 | |||
588 | if (ec_dev->event_data.event_type != EC_MKBP_EVENT_HOST_EVENT) | ||
589 | return 0; | ||
590 | |||
591 | if (ec_dev->event_size != sizeof(host_event)) { | ||
592 | dev_warn(ec_dev->dev, "Invalid host event size\n"); | ||
593 | return 0; | ||
594 | } | ||
595 | |||
596 | host_event = get_unaligned_le32(&ec_dev->event_data.data.host_event); | ||
597 | |||
598 | return host_event; | ||
599 | } | ||
600 | EXPORT_SYMBOL(cros_ec_get_host_event); | ||
diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h index 28baee63eaf6..4e887ba22635 100644 --- a/include/linux/mfd/cros_ec.h +++ b/include/linux/mfd/cros_ec.h | |||
@@ -149,6 +149,7 @@ struct cros_ec_device { | |||
149 | 149 | ||
150 | struct ec_response_get_next_event event_data; | 150 | struct ec_response_get_next_event event_data; |
151 | int event_size; | 151 | int event_size; |
152 | u32 host_event_wake_mask; | ||
152 | }; | 153 | }; |
153 | 154 | ||
154 | /** | 155 | /** |
@@ -172,6 +173,8 @@ struct cros_ec_platform { | |||
172 | u16 cmd_offset; | 173 | u16 cmd_offset; |
173 | }; | 174 | }; |
174 | 175 | ||
176 | struct cros_ec_debugfs; | ||
177 | |||
175 | /* | 178 | /* |
176 | * struct cros_ec_dev - ChromeOS EC device entry point | 179 | * struct cros_ec_dev - ChromeOS EC device entry point |
177 | * | 180 | * |
@@ -179,6 +182,7 @@ struct cros_ec_platform { | |||
179 | * @cdev: Character device structure in /dev | 182 | * @cdev: Character device structure in /dev |
180 | * @ec_dev: cros_ec_device structure to talk to the physical device | 183 | * @ec_dev: cros_ec_device structure to talk to the physical device |
181 | * @dev: pointer to the platform device | 184 | * @dev: pointer to the platform device |
185 | * @debug_info: cros_ec_debugfs structure for debugging information | ||
182 | * @cmd_offset: offset to apply for each command. | 186 | * @cmd_offset: offset to apply for each command. |
183 | */ | 187 | */ |
184 | struct cros_ec_dev { | 188 | struct cros_ec_dev { |
@@ -186,6 +190,7 @@ struct cros_ec_dev { | |||
186 | struct cdev cdev; | 190 | struct cdev cdev; |
187 | struct cros_ec_device *ec_dev; | 191 | struct cros_ec_device *ec_dev; |
188 | struct device *dev; | 192 | struct device *dev; |
193 | struct cros_ec_debugfs *debug_info; | ||
189 | u16 cmd_offset; | 194 | u16 cmd_offset; |
190 | u32 features[2]; | 195 | u32 features[2]; |
191 | }; | 196 | }; |
@@ -295,10 +300,22 @@ int cros_ec_query_all(struct cros_ec_device *ec_dev); | |||
295 | * cros_ec_get_next_event - Fetch next event from the ChromeOS EC | 300 | * cros_ec_get_next_event - Fetch next event from the ChromeOS EC |
296 | * | 301 | * |
297 | * @ec_dev: Device to fetch event from | 302 | * @ec_dev: Device to fetch event from |
303 | * @wake_event: Pointer to a bool set to true upon return if the event might be | ||
304 | * treated as a wake event. Ignored if null. | ||
298 | * | 305 | * |
299 | * Returns: 0 on success, Linux error number on failure | 306 | * Returns: 0 on success, Linux error number on failure |
300 | */ | 307 | */ |
301 | int cros_ec_get_next_event(struct cros_ec_device *ec_dev); | 308 | int cros_ec_get_next_event(struct cros_ec_device *ec_dev, bool *wake_event); |
309 | |||
310 | /** | ||
311 | * cros_ec_get_host_event - Return a mask of event set by the EC. | ||
312 | * | ||
313 | * When MKBP is supported, when the EC raises an interrupt, | ||
314 | * We collect the events raised and call the functions in the ec notifier. | ||
315 | * | ||
316 | * This function is a helper to know which events are raised. | ||
317 | */ | ||
318 | u32 cros_ec_get_host_event(struct cros_ec_device *ec_dev); | ||
302 | 319 | ||
303 | /* sysfs stuff */ | 320 | /* sysfs stuff */ |
304 | extern struct attribute_group cros_ec_attr_group; | 321 | extern struct attribute_group cros_ec_attr_group; |
diff --git a/include/linux/mfd/cros_ec_commands.h b/include/linux/mfd/cros_ec_commands.h index c93e7e0300ef..190c8f4afa02 100644 --- a/include/linux/mfd/cros_ec_commands.h +++ b/include/linux/mfd/cros_ec_commands.h | |||
@@ -625,6 +625,10 @@ struct ec_params_get_cmd_versions { | |||
625 | uint8_t cmd; /* Command to check */ | 625 | uint8_t cmd; /* Command to check */ |
626 | } __packed; | 626 | } __packed; |
627 | 627 | ||
628 | struct ec_params_get_cmd_versions_v1 { | ||
629 | uint16_t cmd; /* Command to check */ | ||
630 | } __packed; | ||
631 | |||
628 | struct ec_response_get_cmd_versions { | 632 | struct ec_response_get_cmd_versions { |
629 | /* | 633 | /* |
630 | * Mask of supported versions; use EC_VER_MASK() to compare with a | 634 | * Mask of supported versions; use EC_VER_MASK() to compare with a |
@@ -1158,13 +1162,20 @@ struct lightbar_params_v1 { | |||
1158 | struct rgb_s color[8]; /* 0-3 are Google colors */ | 1162 | struct rgb_s color[8]; /* 0-3 are Google colors */ |
1159 | } __packed; | 1163 | } __packed; |
1160 | 1164 | ||
1165 | /* Lightbar program */ | ||
1166 | #define EC_LB_PROG_LEN 192 | ||
1167 | struct lightbar_program { | ||
1168 | uint8_t size; | ||
1169 | uint8_t data[EC_LB_PROG_LEN]; | ||
1170 | }; | ||
1171 | |||
1161 | struct ec_params_lightbar { | 1172 | struct ec_params_lightbar { |
1162 | uint8_t cmd; /* Command (see enum lightbar_command) */ | 1173 | uint8_t cmd; /* Command (see enum lightbar_command) */ |
1163 | union { | 1174 | union { |
1164 | struct { | 1175 | struct { |
1165 | /* no args */ | 1176 | /* no args */ |
1166 | } dump, off, on, init, get_seq, get_params_v0, get_params_v1, | 1177 | } dump, off, on, init, get_seq, get_params_v0, get_params_v1, |
1167 | version, get_brightness, get_demo; | 1178 | version, get_brightness, get_demo, suspend, resume; |
1168 | 1179 | ||
1169 | struct { | 1180 | struct { |
1170 | uint8_t num; | 1181 | uint8_t num; |
@@ -1182,8 +1193,13 @@ struct ec_params_lightbar { | |||
1182 | uint8_t led; | 1193 | uint8_t led; |
1183 | } get_rgb; | 1194 | } get_rgb; |
1184 | 1195 | ||
1196 | struct { | ||
1197 | uint8_t enable; | ||
1198 | } manual_suspend_ctrl; | ||
1199 | |||
1185 | struct lightbar_params_v0 set_params_v0; | 1200 | struct lightbar_params_v0 set_params_v0; |
1186 | struct lightbar_params_v1 set_params_v1; | 1201 | struct lightbar_params_v1 set_params_v1; |
1202 | struct lightbar_program set_program; | ||
1187 | }; | 1203 | }; |
1188 | } __packed; | 1204 | } __packed; |
1189 | 1205 | ||
@@ -1216,7 +1232,8 @@ struct ec_response_lightbar { | |||
1216 | struct { | 1232 | struct { |
1217 | /* no return params */ | 1233 | /* no return params */ |
1218 | } off, on, init, set_brightness, seq, reg, set_rgb, | 1234 | } off, on, init, set_brightness, seq, reg, set_rgb, |
1219 | demo, set_params_v0, set_params_v1; | 1235 | demo, set_params_v0, set_params_v1, |
1236 | set_program, manual_suspend_ctrl, suspend, resume; | ||
1220 | }; | 1237 | }; |
1221 | } __packed; | 1238 | } __packed; |
1222 | 1239 | ||
@@ -1240,6 +1257,10 @@ enum lightbar_command { | |||
1240 | LIGHTBAR_CMD_GET_DEMO = 15, | 1257 | LIGHTBAR_CMD_GET_DEMO = 15, |
1241 | LIGHTBAR_CMD_GET_PARAMS_V1 = 16, | 1258 | LIGHTBAR_CMD_GET_PARAMS_V1 = 16, |
1242 | LIGHTBAR_CMD_SET_PARAMS_V1 = 17, | 1259 | LIGHTBAR_CMD_SET_PARAMS_V1 = 17, |
1260 | LIGHTBAR_CMD_SET_PROGRAM = 18, | ||
1261 | LIGHTBAR_CMD_MANUAL_SUSPEND_CTRL = 19, | ||
1262 | LIGHTBAR_CMD_SUSPEND = 20, | ||
1263 | LIGHTBAR_CMD_RESUME = 21, | ||
1243 | LIGHTBAR_NUM_CMDS | 1264 | LIGHTBAR_NUM_CMDS |
1244 | }; | 1265 | }; |
1245 | 1266 | ||
@@ -2285,13 +2306,28 @@ struct ec_params_charge_control { | |||
2285 | #define EC_CMD_CONSOLE_SNAPSHOT 0x97 | 2306 | #define EC_CMD_CONSOLE_SNAPSHOT 0x97 |
2286 | 2307 | ||
2287 | /* | 2308 | /* |
2288 | * Read next chunk of data from saved snapshot. | 2309 | * Read data from the saved snapshot. If the subcmd parameter is |
2310 | * CONSOLE_READ_NEXT, this will return data starting from the beginning of | ||
2311 | * the latest snapshot. If it is CONSOLE_READ_RECENT, it will start from the | ||
2312 | * end of the previous snapshot. | ||
2313 | * | ||
2314 | * The params are only looked at in version >= 1 of this command. Prior | ||
2315 | * versions will just default to CONSOLE_READ_NEXT behavior. | ||
2289 | * | 2316 | * |
2290 | * Response is null-terminated string. Empty string, if there is no more | 2317 | * Response is null-terminated string. Empty string, if there is no more |
2291 | * remaining output. | 2318 | * remaining output. |
2292 | */ | 2319 | */ |
2293 | #define EC_CMD_CONSOLE_READ 0x98 | 2320 | #define EC_CMD_CONSOLE_READ 0x98 |
2294 | 2321 | ||
2322 | enum ec_console_read_subcmd { | ||
2323 | CONSOLE_READ_NEXT = 0, | ||
2324 | CONSOLE_READ_RECENT | ||
2325 | }; | ||
2326 | |||
2327 | struct ec_params_console_read_v1 { | ||
2328 | uint8_t subcmd; /* enum ec_console_read_subcmd */ | ||
2329 | } __packed; | ||
2330 | |||
2295 | /*****************************************************************************/ | 2331 | /*****************************************************************************/ |
2296 | 2332 | ||
2297 | /* | 2333 | /* |
diff --git a/include/linux/mfd/cros_ec_lpc_mec.h b/include/linux/mfd/cros_ec_lpc_mec.h new file mode 100644 index 000000000000..176496ddc66c --- /dev/null +++ b/include/linux/mfd/cros_ec_lpc_mec.h | |||
@@ -0,0 +1,90 @@ | |||
1 | /* | ||
2 | * cros_ec_lpc_mec - LPC variant I/O for Microchip EC | ||
3 | * | ||
4 | * Copyright (C) 2016 Google, Inc | ||
5 | * | ||
6 | * This software is licensed under the terms of the GNU General Public | ||
7 | * License version 2, as published by the Free Software Foundation, and | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * This driver uses the Chrome OS EC byte-level message-based protocol for | ||
16 | * communicating the keyboard state (which keys are pressed) from a keyboard EC | ||
17 | * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing, | ||
18 | * but everything else (including deghosting) is done here. The main | ||
19 | * motivation for this is to keep the EC firmware as simple as possible, since | ||
20 | * it cannot be easily upgraded and EC flash/IRAM space is relatively | ||
21 | * expensive. | ||
22 | */ | ||
23 | |||
24 | #ifndef __LINUX_MFD_CROS_EC_MEC_H | ||
25 | #define __LINUX_MFD_CROS_EC_MEC_H | ||
26 | |||
27 | #include <linux/mfd/cros_ec_commands.h> | ||
28 | |||
29 | enum cros_ec_lpc_mec_emi_access_mode { | ||
30 | /* 8-bit access */ | ||
31 | ACCESS_TYPE_BYTE = 0x0, | ||
32 | /* 16-bit access */ | ||
33 | ACCESS_TYPE_WORD = 0x1, | ||
34 | /* 32-bit access */ | ||
35 | ACCESS_TYPE_LONG = 0x2, | ||
36 | /* | ||
37 | * 32-bit access, read or write of MEC_EMI_EC_DATA_B3 causes the | ||
38 | * EC data register to be incremented. | ||
39 | */ | ||
40 | ACCESS_TYPE_LONG_AUTO_INCREMENT = 0x3, | ||
41 | }; | ||
42 | |||
43 | enum cros_ec_lpc_mec_io_type { | ||
44 | MEC_IO_READ, | ||
45 | MEC_IO_WRITE, | ||
46 | }; | ||
47 | |||
48 | /* Access IO ranges 0x800 thru 0x9ff using EMI interface instead of LPC */ | ||
49 | #define MEC_EMI_RANGE_START EC_HOST_CMD_REGION0 | ||
50 | #define MEC_EMI_RANGE_END (EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SIZE) | ||
51 | |||
52 | /* EMI registers are relative to base */ | ||
53 | #define MEC_EMI_BASE 0x800 | ||
54 | #define MEC_EMI_HOST_TO_EC (MEC_EMI_BASE + 0) | ||
55 | #define MEC_EMI_EC_TO_HOST (MEC_EMI_BASE + 1) | ||
56 | #define MEC_EMI_EC_ADDRESS_B0 (MEC_EMI_BASE + 2) | ||
57 | #define MEC_EMI_EC_ADDRESS_B1 (MEC_EMI_BASE + 3) | ||
58 | #define MEC_EMI_EC_DATA_B0 (MEC_EMI_BASE + 4) | ||
59 | #define MEC_EMI_EC_DATA_B1 (MEC_EMI_BASE + 5) | ||
60 | #define MEC_EMI_EC_DATA_B2 (MEC_EMI_BASE + 6) | ||
61 | #define MEC_EMI_EC_DATA_B3 (MEC_EMI_BASE + 7) | ||
62 | |||
63 | /* | ||
64 | * cros_ec_lpc_mec_init | ||
65 | * | ||
66 | * Initialize MEC I/O. | ||
67 | */ | ||
68 | void cros_ec_lpc_mec_init(void); | ||
69 | |||
70 | /* | ||
71 | * cros_ec_lpc_mec_destroy | ||
72 | * | ||
73 | * Cleanup MEC I/O. | ||
74 | */ | ||
75 | void cros_ec_lpc_mec_destroy(void); | ||
76 | |||
77 | /** | ||
78 | * cros_ec_lpc_io_bytes_mec - Read / write bytes to MEC EMI port | ||
79 | * | ||
80 | * @io_type: MEC_IO_READ or MEC_IO_WRITE, depending on request | ||
81 | * @offset: Base read / write address | ||
82 | * @length: Number of bytes to read / write | ||
83 | * @buf: Destination / source buffer | ||
84 | * | ||
85 | * @return 8-bit checksum of all bytes read / written | ||
86 | */ | ||
87 | u8 cros_ec_lpc_io_bytes_mec(enum cros_ec_lpc_mec_io_type io_type, | ||
88 | unsigned int offset, unsigned int length, u8 *buf); | ||
89 | |||
90 | #endif /* __LINUX_MFD_CROS_EC_MEC_H */ | ||
diff --git a/include/linux/mfd/cros_ec_lpc_reg.h b/include/linux/mfd/cros_ec_lpc_reg.h new file mode 100644 index 000000000000..5560bef63c2b --- /dev/null +++ b/include/linux/mfd/cros_ec_lpc_reg.h | |||
@@ -0,0 +1,61 @@ | |||
1 | /* | ||
2 | * cros_ec_lpc_reg - LPC access to the Chrome OS Embedded Controller | ||
3 | * | ||
4 | * Copyright (C) 2016 Google, Inc | ||
5 | * | ||
6 | * This software is licensed under the terms of the GNU General Public | ||
7 | * License version 2, as published by the Free Software Foundation, and | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * This driver uses the Chrome OS EC byte-level message-based protocol for | ||
16 | * communicating the keyboard state (which keys are pressed) from a keyboard EC | ||
17 | * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing, | ||
18 | * but everything else (including deghosting) is done here. The main | ||
19 | * motivation for this is to keep the EC firmware as simple as possible, since | ||
20 | * it cannot be easily upgraded and EC flash/IRAM space is relatively | ||
21 | * expensive. | ||
22 | */ | ||
23 | |||
24 | #ifndef __LINUX_MFD_CROS_EC_REG_H | ||
25 | #define __LINUX_MFD_CROS_EC_REG_H | ||
26 | |||
27 | /** | ||
28 | * cros_ec_lpc_read_bytes - Read bytes from a given LPC-mapped address. | ||
29 | * Returns 8-bit checksum of all bytes read. | ||
30 | * | ||
31 | * @offset: Base read address | ||
32 | * @length: Number of bytes to read | ||
33 | * @dest: Destination buffer | ||
34 | */ | ||
35 | u8 cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length, u8 *dest); | ||
36 | |||
37 | /** | ||
38 | * cros_ec_lpc_write_bytes - Write bytes to a given LPC-mapped address. | ||
39 | * Returns 8-bit checksum of all bytes written. | ||
40 | * | ||
41 | * @offset: Base write address | ||
42 | * @length: Number of bytes to write | ||
43 | * @msg: Write data buffer | ||
44 | */ | ||
45 | u8 cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg); | ||
46 | |||
47 | /** | ||
48 | * cros_ec_lpc_reg_init | ||
49 | * | ||
50 | * Initialize register I/O. | ||
51 | */ | ||
52 | void cros_ec_lpc_reg_init(void); | ||
53 | |||
54 | /** | ||
55 | * cros_ec_lpc_reg_destroy | ||
56 | * | ||
57 | * Cleanup reg I/O. | ||
58 | */ | ||
59 | void cros_ec_lpc_reg_destroy(void); | ||
60 | |||
61 | #endif /* __LINUX_MFD_CROS_EC_REG_H */ | ||