RE: [PATCH 05/11] misc: MPU3050 and slave device configuration.
From: Nathan Royer
Date: Fri Jul 01 2011 - 13:56:08 EST
Regarding the secondary slave, I've decided to explain it in more detail
in this patch thread since this is where the high level configuration
happens. I've clipped a number of the details to highlight just the high
level activity. For the 3050 master i2c, configuration only occurs with
the accelerometer, but with the 6050 (not included in this patch), it can
support both an accel (external or internal), and compass, and pressure.
The first function here sets the bypass mode. When in bypass mode, the
device master I2C of the mpu3050 is off and the slave chip (accelerometer
for the 3050) can be accessed directly on the same bus as the mpu3050.
When enable is set to false, the slave is disconnected from the bus, and
at that point the only operation that can occur is for the mpu3050 master
i2c to do a block read into the memory of the DMP. The DMP then makes
this data available through its FIFO after some processing with the gyro
data.
Because of the bypass functionality, we designed all acces to the slave to
be through the MPU driver instead of stand alone. That way we can first
put the mpu3050 into bypass before calling any of the slave functions that
will communicate directly with the chip. For example the config functions
with apply == false do not enable bypass mode.
> +/**
> + * @brief enables/disables the I2C bypass to an external device
> + * connected to MPU's secondary I2C bus.
> + * @param enable
> + * Non-zero to enable pass through.
> + * @return INV_SUCCESS if successful, a non-zero error code
> otherwise.
> + */
> +static int mpu_set_i2c_bypass(struct mldl_cfg *mldl_cfg,
> + void *mlsl_handle, unsigned char enable)
> +{
> + return mpu3050_set_i2c_bypass(mldl_cfg, mlsl_handle, enable);
> +}
> +
This next function sets up the before mentioned block read, by setting the
slave I2C address, the slave I2C starting register, and the size of the
read.
> +
> +static int mpu_set_slave(struct mldl_cfg *mldl_cfg,
> + void *gyro_handle,
> + struct ext_slave_descr *slave,
> + struct ext_slave_platform_data *slave_pdata,
> + int slave_id)
> +{
> + return mpu_set_slave_mpu3050(mldl_cfg, gyro_handle, slave,
> + slave_pdata, slave_id);
> +}
The rest of the work happens in the suspend and resume functions:
> +int inv_mpu_resume(struct mldl_cfg *mldl_cfg,
> + void *gyro_handle,
> + void *accel_handle,
> + void *compass_handle,
> + void *pressure_handle,
> + unsigned long sensors)
> +{
...
> + for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
> + if (!mldl_cfg->slave[ii] ||
> + !mldl_cfg->pdata_slave[ii] ||
> + !resume_slave[ii] ||
> + !(mldl_cfg->inv_mpu_state->status & (1 << ii)))
> + continue;
> +
This loop is intended to also support the mpu6050 where multiple slaves
can be on the secondary bus, so each slave is checked to see if it is on
the secondary bus. If it is, switch to bypass mode and resume the device.
> + if (EXT_SLAVE_BUS_SECONDARY ==
> + mldl_cfg->pdata_slave[ii]->bus) {
> + result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle,
> + true);
> + if (result) {
> + LOG_RESULT_LOCATION(result);
> + return result;
> + }
> + }
> + result = mldl_cfg->slave[ii]->resume(slave_handle[ii],
> + mldl_cfg->slave[ii],
> +
mldl_cfg->pdata_slave[ii]);
> + if (result) {
> + LOG_RESULT_LOCATION(result);
> + return result;
> + }
> + mldl_cfg->inv_mpu_state->status &= ~(1 << ii);
> + }
Here is where the slave is set up.
> + for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
> + if (resume_dmp &&
> + !(mldl_cfg->inv_mpu_state->status & (1 << ii)) &&
> + mldl_cfg->pdata_slave[ii] &&
> + EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata_slave[ii]-
> >bus) {
> + result = mpu_set_slave(mldl_cfg,
> + gyro_handle,
> + mldl_cfg->slave[ii],
> + mldl_cfg->pdata_slave[ii],
> + mldl_cfg->slave[ii]->type);
> + if (result) {
> + LOG_RESULT_LOCATION(result);
> + return result;
> + }
> + }
> + }
> +
> + /* Turn on the master i2c iterface if necessary */
> + if (resume_dmp) {
Here is where the bypass mode removed if the DMP needs the data. If this
is done access to the slave cannot occur unless the mpu3050 is first
bypassed.
> + result = mpu_set_i2c_bypass(
> + mldl_cfg, gyro_handle,
> + !(mldl_cfg->inv_mpu_state->i2c_slaves_enabled));
> + if (result) {
> + LOG_RESULT_LOCATION(result);
> + return result;
> + }
> +
> + /* Now start */
> + result = dmp_start(mldl_cfg, gyro_handle);
> + if (result) {
> + LOG_RESULT_LOCATION(result);
> + return result;
> + }
> + }
> + mldl_cfg->inv_mpu_cfg->requested_sensors = sensors;
> +
> + return result;
> +}
We weren't interested in standalone drivers at the time we started writing
the driver, but I think now is the time to start thinking about how to
merge the two sets of requirements. We currently support each sensor in a
standalone fashion, but in our own unique way, I just need to figure out
the right way.
The feedback I've seen so far suggest that the iio layer is where this
driver belongs and how the standalone interface should look. Today was
the first I heard of the iio layer, so I have some significant research to
do on how to do this. From my brief look this morning, it looks like
there is a lot of stuff already in place that we can make use of.
Again, everybody thank you for the feedback. Very helpful!
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/