aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/mfd/cros-ec.txt56
-rw-r--r--drivers/mfd/Kconfig8
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/cros_ec.c189
-rw-r--r--include/linux/mfd/cros_ec.h170
5 files changed, 424 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/mfd/cros-ec.txt b/Documentation/devicetree/bindings/mfd/cros-ec.txt
new file mode 100644
index 000000000000..e0e59c58a1f9
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/cros-ec.txt
@@ -0,0 +1,56 @@
1ChromeOS Embedded Controller
2
3Google's ChromeOS EC is a Cortex-M device which talks to the AP and
4implements various function such as keyboard and battery charging.
5
6The EC can be connect through various means (I2C, SPI, LPC) and the
7compatible string used depends on the inteface. Each connection method has
8its own driver which connects to the top level interface-agnostic EC driver.
9Other Linux driver (such as cros-ec-keyb for the matrix keyboard) connect to
10the top-level driver.
11
12Required properties (I2C):
13- compatible: "google,cros-ec-i2c"
14- reg: I2C slave address
15
16Required properties (SPI):
17- compatible: "google,cros-ec-spi"
18- reg: SPI chip select
19
20Required properties (LPC):
21- compatible: "google,cros-ec-lpc"
22- reg: List of (IO address, size) pairs defining the interface uses
23
24
25Example for I2C:
26
27i2c@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
37Example for SPI:
38
39spi@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
56Example for LPC is not supplied as it is not yet implemented.
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index c346941a2515..a8bafb560196 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -21,6 +21,14 @@ config MFD_88PM860X
21 select individual components like voltage regulators, RTC and 21 select individual components like voltage regulators, RTC and
22 battery-charger under the corresponding menus. 22 battery-charger under the corresponding menus.
23 23
24config 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
24config MFD_88PM800 32config MFD_88PM800
25 tristate "Support Marvell 88PM800" 33 tristate "Support Marvell 88PM800"
26 depends on I2C=y && GENERIC_HARDIRQS 34 depends on I2C=y && GENERIC_HARDIRQS
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index b90409c23664..967767d3d759 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_MFD_88PM800) += 88pm800.o 88pm80x.o
8obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o 8obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o
9obj-$(CONFIG_MFD_SM501) += sm501.o 9obj-$(CONFIG_MFD_SM501) += sm501.o
10obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o 10obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o
11obj-$(CONFIG_MFD_CROS_EC) += cros_ec.o
11 12
12rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o 13rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o
13obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o 14obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o
diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c
new file mode 100644
index 000000000000..ac824ccab52a
--- /dev/null
+++ b/drivers/mfd/cros_ec.c
@@ -0,0 +1,189 @@
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
26int 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
45static 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
61static 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
67static 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
73static 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
85static struct mfd_cell cros_devs[] = {
86 {
87 .name = "cros-ec-keyb",
88 .id = 1,
89 .of_compatible = "google,cros-ec-keyb",
90 },
91};
92
93int 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
144fail_mfd:
145 free_irq(ec_dev->irq, ec_dev);
146fail_irq:
147 kfree(ec_dev->dout);
148fail_dout:
149 kfree(ec_dev->din);
150fail_din:
151 return err;
152}
153
154int 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
165int 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
178int 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
diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h
new file mode 100644
index 000000000000..032af7fc5b2e
--- /dev/null
+++ b/include/linux/mfd/cros_ec.h
@@ -0,0 +1,170 @@
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 */
24enum {
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 */
46struct 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 */
85struct 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 */
123int 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 */
133int 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 */
145int 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 */
157int 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 */
168int cros_ec_register(struct cros_ec_device *ec_dev);
169
170#endif /* __LINUX_MFD_CROS_EC_H */