summaryrefslogtreecommitdiff
path: root/drivers/misc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/Kconfig4
-rw-r--r--drivers/misc/Makefile12
-rw-r--r--drivers/misc/m4sensorhub_audio.c496
-rw-r--r--drivers/misc/m4sensorhub_bmp180.c453
-rw-r--r--drivers/misc/m4sensorhub_display.c558
-rw-r--r--drivers/misc/m4sensorhub_download.c501
-rw-r--r--drivers/misc/m4sensorhub_gesture.c415
-rw-r--r--drivers/misc/m4sensorhub_mpu9150.c1243
-rw-r--r--drivers/misc/m4sensorhub_passive.c325
-rw-r--r--drivers/misc/m4sensorhub_pedometer.c689
-rw-r--r--drivers/misc/m4sensorhub_stillmode.c425
-rw-r--r--drivers/misc/m4sensorhub_tmp006.c432
-rw-r--r--drivers/misc/m4sensorhub_wrist.c836
-rw-r--r--drivers/misc/m4sensorhub_wrist.h106
-rw-r--r--drivers/misc/vib-gpio.c224
15 files changed, 6719 insertions, 0 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 3f5743424ff..18c65fcca01 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -333,6 +333,10 @@ config ISL29020
This driver can also be built as a module. If so, the module
will be called isl29020.
+config VIB_GPIO
+ bool "GPIO Vibrator"
+ default n
+
config SENSORS_TSL2550
tristate "Taos TSL2550 ambient light sensor"
depends on I2C && SYSFS
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index a57666cec34..9f72ecb358d 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -54,3 +54,15 @@ 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_tmp006.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
+obj-$(CONFIG_MFD_M4SENSORHUB) += m4sensorhub_stillmode.o
+obj-$(CONFIG_MFD_M4SENSORHUB) += m4sensorhub_download.o
+obj-$(CONFIG_MFD_M4SENSORHUB) += m4sensorhub_display.o
+obj-$(CONFIG_MFD_M4SENSORHUB) += m4sensorhub_audio.o
+obj-$(CONFIG_MFD_M4SENSORHUB) += m4sensorhub_wrist.o
+obj-$(CONFIG_MFD_M4SENSORHUB) += m4sensorhub_passive.o
+obj-$(CONFIG_VIB_GPIO) += vib-gpio.o
diff --git a/drivers/misc/m4sensorhub_audio.c b/drivers/misc/m4sensorhub_audio.c
new file mode 100644
index 00000000000..511e2776bf8
--- /dev/null
+++ b/drivers/misc/m4sensorhub_audio.c
@@ -0,0 +1,496 @@
+/*
+ * Copyright (C) 2013 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/input.h>
+#include <linux/kernel.h>
+#include <linux/m4sensorhub.h>
+#include <linux/m4sensorhub_client_ioctl.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/proc_fs.h>
+#include <linux/sound.h>
+#include <linux/soundcard.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/m4sensorhub/MemMapAudio.h>
+#include <linux/spi/spi.h>
+
+
+#define AUDIO_CLIENT_DRIVER_NAME "m4sensorhub_audio"
+/* This is the number of total kernel buffers */
+#define AUDIO_NBFRAGS_READ 20
+#define AUDIO_SAMPLE_RATE 16000
+#define AUDIO_TIMEOUT HZ
+#define MIC_ENABLE 0x01
+#define MIC_DISABLE 0x00
+
+/* Mutex used to prevent mutiple calls to audio functions at same time */
+DEFINE_MUTEX(audio_lock);
+
+struct audio_client {
+ struct m4sensorhub_data *m4sensorhub;
+ struct spi_device *spi;
+ int dev_dsp;
+ int dev_dsp_open_count;
+ char *buffers[AUDIO_NBFRAGS_READ];
+ unsigned int usr_head; /* user index where app is reading from */
+ unsigned int buf_head; /* SPI index where SPI writing to */
+ unsigned int usr_offset; /* offset in usr_head buffer to read */
+ int read_buf_full; /* num buffers available for app */
+ u32 total_buf_cnt; /* total num of bufs read from since audio enable*/
+ wait_queue_head_t wq; /* wait till read buffer is available */
+ int active; /* Indicates if audio transfer is active */
+};
+
+struct audio_client *audio_data;
+
+static ssize_t audio_get_loglevel(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned long long loglevel;
+ struct spi_device *spi = to_spi_device(dev);
+ struct audio_client *audio_client_data = spi_get_drvdata(spi);
+
+ m4sensorhub_reg_read(audio_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&loglevel);
+ loglevel = get_log_level(loglevel, AUDIO_MASK_BIT_1);
+ return sprintf(buf, "%llu\n", loglevel);
+}
+static ssize_t audio_set_loglevel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned long level;
+ unsigned long long mask = 0, newlevel;
+ struct spi_device *spi = to_spi_device(dev);
+ struct audio_client *audio_client_data = spi_get_drvdata(spi);
+
+ if (strict_strtoul(buf, 10, &level) < 0)
+ return -1;
+ if (level > M4_MAX_LOG_LEVEL) {
+ KDEBUG(M4SH_ERROR, " Invalid log level - %d\n", (int)level);
+ return -1;
+ }
+ mask = (1ULL << AUDIO_MASK_BIT_1) | (1ULL << AUDIO_MASK_BIT_2);
+ newlevel = (unsigned long long)level << AUDIO_MASK_BIT_1;
+ return m4sensorhub_reg_write(audio_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&newlevel,
+ (unsigned char *)&mask);
+}
+static DEVICE_ATTR(LogLevel, 0664, audio_get_loglevel, audio_set_loglevel);
+
+static void audio_client_spidma_read(struct audio_client *audio_client_data,
+ int len)
+{
+ int ret = 0;
+ struct spi_message msg;
+ struct spi_transfer rx;
+ unsigned char txbuff[AUDIO_BUFFER_SIZE];
+
+ memset(&rx, 0x00, sizeof(struct spi_transfer));
+ memset(&msg, 0x00, sizeof(struct spi_message));
+
+ rx.rx_buf = audio_client_data->buffers[
+ audio_client_data->buf_head];
+ rx.tx_buf = txbuff;
+ rx.len = len;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&rx, &msg);
+
+ ret = spi_sync(audio_client_data->spi, &msg);
+ if (ret < 0)
+ KDEBUG(M4SH_ERROR, "%s failed to read %d bytes, ret = %d\n",
+ __func__, len, ret);
+ else {
+ audio_data->read_buf_full++;
+ audio_data->total_buf_cnt++;
+ wake_up_interruptible(&audio_data->wq);
+
+ if (++audio_client_data->buf_head >= AUDIO_NBFRAGS_READ)
+ audio_client_data->buf_head = 0;
+ }
+
+}
+
+static void m4_handle_audio_irq(enum m4sensorhub_irqs int_event,
+ void *data)
+{
+ u32 m4_buf_cnt = 0;
+ u32 bufs_to_read = 0;
+ struct audio_client *audio_client_data = (struct audio_client *)data;
+ int ret = 0;
+
+ mutex_lock(&audio_lock);
+
+ /* Read the total buf count from M4 */
+ ret = m4sensorhub_reg_read(audio_client_data->m4sensorhub,
+ M4SH_REG_AUDIO_TOTALPACKETS,
+ (char *)&m4_buf_cnt);
+
+ if (ret != m4sensorhub_reg_getsize(audio_client_data->m4sensorhub,
+ M4SH_REG_AUDIO_TOTALPACKETS)) {
+ KDEBUG(M4SH_ERROR, "M4 packet count read failed %d\n", ret);
+ goto EXIT;
+ }
+
+ bufs_to_read = m4_buf_cnt - audio_data->total_buf_cnt;
+ KDEBUG(M4SH_DEBUG, "R = %u, m4_cnt = %u, omap_cnt = %u\n",
+ bufs_to_read, m4_buf_cnt, audio_data->total_buf_cnt);
+
+ /* If no free buffers, then skip reads from SPI */
+ while ((bufs_to_read) &&
+ (audio_data->read_buf_full < AUDIO_NBFRAGS_READ)) {
+ audio_client_spidma_read(audio_client_data, AUDIO_BUFFER_SIZE);
+ bufs_to_read--;
+ }
+
+EXIT:
+ mutex_unlock(&audio_lock);
+}
+
+static int audio_client_open(struct inode *inode, struct file *file)
+{
+ int ret = 0, i = 0;
+ mutex_lock(&audio_lock);
+
+ if (audio_data->dev_dsp_open_count == 1) {
+ KDEBUG(M4SH_ERROR, "Mic already opened, can't open again\n");
+ ret = -EBUSY;
+ goto out;
+ }
+
+ for (i = 0; i < AUDIO_NBFRAGS_READ; i++) {
+ audio_data->buffers[i] = kmalloc(AUDIO_BUFFER_SIZE,
+ GFP_KERNEL | GFP_DMA);
+ if (!audio_data->buffers[i]) {
+ KDEBUG(M4SH_ERROR, "Can't allocate memory for mic\n");
+ ret = -ENOMEM;
+ goto free_buffers;
+ }
+ }
+
+ audio_data->active = 0;
+ audio_data->usr_head = 0;
+ audio_data->usr_offset = 0;
+ audio_data->buf_head = 0;
+ audio_data->read_buf_full = 0;
+
+ ret = m4sensorhub_irq_enable(audio_data->m4sensorhub,
+ M4SH_IRQ_MIC_DATA_READY);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Unable to enable mic irq, ret = %d\n",
+ ret);
+ goto free_buffers;
+ }
+
+ init_waitqueue_head(&audio_data->wq);
+
+ audio_data->dev_dsp_open_count = 1;
+ KDEBUG(M4SH_INFO, "M4 mic driver opened\n");
+ goto out;
+
+free_buffers:
+ for (i = 0; i < AUDIO_NBFRAGS_READ; i++) {
+ kfree((void *) audio_data->buffers[i]);
+ audio_data->buffers[i] = NULL;
+ }
+out:
+ mutex_unlock(&audio_lock);
+ return ret;
+}
+
+static int audio_client_release(struct inode *inode, struct file *file)
+{
+ int i = 0, ret;
+ mutex_lock(&audio_lock);
+
+ audio_data->active = 0;
+ ret = m4sensorhub_irq_disable(audio_data->m4sensorhub,
+ M4SH_IRQ_MIC_DATA_READY);
+ if (ret < 0)
+ KDEBUG(M4SH_ERROR, "Unable to disable mic, ret = %d\n", ret);
+ ret = m4sensorhub_reg_write_1byte(audio_data->m4sensorhub,
+ M4SH_REG_AUDIO_ENABLE, MIC_DISABLE, 0xFF);
+ /* Check that we wrote 1 byte */
+ if (ret != 1)
+ KDEBUG(M4SH_ERROR, "Unable to disable mic, size = %d\n", ret);
+
+ audio_data->dev_dsp_open_count = 0;
+
+ for (i = 0; i < AUDIO_NBFRAGS_READ; i++) {
+ kfree((void *) audio_data->buffers[i]);
+ audio_data->buffers[i] = NULL;
+ }
+
+ mutex_unlock(&audio_lock);
+ KDEBUG(M4SH_INFO, "M4 mic driver closed\n");
+ return 0;
+}
+
+static long audio_client_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ unsigned int samp_rate;
+
+ mutex_lock(&audio_lock);
+
+ switch (cmd) {
+ case OSS_GETVERSION:
+ ret = put_user(SOUND_VERSION, (int *)arg);
+ break;
+
+ case SNDCTL_DSP_SPEED:
+ if (copy_from_user(&samp_rate, (unsigned int *)arg,
+ sizeof(unsigned int)))
+ ret = -EFAULT;
+ else if (samp_rate != AUDIO_SAMPLE_RATE)
+ ret = -EINVAL;
+
+ break;
+
+ case SNDCTL_DSP_GETBLKSIZE:
+ put_user(AUDIO_BUFFER_SIZE, (int *)arg);
+ break;
+
+ default:
+ break;
+ }
+ mutex_unlock(&audio_lock);
+ return ret;
+}
+
+static ssize_t audio_client_read(struct file *file, char *buffer, size_t size,
+ loff_t *nouse)
+{
+ int ret = 0;
+ int local_size = size;
+ int local_offset = 0; /* offset into output buffer */
+ int remainder_buff = 0; /* Indicates bytes remaining in input buffer */
+
+ mutex_lock(&audio_lock);
+
+ if (!audio_data->active) {
+ ret = m4sensorhub_reg_write_1byte(audio_data->m4sensorhub,
+ M4SH_REG_AUDIO_ENABLE, MIC_ENABLE, 0xFF);
+ /* Check that we wrote 1 byte */
+ if (ret != 1) {
+ KDEBUG(M4SH_ERROR, "Unable to enable mic, size = %d\n",
+ ret);
+ goto out;
+ }
+ audio_data->active = 1;
+ audio_data->total_buf_cnt = 0;
+ }
+
+ while (local_size > 0) {
+ mutex_unlock(&audio_lock);
+ ret = wait_event_interruptible_timeout(audio_data->wq,
+ audio_data->read_buf_full > 0, AUDIO_TIMEOUT);
+ mutex_lock(&audio_lock);
+ if (!ret) {
+ KDEBUG(M4SH_ERROR,
+ "Timed out waiting for mic buffer\n");
+ goto out;
+ }
+
+ remainder_buff = AUDIO_BUFFER_SIZE - audio_data->usr_offset;
+ if (local_size > remainder_buff) {
+
+ if (copy_to_user(buffer + local_offset,
+ audio_data->buffers
+ [audio_data->usr_head] +
+ audio_data->usr_offset,
+ remainder_buff)) {
+ KDEBUG(M4SH_ERROR,
+ "Mic driver: copy_to_user failed \n");
+ ret = -EFAULT;
+ goto out;
+ }
+
+ if (++audio_data->usr_head >= AUDIO_NBFRAGS_READ)
+ audio_data->usr_head = 0;
+
+ if (--audio_data->read_buf_full < 0)
+ audio_data->read_buf_full = 0;
+
+ local_size -= remainder_buff;
+ local_offset += remainder_buff;
+ audio_data->usr_offset = 0;
+ } else {
+
+ if (copy_to_user(buffer + local_offset,
+ audio_data->buffers
+ [audio_data->usr_head] +
+ audio_data->usr_offset, local_size)) {
+ KDEBUG(M4SH_ERROR,
+ "Mic driver: copy_to_user failed \n");
+ ret = -EFAULT;
+ goto out;
+ }
+
+ if (local_size == remainder_buff) {
+ if (++audio_data->usr_head >=
+ AUDIO_NBFRAGS_READ)
+ audio_data->usr_head = 0;
+
+ if (--audio_data->read_buf_full < 0)
+ audio_data->read_buf_full = 0;
+
+ audio_data->usr_offset = 0;
+
+ } else {
+ audio_data->usr_offset += local_size;
+ }
+
+ local_size = 0;
+ }
+ }
+ ret = size;
+
+out:
+ mutex_unlock(&audio_lock);
+ return ret;
+}
+
+/* File Ops structure */
+static const struct file_operations audio_client_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_client_open,
+ .release = audio_client_release,
+ .unlocked_ioctl = audio_client_ioctl,
+ .read = audio_client_read,
+};
+
+static int audio_client_probe(struct spi_device *spi)
+{
+ int ret = -1;
+ struct audio_client *audio_client_data;
+ struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata();
+
+ if (!m4sensorhub)
+ return -EFAULT;
+
+ audio_client_data = kzalloc(sizeof(*audio_client_data), GFP_KERNEL);
+ if (!audio_client_data)
+ return -ENOMEM;
+ audio_client_data->m4sensorhub = m4sensorhub;
+ spi_set_drvdata(spi, audio_client_data);
+ audio_client_data->spi = spi;
+ audio_data = audio_client_data;
+
+ ret = register_sound_dsp(&audio_client_fops, -1);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering %s driver\n",
+ AUDIO_CLIENT_DRIVER_NAME);
+ goto free_client_data;
+ }
+ audio_client_data->dev_dsp = ret;
+ audio_client_data->dev_dsp_open_count = 0;
+
+ ret = m4sensorhub_irq_register(m4sensorhub, M4SH_IRQ_MIC_DATA_READY,
+ m4_handle_audio_irq, audio_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering int %d (%d)\n",
+ M4SH_IRQ_MIC_DATA_READY, ret);
+ goto unregister_sound_device;
+ }
+
+ ret = device_create_file(&spi->dev, &dev_attr_LogLevel);
+ if (ret) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n",
+ AUDIO_CLIENT_DRIVER_NAME);
+ goto unregister_irq;
+ }
+
+ KDEBUG(M4SH_ERROR, "Initialized %s driver\n", AUDIO_CLIENT_DRIVER_NAME);
+ return 0;
+
+unregister_irq:
+ m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_MIC_DATA_READY);
+unregister_sound_device:
+ unregister_sound_dsp(audio_client_data->dev_dsp);
+free_client_data:
+ spi_set_drvdata(spi, NULL);
+ kfree(audio_client_data);
+ return ret;
+}
+
+static int __exit audio_client_remove(struct spi_device *spi)
+{
+ struct audio_client *audio_client_data = spi_get_drvdata(spi);
+
+ device_remove_file(&spi->dev, &dev_attr_LogLevel);
+ m4sensorhub_irq_disable(audio_client_data->m4sensorhub,
+ M4SH_IRQ_MIC_DATA_READY);
+ m4sensorhub_irq_unregister(audio_client_data->m4sensorhub,
+ M4SH_IRQ_MIC_DATA_READY);
+ unregister_sound_dsp(audio_client_data->dev_dsp);
+ spi_set_drvdata(spi, NULL);
+ kfree(audio_client_data);
+ return 0;
+}
+
+
+static struct of_device_id m4audio_match_tbl[] = {
+ {.compatible = "mot,m4audio"},
+ {},
+};
+
+static struct spi_driver audio_client_spi_driver = {
+ .driver = {
+ .name = AUDIO_CLIENT_DRIVER_NAME,
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(m4audio_match_tbl),
+ },
+ .suspend = NULL,
+ .resume = NULL,
+ .probe = audio_client_probe,
+ .remove = __exit_p(audio_client_remove),
+};
+
+static int __init audio_client_init(void)
+{
+ int ret = 0;
+
+ ret = spi_register_driver(&audio_client_spi_driver);
+
+ return ret;
+}
+
+static void __exit audio_client_exit(void)
+{
+ spi_unregister_driver(&audio_client_spi_driver);
+}
+
+module_init(audio_client_init);
+module_exit(audio_client_exit);
+
+MODULE_ALIAS("platform:audio_client");
+MODULE_DESCRIPTION("M4 Sensor Hub audio client driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/misc/m4sensorhub_bmp180.c b/drivers/misc/m4sensorhub_bmp180.c
new file mode 100644
index 00000000000..c64fa377ee0
--- /dev/null
+++ b/drivers/misc/m4sensorhub_bmp180.c
@@ -0,0 +1,453 @@
+/*
+ * 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 ssize_t bmp_get_loglevel(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned long long loglevel;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pressure_client *pressure_client_data
+ = platform_get_drvdata(pdev);
+
+ m4sensorhub_reg_read(pressure_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&loglevel);
+ loglevel = get_log_level(loglevel, BMP_MASK_BIT_1);
+ return sprintf(buf, "%llu\n", loglevel);
+}
+static ssize_t bmp_set_loglevel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned long level;
+ unsigned long long mask = 0, newlevel;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pressure_client *pressure_client_data
+ = platform_get_drvdata(pdev);
+
+ if ((strict_strtoul(buf, 10, &level)) < 0)
+ return -1;
+ if (level > M4_MAX_LOG_LEVEL) {
+ KDEBUG(M4SH_ERROR, " Invalid log level - %d\n", (int)level);
+ return -1;
+ }
+ mask = (1ULL << BMP_MASK_BIT_1) | (1ULL << BMP_MASK_BIT_2);
+ newlevel = ((unsigned long long)level << BMP_MASK_BIT_1);
+ return m4sensorhub_reg_write(pressure_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&newlevel, (unsigned char *)&mask);
+}
+
+static DEVICE_ATTR(pressure, 0444, pressure_show, NULL);
+static DEVICE_ATTR(altitude, 0444, altitude_show, NULL);
+static DEVICE_ATTR(LogLevel, 0664, bmp_get_loglevel, bmp_set_loglevel);
+
+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_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);
+ set_bit(ABS_PRESSURE, pressure_client_data->input_dev->absbit);
+ set_bit(ABS_ALTITUDE, pressure_client_data->input_dev->absbit);
+ 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_irq_register(m4sensorhub,
+ M4SH_IRQ_PRESSURE_DATA_READY, m4_handle_pressure_irq,
+ pressure_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering int %d (%d)\n",
+ M4SH_IRQ_PRESSURE_DATA_READY, 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_irq;
+ }
+
+ 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;
+ }
+ if (device_create_file(&pdev->dev, &dev_attr_LogLevel)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n",
+ PRESSURE_CLIENT_DRIVER_NAME);
+ ret = -1;
+ goto remove_alt_device_file;
+ }
+ KDEBUG(M4SH_ERROR, "Initialized %s driver\n", __func__);
+ return 0;
+
+remove_alt_device_file:
+ device_remove_file(&pdev->dev, &dev_attr_altitude);
+remove_device_file:
+ device_remove_file(&pdev->dev, &dev_attr_pressure);
+unregister_irq:
+ m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_PRESSURE_DATA_READY);
+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_LogLevel);
+ 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);
+ 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_display.c b/drivers/misc/m4sensorhub_display.c
new file mode 100644
index 00000000000..2bc96643d89
--- /dev/null
+++ b/drivers/misc/m4sensorhub_display.c
@@ -0,0 +1,558 @@
+/*
+ * Copyright (C) 2013 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/delay.h>
+#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/regulator/consumer.h>
+#include <linux/m4sensorhub.h>
+#include <linux/m4sensorhub_client_ioctl.h>
+#include <linux/uaccess.h>
+#include <linux/time.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+
+#define DISPLAY_CLIENT_DRIVER_NAME "m4sensorhub_display"
+#define SENSORHUB_MIPI_DRIVER_REGULATOR "vcsi"
+#define SYNC_CLOCK_RETRY_TIMES 3
+#define INVALID_UTC_TIME 0xFFFFFFFF
+
+struct display_client {
+ struct m4sensorhub_data *m4sensorhub;
+ struct regulator *regulator;
+ atomic_t m4_lockcnt;
+ struct mutex m4_mutex;
+ int gpio_mipi_mux;
+ int m4_control;
+ int m4_enable;
+ int timezone_offset;
+ int m4_timezone_offset;
+ int dailystep_offset;
+ int m4_dailystep_offset;
+};
+
+struct display_client *global_display_data;
+
+static u32 m4_display_get_clock(void);
+static u32 m4_display_get_kernel_clock(void);
+static int m4_display_set_clock(u32 ms);
+static int m4_display_sync_timezone(struct display_client *data);
+static int m4_display_sync_dailystep(struct display_client *data);
+static int m4_display_sync_clock(void);
+static int m4_display_sync_state(struct display_client *display_data);
+static int m4_display_lock(struct display_client *display_data, int lock);
+static int m4_display_set_control(int m4_ctrl, int gpio_mipi_mux);
+
+static int display_client_open(struct inode *inode, struct file *file)
+{
+ int ret = -EFAULT;
+ KDEBUG(M4SH_DEBUG, "%s:\n", __func__);
+ if (global_display_data) {
+ ret = nonseekable_open(inode, file);
+ if (ret >= 0) {
+ file->private_data = global_display_data;
+ ret = 0;
+ }
+ }
+ if (ret)
+ KDEBUG(M4SH_ERROR, "%s: failed, err=%d\n", __func__, -ret);
+ return ret;
+}
+
+static int display_client_close(struct inode *inode, struct file *file)
+{
+ KDEBUG(M4SH_DEBUG, "%s:\n", __func__);
+ file->private_data = NULL;
+ return 0;
+}
+
+static long display_client_ioctl(
+ struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ struct display_client *display_data = filp->private_data;
+ int value;
+
+ switch (cmd) {
+ case M4_SENSOR_IOCTL_SET_TIMEZONE_OFFSET:
+ if (copy_from_user(&value, argp, sizeof(value)))
+ return -EFAULT;
+ KDEBUG(M4SH_INFO, "%s update timezone offset %d\n",\
+ __func__, value);
+ m4_display_lock(display_data, true);
+ display_data->timezone_offset = value;
+ value = m4_display_sync_timezone(display_data);
+ /* sync M4 clock every time when app set timezone offset */
+ /* FIXME:Setting rtc clock on M4 happens in M4 ISR and leads to
+ * i2c timeout for the first clock set, this causes the bus to
+ * fail for a few mins on klockworks,hence commenting this part
+ * of the code until fixed for klockworks
+ if (value == 0)
+ m4_display_sync_clock();
+ */
+ m4_display_lock(display_data, false);
+ return value;
+ case M4_SENSOR_IOCTL_SET_DAILYSTEP_OFFSET:
+ if (copy_from_user(&value, argp, sizeof(value)))
+ return -EFAULT;
+ KDEBUG(M4SH_INFO, "%s update dailystep offset %d\n",\
+ __func__, value);
+ display_data->dailystep_offset = value;
+ return m4_display_sync_dailystep(display_data);
+ case M4_SENSOR_IOCTL_LOCK_CLOCKFACE:
+ if (copy_from_user(&value, argp, sizeof(value)))
+ return -EFAULT;
+ return m4_display_lock(display_data, value);
+ default:
+ KDEBUG(M4SH_ERROR, "%s Invaild ioctl cmd %d\n", __func__, cmd);
+ break;
+ }
+ return -EINVAL;
+}
+
+/* Get current M4 RTC time of seconds elapsed since 00:00:00
+ * on January 1, 1970, Coordinated Universal Time
+ * return 0xFFFFFFFF if it's something wrong
+ */
+static u32 m4_display_get_clock(void)
+{
+ u32 seconds;
+ struct display_client *data = global_display_data;
+ if (m4sensorhub_reg_getsize(data->m4sensorhub,\
+ M4SH_REG_GENERAL_UTC) != m4sensorhub_reg_read(\
+ data->m4sensorhub, M4SH_REG_GENERAL_UTC,\
+ (char *)&seconds)) {
+ seconds = INVALID_UTC_TIME;
+ KDEBUG(M4SH_ERROR, "%s: Failed get M4 clock!\n", __func__);
+ }
+ return seconds;
+}
+
+/* Set current M4 RTC time of seconds elapsed since 00:00:00
+ * on January 1, 1970, Coordinated Universal Time
+ * return < 0 if it's something wrong
+ */
+static int m4_display_set_clock(u32 seconds)
+{
+ struct display_client *data = global_display_data;
+ if (m4sensorhub_reg_getsize(data->m4sensorhub,\
+ M4SH_REG_GENERAL_UTC) != m4sensorhub_reg_write(\
+ data->m4sensorhub, M4SH_REG_GENERAL_UTC,\
+ (char *)&seconds, m4sh_no_mask)) {
+ KDEBUG(M4SH_ERROR, "%s: Failed set M4 clock!\n", __func__);
+ return -EIO;
+ }
+ return 0;
+}
+
+static void m4_notify_clock_change(void)
+{
+ struct display_client *data = global_display_data;
+ char notify = 1;
+ if (m4sensorhub_reg_getsize(data->m4sensorhub,\
+ M4SH_REG_USERSETTINGS_RTCRESET) != m4sensorhub_reg_write(\
+ data->m4sensorhub, M4SH_REG_USERSETTINGS_RTCRESET,\
+ &notify, m4sh_no_mask)) {
+ KDEBUG(M4SH_ERROR, "Failed to notify clock change");
+ }
+}
+
+/* Get current Kernel RTC time of seconds elapsed since 00:00:00
+ * on January 1, 1970, Coordinated Universal Time
+ */
+static u32 m4_display_get_kernel_clock(void)
+{
+ struct timespec now = current_kernel_time();
+ /* adjust with 500ms */
+ if (now.tv_nsec > 500000000)
+ now.tv_sec++;
+ return (u32)now.tv_sec;
+}
+
+void print_time(char *info, u32 time)
+{
+ struct tm t; /* it convert to year since 1900 */
+ time_to_tm((time_t)(time), 0, &t);
+ KDEBUG(M4SH_INFO, "%s(%d) %02d:%02d:%02d %02d/%02d/%04d\n",\
+ info, (int)time, t.tm_hour, t.tm_min, t.tm_sec,\
+ t.tm_mon+1, t.tm_mday, (int)t.tm_year+1900);
+}
+
+/* Sync M4 clock with current kernel time */
+static int m4_display_sync_clock(void)
+{
+ int retry = 0;
+ do {
+ u32 m4_time = m4_display_get_clock();
+ u32 kernel_time = m4_display_get_kernel_clock();
+ u32 diff_time = m4_time > kernel_time \
+ ? m4_time-kernel_time : kernel_time-m4_time;
+#ifdef DEBUG_CLOCK
+ print_time("M4 :", m4_time);
+ print_time("KNL:", kernel_time);
+#endif
+ /* it needs adjust M4 time if different large than 1 second */
+ if (diff_time < 2) {
+ if (retry) {
+ print_time("Synced M4 clock to", m4_time);
+ m4_notify_clock_change();
+ }
+ return 0;
+ }
+ m4_display_set_clock(kernel_time);
+ } while (retry++ < SYNC_CLOCK_RETRY_TIMES);
+ KDEBUG(M4SH_ERROR, "%s: Failed to sync M4 clock!\n", __func__);
+ return -EIO;
+}
+
+/* Sync M4 TimeZone offset */
+static int m4_display_sync_timezone(struct display_client *data)
+{
+ if (data->m4_timezone_offset == data->timezone_offset)
+ return 0;
+ if (m4sensorhub_reg_getsize(data->m4sensorhub,\
+ M4SH_REG_GENERAL_LOCALTIMEZONE) != m4sensorhub_reg_write(\
+ data->m4sensorhub, M4SH_REG_GENERAL_LOCALTIMEZONE,\
+ (char *)&(data->timezone_offset), m4sh_no_mask)) {
+ KDEBUG(M4SH_ERROR, "%s: Failed set M4 timezone!\n", __func__);
+ return -EIO;
+ }
+ data->m4_timezone_offset = data->timezone_offset;
+ return 0;
+}
+
+/* Sync M4 Daily Steps offset */
+static int m4_display_sync_dailystep(struct display_client *data)
+{
+ if (data->m4_dailystep_offset == data->dailystep_offset)
+ return 0;
+ if (m4sensorhub_reg_getsize(data->m4sensorhub,\
+ M4SH_REG_TIMEPIECE_OFFSETSTEPS) != m4sensorhub_reg_write(\
+ data->m4sensorhub, M4SH_REG_TIMEPIECE_OFFSETSTEPS,\
+ (char *)&(data->dailystep_offset), m4sh_no_mask)) {
+ KDEBUG(M4SH_ERROR, "%s: Failed set M4 dailystep!\n", __func__);
+ return -EIO;
+ }
+ data->m4_dailystep_offset = data->dailystep_offset;
+ return 0;
+}
+
+/* Sync M4 clockface state, it will be enable only when clockface is unlocked
+ */
+static int m4_display_sync_state(struct display_client *display_data)
+{
+ int enable = !!display_data->m4_control;
+ if (atomic_inc_return(&(display_data->m4_lockcnt)) != 1)
+ enable = 0;
+ atomic_dec(&(display_data->m4_lockcnt));
+
+ mutex_lock(&(display_data->m4_mutex));
+ if (enable == display_data->m4_enable)
+ goto __done__;
+ if (enable) {
+ /* switch display control to M4 */
+ m4_display_sync_clock();
+ m4_display_sync_timezone(display_data);
+ m4_display_sync_dailystep(display_data);
+ gpio_set_value(display_data->gpio_mipi_mux, 1);
+ if (regulator_enable(display_data->regulator)) {
+ KDEBUG(M4SH_ERROR, "Failed enable regulator!\n");
+ goto __error__;
+ }
+ }
+ /* comunicate with M4 via I2C */
+ if (1 != m4sensorhub_reg_write_1byte(\
+ display_data->m4sensorhub,\
+ M4SH_REG_TIMEPIECE_ENABLE, enable, 0xFF)) {
+ KDEBUG(M4SH_ERROR, "Failed set m4 display!\n");
+ goto __error__;
+ }
+ if (!enable) {
+ mdelay(2);
+ /* switch display control to OMAP */
+ regulator_disable(display_data->regulator);
+ gpio_set_value(display_data->gpio_mipi_mux, 0);
+ }
+ display_data->m4_enable = enable;
+ KDEBUG(M4SH_INFO, "Synced M4 display state(%d)\n", enable);
+__done__:
+ mutex_unlock(&(display_data->m4_mutex));
+ return 0;
+__error__:
+ /* when error occured, it always set control to OMAP */
+ regulator_disable(display_data->regulator);
+ gpio_set_value(display_data->gpio_mipi_mux, 0);
+ mutex_unlock(&(display_data->m4_mutex));
+ KDEBUG(M4SH_ERROR, "Failed sync m4 with state(%d)!\n", enable);
+ return -EIO;
+}
+
+/* M4 clockface lock/unlock */
+static int m4_display_lock(struct display_client *display_data, int lock)
+{
+ if (lock) {
+ atomic_inc(&(display_data->m4_lockcnt));
+ } else {
+ if (atomic_dec_return(&(display_data->m4_lockcnt)) == -1) {
+ atomic_inc(&(display_data->m4_lockcnt));
+ KDEBUG(M4SH_ERROR, "%s zero unlock count!\n",\
+ __func__);
+ return -EINVAL;
+ }
+ }
+ return m4_display_sync_state(display_data);
+}
+
+static int m4_display_set_control(int m4_ctrl, int gpio_mipi_mux)
+{
+ struct display_client *display_data = global_display_data;
+ KDEBUG(M4SH_INFO, "%s(%d)\n", __func__, m4_ctrl);
+
+ if (!display_data || !display_data->m4sensorhub || (gpio_mipi_mux < 0))
+ return -EINVAL;
+
+ if (m4_ctrl == display_data->m4_control) {
+ KDEBUG(M4SH_DEBUG, "%s is already set!\n", __func__);
+ return 0;
+ }
+
+ display_data->gpio_mipi_mux = gpio_mipi_mux;
+ display_data->m4_control = m4_ctrl;
+
+ return m4_display_sync_state(display_data);
+}
+
+int m4sensorhub_set_display_control(int m4_ctrl, int gpio_mipi_mux)
+{
+ return m4_display_set_control(m4_ctrl, gpio_mipi_mux);
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_set_display_control);
+
+static ssize_t display_get_loglevel(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ uint64_t loglevel;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct display_client *display_data =
+ platform_get_drvdata(pdev);
+
+ m4sensorhub_reg_read(display_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&loglevel);
+ loglevel = get_log_level(loglevel, DISPLAY_MASK_BIT_1);
+ return sprintf(buf, "%d\n", (int)loglevel);
+}
+
+static ssize_t display_set_loglevel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ uint64_t level = 0;
+ uint64_t mask = 0x3;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct display_client *display_data =
+ platform_get_drvdata(pdev);
+
+ if ((strict_strtoul(buf, 10, (unsigned long *)&level)) < 0)
+ return -EINVAL;
+ if (level > M4_MAX_LOG_LEVEL) {
+ KDEBUG(M4SH_ERROR, " Invalid log level - %d\n", (int)level);
+ return -EINVAL;
+ }
+ mask <<= DISPLAY_MASK_BIT_1;
+ level <<= DISPLAY_MASK_BIT_1;
+ return m4sensorhub_reg_write(display_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&level, (unsigned char *)&mask);
+}
+
+static DEVICE_ATTR(LogLevel, 0664, \
+ display_get_loglevel, display_set_loglevel);
+
+static const struct file_operations display_client_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = display_client_ioctl,
+ .open = display_client_open,
+ .release = display_client_close,
+};
+
+static struct miscdevice display_client_miscdrv = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = DISPLAY_CLIENT_DRIVER_NAME,
+ .fops = &display_client_fops,
+};
+
+/* display_panic_restore()
+
+ Panic Callback Handler is called after M4 has been restarted
+
+*/
+static void display_panic_restore(\
+ struct m4sensorhub_data *m4sensorhub, void *data)
+{
+ struct display_client *display_data = (struct display_client *)data;
+ display_data->m4_timezone_offset = 0;
+ display_data->m4_dailystep_offset = 0;
+ if (display_data->m4_enable) {
+ m4_display_sync_clock();
+ m4_display_sync_timezone(display_data);
+ m4_display_sync_dailystep(display_data);
+ /* comunicate with M4 via I2C */
+ if (1 != m4sensorhub_reg_write_1byte(\
+ display_data->m4sensorhub,\
+ M4SH_REG_TIMEPIECE_ENABLE, 1, 0xFF)) {
+ KDEBUG(M4SH_ERROR, "Failed re-enable m4 display!\n");
+ /* TODO retry ? */
+ }
+ }
+}
+
+static int display_client_probe(struct platform_device *pdev)
+{
+ int ret = -EINVAL;
+ struct display_client *display_data;
+ struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata();
+
+ if (!m4sensorhub)
+ return -EFAULT;
+
+ display_data = kzalloc(sizeof(*display_data),
+ GFP_KERNEL);
+ if (!display_data)
+ return -ENOMEM;
+
+ display_data->m4sensorhub = m4sensorhub;
+ display_data->timezone_offset = 0;
+ display_data->dailystep_offset = 0;
+ display_data->m4_timezone_offset = 0;
+ display_data->m4_dailystep_offset = 0;
+
+ platform_set_drvdata(pdev, display_data);
+
+ display_data->regulator = regulator_get(NULL,
+ SENSORHUB_MIPI_DRIVER_REGULATOR);
+ if (IS_ERR(display_data->regulator)) {
+ KDEBUG(M4SH_ERROR, "Error requesting %s regulator\n",
+ SENSORHUB_MIPI_DRIVER_REGULATOR);
+ ret = -EFAULT;
+ goto free_mem;
+ }
+
+ ret = misc_register(&display_client_miscdrv);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering %s driver\n",
+ DISPLAY_CLIENT_DRIVER_NAME);
+ goto disable_regulator;
+ }
+
+ if (device_create_file(&pdev->dev, &dev_attr_LogLevel)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n",
+ DISPLAY_CLIENT_DRIVER_NAME);
+ ret = -EFAULT;
+ goto unregister_misc_device;
+ }
+
+ /* default to host control */
+ display_data->m4_control = 0;
+ display_data->m4_enable = 0;
+ atomic_set(&(display_data->m4_lockcnt), 0);
+ mutex_init(&(display_data->m4_mutex));
+
+ global_display_data = display_data;
+
+ m4sensorhub_panic_register(m4sensorhub, PANICHDL_DISPLAY_RESTORE,\
+ display_panic_restore, display_data);
+
+ KDEBUG(M4SH_INFO, "Initialized %s driver\n", __func__);
+ return 0;
+
+unregister_misc_device:
+ misc_deregister(&display_client_miscdrv);
+disable_regulator:
+ regulator_put(display_data->regulator);
+free_mem:
+ global_display_data = NULL;
+ platform_set_drvdata(pdev, NULL);
+ kfree(display_data);
+ return ret;
+}
+
+static int __exit display_client_remove(struct platform_device *pdev)
+{
+ struct display_client *display_data =
+ platform_get_drvdata(pdev);
+
+ display_data->m4sensorhub->pdev->set_display_control = NULL;
+ device_remove_file(&pdev->dev, &dev_attr_LogLevel);
+ misc_deregister(&display_client_miscdrv);
+ global_display_data = NULL;
+ regulator_put(display_data->regulator);
+ platform_set_drvdata(pdev, NULL);
+ display_data->m4sensorhub = NULL;
+ kfree(display_data);
+ return 0;
+}
+
+static void display_client_shutdown(struct platform_device *pdev)
+{
+ return;
+}
+
+static struct of_device_id m4display_match_tbl[] = {
+ { .compatible = "mot,m4display" },
+ {},
+};
+
+static struct platform_driver display_client_driver = {
+ .probe = display_client_probe,
+ .remove = __exit_p(display_client_remove),
+ .shutdown = display_client_shutdown,
+ .suspend = NULL,
+ .resume = NULL,
+ .driver = {
+ .name = DISPLAY_CLIENT_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(m4display_match_tbl),
+ },
+};
+
+static int __init display_client_init(void)
+{
+ return platform_driver_register(&display_client_driver);
+}
+
+static void __exit display_client_exit(void)
+{
+ platform_driver_unregister(&display_client_driver);
+}
+
+module_init(display_client_init);
+module_exit(display_client_exit);
+
+MODULE_ALIAS("platform:display_client");
+MODULE_DESCRIPTION("M4 Sensor Hub AOD display driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/m4sensorhub_download.c b/drivers/misc/m4sensorhub_download.c
new file mode 100644
index 00000000000..7ad1aae47b5
--- /dev/null
+++ b/drivers/misc/m4sensorhub_download.c
@@ -0,0 +1,501 @@
+/*
+ * 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/MemMapDownload.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+
+#define DOWNLOAD_CLIENT_DRIVER_NAME "m4sensorhub_download"
+#define M4_SENSOR_DL_MAX_RETRY_CNT 3
+#define M4_SENSOR_DL_MAX_RET_SIZE 8
+#define M4_SENSOR_DL_MIN_INPUT_SIZE \
+ (sizeof(struct m4sh_download_packet) - M4_SENSOR_DL_MAX_PACKET_SIZE)
+
+enum {
+ i2c_reg_end,
+ i2c_reg_read,
+ i2c_reg_write,
+ i2c_reg_wait,
+};
+
+struct i2c_reg_sequence {
+ int direct;
+ int reg;
+};
+
+struct download_client {
+ struct m4sensorhub_data *m4sensorhub;
+};
+
+struct download_client *misc_download_data;
+static wait_queue_head_t download_wq;
+static atomic_t m4_dlcmd_resp_ready;
+static atomic_t download_client_entry;
+
+static struct i2c_reg_sequence seq_m4dlm_get_checksum[] = {
+ {i2c_reg_write, M4SH_REG_DOWNLOAD_FILENAME},
+ {i2c_reg_write, M4SH_REG_DOWNLOAD_COMMAND},
+ {i2c_reg_read, M4SH_REG_DOWNLOAD_CHECKSUM},
+ {i2c_reg_read, M4SH_REG_DOWNLOAD_STATUS},
+ {i2c_reg_end, i2c_reg_end}
+};
+
+static struct i2c_reg_sequence seq_m4dlm_open_file[] = {
+ {i2c_reg_write, M4SH_REG_DOWNLOAD_FILENAME},
+ {i2c_reg_write, M4SH_REG_DOWNLOAD_COMMAND},
+ {i2c_reg_wait, M4SH_REG_DOWNLOAD_STATUS},
+ {i2c_reg_end, i2c_reg_end}
+};
+
+static struct i2c_reg_sequence seq_m4dlm_close_file[] = {
+ {i2c_reg_write, M4SH_REG_DOWNLOAD_COMMAND},
+ {i2c_reg_wait, M4SH_REG_DOWNLOAD_STATUS},
+ {i2c_reg_read, M4SH_REG_DOWNLOAD_CHECKSUM},
+ {i2c_reg_end, i2c_reg_end}
+};
+
+static struct i2c_reg_sequence seq_m4dlm_delete_file[] = {
+ {i2c_reg_write, M4SH_REG_DOWNLOAD_FILENAME},
+ {i2c_reg_write, M4SH_REG_DOWNLOAD_COMMAND},
+ {i2c_reg_wait, M4SH_REG_DOWNLOAD_STATUS},
+ {i2c_reg_end, i2c_reg_end}
+};
+
+static struct i2c_reg_sequence seq_m4dlm_write_file[] = {
+ {i2c_reg_write, M4SH_REG_DOWNLOAD_PACKET},
+ {i2c_reg_wait, M4SH_REG_DOWNLOAD_STATUS},
+ {i2c_reg_end, i2c_reg_end}
+};
+
+static struct i2c_reg_sequence seq_m4dlm_write_size_file[] = {
+ {i2c_reg_write, M4SH_REG_DOWNLOAD_SIZE},
+ {i2c_reg_write, M4SH_REG_DOWNLOAD_PACKET},
+ {i2c_reg_wait, M4SH_REG_DOWNLOAD_STATUS},
+ {i2c_reg_end, i2c_reg_end}
+};
+
+static int download_client_open(struct inode *inode, struct file *file)
+{
+ int err = atomic_inc_return(&download_client_entry);
+ if (err == 1) {
+ err = nonseekable_open(inode, file);
+ if (err >= 0) {
+ file->private_data = misc_download_data;
+ return 0;
+ }
+ } else
+ err = -EBUSY;
+
+ atomic_dec_return(&download_client_entry);
+ KDEBUG(M4SH_ERROR, "%s: failed, err=%d\n", __func__, -err);
+ return err;
+}
+
+static int download_client_close(struct inode *inode, struct file *file)
+{
+ int entry = atomic_dec_return(&download_client_entry);
+ file->private_data = NULL;
+ KDEBUG(M4SH_DEBUG, "%s: entry = %d\n", __func__, entry);
+ return 0;
+}
+
+static void m4_handle_download_irq(enum m4sensorhub_irqs int_event,
+ void *download_data)
+{
+ atomic_set(&m4_dlcmd_resp_ready, true);
+ wake_up_interruptible(&download_wq);
+}
+
+static inline void wait_m4_cmd_executed(void)
+{
+ wait_event_interruptible(download_wq, \
+ (atomic_read(&m4_dlcmd_resp_ready)));
+ atomic_set(&m4_dlcmd_resp_ready, false);
+}
+
+static char *m4dlm_i2c_reg_seq_getptr(
+ int reg, struct m4sh_download_packet *dl_packet)
+{
+ switch (reg) {
+ case M4SH_REG_DOWNLOAD_COMMAND:
+ return (char *)(&(dl_packet->command));
+ case M4SH_REG_DOWNLOAD_STATUS:
+ return (char *)(&(dl_packet->status));
+ case M4SH_REG_DOWNLOAD_SIZE:
+ return (char *)(&(dl_packet->size));
+ case M4SH_REG_DOWNLOAD_CHECKSUM:
+ return (char *)(&(dl_packet->checksum));
+ case M4SH_REG_DOWNLOAD_FILENAME:
+ return (char *)(dl_packet->filename);
+ case M4SH_REG_DOWNLOAD_PACKET:
+ return (char *)(dl_packet->buffer);
+ }
+ KDEBUG(M4SH_ERROR, "%s Invaild i2c reg %d\n", __func__, reg);
+ return NULL;
+}
+
+static int m4dlm_i2c_reg_seq_process(
+ struct m4sensorhub_data *m4sensorhub,
+ struct m4sh_download_packet *dl_packet,
+ struct i2c_reg_sequence *i2c_reg_seq)
+{
+ int ret;
+ for (; i2c_reg_seq->direct != i2c_reg_end; i2c_reg_seq++) {
+ /*we don't need retry for I2C read/write as
+ *m4sensorhub_reg_write/read already had retry mechanism
+ */
+ switch (i2c_reg_seq->direct) {
+ case i2c_reg_write:
+ ret = m4sensorhub_reg_write(m4sensorhub,
+ i2c_reg_seq->reg,
+ m4dlm_i2c_reg_seq_getptr(\
+ i2c_reg_seq->reg, dl_packet),
+ m4sh_no_mask);
+ break;
+ case i2c_reg_wait:
+ /*Wait for IRQ answered*/
+ wait_m4_cmd_executed();
+ /*fallback to read status*/
+ case i2c_reg_read:
+ ret = m4sensorhub_reg_read(m4sensorhub,
+ i2c_reg_seq->reg,
+ m4dlm_i2c_reg_seq_getptr(\
+ i2c_reg_seq->reg, dl_packet)
+ );
+ break;
+ default:
+ /*should be fault*/
+ KDEBUG(M4SH_ERROR, "%s: Invaild I2C direct %d\n", \
+ __func__, i2c_reg_seq->direct);
+ return -ENOEXEC;
+ }
+ if (ret != m4sensorhub_reg_getsize(\
+ m4sensorhub, i2c_reg_seq->reg)) {
+ KDEBUG(M4SH_ERROR, "%s: Process I2C [%d-%d] failed!\n",
+ __func__, i2c_reg_seq->direct,
+ i2c_reg_seq->reg);
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
+static long download_client_ioctl(
+ struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+
+ struct download_client *download_data = filp->private_data;
+ struct i2c_reg_sequence *i2c_req_seq = NULL;
+ int ret = -EINVAL, retry;
+ static struct m4sh_download_packet packet;
+ static unsigned short packet_size;
+
+ switch (cmd) {
+ case M4_SENSOR_IOCTL_DL_SEND_PACKET:
+ if (copy_from_user(&packet, argp, M4_SENSOR_DL_MIN_INPUT_SIZE))
+ return -EFAULT;
+
+ KDEBUG(M4SH_INFO, "%s cmd = %d\n", __func__, packet.command);
+
+ switch (packet.command) {
+ case M4_SENSOR_DL_CMD_GET_CHECKSUM:
+ i2c_req_seq = seq_m4dlm_get_checksum;
+ break;
+ case M4_SENSOR_DL_CMD_OPEN_FILE:
+ packet_size = 0;
+ i2c_req_seq = seq_m4dlm_open_file;
+ break;
+ case M4_SENSOR_DL_CMD_DELETE_FILE:
+ i2c_req_seq = seq_m4dlm_delete_file;
+ break;
+ case M4_SENSOR_DL_CMD_CLOSE_FILE:
+ i2c_req_seq = seq_m4dlm_close_file;
+ break;
+ case M4_SENSOR_DL_CMD_WRITE_FILE:
+ if (!packet.size || \
+ (packet.size > M4_SENSOR_DL_MAX_PACKET_SIZE)) {
+ packet.status = M4_SENSOR_DL_ERROR_INVALID_SIZE;
+ /*we only copy packet data before filename*/
+ if (copy_to_user(argp, &packet, \
+ M4_SENSOR_DL_MAX_RET_SIZE))
+ return -EFAULT;
+ return 0;
+ }
+ if (copy_from_user(&packet, argp, sizeof(packet)))
+ return -EFAULT;
+ if (packet.size != packet_size) {
+ i2c_req_seq = seq_m4dlm_write_size_file;
+ packet_size = packet.size;
+ } else
+ i2c_req_seq = seq_m4dlm_write_file;
+ break;
+ default:
+ /*should be wrong command received*/
+ KDEBUG(M4SH_ERROR, "%s Invaild packet cmd %d\n", \
+ __func__, packet.command);
+ return -EINVAL;
+ }
+ for (retry = 0; retry++ < M4_SENSOR_DL_MAX_RETRY_CNT; ) {
+ ret = m4dlm_i2c_reg_seq_process(\
+ download_data->m4sensorhub,
+ &packet, i2c_req_seq);
+ /*only retry if M4 has internal error*/
+ if (!ret) {
+ switch (packet.status) {
+ case M4_SENSOR_DL_ERROR_SEND_CMD:
+ case M4_SENSOR_DL_ERROR_DATA_CHECKSUM:
+ /*something wrong and we need retry*/
+ KDEBUG(M4SH_ERROR, \
+ "Tried %d times for packet cmd %d\n", \
+ retry, packet.command);
+ continue;
+ }
+ }
+ break; /*exit retry loop*/
+ }
+ if (!ret) {
+ /*we only copy packet data before filename for return*/
+ if (copy_to_user(argp, &packet, \
+ M4_SENSOR_DL_MAX_RET_SIZE))
+ return -EFAULT;
+ }
+ break;
+ default:
+ KDEBUG(M4SH_ERROR, "%s Invaild ioctl cmd %d\n", __func__, cmd);
+ break;
+ }
+ return ret;
+}
+
+static ssize_t download_get_loglevel(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int loglevel;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct download_client *download_client_data =
+ platform_get_drvdata(pdev);
+
+ m4sensorhub_reg_read(download_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&loglevel);
+ loglevel = get_log_level(loglevel, DOWNLOAD_MASK_BIT_1);
+ return sprintf(buf, "%d\n", loglevel);
+}
+
+static ssize_t download_set_loglevel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned long level;
+ unsigned int mask = 0;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct download_client *download_client_data =
+ platform_get_drvdata(pdev);
+
+ if ((strict_strtoul(buf, 10, &level)) < 0)
+ return -EINVAL;
+ if (level > M4_MAX_LOG_LEVEL) {
+ KDEBUG(M4SH_ERROR, " Invalid log level - %d\n", (int)level);
+ return -EINVAL;
+ }
+ mask = (1 << DOWNLOAD_MASK_BIT_1) | (1 << DOWNLOAD_MASK_BIT_2);
+ level = (level << DOWNLOAD_MASK_BIT_1);
+ return m4sensorhub_reg_write(download_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&level, (unsigned char *)&mask);
+}
+
+static DEVICE_ATTR(LogLevel, 0664, \
+ download_get_loglevel, download_set_loglevel);
+
+static const struct file_operations download_client_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = download_client_ioctl,
+ .open = download_client_open,
+ .release = download_client_close,
+};
+
+static struct miscdevice download_client_miscdrv = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = DOWNLOAD_CLIENT_DRIVER_NAME,
+ .fops = &download_client_fops,
+};
+
+static int download_client_probe(struct platform_device *pdev)
+{
+ int ret = -1;
+ struct download_client *download_client_data;
+ struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata();
+
+ if (!m4sensorhub) {
+ printk(KERN_WARNING "m4sensorhub is null\n");
+ return -EFAULT;
+ }
+
+ download_client_data =
+ kzalloc(sizeof(*download_client_data), GFP_KERNEL);
+ if (!download_client_data)
+ return -ENOMEM;
+
+ download_client_data->m4sensorhub = m4sensorhub;
+ platform_set_drvdata(pdev, download_client_data);
+
+ ret = misc_register(&download_client_miscdrv);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering %s driver\n",
+ DOWNLOAD_CLIENT_DRIVER_NAME);
+ goto free_memory;
+ }
+ misc_download_data = download_client_data;
+ ret = m4sensorhub_irq_register(m4sensorhub, M4SH_IRQ_DLCMD_RESP_READY,
+ m4_handle_download_irq,
+ download_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering int %d (%d)\n",
+ M4SH_IRQ_DLCMD_RESP_READY, ret);
+ goto unregister_misc_device;
+ }
+
+ ret = m4sensorhub_irq_enable(m4sensorhub, M4SH_IRQ_DLCMD_RESP_READY);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error enable irq %d (%d)\n",
+ M4SH_IRQ_DLCMD_RESP_READY, ret);
+ goto unregister_irq;
+ }
+
+ if (device_create_file(&pdev->dev, &dev_attr_LogLevel)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n",
+ DOWNLOAD_CLIENT_DRIVER_NAME);
+ ret = -1;
+ goto disable_irq;
+ }
+
+ init_waitqueue_head(&download_wq);
+ atomic_set(&m4_dlcmd_resp_ready, false);
+ atomic_set(&download_client_entry, 0);
+
+ KDEBUG(M4SH_INFO, "Initialized %s driver\n",
+ DOWNLOAD_CLIENT_DRIVER_NAME);
+ return 0;
+
+disable_irq:
+ m4sensorhub_irq_disable(m4sensorhub, M4SH_IRQ_DLCMD_RESP_READY);
+unregister_irq:
+ m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_DLCMD_RESP_READY);
+unregister_misc_device:
+ misc_download_data = NULL;
+ misc_deregister(&download_client_miscdrv);
+free_memory:
+ platform_set_drvdata(pdev, NULL);
+ download_client_data->m4sensorhub = NULL;
+ kfree(download_client_data);
+ download_client_data = NULL;
+ return ret;
+}
+
+static int __exit download_client_remove(struct platform_device *pdev)
+{
+ struct download_client *download_client_data =
+ platform_get_drvdata(pdev);
+
+ device_remove_file(&pdev->dev, &dev_attr_LogLevel);
+ m4sensorhub_irq_disable(download_client_data->m4sensorhub,
+ M4SH_IRQ_DLCMD_RESP_READY);
+ m4sensorhub_irq_unregister(download_client_data->m4sensorhub,
+ M4SH_IRQ_DLCMD_RESP_READY);
+ misc_download_data = NULL;
+ misc_deregister(&download_client_miscdrv);
+ platform_set_drvdata(pdev, NULL);
+ download_client_data->m4sensorhub = NULL;
+ kfree(download_client_data);
+ download_client_data = NULL;
+ return 0;
+}
+
+static void download_client_shutdown(struct platform_device *pdev)
+{
+}
+
+#ifdef CONFIG_PM
+
+static int download_client_suspend(struct platform_device *pdev,
+ pm_message_t message)
+{
+ return 0;
+}
+
+static int download_client_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+
+#else
+#define download_client_suspend NULL
+#define download_client_resume NULL
+#endif
+
+static struct of_device_id m4download_match_tbl[] = {
+ { .compatible = "mot,m4download" },
+ {},
+};
+
+static struct platform_driver download_client_driver = {
+ .probe = download_client_probe,
+ .remove = __exit_p(download_client_remove),
+ .shutdown = download_client_shutdown,
+ .suspend = download_client_suspend,
+ .resume = download_client_resume,
+ .driver = {
+ .name = DOWNLOAD_CLIENT_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(m4download_match_tbl),
+ },
+};
+
+static int __init download_client_init(void)
+{
+ return platform_driver_register(&download_client_driver);
+}
+
+static void __exit download_client_exit(void)
+{
+ platform_driver_unregister(&download_client_driver);
+}
+
+module_init(download_client_init);
+module_exit(download_client_exit);
+
+MODULE_ALIAS("platform:download_client");
+MODULE_DESCRIPTION("M4 Sensor Hub driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/misc/m4sensorhub_gesture.c b/drivers/misc/m4sensorhub_gesture.c
new file mode 100644
index 00000000000..b067be71d24
--- /dev/null
+++ b/drivers/misc/m4sensorhub_gesture.c
@@ -0,0 +1,415 @@
+/*
+ * 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/m4sensorhub.h>
+#include <linux/m4sensorhub_client_ioctl.h>
+#include <linux/uaccess.h>
+#include <linux/m4sensorhub/MemMapGesture.h>
+#include <linux/slab.h>
+
+#define GESTURE_CLIENT_DRIVER_NAME "m4sensorhub_gesture"
+
+struct gesture_client {
+ struct m4sensorhub_data *m4sensorhub;
+ struct input_dev *input_dev;
+ struct memMapGesture gesture_data;
+};
+
+static bool read_gesture_value(struct gesture_client *list,
+ signed char *value,
+ eGestureType gesture)
+{
+ if (list->gesture_data.gesture1 == gesture) {
+ *value = list->gesture_data.value1;
+ return true;
+ } else if (list->gesture_data.gesture2 == gesture) {
+ *value = list->gesture_data.value2;
+ return true;
+ } else if (list->gesture_data.gesture3 == gesture) {
+ *value = list->gesture_data.value3;
+ return true;
+ } else {
+ *value = 0;
+ return false;
+ }
+}
+
+static struct gesture_client *misc_gesture_data;
+
+static int gesture_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_gesture_data;
+
+ return 0;
+}
+
+static int gesture_client_close(struct inode *inode, struct file *file)
+{
+ KDEBUG(M4SH_DEBUG, "gesture_client in %s\n", __func__);
+ return 0;
+}
+
+static char m4_read_gesture_data(struct gesture_client *gesture_client_data)
+{
+ int ret;
+ struct memMapGesture *gdata = &(gesture_client_data->gesture_data);
+
+ ret = m4sensorhub_reg_read_n(gesture_client_data->m4sensorhub,
+ M4SH_REG_GESTURE_VERSION,
+ (char *)gdata,
+ sizeof(gesture_client_data->gesture_data));
+
+ if (ret != sizeof(gesture_client_data->gesture_data))
+ goto ERR;
+
+ KDEBUG(M4SH_DEBUG, "Gesture1 = %d, gesture2 = %d, gesture3 = %d\n",
+ gdata->gesture1, gdata->gesture2, gdata->gesture3);
+ KDEBUG(M4SH_DEBUG, "Confidence1 = %d, confidence2 = %d, confidence3 = %d\n",
+ gdata->confidence1, gdata->confidence2, gdata->confidence3);
+ KDEBUG(M4SH_DEBUG, "Value1 = %d, value2 = %d, value3 = %d\n",
+ gdata->value1, gdata->value2, gdata->value3);
+ return 0;
+ERR:
+ KDEBUG(M4SH_ERROR, "Gesture read failed\n");
+ return -1;
+}
+
+static void m4_handle_gesture_irq(enum m4sensorhub_irqs int_event,
+ void *gesture_data)
+{
+ signed char value;
+ /*Trigger broadcast of display gesture intent*/
+ struct gesture_client *gesture_client_data =
+ (struct gesture_client *)gesture_data;
+
+ struct memMapGesture *gdata = &(gesture_client_data->gesture_data);
+
+ if (m4_read_gesture_data(gesture_client_data) < 0) {
+ KDEBUG(M4SH_ERROR, "m4_read_gesture_data returned \
+ error %s\n", __func__);
+ return;
+ }
+
+ if (read_gesture_value(gesture_client_data, &value, GESTURE_TILT_SCROLL)) {
+ input_event(gesture_client_data->input_dev, EV_ABS,
+ ABS_TILTSCROLL, value);
+ } else {
+ if (read_gesture_value(gesture_client_data, &value, GESTURE_WRIST_ROTATE)) {
+ /* send event to stop scrolling for wrist rotate */
+ input_event(gesture_client_data->input_dev, EV_ABS,
+ ABS_TILTSCROLL, 0);
+ }
+ }
+
+ input_event(gesture_client_data->input_dev, EV_MSC,
+ MSC_GESTURE1, gdata->gesture1);
+ input_event(gesture_client_data->input_dev, EV_MSC,
+ MSC_GESTURE2, gdata->gesture2);
+ input_event(gesture_client_data->input_dev, EV_MSC,
+ MSC_GESTURE3, gdata->gesture3);
+ input_event(gesture_client_data->input_dev, EV_MSC,
+ MSC_GESTURE_CONFIDENCE1, gdata->confidence1);
+ input_event(gesture_client_data->input_dev, EV_MSC,
+ MSC_GESTURE_CONFIDENCE2, gdata->confidence2);
+ input_event(gesture_client_data->input_dev, EV_MSC,
+ MSC_GESTURE_CONFIDENCE3, gdata->confidence3);
+ input_event(gesture_client_data->input_dev, EV_MSC,
+ MSC_GESTURE_VALUE1, gdata->value1);
+ input_event(gesture_client_data->input_dev, EV_MSC,
+ MSC_GESTURE_VALUE2, gdata->value2);
+ input_event(gesture_client_data->input_dev, EV_MSC,
+ MSC_GESTURE_VALUE3, gdata->value3);
+
+ input_sync(gesture_client_data->input_dev);
+
+}
+
+/*
+ * Handle commands from user-space.
+ */
+static long gesture_client_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ void __user *argp = (void __user *)arg;
+ struct gesture_client *gesture_client_data = filp->private_data;
+ unsigned char byte;
+
+ switch (cmd) {
+ case M4_SENSOR_IOCTL_SET_SCREEN_ON_GESTURE_STATUS:
+ /* TODO
+ Turn on/off the gesture feature on M4 */
+ break;
+ case M4_SENSOR_IOCTL_SET_SCREEN_STATUS:
+ if (copy_from_user(&byte, argp, sizeof(byte))) {
+ KDEBUG(M4SH_ERROR, "Copy frm usr err:screen status\n");
+ ret = -EFAULT;
+ break;
+ }
+ /* validate data */
+ if (byte > 1) {
+ KDEBUG(M4SH_DEBUG, "Invalid screen status=0x%x", byte);
+ ret = -EINVAL;
+ break;
+ }
+ KDEBUG(M4SH_DEBUG, "Screen status set to = 0x%x", byte);
+ ret = m4sensorhub_reg_write_1byte(
+ gesture_client_data->m4sensorhub,
+ M4SH_REG_USERSETTINGS_SCREENSTATUS, byte, 0xFF);
+ if (ret != 1)
+ KDEBUG(M4SH_ERROR, "Error writing screen status\n");
+ break;
+ default:
+ KDEBUG(M4SH_ERROR, "Invalid IOCTL Command in %s\n", __func__);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static ssize_t m4_gesture_status(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct gesture_client *gesture_client_data = platform_get_drvdata(pdev);
+
+ if (m4_read_gesture_data(gesture_client_data) < 0)
+ KDEBUG(M4SH_ERROR, "%s: Read gesture data failed \n", __func__);
+ KDEBUG(M4SH_DEBUG, "%s:Gesture1,2,3 = %d %d %d conf1,2,3 = %d %d %d value1,2,3 = %d %d %d\n",
+ __func__, gesture_client_data->gesture_data.gesture1,
+ gesture_client_data->gesture_data.gesture2,
+ gesture_client_data->gesture_data.gesture3,
+ gesture_client_data->gesture_data.confidence1,
+ gesture_client_data->gesture_data.confidence2,
+ gesture_client_data->gesture_data.confidence3,
+ gesture_client_data->gesture_data.value1,
+ gesture_client_data->gesture_data.value2,
+ gesture_client_data->gesture_data.value3);
+
+ return sprintf(buf, "gesture1,2,3=%d %d %d,confidence1,2,3=%d %d %d value1,2,3 = %d %d %d\n",
+ gesture_client_data->gesture_data.gesture1,
+ gesture_client_data->gesture_data.gesture2,
+ gesture_client_data->gesture_data.gesture3,
+ gesture_client_data->gesture_data.confidence1,
+ gesture_client_data->gesture_data.confidence2,
+ gesture_client_data->gesture_data.confidence3,
+ gesture_client_data->gesture_data.value1,
+ gesture_client_data->gesture_data.value2,
+ gesture_client_data->gesture_data.value3);
+}
+
+static DEVICE_ATTR(gesture_status, 0444, m4_gesture_status, NULL);
+
+static const struct file_operations gesture_client_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = gesture_client_ioctl,
+ .open = gesture_client_open,
+ .release = gesture_client_close,
+};
+
+static struct miscdevice gesture_client_miscdrv = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = GESTURE_CLIENT_DRIVER_NAME,
+ .fops = &gesture_client_fops,
+};
+
+static int gesture_client_probe(struct platform_device *pdev)
+{
+ int ret = -1;
+ struct gesture_client *gesture_client_data;
+ struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata();
+
+ if (!m4sensorhub)
+ return -EFAULT;
+
+ gesture_client_data = kzalloc(sizeof(*gesture_client_data),
+ GFP_KERNEL);
+ if (!gesture_client_data)
+ return -ENOMEM;
+
+ gesture_client_data->m4sensorhub = m4sensorhub;
+ platform_set_drvdata(pdev, gesture_client_data);
+
+ gesture_client_data->input_dev = input_allocate_device();
+ if (!gesture_client_data->input_dev) {
+ ret = -ENOMEM;
+ KDEBUG(M4SH_ERROR, "%s: input device allocate failed: %d\n",
+ __func__, ret);
+ goto free_mem;
+ }
+
+ gesture_client_data->input_dev->name = GESTURE_CLIENT_DRIVER_NAME;
+ set_bit(EV_MSC, gesture_client_data->input_dev->evbit);
+ set_bit(MSC_GESTURE1, gesture_client_data->input_dev->mscbit);
+ set_bit(MSC_GESTURE2, gesture_client_data->input_dev->mscbit);
+ set_bit(MSC_GESTURE3, gesture_client_data->input_dev->mscbit);
+ set_bit(MSC_GESTURE_CONFIDENCE1, gesture_client_data->input_dev->mscbit);
+ set_bit(MSC_GESTURE_CONFIDENCE2, gesture_client_data->input_dev->mscbit);
+ set_bit(MSC_GESTURE_CONFIDENCE3, gesture_client_data->input_dev->mscbit);
+ set_bit(MSC_GESTURE_VALUE1, gesture_client_data->input_dev->mscbit);
+ set_bit(MSC_GESTURE_VALUE2, gesture_client_data->input_dev->mscbit);
+ set_bit(MSC_GESTURE_VALUE3, gesture_client_data->input_dev->mscbit);
+
+ set_bit(EV_ABS, gesture_client_data->input_dev->evbit);
+ set_bit(ABS_TILTSCROLL, gesture_client_data->input_dev->absbit);
+
+ if (input_register_device(gesture_client_data->input_dev)) {
+ KDEBUG(M4SH_ERROR, "%s: input device register failed\n",
+ __func__);
+ input_free_device(gesture_client_data->input_dev);
+ goto free_mem;
+ }
+
+ ret = misc_register(&gesture_client_miscdrv);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering %s driver\n", __func__);
+ goto unregister_input_device;
+ }
+ misc_gesture_data = gesture_client_data;
+ ret = m4sensorhub_irq_register(m4sensorhub, M4SH_IRQ_GESTURE_DETECTED,
+ m4_handle_gesture_irq,
+ gesture_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering int %d (%d)\n",
+ M4SH_IRQ_GESTURE_DETECTED, ret);
+ goto unregister_misc_device;
+ }
+ ret = m4sensorhub_irq_enable(m4sensorhub, M4SH_IRQ_GESTURE_DETECTED);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error enabling int %d (%d)\n",
+ M4SH_IRQ_GESTURE_DETECTED, ret);
+ goto unregister_display_gesture_irq;
+ }
+ if (device_create_file(&pdev->dev, &dev_attr_gesture_status)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n", __func__);
+ ret = -1;
+ goto disable_display_gesture_irq;
+ }
+ KDEBUG(M4SH_INFO, "Initialized %s driver\n", __func__);
+ return 0;
+
+disable_display_gesture_irq:
+ m4sensorhub_irq_disable(m4sensorhub, M4SH_IRQ_GESTURE_DETECTED);
+unregister_display_gesture_irq:
+ m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_GESTURE_DETECTED);
+unregister_misc_device:
+ misc_gesture_data = NULL;
+ misc_deregister(&gesture_client_miscdrv);
+unregister_input_device:
+ input_unregister_device(gesture_client_data->input_dev);
+free_mem:
+ platform_set_drvdata(pdev, NULL);
+ gesture_client_data->m4sensorhub = NULL;
+ kfree(gesture_client_data);
+ gesture_client_data = NULL;
+ return ret;
+}
+
+static int __exit gesture_client_remove(struct platform_device *pdev)
+{
+ struct gesture_client *gesture_client_data =
+ platform_get_drvdata(pdev);
+
+ device_remove_file(&pdev->dev, &dev_attr_gesture_status);
+ m4sensorhub_irq_disable(gesture_client_data->m4sensorhub,
+ M4SH_IRQ_GESTURE_DETECTED);
+ m4sensorhub_irq_unregister(gesture_client_data->m4sensorhub,
+ M4SH_IRQ_GESTURE_DETECTED);
+ misc_gesture_data = NULL;
+ misc_deregister(&gesture_client_miscdrv);
+ input_unregister_device(gesture_client_data->input_dev);
+ platform_set_drvdata(pdev, NULL);
+ gesture_client_data->m4sensorhub = NULL;
+ kfree(gesture_client_data);
+ gesture_client_data = NULL;
+ return 0;
+}
+
+static void gesture_client_shutdown(struct platform_device *pdev)
+{
+ return;
+}
+#ifdef CONFIG_PM
+static int gesture_client_suspend(struct platform_device *pdev,
+ pm_message_t message)
+{
+ return 0;
+}
+
+static int gesture_client_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#else
+#define gesture_client_suspend NULL
+#define gesture_client_resume NULL
+#endif
+
+static struct of_device_id m4gesture_match_tbl[] = {
+ { .compatible = "mot,m4gesture" },
+ {},
+};
+
+static struct platform_driver gesture_client_driver = {
+ .probe = gesture_client_probe,
+ .remove = __exit_p(gesture_client_remove),
+ .shutdown = gesture_client_shutdown,
+ .suspend = gesture_client_suspend,
+ .resume = gesture_client_resume,
+ .driver = {
+ .name = GESTURE_CLIENT_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(m4gesture_match_tbl),
+ },
+};
+
+static int __init gesture_client_init(void)
+{
+ return platform_driver_register(&gesture_client_driver);
+}
+
+static void __exit gesture_client_exit(void)
+{
+ platform_driver_unregister(&gesture_client_driver);
+}
+
+module_init(gesture_client_init);
+module_exit(gesture_client_exit);
+
+MODULE_ALIAS("platform:gesture_client");
+MODULE_DESCRIPTION("M4 Sensor Hub Gesture client driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/misc/m4sensorhub_mpu9150.c b/drivers/misc/m4sensorhub_mpu9150.c
new file mode 100644
index 00000000000..55195f4d955
--- /dev/null
+++ b/drivers/misc/m4sensorhub_mpu9150.c
@@ -0,0 +1,1243 @@
+/*
+ * 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/m4sensorhub.h>
+#include <linux/m4sensorhub_client_ioctl.h>
+#include <linux/m4sensorhub/MemMapGyroSensor.h>
+#include <linux/m4sensorhub/MemMapAccelSensor.h>
+#include <linux/m4sensorhub/MemMapCompassSensor.h>
+#include <linux/m4sensorhub/MemMapFusionSensor.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+
+#ifdef CONFIG_DEBUG_FS
+#define MPU9150_DEBUG 1
+#else
+#define MPU9150_DEBUG 0
+#endif
+
+#define MPU9150_CLIENT_DRIVER_NAME "m4sensorhub_mpu9150"
+#define SENSOR_IRQ_ENABLE 1
+#define SENSOR_IRQ_DISABLE 0
+
+struct mpu9150_accel_data {
+ int x;
+ int y;
+ int z;
+};
+struct mpu9150_gyro_data {
+ int rx;
+ int ry;
+ int rz;
+};
+struct mpu9150_compass_data {
+ int cx;
+ int cy;
+ int cz;
+ int ca;
+};
+struct mpu9150_accel_local_data {
+ int lx;
+ int ly;
+ int lz;
+};
+struct mpu9150_accel_world_data {
+ int wx;
+ int wy;
+ int wz;
+};
+struct mpu9150_euler_data {
+ int roll;
+ int pitch;
+ int yaw;
+};
+struct mpu9150_heading_data {
+ int heading;
+ int accuracy;
+};
+enum mpu9150_sensor {
+ TYPE_GYRO,
+ TYPE_COMPASS,
+ TYPE_ACCEL,
+ TYPE_FUSION,
+
+ NUM_TYPES, /* Leave as last element */
+} sensor;
+
+struct mpu9150_client {
+ struct m4sensorhub_data *m4sensorhub;
+ struct input_dev *input_dev;
+ signed short samplerate[NUM_TYPES];
+ struct mpu9150_accel_data accel_data;
+ struct mpu9150_gyro_data gyro_data;
+ struct mpu9150_compass_data compass_data;
+ struct mpu9150_accel_local_data accel_local_data;
+ struct mpu9150_accel_world_data accel_world_data;
+ struct mpu9150_euler_data euler_data;
+ struct mpu9150_heading_data heading_data;
+};
+
+struct mpu9150_client *misc_mpu9150_data;
+static int mpu9150_irq_enable_disable(struct mpu9150_client *mpu9150_client_data,
+ enum mpu9150_sensor type, int flag);
+
+static int mpu9150_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_mpu9150_data;
+
+ return 0;
+}
+
+static int mpu9150_client_close(struct inode *inode, struct file *file)
+{
+ KDEBUG(M4SH_DEBUG, "mpu9150_client in %s\n", __func__);
+ return 0;
+}
+
+static void m4_report_mpu9150_inputevent(
+ struct mpu9150_client *mpu9150_client_data,
+ enum mpu9150_sensor type)
+{
+ switch (type) {
+ case TYPE_GYRO:
+ input_report_rel(mpu9150_client_data->input_dev, REL_RX,
+ mpu9150_client_data->gyro_data.rx);
+ input_report_rel(mpu9150_client_data->input_dev, REL_RY,
+ mpu9150_client_data->gyro_data.ry);
+ input_report_rel(mpu9150_client_data->input_dev, REL_RZ,
+ mpu9150_client_data->gyro_data.rz);
+ input_sync(mpu9150_client_data->input_dev);
+ break;
+ case TYPE_ACCEL:
+ input_report_abs(mpu9150_client_data->input_dev, ABS_X,
+ mpu9150_client_data->accel_data.x);
+ input_report_abs(mpu9150_client_data->input_dev, ABS_Y,
+ mpu9150_client_data->accel_data.y);
+ input_report_abs(mpu9150_client_data->input_dev, ABS_Z,
+ mpu9150_client_data->accel_data.z);
+ input_sync(mpu9150_client_data->input_dev);
+ break;
+ case TYPE_COMPASS:
+ input_report_abs(mpu9150_client_data->input_dev, ABS_COMPASS_X,
+ mpu9150_client_data->compass_data.cx);
+ input_report_abs(mpu9150_client_data->input_dev, ABS_COMPASS_Y,
+ mpu9150_client_data->compass_data.cy);
+ input_report_abs(mpu9150_client_data->input_dev, ABS_COMPASS_Z,
+ mpu9150_client_data->compass_data.cz);
+ input_report_abs(mpu9150_client_data->input_dev,
+ ABS_COMPASS_ACCURACY,
+ mpu9150_client_data->compass_data.ca);
+ input_sync(mpu9150_client_data->input_dev);
+ break;
+ case TYPE_FUSION:
+ input_report_rel(mpu9150_client_data->input_dev, REL_X,
+ mpu9150_client_data->accel_data.x);
+ input_report_rel(mpu9150_client_data->input_dev, REL_Y,
+ mpu9150_client_data->accel_data.y);
+ input_report_rel(mpu9150_client_data->input_dev, REL_Z,
+ mpu9150_client_data->accel_data.z);
+ input_report_rel(mpu9150_client_data->input_dev, REL_GX,
+ mpu9150_client_data->gyro_data.rx);
+ input_report_rel(mpu9150_client_data->input_dev, REL_GY,
+ mpu9150_client_data->gyro_data.ry);
+ input_report_rel(mpu9150_client_data->input_dev, REL_GZ,
+ mpu9150_client_data->gyro_data.rz);
+ input_report_rel(mpu9150_client_data->input_dev, REL_LX,
+ mpu9150_client_data->accel_local_data.lx);
+ input_report_rel(mpu9150_client_data->input_dev, REL_LY,
+ mpu9150_client_data->accel_local_data.ly);
+ input_report_rel(mpu9150_client_data->input_dev, REL_LZ,
+ mpu9150_client_data->accel_local_data.lz);
+ input_report_rel(mpu9150_client_data->input_dev, REL_WX,
+ mpu9150_client_data->accel_world_data.wx);
+ input_report_rel(mpu9150_client_data->input_dev, REL_WY,
+ mpu9150_client_data->accel_world_data.wy);
+ input_report_rel(mpu9150_client_data->input_dev, REL_WZ,
+ mpu9150_client_data->accel_world_data.wz);
+ input_report_rel(mpu9150_client_data->input_dev, REL_ROLL,
+ mpu9150_client_data->euler_data.roll);
+ input_report_rel(mpu9150_client_data->input_dev, REL_PITCH,
+ mpu9150_client_data->euler_data.pitch);
+ input_report_rel(mpu9150_client_data->input_dev, REL_YAW,
+ mpu9150_client_data->euler_data.yaw);
+ input_report_rel(mpu9150_client_data->input_dev, REL_HEADING,
+ mpu9150_client_data->heading_data.heading);
+ input_report_rel(mpu9150_client_data->input_dev,
+ REL_HEADING_ACCURACY,
+ mpu9150_client_data->heading_data.accuracy);
+ input_sync(mpu9150_client_data->input_dev);
+ break;
+ default:
+ break;
+ }
+}
+
+/* TO DO
+***** implement the delay functionality when M4 changes will be ready
+*/
+static void m4_set_mpu9150_delay(struct mpu9150_client *mpu9150_client_data,
+ int delay, enum mpu9150_sensor type)
+{
+ if (delay != mpu9150_client_data->samplerate[type]) {
+ switch (type) {
+ case TYPE_GYRO:
+ m4sensorhub_reg_write(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_GYRO_SAMPLERATE, (char *)&delay, m4sh_no_mask);
+ break;
+ case TYPE_ACCEL:
+ m4sensorhub_reg_write(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_ACCEL_SAMPLERATE, (char *)&delay, m4sh_no_mask);
+ break;
+ case TYPE_COMPASS:
+ m4sensorhub_reg_write(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_COMPASS_SAMPLERATE, (char *)&delay, m4sh_no_mask);
+ break;
+ case TYPE_FUSION:
+ m4sensorhub_reg_write(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_FUSION_SAMPLERATE, (char *)&delay, m4sh_no_mask);
+ break;
+ default:
+ return;
+ break;
+ }
+ KDEBUG(M4SH_DEBUG, "%s() updating samplerate for type %d from"
+ " %d to %d\n", __func__, type,
+ mpu9150_client_data->samplerate[type],
+ delay);
+
+ mpu9150_client_data->samplerate[type] = delay;
+ }
+}
+
+static void m4_read_mpu9150_data(struct mpu9150_client *mpu9150_client_data,
+ enum mpu9150_sensor type)
+{
+ sFusionData fusiondata;
+ sCompassData compassdata;
+ sAccelData acceldata;
+ sGyroData gyrodata;
+
+ switch (type) {
+ case TYPE_GYRO:
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_GYRO_X, (char *)&gyrodata.x);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_GYRO_Y, (char *)&gyrodata.y);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_GYRO_Z, (char *)&gyrodata.z);
+ mpu9150_client_data->gyro_data.rx = gyrodata.x;
+ mpu9150_client_data->gyro_data.ry = gyrodata.y;
+ mpu9150_client_data->gyro_data.rz = gyrodata.z;
+ break;
+ case TYPE_ACCEL:
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_ACCEL_X, (char *)&acceldata.x);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_ACCEL_Y, (char *)&acceldata.y);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_ACCEL_Z, (char *)&acceldata.z);
+ mpu9150_client_data->accel_data.x = acceldata.x;
+ mpu9150_client_data->accel_data.y = acceldata.y;
+ mpu9150_client_data->accel_data.z = acceldata.z;
+ break;
+ case TYPE_COMPASS:
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_COMPASS_X, (char *)&compassdata.x);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_COMPASS_Y, (char *)&compassdata.y);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_COMPASS_Z, (char *)&compassdata.z);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_COMPASS_ACCURACY,
+ (char *)&compassdata.accuracy);
+
+ mpu9150_client_data->compass_data.cx = compassdata.x;
+ mpu9150_client_data->compass_data.cy = compassdata.y;
+ mpu9150_client_data->compass_data.cz = compassdata.z;
+ mpu9150_client_data->compass_data.ca = compassdata.accuracy;
+
+ break;
+ case TYPE_FUSION:
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_ACCEL_X, (char *)&acceldata.x);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_ACCEL_Y, (char *)&acceldata.y);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_ACCEL_Z, (char *)&acceldata.z);
+ mpu9150_client_data->accel_data.x = acceldata.x;
+ mpu9150_client_data->accel_data.y = acceldata.y;
+ mpu9150_client_data->accel_data.z = acceldata.z;
+
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_GYRO_X, (char *)&gyrodata.x);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_GYRO_Y, (char *)&gyrodata.y);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_GYRO_Z, (char *)&gyrodata.z);
+ mpu9150_client_data->gyro_data.rx = gyrodata.x;
+ mpu9150_client_data->gyro_data.ry = gyrodata.y;
+ mpu9150_client_data->gyro_data.rz = gyrodata.z;
+
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_FUSION_LOCALX, (char *)&fusiondata.localX);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_FUSION_LOCALY, (char *)&fusiondata.localY);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_FUSION_LOCALZ, (char *)&fusiondata.localZ);
+ mpu9150_client_data->accel_local_data.lx = fusiondata.localX;
+ mpu9150_client_data->accel_local_data.ly = fusiondata.localY;
+ mpu9150_client_data->accel_local_data.lz = fusiondata.localZ;
+
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_FUSION_WORLDX, (char *)&fusiondata.worldX);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_FUSION_WORLDY, (char *)&fusiondata.worldY);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_FUSION_WORLDZ, (char *)&fusiondata.worldZ);
+ mpu9150_client_data->accel_world_data.wx = fusiondata.worldX;
+ mpu9150_client_data->accel_world_data.wy = fusiondata.worldY;
+ mpu9150_client_data->accel_world_data.wz = fusiondata.worldZ;
+
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_FUSION_EULERPITCH,
+ (char *)&fusiondata.eulerPitch);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_FUSION_EULERROLL,
+ (char *)&fusiondata.eulerRoll);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_FUSION_EULERYAW,
+ (char *)&fusiondata.eulerYaw);
+ mpu9150_client_data->euler_data.pitch = fusiondata.eulerPitch;
+ mpu9150_client_data->euler_data.roll = fusiondata.eulerRoll;
+ mpu9150_client_data->euler_data.yaw = fusiondata.eulerYaw;
+
+
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_FUSION_HEADING,
+ (char *)&fusiondata.heading);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_FUSION_HEADING_ACCURACY,
+ (char *)&fusiondata.heading_accuracy);
+ mpu9150_client_data->heading_data.heading = fusiondata.heading;
+ mpu9150_client_data->heading_data.accuracy =
+ fusiondata.heading_accuracy;
+
+ break;
+ default:
+ break;
+ }
+
+}
+static void m4_handle_mpu9150_gyro_irq(enum m4sensorhub_irqs int_event,
+ void *mpu9150_data)
+{
+ struct mpu9150_client *mpu9150_client_data = mpu9150_data;
+
+ m4_read_mpu9150_data(mpu9150_client_data, TYPE_GYRO);
+ m4_report_mpu9150_inputevent(mpu9150_client_data, TYPE_GYRO);
+}
+
+static void m4_handle_mpu9150_accel_irq(enum m4sensorhub_irqs int_event,
+ void *mpu9150_data)
+{
+ struct mpu9150_client *mpu9150_client_data = mpu9150_data;
+
+ m4_read_mpu9150_data(mpu9150_client_data, TYPE_ACCEL);
+ m4_report_mpu9150_inputevent(mpu9150_client_data, TYPE_ACCEL);
+}
+
+static void m4_handle_mpu9150_compass_irq(enum m4sensorhub_irqs int_event,
+ void *mpu9150_data)
+{
+ struct mpu9150_client *mpu9150_client_data = mpu9150_data;
+
+ m4_read_mpu9150_data(mpu9150_client_data, TYPE_COMPASS);
+ m4_report_mpu9150_inputevent(mpu9150_client_data, TYPE_COMPASS);
+}
+
+static void m4_handle_mpu9150_fusion_irq(enum m4sensorhub_irqs int_event,
+ void *mpu9150_data)
+{
+ struct mpu9150_client *mpu9150_client_data = mpu9150_data;
+
+ m4_read_mpu9150_data(mpu9150_client_data, TYPE_FUSION);
+ m4_report_mpu9150_inputevent(mpu9150_client_data, TYPE_FUSION);
+}
+
+/*
+ * Handle commands from user-space.
+ */
+static long mpu9150_client_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ int delay;
+ static int status;
+ void __user *argp = (void __user *)arg;
+ struct mpu9150_client *mpu9150_client_data = filp->private_data;
+
+ switch (cmd) {
+ 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_GYRO_SET_DELAY:
+ if (copy_from_user(&delay, argp, sizeof(delay)))
+ return -EFAULT;
+ m4_set_mpu9150_delay(mpu9150_client_data, delay, TYPE_GYRO);
+ mpu9150_irq_enable_disable(mpu9150_client_data, TYPE_GYRO,
+ SENSOR_IRQ_ENABLE);
+ break;
+ case M4_SENSOR_IOCTL_COMPASS_SET_DELAY:
+ if (copy_from_user(&delay, argp, sizeof(delay)))
+ return -EFAULT;
+ m4_set_mpu9150_delay(mpu9150_client_data, delay, TYPE_COMPASS);
+ mpu9150_irq_enable_disable(mpu9150_client_data, TYPE_COMPASS,
+ SENSOR_IRQ_ENABLE);
+ break;
+ case M4_SENSOR_IOCTL_ACCEL_SET_DELAY:
+ if (copy_from_user(&delay, argp, sizeof(delay)))
+ return -EFAULT;
+ m4_set_mpu9150_delay(mpu9150_client_data, delay, TYPE_ACCEL);
+ mpu9150_irq_enable_disable(mpu9150_client_data, TYPE_ACCEL,
+ SENSOR_IRQ_ENABLE);
+ break;
+ case M4_SENSOR_IOCTL_FUSION_SET_DELAY:
+ if (copy_from_user(&delay, argp, sizeof(delay)))
+ return -EFAULT;
+ if (delay >= 0)
+ m4_set_mpu9150_delay(mpu9150_client_data, delay, TYPE_FUSION);
+ if (delay)
+ mpu9150_irq_enable_disable(mpu9150_client_data, TYPE_FUSION,
+ SENSOR_IRQ_ENABLE);
+ else
+ mpu9150_irq_enable_disable(mpu9150_client_data, TYPE_FUSION,
+ SENSOR_IRQ_DISABLE);
+ break;
+ default:
+ KDEBUG(M4SH_ERROR, "Invalid IOCTL Command in %s\n", __func__);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+#ifdef MPU9150_DEBUG
+static ssize_t m4_mpu9150_local_x(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : x_local = %d\n",
+ __func__, mpu9150_client_data->accel_local_data.lx);
+ return sprintf(buf, "%d \n", mpu9150_client_data->accel_local_data.lx);
+}
+
+static ssize_t m4_mpu9150_local_y(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : y_local = %d\n",
+ __func__, mpu9150_client_data->accel_local_data.ly);
+ return sprintf(buf, "%d \n", mpu9150_client_data->accel_local_data.ly);
+}
+
+static ssize_t m4_mpu9150_local_z(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : z_local = %d\n",
+ __func__, mpu9150_client_data->accel_local_data.lz);
+ return sprintf(buf, "%d \n", mpu9150_client_data->accel_local_data.lz);
+}
+static ssize_t m4_mpu9150_world_x(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : x_world = %d\n",
+ __func__, mpu9150_client_data->accel_world_data.wx);
+ return sprintf(buf, "%d \n", mpu9150_client_data->accel_world_data.wx);
+}
+
+static ssize_t m4_mpu9150_world_y(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : y_world = %d\n",
+ __func__, mpu9150_client_data->accel_world_data.wy);
+ return sprintf(buf, "%d \n", mpu9150_client_data->accel_world_data.wy);
+}
+
+static ssize_t m4_mpu9150_world_z(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : z_world = %d\n",
+ __func__, mpu9150_client_data->accel_world_data.wz);
+ return sprintf(buf, "%d \n", mpu9150_client_data->accel_world_data.wz);
+}
+static ssize_t m4_mpu9150_pitch(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : pitch = %d\n",
+ __func__, mpu9150_client_data->euler_data.pitch);
+ return sprintf(buf, "%d \n", mpu9150_client_data->euler_data.pitch);
+}
+
+static ssize_t m4_mpu9150_roll(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : roll = %d\n",
+ __func__, mpu9150_client_data->euler_data.roll);
+ return sprintf(buf, "%d \n", mpu9150_client_data->euler_data.roll);
+}
+
+static ssize_t m4_mpu9150_yaw(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : yaw = %d\n",
+ __func__, mpu9150_client_data->euler_data.yaw);
+ return sprintf(buf, "%d \n", mpu9150_client_data->euler_data.yaw);
+}
+
+static ssize_t m4_mpu9150_heading(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : heading = %d\n",
+ __func__, mpu9150_client_data->heading_data.heading);
+ return sprintf(buf, "%d \n", mpu9150_client_data->heading_data.heading);
+}
+
+static ssize_t m4_mpu9150_heading_acc(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : heading_acc = %d\n",
+ __func__, mpu9150_client_data->heading_data.accuracy);
+ return sprintf(buf, "%d\n", mpu9150_client_data->heading_data.accuracy);
+}
+
+static ssize_t m4_mpu9150_x(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : raw x = %d\n",
+ __func__, mpu9150_client_data->accel_data.x);
+ return sprintf(buf, "%d \n", mpu9150_client_data->accel_data.x);
+}
+
+static ssize_t m4_mpu9150_y(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : raw y = %d\n",
+ __func__, mpu9150_client_data->accel_data.y);
+ return sprintf(buf, "%d \n", mpu9150_client_data->accel_data.y);
+}
+
+static ssize_t m4_mpu9150_z(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : raw z = %d\n",
+ __func__, mpu9150_client_data->accel_data.z);
+ return sprintf(buf, "%d \n", mpu9150_client_data->accel_data.z);
+}
+
+static ssize_t m4_mpu9150_cx(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : compass cx = %d\n",
+ __func__, mpu9150_client_data->compass_data.cx);
+ return sprintf(buf, "%d \n", mpu9150_client_data->compass_data.cx);
+}
+
+static ssize_t m4_mpu9150_cy(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : compass cy = %d\n",
+ __func__, mpu9150_client_data->compass_data.cy);
+ return sprintf(buf, "%d \n", mpu9150_client_data->compass_data.cy);
+}
+
+static ssize_t m4_mpu9150_cz(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : compass cz = %d\n",
+ __func__, mpu9150_client_data->compass_data.cz);
+ return sprintf(buf, "%d \n", mpu9150_client_data->compass_data.cz);
+}
+
+static ssize_t m4_mpu9150_ca(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : compass ca = %d\n",
+ __func__, mpu9150_client_data->compass_data.ca);
+ return sprintf(buf, "%d \n", mpu9150_client_data->compass_data.ca);
+}
+
+static ssize_t m4_mpu9150_rx(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : rx = %d\n",
+ __func__, mpu9150_client_data->gyro_data.rx);
+ return sprintf(buf, "%d \n", mpu9150_client_data->gyro_data.rx);
+}
+static ssize_t m4_mpu9150_ry(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : ry = %d\n",
+ __func__, mpu9150_client_data->gyro_data.ry);
+ return sprintf(buf, "%d \n", mpu9150_client_data->gyro_data.ry);
+}
+
+static ssize_t m4_mpu9150_rz(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : rz = %d\n",
+ __func__, mpu9150_client_data->gyro_data.rz);
+ return sprintf(buf, "%d \n", mpu9150_client_data->gyro_data.rz);
+}
+
+static int mpu9150_set_loglevel(struct mpu9150_client *mpu9150_client_data,
+ unsigned long long level, enum mpu9150_sensor type)
+{
+ unsigned long long mask;
+ if (level > M4_MAX_LOG_LEVEL) {
+ KDEBUG(M4SH_ERROR, " Invalid log level - %llu !!!\n", level);
+ return -1;
+ }
+ switch (type) {
+ case TYPE_GYRO:
+ mask = (1ULL << GYRO_MASK_BIT_1) | (1ULL << GYRO_MASK_BIT_2);
+ level = (level << GYRO_MASK_BIT_1);
+ break;
+ case TYPE_ACCEL:
+ mask = (1ULL << ACCEL_MASK_BIT_1) | (1ULL << ACCEL_MASK_BIT_2);
+ level = (level << ACCEL_MASK_BIT_1);
+ break;
+ case TYPE_COMPASS:
+ mask = (1ULL << COMPASS_MASK_BIT_1) | (1ULL << COMPASS_MASK_BIT_2);
+ level = (level << COMPASS_MASK_BIT_1);
+ break;
+ case TYPE_FUSION:
+ mask = (1ULL << FUSION_MASK_BIT_1) | (1ULL << FUSION_MASK_BIT_2);
+ level = (level << FUSION_MASK_BIT_1);
+ break;
+ default:
+ return -1;
+ }
+ return m4sensorhub_reg_write(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&level, (unsigned char *)&mask);
+}
+
+static ssize_t gyro_set_loglevel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned long level;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ if ((strict_strtoul(buf, 10, &level)) < 0)
+ return -1;
+ return mpu9150_set_loglevel(mpu9150_client_data, level, TYPE_GYRO);
+}
+
+static ssize_t gyro_get_loglevel(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned long long loglevel;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&loglevel);
+ loglevel = get_log_level(loglevel, GYRO_MASK_BIT_1);
+ return sprintf(buf, "%llu\n", loglevel);
+}
+
+static ssize_t accel_set_loglevel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned long level;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ if ((strict_strtoul(buf, 10, &level)) < 0)
+ return -1;
+ return mpu9150_set_loglevel(mpu9150_client_data, level, TYPE_ACCEL);
+}
+
+static ssize_t accel_get_loglevel(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int loglevel;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&loglevel);
+ loglevel = get_log_level(loglevel, ACCEL_MASK_BIT_1);
+ return sprintf(buf, "%d\n", loglevel);
+}
+
+static ssize_t compass_set_loglevel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned long level;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ if ((strict_strtoul(buf, 10, &level)) < 0)
+ return -1;
+ return mpu9150_set_loglevel(mpu9150_client_data, level, TYPE_COMPASS);
+}
+
+static ssize_t compass_get_loglevel(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int loglevel;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&loglevel);
+ loglevel = get_log_level(loglevel, COMPASS_MASK_BIT_1);
+ return sprintf(buf, "%d\n", loglevel);
+}
+
+static ssize_t fusion_set_loglevel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned long level;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ if ((strict_strtoul(buf, 10, &level)) < 0)
+ return -1;
+ return mpu9150_set_loglevel(mpu9150_client_data, level, TYPE_FUSION);
+}
+static ssize_t fusion_get_loglevel(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int loglevel;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&loglevel);
+ loglevel = get_log_level(loglevel, FUSION_MASK_BIT_1);
+ return sprintf(buf, "%d\n", loglevel);
+}
+
+static DEVICE_ATTR(x_local, 0444, m4_mpu9150_local_x, NULL);
+static DEVICE_ATTR(y_local, 0444, m4_mpu9150_local_y, NULL);
+static DEVICE_ATTR(z_local, 0444, m4_mpu9150_local_z, NULL);
+static DEVICE_ATTR(x_world, 0444, m4_mpu9150_world_x, NULL);
+static DEVICE_ATTR(y_world, 0444, m4_mpu9150_world_y, NULL);
+static DEVICE_ATTR(z_world, 0444, m4_mpu9150_world_z, NULL);
+static DEVICE_ATTR(pitch, 0444, m4_mpu9150_pitch, NULL);
+static DEVICE_ATTR(roll, 0444, m4_mpu9150_roll, NULL);
+static DEVICE_ATTR(yaw, 0444, m4_mpu9150_yaw, NULL);
+static DEVICE_ATTR(heading, 0444, m4_mpu9150_heading, NULL);
+static DEVICE_ATTR(heading_acc, 0444, m4_mpu9150_heading_acc, NULL);
+static DEVICE_ATTR(raw_x, 0444, m4_mpu9150_x, NULL);
+static DEVICE_ATTR(raw_y, 0444, m4_mpu9150_y, NULL);
+static DEVICE_ATTR(raw_z, 0444, m4_mpu9150_z, NULL);
+static DEVICE_ATTR(compass_cx, 0444, m4_mpu9150_cx, NULL);
+static DEVICE_ATTR(compass_cy, 0444, m4_mpu9150_cy, NULL);
+static DEVICE_ATTR(compass_cz, 0444, m4_mpu9150_cz, NULL);
+static DEVICE_ATTR(compass_ca, 0444, m4_mpu9150_ca, NULL);
+static DEVICE_ATTR(rx, 0444, m4_mpu9150_rx, NULL);
+static DEVICE_ATTR(ry, 0444, m4_mpu9150_ry, NULL);
+static DEVICE_ATTR(rz, 0444, m4_mpu9150_rz, NULL);
+static DEVICE_ATTR(gyroLogLevel, 0644, gyro_get_loglevel,
+ gyro_set_loglevel);
+static DEVICE_ATTR(accelLogLevel, 0644, accel_get_loglevel,
+ accel_set_loglevel);
+static DEVICE_ATTR(compassLogLevel, 0644, compass_get_loglevel,
+ compass_set_loglevel);
+static DEVICE_ATTR(fusionLogLevel, 0644, fusion_get_loglevel,
+ fusion_set_loglevel);
+
+static struct attribute *mpu9150_attributes[] = {
+ &dev_attr_x_local.attr,
+ &dev_attr_y_local.attr,
+ &dev_attr_z_local.attr,
+ &dev_attr_x_world.attr,
+ &dev_attr_y_world.attr,
+ &dev_attr_z_world.attr,
+ &dev_attr_pitch.attr,
+ &dev_attr_roll.attr,
+ &dev_attr_yaw.attr,
+ &dev_attr_heading.attr,
+ &dev_attr_heading_acc.attr,
+ &dev_attr_raw_x.attr,
+ &dev_attr_raw_y.attr,
+ &dev_attr_raw_z.attr,
+ &dev_attr_compass_cx.attr,
+ &dev_attr_compass_cy.attr,
+ &dev_attr_compass_cz.attr,
+ &dev_attr_compass_ca.attr,
+ &dev_attr_rx.attr,
+ &dev_attr_ry.attr,
+ &dev_attr_rz.attr,
+ &dev_attr_gyroLogLevel.attr,
+ &dev_attr_accelLogLevel.attr,
+ &dev_attr_compassLogLevel.attr,
+ &dev_attr_fusionLogLevel.attr,
+ NULL
+};
+
+static const struct attribute_group mpu9150_group = {
+ .attrs = mpu9150_attributes,
+};
+#endif
+
+static const struct file_operations mpu9150_client_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = mpu9150_client_ioctl,
+ .open = mpu9150_client_open,
+ .release = mpu9150_client_close,
+};
+
+static struct miscdevice mpu9150_client_miscdrv = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = MPU9150_CLIENT_DRIVER_NAME,
+ .fops = &mpu9150_client_fops,
+};
+
+static int mpu9150_irq_init(struct mpu9150_client *mpu9150_client_data)
+{
+ int ret = -1;
+
+ ret = m4sensorhub_irq_register(mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_GYRO_DATA_READY,
+ m4_handle_mpu9150_gyro_irq,
+ mpu9150_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering int %d (%d)\n",
+ M4SH_IRQ_GYRO_DATA_READY, ret);
+ return ret;
+ }
+ ret = m4sensorhub_irq_register(mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_ACCEL_DATA_READY,
+ m4_handle_mpu9150_accel_irq,
+ mpu9150_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering int %d (%d)\n",
+ M4SH_IRQ_ACCEL_DATA_READY, ret);
+ goto unregister_gyro_irq;
+ }
+ ret = m4sensorhub_irq_register(mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_COMPASS_DATA_READY,
+ m4_handle_mpu9150_compass_irq,
+ mpu9150_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering int %d (%d)\n",
+ M4SH_IRQ_COMPASS_DATA_READY, ret);
+ goto unregister_accel_irq;
+ }
+ ret = m4sensorhub_irq_register(mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_FUSION_DATA_READY,
+ m4_handle_mpu9150_fusion_irq,
+ mpu9150_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering int %d (%d)\n",
+ M4SH_IRQ_FUSION_DATA_READY, ret);
+ goto unregister_compass_irq;
+ }
+ return ret;
+
+unregister_compass_irq:
+ m4sensorhub_irq_unregister(mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_COMPASS_DATA_READY);
+unregister_accel_irq:
+ m4sensorhub_irq_unregister(mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_ACCEL_DATA_READY);
+unregister_gyro_irq:
+ m4sensorhub_irq_unregister(mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_GYRO_DATA_READY);
+ return ret;
+}
+
+static void mpu9150_irq_deinit(struct mpu9150_client *mpu9150_client_data)
+{
+ m4sensorhub_irq_unregister(mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_FUSION_DATA_READY);
+ m4sensorhub_irq_unregister(mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_COMPASS_DATA_READY);
+ m4sensorhub_irq_unregister(mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_ACCEL_DATA_READY);
+ m4sensorhub_irq_unregister(mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_GYRO_DATA_READY);
+}
+
+static int mpu9150_irq_enable_disable(struct mpu9150_client *mpu9150_client_data,
+ enum mpu9150_sensor type, int flag)
+{
+ int ret = 0;
+ int irq_status = 0;
+
+ switch (type) {
+ case TYPE_GYRO:
+ irq_status = m4sensorhub_irq_enable_get(
+ mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_GYRO_DATA_READY);
+ if (flag && (!irq_status)) {
+ ret = m4sensorhub_irq_enable(
+ mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_GYRO_DATA_READY);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error enabling int %d (%d)\n",
+ M4SH_IRQ_GYRO_DATA_READY, ret);
+ return ret;
+ }
+ } else if ((!flag) && irq_status)
+ m4sensorhub_irq_disable(
+ mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_GYRO_DATA_READY);
+ break;
+ case TYPE_ACCEL:
+ irq_status = m4sensorhub_irq_enable_get(
+ mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_ACCEL_DATA_READY);
+ if (flag && (!irq_status)) {
+ ret = m4sensorhub_irq_enable(
+ mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_ACCEL_DATA_READY);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error enabling int %d (%d)\n",
+ M4SH_IRQ_ACCEL_DATA_READY, ret);
+ return ret;
+ }
+ } else if ((!flag) && irq_status)
+ m4sensorhub_irq_disable(
+ mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_ACCEL_DATA_READY);
+ break;
+ case TYPE_COMPASS:
+ irq_status = m4sensorhub_irq_enable_get(
+ mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_COMPASS_DATA_READY);
+ if (flag && (!irq_status)) {
+ ret = m4sensorhub_irq_enable(
+ mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_COMPASS_DATA_READY);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error enabling int %d (%d)\n",
+ M4SH_IRQ_COMPASS_DATA_READY, ret);
+ return ret;
+ }
+ } else if ((!flag) && irq_status)
+ m4sensorhub_irq_disable(
+ mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_COMPASS_DATA_READY);
+ break;
+ case TYPE_FUSION:
+ irq_status = m4sensorhub_irq_enable_get(
+ mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_FUSION_DATA_READY);
+ if (flag && (!irq_status)) {
+ ret = m4sensorhub_irq_enable(
+ mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_FUSION_DATA_READY);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error enabling int %d (%d)\n",
+ M4SH_IRQ_FUSION_DATA_READY, ret);
+ return ret;
+ }
+ } else if ((!flag) && irq_status)
+ m4sensorhub_irq_disable(
+ mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_FUSION_DATA_READY);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int mpu9150_client_probe(struct platform_device *pdev)
+{
+ int ret = -1;
+ struct mpu9150_client *mpu9150_client_data;
+ struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata();
+
+ if (!m4sensorhub)
+ return -EFAULT;
+
+ mpu9150_client_data = kzalloc(sizeof(*mpu9150_client_data),
+ GFP_KERNEL);
+ if (!mpu9150_client_data)
+ return -ENOMEM;
+
+ mpu9150_client_data->m4sensorhub = m4sensorhub;
+ platform_set_drvdata(pdev, mpu9150_client_data);
+
+ mpu9150_client_data->input_dev = input_allocate_device();
+ if (!mpu9150_client_data->input_dev) {
+ ret = -ENOMEM;
+ KDEBUG(M4SH_ERROR, "%s: input device allocate failed: %d\n",
+ __func__, ret);
+ goto free_mem;
+ }
+
+ mpu9150_client_data->input_dev->name = MPU9150_CLIENT_DRIVER_NAME;
+ set_bit(EV_ABS, mpu9150_client_data->input_dev->evbit);
+ set_bit(EV_REL, mpu9150_client_data->input_dev->evbit);
+
+ set_bit(ABS_COMPASS_X, mpu9150_client_data->input_dev->absbit);
+ set_bit(ABS_COMPASS_Y, mpu9150_client_data->input_dev->absbit);
+ set_bit(ABS_COMPASS_Z, mpu9150_client_data->input_dev->absbit);
+ set_bit(ABS_COMPASS_ACCURACY, mpu9150_client_data->input_dev->absbit);
+ set_bit(ABS_X, mpu9150_client_data->input_dev->absbit);
+ set_bit(ABS_Y, mpu9150_client_data->input_dev->absbit);
+ set_bit(ABS_Z, mpu9150_client_data->input_dev->absbit);
+
+ set_bit(REL_X, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_Y, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_Z, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_GX, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_GY, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_GZ, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_LX, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_LY, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_LZ, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_WX, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_WY, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_WZ, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_ROLL, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_PITCH, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_YAW, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_RX, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_RY, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_RZ, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_HEADING, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_HEADING_ACCURACY, mpu9150_client_data->input_dev->relbit);
+
+ if (input_register_device(mpu9150_client_data->input_dev)) {
+ KDEBUG(M4SH_ERROR, "%s: input device register failed\n",
+ __func__);
+ input_free_device(mpu9150_client_data->input_dev);
+ goto free_mem;
+ }
+
+ ret = misc_register(&mpu9150_client_miscdrv);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering %s driver\n", __func__);
+ goto unregister_input_device;
+ }
+ misc_mpu9150_data = mpu9150_client_data;
+ ret = mpu9150_irq_init(mpu9150_client_data);
+ if (ret < 0)
+ goto unregister_misc_device;
+
+#ifdef MPU9150_DEBUG
+ ret = sysfs_create_group(&pdev->dev.kobj, &mpu9150_group);
+ if (ret)
+ goto unregister_irq;
+#endif
+ KDEBUG(M4SH_INFO, "Initialized %s driver\n", __func__);
+ return 0;
+
+#ifdef MPU9150_DEBUG
+unregister_irq:
+ mpu9150_irq_deinit(mpu9150_client_data);
+#endif
+unregister_misc_device:
+ misc_mpu9150_data = NULL;
+ misc_deregister(&mpu9150_client_miscdrv);
+unregister_input_device:
+ input_unregister_device(mpu9150_client_data->input_dev);
+free_mem:
+ platform_set_drvdata(pdev, NULL);
+ mpu9150_client_data->m4sensorhub = NULL;
+ kfree(mpu9150_client_data);
+ mpu9150_client_data = NULL;
+ return ret;
+}
+
+static int __exit mpu9150_client_remove(struct platform_device *pdev)
+{
+ struct mpu9150_client *mpu9150_client_data =
+ platform_get_drvdata(pdev);
+#ifdef MPU9150_DEBUG
+ sysfs_remove_group(&pdev->dev.kobj, &mpu9150_group);
+#endif
+ mpu9150_irq_deinit(mpu9150_client_data);
+ misc_mpu9150_data = NULL;
+ misc_deregister(&mpu9150_client_miscdrv);
+ input_unregister_device(mpu9150_client_data->input_dev);
+ platform_set_drvdata(pdev, NULL);
+ mpu9150_client_data->m4sensorhub = NULL;
+ kfree(mpu9150_client_data);
+ mpu9150_client_data = NULL;
+ return 0;
+}
+
+static void mpu9150_client_shutdown(struct platform_device *pdev)
+{
+ return;
+}
+#ifdef CONFIG_PM
+static int mpu9150_client_suspend(struct platform_device *pdev,
+ pm_message_t message)
+{
+ struct mpu9150_client *mpu9150_client_data =
+ platform_get_drvdata(pdev);
+
+ mpu9150_irq_enable_disable(mpu9150_client_data, TYPE_COMPASS,
+ SENSOR_IRQ_DISABLE);
+ mpu9150_irq_enable_disable(mpu9150_client_data, TYPE_GYRO,
+ SENSOR_IRQ_DISABLE);
+ mpu9150_irq_enable_disable(mpu9150_client_data, TYPE_ACCEL,
+ SENSOR_IRQ_DISABLE);
+ mpu9150_irq_enable_disable(mpu9150_client_data, TYPE_FUSION,
+ SENSOR_IRQ_DISABLE);
+
+ m4_set_mpu9150_delay(mpu9150_client_data, -1, TYPE_COMPASS);
+ m4_set_mpu9150_delay(mpu9150_client_data, -1, TYPE_GYRO);
+ m4_set_mpu9150_delay(mpu9150_client_data, -1, TYPE_ACCEL);
+ m4_set_mpu9150_delay(mpu9150_client_data, -1, TYPE_FUSION);
+
+ return 0;
+}
+
+static int mpu9150_client_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#else
+#define mpu9150_client_suspend NULL
+#define mpu9150_client_resume NULL
+#endif
+
+static struct of_device_id m4mpu9150_match_tbl[] = {
+ { .compatible = "mot,m4mpu9150" },
+ {},
+};
+
+static struct platform_driver mpu9150_client_driver = {
+ .probe = mpu9150_client_probe,
+ .remove = __exit_p(mpu9150_client_remove),
+ .shutdown = mpu9150_client_shutdown,
+ .suspend = mpu9150_client_suspend,
+ .resume = mpu9150_client_resume,
+ .driver = {
+ .name = MPU9150_CLIENT_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(m4mpu9150_match_tbl),
+ },
+};
+
+static int __init mpu9150_client_init(void)
+{
+ return platform_driver_register(&mpu9150_client_driver);
+}
+
+static void __exit mpu9150_client_exit(void)
+{
+ platform_driver_unregister(&mpu9150_client_driver);
+}
+
+module_init(mpu9150_client_init);
+module_exit(mpu9150_client_exit);
+
+MODULE_ALIAS("platform:mpu9150_client");
+MODULE_DESCRIPTION("M4 Sensor Hub Mpu9150 client driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/m4sensorhub_passive.c b/drivers/misc/m4sensorhub_passive.c
new file mode 100644
index 00000000000..86c58fcc7e2
--- /dev/null
+++ b/drivers/misc/m4sensorhub_passive.c
@@ -0,0 +1,325 @@
+/*
+ * 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/m4sensorhub.h>
+#include <linux/m4sensorhub/MemMapPassive.h>
+#include <linux/m4sensorhub_client_ioctl.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+
+#define PASSIVE_CLIENT_DRIVER_NAME "m4sensorhub_passive"
+
+struct m4_passive_data {
+ u32 mets;
+ u32 steps;
+ u32 floorsClimbed;
+ u32 timestamp;
+};
+
+struct passive_client {
+ struct m4sensorhub_data *m4sensorhub;
+ struct input_dev *input_dev;
+};
+
+static struct passive_client *misc_passive_data;
+static struct m4_passive_data pdata_buffer[MAX_PASSIVE_BUFFERS] = {};
+
+static int passive_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_passive_data;
+
+ return 0;
+}
+
+static int passive_client_close(struct inode *inode, struct file *file)
+{
+ KDEBUG(M4SH_DEBUG, "passive_client in %s\n", __func__);
+ return 0;
+}
+
+static void m4_report_passive_inputevent(
+ struct passive_client *passive_client_data)
+{
+ int i;
+ for (i = 0; i < MAX_PASSIVE_BUFFERS; i++) {
+ input_event(passive_client_data->input_dev, EV_MSC,
+ MSC_PASSIVE_STEPS,
+ pdata_buffer[i].steps);
+ input_event(passive_client_data->input_dev, EV_MSC,
+ MSC_PASSIVE_METS,
+ pdata_buffer[i].mets);
+ input_event(passive_client_data->input_dev, EV_MSC,
+ MSC_PASSIVE_TIMESTAMP,
+ pdata_buffer[i].timestamp);
+ input_event(passive_client_data->input_dev, EV_MSC,
+ MSC_PASSIVE_FLOORSCLIMBED,
+ pdata_buffer[i].floorsClimbed);
+ input_sync(passive_client_data->input_dev);
+ }
+}
+
+
+static void m4_read_passive_data(struct passive_client *passive_client_data)
+{
+ int i;
+ u32 steps[MAX_PASSIVE_BUFFERS] = {0};
+ u32 mets[MAX_PASSIVE_BUFFERS] = {0};
+ u32 timestamp[12] = {0};
+ u32 floorsClimbed[MAX_PASSIVE_BUFFERS] = {0};
+
+ /*read all buffers of steps*/
+ m4sensorhub_reg_read(passive_client_data->m4sensorhub,
+ M4SH_REG_PASSIVE_STEPS,
+ (char *)&steps);
+ m4sensorhub_reg_read(passive_client_data->m4sensorhub,
+ M4SH_REG_PASSIVE_METS,
+ (char *)&mets);
+ m4sensorhub_reg_read(passive_client_data->m4sensorhub,
+ M4SH_REG_PASSIVE_TIMESTAMP,
+ (char *)&timestamp);
+ m4sensorhub_reg_read(passive_client_data->m4sensorhub,
+ M4SH_REG_PASSIVE_FLOORSCLIMBED,
+ (char *)&floorsClimbed);
+ for (i = 0; i < MAX_PASSIVE_BUFFERS; i++) {
+ pdata_buffer[i].steps = steps[i];
+ pdata_buffer[i].mets = mets[i];
+ pdata_buffer[i].timestamp = timestamp[i];
+ pdata_buffer[i].floorsClimbed = floorsClimbed[i];
+ KDEBUG(M4SH_DEBUG, "steps = %u, mets = %u, timestamp = %u,\
+ floorsClimbed = %u", pdata_buffer[i].steps,
+ pdata_buffer[i].mets, pdata_buffer[i].timestamp,
+ pdata_buffer[i].floorsClimbed);
+ }
+}
+
+static void m4_handle_passive_irq(enum m4sensorhub_irqs int_event,
+ void *passive_data)
+{
+ struct passive_client *passive_client_data = passive_data;
+
+ m4_read_passive_data(passive_client_data);
+ m4_report_passive_inputevent(passive_client_data);
+}
+
+/*
+ * Handle commands from user-space.
+ */
+static long passive_client_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ struct passive_client *passive_client_data = filp->private_data;
+
+ switch (cmd) {
+ case M4_SENSOR_IOCTL_GET_PASSIVE_DATA:
+ m4_read_passive_data(passive_client_data);
+ m4_report_passive_inputevent(passive_client_data);
+ break;
+ default:
+ KDEBUG(M4SH_ERROR, "Invalid IOCTL Command in %s\n", __func__);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static const struct file_operations passive_client_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = passive_client_ioctl,
+ .open = passive_client_open,
+ .release = passive_client_close,
+};
+
+static struct miscdevice passive_client_miscdrv = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = PASSIVE_CLIENT_DRIVER_NAME,
+ .fops = &passive_client_fops,
+};
+
+static int passive_client_probe(struct platform_device *pdev)
+{
+ int ret = -1;
+ struct passive_client *passive_client_data;
+ struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata();
+
+ if (!m4sensorhub)
+ return -EFAULT;
+
+ passive_client_data = kzalloc(sizeof(*passive_client_data),
+ GFP_KERNEL);
+ if (!passive_client_data)
+ return -ENOMEM;
+
+ passive_client_data->m4sensorhub = m4sensorhub;
+ platform_set_drvdata(pdev, passive_client_data);
+
+ passive_client_data->input_dev = input_allocate_device();
+ if (!passive_client_data->input_dev) {
+ ret = -ENOMEM;
+ KDEBUG(M4SH_ERROR, "%s: input device allocate failed: %d\n",
+ __func__, ret);
+ goto free_mem;
+ }
+
+ passive_client_data->input_dev->name = PASSIVE_CLIENT_DRIVER_NAME;
+ set_bit(EV_MSC, passive_client_data->input_dev->evbit);
+ set_bit(MSC_PASSIVE_STEPS, passive_client_data->input_dev->mscbit);
+ set_bit(MSC_PASSIVE_METS, passive_client_data->input_dev->mscbit);
+ set_bit(MSC_PASSIVE_TIMESTAMP, passive_client_data->input_dev->mscbit);
+ set_bit(MSC_PASSIVE_FLOORSCLIMBED,
+ passive_client_data->input_dev->mscbit);
+ if (input_register_device(passive_client_data->input_dev)) {
+ KDEBUG(M4SH_ERROR, "%s: input device register failed\n",
+ __func__);
+ input_free_device(passive_client_data->input_dev);
+ goto free_mem;
+ }
+
+ ret = misc_register(&passive_client_miscdrv);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering %s driver\n", __func__);
+ goto unregister_input_device;
+ }
+ misc_passive_data = passive_client_data;
+
+ ret = m4sensorhub_irq_register(m4sensorhub,
+ M4SH_IRQ_PASSIVE_BUFFER_FULL,
+ m4_handle_passive_irq,
+ passive_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering int %d (%d)\n",
+ M4SH_IRQ_PASSIVE_BUFFER_FULL, ret);
+ goto unregister_misc_device;
+ }
+ ret = m4sensorhub_irq_enable(m4sensorhub, M4SH_IRQ_PASSIVE_BUFFER_FULL);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error enabling int %d (%d)\n",
+ M4SH_IRQ_PASSIVE_BUFFER_FULL, ret);
+ goto unregister_irq;
+ }
+ KDEBUG(M4SH_INFO, "Initialized %s driver\n", __func__);
+ return 0;
+
+unregister_irq:
+ m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_PASSIVE_BUFFER_FULL);
+unregister_misc_device:
+ misc_passive_data = NULL;
+ misc_deregister(&passive_client_miscdrv);
+unregister_input_device:
+ input_unregister_device(passive_client_data->input_dev);
+free_mem:
+ platform_set_drvdata(pdev, NULL);
+ passive_client_data->m4sensorhub = NULL;
+ kfree(passive_client_data);
+ passive_client_data = NULL;
+ return ret;
+}
+
+static int __exit passive_client_remove(struct platform_device *pdev)
+{
+ struct passive_client *passive_client_data =
+ platform_get_drvdata(pdev);
+
+ m4sensorhub_irq_disable(passive_client_data->m4sensorhub,
+ M4SH_IRQ_PASSIVE_BUFFER_FULL);
+ m4sensorhub_irq_unregister(passive_client_data->m4sensorhub,
+ M4SH_IRQ_PASSIVE_BUFFER_FULL);
+
+ misc_passive_data = NULL;
+ misc_deregister(&passive_client_miscdrv);
+ input_unregister_device(passive_client_data->input_dev);
+ platform_set_drvdata(pdev, NULL);
+ passive_client_data->m4sensorhub = NULL;
+ kfree(passive_client_data);
+ passive_client_data = NULL;
+ return 0;
+}
+
+static void passive_client_shutdown(struct platform_device *pdev)
+{
+ return;
+}
+#ifdef CONFIG_PM
+static int passive_client_suspend(struct platform_device *pdev,
+ pm_message_t message)
+{
+ return 0;
+}
+
+static int passive_client_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#else
+#define passive_client_suspend NULL
+#define passive_client_resume NULL
+#endif
+
+static struct of_device_id m4passive_match_tbl[] = {
+ { .compatible = "mot,m4passive" },
+ {},
+};
+
+static struct platform_driver passive_client_driver = {
+ .probe = passive_client_probe,
+ .remove = __exit_p(passive_client_remove),
+ .shutdown = passive_client_shutdown,
+ .suspend = passive_client_suspend,
+ .resume = passive_client_resume,
+ .driver = {
+ .name = PASSIVE_CLIENT_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(m4passive_match_tbl),
+ },
+};
+
+static int __init passive_client_init(void)
+{
+ return platform_driver_register(&passive_client_driver);
+}
+
+static void __exit passive_client_exit(void)
+{
+ platform_driver_unregister(&passive_client_driver);
+}
+
+module_init(passive_client_init);
+module_exit(passive_client_exit);
+
+MODULE_ALIAS("platform:passive_client");
+MODULE_DESCRIPTION("M4 Sensor Hub Passive mode client driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/misc/m4sensorhub_pedometer.c b/drivers/misc/m4sensorhub_pedometer.c
new file mode 100644
index 00000000000..9a6707fba0c
--- /dev/null
+++ b/drivers/misc/m4sensorhub_pedometer.c
@@ -0,0 +1,689 @@
+/*
+ * 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/m4sensorhub.h>
+#include <linux/m4sensorhub_client_ioctl.h>
+#include <linux/m4sensorhub/MemMapPedometer.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+
+#define PEDOMETER_CLIENT_DRIVER_NAME "m4sensorhub_pedometer"
+
+struct pedometer_data {
+ unsigned char activity;
+ unsigned int distance;
+ unsigned int mets;
+ unsigned char metsactivity;
+ unsigned int calories;
+ unsigned short stepcount;
+ unsigned short speed;
+ unsigned short floorsclimbed;
+};
+
+struct pedometer_client {
+ struct m4sensorhub_data *m4sensorhub;
+ struct input_dev *input_dev;
+ struct pedometer_data prev_data;
+ struct pedometer_data curr_data;
+};
+
+struct pedometer_client *misc_pedometer_data;
+
+static int pedometer_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_pedometer_data;
+
+ return 0;
+}
+
+static int pedometer_client_close(struct inode *inode, struct file *file)
+{
+ KDEBUG(M4SH_DEBUG, "pedometer_client in %s\n", __func__);
+ return 0;
+}
+
+static void m4_report_pedometer_inputevent(
+ struct pedometer_client *pedo_client_data)
+{
+ input_event(pedo_client_data->input_dev, EV_MSC, MSC_ACTIVITY_TYPE,
+ pedo_client_data->curr_data.activity);
+ input_event(pedo_client_data->input_dev, EV_MSC, MSC_STEPCOUNT,
+ pedo_client_data->curr_data.stepcount);
+ input_event(pedo_client_data->input_dev, EV_MSC, MSC_DISTANCE,
+ pedo_client_data->curr_data.distance);
+ input_event(pedo_client_data->input_dev, EV_MSC, MSC_SPEED,
+ pedo_client_data->curr_data.speed);
+ input_event(pedo_client_data->input_dev, EV_MSC, MSC_METS,
+ pedo_client_data->curr_data.mets);
+ input_event(pedo_client_data->input_dev, EV_MSC, MSC_CALORIES,
+ pedo_client_data->curr_data.calories);
+ input_event(pedo_client_data->input_dev, EV_MSC, MSC_FLOORSCLIMBED,
+ pedo_client_data->curr_data.floorsclimbed);
+ input_event(pedo_client_data->input_dev, EV_MSC, MSC_METSACTIVITY,
+ pedo_client_data->curr_data.metsactivity);
+ input_sync(pedo_client_data->input_dev);
+
+ KDEBUG(M4SH_DEBUG, "Sending pedometer data : stepcount = %d,\
+ speed = %d,distance = %d,mets = %d,calories = %d, \
+ activity = %d,floorsclimbed = %d, metsactivity = %d\n",
+ pedo_client_data->curr_data.stepcount,
+ pedo_client_data->curr_data.speed,
+ pedo_client_data->curr_data.distance,
+ pedo_client_data->curr_data.mets,
+ pedo_client_data->curr_data.calories,
+ pedo_client_data->curr_data.activity,
+ pedo_client_data->curr_data.floorsclimbed,
+ pedo_client_data->curr_data.metsactivity);
+}
+
+
+static void m4_set_delay(int delay)
+{
+
+}
+
+static void m4_read_pedometer_data(struct pedometer_client *pedo_client_data)
+{
+ m4sensorhub_reg_read(pedo_client_data->m4sensorhub,
+ M4SH_REG_PEDOMETER_ACTIVITY,
+ (char *)&pedo_client_data->curr_data.activity);
+ m4sensorhub_reg_read(pedo_client_data->m4sensorhub,
+ M4SH_REG_PEDOMETER_TOTATDISTANCE,
+ (char *)&pedo_client_data->curr_data.distance);
+ m4sensorhub_reg_read(pedo_client_data->m4sensorhub,
+ M4SH_REG_PEDOMETER_TOTALSTEPS,
+ (char *)&pedo_client_data->curr_data.stepcount);
+ m4sensorhub_reg_read(pedo_client_data->m4sensorhub,
+ M4SH_REG_PEDOMETER_CURRENTSPEED,
+ (char *)&pedo_client_data->curr_data.speed);
+ m4sensorhub_reg_read(pedo_client_data->m4sensorhub,
+ M4SH_REG_METS_METS,
+ (char *)&pedo_client_data->curr_data.mets);
+ m4sensorhub_reg_read(pedo_client_data->m4sensorhub,
+ M4SH_REG_METS_CALORIES,
+ (char *)&pedo_client_data->curr_data.calories);
+ m4sensorhub_reg_read(pedo_client_data->m4sensorhub,
+ M4SH_REG_PEDOMETER_FLOORSCLIMBED,
+ (char *)&pedo_client_data->curr_data.floorsclimbed);
+ m4sensorhub_reg_read(pedo_client_data->m4sensorhub,
+ M4SH_REG_METS_METSACTIVITY,
+ (char *)&pedo_client_data->curr_data.metsactivity);
+}
+
+static void m4_handle_pedometer_irq(enum m4sensorhub_irqs int_event,
+ void *pedometer_data)
+{
+ struct pedometer_client *pedometer_client_data = pedometer_data;
+
+ m4_read_pedometer_data(pedometer_client_data);
+ m4_report_pedometer_inputevent(pedometer_client_data);
+}
+
+/*
+ * Handle commands from user-space.
+ */
+static long pedometer_client_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ int flag;
+ unsigned char byte;
+ void __user *argp = (void __user *)arg;
+ struct m4sh_user_profile user;
+ struct m4sh_workout_data workout_data;
+ struct pedometer_client *pedometer_client_data = filp->private_data;
+
+ switch (cmd) {
+ case M4_SENSOR_IOCTL_GET_PEDOMETER:
+ m4_read_pedometer_data(pedometer_client_data);
+ m4_report_pedometer_inputevent(pedometer_client_data);
+ break;
+ case M4_SENSOR_IOCTL_SET_DELAY:
+ if (copy_from_user(&flag, argp, sizeof(flag)))
+ return -EFAULT;
+ m4_set_delay(flag);
+ break;
+ /* TO DO
+ Need to implement the following ioctl's when M4 side implementation
+ will be ready
+ */
+ case M4_SENSOR_IOCTL_SET_POSIX_TIME:
+ break;
+ case M4_SENSOR_IOCTL_SET_EQUIPMENT_TYPE:
+ if (copy_from_user(&byte, argp, sizeof(byte))) {
+ printk(KERN_ERR "copy from user returned error eq type\n");
+ ret = -EFAULT;
+ break;
+ }
+ m4sensorhub_reg_write(pedometer_client_data->m4sensorhub,
+ M4SH_REG_PEDOMETER_EQUIPMENTTYPE, &byte, m4sh_no_mask);
+ break;
+ case M4_SENSOR_IOCTL_SET_MANUAL_CALIB_WALK_SPEED:
+ break;
+ case M4_SENSOR_IOCTL_SET_MANUAL_CALIB_JOG_SPEED:
+ break;
+ case M4_SENSOR_IOCTL_SET_MANUAL_CALIB_RUN_SPEED:
+ break;
+ case M4_SENSOR_IOCTL_SET_MANUAL_CALIB_STATUS:
+ break;
+ case M4_SENSOR_IOCTL_SET_USER_PROFILE:
+ if (copy_from_user(&user, argp, sizeof(user))) {
+ printk(KERN_ERR "copy from user returned error\n");
+ ret = -EFAULT;
+ break;
+ }
+ m4sensorhub_reg_write_1byte(pedometer_client_data->m4sensorhub,
+ M4SH_REG_USERSETTINGS_USERAGE, user.age, 0xff);
+ m4sensorhub_reg_write_1byte(pedometer_client_data->m4sensorhub,
+ M4SH_REG_USERSETTINGS_USERGENDER, user.gender, 0xff);
+ m4sensorhub_reg_write_1byte(pedometer_client_data->m4sensorhub,
+ M4SH_REG_USERSETTINGS_USERHEIGHT, user.height, 0xff);
+ m4sensorhub_reg_write_1byte(pedometer_client_data->m4sensorhub,
+ M4SH_REG_USERSETTINGS_USERWEIGHT, user.weight, 0xff);
+ break;
+ case M4_SENSOR_IOCTL_SET_USER_DISTANCE:
+ if (copy_from_user(&workout_data, argp, sizeof(workout_data))) {
+ printk(KERN_ERR "copy from user returned error\n");
+ ret = -EFAULT;
+ break;
+ }
+ m4sensorhub_reg_write(pedometer_client_data->m4sensorhub,
+ M4SH_REG_PEDOMETER_USERDISTANCE,
+ (unsigned char *)&workout_data.user_distance,
+ m4sh_no_mask);
+ m4sensorhub_reg_write(pedometer_client_data->m4sensorhub,
+ M4SH_REG_PEDOMETER_REPORTEDDISTANCE,
+ (unsigned char *)&workout_data.msp_distance,
+ m4sh_no_mask);
+ break;
+ case M4_SENSOR_IOCTL_SET_USER_CALIB_TABLE:
+ break;
+ case M4_SENSOR_IOCTL_GET_MANUAL_CALIB_STATUS:
+ break;
+ case M4_SENSOR_IOCTL_ERASE_CALIB:
+ break;
+ default:
+ KDEBUG(M4SH_ERROR, "Invalid IOCTL Command in %s\n", __func__);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static ssize_t m4_pedometer_activity(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pedometer_client *pedo_client_data = platform_get_drvdata(pdev);
+
+ m4_read_pedometer_data(pedo_client_data);
+ KDEBUG(M4SH_DEBUG, "%s : activity = %d\n",
+ __func__, pedo_client_data->curr_data.activity);
+ return sprintf(buf, "%d \n", pedo_client_data->curr_data.activity);
+}
+
+static ssize_t m4_pedometer_distance(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pedometer_client *pedo_client_data = platform_get_drvdata(pdev);
+
+ m4_read_pedometer_data(pedo_client_data);
+ KDEBUG(M4SH_DEBUG, "%s : distance = %d\n",
+ __func__, pedo_client_data->curr_data.distance);
+ return sprintf(buf, "%d \n", pedo_client_data->curr_data.distance);
+}
+
+static ssize_t m4_pedometer_speed(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pedometer_client *pedo_client_data = platform_get_drvdata(pdev);
+
+ m4_read_pedometer_data(pedo_client_data);
+ KDEBUG(M4SH_DEBUG, "%s : speed = %d\n",
+ __func__, pedo_client_data->curr_data.speed);
+ return sprintf(buf, "%d \n", pedo_client_data->curr_data.speed);
+}
+
+static ssize_t m4_pedometer_stepcount(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pedometer_client *pedo_client_data = platform_get_drvdata(pdev);
+
+ m4_read_pedometer_data(pedo_client_data);
+ KDEBUG(M4SH_DEBUG, "%s : stepcount = %d\n",
+ __func__, pedo_client_data->curr_data.stepcount);
+ return sprintf(buf, "%d \n", pedo_client_data->curr_data.stepcount);
+}
+
+static ssize_t m4_pedometer_mets(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pedometer_client *pedo_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : mets = %d\n",
+ __func__, pedo_client_data->curr_data.mets);
+ return sprintf(buf, "%d \n", pedo_client_data->curr_data.mets);
+}
+
+static ssize_t m4_pedometer_calories(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pedometer_client *pedo_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : calories = %d\n",
+ __func__, pedo_client_data->curr_data.calories);
+ return sprintf(buf, "%d \n", pedo_client_data->curr_data.calories);
+}
+
+static ssize_t m4_pedometer_floorsclimbed(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pedometer_client *pedo_client_data = platform_get_drvdata(pdev);
+
+ m4_read_pedometer_data(pedo_client_data);
+ KDEBUG(M4SH_DEBUG, "%s : floorsclimbed = %d\n",
+ __func__, pedo_client_data->curr_data.floorsclimbed);
+ return sprintf(buf, "%d \n", pedo_client_data->curr_data.floorsclimbed);
+}
+
+static ssize_t m4_pedometer_metsactivity(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pedometer_client *pedo_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : metsactivity = %d\n",
+ __func__, pedo_client_data->curr_data.metsactivity);
+ return sprintf(buf, "%d \n", pedo_client_data->curr_data.metsactivity);
+}
+
+static ssize_t pedo_get_loglevel(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+
+ int loglevel;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pedometer_client *pedo_client_data = platform_get_drvdata(pdev);
+
+ m4sensorhub_reg_read(pedo_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&loglevel);
+ loglevel = get_log_level(loglevel, PEDO_MASK_BIT_1);
+ return sprintf(buf, "%d\n", loglevel);
+}
+
+static ssize_t pedo_set_loglevel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned long level;
+ unsigned int mask = 0;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pedometer_client *pedo_client_data = platform_get_drvdata(pdev);
+
+ if ((strict_strtoul(buf, 10, &level)) < 0)
+ return -1;
+ if (level > M4_MAX_LOG_LEVEL) {
+ KDEBUG(M4SH_ERROR, " Invalid log level - %d\n", (int)level);
+ return -1;
+ }
+ mask = (1 << PEDO_MASK_BIT_1) | (1 << PEDO_MASK_BIT_2);
+ level = (level << PEDO_MASK_BIT_1);
+ return m4sensorhub_reg_write(pedo_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&level, (unsigned char *)&mask);
+}
+
+static ssize_t mets_get_loglevel(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int loglevel;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pedometer_client *pedo_client_data = platform_get_drvdata(pdev);
+
+ m4sensorhub_reg_read(pedo_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&loglevel);
+ loglevel = get_log_level(loglevel, METS_MASK_BIT_1);
+ return sprintf(buf, "%d\n", loglevel);
+}
+
+static ssize_t mets_set_loglevel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned long level;
+ unsigned int mask = 0;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pedometer_client *pedo_client_data = platform_get_drvdata(pdev);
+
+ if ((strict_strtoul(buf, 10, &level)) < 0)
+ return -1;
+ if (level > M4_MAX_LOG_LEVEL) {
+ KDEBUG(M4SH_ERROR, " Invalid log level - %d\n", (int)level);
+ return -1;
+ }
+ mask = (1 << METS_MASK_BIT_1) | (1 << METS_MASK_BIT_2);
+ level = (level << METS_MASK_BIT_1);
+ return m4sensorhub_reg_write(pedo_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&level, (unsigned char *)&mask);
+}
+
+static DEVICE_ATTR(activity, 0444, m4_pedometer_activity, NULL);
+static DEVICE_ATTR(distance, 0444, m4_pedometer_distance, NULL);
+static DEVICE_ATTR(speed, 0444, m4_pedometer_speed, NULL);
+static DEVICE_ATTR(stepcount, 0444, m4_pedometer_stepcount, NULL);
+static DEVICE_ATTR(mets, 0444, m4_pedometer_mets, NULL);
+static DEVICE_ATTR(calories, 0444, m4_pedometer_calories, NULL);
+static DEVICE_ATTR(floorsclimbed, 0444, m4_pedometer_floorsclimbed, NULL);
+static DEVICE_ATTR(metsactivity, 0444, m4_pedometer_metsactivity, NULL);
+static DEVICE_ATTR(pedoLogLevel, 0664, pedo_get_loglevel, pedo_set_loglevel);
+static DEVICE_ATTR(metsLogLevel, 0644, mets_get_loglevel, mets_set_loglevel);
+
+static const struct file_operations pedometer_client_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = pedometer_client_ioctl,
+ .open = pedometer_client_open,
+ .release = pedometer_client_close,
+};
+
+static struct miscdevice pedometer_client_miscdrv = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = PEDOMETER_CLIENT_DRIVER_NAME,
+ .fops = &pedometer_client_fops,
+};
+
+static int pedometer_client_probe(struct platform_device *pdev)
+{
+ int ret = -1;
+ struct pedometer_client *pedometer_client_data;
+ struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata();
+
+ if (!m4sensorhub)
+ return -EFAULT;
+
+ pedometer_client_data = kzalloc(sizeof(*pedometer_client_data),
+ GFP_KERNEL);
+ if (!pedometer_client_data)
+ return -ENOMEM;
+
+ pedometer_client_data->m4sensorhub = m4sensorhub;
+ platform_set_drvdata(pdev, pedometer_client_data);
+
+ pedometer_client_data->prev_data.stepcount = 0;
+ pedometer_client_data->prev_data.distance = 0;
+ pedometer_client_data->prev_data.activity = 0;
+ pedometer_client_data->prev_data.speed = 0;
+ pedometer_client_data->prev_data.floorsclimbed = 0;
+
+ pedometer_client_data->input_dev = input_allocate_device();
+ if (!pedometer_client_data->input_dev) {
+ ret = -ENOMEM;
+ KDEBUG(M4SH_ERROR, "%s: input device allocate failed: %d\n",
+ __func__, ret);
+ goto free_mem;
+ }
+
+ pedometer_client_data->input_dev->name = PEDOMETER_CLIENT_DRIVER_NAME;
+ set_bit(EV_MSC, pedometer_client_data->input_dev->evbit);
+ set_bit(MSC_ACTIVITY_TYPE, pedometer_client_data->input_dev->mscbit);
+ set_bit(MSC_STEPCOUNT, pedometer_client_data->input_dev->mscbit);
+ set_bit(MSC_SPEED, pedometer_client_data->input_dev->mscbit);
+ set_bit(MSC_DISTANCE, pedometer_client_data->input_dev->mscbit);
+ set_bit(MSC_METS, pedometer_client_data->input_dev->mscbit);
+ set_bit(MSC_CALORIES, pedometer_client_data->input_dev->mscbit);
+ set_bit(MSC_FLOORSCLIMBED, pedometer_client_data->input_dev->mscbit);
+ set_bit(MSC_METSACTIVITY, pedometer_client_data->input_dev->mscbit);
+
+ if (input_register_device(pedometer_client_data->input_dev)) {
+ KDEBUG(M4SH_ERROR, "%s: input device register failed\n",
+ __func__);
+ input_free_device(pedometer_client_data->input_dev);
+ goto free_mem;
+ }
+
+ ret = misc_register(&pedometer_client_miscdrv);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering %s driver\n", __func__);
+ goto unregister_input_device;
+ }
+ misc_pedometer_data = pedometer_client_data;
+ ret = m4sensorhub_irq_register(m4sensorhub,
+ M4SH_IRQ_PEDOMETER_DATA_READY, m4_handle_pedometer_irq,
+ pedometer_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering int %d (%d)\n",
+ M4SH_IRQ_PEDOMETER_DATA_READY, ret);
+ goto unregister_misc_device;
+ }
+ ret = m4sensorhub_irq_register(m4sensorhub, M4SH_IRQ_ACTIVITY_CHANGE,
+ m4_handle_pedometer_irq,
+ pedometer_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering int %d (%d)\n",
+ M4SH_IRQ_ACTIVITY_CHANGE, ret);
+ goto unregister_pedometer_irq;
+ }
+
+ ret = m4sensorhub_irq_enable(m4sensorhub, M4SH_IRQ_ACTIVITY_CHANGE);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error enabling int %d (%d)\n",
+ M4SH_IRQ_ACTIVITY_CHANGE, ret);
+ goto unregister_activity_irq;
+ }
+
+ if (device_create_file(&pdev->dev, &dev_attr_activity)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n", __func__);
+ ret = -1;
+ goto disable_activity_irq;
+ }
+ if (device_create_file(&pdev->dev, &dev_attr_distance)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n", __func__);
+ ret = -1;
+ goto remove_activity_device_file;
+ }
+ if (device_create_file(&pdev->dev, &dev_attr_speed)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n", __func__);
+ ret = -1;
+ goto remove_distance_device_file;
+ }
+ if (device_create_file(&pdev->dev, &dev_attr_stepcount)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n", __func__);
+ ret = -1;
+ goto remove_speed_device_file;
+ }
+ if (device_create_file(&pdev->dev, &dev_attr_mets)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n", __func__);
+ ret = -1;
+ goto remove_stepcount_device_file;
+ }
+ if (device_create_file(&pdev->dev, &dev_attr_pedoLogLevel)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n", __func__);
+ ret = -1;
+ goto remove_mets_device_file;
+ }
+ if (device_create_file(&pdev->dev, &dev_attr_metsLogLevel)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n", __func__);
+ ret = -1;
+ goto remove_pedoLogLevel_device_file;
+ }
+ if (device_create_file(&pdev->dev, &dev_attr_calories)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n", __func__);
+ ret = -1;
+ goto remove_metsLogLevel_device_file;
+ }
+ if (device_create_file(&pdev->dev, &dev_attr_floorsclimbed)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n", __func__);
+ ret = -1;
+ goto remove_cals_device_file;
+ }
+ if (device_create_file(&pdev->dev, &dev_attr_metsactivity)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n", __func__);
+ ret = -1;
+ goto remove_floorsclimbed_device_file;
+ }
+ KDEBUG(M4SH_INFO, "Initialized %s driver\n", __func__);
+ return 0;
+
+remove_floorsclimbed_device_file:
+ device_remove_file(&pdev->dev, &dev_attr_floorsclimbed);
+remove_cals_device_file:
+ device_remove_file(&pdev->dev, &dev_attr_calories);
+remove_metsLogLevel_device_file:
+ device_remove_file(&pdev->dev, &dev_attr_metsLogLevel);
+remove_pedoLogLevel_device_file:
+ device_remove_file(&pdev->dev, &dev_attr_pedoLogLevel);
+remove_mets_device_file:
+ device_remove_file(&pdev->dev, &dev_attr_mets);
+remove_stepcount_device_file:
+ device_remove_file(&pdev->dev, &dev_attr_stepcount);
+remove_speed_device_file:
+ device_remove_file(&pdev->dev, &dev_attr_speed);
+remove_distance_device_file:
+ device_remove_file(&pdev->dev, &dev_attr_distance);
+remove_activity_device_file:
+ device_remove_file(&pdev->dev, &dev_attr_activity);
+disable_activity_irq:
+ m4sensorhub_irq_disable(m4sensorhub, M4SH_IRQ_ACTIVITY_CHANGE);
+unregister_activity_irq:
+ m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_ACTIVITY_CHANGE);
+unregister_pedometer_irq:
+ m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_PEDOMETER_DATA_READY);
+unregister_misc_device:
+ misc_pedometer_data = NULL;
+ misc_deregister(&pedometer_client_miscdrv);
+unregister_input_device:
+ input_unregister_device(pedometer_client_data->input_dev);
+free_mem:
+ platform_set_drvdata(pdev, NULL);
+ pedometer_client_data->m4sensorhub = NULL;
+ kfree(pedometer_client_data);
+ pedometer_client_data = NULL;
+ return ret;
+}
+
+static int __exit pedometer_client_remove(struct platform_device *pdev)
+{
+ struct pedometer_client *pedometer_client_data =
+ platform_get_drvdata(pdev);
+
+ device_remove_file(&pdev->dev, &dev_attr_metsLogLevel);
+ device_remove_file(&pdev->dev, &dev_attr_pedoLogLevel);
+ device_remove_file(&pdev->dev, &dev_attr_mets);
+ device_remove_file(&pdev->dev, &dev_attr_calories);
+ device_remove_file(&pdev->dev, &dev_attr_stepcount);
+ device_remove_file(&pdev->dev, &dev_attr_speed);
+ device_remove_file(&pdev->dev, &dev_attr_distance);
+ device_remove_file(&pdev->dev, &dev_attr_activity);
+ device_remove_file(&pdev->dev, &dev_attr_floorsclimbed);
+ device_remove_file(&pdev->dev, &dev_attr_metsactivity);
+
+ m4sensorhub_irq_unregister(pedometer_client_data->m4sensorhub,
+ M4SH_IRQ_PEDOMETER_DATA_READY);
+ m4sensorhub_irq_disable(pedometer_client_data->m4sensorhub,
+ M4SH_IRQ_ACTIVITY_CHANGE);
+ m4sensorhub_irq_unregister(pedometer_client_data->m4sensorhub,
+ M4SH_IRQ_ACTIVITY_CHANGE);
+ misc_pedometer_data = NULL;
+ misc_deregister(&pedometer_client_miscdrv);
+ input_unregister_device(pedometer_client_data->input_dev);
+ platform_set_drvdata(pdev, NULL);
+ pedometer_client_data->m4sensorhub = NULL;
+ kfree(pedometer_client_data);
+ pedometer_client_data = NULL;
+ return 0;
+}
+
+static void pedometer_client_shutdown(struct platform_device *pdev)
+{
+ return;
+}
+#ifdef CONFIG_PM
+static int pedometer_client_suspend(struct platform_device *pdev,
+ pm_message_t message)
+{
+ return 0;
+}
+
+static int pedometer_client_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#else
+#define pedometer_client_suspend NULL
+#define pedometer_client_resume NULL
+#endif
+
+
+static struct of_device_id m4pedometer_match_tbl[] = {
+ { .compatible = "mot,m4pedometer" },
+ {},
+};
+
+
+static struct platform_driver pedometer_client_driver = {
+ .probe = pedometer_client_probe,
+ .remove = __exit_p(pedometer_client_remove),
+ .shutdown = pedometer_client_shutdown,
+ .suspend = pedometer_client_suspend,
+ .resume = pedometer_client_resume,
+ .driver = {
+ .name = PEDOMETER_CLIENT_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(m4pedometer_match_tbl),
+ },
+};
+
+static int __init pedometer_client_init(void)
+{
+ return platform_driver_register(&pedometer_client_driver);
+}
+
+static void __exit pedometer_client_exit(void)
+{
+ platform_driver_unregister(&pedometer_client_driver);
+}
+
+module_init(pedometer_client_init);
+module_exit(pedometer_client_exit);
+
+MODULE_ALIAS("platform:pedometer_client");
+MODULE_DESCRIPTION("M4 Sensor Hub Pedometer client driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/misc/m4sensorhub_stillmode.c b/drivers/misc/m4sensorhub_stillmode.c
new file mode 100644
index 00000000000..3fa61219245
--- /dev/null
+++ b/drivers/misc/m4sensorhub_stillmode.c
@@ -0,0 +1,425 @@
+/*
+ * 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/kernel.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/input.h>
+#include <linux/wakelock.h>
+#include <linux/workqueue.h>
+#include <linux/m4sensorhub.h>
+#include <linux/slab.h>
+
+#define STILLMODE_CLIENT_DRIVER_NAME "m4sensorhub_stillmode"
+#define STILLMODE_DEFAULT_TIMEOUT 600 /* 10 minutes */
+
+static DEFINE_MUTEX(state_access);
+
+enum m4_stillmode_type {
+ MOTION,
+ STILL,
+};
+
+struct stillmode_client {
+ struct m4sensorhub_data *m4sensorhub;
+ struct input_dev *input_dev;
+ enum m4_stillmode_type state;
+ struct wake_lock wakelock;
+ struct work_struct queued_work;
+ u16 timeout;
+};
+
+static struct stillmode_client *g_stillmode_data;
+
+static int stillmode_set_timeout(struct stillmode_client *stillmode_client_data,
+ u16 timeout)
+{
+ int ret;
+
+ ret = m4sensorhub_reg_write(stillmode_client_data->m4sensorhub,
+ M4SH_REG_POWER_STILLMODETIMEOUT,
+ (char *)&timeout, m4sh_no_mask);
+ if (ret == m4sensorhub_reg_getsize(stillmode_client_data->m4sensorhub,
+ M4SH_REG_POWER_STILLMODETIMEOUT)) {
+ stillmode_client_data->timeout = timeout;
+ ret = 0;
+ } else
+ ret = -EIO;
+
+ return ret;
+
+}
+
+static void stillmode_set_state(struct stillmode_client *stillmode_client_data,
+ enum m4_stillmode_type state)
+{
+ mutex_lock(&state_access);
+ if (stillmode_client_data->state == state) {
+ mutex_unlock(&state_access);
+ printk(KERN_WARNING "M4SH duplicate stillmode update (%s)\n",
+ (state == STILL) ? "still" : "moving");
+ } else {
+ stillmode_client_data->state = state;
+ mutex_unlock(&state_access);
+
+ /* Hold a 500ms wakelock to let data get to KineticManager */
+ wake_lock_timeout(&stillmode_client_data->wakelock, 0.5 * HZ);
+
+ input_report_switch(stillmode_client_data->input_dev,
+ SW_STILL_MODE,
+ (stillmode_client_data->state == STILL));
+ input_sync(stillmode_client_data->input_dev);
+ printk(KERN_INFO "stillmode state changed to %s (%d)\n",
+ (state == STILL) ? "still" : "moving", state);
+ }
+}
+
+static int m4_stillmode_exit(void)
+{
+ struct stillmode_client *stillmode_client_data = g_stillmode_data;
+ int ret = 0;
+
+ KDEBUG(M4SH_INFO, "Resetting stillmode timer\n");
+
+ /* writing timeout value to M4 resets its timer */
+ ret = stillmode_set_timeout(stillmode_client_data,
+ stillmode_client_data->timeout);
+ if (ret == 0) {
+ if (stillmode_client_data->state == STILL)
+ stillmode_set_state(stillmode_client_data, MOTION);
+ } else
+ KDEBUG(M4SH_ERROR, "M4SH Error setting timeout (%d)\n", ret);
+
+ return ret;
+}
+
+
+int m4sensorhub_stillmode_exit(void)
+{
+ return m4_stillmode_exit();
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_stillmode_exit);
+
+
+static void m4sensorhub_stillmode_work(struct work_struct *work)
+{
+ m4sensorhub_stillmode_exit();
+}
+
+static void m4_handle_stillmode_irq(enum m4sensorhub_irqs int_event,
+ void *stillmode_data)
+{
+ struct stillmode_client *stillmode_client_data = stillmode_data;
+ enum m4_stillmode_type new_state;
+
+ KDEBUG(M4SH_INFO, "%s() got irq %d (%s)\n", __func__, int_event,
+ int_event == M4SH_IRQ_STILL_DETECTED ? "STILL_MODE" : "MOTION_MODE");
+
+ switch (int_event) {
+ case (M4SH_IRQ_STILL_DETECTED):
+ new_state = STILL;
+ break;
+ case (M4SH_IRQ_MOTION_DETECTED):
+ new_state = MOTION;
+ break;
+ default:
+ printk(KERN_ERR "%s() Unexpected irq: %d\n",
+ __func__, int_event);
+ return;
+ break;
+ }
+
+ stillmode_set_state(stillmode_client_data, new_state);
+}
+
+static ssize_t m4_stillmode_getstate(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct stillmode_client *stillmode_client_data =
+ platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%d \n", stillmode_client_data->state);
+}
+
+static ssize_t m4_stillmode_setstate(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct stillmode_client *stillmode_client_data =
+ platform_get_drvdata(pdev);
+ long value;
+ int ret = size;
+
+ if (((strict_strtoul(buf, 10, &value)) < 0) ||
+ (value != MOTION)) {
+ KDEBUG(M4SH_ERROR, "M4SH stillmode invalid value: %ld. Only "
+ "%d is allowed\n", value, MOTION);
+ return -EINVAL;
+ }
+
+ if (value != stillmode_client_data->state)
+ return m4sensorhub_stillmode_exit();
+
+ return ret;
+}
+
+static ssize_t m4_stillmode_get_timeout(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct stillmode_client *stillmode_client_data =
+ platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%d \n", stillmode_client_data->timeout);
+}
+
+static ssize_t m4_stillmode_set_timeout(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct stillmode_client *stillmode_client_data =
+ platform_get_drvdata(pdev);
+ long value;
+ int ret;
+
+ if (((strict_strtoul(buf, 10, &value)) < 0) ||
+ (value < 0) || (value > USHRT_MAX)) {
+ KDEBUG(M4SH_ERROR, "M4SH stillmode invalid timeout: %ld\n",
+ value);
+ return -EINVAL;
+ }
+
+ KDEBUG(M4SH_DEBUG, "%s() setting timeout to %ld\n", __func__, value);
+
+ ret = stillmode_set_timeout(stillmode_client_data, value);
+
+ return ((ret == 0) ? size : ret);
+}
+
+static DEVICE_ATTR(state, 0664, m4_stillmode_getstate,
+ m4_stillmode_setstate);
+static DEVICE_ATTR(timeout, 0664, m4_stillmode_get_timeout,
+ m4_stillmode_set_timeout);
+
+static int stillmode_client_probe(struct platform_device *pdev)
+{
+ int ret = -1;
+ struct stillmode_client *stillmode_client_data;
+ struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata();
+
+ if (!m4sensorhub)
+ return -EFAULT;
+
+ stillmode_client_data = kzalloc(sizeof(*stillmode_client_data),
+ GFP_KERNEL);
+ if (!stillmode_client_data)
+ return -ENOMEM;
+
+ g_stillmode_data = stillmode_client_data;
+ stillmode_client_data->m4sensorhub = m4sensorhub;
+ platform_set_drvdata(pdev, stillmode_client_data);
+ stillmode_client_data->state = MOTION;
+ stillmode_client_data->timeout = STILLMODE_DEFAULT_TIMEOUT;
+
+ stillmode_client_data->input_dev = input_allocate_device();
+ if (!stillmode_client_data->input_dev) {
+ ret = -ENOMEM;
+ KDEBUG(M4SH_ERROR, "%s: input device allocate failed: %d\n",
+ __func__, ret);
+ goto free_memory;
+ }
+
+ stillmode_client_data->input_dev->name = STILLMODE_CLIENT_DRIVER_NAME;
+ set_bit(EV_SW, stillmode_client_data->input_dev->evbit);
+ set_bit(SW_STILL_MODE, stillmode_client_data->input_dev->swbit);
+
+ if (input_register_device(stillmode_client_data->input_dev)) {
+ KDEBUG(M4SH_ERROR, "%s: input device register failed\n",
+ __func__);
+ input_free_device(stillmode_client_data->input_dev);
+ goto free_memory;
+ }
+
+ wake_lock_init(&stillmode_client_data->wakelock, WAKE_LOCK_SUSPEND,
+ STILLMODE_CLIENT_DRIVER_NAME);
+
+ INIT_WORK(&stillmode_client_data->queued_work,
+ m4sensorhub_stillmode_work);
+
+ ret = m4sensorhub_irq_register(m4sensorhub, M4SH_IRQ_STILL_DETECTED,
+ m4_handle_stillmode_irq,
+ stillmode_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering still mode IRQ: "
+ "%d\n", ret);
+ goto destroy_wakelock;
+ }
+ ret = m4sensorhub_irq_enable(m4sensorhub, M4SH_IRQ_STILL_DETECTED);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error enabling still mode int: "
+ "%d\n", ret);
+ goto unregister_still_irq;
+ }
+
+ ret = m4sensorhub_irq_register(m4sensorhub, M4SH_IRQ_MOTION_DETECTED,
+ m4_handle_stillmode_irq,
+ stillmode_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering moving mode IRQ: "
+ "%d\n", ret);
+ goto disable_still_irq;
+ }
+ ret = m4sensorhub_irq_enable(m4sensorhub, M4SH_IRQ_MOTION_DETECTED);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error enabling moving mode int: "
+ "%d\n", ret);
+ goto unregister_moving_irq;
+ }
+
+ if (device_create_file(&pdev->dev, &dev_attr_state)) {
+ KDEBUG(M4SH_ERROR, "Error creating stillmode sys entry\n");
+ ret = -1;
+ goto disable_moving_irq;
+ }
+
+ if (device_create_file(&pdev->dev, &dev_attr_timeout)) {
+ KDEBUG(M4SH_ERROR, "Error creating timeout sys entry\n");
+ ret = -1;
+ goto remove_stillmode_sysfs;
+ }
+
+ /* initialize timer on M4 */
+ m4sensorhub_stillmode_exit();
+
+ KDEBUG(M4SH_INFO, "Initialized %s driver\n",
+ STILLMODE_CLIENT_DRIVER_NAME);
+
+ return 0;
+
+remove_stillmode_sysfs:
+ device_remove_file(&pdev->dev, &dev_attr_state);
+disable_moving_irq:
+ m4sensorhub_irq_disable(m4sensorhub, M4SH_IRQ_MOTION_DETECTED);
+unregister_moving_irq:
+ m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_MOTION_DETECTED);
+disable_still_irq:
+ m4sensorhub_irq_disable(m4sensorhub, M4SH_IRQ_STILL_DETECTED);
+unregister_still_irq:
+ m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_STILL_DETECTED);
+destroy_wakelock:
+ wake_lock_destroy(&stillmode_client_data->wakelock);
+ input_unregister_device(stillmode_client_data->input_dev);
+free_memory:
+ platform_set_drvdata(pdev, NULL);
+ m4sensorhub->pdev->stillmode_exit = NULL;
+ stillmode_client_data->m4sensorhub = NULL;
+ kfree(stillmode_client_data);
+ g_stillmode_data = NULL;
+
+ return ret;
+}
+
+static int __exit stillmode_client_remove(struct platform_device *pdev)
+{
+ struct stillmode_client *stillmode_client_data =
+ platform_get_drvdata(pdev);
+ struct m4sensorhub_data *m4sensorhub =
+ stillmode_client_data->m4sensorhub;
+
+ device_remove_file(&pdev->dev, &dev_attr_timeout);
+ device_remove_file(&pdev->dev, &dev_attr_state);
+ m4sensorhub_irq_disable(m4sensorhub, M4SH_IRQ_MOTION_DETECTED);
+ m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_MOTION_DETECTED);
+ m4sensorhub_irq_disable(m4sensorhub, M4SH_IRQ_STILL_DETECTED);
+ m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_STILL_DETECTED);
+ wake_lock_destroy(&stillmode_client_data->wakelock);
+ input_unregister_device(stillmode_client_data->input_dev);
+ platform_set_drvdata(pdev, NULL);
+ m4sensorhub->pdev->stillmode_exit = NULL;
+ stillmode_client_data->m4sensorhub = NULL;
+ kfree(stillmode_client_data);
+ g_stillmode_data = NULL;
+
+ return 0;
+}
+
+static void stillmode_client_shutdown(struct platform_device *pdev)
+{
+ return;
+}
+#ifdef CONFIG_PM
+static int stillmode_client_suspend(struct platform_device *pdev,
+ pm_message_t message)
+{
+ return 0;
+}
+
+static int stillmode_client_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#else
+#define stillmode_client_suspend NULL
+#define stillmode_client_resume NULL
+#endif
+
+
+static struct of_device_id m4stillmode_match_tbl[] = {
+ { .compatible = "mot,m4stillmode" },
+ {},
+};
+
+static struct platform_driver stillmode_client_driver = {
+ .probe = stillmode_client_probe,
+ .remove = __exit_p(stillmode_client_remove),
+ .shutdown = stillmode_client_shutdown,
+ .suspend = stillmode_client_suspend,
+ .resume = stillmode_client_resume,
+ .driver = {
+ .name = STILLMODE_CLIENT_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(m4stillmode_match_tbl),
+ },
+};
+
+static int __init stillmode_client_init(void)
+{
+ return platform_driver_register(&stillmode_client_driver);
+}
+
+static void __exit stillmode_client_exit(void)
+{
+ platform_driver_unregister(&stillmode_client_driver);
+}
+
+module_init(stillmode_client_init);
+module_exit(stillmode_client_exit);
+
+MODULE_ALIAS("platform:stillmode_client");
+MODULE_DESCRIPTION("M4 sensorhub still mode client driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/misc/m4sensorhub_tmp006.c b/drivers/misc/m4sensorhub_tmp006.c
new file mode 100644
index 00000000000..44cb293f9a4
--- /dev/null
+++ b/drivers/misc/m4sensorhub_tmp006.c
@@ -0,0 +1,432 @@
+/*
+ * 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/slab.h>
+#include <linux/uaccess.h>
+#include <linux/m4sensorhub.h>
+#include <linux/m4sensorhub_client_ioctl.h>
+#include <linux/m4sensorhub/MemMapTempSensor.h>
+
+#define TMP_CLIENT_DRIVER_NAME "m4sensorhub_tmp006"
+#define TMP_MAX 1250
+#define TMP_MIN -400
+
+struct tmp_client {
+ struct m4sensorhub_data *m4sensorhub;
+ struct input_dev *input_dev;
+ int internal_tmp;
+ int external_tmp;
+ signed short samplerate;
+};
+
+struct tmp_client *misc_tmp_data;
+
+static int temperature_client_open(struct inode *inode, struct file *file)
+{
+ int err = 0;
+
+ err = nonseekable_open(inode, file);
+ if (err < 0) {
+ KDEBUG(M4SH_DEBUG, "temperature_clientopen failed\n");
+ return err;
+ }
+ file->private_data = misc_tmp_data;
+
+ return 0;
+}
+
+static int temperature_client_close(struct inode *inode, struct file *file)
+{
+ KDEBUG(M4SH_DEBUG, "temperature_client in %s\n", __func__);
+ return 0;
+}
+
+static void m4_report_temp_inputevent(struct tmp_client *tmp_data)
+{
+ input_report_abs(tmp_data->input_dev, ABS_THROTTLE,
+ tmp_data->external_tmp);
+ input_sync(tmp_data->input_dev);
+}
+
+static void m4_read_temp_data(struct tmp_client *tmp_data)
+{
+ sTempData tmp;
+
+ m4sensorhub_reg_read(tmp_data->m4sensorhub,
+ M4SH_REG_TEMP_EXTRNLTEMP, (char *)&tmp.extrnlTemp);
+ m4sensorhub_reg_read(tmp_data->m4sensorhub,
+ M4SH_REG_TEMP_INTRNLTEMP, (char *)&tmp.intrnlTemp);
+ tmp_data->internal_tmp = tmp.intrnlTemp;
+ tmp_data->external_tmp = tmp.extrnlTemp;
+}
+
+static void m4_handle_tmp_irq(enum m4sensorhub_irqs int_event,
+ void *tmp_data)
+{
+ struct tmp_client *tmp_client_data = tmp_data;
+
+ m4_read_temp_data(tmp_client_data);
+ m4_report_temp_inputevent(tmp_client_data);
+}
+
+static int m4_set_tmp_samplerate(
+ struct tmp_client *tmp_client_data,
+ signed int samplerate)
+{
+ int ret = 0;
+
+ if (samplerate != tmp_client_data->samplerate) {
+ ret = m4sensorhub_reg_write(tmp_client_data->m4sensorhub,
+ M4SH_REG_TEMP_SAMPLERATE,
+ (char *)&samplerate, m4sh_no_mask);
+ if (ret != m4sensorhub_reg_getsize(
+ tmp_client_data->m4sensorhub,
+ M4SH_REG_TEMP_SAMPLERATE)) {
+ KDEBUG(M4SH_ERROR, "Unable to set delay \
+ for temperature sensor\n");
+ return ret;
+ }
+
+ KDEBUG(M4SH_DEBUG, "%s() updating samplerate from %d to %d\n",
+ __func__, tmp_client_data->samplerate,
+ samplerate);
+ tmp_client_data->samplerate = samplerate;
+
+ if (samplerate >= 0)
+ ret = m4sensorhub_irq_enable(
+ tmp_client_data->m4sensorhub,
+ M4SH_IRQ_TMP_DATA_READY);
+ else
+ ret = m4sensorhub_irq_disable(
+ tmp_client_data->m4sensorhub,
+ M4SH_IRQ_TMP_DATA_READY);
+ if (ret != 0)
+ KDEBUG(M4SH_ERROR, "Unable to enable/disable \
+ temperature irq\n");
+ }
+
+ return ret;
+}
+
+
+/*
+ * Handle commands from user-space.
+ */
+static long temperature_client_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ int delay = 0;
+ static int status;
+
+ void __user *argp = (void __user *)arg;
+ struct tmp_client *tmp_data = filp->private_data;
+
+ switch (cmd) {
+ case M4_SENSOR_IOCTL_GET_TEMPRATURE:
+ m4_read_temp_data(tmp_data);
+ m4_report_temp_inputevent(tmp_data);
+ break;
+ case M4_SENSOR_IOCTL_SET_DELAY:
+ if (copy_from_user(&delay, argp, sizeof(delay)))
+ return -EFAULT;
+ if (delay >= 0)
+ ret = m4_set_tmp_samplerate(tmp_data, delay);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error setting samplerate to %d"
+ " (%d)\n", delay, 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;
+ default:
+ KDEBUG(M4SH_ERROR, "Invalid IOCTL Command %d\n", cmd);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static ssize_t GetExternalTemp(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct tmp_client *tmp_client_data = platform_get_drvdata(pdev);
+
+ m4_read_temp_data(tmp_client_data);
+ KDEBUG(M4SH_DEBUG, "%s : external temp = %d",
+ __func__, tmp_client_data->external_tmp);
+ return sprintf(buf, "%d\n", tmp_client_data->external_tmp);
+}
+
+static ssize_t GetInternalTemp(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct tmp_client *tmp_client_data = platform_get_drvdata(pdev);
+
+ m4_read_temp_data(tmp_client_data);
+ KDEBUG(M4SH_DEBUG, "%s : internal temp = %d",
+ __func__, tmp_client_data->internal_tmp);
+ return sprintf(buf, "%d\n", tmp_client_data->internal_tmp);
+}
+
+static ssize_t tmp_get_loglevel(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned long long loglevel;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct tmp_client *tmp_client_data = platform_get_drvdata(pdev);
+
+ m4sensorhub_reg_read(tmp_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&loglevel);
+ loglevel = get_log_level(loglevel, TMP_MASK_BIT_1);
+ return sprintf(buf, "%llu\n", loglevel);
+}
+
+static ssize_t tmp_set_loglevel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned long level;
+ unsigned long long mask = 0, newlevel;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct tmp_client *tmp_client_data = platform_get_drvdata(pdev);
+
+ if ((strict_strtoul(buf, 10, &level)) < 0)
+ return -1;
+ if (level > M4_MAX_LOG_LEVEL) {
+ KDEBUG(M4SH_ERROR, " Invalid log level - %d\n", (int)level);
+ return -1;
+ }
+ mask = (1ULL << TMP_MASK_BIT_1) | (1ULL << TMP_MASK_BIT_2);
+ newlevel = (level << TMP_MASK_BIT_1);
+ return m4sensorhub_reg_write(tmp_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&newlevel, (unsigned char *)&mask);
+}
+
+static DEVICE_ATTR(internal, 0444, GetInternalTemp, NULL);
+static DEVICE_ATTR(external, 0444, GetExternalTemp, NULL);
+static DEVICE_ATTR(LogLevel, 0444, tmp_get_loglevel, tmp_set_loglevel);
+
+static const struct file_operations temperature_client_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = temperature_client_ioctl,
+ .open = temperature_client_open,
+ .release = temperature_client_close,
+};
+
+static struct miscdevice temperature_client_miscdrv = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = TMP_CLIENT_DRIVER_NAME,
+ .fops = &temperature_client_fops,
+};
+
+static int temperature_client_probe(struct platform_device *pdev)
+{
+ int ret = -1;
+ struct tmp_client *tmp_client_data;
+ struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata();
+
+ if (!m4sensorhub)
+ return -EFAULT;
+
+ tmp_client_data = kzalloc(sizeof(*tmp_client_data), GFP_KERNEL);
+ if (!tmp_client_data)
+ return -ENOMEM;
+
+ tmp_client_data->m4sensorhub = m4sensorhub;
+ platform_set_drvdata(pdev, tmp_client_data);
+
+ tmp_client_data->input_dev = input_allocate_device();
+ if (!tmp_client_data->input_dev) {
+ ret = -ENOMEM;
+ KDEBUG(M4SH_ERROR, "%s: input device allocate failed: %d\n",
+ __func__, ret);
+ goto free_memory;
+ }
+
+ tmp_client_data->input_dev->name = TMP_CLIENT_DRIVER_NAME;
+ set_bit(EV_ABS, tmp_client_data->input_dev->evbit);
+ set_bit(ABS_THROTTLE, tmp_client_data->input_dev->absbit);
+ input_set_abs_params(tmp_client_data->input_dev, ABS_THROTTLE,
+ TMP_MIN, TMP_MAX, 0, 0);
+
+ if (input_register_device(tmp_client_data->input_dev)) {
+ KDEBUG(M4SH_INFO, "%s: input device register failed\n",
+ __func__);
+ input_free_device(tmp_client_data->input_dev);
+ goto free_memory;
+ }
+
+ ret = misc_register(&temperature_client_miscdrv);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering %s driver\n",
+ TMP_CLIENT_DRIVER_NAME);
+ goto unregister_input_device;
+ }
+ misc_tmp_data = tmp_client_data;
+ ret = m4sensorhub_irq_register(m4sensorhub, M4SH_IRQ_TMP_DATA_READY,
+ m4_handle_tmp_irq,
+ tmp_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering int %d (%d)\n",
+ M4SH_IRQ_TMP_DATA_READY, ret);
+ goto unregister_misc_device;
+ }
+
+ if (device_create_file(&pdev->dev, &dev_attr_internal)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n",
+ TMP_CLIENT_DRIVER_NAME);
+ ret = -1;
+ goto unregister_irq;
+ }
+
+ if (device_create_file(&pdev->dev, &dev_attr_external)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n",
+ TMP_CLIENT_DRIVER_NAME);
+ ret = -1;
+ goto remove_internal_device_file;
+ }
+ if (device_create_file(&pdev->dev, &dev_attr_LogLevel)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n",
+ TMP_CLIENT_DRIVER_NAME);
+ ret = -1;
+ goto remove_external_device_file;
+ }
+ KDEBUG(M4SH_INFO, "Initialized %s driver\n", TMP_CLIENT_DRIVER_NAME);
+ return 0;
+
+remove_external_device_file:
+ device_remove_file(&pdev->dev, &dev_attr_external);
+remove_internal_device_file:
+ device_remove_file(&pdev->dev, &dev_attr_internal);
+unregister_irq:
+ m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_TMP_DATA_READY);
+unregister_misc_device:
+ misc_tmp_data = NULL;
+ misc_deregister(&temperature_client_miscdrv);
+unregister_input_device:
+ input_unregister_device(tmp_client_data->input_dev);
+free_memory:
+ platform_set_drvdata(pdev, NULL);
+ tmp_client_data->m4sensorhub = NULL;
+ kfree(tmp_client_data);
+ tmp_client_data = NULL;
+ return ret;
+}
+
+static int __exit temperature_client_remove(struct platform_device *pdev)
+{
+ struct tmp_client *tmp_client_data = platform_get_drvdata(pdev);
+
+ device_remove_file(&pdev->dev, &dev_attr_LogLevel);
+ device_remove_file(&pdev->dev, &dev_attr_external);
+ device_remove_file(&pdev->dev, &dev_attr_internal);
+ m4sensorhub_irq_disable(tmp_client_data->m4sensorhub,
+ M4SH_IRQ_TMP_DATA_READY);
+ m4sensorhub_irq_unregister(tmp_client_data->m4sensorhub,
+ M4SH_IRQ_TMP_DATA_READY);
+ misc_tmp_data = NULL;
+ misc_deregister(&temperature_client_miscdrv);
+ input_unregister_device(tmp_client_data->input_dev);
+ platform_set_drvdata(pdev, NULL);
+ tmp_client_data->m4sensorhub = NULL;
+ kfree(tmp_client_data);
+ tmp_client_data = NULL;
+ return 0;
+}
+
+static void temperature_client_shutdown(struct platform_device *pdev)
+{
+ return;
+}
+
+#ifdef CONFIG_PM
+
+static int temperature_client_suspend(struct platform_device *pdev,
+ pm_message_t message)
+{
+ struct tmp_client *tmp_client_data = platform_get_drvdata(pdev);
+
+ return m4_set_tmp_samplerate(tmp_client_data, -1);
+}
+
+static int temperature_client_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+
+#else
+#define temperature_client_suspend NULL
+#define temperature_client_resume NULL
+#endif
+
+static struct of_device_id m4temp_match_tbl[] = {
+ { .compatible = "mot,m4temperature" },
+ {},
+};
+
+static struct platform_driver temp_client_driver = {
+ .probe = temperature_client_probe,
+ .remove = __exit_p(temperature_client_remove),
+ .shutdown = temperature_client_shutdown,
+ .suspend = temperature_client_suspend,
+ .resume = temperature_client_resume,
+ .driver = {
+ .name = TMP_CLIENT_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(m4temp_match_tbl),
+ },
+};
+
+static int __init temperature_client_init(void)
+{
+ return platform_driver_register(&temp_client_driver);
+}
+
+static void __exit temperature_client_exit(void)
+{
+ platform_driver_unregister(&temp_client_driver);
+}
+
+module_init(temperature_client_init);
+module_exit(temperature_client_exit);
+
+MODULE_ALIAS("platform:temperature_client");
+MODULE_DESCRIPTION("M4 Sensor Hub driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/misc/m4sensorhub_wrist.c b/drivers/misc/m4sensorhub_wrist.c
new file mode 100644
index 00000000000..faed5f4068b
--- /dev/null
+++ b/drivers/misc/m4sensorhub_wrist.c
@@ -0,0 +1,836 @@
+/*
+ * Copyright (C) 2013 Motorola Mobility, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#include "m4sensorhub_wrist.h"
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/m4sensorhub.h>
+#include <linux/firmware.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/m4sensorhub_gpio.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+
+static struct platform_driver m4wrist_client_driver;
+static int m4wrist_probe(struct platform_device *pdev);
+static int m4wrist_remove(struct platform_device *pdev);
+static int m4wrist_init(void);
+static void m4wrist_exit(void);
+static void m4wrist_free(struct m4wrist_driver_data *dd);
+static int m4wrist_gpio_init(struct m4wrist_driver_data *dd);
+static int m4wrist_request_firmware(struct m4wrist_driver_data *dd);
+static int m4wrist_request_irq(struct m4wrist_driver_data *dd);
+static void m4wrist_firmware_callback(const struct firmware *fw,
+ void *context);
+static void m4wrist_irq(enum m4sensorhub_irqs event, void *context);
+static int m4wrist_gpio_control(struct m4wrist_driver_data *dd);
+static int m4wrist_gpio_release(struct m4wrist_driver_data *dd);
+static int m4wrist_reflash_ic(struct m4wrist_driver_data *dd);
+static int m4wrist_enter_reset_mode(struct m4wrist_driver_data *dd);
+static int m4wrist_erase_flash(struct m4wrist_driver_data *dd);
+static int m4wrist_program_image(struct m4wrist_driver_data *dd);
+static void m4wrist_send_bitstream(struct m4wrist_driver_data *dd,
+ uint8_t *stream, uint32_t bits);
+static void m4wrist_toggle_clock(struct m4wrist_driver_data *dd, int cycles);
+static int m4wrist_wait_poll(struct m4wrist_driver_data *dd);
+static int m4wrist_read_id_word(struct m4wrist_driver_data *dd, uint8_t *data);
+
+
+static struct of_device_id m4wrist_match_tbl[] = {
+ { .compatible = "mot,m4wrist" },
+ {},
+};
+
+static struct platform_driver m4wrist_client_driver = {
+ .probe = m4wrist_probe,
+ .remove = m4wrist_remove,
+ .driver = {
+ .name = "m4sensorhub_wrist",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(m4wrist_match_tbl),
+ },
+};
+
+static int m4wrist_probe(struct platform_device *pdev)
+{
+ int err = 0;
+ struct m4wrist_driver_data *dd = NULL;
+ struct device_node *node = pdev->dev.of_node;
+
+ if (!node) {
+ pr_warn("devtree node not present!\n");
+ return -ENODEV;
+ }
+ dd = kzalloc(sizeof(struct m4wrist_driver_data), GFP_KERNEL);
+ if (dd == NULL) {
+ printk(KERN_ERR "%s: Unable to create driver data.\n",
+ __func__);
+ err = -ENOMEM;
+ goto m4wrist_probe_fail;
+ }
+
+ dd->pdev = pdev;
+ platform_set_drvdata(pdev, dd);
+
+ dd->mutex = kzalloc(sizeof(struct mutex), GFP_KERNEL);
+ if (dd->mutex == NULL) {
+ printk(KERN_ERR "%s: Unable to create mutex lock.\n",
+ __func__);
+ err = -ENOMEM;
+ goto m4wrist_probe_fail;
+ }
+ mutex_init(dd->mutex);
+
+ dd->gpio_xres = of_get_named_gpio_flags(node,
+ "mot,wrist_xres", 0, NULL);
+ if (dd->gpio_xres <= 0) {
+ printk(KERN_ERR "%s: gpio_xres is invalid.\n", __func__);
+ err = -EINVAL;
+ goto m4wrist_probe_fail;
+ }
+ dd->gpio_clk = of_get_named_gpio_flags(node, "mot,wrist_clk", 0, NULL);
+ if (dd->gpio_clk <= 0) {
+ printk(KERN_ERR "%s: gpio_clk is invalid.\n", __func__);
+ err = -EINVAL;
+ goto m4wrist_probe_fail;
+ }
+ dd->gpio_data = of_get_named_gpio_flags(node,
+ "mot,wrist_data", 0, NULL);
+ if (dd->gpio_data <= 0) {
+ printk(KERN_ERR "%s: gpio_data is invalid.\n", __func__);
+ err = -EINVAL;
+ goto m4wrist_probe_fail;
+ }
+
+ err = m4wrist_gpio_init(dd);
+ if (err < 0)
+ goto m4wrist_probe_fail;
+
+ err = m4wrist_request_firmware(dd);
+ if (err < 0)
+ goto m4wrist_probe_fail;
+
+ err = m4wrist_request_irq(dd);
+ if (err < 0)
+ goto m4wrist_probe_fail;
+
+ goto m4wrist_probe_pass;
+
+m4wrist_probe_fail:
+ m4wrist_free(dd);
+ printk(KERN_ERR "%s: Probe failed with error code %d.\n",
+ __func__, err);
+ return err;
+
+m4wrist_probe_pass:
+ return 0;
+}
+
+static void m4wrist_free(struct m4wrist_driver_data *dd)
+{
+ if (dd != NULL) {
+ kfree(dd->img);
+
+ if (dd->client != NULL) {
+ m4sensorhub_irq_disable(dd->client,
+ M4SH_IRQ_WRIST_READY);
+ m4sensorhub_irq_unregister(dd->client,
+ M4SH_IRQ_WRIST_READY);
+ }
+
+ kfree(dd->mutex);
+ platform_set_drvdata(dd->pdev, NULL);
+ kfree(dd);
+ }
+
+ return;
+}
+
+static int m4wrist_gpio_init(struct m4wrist_driver_data *dd)
+{
+ int err = 0;
+ int i = 0;
+ int gpio_nums[3] = {dd->gpio_xres, dd->gpio_clk, dd->gpio_data};
+ char *gpio_names[3] = {"wrist_xres", "wrist_clk", "wrist_data"};
+
+ for (i = 0; i < 3; i++) {
+ err = gpio_request(gpio_nums[i], gpio_names[i]);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to request %s.\n",
+ __func__, gpio_names[i]);
+ i--;
+ goto m4wrist_gpio_init_fail;
+ }
+
+ err = gpio_direction_input(gpio_nums[i]);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to make %s an input.\n",
+ __func__, gpio_names[i]);
+ gpio_free(gpio_nums[i]);
+ i--;
+ goto m4wrist_gpio_init_fail;
+ }
+ }
+
+ goto m4wrist_gpio_init_exit;
+
+m4wrist_gpio_init_fail:
+ while (i >= 0) {
+ gpio_free(gpio_nums[i]);
+ i--;
+ }
+
+m4wrist_gpio_init_exit:
+ return err;
+}
+
+static int m4wrist_request_firmware(struct m4wrist_driver_data *dd)
+{
+ int err = 0;
+ const struct firmware *fw = NULL;
+
+ err = request_firmware(&fw,
+ "m4sensorhub_wrist.bin", &(dd->pdev->dev));
+ if (err < 0) {
+ printk(KERN_ERR "%s: Firmware request failed.\n", __func__);
+ goto m4wrist_request_firmware_fail;
+ }
+
+ m4wrist_firmware_callback(fw, dd);
+
+m4wrist_request_firmware_fail:
+ return err;
+}
+
+static int m4wrist_request_irq(struct m4wrist_driver_data *dd)
+{
+ int err = 0;
+
+ dd->client = m4sensorhub_client_get_drvdata();
+ if (dd->client == NULL) {
+ printk(KERN_ERR "%s: No client data retrieved.\n",
+ __func__);
+ err = -ENODATA;
+ goto m4wrist_request_irq_fail;
+ }
+
+ err = m4sensorhub_irq_register(dd->client,
+ M4SH_IRQ_WRIST_READY, m4wrist_irq, dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to register IRQ.\n", __func__);
+ dd->client = NULL;
+ goto m4wrist_request_irq_fail;
+ }
+
+ err = m4sensorhub_irq_enable(dd->client, M4SH_IRQ_WRIST_READY);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to enable IRQ.\n", __func__);
+ m4sensorhub_irq_unregister(dd->client, M4SH_IRQ_WRIST_READY);
+ dd->client = NULL;
+ goto m4wrist_request_irq_fail;
+ }
+
+m4wrist_request_irq_fail:
+ return err;
+}
+
+static void m4wrist_firmware_callback(const struct firmware *fw,
+ void *context)
+{
+ struct m4wrist_driver_data *dd = context;
+
+ mutex_lock(dd->mutex);
+
+ if (fw == NULL) {
+ printk(KERN_ERR "%s: No firmware received.\n", __func__);
+ goto m4wrist_firmware_callback_fail;
+ } else if (fw->data == NULL || fw->size == 0) {
+ printk(KERN_ERR "%s: No data found.\n", __func__);
+ goto m4wrist_firmware_callback_fail;
+ } else if (fw->data[0] < 4) {
+ printk(KERN_ERR "%s: Firmware header is too small.\n",
+ __func__);
+ goto m4wrist_firmware_callback_fail;
+ } else if (fw->data[0] >= fw->size) {
+ printk(KERN_ERR "%s: Firmware data is missing.\n", __func__);
+ goto m4wrist_firmware_callback_fail;
+ }
+
+ dd->size = fw->size - (fw->data[0] + 1);
+ dd->img = kzalloc(dd->size * sizeof(uint8_t), GFP_KERNEL);
+ if (dd->img == NULL) {
+ printk(KERN_ERR "%s: Failed to allocate memory for firmware.\n",
+ __func__);
+ goto m4wrist_firmware_callback_fail;
+ }
+ memcpy(dd->img, &(fw->data[fw->data[0] + 1]), dd->size);
+ dd->si_id[0] = fw->data[1];
+ dd->si_id[1] = fw->data[2];
+ dd->fw_ver[0] = fw->data[3];
+ dd->fw_ver[1] = fw->data[4];
+
+m4wrist_firmware_callback_fail:
+ release_firmware(fw);
+ mutex_unlock(dd->mutex);
+ return;
+}
+
+static int m4wrist_remove(struct platform_device *pdev)
+{
+ struct m4wrist_driver_data *dd = NULL;
+
+ dd = platform_get_drvdata(pdev);
+ if (dd != NULL) {
+ gpio_free(dd->gpio_xres);
+ gpio_free(dd->gpio_clk);
+ gpio_free(dd->gpio_data);
+ m4wrist_free(dd);
+ }
+
+ return 0;
+}
+
+static int m4wrist_init(void)
+{
+ return platform_driver_register(&m4wrist_client_driver);
+}
+
+static void m4wrist_exit(void)
+{
+ platform_driver_unregister(&m4wrist_client_driver);
+}
+
+module_init(m4wrist_init);
+module_exit(m4wrist_exit);
+
+static void m4wrist_irq(enum m4sensorhub_irqs event, void *context)
+{
+ struct m4wrist_driver_data *dd = context;
+ int err = 0;
+ uint8_t irq_reason = 0x00;
+ uint8_t val[2] = {0x00, 0x00};
+ uint8_t mask[2] = {0xFF, 0xFF};
+ mutex_lock(dd->mutex);
+
+ if (dd->img == NULL) {
+ printk(KERN_ERR "%s: Firmware image is missing--%s.\n",
+ __func__, "unable to respond to interrupts");
+ err = -ENODATA;
+ goto m4wrist_irq_fail;
+ }
+
+ err = m4sensorhub_reg_read(dd->client,
+ M4SH_REG_WRIST_INTERRUPTREASON, &irq_reason);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to read interrupt reason.\n",
+ __func__);
+ goto m4wrist_irq_fail;
+ } else if (err < 1) {
+ printk(KERN_ERR "%s: Read %d bytes instead of 1.\n",
+ __func__, err);
+ err = -EINVAL;
+ goto m4wrist_irq_fail;
+ }
+
+ switch (irq_reason) {
+ case 0x00:
+ err = m4wrist_gpio_control(dd);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Failed to take control of GPIO lines.\n",
+ __func__);
+ goto m4wrist_irq_fail;
+ }
+
+ err = m4wrist_reflash_ic(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to reflash IC.\n",
+ __func__);
+ goto m4wrist_irq_fail;
+ }
+
+ err = m4wrist_gpio_release(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to release GPIO lines.\n",
+ __func__);
+ goto m4wrist_irq_fail;
+ }
+
+ val[0] = 0x01;
+ err = m4sensorhub_reg_write(dd->client,
+ M4SH_REG_WRIST_HOSTRESPONSE, &(val[0]), &(mask[0]));
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to write host response.\n",
+ __func__);
+ goto m4wrist_irq_fail;
+ } else if (err < 1) {
+ printk(KERN_ERR "%s: Wrote %d bytes instead of 1.\n",
+ __func__, err);
+ err = -EINVAL;
+ goto m4wrist_irq_fail;
+ }
+ break;
+
+ case 0x01:
+ val[0] = dd->fw_ver[0];
+ val[1] = dd->fw_ver[1];
+ err = m4sensorhub_reg_write(dd->client,
+ M4SH_REG_WRIST_FMONFILE, &(val[0]), &(mask[0]));
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to write firmware version.\n",
+ __func__);
+ goto m4wrist_irq_fail;
+ } else if (err < 2) {
+ printk(KERN_ERR "%s: Wrote %d bytes instead of 2.\n",
+ __func__, err);
+ err = -EINVAL;
+ goto m4wrist_irq_fail;
+ }
+ break;
+
+ default:
+ printk(KERN_ERR "%s: Unexpected interrupt 0x%02X received.\n",
+ __func__, irq_reason);
+ err = -EINVAL;
+ goto m4wrist_irq_fail;
+ break;
+ }
+
+ goto m4wrist_irq_pass;
+
+m4wrist_irq_fail:
+ printk(KERN_ERR "%s: IRQ handler failed with error code %d.\n",
+ __func__, err);
+
+m4wrist_irq_pass:
+ mutex_unlock(dd->mutex);
+ return;
+}
+
+static int m4wrist_gpio_control(struct m4wrist_driver_data *dd)
+{
+ int err = 0;
+ int i = 0;
+ int gpio_nums[3] = {dd->gpio_xres, dd->gpio_clk, dd->gpio_data};
+ char *gpio_names[3] = {"wrist_xres", "wrist_clk", "wrist_data"};
+
+ for (i = 0; i < 3; i++) {
+ err = gpio_direction_output(gpio_nums[i], 0);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to take control of %s.\n",
+ __func__, gpio_names[i]);
+ goto m4wrist_gpio_control_fail;
+ }
+ }
+
+m4wrist_gpio_control_fail:
+ return err;
+}
+
+static int m4wrist_gpio_release(struct m4wrist_driver_data *dd)
+{
+ int err = 0;
+ int i = 0;
+ int gpio_nums[3] = {dd->gpio_xres, dd->gpio_clk, dd->gpio_data};
+ char *gpio_names[3] = {"wrist_xres", "wrist_clk", "wrist_data"};
+
+ for (i = 0; i < 3; i++) {
+ err = gpio_direction_input(gpio_nums[i]);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to release %s.\n",
+ __func__, gpio_names[i]);
+ goto m4wrist_gpio_release_fail;
+ }
+ }
+
+m4wrist_gpio_release_fail:
+ return err;
+}
+
+static int m4wrist_reflash_ic(struct m4wrist_driver_data *dd)
+{
+ int err = 0;
+
+ err = m4wrist_enter_reset_mode(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to enter programming mode.\n",
+ __func__);
+ goto m4wrist_reflash_ic_fail;
+ }
+
+ err = m4wrist_erase_flash(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to erase IC flash.\n", __func__);
+ goto m4wrist_reflash_ic_fail;
+ }
+
+ err = m4wrist_program_image(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to program firmware image",
+ __func__);
+ goto m4wrist_reflash_ic_fail;
+ }
+
+ gpio_set_value(dd->gpio_xres, 1);
+ udelay(100);
+ gpio_set_value(dd->gpio_xres, 0);
+
+m4wrist_reflash_ic_fail:
+ return err;
+}
+
+static int m4wrist_enter_reset_mode(struct m4wrist_driver_data *dd)
+{
+ int err = 0;
+ uint8_t silicon_id[2] = {0x00, 0x00};
+
+ msleep(20);
+ gpio_set_value(dd->gpio_xres, 1);
+ udelay(400);
+ gpio_set_value(dd->gpio_xres, 0);
+ udelay(1);
+
+ m4wrist_send_bitstream(dd, &(m4wrist_id_setup_1[0]),
+ M4WRIST_ID_SETUP_1_BITS);
+
+ err = m4wrist_wait_poll(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to wait-and-poll 1.\n", __func__);
+ goto m4wrist_enter_reset_mode_fail;
+ }
+
+ m4wrist_send_bitstream(dd, &(m4wrist_id_setup_2[0]),
+ M4WRIST_ID_SETUP_2_BITS);
+
+ err = m4wrist_wait_poll(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to wait-and-poll 2.\n", __func__);
+ goto m4wrist_enter_reset_mode_fail;
+ }
+
+ m4wrist_send_bitstream(dd, &(m4wrist_sync_enable[0]),
+ M4WRIST_SYNC_ENABLE_BITS);
+
+ err = m4wrist_read_id_word(dd, &(silicon_id[0]));
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to read silicon ID\n", __func__);
+ goto m4wrist_enter_reset_mode_fail;
+ }
+
+ m4wrist_send_bitstream(dd, &(m4wrist_sync_disable[0]),
+ M4WRIST_SYNC_DISABLE_BITS);
+
+ if (silicon_id[0] != dd->si_id[0] || silicon_id[1] != dd->si_id[1]) {
+ printk(KERN_ERR "%s: Silicon ID mismatch (read 0x%02X%02X).\n",
+ __func__, silicon_id[0], silicon_id[1]);
+ err = -EINVAL;
+ goto m4wrist_enter_reset_mode_fail;
+ }
+
+m4wrist_enter_reset_mode_fail:
+ return err;
+}
+
+static int m4wrist_erase_flash(struct m4wrist_driver_data *dd)
+{
+ int err = 0;
+
+ m4wrist_send_bitstream(dd, &(m4wrist_erase[0]),
+ M4WRIST_ERASE_BITS);
+
+ err = m4wrist_wait_poll(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to wait-and-poll.\n", __func__);
+ goto m4wrist_erase_flash_fail;
+ }
+
+m4wrist_erase_flash_fail:
+ return err;
+}
+
+static int m4wrist_program_image(struct m4wrist_driver_data *dd)
+{
+ int err = 0;
+ uint32_t iter = 0;
+ int i = 0;
+ uint8_t blk_num = 0x00;
+ uint8_t cur_addr = 0x00;
+ uint8_t cur_vector[3] = {0x00, 0x00, 0x00};
+ uint8_t cur_blk_data[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ uint8_t status[2] = {0x00, 0x00};
+
+ if (((dd->size % 128) != 0) || (dd->size == 0)) {
+ printk(KERN_ERR "%s: Firmware has invalid size of %u.\n",
+ __func__, dd->size);
+ err = -EINVAL;
+ goto m4wrist_program_image_fail;
+ }
+
+ printk(KERN_INFO "%s: Flashing version 0x%02X 0x%02X...\n", __func__,
+ dd->fw_ver[0], dd->fw_ver[1]);
+
+ while (iter < (dd->size - 1)) {
+ m4wrist_send_bitstream(dd, &(m4wrist_sync_enable[0]),
+ M4WRIST_SYNC_ENABLE_BITS);
+
+ m4wrist_send_bitstream(dd, &(m4wrist_read_write_setup[0]),
+ M4WRIST_READ_WRITE_SETUP_BITS);
+
+ cur_addr = 0x00;
+ for (i = 0; i < 128; i++) {
+ cur_vector[0] = 0x90 | ((cur_addr & 0x78) >> 3);
+ cur_vector[1] = ((cur_addr & 0x07) << 5) |
+ ((dd->img[iter] & 0xF8) >> 3);
+ cur_vector[2] = ((dd->img[iter] & 0x07) << 5) | 0x1C;
+ m4wrist_send_bitstream(dd, &(cur_vector[0]), 22);
+
+ cur_addr++;
+ iter++;
+ }
+
+ m4wrist_send_bitstream(dd, &(m4wrist_sync_enable[0]),
+ M4WRIST_SYNC_ENABLE_BITS);
+
+ cur_blk_data[0] = 0xDE;
+ cur_blk_data[1] = 0xE0;
+ cur_blk_data[2] = 0x1E;
+ cur_blk_data[3] = 0x7D;
+ cur_blk_data[4] = (blk_num & 0xFE) >> 1;
+ cur_blk_data[5] = ((blk_num & 0x01) << 7) | 0x70;
+ m4wrist_send_bitstream(dd, &(cur_blk_data[0]), 44);
+
+ m4wrist_send_bitstream(dd, &(m4wrist_sync_disable[0]),
+ M4WRIST_SYNC_DISABLE_BITS);
+
+ m4wrist_send_bitstream(dd, &(m4wrist_program_and_verify[0]),
+ M4WRIST_PROGRAM_AND_VERIFY_BITS);
+
+ err = m4wrist_wait_poll(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: %s for block 0x%02X.\n",
+ __func__, "Failed to wait-and-poll", blk_num);
+ goto m4wrist_program_image_fail;
+ }
+
+ m4wrist_send_bitstream(dd, &(m4wrist_sync_enable[0]),
+ M4WRIST_SYNC_ENABLE_BITS);
+
+ err = m4wrist_read_id_word(dd, &(status[0]));
+ if (err < 0) {
+ printk(KERN_ERR "%s: %s for block 0x%02X.\n",
+ __func__, "Failed to read status", blk_num);
+ }
+
+ m4wrist_send_bitstream(dd, &(m4wrist_sync_disable[0]),
+ M4WRIST_SYNC_DISABLE_BITS);
+
+ if (status[0] != 0x00) {
+ printk(KERN_ERR "%s: %s 0x%02X %s 0x%02X.\n", __func__,
+ "Programming block", blk_num,
+ "failed with error code", status[0]);
+ err = -EINVAL;
+ goto m4wrist_program_image_fail;
+ }
+
+ blk_num++;
+ }
+
+m4wrist_program_image_fail:
+ return err;
+}
+
+static void m4wrist_send_bitstream(struct m4wrist_driver_data *dd,
+ uint8_t *stream, uint32_t bits)
+{
+ int i = 0;
+ int j = 0;
+ uint32_t bits_sent = 0;
+
+ j = 7;
+ while (bits_sent < bits) {
+ if (stream[i] & (0x01 << j))
+ gpio_set_value(dd->gpio_data, 1);
+ else
+ gpio_set_value(dd->gpio_data, 0);
+
+ gpio_set_value(dd->gpio_clk, 1);
+ gpio_set_value(dd->gpio_clk, 0);
+
+ bits_sent++;
+ j--;
+ if (j < 0) {
+ j = 7;
+ i++;
+ }
+ }
+
+ return;
+}
+
+static void m4wrist_toggle_clock(struct m4wrist_driver_data *dd, int cycles)
+{
+ int i = 0;
+
+ for (i = 0; i < cycles; i++) {
+ gpio_set_value(dd->gpio_clk, 1);
+ gpio_set_value(dd->gpio_clk, 0);
+ }
+
+ return;
+}
+
+static int m4wrist_wait_poll(struct m4wrist_driver_data *dd)
+{
+ int err = 0;
+ int i = 0;
+ bool saw_event = false;
+ uint8_t clear[5] = {0x00, 0x00, 0x00, 0x00, 0x00};
+
+ err = gpio_direction_input(dd->gpio_data);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to release data line.\n", __func__);
+ goto m4wrist_wait_poll_fail;
+ }
+
+ udelay(1);
+
+ for (i = 0; i < 200000; i++) {
+ if (gpio_get_value(dd->gpio_data) == 1) {
+ saw_event = true;
+ break;
+ } else {
+ m4wrist_toggle_clock(dd, 1);
+ }
+ }
+
+ if (!saw_event) {
+ printk(KERN_ERR "%s: Timeout waiting for data high.\n",
+ __func__);
+ err = -ETIME;
+ goto m4wrist_wait_poll_fail;
+ }
+
+ saw_event = false;
+ for (i = 0; i < 200000; i++) {
+ if (gpio_get_value(dd->gpio_data) == 0) {
+ saw_event = true;
+ break;
+ } else {
+ udelay(1);
+ }
+ }
+
+ if (!saw_event) {
+ printk(KERN_ERR "%s: Timeout waiting for data low.\n",
+ __func__);
+ err = -ETIME;
+ goto m4wrist_wait_poll_fail;
+ }
+
+ err = gpio_direction_output(dd->gpio_data, 0);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to acquire data line.\n",
+ __func__);
+ goto m4wrist_wait_poll_fail;
+ }
+
+ m4wrist_send_bitstream(dd, &(clear[0]), 40);
+
+m4wrist_wait_poll_fail:
+ return err;
+}
+
+static int m4wrist_read_id_word(struct m4wrist_driver_data *dd, uint8_t *data)
+{
+ int err = 0;
+ int i = 0;
+ int bit = 0;
+ uint8_t stream1[2] = {0xBF, 0x00};
+ uint8_t stream2[2] = {0xDF, 0x90};
+
+ m4wrist_send_bitstream(dd, &(stream1[0]), 11);
+
+ err = gpio_direction_input(dd->gpio_data);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to release initial data line.\n",
+ __func__);
+ goto m4wrist_read_id_word_fail;
+ }
+
+ m4wrist_toggle_clock(dd, 2);
+
+ data[0] = 0x00;
+ for (i = 0; i <= 7; i++) {
+ gpio_set_value(dd->gpio_clk, 1);
+ data[0] = (data[0] << 1);
+ bit = gpio_get_value(dd->gpio_data);
+ if (bit == 1)
+ (data[0])++;
+
+ gpio_set_value(dd->gpio_clk, 0);
+ }
+
+ gpio_set_value(dd->gpio_clk, 1);
+
+ err = gpio_direction_output(dd->gpio_data, 1);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to acquire initial data line.\n",
+ __func__);
+ goto m4wrist_read_id_word_fail;
+ }
+
+ m4wrist_send_bitstream(dd, &(stream2[0]), 12);
+
+ err = gpio_direction_input(dd->gpio_data);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to release final data line.\n",
+ __func__);
+ goto m4wrist_read_id_word_fail;
+ }
+
+ m4wrist_toggle_clock(dd, 2);
+
+ data[1] = 0x00;
+ for (i = 0; i <= 7; i++) {
+ gpio_set_value(dd->gpio_clk, 1);
+ data[1] = (data[1] << 1);
+ bit = gpio_get_value(dd->gpio_data);
+ if (bit == 1)
+ (data[1])++;
+
+ gpio_set_value(dd->gpio_clk, 0);
+ }
+
+ m4wrist_toggle_clock(dd, 1);
+
+ err = gpio_direction_output(dd->gpio_data, 0);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to acquire final data line.\n",
+ __func__);
+ goto m4wrist_read_id_word_fail;
+ }
+
+ m4wrist_toggle_clock(dd, 1);
+
+m4wrist_read_id_word_fail:
+ return err;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/m4sensorhub_wrist.h b/drivers/misc/m4sensorhub_wrist.h
new file mode 100644
index 00000000000..7065935cc17
--- /dev/null
+++ b/drivers/misc/m4sensorhub_wrist.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2013 Motorola Mobility, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifndef _M4SENSORHUB_WRIST_H_
+#define _M4SENSORHUB_WRIST_H_
+
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/m4sensorhub.h>
+
+struct m4wrist_driver_data {
+ struct m4sensorhub_data *client;
+ struct mutex *mutex;
+ struct platform_device *pdev;
+
+ int gpio_xres;
+ int gpio_clk;
+ int gpio_data;
+
+ uint8_t *img;
+ uint32_t size;
+ uint8_t si_id[2];
+ uint8_t fw_ver[2];
+} __packed;
+
+#define M4WRIST_ID_SETUP_1_BITS 594
+static uint8_t m4wrist_id_setup_1[] = {
+ 0xCA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0D, 0xEE, 0x21, 0xF7, 0xF0, 0x27, 0xDC, 0x40,
+ 0x9F, 0x70, 0x01, 0xFD, 0xEE, 0x01, 0xE7, 0xC1,
+ 0xD7, 0x9F, 0x20, 0x7E, 0x7D, 0x88, 0x7D, 0xEE,
+ 0x21, 0xF7, 0xF0, 0x07, 0xDC, 0x40, 0x1F, 0x70,
+ 0x01, 0xFD, 0xEE, 0x01, 0xF7, 0xA0, 0x1F, 0xDE,
+ 0xA0, 0x1F, 0x7B, 0x00, 0x7D, 0xE0, 0x13, 0xF7,
+ 0xC0, 0x07, 0xDF, 0x28, 0x1F, 0x7D, 0x18, 0x7D,
+ 0xFE, 0x25, 0x80,
+};
+
+#define M4WRIST_ID_SETUP_2_BITS 418
+static uint8_t m4wrist_id_setup_2[] = {
+ 0xDE, 0xE2, 0x1F, 0x7F, 0x02, 0x7D, 0xC4, 0x09,
+ 0xF7, 0x00, 0x1F, 0x9F, 0x07, 0x5E, 0x7C, 0x81,
+ 0xF9, 0xF4, 0x01, 0xF7, 0xF0, 0x07, 0xDC, 0x40,
+ 0x1F, 0x70, 0x01, 0xFD, 0xEE, 0x01, 0xF7, 0xA0,
+ 0x1F, 0xDE, 0xA0, 0x1F, 0x7B, 0x00, 0x7D, 0xE0,
+ 0x0D, 0xF7, 0xC0, 0x07, 0xDF, 0x28, 0x1F, 0x7D,
+ 0x18, 0x7D, 0xFE, 0x25, 0x80,
+};
+
+#define M4WRIST_SYNC_ENABLE_BITS 110
+static uint8_t m4wrist_sync_enable[] = {
+ 0xDE, 0xE2, 0x1F, 0x7F, 0x02, 0x7D, 0xC4, 0x09,
+ 0xF7, 0x00, 0x1F, 0xDE, 0xE0, 0x1C,
+};
+
+#define M4WRIST_SYNC_DISABLE_BITS 110
+static uint8_t m4wrist_sync_disable[] = {
+ 0xDE, 0xE2, 0x1F, 0x71, 0x00, 0x7D, 0xFC, 0x01,
+ 0xF7, 0x00, 0x1F, 0xDE, 0xE0, 0x1C,
+};
+
+#define M4WRIST_ERASE_BITS 396
+static uint8_t m4wrist_erase[] = {
+ 0xDE, 0xE2, 0x1F, 0x7F, 0x02, 0x7D, 0xC4, 0x09,
+ 0xF7, 0x00, 0x1F, 0x9F, 0x07, 0x5E, 0x7C, 0x85,
+ 0xFD, 0xFC, 0x01, 0xF7, 0x10, 0x07, 0xDC, 0x00,
+ 0x7F, 0x7B, 0x80, 0x7D, 0xE0, 0x0B, 0xF7, 0xA0,
+ 0x1F, 0xDE, 0xA0, 0x1F, 0x7B, 0x04, 0x7D, 0xF0,
+ 0x01, 0xF7, 0xC9, 0x87, 0xDF, 0x48, 0x1F, 0x7F,
+ 0x89, 0x60,
+};
+
+#define M4WRIST_READ_WRITE_SETUP_BITS 66
+static uint8_t m4wrist_read_write_setup[] = {
+ 0xDE, 0xF0, 0x1F, 0x78, 0x00, 0x7D, 0xA0, 0x03,
+ 0xC0,
+};
+
+#define M4WRIST_PROGRAM_AND_VERIFY_BITS 440
+static uint8_t m4wrist_program_and_verify[] = {
+ 0xDE, 0xE2, 0x1F, 0x7F, 0x02, 0x7D, 0xC4, 0x09,
+ 0xF7, 0x00, 0x1F, 0x9F, 0x07, 0x5E, 0x7C, 0x81,
+ 0xF9, 0xF7, 0x01, 0xF7, 0xF0, 0x07, 0xDC, 0x40,
+ 0x1F, 0x70, 0x01, 0xFD, 0xEE, 0x01, 0xF6, 0xA0,
+ 0x0F, 0xDE, 0x80, 0x7F, 0x7A, 0x80, 0x7D, 0xEC,
+ 0x01, 0xF7, 0x80, 0x57, 0xDF, 0x00, 0x1F, 0x7C,
+ 0xA0, 0x7D, 0xF4, 0x61, 0xF7, 0xF8, 0x96,
+};
+
+#endif /* _M4SENSORHUB_WRIST_H_ */
diff --git a/drivers/misc/vib-gpio.c b/drivers/misc/vib-gpio.c
new file mode 100644
index 00000000000..d80d34db9f3
--- /dev/null
+++ b/drivers/misc/vib-gpio.c
@@ -0,0 +1,224 @@
+/* drivers/misc/vib-gpio.c
+ *
+ * Copyright (C) 2009 Motorola, Inc.
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/hrtimer.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/vib-gpio.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+
+/* TODO: replace with correct header */
+#include "../staging/android/timed_output.h"
+
+struct vib_gpio_data {
+ struct timed_output_dev dev;
+ struct work_struct vib_work;
+ struct hrtimer timer;
+ spinlock_t lock;
+
+ struct vib_gpio_platform_data *pdata;
+
+ int vib_power_state;
+ int vib_state;
+};
+
+struct vib_gpio_data *misc_data;
+
+static void vib_gpio_set(int on)
+{
+ if (on) {
+ if (misc_data->pdata->power_on && !misc_data->vib_power_state) {
+ misc_data->pdata->power_on();
+ misc_data->vib_power_state = 1;
+ }
+ if (misc_data->pdata->gpio >= 0)
+ gpio_direction_output(misc_data->pdata->gpio,
+ misc_data->pdata->active_low ?
+ 0 : 1);
+ } else {
+ if (misc_data->pdata->gpio >= 0)
+ gpio_direction_output(misc_data->pdata->gpio,
+ misc_data->pdata->active_low ?
+ 1 : 0);
+
+ if (misc_data->pdata->power_off && misc_data->vib_power_state) {
+ misc_data->pdata->power_off();
+ misc_data->vib_power_state = 0;
+ }
+ }
+}
+
+static void vib_gpio_update(struct work_struct *work)
+{
+ vib_gpio_set(misc_data->vib_state);
+}
+
+static enum hrtimer_restart gpio_timer_func(struct hrtimer *timer)
+{
+ struct vib_gpio_data *data =
+ container_of(timer, struct vib_gpio_data, timer);
+ data->vib_state = 0;
+ schedule_work(&data->vib_work);
+ return HRTIMER_NORESTART;
+}
+
+static int vib_gpio_get_time(struct timed_output_dev *dev)
+{
+ struct vib_gpio_data *data =
+ container_of(dev, struct vib_gpio_data, dev);
+
+ if (hrtimer_active(&data->timer)) {
+ ktime_t r = hrtimer_get_remaining(&data->timer);
+ struct timeval t = ktime_to_timeval(r);
+ return t.tv_sec * 1000 + t.tv_usec / 1000;
+ } else
+ return 0;
+}
+
+static void vib_gpio_enable(struct timed_output_dev *dev, int value)
+{
+ struct vib_gpio_data *data =
+ container_of(dev, struct vib_gpio_data, dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&data->lock, flags);
+ hrtimer_cancel(&data->timer);
+
+ if (value == 0)
+ data->vib_state = 0;
+ else {
+ value = (value > data->pdata->max_timeout ?
+ data->pdata->max_timeout : value);
+ data->vib_state = 1;
+ hrtimer_start(&data->timer,
+ ktime_set(value / 1000, (value % 1000) * 1000000),
+ HRTIMER_MODE_REL);
+ }
+
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ schedule_work(&data->vib_work);
+}
+
+/* This is a temporary solution until a more global haptics soltion is
+ * available for haptics that need to occur in any application */
+void vibrator_haptic_fire(int value)
+{
+ vib_gpio_enable(&misc_data->dev, value);
+}
+
+static int vib_gpio_probe(struct platform_device *pdev)
+{
+ struct vib_gpio_platform_data *pdata = pdev->dev.platform_data;
+ struct vib_gpio_data *gpio_data;
+ int ret = 0;
+
+ if (!pdata) {
+ ret = -EBUSY;
+ goto err0;
+ }
+ gpio_data = kzalloc(sizeof(struct vib_gpio_data), GFP_KERNEL);
+ if (!gpio_data) {
+ ret = -ENOMEM;
+ goto err0;
+ }
+
+ gpio_data->pdata = pdata;
+
+ INIT_WORK(&gpio_data->vib_work, vib_gpio_update);
+
+ hrtimer_init(&gpio_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+
+ gpio_data->timer.function = gpio_timer_func;
+ spin_lock_init(&gpio_data->lock);
+
+ gpio_data->dev.name = "vibrator";
+ gpio_data->dev.get_time = vib_gpio_get_time;
+ gpio_data->dev.enable = vib_gpio_enable;
+ ret = timed_output_dev_register(&gpio_data->dev);
+ if (ret < 0)
+ goto err1;
+
+ if (gpio_data->pdata->init)
+ ret = gpio_data->pdata->init();
+ if (ret < 0)
+ goto err2;
+
+ misc_data = gpio_data;
+ if (misc_data->pdata->gpio >= 0)
+ gpio_direction_output(gpio_data->pdata->gpio,
+ gpio_data->pdata->active_low);
+
+ platform_set_drvdata(pdev, gpio_data);
+
+ vib_gpio_enable(&gpio_data->dev, gpio_data->pdata->initial_vibrate);
+
+ pr_info("vib gpio probe done");
+ return 0;
+
+err2:
+ timed_output_dev_unregister(&gpio_data->dev);
+err1:
+ kfree(gpio_data->pdata);
+ kfree(gpio_data);
+err0:
+ return ret;
+}
+
+static int vib_gpio_remove(struct platform_device *pdev)
+{
+ struct vib_gpio_data *gpio_data = platform_get_drvdata(pdev);
+
+ if (gpio_data->pdata->exit)
+ gpio_data->pdata->exit();
+
+ timed_output_dev_unregister(&gpio_data->dev);
+
+ kfree(gpio_data->pdata);
+ kfree(gpio_data);
+
+ return 0;
+}
+
+static struct platform_driver vib_gpio_driver = {
+ .probe = vib_gpio_probe,
+ .remove = vib_gpio_remove,
+ .driver = {
+ .name = VIB_GPIO_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init vib_gpio_init(void)
+{
+ return platform_driver_register(&vib_gpio_driver);
+}
+
+static void __exit vib_gpio_exit(void)
+{
+ platform_driver_unregister(&vib_gpio_driver);
+}
+
+late_initcall(vib_gpio_init);
+module_exit(vib_gpio_exit);
+
+MODULE_AUTHOR("Motorola");
+MODULE_DESCRIPTION("vib gpio driver");
+MODULE_LICENSE("GPL");