linux/drivers/platform/chrome/wilco_ec/sysfs.c
<<
>>
Prefs
   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/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                     0x7C
  19#define SUB_CMD_KB_CMOS_AUTO_ON         0x03
  20
  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 0x39
  31
  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                     0x38
  51enum 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 scnprintf(buf, PAGE_SIZE, "%.*s\n", (int)sizeof(resp.value),
 123                         (char *)&resp.value);
 124}
 125
 126static ssize_t version_show(struct device *dev, struct device_attribute *attr,
 127                          char *buf)
 128{
 129        return get_info(dev, buf, CMD_GET_EC_LABEL);
 130}
 131
 132static DEVICE_ATTR_RO(version);
 133
 134static ssize_t build_revision_show(struct device *dev,
 135                                   struct device_attribute *attr, char *buf)
 136{
 137        return get_info(dev, buf, CMD_GET_EC_REV);
 138}
 139
 140static DEVICE_ATTR_RO(build_revision);
 141
 142static ssize_t build_date_show(struct device *dev,
 143                               struct device_attribute *attr, char *buf)
 144{
 145        return get_info(dev, buf, CMD_GET_EC_BUILD_DATE);
 146}
 147
 148static DEVICE_ATTR_RO(build_date);
 149
 150static ssize_t model_number_show(struct device *dev,
 151                                 struct device_attribute *attr, char *buf)
 152{
 153        return get_info(dev, buf, CMD_GET_EC_MODEL);
 154}
 155
 156static DEVICE_ATTR_RO(model_number);
 157
 158static int send_usb_charge(struct wilco_ec_device *ec,
 159                                struct usb_charge_request *rq,
 160                                struct usb_charge_response *rs)
 161{
 162        struct wilco_ec_message msg;
 163        int ret;
 164
 165        memset(&msg, 0, sizeof(msg));
 166        msg.type = WILCO_EC_MSG_LEGACY;
 167        msg.request_data = rq;
 168        msg.request_size = sizeof(*rq);
 169        msg.response_data = rs;
 170        msg.response_size = sizeof(*rs);
 171        ret = wilco_ec_mailbox(ec, &msg);
 172        if (ret < 0)
 173                return ret;
 174        if (rs->status)
 175                return -EIO;
 176
 177        return 0;
 178}
 179
 180static ssize_t usb_charge_show(struct device *dev,
 181                                    struct device_attribute *attr, char *buf)
 182{
 183        struct wilco_ec_device *ec = dev_get_drvdata(dev);
 184        struct usb_charge_request rq;
 185        struct usb_charge_response rs;
 186        int ret;
 187
 188        memset(&rq, 0, sizeof(rq));
 189        rq.cmd = CMD_USB_CHARGE;
 190        rq.op = USB_CHARGE_GET;
 191
 192        ret = send_usb_charge(ec, &rq, &rs);
 193        if (ret < 0)
 194                return ret;
 195
 196        return sprintf(buf, "%d\n", rs.val);
 197}
 198
 199static ssize_t usb_charge_store(struct device *dev,
 200                                     struct device_attribute *attr,
 201                                     const char *buf, size_t count)
 202{
 203        struct wilco_ec_device *ec = dev_get_drvdata(dev);
 204        struct usb_charge_request rq;
 205        struct usb_charge_response rs;
 206        int ret;
 207        u8 val;
 208
 209        ret = kstrtou8(buf, 10, &val);
 210        if (ret < 0)
 211                return ret;
 212        if (val > 1)
 213                return -EINVAL;
 214
 215        memset(&rq, 0, sizeof(rq));
 216        rq.cmd = CMD_USB_CHARGE;
 217        rq.op = USB_CHARGE_SET;
 218        rq.val = val;
 219
 220        ret = send_usb_charge(ec, &rq, &rs);
 221        if (ret < 0)
 222                return ret;
 223
 224        return count;
 225}
 226
 227static DEVICE_ATTR_RW(usb_charge);
 228
 229static struct attribute *wilco_dev_attrs[] = {
 230        &dev_attr_boot_on_ac.attr,
 231        &dev_attr_build_date.attr,
 232        &dev_attr_build_revision.attr,
 233        &dev_attr_model_number.attr,
 234        &dev_attr_usb_charge.attr,
 235        &dev_attr_version.attr,
 236        NULL,
 237};
 238
 239static const struct attribute_group wilco_dev_attr_group = {
 240        .attrs = wilco_dev_attrs,
 241};
 242
 243int wilco_ec_add_sysfs(struct wilco_ec_device *ec)
 244{
 245        return sysfs_create_group(&ec->dev->kobj, &wilco_dev_attr_group);
 246}
 247
 248void wilco_ec_remove_sysfs(struct wilco_ec_device *ec)
 249{
 250        sysfs_remove_group(&ec->dev->kobj, &wilco_dev_attr_group);
 251}
 252