xref: /openbmc/u-boot/drivers/misc/cros_ec_spi.c (revision 86bedaeb)
1 /*
2  * Chromium OS cros_ec driver - SPI interface
3  *
4  * Copyright (c) 2012 The Chromium OS Authors.
5  *
6  * SPDX-License-Identifier:	GPL-2.0+
7  */
8 
9 /*
10  * The Matrix Keyboard Protocol driver handles talking to the keyboard
11  * controller chip. Mostly this is for keyboard functions, but some other
12  * things have slipped in, so we provide generic services to talk to the
13  * KBC.
14  */
15 
16 #include <common.h>
17 #include <cros_ec.h>
18 #include <dm.h>
19 #include <errno.h>
20 #include <spi.h>
21 
22 DECLARE_GLOBAL_DATA_PTR;
23 
24 int cros_ec_spi_packet(struct udevice *udev, int out_bytes, int in_bytes)
25 {
26 	struct cros_ec_dev *dev = udev->uclass_priv;
27 	struct spi_slave *slave = dev_get_parentdata(dev->dev);
28 	int rv;
29 
30 	/* Do the transfer */
31 	if (spi_claim_bus(slave)) {
32 		debug("%s: Cannot claim SPI bus\n", __func__);
33 		return -1;
34 	}
35 
36 	rv = spi_xfer(slave, max(out_bytes, in_bytes) * 8,
37 		      dev->dout, dev->din,
38 		      SPI_XFER_BEGIN | SPI_XFER_END);
39 
40 	spi_release_bus(slave);
41 
42 	if (rv) {
43 		debug("%s: Cannot complete SPI transfer\n", __func__);
44 		return -1;
45 	}
46 
47 	return in_bytes;
48 }
49 
50 /**
51  * Send a command to a LPC CROS_EC device and return the reply.
52  *
53  * The device's internal input/output buffers are used.
54  *
55  * @param dev		CROS_EC device
56  * @param cmd		Command to send (EC_CMD_...)
57  * @param cmd_version	Version of command to send (EC_VER_...)
58  * @param dout		Output data (may be NULL If dout_len=0)
59  * @param dout_len      Size of output data in bytes
60  * @param dinp		Returns pointer to response data. This will be
61  *			untouched unless we return a value > 0.
62  * @param din_len	Maximum size of response in bytes
63  * @return number of bytes in response, or -1 on error
64  */
65 int cros_ec_spi_command(struct udevice *udev, uint8_t cmd, int cmd_version,
66 		     const uint8_t *dout, int dout_len,
67 		     uint8_t **dinp, int din_len)
68 {
69 	struct cros_ec_dev *dev = udev->uclass_priv;
70 	struct spi_slave *slave = dev_get_parentdata(dev->dev);
71 	int in_bytes = din_len + 4;	/* status, length, checksum, trailer */
72 	uint8_t *out;
73 	uint8_t *p;
74 	int csum, len;
75 	int rv;
76 
77 	if (dev->protocol_version != 2) {
78 		debug("%s: Unsupported EC protcol version %d\n",
79 		      __func__, dev->protocol_version);
80 		return -1;
81 	}
82 
83 	/*
84 	 * Sanity-check input size to make sure it plus transaction overhead
85 	 * fits in the internal device buffer.
86 	 */
87 	if (in_bytes > sizeof(dev->din)) {
88 		debug("%s: Cannot receive %d bytes\n", __func__, din_len);
89 		return -1;
90 	}
91 
92 	/* We represent message length as a byte */
93 	if (dout_len > 0xff) {
94 		debug("%s: Cannot send %d bytes\n", __func__, dout_len);
95 		return -1;
96 	}
97 
98 	/*
99 	 * Clear input buffer so we don't get false hits for MSG_HEADER
100 	 */
101 	memset(dev->din, '\0', in_bytes);
102 
103 	if (spi_claim_bus(slave)) {
104 		debug("%s: Cannot claim SPI bus\n", __func__);
105 		return -1;
106 	}
107 
108 	out = dev->dout;
109 	out[0] = EC_CMD_VERSION0 + cmd_version;
110 	out[1] = cmd;
111 	out[2] = (uint8_t)dout_len;
112 	memcpy(out + 3, dout, dout_len);
113 	csum = cros_ec_calc_checksum(out, 3)
114 	       + cros_ec_calc_checksum(dout, dout_len);
115 	out[3 + dout_len] = (uint8_t)csum;
116 
117 	/*
118 	 * Send output data and receive input data starting such that the
119 	 * message body will be dword aligned.
120 	 */
121 	p = dev->din + sizeof(int64_t) - 2;
122 	len = dout_len + 4;
123 	cros_ec_dump_data("out", cmd, out, len);
124 	rv = spi_xfer(slave, max(len, in_bytes) * 8, out, p,
125 		      SPI_XFER_BEGIN | SPI_XFER_END);
126 
127 	spi_release_bus(slave);
128 
129 	if (rv) {
130 		debug("%s: Cannot complete SPI transfer\n", __func__);
131 		return -1;
132 	}
133 
134 	len = min((int)p[1], din_len);
135 	cros_ec_dump_data("in", -1, p, len + 3);
136 
137 	/* Response code is first byte of message */
138 	if (p[0] != EC_RES_SUCCESS) {
139 		printf("%s: Returned status %d\n", __func__, p[0]);
140 		return -(int)(p[0]);
141 	}
142 
143 	/* Check checksum */
144 	csum = cros_ec_calc_checksum(p, len + 2);
145 	if (csum != p[len + 2]) {
146 		debug("%s: Invalid checksum rx %#02x, calced %#02x\n", __func__,
147 		      p[2 + len], csum);
148 		return -1;
149 	}
150 
151 	/* Anything else is the response data */
152 	*dinp = p + 2;
153 
154 	return len;
155 }
156 
157 static int cros_ec_probe(struct udevice *dev)
158 {
159 	return cros_ec_register(dev);
160 }
161 
162 static struct dm_cros_ec_ops cros_ec_ops = {
163 	.packet = cros_ec_spi_packet,
164 	.command = cros_ec_spi_command,
165 };
166 
167 static const struct udevice_id cros_ec_ids[] = {
168 	{ .compatible = "google,cros-ec" },
169 	{ }
170 };
171 
172 U_BOOT_DRIVER(cros_ec_spi) = {
173 	.name		= "cros_ec",
174 	.id		= UCLASS_CROS_EC,
175 	.of_match	= cros_ec_ids,
176 	.probe		= cros_ec_probe,
177 	.ops		= &cros_ec_ops,
178 };
179