diff options
Diffstat (limited to 'drivers/mfd/m4sensorhub-core.c')
| -rw-r--r-- | drivers/mfd/m4sensorhub-core.c | 569 | 
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 *)¤tLogLevels); +	while (1) { +		tag = strsep(logbuf, "=,\n "); +		if (tag == NULL) +			break; +		level = strsep(logbuf, "=,\n "); +		if (level == NULL) +			break; +		ParseAndUpdateLogLevels(tag, level, ¤tLogLevels); +	} + +	return m4sensorhub_reg_write(m4sensorhub_misc_data, +		M4SH_REG_LOG_LOGENABLE, (char *)¤tLogLevels, +		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");  |