diff options
| author | Wim Van Sebroeck <wim@iguana.be> | 2007-05-11 19:03:13 +0000 | 
|---|---|---|
| committer | Wim Van Sebroeck <wim@iguana.be> | 2007-05-11 19:03:13 +0000 | 
| commit | 5c34202b8bf942da411b6599668a76b07449bbfd (patch) | |
| tree | 5719c361321eaddc8e4f1b0c8a7994f0e9a6fdd3 /drivers/rtc | |
| parent | 0d4804b31f91cfbcff6d62af0bc09a893a1c8ae0 (diff) | |
| parent | 1f8a6b658a943b4f04a1fc7b3a420360202c86cd (diff) | |
Merge /pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'drivers/rtc')
| -rw-r--r-- | drivers/rtc/Kconfig | 275 | ||||
| -rw-r--r-- | drivers/rtc/Makefile | 9 | ||||
| -rw-r--r-- | drivers/rtc/class.c | 118 | ||||
| -rw-r--r-- | drivers/rtc/hctosys.c | 14 | ||||
| -rw-r--r-- | drivers/rtc/interface.c | 86 | ||||
| -rw-r--r-- | drivers/rtc/rtc-at91rm9200.c | 32 | ||||
| -rw-r--r-- | drivers/rtc/rtc-bfin.c | 445 | ||||
| -rw-r--r-- | drivers/rtc/rtc-cmos.c | 79 | ||||
| -rw-r--r-- | drivers/rtc/rtc-core.h | 70 | ||||
| -rw-r--r-- | drivers/rtc/rtc-dev.c | 184 | ||||
| -rw-r--r-- | drivers/rtc/rtc-ds1553.c | 2 | ||||
| -rw-r--r-- | drivers/rtc/rtc-lib.c | 81 | ||||
| -rw-r--r-- | drivers/rtc/rtc-max6900.c | 311 | ||||
| -rw-r--r-- | drivers/rtc/rtc-omap.c | 57 | ||||
| -rw-r--r-- | drivers/rtc/rtc-pl031.c | 2 | ||||
| -rw-r--r-- | drivers/rtc/rtc-proc.c | 68 | ||||
| -rw-r--r-- | drivers/rtc/rtc-rs5c313.c | 423 | ||||
| -rw-r--r-- | drivers/rtc/rtc-s3c.c | 26 | ||||
| -rw-r--r-- | drivers/rtc/rtc-sa1100.c | 4 | ||||
| -rw-r--r-- | drivers/rtc/rtc-sh.c | 8 | ||||
| -rw-r--r-- | drivers/rtc/rtc-sysfs.c | 129 | ||||
| -rw-r--r-- | drivers/rtc/rtc-test.c | 6 | ||||
| -rw-r--r-- | drivers/rtc/rtc-vr41xx.c | 32 | 
23 files changed, 1803 insertions, 658 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 95826b92ca4..95ce8f49e38 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -3,6 +3,7 @@  #  menu "Real Time Clock" +	depends on !S390  config RTC_LIB  	tristate @@ -21,21 +22,31 @@ config RTC_CLASS  	  will be called rtc-class.  config RTC_HCTOSYS -	bool "Set system time from RTC on startup" +	bool "Set system time from RTC on startup and resume"  	depends on RTC_CLASS = y  	default y  	help -	  If you say yes here, the system time will be set using -	  the value read from the specified RTC device. This is useful -	  in order to avoid unnecessary fsck runs. +	  If you say yes here, the system time (wall clock) will be set using +	  the value read from a specified RTC device. This is useful to avoid +	  unnecessary fsck runs at boot time, and to network better.  config RTC_HCTOSYS_DEVICE -	string "The RTC to read the time from" +	string "RTC used to set the system time"  	depends on RTC_HCTOSYS = y  	default "rtc0"  	help -	  The RTC device that will be used as the source for -	  the system time, usually rtc0. +	  The RTC device that will be used to (re)initialize the system +	  clock, usually rtc0.  Initialization is done when the system +	  starts up, and when it resumes from a low power state. + +	  This clock should be battery-backed, so that it reads the correct +	  time when the system boots from a power-off state.  Otherwise, your +	  system will need an external clock source (like an NTP server). + +	  If the clock you specify here is not battery backed, it may still +	  be useful to reinitialize system time when resuming from system +	  sleep states.  Do not specify an RTC here unless it stays powered +	  during all this system's supported sleep states.  config RTC_DEBUG  	bool "RTC debug support" @@ -48,7 +59,7 @@ comment "RTC interfaces"  	depends on RTC_CLASS  config RTC_INTF_SYSFS -	tristate "sysfs" +	boolean "sysfs"  	depends on RTC_CLASS && SYSFS  	default RTC_CLASS  	help @@ -59,7 +70,7 @@ config RTC_INTF_SYSFS  	  will be called rtc-sysfs.  config RTC_INTF_PROC -	tristate "proc" +	boolean "proc"  	depends on RTC_CLASS && PROC_FS  	default RTC_CLASS  	help @@ -71,7 +82,7 @@ config RTC_INTF_PROC  	  will be called rtc-proc.  config RTC_INTF_DEV -	tristate "dev" +	boolean "dev"  	depends on RTC_CLASS  	default RTC_CLASS  	help @@ -88,48 +99,30 @@ config RTC_INTF_DEV_UIE_EMUL  	bool "RTC UIE emulation on dev interface"  	depends on RTC_INTF_DEV  	help -	  Provides an emulation for RTC_UIE if the underlaying rtc chip +	  Provides an emulation for RTC_UIE if the underlying rtc chip  	  driver does not expose RTC_UIE ioctls.  Those requests generate  	  once-per-second update interrupts, used for synchronization. -comment "RTC drivers" +config RTC_DRV_TEST +	tristate "Test driver/device"  	depends on RTC_CLASS - -# this 'CMOS' RTC driver is arch dependent because <asm-generic/rtc.h> -# requires <asm/mc146818rtc.h> defining CMOS_READ/CMOS_WRITE, and a -# global rtc_lock ... it's not yet just another platform_device. - -config RTC_DRV_CMOS -	tristate "PC-style 'CMOS' real time clock" -	depends on RTC_CLASS && (X86 || ALPHA || ARM26 || ARM \ -		|| M32R || ATARI || POWERPC) -	help -	  Say "yes" here to get direct support for the real time clock -	  found in every PC or ACPI-based system, and some other boards. -	  Specifically the original MC146818, compatibles like those in -	  PC south bridges, the DS12887 or M48T86, some multifunction -	  or LPC bus chips, and so on. - -	  Your system will need to define the platform device used by -	  this driver, otherwise it won't be accessible.  This means -	  you can safely enable this driver if you don't know whether -	  or not your board has this kind of hardware. - -	  This driver can also be built as a module. If so, the module -	  will be called rtc-cmos. - -config RTC_DRV_X1205 -	tristate "Xicor/Intersil X1205" -	depends on RTC_CLASS && I2C  	help  	  If you say yes here you get support for the -	  Xicor/Intersil X1205 RTC chip. +	  RTC test driver. It's a software RTC which can be +	  used to test the RTC subsystem APIs. It gets +	  the time from the system clock. +	  You want this driver only if you are doing development +	  on the RTC subsystem. Please read the source code +	  for further details.  	  This driver can also be built as a module. If so, the module -	  will be called rtc-x1205. +	  will be called rtc-test. + +comment "I2C RTC drivers" +	depends on RTC_CLASS  config RTC_DRV_DS1307 -	tristate "Dallas/Maxim DS1307 and similar I2C RTC chips" +	tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00"  	depends on RTC_CLASS && I2C  	help  	  If you say yes here you get support for various compatible RTC @@ -146,53 +139,55 @@ config RTC_DRV_DS1307  	  This driver can also be built as a module. If so, the module  	  will be called rtc-ds1307. -config RTC_DRV_DS1553 -	tristate "Dallas DS1553" -	depends on RTC_CLASS +config RTC_DRV_DS1672 +	tristate "Dallas/Maxim DS1672" +	depends on RTC_CLASS && I2C  	help  	  If you say yes here you get support for the -	  Dallas DS1553 timekeeping chip. +	  Dallas/Maxim DS1672 timekeeping chip.  	  This driver can also be built as a module. If so, the module -	  will be called rtc-ds1553. +	  will be called rtc-ds1672. -config RTC_DRV_ISL1208 -	tristate "Intersil 1208" +config RTC_DRV_MAX6900 +	tristate "Maxim 6900"  	depends on RTC_CLASS && I2C  	help -	  If you say yes here you get support for the -	  Intersil 1208 RTC chip. +	  If you say yes here you will get support for the +	  Maxim MAX6900 I2C RTC chip.  	  This driver can also be built as a module. If so, the module -	  will be called rtc-isl1208. +	  will be called rtc-max6900. -config RTC_DRV_DS1672 -	tristate "Dallas/Maxim DS1672" +config RTC_DRV_RS5C372 +	tristate "Ricoh RS5C372A/B"  	depends on RTC_CLASS && I2C  	help  	  If you say yes here you get support for the -	  Dallas/Maxim DS1672 timekeeping chip. +	  Ricoh RS5C372A and RS5C372B RTC chips.  	  This driver can also be built as a module. If so, the module -	  will be called rtc-ds1672. +	  will be called rtc-rs5c372. -config RTC_DRV_DS1742 -	tristate "Dallas DS1742/1743" -	depends on RTC_CLASS +config RTC_DRV_ISL1208 +	tristate "Intersil 1208" +	depends on RTC_CLASS && I2C  	help  	  If you say yes here you get support for the -	  Dallas DS1742/1743 timekeeping chip. +	  Intersil 1208 RTC chip.  	  This driver can also be built as a module. If so, the module -	  will be called rtc-ds1742. +	  will be called rtc-isl1208. -config RTC_DRV_OMAP -	tristate "TI OMAP1" -	depends on RTC_CLASS && ( \ -		ARCH_OMAP15XX || ARCH_OMAP16XX || ARCH_OMAP730 ) +config RTC_DRV_X1205 +	tristate "Xicor/Intersil X1205" +	depends on RTC_CLASS && I2C  	help -	  Say "yes" here to support the real time clock on TI OMAP1 chips. -	  This driver can also be built as a module called rtc-omap. +	  If you say yes here you get support for the +	  Xicor/Intersil X1205 RTC chip. + +	  This driver can also be built as a module. If so, the module +	  will be called rtc-x1205.  config RTC_DRV_PCF8563  	tristate "Philips PCF8563/Epson RTC8564" @@ -207,16 +202,20 @@ config RTC_DRV_PCF8563  config RTC_DRV_PCF8583  	tristate "Philips PCF8583" -	depends on RTC_CLASS && I2C && ARCH_RPC +	depends on RTC_CLASS && I2C  	help  	  If you say yes here you get support for the Philips PCF8583 -	  RTC chip found on Acorn RiscPCs.  This driver supports the +	  RTC chip found on Acorn RiscPCs. This driver supports the  	  platform specific method of retrieving the current year from -	  the RTC's SRAM. +	  the RTC's SRAM. It will work on other platforms with the same +	  chip, but the year will probably have to be tweaked.  	  This driver can also be built as a module. If so, the module  	  will be called rtc-pcf8583. +comment "SPI RTC drivers" +	depends on RTC_CLASS +  config RTC_DRV_RS5C348  	tristate "Ricoh RS5C348A/B"  	depends on RTC_CLASS && SPI @@ -227,15 +226,92 @@ config RTC_DRV_RS5C348  	  This driver can also be built as a module. If so, the module  	  will be called rtc-rs5c348. -config RTC_DRV_RS5C372 -	tristate "Ricoh RS5C372A/B" -	depends on RTC_CLASS && I2C +config RTC_DRV_MAX6902 +	tristate "Maxim 6902" +	depends on RTC_CLASS && SPI +	help +	  If you say yes here you will get support for the +	  Maxim MAX6902 SPI RTC chip. + +	  This driver can also be built as a module. If so, the module +	  will be called rtc-max6902. + +comment "Platform RTC drivers" +	depends on RTC_CLASS + +# this 'CMOS' RTC driver is arch dependent because <asm-generic/rtc.h> +# requires <asm/mc146818rtc.h> defining CMOS_READ/CMOS_WRITE, and a +# global rtc_lock ... it's not yet just another platform_device. + +config RTC_DRV_CMOS +	tristate "PC-style 'CMOS'" +	depends on RTC_CLASS && (X86 || ALPHA || ARM26 || ARM \ +		|| M32R || ATARI || POWERPC || MIPS) +	help +	  Say "yes" here to get direct support for the real time clock +	  found in every PC or ACPI-based system, and some other boards. +	  Specifically the original MC146818, compatibles like those in +	  PC south bridges, the DS12887 or M48T86, some multifunction +	  or LPC bus chips, and so on. + +	  Your system will need to define the platform device used by +	  this driver, otherwise it won't be accessible.  This means +	  you can safely enable this driver if you don't know whether +	  or not your board has this kind of hardware. + +	  This driver can also be built as a module. If so, the module +	  will be called rtc-cmos. + +config RTC_DRV_DS1553 +	tristate "Dallas DS1553" +	depends on RTC_CLASS  	help  	  If you say yes here you get support for the -	  Ricoh RS5C372A and RS5C372B RTC chips. +	  Dallas DS1553 timekeeping chip.  	  This driver can also be built as a module. If so, the module -	  will be called rtc-rs5c372. +	  will be called rtc-ds1553. + +config RTC_DRV_DS1742 +	tristate "Dallas DS1742/1743" +	depends on RTC_CLASS +	help +	  If you say yes here you get support for the +	  Dallas DS1742/1743 timekeeping chip. + +	  This driver can also be built as a module. If so, the module +	  will be called rtc-ds1742. + +config RTC_DRV_M48T86 +	tristate "ST M48T86/Dallas DS12887" +	depends on RTC_CLASS +	help +	  If you say Y here you will get support for the +	  ST M48T86 and Dallas DS12887 RTC chips. + +	  This driver can also be built as a module. If so, the module +	  will be called rtc-m48t86. + +config RTC_DRV_V3020 +	tristate "EM Microelectronic V3020" +	depends on RTC_CLASS +	help +	  If you say yes here you will get support for the +	  EM Microelectronic v3020 RTC chip. + +	  This driver can also be built as a module. If so, the module +	  will be called rtc-v3020. + +comment "on-CPU RTC drivers" +	depends on RTC_CLASS + +config RTC_DRV_OMAP +	tristate "TI OMAP1" +	depends on RTC_CLASS && ( \ +		ARCH_OMAP15XX || ARCH_OMAP16XX || ARCH_OMAP730 ) +	help +	  Say "yes" here to support the real time clock on TI OMAP1 chips. +	  This driver can also be built as a module called rtc-omap.  config RTC_DRV_S3C  	tristate "Samsung S3C series SoC RTC" @@ -253,16 +329,6 @@ config RTC_DRV_S3C  	  This driver can also be build as a module. If so, the module  	  will be called rtc-s3c. -config RTC_DRV_M48T86 -	tristate "ST M48T86/Dallas DS12887" -	depends on RTC_CLASS -	help -	  If you say Y here you will get support for the -	  ST M48T86 and Dallas DS12887 RTC chips. - -	  This driver can also be built as a module. If so, the module -	  will be called rtc-m48t86. -  config RTC_DRV_EP93XX  	tristate "Cirrus Logic EP93XX"  	depends on RTC_CLASS && ARCH_EP93XX @@ -308,7 +374,7 @@ config RTC_DRV_PL031  	depends on RTC_CLASS && ARM_AMBA  	help  	  If you say Y here you will get access to ARM AMBA -	  PrimeCell PL031 UART found on certain ARM SOCs. +	  PrimeCell PL031 RTC found on certain ARM SOCs.  	  To compile this driver as a module, choose M here: the  	  module will be called rtc-pl031. @@ -319,39 +385,20 @@ config RTC_DRV_AT91RM9200  	help  	  Driver for the Atmel AT91RM9200's internal RTC (Realtime Clock). -config RTC_DRV_TEST -	tristate "Test driver/device" -	depends on RTC_CLASS -	help -	  If you say yes here you get support for the -	  RTC test driver. It's a software RTC which can be -	  used to test the RTC subsystem APIs. It gets -	  the time from the system clock. -	  You want this driver only if you are doing development -	  on the RTC subsystem. Please read the source code -	  for further details. - -	  This driver can also be built as a module. If so, the module -	  will be called rtc-test. - -config RTC_DRV_MAX6902 -	tristate "Maxim 6902" -	depends on RTC_CLASS && SPI +config RTC_DRV_BFIN +	tristate "Blackfin On-Chip RTC" +	depends on RTC_CLASS && BFIN  	help  	  If you say yes here you will get support for the -	  Maxim MAX6902 spi RTC chip. +	  Blackfin On-Chip Real Time Clock.  	  This driver can also be built as a module. If so, the module -	  will be called rtc-max6902. +	  will be called rtc-bfin. -config RTC_DRV_V3020 -	tristate "EM Microelectronic V3020" -	depends on RTC_CLASS +config RTC_DRV_RS5C313 +	tristate "Ricoh RS5C313" +	depends on RTC_CLASS && SH_LANDISK  	help -	  If you say yes here you will get support for the -	  EM Microelectronic v3020 RTC chip. - -	  This driver can also be built as a module. If so, the module -	  will be called rtc-v3020. +	  If you say yes here you get support for the Ricoh RS5C313 RTC chips.  endmenu diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 92bfe1b3a5f..a1afbc23607 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -11,9 +11,9 @@ obj-$(CONFIG_RTC_HCTOSYS)	+= hctosys.o  obj-$(CONFIG_RTC_CLASS)		+= rtc-core.o  rtc-core-y			:= class.o interface.o -obj-$(CONFIG_RTC_INTF_SYSFS)	+= rtc-sysfs.o -obj-$(CONFIG_RTC_INTF_PROC)	+= rtc-proc.o -obj-$(CONFIG_RTC_INTF_DEV)	+= rtc-dev.o +rtc-core-$(CONFIG_RTC_INTF_DEV)	+= rtc-dev.o +rtc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o +rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o  obj-$(CONFIG_RTC_DRV_CMOS)	+= rtc-cmos.o  obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205.o @@ -30,11 +30,14 @@ obj-$(CONFIG_RTC_DRV_S3C)	+= rtc-s3c.o  obj-$(CONFIG_RTC_DRV_RS5C348)	+= rtc-rs5c348.o  obj-$(CONFIG_RTC_DRV_M48T86)	+= rtc-m48t86.o  obj-$(CONFIG_RTC_DRV_DS1553)	+= rtc-ds1553.o +obj-$(CONFIG_RTC_DRV_RS5C313)	+= rtc-rs5c313.o  obj-$(CONFIG_RTC_DRV_EP93XX)	+= rtc-ep93xx.o  obj-$(CONFIG_RTC_DRV_SA1100)	+= rtc-sa1100.o  obj-$(CONFIG_RTC_DRV_VR41XX)	+= rtc-vr41xx.o  obj-$(CONFIG_RTC_DRV_PL031)	+= rtc-pl031.o +obj-$(CONFIG_RTC_DRV_MAX6900)	+= rtc-max6900.o  obj-$(CONFIG_RTC_DRV_MAX6902)	+= rtc-max6902.o  obj-$(CONFIG_RTC_DRV_V3020)	+= rtc-v3020.o  obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o  obj-$(CONFIG_RTC_DRV_SH)	+= rtc-sh.o +obj-$(CONFIG_RTC_DRV_BFIN)	+= rtc-bfin.o diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 04aaa634723..8b3cd31d6a6 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -16,19 +16,94 @@  #include <linux/kdev_t.h>  #include <linux/idr.h> +#include "rtc-core.h" + +  static DEFINE_IDR(rtc_idr);  static DEFINE_MUTEX(idr_lock);  struct class *rtc_class; -static void rtc_device_release(struct class_device *class_dev) +static void rtc_device_release(struct device *dev)  { -	struct rtc_device *rtc = to_rtc_device(class_dev); +	struct rtc_device *rtc = to_rtc_device(dev);  	mutex_lock(&idr_lock);  	idr_remove(&rtc_idr, rtc->id);  	mutex_unlock(&idr_lock);  	kfree(rtc);  } +#if defined(CONFIG_PM) && defined(CONFIG_RTC_HCTOSYS_DEVICE) + +/* + * On suspend(), measure the delta between one RTC and the + * system's wall clock; restore it on resume(). + */ + +static struct timespec	delta; +static time_t		oldtime; + +static int rtc_suspend(struct device *dev, pm_message_t mesg) +{ +	struct rtc_device	*rtc = to_rtc_device(dev); +	struct rtc_time		tm; + +	if (strncmp(rtc->dev.bus_id, +				CONFIG_RTC_HCTOSYS_DEVICE, +				BUS_ID_SIZE) != 0) +		return 0; + +	rtc_read_time(rtc, &tm); +	rtc_tm_to_time(&tm, &oldtime); + +	/* RTC precision is 1 second; adjust delta for avg 1/2 sec err */ +	set_normalized_timespec(&delta, +				xtime.tv_sec - oldtime, +				xtime.tv_nsec - (NSEC_PER_SEC >> 1)); + +	return 0; +} + +static int rtc_resume(struct device *dev) +{ +	struct rtc_device	*rtc = to_rtc_device(dev); +	struct rtc_time		tm; +	time_t			newtime; +	struct timespec		time; + +	if (strncmp(rtc->dev.bus_id, +				CONFIG_RTC_HCTOSYS_DEVICE, +				BUS_ID_SIZE) != 0) +		return 0; + +	rtc_read_time(rtc, &tm); +	if (rtc_valid_tm(&tm) != 0) { +		pr_debug("%s:  bogus resume time\n", rtc->dev.bus_id); +		return 0; +	} +	rtc_tm_to_time(&tm, &newtime); +	if (newtime <= oldtime) { +		if (newtime < oldtime) +			pr_debug("%s:  time travel!\n", rtc->dev.bus_id); +		return 0; +	} + +	/* restore wall clock using delta against this RTC; +	 * adjust again for avg 1/2 second RTC sampling error +	 */ +	set_normalized_timespec(&time, +				newtime + delta.tv_sec, +				(NSEC_PER_SEC >> 1) + delta.tv_nsec); +	do_settimeofday(&time); + +	return 0; +} + +#else +#define rtc_suspend	NULL +#define rtc_resume	NULL +#endif + +  /**   * rtc_device_register - register w/ RTC class   * @dev: the device to register @@ -70,23 +145,29 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev,  	rtc->ops = ops;  	rtc->owner = owner;  	rtc->max_user_freq = 64; -	rtc->class_dev.dev = dev; -	rtc->class_dev.class = rtc_class; -	rtc->class_dev.release = rtc_device_release; +	rtc->dev.parent = dev; +	rtc->dev.class = rtc_class; +	rtc->dev.release = rtc_device_release;  	mutex_init(&rtc->ops_lock);  	spin_lock_init(&rtc->irq_lock);  	spin_lock_init(&rtc->irq_task_lock);  	strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE); -	snprintf(rtc->class_dev.class_id, BUS_ID_SIZE, "rtc%d", id); +	snprintf(rtc->dev.bus_id, BUS_ID_SIZE, "rtc%d", id); -	err = class_device_register(&rtc->class_dev); +	rtc_dev_prepare(rtc); + +	err = device_register(&rtc->dev);  	if (err)  		goto exit_kfree; +	rtc_dev_add_device(rtc); +	rtc_sysfs_add_device(rtc); +	rtc_proc_add_device(rtc); +  	dev_info(dev, "rtc core: registered %s as %s\n", -			rtc->name, rtc->class_dev.class_id); +			rtc->name, rtc->dev.bus_id);  	return rtc; @@ -113,26 +194,22 @@ EXPORT_SYMBOL_GPL(rtc_device_register);   */  void rtc_device_unregister(struct rtc_device *rtc)  { -	if (class_device_get(&rtc->class_dev) != NULL) { +	if (get_device(&rtc->dev) != NULL) {  		mutex_lock(&rtc->ops_lock);  		/* remove innards of this RTC, then disable it, before  		 * letting any rtc_class_open() users access it again  		 */ -		class_device_unregister(&rtc->class_dev); +		rtc_sysfs_del_device(rtc); +		rtc_dev_del_device(rtc); +		rtc_proc_del_device(rtc); +		device_unregister(&rtc->dev);  		rtc->ops = NULL;  		mutex_unlock(&rtc->ops_lock); -		class_device_put(&rtc->class_dev); +		put_device(&rtc->dev);  	}  }  EXPORT_SYMBOL_GPL(rtc_device_unregister); -int rtc_interface_register(struct class_interface *intf) -{ -	intf->class = rtc_class; -	return class_interface_register(intf); -} -EXPORT_SYMBOL_GPL(rtc_interface_register); -  static int __init rtc_init(void)  {  	rtc_class = class_create(THIS_MODULE, "rtc"); @@ -140,11 +217,16 @@ static int __init rtc_init(void)  		printk(KERN_ERR "%s: couldn't create class\n", __FILE__);  		return PTR_ERR(rtc_class);  	} +	rtc_class->suspend = rtc_suspend; +	rtc_class->resume = rtc_resume; +	rtc_dev_init(); +	rtc_sysfs_init(rtc_class);  	return 0;  }  static void __exit rtc_exit(void)  { +	rtc_dev_exit();  	class_destroy(rtc_class);  } diff --git a/drivers/rtc/hctosys.c b/drivers/rtc/hctosys.c index d02fe9a0001..178527252c6 100644 --- a/drivers/rtc/hctosys.c +++ b/drivers/rtc/hctosys.c @@ -26,15 +26,15 @@ static int __init rtc_hctosys(void)  {  	int err;  	struct rtc_time tm; -	struct class_device *class_dev = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE); +	struct rtc_device *rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE); -	if (class_dev == NULL) { +	if (rtc == NULL) {  		printk("%s: unable to open rtc device (%s)\n",  			__FILE__, CONFIG_RTC_HCTOSYS_DEVICE);  		return -ENODEV;  	} -	err = rtc_read_time(class_dev, &tm); +	err = rtc_read_time(rtc, &tm);  	if (err == 0) {  		err = rtc_valid_tm(&tm);  		if (err == 0) { @@ -46,7 +46,7 @@ static int __init rtc_hctosys(void)  			do_settimeofday(&tv); -			dev_info(class_dev->dev, +			dev_info(rtc->dev.parent,  				"setting the system clock to "  				"%d-%02d-%02d %02d:%02d:%02d (%u)\n",  				tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, @@ -54,14 +54,14 @@ static int __init rtc_hctosys(void)  				(unsigned int) tv.tv_sec);  		}  		else -			dev_err(class_dev->dev, +			dev_err(rtc->dev.parent,  				"hctosys: invalid date/time\n");  	}  	else -		dev_err(class_dev->dev, +		dev_err(rtc->dev.parent,  			"hctosys: unable to read the hardware clock\n"); -	rtc_class_close(class_dev); +	rtc_class_close(rtc);  	return 0;  } diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index ef40df0f169..ad66c6ecf36 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -13,10 +13,9 @@  #include <linux/rtc.h> -int rtc_read_time(struct class_device *class_dev, struct rtc_time *tm) +int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)  {  	int err; -	struct rtc_device *rtc = to_rtc_device(class_dev);  	err = mutex_lock_interruptible(&rtc->ops_lock);  	if (err) @@ -28,7 +27,7 @@ int rtc_read_time(struct class_device *class_dev, struct rtc_time *tm)  		err = -EINVAL;  	else {  		memset(tm, 0, sizeof(struct rtc_time)); -		err = rtc->ops->read_time(class_dev->dev, tm); +		err = rtc->ops->read_time(rtc->dev.parent, tm);  	}  	mutex_unlock(&rtc->ops_lock); @@ -36,10 +35,9 @@ int rtc_read_time(struct class_device *class_dev, struct rtc_time *tm)  }  EXPORT_SYMBOL_GPL(rtc_read_time); -int rtc_set_time(struct class_device *class_dev, struct rtc_time *tm) +int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)  {  	int err; -	struct rtc_device *rtc = to_rtc_device(class_dev);  	err = rtc_valid_tm(tm);  	if (err != 0) @@ -54,17 +52,16 @@ int rtc_set_time(struct class_device *class_dev, struct rtc_time *tm)  	else if (!rtc->ops->set_time)  		err = -EINVAL;  	else -		err = rtc->ops->set_time(class_dev->dev, tm); +		err = rtc->ops->set_time(rtc->dev.parent, tm);  	mutex_unlock(&rtc->ops_lock);  	return err;  }  EXPORT_SYMBOL_GPL(rtc_set_time); -int rtc_set_mmss(struct class_device *class_dev, unsigned long secs) +int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs)  {  	int err; -	struct rtc_device *rtc = to_rtc_device(class_dev);  	err = mutex_lock_interruptible(&rtc->ops_lock);  	if (err) @@ -73,11 +70,11 @@ int rtc_set_mmss(struct class_device *class_dev, unsigned long secs)  	if (!rtc->ops)  		err = -ENODEV;  	else if (rtc->ops->set_mmss) -		err = rtc->ops->set_mmss(class_dev->dev, secs); +		err = rtc->ops->set_mmss(rtc->dev.parent, secs);  	else if (rtc->ops->read_time && rtc->ops->set_time) {  		struct rtc_time new, old; -		err = rtc->ops->read_time(class_dev->dev, &old); +		err = rtc->ops->read_time(rtc->dev.parent, &old);  		if (err == 0) {  			rtc_time_to_tm(secs, &new); @@ -89,7 +86,8 @@ int rtc_set_mmss(struct class_device *class_dev, unsigned long secs)  			 */  			if (!((old.tm_hour == 23 && old.tm_min == 59) ||  				(new.tm_hour == 23 && new.tm_min == 59))) -				err = rtc->ops->set_time(class_dev->dev, &new); +				err = rtc->ops->set_time(rtc->dev.parent, +						&new);  		}  	}  	else @@ -101,10 +99,9 @@ int rtc_set_mmss(struct class_device *class_dev, unsigned long secs)  }  EXPORT_SYMBOL_GPL(rtc_set_mmss); -int rtc_read_alarm(struct class_device *class_dev, struct rtc_wkalrm *alarm) +int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)  {  	int err; -	struct rtc_device *rtc = to_rtc_device(class_dev);  	err = mutex_lock_interruptible(&rtc->ops_lock);  	if (err) @@ -116,7 +113,7 @@ int rtc_read_alarm(struct class_device *class_dev, struct rtc_wkalrm *alarm)  		err = -EINVAL;  	else {  		memset(alarm, 0, sizeof(struct rtc_wkalrm)); -		err = rtc->ops->read_alarm(class_dev->dev, alarm); +		err = rtc->ops->read_alarm(rtc->dev.parent, alarm);  	}  	mutex_unlock(&rtc->ops_lock); @@ -124,10 +121,13 @@ int rtc_read_alarm(struct class_device *class_dev, struct rtc_wkalrm *alarm)  }  EXPORT_SYMBOL_GPL(rtc_read_alarm); -int rtc_set_alarm(struct class_device *class_dev, struct rtc_wkalrm *alarm) +int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)  {  	int err; -	struct rtc_device *rtc = to_rtc_device(class_dev); + +	err = rtc_valid_tm(&alarm->time); +	if (err != 0) +		return err;  	err = mutex_lock_interruptible(&rtc->ops_lock);  	if (err) @@ -138,7 +138,7 @@ int rtc_set_alarm(struct class_device *class_dev, struct rtc_wkalrm *alarm)  	else if (!rtc->ops->set_alarm)  		err = -EINVAL;  	else -		err = rtc->ops->set_alarm(class_dev->dev, alarm); +		err = rtc->ops->set_alarm(rtc->dev.parent, alarm);  	mutex_unlock(&rtc->ops_lock);  	return err; @@ -147,16 +147,14 @@ EXPORT_SYMBOL_GPL(rtc_set_alarm);  /**   * rtc_update_irq - report RTC periodic, alarm, and/or update irqs - * @class_dev: the rtc's class device + * @rtc: the rtc device   * @num: how many irqs are being reported (usually one)   * @events: mask of RTC_IRQF with one or more of RTC_PF, RTC_AF, RTC_UF   * Context: in_interrupt(), irqs blocked   */ -void rtc_update_irq(struct class_device *class_dev, +void rtc_update_irq(struct rtc_device *rtc,  		unsigned long num, unsigned long events)  { -	struct rtc_device *rtc = to_rtc_device(class_dev); -  	spin_lock(&rtc->irq_lock);  	rtc->irq_data = (rtc->irq_data + (num << 8)) | events;  	spin_unlock(&rtc->irq_lock); @@ -171,40 +169,43 @@ void rtc_update_irq(struct class_device *class_dev,  }  EXPORT_SYMBOL_GPL(rtc_update_irq); -struct class_device *rtc_class_open(char *name) +struct rtc_device *rtc_class_open(char *name)  { -	struct class_device *class_dev = NULL, -				*class_dev_tmp; +	struct device *dev; +	struct rtc_device *rtc = NULL;  	down(&rtc_class->sem); -	list_for_each_entry(class_dev_tmp, &rtc_class->children, node) { -		if (strncmp(class_dev_tmp->class_id, name, BUS_ID_SIZE) == 0) { -			class_dev = class_device_get(class_dev_tmp); +	list_for_each_entry(dev, &rtc_class->devices, node) { +		if (strncmp(dev->bus_id, name, BUS_ID_SIZE) == 0) { +			dev = get_device(dev); +			if (dev) +				rtc = to_rtc_device(dev);  			break;  		}  	} -	if (class_dev) { -		if (!try_module_get(to_rtc_device(class_dev)->owner)) -			class_dev = NULL; +	if (rtc) { +		if (!try_module_get(rtc->owner)) { +			put_device(dev); +			rtc = NULL; +		}  	}  	up(&rtc_class->sem); -	return class_dev; +	return rtc;  }  EXPORT_SYMBOL_GPL(rtc_class_open); -void rtc_class_close(struct class_device *class_dev) +void rtc_class_close(struct rtc_device *rtc)  { -	module_put(to_rtc_device(class_dev)->owner); -	class_device_put(class_dev); +	module_put(rtc->owner); +	put_device(&rtc->dev);  }  EXPORT_SYMBOL_GPL(rtc_class_close); -int rtc_irq_register(struct class_device *class_dev, struct rtc_task *task) +int rtc_irq_register(struct rtc_device *rtc, struct rtc_task *task)  {  	int retval = -EBUSY; -	struct rtc_device *rtc = to_rtc_device(class_dev);  	if (task == NULL || task->func == NULL)  		return -EINVAL; @@ -220,9 +221,8 @@ int rtc_irq_register(struct class_device *class_dev, struct rtc_task *task)  }  EXPORT_SYMBOL_GPL(rtc_irq_register); -void rtc_irq_unregister(struct class_device *class_dev, struct rtc_task *task) +void rtc_irq_unregister(struct rtc_device *rtc, struct rtc_task *task)  { -	struct rtc_device *rtc = to_rtc_device(class_dev);  	spin_lock_irq(&rtc->irq_task_lock);  	if (rtc->irq_task == task) @@ -231,11 +231,10 @@ void rtc_irq_unregister(struct class_device *class_dev, struct rtc_task *task)  }  EXPORT_SYMBOL_GPL(rtc_irq_unregister); -int rtc_irq_set_state(struct class_device *class_dev, struct rtc_task *task, int enabled) +int rtc_irq_set_state(struct rtc_device *rtc, struct rtc_task *task, int enabled)  {  	int err = 0;  	unsigned long flags; -	struct rtc_device *rtc = to_rtc_device(class_dev);  	if (rtc->ops->irq_set_state == NULL)  		return -ENXIO; @@ -246,17 +245,16 @@ int rtc_irq_set_state(struct class_device *class_dev, struct rtc_task *task, int  	spin_unlock_irqrestore(&rtc->irq_task_lock, flags);  	if (err == 0) -		err = rtc->ops->irq_set_state(class_dev->dev, enabled); +		err = rtc->ops->irq_set_state(rtc->dev.parent, enabled);  	return err;  }  EXPORT_SYMBOL_GPL(rtc_irq_set_state); -int rtc_irq_set_freq(struct class_device *class_dev, struct rtc_task *task, int freq) +int rtc_irq_set_freq(struct rtc_device *rtc, struct rtc_task *task, int freq)  {  	int err = 0;  	unsigned long flags; -	struct rtc_device *rtc = to_rtc_device(class_dev);  	if (rtc->ops->irq_set_freq == NULL)  		return -ENXIO; @@ -267,7 +265,7 @@ int rtc_irq_set_freq(struct class_device *class_dev, struct rtc_task *task, int  	spin_unlock_irqrestore(&rtc->irq_task_lock, flags);  	if (err == 0) { -		err = rtc->ops->irq_set_freq(class_dev->dev, freq); +		err = rtc->ops->irq_set_freq(rtc->dev.parent, freq);  		if (err == 0)  			rtc->irq_freq = freq;  	} diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c index ac0e68e2f02..33795e5a559 100644 --- a/drivers/rtc/rtc-at91rm9200.c +++ b/drivers/rtc/rtc-at91rm9200.c @@ -263,7 +263,7 @@ static irqreturn_t at91_rtc_interrupt(int irq, void *dev_id)  		at91_sys_write(AT91_RTC_SCCR, rtsr);	/* clear status reg */ -		rtc_update_irq(&rtc->class_dev, 1, events); +		rtc_update_irq(rtc, 1, events);  		pr_debug("%s(): num=%ld, events=0x%02lx\n", __FUNCTION__,  			events >> 8, events & 0x000000FF); @@ -348,21 +348,10 @@ static int __exit at91_rtc_remove(struct platform_device *pdev)  /* AT91RM9200 RTC Power management control */ -static struct timespec at91_rtc_delta;  static u32 at91_rtc_imr;  static int at91_rtc_suspend(struct platform_device *pdev, pm_message_t state)  { -	struct rtc_time tm; -	struct timespec time; - -	time.tv_nsec = 0; - -	/* calculate time delta for suspend */ -	at91_rtc_readtime(&pdev->dev, &tm); -	rtc_tm_to_time(&tm, &time.tv_sec); -	save_time_delta(&at91_rtc_delta, &time); -  	/* this IRQ is shared with DBGU and other hardware which isn't  	 * necessarily doing PM like we are...  	 */ @@ -374,36 +363,17 @@ static int at91_rtc_suspend(struct platform_device *pdev, pm_message_t state)  		else  			at91_sys_write(AT91_RTC_IDR, at91_rtc_imr);  	} - -	pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__, -		1900 + tm.tm_year, tm.tm_mon, tm.tm_mday, -		tm.tm_hour, tm.tm_min, tm.tm_sec); -  	return 0;  }  static int at91_rtc_resume(struct platform_device *pdev)  { -	struct rtc_time tm; -	struct timespec time; - -	time.tv_nsec = 0; - -	at91_rtc_readtime(&pdev->dev, &tm); -	rtc_tm_to_time(&tm, &time.tv_sec); -	restore_time_delta(&at91_rtc_delta, &time); -  	if (at91_rtc_imr) {  		if (device_may_wakeup(&pdev->dev))  			disable_irq_wake(AT91_ID_SYS);  		else  			at91_sys_write(AT91_RTC_IER, at91_rtc_imr);  	} - -	pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__, -		1900 + tm.tm_year, tm.tm_mon, tm.tm_mday, -		tm.tm_hour, tm.tm_min, tm.tm_sec); -  	return 0;  }  #else diff --git a/drivers/rtc/rtc-bfin.c b/drivers/rtc/rtc-bfin.c new file mode 100644 index 00000000000..260ead95991 --- /dev/null +++ b/drivers/rtc/rtc-bfin.c @@ -0,0 +1,445 @@ +/* + * Blackfin On-Chip Real Time Clock Driver + *  Supports BF531/BF532/BF533/BF534/BF536/BF537 + * + * Copyright 2004-2007 Analog Devices Inc. + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +/* The biggest issue we deal with in this driver is that register writes are + * synced to the RTC frequency of 1Hz.  So if you write to a register and + * attempt to write again before the first write has completed, the new write + * is simply discarded.  This can easily be troublesome if userspace disables + * one event (say periodic) and then right after enables an event (say alarm). + * Since all events are maintained in the same interrupt mask register, if + * we wrote to it to disable the first event and then wrote to it again to + * enable the second event, that second event would not be enabled as the + * write would be discarded and things quickly fall apart. + * + * To keep this delay from significantly degrading performance (we, in theory, + * would have to sleep for up to 1 second everytime we wanted to write a + * register), we only check the write pending status before we start to issue + * a new write.  We bank on the idea that it doesnt matter when the sync + * happens so long as we don't attempt another write before it does.  The only + * time userspace would take this penalty is when they try and do multiple + * operations right after another ... but in this case, they need to take the + * sync penalty, so we should be OK. + * + * Also note that the RTC_ISTAT register does not suffer this penalty; its + * writes to clear status registers complete immediately. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/bcd.h> +#include <linux/rtc.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/seq_file.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/delay.h> + +#include <asm/blackfin.h> + +#define stamp(fmt, args...) pr_debug("%s:%i: " fmt "\n", __FUNCTION__, __LINE__, ## args) +#define stampit() stamp("here i am") + +struct bfin_rtc { +	struct rtc_device *rtc_dev; +	struct rtc_time rtc_alarm; +	spinlock_t lock; +}; + +/* Bit values for the ISTAT / ICTL registers */ +#define RTC_ISTAT_WRITE_COMPLETE  0x8000 +#define RTC_ISTAT_WRITE_PENDING   0x4000 +#define RTC_ISTAT_ALARM_DAY       0x0040 +#define RTC_ISTAT_24HR            0x0020 +#define RTC_ISTAT_HOUR            0x0010 +#define RTC_ISTAT_MIN             0x0008 +#define RTC_ISTAT_SEC             0x0004 +#define RTC_ISTAT_ALARM           0x0002 +#define RTC_ISTAT_STOPWATCH       0x0001 + +/* Shift values for RTC_STAT register */ +#define DAY_BITS_OFF    17 +#define HOUR_BITS_OFF   12 +#define MIN_BITS_OFF    6 +#define SEC_BITS_OFF    0 + +/* Some helper functions to convert between the common RTC notion of time + * and the internal Blackfin notion that is stored in 32bits. + */ +static inline u32 rtc_time_to_bfin(unsigned long now) +{ +	u32 sec  = (now % 60); +	u32 min  = (now % (60 * 60)) / 60; +	u32 hour = (now % (60 * 60 * 24)) / (60 * 60); +	u32 days = (now / (60 * 60 * 24)); +	return (sec  << SEC_BITS_OFF) + +	       (min  << MIN_BITS_OFF) + +	       (hour << HOUR_BITS_OFF) + +	       (days << DAY_BITS_OFF); +} +static inline unsigned long rtc_bfin_to_time(u32 rtc_bfin) +{ +	return (((rtc_bfin >> SEC_BITS_OFF)  & 0x003F)) + +	       (((rtc_bfin >> MIN_BITS_OFF)  & 0x003F) * 60) + +	       (((rtc_bfin >> HOUR_BITS_OFF) & 0x001F) * 60 * 60) + +	       (((rtc_bfin >> DAY_BITS_OFF)  & 0x7FFF) * 60 * 60 * 24); +} +static inline void rtc_bfin_to_tm(u32 rtc_bfin, struct rtc_time *tm) +{ +	rtc_time_to_tm(rtc_bfin_to_time(rtc_bfin), tm); +} + +/* Wait for the previous write to a RTC register to complete. + * Unfortunately, we can't sleep here as that introduces a race condition when + * turning on interrupt events.  Consider this: + *  - process sets alarm + *  - process enables alarm + *  - process sleeps while waiting for rtc write to sync + *  - interrupt fires while process is sleeping + *  - interrupt acks the event by writing to ISTAT + *  - interrupt sets the WRITE PENDING bit + *  - interrupt handler finishes + *  - process wakes up, sees WRITE PENDING bit set, goes to sleep + *  - interrupt fires while process is sleeping + * If anyone can point out the obvious solution here, i'm listening :).  This + * shouldn't be an issue on an SMP or preempt system as this function should + * only be called with the rtc lock held. + */ +static void rtc_bfin_sync_pending(void) +{ +	stampit(); +	while (!(bfin_read_RTC_ISTAT() & RTC_ISTAT_WRITE_COMPLETE)) { +		if (!(bfin_read_RTC_ISTAT() & RTC_ISTAT_WRITE_PENDING)) +			break; +	} +	bfin_write_RTC_ISTAT(RTC_ISTAT_WRITE_COMPLETE); +} + +static void rtc_bfin_reset(struct bfin_rtc *rtc) +{ +	/* Initialize the RTC. Enable pre-scaler to scale RTC clock +	 * to 1Hz and clear interrupt/status registers. */ +	spin_lock_irq(&rtc->lock); +	rtc_bfin_sync_pending(); +	bfin_write_RTC_PREN(0x1); +	bfin_write_RTC_ICTL(0); +	bfin_write_RTC_SWCNT(0); +	bfin_write_RTC_ALARM(0); +	bfin_write_RTC_ISTAT(0xFFFF); +	spin_unlock_irq(&rtc->lock); +} + +static irqreturn_t bfin_rtc_interrupt(int irq, void *dev_id) +{ +	struct platform_device *pdev = to_platform_device(dev_id); +	struct bfin_rtc *rtc = platform_get_drvdata(pdev); +	unsigned long events = 0; +	u16 rtc_istat; + +	stampit(); + +	spin_lock_irq(&rtc->lock); + +	rtc_istat = bfin_read_RTC_ISTAT(); + +	if (rtc_istat & (RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY)) { +		bfin_write_RTC_ISTAT(RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY); +		events |= RTC_AF | RTC_IRQF; +	} + +	if (rtc_istat & RTC_ISTAT_STOPWATCH) { +		bfin_write_RTC_ISTAT(RTC_ISTAT_STOPWATCH); +		events |= RTC_PF | RTC_IRQF; +		bfin_write_RTC_SWCNT(rtc->rtc_dev->irq_freq); +	} + +	if (rtc_istat & RTC_ISTAT_SEC) { +		bfin_write_RTC_ISTAT(RTC_ISTAT_SEC); +		events |= RTC_UF | RTC_IRQF; +	} + +	rtc_update_irq(rtc->rtc_dev, 1, events); + +	spin_unlock_irq(&rtc->lock); + +	return IRQ_HANDLED; +} + +static int bfin_rtc_open(struct device *dev) +{ +	struct bfin_rtc *rtc = dev_get_drvdata(dev); +	int ret; + +	stampit(); + +	ret = request_irq(IRQ_RTC, bfin_rtc_interrupt, IRQF_DISABLED, "rtc-bfin", dev); +	if (unlikely(ret)) { +		dev_err(dev, "request RTC IRQ failed with %d\n", ret); +		return ret; +	} + +	rtc_bfin_reset(rtc); + +	return ret; +} + +static void bfin_rtc_release(struct device *dev) +{ +	struct bfin_rtc *rtc = dev_get_drvdata(dev); +	stampit(); +	rtc_bfin_reset(rtc); +	free_irq(IRQ_RTC, dev); +} + +static int bfin_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ +	struct bfin_rtc *rtc = dev_get_drvdata(dev); + +	stampit(); + +	switch (cmd) { +	case RTC_PIE_ON: +		stampit(); +		spin_lock_irq(&rtc->lock); +		rtc_bfin_sync_pending(); +		bfin_write_RTC_ISTAT(RTC_ISTAT_STOPWATCH); +		bfin_write_RTC_SWCNT(rtc->rtc_dev->irq_freq); +		bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() | RTC_ISTAT_STOPWATCH); +		spin_unlock_irq(&rtc->lock); +		return 0; +	case RTC_PIE_OFF: +		stampit(); +		spin_lock_irq(&rtc->lock); +		rtc_bfin_sync_pending(); +		bfin_write_RTC_SWCNT(0); +		bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() & ~RTC_ISTAT_STOPWATCH); +		spin_unlock_irq(&rtc->lock); +		return 0; + +	case RTC_UIE_ON: +		stampit(); +		spin_lock_irq(&rtc->lock); +		rtc_bfin_sync_pending(); +		bfin_write_RTC_ISTAT(RTC_ISTAT_SEC); +		bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() | RTC_ISTAT_SEC); +		spin_unlock_irq(&rtc->lock); +		return 0; +	case RTC_UIE_OFF: +		stampit(); +		spin_lock_irq(&rtc->lock); +		rtc_bfin_sync_pending(); +		bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() & ~RTC_ISTAT_SEC); +		spin_unlock_irq(&rtc->lock); +		return 0; + +	case RTC_AIE_ON: { +		unsigned long rtc_alarm; +		u16 which_alarm; +		int ret = 0; + +		stampit(); + +		spin_lock_irq(&rtc->lock); + +		rtc_bfin_sync_pending(); +		if (rtc->rtc_alarm.tm_yday == -1) { +			struct rtc_time now; +			rtc_bfin_to_tm(bfin_read_RTC_STAT(), &now); +			now.tm_sec = rtc->rtc_alarm.tm_sec; +			now.tm_min = rtc->rtc_alarm.tm_min; +			now.tm_hour = rtc->rtc_alarm.tm_hour; +			ret = rtc_tm_to_time(&now, &rtc_alarm); +			which_alarm = RTC_ISTAT_ALARM; +		} else { +			ret = rtc_tm_to_time(&rtc->rtc_alarm, &rtc_alarm); +			which_alarm = RTC_ISTAT_ALARM_DAY; +		} +		if (ret == 0) { +			bfin_write_RTC_ISTAT(which_alarm); +			bfin_write_RTC_ALARM(rtc_time_to_bfin(rtc_alarm)); +			bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() | which_alarm); +		} + +		spin_unlock_irq(&rtc->lock); + +		return ret; +	} +	case RTC_AIE_OFF: +		stampit(); +		spin_lock_irq(&rtc->lock); +		rtc_bfin_sync_pending(); +		bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() & ~(RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY)); +		spin_unlock_irq(&rtc->lock); +		return 0; +	} + +	return -ENOIOCTLCMD; +} + +static int bfin_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ +	struct bfin_rtc *rtc = dev_get_drvdata(dev); + +	stampit(); + +	spin_lock_irq(&rtc->lock); +	rtc_bfin_sync_pending(); +	rtc_bfin_to_tm(bfin_read_RTC_STAT(), tm); +	spin_unlock_irq(&rtc->lock); + +	return 0; +} + +static int bfin_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ +	struct bfin_rtc *rtc = dev_get_drvdata(dev); +	int ret; +	unsigned long now; + +	stampit(); + +	spin_lock_irq(&rtc->lock); + +	ret = rtc_tm_to_time(tm, &now); +	if (ret == 0) { +		rtc_bfin_sync_pending(); +		bfin_write_RTC_STAT(rtc_time_to_bfin(now)); +	} + +	spin_unlock_irq(&rtc->lock); + +	return ret; +} + +static int bfin_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ +	struct bfin_rtc *rtc = dev_get_drvdata(dev); +	stampit(); +	memcpy(&alrm->time, &rtc->rtc_alarm, sizeof(struct rtc_time)); +	alrm->pending = !!(bfin_read_RTC_ICTL() & (RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY)); +	return 0; +} + +static int bfin_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ +	struct bfin_rtc *rtc = dev_get_drvdata(dev); +	stampit(); +	memcpy(&rtc->rtc_alarm, &alrm->time, sizeof(struct rtc_time)); +	return 0; +} + +static int bfin_rtc_proc(struct device *dev, struct seq_file *seq) +{ +#define yesno(x) (x ? "yes" : "no") +	u16 ictl = bfin_read_RTC_ICTL(); +	stampit(); +	seq_printf(seq, "alarm_IRQ\t: %s\n", yesno(ictl & RTC_ISTAT_ALARM)); +	seq_printf(seq, "wkalarm_IRQ\t: %s\n", yesno(ictl & RTC_ISTAT_ALARM_DAY)); +	seq_printf(seq, "seconds_IRQ\t: %s\n", yesno(ictl & RTC_ISTAT_SEC)); +	seq_printf(seq, "periodic_IRQ\t: %s\n", yesno(ictl & RTC_ISTAT_STOPWATCH)); +#ifdef DEBUG +	seq_printf(seq, "RTC_STAT\t: 0x%08X\n", bfin_read_RTC_STAT()); +	seq_printf(seq, "RTC_ICTL\t: 0x%04X\n", bfin_read_RTC_ICTL()); +	seq_printf(seq, "RTC_ISTAT\t: 0x%04X\n", bfin_read_RTC_ISTAT()); +	seq_printf(seq, "RTC_SWCNT\t: 0x%04X\n", bfin_read_RTC_SWCNT()); +	seq_printf(seq, "RTC_ALARM\t: 0x%08X\n", bfin_read_RTC_ALARM()); +	seq_printf(seq, "RTC_PREN\t: 0x%04X\n", bfin_read_RTC_PREN()); +#endif +	return 0; +} + +static int bfin_irq_set_freq(struct device *dev, int freq) +{ +	struct bfin_rtc *rtc = dev_get_drvdata(dev); +	stampit(); +	rtc->rtc_dev->irq_freq = freq; +	return 0; +} + +static struct rtc_class_ops bfin_rtc_ops = { +	.open          = bfin_rtc_open, +	.release       = bfin_rtc_release, +	.ioctl         = bfin_rtc_ioctl, +	.read_time     = bfin_rtc_read_time, +	.set_time      = bfin_rtc_set_time, +	.read_alarm    = bfin_rtc_read_alarm, +	.set_alarm     = bfin_rtc_set_alarm, +	.proc          = bfin_rtc_proc, +	.irq_set_freq  = bfin_irq_set_freq, +}; + +static int __devinit bfin_rtc_probe(struct platform_device *pdev) +{ +	struct bfin_rtc *rtc; +	int ret = 0; + +	stampit(); + +	rtc = kzalloc(sizeof(*rtc), GFP_KERNEL); +	if (unlikely(!rtc)) +		return -ENOMEM; + +	spin_lock_init(&rtc->lock); + +	rtc->rtc_dev = rtc_device_register(pdev->name, &pdev->dev, &bfin_rtc_ops, THIS_MODULE); +	if (unlikely(IS_ERR(rtc))) { +		ret = PTR_ERR(rtc->rtc_dev); +		goto err; +	} +	rtc->rtc_dev->irq_freq = 0; +	rtc->rtc_dev->max_user_freq = (2 << 16); /* stopwatch is an unsigned 16 bit reg */ + +	platform_set_drvdata(pdev, rtc); + +	return 0; + +err: +	kfree(rtc); +	return ret; +} + +static int __devexit bfin_rtc_remove(struct platform_device *pdev) +{ +	struct bfin_rtc *rtc = platform_get_drvdata(pdev); + +	rtc_device_unregister(rtc->rtc_dev); +	platform_set_drvdata(pdev, NULL); +	kfree(rtc); + +	return 0; +} + +static struct platform_driver bfin_rtc_driver = { +	.driver		= { +		.name	= "rtc-bfin", +		.owner	= THIS_MODULE, +	}, +	.probe		= bfin_rtc_probe, +	.remove		= __devexit_p(bfin_rtc_remove), +}; + +static int __init bfin_rtc_init(void) +{ +	stampit(); +	return platform_driver_register(&bfin_rtc_driver); +} + +static void __exit bfin_rtc_exit(void) +{ +	platform_driver_unregister(&bfin_rtc_driver); +} + +module_init(bfin_rtc_init); +module_exit(bfin_rtc_exit); + +MODULE_DESCRIPTION("Blackfin On-Chip Real Time Clock Driver"); +MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index 7c0d6091007..6085261aa2c 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -46,6 +46,10 @@ struct cmos_rtc {  	int			irq;  	struct resource		*iomem; +	void			(*wake_on)(struct device *); +	void			(*wake_off)(struct device *); + +	u8			enabled_wake;  	u8			suspend_ctrl;  	/* newer hardware extends the original register set */ @@ -203,7 +207,7 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)  	rtc_intr = CMOS_READ(RTC_INTR_FLAGS);  	rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;  	if (is_intr(rtc_intr)) -		rtc_update_irq(&cmos->rtc->class_dev, 1, rtc_intr); +		rtc_update_irq(cmos->rtc, 1, rtc_intr);  	/* update alarm */  	CMOS_WRITE(hrs, RTC_HOURS_ALARM); @@ -223,7 +227,7 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)  		rtc_intr = CMOS_READ(RTC_INTR_FLAGS);  		rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;  		if (is_intr(rtc_intr)) -			rtc_update_irq(&cmos->rtc->class_dev, 1, rtc_intr); +			rtc_update_irq(cmos->rtc, 1, rtc_intr);  	}  	spin_unlock_irq(&rtc_lock); @@ -304,7 +308,7 @@ cmos_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)  	rtc_intr = CMOS_READ(RTC_INTR_FLAGS);  	rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;  	if (is_intr(rtc_intr)) -		rtc_update_irq(&cmos->rtc->class_dev, 1, rtc_intr); +		rtc_update_irq(cmos->rtc, 1, rtc_intr);  	spin_unlock_irqrestore(&rtc_lock, flags);  	return 0;  } @@ -379,12 +383,12 @@ static irqreturn_t cmos_interrupt(int irq, void *p)  		return IRQ_NONE;  } -#ifdef	CONFIG_PNPACPI -#define	is_pnpacpi()	1 +#ifdef	CONFIG_PNP +#define	is_pnp()	1  #define	INITSECTION  #else -#define	is_pnpacpi()	0 +#define	is_pnp()	0  #define	INITSECTION	__init  #endif @@ -405,13 +409,20 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)  	cmos_rtc.irq = rtc_irq;  	cmos_rtc.iomem = ports; -	/* For ACPI systems the info comes from the FADT.  On others, -	 * board specific setup provides it as appropriate. +	/* For ACPI systems extension info comes from the FADT.  On others, +	 * board specific setup provides it as appropriate.  Systems where +	 * the alarm IRQ isn't automatically a wakeup IRQ (like ACPI, and +	 * some almost-clones) can provide hooks to make that behave.  	 */  	if (info) {  		cmos_rtc.day_alrm = info->rtc_day_alarm;  		cmos_rtc.mon_alrm = info->rtc_mon_alarm;  		cmos_rtc.century = info->rtc_century; + +		if (info->wake_on && info->wake_off) { +			cmos_rtc.wake_on = info->wake_on; +			cmos_rtc.wake_off = info->wake_off; +		}  	}  	cmos_rtc.rtc = rtc_device_register(driver_name, dev, @@ -427,14 +438,14 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)  	 * REVISIT for non-x86 systems we may need to handle io memory  	 * resources: ioremap them, and request_mem_region().  	 */ -	if (is_pnpacpi()) { +	if (is_pnp()) {  		retval = request_resource(&ioport_resource, ports);  		if (retval < 0) {  			dev_dbg(dev, "i/o registers already in use\n");  			goto cleanup0;  		}  	} -	rename_region(ports, cmos_rtc.rtc->class_dev.class_id); +	rename_region(ports, cmos_rtc.rtc->dev.bus_id);  	spin_lock_irq(&rtc_lock); @@ -470,8 +481,8 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)  	if (is_valid_irq(rtc_irq))  		retval = request_irq(rtc_irq, cmos_interrupt, IRQF_DISABLED, -				cmos_rtc.rtc->class_dev.class_id, -				&cmos_rtc.rtc->class_dev); +				cmos_rtc.rtc->dev.bus_id, +				cmos_rtc.rtc);  	if (retval < 0) {  		dev_dbg(dev, "IRQ %d is already in use\n", rtc_irq);  		goto cleanup1; @@ -483,7 +494,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)  	 */  	pr_info("%s: alarms up to one %s%s\n", -			cmos_rtc.rtc->class_dev.class_id, +			cmos_rtc.rtc->dev.bus_id,  			is_valid_irq(rtc_irq)  				?  (cmos_rtc.mon_alrm  					? "year" @@ -520,12 +531,12 @@ static void __exit cmos_do_remove(struct device *dev)  	cmos_do_shutdown(); -	if (is_pnpacpi()) +	if (is_pnp())  		release_resource(cmos->iomem);  	rename_region(cmos->iomem, NULL);  	if (is_valid_irq(cmos->irq)) -		free_irq(cmos->irq, &cmos_rtc.rtc->class_dev); +		free_irq(cmos->irq, cmos_rtc.rtc);  	rtc_device_unregister(cmos_rtc.rtc); @@ -555,16 +566,20 @@ static int cmos_suspend(struct device *dev, pm_message_t mesg)  		irqstat = CMOS_READ(RTC_INTR_FLAGS);  		irqstat &= (tmp & RTC_IRQMASK) | RTC_IRQF;  		if (is_intr(irqstat)) -			rtc_update_irq(&cmos->rtc->class_dev, 1, irqstat); +			rtc_update_irq(cmos->rtc, 1, irqstat);  	}  	spin_unlock_irq(&rtc_lock); -	/* ACPI HOOK:  enable ACPI_EVENT_RTC when (tmp & RTC_AIE) -	 * ... it'd be best if we could do that under rtc_lock. -	 */ +	if (tmp & RTC_AIE) { +		cmos->enabled_wake = 1; +		if (cmos->wake_on) +			cmos->wake_on(dev); +		else +			enable_irq_wake(cmos->irq); +	}  	pr_debug("%s: suspend%s, ctrl %02x\n", -			cmos_rtc.rtc->class_dev.class_id, +			cmos_rtc.rtc->dev.bus_id,  			(tmp & RTC_AIE) ? ", alarm may wake" : "",  			tmp); @@ -576,26 +591,28 @@ static int cmos_resume(struct device *dev)  	struct cmos_rtc	*cmos = dev_get_drvdata(dev);  	unsigned char	tmp = cmos->suspend_ctrl; -	/* REVISIT:  a mechanism to resync the system clock (jiffies) -	 * on resume should be portable between platforms ... -	 */ -  	/* re-enable any irqs previously active */  	if (tmp & (RTC_PIE|RTC_AIE|RTC_UIE)) { -		/* ACPI HOOK:  disable ACPI_EVENT_RTC when (tmp & RTC_AIE) */ +		if (cmos->enabled_wake) { +			if (cmos->wake_off) +				cmos->wake_off(dev); +			else +				disable_irq_wake(cmos->irq); +			cmos->enabled_wake = 0; +		}  		spin_lock_irq(&rtc_lock);  		CMOS_WRITE(tmp, RTC_CONTROL);  		tmp = CMOS_READ(RTC_INTR_FLAGS);  		tmp &= (cmos->suspend_ctrl & RTC_IRQMASK) | RTC_IRQF;  		if (is_intr(tmp)) -			rtc_update_irq(&cmos->rtc->class_dev, 1, tmp); +			rtc_update_irq(cmos->rtc, 1, tmp);  		spin_unlock_irq(&rtc_lock);  	}  	pr_debug("%s: resume, ctrl %02x\n", -			cmos_rtc.rtc->class_dev.class_id, +			cmos_rtc.rtc->dev.bus_id,  			cmos->suspend_ctrl); @@ -613,7 +630,7 @@ static int cmos_resume(struct device *dev)   * the device node will always be created as a PNPACPI device.   */ -#ifdef	CONFIG_PNPACPI +#ifdef	CONFIG_PNP  #include <linux/pnp.h> @@ -684,11 +701,11 @@ static void __exit cmos_exit(void)  }  module_exit(cmos_exit); -#else	/* no PNPACPI */ +#else	/* no PNP */  /*----------------------------------------------------------------*/ -/* Platform setup should have set up an RTC device, when PNPACPI is +/* Platform setup should have set up an RTC device, when PNP is   * unavailable ... this could happen even on (older) PCs.   */ @@ -734,7 +751,7 @@ static void __exit cmos_exit(void)  module_exit(cmos_exit); -#endif	/* !PNPACPI */ +#endif	/* !PNP */  MODULE_AUTHOR("David Brownell");  MODULE_DESCRIPTION("Driver for PC-style 'CMOS' RTCs"); diff --git a/drivers/rtc/rtc-core.h b/drivers/rtc/rtc-core.h new file mode 100644 index 00000000000..5f9df7430a2 --- /dev/null +++ b/drivers/rtc/rtc-core.h @@ -0,0 +1,70 @@ +#ifdef CONFIG_RTC_INTF_DEV + +extern void __init rtc_dev_init(void); +extern void __exit rtc_dev_exit(void); +extern void rtc_dev_prepare(struct rtc_device *rtc); +extern void rtc_dev_add_device(struct rtc_device *rtc); +extern void rtc_dev_del_device(struct rtc_device *rtc); + +#else + +static inline void rtc_dev_init(void) +{ +} + +static inline void rtc_dev_exit(void) +{ +} + +static inline void rtc_dev_prepare(struct rtc_device *rtc) +{ +} + +static inline void rtc_dev_add_device(struct rtc_device *rtc) +{ +} + +static inline void rtc_dev_del_device(struct rtc_device *rtc) +{ +} + +#endif + +#ifdef CONFIG_RTC_INTF_PROC + +extern void rtc_proc_add_device(struct rtc_device *rtc); +extern void rtc_proc_del_device(struct rtc_device *rtc); + +#else + +static inline void rtc_proc_add_device(struct rtc_device *rtc) +{ +} + +static inline void rtc_proc_del_device(struct rtc_device *rtc) +{ +} + +#endif + +#ifdef CONFIG_RTC_INTF_SYSFS + +extern void __init rtc_sysfs_init(struct class *); +extern void rtc_sysfs_add_device(struct rtc_device *rtc); +extern void rtc_sysfs_del_device(struct rtc_device *rtc); + +#else + +static inline void rtc_sysfs_init(struct class *rtc) +{ +} + +static inline void rtc_sysfs_add_device(struct rtc_device *rtc) +{ +} + +static inline void rtc_sysfs_del_device(struct rtc_device *rtc) +{ +} + +#endif diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c index 137330b8636..f4e5f0040ff 100644 --- a/drivers/rtc/rtc-dev.c +++ b/drivers/rtc/rtc-dev.c @@ -13,8 +13,8 @@  #include <linux/module.h>  #include <linux/rtc.h> +#include "rtc-core.h" -static struct class *rtc_dev_class;  static dev_t rtc_devt;  #define RTC_DEV_MAX 16 /* 16 RTCs should be enough for everyone... */ @@ -32,9 +32,9 @@ static int rtc_dev_open(struct inode *inode, struct file *file)  	if (!(mutex_trylock(&rtc->char_lock)))  		return -EBUSY; -	file->private_data = &rtc->class_dev; +	file->private_data = rtc; -	err = ops->open ? ops->open(rtc->class_dev.dev) : 0; +	err = ops->open ? ops->open(rtc->dev.parent) : 0;  	if (err == 0) {  		spin_lock_irq(&rtc->irq_lock);  		rtc->irq_data = 0; @@ -61,7 +61,7 @@ static void rtc_uie_task(struct work_struct *work)  	int num = 0;  	int err; -	err = rtc_read_time(&rtc->class_dev, &tm); +	err = rtc_read_time(rtc, &tm);  	local_irq_disable();  	spin_lock(&rtc->irq_lock); @@ -79,7 +79,7 @@ static void rtc_uie_task(struct work_struct *work)  	}  	spin_unlock(&rtc->irq_lock);  	if (num) -		rtc_update_irq(&rtc->class_dev, num, RTC_UF | RTC_IRQF); +		rtc_update_irq(rtc, num, RTC_UF | RTC_IRQF);  	local_irq_enable();  }  static void rtc_uie_timer(unsigned long data) @@ -121,7 +121,7 @@ static int set_uie(struct rtc_device *rtc)  	struct rtc_time tm;  	int err; -	err = rtc_read_time(&rtc->class_dev, &tm); +	err = rtc_read_time(rtc, &tm);  	if (err)  		return err;  	spin_lock_irq(&rtc->irq_lock); @@ -180,7 +180,7 @@ rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)  	if (ret == 0) {  		/* Check for any data updates */  		if (rtc->ops->read_callback) -			data = rtc->ops->read_callback(rtc->class_dev.dev, +			data = rtc->ops->read_callback(rtc->dev.parent,  						       data);  		if (sizeof(int) != sizeof(long) && @@ -210,8 +210,7 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,  		unsigned int cmd, unsigned long arg)  {  	int err = 0; -	struct class_device *class_dev = file->private_data; -	struct rtc_device *rtc = to_rtc_device(class_dev); +	struct rtc_device *rtc = file->private_data;  	const struct rtc_class_ops *ops = rtc->ops;  	struct rtc_time tm;  	struct rtc_wkalrm alarm; @@ -252,7 +251,7 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,  	/* try the driver's ioctl interface */  	if (ops->ioctl) { -		err = ops->ioctl(class_dev->dev, cmd, arg); +		err = ops->ioctl(rtc->dev.parent, cmd, arg);  		if (err != -ENOIOCTLCMD)  			return err;  	} @@ -264,7 +263,7 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,  	switch (cmd) {  	case RTC_ALM_READ: -		err = rtc_read_alarm(class_dev, &alarm); +		err = rtc_read_alarm(rtc, &alarm);  		if (err < 0)  			return err; @@ -278,17 +277,53 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,  		alarm.enabled = 0;  		alarm.pending = 0; -		alarm.time.tm_mday = -1; -		alarm.time.tm_mon = -1; -		alarm.time.tm_year = -1;  		alarm.time.tm_wday = -1;  		alarm.time.tm_yday = -1;  		alarm.time.tm_isdst = -1; -		err = rtc_set_alarm(class_dev, &alarm); + +		/* RTC_ALM_SET alarms may be up to 24 hours in the future. +		 * Rather than expecting every RTC to implement "don't care" +		 * for day/month/year fields, just force the alarm to have +		 * the right values for those fields. +		 * +		 * RTC_WKALM_SET should be used instead.  Not only does it +		 * eliminate the need for a separate RTC_AIE_ON call, it +		 * doesn't have the "alarm 23:59:59 in the future" race. +		 * +		 * NOTE:  some legacy code may have used invalid fields as +		 * wildcards, exposing hardware "periodic alarm" capabilities. +		 * Not supported here. +		 */ +		{ +			unsigned long now, then; + +			err = rtc_read_time(rtc, &tm); +			if (err < 0) +				return err; +			rtc_tm_to_time(&tm, &now); + +			alarm.time.tm_mday = tm.tm_mday; +			alarm.time.tm_mon = tm.tm_mon; +			alarm.time.tm_year = tm.tm_year; +			err  = rtc_valid_tm(&alarm.time); +			if (err < 0) +				return err; +			rtc_tm_to_time(&alarm.time, &then); + +			/* alarm may need to wrap into tomorrow */ +			if (then < now) { +				rtc_time_to_tm(now + 24 * 60 * 60, &tm); +				alarm.time.tm_mday = tm.tm_mday; +				alarm.time.tm_mon = tm.tm_mon; +				alarm.time.tm_year = tm.tm_year; +			} +		} + +		err = rtc_set_alarm(rtc, &alarm);  		break;  	case RTC_RD_TIME: -		err = rtc_read_time(class_dev, &tm); +		err = rtc_read_time(rtc, &tm);  		if (err < 0)  			return err; @@ -300,7 +335,7 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,  		if (copy_from_user(&tm, uarg, sizeof(tm)))  			return -EFAULT; -		err = rtc_set_time(class_dev, &tm); +		err = rtc_set_time(rtc, &tm);  		break;  	case RTC_IRQP_READ: @@ -310,7 +345,7 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,  	case RTC_IRQP_SET:  		if (ops->irq_set_freq) -			err = rtc_irq_set_freq(class_dev, rtc->irq_task, arg); +			err = rtc_irq_set_freq(rtc, rtc->irq_task, arg);  		break;  #if 0 @@ -336,11 +371,11 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,  		if (copy_from_user(&alarm, uarg, sizeof(alarm)))  			return -EFAULT; -		err = rtc_set_alarm(class_dev, &alarm); +		err = rtc_set_alarm(rtc, &alarm);  		break;  	case RTC_WKALM_RD: -		err = rtc_read_alarm(class_dev, &alarm); +		err = rtc_read_alarm(rtc, &alarm);  		if (err < 0)  			return err; @@ -372,7 +407,7 @@ static int rtc_dev_release(struct inode *inode, struct file *file)  	clear_uie(rtc);  #endif  	if (rtc->ops->release) -		rtc->ops->release(rtc->class_dev.dev); +		rtc->ops->release(rtc->dev.parent);  	mutex_unlock(&rtc->char_lock);  	return 0; @@ -397,17 +432,18 @@ static const struct file_operations rtc_dev_fops = {  /* insertion/removal hooks */ -static int rtc_dev_add_device(struct class_device *class_dev, -				struct class_interface *class_intf) +void rtc_dev_prepare(struct rtc_device *rtc)  { -	int err = 0; -	struct rtc_device *rtc = to_rtc_device(class_dev); +	if (!rtc_devt) +		return;  	if (rtc->id >= RTC_DEV_MAX) { -		dev_err(class_dev->dev, "too many RTCs\n"); -		return -EINVAL; +		pr_debug("%s: too many RTC devices\n", rtc->name); +		return;  	} +	rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id); +  	mutex_init(&rtc->char_lock);  	spin_lock_init(&rtc->irq_lock);  	init_waitqueue_head(&rtc->irq_queue); @@ -418,100 +454,36 @@ static int rtc_dev_add_device(struct class_device *class_dev,  	cdev_init(&rtc->char_dev, &rtc_dev_fops);  	rtc->char_dev.owner = rtc->owner; +} -	if (cdev_add(&rtc->char_dev, MKDEV(MAJOR(rtc_devt), rtc->id), 1)) { -		dev_err(class_dev->dev, -			"failed to add char device %d:%d\n", +void rtc_dev_add_device(struct rtc_device *rtc) +{ +	if (cdev_add(&rtc->char_dev, rtc->dev.devt, 1)) +		printk(KERN_WARNING "%s: failed to add char device %d:%d\n", +			rtc->name, MAJOR(rtc_devt), rtc->id); +	else +		pr_debug("%s: dev (%d:%d)\n", rtc->name,  			MAJOR(rtc_devt), rtc->id); -		return -ENODEV; -	} - -	rtc->rtc_dev = class_device_create(rtc_dev_class, NULL, -						MKDEV(MAJOR(rtc_devt), rtc->id), -						class_dev->dev, "rtc%d", rtc->id); -	if (IS_ERR(rtc->rtc_dev)) { -		dev_err(class_dev->dev, "cannot create rtc_dev device\n"); -		err = PTR_ERR(rtc->rtc_dev); -		goto err_cdev_del; -	} - -	dev_dbg(class_dev->dev, "rtc intf: dev (%d:%d)\n", -		MAJOR(rtc->rtc_dev->devt), -		MINOR(rtc->rtc_dev->devt)); - -	return 0; - -err_cdev_del: - -	cdev_del(&rtc->char_dev); -	return err;  } -static void rtc_dev_remove_device(struct class_device *class_dev, -					struct class_interface *class_intf) +void rtc_dev_del_device(struct rtc_device *rtc)  { -	struct rtc_device *rtc = to_rtc_device(class_dev); - -	if (rtc->rtc_dev) { -		dev_dbg(class_dev->dev, "removing char %d:%d\n", -			MAJOR(rtc->rtc_dev->devt), -			MINOR(rtc->rtc_dev->devt)); - -		class_device_unregister(rtc->rtc_dev); +	if (rtc->dev.devt)  		cdev_del(&rtc->char_dev); -	}  } -/* interface registration */ - -static struct class_interface rtc_dev_interface = { -	.add = &rtc_dev_add_device, -	.remove = &rtc_dev_remove_device, -}; - -static int __init rtc_dev_init(void) +void __init rtc_dev_init(void)  {  	int err; -	rtc_dev_class = class_create(THIS_MODULE, "rtc-dev"); -	if (IS_ERR(rtc_dev_class)) -		return PTR_ERR(rtc_dev_class); -  	err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc"); -	if (err < 0) { +	if (err < 0)  		printk(KERN_ERR "%s: failed to allocate char dev region\n",  			__FILE__); -		goto err_destroy_class; -	} - -	err = rtc_interface_register(&rtc_dev_interface); -	if (err < 0) { -		printk(KERN_ERR "%s: failed to register the interface\n", -			__FILE__); -		goto err_unregister_chrdev; -	} - -	return 0; - -err_unregister_chrdev: -	unregister_chrdev_region(rtc_devt, RTC_DEV_MAX); - -err_destroy_class: -	class_destroy(rtc_dev_class); - -	return err;  } -static void __exit rtc_dev_exit(void) +void __exit rtc_dev_exit(void)  { -	class_interface_unregister(&rtc_dev_interface); -	class_destroy(rtc_dev_class); -	unregister_chrdev_region(rtc_devt, RTC_DEV_MAX); +	if (rtc_devt) +		unregister_chrdev_region(rtc_devt, RTC_DEV_MAX);  } - -subsys_initcall(rtc_dev_init); -module_exit(rtc_dev_exit); - -MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); -MODULE_DESCRIPTION("RTC class dev interface"); -MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c index e27176c0e18..afa64c7fa2e 100644 --- a/drivers/rtc/rtc-ds1553.c +++ b/drivers/rtc/rtc-ds1553.c @@ -203,7 +203,7 @@ static irqreturn_t ds1553_rtc_interrupt(int irq, void *dev_id)  		events |= RTC_UF;  	else  		events |= RTC_AF; -	rtc_update_irq(&pdata->rtc->class_dev, 1, events); +	rtc_update_irq(pdata->rtc, 1, events);  	return IRQ_HANDLED;  } diff --git a/drivers/rtc/rtc-lib.c b/drivers/rtc/rtc-lib.c index 7bbc26a34bd..ba795a4db1e 100644 --- a/drivers/rtc/rtc-lib.c +++ b/drivers/rtc/rtc-lib.c @@ -117,85 +117,4 @@ int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time)  }  EXPORT_SYMBOL(rtc_tm_to_time); - -/* Merge the valid (i.e. non-negative) fields of alarm into the current - * time.  If the valid alarm fields are earlier than the equivalent - * fields in the time, carry one into the least significant invalid - * field, so that the alarm expiry is in the future.  It assumes that the - * least significant invalid field is more significant than the most - * significant valid field, and that the seconds field is valid. - * - * This is used by alarms that take relative (rather than absolute) - * times, and/or have a simple binary second counter instead of - * day/hour/minute/sec registers. - */ -void rtc_merge_alarm(struct rtc_time *now, struct rtc_time *alarm) -{ -	int *alarmp = &alarm->tm_sec; -	int *timep = &now->tm_sec; -	int carry_into, i; - -	/* Ignore everything past the 6th element (tm_year). */ -	for (i = 5; i > 0; i--) { -		if (alarmp[i] < 0) -			alarmp[i] = timep[i]; -		else -			break; -	} - -	/* No carry needed if all fields are valid. */ -	if (i == 5) -		return; - -	for (carry_into = i + 1; i >= 0; i--) { -		if (alarmp[i] < timep[i]) -			break; - -		if (alarmp[i] > timep[i]) -			return; -	} - -	switch (carry_into) { -		case 1: -			alarm->tm_min++; - -			if (alarm->tm_min < 60) -				return; - -			alarm->tm_min = 0; -			/* fall-through */ - -		case 2: -			alarm->tm_hour++; - -			if (alarm->tm_hour < 60) -				return; - -			alarm->tm_hour = 0; -			/* fall-through */ - -		case 3: -			alarm->tm_mday++; - -			if (alarm->tm_mday <= rtc_days_in_month[alarm->tm_mon]) -				return; - -			alarm->tm_mday = 1; -			/* fall-through */ - -		case 4: -			alarm->tm_mon++; - -			if (alarm->tm_mon <= 12) -				return; - -			alarm->tm_mon = 1; -			/* fall-through */ - -		case 5: -			alarm->tm_year++; -	} -} -EXPORT_SYMBOL(rtc_merge_alarm); -  MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-max6900.c b/drivers/rtc/rtc-max6900.c new file mode 100644 index 00000000000..eee4ee5bb75 --- /dev/null +++ b/drivers/rtc/rtc-max6900.c @@ -0,0 +1,311 @@ +/* + * rtc class driver for the Maxim MAX6900 chip + * + * Author: Dale Farnsworth <dale@farnsworth.org> + * + * based on previously existing rtc class drivers + * + * 2007 (c) MontaVista, Software, Inc.  This file is licensed under + * the terms of the GNU General Public License version 2.  This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/bcd.h> +#include <linux/rtc.h> +#include <linux/delay.h> + +#define DRV_NAME "max6900" +#define DRV_VERSION "0.1" + +/* + * register indices + */ +#define MAX6900_REG_SC			0	/* seconds	00-59 */ +#define MAX6900_REG_MN			1	/* minutes	00-59 */ +#define MAX6900_REG_HR			2	/* hours	00-23 */ +#define MAX6900_REG_DT			3	/* day of month	00-31 */ +#define MAX6900_REG_MO			4	/* month	01-12 */ +#define MAX6900_REG_DW			5	/* day of week	 1-7  */ +#define MAX6900_REG_YR			6	/* year		00-99 */ +#define MAX6900_REG_CT			7	/* control */ +#define MAX6900_REG_LEN			8 + +#define MAX6900_REG_CT_WP		(1 << 7)	/* Write Protect */ + +/* + * register read/write commands + */ +#define MAX6900_REG_CONTROL_WRITE	0x8e +#define MAX6900_REG_BURST_READ		0xbf +#define MAX6900_REG_BURST_WRITE		0xbe +#define MAX6900_REG_RESERVED_READ	0x96 + +#define MAX6900_IDLE_TIME_AFTER_WRITE	3	/* specification says 2.5 mS */ + +#define MAX6900_I2C_ADDR		0xa0 + +static unsigned short normal_i2c[] = { +	MAX6900_I2C_ADDR >> 1, +	I2C_CLIENT_END +}; + +I2C_CLIENT_INSMOD;			/* defines addr_data */ + +static int max6900_probe(struct i2c_adapter *adapter, int addr, int kind); + +static int max6900_i2c_read_regs(struct i2c_client *client, u8 *buf) +{ +	u8 reg_addr[1] = { MAX6900_REG_BURST_READ }; +	struct i2c_msg msgs[2] = { +		{ +			.addr	= client->addr, +			.flags	= 0, /* write */ +			.len	= sizeof(reg_addr), +			.buf	= reg_addr +		}, +		{ +			.addr	= client->addr, +			.flags	= I2C_M_RD, +			.len	= MAX6900_REG_LEN, +			.buf	= buf +		} +	}; +	int rc; + +	rc = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); +	if (rc != ARRAY_SIZE(msgs)) { +		dev_err(&client->dev, "%s: register read failed\n", +			__FUNCTION__); +		return -EIO; +	} +	return 0; +} + +static int max6900_i2c_write_regs(struct i2c_client *client, u8 const *buf) +{ +	u8 i2c_buf[MAX6900_REG_LEN + 1] = { MAX6900_REG_BURST_WRITE }; +	struct i2c_msg msgs[1] = { +		{ +			.addr	= client->addr, +			.flags	= 0, /* write */ +			.len	= MAX6900_REG_LEN + 1, +			.buf	= i2c_buf +		} +	}; +	int rc; + +	memcpy(&i2c_buf[1], buf, MAX6900_REG_LEN); + +	rc = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); +	if (rc != ARRAY_SIZE(msgs)) { +		dev_err(&client->dev, "%s: register write failed\n", +			__FUNCTION__); +		return -EIO; +	} +	msleep(MAX6900_IDLE_TIME_AFTER_WRITE); +	return 0; +} + +static int max6900_i2c_validate_client(struct i2c_client *client) +{ +	u8 regs[MAX6900_REG_LEN]; +	u8 zero_mask[MAX6900_REG_LEN] = { +		0x80,	/* seconds */ +		0x80,	/* minutes */ +		0x40,	/* hours */ +		0xc0,	/* day of month */ +		0xe0,	/* month */ +		0xf8,	/* day of week */ +		0x00,	/* year */ +		0x7f,	/* control */ +	}; +	int i; +	int rc; +	int reserved; + +	reserved = i2c_smbus_read_byte_data(client, MAX6900_REG_RESERVED_READ); +	if (reserved != 0x07) +		return -ENODEV; + +	rc = max6900_i2c_read_regs(client, regs); +	if (rc < 0) +		return rc; + +	for (i = 0; i < MAX6900_REG_LEN; ++i) { +		if (regs[i] & zero_mask[i]) +			return -ENODEV; +	} + +	return 0; +} + +static int max6900_i2c_read_time(struct i2c_client *client, struct rtc_time *tm) +{ +	int rc; +	u8 regs[MAX6900_REG_LEN]; + +	rc = max6900_i2c_read_regs(client, regs); +	if (rc < 0) +		return rc; + +	tm->tm_sec = BCD2BIN(regs[MAX6900_REG_SC]); +	tm->tm_min = BCD2BIN(regs[MAX6900_REG_MN]); +	tm->tm_hour = BCD2BIN(regs[MAX6900_REG_HR] & 0x3f); +	tm->tm_mday = BCD2BIN(regs[MAX6900_REG_DT]); +	tm->tm_mon = BCD2BIN(regs[MAX6900_REG_MO]) - 1; +	tm->tm_year = BCD2BIN(regs[MAX6900_REG_YR]) + 100; +	tm->tm_wday = BCD2BIN(regs[MAX6900_REG_DW]); + +	return 0; +} + +static int max6900_i2c_clear_write_protect(struct i2c_client *client) +{ +	int rc; +	rc = i2c_smbus_write_byte_data (client, MAX6900_REG_CONTROL_WRITE, 0); +	if (rc < 0) { +		dev_err(&client->dev, "%s: control register write failed\n", +			__FUNCTION__); +		return -EIO; +	} +	return 0; +} + +static int max6900_i2c_set_time(struct i2c_client *client, +				struct rtc_time const *tm) +{ +	u8 regs[MAX6900_REG_LEN]; +	int rc; + +	rc = max6900_i2c_clear_write_protect(client); +	if (rc < 0) +		return rc; + +	regs[MAX6900_REG_SC] = BIN2BCD(tm->tm_sec); +	regs[MAX6900_REG_MN] = BIN2BCD(tm->tm_min); +	regs[MAX6900_REG_HR] = BIN2BCD(tm->tm_hour); +	regs[MAX6900_REG_DT] = BIN2BCD(tm->tm_mday); +	regs[MAX6900_REG_MO] = BIN2BCD(tm->tm_mon + 1); +	regs[MAX6900_REG_YR] = BIN2BCD(tm->tm_year - 100); +	regs[MAX6900_REG_DW] = BIN2BCD(tm->tm_wday); +	regs[MAX6900_REG_CT] = MAX6900_REG_CT_WP;	/* set write protect */ + +	rc = max6900_i2c_write_regs(client, regs); +	if (rc < 0) +		return rc; + +	return 0; +} + +static int max6900_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ +	return max6900_i2c_read_time(to_i2c_client(dev), tm); +} + +static int max6900_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ +	return max6900_i2c_set_time(to_i2c_client(dev), tm); +} + +static int max6900_attach_adapter(struct i2c_adapter *adapter) +{ +	return i2c_probe(adapter, &addr_data, max6900_probe); +} + +static int max6900_detach_client(struct i2c_client *client) +{ +	struct rtc_device *const rtc = i2c_get_clientdata(client); + +	if (rtc) +		rtc_device_unregister(rtc); + +	return i2c_detach_client(client); +} + +static struct i2c_driver max6900_driver = { +	.driver		= { +		.name	= DRV_NAME, +	}, +	.id		= I2C_DRIVERID_MAX6900, +	.attach_adapter = max6900_attach_adapter, +	.detach_client	= max6900_detach_client, +}; + +static const struct rtc_class_ops max6900_rtc_ops = { +	.read_time	= max6900_rtc_read_time, +	.set_time	= max6900_rtc_set_time, +}; + +static int max6900_probe(struct i2c_adapter *adapter, int addr, int kind) +{ +	int rc = 0; +	struct i2c_client *client = NULL; +	struct rtc_device *rtc = NULL; + +	if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { +		rc = -ENODEV; +		goto failout; +	} + +	client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); +	if (client == NULL) { +		rc = -ENOMEM; +		goto failout; +	} + +	client->addr = addr; +	client->adapter = adapter; +	client->driver = &max6900_driver; +	strlcpy(client->name, DRV_NAME, I2C_NAME_SIZE); + +	if (kind < 0) { +		rc = max6900_i2c_validate_client(client); +		if (rc < 0) +			goto failout; +	} + +	rc = i2c_attach_client(client); +	if (rc < 0) +		goto failout; + +	dev_info(&client->dev, +		 "chip found, driver version " DRV_VERSION "\n"); + +	rtc = rtc_device_register(max6900_driver.driver.name, +				  &client->dev, +				  &max6900_rtc_ops, THIS_MODULE); +	if (IS_ERR(rtc)) { +		rc = PTR_ERR(rtc); +		goto failout_detach; +	} + +	i2c_set_clientdata(client, rtc); + +	return 0; + +failout_detach: +	i2c_detach_client(client); +failout: +	kfree(client); +	return rc; +} + +static int __init max6900_init(void) +{ +	return i2c_add_driver(&max6900_driver); +} + +static void __exit max6900_exit(void) +{ +	i2c_del_driver(&max6900_driver); +} + +MODULE_DESCRIPTION("Maxim MAX6900 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +module_init(max6900_init); +module_exit(max6900_exit); diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c index 9de8d67f4f8..60a8a4bb8bd 100644 --- a/drivers/rtc/rtc-omap.c +++ b/drivers/rtc/rtc-omap.c @@ -124,7 +124,7 @@ static void rtc_wait_not_busy(void)  	/* now we have ~15 usec to read/write various registers */  } -static irqreturn_t rtc_irq(int irq, void *class_dev) +static irqreturn_t rtc_irq(int irq, void *rtc)  {  	unsigned long		events = 0;  	u8			irq_data; @@ -141,7 +141,7 @@ static irqreturn_t rtc_irq(int irq, void *class_dev)  	if (irq_data & OMAP_RTC_STATUS_1S_EVENT)  		events |= RTC_IRQF | RTC_UF; -	rtc_update_irq(class_dev, 1, events); +	rtc_update_irq(rtc, 1, events);  	return IRQ_HANDLED;  } @@ -289,34 +289,6 @@ static int omap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)  {  	u8 reg; -	/* Much userspace code uses RTC_ALM_SET, thus "don't care" for -	 * day/month/year specifies alarms up to 24 hours in the future. -	 * So we need to handle that ... but let's ignore the "don't care" -	 * values for hours/minutes/seconds. -	 */ -	if (alm->time.tm_mday <= 0 -			&& alm->time.tm_mon < 0 -			&& alm->time.tm_year < 0) { -		struct rtc_time tm; -		unsigned long now, then; - -		omap_rtc_read_time(dev, &tm); -		rtc_tm_to_time(&tm, &now); - -		alm->time.tm_mday = tm.tm_mday; -		alm->time.tm_mon = tm.tm_mon; -		alm->time.tm_year = tm.tm_year; -		rtc_tm_to_time(&alm->time, &then); - -		/* sometimes the alarm wraps into tomorrow */ -		if (then < now) { -			rtc_time_to_tm(now + 24 * 60 * 60, &tm); -			alm->time.tm_mday = tm.tm_mday; -			alm->time.tm_mon = tm.tm_mon; -			alm->time.tm_year = tm.tm_year; -		} -	} -  	if (tm2bcd(&alm->time) < 0)  		return -EINVAL; @@ -399,7 +371,7 @@ static int __devinit omap_rtc_probe(struct platform_device *pdev)  		goto fail;  	}  	platform_set_drvdata(pdev, rtc); -	class_set_devdata(&rtc->class_dev, mem); +	dev_set_devdata(&rtc->dev, mem);  	/* clear pending irqs, and set 1/second periodic,  	 * which we'll use instead of update irqs @@ -418,13 +390,13 @@ static int __devinit omap_rtc_probe(struct platform_device *pdev)  	/* handle periodic and alarm irqs */  	if (request_irq(omap_rtc_timer, rtc_irq, IRQF_DISABLED, -			rtc->class_dev.class_id, &rtc->class_dev)) { +			rtc->dev.bus_id, rtc)) {  		pr_debug("%s: RTC timer interrupt IRQ%d already claimed\n",  			pdev->name, omap_rtc_timer);  		goto fail0;  	}  	if (request_irq(omap_rtc_alarm, rtc_irq, IRQF_DISABLED, -			rtc->class_dev.class_id, &rtc->class_dev)) { +			rtc->dev.bus_id, rtc)) {  		pr_debug("%s: RTC alarm interrupt IRQ%d already claimed\n",  			pdev->name, omap_rtc_alarm);  		goto fail1; @@ -481,26 +453,17 @@ static int __devexit omap_rtc_remove(struct platform_device *pdev)  	free_irq(omap_rtc_timer, rtc);  	free_irq(omap_rtc_alarm, rtc); -	release_resource(class_get_devdata(&rtc->class_dev)); +	release_resource(dev_get_devdata(&rtc->dev));  	rtc_device_unregister(rtc);  	return 0;  }  #ifdef CONFIG_PM -static struct timespec rtc_delta;  static u8 irqstat;  static int omap_rtc_suspend(struct platform_device *pdev, pm_message_t state)  { -	struct rtc_time rtc_tm; -	struct timespec time; - -	time.tv_nsec = 0; -	omap_rtc_read_time(NULL, &rtc_tm); -	rtc_tm_to_time(&rtc_tm, &time.tv_sec); - -	save_time_delta(&rtc_delta, &time);  	irqstat = rtc_read(OMAP_RTC_INTERRUPTS_REG);  	/* FIXME the RTC alarm is not currently acting as a wakeup event @@ -517,14 +480,6 @@ static int omap_rtc_suspend(struct platform_device *pdev, pm_message_t state)  static int omap_rtc_resume(struct platform_device *pdev)  { -	struct rtc_time rtc_tm; -	struct timespec time; - -	time.tv_nsec = 0; -	omap_rtc_read_time(NULL, &rtc_tm); -	rtc_tm_to_time(&rtc_tm, &time.tv_sec); - -	restore_time_delta(&rtc_delta, &time);  	if (device_may_wakeup(&pdev->dev))  		disable_irq_wake(omap_rtc_alarm);  	else diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c index f13daa9feca..e4bf68ca96f 100644 --- a/drivers/rtc/rtc-pl031.c +++ b/drivers/rtc/rtc-pl031.c @@ -51,7 +51,7 @@ static irqreturn_t pl031_interrupt(int irq, void *dev_id)  {  	struct rtc_device *rtc = dev_id; -	rtc_update_irq(&rtc->class_dev, 1, RTC_AF); +	rtc_update_irq(rtc, 1, RTC_AF);  	return IRQ_HANDLED;  } diff --git a/drivers/rtc/rtc-proc.c b/drivers/rtc/rtc-proc.c index 1bd624fc685..8d300e6d0d9 100644 --- a/drivers/rtc/rtc-proc.c +++ b/drivers/rtc/rtc-proc.c @@ -16,18 +16,18 @@  #include <linux/proc_fs.h>  #include <linux/seq_file.h> -static struct class_device *rtc_dev = NULL; -static DEFINE_MUTEX(rtc_lock); +#include "rtc-core.h" +  static int rtc_proc_show(struct seq_file *seq, void *offset)  {  	int err; -	struct class_device *class_dev = seq->private; -	const struct rtc_class_ops *ops = to_rtc_device(class_dev)->ops; +	struct rtc_device *rtc = seq->private; +	const struct rtc_class_ops *ops = rtc->ops;  	struct rtc_wkalrm alrm;  	struct rtc_time tm; -	err = rtc_read_time(class_dev, &tm); +	err = rtc_read_time(rtc, &tm);  	if (err == 0) {  		seq_printf(seq,  			"rtc_time\t: %02d:%02d:%02d\n" @@ -36,7 +36,7 @@ static int rtc_proc_show(struct seq_file *seq, void *offset)  			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);  	} -	err = rtc_read_alarm(class_dev, &alrm); +	err = rtc_read_alarm(rtc, &alrm);  	if (err == 0) {  		seq_printf(seq, "alrm_time\t: ");  		if ((unsigned int)alrm.time.tm_hour <= 24) @@ -74,19 +74,19 @@ static int rtc_proc_show(struct seq_file *seq, void *offset)  	seq_printf(seq, "24hr\t\t: yes\n");  	if (ops->proc) -		ops->proc(class_dev->dev, seq); +		ops->proc(rtc->dev.parent, seq);  	return 0;  }  static int rtc_proc_open(struct inode *inode, struct file *file)  { -	struct class_device *class_dev = PDE(inode)->data; +	struct rtc_device *rtc = PDE(inode)->data;  	if (!try_module_get(THIS_MODULE))  		return -ENODEV; -	return single_open(file, rtc_proc_show, class_dev); +	return single_open(file, rtc_proc_show, rtc);  }  static int rtc_proc_release(struct inode *inode, struct file *file) @@ -103,62 +103,22 @@ static const struct file_operations rtc_proc_fops = {  	.release	= rtc_proc_release,  }; -static int rtc_proc_add_device(struct class_device *class_dev, -					struct class_interface *class_intf) +void rtc_proc_add_device(struct rtc_device *rtc)  { -	mutex_lock(&rtc_lock); -	if (rtc_dev == NULL) { +	if (rtc->id == 0) {  		struct proc_dir_entry *ent; -		rtc_dev = class_dev; -  		ent = create_proc_entry("driver/rtc", 0, NULL);  		if (ent) { -			struct rtc_device *rtc = to_rtc_device(class_dev); -  			ent->proc_fops = &rtc_proc_fops;  			ent->owner = rtc->owner; -			ent->data = class_dev; - -			dev_dbg(class_dev->dev, "rtc intf: proc\n"); +			ent->data = rtc;  		} -		else -			rtc_dev = NULL;  	} -	mutex_unlock(&rtc_lock); - -	return 0;  } -static void rtc_proc_remove_device(struct class_device *class_dev, -					struct class_interface *class_intf) +void rtc_proc_del_device(struct rtc_device *rtc)  { -	mutex_lock(&rtc_lock); -	if (rtc_dev == class_dev) { +	if (rtc->id == 0)  		remove_proc_entry("driver/rtc", NULL); -		rtc_dev = NULL; -	} -	mutex_unlock(&rtc_lock); -} - -static struct class_interface rtc_proc_interface = { -	.add = &rtc_proc_add_device, -	.remove = &rtc_proc_remove_device, -}; - -static int __init rtc_proc_init(void) -{ -	return rtc_interface_register(&rtc_proc_interface);  } - -static void __exit rtc_proc_exit(void) -{ -	class_interface_unregister(&rtc_proc_interface); -} - -subsys_initcall(rtc_proc_init); -module_exit(rtc_proc_exit); - -MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); -MODULE_DESCRIPTION("RTC class proc interface"); -MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-rs5c313.c b/drivers/rtc/rtc-rs5c313.c new file mode 100644 index 00000000000..66eb133bf5f --- /dev/null +++ b/drivers/rtc/rtc-rs5c313.c @@ -0,0 +1,423 @@ +/* + * Ricoh RS5C313 RTC device/driver + *  Copyright (C) 2007 Nobuhiro Iwamatsu + * + *  2005-09-19 modifed by kogiidena + * + * Based on the old drivers/char/rs5c313_rtc.c  by: + *  Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> + *  Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka + * + * Based on code written by Paul Gortmaker. + *  Copyright (C) 1996 Paul Gortmaker + * + * This file is subject to the terms and conditions of the GNU General Public + * License.  See the file "COPYING" in the main directory of this archive + * for more details. + * + * Based on other minimal char device drivers, like Alan's + * watchdog, Ted's random, etc. etc. + * + *	1.07	Paul Gortmaker. + *	1.08	Miquel van Smoorenburg: disallow certain things on the + *		DEC Alpha as the CMOS clock is also used for other things. + *	1.09	Nikita Schmidt: epoch support and some Alpha cleanup. + *	1.09a	Pete Zaitcev: Sun SPARC + *	1.09b	Jeff Garzik: Modularize, init cleanup + *	1.09c	Jeff Garzik: SMP cleanup + *	1.10    Paul Barton-Davis: add support for async I/O + *	1.10a	Andrea Arcangeli: Alpha updates + *	1.10b	Andrew Morton: SMP lock fix + *	1.10c	Cesar Barros: SMP locking fixes and cleanup + *	1.10d	Paul Gortmaker: delete paranoia check in rtc_exit + *	1.10e	Maciej W. Rozycki: Handle DECstation's year weirdness. + *      1.11    Takashi Iwai: Kernel access functions + *			      rtc_register/rtc_unregister/rtc_control + *      1.11a   Daniele Bellucci: Audit create_proc_read_entry in rtc_init + *	1.12	Venkatesh Pallipadi: Hooks for emulating rtc on HPET base-timer + *		CONFIG_HPET_EMULATE_RTC + *	1.13	Nobuhiro Iwamatsu: Updata driver. + */ + +#include <linux/module.h> +#include <linux/err.h> +#include <linux/rtc.h> +#include <linux/platform_device.h> +#include <linux/bcd.h> +#include <linux/delay.h> +#include <asm/io.h> + +#define DRV_NAME	"rs5c313" +#define DRV_VERSION 	"1.13" + +#ifdef CONFIG_SH_LANDISK +/*****************************************************/ +/* LANDISK dependence part of RS5C313                */ +/*****************************************************/ + +#define SCSMR1		0xFFE00000 +#define SCSCR1		0xFFE00008 +#define SCSMR1_CA	0x80 +#define SCSCR1_CKE	0x03 +#define SCSPTR1		0xFFE0001C +#define SCSPTR1_EIO	0x80 +#define SCSPTR1_SPB1IO	0x08 +#define SCSPTR1_SPB1DT	0x04 +#define SCSPTR1_SPB0IO	0x02 +#define SCSPTR1_SPB0DT	0x01 + +#define SDA_OEN		SCSPTR1_SPB1IO +#define SDA		SCSPTR1_SPB1DT +#define SCL_OEN		SCSPTR1_SPB0IO +#define SCL		SCSPTR1_SPB0DT + +/* RICOH RS5C313 CE port */ +#define RS5C313_CE	0xB0000003 + +/* RICOH RS5C313 CE port bit */ +#define RS5C313_CE_RTCCE	0x02 + +/* SCSPTR1 data */ +unsigned char scsptr1_data; + +#define RS5C313_CEENABLE    ctrl_outb(RS5C313_CE_RTCCE, RS5C313_CE); +#define RS5C313_CEDISABLE   ctrl_outb(0x00, RS5C313_CE) +#define RS5C313_MISCOP      ctrl_outb(0x02, 0xB0000008) + +static void rs5c313_init_port(void) +{ +	/* Set SCK as I/O port and Initialize SCSPTR1 data & I/O port. */ +	ctrl_outb(ctrl_inb(SCSMR1) & ~SCSMR1_CA, SCSMR1); +	ctrl_outb(ctrl_inb(SCSCR1) & ~SCSCR1_CKE, SCSCR1); + +	/* And Initialize SCL for RS5C313 clock */ +	scsptr1_data = ctrl_inb(SCSPTR1) | SCL;	/* SCL:H */ +	ctrl_outb(scsptr1_data, SCSPTR1); +	scsptr1_data = ctrl_inb(SCSPTR1) | SCL_OEN;	/* SCL output enable */ +	ctrl_outb(scsptr1_data, SCSPTR1); +	RS5C313_CEDISABLE;	/* CE:L */ +} + +static void rs5c313_write_data(unsigned char data) +{ +	int i; + +	for (i = 0; i < 8; i++) { +		/* SDA:Write Data */ +		scsptr1_data = (scsptr1_data & ~SDA) | +				((((0x80 >> i) & data) >> (7 - i)) << 2); +		ctrl_outb(scsptr1_data, SCSPTR1); +		if (i == 0) { +			scsptr1_data |= SDA_OEN;	/* SDA:output enable */ +			ctrl_outb(scsptr1_data, SCSPTR1); +		} +		ndelay(700); +		scsptr1_data &= ~SCL;	/* SCL:L */ +		ctrl_outb(scsptr1_data, SCSPTR1); +		ndelay(700); +		scsptr1_data |= SCL;	/* SCL:H */ +		ctrl_outb(scsptr1_data, SCSPTR1); +	} + +	scsptr1_data &= ~SDA_OEN;	/* SDA:output disable */ +	ctrl_outb(scsptr1_data, SCSPTR1); +} + +static unsigned char rs5c313_read_data(void) +{ +	int i; +	unsigned char data = 0; + +	for (i = 0; i < 8; i++) { +		ndelay(700); +		/* SDA:Read Data */ +		data |= ((ctrl_inb(SCSPTR1) & SDA) >> 2) << (7 - i); +		scsptr1_data &= ~SCL;	/* SCL:L */ +		ctrl_outb(scsptr1_data, SCSPTR1); +		ndelay(700); +		scsptr1_data |= SCL;	/* SCL:H */ +		ctrl_outb(scsptr1_data, SCSPTR1); +	} +	return data & 0x0F; +} + +#endif /* CONFIG_SH_LANDISK */ + +/*****************************************************/ +/* machine independence part of RS5C313              */ +/*****************************************************/ + +/* RICOH RS5C313 address */ +#define RS5C313_ADDR_SEC	0x00 +#define RS5C313_ADDR_SEC10	0x01 +#define RS5C313_ADDR_MIN	0x02 +#define RS5C313_ADDR_MIN10	0x03 +#define RS5C313_ADDR_HOUR	0x04 +#define RS5C313_ADDR_HOUR10	0x05 +#define RS5C313_ADDR_WEEK	0x06 +#define RS5C313_ADDR_INTINTVREG	0x07 +#define RS5C313_ADDR_DAY	0x08 +#define RS5C313_ADDR_DAY10	0x09 +#define RS5C313_ADDR_MON	0x0A +#define RS5C313_ADDR_MON10	0x0B +#define RS5C313_ADDR_YEAR	0x0C +#define RS5C313_ADDR_YEAR10	0x0D +#define RS5C313_ADDR_CNTREG	0x0E +#define RS5C313_ADDR_TESTREG	0x0F + +/* RICOH RS5C313 control register */ +#define RS5C313_CNTREG_ADJ_BSY	0x01 +#define RS5C313_CNTREG_WTEN_XSTP	0x02 +#define RS5C313_CNTREG_12_24	0x04 +#define RS5C313_CNTREG_CTFG	0x08 + +/* RICOH RS5C313 test register */ +#define RS5C313_TESTREG_TEST	0x01 + +/* RICOH RS5C313 control bit */ +#define RS5C313_CNTBIT_READ	0x40 +#define RS5C313_CNTBIT_AD	0x20 +#define RS5C313_CNTBIT_DT	0x10 + +static unsigned char rs5c313_read_reg(unsigned char addr) +{ + +	rs5c313_write_data(addr | RS5C313_CNTBIT_READ | RS5C313_CNTBIT_AD); +	return rs5c313_read_data(); +} + +static void rs5c313_write_reg(unsigned char addr, unsigned char data) +{ +	data &= 0x0f; +	rs5c313_write_data(addr | RS5C313_CNTBIT_AD); +	rs5c313_write_data(data | RS5C313_CNTBIT_DT); +	return; +} + +static inline unsigned char rs5c313_read_cntreg(void) +{ +	return rs5c313_read_reg(RS5C313_ADDR_CNTREG); +} + +static inline void rs5c313_write_cntreg(unsigned char data) +{ +	rs5c313_write_reg(RS5C313_ADDR_CNTREG, data); +} + +static inline void rs5c313_write_intintvreg(unsigned char data) +{ +	rs5c313_write_reg(RS5C313_ADDR_INTINTVREG, data); +} + +static int rs5c313_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ +	int data; +	int cnt; + +	cnt = 0; +	while (1) { +		RS5C313_CEENABLE;	/* CE:H */ + +		/* Initialize control reg. 24 hour */ +		rs5c313_write_cntreg(0x04); + +		if (!(rs5c313_read_cntreg() & RS5C313_CNTREG_ADJ_BSY)) +			break; + +		RS5C313_CEDISABLE; +		ndelay(700);	/* CE:L */ + +		if (cnt++ > 100) { +			dev_err(dev, "%s: timeout error\n", __FUNCTION__); +			return -EIO; +		} +	} + +	data = rs5c313_read_reg(RS5C313_ADDR_SEC); +	data |= (rs5c313_read_reg(RS5C313_ADDR_SEC10) << 4); +	tm->tm_sec = BCD2BIN(data); + +	data = rs5c313_read_reg(RS5C313_ADDR_MIN); +	data |= (rs5c313_read_reg(RS5C313_ADDR_MIN10) << 4); +	tm->tm_min = BCD2BIN(data); + +	data = rs5c313_read_reg(RS5C313_ADDR_HOUR); +	data |= (rs5c313_read_reg(RS5C313_ADDR_HOUR10) << 4); +	tm->tm_hour = BCD2BIN(data); + +	data = rs5c313_read_reg(RS5C313_ADDR_DAY); +	data |= (rs5c313_read_reg(RS5C313_ADDR_DAY10) << 4); +	tm->tm_mday = BCD2BIN(data); + +	data = rs5c313_read_reg(RS5C313_ADDR_MON); +	data |= (rs5c313_read_reg(RS5C313_ADDR_MON10) << 4); +	tm->tm_mon = BCD2BIN(data) - 1; + +	data = rs5c313_read_reg(RS5C313_ADDR_YEAR); +	data |= (rs5c313_read_reg(RS5C313_ADDR_YEAR10) << 4); +	tm->tm_year = BCD2BIN(data); + +	if (tm->tm_year < 70) +		tm->tm_year += 100; + +	data = rs5c313_read_reg(RS5C313_ADDR_WEEK); +	tm->tm_wday = BCD2BIN(data); + +	RS5C313_CEDISABLE; +	ndelay(700);		/* CE:L */ + +	return 0; +} + +static int rs5c313_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ +	int data; +	int cnt; + +	cnt = 0; +	/* busy check. */ +	while (1) { +		RS5C313_CEENABLE;	/* CE:H */ + +		/* Initiatlize control reg. 24 hour */ +		rs5c313_write_cntreg(0x04); + +		if (!(rs5c313_read_cntreg() & RS5C313_CNTREG_ADJ_BSY)) +			break; +		RS5C313_MISCOP; +		RS5C313_CEDISABLE; +		ndelay(700);	/* CE:L */ + +		if (cnt++ > 100) { +			dev_err(dev, "%s: timeout error\n", __FUNCTION__); +			return -EIO; +		} +	} + +	data = BIN2BCD(tm->tm_sec); +	rs5c313_write_reg(RS5C313_ADDR_SEC, data); +	rs5c313_write_reg(RS5C313_ADDR_SEC10, (data >> 4)); + +	data = BIN2BCD(tm->tm_min); +	rs5c313_write_reg(RS5C313_ADDR_MIN, data ); +	rs5c313_write_reg(RS5C313_ADDR_MIN10, (data >> 4)); + +	data = BIN2BCD(tm->tm_hour); +	rs5c313_write_reg(RS5C313_ADDR_HOUR, data); +	rs5c313_write_reg(RS5C313_ADDR_HOUR10, (data >> 4)); + +	data = BIN2BCD(tm->tm_mday); +	rs5c313_write_reg(RS5C313_ADDR_DAY, data); +	rs5c313_write_reg(RS5C313_ADDR_DAY10, (data>> 4)); + +	data = BIN2BCD(tm->tm_mon + 1); +	rs5c313_write_reg(RS5C313_ADDR_MON, data); +	rs5c313_write_reg(RS5C313_ADDR_MON10, (data >> 4)); + +	data = BIN2BCD(tm->tm_year % 100); +	rs5c313_write_reg(RS5C313_ADDR_YEAR, data); +	rs5c313_write_reg(RS5C313_ADDR_YEAR10, (data >> 4)); + +	data = BIN2BCD(tm->tm_wday); +	rs5c313_write_reg(RS5C313_ADDR_WEEK, data); + +	RS5C313_CEDISABLE;	/* CE:H */ +	ndelay(700); + +	return 0; +} + +static void rs5c313_check_xstp_bit(void) +{ +	struct rtc_time tm; +	int cnt; + +	RS5C313_CEENABLE;	/* CE:H */ +	if (rs5c313_read_cntreg() & RS5C313_CNTREG_WTEN_XSTP) { +		/* INT interval reg. OFF */ +		rs5c313_write_intintvreg(0x00); +		/* Initialize control reg. 24 hour & adjust */ +		rs5c313_write_cntreg(0x07); + +		/* busy check. */ +		for (cnt = 0; cnt < 100; cnt++) { +			if (!(rs5c313_read_cntreg() & RS5C313_CNTREG_ADJ_BSY)) +				break; +			RS5C313_MISCOP; +		} + +		memset(&tm, 0, sizeof(struct rtc_time)); +		tm.tm_mday 	= 1; +		tm.tm_mon 	= 1 - 1; +		tm.tm_year 	= 2000 - 1900; + +		rs5c313_rtc_set_time(NULL, &tm); +		printk(KERN_ERR "RICHO RS5C313: invalid value, resetting to " +				"1 Jan 2000\n"); +	} +	RS5C313_CEDISABLE; +	ndelay(700);		/* CE:L */ +} + +static const struct rtc_class_ops rs5c313_rtc_ops = { +	.read_time = rs5c313_rtc_read_time, +	.set_time = rs5c313_rtc_set_time, +}; + +static int rs5c313_rtc_probe(struct platform_device *pdev) +{ +	struct rtc_device *rtc = rtc_device_register("rs5c313", &pdev->dev, +				&rs5c313_rtc_ops, THIS_MODULE); + +	if (IS_ERR(rtc)) +		return PTR_ERR(rtc); + +	platform_set_drvdata(pdev, rtc); + +	return 0; +} + +static int __devexit rs5c313_rtc_remove(struct platform_device *pdev) +{ +	struct rtc_device *rtc = platform_get_drvdata( pdev ); + +	rtc_device_unregister(rtc); + +	return 0; +} + +static struct platform_driver rs5c313_rtc_platform_driver = { +	.driver         = { +		.name   = DRV_NAME, +		.owner  = THIS_MODULE, +	}, +	.probe 	= rs5c313_rtc_probe, +	.remove = __devexit_p( rs5c313_rtc_remove ), +}; + +static int __init rs5c313_rtc_init(void) +{ +	int err; + +	err = platform_driver_register(&rs5c313_rtc_platform_driver); +	if (err) +		return err; + +	rs5c313_init_port(); +	rs5c313_check_xstp_bit(); + +	return 0; +} + +static void __exit rs5c313_rtc_exit(void) +{ +	platform_driver_unregister( &rs5c313_rtc_platform_driver ); +} + +module_init(rs5c313_rtc_init); +module_exit(rs5c313_rtc_exit); + +MODULE_VERSION(DRV_VERSION); +MODULE_AUTHOR("kogiidena , Nobuhiro Iwamatsu <iwamatsu@nigauri.org>"); +MODULE_DESCRIPTION("Ricoh RS5C313 RTC device driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 9a79a24a748..54b61305346 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -50,7 +50,7 @@ static irqreturn_t s3c_rtc_alarmirq(int irq, void *id)  {  	struct rtc_device *rdev = id; -	rtc_update_irq(&rdev->class_dev, 1, RTC_AF | RTC_IRQF); +	rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF);  	return IRQ_HANDLED;  } @@ -58,7 +58,7 @@ static irqreturn_t s3c_rtc_tickirq(int irq, void *id)  {  	struct rtc_device *rdev = id; -	rtc_update_irq(&rdev->class_dev, tick_count++, RTC_PF | RTC_IRQF); +	rtc_update_irq(rdev, tick_count++, RTC_PF | RTC_IRQF);  	return IRQ_HANDLED;  } @@ -548,37 +548,15 @@ static int ticnt_save;  static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state)  { -	struct rtc_time tm; -	struct timespec time; - -	time.tv_nsec = 0; -  	/* save TICNT for anyone using periodic interrupts */ -  	ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT); - -	/* calculate time delta for suspend */ - -	s3c_rtc_gettime(&pdev->dev, &tm); -	rtc_tm_to_time(&tm, &time.tv_sec); -	save_time_delta(&s3c_rtc_delta, &time);  	s3c_rtc_enable(pdev, 0); -  	return 0;  }  static int s3c_rtc_resume(struct platform_device *pdev)  { -	struct rtc_time tm; -	struct timespec time; - -	time.tv_nsec = 0; -  	s3c_rtc_enable(pdev, 1); -	s3c_rtc_gettime(&pdev->dev, &tm); -	rtc_tm_to_time(&tm, &time.tv_sec); -	restore_time_delta(&s3c_rtc_delta, &time); -  	writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT);  	return 0;  } diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c index 677bae820dc..0918b787c4d 100644 --- a/drivers/rtc/rtc-sa1100.c +++ b/drivers/rtc/rtc-sa1100.c @@ -93,7 +93,7 @@ static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id)  	if (rtsr & RTSR_HZ)  		events |= RTC_UF | RTC_IRQF; -	rtc_update_irq(&rtc->class_dev, 1, events); +	rtc_update_irq(rtc, 1, events);  	if (rtsr & RTSR_AL && rtc_periodic_alarm(&rtc_alarm))  		rtc_update_alarm(&rtc_alarm); @@ -119,7 +119,7 @@ static irqreturn_t timer1_interrupt(int irq, void *dev_id)  	 */  	OSSR = OSSR_M1;	/* clear match on timer1 */ -	rtc_update_irq(&rtc->class_dev, rtc_timer1_count, RTC_PF | RTC_IRQF); +	rtc_update_irq(rtc, rtc_timer1_count, RTC_PF | RTC_IRQF);  	if (rtc_timer1_count == 1)  		rtc_timer1_count = (rtc_freq * ((1<<30)/(TIMER_FREQ>>2))); diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c index 198b9f22fbf..e0f91dfce0f 100644 --- a/drivers/rtc/rtc-sh.c +++ b/drivers/rtc/rtc-sh.c @@ -104,7 +104,7 @@ static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id)  	writeb(tmp, rtc->regbase + RCR1); -	rtc_update_irq(&rtc->rtc_dev->class_dev, 1, events); +	rtc_update_irq(rtc->rtc_dev, 1, events);  	spin_unlock(&rtc->lock); @@ -139,7 +139,7 @@ static irqreturn_t sh_rtc_alarm(int irq, void *dev_id)  		rtc->rearm_aie = 1; -		rtc_update_irq(&rtc->rtc_dev->class_dev, 1, events); +		rtc_update_irq(rtc->rtc_dev, 1, events);  	}  	spin_unlock(&rtc->lock); @@ -153,7 +153,7 @@ static irqreturn_t sh_rtc_periodic(int irq, void *dev_id)  	spin_lock(&rtc->lock); -	rtc_update_irq(&rtc->rtc_dev->class_dev, 1, RTC_PF | RTC_IRQF); +	rtc_update_irq(rtc->rtc_dev, 1, RTC_PF | RTC_IRQF);  	spin_unlock(&rtc->lock); @@ -341,7 +341,7 @@ static int sh_rtc_read_time(struct device *dev, struct rtc_time *tm)  		tm->tm_sec--;  #endif -	dev_dbg(&dev, "%s: tm is secs=%d, mins=%d, hours=%d, " +	dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "  		"mday=%d, mon=%d, year=%d, wday=%d\n",  		__FUNCTION__,  		tm->tm_sec, tm->tm_min, tm->tm_hour, diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c index 899ab8c514f..69df94b4484 100644 --- a/drivers/rtc/rtc-sysfs.c +++ b/drivers/rtc/rtc-sysfs.c @@ -12,20 +12,26 @@  #include <linux/module.h>  #include <linux/rtc.h> +#include "rtc-core.h" + +  /* device attributes */ -static ssize_t rtc_sysfs_show_name(struct class_device *dev, char *buf) +static ssize_t +rtc_sysfs_show_name(struct device *dev, struct device_attribute *attr, +		char *buf)  {  	return sprintf(buf, "%s\n", to_rtc_device(dev)->name);  } -static CLASS_DEVICE_ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL); -static ssize_t rtc_sysfs_show_date(struct class_device *dev, char *buf) +static ssize_t +rtc_sysfs_show_date(struct device *dev, struct device_attribute *attr, +		char *buf)  {  	ssize_t retval;  	struct rtc_time tm; -	retval = rtc_read_time(dev, &tm); +	retval = rtc_read_time(to_rtc_device(dev), &tm);  	if (retval == 0) {  		retval = sprintf(buf, "%04d-%02d-%02d\n",  			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); @@ -33,14 +39,15 @@ static ssize_t rtc_sysfs_show_date(struct class_device *dev, char *buf)  	return retval;  } -static CLASS_DEVICE_ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL); -static ssize_t rtc_sysfs_show_time(struct class_device *dev, char *buf) +static ssize_t +rtc_sysfs_show_time(struct device *dev, struct device_attribute *attr, +		char *buf)  {  	ssize_t retval;  	struct rtc_time tm; -	retval = rtc_read_time(dev, &tm); +	retval = rtc_read_time(to_rtc_device(dev), &tm);  	if (retval == 0) {  		retval = sprintf(buf, "%02d:%02d:%02d\n",  			tm.tm_hour, tm.tm_min, tm.tm_sec); @@ -48,14 +55,15 @@ static ssize_t rtc_sysfs_show_time(struct class_device *dev, char *buf)  	return retval;  } -static CLASS_DEVICE_ATTR(time, S_IRUGO, rtc_sysfs_show_time, NULL); -static ssize_t rtc_sysfs_show_since_epoch(struct class_device *dev, char *buf) +static ssize_t +rtc_sysfs_show_since_epoch(struct device *dev, struct device_attribute *attr, +		char *buf)  {  	ssize_t retval;  	struct rtc_time tm; -	retval = rtc_read_time(dev, &tm); +	retval = rtc_read_time(to_rtc_device(dev), &tm);  	if (retval == 0) {  		unsigned long time;  		rtc_tm_to_time(&tm, &time); @@ -64,23 +72,18 @@ static ssize_t rtc_sysfs_show_since_epoch(struct class_device *dev, char *buf)  	return retval;  } -static CLASS_DEVICE_ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL); - -static struct attribute *rtc_attrs[] = { -	&class_device_attr_name.attr, -	&class_device_attr_date.attr, -	&class_device_attr_time.attr, -	&class_device_attr_since_epoch.attr, -	NULL, -}; -static struct attribute_group rtc_attr_group = { -	.attrs = rtc_attrs, +static struct device_attribute rtc_attrs[] = { +	__ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL), +	__ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL), +	__ATTR(time, S_IRUGO, rtc_sysfs_show_time, NULL), +	__ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL), +	{ },  }; -  static ssize_t -rtc_sysfs_show_wakealarm(struct class_device *dev, char *buf) +rtc_sysfs_show_wakealarm(struct device *dev, struct device_attribute *attr, +		char *buf)  {  	ssize_t retval;  	unsigned long alarm; @@ -94,7 +97,7 @@ rtc_sysfs_show_wakealarm(struct class_device *dev, char *buf)  	 * REVISIT maybe we should require RTC implementations to  	 * disable the RTC alarm after it triggers, for uniformity.  	 */ -	retval = rtc_read_alarm(dev, &alm); +	retval = rtc_read_alarm(to_rtc_device(dev), &alm);  	if (retval == 0 && alm.enabled) {  		rtc_tm_to_time(&alm.time, &alarm);  		retval = sprintf(buf, "%lu\n", alarm); @@ -104,16 +107,18 @@ rtc_sysfs_show_wakealarm(struct class_device *dev, char *buf)  }  static ssize_t -rtc_sysfs_set_wakealarm(struct class_device *dev, const char *buf, size_t n) +rtc_sysfs_set_wakealarm(struct device *dev, struct device_attribute *attr, +		const char *buf, size_t n)  {  	ssize_t retval;  	unsigned long now, alarm;  	struct rtc_wkalrm alm; +	struct rtc_device *rtc = to_rtc_device(dev);  	/* Only request alarms that trigger in the future.  Disable them  	 * by writing another time, e.g. 0 meaning Jan 1 1970 UTC.  	 */ -	retval = rtc_read_time(dev, &alm.time); +	retval = rtc_read_time(rtc, &alm.time);  	if (retval < 0)  		return retval;  	rtc_tm_to_time(&alm.time, &now); @@ -124,7 +129,7 @@ rtc_sysfs_set_wakealarm(struct class_device *dev, const char *buf, size_t n)  		 * entirely prevent that here, without even the minimal  		 * locking from the /dev/rtcN api.  		 */ -		retval = rtc_read_alarm(dev, &alm); +		retval = rtc_read_alarm(rtc, &alm);  		if (retval < 0)  			return retval;  		if (alm.enabled) @@ -141,10 +146,10 @@ rtc_sysfs_set_wakealarm(struct class_device *dev, const char *buf, size_t n)  	}  	rtc_time_to_tm(alarm, &alm.time); -	retval = rtc_set_alarm(dev, &alm); +	retval = rtc_set_alarm(rtc, &alm);  	return (retval < 0) ? retval : n;  } -static const CLASS_DEVICE_ATTR(wakealarm, S_IRUGO | S_IWUSR, +static DEVICE_ATTR(wakealarm, S_IRUGO | S_IWUSR,  		rtc_sysfs_show_wakealarm, rtc_sysfs_set_wakealarm); @@ -153,71 +158,37 @@ static const CLASS_DEVICE_ATTR(wakealarm, S_IRUGO | S_IWUSR,   * suspend-to-disk.  So: no attribute unless that side effect is possible.   * (Userspace may disable that mechanism later.)   */ -static inline int rtc_does_wakealarm(struct class_device *class_dev) +static inline int rtc_does_wakealarm(struct rtc_device *rtc)  { -	struct rtc_device *rtc; - -	if (!device_can_wakeup(class_dev->dev)) +	if (!device_can_wakeup(rtc->dev.parent))  		return 0; -	rtc = to_rtc_device(class_dev);  	return rtc->ops->set_alarm != NULL;  } -static int rtc_sysfs_add_device(struct class_device *class_dev, -					struct class_interface *class_intf) +void rtc_sysfs_add_device(struct rtc_device *rtc)  {  	int err; -	dev_dbg(class_dev->dev, "rtc intf: sysfs\n"); +	/* not all RTCs support both alarms and wakeup */ +	if (!rtc_does_wakealarm(rtc)) +		return; -	err = sysfs_create_group(&class_dev->kobj, &rtc_attr_group); +	err = device_create_file(&rtc->dev, &dev_attr_wakealarm);  	if (err) -		dev_err(class_dev->dev, "failed to create %s\n", -				"sysfs attributes"); -	else if (rtc_does_wakealarm(class_dev)) { -		/* not all RTCs support both alarms and wakeup */ -		err = class_device_create_file(class_dev, -					&class_device_attr_wakealarm); -		if (err) { -			dev_err(class_dev->dev, "failed to create %s\n", -					"alarm attribute"); -			sysfs_remove_group(&class_dev->kobj, &rtc_attr_group); -		} -	} - -	return err; +		dev_err(rtc->dev.parent, "failed to create " +				"alarm attribute, %d", +				err);  } -static void rtc_sysfs_remove_device(struct class_device *class_dev, -				struct class_interface *class_intf) +void rtc_sysfs_del_device(struct rtc_device *rtc)  { -	if (rtc_does_wakealarm(class_dev)) -		class_device_remove_file(class_dev, -				&class_device_attr_wakealarm); -	sysfs_remove_group(&class_dev->kobj, &rtc_attr_group); +	/* REVISIT did we add it successfully? */ +	if (rtc_does_wakealarm(rtc)) +		device_remove_file(&rtc->dev, &dev_attr_wakealarm);  } -/* interface registration */ - -static struct class_interface rtc_sysfs_interface = { -	.add = &rtc_sysfs_add_device, -	.remove = &rtc_sysfs_remove_device, -}; - -static int __init rtc_sysfs_init(void) +void __init rtc_sysfs_init(struct class *rtc_class)  { -	return rtc_interface_register(&rtc_sysfs_interface); +	rtc_class->dev_attrs = rtc_attrs;  } - -static void __exit rtc_sysfs_exit(void) -{ -	class_interface_unregister(&rtc_sysfs_interface); -} - -subsys_initcall(rtc_sysfs_init); -module_exit(rtc_sysfs_exit); - -MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); -MODULE_DESCRIPTION("RTC class sysfs interface"); -MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-test.c b/drivers/rtc/rtc-test.c index f50a1b8e160..254c9fce27d 100644 --- a/drivers/rtc/rtc-test.c +++ b/drivers/rtc/rtc-test.c @@ -101,11 +101,11 @@ static ssize_t test_irq_store(struct device *dev,  	retval = count;  	local_irq_disable();  	if (strncmp(buf, "tick", 4) == 0) -		rtc_update_irq(&rtc->class_dev, 1, RTC_PF | RTC_IRQF); +		rtc_update_irq(rtc, 1, RTC_PF | RTC_IRQF);  	else if (strncmp(buf, "alarm", 5) == 0) -		rtc_update_irq(&rtc->class_dev, 1, RTC_AF | RTC_IRQF); +		rtc_update_irq(rtc, 1, RTC_AF | RTC_IRQF);  	else if (strncmp(buf, "update", 6) == 0) -		rtc_update_irq(&rtc->class_dev, 1, RTC_UF | RTC_IRQF); +		rtc_update_irq(rtc, 1, RTC_UF | RTC_IRQF);  	else  		retval = -EINVAL;  	local_irq_enable(); diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c index e40322b7193..af7596ef29e 100644 --- a/drivers/rtc/rtc-vr41xx.c +++ b/drivers/rtc/rtc-vr41xx.c @@ -97,6 +97,7 @@ static DEFINE_SPINLOCK(rtc_lock);  static char rtc_name[] = "RTC";  static unsigned long periodic_frequency;  static unsigned long periodic_count; +static unsigned int alarm_enabled;  struct resource rtc_resource[2] = {  	{	.name	= rtc_name, @@ -188,6 +189,7 @@ static int vr41xx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)  	low = rtc1_read(ECMPLREG);  	mid = rtc1_read(ECMPMREG);  	high = rtc1_read(ECMPHREG); +	wkalrm->enabled = alarm_enabled;  	spin_unlock_irq(&rtc_lock); @@ -206,10 +208,18 @@ static int vr41xx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)  	spin_lock_irq(&rtc_lock); +	if (alarm_enabled) +		disable_irq(ELAPSEDTIME_IRQ); +  	rtc1_write(ECMPLREG, (uint16_t)(alarm_sec << 15));  	rtc1_write(ECMPMREG, (uint16_t)(alarm_sec >> 1));  	rtc1_write(ECMPHREG, (uint16_t)(alarm_sec >> 17)); +	if (wkalrm->enabled) +		enable_irq(ELAPSEDTIME_IRQ); + +	alarm_enabled = wkalrm->enabled; +  	spin_unlock_irq(&rtc_lock);  	return 0; @@ -221,10 +231,24 @@ static int vr41xx_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long  	switch (cmd) {  	case RTC_AIE_ON: -		enable_irq(ELAPSEDTIME_IRQ); +		spin_lock_irq(&rtc_lock); + +		if (!alarm_enabled) { +			enable_irq(ELAPSEDTIME_IRQ); +			alarm_enabled = 1; +		} + +		spin_unlock_irq(&rtc_lock);  		break;  	case RTC_AIE_OFF: -		disable_irq(ELAPSEDTIME_IRQ); +		spin_lock_irq(&rtc_lock); + +		if (alarm_enabled) { +			disable_irq(ELAPSEDTIME_IRQ); +			alarm_enabled = 0; +		} + +		spin_unlock_irq(&rtc_lock);  		break;  	case RTC_PIE_ON:  		enable_irq(RTCLONG1_IRQ); @@ -275,7 +299,7 @@ static irqreturn_t elapsedtime_interrupt(int irq, void *dev_id)  	rtc2_write(RTCINTREG, ELAPSEDTIME_INT); -	rtc_update_irq(&rtc->class_dev, 1, RTC_AF); +	rtc_update_irq(rtc, 1, RTC_AF);  	return IRQ_HANDLED;  } @@ -291,7 +315,7 @@ static irqreturn_t rtclong1_interrupt(int irq, void *dev_id)  	rtc1_write(RTCL1LREG, count);  	rtc1_write(RTCL1HREG, count >> 16); -	rtc_update_irq(&rtc->class_dev, 1, RTC_PF); +	rtc_update_irq(rtc, 1, RTC_PF);  	return IRQ_HANDLED;  }  | 
