CHROMIUM: add vbc_ec implementation
This allows kernel to read/write vboot context from EC's nvram. BUG=chrome-os-partner:15609 TEST=manual 1. Make sure kernel is set to use vbc ec and change directory $ cat /proc/device-tree/firmware/chromeos/nonvolatile-context-storage mkbp $ cd /sys/bus/platform/drivers/cros-ec-vbc/cros-ec-vbc.4 2. Test read $ test $(cat vboot_context) = $(mosys nvram vboot read) ; echo $? 0 3. Test write $ echo 0123456789abcdef0123456789abcdef > vboot_context $ test 0123456789abcdef0123456789abcdef = $(mosys nvram vboot read) ; echo $? 0 Change-Id: I860921403c09e8c6cfb04713b5c76a9fd9ffbe47 [sonnyrao: topic sync: split into platform and exynos] [dianders: fixes for 3.8 and upstream ec] [bleung: topic sync: split into cros_ec topic and platform] Signed-off-by: Che-Liang Chiou <clchiou@chromium.org> Signed-off-by: Doug Anderson <dianders@chromium.org> Reviewed-by: Simon Glass <sjg@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/49246
This commit is contained in:
committed by
Ben Zhang
parent
9ce7185b8f
commit
f45daeadda
@@ -17,6 +17,8 @@ Optional properties:
|
||||
look for the block device used for storing vboot context in this property.
|
||||
It is a 4-tuple of block device phandle, LBA of the block storing vboot
|
||||
context, byte offset in the block, and size of vboot context in bytes.
|
||||
- chromeos-vbc-ec : If nonvolatile-context-storage is "mkbp", kernel should
|
||||
look for the phandle to the device tree node of EC in this property.
|
||||
|
||||
Example:
|
||||
|
||||
@@ -25,5 +27,6 @@ firmware {
|
||||
write-protect-gpio = <&gpd1 6 0 0x10000 0>;
|
||||
nonvolatile-context-storage = "disk";
|
||||
chromeos-vbc-blk = <&mshc_0 0 0 16>;
|
||||
chromeos-vbc-ec = <&ec>;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -30,3 +30,12 @@ config CHROMEOS_VBC_BLK
|
||||
Provides access to vboot context stored on block device. Select this
|
||||
if you have a ChromeOS firmware that does not support storing vboot
|
||||
context in EC's nvram.
|
||||
|
||||
config CHROMEOS_VBC_EC
|
||||
bool
|
||||
depends on CHROMEOS
|
||||
|
||||
---help---
|
||||
Provides access to vboot context stored on EC's nvram. Select this
|
||||
if you have a ChromeOS firmware that supports storing vboot context
|
||||
in EC's nvram.
|
||||
|
||||
@@ -8,3 +8,4 @@ obj-$(CONFIG_GOLDFISH) += goldfish/
|
||||
obj-$(CONFIG_CHROME_PLATFORMS) += chrome/
|
||||
obj-$(CONFIG_CHROMEOS) += chromeos.o
|
||||
obj-$(CONFIG_CHROMEOS_VBC_BLK) += chromeos_vbc_blk.o
|
||||
obj-$(CONFIG_CHROMEOS_VBC_EC) += chromeos_vbc_ec.o
|
||||
|
||||
@@ -17,6 +17,7 @@ config ARM_CHROMEOS_FIRMWARE
|
||||
bool "ChromeOS firmware interface driver"
|
||||
select CHROMEOS
|
||||
select CHROMEOS_VBC_BLK
|
||||
select CHROMEOS_VBC_EC
|
||||
---help---
|
||||
This driver provides an interface to ChromeOS firmware.
|
||||
|
||||
|
||||
273
drivers/platform/chromeos_vbc_ec.c
Normal file
273
drivers/platform/chromeos_vbc_ec.c
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Chromium OS Authors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "chromeos_vbc_ec: " fmt
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/mfd/cros_ec.h>
|
||||
|
||||
#include "chromeos.h"
|
||||
|
||||
static phandle ec_phandle;
|
||||
|
||||
static int match_of_node(struct device *dev, void *data)
|
||||
{
|
||||
return dev->of_node == data;
|
||||
}
|
||||
|
||||
static struct device *get_ec_dev(phandle phandle)
|
||||
{
|
||||
struct device_node *dn;
|
||||
struct device *dev;
|
||||
|
||||
if (!phandle)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
dn = of_find_node_by_phandle(phandle);
|
||||
if (!dn)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
dev = bus_find_device(&platform_bus_type, NULL, dn, match_of_node);
|
||||
of_node_put(dn);
|
||||
if (!dev)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
static int vbc_read(struct device *dev, void *buf, size_t count)
|
||||
{
|
||||
struct cros_ec_device *ec;
|
||||
struct ec_params_vbnvcontext param;
|
||||
struct ec_response_vbnvcontext resp;
|
||||
int err;
|
||||
|
||||
ec = dev_get_drvdata(dev->parent);
|
||||
|
||||
param.op = EC_VBNV_CONTEXT_OP_READ;
|
||||
err = ec->command_sendrecv(ec,
|
||||
EC_CMD_VBNV_CONTEXT | (EC_VER_VBNV_CONTEXT << 8),
|
||||
¶m, sizeof(param.op),
|
||||
&resp, sizeof(resp));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
count = min(count, sizeof(resp.block));
|
||||
memcpy(buf, resp.block, count);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int vbc_write(struct device *dev, const void *buf, size_t count)
|
||||
{
|
||||
struct cros_ec_device *ec;
|
||||
struct ec_params_vbnvcontext param;
|
||||
int err;
|
||||
|
||||
ec = dev_get_drvdata(dev->parent);
|
||||
|
||||
count = min(count, sizeof(param.block));
|
||||
|
||||
param.op = EC_VBNV_CONTEXT_OP_WRITE;
|
||||
memcpy(param.block, buf, count);
|
||||
err = ec->command_send(ec,
|
||||
EC_CMD_VBNV_CONTEXT | (EC_VER_VBNV_CONTEXT << 8),
|
||||
¶m, sizeof(param));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t dev_attr_vbc_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ec_params_vbnvcontext param;
|
||||
int i, j, count;
|
||||
|
||||
count = vbc_read(dev, param.block, sizeof(param.block));
|
||||
if (count < 0)
|
||||
return count;
|
||||
|
||||
for (i = j = 0; i < count && j < PAGE_SIZE - 2; i++) {
|
||||
buf[j++] = hex_asc_hi(param.block[i]);
|
||||
buf[j++] = hex_asc_lo(param.block[i]);
|
||||
}
|
||||
buf[j++] = '\0';
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
static ssize_t dev_attr_vbc_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct ec_params_vbnvcontext param;
|
||||
int err;
|
||||
|
||||
memset(param.block, 0x0, sizeof(param.block));
|
||||
err = hex2bin(param.block, buf, min(count / 2, sizeof(param.block)));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = vbc_write(dev, param.block, sizeof(param.block));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(vboot_context, S_IRUSR | S_IWUSR,
|
||||
dev_attr_vbc_show, dev_attr_vbc_store);
|
||||
|
||||
static struct attribute *vbc_attrs[] = {
|
||||
&dev_attr_vboot_context.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group vbc_attr_group = {
|
||||
.attrs = vbc_attrs,
|
||||
};
|
||||
|
||||
static int chromeos_vbc_ec_read(void *buf, size_t count)
|
||||
{
|
||||
struct device *dev;
|
||||
int err;
|
||||
|
||||
dev = get_ec_dev(ec_phandle);
|
||||
if (IS_ERR(dev))
|
||||
return PTR_ERR(dev);
|
||||
|
||||
err = vbc_read(dev, buf, count);
|
||||
put_device(dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int chromeos_vbc_ec_write(const void *buf, size_t count)
|
||||
{
|
||||
struct device *dev;
|
||||
int err;
|
||||
|
||||
dev = get_ec_dev(ec_phandle);
|
||||
if (IS_ERR(dev))
|
||||
return PTR_ERR(dev);
|
||||
|
||||
err = vbc_write(dev, buf, count);
|
||||
put_device(dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int chromeos_vbc_ec_probe(struct platform_device *pdev)
|
||||
{
|
||||
pdev->dev.of_node = of_find_node_by_phandle(ec_phandle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct chromeos_vbc chromeos_vbc_ec = {
|
||||
.name = "cros-ec-vbc",
|
||||
.read = chromeos_vbc_ec_read,
|
||||
.write = chromeos_vbc_ec_write,
|
||||
};
|
||||
|
||||
static struct platform_driver chromeos_vbc_ec_driver = {
|
||||
.probe = chromeos_vbc_ec_probe,
|
||||
.driver = {
|
||||
.name = "cros-ec-vbc",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init chromeos_vbc_ec_init(void)
|
||||
{
|
||||
struct device_node *of_node;
|
||||
struct device *dev;
|
||||
const char *vbc_type;
|
||||
int err;
|
||||
|
||||
of_node = of_find_compatible_node(NULL, NULL, "chromeos-firmware");
|
||||
if (!of_node)
|
||||
return -ENODEV;
|
||||
|
||||
err = of_property_read_string(of_node, "nonvolatile-context-storage",
|
||||
&vbc_type);
|
||||
if (err)
|
||||
goto exit;
|
||||
|
||||
if (strcmp(vbc_type, "mkbp")) {
|
||||
err = 0; /* not configured to use vbc_ec, exit normally. */
|
||||
goto exit;
|
||||
}
|
||||
|
||||
err = of_property_read_u32(of_node, "chromeos-vbc-ec", &ec_phandle);
|
||||
if (err) {
|
||||
pr_err("Could not find chromeos-vbc-ec: err=%d\n", err);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
err = chromeos_vbc_register(&chromeos_vbc_ec);
|
||||
if (err < 0)
|
||||
goto exit;
|
||||
|
||||
err = platform_driver_register(&chromeos_vbc_ec_driver);
|
||||
if (err < 0)
|
||||
goto exit;
|
||||
|
||||
/* Create a sysfs entry for manual debugging/testing. */
|
||||
dev = get_ec_dev(ec_phandle);
|
||||
if (IS_ERR(dev)) {
|
||||
err = PTR_ERR(dev);
|
||||
pr_err("Could not find ec device: err=%d\n", err);
|
||||
goto exit;
|
||||
}
|
||||
err = sysfs_create_group(&dev->kobj, &vbc_attr_group);
|
||||
put_device(dev);
|
||||
if (err) {
|
||||
pr_err("Could not create sysfs entries: err=%d\n", err);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
exit:
|
||||
of_node_put(of_node);
|
||||
return err;
|
||||
}
|
||||
module_init(chromeos_vbc_ec_init);
|
||||
|
||||
static void __exit chromeos_vbc_ec_exit(void)
|
||||
{
|
||||
struct device *dev = get_ec_dev(ec_phandle);
|
||||
|
||||
if (IS_ERR(dev)) {
|
||||
pr_warn("Could not find ec device: err=%ld\n", PTR_ERR(dev));
|
||||
} else {
|
||||
sysfs_remove_group(&dev->kobj, &vbc_attr_group);
|
||||
put_device(dev);
|
||||
}
|
||||
|
||||
platform_driver_unregister(&chromeos_vbc_ec_driver);
|
||||
}
|
||||
module_exit(chromeos_vbc_ec_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("ChromeOS vboot context on EC accessor");
|
||||
Reference in New Issue
Block a user