uboot/drivers/misc/cros_ec_i2c.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Chromium OS cros_ec driver - I2C interface
   4 *
   5 * Copyright (c) 2012 The Chromium OS Authors.
   6 */
   7
   8/*
   9 * The Matrix Keyboard Protocol driver handles talking to the keyboard
  10 * controller chip. Mostly this is for keyboard functions, but some other
  11 * things have slipped in, so we provide generic services to talk to the
  12 * KBC.
  13 */
  14
  15#include <common.h>
  16#include <dm.h>
  17#include <i2c.h>
  18#include <cros_ec.h>
  19
  20#ifdef DEBUG_TRACE
  21#define debug_trace(fmt, b...)  debug(fmt, #b)
  22#else
  23#define debug_trace(fmt, b...)
  24#endif
  25
  26/**
  27 * Request format for protocol v3
  28 * byte 0       0xda (EC_COMMAND_PROTOCOL_3)
  29 * byte 1-8     struct ec_host_request
  30 * byte 10-     response data
  31 */
  32struct ec_host_request_i2c {
  33        /* Always 0xda to backward compatible with v2 struct */
  34        uint8_t  command_protocol;
  35        struct ec_host_request ec_request;
  36} __packed;
  37
  38/*
  39 * Response format for protocol v3
  40 * byte 0       result code
  41 * byte 1       packet_length
  42 * byte 2-9     struct ec_host_response
  43 * byte 10-     response data
  44 */
  45struct ec_host_response_i2c {
  46        uint8_t result;
  47        uint8_t packet_length;
  48        struct ec_host_response ec_response;
  49} __packed;
  50
  51static int cros_ec_i2c_packet(struct udevice *udev, int out_bytes, int in_bytes)
  52{
  53        struct cros_ec_dev *dev = dev_get_uclass_priv(udev);
  54        struct dm_i2c_chip *chip = dev_get_parent_platdata(udev);
  55        struct ec_host_request_i2c *ec_request_i2c =
  56                (struct ec_host_request_i2c *)dev->dout;
  57        struct ec_host_response_i2c *ec_response_i2c =
  58                (struct ec_host_response_i2c *)dev->din;
  59        struct i2c_msg i2c_msg[2];
  60        int ret;
  61
  62        i2c_msg[0].addr = chip->chip_addr;
  63        i2c_msg[0].flags = 0;
  64        i2c_msg[1].addr = chip->chip_addr;
  65        i2c_msg[1].flags = I2C_M_RD;
  66
  67        /* one extra byte, to indicate v3 */
  68        i2c_msg[0].len = out_bytes + 1;
  69        i2c_msg[0].buf = dev->dout;
  70
  71        /* stitch on EC_COMMAND_PROTOCOL_3 */
  72        memmove(&ec_request_i2c->ec_request, dev->dout, out_bytes);
  73        ec_request_i2c->command_protocol = EC_COMMAND_PROTOCOL_3;
  74
  75        /* two extra bytes for v3 */
  76        i2c_msg[1].len = in_bytes + 2;
  77        i2c_msg[1].buf = dev->din;
  78
  79        ret = dm_i2c_xfer(udev, &i2c_msg[0], 2);
  80        if (ret) {
  81                printf("%s: Could not execute transfer: %d\n", __func__, ret);
  82                return ret;
  83        }
  84
  85        /* When we send a v3 request to v2 ec, ec won't recognize the 0xda
  86         * (EC_COMMAND_PROTOCOL_3) and will return with status
  87         * EC_RES_INVALID_COMMAND with zero data length
  88         *
  89         * In case of invalid command for v3 protocol the data length
  90         * will be at least sizeof(struct ec_host_response)
  91         */
  92        if (ec_response_i2c->result == EC_RES_INVALID_COMMAND &&
  93            ec_response_i2c->packet_length == 0)
  94                return -EPROTONOSUPPORT;
  95
  96        if (ec_response_i2c->packet_length < sizeof(struct ec_host_response)) {
  97                printf("%s: response of %u bytes too short; not a full hdr\n",
  98                       __func__, ec_response_i2c->packet_length);
  99                return -EBADMSG;
 100        }
 101
 102
 103        /* drop result and packet_len */
 104        memmove(dev->din, &ec_response_i2c->ec_response, in_bytes);
 105
 106        return in_bytes;
 107}
 108
 109static int cros_ec_i2c_command(struct udevice *udev, uint8_t cmd,
 110                               int cmd_version, const uint8_t *dout,
 111                               int dout_len, uint8_t **dinp, int din_len)
 112{
 113        struct cros_ec_dev *dev = dev_get_uclass_priv(udev);
 114        struct dm_i2c_chip *chip = dev_get_parent_platdata(udev);
 115        struct i2c_msg i2c_msg[2];
 116        /* version8, cmd8, arglen8, out8[dout_len], csum8 */
 117        int out_bytes = dout_len + 4;
 118        /* response8, arglen8, in8[din_len], checksum8 */
 119        int in_bytes = din_len + 3;
 120        uint8_t *ptr;
 121        /* Receive input data, so that args will be dword aligned */
 122        uint8_t *in_ptr;
 123        int len, csum, ret;
 124
 125        /*
 126         * Sanity-check I/O sizes given transaction overhead in internal
 127         * buffers.
 128         */
 129        if (out_bytes > sizeof(dev->dout)) {
 130                debug("%s: Cannot send %d bytes\n", __func__, dout_len);
 131                return -1;
 132        }
 133        if (in_bytes > sizeof(dev->din)) {
 134                debug("%s: Cannot receive %d bytes\n", __func__, din_len);
 135                return -1;
 136        }
 137        assert(dout_len >= 0);
 138        assert(dinp);
 139
 140        i2c_msg[0].addr = chip->chip_addr;
 141        i2c_msg[0].len = out_bytes;
 142        i2c_msg[0].buf = dev->dout;
 143        i2c_msg[0].flags = 0;
 144
 145        /*
 146         * Copy command and data into output buffer so we can do a single I2C
 147         * burst transaction.
 148         */
 149        ptr = dev->dout;
 150
 151        /*
 152         * in_ptr starts of pointing to a dword-aligned input data buffer.
 153         * We decrement it back by the number of header bytes we expect to
 154         * receive, so that the first parameter of the resulting input data
 155         * will be dword aligned.
 156         */
 157        in_ptr = dev->din + sizeof(int64_t);
 158
 159        if (dev->protocol_version != 2) {
 160                /* Something we don't support */
 161                debug("%s: Protocol version %d unsupported\n",
 162                      __func__, dev->protocol_version);
 163                return -1;
 164        }
 165
 166        *ptr++ = EC_CMD_VERSION0 + cmd_version;
 167        *ptr++ = cmd;
 168        *ptr++ = dout_len;
 169        in_ptr -= 2;    /* Expect status, length bytes */
 170
 171        memcpy(ptr, dout, dout_len);
 172        ptr += dout_len;
 173
 174        *ptr++ = (uint8_t)
 175                cros_ec_calc_checksum(dev->dout, dout_len + 3);
 176
 177        i2c_msg[1].addr = chip->chip_addr;
 178        i2c_msg[1].len = in_bytes;
 179        i2c_msg[1].buf = in_ptr;
 180        i2c_msg[1].flags = I2C_M_RD;
 181
 182        /* Send output data */
 183        cros_ec_dump_data("out", -1, dev->dout, out_bytes);
 184
 185        ret = dm_i2c_xfer(udev, &i2c_msg[0], 2);
 186        if (ret) {
 187                debug("%s: Could not execute transfer to %s\n", __func__,
 188                      udev->name);
 189                ret = -1;
 190        }
 191
 192        if (*in_ptr != EC_RES_SUCCESS) {
 193                debug("%s: Received bad result code %d\n", __func__, *in_ptr);
 194                return -(int)*in_ptr;
 195        }
 196
 197        len = in_ptr[1];
 198        if (len + 3 > sizeof(dev->din)) {
 199                debug("%s: Received length %#02x too large\n",
 200                      __func__, len);
 201                return -1;
 202        }
 203        csum = cros_ec_calc_checksum(in_ptr, 2 + len);
 204        if (csum != in_ptr[2 + len]) {
 205                debug("%s: Invalid checksum rx %#02x, calced %#02x\n",
 206                      __func__, in_ptr[2 + din_len], csum);
 207                return -1;
 208        }
 209        din_len = min(din_len, len);
 210        cros_ec_dump_data("in", -1, in_ptr, din_len + 3);
 211
 212        /* Return pointer to dword-aligned input data, if any */
 213        *dinp = dev->din + sizeof(int64_t);
 214
 215        return din_len;
 216}
 217
 218static int cros_ec_probe(struct udevice *dev)
 219{
 220        return cros_ec_register(dev);
 221}
 222
 223static struct dm_cros_ec_ops cros_ec_ops = {
 224        .command = cros_ec_i2c_command,
 225        .packet = cros_ec_i2c_packet,
 226};
 227
 228static const struct udevice_id cros_ec_ids[] = {
 229        { .compatible = "google,cros-ec-i2c" },
 230        { }
 231};
 232
 233U_BOOT_DRIVER(cros_ec_i2c) = {
 234        .name           = "cros_ec_i2c",
 235        .id             = UCLASS_CROS_EC,
 236        .of_match       = cros_ec_ids,
 237        .probe          = cros_ec_probe,
 238        .ops            = &cros_ec_ops,
 239};
 240