Commit 4ab6174e8cdb007cf500e484bdf454b8d14d524a

Authored by Simon Glass
Committed by Samuel Ortiz
1 parent deaf39efbc

mfd: Add ChromeOS EC implementation

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>

Showing 5 changed files with 424 additions and 0 deletions Side-by-side Diff

Documentation/devicetree/bindings/mfd/cros-ec.txt
  1 +ChromeOS Embedded Controller
  2 +
  3 +Google's ChromeOS EC is a Cortex-M device which talks to the AP and
  4 +implements various function such as keyboard and battery charging.
  5 +
  6 +The EC can be connect through various means (I2C, SPI, LPC) and the
  7 +compatible string used depends on the inteface. Each connection method has
  8 +its own driver which connects to the top level interface-agnostic EC driver.
  9 +Other Linux driver (such as cros-ec-keyb for the matrix keyboard) connect to
  10 +the top-level driver.
  11 +
  12 +Required properties (I2C):
  13 +- compatible: "google,cros-ec-i2c"
  14 +- reg: I2C slave address
  15 +
  16 +Required properties (SPI):
  17 +- compatible: "google,cros-ec-spi"
  18 +- reg: SPI chip select
  19 +
  20 +Required properties (LPC):
  21 +- compatible: "google,cros-ec-lpc"
  22 +- reg: List of (IO address, size) pairs defining the interface uses
  23 +
  24 +
  25 +Example for I2C:
  26 +
  27 +i2c@12CA0000 {
  28 + cros-ec@1e {
  29 + reg = <0x1e>;
  30 + compatible = "google,cros-ec-i2c";
  31 + interrupts = <14 0>;
  32 + interrupt-parent = <&wakeup_eint>;
  33 + wakeup-source;
  34 + };
  35 +
  36 +
  37 +Example for SPI:
  38 +
  39 +spi@131b0000 {
  40 + ec@0 {
  41 + compatible = "google,cros-ec-spi";
  42 + reg = <0x0>;
  43 + interrupts = <14 0>;
  44 + interrupt-parent = <&wakeup_eint>;
  45 + wakeup-source;
  46 + spi-max-frequency = <5000000>;
  47 + controller-data {
  48 + cs-gpio = <&gpf0 3 4 3 0>;
  49 + samsung,spi-cs;
  50 + samsung,spi-feedback-delay = <2>;
  51 + };
  52 + };
  53 +};
  54 +
  55 +
  56 +Example for LPC is not supplied as it is not yet implemented.
... ... @@ -21,6 +21,14 @@
21 21 select individual components like voltage regulators, RTC and
22 22 battery-charger under the corresponding menus.
23 23  
  24 +config MFD_CROS_EC
  25 + tristate "Support ChromeOS Embedded Controller"
  26 + help
  27 + If you say Y here you get support for the ChromeOS Embedded
  28 + Controller (EC) providing keyboard, battery and power services.
  29 + You also ned to enable the driver for the bus you are using. The
  30 + protocol for talking to the EC is defined by the bus driver.
  31 +
24 32 config MFD_88PM800
25 33 tristate "Support Marvell 88PM800"
26 34 depends on I2C=y && GENERIC_HARDIRQS
drivers/mfd/Makefile
... ... @@ -8,6 +8,7 @@
8 8 obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o
9 9 obj-$(CONFIG_MFD_SM501) += sm501.o
10 10 obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o
  11 +obj-$(CONFIG_MFD_CROS_EC) += cros_ec.o
11 12  
12 13 rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o
13 14 obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o
drivers/mfd/cros_ec.c
  1 +/*
  2 + * ChromeOS EC multi-function device
  3 + *
  4 + * Copyright (C) 2012 Google, Inc
  5 + *
  6 + * This software is licensed under the terms of the GNU General Public
  7 + * License version 2, as published by the Free Software Foundation, and
  8 + * may be copied, distributed, and modified under those terms.
  9 + *
  10 + * This program is distributed in the hope that it will be useful,
  11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13 + * GNU General Public License for more details.
  14 + *
  15 + * The ChromeOS EC multi function device is used to mux all the requests
  16 + * to the EC device for its multiple features: keyboard controller,
  17 + * battery charging and regulator control, firmware update.
  18 + */
  19 +
  20 +#include <linux/interrupt.h>
  21 +#include <linux/slab.h>
  22 +#include <linux/mfd/core.h>
  23 +#include <linux/mfd/cros_ec.h>
  24 +#include <linux/mfd/cros_ec_commands.h>
  25 +
  26 +int cros_ec_prepare_tx(struct cros_ec_device *ec_dev,
  27 + struct cros_ec_msg *msg)
  28 +{
  29 + uint8_t *out;
  30 + int csum, i;
  31 +
  32 + BUG_ON(msg->out_len > EC_HOST_PARAM_SIZE);
  33 + out = ec_dev->dout;
  34 + out[0] = EC_CMD_VERSION0 + msg->version;
  35 + out[1] = msg->cmd;
  36 + out[2] = msg->out_len;
  37 + csum = out[0] + out[1] + out[2];
  38 + for (i = 0; i < msg->out_len; i++)
  39 + csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->out_buf[i];
  40 + out[EC_MSG_TX_HEADER_BYTES + msg->out_len] = (uint8_t)(csum & 0xff);
  41 +
  42 + return EC_MSG_TX_PROTO_BYTES + msg->out_len;
  43 +}
  44 +
  45 +static int cros_ec_command_sendrecv(struct cros_ec_device *ec_dev,
  46 + uint16_t cmd, void *out_buf, int out_len,
  47 + void *in_buf, int in_len)
  48 +{
  49 + struct cros_ec_msg msg;
  50 +
  51 + msg.version = cmd >> 8;
  52 + msg.cmd = cmd & 0xff;
  53 + msg.out_buf = out_buf;
  54 + msg.out_len = out_len;
  55 + msg.in_buf = in_buf;
  56 + msg.in_len = in_len;
  57 +
  58 + return ec_dev->command_xfer(ec_dev, &msg);
  59 +}
  60 +
  61 +static int cros_ec_command_recv(struct cros_ec_device *ec_dev,
  62 + uint16_t cmd, void *buf, int buf_len)
  63 +{
  64 + return cros_ec_command_sendrecv(ec_dev, cmd, NULL, 0, buf, buf_len);
  65 +}
  66 +
  67 +static int cros_ec_command_send(struct cros_ec_device *ec_dev,
  68 + uint16_t cmd, void *buf, int buf_len)
  69 +{
  70 + return cros_ec_command_sendrecv(ec_dev, cmd, buf, buf_len, NULL, 0);
  71 +}
  72 +
  73 +static irqreturn_t ec_irq_thread(int irq, void *data)
  74 +{
  75 + struct cros_ec_device *ec_dev = data;
  76 +
  77 + if (device_may_wakeup(ec_dev->dev))
  78 + pm_wakeup_event(ec_dev->dev, 0);
  79 +
  80 + blocking_notifier_call_chain(&ec_dev->event_notifier, 1, ec_dev);
  81 +
  82 + return IRQ_HANDLED;
  83 +}
  84 +
  85 +static struct mfd_cell cros_devs[] = {
  86 + {
  87 + .name = "cros-ec-keyb",
  88 + .id = 1,
  89 + .of_compatible = "google,cros-ec-keyb",
  90 + },
  91 +};
  92 +
  93 +int cros_ec_register(struct cros_ec_device *ec_dev)
  94 +{
  95 + struct device *dev = ec_dev->dev;
  96 + int err = 0;
  97 +
  98 + BLOCKING_INIT_NOTIFIER_HEAD(&ec_dev->event_notifier);
  99 +
  100 + ec_dev->command_send = cros_ec_command_send;
  101 + ec_dev->command_recv = cros_ec_command_recv;
  102 + ec_dev->command_sendrecv = cros_ec_command_sendrecv;
  103 +
  104 + if (ec_dev->din_size) {
  105 + ec_dev->din = kmalloc(ec_dev->din_size, GFP_KERNEL);
  106 + if (!ec_dev->din) {
  107 + err = -ENOMEM;
  108 + goto fail_din;
  109 + }
  110 + }
  111 + if (ec_dev->dout_size) {
  112 + ec_dev->dout = kmalloc(ec_dev->dout_size, GFP_KERNEL);
  113 + if (!ec_dev->dout) {
  114 + err = -ENOMEM;
  115 + goto fail_dout;
  116 + }
  117 + }
  118 +
  119 + if (!ec_dev->irq) {
  120 + dev_dbg(dev, "no valid IRQ: %d\n", ec_dev->irq);
  121 + goto fail_irq;
  122 + }
  123 +
  124 + err = request_threaded_irq(ec_dev->irq, NULL, ec_irq_thread,
  125 + IRQF_TRIGGER_LOW | IRQF_ONESHOT,
  126 + "chromeos-ec", ec_dev);
  127 + if (err) {
  128 + dev_err(dev, "request irq %d: error %d\n", ec_dev->irq, err);
  129 + goto fail_irq;
  130 + }
  131 +
  132 + err = mfd_add_devices(dev, 0, cros_devs,
  133 + ARRAY_SIZE(cros_devs),
  134 + NULL, ec_dev->irq, NULL);
  135 + if (err) {
  136 + dev_err(dev, "failed to add mfd devices\n");
  137 + goto fail_mfd;
  138 + }
  139 +
  140 + dev_info(dev, "Chrome EC (%s)\n", ec_dev->name);
  141 +
  142 + return 0;
  143 +
  144 +fail_mfd:
  145 + free_irq(ec_dev->irq, ec_dev);
  146 +fail_irq:
  147 + kfree(ec_dev->dout);
  148 +fail_dout:
  149 + kfree(ec_dev->din);
  150 +fail_din:
  151 + return err;
  152 +}
  153 +
  154 +int cros_ec_remove(struct cros_ec_device *ec_dev)
  155 +{
  156 + mfd_remove_devices(ec_dev->dev);
  157 + free_irq(ec_dev->irq, ec_dev);
  158 + kfree(ec_dev->dout);
  159 + kfree(ec_dev->din);
  160 +
  161 + return 0;
  162 +}
  163 +
  164 +#ifdef CONFIG_PM_SLEEP
  165 +int cros_ec_suspend(struct cros_ec_device *ec_dev)
  166 +{
  167 + struct device *dev = ec_dev->dev;
  168 +
  169 + if (device_may_wakeup(dev))
  170 + ec_dev->wake_enabled = !enable_irq_wake(ec_dev->irq);
  171 +
  172 + disable_irq(ec_dev->irq);
  173 + ec_dev->was_wake_device = ec_dev->wake_enabled;
  174 +
  175 + return 0;
  176 +}
  177 +
  178 +int cros_ec_resume(struct cros_ec_device *ec_dev)
  179 +{
  180 + enable_irq(ec_dev->irq);
  181 +
  182 + if (ec_dev->wake_enabled) {
  183 + disable_irq_wake(ec_dev->irq);
  184 + ec_dev->wake_enabled = 0;
  185 + }
  186 +
  187 + return 0;
  188 +}
  189 +#endif
include/linux/mfd/cros_ec.h
  1 +/*
  2 + * ChromeOS EC multi-function device
  3 + *
  4 + * Copyright (C) 2012 Google, Inc
  5 + *
  6 + * This software is licensed under the terms of the GNU General Public
  7 + * License version 2, as published by the Free Software Foundation, and
  8 + * may be copied, distributed, and modified under those terms.
  9 + *
  10 + * This program is distributed in the hope that it will be useful,
  11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13 + * GNU General Public License for more details.
  14 + */
  15 +
  16 +#ifndef __LINUX_MFD_CROS_EC_H
  17 +#define __LINUX_MFD_CROS_EC_H
  18 +
  19 +#include <linux/mfd/cros_ec_commands.h>
  20 +
  21 +/*
  22 + * Command interface between EC and AP, for LPC, I2C and SPI interfaces.
  23 + */
  24 +enum {
  25 + EC_MSG_TX_HEADER_BYTES = 3,
  26 + EC_MSG_TX_TRAILER_BYTES = 1,
  27 + EC_MSG_TX_PROTO_BYTES = EC_MSG_TX_HEADER_BYTES +
  28 + EC_MSG_TX_TRAILER_BYTES,
  29 + EC_MSG_RX_PROTO_BYTES = 3,
  30 +
  31 + /* Max length of messages */
  32 + EC_MSG_BYTES = EC_HOST_PARAM_SIZE + EC_MSG_TX_PROTO_BYTES,
  33 +
  34 +};
  35 +
  36 +/**
  37 + * struct cros_ec_msg - A message sent to the EC, and its reply
  38 + *
  39 + * @version: Command version number (often 0)
  40 + * @cmd: Command to send (EC_CMD_...)
  41 + * @out_buf: Outgoing payload (to EC)
  42 + * @outlen: Outgoing length
  43 + * @in_buf: Incoming payload (from EC)
  44 + * @in_len: Incoming length
  45 + */
  46 +struct cros_ec_msg {
  47 + u8 version;
  48 + u8 cmd;
  49 + uint8_t *out_buf;
  50 + int out_len;
  51 + uint8_t *in_buf;
  52 + int in_len;
  53 +};
  54 +
  55 +/**
  56 + * struct cros_ec_device - Information about a ChromeOS EC device
  57 + *
  58 + * @name: Name of this EC interface
  59 + * @priv: Private data
  60 + * @irq: Interrupt to use
  61 + * @din: input buffer (from EC)
  62 + * @dout: output buffer (to EC)
  63 + * \note
  64 + * These two buffers will always be dword-aligned and include enough
  65 + * space for up to 7 word-alignment bytes also, so we can ensure that
  66 + * the body of the message is always dword-aligned (64-bit).
  67 + *
  68 + * We use this alignment to keep ARM and x86 happy. Probably word
  69 + * alignment would be OK, there might be a small performance advantage
  70 + * to using dword.
  71 + * @din_size: size of din buffer
  72 + * @dout_size: size of dout buffer
  73 + * @command_send: send a command
  74 + * @command_recv: receive a command
  75 + * @ec_name: name of EC device (e.g. 'chromeos-ec')
  76 + * @phys_name: name of physical comms layer (e.g. 'i2c-4')
  77 + * @parent: pointer to parent device (e.g. i2c or spi device)
  78 + * @dev: Device pointer
  79 + * dev_lock: Lock to prevent concurrent access
  80 + * @wake_enabled: true if this device can wake the system from sleep
  81 + * @was_wake_device: true if this device was set to wake the system from
  82 + * sleep at the last suspend
  83 + * @event_notifier: interrupt event notifier for transport devices
  84 + */
  85 +struct cros_ec_device {
  86 + const char *name;
  87 + void *priv;
  88 + int irq;
  89 + uint8_t *din;
  90 + uint8_t *dout;
  91 + int din_size;
  92 + int dout_size;
  93 + int (*command_send)(struct cros_ec_device *ec,
  94 + uint16_t cmd, void *out_buf, int out_len);
  95 + int (*command_recv)(struct cros_ec_device *ec,
  96 + uint16_t cmd, void *in_buf, int in_len);
  97 + int (*command_sendrecv)(struct cros_ec_device *ec,
  98 + uint16_t cmd, void *out_buf, int out_len,
  99 + void *in_buf, int in_len);
  100 + int (*command_xfer)(struct cros_ec_device *ec,
  101 + struct cros_ec_msg *msg);
  102 +
  103 + const char *ec_name;
  104 + const char *phys_name;
  105 + struct device *parent;
  106 +
  107 + /* These are --private-- fields - do not assign */
  108 + struct device *dev;
  109 + struct mutex dev_lock;
  110 + bool wake_enabled;
  111 + bool was_wake_device;
  112 + struct blocking_notifier_head event_notifier;
  113 +};
  114 +
  115 +/**
  116 + * cros_ec_suspend - Handle a suspend operation for the ChromeOS EC device
  117 + *
  118 + * This can be called by drivers to handle a suspend event.
  119 + *
  120 + * ec_dev: Device to suspend
  121 + * @return 0 if ok, -ve on error
  122 + */
  123 +int cros_ec_suspend(struct cros_ec_device *ec_dev);
  124 +
  125 +/**
  126 + * cros_ec_resume - Handle a resume operation for the ChromeOS EC device
  127 + *
  128 + * This can be called by drivers to handle a resume event.
  129 + *
  130 + * @ec_dev: Device to resume
  131 + * @return 0 if ok, -ve on error
  132 + */
  133 +int cros_ec_resume(struct cros_ec_device *ec_dev);
  134 +
  135 +/**
  136 + * cros_ec_prepare_tx - Prepare an outgoing message in the output buffer
  137 + *
  138 + * This is intended to be used by all ChromeOS EC drivers, but at present
  139 + * only SPI uses it. Once LPC uses the same protocol it can start using it.
  140 + * I2C could use it now, with a refactor of the existing code.
  141 + *
  142 + * @ec_dev: Device to register
  143 + * @msg: Message to write
  144 + */
  145 +int cros_ec_prepare_tx(struct cros_ec_device *ec_dev,
  146 + struct cros_ec_msg *msg);
  147 +
  148 +/**
  149 + * cros_ec_remove - Remove a ChromeOS EC
  150 + *
  151 + * Call this to deregister a ChromeOS EC. After this you should call
  152 + * cros_ec_free().
  153 + *
  154 + * @ec_dev: Device to register
  155 + * @return 0 if ok, -ve on error
  156 + */
  157 +int cros_ec_remove(struct cros_ec_device *ec_dev);
  158 +
  159 +/**
  160 + * cros_ec_register - Register a new ChromeOS EC, using the provided info
  161 + *
  162 + * Before calling this, allocate a pointer to a new device and then fill
  163 + * in all the fields up to the --private-- marker.
  164 + *
  165 + * @ec_dev: Device to register
  166 + * @return 0 if ok, -ve on error
  167 + */
  168 +int cros_ec_register(struct cros_ec_device *ec_dev);
  169 +
  170 +#endif /* __LINUX_MFD_CROS_EC_H */