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_USB_CHARGE 0x39
27 
28 enum usb_charge_op {
29 	USB_CHARGE_GET = 0,
30 	USB_CHARGE_SET = 1,
31 };
32 
33 struct usb_charge_request {
34 	u8 cmd;		/* Always CMD_USB_CHARGE */
35 	u8 reserved;
36 	u8 op;		/* One of enum usb_charge_op */
37 	u8 val;		/* When setting, either 0 or 1 */
38 } __packed;
39 
40 struct usb_charge_response {
41 	u8 reserved;
42 	u8 status;	/* Set by EC to 0 on success, other value on failure */
43 	u8 val;		/* When getting, set by EC to either 0 or 1 */
44 } __packed;
45 
46 #define CMD_EC_INFO			0x38
47 enum get_ec_info_op {
48 	CMD_GET_EC_LABEL	= 0,
49 	CMD_GET_EC_REV		= 1,
50 	CMD_GET_EC_MODEL	= 2,
51 	CMD_GET_EC_BUILD_DATE	= 3,
52 };
53 
54 struct get_ec_info_req {
55 	u8 cmd;			/* Always CMD_EC_INFO */
56 	u8 reserved;
57 	u8 op;			/* One of enum get_ec_info_op */
58 } __packed;
59 
60 struct get_ec_info_resp {
61 	u8 reserved[2];
62 	char value[9]; /* __nonstring: might not be null terminated */
63 } __packed;
64 
65 static ssize_t boot_on_ac_store(struct device *dev,
66 				struct device_attribute *attr,
67 				const char *buf, size_t count)
68 {
69 	struct wilco_ec_device *ec = dev_get_drvdata(dev);
70 	struct boot_on_ac_request rq;
71 	struct wilco_ec_message msg;
72 	int ret;
73 	u8 val;
74 
75 	ret = kstrtou8(buf, 10, &val);
76 	if (ret < 0)
77 		return ret;
78 	if (val > 1)
79 		return -EINVAL;
80 
81 	memset(&rq, 0, sizeof(rq));
82 	rq.cmd = CMD_KB_CMOS;
83 	rq.sub_cmd = SUB_CMD_KB_CMOS_AUTO_ON;
84 	rq.val = val;
85 
86 	memset(&msg, 0, sizeof(msg));
87 	msg.type = WILCO_EC_MSG_LEGACY;
88 	msg.request_data = &rq;
89 	msg.request_size = sizeof(rq);
90 	ret = wilco_ec_mailbox(ec, &msg);
91 	if (ret < 0)
92 		return ret;
93 
94 	return count;
95 }
96 
97 static DEVICE_ATTR_WO(boot_on_ac);
98 
99 static ssize_t get_info(struct device *dev, char *buf, enum get_ec_info_op op)
100 {
101 	struct wilco_ec_device *ec = dev_get_drvdata(dev);
102 	struct get_ec_info_req req = { .cmd = CMD_EC_INFO, .op = op };
103 	struct get_ec_info_resp resp;
104 	int ret;
105 
106 	struct wilco_ec_message msg = {
107 		.type = WILCO_EC_MSG_LEGACY,
108 		.request_data = &req,
109 		.request_size = sizeof(req),
110 		.response_data = &resp,
111 		.response_size = sizeof(resp),
112 	};
113 
114 	ret = wilco_ec_mailbox(ec, &msg);
115 	if (ret < 0)
116 		return ret;
117 
118 	return scnprintf(buf, PAGE_SIZE, "%.*s\n", (int)sizeof(resp.value),
119 			 (char *)&resp.value);
120 }
121 
122 static ssize_t version_show(struct device *dev, struct device_attribute *attr,
123 			  char *buf)
124 {
125 	return get_info(dev, buf, CMD_GET_EC_LABEL);
126 }
127 
128 static DEVICE_ATTR_RO(version);
129 
130 static ssize_t build_revision_show(struct device *dev,
131 				   struct device_attribute *attr, char *buf)
132 {
133 	return get_info(dev, buf, CMD_GET_EC_REV);
134 }
135 
136 static DEVICE_ATTR_RO(build_revision);
137 
138 static ssize_t build_date_show(struct device *dev,
139 			       struct device_attribute *attr, char *buf)
140 {
141 	return get_info(dev, buf, CMD_GET_EC_BUILD_DATE);
142 }
143 
144 static DEVICE_ATTR_RO(build_date);
145 
146 static ssize_t model_number_show(struct device *dev,
147 				 struct device_attribute *attr, char *buf)
148 {
149 	return get_info(dev, buf, CMD_GET_EC_MODEL);
150 }
151 
152 static DEVICE_ATTR_RO(model_number);
153 
154 static int send_usb_charge(struct wilco_ec_device *ec,
155 				struct usb_charge_request *rq,
156 				struct usb_charge_response *rs)
157 {
158 	struct wilco_ec_message msg;
159 	int ret;
160 
161 	memset(&msg, 0, sizeof(msg));
162 	msg.type = WILCO_EC_MSG_LEGACY;
163 	msg.request_data = rq;
164 	msg.request_size = sizeof(*rq);
165 	msg.response_data = rs;
166 	msg.response_size = sizeof(*rs);
167 	ret = wilco_ec_mailbox(ec, &msg);
168 	if (ret < 0)
169 		return ret;
170 	if (rs->status)
171 		return -EIO;
172 
173 	return 0;
174 }
175 
176 static ssize_t usb_charge_show(struct device *dev,
177 				    struct device_attribute *attr, char *buf)
178 {
179 	struct wilco_ec_device *ec = dev_get_drvdata(dev);
180 	struct usb_charge_request rq;
181 	struct usb_charge_response rs;
182 	int ret;
183 
184 	memset(&rq, 0, sizeof(rq));
185 	rq.cmd = CMD_USB_CHARGE;
186 	rq.op = USB_CHARGE_GET;
187 
188 	ret = send_usb_charge(ec, &rq, &rs);
189 	if (ret < 0)
190 		return ret;
191 
192 	return sprintf(buf, "%d\n", rs.val);
193 }
194 
195 static ssize_t usb_charge_store(struct device *dev,
196 				     struct device_attribute *attr,
197 				     const char *buf, size_t count)
198 {
199 	struct wilco_ec_device *ec = dev_get_drvdata(dev);
200 	struct usb_charge_request rq;
201 	struct usb_charge_response rs;
202 	int ret;
203 	u8 val;
204 
205 	ret = kstrtou8(buf, 10, &val);
206 	if (ret < 0)
207 		return ret;
208 	if (val > 1)
209 		return -EINVAL;
210 
211 	memset(&rq, 0, sizeof(rq));
212 	rq.cmd = CMD_USB_CHARGE;
213 	rq.op = USB_CHARGE_SET;
214 	rq.val = val;
215 
216 	ret = send_usb_charge(ec, &rq, &rs);
217 	if (ret < 0)
218 		return ret;
219 
220 	return count;
221 }
222 
223 static DEVICE_ATTR_RW(usb_charge);
224 
225 static struct attribute *wilco_dev_attrs[] = {
226 	&dev_attr_boot_on_ac.attr,
227 	&dev_attr_build_date.attr,
228 	&dev_attr_build_revision.attr,
229 	&dev_attr_model_number.attr,
230 	&dev_attr_usb_charge.attr,
231 	&dev_attr_version.attr,
232 	NULL,
233 };
234 
235 static struct attribute_group wilco_dev_attr_group = {
236 	.attrs = wilco_dev_attrs,
237 };
238 
239 int wilco_ec_add_sysfs(struct wilco_ec_device *ec)
240 {
241 	return sysfs_create_group(&ec->dev->kobj, &wilco_dev_attr_group);
242 }
243 
244 void wilco_ec_remove_sysfs(struct wilco_ec_device *ec)
245 {
246 	sysfs_remove_group(&ec->dev->kobj, &wilco_dev_attr_group);
247 }
248