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