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