uboot/drivers/misc/cros_ec_spi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Chromium OS cros_ec driver - SPI 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 <cros_ec.h>
  17#include <dm.h>
  18#include <errno.h>
  19#include <spi.h>
  20
  21int cros_ec_spi_packet(struct udevice *udev, int out_bytes, int in_bytes)
  22{
  23        struct cros_ec_dev *dev = dev_get_uclass_priv(udev);
  24        struct spi_slave *slave = dev_get_parent_priv(dev->dev);
  25        ulong start;
  26        uint8_t byte;
  27        int rv;
  28
  29        /* Do the transfer */
  30        if (spi_claim_bus(slave)) {
  31                debug("%s: Cannot claim SPI bus\n", __func__);
  32                return -1;
  33        }
  34
  35        rv = spi_xfer(slave, out_bytes * 8, dev->dout, NULL, SPI_XFER_BEGIN);
  36        if (rv)
  37                goto done;
  38        start = get_timer(0);
  39        while (1) {
  40                rv = spi_xfer(slave, 8, NULL, &byte, 0);
  41                if (byte == SPI_PREAMBLE_END_BYTE)
  42                        break;
  43                if (rv)
  44                        goto done;
  45                if (get_timer(start) > 100) {
  46                        rv = -ETIMEDOUT;
  47                        goto done;
  48                }
  49        }
  50
  51        rv = spi_xfer(slave, in_bytes * 8, NULL, dev->din, 0);
  52done:
  53        spi_xfer(slave, 0, NULL, NULL, SPI_XFER_END);
  54        spi_release_bus(slave);
  55
  56        if (rv) {
  57                debug("%s: Cannot complete SPI transfer\n", __func__);
  58                return -1;
  59        }
  60
  61        return in_bytes;
  62}
  63
  64/**
  65 * Send a command to a LPC CROS_EC device and return the reply.
  66 *
  67 * The device's internal input/output buffers are used.
  68 *
  69 * @param dev           CROS_EC device
  70 * @param cmd           Command to send (EC_CMD_...)
  71 * @param cmd_version   Version of command to send (EC_VER_...)
  72 * @param dout          Output data (may be NULL If dout_len=0)
  73 * @param dout_len      Size of output data in bytes
  74 * @param dinp          Returns pointer to response data. This will be
  75 *                      untouched unless we return a value > 0.
  76 * @param din_len       Maximum size of response in bytes
  77 * @return number of bytes in response, or -1 on error
  78 */
  79int cros_ec_spi_command(struct udevice *udev, uint8_t cmd, int cmd_version,
  80                     const uint8_t *dout, int dout_len,
  81                     uint8_t **dinp, int din_len)
  82{
  83        struct cros_ec_dev *dev = dev_get_uclass_priv(udev);
  84        struct spi_slave *slave = dev_get_parent_priv(dev->dev);
  85        int in_bytes = din_len + 4;     /* status, length, checksum, trailer */
  86        uint8_t *out;
  87        uint8_t *p;
  88        int csum, len;
  89        int rv;
  90
  91        if (dev->protocol_version != 2) {
  92                debug("%s: Unsupported EC protcol version %d\n",
  93                      __func__, dev->protocol_version);
  94                return -1;
  95        }
  96
  97        /*
  98         * Sanity-check input size to make sure it plus transaction overhead
  99         * fits in the internal device buffer.
 100         */
 101        if (in_bytes > sizeof(dev->din)) {
 102                debug("%s: Cannot receive %d bytes\n", __func__, din_len);
 103                return -1;
 104        }
 105
 106        /* We represent message length as a byte */
 107        if (dout_len > 0xff) {
 108                debug("%s: Cannot send %d bytes\n", __func__, dout_len);
 109                return -1;
 110        }
 111
 112        /*
 113         * Clear input buffer so we don't get false hits for MSG_HEADER
 114         */
 115        memset(dev->din, '\0', in_bytes);
 116
 117        if (spi_claim_bus(slave)) {
 118                debug("%s: Cannot claim SPI bus\n", __func__);
 119                return -1;
 120        }
 121
 122        out = dev->dout;
 123        out[0] = EC_CMD_VERSION0 + cmd_version;
 124        out[1] = cmd;
 125        out[2] = (uint8_t)dout_len;
 126        memcpy(out + 3, dout, dout_len);
 127        csum = cros_ec_calc_checksum(out, 3)
 128               + cros_ec_calc_checksum(dout, dout_len);
 129        out[3 + dout_len] = (uint8_t)csum;
 130
 131        /*
 132         * Send output data and receive input data starting such that the
 133         * message body will be dword aligned.
 134         */
 135        p = dev->din + sizeof(int64_t) - 2;
 136        len = dout_len + 4;
 137        cros_ec_dump_data("out", cmd, out, len);
 138        rv = spi_xfer(slave, max(len, in_bytes) * 8, out, p,
 139                      SPI_XFER_BEGIN | SPI_XFER_END);
 140
 141        spi_release_bus(slave);
 142
 143        if (rv) {
 144                debug("%s: Cannot complete SPI transfer\n", __func__);
 145                return -1;
 146        }
 147
 148        len = min((int)p[1], din_len);
 149        cros_ec_dump_data("in", -1, p, len + 3);
 150
 151        /* Response code is first byte of message */
 152        if (p[0] != EC_RES_SUCCESS) {
 153                printf("%s: Returned status %d\n", __func__, p[0]);
 154                return -(int)(p[0]);
 155        }
 156
 157        /* Check checksum */
 158        csum = cros_ec_calc_checksum(p, len + 2);
 159        if (csum != p[len + 2]) {
 160                debug("%s: Invalid checksum rx %#02x, calced %#02x\n", __func__,
 161                      p[2 + len], csum);
 162                return -1;
 163        }
 164
 165        /* Anything else is the response data */
 166        *dinp = p + 2;
 167
 168        return len;
 169}
 170
 171static int cros_ec_probe(struct udevice *dev)
 172{
 173        return cros_ec_register(dev);
 174}
 175
 176static struct dm_cros_ec_ops cros_ec_ops = {
 177        .packet = cros_ec_spi_packet,
 178        .command = cros_ec_spi_command,
 179};
 180
 181static const struct udevice_id cros_ec_ids[] = {
 182        { .compatible = "google,cros-ec-spi" },
 183        { }
 184};
 185
 186U_BOOT_DRIVER(cros_ec_spi) = {
 187        .name           = "cros_ec_spi",
 188        .id             = UCLASS_CROS_EC,
 189        .of_match       = cros_ec_ids,
 190        .probe          = cros_ec_probe,
 191        .ops            = &cros_ec_ops,
 192};
 193