blob: af65c559dc6963fa4b57d77c8a71437aeba6a1c6 [file] [log] [blame]
Marek Behúnef2b6b12017-06-09 19:28:44 +02001/*
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 Behun, CZ.NIC, marek.behun@nic.cz
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>
Simon Glass0f2af882020-05-10 11:40:05 -060018#include <log.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060019#include <asm/global_data.h>
Simon Glassdbd79542020-05-10 11:40:11 -060020#include <linux/delay.h>
Simon Glass48b6c6b2019-11-14 12:57:16 -070021#include <u-boot/crc.h>
Marek Behúnef2b6b12017-06-09 19:28:44 +020022
23#define ATSHA204A_TWLO 60
24#define ATSHA204A_TRANSACTION_TIMEOUT 100000
25#define ATSHA204A_TRANSACTION_RETRY 5
26#define ATSHA204A_EXECTIME 5000
27
28DECLARE_GLOBAL_DATA_PTR;
29
30/*
31 * The ATSHA204A uses an (to me) unknown CRC-16 algorithm.
32 * The Reveng CRC-16 catalogue does not contain it.
33 *
34 * Because in Atmel's documentation only a primitive implementation
35 * can be found, I have implemented this one with lookup table.
36 */
37
38/*
39 * This is the code that computes the table below:
40 *
41 * int i, j;
42 * for (i = 0; i < 256; ++i) {
43 * u8 c = 0;
44 * for (j = 0; j < 8; ++j) {
45 * c = (c << 1) | ((i >> j) & 1);
46 * }
47 * bitreverse_table[i] = c;
48 * }
49 */
50
51static u8 const bitreverse_table[256] = {
52 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
53 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
54 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
55 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
56 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
57 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
58 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
59 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
60 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
61 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
62 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
63 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
64 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
65 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
66 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
67 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
68 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
69 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
70 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
71 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
72 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
73 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
74 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
75 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
76 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
77 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
78 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
79 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
80 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
81 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
82 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
83 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
84};
85
86/*
87 * This is the code that computes the table below:
88 *
89 * int i, j;
90 * for (i = 0; i < 256; ++i) {
91 * u16 c = i << 8;
92 * for (j = 0; j < 8; ++j) {
93 * int b = c >> 15;
94 * c <<= 1;
95 * if (b)
96 * c ^= 0x8005;
97 * }
98 * crc16_table[i] = c;
99 * }
100 */
101static u16 const crc16_table[256] = {
102 0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011,
103 0x8033, 0x0036, 0x003c, 0x8039, 0x0028, 0x802d, 0x8027, 0x0022,
104 0x8063, 0x0066, 0x006c, 0x8069, 0x0078, 0x807d, 0x8077, 0x0072,
105 0x0050, 0x8055, 0x805f, 0x005a, 0x804b, 0x004e, 0x0044, 0x8041,
106 0x80c3, 0x00c6, 0x00cc, 0x80c9, 0x00d8, 0x80dd, 0x80d7, 0x00d2,
107 0x00f0, 0x80f5, 0x80ff, 0x00fa, 0x80eb, 0x00ee, 0x00e4, 0x80e1,
108 0x00a0, 0x80a5, 0x80af, 0x00aa, 0x80bb, 0x00be, 0x00b4, 0x80b1,
109 0x8093, 0x0096, 0x009c, 0x8099, 0x0088, 0x808d, 0x8087, 0x0082,
110 0x8183, 0x0186, 0x018c, 0x8189, 0x0198, 0x819d, 0x8197, 0x0192,
111 0x01b0, 0x81b5, 0x81bf, 0x01ba, 0x81ab, 0x01ae, 0x01a4, 0x81a1,
112 0x01e0, 0x81e5, 0x81ef, 0x01ea, 0x81fb, 0x01fe, 0x01f4, 0x81f1,
113 0x81d3, 0x01d6, 0x01dc, 0x81d9, 0x01c8, 0x81cd, 0x81c7, 0x01c2,
114 0x0140, 0x8145, 0x814f, 0x014a, 0x815b, 0x015e, 0x0154, 0x8151,
115 0x8173, 0x0176, 0x017c, 0x8179, 0x0168, 0x816d, 0x8167, 0x0162,
116 0x8123, 0x0126, 0x012c, 0x8129, 0x0138, 0x813d, 0x8137, 0x0132,
117 0x0110, 0x8115, 0x811f, 0x011a, 0x810b, 0x010e, 0x0104, 0x8101,
118 0x8303, 0x0306, 0x030c, 0x8309, 0x0318, 0x831d, 0x8317, 0x0312,
119 0x0330, 0x8335, 0x833f, 0x033a, 0x832b, 0x032e, 0x0324, 0x8321,
120 0x0360, 0x8365, 0x836f, 0x036a, 0x837b, 0x037e, 0x0374, 0x8371,
121 0x8353, 0x0356, 0x035c, 0x8359, 0x0348, 0x834d, 0x8347, 0x0342,
122 0x03c0, 0x83c5, 0x83cf, 0x03ca, 0x83db, 0x03de, 0x03d4, 0x83d1,
123 0x83f3, 0x03f6, 0x03fc, 0x83f9, 0x03e8, 0x83ed, 0x83e7, 0x03e2,
124 0x83a3, 0x03a6, 0x03ac, 0x83a9, 0x03b8, 0x83bd, 0x83b7, 0x03b2,
125 0x0390, 0x8395, 0x839f, 0x039a, 0x838b, 0x038e, 0x0384, 0x8381,
126 0x0280, 0x8285, 0x828f, 0x028a, 0x829b, 0x029e, 0x0294, 0x8291,
127 0x82b3, 0x02b6, 0x02bc, 0x82b9, 0x02a8, 0x82ad, 0x82a7, 0x02a2,
128 0x82e3, 0x02e6, 0x02ec, 0x82e9, 0x02f8, 0x82fd, 0x82f7, 0x02f2,
129 0x02d0, 0x82d5, 0x82df, 0x02da, 0x82cb, 0x02ce, 0x02c4, 0x82c1,
130 0x8243, 0x0246, 0x024c, 0x8249, 0x0258, 0x825d, 0x8257, 0x0252,
131 0x0270, 0x8275, 0x827f, 0x027a, 0x826b, 0x026e, 0x0264, 0x8261,
132 0x0220, 0x8225, 0x822f, 0x022a, 0x823b, 0x023e, 0x0234, 0x8231,
133 0x8213, 0x0216, 0x021c, 0x8219, 0x0208, 0x820d, 0x8207, 0x0202,
134};
135
136static inline u16 crc16_byte(u16 crc, const u8 data)
137{
138 u16 t = crc16_table[((crc >> 8) ^ bitreverse_table[data]) & 0xff];
139 return ((crc << 8) ^ t);
140}
141
142static u16 atsha204a_crc16(const u8 *buffer, size_t len)
143{
144 u16 crc = 0;
145
146 while (len--)
147 crc = crc16_byte(crc, *buffer++);
148
149 return cpu_to_le16(crc);
150}
151
152static int atsha204a_send(struct udevice *dev, const u8 *buf, u8 len)
153{
154 fdt_addr_t *priv = dev_get_priv(dev);
155 struct i2c_msg msg;
156
157 msg.addr = *priv;
158 msg.flags = I2C_M_STOP;
159 msg.len = len;
160 msg.buf = (u8 *) buf;
161
162 return dm_i2c_xfer(dev, &msg, 1);
163}
164
165static int atsha204a_recv(struct udevice *dev, u8 *buf, u8 len)
166{
167 fdt_addr_t *priv = dev_get_priv(dev);
168 struct i2c_msg msg;
169
170 msg.addr = *priv;
171 msg.flags = I2C_M_RD | I2C_M_STOP;
172 msg.len = len;
173 msg.buf = (u8 *) buf;
174
175 return dm_i2c_xfer(dev, &msg, 1);
176}
177
178static int atsha204a_recv_resp(struct udevice *dev,
179 struct atsha204a_resp *resp)
180{
181 int res;
182 u16 resp_crc, computed_crc;
183 u8 *p = (u8 *) resp;
184
185 res = atsha204a_recv(dev, p, 4);
186 if (res)
187 return res;
188
189 if (resp->length > 4) {
190 if (resp->length > sizeof(*resp))
191 return -EMSGSIZE;
192
193 res = atsha204a_recv(dev, p + 4, resp->length - 4);
194 if (res)
195 return res;
196 }
197
198 resp_crc = (u16) p[resp->length - 2]
199 | (((u16) p[resp->length - 1]) << 8);
200 computed_crc = atsha204a_crc16(p, resp->length - 2);
201
202 if (resp_crc != computed_crc) {
203 debug("Invalid checksum in ATSHA204A response\n");
204 return -EBADMSG;
205 }
206
207 return 0;
208}
209
210int atsha204a_wakeup(struct udevice *dev)
211{
212 u8 req[4];
213 struct atsha204a_resp resp;
214 int try, res;
215
216 debug("Waking up ATSHA204A\n");
217
218 for (try = 1; try <= 10; ++try) {
219 debug("Try %i... ", try);
220
221 memset(req, 0, 4);
222 res = atsha204a_send(dev, req, 4);
223 if (res) {
224 debug("failed on I2C send, trying again\n");
225 continue;
226 }
227
228 udelay(ATSHA204A_TWLO);
229
230 res = atsha204a_recv_resp(dev, &resp);
231 if (res) {
232 debug("failed on receiving response, ending\n");
233 return res;
234 }
235
236 if (resp.code != ATSHA204A_STATUS_AFTER_WAKE) {
237 debug ("failed (responce code = %02x), ending\n",
238 resp.code);
239 return -EBADMSG;
240 }
241
242 debug("success\n");
243 break;
244 }
245
246 return 0;
247}
248
249int atsha204a_idle(struct udevice *dev)
250{
251 int res;
252 u8 req = ATSHA204A_FUNC_IDLE;
253
254 res = atsha204a_send(dev, &req, 1);
255 if (res)
256 debug("Failed putting ATSHA204A idle\n");
257 return res;
258}
259
260int atsha204a_sleep(struct udevice *dev)
261{
262 int res;
263 u8 req = ATSHA204A_FUNC_IDLE;
264
265 res = atsha204a_send(dev, &req, 1);
266 if (res)
267 debug("Failed putting ATSHA204A to sleep\n");
268 return res;
269}
270
271static int atsha204a_transaction(struct udevice *dev, struct atsha204a_req *req,
272 struct atsha204a_resp *resp)
273{
274 int res, timeout = ATSHA204A_TRANSACTION_TIMEOUT;
275
276 res = atsha204a_send(dev, (u8 *) req, req->length + 1);
277 if (res) {
278 debug("ATSHA204A transaction send failed\n");
279 return -EBUSY;
280 }
281
282 do {
283 res = atsha204a_recv_resp(dev, resp);
284 if (!res || res == -EMSGSIZE || res == -EBADMSG)
285 break;
286
287 debug("ATSHA204A transaction polling for response "
288 "(timeout = %d)\n", timeout);
289
290 udelay(ATSHA204A_EXECTIME);
291 timeout -= ATSHA204A_EXECTIME;
292 } while (timeout > 0);
293
294 if (timeout <= 0) {
295 debug("ATSHA204A transaction timed out\n");
296 return -ETIMEDOUT;
297 }
298
299 return res;
300}
301
302static void atsha204a_req_crc32(struct atsha204a_req *req)
303{
304 u8 *p = (u8 *) req;
305 u16 computed_crc;
306 u16 *crc_ptr = (u16 *) &p[req->length - 1];
307
308 /* The buffer to crc16 starts at byte 1, not 0 */
309 computed_crc = atsha204a_crc16(p + 1, req->length - 2);
310
311 *crc_ptr = cpu_to_le16(computed_crc);
312}
313
314int atsha204a_read(struct udevice *dev, enum atsha204a_zone zone, bool read32,
315 u16 addr, u8 *buffer)
316{
317 int res, retry = ATSHA204A_TRANSACTION_RETRY;
318 struct atsha204a_req req;
319 struct atsha204a_resp resp;
320
321 req.function = ATSHA204A_FUNC_COMMAND;
322 req.length = 7;
323 req.command = ATSHA204A_CMD_READ;
324
325 req.param1 = (u8) zone;
326 if (read32)
327 req.param1 |= 0x80;
328
329 req.param2 = cpu_to_le16(addr);
330
331 atsha204a_req_crc32(&req);
332
333 do {
334 res = atsha204a_transaction(dev, &req, &resp);
335 if (!res)
336 break;
337
338 debug("ATSHA204A read retry (%d)\n", retry);
339 retry--;
340 atsha204a_wakeup(dev);
341 } while (retry >= 0);
342
343 if (res) {
344 debug("ATSHA204A read failed\n");
345 return res;
346 }
347
348 if (resp.length != (read32 ? 32 : 4) + 3) {
349 debug("ATSHA204A read bad response length (%d)\n",
350 resp.length);
351 return -EBADMSG;
352 }
353
354 memcpy(buffer, ((u8 *) &resp) + 1, read32 ? 32 : 4);
355
356 return 0;
357}
358
359int atsha204a_get_random(struct udevice *dev, u8 *buffer, size_t max)
360{
361 int res;
362 struct atsha204a_req req;
363 struct atsha204a_resp resp;
364
365 req.function = ATSHA204A_FUNC_COMMAND;
366 req.length = 7;
367 req.command = ATSHA204A_CMD_RANDOM;
368
369 req.param1 = 1;
370 req.param2 = 0;
371
372 /* We do not have to compute the checksum dynamically */
373 req.data[0] = 0x27;
374 req.data[1] = 0x47;
375
376 res = atsha204a_transaction(dev, &req, &resp);
377 if (res) {
378 debug("ATSHA204A random transaction failed\n");
379 return res;
380 }
381
382 memcpy(buffer, ((u8 *) &resp) + 1, max >= 32 ? 32 : max);
383 return 0;
384}
385
Simon Glassaad29ae2020-12-03 16:55:21 -0700386static int atsha204a_of_to_plat(struct udevice *dev)
Marek Behúnef2b6b12017-06-09 19:28:44 +0200387{
388 fdt_addr_t *priv = dev_get_priv(dev);
389 fdt_addr_t addr;
390
391 addr = fdtdec_get_addr(gd->fdt_blob, dev_of_offset(dev), "reg");
392 if (addr == FDT_ADDR_T_NONE) {
393 debug("Can't get ATSHA204A I2C base address\n");
394 return -ENXIO;
395 }
396
397 *priv = addr;
398 return 0;
399}
400
401static const struct udevice_id atsha204a_ids[] = {
402 { .compatible = "atmel,atsha204a" },
403 { }
404};
405
406U_BOOT_DRIVER(atsha204) = {
407 .name = "atsha204",
408 .id = UCLASS_MISC,
409 .of_match = atsha204a_ids,
Simon Glassaad29ae2020-12-03 16:55:21 -0700410 .of_to_plat = atsha204a_of_to_plat,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700411 .priv_auto = sizeof(fdt_addr_t),
Marek Behúnef2b6b12017-06-09 19:28:44 +0200412};