Skip to content

Commit

Permalink
mfd: Add ChromeOS EC implementation
Browse files Browse the repository at this point in the history
This is the base EC implementation, which provides a high level
interface to the EC for use by the rest of the kernel. The actual
communcations is dealt with by a separate protocol driver which
registers itself with this interface.

Interrupts are passed on through a notifier.

A simple message structure is used to pass messages to the
protocol driver.
Signed-off-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Che-Liang Chiou <clchiou@chromium.org>
Signed-off-by: Jonathan Kliegman <kliegs@chromium.org>
Signed-off-by: Luigi Semenzato <semenzato@chromium.org>
Signed-off-by: Olof Johansson <olofj@chromium.org>
Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
  • Loading branch information
Simon Glass authored and Samuel Ortiz committed Apr 5, 2013
1 parent deaf39e commit 4ab6174
Show file tree
Hide file tree
Showing 5 changed files with 424 additions and 0 deletions.
56 changes: 56 additions & 0 deletions Documentation/devicetree/bindings/mfd/cros-ec.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
ChromeOS Embedded Controller

Google's ChromeOS EC is a Cortex-M device which talks to the AP and
implements various function such as keyboard and battery charging.

The EC can be connect through various means (I2C, SPI, LPC) and the
compatible string used depends on the inteface. Each connection method has
its own driver which connects to the top level interface-agnostic EC driver.
Other Linux driver (such as cros-ec-keyb for the matrix keyboard) connect to
the top-level driver.

Required properties (I2C):
- compatible: "google,cros-ec-i2c"
- reg: I2C slave address

Required properties (SPI):
- compatible: "google,cros-ec-spi"
- reg: SPI chip select

Required properties (LPC):
- compatible: "google,cros-ec-lpc"
- reg: List of (IO address, size) pairs defining the interface uses


Example for I2C:

