diff options
Diffstat (limited to 'drivers')
27 files changed, 1402 insertions, 232 deletions
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 01e21037d8f..d7872b96019 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -27,6 +27,7 @@  #include <linux/pm.h>  #include <linux/suspend.h>  #include <linux/syscore_ops.h> +#include <linux/reboot.h>  #include <generated/utsrelease.h> @@ -130,6 +131,7 @@ struct firmware_buf {  	struct page **pages;  	int nr_pages;  	int page_array_size; +	struct list_head pending_list;  #endif  	char fw_id[];  }; @@ -171,6 +173,9 @@ static struct firmware_buf *__allocate_fw_buf(const char *fw_name,  	strcpy(buf->fw_id, fw_name);  	buf->fwc = fwc;  	init_completion(&buf->completion); +#ifdef CONFIG_FW_LOADER_USER_HELPER +	INIT_LIST_HEAD(&buf->pending_list); +#endif  	pr_debug("%s: fw-%s buf=%p\n", __func__, fw_name, buf); @@ -446,10 +451,8 @@ static struct firmware_priv *to_firmware_priv(struct device *dev)  	return container_of(dev, struct firmware_priv, dev);  } -static void fw_load_abort(struct firmware_priv *fw_priv) +static void __fw_load_abort(struct firmware_buf *buf)  { -	struct firmware_buf *buf = fw_priv->buf; -  	/*  	 * There is a small window in which user can write to 'loading'  	 * between loading done and disappearance of 'loading' @@ -457,8 +460,16 @@ static void fw_load_abort(struct firmware_priv *fw_priv)  	if (test_bit(FW_STATUS_DONE, &buf->status))  		return; +	list_del_init(&buf->pending_list);  	set_bit(FW_STATUS_ABORT, &buf->status);  	complete_all(&buf->completion); +} + +static void fw_load_abort(struct firmware_priv *fw_priv) +{ +	struct firmware_buf *buf = fw_priv->buf; + +	__fw_load_abort(buf);  	/* avoid user action after loading abort */  	fw_priv->buf = NULL; @@ -467,6 +478,25 @@ static void fw_load_abort(struct firmware_priv *fw_priv)  #define is_fw_load_aborted(buf)	\  	test_bit(FW_STATUS_ABORT, &(buf)->status) +static LIST_HEAD(pending_fw_head); + +/* reboot notifier for avoid deadlock with usermode_lock */ +static int fw_shutdown_notify(struct notifier_block *unused1, +			      unsigned long unused2, void *unused3) +{ +	mutex_lock(&fw_lock); +	while (!list_empty(&pending_fw_head)) +		__fw_load_abort(list_first_entry(&pending_fw_head, +					       struct firmware_buf, +					       pending_list)); +	mutex_unlock(&fw_lock); +	return NOTIFY_DONE; +} + +static struct notifier_block fw_shutdown_nb = { +	.notifier_call = fw_shutdown_notify, +}; +  static ssize_t firmware_timeout_show(struct class *class,  				     struct class_attribute *attr,  				     char *buf) @@ -619,6 +649,7 @@ static ssize_t firmware_loading_store(struct device *dev,  			 * is completed.  			 * */  			fw_map_pages_buf(fw_buf); +			list_del_init(&fw_buf->pending_list);  			complete_all(&fw_buf->completion);  			break;  		} @@ -853,8 +884,15 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,  		goto err_del_dev;  	} +	mutex_lock(&fw_lock); +	list_add(&buf->pending_list, &pending_fw_head); +	mutex_unlock(&fw_lock); +  	retval = device_create_file(f_dev, &dev_attr_loading);  	if (retval) { +		mutex_lock(&fw_lock); +		list_del_init(&buf->pending_list); +		mutex_unlock(&fw_lock);  		dev_err(f_dev, "%s: device_create_file failed\n", __func__);  		goto err_del_bin_attr;  	} @@ -1526,6 +1564,7 @@ static int __init firmware_class_init(void)  {  	fw_cache_init();  #ifdef CONFIG_FW_LOADER_USER_HELPER +	register_reboot_notifier(&fw_shutdown_nb);  	return class_register(&firmware_class);  #else  	return 0; @@ -1539,6 +1578,7 @@ static void __exit firmware_class_exit(void)  	unregister_pm_notifier(&fw_cache.pm_notify);  #endif  #ifdef CONFIG_FW_LOADER_USER_HELPER +	unregister_reboot_notifier(&fw_shutdown_nb);  	class_unregister(&firmware_class);  #endif  } diff --git a/drivers/cpufreq/cpufreq-cpu0.c b/drivers/cpufreq/cpufreq-cpu0.c index dd7bc175ca8..0b9f6a3c66c 100644 --- a/drivers/cpufreq/cpufreq-cpu0.c +++ b/drivers/cpufreq/cpufreq-cpu0.c @@ -150,15 +150,22 @@ out:  static int cpu0_cpufreq_pm_notify(struct notifier_block *nb,  	unsigned long event, void *dummy)  { +	static unsigned int old_policy_max;  	mutex_lock(&cpu0_cpufreq_lock);  	if (event == PM_SUSPEND_PREPARE) {  		struct cpufreq_policy *policy = cpufreq_cpu_get(0);  		is_suspended = true; +		old_policy_max = policy->max; +		policy->max = policy->cpuinfo.max_freq;  		pr_debug("cpu0 cpufreq suspend: setting frequency to %d kHz\n",  			policy->max);  		__cpu0_set_target(policy, policy->max, CPUFREQ_RELATION_L);  		cpufreq_cpu_put(policy);  	} else if (event == PM_POST_SUSPEND) { +		struct cpufreq_policy *policy = cpufreq_cpu_get(0); +		policy->max = old_policy_max; +		__cpu0_set_target(policy, policy->max, CPUFREQ_RELATION_L); +		cpufreq_cpu_put(policy);  		is_suspended = false;  	}  	mutex_unlock(&cpu0_cpufreq_lock); @@ -182,6 +189,7 @@ static int cpu0_cpufreq_reboot_notify(struct notifier_block *nb,  		mutex_lock(&cpu0_cpufreq_lock);  		policy = cpufreq_cpu_get(0);  		is_suspended = true; +		policy->max = policy->cpuinfo.max_freq;  		pr_info("cpu0 cpufreq shutdown: setting frequency to %d kHz\n",  				policy->max);  		__cpu0_set_target(policy, policy->max, CPUFREQ_RELATION_L); diff --git a/drivers/gpu/pvr/services4/srvkm/env/linux/osfunc.c b/drivers/gpu/pvr/services4/srvkm/env/linux/osfunc.c index c9b577eddbe..f13d0cfa586 100755 --- a/drivers/gpu/pvr/services4/srvkm/env/linux/osfunc.c +++ b/drivers/gpu/pvr/services4/srvkm/env/linux/osfunc.c @@ -3169,7 +3169,7 @@ PVRSRV_ERROR OSEventObjectSignalKM(IMG_HANDLE hOSEventKM)  ******************************************************************************/  IMG_BOOL OSProcHasPrivSrvInit(IMG_VOID)  { -    return (capable(CAP_SYS_MODULE) != 0) ? IMG_TRUE : IMG_FALSE; +    return (capable(CAP_SYS_ADMIN) != 0) ? IMG_TRUE : IMG_FALSE;  }  /*! diff --git a/drivers/leds/leds-lm3535.c b/drivers/leds/leds-lm3535.c index 3f157942803..23b7558674e 100644 --- a/drivers/leds/leds-lm3535.c +++ b/drivers/leds/leds-lm3535.c @@ -50,10 +50,11 @@  #ifdef CONFIG_LM3535_ESD_RECOVERY  #include <mot/esd_poll.h>  #endif /* CONFIG_LM3535_ESD_RECOVERY */ -#ifdef CONFIG_WAKEUP_SOURCE_NOTIFY  #include <linux/notifier.h> +#ifdef CONFIG_WAKEUP_SOURCE_NOTIFY +#include <linux/als_notify.h>  #include <linux/wakeup_source_notify.h> -#define MIN_DOCK_BVALUE		36 +#define MIN_DOCK_BVALUE			36  #include <linux/m4sensorhub.h>  #include <linux/m4sensorhub/MemMapUserSettings.h>  #endif @@ -273,7 +274,12 @@ struct lm3535 {  	int prevent_als_read;	/* Whether to prevent als reads for a time */  #ifdef CONFIG_WAKEUP_SOURCE_NOTIFY  	atomic_t docked; +	atomic_t alsstatus; +#ifdef CONFIG_ALS_WHILE_CHARGING +	atomic_t interactive; +#endif  	struct notifier_block dock_nb; +	struct notifier_block als_nb;  #endif  };  static DEFINE_MUTEX(lm3535_mutex); @@ -574,30 +580,34 @@ static uint8_t lm3535_convert_value (unsigned value, unsigned zone)          reg = res / als_denom;  #ifdef CONFIG_WAKEUP_SOURCE_NOTIFY -	if (!lm3535_data.prevent_als_read) { -		/* make sure this is atleast as high as corresponding ambient -		 * mode value for current ALS condition */ -		m4sensorhub = m4sensorhub_client_get_drvdata(); -		size = m4sensorhub_reg_getsize(m4sensorhub, -					M4SH_REG_LIGHTSENSOR_SIGNAL); -		if (size != sizeof(als)) { -			pr_err("can't get M4 reg size for ALS\n"); -			ambient_als_backlight = 0; -		} else if (size != m4sensorhub_reg_read(m4sensorhub, -						M4SH_REG_LIGHTSENSOR_SIGNAL, -						(char *)&als)) { -			pr_err("error reading M4 ALS value\n"); -			ambient_als_backlight = 0; -		} else { -			adjust_als = true; -			/* prevent als reads for next 500 ms */ -			lm3535_data.prevent_als_read = 1; -			schedule_delayed_work(&lm3535_data.als_delayed_work, -					      msecs_to_jiffies(500)); +	if (atomic_read(&lm3535_data.alsstatus)) { +		if (!lm3535_data.prevent_als_read) { +			/* make sure this is atleast as +			high as corresponding ambient +			* mode value for current ALS condition */ +			m4sensorhub = m4sensorhub_client_get_drvdata(); +			size = m4sensorhub_reg_getsize(m4sensorhub, +						M4SH_REG_LIGHTSENSOR_SIGNAL); +			if (size != sizeof(als)) { +				pr_err("can't get M4 reg size for ALS\n"); +				ambient_als_backlight = 0; +			} else if (size != m4sensorhub_reg_read(m4sensorhub, +					M4SH_REG_LIGHTSENSOR_SIGNAL, +							(char *)&als)) { +				pr_err("error reading M4 ALS value\n"); +				ambient_als_backlight = 0; +			} else { +				adjust_als = true; +				/* prevent als reads for next 500 ms */ +				lm3535_data.prevent_als_read = 1; +				schedule_delayed_work( +					&lm3535_data.als_delayed_work, +						      msecs_to_jiffies(500)); +			} +		} else if (ambient_als_backlight > reg) { +			/* If valid, use previously read als value */ +			reg = ambient_als_backlight;  		} -	} else if (ambient_als_backlight > reg) { -		/* If valid, use previously read als value */ -		reg = ambient_als_backlight;  	}  	if (adjust_als) { @@ -606,7 +616,7 @@ static uint8_t lm3535_convert_value (unsigned value, unsigned zone)  		if (ambient_als_backlight > reg)  			reg = ambient_als_backlight;  	} -#endif +#endif /* CONFIG_WAKEUP_SOURCE_NOTIFY*/  	printk_br(KERN_INFO "%s: v=%d, z=%d, res=0x%x, reg=0x%x\n",  		__func__, value, zone, res, reg); @@ -660,6 +670,31 @@ static void lm3535_brightness_set_raw_als(struct led_classdev *led_cdev,  	mutex_unlock(&lm3535_mutex);  }  #endif + +#ifdef CONFIG_WAKEUP_SOURCE_NOTIFY +static int lm3535_als_notifier(struct notifier_block *self, +				unsigned long action, void *dev) +{ +	pr_info("%s: ALS value is %lu\n", __func__, action); +	switch (action) { +	case ALS_ENABLED: +	case ALS_DISABLED: +		atomic_set(&lm3535_data.use_als, (action == ALS_ENABLED)); +	break; +	default: +#ifdef CONFIG_ALS_WHILE_CHARGING +		if (atomic_read(&lm3535_data.interactive) == 0) +			lm3535_brightness_set_raw_als(led_get_default_dev(), +						      (unsigned int)action); +		else +			pr_info("%s: ignoring ALS notifications\n", __func__); +#endif +	break; +	} +	return NOTIFY_OK; +} +#endif /* CONFIG_WAKEUP_SOURCE_NOTIFY */ +  static void lm3535_brightness_set (struct led_classdev *led_cdev,                  enum led_brightness value)  { @@ -707,6 +742,7 @@ static void lm3535_brightness_set (struct led_classdev *led_cdev,      /* Calculate brightness value for each zone relative to its cap */      bvalue = lm3535_convert_value (value, bright_zone); +  #ifdef CONFIG_WAKEUP_SOURCE_NOTIFY  	if (atomic_read(&lm3535_data.docked) && (bvalue < MIN_DOCK_BVALUE))  		bvalue = MIN_DOCK_BVALUE; /* hard code for dock mode */ @@ -922,6 +958,38 @@ static ssize_t lm3535_suspend_store (struct device *dev,  }  static DEVICE_ATTR(suspend, 0644, lm3535_suspend_show, lm3535_suspend_store); +#ifdef CONFIG_ALS_WHILE_CHARGING +static ssize_t lm3535_interactive_show(struct device *dev, +					struct device_attribute *attr, +					char *buf) +{ +	return sprintf(buf, "%d\n", atomic_read(&lm3535_data.interactive)); +} + +static ssize_t lm3535_interactive_store(struct device *dev, +					struct device_attribute *attr, +					const char *buf, size_t size) +{ +	unsigned value = 0; + +	if (!buf || size == 0) { +		pr_err("%s: invalid command\n", __func__); +		return -EINVAL; +	} + +	sscanf(buf, "%d", &value); +	if (value) +		atomic_set(&lm3535_data.interactive, 1); +	else +		atomic_set(&lm3535_data.interactive, 0); + +	return size; +} +static DEVICE_ATTR(interactive, S_IRUGO | S_IWUSR, +					lm3535_interactive_show, +					lm3535_interactive_store); +#endif +  /* This function is called by i2c_probe */  static int lm3535_probe (struct i2c_client *client,      const struct i2c_device_id *id) @@ -997,6 +1065,18 @@ static int lm3535_probe (struct i2c_client *client,          misc_deregister (&als_miscdev);          return ret;      } +#ifdef CONFIG_ALS_WHILE_CHARGING +	ret = device_create_file(lm3535_led.dev, &dev_attr_interactive); +	if (ret) { +		pr_err("err creating interactive file for %s: %d\n", +		       lm3535_led.name, ret); +		led_classdev_unregister(&lm3535_led); +		led_classdev_unregister(&lm3535_led_noramp); +		device_remove_file(lm3535_led.dev, &dev_attr_suspend); +		misc_deregister(&als_miscdev); +		return ret; +	} +#endif      dev_set_drvdata (lm3535_led.dev, &lm3535_led);  #if 0      lm3535_data.idev = input_allocate_device(); @@ -1029,15 +1109,23 @@ static int lm3535_probe (struct i2c_client *client,      register_early_suspend (&early_suspend_data);  #endif -    lm3535_led.brightness = 255; -    lm3535_led_noramp.brightness = 255; -    //lm3535_brightness_set (&lm3535_led_noramp, 255); -    lm3535_write_reg (LM3535_BRIGHTNESS_CTRL_REG_A, 0x79, __FUNCTION__); -    lm3535_data.initialized = 1; +	lm3535_led.brightness = 84; +	lm3535_led_noramp.brightness = 84; +	/* lm3535_brightness_set (&lm3535_led_noramp, 255); */ +	lm3535_write_reg(LM3535_BRIGHTNESS_CTRL_REG_A, 87, __func__); +	lm3535_data.initialized = 1;  #ifdef CONFIG_WAKEUP_SOURCE_NOTIFY  	atomic_set(&lm3535_data.docked, 0); +	/* default setting for minnow is to use ALS */ +	atomic_set(&lm3535_data.alsstatus, 1); +#ifdef CONFIG_ALS_WHILE_CHARGING +	atomic_set(&lm3535_data.interactive, 1); +#endif  	lm3535_data.dock_nb.notifier_call = lm3535_dock_notifier;  	wakeup_source_register_notify(&lm3535_data.dock_nb); + +	lm3535_data.als_nb.notifier_call = lm3535_als_notifier; +	als_register_notify(&lm3535_data.als_nb);  #endif /* CONFIG_WAKEUP_SOURCE_NOTIFY */  	INIT_DELAYED_WORK(&lm3535_data.als_delayed_work, @@ -1515,10 +1603,18 @@ static int lm3535_remove (struct i2c_client *client)  /*    led_classdev_unregister (&lm3535_led_noramp); */      misc_deregister (&als_miscdev);      device_remove_file (lm3535_led.dev, &dev_attr_suspend); +#ifdef CONFIG_ALS_WHILE_CHARGING +	device_remove_file(lm3535_led.dev, &dev_attr_interactive); +#endif  #if 0      input_unregister_device (lm3535_data.idev);      input_free_device (lm3535_data.idev);  #endif + +#ifdef CONFIG_WAKEUP_SOURCE_NOTIFY +	wakeup_source_unregister_notify(&lm3535_data.dock_nb); +	als_unregister_notify(&lm3535_data.als_nb); +#endif      return 0;  } diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index c0704d70b97..e14fcb9363f 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -171,6 +171,7 @@ m4sensorhub-objs                := m4sensorhub-core.o \                                     m4sensorhub-reg.o \                                     m4sensorhub-irq.o \                                     m4sensorhub-panic.o \ +                                   m4sensorhub-extern.o \                                     m4sensorhub-stm32-fw.o \                                     m4sensorhub-stm32_401-fw.o diff --git a/drivers/mfd/m4sensorhub-core.c b/drivers/mfd/m4sensorhub-core.c index a10ab20280b..203109e6ac7 100644 --- a/drivers/mfd/m4sensorhub-core.c +++ b/drivers/mfd/m4sensorhub-core.c @@ -90,6 +90,12 @@ static ssize_t m4sensorhub_get_dbg(struct device *dev,  /* BEGIN BOARD FILE */  /* TODO: replace with request array */ +int m4sensorhub_get_current_mode(void) +{ +	return m4sensorhub_misc_data.mode; +} +EXPORT_SYMBOL_GPL(m4sensorhub_get_current_mode); +  int m4sensorhub_set_bootmode(struct m4sensorhub_data *m4sensorhub,  			 enum m4sensorhub_bootmode bootmode)  { @@ -496,6 +502,12 @@ static void m4sensorhub_initialize(const struct firmware *firmware,  		return;  	} +	err = m4sensorhub_extern_init(&m4sensorhub_misc_data); +	if (err < 0) { +		KDEBUG(M4SH_ERROR, "%s: Extern init failed.\n", __func__); +		return; +	} +  	/* Initialize all the m4 drivers */  	inc = inithead;  	arg.p_m4sensorhub_data = &m4sensorhub_misc_data; @@ -540,20 +552,27 @@ static DEVICE_ATTR(debug_level, S_IRUSR|S_IWUSR, m4sensorhub_get_dbg,  static ssize_t m4sensorhub_get_loglevel(struct device *dev,  		struct device_attribute *attr, char *buf)  { -	unsigned long long loglevel; +	uint32_t logenable[LOG_EN_SIZE], len, i;  	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); +			     M4SH_REG_LOG_LOGENABLE, (char *)&logenable); + +	len = sprintf(buf, "M4 log levels = 0x"); +	for (i = 0; i < LOG_EN_SIZE; i++) +		len += sprintf(buf+len, "%08x", logenable[i]); +	KDEBUG(M4SH_INFO, "%s\n", buf); +	return snprintf(buf, PAGE_SIZE, "%s\n", buf);  } -void m4sensorhub_update_loglevels(char *tag, char *level, -			unsigned long long *log_levels) +void m4sensorhub_update_loglevels(int8_t *tag, int8_t *level, +			uint32_t *log_level)  { -	int i; -	int levelindex = -1; -	int tagindex = -1; -	unsigned long long mask; +	uint32_t i; +	int32_t levelindex = -1; +	int32_t tagindex = -1; +	uint32_t mask; +	int32_t logenableindex; +	int32_t en = LOG_TAGS_PER_ENABLE; +	int32_t b = LOG_NO_OF_BITS_PER_TAG;  	for (i = 0; i < LOG_LEVELS_MAX; i++) {  		if (strcmp(acLogLevels[i], level) == 0) { @@ -568,16 +587,21 @@ void m4sensorhub_update_loglevels(char *tag, char *level,  			break;  		}  	} -  	if ((tagindex == -1) || (levelindex == -1))  		return; +	logenableindex = tagindex/LOG_TAGS_PER_ENABLE; +  	/*Clear the revelant bits*/ -	mask = 0x03; -	*log_levels &= ~(mask << (tagindex * 2)); +	mask = LOG_TAG_MASK; +	*(log_level+logenableindex) &= ~(mask << ((tagindex % en) * b));  	/*set debug level for the relevant bits*/ -	*log_levels |= (levelindex << (tagindex * 2)); -	KDEBUG(M4SH_INFO, "New M4 log levels = 0x%llx\n", *log_levels); +	*(log_level+logenableindex) |= (levelindex << ((tagindex % en) * b)); + +	KDEBUG(M4SH_DEBUG, "New M4 log levels = "); +	for (i = 0; i < LOG_EN_SIZE; i++) +		KDEBUG(M4SH_DEBUG, "enable %d = 0x%08x ", i, *(log_level+i)); +	KDEBUG(M4SH_DEBUG, "\n");  }  /* Usage: adb shell into the directory of sysinterface log_level and @@ -585,25 +609,29 @@ void m4sensorhub_update_loglevels(char *tag, char *level,  static ssize_t m4sensorhub_set_loglevel(struct device *dev,  	struct device_attribute *attr, const char *buf, size_t count)  { -	unsigned long long cur_loglevels; +	uint32_t cur_loglevels[LOG_EN_SIZE];  	char *tag, *level;  	char **logbuf = (char **) &buf;  	m4sensorhub_reg_read(&m4sensorhub_misc_data, -		M4SH_REG_LOG_LOGENABLE, (char *)&cur_loglevels); +		M4SH_REG_LOG_LOGENABLE, (char *)cur_loglevels); +  	while (1) {  		tag = strsep(logbuf, "=,\n "); -		if (tag == NULL) +		if ((tag == NULL) || (logbuf == NULL))  			break;  		level = strsep(logbuf, "=,\n "); -		if (level == NULL) +		if ((level == NULL) || (logbuf == NULL))  			break; -		m4sensorhub_update_loglevels(tag, level, &cur_loglevels); +		m4sensorhub_update_loglevels(tag, level, +					     (uint32_t *)cur_loglevels);  	} -	return m4sensorhub_reg_write(&m4sensorhub_misc_data, -		M4SH_REG_LOG_LOGENABLE, (char *)&cur_loglevels, -		m4sh_no_mask); +	m4sensorhub_reg_write(&m4sensorhub_misc_data, +			      M4SH_REG_LOG_LOGENABLE, (char *)cur_loglevels, +			      m4sh_no_mask); + +	return count;  }  static DEVICE_ATTR(log_level, S_IRUSR|S_IWUSR, m4sensorhub_get_loglevel, diff --git a/drivers/mfd/m4sensorhub-extern.c b/drivers/mfd/m4sensorhub-extern.c new file mode 100644 index 00000000000..771dc5cc4f3 --- /dev/null +++ b/drivers/mfd/m4sensorhub-extern.c @@ -0,0 +1,158 @@ +/* + *  Copyright (C) 2014 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/m4sensorhub.h> +#include <linux/slab.h> + +#define m4ext_err(format, args...)  KDEBUG(M4SH_ERROR, format, ## args) + +struct m4sensorhub_data *m4ext_m4; +DEFINE_MUTEX(m4ext_mutex); +uint8_t m4ext_display_status; +uint8_t m4ext_audio_status; + +static void m4ext_panic_callback(struct m4sensorhub_data *m4, void *context) +{ +	int err; + +	mutex_lock(&m4ext_mutex); + +	if (m4ext_m4 == NULL) { +		m4ext_err("%s: M4 data is NULL.\n", __func__); +		err = -ENODATA; +		goto m4ext_panic_callback_fail; +	} + +	err = m4sensorhub_reg_write(m4, M4SH_REG_USERSETTINGS_SCREENSTATUS, +		(char *)&m4ext_display_status, m4sh_no_mask); +	if (err < 0) +		m4ext_err("%s: Screen status write failed (%d).\n", +			__func__, err); +	else if (err != 1) +		m4ext_err("%s: Screen status wrote %d bytes instead of 1.\n", +			__func__, err); + +	err = m4sensorhub_reg_write(m4, M4SH_REG_USERSETTINGS_AUDIOSTATUS, +		(char *)&m4ext_audio_status, m4sh_no_mask); +	if (err < 0) +		m4ext_err("%s: Audio status write failed (%d).\n", +			__func__, err); +	else if (err != 1) +		m4ext_err("%s: Audio status wrote %d bytes instead of 1.\n", +			__func__, err); + +m4ext_panic_callback_fail: +	mutex_unlock(&m4ext_mutex); +	return; +} + +int m4sensorhub_extern_init(struct m4sensorhub_data *m4) +{ +	int err; + +	mutex_lock(&m4ext_mutex); + +	err = m4sensorhub_panic_register(m4, PANICHDL_EXTERN_RESTORE, +		m4ext_panic_callback, NULL); +	if (err < 0) { +		m4ext_err("%s: Failed to register panic callback.\n", __func__); +		goto m4sensorhub_extern_init_fail; +	} + +	m4ext_m4 = m4; + +m4sensorhub_extern_init_fail: +	mutex_unlock(&m4ext_mutex); +	return err; +} +EXPORT_SYMBOL_GPL(m4sensorhub_extern_init); + +int m4sensorhub_extern_set_display_status(uint8_t status) +{ +	int err; + +	mutex_lock(&m4ext_mutex); + +	if (m4ext_m4 == NULL) { +		m4ext_err("%s: M4 data is NULL.\n", __func__); +		err = -ENODATA; +		goto m4sensorhub_extern_set_display_status_fail; +	} + +	m4ext_display_status = status; + +	err = m4sensorhub_reg_write(m4ext_m4, +		M4SH_REG_USERSETTINGS_SCREENSTATUS, +		(char *)&status, m4sh_no_mask); +	if (err < 0) { +		m4ext_err("%s: I2C write failed (%d).\n", __func__, err); +		goto m4sensorhub_extern_set_display_status_fail; +	} else if (err != 1) { +		m4ext_err("%s: Wrote %d bytes instead of 1.\n", __func__, err); +		err = -EINVAL; +		goto m4sensorhub_extern_set_display_status_fail; +	} + +	err = 0; + +m4sensorhub_extern_set_display_status_fail: +	mutex_unlock(&m4ext_mutex); +	return err; +} +EXPORT_SYMBOL_GPL(m4sensorhub_extern_set_display_status); + +int m4sensorhub_extern_set_audio_status(uint8_t status) +{ +	int err; + +	mutex_lock(&m4ext_mutex); + +	if (m4ext_m4 == NULL) { +		m4ext_err("%s: M4 data is NULL.\n", __func__); +		err = -ENODATA; +		goto m4sensorhub_extern_set_audio_status_fail; +	} + +	m4ext_audio_status = status; + +	err = m4sensorhub_reg_write(m4ext_m4, +		M4SH_REG_USERSETTINGS_AUDIOSTATUS, +		(char *)&status, m4sh_no_mask); +	if (err < 0) { +		m4ext_err("%s: I2C write failed (%d).\n", __func__, err); +		goto m4sensorhub_extern_set_audio_status_fail; +	} else if (err != 1) { +		m4ext_err("%s: Wrote %d bytes instead of 1.\n", __func__, err); +		err = -EINVAL; +		goto m4sensorhub_extern_set_audio_status_fail; +	} + +	err = 0; + +m4sensorhub_extern_set_audio_status_fail: +	mutex_unlock(&m4ext_mutex); +	return err; +} +EXPORT_SYMBOL_GPL(m4sensorhub_extern_set_audio_status); diff --git a/drivers/mfd/m4sensorhub-panic.c b/drivers/mfd/m4sensorhub-panic.c index b127adeba5c..34ad6658453 100644 --- a/drivers/mfd/m4sensorhub-panic.c +++ b/drivers/mfd/m4sensorhub-panic.c @@ -40,6 +40,7 @@ static const char *callback_name[PANICHDL_MAX] = {  	[PANICHDL_FUSION_RESTORE] = "fusion_restore",  	[PANICHDL_MPU9150_RESTORE] = "mpu9150_restore",  	[PANICHDL_PEDOMETER_RESTORE] = "pedometer_restore", +	[PANICHDL_EXTERN_RESTORE] = "extern_restore",  };  struct m4sensorhub_panic_callback { diff --git a/drivers/mfd/m4sensorhub-reg.h b/drivers/mfd/m4sensorhub-reg.h index 96719a08927..753ea9a8846 100644 --- a/drivers/mfd/m4sensorhub-reg.h +++ b/drivers/mfd/m4sensorhub-reg.h @@ -87,9 +87,9 @@ static const struct {  	[M4SH_REG_FUSION_LOCALX]             = {M4SH_TYPE_FUSION, 0x10, 4},  	[M4SH_REG_FUSION_LOCALY]             = {M4SH_TYPE_FUSION, 0x14, 4},  	[M4SH_REG_FUSION_LOCALZ]             = {M4SH_TYPE_FUSION, 0x18, 4}, -	[M4SH_REG_FUSION_WORLDX]             = {M4SH_TYPE_FUSION, 0x1c, 4}, -	[M4SH_REG_FUSION_WORLDY]             = {M4SH_TYPE_FUSION, 0x20, 4}, -	[M4SH_REG_FUSION_WORLDZ]             = {M4SH_TYPE_FUSION, 0x24, 4}, +	[M4SH_REG_FUSION_GRAVITYX]           = {M4SH_TYPE_FUSION, 0x1c, 4}, +	[M4SH_REG_FUSION_GRAVITYY]           = {M4SH_TYPE_FUSION, 0x20, 4}, +	[M4SH_REG_FUSION_GRAVITYZ]           = {M4SH_TYPE_FUSION, 0x24, 4},  	[M4SH_REG_FUSION_ROTATIONVECTOR]     = {M4SH_TYPE_FUSION, 0x28, 16},  	[M4SH_REG_FUSION_HEADING]            = {M4SH_TYPE_FUSION, 0x38, 2},  	[M4SH_REG_FUSION_HEADING_ACCURACY]   = {M4SH_TYPE_FUSION, 0x3a, 1}, @@ -112,6 +112,8 @@ static const struct {  	[M4SH_REG_METS_METS]                 = {M4SH_TYPE_METS, 0x4, 4},  	[M4SH_REG_METS_CALORIES]             = {M4SH_TYPE_METS, 0x8, 4},  	[M4SH_REG_METS_HEALTHYMINUTES]       = {M4SH_TYPE_METS, 0xc, 4}, +	[M4SH_REG_METS_METS_NO_RMR]          = {M4SH_TYPE_METS, 0x10, 4}, +	[M4SH_REG_METS_CALORIES_NO_RMR]      = {M4SH_TYPE_METS, 0x14, 4},  	[M4SH_REG_USERSETTINGS_SCREENSTATUS] = {M4SH_TYPE_USERSETTINGS, 0x0, 1},  	[M4SH_REG_USERSETTINGS_USERAGE]      = {M4SH_TYPE_USERSETTINGS, 0x1, 1},  	[M4SH_REG_USERSETTINGS_USERGENDER]   = {M4SH_TYPE_USERSETTINGS, 0x2, 1}, @@ -205,7 +207,7 @@ static const unsigned int bank_size_tbl[M4SH_TYPE__NUM] = {  	[M4SH_TYPE_FUSION]                   = 59,  	[M4SH_TYPE_COMPASS]                  = 17,  	[M4SH_TYPE_GYRO]                     = 16, -	[M4SH_TYPE_METS]                     = 16, +	[M4SH_TYPE_METS]                     = 24,  	[M4SH_TYPE_USERSETTINGS]             = 8,  	[M4SH_TYPE_POWER]                    = 9,  	[M4SH_TYPE_LOCATION]                 = 14, diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 5b96da5fda8..5aed79aa3c4 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -586,6 +586,13 @@ config WAKEUP_SOURCE_NOTIFY          help            Driver to allow early notification of wakeups +config ALS_WHILE_CHARGING +	tristate "use ALS when on charger" +	depends on WAKEUP_SOURCE_NOTIFY +	help +	  ALS when unit is on charger + +  source "drivers/misc/c2port/Kconfig"  source "drivers/misc/eeprom/Kconfig"  source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 2f57dbac599..39d1e04e896 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -68,3 +68,4 @@ obj-$(CONFIG_MOT_UTAG)          += utag/  obj-$(CONFIG_BQ5105X_CTRL)      += bq5105x_ctrl.o  obj-$(CONFIG_BQ5105X_DETECT)    += bq5105x_detect.o  obj-$(CONFIG_WAKEUP_SOURCE_NOTIFY)	+= wakeup_source_notify.o +obj-$(CONFIG_WAKEUP_SOURCE_NOTIFY)	+= als_notify.o diff --git a/drivers/misc/als_notify.c b/drivers/misc/als_notify.c new file mode 100644 index 00000000000..cbb4f3a55cd --- /dev/null +++ b/drivers/misc/als_notify.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2014 Motorola Mobility LLC. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/notifier.h> +#include <linux/als_notify.h> + +static BLOCKING_NOTIFIER_HEAD(als_notifier_list); + +/** + * als_register_notify - register a notifier callback for triggering display init + * @nb: pointer to the notifier block for the callback events. + * + */ +void als_register_notify(struct notifier_block *nb) +{ +	blocking_notifier_chain_register(&als_notifier_list, nb); +} +EXPORT_SYMBOL_GPL(als_register_notify); + +/** + * als_unregister_notify - unregister a notifier callback + * @nb: pointer to the notifier block for the callback events. + * + * als_register_notify() must have been previously called + * for this function to work properly. + */ +void als_unregister_notify(struct notifier_block *nb) +{ +	blocking_notifier_chain_unregister(&als_notifier_list, nb); +} +EXPORT_SYMBOL_GPL(als_unregister_notify); + +void als_notify_subscriber(unsigned long event) +{ +	blocking_notifier_call_chain(&als_notifier_list, event, NULL); +} +EXPORT_SYMBOL_GPL(als_notify_subscriber); diff --git a/drivers/misc/c55_ctrl.c b/drivers/misc/c55_ctrl.c index 54ffc6e5b2b..cf763272922 100644 --- a/drivers/misc/c55_ctrl.c +++ b/drivers/misc/c55_ctrl.c @@ -143,7 +143,6 @@ static ssize_t c55_ctrl_enable(struct device *dev,  	struct device_attribute *attr, const char *buf, size_t count)  {  	struct c55_ctrl_data *cdata = dev_get_drvdata(dev); -	struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata();  	int mode;  	if (kstrtoint(buf, 10, &mode) < 0) @@ -154,7 +153,7 @@ static ssize_t c55_ctrl_enable(struct device *dev,  		return -EINVAL;  	} -	if (m4sensorhub->mode != NORMALMODE) { +	if (m4sensorhub_get_current_mode() != NORMALMODE) {  		dev_err(dev, "M4 not ready, Unable to set screen status\n");  		return -EINVAL;  	} @@ -169,9 +168,7 @@ static ssize_t c55_ctrl_enable(struct device *dev,  		gpio_set_value(cdata->ap_c55_int_gpio, 1); -		if (m4sensorhub_reg_write_1byte -		    (m4sensorhub, M4SH_REG_USERSETTINGS_AUDIOSTATUS, -		    AUDIO_STATUS_ON, 0xFF) != 1) { +		if (m4sensorhub_extern_set_audio_status(AUDIO_STATUS_ON) < 0) {  			dev_err(dev, "Unable to set screen status to 0x01\n");  			mutex_unlock(&cdata->ctrl_mutex);  			return -EINVAL; @@ -189,9 +186,7 @@ static ssize_t c55_ctrl_enable(struct device *dev,  			cdata->c55_ap_int_enabled = 0;  		} -		if (m4sensorhub_reg_write_1byte -		    (m4sensorhub, M4SH_REG_USERSETTINGS_AUDIOSTATUS, -		    AUDIO_STATUS_OFF, 0xFF) != 1) { +		if (m4sensorhub_extern_set_audio_status(AUDIO_STATUS_OFF) < 0) {  			dev_err(dev, "Unable to set screen status to 0x00\n");  			mutex_unlock(&cdata->ctrl_mutex);  			return -EINVAL; @@ -363,7 +358,6 @@ static int c55_ctrl_remove(struct platform_device *pdev)  static int c55_ctrl_suspend(struct platform_device *dev, pm_message_t state)  {  	struct c55_ctrl_data *cdata = dev_get_drvdata(&dev->dev); -	struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata();  	if (cdata->c55_mode != C55_OFF) {  		dev_warn(&dev->dev, "C55 still ON when going into suspend\n"); @@ -375,9 +369,7 @@ static int c55_ctrl_suspend(struct platform_device *dev, pm_message_t state)  			cdata->c55_ap_int_enabled = 0;  		} -		if (m4sensorhub_reg_write_1byte -		    (m4sensorhub, M4SH_REG_USERSETTINGS_AUDIOSTATUS, -		    AUDIO_STATUS_OFF, 0xFF) != 1) { +		if (m4sensorhub_extern_set_audio_status(AUDIO_STATUS_OFF) < 0) {  			dev_err(&dev->dev,  				"Unable to set screen status to 0x00\n");  		} diff --git a/drivers/misc/m4sensorhub_als.c b/drivers/misc/m4sensorhub_als.c index 3f61edefff2..6445d9f0181 100644 --- a/drivers/misc/m4sensorhub_als.c +++ b/drivers/misc/m4sensorhub_als.c @@ -28,12 +28,21 @@  #include <linux/m4sensorhub.h>  #include <linux/input.h>  #include <linux/slab.h> +#include <linux/delay.h> +#ifdef CONFIG_WAKEUP_SOURCE_NOTIFY +#include <linux/notifier.h> +#include <linux/als_notify.h> +#ifdef CONFIG_ALS_WHILE_CHARGING +#include <linux/wakeup_source_notify.h> +#endif /* CONFIG_ALS_WHILE_CHARGING */ +#endif  #define m4als_err(format, args...)  KDEBUG(M4SH_ERROR, format, ## args)  #define M4ALS_DRIVER_NAME           "m4sensorhub_als"  #define M4ALS_IRQ_ENABLED_BIT       0 +#define ALS_SAMPLERATE_WHILE_DOCKED	10 /* in seconds */  struct m4als_driver_data {  	struct platform_device      *pdev; @@ -42,13 +51,39 @@ struct m4als_driver_data {  	struct input_dev            *indev;  	struct delayed_work         m4als_work; +	/* Beware of changing this from uint16, check als_notify.h +	since notifier uses values outside luminosity range for +	conveying enable/disable status */  	uint16_t        luminosity;  	int16_t         samplerate;  	int16_t         latest_samplerate;  	int16_t         fastest_rate;  	uint16_t        status; +#ifdef CONFIG_ALS_WHILE_CHARGING +	struct          notifier_block charger_nb; +	bool            chargerstatus; +#endif  }; +#ifdef CONFIG_ALS_WHILE_CHARGING +static int charger_notify(struct notifier_block *self, +			unsigned long action, void *dev) +{ +	struct m4als_driver_data *dd = +		container_of(self, struct m4als_driver_data, charger_nb); +	switch (action) { +	case DISPLAY_WAKE_EVENT_DOCKON: +	case DISPLAY_WAKE_EVENT_DOCKOFF: +		dd->chargerstatus = (action == DISPLAY_WAKE_EVENT_DOCKON); +		pr_info("%s: dd->chargerstatus is %d\n", +			__func__, dd->chargerstatus); +		break; +	} + +	return NOTIFY_OK; +} +#endif +  static void m4als_work_func(struct work_struct *work)  {  	int err = 0; @@ -81,8 +116,18 @@ static void m4als_work_func(struct work_struct *work)  	dd->luminosity = luminosity; -	input_event(dd->indev, EV_MSC, MSC_RAW, dd->luminosity); -	input_sync(dd->indev); +#ifdef CONFIG_ALS_WHILE_CHARGING +	if (dd->chargerstatus == true) { +		als_notify_subscriber(luminosity); +	} else { +		input_event(dd->indev, EV_MSC, MSC_RAW, dd->luminosity); +		input_sync(dd->indev); +	} +#else +		input_event(dd->indev, EV_MSC, MSC_RAW, dd->luminosity); +		input_sync(dd->indev); +#endif +  	if (dd->samplerate > 0)  		queue_delayed_work(system_freezable_wq, &(dd->m4als_work),  				      msecs_to_jiffies(dd->samplerate)); @@ -101,6 +146,12 @@ static int m4als_set_samplerate(struct m4als_driver_data *dd, int16_t rate)  	int err = 0;  	int size = 0; +#ifdef CONFIG_ALS_WHILE_CHARGING +	if (rate == -1 && dd->chargerstatus == true) { +		rate = ALS_SAMPLERATE_WHILE_DOCKED * 1000; +	} +#endif +  	if ((rate >= 0) && (rate <= dd->fastest_rate))  		rate = dd->fastest_rate; @@ -132,9 +183,17 @@ static int m4als_set_samplerate(struct m4als_driver_data *dd, int16_t rate)  	}  	cancel_delayed_work(&(dd->m4als_work));  	dd->samplerate = rate; -	if (dd->samplerate > 0) +	if (dd->samplerate > 0) {  		queue_delayed_work(system_freezable_wq, &(dd->m4als_work),  				      msecs_to_jiffies(rate)); +#ifdef CONFIG_WAKEUP_SOURCE_NOTIFY +		als_notify_subscriber(ALS_ENABLED); +#endif +	} else { +#ifdef CONFIG_WAKEUP_SOURCE_NOTIFY +		als_notify_subscriber(ALS_DISABLED); +#endif +	}  m4als_set_samplerate_fail:  	return err; @@ -153,6 +212,8 @@ static ssize_t m4als_setrate_store(struct device *dev,  	int err = 0;  	struct m4als_driver_data *dd = dev_get_drvdata(dev);  	int value = 0; +	int regsize = 0; +	uint16_t luminosity = 0;  	mutex_lock(&(dd->mutex)); @@ -175,6 +236,32 @@ static ssize_t m4als_setrate_store(struct device *dev,  		goto m4als_enable_store_exit;  	} +	/* Read and send raw value for gesture wakeup */ +	msleep(120); +	regsize = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_LIGHTSENSOR_SIGNAL); +	if (regsize < 0) { +		m4als_err("%s: Reading from invalid register %d.\n", +			  __func__, regsize); +		err = regsize; +		goto m4als_enable_store_exit; +	} + +	err = m4sensorhub_reg_read(dd->m4, M4SH_REG_LIGHTSENSOR_SIGNAL, +		(char *)&luminosity); +	if (err < 0) { +		m4als_err("%s: Failed to read luminosity data.\n", __func__); +		goto m4als_enable_store_exit; +	} else if (err != regsize) { +		m4als_err("%s: Read %d bytes instead of %d.\n", +			  __func__, err, regsize); +		goto m4als_enable_store_exit; +	} + +	dd->luminosity = luminosity; + +	input_event(dd->indev, EV_MSC, MSC_RAW, dd->luminosity); +	input_sync(dd->indev); +  m4als_enable_store_exit:  	if (err < 0)  		m4als_err("%s: Failed with error code %d.\n", __func__, err); @@ -304,12 +391,6 @@ static int m4als_driver_init(struct init_calldata *p_arg)  		goto m4als_driver_init_fail;  	} -	err = m4als_create_sysfs(dd); -	if (err < 0) { -		m4als_err("%s: Failed to create sysfs.\n", __func__); -		goto m4als_driver_init_sysfs_fail; -	} -  	INIT_DELAYED_WORK(&(dd->m4als_work), m4als_work_func);  	err = m4sensorhub_panic_register(dd->m4, PANICHDL_ALS_RESTORE, @@ -318,8 +399,6 @@ static int m4als_driver_init(struct init_calldata *p_arg)  		KDEBUG(M4SH_ERROR, "Als panic callback register failed\n");  	goto m4als_driver_init_exit; -m4als_driver_init_sysfs_fail: -	input_unregister_device(dd->indev);  m4als_driver_init_fail:  	m4als_err("%s: Init failed with error code %d.\n", __func__, err);  m4als_driver_init_exit: @@ -359,8 +438,22 @@ static int m4als_probe(struct platform_device *pdev)  		goto m4als_probe_fail;  	} +	err = m4als_create_sysfs(dd); +	if (err < 0) { +		m4als_err("%s: Failed to create sysfs.\n", __func__); +		goto m4als_driver_init_sysfs_fail; +	} + +#ifdef CONFIG_ALS_WHILE_CHARGING +	dd->chargerstatus = false; +	dd->charger_nb.notifier_call = charger_notify; +	wakeup_source_register_notify(&dd->charger_nb); +#endif +  	return 0; +m4als_driver_init_sysfs_fail: +	m4sensorhub_unregister_initcall(m4als_driver_init);  m4als_probe_fail:  	mutex_destroy(&(dd->mutex));  	kfree(dd); @@ -377,6 +470,9 @@ static int __exit m4als_remove(struct platform_device *pdev)  	cancel_delayed_work(&(dd->m4als_work));  	m4als_remove_sysfs(dd);  	m4sensorhub_unregister_initcall(m4als_driver_init); +#ifdef CONFIG_ALS_WHILE_CHARGING +	wakeup_source_unregister_notify(&dd->charger_nb); +#endif  	if (dd->indev != NULL)  		input_unregister_device(dd->indev);  	mutex_destroy(&(dd->mutex)); diff --git a/drivers/misc/m4sensorhub_fusion.c b/drivers/misc/m4sensorhub_fusion.c index e1330f56263..816b89bea39 100644 --- a/drivers/misc/m4sensorhub_fusion.c +++ b/drivers/misc/m4sensorhub_fusion.c @@ -126,6 +126,90 @@ static void m4fus_work_func(struct work_struct *work)  	dd->iiodat[1].type = FUSION_TYPE_ORIENTATION;  	dd->iiodat[1].timestamp = iio_get_time_ns(); +	size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_FUSION_GRAVITYX); +	err = m4sensorhub_reg_read(dd->m4, M4SH_REG_FUSION_GRAVITYX, +		(char *)&(dd->iiodat[2].values[0])); +	if (err < 0) { +		m4fus_err("%s: Failed to read gravityX data.\n", __func__); +		goto m4fus_isr_fail; +	} else if (err != size) { +		m4fus_err("%s: Read %d bytes instead of %d for %s.\n", +			  __func__, err, size, "gravityX"); +		err = -EBADE; +		goto m4fus_isr_fail; +	} + +	size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_FUSION_GRAVITYY); +	err = m4sensorhub_reg_read(dd->m4, M4SH_REG_FUSION_GRAVITYY, +		(char *)&(dd->iiodat[2].values[1])); +	if (err < 0) { +		m4fus_err("%s: Failed to read gravityY data.\n", __func__); +		goto m4fus_isr_fail; +	} else if (err != size) { +		m4fus_err("%s: Read %d bytes instead of %d for %s.\n", +			  __func__, err, size, "gravityY"); +		err = -EBADE; +		goto m4fus_isr_fail; +	} + +	size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_FUSION_GRAVITYZ); +	err = m4sensorhub_reg_read(dd->m4, M4SH_REG_FUSION_GRAVITYZ, +		(char *)&(dd->iiodat[2].values[2])); +	if (err < 0) { +		m4fus_err("%s: Failed to read gravityZ data.\n", __func__); +		goto m4fus_isr_fail; +	} else if (err != size) { +		m4fus_err("%s: Read %d bytes instead of %d for %s.\n", +			  __func__, err, size, "gravityZ"); +		err = -EBADE; +		goto m4fus_isr_fail; +	} + +	dd->iiodat[2].type = FUSION_TYPE_GRAVITY; +	dd->iiodat[2].timestamp = iio_get_time_ns(); + +	size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_FUSION_LOCALX); +	err = m4sensorhub_reg_read(dd->m4, M4SH_REG_FUSION_LOCALX, +		(char *)&(dd->iiodat[3].values[0])); +	if (err < 0) { +		m4fus_err("%s: Failed to read localX data.\n", __func__); +		goto m4fus_isr_fail; +	} else if (err != size) { +		m4fus_err("%s: Read %d bytes instead of %d for %s.\n", +			  __func__, err, size, "localX"); +		err = -EBADE; +		goto m4fus_isr_fail; +	} + +	size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_FUSION_LOCALY); +	err = m4sensorhub_reg_read(dd->m4, M4SH_REG_FUSION_LOCALY, +		(char *)&(dd->iiodat[3].values[1])); +	if (err < 0) { +		m4fus_err("%s: Failed to read localY data.\n", __func__); +		goto m4fus_isr_fail; +	} else if (err != size) { +		m4fus_err("%s: Read %d bytes instead of %d for %s.\n", +			  __func__, err, size, "localY"); +		err = -EBADE; +		goto m4fus_isr_fail; +	} + +	size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_FUSION_LOCALZ); +	err = m4sensorhub_reg_read(dd->m4, M4SH_REG_FUSION_LOCALZ, +		(char *)&(dd->iiodat[3].values[2])); +	if (err < 0) { +		m4fus_err("%s: Failed to read localZ data.\n", __func__); +		goto m4fus_isr_fail; +	} else if (err != size) { +		m4fus_err("%s: Read %d bytes instead of %d for %s.\n", +			  __func__, err, size, "localZ"); +		err = -EBADE; +		goto m4fus_isr_fail; +	} + +	dd->iiodat[3].type = FUSION_TYPE_LINEAR_ACCELERATION; +	dd->iiodat[3].timestamp = iio_get_time_ns(); +  	/*  	 * For some reason, IIO knows we are sending an array,  	 * so all FUSION_TYPE_* indicies will be sent diff --git a/drivers/misc/m4sensorhub_gesture.c b/drivers/misc/m4sensorhub_gesture.c index d5e364ea6c5..9ccbbe3091e 100644 --- a/drivers/misc/m4sensorhub_gesture.c +++ b/drivers/misc/m4sensorhub_gesture.c @@ -114,7 +114,7 @@ static void m4ges_isr(enum m4sensorhub_irqs int_event, void *handle)  		/* the GESTURE_VIEW is only effect for kernel now  		 * do not send gesture to android  		 */ -		goto m4ges_isr_fail; +		goto m4ges_no_iio_push;  	}  #endif /* CONFIG_WAKEUP_SOURCE_NOTIFY */ @@ -122,6 +122,12 @@ static void m4ges_isr(enum m4sensorhub_irqs int_event, void *handle)  	iio_push_to_buffers(iio, (unsigned char *)&(dd->iiodat));  	dd->gesture_count++; +m4ges_no_iio_push: +	/* Log gestures received  */ +	pr_info("%s: Gesture received: count=%u, type=%hhu, value=%hhd.\n", +		__func__, dd->gesture_count, dd->iiodat.gesture_type, +		dd->iiodat.gesture_value); +  m4ges_isr_fail:  	if (err < 0)  		m4ges_err("%s: Failed with error code %d.\n", __func__, err); diff --git a/drivers/misc/m4sensorhub_mpu9150.c b/drivers/misc/m4sensorhub_mpu9150.c index a2411c27d4f..55d160bc607 100644 --- a/drivers/misc/m4sensorhub_mpu9150.c +++ b/drivers/misc/m4sensorhub_mpu9150.c @@ -79,8 +79,6 @@ struct mpu9150_client {  	struct mutex mutex; /* prevent concurrent thread access */  	struct delayed_work mpu9150_work[NUM_TYPES];  	signed short fastest_rate[NUM_TYPES]; -	int calibration_done; -	int app_override;  };  struct mpu9150_client *misc_mpu9150_data; @@ -142,23 +140,6 @@ static void m4_report_mpu9150_inputevent(  	}  } -static void m4_queue_delayed_work(struct mpu9150_client *dd, -			int delay, enum mpu9150_sensor type) -{ -	if (type == TYPE_COMPASS) { -		/* initial calibration is not done and there is -		no app requesting compass data */ -		if ((!dd->calibration_done) && (!dd->app_override)) -			/* For current drain saving, m4 is sampling -			   compass at 40ms while omap is polling -			   for compass data at 15 secs */ -			delay = 15000; -	} -	queue_delayed_work(system_freezable_wq, -			   &(dd->mpu9150_work[type]), -			   msecs_to_jiffies(delay)); -} -  static void m4_set_mpu9150_delay(struct mpu9150_client *mpu9150_client_data,  			int delay, enum mpu9150_sensor type)  { @@ -197,18 +178,19 @@ static void m4_set_mpu9150_delay(struct mpu9150_client *mpu9150_client_data,  		cancel_delayed_work(&(dd->mpu9150_work[type]));  		dd->samplerate[type] = delay;  		if (dd->samplerate[type] > 0) -			m4_queue_delayed_work(dd, delay, type); +			queue_delayed_work(system_freezable_wq, +					&(dd->mpu9150_work[type]), +					msecs_to_jiffies(delay)); +  	}  } -  static void m4_read_mpu9150_data(struct mpu9150_client *mpu9150_client_data,  			enum mpu9150_sensor type)  {  	sCompassData compassdata;  	sAccelData acceldata;  	sGyroData gyrodata; -	struct mpu9150_client *dd = mpu9150_client_data;  	switch (type) {  	case TYPE_GYRO: @@ -249,18 +231,6 @@ static void m4_read_mpu9150_data(struct mpu9150_client *mpu9150_client_data,  		mpu9150_client_data->compass_data.cz =  compassdata.z;  		mpu9150_client_data->compass_data.ca =  compassdata.accuracy; -		/* Check if calibration is complete */ -		if ((!(dd->calibration_done)) && (compassdata.accuracy)) { -			dd->calibration_done = 1; -			KDEBUG(M4SH_INFO, "Calibration complete\n"); -			/* Stop compass sampling if no app is using the data */ -			if (dd->app_override == 0) { -				m4_set_mpu9150_delay(dd, -						     -1, -						     TYPE_COMPASS); -			KDEBUG(M4SH_INFO, "Init cal done. Turning off compass"); -			} -		}  		break;  	default: @@ -320,7 +290,9 @@ static void m4compass_work_func(struct work_struct *work)  	m4_report_mpu9150_inputevent(dd, TYPE_COMPASS);  	rate = dd->samplerate[TYPE_COMPASS];  	if (rate > 0) -		m4_queue_delayed_work(dd, rate, TYPE_COMPASS); +		queue_delayed_work(system_freezable_wq, +				&(dd->mpu9150_work[TYPE_COMPASS]), +				msecs_to_jiffies(rate));  	mutex_unlock(&(dd->mutex));  } @@ -407,20 +379,8 @@ static ssize_t m4_mpu9150_write_compass_setdelay(struct device *dev,  	mutex_lock(&(misc_mpu9150_data->mutex)); -	if (misc_mpu9150_data->calibration_done == 0) { -		/* If calibration is not complete and app tries to -		turn off ignore */ -		if (scanresult < 0) { -			misc_mpu9150_data->app_override = 0; -			goto compass_setdelay_exit; -		} else { -			misc_mpu9150_data->app_override = 1; -		} -	}  	m4_set_mpu9150_delay(misc_mpu9150_data, scanresult, TYPE_COMPASS); -compass_setdelay_exit: -  	mutex_unlock(&(misc_mpu9150_data->mutex));  	return count; @@ -611,7 +571,9 @@ static void mpu9150_panic_restore(struct m4sensorhub_data *m4sensorhub,  		m4_set_mpu9150_delay(dd, rate, type);  		cancel_delayed_work(&(dd->mpu9150_work[type]));  		if (rate > 0) -			m4_queue_delayed_work(dd, rate, type); +			queue_delayed_work(system_freezable_wq, +					&(dd->mpu9150_work[type]), +					msecs_to_jiffies(rate));  	}  	mutex_unlock(&(dd->mutex));  } @@ -633,9 +595,7 @@ static int mpu9150_driver_init(struct init_calldata *p_arg)  					 dd);  	if (ret < 0)  		KDEBUG(M4SH_ERROR, "HR panic callback register failed\n"); -	m4_set_mpu9150_delay(dd, -			     40, -			     TYPE_COMPASS); +  	mutex_unlock(&(dd->mutex));  	return ret;  } @@ -668,8 +628,6 @@ static int mpu9150_client_probe(struct platform_device *pdev)  	mpu9150_client_data->fastest_rate[TYPE_ACCEL] = 40;  	mpu9150_client_data->fastest_rate[TYPE_GYRO] = 40;  	mpu9150_client_data->fastest_rate[TYPE_COMPASS] = 40; -	mpu9150_client_data->calibration_done = 0; -	mpu9150_client_data->app_override = 0;  	mpu9150_client_data->input_dev = input_allocate_device();  	if (!mpu9150_client_data->input_dev) { diff --git a/drivers/misc/m4sensorhub_pedometer.c b/drivers/misc/m4sensorhub_pedometer.c index cf326454bea..0be14698e1d 100644 --- a/drivers/misc/m4sensorhub_pedometer.c +++ b/drivers/misc/m4sensorhub_pedometer.c @@ -40,6 +40,8 @@  #define M4PED_IRQ_ENABLED_BIT       0  #define M4PED_FEATURE_ENABLED_BIT   1 +#define M4PED_USERDATA_SIZE         5 +  struct m4ped_driver_data {  	struct platform_device      *pdev;  	struct m4sensorhub_data     *m4; @@ -47,9 +49,12 @@ struct m4ped_driver_data {  	struct m4sensorhub_pedometer_iio_data   iiodat;  	struct m4sensorhub_pedometer_iio_data   base_dat; -	struct delayed_work	m4ped_work; +	struct m4sensorhub_pedometer_iio_data   last_dat; +	struct delayed_work                     m4ped_work; +  	int16_t         samplerate;  	int16_t         fastest_rate; +	uint8_t         userdata[M4PED_USERDATA_SIZE];  	uint16_t        status;  }; @@ -57,6 +62,7 @@ static int m4ped_read_report_data(struct iio_dev *iio,  				  struct m4ped_driver_data *dd)  {  	int err = 0, size = 0; +	struct m4sensorhub_pedometer_iio_data dat;  	/*input validations */  	if ((iio == NULL) || (dd == NULL)) { @@ -80,7 +86,7 @@ static int m4ped_read_report_data(struct iio_dev *iio,  	size = m4sensorhub_reg_getsize(dd->m4,  		M4SH_REG_PEDOMETER_TOTALDISTANCE);  	err = m4sensorhub_reg_read(dd->m4, M4SH_REG_PEDOMETER_TOTALDISTANCE, -		(char *)&(dd->iiodat.total_distance)); +		(char *)&(dat.total_distance));  	if (err < 0) {  		m4ped_err("%s: Failed to read total_distance data.\n",  			  __func__); @@ -94,7 +100,7 @@ static int m4ped_read_report_data(struct iio_dev *iio,  	size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_PEDOMETER_TOTALSTEPS);  	err = m4sensorhub_reg_read(dd->m4, M4SH_REG_PEDOMETER_TOTALSTEPS, -		(char *)&(dd->iiodat.total_steps)); +		(char *)&(dat.total_steps));  	if (err < 0) {  		m4ped_err("%s: Failed to read total_steps data.\n", __func__);  		goto m4ped_read_fail; @@ -121,7 +127,7 @@ static int m4ped_read_report_data(struct iio_dev *iio,  	size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_METS_HEALTHYMINUTES);  	err = m4sensorhub_reg_read(dd->m4, M4SH_REG_METS_HEALTHYMINUTES, -		(char *)&(dd->iiodat.healthy_minutes)); +		(char *)&(dat.healthy_minutes));  	if (err < 0) {  		m4ped_err("%s: Failed to read healthy_minutes data.\n",  			  __func__); @@ -135,7 +141,7 @@ static int m4ped_read_report_data(struct iio_dev *iio,  	size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_METS_CALORIES);  	err = m4sensorhub_reg_read(dd->m4, M4SH_REG_METS_CALORIES, -		(char *)&(dd->iiodat.calories)); +		(char *)&(dat.calories));  	if (err < 0) {  		m4ped_err("%s: Failed to read calories data.\n", __func__);  		goto m4ped_read_fail; @@ -146,12 +152,63 @@ static int m4ped_read_report_data(struct iio_dev *iio,  		goto m4ped_read_fail;  	} +	size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_METS_CALORIES_NO_RMR); +	err = m4sensorhub_reg_read(dd->m4, M4SH_REG_METS_CALORIES_NO_RMR, +		(char *)&(dat.calories_normr)); +	if (err < 0) { +		m4ped_err("%s: Failed to read calories_normr data.\n", +			__func__); +		goto m4ped_read_fail; +	} else if (err != size) { +		m4ped_err("%s: Read %d bytes instead of %d for %s.\n", +			  __func__, err, size, "calories_normr"); +		err = -EBADE; +		goto m4ped_read_fail; +	} +  	dd->iiodat.timestamp = iio_get_time_ns(); -	dd->iiodat.total_distance += dd->base_dat.total_distance; -	dd->iiodat.total_steps += dd->base_dat.total_steps; -	dd->iiodat.healthy_minutes += dd->base_dat.healthy_minutes; -	dd->iiodat.calories += dd->base_dat.calories; +	/* Save data if these values decrease (they monotonically increase) */ +	if ((dat.total_distance < dd->last_dat.total_distance) || +		(dat.total_steps < dd->last_dat.total_steps) || +		(dat.healthy_minutes < dd->last_dat.healthy_minutes) || +		(dat.calories < dd->last_dat.calories) || +		(dat.calories_normr < dd->last_dat.calories_normr)) { +		m4ped_err("%s: Error: Current = %u %u %u %u %u " +			  "Last = %u %u %u %u %u, Base = %u %u %u %u %u\n", +			   __func__, dat.total_distance, +			  dat.total_steps, dat.healthy_minutes, +			  dat.calories, dat.calories_normr, +			  dd->last_dat.total_distance, +			  dd->last_dat.total_steps, +			  dd->last_dat.healthy_minutes, dd->last_dat.calories, +			  dd->last_dat.calories_normr, +			  dd->base_dat.total_distance, +			  dd->base_dat.total_steps, +			  dd->base_dat.healthy_minutes, dd->base_dat.calories, +			  dd->base_dat.calories_normr); +		m4ped_err("%s: iio = %u %u %u %u %u\n", __func__, +			  dd->iiodat.total_distance, +			  dd->iiodat.total_steps, +			  dd->iiodat.healthy_minutes, dd->iiodat.calories, +			  dd->iiodat.calories_normr); +		goto m4ped_read_fail; +	} + +	dd->last_dat.total_distance = dat.total_distance; +	dd->last_dat.total_steps = dat.total_steps; +	dd->last_dat.healthy_minutes = dat.healthy_minutes; +	dd->last_dat.calories = dat.calories; +	dd->last_dat.calories_normr = dat.calories_normr; + +	dd->iiodat.total_distance = dat.total_distance + +		dd->base_dat.total_distance; +	dd->iiodat.total_steps = dat.total_steps + dd->base_dat.total_steps; +	dd->iiodat.healthy_minutes = dat.healthy_minutes + +		dd->base_dat.healthy_minutes; +	dd->iiodat.calories = dat.calories + dd->base_dat.calories; +	dd->iiodat.calories_normr = dat.calories_normr + +		dd->base_dat.calories_normr;  	iio_push_to_buffers(iio, (unsigned char *)&(dd->iiodat)); @@ -181,6 +238,42 @@ static void m4ped_work_func(struct work_struct *work)  	return;  } +static int m4ped_write_userdata(struct m4ped_driver_data *dd) +{ +	int err; + +	err = m4sensorhub_reg_write(dd->m4, M4SH_REG_USERSETTINGS_USERAGE, +		&(dd->userdata[0]), m4sh_no_mask); +	if (err < 0) { +		m4ped_err("%s: Failed to write age.\n", __func__); +		goto m4ped_write_userdata_fail; +	} + +	err = m4sensorhub_reg_write(dd->m4, M4SH_REG_USERSETTINGS_USERGENDER, +		&(dd->userdata[1]), m4sh_no_mask); +	if (err < 0) { +		m4ped_err("%s: Failed to write gender.\n", __func__); +		goto m4ped_write_userdata_fail; +	} + +	err = m4sensorhub_reg_write(dd->m4, M4SH_REG_USERSETTINGS_USERHEIGHT, +		&(dd->userdata[2]), m4sh_no_mask); +	if (err < 0) { +		m4ped_err("%s: Failed to write height.\n", __func__); +		goto m4ped_write_userdata_fail; +	} + +	err = m4sensorhub_reg_write(dd->m4, M4SH_REG_USERSETTINGS_USERWEIGHT, +		&(dd->userdata[3]), m4sh_no_mask); +	if (err < 0) { +		m4ped_err("%s: Failed to write weight.\n", __func__); +		goto m4ped_write_userdata_fail; +	} + +m4ped_write_userdata_fail: +	return err; +} +  static int m4ped_set_samplerate(struct iio_dev *iio, int16_t rate)  {  	int err = 0; @@ -278,13 +371,14 @@ static ssize_t m4ped_iiodata_show(struct device *dev,  	mutex_lock(&(dd->mutex));  	size = snprintf(buf, PAGE_SIZE, -		"%s%hhu\n%s%u\n%s%u\n%s%hu\n%s%u\n%s%u\n", +		"%s%hhu\n%s%u\n%s%u\n%s%hu\n%s%u\n%s%u\n%s%u\n",  		"ped_activity: ", dd->iiodat.ped_activity,  		"total_distance: ", dd->iiodat.total_distance,  		"total_steps: ", dd->iiodat.total_steps,  		"current_speed: ", dd->iiodat.current_speed,  		"healthy_minutes: ", dd->iiodat.healthy_minutes, -		"calories: ", dd->iiodat.calories); +		"calories: ", dd->iiodat.calories, +		"calories_normr: ", dd->iiodat.calories_normr);  	mutex_unlock(&(dd->mutex));  	return size;  } @@ -298,11 +392,11 @@ static ssize_t m4ped_userdata_show(struct device *dev,  	struct iio_dev *iio = platform_get_drvdata(pdev);  	struct m4ped_driver_data *dd = iio_priv(iio);  	ssize_t size = 0; -	uint8_t data[5] = {0x00, 0x00, 0x00, 0x00, 0x00}; +	uint8_t data[M4PED_USERDATA_SIZE] = {0x00};  	mutex_lock(&(dd->mutex)); -	err = m4sensorhub_reg_read_n(dd->m4, M4SH_REG_USERSETTINGS_SCREENSTATUS, +	err = m4sensorhub_reg_read_n(dd->m4, M4SH_REG_USERSETTINGS_USERAGE,  		(char *)&(data[0]), ARRAY_SIZE(data));  	if (err < 0) {  		m4ped_err("%s: Failed to read user data.\n", __func__); @@ -315,11 +409,11 @@ static ssize_t m4ped_userdata_show(struct device *dev,  	}  	size = snprintf(buf, PAGE_SIZE, -		"%s%s\n%s%hhu\n%s%hhu\n%s%hhu\n", -		"Gender (M/F): ", data[2] ? "M" : "F", -		"Age    (yrs): ", data[1], -		"Height  (cm): ", data[3], -		"Weight  (kg): ", data[4]); +		"%s%s\n%s%hhu\n%s%hhu\n%s%hu\n", +		"Gender (M/F): ", data[1] ? "M" : "F", +		"Age    (yrs): ", data[0], +		"Height  (cm): ", data[2], +		"Weight  (kg): ", data[3] | (data[4] << 8));  m4ped_userdata_show_fail:  	mutex_unlock(&(dd->mutex)); @@ -334,6 +428,7 @@ m4ped_userdata_show_fail:   * Example:   *    Female, 22, 168cm, 49kg   *    0x00,0x16,0xA7,0x31\n + *    echo 0x00,0x16,0xA7,0x31 > userdata   */  static ssize_t m4ped_userdata_store(struct device *dev,  		struct device_attribute *attr, const char *buf, size_t size) @@ -344,7 +439,7 @@ static ssize_t m4ped_userdata_store(struct device *dev,  	struct m4ped_driver_data *dd = iio_priv(iio);  	unsigned int value = 0;  	unsigned char convbuf[5] = {0x00, 0x00, 0x00, 0x00, 0x00}; -	unsigned char outbuf[4] = {0x00, 0x00, 0x00, 0x00}; +	unsigned char outbuf[M4PED_USERDATA_SIZE] = {0x00};  	int i = 0;  	mutex_lock(&(dd->mutex)); @@ -373,31 +468,16 @@ static ssize_t m4ped_userdata_store(struct device *dev,  		outbuf[i] = (unsigned char) value;  	} -	err = m4sensorhub_reg_write(dd->m4, M4SH_REG_USERSETTINGS_USERAGE, -		&(outbuf[1]), m4sh_no_mask); -	if (err < 0) { -		m4ped_err("%s: Failed to write user data.\n", __func__); -		goto m4ped_userdata_store_fail; -	} +	for (i = 0; i < M4PED_USERDATA_SIZE; i++) +		dd->userdata[i] = outbuf[i]; -	err = m4sensorhub_reg_write(dd->m4, M4SH_REG_USERSETTINGS_USERGENDER, -		&(outbuf[0]), m4sh_no_mask); -	if (err < 0) { -		m4ped_err("%s: Failed to write user data.\n", __func__); -		goto m4ped_userdata_store_fail; -	} - -	err = m4sensorhub_reg_write(dd->m4, M4SH_REG_USERSETTINGS_USERHEIGHT, -		&(outbuf[2]), m4sh_no_mask); -	if (err < 0) { -		m4ped_err("%s: Failed to write user data.\n", __func__); -		goto m4ped_userdata_store_fail; -	} +	dd->userdata[0] = outbuf[1]; /* Age */ +	dd->userdata[1] = outbuf[0]; /* Gender */ -	err = m4sensorhub_reg_write_n(dd->m4, M4SH_REG_USERSETTINGS_USERWEIGHT, -		&(outbuf[3]), m4sh_no_mask, 1); +	err = m4ped_write_userdata(dd);  	if (err < 0) { -		m4ped_err("%s: Failed to write user data.\n", __func__); +		m4ped_err("%s: Failed to write user data (%d).\n", +			__func__, err);  		goto m4ped_userdata_store_fail;  	} @@ -606,13 +686,12 @@ static void m4ped_panic_restore(struct m4sensorhub_data *m4sensorhub,  	mutex_lock(&(dd->mutex)); -	dd->base_dat.total_distance = dd->iiodat.total_distance; -	dd->base_dat.total_steps = dd->iiodat.total_steps; -	dd->base_dat.healthy_minutes = dd->iiodat.healthy_minutes; -	dd->base_dat.calories = dd->iiodat.calories; -	m4ped_err("%s: Pedometer bases after panic = %d %d %d %d", __func__, -		  dd->base_dat.total_distance, dd->base_dat.total_steps, -		  dd->base_dat.healthy_minutes, dd->base_dat.calories); +	err = m4ped_write_userdata(dd); +	if (err < 0) { +		m4ped_err("%s: Failed to write user data (%d).\n", +			__func__, err); +		goto m4ped_panic_restore_fail; +	}  	if (!(dd->status & (1 << M4PED_FEATURE_ENABLED_BIT))) {  		err = m4sensorhub_reg_write(dd->m4, M4SH_REG_PEDOMETER_ENABLE, @@ -631,6 +710,19 @@ static void m4ped_panic_restore(struct m4sensorhub_data *m4sensorhub,  			goto m4ped_panic_restore_fail;  		}  	} +	/* Update base and reset last */ +	dd->base_dat.total_distance = dd->iiodat.total_distance; +	dd->base_dat.total_steps = dd->iiodat.total_steps; +	dd->base_dat.healthy_minutes = dd->iiodat.healthy_minutes; +	dd->base_dat.calories = dd->iiodat.calories; +	dd->base_dat.calories_normr = dd->iiodat.calories_normr; + +	dd->last_dat.total_distance = 0; +	dd->last_dat.total_steps = 0; +	dd->last_dat.healthy_minutes = 0; +	dd->last_dat.calories = 0; +	dd->last_dat.calories_normr = 0; +  	cancel_delayed_work(&(dd->m4ped_work));  	if (dd->samplerate > 0)  		queue_delayed_work(system_freezable_wq, &(dd->m4ped_work), @@ -692,6 +784,12 @@ static int m4ped_probe(struct platform_device *pdev)  	dd->fastest_rate = 1000; /* in milli secs */  	dd->status = dd->status | (1 << M4PED_FEATURE_ENABLED_BIT); +	dd->userdata[0] = 0x23; /* Age (35) */ +	dd->userdata[1] = 0x01; /* Gender (Male) */ +	dd->userdata[2] = 0xB2; /* Height (178cm) */ +	dd->userdata[3] = 0x5B; /* Weight (91kg) */ +	dd->userdata[4] = 0x00; /* Weight */ +  	err = m4ped_create_iiodev(iio); /* iio and dd are freed on fail */  	if (err < 0) {  		m4ped_err("%s: Failed to create IIO device.\n", __func__); diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 5d04f741899..60d5c8e01cb 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -2311,6 +2311,8 @@ static const struct mmc_fixup blk_fixups[] =  		  MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),  	MMC_FIXUP("VZL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,  		  MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), +	MMC_FIXUP("4FEACB", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc, +		  MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),  	MMC_FIXUP("004G90", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,  		  MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), diff --git a/drivers/power/avs/Makefile b/drivers/power/avs/Makefile index 9827e6ce4ec..7b8ef7ee817 100644 --- a/drivers/power/avs/Makefile +++ b/drivers/power/avs/Makefile @@ -3,7 +3,7 @@ obj-$(CONFIG_POWER_AVS_OMAP)		+= smartreflex.o  ifneq ($(CONFIG_POWER_TI_HARDWARE_VOLTAGE_CONTROL),)  # OMAP Common -omap-volt-common			=  omap_vc.o omap_vp.o +omap-volt-common			=  omap_vc.o omap_vp.o omap_core_dvfs.o  # OMAP SoC specific  ifneq ($(CONFIG_ARCH_OMAP3),) diff --git a/drivers/power/avs/omap_core_dvfs.c b/drivers/power/avs/omap_core_dvfs.c new file mode 100644 index 00000000000..2f98d534fbb --- /dev/null +++ b/drivers/power/avs/omap_core_dvfs.c @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2014 Motorola Mobility LLC + * + * 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 + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/opp.h> +#include <linux/of_device.h> +#include <linux/regulator/consumer.h> +#include <linux/regmap.h> +#include <linux/clk.h> +#include <linux/err.h> + +#define DRIVER_NAME	"omap-core-dvfs" + + +struct omap_core_dvfs_map { +	unsigned long cpu_freq; +	unsigned long core_freq; +}; + +struct omap_core_dvfs_data { +	struct device *dev; +	struct clk *l3_clock; +	struct clk *dpll_clock; +	struct regulator *reg; +	unsigned int volt_tolerance; +	long curr_freq; +	struct omap_core_dvfs_map *map; +}; + +static struct omap_core_dvfs_data *core_dvfs_data; + +static const struct of_device_id omap_core_dvfs_match_tbl[] = { +	{.compatible = "ti,omap-core-dvfs"}, +	{}, +}; +MODULE_DEVICE_TABLE(of, omap_core_dvfs_match_tbl); +static int get_match_freq(unsigned long cpu_freq, unsigned long *core_freq) +{ +	struct omap_core_dvfs_map *map = core_dvfs_data->map; +	while (map->core_freq && map->cpu_freq) { +		if (map->cpu_freq == cpu_freq) { +			*core_freq = map->core_freq; +			return 0; +		} +		map++; +	} +	return -ENODATA; +} +static int cpufreq_trans(struct notifier_block *nb, +		unsigned long val, void *data) +{ +	struct cpufreq_freqs *freqs = (struct cpufreq_freqs *)data; +	struct omap_core_dvfs_data *pdata = core_dvfs_data; +	int ret = 0; +	long d, new_freq, dpll_freq, old_freq; +	struct opp *opp; +	unsigned long volt = 0, volt_old = 0, tol = 0; + +	if (IS_ERR_OR_NULL(freqs)) +		return -ENODATA; + +	if (val != CPUFREQ_PRECHANGE || freqs->new == freqs->old) +		return 0; + +	ret = get_match_freq(freqs->new * 1000, &new_freq); +	if (ret) { +		pr_err( +		"Could not find cpu freq %u in core map\n", freqs->new * 1000); +		goto f_out; +	} +	if (pdata->curr_freq == new_freq) +		return 0; + +	/* calculate target frequency to be set in dpll */ +	old_freq = clk_get_rate(pdata->l3_clock); +	d = clk_get_rate(pdata->dpll_clock) / old_freq; +	dpll_freq = clk_round_rate(pdata->dpll_clock, new_freq * d); +	if (dpll_freq < 0) +		dpll_freq = new_freq * d; + +	rcu_read_lock(); +	opp = opp_find_freq_ceil(pdata->dev, &new_freq); +	if (IS_ERR(opp)) { +		rcu_read_unlock(); +		pr_err("failed to find core OPP for %ld\n", new_freq); +		ret = PTR_ERR(opp); +		goto f_out; +	} +	volt = opp_get_voltage(opp); +	rcu_read_unlock(); + +	tol = volt * pdata->volt_tolerance / 100; +	volt_old = regulator_get_voltage(pdata->reg); + +	pr_debug("L3 DVFS %ld MHz, %ld mV --> %ld MHz, %ld mV\n", +		 old_freq / 1000000, volt_old ? volt_old / 1000 : -1, +		 dpll_freq / d / 1000000, volt ? volt / 1000 : -1); + +	if (freqs->new > freqs->old) { +		ret = regulator_set_voltage_tol(pdata->reg, volt, tol); +		if (ret) { +			pr_err("failed to scale core voltage up: %d\n", ret); +			goto f_out; +		} +	} +	ret = clk_set_rate(pdata->dpll_clock, dpll_freq); +	if (ret) { +		pr_err("failed to set core clock rate: %d\n", ret); +		regulator_set_voltage_tol(pdata->reg, volt_old, tol); +		goto f_out; +	} +	if (freqs->new < freqs->old) { +		ret = regulator_set_voltage_tol(pdata->reg, volt, tol); +		if (ret) { +			pr_err("failed to scale voltage down: %d\n", ret); +			clk_set_rate(pdata->dpll_clock, old_freq * d); +			goto f_out; +		} +	} +	pdata->curr_freq = new_freq; +f_out: +	return ret; +} + +static struct notifier_block cpufreq_trans_block = { +	.notifier_call = cpufreq_trans +}; + +static int of_init_opp_map(struct device *dev, struct omap_core_dvfs_map **map) +{ +	const struct property *prop; +	const __be32 *val; +	struct omap_core_dvfs_map *m; +	int nr, i; + +	prop = of_find_property(dev->of_node, "map", NULL); +	if (!prop) +		return -ENODEV; +	if (!prop->value) +		return -ENODATA; + +	/* +	 * Each entry is a set of tuples consisting of freq-kHz from +	 * OPP CPU list and index of matching OPP in core list. +	 */ +	nr = prop->length / sizeof(u32); +	if (nr % 2) { +		dev_err(dev, "Invalid map\n"); +		return -EINVAL; +	} + +	m = devm_kzalloc(dev, prop->length, GFP_KERNEL); +	if (!m) { +		dev_err(dev, "Unable to create new map\n"); +		return -ENOMEM; +	} +	*map = m; +	val = prop->value; +	for (i = 0; i < nr; i += 2, m++) { +		m->cpu_freq = be32_to_cpup(val++) * 1000; +		m->core_freq = be32_to_cpup(val++) * 1000; +	} +	m->cpu_freq = 0; +	m->core_freq = 0; + +	return 0; +} + +static int omap_core_dvfs_probe(struct platform_device *pdev) +{ +	struct device *dev = &pdev->dev; +	struct device_node *nd = dev->of_node; +	int ret; +	struct omap_core_dvfs_data *data; +	const char *pname, *str; +	struct regulator *reg; + +	if (!nd) { +		dev_err(dev, "no OF information?\n"); +		return -EINVAL; +	} + +	reg = devm_regulator_get(dev, "core_dvfs"); +	if (IS_ERR(reg)) { +		dev_err(dev, "core_dvfs regulator not ready, retry\n"); +		return -EPROBE_DEFER; +	} + + +	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); +	if (!data) { +		dev_err(dev, "Unable to allocate data\n"); +		return -ENOMEM; +	} + +	ret = of_init_opp_table(dev); +	if (ret) { +		dev_err(dev, "Failed to init OPP table: %d\n", ret); +		goto fail; +	} + +	ret = of_init_opp_map(dev, &data->map); +	if (ret) { +		dev_err(dev, "Failed to init map: %d\n", ret); +		goto fail; +	} +	pname = "l3_clkname"; +	ret = of_property_read_string(nd, pname, &str); +	if (ret) +		goto property_err; + +	data->l3_clock = devm_clk_get(dev, str); +	if (IS_ERR(data->l3_clock)) { +		ret = PTR_ERR(data->l3_clock); +		dev_err(dev, "Failed to get %s clock: %d\n", str, ret); +		goto fail; +	} + +	pname = "dpll_clkname"; +	ret = of_property_read_string(nd, pname, &str); +	if (ret) +		goto property_err; + +	data->dpll_clock = devm_clk_get(dev, str); +	if (IS_ERR(data->dpll_clock)) { +		ret = PTR_ERR(data->dpll_clock); +		dev_err(dev, "Failed to get %s clock: %d\n", str, ret); +		goto fail; +	} + +	of_property_read_u32(nd, "voltage-tolerance", &data->volt_tolerance); + +	ret = cpufreq_register_notifier(&cpufreq_trans_block, +		CPUFREQ_TRANSITION_NOTIFIER); +	if (ret) { +		dev_err(dev, "CPU notifier registration failed with %d\n", ret); +		cpufreq_unregister_notifier( +			&cpufreq_trans_block, CPUFREQ_TRANSITION_NOTIFIER); +		goto fail; +	} +	data->reg = reg; +	data->dev = dev; +	core_dvfs_data = data; +	platform_set_drvdata(pdev, data); + +	return 0; + +property_err: +	dev_err(dev, " Missing/Invalid '%s' property\n", pname); + +fail: +	return ret; +} + +static struct platform_driver omap_core_dvfs_driver = { +	.driver = { +		.name = DRIVER_NAME, +		.owner = THIS_MODULE, +		.of_match_table = of_match_ptr(omap_core_dvfs_match_tbl), +		}, +	.probe = omap_core_dvfs_probe, +}; +static int __init omap_core_dvfs_init(void) +{ +	int ret; +	ret = platform_driver_register(&omap_core_dvfs_driver); +	if (ret) +		pr_err("driver register failed for omap_pmic(%d)\n", ret); +	return ret; +} +device_initcall_sync(omap_core_dvfs_init); + +static void __exit omap_core_dvfs_exit(void) +{ +	platform_driver_unregister(&omap_core_dvfs_driver); +} +module_exit(omap_core_dvfs_exit); + +MODULE_ALIAS("platform:" DRIVER_NAME); +MODULE_AUTHOR("Motorola Mobility LLC"); +MODULE_DESCRIPTION("OMAP Global PRM driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c index 6d0327f120a..c6c70c4afeb 100644 --- a/drivers/power/max17042_battery.c +++ b/drivers/power/max17042_battery.c @@ -64,15 +64,23 @@ CONFIG_TS_BIT_ENBL | CONFIG_SS_BIT_ENBL)  #define MODEL_LOCK1		0X0000  #define MODEL_LOCK2		0X0000 -#define dQ_ACC_DIV	0x4 -#define dP_ACC_100	0x1900 -#define dP_ACC_200	0x3200 +#define MAX17042_INIT_NUM_CYCLES	160 +#define MAX17047_INIT_NUM_CYCLES	96 + +#define MAX17042_dQ_ACC_DIV	4 +#define MAX17047_dQ_ACC_DIV	16 + +#define MAX17042_dP_ACC_200	0x3200 +#define MAX17047_dP_ACC_200	0x0C80  #define MAX17042_IC_VERSION	0x0092  #define MAX17047_IC_VERSION	0x00AC	/* same for max17050 */ +#define MAX17042_AGE_DIV	256 +  #define INIT_DATA_PROPERTY		"maxim,regs-init-data"  #define CONFIG_NODE			"maxim,configuration" +#define VERSION_PROPERTY		"version"  #define CONFIG_PROPERTY			"config"  #define FULL_SOC_THRESH_PROPERTY	"full_soc_thresh"  #define DESIGN_CAP_PROPERTY		"design_cap" @@ -119,6 +127,26 @@ struct max17042_chip {  	int malicious_online;  }; +#ifdef CONFIG_OF +const char *get_dts_batt_id(struct device *dev) +{ +	int lenp; +	const char *retval = NULL; +	struct device_node *n = of_find_node_by_path("/chosen"); + +	if (n) { +		retval = of_get_property(n, "batt-id", &lenp); +		if (!retval || !lenp) { +			dev_err(dev, "%s: batt-id len %d\n", __func__, lenp); +			retval = NULL; +		} +		of_node_put(n); +	} + +	return retval; +} +#endif +  static int max17042_write_reg(struct i2c_client *client, u8 reg, u16 value)  {  	int ret = i2c_smbus_write_word_data(client, reg, value); @@ -598,8 +626,9 @@ static void max17042_update_capacity_regs(struct max17042_chip *chip)  	max17042_write_verify_reg(chip->client, MAX17042_FullCAP,  				config->fullcap); +	/* Set DesignCap to fullcapnom here */  	max17042_write_reg(chip->client, MAX17042_DesignCap, -			config->design_cap); +			config->fullcapnom);  	max17042_write_verify_reg(chip->client, MAX17042_FullCAPNom,  				config->fullcapnom);  } @@ -614,10 +643,21 @@ static void max17042_reset_vfsoc0_reg(struct max17042_chip *chip)  	max17042_write_reg(chip->client, MAX17042_VFSOC0Enable, VFSOC0_LOCK);  } +static void max17042_advance_to_coulomb_counter_mode(struct max17042_chip *chip) +{ +	u16 value = (chip->chip_type == MAX17042 ? +			MAX17042_INIT_NUM_CYCLES : MAX17047_INIT_NUM_CYCLES); +	max17042_write_verify_reg(chip->client, MAX17042_Cycles, value); +} +  static void max17042_load_new_capacity_params(struct max17042_chip *chip)  {  	u16 rep_cap, dq_acc, vfSoc;  	u32 rem_cap; +	u16 dQ_ACC_DIV = (chip->chip_type == MAX17042 ? +				MAX17042_dQ_ACC_DIV : MAX17047_dQ_ACC_DIV); +	u16 dP_ACC_200 = (chip->chip_type == MAX17042 ? +				MAX17042_dP_ACC_200 : MAX17047_dP_ACC_200);  	struct max17042_config_data *config = chip->pdata->config_data; @@ -669,25 +709,14 @@ static inline void max17042_override_por_values(struct max17042_chip *chip)  			config->soc_alrt_thresh);  	max17042_override_por(client, MAX17042_CONFIG, config->config);  	max17042_override_por(client, MAX17042_SHDNTIMER, config->shdntimer); - -	max17042_override_por(client, MAX17042_DesignCap, config->design_cap); -	max17042_override_por(client, MAX17042_ICHGTerm, config->ichgt_term); -  	max17042_override_por(client, MAX17042_AtRate, config->at_rate); -	max17042_override_por(client, MAX17042_LearnCFG, config->learn_cfg); -	max17042_override_por(client, MAX17042_FilterCFG, config->filter_cfg); -	max17042_override_por(client, MAX17042_RelaxCFG, config->relax_cfg);  	max17042_override_por(client, MAX17042_MiscCFG, config->misc_cfg);  	max17042_override_por(client, MAX17042_MaskSOC, config->masksoc); -	max17042_override_por(client, MAX17042_FullCAP, config->fullcap); -	max17042_override_por(client, MAX17042_FullCAPNom, config->fullcapnom);  	if (chip->chip_type == MAX17042)  		max17042_override_por(client, MAX17042_SOC_empty,  						config->socempty);  	max17042_override_por(client, MAX17042_LAvg_empty, config->lavg_empty); -	max17042_override_por(client, MAX17042_dQacc, config->dqacc); -	max17042_override_por(client, MAX17042_dPacc, config->dpacc);  	if (chip->chip_type == MAX17042)  		max17042_override_por(client, MAX17042_V_empty, config->vempty); @@ -696,8 +725,6 @@ static inline void max17042_override_por_values(struct max17042_chip *chip)  	max17042_override_por(client, MAX17042_TempNom, config->temp_nom);  	max17042_override_por(client, MAX17042_TempLim, config->temp_lim);  	max17042_override_por(client, MAX17042_FCTC, config->fctc); -	max17042_override_por(client, MAX17042_RCOMP0, config->rcomp0); -	max17042_override_por(client, MAX17042_TempCo, config->tcompc0);  	if (chip->chip_type == MAX17042) {  		max17042_override_por(client, MAX17042_EmptyTempCo,  					config->empty_tempco); @@ -748,6 +775,9 @@ static int max17042_init_chip(struct max17042_chip *chip)  	/* reset vfsoc0 reg */  	max17042_reset_vfsoc0_reg(chip); +	/* advance to coulomb-counter mode */ +	max17042_advance_to_coulomb_counter_mode(chip); +  	/* load new capacity params */  	max17042_load_new_capacity_params(chip); @@ -821,8 +851,13 @@ static void max17042_init_worker(struct work_struct *work)  		ret = max17042_init_chip(chip);  	} -	if (!ret) +	if (!ret) {  		chip->init_complete = 1; +		if (chip->chip_type == MAX17047) { +			max17042_write_reg(chip->client, MAX17047_Config_Ver, +					   chip->pdata->config_data->version); +		} +	}  	mutex_unlock(&chip->lock);  } @@ -831,11 +866,16 @@ static void max17042_malicious_removed_worker(struct work_struct *work)  {  	struct max17042_chip *chip = container_of(work,  				struct max17042_chip, work_malicious_removed); +	int ret;  	mutex_lock(&chip->lock);  	max17042_perform_soft_POR(chip); -	max17042_init_chip(chip); +	ret = max17042_init_chip(chip); +	if (!ret && chip->chip_type == MAX17047) +		max17042_write_reg(chip->client, MAX17047_Config_Ver, +				   chip->pdata->config_data->version); +  	dev_info(&chip->client->dev, "malicious ps removed, chip re-inited\n");  	mutex_unlock(&chip->lock); @@ -949,6 +989,10 @@ static int max17042_cfg_rqrd_prop(struct device *dev,  				  struct device_node *np,  				  struct max17042_config_data *config_data)  { +	if (of_property_read_u16(np, VERSION_PROPERTY, +				 &config_data->version)) +		return -EINVAL; +  	if (of_property_read_u16(np, CONFIG_PROPERTY,  				 &config_data->config))  		return -EINVAL; @@ -1008,13 +1052,26 @@ static void max17042_cfg_optnl_prop(struct device_node *np,  static struct max17042_config_data *  max17042_get_config_data(struct device *dev)  { +	char *config_node = NULL; +	char config_node_path[64];  	struct max17042_config_data *config_data;  	struct device_node *np = dev->of_node;  	if (!np)  		return NULL; -	np = of_get_child_by_name(np, CONFIG_NODE); +	config_node = (char *)get_dts_batt_id(dev); +	if (config_node) { +		snprintf(config_node_path, sizeof(config_node_path), +			 "%s-%s", CONFIG_NODE, config_node); +		config_node = config_node_path; +	} else { +		config_node = CONFIG_NODE; +	} + +	dev_info(dev, "using %s profile\n", config_node); + +	np = of_get_child_by_name(np, config_node);  	if (!np)  		return NULL; @@ -1175,6 +1232,23 @@ static int max17042_debugfs_write_data(void *data, u64 val)  DEFINE_SIMPLE_ATTRIBUTE(data_fops, max17042_debugfs_read_data,  			max17042_debugfs_write_data, "0x%02llx\n"); +static int max17042_debugfs_read_capacity(void *data, u64 *val) +{ +	struct max17042_chip *chip = (struct max17042_chip *)data; +	*val = chip->debugfs_capacity; +	return 0; +} + +static int max17042_debugfs_write_capacity(void *data, u64 val) +{ +	struct max17042_chip *chip = (struct max17042_chip *)data; +	chip->debugfs_capacity = val; +	power_supply_changed(&chip->battery); +	return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(capacity_fops, max17042_debugfs_read_capacity, +			max17042_debugfs_write_capacity, "%llu\n"); +  static int max17042_debugfs_create(struct max17042_chip *chip)  {  	chip->debugfs_root = debugfs_create_dir(dev_name(&chip->client->dev), @@ -1191,8 +1265,8 @@ static int max17042_debugfs_create(struct max17042_chip *chip)  		goto err_debugfs;  	chip->debugfs_capacity = 0xFF; -	if (!debugfs_create_u8("capacity", S_IRUGO | S_IWUSR, -			       chip->debugfs_root, &chip->debugfs_capacity)) +	if (!debugfs_create_file("capacity", S_IRUGO | S_IWUSR, +				 chip->debugfs_root, chip, &capacity_fops))  		goto err_debugfs;  	return 0; @@ -1226,7 +1300,6 @@ static void max17042_external_power_changed(struct power_supply *psy)  	}  } -  static ssize_t max17042_show_alert_threshold(struct device *dev,  		struct device_attribute *attr,  		char *buf) @@ -1256,8 +1329,21 @@ static ssize_t max17042_store_alert_threshold(struct device *dev,  static DEVICE_ATTR(alert_threshold, S_IRUGO | S_IWUSR,  		max17042_show_alert_threshold, max17042_store_alert_threshold); + +static ssize_t max17042_show_battery_age(struct device *dev, +					 struct device_attribute *attr, +					 char *buf) +{ +	struct max17042_chip *chip = dev_get_drvdata(dev); +	int ret = max17042_read_reg(chip->client, MAX17042_Age); + +	return ret < 0 ? ret : sprintf(buf, "%u\n", ret / MAX17042_AGE_DIV); +} +static DEVICE_ATTR(battery_age, S_IRUGO, max17042_show_battery_age, NULL); +  static struct attribute *max17042_attrs[] = {  	&dev_attr_alert_threshold.attr, +	&dev_attr_battery_age.attr,  	NULL,  }; @@ -1265,6 +1351,20 @@ static struct attribute_group max17042_attr_group = {  	.attrs = max17042_attrs,  }; +static bool max17042_new_config_data(struct max17042_chip *chip) +{ +	int ret; + +	if (chip->chip_type == MAX17042) +		return false; + +	ret = max17042_read_reg(chip->client, MAX17047_Config_Ver); +	if (ret < 0) +		return false; + +	return (chip->pdata->config_data->version != ret); +} +  static int max17042_probe(struct i2c_client *client,  			const struct i2c_device_id *id)  { @@ -1384,7 +1484,7 @@ static int max17042_probe(struct i2c_client *client,  	}  	reg = max17042_read_reg(chip->client, MAX17042_STATUS); -	if (reg & STATUS_POR_BIT) { +	if (reg & STATUS_POR_BIT || max17042_new_config_data(chip)) {  		INIT_WORK(&chip->work, max17042_init_worker);  		schedule_work(&chip->work);  	} else { diff --git a/drivers/regulator/omap-pmic-regulator.c b/drivers/regulator/omap-pmic-regulator.c index a19f9ebb7de..2eab7c158e2 100644 --- a/drivers/regulator/omap-pmic-regulator.c +++ b/drivers/regulator/omap-pmic-regulator.c @@ -661,7 +661,22 @@ static struct platform_driver omap_pmic_driver = {  		   },  	.probe = omap_pmic_probe,  }; -module_platform_driver(omap_pmic_driver); + +static int __init omap_pmic_init(void) +{ +	int ret; +	ret = platform_driver_register(&omap_pmic_driver); +	if (ret) +		pr_err("driver register failed for omap_pmic (%d)\n", ret); +	return ret; +} +device_initcall_sync(omap_pmic_init); + +static void __exit omap_pmic_exit(void) +{ +	platform_driver_unregister(&omap_pmic_driver); +} +module_exit(omap_pmic_exit);  MODULE_DESCRIPTION("OMAP Generic PMIC Regulator");  MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-sensorhub.c b/drivers/rtc/rtc-sensorhub.c index f4e713d741f..16f95758f93 100644 --- a/drivers/rtc/rtc-sensorhub.c +++ b/drivers/rtc/rtc-sensorhub.c @@ -223,6 +223,9 @@ static int rtc_sensorhub_rtc_set_time(struct device *p_dev,  				"set time, but failed to set M4 clock!\n");  			return -EIO;  	} +	dev_dbg(p_dev, "Set RTC time to %d-%02d-%02d %02d:%02d:%02d UTC (%ld)\n", +			p_tm->tm_year + 1900, p_tm->tm_mon + 1, p_tm->tm_mday, +			p_tm->tm_hour, p_tm->tm_min, p_tm->tm_sec, sec);  	return 0;  } diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 7d041f28cf2..3009a8909cc 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -172,6 +172,7 @@ struct uart_omap_port {  	struct pinctrl_state	*pin_default;  	struct pinctrl_state	*pin_idle;  	bool			is_suspending; +	spinlock_t		delayed_rts_lock; /* protect need_delayed_rts*/  	bool			need_delayed_rts;  	bool			in_transmit;  	int			ext_rt_cnt; @@ -1372,12 +1373,16 @@ static int serial_omap_prepare(struct device *dev)  static void serial_omap_complete(struct device *dev)  {  	struct uart_omap_port *up = dev_get_drvdata(dev); +	unsigned long flags; + +	spin_lock_irqsave(&up->delayed_rts_lock, flags);  	if (up->need_delayed_rts && up->pin_default && up->pin_idle) {  		pinctrl_select_state(up->pins, up->pin_default);  		up->need_delayed_rts = 0;  	}  	up->is_suspending = false; +	spin_unlock_irqrestore(&up->delayed_rts_lock, flags);  }  static int serial_omap_suspend(struct device *dev) @@ -1747,6 +1752,7 @@ static void serial_omap_restore_context(struct uart_omap_port *up)  static int serial_omap_runtime_suspend(struct device *dev)  {  	struct uart_omap_port *up = dev_get_drvdata(dev); +	unsigned long flags;  	/*  	* When using 'no_console_suspend', the console UART must not be @@ -1762,8 +1768,10 @@ static int serial_omap_runtime_suspend(struct device *dev)  		return -EINVAL;  	if (up->pin_idle) { +		spin_lock_irqsave(&up->delayed_rts_lock, flags);  		pinctrl_select_state(up->pins, up->pin_idle);  		up->need_delayed_rts = 0; +		spin_unlock_irqrestore(&up->delayed_rts_lock, flags);  	}  	up->context_loss_cnt = serial_omap_get_context_loss_count(up); diff --git a/drivers/video/omap2/displays/panel-minnow-common.h b/drivers/video/omap2/displays/panel-minnow-common.h index 1001cc67f46..13ce3050a9e 100644 --- a/drivers/video/omap2/displays/panel-minnow-common.h +++ b/drivers/video/omap2/displays/panel-minnow-common.h @@ -18,7 +18,7 @@  #ifndef _MINNOW_PANEL_COMMON_HEADER_ -#define	INIT_DATA_VERSION	(0x072314) /*MM/DD/YY*/ +#define	INIT_DATA_VERSION	(0x081214) /*MM/DD/YY*/  /* This header file is used to sync Bootloader and Kernel Display Initialize   * Structure/Data, please make sure sync it for both Bootloader/Kernel when   * it changes some settings for Solomon/Orise. Bootloader should pass @@ -215,8 +215,8 @@ static u8 panel_init_ssd2848_320x320[] = {  2, OTM3201_CMD, 0xE9, 0x46,  /* Display Inversion Control (RB1h) */  2, OTM3201_CMD, 0xB1, 0x12, -/* ??? undefined */ -2, OTM3201_CMD, 0xE2, 0xF0, +/* MIPI RX Delay Setting (RE2h) */ +2, OTM3201_CMD, 0xE2, 0xF5,  /* Display Waveform Cycle setting (RBAh)  */  5, OTM3201_CMD, 0xBA, 0x06, 0x15, 0x2B, 0x01,  /* RGB Interface Blanking Porch setting (RB3h) diff --git a/drivers/video/omap2/displays/panel-minnow.c b/drivers/video/omap2/displays/panel-minnow.c index 8d127ae401f..d1014b0af1d 100644 --- a/drivers/video/omap2/displays/panel-minnow.c +++ b/drivers/video/omap2/displays/panel-minnow.c @@ -302,6 +302,8 @@ struct minnow_panel_data {  	int id_panel;  	int x_offset;  	int y_offset; +	int xres_um; +	int yres_um;  	int reset_ms;  	int release_ms; @@ -439,6 +441,27 @@ static void minnow_panel_sync_resume_mlocked(struct minnow_panel_data *mpd)  #endif  #ifdef CONFIG_WAKEUP_SOURCE_NOTIFY +static char *action_to_str(unsigned long action) +{ +	switch (action) { +	case DISPLAY_WAKE_EVENT_POWERKEY: +		return "power_key"; +	case DISPLAY_WAKE_EVENT_TOUCH: +		return "touch"; +	case DISPLAY_WAKE_EVENT_GESTURE: +		return "gesture_wrist"; +	case DISPLAY_WAKE_EVENT_GESTURE_VIEWON: +		return "gesture_view_on"; +	case DISPLAY_WAKE_EVENT_GESTURE_VIEWOFF: +		return "gesture_view_off"; +	case DISPLAY_WAKE_EVENT_DOCKON: +		return "dock_on"; +	case DISPLAY_WAKE_EVENT_DOCKOFF: +		return "dock_off"; +	} +	return "unsupported"; +} +  static int omapdss_displayenable_notify(struct notifier_block *self,  			unsigned long action, void *dev)  { @@ -448,7 +471,8 @@ static int omapdss_displayenable_notify(struct notifier_block *self,  	if (GET_WAKEUP_EVENT_TYPE(action) != WAKEUP_DISPLAY)  		return NOTIFY_OK; -	dev_info(&mpd->dssdev->dev, "%s, action is %lu", __func__, action); +	dev_info(&mpd->dssdev->dev, "%s, action is %lu-%s", +		 __func__, action, action_to_str(action));  	switch (action) {  	case DISPLAY_WAKE_EVENT_POWERKEY: @@ -1420,6 +1444,14 @@ static void minnow_panel_get_resolution(struct omap_dss_device *dssdev,  	*yres = dssdev->panel.timings.y_res;  } +static void minnow_panel_get_dimensions(struct omap_dss_device *dssdev, +		u32 *xres, u32 *yres) +{ +	struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev); +	*xres = mpd->xres_um; +	*yres = mpd->yres_um; +} +  static ssize_t minnow_panel_errors_show(struct device *dev,  	struct device_attribute *attr, char *buf)  { @@ -1874,6 +1906,49 @@ static ssize_t minnow_panel_store_interactivemode(struct device *dev,  	return r ? r : count;  } +static ssize_t minnow_panel_show_smartambient(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct omap_dss_device *dssdev = to_dss_device(dev); +	struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev); +	unsigned t; + +	t = mpd->smart_ambient; + +	return snprintf(buf, PAGE_SIZE, "%u\n", t); +} + +static ssize_t minnow_panel_store_smartambient(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct omap_dss_device *dssdev = to_dss_device(dev); +	struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev); +	unsigned long t; +	int r; +	bool enable; + +	r = kstrtoul(buf, 10, &t); +	if (!r) { +		mutex_lock(&mpd->lock); +		enable = !!t; +		if (mpd->state != DISPLAY_ENABLE) { +			dev_err(&dssdev->dev, "%s failed as display is not enabled\n", +				__func__); +			r = -EBUSY; +		} else if (mpd->smart_ambient != enable) { +			mpd->smart_ambient = enable; +		} +		mutex_unlock(&mpd->lock); +		if (r) +			dev_err(&dssdev->dev, "setting smartambient_status to %ld failed %d\n", +				t, r); +		else +			dev_dbg(&dssdev->dev, "setting smartambient_status to %ld succeeded\n", +				t); +	} + +	return r ? r : count; +}  static ssize_t minnow_panel_show_ambient_timeout(struct device *dev,  	struct device_attribute *attr, char *buf)  { @@ -2050,6 +2125,9 @@ static DEVICE_ATTR(init_data, S_IRUGO | S_IWUSR,  static DEVICE_ATTR(interactivemode, S_IRUGO | S_IWUSR,  		   minnow_panel_show_interactivemode,  		   minnow_panel_store_interactivemode); +static DEVICE_ATTR(smartambient, S_IRUSR | S_IWUSR, +		   minnow_panel_show_smartambient, +		   minnow_panel_store_smartambient);  static DEVICE_ATTR(ambient_timeout, S_IRUGO | S_IWUSR,  		   minnow_panel_show_ambient_timeout,  		   minnow_panel_store_ambient_timeout); @@ -2076,6 +2154,7 @@ static struct attribute *minnow_panel_attrs[] = {  #endif  #ifdef	CONFIG_HAS_AMBIENTMODE  	&dev_attr_interactivemode.attr, +	&dev_attr_smartambient.attr,  	&dev_attr_ambient_timeout.attr,  #endif  #ifdef	PANEL_PERF_TIME @@ -2409,6 +2488,15 @@ static int minnow_panel_dt_init(struct minnow_panel_data *mpd)  			mpd->dsi_config.lp_clk_max);  	} +	mpd->xres_um = 0; +	mpd->yres_um = 0; +	if (!of_property_read_u32_array(dt_node, "panel_size_um", range, 2)) { +		mpd->xres_um = range[0]; +		mpd->yres_um = range[1]; +		DTINFO("physical panel width = %d um, height = %d um\n", +			mpd->xres_um, mpd->yres_um); +	} +  	return 0;  } @@ -2453,6 +2541,7 @@ static int minnow_panel_probe(struct omap_dss_device *dssdev)  	mpd->dssdev = dssdev;  	mpd->first_enable = true;  	mpd->m4_state = DISPLAY_ENABLE; +	mpd->interactive = true;  	r = minnow_panel_dt_init(mpd);  	if (r) @@ -3030,8 +3119,9 @@ static int minnow_panel_enable_mlocked(struct minnow_panel_data *mpd)  	bool update;  	int r = 0; -	dev_info(&dssdev->dev, "%s: current state = %d\n", -		 __func__, dssdev->state); +	dev_info(&dssdev->dev, "%s: current display is %s\n", __func__, +		 dssdev->state == OMAP_DSS_DISPLAY_DISABLED +		 ? "disabled" : "enabled");  	if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED) {  		wake_lock(&mpd->wake_lock); @@ -3067,8 +3157,9 @@ static void minnow_panel_disable_mlocked(struct minnow_panel_data *mpd)  {  	struct omap_dss_device *dssdev = mpd->dssdev; -	dev_info(&dssdev->dev, "%s: current state = %d\n", -		 __func__, dssdev->state); +	dev_info(&dssdev->dev, "%s: current display is %s\n", __func__, +		 dssdev->state == OMAP_DSS_DISPLAY_DISABLED +		 ? "disabled" : "enabled");  	wake_lock(&mpd->wake_lock);  	mpd->early_inited = false; @@ -3091,7 +3182,6 @@ static void minnow_panel_disable_mlocked(struct minnow_panel_data *mpd)  static void minnow_panel_sync_display_status_mlocked(  	struct minnow_panel_data *mpd)  { -	struct m4sensorhub_data *m4sensorhub;  	enum display_state m4_state = mpd->state;  	/* special case for dock mode, set to DISPLAY_ENABLE  	 * to block all wakeup gestures @@ -3104,21 +3194,19 @@ static void minnow_panel_sync_display_status_mlocked(  	/* be safety to sync resume states first */  	minnow_panel_sync_resume_mlocked(mpd); -	m4sensorhub = m4sensorhub_client_get_drvdata(); -	if (m4sensorhub->mode != NORMALMODE) { +	if (m4sensorhub_get_current_mode() != NORMALMODE) {  		dev_err(&mpd->dssdev->dev,  			"M4 is not ready, unable to set screen status(%d)\n",  			m4_state);  		return;  	} -	if (m4sensorhub_reg_write_1byte(m4sensorhub, -					M4SH_REG_USERSETTINGS_SCREENSTATUS, -					m4_state, 0xFF) != 1) { + +	if (m4sensorhub_extern_set_display_status(m4_state) < 0) {  		dev_err(&mpd->dssdev->dev, -			"Unable to set screen status(%d) to M4\n", -			m4_state); +			"Unable to set screen status(%d) to M4\n", m4_state);  		return;  	} +  	dev_dbg(&mpd->dssdev->dev,  		"Set screen status(%d) to M4 success!\n", m4_state);  	mpd->m4_state = m4_state; @@ -3145,13 +3233,32 @@ static void led_set_dim_brightness(struct device *dev)  }  #endif /* CONFIG_HAS_AMBIENTMODE */ +static char *state_to_str(enum display_state state) +{ +	switch (state) { +	case DISPLAY_DISABLE: +		return "normal_off"; +	case DISPLAY_ENABLE: +		return "normal_on"; +#ifdef	CONFIG_HAS_AMBIENTMODE +	case DISPLAY_AMBIENT_OFF: +		return "ambient_off"; +	case DISPLAY_AMBIENT_ON: +		return "ambient_on"; +#endif +	} +	return "unknown???"; +} +  static int minnow_panel_change_state_mlocked(struct minnow_panel_data *mpd,  					     int state)  {  	int r = 0;  	dev_info(&mpd->dssdev->dev, -		 "change state %d ==> %d\n", mpd->state, state); +		 "change state %d(%s) ==> %d(%s)\n", +		 mpd->state, state_to_str(mpd->state), +		 state, state_to_str(state));  	/* already in state, return success */  	if (state == mpd->state) { @@ -3364,7 +3471,7 @@ static void minnow_panel_te_timeout_work_callback(struct work_struct *work)  static int minnow_panel_enable(struct omap_dss_device *dssdev)  {  	struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev); -	int r; +	int r, state;  	mutex_lock(&mpd->lock);  #ifdef	CONFIG_WAKEUP_SOURCE_NOTIFY @@ -3374,7 +3481,12 @@ static int minnow_panel_enable(struct omap_dss_device *dssdev)  		cancel_delayed_work(&mpd->early_init_timeout_work);  	}  #endif -	r = minnow_panel_change_state_mlocked(mpd, DISPLAY_ENABLE); +	state = DISPLAY_ENABLE; +#ifdef	CONFIG_HAS_AMBIENTMODE +	if (!mpd->interactive) +		state = DISPLAY_AMBIENT_ON; +#endif +	r = minnow_panel_change_state_mlocked(mpd, state);  	mutex_unlock(&mpd->lock);  	return r;  } @@ -3726,6 +3838,7 @@ static struct omap_dss_driver minnow_panel_driver = {  	.sync		= minnow_panel_sync,  	.get_resolution	= minnow_panel_get_resolution, +	.get_dimensions	= minnow_panel_get_dimensions,  	.get_recommended_bpp = omapdss_default_get_recommended_bpp,  	.enable_te	= minnow_panel_enable_te,  |