18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2019 Google LLC 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Sysfs properties to view and modify EC-controlled features on Wilco devices. 68c2ecf20Sopenharmony_ci * The entries will appear under /sys/bus/platform/devices/GOOG000C:00/ 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * See Documentation/ABI/testing/sysfs-platform-wilco-ec for more information. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/device.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_data/wilco-ec.h> 148c2ecf20Sopenharmony_ci#include <linux/string.h> 158c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 168c2ecf20Sopenharmony_ci#include <linux/types.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define CMD_KB_CMOS 0x7C 198c2ecf20Sopenharmony_ci#define SUB_CMD_KB_CMOS_AUTO_ON 0x03 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistruct boot_on_ac_request { 228c2ecf20Sopenharmony_ci u8 cmd; /* Always CMD_KB_CMOS */ 238c2ecf20Sopenharmony_ci u8 reserved1; 248c2ecf20Sopenharmony_ci u8 sub_cmd; /* Always SUB_CMD_KB_CMOS_AUTO_ON */ 258c2ecf20Sopenharmony_ci u8 reserved3to5[3]; 268c2ecf20Sopenharmony_ci u8 val; /* Either 0 or 1 */ 278c2ecf20Sopenharmony_ci u8 reserved7; 288c2ecf20Sopenharmony_ci} __packed; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define CMD_USB_CHARGE 0x39 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cienum usb_charge_op { 338c2ecf20Sopenharmony_ci USB_CHARGE_GET = 0, 348c2ecf20Sopenharmony_ci USB_CHARGE_SET = 1, 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistruct usb_charge_request { 388c2ecf20Sopenharmony_ci u8 cmd; /* Always CMD_USB_CHARGE */ 398c2ecf20Sopenharmony_ci u8 reserved; 408c2ecf20Sopenharmony_ci u8 op; /* One of enum usb_charge_op */ 418c2ecf20Sopenharmony_ci u8 val; /* When setting, either 0 or 1 */ 428c2ecf20Sopenharmony_ci} __packed; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistruct usb_charge_response { 458c2ecf20Sopenharmony_ci u8 reserved; 468c2ecf20Sopenharmony_ci u8 status; /* Set by EC to 0 on success, other value on failure */ 478c2ecf20Sopenharmony_ci u8 val; /* When getting, set by EC to either 0 or 1 */ 488c2ecf20Sopenharmony_ci} __packed; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define CMD_EC_INFO 0x38 518c2ecf20Sopenharmony_cienum get_ec_info_op { 528c2ecf20Sopenharmony_ci CMD_GET_EC_LABEL = 0, 538c2ecf20Sopenharmony_ci CMD_GET_EC_REV = 1, 548c2ecf20Sopenharmony_ci CMD_GET_EC_MODEL = 2, 558c2ecf20Sopenharmony_ci CMD_GET_EC_BUILD_DATE = 3, 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistruct get_ec_info_req { 598c2ecf20Sopenharmony_ci u8 cmd; /* Always CMD_EC_INFO */ 608c2ecf20Sopenharmony_ci u8 reserved; 618c2ecf20Sopenharmony_ci u8 op; /* One of enum get_ec_info_op */ 628c2ecf20Sopenharmony_ci} __packed; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistruct get_ec_info_resp { 658c2ecf20Sopenharmony_ci u8 reserved[2]; 668c2ecf20Sopenharmony_ci char value[9]; /* __nonstring: might not be null terminated */ 678c2ecf20Sopenharmony_ci} __packed; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic ssize_t boot_on_ac_store(struct device *dev, 708c2ecf20Sopenharmony_ci struct device_attribute *attr, 718c2ecf20Sopenharmony_ci const char *buf, size_t count) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct wilco_ec_device *ec = dev_get_drvdata(dev); 748c2ecf20Sopenharmony_ci struct boot_on_ac_request rq; 758c2ecf20Sopenharmony_ci struct wilco_ec_message msg; 768c2ecf20Sopenharmony_ci int ret; 778c2ecf20Sopenharmony_ci u8 val; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci ret = kstrtou8(buf, 10, &val); 808c2ecf20Sopenharmony_ci if (ret < 0) 818c2ecf20Sopenharmony_ci return ret; 828c2ecf20Sopenharmony_ci if (val > 1) 838c2ecf20Sopenharmony_ci return -EINVAL; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci memset(&rq, 0, sizeof(rq)); 868c2ecf20Sopenharmony_ci rq.cmd = CMD_KB_CMOS; 878c2ecf20Sopenharmony_ci rq.sub_cmd = SUB_CMD_KB_CMOS_AUTO_ON; 888c2ecf20Sopenharmony_ci rq.val = val; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 918c2ecf20Sopenharmony_ci msg.type = WILCO_EC_MSG_LEGACY; 928c2ecf20Sopenharmony_ci msg.request_data = &rq; 938c2ecf20Sopenharmony_ci msg.request_size = sizeof(rq); 948c2ecf20Sopenharmony_ci ret = wilco_ec_mailbox(ec, &msg); 958c2ecf20Sopenharmony_ci if (ret < 0) 968c2ecf20Sopenharmony_ci return ret; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci return count; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(boot_on_ac); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic ssize_t get_info(struct device *dev, char *buf, enum get_ec_info_op op) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct wilco_ec_device *ec = dev_get_drvdata(dev); 1068c2ecf20Sopenharmony_ci struct get_ec_info_req req = { .cmd = CMD_EC_INFO, .op = op }; 1078c2ecf20Sopenharmony_ci struct get_ec_info_resp resp; 1088c2ecf20Sopenharmony_ci int ret; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci struct wilco_ec_message msg = { 1118c2ecf20Sopenharmony_ci .type = WILCO_EC_MSG_LEGACY, 1128c2ecf20Sopenharmony_ci .request_data = &req, 1138c2ecf20Sopenharmony_ci .request_size = sizeof(req), 1148c2ecf20Sopenharmony_ci .response_data = &resp, 1158c2ecf20Sopenharmony_ci .response_size = sizeof(resp), 1168c2ecf20Sopenharmony_ci }; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci ret = wilco_ec_mailbox(ec, &msg); 1198c2ecf20Sopenharmony_ci if (ret < 0) 1208c2ecf20Sopenharmony_ci return ret; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%.*s\n", (int)sizeof(resp.value), 1238c2ecf20Sopenharmony_ci (char *)&resp.value); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic ssize_t version_show(struct device *dev, struct device_attribute *attr, 1278c2ecf20Sopenharmony_ci char *buf) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci return get_info(dev, buf, CMD_GET_EC_LABEL); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(version); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic ssize_t build_revision_show(struct device *dev, 1358c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci return get_info(dev, buf, CMD_GET_EC_REV); 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(build_revision); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic ssize_t build_date_show(struct device *dev, 1438c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci return get_info(dev, buf, CMD_GET_EC_BUILD_DATE); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(build_date); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic ssize_t model_number_show(struct device *dev, 1518c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci return get_info(dev, buf, CMD_GET_EC_MODEL); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(model_number); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int send_usb_charge(struct wilco_ec_device *ec, 1598c2ecf20Sopenharmony_ci struct usb_charge_request *rq, 1608c2ecf20Sopenharmony_ci struct usb_charge_response *rs) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct wilco_ec_message msg; 1638c2ecf20Sopenharmony_ci int ret; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 1668c2ecf20Sopenharmony_ci msg.type = WILCO_EC_MSG_LEGACY; 1678c2ecf20Sopenharmony_ci msg.request_data = rq; 1688c2ecf20Sopenharmony_ci msg.request_size = sizeof(*rq); 1698c2ecf20Sopenharmony_ci msg.response_data = rs; 1708c2ecf20Sopenharmony_ci msg.response_size = sizeof(*rs); 1718c2ecf20Sopenharmony_ci ret = wilco_ec_mailbox(ec, &msg); 1728c2ecf20Sopenharmony_ci if (ret < 0) 1738c2ecf20Sopenharmony_ci return ret; 1748c2ecf20Sopenharmony_ci if (rs->status) 1758c2ecf20Sopenharmony_ci return -EIO; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci return 0; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic ssize_t usb_charge_show(struct device *dev, 1818c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct wilco_ec_device *ec = dev_get_drvdata(dev); 1848c2ecf20Sopenharmony_ci struct usb_charge_request rq; 1858c2ecf20Sopenharmony_ci struct usb_charge_response rs; 1868c2ecf20Sopenharmony_ci int ret; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci memset(&rq, 0, sizeof(rq)); 1898c2ecf20Sopenharmony_ci rq.cmd = CMD_USB_CHARGE; 1908c2ecf20Sopenharmony_ci rq.op = USB_CHARGE_GET; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci ret = send_usb_charge(ec, &rq, &rs); 1938c2ecf20Sopenharmony_ci if (ret < 0) 1948c2ecf20Sopenharmony_ci return ret; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", rs.val); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic ssize_t usb_charge_store(struct device *dev, 2008c2ecf20Sopenharmony_ci struct device_attribute *attr, 2018c2ecf20Sopenharmony_ci const char *buf, size_t count) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct wilco_ec_device *ec = dev_get_drvdata(dev); 2048c2ecf20Sopenharmony_ci struct usb_charge_request rq; 2058c2ecf20Sopenharmony_ci struct usb_charge_response rs; 2068c2ecf20Sopenharmony_ci int ret; 2078c2ecf20Sopenharmony_ci u8 val; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci ret = kstrtou8(buf, 10, &val); 2108c2ecf20Sopenharmony_ci if (ret < 0) 2118c2ecf20Sopenharmony_ci return ret; 2128c2ecf20Sopenharmony_ci if (val > 1) 2138c2ecf20Sopenharmony_ci return -EINVAL; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci memset(&rq, 0, sizeof(rq)); 2168c2ecf20Sopenharmony_ci rq.cmd = CMD_USB_CHARGE; 2178c2ecf20Sopenharmony_ci rq.op = USB_CHARGE_SET; 2188c2ecf20Sopenharmony_ci rq.val = val; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci ret = send_usb_charge(ec, &rq, &rs); 2218c2ecf20Sopenharmony_ci if (ret < 0) 2228c2ecf20Sopenharmony_ci return ret; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci return count; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(usb_charge); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic struct attribute *wilco_dev_attrs[] = { 2308c2ecf20Sopenharmony_ci &dev_attr_boot_on_ac.attr, 2318c2ecf20Sopenharmony_ci &dev_attr_build_date.attr, 2328c2ecf20Sopenharmony_ci &dev_attr_build_revision.attr, 2338c2ecf20Sopenharmony_ci &dev_attr_model_number.attr, 2348c2ecf20Sopenharmony_ci &dev_attr_usb_charge.attr, 2358c2ecf20Sopenharmony_ci &dev_attr_version.attr, 2368c2ecf20Sopenharmony_ci NULL, 2378c2ecf20Sopenharmony_ci}; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic struct attribute_group wilco_dev_attr_group = { 2408c2ecf20Sopenharmony_ci .attrs = wilco_dev_attrs, 2418c2ecf20Sopenharmony_ci}; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ciint wilco_ec_add_sysfs(struct wilco_ec_device *ec) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci return sysfs_create_group(&ec->dev->kobj, &wilco_dev_attr_group); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_civoid wilco_ec_remove_sysfs(struct wilco_ec_device *ec) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci sysfs_remove_group(&ec->dev->kobj, &wilco_dev_attr_group); 2518c2ecf20Sopenharmony_ci} 252