diff options
Diffstat (limited to 'util/ectool.c')
-rw-r--r-- | util/ectool.c | 551 |
1 files changed, 551 insertions, 0 deletions
diff --git a/util/ectool.c b/util/ectool.c new file mode 100644 index 0000000000..3c377b4c18 --- /dev/null +++ b/util/ectool.c @@ -0,0 +1,551 @@ +/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/io.h> +#include <unistd.h> + +#include "lpc_commands.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +/* Don't use a macro where an inline will do... */ +static inline int MIN(int a, int b) { return a < b ? a : b; } + + +const char help_str[] = + "Commands:\n" + " flashinfo\n" + " Prints information on the EC flash\n" + " flashread <offset> <size> <outfile>\n" + " Reads from EC flash to a file\n" + " flashwrite <offset> <infile>\n" + " Writes to EC flash from a file\n" + " flasherase <offset> <size>\n" + " Erases EC flash\n" + " hello\n" + " Checks for basic communication with EC\n" + " readtest <patternoffset> <size>\n" + " Reads a pattern from the EC via LPC\n" + " sertest\n" + " Serial output test for COM2\n" + " version\n" + " Prints EC version\n" + " temps <sensorid>\n" + " Print temperature.\n" + " pwmgetfanrpm\n" + " Prints current fan RPM\n" + " pwmsetfanrpm <targetrpm>\n" + " Set target fan RPM\n" + "\n" + "Not working for you? Make sure LPC I/O is configured:\n" + " pci_write32 0 0x1f 0 0x88 0x007c0801\n" + " pci_write32 0 0x1f 0 0x8c 0x007c0901\n" + " pci_write16 0 0x1f 0 0x80 0x0010\n" + " pci_write16 0 0x1f 0 0x82 0x3f02\n" + ""; + + +/* Waits for the EC to be unbusy. Returns 0 if unbusy, non-zero if + * timeout. */ +int wait_for_ec(int status_addr, int timeout_usec) +{ + int i; + for (i = 0; i < timeout_usec; i += 10) { + usleep(10); /* Delay first, in case we just sent a command */ + if (!(inb(status_addr) & EC_LPC_BUSY_MASK)) + return 0; + } + return -1; /* Timeout */ +} + + +/* Sends a command to the EC. Returns the command status code, or + * -1 if other error. */ +int ec_command(int command, const void *indata, int insize, + void *outdata, int outsize) { + uint8_t *d; + int i; + + /* TODO: add command line option to use kernel command/param window */ + int cmd_addr = EC_LPC_ADDR_USER_CMD; + int param_addr = EC_LPC_ADDR_USER_PARAM; + + if (insize > EC_LPC_PARAM_SIZE || outsize > EC_LPC_PARAM_SIZE) { + fprintf(stderr, "Data size too big\n"); + return -1; + } + + if (wait_for_ec(cmd_addr, 1000000)) { + fprintf(stderr, "Timeout waiting for EC ready\n"); + return -1; + } + + /* Write data, if any */ + /* TODO: optimized copy using outl() */ + for (i = 0, d = (uint8_t *)indata; i < insize; i++, d++) + outb(*d, param_addr + i); + + outb(command, cmd_addr); + + if (wait_for_ec(cmd_addr, 1000000)) { + fprintf(stderr, "Timeout waiting for EC response\n"); + return -1; + } + + /* Check status */ + i = inb(cmd_addr); + i = EC_LPC_GET_STATUS(i); + if (i) { + fprintf(stderr, "EC returned error status %d\n", i); + return i; + } + + /* Read data, if any */ + for (i = 0, d = (uint8_t *)outdata; i < outsize; i++, d++) + *d = inb(param_addr + i); + + return 0; +} + + +void print_help(const char *prog) +{ + printf("Usage: %s <command> [params]\n\n", prog); + puts(help_str); +} + + +int cmd_hello(void) +{ + struct lpc_params_hello p; + struct lpc_response_hello r; + int rv; + + p.in_data = 0xa0b0c0d0; + + rv = ec_command(EC_LPC_COMMAND_HELLO, &p, sizeof(p), &r, sizeof(r)); + if (rv) + return rv; + + if (r.out_data != 0xa1b2c3d4) { + fprintf(stderr, "Expected response 0x%08x, got 0x%08x\n", + 0xa1b2c3d4, r.out_data); + return -1; + } + + printf("EC says hello!\n"); + return 0; +} + + +int cmd_version(void) +{ + static const char * const fw_copies[] = {"unknown", "RO", "A", "B"}; + struct lpc_response_get_version r; + int rv; + + rv = ec_command(EC_LPC_COMMAND_GET_VERSION, NULL, 0, &r, sizeof(r)); + if (rv) + return rv; + + /* Ensure versions are null-terminated before we print them */ + r.version_string_ro[sizeof(r.version_string_ro) - 1] = '\0'; + r.version_string_rw_a[sizeof(r.version_string_rw_a) - 1] = '\0'; + r.version_string_rw_b[sizeof(r.version_string_rw_b) - 1] = '\0'; + + /* Print versions */ + printf("RO version: %s\n", r.version_string_ro); + printf("RW-A version: %s\n", r.version_string_rw_a); + printf("RW-B version: %s\n", r.version_string_rw_b); + printf("Firmware copy: %s\n", + (r.current_image < ARRAY_SIZE(fw_copies) ? + fw_copies[r.current_image] : "?")); + return 0; +} + + +int cmd_read_test(int argc, char *argv[]) +{ + struct lpc_params_read_test p; + struct lpc_response_read_test r; + int offset, size; + int errors = 0; + int rv; + int i; + char *e; + char *buf; + uint32_t *b; + + if (argc < 2) { + fprintf(stderr, "Usage: readtest <pattern_offset> <size>\n"); + return -1; + } + offset = strtol(argv[0], &e, 0); + size = strtol(argv[1], &e, 0); + if ((e && *e) || size <= 0 || size > 0x100000) { + fprintf(stderr, "Bad size.\n"); + return -1; + } + printf("Reading %d bytes with pattern offset 0x%x...\n", size, offset); + + buf = (char *)malloc(size); + if (!buf) { + fprintf(stderr, "Unable to allocate buffer.\n"); + return -1; + } + + /* Read data in chunks */ + for (i = 0; i < size; i += sizeof(r.data)) { + p.offset = offset + i / sizeof(uint32_t); + p.size = MIN(size - i, sizeof(r.data)); + rv = ec_command(EC_LPC_COMMAND_READ_TEST, &p, sizeof(p), + &r, sizeof(r)); + if (rv) { + fprintf(stderr, "Read error at offset %d\n", i); + free(buf); + return -1; + } + memcpy(buf + i, r.data, p.size); + } + + /* Check data */ + for (i = 0, b = (uint32_t *)buf; i < size / 4; i++, b++) { + if (*b != i + offset) { + printf("Mismatch at byte offset 0x%x: " + "expected 0x%08x, got 0x%08x\n", + (int)(i * sizeof(uint32_t)), i + offset, *b); + errors++; + } + } + + free(buf); + if (errors) { + printf("Found %d errors\n", errors); + return -1; + } + + printf("done.\n"); + return 0; +} + + +int cmd_flash_info(void) +{ + struct lpc_response_flash_info r; + int rv; + + rv = ec_command(EC_LPC_COMMAND_FLASH_INFO, NULL, 0, &r, sizeof(r)); + if (rv) + return rv; + + printf("FlashSize %d\nWriteSize %d\nEraseSize %d\nProtectSize %d\n", + r.flash_size, r.write_block_size, r.erase_block_size, + r.protect_block_size); + + return 0; +} + + +int cmd_flash_read(int argc, char *argv[]) +{ + struct lpc_params_flash_read p; + struct lpc_response_flash_read r; + int offset, size; + int rv; + int i; + char *e; + char *buf; + FILE *f; + + if (argc < 3) { + fprintf(stderr, + "Usage: flashread <offset> <size> <filename>\n"); + return -1; + } + offset = strtol(argv[0], &e, 0); + if ((e && *e) || offset < 0 || offset > 0x100000) { + fprintf(stderr, "Bad offset.\n"); + return -1; + } + size = strtol(argv[1], &e, 0); + if ((e && *e) || size <= 0 || size > 0x100000) { + fprintf(stderr, "Bad size.\n"); + return -1; + } + printf("Reading %d bytes at offset %d...\n", size, offset); + + buf = (char *)malloc(size); + if (!buf) { + fprintf(stderr, "Unable to allocate buffer.\n"); + return -1; + } + + /* Read data in chunks */ + for (i = 0; i < size; i += EC_LPC_FLASH_SIZE_MAX) { + p.offset = offset + i; + p.size = MIN(size - i, EC_LPC_FLASH_SIZE_MAX); + rv = ec_command(EC_LPC_COMMAND_FLASH_READ, + &p, sizeof(p), &r, sizeof(r)); + if (rv) { + fprintf(stderr, "Read error at offset %d\n", i); + free(buf); + return -1; + } + memcpy(buf + i, r.data, p.size); + } + + /* Write to file */ + f = fopen(argv[2], "wb"); + if (!f) { + perror("Error opening output file"); + free(buf); + return -1; + } + i = fwrite(buf, 1, size, f); + fclose(f); + free(buf); + if (i != size) { + perror("Error writing to file"); + return -1; + } + printf("done.\n"); + return 0; +} + + +int cmd_flash_write(int argc, char *argv[]) +{ + struct lpc_params_flash_write p; + int offset, size; + int rv; + int i; + char *e; + char *buf; + FILE *f; + + if (argc < 2) { + fprintf(stderr, "Usage: flashwrite <offset> <filename>\n"); + return -1; + } + offset = strtol(argv[0], &e, 0); + if ((e && *e) || offset < 0 || offset > 0x100000) { + fprintf(stderr, "Bad offset.\n"); + return -1; + } + + /* Read the input file */ + f = fopen(argv[1], "rb"); + if (!f) { + perror("Error opening input file"); + return -1; + } + fseek(f, 0, SEEK_END); + size = ftell(f); + rewind(f); + if (size > 0x100000) { + fprintf(stderr, "File seems unreasonably large\n"); + fclose(f); + return -1; + } + + buf = (char *)malloc(size); + if (!buf) { + fprintf(stderr, "Unable to allocate buffer.\n"); + fclose(f); + return -1; + } + + printf("Reading %d bytes from %s...\n", size, argv[1]); + i = fread(buf, 1, size, f); + if (i != size) { + perror("Error reading file"); + free(buf); + return -1; + } + + printf("Writing to offset %d...\n", offset); + + /* Write data in chunks */ + for (i = 0; i < size; i += EC_LPC_FLASH_SIZE_MAX) { + p.offset = offset + i; + p.size = MIN(size - i, EC_LPC_FLASH_SIZE_MAX); + memcpy(p.data, buf + i, p.size); + rv = ec_command(EC_LPC_COMMAND_FLASH_WRITE, + &p, sizeof(p), NULL, 0); + if (rv) { + fprintf(stderr, "Write error at offset %d\n", i); + free(buf); + return -1; + } + } + + free(buf); + printf("done.\n"); + return 0; +} + + +int cmd_flash_erase(int argc, char *argv[]) +{ + struct lpc_params_flash_erase p; + char *e; + + if (argc < 2) { + fprintf(stderr, "Usage: flasherase <offset> <size>\n"); + return -1; + } + p.offset = strtol(argv[0], &e, 0); + if ((e && *e) || p.offset < 0 || p.offset > 0x100000) { + fprintf(stderr, "Bad offset.\n"); + return -1; + } + p.size = strtol(argv[1], &e, 0); + if ((e && *e) || p.size <= 0 || p.size > 0x100000) { + fprintf(stderr, "Bad size.\n"); + return -1; + } + + printf("Erasing %d bytes at offset %d...\n", p.size, p.offset); + if (ec_command(EC_LPC_COMMAND_FLASH_ERASE, &p, sizeof(p), NULL, 0)) + return -1; + + printf("done.\n"); + return 0; +} + + +int cmd_serial_test(int argc, char *argv[]) +{ + const char *c = "COM2 sample serial output from host!\r\n"; + + printf("Writing sample serial output to COM2\n"); + + while (*c) { + /* Wait for space in transmit FIFO */ + while (!(inb(0x2fd) & 0x20)) {} + + /* Put the next character */ + outb(*c++, 0x2f8); + } + + printf("done.\n"); + return 0; +} + +int cmd_temperature(int argc, char *argv[]) +{ + struct lpc_params_temp_sensor_get_readings p; + struct lpc_response_temp_sensor_get_readings r; + int rv; + int id; + char *e; + + if (argc != 1) { + fprintf(stderr, "Usage: temps <sensorid>\n"); + return -1; + } + + id = strtol(argv[0], &e, 0); + if (e && *e) { + fprintf(stderr, "Bad sensor ID.\n"); + return -1; + } + + p.temp_sensor_id = id; + printf("Reading temperature..."); + rv = ec_command(EC_LPC_COMMAND_TEMP_SENSOR_GET_READINGS, + &p, sizeof(p), &r, sizeof(r)); + if (rv) + printf("Error\n"); + else + printf("%d\n", r.value); + return rv; +} + +int cmd_pwm_get_fan_rpm(void) +{ + struct lpc_response_pwm_get_fan_rpm r; + int rv; + + rv = ec_command(EC_LPC_COMMAND_PWM_GET_FAN_RPM, NULL, 0, &r, sizeof(r)); + if (rv) + return rv; + + printf("Current fan RPM: %d\n", r.rpm); + + return 0; +} + +int cmd_pwm_set_fan_rpm(int argc, char *argv[]) +{ + struct lpc_params_pwm_set_fan_target_rpm p; + char *e; + int rv; + + if (argc != 1) { + fprintf(stderr, + "Usage: pwmsetfanrpm <targetrpm>\n"); + return -1; + } + p.rpm = strtol(argv[0], &e, 0); + if (e && *e) { + fprintf(stderr, "Bad RPM.\n"); + return -1; + } + + rv = ec_command(EC_LPC_COMMAND_PWM_SET_FAN_TARGET_RPM, + &p, sizeof(p), NULL, 0); + if (rv) + return rv; + + printf("Fan target RPM set.\n"); + return 0; +} + +int main(int argc, char *argv[]) +{ + if (argc < 2 || !strcasecmp(argv[1], "-?") || + !strcasecmp(argv[1], "help")) { + print_help(argv[0]); + return -2; + } + + /* Request I/O privilege */ + if (iopl(3) < 0) { + perror("Error getting I/O privilege"); + return -3; + } + + /* Handle commands */ + if (!strcasecmp(argv[1], "flashinfo")) + return cmd_flash_info(); + if (!strcasecmp(argv[1], "flashread")) + return cmd_flash_read(argc - 2, argv + 2); + if (!strcasecmp(argv[1], "flashwrite")) + return cmd_flash_write(argc - 2, argv + 2); + if (!strcasecmp(argv[1], "flasherase")) + return cmd_flash_erase(argc - 2, argv + 2); + if (!strcasecmp(argv[1], "hello")) + return cmd_hello(); + if (!strcasecmp(argv[1], "readtest")) + return cmd_read_test(argc - 2, argv + 2); + if (!strcasecmp(argv[1], "sertest")) + return cmd_serial_test(argc - 2, argv + 2); + if (!strcasecmp(argv[1], "version")) + return cmd_version(); + if (!strcasecmp(argv[1], "temps")) + return cmd_temperature(argc - 2, argv + 2); + if (!strcasecmp(argv[1], "pwmgetfanrpm")) + return cmd_pwm_get_fan_rpm(); + if (!strcasecmp(argv[1], "pwmsetfanrpm")) + return cmd_pwm_set_fan_rpm(argc - 2, argv + 2); + + /* If we're still here, command was unknown */ + fprintf(stderr, "Unknown command '%s'\n\n", argv[1]); + print_help(argv[0]); + return -2; +} |