diff options
Diffstat (limited to 'drivers/rtc')
| -rw-r--r-- | drivers/rtc/Kconfig | 51 | ||||
| -rw-r--r-- | drivers/rtc/Makefile | 4 | ||||
| -rw-r--r-- | drivers/rtc/rtc-ab3100.c | 2 | ||||
| -rw-r--r-- | drivers/rtc/rtc-bfin.c | 15 | ||||
| -rw-r--r-- | drivers/rtc/rtc-cmos.c | 6 | ||||
| -rw-r--r-- | drivers/rtc/rtc-ds3232.c | 324 | ||||
| -rw-r--r-- | drivers/rtc/rtc-fm3130.c | 183 | ||||
| -rw-r--r-- | drivers/rtc/rtc-imxdi.c | 519 | ||||
| -rw-r--r-- | drivers/rtc/rtc-isl12022.c | 327 | ||||
| -rw-r--r-- | drivers/rtc/rtc-jz4740.c | 345 | ||||
| -rw-r--r-- | drivers/rtc/rtc-m41t80.c | 2 | ||||
| -rw-r--r-- | drivers/rtc/rtc-m48t59.c | 5 | ||||
| -rw-r--r-- | drivers/rtc/rtc-m48t86.c | 2 | ||||
| -rw-r--r-- | drivers/rtc/rtc-max6900.c | 2 | ||||
| -rw-r--r-- | drivers/rtc/rtc-mpc5121.c | 4 | ||||
| -rw-r--r-- | drivers/rtc/rtc-mxc.c | 6 | ||||
| -rw-r--r-- | drivers/rtc/rtc-nuc900.c | 64 | ||||
| -rw-r--r-- | drivers/rtc/rtc-pcf8563.c | 8 | ||||
| -rw-r--r-- | drivers/rtc/rtc-pl031.c | 5 | ||||
| -rw-r--r-- | drivers/rtc/rtc-pxa.c | 42 | ||||
| -rw-r--r-- | drivers/rtc/rtc-rp5c01.c | 89 | ||||
| -rw-r--r-- | drivers/rtc/rtc-rx8025.c | 2 | ||||
| -rw-r--r-- | drivers/rtc/rtc-s3c.c | 57 | 
23 files changed, 1917 insertions, 147 deletions
| diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 10ba12c8c5e..48ca7132cc0 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -166,6 +166,16 @@ config RTC_DRV_DS1672  	  This driver can also be built as a module. If so, the module  	  will be called rtc-ds1672. +config RTC_DRV_DS3232 +	tristate "Dallas/Maxim DS3232" +	depends on RTC_CLASS && I2C +	help +	  If you say yes here you get support for Dallas Semiconductor +	  DS3232 real-time clock chips. + +	  This driver can also be built as a module.  If so, the module +	  will be called rtc-ds3232. +  config RTC_DRV_MAX6900  	tristate "Maxim MAX6900"  	help @@ -203,6 +213,15 @@ config RTC_DRV_ISL1208  	  This driver can also be built as a module. If so, the module  	  will be called rtc-isl1208. +config RTC_DRV_ISL12022 +	tristate "Intersil ISL12022" +	help +	  If you say yes here you get support for the +	  Intersil ISL12022 RTC chip. + +	  This driver can also be built as a module. If so, the module +	  will be called rtc-isl12022. +  config RTC_DRV_X1205  	tristate "Xicor/Intersil X1205"  	help @@ -537,6 +556,16 @@ config RTC_DRV_MSM6242  	  This driver can also be built as a module. If so, the module  	  will be called rtc-msm6242. +config RTC_DRV_IMXDI +	tristate "Freescale IMX DryIce Real Time Clock" +	depends on ARCH_MX25 +	depends on RTC_CLASS +	help +	   Support for Freescale IMX DryIce RTC + +	   This driver can also be built as a module, if so, the module +	   will be called "rtc-imxdi". +  config RTC_MXC  	tristate "Freescale MXC Real Time Clock"  	depends on ARCH_MXC @@ -645,9 +674,16 @@ config RTC_DRV_OMAP  	  DA8xx/OMAP-L13x chips.  This driver can also be built as a  	  module called rtc-omap. +config HAVE_S3C_RTC +	bool +	help +	  This will include RTC support for Samsung SoCs. If +	  you want to include RTC support for any machine, kindly +	  select this in the respective mach-XXXX/Kconfig file. +  config RTC_DRV_S3C  	tristate "Samsung S3C series SoC RTC" -	depends on ARCH_S3C2410 || ARCH_S3C64XX +	depends on ARCH_S3C2410 || ARCH_S3C64XX || HAVE_S3C_RTC  	help  	  RTC (Realtime Clock) driver for the clock inbuilt into the  	  Samsung S3C24XX series of SoCs. This can provide periodic @@ -774,7 +810,7 @@ config RTC_DRV_AT91SAM9_GPBR  config RTC_DRV_AU1XXX  	tristate "Au1xxx Counter0 RTC support" -	depends on SOC_AU1X00 +	depends on MIPS_ALCHEMY  	help  	  This is a driver for the Au1xxx on-chip Counter0 (Time-Of-Year  	  counter) to be used as a RTC. @@ -905,4 +941,15 @@ config RTC_DRV_MPC5121  	  This driver can also be built as a module. If so, the module  	  will be called rtc-mpc5121. +config RTC_DRV_JZ4740 +	tristate "Ingenic JZ4740 SoC" +	depends on RTC_CLASS +	depends on MACH_JZ4740 +	help +	  If you say yes here you get support for the Ingenic JZ4740 SoC RTC +	  controller. + +	  This driver can also be buillt as a module. If so, the module +	  will be called rtc-jz4740. +  endif # RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 5adbba7cf89..0f207b3b583 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -41,12 +41,16 @@ obj-$(CONFIG_RTC_DRV_DS1511)	+= rtc-ds1511.o  obj-$(CONFIG_RTC_DRV_DS1553)	+= rtc-ds1553.o  obj-$(CONFIG_RTC_DRV_DS1672)	+= rtc-ds1672.o  obj-$(CONFIG_RTC_DRV_DS1742)	+= rtc-ds1742.o +obj-$(CONFIG_RTC_DRV_DS3232)	+= rtc-ds3232.o  obj-$(CONFIG_RTC_DRV_DS3234)	+= rtc-ds3234.o  obj-$(CONFIG_RTC_DRV_EFI)	+= rtc-efi.o  obj-$(CONFIG_RTC_DRV_EP93XX)	+= rtc-ep93xx.o  obj-$(CONFIG_RTC_DRV_FM3130)	+= rtc-fm3130.o  obj-$(CONFIG_RTC_DRV_GENERIC)	+= rtc-generic.o +obj-$(CONFIG_RTC_DRV_IMXDI)	+= rtc-imxdi.o  obj-$(CONFIG_RTC_DRV_ISL1208)	+= rtc-isl1208.o +obj-$(CONFIG_RTC_DRV_ISL12022)	+= rtc-isl12022.o +obj-$(CONFIG_RTC_DRV_JZ4740)	+= rtc-jz4740.o  obj-$(CONFIG_RTC_DRV_M41T80)	+= rtc-m41t80.o  obj-$(CONFIG_RTC_DRV_M41T94)	+= rtc-m41t94.o  obj-$(CONFIG_RTC_DRV_M48T35)	+= rtc-m48t35.o diff --git a/drivers/rtc/rtc-ab3100.c b/drivers/rtc/rtc-ab3100.c index d26780ea254..261a07e0fb2 100644 --- a/drivers/rtc/rtc-ab3100.c +++ b/drivers/rtc/rtc-ab3100.c @@ -235,6 +235,7 @@ static int __init ab3100_rtc_probe(struct platform_device *pdev)  		err = PTR_ERR(rtc);  		return err;  	} +	platform_set_drvdata(pdev, rtc);  	return 0;  } @@ -244,6 +245,7 @@ static int __exit ab3100_rtc_remove(struct platform_device *pdev)  	struct rtc_device *rtc = platform_get_drvdata(pdev);  	rtc_device_unregister(rtc); +	platform_set_drvdata(pdev, NULL);  	return 0;  } diff --git a/drivers/rtc/rtc-bfin.c b/drivers/rtc/rtc-bfin.c index 72b2bcc2c22..d4fb82d85e9 100644 --- a/drivers/rtc/rtc-bfin.c +++ b/drivers/rtc/rtc-bfin.c @@ -426,7 +426,7 @@ static int bfin_rtc_suspend(struct platform_device *pdev, pm_message_t state)  		enable_irq_wake(IRQ_RTC);  		bfin_rtc_sync_pending(&pdev->dev);  	} else -		bfin_rtc_int_clear(-1); +		bfin_rtc_int_clear(0);  	return 0;  } @@ -435,8 +435,17 @@ static int bfin_rtc_resume(struct platform_device *pdev)  {  	if (device_may_wakeup(&pdev->dev))  		disable_irq_wake(IRQ_RTC); -	else -		bfin_write_RTC_ISTAT(-1); + +	/* +	 * Since only some of the RTC bits are maintained externally in the +	 * Vbat domain, we need to wait for the RTC MMRs to be synced into +	 * the core after waking up.  This happens every RTC 1HZ.  Once that +	 * has happened, we can go ahead and re-enable the important write +	 * complete interrupt event. +	 */ +	while (!(bfin_read_RTC_ISTAT() & RTC_ISTAT_SEC)) +		continue; +	bfin_rtc_int_set(RTC_ISTAT_WRITE_COMPLETE);  	return 0;  } diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index 11b8ea29d2b..5856167a0c9 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -970,7 +970,6 @@ static inline int cmos_poweroff(struct device *dev)  #include <linux/acpi.h> -#ifdef	CONFIG_PM  static u32 rtc_handler(void *context)  {  	acpi_clear_event(ACPI_EVENT_RTC); @@ -999,11 +998,6 @@ static void rtc_wake_off(struct device *dev)  {  	acpi_disable_event(ACPI_EVENT_RTC, 0);  } -#else -#define rtc_wake_setup()	do{}while(0) -#define rtc_wake_on		NULL -#define rtc_wake_off		NULL -#endif  /* Every ACPI platform has a mc146818 compatible "cmos rtc".  Here we find   * its device node and pass extra config data.  This helps its driver use diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c new file mode 100644 index 00000000000..9de8516e353 --- /dev/null +++ b/drivers/rtc/rtc-ds3232.c @@ -0,0 +1,324 @@ +/* + * RTC client/driver for the Maxim/Dallas DS3232 Real-Time Clock over I2C + * + * Copyright (C) 2009-2010 Freescale Semiconductor. + * + * 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. + */ +/* + * It would be more efficient to use i2c msgs/i2c_transfer directly but, as + * recommened in .../Documentation/i2c/writing-clients section + * "Sending and receiving", using SMBus level communication is preferred. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/rtc.h> +#include <linux/bcd.h> +#include <linux/workqueue.h> +#include <linux/slab.h> + +#define DS3232_REG_SECONDS	0x00 +#define DS3232_REG_MINUTES	0x01 +#define DS3232_REG_HOURS	0x02 +#define DS3232_REG_AMPM		0x02 +#define DS3232_REG_DAY		0x03 +#define DS3232_REG_DATE		0x04 +#define DS3232_REG_MONTH	0x05 +#define DS3232_REG_CENTURY	0x05 +#define DS3232_REG_YEAR		0x06 +#define DS3232_REG_ALARM1         0x07	/* Alarm 1 BASE */ +#define DS3232_REG_ALARM2         0x0B	/* Alarm 2 BASE */ +#define DS3232_REG_CR		0x0E	/* Control register */ +#	define DS3232_REG_CR_nEOSC        0x80 +#       define DS3232_REG_CR_INTCN        0x04 +#       define DS3232_REG_CR_A2IE        0x02 +#       define DS3232_REG_CR_A1IE        0x01 + +#define DS3232_REG_SR	0x0F	/* control/status register */ +#	define DS3232_REG_SR_OSF   0x80 +#       define DS3232_REG_SR_BSY   0x04 +#       define DS3232_REG_SR_A2F   0x02 +#       define DS3232_REG_SR_A1F   0x01 + +struct ds3232 { +	struct i2c_client *client; +	struct rtc_device *rtc; +	struct work_struct work; + +	/* The mutex protects alarm operations, and prevents a race +	 * between the enable_irq() in the workqueue and the free_irq() +	 * in the remove function. +	 */ +	struct mutex mutex; +	int exiting; +}; + +static struct i2c_driver ds3232_driver; + +static int ds3232_check_rtc_status(struct i2c_client *client) +{ +	int ret = 0; +	int control, stat; + +	stat = i2c_smbus_read_byte_data(client, DS3232_REG_SR); +	if (stat < 0) +		return stat; + +	if (stat & DS3232_REG_SR_OSF) +		dev_warn(&client->dev, +				"oscillator discontinuity flagged, " +				"time unreliable\n"); + +	stat &= ~(DS3232_REG_SR_OSF | DS3232_REG_SR_A1F | DS3232_REG_SR_A2F); + +	ret = i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat); +	if (ret < 0) +		return ret; + +	/* If the alarm is pending, clear it before requesting +	 * the interrupt, so an interrupt event isn't reported +	 * before everything is initialized. +	 */ + +	control = i2c_smbus_read_byte_data(client, DS3232_REG_CR); +	if (control < 0) +		return control; + +	control &= ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE); +	control |= DS3232_REG_CR_INTCN; + +	return i2c_smbus_write_byte_data(client, DS3232_REG_CR, control); +} + +static int ds3232_read_time(struct device *dev, struct rtc_time *time) +{ +	struct i2c_client *client = to_i2c_client(dev); +	int ret; +	u8 buf[7]; +	unsigned int year, month, day, hour, minute, second; +	unsigned int week, twelve_hr, am_pm; +	unsigned int century, add_century = 0; + +	ret = i2c_smbus_read_i2c_block_data(client, DS3232_REG_SECONDS, 7, buf); + +	if (ret < 0) +		return ret; +	if (ret < 7) +		return -EIO; + +	second = buf[0]; +	minute = buf[1]; +	hour = buf[2]; +	week = buf[3]; +	day = buf[4]; +	month = buf[5]; +	year = buf[6]; + +	/* Extract additional information for AM/PM and century */ + +	twelve_hr = hour & 0x40; +	am_pm = hour & 0x20; +	century = month & 0x80; + +	/* Write to rtc_time structure */ + +	time->tm_sec = bcd2bin(second); +	time->tm_min = bcd2bin(minute); +	if (twelve_hr) { +		/* Convert to 24 hr */ +		if (am_pm) +			time->tm_hour = bcd2bin(hour & 0x1F) + 12; +		else +			time->tm_hour = bcd2bin(hour & 0x1F); +	} else { +		time->tm_hour = bcd2bin(hour); +	} + +	time->tm_wday = bcd2bin(week); +	time->tm_mday = bcd2bin(day); +	time->tm_mon = bcd2bin(month & 0x7F); +	if (century) +		add_century = 100; + +	time->tm_year = bcd2bin(year) + add_century; + +	return rtc_valid_tm(time); +} + +static int ds3232_set_time(struct device *dev, struct rtc_time *time) +{ +	struct i2c_client *client = to_i2c_client(dev); +	u8 buf[7]; + +	/* Extract time from rtc_time and load into ds3232*/ + +	buf[0] = bin2bcd(time->tm_sec); +	buf[1] = bin2bcd(time->tm_min); +	buf[2] = bin2bcd(time->tm_hour); +	buf[3] = bin2bcd(time->tm_wday); /* Day of the week */ +	buf[4] = bin2bcd(time->tm_mday); /* Date */ +	buf[5] = bin2bcd(time->tm_mon); +	if (time->tm_year >= 100) { +		buf[5] |= 0x80; +		buf[6] = bin2bcd(time->tm_year - 100); +	} else { +		buf[6] = bin2bcd(time->tm_year); +	} + +	return i2c_smbus_write_i2c_block_data(client, +					      DS3232_REG_SECONDS, 7, buf); +} + +static irqreturn_t ds3232_irq(int irq, void *dev_id) +{ +	struct i2c_client *client = dev_id; +	struct ds3232 *ds3232 = i2c_get_clientdata(client); + +	disable_irq_nosync(irq); +	schedule_work(&ds3232->work); +	return IRQ_HANDLED; +} + +static void ds3232_work(struct work_struct *work) +{ +	struct ds3232 *ds3232 = container_of(work, struct ds3232, work); +	struct i2c_client *client = ds3232->client; +	int stat, control; + +	mutex_lock(&ds3232->mutex); + +	stat = i2c_smbus_read_byte_data(client, DS3232_REG_SR); +	if (stat < 0) +		goto unlock; + +	if (stat & DS3232_REG_SR_A1F) { +		control = i2c_smbus_read_byte_data(client, DS3232_REG_CR); +		if (control < 0) +			goto out; +		/* disable alarm1 interrupt */ +		control &= ~(DS3232_REG_CR_A1IE); +		i2c_smbus_write_byte_data(client, DS3232_REG_CR, control); + +		/* clear the alarm pend flag */ +		stat &= ~DS3232_REG_SR_A1F; +		i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat); + +		rtc_update_irq(ds3232->rtc, 1, RTC_AF | RTC_IRQF); +	} + +out: +	if (!ds3232->exiting) +		enable_irq(client->irq); +unlock: +	mutex_unlock(&ds3232->mutex); +} + +static const struct rtc_class_ops ds3232_rtc_ops = { +	.read_time = ds3232_read_time, +	.set_time = ds3232_set_time, +}; + +static int __devinit ds3232_probe(struct i2c_client *client, +		const struct i2c_device_id *id) +{ +	struct ds3232 *ds3232; +	int ret; + +	ds3232 = kzalloc(sizeof(struct ds3232), GFP_KERNEL); +	if (!ds3232) +		return -ENOMEM; + +	ds3232->client = client; +	i2c_set_clientdata(client, ds3232); + +	INIT_WORK(&ds3232->work, ds3232_work); +	mutex_init(&ds3232->mutex); + +	ret = ds3232_check_rtc_status(client); +	if (ret) +		goto out_free; + +	ds3232->rtc = rtc_device_register(client->name, &client->dev, +					  &ds3232_rtc_ops, THIS_MODULE); +	if (IS_ERR(ds3232->rtc)) { +		ret = PTR_ERR(ds3232->rtc); +		dev_err(&client->dev, "unable to register the class device\n"); +		goto out_irq; +	} + +	if (client->irq >= 0) { +		ret = request_irq(client->irq, ds3232_irq, 0, +				 "ds3232", client); +		if (ret) { +			dev_err(&client->dev, "unable to request IRQ\n"); +			goto out_free; +		} +	} + +	return 0; + +out_irq: +	if (client->irq >= 0) +		free_irq(client->irq, client); + +out_free: +	kfree(ds3232); +	return ret; +} + +static int __devexit ds3232_remove(struct i2c_client *client) +{ +	struct ds3232 *ds3232 = i2c_get_clientdata(client); + +	if (client->irq >= 0) { +		mutex_lock(&ds3232->mutex); +		ds3232->exiting = 1; +		mutex_unlock(&ds3232->mutex); + +		free_irq(client->irq, client); +		flush_scheduled_work(); +	} + +	rtc_device_unregister(ds3232->rtc); +	kfree(ds3232); +	return 0; +} + +static const struct i2c_device_id ds3232_id[] = { +	{ "ds3232", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, ds3232_id); + +static struct i2c_driver ds3232_driver = { +	.driver = { +		.name = "rtc-ds3232", +		.owner = THIS_MODULE, +	}, +	.probe = ds3232_probe, +	.remove = __devexit_p(ds3232_remove), +	.id_table = ds3232_id, +}; + +static int __init ds3232_init(void) +{ +	return i2c_add_driver(&ds3232_driver); +} + +static void __exit ds3232_exit(void) +{ +	i2c_del_driver(&ds3232_driver); +} + +module_init(ds3232_init); +module_exit(ds3232_exit); + +MODULE_AUTHOR("Srikanth Srinivasan <srikanth.srinivasan@freescale.com>"); +MODULE_DESCRIPTION("Maxim/Dallas DS3232 RTC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-fm3130.c b/drivers/rtc/rtc-fm3130.c index ff6fce61ea4..4cf2e70c507 100644 --- a/drivers/rtc/rtc-fm3130.c +++ b/drivers/rtc/rtc-fm3130.c @@ -52,8 +52,8 @@ struct fm3130 {  	struct i2c_msg		msg[4];  	struct i2c_client	*client;  	struct rtc_device	*rtc; +	int			alarm_valid;  	int			data_valid; -	int			alarm;  };  static const struct i2c_device_id fm3130_id[] = {  	{ "fm3130", 0 }, @@ -87,11 +87,7 @@ static void fm3130_rtc_mode(struct device *dev, int mode)  		dev_dbg(dev, "invalid mode %d\n", mode);  		break;  	} -	/* Checking for alarm */ -	if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AF) { -		fm3130->alarm = 1; -		fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF; -	} +  	i2c_smbus_write_byte_data(fm3130->client,  		 FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL]);  } @@ -104,7 +100,7 @@ static int fm3130_get_time(struct device *dev, struct rtc_time *t)  	if (!fm3130->data_valid) {  		/* We have invalid data in RTC, probably due  		to battery faults or other problems. Return EIO -		for now, it will allow us to set data later insted +		for now, it will allow us to set data later instead  		of error during probing which disables device */  		return -EIO;  	} @@ -208,6 +204,17 @@ static int fm3130_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)  	struct fm3130 *fm3130 = dev_get_drvdata(dev);  	int tmp;  	struct rtc_time *tm = &alrm->time; + +	if (!fm3130->alarm_valid) { +		/* +		 * We have invalid alarm in RTC, probably due to battery faults +		 * or other problems. Return EIO for now, it will allow us to +		 * set alarm value later instead of error during probing which +		 * disables device +		 */ +		return -EIO; +	} +  	/* read the RTC alarm registers all at once */  	tmp = i2c_transfer(to_i2c_adapter(fm3130->client->dev.parent),  			&fm3130->msg[2], 2); @@ -222,20 +229,31 @@ static int fm3130_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)  			fm3130->regs[FM3130_ALARM_DATE],  			fm3130->regs[FM3130_ALARM_MONTHS]); -  	tm->tm_sec	= bcd2bin(fm3130->regs[FM3130_ALARM_SECONDS] & 0x7F);  	tm->tm_min	= bcd2bin(fm3130->regs[FM3130_ALARM_MINUTES] & 0x7F);  	tm->tm_hour	= bcd2bin(fm3130->regs[FM3130_ALARM_HOURS] & 0x3F);  	tm->tm_mday	= bcd2bin(fm3130->regs[FM3130_ALARM_DATE] & 0x3F);  	tm->tm_mon	= bcd2bin(fm3130->regs[FM3130_ALARM_MONTHS] & 0x1F); +  	if (tm->tm_mon > 0)  		tm->tm_mon -= 1; /* RTC is 1-12, tm_mon is 0-11 */ +  	dev_dbg(dev, "%s secs=%d, mins=%d, "  		"hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",  		"read alarm", tm->tm_sec, tm->tm_min,  		tm->tm_hour, tm->tm_mday,  		tm->tm_mon, tm->tm_year, tm->tm_wday); +	/* check if alarm enabled */ +	fm3130->regs[FM3130_RTC_CONTROL] = +		i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL); + +	if ((fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AEN) && +		(~fm3130->regs[FM3130_RTC_CONTROL] & +			FM3130_RTC_CONTROL_BIT_CAL)) { +		alrm->enabled = 1; +	} +  	return 0;  } @@ -251,25 +269,20 @@ static int fm3130_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)  		tm->tm_hour, tm->tm_mday,  		tm->tm_mon, tm->tm_year, tm->tm_wday); -	if (tm->tm_sec != -1) -		fm3130->regs[FM3130_ALARM_SECONDS] = -			bin2bcd(tm->tm_sec) | 0x80; +	fm3130->regs[FM3130_ALARM_SECONDS] = +		(tm->tm_sec != -1) ? bin2bcd(tm->tm_sec) : 0x80; -	if (tm->tm_min != -1) -		fm3130->regs[FM3130_ALARM_MINUTES] = -			bin2bcd(tm->tm_min) | 0x80; +	fm3130->regs[FM3130_ALARM_MINUTES] = +		(tm->tm_min != -1) ? bin2bcd(tm->tm_min) : 0x80; -	if (tm->tm_hour != -1) -		fm3130->regs[FM3130_ALARM_HOURS] = -			bin2bcd(tm->tm_hour) | 0x80; +	fm3130->regs[FM3130_ALARM_HOURS] = +		(tm->tm_hour != -1) ? bin2bcd(tm->tm_hour) : 0x80; -	if (tm->tm_mday != -1) -		fm3130->regs[FM3130_ALARM_DATE] = -			bin2bcd(tm->tm_mday) | 0x80; +	fm3130->regs[FM3130_ALARM_DATE] = +		(tm->tm_mday != -1) ? bin2bcd(tm->tm_mday) : 0x80; -	if (tm->tm_mon != -1) -		fm3130->regs[FM3130_ALARM_MONTHS] = -			bin2bcd(tm->tm_mon + 1) | 0x80; +	fm3130->regs[FM3130_ALARM_MONTHS] = +		(tm->tm_mon != -1) ? bin2bcd(tm->tm_mon + 1) : 0x80;  	dev_dbg(dev, "alarm write %02x %02x %02x %02x %02x\n",  			fm3130->regs[FM3130_ALARM_SECONDS], @@ -285,11 +298,8 @@ static int fm3130_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)  	}  	fm3130->regs[FM3130_RTC_CONTROL] =  		i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL); -	/* Checking for alarm */ -	if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AF) { -		fm3130->alarm = 1; -		fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF; -	} + +	/* enable or disable alarm */  	if (alrm->enabled) {  		i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL,  			(fm3130->regs[FM3130_RTC_CONTROL] & @@ -298,16 +308,55 @@ static int fm3130_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)  	} else {  		i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL,  			fm3130->regs[FM3130_RTC_CONTROL] & -				~(FM3130_RTC_CONTROL_BIT_AEN)); +				~(FM3130_RTC_CONTROL_BIT_CAL) & +					~(FM3130_RTC_CONTROL_BIT_AEN));  	} + +	/* We assume here that data is valid once written */ +	if (!fm3130->alarm_valid) +		fm3130->alarm_valid = 1; +  	return 0;  } +static int fm3130_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ +	struct fm3130 *fm3130 = dev_get_drvdata(dev); +	int ret = 0; + +	fm3130->regs[FM3130_RTC_CONTROL] = +		i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL); + +	dev_dbg(dev, "alarm_irq_enable: enable=%d, FM3130_RTC_CONTROL=%02x\n", +		enabled, fm3130->regs[FM3130_RTC_CONTROL]); + +	switch (enabled) { +	case 0:		/* alarm off */ +		ret = i2c_smbus_write_byte_data(fm3130->client, +			FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL] & +				~(FM3130_RTC_CONTROL_BIT_CAL) & +					~(FM3130_RTC_CONTROL_BIT_AEN)); +		break; +	case 1:		/* alarm on */ +		ret = i2c_smbus_write_byte_data(fm3130->client, +			FM3130_RTC_CONTROL, (fm3130->regs[FM3130_RTC_CONTROL] & +				~(FM3130_RTC_CONTROL_BIT_CAL)) | +					FM3130_RTC_CONTROL_BIT_AEN); +		break; +	default: +		ret = -EINVAL; +		break; +	} + +	return ret; +} +  static const struct rtc_class_ops fm3130_rtc_ops = {  	.read_time	= fm3130_get_time,  	.set_time	= fm3130_set_time,  	.read_alarm	= fm3130_read_alarm,  	.set_alarm	= fm3130_set_alarm, +	.alarm_irq_enable = fm3130_alarm_irq_enable,  };  static struct i2c_driver fm3130_driver; @@ -356,6 +405,7 @@ static int __devinit fm3130_probe(struct i2c_client *client,  	fm3130->msg[3].len = FM3130_ALARM_REGS;  	fm3130->msg[3].buf = &fm3130->regs[FM3130_ALARM_SECONDS]; +	fm3130->alarm_valid = 0;  	fm3130->data_valid = 0;  	tmp = i2c_transfer(adapter, fm3130->msg, 4); @@ -370,12 +420,6 @@ static int __devinit fm3130_probe(struct i2c_client *client,  	fm3130->regs[FM3130_CAL_CONTROL] =  		i2c_smbus_read_byte_data(client, FM3130_CAL_CONTROL); -	/* Checking for alarm */ -	if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AF) { -		fm3130->alarm = 1; -		fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF; -	} -  	/* Disabling calibration mode */  	if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_CAL) {  		i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL, @@ -400,44 +444,79 @@ static int __devinit fm3130_probe(struct i2c_client *client,  			fm3130->regs[FM3130_CAL_CONTROL] &  				~(FM3130_CAL_CONTROL_BIT_nOSCEN)); -	/* oscillator fault?  clear flag, and warn */ -	if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_LB) +	/* low battery?  clear flag, and warn */ +	if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_LB) { +		i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL, +			fm3130->regs[FM3130_RTC_CONTROL] & +				~(FM3130_RTC_CONTROL_BIT_LB));  		dev_warn(&client->dev, "Low battery!\n"); +	} -	/* oscillator fault?  clear flag, and warn */ +	/* check if Power On Reset bit is set */  	if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_POR) {  		i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL,  			fm3130->regs[FM3130_RTC_CONTROL] &  				~FM3130_RTC_CONTROL_BIT_POR); -		dev_warn(&client->dev, "SET TIME!\n"); +		dev_dbg(&client->dev, "POR bit is set\n");  	}  	/* ACS is controlled by alarm */  	i2c_smbus_write_byte_data(client, FM3130_ALARM_WP_CONTROL, 0x80); -	/* TODO */ -	/* TODO need to sanity check alarm */ -	tmp = fm3130->regs[FM3130_RTC_SECONDS]; -	tmp = bcd2bin(tmp & 0x7f); -	if (tmp > 60) -		goto exit_bad; +	/* alarm registers sanity check */ +	tmp = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f); +	if (tmp > 59) +		goto bad_alarm; +  	tmp = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f); -	if (tmp > 60) -		goto exit_bad; +	if (tmp > 59) +		goto bad_alarm; + +	tmp = bcd2bin(fm3130->regs[FM3130_RTC_HOURS] & 0x3f); +	if (tmp > 23) +		goto bad_alarm;  	tmp = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f);  	if (tmp == 0 || tmp > 31) -		goto exit_bad; +		goto bad_alarm;  	tmp = bcd2bin(fm3130->regs[FM3130_RTC_MONTHS] & 0x1f);  	if (tmp == 0 || tmp > 12) -		goto exit_bad; +		goto bad_alarm; -	tmp = fm3130->regs[FM3130_RTC_HOURS]; +	fm3130->alarm_valid = 1; + +bad_alarm: + +	/* clock registers sanity chek */ +	tmp = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f); +	if (tmp > 59) +		goto bad_clock; + +	tmp = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f); +	if (tmp > 59) +		goto bad_clock; + +	tmp = bcd2bin(fm3130->regs[FM3130_RTC_HOURS] & 0x3f); +	if (tmp > 23) +		goto bad_clock; + +	tmp = bcd2bin(fm3130->regs[FM3130_RTC_DAY] & 0x7); +	if (tmp == 0 || tmp > 7) +		goto bad_clock; + +	tmp = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f); +	if (tmp == 0 || tmp > 31) +		goto bad_clock; + +	tmp = bcd2bin(fm3130->regs[FM3130_RTC_MONTHS] & 0x1f); +	if (tmp == 0 || tmp > 12) +		goto bad_clock;  	fm3130->data_valid = 1; -exit_bad: -	if (!fm3130->data_valid) +bad_clock: + +	if (!fm3130->data_valid || !fm3130->alarm_valid)  		dev_dbg(&client->dev,  				"%s: %02x %02x %02x %02x %02x %02x %02x %02x"  				"%02x %02x %02x %02x %02x %02x %02x\n", diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c new file mode 100644 index 00000000000..2dd3c016327 --- /dev/null +++ b/drivers/rtc/rtc-imxdi.c @@ -0,0 +1,519 @@ +/* + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2010 Orex Computed Radiography + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/* based on rtc-mc13892.c */ + +/* + * This driver uses the 47-bit 32 kHz counter in the Freescale DryIce block + * to implement a Linux RTC. Times and alarms are truncated to seconds. + * Since the RTC framework performs API locking via rtc->ops_lock the + * only simultaneous accesses we need to deal with is updating DryIce + * registers while servicing an alarm. + * + * Note that reading the DSR (DryIce Status Register) automatically clears + * the WCF (Write Complete Flag). All DryIce writes are synchronized to the + * LP (Low Power) domain and set the WCF upon completion. Writes to the + * DIER (DryIce Interrupt Enable Register) are the only exception. These + * occur at normal bus speeds and do not set WCF.  Periodic interrupts are + * not supported by the hardware. + */ + +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> +#include <linux/workqueue.h> + +/* DryIce Register Definitions */ + +#define DTCMR     0x00           /* Time Counter MSB Reg */ +#define DTCLR     0x04           /* Time Counter LSB Reg */ + +#define DCAMR     0x08           /* Clock Alarm MSB Reg */ +#define DCALR     0x0c           /* Clock Alarm LSB Reg */ +#define DCAMR_UNSET  0xFFFFFFFF  /* doomsday - 1 sec */ + +#define DCR       0x10           /* Control Reg */ +#define DCR_TCE   (1 << 3)       /* Time Counter Enable */ + +#define DSR       0x14           /* Status Reg */ +#define DSR_WBF   (1 << 10)      /* Write Busy Flag */ +#define DSR_WNF   (1 << 9)       /* Write Next Flag */ +#define DSR_WCF   (1 << 8)       /* Write Complete Flag */ +#define DSR_WEF   (1 << 7)       /* Write Error Flag */ +#define DSR_CAF   (1 << 4)       /* Clock Alarm Flag */ +#define DSR_NVF   (1 << 1)       /* Non-Valid Flag */ +#define DSR_SVF   (1 << 0)       /* Security Violation Flag */ + +#define DIER      0x18           /* Interrupt Enable Reg */ +#define DIER_WNIE (1 << 9)       /* Write Next Interrupt Enable */ +#define DIER_WCIE (1 << 8)       /* Write Complete Interrupt Enable */ +#define DIER_WEIE (1 << 7)       /* Write Error Interrupt Enable */ +#define DIER_CAIE (1 << 4)       /* Clock Alarm Interrupt Enable */ + +/** + * struct imxdi_dev - private imxdi rtc data + * @pdev: pionter to platform dev + * @rtc: pointer to rtc struct + * @ioaddr: IO registers pointer + * @irq: dryice normal interrupt + * @clk: input reference clock + * @dsr: copy of the DSR register + * @irq_lock: interrupt enable register (DIER) lock + * @write_wait: registers write complete queue + * @write_mutex: serialize registers write + * @work: schedule alarm work + */ +struct imxdi_dev { +	struct platform_device *pdev; +	struct rtc_device *rtc; +	void __iomem *ioaddr; +	int irq; +	struct clk *clk; +	u32 dsr; +	spinlock_t irq_lock; +	wait_queue_head_t write_wait; +	struct mutex write_mutex; +	struct work_struct work; +}; + +/* + * enable a dryice interrupt + */ +static void di_int_enable(struct imxdi_dev *imxdi, u32 intr) +{ +	unsigned long flags; + +	spin_lock_irqsave(&imxdi->irq_lock, flags); +	__raw_writel(__raw_readl(imxdi->ioaddr + DIER) | intr, +			imxdi->ioaddr + DIER); +	spin_unlock_irqrestore(&imxdi->irq_lock, flags); +} + +/* + * disable a dryice interrupt + */ +static void di_int_disable(struct imxdi_dev *imxdi, u32 intr) +{ +	unsigned long flags; + +	spin_lock_irqsave(&imxdi->irq_lock, flags); +	__raw_writel(__raw_readl(imxdi->ioaddr + DIER) & ~intr, +			imxdi->ioaddr + DIER); +	spin_unlock_irqrestore(&imxdi->irq_lock, flags); +} + +/* + * This function attempts to clear the dryice write-error flag. + * + * A dryice write error is similar to a bus fault and should not occur in + * normal operation.  Clearing the flag requires another write, so the root + * cause of the problem may need to be fixed before the flag can be cleared. + */ +static void clear_write_error(struct imxdi_dev *imxdi) +{ +	int cnt; + +	dev_warn(&imxdi->pdev->dev, "WARNING: Register write error!\n"); + +	/* clear the write error flag */ +	__raw_writel(DSR_WEF, imxdi->ioaddr + DSR); + +	/* wait for it to take effect */ +	for (cnt = 0; cnt < 1000; cnt++) { +		if ((__raw_readl(imxdi->ioaddr + DSR) & DSR_WEF) == 0) +			return; +		udelay(10); +	} +	dev_err(&imxdi->pdev->dev, +			"ERROR: Cannot clear write-error flag!\n"); +} + +/* + * Write a dryice register and wait until it completes. + * + * This function uses interrupts to determine when the + * write has completed. + */ +static int di_write_wait(struct imxdi_dev *imxdi, u32 val, int reg) +{ +	int ret; +	int rc = 0; + +	/* serialize register writes */ +	mutex_lock(&imxdi->write_mutex); + +	/* enable the write-complete interrupt */ +	di_int_enable(imxdi, DIER_WCIE); + +	imxdi->dsr = 0; + +	/* do the register write */ +	__raw_writel(val, imxdi->ioaddr + reg); + +	/* wait for the write to finish */ +	ret = wait_event_interruptible_timeout(imxdi->write_wait, +			imxdi->dsr & (DSR_WCF | DSR_WEF), msecs_to_jiffies(1)); +	if (ret < 0) { +		rc = ret; +		goto out; +	} else if (ret == 0) { +		dev_warn(&imxdi->pdev->dev, +				"Write-wait timeout " +				"val = 0x%08x reg = 0x%08x\n", val, reg); +	} + +	/* check for write error */ +	if (imxdi->dsr & DSR_WEF) { +		clear_write_error(imxdi); +		rc = -EIO; +	} + +out: +	mutex_unlock(&imxdi->write_mutex); + +	return rc; +} + +/* + * read the seconds portion of the current time from the dryice time counter + */ +static int dryice_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ +	struct imxdi_dev *imxdi = dev_get_drvdata(dev); +	unsigned long now; + +	now = __raw_readl(imxdi->ioaddr + DTCMR); +	rtc_time_to_tm(now, tm); + +	return 0; +} + +/* + * set the seconds portion of dryice time counter and clear the + * fractional part. + */ +static int dryice_rtc_set_mmss(struct device *dev, unsigned long secs) +{ +	struct imxdi_dev *imxdi = dev_get_drvdata(dev); +	int rc; + +	/* zero the fractional part first */ +	rc = di_write_wait(imxdi, 0, DTCLR); +	if (rc == 0) +		rc = di_write_wait(imxdi, secs, DTCMR); + +	return rc; +} + +static int dryice_rtc_alarm_irq_enable(struct device *dev, +		unsigned int enabled) +{ +	struct imxdi_dev *imxdi = dev_get_drvdata(dev); + +	if (enabled) +		di_int_enable(imxdi, DIER_CAIE); +	else +		di_int_disable(imxdi, DIER_CAIE); + +	return 0; +} + +/* + * read the seconds portion of the alarm register. + * the fractional part of the alarm register is always zero. + */ +static int dryice_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ +	struct imxdi_dev *imxdi = dev_get_drvdata(dev); +	u32 dcamr; + +	dcamr = __raw_readl(imxdi->ioaddr + DCAMR); +	rtc_time_to_tm(dcamr, &alarm->time); + +	/* alarm is enabled if the interrupt is enabled */ +	alarm->enabled = (__raw_readl(imxdi->ioaddr + DIER) & DIER_CAIE) != 0; + +	/* don't allow the DSR read to mess up DSR_WCF */ +	mutex_lock(&imxdi->write_mutex); + +	/* alarm is pending if the alarm flag is set */ +	alarm->pending = (__raw_readl(imxdi->ioaddr + DSR) & DSR_CAF) != 0; + +	mutex_unlock(&imxdi->write_mutex); + +	return 0; +} + +/* + * set the seconds portion of dryice alarm register + */ +static int dryice_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ +	struct imxdi_dev *imxdi = dev_get_drvdata(dev); +	unsigned long now; +	unsigned long alarm_time; +	int rc; + +	rc = rtc_tm_to_time(&alarm->time, &alarm_time); +	if (rc) +		return rc; + +	/* don't allow setting alarm in the past */ +	now = __raw_readl(imxdi->ioaddr + DTCMR); +	if (alarm_time < now) +		return -EINVAL; + +	/* write the new alarm time */ +	rc = di_write_wait(imxdi, (u32)alarm_time, DCAMR); +	if (rc) +		return rc; + +	if (alarm->enabled) +		di_int_enable(imxdi, DIER_CAIE);  /* enable alarm intr */ +	else +		di_int_disable(imxdi, DIER_CAIE); /* disable alarm intr */ + +	return 0; +} + +static struct rtc_class_ops dryice_rtc_ops = { +	.read_time		= dryice_rtc_read_time, +	.set_mmss		= dryice_rtc_set_mmss, +	.alarm_irq_enable	= dryice_rtc_alarm_irq_enable, +	.read_alarm		= dryice_rtc_read_alarm, +	.set_alarm		= dryice_rtc_set_alarm, +}; + +/* + * dryice "normal" interrupt handler + */ +static irqreturn_t dryice_norm_irq(int irq, void *dev_id) +{ +	struct imxdi_dev *imxdi = dev_id; +	u32 dsr, dier; +	irqreturn_t rc = IRQ_NONE; + +	dier = __raw_readl(imxdi->ioaddr + DIER); + +	/* handle write complete and write error cases */ +	if ((dier & DIER_WCIE)) { +		/*If the write wait queue is empty then there is no pending +		  operations. It means the interrupt is for DryIce -Security. +		  IRQ must be returned as none.*/ +		if (list_empty_careful(&imxdi->write_wait.task_list)) +			return rc; + +		/* DSR_WCF clears itself on DSR read */ +		dsr = __raw_readl(imxdi->ioaddr + DSR); +		if ((dsr & (DSR_WCF | DSR_WEF))) { +			/* mask the interrupt */ +			di_int_disable(imxdi, DIER_WCIE); + +			/* save the dsr value for the wait queue */ +			imxdi->dsr |= dsr; + +			wake_up_interruptible(&imxdi->write_wait); +			rc = IRQ_HANDLED; +		} +	} + +	/* handle the alarm case */ +	if ((dier & DIER_CAIE)) { +		/* DSR_WCF clears itself on DSR read */ +		dsr = __raw_readl(imxdi->ioaddr + DSR); +		if (dsr & DSR_CAF) { +			/* mask the interrupt */ +			di_int_disable(imxdi, DIER_CAIE); + +			/* finish alarm in user context */ +			schedule_work(&imxdi->work); +			rc = IRQ_HANDLED; +		} +	} +	return rc; +} + +/* + * post the alarm event from user context so it can sleep + * on the write completion. + */ +static void dryice_work(struct work_struct *work) +{ +	struct imxdi_dev *imxdi = container_of(work, +			struct imxdi_dev, work); + +	/* dismiss the interrupt (ignore error) */ +	di_write_wait(imxdi, DSR_CAF, DSR); + +	/* pass the alarm event to the rtc framework. */ +	rtc_update_irq(imxdi->rtc, 1, RTC_AF | RTC_IRQF); +} + +/* + * probe for dryice rtc device + */ +static int dryice_rtc_probe(struct platform_device *pdev) +{ +	struct resource *res; +	struct imxdi_dev *imxdi; +	int rc; + +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (!res) +		return -ENODEV; + +	imxdi = devm_kzalloc(&pdev->dev, sizeof(*imxdi), GFP_KERNEL); +	if (!imxdi) +		return -ENOMEM; + +	imxdi->pdev = pdev; + +	if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), +				pdev->name)) +		return -EBUSY; + +	imxdi->ioaddr = devm_ioremap(&pdev->dev, res->start, +			resource_size(res)); +	if (imxdi->ioaddr == NULL) +		return -ENOMEM; + +	imxdi->irq = platform_get_irq(pdev, 0); +	if (imxdi->irq < 0) +		return imxdi->irq; + +	init_waitqueue_head(&imxdi->write_wait); + +	INIT_WORK(&imxdi->work, dryice_work); + +	mutex_init(&imxdi->write_mutex); + +	imxdi->clk = clk_get(&pdev->dev, NULL); +	if (IS_ERR(imxdi->clk)) +		return PTR_ERR(imxdi->clk); +	clk_enable(imxdi->clk); + +	/* +	 * Initialize dryice hardware +	 */ + +	/* mask all interrupts */ +	__raw_writel(0, imxdi->ioaddr + DIER); + +	rc = devm_request_irq(&pdev->dev, imxdi->irq, dryice_norm_irq, +			IRQF_SHARED, pdev->name, imxdi); +	if (rc) { +		dev_warn(&pdev->dev, "interrupt not available.\n"); +		goto err; +	} + +	/* put dryice into valid state */ +	if (__raw_readl(imxdi->ioaddr + DSR) & DSR_NVF) { +		rc = di_write_wait(imxdi, DSR_NVF | DSR_SVF, DSR); +		if (rc) +			goto err; +	} + +	/* initialize alarm */ +	rc = di_write_wait(imxdi, DCAMR_UNSET, DCAMR); +	if (rc) +		goto err; +	rc = di_write_wait(imxdi, 0, DCALR); +	if (rc) +		goto err; + +	/* clear alarm flag */ +	if (__raw_readl(imxdi->ioaddr + DSR) & DSR_CAF) { +		rc = di_write_wait(imxdi, DSR_CAF, DSR); +		if (rc) +			goto err; +	} + +	/* the timer won't count if it has never been written to */ +	if (__raw_readl(imxdi->ioaddr + DTCMR) == 0) { +		rc = di_write_wait(imxdi, 0, DTCMR); +		if (rc) +			goto err; +	} + +	/* start keeping time */ +	if (!(__raw_readl(imxdi->ioaddr + DCR) & DCR_TCE)) { +		rc = di_write_wait(imxdi, +				__raw_readl(imxdi->ioaddr + DCR) | DCR_TCE, +				DCR); +		if (rc) +			goto err; +	} + +	platform_set_drvdata(pdev, imxdi); +	imxdi->rtc = rtc_device_register(pdev->name, &pdev->dev, +				  &dryice_rtc_ops, THIS_MODULE); +	if (IS_ERR(imxdi->rtc)) { +		rc = PTR_ERR(imxdi->rtc); +		goto err; +	} + +	return 0; + +err: +	clk_disable(imxdi->clk); +	clk_put(imxdi->clk); + +	return rc; +} + +static int __devexit dryice_rtc_remove(struct platform_device *pdev) +{ +	struct imxdi_dev *imxdi = platform_get_drvdata(pdev); + +	flush_work(&imxdi->work); + +	/* mask all interrupts */ +	__raw_writel(0, imxdi->ioaddr + DIER); + +	rtc_device_unregister(imxdi->rtc); + +	clk_disable(imxdi->clk); +	clk_put(imxdi->clk); + +	return 0; +} + +static struct platform_driver dryice_rtc_driver = { +	.driver = { +		   .name = "imxdi_rtc", +		   .owner = THIS_MODULE, +		   }, +	.remove = __devexit_p(dryice_rtc_remove), +}; + +static int __init dryice_rtc_init(void) +{ +	return platform_driver_probe(&dryice_rtc_driver, dryice_rtc_probe); +} + +static void __exit dryice_rtc_exit(void) +{ +	platform_driver_unregister(&dryice_rtc_driver); +} + +module_init(dryice_rtc_init); +module_exit(dryice_rtc_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); +MODULE_DESCRIPTION("IMX DryIce Realtime Clock Driver (RTC)"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-isl12022.c b/drivers/rtc/rtc-isl12022.c new file mode 100644 index 00000000000..ddbc797ea6c --- /dev/null +++ b/drivers/rtc/rtc-isl12022.c @@ -0,0 +1,327 @@ +/* + * An I2C driver for the Intersil ISL 12022 + * + * Author: Roman Fietze <roman.fietze@telemotive.de> + * + * Based on the Philips PCF8563 RTC + * by Alessandro Zummo <a.zummo@towertech.it>. + * + * 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. + */ + +#include <linux/i2c.h> +#include <linux/bcd.h> +#include <linux/rtc.h> +#include <linux/slab.h> + +#define DRV_VERSION "0.1" + +/* ISL register offsets */ +#define ISL12022_REG_SC		0x00 +#define ISL12022_REG_MN		0x01 +#define ISL12022_REG_HR		0x02 +#define ISL12022_REG_DT		0x03 +#define ISL12022_REG_MO		0x04 +#define ISL12022_REG_YR		0x05 +#define ISL12022_REG_DW		0x06 + +#define ISL12022_REG_SR		0x07 +#define ISL12022_REG_INT	0x08 + +/* ISL register bits */ +#define ISL12022_HR_MIL		(1 << 7)	/* military or 24 hour time */ + +#define ISL12022_SR_LBAT85	(1 << 2) +#define ISL12022_SR_LBAT75	(1 << 1) + +#define ISL12022_INT_WRTC	(1 << 6) + + +static struct i2c_driver isl12022_driver; + +struct isl12022 { +	struct rtc_device *rtc; + +	bool write_enabled;	/* true if write enable is set */ +}; + + +static int isl12022_read_regs(struct i2c_client *client, uint8_t reg, +			      uint8_t *data, size_t n) +{ +	struct i2c_msg msgs[] = { +		{ +			.addr	= client->addr, +			.flags	= 0, +			.len	= 1, +			.buf	= data +		},		/* setup read ptr */ +		{ +			.addr	= client->addr, +			.flags	= I2C_M_RD, +			.len	= n, +			.buf	= data +		} +	}; + +	int ret; + +	data[0] = reg; +	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); +	if (ret != ARRAY_SIZE(msgs)) { +		dev_err(&client->dev, "%s: read error, ret=%d\n", +			__func__, ret); +		return -EIO; +	} + +	return 0; +} + + +static int isl12022_write_reg(struct i2c_client *client, +			      uint8_t reg, uint8_t val) +{ +	uint8_t data[2] = { reg, val }; +	int err; + +	err = i2c_master_send(client, data, sizeof(data)); +	if (err != sizeof(data)) { +		dev_err(&client->dev, +			"%s: err=%d addr=%02x, data=%02x\n", +			__func__, err, data[0], data[1]); +		return -EIO; +	} + +	return 0; +} + + +/* + * In the routines that deal directly with the isl12022 hardware, we use + * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch. + */ +static int isl12022_get_datetime(struct i2c_client *client, struct rtc_time *tm) +{ +	uint8_t buf[ISL12022_REG_INT + 1]; +	int ret; + +	ret = isl12022_read_regs(client, ISL12022_REG_SC, buf, sizeof(buf)); +	if (ret) +		return ret; + +	if (buf[ISL12022_REG_SR] & (ISL12022_SR_LBAT85 | ISL12022_SR_LBAT75)) { +		dev_warn(&client->dev, +			 "voltage dropped below %u%%, " +			 "date and time is not reliable.\n", +			 buf[ISL12022_REG_SR] & ISL12022_SR_LBAT85 ? 85 : 75); +	} + +	dev_dbg(&client->dev, +		"%s: raw data is sec=%02x, min=%02x, hr=%02x, " +		"mday=%02x, mon=%02x, year=%02x, wday=%02x, " +		"sr=%02x, int=%02x", +		__func__, +		buf[ISL12022_REG_SC], +		buf[ISL12022_REG_MN], +		buf[ISL12022_REG_HR], +		buf[ISL12022_REG_DT], +		buf[ISL12022_REG_MO], +		buf[ISL12022_REG_YR], +		buf[ISL12022_REG_DW], +		buf[ISL12022_REG_SR], +		buf[ISL12022_REG_INT]); + +	tm->tm_sec = bcd2bin(buf[ISL12022_REG_SC] & 0x7F); +	tm->tm_min = bcd2bin(buf[ISL12022_REG_MN] & 0x7F); +	tm->tm_hour = bcd2bin(buf[ISL12022_REG_HR] & 0x3F); +	tm->tm_mday = bcd2bin(buf[ISL12022_REG_DT] & 0x3F); +	tm->tm_wday = buf[ISL12022_REG_DW] & 0x07; +	tm->tm_mon = bcd2bin(buf[ISL12022_REG_MO] & 0x1F) - 1; +	tm->tm_year = bcd2bin(buf[ISL12022_REG_YR]) + 100; + +	dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, " +		"mday=%d, mon=%d, year=%d, wday=%d\n", +		__func__, +		tm->tm_sec, tm->tm_min, tm->tm_hour, +		tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + +	/* The clock can give out invalid datetime, but we cannot return +	 * -EINVAL otherwise hwclock will refuse to set the time on bootup. */ +	if (rtc_valid_tm(tm) < 0) +		dev_err(&client->dev, "retrieved date and time is invalid.\n"); + +	return 0; +} + +static int isl12022_set_datetime(struct i2c_client *client, struct rtc_time *tm) +{ +	struct isl12022 *isl12022 = i2c_get_clientdata(client); +	size_t i; +	int ret; +	uint8_t buf[ISL12022_REG_DW + 1]; + +	dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, " +		"mday=%d, mon=%d, year=%d, wday=%d\n", +		__func__, +		tm->tm_sec, tm->tm_min, tm->tm_hour, +		tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + +	if (!isl12022->write_enabled) { + +		ret = isl12022_read_regs(client, ISL12022_REG_INT, buf, 1); +		if (ret) +			return ret; + +		/* Check if WRTC (write rtc enable) is set factory default is +		 * 0 (not set) */ +		if (!(buf[0] & ISL12022_INT_WRTC)) { +			dev_info(&client->dev, +				 "init write enable and 24 hour format\n"); + +			/* Set the write enable bit. */ +			ret = isl12022_write_reg(client, +						 ISL12022_REG_INT, +						 buf[0] | ISL12022_INT_WRTC); +			if (ret) +				return ret; + +			/* Write to any RTC register to start RTC, we use the +			 * HR register, setting the MIL bit to use the 24 hour +			 * format. */ +			ret = isl12022_read_regs(client, ISL12022_REG_HR, +						 buf, 1); +			if (ret) +				return ret; + +			ret = isl12022_write_reg(client, +						 ISL12022_REG_HR, +						 buf[0] | ISL12022_HR_MIL); +			if (ret) +				return ret; +		} + +		isl12022->write_enabled = 1; +	} + +	/* hours, minutes and seconds */ +	buf[ISL12022_REG_SC] = bin2bcd(tm->tm_sec); +	buf[ISL12022_REG_MN] = bin2bcd(tm->tm_min); +	buf[ISL12022_REG_HR] = bin2bcd(tm->tm_hour) | ISL12022_HR_MIL; + +	buf[ISL12022_REG_DT] = bin2bcd(tm->tm_mday); + +	/* month, 1 - 12 */ +	buf[ISL12022_REG_MO] = bin2bcd(tm->tm_mon + 1); + +	/* year and century */ +	buf[ISL12022_REG_YR] = bin2bcd(tm->tm_year % 100); + +	buf[ISL12022_REG_DW] = tm->tm_wday & 0x07; + +	/* write register's data */ +	for (i = 0; i < ARRAY_SIZE(buf); i++) { +		ret = isl12022_write_reg(client, ISL12022_REG_SC + i, +					 buf[ISL12022_REG_SC + i]); +		if (ret) +			return -EIO; +	}; + +	return 0; +} + +static int isl12022_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ +	return isl12022_get_datetime(to_i2c_client(dev), tm); +} + +static int isl12022_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ +	return isl12022_set_datetime(to_i2c_client(dev), tm); +} + +static const struct rtc_class_ops isl12022_rtc_ops = { +	.read_time	= isl12022_rtc_read_time, +	.set_time	= isl12022_rtc_set_time, +}; + +static int isl12022_probe(struct i2c_client *client, +			  const struct i2c_device_id *id) +{ +	struct isl12022 *isl12022; + +	int ret = 0; + +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) +		return -ENODEV; + +	isl12022 = kzalloc(sizeof(struct isl12022), GFP_KERNEL); +	if (!isl12022) +		return -ENOMEM; + +	dev_dbg(&client->dev, "chip found, driver version " DRV_VERSION "\n"); + +	i2c_set_clientdata(client, isl12022); + +	isl12022->rtc = rtc_device_register(isl12022_driver.driver.name, +					    &client->dev, +					    &isl12022_rtc_ops, +					    THIS_MODULE); + +	if (IS_ERR(isl12022->rtc)) { +		ret = PTR_ERR(isl12022->rtc); +		goto exit_kfree; +	} + +	return 0; + +exit_kfree: +	kfree(isl12022); + +	return ret; +} + +static int isl12022_remove(struct i2c_client *client) +{ +	struct isl12022 *isl12022 = i2c_get_clientdata(client); + +	rtc_device_unregister(isl12022->rtc); +	kfree(isl12022); + +	return 0; +} + +static const struct i2c_device_id isl12022_id[] = { +	{ "isl12022", 0 }, +	{ "rtc8564", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, isl12022_id); + +static struct i2c_driver isl12022_driver = { +	.driver		= { +		.name	= "rtc-isl12022", +	}, +	.probe		= isl12022_probe, +	.remove		= isl12022_remove, +	.id_table	= isl12022_id, +}; + +static int __init isl12022_init(void) +{ +	return i2c_add_driver(&isl12022_driver); +} + +static void __exit isl12022_exit(void) +{ +	i2c_del_driver(&isl12022_driver); +} + +module_init(isl12022_init); +module_exit(isl12022_exit); + +MODULE_AUTHOR("roman.fietze@telemotive.de"); +MODULE_DESCRIPTION("ISL 12022 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c new file mode 100644 index 00000000000..2619d57b91d --- /dev/null +++ b/drivers/rtc/rtc-jz4740.c @@ -0,0 +1,345 @@ +/* + *  Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> + *	 JZ4740 SoC RTC driver + * + *  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. + * + *  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., + *  675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#define JZ_REG_RTC_CTRL		0x00 +#define JZ_REG_RTC_SEC		0x04 +#define JZ_REG_RTC_SEC_ALARM	0x08 +#define JZ_REG_RTC_REGULATOR	0x0C +#define JZ_REG_RTC_HIBERNATE	0x20 +#define JZ_REG_RTC_SCRATCHPAD	0x34 + +#define JZ_RTC_CTRL_WRDY	BIT(7) +#define JZ_RTC_CTRL_1HZ		BIT(6) +#define JZ_RTC_CTRL_1HZ_IRQ	BIT(5) +#define JZ_RTC_CTRL_AF		BIT(4) +#define JZ_RTC_CTRL_AF_IRQ	BIT(3) +#define JZ_RTC_CTRL_AE		BIT(2) +#define JZ_RTC_CTRL_ENABLE	BIT(0) + +struct jz4740_rtc { +	struct resource *mem; +	void __iomem *base; + +	struct rtc_device *rtc; + +	unsigned int irq; + +	spinlock_t lock; +}; + +static inline uint32_t jz4740_rtc_reg_read(struct jz4740_rtc *rtc, size_t reg) +{ +	return readl(rtc->base + reg); +} + +static int jz4740_rtc_wait_write_ready(struct jz4740_rtc *rtc) +{ +	uint32_t ctrl; +	int timeout = 1000; + +	do { +		ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); +	} while (!(ctrl & JZ_RTC_CTRL_WRDY) && --timeout); + +	return timeout ? 0 : -EIO; +} + +static inline int jz4740_rtc_reg_write(struct jz4740_rtc *rtc, size_t reg, +	uint32_t val) +{ +	int ret; +	ret = jz4740_rtc_wait_write_ready(rtc); +	if (ret == 0) +		writel(val, rtc->base + reg); + +	return ret; +} + +static int jz4740_rtc_ctrl_set_bits(struct jz4740_rtc *rtc, uint32_t mask, +	bool set) +{ +	int ret; +	unsigned long flags; +	uint32_t ctrl; + +	spin_lock_irqsave(&rtc->lock, flags); + +	ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); + +	/* Don't clear interrupt flags by accident */ +	ctrl |= JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF; + +	if (set) +		ctrl |= mask; +	else +		ctrl &= ~mask; + +	ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_CTRL, ctrl); + +	spin_unlock_irqrestore(&rtc->lock, flags); + +	return ret; +} + +static int jz4740_rtc_read_time(struct device *dev, struct rtc_time *time) +{ +	struct jz4740_rtc *rtc = dev_get_drvdata(dev); +	uint32_t secs, secs2; +	int timeout = 5; + +	/* If the seconds register is read while it is updated, it can contain a +	 * bogus value. This can be avoided by making sure that two consecutive +	 * reads have the same value. +	 */ +	secs = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC); +	secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC); + +	while (secs != secs2 && --timeout) { +		secs = secs2; +		secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC); +	} + +	if (timeout == 0) +		return -EIO; + +	rtc_time_to_tm(secs, time); + +	return rtc_valid_tm(time); +} + +static int jz4740_rtc_set_mmss(struct device *dev, unsigned long secs) +{ +	struct jz4740_rtc *rtc = dev_get_drvdata(dev); + +	return jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, secs); +} + +static int jz4740_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ +	struct jz4740_rtc *rtc = dev_get_drvdata(dev); +	uint32_t secs; +	uint32_t ctrl; + +	secs = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC_ALARM); + +	ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); + +	alrm->enabled = !!(ctrl & JZ_RTC_CTRL_AE); +	alrm->pending = !!(ctrl & JZ_RTC_CTRL_AF); + +	rtc_time_to_tm(secs, &alrm->time); + +	return rtc_valid_tm(&alrm->time); +} + +static int jz4740_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ +	int ret; +	struct jz4740_rtc *rtc = dev_get_drvdata(dev); +	unsigned long secs; + +	rtc_tm_to_time(&alrm->time, &secs); + +	ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC_ALARM, secs); +	if (!ret) +		ret = jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_AE, alrm->enabled); + +	return ret; +} + +static int jz4740_rtc_update_irq_enable(struct device *dev, unsigned int enable) +{ +	struct jz4740_rtc *rtc = dev_get_drvdata(dev); +	return jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_1HZ_IRQ, enable); +} + +static int jz4740_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) +{ +	struct jz4740_rtc *rtc = dev_get_drvdata(dev); +	return jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_AF_IRQ, enable); +} + +static struct rtc_class_ops jz4740_rtc_ops = { +	.read_time	= jz4740_rtc_read_time, +	.set_mmss	= jz4740_rtc_set_mmss, +	.read_alarm	= jz4740_rtc_read_alarm, +	.set_alarm	= jz4740_rtc_set_alarm, +	.update_irq_enable = jz4740_rtc_update_irq_enable, +	.alarm_irq_enable = jz4740_rtc_alarm_irq_enable, +}; + +static irqreturn_t jz4740_rtc_irq(int irq, void *data) +{ +	struct jz4740_rtc *rtc = data; +	uint32_t ctrl; +	unsigned long events = 0; + +	ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); + +	if (ctrl & JZ_RTC_CTRL_1HZ) +		events |= (RTC_UF | RTC_IRQF); + +	if (ctrl & JZ_RTC_CTRL_AF) +		events |= (RTC_AF | RTC_IRQF); + +	rtc_update_irq(rtc->rtc, 1, events); + +	jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF, false); + +	return IRQ_HANDLED; +} + +void jz4740_rtc_poweroff(struct device *dev) +{ +	struct jz4740_rtc *rtc = dev_get_drvdata(dev); +	jz4740_rtc_reg_write(rtc, JZ_REG_RTC_HIBERNATE, 1); +} +EXPORT_SYMBOL_GPL(jz4740_rtc_poweroff); + +static int __devinit jz4740_rtc_probe(struct platform_device *pdev) +{ +	int ret; +	struct jz4740_rtc *rtc; +	uint32_t scratchpad; + +	rtc = kzalloc(sizeof(*rtc), GFP_KERNEL); +	if (!rtc) +		return -ENOMEM; + +	rtc->irq = platform_get_irq(pdev, 0); +	if (rtc->irq < 0) { +		ret = -ENOENT; +		dev_err(&pdev->dev, "Failed to get platform irq\n"); +		goto err_free; +	} + +	rtc->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (!rtc->mem) { +		ret = -ENOENT; +		dev_err(&pdev->dev, "Failed to get platform mmio memory\n"); +		goto err_free; +	} + +	rtc->mem = request_mem_region(rtc->mem->start, resource_size(rtc->mem), +					pdev->name); +	if (!rtc->mem) { +		ret = -EBUSY; +		dev_err(&pdev->dev, "Failed to request mmio memory region\n"); +		goto err_free; +	} + +	rtc->base = ioremap_nocache(rtc->mem->start, resource_size(rtc->mem)); +	if (!rtc->base) { +		ret = -EBUSY; +		dev_err(&pdev->dev, "Failed to ioremap mmio memory\n"); +		goto err_release_mem_region; +	} + +	spin_lock_init(&rtc->lock); + +	platform_set_drvdata(pdev, rtc); + +	rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, &jz4740_rtc_ops, +					THIS_MODULE); +	if (IS_ERR(rtc->rtc)) { +		ret = PTR_ERR(rtc->rtc); +		dev_err(&pdev->dev, "Failed to register rtc device: %d\n", ret); +		goto err_iounmap; +	} + +	ret = request_irq(rtc->irq, jz4740_rtc_irq, 0, +				pdev->name, rtc); +	if (ret) { +		dev_err(&pdev->dev, "Failed to request rtc irq: %d\n", ret); +		goto err_unregister_rtc; +	} + +	scratchpad = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SCRATCHPAD); +	if (scratchpad != 0x12345678) { +		ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SCRATCHPAD, 0x12345678); +		ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, 0); +		if (ret) { +			dev_err(&pdev->dev, "Could not write write to RTC registers\n"); +			goto err_free_irq; +		} +	} + +	return 0; + +err_free_irq: +	free_irq(rtc->irq, rtc); +err_unregister_rtc: +	rtc_device_unregister(rtc->rtc); +err_iounmap: +	platform_set_drvdata(pdev, NULL); +	iounmap(rtc->base); +err_release_mem_region: +	release_mem_region(rtc->mem->start, resource_size(rtc->mem)); +err_free: +	kfree(rtc); + +	return ret; +} + +static int __devexit jz4740_rtc_remove(struct platform_device *pdev) +{ +	struct jz4740_rtc *rtc = platform_get_drvdata(pdev); + +	free_irq(rtc->irq, rtc); + +	rtc_device_unregister(rtc->rtc); + +	iounmap(rtc->base); +	release_mem_region(rtc->mem->start, resource_size(rtc->mem)); + +	kfree(rtc); + +	platform_set_drvdata(pdev, NULL); + +	return 0; +} + +struct platform_driver jz4740_rtc_driver = { +	.probe = jz4740_rtc_probe, +	.remove = __devexit_p(jz4740_rtc_remove), +	.driver = { +		.name = "jz4740-rtc", +		.owner = THIS_MODULE, +	}, +}; + +static int __init jz4740_rtc_init(void) +{ +	return platform_driver_register(&jz4740_rtc_driver); +} +module_init(jz4740_rtc_init); + +static void __exit jz4740_rtc_exit(void) +{ +	platform_driver_unregister(&jz4740_rtc_driver); +} +module_exit(jz4740_rtc_exit); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("RTC driver for the JZ4740 SoC\n"); +MODULE_ALIAS("platform:jz4740-rtc"); diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c index 6dc4e624141..d60557cae8e 100644 --- a/drivers/rtc/rtc-m41t80.c +++ b/drivers/rtc/rtc-m41t80.c @@ -121,7 +121,7 @@ static int m41t80_get_datetime(struct i2c_client *client,  	/* assume 20YY not 19YY, and ignore the Century Bit */  	tm->tm_year = bcd2bin(buf[M41T80_REG_YEAR]) + 100; -	return 0; +	return rtc_valid_tm(tm);  }  /* Sets the given date and time to the real time clock. */ diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c index be8359fdb65..a99a0b554eb 100644 --- a/drivers/rtc/rtc-m48t59.c +++ b/drivers/rtc/rtc-m48t59.c @@ -105,7 +105,7 @@ static int m48t59_rtc_read_time(struct device *dev, struct rtc_time *tm)  	dev_dbg(dev, "RTC read time %04d-%02d-%02d %02d/%02d/%02d\n",  		tm->tm_year + 1900, tm->tm_mon, tm->tm_mday,  		tm->tm_hour, tm->tm_min, tm->tm_sec); -	return 0; +	return rtc_valid_tm(tm);  }  static int m48t59_rtc_set_time(struct device *dev, struct rtc_time *tm) @@ -196,7 +196,7 @@ static int m48t59_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)  	dev_dbg(dev, "RTC read alarm time %04d-%02d-%02d %02d/%02d/%02d\n",  		tm->tm_year + 1900, tm->tm_mon, tm->tm_mday,  		tm->tm_hour, tm->tm_min, tm->tm_sec); -	return 0; +	return rtc_valid_tm(tm);  }  /* @@ -506,7 +506,6 @@ out:  		free_irq(m48t59->irq, &pdev->dev);  	if (m48t59->ioaddr)  		iounmap(m48t59->ioaddr); -	if (m48t59)  		kfree(m48t59);  	return ret;  } diff --git a/drivers/rtc/rtc-m48t86.c b/drivers/rtc/rtc-m48t86.c index 7c045cffa9f..f981287d582 100644 --- a/drivers/rtc/rtc-m48t86.c +++ b/drivers/rtc/rtc-m48t86.c @@ -77,7 +77,7 @@ static int m48t86_rtc_read_time(struct device *dev, struct rtc_time *tm)  		if (ops->readbyte(M48T86_REG_HOUR) & 0x80)  			tm->tm_hour += 12; -	return 0; +	return rtc_valid_tm(tm);  }  static int m48t86_rtc_set_time(struct device *dev, struct rtc_time *tm) diff --git a/drivers/rtc/rtc-max6900.c b/drivers/rtc/rtc-max6900.c index a4f6665ab3c..486142c2637 100644 --- a/drivers/rtc/rtc-max6900.c +++ b/drivers/rtc/rtc-max6900.c @@ -159,7 +159,7 @@ static int max6900_i2c_read_time(struct i2c_client *client, struct rtc_time *tm)  		      bcd2bin(regs[MAX6900_REG_CENTURY]) * 100 - 1900;  	tm->tm_wday = bcd2bin(regs[MAX6900_REG_DW]); -	return 0; +	return rtc_valid_tm(tm);  }  static int max6900_i2c_clear_write_protect(struct i2c_client *client) diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c index db5d8c416d2..dfcdf0901d2 100644 --- a/drivers/rtc/rtc-mpc5121.c +++ b/drivers/rtc/rtc-mpc5121.c @@ -268,7 +268,7 @@ static const struct rtc_class_ops mpc5121_rtc_ops = {  	.update_irq_enable = mpc5121_rtc_update_irq_enable,  }; -static int __devinit mpc5121_rtc_probe(struct of_device *op, +static int __devinit mpc5121_rtc_probe(struct platform_device *op,  					const struct of_device_id *match)  {  	struct mpc5121_rtc_data *rtc; @@ -338,7 +338,7 @@ out_free:  	return err;  } -static int __devexit mpc5121_rtc_remove(struct of_device *op) +static int __devexit mpc5121_rtc_remove(struct platform_device *op)  {  	struct mpc5121_rtc_data *rtc = dev_get_drvdata(&op->dev);  	struct mpc5121_rtc_regs __iomem *regs = rtc->regs; diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c index 25ec921db07..0b06c1e03fd 100644 --- a/drivers/rtc/rtc-mxc.c +++ b/drivers/rtc/rtc-mxc.c @@ -83,12 +83,6 @@ struct rtc_plat_data {  	void __iomem *ioaddr;  	int irq;  	struct clk *clk; -	unsigned int irqen; -	int alrm_sec; -	int alrm_min; -	int alrm_hour; -	int alrm_mday; -	struct timespec mxc_rtc_delta;  	struct rtc_time g_rtc_alarm;  }; diff --git a/drivers/rtc/rtc-nuc900.c b/drivers/rtc/rtc-nuc900.c index a351bd5d817..62de66af0a6 100644 --- a/drivers/rtc/rtc-nuc900.c +++ b/drivers/rtc/rtc-nuc900.c @@ -85,25 +85,24 @@ static irqreturn_t nuc900_rtc_interrupt(int irq, void *_rtc)  static int *check_rtc_access_enable(struct nuc900_rtc *nuc900_rtc)  { -	unsigned int i; +	unsigned int timeout = 0x1000;  	__raw_writel(INIRRESET, nuc900_rtc->rtc_reg + REG_RTC_INIR);  	mdelay(10);  	__raw_writel(AERPOWERON, nuc900_rtc->rtc_reg + REG_RTC_AER); -	for (i = 0; i < 1000; i++) { -		if (__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB) -			return 0; -	} +	while (!(__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB) +								&& timeout--) +		mdelay(1); -	if ((__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB) == 0x0) -		return ERR_PTR(-ENODEV); +	if (!timeout) +		return ERR_PTR(-EPERM); -	return ERR_PTR(-EPERM); +	return 0;  } -static void nuc900_rtc_bcd2bin(unsigned int timereg, +static int nuc900_rtc_bcd2bin(unsigned int timereg,  				unsigned int calreg, struct rtc_time *tm)  {  	tm->tm_mday	= bcd2bin(calreg >> 0); @@ -114,15 +113,21 @@ static void nuc900_rtc_bcd2bin(unsigned int timereg,  	tm->tm_min	= bcd2bin(timereg >> 8);  	tm->tm_hour	= bcd2bin(timereg >> 16); -	rtc_valid_tm(tm); +	return rtc_valid_tm(tm);  } -static void nuc900_rtc_bin2bcd(struct rtc_time *settm, +static void nuc900_rtc_bin2bcd(struct device *dev, struct rtc_time *settm,  						struct nuc900_bcd_time *gettm)  {  	gettm->bcd_mday = bin2bcd(settm->tm_mday) << 0;  	gettm->bcd_mon  = bin2bcd(settm->tm_mon) << 8; -	gettm->bcd_year = bin2bcd(settm->tm_year - 100) << 16; + +	if (settm->tm_year < 100) { +		dev_warn(dev, "The year will be between 1970-1999, right?\n"); +		gettm->bcd_year = bin2bcd(settm->tm_year) << 16; +	} else { +		gettm->bcd_year = bin2bcd(settm->tm_year - 100) << 16; +	}  	gettm->bcd_sec  = bin2bcd(settm->tm_sec) << 0;  	gettm->bcd_min  = bin2bcd(settm->tm_min) << 8; @@ -165,9 +170,7 @@ static int nuc900_rtc_read_time(struct device *dev, struct rtc_time *tm)  	timeval = __raw_readl(rtc->rtc_reg + REG_RTC_TLR);  	clrval	= __raw_readl(rtc->rtc_reg + REG_RTC_CLR); -	nuc900_rtc_bcd2bin(timeval, clrval, tm); - -	return 0; +	return nuc900_rtc_bcd2bin(timeval, clrval, tm);  }  static int nuc900_rtc_set_time(struct device *dev, struct rtc_time *tm) @@ -177,7 +180,7 @@ static int nuc900_rtc_set_time(struct device *dev, struct rtc_time *tm)  	unsigned long val;  	int *err; -	nuc900_rtc_bin2bcd(tm, &gettm); +	nuc900_rtc_bin2bcd(dev, tm, &gettm);  	err = check_rtc_access_enable(rtc);  	if (IS_ERR(err)) @@ -200,9 +203,7 @@ static int nuc900_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)  	timeval = __raw_readl(rtc->rtc_reg + REG_RTC_TAR);  	carval	= __raw_readl(rtc->rtc_reg + REG_RTC_CAR); -	nuc900_rtc_bcd2bin(timeval, carval, &alrm->time); - -	return 0; +	return nuc900_rtc_bcd2bin(timeval, carval, &alrm->time);  }  static int nuc900_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) @@ -212,7 +213,7 @@ static int nuc900_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)  	unsigned long val;  	int *err; -	nuc900_rtc_bin2bcd(&alrm->time, &tm); +	nuc900_rtc_bin2bcd(dev, &alrm->time, &tm);  	err = check_rtc_access_enable(rtc);  	if (IS_ERR(err)) @@ -268,29 +269,30 @@ static int __devinit nuc900_rtc_probe(struct platform_device *pdev)  		goto fail2;  	} -	nuc900_rtc->irq_num = platform_get_irq(pdev, 0); -	if (request_irq(nuc900_rtc->irq_num, nuc900_rtc_interrupt, -				IRQF_DISABLED, "nuc900rtc", nuc900_rtc)) { -		dev_err(&pdev->dev, "NUC900 RTC request irq failed\n"); -		err = -EBUSY; -		goto fail3; -	} +	platform_set_drvdata(pdev, nuc900_rtc);  	nuc900_rtc->rtcdev = rtc_device_register(pdev->name, &pdev->dev,  						&nuc900_rtc_ops, THIS_MODULE);  	if (IS_ERR(nuc900_rtc->rtcdev)) {  		dev_err(&pdev->dev, "rtc device register faild\n");  		err = PTR_ERR(nuc900_rtc->rtcdev); -		goto fail4; +		goto fail3;  	} -	platform_set_drvdata(pdev, nuc900_rtc);  	__raw_writel(__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_TSSR) | MODE24,  					nuc900_rtc->rtc_reg + REG_RTC_TSSR); +	nuc900_rtc->irq_num = platform_get_irq(pdev, 0); +	if (request_irq(nuc900_rtc->irq_num, nuc900_rtc_interrupt, +				IRQF_DISABLED, "nuc900rtc", nuc900_rtc)) { +		dev_err(&pdev->dev, "NUC900 RTC request irq failed\n"); +		err = -EBUSY; +		goto fail4; +	} +  	return 0; -fail4:	free_irq(nuc900_rtc->irq_num, nuc900_rtc); +fail4:	rtc_device_unregister(nuc900_rtc->rtcdev);  fail3:	iounmap(nuc900_rtc->rtc_reg);  fail2:	release_mem_region(res->start, resource_size(res));  fail1:	kfree(nuc900_rtc); @@ -302,8 +304,8 @@ static int __devexit nuc900_rtc_remove(struct platform_device *pdev)  	struct nuc900_rtc *nuc900_rtc = platform_get_drvdata(pdev);  	struct resource *res; -	rtc_device_unregister(nuc900_rtc->rtcdev);  	free_irq(nuc900_rtc->irq_num, nuc900_rtc); +	rtc_device_unregister(nuc900_rtc->rtcdev);  	iounmap(nuc900_rtc->rtc_reg);  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index 1af42b4a6f5..b42c0c67926 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -172,14 +172,6 @@ static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm)  	return 0;  } -struct pcf8563_limit -{ -	unsigned char reg; -	unsigned char mask; -	unsigned char min; -	unsigned char max; -}; -  static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm)  {  	return pcf8563_get_datetime(to_i2c_client(dev), tm); diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c index 3587d9922f2..b7a6690e5b3 100644 --- a/drivers/rtc/rtc-pl031.c +++ b/drivers/rtc/rtc-pl031.c @@ -23,7 +23,6 @@  #include <linux/io.h>  #include <linux/bcd.h>  #include <linux/delay.h> -#include <linux/version.h>  #include <linux/slab.h>  /* @@ -404,7 +403,7 @@ static int pl031_probe(struct amba_device *adev, struct amba_id *id)  	}  	if (request_irq(adev->irq[0], pl031_interrupt, -			IRQF_DISABLED | IRQF_SHARED, "rtc-pl031", ldata)) { +			IRQF_DISABLED, "rtc-pl031", ldata)) {  		ret = -EIO;  		goto out_no_irq;  	} @@ -456,7 +455,7 @@ static struct rtc_class_ops stv2_pl031_ops = {  	.irq_set_freq = pl031_irq_set_freq,  }; -static struct amba_id pl031_ids[] __initdata = { +static struct amba_id pl031_ids[] = {  	{  		.id = 0x00041031,  		.mask = 0x000fffff, diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c index e9c6fa03598..29e867a1aaa 100644 --- a/drivers/rtc/rtc-pxa.c +++ b/drivers/rtc/rtc-pxa.c @@ -87,7 +87,6 @@ struct pxa_rtc {  	int			irq_Alrm;  	struct rtc_device	*rtc;  	spinlock_t		lock;		/* Protects this structure */ -	struct rtc_time		rtc_alarm;  };  static u32 ryxr_calc(struct rtc_time *tm) @@ -236,32 +235,34 @@ static int pxa_periodic_irq_set_state(struct device *dev, int enabled)  	return 0;  } -static int pxa_rtc_ioctl(struct device *dev, unsigned int cmd, -		unsigned long arg) +static int pxa_alarm_irq_enable(struct device *dev, unsigned int enabled)  {  	struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev); -	int ret = 0;  	spin_lock_irq(&pxa_rtc->lock); -	switch (cmd) { -	case RTC_AIE_OFF: -		rtsr_clear_bits(pxa_rtc, RTSR_RDALE1); -		break; -	case RTC_AIE_ON: + +	if (enabled)  		rtsr_set_bits(pxa_rtc, RTSR_RDALE1); -		break; -	case RTC_UIE_OFF: -		rtsr_clear_bits(pxa_rtc, RTSR_HZE); -		break; -	case RTC_UIE_ON: +	else +		rtsr_clear_bits(pxa_rtc, RTSR_RDALE1); + +	spin_unlock_irq(&pxa_rtc->lock); +	return 0; +} + +static int pxa_update_irq_enable(struct device *dev, unsigned int enabled) +{ +	struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev); + +	spin_lock_irq(&pxa_rtc->lock); + +	if (enabled)  		rtsr_set_bits(pxa_rtc, RTSR_HZE); -		break; -	default: -		ret = -ENOIOCTLCMD; -	} +	else +		rtsr_clear_bits(pxa_rtc, RTSR_HZE);  	spin_unlock_irq(&pxa_rtc->lock); -	return ret; +	return 0;  }  static int pxa_rtc_read_time(struct device *dev, struct rtc_time *tm) @@ -340,11 +341,12 @@ static int pxa_rtc_proc(struct device *dev, struct seq_file *seq)  static const struct rtc_class_ops pxa_rtc_ops = {  	.open = pxa_rtc_open,  	.release = pxa_rtc_release, -	.ioctl = pxa_rtc_ioctl,  	.read_time = pxa_rtc_read_time,  	.set_time = pxa_rtc_set_time,  	.read_alarm = pxa_rtc_read_alarm,  	.set_alarm = pxa_rtc_set_alarm, +	.alarm_irq_enable = pxa_alarm_irq_enable, +	.update_irq_enable = pxa_update_irq_enable,  	.proc = pxa_rtc_proc,  	.irq_set_state = pxa_periodic_irq_set_state,  	.irq_set_freq = pxa_periodic_irq_set_freq, diff --git a/drivers/rtc/rtc-rp5c01.c b/drivers/rtc/rtc-rp5c01.c index a95f733bb15..36eb6618446 100644 --- a/drivers/rtc/rtc-rp5c01.c +++ b/drivers/rtc/rtc-rp5c01.c @@ -63,6 +63,8 @@ enum {  struct rp5c01_priv {  	u32 __iomem *regs;  	struct rtc_device *rtc; +	spinlock_t lock;	/* against concurrent RTC/NVRAM access */ +	struct bin_attribute nvram_attr;  };  static inline unsigned int rp5c01_read(struct rp5c01_priv *priv, @@ -92,6 +94,7 @@ static int rp5c01_read_time(struct device *dev, struct rtc_time *tm)  {  	struct rp5c01_priv *priv = dev_get_drvdata(dev); +	spin_lock_irq(&priv->lock);  	rp5c01_lock(priv);  	tm->tm_sec  = rp5c01_read(priv, RP5C01_10_SECOND) * 10 + @@ -111,6 +114,7 @@ static int rp5c01_read_time(struct device *dev, struct rtc_time *tm)  		tm->tm_year += 100;  	rp5c01_unlock(priv); +	spin_unlock_irq(&priv->lock);  	return rtc_valid_tm(tm);  } @@ -119,6 +123,7 @@ static int rp5c01_set_time(struct device *dev, struct rtc_time *tm)  {  	struct rp5c01_priv *priv = dev_get_drvdata(dev); +	spin_lock_irq(&priv->lock);  	rp5c01_lock(priv);  	rp5c01_write(priv, tm->tm_sec / 10, RP5C01_10_SECOND); @@ -139,6 +144,7 @@ static int rp5c01_set_time(struct device *dev, struct rtc_time *tm)  	rp5c01_write(priv, tm->tm_year % 10, RP5C01_1_YEAR);  	rp5c01_unlock(priv); +	spin_unlock_irq(&priv->lock);  	return 0;  } @@ -147,6 +153,72 @@ static const struct rtc_class_ops rp5c01_rtc_ops = {  	.set_time	= rp5c01_set_time,  }; + +/* + * The NVRAM is organized as 2 blocks of 13 nibbles of 4 bits. + * We provide access to them like AmigaOS does: the high nibble of each 8-bit + * byte is stored in BLOCK10, the low nibble in BLOCK11. + */ + +static ssize_t rp5c01_nvram_read(struct file *filp, struct kobject *kobj, +				 struct bin_attribute *bin_attr, +				 char *buf, loff_t pos, size_t size) +{ +	struct device *dev = container_of(kobj, struct device, kobj); +	struct rp5c01_priv *priv = dev_get_drvdata(dev); +	ssize_t count; + +	spin_lock_irq(&priv->lock); + +	for (count = 0; size > 0 && pos < RP5C01_MODE; count++, size--) { +		u8 data; + +		rp5c01_write(priv, +			     RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK10, +			     RP5C01_MODE); +		data = rp5c01_read(priv, pos) << 4; +		rp5c01_write(priv, +			     RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK11, +			     RP5C01_MODE); +		data |= rp5c01_read(priv, pos++); +		rp5c01_write(priv, RP5C01_MODE_TIMER_EN | RP5C01_MODE_MODE01, +			     RP5C01_MODE); +		*buf++ = data; +	} + +	spin_unlock_irq(&priv->lock); +	return count; +} + +static ssize_t rp5c01_nvram_write(struct file *filp, struct kobject *kobj, +				  struct bin_attribute *bin_attr, +				  char *buf, loff_t pos, size_t size) +{ +	struct device *dev = container_of(kobj, struct device, kobj); +	struct rp5c01_priv *priv = dev_get_drvdata(dev); +	ssize_t count; + +	spin_lock_irq(&priv->lock); + +	for (count = 0; size > 0 && pos < RP5C01_MODE; count++, size--) { +		u8 data = *buf++; + +		rp5c01_write(priv, +			     RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK10, +			     RP5C01_MODE); +		rp5c01_write(priv, data >> 4, pos); +		rp5c01_write(priv, +			     RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK11, +			     RP5C01_MODE); +		rp5c01_write(priv, data & 0xf, pos++); +		rp5c01_write(priv, RP5C01_MODE_TIMER_EN | RP5C01_MODE_MODE01, +			     RP5C01_MODE); +	} + +	spin_unlock_irq(&priv->lock); +	return count; +} +  static int __init rp5c01_rtc_probe(struct platform_device *dev)  {  	struct resource *res; @@ -168,6 +240,15 @@ static int __init rp5c01_rtc_probe(struct platform_device *dev)  		goto out_free_priv;  	} +	sysfs_bin_attr_init(&priv->nvram_attr); +	priv->nvram_attr.attr.name = "nvram"; +	priv->nvram_attr.attr.mode = S_IRUGO | S_IWUSR; +	priv->nvram_attr.read = rp5c01_nvram_read; +	priv->nvram_attr.write = rp5c01_nvram_write; +	priv->nvram_attr.size = RP5C01_MODE; + +	spin_lock_init(&priv->lock); +  	rtc = rtc_device_register("rtc-rp5c01", &dev->dev, &rp5c01_rtc_ops,  				  THIS_MODULE);  	if (IS_ERR(rtc)) { @@ -177,8 +258,15 @@ static int __init rp5c01_rtc_probe(struct platform_device *dev)  	priv->rtc = rtc;  	platform_set_drvdata(dev, priv); + +	error = sysfs_create_bin_file(&dev->dev.kobj, &priv->nvram_attr); +	if (error) +		goto out_unregister; +  	return 0; +out_unregister: +	rtc_device_unregister(rtc);  out_unmap:  	iounmap(priv->regs);  out_free_priv: @@ -190,6 +278,7 @@ static int __exit rp5c01_rtc_remove(struct platform_device *dev)  {  	struct rp5c01_priv *priv = platform_get_drvdata(dev); +	sysfs_remove_bin_file(&dev->dev.kobj, &priv->nvram_attr);  	rtc_device_unregister(priv->rtc);  	iounmap(priv->regs);  	kfree(priv); diff --git a/drivers/rtc/rtc-rx8025.c b/drivers/rtc/rtc-rx8025.c index 789f62f9b47..1146e3522d3 100644 --- a/drivers/rtc/rtc-rx8025.c +++ b/drivers/rtc/rtc-rx8025.c @@ -461,7 +461,7 @@ static struct rtc_class_ops rx8025_rtc_ops = {   * Clock precision adjustment support   *   * According to the RX8025 SA/NB application manual the frequency and - * temperature charateristics can be approximated using the following + * temperature characteristics can be approximated using the following   * equation:   *   *   df = a * (ut - t)**2 diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 70b68d35f96..f57a87f4ae9 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -1,5 +1,8 @@  /* drivers/rtc/rtc-s3c.c   * + * Copyright (c) 2010 Samsung Electronics Co., Ltd. + *		http://www.samsung.com/ + *   * Copyright (c) 2004,2006 Simtec Electronics   *	Ben Dooks, <ben@simtec.co.uk>   *	http://armlinux.simtec.co.uk/ @@ -39,6 +42,7 @@ enum s3c_cpu_type {  static struct resource *s3c_rtc_mem; +static struct clk *rtc_clk;  static void __iomem *s3c_rtc_base;  static int s3c_rtc_alarmno = NO_IRQ;  static int s3c_rtc_tickno  = NO_IRQ; @@ -53,6 +57,10 @@ static irqreturn_t s3c_rtc_alarmirq(int irq, void *id)  	struct rtc_device *rdev = id;  	rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF); + +	if (s3c_rtc_cpu_type == TYPE_S3C64XX) +		writeb(S3C2410_INTP_ALM, s3c_rtc_base + S3C2410_INTP); +  	return IRQ_HANDLED;  } @@ -61,6 +69,10 @@ static irqreturn_t s3c_rtc_tickirq(int irq, void *id)  	struct rtc_device *rdev = id;  	rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF); + +	if (s3c_rtc_cpu_type == TYPE_S3C64XX) +		writeb(S3C2410_INTP_TIC, s3c_rtc_base + S3C2410_INTP); +  	return IRQ_HANDLED;  } @@ -94,7 +106,7 @@ static int s3c_rtc_setpie(struct device *dev, int enabled)  		if (enabled)  			tmp |= S3C64XX_RTCCON_TICEN; -		writeb(tmp, s3c_rtc_base + S3C2410_RTCCON); +		writew(tmp, s3c_rtc_base + S3C2410_RTCCON);  	} else {  		tmp = readb(s3c_rtc_base + S3C2410_TICNT);  		tmp &= ~S3C2410_TICNT_ENABLE; @@ -128,7 +140,7 @@ static int s3c_rtc_setfreq(struct device *dev, int freq)  	tmp |= (rtc_dev->max_user_freq / freq)-1; -	writeb(tmp, s3c_rtc_base + S3C2410_TICNT); +	writel(tmp, s3c_rtc_base + S3C2410_TICNT);  	spin_unlock_irq(&s3c_rtc_pie_lock);  	return 0; @@ -298,11 +310,6 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)  	s3c_rtc_setaie(alrm->enabled); -	if (alrm->enabled) -		enable_irq_wake(s3c_rtc_alarmno); -	else -		disable_irq_wake(s3c_rtc_alarmno); -  	return 0;  } @@ -431,6 +438,10 @@ static int __devexit s3c_rtc_remove(struct platform_device *dev)  	s3c_rtc_setpie(&dev->dev, 0);  	s3c_rtc_setaie(0); +	clk_disable(rtc_clk); +	clk_put(rtc_clk); +	rtc_clk = NULL; +  	iounmap(s3c_rtc_base);  	release_resource(s3c_rtc_mem);  	kfree(s3c_rtc_mem); @@ -442,6 +453,7 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)  {  	struct rtc_device *rtc;  	struct resource *res; +	unsigned int tmp, i;  	int ret;  	pr_debug("%s: probe=%p\n", __func__, pdev); @@ -488,6 +500,16 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)  		goto err_nomap;  	} +	rtc_clk = clk_get(&pdev->dev, "rtc"); +	if (IS_ERR(rtc_clk)) { +		dev_err(&pdev->dev, "failed to find rtc clock source\n"); +		ret = PTR_ERR(rtc_clk); +		rtc_clk = NULL; +		goto err_clk; +	} + +	clk_enable(rtc_clk); +  	/* check to see if everything is setup correctly */  	s3c_rtc_enable(pdev, 1); @@ -510,6 +532,15 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)  	s3c_rtc_cpu_type = platform_get_device_id(pdev)->driver_data; +	/* Check RTC Time */ + +	for (i = S3C2410_RTCSEC; i <= S3C2410_RTCYEAR; i += 0x4) { +		tmp = readb(s3c_rtc_base + i); + +		if ((tmp & 0xf) > 0x9 || ((tmp >> 4) & 0xf) > 0x9) +			writeb(0, s3c_rtc_base + i); +	} +  	if (s3c_rtc_cpu_type == TYPE_S3C64XX)  		rtc->max_user_freq = 32768;  	else @@ -523,6 +554,10 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)   err_nortc:  	s3c_rtc_enable(pdev, 0); +	clk_disable(rtc_clk); +	clk_put(rtc_clk); + + err_clk:  	iounmap(s3c_rtc_base);   err_nomap: @@ -547,6 +582,10 @@ static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state)  		ticnt_en_save &= S3C64XX_RTCCON_TICEN;  	}  	s3c_rtc_enable(pdev, 0); + +	if (device_may_wakeup(&pdev->dev)) +		enable_irq_wake(s3c_rtc_alarmno); +  	return 0;  } @@ -560,6 +599,10 @@ static int s3c_rtc_resume(struct platform_device *pdev)  		tmp = readb(s3c_rtc_base + S3C2410_RTCCON);  		writeb(tmp | ticnt_en_save, s3c_rtc_base + S3C2410_RTCCON);  	} + +	if (device_may_wakeup(&pdev->dev)) +		disable_irq_wake(s3c_rtc_alarmno); +  	return 0;  }  #else | 
