1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright 2019 Google LLC
4  *
5  * Sysfs properties to view and modify EC-controlled features on Wilco devices.
6  * The entries will appear under /sys/bus/platform/devices/GOOG000C:00/
7  *
8  * See Documentation/ABI/testing/sysfs-platform-wilco-ec for more information.
9  */
10 
11 #include <linux/platform_data/wilco-ec.h>
12 #include <linux/sysfs.h>
13 
14 #define CMD_KB_CMOS			0x7C
15 #define SUB_CMD_KB_CMOS_AUTO_ON		0x03
16 
17 struct boot_on_ac_request {
18 	u8 cmd;			/* Always CMD_KB_CMOS */
19 	u8 reserved1;
20 	u8 sub_cmd;		/* Always SUB_CMD_KB_CMOS_AUTO_ON */
21 	u8 reserved3to5[3];
22 	u8 val;			/* Either 0 or 1 */
23 	u8 reserved7;
24 } __packed;
25 
26 #define CMD_EC_INFO			0x38
27 enum get_ec_info_op {
28 	CMD_GET_EC_LABEL	= 0,
29 	CMD_GET_EC_REV		= 1,
30 	CMD_GET_EC_MODEL	= 2,
31 	CMD_GET_EC_BUILD_DATE	= 3,
32 };
33 
34 struct get_ec_info_req {
35 	u8 cmd;			/* Always CMD_EC_INFO */
36 	u8 reserved;
37 	u8 op;			/* One of enum get_ec_info_op */
38 } __packed;
39 
40 struct get_ec_info_resp {
41 	u8 reserved[2];
42 	char value[9]; /* __nonstring: might not be null terminated */
43 } __packed;
44 
45 static ssize_t boot_on_ac_store(struct device *dev,
46 				struct device_attribute *attr,
47 				const char *buf, size_t count)
48 {
49 	struct wilco_ec_device *ec = dev_get_drvdata(dev);
50 	struct boot_on_ac_request rq;
51 	struct wilco_ec_message msg;
52 	int ret;
53 	u8 val;
54 
55 	ret = kstrtou8(buf, 10, &val);
56 	if (ret < 0)
57 		return ret;
58 	if (val > 1)
59 		return -EINVAL;
60 
61 	memset(&rq, 0, sizeof(rq));
62 	rq.cmd = CMD_KB_CMOS;
63 	rq.sub_cmd = SUB_CMD_KB_CMOS_AUTO_ON;
64 	rq.val = val;
65 
66 	memset(&msg, 0, sizeof(msg));
67 	msg.type = WILCO_EC_MSG_LEGACY;
68 	msg.request_data = &rq;
69 	msg.request_size = sizeof(rq);
70 	ret = wilco_ec_mailbox(ec, &msg);
71 	if (ret < 0)
72 		return ret;
73 
74 	return count;
75 }
76 
77 static DEVICE_ATTR_WO(boot_on_ac);
78 
79 static ssize_t get_info(struct device *dev, char *buf, enum get_ec_info_op op)
80 {
81 	struct wilco_ec_device *ec = dev_get_drvdata(dev);
82 	struct get_ec_info_req req = { .cmd = CMD_EC_INFO, .op = op };
83 	struct get_ec_info_resp resp;
84 	int ret;
85 
86 	struct wilco_ec_message msg = {
87 		.type = WILCO_EC_MSG_LEGACY,
88 		.request_data = &req,
89 		.request_size = sizeof(req),
90 		.response_data = &resp,
91 		.response_size = sizeof(resp),
92 	};
93 
94 	ret = wilco_ec_mailbox(ec, &msg);
95 	if (ret < 0)
96 		return ret;
97 
98 	return scnprintf(buf, PAGE_SIZE, "%.*s\n", (int)sizeof(resp.value),
99 			 (char *)&resp.value);
100 }
101 
102 static ssize_t version_show(struct device *dev, struct device_attribute *attr,
103 			  char *buf)
104 {
105 	return get_info(dev, buf, CMD_GET_EC_LABEL);
106 }
107 
108 static DEVICE_ATTR_RO(version);
109 
110 static ssize_t build_revision_show(struct device *dev,
111 				   struct device_attribute *attr, char *buf)
112 {
113 	return get_info(dev, buf, CMD_GET_EC_REV);
114 }
115 
116 static DEVICE_ATTR_RO(build_revision);
117 
118 static ssize_t build_date_show(struct device *dev,
119 			       struct device_attribute *attr, char *buf)
120 {
121 	return get_info(dev, buf, CMD_GET_EC_BUILD_DATE);
122 }
123 
124 static DEVICE_ATTR_RO(build_date);
125 
126 static ssize_t model_number_show(struct device *dev,
127 				 struct device_attribute *attr, char *buf)
128 {
129 	return get_info(dev, buf, CMD_GET_EC_MODEL);
130 }
131 
132 static DEVICE_ATTR_RO(model_number);
133 
134 
135 static struct attribute *wilco_dev_attrs[] = {
136 	&dev_attr_boot_on_ac.attr,
137 	&dev_attr_build_date.attr,
138 	&dev_attr_build_revision.attr,
139 	&dev_attr_model_number.attr,
140 	&dev_attr_version.attr,
141 	NULL,
142 };
143 
144 static struct attribute_group wilco_dev_attr_group = {
145 	.attrs = wilco_dev_attrs,
146 };
147 
148 int wilco_ec_add_sysfs(struct wilco_ec_device *ec)
149 {
150 	return sysfs_create_group(&ec->dev->kobj, &wilco_dev_attr_group);
151 }
152 
153 void wilco_ec_remove_sysfs(struct wilco_ec_device *ec)
154 {
155 	sysfs_remove_group(&ec->dev->kobj, &wilco_dev_attr_group);
156 }
157