aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform/chrome/cros_ec_sysfs.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-04-26 16:36:02 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2015-04-26 16:36:02 -0400
commit36a8032d77649430f5ef11fbf0df2bb026be0b04 (patch)
tree18e386a957bde5f71e9efd2cddf1d8aadafe48e4 /drivers/platform/chrome/cros_ec_sysfs.c
parent7f9f44308c8993c9ab8078d174dad34bea3e82d7 (diff)
parent96cba9b00e297303774bec59e192064d20adeb3d (diff)
Merge tag 'chrome-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/olof/chrome-platform
Pull chrome platform updates from Olof Johansson: "Here's a set of updates to the Chrome OS platform drivers for this merge window. Main new things this cycle is: - Driver changes to expose the lightbar to users. With this, you can make your own blinkenlights on Chromebook Pixels. - Changes in the way that the atmel_mxt trackpads are probed. The laptop driver is trying to be smart and not instantiate the devices that don't answer to probe. For the trackpad that can come up in two modes (bootloader or regular), this gets complicated since the driver already knows how to handle the two modes including the actual addresses used. So now the laptop driver needs to know more too, instantiating the regular address even if the bootloader one is the probe that passed. - mfd driver improvements by Javier Martines Canillas, and a few bugfixes from him, kbuild and myself" * tag 'chrome-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/olof/chrome-platform: platform/chrome: chromeos_laptop - instantiate Atmel at primary address platform/chrome: cros_ec_lpc - Depend on X86 || COMPILE_TEST platform/chrome: cros_ec_lpc - Include linux/io.h header file platform/chrome: fix platform_no_drv_owner.cocci warnings platform/chrome: cros_ec_lightbar - fix duplicate const warning platform/chrome: cros_ec_dev - fix Unknown escape '%' warning platform/chrome: Expose Chrome OS Lightbar to users platform/chrome: Create sysfs attributes for the ChromeOS EC mfd: cros_ec: Instantiate ChromeOS EC character device platform/chrome: Add Chrome OS EC userspace device interface platform/chrome: Add cros_ec_lpc driver for x86 devices mfd: cros_ec: Add char dev and virtual dev pointers mfd: cros_ec: Use fixed size arrays to transfer data with the EC
Diffstat (limited to 'drivers/platform/chrome/cros_ec_sysfs.c')
-rw-r--r--drivers/platform/chrome/cros_ec_sysfs.c271
1 files changed, 271 insertions, 0 deletions
diff --git a/drivers/platform/chrome/cros_ec_sysfs.c b/drivers/platform/chrome/cros_ec_sysfs.c
new file mode 100644
index 000000000000..fb62ab6cc659
--- /dev/null
+++ b/drivers/platform/chrome/cros_ec_sysfs.c
@@ -0,0 +1,271 @@
1/*
2 * cros_ec_sysfs - expose the Chrome OS EC through sysfs
3 *
4 * Copyright (C) 2014 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#define pr_fmt(fmt) "cros_ec_sysfs: " fmt
21
22#include <linux/ctype.h>
23#include <linux/delay.h>
24#include <linux/device.h>
25#include <linux/fs.h>
26#include <linux/kobject.h>
27#include <linux/mfd/cros_ec.h>
28#include <linux/mfd/cros_ec_commands.h>
29#include <linux/module.h>
30#include <linux/platform_device.h>
31#include <linux/printk.h>
32#include <linux/stat.h>
33#include <linux/types.h>
34#include <linux/uaccess.h>
35
36#include "cros_ec_dev.h"
37
38/* Accessor functions */
39
40static ssize_t show_ec_reboot(struct device *dev,
41 struct device_attribute *attr, char *buf)
42{
43 int count = 0;
44
45 count += scnprintf(buf + count, PAGE_SIZE - count,
46 "ro|rw|cancel|cold|disable-jump|hibernate");
47 count += scnprintf(buf + count, PAGE_SIZE - count,
48 " [at-shutdown]\n");
49 return count;
50}
51
52static ssize_t store_ec_reboot(struct device *dev,
53 struct device_attribute *attr,
54 const char *buf, size_t count)
55{
56 static const struct {
57 const char * const str;
58 uint8_t cmd;
59 uint8_t flags;
60 } words[] = {
61 {"cancel", EC_REBOOT_CANCEL, 0},
62 {"ro", EC_REBOOT_JUMP_RO, 0},
63 {"rw", EC_REBOOT_JUMP_RW, 0},
64 {"cold", EC_REBOOT_COLD, 0},
65 {"disable-jump", EC_REBOOT_DISABLE_JUMP, 0},
66 {"hibernate", EC_REBOOT_HIBERNATE, 0},
67 {"at-shutdown", -1, EC_REBOOT_FLAG_ON_AP_SHUTDOWN},
68 };
69 struct cros_ec_command msg = { 0 };
70 struct ec_params_reboot_ec *param =
71 (struct ec_params_reboot_ec *)msg.outdata;
72 int got_cmd = 0, offset = 0;
73 int i;
74 int ret;
75 struct cros_ec_device *ec = dev_get_drvdata(dev);
76
77 param->flags = 0;
78 while (1) {
79 /* Find word to start scanning */
80 while (buf[offset] && isspace(buf[offset]))
81 offset++;
82 if (!buf[offset])
83 break;
84
85 for (i = 0; i < ARRAY_SIZE(words); i++) {
86 if (!strncasecmp(words[i].str, buf+offset,
87 strlen(words[i].str))) {
88 if (words[i].flags) {
89 param->flags |= words[i].flags;
90 } else {
91 param->cmd = words[i].cmd;
92 got_cmd = 1;
93 }
94 break;
95 }
96 }
97
98 /* On to the next word, if any */
99 while (buf[offset] && !isspace(buf[offset]))
100 offset++;
101 }
102
103 if (!got_cmd)
104 return -EINVAL;
105
106 msg.command = EC_CMD_REBOOT_EC;
107 msg.outsize = sizeof(param);
108 ret = cros_ec_cmd_xfer(ec, &msg);
109 if (ret < 0)
110 return ret;
111 if (msg.result != EC_RES_SUCCESS) {
112 dev_dbg(ec->dev, "EC result %d\n", msg.result);
113 return -EINVAL;
114 }
115
116 return count;
117}
118
119static ssize_t show_ec_version(struct device *dev,
120 struct device_attribute *attr, char *buf)
121{
122 static const char * const image_names[] = {"unknown", "RO", "RW"};
123 struct ec_response_get_version *r_ver;
124 struct ec_response_get_chip_info *r_chip;
125 struct ec_response_board_version *r_board;
126 struct cros_ec_command msg = { 0 };
127 int ret;
128 int count = 0;
129 struct cros_ec_device *ec = dev_get_drvdata(dev);
130
131 /* Get versions. RW may change. */
132 msg.command = EC_CMD_GET_VERSION;
133 msg.insize = sizeof(*r_ver);
134 ret = cros_ec_cmd_xfer(ec, &msg);
135 if (ret < 0)
136 return ret;
137 if (msg.result != EC_RES_SUCCESS)
138 return scnprintf(buf, PAGE_SIZE,
139 "ERROR: EC returned %d\n", msg.result);
140
141 r_ver = (struct ec_response_get_version *)msg.indata;
142 /* Strings should be null-terminated, but let's be sure. */
143 r_ver->version_string_ro[sizeof(r_ver->version_string_ro) - 1] = '\0';
144 r_ver->version_string_rw[sizeof(r_ver->version_string_rw) - 1] = '\0';
145 count += scnprintf(buf + count, PAGE_SIZE - count,
146 "RO version: %s\n", r_ver->version_string_ro);
147 count += scnprintf(buf + count, PAGE_SIZE - count,
148 "RW version: %s\n", r_ver->version_string_rw);
149 count += scnprintf(buf + count, PAGE_SIZE - count,
150 "Firmware copy: %s\n",
151 (r_ver->current_image < ARRAY_SIZE(image_names) ?
152 image_names[r_ver->current_image] : "?"));
153
154 /* Get build info. */
155 msg.command = EC_CMD_GET_BUILD_INFO;
156 msg.insize = sizeof(msg.indata);
157 ret = cros_ec_cmd_xfer(ec, &msg);
158 if (ret < 0)
159 count += scnprintf(buf + count, PAGE_SIZE - count,
160 "Build info: XFER ERROR %d\n", ret);
161 else if (msg.result != EC_RES_SUCCESS)
162 count += scnprintf(buf + count, PAGE_SIZE - count,
163 "Build info: EC error %d\n", msg.result);
164 else {
165 msg.indata[sizeof(msg.indata) - 1] = '\0';
166 count += scnprintf(buf + count, PAGE_SIZE - count,
167 "Build info: %s\n", msg.indata);
168 }
169
170 /* Get chip info. */
171 msg.command = EC_CMD_GET_CHIP_INFO;
172 msg.insize = sizeof(*r_chip);
173 ret = cros_ec_cmd_xfer(ec, &msg);
174 if (ret < 0)
175 count += scnprintf(buf + count, PAGE_SIZE - count,
176 "Chip info: XFER ERROR %d\n", ret);
177 else if (msg.result != EC_RES_SUCCESS)
178 count += scnprintf(buf + count, PAGE_SIZE - count,
179 "Chip info: EC error %d\n", msg.result);
180 else {
181 r_chip = (struct ec_response_get_chip_info *)msg.indata;
182
183 r_chip->vendor[sizeof(r_chip->vendor) - 1] = '\0';
184 r_chip->name[sizeof(r_chip->name) - 1] = '\0';
185 r_chip->revision[sizeof(r_chip->revision) - 1] = '\0';
186 count += scnprintf(buf + count, PAGE_SIZE - count,
187 "Chip vendor: %s\n", r_chip->vendor);
188 count += scnprintf(buf + count, PAGE_SIZE - count,
189 "Chip name: %s\n", r_chip->name);
190 count += scnprintf(buf + count, PAGE_SIZE - count,
191 "Chip revision: %s\n", r_chip->revision);
192 }
193
194 /* Get board version */
195 msg.command = EC_CMD_GET_BOARD_VERSION;
196 msg.insize = sizeof(*r_board);
197 ret = cros_ec_cmd_xfer(ec, &msg);
198 if (ret < 0)
199 count += scnprintf(buf + count, PAGE_SIZE - count,
200 "Board version: XFER ERROR %d\n", ret);
201 else if (msg.result != EC_RES_SUCCESS)
202 count += scnprintf(buf + count, PAGE_SIZE - count,
203 "Board version: EC error %d\n", msg.result);
204 else {
205 r_board = (struct ec_response_board_version *)msg.indata;
206
207 count += scnprintf(buf + count, PAGE_SIZE - count,
208 "Board version: %d\n",
209 r_board->board_version);
210 }
211
212 return count;
213}
214
215static ssize_t show_ec_flashinfo(struct device *dev,
216 struct device_attribute *attr, char *buf)
217{
218 struct ec_response_flash_info *resp;
219 struct cros_ec_command msg = { 0 };
220 int ret;
221 struct cros_ec_device *ec = dev_get_drvdata(dev);
222
223 /* The flash info shouldn't ever change, but ask each time anyway. */
224 msg.command = EC_CMD_FLASH_INFO;
225 msg.insize = sizeof(*resp);
226 ret = cros_ec_cmd_xfer(ec, &msg);
227 if (ret < 0)
228 return ret;
229 if (msg.result != EC_RES_SUCCESS)
230 return scnprintf(buf, PAGE_SIZE,
231 "ERROR: EC returned %d\n", msg.result);
232
233 resp = (struct ec_response_flash_info *)msg.indata;
234
235 return scnprintf(buf, PAGE_SIZE,
236 "FlashSize %d\nWriteSize %d\n"
237 "EraseSize %d\nProtectSize %d\n",
238 resp->flash_size, resp->write_block_size,
239 resp->erase_block_size, resp->protect_block_size);
240}
241
242/* Module initialization */
243
244static DEVICE_ATTR(reboot, S_IWUSR | S_IRUGO, show_ec_reboot, store_ec_reboot);
245static DEVICE_ATTR(version, S_IRUGO, show_ec_version, NULL);
246static DEVICE_ATTR(flashinfo, S_IRUGO, show_ec_flashinfo, NULL);
247
248static struct attribute *__ec_attrs[] = {
249 &dev_attr_reboot.attr,
250 &dev_attr_version.attr,
251 &dev_attr_flashinfo.attr,
252 NULL,
253};
254
255static struct attribute_group ec_attr_group = {
256 .attrs = __ec_attrs,
257};
258
259void ec_dev_sysfs_init(struct cros_ec_device *ec)
260{
261 int error;
262
263 error = sysfs_create_group(&ec->vdev->kobj, &ec_attr_group);
264 if (error)
265 pr_warn("failed to create group: %d\n", error);
266}
267
268void ec_dev_sysfs_remove(struct cros_ec_device *ec)
269{
270 sysfs_remove_group(&ec->vdev->kobj, &ec_attr_group);
271}