Message ID | 1309486707-1658-11-git-send-email-nroyer@invensense.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
The slave devices all appear to be generic i2c interface hardware. I don't see why they are separate special slave devices to your driver, they should simply be i²c drivers so they can be used when those devices are found without the mpu3050. Alan -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Fri, 1 Jul 2011 08:56:04 +0100, Alan Cox wrote: > The slave devices all appear to be generic i2c interface hardware. I > don't see why they are separate special slave devices to your driver, > they should simply be i²c drivers so they can be used when those > devices are found without the mpu3050. Seconded. As a matter of fact, we already have a driver for the AK8975 magnetometer: drivers/staging/iio/magnetometer/ak8975.c
Hang on, so your motion processing unit takes in a pressure sensor? Interesting.... Manuel, am I imagining things or did you have a driver for one of these? Happy to take barometers in IIO. > Signed-off-by: Nathan Royer <nroyer@invensense.com> > --- > drivers/misc/inv_mpu/Kconfig | 1 + > drivers/misc/inv_mpu/Makefile | 1 + > drivers/misc/inv_mpu/pressure/Kconfig | 20 ++ > drivers/misc/inv_mpu/pressure/Makefile | 8 + > drivers/misc/inv_mpu/pressure/bma085.c | 367 ++++++++++++++++++++++++++++++++ > 5 files changed, 397 insertions(+), 0 deletions(-) > create mode 100644 drivers/misc/inv_mpu/pressure/Kconfig > create mode 100644 drivers/misc/inv_mpu/pressure/Makefile > create mode 100644 drivers/misc/inv_mpu/pressure/bma085.c > > diff --git a/drivers/misc/inv_mpu/Kconfig b/drivers/misc/inv_mpu/Kconfig > index 7d9f2c3..ea7d754 100644 > --- a/drivers/misc/inv_mpu/Kconfig > +++ b/drivers/misc/inv_mpu/Kconfig > @@ -57,5 +57,6 @@ endchoice > > source "drivers/misc/inv_mpu/accel/Kconfig" > source "drivers/misc/inv_mpu/compass/Kconfig" > +source "drivers/misc/inv_mpu/pressure/Kconfig" > > endif #INV_SENSORS > diff --git a/drivers/misc/inv_mpu/Makefile b/drivers/misc/inv_mpu/Makefile > index dc77a1b..506b8f5 100644 > --- a/drivers/misc/inv_mpu/Makefile > +++ b/drivers/misc/inv_mpu/Makefile > @@ -18,3 +18,4 @@ EXTRA_CFLAGS += -Idrivers/misc/inv_mpu > > obj-y += accel/ > obj-y += compass/ > +obj-y += pressure/ > diff --git a/drivers/misc/inv_mpu/pressure/Kconfig b/drivers/misc/inv_mpu/pressure/Kconfig > new file mode 100644 > index 0000000..f1c021e > --- /dev/null > +++ b/drivers/misc/inv_mpu/pressure/Kconfig > @@ -0,0 +1,20 @@ > +menuconfig: INV_SENSORS_PRESSURE > + bool "Pressure Sensor Slaves" > + depends on INV_SENSORS > + default y > + help > + Select y to see a list of supported pressure sensors that can be > + integrated with the MPUxxxx set of motion processors. > + > +if INV_SENSORS_PRESSURE > + > +config MPU_SENSORS_BMA085 > + tristate "Bosch BMA085" > + help > + This enables support for the Bosch bma085 pressure sensor > + This support is for integration with the MPU3050 or MPU6050 gyroscope > + device driver. Only one accelerometer can be registered at a time. > + Specifying more that one accelerometer in the board file will result > + in runtime errors. That's a seriously weird message for a pressure sensor... > + > +endif > diff --git a/drivers/misc/inv_mpu/pressure/Makefile b/drivers/misc/inv_mpu/pressure/Makefile > new file mode 100644 > index 0000000..595923d > --- /dev/null > +++ b/drivers/misc/inv_mpu/pressure/Makefile > @@ -0,0 +1,8 @@ > +# > +# Pressure Slaves to MPUxxxx > +# > +obj-$(CONFIG_MPU_SENSORS_BMA085) += inv_mpu_bma085.o > +inv_mpu_bma085-objs += bma085.o > + > +EXTRA_CFLAGS += -Idrivers/misc/inv_mpu > +EXTRA_CFLAGS += -D__C99_DESIGNATED_INITIALIZER > diff --git a/drivers/misc/inv_mpu/pressure/bma085.c b/drivers/misc/inv_mpu/pressure/bma085.c > new file mode 100644 > index 0000000..696d2b6 > --- /dev/null > +++ b/drivers/misc/inv_mpu/pressure/bma085.c > @@ -0,0 +1,367 @@ > +/* > + $License: > + Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. > + > + This program is free software; you can redistribute it and/or modify > + it under the terms of the GNU General Public License as published by > + the Free Software Foundation; either version 2 of the License, or > + (at your option) any later version. > + > + This program is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + GNU General Public License for more details. > + > + You should have received a copy of the GNU General Public License > + along with this program. If not, see <http://www.gnu.org/licenses/>. > + $ > + */ > + > +/** > + * @defgroup ACCELDL (Motion Library - Pressure Driver Layer) > + * @brief Provides the interface to setup and handle a pressure > + * connected to the secondary I2C interface of the gyroscope. > + * > + * @{ > + * @file bma085.c > + * @brief Pressure setup and handling methods. > + */ > + > +/* ------------------ */ > +/* - Include Files. - */ > +/* ------------------ */ > + > +#include <linux/i2c.h> > +#include <linux/module.h> > +#include <linux/moduleparam.h> > +#include <linux/kernel.h> > +#include <linux/errno.h> > +#include <linux/slab.h> > +#include <linux/delay.h> > +#include "mpu-dev.h" > + > +#include <linux/mpu.h> > +#include "mlsl.h" > +#include "log.h" > + > +/* > + * this structure holds all device specific calibration parameters > + */ > +struct bmp085_calibration_param_t { > + short ac1; > + short ac2; > + short ac3; > + unsigned short ac4; > + unsigned short ac5; > + unsigned short ac6; > + short b1; > + short b2; > + short mb; > + short mc; > + short md; > + long param_b5; > +}; > + > +struct bmp085_calibration_param_t cal_param; > + > +#define PRESSURE_BMA085_PARAM_MG 3038 /* calibration parameter */ > +#define PRESSURE_BMA085_PARAM_MH -7357 /* calibration parameter */ > +#define PRESSURE_BMA085_PARAM_MI 3791 /* calibration parameter */ > + > +/********************************************* > + * Pressure Initialization Functions > + *********************************************/ > + > +static int bma085_suspend(void *mlsl_handle, > + struct ext_slave_descr *slave, > + struct ext_slave_platform_data *pdata) > +{ > + int result = INV_SUCCESS; > + return result; > +} > + > +#define PRESSURE_BMA085_PROM_START_ADDR (0xAA) > +#define PRESSURE_BMA085_PROM_DATA_LEN (22) > +#define PRESSURE_BMP085_CTRL_MEAS_REG (0xF4) > +/* temperature measurent */ > +#define PRESSURE_BMP085_T_MEAS (0x2E) > +/* pressure measurement; oversampling_setting */ > +#define PRESSURE_BMP085_P_MEAS_OSS_0 (0x34) > +#define PRESSURE_BMP085_P_MEAS_OSS_1 (0x74) > +#define PRESSURE_BMP085_P_MEAS_OSS_2 (0xB4) > +#define PRESSURE_BMP085_P_MEAS_OSS_3 (0xF4) > +#define PRESSURE_BMP085_ADC_OUT_MSB_REG (0xF6) > +#define PRESSURE_BMP085_ADC_OUT_LSB_REG (0xF7) > + > +static int bma085_resume(void *mlsl_handle, > + struct ext_slave_descr *slave, > + struct ext_slave_platform_data *pdata) > +{ > + int result; > + unsigned char data[PRESSURE_BMA085_PROM_DATA_LEN]; > + > + result = > + inv_serial_read(mlsl_handle, pdata->address, > + PRESSURE_BMA085_PROM_START_ADDR, > + PRESSURE_BMA085_PROM_DATA_LEN, data); > + if (result) { > + LOG_RESULT_LOCATION(result); > + return result; > + } > + > + /* parameters AC1-AC6 */ > + cal_param.ac1 = (data[0] << 8) | data[1]; > + cal_param.ac2 = (data[2] << 8) | data[3]; > + cal_param.ac3 = (data[4] << 8) | data[5]; > + cal_param.ac4 = (data[6] << 8) | data[7]; > + cal_param.ac5 = (data[8] << 8) | data[9]; > + cal_param.ac6 = (data[10] << 8) | data[11]; > + > + /* parameters B1,B2 */ > + cal_param.b1 = (data[12] << 8) | data[13]; > + cal_param.b2 = (data[14] << 8) | data[15]; > + > + /* parameters MB,MC,MD */ > + cal_param.mb = (data[16] << 8) | data[17]; > + cal_param.mc = (data[18] << 8) | data[19]; > + cal_param.md = (data[20] << 8) | data[21]; > + > + return result; > +} > + > +static int bma085_read(void *mlsl_handle, > + struct ext_slave_descr *slave, > + struct ext_slave_platform_data *pdata, > + unsigned char *data) > +{ > + int result; > + long pressure, x1, x2, x3, b3, b6; > + unsigned long b4, b7; > + unsigned long up; > + unsigned short ut; > + short oversampling_setting = 0; > + short temperature; > + long divisor; > + > + /* get temprature */ > + result = inv_serial_single_write(mlsl_handle, pdata->address, > + PRESSURE_BMP085_CTRL_MEAS_REG, > + PRESSURE_BMP085_T_MEAS); > + msleep(5); > + result = > + inv_serial_read(mlsl_handle, pdata->address, > + PRESSURE_BMP085_ADC_OUT_MSB_REG, 2, > + (unsigned char *)data); > + if (result) { > + LOG_RESULT_LOCATION(result); > + return result; > + } > + ut = (data[0] << 8) | data[1]; > + > + x1 = (((long) ut - (long)cal_param.ac6) * (long)cal_param.ac5) >> 15; > + divisor = x1 + cal_param.md; > + if (!divisor) > + return INV_ERROR_DIVIDE_BY_ZERO; > + > + x2 = ((long)cal_param.mc << 11) / (x1 + cal_param.md); > + cal_param.param_b5 = x1 + x2; > + /* temperature in 0.1 degree C */ > + temperature = (short)((cal_param.param_b5 + 8) >> 4); > + > + /* get pressure */ > + result = inv_serial_single_write(mlsl_handle, pdata->address, > + PRESSURE_BMP085_CTRL_MEAS_REG, > + PRESSURE_BMP085_P_MEAS_OSS_0); > + msleep(5); > + result = > + inv_serial_read(mlsl_handle, pdata->address, > + PRESSURE_BMP085_ADC_OUT_MSB_REG, 2, > + (unsigned char *)data); > + if (result) { > + LOG_RESULT_LOCATION(result); > + return result; > + } > + up = (((unsigned long) data[0] << 8) | ((unsigned long) data[1])); > + > + b6 = cal_param.param_b5 - 4000; > + /* calculate B3 */ > + x1 = (b6*b6) >> 12; > + x1 *= cal_param.b2; > + x1 >>= 11; > + > + x2 = (cal_param.ac2*b6); > + x2 >>= 11; > + > + x3 = x1 + x2; > + > + b3 = (((((long)cal_param.ac1) * 4 + x3) > + << oversampling_setting) + 2) >> 2; > + > + /* calculate B4 */ > + x1 = (cal_param.ac3 * b6) >> 13; > + x2 = (cal_param.b1 * ((b6*b6) >> 12)) >> 16; > + x3 = ((x1 + x2) + 2) >> 2; > + b4 = (cal_param.ac4 * (unsigned long) (x3 + 32768)) >> 15; > + if (!b4) > + return INV_ERROR; > + > + b7 = ((unsigned long)(up - b3) * (50000>>oversampling_setting)); > + if (b7 < 0x80000000) > + pressure = (b7 << 1) / b4; > + else > + pressure = (b7 / b4) << 1; > + > + x1 = pressure >> 8; > + x1 *= x1; > + x1 = (x1 * PRESSURE_BMA085_PARAM_MG) >> 16; > + x2 = (pressure * PRESSURE_BMA085_PARAM_MH) >> 16; > + /* pressure in Pa */ > + pressure += (x1 + x2 + PRESSURE_BMA085_PARAM_MI) >> 4; > + > + data[0] = (unsigned char)(pressure >> 16); > + data[1] = (unsigned char)(pressure >> 8); > + data[2] = (unsigned char)(pressure & 0xFF); > + > + return result; > +} > + > +static struct ext_slave_descr bma085_descr = { > + .init = NULL, > + .exit = NULL, > + .suspend = bma085_suspend, > + .resume = bma085_resume, > + .read = bma085_read, > + .config = NULL, > + .get_config = NULL, > + .name = "bma085", > + .type = EXT_SLAVE_TYPE_PRESSURE, > + .id = PRESSURE_ID_BMA085, > + .read_reg = 0xF6, > + .read_len = 3, > + .endian = EXT_SLAVE_BIG_ENDIAN, > + .range = {0, 0}, > +}; > + > +static > +struct ext_slave_descr *bma085_get_slave_descr(void) > +{ > + return &bma085_descr; > +} > + > +/* Platform data for the MPU */ > +struct bma085_mod_private_data { > + struct i2c_client *client; > + struct ext_slave_platform_data *pdata; > +}; > + > +static unsigned short normal_i2c[] = { I2C_CLIENT_END }; > + > +static int bma085_mod_probe(struct i2c_client *client, > + const struct i2c_device_id *devid) > +{ > + struct ext_slave_platform_data *pdata; > + struct bma085_mod_private_data *private_data; > + int result = 0; > + > + dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); > + > + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { > + result = -ENODEV; > + goto out_no_free; > + } > + > + pdata = client->dev.platform_data; > + if (!pdata) { > + dev_err(&client->adapter->dev, > + "Missing platform data for slave %s\n", devid->name); > + result = -EFAULT; > + goto out_no_free; > + } > + > + private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); > + if (!private_data) { > + result = -ENOMEM; > + goto out_no_free; > + } > + > + i2c_set_clientdata(client, private_data); > + private_data->client = client; > + private_data->pdata = pdata; > + > + result = inv_mpu_register_slave(THIS_MODULE, client, pdata, > + bma085_get_slave_descr); > + if (result) { > + dev_err(&client->adapter->dev, > + "Slave registration failed: %s, %d\n", > + devid->name, result); > + goto out_free_memory; > + } > + > + return result; > + > +out_free_memory: > + kfree(private_data); > +out_no_free: > + dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); > + return result; > + > +} > + > +static int bma085_mod_remove(struct i2c_client *client) > +{ > + struct bma085_mod_private_data *private_data = > + i2c_get_clientdata(client); > + > + dev_dbg(&client->adapter->dev, "%s\n", __func__); > + > + inv_mpu_unregister_slave(client, private_data->pdata, > + bma085_get_slave_descr); > + > + kfree(private_data); > + return 0; > +} > + > +static const struct i2c_device_id bma085_mod_id[] = { > + { "bma085", PRESSURE_ID_BMA085 }, > + {} > +}; > + > +MODULE_DEVICE_TABLE(i2c, bma085_mod_id); > + > +static struct i2c_driver bma085_mod_driver = { > + .class = I2C_CLASS_HWMON, > + .probe = bma085_mod_probe, > + .remove = bma085_mod_remove, > + .id_table = bma085_mod_id, > + .driver = { > + .owner = THIS_MODULE, > + .name = "bma085_mod", > + }, > + .address_list = normal_i2c, > +}; > + > +static int __init bma085_mod_init(void) > +{ > + int res = i2c_add_driver(&bma085_mod_driver); > + pr_info("%s: Probe name %s\n", __func__, "bma085_mod"); > + if (res) > + pr_err("%s failed\n", __func__); > + return res; > +} > + > +static void __exit bma085_mod_exit(void) > +{ > + pr_info("%s\n", __func__); > + i2c_del_driver(&bma085_mod_driver); > +} > + > +module_init(bma085_mod_init); > +module_exit(bma085_mod_exit); > + > +MODULE_AUTHOR("Invensense Corporation"); > +MODULE_DESCRIPTION("Driver to integrate BMA085 sensor with the MPU"); > +MODULE_LICENSE("GPL"); > +MODULE_ALIAS("bma085_mod"); > +/** > + * @} > +**/ -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On 01.07.2011 11:05, Jonathan Cameron wrote: > Hang on, so your motion processing unit takes in a pressure sensor? > Interesting.... > > Manuel, am I imagining things or did you have a driver for one of these? Yes, indeed we have one for bmp085. I have to update it for the latest API, but these are things I'm going to do during the next 2 month. Regards,
On Fri, Jul 1, 2011 at 12:56 AM, Alan Cox <alan@linux.intel.com> wrote: > The slave devices all appear to be generic i2c interface hardware. I > don't see why they are separate special slave devices to your driver, > they should simply be i²c drivers so they can be used when those > devices are found without the mpu3050. Just to field this quickly, since I've been playing with the hardware involved. There are actually two different things handled by the "slave devices". The MPU-3050 (and some similar InvenSense parts) provide a secondary I2C bus. By default this is passed through to the I2C bus on which the 3050 is installed, but the MPU can also detach the extra bus and act as an I2C master on it. A slave device is connected on this extra bus, configured normally by the kernel, and then taken over by the MPU (which will periodically do a block read of a configured address). The second case of a slave device is simply some other device that the driver reads from, and passes the data to the MPU for processing. Part of the reason I got started on the KXTF9 driver was to try and understand how a normal kernel driver could cope with the "here and gone again" behavior of being on the MPU's secondary bus. The other note that may not have been made explicit yet is that the 3050 will happily provide unprocessed gyro and slave data without firmware loaded. The firmware sets up some additional processing capabilities of the chip, which are used by InvenSense's user-space products. Chris [Sorry about gmail. I'm on the road and hope it doesn't mangle things too badly.] -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
> Just to field this quickly, since I've been playing with the hardware (Ditto) > Part of the reason I got started on the KXTF9 driver was to try and > understand how a normal kernel driver could cope with the "here and > gone again" behavior of being on the MPU's secondary bus. It's a hot pluggable bus - no different to an i2c bus on a hot pluggable device. I would guess however that if you knew the device was going to be used for the mpu3050 only you'd not add the "slave" devices in your platform data in the first place as well so they didn't bounce in and out and you'd probably teach the mpu3050 code to not create the bus if it was then going to destroy it again a moment later. > The other note that may not have been made explicit yet is that the > 3050 will happily provide unprocessed gyro and slave data without > firmware loaded. The firmware sets up some additional processing > capabilities of the chip, which are used by InvenSense's user-space > products. Yes - and the current mpu3050 driver provides just these interfaces. Of the other stuff the slave drivers seem to be no problem as standard i²c bus devices, the MPU mode may depend upon whether there is open documentation, applying the standards we apply to everything else. That is "can someone write their own tools to use that interface based upon the documentation/source code examples available". Eg for the ektf driver which is pending the interface is all documented and provides rectangles of pressure data but you'll have to go write your own gesture recognizer parts if you want to do clever stuff with it. Alan -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Fri, Jul 1, 2011 at 7:41 AM, Alan Cox <alan@lxorguk.ukuu.org.uk> wrote: >> Part of the reason I got started on the KXTF9 driver was to try and >> understand how a normal kernel driver could cope with the "here and >> gone again" behavior of being on the MPU's secondary bus. > > It's a hot pluggable bus - no different to an i2c bus on a hot pluggable > device. Can such a hot pluggable device maintain state while "unplugged"? e.g. if the system is going to suspend, we need to pause the MPU, switch it back to pass-through mode, then send commands to suspend the slave on that bus. I took that to mean we wanted the secondary bus to have a normal device on it, which is still considered plugged in, but temporarily inaccessible from the CPU. > I would guess however that if you knew the device was going to be used for > the mpu3050 only you'd not add the "slave" devices in your platform data > in the first place as well so they didn't bounce in and out and you'd > probably teach the mpu3050 code to not create the bus if it was then > going to destroy it again a moment later. Something needs to initialize and configure the slaved devices (and suspend/resume/shutdown/etc). I'm not sure where this happens if not A) special-purpose code in the mpu3050 driver for each possible slave; or B) normal drivers and platform data which cope with B1) the kernel relaying their data to the MPU and B2) being disconnected from the CPU while the MPU reads from the device directly. >> The other note that may not have been made explicit yet is that the >> 3050 will happily provide unprocessed gyro and slave data without >> firmware loaded. The firmware sets up some additional processing >> capabilities of the chip, which are used by InvenSense's user-space >> products. > > Yes - and the current mpu3050 driver provides just these interfaces. Ah, great. Sorry for missing them. In scanning the ioctls, it looked like dev-i2c was still a similar level of abstraction :) Chris -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Fri, 1 Jul 2011 15:41:50 +0100, Alan Cox wrote: > > Just to field this quickly, since I've been playing with the hardware > > (Ditto) > > > Part of the reason I got started on the KXTF9 driver was to try and > > understand how a normal kernel driver could cope with the "here and > > gone again" behavior of being on the MPU's secondary bus. > > It's a hot pluggable bus - no different to an i2c bus on a hot pluggable > device. > > I would guess however that if you knew the device was going to be used for > the mpu3050 only you'd not add the "slave" devices in your platform data > in the first place as well so they didn't bounce in and out and you'd > probably teach the mpu3050 code to not create the bus if it was then > going to destroy it again a moment later. I didn't look in the code in details, but I see no reason to destroy buses. This is a standard multi-master case, where a given I2C bus segment can be driven by two masters. If both the host I2C controller and the MPU3050 master are fully I2C compliant, it should be no problem to let both coexist. If you really need to handle mutual exclusion (e.g. the MPU3050 implementation forces you to do so), you can leverage the i2c multiplexing support for this, which is available since kernel 2.6.36. See drivers/i2c/muxes/pca9541.c for an example implementation.
> > It's a hot pluggable bus - no different to an i2c bus on a hot pluggable > > device. > > Can such a hot pluggable device maintain state while "unplugged"? The hardware or the driver ? As far as the OS is concerned it would have gone away. > e.g. if the system is going to suspend, we need to pause the MPU, > switch it back to pass-through mode, then send commands to suspend the > slave on that bus. I took that to mean we wanted the secondary bus to > have a normal device on it, which is still considered plugged in, but > temporarily inaccessible from the CPU. Thats a bit messier but we'd still want them to be normal i²c drivers when on the slave bus and being driven by Linux. Possibly you end up with a little helper to send i²c commands directly to the bus in the "smart" mode. > A) special-purpose code in the mpu3050 driver for each possible slave; > or B) normal drivers and platform data which cope with B1) the kernel > relaying their data to the MPU and B2) being disconnected from the CPU > while the MPU reads from the device directly. If it's operating as a normal i²c device then it'll behave like any normal kernel device, and really ought to be using runtime pm anyway. In "smart" mode its really up to the driver how it does it but two versions of every device is not a good answer ! > > >> The other note that may not have been made explicit yet is that the > >> 3050 will happily provide unprocessed gyro and slave data without > >> firmware loaded. The firmware sets up some additional processing > >> capabilities of the chip, which are used by InvenSense's user-space > >> products. > > > > Yes - and the current mpu3050 driver provides just these interfaces. > > Ah, great. Sorry for missing them. In scanning the ioctls, it looked > like dev-i2c was still a similar level of abstraction :) Sorry may not have been clear "just those" as in "just those which can be used without loading firmware and running non-free stuff" And really until that area is sorted it may be premature to worry about the rest - if its going to need proprietary bits to use the smart mode, then the kernel just needs an i2c slave bus implementation, a few drivers (we have most listed already it seems), and we are done. Alan -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
I've added an explanation of some of the high level bypass mode work which is done in mldl_cfg.c in response that patch [PATCH 05/11] misc: MPU3050 and slave device configuration. -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/drivers/misc/inv_mpu/Kconfig b/drivers/misc/inv_mpu/Kconfig index 7d9f2c3..ea7d754 100644 --- a/drivers/misc/inv_mpu/Kconfig +++ b/drivers/misc/inv_mpu/Kconfig @@ -57,5 +57,6 @@ endchoice source "drivers/misc/inv_mpu/accel/Kconfig" source "drivers/misc/inv_mpu/compass/Kconfig" +source "drivers/misc/inv_mpu/pressure/Kconfig" endif #INV_SENSORS diff --git a/drivers/misc/inv_mpu/Makefile b/drivers/misc/inv_mpu/Makefile index dc77a1b..506b8f5 100644 --- a/drivers/misc/inv_mpu/Makefile +++ b/drivers/misc/inv_mpu/Makefile @@ -18,3 +18,4 @@ EXTRA_CFLAGS += -Idrivers/misc/inv_mpu obj-y += accel/ obj-y += compass/ +obj-y += pressure/ diff --git a/drivers/misc/inv_mpu/pressure/Kconfig b/drivers/misc/inv_mpu/pressure/Kconfig new file mode 100644 index 0000000..f1c021e --- /dev/null +++ b/drivers/misc/inv_mpu/pressure/Kconfig @@ -0,0 +1,20 @@ +menuconfig: INV_SENSORS_PRESSURE + bool "Pressure Sensor Slaves" + depends on INV_SENSORS + default y + help + Select y to see a list of supported pressure sensors that can be + integrated with the MPUxxxx set of motion processors. + +if INV_SENSORS_PRESSURE + +config MPU_SENSORS_BMA085 + tristate "Bosch BMA085" + help + This enables support for the Bosch bma085 pressure sensor + This support is for integration with the MPU3050 or MPU6050 gyroscope + device driver. Only one accelerometer can be registered at a time. + Specifying more that one accelerometer in the board file will result + in runtime errors. + +endif diff --git a/drivers/misc/inv_mpu/pressure/Makefile b/drivers/misc/inv_mpu/pressure/Makefile new file mode 100644 index 0000000..595923d --- /dev/null +++ b/drivers/misc/inv_mpu/pressure/Makefile @@ -0,0 +1,8 @@ +# +# Pressure Slaves to MPUxxxx +# +obj-$(CONFIG_MPU_SENSORS_BMA085) += inv_mpu_bma085.o +inv_mpu_bma085-objs += bma085.o + +EXTRA_CFLAGS += -Idrivers/misc/inv_mpu +EXTRA_CFLAGS += -D__C99_DESIGNATED_INITIALIZER diff --git a/drivers/misc/inv_mpu/pressure/bma085.c b/drivers/misc/inv_mpu/pressure/bma085.c new file mode 100644 index 0000000..696d2b6 --- /dev/null +++ b/drivers/misc/inv_mpu/pressure/bma085.c @@ -0,0 +1,367 @@ +/* + $License: + Copyright (C) 2011 InvenSense Corporation, All Rights Reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + $ + */ + +/** + * @defgroup ACCELDL (Motion Library - Pressure Driver Layer) + * @brief Provides the interface to setup and handle a pressure + * connected to the secondary I2C interface of the gyroscope. + * + * @{ + * @file bma085.c + * @brief Pressure setup and handling methods. + */ + +/* ------------------ */ +/* - Include Files. - */ +/* ------------------ */ + +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include "mpu-dev.h" + +#include <linux/mpu.h> +#include "mlsl.h" +#include "log.h" + +/* + * this structure holds all device specific calibration parameters + */ +struct bmp085_calibration_param_t { + short ac1; + short ac2; + short ac3; + unsigned short ac4; + unsigned short ac5; + unsigned short ac6; + short b1; + short b2; + short mb; + short mc; + short md; + long param_b5; +}; + +struct bmp085_calibration_param_t cal_param; + +#define PRESSURE_BMA085_PARAM_MG 3038 /* calibration parameter */ +#define PRESSURE_BMA085_PARAM_MH -7357 /* calibration parameter */ +#define PRESSURE_BMA085_PARAM_MI 3791 /* calibration parameter */ + +/********************************************* + * Pressure Initialization Functions + *********************************************/ + +static int bma085_suspend(void *mlsl_handle, + struct ext_slave_descr *slave, + struct ext_slave_platform_data *pdata) +{ + int result = INV_SUCCESS; + return result; +} + +#define PRESSURE_BMA085_PROM_START_ADDR (0xAA) +#define PRESSURE_BMA085_PROM_DATA_LEN (22) +#define PRESSURE_BMP085_CTRL_MEAS_REG (0xF4) +/* temperature measurent */ +#define PRESSURE_BMP085_T_MEAS (0x2E) +/* pressure measurement; oversampling_setting */ +#define PRESSURE_BMP085_P_MEAS_OSS_0 (0x34) +#define PRESSURE_BMP085_P_MEAS_OSS_1 (0x74) +#define PRESSURE_BMP085_P_MEAS_OSS_2 (0xB4) +#define PRESSURE_BMP085_P_MEAS_OSS_3 (0xF4) +#define PRESSURE_BMP085_ADC_OUT_MSB_REG (0xF6) +#define PRESSURE_BMP085_ADC_OUT_LSB_REG (0xF7) + +static int bma085_resume(void *mlsl_handle, + struct ext_slave_descr *slave, + struct ext_slave_platform_data *pdata) +{ + int result; + unsigned char data[PRESSURE_BMA085_PROM_DATA_LEN]; + + result = + inv_serial_read(mlsl_handle, pdata->address, + PRESSURE_BMA085_PROM_START_ADDR, + PRESSURE_BMA085_PROM_DATA_LEN, data); + if (result) { + LOG_RESULT_LOCATION(result); + return result; + } + + /* parameters AC1-AC6 */ + cal_param.ac1 = (data[0] << 8) | data[1]; + cal_param.ac2 = (data[2] << 8) | data[3]; + cal_param.ac3 = (data[4] << 8) | data[5]; + cal_param.ac4 = (data[6] << 8) | data[7]; + cal_param.ac5 = (data[8] << 8) | data[9]; + cal_param.ac6 = (data[10] << 8) | data[11]; + + /* parameters B1,B2 */ + cal_param.b1 = (data[12] << 8) | data[13]; + cal_param.b2 = (data[14] << 8) | data[15]; + + /* parameters MB,MC,MD */ + cal_param.mb = (data[16] << 8) | data[17]; + cal_param.mc = (data[18] << 8) | data[19]; + cal_param.md = (data[20] << 8) | data[21]; + + return result; +} + +static int bma085_read(void *mlsl_handle, + struct ext_slave_descr *slave, + struct ext_slave_platform_data *pdata, + unsigned char *data) +{ + int result; + long pressure, x1, x2, x3, b3, b6; + unsigned long b4, b7; + unsigned long up; + unsigned short ut; + short oversampling_setting = 0; + short temperature; + long divisor; + + /* get temprature */ + result = inv_serial_single_write(mlsl_handle, pdata->address, + PRESSURE_BMP085_CTRL_MEAS_REG, + PRESSURE_BMP085_T_MEAS); + msleep(5); + result = + inv_serial_read(mlsl_handle, pdata->address, + PRESSURE_BMP085_ADC_OUT_MSB_REG, 2, + (unsigned char *)data); + if (result) { + LOG_RESULT_LOCATION(result); + return result; + } + ut = (data[0] << 8) | data[1]; + + x1 = (((long) ut - (long)cal_param.ac6) * (long)cal_param.ac5) >> 15; + divisor = x1 + cal_param.md; + if (!divisor) + return INV_ERROR_DIVIDE_BY_ZERO; + + x2 = ((long)cal_param.mc << 11) / (x1 + cal_param.md); + cal_param.param_b5 = x1 + x2; + /* temperature in 0.1 degree C */ + temperature = (short)((cal_param.param_b5 + 8) >> 4); + + /* get pressure */ + result = inv_serial_single_write(mlsl_handle, pdata->address, + PRESSURE_BMP085_CTRL_MEAS_REG, + PRESSURE_BMP085_P_MEAS_OSS_0); + msleep(5); + result = + inv_serial_read(mlsl_handle, pdata->address, + PRESSURE_BMP085_ADC_OUT_MSB_REG, 2, + (unsigned char *)data); + if (result) { + LOG_RESULT_LOCATION(result); + return result; + } + up = (((unsigned long) data[0] << 8) | ((unsigned long) data[1])); + + b6 = cal_param.param_b5 - 4000; + /* calculate B3 */ + x1 = (b6*b6) >> 12; + x1 *= cal_param.b2; + x1 >>= 11; + + x2 = (cal_param.ac2*b6); + x2 >>= 11; + + x3 = x1 + x2; + + b3 = (((((long)cal_param.ac1) * 4 + x3) + << oversampling_setting) + 2) >> 2; + + /* calculate B4 */ + x1 = (cal_param.ac3 * b6) >> 13; + x2 = (cal_param.b1 * ((b6*b6) >> 12)) >> 16; + x3 = ((x1 + x2) + 2) >> 2; + b4 = (cal_param.ac4 * (unsigned long) (x3 + 32768)) >> 15; + if (!b4) + return INV_ERROR; + + b7 = ((unsigned long)(up - b3) * (50000>>oversampling_setting)); + if (b7 < 0x80000000) + pressure = (b7 << 1) / b4; + else + pressure = (b7 / b4) << 1; + + x1 = pressure >> 8; + x1 *= x1; + x1 = (x1 * PRESSURE_BMA085_PARAM_MG) >> 16; + x2 = (pressure * PRESSURE_BMA085_PARAM_MH) >> 16; + /* pressure in Pa */ + pressure += (x1 + x2 + PRESSURE_BMA085_PARAM_MI) >> 4; + + data[0] = (unsigned char)(pressure >> 16); + data[1] = (unsigned char)(pressure >> 8); + data[2] = (unsigned char)(pressure & 0xFF); + + return result; +} + +static struct ext_slave_descr bma085_descr = { + .init = NULL, + .exit = NULL, + .suspend = bma085_suspend, + .resume = bma085_resume, + .read = bma085_read, + .config = NULL, + .get_config = NULL, + .name = "bma085", + .type = EXT_SLAVE_TYPE_PRESSURE, + .id = PRESSURE_ID_BMA085, + .read_reg = 0xF6, + .read_len = 3, + .endian = EXT_SLAVE_BIG_ENDIAN, + .range = {0, 0}, +}; + +static +struct ext_slave_descr *bma085_get_slave_descr(void) +{ + return &bma085_descr; +} + +/* Platform data for the MPU */ +struct bma085_mod_private_data { + struct i2c_client *client; + struct ext_slave_platform_data *pdata; +}; + +static unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +static int bma085_mod_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + struct ext_slave_platform_data *pdata; + struct bma085_mod_private_data *private_data; + int result = 0; + + dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + result = -ENODEV; + goto out_no_free; + } + + pdata = client->dev.platform_data; + if (!pdata) { + dev_err(&client->adapter->dev, + "Missing platform data for slave %s\n", devid->name); + result = -EFAULT; + goto out_no_free; + } + + private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); + if (!private_data) { + result = -ENOMEM; + goto out_no_free; + } + + i2c_set_clientdata(client, private_data); + private_data->client = client; + private_data->pdata = pdata; + + result = inv_mpu_register_slave(THIS_MODULE, client, pdata, + bma085_get_slave_descr); + if (result) { + dev_err(&client->adapter->dev, + "Slave registration failed: %s, %d\n", + devid->name, result); + goto out_free_memory; + } + + return result; + +out_free_memory: + kfree(private_data); +out_no_free: + dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); + return result; + +} + +static int bma085_mod_remove(struct i2c_client *client) +{ + struct bma085_mod_private_data *private_data = + i2c_get_clientdata(client); + + dev_dbg(&client->adapter->dev, "%s\n", __func__); + + inv_mpu_unregister_slave(client, private_data->pdata, + bma085_get_slave_descr); + + kfree(private_data); + return 0; +} + +static const struct i2c_device_id bma085_mod_id[] = { + { "bma085", PRESSURE_ID_BMA085 }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, bma085_mod_id); + +static struct i2c_driver bma085_mod_driver = { + .class = I2C_CLASS_HWMON, + .probe = bma085_mod_probe, + .remove = bma085_mod_remove, + .id_table = bma085_mod_id, + .driver = { + .owner = THIS_MODULE, + .name = "bma085_mod", + }, + .address_list = normal_i2c, +}; + +static int __init bma085_mod_init(void) +{ + int res = i2c_add_driver(&bma085_mod_driver); + pr_info("%s: Probe name %s\n", __func__, "bma085_mod"); + if (res) + pr_err("%s failed\n", __func__); + return res; +} + +static void __exit bma085_mod_exit(void) +{ + pr_info("%s\n", __func__); + i2c_del_driver(&bma085_mod_driver); +} + +module_init(bma085_mod_init); +module_exit(bma085_mod_exit); + +MODULE_AUTHOR("Invensense Corporation"); +MODULE_DESCRIPTION("Driver to integrate BMA085 sensor with the MPU"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("bma085_mod"); +/** + * @} +**/
Signed-off-by: Nathan Royer <nroyer@invensense.com> --- drivers/misc/inv_mpu/Kconfig | 1 + drivers/misc/inv_mpu/Makefile | 1 + drivers/misc/inv_mpu/pressure/Kconfig | 20 ++ drivers/misc/inv_mpu/pressure/Makefile | 8 + drivers/misc/inv_mpu/pressure/bma085.c | 367 ++++++++++++++++++++++++++++++++ 5 files changed, 397 insertions(+), 0 deletions(-) create mode 100644 drivers/misc/inv_mpu/pressure/Kconfig create mode 100644 drivers/misc/inv_mpu/pressure/Makefile create mode 100644 drivers/misc/inv_mpu/pressure/bma085.c