Commit e7c256fb authored by Bill Richardson's avatar Bill Richardson Committed by Olof Johansson

platform/chrome: Add Chrome OS EC userspace device interface

This patch adds a device interface to access the
Chrome OS Embedded Controller from user-space.
Signed-off-by: default avatarBill Richardson <wfrichar@chromium.org>
Reviewed-by: default avatarSimon Glass <sjg@google.com>
Signed-off-by: default avatarJavier Martinez Canillas <javier.martinez@collabora.co.uk>
Reviewed-by: default avatarGwendal Grignou <gwendal@chromium.org>
Tested-by: default avatarGwendal Grignou <gwendal@chromium.org>
Reviewed-by: default avatarGwendal Grignou <gwendal@chromium.org>
Signed-off-by: default avatarOlof Johansson <olof@lixom.net>
parent ec2f33ab
...@@ -321,6 +321,7 @@ Code Seq#(hex) Include File Comments ...@@ -321,6 +321,7 @@ Code Seq#(hex) Include File Comments
0xDB 00-0F drivers/char/mwave/mwavepub.h 0xDB 00-0F drivers/char/mwave/mwavepub.h
0xDD 00-3F ZFCP device driver see drivers/s390/scsi/ 0xDD 00-3F ZFCP device driver see drivers/s390/scsi/
<mailto:aherrman@de.ibm.com> <mailto:aherrman@de.ibm.com>
0xEC 00-01 drivers/platform/chrome/cros_ec_dev.h ChromeOS EC driver
0xF3 00-3F drivers/usb/misc/sisusbvga/sisusb.h sisfb (in development) 0xF3 00-3F drivers/usb/misc/sisusbvga/sisusb.h sisfb (in development)
<mailto:thomas@winischhofer.net> <mailto:thomas@winischhofer.net>
0xF4 00-1F video/mbxfb.h mbxfb 0xF4 00-1F video/mbxfb.h mbxfb
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
menuconfig CHROME_PLATFORMS menuconfig CHROME_PLATFORMS
bool "Platform support for Chrome hardware" bool "Platform support for Chrome hardware"
depends on X86 depends on X86 || ARM
---help--- ---help---
Say Y here to get to see options for platform support for Say Y here to get to see options for platform support for
various Chromebooks and Chromeboxes. This option alone does various Chromebooks and Chromeboxes. This option alone does
...@@ -16,8 +16,7 @@ if CHROME_PLATFORMS ...@@ -16,8 +16,7 @@ if CHROME_PLATFORMS
config CHROMEOS_LAPTOP config CHROMEOS_LAPTOP
tristate "Chrome OS Laptop" tristate "Chrome OS Laptop"
depends on I2C depends on I2C && DMI && X86
depends on DMI
---help--- ---help---
This driver instantiates i2c and smbus devices such as This driver instantiates i2c and smbus devices such as
light sensors and touchpads. light sensors and touchpads.
...@@ -27,6 +26,7 @@ config CHROMEOS_LAPTOP ...@@ -27,6 +26,7 @@ config CHROMEOS_LAPTOP
config CHROMEOS_PSTORE config CHROMEOS_PSTORE
tristate "Chrome OS pstore support" tristate "Chrome OS pstore support"
depends on X86
---help--- ---help---
This module instantiates the persistent storage on x86 ChromeOS This module instantiates the persistent storage on x86 ChromeOS
devices. It can be used to store away console logs and crash devices. It can be used to store away console logs and crash
...@@ -38,6 +38,14 @@ config CHROMEOS_PSTORE ...@@ -38,6 +38,14 @@ config CHROMEOS_PSTORE
If you have a supported Chromebook, choose Y or M here. If you have a supported Chromebook, choose Y or M here.
The module will be called chromeos_pstore. The module will be called chromeos_pstore.
config CROS_EC_CHARDEV
tristate "Chrome OS Embedded Controller userspace device interface"
depends on MFD_CROS_EC
---help---
This driver adds support to talk with the ChromeOS EC from userspace.
If you have a supported Chromebook, choose Y or M here.
The module will be called cros_ec_dev.
config CROS_EC_LPC config CROS_EC_LPC
tristate "ChromeOS Embedded Controller (LPC)" tristate "ChromeOS Embedded Controller (LPC)"
......
obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o
obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o
obj-$(CONFIG_CROS_EC_CHARDEV) += cros_ec_dev.o
obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpc.o obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpc.o
/*
* cros_ec_dev - expose the Chrome OS Embedded Controller to user-space
*
* Copyright (C) 2014 Google, Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include "cros_ec_dev.h"
/* Device variables */
#define CROS_MAX_DEV 128
static struct class *cros_class;
static int ec_major;
/* Basic communication */
static int ec_get_version(struct cros_ec_device *ec, char *str, int maxlen)
{
struct ec_response_get_version *resp;
static const char * const current_image_name[] = {
"unknown", "read-only", "read-write", "invalid",
};
struct cros_ec_command msg = {
.version = 0,
.command = EC_CMD_GET_VERSION,
.outdata = { 0 },
.outsize = 0,
.indata = { 0 },
.insize = sizeof(*resp),
};
int ret;
ret = cros_ec_cmd_xfer(ec, &msg);
if (ret < 0)
return ret;
if (msg.result != EC_RES_SUCCESS) {
snprintf(str, maxlen,
"%s\nUnknown EC version: EC returned %d\n",
CROS_EC_DEV_VERSION, msg.result);
return 0;
}
resp = (struct ec_response_get_version *)msg.indata;
if (resp->current_image >= ARRAY_SIZE(current_image_name))
resp->current_image = 3; /* invalid */
snprintf(str, maxlen, "%s\n%s\n%s\n\%s\n", CROS_EC_DEV_VERSION,
resp->version_string_ro, resp->version_string_rw,
current_image_name[resp->current_image]);
return 0;
}
/* Device file ops */
static int ec_device_open(struct inode *inode, struct file *filp)
{
filp->private_data = container_of(inode->i_cdev,
struct cros_ec_device, cdev);
return 0;
}
static int ec_device_release(struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t ec_device_read(struct file *filp, char __user *buffer,
size_t length, loff_t *offset)
{
struct cros_ec_device *ec = filp->private_data;
char msg[sizeof(struct ec_response_get_version) +
sizeof(CROS_EC_DEV_VERSION)];
size_t count;
int ret;
if (*offset != 0)
return 0;
ret = ec_get_version(ec, msg, sizeof(msg));
if (ret)
return ret;
count = min(length, strlen(msg));
if (copy_to_user(buffer, msg, count))
return -EFAULT;
*offset = count;
return count;
}
/* Ioctls */
static long ec_device_ioctl_xcmd(struct cros_ec_device *ec, void __user *arg)
{
long ret;
struct cros_ec_command s_cmd = { };
if (copy_from_user(&s_cmd, arg, sizeof(s_cmd)))
return -EFAULT;
ret = cros_ec_cmd_xfer(ec, &s_cmd);
/* Only copy data to userland if data was received. */
if (ret < 0)
return ret;
if (copy_to_user(arg, &s_cmd, sizeof(s_cmd)))
return -EFAULT;
return 0;
}
static long ec_device_ioctl_readmem(struct cros_ec_device *ec, void __user *arg)
{
struct cros_ec_readmem s_mem = { };
long num;
/* Not every platform supports direct reads */
if (!ec->cmd_readmem)
return -ENOTTY;
if (copy_from_user(&s_mem, arg, sizeof(s_mem)))
return -EFAULT;
num = ec->cmd_readmem(ec, s_mem.offset, s_mem.bytes, s_mem.buffer);
if (num <= 0)
return num;
if (copy_to_user((void __user *)arg, &s_mem, sizeof(s_mem)))
return -EFAULT;
return 0;
}
static long ec_device_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
struct cros_ec_device *ec = filp->private_data;
if (_IOC_TYPE(cmd) != CROS_EC_DEV_IOC)
return -ENOTTY;
switch (cmd) {
case CROS_EC_DEV_IOCXCMD:
return ec_device_ioctl_xcmd(ec, (void __user *)arg);
case CROS_EC_DEV_IOCRDMEM:
return ec_device_ioctl_readmem(ec, (void __user *)arg);
}
return -ENOTTY;
}
/* Module initialization */
static const struct file_operations fops = {
.open = ec_device_open,
.release = ec_device_release,
.read = ec_device_read,
.unlocked_ioctl = ec_device_ioctl,
};
static int ec_device_probe(struct platform_device *pdev)
{
struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
int retval = -ENOTTY;
dev_t devno = MKDEV(ec_major, 0);
/* Instantiate it (and remember the EC) */
cdev_init(&ec->cdev, &fops);
retval = cdev_add(&ec->cdev, devno, 1);
if (retval) {
dev_err(&pdev->dev, ": failed to add character device\n");
return retval;
}
ec->vdev = device_create(cros_class, NULL, devno, ec,
CROS_EC_DEV_NAME);
if (IS_ERR(ec->vdev)) {
retval = PTR_ERR(ec->vdev);
dev_err(&pdev->dev, ": failed to create device\n");
cdev_del(&ec->cdev);
return retval;
}
return 0;
}
static int ec_device_remove(struct platform_device *pdev)
{
struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
device_destroy(cros_class, MKDEV(ec_major, 0));
cdev_del(&ec->cdev);
return 0;
}
static struct platform_driver cros_ec_dev_driver = {
.driver = {
.name = "cros-ec-ctl",
},
.probe = ec_device_probe,
.remove = ec_device_remove,
};
static int __init cros_ec_dev_init(void)
{
int ret;
dev_t dev = 0;
cros_class = class_create(THIS_MODULE, "chromeos");
if (IS_ERR(cros_class)) {
pr_err(CROS_EC_DEV_NAME ": failed to register device class\n");
return PTR_ERR(cros_class);
}
/* Get a range of minor numbers (starting with 0) to work with */
ret = alloc_chrdev_region(&dev, 0, CROS_MAX_DEV, CROS_EC_DEV_NAME);
if (ret < 0) {
pr_err(CROS_EC_DEV_NAME ": alloc_chrdev_region() failed\n");
goto failed_chrdevreg;
}
ec_major = MAJOR(dev);
/* Register the driver */
ret = platform_driver_register(&cros_ec_dev_driver);
if (ret < 0) {
pr_warn(CROS_EC_DEV_NAME ": can't register driver: %d\n", ret);
goto failed_devreg;
}
return 0;
failed_devreg:
unregister_chrdev_region(MKDEV(ec_major, 0), CROS_MAX_DEV);
failed_chrdevreg:
class_destroy(cros_class);
return ret;
}
static void __exit cros_ec_dev_exit(void)
{
platform_driver_unregister(&cros_ec_dev_driver);
unregister_chrdev(ec_major, CROS_EC_DEV_NAME);
class_destroy(cros_class);
}
module_init(cros_ec_dev_init);
module_exit(cros_ec_dev_exit);
MODULE_AUTHOR("Bill Richardson <wfrichar@chromium.org>");
MODULE_DESCRIPTION("Userspace interface to the Chrome OS Embedded Controller");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");
/*
* cros_ec_dev - expose the Chrome OS Embedded Controller to userspace
*
* Copyright (C) 2014 Google, Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef _CROS_EC_DEV_H_
#define _CROS_EC_DEV_H_
#include <linux/ioctl.h>
#include <linux/types.h>
#include <linux/mfd/cros_ec.h>
#define CROS_EC_DEV_NAME "cros_ec"
#define CROS_EC_DEV_VERSION "1.0.0"
/*
* @offset: within EC_LPC_ADDR_MEMMAP region
* @bytes: number of bytes to read. zero means "read a string" (including '\0')
* (at most only EC_MEMMAP_SIZE bytes can be read)
* @buffer: where to store the result
* ioctl returns the number of bytes read, negative on error
*/
struct cros_ec_readmem {
uint32_t offset;
uint32_t bytes;
uint8_t buffer[EC_MEMMAP_SIZE];
};
#define CROS_EC_DEV_IOC 0xEC
#define CROS_EC_DEV_IOCXCMD _IOWR(CROS_EC_DEV_IOC, 0, struct cros_ec_command)
#define CROS_EC_DEV_IOCRDMEM _IOWR(CROS_EC_DEV_IOC, 1, struct cros_ec_readmem)
#endif /* _CROS_EC_DEV_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment