]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
c8d3328a HT |
2 | /* |
3 | * Chromium OS cros_ec driver - LPC interface | |
4 | * | |
5 | * Copyright (c) 2012 The Chromium OS Authors. | |
c8d3328a HT |
6 | */ |
7 | ||
8 | /* | |
9 | * The Matrix Keyboard Protocol driver handles talking to the keyboard | |
10 | * controller chip. Mostly this is for keyboard functions, but some other | |
11 | * things have slipped in, so we provide generic services to talk to the | |
12 | * KBC. | |
13 | */ | |
14 | ||
72a38e06 | 15 | #include <dm.h> |
c8d3328a HT |
16 | #include <command.h> |
17 | #include <cros_ec.h> | |
f7ae49fc | 18 | #include <log.h> |
03de305e | 19 | #include <time.h> |
c8d3328a HT |
20 | #include <asm/io.h> |
21 | ||
22 | #ifdef DEBUG_TRACE | |
23 | #define debug_trace(fmt, b...) debug(fmt, ##b) | |
24 | #else | |
25 | #define debug_trace(fmt, b...) | |
26 | #endif | |
27 | ||
4cb862fe SG |
28 | /* Timeout waiting for a flash erase command to complete */ |
29 | static const int CROS_EC_CMD_TIMEOUT_MS = 5000; | |
30 | ||
c8d3328a HT |
31 | static int wait_for_sync(struct cros_ec_dev *dev) |
32 | { | |
33 | unsigned long start; | |
34 | ||
35 | start = get_timer(0); | |
36 | while (inb(EC_LPC_ADDR_HOST_CMD) & EC_LPC_STATUS_BUSY_MASK) { | |
4cb862fe | 37 | if (get_timer(start) > CROS_EC_CMD_TIMEOUT_MS) { |
c8d3328a HT |
38 | debug("%s: Timeout waiting for CROS_EC sync\n", |
39 | __func__); | |
40 | return -1; | |
41 | } | |
42 | } | |
43 | ||
44 | return 0; | |
45 | } | |
46 | ||
d07b6e14 SG |
47 | int cros_ec_lpc_packet(struct udevice *udev, int out_bytes, int in_bytes) |
48 | { | |
49 | struct cros_ec_dev *dev = dev_get_uclass_priv(udev); | |
50 | uint8_t *d; | |
51 | int i; | |
52 | ||
53 | if (out_bytes > EC_LPC_HOST_PACKET_SIZE) | |
54 | return log_msg_ret("Cannot send that many bytes\n", -E2BIG); | |
55 | ||
56 | if (in_bytes > EC_LPC_HOST_PACKET_SIZE) | |
57 | return log_msg_ret("Cannot receive that many bytes\n", -E2BIG); | |
58 | ||
59 | if (wait_for_sync(dev)) | |
60 | return log_msg_ret("Timeout waiting ready\n", -ETIMEDOUT); | |
61 | ||
62 | /* Write data */ | |
63 | for (i = 0, d = (uint8_t *)dev->dout; i < out_bytes; i++, d++) | |
64 | outb(*d, EC_LPC_ADDR_HOST_PACKET + i); | |
65 | ||
66 | /* Start the command */ | |
67 | outb(EC_COMMAND_PROTOCOL_3, EC_LPC_ADDR_HOST_CMD); | |
68 | ||
69 | if (wait_for_sync(dev)) | |
70 | return log_msg_ret("Timeout waiting ready\n", -ETIMEDOUT); | |
71 | ||
72 | /* Read back args */ | |
73 | for (i = 0, d = dev->din; i < in_bytes; i++, d++) | |
74 | *d = inb(EC_LPC_ADDR_HOST_PACKET + i); | |
75 | ||
76 | return in_bytes; | |
77 | } | |
78 | ||
72a38e06 SG |
79 | int cros_ec_lpc_command(struct udevice *udev, uint8_t cmd, int cmd_version, |
80 | const uint8_t *dout, int dout_len, | |
81 | uint8_t **dinp, int din_len) | |
82 | { | |
83 | struct cros_ec_dev *dev = dev_get_uclass_priv(udev); | |
c8d3328a HT |
84 | const int cmd_addr = EC_LPC_ADDR_HOST_CMD; |
85 | const int data_addr = EC_LPC_ADDR_HOST_DATA; | |
86 | const int args_addr = EC_LPC_ADDR_HOST_ARGS; | |
87 | const int param_addr = EC_LPC_ADDR_HOST_PARAM; | |
88 | ||
89 | struct ec_lpc_host_args args; | |
90 | uint8_t *d; | |
91 | int csum; | |
92 | int i; | |
93 | ||
f1269925 | 94 | if (dout_len > EC_PROTO2_MAX_PARAM_SIZE) { |
c8d3328a HT |
95 | debug("%s: Cannot send %d bytes\n", __func__, dout_len); |
96 | return -1; | |
97 | } | |
98 | ||
99 | /* Fill in args */ | |
100 | args.flags = EC_HOST_ARGS_FLAG_FROM_HOST; | |
101 | args.command_version = cmd_version; | |
102 | args.data_size = dout_len; | |
103 | ||
104 | /* Calculate checksum */ | |
105 | csum = cmd + args.flags + args.command_version + args.data_size; | |
106 | for (i = 0, d = (uint8_t *)dout; i < dout_len; i++, d++) | |
107 | csum += *d; | |
108 | ||
109 | args.checksum = (uint8_t)csum; | |
110 | ||
111 | if (wait_for_sync(dev)) { | |
112 | debug("%s: Timeout waiting ready\n", __func__); | |
113 | return -1; | |
114 | } | |
115 | ||
116 | /* Write args */ | |
117 | for (i = 0, d = (uint8_t *)&args; i < sizeof(args); i++, d++) | |
118 | outb(*d, args_addr + i); | |
119 | ||
120 | /* Write data, if any */ | |
121 | debug_trace("cmd: %02x, ver: %02x", cmd, cmd_version); | |
122 | for (i = 0, d = (uint8_t *)dout; i < dout_len; i++, d++) { | |
123 | outb(*d, param_addr + i); | |
124 | debug_trace("%02x ", *d); | |
125 | } | |
126 | ||
127 | outb(cmd, cmd_addr); | |
128 | debug_trace("\n"); | |
129 | ||
130 | if (wait_for_sync(dev)) { | |
131 | debug("%s: Timeout waiting for response\n", __func__); | |
132 | return -1; | |
133 | } | |
134 | ||
135 | /* Check result */ | |
136 | i = inb(data_addr); | |
137 | if (i) { | |
138 | debug("%s: CROS_EC result code %d\n", __func__, i); | |
139 | return -i; | |
140 | } | |
141 | ||
142 | /* Read back args */ | |
143 | for (i = 0, d = (uint8_t *)&args; i < sizeof(args); i++, d++) | |
144 | *d = inb(args_addr + i); | |
145 | ||
146 | /* | |
147 | * If EC didn't modify args flags, then somehow we sent a new-style | |
148 | * command to an old EC, which means it would have read its params | |
149 | * from the wrong place. | |
150 | */ | |
151 | if (!(args.flags & EC_HOST_ARGS_FLAG_TO_HOST)) { | |
152 | debug("%s: CROS_EC protocol mismatch\n", __func__); | |
153 | return -EC_RES_INVALID_RESPONSE; | |
154 | } | |
155 | ||
156 | if (args.data_size > din_len) { | |
157 | debug("%s: CROS_EC returned too much data %d > %d\n", | |
158 | __func__, args.data_size, din_len); | |
159 | return -EC_RES_INVALID_RESPONSE; | |
160 | } | |
161 | ||
162 | /* Read data, if any */ | |
163 | for (i = 0, d = (uint8_t *)dev->din; i < args.data_size; i++, d++) { | |
164 | *d = inb(param_addr + i); | |
165 | debug_trace("%02x ", *d); | |
166 | } | |
167 | debug_trace("\n"); | |
168 | ||
169 | /* Verify checksum */ | |
170 | csum = cmd + args.flags + args.command_version + args.data_size; | |
171 | for (i = 0, d = (uint8_t *)dev->din; i < args.data_size; i++, d++) | |
172 | csum += *d; | |
173 | ||
174 | if (args.checksum != (uint8_t)csum) { | |
175 | debug("%s: CROS_EC response has invalid checksum\n", __func__); | |
176 | return -EC_RES_INVALID_CHECKSUM; | |
177 | } | |
178 | *dinp = dev->din; | |
179 | ||
180 | /* Return actual amount of data received */ | |
181 | return args.data_size; | |
182 | } | |
183 | ||
184 | /** | |
185 | * Initialize LPC protocol. | |
186 | * | |
187 | * @param dev CROS_EC device | |
188 | * @param blob Device tree blob | |
185f812c | 189 | * Return: 0 if ok, -1 on error |
c8d3328a HT |
190 | */ |
191 | int cros_ec_lpc_init(struct cros_ec_dev *dev, const void *blob) | |
192 | { | |
193 | int byte, i; | |
194 | ||
195 | /* See if we can find an EC at the other end */ | |
196 | byte = 0xff; | |
197 | byte &= inb(EC_LPC_ADDR_HOST_CMD); | |
198 | byte &= inb(EC_LPC_ADDR_HOST_DATA); | |
f1269925 | 199 | for (i = 0; i < EC_PROTO2_MAX_PARAM_SIZE && (byte == 0xff); i++) |
c8d3328a HT |
200 | byte &= inb(EC_LPC_ADDR_HOST_PARAM + i); |
201 | if (byte == 0xff) { | |
202 | debug("%s: CROS_EC device not found on LPC bus\n", | |
203 | __func__); | |
204 | return -1; | |
205 | } | |
206 | ||
207 | return 0; | |
208 | } | |
209 | ||
3a6c994f SG |
210 | /* Return the byte of EC switch states */ |
211 | static int cros_ec_lpc_get_switches(struct udevice *dev) | |
212 | { | |
213 | return inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SWITCHES); | |
214 | } | |
215 | ||
c8d3328a HT |
216 | /* |
217 | * Test if LPC command args are supported. | |
218 | * | |
219 | * The cheapest way to do this is by looking for the memory-mapped | |
220 | * flag. This is faster than sending a new-style 'hello' command and | |
221 | * seeing whether the EC sets the EC_HOST_ARGS_FLAG_FROM_HOST flag | |
222 | * in args when it responds. | |
223 | */ | |
72a38e06 | 224 | static int cros_ec_lpc_check_version(struct udevice *dev) |
c8d3328a HT |
225 | { |
226 | if (inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID) == 'E' && | |
227 | inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID + 1) | |
228 | == 'C' && | |
229 | (inb(EC_LPC_ADDR_MEMMAP + | |
230 | EC_MEMMAP_HOST_CMD_FLAGS) & | |
231 | EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED)) { | |
4ff9b461 | 232 | return 0; |
c8d3328a | 233 | } |
c8d3328a | 234 | |
4ff9b461 VB |
235 | printf("%s: ERROR: old EC interface not supported\n", __func__); |
236 | return -1; | |
c8d3328a | 237 | } |
72a38e06 | 238 | |
72a38e06 SG |
239 | static int cros_ec_probe(struct udevice *dev) |
240 | { | |
241 | return cros_ec_register(dev); | |
242 | } | |
243 | ||
244 | static struct dm_cros_ec_ops cros_ec_ops = { | |
d07b6e14 | 245 | .packet = cros_ec_lpc_packet, |
72a38e06 SG |
246 | .command = cros_ec_lpc_command, |
247 | .check_version = cros_ec_lpc_check_version, | |
3a6c994f | 248 | .get_switches = cros_ec_lpc_get_switches, |
72a38e06 SG |
249 | }; |
250 | ||
251 | static const struct udevice_id cros_ec_ids[] = { | |
3fbb7871 | 252 | { .compatible = "google,cros-ec-lpc" }, |
72a38e06 SG |
253 | { } |
254 | }; | |
255 | ||
ed0f868d SG |
256 | U_BOOT_DRIVER(google_cros_ec_lpc) = { |
257 | .name = "google_cros_ec_lpc", | |
72a38e06 SG |
258 | .id = UCLASS_CROS_EC, |
259 | .of_match = cros_ec_ids, | |
260 | .probe = cros_ec_probe, | |
261 | .ops = &cros_ec_ops, | |
262 | }; |