diff options
-rw-r--r-- | board/host/board.h | 2 | ||||
-rw-r--r-- | common/motion_sense.c | 176 | ||||
-rw-r--r-- | driver/accel_kxcj9.c | 238 | ||||
-rw-r--r-- | driver/accel_kxcj9.h | 30 | ||||
-rw-r--r-- | include/accelerometer.h | 50 | ||||
-rw-r--r-- | include/ec_commands.h | 126 | ||||
-rw-r--r-- | test/math_util.c | 27 | ||||
-rw-r--r-- | test/motion_sense.c | 25 | ||||
-rw-r--r-- | util/ectool.c | 221 |
9 files changed, 789 insertions, 106 deletions
diff --git a/board/host/board.h b/board/host/board.h index 130bdcd385..5ab078292f 100644 --- a/board/host/board.h +++ b/board/host/board.h @@ -54,8 +54,8 @@ enum adc_channel { /* Identifiers for each accelerometer used. */ enum accel_id { - ACCEL_LID, ACCEL_BASE, + ACCEL_LID, /* Number of accelerometers. */ ACCEL_COUNT diff --git a/common/motion_sense.c b/common/motion_sense.c index 5dad6db1cf..8629fa7588 100644 --- a/common/motion_sense.c +++ b/common/motion_sense.c @@ -30,9 +30,13 @@ static vector_3_t acc_lid_host, acc_base_host; static float lid_angle_deg; static int lid_angle_is_reliable; +/* Bounds for setting the sensor polling interval. */ +#define MIN_POLLING_INTERVAL_MS 5 +#define MAX_POLLING_INTERVAL_MS 1000 + /* Accelerometer polling intervals based on chipset state. */ -#define ACCEL_INTERVAL_AP_ON_MS 10 -#define ACCEL_INTERVAL_AP_SUSPEND_MS 100 +static int accel_interval_ap_on_ms = 10; +static const int accel_interval_ap_suspend_ms = 100; /* * Angle threshold for how close the hinge aligns with gravity before @@ -43,7 +47,7 @@ static int lid_angle_is_reliable; #define HINGE_ALIGNED_WITH_GRAVITY_THRESHOLD 0.96593F /* Sampling interval for measuring acceleration and calculating lid angle. */ -static int accel_interval_ms = ACCEL_INTERVAL_AP_SUSPEND_MS; +static int accel_interval_ms; #ifdef CONFIG_CMD_LID_ANGLE static int accel_disp; @@ -153,19 +157,17 @@ void motion_get_accel_base(vector_3_t *v) } #endif -/* Lower accel polling rate on chipset suspend. */ -static void set_slow_accel_polling(void) +static void set_ap_suspend_polling(void) { - accel_interval_ms = ACCEL_INTERVAL_AP_SUSPEND_MS; + accel_interval_ms = accel_interval_ap_suspend_ms; } -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, set_slow_accel_polling, HOOK_PRIO_DEFAULT); +DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, set_ap_suspend_polling, HOOK_PRIO_DEFAULT); -/* Raise accel polling rate on chipset resume. */ -static void set_fast_accel_polling(void) +static void set_ap_on_polling(void) { - accel_interval_ms = ACCEL_INTERVAL_AP_ON_MS; + accel_interval_ms = accel_interval_ap_on_ms; } -DECLARE_HOOK(HOOK_CHIPSET_RESUME, set_fast_accel_polling, HOOK_PRIO_DEFAULT); +DECLARE_HOOK(HOOK_CHIPSET_RESUME, set_ap_on_polling, HOOK_PRIO_DEFAULT); void motion_sense_task(void) @@ -180,6 +182,14 @@ void motion_sense_task(void) lpc_status = host_get_memmap(EC_MEMMAP_ACC_STATUS); lpc_data = (uint16_t *)host_get_memmap(EC_MEMMAP_ACC_DATA); + /* + * TODO(crosbug.com/p/27320): The motion_sense task currently assumes + * one configuration of motion sensors. Namely, it assumes there is + * one accel in the base, one in the lid, and they both use the same + * driver. Eventually, all of these assumptions will have to be removed + * when we have other configurations of motion sensors. + */ + /* Initialize accelerometers. */ ret = accel_init(ACCEL_LID); ret |= accel_init(ACCEL_BASE); @@ -191,6 +201,17 @@ void motion_sense_task(void) return; } + /* Initialize sampling interval. */ + accel_interval_ms = accel_interval_ap_suspend_ms; + + /* Set default accelerometer parameters. */ + accel_set_range(ACCEL_LID, 2, 1); + accel_set_range(ACCEL_BASE, 2, 1); + accel_set_resolution(ACCEL_LID, 12, 1); + accel_set_resolution(ACCEL_BASE, 12, 1); + accel_set_datarate(ACCEL_LID, 100000, 1); + accel_set_datarate(ACCEL_BASE, 100000, 1); + /* Write to status byte to represent that accelerometers are present. */ *lpc_status |= EC_MEMMAP_ACC_STATUS_PRESENCE_BIT; @@ -299,6 +320,139 @@ void accel_int_base(enum gpio_signal signal) } /*****************************************************************************/ +/* Host commands */ + +static int host_cmd_motion_sense(struct host_cmd_handler_args *args) +{ + const struct ec_params_motion_sense *in = args->params; + struct ec_response_motion_sense *out = args->response; + int id, data; + + switch (in->cmd) { + case MOTIONSENSE_CMD_DUMP: + /* + * TODO(crosbug.com/p/27320): Need to remove hard coding and + * use some motion_sense data structure from the board file to + * help fill in this response. + */ + out->dump.sensor_presence[0] = 1; + out->dump.sensor_presence[1] = 1; + out->dump.sensor_presence[2] = 0; + out->dump.data[0] = acc_base_host[X]; + out->dump.data[1] = acc_base_host[Y]; + out->dump.data[2] = acc_base_host[Z]; + out->dump.data[3] = acc_lid_host[X]; + out->dump.data[4] = acc_lid_host[Y]; + out->dump.data[5] = acc_lid_host[Z]; + + args->response_size = sizeof(out->dump); + break; + + case MOTIONSENSE_CMD_INFO: + /* + * TODO(crosbug.com/p/27320): Need to remove hard coding and + * use some motion_sense data structure from the board file to + * help fill in this response. + */ + switch (in->sensor_odr.sensor_num) { + case ACCEL_BASE: + out->info.type = MOTIONSENSE_TYPE_ACCEL; + out->info.location = MOTIONSENSE_LOC_BASE; + out->info.chip = MOTIONSENSE_CHIP_KXCJ9; + break; + case ACCEL_LID: + out->info.type = MOTIONSENSE_TYPE_ACCEL; + out->info.location = MOTIONSENSE_LOC_LID; + out->info.chip = MOTIONSENSE_CHIP_KXCJ9; + break; + default: + return EC_RES_INVALID_PARAM; + } + + args->response_size = sizeof(out->info); + break; + + case MOTIONSENSE_CMD_EC_RATE: + /* + * Set new sensor sampling rate when AP is on, if the data arg + * has a value. + */ + if (in->ec_rate.data != EC_MOTION_SENSE_NO_VALUE) { + if (in->ec_rate.data >= MIN_POLLING_INTERVAL_MS && + in->ec_rate.data <= MAX_POLLING_INTERVAL_MS) { + accel_interval_ap_on_ms = in->ec_rate.data; + accel_interval_ms = accel_interval_ap_on_ms; + } else { + CPRINTF("[%T MS bad EC sampling rate %d]\n", + in->ec_rate.data); + return EC_RES_INVALID_PARAM; + } + } + + out->ec_rate.ret = accel_interval_ap_on_ms; + + args->response_size = sizeof(out->ec_rate); + break; + + case MOTIONSENSE_CMD_SENSOR_ODR: + /* Verify sensor number is valid. */ + if (in->sensor_odr.sensor_num >= ACCEL_COUNT) + return EC_RES_INVALID_PARAM; + + id = in->sensor_odr.sensor_num; + + /* Set new datarate if the data arg has a value. */ + if (in->sensor_odr.data != EC_MOTION_SENSE_NO_VALUE) { + if (accel_set_datarate(id, in->sensor_odr.data, + in->sensor_odr.roundup) != EC_SUCCESS) { + CPRINTF("[%T MS bad sensor rate %d]\n", + in->sensor_odr.data); + return EC_RES_INVALID_PARAM; + } + } + + accel_get_datarate(id, &data); + out->sensor_odr.ret = data; + + args->response_size = sizeof(out->sensor_odr); + break; + + case MOTIONSENSE_CMD_SENSOR_RANGE: + /* Verify sensor number is valid. */ + if (in->sensor_odr.sensor_num >= ACCEL_COUNT) + return EC_RES_INVALID_PARAM; + + id = in->sensor_odr.sensor_num; + + /* Set new datarate if the data arg has a value. */ + if (in->sensor_range.data != EC_MOTION_SENSE_NO_VALUE) { + if (accel_set_range(id, in->sensor_range.data, + in->sensor_range.roundup) != EC_SUCCESS) { + CPRINTF("[%T MS bad sensor range %d]\n", + in->sensor_range.data); + return EC_RES_INVALID_PARAM; + } + } + + accel_get_range(id, &data); + out->sensor_range.ret = data; + + args->response_size = sizeof(out->sensor_range); + break; + + default: + CPRINTF("[%T MS bad cmd 0x%x]\n", in->cmd); + return EC_RES_INVALID_PARAM; + } + + return EC_RES_SUCCESS; +} + +DECLARE_HOST_COMMAND(EC_CMD_MOTION_SENSE_CMD, + host_cmd_motion_sense, + EC_VER_MASK(0)); + +/*****************************************************************************/ /* Console commands */ #ifdef CONFIG_CMD_LID_ANGLE static int command_ctrl_print_lid_angle_calcs(int argc, char **argv) diff --git a/driver/accel_kxcj9.c b/driver/accel_kxcj9.c index 78405a039f..76aee6f1b5 100644 --- a/driver/accel_kxcj9.c +++ b/driver/accel_kxcj9.c @@ -21,19 +21,90 @@ /* Number of times to attempt to enable sensor before giving up. */ #define SENSOR_ENABLE_ATTEMPTS 3 -/* Range of the accelerometers: 2G, 4G, or 8G. */ -static int sensor_range[ACCEL_COUNT] = {KXCJ9_GSEL_2G, KXCJ9_GSEL_2G}; +/* + * Struct for pairing an engineering value with the register value for a + * parameter. + */ +struct accel_param_pair { + int val; /* Value in engineering units. */ + int reg; /* Corresponding register value. */ +}; + +/* List of range values in +/-G's and their associated register values. */ +const struct accel_param_pair ranges[] = { + {2, KXCJ9_GSEL_2G}, + {4, KXCJ9_GSEL_4G}, + {8, KXCJ9_GSEL_8G_14BIT} +}; + +/* List of resolution values in bits and their associated register values. */ +const struct accel_param_pair resolutions[] = { + {8, KXCJ9_RES_8BIT}, + {12, KXCJ9_RES_12BIT} +}; + +/* List of ODR values in mHz and their associated register values. */ +const struct accel_param_pair datarates[] = { + {781, KXCJ9_OSA_0_781HZ}, + {1563, KXCJ9_OSA_1_563HZ}, + {3125, KXCJ9_OSA_3_125HZ}, + {6250, KXCJ9_OSA_6_250HZ}, + {12500, KXCJ9_OSA_12_50HZ}, + {25000, KXCJ9_OSA_25_00HZ}, + {50000, KXCJ9_OSA_50_00HZ}, + {100000, KXCJ9_OSA_100_0HZ}, + {200000, KXCJ9_OSA_200_0HZ}, + {400000, KXCJ9_OSA_400_0HZ}, + {800000, KXCJ9_OSA_800_0HZ}, + {1600000, KXCJ9_OSA_1600_HZ} +}; + +/* Current range of each accelerometer. The value is an index into ranges[]. */ +static int sensor_range[ACCEL_COUNT] = {0, 0}; + +/* + * Current resolution of each accelerometer. The value is an index into + * resolutions[]. + */ +static int sensor_resolution[ACCEL_COUNT] = {1, 1}; -/* Resolution: KXCJ9_RES_12BIT or KXCJ9_RES_12BIT. */ -static int sensor_resolution[ACCEL_COUNT] = {KXCJ9_RES_12BIT, KXCJ9_RES_12BIT}; +/* + * Current output data rate of each accelerometer. The value is an index into + * datarates[]. + */ +static int sensor_datarate[ACCEL_COUNT] = {6, 6}; -/* Output data rate: KXCJ9_OSA_* ranges from 0.781Hz to 1600Hz. */ -static int sensor_datarate[ACCEL_COUNT] = {KXCJ9_OSA_100_0HZ, - KXCJ9_OSA_100_0HZ}; static struct mutex accel_mutex[ACCEL_COUNT]; /** + * Find index into a accel_param_pair that matches the given engineering value + * passed in. The round_up flag is used to specify whether to round up or down. + * Note, this function always returns a valid index. If the request is + * outside the range of values, it returns the closest valid index. + */ +static int find_param_index(const int eng_val, const int round_up, + const struct accel_param_pair *pairs, const int size) +{ + int i; + + /* Linear search for index to match. */ + for (i = 0; i < size - 1; i++) { + if (eng_val <= pairs[i].val) + return i; + + if (eng_val < pairs[i+1].val) { + if (round_up) + return i + 1; + else + return i; + } + } + + return i; +} + +/** * Read register from accelerometer. */ static int raw_read8(const int addr, const int reg, int *data_ptr) @@ -125,22 +196,16 @@ static int enable_sensor(const enum accel_id id, const int ctrl1) return ret; } - -int accel_write_range(const enum accel_id id, const int range) +int accel_set_range(const enum accel_id id, const int range, const int rnd) { - int ret, ctrl1, ctrl1_new; + int ret, ctrl1, ctrl1_new, index; /* Check for valid id. */ if (id < 0 || id >= ACCEL_COUNT) return EC_ERROR_INVAL; - /* - * Verify that the input range is valid. Note that we currently - * don't support the 8G with 14-bit resolution mode. - */ - if (range != KXCJ9_GSEL_2G && range != KXCJ9_GSEL_4G && - range != KXCJ9_GSEL_8G) - return EC_ERROR_INVAL; + /* Find index for interface pair matching the specified range. */ + index = find_param_index(range, rnd, ranges, ARRAY_SIZE(ranges)); /* Disable the sensor to allow for changing of critical parameters. */ ret = disable_sensor(id, &ctrl1); @@ -148,12 +213,12 @@ int accel_write_range(const enum accel_id id, const int range) return ret; /* Determine new value of CTRL1 reg and attempt to write it. */ - ctrl1_new = (ctrl1 & ~KXCJ9_GSEL_ALL) | range; + ctrl1_new = (ctrl1 & ~KXCJ9_GSEL_ALL) | ranges[index].reg; ret = raw_write8(accel_addr[id], KXCJ9_CTRL1, ctrl1_new); /* If successfully written, then save the range. */ if (ret == EC_SUCCESS) { - sensor_range[id] = range; + sensor_range[id] = index; ctrl1 = ctrl1_new; } @@ -164,30 +229,40 @@ int accel_write_range(const enum accel_id id, const int range) return ret; } -int accel_write_resolution(const enum accel_id id, const int res) +int accel_get_range(const enum accel_id id, int * const range) { - int ret, ctrl1, ctrl1_new; - /* Check for valid id. */ if (id < 0 || id >= ACCEL_COUNT) return EC_ERROR_INVAL; - /* Check that resolution input is valid. */ - if (res != KXCJ9_RES_12BIT && res != KXCJ9_RES_8BIT) + *range = ranges[sensor_range[id]].val; + return EC_SUCCESS; +} + +int accel_set_resolution(const enum accel_id id, const int res, const int rnd) +{ + int ret, ctrl1, ctrl1_new, index; + + /* Check for valid id. */ + if (id < 0 || id >= ACCEL_COUNT) return EC_ERROR_INVAL; + /* Find index for interface pair matching the specified resolution. */ + index = find_param_index(res, rnd, resolutions, + ARRAY_SIZE(resolutions)); + /* Disable the sensor to allow for changing of critical parameters. */ ret = disable_sensor(id, &ctrl1); if (ret != EC_SUCCESS) return ret; /* Determine new value of CTRL1 reg and attempt to write it. */ - ctrl1_new = (ctrl1 & ~KXCJ9_RES_12BIT) | res; + ctrl1_new = (ctrl1 & ~KXCJ9_RES_12BIT) | resolutions[index].reg; ret = raw_write8(accel_addr[id], KXCJ9_CTRL1, ctrl1_new); /* If successfully written, then save the range. */ if (ret == EC_SUCCESS) { - sensor_resolution[id] = res; + sensor_resolution[id] = index; ctrl1 = ctrl1_new; } @@ -198,29 +273,39 @@ int accel_write_resolution(const enum accel_id id, const int res) return ret; } -int accel_write_datarate(const enum accel_id id, const int rate) +int accel_get_resolution(const enum accel_id id, int * const res) { - int ret, ctrl1; - /* Check for valid id. */ if (id < 0 || id >= ACCEL_COUNT) return EC_ERROR_INVAL; - /* Check that rate input is valid. */ - if (rate < KXCJ9_OSA_12_50HZ || rate > KXCJ9_OSA_6_250HZ) + *res = resolutions[sensor_resolution[id]].val; + return EC_SUCCESS; +} + +int accel_set_datarate(const enum accel_id id, const int rate, const int rnd) +{ + int ret, ctrl1, index; + + /* Check for valid id. */ + if (id < 0 || id >= ACCEL_COUNT) return EC_ERROR_INVAL; + /* Find index for interface pair matching the specified rate. */ + index = find_param_index(rate, rnd, datarates, ARRAY_SIZE(datarates)); + /* Disable the sensor to allow for changing of critical parameters. */ ret = disable_sensor(id, &ctrl1); if (ret != EC_SUCCESS) return ret; /* Set output data rate. */ - ret = raw_write8(accel_addr[id], KXCJ9_DATA_CTRL, rate); + ret = raw_write8(accel_addr[id], KXCJ9_DATA_CTRL, + datarates[index].reg); /* If successfully written, then save the range. */ if (ret == EC_SUCCESS) - sensor_datarate[id] = rate; + sensor_datarate[id] = index; /* Re-enable the sensor. */ if (enable_sensor(id, ctrl1) != EC_SUCCESS) @@ -229,6 +314,17 @@ int accel_write_datarate(const enum accel_id id, const int rate) return ret; } +int accel_get_datarate(const enum accel_id id, int * const rate) +{ + /* Check for valid id. */ + if (id < 0 || id >= ACCEL_COUNT) + return EC_ERROR_INVAL; + + *rate = datarates[sensor_datarate[id]].val; + return EC_SUCCESS; +} + + #ifdef CONFIG_ACCEL_INTERRUPTS int accel_set_interrupt(const enum accel_id id, unsigned int threshold) { @@ -284,7 +380,8 @@ error_enable_sensor: } #endif -int accel_read(enum accel_id id, int *x_acc, int *y_acc, int *z_acc) +int accel_read(const enum accel_id id, int * const x_acc, int * const y_acc, + int * const z_acc) { uint8_t acc[6]; uint8_t reg = KXCJ9_XOUT_L; @@ -306,7 +403,7 @@ int accel_read(enum accel_id id, int *x_acc, int *y_acc, int *z_acc) return ret; /* Determine multiplier based on stored range. */ - switch (sensor_range[id]) { + switch (ranges[sensor_range[id]].reg) { case KXCJ9_GSEL_2G: multiplier = 1; break; @@ -314,6 +411,7 @@ int accel_read(enum accel_id id, int *x_acc, int *y_acc, int *z_acc) multiplier = 2; break; case KXCJ9_GSEL_8G: + case KXCJ9_GSEL_8G_14BIT: multiplier = 4; break; default: @@ -338,7 +436,7 @@ int accel_read(enum accel_id id, int *x_acc, int *y_acc, int *z_acc) return EC_SUCCESS; } -int accel_init(enum accel_id id) +int accel_init(const enum accel_id id) { int ret = EC_SUCCESS; int cnt = 0, ctrl1, ctrl2; @@ -378,7 +476,8 @@ int accel_init(enum accel_id id) } /* Set resolution and range. */ - ctrl1 = sensor_resolution[id] | sensor_range[id]; + ctrl1 = resolutions[sensor_resolution[id]].reg | + ranges[sensor_range[id]].reg; #ifdef CONFIG_ACCEL_INTERRUPTS /* Enable wake up (motion detect) functionality. */ ctrl1 |= KXCJ9_CTRL1_WUFE; @@ -407,7 +506,8 @@ int accel_init(enum accel_id id) #endif /* Set output data rate. */ - ret |= raw_write8(accel_addr[id], KXCJ9_DATA_CTRL, sensor_datarate[id]); + ret |= raw_write8(accel_addr[id], KXCJ9_DATA_CTRL, + datarates[sensor_datarate[id]].reg); /* Enable the sensor. */ ret |= enable_sensor(id, ctrl1); @@ -482,9 +582,9 @@ DECLARE_CONSOLE_COMMAND(accelwrite, command_write_accelerometer, static int command_accelrange(int argc, char **argv) { char *e; - int id, data; + int id, data, round = 1; - if (argc < 2 || argc > 3) + if (argc < 2 || argc > 4) return EC_ERROR_PARAM_COUNT; /* First argument is sensor id. */ @@ -492,34 +592,42 @@ static int command_accelrange(int argc, char **argv) if (*e || id < 0 || id > ACCEL_COUNT) return EC_ERROR_PARAM1; - if (argc == 3) { + if (argc >= 3) { /* Second argument is data to write. */ data = strtoi(argv[2], &e, 0); if (*e) return EC_ERROR_PARAM2; + if (argc == 4) { + /* Third argument is rounding flag. */ + round = strtoi(argv[3], &e, 0); + if (*e) + return EC_ERROR_PARAM3; + } + /* * Write new range, if it returns invalid arg, then return * a parameter error. */ - if (accel_write_range(id, data) == EC_ERROR_INVAL) + if (accel_set_range(id, data, round) == EC_ERROR_INVAL) return EC_ERROR_PARAM2; } else { - ccprintf("Range for sensor %d: 0x%02x\n", id, sensor_range[id]); + accel_get_range(id, &data); + ccprintf("Range for sensor %d: %d\n", id, data); } return EC_SUCCESS; } DECLARE_CONSOLE_COMMAND(accelrange, command_accelrange, - "id [data]", + "id [data [roundup]]", "Read or write accelerometer range", NULL); static int command_accelresolution(int argc, char **argv) { char *e; - int id, data; + int id, data, round = 1; - if (argc < 2 || argc > 3) + if (argc < 2 || argc > 4) return EC_ERROR_PARAM_COUNT; /* First argument is sensor id. */ @@ -527,35 +635,42 @@ static int command_accelresolution(int argc, char **argv) if (*e || id < 0 || id > ACCEL_COUNT) return EC_ERROR_PARAM1; - if (argc == 3) { + if (argc >= 3) { /* Second argument is data to write. */ data = strtoi(argv[2], &e, 0); if (*e) return EC_ERROR_PARAM2; + if (argc == 4) { + /* Third argument is rounding flag. */ + round = strtoi(argv[3], &e, 0); + if (*e) + return EC_ERROR_PARAM3; + } + /* * Write new resolution, if it returns invalid arg, then * return a parameter error. */ - if (accel_write_resolution(id, data) == EC_ERROR_INVAL) + if (accel_set_resolution(id, data, round) == EC_ERROR_INVAL) return EC_ERROR_PARAM2; } else { - ccprintf("Resolution for sensor %d: 0x%02x\n", id, - sensor_resolution[id]); + accel_get_resolution(id, &data); + ccprintf("Resolution for sensor %d: %d\n", id, data); } return EC_SUCCESS; } DECLARE_CONSOLE_COMMAND(accelres, command_accelresolution, - "id [data]", + "id [data [roundup]]", "Read or write accelerometer resolution", NULL); static int command_acceldatarate(int argc, char **argv) { char *e; - int id, data; + int id, data, round = 1; - if (argc < 2 || argc > 3) + if (argc < 2 || argc > 4) return EC_ERROR_PARAM_COUNT; /* First argument is sensor id. */ @@ -563,27 +678,34 @@ static int command_acceldatarate(int argc, char **argv) if (*e || id < 0 || id > ACCEL_COUNT) return EC_ERROR_PARAM1; - if (argc == 3) { + if (argc >= 3) { /* Second argument is data to write. */ data = strtoi(argv[2], &e, 0); if (*e) return EC_ERROR_PARAM2; + if (argc == 4) { + /* Third argument is rounding flag. */ + round = strtoi(argv[3], &e, 0); + if (*e) + return EC_ERROR_PARAM3; + } + /* * Write new data rate, if it returns invalid arg, then * return a parameter error. */ - if (accel_write_datarate(id, data) == EC_ERROR_INVAL) + if (accel_set_datarate(id, data, round) == EC_ERROR_INVAL) return EC_ERROR_PARAM2; } else { - ccprintf("Data rate for sensor %d: 0x%02x\n", id, - sensor_datarate[id]); + accel_get_datarate(id, &data); + ccprintf("Data rate for sensor %d: %d\n", id, data); } return EC_SUCCESS; } DECLARE_CONSOLE_COMMAND(accelrate, command_acceldatarate, - "id [data]", + "id [data [roundup]]", "Read or write accelerometer range", NULL); #ifdef CONFIG_ACCEL_INTERRUPTS diff --git a/driver/accel_kxcj9.h b/driver/accel_kxcj9.h index a7137326d9..ea15b6ec17 100644 --- a/driver/accel_kxcj9.h +++ b/driver/accel_kxcj9.h @@ -100,36 +100,6 @@ #define KXCJ9_OSA_1600_HZ 7 -/** - * Write the accelerometer range. - * - * @param id Target accelerometer - * @param range Range (KXCJ9_GSEL_*). - * - * @return EC_SUCCESS if successful, non-zero if error. - */ -int accel_write_range(const enum accel_id id, const int range); - -/** - * Write the accelerometer resolution. - * - * @param id Target accelerometer - * @param range Resolution (KXCJ9_RES_*). - * - * @return EC_SUCCESS if successful, non-zero if error. - */ -int accel_write_resolution(const enum accel_id id, const int res); - -/** - * Write the accelerometer data rate. - * - * @param id Target accelerometer - * @param range Data rate (KXCJ9_OSA_*). - * - * @return EC_SUCCESS if successful, non-zero if error. - */ -int accel_write_datarate(const enum accel_id id, const int rate); - #ifdef CONFIG_ACCEL_INTERRUPTS /** * Setup a one-time accel interrupt. If the threshold is low enough, the diff --git a/include/accelerometer.h b/include/accelerometer.h index a69c227401..0c8703f3ae 100644 --- a/include/accelerometer.h +++ b/include/accelerometer.h @@ -29,15 +29,59 @@ enum accel_id; * * @return EC_SUCCESS if successful, non-zero if error. */ -int accel_read(enum accel_id id, int *x_acc, int *y_acc, int *z_acc); +int accel_read(const enum accel_id id, int * const x_acc, int * const y_acc, + int * const z_acc); /** - * Initiailze accelerometers. + * Initialize accelerometers. * * @param id Target accelerometer * * @return EC_SUCCESS if successful, non-zero if error. */ -int accel_init(enum accel_id id); +int accel_init(const enum accel_id id); + +/** + * Setter and getter methods for the sensor range. The sensor range defines + * the maximum value that can be returned from accel_read(). As the range + * increases, the resolution gets worse. + * + * @param id Target accelerometer + * @param range Range (Units are +/- G's for accel, +/- deg/s for gyro) + * @param rnd Rounding flag. If true, it rounds up to nearest valid value. + * Otherwise, it rounds down. + * + * @return EC_SUCCESS if successful, non-zero if error. + */ +int accel_set_range(const enum accel_id id, const int range, const int rnd); +int accel_get_range(const enum accel_id id, int * const range); + + +/** + * Setter and getter methods for the sensor resolution. + * + * @param id Target accelerometer + * @param range Resolution (Units are number of bits) + * param rnd Rounding flag. If true, it rounds up to nearest valid value. + * Otherwise, it rounds down. + * + * @return EC_SUCCESS if successful, non-zero if error. + */ +int accel_set_resolution(const enum accel_id id, const int res, const int rnd); +int accel_get_resolution(const enum accel_id id, int * const res); + +/** + * Setter and getter methods for the sensor output data range. As the ODR + * increases, the LPF roll-off frequency also increases. + * + * @param id Target accelerometer + * @param rate Output data rate (units are mHz) + * @param rnd Rounding flag. If true, it rounds up to nearest valid value. + * Otherwise, it rounds down. + * + * @return EC_SUCCESS if successful, non-zero if error. + */ +int accel_set_datarate(const enum accel_id id, const int rate, const int rnd); +int accel_get_datarate(const enum accel_id id, int * const rate); #endif /* __CROS_EC_ACCELEROMETER_H */ diff --git a/include/ec_commands.h b/include/ec_commands.h index 7b2f5c645a..082c4d16c0 100644 --- a/include/ec_commands.h +++ b/include/ec_commands.h @@ -1161,6 +1161,130 @@ enum ec_vboot_hash_status { #define EC_VBOOT_HASH_OFFSET_RW 0xfffffffd /*****************************************************************************/ +/* + * Motion sense commands. We'll make separate structs for sub-commands with + * different input args, so that we know how much to expect. + */ +#define EC_CMD_MOTION_SENSE_CMD 0x2B + +/* Motion sense commands */ +enum motionsense_command { + /* + * Dump command returns all motion sensor data including a physical + * presence bit for each potential sensor. + */ + MOTIONSENSE_CMD_DUMP = 0, + + /* + * Info command returns data describing the details of a given sensor, + * including enum motionsensor_type, enum motionsensor_location, and + * enum motionsensor_chip. + */ + MOTIONSENSE_CMD_INFO = 1, + + /* + * EC Rate command is a setter/getter command for the EC sampling rate + * of all motion sensors in milliseconds. + */ + MOTIONSENSE_CMD_EC_RATE = 2, + + /* + * Sensor ODR command is a setter/getter command for the output data + * rate of a specific motion sensor in millihertz. + */ + MOTIONSENSE_CMD_SENSOR_ODR = 3, + + /* + * Sensor range command is a setter/getter command for the range of + * a specified motion sensor in +/-G's or +/- deg/s. + */ + MOTIONSENSE_CMD_SENSOR_RANGE = 4, + + /* Number of motionsense sub-commands. */ + MOTIONSENSE_NUM_CMDS +}; + +/* List of motion sensor types. */ +enum motionsensor_type { + MOTIONSENSE_TYPE_ACCEL, + MOTIONSENSE_TYPE_GYRO, +}; + +/* List of motion sensor locations. */ +enum motionsensor_location { + MOTIONSENSE_LOC_BASE, + MOTIONSENSE_LOC_LID, +}; + +/* List of motion sensor chips. */ +enum motionsensor_chip { + MOTIONSENSE_CHIP_KXCJ9, +}; + +/* + * Send this value for the data element to only perform a read. If you + * send any other value, the EC will interpret it as data to set. + */ +#define EC_MOTION_SENSE_NO_VALUE -1 + +struct ec_params_motion_sense { + uint8_t cmd; + union { + /* Used for MOTIONSENSE_CMD_DUMP. */ + struct { + /* no args */ + } dump; + + /* Used for MOTIONSENSE_CMD_EC_RATE. */ + struct { + int16_t data; + } ec_rate; + + /* Used for MOTIONSENSE_CMD_INFO. */ + struct { + uint8_t sensor_num; + } info; + + /* + * Used for MOTIONSENSE_CMD_SENSOR_ODR and + * MOTIONSENSE_CMD_SENSOR_RANGE. + */ + struct { + uint8_t sensor_num; + uint8_t roundup; + uint16_t reserved; + int32_t data; + } sensor_odr, sensor_range; + }; +} __packed; + +struct ec_response_motion_sense { + union { + /* Used for MOTIONSENSE_CMD_DUMP. */ + struct { + uint8_t sensor_presence[3]; + uint8_t reserved; + int16_t data[9]; + } dump; + + /* Used for MOTIONSENSE_CMD_INFO. */ + struct { + uint8_t type; + uint8_t location; + uint8_t chip; + } info; + + /* + * Used for MOTIONSENSE_CMD_EC_RATE, MOTIONSENSE_CMD_SENSOR_ODR, + * and MOTIONSENSE_CMD_SENSOR_RANGE. + */ + struct { + int32_t ret; + } ec_rate, sensor_odr, sensor_range; + }; +} __packed; + +/*****************************************************************************/ /* USB charging control commands */ /* Set USB port charging mode */ @@ -2143,7 +2267,7 @@ struct ec_params_reboot_ec { * Header bytes greater than this indicate a later version. For example, * EC_CMD_VERSION0 + 1 means we are using version 1. * - * The old EC interface must not use commands 0dc or higher. + * The old EC interface must not use commands 0xdc or higher. */ #define EC_CMD_VERSION0 0xdc diff --git a/test/math_util.c b/test/math_util.c index bfc0345655..5ace8d2a83 100644 --- a/test/math_util.c +++ b/test/math_util.c @@ -14,16 +14,39 @@ /*****************************************************************************/ /* Mock functions */ -/* Need to define accelerometer init and read functions just to compile. */ +/* Need to define accelerometer functions just to compile. */ int accel_init(enum accel_id id) { return EC_SUCCESS; } - int accel_read(enum accel_id id, int *x_acc, int *y_acc, int *z_acc) { return EC_SUCCESS; } +int accel_set_range(const enum accel_id id, const int range, const int rnd) +{ + return EC_SUCCESS; +} +int accel_get_range(const enum accel_id id, int * const range) +{ + return EC_SUCCESS; +} +int accel_set_resolution(const enum accel_id id, const int res, const int rnd) +{ + return EC_SUCCESS; +} +int accel_get_resolution(const enum accel_id id, int * const res) +{ + return EC_SUCCESS; +} +int accel_set_datarate(const enum accel_id id, const int rate, const int rnd) +{ + return EC_SUCCESS; +} +int accel_get_datarate(const enum accel_id id, int * const rate) +{ + return EC_SUCCESS; +} /*****************************************************************************/ /* Test utilities */ diff --git a/test/motion_sense.c b/test/motion_sense.c index c332f962c5..ab07a41e96 100644 --- a/test/motion_sense.c +++ b/test/motion_sense.c @@ -34,6 +34,31 @@ int accel_read(enum accel_id id, int *x_acc, int *y_acc, int *z_acc) return EC_SUCCESS; } +int accel_set_range(const enum accel_id id, const int range, const int rnd) +{ + return EC_SUCCESS; +} +int accel_get_range(const enum accel_id id, int * const range) +{ + return EC_SUCCESS; +} +int accel_set_resolution(const enum accel_id id, const int res, const int rnd) +{ + return EC_SUCCESS; +} +int accel_get_resolution(const enum accel_id id, int * const res) +{ + return EC_SUCCESS; +} +int accel_set_datarate(const enum accel_id id, const int rate, const int rnd) +{ + return EC_SUCCESS; +} +int accel_get_datarate(const enum accel_id id, int * const rate) +{ + return EC_SUCCESS; +} + /*****************************************************************************/ /* Test utilities */ diff --git a/util/ectool.c b/util/ectool.c index 2e58dacef1..712f4fcf9d 100644 --- a/util/ectool.c +++ b/util/ectool.c @@ -106,6 +106,8 @@ const char help_str[] = " Set the color of an LED or query brightness range\n" " lightbar [CMDS]\n" " Various lightbar control commands\n" + " motionsense [CMDS]\n" + " Various motion sense control commands\n" " panicinfo\n" " Prints saved panic info\n" " pause_in_s5 [on|off]\n" @@ -1686,6 +1688,224 @@ static int cmd_lightbar(int argc, char **argv) return lb_help(argv[0]); } +/* Create an array to store sizes of motion sense param and response structs. */ +#define MS_SIZES(SUBCMD) { \ + sizeof(((struct ec_params_motion_sense *)0)->SUBCMD) \ + + sizeof(((struct ec_params_motion_sense *)0)->cmd), \ + sizeof(((struct ec_response_motion_sense *)0)->SUBCMD) } +static const struct { + uint8_t insize; + uint8_t outsize; +} ms_command_sizes[] = { + MS_SIZES(dump), + MS_SIZES(info), + MS_SIZES(ec_rate), + MS_SIZES(sensor_odr), + MS_SIZES(sensor_range), +}; +BUILD_ASSERT(ARRAY_SIZE(ms_command_sizes) == MOTIONSENSE_NUM_CMDS); +#undef MS_SIZES + +static int ms_help(const char *cmd) +{ + printf("Usage:\n"); + printf(" %s - dump all motion data\n", cmd); + printf(" %s info NUM - print sensor info\n", cmd); + printf(" %s ec_rate [RATE_MS] - set/get sample rate\n", cmd); + printf(" %s odr NUM [ODR [ROUNDUP]] - set/get sensor ODR\n", cmd); + printf(" %s range NUM [RANGE [ROUNDUP]]- set/get sensor range\n", cmd); + + return 0; +} + +static int cmd_motionsense(int argc, char **argv) +{ + int i, rv; + struct ec_params_motion_sense param; + struct ec_response_motion_sense resp; + char *e; + + /* No motionsense command has more than 5 args. */ + if (argc > 5) + return ms_help(argv[0]); + + if (argc == 1) { + /* No args, dump motion data. */ + param.cmd = MOTIONSENSE_CMD_DUMP; + rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 0, + ¶m, ms_command_sizes[param.cmd].insize, + &resp, ms_command_sizes[param.cmd].outsize); + + if (rv < 0) + return rv; + + for (i = 0; i < ARRAY_SIZE(resp.dump.sensor_presence); i++) { + printf("Sensor %d: ", i); + if (resp.dump.sensor_presence[i]) + printf("%d\t%d\t%d\n", resp.dump.data[3*i], + resp.dump.data[3*i+1], + resp.dump.data[3*i+2]); + else + printf("None\n"); + } + + return 0; + } + + if (argc == 3 && !strcasecmp(argv[1], "info")) { + param.cmd = MOTIONSENSE_CMD_INFO; + + param.sensor_odr.sensor_num = strtol(argv[2], &e, 0); + if (e && *e) { + fprintf(stderr, "Bad %s arg.\n", argv[1]); + return -1; + } + + rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 0, + ¶m, ms_command_sizes[param.cmd].insize, + &resp, ms_command_sizes[param.cmd].outsize); + + if (rv < 0) + return rv; + + printf("Type: "); + switch (resp.info.type) { + case MOTIONSENSE_TYPE_ACCEL: + printf("accel\n"); + break; + case MOTIONSENSE_TYPE_GYRO: + printf("gyro\n"); + break; + default: + printf("unknown\n"); + } + + printf("Location: "); + switch (resp.info.location) { + case MOTIONSENSE_LOC_BASE: + printf("base\n"); + break; + case MOTIONSENSE_LOC_LID: + printf("lid\n"); + break; + default: + printf("unknown\n"); + } + + printf("Chip: "); + switch (resp.info.chip) { + case MOTIONSENSE_CHIP_KXCJ9: + printf("kxcj9\n"); + break; + default: + printf("unknown\n"); + } + + return 0; + } + + if (argc < 4 && !strcasecmp(argv[1], "ec_rate")) { + param.cmd = MOTIONSENSE_CMD_EC_RATE; + param.ec_rate.data = EC_MOTION_SENSE_NO_VALUE; + + if (argc == 3) { + param.ec_rate.data = strtol(argv[2], &e, 0); + if (e && *e) { + fprintf(stderr, "Bad %s arg.\n", argv[1]); + return -1; + } + } + + rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 0, + ¶m, ms_command_sizes[param.cmd].insize, + &resp, ms_command_sizes[param.cmd].outsize); + + if (rv < 0) + return rv; + + printf("%d\n", resp.ec_rate.ret); + return 0; + } + + if (argc > 2 && !strcasecmp(argv[1], "odr")) { + param.cmd = MOTIONSENSE_CMD_SENSOR_ODR; + param.sensor_odr.data = EC_MOTION_SENSE_NO_VALUE; + param.sensor_odr.roundup = 1; + + param.sensor_odr.sensor_num = strtol(argv[2], &e, 0); + if (e && *e) { + fprintf(stderr, "Bad %s arg.\n", argv[1]); + return -1; + } + + if (argc >= 4) { + param.sensor_odr.data = strtol(argv[3], &e, 0); + if (e && *e) { + fprintf(stderr, "Bad %s arg.\n", argv[1]); + return -1; + } + } + + if (argc == 5) { + param.sensor_odr.roundup = strtol(argv[4], &e, 0); + if (e && *e) { + fprintf(stderr, "Bad %s arg.\n", argv[1]); + return -1; + } + } + + rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 0, + ¶m, ms_command_sizes[param.cmd].insize, + &resp, ms_command_sizes[param.cmd].outsize); + + if (rv < 0) + return rv; + + printf("%d\n", resp.sensor_odr.ret); + return 0; + } + + if (argc > 2 && !strcasecmp(argv[1], "range")) { + param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE; + param.sensor_range.data = EC_MOTION_SENSE_NO_VALUE; + param.sensor_odr.roundup = 1; + + param.sensor_range.sensor_num = strtol(argv[2], &e, 0); + if (e && *e) { + fprintf(stderr, "Bad %s arg.\n", argv[1]); + return -1; + } + + if (argc >= 4) { + param.sensor_range.data = strtol(argv[3], &e, 0); + if (e && *e) { + fprintf(stderr, "Bad %s arg.\n", argv[1]); + return -1; + } + } + + if (argc == 5) { + param.sensor_odr.roundup = strtol(argv[4], &e, 0); + if (e && *e) { + fprintf(stderr, "Bad %s arg.\n", argv[1]); + return -1; + } + } + + rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 0, + ¶m, ms_command_sizes[param.cmd].insize, + &resp, ms_command_sizes[param.cmd].outsize); + + if (rv < 0) + return rv; + + printf("%d\n", resp.sensor_range.ret); + return 0; + } + + return ms_help(argv[0]); +} + static int find_led_color_by_name(const char *color) { int i; @@ -3681,6 +3901,7 @@ const struct command commands[] = { {"lightbar", cmd_lightbar}, {"keyconfig", cmd_keyconfig}, {"keyscan", cmd_keyscan}, + {"motionsense", cmd_motionsense}, {"panicinfo", cmd_panic_info}, {"pause_in_s5", cmd_s5}, {"powerinfo", cmd_power_info}, |