1/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. 2 * Use of this source code is governed by a BSD-style license that can be 3 * found in the LICENSE file. 4 */ 5 6#include <chromiumos-platform-ec/ec_commands.h> 7 8#include <stdint.h> 9#include <stdio.h> 10#include <sys/io.h> 11#include <sys/param.h> 12#include <unistd.h> 13#include <zircon/errors.h> 14 15#define INITIAL_UDELAY 5 /* 5 us */ 16#define MAXIMUM_UDELAY 10000 /* 10 ms */ 17 18/* 19 * Wait for the EC to be unbusy. Returns 0 if unbusy, non-zero if 20 * timeout. 21 */ 22static int wait_for_ec(uint16_t status_addr, int timeout_usec) 23{ 24 int i; 25 int delay = INITIAL_UDELAY; 26 27 for (i = 0; i < timeout_usec; i += delay) { 28 /* 29 * Delay first, in case we just sent out a command but the EC 30 * hasn't raised the busy flag. However, I think this doesn't 31 * happen since the LPC commands are executed in order and the 32 * busy flag is set by hardware. Minor issue in any case, 33 * since the initial delay is very short. 34 */ 35 usleep(MIN(delay, timeout_usec - i)); 36 37 if (!(inb(status_addr) & EC_LPC_STATUS_BUSY_MASK)) 38 return 0; 39 40 /* Increase the delay interval after a few rapid checks */ 41 if (i > 20) 42 delay = MIN(delay * 2, MAXIMUM_UDELAY); 43 } 44 return -1; /* Timeout */ 45} 46 47namespace CrOsEc { 48 49zx_status_t CommandLpc3(uint16_t command, uint8_t version, 50 const void *outdata, size_t outsize, 51 void *indata, size_t insize, size_t *actual) 52{ 53 struct ec_host_request rq; 54 struct ec_host_response rs; 55 const uint8_t *d; 56 uint8_t *dout; 57 int csum = 0; 58 size_t i; 59 60 static_assert(EC_LPC_HOST_PACKET_SIZE <= UINT16_MAX, ""); 61 /* Fail if output size is too big */ 62 if (outsize + sizeof(rq) > EC_LPC_HOST_PACKET_SIZE) 63 return ZX_ERR_INVALID_ARGS; 64 65 /* Fill in request packet */ 66 /* TODO(crosbug.com/p/23825): This should be common to all protocols */ 67 rq.struct_version = EC_HOST_REQUEST_VERSION; 68 rq.checksum = 0; 69 rq.command = command; 70 rq.command_version = version; 71 rq.reserved = 0; 72 rq.data_len = static_cast<uint16_t>(outsize); 73 74 /* Copy data and start checksum */ 75 for (i = 0, d = (const uint8_t *)outdata; i < outsize; i++, d++) { 76 const uint16_t addr = static_cast<uint16_t>(EC_LPC_ADDR_HOST_PACKET + sizeof(rq) + i); 77 outb(*d, addr); 78 csum += *d; 79 } 80 81 /* Finish checksum */ 82 for (i = 0, d = (const uint8_t *)&rq; i < sizeof(rq); i++, d++) 83 csum += *d; 84 85 /* Write checksum field so the entire packet sums to 0 */ 86 rq.checksum = (uint8_t)(-csum); 87 88 /* Copy header */ 89 for (i = 0, d = (const uint8_t *)&rq; i < sizeof(rq); i++, d++) { 90 const uint16_t addr = static_cast<uint16_t>(EC_LPC_ADDR_HOST_PACKET + i); 91 outb(*d, addr); 92 } 93 94 /* Start the command */ 95 outb(EC_COMMAND_PROTOCOL_3, EC_LPC_ADDR_HOST_CMD); 96 97 if (wait_for_ec(EC_LPC_ADDR_HOST_CMD, 1000000)) { 98 return ZX_ERR_TIMED_OUT; 99 } 100 101 /* Check result */ 102 i = inb(EC_LPC_ADDR_HOST_DATA); 103 if (i) { 104 return ZX_ERR_IO; 105 } 106 107 /* Read back response header and start checksum */ 108 csum = 0; 109 for (i = 0, dout = (uint8_t *)&rs; i < sizeof(rs); i++, dout++) { 110 const uint16_t addr = static_cast<uint16_t>(EC_LPC_ADDR_HOST_PACKET + i); 111 *dout = inb(addr); 112 csum += *dout; 113 } 114 115 if (rs.struct_version != EC_HOST_RESPONSE_VERSION) { 116 return ZX_ERR_IO; 117 } 118 119 if (rs.reserved) { 120 return ZX_ERR_IO; 121 } 122 123 if (rs.data_len > insize) { 124 return ZX_ERR_BUFFER_TOO_SMALL; 125 } 126 127 /* Read back data and update checksum */ 128 for (i = 0, dout = (uint8_t *)indata; i < rs.data_len; i++, dout++) { 129 const uint16_t addr = static_cast<uint16_t>(EC_LPC_ADDR_HOST_PACKET + sizeof(rs) + i); 130 *dout = inb(addr); 131 csum += *dout; 132 } 133 134 /* Verify checksum */ 135 if ((uint8_t)csum) { 136 return ZX_ERR_IO_DATA_INTEGRITY; 137 } 138 139 /* Return actual amount of data received */ 140 *actual = rs.data_len; 141 return ZX_OK; 142} 143 144bool IsLpc3Supported() 145{ 146 int i; 147 int byte = 0xff; 148 149 /* 150 * Test if the I/O port has been configured for Chromium EC LPC 151 * interface. Chromium EC guarantees that at least one status bit will 152 * be 0, so if the command and data bytes are both 0xff, very likely 153 * that Chromium EC is not present. See crosbug.com/p/10963. 154 */ 155 byte &= inb(EC_LPC_ADDR_HOST_CMD); 156 byte &= inb(EC_LPC_ADDR_HOST_DATA); 157 if (byte == 0xff) { 158 return false; 159 } 160 161 /* 162 * Test if LPC command args are supported. 163 * 164 * The cheapest way to do this is by looking for the memory-mapped 165 * flag. This is faster than sending a new-style 'hello' command and 166 * seeing whether the EC sets the EC_HOST_ARGS_FLAG_FROM_HOST flag 167 * in args when it responds. 168 */ 169 if (inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID) != 'E' || 170 inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID + 1) != 'C') { 171 return false; 172 } 173 174 /* Check which command version we'll use */ 175 i = inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_HOST_CMD_FLAGS); 176 return !!(i & EC_HOST_CMD_FLAG_VERSION_3); 177} 178 179} // namespace CrOsEc 180