mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-10 17:41:54 +00:00
Changed motion_sense task to assume sensors are unpowered in G3 and re-initialize sensors every time coming out of G3. Added EC command line test utils as well. Fixed some bug during unit tests. BUG=chrome-os-partner:27313,27320 BRANCH=ToT TEST=Verified on Samus. Tested with accel EC CLIs accelread, accelrange, accelrate, accelres Tested accelcalib, a ACCEL calibration util, and it succeeded. Tested sysfs interface: cd /sys/bus/iio/devices/iio:device1 cat in_accel_*_gyro_raw Signed-off-by: Sheng-Liang Song <ssl@chromium.org> Change-Id: I5752b00c03e1942c790ea4f28610fda83fa2dcbc Reviewed-on: https://chromium-review.googlesource.com/211484 Reviewed-by: Alec Berg <alecaberg@chromium.org>
546 lines
14 KiB
C
546 lines
14 KiB
C
/* Copyright (c) 2014 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.
|
|
*/
|
|
|
|
/* KXCJ9 gsensor module for Chrome EC */
|
|
|
|
#include "accelgyro.h"
|
|
#include "common.h"
|
|
#include "console.h"
|
|
#include "driver/accel_kxcj9.h"
|
|
#include "gpio.h"
|
|
#include "i2c.h"
|
|
#include "task.h"
|
|
#include "timer.h"
|
|
#include "util.h"
|
|
|
|
#define CPUTS(outstr) cputs(CC_ACCEL, outstr)
|
|
#define CPRINTF(format, args...) cprintf(CC_ACCEL, format, ## args)
|
|
|
|
/* Number of times to attempt to enable sensor before giving up. */
|
|
#define SENSOR_ENABLE_ATTEMPTS 3
|
|
|
|
/*
|
|
* 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. */
|
|
static 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. */
|
|
static 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. */
|
|
static const struct accel_param_pair datarates[] = {
|
|
{0, KXCJ9_OSA_0_000HZ},
|
|
{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}
|
|
};
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
return i2c_read8(I2C_PORT_ACCEL, addr, reg, data_ptr);
|
|
}
|
|
|
|
/**
|
|
* Write register from accelerometer.
|
|
*/
|
|
static int raw_write8(const int addr, const int reg, int data)
|
|
{
|
|
return i2c_write8(I2C_PORT_ACCEL, addr, reg, data);
|
|
}
|
|
|
|
/**
|
|
* Disable sensor by taking it out of operating mode. When disabled, the
|
|
* acceleration data does not change.
|
|
*
|
|
* Note: This is intended to be called in a pair with enable_sensor().
|
|
*
|
|
* @data Pointer to motion sensor data
|
|
* @ctrl1 Pointer to location to store KXCJ9_CTRL1 register after disabling
|
|
*
|
|
* @return EC_SUCCESS if successful, EC_ERROR_* otherwise
|
|
*/
|
|
static int disable_sensor(const struct motion_sensor_t *s, int *ctrl1)
|
|
{
|
|
int i, ret;
|
|
|
|
/*
|
|
* Read the current state of the ctrl1 register
|
|
* so that we can restore it later.
|
|
*/
|
|
for (i = 0; i < SENSOR_ENABLE_ATTEMPTS; i++) {
|
|
ret = raw_read8(s->i2c_addr, KXCJ9_CTRL1, ctrl1);
|
|
if (ret != EC_SUCCESS)
|
|
continue;
|
|
|
|
*ctrl1 &= ~KXCJ9_CTRL1_PC1;
|
|
|
|
ret = raw_write8(s->i2c_addr, KXCJ9_CTRL1, *ctrl1);
|
|
if (ret == EC_SUCCESS)
|
|
return EC_SUCCESS;
|
|
}
|
|
CPRINTF("Error trying to disable accelerometer\n");
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Enable sensor by placing it in operating mode.
|
|
*
|
|
* Note: This is intended to be called in a pair with disable_sensor().
|
|
*
|
|
* @data Pointer to motion sensor data
|
|
* @ctrl1 Value of KXCJ9_CTRL1 register to write to sensor
|
|
*
|
|
* @return EC_SUCCESS if successful, EC_ERROR_* otherwise
|
|
*/
|
|
static int enable_sensor(const struct motion_sensor_t *s, int ctrl1)
|
|
{
|
|
int i, ret;
|
|
|
|
for (i = 0; i < SENSOR_ENABLE_ATTEMPTS; i++) {
|
|
ret = raw_read8(s->i2c_addr, KXCJ9_CTRL1, &ctrl1);
|
|
if (ret != EC_SUCCESS)
|
|
continue;
|
|
|
|
/* Enable accelerometer based on ctrl1 value. */
|
|
ret = raw_write8(s->i2c_addr, KXCJ9_CTRL1,
|
|
ctrl1 | KXCJ9_CTRL1_PC1);
|
|
|
|
/* On first success, we are done. */
|
|
if (ret == EC_SUCCESS)
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
/* Cannot enable accel, print warning and return an error. */
|
|
CPRINTF("Error trying to enable accelerometer\n");
|
|
return ret;
|
|
}
|
|
|
|
static int set_range(const struct motion_sensor_t *s,
|
|
int range,
|
|
int rnd)
|
|
{
|
|
int ret, ctrl1, ctrl1_new, index;
|
|
struct kxcj9_data *data = (struct kxcj9_data *)s->drv_data;
|
|
|
|
/* 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. */
|
|
mutex_lock(s->mutex);
|
|
ret = disable_sensor(s, &ctrl1);
|
|
if (ret != EC_SUCCESS) {
|
|
mutex_unlock(s->mutex);
|
|
return ret;
|
|
}
|
|
|
|
/* Determine new value of CTRL1 reg and attempt to write it. */
|
|
ctrl1_new = (ctrl1 & ~KXCJ9_GSEL_ALL) | ranges[index].reg;
|
|
ret = raw_write8(s->i2c_addr, KXCJ9_CTRL1, ctrl1_new);
|
|
|
|
/* If successfully written, then save the range. */
|
|
if (ret == EC_SUCCESS) {
|
|
data->sensor_range = index;
|
|
ctrl1 = ctrl1_new;
|
|
}
|
|
|
|
/* Re-enable the sensor. */
|
|
if (enable_sensor(s, ctrl1) != EC_SUCCESS)
|
|
ret = EC_ERROR_UNKNOWN;
|
|
|
|
mutex_unlock(s->mutex);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int get_range(const struct motion_sensor_t *s,
|
|
int * const range)
|
|
{
|
|
struct kxcj9_data *data = (struct kxcj9_data *)s->drv_data;
|
|
*range = ranges[data->sensor_range].val;
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
static int set_resolution(const struct motion_sensor_t *s,
|
|
int res,
|
|
int rnd)
|
|
{
|
|
int ret, ctrl1, ctrl1_new, index;
|
|
struct kxcj9_data *data = (struct kxcj9_data *)s->drv_data;
|
|
|
|
/* 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. */
|
|
mutex_lock(s->mutex);
|
|
ret = disable_sensor(s, &ctrl1);
|
|
if (ret != EC_SUCCESS) {
|
|
mutex_unlock(s->mutex);
|
|
return ret;
|
|
}
|
|
|
|
/* Determine new value of CTRL1 reg and attempt to write it. */
|
|
ctrl1_new = (ctrl1 & ~KXCJ9_RES_12BIT) | resolutions[index].reg;
|
|
ret = raw_write8(s->i2c_addr, KXCJ9_CTRL1, ctrl1_new);
|
|
|
|
/* If successfully written, then save the range. */
|
|
if (ret == EC_SUCCESS) {
|
|
data->sensor_resolution = index;
|
|
ctrl1 = ctrl1_new;
|
|
}
|
|
|
|
/* Re-enable the sensor. */
|
|
if (enable_sensor(s, ctrl1) != EC_SUCCESS)
|
|
ret = EC_ERROR_UNKNOWN;
|
|
|
|
mutex_unlock(s->mutex);
|
|
return ret;
|
|
}
|
|
|
|
static int get_resolution(const struct motion_sensor_t *s,
|
|
int *res)
|
|
{
|
|
struct kxcj9_data *data = (struct kxcj9_data *)s->drv_data;
|
|
*res = resolutions[data->sensor_resolution].val;
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
static int set_data_rate(const struct motion_sensor_t *s,
|
|
int rate,
|
|
int rnd)
|
|
{
|
|
int ret, ctrl1, index;
|
|
struct kxcj9_data *data = (struct kxcj9_data *)s->drv_data;
|
|
|
|
/* 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. */
|
|
mutex_lock(s->mutex);
|
|
ret = disable_sensor(s, &ctrl1);
|
|
if (ret != EC_SUCCESS) {
|
|
mutex_unlock(s->mutex);
|
|
return ret;
|
|
}
|
|
|
|
/* Set output data rate. */
|
|
ret = raw_write8(s->i2c_addr, KXCJ9_DATA_CTRL,
|
|
datarates[index].reg);
|
|
|
|
/* If successfully written, then save the range. */
|
|
if (ret == EC_SUCCESS)
|
|
data->sensor_datarate = index;
|
|
|
|
/* Re-enable the sensor. */
|
|
if (enable_sensor(s, ctrl1) != EC_SUCCESS)
|
|
ret = EC_ERROR_UNKNOWN;
|
|
|
|
mutex_unlock(s->mutex);
|
|
return ret;
|
|
}
|
|
|
|
static int get_data_rate(const struct motion_sensor_t *s,
|
|
int *rate)
|
|
{
|
|
struct kxcj9_data *data = (struct kxcj9_data *)s->drv_data;
|
|
*rate = datarates[data->sensor_datarate].val;
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_ACCEL_INTERRUPTS
|
|
static int set_interrupt(const struct motion_sensor_t *s,
|
|
unsigned int threshold)
|
|
{
|
|
int ctrl1, tmp, ret;
|
|
struct kxcj9_data *data = (struct kxcj9_data *)s->drv_data;
|
|
|
|
/* Disable the sensor to allow for changing of critical parameters. */
|
|
mutex_lock(s->mutex);
|
|
ret = disable_sensor(s, &ctrl1);
|
|
if (ret != EC_SUCCESS) {
|
|
mutex_unlock(s->mutex);
|
|
return ret;
|
|
}
|
|
|
|
/* Set interrupt timer to 1 so it wakes up immediately. */
|
|
ret = raw_write8(s->i2c_addr, KXCJ9_WAKEUP_TIMER, 1);
|
|
if (ret != EC_SUCCESS)
|
|
goto error_enable_sensor;
|
|
|
|
/*
|
|
* Set threshold, note threshold register is in units of 16 counts, so
|
|
* first we need to divide by 16 to get the value to send.
|
|
*/
|
|
threshold >>= 4;
|
|
ret = raw_write8(s->i2c_addr, KXCJ9_WAKEUP_THRESHOLD, threshold);
|
|
if (ret != EC_SUCCESS)
|
|
goto error_enable_sensor;
|
|
|
|
/*
|
|
* Set interrupt enable register on sensor. Note that once this
|
|
* function is called once, the interrupt stays enabled and it is
|
|
* only necessary to clear KXCJ9_INT_REL to allow the next interrupt.
|
|
*/
|
|
ret = raw_read8(s->i2c_addr, KXCJ9_INT_CTRL1, &tmp);
|
|
if (ret != EC_SUCCESS)
|
|
goto error_enable_sensor;
|
|
if (!(tmp & KXCJ9_INT_CTRL1_IEN)) {
|
|
ret = raw_write8(s->i2c_addr, KXCJ9_INT_CTRL1,
|
|
tmp | KXCJ9_INT_CTRL1_IEN);
|
|
if (ret != EC_SUCCESS)
|
|
goto error_enable_sensor;
|
|
}
|
|
|
|
/*
|
|
* Clear any pending interrupt on sensor by reading INT_REL register.
|
|
* Note: this register latches motion detected above threshold. Once
|
|
* latched, no interrupt can occur until this register is cleared.
|
|
*/
|
|
ret = raw_read8(s->i2c_addr, KXCJ9_INT_REL, &tmp);
|
|
|
|
error_enable_sensor:
|
|
/* Re-enable the sensor. */
|
|
if (enable_sensor(s, ctrl1) != EC_SUCCESS)
|
|
ret = EC_ERROR_UNKNOWN;
|
|
mutex_unlock(s->mutex);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static int read(const struct motion_sensor_t *s,
|
|
int *x_acc,
|
|
int *y_acc,
|
|
int *z_acc)
|
|
{
|
|
uint8_t acc[6];
|
|
uint8_t reg = KXCJ9_XOUT_L;
|
|
int ret, multiplier;
|
|
struct kxcj9_data *data = (struct kxcj9_data *)s->drv_data;
|
|
|
|
/* Read 6 bytes starting at KXCJ9_XOUT_L. */
|
|
mutex_lock(s->mutex);
|
|
i2c_lock(I2C_PORT_ACCEL, 1);
|
|
ret = i2c_xfer(I2C_PORT_ACCEL, s->i2c_addr, ®, 1, acc, 6,
|
|
I2C_XFER_SINGLE);
|
|
i2c_lock(I2C_PORT_ACCEL, 0);
|
|
mutex_unlock(s->mutex);
|
|
|
|
if (ret != EC_SUCCESS)
|
|
return ret;
|
|
|
|
/* Determine multiplier based on stored range. */
|
|
switch (ranges[data->sensor_range].reg) {
|
|
case KXCJ9_GSEL_2G:
|
|
multiplier = 1;
|
|
break;
|
|
case KXCJ9_GSEL_4G:
|
|
multiplier = 2;
|
|
break;
|
|
case KXCJ9_GSEL_8G:
|
|
case KXCJ9_GSEL_8G_14BIT:
|
|
multiplier = 4;
|
|
break;
|
|
default:
|
|
return EC_ERROR_UNKNOWN;
|
|
}
|
|
|
|
/*
|
|
* Convert acceleration to a signed 12-bit number. Note, based on
|
|
* the order of the registers:
|
|
*
|
|
* acc[0] = KXCJ9_XOUT_L
|
|
* acc[1] = KXCJ9_XOUT_H
|
|
* acc[2] = KXCJ9_YOUT_L
|
|
* acc[3] = KXCJ9_YOUT_H
|
|
* acc[4] = KXCJ9_ZOUT_L
|
|
* acc[5] = KXCJ9_ZOUT_H
|
|
*/
|
|
*x_acc = multiplier * (((int8_t)acc[1]) << 4) | (acc[0] >> 4);
|
|
*y_acc = multiplier * (((int8_t)acc[3]) << 4) | (acc[2] >> 4);
|
|
*z_acc = multiplier * (((int8_t)acc[5]) << 4) | (acc[4] >> 4);
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
#ifdef CONFIG_ACCEL_INTERRUPTS
|
|
static int config_interrupt(const struct motion_sensor_t *s)
|
|
{
|
|
int ctrl1;
|
|
mutex_lock(s->mutex);
|
|
|
|
/* Disable the sensor to allow for changing of critical parameters. */
|
|
ret = disable_sensor(s, &ctrl1);
|
|
if (ret != EC_SUCCESS)
|
|
goto cleanup_exit;
|
|
|
|
/* Enable wake up (motion detect) functionality. */
|
|
ret = raw_read8(s->i2c_addr, KXCJ9_CTRL1, &tmp);
|
|
tmp &= ~KXCJ9_CTRL1_PC1;
|
|
tmp |= KXCJ9_CTRL1_WUFE;
|
|
ret = raw_write8(s->i2c_addr, KXCJ9_CTRL1, tmp);
|
|
|
|
/* Set interrupt polarity to rising edge and keep interrupt disabled. */
|
|
ret = raw_write8(s->i2c_addr,
|
|
KXCJ9_INT_CTRL1,
|
|
KXCJ9_INT_CTRL1_IEA);
|
|
if (ret != EC_SUCCESS)
|
|
goto cleanup_exit;
|
|
|
|
/* Set output data rate for wake-up interrupt function. */
|
|
ret = raw_write8(s->i2c_addr, KXCJ9_CTRL2, KXCJ9_OWUF_100_0HZ);
|
|
if (ret != EC_SUCCESS)
|
|
goto cleanup_exit;
|
|
|
|
/* Set interrupt to trigger on motion on any axis. */
|
|
ret = raw_write8(s->i2c_addr, KXCJ9_INT_CTRL2,
|
|
KXCJ9_INT_SRC2_XNWU | KXCJ9_INT_SRC2_XPWU |
|
|
KXCJ9_INT_SRC2_YNWU | KXCJ9_INT_SRC2_YPWU |
|
|
KXCJ9_INT_SRC2_ZNWU | KXCJ9_INT_SRC2_ZPWU);
|
|
if (ret != EC_SUCCESS)
|
|
goto cleanup_exit;
|
|
|
|
/*
|
|
* Enable accel interrupts. Note: accels will not initiate an interrupt
|
|
* until interrupt enable bit in KXCJ9_INT_CTRL1 is set on the device.
|
|
*/
|
|
gpio_enable_interrupt(GPIO_ACCEL_INT_LID);
|
|
gpio_enable_interrupt(GPIO_ACCEL_INT_BASE);
|
|
|
|
/* Enable the sensor. */
|
|
ret = enable_sensor(s, ctrl1);
|
|
cleanup_exit:
|
|
mutex_unlock(s->mutex);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static int init(const struct motion_sensor_t *s)
|
|
{
|
|
int ret = EC_SUCCESS;
|
|
int cnt = 0, tmp, range, rate;
|
|
|
|
/*
|
|
* This sensor can be powered through an EC reboot, so the state of
|
|
* the sensor is unknown here. Initiate software reset to restore
|
|
* sensor to default.
|
|
*/
|
|
mutex_lock(s->mutex);
|
|
ret = raw_write8(s->i2c_addr, KXCJ9_CTRL2, KXCJ9_CTRL2_SRST);
|
|
mutex_unlock(s->mutex);
|
|
if (ret != EC_SUCCESS)
|
|
return ret;
|
|
|
|
/* Wait until software reset is complete or timeout. */
|
|
do {
|
|
/* Added 1m delay after software reset */
|
|
msleep(1);
|
|
|
|
ret = raw_read8(s->i2c_addr, KXCJ9_CTRL2, &tmp);
|
|
if (ret != EC_SUCCESS)
|
|
return ret;
|
|
|
|
/* Reset complete. */
|
|
if (ret == EC_SUCCESS && !(tmp & KXCJ9_CTRL2_SRST))
|
|
break;
|
|
|
|
/* Check for timeout. */
|
|
if (cnt++ > 5) {
|
|
ret = EC_ERROR_TIMEOUT;
|
|
CPRINTF("%s: SRST Error.\n", s->name);
|
|
return ret;
|
|
}
|
|
} while (1);
|
|
|
|
ret = set_range(s, 2, 1);
|
|
if (ret != EC_SUCCESS)
|
|
return ret;
|
|
|
|
ret = set_resolution(s, 12, 1);
|
|
if (ret != EC_SUCCESS)
|
|
return ret;
|
|
|
|
ret = set_data_rate(s, 100000, 1);
|
|
if (ret != EC_SUCCESS)
|
|
return ret;
|
|
|
|
#ifdef CONFIG_ACCEL_INTERRUPTS
|
|
config_interrupt(s);
|
|
#endif
|
|
get_range(s, &range);
|
|
get_data_rate(s, &rate);
|
|
CPRINTF("[%T %s: Done Init type:0x%X range:%d rate:%d]\n",
|
|
s->name, s->type, range, rate);
|
|
|
|
return ret;
|
|
}
|
|
|
|
const struct accelgyro_drv kxcj9_drv = {
|
|
.init = init,
|
|
.read = read,
|
|
.set_range = set_range,
|
|
.get_range = get_range,
|
|
.set_resolution = set_resolution,
|
|
.get_resolution = get_resolution,
|
|
.set_data_rate = set_data_rate,
|
|
.get_data_rate = get_data_rate,
|
|
#ifdef CONFIG_ACCEL_INTERRUPTS
|
|
.set_interrupt = set_interrupt,
|
|
#endif
|
|
};
|