0001
0002
0003
0004
0005
0006
0007 #include <linux/of.h>
0008 #include <linux/platform_device.h>
0009 #include <linux/module.h>
0010 #include <linux/platform_data/cros_ec_commands.h>
0011 #include <linux/platform_data/cros_ec_proto.h>
0012 #include <linux/slab.h>
0013
0014 #define DRV_NAME "cros-ec-vbc"
0015
0016 static ssize_t vboot_context_read(struct file *filp, struct kobject *kobj,
0017 struct bin_attribute *att, char *buf,
0018 loff_t pos, size_t count)
0019 {
0020 struct device *dev = kobj_to_dev(kobj);
0021 struct cros_ec_dev *ec = to_cros_ec_dev(dev);
0022 struct cros_ec_device *ecdev = ec->ec_dev;
0023 struct ec_params_vbnvcontext *params;
0024 struct cros_ec_command *msg;
0025 int err;
0026 const size_t para_sz = sizeof(params->op);
0027 const size_t resp_sz = sizeof(struct ec_response_vbnvcontext);
0028 const size_t payload = max(para_sz, resp_sz);
0029
0030 msg = kmalloc(sizeof(*msg) + payload, GFP_KERNEL);
0031 if (!msg)
0032 return -ENOMEM;
0033
0034
0035 params = (struct ec_params_vbnvcontext *)msg->data;
0036 params->op = EC_VBNV_CONTEXT_OP_READ;
0037
0038 msg->version = EC_VER_VBNV_CONTEXT;
0039 msg->command = EC_CMD_VBNV_CONTEXT;
0040 msg->outsize = para_sz;
0041 msg->insize = resp_sz;
0042
0043 err = cros_ec_cmd_xfer_status(ecdev, msg);
0044 if (err < 0) {
0045 dev_err(dev, "Error sending read request: %d\n", err);
0046 kfree(msg);
0047 return err;
0048 }
0049
0050 memcpy(buf, msg->data, resp_sz);
0051
0052 kfree(msg);
0053 return resp_sz;
0054 }
0055
0056 static ssize_t vboot_context_write(struct file *filp, struct kobject *kobj,
0057 struct bin_attribute *attr, char *buf,
0058 loff_t pos, size_t count)
0059 {
0060 struct device *dev = kobj_to_dev(kobj);
0061 struct cros_ec_dev *ec = to_cros_ec_dev(dev);
0062 struct cros_ec_device *ecdev = ec->ec_dev;
0063 struct ec_params_vbnvcontext *params;
0064 struct cros_ec_command *msg;
0065 int err;
0066 const size_t para_sz = sizeof(*params);
0067 const size_t data_sz = sizeof(params->block);
0068
0069
0070 if (count != data_sz)
0071 return -EINVAL;
0072
0073 msg = kmalloc(sizeof(*msg) + para_sz, GFP_KERNEL);
0074 if (!msg)
0075 return -ENOMEM;
0076
0077 params = (struct ec_params_vbnvcontext *)msg->data;
0078 params->op = EC_VBNV_CONTEXT_OP_WRITE;
0079 memcpy(params->block, buf, data_sz);
0080
0081 msg->version = EC_VER_VBNV_CONTEXT;
0082 msg->command = EC_CMD_VBNV_CONTEXT;
0083 msg->outsize = para_sz;
0084 msg->insize = 0;
0085
0086 err = cros_ec_cmd_xfer_status(ecdev, msg);
0087 if (err < 0) {
0088 dev_err(dev, "Error sending write request: %d\n", err);
0089 kfree(msg);
0090 return err;
0091 }
0092
0093 kfree(msg);
0094 return data_sz;
0095 }
0096
0097 static BIN_ATTR_RW(vboot_context, 16);
0098
0099 static struct bin_attribute *cros_ec_vbc_bin_attrs[] = {
0100 &bin_attr_vboot_context,
0101 NULL
0102 };
0103
0104 static const struct attribute_group cros_ec_vbc_attr_group = {
0105 .name = "vbc",
0106 .bin_attrs = cros_ec_vbc_bin_attrs,
0107 };
0108
0109 static int cros_ec_vbc_probe(struct platform_device *pd)
0110 {
0111 struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent);
0112 struct device *dev = &pd->dev;
0113 int ret;
0114
0115 ret = sysfs_create_group(&ec_dev->class_dev.kobj,
0116 &cros_ec_vbc_attr_group);
0117 if (ret < 0)
0118 dev_err(dev, "failed to create %s attributes. err=%d\n",
0119 cros_ec_vbc_attr_group.name, ret);
0120
0121 return ret;
0122 }
0123
0124 static int cros_ec_vbc_remove(struct platform_device *pd)
0125 {
0126 struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent);
0127
0128 sysfs_remove_group(&ec_dev->class_dev.kobj,
0129 &cros_ec_vbc_attr_group);
0130
0131 return 0;
0132 }
0133
0134 static struct platform_driver cros_ec_vbc_driver = {
0135 .driver = {
0136 .name = DRV_NAME,
0137 },
0138 .probe = cros_ec_vbc_probe,
0139 .remove = cros_ec_vbc_remove,
0140 };
0141
0142 module_platform_driver(cros_ec_vbc_driver);
0143
0144 MODULE_LICENSE("GPL");
0145 MODULE_DESCRIPTION("Expose the vboot context nvram to userspace");
0146 MODULE_ALIAS("platform:" DRV_NAME);