diff options
Diffstat (limited to 'drivers/rtc')
| -rw-r--r-- | drivers/rtc/rtc-sensorhub.c | 221 | 
1 files changed, 155 insertions, 66 deletions
diff --git a/drivers/rtc/rtc-sensorhub.c b/drivers/rtc/rtc-sensorhub.c index 4939ff8874f..c57d7ea95a5 100644 --- a/drivers/rtc/rtc-sensorhub.c +++ b/drivers/rtc/rtc-sensorhub.c @@ -22,9 +22,14 @@  #define DRIVER_NAME "rtc-sensorhub"  struct rtc_sensorhub_private_data { -	struct rtc_device *p_rtc; -	struct m4sensorhub_data *p_m4sensorhub_data; -	struct rtc_wkalrm next_alarm_set; +	struct rtc_device         *p_rtc; +	struct m4sensorhub_data   *p_m4sensorhub_data; +	struct rtc_wkalrm         next_alarm_set; + +	unsigned long   m4_seconds_cached; +	unsigned long   m4_boot_cached; +	unsigned long   sys_seconds_cached; +	unsigned long   sys_boot_cached;  };  static int rtc_sensorhub_rtc_alarm_irq_enable(struct device *p_dev, @@ -38,19 +43,22 @@ static int rtc_sensorhub_rtc_alarm_irq_enable(struct device *p_dev,  	dev_dbg(p_dev, "enable is %u\n", enable);  	if (!(p_priv_data->p_m4sensorhub_data)) { -		dev_err(p_dev, "RTC hardware not ready yet\n"); +		dev_err(p_dev, "Cannot %s alarm--RTC hardware not ready yet\n", +			(enable ? "set" : "clear"));  		return -EIO;  	} -	if (enable == 1) +	if (enable == 1) {  		err = m4sensorhub_irq_enable(p_priv_data->p_m4sensorhub_data,  				M4SH_IRQ_AP_ALARM_EXPIRED); -	else +		if (err < 0) +			dev_err(p_dev, "Enabling IRQ failed (%d)\n", err); +	} else {  		err = m4sensorhub_irq_disable(p_priv_data->p_m4sensorhub_data,  				M4SH_IRQ_AP_ALARM_EXPIRED); - -	if (err < 0) -		dev_err(p_dev, "couldn't enable irq\n"); +		if (err < 0) +			dev_err(p_dev, "Disabling IRQ failed (%d)\n", err); +	}  	return err;  } @@ -63,13 +71,10 @@ static int rtc_sensorhub_rtc_read_alarm(struct device *p_dev,  						platform_get_drvdata(p_platdev);  	struct rtc_time rtc = p_alrm->time; -	memcpy( -			p_alrm, &(p_priv_data->next_alarm_set), -			sizeof(struct rtc_wkalrm) -		); +	memcpy(p_alrm, &(p_priv_data->next_alarm_set), +			sizeof(struct rtc_wkalrm)); -	dev_info(p_dev, "alarm read for " -		"%d-%02d-%02d %02d:%02d:%02d UTC\n", +	dev_info(p_dev, "alarm read for %d-%02d-%02d %02d:%02d:%02d UTC\n",  		rtc.tm_year + 1900, rtc.tm_mon + 1, rtc.tm_mday,  		rtc.tm_hour, rtc.tm_min, rtc.tm_sec); @@ -90,8 +95,7 @@ static int rtc_sensorhub_rtc_set_alarm(struct device *p_dev,  	unsigned long requested_time, time_diff;  	int ret; -	dev_info(p_dev, "alarm requested for " -		"%d-%02d-%02d %02d:%02d:%02d UTC\n", +	dev_info(p_dev, "alarm requested for %d-%02d-%02d %02d:%02d:%02d UTC\n",  		rtc.tm_year + 1900, rtc.tm_mon + 1, rtc.tm_mday,  		rtc.tm_hour, rtc.tm_min, rtc.tm_sec); @@ -111,18 +115,16 @@ static int rtc_sensorhub_rtc_set_alarm(struct device *p_dev,  	time_diff = requested_time - tv_current.tv_sec;  	if (time_diff >= SECONDS_IN_DAY || time_diff <= 0) { -		dev_err(p_dev, "requested alarm out of range, rejecting alarm\n"); +		dev_err(p_dev, +			"requested alarm out of range, rejecting alarm\n");  		return -EINVAL;  	} -	if (m4sensorhub_reg_getsize( -				p_m4_drvdata, -				M4SH_REG_GENERAL_APALARM -			) != m4sensorhub_reg_write( -		p_m4_drvdata, M4SH_REG_GENERAL_APALARM, +	if (m4sensorhub_reg_getsize(p_m4_drvdata, M4SH_REG_GENERAL_APALARM) != +		m4sensorhub_reg_write(p_m4_drvdata, M4SH_REG_GENERAL_APALARM,  		(char *)&time_diff, m4sh_no_mask)) { -			dev_err(p_dev, "Failed to set M4 alarm!\n"); -			return -EIO; +		dev_err(p_dev, "Failed to set M4 alarm!\n"); +		return -EIO;  	}  	ret = rtc_sensorhub_rtc_alarm_irq_enable(p_dev, p_alrm->enabled); @@ -132,23 +134,20 @@ static int rtc_sensorhub_rtc_set_alarm(struct device *p_dev,  	}  	/* Store the info abt this alarm in our local datastructure */ -	memcpy( -			&(p_priv_data->next_alarm_set), p_alrm, -						sizeof(struct rtc_wkalrm) -		); +	memcpy(&(p_priv_data->next_alarm_set), p_alrm, +		sizeof(struct rtc_wkalrm));  	return 0;  }  static int rtc_sensorhub_get_rtc_from_m4(struct rtc_time *p_tm,  			struct m4sensorhub_data *p_m4_drvdata)  { -	u32 seconds; +	u32 seconds = 0; -	if (m4sensorhub_reg_getsize(p_m4_drvdata, -		M4SH_REG_GENERAL_UTC) != m4sensorhub_reg_read( -		p_m4_drvdata, M4SH_REG_GENERAL_UTC, +	if (m4sensorhub_reg_getsize(p_m4_drvdata, M4SH_REG_GENERAL_UTC) != +		m4sensorhub_reg_read(p_m4_drvdata, M4SH_REG_GENERAL_UTC,  		(char *)&seconds)) { -		pr_err("%s: Failed get M4 clock!\n", DRIVER_NAME); +		pr_err("%s: Failed to get M4 clock!\n", DRIVER_NAME);  		return -EIO;  	} @@ -159,16 +158,26 @@ static int rtc_sensorhub_get_rtc_from_m4(struct rtc_time *p_tm,  static int rtc_sensorhub_rtc_read_time(struct device *p_dev,  	struct rtc_time *p_tm)  { -	int err; +	int err = 0;  	struct platform_device *p_platdev = to_platform_device(p_dev);  	struct rtc_sensorhub_private_data *p_priv_data =  						platform_get_drvdata(p_platdev);  	if (!(p_priv_data->p_m4sensorhub_data)) { -		dev_err(p_dev, "read time, but RTC hardware not ready\n"); -		/* M4 driver is not yet ready, just give the time since boot -		and treat boot as start of epoch */ -		rtc_time_to_tm(get_seconds(), p_tm); +		if ((p_priv_data->sys_seconds_cached != 0) && +		(p_priv_data->sys_boot_cached != 0)) { +			dev_err(p_dev, +				"Using saved set RTC time (hardware not ready)\n"); +			/* Use saved settime request (will go to M4) */ +			rtc_time_to_tm(p_priv_data->sys_seconds_cached + +				get_seconds() - p_priv_data->sys_boot_cached, p_tm); +		} else { +			dev_err(p_dev, +				"Using cached M4 RTC time (hardware not ready)\n"); +			/* Use cached time from M4 */ +			rtc_time_to_tm(p_priv_data->m4_seconds_cached + +				get_seconds() - p_priv_data->m4_boot_cached, p_tm); +		}  		return 0;  	} @@ -181,36 +190,37 @@ static int rtc_sensorhub_rtc_read_time(struct device *p_dev,  static int rtc_sensorhub_rtc_set_time(struct device *p_dev,  	struct rtc_time *p_tm)  { -	unsigned long sec; +	unsigned long sec = 0;  	struct platform_device *p_platdev = to_platform_device(p_dev);  	struct rtc_sensorhub_private_data *p_priv_data =  						platform_get_drvdata(p_platdev);  	struct m4sensorhub_data *p_m4_drvdata =  			p_priv_data->p_m4sensorhub_data; -	if (!(p_m4_drvdata)) { -		dev_err(p_dev, "set time, but M4 not ready, ignore func call\n"); -		return 0; -	} -  	/* M4 expects the UTC time in seconds from Jan 1, 1970,  	basically epoch_time in seconds */  	rtc_tm_to_time(p_tm, &sec); +	if (!(p_m4_drvdata)) { +		dev_err(p_dev, +			"Saving set time request (hardware not ready)\n"); +		p_priv_data->sys_seconds_cached = sec; +		p_priv_data->sys_boot_cached = get_seconds(); +		return 0; +	} +  	/* M4 accepts time as u32*/ -	if (m4sensorhub_reg_getsize(p_m4_drvdata, -		M4SH_REG_GENERAL_UTC) != m4sensorhub_reg_write( -		p_m4_drvdata, M4SH_REG_GENERAL_UTC, +	if (m4sensorhub_reg_getsize(p_m4_drvdata, M4SH_REG_GENERAL_UTC) != +		m4sensorhub_reg_write(p_m4_drvdata, M4SH_REG_GENERAL_UTC,  		(char *)&sec, m4sh_no_mask)) { -			dev_err(p_dev, "set time, but failed to set M4 clock!\n"); +			dev_err(p_dev, +				"set time, but failed to set M4 clock!\n");  			return -EIO;  	}  	return 0;  } - -  static const struct rtc_class_ops rtc_sensorhub_rtc_ops = {  	.read_time = rtc_sensorhub_rtc_read_time,  	.set_time = rtc_sensorhub_rtc_set_time, @@ -219,8 +229,55 @@ static const struct rtc_class_ops rtc_sensorhub_rtc_ops = {  	.alarm_irq_enable = rtc_sensorhub_rtc_alarm_irq_enable,  }; +static int rtc_sensorhub_preflash(struct init_calldata *p_arg) +{ +	int err = 0; +	uint32_t seconds = 0; +	int size = 0; +	struct rtc_sensorhub_private_data *rtcpd = NULL; +	struct m4sensorhub_data *m4 = NULL; + +	if (p_arg == NULL) { +		pr_err("%s: No callback data received.\n", __func__); +		err = -ENODATA; +		goto rtc_sensorhub_preflash_fail; +	} else if (p_arg->p_data == NULL) { +		pr_err("%s: No private data received.\n", __func__); +		err = -ENODATA; +		goto rtc_sensorhub_preflash_fail; +	} else if (p_arg->p_m4sensorhub_data == NULL) { +		pr_err("%s: M4 data is NULL.\n", __func__); +		err = -ENODATA; +		goto rtc_sensorhub_preflash_fail; +	} + +	/* +	 * NOTE: We don't want to save the M4 data struct; +	 *       M4 will be reflashed after this callback returns +	 *       (thus the struct will become invalid but we have +	 *        no way to communicate that to the rest of the driver). +	 */ +	rtcpd = p_arg->p_data; +	m4 = p_arg->p_m4sensorhub_data; +	size = m4sensorhub_reg_getsize(m4, M4SH_REG_GENERAL_UTC); +	err = m4sensorhub_reg_read(m4, M4SH_REG_GENERAL_UTC, (char *)&seconds); +	if (err < 0) { +		pr_err("%s: Failed to read RTC seconds from M4.\n", __func__); +		goto rtc_sensorhub_preflash_fail; +	} else if (err != size) { +		pr_err("%s: Read %d bytes instead of %d.\n", +			__func__, err, size); +	} + +	rtcpd->m4_seconds_cached = seconds; +	rtcpd->m4_boot_cached = get_seconds(); + +rtc_sensorhub_preflash_fail: +	return err; +} +  static void rtc_handle_sensorhub_irq(enum m4sensorhub_irqs int_event, -						void *p_data) +	void *p_data)  {  	struct rtc_sensorhub_private_data *p_priv_data =  			(struct rtc_sensorhub_private_data *)(p_data); @@ -236,34 +293,56 @@ static int rtc_sensorhub_init(struct init_calldata *p_arg)  	struct timespec tv;  	struct rtc_sensorhub_private_data *p_priv_data =  			(struct rtc_sensorhub_private_data *)(p_arg->p_data); +	uint32_t seconds = 0;  	p_priv_data->p_m4sensorhub_data = p_arg->p_m4sensorhub_data; -	/* read RTC time from M4 and set the system time */ -	err = rtc_sensorhub_get_rtc_from_m4(&rtc, -				p_priv_data->p_m4sensorhub_data); -	if (err) { -		pr_err("%s: get_rtc failed\n", DRIVER_NAME); -		return 0; +	if ((p_priv_data->sys_seconds_cached != 0) && +		(p_priv_data->sys_boot_cached != 0)) { +		pr_err("%s: Setting M4 to a saved time request\n", __func__); +		seconds = p_priv_data->sys_seconds_cached + +			get_seconds() - p_priv_data->sys_boot_cached; +		if (m4sensorhub_reg_getsize(p_priv_data->p_m4sensorhub_data, +			M4SH_REG_GENERAL_UTC) != m4sensorhub_reg_write( +			p_priv_data->p_m4sensorhub_data, M4SH_REG_GENERAL_UTC, +			(char *)&seconds, m4sh_no_mask)) { +				pr_err("%s: Failed to set M4 RTC\n", +					__func__); +				return 0; +		} +		/* +		 * We don't write directly to tv_sec here because we want +		 * to print below the system clock we are setting, +		 * which means we need to populate rtc anyway. +		 */ +		rtc_time_to_tm(seconds, &rtc); +	} else { +		/* read RTC time from M4 and set the system time */ +		err = rtc_sensorhub_get_rtc_from_m4(&rtc, +					p_priv_data->p_m4sensorhub_data); +		if (err) { +			pr_err("%s: get_rtc failed\n", DRIVER_NAME); +			return 0; +		}  	} +	tv.tv_nsec = 0;  /* Initialize variable or do_settimeofday will fail */  	rtc_tm_to_time(&rtc, &tv.tv_sec);  	err = do_settimeofday(&tv);  	if (err) -		pr_err("%s: settimeofday failed\n", DRIVER_NAME); +		pr_err("%s: settimeofday failed (err=%d)\n", DRIVER_NAME, err); -	pr_info("setting system clock to " -		"%d-%02d-%02d %02d:%02d:%02d UTC (%u)\n", +	pr_info("%s %d-%02d-%02d %02d:%02d:%02d UTC (%u)\n", +		"setting system clock to",  		rtc.tm_year + 1900, rtc.tm_mon + 1, rtc.tm_mday,  		rtc.tm_hour, rtc.tm_min, rtc.tm_sec,  		(unsigned int) tv.tv_sec);  	/* register an irq handler*/  	err = m4sensorhub_irq_register(p_priv_data->p_m4sensorhub_data, -						M4SH_IRQ_AP_ALARM_EXPIRED, -						rtc_handle_sensorhub_irq, -						p_priv_data, 1); +		M4SH_IRQ_AP_ALARM_EXPIRED, rtc_handle_sensorhub_irq, +		p_priv_data, 1);  	if (err < 0)  		pr_err("%s: irq register failed\n", DRIVER_NAME); @@ -296,7 +375,7 @@ static int rtc_sensorhub_probe(struct platform_device *p_platdev)  	}  	p_rtc = devm_rtc_device_register(&p_platdev->dev, "rtc_sensorhub", -				&rtc_sensorhub_rtc_ops, THIS_MODULE); +		&rtc_sensorhub_rtc_ops, THIS_MODULE);  	if (IS_ERR(p_rtc)) {  		err = PTR_ERR(p_rtc); @@ -305,14 +384,24 @@ static int rtc_sensorhub_probe(struct platform_device *p_platdev)  	p_priv_data->p_rtc = p_rtc; +	err = m4sensorhub_register_preflash_callback(rtc_sensorhub_preflash, +		p_priv_data); +	if (err < 0) { +		dev_err(&(p_platdev->dev), +			"Failed to register M4 preflash callback\n"); +		goto err_unregister_rtc; +	} +  	err = m4sensorhub_register_initcall(rtc_sensorhub_init, p_priv_data);  	if (err) {  		dev_err(&(p_platdev->dev), "can't register init with m4\n"); -		goto err_unregister_rtc; +		goto err_unregister_preflash_callback;  	}  	return 0; +err_unregister_preflash_callback: +	m4sensorhub_unregister_preflash_callback(rtc_sensorhub_preflash);  err_unregister_rtc:  	devm_rtc_device_unregister(&p_platdev->dev, p_rtc);  	kfree(p_rtc);  |