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