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