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