uboot/drivers/misc/atsha204a-i2c.c
<<
>>
Prefs
   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
  24DECLARE_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
  47static 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 */
  97static 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
 132static 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
 138static 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
 148static 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
 161static 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
 174static 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
 206int 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
 245int 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
 256int 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
 267static 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
 298static 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
 310int 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
 355int 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
 382static 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
 397static const struct udevice_id atsha204a_ids[] = {
 398        { .compatible = "atmel,atsha204a" },
 399        { }
 400};
 401
 402U_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