uboot/drivers/misc/cros_ec_i2c.c
<<
>>
Prefs
   1/*
   2 * Chromium OS cros_ec driver - I2C interface
   3 *
   4 * Copyright (c) 2012 The Chromium OS Authors.
   5 *
   6 * SPDX-License-Identifier:     GPL-2.0+
   7 */
   8
   9/*
  10 * The Matrix Keyboard Protocol driver handles talking to the keyboard
  11 * controller chip. Mostly this is for keyboard functions, but some other
  12 * things have slipped in, so we provide generic services to talk to the
  13 * KBC.
  14 */
  15
  16#include <common.h>
  17#include <dm.h>
  18#include <i2c.h>
  19#include <cros_ec.h>
  20
  21#ifdef DEBUG_TRACE
  22#define debug_trace(fmt, b...)  debug(fmt, #b)
  23#else
  24#define debug_trace(fmt, b...)
  25#endif
  26
  27static int cros_ec_i2c_command(struct udevice *udev, uint8_t cmd,
  28                               int cmd_version, const uint8_t *dout,
  29                               int dout_len, uint8_t **dinp, int din_len)
  30{
  31        struct cros_ec_dev *dev = dev_get_uclass_priv(udev);
  32        /* version8, cmd8, arglen8, out8[dout_len], csum8 */
  33        int out_bytes = dout_len + 4;
  34        /* response8, arglen8, in8[din_len], checksum8 */
  35        int in_bytes = din_len + 3;
  36        uint8_t *ptr;
  37        /* Receive input data, so that args will be dword aligned */
  38        uint8_t *in_ptr;
  39        int len, csum, ret;
  40
  41        /*
  42         * Sanity-check I/O sizes given transaction overhead in internal
  43         * buffers.
  44         */
  45        if (out_bytes > sizeof(dev->dout)) {
  46                debug("%s: Cannot send %d bytes\n", __func__, dout_len);
  47                return -1;
  48        }
  49        if (in_bytes > sizeof(dev->din)) {
  50                debug("%s: Cannot receive %d bytes\n", __func__, din_len);
  51                return -1;
  52        }
  53        assert(dout_len >= 0);
  54        assert(dinp);
  55
  56        /*
  57         * Copy command and data into output buffer so we can do a single I2C
  58         * burst transaction.
  59         */
  60        ptr = dev->dout;
  61
  62        /*
  63         * in_ptr starts of pointing to a dword-aligned input data buffer.
  64         * We decrement it back by the number of header bytes we expect to
  65         * receive, so that the first parameter of the resulting input data
  66         * will be dword aligned.
  67         */
  68        in_ptr = dev->din + sizeof(int64_t);
  69
  70        if (dev->protocol_version != 2) {
  71                /* Something we don't support */
  72                debug("%s: Protocol version %d unsupported\n",
  73                      __func__, dev->protocol_version);
  74                return -1;
  75        }
  76
  77        *ptr++ = EC_CMD_VERSION0 + cmd_version;
  78        *ptr++ = cmd;
  79        *ptr++ = dout_len;
  80        in_ptr -= 2;    /* Expect status, length bytes */
  81
  82        memcpy(ptr, dout, dout_len);
  83        ptr += dout_len;
  84
  85        *ptr++ = (uint8_t)
  86                cros_ec_calc_checksum(dev->dout, dout_len + 3);
  87
  88        /* Send output data */
  89        cros_ec_dump_data("out", -1, dev->dout, out_bytes);
  90        ret = dm_i2c_write(udev, 0, dev->dout, out_bytes);
  91        if (ret) {
  92                debug("%s: Cannot complete I2C write to %s\n", __func__,
  93                      udev->name);
  94                ret = -1;
  95        }
  96
  97        if (!ret) {
  98                ret = dm_i2c_read(udev, 0, in_ptr, in_bytes);
  99                if (ret) {
 100                        debug("%s: Cannot complete I2C read from %s\n",
 101                              __func__, udev->name);
 102                        ret = -1;
 103                }
 104        }
 105
 106        if (*in_ptr != EC_RES_SUCCESS) {
 107                debug("%s: Received bad result code %d\n", __func__, *in_ptr);
 108                return -(int)*in_ptr;
 109        }
 110
 111        len = in_ptr[1];
 112        if (len + 3 > sizeof(dev->din)) {
 113                debug("%s: Received length %#02x too large\n",
 114                      __func__, len);
 115                return -1;
 116        }
 117        csum = cros_ec_calc_checksum(in_ptr, 2 + len);
 118        if (csum != in_ptr[2 + len]) {
 119                debug("%s: Invalid checksum rx %#02x, calced %#02x\n",
 120                      __func__, in_ptr[2 + din_len], csum);
 121                return -1;
 122        }
 123        din_len = min(din_len, len);
 124        cros_ec_dump_data("in", -1, in_ptr, din_len + 3);
 125
 126        /* Return pointer to dword-aligned input data, if any */
 127        *dinp = dev->din + sizeof(int64_t);
 128
 129        return din_len;
 130}
 131
 132static int cros_ec_probe(struct udevice *dev)
 133{
 134        return cros_ec_register(dev);
 135}
 136
 137static struct dm_cros_ec_ops cros_ec_ops = {
 138        .command = cros_ec_i2c_command,
 139};
 140
 141static const struct udevice_id cros_ec_ids[] = {
 142        { .compatible = "google,cros-ec-i2c" },
 143        { }
 144};
 145
 146U_BOOT_DRIVER(cros_ec_i2c) = {
 147        .name           = "cros_ec_i2c",
 148        .id             = UCLASS_CROS_EC,
 149        .of_match       = cros_ec_ids,
 150        .probe          = cros_ec_probe,
 151        .ops            = &cros_ec_ops,
 152};
 153