| /* Copyright 2020 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 "accelgyro.h" |
| #include "atomic.h" |
| #include "hwtimer.h" |
| #include "online_calibration.h" |
| #include "common.h" |
| #include "mag_cal.h" |
| #include "util.h" |
| #include "vec3.h" |
| #include "task.h" |
| #include "ec_commands.h" |
| #include "accel_cal.h" |
| #include "mkbp_event.h" |
| #include "gyro_cal.h" |
| |
| #define CPRINTS(format, args...) cprints(CC_MOTION_SENSE, format, ##args) |
| |
| #ifndef CONFIG_MKBP_EVENT |
| #error "Must use CONFIG_MKBP_EVENT for online calibration" |
| #endif /* CONFIG_MKBP_EVENT */ |
| |
| /** Bitmap telling which online calibration values are valid. */ |
| static uint32_t sensor_calib_cache_valid_map; |
| /** Bitmap telling which online calibration values are dirty. */ |
| static uint32_t sensor_calib_cache_dirty_map; |
| |
| struct mutex g_calib_cache_mutex; |
| |
| static int get_temperature(struct motion_sensor_t *sensor, int *temp) |
| { |
| struct online_calib_data *entry = sensor->online_calib_data; |
| uint32_t now; |
| |
| if (sensor->drv->read_temp == NULL) |
| return EC_ERROR_UNIMPLEMENTED; |
| |
| now = __hw_clock_source_read(); |
| if (entry->last_temperature < 0 || |
| time_until(entry->last_temperature_timestamp, now) > |
| CONFIG_TEMP_CACHE_STALE_THRES) { |
| int t; |
| int rc = sensor->drv->read_temp(sensor, &t); |
| |
| if (rc == EC_SUCCESS) { |
| entry->last_temperature = t; |
| entry->last_temperature_timestamp = now; |
| } else { |
| return rc; |
| } |
| } |
| |
| *temp = entry->last_temperature; |
| return EC_SUCCESS; |
| } |
| |
| static void data_int16_to_fp(const struct motion_sensor_t *s, |
| const int16_t *data, fpv3_t out) |
| { |
| int i; |
| fp_t range = INT_TO_FP(s->current_range); |
| |
| for (i = 0; i < 3; ++i) { |
| fp_t v = INT_TO_FP((int32_t)data[i]); |
| |
| out[i] = fp_div(v, INT_TO_FP((data[i] >= 0) ? 0x7fff : 0x8000)); |
| out[i] = fp_mul(out[i], range); |
| /* Check for overflow */ |
| out[i] = CLAMP(out[i], -range, range); |
| } |
| } |
| |
| static void data_fp_to_int16(const struct motion_sensor_t *s, const fpv3_t data, |
| int16_t *out) |
| { |
| int i; |
| fp_t range = INT_TO_FP(s->current_range); |
| |
| for (i = 0; i < 3; ++i) { |
| int32_t iv; |
| fp_t v = fp_div(data[i], range); |
| |
| v = fp_mul(v, INT_TO_FP(0x7fff)); |
| iv = FP_TO_INT(v); |
| /* Check for overflow */ |
| out[i] = ec_motion_sensor_clamp_i16(iv); |
| } |
| } |
| |
| /** |
| * Check a gyroscope for new bias. This function checks a given sensor (must be |
| * a gyroscope) for new bias values. If found, it will update the appropriate |
| * caches and notify the AP. |
| * |
| * @param sensor Pointer to the gyroscope sensor to check. |
| */ |
| static bool check_gyro_cal_new_bias(struct motion_sensor_t *sensor, |
| fpv3_t bias_out) |
| { |
| struct online_calib_data *calib_data = |
| (struct online_calib_data *)sensor->online_calib_data; |
| struct gyro_cal_data *data = |
| (struct gyro_cal_data *)calib_data->type_specific_data; |
| int temp_out; |
| uint32_t timestamp_out; |
| |
| /* Check that we have a new bias. */ |
| if (data == NULL || calib_data == NULL || |
| !gyro_cal_new_bias_available(&data->gyro_cal)) |
| return false; |
| |
| /* Read the calibration values. */ |
| gyro_cal_get_bias(&data->gyro_cal, bias_out, &temp_out, ×tamp_out); |
| return true; |
| } |
| |
| static void set_gyro_cal_cache_values(struct motion_sensor_t *sensor, |
| fpv3_t bias) |
| { |
| size_t sensor_num = sensor - motion_sensors; |
| struct online_calib_data *calib_data = |
| (struct online_calib_data *)sensor->online_calib_data; |
| |
| mutex_lock(&g_calib_cache_mutex); |
| /* Convert result to the right scale and save to cache. */ |
| data_fp_to_int16(sensor, bias, calib_data->cache); |
| /* Set valid and dirty. */ |
| sensor_calib_cache_valid_map |= BIT(sensor_num); |
| sensor_calib_cache_dirty_map |= BIT(sensor_num); |
| mutex_unlock(&g_calib_cache_mutex); |
| /* Notify the AP. */ |
| mkbp_send_event(EC_MKBP_EVENT_ONLINE_CALIBRATION); |
| } |
| |
| /** |
| * Update the data stream (accel/mag) for a given sensor and data in all |
| * gyroscopes that are interested. |
| * |
| * @param sensor Pointer to the sensor that generated the data. |
| * @param data 3 floats/fixed point data points generated by the sensor. |
| * @param timestamp The timestamp at which the data was generated. |
| */ |
| static void update_gyro_cal(struct motion_sensor_t *sensor, fpv3_t data, |
| uint32_t timestamp) |
| { |
| int i; |
| fpv3_t gyro_cal_data_out; |
| |
| /* |
| * Find gyroscopes, while we don't currently have instance where more |
| * than one are present in a board, this loop will work with any number |
| * of them. |
| */ |
| for (i = 0; i < SENSOR_COUNT; ++i) { |
| struct motion_sensor_t *s = motion_sensors + i; |
| struct gyro_cal_data *gyro_cal_data = |
| (struct gyro_cal_data *) |
| s->online_calib_data->type_specific_data; |
| bool has_new_gyro_cal_bias = false; |
| |
| /* |
| * If we're not looking at a gyroscope OR if the calibration |
| * data is NULL, skip this sensor. |
| */ |
| if (s->type != MOTIONSENSE_TYPE_GYRO || gyro_cal_data == NULL) |
| continue; |
| |
| /* |
| * Update the appropriate data stream (accel/mag) depending on |
| * which sensors the gyroscope is tracking. |
| */ |
| if (sensor->type == MOTIONSENSE_TYPE_ACCEL && |
| gyro_cal_data->accel_sensor_id == sensor - motion_sensors) { |
| gyro_cal_update_accel(&gyro_cal_data->gyro_cal, |
| timestamp, data[X], data[Y], |
| data[Z]); |
| has_new_gyro_cal_bias = |
| check_gyro_cal_new_bias(s, gyro_cal_data_out); |
| } else if (sensor->type == MOTIONSENSE_TYPE_MAG && |
| gyro_cal_data->mag_sensor_id == |
| sensor - motion_sensors) { |
| gyro_cal_update_mag(&gyro_cal_data->gyro_cal, timestamp, |
| data[X], data[Y], data[Z]); |
| has_new_gyro_cal_bias = |
| check_gyro_cal_new_bias(s, gyro_cal_data_out); |
| } |
| |
| if (has_new_gyro_cal_bias) |
| set_gyro_cal_cache_values(s, gyro_cal_data_out); |
| } |
| } |
| |
| void online_calibration_init(void) |
| { |
| size_t i; |
| |
| for (i = 0; i < SENSOR_COUNT; i++) { |
| struct motion_sensor_t *s = motion_sensors + i; |
| void *type_specific_data = NULL; |
| |
| if (s->online_calib_data) { |
| s->online_calib_data->last_temperature = -1; |
| type_specific_data = |
| s->online_calib_data->type_specific_data; |
| } |
| |
| if (!type_specific_data) |
| continue; |
| |
| switch (s->type) { |
| case MOTIONSENSE_TYPE_ACCEL: { |
| accel_cal_reset((struct accel_cal *)type_specific_data); |
| break; |
| } |
| case MOTIONSENSE_TYPE_MAG: { |
| init_mag_cal((struct mag_cal_t *)type_specific_data); |
| break; |
| } |
| case MOTIONSENSE_TYPE_GYRO: { |
| init_gyro_cal( |
| &((struct gyro_cal_data *)type_specific_data) |
| ->gyro_cal); |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| } |
| |
| bool online_calibration_has_new_values(void) |
| { |
| bool has_dirty; |
| |
| mutex_lock(&g_calib_cache_mutex); |
| has_dirty = sensor_calib_cache_dirty_map != 0; |
| mutex_unlock(&g_calib_cache_mutex); |
| |
| return has_dirty; |
| } |
| |
| bool online_calibration_read(struct motion_sensor_t *sensor, |
| struct ec_response_online_calibration_data *out) |
| { |
| int sensor_num = sensor - motion_sensors; |
| bool has_valid; |
| |
| mutex_lock(&g_calib_cache_mutex); |
| has_valid = (sensor_calib_cache_valid_map & BIT(sensor_num)) != 0; |
| if (has_valid) { |
| /* Update data in out */ |
| memcpy(out->data, sensor->online_calib_data->cache, |
| sizeof(out->data)); |
| /* Clear dirty bit */ |
| sensor_calib_cache_dirty_map &= ~(1 << sensor_num); |
| } |
| mutex_unlock(&g_calib_cache_mutex); |
| |
| return has_valid; |
| } |
| |
| int online_calibration_process_data(struct ec_response_motion_sensor_data *data, |
| struct motion_sensor_t *sensor, |
| uint32_t timestamp) |
| { |
| int sensor_num = sensor - motion_sensors; |
| int rc; |
| int temperature; |
| struct online_calib_data *calib_data; |
| fpv3_t fdata; |
| bool is_spoofed = IS_ENABLED(CONFIG_ONLINE_CALIB_SPOOF_MODE) && |
| (sensor->flags & MOTIONSENSE_FLAG_IN_SPOOF_MODE); |
| bool has_new_calibration_values = false; |
| |
| /* Convert data to fp. */ |
| data_int16_to_fp(sensor, data->data, fdata); |
| |
| calib_data = sensor->online_calib_data; |
| switch (sensor->type) { |
| case MOTIONSENSE_TYPE_ACCEL: { |
| struct accel_cal *cal = |
| (struct accel_cal *)(calib_data->type_specific_data); |
| |
| if (is_spoofed) { |
| /* Copy the data to the calibration result. */ |
| cal->bias[X] = fdata[X]; |
| cal->bias[Y] = fdata[Y]; |
| cal->bias[Z] = fdata[Z]; |
| has_new_calibration_values = true; |
| } else { |
| /* Possibly update the gyroscope calibration. */ |
| update_gyro_cal(sensor, fdata, timestamp); |
| |
| /* |
| * Temperature is required for accelerometer |
| * calibration. |
| */ |
| rc = get_temperature(sensor, &temperature); |
| if (rc != EC_SUCCESS) |
| return rc; |
| |
| has_new_calibration_values = accel_cal_accumulate( |
| cal, timestamp, fdata[X], fdata[Y], fdata[Z], |
| temperature); |
| } |
| |
| if (has_new_calibration_values) { |
| mutex_lock(&g_calib_cache_mutex); |
| /* Convert result to the right scale. */ |
| data_fp_to_int16(sensor, cal->bias, calib_data->cache); |
| /* Set valid and dirty. */ |
| sensor_calib_cache_valid_map |= BIT(sensor_num); |
| sensor_calib_cache_dirty_map |= BIT(sensor_num); |
| mutex_unlock(&g_calib_cache_mutex); |
| /* Notify the AP. */ |
| mkbp_send_event(EC_MKBP_EVENT_ONLINE_CALIBRATION); |
| } |
| break; |
| } |
| case MOTIONSENSE_TYPE_MAG: { |
| struct mag_cal_t *cal = |
| (struct mag_cal_t *)(calib_data->type_specific_data); |
| int idata[] = { |
| (int)data->data[X], |
| (int)data->data[Y], |
| (int)data->data[Z], |
| }; |
| |
| if (is_spoofed) { |
| /* Copy the data to the calibration result. */ |
| cal->bias[X] = INT_TO_FP(idata[X]); |
| cal->bias[Y] = INT_TO_FP(idata[Y]); |
| cal->bias[Z] = INT_TO_FP(idata[Z]); |
| has_new_calibration_values = true; |
| } else { |
| /* Possibly update the gyroscope calibration. */ |
| update_gyro_cal(sensor, fdata, timestamp); |
| |
| has_new_calibration_values = mag_cal_update(cal, idata); |
| } |
| |
| if (has_new_calibration_values) { |
| mutex_lock(&g_calib_cache_mutex); |
| /* Copy the values */ |
| calib_data->cache[X] = cal->bias[X]; |
| calib_data->cache[Y] = cal->bias[Y]; |
| calib_data->cache[Z] = cal->bias[Z]; |
| /* Set valid and dirty. */ |
| sensor_calib_cache_valid_map |= BIT(sensor_num); |
| sensor_calib_cache_dirty_map |= BIT(sensor_num); |
| mutex_unlock(&g_calib_cache_mutex); |
| /* Notify the AP. */ |
| mkbp_send_event(EC_MKBP_EVENT_ONLINE_CALIBRATION); |
| } |
| break; |
| } |
| case MOTIONSENSE_TYPE_GYRO: { |
| if (is_spoofed) { |
| /* |
| * Gyroscope uses fdata to store the calibration |
| * result, so there's no need to copy anything. |
| */ |
| has_new_calibration_values = true; |
| } else { |
| struct gyro_cal_data *gyro_cal_data = |
| (struct gyro_cal_data *) |
| calib_data->type_specific_data; |
| struct gyro_cal *gyro_cal = &gyro_cal_data->gyro_cal; |
| |
| /* Temperature is required for gyro calibration. */ |
| rc = get_temperature(sensor, &temperature); |
| if (rc != EC_SUCCESS) |
| return rc; |
| |
| /* Update gyroscope calibration. */ |
| gyro_cal_update_gyro(gyro_cal, timestamp, fdata[X], |
| fdata[Y], fdata[Z], temperature); |
| has_new_calibration_values = |
| check_gyro_cal_new_bias(sensor, fdata); |
| } |
| |
| if (has_new_calibration_values) |
| set_gyro_cal_cache_values(sensor, fdata); |
| break; |
| } |
| default: |
| break; |
| } |
| |
| return EC_SUCCESS; |
| } |