1 // SPDX-License-Identifier: GPL-2.0+
2 // Expose the ChromeOS EC through sysfs
4 // Copyright (C) 2014 Google, Inc.
6 #include <linux/ctype.h>
7 #include <linux/delay.h>
8 #include <linux/device.h>
10 #include <linux/kobject.h>
11 #include <linux/module.h>
12 #include <linux/platform_data/cros_ec_commands.h>
13 #include <linux/platform_data/cros_ec_proto.h>
14 #include <linux/platform_device.h>
15 #include <linux/printk.h>
16 #include <linux/slab.h>
17 #include <linux/stat.h>
18 #include <linux/types.h>
19 #include <linux/uaccess.h>
21 #define DRV_NAME "cros-ec-sysfs"
23 /* Accessor functions */
25 static ssize_t
reboot_show(struct device
*dev
,
26 struct device_attribute
*attr
, char *buf
)
30 count
+= scnprintf(buf
+ count
, PAGE_SIZE
- count
,
31 "ro|rw|cancel|cold|disable-jump|hibernate");
32 count
+= scnprintf(buf
+ count
, PAGE_SIZE
- count
,
37 static ssize_t
reboot_store(struct device
*dev
,
38 struct device_attribute
*attr
,
39 const char *buf
, size_t count
)
42 const char * const str
;
46 {"cancel", EC_REBOOT_CANCEL
, 0},
47 {"ro", EC_REBOOT_JUMP_RO
, 0},
48 {"rw", EC_REBOOT_JUMP_RW
, 0},
49 {"cold", EC_REBOOT_COLD
, 0},
50 {"disable-jump", EC_REBOOT_DISABLE_JUMP
, 0},
51 {"hibernate", EC_REBOOT_HIBERNATE
, 0},
52 {"at-shutdown", -1, EC_REBOOT_FLAG_ON_AP_SHUTDOWN
},
54 struct cros_ec_command
*msg
;
55 struct ec_params_reboot_ec
*param
;
56 int got_cmd
= 0, offset
= 0;
59 struct cros_ec_dev
*ec
= to_cros_ec_dev(dev
);
61 msg
= kmalloc(sizeof(*msg
) + sizeof(*param
), GFP_KERNEL
);
65 param
= (struct ec_params_reboot_ec
*)msg
->data
;
69 /* Find word to start scanning */
70 while (buf
[offset
] && isspace(buf
[offset
]))
75 for (i
= 0; i
< ARRAY_SIZE(words
); i
++) {
76 if (!strncasecmp(words
[i
].str
, buf
+offset
,
77 strlen(words
[i
].str
))) {
79 param
->flags
|= words
[i
].flags
;
81 param
->cmd
= words
[i
].cmd
;
88 /* On to the next word, if any */
89 while (buf
[offset
] && !isspace(buf
[offset
]))
99 msg
->command
= EC_CMD_REBOOT_EC
+ ec
->cmd_offset
;
100 msg
->outsize
= sizeof(*param
);
102 ret
= cros_ec_cmd_xfer_status(ec
->ec_dev
, msg
);
110 static ssize_t
version_show(struct device
*dev
,
111 struct device_attribute
*attr
, char *buf
)
113 static const char * const image_names
[] = {"unknown", "RO", "RW"};
114 struct ec_response_get_version
*r_ver
;
115 struct ec_response_get_chip_info
*r_chip
;
116 struct ec_response_board_version
*r_board
;
117 struct cros_ec_command
*msg
;
120 struct cros_ec_dev
*ec
= to_cros_ec_dev(dev
);
122 msg
= kmalloc(sizeof(*msg
) + EC_HOST_PARAM_SIZE
, GFP_KERNEL
);
126 /* Get versions. RW may change. */
128 msg
->command
= EC_CMD_GET_VERSION
+ ec
->cmd_offset
;
129 msg
->insize
= sizeof(*r_ver
);
131 ret
= cros_ec_cmd_xfer_status(ec
->ec_dev
, msg
);
136 r_ver
= (struct ec_response_get_version
*)msg
->data
;
137 /* Strings should be null-terminated, but let's be sure. */
138 r_ver
->version_string_ro
[sizeof(r_ver
->version_string_ro
) - 1] = '\0';
139 r_ver
->version_string_rw
[sizeof(r_ver
->version_string_rw
) - 1] = '\0';
140 count
+= scnprintf(buf
+ count
, PAGE_SIZE
- count
,
141 "RO version: %s\n", r_ver
->version_string_ro
);
142 count
+= scnprintf(buf
+ count
, PAGE_SIZE
- count
,
143 "RW version: %s\n", r_ver
->version_string_rw
);
144 count
+= scnprintf(buf
+ count
, PAGE_SIZE
- count
,
145 "Firmware copy: %s\n",
146 (r_ver
->current_image
< ARRAY_SIZE(image_names
) ?
147 image_names
[r_ver
->current_image
] : "?"));
149 /* Get build info. */
150 msg
->command
= EC_CMD_GET_BUILD_INFO
+ ec
->cmd_offset
;
151 msg
->insize
= EC_HOST_PARAM_SIZE
;
152 ret
= cros_ec_cmd_xfer_status(ec
->ec_dev
, msg
);
154 count
+= scnprintf(buf
+ count
, PAGE_SIZE
- count
,
155 "Build info: XFER / EC ERROR %d / %d\n",
158 msg
->data
[EC_HOST_PARAM_SIZE
- 1] = '\0';
159 count
+= scnprintf(buf
+ count
, PAGE_SIZE
- count
,
160 "Build info: %s\n", msg
->data
);
164 msg
->command
= EC_CMD_GET_CHIP_INFO
+ ec
->cmd_offset
;
165 msg
->insize
= sizeof(*r_chip
);
166 ret
= cros_ec_cmd_xfer_status(ec
->ec_dev
, msg
);
168 count
+= scnprintf(buf
+ count
, PAGE_SIZE
- count
,
169 "Chip info: XFER / EC ERROR %d / %d\n",
172 r_chip
= (struct ec_response_get_chip_info
*)msg
->data
;
174 r_chip
->vendor
[sizeof(r_chip
->vendor
) - 1] = '\0';
175 r_chip
->name
[sizeof(r_chip
->name
) - 1] = '\0';
176 r_chip
->revision
[sizeof(r_chip
->revision
) - 1] = '\0';
177 count
+= scnprintf(buf
+ count
, PAGE_SIZE
- count
,
178 "Chip vendor: %s\n", r_chip
->vendor
);
179 count
+= scnprintf(buf
+ count
, PAGE_SIZE
- count
,
180 "Chip name: %s\n", r_chip
->name
);
181 count
+= scnprintf(buf
+ count
, PAGE_SIZE
- count
,
182 "Chip revision: %s\n", r_chip
->revision
);
185 /* Get board version */
186 msg
->command
= EC_CMD_GET_BOARD_VERSION
+ ec
->cmd_offset
;
187 msg
->insize
= sizeof(*r_board
);
188 ret
= cros_ec_cmd_xfer_status(ec
->ec_dev
, msg
);
190 count
+= scnprintf(buf
+ count
, PAGE_SIZE
- count
,
191 "Board version: XFER / EC ERROR %d / %d\n",
194 r_board
= (struct ec_response_board_version
*)msg
->data
;
196 count
+= scnprintf(buf
+ count
, PAGE_SIZE
- count
,
197 "Board version: %d\n",
198 r_board
->board_version
);
206 static ssize_t
flashinfo_show(struct device
*dev
,
207 struct device_attribute
*attr
, char *buf
)
209 struct ec_response_flash_info
*resp
;
210 struct cros_ec_command
*msg
;
212 struct cros_ec_dev
*ec
= to_cros_ec_dev(dev
);
214 msg
= kmalloc(sizeof(*msg
) + sizeof(*resp
), GFP_KERNEL
);
218 /* The flash info shouldn't ever change, but ask each time anyway. */
220 msg
->command
= EC_CMD_FLASH_INFO
+ ec
->cmd_offset
;
221 msg
->insize
= sizeof(*resp
);
223 ret
= cros_ec_cmd_xfer_status(ec
->ec_dev
, msg
);
227 resp
= (struct ec_response_flash_info
*)msg
->data
;
229 ret
= scnprintf(buf
, PAGE_SIZE
,
230 "FlashSize %d\nWriteSize %d\n"
231 "EraseSize %d\nProtectSize %d\n",
232 resp
->flash_size
, resp
->write_block_size
,
233 resp
->erase_block_size
, resp
->protect_block_size
);
239 /* Keyboard wake angle control */
240 static ssize_t
kb_wake_angle_show(struct device
*dev
,
241 struct device_attribute
*attr
, char *buf
)
243 struct cros_ec_dev
*ec
= to_cros_ec_dev(dev
);
244 struct ec_response_motion_sense
*resp
;
245 struct ec_params_motion_sense
*param
;
246 struct cros_ec_command
*msg
;
249 msg
= kmalloc(sizeof(*msg
) + EC_HOST_PARAM_SIZE
, GFP_KERNEL
);
253 param
= (struct ec_params_motion_sense
*)msg
->data
;
254 msg
->command
= EC_CMD_MOTION_SENSE_CMD
+ ec
->cmd_offset
;
256 param
->cmd
= MOTIONSENSE_CMD_KB_WAKE_ANGLE
;
257 param
->kb_wake_angle
.data
= EC_MOTION_SENSE_NO_VALUE
;
258 msg
->outsize
= sizeof(*param
);
259 msg
->insize
= sizeof(*resp
);
261 ret
= cros_ec_cmd_xfer_status(ec
->ec_dev
, msg
);
265 resp
= (struct ec_response_motion_sense
*)msg
->data
;
266 ret
= scnprintf(buf
, PAGE_SIZE
, "%d\n", resp
->kb_wake_angle
.ret
);
272 static ssize_t
kb_wake_angle_store(struct device
*dev
,
273 struct device_attribute
*attr
,
274 const char *buf
, size_t count
)
276 struct cros_ec_dev
*ec
= to_cros_ec_dev(dev
);
277 struct ec_params_motion_sense
*param
;
278 struct cros_ec_command
*msg
;
282 ret
= kstrtou16(buf
, 0, &angle
);
286 msg
= kmalloc(sizeof(*msg
) + EC_HOST_PARAM_SIZE
, GFP_KERNEL
);
290 param
= (struct ec_params_motion_sense
*)msg
->data
;
291 msg
->command
= EC_CMD_MOTION_SENSE_CMD
+ ec
->cmd_offset
;
293 param
->cmd
= MOTIONSENSE_CMD_KB_WAKE_ANGLE
;
294 param
->kb_wake_angle
.data
= angle
;
295 msg
->outsize
= sizeof(*param
);
296 msg
->insize
= sizeof(struct ec_response_motion_sense
);
298 ret
= cros_ec_cmd_xfer_status(ec
->ec_dev
, msg
);
305 /* Module initialization */
307 static DEVICE_ATTR_RW(reboot
);
308 static DEVICE_ATTR_RO(version
);
309 static DEVICE_ATTR_RO(flashinfo
);
310 static DEVICE_ATTR_RW(kb_wake_angle
);
312 static struct attribute
*__ec_attrs
[] = {
313 &dev_attr_kb_wake_angle
.attr
,
314 &dev_attr_reboot
.attr
,
315 &dev_attr_version
.attr
,
316 &dev_attr_flashinfo
.attr
,
320 static umode_t
cros_ec_ctrl_visible(struct kobject
*kobj
,
321 struct attribute
*a
, int n
)
323 struct device
*dev
= kobj_to_dev(kobj
);
324 struct cros_ec_dev
*ec
= to_cros_ec_dev(dev
);
326 if (a
== &dev_attr_kb_wake_angle
.attr
&& !ec
->has_kb_wake_angle
)
332 static struct attribute_group cros_ec_attr_group
= {
334 .is_visible
= cros_ec_ctrl_visible
,
337 static int cros_ec_sysfs_probe(struct platform_device
*pd
)
339 struct cros_ec_dev
*ec_dev
= dev_get_drvdata(pd
->dev
.parent
);
340 struct device
*dev
= &pd
->dev
;
343 ret
= sysfs_create_group(&ec_dev
->class_dev
.kobj
, &cros_ec_attr_group
);
345 dev_err(dev
, "failed to create attributes. err=%d\n", ret
);
350 static int cros_ec_sysfs_remove(struct platform_device
*pd
)
352 struct cros_ec_dev
*ec_dev
= dev_get_drvdata(pd
->dev
.parent
);
354 sysfs_remove_group(&ec_dev
->class_dev
.kobj
, &cros_ec_attr_group
);
359 static struct platform_driver cros_ec_sysfs_driver
= {
363 .probe
= cros_ec_sysfs_probe
,
364 .remove
= cros_ec_sysfs_remove
,
367 module_platform_driver(cros_ec_sysfs_driver
);
369 MODULE_LICENSE("GPL");
370 MODULE_DESCRIPTION("Expose the ChromeOS EC through sysfs");
371 MODULE_ALIAS("platform:" DRV_NAME
);