i2c@12CA0000 {
cros-ec@1e {
reg = <0x1e>;
compatible = "google,cros-ec-i2c";
interrupts = <14 0>;
interrupt-parent = <&wakeup_eint>;
wakeup-source;
};


Example for SPI:

spi@131b0000 {
ec@0 {
compatible = "google,cros-ec-spi";
reg = <0x0>;
interrupts = <14 0>;
interrupt-parent = <&wakeup_eint>;
wakeup-source;
spi-max-frequency = <5000000>;
controller-data {
cs-gpio = <&gpf0 3 4 3 0>;
samsung,spi-cs;
samsung,spi-feedback-delay = <2>;
};
};
};


Example for LPC is not supplied as it is not yet implemented.
8 changes: 8 additions & 0 deletions drivers/mfd/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ config MFD_88PM860X
select individual components like voltage regulators, RTC and
battery-charger under the corresponding menus.

config MFD_CROS_EC
tristate "Support ChromeOS Embedded Controller"
help
If you say Y here you get support for the ChromeOS Embedded
Controller (EC) providing keyboard, battery and power services.
You also ned to enable the driver for the bus you are using. The
protocol for talking to the EC is defined by the bus driver.

config MFD_88PM800
tristate "Support Marvell 88PM800"
depends on I2C=y && GENERIC_HARDIRQS
Expand Down
1 change: 1 addition & 0 deletions drivers/mfd/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ obj-$(CONFIG_MFD_88PM800) += 88pm800.o 88pm80x.o
obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o
obj-$(CONFIG_MFD_SM501) += sm501.o
obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o
obj-$(CONFIG_MFD_CROS_EC) += cros_ec.o

rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o
obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o
Expand Down
189 changes: 189 additions & 0 deletions drivers/mfd/cros_ec.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/*
* ChromeOS EC multi-function device
*
* Copyright (C) 2012 Google, Inc
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*
* The ChromeOS EC multi function device is used to mux all the requests
* to the EC device for its multiple features: keyboard controller,
* battery charging and regulator control, firmware update.
*/

#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/mfd/core.h>
#include <linux/mfd/cros_ec.h>
#include <linux/mfd/cros_ec_commands.h>

int cros_ec_prepare_tx(struct cros_ec_device *ec_dev,
struct cros_ec_msg *msg)
{
uint8_t *out;
int csum, i;

BUG_ON(msg->out_len > EC_HOST_PARAM_SIZE);
out = ec_dev->dout;
out[0] = EC_CMD_VERSION0 + msg->version;
out[1] = msg->cmd;
out[2] = msg->out_len;
csum = out[0] + out[1] + out[2];
for (i = 0; i < msg->out_len; i++)
csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->out_buf[i];
out[EC_MSG_TX_HEADER_BYTES + msg->out_len] = (uint8_t)(csum & 0xff);

return EC_MSG_TX_PROTO_BYTES + msg->out_len;
}

static int cros_ec_command_sendrecv(struct cros_ec_device *ec_dev,
uint16_t cmd, void *out_buf, int out_len,
void *in_buf, int in_len)
{
struct cros_ec_msg msg;

msg.version = cmd >> 8;
msg.cmd = cmd & 0xff;
msg.out_buf = out_buf;
msg.out_len = out_len;
msg.in_buf = in_buf;
msg.in_len = in_len;

return ec_dev->command_xfer(ec_dev, &msg);
}

static int cros_ec_command_recv(struct cros_ec_device *ec_dev,
uint16_t cmd, void *buf, int buf_len)
{
return cros_ec_command_sendrecv(ec_dev, cmd, NULL, 0, buf, buf_len);
}

static int cros_ec_command_send(struct cros_ec_device *ec_dev,
uint16_t cmd, void *buf, int buf_len)
{
return cros_ec_command_sendrecv(ec_dev, cmd, buf, buf_len, NULL, 0);
}

static irqreturn_t ec_irq_thread(int irq, void *data)
{
struct cros_ec_device *ec_dev = data;

if (device_may_wakeup(ec_dev->dev))
pm_wakeup_event(ec_dev->dev, 0);

blocking_notifier_call_chain(&ec_dev->event_notifier, 1, ec_dev);

return IRQ_HANDLED;
}

static struct mfd_cell cros_devs[] = {
{
.name = "cros-ec-keyb",
.id = 1,
.of_compatible = "google,cros-ec-keyb",
},
};

int cros_ec_register(struct cros_ec_device *ec_dev)
{
struct device *dev = ec_dev->dev;
int err = 0;

BLOCKING_INIT_NOTIFIER_HEAD(&ec_dev->event_notifier);

ec_dev->command_send = cros_ec_command_send;
ec_dev->command_recv = cros_ec_command_recv;
ec_dev->command_sendrecv = cros_ec_command_sendrecv;

if (ec_dev->din_size) {
ec_dev->din = kmalloc(ec_dev->din_size, GFP_KERNEL);
if (!ec_dev->din) {
err = -ENOMEM;
goto fail_din;
}
}
if (ec_dev->dout_size) {
ec_dev->dout = kmalloc(ec_dev->dout_size, GFP_KERNEL);
if (!ec_dev->dout) {
err = -ENOMEM;
goto fail_dout;
}
}

if (!ec_dev->irq) {
dev_dbg(dev, "no valid IRQ: %d\n", ec_dev->irq);
goto fail_irq;
}

err = request_threaded_irq(ec_dev->irq, NULL, ec_irq_thread,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"chromeos-ec", ec_dev);
if (err) {
dev_err(dev, "request irq %d: error %d\n", ec_dev->irq, err);
goto fail_irq;
}

err = mfd_add_devices(dev, 0, cros_devs,
ARRAY_SIZE(cros_devs),
NULL, ec_dev->irq, NULL);
if (err) {
dev_err(dev, "failed to add mfd devices\n");
goto fail_mfd;
}

dev_info(dev, "Chrome EC (%s)\n", ec_dev->name);

return 0;

fail_mfd:
free_irq(ec_dev->irq, ec_dev);
fail_irq:
kfree(ec_dev->dout);
fail_dout:
kfree(ec_dev->din);
fail_din:
return err;
}

int cros_ec_remove(struct cros_ec_device *ec_dev)
{
mfd_remove_devices(ec_dev->dev);
free_irq(ec_dev->irq, ec_dev);
kfree(ec_dev->dout);
kfree(ec_dev->din);

return 0;
}

#ifdef CONFIG_PM_SLEEP
int cros_ec_suspend(struct cros_ec_device *ec_dev)
{
struct device *dev = ec_dev->dev;

if (device_may_wakeup(dev))
ec_dev->wake_enabled = !enable_irq_wake(ec_dev->irq);

disable_irq(ec_dev->irq);
ec_dev->was_wake_device = ec_dev->wake_enabled;

return 0;
}

int cros_ec_resume(struct cros_ec_device *ec_dev)
{
enable_irq(ec_dev->irq);

if (ec_dev->wake_enabled) {
disable_irq_wake(ec_dev->irq);
ec_dev->wake_enabled = 0;
}

return 0;
}
#endif
Loading

0 comments on commit 4ab6174

Please sign in to comment.