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