diff options
Diffstat (limited to 'drivers/rtc/rtc-ab8500.c')
| -rw-r--r-- | drivers/rtc/rtc-ab8500.c | 257 | 
1 files changed, 211 insertions, 46 deletions
diff --git a/drivers/rtc/rtc-ab8500.c b/drivers/rtc/rtc-ab8500.c index e346705aae9..727e2f5d14d 100644 --- a/drivers/rtc/rtc-ab8500.c +++ b/drivers/rtc/rtc-ab8500.c @@ -15,8 +15,9 @@  #include <linux/platform_device.h>  #include <linux/rtc.h>  #include <linux/mfd/abx500.h> -#include <linux/mfd/ab8500.h> +#include <linux/mfd/abx500/ab8500.h>  #include <linux/delay.h> +#include <linux/of.h>  #define AB8500_RTC_SOFF_STAT_REG	0x00  #define AB8500_RTC_CC_CONF_REG		0x01 @@ -34,6 +35,10 @@  #define AB8500_RTC_FORCE_BKUP_REG	0x0D  #define AB8500_RTC_CALIB_REG		0x0E  #define AB8500_RTC_SWITCH_STAT_REG	0x0F +#define AB8540_RTC_ALRM_SEC		0x22 +#define AB8540_RTC_ALRM_MIN_LOW_REG	0x23 +#define AB8540_RTC_ALRM_MIN_MID_REG	0x24 +#define AB8540_RTC_ALRM_MIN_HI_REG	0x25  /* RtcReadRequest bits */  #define RTC_READ_REQUEST		0x01 @@ -57,6 +62,11 @@ static const u8 ab8500_rtc_alarm_regs[] = {  	AB8500_RTC_ALRM_MIN_LOW_REG  }; +static const u8 ab8540_rtc_alarm_regs[] = { +	AB8540_RTC_ALRM_MIN_HI_REG, AB8540_RTC_ALRM_MIN_MID_REG, +	AB8540_RTC_ALRM_MIN_LOW_REG, AB8540_RTC_ALRM_SEC +}; +  /* Calculate the seconds from 1970 to 01-01-2000 00:00:00 */  static unsigned long get_elapsed_seconds(int year)  { @@ -88,22 +98,17 @@ static int ab8500_rtc_read_time(struct device *dev, struct rtc_time *tm)  	if (retval < 0)  		return retval; -	/* Early AB8500 chips will not clear the rtc read request bit */ -	if (abx500_get_chip_id(dev) == 0) { -		msleep(1); -	} else { -		/* Wait for some cycles after enabling the rtc read in ab8500 */ -		while (time_before(jiffies, timeout)) { -			retval = abx500_get_register_interruptible(dev, -				AB8500_RTC, AB8500_RTC_READ_REQ_REG, &value); -			if (retval < 0) -				return retval; - -			if (!(value & RTC_READ_REQUEST)) -				break; - -			msleep(1); -		} +	/* Wait for some cycles after enabling the rtc read in ab8500 */ +	while (time_before(jiffies, timeout)) { +		retval = abx500_get_register_interruptible(dev, +			AB8500_RTC, AB8500_RTC_READ_REQ_REG, &value); +		if (retval < 0) +			return retval; + +		if (!(value & RTC_READ_REQUEST)) +			break; + +		usleep_range(1000, 5000);  	}  	/* Read the Watchtime registers */ @@ -224,7 +229,8 @@ static int ab8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)  {  	int retval, i;  	unsigned char buf[ARRAY_SIZE(ab8500_rtc_alarm_regs)]; -	unsigned long mins, secs = 0; +	unsigned long mins, secs = 0, cursec = 0; +	struct rtc_time curtm;  	if (alarm->time.tm_year < (AB8500_RTC_EPOCH - 1900)) {  		dev_dbg(dev, "year should be equal to or greater than %d\n", @@ -236,6 +242,18 @@ static int ab8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)  	rtc_tm_to_time(&alarm->time, &secs);  	/* +	 * Check whether alarm is set less than 1min. +	 * Since our RTC doesn't support alarm resolution less than 1min, +	 * return -EINVAL, so UIE EMUL can take it up, incase of UIE_ON +	 */ +	ab8500_rtc_read_time(dev, &curtm); /* Read current time */ +	rtc_tm_to_time(&curtm, &cursec); +	if ((secs - cursec) < 59) { +		dev_dbg(dev, "Alarm less than 1 minute not supported\r\n"); +		return -EINVAL; +	} + +	/*  	 * Convert it to the number of seconds since 01-01-2000 00:00:00, since  	 * we only have a small counter in the RTC.  	 */ @@ -258,6 +276,145 @@ static int ab8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)  	return ab8500_rtc_irq_enable(dev, alarm->enabled);  } +static int ab8540_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ +	int retval, i; +	unsigned char buf[ARRAY_SIZE(ab8540_rtc_alarm_regs)]; +	unsigned long mins, secs = 0; + +	if (alarm->time.tm_year < (AB8500_RTC_EPOCH - 1900)) { +		dev_dbg(dev, "year should be equal to or greater than %d\n", +				AB8500_RTC_EPOCH); +		return -EINVAL; +	} + +	/* Get the number of seconds since 1970 */ +	rtc_tm_to_time(&alarm->time, &secs); + +	/* +	 * Convert it to the number of seconds since 01-01-2000 00:00:00 +	 */ +	secs -= get_elapsed_seconds(AB8500_RTC_EPOCH); +	mins = secs / 60; + +	buf[3] = secs % 60; +	buf[2] = mins & 0xFF; +	buf[1] = (mins >> 8) & 0xFF; +	buf[0] = (mins >> 16) & 0xFF; + +	/* Set the alarm time */ +	for (i = 0; i < ARRAY_SIZE(ab8540_rtc_alarm_regs); i++) { +		retval = abx500_set_register_interruptible(dev, AB8500_RTC, +			ab8540_rtc_alarm_regs[i], buf[i]); +		if (retval < 0) +			return retval; +	} + +	return ab8500_rtc_irq_enable(dev, alarm->enabled); +} + +static int ab8500_rtc_set_calibration(struct device *dev, int calibration) +{ +	int retval; +	u8  rtccal = 0; + +	/* +	 * Check that the calibration value (which is in units of 0.5 +	 * parts-per-million) is in the AB8500's range for RtcCalibration +	 * register. -128 (0x80) is not permitted because the AB8500 uses +	 * a sign-bit rather than two's complement, so 0x80 is just another +	 * representation of zero. +	 */ +	if ((calibration < -127) || (calibration > 127)) { +		dev_err(dev, "RtcCalibration value outside permitted range\n"); +		return -EINVAL; +	} + +	/* +	 * The AB8500 uses sign (in bit7) and magnitude (in bits0-7) +	 * so need to convert to this sort of representation before writing +	 * into RtcCalibration register... +	 */ +	if (calibration >= 0) +		rtccal = 0x7F & calibration; +	else +		rtccal = ~(calibration - 1) | 0x80; + +	retval = abx500_set_register_interruptible(dev, AB8500_RTC, +			AB8500_RTC_CALIB_REG, rtccal); + +	return retval; +} + +static int ab8500_rtc_get_calibration(struct device *dev, int *calibration) +{ +	int retval; +	u8  rtccal = 0; + +	retval =  abx500_get_register_interruptible(dev, AB8500_RTC, +			AB8500_RTC_CALIB_REG, &rtccal); +	if (retval >= 0) { +		/* +		 * The AB8500 uses sign (in bit7) and magnitude (in bits0-7) +		 * so need to convert value from RtcCalibration register into +		 * a two's complement signed value... +		 */ +		if (rtccal & 0x80) +			*calibration = 0 - (rtccal & 0x7F); +		else +			*calibration = 0x7F & rtccal; +	} + +	return retval; +} + +static ssize_t ab8500_sysfs_store_rtc_calibration(struct device *dev, +				struct device_attribute *attr, +				const char *buf, size_t count) +{ +	int retval; +	int calibration = 0; + +	if (sscanf(buf, " %i ", &calibration) != 1) { +		dev_err(dev, "Failed to store RTC calibration attribute\n"); +		return -EINVAL; +	} + +	retval = ab8500_rtc_set_calibration(dev, calibration); + +	return retval ? retval : count; +} + +static ssize_t ab8500_sysfs_show_rtc_calibration(struct device *dev, +				struct device_attribute *attr, char *buf) +{ +	int  retval = 0; +	int  calibration = 0; + +	retval = ab8500_rtc_get_calibration(dev, &calibration); +	if (retval < 0) { +		dev_err(dev, "Failed to read RTC calibration attribute\n"); +		sprintf(buf, "0\n"); +		return retval; +	} + +	return sprintf(buf, "%d\n", calibration); +} + +static DEVICE_ATTR(rtc_calibration, S_IRUGO | S_IWUSR, +		   ab8500_sysfs_show_rtc_calibration, +		   ab8500_sysfs_store_rtc_calibration); + +static int ab8500_sysfs_rtc_register(struct device *dev) +{ +	return device_create_file(dev, &dev_attr_rtc_calibration); +} + +static void ab8500_sysfs_rtc_unregister(struct device *dev) +{ +	device_remove_file(dev, &dev_attr_rtc_calibration); +} +  static irqreturn_t rtc_alarm_handler(int irq, void *data)  {  	struct rtc_device *rtc = data; @@ -277,8 +434,22 @@ static const struct rtc_class_ops ab8500_rtc_ops = {  	.alarm_irq_enable	= ab8500_rtc_irq_enable,  }; -static int __devinit ab8500_rtc_probe(struct platform_device *pdev) +static const struct rtc_class_ops ab8540_rtc_ops = { +	.read_time		= ab8500_rtc_read_time, +	.set_time		= ab8500_rtc_set_time, +	.read_alarm		= ab8500_rtc_read_alarm, +	.set_alarm		= ab8540_rtc_set_alarm, +	.alarm_irq_enable	= ab8500_rtc_irq_enable, +}; + +static struct platform_device_id ab85xx_rtc_ids[] = { +	{ "ab8500-rtc", (kernel_ulong_t)&ab8500_rtc_ops, }, +	{ "ab8540-rtc", (kernel_ulong_t)&ab8540_rtc_ops, }, +}; + +static int ab8500_rtc_probe(struct platform_device *pdev)  { +	const struct platform_device_id *platid = platform_get_device_id(pdev);  	int err;  	struct rtc_device *rtc;  	u8 rtc_ctrl; @@ -295,7 +466,7 @@ static int __devinit ab8500_rtc_probe(struct platform_device *pdev)  		return err;  	/* Wait for reset by the PorRtc */ -	msleep(1); +	usleep_range(1000, 5000);  	err = abx500_get_register_interruptible(&pdev->dev, AB8500_RTC,  		AB8500_RTC_STAT_REG, &rtc_ctrl); @@ -308,34 +479,37 @@ static int __devinit ab8500_rtc_probe(struct platform_device *pdev)  		return -ENODEV;  	} -	rtc = rtc_device_register("ab8500-rtc", &pdev->dev, &ab8500_rtc_ops, -			THIS_MODULE); +	device_init_wakeup(&pdev->dev, true); + +	rtc = devm_rtc_device_register(&pdev->dev, "ab8500-rtc", +				(struct rtc_class_ops *)platid->driver_data, +				THIS_MODULE);  	if (IS_ERR(rtc)) {  		dev_err(&pdev->dev, "Registration failed\n");  		err = PTR_ERR(rtc);  		return err;  	} -	err = request_threaded_irq(irq, NULL, rtc_alarm_handler, 0, -				   "ab8500-rtc", rtc); -	if (err < 0) { -		rtc_device_unregister(rtc); +	err = devm_request_threaded_irq(&pdev->dev, irq, NULL, +			rtc_alarm_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT, +			"ab8500-rtc", rtc); +	if (err < 0)  		return err; -	}  	platform_set_drvdata(pdev, rtc); +	err = ab8500_sysfs_rtc_register(&pdev->dev); +	if (err) { +		dev_err(&pdev->dev, "sysfs RTC failed to register\n"); +		return err; +	} +  	return 0;  } -static int __devexit ab8500_rtc_remove(struct platform_device *pdev) +static int ab8500_rtc_remove(struct platform_device *pdev)  { -	struct rtc_device *rtc = platform_get_drvdata(pdev); -	int irq = platform_get_irq_byname(pdev, "ALARM"); - -	free_irq(irq, rtc); -	rtc_device_unregister(rtc); -	platform_set_drvdata(pdev, NULL); +	ab8500_sysfs_rtc_unregister(&pdev->dev);  	return 0;  } @@ -346,21 +520,12 @@ static struct platform_driver ab8500_rtc_driver = {  		.owner = THIS_MODULE,  	},  	.probe	= ab8500_rtc_probe, -	.remove = __devexit_p(ab8500_rtc_remove), +	.remove = ab8500_rtc_remove, +	.id_table = ab85xx_rtc_ids,  }; -static int __init ab8500_rtc_init(void) -{ -	return platform_driver_register(&ab8500_rtc_driver); -} - -static void __exit ab8500_rtc_exit(void) -{ -	platform_driver_unregister(&ab8500_rtc_driver); -} +module_platform_driver(ab8500_rtc_driver); -module_init(ab8500_rtc_init); -module_exit(ab8500_rtc_exit);  MODULE_AUTHOR("Virupax Sadashivpetimath <virupax.sadashivpetimath@stericsson.com>");  MODULE_DESCRIPTION("AB8500 RTC Driver");  MODULE_LICENSE("GPL v2");  | 
