183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
2c8d3328aSHung-ying Tyan /*
3c8d3328aSHung-ying Tyan * Chromium OS cros_ec driver - LPC interface
4c8d3328aSHung-ying Tyan *
5c8d3328aSHung-ying Tyan * Copyright (c) 2012 The Chromium OS Authors.
6c8d3328aSHung-ying Tyan */
7c8d3328aSHung-ying Tyan
8c8d3328aSHung-ying Tyan /*
9c8d3328aSHung-ying Tyan * The Matrix Keyboard Protocol driver handles talking to the keyboard
10c8d3328aSHung-ying Tyan * controller chip. Mostly this is for keyboard functions, but some other
11c8d3328aSHung-ying Tyan * things have slipped in, so we provide generic services to talk to the
12c8d3328aSHung-ying Tyan * KBC.
13c8d3328aSHung-ying Tyan */
14c8d3328aSHung-ying Tyan
15c8d3328aSHung-ying Tyan #include <common.h>
1672a38e06SSimon Glass #include <dm.h>
17c8d3328aSHung-ying Tyan #include <command.h>
18c8d3328aSHung-ying Tyan #include <cros_ec.h>
19c8d3328aSHung-ying Tyan #include <asm/io.h>
20c8d3328aSHung-ying Tyan
21c8d3328aSHung-ying Tyan #ifdef DEBUG_TRACE
22c8d3328aSHung-ying Tyan #define debug_trace(fmt, b...) debug(fmt, ##b)
23c8d3328aSHung-ying Tyan #else
24c8d3328aSHung-ying Tyan #define debug_trace(fmt, b...)
25c8d3328aSHung-ying Tyan #endif
26c8d3328aSHung-ying Tyan
wait_for_sync(struct cros_ec_dev * dev)27c8d3328aSHung-ying Tyan static int wait_for_sync(struct cros_ec_dev *dev)
28c8d3328aSHung-ying Tyan {
29c8d3328aSHung-ying Tyan unsigned long start;
30c8d3328aSHung-ying Tyan
31c8d3328aSHung-ying Tyan start = get_timer(0);
32c8d3328aSHung-ying Tyan while (inb(EC_LPC_ADDR_HOST_CMD) & EC_LPC_STATUS_BUSY_MASK) {
33c8d3328aSHung-ying Tyan if (get_timer(start) > 1000) {
34c8d3328aSHung-ying Tyan debug("%s: Timeout waiting for CROS_EC sync\n",
35c8d3328aSHung-ying Tyan __func__);
36c8d3328aSHung-ying Tyan return -1;
37c8d3328aSHung-ying Tyan }
38c8d3328aSHung-ying Tyan }
39c8d3328aSHung-ying Tyan
40c8d3328aSHung-ying Tyan return 0;
41c8d3328aSHung-ying Tyan }
42c8d3328aSHung-ying Tyan
cros_ec_lpc_packet(struct udevice * udev,int out_bytes,int in_bytes)43*d07b6e14SSimon Glass int cros_ec_lpc_packet(struct udevice *udev, int out_bytes, int in_bytes)
44*d07b6e14SSimon Glass {
45*d07b6e14SSimon Glass struct cros_ec_dev *dev = dev_get_uclass_priv(udev);
46*d07b6e14SSimon Glass uint8_t *d;
47*d07b6e14SSimon Glass int i;
48*d07b6e14SSimon Glass
49*d07b6e14SSimon Glass if (out_bytes > EC_LPC_HOST_PACKET_SIZE)
50*d07b6e14SSimon Glass return log_msg_ret("Cannot send that many bytes\n", -E2BIG);
51*d07b6e14SSimon Glass
52*d07b6e14SSimon Glass if (in_bytes > EC_LPC_HOST_PACKET_SIZE)
53*d07b6e14SSimon Glass return log_msg_ret("Cannot receive that many bytes\n", -E2BIG);
54*d07b6e14SSimon Glass
55*d07b6e14SSimon Glass if (wait_for_sync(dev))
56*d07b6e14SSimon Glass return log_msg_ret("Timeout waiting ready\n", -ETIMEDOUT);
57*d07b6e14SSimon Glass
58*d07b6e14SSimon Glass /* Write data */
59*d07b6e14SSimon Glass for (i = 0, d = (uint8_t *)dev->dout; i < out_bytes; i++, d++)
60*d07b6e14SSimon Glass outb(*d, EC_LPC_ADDR_HOST_PACKET + i);
61*d07b6e14SSimon Glass
62*d07b6e14SSimon Glass /* Start the command */
63*d07b6e14SSimon Glass outb(EC_COMMAND_PROTOCOL_3, EC_LPC_ADDR_HOST_CMD);
64*d07b6e14SSimon Glass
65*d07b6e14SSimon Glass if (wait_for_sync(dev))
66*d07b6e14SSimon Glass return log_msg_ret("Timeout waiting ready\n", -ETIMEDOUT);
67*d07b6e14SSimon Glass
68*d07b6e14SSimon Glass /* Read back args */
69*d07b6e14SSimon Glass for (i = 0, d = dev->din; i < in_bytes; i++, d++)
70*d07b6e14SSimon Glass *d = inb(EC_LPC_ADDR_HOST_PACKET + i);
71*d07b6e14SSimon Glass
72*d07b6e14SSimon Glass return in_bytes;
73*d07b6e14SSimon Glass }
74*d07b6e14SSimon Glass
cros_ec_lpc_command(struct udevice * udev,uint8_t cmd,int cmd_version,const uint8_t * dout,int dout_len,uint8_t ** dinp,int din_len)7572a38e06SSimon Glass int cros_ec_lpc_command(struct udevice *udev, uint8_t cmd, int cmd_version,
7672a38e06SSimon Glass const uint8_t *dout, int dout_len,
7772a38e06SSimon Glass uint8_t **dinp, int din_len)
7872a38e06SSimon Glass {
7972a38e06SSimon Glass struct cros_ec_dev *dev = dev_get_uclass_priv(udev);
80c8d3328aSHung-ying Tyan const int cmd_addr = EC_LPC_ADDR_HOST_CMD;
81c8d3328aSHung-ying Tyan const int data_addr = EC_LPC_ADDR_HOST_DATA;
82c8d3328aSHung-ying Tyan const int args_addr = EC_LPC_ADDR_HOST_ARGS;
83c8d3328aSHung-ying Tyan const int param_addr = EC_LPC_ADDR_HOST_PARAM;
84c8d3328aSHung-ying Tyan
85c8d3328aSHung-ying Tyan struct ec_lpc_host_args args;
86c8d3328aSHung-ying Tyan uint8_t *d;
87c8d3328aSHung-ying Tyan int csum;
88c8d3328aSHung-ying Tyan int i;
89c8d3328aSHung-ying Tyan
90f1269925SSimon Glass if (dout_len > EC_PROTO2_MAX_PARAM_SIZE) {
91c8d3328aSHung-ying Tyan debug("%s: Cannot send %d bytes\n", __func__, dout_len);
92c8d3328aSHung-ying Tyan return -1;
93c8d3328aSHung-ying Tyan }
94c8d3328aSHung-ying Tyan
95c8d3328aSHung-ying Tyan /* Fill in args */
96c8d3328aSHung-ying Tyan args.flags = EC_HOST_ARGS_FLAG_FROM_HOST;
97c8d3328aSHung-ying Tyan args.command_version = cmd_version;
98c8d3328aSHung-ying Tyan args.data_size = dout_len;
99c8d3328aSHung-ying Tyan
100c8d3328aSHung-ying Tyan /* Calculate checksum */
101c8d3328aSHung-ying Tyan csum = cmd + args.flags + args.command_version + args.data_size;
102c8d3328aSHung-ying Tyan for (i = 0, d = (uint8_t *)dout; i < dout_len; i++, d++)
103c8d3328aSHung-ying Tyan csum += *d;
104c8d3328aSHung-ying Tyan
105c8d3328aSHung-ying Tyan args.checksum = (uint8_t)csum;
106c8d3328aSHung-ying Tyan
107c8d3328aSHung-ying Tyan if (wait_for_sync(dev)) {
108c8d3328aSHung-ying Tyan debug("%s: Timeout waiting ready\n", __func__);
109c8d3328aSHung-ying Tyan return -1;
110c8d3328aSHung-ying Tyan }
111c8d3328aSHung-ying Tyan
112c8d3328aSHung-ying Tyan /* Write args */
113c8d3328aSHung-ying Tyan for (i = 0, d = (uint8_t *)&args; i < sizeof(args); i++, d++)
114c8d3328aSHung-ying Tyan outb(*d, args_addr + i);
115c8d3328aSHung-ying Tyan
116c8d3328aSHung-ying Tyan /* Write data, if any */
117c8d3328aSHung-ying Tyan debug_trace("cmd: %02x, ver: %02x", cmd, cmd_version);
118c8d3328aSHung-ying Tyan for (i = 0, d = (uint8_t *)dout; i < dout_len; i++, d++) {
119c8d3328aSHung-ying Tyan outb(*d, param_addr + i);
120c8d3328aSHung-ying Tyan debug_trace("%02x ", *d);
121c8d3328aSHung-ying Tyan }
122c8d3328aSHung-ying Tyan
123c8d3328aSHung-ying Tyan outb(cmd, cmd_addr);
124c8d3328aSHung-ying Tyan debug_trace("\n");
125c8d3328aSHung-ying Tyan
126c8d3328aSHung-ying Tyan if (wait_for_sync(dev)) {
127c8d3328aSHung-ying Tyan debug("%s: Timeout waiting for response\n", __func__);
128c8d3328aSHung-ying Tyan return -1;
129c8d3328aSHung-ying Tyan }
130c8d3328aSHung-ying Tyan
131c8d3328aSHung-ying Tyan /* Check result */
132c8d3328aSHung-ying Tyan i = inb(data_addr);
133c8d3328aSHung-ying Tyan if (i) {
134c8d3328aSHung-ying Tyan debug("%s: CROS_EC result code %d\n", __func__, i);
135c8d3328aSHung-ying Tyan return -i;
136c8d3328aSHung-ying Tyan }
137c8d3328aSHung-ying Tyan
138c8d3328aSHung-ying Tyan /* Read back args */
139c8d3328aSHung-ying Tyan for (i = 0, d = (uint8_t *)&args; i < sizeof(args); i++, d++)
140c8d3328aSHung-ying Tyan *d = inb(args_addr + i);
141c8d3328aSHung-ying Tyan
142c8d3328aSHung-ying Tyan /*
143c8d3328aSHung-ying Tyan * If EC didn't modify args flags, then somehow we sent a new-style
144c8d3328aSHung-ying Tyan * command to an old EC, which means it would have read its params
145c8d3328aSHung-ying Tyan * from the wrong place.
146c8d3328aSHung-ying Tyan */
147c8d3328aSHung-ying Tyan if (!(args.flags & EC_HOST_ARGS_FLAG_TO_HOST)) {
148c8d3328aSHung-ying Tyan debug("%s: CROS_EC protocol mismatch\n", __func__);
149c8d3328aSHung-ying Tyan return -EC_RES_INVALID_RESPONSE;
150c8d3328aSHung-ying Tyan }
151c8d3328aSHung-ying Tyan
152c8d3328aSHung-ying Tyan if (args.data_size > din_len) {
153c8d3328aSHung-ying Tyan debug("%s: CROS_EC returned too much data %d > %d\n",
154c8d3328aSHung-ying Tyan __func__, args.data_size, din_len);
155c8d3328aSHung-ying Tyan return -EC_RES_INVALID_RESPONSE;
156c8d3328aSHung-ying Tyan }
157c8d3328aSHung-ying Tyan
158c8d3328aSHung-ying Tyan /* Read data, if any */
159c8d3328aSHung-ying Tyan for (i = 0, d = (uint8_t *)dev->din; i < args.data_size; i++, d++) {
160c8d3328aSHung-ying Tyan *d = inb(param_addr + i);
161c8d3328aSHung-ying Tyan debug_trace("%02x ", *d);
162c8d3328aSHung-ying Tyan }
163c8d3328aSHung-ying Tyan debug_trace("\n");
164c8d3328aSHung-ying Tyan
165c8d3328aSHung-ying Tyan /* Verify checksum */
166c8d3328aSHung-ying Tyan csum = cmd + args.flags + args.command_version + args.data_size;
167c8d3328aSHung-ying Tyan for (i = 0, d = (uint8_t *)dev->din; i < args.data_size; i++, d++)
168c8d3328aSHung-ying Tyan csum += *d;
169c8d3328aSHung-ying Tyan
170c8d3328aSHung-ying Tyan if (args.checksum != (uint8_t)csum) {
171c8d3328aSHung-ying Tyan debug("%s: CROS_EC response has invalid checksum\n", __func__);
172c8d3328aSHung-ying Tyan return -EC_RES_INVALID_CHECKSUM;
173c8d3328aSHung-ying Tyan }
174c8d3328aSHung-ying Tyan *dinp = dev->din;
175c8d3328aSHung-ying Tyan
176c8d3328aSHung-ying Tyan /* Return actual amount of data received */
177c8d3328aSHung-ying Tyan return args.data_size;
178c8d3328aSHung-ying Tyan }
179c8d3328aSHung-ying Tyan
180c8d3328aSHung-ying Tyan /**
181c8d3328aSHung-ying Tyan * Initialize LPC protocol.
182c8d3328aSHung-ying Tyan *
183c8d3328aSHung-ying Tyan * @param dev CROS_EC device
184c8d3328aSHung-ying Tyan * @param blob Device tree blob
185c8d3328aSHung-ying Tyan * @return 0 if ok, -1 on error
186c8d3328aSHung-ying Tyan */
cros_ec_lpc_init(struct cros_ec_dev * dev,const void * blob)187c8d3328aSHung-ying Tyan int cros_ec_lpc_init(struct cros_ec_dev *dev, const void *blob)
188c8d3328aSHung-ying Tyan {
189c8d3328aSHung-ying Tyan int byte, i;
190c8d3328aSHung-ying Tyan
191c8d3328aSHung-ying Tyan /* See if we can find an EC at the other end */
192c8d3328aSHung-ying Tyan byte = 0xff;
193c8d3328aSHung-ying Tyan byte &= inb(EC_LPC_ADDR_HOST_CMD);
194c8d3328aSHung-ying Tyan byte &= inb(EC_LPC_ADDR_HOST_DATA);
195f1269925SSimon Glass for (i = 0; i < EC_PROTO2_MAX_PARAM_SIZE && (byte == 0xff); i++)
196c8d3328aSHung-ying Tyan byte &= inb(EC_LPC_ADDR_HOST_PARAM + i);
197c8d3328aSHung-ying Tyan if (byte == 0xff) {
198c8d3328aSHung-ying Tyan debug("%s: CROS_EC device not found on LPC bus\n",
199c8d3328aSHung-ying Tyan __func__);
200c8d3328aSHung-ying Tyan return -1;
201c8d3328aSHung-ying Tyan }
202c8d3328aSHung-ying Tyan
203c8d3328aSHung-ying Tyan return 0;
204c8d3328aSHung-ying Tyan }
205c8d3328aSHung-ying Tyan
206c8d3328aSHung-ying Tyan /*
207c8d3328aSHung-ying Tyan * Test if LPC command args are supported.
208c8d3328aSHung-ying Tyan *
209c8d3328aSHung-ying Tyan * The cheapest way to do this is by looking for the memory-mapped
210c8d3328aSHung-ying Tyan * flag. This is faster than sending a new-style 'hello' command and
211c8d3328aSHung-ying Tyan * seeing whether the EC sets the EC_HOST_ARGS_FLAG_FROM_HOST flag
212c8d3328aSHung-ying Tyan * in args when it responds.
213c8d3328aSHung-ying Tyan */
cros_ec_lpc_check_version(struct udevice * dev)21472a38e06SSimon Glass static int cros_ec_lpc_check_version(struct udevice *dev)
215c8d3328aSHung-ying Tyan {
216c8d3328aSHung-ying Tyan if (inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID) == 'E' &&
217c8d3328aSHung-ying Tyan inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID + 1)
218c8d3328aSHung-ying Tyan == 'C' &&
219c8d3328aSHung-ying Tyan (inb(EC_LPC_ADDR_MEMMAP +
220c8d3328aSHung-ying Tyan EC_MEMMAP_HOST_CMD_FLAGS) &
221c8d3328aSHung-ying Tyan EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED)) {
222c8d3328aSHung-ying Tyan return 0;
223c8d3328aSHung-ying Tyan }
2244ff9b461SVadim Bendebury
2254ff9b461SVadim Bendebury printf("%s: ERROR: old EC interface not supported\n", __func__);
2264ff9b461SVadim Bendebury return -1;
2274ff9b461SVadim Bendebury }
22872a38e06SSimon Glass
cros_ec_probe(struct udevice * dev)22972a38e06SSimon Glass static int cros_ec_probe(struct udevice *dev)
23072a38e06SSimon Glass {
23172a38e06SSimon Glass return cros_ec_register(dev);
23272a38e06SSimon Glass }
23372a38e06SSimon Glass
23472a38e06SSimon Glass static struct dm_cros_ec_ops cros_ec_ops = {
235*d07b6e14SSimon Glass .packet = cros_ec_lpc_packet,
23672a38e06SSimon Glass .command = cros_ec_lpc_command,
23772a38e06SSimon Glass .check_version = cros_ec_lpc_check_version,
23872a38e06SSimon Glass };
23972a38e06SSimon Glass
24072a38e06SSimon Glass static const struct udevice_id cros_ec_ids[] = {
2413fbb7871SSimon Glass { .compatible = "google,cros-ec-lpc" },
24272a38e06SSimon Glass { }
24372a38e06SSimon Glass };
24472a38e06SSimon Glass
24572a38e06SSimon Glass U_BOOT_DRIVER(cros_ec_lpc) = {
2463fbb7871SSimon Glass .name = "cros_ec_lpc",
24772a38e06SSimon Glass .id = UCLASS_CROS_EC,
24872a38e06SSimon Glass .of_match = cros_ec_ids,
24972a38e06SSimon Glass .probe = cros_ec_probe,
25072a38e06SSimon Glass .ops = &cros_ec_ops,
25172a38e06SSimon Glass };
252