]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - drivers/misc/cros_ec_lpc.c
Merge branch 'master' of git://git.denx.de/u-boot-nand-flash
[karo-tx-uboot.git] / drivers / misc / cros_ec_lpc.c
1 /*
2  * Chromium OS cros_ec driver - LPC interface
3  *
4  * Copyright (c) 2012 The Chromium OS Authors.
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA
22  */
23
24 /*
25  * The Matrix Keyboard Protocol driver handles talking to the keyboard
26  * controller chip. Mostly this is for keyboard functions, but some other
27  * things have slipped in, so we provide generic services to talk to the
28  * KBC.
29  */
30
31 #include <common.h>
32 #include <command.h>
33 #include <cros_ec.h>
34 #include <asm/io.h>
35
36 #ifdef DEBUG_TRACE
37 #define debug_trace(fmt, b...)  debug(fmt, ##b)
38 #else
39 #define debug_trace(fmt, b...)
40 #endif
41
42 static int wait_for_sync(struct cros_ec_dev *dev)
43 {
44         unsigned long start;
45
46         start = get_timer(0);
47         while (inb(EC_LPC_ADDR_HOST_CMD) & EC_LPC_STATUS_BUSY_MASK) {
48                 if (get_timer(start) > 1000) {
49                         debug("%s: Timeout waiting for CROS_EC sync\n",
50                               __func__);
51                         return -1;
52                 }
53         }
54
55         return 0;
56 }
57
58 /**
59  * Send a command to a LPC CROS_EC device and return the reply.
60  *
61  * The device's internal input/output buffers are used.
62  *
63  * @param dev           CROS_EC device
64  * @param cmd           Command to send (EC_CMD_...)
65  * @param cmd_version   Version of command to send (EC_VER_...)
66  * @param dout          Output data (may be NULL If dout_len=0)
67  * @param dout_len      Size of output data in bytes
68  * @param dinp          Place to put pointer to response data
69  * @param din_len       Maximum size of response in bytes
70  * @return number of bytes in response, or -1 on error
71  */
72 static int old_lpc_command(struct cros_ec_dev *dev, uint8_t cmd,
73                      const uint8_t *dout, int dout_len,
74                      uint8_t **dinp, int din_len)
75 {
76         int ret, i;
77
78         if (dout_len > EC_OLD_PARAM_SIZE) {
79                 debug("%s: Cannot send %d bytes\n", __func__, dout_len);
80                 return -1;
81         }
82
83         if (din_len > EC_OLD_PARAM_SIZE) {
84                 debug("%s: Cannot receive %d bytes\n", __func__, din_len);
85                 return -1;
86         }
87
88         if (wait_for_sync(dev)) {
89                 debug("%s: Timeout waiting ready\n", __func__);
90                 return -1;
91         }
92
93         debug_trace("cmd: %02x, ", cmd);
94         for (i = 0; i < dout_len; i++) {
95                 debug_trace("%02x ", dout[i]);
96                 outb(dout[i], EC_LPC_ADDR_OLD_PARAM + i);
97         }
98         outb(cmd, EC_LPC_ADDR_HOST_CMD);
99         debug_trace("\n");
100
101         if (wait_for_sync(dev)) {
102                 debug("%s: Timeout waiting ready\n", __func__);
103                 return -1;
104         }
105
106         ret = inb(EC_LPC_ADDR_HOST_DATA);
107         if (ret) {
108                 debug("%s: CROS_EC result code %d\n", __func__, ret);
109                 return -ret;
110         }
111
112         debug_trace("resp: %02x, ", ret);
113         for (i = 0; i < din_len; i++) {
114                 dev->din[i] = inb(EC_LPC_ADDR_OLD_PARAM + i);
115                 debug_trace("%02x ", dev->din[i]);
116         }
117         debug_trace("\n");
118         *dinp = dev->din;
119
120         return din_len;
121 }
122
123 int cros_ec_lpc_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version,
124                      const uint8_t *dout, int dout_len,
125                      uint8_t **dinp, int din_len)
126 {
127         const int cmd_addr = EC_LPC_ADDR_HOST_CMD;
128         const int data_addr = EC_LPC_ADDR_HOST_DATA;
129         const int args_addr = EC_LPC_ADDR_HOST_ARGS;
130         const int param_addr = EC_LPC_ADDR_HOST_PARAM;
131
132         struct ec_lpc_host_args args;
133         uint8_t *d;
134         int csum;
135         int i;
136
137         /* Fall back to old-style command interface if args aren't supported */
138         if (!dev->cmd_version_is_supported)
139                 return old_lpc_command(dev, cmd, dout, dout_len, dinp,
140                                        din_len);
141
142         if (dout_len > EC_HOST_PARAM_SIZE) {
143                 debug("%s: Cannot send %d bytes\n", __func__, dout_len);
144                 return -1;
145         }
146
147         /* Fill in args */
148         args.flags = EC_HOST_ARGS_FLAG_FROM_HOST;
149         args.command_version = cmd_version;
150         args.data_size = dout_len;
151
152         /* Calculate checksum */
153         csum = cmd + args.flags + args.command_version + args.data_size;
154         for (i = 0, d = (uint8_t *)dout; i < dout_len; i++, d++)
155                 csum += *d;
156
157         args.checksum = (uint8_t)csum;
158
159         if (wait_for_sync(dev)) {
160                 debug("%s: Timeout waiting ready\n", __func__);
161                 return -1;
162         }
163
164         /* Write args */
165         for (i = 0, d = (uint8_t *)&args; i < sizeof(args); i++, d++)
166                 outb(*d, args_addr + i);
167
168         /* Write data, if any */
169         debug_trace("cmd: %02x, ver: %02x", cmd, cmd_version);
170         for (i = 0, d = (uint8_t *)dout; i < dout_len; i++, d++) {
171                 outb(*d, param_addr + i);
172                 debug_trace("%02x ", *d);
173         }
174
175         outb(cmd, cmd_addr);
176         debug_trace("\n");
177
178         if (wait_for_sync(dev)) {
179                 debug("%s: Timeout waiting for response\n", __func__);
180                 return -1;
181         }
182
183         /* Check result */
184         i = inb(data_addr);
185         if (i) {
186                 debug("%s: CROS_EC result code %d\n", __func__, i);
187                 return -i;
188         }
189
190         /* Read back args */
191         for (i = 0, d = (uint8_t *)&args; i < sizeof(args); i++, d++)
192                 *d = inb(args_addr + i);
193
194         /*
195          * If EC didn't modify args flags, then somehow we sent a new-style
196          * command to an old EC, which means it would have read its params
197          * from the wrong place.
198          */
199         if (!(args.flags & EC_HOST_ARGS_FLAG_TO_HOST)) {
200                 debug("%s: CROS_EC protocol mismatch\n", __func__);
201                 return -EC_RES_INVALID_RESPONSE;
202         }
203
204         if (args.data_size > din_len) {
205                 debug("%s: CROS_EC returned too much data %d > %d\n",
206                       __func__, args.data_size, din_len);
207                 return -EC_RES_INVALID_RESPONSE;
208         }
209
210         /* Read data, if any */
211         for (i = 0, d = (uint8_t *)dev->din; i < args.data_size; i++, d++) {
212                 *d = inb(param_addr + i);
213                 debug_trace("%02x ", *d);
214         }
215         debug_trace("\n");
216
217         /* Verify checksum */
218         csum = cmd + args.flags + args.command_version + args.data_size;
219         for (i = 0, d = (uint8_t *)dev->din; i < args.data_size; i++, d++)
220                 csum += *d;
221
222         if (args.checksum != (uint8_t)csum) {
223                 debug("%s: CROS_EC response has invalid checksum\n", __func__);
224                 return -EC_RES_INVALID_CHECKSUM;
225         }
226         *dinp = dev->din;
227
228         /* Return actual amount of data received */
229         return args.data_size;
230 }
231
232 /**
233  * Initialize LPC protocol.
234  *
235  * @param dev           CROS_EC device
236  * @param blob          Device tree blob
237  * @return 0 if ok, -1 on error
238  */
239 int cros_ec_lpc_init(struct cros_ec_dev *dev, const void *blob)
240 {
241         int byte, i;
242
243         /* See if we can find an EC at the other end */
244         byte = 0xff;
245         byte &= inb(EC_LPC_ADDR_HOST_CMD);
246         byte &= inb(EC_LPC_ADDR_HOST_DATA);
247         for (i = 0; i < EC_HOST_PARAM_SIZE && (byte == 0xff); i++)
248                 byte &= inb(EC_LPC_ADDR_HOST_PARAM + i);
249         if (byte == 0xff) {
250                 debug("%s: CROS_EC device not found on LPC bus\n",
251                         __func__);
252                 return -1;
253         }
254
255         return 0;
256 }
257
258 /*
259  * Test if LPC command args are supported.
260  *
261  * The cheapest way to do this is by looking for the memory-mapped
262  * flag.  This is faster than sending a new-style 'hello' command and
263  * seeing whether the EC sets the EC_HOST_ARGS_FLAG_FROM_HOST flag
264  * in args when it responds.
265  */
266 int cros_ec_lpc_check_version(struct cros_ec_dev *dev)
267 {
268         if (inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID) == 'E' &&
269                         inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID + 1)
270                                 == 'C' &&
271                         (inb(EC_LPC_ADDR_MEMMAP +
272                                 EC_MEMMAP_HOST_CMD_FLAGS) &
273                                 EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED)) {
274                 dev->cmd_version_is_supported = 1;
275         } else {
276                 /* We are going to use the old IO ports */
277                 dev->cmd_version_is_supported = 0;
278         }
279         debug("lpc: version %s\n", dev->cmd_version_is_supported ?
280                         "new" : "old");
281
282         return 0;
283 }