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#include <log.h>
  19#include <asm/global_data.h>
  20#include <linux/delay.h>
  21#include <u-boot/crc.h>
  22
  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
 386static int atsha204a_of_to_plat(struct udevice *dev)
 387{
 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,
 410        .of_to_plat     = atsha204a_of_to_plat,
 411        .priv_auto      = sizeof(fdt_addr_t),
 412};
 413