diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-07-11 12:55:47 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-07-11 12:55:47 -0400 |
| commit | a3ddacbae5abc0a5aabb1e75b655e8cd6dc83888 (patch) | |
| tree | 9b33fb1fdb67080e05b0603c5639bd7b6149b523 | |
| parent | a0188177344c8efdb1467889a0ec148554869e15 (diff) | |
| parent | 3c778a7fcfaa4bb51aefb4f2c7364a5e2c4d0b66 (diff) | |
Merge tag 'chrome-platform-for-linus-4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/bleung/chrome-platform
Pull chrome platform updates from Benson Leung:
"Changes in this pull request are around catching up cros_ec with the
internal chromeos-kernel versions of cros_ec, cros_ec_lpc, and
cros_ec_lightbar.
Also, switching maintainership from olof to bleung"
* tag 'chrome-platform-for-linus-4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/bleung/chrome-platform:
platform/chrome : Add myself as Maintainer
platform/chrome: cros_ec_lightbar - hide unused PM functions
cros_ec: Don't signal wake event for non-wake host events
cros_ec: Fix deadlock when EC is not responsive at probe
cros_ec: Don't return error when checking command version
platform/chrome: cros_ec_lightbar - Avoid I2C xfer to EC during suspend
platform/chrome: cros_ec_lightbar - Add userspace lightbar control bit to EC
platform/chrome: cros_ec_lightbar - Control of suspend/resume lightbar sequence
platform/chrome: cros_ec_lightbar - Add lightbar program feature to sysfs
platform/chrome: cros_ec_lpc: Add MKBP events support over ACPI
platform/chrome: cros_ec_lpc: Add power management ops
platform/chrome: cros_ec_lpc: Add support for GOOG004 ACPI device
platform/chrome: cros_ec_lpc: Add support for mec1322 EC
platform/chrome: cros_ec_lpc: Add R/W helpers to LPC protocol variants
mfd: cros_ec: Add support for dumping panic information
cros_ec_debugfs: Pass proper struct sizes to cros_ec_cmd_xfer()
mfd: cros_ec: add debugfs, console log file
mfd: cros_ec: Add EC console read structures definitions
mfd: cros_ec: Add helper for event notifier.
| -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 */ | ||
