aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/i2c/busses/i2c-cros-ec-tunnel.c51
-rw-r--r--drivers/input/keyboard/cros_ec_keyb.c13
-rw-r--r--drivers/mfd/cros_ec.c19
-rw-r--r--drivers/platform/chrome/Kconfig26
-rw-r--r--drivers/platform/chrome/Makefile3
-rw-r--r--drivers/platform/chrome/chromeos_laptop.c35
-rw-r--r--drivers/platform/chrome/cros_ec_dev.c274
-rw-r--r--drivers/platform/chrome/cros_ec_dev.h53
-rw-r--r--drivers/platform/chrome/cros_ec_lightbar.c367
-rw-r--r--drivers/platform/chrome/cros_ec_lpc.c319
-rw-r--r--drivers/platform/chrome/cros_ec_sysfs.c271
11 files changed, 1365 insertions, 66 deletions
diff --git a/drivers/i2c/busses/i2c-cros-ec-tunnel.c b/drivers/i2c/busses/i2c-cros-ec-tunnel.c
index 875c22ae5400..fa8dedd8c3a2 100644
--- a/drivers/i2c/busses/i2c-cros-ec-tunnel.c
+++ b/drivers/i2c/busses/i2c-cros-ec-tunnel.c
@@ -182,72 +182,41 @@ static int ec_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg i2c_msgs[],
182 const u16 bus_num = bus->remote_bus; 182 const u16 bus_num = bus->remote_bus;
183 int request_len; 183 int request_len;
184 int response_len; 184 int response_len;
185 u8 *request = NULL;
186 u8 *response = NULL;
187 int result; 185 int result;
188 struct cros_ec_command msg; 186 struct cros_ec_command msg = { };
189 187
190 request_len = ec_i2c_count_message(i2c_msgs, num); 188 request_len = ec_i2c_count_message(i2c_msgs, num);
191 if (request_len < 0) { 189 if (request_len < 0) {
192 dev_warn(dev, "Error constructing message %d\n", request_len); 190 dev_warn(dev, "Error constructing message %d\n", request_len);
193 result = request_len; 191 return request_len;
194 goto exit;
195 } 192 }
193
196 response_len = ec_i2c_count_response(i2c_msgs, num); 194 response_len = ec_i2c_count_response(i2c_msgs, num);
197 if (response_len < 0) { 195 if (response_len < 0) {
198 /* Unexpected; no errors should come when NULL response */ 196 /* Unexpected; no errors should come when NULL response */
199 dev_warn(dev, "Error preparing response %d\n", response_len); 197 dev_warn(dev, "Error preparing response %d\n", response_len);
200 result = response_len; 198 return response_len;
201 goto exit;
202 }
203
204 if (request_len <= ARRAY_SIZE(bus->request_buf)) {
205 request = bus->request_buf;
206 } else {
207 request = kzalloc(request_len, GFP_KERNEL);
208 if (request == NULL) {
209 result = -ENOMEM;
210 goto exit;
211 }
212 }
213 if (response_len <= ARRAY_SIZE(bus->response_buf)) {
214 response = bus->response_buf;
215 } else {
216 response = kzalloc(response_len, GFP_KERNEL);
217 if (response == NULL) {
218 result = -ENOMEM;
219 goto exit;
220 }
221 } 199 }
222 200
223 result = ec_i2c_construct_message(request, i2c_msgs, num, bus_num); 201 result = ec_i2c_construct_message(msg.outdata, i2c_msgs, num, bus_num);
224 if (result) 202 if (result)
225 goto exit; 203 return result;
226 204
227 msg.version = 0; 205 msg.version = 0;
228 msg.command = EC_CMD_I2C_PASSTHRU; 206 msg.command = EC_CMD_I2C_PASSTHRU;
229 msg.outdata = request;
230 msg.outsize = request_len; 207 msg.outsize = request_len;
231 msg.indata = response;
232 msg.insize = response_len; 208 msg.insize = response_len;
233 209
234 result = cros_ec_cmd_xfer(bus->ec, &msg); 210 result = cros_ec_cmd_xfer(bus->ec, &msg);
235 if (result < 0) 211 if (result < 0)
236 goto exit; 212 return result;
237 213
238 result = ec_i2c_parse_response(response, i2c_msgs, &num); 214 result = ec_i2c_parse_response(msg.indata, i2c_msgs, &num);
239 if (result < 0) 215 if (result < 0)
240 goto exit; 216 return result;
241 217
242 /* Indicate success by saying how many messages were sent */ 218 /* Indicate success by saying how many messages were sent */
243 result = num; 219 return num;
244exit:
245 if (request != bus->request_buf)
246 kfree(request);
247 if (response != bus->response_buf)
248 kfree(response);
249
250 return result;
251} 220}
252 221
253static u32 ec_i2c_functionality(struct i2c_adapter *adap) 222static u32 ec_i2c_functionality(struct i2c_adapter *adap)
diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c
index 64b9b59ad4cb..b50c5b8b8a4d 100644
--- a/drivers/input/keyboard/cros_ec_keyb.c
+++ b/drivers/input/keyboard/cros_ec_keyb.c
@@ -148,16 +148,19 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev,
148 148
149static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state) 149static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state)
150{ 150{
151 int ret;
151 struct cros_ec_command msg = { 152 struct cros_ec_command msg = {
152 .version = 0,
153 .command = EC_CMD_MKBP_STATE, 153 .command = EC_CMD_MKBP_STATE,
154 .outdata = NULL,
155 .outsize = 0,
156 .indata = kb_state,
157 .insize = ckdev->cols, 154 .insize = ckdev->cols,
158 }; 155 };
159 156
160 return cros_ec_cmd_xfer(ckdev->ec, &msg); 157 ret = cros_ec_cmd_xfer(ckdev->ec, &msg);
158 if (ret < 0)
159 return ret;
160
161 memcpy(kb_state, msg.indata, ckdev->cols);
162
163 return 0;
161} 164}
162 165
163static irqreturn_t cros_ec_keyb_irq(int irq, void *data) 166static irqreturn_t cros_ec_keyb_irq(int irq, void *data)
diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c
index fc0c81ef04ff..c4aecc6f8373 100644
--- a/drivers/mfd/cros_ec.c
+++ b/drivers/mfd/cros_ec.c
@@ -74,15 +74,11 @@ int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev,
74 ret = ec_dev->cmd_xfer(ec_dev, msg); 74 ret = ec_dev->cmd_xfer(ec_dev, msg);
75 if (msg->result == EC_RES_IN_PROGRESS) { 75 if (msg->result == EC_RES_IN_PROGRESS) {
76 int i; 76 int i;
77 struct cros_ec_command status_msg; 77 struct cros_ec_command status_msg = { };
78 struct ec_response_get_comms_status status; 78 struct ec_response_get_comms_status *status;
79 79
80 status_msg.version = 0;
81 status_msg.command = EC_CMD_GET_COMMS_STATUS; 80 status_msg.command = EC_CMD_GET_COMMS_STATUS;
82 status_msg.outdata = NULL; 81 status_msg.insize = sizeof(*status);
83 status_msg.outsize = 0;
84 status_msg.indata = (uint8_t *)&status;
85 status_msg.insize = sizeof(status);
86 82
87 /* 83 /*
88 * Query the EC's status until it's no longer busy or 84 * Query the EC's status until it's no longer busy or
@@ -98,7 +94,10 @@ int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev,
98 msg->result = status_msg.result; 94 msg->result = status_msg.result;
99 if (status_msg.result != EC_RES_SUCCESS) 95 if (status_msg.result != EC_RES_SUCCESS)
100 break; 96 break;
101 if (!(status.flags & EC_COMMS_STATUS_PROCESSING)) 97
98 status = (struct ec_response_get_comms_status *)
99 status_msg.indata;
100 if (!(status->flags & EC_COMMS_STATUS_PROCESSING))
102 break; 101 break;
103 } 102 }
104 } 103 }
@@ -119,6 +118,10 @@ static const struct mfd_cell cros_devs[] = {
119 .id = 2, 118 .id = 2,
120 .of_compatible = "google,cros-ec-i2c-tunnel", 119 .of_compatible = "google,cros-ec-i2c-tunnel",
121 }, 120 },
121 {
122 .name = "cros-ec-ctl",
123 .id = 3,
124 },
122}; 125};
123 126
124int cros_ec_register(struct cros_ec_device *ec_dev) 127int cros_ec_register(struct cros_ec_device *ec_dev)
diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig
index 440ed776efd4..2a6531a5fde8 100644
--- a/drivers/platform/chrome/Kconfig
+++ b/drivers/platform/chrome/Kconfig
@@ -4,7 +4,7 @@
4 4
5menuconfig CHROME_PLATFORMS 5menuconfig CHROME_PLATFORMS
6 bool "Platform support for Chrome hardware" 6 bool "Platform support for Chrome hardware"
7 depends on X86 7 depends on X86 || ARM
8 ---help--- 8 ---help---
9 Say Y here to get to see options for platform support for 9 Say Y here to get to see options for platform support for
10 various Chromebooks and Chromeboxes. This option alone does 10 various Chromebooks and Chromeboxes. This option alone does
@@ -16,8 +16,7 @@ if CHROME_PLATFORMS
16 16
17config CHROMEOS_LAPTOP 17config CHROMEOS_LAPTOP
18 tristate "Chrome OS Laptop" 18 tristate "Chrome OS Laptop"
19 depends on I2C 19 depends on I2C && DMI && X86
20 depends on DMI
21 ---help--- 20 ---help---
22 This driver instantiates i2c and smbus devices such as 21 This driver instantiates i2c and smbus devices such as
23 light sensors and touchpads. 22 light sensors and touchpads.
@@ -27,6 +26,7 @@ config CHROMEOS_LAPTOP
27 26
28config CHROMEOS_PSTORE 27config CHROMEOS_PSTORE
29 tristate "Chrome OS pstore support" 28 tristate "Chrome OS pstore support"
29 depends on X86
30 ---help--- 30 ---help---
31 This module instantiates the persistent storage on x86 ChromeOS 31 This module instantiates the persistent storage on x86 ChromeOS
32 devices. It can be used to store away console logs and crash 32 devices. It can be used to store away console logs and crash
@@ -38,5 +38,25 @@ config CHROMEOS_PSTORE
38 If you have a supported Chromebook, choose Y or M here. 38 If you have a supported Chromebook, choose Y or M here.
39 The module will be called chromeos_pstore. 39 The module will be called chromeos_pstore.
40 40
41config CROS_EC_CHARDEV
42 tristate "Chrome OS Embedded Controller userspace device interface"
43 depends on MFD_CROS_EC
44 ---help---
45 This driver adds support to talk with the ChromeOS EC from userspace.
46
47 If you have a supported Chromebook, choose Y or M here.
48 The module will be called cros_ec_dev.
49
50config CROS_EC_LPC
51 tristate "ChromeOS Embedded Controller (LPC)"
52 depends on MFD_CROS_EC && (X86 || COMPILE_TEST)
53 help
54 If you say Y here, you get support for talking to the ChromeOS EC
55 over an LPC bus. This uses a simple byte-level protocol with a
56 checksum. This is used for userspace access only. The kernel
57 typically has its own communication methods.
58
59 To compile this driver as a module, choose M here: the
60 module will be called cros_ec_lpc.
41 61
42endif # CHROMEOS_PLATFORMS 62endif # CHROMEOS_PLATFORMS
diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
index 2b860ca7450f..bd8d8601e875 100644
--- a/drivers/platform/chrome/Makefile
+++ b/drivers/platform/chrome/Makefile
@@ -1,3 +1,6 @@
1 1
2obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o 2obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o
3obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o 3obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o
4cros_ec_devs-objs := cros_ec_dev.o cros_ec_sysfs.o cros_ec_lightbar.o
5obj-$(CONFIG_CROS_EC_CHARDEV) += cros_ec_devs.o
6obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpc.o
diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c
index b84fdd6b629b..a04019ab9feb 100644
--- a/drivers/platform/chrome/chromeos_laptop.c
+++ b/drivers/platform/chrome/chromeos_laptop.c
@@ -133,12 +133,13 @@ static struct i2c_client *__add_probed_i2c_device(
133 const char *name, 133 const char *name,
134 int bus, 134 int bus,
135 struct i2c_board_info *info, 135 struct i2c_board_info *info,
136 const unsigned short *addrs) 136 const unsigned short *alt_addr_list)
137{ 137{
138 const struct dmi_device *dmi_dev; 138 const struct dmi_device *dmi_dev;
139 const struct dmi_dev_onboard *dev_data; 139 const struct dmi_dev_onboard *dev_data;
140 struct i2c_adapter *adapter; 140 struct i2c_adapter *adapter;
141 struct i2c_client *client; 141 struct i2c_client *client = NULL;
142 const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END };
142 143
143 if (bus < 0) 144 if (bus < 0)
144 return NULL; 145 return NULL;
@@ -169,8 +170,28 @@ static struct i2c_client *__add_probed_i2c_device(
169 return NULL; 170 return NULL;
170 } 171 }
171 172
172 /* add the i2c device */ 173 /*
173 client = i2c_new_probed_device(adapter, info, addrs, NULL); 174 * Add the i2c device. If we can't detect it at the primary
175 * address we scan secondary addresses. In any case the client
176 * structure gets assigned primary address.
177 */
178 client = i2c_new_probed_device(adapter, info, addr_list, NULL);
179 if (!client && alt_addr_list) {
180 struct i2c_board_info dummy_info = {
181 I2C_BOARD_INFO("dummy", info->addr),
182 };
183 struct i2c_client *dummy;
184
185 dummy = i2c_new_probed_device(adapter, &dummy_info,
186 alt_addr_list, NULL);
187 if (dummy) {
188 pr_debug("%s %d-%02x is probed at %02x\n",
189 __func__, bus, info->addr, dummy->addr);
190 i2c_unregister_device(dummy);
191 client = i2c_new_device(adapter, info);
192 }
193 }
194
174 if (!client) 195 if (!client)
175 pr_notice("%s failed to register device %d-%02x\n", 196 pr_notice("%s failed to register device %d-%02x\n",
176 __func__, bus, info->addr); 197 __func__, bus, info->addr);
@@ -254,12 +275,10 @@ static struct i2c_client *add_i2c_device(const char *name,
254 enum i2c_adapter_type type, 275 enum i2c_adapter_type type,
255 struct i2c_board_info *info) 276 struct i2c_board_info *info)
256{ 277{
257 const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END };
258
259 return __add_probed_i2c_device(name, 278 return __add_probed_i2c_device(name,
260 find_i2c_adapter_num(type), 279 find_i2c_adapter_num(type),
261 info, 280 info,
262 addr_list); 281 NULL);
263} 282}
264 283
265static int setup_cyapa_tp(enum i2c_adapter_type type) 284static int setup_cyapa_tp(enum i2c_adapter_type type)
@@ -275,7 +294,6 @@ static int setup_cyapa_tp(enum i2c_adapter_type type)
275static int setup_atmel_224s_tp(enum i2c_adapter_type type) 294static int setup_atmel_224s_tp(enum i2c_adapter_type type)
276{ 295{
277 const unsigned short addr_list[] = { ATMEL_TP_I2C_BL_ADDR, 296 const unsigned short addr_list[] = { ATMEL_TP_I2C_BL_ADDR,
278 ATMEL_TP_I2C_ADDR,
279 I2C_CLIENT_END }; 297 I2C_CLIENT_END };
280 if (tp) 298 if (tp)
281 return 0; 299 return 0;
@@ -289,7 +307,6 @@ static int setup_atmel_224s_tp(enum i2c_adapter_type type)
289static int setup_atmel_1664s_ts(enum i2c_adapter_type type) 307static int setup_atmel_1664s_ts(enum i2c_adapter_type type)
290{ 308{
291 const unsigned short addr_list[] = { ATMEL_TS_I2C_BL_ADDR, 309 const unsigned short addr_list[] = { ATMEL_TS_I2C_BL_ADDR,
292 ATMEL_TS_I2C_ADDR,
293 I2C_CLIENT_END }; 310 I2C_CLIENT_END };
294 if (ts) 311 if (ts)
295 return 0; 312 return 0;
diff --git a/drivers/platform/chrome/cros_ec_dev.c b/drivers/platform/chrome/cros_ec_dev.c
new file mode 100644
index 000000000000..6090d0b2826f
--- /dev/null
+++ b/drivers/platform/chrome/cros_ec_dev.c
@@ -0,0 +1,274 @@
1/*
2 * cros_ec_dev - expose the Chrome OS Embedded Controller to user-space
3 *
4 * Copyright (C) 2014 Google, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <linux/fs.h>
21#include <linux/module.h>
22#include <linux/platform_device.h>
23#include <linux/uaccess.h>
24
25#include "cros_ec_dev.h"
26
27/* Device variables */
28#define CROS_MAX_DEV 128
29static struct class *cros_class;
30static int ec_major;
31
32/* Basic communication */
33static int ec_get_version(struct cros_ec_device *ec, char *str, int maxlen)
34{
35 struct ec_response_get_version *resp;
36 static const char * const current_image_name[] = {
37 "unknown", "read-only", "read-write", "invalid",
38 };
39 struct cros_ec_command msg = {
40 .version = 0,
41 .command = EC_CMD_GET_VERSION,
42 .outdata = { 0 },
43 .outsize = 0,
44 .indata = { 0 },
45 .insize = sizeof(*resp),
46 };
47 int ret;
48
49 ret = cros_ec_cmd_xfer(ec, &msg);
50 if (ret < 0)
51 return ret;
52
53 if (msg.result != EC_RES_SUCCESS) {
54 snprintf(str, maxlen,
55 "%s\nUnknown EC version: EC returned %d\n",
56 CROS_EC_DEV_VERSION, msg.result);
57 return 0;
58 }
59
60 resp = (struct ec_response_get_version *)msg.indata;
61 if (resp->current_image >= ARRAY_SIZE(current_image_name))
62 resp->current_image = 3; /* invalid */
63
64 snprintf(str, maxlen, "%s\n%s\n%s\n%s\n", CROS_EC_DEV_VERSION,
65 resp->version_string_ro, resp->version_string_rw,
66 current_image_name[resp->current_image]);
67
68 return 0;
69}
70
71/* Device file ops */
72static int ec_device_open(struct inode *inode, struct file *filp)
73{
74 filp->private_data = container_of(inode->i_cdev,
75 struct cros_ec_device, cdev);
76 return 0;
77}
78
79static int ec_device_release(struct inode *inode, struct file *filp)
80{
81 return 0;
82}
83
84static ssize_t ec_device_read(struct file *filp, char __user *buffer,
85 size_t length, loff_t *offset)
86{
87 struct cros_ec_device *ec = filp->private_data;
88 char msg[sizeof(struct ec_response_get_version) +
89 sizeof(CROS_EC_DEV_VERSION)];
90 size_t count;
91 int ret;
92
93 if (*offset != 0)
94 return 0;
95
96 ret = ec_get_version(ec, msg, sizeof(msg));
97 if (ret)
98 return ret;
99
100 count = min(length, strlen(msg));
101
102 if (copy_to_user(buffer, msg, count))
103 return -EFAULT;
104
105 *offset = count;
106 return count;
107}
108
109/* Ioctls */
110static long ec_device_ioctl_xcmd(struct cros_ec_device *ec, void __user *arg)
111{
112 long ret;
113 struct cros_ec_command s_cmd = { };
114
115 if (copy_from_user(&s_cmd, arg, sizeof(s_cmd)))
116 return -EFAULT;
117
118 ret = cros_ec_cmd_xfer(ec, &s_cmd);
119 /* Only copy data to userland if data was received. */
120 if (ret < 0)
121 return ret;
122
123 if (copy_to_user(arg, &s_cmd, sizeof(s_cmd)))
124 return -EFAULT;
125
126 return 0;
127}
128
129static long ec_device_ioctl_readmem(struct cros_ec_device *ec, void __user *arg)
130{
131 struct cros_ec_readmem s_mem = { };
132 long num;
133
134 /* Not every platform supports direct reads */
135 if (!ec->cmd_readmem)
136 return -ENOTTY;
137
138 if (copy_from_user(&s_mem, arg, sizeof(s_mem)))
139 return -EFAULT;
140
141 num = ec->cmd_readmem(ec, s_mem.offset, s_mem.bytes, s_mem.buffer);
142 if (num <= 0)
143 return num;
144
145 if (copy_to_user((void __user *)arg, &s_mem, sizeof(s_mem)))
146 return -EFAULT;
147
148 return 0;
149}
150
151static long ec_device_ioctl(struct file *filp, unsigned int cmd,
152 unsigned long arg)
153{
154 struct cros_ec_device *ec = filp->private_data;
155
156 if (_IOC_TYPE(cmd) != CROS_EC_DEV_IOC)
157 return -ENOTTY;
158
159 switch (cmd) {
160 case CROS_EC_DEV_IOCXCMD:
161 return ec_device_ioctl_xcmd(ec, (void __user *)arg);
162 case CROS_EC_DEV_IOCRDMEM:
163 return ec_device_ioctl_readmem(ec, (void __user *)arg);
164 }
165
166 return -ENOTTY;
167}
168
169/* Module initialization */
170static const struct file_operations fops = {
171 .open = ec_device_open,
172 .release = ec_device_release,
173 .read = ec_device_read,
174 .unlocked_ioctl = ec_device_ioctl,
175};
176
177static int ec_device_probe(struct platform_device *pdev)
178{
179 struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
180 int retval = -ENOTTY;
181 dev_t devno = MKDEV(ec_major, 0);
182
183 /* Instantiate it (and remember the EC) */
184 cdev_init(&ec->cdev, &fops);
185
186 retval = cdev_add(&ec->cdev, devno, 1);
187 if (retval) {
188 dev_err(&pdev->dev, ": failed to add character device\n");
189 return retval;
190 }
191
192 ec->vdev = device_create(cros_class, NULL, devno, ec,
193 CROS_EC_DEV_NAME);
194 if (IS_ERR(ec->vdev)) {
195 retval = PTR_ERR(ec->vdev);
196 dev_err(&pdev->dev, ": failed to create device\n");
197 cdev_del(&ec->cdev);
198 return retval;
199 }
200
201 /* Initialize extra interfaces */
202 ec_dev_sysfs_init(ec);
203 ec_dev_lightbar_init(ec);
204
205 return 0;
206}
207
208static int ec_device_remove(struct platform_device *pdev)
209{
210 struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
211
212 ec_dev_lightbar_remove(ec);
213 ec_dev_sysfs_remove(ec);
214 device_destroy(cros_class, MKDEV(ec_major, 0));
215 cdev_del(&ec->cdev);
216 return 0;
217}
218
219static struct platform_driver cros_ec_dev_driver = {
220 .driver = {
221 .name = "cros-ec-ctl",
222 },
223 .probe = ec_device_probe,
224 .remove = ec_device_remove,
225};
226
227static int __init cros_ec_dev_init(void)
228{
229 int ret;
230 dev_t dev = 0;
231
232 cros_class = class_create(THIS_MODULE, "chromeos");
233 if (IS_ERR(cros_class)) {
234 pr_err(CROS_EC_DEV_NAME ": failed to register device class\n");
235 return PTR_ERR(cros_class);
236 }
237
238 /* Get a range of minor numbers (starting with 0) to work with */
239 ret = alloc_chrdev_region(&dev, 0, CROS_MAX_DEV, CROS_EC_DEV_NAME);
240 if (ret < 0) {
241 pr_err(CROS_EC_DEV_NAME ": alloc_chrdev_region() failed\n");
242 goto failed_chrdevreg;
243 }
244 ec_major = MAJOR(dev);
245
246 /* Register the driver */
247 ret = platform_driver_register(&cros_ec_dev_driver);
248 if (ret < 0) {
249 pr_warn(CROS_EC_DEV_NAME ": can't register driver: %d\n", ret);
250 goto failed_devreg;
251 }
252 return 0;
253
254failed_devreg:
255 unregister_chrdev_region(MKDEV(ec_major, 0), CROS_MAX_DEV);
256failed_chrdevreg:
257 class_destroy(cros_class);
258 return ret;
259}
260
261static void __exit cros_ec_dev_exit(void)
262{
263 platform_driver_unregister(&cros_ec_dev_driver);
264 unregister_chrdev(ec_major, CROS_EC_DEV_NAME);
265 class_destroy(cros_class);
266}
267
268module_init(cros_ec_dev_init);
269module_exit(cros_ec_dev_exit);
270
271MODULE_AUTHOR("Bill Richardson <wfrichar@chromium.org>");
272MODULE_DESCRIPTION("Userspace interface to the Chrome OS Embedded Controller");
273MODULE_VERSION("1.0");
274MODULE_LICENSE("GPL");
diff --git a/drivers/platform/chrome/cros_ec_dev.h b/drivers/platform/chrome/cros_ec_dev.h
new file mode 100644
index 000000000000..45d67f7e518c
--- /dev/null
+++ b/drivers/platform/chrome/cros_ec_dev.h
@@ -0,0 +1,53 @@
1/*
2 * cros_ec_dev - expose the Chrome OS Embedded Controller to userspace
3 *
4 * Copyright (C) 2014 Google, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#ifndef _CROS_EC_DEV_H_
21#define _CROS_EC_DEV_H_
22
23#include <linux/ioctl.h>
24#include <linux/types.h>
25#include <linux/mfd/cros_ec.h>
26
27#define CROS_EC_DEV_NAME "cros_ec"
28#define CROS_EC_DEV_VERSION "1.0.0"
29
30/*
31 * @offset: within EC_LPC_ADDR_MEMMAP region
32 * @bytes: number of bytes to read. zero means "read a string" (including '\0')
33 * (at most only EC_MEMMAP_SIZE bytes can be read)
34 * @buffer: where to store the result
35 * ioctl returns the number of bytes read, negative on error
36 */
37struct cros_ec_readmem {
38 uint32_t offset;
39 uint32_t bytes;
40 uint8_t buffer[EC_MEMMAP_SIZE];
41};
42
43#define CROS_EC_DEV_IOC 0xEC
44#define CROS_EC_DEV_IOCXCMD _IOWR(CROS_EC_DEV_IOC, 0, struct cros_ec_command)
45#define CROS_EC_DEV_IOCRDMEM _IOWR(CROS_EC_DEV_IOC, 1, struct cros_ec_readmem)
46
47void ec_dev_sysfs_init(struct cros_ec_device *);
48void ec_dev_sysfs_remove(struct cros_ec_device *);
49
50void ec_dev_lightbar_init(struct cros_ec_device *);
51void ec_dev_lightbar_remove(struct cros_ec_device *);
52
53#endif /* _CROS_EC_DEV_H_ */
diff --git a/drivers/platform/chrome/cros_ec_lightbar.c b/drivers/platform/chrome/cros_ec_lightbar.c
new file mode 100644
index 000000000000..b4ff47a9069a
--- /dev/null
+++ b/drivers/platform/chrome/cros_ec_lightbar.c
@@ -0,0 +1,367 @@
1/*
2 * cros_ec_lightbar - expose the Chromebook Pixel lightbar to userspace
3 *
4 * Copyright (C) 2014 Google, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#define pr_fmt(fmt) "cros_ec_lightbar: " fmt
21
22#include <linux/ctype.h>
23#include <linux/delay.h>
24#include <linux/device.h>
25#include <linux/fs.h>
26#include <linux/kobject.h>
27#include <linux/mfd/cros_ec.h>
28#include <linux/mfd/cros_ec_commands.h>
29#include <linux/module.h>
30#include <linux/platform_device.h>
31#include <linux/sched.h>
32#include <linux/types.h>
33#include <linux/uaccess.h>
34
35#include "cros_ec_dev.h"
36
37/* Rate-limit the lightbar interface to prevent DoS. */
38static unsigned long lb_interval_jiffies = 50 * HZ / 1000;
39
40static ssize_t interval_msec_show(struct device *dev,
41 struct device_attribute *attr, char *buf)
42{
43 unsigned long msec = lb_interval_jiffies * 1000 / HZ;
44
45 return scnprintf(buf, PAGE_SIZE, "%lu\n", msec);
46}
47
48static ssize_t interval_msec_store(struct device *dev,
49 struct device_attribute *attr,
50 const char *buf, size_t count)
51{
52 unsigned long msec;
53
54 if (kstrtoul(buf, 0, &msec))
55 return -EINVAL;
56
57 lb_interval_jiffies = msec * HZ / 1000;
58
59 return count;
60}
61
62static DEFINE_MUTEX(lb_mutex);
63/* Return 0 if able to throttle correctly, error otherwise */
64static int lb_throttle(void)
65{
66 static unsigned long last_access;
67 unsigned long now, next_timeslot;
68 long delay;
69 int ret = 0;
70
71 mutex_lock(&lb_mutex);
72
73 now = jiffies;
74 next_timeslot = last_access + lb_interval_jiffies;
75
76 if (time_before(now, next_timeslot)) {
77 delay = (long)(next_timeslot) - (long)now;
78 set_current_state(TASK_INTERRUPTIBLE);
79 if (schedule_timeout(delay) > 0) {
80 /* interrupted - just abort */
81 ret = -EINTR;
82 goto out;
83 }
84 now = jiffies;
85 }
86
87 last_access = now;
88out:
89 mutex_unlock(&lb_mutex);
90
91 return ret;
92}
93
94#define INIT_MSG(P, R) { \
95 .command = EC_CMD_LIGHTBAR_CMD, \
96 .outsize = sizeof(*P), \
97 .insize = sizeof(*R), \
98 }
99
100static int get_lightbar_version(struct cros_ec_device *ec,
101 uint32_t *ver_ptr, uint32_t *flg_ptr)
102{
103 struct ec_params_lightbar *param;
104 struct ec_response_lightbar *resp;
105 struct cros_ec_command msg = INIT_MSG(param, resp);
106 int ret;
107
108 param = (struct ec_params_lightbar *)msg.outdata;
109 param->cmd = LIGHTBAR_CMD_VERSION;
110 ret = cros_ec_cmd_xfer(ec, &msg);
111 if (ret < 0)
112 return 0;
113
114 switch (msg.result) {
115 case EC_RES_INVALID_PARAM:
116 /* Pixel had no version command. */
117 if (ver_ptr)
118 *ver_ptr = 0;
119 if (flg_ptr)
120 *flg_ptr = 0;
121 return 1;
122
123 case EC_RES_SUCCESS:
124 resp = (struct ec_response_lightbar *)msg.indata;
125
126 /* Future devices w/lightbars should implement this command */
127 if (ver_ptr)
128 *ver_ptr = resp->version.num;
129 if (flg_ptr)
130 *flg_ptr = resp->version.flags;
131 return 1;
132 }
133
134 /* Anything else (ie, EC_RES_INVALID_COMMAND) - no lightbar */
135 return 0;
136}
137
138static ssize_t version_show(struct device *dev,
139 struct device_attribute *attr, char *buf)
140{
141 uint32_t version, flags;
142 struct cros_ec_device *ec = dev_get_drvdata(dev);
143 int ret;
144
145 ret = lb_throttle();
146 if (ret)
147 return ret;
148
149 /* This should always succeed, because we check during init. */
150 if (!get_lightbar_version(ec, &version, &flags))
151 return -EIO;
152
153 return scnprintf(buf, PAGE_SIZE, "%d %d\n", version, flags);
154}
155
156static ssize_t brightness_store(struct device *dev,
157 struct device_attribute *attr,
158 const char *buf, size_t count)
159{
160 struct ec_params_lightbar *param;
161 struct ec_response_lightbar *resp;
162 struct cros_ec_command msg = INIT_MSG(param, resp);
163 int ret;
164 unsigned int val;
165 struct cros_ec_device *ec = dev_get_drvdata(dev);
166
167 if (kstrtouint(buf, 0, &val))
168 return -EINVAL;
169
170 param = (struct ec_params_lightbar *)msg.outdata;
171 param->cmd = LIGHTBAR_CMD_BRIGHTNESS;
172 param->brightness.num = val;
173 ret = lb_throttle();
174 if (ret)
175 return ret;
176
177 ret = cros_ec_cmd_xfer(ec, &msg);
178 if (ret < 0)
179 return ret;
180
181 if (msg.result != EC_RES_SUCCESS)
182 return -EINVAL;
183
184 return count;
185}
186
187
188/*
189 * We expect numbers, and we'll keep reading until we find them, skipping over
190 * any whitespace (sysfs guarantees that the input is null-terminated). Every
191 * four numbers are sent to the lightbar as <LED,R,G,B>. We fail at the first
192 * parsing error, if we don't parse any numbers, or if we have numbers left
193 * over.
194 */
195static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr,
196 const char *buf, size_t count)
197{
198 struct ec_params_lightbar *param;
199 struct ec_response_lightbar *resp;
200 struct cros_ec_command msg = INIT_MSG(param, resp);
201 struct cros_ec_device *ec = dev_get_drvdata(dev);
202 unsigned int val[4];
203 int ret, i = 0, j = 0, ok = 0;
204
205 do {
206 /* Skip any whitespace */
207 while (*buf && isspace(*buf))
208 buf++;
209
210 if (!*buf)
211 break;
212
213 ret = sscanf(buf, "%i", &val[i++]);
214 if (ret == 0)
215 return -EINVAL;
216
217 if (i == 4) {
218 param = (struct ec_params_lightbar *)msg.outdata;
219 param->cmd = LIGHTBAR_CMD_RGB;
220 param->rgb.led = val[0];
221 param->rgb.red = val[1];
222 param->rgb.green = val[2];
223 param->rgb.blue = val[3];
224 /*
225 * Throttle only the first of every four transactions,
226 * so that the user can update all four LEDs at once.
227 */
228 if ((j++ % 4) == 0) {
229 ret = lb_throttle();
230 if (ret)
231 return ret;
232 }
233
234 ret = cros_ec_cmd_xfer(ec, &msg);
235 if (ret < 0)
236 return ret;
237
238 if (msg.result != EC_RES_SUCCESS)
239 return -EINVAL;
240
241 i = 0;
242 ok = 1;
243 }
244
245 /* Skip over the number we just read */
246 while (*buf && !isspace(*buf))
247 buf++;
248
249 } while (*buf);
250
251 return (ok && i == 0) ? count : -EINVAL;
252}
253
254static char const *seqname[] = {
255 "ERROR", "S5", "S3", "S0", "S5S3", "S3S0",
256 "S0S3", "S3S5", "STOP", "RUN", "PULSE", "TEST", "KONAMI",
257};
258
259static ssize_t sequence_show(struct device *dev,
260 struct device_attribute *attr, char *buf)
261{
262 struct ec_params_lightbar *param;
263 struct ec_response_lightbar *resp;
264 struct cros_ec_command msg = INIT_MSG(param, resp);
265 int ret;
266 struct cros_ec_device *ec = dev_get_drvdata(dev);
267
268 param = (struct ec_params_lightbar *)msg.outdata;
269 param->cmd = LIGHTBAR_CMD_GET_SEQ;
270 ret = lb_throttle();
271 if (ret)
272 return ret;
273
274 ret = cros_ec_cmd_xfer(ec, &msg);
275 if (ret < 0)
276 return ret;
277
278 if (msg.result != EC_RES_SUCCESS)
279 return scnprintf(buf, PAGE_SIZE,
280 "ERROR: EC returned %d\n", msg.result);
281
282 resp = (struct ec_response_lightbar *)msg.indata;
283 if (resp->get_seq.num >= ARRAY_SIZE(seqname))
284 return scnprintf(buf, PAGE_SIZE, "%d\n", resp->get_seq.num);
285 else
286 return scnprintf(buf, PAGE_SIZE, "%s\n",
287 seqname[resp->get_seq.num]);
288}
289
290static ssize_t sequence_store(struct device *dev, struct device_attribute *attr,
291 const char *buf, size_t count)
292{
293 struct ec_params_lightbar *param;
294 struct ec_response_lightbar *resp;
295 struct cros_ec_command msg = INIT_MSG(param, resp);
296 unsigned int num;
297 int ret, len;
298 struct cros_ec_device *ec = dev_get_drvdata(dev);
299
300 for (len = 0; len < count; len++)
301 if (!isalnum(buf[len]))
302 break;
303
304 for (num = 0; num < ARRAY_SIZE(seqname); num++)
305 if (!strncasecmp(seqname[num], buf, len))
306 break;
307
308 if (num >= ARRAY_SIZE(seqname)) {
309 ret = kstrtouint(buf, 0, &num);
310 if (ret)
311 return ret;
312 }
313
314 param = (struct ec_params_lightbar *)msg.outdata;
315 param->cmd = LIGHTBAR_CMD_SEQ;
316 param->seq.num = num;
317 ret = lb_throttle();
318 if (ret)
319 return ret;
320
321 ret = cros_ec_cmd_xfer(ec, &msg);
322 if (ret < 0)
323 return ret;
324
325 if (msg.result != EC_RES_SUCCESS)
326 return -EINVAL;
327
328 return count;
329}
330
331/* Module initialization */
332
333static DEVICE_ATTR_RW(interval_msec);
334static DEVICE_ATTR_RO(version);
335static DEVICE_ATTR_WO(brightness);
336static DEVICE_ATTR_WO(led_rgb);
337static DEVICE_ATTR_RW(sequence);
338static struct attribute *__lb_cmds_attrs[] = {
339 &dev_attr_interval_msec.attr,
340 &dev_attr_version.attr,
341 &dev_attr_brightness.attr,
342 &dev_attr_led_rgb.attr,
343 &dev_attr_sequence.attr,
344 NULL,
345};
346static struct attribute_group lb_cmds_attr_group = {
347 .name = "lightbar",
348 .attrs = __lb_cmds_attrs,
349};
350
351void ec_dev_lightbar_init(struct cros_ec_device *ec)
352{
353 int ret = 0;
354
355 /* Only instantiate this stuff if the EC has a lightbar */
356 if (!get_lightbar_version(ec, NULL, NULL))
357 return;
358
359 ret = sysfs_create_group(&ec->vdev->kobj, &lb_cmds_attr_group);
360 if (ret)
361 pr_warn("sysfs_create_group() failed: %d\n", ret);
362}
363
364void ec_dev_lightbar_remove(struct cros_ec_device *ec)
365{
366 sysfs_remove_group(&ec->vdev->kobj, &lb_cmds_attr_group);
367}
diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c
new file mode 100644
index 000000000000..8f9ac4d7bbd0
--- /dev/null
+++ b/drivers/platform/chrome/cros_ec_lpc.c
@@ -0,0 +1,319 @@
1/*
2 * cros_ec_lpc - LPC access to the Chrome OS Embedded Controller
3 *
4 * Copyright (C) 2012-2015 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 * This driver uses the Chrome OS EC byte-level message-based protocol for
16 * communicating the keyboard state (which keys are pressed) from a keyboard EC
17 * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing,
18 * but everything else (including deghosting) is done here. The main
19 * motivation for this is to keep the EC firmware as simple as possible, since
20 * it cannot be easily upgraded and EC flash/IRAM space is relatively
21 * expensive.
22 */
23
24#include <linux/dmi.h>
25#include <linux/delay.h>
26#include <linux/io.h>
27#include <linux/mfd/cros_ec.h>
28#include <linux/mfd/cros_ec_commands.h>
29#include <linux/module.h>
30#include <linux/platform_device.h>
31#include <linux/printk.h>
32
33#define DRV_NAME "cros_ec_lpc"
34
35static int ec_response_timed_out(void)
36{
37 unsigned long one_second = jiffies + HZ;
38
39 usleep_range(200, 300);
40 do {
41 if (!(inb(EC_LPC_ADDR_HOST_CMD) & EC_LPC_STATUS_BUSY_MASK))
42 return 0;
43 usleep_range(100, 200);
44 } while (time_before(jiffies, one_second));
45
46 return 1;
47}
48
49static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec,
50 struct cros_ec_command *msg)
51{
52 struct ec_lpc_host_args args;
53 int csum;
54 int i;
55 int ret = 0;
56
57 if (msg->outsize > EC_PROTO2_MAX_PARAM_SIZE ||
58 msg->insize > EC_PROTO2_MAX_PARAM_SIZE) {
59 dev_err(ec->dev,
60 "invalid buffer sizes (out %d, in %d)\n",
61 msg->outsize, msg->insize);
62 return -EINVAL;
63 }
64
65 /* Now actually send the command to the EC and get the result */
66 args.flags = EC_HOST_ARGS_FLAG_FROM_HOST;
67 args.command_version = msg->version;
68 args.data_size = msg->outsize;
69
70 /* Initialize checksum */
71 csum = msg->command + args.flags +
72 args.command_version + args.data_size;
73
74 /* Copy data and update checksum */
75 for (i = 0; i < msg->outsize; i++) {
76 outb(msg->outdata[i], EC_LPC_ADDR_HOST_PARAM + i);
77 csum += msg->outdata[i];
78 }
79
80 /* Finalize checksum and write args */
81 args.checksum = csum & 0xFF;
82 outb(args.flags, EC_LPC_ADDR_HOST_ARGS);
83 outb(args.command_version, EC_LPC_ADDR_HOST_ARGS + 1);
84 outb(args.data_size, EC_LPC_ADDR_HOST_ARGS + 2);
85 outb(args.checksum, EC_LPC_ADDR_HOST_ARGS + 3);
86
87 /* Here we go */
88 outb(msg->command, EC_LPC_ADDR_HOST_CMD);
89
90 if (ec_response_timed_out()) {
91 dev_warn(ec->dev, "EC responsed timed out\n");
92 ret = -EIO;
93 goto done;
94 }
95
96 /* Check result */
97 msg->result = inb(EC_LPC_ADDR_HOST_DATA);
98
99 switch (msg->result) {
100 case EC_RES_SUCCESS:
101 break;
102 case EC_RES_IN_PROGRESS:
103 ret = -EAGAIN;
104 dev_dbg(ec->dev, "command 0x%02x in progress\n",
105 msg->command);
106 goto done;
107 default:
108 dev_dbg(ec->dev, "command 0x%02x returned %d\n",
109 msg->command, msg->result);
110 }
111
112 /* Read back args */
113 args.flags = inb(EC_LPC_ADDR_HOST_ARGS);
114 args.command_version = inb(EC_LPC_ADDR_HOST_ARGS + 1);
115 args.data_size = inb(EC_LPC_ADDR_HOST_ARGS + 2);
116 args.checksum = inb(EC_LPC_ADDR_HOST_ARGS + 3);
117
118 if (args.data_size > msg->insize) {
119 dev_err(ec->dev,
120 "packet too long (%d bytes, expected %d)",
121 args.data_size, msg->insize);
122 ret = -ENOSPC;
123 goto done;
124 }
125
126 /* Start calculating response checksum */
127 csum = msg->command + args.flags +
128 args.command_version + args.data_size;
129
130 /* Read response and update checksum */
131 for (i = 0; i < args.data_size; i++) {
132 msg->indata[i] = inb(EC_LPC_ADDR_HOST_PARAM + i);
133 csum += msg->indata[i];
134 }
135
136 /* Verify checksum */
137 if (args.checksum != (csum & 0xFF)) {
138 dev_err(ec->dev,
139 "bad packet checksum, expected %02x, got %02x\n",
140 args.checksum, csum & 0xFF);
141 ret = -EBADMSG;
142 goto done;
143 }
144
145 /* Return actual amount of data received */
146 ret = args.data_size;
147done:
148 return ret;
149}
150
151/* Returns num bytes read, or negative on error. Doesn't need locking. */
152static int cros_ec_lpc_readmem(struct cros_ec_device *ec, unsigned int offset,
153 unsigned int bytes, void *dest)
154{
155 int i = offset;
156 char *s = dest;
157 int cnt = 0;
158
159 if (offset >= EC_MEMMAP_SIZE - bytes)
160 return -EINVAL;
161
162 /* fixed length */
163 if (bytes) {
164 for (; cnt < bytes; i++, s++, cnt++)
165 *s = inb(EC_LPC_ADDR_MEMMAP + i);
166 return cnt;
167 }
168
169 /* string */
170 for (; i < EC_MEMMAP_SIZE; i++, s++) {
171 *s = inb(EC_LPC_ADDR_MEMMAP + i);
172 cnt++;
173 if (!*s)
174 break;
175 }
176
177 return cnt;
178}
179
180static int cros_ec_lpc_probe(struct platform_device *pdev)
181{
182 struct device *dev = &pdev->dev;
183 struct cros_ec_device *ec_dev;
184 int ret;
185
186 if (!devm_request_region(dev, EC_LPC_ADDR_MEMMAP, EC_MEMMAP_SIZE,
187 dev_name(dev))) {
188 dev_err(dev, "couldn't reserve memmap region\n");
189 return -EBUSY;
190 }
191
192 if ((inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID) != 'E') ||
193 (inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID + 1) != 'C')) {
194 dev_err(dev, "EC ID not detected\n");
195 return -ENODEV;
196 }
197
198 if (!devm_request_region(dev, EC_HOST_CMD_REGION0,
199 EC_HOST_CMD_REGION_SIZE, dev_name(dev))) {
200 dev_err(dev, "couldn't reserve region0\n");
201 return -EBUSY;
202 }
203 if (!devm_request_region(dev, EC_HOST_CMD_REGION1,
204 EC_HOST_CMD_REGION_SIZE, dev_name(dev))) {
205 dev_err(dev, "couldn't reserve region1\n");
206 return -EBUSY;
207 }
208
209 ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL);
210 if (!ec_dev)
211 return -ENOMEM;
212
213 platform_set_drvdata(pdev, ec_dev);
214 ec_dev->dev = dev;
215 ec_dev->ec_name = pdev->name;
216 ec_dev->phys_name = dev_name(dev);
217 ec_dev->parent = dev;
218 ec_dev->cmd_xfer = cros_ec_cmd_xfer_lpc;
219 ec_dev->cmd_readmem = cros_ec_lpc_readmem;
220
221 ret = cros_ec_register(ec_dev);
222 if (ret) {
223 dev_err(dev, "couldn't register ec_dev (%d)\n", ret);
224 return ret;
225 }
226
227 return 0;
228}
229
230static int cros_ec_lpc_remove(struct platform_device *pdev)
231{
232 struct cros_ec_device *ec_dev;
233
234 ec_dev = platform_get_drvdata(pdev);
235 cros_ec_remove(ec_dev);
236
237 return 0;
238}
239
240static struct dmi_system_id cros_ec_lpc_dmi_table[] __initdata = {
241 {
242 /*
243 * Today all Chromebooks/boxes ship with Google_* as version and
244 * coreboot as bios vendor. No other systems with this
245 * combination are known to date.
246 */
247 .matches = {
248 DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
249 DMI_MATCH(DMI_BIOS_VERSION, "Google_"),
250 },
251 },
252 {
253 /* x86-link, the Chromebook Pixel. */
254 .matches = {
255 DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
256 DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
257 },
258 },
259 {
260 /* x86-peppy, the Acer C720 Chromebook. */
261 .matches = {
262 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
263 DMI_MATCH(DMI_PRODUCT_NAME, "Peppy"),
264 },
265 },
266 { /* sentinel */ }
267};
268MODULE_DEVICE_TABLE(dmi, cros_ec_lpc_dmi_table);
269
270static struct platform_driver cros_ec_lpc_driver = {
271 .driver = {
272 .name = DRV_NAME,
273 },
274 .probe = cros_ec_lpc_probe,
275 .remove = cros_ec_lpc_remove,
276};
277
278static struct platform_device cros_ec_lpc_device = {
279 .name = DRV_NAME
280};
281
282static int __init cros_ec_lpc_init(void)
283{
284 int ret;
285
286 if (!dmi_check_system(cros_ec_lpc_dmi_table)) {
287 pr_err(DRV_NAME ": unsupported system.\n");
288 return -ENODEV;
289 }
290
291 /* Register the driver */
292 ret = platform_driver_register(&cros_ec_lpc_driver);
293 if (ret) {
294 pr_err(DRV_NAME ": can't register driver: %d\n", ret);
295 return ret;
296 }
297
298 /* Register the device, and it'll get hooked up automatically */
299 ret = platform_device_register(&cros_ec_lpc_device);
300 if (ret) {
301 pr_err(DRV_NAME ": can't register device: %d\n", ret);
302 platform_driver_unregister(&cros_ec_lpc_driver);
303 return ret;
304 }
305
306 return 0;
307}
308
309static void __exit cros_ec_lpc_exit(void)
310{
311 platform_device_unregister(&cros_ec_lpc_device);
312 platform_driver_unregister(&cros_ec_lpc_driver);
313}
314
315module_init(cros_ec_lpc_init);
316module_exit(cros_ec_lpc_exit);
317
318MODULE_LICENSE("GPL");
319MODULE_DESCRIPTION("ChromeOS EC LPC driver");
diff --git a/drivers/platform/chrome/cros_ec_sysfs.c b/drivers/platform/chrome/cros_ec_sysfs.c
new file mode 100644
index 000000000000..fb62ab6cc659
--- /dev/null
+++ b/drivers/platform/chrome/cros_ec_sysfs.c
@@ -0,0 +1,271 @@
1/*
2 * cros_ec_sysfs - expose the Chrome OS EC through sysfs
3 *
4 * Copyright (C) 2014 Google, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#define pr_fmt(fmt) "cros_ec_sysfs: " fmt
21
22#include <linux/ctype.h>
23#include <linux/delay.h>
24#include <linux/device.h>
25#include <linux/fs.h>
26#include <linux/kobject.h>
27#include <linux/mfd/cros_ec.h>
28#include <linux/mfd/cros_ec_commands.h>
29#include <linux/module.h>
30#include <linux/platform_device.h>
31#include <linux/printk.h>
32#include <linux/stat.h>
33#include <linux/types.h>
34#include <linux/uaccess.h>
35
36#include "cros_ec_dev.h"
37
38/* Accessor functions */
39
40static ssize_t show_ec_reboot(struct device *dev,
41 struct device_attribute *attr, char *buf)
42{
43 int count = 0;
44
45 count += scnprintf(buf + count, PAGE_SIZE - count,
46 "ro|rw|cancel|cold|disable-jump|hibernate");
47 count += scnprintf(buf + count, PAGE_SIZE - count,
48 " [at-shutdown]\n");
49 return count;
50}
51
52static ssize_t store_ec_reboot(struct device *dev,
53 struct device_attribute *attr,
54 const char *buf, size_t count)
55{
56 static const struct {
57 const char * const str;
58 uint8_t cmd;
59 uint8_t flags;
60 } words[] = {
61 {"cancel", EC_REBOOT_CANCEL, 0},
62 {"ro", EC_REBOOT_JUMP_RO, 0},
63 {"rw", EC_REBOOT_JUMP_RW, 0},
64 {"cold", EC_REBOOT_COLD, 0},
65 {"disable-jump", EC_REBOOT_DISABLE_JUMP, 0},
66 {"hibernate", EC_REBOOT_HIBERNATE, 0},
67 {"at-shutdown", -1, EC_REBOOT_FLAG_ON_AP_SHUTDOWN},
68 };
69 struct cros_ec_command msg = { 0 };
70 struct ec_params_reboot_ec *param =
71 (struct ec_params_reboot_ec *)msg.outdata;
72 int got_cmd = 0, offset = 0;
73 int i;
74 int ret;
75 struct cros_ec_device *ec = dev_get_drvdata(dev);
76
77 param->flags = 0;
78 while (1) {
79 /* Find word to start scanning */
80 while (buf[offset] && isspace(buf[offset]))
81 offset++;
82 if (!buf[offset])
83 break;
84
85 for (i = 0; i < ARRAY_SIZE(words); i++) {
86 if (!strncasecmp(words[i].str, buf+offset,
87 strlen(words[i].str))) {
88 if (words[i].flags) {
89 param->flags |= words[i].flags;
90 } else {
91 param->cmd = words[i].cmd;
92 got_cmd = 1;
93 }
94 break;
95 }
96 }
97
98 /* On to the next word, if any */
99 while (buf[offset] && !isspace(buf[offset]))
100 offset++;
101 }
102
103 if (!got_cmd)
104 return -EINVAL;
105
106 msg.command = EC_CMD_REBOOT_EC;
107 msg.outsize = sizeof(param);
108 ret = cros_ec_cmd_xfer(ec, &msg);
109 if (ret < 0)
110 return ret;
111 if (msg.result != EC_RES_SUCCESS) {
112 dev_dbg(ec->dev, "EC result %d\n", msg.result);
113 return -EINVAL;
114 }
115
116 return count;
117}
118
119static ssize_t show_ec_version(struct device *dev,
120 struct device_attribute *attr, char *buf)
121{
122 static const char * const image_names[] = {"unknown", "RO", "RW"};
123 struct ec_response_get_version *r_ver;
124 struct ec_response_get_chip_info *r_chip;
125 struct ec_response_board_version *r_board;
126 struct cros_ec_command msg = { 0 };
127 int ret;
128 int count = 0;
129 struct cros_ec_device *ec = dev_get_drvdata(dev);
130
131 /* Get versions. RW may change. */
132 msg.command = EC_CMD_GET_VERSION;
133 msg.insize = sizeof(*r_ver);
134 ret = cros_ec_cmd_xfer(ec, &msg);
135 if (ret < 0)
136 return ret;
137 if (msg.result != EC_RES_SUCCESS)
138 return scnprintf(buf, PAGE_SIZE,
139 "ERROR: EC returned %d\n", msg.result);
140
141 r_ver = (struct ec_response_get_version *)msg.indata;
142 /* Strings should be null-terminated, but let's be sure. */
143 r_ver->version_string_ro[sizeof(r_ver->version_string_ro) - 1] = '\0';
144 r_ver->version_string_rw[sizeof(r_ver->version_string_rw) - 1] = '\0';
145 count += scnprintf(buf + count, PAGE_SIZE - count,
146 "RO version: %s\n", r_ver->version_string_ro);
147 count += scnprintf(buf + count, PAGE_SIZE - count,
148 "RW version: %s\n", r_ver->version_string_rw);
149 count += scnprintf(buf + count, PAGE_SIZE - count,
150 "Firmware copy: %s\n",
151 (r_ver->current_image < ARRAY_SIZE(image_names) ?
152 image_names[r_ver->current_image] : "?"));
153
154 /* Get build info. */
155 msg.command = EC_CMD_GET_BUILD_INFO;
156 msg.insize = sizeof(msg.indata);
157 ret = cros_ec_cmd_xfer(ec, &msg);
158 if (ret < 0)
159 count += scnprintf(buf + count, PAGE_SIZE - count,
160 "Build info: XFER ERROR %d\n", ret);
161 else if (msg.result != EC_RES_SUCCESS)
162 count += scnprintf(buf + count, PAGE_SIZE - count,
163 "Build info: EC error %d\n", msg.result);
164 else {
165 msg.indata[sizeof(msg.indata) - 1] = '\0';
166 count += scnprintf(buf + count, PAGE_SIZE - count,
167 "Build info: %s\n", msg.indata);
168 }
169
170 /* Get chip info. */
171 msg.command = EC_CMD_GET_CHIP_INFO;
172 msg.insize = sizeof(*r_chip);
173 ret = cros_ec_cmd_xfer(ec, &msg);
174 if (ret < 0)
175 count += scnprintf(buf + count, PAGE_SIZE - count,
176 "Chip info: XFER ERROR %d\n", ret);
177 else if (msg.result != EC_RES_SUCCESS)
178 count += scnprintf(buf + count, PAGE_SIZE - count,
179 "Chip info: EC error %d\n", msg.result);
180 else {
181 r_chip = (struct ec_response_get_chip_info *)msg.indata;
182
183 r_chip->vendor[sizeof(r_chip->vendor) - 1] = '\0';
184 r_chip->name[sizeof(r_chip->name) - 1] = '\0';
185 r_chip->revision[sizeof(r_chip->revision) - 1] = '\0';
186 count += scnprintf(buf + count, PAGE_SIZE - count,
187 "Chip vendor: %s\n", r_chip->vendor);
188 count += scnprintf(buf + count, PAGE_SIZE - count,
189 "Chip name: %s\n", r_chip->name);
190 count += scnprintf(buf + count, PAGE_SIZE - count,
191 "Chip revision: %s\n", r_chip->revision);
192 }
193
194 /* Get board version */
195 msg.command = EC_CMD_GET_BOARD_VERSION;
196 msg.insize = sizeof(*r_board);
197 ret = cros_ec_cmd_xfer(ec, &msg);
198 if (ret < 0)
199 count += scnprintf(buf + count, PAGE_SIZE - count,
200 "Board version: XFER ERROR %d\n", ret);
201 else if (msg.result != EC_RES_SUCCESS)
202 count += scnprintf(buf + count, PAGE_SIZE - count,
203 "Board version: EC error %d\n", msg.result);
204 else {
205 r_board = (struct ec_response_board_version *)msg.indata;
206
207 count += scnprintf(buf + count, PAGE_SIZE - count,
208 "Board version: %d\n",
209 r_board->board_version);
210 }
211
212 return count;
213}
214
215static ssize_t show_ec_flashinfo(struct device *dev,
216 struct device_attribute *attr, char *buf)
217{
218 struct ec_response_flash_info *resp;
219 struct cros_ec_command msg = { 0 };
220 int ret;
221 struct cros_ec_device *ec = dev_get_drvdata(dev);
222
223 /* The flash info shouldn't ever change, but ask each time anyway. */
224 msg.command = EC_CMD_FLASH_INFO;
225 msg.insize = sizeof(*resp);
226 ret = cros_ec_cmd_xfer(ec, &msg);
227 if (ret < 0)
228 return ret;
229 if (msg.result != EC_RES_SUCCESS)
230 return scnprintf(buf, PAGE_SIZE,
231 "ERROR: EC returned %d\n", msg.result);
232
233 resp = (struct ec_response_flash_info *)msg.indata;
234
235 return scnprintf(buf, PAGE_SIZE,
236 "FlashSize %d\nWriteSize %d\n"
237 "EraseSize %d\nProtectSize %d\n",
238 resp->flash_size, resp->write_block_size,
239 resp->erase_block_size, resp->protect_block_size);
240}
241
242/* Module initialization */
243
244static DEVICE_ATTR(reboot, S_IWUSR | S_IRUGO, show_ec_reboot, store_ec_reboot);
245static DEVICE_ATTR(version, S_IRUGO, show_ec_version, NULL);
246static DEVICE_ATTR(flashinfo, S_IRUGO, show_ec_flashinfo, NULL);
247
248static struct attribute *__ec_attrs[] = {
249 &dev_attr_reboot.attr,
250 &dev_attr_version.attr,
251 &dev_attr_flashinfo.attr,
252 NULL,
253};
254
255static struct attribute_group ec_attr_group = {
256 .attrs = __ec_attrs,
257};
258
259void ec_dev_sysfs_init(struct cros_ec_device *ec)
260{
261 int error;
262
263 error = sysfs_create_group(&ec->vdev->kobj, &ec_attr_group);
264 if (error)
265 pr_warn("failed to create group: %d\n", error);
266}
267
268void ec_dev_sysfs_remove(struct cros_ec_device *ec)
269{
270 sysfs_remove_group(&ec->vdev->kobj, &ec_attr_group);
271}