diff options
-rw-r--r-- | drivers/platform/chrome/Makefile | 3 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_lpc.c | 88 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_lpc_reg.c | 64 | ||||
-rw-r--r-- | include/linux/mfd/cros_ec_lpc_reg.h | 47 |
4 files changed, 151 insertions, 51 deletions
diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile index 3870afeefcf4..61182fd8f597 100644 --- a/drivers/platform/chrome/Makefile +++ b/drivers/platform/chrome/Makefile | |||
@@ -5,6 +5,7 @@ cros_ec_devs-objs := cros_ec_dev.o cros_ec_sysfs.o \ | |||
5 | cros_ec_lightbar.o cros_ec_vbc.o \ | 5 | cros_ec_lightbar.o cros_ec_vbc.o \ |
6 | cros_ec_debugfs.o | 6 | cros_ec_debugfs.o |
7 | obj-$(CONFIG_CROS_EC_CHARDEV) += cros_ec_devs.o | 7 | obj-$(CONFIG_CROS_EC_CHARDEV) += cros_ec_devs.o |
8 | obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpc.o | 8 | cros_ec_lpcs-objs := cros_ec_lpc.o cros_ec_lpc_reg.o |
9 | obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpcs.o | ||
9 | obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o | 10 | obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o |
10 | obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT) += cros_kbd_led_backlight.o | 11 | obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT) += cros_kbd_led_backlight.o |
diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c index f9a245465fd0..6a782a695eb6 100644 --- a/drivers/platform/chrome/cros_ec_lpc.c +++ b/drivers/platform/chrome/cros_ec_lpc.c | |||
@@ -26,19 +26,22 @@ | |||
26 | #include <linux/io.h> | 26 | #include <linux/io.h> |
27 | #include <linux/mfd/cros_ec.h> | 27 | #include <linux/mfd/cros_ec.h> |
28 | #include <linux/mfd/cros_ec_commands.h> | 28 | #include <linux/mfd/cros_ec_commands.h> |
29 | #include <linux/mfd/cros_ec_lpc_reg.h> | ||
29 | #include <linux/module.h> | 30 | #include <linux/module.h> |
30 | #include <linux/platform_device.h> | 31 | #include <linux/platform_device.h> |
31 | #include <linux/printk.h> | 32 | #include <linux/printk.h> |
32 | 33 | ||
33 | #define DRV_NAME "cros_ec_lpc" | 34 | #define DRV_NAME "cros_ec_lpcs" |
34 | 35 | ||
35 | static int ec_response_timed_out(void) | 36 | static int ec_response_timed_out(void) |
36 | { | 37 | { |
37 | unsigned long one_second = jiffies + HZ; | 38 | unsigned long one_second = jiffies + HZ; |
39 | u8 data; | ||
38 | 40 | ||
39 | usleep_range(200, 300); | 41 | usleep_range(200, 300); |
40 | do { | 42 | do { |
41 | if (!(inb(EC_LPC_ADDR_HOST_CMD) & EC_LPC_STATUS_BUSY_MASK)) | 43 | if (!(cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_CMD, 1, &data) & |
44 | EC_LPC_STATUS_BUSY_MASK)) | ||
42 | return 0; | 45 | return 0; |
43 | usleep_range(100, 200); | 46 | usleep_range(100, 200); |
44 | } while (time_before(jiffies, one_second)); | 47 | } while (time_before(jiffies, one_second)); |
@@ -51,21 +54,20 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec, | |||
51 | { | 54 | { |
52 | struct ec_host_request *request; | 55 | struct ec_host_request *request; |
53 | struct ec_host_response response; | 56 | struct ec_host_response response; |
54 | u8 sum = 0; | 57 | u8 sum; |
55 | int i; | ||
56 | int ret = 0; | 58 | int ret = 0; |
57 | u8 *dout; | 59 | u8 *dout; |
58 | 60 | ||
59 | ret = cros_ec_prepare_tx(ec, msg); | 61 | ret = cros_ec_prepare_tx(ec, msg); |
60 | 62 | ||
61 | /* Write buffer */ | 63 | /* Write buffer */ |
62 | for (i = 0; i < ret; i++) | 64 | cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_PACKET, ret, ec->dout); |
63 | outb(ec->dout[i], EC_LPC_ADDR_HOST_PACKET + i); | ||
64 | 65 | ||
65 | request = (struct ec_host_request *)ec->dout; | 66 | request = (struct ec_host_request *)ec->dout; |
66 | 67 | ||
67 | /* Here we go */ | 68 | /* Here we go */ |
68 | outb(EC_COMMAND_PROTOCOL_3, EC_LPC_ADDR_HOST_CMD); | 69 | sum = EC_COMMAND_PROTOCOL_3; |
70 | cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_CMD, 1, &sum); | ||
69 | 71 | ||
70 | if (ec_response_timed_out()) { | 72 | if (ec_response_timed_out()) { |
71 | dev_warn(ec->dev, "EC responsed timed out\n"); | 73 | dev_warn(ec->dev, "EC responsed timed out\n"); |
@@ -74,17 +76,15 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec, | |||
74 | } | 76 | } |
75 | 77 | ||
76 | /* Check result */ | 78 | /* Check result */ |
77 | msg->result = inb(EC_LPC_ADDR_HOST_DATA); | 79 | msg->result = cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_DATA, 1, &sum); |
78 | ret = cros_ec_check_result(ec, msg); | 80 | ret = cros_ec_check_result(ec, msg); |
79 | if (ret) | 81 | if (ret) |
80 | goto done; | 82 | goto done; |
81 | 83 | ||
82 | /* Read back response */ | 84 | /* Read back response */ |
83 | dout = (u8 *)&response; | 85 | dout = (u8 *)&response; |
84 | for (i = 0; i < sizeof(response); i++) { | 86 | sum = cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_PACKET, sizeof(response), |
85 | dout[i] = inb(EC_LPC_ADDR_HOST_PACKET + i); | 87 | dout); |
86 | sum += dout[i]; | ||
87 | } | ||
88 | 88 | ||
89 | msg->result = response.result; | 89 | msg->result = response.result; |
90 | 90 | ||
@@ -97,11 +97,9 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec, | |||
97 | } | 97 | } |
98 | 98 | ||
99 | /* Read response and process checksum */ | 99 | /* Read response and process checksum */ |
100 | for (i = 0; i < response.data_len; i++) { | 100 | sum += cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_PACKET + |
101 | msg->data[i] = | 101 | sizeof(response), response.data_len, |
102 | inb(EC_LPC_ADDR_HOST_PACKET + sizeof(response) + i); | 102 | msg->data); |
103 | sum += msg->data[i]; | ||
104 | } | ||
105 | 103 | ||
106 | if (sum) { | 104 | if (sum) { |
107 | dev_err(ec->dev, | 105 | dev_err(ec->dev, |
@@ -121,8 +119,7 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, | |||
121 | struct cros_ec_command *msg) | 119 | struct cros_ec_command *msg) |
122 | { | 120 | { |
123 | struct ec_lpc_host_args args; | 121 | struct ec_lpc_host_args args; |
124 | int csum; | 122 | u8 sum; |
125 | int i; | ||
126 | int ret = 0; | 123 | int ret = 0; |
127 | 124 | ||
128 | if (msg->outsize > EC_PROTO2_MAX_PARAM_SIZE || | 125 | if (msg->outsize > EC_PROTO2_MAX_PARAM_SIZE || |
@@ -139,24 +136,20 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, | |||
139 | args.data_size = msg->outsize; | 136 | args.data_size = msg->outsize; |
140 | 137 | ||
141 | /* Initialize checksum */ | 138 | /* Initialize checksum */ |
142 | csum = msg->command + args.flags + | 139 | sum = msg->command + args.flags + args.command_version + args.data_size; |
143 | args.command_version + args.data_size; | ||
144 | 140 | ||
145 | /* Copy data and update checksum */ | 141 | /* Copy data and update checksum */ |
146 | for (i = 0; i < msg->outsize; i++) { | 142 | sum += cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_PARAM, msg->outsize, |
147 | outb(msg->data[i], EC_LPC_ADDR_HOST_PARAM + i); | 143 | msg->data); |
148 | csum += msg->data[i]; | ||
149 | } | ||
150 | 144 | ||
151 | /* Finalize checksum and write args */ | 145 | /* Finalize checksum and write args */ |
152 | args.checksum = csum & 0xFF; | 146 | args.checksum = sum; |
153 | outb(args.flags, EC_LPC_ADDR_HOST_ARGS); | 147 | cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_ARGS, sizeof(args), |
154 | outb(args.command_version, EC_LPC_ADDR_HOST_ARGS + 1); | 148 | (u8 *)&args); |
155 | outb(args.data_size, EC_LPC_ADDR_HOST_ARGS + 2); | ||
156 | outb(args.checksum, EC_LPC_ADDR_HOST_ARGS + 3); | ||
157 | 149 | ||
158 | /* Here we go */ | 150 | /* Here we go */ |
159 | outb(msg->command, EC_LPC_ADDR_HOST_CMD); | 151 | sum = msg->command; |
152 | cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_CMD, 1, &sum); | ||
160 | 153 | ||
161 | if (ec_response_timed_out()) { | 154 | if (ec_response_timed_out()) { |
162 | dev_warn(ec->dev, "EC responsed timed out\n"); | 155 | dev_warn(ec->dev, "EC responsed timed out\n"); |
@@ -165,16 +158,14 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, | |||
165 | } | 158 | } |
166 | 159 | ||
167 | /* Check result */ | 160 | /* Check result */ |
168 | msg->result = inb(EC_LPC_ADDR_HOST_DATA); | 161 | msg->result = cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_DATA, 1, &sum); |
169 | ret = cros_ec_check_result(ec, msg); | 162 | ret = cros_ec_check_result(ec, msg); |
170 | if (ret) | 163 | if (ret) |
171 | goto done; | 164 | goto done; |
172 | 165 | ||
173 | /* Read back args */ | 166 | /* Read back args */ |
174 | args.flags = inb(EC_LPC_ADDR_HOST_ARGS); | 167 | cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_ARGS, sizeof(args), |
175 | args.command_version = inb(EC_LPC_ADDR_HOST_ARGS + 1); | 168 | (u8 *)&args); |
176 | args.data_size = inb(EC_LPC_ADDR_HOST_ARGS + 2); | ||
177 | args.checksum = inb(EC_LPC_ADDR_HOST_ARGS + 3); | ||
178 | 169 | ||
179 | if (args.data_size > msg->insize) { | 170 | if (args.data_size > msg->insize) { |
180 | dev_err(ec->dev, | 171 | dev_err(ec->dev, |
@@ -185,20 +176,17 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, | |||
185 | } | 176 | } |
186 | 177 | ||
187 | /* Start calculating response checksum */ | 178 | /* Start calculating response checksum */ |
188 | csum = msg->command + args.flags + | 179 | sum = msg->command + args.flags + args.command_version + args.data_size; |
189 | args.command_version + args.data_size; | ||
190 | 180 | ||
191 | /* Read response and update checksum */ | 181 | /* Read response and update checksum */ |
192 | for (i = 0; i < args.data_size; i++) { | 182 | sum += cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_PARAM, args.data_size, |
193 | msg->data[i] = inb(EC_LPC_ADDR_HOST_PARAM + i); | 183 | msg->data); |
194 | csum += msg->data[i]; | ||
195 | } | ||
196 | 184 | ||
197 | /* Verify checksum */ | 185 | /* Verify checksum */ |
198 | if (args.checksum != (csum & 0xFF)) { | 186 | if (args.checksum != sum) { |
199 | dev_err(ec->dev, | 187 | dev_err(ec->dev, |
200 | "bad packet checksum, expected %02x, got %02x\n", | 188 | "bad packet checksum, expected %02x, got %02x\n", |
201 | args.checksum, csum & 0xFF); | 189 | args.checksum, sum); |
202 | ret = -EBADMSG; | 190 | ret = -EBADMSG; |
203 | goto done; | 191 | goto done; |
204 | } | 192 | } |
@@ -222,14 +210,13 @@ static int cros_ec_lpc_readmem(struct cros_ec_device *ec, unsigned int offset, | |||
222 | 210 | ||
223 | /* fixed length */ | 211 | /* fixed length */ |
224 | if (bytes) { | 212 | if (bytes) { |
225 | for (; cnt < bytes; i++, s++, cnt++) | 213 | cros_ec_lpc_read_bytes(EC_LPC_ADDR_MEMMAP + offset, bytes, s); |
226 | *s = inb(EC_LPC_ADDR_MEMMAP + i); | 214 | return bytes; |
227 | return cnt; | ||
228 | } | 215 | } |
229 | 216 | ||
230 | /* string */ | 217 | /* string */ |
231 | for (; i < EC_MEMMAP_SIZE; i++, s++) { | 218 | for (; i < EC_MEMMAP_SIZE; i++, s++) { |
232 | *s = inb(EC_LPC_ADDR_MEMMAP + i); | 219 | cros_ec_lpc_read_bytes(EC_LPC_ADDR_MEMMAP + i, 1, s); |
233 | cnt++; | 220 | cnt++; |
234 | if (!*s) | 221 | if (!*s) |
235 | break; | 222 | break; |
@@ -242,6 +229,7 @@ static int cros_ec_lpc_probe(struct platform_device *pdev) | |||
242 | { | 229 | { |
243 | struct device *dev = &pdev->dev; | 230 | struct device *dev = &pdev->dev; |
244 | struct cros_ec_device *ec_dev; | 231 | struct cros_ec_device *ec_dev; |
232 | u8 buf[2]; | ||
245 | int ret; | 233 | int ret; |
246 | 234 | ||
247 | if (!devm_request_region(dev, EC_LPC_ADDR_MEMMAP, EC_MEMMAP_SIZE, | 235 | if (!devm_request_region(dev, EC_LPC_ADDR_MEMMAP, EC_MEMMAP_SIZE, |
@@ -250,8 +238,8 @@ static int cros_ec_lpc_probe(struct platform_device *pdev) | |||
250 | return -EBUSY; | 238 | return -EBUSY; |
251 | } | 239 | } |
252 | 240 | ||
253 | if ((inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID) != 'E') || | 241 | cros_ec_lpc_read_bytes(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID, 2, buf); |
254 | (inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID + 1) != 'C')) { | 242 | if (buf[0] != 'E' || buf[1] != 'C') { |
255 | dev_err(dev, "EC ID not detected\n"); | 243 | dev_err(dev, "EC ID not detected\n"); |
256 | return -ENODEV; | 244 | return -ENODEV; |
257 | } | 245 | } |
diff --git a/drivers/platform/chrome/cros_ec_lpc_reg.c b/drivers/platform/chrome/cros_ec_lpc_reg.c new file mode 100644 index 000000000000..03c97813171e --- /dev/null +++ b/drivers/platform/chrome/cros_ec_lpc_reg.c | |||
@@ -0,0 +1,64 @@ | |||
1 | /* | ||
2 | * cros_ec_lpc_reg - LPC access to the Chrome OS Embedded Controller | ||
3 | * | ||
4 | * Copyright (C) 2016 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/io.h> | ||
25 | #include <linux/mfd/cros_ec.h> | ||
26 | #include <linux/mfd/cros_ec_commands.h> | ||
27 | |||
28 | static u8 lpc_read_bytes(unsigned int offset, unsigned int length, u8 *dest) | ||
29 | { | ||
30 | int i; | ||
31 | int sum = 0; | ||
32 | |||
33 | for (i = 0; i < length; ++i) { | ||
34 | dest[i] = inb(offset + i); | ||
35 | sum += dest[i]; | ||
36 | } | ||
37 | |||
38 | /* Return checksum of all bytes read */ | ||
39 | return sum; | ||
40 | } | ||
41 | |||
42 | static u8 lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg) | ||
43 | { | ||
44 | int i; | ||
45 | int sum = 0; | ||
46 | |||
47 | for (i = 0; i < length; ++i) { | ||
48 | outb(msg[i], offset + i); | ||
49 | sum += msg[i]; | ||
50 | } | ||
51 | |||
52 | /* Return checksum of all bytes written */ | ||
53 | return sum; | ||
54 | } | ||
55 | |||
56 | u8 cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length, u8 *dest) | ||
57 | { | ||
58 | return lpc_read_bytes(offset, length, dest); | ||
59 | } | ||
60 | |||
61 | u8 cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg) | ||
62 | { | ||
63 | return lpc_write_bytes(offset, length, msg); | ||
64 | } | ||
diff --git a/include/linux/mfd/cros_ec_lpc_reg.h b/include/linux/mfd/cros_ec_lpc_reg.h new file mode 100644 index 000000000000..4089bd5c8313 --- /dev/null +++ b/include/linux/mfd/cros_ec_lpc_reg.h | |||
@@ -0,0 +1,47 @@ | |||
1 | /* | ||
2 | * cros_ec_lpc_reg - LPC access to the Chrome OS Embedded Controller | ||
3 | * | ||
4 | * Copyright (C) 2016 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 | #ifndef __LINUX_MFD_CROS_EC_REG_H | ||
25 | #define __LINUX_MFD_CROS_EC_REG_H | ||
26 | |||
27 | /** | ||
28 | * cros_ec_lpc_read_bytes - Read bytes from a given LPC-mapped address. | ||
29 | * Returns 8-bit checksum of all bytes read. | ||
30 | * | ||
31 | * @offset: Base read address | ||
32 | * @length: Number of bytes to read | ||
33 | * @dest: Destination buffer | ||
34 | */ | ||
35 | u8 cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length, u8 *dest); | ||
36 | |||
37 | /** | ||
38 | * cros_ec_lpc_write_bytes - Write bytes to a given LPC-mapped address. | ||
39 | * Returns 8-bit checksum of all bytes written. | ||
40 | * | ||
41 | * @offset: Base write address | ||
42 | * @length: Number of bytes to write | ||
43 | * @msg: Write data buffer | ||
44 | */ | ||
45 | u8 cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg); | ||
46 | |||
47 | #endif /* __LINUX_MFD_CROS_EC_REG_H */ | ||