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:
Che-Liang Chiou
2012-11-06 18:07:56 -08:00
committed by Ben Zhang
parent 9ce7185b8f
commit f45daeadda
5 changed files with 287 additions and 0 deletions

View File

@@ -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>;
};
};

View File

@@ -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.

View File

@@ -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

View File

@@ -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.

View 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),
&param, 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),
&param, 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");