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