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