aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorSimon Glass <sjg@chromium.org>2013-02-25 17:08:37 -0500
committerSamuel Ortiz <sameo@linux.intel.com>2013-04-05 05:20:13 -0400
commit4ab6174e8cdb007cf500e484bdf454b8d14d524a (patch)
tree765eb600ad56a1f3271ad23aac795d91a35bd181 /drivers
parentdeaf39efbc0829f26ae0b8fbe5de820588982f72 (diff)
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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mfd/Kconfig8
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/cros_ec.c189
3 files changed, 198 insertions, 0 deletions
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