summaryrefslogtreecommitdiff
path: root/drivers/mfd/m4sensorhub-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mfd/m4sensorhub-core.c')
-rw-r--r--drivers/mfd/m4sensorhub-core.c569
1 files changed, 569 insertions, 0 deletions
diff --git a/drivers/mfd/m4sensorhub-core.c b/drivers/mfd/m4sensorhub-core.c
new file mode 100644
index 00000000000..cf1a363921a
--- /dev/null
+++ b/drivers/mfd/m4sensorhub-core.c
@@ -0,0 +1,569 @@
+/*
+ * 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 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/rtc.h>
+#include <linux/gpio.h>
+#include <linux/string.h>
+#include <linux/m4sensorhub/MemMapLog.h>
+#include <linux/m4sensorhub.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/delay.h>
+
+
+#define M4SENSORHUB_NUM_GPIOS 6
+
+/* --------------- Global Declarations -------------- */
+char m4sensorhub_debug;
+EXPORT_SYMBOL_GPL(m4sensorhub_debug);
+
+/* ------------ Local Function Prototypes ----------- */
+
+/* -------------- Local Data Structures ------------- */
+static struct miscdevice m4sensorhub_misc_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = M4SENSORHUB_DRIVER_NAME,
+};
+
+/* --------------- Local Declarations -------------- */
+static struct m4sensorhub_data *m4sensorhub_misc_data;
+static DEFINE_MUTEX(m4sensorhub_driver_lock);
+
+unsigned short force_upgrade;
+module_param(force_upgrade, short, 0644);
+MODULE_PARM_DESC(force_upgrade, "Force FW download ignoring version check");
+
+unsigned short debug_level;
+module_param(debug_level, short, 0644);
+MODULE_PARM_DESC(debug_level, "Set debug level 1 (CRITICAL) to "
+ "7 (VERBOSE_DEBUG)");
+
+/* -------------- Global Functions ----------------- */
+struct m4sensorhub_data *m4sensorhub_client_get_drvdata(void)
+{
+ return m4sensorhub_misc_data;
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_client_get_drvdata);
+
+
+/* -------------- Local Functions ----------------- */
+
+static ssize_t m4sensorhub_get_dbg(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", m4sensorhub_debug);
+}
+
+/* BEGIN BOARD FILE */
+/* TODO: replace with request array */
+
+int m4sensorhub_set_bootmode(struct m4sensorhub_data *m4sensorhub,
+ enum m4sensorhub_bootmode bootmode)
+{
+ if (!m4sensorhub) {
+ printk(KERN_ERR "set_bootmode: invalid pointer\n");
+ return -EINVAL;
+ }
+
+ switch (bootmode) {
+ case BOOTMODE00:
+ gpio_set_value(m4sensorhub->hwconfig.boot0_gpio, 0);
+ gpio_set_value(m4sensorhub->hwconfig.boot1_gpio, 0);
+ break;
+ case BOOTMODE01:
+ gpio_set_value(m4sensorhub->hwconfig.boot0_gpio, 1);
+ gpio_set_value(m4sensorhub->hwconfig.boot1_gpio, 0);
+ break;
+ case BOOTMODE10:
+ gpio_set_value(m4sensorhub->hwconfig.boot0_gpio, 0);
+ gpio_set_value(m4sensorhub->hwconfig.boot1_gpio, 1);
+ break;
+ case BOOTMODE11:
+ gpio_set_value(m4sensorhub->hwconfig.boot0_gpio, 1);
+ gpio_set_value(m4sensorhub->hwconfig.boot1_gpio, 1);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void minnow_m4sensorhub_hw_reset(struct m4sensorhub_data *m4sensorhub)
+{
+ if (!m4sensorhub) {
+ printk(KERN_ERR "m4sensorhub_hw_reset: invalid pointer\n");
+ return;
+ }
+
+ m4sensorhub_set_bootmode(m4sensorhub, BOOTMODE00);
+ gpio_set_value(m4sensorhub->hwconfig.reset_gpio, 1);
+ msleep(5);
+ gpio_set_value(m4sensorhub->hwconfig.reset_gpio, 0);
+ msleep(5);
+ gpio_set_value(m4sensorhub->hwconfig.reset_gpio, 1);
+}
+
+/* callback from driver to initialize hardware on probe */
+static int minnow_m4sensorhub_hw_init(struct m4sensorhub_data *m4sensorhub,
+ struct device_node *node)
+{
+ int gpio;
+ int err = -EINVAL;
+
+ if (!m4sensorhub) {
+ printk(KERN_ERR "m4sensorhub_hw_init: invalid pointer\n");
+ err = -EINVAL;
+ goto error;
+ }
+ if (node == NULL) {
+ printk(KERN_ERR "m4sensorhub_hw_init: node null\n");
+ err = -EINVAL;
+ goto error;
+ }
+
+ gpio = of_get_named_gpio_flags(node, "mot,irq-gpio", 0, NULL);
+ err = (gpio < 0) ? -ENODEV : gpio_request(gpio, "m4sensorhub-intr");
+ if (err) {
+ pr_err("Failed acquiring M4 Sensor Hub IRQ GPIO-%d (%d)\n",
+ gpio, err);
+ goto error;
+ }
+ gpio_direction_input(gpio);
+ m4sensorhub->hwconfig.irq_gpio = gpio;
+
+ gpio = of_get_named_gpio_flags(node, "mot,reset-gpio", 0, NULL);
+ err = (gpio < 0) ? -ENODEV : gpio_request(gpio, "m4sensorhub-reset");
+ if (err) {
+ pr_err("Failed acquiring M4 Sensor Hub Reset GPIO-%d (%d)\n",
+ gpio, err);
+ goto error_reset;
+ }
+ gpio_direction_output(gpio, 1);
+ m4sensorhub->hwconfig.reset_gpio = gpio;
+
+ gpio = of_get_named_gpio_flags(node, "mot,wake-gpio", 0, NULL);
+ err = (gpio < 0) ? -ENODEV : gpio_request(gpio, "m4sensorhub-wake");
+ if (err) {
+ pr_err("Failed acquiring M4 Sensor Hub Wake GPIO-%d (%d)\n",
+ gpio, err);
+ goto error_wake;
+ }
+ gpio_direction_output(gpio, 0);
+ m4sensorhub->hwconfig.wake_gpio = gpio;
+
+ gpio = of_get_named_gpio_flags(node, "mot,boot0-gpio", 0, NULL);
+ err = (gpio < 0) ? -ENODEV : gpio_request(gpio, "m4sensorhub-boot0");
+ if (err) {
+ pr_err("Failed acquiring M4 Sensor Hub Boot0 GPIO-%d (%d)\n",
+ gpio, err);
+ goto error_boot0;
+ }
+ gpio_direction_output(gpio, 0);
+ m4sensorhub->hwconfig.boot0_gpio = gpio;
+
+ gpio = of_get_named_gpio_flags(node, "mot,boot1-gpio", 0, NULL);
+ err = (gpio < 0) ? -ENODEV : gpio_request(gpio, "m4sensorhub-boot1");
+ if (err) {
+ pr_err("Failed acquiring M4 Sensor Hub Boot1 GPIO-%d (%d)\n",
+ gpio, err);
+ goto error_boot1;
+ }
+ gpio_direction_output(gpio, 0);
+ m4sensorhub->hwconfig.boot1_gpio = gpio;
+
+ gpio = of_get_named_gpio_flags(node, "mot,enable-gpio", 0, NULL);
+ err = (gpio < 0) ? -ENODEV : gpio_request(gpio, "m4sensorhub-enable");
+ if (err) {
+ pr_err("Failed acquiring M4 Sensor Hub Enable GPIO-%d (%d)\n",
+ gpio, err);
+ goto error_enable;
+ }
+ gpio_direction_output(gpio, 0);
+ m4sensorhub->hwconfig.mpu_9150_en_gpio = gpio;
+
+ minnow_m4sensorhub_hw_reset(m4sensorhub);
+
+ return 0;
+
+error_enable:
+ gpio_free(m4sensorhub->hwconfig.boot1_gpio);
+ m4sensorhub->hwconfig.boot1_gpio = -1;
+error_boot1:
+ gpio_free(m4sensorhub->hwconfig.boot0_gpio);
+ m4sensorhub->hwconfig.boot0_gpio = -1;
+error_boot0:
+ gpio_free(m4sensorhub->hwconfig.wake_gpio);
+ m4sensorhub->hwconfig.wake_gpio = -1;
+error_wake:
+ gpio_free(m4sensorhub->hwconfig.reset_gpio);
+ m4sensorhub->hwconfig.reset_gpio = -1;
+error_reset:
+ gpio_free(m4sensorhub->hwconfig.irq_gpio);
+ m4sensorhub->hwconfig.irq_gpio = -1;
+error:
+ return err;
+}
+
+/* callback from driver to free hardware on shutdown */
+static void minnow_m4sensorhub_hw_free(struct m4sensorhub_data *m4sensorhub)
+{
+
+ if (!m4sensorhub) {
+ printk(KERN_ERR "hw_free: invalid pointer\n");
+ return;
+ }
+
+ if (m4sensorhub->hwconfig.irq_gpio >= 0) {
+ gpio_free(m4sensorhub->hwconfig.irq_gpio);
+ m4sensorhub->hwconfig.irq_gpio = -1;
+ }
+
+ if (m4sensorhub->hwconfig.reset_gpio >= 0) {
+ gpio_free(m4sensorhub->hwconfig.reset_gpio);
+ m4sensorhub->hwconfig.reset_gpio = -1;
+ }
+
+ if (m4sensorhub->hwconfig.wake_gpio >= 0) {
+ gpio_free(m4sensorhub->hwconfig.wake_gpio);
+ m4sensorhub->hwconfig.wake_gpio = -1;
+ }
+
+ if (m4sensorhub->hwconfig.boot0_gpio >= 0) {
+ gpio_free(m4sensorhub->hwconfig.boot0_gpio);
+ m4sensorhub->hwconfig.boot0_gpio = -1;
+ }
+
+ if (m4sensorhub->hwconfig.boot1_gpio >= 0) {
+ gpio_free(m4sensorhub->hwconfig.boot1_gpio);
+ m4sensorhub->hwconfig.boot1_gpio = -1;
+ }
+
+ if (m4sensorhub->hwconfig.mpu_9150_en_gpio >= 0) {
+ gpio_free(m4sensorhub->hwconfig.mpu_9150_en_gpio);
+ m4sensorhub->hwconfig.mpu_9150_en_gpio = -1;
+ }
+}
+
+/* END BOARD FILE FUNCTIONS */
+
+static ssize_t m4sensorhub_set_dbg(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned long debug;
+
+ if ((strict_strtol(buf, 10, &debug) < 0) ||
+ (debug < M4SH_NODEBUG) || (debug > M4SH_VERBOSE_DEBUG))
+ return -EINVAL;
+
+ m4sensorhub_debug = debug;
+ KDEBUG(M4SH_CRITICAL, "M4 Sensor Hub debug level = %d\n",
+ m4sensorhub_debug);
+
+ return count;
+}
+
+static DEVICE_ATTR(debug_level, S_IRUGO|S_IWUGO, m4sensorhub_get_dbg,
+ m4sensorhub_set_dbg);
+
+static ssize_t m4sensorhub_get_loglevel(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned long long loglevel;
+
+ m4sensorhub_reg_read(m4sensorhub_misc_data,
+ M4SH_REG_LOG_LOGENABLE, (char *)&loglevel);
+ KDEBUG(M4SH_INFO, "M4 loglevel = %llx", loglevel);
+ return sprintf(buf, "%llu\n", loglevel);
+}
+void ParseAndUpdateLogLevels(char *tag, char *level,
+ unsigned long long *logLevels)
+{
+ int i;
+ int levelindex = -1;
+ int tagindex = -1;
+ unsigned long long mask;
+
+ for (i = 0; i < LOG_LEVELS_MAX; i++) {
+ if (strcmp(acLogLevels[i], level) == 0) {
+ levelindex = i;
+ break;
+ }
+ }
+
+ for (i = 0; i < LOG_MAX; i++) {
+ if (strcmp(acLogTags[i], tag) == 0) {
+ tagindex = i;
+ break;
+ }
+ }
+
+ if ((tagindex == -1) || (levelindex == -1))
+ return;
+
+ /*Clear the revelant bits*/
+ mask = 0x03;
+ *logLevels &= ~(mask << (tagindex * 2));
+ /*set debug level for the relevant bits*/
+ *logLevels |= (levelindex << (tagindex * 2));
+ KDEBUG(M4SH_INFO, "New M4 log levels = 0x%llx \n", *logLevels);
+}
+
+/* Usage: adb shell into the directory of sysinterface log_level and
+ echo LOG_ACCEL=LOG_DEGUB,LOG_POWER=LOG_ERROR > log_level */
+static ssize_t m4sensorhub_set_loglevel(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned long long currentLogLevels;
+ char *tag, *level;
+ char **logbuf = (char **) &buf;
+
+ m4sensorhub_reg_read(m4sensorhub_misc_data,
+ M4SH_REG_LOG_LOGENABLE, (char *)&currentLogLevels);
+ while (1) {
+ tag = strsep(logbuf, "=,\n ");
+ if (tag == NULL)
+ break;
+ level = strsep(logbuf, "=,\n ");
+ if (level == NULL)
+ break;
+ ParseAndUpdateLogLevels(tag, level, &currentLogLevels);
+ }
+
+ return m4sensorhub_reg_write(m4sensorhub_misc_data,
+ M4SH_REG_LOG_LOGENABLE, (char *)&currentLogLevels,
+ m4sh_no_mask);
+}
+
+static DEVICE_ATTR(log_level, S_IRUGO|S_IWUGO, m4sensorhub_get_loglevel,
+ m4sensorhub_set_loglevel);
+
+static int m4sensorhub_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct m4sensorhub_data *m4sensorhub;
+ struct device_node *node = client->dev.of_node;
+ int err = -EINVAL;
+
+
+ /* Set debug based on module argument if set, otherwise use
+ default logging rate based on build type */
+ if (debug_level)
+ m4sensorhub_debug = debug_level;
+ else {
+#ifdef CONFIG_DEBUG_FS
+ /* engineering build */
+ m4sensorhub_debug = M4SH_INFO;
+#else
+ /* user/userdebug builds */
+ m4sensorhub_debug = M4SH_ERROR;
+#endif
+ }
+ KDEBUG(M4SH_ERROR, "Initializing M4 Sensor Hub: force_upgrade=%d "
+ "debug=%d\n", force_upgrade, m4sensorhub_debug);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ KDEBUG(M4SH_ERROR, "client not i2c capable\n");
+ err = -ENODEV;
+ goto err_unload;
+ }
+ m4sensorhub = kzalloc(sizeof(*m4sensorhub), GFP_KERNEL);
+ if (m4sensorhub == NULL) {
+ err = -ENOMEM;
+ KDEBUG(M4SH_ERROR,
+ "failed to allocate memory for module data: %d\n", err);
+ goto err_unload;
+ }
+ m4sensorhub_misc_data = m4sensorhub;
+
+ err = minnow_m4sensorhub_hw_init(m4sensorhub, node);
+ if (err)
+ printk(KERN_ERR "%s: hw_init Failed!", __func__);
+
+ /* link i2c_client to m4sensorhub */
+ i2c_set_clientdata(client, m4sensorhub);
+
+ /* link m4sensorhub to i2c_client */
+ m4sensorhub->i2c_client = client;
+
+ err = misc_register(&m4sensorhub_misc_device);
+ if (err < 0) {
+ KDEBUG(M4SH_ERROR, "misc register failed: %d\n", err);
+ goto err_hw_free;
+ }
+
+ err = device_create_file(&client->dev, &dev_attr_debug_level);
+ if (err < 0) {
+ KDEBUG(M4SH_ERROR, "Error creating debug_level file\n");
+ goto err_deregister;
+ }
+
+ err = device_create_file(&client->dev, &dev_attr_log_level);
+ if (err < 0) {
+ KDEBUG(M4SH_ERROR, "Error creating log_level file\n");
+ goto err_del_debug_file;
+ }
+
+ err = m4sensorhub_load_firmware(m4sensorhub, force_upgrade);
+ if (err < 0) {
+ dev_err(&client->dev, "load firmware file failed: %d\n", err);
+ goto err_del_log_file;
+ }
+
+ err = m4sensorhub_reg_init(m4sensorhub);
+ if (err < 0)
+ goto err_set_bootmode;
+
+ if (m4sensorhub->hwconfig.irq_gpio >= 0)
+ client->irq = gpio_to_irq(m4sensorhub->hwconfig.irq_gpio);
+ else {
+ KDEBUG(M4SH_ERROR, "Error: No IRQ configured\n");
+ err = -ENODEV;
+ goto err_reg_shutdown;
+ }
+
+ err = m4sensorhub_panic_init(m4sensorhub);
+ if (err < 0)
+ goto err_reg_shutdown;
+
+ err = m4sensorhub_irq_init(m4sensorhub);
+ if (err < 0)
+ goto err_panic_shutdown;
+
+
+ KDEBUG(M4SH_NOTICE, "Registered M4 Sensor Hub\n");
+
+ goto done;
+
+err_panic_shutdown:
+ m4sensorhub_panic_shutdown(m4sensorhub);
+err_reg_shutdown:
+ m4sensorhub_reg_shutdown(m4sensorhub);
+err_set_bootmode:
+ minnow_m4sensorhub_hw_reset(m4sensorhub);
+err_del_log_file:
+ device_remove_file(&client->dev, &dev_attr_log_level);
+err_del_debug_file:
+ device_remove_file(&client->dev, &dev_attr_debug_level);
+err_deregister:
+ misc_deregister(&m4sensorhub_misc_device);
+err_hw_free:
+ m4sensorhub->i2c_client = NULL;
+ i2c_set_clientdata(client, NULL);
+ minnow_m4sensorhub_hw_free(m4sensorhub);
+ kfree(m4sensorhub);
+ m4sensorhub = NULL;
+ m4sensorhub_misc_data = NULL;
+err_unload:
+done:
+ return err;
+}
+
+static int __exit m4sensorhub_remove(struct i2c_client *client)
+{
+ struct m4sensorhub_data *m4sensorhub = i2c_get_clientdata(client);
+ KDEBUG(M4SH_INFO, "Removing M4 Sensor Hub Driver\n");
+
+ m4sensorhub_irq_shutdown(m4sensorhub);
+ m4sensorhub_panic_shutdown(m4sensorhub);
+ m4sensorhub_reg_shutdown(m4sensorhub);
+ device_remove_file(&client->dev, &dev_attr_log_level);
+ device_remove_file(&client->dev, &dev_attr_debug_level);
+ minnow_m4sensorhub_hw_reset(m4sensorhub);
+ misc_deregister(&m4sensorhub_misc_device);
+ m4sensorhub->i2c_client = NULL;
+ i2c_set_clientdata(client, NULL);
+ minnow_m4sensorhub_hw_free(m4sensorhub);
+ kfree(m4sensorhub);
+ m4sensorhub = NULL;
+ m4sensorhub_misc_data = NULL;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int m4sensorhub_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ int err = 0;
+ KDEBUG(M4SH_INFO, "%s\n", __func__);
+ m4sensorhub_irq_pm_dbg_suspend();
+ return err;
+}
+
+static int m4sensorhub_resume(struct i2c_client *client)
+{
+
+ int err = 0;
+ KDEBUG(M4SH_INFO, "%s\n", __func__);
+ m4sensorhub_irq_pm_dbg_resume();
+ return err;
+}
+#endif /* CONFIG_PM */
+static const struct of_device_id of_m4sensorhub_match[] = {
+ { .compatible = "mot,m4sensorhub", },
+ {},
+};
+
+static const struct i2c_device_id m4sensorhub_id[] = {
+ {M4SENSORHUB_DRIVER_NAME, 0},
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, m4sensorhub_id);
+
+static struct i2c_driver m4sensorhub_driver = {
+ .driver = {
+ .name = M4SENSORHUB_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(of_m4sensorhub_match),
+ },
+ .probe = m4sensorhub_probe,
+ .remove = __exit_p(m4sensorhub_remove),
+#ifdef CONFIG_PM
+ .suspend = m4sensorhub_suspend,
+ .resume = m4sensorhub_resume,
+#endif /* CONFIG_PM */
+ .id_table = m4sensorhub_id,
+};
+
+static int __init m4sensorhub_init(void)
+{
+ return i2c_add_driver(&m4sensorhub_driver);
+}
+
+static void __exit m4sensorhub_exit(void)
+{
+ i2c_del_driver(&m4sensorhub_driver);
+ return;
+}
+
+module_init(m4sensorhub_init);
+module_exit(m4sensorhub_exit);
+
+MODULE_ALIAS("platform:m4sensorhub");
+MODULE_DESCRIPTION("M4 Sensor Hub driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");