1 /* 2 * Chromium OS cros_ec driver - I2C 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 <dm.h> 18 #include <i2c.h> 19 #include <cros_ec.h> 20 21 #ifdef DEBUG_TRACE 22 #define debug_trace(fmt, b...) debug(fmt, #b) 23 #else 24 #define debug_trace(fmt, b...) 25 #endif 26 27 static int cros_ec_i2c_command(struct udevice *udev, uint8_t cmd, 28 int cmd_version, const uint8_t *dout, 29 int dout_len, uint8_t **dinp, int din_len) 30 { 31 struct cros_ec_dev *dev = dev_get_uclass_priv(udev); 32 /* version8, cmd8, arglen8, out8[dout_len], csum8 */ 33 int out_bytes = dout_len + 4; 34 /* response8, arglen8, in8[din_len], checksum8 */ 35 int in_bytes = din_len + 3; 36 uint8_t *ptr; 37 /* Receive input data, so that args will be dword aligned */ 38 uint8_t *in_ptr; 39 int len, csum, ret; 40 41 /* 42 * Sanity-check I/O sizes given transaction overhead in internal 43 * buffers. 44 */ 45 if (out_bytes > sizeof(dev->dout)) { 46 debug("%s: Cannot send %d bytes\n", __func__, dout_len); 47 return -1; 48 } 49 if (in_bytes > sizeof(dev->din)) { 50 debug("%s: Cannot receive %d bytes\n", __func__, din_len); 51 return -1; 52 } 53 assert(dout_len >= 0); 54 assert(dinp); 55 56 /* 57 * Copy command and data into output buffer so we can do a single I2C 58 * burst transaction. 59 */ 60 ptr = dev->dout; 61 62 /* 63 * in_ptr starts of pointing to a dword-aligned input data buffer. 64 * We decrement it back by the number of header bytes we expect to 65 * receive, so that the first parameter of the resulting input data 66 * will be dword aligned. 67 */ 68 in_ptr = dev->din + sizeof(int64_t); 69 70 if (dev->protocol_version != 2) { 71 /* Something we don't support */ 72 debug("%s: Protocol version %d unsupported\n", 73 __func__, dev->protocol_version); 74 return -1; 75 } 76 77 *ptr++ = EC_CMD_VERSION0 + cmd_version; 78 *ptr++ = cmd; 79 *ptr++ = dout_len; 80 in_ptr -= 2; /* Expect status, length bytes */ 81 82 memcpy(ptr, dout, dout_len); 83 ptr += dout_len; 84 85 *ptr++ = (uint8_t) 86 cros_ec_calc_checksum(dev->dout, dout_len + 3); 87 88 /* Send output data */ 89 cros_ec_dump_data("out", -1, dev->dout, out_bytes); 90 ret = dm_i2c_write(udev, 0, dev->dout, out_bytes); 91 if (ret) { 92 debug("%s: Cannot complete I2C write to %s\n", __func__, 93 udev->name); 94 ret = -1; 95 } 96 97 if (!ret) { 98 ret = dm_i2c_read(udev, 0, in_ptr, in_bytes); 99 if (ret) { 100 debug("%s: Cannot complete I2C read from %s\n", 101 __func__, udev->name); 102 ret = -1; 103 } 104 } 105 106 if (*in_ptr != EC_RES_SUCCESS) { 107 debug("%s: Received bad result code %d\n", __func__, *in_ptr); 108 return -(int)*in_ptr; 109 } 110 111 len = in_ptr[1]; 112 if (len + 3 > sizeof(dev->din)) { 113 debug("%s: Received length %#02x too large\n", 114 __func__, len); 115 return -1; 116 } 117 csum = cros_ec_calc_checksum(in_ptr, 2 + len); 118 if (csum != in_ptr[2 + len]) { 119 debug("%s: Invalid checksum rx %#02x, calced %#02x\n", 120 __func__, in_ptr[2 + din_len], csum); 121 return -1; 122 } 123 din_len = min(din_len, len); 124 cros_ec_dump_data("in", -1, in_ptr, din_len + 3); 125 126 /* Return pointer to dword-aligned input data, if any */ 127 *dinp = dev->din + sizeof(int64_t); 128 129 return din_len; 130 } 131 132 static int cros_ec_probe(struct udevice *dev) 133 { 134 return cros_ec_register(dev); 135 } 136 137 static struct dm_cros_ec_ops cros_ec_ops = { 138 .command = cros_ec_i2c_command, 139 }; 140 141 static const struct udevice_id cros_ec_ids[] = { 142 { .compatible = "google,cros-ec-i2c" }, 143 { } 144 }; 145 146 U_BOOT_DRIVER(cros_ec_i2c) = { 147 .name = "cros_ec_i2c", 148 .id = UCLASS_CROS_EC, 149 .of_match = cros_ec_ids, 150 .probe = cros_ec_probe, 151 .ops = &cros_ec_ops, 152 }; 153