diff options
| -rw-r--r-- | arch/arm/boot/dts/omap3-minnow.dtsi | 2 | ||||
| -rw-r--r-- | arch/arm/configs/minnow_defconfig | 197 | ||||
| -rw-r--r-- | drivers/misc/Makefile | 2 | ||||
| -rw-r--r-- | drivers/misc/m4sensorhub_bmp180.c | 423 | ||||
| -rw-r--r-- | drivers/misc/m4sensorhub_pressure.c | 420 | ||||
| -rw-r--r-- | include/linux/iio/m4sensorhub_pressure.h | 12 |
6 files changed, 630 insertions, 426 deletions
diff --git a/arch/arm/boot/dts/omap3-minnow.dtsi b/arch/arm/boot/dts/omap3-minnow.dtsi index a747680c1a5..879cb58f26e 100644 --- a/arch/arm/boot/dts/omap3-minnow.dtsi +++ b/arch/arm/boot/dts/omap3-minnow.dtsi @@ -117,7 +117,7 @@ compatible = "simple-bus"; pressure { - compatible = "mot,m4pressure"; + compatible = "mot,m4sensorhub_pressure"; }; mpu9150 { compatible = "mot,m4mpu9150"; diff --git a/arch/arm/configs/minnow_defconfig b/arch/arm/configs/minnow_defconfig index 396909a7dc6..761ec0d8f04 100644 --- a/arch/arm/configs/minnow_defconfig +++ b/arch/arm/configs/minnow_defconfig @@ -1435,6 +1435,7 @@ CONFIG_HDQ_MASTER_OMAP=y CONFIG_POWER_SUPPLY=y # CONFIG_POWER_SUPPLY_DEBUG is not set # CONFIG_PDA_POWER is not set +# CONFIG_GENERIC_ADC_BATTERY is not set # CONFIG_TEST_POWER is not set # CONFIG_BATTERY_DS2780 is not set # CONFIG_BATTERY_DS2781 is not set @@ -2134,6 +2135,105 @@ CONFIG_STAGING=y # CONFIG_TRANZPORT is not set # CONFIG_LINE6_USB is not set # CONFIG_VT6656 is not set + +# +# IIO staging drivers +# + +# +# Accelerometers +# +# CONFIG_ADIS16201 is not set +# CONFIG_ADIS16203 is not set +# CONFIG_ADIS16204 is not set +# CONFIG_ADIS16209 is not set +# CONFIG_ADIS16220 is not set +# CONFIG_ADIS16240 is not set +# CONFIG_LIS3L02DQ is not set +# CONFIG_SCA3000 is not set + +# +# Analog to digital converters +# +# CONFIG_AD7291 is not set +# CONFIG_AD7606 is not set +# CONFIG_AD799X is not set +# CONFIG_AD7780 is not set +# CONFIG_AD7816 is not set +# CONFIG_AD7192 is not set +# CONFIG_AD7280 is not set + +# +# Analog digital bi-direction converters +# +# CONFIG_ADT7316 is not set + +# +# Capacitance to digital converters +# +# CONFIG_AD7150 is not set +# CONFIG_AD7152 is not set +# CONFIG_AD7746 is not set + +# +# Direct Digital Synthesis +# +# CONFIG_AD5930 is not set +# CONFIG_AD9832 is not set +# CONFIG_AD9834 is not set +# CONFIG_AD9850 is not set +# CONFIG_AD9852 is not set +# CONFIG_AD9910 is not set +# CONFIG_AD9951 is not set + +# +# Digital gyroscope sensors +# +# CONFIG_ADIS16060 is not set +# CONFIG_ADIS16130 is not set +# CONFIG_ADIS16260 is not set + +# +# Network Analyzer, Impedance Converters +# +# CONFIG_AD5933 is not set + +# +# Light sensors +# +# CONFIG_SENSORS_ISL29018 is not set +# CONFIG_SENSORS_ISL29028 is not set +# CONFIG_TSL2583 is not set +# CONFIG_TSL2x7x is not set + +# +# Magnetometer sensors +# +# CONFIG_SENSORS_HMC5843 is not set + +# +# Active energy metering IC +# +# CONFIG_ADE7753 is not set +# CONFIG_ADE7754 is not set +# CONFIG_ADE7758 is not set +# CONFIG_ADE7759 is not set +# CONFIG_ADE7854 is not set + +# +# Resolver to digital converters +# +# CONFIG_AD2S90 is not set +# CONFIG_AD2S1200 is not set +# CONFIG_AD2S1210 is not set + +# +# Triggers - standalone +# +# CONFIG_IIO_PERIODIC_RTC_TRIGGER is not set +# CONFIG_IIO_GPIO_TRIGGER is not set +# CONFIG_IIO_SYSFS_TRIGGER is not set +# CONFIG_IIO_SIMPLE_DUMMY is not set # CONFIG_ZSMALLOC is not set # CONFIG_BCM_WIMAX is not set # CONFIG_FT1000 is not set @@ -2194,7 +2294,102 @@ CONFIG_OF_IOMMU=y # CONFIG_PM_DEVFREQ is not set # CONFIG_EXTCON is not set # CONFIG_MEMORY is not set -# CONFIG_IIO is not set +CONFIG_IIO=y +CONFIG_IIO_BUFFER=y +# CONFIG_IIO_BUFFER_CB is not set +CONFIG_IIO_KFIFO_BUF=y +CONFIG_IIO_TRIGGER=y +CONFIG_IIO_CONSUMERS_PER_TRIGGER=2 + +# +# Accelerometers +# +# CONFIG_KXSD9 is not set +# CONFIG_IIO_ST_ACCEL_3AXIS is not set + +# +# Analog to digital converters +# +# CONFIG_AD7266 is not set +# CONFIG_AD7298 is not set +# CONFIG_AD7923 is not set +# CONFIG_AD7791 is not set +# CONFIG_AD7793 is not set +# CONFIG_AD7476 is not set +# CONFIG_AD7887 is not set +# CONFIG_EXYNOS_ADC is not set +# CONFIG_MAX1363 is not set +# CONFIG_TI_ADC081C is not set + +# +# Amplifiers +# +# CONFIG_AD8366 is not set + +# +# Hid Sensor IIO Common +# + +# +# Digital to analog converters +# +# CONFIG_AD5064 is not set +# CONFIG_AD5360 is not set +# CONFIG_AD5380 is not set +# CONFIG_AD5421 is not set +# CONFIG_AD5624R_SPI is not set +# CONFIG_AD5446 is not set +# CONFIG_AD5449 is not set +# CONFIG_AD5504 is not set +# CONFIG_AD5755 is not set +# CONFIG_AD5764 is not set +# CONFIG_AD5791 is not set +# CONFIG_AD5686 is not set +# CONFIG_MAX517 is not set +# CONFIG_MCP4725 is not set + +# +# Frequency Synthesizers DDS/PLL +# + +# +# Clock Generator/Distribution +# +# CONFIG_AD9523 is not set + +# +# Phase-Locked Loop (PLL) frequency synthesizers +# +# CONFIG_ADF4350 is not set + +# +# Digital gyroscope sensors +# +# CONFIG_ADIS16080 is not set +# CONFIG_ADIS16136 is not set +# CONFIG_ADXRS450 is not set +# CONFIG_IIO_ST_GYRO_3AXIS is not set +# CONFIG_ITG3200 is not set + +# +# Inertial measurement units +# +# CONFIG_ADIS16400 is not set +# CONFIG_ADIS16480 is not set +# CONFIG_INV_MPU6050_IIO is not set + +# +# Light sensors +# +# CONFIG_ADJD_S311 is not set +# CONFIG_SENSORS_TSL2563 is not set +# CONFIG_VCNL4000 is not set + +# +# Magnetometer sensors +# +# CONFIG_AK8975 is not set +# CONFIG_IIO_ST_MAGN_3AXIS is not set # CONFIG_PWM is not set CONFIG_IRQCHIP=y # CONFIG_IPACK_BUS is not set diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index a354115b155..95233b463a5 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -54,7 +54,6 @@ obj-$(CONFIG_INTEL_MEI) += mei/ obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o obj-$(CONFIG_SRAM) += sram.o -obj-$(CONFIG_MFD_M4SENSORHUB) += m4sensorhub_bmp180.o obj-$(CONFIG_MFD_M4SENSORHUB) += m4sensorhub_mpu9150.o obj-$(CONFIG_MFD_M4SENSORHUB) += m4sensorhub_pedometer.o obj-$(CONFIG_MFD_M4SENSORHUB) += m4sensorhub_gesture.o @@ -64,6 +63,7 @@ obj-$(CONFIG_MFD_M4SENSORHUB) += m4sensorhub_display.o obj-$(CONFIG_MFD_M4SENSORHUB) += m4sensorhub_passive.o obj-$(CONFIG_MFD_M4SENSORHUB) += m4sensorhub_als.o obj-$(CONFIG_MFD_M4SENSORHUB) += m4sensorhub_heartrate.o +obj-$(CONFIG_MFD_M4SENSORHUB) += m4sensorhub_pressure.o obj-$(CONFIG_VIB_GPIO) += vib-gpio.o obj-$(CONFIG_C55_CTRL) += c55_ctrl.o obj-$(CONFIG_MMI_FACTORY) += mmi-factory.o diff --git a/drivers/misc/m4sensorhub_bmp180.c b/drivers/misc/m4sensorhub_bmp180.c deleted file mode 100644 index 02fa1e7c102..00000000000 --- a/drivers/misc/m4sensorhub_bmp180.c +++ /dev/null @@ -1,423 +0,0 @@ -/* - * Copyright (C) 2012 Motorola, Inc. - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Adds ability to program periodic interrupts from user space that - * can wake the phone out of low power modes. - * - */ - -#include <linux/module.h> -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/miscdevice.h> -#include <linux/platform_device.h> -#include <linux/proc_fs.h> -#include <linux/input.h> -#include <linux/uaccess.h> -#include <linux/m4sensorhub.h> -#include <linux/m4sensorhub_client_ioctl.h> -#include <linux/m4sensorhub/MemMapPressureSensor.h> -#include <linux/slab.h> - -#define PRESSURE_CLIENT_DRIVER_NAME "m4sensorhub_bmp180" -#define PRESSURE_MIN 30000 -#define PRESSURE_MAX 110000 - -struct pressure_client { - struct m4sensorhub_data *m4sensorhub; - struct input_dev *input_dev; - int pressure; - int altitude; - signed short samplerate; -}; - -struct pressure_client *misc_pressure_data; - -static int pressure_client_open(struct inode *inode, struct file *file) -{ - int err = 0; - - err = nonseekable_open(inode, file); - if (err < 0) { - KDEBUG(M4SH_ERROR, " %s failed\n", __func__); - return err; - } - file->private_data = misc_pressure_data; - - return 0; -} - -static int pressure_client_close(struct inode *inode, struct file *file) -{ - KDEBUG(M4SH_DEBUG, " pressure_client in %s\n", __func__); - return 0; -} - -static void m4_report_pressure_inputevent(struct pressure_client - *pressure_client_data) -{ - input_report_abs(pressure_client_data->input_dev, ABS_PRESSURE, - pressure_client_data->pressure); - input_report_abs(pressure_client_data->input_dev, ABS_ALTITUDE, - pressure_client_data->altitude); - input_sync(pressure_client_data->input_dev); -} - -static void m4_read_pressure_data(struct pressure_client *pressure_data) -{ - sPressureData pressure; - - m4sensorhub_reg_read(pressure_data->m4sensorhub, - M4SH_REG_PRESSURE_PRESSURE, - (char *)&pressure.pressure); - pressure_data->pressure = pressure.pressure; - m4sensorhub_reg_read(pressure_data->m4sensorhub, - M4SH_REG_PRESSURE_ABSOLUTEALTITUDE, - (char *)&pressure.absoluteAltitude); - pressure_data->altitude = pressure.absoluteAltitude; -} - -static void m4_handle_pressure_irq(enum m4sensorhub_irqs int_event, - void *pressure_data) -{ - struct pressure_client *pressure_client_data = pressure_data; - - m4_read_pressure_data(pressure_client_data); - m4_report_pressure_inputevent(pressure_client_data); -} - -static int m4_set_pressure_samplerate( - struct pressure_client *pressure_client_data, - signed int samplerate) -{ - int ret = 0; - - if (samplerate != pressure_client_data->samplerate) { - ret = m4sensorhub_reg_write(pressure_client_data->m4sensorhub, - M4SH_REG_PRESSURE_SAMPLERATE, - (char *)&samplerate, m4sh_no_mask); - if (ret != m4sensorhub_reg_getsize( - pressure_client_data->m4sensorhub, - M4SH_REG_PRESSURE_SAMPLERATE)) { - KDEBUG(M4SH_ERROR, "Unable to set delay for \ - pressure sensor\n"); - return ret; - } - - KDEBUG(M4SH_DEBUG, "%s() updating samplerate from %d to %d\n", - __func__, pressure_client_data->samplerate, - samplerate); - pressure_client_data->samplerate = samplerate; - - if (samplerate >= 0) - ret = m4sensorhub_irq_enable( - pressure_client_data->m4sensorhub, - M4SH_IRQ_PRESSURE_DATA_READY); - else - ret = m4sensorhub_irq_disable( - pressure_client_data->m4sensorhub, - M4SH_IRQ_PRESSURE_DATA_READY); - if (ret != 0) - KDEBUG(M4SH_ERROR, "Unable to enable/disable \ - pressure irq\n"); - } - - return ret; -} - -/* - * Handle commands from user-space. - */ -static long pressure_client_ioctl(struct file *filp, - unsigned int cmd, unsigned long arg) -{ - int ret = 0; - int delay; - unsigned char mask[] = {0xff, 0xff, 0xff, 0xff}; - static int status; - int altitude = 0; - void __user *argp = (void __user *)arg; - struct pressure_client *pressure_client_data = filp->private_data; - - switch (cmd) { - case M4_SENSOR_IOCTL_GET_PRESSURE: - m4_read_pressure_data(pressure_client_data); - m4_report_pressure_inputevent(pressure_client_data); - break; - - case M4_SENSOR_IOCTL_SET_DELAY: - if (copy_from_user(&delay, argp, sizeof(delay))) - return -EFAULT; - if (delay >= 0) - ret = m4_set_pressure_samplerate(pressure_client_data, - delay); - if (ret < 0) { - KDEBUG(M4SH_ERROR, "Error enabling int %d (%d)\n", - M4SH_IRQ_PRESSURE_DATA_READY, ret); - return -EFAULT; - } - break; - - case M4_SENSOR_IOCTL_APP_GET_FLAG: - if (copy_to_user(argp, &status, sizeof(status))) - return -EFAULT; - break; - - case M4_SENSOR_IOCTL_APP_SET_FLAG: - if (copy_from_user(&status, argp, sizeof(status))) - return -EFAULT; - break; - case M4_SENSOR_IOCTL_SET_ALTITUDE: - if (copy_from_user(&altitude, argp, sizeof(altitude))) - return -EFAULT; - if (altitude > 0) - m4sensorhub_reg_write(pressure_client_data->m4sensorhub, - M4SH_REG_PRESSURE_REFERENCEALTITUDE, - (char *)&altitude, mask); - - break; - default: - KDEBUG(M4SH_ERROR, "Invalid IOCTL Command in %s \n", __func__); - ret = -EINVAL; - } - return ret; -} - -static ssize_t pressure_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct platform_device *pdev = to_platform_device(dev); - struct pressure_client *pressure_client_data - = platform_get_drvdata(pdev); - - m4_read_pressure_data(pressure_client_data); - KDEBUG(M4SH_DEBUG, "%s : Pressure : = %d", - __func__, pressure_client_data->pressure); - return sprintf(buf, "%d\n", pressure_client_data->pressure); -} - -static ssize_t altitude_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct platform_device *pdev = to_platform_device(dev); - struct pressure_client *pressure_client_data - = platform_get_drvdata(pdev); - - m4_read_pressure_data(pressure_client_data); - KDEBUG(M4SH_DEBUG, "%s : Altitude : %d", - __func__, pressure_client_data->altitude); - return sprintf(buf, "%d\n", pressure_client_data->altitude); -} - -static DEVICE_ATTR(pressure, 0444, pressure_show, NULL); -static DEVICE_ATTR(altitude, 0444, altitude_show, NULL); - -static const struct file_operations pressure_client_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = pressure_client_ioctl, - .open = pressure_client_open, - .release = pressure_client_close, -}; - -static struct miscdevice pressure_client_miscdrv = { - .minor = MISC_DYNAMIC_MINOR, - .name = PRESSURE_CLIENT_DRIVER_NAME, - .fops = &pressure_client_fops, -}; - - -static int pressure_driver_init(struct init_calldata *p_arg) -{ - int ret; - struct m4sensorhub_data *m4sensorhub = p_arg->p_m4sensorhub_data; - ret = m4sensorhub_irq_register(m4sensorhub, - M4SH_IRQ_PRESSURE_DATA_READY, - m4_handle_pressure_irq, - misc_pressure_data); - if (ret < 0) - KDEBUG(M4SH_ERROR, "Error registering int %d (%d)\n", - M4SH_IRQ_PRESSURE_DATA_READY, ret); - return ret; -} - -static int pressure_client_probe(struct platform_device *pdev) -{ - int ret = -1; - struct pressure_client *pressure_client_data; - struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata(); - - if (!m4sensorhub) - return -EFAULT; - - pressure_client_data = kzalloc(sizeof(*pressure_client_data), - GFP_KERNEL); - if (!pressure_client_data) - return -ENOMEM; - - pressure_client_data->m4sensorhub = m4sensorhub; - platform_set_drvdata(pdev, pressure_client_data); - - pressure_client_data->input_dev = input_allocate_device(); - if (!pressure_client_data->input_dev) { - ret = -ENOMEM; - KDEBUG(M4SH_ERROR, "%s: input device allocate failed: %d\n", - __func__, ret); - goto free_mem; - } - - pressure_client_data->input_dev->name = PRESSURE_CLIENT_DRIVER_NAME; - set_bit(EV_ABS, pressure_client_data->input_dev->evbit); - input_set_abs_params(pressure_client_data->input_dev, ABS_ALTITUDE, - -2147483647, 2147483647, 0, 0); - input_set_abs_params(pressure_client_data->input_dev, ABS_PRESSURE, - PRESSURE_MIN, PRESSURE_MAX, 0, 0); - - if (input_register_device(pressure_client_data->input_dev)) { - KDEBUG(M4SH_ERROR, "%s: input device register failed\n", - __func__); - input_free_device(pressure_client_data->input_dev); - goto free_mem; - } - - ret = misc_register(&pressure_client_miscdrv); - if (ret < 0) { - KDEBUG(M4SH_ERROR, "Error registering %s driver\n", - PRESSURE_CLIENT_DRIVER_NAME); - goto unregister_input_device; - } - misc_pressure_data = pressure_client_data; - ret = m4sensorhub_register_initcall(pressure_driver_init, - pressure_client_data); - if (ret < 0) { - KDEBUG(M4SH_ERROR, "Unable to register init function " - "for pressure client = %d\n", ret); - goto unregister_misc_device; - } - if (device_create_file(&pdev->dev, &dev_attr_pressure)) { - KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n", - PRESSURE_CLIENT_DRIVER_NAME); - ret = -1; - goto unregister_initcall; - } - - if (device_create_file(&pdev->dev, &dev_attr_altitude)) { - KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n", - PRESSURE_CLIENT_DRIVER_NAME); - ret = -1; - goto remove_device_file; - } - KDEBUG(M4SH_ERROR, "Initialized %s driver\n", __func__); - return 0; - -remove_device_file: - device_remove_file(&pdev->dev, &dev_attr_pressure); -unregister_initcall: - m4sensorhub_unregister_initcall(pressure_driver_init); -unregister_misc_device: - misc_pressure_data = NULL; - misc_deregister(&pressure_client_miscdrv); -unregister_input_device: - input_unregister_device(pressure_client_data->input_dev); -free_mem: - platform_set_drvdata(pdev, NULL); - pressure_client_data->m4sensorhub = NULL; - kfree(pressure_client_data); - pressure_client_data = NULL; - return ret; -} - -static int __exit pressure_client_remove(struct platform_device *pdev) -{ - struct pressure_client *pressure_client_data = - platform_get_drvdata(pdev); - - device_remove_file(&pdev->dev, &dev_attr_pressure); - device_remove_file(&pdev->dev, &dev_attr_altitude); - m4sensorhub_irq_disable(pressure_client_data->m4sensorhub, - M4SH_IRQ_PRESSURE_DATA_READY); - m4sensorhub_irq_unregister(pressure_client_data->m4sensorhub, - M4SH_IRQ_PRESSURE_DATA_READY); - m4sensorhub_unregister_initcall(pressure_driver_init); - misc_pressure_data = NULL; - misc_deregister(&pressure_client_miscdrv); - input_unregister_device(pressure_client_data->input_dev); - platform_set_drvdata(pdev, NULL); - pressure_client_data->m4sensorhub = NULL; - kfree(pressure_client_data); - pressure_client_data = NULL; - return 0; -} - -static void pressure_client_shutdown(struct platform_device *pdev) -{ - return; -} -#ifdef CONFIG_PM -static int pressure_client_suspend(struct platform_device *pdev, - pm_message_t message) -{ - struct pressure_client *pressure_client_data = - platform_get_drvdata(pdev); - - return m4_set_pressure_samplerate(pressure_client_data, -1); -} - -static int pressure_client_resume(struct platform_device *pdev) -{ - return 0; -} -#else -#define pressure_client_suspend NULL -#define pressure_client_resume NULL -#endif - -static struct of_device_id m4pressure_match_tbl[] = { - {.compatible = "mot,m4pressure" }, - {}, -}; - -static struct platform_driver pressure_client_driver = { - .probe = pressure_client_probe, - .remove = __exit_p(pressure_client_remove), - .shutdown = pressure_client_shutdown, - .suspend = pressure_client_suspend, - .resume = pressure_client_resume, - .driver = { - .name = PRESSURE_CLIENT_DRIVER_NAME, - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(m4pressure_match_tbl), - }, -}; - -static int __init pressure_client_init(void) -{ - return platform_driver_register(&pressure_client_driver); -} - -static void __exit pressure_client_exit(void) -{ - platform_driver_unregister(&pressure_client_driver); -} - -module_init(pressure_client_init); -module_exit(pressure_client_exit); - -MODULE_ALIAS("platform:pressure_client"); -MODULE_DESCRIPTION("M4 Sensor Hub Pressure client driver"); -MODULE_AUTHOR("Motorola"); -MODULE_LICENSE("GPL"); - diff --git a/drivers/misc/m4sensorhub_pressure.c b/drivers/misc/m4sensorhub_pressure.c new file mode 100644 index 00000000000..ae3cbc67d61 --- /dev/null +++ b/drivers/misc/m4sensorhub_pressure.c @@ -0,0 +1,420 @@ +/* + * Copyright (C) 2012 Motorola, Inc. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Adds ability to program periodic interrupts from user space that + * can wake the phone out of low power modes. + * + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/m4sensorhub.h> +#include <linux/m4sensorhub/MemMapPressureSensor.h> +#include <linux/m4sensorhub/m4sensorhub_registers.h> +#include <linux/uaccess.h> +#include <linux/slab.h> +#include <linux/iio/iio.h> +#include <linux/iio/types.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/events.h> +#include <linux/iio/buffer.h> +#include <linux/iio/kfifo_buf.h> +#include <linux/iio/m4sensorhub_pressure.h> + +#define m4sensorhub_pressure_DRIVER_NAME "m4sensorhub_pressure" + +struct m4sensorhub_pressure_drvdata { + struct m4sensorhub_data *p_m4sensorhub; + int samplerate; + bool enable; +}; + +#define DATA_SIZE_IN_BITS (sizeof(struct m4sensorhub_pressure_data) * 8) + +/* We are ADC with 1 channel */ +static const struct iio_chan_spec m4sensorhub_pressure_channels[] = { + { + .type = IIO_PRESSURE, + /* Channel has a numeric index of 0 */ + .indexed = 1, + .channel = 0, + .info_mask_separate = + /* + * in_pressure0_raw + * Raw (unscaled no bias removal etc) measurement + * from the device. + */ + BIT(IIO_CHAN_INFO_RAW), + + .scan_index = 0, + .scan_type = { /* Description of storage in buffer */ + .sign = 'u', /* unsigned */ + .realbits = DATA_SIZE_IN_BITS, + .storagebits = DATA_SIZE_IN_BITS, + .shift = 0, /* zero shift */ + }, + }, +}; + + +static void m4_handle_pressure_irq(enum m4sensorhub_irqs int_event, + void *p_data) +{ + struct iio_dev *p_iio_dev = (struct iio_dev *)p_data; + struct m4sensorhub_pressure_drvdata *p_priv_data = iio_priv(p_iio_dev); + struct m4sensorhub_data *p_m4sensorhub = p_priv_data->p_m4sensorhub; + sPressureData pressure; + struct m4sensorhub_pressure_data data; + + m4sensorhub_reg_read(p_m4sensorhub, + M4SH_REG_PRESSURE_PRESSURE, + (char *)&pressure.pressure); + m4sensorhub_reg_read(p_m4sensorhub, + M4SH_REG_PRESSURE_ABSOLUTEALTITUDE, + (char *)&pressure.absoluteAltitude); + + data.pressure = pressure.pressure; + data.altitude = pressure.absoluteAltitude; + data.timestamp = iio_get_time_ns(); + + iio_push_to_buffers(p_iio_dev, (unsigned char *)&data); +} + + +static int m4sensorhub_pressure_driver_initcallback(struct init_calldata *p_arg) +{ + struct iio_dev *p_iio_dev = (struct iio_dev *)(p_arg->p_data); + struct m4sensorhub_data *p_m4sensorhub = p_arg->p_m4sensorhub_data; + struct m4sensorhub_pressure_drvdata *p_priv_data = iio_priv(p_iio_dev); + int ret; + + p_priv_data->p_m4sensorhub = p_m4sensorhub; + + /* register for pressure interrupt from M4 */ + ret = m4sensorhub_irq_register(p_m4sensorhub, + M4SH_IRQ_PRESSURE_DATA_READY, + m4_handle_pressure_irq, + p_iio_dev); + + if (ret < 0) + pr_err("%s: Failed to register interrupt handler\n", __func__); + + return ret; +} + +/* Sysfs interface for enable and setdelay */ +/* enable */ +static ssize_t m4sensorhub_pressure_store_enable(struct device *p_dev, + struct device_attribute *p_attr, + const char *p_buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(p_dev); + struct iio_dev *p_iio_dev = + platform_get_drvdata(pdev); + struct m4sensorhub_pressure_drvdata *p_priv_data = iio_priv(p_iio_dev); + struct m4sensorhub_data *p_m4sensorhub = p_priv_data->p_m4sensorhub; + bool requested_state; + int ret; + + ret = strtobool(p_buf, &requested_state); + if (ret < 0) + return ret; + + if (requested_state == false) { + ret = m4sensorhub_irq_disable( + p_m4sensorhub, + M4SH_IRQ_PRESSURE_DATA_READY); + + if (ret < 0) { + pr_err("%s: failed to disable irq\n", __func__); + return ret; + } + } + + p_priv_data->enable = requested_state; + + return count; +} + +static ssize_t m4sensorhub_pressure_show_enable(struct device *p_dev, + struct device_attribute *p_attr, char *p_buf) +{ + struct platform_device *pdev = to_platform_device(p_dev); + struct iio_dev *p_iio_dev = + platform_get_drvdata(pdev); + struct m4sensorhub_pressure_drvdata *p_priv_data = iio_priv(p_iio_dev); + int ret = 0; + if (p_priv_data->enable) + ret = 1; + return snprintf(p_buf, PAGE_SIZE, "%d\n", ret); +} + +/* setdelay */ +static ssize_t m4sensorhub_pressure_store_setdelay(struct device *p_dev, + struct device_attribute *p_attr, + const char *p_buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(p_dev); + struct iio_dev *p_iio_dev = + platform_get_drvdata(pdev); + struct m4sensorhub_pressure_drvdata *p_priv_data = iio_priv(p_iio_dev); + int ret; + int samplerate; + + ret = kstrtoint(p_buf, 10, &samplerate); + if (ret < 0) + return ret; + + if (samplerate < 0) { + pr_err("%s: negative sample rate, rejecting\n", __func__); + return -EINVAL; + } + if (samplerate != p_priv_data->samplerate) { + ret = m4sensorhub_reg_write(p_priv_data->p_m4sensorhub, + M4SH_REG_PRESSURE_SAMPLERATE, + (char *)&samplerate, m4sh_no_mask); + if (ret != m4sensorhub_reg_getsize( + p_priv_data->p_m4sensorhub, + M4SH_REG_PRESSURE_SAMPLERATE)) { + pr_err("%s:Unable to set delay\n", __func__); + return ret; + } + + p_priv_data->samplerate = samplerate; + + ret = m4sensorhub_irq_enable( + p_priv_data->p_m4sensorhub, + M4SH_IRQ_PRESSURE_DATA_READY); + if (ret < 0) { + pr_err("%s: failed to disable irq\n", __func__); + return ret; + } + } + + return count; +} + +static ssize_t m4sensorhub_pressure_show_setdelay(struct device *p_dev, + struct device_attribute *p_attr, char *p_buf) +{ + struct platform_device *pdev = to_platform_device(p_dev); + struct iio_dev *p_iio_dev = + platform_get_drvdata(pdev); + struct m4sensorhub_pressure_drvdata *p_priv_data = iio_priv(p_iio_dev); + return snprintf(p_buf, PAGE_SIZE, "%d\n", p_priv_data->samplerate); +} + +static IIO_DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, + m4sensorhub_pressure_show_enable, + m4sensorhub_pressure_store_enable, 0); +static IIO_DEVICE_ATTR(setdelay, S_IRUGO | S_IWUSR, + m4sensorhub_pressure_show_setdelay, + m4sensorhub_pressure_store_setdelay, 0); + +#define M4_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr) + +static struct attribute *m4sensorhub_pressure_attributes[] = { + M4_DEV_ATTR(enable), + M4_DEV_ATTR(setdelay), + NULL +}; + +static const struct attribute_group m4sensorhub_pressure_group = { + .attrs = m4sensorhub_pressure_attributes, +}; + +static const struct iio_info m4sensorhub_pressure_iio_info = { + .driver_module = THIS_MODULE, + .attrs = &m4sensorhub_pressure_group, +}; + +static int m4sensorhub_pressure_setup_buffer(struct iio_dev *p_iio_dev) +{ + struct iio_buffer *p_buffer; + int ret; + p_buffer = iio_kfifo_allocate(p_iio_dev); + if (p_buffer == NULL) { + pr_err("%s: failed to allocate buffer\n", __func__); + ret = -ENOMEM; + return ret; + } + + p_iio_dev->buffer = p_buffer; + + /* need timestamps */ + p_buffer->scan_timestamp = true; + ret = iio_buffer_register(p_iio_dev, p_iio_dev->channels, + p_iio_dev->num_channels); + + if (ret < 0) { + pr_err("%s: failed to register buffer\n", __func__); + goto err; + } + p_buffer->access->set_bytes_per_datum(p_buffer, + sizeof(struct m4sensorhub_pressure_data)); + + ret = 0; + return ret; +err: + iio_kfifo_free(p_buffer); + + return ret; +} + +static int m4sensorhub_pressure_probe(struct platform_device *pdev) +{ + int ret = -1; + struct iio_dev *p_iio_dev; + struct m4sensorhub_pressure_drvdata *p_priv_data; + + p_iio_dev = iio_device_alloc( + sizeof(struct m4sensorhub_pressure_drvdata)); + + if (p_iio_dev == NULL) { + pr_err("%s: no mem", __func__); + ret = -ENOMEM; + goto err; + } + + p_priv_data = iio_priv(p_iio_dev); + p_priv_data->samplerate = -1; + p_priv_data->enable = false; + p_priv_data->p_m4sensorhub = NULL; + + platform_set_drvdata(pdev, p_iio_dev); + + p_iio_dev->info = &m4sensorhub_pressure_iio_info; + p_iio_dev->name = m4sensorhub_pressure_DRIVER_NAME; + p_iio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_HARDWARE; + p_iio_dev->channels = m4sensorhub_pressure_channels; + p_iio_dev->num_channels = ARRAY_SIZE(m4sensorhub_pressure_channels); + + /* Register the channel with a buffer */ + ret = m4sensorhub_pressure_setup_buffer(p_iio_dev); + if (ret < 0) { + pr_err("%s: can't setup buffer", __func__); + goto cleanup1; + } + + ret = iio_device_register(p_iio_dev); + if (ret < 0) { + pr_err("%s: iio_register failed", __func__); + goto cleanup2; + } + + ret = m4sensorhub_register_initcall( + m4sensorhub_pressure_driver_initcallback, + p_iio_dev); + if (ret < 0) { + pr_err("%s:Register init failed, ret = %d\n", __func__, ret); + goto cleanup3; + } + + return 0; +cleanup3: + iio_device_unregister(p_iio_dev); +cleanup2: + iio_kfifo_free(p_iio_dev->buffer); + iio_buffer_unregister(p_iio_dev); +cleanup1: + iio_device_free(p_iio_dev); + platform_set_drvdata(pdev, NULL); +err: + return ret; +} + +static int __exit m4sensorhub_pressure_remove(struct platform_device *pdev) +{ + struct iio_dev *p_iio_dev = + platform_get_drvdata(pdev); + struct m4sensorhub_pressure_drvdata *p_priv_data = iio_priv(p_iio_dev); + + m4sensorhub_unregister_initcall( + m4sensorhub_pressure_driver_initcallback); + m4sensorhub_irq_unregister(p_priv_data->p_m4sensorhub, + M4SH_IRQ_PRESSURE_DATA_READY); + + iio_kfifo_free(p_iio_dev->buffer); + iio_buffer_unregister(p_iio_dev); + iio_device_unregister(p_iio_dev); + iio_device_free(p_iio_dev); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static void m4sensorhub_pressure_shutdown(struct platform_device *pdev) +{ + return; +} +/* when we suspend, we disable pressure IRQ and set samplerate to -1 +when we resume, we wait for the app to re-register and that re-enables +the sensor. This is global M4 strategy, nothing specific for this driver */ +#ifdef CONFIG_PM +static int m4sensorhub_pressure_suspend(struct platform_device *pdev, + pm_message_t message) +{ + struct iio_dev *p_iio_dev = + platform_get_drvdata(pdev); + struct m4sensorhub_pressure_drvdata *p_priv_data = iio_priv(p_iio_dev); + struct m4sensorhub_data *p_m4sensorhub = p_priv_data->p_m4sensorhub; + int samplerate = -1; + + m4sensorhub_irq_disable(p_m4sensorhub, + M4SH_IRQ_PRESSURE_DATA_READY); + m4sensorhub_reg_write(p_m4sensorhub, + M4SH_REG_PRESSURE_SAMPLERATE, + (char *)&samplerate, m4sh_no_mask); + return 0; +} + +static int m4sensorhub_pressure_resume(struct platform_device *pdev) +{ + return 0; +} +#else +#define m4sensorhub_pressure_suspend NULL +#define m4sensorhub_pressure_resume NULL +#endif + +static struct of_device_id m4sensorhub_pressure_match_tbl[] = { + { .compatible = "mot,m4sensorhub_pressure" }, + {}, +}; + +static struct platform_driver m4sensorhub_pressure_driver = { + .probe = m4sensorhub_pressure_probe, + .remove = __exit_p(m4sensorhub_pressure_remove), + .shutdown = m4sensorhub_pressure_shutdown, + .suspend = m4sensorhub_pressure_suspend, + .resume = m4sensorhub_pressure_resume, + .driver = { + .name = m4sensorhub_pressure_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(m4sensorhub_pressure_match_tbl), + }, +}; + +module_platform_driver(m4sensorhub_pressure_driver); + +MODULE_ALIAS("platform:m4sensorhub_pressure"); +MODULE_DESCRIPTION("M4 Sensor Hub Pressure IIO driver"); +MODULE_AUTHOR("Motorola"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/iio/m4sensorhub_pressure.h b/include/linux/iio/m4sensorhub_pressure.h new file mode 100644 index 00000000000..856364f0e3a --- /dev/null +++ b/include/linux/iio/m4sensorhub_pressure.h @@ -0,0 +1,12 @@ +/* HEADER */ + +#include <linux/types.h> + +/* This needs to be thought through when we +add other sensors */ + +struct m4sensorhub_pressure_data { + int pressure; + int altitude; + long long timestamp; +}; |