diff options
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/cros_ec.c | 200 | ||||
-rw-r--r-- | drivers/misc/cros_ec_lpc.c | 7 | ||||
-rw-r--r-- | drivers/misc/cros_ec_sandbox.c | 100 |
3 files changed, 278 insertions, 29 deletions
diff --git a/drivers/misc/cros_ec.c b/drivers/misc/cros_ec.c index f03b7d55d6..ebfa7c41c2 100644 --- a/drivers/misc/cros_ec.c +++ b/drivers/misc/cros_ec.c @@ -44,6 +44,10 @@ enum { CROS_EC_CMD_TIMEOUT_MS = 5000, /* Timeout waiting for a synchronous hash to be recomputed */ CROS_EC_CMD_HASH_TIMEOUT_MS = 2000, + + /* Wait 10 ms between attempts to check if EC's hash is ready */ + CROS_EC_HASH_CHECK_DELAY_MS = 10, + }; #define INVALID_HCMD 0xFF @@ -400,6 +404,8 @@ static int ec_command(struct udevice *dev, uint cmd, int cmd_version, */ if (din && in_buffer) { assert(len <= din_len); + if (len > din_len) + return -ENOSPC; memmove(din, in_buffer, len); } } @@ -502,9 +508,10 @@ static int cros_ec_wait_on_hash_done(struct udevice *dev, start = get_timer(0); while (hash->status == EC_VBOOT_HASH_STATUS_BUSY) { - mdelay(50); /* Insert some reasonable delay */ + mdelay(CROS_EC_HASH_CHECK_DELAY_MS); p->cmd = EC_VBOOT_HASH_GET; + if (ec_command(dev, EC_CMD_VBOOT_HASH, 0, p, sizeof(*p), hash, sizeof(*hash)) < 0) return -1; @@ -591,6 +598,25 @@ static int cros_ec_invalidate_hash(struct udevice *dev) return 0; } +int cros_ec_hello(struct udevice *dev, uint *handshakep) +{ + struct ec_params_hello req; + struct ec_response_hello *resp; + + req.in_data = 0x12345678; + if (ec_command_inptr(dev, EC_CMD_HELLO, 0, &req, sizeof(req), + (uint8_t **)&resp, sizeof(*resp)) < 0) + return -EIO; + if (resp->out_data != req.in_data + 0x01020304) { + printf("Received invalid handshake %x\n", resp->out_data); + if (handshakep) + *handshakep = req.in_data; + return -ENOTSYNC; + } + + return 0; +} + int cros_ec_reboot(struct udevice *dev, enum ec_reboot_cmd cmd, uint8_t flags) { struct ec_params_reboot_ec p; @@ -603,18 +629,23 @@ int cros_ec_reboot(struct udevice *dev, enum ec_reboot_cmd cmd, uint8_t flags) return -1; if (!(flags & EC_REBOOT_FLAG_ON_AP_SHUTDOWN)) { + ulong start; + /* * EC reboot will take place immediately so delay to allow it * to complete. Note that some reboot types (EC_REBOOT_COLD) * will reboot the AP as well, in which case we won't actually * get to this point. */ - /* - * TODO(rspangler@chromium.org): Would be nice if we had a - * better way to determine when the reboot is complete. Could - * we poll a memory-mapped LPC value? - */ - udelay(50000); + mdelay(50); + start = get_timer(0); + while (cros_ec_hello(dev, NULL)) { + if (get_timer(start) > 3000) { + log_err("EC did not return from reboot\n"); + return -ETIMEDOUT; + } + mdelay(5); + } } return 0; @@ -738,7 +769,6 @@ static int cros_ec_check_version(struct udevice *dev) { struct cros_ec_dev *cdev = dev_get_uclass_priv(dev); struct ec_params_hello req; - struct ec_response_hello *resp; struct dm_cros_ec_ops *ops; int ret; @@ -767,14 +797,14 @@ static int cros_ec_check_version(struct udevice *dev) /* Try sending a version 3 packet */ cdev->protocol_version = 3; req.in_data = 0; - if (ec_command_inptr(dev, EC_CMD_HELLO, 0, &req, sizeof(req), - (uint8_t **)&resp, sizeof(*resp)) > 0) + ret = cros_ec_hello(dev, NULL); + if (!ret || ret == -ENOTSYNC) return 0; /* Try sending a version 2 packet */ cdev->protocol_version = 2; - if (ec_command_inptr(dev, EC_CMD_HELLO, 0, &req, sizeof(req), - (uint8_t **)&resp, sizeof(*resp)) > 0) + ret = cros_ec_hello(dev, NULL); + if (!ret || ret == -ENOTSYNC) return 0; /* @@ -790,18 +820,16 @@ static int cros_ec_check_version(struct udevice *dev) int cros_ec_test(struct udevice *dev) { - struct ec_params_hello req; - struct ec_response_hello *resp; + uint out_data; + int ret; - req.in_data = 0x12345678; - if (ec_command_inptr(dev, EC_CMD_HELLO, 0, &req, sizeof(req), - (uint8_t **)&resp, sizeof(*resp)) < sizeof(*resp)) { + ret = cros_ec_hello(dev, &out_data); + if (ret == -ENOTSYNC) { + printf("Received invalid handshake %x\n", out_data); + return ret; + } else if (ret) { printf("ec_command_inptr() returned error\n"); - return -1; - } - if (resp->out_data != req.in_data + 0x01020304) { - printf("Received invalid handshake %x\n", resp->out_data); - return -1; + return ret; } return 0; @@ -1077,6 +1105,19 @@ int cros_ec_flash_update_rw(struct udevice *dev, const uint8_t *image, return 0; } +int cros_ec_get_sku_id(struct udevice *dev) +{ + struct ec_sku_id_info *r; + int ret; + + ret = ec_command_inptr(dev, EC_CMD_GET_SKU_ID, 0, NULL, 0, + (uint8_t **)&r, sizeof(*r)); + if (ret != sizeof(*r)) + return -ret; + + return r->sku_id; +} + int cros_ec_read_nvdata(struct udevice *dev, uint8_t *block, int size) { struct ec_params_vbnvcontext p; @@ -1303,19 +1344,33 @@ int cros_ec_i2c_tunnel(struct udevice *dev, int port, struct i2c_msg *in, return 0; } -int cros_ec_check_feature(struct udevice *dev, int feature) +int cros_ec_get_features(struct udevice *dev, u64 *featuresp) { struct ec_response_get_features r; int rv; - rv = ec_command(dev, EC_CMD_GET_FEATURES, 0, &r, sizeof(r), NULL, 0); - if (rv) - return rv; + rv = ec_command(dev, EC_CMD_GET_FEATURES, 0, NULL, 0, &r, sizeof(r)); + if (rv != sizeof(r)) + return -EIO; + *featuresp = r.flags[0] | (u64)r.flags[1] << 32; + + return 0; +} + +int cros_ec_check_feature(struct udevice *dev, uint feature) +{ + struct ec_response_get_features r; + int rv; + + rv = ec_command(dev, EC_CMD_GET_FEATURES, 0, NULL, 0, &r, sizeof(r)); + if (rv != sizeof(r)) + return -EIO; if (feature >= 8 * sizeof(r.flags)) - return -1; + return -EINVAL; - return r.flags[feature / 32] & EC_FEATURE_MASK_0(feature); + return r.flags[feature / 32] & EC_FEATURE_MASK_0(feature) ? true : + false; } /* @@ -1502,10 +1557,99 @@ int cros_ec_set_lid_shutdown_mask(struct udevice *dev, int enable) return 0; } +int cros_ec_vstore_supported(struct udevice *dev) +{ + return cros_ec_check_feature(dev, EC_FEATURE_VSTORE); +} + +int cros_ec_vstore_info(struct udevice *dev, u32 *lockedp) +{ + struct ec_response_vstore_info *resp; + + if (ec_command_inptr(dev, EC_CMD_VSTORE_INFO, 0, NULL, 0, + (uint8_t **)&resp, sizeof(*resp)) != sizeof(*resp)) + return -EIO; + + if (lockedp) + *lockedp = resp->slot_locked; + + return resp->slot_count; +} + +/* + * cros_ec_vstore_read - Read data from EC vstore slot + * + * @slot: vstore slot to read from + * @data: buffer to store read data, must be EC_VSTORE_SLOT_SIZE bytes + */ +int cros_ec_vstore_read(struct udevice *dev, int slot, uint8_t *data) +{ + struct ec_params_vstore_read req; + struct ec_response_vstore_read *resp; + + req.slot = slot; + if (ec_command_inptr(dev, EC_CMD_VSTORE_READ, 0, &req, sizeof(req), + (uint8_t **)&resp, sizeof(*resp)) != sizeof(*resp)) + return -EIO; + + if (!data || req.slot >= EC_VSTORE_SLOT_MAX) + return -EINVAL; + + memcpy(data, resp->data, sizeof(resp->data)); + + return 0; +} + +/* + * cros_ec_vstore_write - Save data into EC vstore slot + * + * @slot: vstore slot to write into + * @data: data to write + * @size: size of data in bytes + * + * Maximum size of data is EC_VSTORE_SLOT_SIZE. It is the callers + * responsibility to check the number of implemented slots by + * querying the vstore info. + */ +int cros_ec_vstore_write(struct udevice *dev, int slot, const uint8_t *data, + size_t size) +{ + struct ec_params_vstore_write req; + + if (slot >= EC_VSTORE_SLOT_MAX || size > EC_VSTORE_SLOT_SIZE) + return -EINVAL; + + req.slot = slot; + memcpy(req.data, data, size); + + if (ec_command(dev, EC_CMD_VSTORE_WRITE, 0, &req, sizeof(req), NULL, 0)) + return -EIO; + + return 0; +} + +int cros_ec_get_switches(struct udevice *dev) +{ + struct dm_cros_ec_ops *ops; + int ret; + + ops = dm_cros_ec_get_ops(dev); + if (!ops->get_switches) + return -ENOSYS; + + ret = ops->get_switches(dev); + if (ret < 0) + return log_msg_ret("get", ret); + + return ret; +} + UCLASS_DRIVER(cros_ec) = { .id = UCLASS_CROS_EC, .name = "cros-ec", .per_device_auto = sizeof(struct cros_ec_dev), +#if !CONFIG_IS_ENABLED(OF_PLATDATA) .post_bind = dm_scan_fdt_dev, +#endif .flags = DM_UC_FLAG_ALLOC_PRIV_DMA, }; diff --git a/drivers/misc/cros_ec_lpc.c b/drivers/misc/cros_ec_lpc.c index e0002b9753..f40375978d 100644 --- a/drivers/misc/cros_ec_lpc.c +++ b/drivers/misc/cros_ec_lpc.c @@ -207,6 +207,12 @@ int cros_ec_lpc_init(struct cros_ec_dev *dev, const void *blob) return 0; } +/* Return the byte of EC switch states */ +static int cros_ec_lpc_get_switches(struct udevice *dev) +{ + return inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SWITCHES); +} + /* * Test if LPC command args are supported. * @@ -239,6 +245,7 @@ static struct dm_cros_ec_ops cros_ec_ops = { .packet = cros_ec_lpc_packet, .command = cros_ec_lpc_command, .check_version = cros_ec_lpc_check_version, + .get_switches = cros_ec_lpc_get_switches, }; static const struct udevice_id cros_ec_ids[] = { diff --git a/drivers/misc/cros_ec_sandbox.c b/drivers/misc/cros_ec_sandbox.c index 9fd6cc2086..cb8adc4495 100644 --- a/drivers/misc/cros_ec_sandbox.c +++ b/drivers/misc/cros_ec_sandbox.c @@ -18,6 +18,7 @@ #include <asm/malloc.h> #include <asm/state.h> #include <asm/sdl.h> +#include <asm/test.h> #include <linux/input.h> /* @@ -61,6 +62,15 @@ struct ec_keymatrix_entry { int keycode; /* corresponding linux key code */ }; +enum { + VSTORE_SLOT_COUNT = 4, +}; + +struct vstore_slot { + bool locked; + u8 data[EC_VSTORE_SLOT_SIZE]; +}; + /** * struct ec_state - Information about the EC state * @@ -73,6 +83,8 @@ struct ec_keymatrix_entry { * @matrix: Information about keyboard matrix * @keyscan: Current keyscan information (bit set for each row/column pressed) * @recovery_req: Keyboard recovery requested + * @test_flags: Flags that control behaviour for tests + * @slot_locked: Locked vstore slots (mask) */ struct ec_state { u8 vbnv_context[EC_VBNV_BLOCK_SIZE_V2]; @@ -84,6 +96,8 @@ struct ec_state { struct ec_keymatrix_entry *matrix; /* the key matrix info */ uint8_t keyscan[KEYBOARD_COLS]; bool recovery_req; + uint test_flags; + struct vstore_slot slot[VSTORE_SLOT_COUNT]; } s_state, *g_state; /** @@ -295,6 +309,8 @@ static int process_cmd(struct ec_state *ec, struct ec_response_hello *resp = resp_data; resp->out_data = req->in_data + 0x01020304; + if (ec->test_flags & CROSECT_BREAK_HELLO) + resp->out_data++; len = sizeof(*resp); break; } @@ -358,10 +374,20 @@ static int process_cmd(struct ec_state *ec, resp->mask |= EC_HOST_EVENT_MASK( EC_HOST_EVENT_KEYBOARD_RECOVERY); } - + if (ec->test_flags & CROSECT_LID_OPEN) + resp->mask |= + EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_OPEN); len = sizeof(*resp); break; } + case EC_CMD_HOST_EVENT_CLEAR_B: { + const struct ec_params_host_event_mask *req = req_data; + + if (req->mask & EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_OPEN)) + ec->test_flags &= ~CROSECT_LID_OPEN; + len = 0; + break; + } case EC_CMD_VBOOT_HASH: { const struct ec_params_vboot_hash *req = req_data; struct ec_response_vboot_hash *resp = resp_data; @@ -468,6 +494,62 @@ static int process_cmd(struct ec_state *ec, len = sizeof(*resp); break; } + case EC_CMD_GET_SKU_ID: { + struct ec_sku_id_info *resp = resp_data; + + resp->sku_id = 1234; + len = sizeof(*resp); + break; + } + case EC_CMD_GET_FEATURES: { + struct ec_response_get_features *resp = resp_data; + + resp->flags[0] = EC_FEATURE_MASK_0(EC_FEATURE_FLASH) | + EC_FEATURE_MASK_0(EC_FEATURE_I2C) | + EC_FEATURE_MASK_0(EC_FEATURE_VSTORE); + resp->flags[1] = + EC_FEATURE_MASK_1(EC_FEATURE_UNIFIED_WAKE_MASKS) | + EC_FEATURE_MASK_1(EC_FEATURE_ISH); + len = sizeof(*resp); + break; + } + case EC_CMD_VSTORE_INFO: { + struct ec_response_vstore_info *resp = resp_data; + int i; + + resp->slot_count = VSTORE_SLOT_COUNT; + resp->slot_locked = 0; + for (i = 0; i < VSTORE_SLOT_COUNT; i++) { + if (ec->slot[i].locked) + resp->slot_locked |= 1 << i; + } + len = sizeof(*resp); + break; + }; + case EC_CMD_VSTORE_WRITE: { + const struct ec_params_vstore_write *req = req_data; + struct vstore_slot *slot; + + if (req->slot >= EC_VSTORE_SLOT_MAX) + return -EINVAL; + slot = &ec->slot[req->slot]; + slot->locked = true; + memcpy(slot->data, req->data, EC_VSTORE_SLOT_SIZE); + len = 0; + break; + } + case EC_CMD_VSTORE_READ: { + const struct ec_params_vstore_read *req = req_data; + struct ec_response_vstore_read *resp = resp_data; + struct vstore_slot *slot; + + if (req->slot >= EC_VSTORE_SLOT_MAX) + return -EINVAL; + slot = &ec->slot[req->slot]; + memcpy(resp->data, slot->data, EC_VSTORE_SLOT_SIZE); + len = sizeof(*resp); + break; + } default: printf(" ** Unknown EC command %#02x\n", req_hdr->command); return -1; @@ -518,6 +600,21 @@ void cros_ec_check_keyboard(struct udevice *dev) } } +/* Return the byte of EC switch states */ +static int cros_ec_sandbox_get_switches(struct udevice *dev) +{ + struct ec_state *ec = dev_get_priv(dev); + + return ec->test_flags & CROSECT_LID_OPEN ? EC_SWITCH_LID_OPEN : 0; +} + +void sandbox_cros_ec_set_test_flags(struct udevice *dev, uint flags) +{ + struct ec_state *ec = dev_get_priv(dev); + + ec->test_flags = flags; +} + int cros_ec_probe(struct udevice *dev) { struct ec_state *ec = dev_get_priv(dev); @@ -573,6 +670,7 @@ int cros_ec_probe(struct udevice *dev) struct dm_cros_ec_ops cros_ec_ops = { .packet = cros_ec_sandbox_packet, + .get_switches = cros_ec_sandbox_get_switches, }; static const struct udevice_id cros_ec_ids[] = { |