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/mod_devicetable.h>
12 #include <linux/module.h>
13 #include <linux/platform_data/cros_ec_commands.h>
14 #include <linux/platform_data/cros_ec_proto.h>
15 #include <linux/platform_device.h>
16 #include <linux/printk.h>
17 #include <linux/slab.h>
18 #include <linux/stat.h>
19 #include <linux/types.h>
20 #include <linux/uaccess.h>
22 #define DRV_NAME "cros-ec-sysfs"
24 /* Accessor functions */
26 static ssize_t
reboot_show(struct device
*dev
,
27 struct device_attribute
*attr
, char *buf
)
31 count
+= sysfs_emit_at(buf
, count
,
32 "ro|rw|cancel|cold|disable-jump|hibernate|cold-ap-off");
33 count
+= sysfs_emit_at(buf
, count
, " [at-shutdown]\n");
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-ap-off", EC_REBOOT_COLD_AP_OFF
, 0},
50 {"cold", EC_REBOOT_COLD
, 0},
51 {"disable-jump", EC_REBOOT_DISABLE_JUMP
, 0},
52 {"hibernate", EC_REBOOT_HIBERNATE
, 0},
53 {"at-shutdown", -1, EC_REBOOT_FLAG_ON_AP_SHUTDOWN
},
55 struct cros_ec_command
*msg
;
56 struct ec_params_reboot_ec
*param
;
57 int got_cmd
= 0, offset
= 0;
60 struct cros_ec_dev
*ec
= to_cros_ec_dev(dev
);
62 msg
= kmalloc(sizeof(*msg
) + sizeof(*param
), GFP_KERNEL
);
66 param
= (struct ec_params_reboot_ec
*)msg
->data
;
70 /* Find word to start scanning */
71 while (buf
[offset
] && isspace(buf
[offset
]))
76 for (i
= 0; i
< ARRAY_SIZE(words
); i
++) {
77 if (!strncasecmp(words
[i
].str
, buf
+offset
,
78 strlen(words
[i
].str
))) {
80 param
->flags
|= words
[i
].flags
;
82 param
->cmd
= words
[i
].cmd
;
89 /* On to the next word, if any */
90 while (buf
[offset
] && !isspace(buf
[offset
]))
100 msg
->command
= EC_CMD_REBOOT_EC
+ ec
->cmd_offset
;
101 msg
->outsize
= sizeof(*param
);
103 ret
= cros_ec_cmd_xfer_status(ec
->ec_dev
, msg
);
111 static ssize_t
version_show(struct device
*dev
,
112 struct device_attribute
*attr
, char *buf
)
114 static const char * const image_names
[] = {"unknown", "RO", "RW"};
115 struct ec_response_get_version
*r_ver
;
116 struct ec_response_get_chip_info
*r_chip
;
117 struct ec_response_board_version
*r_board
;
118 struct cros_ec_command
*msg
;
121 struct cros_ec_dev
*ec
= to_cros_ec_dev(dev
);
123 msg
= kmalloc(sizeof(*msg
) + EC_HOST_PARAM_SIZE
, GFP_KERNEL
);
127 /* Get versions. RW may change. */
129 msg
->command
= EC_CMD_GET_VERSION
+ ec
->cmd_offset
;
130 msg
->insize
= sizeof(*r_ver
);
132 ret
= cros_ec_cmd_xfer_status(ec
->ec_dev
, msg
);
137 r_ver
= (struct ec_response_get_version
*)msg
->data
;
138 /* Strings should be null-terminated, but let's be sure. */
139 r_ver
->version_string_ro
[sizeof(r_ver
->version_string_ro
) - 1] = '\0';
140 r_ver
->version_string_rw
[sizeof(r_ver
->version_string_rw
) - 1] = '\0';
141 count
+= sysfs_emit_at(buf
, count
, "RO version: %s\n", r_ver
->version_string_ro
);
142 count
+= sysfs_emit_at(buf
, count
, "RW version: %s\n", r_ver
->version_string_rw
);
143 count
+= sysfs_emit_at(buf
, count
, "Firmware copy: %s\n",
144 (r_ver
->current_image
< ARRAY_SIZE(image_names
) ?
145 image_names
[r_ver
->current_image
] : "?"));
147 /* Get build info. */
148 msg
->command
= EC_CMD_GET_BUILD_INFO
+ ec
->cmd_offset
;
149 msg
->insize
= EC_HOST_PARAM_SIZE
;
150 ret
= cros_ec_cmd_xfer_status(ec
->ec_dev
, msg
);
152 count
+= sysfs_emit_at(buf
, count
,
153 "Build info: XFER / EC ERROR %d / %d\n",
156 msg
->data
[EC_HOST_PARAM_SIZE
- 1] = '\0';
157 count
+= sysfs_emit_at(buf
, count
, "Build info: %s\n", msg
->data
);
161 msg
->command
= EC_CMD_GET_CHIP_INFO
+ ec
->cmd_offset
;
162 msg
->insize
= sizeof(*r_chip
);
163 ret
= cros_ec_cmd_xfer_status(ec
->ec_dev
, msg
);
165 count
+= sysfs_emit_at(buf
, count
,
166 "Chip info: XFER / EC ERROR %d / %d\n",
169 r_chip
= (struct ec_response_get_chip_info
*)msg
->data
;
171 r_chip
->vendor
[sizeof(r_chip
->vendor
) - 1] = '\0';
172 r_chip
->name
[sizeof(r_chip
->name
) - 1] = '\0';
173 r_chip
->revision
[sizeof(r_chip
->revision
) - 1] = '\0';
174 count
+= sysfs_emit_at(buf
, count
, "Chip vendor: %s\n", r_chip
->vendor
);
175 count
+= sysfs_emit_at(buf
, count
, "Chip name: %s\n", r_chip
->name
);
176 count
+= sysfs_emit_at(buf
, count
, "Chip revision: %s\n", r_chip
->revision
);
179 /* Get board version */
180 msg
->command
= EC_CMD_GET_BOARD_VERSION
+ ec
->cmd_offset
;
181 msg
->insize
= sizeof(*r_board
);
182 ret
= cros_ec_cmd_xfer_status(ec
->ec_dev
, msg
);
184 count
+= sysfs_emit_at(buf
, count
,
185 "Board version: XFER / EC ERROR %d / %d\n",
188 r_board
= (struct ec_response_board_version
*)msg
->data
;
190 count
+= sysfs_emit_at(buf
, count
,
191 "Board version: %d\n",
192 r_board
->board_version
);
200 static ssize_t
flashinfo_show(struct device
*dev
,
201 struct device_attribute
*attr
, char *buf
)
203 struct ec_response_flash_info
*resp
;
204 struct cros_ec_command
*msg
;
206 struct cros_ec_dev
*ec
= to_cros_ec_dev(dev
);
208 msg
= kmalloc(sizeof(*msg
) + sizeof(*resp
), GFP_KERNEL
);
212 /* The flash info shouldn't ever change, but ask each time anyway. */
214 msg
->command
= EC_CMD_FLASH_INFO
+ ec
->cmd_offset
;
215 msg
->insize
= sizeof(*resp
);
217 ret
= cros_ec_cmd_xfer_status(ec
->ec_dev
, msg
);
221 resp
= (struct ec_response_flash_info
*)msg
->data
;
223 ret
= sysfs_emit(buf
,
224 "FlashSize %d\nWriteSize %d\n"
225 "EraseSize %d\nProtectSize %d\n",
226 resp
->flash_size
, resp
->write_block_size
,
227 resp
->erase_block_size
, resp
->protect_block_size
);
233 /* Keyboard wake angle control */
234 static ssize_t
kb_wake_angle_show(struct device
*dev
,
235 struct device_attribute
*attr
, char *buf
)
237 struct cros_ec_dev
*ec
= to_cros_ec_dev(dev
);
238 struct ec_response_motion_sense
*resp
;
239 struct ec_params_motion_sense
*param
;
240 struct cros_ec_command
*msg
;
243 msg
= kmalloc(sizeof(*msg
) + EC_HOST_PARAM_SIZE
, GFP_KERNEL
);
247 param
= (struct ec_params_motion_sense
*)msg
->data
;
248 msg
->command
= EC_CMD_MOTION_SENSE_CMD
+ ec
->cmd_offset
;
250 param
->cmd
= MOTIONSENSE_CMD_KB_WAKE_ANGLE
;
251 param
->kb_wake_angle
.data
= EC_MOTION_SENSE_NO_VALUE
;
252 msg
->outsize
= sizeof(*param
);
253 msg
->insize
= sizeof(*resp
);
255 ret
= cros_ec_cmd_xfer_status(ec
->ec_dev
, msg
);
259 resp
= (struct ec_response_motion_sense
*)msg
->data
;
260 ret
= sysfs_emit(buf
, "%d\n", resp
->kb_wake_angle
.ret
);
266 static ssize_t
kb_wake_angle_store(struct device
*dev
,
267 struct device_attribute
*attr
,
268 const char *buf
, size_t count
)
270 struct cros_ec_dev
*ec
= to_cros_ec_dev(dev
);
271 struct ec_params_motion_sense
*param
;
272 struct cros_ec_command
*msg
;
276 ret
= kstrtou16(buf
, 0, &angle
);
280 msg
= kmalloc(sizeof(*msg
) + EC_HOST_PARAM_SIZE
, GFP_KERNEL
);
284 param
= (struct ec_params_motion_sense
*)msg
->data
;
285 msg
->command
= EC_CMD_MOTION_SENSE_CMD
+ ec
->cmd_offset
;
287 param
->cmd
= MOTIONSENSE_CMD_KB_WAKE_ANGLE
;
288 param
->kb_wake_angle
.data
= angle
;
289 msg
->outsize
= sizeof(*param
);
290 msg
->insize
= sizeof(struct ec_response_motion_sense
);
292 ret
= cros_ec_cmd_xfer_status(ec
->ec_dev
, msg
);
299 /* Module initialization */
301 static DEVICE_ATTR_RW(reboot
);
302 static DEVICE_ATTR_RO(version
);
303 static DEVICE_ATTR_RO(flashinfo
);
304 static DEVICE_ATTR_RW(kb_wake_angle
);
306 static struct attribute
*__ec_attrs
[] = {
307 &dev_attr_kb_wake_angle
.attr
,
308 &dev_attr_reboot
.attr
,
309 &dev_attr_version
.attr
,
310 &dev_attr_flashinfo
.attr
,
314 static umode_t
cros_ec_ctrl_visible(struct kobject
*kobj
,
315 struct attribute
*a
, int n
)
317 struct device
*dev
= kobj_to_dev(kobj
);
318 struct cros_ec_dev
*ec
= to_cros_ec_dev(dev
);
320 if (a
== &dev_attr_kb_wake_angle
.attr
&& !ec
->has_kb_wake_angle
)
326 static const struct attribute_group cros_ec_attr_group
= {
328 .is_visible
= cros_ec_ctrl_visible
,
331 static int cros_ec_sysfs_probe(struct platform_device
*pd
)
333 struct cros_ec_dev
*ec_dev
= dev_get_drvdata(pd
->dev
.parent
);
334 struct device
*dev
= &pd
->dev
;
337 ret
= sysfs_create_group(&ec_dev
->class_dev
.kobj
, &cros_ec_attr_group
);
339 dev_err(dev
, "failed to create attributes. err=%d\n", ret
);
344 static void cros_ec_sysfs_remove(struct platform_device
*pd
)
346 struct cros_ec_dev
*ec_dev
= dev_get_drvdata(pd
->dev
.parent
);
348 sysfs_remove_group(&ec_dev
->class_dev
.kobj
, &cros_ec_attr_group
);
351 static const struct platform_device_id cros_ec_sysfs_id
[] = {
355 MODULE_DEVICE_TABLE(platform
, cros_ec_sysfs_id
);
357 static struct platform_driver cros_ec_sysfs_driver
= {
361 .probe
= cros_ec_sysfs_probe
,
362 .remove_new
= cros_ec_sysfs_remove
,
363 .id_table
= cros_ec_sysfs_id
,
366 module_platform_driver(cros_ec_sysfs_driver
);
368 MODULE_LICENSE("GPL");
369 MODULE_DESCRIPTION("Expose the ChromeOS EC through sysfs");