xref: /rk3399_rockchip-uboot/drivers/misc/cros_ec_spi.c (revision ea0ebc8662357f8f5c7f568ec5e1eb8d773ae4ee)
1f3424c55SHung-ying Tyan /*
2f3424c55SHung-ying Tyan  * Chromium OS cros_ec driver - SPI interface
3f3424c55SHung-ying Tyan  *
4f3424c55SHung-ying Tyan  * Copyright (c) 2012 The Chromium OS Authors.
5f3424c55SHung-ying Tyan  *
61a459660SWolfgang Denk  * SPDX-License-Identifier:	GPL-2.0+
7f3424c55SHung-ying Tyan  */
8f3424c55SHung-ying Tyan 
9f3424c55SHung-ying Tyan /*
10f3424c55SHung-ying Tyan  * The Matrix Keyboard Protocol driver handles talking to the keyboard
11f3424c55SHung-ying Tyan  * controller chip. Mostly this is for keyboard functions, but some other
12f3424c55SHung-ying Tyan  * things have slipped in, so we provide generic services to talk to the
13f3424c55SHung-ying Tyan  * KBC.
14f3424c55SHung-ying Tyan  */
15f3424c55SHung-ying Tyan 
16f3424c55SHung-ying Tyan #include <common.h>
17f3424c55SHung-ying Tyan #include <cros_ec.h>
18*ea0ebc86SSimon Glass #include <dm.h>
19*ea0ebc86SSimon Glass #include <errno.h>
20f3424c55SHung-ying Tyan #include <spi.h>
21f3424c55SHung-ying Tyan 
22*ea0ebc86SSimon Glass DECLARE_GLOBAL_DATA_PTR;
23*ea0ebc86SSimon Glass 
24*ea0ebc86SSimon Glass #ifdef CONFIG_DM_CROS_EC
25*ea0ebc86SSimon Glass int cros_ec_spi_packet(struct udevice *udev, int out_bytes, int in_bytes)
26*ea0ebc86SSimon Glass {
27*ea0ebc86SSimon Glass 	struct cros_ec_dev *dev = udev->uclass_priv;
28*ea0ebc86SSimon Glass #else
29a6070283SRandall Spangler int cros_ec_spi_packet(struct cros_ec_dev *dev, int out_bytes, int in_bytes)
30a6070283SRandall Spangler {
31*ea0ebc86SSimon Glass #endif
32*ea0ebc86SSimon Glass 	struct spi_slave *slave = dev_get_parentdata(dev->dev);
33a6070283SRandall Spangler 	int rv;
34a6070283SRandall Spangler 
35a6070283SRandall Spangler 	/* Do the transfer */
36*ea0ebc86SSimon Glass 	if (spi_claim_bus(slave)) {
37a6070283SRandall Spangler 		debug("%s: Cannot claim SPI bus\n", __func__);
38a6070283SRandall Spangler 		return -1;
39a6070283SRandall Spangler 	}
40a6070283SRandall Spangler 
41*ea0ebc86SSimon Glass 	rv = spi_xfer(slave, max(out_bytes, in_bytes) * 8,
42a6070283SRandall Spangler 		      dev->dout, dev->din,
43a6070283SRandall Spangler 		      SPI_XFER_BEGIN | SPI_XFER_END);
44a6070283SRandall Spangler 
45*ea0ebc86SSimon Glass 	spi_release_bus(slave);
46a6070283SRandall Spangler 
47a6070283SRandall Spangler 	if (rv) {
48a6070283SRandall Spangler 		debug("%s: Cannot complete SPI transfer\n", __func__);
49a6070283SRandall Spangler 		return -1;
50a6070283SRandall Spangler 	}
51a6070283SRandall Spangler 
52a6070283SRandall Spangler 	return in_bytes;
53a6070283SRandall Spangler }
54a6070283SRandall Spangler 
55f3424c55SHung-ying Tyan /**
56f3424c55SHung-ying Tyan  * Send a command to a LPC CROS_EC device and return the reply.
57f3424c55SHung-ying Tyan  *
58f3424c55SHung-ying Tyan  * The device's internal input/output buffers are used.
59f3424c55SHung-ying Tyan  *
60f3424c55SHung-ying Tyan  * @param dev		CROS_EC device
61f3424c55SHung-ying Tyan  * @param cmd		Command to send (EC_CMD_...)
62f3424c55SHung-ying Tyan  * @param cmd_version	Version of command to send (EC_VER_...)
63f3424c55SHung-ying Tyan  * @param dout		Output data (may be NULL If dout_len=0)
64f3424c55SHung-ying Tyan  * @param dout_len      Size of output data in bytes
65f3424c55SHung-ying Tyan  * @param dinp		Returns pointer to response data. This will be
66f3424c55SHung-ying Tyan  *			untouched unless we return a value > 0.
67f3424c55SHung-ying Tyan  * @param din_len	Maximum size of response in bytes
68f3424c55SHung-ying Tyan  * @return number of bytes in response, or -1 on error
69f3424c55SHung-ying Tyan  */
70*ea0ebc86SSimon Glass #ifdef CONFIG_DM_CROS_EC
71*ea0ebc86SSimon Glass int cros_ec_spi_command(struct udevice *udev, uint8_t cmd, int cmd_version,
72*ea0ebc86SSimon Glass 		     const uint8_t *dout, int dout_len,
73*ea0ebc86SSimon Glass 		     uint8_t **dinp, int din_len)
74*ea0ebc86SSimon Glass {
75*ea0ebc86SSimon Glass 	struct cros_ec_dev *dev = udev->uclass_priv;
76*ea0ebc86SSimon Glass #else
77f3424c55SHung-ying Tyan int cros_ec_spi_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version,
78f3424c55SHung-ying Tyan 		     const uint8_t *dout, int dout_len,
79f3424c55SHung-ying Tyan 		     uint8_t **dinp, int din_len)
80f3424c55SHung-ying Tyan {
81*ea0ebc86SSimon Glass #endif
82*ea0ebc86SSimon Glass 	struct spi_slave *slave = dev_get_parentdata(dev->dev);
83f3424c55SHung-ying Tyan 	int in_bytes = din_len + 4;	/* status, length, checksum, trailer */
84f3424c55SHung-ying Tyan 	uint8_t *out;
85f3424c55SHung-ying Tyan 	uint8_t *p;
86f3424c55SHung-ying Tyan 	int csum, len;
87f3424c55SHung-ying Tyan 	int rv;
88f3424c55SHung-ying Tyan 
89e8c12662SRandall Spangler 	if (dev->protocol_version != 2) {
90e8c12662SRandall Spangler 		debug("%s: Unsupported EC protcol version %d\n",
91e8c12662SRandall Spangler 		      __func__, dev->protocol_version);
92e8c12662SRandall Spangler 		return -1;
93e8c12662SRandall Spangler 	}
94e8c12662SRandall Spangler 
95f3424c55SHung-ying Tyan 	/*
96f3424c55SHung-ying Tyan 	 * Sanity-check input size to make sure it plus transaction overhead
97f3424c55SHung-ying Tyan 	 * fits in the internal device buffer.
98f3424c55SHung-ying Tyan 	 */
99f3424c55SHung-ying Tyan 	if (in_bytes > sizeof(dev->din)) {
100f3424c55SHung-ying Tyan 		debug("%s: Cannot receive %d bytes\n", __func__, din_len);
101f3424c55SHung-ying Tyan 		return -1;
102f3424c55SHung-ying Tyan 	}
103f3424c55SHung-ying Tyan 
104f3424c55SHung-ying Tyan 	/* We represent message length as a byte */
105f3424c55SHung-ying Tyan 	if (dout_len > 0xff) {
106f3424c55SHung-ying Tyan 		debug("%s: Cannot send %d bytes\n", __func__, dout_len);
107f3424c55SHung-ying Tyan 		return -1;
108f3424c55SHung-ying Tyan 	}
109f3424c55SHung-ying Tyan 
110f3424c55SHung-ying Tyan 	/*
111f3424c55SHung-ying Tyan 	 * Clear input buffer so we don't get false hits for MSG_HEADER
112f3424c55SHung-ying Tyan 	 */
113f3424c55SHung-ying Tyan 	memset(dev->din, '\0', in_bytes);
114f3424c55SHung-ying Tyan 
115*ea0ebc86SSimon Glass 	if (spi_claim_bus(slave)) {
116f3424c55SHung-ying Tyan 		debug("%s: Cannot claim SPI bus\n", __func__);
117f3424c55SHung-ying Tyan 		return -1;
118f3424c55SHung-ying Tyan 	}
119f3424c55SHung-ying Tyan 
120f3424c55SHung-ying Tyan 	out = dev->dout;
1212001b9a6SSimon Glass 	out[0] = EC_CMD_VERSION0 + cmd_version;
122f3424c55SHung-ying Tyan 	out[1] = cmd;
123f3424c55SHung-ying Tyan 	out[2] = (uint8_t)dout_len;
124f3424c55SHung-ying Tyan 	memcpy(out + 3, dout, dout_len);
125f3424c55SHung-ying Tyan 	csum = cros_ec_calc_checksum(out, 3)
126f3424c55SHung-ying Tyan 	       + cros_ec_calc_checksum(dout, dout_len);
127f3424c55SHung-ying Tyan 	out[3 + dout_len] = (uint8_t)csum;
128f3424c55SHung-ying Tyan 
129f3424c55SHung-ying Tyan 	/*
130f3424c55SHung-ying Tyan 	 * Send output data and receive input data starting such that the
131f3424c55SHung-ying Tyan 	 * message body will be dword aligned.
132f3424c55SHung-ying Tyan 	 */
133f3424c55SHung-ying Tyan 	p = dev->din + sizeof(int64_t) - 2;
134f3424c55SHung-ying Tyan 	len = dout_len + 4;
135f3424c55SHung-ying Tyan 	cros_ec_dump_data("out", cmd, out, len);
136*ea0ebc86SSimon Glass 	rv = spi_xfer(slave, max(len, in_bytes) * 8, out, p,
137f3424c55SHung-ying Tyan 		      SPI_XFER_BEGIN | SPI_XFER_END);
138f3424c55SHung-ying Tyan 
139*ea0ebc86SSimon Glass 	spi_release_bus(slave);
140f3424c55SHung-ying Tyan 
141f3424c55SHung-ying Tyan 	if (rv) {
142f3424c55SHung-ying Tyan 		debug("%s: Cannot complete SPI transfer\n", __func__);
143f3424c55SHung-ying Tyan 		return -1;
144f3424c55SHung-ying Tyan 	}
145f3424c55SHung-ying Tyan 
146f3424c55SHung-ying Tyan 	len = min(p[1], din_len);
147f3424c55SHung-ying Tyan 	cros_ec_dump_data("in", -1, p, len + 3);
148f3424c55SHung-ying Tyan 
149f3424c55SHung-ying Tyan 	/* Response code is first byte of message */
150f3424c55SHung-ying Tyan 	if (p[0] != EC_RES_SUCCESS) {
151f3424c55SHung-ying Tyan 		printf("%s: Returned status %d\n", __func__, p[0]);
152f3424c55SHung-ying Tyan 		return -(int)(p[0]);
153f3424c55SHung-ying Tyan 	}
154f3424c55SHung-ying Tyan 
155f3424c55SHung-ying Tyan 	/* Check checksum */
156f3424c55SHung-ying Tyan 	csum = cros_ec_calc_checksum(p, len + 2);
157f3424c55SHung-ying Tyan 	if (csum != p[len + 2]) {
158f3424c55SHung-ying Tyan 		debug("%s: Invalid checksum rx %#02x, calced %#02x\n", __func__,
159f3424c55SHung-ying Tyan 		      p[2 + len], csum);
160f3424c55SHung-ying Tyan 		return -1;
161f3424c55SHung-ying Tyan 	}
162f3424c55SHung-ying Tyan 
163f3424c55SHung-ying Tyan 	/* Anything else is the response data */
164f3424c55SHung-ying Tyan 	*dinp = p + 2;
165f3424c55SHung-ying Tyan 
166f3424c55SHung-ying Tyan 	return len;
167f3424c55SHung-ying Tyan }
168f3424c55SHung-ying Tyan 
169*ea0ebc86SSimon Glass #ifndef CONFIG_DM_CROS_EC
170f3424c55SHung-ying Tyan int cros_ec_spi_decode_fdt(struct cros_ec_dev *dev, const void *blob)
171f3424c55SHung-ying Tyan {
172f3424c55SHung-ying Tyan 	/* Decode interface-specific FDT params */
173f3424c55SHung-ying Tyan 	dev->max_frequency = fdtdec_get_int(blob, dev->node,
174f3424c55SHung-ying Tyan 					    "spi-max-frequency", 500000);
175f3424c55SHung-ying Tyan 	dev->cs = fdtdec_get_int(blob, dev->node, "reg", 0);
176f3424c55SHung-ying Tyan 
177f3424c55SHung-ying Tyan 	return 0;
178f3424c55SHung-ying Tyan }
179f3424c55SHung-ying Tyan 
180f3424c55SHung-ying Tyan /**
181f3424c55SHung-ying Tyan  * Initialize SPI protocol.
182f3424c55SHung-ying Tyan  *
183f3424c55SHung-ying Tyan  * @param dev		CROS_EC device
184f3424c55SHung-ying Tyan  * @param blob		Device tree blob
185f3424c55SHung-ying Tyan  * @return 0 if ok, -1 on error
186f3424c55SHung-ying Tyan  */
187f3424c55SHung-ying Tyan int cros_ec_spi_init(struct cros_ec_dev *dev, const void *blob)
188f3424c55SHung-ying Tyan {
189*ea0ebc86SSimon Glass 	int ret;
190*ea0ebc86SSimon Glass 
191*ea0ebc86SSimon Glass 	ret = spi_setup_slave_fdt(blob, dev->node, dev->parent_node,
192*ea0ebc86SSimon Glass 				  &slave);
193*ea0ebc86SSimon Glass 	if (ret) {
194f3424c55SHung-ying Tyan 		debug("%s: Could not setup SPI slave\n", __func__);
195*ea0ebc86SSimon Glass 		return ret;
196f3424c55SHung-ying Tyan 	}
197f3424c55SHung-ying Tyan 
198f3424c55SHung-ying Tyan 	return 0;
199f3424c55SHung-ying Tyan }
200*ea0ebc86SSimon Glass #endif
201*ea0ebc86SSimon Glass 
202*ea0ebc86SSimon Glass #ifdef CONFIG_DM_CROS_EC
203*ea0ebc86SSimon Glass int cros_ec_probe(struct udevice *dev)
204*ea0ebc86SSimon Glass {
205*ea0ebc86SSimon Glass 	struct spi_slave *slave = dev_get_parentdata(dev);
206*ea0ebc86SSimon Glass 	int ret;
207*ea0ebc86SSimon Glass 
208*ea0ebc86SSimon Glass 	/*
209*ea0ebc86SSimon Glass 	 * TODO(sjg@chromium.org)
210*ea0ebc86SSimon Glass 	 *
211*ea0ebc86SSimon Glass 	 * This is really horrible at present. It is an artifact of removing
212*ea0ebc86SSimon Glass 	 * the child_pre_probe() method for SPI. Everything here could go in
213*ea0ebc86SSimon Glass 	 * an automatic function, except that spi_get_bus_and_cs() wants to
214*ea0ebc86SSimon Glass 	 * set it up manually and call device_probe_child().
215*ea0ebc86SSimon Glass 	 *
216*ea0ebc86SSimon Glass 	 * The solution may be to re-enable the child_pre_probe() method for
217*ea0ebc86SSimon Glass 	 * SPI and have it do nothing if the child is already passed in via
218*ea0ebc86SSimon Glass 	 * device_probe_child().
219*ea0ebc86SSimon Glass 	 */
220*ea0ebc86SSimon Glass 	slave->dev = dev;
221*ea0ebc86SSimon Glass 	ret = spi_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, slave);
222*ea0ebc86SSimon Glass 	if (ret)
223*ea0ebc86SSimon Glass 		return ret;
224*ea0ebc86SSimon Glass 	return cros_ec_register(dev);
225*ea0ebc86SSimon Glass }
226*ea0ebc86SSimon Glass 
227*ea0ebc86SSimon Glass struct dm_cros_ec_ops cros_ec_ops = {
228*ea0ebc86SSimon Glass 	.packet = cros_ec_spi_packet,
229*ea0ebc86SSimon Glass 	.command = cros_ec_spi_command,
230*ea0ebc86SSimon Glass };
231*ea0ebc86SSimon Glass 
232*ea0ebc86SSimon Glass static const struct udevice_id cros_ec_ids[] = {
233*ea0ebc86SSimon Glass 	{ .compatible = "google,cros-ec" },
234*ea0ebc86SSimon Glass 	{ }
235*ea0ebc86SSimon Glass };
236*ea0ebc86SSimon Glass 
237*ea0ebc86SSimon Glass U_BOOT_DRIVER(cros_ec_spi) = {
238*ea0ebc86SSimon Glass 	.name		= "cros_ec",
239*ea0ebc86SSimon Glass 	.id		= UCLASS_CROS_EC,
240*ea0ebc86SSimon Glass 	.of_match	= cros_ec_ids,
241*ea0ebc86SSimon Glass 	.probe		= cros_ec_probe,
242*ea0ebc86SSimon Glass 	.ops		= &cros_ec_ops,
243*ea0ebc86SSimon Glass };
244*ea0ebc86SSimon Glass #endif
245