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