linux/drivers/platform/chrome/wilco_ec/mailbox.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Mailbox interface for Wilco Embedded Controller
   4 *
   5 * Copyright 2018 Google LLC
   6 *
   7 * The Wilco EC is similar to a typical ChromeOS embedded controller.
   8 * It uses the same MEC based low-level communication and a similar
   9 * protocol, but with some important differences.  The EC firmware does
  10 * not support the same mailbox commands so it is not registered as a
  11 * cros_ec device type.
  12 *
  13 * Most messages follow a standard format, but there are some exceptions
  14 * and an interface is provided to do direct/raw transactions that do not
  15 * make assumptions about byte placement.
  16 */
  17
  18#include <linux/delay.h>
  19#include <linux/device.h>
  20#include <linux/io.h>
  21#include <linux/platform_data/wilco-ec.h>
  22#include <linux/platform_device.h>
  23
  24#include "../cros_ec_lpc_mec.h"
  25
  26/* Version of mailbox interface */
  27#define EC_MAILBOX_VERSION              0
  28
  29/* Command to start mailbox transaction */
  30#define EC_MAILBOX_START_COMMAND        0xda
  31
  32/* Version of EC protocol */
  33#define EC_MAILBOX_PROTO_VERSION        3
  34
  35/* Number of header bytes to be counted as data bytes */
  36#define EC_MAILBOX_DATA_EXTRA           2
  37
  38/* Maximum timeout */
  39#define EC_MAILBOX_TIMEOUT              HZ
  40
  41/* EC response flags */
  42#define EC_CMDR_DATA            BIT(0)  /* Data ready for host to read */
  43#define EC_CMDR_PENDING         BIT(1)  /* Write pending to EC */
  44#define EC_CMDR_BUSY            BIT(2)  /* EC is busy processing a command */
  45#define EC_CMDR_CMD             BIT(3)  /* Last host write was a command */
  46
  47/**
  48 * wilco_ec_response_timed_out() - Wait for EC response.
  49 * @ec: EC device.
  50 *
  51 * Return: true if EC timed out, false if EC did not time out.
  52 */
  53static bool wilco_ec_response_timed_out(struct wilco_ec_device *ec)
  54{
  55        unsigned long timeout = jiffies + EC_MAILBOX_TIMEOUT;
  56
  57        do {
  58                if (!(inb(ec->io_command->start) &
  59                      (EC_CMDR_PENDING | EC_CMDR_BUSY)))
  60                        return false;
  61                usleep_range(100, 200);
  62        } while (time_before(jiffies, timeout));
  63
  64        return true;
  65}
  66
  67/**
  68 * wilco_ec_checksum() - Compute 8-bit checksum over data range.
  69 * @data: Data to checksum.
  70 * @size: Number of bytes to checksum.
  71 *
  72 * Return: 8-bit checksum of provided data.
  73 */
  74static u8 wilco_ec_checksum(const void *data, size_t size)
  75{
  76        u8 *data_bytes = (u8 *)data;
  77        u8 checksum = 0;
  78        size_t i;
  79
  80        for (i = 0; i < size; i++)
  81                checksum += data_bytes[i];
  82
  83        return checksum;
  84}
  85
  86/**
  87 * wilco_ec_prepare() - Prepare the request structure for the EC.
  88 * @msg: EC message with request information.
  89 * @rq: EC request structure to fill.
  90 */
  91static void wilco_ec_prepare(struct wilco_ec_message *msg,
  92                             struct wilco_ec_request *rq)
  93{
  94        memset(rq, 0, sizeof(*rq));
  95        rq->struct_version = EC_MAILBOX_PROTO_VERSION;
  96        rq->mailbox_id = msg->type;
  97        rq->mailbox_version = EC_MAILBOX_VERSION;
  98        rq->data_size = msg->request_size;
  99
 100        /* Checksum header and data */
 101        rq->checksum = wilco_ec_checksum(rq, sizeof(*rq));
 102        rq->checksum += wilco_ec_checksum(msg->request_data, msg->request_size);
 103        rq->checksum = -rq->checksum;
 104}
 105
 106/**
 107 * wilco_ec_transfer() - Perform actual data transfer.
 108 * @ec: EC device.
 109 * @msg: EC message data for request and response.
 110 * @rq: Filled in request structure
 111 *
 112 * Context: ec->mailbox_lock should be held while using this function.
 113 * Return: number of bytes received or negative error code on failure.
 114 */
 115static int wilco_ec_transfer(struct wilco_ec_device *ec,
 116                             struct wilco_ec_message *msg,
 117                             struct wilco_ec_request *rq)
 118{
 119        struct wilco_ec_response *rs;
 120        u8 checksum;
 121        u8 flag;
 122        size_t size;
 123
 124        /* Write request header, then data */
 125        cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, 0, sizeof(*rq), (u8 *)rq);
 126        cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, sizeof(*rq), msg->request_size,
 127                                 msg->request_data);
 128
 129        /* Start the command */
 130        outb(EC_MAILBOX_START_COMMAND, ec->io_command->start);
 131
 132        /* For some commands (eg shutdown) the EC will not respond, that's OK */
 133        if (msg->flags & WILCO_EC_FLAG_NO_RESPONSE) {
 134                dev_dbg(ec->dev, "EC does not respond to this command\n");
 135                return 0;
 136        }
 137
 138        /* Wait for it to complete */
 139        if (wilco_ec_response_timed_out(ec)) {
 140                dev_dbg(ec->dev, "response timed out\n");
 141                return -ETIMEDOUT;
 142        }
 143
 144        /* Check result */
 145        flag = inb(ec->io_data->start);
 146        if (flag) {
 147                dev_dbg(ec->dev, "bad response: 0x%02x\n", flag);
 148                return -EIO;
 149        }
 150
 151        /*
 152         * The EC always returns either EC_MAILBOX_DATA_SIZE or
 153         * EC_MAILBOX_DATA_SIZE_EXTENDED bytes of data, so we need to
 154         * calculate the checksum on **all** of this data, even if we
 155         * won't use all of it.
 156         */
 157        if (msg->flags & WILCO_EC_FLAG_EXTENDED_DATA)
 158                size = EC_MAILBOX_DATA_SIZE_EXTENDED;
 159        else
 160                size = EC_MAILBOX_DATA_SIZE;
 161
 162        /* Read back response */
 163        rs = ec->data_buffer;
 164        checksum = cros_ec_lpc_io_bytes_mec(MEC_IO_READ, 0,
 165                                            sizeof(*rs) + size, (u8 *)rs);
 166        if (checksum) {
 167                dev_dbg(ec->dev, "bad packet checksum 0x%02x\n", rs->checksum);
 168                return -EBADMSG;
 169        }
 170
 171        if (rs->result) {
 172                dev_dbg(ec->dev, "EC reported failure: 0x%02x\n", rs->result);
 173                return -EBADMSG;
 174        }
 175
 176        if (rs->data_size != size) {
 177                dev_dbg(ec->dev, "unexpected packet size (%u != %zu)",
 178                        rs->data_size, size);
 179                return -EMSGSIZE;
 180        }
 181
 182        if (rs->data_size < msg->response_size) {
 183                dev_dbg(ec->dev, "EC didn't return enough data (%u < %zu)",
 184                        rs->data_size, msg->response_size);
 185                return -EMSGSIZE;
 186        }
 187
 188        memcpy(msg->response_data, rs->data, msg->response_size);
 189
 190        return rs->data_size;
 191}
 192
 193/**
 194 * wilco_ec_mailbox() - Send EC request and receive EC response.
 195 * @ec: EC device.
 196 * @msg: EC message data for request and response.
 197 *
 198 * On entry msg->type, msg->request_size, and msg->request_data should all be
 199 * filled in. If desired, msg->flags can be set.
 200 *
 201 * If a response is expected, msg->response_size should be set, and
 202 * msg->response_data should point to a buffer with enough space. On exit
 203 * msg->response_data will be filled.
 204 *
 205 * Return: number of bytes received or negative error code on failure.
 206 */
 207int wilco_ec_mailbox(struct wilco_ec_device *ec, struct wilco_ec_message *msg)
 208{
 209        struct wilco_ec_request *rq;
 210        int ret;
 211
 212        dev_dbg(ec->dev, "type=%04x flags=%02x rslen=%zu rqlen=%zu\n",
 213                msg->type, msg->flags, msg->response_size, msg->request_size);
 214
 215        mutex_lock(&ec->mailbox_lock);
 216        /* Prepare request packet */
 217        rq = ec->data_buffer;
 218        wilco_ec_prepare(msg, rq);
 219
 220        ret = wilco_ec_transfer(ec, msg, rq);
 221        mutex_unlock(&ec->mailbox_lock);
 222
 223        return ret;
 224
 225}
 226EXPORT_SYMBOL_GPL(wilco_ec_mailbox);
 227