118800fc7SEmilio López /* 218800fc7SEmilio López * cros_ec_vbc - Expose the vboot context nvram to userspace 318800fc7SEmilio López * 418800fc7SEmilio López * Copyright (C) 2015 Collabora Ltd. 518800fc7SEmilio López * 618800fc7SEmilio López * based on vendor driver, 718800fc7SEmilio López * 818800fc7SEmilio López * Copyright (C) 2012 The Chromium OS Authors 918800fc7SEmilio López * 1018800fc7SEmilio López * This program is free software; you can redistribute it and/or modify 1118800fc7SEmilio López * it under the terms of the GNU General Public License as published by 1218800fc7SEmilio López * the Free Software Foundation; either version 2 of the License, or 1318800fc7SEmilio López * (at your option) any later version. 1418800fc7SEmilio López * 1518800fc7SEmilio López * This program is distributed in the hope that it will be useful, 1618800fc7SEmilio López * but WITHOUT ANY WARRANTY; without even the implied warranty of 1718800fc7SEmilio López * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1818800fc7SEmilio López * GNU General Public License for more details. 1918800fc7SEmilio López */ 2018800fc7SEmilio López 2118800fc7SEmilio López #include <linux/of.h> 2218800fc7SEmilio López #include <linux/platform_device.h> 2318800fc7SEmilio López #include <linux/mfd/cros_ec.h> 2418800fc7SEmilio López #include <linux/mfd/cros_ec_commands.h> 2518800fc7SEmilio López #include <linux/slab.h> 2618800fc7SEmilio López 2718800fc7SEmilio López static ssize_t vboot_context_read(struct file *filp, struct kobject *kobj, 2818800fc7SEmilio López struct bin_attribute *att, char *buf, 2918800fc7SEmilio López loff_t pos, size_t count) 3018800fc7SEmilio López { 3118800fc7SEmilio López struct device *dev = container_of(kobj, struct device, kobj); 32*79a3d603SGwendal Grignou struct cros_ec_dev *ec = to_cros_ec_dev(dev); 3318800fc7SEmilio López struct cros_ec_device *ecdev = ec->ec_dev; 3418800fc7SEmilio López struct ec_params_vbnvcontext *params; 3518800fc7SEmilio López struct cros_ec_command *msg; 3618800fc7SEmilio López int err; 3718800fc7SEmilio López const size_t para_sz = sizeof(params->op); 3818800fc7SEmilio López const size_t resp_sz = sizeof(struct ec_response_vbnvcontext); 3918800fc7SEmilio López const size_t payload = max(para_sz, resp_sz); 4018800fc7SEmilio López 4118800fc7SEmilio López msg = kmalloc(sizeof(*msg) + payload, GFP_KERNEL); 4218800fc7SEmilio López if (!msg) 4318800fc7SEmilio López return -ENOMEM; 4418800fc7SEmilio López 4518800fc7SEmilio López /* NB: we only kmalloc()ated enough space for the op field */ 4618800fc7SEmilio López params = (struct ec_params_vbnvcontext *)msg->data; 4718800fc7SEmilio López params->op = EC_VBNV_CONTEXT_OP_READ; 4818800fc7SEmilio López 4918800fc7SEmilio López msg->version = EC_VER_VBNV_CONTEXT; 5018800fc7SEmilio López msg->command = EC_CMD_VBNV_CONTEXT; 5118800fc7SEmilio López msg->outsize = para_sz; 5218800fc7SEmilio López msg->insize = resp_sz; 5318800fc7SEmilio López 5418800fc7SEmilio López err = cros_ec_cmd_xfer(ecdev, msg); 5518800fc7SEmilio López if (err < 0) { 5618800fc7SEmilio López dev_err(dev, "Error sending read request: %d\n", err); 5718800fc7SEmilio López kfree(msg); 5818800fc7SEmilio López return err; 5918800fc7SEmilio López } 6018800fc7SEmilio López 6118800fc7SEmilio López memcpy(buf, msg->data, resp_sz); 6218800fc7SEmilio López 6318800fc7SEmilio López kfree(msg); 6418800fc7SEmilio López return resp_sz; 6518800fc7SEmilio López } 6618800fc7SEmilio López 6718800fc7SEmilio López static ssize_t vboot_context_write(struct file *filp, struct kobject *kobj, 6818800fc7SEmilio López struct bin_attribute *attr, char *buf, 6918800fc7SEmilio López loff_t pos, size_t count) 7018800fc7SEmilio López { 7118800fc7SEmilio López struct device *dev = container_of(kobj, struct device, kobj); 72*79a3d603SGwendal Grignou struct cros_ec_dev *ec = to_cros_ec_dev(dev); 7318800fc7SEmilio López struct cros_ec_device *ecdev = ec->ec_dev; 7418800fc7SEmilio López struct ec_params_vbnvcontext *params; 7518800fc7SEmilio López struct cros_ec_command *msg; 7618800fc7SEmilio López int err; 7718800fc7SEmilio López const size_t para_sz = sizeof(*params); 7818800fc7SEmilio López const size_t data_sz = sizeof(params->block); 7918800fc7SEmilio López 8018800fc7SEmilio López /* Only write full values */ 8118800fc7SEmilio López if (count != data_sz) 8218800fc7SEmilio López return -EINVAL; 8318800fc7SEmilio López 8418800fc7SEmilio López msg = kmalloc(sizeof(*msg) + para_sz, GFP_KERNEL); 8518800fc7SEmilio López if (!msg) 8618800fc7SEmilio López return -ENOMEM; 8718800fc7SEmilio López 8818800fc7SEmilio López params = (struct ec_params_vbnvcontext *)msg->data; 8918800fc7SEmilio López params->op = EC_VBNV_CONTEXT_OP_WRITE; 9018800fc7SEmilio López memcpy(params->block, buf, data_sz); 9118800fc7SEmilio López 9218800fc7SEmilio López msg->version = EC_VER_VBNV_CONTEXT; 9318800fc7SEmilio López msg->command = EC_CMD_VBNV_CONTEXT; 9418800fc7SEmilio López msg->outsize = para_sz; 9518800fc7SEmilio López msg->insize = 0; 9618800fc7SEmilio López 9718800fc7SEmilio López err = cros_ec_cmd_xfer(ecdev, msg); 9818800fc7SEmilio López if (err < 0) { 9918800fc7SEmilio López dev_err(dev, "Error sending write request: %d\n", err); 10018800fc7SEmilio López kfree(msg); 10118800fc7SEmilio López return err; 10218800fc7SEmilio López } 10318800fc7SEmilio López 10418800fc7SEmilio López kfree(msg); 10518800fc7SEmilio López return data_sz; 10618800fc7SEmilio López } 10718800fc7SEmilio López 10818800fc7SEmilio López static umode_t cros_ec_vbc_is_visible(struct kobject *kobj, 10918800fc7SEmilio López struct bin_attribute *a, int n) 11018800fc7SEmilio López { 11118800fc7SEmilio López struct device *dev = container_of(kobj, struct device, kobj); 112*79a3d603SGwendal Grignou struct cros_ec_dev *ec = to_cros_ec_dev(dev); 11318800fc7SEmilio López struct device_node *np = ec->ec_dev->dev->of_node; 11418800fc7SEmilio López 11518800fc7SEmilio López if (IS_ENABLED(CONFIG_OF) && np) { 11618800fc7SEmilio López if (of_property_read_bool(np, "google,has-vbc-nvram")) 11718800fc7SEmilio López return a->attr.mode; 11818800fc7SEmilio López } 11918800fc7SEmilio López 12018800fc7SEmilio López return 0; 12118800fc7SEmilio López } 12218800fc7SEmilio López 12318800fc7SEmilio López static BIN_ATTR_RW(vboot_context, 16); 12418800fc7SEmilio López 12518800fc7SEmilio López static struct bin_attribute *cros_ec_vbc_bin_attrs[] = { 12618800fc7SEmilio López &bin_attr_vboot_context, 12718800fc7SEmilio López NULL 12818800fc7SEmilio López }; 12918800fc7SEmilio López 13018800fc7SEmilio López struct attribute_group cros_ec_vbc_attr_group = { 13118800fc7SEmilio López .name = "vbc", 13218800fc7SEmilio López .bin_attrs = cros_ec_vbc_bin_attrs, 13318800fc7SEmilio López .is_bin_visible = cros_ec_vbc_is_visible, 13418800fc7SEmilio López }; 135ea01a31bSThierry Escande EXPORT_SYMBOL(cros_ec_vbc_attr_group); 136