]> git.ipfire.org Git - thirdparty/u-boot.git/blob - drivers/misc/atsha204a-i2c.c
misc: atsha204a: fix wakeup function
[thirdparty/u-boot.git] / drivers / misc / atsha204a-i2c.c
1 /*
2 * I2C Driver for Atmel ATSHA204 over I2C
3 *
4 * Copyright (C) 2014 Josh Datko, Cryptotronix, jbd@cryptotronix.com
5 * 2016 Tomas Hlavacek, CZ.NIC, tmshlvck@gmail.com
6 * 2017 Marek BehĂșn, CZ.NIC, kabel@kernel.org
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13 #include <common.h>
14 #include <dm.h>
15 #include <i2c.h>
16 #include <errno.h>
17 #include <atsha204a-i2c.h>
18 #include <log.h>
19 #include <asm/global_data.h>
20 #include <linux/delay.h>
21 #include <linux/bitrev.h>
22 #include <u-boot/crc.h>
23
24 #define ATSHA204A_TWHI_US 2500
25 #define ATSHA204A_TRANSACTION_TIMEOUT 100000
26 #define ATSHA204A_TRANSACTION_RETRY 5
27 #define ATSHA204A_EXECTIME 5000
28
29 DECLARE_GLOBAL_DATA_PTR;
30
31 static inline u16 atsha204a_crc16(const u8 *buffer, size_t len)
32 {
33 return bitrev16(crc16(0, buffer, len));
34 }
35
36 static int atsha204a_ping_bus(struct udevice *dev)
37 {
38 struct udevice *bus = dev_get_parent(dev);
39 struct i2c_msg msg;
40 int speed;
41 int res;
42 u8 val = 0;
43
44 speed = dm_i2c_get_bus_speed(bus);
45 if (speed != I2C_SPEED_STANDARD_RATE) {
46 int rv;
47
48 rv = dm_i2c_set_bus_speed(bus, I2C_SPEED_STANDARD_RATE);
49 if (rv)
50 debug("Couldn't change the I2C bus speed\n");
51 }
52
53 /*
54 * The I2C drivers don't support sending messages when NAK is received.
55 * This chip requires wake up low signal on SDA for >= 60us.
56 * To achieve this, we slow the bus to 100kHz and send an empty
57 * message to address 0. This will hold the SDA line low for the
58 * required time to wake up the chip.
59 */
60 msg.addr = 0;
61 msg.flags = I2C_M_STOP;
62 msg.len = sizeof(val);
63 msg.buf = &val;
64
65 res = dm_i2c_xfer(dev, &msg, 1);
66
67 if (speed != I2C_SPEED_STANDARD_RATE) {
68 int rv;
69
70 rv = dm_i2c_set_bus_speed(bus, speed);
71 if (rv)
72 debug("Couldn't restore the I2C bus speed\n");
73 }
74
75 return res;
76 }
77
78 static int atsha204a_send(struct udevice *dev, const u8 *buf, u8 len)
79 {
80 fdt_addr_t *priv = dev_get_priv(dev);
81 struct i2c_msg msg;
82
83 msg.addr = *priv;
84 msg.flags = I2C_M_STOP;
85 msg.len = len;
86 msg.buf = (u8 *) buf;
87
88 return dm_i2c_xfer(dev, &msg, 1);
89 }
90
91 static int atsha204a_recv(struct udevice *dev, u8 *buf, u8 len)
92 {
93 fdt_addr_t *priv = dev_get_priv(dev);
94 struct i2c_msg msg;
95
96 msg.addr = *priv;
97 msg.flags = I2C_M_RD | I2C_M_STOP;
98 msg.len = len;
99 msg.buf = (u8 *) buf;
100
101 return dm_i2c_xfer(dev, &msg, 1);
102 }
103
104 static int atsha204a_recv_resp(struct udevice *dev,
105 struct atsha204a_resp *resp)
106 {
107 int res;
108 u16 resp_crc, computed_crc;
109 u8 *p = (u8 *) resp;
110
111 res = atsha204a_recv(dev, p, 4);
112 if (res)
113 return res;
114
115 if (resp->length > 4) {
116 if (resp->length > sizeof(*resp))
117 return -EMSGSIZE;
118
119 res = atsha204a_recv(dev, p + 4, resp->length - 4);
120 if (res)
121 return res;
122 }
123
124 resp_crc = (u16) p[resp->length - 2]
125 | (((u16) p[resp->length - 1]) << 8);
126 computed_crc = atsha204a_crc16(p, resp->length - 2);
127
128 if (resp_crc != computed_crc) {
129 debug("Invalid checksum in ATSHA204A response\n");
130 return -EBADMSG;
131 }
132
133 return 0;
134 }
135
136 int atsha204a_wakeup(struct udevice *dev)
137 {
138 struct atsha204a_resp resp;
139 int res;
140
141 debug("Waking up ATSHA204A\n");
142
143 /*
144 * The device ignores any levels or transitions on the SCL pin
145 * when the device is idle, asleep or during waking up.
146 * Don't check for error when waking up the device.
147 */
148 atsha204a_ping_bus(dev);
149
150 udelay(ATSHA204A_TWHI_US);
151
152 res = atsha204a_recv_resp(dev, &resp);
153 if (res) {
154 debug("failed on receiving response, ending\n");
155 return res;
156 }
157
158 if (resp.code != ATSHA204A_STATUS_AFTER_WAKE) {
159 debug("failed (response code = %02x), ending\n", resp.code);
160 return -EBADMSG;
161 }
162
163 debug("success\n");
164 return 0;
165 }
166
167 int atsha204a_idle(struct udevice *dev)
168 {
169 int res;
170 u8 req = ATSHA204A_FUNC_IDLE;
171
172 res = atsha204a_send(dev, &req, 1);
173 if (res)
174 debug("Failed putting ATSHA204A idle\n");
175 return res;
176 }
177
178 int atsha204a_sleep(struct udevice *dev)
179 {
180 int res;
181 u8 req = ATSHA204A_FUNC_SLEEP;
182
183 res = atsha204a_send(dev, &req, 1);
184 if (res)
185 debug("Failed putting ATSHA204A to sleep\n");
186 return res;
187 }
188
189 static int atsha204a_transaction(struct udevice *dev, struct atsha204a_req *req,
190 struct atsha204a_resp *resp)
191 {
192 int res, timeout = ATSHA204A_TRANSACTION_TIMEOUT;
193
194 res = atsha204a_send(dev, (u8 *) req, req->length + 1);
195 if (res) {
196 debug("ATSHA204A transaction send failed\n");
197 return -EBUSY;
198 }
199
200 do {
201 udelay(ATSHA204A_EXECTIME);
202 res = atsha204a_recv_resp(dev, resp);
203 if (!res || res == -EMSGSIZE || res == -EBADMSG)
204 break;
205
206 debug("ATSHA204A transaction polling for response "
207 "(timeout = %d)\n", timeout);
208
209 timeout -= ATSHA204A_EXECTIME;
210 } while (timeout > 0);
211
212 if (timeout <= 0) {
213 debug("ATSHA204A transaction timed out\n");
214 return -ETIMEDOUT;
215 }
216
217 return res;
218 }
219
220 static void atsha204a_req_crc32(struct atsha204a_req *req)
221 {
222 u8 *p = (u8 *) req;
223 u16 computed_crc;
224 u16 *crc_ptr = (u16 *) &p[req->length - 1];
225
226 /* The buffer to crc16 starts at byte 1, not 0 */
227 computed_crc = atsha204a_crc16(p + 1, req->length - 2);
228
229 *crc_ptr = cpu_to_le16(computed_crc);
230 }
231
232 int atsha204a_read(struct udevice *dev, enum atsha204a_zone zone, bool read32,
233 u16 addr, u8 *buffer)
234 {
235 int res, retry = ATSHA204A_TRANSACTION_RETRY;
236 struct atsha204a_req req;
237 struct atsha204a_resp resp;
238
239 req.function = ATSHA204A_FUNC_COMMAND;
240 req.length = 7;
241 req.command = ATSHA204A_CMD_READ;
242
243 req.param1 = (u8) zone;
244 if (read32)
245 req.param1 |= 0x80;
246
247 req.param2 = cpu_to_le16(addr);
248
249 atsha204a_req_crc32(&req);
250
251 do {
252 res = atsha204a_transaction(dev, &req, &resp);
253 if (!res)
254 break;
255
256 debug("ATSHA204A read retry (%d)\n", retry);
257 retry--;
258 atsha204a_wakeup(dev);
259 } while (retry >= 0);
260
261 if (res) {
262 debug("ATSHA204A read failed\n");
263 return res;
264 }
265
266 if (resp.length != (read32 ? 32 : 4) + 3) {
267 debug("ATSHA204A read bad response length (%d)\n",
268 resp.length);
269 return -EBADMSG;
270 }
271
272 memcpy(buffer, ((u8 *) &resp) + 1, read32 ? 32 : 4);
273
274 return 0;
275 }
276
277 int atsha204a_get_random(struct udevice *dev, u8 *buffer, size_t max)
278 {
279 int res;
280 struct atsha204a_req req;
281 struct atsha204a_resp resp;
282
283 req.function = ATSHA204A_FUNC_COMMAND;
284 req.length = 7;
285 req.command = ATSHA204A_CMD_RANDOM;
286
287 req.param1 = 1;
288 req.param2 = 0;
289
290 /* We do not have to compute the checksum dynamically */
291 req.data[0] = 0x27;
292 req.data[1] = 0x47;
293
294 res = atsha204a_transaction(dev, &req, &resp);
295 if (res) {
296 debug("ATSHA204A random transaction failed\n");
297 return res;
298 }
299
300 memcpy(buffer, ((u8 *) &resp) + 1, max >= 32 ? 32 : max);
301 return 0;
302 }
303
304 static int atsha204a_of_to_plat(struct udevice *dev)
305 {
306 fdt_addr_t *priv = dev_get_priv(dev);
307 fdt_addr_t addr;
308
309 addr = dev_read_addr(dev);
310 if (addr == FDT_ADDR_T_NONE) {
311 debug("Can't get ATSHA204A I2C base address\n");
312 return -ENXIO;
313 }
314
315 *priv = addr;
316 return 0;
317 }
318
319 static const struct udevice_id atsha204a_ids[] = {
320 { .compatible = "atmel,atsha204" },
321 { .compatible = "atmel,atsha204a" },
322 { }
323 };
324
325 U_BOOT_DRIVER(atsha204) = {
326 .name = "atsha204",
327 .id = UCLASS_MISC,
328 .of_match = atsha204a_ids,
329 .of_to_plat = atsha204a_of_to_plat,
330 .priv_auto = sizeof(fdt_addr_t),
331 };