summaryrefslogtreecommitdiff
path: root/drivers/rtc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/Kconfig129
-rw-r--r--drivers/rtc/Makefile12
-rw-r--r--drivers/rtc/class.c6
-rw-r--r--drivers/rtc/hctosys.c59
-rw-r--r--drivers/rtc/rtc-ab3100.c43
-rw-r--r--drivers/rtc/rtc-ab8500.c366
-rw-r--r--drivers/rtc/rtc-at32ap700x.c1
-rw-r--r--drivers/rtc/rtc-at91sam9.c3
-rw-r--r--drivers/rtc/rtc-bfin.c59
-rw-r--r--drivers/rtc/rtc-bq4802.c1
-rw-r--r--drivers/rtc/rtc-cmos.c100
-rw-r--r--drivers/rtc/rtc-coh901331.c6
-rw-r--r--drivers/rtc/rtc-davinci.c674
-rw-r--r--drivers/rtc/rtc-ds1216.c1
-rw-r--r--drivers/rtc/rtc-ds1286.c1
-rw-r--r--drivers/rtc/rtc-ds1302.c85
-rw-r--r--drivers/rtc/rtc-ds1305.c7
-rw-r--r--drivers/rtc/rtc-ds1307.c10
-rw-r--r--drivers/rtc/rtc-ds1374.c3
-rw-r--r--drivers/rtc/rtc-ds1390.c1
-rw-r--r--drivers/rtc/rtc-ds1511.c11
-rw-r--r--drivers/rtc/rtc-ds1553.c5
-rw-r--r--drivers/rtc/rtc-ds1742.c6
-rw-r--r--drivers/rtc/rtc-ds3232.c505
-rw-r--r--drivers/rtc/rtc-ep93xx.c72
-rw-r--r--drivers/rtc/rtc-fm3130.c190
-rw-r--r--drivers/rtc/rtc-imxdi.c519
-rw-r--r--drivers/rtc/rtc-isl12022.c327
-rw-r--r--drivers/rtc/rtc-isl1208.c45
-rw-r--r--drivers/rtc/rtc-jz4740.c380
-rw-r--r--drivers/rtc/rtc-lpc32xx.c414
-rw-r--r--drivers/rtc/rtc-m41t80.c34
-rw-r--r--drivers/rtc/rtc-m48t35.c1
-rw-r--r--drivers/rtc/rtc-m48t59.c10
-rw-r--r--drivers/rtc/rtc-m48t86.c2
-rw-r--r--drivers/rtc/rtc-max6900.c2
-rw-r--r--drivers/rtc/rtc-max8925.c315
-rw-r--r--drivers/rtc/rtc-max8998.c300
-rw-r--r--drivers/rtc/rtc-mc13783.c262
-rw-r--r--drivers/rtc/rtc-mc13xxx.c437
-rw-r--r--drivers/rtc/rtc-mpc5121.c390
-rw-r--r--drivers/rtc/rtc-msm6242.c1
-rw-r--r--drivers/rtc/rtc-mv.c1
-rw-r--r--drivers/rtc/rtc-mxc.c59
-rw-r--r--drivers/rtc/rtc-nuc900.c67
-rw-r--r--drivers/rtc/rtc-omap.c12
-rw-r--r--drivers/rtc/rtc-pcap.c1
-rw-r--r--drivers/rtc/rtc-pcf2123.c3
-rw-r--r--drivers/rtc/rtc-pcf50633.c1
-rw-r--r--drivers/rtc/rtc-pcf8563.c9
-rw-r--r--drivers/rtc/rtc-pl030.c1
-rw-r--r--drivers/rtc/rtc-pl031.c367
-rw-r--r--drivers/rtc/rtc-pxa.c43
-rw-r--r--drivers/rtc/rtc-rp5c01.c90
-rw-r--r--drivers/rtc/rtc-rs5c313.c34
-rw-r--r--drivers/rtc/rtc-rs5c348.c1
-rw-r--r--drivers/rtc/rtc-rs5c372.c1
-rw-r--r--drivers/rtc/rtc-rx8025.c5
-rw-r--r--drivers/rtc/rtc-rx8581.c26
-rw-r--r--drivers/rtc/rtc-s35390a.c2
-rw-r--r--drivers/rtc/rtc-s3c.c238
-rw-r--r--drivers/rtc/rtc-sh.c1
-rw-r--r--drivers/rtc/rtc-stk17ta8.c9
-rw-r--r--drivers/rtc/rtc-stmp3xxx.c1
-rw-r--r--drivers/rtc/rtc-sysfs.c5
-rw-r--r--drivers/rtc/rtc-twl.c4
-rw-r--r--drivers/rtc/rtc-tx4939.c5
-rw-r--r--drivers/rtc/rtc-v3020.c1
-rw-r--r--drivers/rtc/rtc-wm831x.c17
-rw-r--r--drivers/rtc/rtc-wm8350.c11
70 files changed, 5982 insertions, 828 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 8167e9e6827a..2883428d5ac8 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -166,6 +166,17 @@ config RTC_DRV_DS1672
This driver can also be built as a module. If so, the module
will be called rtc-ds1672.
+config RTC_DRV_DS3232
+ tristate "Dallas/Maxim DS3232"
+ depends on RTC_CLASS && I2C
+ help
+ If you say yes here you get support for Dallas Semiconductor
+ DS3232 real-time clock chips. If an interrupt is associated
+ with the device, the alarm functionality is supported.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds3232.
+
config RTC_DRV_MAX6900
tristate "Maxim MAX6900"
help
@@ -175,6 +186,26 @@ config RTC_DRV_MAX6900
This driver can also be built as a module. If so, the module
will be called rtc-max6900.
+config RTC_DRV_MAX8925
+ tristate "Maxim MAX8925"
+ depends on MFD_MAX8925
+ help
+ If you say yes here you will get support for the
+ RTC of Maxim MAX8925 PMIC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-max8925.
+
+config RTC_DRV_MAX8998
+ tristate "Maxim MAX8998"
+ depends on MFD_MAX8998
+ help
+ If you say yes here you will get support for the
+ RTC of Maxim MAX8998 PMIC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-max8998.
+
config RTC_DRV_RS5C372
tristate "Ricoh R2025S/D, RS5C372A/B, RV5C386, RV5C387A"
help
@@ -193,6 +224,15 @@ config RTC_DRV_ISL1208
This driver can also be built as a module. If so, the module
will be called rtc-isl1208.
+config RTC_DRV_ISL12022
+ tristate "Intersil ISL12022"
+ help
+ If you say yes here you get support for the
+ Intersil ISL12022 RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-isl12022.
+
config RTC_DRV_X1205
tristate "Xicor/Intersil X1205"
help
@@ -527,6 +567,16 @@ config RTC_DRV_MSM6242
This driver can also be built as a module. If so, the module
will be called rtc-msm6242.
+config RTC_DRV_IMXDI
+ tristate "Freescale IMX DryIce Real Time Clock"
+ depends on ARCH_MX25
+ depends on RTC_CLASS
+ help
+ Support for Freescale IMX DryIce RTC
+
+ This driver can also be built as a module, if so, the module
+ will be called "rtc-imxdi".
+
config RTC_MXC
tristate "Freescale MXC Real Time Clock"
depends on ARCH_MXC
@@ -601,6 +651,13 @@ config RTC_DRV_AB3100
Select this to enable the ST-Ericsson AB3100 Mixed Signal IC RTC
support. This chip contains a battery- and capacitor-backed RTC.
+config RTC_DRV_AB8500
+ tristate "ST-Ericsson AB8500 RTC"
+ depends on AB8500_CORE
+ help
+ Select this to enable the ST-Ericsson AB8500 power management IC RTC
+ support. This chip contains a battery- and capacitor-backed RTC.
+
config RTC_DRV_NUC900
tristate "NUC910/NUC920 RTC driver"
depends on RTC_CLASS && ARCH_W90X900
@@ -610,6 +667,16 @@ config RTC_DRV_NUC900
comment "on-CPU RTC drivers"
+config RTC_DRV_DAVINCI
+ tristate "TI DaVinci RTC"
+ depends on ARCH_DAVINCI_DM365
+ help
+ If you say yes here you get support for the RTC on the
+ DaVinci platforms (DM365).
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-davinci.
+
config RTC_DRV_OMAP
tristate "TI OMAP1"
depends on ARCH_OMAP15XX || ARCH_OMAP16XX || ARCH_OMAP730 || ARCH_DAVINCI_DA8XX
@@ -618,9 +685,16 @@ config RTC_DRV_OMAP
DA8xx/OMAP-L13x chips. This driver can also be built as a
module called rtc-omap.
+config HAVE_S3C_RTC
+ bool
+ help
+ This will include RTC support for Samsung SoCs. If
+ you want to include RTC support for any machine, kindly
+ select this in the respective mach-XXXX/Kconfig file.
+
config RTC_DRV_S3C
tristate "Samsung S3C series SoC RTC"
- depends on ARCH_S3C2410
+ depends on ARCH_S3C2410 || ARCH_S3C64XX || HAVE_S3C_RTC
help
RTC (Realtime Clock) driver for the clock inbuilt into the
Samsung S3C24XX series of SoCs. This can provide periodic
@@ -702,15 +776,15 @@ config RTC_DRV_AT32AP700X
AT32AP700x family processors.
config RTC_DRV_AT91RM9200
- tristate "AT91RM9200 or AT91SAM9RL"
- depends on ARCH_AT91RM9200 || ARCH_AT91SAM9RL
+ tristate "AT91RM9200 or some AT91SAM9 RTC"
+ depends on ARCH_AT91RM9200 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
help
Driver for the internal RTC (Realtime Clock) module found on
- Atmel AT91RM9200's and AT91SAM9RL chips. On SAM9RL chips
+ Atmel AT91RM9200's and some AT91SAM9 chips. On AT91SAM9 chips
this is powered by the backup power supply.
config RTC_DRV_AT91SAM9
- tristate "AT91SAM9x/AT91CAP9"
+ tristate "AT91SAM9x/AT91CAP9 RTT as RTC"
depends on ARCH_AT91 && !(ARCH_AT91RM9200 || ARCH_AT91X40)
help
RTC driver for the Atmel AT91SAM9x and AT91CAP9 internal RTT
@@ -718,8 +792,8 @@ config RTC_DRV_AT91SAM9
supply (such as a small coin cell battery), but do not need to
be used as RTCs.
- (On AT91SAM9rl chips you probably want to use the dedicated RTC
- module and leave the RTT available for other uses.)
+ (On AT91SAM9rl and AT91SAM9G45 chips you probably want to use the
+ dedicated RTC module and leave the RTT available for other uses.)
config RTC_DRV_AT91SAM9_RTT
int
@@ -747,7 +821,7 @@ config RTC_DRV_AT91SAM9_GPBR
config RTC_DRV_AU1XXX
tristate "Au1xxx Counter0 RTC support"
- depends on SOC_AU1X00
+ depends on MIPS_ALCHEMY
help
This is a driver for the Au1xxx on-chip Counter0 (Time-Of-Year
counter) to be used as a RTC.
@@ -862,10 +936,41 @@ config RTC_DRV_PCAP
If you say Y here you will get support for the RTC found on
the PCAP2 ASIC used on some Motorola phones.
-config RTC_DRV_MC13783
- depends on MFD_MC13783
- tristate "Freescale MC13783 RTC"
+config RTC_DRV_MC13XXX
+ depends on MFD_MC13XXX
+ tristate "Freescale MC13xxx RTC"
help
- This enables support for the Freescale MC13783 PMIC RTC
+ This enables support for the RTCs found on Freescale's PMICs
+ MC13783 and MC13892.
+
+config RTC_DRV_MPC5121
+ tristate "Freescale MPC5121 built-in RTC"
+ depends on PPC_MPC512x && RTC_CLASS
+ help
+ If you say yes here you will get support for the
+ built-in RTC MPC5121.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-mpc5121.
+
+config RTC_DRV_JZ4740
+ tristate "Ingenic JZ4740 SoC"
+ depends on RTC_CLASS
+ depends on MACH_JZ4740
+ help
+ If you say yes here you get support for the Ingenic JZ4740 SoC RTC
+ controller.
+
+ This driver can also be buillt as a module. If so, the module
+ will be called rtc-jz4740.
+
+config RTC_DRV_LPC32XX
+ depends on ARCH_LPC32XX
+ tristate "NXP LPC32XX RTC"
+ help
+ This enables support for the NXP RTC in the LPC32XX
+
+ This driver can also be buillt as a module. If so, the module
+ will be called rtc-lpc32xx.
endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index e5160fddc446..4c2832df4697 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -18,6 +18,7 @@ rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o
# Keep the list ordered.
obj-$(CONFIG_RTC_DRV_AB3100) += rtc-ab3100.o
+obj-$(CONFIG_RTC_DRV_AB8500) += rtc-ab8500.o
obj-$(CONFIG_RTC_DRV_AT32AP700X)+= rtc-at32ap700x.o
obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o
@@ -27,6 +28,7 @@ obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o
obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o
obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o
+obj-$(CONFIG_RTC_DRV_DAVINCI) += rtc-davinci.o
obj-$(CONFIG_RTC_DRV_DM355EVM) += rtc-dm355evm.o
obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o
obj-$(CONFIG_RTC_DRV_DS1286) += rtc-ds1286.o
@@ -39,12 +41,17 @@ obj-$(CONFIG_RTC_DRV_DS1511) += rtc-ds1511.o
obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
+obj-$(CONFIG_RTC_DRV_DS3232) += rtc-ds3232.o
obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o
obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o
+obj-$(CONFIG_RTC_DRV_IMXDI) += rtc-imxdi.o
obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
+obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o
+obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o
+obj-$(CONFIG_RTC_DRV_LPC32XX) += rtc-lpc32xx.o
obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o
obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o
obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o
@@ -52,9 +59,12 @@ obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o
obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o
obj-$(CONFIG_RTC_MXC) += rtc-mxc.o
obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o
+obj-$(CONFIG_RTC_DRV_MAX8925) += rtc-max8925.o
+obj-$(CONFIG_RTC_DRV_MAX8998) += rtc-max8998.o
obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
-obj-$(CONFIG_RTC_DRV_MC13783) += rtc-mc13783.o
+obj-$(CONFIG_RTC_DRV_MC13XXX) += rtc-mc13xxx.o
obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o
+obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o
obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o
obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o
obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
index be5a6b73e601..e6539cbabb35 100644
--- a/drivers/rtc/class.c
+++ b/drivers/rtc/class.c
@@ -15,6 +15,7 @@
#include <linux/rtc.h>
#include <linux/kdev_t.h>
#include <linux/idr.h>
+#include <linux/slab.h>
#include "rtc-core.h"
@@ -157,8 +158,10 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev,
rtc_dev_prepare(rtc);
err = device_register(&rtc->dev);
- if (err)
+ if (err) {
+ put_device(&rtc->dev);
goto exit_kfree;
+ }
rtc_dev_add_device(rtc);
rtc_sysfs_add_device(rtc);
@@ -226,6 +229,7 @@ static void __exit rtc_exit(void)
{
rtc_dev_exit();
class_destroy(rtc_class);
+ idr_destroy(&rtc_idr);
}
subsys_initcall(rtc_init);
diff --git a/drivers/rtc/hctosys.c b/drivers/rtc/hctosys.c
index 33c0e98243ee..bc90b091f195 100644
--- a/drivers/rtc/hctosys.c
+++ b/drivers/rtc/hctosys.c
@@ -22,48 +22,57 @@
* the best guess is to add 0.5s.
*/
+int rtc_hctosys_ret = -ENODEV;
+
static int __init rtc_hctosys(void)
{
- int err;
+ int err = -ENODEV;
struct rtc_time tm;
+ struct timespec tv = {
+ .tv_nsec = NSEC_PER_SEC >> 1,
+ };
struct rtc_device *rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
if (rtc == NULL) {
- printk("%s: unable to open rtc device (%s)\n",
+ pr_err("%s: unable to open rtc device (%s)\n",
__FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
- return -ENODEV;
+ goto err_open;
}
err = rtc_read_time(rtc, &tm);
- if (err == 0) {
- err = rtc_valid_tm(&tm);
- if (err == 0) {
- struct timespec tv;
+ if (err) {
+ dev_err(rtc->dev.parent,
+ "hctosys: unable to read the hardware clock\n");
+ goto err_read;
- tv.tv_nsec = NSEC_PER_SEC >> 1;
+ }
- rtc_tm_to_time(&tm, &tv.tv_sec);
+ err = rtc_valid_tm(&tm);
+ if (err) {
+ dev_err(rtc->dev.parent,
+ "hctosys: invalid date/time\n");
+ goto err_invalid;
+ }
- do_settimeofday(&tv);
+ rtc_tm_to_time(&tm, &tv.tv_sec);
- dev_info(rtc->dev.parent,
- "setting system clock to "
- "%d-%02d-%02d %02d:%02d:%02d UTC (%u)\n",
- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec,
- (unsigned int) tv.tv_sec);
- }
- else
- dev_err(rtc->dev.parent,
- "hctosys: invalid date/time\n");
- }
- else
- dev_err(rtc->dev.parent,
- "hctosys: unable to read the hardware clock\n");
+ do_settimeofday(&tv);
+ dev_info(rtc->dev.parent,
+ "setting system clock to "
+ "%d-%02d-%02d %02d:%02d:%02d UTC (%u)\n",
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec,
+ (unsigned int) tv.tv_sec);
+
+err_invalid:
+err_read:
rtc_class_close(rtc);
- return 0;
+err_open:
+ rtc_hctosys_ret = err;
+
+ return err;
}
late_initcall(rtc_hctosys);
diff --git a/drivers/rtc/rtc-ab3100.c b/drivers/rtc/rtc-ab3100.c
index 4704aac2b5af..261a07e0fb24 100644
--- a/drivers/rtc/rtc-ab3100.c
+++ b/drivers/rtc/rtc-ab3100.c
@@ -9,7 +9,7 @@
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
-#include <linux/mfd/ab3100.h>
+#include <linux/mfd/abx500.h>
/* Clock rate in Hz */
#define AB3100_RTC_CLOCK_RATE 32768
@@ -45,7 +45,6 @@
*/
static int ab3100_rtc_set_mmss(struct device *dev, unsigned long secs)
{
- struct ab3100 *ab3100_data = dev_get_drvdata(dev);
u8 regs[] = {AB3100_TI0, AB3100_TI1, AB3100_TI2,
AB3100_TI3, AB3100_TI4, AB3100_TI5};
unsigned char buf[6];
@@ -61,27 +60,26 @@ static int ab3100_rtc_set_mmss(struct device *dev, unsigned long secs)
buf[5] = (fat_time >> 40) & 0xFF;
for (i = 0; i < 6; i++) {
- err = ab3100_set_register_interruptible(ab3100_data,
+ err = abx500_set_register_interruptible(dev, 0,
regs[i], buf[i]);
if (err)
return err;
}
/* Set the flag to mark that the clock is now set */
- return ab3100_mask_and_set_register_interruptible(ab3100_data,
+ return abx500_mask_and_set_register_interruptible(dev, 0,
AB3100_RTC,
- 0xFE, 0x01);
+ 0x01, 0x01);
}
static int ab3100_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
- struct ab3100 *ab3100_data = dev_get_drvdata(dev);
unsigned long time;
u8 rtcval;
int err;
- err = ab3100_get_register_interruptible(ab3100_data,
+ err = abx500_get_register_interruptible(dev, 0,
AB3100_RTC, &rtcval);
if (err)
return err;
@@ -94,7 +92,7 @@ static int ab3100_rtc_read_time(struct device *dev, struct rtc_time *tm)
u8 buf[6];
/* Read out time registers */
- err = ab3100_get_register_page_interruptible(ab3100_data,
+ err = abx500_get_register_page_interruptible(dev, 0,
AB3100_TI0,
buf, 6);
if (err != 0)
@@ -114,7 +112,6 @@ static int ab3100_rtc_read_time(struct device *dev, struct rtc_time *tm)
static int ab3100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
- struct ab3100 *ab3100_data = dev_get_drvdata(dev);
unsigned long time;
u64 fat_time;
u8 buf[6];
@@ -122,7 +119,7 @@ static int ab3100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
int err;
/* Figure out if alarm is enabled or not */
- err = ab3100_get_register_interruptible(ab3100_data,
+ err = abx500_get_register_interruptible(dev, 0,
AB3100_RTC, &rtcval);
if (err)
return err;
@@ -133,7 +130,7 @@ static int ab3100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
/* No idea how this could be represented */
alarm->pending = 0;
/* Read out alarm registers, only 4 bytes */
- err = ab3100_get_register_page_interruptible(ab3100_data,
+ err = abx500_get_register_page_interruptible(dev, 0,
AB3100_AL0, buf, 4);
if (err)
return err;
@@ -148,7 +145,6 @@ static int ab3100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
static int ab3100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
- struct ab3100 *ab3100_data = dev_get_drvdata(dev);
u8 regs[] = {AB3100_AL0, AB3100_AL1, AB3100_AL2, AB3100_AL3};
unsigned char buf[4];
unsigned long secs;
@@ -165,21 +161,19 @@ static int ab3100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
/* Set the alarm */
for (i = 0; i < 4; i++) {
- err = ab3100_set_register_interruptible(ab3100_data,
+ err = abx500_set_register_interruptible(dev, 0,
regs[i], buf[i]);
if (err)
return err;
}
/* Then enable the alarm */
- return ab3100_mask_and_set_register_interruptible(ab3100_data,
- AB3100_RTC, ~(1 << 2),
+ return abx500_mask_and_set_register_interruptible(dev, 0,
+ AB3100_RTC, (1 << 2),
alarm->enabled << 2);
}
static int ab3100_rtc_irq_enable(struct device *dev, unsigned int enabled)
{
- struct ab3100 *ab3100_data = dev_get_drvdata(dev);
-
/*
* It's not possible to enable/disable the alarm IRQ for this RTC.
* It does not actually trigger any IRQ: instead its only function is
@@ -188,12 +182,12 @@ static int ab3100_rtc_irq_enable(struct device *dev, unsigned int enabled)
* and need to be handled there instead.
*/
if (enabled)
- return ab3100_mask_and_set_register_interruptible(ab3100_data,
- AB3100_RTC, ~(1 << 2),
+ return abx500_mask_and_set_register_interruptible(dev, 0,
+ AB3100_RTC, (1 << 2),
1 << 2);
else
- return ab3100_mask_and_set_register_interruptible(ab3100_data,
- AB3100_RTC, ~(1 << 2),
+ return abx500_mask_and_set_register_interruptible(dev, 0,
+ AB3100_RTC, (1 << 2),
0);
}
@@ -210,10 +204,9 @@ static int __init ab3100_rtc_probe(struct platform_device *pdev)
int err;
u8 regval;
struct rtc_device *rtc;
- struct ab3100 *ab3100_data = platform_get_drvdata(pdev);
/* The first RTC register needs special treatment */
- err = ab3100_get_register_interruptible(ab3100_data,
+ err = abx500_get_register_interruptible(&pdev->dev, 0,
AB3100_RTC, &regval);
if (err) {
dev_err(&pdev->dev, "unable to read RTC register\n");
@@ -231,7 +224,7 @@ static int __init ab3100_rtc_probe(struct platform_device *pdev)
* This bit remains until RTC power is lost.
*/
regval = 1 | RTC_SETTING;
- err = ab3100_set_register_interruptible(ab3100_data,
+ err = abx500_set_register_interruptible(&pdev->dev, 0,
AB3100_RTC, regval);
/* Ignore any error on this write */
}
@@ -242,6 +235,7 @@ static int __init ab3100_rtc_probe(struct platform_device *pdev)
err = PTR_ERR(rtc);
return err;
}
+ platform_set_drvdata(pdev, rtc);
return 0;
}
@@ -251,6 +245,7 @@ static int __exit ab3100_rtc_remove(struct platform_device *pdev)
struct rtc_device *rtc = platform_get_drvdata(pdev);
rtc_device_unregister(rtc);
+ platform_set_drvdata(pdev, NULL);
return 0;
}
diff --git a/drivers/rtc/rtc-ab8500.c b/drivers/rtc/rtc-ab8500.c
new file mode 100644
index 000000000000..e346705aae92
--- /dev/null
+++ b/drivers/rtc/rtc-ab8500.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ * Author: Virupax Sadashivpetimath <virupax.sadashivpetimath@stericsson.com>
+ *
+ * RTC clock driver for the RTC part of the AB8500 Power management chip.
+ * Based on RTC clock driver for the AB3100 Analog Baseband Chip by
+ * Linus Walleij <linus.walleij@stericsson.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/ab8500.h>
+#include <linux/delay.h>
+
+#define AB8500_RTC_SOFF_STAT_REG 0x00
+#define AB8500_RTC_CC_CONF_REG 0x01
+#define AB8500_RTC_READ_REQ_REG 0x02
+#define AB8500_RTC_WATCH_TSECMID_REG 0x03
+#define AB8500_RTC_WATCH_TSECHI_REG 0x04
+#define AB8500_RTC_WATCH_TMIN_LOW_REG 0x05
+#define AB8500_RTC_WATCH_TMIN_MID_REG 0x06
+#define AB8500_RTC_WATCH_TMIN_HI_REG 0x07
+#define AB8500_RTC_ALRM_MIN_LOW_REG 0x08
+#define AB8500_RTC_ALRM_MIN_MID_REG 0x09
+#define AB8500_RTC_ALRM_MIN_HI_REG 0x0A
+#define AB8500_RTC_STAT_REG 0x0B
+#define AB8500_RTC_BKUP_CHG_REG 0x0C
+#define AB8500_RTC_FORCE_BKUP_REG 0x0D
+#define AB8500_RTC_CALIB_REG 0x0E
+#define AB8500_RTC_SWITCH_STAT_REG 0x0F
+
+/* RtcReadRequest bits */
+#define RTC_READ_REQUEST 0x01
+#define RTC_WRITE_REQUEST 0x02
+
+/* RtcCtrl bits */
+#define RTC_ALARM_ENA 0x04
+#define RTC_STATUS_DATA 0x01
+
+#define COUNTS_PER_SEC (0xF000 / 60)
+#define AB8500_RTC_EPOCH 2000
+
+static const u8 ab8500_rtc_time_regs[] = {
+ AB8500_RTC_WATCH_TMIN_HI_REG, AB8500_RTC_WATCH_TMIN_MID_REG,
+ AB8500_RTC_WATCH_TMIN_LOW_REG, AB8500_RTC_WATCH_TSECHI_REG,
+ AB8500_RTC_WATCH_TSECMID_REG
+};
+
+static const u8 ab8500_rtc_alarm_regs[] = {
+ AB8500_RTC_ALRM_MIN_HI_REG, AB8500_RTC_ALRM_MIN_MID_REG,
+ AB8500_RTC_ALRM_MIN_LOW_REG
+};
+
+/* Calculate the seconds from 1970 to 01-01-2000 00:00:00 */
+static unsigned long get_elapsed_seconds(int year)
+{
+ unsigned long secs;
+ struct rtc_time tm = {
+ .tm_year = year - 1900,
+ .tm_mday = 1,
+ };
+
+ /*
+ * This function calculates secs from 1970 and not from
+ * 1900, even if we supply the offset from year 1900.
+ */
+ rtc_tm_to_time(&tm, &secs);
+ return secs;
+}
+
+static int ab8500_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned long timeout = jiffies + HZ;
+ int retval, i;
+ unsigned long mins, secs;
+ unsigned char buf[ARRAY_SIZE(ab8500_rtc_time_regs)];
+ u8 value;
+
+ /* Request a data read */
+ retval = abx500_set_register_interruptible(dev,
+ AB8500_RTC, AB8500_RTC_READ_REQ_REG, RTC_READ_REQUEST);
+ if (retval < 0)
+ return retval;
+
+ /* Early AB8500 chips will not clear the rtc read request bit */
+ if (abx500_get_chip_id(dev) == 0) {
+ msleep(1);
+ } else {
+ /* Wait for some cycles after enabling the rtc read in ab8500 */
+ while (time_before(jiffies, timeout)) {
+ retval = abx500_get_register_interruptible(dev,
+ AB8500_RTC, AB8500_RTC_READ_REQ_REG, &value);
+ if (retval < 0)
+ return retval;
+
+ if (!(value & RTC_READ_REQUEST))
+ break;
+
+ msleep(1);
+ }
+ }
+
+ /* Read the Watchtime registers */
+ for (i = 0; i < ARRAY_SIZE(ab8500_rtc_time_regs); i++) {
+ retval = abx500_get_register_interruptible(dev,
+ AB8500_RTC, ab8500_rtc_time_regs[i], &value);
+ if (retval < 0)
+ return retval;
+ buf[i] = value;
+ }
+
+ mins = (buf[0] << 16) | (buf[1] << 8) | buf[2];
+
+ secs = (buf[3] << 8) | buf[4];
+ secs = secs / COUNTS_PER_SEC;
+ secs = secs + (mins * 60);
+
+ /* Add back the initially subtracted number of seconds */
+ secs += get_elapsed_seconds(AB8500_RTC_EPOCH);
+
+ rtc_time_to_tm(secs, tm);
+ return rtc_valid_tm(tm);
+}
+
+static int ab8500_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ int retval, i;
+ unsigned char buf[ARRAY_SIZE(ab8500_rtc_time_regs)];
+ unsigned long no_secs, no_mins, secs = 0;
+
+ if (tm->tm_year < (AB8500_RTC_EPOCH - 1900)) {
+ dev_dbg(dev, "year should be equal to or greater than %d\n",
+ AB8500_RTC_EPOCH);
+ return -EINVAL;
+ }
+
+ /* Get the number of seconds since 1970 */
+ rtc_tm_to_time(tm, &secs);
+
+ /*
+ * Convert it to the number of seconds since 01-01-2000 00:00:00, since
+ * we only have a small counter in the RTC.
+ */
+ secs -= get_elapsed_seconds(AB8500_RTC_EPOCH);
+
+ no_mins = secs / 60;
+
+ no_secs = secs % 60;
+ /* Make the seconds count as per the RTC resolution */
+ no_secs = no_secs * COUNTS_PER_SEC;
+
+ buf[4] = no_secs & 0xFF;
+ buf[3] = (no_secs >> 8) & 0xFF;
+
+ buf[2] = no_mins & 0xFF;
+ buf[1] = (no_mins >> 8) & 0xFF;
+ buf[0] = (no_mins >> 16) & 0xFF;
+
+ for (i = 0; i < ARRAY_SIZE(ab8500_rtc_time_regs); i++) {
+ retval = abx500_set_register_interruptible(dev, AB8500_RTC,
+ ab8500_rtc_time_regs[i], buf[i]);
+ if (retval < 0)
+ return retval;
+ }
+
+ /* Request a data write */
+ return abx500_set_register_interruptible(dev, AB8500_RTC,
+ AB8500_RTC_READ_REQ_REG, RTC_WRITE_REQUEST);
+}
+
+static int ab8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ int retval, i;
+ u8 rtc_ctrl, value;
+ unsigned char buf[ARRAY_SIZE(ab8500_rtc_alarm_regs)];
+ unsigned long secs, mins;
+
+ /* Check if the alarm is enabled or not */
+ retval = abx500_get_register_interruptible(dev, AB8500_RTC,
+ AB8500_RTC_STAT_REG, &rtc_ctrl);
+ if (retval < 0)
+ return retval;
+
+ if (rtc_ctrl & RTC_ALARM_ENA)
+ alarm->enabled = 1;
+ else
+ alarm->enabled = 0;
+
+ alarm->pending = 0;
+
+ for (i = 0; i < ARRAY_SIZE(ab8500_rtc_alarm_regs); i++) {
+ retval = abx500_get_register_interruptible(dev, AB8500_RTC,
+ ab8500_rtc_alarm_regs[i], &value);
+ if (retval < 0)
+ return retval;
+ buf[i] = value;
+ }
+
+ mins = (buf[0] << 16) | (buf[1] << 8) | (buf[2]);
+ secs = mins * 60;
+
+ /* Add back the initially subtracted number of seconds */
+ secs += get_elapsed_seconds(AB8500_RTC_EPOCH);
+
+ rtc_time_to_tm(secs, &alarm->time);
+
+ return rtc_valid_tm(&alarm->time);
+}
+
+static int ab8500_rtc_irq_enable(struct device *dev, unsigned int enabled)
+{
+ return abx500_mask_and_set_register_interruptible(dev, AB8500_RTC,
+ AB8500_RTC_STAT_REG, RTC_ALARM_ENA,
+ enabled ? RTC_ALARM_ENA : 0);
+}
+
+static int ab8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ int retval, i;
+ unsigned char buf[ARRAY_SIZE(ab8500_rtc_alarm_regs)];
+ unsigned long mins, secs = 0;
+
+ if (alarm->time.tm_year < (AB8500_RTC_EPOCH - 1900)) {
+ dev_dbg(dev, "year should be equal to or greater than %d\n",
+ AB8500_RTC_EPOCH);
+ return -EINVAL;
+ }
+
+ /* Get the number of seconds since 1970 */
+ rtc_tm_to_time(&alarm->time, &secs);
+
+ /*
+ * Convert it to the number of seconds since 01-01-2000 00:00:00, since
+ * we only have a small counter in the RTC.
+ */
+ secs -= get_elapsed_seconds(AB8500_RTC_EPOCH);
+
+ mins = secs / 60;
+
+ buf[2] = mins & 0xFF;
+ buf[1] = (mins >> 8) & 0xFF;
+ buf[0] = (mins >> 16) & 0xFF;
+
+ /* Set the alarm time */
+ for (i = 0; i < ARRAY_SIZE(ab8500_rtc_alarm_regs); i++) {
+ retval = abx500_set_register_interruptible(dev, AB8500_RTC,
+ ab8500_rtc_alarm_regs[i], buf[i]);
+ if (retval < 0)
+ return retval;
+ }
+
+ return ab8500_rtc_irq_enable(dev, alarm->enabled);
+}
+
+static irqreturn_t rtc_alarm_handler(int irq, void *data)
+{
+ struct rtc_device *rtc = data;
+ unsigned long events = RTC_IRQF | RTC_AF;
+
+ dev_dbg(&rtc->dev, "%s\n", __func__);
+ rtc_update_irq(rtc, 1, events);
+
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops ab8500_rtc_ops = {
+ .read_time = ab8500_rtc_read_time,
+ .set_time = ab8500_rtc_set_time,
+ .read_alarm = ab8500_rtc_read_alarm,
+ .set_alarm = ab8500_rtc_set_alarm,
+ .alarm_irq_enable = ab8500_rtc_irq_enable,
+};
+
+static int __devinit ab8500_rtc_probe(struct platform_device *pdev)
+{
+ int err;
+ struct rtc_device *rtc;
+ u8 rtc_ctrl;
+ int irq;
+
+ irq = platform_get_irq_byname(pdev, "ALARM");
+ if (irq < 0)
+ return irq;
+
+ /* For RTC supply test */
+ err = abx500_mask_and_set_register_interruptible(&pdev->dev, AB8500_RTC,
+ AB8500_RTC_STAT_REG, RTC_STATUS_DATA, RTC_STATUS_DATA);
+ if (err < 0)
+ return err;
+
+ /* Wait for reset by the PorRtc */
+ msleep(1);
+
+ err = abx500_get_register_interruptible(&pdev->dev, AB8500_RTC,
+ AB8500_RTC_STAT_REG, &rtc_ctrl);
+ if (err < 0)
+ return err;
+
+ /* Check if the RTC Supply fails */
+ if (!(rtc_ctrl & RTC_STATUS_DATA)) {
+ dev_err(&pdev->dev, "RTC supply failure\n");
+ return -ENODEV;
+ }
+
+ rtc = rtc_device_register("ab8500-rtc", &pdev->dev, &ab8500_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ dev_err(&pdev->dev, "Registration failed\n");
+ err = PTR_ERR(rtc);
+ return err;
+ }
+
+ err = request_threaded_irq(irq, NULL, rtc_alarm_handler, 0,
+ "ab8500-rtc", rtc);
+ if (err < 0) {
+ rtc_device_unregister(rtc);
+ return err;
+ }
+
+ platform_set_drvdata(pdev, rtc);
+
+ return 0;
+}
+
+static int __devexit ab8500_rtc_remove(struct platform_device *pdev)
+{
+ struct rtc_device *rtc = platform_get_drvdata(pdev);
+ int irq = platform_get_irq_byname(pdev, "ALARM");
+
+ free_irq(irq, rtc);
+ rtc_device_unregister(rtc);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver ab8500_rtc_driver = {
+ .driver = {
+ .name = "ab8500-rtc",
+ .owner = THIS_MODULE,
+ },
+ .probe = ab8500_rtc_probe,
+ .remove = __devexit_p(ab8500_rtc_remove),
+};
+
+static int __init ab8500_rtc_init(void)
+{
+ return platform_driver_register(&ab8500_rtc_driver);
+}
+
+static void __exit ab8500_rtc_exit(void)
+{
+ platform_driver_unregister(&ab8500_rtc_driver);
+}
+
+module_init(ab8500_rtc_init);
+module_exit(ab8500_rtc_exit);
+MODULE_AUTHOR("Virupax Sadashivpetimath <virupax.sadashivpetimath@stericsson.com>");
+MODULE_DESCRIPTION("AB8500 RTC Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/rtc-at32ap700x.c b/drivers/rtc/rtc-at32ap700x.c
index 8825695777df..b2752b6e7a2f 100644
--- a/drivers/rtc/rtc-at32ap700x.c
+++ b/drivers/rtc/rtc-at32ap700x.c
@@ -11,6 +11,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
#include <linux/rtc.h>
#include <linux/io.h>
diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c
index 86c61f143515..f677e0710ca1 100644
--- a/drivers/rtc/rtc-at91sam9.c
+++ b/drivers/rtc/rtc-at91sam9.c
@@ -18,6 +18,7 @@
#include <linux/rtc.h>
#include <linux/interrupt.h>
#include <linux/ioctl.h>
+#include <linux/slab.h>
#include <mach/board.h>
#include <mach/at91_rtt.h>
@@ -161,7 +162,7 @@ static int at91_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
if (offset == 0)
return -EILSEQ;
- memset(alrm, 0, sizeof(alrm));
+ memset(alrm, 0, sizeof(*alrm));
if (alarm != ALARM_DISABLED && offset != 0) {
rtc_time_to_tm(offset + alarm, tm);
diff --git a/drivers/rtc/rtc-bfin.c b/drivers/rtc/rtc-bfin.c
index b11485b9f21c..b4b6087f2234 100644
--- a/drivers/rtc/rtc-bfin.c
+++ b/drivers/rtc/rtc-bfin.c
@@ -2,7 +2,7 @@
* Blackfin On-Chip Real Time Clock Driver
* Supports BF51x/BF52x/BF53[123]/BF53[467]/BF54x
*
- * Copyright 2004-2009 Analog Devices Inc.
+ * Copyright 2004-2010 Analog Devices Inc.
*
* Enter bugs at http://blackfin.uclinux.org/
*
@@ -51,6 +51,7 @@
#include <linux/platform_device.h>
#include <linux/rtc.h>
#include <linux/seq_file.h>
+#include <linux/slab.h>
#include <asm/blackfin.h>
@@ -182,29 +183,33 @@ static irqreturn_t bfin_rtc_interrupt(int irq, void *dev_id)
struct bfin_rtc *rtc = dev_get_drvdata(dev);
unsigned long events = 0;
bool write_complete = false;
- u16 rtc_istat, rtc_ictl;
+ u16 rtc_istat, rtc_istat_clear, rtc_ictl, bits;
dev_dbg_stamp(dev);
rtc_istat = bfin_read_RTC_ISTAT();
rtc_ictl = bfin_read_RTC_ICTL();
+ rtc_istat_clear = 0;
- if (rtc_istat & RTC_ISTAT_WRITE_COMPLETE) {
- bfin_write_RTC_ISTAT(RTC_ISTAT_WRITE_COMPLETE);
+ bits = RTC_ISTAT_WRITE_COMPLETE;
+ if (rtc_istat & bits) {
+ rtc_istat_clear |= bits;
write_complete = true;
complete(&bfin_write_complete);
}
- if (rtc_ictl & (RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY)) {
- if (rtc_istat & (RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY)) {
- bfin_write_RTC_ISTAT(RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY);
+ bits = (RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY);
+ if (rtc_ictl & bits) {
+ if (rtc_istat & bits) {
+ rtc_istat_clear |= bits;
events |= RTC_AF | RTC_IRQF;
}
}
- if (rtc_ictl & RTC_ISTAT_SEC) {
- if (rtc_istat & RTC_ISTAT_SEC) {
- bfin_write_RTC_ISTAT(RTC_ISTAT_SEC);
+ bits = RTC_ISTAT_SEC;
+ if (rtc_ictl & bits) {
+ if (rtc_istat & bits) {
+ rtc_istat_clear |= bits;
events |= RTC_UF | RTC_IRQF;
}
}
@@ -212,9 +217,10 @@ static irqreturn_t bfin_rtc_interrupt(int irq, void *dev_id)
if (events)
rtc_update_irq(rtc->rtc_dev, 1, events);
- if (write_complete || events)
+ if (write_complete || events) {
+ bfin_write_RTC_ISTAT(rtc_istat_clear);
return IRQ_HANDLED;
- else
+ } else
return IRQ_NONE;
}
@@ -421,21 +427,38 @@ static int __devexit bfin_rtc_remove(struct platform_device *pdev)
#ifdef CONFIG_PM
static int bfin_rtc_suspend(struct platform_device *pdev, pm_message_t state)
{
- if (device_may_wakeup(&pdev->dev)) {
+ struct device *dev = &pdev->dev;
+
+ dev_dbg_stamp(dev);
+
+ if (device_may_wakeup(dev)) {
enable_irq_wake(IRQ_RTC);
- bfin_rtc_sync_pending(&pdev->dev);
+ bfin_rtc_sync_pending(dev);
} else
- bfin_rtc_int_clear(-1);
+ bfin_rtc_int_clear(0);
return 0;
}
static int bfin_rtc_resume(struct platform_device *pdev)
{
- if (device_may_wakeup(&pdev->dev))
+ struct device *dev = &pdev->dev;
+
+ dev_dbg_stamp(dev);
+
+ if (device_may_wakeup(dev))
disable_irq_wake(IRQ_RTC);
- else
- bfin_write_RTC_ISTAT(-1);
+
+ /*
+ * Since only some of the RTC bits are maintained externally in the
+ * Vbat domain, we need to wait for the RTC MMRs to be synced into
+ * the core after waking up. This happens every RTC 1HZ. Once that
+ * has happened, we can go ahead and re-enable the important write
+ * complete interrupt event.
+ */
+ while (!(bfin_read_RTC_ISTAT() & RTC_ISTAT_SEC))
+ continue;
+ bfin_rtc_int_set(RTC_ISTAT_WRITE_COMPLETE);
return 0;
}
diff --git a/drivers/rtc/rtc-bq4802.c b/drivers/rtc/rtc-bq4802.c
index 280fe48ada0b..128270ce355d 100644
--- a/drivers/rtc/rtc-bq4802.c
+++ b/drivers/rtc/rtc-bq4802.c
@@ -10,6 +10,7 @@
#include <linux/platform_device.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
+#include <linux/slab.h>
MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
MODULE_DESCRIPTION("TI BQ4802 RTC driver");
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index e9aa814ddd23..5856167a0c90 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -238,31 +238,32 @@ static int cmos_read_alarm(struct device *dev, struct rtc_wkalrm *t)
rtc_control = CMOS_READ(RTC_CONTROL);
spin_unlock_irq(&rtc_lock);
- /* REVISIT this assumes PC style usage: always BCD */
-
- if (((unsigned)t->time.tm_sec) < 0x60)
- t->time.tm_sec = bcd2bin(t->time.tm_sec);
- else
- t->time.tm_sec = -1;
- if (((unsigned)t->time.tm_min) < 0x60)
- t->time.tm_min = bcd2bin(t->time.tm_min);
- else
- t->time.tm_min = -1;
- if (((unsigned)t->time.tm_hour) < 0x24)
- t->time.tm_hour = bcd2bin(t->time.tm_hour);
- else
- t->time.tm_hour = -1;
-
- if (cmos->day_alrm) {
- if (((unsigned)t->time.tm_mday) <= 0x31)
- t->time.tm_mday = bcd2bin(t->time.tm_mday);
+ if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
+ if (((unsigned)t->time.tm_sec) < 0x60)
+ t->time.tm_sec = bcd2bin(t->time.tm_sec);
else
- t->time.tm_mday = -1;
- if (cmos->mon_alrm) {
- if (((unsigned)t->time.tm_mon) <= 0x12)
- t->time.tm_mon = bcd2bin(t->time.tm_mon) - 1;
+ t->time.tm_sec = -1;
+ if (((unsigned)t->time.tm_min) < 0x60)
+ t->time.tm_min = bcd2bin(t->time.tm_min);
+ else
+ t->time.tm_min = -1;
+ if (((unsigned)t->time.tm_hour) < 0x24)
+ t->time.tm_hour = bcd2bin(t->time.tm_hour);
+ else
+ t->time.tm_hour = -1;
+
+ if (cmos->day_alrm) {
+ if (((unsigned)t->time.tm_mday) <= 0x31)
+ t->time.tm_mday = bcd2bin(t->time.tm_mday);
else
- t->time.tm_mon = -1;
+ t->time.tm_mday = -1;
+
+ if (cmos->mon_alrm) {
+ if (((unsigned)t->time.tm_mon) <= 0x12)
+ t->time.tm_mon = bcd2bin(t->time.tm_mon)-1;
+ else
+ t->time.tm_mon = -1;
+ }
}
}
t->time.tm_year = -1;
@@ -322,29 +323,26 @@ static void cmos_irq_disable(struct cmos_rtc *cmos, unsigned char mask)
static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
{
struct cmos_rtc *cmos = dev_get_drvdata(dev);
- unsigned char mon, mday, hrs, min, sec;
+ unsigned char mon, mday, hrs, min, sec, rtc_control;
if (!is_valid_irq(cmos->irq))
return -EIO;
- /* REVISIT this assumes PC style usage: always BCD */
-
- /* Writing 0xff means "don't care" or "match all". */
-
mon = t->time.tm_mon + 1;
- mon = (mon <= 12) ? bin2bcd(mon) : 0xff;
-
mday = t->time.tm_mday;
- mday = (mday >= 1 && mday <= 31) ? bin2bcd(mday) : 0xff;
-
hrs = t->time.tm_hour;
- hrs = (hrs < 24) ? bin2bcd(hrs) : 0xff;
-
min = t->time.tm_min;
- min = (min < 60) ? bin2bcd(min) : 0xff;
-
sec = t->time.tm_sec;
- sec = (sec < 60) ? bin2bcd(sec) : 0xff;
+
+ rtc_control = CMOS_READ(RTC_CONTROL);
+ if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
+ /* Writing 0xff means "don't care" or "match all". */
+ mon = (mon <= 12) ? bin2bcd(mon) : 0xff;
+ mday = (mday >= 1 && mday <= 31) ? bin2bcd(mday) : 0xff;
+ hrs = (hrs < 24) ? bin2bcd(hrs) : 0xff;
+ min = (min < 60) ? bin2bcd(min) : 0xff;
+ sec = (sec < 60) ? bin2bcd(sec) : 0xff;
+ }
spin_lock_irq(&rtc_lock);
@@ -478,7 +476,7 @@ static int cmos_procfs(struct device *dev, struct seq_file *seq)
"update_IRQ\t: %s\n"
"HPET_emulated\t: %s\n"
// "square_wave\t: %s\n"
- // "BCD\t\t: %s\n"
+ "BCD\t\t: %s\n"
"DST_enable\t: %s\n"
"periodic_freq\t: %d\n"
"batt_status\t: %s\n",
@@ -486,7 +484,7 @@ static int cmos_procfs(struct device *dev, struct seq_file *seq)
(rtc_control & RTC_UIE) ? "yes" : "no",
is_hpet_enabled() ? "yes" : "no",
// (rtc_control & RTC_SQWE) ? "yes" : "no",
- // (rtc_control & RTC_DM_BINARY) ? "no" : "yes",
+ (rtc_control & RTC_DM_BINARY) ? "no" : "yes",
(rtc_control & RTC_DST_EN) ? "yes" : "no",
cmos->rtc->irq_freq,
(valid & RTC_VRT) ? "okay" : "dead");
@@ -519,7 +517,8 @@ static const struct rtc_class_ops cmos_rtc_ops = {
#define NVRAM_OFFSET (RTC_REG_D + 1)
static ssize_t
-cmos_nvram_read(struct kobject *kobj, struct bin_attribute *attr,
+cmos_nvram_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
int retval;
@@ -547,7 +546,8 @@ cmos_nvram_read(struct kobject *kobj, struct bin_attribute *attr,
}
static ssize_t
-cmos_nvram_write(struct kobject *kobj, struct bin_attribute *attr,
+cmos_nvram_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct cmos_rtc *cmos;
@@ -719,6 +719,9 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
}
}
+ cmos_rtc.dev = dev;
+ dev_set_drvdata(dev, &cmos_rtc);
+
cmos_rtc.rtc = rtc_device_register(driver_name, dev,
&cmos_rtc_ops, THIS_MODULE);
if (IS_ERR(cmos_rtc.rtc)) {
@@ -726,8 +729,6 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
goto cleanup0;
}
- cmos_rtc.dev = dev;
- dev_set_drvdata(dev, &cmos_rtc);
rename_region(ports, dev_name(&cmos_rtc.rtc->dev));
spin_lock_irq(&rtc_lock);
@@ -749,12 +750,11 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
spin_unlock_irq(&rtc_lock);
- /* FIXME teach the alarm code how to handle binary mode;
+ /* FIXME:
* <asm-generic/rtc.h> doesn't know 12-hour mode either.
*/
- if (is_valid_irq(rtc_irq) &&
- (!(rtc_control & RTC_24H) || (rtc_control & (RTC_DM_BINARY)))) {
- dev_dbg(dev, "only 24-hr BCD mode supported\n");
+ if (is_valid_irq(rtc_irq) && !(rtc_control & RTC_24H)) {
+ dev_warn(dev, "only 24-hr supported\n");
retval = -ENXIO;
goto cleanup1;
}
@@ -970,7 +970,6 @@ static inline int cmos_poweroff(struct device *dev)
#include <linux/acpi.h>
-#ifdef CONFIG_PM
static u32 rtc_handler(void *context)
{
acpi_clear_event(ACPI_EVENT_RTC);
@@ -999,11 +998,6 @@ static void rtc_wake_off(struct device *dev)
{
acpi_disable_event(ACPI_EVENT_RTC, 0);
}
-#else
-#define rtc_wake_setup() do{}while(0)
-#define rtc_wake_on NULL
-#define rtc_wake_off NULL
-#endif
/* Every ACPI platform has a mc146818 compatible "cmos rtc". Here we find
* its device node and pass extra config data. This helps its driver use
diff --git a/drivers/rtc/rtc-coh901331.c b/drivers/rtc/rtc-coh901331.c
index 03ea530981d1..316f484999b5 100644
--- a/drivers/rtc/rtc-coh901331.c
+++ b/drivers/rtc/rtc-coh901331.c
@@ -14,6 +14,7 @@
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/slab.h>
/*
* Registers in the COH 901 331
@@ -271,12 +272,13 @@ static int coh901331_resume(struct platform_device *pdev)
{
struct coh901331_port *rtap = dev_get_drvdata(&pdev->dev);
- if (device_may_wakeup(&pdev->dev))
+ if (device_may_wakeup(&pdev->dev)) {
disable_irq_wake(rtap->irq);
- else
+ } else {
clk_enable(rtap->clk);
writel(rtap->irqmaskstore, rtap->virtbase + COH901331_IRQ_MASK);
clk_disable(rtap->clk);
+ }
return 0;
}
#else
diff --git a/drivers/rtc/rtc-davinci.c b/drivers/rtc/rtc-davinci.c
new file mode 100644
index 000000000000..34647fc1ee98
--- /dev/null
+++ b/drivers/rtc/rtc-davinci.c
@@ -0,0 +1,674 @@
+/*
+ * DaVinci Power Management and Real Time Clock Driver for TI platforms
+ *
+ * Copyright (C) 2009 Texas Instruments, Inc
+ *
+ * Author: Miguel Aguilar <miguel.aguilar@ridgerun.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+/*
+ * The DaVinci RTC is a simple RTC with the following
+ * Sec: 0 - 59 : BCD count
+ * Min: 0 - 59 : BCD count
+ * Hour: 0 - 23 : BCD count
+ * Day: 0 - 0x7FFF(32767) : Binary count ( Over 89 years )
+ */
+
+/* PRTC interface registers */
+#define DAVINCI_PRTCIF_PID 0x00
+#define PRTCIF_CTLR 0x04
+#define PRTCIF_LDATA 0x08
+#define PRTCIF_UDATA 0x0C
+#define PRTCIF_INTEN 0x10
+#define PRTCIF_INTFLG 0x14
+
+/* PRTCIF_CTLR bit fields */
+#define PRTCIF_CTLR_BUSY BIT(31)
+#define PRTCIF_CTLR_SIZE BIT(25)
+#define PRTCIF_CTLR_DIR BIT(24)
+#define PRTCIF_CTLR_BENU_MSB BIT(23)
+#define PRTCIF_CTLR_BENU_3RD_BYTE BIT(22)
+#define PRTCIF_CTLR_BENU_2ND_BYTE BIT(21)
+#define PRTCIF_CTLR_BENU_LSB BIT(20)
+#define PRTCIF_CTLR_BENU_MASK (0x00F00000)
+#define PRTCIF_CTLR_BENL_MSB BIT(19)
+#define PRTCIF_CTLR_BENL_3RD_BYTE BIT(18)
+#define PRTCIF_CTLR_BENL_2ND_BYTE BIT(17)
+#define PRTCIF_CTLR_BENL_LSB BIT(16)
+#define PRTCIF_CTLR_BENL_MASK (0x000F0000)
+
+/* PRTCIF_INTEN bit fields */
+#define PRTCIF_INTEN_RTCSS BIT(1)
+#define PRTCIF_INTEN_RTCIF BIT(0)
+#define PRTCIF_INTEN_MASK (PRTCIF_INTEN_RTCSS \
+ | PRTCIF_INTEN_RTCIF)
+
+/* PRTCIF_INTFLG bit fields */
+#define PRTCIF_INTFLG_RTCSS BIT(1)
+#define PRTCIF_INTFLG_RTCIF BIT(0)
+#define PRTCIF_INTFLG_MASK (PRTCIF_INTFLG_RTCSS \
+ | PRTCIF_INTFLG_RTCIF)
+
+/* PRTC subsystem registers */
+#define PRTCSS_RTC_INTC_EXTENA1 (0x0C)
+#define PRTCSS_RTC_CTRL (0x10)
+#define PRTCSS_RTC_WDT (0x11)
+#define PRTCSS_RTC_TMR0 (0x12)
+#define PRTCSS_RTC_TMR1 (0x13)
+#define PRTCSS_RTC_CCTRL (0x14)
+#define PRTCSS_RTC_SEC (0x15)
+#define PRTCSS_RTC_MIN (0x16)
+#define PRTCSS_RTC_HOUR (0x17)
+#define PRTCSS_RTC_DAY0 (0x18)
+#define PRTCSS_RTC_DAY1 (0x19)
+#define PRTCSS_RTC_AMIN (0x1A)
+#define PRTCSS_RTC_AHOUR (0x1B)
+#define PRTCSS_RTC_ADAY0 (0x1C)
+#define PRTCSS_RTC_ADAY1 (0x1D)
+#define PRTCSS_RTC_CLKC_CNT (0x20)
+
+/* PRTCSS_RTC_INTC_EXTENA1 */
+#define PRTCSS_RTC_INTC_EXTENA1_MASK (0x07)
+
+/* PRTCSS_RTC_CTRL bit fields */
+#define PRTCSS_RTC_CTRL_WDTBUS BIT(7)
+#define PRTCSS_RTC_CTRL_WEN BIT(6)
+#define PRTCSS_RTC_CTRL_WDRT BIT(5)
+#define PRTCSS_RTC_CTRL_WDTFLG BIT(4)
+#define PRTCSS_RTC_CTRL_TE BIT(3)
+#define PRTCSS_RTC_CTRL_TIEN BIT(2)
+#define PRTCSS_RTC_CTRL_TMRFLG BIT(1)
+#define PRTCSS_RTC_CTRL_TMMD BIT(0)
+
+/* PRTCSS_RTC_CCTRL bit fields */
+#define PRTCSS_RTC_CCTRL_CALBUSY BIT(7)
+#define PRTCSS_RTC_CCTRL_DAEN BIT(5)
+#define PRTCSS_RTC_CCTRL_HAEN BIT(4)
+#define PRTCSS_RTC_CCTRL_MAEN BIT(3)
+#define PRTCSS_RTC_CCTRL_ALMFLG BIT(2)
+#define PRTCSS_RTC_CCTRL_AIEN BIT(1)
+#define PRTCSS_RTC_CCTRL_CAEN BIT(0)
+
+static DEFINE_SPINLOCK(davinci_rtc_lock);
+
+struct davinci_rtc {
+ struct rtc_device *rtc;
+ void __iomem *base;
+ resource_size_t pbase;
+ size_t base_size;
+ int irq;
+};
+
+static inline void rtcif_write(struct davinci_rtc *davinci_rtc,
+ u32 val, u32 addr)
+{
+ writel(val, davinci_rtc->base + addr);
+}
+
+static inline u32 rtcif_read(struct davinci_rtc *davinci_rtc, u32 addr)
+{
+ return readl(davinci_rtc->base + addr);
+}
+
+static inline void rtcif_wait(struct davinci_rtc *davinci_rtc)
+{
+ while (rtcif_read(davinci_rtc, PRTCIF_CTLR) & PRTCIF_CTLR_BUSY)
+ cpu_relax();
+}
+
+static inline void rtcss_write(struct davinci_rtc *davinci_rtc,
+ unsigned long val, u8 addr)
+{
+ rtcif_wait(davinci_rtc);
+
+ rtcif_write(davinci_rtc, PRTCIF_CTLR_BENL_LSB | addr, PRTCIF_CTLR);
+ rtcif_write(davinci_rtc, val, PRTCIF_LDATA);
+
+ rtcif_wait(davinci_rtc);
+}
+
+static inline u8 rtcss_read(struct davinci_rtc *davinci_rtc, u8 addr)
+{
+ rtcif_wait(davinci_rtc);
+
+ rtcif_write(davinci_rtc, PRTCIF_CTLR_DIR | PRTCIF_CTLR_BENL_LSB | addr,
+ PRTCIF_CTLR);
+
+ rtcif_wait(davinci_rtc);
+
+ return rtcif_read(davinci_rtc, PRTCIF_LDATA);
+}
+
+static inline void davinci_rtcss_calendar_wait(struct davinci_rtc *davinci_rtc)
+{
+ while (rtcss_read(davinci_rtc, PRTCSS_RTC_CCTRL) &
+ PRTCSS_RTC_CCTRL_CALBUSY)
+ cpu_relax();
+}
+
+static irqreturn_t davinci_rtc_interrupt(int irq, void *class_dev)
+{
+ struct davinci_rtc *davinci_rtc = class_dev;
+ unsigned long events = 0;
+ u32 irq_flg;
+ u8 alm_irq, tmr_irq;
+ u8 rtc_ctrl, rtc_cctrl;
+ int ret = IRQ_NONE;
+
+ irq_flg = rtcif_read(davinci_rtc, PRTCIF_INTFLG) &
+ PRTCIF_INTFLG_RTCSS;
+
+ alm_irq = rtcss_read(davinci_rtc, PRTCSS_RTC_CCTRL) &
+ PRTCSS_RTC_CCTRL_ALMFLG;
+
+ tmr_irq = rtcss_read(davinci_rtc, PRTCSS_RTC_CTRL) &
+ PRTCSS_RTC_CTRL_TMRFLG;
+
+ if (irq_flg) {
+ if (alm_irq) {
+ events |= RTC_IRQF | RTC_AF;
+ rtc_cctrl = rtcss_read(davinci_rtc, PRTCSS_RTC_CCTRL);
+ rtc_cctrl |= PRTCSS_RTC_CCTRL_ALMFLG;
+ rtcss_write(davinci_rtc, rtc_cctrl, PRTCSS_RTC_CCTRL);
+ } else if (tmr_irq) {
+ events |= RTC_IRQF | RTC_PF;
+ rtc_ctrl = rtcss_read(davinci_rtc, PRTCSS_RTC_CTRL);
+ rtc_ctrl |= PRTCSS_RTC_CTRL_TMRFLG;
+ rtcss_write(davinci_rtc, rtc_ctrl, PRTCSS_RTC_CTRL);
+ }
+
+ rtcif_write(davinci_rtc, PRTCIF_INTFLG_RTCSS,
+ PRTCIF_INTFLG);
+ rtc_update_irq(davinci_rtc->rtc, 1, events);
+
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+static int
+davinci_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+{
+ struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev);
+ u8 rtc_ctrl;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&davinci_rtc_lock, flags);
+
+ rtc_ctrl = rtcss_read(davinci_rtc, PRTCSS_RTC_CTRL);
+
+ switch (cmd) {
+ case RTC_WIE_ON:
+ rtc_ctrl |= PRTCSS_RTC_CTRL_WEN | PRTCSS_RTC_CTRL_WDTFLG;
+ break;
+ case RTC_WIE_OFF:
+ rtc_ctrl &= ~PRTCSS_RTC_CTRL_WEN;
+ break;
+ case RTC_UIE_OFF:
+ case RTC_UIE_ON:
+ ret = -ENOTTY;
+ break;
+ default:
+ ret = -ENOIOCTLCMD;
+ }
+
+ rtcss_write(davinci_rtc, rtc_ctrl, PRTCSS_RTC_CTRL);
+
+ spin_unlock_irqrestore(&davinci_rtc_lock, flags);
+
+ return ret;
+}
+
+static int convertfromdays(u16 days, struct rtc_time *tm)
+{
+ int tmp_days, year, mon;
+
+ for (year = 2000;; year++) {
+ tmp_days = rtc_year_days(1, 12, year);
+ if (days >= tmp_days)
+ days -= tmp_days;
+ else {
+ for (mon = 0;; mon++) {
+ tmp_days = rtc_month_days(mon, year);
+ if (days >= tmp_days) {
+ days -= tmp_days;
+ } else {
+ tm->tm_year = year - 1900;
+ tm->tm_mon = mon;
+ tm->tm_mday = days + 1;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+static int convert2days(u16 *days, struct rtc_time *tm)
+{
+ int i;
+ *days = 0;
+
+ /* epoch == 1900 */
+ if (tm->tm_year < 100 || tm->tm_year > 199)
+ return -EINVAL;
+
+ for (i = 2000; i < 1900 + tm->tm_year; i++)
+ *days += rtc_year_days(1, 12, i);
+
+ *days += rtc_year_days(tm->tm_mday, tm->tm_mon, 1900 + tm->tm_year);
+
+ return 0;
+}
+
+static int davinci_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev);
+ u16 days = 0;
+ u8 day0, day1;
+ unsigned long flags;
+
+ spin_lock_irqsave(&davinci_rtc_lock, flags);
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ tm->tm_sec = bcd2bin(rtcss_read(davinci_rtc, PRTCSS_RTC_SEC));
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ tm->tm_min = bcd2bin(rtcss_read(davinci_rtc, PRTCSS_RTC_MIN));
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ tm->tm_hour = bcd2bin(rtcss_read(davinci_rtc, PRTCSS_RTC_HOUR));
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ day0 = rtcss_read(davinci_rtc, PRTCSS_RTC_DAY0);
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ day1 = rtcss_read(davinci_rtc, PRTCSS_RTC_DAY1);
+
+ spin_unlock_irqrestore(&davinci_rtc_lock, flags);
+
+ days |= day1;
+ days <<= 8;
+ days |= day0;
+
+ if (convertfromdays(days, tm) < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int davinci_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev);
+ u16 days;
+ u8 rtc_cctrl;
+ unsigned long flags;
+
+ if (convert2days(&days, tm) < 0)
+ return -EINVAL;
+
+ spin_lock_irqsave(&davinci_rtc_lock, flags);
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ rtcss_write(davinci_rtc, bin2bcd(tm->tm_sec), PRTCSS_RTC_SEC);
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ rtcss_write(davinci_rtc, bin2bcd(tm->tm_min), PRTCSS_RTC_MIN);
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ rtcss_write(davinci_rtc, bin2bcd(tm->tm_hour), PRTCSS_RTC_HOUR);
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ rtcss_write(davinci_rtc, days & 0xFF, PRTCSS_RTC_DAY0);
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ rtcss_write(davinci_rtc, (days & 0xFF00) >> 8, PRTCSS_RTC_DAY1);
+
+ rtc_cctrl = rtcss_read(davinci_rtc, PRTCSS_RTC_CCTRL);
+ rtc_cctrl |= PRTCSS_RTC_CCTRL_CAEN;
+ rtcss_write(davinci_rtc, rtc_cctrl, PRTCSS_RTC_CCTRL);
+
+ spin_unlock_irqrestore(&davinci_rtc_lock, flags);
+
+ return 0;
+}
+
+static int davinci_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev);
+ unsigned long flags;
+ u8 rtc_cctrl = rtcss_read(davinci_rtc, PRTCSS_RTC_CCTRL);
+
+ spin_lock_irqsave(&davinci_rtc_lock, flags);
+
+ if (enabled)
+ rtc_cctrl |= PRTCSS_RTC_CCTRL_DAEN |
+ PRTCSS_RTC_CCTRL_HAEN |
+ PRTCSS_RTC_CCTRL_MAEN |
+ PRTCSS_RTC_CCTRL_ALMFLG |
+ PRTCSS_RTC_CCTRL_AIEN;
+ else
+ rtc_cctrl &= ~PRTCSS_RTC_CCTRL_AIEN;
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ rtcss_write(davinci_rtc, rtc_cctrl, PRTCSS_RTC_CCTRL);
+
+ spin_unlock_irqrestore(&davinci_rtc_lock, flags);
+
+ return 0;
+}
+
+static int davinci_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev);
+ u16 days = 0;
+ u8 day0, day1;
+ unsigned long flags;
+
+ spin_lock_irqsave(&davinci_rtc_lock, flags);
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ alm->time.tm_min = bcd2bin(rtcss_read(davinci_rtc, PRTCSS_RTC_AMIN));
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ alm->time.tm_hour = bcd2bin(rtcss_read(davinci_rtc, PRTCSS_RTC_AHOUR));
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ day0 = rtcss_read(davinci_rtc, PRTCSS_RTC_ADAY0);
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ day1 = rtcss_read(davinci_rtc, PRTCSS_RTC_ADAY1);
+
+ spin_unlock_irqrestore(&davinci_rtc_lock, flags);
+ days |= day1;
+ days <<= 8;
+ days |= day0;
+
+ if (convertfromdays(days, &alm->time) < 0)
+ return -EINVAL;
+
+ alm->pending = !!(rtcss_read(davinci_rtc,
+ PRTCSS_RTC_CCTRL) &
+ PRTCSS_RTC_CCTRL_AIEN);
+ alm->enabled = alm->pending && device_may_wakeup(dev);
+
+ return 0;
+}
+
+static int davinci_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev);
+ unsigned long flags;
+ u16 days;
+
+ if (alm->time.tm_mday <= 0 && alm->time.tm_mon < 0
+ && alm->time.tm_year < 0) {
+ struct rtc_time tm;
+ unsigned long now, then;
+
+ davinci_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);
+
+ 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 (convert2days(&days, &alm->time) < 0)
+ return -EINVAL;
+
+ spin_lock_irqsave(&davinci_rtc_lock, flags);
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ rtcss_write(davinci_rtc, bin2bcd(alm->time.tm_min), PRTCSS_RTC_AMIN);
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ rtcss_write(davinci_rtc, bin2bcd(alm->time.tm_hour), PRTCSS_RTC_AHOUR);
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ rtcss_write(davinci_rtc, days & 0xFF, PRTCSS_RTC_ADAY0);
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ rtcss_write(davinci_rtc, (days & 0xFF00) >> 8, PRTCSS_RTC_ADAY1);
+
+ spin_unlock_irqrestore(&davinci_rtc_lock, flags);
+
+ return 0;
+}
+
+static int davinci_rtc_irq_set_state(struct device *dev, int enabled)
+{
+ struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev);
+ unsigned long flags;
+ u8 rtc_ctrl;
+
+ spin_lock_irqsave(&davinci_rtc_lock, flags);
+
+ rtc_ctrl = rtcss_read(davinci_rtc, PRTCSS_RTC_CTRL);
+
+ if (enabled) {
+ while (rtcss_read(davinci_rtc, PRTCSS_RTC_CTRL)
+ & PRTCSS_RTC_CTRL_WDTBUS)
+ cpu_relax();
+
+ rtc_ctrl |= PRTCSS_RTC_CTRL_TE;
+ rtcss_write(davinci_rtc, rtc_ctrl, PRTCSS_RTC_CTRL);
+
+ rtcss_write(davinci_rtc, 0x0, PRTCSS_RTC_CLKC_CNT);
+
+ rtc_ctrl |= PRTCSS_RTC_CTRL_TIEN |
+ PRTCSS_RTC_CTRL_TMMD |
+ PRTCSS_RTC_CTRL_TMRFLG;
+ } else
+ rtc_ctrl &= ~PRTCSS_RTC_CTRL_TIEN;
+
+ rtcss_write(davinci_rtc, rtc_ctrl, PRTCSS_RTC_CTRL);
+
+ spin_unlock_irqrestore(&davinci_rtc_lock, flags);
+
+ return 0;
+}
+
+static int davinci_rtc_irq_set_freq(struct device *dev, int freq)
+{
+ struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev);
+ unsigned long flags;
+ u16 tmr_counter = (0x8000 >> (ffs(freq) - 1));
+
+ spin_lock_irqsave(&davinci_rtc_lock, flags);
+
+ rtcss_write(davinci_rtc, tmr_counter & 0xFF, PRTCSS_RTC_TMR0);
+ rtcss_write(davinci_rtc, (tmr_counter & 0xFF00) >> 8, PRTCSS_RTC_TMR1);
+
+ spin_unlock_irqrestore(&davinci_rtc_lock, flags);
+
+ return 0;
+}
+
+static struct rtc_class_ops davinci_rtc_ops = {
+ .ioctl = davinci_rtc_ioctl,
+ .read_time = davinci_rtc_read_time,
+ .set_time = davinci_rtc_set_time,
+ .alarm_irq_enable = davinci_rtc_alarm_irq_enable,
+ .read_alarm = davinci_rtc_read_alarm,
+ .set_alarm = davinci_rtc_set_alarm,
+ .irq_set_state = davinci_rtc_irq_set_state,
+ .irq_set_freq = davinci_rtc_irq_set_freq,
+};
+
+static int __init davinci_rtc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct davinci_rtc *davinci_rtc;
+ struct resource *res, *mem;
+ int ret = 0;
+
+ davinci_rtc = kzalloc(sizeof(struct davinci_rtc), GFP_KERNEL);
+ if (!davinci_rtc) {
+ dev_dbg(dev, "could not allocate memory for private data\n");
+ return -ENOMEM;
+ }
+
+ davinci_rtc->irq = platform_get_irq(pdev, 0);
+ if (davinci_rtc->irq < 0) {
+ dev_err(dev, "no RTC irq\n");
+ ret = davinci_rtc->irq;
+ goto fail1;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "no mem resource\n");
+ ret = -EINVAL;
+ goto fail1;
+ }
+
+ davinci_rtc->pbase = res->start;
+ davinci_rtc->base_size = resource_size(res);
+
+ mem = request_mem_region(davinci_rtc->pbase, davinci_rtc->base_size,
+ pdev->name);
+ if (!mem) {
+ dev_err(dev, "RTC registers at %08x are not free\n",
+ davinci_rtc->pbase);
+ ret = -EBUSY;
+ goto fail1;
+ }
+
+ davinci_rtc->base = ioremap(davinci_rtc->pbase, davinci_rtc->base_size);
+ if (!davinci_rtc->base) {
+ dev_err(dev, "unable to ioremap MEM resource\n");
+ ret = -ENOMEM;
+ goto fail2;
+ }
+
+ davinci_rtc->rtc = rtc_device_register(pdev->name, &pdev->dev,
+ &davinci_rtc_ops, THIS_MODULE);
+ if (IS_ERR(davinci_rtc->rtc)) {
+ dev_err(dev, "unable to register RTC device, err %ld\n",
+ PTR_ERR(davinci_rtc->rtc));
+ goto fail3;
+ }
+
+ rtcif_write(davinci_rtc, PRTCIF_INTFLG_RTCSS, PRTCIF_INTFLG);
+ rtcif_write(davinci_rtc, 0, PRTCIF_INTEN);
+ rtcss_write(davinci_rtc, 0, PRTCSS_RTC_INTC_EXTENA1);
+
+ rtcss_write(davinci_rtc, 0, PRTCSS_RTC_CTRL);
+ rtcss_write(davinci_rtc, 0, PRTCSS_RTC_CCTRL);
+
+ ret = request_irq(davinci_rtc->irq, davinci_rtc_interrupt,
+ IRQF_DISABLED, "davinci_rtc", davinci_rtc);
+ if (ret < 0) {
+ dev_err(dev, "unable to register davinci RTC interrupt\n");
+ goto fail4;
+ }
+
+ /* Enable interrupts */
+ rtcif_write(davinci_rtc, PRTCIF_INTEN_RTCSS, PRTCIF_INTEN);
+ rtcss_write(davinci_rtc, PRTCSS_RTC_INTC_EXTENA1_MASK,
+ PRTCSS_RTC_INTC_EXTENA1);
+
+ rtcss_write(davinci_rtc, PRTCSS_RTC_CCTRL_CAEN, PRTCSS_RTC_CCTRL);
+
+ platform_set_drvdata(pdev, davinci_rtc);
+
+ device_init_wakeup(&pdev->dev, 0);
+
+ return 0;
+
+fail4:
+ rtc_device_unregister(davinci_rtc->rtc);
+fail3:
+ iounmap(davinci_rtc->base);
+fail2:
+ release_mem_region(davinci_rtc->pbase, davinci_rtc->base_size);
+fail1:
+ kfree(davinci_rtc);
+
+ return ret;
+}
+
+static int __devexit davinci_rtc_remove(struct platform_device *pdev)
+{
+ struct davinci_rtc *davinci_rtc = platform_get_drvdata(pdev);
+
+ device_init_wakeup(&pdev->dev, 0);
+
+ rtcif_write(davinci_rtc, 0, PRTCIF_INTEN);
+
+ free_irq(davinci_rtc->irq, davinci_rtc);
+
+ rtc_device_unregister(davinci_rtc->rtc);
+
+ iounmap(davinci_rtc->base);
+ release_mem_region(davinci_rtc->pbase, davinci_rtc->base_size);
+
+ platform_set_drvdata(pdev, NULL);
+
+ kfree(davinci_rtc);
+
+ return 0;
+}
+
+static struct platform_driver davinci_rtc_driver = {
+ .probe = davinci_rtc_probe,
+ .remove = __devexit_p(davinci_rtc_remove),
+ .driver = {
+ .name = "rtc_davinci",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init rtc_init(void)
+{
+ return platform_driver_probe(&davinci_rtc_driver, davinci_rtc_probe);
+}
+module_init(rtc_init);
+
+static void __exit rtc_exit(void)
+{
+ platform_driver_unregister(&davinci_rtc_driver);
+}
+module_exit(rtc_exit);
+
+MODULE_AUTHOR("Miguel Aguilar <miguel.aguilar@ridgerun.com>");
+MODULE_DESCRIPTION("Texas Instruments DaVinci PRTC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-ds1216.c b/drivers/rtc/rtc-ds1216.c
index 4aedc705518c..45cd8c9f5a39 100644
--- a/drivers/rtc/rtc-ds1216.c
+++ b/drivers/rtc/rtc-ds1216.c
@@ -9,6 +9,7 @@
#include <linux/rtc.h>
#include <linux/platform_device.h>
#include <linux/bcd.h>
+#include <linux/slab.h>
#define DRV_VERSION "0.2"
diff --git a/drivers/rtc/rtc-ds1286.c b/drivers/rtc/rtc-ds1286.c
index 4fcb16bbff4a..bf430f9091ed 100644
--- a/drivers/rtc/rtc-ds1286.c
+++ b/drivers/rtc/rtc-ds1286.c
@@ -18,6 +18,7 @@
#include <linux/bcd.h>
#include <linux/ds1286.h>
#include <linux/io.h>
+#include <linux/slab.h>
#define DRV_VERSION "1.0"
diff --git a/drivers/rtc/rtc-ds1302.c b/drivers/rtc/rtc-ds1302.c
index 532acf9b05d8..359d1e04626c 100644
--- a/drivers/rtc/rtc-ds1302.c
+++ b/drivers/rtc/rtc-ds1302.c
@@ -16,7 +16,6 @@
#include <linux/rtc.h>
#include <linux/io.h>
#include <linux/bcd.h>
-#include <asm/rtc.h>
#define DRV_NAME "rtc-ds1302"
#define DRV_VERSION "0.1.1"
@@ -34,14 +33,55 @@
#define RTC_ADDR_MIN 0x01 /* Address of minute register */
#define RTC_ADDR_SEC 0x00 /* Address of second register */
+#ifdef CONFIG_SH_SECUREEDGE5410
+#include <asm/rtc.h>
+#include <mach/snapgear.h>
+
#define RTC_RESET 0x1000
#define RTC_IODATA 0x0800
#define RTC_SCLK 0x0400
-#ifdef CONFIG_SH_SECUREEDGE5410
-#include <mach/snapgear.h>
#define set_dp(x) SECUREEDGE_WRITE_IOPORT(x, 0x1c00)
#define get_dp() SECUREEDGE_READ_IOPORT()
+#define ds1302_set_tx()
+#define ds1302_set_rx()
+
+static inline int ds1302_hw_init(void)
+{
+ return 0;
+}
+
+static inline void ds1302_reset(void)
+{
+ set_dp(get_dp() & ~(RTC_RESET | RTC_IODATA | RTC_SCLK));
+}
+
+static inline void ds1302_clock(void)
+{
+ set_dp(get_dp() | RTC_SCLK); /* clock high */
+ set_dp(get_dp() & ~RTC_SCLK); /* clock low */
+}
+
+static inline void ds1302_start(void)
+{
+ set_dp(get_dp() | RTC_RESET);
+}
+
+static inline void ds1302_stop(void)
+{
+ set_dp(get_dp() & ~RTC_RESET);
+}
+
+static inline void ds1302_txbit(int bit)
+{
+ set_dp((get_dp() & ~RTC_IODATA) | (bit ? RTC_IODATA : 0));
+}
+
+static inline int ds1302_rxbit(void)
+{
+ return !!(get_dp() & RTC_IODATA);
+}
+
#else
#error "Add support for your platform"
#endif
@@ -50,11 +90,11 @@ static void ds1302_sendbits(unsigned int val)
{
int i;
+ ds1302_set_tx();
+
for (i = 8; (i); i--, val >>= 1) {
- set_dp((get_dp() & ~RTC_IODATA) | ((val & 0x1) ?
- RTC_IODATA : 0));
- set_dp(get_dp() | RTC_SCLK); /* clock high */
- set_dp(get_dp() & ~RTC_SCLK); /* clock low */
+ ds1302_txbit(val & 0x1);
+ ds1302_clock();
}
}
@@ -63,10 +103,11 @@ static unsigned int ds1302_recvbits(void)
unsigned int val;
int i;
+ ds1302_set_rx();
+
for (i = 0, val = 0; (i < 8); i++) {
- val |= (((get_dp() & RTC_IODATA) ? 1 : 0) << i);
- set_dp(get_dp() | RTC_SCLK); /* clock high */
- set_dp(get_dp() & ~RTC_SCLK); /* clock low */
+ val |= (ds1302_rxbit() << i);
+ ds1302_clock();
}
return val;
@@ -76,23 +117,24 @@ static unsigned int ds1302_readbyte(unsigned int addr)
{
unsigned int val;
- set_dp(get_dp() & ~(RTC_RESET | RTC_IODATA | RTC_SCLK));
+ ds1302_reset();
- set_dp(get_dp() | RTC_RESET);
+ ds1302_start();
ds1302_sendbits(((addr & 0x3f) << 1) | RTC_CMD_READ);
val = ds1302_recvbits();
- set_dp(get_dp() & ~RTC_RESET);
+ ds1302_stop();
return val;
}
static void ds1302_writebyte(unsigned int addr, unsigned int val)
{
- set_dp(get_dp() & ~(RTC_RESET | RTC_IODATA | RTC_SCLK));
- set_dp(get_dp() | RTC_RESET);
+ ds1302_reset();
+
+ ds1302_start();
ds1302_sendbits(((addr & 0x3f) << 1) | RTC_CMD_WRITE);
ds1302_sendbits(val);
- set_dp(get_dp() & ~RTC_RESET);
+ ds1302_stop();
}
static int ds1302_rtc_read_time(struct device *dev, struct rtc_time *tm)
@@ -167,13 +209,20 @@ static int __init ds1302_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
+ if (ds1302_hw_init()) {
+ dev_err(&pdev->dev, "Failed to init communication channel");
+ return -EINVAL;
+ }
+
/* Reset */
- set_dp(get_dp() & ~(RTC_RESET | RTC_IODATA | RTC_SCLK));
+ ds1302_reset();
/* Write a magic value to the DS1302 RAM, and see if it sticks. */
ds1302_writebyte(RTC_ADDR_RAM0, 0x42);
- if (ds1302_readbyte(RTC_ADDR_RAM0) != 0x42)
+ if (ds1302_readbyte(RTC_ADDR_RAM0) != 0x42) {
+ dev_err(&pdev->dev, "Failed to probe");
return -ENODEV;
+ }
rtc = rtc_device_register("ds1302", &pdev->dev,
&ds1302_rtc_ops, THIS_MODULE);
diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c
index 9630e7d3314e..48da85e97ca4 100644
--- a/drivers/rtc/rtc-ds1305.c
+++ b/drivers/rtc/rtc-ds1305.c
@@ -11,6 +11,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/bcd.h>
+#include <linux/slab.h>
#include <linux/rtc.h>
#include <linux/workqueue.h>
@@ -541,7 +542,8 @@ static void msg_init(struct spi_message *m, struct spi_transfer *x,
}
static ssize_t
-ds1305_nvram_read(struct kobject *kobj, struct bin_attribute *attr,
+ds1305_nvram_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct spi_device *spi;
@@ -571,7 +573,8 @@ ds1305_nvram_read(struct kobject *kobj, struct bin_attribute *attr,
}
static ssize_t
-ds1305_nvram_write(struct kobject *kobj, struct bin_attribute *attr,
+ds1305_nvram_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct spi_device *spi;
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index c4ec5c158aa1..d827ce570a8c 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -556,7 +556,8 @@ static const struct rtc_class_ops ds13xx_rtc_ops = {
#define NVRAM_SIZE 56
static ssize_t
-ds1307_nvram_read(struct kobject *kobj, struct bin_attribute *attr,
+ds1307_nvram_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct i2c_client *client;
@@ -580,7 +581,8 @@ ds1307_nvram_read(struct kobject *kobj, struct bin_attribute *attr,
}
static ssize_t
-ds1307_nvram_write(struct kobject *kobj, struct bin_attribute *attr,
+ds1307_nvram_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct i2c_client *client;
@@ -775,7 +777,7 @@ static int __devinit ds1307_probe(struct i2c_client *client,
read_rtc:
/* read RTC registers */
- tmp = ds1307->read_block_data(ds1307->client, 0, 8, buf);
+ tmp = ds1307->read_block_data(ds1307->client, ds1307->offset, 8, buf);
if (tmp != 8) {
pr_debug("read error %d\n", tmp);
err = -EIO;
@@ -860,7 +862,7 @@ read_rtc:
if (ds1307->regs[DS1307_REG_HOUR] & DS1307_BIT_PM)
tmp += 12;
i2c_smbus_write_byte_data(client,
- DS1307_REG_HOUR,
+ ds1307->offset + DS1307_REG_HOUR,
bin2bcd(tmp));
}
diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c
index 5317bbcbc7a0..1f0007fd4431 100644
--- a/drivers/rtc/rtc-ds1374.c
+++ b/drivers/rtc/rtc-ds1374.c
@@ -24,6 +24,7 @@
#include <linux/rtc.h>
#include <linux/bcd.h>
#include <linux/workqueue.h>
+#include <linux/slab.h>
#define DS1374_REG_TOD0 0x00 /* Time of Day */
#define DS1374_REG_TOD1 0x01
@@ -402,7 +403,6 @@ out_irq:
free_irq(client->irq, client);
out_free:
- i2c_set_clientdata(client, NULL);
kfree(ds1374);
return ret;
}
@@ -421,7 +421,6 @@ static int __devexit ds1374_remove(struct i2c_client *client)
}
rtc_device_unregister(ds1374->rtc);
- i2c_set_clientdata(client, NULL);
kfree(ds1374);
return 0;
}
diff --git a/drivers/rtc/rtc-ds1390.c b/drivers/rtc/rtc-ds1390.c
index cdb705057091..26a86d235051 100644
--- a/drivers/rtc/rtc-ds1390.c
+++ b/drivers/rtc/rtc-ds1390.c
@@ -19,6 +19,7 @@
#include <linux/rtc.h>
#include <linux/spi/spi.h>
#include <linux/bcd.h>
+#include <linux/slab.h>
#define DS1390_REG_100THS 0x00
#define DS1390_REG_SECONDS 0x01
diff --git a/drivers/rtc/rtc-ds1511.c b/drivers/rtc/rtc-ds1511.c
index 4166b84cb514..37268e97de49 100644
--- a/drivers/rtc/rtc-ds1511.c
+++ b/drivers/rtc/rtc-ds1511.c
@@ -17,6 +17,7 @@
#include <linux/bcd.h>
#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/gfp.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/rtc.h>
@@ -422,8 +423,9 @@ static const struct rtc_class_ops ds1511_rtc_ops = {
};
static ssize_t
-ds1511_nvram_read(struct kobject *kobj, struct bin_attribute *ba,
- char *buf, loff_t pos, size_t size)
+ds1511_nvram_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *ba,
+ char *buf, loff_t pos, size_t size)
{
ssize_t count;
@@ -451,8 +453,9 @@ ds1511_nvram_read(struct kobject *kobj, struct bin_attribute *ba,
}
static ssize_t
-ds1511_nvram_write(struct kobject *kobj, struct bin_attribute *bin_attr,
- char *buf, loff_t pos, size_t size)
+ds1511_nvram_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t size)
{
ssize_t count;
diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c
index ed1ef7c9cc06..ff432e2ca275 100644
--- a/drivers/rtc/rtc-ds1553.c
+++ b/drivers/rtc/rtc-ds1553.c
@@ -11,6 +11,7 @@
#include <linux/bcd.h>
#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/gfp.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/interrupt.h>
@@ -251,7 +252,7 @@ static const struct rtc_class_ops ds1553_rtc_ops = {
.update_irq_enable = ds1553_rtc_update_irq_enable,
};
-static ssize_t ds1553_nvram_read(struct kobject *kobj,
+static ssize_t ds1553_nvram_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t pos, size_t size)
{
@@ -266,7 +267,7 @@ static ssize_t ds1553_nvram_read(struct kobject *kobj,
return count;
}
-static ssize_t ds1553_nvram_write(struct kobject *kobj,
+static ssize_t ds1553_nvram_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t pos, size_t size)
{
diff --git a/drivers/rtc/rtc-ds1742.c b/drivers/rtc/rtc-ds1742.c
index a1273360a44e..042630c90dd3 100644
--- a/drivers/rtc/rtc-ds1742.c
+++ b/drivers/rtc/rtc-ds1742.c
@@ -15,6 +15,7 @@
#include <linux/bcd.h>
#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/gfp.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/rtc.h>
@@ -127,7 +128,7 @@ static const struct rtc_class_ops ds1742_rtc_ops = {
.set_time = ds1742_rtc_set_time,
};
-static ssize_t ds1742_nvram_read(struct kobject *kobj,
+static ssize_t ds1742_nvram_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t pos, size_t size)
{
@@ -142,7 +143,7 @@ static ssize_t ds1742_nvram_read(struct kobject *kobj,
return count;
}
-static ssize_t ds1742_nvram_write(struct kobject *kobj,
+static ssize_t ds1742_nvram_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t pos, size_t size)
{
@@ -184,6 +185,7 @@ static int __devinit ds1742_rtc_probe(struct platform_device *pdev)
pdata->size_nvram = pdata->size - RTC_SIZE;
pdata->ioaddr_rtc = ioaddr + pdata->size_nvram;
+ sysfs_bin_attr_init(&pdata->nvram_attr);
pdata->nvram_attr.attr.name = "nvram";
pdata->nvram_attr.attr.mode = S_IRUGO | S_IWUSR;
pdata->nvram_attr.read = ds1742_nvram_read;
diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c
new file mode 100644
index 000000000000..57063552d3b7
--- /dev/null
+++ b/drivers/rtc/rtc-ds3232.c
@@ -0,0 +1,505 @@
+/*
+ * RTC client/driver for the Maxim/Dallas DS3232 Real-Time Clock over I2C
+ *
+ * Copyright (C) 2009-2010 Freescale Semiconductor.
+ * Author: Jack Lan <jack.lan@freescale.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+/*
+ * It would be more efficient to use i2c msgs/i2c_transfer directly but, as
+ * recommened in .../Documentation/i2c/writing-clients section
+ * "Sending and receiving", using SMBus level communication is preferred.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+
+#define DS3232_REG_SECONDS 0x00
+#define DS3232_REG_MINUTES 0x01
+#define DS3232_REG_HOURS 0x02
+#define DS3232_REG_AMPM 0x02
+#define DS3232_REG_DAY 0x03
+#define DS3232_REG_DATE 0x04
+#define DS3232_REG_MONTH 0x05
+#define DS3232_REG_CENTURY 0x05
+#define DS3232_REG_YEAR 0x06
+#define DS3232_REG_ALARM1 0x07 /* Alarm 1 BASE */
+#define DS3232_REG_ALARM2 0x0B /* Alarm 2 BASE */
+#define DS3232_REG_CR 0x0E /* Control register */
+# define DS3232_REG_CR_nEOSC 0x80
+# define DS3232_REG_CR_INTCN 0x04
+# define DS3232_REG_CR_A2IE 0x02
+# define DS3232_REG_CR_A1IE 0x01
+
+#define DS3232_REG_SR 0x0F /* control/status register */
+# define DS3232_REG_SR_OSF 0x80
+# define DS3232_REG_SR_BSY 0x04
+# define DS3232_REG_SR_A2F 0x02
+# define DS3232_REG_SR_A1F 0x01
+
+struct ds3232 {
+ struct i2c_client *client;
+ struct rtc_device *rtc;
+ struct work_struct work;
+
+ /* The mutex protects alarm operations, and prevents a race
+ * between the enable_irq() in the workqueue and the free_irq()
+ * in the remove function.
+ */
+ struct mutex mutex;
+ int exiting;
+};
+
+static struct i2c_driver ds3232_driver;
+
+static int ds3232_check_rtc_status(struct i2c_client *client)
+{
+ int ret = 0;
+ int control, stat;
+
+ stat = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
+ if (stat < 0)
+ return stat;
+
+ if (stat & DS3232_REG_SR_OSF)
+ dev_warn(&client->dev,
+ "oscillator discontinuity flagged, "
+ "time unreliable\n");
+
+ stat &= ~(DS3232_REG_SR_OSF | DS3232_REG_SR_A1F | DS3232_REG_SR_A2F);
+
+ ret = i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat);
+ if (ret < 0)
+ return ret;
+
+ /* If the alarm is pending, clear it before requesting
+ * the interrupt, so an interrupt event isn't reported
+ * before everything is initialized.
+ */
+
+ control = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
+ if (control < 0)
+ return control;
+
+ control &= ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE);
+ control |= DS3232_REG_CR_INTCN;
+
+ return i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
+}
+
+static int ds3232_read_time(struct device *dev, struct rtc_time *time)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int ret;
+ u8 buf[7];
+ unsigned int year, month, day, hour, minute, second;
+ unsigned int week, twelve_hr, am_pm;
+ unsigned int century, add_century = 0;
+
+ ret = i2c_smbus_read_i2c_block_data(client, DS3232_REG_SECONDS, 7, buf);
+
+ if (ret < 0)
+ return ret;
+ if (ret < 7)
+ return -EIO;
+
+ second = buf[0];
+ minute = buf[1];
+ hour = buf[2];
+ week = buf[3];
+ day = buf[4];
+ month = buf[5];
+ year = buf[6];
+
+ /* Extract additional information for AM/PM and century */
+
+ twelve_hr = hour & 0x40;
+ am_pm = hour & 0x20;
+ century = month & 0x80;
+
+ /* Write to rtc_time structure */
+
+ time->tm_sec = bcd2bin(second);
+ time->tm_min = bcd2bin(minute);
+ if (twelve_hr) {
+ /* Convert to 24 hr */
+ if (am_pm)
+ time->tm_hour = bcd2bin(hour & 0x1F) + 12;
+ else
+ time->tm_hour = bcd2bin(hour & 0x1F);
+ } else {
+ time->tm_hour = bcd2bin(hour);
+ }
+
+ time->tm_wday = bcd2bin(week);
+ time->tm_mday = bcd2bin(day);
+ time->tm_mon = bcd2bin(month & 0x7F);
+ if (century)
+ add_century = 100;
+
+ time->tm_year = bcd2bin(year) + add_century;
+
+ return rtc_valid_tm(time);
+}
+
+static int ds3232_set_time(struct device *dev, struct rtc_time *time)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ u8 buf[7];
+
+ /* Extract time from rtc_time and load into ds3232*/
+
+ buf[0] = bin2bcd(time->tm_sec);
+ buf[1] = bin2bcd(time->tm_min);
+ buf[2] = bin2bcd(time->tm_hour);
+ buf[3] = bin2bcd(time->tm_wday); /* Day of the week */
+ buf[4] = bin2bcd(time->tm_mday); /* Date */
+ buf[5] = bin2bcd(time->tm_mon);
+ if (time->tm_year >= 100) {
+ buf[5] |= 0x80;
+ buf[6] = bin2bcd(time->tm_year - 100);
+ } else {
+ buf[6] = bin2bcd(time->tm_year);
+ }
+
+ return i2c_smbus_write_i2c_block_data(client,
+ DS3232_REG_SECONDS, 7, buf);
+}
+
+/*
+ * DS3232 has two alarm, we only use alarm1
+ * According to linux specification, only support one-shot alarm
+ * no periodic alarm mode
+ */
+static int ds3232_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ds3232 *ds3232 = i2c_get_clientdata(client);
+ int control, stat;
+ int ret;
+ u8 buf[4];
+
+ mutex_lock(&ds3232->mutex);
+
+ ret = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
+ if (ret < 0)
+ goto out;
+ stat = ret;
+ ret = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
+ if (ret < 0)
+ goto out;
+ control = ret;
+ ret = i2c_smbus_read_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf);
+ if (ret < 0)
+ goto out;
+
+ alarm->time.tm_sec = bcd2bin(buf[0] & 0x7F);
+ alarm->time.tm_min = bcd2bin(buf[1] & 0x7F);
+ alarm->time.tm_hour = bcd2bin(buf[2] & 0x7F);
+ alarm->time.tm_mday = bcd2bin(buf[3] & 0x7F);
+
+ 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;
+
+ alarm->enabled = !!(control & DS3232_REG_CR_A1IE);
+ alarm->pending = !!(stat & DS3232_REG_SR_A1F);
+
+ ret = 0;
+out:
+ mutex_unlock(&ds3232->mutex);
+ return ret;
+}
+
+/*
+ * linux rtc-module does not support wday alarm
+ * and only 24h time mode supported indeed
+ */
+static int ds3232_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ds3232 *ds3232 = i2c_get_clientdata(client);
+ int control, stat;
+ int ret;
+ u8 buf[4];
+
+ if (client->irq <= 0)
+ return -EINVAL;
+
+ mutex_lock(&ds3232->mutex);
+
+ buf[0] = bin2bcd(alarm->time.tm_sec);
+ buf[1] = bin2bcd(alarm->time.tm_min);
+ buf[2] = bin2bcd(alarm->time.tm_hour);
+ buf[3] = bin2bcd(alarm->time.tm_mday);
+
+ /* clear alarm interrupt enable bit */
+ ret = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
+ if (ret < 0)
+ goto out;
+ control = ret;
+ control &= ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE);
+ ret = i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
+ if (ret < 0)
+ goto out;
+
+ /* clear any pending alarm flag */
+ ret = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
+ if (ret < 0)
+ goto out;
+ stat = ret;
+ stat &= ~(DS3232_REG_SR_A1F | DS3232_REG_SR_A2F);
+ ret = i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat);
+ if (ret < 0)
+ goto out;
+
+ ret = i2c_smbus_write_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf);
+
+ if (alarm->enabled) {
+ control |= DS3232_REG_CR_A1IE;
+ ret = i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
+ }
+out:
+ mutex_unlock(&ds3232->mutex);
+ return ret;
+}
+
+static void ds3232_update_alarm(struct i2c_client *client)
+{
+ struct ds3232 *ds3232 = i2c_get_clientdata(client);
+ int control;
+ int ret;
+ u8 buf[4];
+
+ mutex_lock(&ds3232->mutex);
+
+ ret = i2c_smbus_read_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf);
+ if (ret < 0)
+ goto unlock;
+
+ buf[0] = bcd2bin(buf[0]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ?
+ 0x80 : buf[0];
+ buf[1] = bcd2bin(buf[1]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ?
+ 0x80 : buf[1];
+ buf[2] = bcd2bin(buf[2]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ?
+ 0x80 : buf[2];
+ buf[3] = bcd2bin(buf[3]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ?
+ 0x80 : buf[3];
+
+ ret = i2c_smbus_write_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf);
+ if (ret < 0)
+ goto unlock;
+
+ control = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
+ if (control < 0)
+ goto unlock;
+
+ if (ds3232->rtc->irq_data & (RTC_AF | RTC_UF))
+ /* enable alarm1 interrupt */
+ control |= DS3232_REG_CR_A1IE;
+ else
+ /* disable alarm1 interrupt */
+ control &= ~(DS3232_REG_CR_A1IE);
+ i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
+
+unlock:
+ mutex_unlock(&ds3232->mutex);
+}
+
+static int ds3232_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ds3232 *ds3232 = i2c_get_clientdata(client);
+
+ if (client->irq <= 0)
+ return -EINVAL;
+
+ if (enabled)
+ ds3232->rtc->irq_data |= RTC_AF;
+ else
+ ds3232->rtc->irq_data &= ~RTC_AF;
+
+ ds3232_update_alarm(client);
+ return 0;
+}
+
+static int ds3232_update_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ds3232 *ds3232 = i2c_get_clientdata(client);
+
+ if (client->irq <= 0)
+ return -EINVAL;
+
+ if (enabled)
+ ds3232->rtc->irq_data |= RTC_UF;
+ else
+ ds3232->rtc->irq_data &= ~RTC_UF;
+
+ ds3232_update_alarm(client);
+ return 0;
+}
+
+static irqreturn_t ds3232_irq(int irq, void *dev_id)
+{
+ struct i2c_client *client = dev_id;
+ struct ds3232 *ds3232 = i2c_get_clientdata(client);
+
+ disable_irq_nosync(irq);
+ schedule_work(&ds3232->work);
+ return IRQ_HANDLED;
+}
+
+static void ds3232_work(struct work_struct *work)
+{
+ struct ds3232 *ds3232 = container_of(work, struct ds3232, work);
+ struct i2c_client *client = ds3232->client;
+ int stat, control;
+
+ mutex_lock(&ds3232->mutex);
+
+ stat = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
+ if (stat < 0)
+ goto unlock;
+
+ if (stat & DS3232_REG_SR_A1F) {
+ control = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
+ if (control < 0)
+ goto out;
+ /* disable alarm1 interrupt */
+ control &= ~(DS3232_REG_CR_A1IE);
+ i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
+
+ /* clear the alarm pend flag */
+ stat &= ~DS3232_REG_SR_A1F;
+ i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat);
+
+ rtc_update_irq(ds3232->rtc, 1, RTC_AF | RTC_IRQF);
+ }
+
+out:
+ if (!ds3232->exiting)
+ enable_irq(client->irq);
+unlock:
+ mutex_unlock(&ds3232->mutex);
+}
+
+static const struct rtc_class_ops ds3232_rtc_ops = {
+ .read_time = ds3232_read_time,
+ .set_time = ds3232_set_time,
+ .read_alarm = ds3232_read_alarm,
+ .set_alarm = ds3232_set_alarm,
+ .alarm_irq_enable = ds3232_alarm_irq_enable,
+ .update_irq_enable = ds3232_update_irq_enable,
+};
+
+static int __devinit ds3232_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ds3232 *ds3232;
+ int ret;
+
+ ds3232 = kzalloc(sizeof(struct ds3232), GFP_KERNEL);
+ if (!ds3232)
+ return -ENOMEM;
+
+ ds3232->client = client;
+ i2c_set_clientdata(client, ds3232);
+
+ INIT_WORK(&ds3232->work, ds3232_work);
+ mutex_init(&ds3232->mutex);
+
+ ret = ds3232_check_rtc_status(client);
+ if (ret)
+ goto out_free;
+
+ ds3232->rtc = rtc_device_register(client->name, &client->dev,
+ &ds3232_rtc_ops, THIS_MODULE);
+ if (IS_ERR(ds3232->rtc)) {
+ ret = PTR_ERR(ds3232->rtc);
+ dev_err(&client->dev, "unable to register the class device\n");
+ goto out_irq;
+ }
+
+ if (client->irq >= 0) {
+ ret = request_irq(client->irq, ds3232_irq, 0,
+ "ds3232", client);
+ if (ret) {
+ dev_err(&client->dev, "unable to request IRQ\n");
+ goto out_free;
+ }
+ }
+
+ return 0;
+
+out_irq:
+ if (client->irq >= 0)
+ free_irq(client->irq, client);
+
+out_free:
+ kfree(ds3232);
+ return ret;
+}
+
+static int __devexit ds3232_remove(struct i2c_client *client)
+{
+ struct ds3232 *ds3232 = i2c_get_clientdata(client);
+
+ if (client->irq >= 0) {
+ mutex_lock(&ds3232->mutex);
+ ds3232->exiting = 1;
+ mutex_unlock(&ds3232->mutex);
+
+ free_irq(client->irq, client);
+ flush_scheduled_work();
+ }
+
+ rtc_device_unregister(ds3232->rtc);
+ kfree(ds3232);
+ return 0;
+}
+
+static const struct i2c_device_id ds3232_id[] = {
+ { "ds3232", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ds3232_id);
+
+static struct i2c_driver ds3232_driver = {
+ .driver = {
+ .name = "rtc-ds3232",
+ .owner = THIS_MODULE,
+ },
+ .probe = ds3232_probe,
+ .remove = __devexit_p(ds3232_remove),
+ .id_table = ds3232_id,
+};
+
+static int __init ds3232_init(void)
+{
+ return i2c_add_driver(&ds3232_driver);
+}
+
+static void __exit ds3232_exit(void)
+{
+ i2c_del_driver(&ds3232_driver);
+}
+
+module_init(ds3232_init);
+module_exit(ds3232_exit);
+
+MODULE_AUTHOR("Srikanth Srinivasan <srikanth.srinivasan@freescale.com>");
+MODULE_DESCRIPTION("Maxim/Dallas DS3232 RTC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-ep93xx.c b/drivers/rtc/rtc-ep93xx.c
index 9da02d108b73..11ae64dcbf3c 100644
--- a/drivers/rtc/rtc-ep93xx.c
+++ b/drivers/rtc/rtc-ep93xx.c
@@ -13,6 +13,7 @@
#include <linux/rtc.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/gfp.h>
#define EP93XX_RTC_DATA 0x000
#define EP93XX_RTC_MATCH 0x004
@@ -115,6 +116,15 @@ static ssize_t ep93xx_rtc_show_comp_delete(struct device *dev,
}
static DEVICE_ATTR(comp_delete, S_IRUGO, ep93xx_rtc_show_comp_delete, NULL);
+static struct attribute *ep93xx_rtc_attrs[] = {
+ &dev_attr_comp_preload.attr,
+ &dev_attr_comp_delete.attr,
+ NULL
+};
+
+static const struct attribute_group ep93xx_rtc_sysfs_files = {
+ .attrs = ep93xx_rtc_attrs,
+};
static int __init ep93xx_rtc_probe(struct platform_device *pdev)
{
@@ -123,27 +133,22 @@ static int __init ep93xx_rtc_probe(struct platform_device *pdev)
struct rtc_device *rtc;
int err;
- ep93xx_rtc = kzalloc(sizeof(struct ep93xx_rtc), GFP_KERNEL);
- if (ep93xx_rtc == NULL)
+ ep93xx_rtc = devm_kzalloc(&pdev->dev, sizeof(*ep93xx_rtc), GFP_KERNEL);
+ if (!ep93xx_rtc)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- err = -ENXIO;
- goto fail_free;
- }
+ if (!res)
+ return -ENXIO;
- res = request_mem_region(res->start, resource_size(res), pdev->name);
- if (res == NULL) {
- err = -EBUSY;
- goto fail_free;
- }
+ if (!devm_request_mem_region(&pdev->dev, res->start,
+ resource_size(res), pdev->name))
+ return -EBUSY;
- ep93xx_rtc->mmio_base = ioremap(res->start, resource_size(res));
- if (ep93xx_rtc->mmio_base == NULL) {
- err = -ENXIO;
- goto fail;
- }
+ ep93xx_rtc->mmio_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!ep93xx_rtc->mmio_base)
+ return -ENXIO;
pdev->dev.platform_data = ep93xx_rtc;
@@ -151,53 +156,34 @@ static int __init ep93xx_rtc_probe(struct platform_device *pdev)
&pdev->dev, &ep93xx_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
err = PTR_ERR(rtc);
- goto fail;
+ goto exit;
}
platform_set_drvdata(pdev, rtc);
- err = device_create_file(&pdev->dev, &dev_attr_comp_preload);
+ err = sysfs_create_group(&pdev->dev.kobj, &ep93xx_rtc_sysfs_files);
if (err)
goto fail;
- err = device_create_file(&pdev->dev, &dev_attr_comp_delete);
- if (err) {
- device_remove_file(&pdev->dev, &dev_attr_comp_preload);
- goto fail;
- }
return 0;
fail:
- if (ep93xx_rtc->mmio_base) {
- iounmap(ep93xx_rtc->mmio_base);
- pdev->dev.platform_data = NULL;
- }
- release_mem_region(res->start, resource_size(res));
-fail_free:
- kfree(ep93xx_rtc);
+ platform_set_drvdata(pdev, NULL);
+ rtc_device_unregister(rtc);
+exit:
+ pdev->dev.platform_data = NULL;
return err;
}
static int __exit ep93xx_rtc_remove(struct platform_device *pdev)
{
struct rtc_device *rtc = platform_get_drvdata(pdev);
- struct ep93xx_rtc *ep93xx_rtc = pdev->dev.platform_data;
- struct resource *res;
-
- /* cleanup sysfs */
- device_remove_file(&pdev->dev, &dev_attr_comp_delete);
- device_remove_file(&pdev->dev, &dev_attr_comp_preload);
+ sysfs_remove_group(&pdev->dev.kobj, &ep93xx_rtc_sysfs_files);
+ platform_set_drvdata(pdev, NULL);
rtc_device_unregister(rtc);
-
- iounmap(ep93xx_rtc->mmio_base);
pdev->dev.platform_data = NULL;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, resource_size(res));
-
- platform_set_drvdata(pdev, NULL);
-
return 0;
}
diff --git a/drivers/rtc/rtc-fm3130.c b/drivers/rtc/rtc-fm3130.c
index 3a7be11cc6b9..4cf2e70c5078 100644
--- a/drivers/rtc/rtc-fm3130.c
+++ b/drivers/rtc/rtc-fm3130.c
@@ -13,6 +13,7 @@
#include <linux/i2c.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
+#include <linux/slab.h>
#define FM3130_RTC_CONTROL (0x0)
#define FM3130_CAL_CONTROL (0x1)
@@ -51,8 +52,8 @@ struct fm3130 {
struct i2c_msg msg[4];
struct i2c_client *client;
struct rtc_device *rtc;
+ int alarm_valid;
int data_valid;
- int alarm;
};
static const struct i2c_device_id fm3130_id[] = {
{ "fm3130", 0 },
@@ -86,11 +87,7 @@ static void fm3130_rtc_mode(struct device *dev, int mode)
dev_dbg(dev, "invalid mode %d\n", mode);
break;
}
- /* Checking for alarm */
- if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AF) {
- fm3130->alarm = 1;
- fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF;
- }
+
i2c_smbus_write_byte_data(fm3130->client,
FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL]);
}
@@ -103,7 +100,7 @@ static int fm3130_get_time(struct device *dev, struct rtc_time *t)
if (!fm3130->data_valid) {
/* We have invalid data in RTC, probably due
to battery faults or other problems. Return EIO
- for now, it will allow us to set data later insted
+ for now, it will allow us to set data later instead
of error during probing which disables device */
return -EIO;
}
@@ -207,6 +204,17 @@ static int fm3130_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
struct fm3130 *fm3130 = dev_get_drvdata(dev);
int tmp;
struct rtc_time *tm = &alrm->time;
+
+ if (!fm3130->alarm_valid) {
+ /*
+ * We have invalid alarm in RTC, probably due to battery faults
+ * or other problems. Return EIO for now, it will allow us to
+ * set alarm value later instead of error during probing which
+ * disables device
+ */
+ return -EIO;
+ }
+
/* read the RTC alarm registers all at once */
tmp = i2c_transfer(to_i2c_adapter(fm3130->client->dev.parent),
&fm3130->msg[2], 2);
@@ -221,20 +229,31 @@ static int fm3130_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
fm3130->regs[FM3130_ALARM_DATE],
fm3130->regs[FM3130_ALARM_MONTHS]);
-
tm->tm_sec = bcd2bin(fm3130->regs[FM3130_ALARM_SECONDS] & 0x7F);
tm->tm_min = bcd2bin(fm3130->regs[FM3130_ALARM_MINUTES] & 0x7F);
tm->tm_hour = bcd2bin(fm3130->regs[FM3130_ALARM_HOURS] & 0x3F);
tm->tm_mday = bcd2bin(fm3130->regs[FM3130_ALARM_DATE] & 0x3F);
tm->tm_mon = bcd2bin(fm3130->regs[FM3130_ALARM_MONTHS] & 0x1F);
+
if (tm->tm_mon > 0)
tm->tm_mon -= 1; /* RTC is 1-12, tm_mon is 0-11 */
+
dev_dbg(dev, "%s secs=%d, mins=%d, "
"hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
"read alarm", tm->tm_sec, tm->tm_min,
tm->tm_hour, tm->tm_mday,
tm->tm_mon, tm->tm_year, tm->tm_wday);
+ /* check if alarm enabled */
+ fm3130->regs[FM3130_RTC_CONTROL] =
+ i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL);
+
+ if ((fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AEN) &&
+ (~fm3130->regs[FM3130_RTC_CONTROL] &
+ FM3130_RTC_CONTROL_BIT_CAL)) {
+ alrm->enabled = 1;
+ }
+
return 0;
}
@@ -250,25 +269,20 @@ static int fm3130_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
tm->tm_hour, tm->tm_mday,
tm->tm_mon, tm->tm_year, tm->tm_wday);
- if (tm->tm_sec != -1)
- fm3130->regs[FM3130_ALARM_SECONDS] =
- bin2bcd(tm->tm_sec) | 0x80;
+ fm3130->regs[FM3130_ALARM_SECONDS] =
+ (tm->tm_sec != -1) ? bin2bcd(tm->tm_sec) : 0x80;
- if (tm->tm_min != -1)
- fm3130->regs[FM3130_ALARM_MINUTES] =
- bin2bcd(tm->tm_min) | 0x80;
+ fm3130->regs[FM3130_ALARM_MINUTES] =
+ (tm->tm_min != -1) ? bin2bcd(tm->tm_min) : 0x80;
- if (tm->tm_hour != -1)
- fm3130->regs[FM3130_ALARM_HOURS] =
- bin2bcd(tm->tm_hour) | 0x80;
+ fm3130->regs[FM3130_ALARM_HOURS] =
+ (tm->tm_hour != -1) ? bin2bcd(tm->tm_hour) : 0x80;
- if (tm->tm_mday != -1)
- fm3130->regs[FM3130_ALARM_DATE] =
- bin2bcd(tm->tm_mday) | 0x80;
+ fm3130->regs[FM3130_ALARM_DATE] =
+ (tm->tm_mday != -1) ? bin2bcd(tm->tm_mday) : 0x80;
- if (tm->tm_mon != -1)
- fm3130->regs[FM3130_ALARM_MONTHS] =
- bin2bcd(tm->tm_mon + 1) | 0x80;
+ fm3130->regs[FM3130_ALARM_MONTHS] =
+ (tm->tm_mon != -1) ? bin2bcd(tm->tm_mon + 1) : 0x80;
dev_dbg(dev, "alarm write %02x %02x %02x %02x %02x\n",
fm3130->regs[FM3130_ALARM_SECONDS],
@@ -284,11 +298,8 @@ static int fm3130_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
}
fm3130->regs[FM3130_RTC_CONTROL] =
i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL);
- /* Checking for alarm */
- if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AF) {
- fm3130->alarm = 1;
- fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF;
- }
+
+ /* enable or disable alarm */
if (alrm->enabled) {
i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL,
(fm3130->regs[FM3130_RTC_CONTROL] &
@@ -297,16 +308,55 @@ static int fm3130_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
} else {
i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL,
fm3130->regs[FM3130_RTC_CONTROL] &
- ~(FM3130_RTC_CONTROL_BIT_AEN));
+ ~(FM3130_RTC_CONTROL_BIT_CAL) &
+ ~(FM3130_RTC_CONTROL_BIT_AEN));
}
+
+ /* We assume here that data is valid once written */
+ if (!fm3130->alarm_valid)
+ fm3130->alarm_valid = 1;
+
return 0;
}
+static int fm3130_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct fm3130 *fm3130 = dev_get_drvdata(dev);
+ int ret = 0;
+
+ fm3130->regs[FM3130_RTC_CONTROL] =
+ i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL);
+
+ dev_dbg(dev, "alarm_irq_enable: enable=%d, FM3130_RTC_CONTROL=%02x\n",
+ enabled, fm3130->regs[FM3130_RTC_CONTROL]);
+
+ switch (enabled) {
+ case 0: /* alarm off */
+ ret = i2c_smbus_write_byte_data(fm3130->client,
+ FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL] &
+ ~(FM3130_RTC_CONTROL_BIT_CAL) &
+ ~(FM3130_RTC_CONTROL_BIT_AEN));
+ break;
+ case 1: /* alarm on */
+ ret = i2c_smbus_write_byte_data(fm3130->client,
+ FM3130_RTC_CONTROL, (fm3130->regs[FM3130_RTC_CONTROL] &
+ ~(FM3130_RTC_CONTROL_BIT_CAL)) |
+ FM3130_RTC_CONTROL_BIT_AEN);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
static const struct rtc_class_ops fm3130_rtc_ops = {
.read_time = fm3130_get_time,
.set_time = fm3130_set_time,
.read_alarm = fm3130_read_alarm,
.set_alarm = fm3130_set_alarm,
+ .alarm_irq_enable = fm3130_alarm_irq_enable,
};
static struct i2c_driver fm3130_driver;
@@ -355,6 +405,7 @@ static int __devinit fm3130_probe(struct i2c_client *client,
fm3130->msg[3].len = FM3130_ALARM_REGS;
fm3130->msg[3].buf = &fm3130->regs[FM3130_ALARM_SECONDS];
+ fm3130->alarm_valid = 0;
fm3130->data_valid = 0;
tmp = i2c_transfer(adapter, fm3130->msg, 4);
@@ -369,27 +420,23 @@ static int __devinit fm3130_probe(struct i2c_client *client,
fm3130->regs[FM3130_CAL_CONTROL] =
i2c_smbus_read_byte_data(client, FM3130_CAL_CONTROL);
- /* Checking for alarm */
- if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AF) {
- fm3130->alarm = 1;
- fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF;
- }
-
/* Disabling calibration mode */
- if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_CAL)
+ if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_CAL) {
i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL,
fm3130->regs[FM3130_RTC_CONTROL] &
~(FM3130_RTC_CONTROL_BIT_CAL));
dev_warn(&client->dev, "Disabling calibration mode!\n");
+ }
/* Disabling read and write modes */
if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_WRITE ||
- fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_READ)
+ fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_READ) {
i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL,
fm3130->regs[FM3130_RTC_CONTROL] &
~(FM3130_RTC_CONTROL_BIT_READ |
FM3130_RTC_CONTROL_BIT_WRITE));
dev_warn(&client->dev, "Disabling READ or WRITE mode!\n");
+ }
/* oscillator off? turn it on, so clock can tick. */
if (fm3130->regs[FM3130_CAL_CONTROL] & FM3130_CAL_CONTROL_BIT_nOSCEN)
@@ -397,44 +444,79 @@ static int __devinit fm3130_probe(struct i2c_client *client,
fm3130->regs[FM3130_CAL_CONTROL] &
~(FM3130_CAL_CONTROL_BIT_nOSCEN));
- /* oscillator fault? clear flag, and warn */
- if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_LB)
+ /* low battery? clear flag, and warn */
+ if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_LB) {
+ i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL,
+ fm3130->regs[FM3130_RTC_CONTROL] &
+ ~(FM3130_RTC_CONTROL_BIT_LB));
dev_warn(&client->dev, "Low battery!\n");
+ }
- /* oscillator fault? clear flag, and warn */
+ /* check if Power On Reset bit is set */
if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_POR) {
i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL,
fm3130->regs[FM3130_RTC_CONTROL] &
~FM3130_RTC_CONTROL_BIT_POR);
- dev_warn(&client->dev, "SET TIME!\n");
+ dev_dbg(&client->dev, "POR bit is set\n");
}
/* ACS is controlled by alarm */
i2c_smbus_write_byte_data(client, FM3130_ALARM_WP_CONTROL, 0x80);
- /* TODO */
- /* TODO need to sanity check alarm */
- tmp = fm3130->regs[FM3130_RTC_SECONDS];
- tmp = bcd2bin(tmp & 0x7f);
- if (tmp > 60)
- goto exit_bad;
+ /* alarm registers sanity check */
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f);
+ if (tmp > 59)
+ goto bad_alarm;
+
tmp = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f);
- if (tmp > 60)
- goto exit_bad;
+ if (tmp > 59)
+ goto bad_alarm;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_HOURS] & 0x3f);
+ if (tmp > 23)
+ goto bad_alarm;
tmp = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f);
if (tmp == 0 || tmp > 31)
- goto exit_bad;
+ goto bad_alarm;
tmp = bcd2bin(fm3130->regs[FM3130_RTC_MONTHS] & 0x1f);
if (tmp == 0 || tmp > 12)
- goto exit_bad;
+ goto bad_alarm;
- tmp = fm3130->regs[FM3130_RTC_HOURS];
+ fm3130->alarm_valid = 1;
+
+bad_alarm:
+
+ /* clock registers sanity chek */
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f);
+ if (tmp > 59)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f);
+ if (tmp > 59)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_HOURS] & 0x3f);
+ if (tmp > 23)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_DAY] & 0x7);
+ if (tmp == 0 || tmp > 7)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f);
+ if (tmp == 0 || tmp > 31)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_MONTHS] & 0x1f);
+ if (tmp == 0 || tmp > 12)
+ goto bad_clock;
fm3130->data_valid = 1;
-exit_bad:
- if (!fm3130->data_valid)
+bad_clock:
+
+ if (!fm3130->data_valid || !fm3130->alarm_valid)
dev_dbg(&client->dev,
"%s: %02x %02x %02x %02x %02x %02x %02x %02x"
"%02x %02x %02x %02x %02x %02x %02x\n",
diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c
new file mode 100644
index 000000000000..2dd3c0163272
--- /dev/null
+++ b/drivers/rtc/rtc-imxdi.c
@@ -0,0 +1,519 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2010 Orex Computed Radiography
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/* based on rtc-mc13892.c */
+
+/*
+ * This driver uses the 47-bit 32 kHz counter in the Freescale DryIce block
+ * to implement a Linux RTC. Times and alarms are truncated to seconds.
+ * Since the RTC framework performs API locking via rtc->ops_lock the
+ * only simultaneous accesses we need to deal with is updating DryIce
+ * registers while servicing an alarm.
+ *
+ * Note that reading the DSR (DryIce Status Register) automatically clears
+ * the WCF (Write Complete Flag). All DryIce writes are synchronized to the
+ * LP (Low Power) domain and set the WCF upon completion. Writes to the
+ * DIER (DryIce Interrupt Enable Register) are the only exception. These
+ * occur at normal bus speeds and do not set WCF. Periodic interrupts are
+ * not supported by the hardware.
+ */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/workqueue.h>
+
+/* DryIce Register Definitions */
+
+#define DTCMR 0x00 /* Time Counter MSB Reg */
+#define DTCLR 0x04 /* Time Counter LSB Reg */
+
+#define DCAMR 0x08 /* Clock Alarm MSB Reg */
+#define DCALR 0x0c /* Clock Alarm LSB Reg */
+#define DCAMR_UNSET 0xFFFFFFFF /* doomsday - 1 sec */
+
+#define DCR 0x10 /* Control Reg */
+#define DCR_TCE (1 << 3) /* Time Counter Enable */
+
+#define DSR 0x14 /* Status Reg */
+#define DSR_WBF (1 << 10) /* Write Busy Flag */
+#define DSR_WNF (1 << 9) /* Write Next Flag */
+#define DSR_WCF (1 << 8) /* Write Complete Flag */
+#define DSR_WEF (1 << 7) /* Write Error Flag */
+#define DSR_CAF (1 << 4) /* Clock Alarm Flag */
+#define DSR_NVF (1 << 1) /* Non-Valid Flag */
+#define DSR_SVF (1 << 0) /* Security Violation Flag */
+
+#define DIER 0x18 /* Interrupt Enable Reg */
+#define DIER_WNIE (1 << 9) /* Write Next Interrupt Enable */
+#define DIER_WCIE (1 << 8) /* Write Complete Interrupt Enable */
+#define DIER_WEIE (1 << 7) /* Write Error Interrupt Enable */
+#define DIER_CAIE (1 << 4) /* Clock Alarm Interrupt Enable */
+
+/**
+ * struct imxdi_dev - private imxdi rtc data
+ * @pdev: pionter to platform dev
+ * @rtc: pointer to rtc struct
+ * @ioaddr: IO registers pointer
+ * @irq: dryice normal interrupt
+ * @clk: input reference clock
+ * @dsr: copy of the DSR register
+ * @irq_lock: interrupt enable register (DIER) lock
+ * @write_wait: registers write complete queue
+ * @write_mutex: serialize registers write
+ * @work: schedule alarm work
+ */
+struct imxdi_dev {
+ struct platform_device *pdev;
+ struct rtc_device *rtc;
+ void __iomem *ioaddr;
+ int irq;
+ struct clk *clk;
+ u32 dsr;
+ spinlock_t irq_lock;
+ wait_queue_head_t write_wait;
+ struct mutex write_mutex;
+ struct work_struct work;
+};
+
+/*
+ * enable a dryice interrupt
+ */
+static void di_int_enable(struct imxdi_dev *imxdi, u32 intr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&imxdi->irq_lock, flags);
+ __raw_writel(__raw_readl(imxdi->ioaddr + DIER) | intr,
+ imxdi->ioaddr + DIER);
+ spin_unlock_irqrestore(&imxdi->irq_lock, flags);
+}
+
+/*
+ * disable a dryice interrupt
+ */
+static void di_int_disable(struct imxdi_dev *imxdi, u32 intr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&imxdi->irq_lock, flags);
+ __raw_writel(__raw_readl(imxdi->ioaddr + DIER) & ~intr,
+ imxdi->ioaddr + DIER);
+ spin_unlock_irqrestore(&imxdi->irq_lock, flags);
+}
+
+/*
+ * This function attempts to clear the dryice write-error flag.
+ *
+ * A dryice write error is similar to a bus fault and should not occur in
+ * normal operation. Clearing the flag requires another write, so the root
+ * cause of the problem may need to be fixed before the flag can be cleared.
+ */
+static void clear_write_error(struct imxdi_dev *imxdi)
+{
+ int cnt;
+
+ dev_warn(&imxdi->pdev->dev, "WARNING: Register write error!\n");
+
+ /* clear the write error flag */
+ __raw_writel(DSR_WEF, imxdi->ioaddr + DSR);
+
+ /* wait for it to take effect */
+ for (cnt = 0; cnt < 1000; cnt++) {
+ if ((__raw_readl(imxdi->ioaddr + DSR) & DSR_WEF) == 0)
+ return;
+ udelay(10);
+ }
+ dev_err(&imxdi->pdev->dev,
+ "ERROR: Cannot clear write-error flag!\n");
+}
+
+/*
+ * Write a dryice register and wait until it completes.
+ *
+ * This function uses interrupts to determine when the
+ * write has completed.
+ */
+static int di_write_wait(struct imxdi_dev *imxdi, u32 val, int reg)
+{
+ int ret;
+ int rc = 0;
+
+ /* serialize register writes */
+ mutex_lock(&imxdi->write_mutex);
+
+ /* enable the write-complete interrupt */
+ di_int_enable(imxdi, DIER_WCIE);
+
+ imxdi->dsr = 0;
+
+ /* do the register write */
+ __raw_writel(val, imxdi->ioaddr + reg);
+
+ /* wait for the write to finish */
+ ret = wait_event_interruptible_timeout(imxdi->write_wait,
+ imxdi->dsr & (DSR_WCF | DSR_WEF), msecs_to_jiffies(1));
+ if (ret < 0) {
+ rc = ret;
+ goto out;
+ } else if (ret == 0) {
+ dev_warn(&imxdi->pdev->dev,
+ "Write-wait timeout "
+ "val = 0x%08x reg = 0x%08x\n", val, reg);
+ }
+
+ /* check for write error */
+ if (imxdi->dsr & DSR_WEF) {
+ clear_write_error(imxdi);
+ rc = -EIO;
+ }
+
+out:
+ mutex_unlock(&imxdi->write_mutex);
+
+ return rc;
+}
+
+/*
+ * read the seconds portion of the current time from the dryice time counter
+ */
+static int dryice_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+ unsigned long now;
+
+ now = __raw_readl(imxdi->ioaddr + DTCMR);
+ rtc_time_to_tm(now, tm);
+
+ return 0;
+}
+
+/*
+ * set the seconds portion of dryice time counter and clear the
+ * fractional part.
+ */
+static int dryice_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+ struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+ int rc;
+
+ /* zero the fractional part first */
+ rc = di_write_wait(imxdi, 0, DTCLR);
+ if (rc == 0)
+ rc = di_write_wait(imxdi, secs, DTCMR);
+
+ return rc;
+}
+
+static int dryice_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+
+ if (enabled)
+ di_int_enable(imxdi, DIER_CAIE);
+ else
+ di_int_disable(imxdi, DIER_CAIE);
+
+ return 0;
+}
+
+/*
+ * read the seconds portion of the alarm register.
+ * the fractional part of the alarm register is always zero.
+ */
+static int dryice_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+ u32 dcamr;
+
+ dcamr = __raw_readl(imxdi->ioaddr + DCAMR);
+ rtc_time_to_tm(dcamr, &alarm->time);
+
+ /* alarm is enabled if the interrupt is enabled */
+ alarm->enabled = (__raw_readl(imxdi->ioaddr + DIER) & DIER_CAIE) != 0;
+
+ /* don't allow the DSR read to mess up DSR_WCF */
+ mutex_lock(&imxdi->write_mutex);
+
+ /* alarm is pending if the alarm flag is set */
+ alarm->pending = (__raw_readl(imxdi->ioaddr + DSR) & DSR_CAF) != 0;
+
+ mutex_unlock(&imxdi->write_mutex);
+
+ return 0;
+}
+
+/*
+ * set the seconds portion of dryice alarm register
+ */
+static int dryice_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+ unsigned long now;
+ unsigned long alarm_time;
+ int rc;
+
+ rc = rtc_tm_to_time(&alarm->time, &alarm_time);
+ if (rc)
+ return rc;
+
+ /* don't allow setting alarm in the past */
+ now = __raw_readl(imxdi->ioaddr + DTCMR);
+ if (alarm_time < now)
+ return -EINVAL;
+
+ /* write the new alarm time */
+ rc = di_write_wait(imxdi, (u32)alarm_time, DCAMR);
+ if (rc)
+ return rc;
+
+ if (alarm->enabled)
+ di_int_enable(imxdi, DIER_CAIE); /* enable alarm intr */
+ else
+ di_int_disable(imxdi, DIER_CAIE); /* disable alarm intr */
+
+ return 0;
+}
+
+static struct rtc_class_ops dryice_rtc_ops = {
+ .read_time = dryice_rtc_read_time,
+ .set_mmss = dryice_rtc_set_mmss,
+ .alarm_irq_enable = dryice_rtc_alarm_irq_enable,
+ .read_alarm = dryice_rtc_read_alarm,
+ .set_alarm = dryice_rtc_set_alarm,
+};
+
+/*
+ * dryice "normal" interrupt handler
+ */
+static irqreturn_t dryice_norm_irq(int irq, void *dev_id)
+{
+ struct imxdi_dev *imxdi = dev_id;
+ u32 dsr, dier;
+ irqreturn_t rc = IRQ_NONE;
+
+ dier = __raw_readl(imxdi->ioaddr + DIER);
+
+ /* handle write complete and write error cases */
+ if ((dier & DIER_WCIE)) {
+ /*If the write wait queue is empty then there is no pending
+ operations. It means the interrupt is for DryIce -Security.
+ IRQ must be returned as none.*/
+ if (list_empty_careful(&imxdi->write_wait.task_list))
+ return rc;
+
+ /* DSR_WCF clears itself on DSR read */
+ dsr = __raw_readl(imxdi->ioaddr + DSR);
+ if ((dsr & (DSR_WCF | DSR_WEF))) {
+ /* mask the interrupt */
+ di_int_disable(imxdi, DIER_WCIE);
+
+ /* save the dsr value for the wait queue */
+ imxdi->dsr |= dsr;
+
+ wake_up_interruptible(&imxdi->write_wait);
+ rc = IRQ_HANDLED;
+ }
+ }
+
+ /* handle the alarm case */
+ if ((dier & DIER_CAIE)) {
+ /* DSR_WCF clears itself on DSR read */
+ dsr = __raw_readl(imxdi->ioaddr + DSR);
+ if (dsr & DSR_CAF) {
+ /* mask the interrupt */
+ di_int_disable(imxdi, DIER_CAIE);
+
+ /* finish alarm in user context */
+ schedule_work(&imxdi->work);
+ rc = IRQ_HANDLED;
+ }
+ }
+ return rc;
+}
+
+/*
+ * post the alarm event from user context so it can sleep
+ * on the write completion.
+ */
+static void dryice_work(struct work_struct *work)
+{
+ struct imxdi_dev *imxdi = container_of(work,
+ struct imxdi_dev, work);
+
+ /* dismiss the interrupt (ignore error) */
+ di_write_wait(imxdi, DSR_CAF, DSR);
+
+ /* pass the alarm event to the rtc framework. */
+ rtc_update_irq(imxdi->rtc, 1, RTC_AF | RTC_IRQF);
+}
+
+/*
+ * probe for dryice rtc device
+ */
+static int dryice_rtc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct imxdi_dev *imxdi;
+ int rc;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ imxdi = devm_kzalloc(&pdev->dev, sizeof(*imxdi), GFP_KERNEL);
+ if (!imxdi)
+ return -ENOMEM;
+
+ imxdi->pdev = pdev;
+
+ if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
+ pdev->name))
+ return -EBUSY;
+
+ imxdi->ioaddr = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (imxdi->ioaddr == NULL)
+ return -ENOMEM;
+
+ imxdi->irq = platform_get_irq(pdev, 0);
+ if (imxdi->irq < 0)
+ return imxdi->irq;
+
+ init_waitqueue_head(&imxdi->write_wait);
+
+ INIT_WORK(&imxdi->work, dryice_work);
+
+ mutex_init(&imxdi->write_mutex);
+
+ imxdi->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(imxdi->clk))
+ return PTR_ERR(imxdi->clk);
+ clk_enable(imxdi->clk);
+
+ /*
+ * Initialize dryice hardware
+ */
+
+ /* mask all interrupts */
+ __raw_writel(0, imxdi->ioaddr + DIER);
+
+ rc = devm_request_irq(&pdev->dev, imxdi->irq, dryice_norm_irq,
+ IRQF_SHARED, pdev->name, imxdi);
+ if (rc) {
+ dev_warn(&pdev->dev, "interrupt not available.\n");
+ goto err;
+ }
+
+ /* put dryice into valid state */
+ if (__raw_readl(imxdi->ioaddr + DSR) & DSR_NVF) {
+ rc = di_write_wait(imxdi, DSR_NVF | DSR_SVF, DSR);
+ if (rc)
+ goto err;
+ }
+
+ /* initialize alarm */
+ rc = di_write_wait(imxdi, DCAMR_UNSET, DCAMR);
+ if (rc)
+ goto err;
+ rc = di_write_wait(imxdi, 0, DCALR);
+ if (rc)
+ goto err;
+
+ /* clear alarm flag */
+ if (__raw_readl(imxdi->ioaddr + DSR) & DSR_CAF) {
+ rc = di_write_wait(imxdi, DSR_CAF, DSR);
+ if (rc)
+ goto err;
+ }
+
+ /* the timer won't count if it has never been written to */
+ if (__raw_readl(imxdi->ioaddr + DTCMR) == 0) {
+ rc = di_write_wait(imxdi, 0, DTCMR);
+ if (rc)
+ goto err;
+ }
+
+ /* start keeping time */
+ if (!(__raw_readl(imxdi->ioaddr + DCR) & DCR_TCE)) {
+ rc = di_write_wait(imxdi,
+ __raw_readl(imxdi->ioaddr + DCR) | DCR_TCE,
+ DCR);
+ if (rc)
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, imxdi);
+ imxdi->rtc = rtc_device_register(pdev->name, &pdev->dev,
+ &dryice_rtc_ops, THIS_MODULE);
+ if (IS_ERR(imxdi->rtc)) {
+ rc = PTR_ERR(imxdi->rtc);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ clk_disable(imxdi->clk);
+ clk_put(imxdi->clk);
+
+ return rc;
+}
+
+static int __devexit dryice_rtc_remove(struct platform_device *pdev)
+{
+ struct imxdi_dev *imxdi = platform_get_drvdata(pdev);
+
+ flush_work(&imxdi->work);
+
+ /* mask all interrupts */
+ __raw_writel(0, imxdi->ioaddr + DIER);
+
+ rtc_device_unregister(imxdi->rtc);
+
+ clk_disable(imxdi->clk);
+ clk_put(imxdi->clk);
+
+ return 0;
+}
+
+static struct platform_driver dryice_rtc_driver = {
+ .driver = {
+ .name = "imxdi_rtc",
+ .owner = THIS_MODULE,
+ },
+ .remove = __devexit_p(dryice_rtc_remove),
+};
+
+static int __init dryice_rtc_init(void)
+{
+ return platform_driver_probe(&dryice_rtc_driver, dryice_rtc_probe);
+}
+
+static void __exit dryice_rtc_exit(void)
+{
+ platform_driver_unregister(&dryice_rtc_driver);
+}
+
+module_init(dryice_rtc_init);
+module_exit(dryice_rtc_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
+MODULE_DESCRIPTION("IMX DryIce Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-isl12022.c b/drivers/rtc/rtc-isl12022.c
new file mode 100644
index 000000000000..ddbc797ea6cd
--- /dev/null
+++ b/drivers/rtc/rtc-isl12022.c
@@ -0,0 +1,327 @@
+/*
+ * An I2C driver for the Intersil ISL 12022
+ *
+ * Author: Roman Fietze <roman.fietze@telemotive.de>
+ *
+ * Based on the Philips PCF8563 RTC
+ * by Alessandro Zummo <a.zummo@towertech.it>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+
+#define DRV_VERSION "0.1"
+
+/* ISL register offsets */
+#define ISL12022_REG_SC 0x00
+#define ISL12022_REG_MN 0x01
+#define ISL12022_REG_HR 0x02
+#define ISL12022_REG_DT 0x03
+#define ISL12022_REG_MO 0x04
+#define ISL12022_REG_YR 0x05
+#define ISL12022_REG_DW 0x06
+
+#define ISL12022_REG_SR 0x07
+#define ISL12022_REG_INT 0x08
+
+/* ISL register bits */
+#define ISL12022_HR_MIL (1 << 7) /* military or 24 hour time */
+
+#define ISL12022_SR_LBAT85 (1 << 2)
+#define ISL12022_SR_LBAT75 (1 << 1)
+
+#define ISL12022_INT_WRTC (1 << 6)
+
+
+static struct i2c_driver isl12022_driver;
+
+struct isl12022 {
+ struct rtc_device *rtc;
+
+ bool write_enabled; /* true if write enable is set */
+};
+
+
+static int isl12022_read_regs(struct i2c_client *client, uint8_t reg,
+ uint8_t *data, size_t n)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = data
+ }, /* setup read ptr */
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = n,
+ .buf = data
+ }
+ };
+
+ int ret;
+
+ data[0] = reg;
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs)) {
+ dev_err(&client->dev, "%s: read error, ret=%d\n",
+ __func__, ret);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+static int isl12022_write_reg(struct i2c_client *client,
+ uint8_t reg, uint8_t val)
+{
+ uint8_t data[2] = { reg, val };
+ int err;
+
+ err = i2c_master_send(client, data, sizeof(data));
+ if (err != sizeof(data)) {
+ dev_err(&client->dev,
+ "%s: err=%d addr=%02x, data=%02x\n",
+ __func__, err, data[0], data[1]);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+/*
+ * In the routines that deal directly with the isl12022 hardware, we use
+ * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
+ */
+static int isl12022_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ uint8_t buf[ISL12022_REG_INT + 1];
+ int ret;
+
+ ret = isl12022_read_regs(client, ISL12022_REG_SC, buf, sizeof(buf));
+ if (ret)
+ return ret;
+
+ if (buf[ISL12022_REG_SR] & (ISL12022_SR_LBAT85 | ISL12022_SR_LBAT75)) {
+ dev_warn(&client->dev,
+ "voltage dropped below %u%%, "
+ "date and time is not reliable.\n",
+ buf[ISL12022_REG_SR] & ISL12022_SR_LBAT85 ? 85 : 75);
+ }
+
+ dev_dbg(&client->dev,
+ "%s: raw data is sec=%02x, min=%02x, hr=%02x, "
+ "mday=%02x, mon=%02x, year=%02x, wday=%02x, "
+ "sr=%02x, int=%02x",
+ __func__,
+ buf[ISL12022_REG_SC],
+ buf[ISL12022_REG_MN],
+ buf[ISL12022_REG_HR],
+ buf[ISL12022_REG_DT],
+ buf[ISL12022_REG_MO],
+ buf[ISL12022_REG_YR],
+ buf[ISL12022_REG_DW],
+ buf[ISL12022_REG_SR],
+ buf[ISL12022_REG_INT]);
+
+ tm->tm_sec = bcd2bin(buf[ISL12022_REG_SC] & 0x7F);
+ tm->tm_min = bcd2bin(buf[ISL12022_REG_MN] & 0x7F);
+ tm->tm_hour = bcd2bin(buf[ISL12022_REG_HR] & 0x3F);
+ tm->tm_mday = bcd2bin(buf[ISL12022_REG_DT] & 0x3F);
+ tm->tm_wday = buf[ISL12022_REG_DW] & 0x07;
+ tm->tm_mon = bcd2bin(buf[ISL12022_REG_MO] & 0x1F) - 1;
+ tm->tm_year = bcd2bin(buf[ISL12022_REG_YR]) + 100;
+
+ dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __func__,
+ tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ /* The clock can give out invalid datetime, but we cannot return
+ * -EINVAL otherwise hwclock will refuse to set the time on bootup. */
+ if (rtc_valid_tm(tm) < 0)
+ dev_err(&client->dev, "retrieved date and time is invalid.\n");
+
+ return 0;
+}
+
+static int isl12022_set_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ struct isl12022 *isl12022 = i2c_get_clientdata(client);
+ size_t i;
+ int ret;
+ uint8_t buf[ISL12022_REG_DW + 1];
+
+ dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __func__,
+ tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ if (!isl12022->write_enabled) {
+
+ ret = isl12022_read_regs(client, ISL12022_REG_INT, buf, 1);
+ if (ret)
+ return ret;
+
+ /* Check if WRTC (write rtc enable) is set factory default is
+ * 0 (not set) */
+ if (!(buf[0] & ISL12022_INT_WRTC)) {
+ dev_info(&client->dev,
+ "init write enable and 24 hour format\n");
+
+ /* Set the write enable bit. */
+ ret = isl12022_write_reg(client,
+ ISL12022_REG_INT,
+ buf[0] | ISL12022_INT_WRTC);
+ if (ret)
+ return ret;
+
+ /* Write to any RTC register to start RTC, we use the
+ * HR register, setting the MIL bit to use the 24 hour
+ * format. */
+ ret = isl12022_read_regs(client, ISL12022_REG_HR,
+ buf, 1);
+ if (ret)
+ return ret;
+
+ ret = isl12022_write_reg(client,
+ ISL12022_REG_HR,
+ buf[0] | ISL12022_HR_MIL);
+ if (ret)
+ return ret;
+ }
+
+ isl12022->write_enabled = 1;
+ }
+
+ /* hours, minutes and seconds */
+ buf[ISL12022_REG_SC] = bin2bcd(tm->tm_sec);
+ buf[ISL12022_REG_MN] = bin2bcd(tm->tm_min);
+ buf[ISL12022_REG_HR] = bin2bcd(tm->tm_hour) | ISL12022_HR_MIL;
+
+ buf[ISL12022_REG_DT] = bin2bcd(tm->tm_mday);
+
+ /* month, 1 - 12 */
+ buf[ISL12022_REG_MO] = bin2bcd(tm->tm_mon + 1);
+
+ /* year and century */
+ buf[ISL12022_REG_YR] = bin2bcd(tm->tm_year % 100);
+
+ buf[ISL12022_REG_DW] = tm->tm_wday & 0x07;
+
+ /* write register's data */
+ for (i = 0; i < ARRAY_SIZE(buf); i++) {
+ ret = isl12022_write_reg(client, ISL12022_REG_SC + i,
+ buf[ISL12022_REG_SC + i]);
+ if (ret)
+ return -EIO;
+ };
+
+ return 0;
+}
+
+static int isl12022_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ return isl12022_get_datetime(to_i2c_client(dev), tm);
+}
+
+static int isl12022_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ return isl12022_set_datetime(to_i2c_client(dev), tm);
+}
+
+static const struct rtc_class_ops isl12022_rtc_ops = {
+ .read_time = isl12022_rtc_read_time,
+ .set_time = isl12022_rtc_set_time,
+};
+
+static int isl12022_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct isl12022 *isl12022;
+
+ int ret = 0;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ isl12022 = kzalloc(sizeof(struct isl12022), GFP_KERNEL);
+ if (!isl12022)
+ return -ENOMEM;
+
+ dev_dbg(&client->dev, "chip found, driver version " DRV_VERSION "\n");
+
+ i2c_set_clientdata(client, isl12022);
+
+ isl12022->rtc = rtc_device_register(isl12022_driver.driver.name,
+ &client->dev,
+ &isl12022_rtc_ops,
+ THIS_MODULE);
+
+ if (IS_ERR(isl12022->rtc)) {
+ ret = PTR_ERR(isl12022->rtc);
+ goto exit_kfree;
+ }
+
+ return 0;
+
+exit_kfree:
+ kfree(isl12022);
+
+ return ret;
+}
+
+static int isl12022_remove(struct i2c_client *client)
+{
+ struct isl12022 *isl12022 = i2c_get_clientdata(client);
+
+ rtc_device_unregister(isl12022->rtc);
+ kfree(isl12022);
+
+ return 0;
+}
+
+static const struct i2c_device_id isl12022_id[] = {
+ { "isl12022", 0 },
+ { "rtc8564", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, isl12022_id);
+
+static struct i2c_driver isl12022_driver = {
+ .driver = {
+ .name = "rtc-isl12022",
+ },
+ .probe = isl12022_probe,
+ .remove = isl12022_remove,
+ .id_table = isl12022_id,
+};
+
+static int __init isl12022_init(void)
+{
+ return i2c_add_driver(&isl12022_driver);
+}
+
+static void __exit isl12022_exit(void)
+{
+ i2c_del_driver(&isl12022_driver);
+}
+
+module_init(isl12022_init);
+module_exit(isl12022_exit);
+
+MODULE_AUTHOR("roman.fietze@telemotive.de");
+MODULE_DESCRIPTION("ISL 12022 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c
index 054e05294af8..468200c38ecb 100644
--- a/drivers/rtc/rtc-isl1208.c
+++ b/drivers/rtc/rtc-isl1208.c
@@ -462,39 +462,16 @@ isl1208_sysfs_store_usr(struct device *dev,
static DEVICE_ATTR(usr, S_IRUGO | S_IWUSR, isl1208_sysfs_show_usr,
isl1208_sysfs_store_usr);
-static int
-isl1208_sysfs_register(struct device *dev)
-{
- int err;
-
- err = device_create_file(dev, &dev_attr_atrim);
- if (err)
- return err;
-
- err = device_create_file(dev, &dev_attr_dtrim);
- if (err) {
- device_remove_file(dev, &dev_attr_atrim);
- return err;
- }
-
- err = device_create_file(dev, &dev_attr_usr);
- if (err) {
- device_remove_file(dev, &dev_attr_atrim);
- device_remove_file(dev, &dev_attr_dtrim);
- }
-
- return 0;
-}
-
-static int
-isl1208_sysfs_unregister(struct device *dev)
-{
- device_remove_file(dev, &dev_attr_dtrim);
- device_remove_file(dev, &dev_attr_atrim);
- device_remove_file(dev, &dev_attr_usr);
+static struct attribute *isl1208_rtc_attrs[] = {
+ &dev_attr_atrim.attr,
+ &dev_attr_dtrim.attr,
+ &dev_attr_usr.attr,
+ NULL
+};
- return 0;
-}
+static const struct attribute_group isl1208_rtc_sysfs_files = {
+ .attrs = isl1208_rtc_attrs,
+};
static int
isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
@@ -529,7 +506,7 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
dev_warn(&client->dev, "rtc power failure detected, "
"please set clock.\n");
- rc = isl1208_sysfs_register(&client->dev);
+ rc = sysfs_create_group(&client->dev.kobj, &isl1208_rtc_sysfs_files);
if (rc)
goto exit_unregister;
@@ -546,7 +523,7 @@ isl1208_remove(struct i2c_client *client)
{
struct rtc_device *rtc = i2c_get_clientdata(client);
- isl1208_sysfs_unregister(&client->dev);
+ sysfs_remove_group(&client->dev.kobj, &isl1208_rtc_sysfs_files);
rtc_device_unregister(rtc);
return 0;
diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c
new file mode 100644
index 000000000000..2e16f72c9056
--- /dev/null
+++ b/drivers/rtc/rtc-jz4740.c
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
+ * Copyright (C) 2010, Paul Cercueil <paul@crapouillou.net>
+ * JZ4740 SoC RTC driver
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#define JZ_REG_RTC_CTRL 0x00
+#define JZ_REG_RTC_SEC 0x04
+#define JZ_REG_RTC_SEC_ALARM 0x08
+#define JZ_REG_RTC_REGULATOR 0x0C
+#define JZ_REG_RTC_HIBERNATE 0x20
+#define JZ_REG_RTC_SCRATCHPAD 0x34
+
+#define JZ_RTC_CTRL_WRDY BIT(7)
+#define JZ_RTC_CTRL_1HZ BIT(6)
+#define JZ_RTC_CTRL_1HZ_IRQ BIT(5)
+#define JZ_RTC_CTRL_AF BIT(4)
+#define JZ_RTC_CTRL_AF_IRQ BIT(3)
+#define JZ_RTC_CTRL_AE BIT(2)
+#define JZ_RTC_CTRL_ENABLE BIT(0)
+
+struct jz4740_rtc {
+ struct resource *mem;
+ void __iomem *base;
+
+ struct rtc_device *rtc;
+
+ unsigned int irq;
+
+ spinlock_t lock;
+};
+
+static inline uint32_t jz4740_rtc_reg_read(struct jz4740_rtc *rtc, size_t reg)
+{
+ return readl(rtc->base + reg);
+}
+
+static int jz4740_rtc_wait_write_ready(struct jz4740_rtc *rtc)
+{
+ uint32_t ctrl;
+ int timeout = 1000;
+
+ do {
+ ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL);
+ } while (!(ctrl & JZ_RTC_CTRL_WRDY) && --timeout);
+
+ return timeout ? 0 : -EIO;
+}
+
+static inline int jz4740_rtc_reg_write(struct jz4740_rtc *rtc, size_t reg,
+ uint32_t val)
+{
+ int ret;
+ ret = jz4740_rtc_wait_write_ready(rtc);
+ if (ret == 0)
+ writel(val, rtc->base + reg);
+
+ return ret;
+}
+
+static int jz4740_rtc_ctrl_set_bits(struct jz4740_rtc *rtc, uint32_t mask,
+ bool set)
+{
+ int ret;
+ unsigned long flags;
+ uint32_t ctrl;
+
+ spin_lock_irqsave(&rtc->lock, flags);
+
+ ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL);
+
+ /* Don't clear interrupt flags by accident */
+ ctrl |= JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF;
+
+ if (set)
+ ctrl |= mask;
+ else
+ ctrl &= ~mask;
+
+ ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_CTRL, ctrl);
+
+ spin_unlock_irqrestore(&rtc->lock, flags);
+
+ return ret;
+}
+
+static int jz4740_rtc_read_time(struct device *dev, struct rtc_time *time)
+{
+ struct jz4740_rtc *rtc = dev_get_drvdata(dev);
+ uint32_t secs, secs2;
+ int timeout = 5;
+
+ /* If the seconds register is read while it is updated, it can contain a
+ * bogus value. This can be avoided by making sure that two consecutive
+ * reads have the same value.
+ */
+ secs = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC);
+ secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC);
+
+ while (secs != secs2 && --timeout) {
+ secs = secs2;
+ secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC);
+ }
+
+ if (timeout == 0)
+ return -EIO;
+
+ rtc_time_to_tm(secs, time);
+
+ return rtc_valid_tm(time);
+}
+
+static int jz4740_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+ struct jz4740_rtc *rtc = dev_get_drvdata(dev);
+
+ return jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, secs);
+}
+
+static int jz4740_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct jz4740_rtc *rtc = dev_get_drvdata(dev);
+ uint32_t secs;
+ uint32_t ctrl;
+
+ secs = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC_ALARM);
+
+ ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL);
+
+ alrm->enabled = !!(ctrl & JZ_RTC_CTRL_AE);
+ alrm->pending = !!(ctrl & JZ_RTC_CTRL_AF);
+
+ rtc_time_to_tm(secs, &alrm->time);
+
+ return rtc_valid_tm(&alrm->time);
+}
+
+static int jz4740_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ int ret;
+ struct jz4740_rtc *rtc = dev_get_drvdata(dev);
+ unsigned long secs;
+
+ rtc_tm_to_time(&alrm->time, &secs);
+
+ ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC_ALARM, secs);
+ if (!ret)
+ ret = jz4740_rtc_ctrl_set_bits(rtc,
+ JZ_RTC_CTRL_AE | JZ_RTC_CTRL_AF_IRQ, alrm->enabled);
+
+ return ret;
+}
+
+static int jz4740_rtc_update_irq_enable(struct device *dev, unsigned int enable)
+{
+ struct jz4740_rtc *rtc = dev_get_drvdata(dev);
+ return jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_1HZ_IRQ, enable);
+}
+
+static int jz4740_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
+{
+ struct jz4740_rtc *rtc = dev_get_drvdata(dev);
+ return jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_AF_IRQ, enable);
+}
+
+static struct rtc_class_ops jz4740_rtc_ops = {
+ .read_time = jz4740_rtc_read_time,
+ .set_mmss = jz4740_rtc_set_mmss,
+ .read_alarm = jz4740_rtc_read_alarm,
+ .set_alarm = jz4740_rtc_set_alarm,
+ .update_irq_enable = jz4740_rtc_update_irq_enable,
+ .alarm_irq_enable = jz4740_rtc_alarm_irq_enable,
+};
+
+static irqreturn_t jz4740_rtc_irq(int irq, void *data)
+{
+ struct jz4740_rtc *rtc = data;
+ uint32_t ctrl;
+ unsigned long events = 0;
+
+ ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL);
+
+ if (ctrl & JZ_RTC_CTRL_1HZ)
+ events |= (RTC_UF | RTC_IRQF);
+
+ if (ctrl & JZ_RTC_CTRL_AF)
+ events |= (RTC_AF | RTC_IRQF);
+
+ rtc_update_irq(rtc->rtc, 1, events);
+
+ jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF, false);
+
+ return IRQ_HANDLED;
+}
+
+void jz4740_rtc_poweroff(struct device *dev)
+{
+ struct jz4740_rtc *rtc = dev_get_drvdata(dev);
+ jz4740_rtc_reg_write(rtc, JZ_REG_RTC_HIBERNATE, 1);
+}
+EXPORT_SYMBOL_GPL(jz4740_rtc_poweroff);
+
+static int __devinit jz4740_rtc_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct jz4740_rtc *rtc;
+ uint32_t scratchpad;
+
+ rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ rtc->irq = platform_get_irq(pdev, 0);
+ if (rtc->irq < 0) {
+ ret = -ENOENT;
+ dev_err(&pdev->dev, "Failed to get platform irq\n");
+ goto err_free;
+ }
+
+ rtc->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!rtc->mem) {
+ ret = -ENOENT;
+ dev_err(&pdev->dev, "Failed to get platform mmio memory\n");
+ goto err_free;
+ }
+
+ rtc->mem = request_mem_region(rtc->mem->start, resource_size(rtc->mem),
+ pdev->name);
+ if (!rtc->mem) {
+ ret = -EBUSY;
+ dev_err(&pdev->dev, "Failed to request mmio memory region\n");
+ goto err_free;
+ }
+
+ rtc->base = ioremap_nocache(rtc->mem->start, resource_size(rtc->mem));
+ if (!rtc->base) {
+ ret = -EBUSY;
+ dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
+ goto err_release_mem_region;
+ }
+
+ spin_lock_init(&rtc->lock);
+
+ platform_set_drvdata(pdev, rtc);
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, &jz4740_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc->rtc)) {
+ ret = PTR_ERR(rtc->rtc);
+ dev_err(&pdev->dev, "Failed to register rtc device: %d\n", ret);
+ goto err_iounmap;
+ }
+
+ ret = request_irq(rtc->irq, jz4740_rtc_irq, 0,
+ pdev->name, rtc);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request rtc irq: %d\n", ret);
+ goto err_unregister_rtc;
+ }
+
+ scratchpad = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SCRATCHPAD);
+ if (scratchpad != 0x12345678) {
+ ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SCRATCHPAD, 0x12345678);
+ ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not write write to RTC registers\n");
+ goto err_free_irq;
+ }
+ }
+
+ return 0;
+
+err_free_irq:
+ free_irq(rtc->irq, rtc);
+err_unregister_rtc:
+ rtc_device_unregister(rtc->rtc);
+err_iounmap:
+ platform_set_drvdata(pdev, NULL);
+ iounmap(rtc->base);
+err_release_mem_region:
+ release_mem_region(rtc->mem->start, resource_size(rtc->mem));
+err_free:
+ kfree(rtc);
+
+ return ret;
+}
+
+static int __devexit jz4740_rtc_remove(struct platform_device *pdev)
+{
+ struct jz4740_rtc *rtc = platform_get_drvdata(pdev);
+
+ free_irq(rtc->irq, rtc);
+
+ rtc_device_unregister(rtc->rtc);
+
+ iounmap(rtc->base);
+ release_mem_region(rtc->mem->start, resource_size(rtc->mem));
+
+ kfree(rtc);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+
+#ifdef CONFIG_PM
+static int jz4740_rtc_suspend(struct device *dev)
+{
+ struct jz4740_rtc *rtc = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(rtc->irq);
+ return 0;
+}
+
+static int jz4740_rtc_resume(struct device *dev)
+{
+ struct jz4740_rtc *rtc = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(rtc->irq);
+ return 0;
+}
+
+static const struct dev_pm_ops jz4740_pm_ops = {
+ .suspend = jz4740_rtc_suspend,
+ .resume = jz4740_rtc_resume,
+};
+#define JZ4740_RTC_PM_OPS (&jz4740_pm_ops)
+
+#else
+#define JZ4740_RTC_PM_OPS NULL
+#endif /* CONFIG_PM */
+
+struct platform_driver jz4740_rtc_driver = {
+ .probe = jz4740_rtc_probe,
+ .remove = __devexit_p(jz4740_rtc_remove),
+ .driver = {
+ .name = "jz4740-rtc",
+ .owner = THIS_MODULE,
+ .pm = JZ4740_RTC_PM_OPS,
+ },
+};
+
+static int __init jz4740_rtc_init(void)
+{
+ return platform_driver_register(&jz4740_rtc_driver);
+}
+module_init(jz4740_rtc_init);
+
+static void __exit jz4740_rtc_exit(void)
+{
+ platform_driver_unregister(&jz4740_rtc_driver);
+}
+module_exit(jz4740_rtc_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("RTC driver for the JZ4740 SoC\n");
+MODULE_ALIAS("platform:jz4740-rtc");
diff --git a/drivers/rtc/rtc-lpc32xx.c b/drivers/rtc/rtc-lpc32xx.c
new file mode 100644
index 000000000000..ec8701ce99f9
--- /dev/null
+++ b/drivers/rtc/rtc-lpc32xx.c
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2010 NXP Semiconductors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+
+/*
+ * Clock and Power control register offsets
+ */
+#define LPC32XX_RTC_UCOUNT 0x00
+#define LPC32XX_RTC_DCOUNT 0x04
+#define LPC32XX_RTC_MATCH0 0x08
+#define LPC32XX_RTC_MATCH1 0x0C
+#define LPC32XX_RTC_CTRL 0x10
+#define LPC32XX_RTC_INTSTAT 0x14
+#define LPC32XX_RTC_KEY 0x18
+#define LPC32XX_RTC_SRAM 0x80
+
+#define LPC32XX_RTC_CTRL_MATCH0 (1 << 0)
+#define LPC32XX_RTC_CTRL_MATCH1 (1 << 1)
+#define LPC32XX_RTC_CTRL_ONSW_MATCH0 (1 << 2)
+#define LPC32XX_RTC_CTRL_ONSW_MATCH1 (1 << 3)
+#define LPC32XX_RTC_CTRL_SW_RESET (1 << 4)
+#define LPC32XX_RTC_CTRL_CNTR_DIS (1 << 6)
+#define LPC32XX_RTC_CTRL_ONSW_FORCE_HI (1 << 7)
+
+#define LPC32XX_RTC_INTSTAT_MATCH0 (1 << 0)
+#define LPC32XX_RTC_INTSTAT_MATCH1 (1 << 1)
+#define LPC32XX_RTC_INTSTAT_ONSW (1 << 2)
+
+#define LPC32XX_RTC_KEY_ONSW_LOADVAL 0xB5C13F27
+
+#define RTC_NAME "rtc-lpc32xx"
+
+#define rtc_readl(dev, reg) \
+ __raw_readl((dev)->rtc_base + (reg))
+#define rtc_writel(dev, reg, val) \
+ __raw_writel((val), (dev)->rtc_base + (reg))
+
+struct lpc32xx_rtc {
+ void __iomem *rtc_base;
+ int irq;
+ unsigned char alarm_enabled;
+ struct rtc_device *rtc;
+ spinlock_t lock;
+};
+
+static int lpc32xx_rtc_read_time(struct device *dev, struct rtc_time *time)
+{
+ unsigned long elapsed_sec;
+ struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
+
+ elapsed_sec = rtc_readl(rtc, LPC32XX_RTC_UCOUNT);
+ rtc_time_to_tm(elapsed_sec, time);
+
+ return rtc_valid_tm(time);
+}
+
+static int lpc32xx_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+ struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
+ u32 tmp;
+
+ spin_lock_irq(&rtc->lock);
+
+ /* RTC must be disabled during count update */
+ tmp = rtc_readl(rtc, LPC32XX_RTC_CTRL);
+ rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp | LPC32XX_RTC_CTRL_CNTR_DIS);
+ rtc_writel(rtc, LPC32XX_RTC_UCOUNT, secs);
+ rtc_writel(rtc, LPC32XX_RTC_DCOUNT, 0xFFFFFFFF - secs);
+ rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp &= ~LPC32XX_RTC_CTRL_CNTR_DIS);
+
+ spin_unlock_irq(&rtc->lock);
+
+ return 0;
+}
+
+static int lpc32xx_rtc_read_alarm(struct device *dev,
+ struct rtc_wkalrm *wkalrm)
+{
+ struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
+
+ rtc_time_to_tm(rtc_readl(rtc, LPC32XX_RTC_MATCH0), &wkalrm->time);
+ wkalrm->enabled = rtc->alarm_enabled;
+ wkalrm->pending = !!(rtc_readl(rtc, LPC32XX_RTC_INTSTAT) &
+ LPC32XX_RTC_INTSTAT_MATCH0);
+
+ return rtc_valid_tm(&wkalrm->time);
+}
+
+static int lpc32xx_rtc_set_alarm(struct device *dev,
+ struct rtc_wkalrm *wkalrm)
+{
+ struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
+ unsigned long alarmsecs;
+ u32 tmp;
+ int ret;
+
+ ret = rtc_tm_to_time(&wkalrm->time, &alarmsecs);
+ if (ret < 0) {
+ dev_warn(dev, "Failed to convert time: %d\n", ret);
+ return ret;
+ }
+
+ spin_lock_irq(&rtc->lock);
+
+ /* Disable alarm during update */
+ tmp = rtc_readl(rtc, LPC32XX_RTC_CTRL);
+ rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp & ~LPC32XX_RTC_CTRL_MATCH0);
+
+ rtc_writel(rtc, LPC32XX_RTC_MATCH0, alarmsecs);
+
+ rtc->alarm_enabled = wkalrm->enabled;
+ if (wkalrm->enabled) {
+ rtc_writel(rtc, LPC32XX_RTC_INTSTAT,
+ LPC32XX_RTC_INTSTAT_MATCH0);
+ rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp |
+ LPC32XX_RTC_CTRL_MATCH0);
+ }
+
+ spin_unlock_irq(&rtc->lock);
+
+ return 0;
+}
+
+static int lpc32xx_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
+ u32 tmp;
+
+ spin_lock_irq(&rtc->lock);
+ tmp = rtc_readl(rtc, LPC32XX_RTC_CTRL);
+
+ if (enabled) {
+ rtc->alarm_enabled = 1;
+ tmp |= LPC32XX_RTC_CTRL_MATCH0;
+ } else {
+ rtc->alarm_enabled = 0;
+ tmp &= ~LPC32XX_RTC_CTRL_MATCH0;
+ }
+
+ rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp);
+ spin_unlock_irq(&rtc->lock);
+
+ return 0;
+}
+
+static irqreturn_t lpc32xx_rtc_alarm_interrupt(int irq, void *dev)
+{
+ struct lpc32xx_rtc *rtc = dev;
+
+ spin_lock(&rtc->lock);
+
+ /* Disable alarm interrupt */
+ rtc_writel(rtc, LPC32XX_RTC_CTRL,
+ rtc_readl(rtc, LPC32XX_RTC_CTRL) &
+ ~LPC32XX_RTC_CTRL_MATCH0);
+ rtc->alarm_enabled = 0;
+
+ /*
+ * Write a large value to the match value so the RTC won't
+ * keep firing the match status
+ */
+ rtc_writel(rtc, LPC32XX_RTC_MATCH0, 0xFFFFFFFF);
+ rtc_writel(rtc, LPC32XX_RTC_INTSTAT, LPC32XX_RTC_INTSTAT_MATCH0);
+
+ spin_unlock(&rtc->lock);
+
+ rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops lpc32xx_rtc_ops = {
+ .read_time = lpc32xx_rtc_read_time,
+ .set_mmss = lpc32xx_rtc_set_mmss,
+ .read_alarm = lpc32xx_rtc_read_alarm,
+ .set_alarm = lpc32xx_rtc_set_alarm,
+ .alarm_irq_enable = lpc32xx_rtc_alarm_irq_enable,
+};
+
+static int __devinit lpc32xx_rtc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct lpc32xx_rtc *rtc;
+ resource_size_t size;
+ int rtcirq;
+ u32 tmp;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "Can't get memory resource\n");
+ return -ENOENT;
+ }
+
+ rtcirq = platform_get_irq(pdev, 0);
+ if (rtcirq < 0 || rtcirq >= NR_IRQS) {
+ dev_warn(&pdev->dev, "Can't get interrupt resource\n");
+ rtcirq = -1;
+ }
+
+ rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
+ if (unlikely(!rtc)) {
+ dev_err(&pdev->dev, "Can't allocate memory\n");
+ return -ENOMEM;
+ }
+ rtc->irq = rtcirq;
+
+ size = resource_size(res);
+
+ if (!devm_request_mem_region(&pdev->dev, res->start, size,
+ pdev->name)) {
+ dev_err(&pdev->dev, "RTC registers are not free\n");
+ return -EBUSY;
+ }
+
+ rtc->rtc_base = devm_ioremap(&pdev->dev, res->start, size);
+ if (!rtc->rtc_base) {
+ dev_err(&pdev->dev, "Can't map memory\n");
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&rtc->lock);
+
+ /*
+ * The RTC is on a seperate power domain and can keep it's state
+ * across a chip power cycle. If the RTC has never been previously
+ * setup, then set it up now for the first time.
+ */
+ tmp = rtc_readl(rtc, LPC32XX_RTC_CTRL);
+ if (rtc_readl(rtc, LPC32XX_RTC_KEY) != LPC32XX_RTC_KEY_ONSW_LOADVAL) {
+ tmp &= ~(LPC32XX_RTC_CTRL_SW_RESET |
+ LPC32XX_RTC_CTRL_CNTR_DIS |
+ LPC32XX_RTC_CTRL_MATCH0 |
+ LPC32XX_RTC_CTRL_MATCH1 |
+ LPC32XX_RTC_CTRL_ONSW_MATCH0 |
+ LPC32XX_RTC_CTRL_ONSW_MATCH1 |
+ LPC32XX_RTC_CTRL_ONSW_FORCE_HI);
+ rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp);
+
+ /* Clear latched interrupt states */
+ rtc_writel(rtc, LPC32XX_RTC_MATCH0, 0xFFFFFFFF);
+ rtc_writel(rtc, LPC32XX_RTC_INTSTAT,
+ LPC32XX_RTC_INTSTAT_MATCH0 |
+ LPC32XX_RTC_INTSTAT_MATCH1 |
+ LPC32XX_RTC_INTSTAT_ONSW);
+
+ /* Write key value to RTC so it won't reload on reset */
+ rtc_writel(rtc, LPC32XX_RTC_KEY,
+ LPC32XX_RTC_KEY_ONSW_LOADVAL);
+ } else {
+ rtc_writel(rtc, LPC32XX_RTC_CTRL,
+ tmp & ~LPC32XX_RTC_CTRL_MATCH0);
+ }
+
+ platform_set_drvdata(pdev, rtc);
+
+ rtc->rtc = rtc_device_register(RTC_NAME, &pdev->dev, &lpc32xx_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc->rtc)) {
+ dev_err(&pdev->dev, "Can't get RTC\n");
+ platform_set_drvdata(pdev, NULL);
+ return PTR_ERR(rtc->rtc);
+ }
+
+ /*
+ * IRQ is enabled after device registration in case alarm IRQ
+ * is pending upon suspend exit.
+ */
+ if (rtc->irq >= 0) {
+ if (devm_request_irq(&pdev->dev, rtc->irq,
+ lpc32xx_rtc_alarm_interrupt,
+ IRQF_DISABLED, pdev->name, rtc) < 0) {
+ dev_warn(&pdev->dev, "Can't request interrupt.\n");
+ rtc->irq = -1;
+ } else {
+ device_init_wakeup(&pdev->dev, 1);
+ }
+ }
+
+ return 0;
+}
+
+static int __devexit lpc32xx_rtc_remove(struct platform_device *pdev)
+{
+ struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev);
+
+ if (rtc->irq >= 0)
+ device_init_wakeup(&pdev->dev, 0);
+
+ platform_set_drvdata(pdev, NULL);
+ rtc_device_unregister(rtc->rtc);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int lpc32xx_rtc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev);
+
+ if (rtc->irq >= 0) {
+ if (device_may_wakeup(&pdev->dev))
+ enable_irq_wake(rtc->irq);
+ else
+ disable_irq_wake(rtc->irq);
+ }
+
+ return 0;
+}
+
+static int lpc32xx_rtc_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev);
+
+ if (rtc->irq >= 0 && device_may_wakeup(&pdev->dev))
+ disable_irq_wake(rtc->irq);
+
+ return 0;
+}
+
+/* Unconditionally disable the alarm */
+static int lpc32xx_rtc_freeze(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev);
+
+ spin_lock_irq(&rtc->lock);
+
+ rtc_writel(rtc, LPC32XX_RTC_CTRL,
+ rtc_readl(rtc, LPC32XX_RTC_CTRL) &
+ ~LPC32XX_RTC_CTRL_MATCH0);
+
+ spin_unlock_irq(&rtc->lock);
+
+ return 0;
+}
+
+static int lpc32xx_rtc_thaw(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev);
+
+ if (rtc->alarm_enabled) {
+ spin_lock_irq(&rtc->lock);
+
+ rtc_writel(rtc, LPC32XX_RTC_CTRL,
+ rtc_readl(rtc, LPC32XX_RTC_CTRL) |
+ LPC32XX_RTC_CTRL_MATCH0);
+
+ spin_unlock_irq(&rtc->lock);
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops lpc32xx_rtc_pm_ops = {
+ .suspend = lpc32xx_rtc_suspend,
+ .resume = lpc32xx_rtc_resume,
+ .freeze = lpc32xx_rtc_freeze,
+ .thaw = lpc32xx_rtc_thaw,
+ .restore = lpc32xx_rtc_resume
+};
+
+#define LPC32XX_RTC_PM_OPS (&lpc32xx_rtc_pm_ops)
+#else
+#define LPC32XX_RTC_PM_OPS NULL
+#endif
+
+static struct platform_driver lpc32xx_rtc_driver = {
+ .probe = lpc32xx_rtc_probe,
+ .remove = __devexit_p(lpc32xx_rtc_remove),
+ .driver = {
+ .name = RTC_NAME,
+ .owner = THIS_MODULE,
+ .pm = LPC32XX_RTC_PM_OPS
+ },
+};
+
+static int __init lpc32xx_rtc_init(void)
+{
+ return platform_driver_register(&lpc32xx_rtc_driver);
+}
+module_init(lpc32xx_rtc_init);
+
+static void __exit lpc32xx_rtc_exit(void)
+{
+ platform_driver_unregister(&lpc32xx_rtc_driver);
+}
+module_exit(lpc32xx_rtc_exit);
+
+MODULE_AUTHOR("Kevin Wells <wellsk40@gmail.com");
+MODULE_DESCRIPTION("RTC driver for the LPC32xx SoC");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:rtc-lpc32xx");
diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c
index 60fe266f0f49..5a8daa358066 100644
--- a/drivers/rtc/rtc-m41t80.c
+++ b/drivers/rtc/rtc-m41t80.c
@@ -20,7 +20,7 @@
#include <linux/module.h>
#include <linux/rtc.h>
#include <linux/slab.h>
-#include <linux/smp_lock.h>
+#include <linux/mutex.h>
#include <linux/string.h>
#ifdef CONFIG_RTC_DRV_M41T80_WDT
#include <linux/fs.h>
@@ -68,6 +68,7 @@
#define DRV_VERSION "0.05"
+static DEFINE_MUTEX(m41t80_rtc_mutex);
static const struct i2c_device_id m41t80_id[] = {
{ "m41t62", M41T80_FEATURE_SQ | M41T80_FEATURE_SQ_ALT },
{ "m41t65", M41T80_FEATURE_HT | M41T80_FEATURE_WD },
@@ -121,7 +122,7 @@ static int m41t80_get_datetime(struct i2c_client *client,
/* assume 20YY not 19YY, and ignore the Century Bit */
tm->tm_year = bcd2bin(buf[M41T80_REG_YEAR]) + 100;
- return 0;
+ return rtc_valid_tm(tm);
}
/* Sets the given date and time to the real time clock. */
@@ -595,10 +596,6 @@ static void wdt_disable(void)
static ssize_t wdt_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- /* Can't seek (pwrite) on this device
- if (ppos != &file->f_pos)
- return -ESPIPE;
- */
if (count) {
wdt_ping();
return 1;
@@ -623,7 +620,7 @@ static ssize_t wdt_read(struct file *file, char __user *buf,
* according to their available features. We only actually usefully support
* querying capabilities and current status.
*/
-static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+static int wdt_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
int new_margin, rv;
@@ -676,6 +673,18 @@ static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
return -ENOTTY;
}
+static long wdt_unlocked_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret;
+
+ mutex_lock(&m41t80_rtc_mutex);
+ ret = wdt_ioctl(file, cmd, arg);
+ mutex_unlock(&m41t80_rtc_mutex);
+
+ return ret;
+}
+
/**
* wdt_open:
* @inode: inode of device
@@ -685,17 +694,17 @@ static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
static int wdt_open(struct inode *inode, struct file *file)
{
if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) {
- lock_kernel();
+ mutex_lock(&m41t80_rtc_mutex);
if (test_and_set_bit(0, &wdt_is_open)) {
- unlock_kernel();
+ mutex_unlock(&m41t80_rtc_mutex);
return -EBUSY;
}
/*
* Activate
*/
wdt_is_open = 1;
- unlock_kernel();
- return 0;
+ mutex_unlock(&m41t80_rtc_mutex);
+ return nonseekable_open(inode, file);
}
return -ENODEV;
}
@@ -736,10 +745,11 @@ static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
static const struct file_operations wdt_fops = {
.owner = THIS_MODULE,
.read = wdt_read,
- .ioctl = wdt_ioctl,
+ .unlocked_ioctl = wdt_unlocked_ioctl,
.write = wdt_write,
.open = wdt_open,
.release = wdt_release,
+ .llseek = no_llseek,
};
static struct miscdevice wdt_dev = {
diff --git a/drivers/rtc/rtc-m48t35.c b/drivers/rtc/rtc-m48t35.c
index 8cb5b8959e5b..7410875e5838 100644
--- a/drivers/rtc/rtc-m48t35.c
+++ b/drivers/rtc/rtc-m48t35.c
@@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/rtc.h>
+#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/bcd.h>
#include <linux/io.h>
diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c
index ede43b846859..a99a0b554eb8 100644
--- a/drivers/rtc/rtc-m48t59.c
+++ b/drivers/rtc/rtc-m48t59.c
@@ -19,6 +19,7 @@
#include <linux/rtc.h>
#include <linux/rtc/m48t59.h>
#include <linux/bcd.h>
+#include <linux/slab.h>
#ifndef NO_IRQ
#define NO_IRQ (-1)
@@ -104,7 +105,7 @@ static int m48t59_rtc_read_time(struct device *dev, struct rtc_time *tm)
dev_dbg(dev, "RTC read time %04d-%02d-%02d %02d/%02d/%02d\n",
tm->tm_year + 1900, tm->tm_mon, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
- return 0;
+ return rtc_valid_tm(tm);
}
static int m48t59_rtc_set_time(struct device *dev, struct rtc_time *tm)
@@ -195,7 +196,7 @@ static int m48t59_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
dev_dbg(dev, "RTC read alarm time %04d-%02d-%02d %02d/%02d/%02d\n",
tm->tm_year + 1900, tm->tm_mon, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
- return 0;
+ return rtc_valid_tm(tm);
}
/*
@@ -342,7 +343,7 @@ static const struct rtc_class_ops m48t02_rtc_ops = {
.set_time = m48t59_rtc_set_time,
};
-static ssize_t m48t59_nvram_read(struct kobject *kobj,
+static ssize_t m48t59_nvram_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t pos, size_t size)
{
@@ -362,7 +363,7 @@ static ssize_t m48t59_nvram_read(struct kobject *kobj,
return cnt;
}
-static ssize_t m48t59_nvram_write(struct kobject *kobj,
+static ssize_t m48t59_nvram_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t pos, size_t size)
{
@@ -505,7 +506,6 @@ out:
free_irq(m48t59->irq, &pdev->dev);
if (m48t59->ioaddr)
iounmap(m48t59->ioaddr);
- if (m48t59)
kfree(m48t59);
return ret;
}
diff --git a/drivers/rtc/rtc-m48t86.c b/drivers/rtc/rtc-m48t86.c
index 7c045cffa9ff..f981287d582b 100644
--- a/drivers/rtc/rtc-m48t86.c
+++ b/drivers/rtc/rtc-m48t86.c
@@ -77,7 +77,7 @@ static int m48t86_rtc_read_time(struct device *dev, struct rtc_time *tm)
if (ops->readbyte(M48T86_REG_HOUR) & 0x80)
tm->tm_hour += 12;
- return 0;
+ return rtc_valid_tm(tm);
}
static int m48t86_rtc_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-max6900.c b/drivers/rtc/rtc-max6900.c
index a4f6665ab3c5..486142c2637a 100644
--- a/drivers/rtc/rtc-max6900.c
+++ b/drivers/rtc/rtc-max6900.c
@@ -159,7 +159,7 @@ static int max6900_i2c_read_time(struct i2c_client *client, struct rtc_time *tm)
bcd2bin(regs[MAX6900_REG_CENTURY]) * 100 - 1900;
tm->tm_wday = bcd2bin(regs[MAX6900_REG_DW]);
- return 0;
+ return rtc_valid_tm(tm);
}
static int max6900_i2c_clear_write_protect(struct i2c_client *client)
diff --git a/drivers/rtc/rtc-max8925.c b/drivers/rtc/rtc-max8925.c
new file mode 100644
index 000000000000..174036dda786
--- /dev/null
+++ b/drivers/rtc/rtc-max8925.c
@@ -0,0 +1,315 @@
+/*
+ * RTC driver for Maxim MAX8925
+ *
+ * Copyright (C) 2009-2010 Marvell International Ltd.
+ * Haojian Zhuang <haojian.zhuang@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/max8925.h>
+
+enum {
+ RTC_SEC = 0,
+ RTC_MIN,
+ RTC_HOUR,
+ RTC_WEEKDAY,
+ RTC_DATE,
+ RTC_MONTH,
+ RTC_YEAR1,
+ RTC_YEAR2,
+};
+
+#define MAX8925_RTC_SEC 0x00
+#define MAX8925_RTC_MIN 0x01
+#define MAX8925_RTC_HOUR 0x02
+#define MAX8925_RTC_WEEKDAY 0x03
+#define MAX8925_RTC_DATE 0x04
+#define MAX8925_RTC_MONTH 0x05
+#define MAX8925_RTC_YEAR1 0x06
+#define MAX8925_RTC_YEAR2 0x07
+#define MAX8925_ALARM0_SEC 0x08
+#define MAX8925_ALARM0_MIN 0x09
+#define MAX8925_ALARM0_HOUR 0x0a
+#define MAX8925_ALARM0_WEEKDAY 0x0b
+#define MAX8925_ALARM0_DATE 0x0c
+#define MAX8925_ALARM0_MON 0x0d
+#define MAX8925_ALARM0_YEAR1 0x0e
+#define MAX8925_ALARM0_YEAR2 0x0f
+#define MAX8925_ALARM1_SEC 0x10
+#define MAX8925_ALARM1_MIN 0x11
+#define MAX8925_ALARM1_HOUR 0x12
+#define MAX8925_ALARM1_WEEKDAY 0x13
+#define MAX8925_ALARM1_DATE 0x14
+#define MAX8925_ALARM1_MON 0x15
+#define MAX8925_ALARM1_YEAR1 0x16
+#define MAX8925_ALARM1_YEAR2 0x17
+#define MAX8925_RTC_CNTL 0x1b
+#define MAX8925_RTC_STATUS 0x20
+
+#define TIME_NUM 8
+#define ALARM_1SEC (1 << 7)
+#define HOUR_12 (1 << 7)
+#define HOUR_AM_PM (1 << 5)
+#define ALARM0_IRQ (1 << 3)
+#define ALARM1_IRQ (1 << 2)
+#define ALARM0_STATUS (1 << 2)
+#define ALARM1_STATUS (1 << 1)
+
+
+struct max8925_rtc_info {
+ struct rtc_device *rtc_dev;
+ struct max8925_chip *chip;
+ struct i2c_client *rtc;
+ struct device *dev;
+};
+
+static irqreturn_t rtc_update_handler(int irq, void *data)
+{
+ struct max8925_rtc_info *info = (struct max8925_rtc_info *)data;
+
+ /* disable ALARM0 except for 1SEC alarm */
+ max8925_set_bits(info->rtc, MAX8925_ALARM0_CNTL, 0x7f, 0);
+ rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
+ return IRQ_HANDLED;
+}
+
+static int tm_calc(struct rtc_time *tm, unsigned char *buf, int len)
+{
+ if (len < TIME_NUM)
+ return -EINVAL;
+ tm->tm_year = (buf[RTC_YEAR2] >> 4) * 1000
+ + (buf[RTC_YEAR2] & 0xf) * 100
+ + (buf[RTC_YEAR1] >> 4) * 10
+ + (buf[RTC_YEAR1] & 0xf);
+ tm->tm_year -= 1900;
+ tm->tm_mon = ((buf[RTC_MONTH] >> 4) & 0x01) * 10
+ + (buf[RTC_MONTH] & 0x0f);
+ tm->tm_mday = ((buf[RTC_DATE] >> 4) & 0x03) * 10
+ + (buf[RTC_DATE] & 0x0f);
+ tm->tm_wday = buf[RTC_WEEKDAY] & 0x07;
+ if (buf[RTC_HOUR] & HOUR_12) {
+ tm->tm_hour = ((buf[RTC_HOUR] >> 4) & 0x1) * 10
+ + (buf[RTC_HOUR] & 0x0f);
+ if (buf[RTC_HOUR] & HOUR_AM_PM)
+ tm->tm_hour += 12;
+ } else
+ tm->tm_hour = ((buf[RTC_HOUR] >> 4) & 0x03) * 10
+ + (buf[RTC_HOUR] & 0x0f);
+ tm->tm_min = ((buf[RTC_MIN] >> 4) & 0x7) * 10
+ + (buf[RTC_MIN] & 0x0f);
+ tm->tm_sec = ((buf[RTC_SEC] >> 4) & 0x7) * 10
+ + (buf[RTC_SEC] & 0x0f);
+ return 0;
+}
+
+static int data_calc(unsigned char *buf, struct rtc_time *tm, int len)
+{
+ unsigned char high, low;
+
+ if (len < TIME_NUM)
+ return -EINVAL;
+
+ high = (tm->tm_year + 1900) / 1000;
+ low = (tm->tm_year + 1900) / 100;
+ low = low - high * 10;
+ buf[RTC_YEAR2] = (high << 4) + low;
+ high = (tm->tm_year + 1900) / 10;
+ low = tm->tm_year + 1900;
+ low = low - high * 10;
+ high = high - (high / 10) * 10;
+ buf[RTC_YEAR1] = (high << 4) + low;
+ high = tm->tm_mon / 10;
+ low = tm->tm_mon;
+ low = low - high * 10;
+ buf[RTC_MONTH] = (high << 4) + low;
+ high = tm->tm_mday / 10;
+ low = tm->tm_mday;
+ low = low - high * 10;
+ buf[RTC_DATE] = (high << 4) + low;
+ buf[RTC_WEEKDAY] = tm->tm_wday;
+ high = tm->tm_hour / 10;
+ low = tm->tm_hour;
+ low = low - high * 10;
+ buf[RTC_HOUR] = (high << 4) + low;
+ high = tm->tm_min / 10;
+ low = tm->tm_min;
+ low = low - high * 10;
+ buf[RTC_MIN] = (high << 4) + low;
+ high = tm->tm_sec / 10;
+ low = tm->tm_sec;
+ low = low - high * 10;
+ buf[RTC_SEC] = (high << 4) + low;
+ return 0;
+}
+
+static int max8925_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max8925_rtc_info *info = dev_get_drvdata(dev);
+ unsigned char buf[TIME_NUM];
+ int ret;
+
+ ret = max8925_bulk_read(info->rtc, MAX8925_RTC_SEC, TIME_NUM, buf);
+ if (ret < 0)
+ goto out;
+ ret = tm_calc(tm, buf, TIME_NUM);
+out:
+ return ret;
+}
+
+static int max8925_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max8925_rtc_info *info = dev_get_drvdata(dev);
+ unsigned char buf[TIME_NUM];
+ int ret;
+
+ ret = data_calc(buf, tm, TIME_NUM);
+ if (ret < 0)
+ goto out;
+ ret = max8925_bulk_write(info->rtc, MAX8925_RTC_SEC, TIME_NUM, buf);
+out:
+ return ret;
+}
+
+static int max8925_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max8925_rtc_info *info = dev_get_drvdata(dev);
+ unsigned char buf[TIME_NUM];
+ int ret;
+
+ ret = max8925_bulk_read(info->rtc, MAX8925_ALARM0_SEC, TIME_NUM, buf);
+ if (ret < 0)
+ goto out;
+ ret = tm_calc(&alrm->time, buf, TIME_NUM);
+ if (ret < 0)
+ goto out;
+ ret = max8925_reg_read(info->rtc, MAX8925_RTC_IRQ_MASK);
+ if (ret < 0)
+ goto out;
+ if ((ret & ALARM0_IRQ) == 0)
+ alrm->enabled = 1;
+ else
+ alrm->enabled = 0;
+ ret = max8925_reg_read(info->rtc, MAX8925_RTC_STATUS);
+ if (ret < 0)
+ goto out;
+ if (ret & ALARM0_STATUS)
+ alrm->pending = 1;
+ else
+ alrm->pending = 0;
+out:
+ return ret;
+}
+
+static int max8925_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max8925_rtc_info *info = dev_get_drvdata(dev);
+ unsigned char buf[TIME_NUM];
+ int ret;
+
+ ret = data_calc(buf, &alrm->time, TIME_NUM);
+ if (ret < 0)
+ goto out;
+ ret = max8925_bulk_write(info->rtc, MAX8925_ALARM0_SEC, TIME_NUM, buf);
+ if (ret < 0)
+ goto out;
+ /* only enable alarm on year/month/day/hour/min/sec */
+ ret = max8925_reg_write(info->rtc, MAX8925_ALARM0_CNTL, 0x77);
+ if (ret < 0)
+ goto out;
+out:
+ return ret;
+}
+
+static const struct rtc_class_ops max8925_rtc_ops = {
+ .read_time = max8925_rtc_read_time,
+ .set_time = max8925_rtc_set_time,
+ .read_alarm = max8925_rtc_read_alarm,
+ .set_alarm = max8925_rtc_set_alarm,
+};
+
+static int __devinit max8925_rtc_probe(struct platform_device *pdev)
+{
+ struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
+ struct max8925_rtc_info *info;
+ int irq, ret;
+
+ info = kzalloc(sizeof(struct max8925_rtc_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ info->chip = chip;
+ info->rtc = chip->rtc;
+ info->dev = &pdev->dev;
+ irq = chip->irq_base + MAX8925_IRQ_RTC_ALARM0;
+
+ ret = request_threaded_irq(irq, NULL, rtc_update_handler,
+ IRQF_ONESHOT, "rtc-alarm0", info);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
+ irq, ret);
+ goto out_irq;
+ }
+
+ info->rtc_dev = rtc_device_register("max8925-rtc", &pdev->dev,
+ &max8925_rtc_ops, THIS_MODULE);
+ ret = PTR_ERR(info->rtc_dev);
+ if (IS_ERR(info->rtc_dev)) {
+ dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
+ goto out_rtc;
+ }
+
+ dev_set_drvdata(&pdev->dev, info);
+ platform_set_drvdata(pdev, info);
+
+ return 0;
+out_rtc:
+ free_irq(chip->irq_base + MAX8925_IRQ_RTC_ALARM0, info);
+out_irq:
+ kfree(info);
+ return ret;
+}
+
+static int __devexit max8925_rtc_remove(struct platform_device *pdev)
+{
+ struct max8925_rtc_info *info = platform_get_drvdata(pdev);
+
+ if (info) {
+ free_irq(info->chip->irq_base + MAX8925_IRQ_RTC_ALARM0, info);
+ rtc_device_unregister(info->rtc_dev);
+ kfree(info);
+ }
+ return 0;
+}
+
+static struct platform_driver max8925_rtc_driver = {
+ .driver = {
+ .name = "max8925-rtc",
+ .owner = THIS_MODULE,
+ },
+ .probe = max8925_rtc_probe,
+ .remove = __devexit_p(max8925_rtc_remove),
+};
+
+static int __init max8925_rtc_init(void)
+{
+ return platform_driver_register(&max8925_rtc_driver);
+}
+module_init(max8925_rtc_init);
+
+static void __exit max8925_rtc_exit(void)
+{
+ platform_driver_unregister(&max8925_rtc_driver);
+}
+module_exit(max8925_rtc_exit);
+
+MODULE_DESCRIPTION("Maxim MAX8925 RTC driver");
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/rtc/rtc-max8998.c b/drivers/rtc/rtc-max8998.c
new file mode 100644
index 000000000000..f22dee35f330
--- /dev/null
+++ b/drivers/rtc/rtc-max8998.c
@@ -0,0 +1,300 @@
+/*
+ * RTC driver for Maxim MAX8998
+ *
+ * Copyright (C) 2010 Samsung Electronics Co.Ltd
+ * Author: Minkyu Kang <mk7.kang@samsung.com>
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/max8998.h>
+#include <linux/mfd/max8998-private.h>
+
+#define MAX8998_RTC_SEC 0x00
+#define MAX8998_RTC_MIN 0x01
+#define MAX8998_RTC_HOUR 0x02
+#define MAX8998_RTC_WEEKDAY 0x03
+#define MAX8998_RTC_DATE 0x04
+#define MAX8998_RTC_MONTH 0x05
+#define MAX8998_RTC_YEAR1 0x06
+#define MAX8998_RTC_YEAR2 0x07
+#define MAX8998_ALARM0_SEC 0x08
+#define MAX8998_ALARM0_MIN 0x09
+#define MAX8998_ALARM0_HOUR 0x0a
+#define MAX8998_ALARM0_WEEKDAY 0x0b
+#define MAX8998_ALARM0_DATE 0x0c
+#define MAX8998_ALARM0_MONTH 0x0d
+#define MAX8998_ALARM0_YEAR1 0x0e
+#define MAX8998_ALARM0_YEAR2 0x0f
+#define MAX8998_ALARM1_SEC 0x10
+#define MAX8998_ALARM1_MIN 0x11
+#define MAX8998_ALARM1_HOUR 0x12
+#define MAX8998_ALARM1_WEEKDAY 0x13
+#define MAX8998_ALARM1_DATE 0x14
+#define MAX8998_ALARM1_MONTH 0x15
+#define MAX8998_ALARM1_YEAR1 0x16
+#define MAX8998_ALARM1_YEAR2 0x17
+#define MAX8998_ALARM0_CONF 0x18
+#define MAX8998_ALARM1_CONF 0x19
+#define MAX8998_RTC_STATUS 0x1a
+#define MAX8998_WTSR_SMPL_CNTL 0x1b
+#define MAX8998_TEST 0x1f
+
+#define HOUR_12 (1 << 7)
+#define HOUR_PM (1 << 5)
+#define ALARM0_STATUS (1 << 1)
+#define ALARM1_STATUS (1 << 2)
+
+enum {
+ RTC_SEC = 0,
+ RTC_MIN,
+ RTC_HOUR,
+ RTC_WEEKDAY,
+ RTC_DATE,
+ RTC_MONTH,
+ RTC_YEAR1,
+ RTC_YEAR2,
+};
+
+struct max8998_rtc_info {
+ struct device *dev;
+ struct max8998_dev *max8998;
+ struct i2c_client *rtc;
+ struct rtc_device *rtc_dev;
+ int irq;
+};
+
+static void max8998_data_to_tm(u8 *data, struct rtc_time *tm)
+{
+ tm->tm_sec = bcd2bin(data[RTC_SEC]);
+ tm->tm_min = bcd2bin(data[RTC_MIN]);
+ if (data[RTC_HOUR] & HOUR_12) {
+ tm->tm_hour = bcd2bin(data[RTC_HOUR] & 0x1f);
+ if (data[RTC_HOUR] & HOUR_PM)
+ tm->tm_hour += 12;
+ } else
+ tm->tm_hour = bcd2bin(data[RTC_HOUR] & 0x3f);
+
+ tm->tm_wday = data[RTC_WEEKDAY] & 0x07;
+ tm->tm_mday = bcd2bin(data[RTC_DATE]);
+ tm->tm_mon = bcd2bin(data[RTC_MONTH]);
+ tm->tm_year = bcd2bin(data[RTC_YEAR1]) + bcd2bin(data[RTC_YEAR2]) * 100;
+ tm->tm_year -= 1900;
+}
+
+static void max8998_tm_to_data(struct rtc_time *tm, u8 *data)
+{
+ data[RTC_SEC] = bin2bcd(tm->tm_sec);
+ data[RTC_MIN] = bin2bcd(tm->tm_min);
+ data[RTC_HOUR] = bin2bcd(tm->tm_hour);
+ data[RTC_WEEKDAY] = tm->tm_wday;
+ data[RTC_DATE] = bin2bcd(tm->tm_mday);
+ data[RTC_MONTH] = bin2bcd(tm->tm_mon);
+ data[RTC_YEAR1] = bin2bcd(tm->tm_year % 100);
+ data[RTC_YEAR2] = bin2bcd((tm->tm_year + 1900) / 100);
+}
+
+static int max8998_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max8998_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[8];
+ int ret;
+
+ ret = max8998_bulk_read(info->rtc, MAX8998_RTC_SEC, 8, data);
+ if (ret < 0)
+ return ret;
+
+ max8998_data_to_tm(data, tm);
+
+ return rtc_valid_tm(tm);
+}
+
+static int max8998_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max8998_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[8];
+
+ max8998_tm_to_data(tm, data);
+
+ return max8998_bulk_write(info->rtc, MAX8998_RTC_SEC, 8, data);
+}
+
+static int max8998_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max8998_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[8];
+ u8 val;
+ int ret;
+
+ ret = max8998_bulk_read(info->rtc, MAX8998_ALARM0_SEC, 8, data);
+ if (ret < 0)
+ return ret;
+
+ max8998_data_to_tm(data, &alrm->time);
+
+ ret = max8998_read_reg(info->rtc, MAX8998_ALARM0_CONF, &val);
+ if (ret < 0)
+ return ret;
+
+ alrm->enabled = !!val;
+
+ ret = max8998_read_reg(info->rtc, MAX8998_RTC_STATUS, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val & ALARM0_STATUS)
+ alrm->pending = 1;
+ else
+ alrm->pending = 0;
+
+ return 0;
+}
+
+static int max8998_rtc_stop_alarm(struct max8998_rtc_info *info)
+{
+ return max8998_write_reg(info->rtc, MAX8998_ALARM0_CONF, 0);
+}
+
+static int max8998_rtc_start_alarm(struct max8998_rtc_info *info)
+{
+ return max8998_write_reg(info->rtc, MAX8998_ALARM0_CONF, 0x77);
+}
+
+static int max8998_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max8998_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[8];
+ int ret;
+
+ max8998_tm_to_data(&alrm->time, data);
+
+ ret = max8998_rtc_stop_alarm(info);
+ if (ret < 0)
+ return ret;
+
+ ret = max8998_bulk_write(info->rtc, MAX8998_ALARM0_SEC, 8, data);
+ if (ret < 0)
+ return ret;
+
+ if (alrm->enabled)
+ return max8998_rtc_start_alarm(info);
+
+ return 0;
+}
+
+static int max8998_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct max8998_rtc_info *info = dev_get_drvdata(dev);
+
+ if (enabled)
+ return max8998_rtc_start_alarm(info);
+ else
+ return max8998_rtc_stop_alarm(info);
+}
+
+static irqreturn_t max8998_rtc_alarm_irq(int irq, void *data)
+{
+ struct max8998_rtc_info *info = data;
+
+ rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops max8998_rtc_ops = {
+ .read_time = max8998_rtc_read_time,
+ .set_time = max8998_rtc_set_time,
+ .read_alarm = max8998_rtc_read_alarm,
+ .set_alarm = max8998_rtc_set_alarm,
+ .alarm_irq_enable = max8998_rtc_alarm_irq_enable,
+};
+
+static int __devinit max8998_rtc_probe(struct platform_device *pdev)
+{
+ struct max8998_dev *max8998 = dev_get_drvdata(pdev->dev.parent);
+ struct max8998_rtc_info *info;
+ int ret;
+
+ info = kzalloc(sizeof(struct max8998_rtc_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->dev = &pdev->dev;
+ info->max8998 = max8998;
+ info->rtc = max8998->rtc;
+ info->irq = max8998->irq_base + MAX8998_IRQ_ALARM0;
+
+ info->rtc_dev = rtc_device_register("max8998-rtc", &pdev->dev,
+ &max8998_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(info->rtc_dev)) {
+ ret = PTR_ERR(info->rtc_dev);
+ dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
+ goto out_rtc;
+ }
+
+ platform_set_drvdata(pdev, info);
+
+ ret = request_threaded_irq(info->irq, NULL, max8998_rtc_alarm_irq, 0,
+ "rtc-alarm0", info);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
+ info->irq, ret);
+
+ return 0;
+
+out_rtc:
+ kfree(info);
+ return ret;
+}
+
+static int __devexit max8998_rtc_remove(struct platform_device *pdev)
+{
+ struct max8998_rtc_info *info = platform_get_drvdata(pdev);
+
+ if (info) {
+ free_irq(info->irq, info);
+ rtc_device_unregister(info->rtc_dev);
+ kfree(info);
+ }
+
+ return 0;
+}
+
+static struct platform_driver max8998_rtc_driver = {
+ .driver = {
+ .name = "max8998-rtc",
+ .owner = THIS_MODULE,
+ },
+ .probe = max8998_rtc_probe,
+ .remove = __devexit_p(max8998_rtc_remove),
+};
+
+static int __init max8998_rtc_init(void)
+{
+ return platform_driver_register(&max8998_rtc_driver);
+}
+module_init(max8998_rtc_init);
+
+static void __exit max8998_rtc_exit(void)
+{
+ platform_driver_unregister(&max8998_rtc_driver);
+}
+module_exit(max8998_rtc_exit);
+
+MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>");
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_DESCRIPTION("Maxim MAX8998 RTC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-mc13783.c b/drivers/rtc/rtc-mc13783.c
deleted file mode 100644
index 850f983c039c..000000000000
--- a/drivers/rtc/rtc-mc13783.c
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Real Time Clock driver for Freescale MC13783 PMIC
- *
- * (C) 2009 Sascha Hauer, Pengutronix
- * (C) 2009 Uwe Kleine-Koenig, Pengutronix
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/mfd/mc13783.h>
-#include <linux/platform_device.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/rtc.h>
-
-#define DRIVER_NAME "mc13783-rtc"
-
-#define MC13783_RTCTOD 20
-#define MC13783_RTCTODA 21
-#define MC13783_RTCDAY 22
-#define MC13783_RTCDAYA 23
-
-struct mc13783_rtc {
- struct rtc_device *rtc;
- struct mc13783 *mc13783;
- int valid;
-};
-
-static int mc13783_rtc_read_time(struct device *dev, struct rtc_time *tm)
-{
- struct mc13783_rtc *priv = dev_get_drvdata(dev);
- unsigned int seconds, days1, days2;
- unsigned long s1970;
- int ret;
-
- mc13783_lock(priv->mc13783);
-
- if (!priv->valid) {
- ret = -ENODATA;
- goto out;
- }
-
- ret = mc13783_reg_read(priv->mc13783, MC13783_RTCDAY, &days1);
- if (unlikely(ret))
- goto out;
-
- ret = mc13783_reg_read(priv->mc13783, MC13783_RTCTOD, &seconds);
- if (unlikely(ret))
- goto out;
-
- ret = mc13783_reg_read(priv->mc13783, MC13783_RTCDAY, &days2);
-out:
- mc13783_unlock(priv->mc13783);
-
- if (ret)
- return ret;
-
- if (days2 == days1 + 1) {
- if (seconds >= 86400 / 2)
- days2 = days1;
- else
- days1 = days2;
- }
-
- if (days1 != days2)
- return -EIO;
-
- s1970 = days1 * 86400 + seconds;
-
- rtc_time_to_tm(s1970, tm);
-
- return rtc_valid_tm(tm);
-}
-
-static int mc13783_rtc_set_mmss(struct device *dev, unsigned long secs)
-{
- struct mc13783_rtc *priv = dev_get_drvdata(dev);
- unsigned int seconds, days;
- int ret;
-
- seconds = secs % 86400;
- days = secs / 86400;
-
- mc13783_lock(priv->mc13783);
-
- /*
- * first write seconds=0 to prevent a day switch between writing days
- * and seconds below
- */
- ret = mc13783_reg_write(priv->mc13783, MC13783_RTCTOD, 0);
- if (unlikely(ret))
- goto out;
-
- ret = mc13783_reg_write(priv->mc13783, MC13783_RTCDAY, days);
- if (unlikely(ret))
- goto out;
-
- ret = mc13783_reg_write(priv->mc13783, MC13783_RTCTOD, seconds);
- if (unlikely(ret))
- goto out;
-
- ret = mc13783_ackirq(priv->mc13783, MC13783_IRQ_RTCRST);
- if (unlikely(ret))
- goto out;
-
- ret = mc13783_unmask(priv->mc13783, MC13783_IRQ_RTCRST);
-out:
- priv->valid = !ret;
-
- mc13783_unlock(priv->mc13783);
-
- return ret;
-}
-
-static irqreturn_t mc13783_rtc_update_handler(int irq, void *dev)
-{
- struct mc13783_rtc *priv = dev;
- struct mc13783 *mc13783 = priv->mc13783;
-
- dev_dbg(&priv->rtc->dev, "1HZ\n");
-
- rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_UF);
-
- mc13783_ackirq(mc13783, irq);
-
- return IRQ_HANDLED;
-}
-
-static int mc13783_rtc_update_irq_enable(struct device *dev,
- unsigned int enabled)
-{
- struct mc13783_rtc *priv = dev_get_drvdata(dev);
- int ret = -ENODATA;
-
- mc13783_lock(priv->mc13783);
- if (!priv->valid)
- goto out;
-
- ret = (enabled ? mc13783_unmask : mc13783_mask)(priv->mc13783,
- MC13783_IRQ_1HZ);
-out:
- mc13783_unlock(priv->mc13783);
-
- return ret;
-}
-
-static const struct rtc_class_ops mc13783_rtc_ops = {
- .read_time = mc13783_rtc_read_time,
- .set_mmss = mc13783_rtc_set_mmss,
- .update_irq_enable = mc13783_rtc_update_irq_enable,
-};
-
-static irqreturn_t mc13783_rtc_reset_handler(int irq, void *dev)
-{
- struct mc13783_rtc *priv = dev;
- struct mc13783 *mc13783 = priv->mc13783;
-
- dev_dbg(&priv->rtc->dev, "RTCRST\n");
- priv->valid = 0;
-
- mc13783_mask(mc13783, irq);
-
- return IRQ_HANDLED;
-}
-
-static int __devinit mc13783_rtc_probe(struct platform_device *pdev)
-{
- int ret;
- struct mc13783_rtc *priv;
-
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- priv->mc13783 = dev_get_drvdata(pdev->dev.parent);
- platform_set_drvdata(pdev, priv);
-
- priv->valid = 1;
-
- mc13783_lock(priv->mc13783);
-
- ret = mc13783_irq_request(priv->mc13783, MC13783_IRQ_RTCRST,
- mc13783_rtc_reset_handler, DRIVER_NAME, priv);
- if (ret)
- goto err_reset_irq_request;
-
- ret = mc13783_irq_request_nounmask(priv->mc13783, MC13783_IRQ_1HZ,
- mc13783_rtc_update_handler, DRIVER_NAME, priv);
- if (ret)
- goto err_update_irq_request;
-
- mc13783_unlock(priv->mc13783);
-
- priv->rtc = rtc_device_register(pdev->name,
- &pdev->dev, &mc13783_rtc_ops, THIS_MODULE);
-
- if (IS_ERR(priv->rtc)) {
- ret = PTR_ERR(priv->rtc);
-
- mc13783_lock(priv->mc13783);
-
- mc13783_irq_free(priv->mc13783, MC13783_IRQ_1HZ, priv);
-err_update_irq_request:
-
- mc13783_irq_free(priv->mc13783, MC13783_IRQ_RTCRST, priv);
-err_reset_irq_request:
-
- mc13783_unlock(priv->mc13783);
-
- platform_set_drvdata(pdev, NULL);
- kfree(priv);
- }
-
- return ret;
-}
-
-static int __devexit mc13783_rtc_remove(struct platform_device *pdev)
-{
- struct mc13783_rtc *priv = platform_get_drvdata(pdev);
-
- rtc_device_unregister(priv->rtc);
-
- mc13783_lock(priv->mc13783);
-
- mc13783_irq_free(priv->mc13783, MC13783_IRQ_1HZ, priv);
- mc13783_irq_free(priv->mc13783, MC13783_IRQ_RTCRST, priv);
-
- mc13783_unlock(priv->mc13783);
-
- platform_set_drvdata(pdev, NULL);
-
- kfree(priv);
-
- return 0;
-}
-
-static struct platform_driver mc13783_rtc_driver = {
- .remove = __devexit_p(mc13783_rtc_remove),
- .driver = {
- .name = DRIVER_NAME,
- .owner = THIS_MODULE,
- },
-};
-
-static int __init mc13783_rtc_init(void)
-{
- return platform_driver_probe(&mc13783_rtc_driver, &mc13783_rtc_probe);
-}
-module_init(mc13783_rtc_init);
-
-static void __exit mc13783_rtc_exit(void)
-{
- platform_driver_unregister(&mc13783_rtc_driver);
-}
-module_exit(mc13783_rtc_exit);
-
-MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
-MODULE_DESCRIPTION("RTC driver for Freescale MC13783 PMIC");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/rtc/rtc-mc13xxx.c b/drivers/rtc/rtc-mc13xxx.c
new file mode 100644
index 000000000000..5314b153bfba
--- /dev/null
+++ b/drivers/rtc/rtc-mc13xxx.c
@@ -0,0 +1,437 @@
+/*
+ * Real Time Clock driver for Freescale MC13XXX PMIC
+ *
+ * (C) 2009 Sascha Hauer, Pengutronix
+ * (C) 2009 Uwe Kleine-Koenig, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/mfd/mc13xxx.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/rtc.h>
+
+#define DRIVER_NAME "mc13xxx-rtc"
+
+#define MC13XXX_RTCTOD 20
+#define MC13XXX_RTCTODA 21
+#define MC13XXX_RTCDAY 22
+#define MC13XXX_RTCDAYA 23
+
+struct mc13xxx_rtc {
+ struct rtc_device *rtc;
+ struct mc13xxx *mc13xxx;
+ int valid;
+};
+
+static int mc13xxx_rtc_irq_enable_unlocked(struct device *dev,
+ unsigned int enabled, int irq)
+{
+ struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
+ int (*func)(struct mc13xxx *mc13xxx, int irq);
+
+ if (!priv->valid)
+ return -ENODATA;
+
+ func = enabled ? mc13xxx_irq_unmask : mc13xxx_irq_mask;
+ return func(priv->mc13xxx, irq);
+}
+
+static int mc13xxx_rtc_irq_enable(struct device *dev,
+ unsigned int enabled, int irq)
+{
+ struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
+ int ret;
+
+ mc13xxx_lock(priv->mc13xxx);
+
+ ret = mc13xxx_rtc_irq_enable_unlocked(dev, enabled, irq);
+
+ mc13xxx_unlock(priv->mc13xxx);
+
+ return ret;
+}
+
+static int mc13xxx_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
+ unsigned int seconds, days1, days2;
+ unsigned long s1970;
+ int ret;
+
+ mc13xxx_lock(priv->mc13xxx);
+
+ if (!priv->valid) {
+ ret = -ENODATA;
+ goto out;
+ }
+
+ ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCDAY, &days1);
+ if (unlikely(ret))
+ goto out;
+
+ ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCTOD, &seconds);
+ if (unlikely(ret))
+ goto out;
+
+ ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCDAY, &days2);
+out:
+ mc13xxx_unlock(priv->mc13xxx);
+
+ if (ret)
+ return ret;
+
+ if (days2 == days1 + 1) {
+ if (seconds >= 86400 / 2)
+ days2 = days1;
+ else
+ days1 = days2;
+ }
+
+ if (days1 != days2)
+ return -EIO;
+
+ s1970 = days1 * 86400 + seconds;
+
+ rtc_time_to_tm(s1970, tm);
+
+ return rtc_valid_tm(tm);
+}
+
+static int mc13xxx_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+ struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
+ unsigned int seconds, days;
+ unsigned int alarmseconds;
+ int ret;
+
+ seconds = secs % 86400;
+ days = secs / 86400;
+
+ mc13xxx_lock(priv->mc13xxx);
+
+ /*
+ * temporarily invalidate alarm to prevent triggering it when the day is
+ * already updated while the time isn't yet.
+ */
+ ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCTODA, &alarmseconds);
+ if (unlikely(ret))
+ goto out;
+
+ if (alarmseconds < 86400) {
+ ret = mc13xxx_reg_write(priv->mc13xxx,
+ MC13XXX_RTCTODA, 0x1ffff);
+ if (unlikely(ret))
+ goto out;
+ }
+
+ /*
+ * write seconds=0 to prevent a day switch between writing days
+ * and seconds below
+ */
+ ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCTOD, 0);
+ if (unlikely(ret))
+ goto out;
+
+ ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCDAY, days);
+ if (unlikely(ret))
+ goto out;
+
+ ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCTOD, seconds);
+ if (unlikely(ret))
+ goto out;
+
+ /* restore alarm */
+ if (alarmseconds < 86400) {
+ ret = mc13xxx_reg_write(priv->mc13xxx,
+ MC13XXX_RTCTODA, alarmseconds);
+ if (unlikely(ret))
+ goto out;
+ }
+
+ ret = mc13xxx_irq_ack(priv->mc13xxx, MC13XXX_IRQ_RTCRST);
+ if (unlikely(ret))
+ goto out;
+
+ ret = mc13xxx_irq_unmask(priv->mc13xxx, MC13XXX_IRQ_RTCRST);
+out:
+ priv->valid = !ret;
+
+ mc13xxx_unlock(priv->mc13xxx);
+
+ return ret;
+}
+
+static int mc13xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
+ unsigned seconds, days;
+ unsigned long s1970;
+ int enabled, pending;
+ int ret;
+
+ mc13xxx_lock(priv->mc13xxx);
+
+ ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCTODA, &seconds);
+ if (unlikely(ret))
+ goto out;
+ if (seconds >= 86400) {
+ ret = -ENODATA;
+ goto out;
+ }
+
+ ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCDAY, &days);
+ if (unlikely(ret))
+ goto out;
+
+ ret = mc13xxx_irq_status(priv->mc13xxx, MC13XXX_IRQ_TODA,
+ &enabled, &pending);
+
+out:
+ mc13xxx_unlock(priv->mc13xxx);
+
+ if (ret)
+ return ret;
+
+ alarm->enabled = enabled;
+ alarm->pending = pending;
+
+ s1970 = days * 86400 + seconds;
+
+ rtc_time_to_tm(s1970, &alarm->time);
+ dev_dbg(dev, "%s: %lu\n", __func__, s1970);
+
+ return 0;
+}
+
+static int mc13xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
+ unsigned long s1970;
+ unsigned seconds, days;
+ int ret;
+
+ mc13xxx_lock(priv->mc13xxx);
+
+ /* disable alarm to prevent false triggering */
+ ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCTODA, 0x1ffff);
+ if (unlikely(ret))
+ goto out;
+
+ ret = mc13xxx_irq_ack(priv->mc13xxx, MC13XXX_IRQ_TODA);
+ if (unlikely(ret))
+ goto out;
+
+ ret = rtc_tm_to_time(&alarm->time, &s1970);
+ if (unlikely(ret))
+ goto out;
+
+ dev_dbg(dev, "%s: o%2.s %lu\n", __func__, alarm->enabled ? "n" : "ff",
+ s1970);
+
+ ret = mc13xxx_rtc_irq_enable_unlocked(dev, alarm->enabled,
+ MC13XXX_IRQ_TODA);
+ if (unlikely(ret))
+ goto out;
+
+ seconds = s1970 % 86400;
+ days = s1970 / 86400;
+
+ ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCDAYA, days);
+ if (unlikely(ret))
+ goto out;
+
+ ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCTODA, seconds);
+
+out:
+ mc13xxx_unlock(priv->mc13xxx);
+
+ return ret;
+}
+
+static irqreturn_t mc13xxx_rtc_alarm_handler(int irq, void *dev)
+{
+ struct mc13xxx_rtc *priv = dev;
+ struct mc13xxx *mc13xxx = priv->mc13xxx;
+
+ dev_dbg(&priv->rtc->dev, "Alarm\n");
+
+ rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_AF);
+
+ mc13xxx_irq_ack(mc13xxx, irq);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mc13xxx_rtc_update_handler(int irq, void *dev)
+{
+ struct mc13xxx_rtc *priv = dev;
+ struct mc13xxx *mc13xxx = priv->mc13xxx;
+
+ dev_dbg(&priv->rtc->dev, "1HZ\n");
+
+ rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_UF);
+
+ mc13xxx_irq_ack(mc13xxx, irq);
+
+ return IRQ_HANDLED;
+}
+
+static int mc13xxx_rtc_update_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ return mc13xxx_rtc_irq_enable(dev, enabled, MC13XXX_IRQ_1HZ);
+}
+
+static int mc13xxx_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ return mc13xxx_rtc_irq_enable(dev, enabled, MC13XXX_IRQ_TODA);
+}
+
+static const struct rtc_class_ops mc13xxx_rtc_ops = {
+ .read_time = mc13xxx_rtc_read_time,
+ .set_mmss = mc13xxx_rtc_set_mmss,
+ .read_alarm = mc13xxx_rtc_read_alarm,
+ .set_alarm = mc13xxx_rtc_set_alarm,
+ .alarm_irq_enable = mc13xxx_rtc_alarm_irq_enable,
+ .update_irq_enable = mc13xxx_rtc_update_irq_enable,
+};
+
+static irqreturn_t mc13xxx_rtc_reset_handler(int irq, void *dev)
+{
+ struct mc13xxx_rtc *priv = dev;
+ struct mc13xxx *mc13xxx = priv->mc13xxx;
+
+ dev_dbg(&priv->rtc->dev, "RTCRST\n");
+ priv->valid = 0;
+
+ mc13xxx_irq_mask(mc13xxx, irq);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit mc13xxx_rtc_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct mc13xxx_rtc *priv;
+ struct mc13xxx *mc13xxx;
+ int rtcrst_pending;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ mc13xxx = dev_get_drvdata(pdev->dev.parent);
+ priv->mc13xxx = mc13xxx;
+
+ platform_set_drvdata(pdev, priv);
+
+ mc13xxx_lock(mc13xxx);
+
+ ret = mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_RTCRST,
+ mc13xxx_rtc_reset_handler, DRIVER_NAME, priv);
+ if (ret)
+ goto err_reset_irq_request;
+
+ ret = mc13xxx_irq_status(mc13xxx, MC13XXX_IRQ_RTCRST,
+ NULL, &rtcrst_pending);
+ if (ret)
+ goto err_reset_irq_status;
+
+ priv->valid = !rtcrst_pending;
+
+ ret = mc13xxx_irq_request_nounmask(mc13xxx, MC13XXX_IRQ_1HZ,
+ mc13xxx_rtc_update_handler, DRIVER_NAME, priv);
+ if (ret)
+ goto err_update_irq_request;
+
+ ret = mc13xxx_irq_request_nounmask(mc13xxx, MC13XXX_IRQ_TODA,
+ mc13xxx_rtc_alarm_handler, DRIVER_NAME, priv);
+ if (ret)
+ goto err_alarm_irq_request;
+
+ priv->rtc = rtc_device_register(pdev->name,
+ &pdev->dev, &mc13xxx_rtc_ops, THIS_MODULE);
+ if (IS_ERR(priv->rtc)) {
+ ret = PTR_ERR(priv->rtc);
+
+ mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_TODA, priv);
+err_alarm_irq_request:
+
+ mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_1HZ, priv);
+err_update_irq_request:
+
+err_reset_irq_status:
+
+ mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_RTCRST, priv);
+err_reset_irq_request:
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(priv);
+ }
+
+ mc13xxx_unlock(mc13xxx);
+
+ return ret;
+}
+
+static int __devexit mc13xxx_rtc_remove(struct platform_device *pdev)
+{
+ struct mc13xxx_rtc *priv = platform_get_drvdata(pdev);
+
+ mc13xxx_lock(priv->mc13xxx);
+
+ rtc_device_unregister(priv->rtc);
+
+ mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_TODA, priv);
+ mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_1HZ, priv);
+ mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_RTCRST, priv);
+
+ mc13xxx_unlock(priv->mc13xxx);
+
+ platform_set_drvdata(pdev, NULL);
+
+ kfree(priv);
+
+ return 0;
+}
+
+const struct platform_device_id mc13xxx_rtc_idtable[] = {
+ {
+ .name = "mc13783-rtc",
+ }, {
+ .name = "mc13892-rtc",
+ },
+};
+
+static struct platform_driver mc13xxx_rtc_driver = {
+ .id_table = mc13xxx_rtc_idtable,
+ .remove = __devexit_p(mc13xxx_rtc_remove),
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init mc13xxx_rtc_init(void)
+{
+ return platform_driver_probe(&mc13xxx_rtc_driver, &mc13xxx_rtc_probe);
+}
+module_init(mc13xxx_rtc_init);
+
+static void __exit mc13xxx_rtc_exit(void)
+{
+ platform_driver_unregister(&mc13xxx_rtc_driver);
+}
+module_exit(mc13xxx_rtc_exit);
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_DESCRIPTION("RTC driver for Freescale MC13XXX PMIC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c
new file mode 100644
index 000000000000..dfcdf0901d21
--- /dev/null
+++ b/drivers/rtc/rtc-mpc5121.c
@@ -0,0 +1,390 @@
+/*
+ * Real-time clock driver for MPC5121
+ *
+ * Copyright 2007, Domen Puncer <domen.puncer@telargo.com>
+ * Copyright 2008, Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+struct mpc5121_rtc_regs {
+ u8 set_time; /* RTC + 0x00 */
+ u8 hour_set; /* RTC + 0x01 */
+ u8 minute_set; /* RTC + 0x02 */
+ u8 second_set; /* RTC + 0x03 */
+
+ u8 set_date; /* RTC + 0x04 */
+ u8 month_set; /* RTC + 0x05 */
+ u8 weekday_set; /* RTC + 0x06 */
+ u8 date_set; /* RTC + 0x07 */
+
+ u8 write_sw; /* RTC + 0x08 */
+ u8 sw_set; /* RTC + 0x09 */
+ u16 year_set; /* RTC + 0x0a */
+
+ u8 alm_enable; /* RTC + 0x0c */
+ u8 alm_hour_set; /* RTC + 0x0d */
+ u8 alm_min_set; /* RTC + 0x0e */
+ u8 int_enable; /* RTC + 0x0f */
+
+ u8 reserved1;
+ u8 hour; /* RTC + 0x11 */
+ u8 minute; /* RTC + 0x12 */
+ u8 second; /* RTC + 0x13 */
+
+ u8 month; /* RTC + 0x14 */
+ u8 wday_mday; /* RTC + 0x15 */
+ u16 year; /* RTC + 0x16 */
+
+ u8 int_alm; /* RTC + 0x18 */
+ u8 int_sw; /* RTC + 0x19 */
+ u8 alm_status; /* RTC + 0x1a */
+ u8 sw_minute; /* RTC + 0x1b */
+
+ u8 bus_error_1; /* RTC + 0x1c */
+ u8 int_day; /* RTC + 0x1d */
+ u8 int_min; /* RTC + 0x1e */
+ u8 int_sec; /* RTC + 0x1f */
+
+ /*
+ * target_time:
+ * intended to be used for hibernation but hibernation
+ * does not work on silicon rev 1.5 so use it for non-volatile
+ * storage of offset between the actual_time register and linux
+ * time
+ */
+ u32 target_time; /* RTC + 0x20 */
+ /*
+ * actual_time:
+ * readonly time since VBAT_RTC was last connected
+ */
+ u32 actual_time; /* RTC + 0x24 */
+ u32 keep_alive; /* RTC + 0x28 */
+};
+
+struct mpc5121_rtc_data {
+ unsigned irq;
+ unsigned irq_periodic;
+ struct mpc5121_rtc_regs __iomem *regs;
+ struct rtc_device *rtc;
+ struct rtc_wkalrm wkalarm;
+};
+
+/*
+ * Update second/minute/hour registers.
+ *
+ * This is just so alarm will work.
+ */
+static void mpc5121_rtc_update_smh(struct mpc5121_rtc_regs __iomem *regs,
+ struct rtc_time *tm)
+{
+ out_8(&regs->second_set, tm->tm_sec);
+ out_8(&regs->minute_set, tm->tm_min);
+ out_8(&regs->hour_set, tm->tm_hour);
+
+ /* set time sequence */
+ out_8(&regs->set_time, 0x1);
+ out_8(&regs->set_time, 0x3);
+ out_8(&regs->set_time, 0x1);
+ out_8(&regs->set_time, 0x0);
+}
+
+static int mpc5121_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+ unsigned long now;
+
+ /*
+ * linux time is actual_time plus the offset saved in target_time
+ */
+ now = in_be32(&regs->actual_time) + in_be32(&regs->target_time);
+
+ rtc_time_to_tm(now, tm);
+
+ /*
+ * update second minute hour registers
+ * so alarms will work
+ */
+ mpc5121_rtc_update_smh(regs, tm);
+
+ return rtc_valid_tm(tm);
+}
+
+static int mpc5121_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+ int ret;
+ unsigned long now;
+
+ /*
+ * The actual_time register is read only so we write the offset
+ * between it and linux time to the target_time register.
+ */
+ ret = rtc_tm_to_time(tm, &now);
+ if (ret == 0)
+ out_be32(&regs->target_time, now - in_be32(&regs->actual_time));
+
+ /*
+ * update second minute hour registers
+ * so alarms will work
+ */
+ mpc5121_rtc_update_smh(regs, tm);
+
+ return 0;
+}
+
+static int mpc5121_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+
+ *alarm = rtc->wkalarm;
+
+ alarm->pending = in_8(&regs->alm_status);
+
+ return 0;
+}
+
+static int mpc5121_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+
+ /*
+ * the alarm has no seconds so deal with it
+ */
+ if (alarm->time.tm_sec) {
+ alarm->time.tm_sec = 0;
+ alarm->time.tm_min++;
+ if (alarm->time.tm_min >= 60) {
+ alarm->time.tm_min = 0;
+ alarm->time.tm_hour++;
+ if (alarm->time.tm_hour >= 24)
+ alarm->time.tm_hour = 0;
+ }
+ }
+
+ alarm->time.tm_mday = -1;
+ alarm->time.tm_mon = -1;
+ alarm->time.tm_year = -1;
+
+ out_8(&regs->alm_min_set, alarm->time.tm_min);
+ out_8(&regs->alm_hour_set, alarm->time.tm_hour);
+
+ out_8(&regs->alm_enable, alarm->enabled);
+
+ rtc->wkalarm = *alarm;
+ return 0;
+}
+
+static irqreturn_t mpc5121_rtc_handler(int irq, void *dev)
+{
+ struct mpc5121_rtc_data *rtc = dev_get_drvdata((struct device *)dev);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+
+ if (in_8(&regs->int_alm)) {
+ /* acknowledge and clear status */
+ out_8(&regs->int_alm, 1);
+ out_8(&regs->alm_status, 1);
+
+ rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static irqreturn_t mpc5121_rtc_handler_upd(int irq, void *dev)
+{
+ struct mpc5121_rtc_data *rtc = dev_get_drvdata((struct device *)dev);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+
+ if (in_8(&regs->int_sec) && (in_8(&regs->int_enable) & 0x1)) {
+ /* acknowledge */
+ out_8(&regs->int_sec, 1);
+
+ rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_UF);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int mpc5121_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+ int val;
+
+ if (enabled)
+ val = 1;
+ else
+ val = 0;
+
+ out_8(&regs->alm_enable, val);
+ rtc->wkalarm.enabled = val;
+
+ return 0;
+}
+
+static int mpc5121_rtc_update_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+ int val;
+
+ val = in_8(&regs->int_enable);
+
+ if (enabled)
+ val = (val & ~0x8) | 0x1;
+ else
+ val &= ~0x1;
+
+ out_8(&regs->int_enable, val);
+
+ return 0;
+}
+
+static const struct rtc_class_ops mpc5121_rtc_ops = {
+ .read_time = mpc5121_rtc_read_time,
+ .set_time = mpc5121_rtc_set_time,
+ .read_alarm = mpc5121_rtc_read_alarm,
+ .set_alarm = mpc5121_rtc_set_alarm,
+ .alarm_irq_enable = mpc5121_rtc_alarm_irq_enable,
+ .update_irq_enable = mpc5121_rtc_update_irq_enable,
+};
+
+static int __devinit mpc5121_rtc_probe(struct platform_device *op,
+ const struct of_device_id *match)
+{
+ struct mpc5121_rtc_data *rtc;
+ int err = 0;
+ u32 ka;
+
+ rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ rtc->regs = of_iomap(op->dev.of_node, 0);
+ if (!rtc->regs) {
+ dev_err(&op->dev, "%s: couldn't map io space\n", __func__);
+ err = -ENOSYS;
+ goto out_free;
+ }
+
+ device_init_wakeup(&op->dev, 1);
+
+ dev_set_drvdata(&op->dev, rtc);
+
+ rtc->irq = irq_of_parse_and_map(op->dev.of_node, 1);
+ err = request_irq(rtc->irq, mpc5121_rtc_handler, IRQF_DISABLED,
+ "mpc5121-rtc", &op->dev);
+ if (err) {
+ dev_err(&op->dev, "%s: could not request irq: %i\n",
+ __func__, rtc->irq);
+ goto out_dispose;
+ }
+
+ rtc->irq_periodic = irq_of_parse_and_map(op->dev.of_node, 0);
+ err = request_irq(rtc->irq_periodic, mpc5121_rtc_handler_upd,
+ IRQF_DISABLED, "mpc5121-rtc_upd", &op->dev);
+ if (err) {
+ dev_err(&op->dev, "%s: could not request irq: %i\n",
+ __func__, rtc->irq_periodic);
+ goto out_dispose2;
+ }
+
+ ka = in_be32(&rtc->regs->keep_alive);
+ if (ka & 0x02) {
+ dev_warn(&op->dev,
+ "mpc5121-rtc: Battery or oscillator failure!\n");
+ out_be32(&rtc->regs->keep_alive, ka);
+ }
+
+ rtc->rtc = rtc_device_register("mpc5121-rtc", &op->dev,
+ &mpc5121_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc->rtc)) {
+ err = PTR_ERR(rtc->rtc);
+ goto out_free_irq;
+ }
+
+ return 0;
+
+out_free_irq:
+ free_irq(rtc->irq_periodic, &op->dev);
+out_dispose2:
+ irq_dispose_mapping(rtc->irq_periodic);
+ free_irq(rtc->irq, &op->dev);
+out_dispose:
+ irq_dispose_mapping(rtc->irq);
+ iounmap(rtc->regs);
+out_free:
+ kfree(rtc);
+
+ return err;
+}
+
+static int __devexit mpc5121_rtc_remove(struct platform_device *op)
+{
+ struct mpc5121_rtc_data *rtc = dev_get_drvdata(&op->dev);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+
+ /* disable interrupt, so there are no nasty surprises */
+ out_8(&regs->alm_enable, 0);
+ out_8(&regs->int_enable, in_8(&regs->int_enable) & ~0x1);
+
+ rtc_device_unregister(rtc->rtc);
+ iounmap(rtc->regs);
+ free_irq(rtc->irq, &op->dev);
+ free_irq(rtc->irq_periodic, &op->dev);
+ irq_dispose_mapping(rtc->irq);
+ irq_dispose_mapping(rtc->irq_periodic);
+ dev_set_drvdata(&op->dev, NULL);
+ kfree(rtc);
+
+ return 0;
+}
+
+static struct of_device_id mpc5121_rtc_match[] __devinitdata = {
+ { .compatible = "fsl,mpc5121-rtc", },
+ {},
+};
+
+static struct of_platform_driver mpc5121_rtc_driver = {
+ .driver = {
+ .name = "mpc5121-rtc",
+ .owner = THIS_MODULE,
+ .of_match_table = mpc5121_rtc_match,
+ },
+ .probe = mpc5121_rtc_probe,
+ .remove = __devexit_p(mpc5121_rtc_remove),
+};
+
+static int __init mpc5121_rtc_init(void)
+{
+ return of_register_platform_driver(&mpc5121_rtc_driver);
+}
+module_init(mpc5121_rtc_init);
+
+static void __exit mpc5121_rtc_exit(void)
+{
+ of_unregister_platform_driver(&mpc5121_rtc_driver);
+}
+module_exit(mpc5121_rtc_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("John Rigby <jcrigby@gmail.com>");
diff --git a/drivers/rtc/rtc-msm6242.c b/drivers/rtc/rtc-msm6242.c
index 5f5968a48925..b2fff0ca49f8 100644
--- a/drivers/rtc/rtc-msm6242.c
+++ b/drivers/rtc/rtc-msm6242.c
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
+#include <linux/slab.h>
enum {
diff --git a/drivers/rtc/rtc-mv.c b/drivers/rtc/rtc-mv.c
index dc052ce6e63a..bcca47298554 100644
--- a/drivers/rtc/rtc-mv.c
+++ b/drivers/rtc/rtc-mv.c
@@ -13,6 +13,7 @@
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
+#include <linux/gfp.h>
#define RTC_TIME_REG_OFFS 0
diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c
index 6bd5072d4eb7..0b06c1e03fd5 100644
--- a/drivers/rtc/rtc-mxc.c
+++ b/drivers/rtc/rtc-mxc.c
@@ -12,6 +12,7 @@
#include <linux/io.h>
#include <linux/rtc.h>
#include <linux/module.h>
+#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
@@ -82,12 +83,6 @@ struct rtc_plat_data {
void __iomem *ioaddr;
int irq;
struct clk *clk;
- unsigned int irqen;
- int alrm_sec;
- int alrm_min;
- int alrm_hour;
- int alrm_mday;
- struct timespec mxc_rtc_delta;
struct rtc_time g_rtc_alarm;
};
@@ -378,29 +373,37 @@ static struct rtc_class_ops mxc_rtc_ops = {
static int __init mxc_rtc_probe(struct platform_device *pdev)
{
- struct clk *clk;
struct resource *res;
struct rtc_device *rtc;
struct rtc_plat_data *pdata = NULL;
u32 reg;
- int ret, rate;
+ unsigned long rate;
+ int ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
- pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
- pdata->ioaddr = ioremap(res->start, resource_size(res));
+ if (!devm_request_mem_region(&pdev->dev, res->start,
+ resource_size(res), pdev->name))
+ return -EBUSY;
- clk = clk_get(&pdev->dev, "ckil");
- if (IS_ERR(clk))
- return PTR_ERR(clk);
+ pdata->ioaddr = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
- rate = clk_get_rate(clk);
- clk_put(clk);
+ pdata->clk = clk_get(&pdev->dev, "rtc");
+ if (IS_ERR(pdata->clk)) {
+ dev_err(&pdev->dev, "unable to get clock!\n");
+ ret = PTR_ERR(pdata->clk);
+ goto exit_free_pdata;
+ }
+
+ clk_enable(pdata->clk);
+ rate = clk_get_rate(pdata->clk);
if (rate == 32768)
reg = RTC_INPUT_CLK_32768HZ;
@@ -409,10 +412,9 @@ static int __init mxc_rtc_probe(struct platform_device *pdev)
else if (rate == 38400)
reg = RTC_INPUT_CLK_38400HZ;
else {
- dev_err(&pdev->dev, "rtc clock is not valid (%lu)\n",
- clk_get_rate(clk));
+ dev_err(&pdev->dev, "rtc clock is not valid (%lu)\n", rate);
ret = -EINVAL;
- goto exit_free_pdata;
+ goto exit_put_clk;
}
reg |= RTC_ENABLE_BIT;
@@ -420,18 +422,9 @@ static int __init mxc_rtc_probe(struct platform_device *pdev)
if (((readw(pdata->ioaddr + RTC_RTCCTL)) & RTC_ENABLE_BIT) == 0) {
dev_err(&pdev->dev, "hardware module can't be enabled!\n");
ret = -EIO;
- goto exit_free_pdata;
- }
-
- pdata->clk = clk_get(&pdev->dev, "rtc");
- if (IS_ERR(pdata->clk)) {
- dev_err(&pdev->dev, "unable to get clock!\n");
- ret = PTR_ERR(pdata->clk);
- goto exit_free_pdata;
+ goto exit_put_clk;
}
- clk_enable(pdata->clk);
-
rtc = rtc_device_register(pdev->name, &pdev->dev, &mxc_rtc_ops,
THIS_MODULE);
if (IS_ERR(rtc)) {
@@ -446,8 +439,8 @@ static int __init mxc_rtc_probe(struct platform_device *pdev)
pdata->irq = platform_get_irq(pdev, 0);
if (pdata->irq >= 0 &&
- request_irq(pdata->irq, mxc_rtc_interrupt, IRQF_SHARED,
- pdev->name, pdev) < 0) {
+ devm_request_irq(&pdev->dev, pdata->irq, mxc_rtc_interrupt,
+ IRQF_SHARED, pdev->name, pdev) < 0) {
dev_warn(&pdev->dev, "interrupt not available.\n");
pdata->irq = -1;
}
@@ -455,10 +448,10 @@ static int __init mxc_rtc_probe(struct platform_device *pdev)
return 0;
exit_put_clk:
+ clk_disable(pdata->clk);
clk_put(pdata->clk);
exit_free_pdata:
- kfree(pdata);
return ret;
}
@@ -469,12 +462,8 @@ static int __exit mxc_rtc_remove(struct platform_device *pdev)
rtc_device_unregister(pdata->rtc);
- if (pdata->irq >= 0)
- free_irq(pdata->irq, pdev);
-
clk_disable(pdata->clk);
clk_put(pdata->clk);
- kfree(pdata);
platform_set_drvdata(pdev, NULL);
return 0;
diff --git a/drivers/rtc/rtc-nuc900.c b/drivers/rtc/rtc-nuc900.c
index bf59c9c586b2..ddb0857e15a4 100644
--- a/drivers/rtc/rtc-nuc900.c
+++ b/drivers/rtc/rtc-nuc900.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
#include <linux/rtc.h>
#include <linux/delay.h>
#include <linux/io.h>
@@ -84,25 +85,24 @@ static irqreturn_t nuc900_rtc_interrupt(int irq, void *_rtc)
static int *check_rtc_access_enable(struct nuc900_rtc *nuc900_rtc)
{
- unsigned int i;
+ unsigned int timeout = 0x1000;
__raw_writel(INIRRESET, nuc900_rtc->rtc_reg + REG_RTC_INIR);
mdelay(10);
__raw_writel(AERPOWERON, nuc900_rtc->rtc_reg + REG_RTC_AER);
- for (i = 0; i < 1000; i++) {
- if (__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB)
- return 0;
- }
+ while (!(__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB)
+ && timeout--)
+ mdelay(1);
- if ((__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB) == 0x0)
- return ERR_PTR(-ENODEV);
+ if (!timeout)
+ return ERR_PTR(-EPERM);
- return ERR_PTR(-EPERM);
+ return 0;
}
-static void nuc900_rtc_bcd2bin(unsigned int timereg,
+static int nuc900_rtc_bcd2bin(unsigned int timereg,
unsigned int calreg, struct rtc_time *tm)
{
tm->tm_mday = bcd2bin(calreg >> 0);
@@ -113,15 +113,21 @@ static void nuc900_rtc_bcd2bin(unsigned int timereg,
tm->tm_min = bcd2bin(timereg >> 8);
tm->tm_hour = bcd2bin(timereg >> 16);
- rtc_valid_tm(tm);
+ return rtc_valid_tm(tm);
}
-static void nuc900_rtc_bin2bcd(struct rtc_time *settm,
+static void nuc900_rtc_bin2bcd(struct device *dev, struct rtc_time *settm,
struct nuc900_bcd_time *gettm)
{
gettm->bcd_mday = bin2bcd(settm->tm_mday) << 0;
gettm->bcd_mon = bin2bcd(settm->tm_mon) << 8;
- gettm->bcd_year = bin2bcd(settm->tm_year - 100) << 16;
+
+ if (settm->tm_year < 100) {
+ dev_warn(dev, "The year will be between 1970-1999, right?\n");
+ gettm->bcd_year = bin2bcd(settm->tm_year) << 16;
+ } else {
+ gettm->bcd_year = bin2bcd(settm->tm_year - 100) << 16;
+ }
gettm->bcd_sec = bin2bcd(settm->tm_sec) << 0;
gettm->bcd_min = bin2bcd(settm->tm_min) << 8;
@@ -164,9 +170,7 @@ static int nuc900_rtc_read_time(struct device *dev, struct rtc_time *tm)
timeval = __raw_readl(rtc->rtc_reg + REG_RTC_TLR);
clrval = __raw_readl(rtc->rtc_reg + REG_RTC_CLR);
- nuc900_rtc_bcd2bin(timeval, clrval, tm);
-
- return 0;
+ return nuc900_rtc_bcd2bin(timeval, clrval, tm);
}
static int nuc900_rtc_set_time(struct device *dev, struct rtc_time *tm)
@@ -176,7 +180,7 @@ static int nuc900_rtc_set_time(struct device *dev, struct rtc_time *tm)
unsigned long val;
int *err;
- nuc900_rtc_bin2bcd(tm, &gettm);
+ nuc900_rtc_bin2bcd(dev, tm, &gettm);
err = check_rtc_access_enable(rtc);
if (IS_ERR(err))
@@ -199,9 +203,7 @@ static int nuc900_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
timeval = __raw_readl(rtc->rtc_reg + REG_RTC_TAR);
carval = __raw_readl(rtc->rtc_reg + REG_RTC_CAR);
- nuc900_rtc_bcd2bin(timeval, carval, &alrm->time);
-
- return 0;
+ return nuc900_rtc_bcd2bin(timeval, carval, &alrm->time);
}
static int nuc900_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
@@ -211,7 +213,7 @@ static int nuc900_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
unsigned long val;
int *err;
- nuc900_rtc_bin2bcd(&alrm->time, &tm);
+ nuc900_rtc_bin2bcd(dev, &alrm->time, &tm);
err = check_rtc_access_enable(rtc);
if (IS_ERR(err))
@@ -267,29 +269,30 @@ static int __devinit nuc900_rtc_probe(struct platform_device *pdev)
goto fail2;
}
- nuc900_rtc->irq_num = platform_get_irq(pdev, 0);
- if (request_irq(nuc900_rtc->irq_num, nuc900_rtc_interrupt,
- IRQF_DISABLED, "nuc900rtc", nuc900_rtc)) {
- dev_err(&pdev->dev, "NUC900 RTC request irq failed\n");
- err = -EBUSY;
- goto fail3;
- }
+ platform_set_drvdata(pdev, nuc900_rtc);
nuc900_rtc->rtcdev = rtc_device_register(pdev->name, &pdev->dev,
&nuc900_rtc_ops, THIS_MODULE);
if (IS_ERR(nuc900_rtc->rtcdev)) {
- dev_err(&pdev->dev, "rtc device register faild\n");
+ dev_err(&pdev->dev, "rtc device register failed\n");
err = PTR_ERR(nuc900_rtc->rtcdev);
- goto fail4;
+ goto fail3;
}
- platform_set_drvdata(pdev, nuc900_rtc);
__raw_writel(__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_TSSR) | MODE24,
nuc900_rtc->rtc_reg + REG_RTC_TSSR);
+ nuc900_rtc->irq_num = platform_get_irq(pdev, 0);
+ if (request_irq(nuc900_rtc->irq_num, nuc900_rtc_interrupt,
+ IRQF_DISABLED, "nuc900rtc", nuc900_rtc)) {
+ dev_err(&pdev->dev, "NUC900 RTC request irq failed\n");
+ err = -EBUSY;
+ goto fail4;
+ }
+
return 0;
-fail4: free_irq(nuc900_rtc->irq_num, nuc900_rtc);
+fail4: rtc_device_unregister(nuc900_rtc->rtcdev);
fail3: iounmap(nuc900_rtc->rtc_reg);
fail2: release_mem_region(res->start, resource_size(res));
fail1: kfree(nuc900_rtc);
@@ -301,8 +304,8 @@ static int __devexit nuc900_rtc_remove(struct platform_device *pdev)
struct nuc900_rtc *nuc900_rtc = platform_get_drvdata(pdev);
struct resource *res;
- rtc_device_unregister(nuc900_rtc->rtcdev);
free_irq(nuc900_rtc->irq_num, nuc900_rtc);
+ rtc_device_unregister(nuc900_rtc->rtcdev);
iounmap(nuc900_rtc->rtc_reg);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c
index 64d9727b7229..73377b0d65da 100644
--- a/drivers/rtc/rtc-omap.c
+++ b/drivers/rtc/rtc-omap.c
@@ -34,7 +34,8 @@
* Board-specific wiring options include using split power mode with
* RTC_OFF_NOFF used as the reset signal (so the RTC won't be reset),
* and wiring RTC_WAKE_INT (so the RTC alarm can wake the system from
- * low power modes). See the BOARD-SPECIFIC CUSTOMIZATION comment.
+ * low power modes) for OMAP1 boards (OMAP-L138 has this built into
+ * the SoC). See the BOARD-SPECIFIC CUSTOMIZATION comment.
*/
#define OMAP_RTC_BASE 0xfffb4800
@@ -401,16 +402,17 @@ static int __init omap_rtc_probe(struct platform_device *pdev)
/* BOARD-SPECIFIC CUSTOMIZATION CAN GO HERE:
*
- * - Boards wired so that RTC_WAKE_INT does something, and muxed
- * right (W13_1610_RTC_WAKE_INT is the default after chip reset),
- * should initialize the device wakeup flag appropriately.
+ * - Device wake-up capability setting should come through chip
+ * init logic. OMAP1 boards should initialize the "wakeup capable"
+ * flag in the platform device if the board is wired right for
+ * being woken up by RTC alarm. For OMAP-L138, this capability
+ * is built into the SoC by the "Deep Sleep" capability.
*
* - Boards wired so RTC_ON_nOFF is used as the reset signal,
* rather than nPWRON_RESET, should forcibly enable split
* power mode. (Some chip errata report that RTC_CTRL_SPLIT
* is write-only, and always reads as zero...)
*/
- device_init_wakeup(&pdev->dev, 0);
if (new_ctrl & (u8) OMAP_RTC_CTRL_SPLIT)
pr_info("%s: split power mode\n", pdev->name);
diff --git a/drivers/rtc/rtc-pcap.c b/drivers/rtc/rtc-pcap.c
index a99c28992d21..25c0b3fd44f1 100644
--- a/drivers/rtc/rtc-pcap.c
+++ b/drivers/rtc/rtc-pcap.c
@@ -17,6 +17,7 @@
#include <linux/init.h>
#include <linux/mfd/ezx-pcap.h>
#include <linux/rtc.h>
+#include <linux/slab.h>
#include <linux/platform_device.h>
struct pcap_rtc {
diff --git a/drivers/rtc/rtc-pcf2123.c b/drivers/rtc/rtc-pcf2123.c
index e75df9d50e27..71bab0ef5443 100644
--- a/drivers/rtc/rtc-pcf2123.c
+++ b/drivers/rtc/rtc-pcf2123.c
@@ -39,6 +39,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
+#include <linux/slab.h>
#include <linux/rtc.h>
#include <linux/spi/spi.h>
@@ -315,7 +316,7 @@ kfree_exit:
return ret;
}
-static int pcf2123_remove(struct spi_device *spi)
+static int __devexit pcf2123_remove(struct spi_device *spi)
{
struct pcf2123_plat_data *pdata = spi->dev.platform_data;
int i;
diff --git a/drivers/rtc/rtc-pcf50633.c b/drivers/rtc/rtc-pcf50633.c
index 854c3cb365a1..16edf94ab42f 100644
--- a/drivers/rtc/rtc-pcf50633.c
+++ b/drivers/rtc/rtc-pcf50633.c
@@ -18,6 +18,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
+#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c
index 65f346b2fbae..b42c0c679266 100644
--- a/drivers/rtc/rtc-pcf8563.c
+++ b/drivers/rtc/rtc-pcf8563.c
@@ -17,6 +17,7 @@
#include <linux/i2c.h>
#include <linux/bcd.h>
#include <linux/rtc.h>
+#include <linux/slab.h>
#define DRV_VERSION "0.4.3"
@@ -171,14 +172,6 @@ static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm)
return 0;
}
-struct pcf8563_limit
-{
- unsigned char reg;
- unsigned char mask;
- unsigned char min;
- unsigned char max;
-};
-
static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
return pcf8563_get_datetime(to_i2c_client(dev), tm);
diff --git a/drivers/rtc/rtc-pl030.c b/drivers/rtc/rtc-pl030.c
index 457231bb1029..bbdb2f02798a 100644
--- a/drivers/rtc/rtc-pl030.c
+++ b/drivers/rtc/rtc-pl030.c
@@ -13,6 +13,7 @@
#include <linux/interrupt.h>
#include <linux/amba/bus.h>
#include <linux/io.h>
+#include <linux/slab.h>
#define RTC_DR (0)
#define RTC_MR (4)
diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c
index 0264b117893b..b7a6690e5b35 100644
--- a/drivers/rtc/rtc-pl031.c
+++ b/drivers/rtc/rtc-pl031.c
@@ -7,6 +7,9 @@
*
* Copyright 2006 (c) MontaVista Software, Inc.
*
+ * Author: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>
+ * Copyright 2010 (c) ST-Ericsson AB
+ *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
@@ -18,6 +21,9 @@
#include <linux/interrupt.h>
#include <linux/amba/bus.h>
#include <linux/io.h>
+#include <linux/bcd.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
/*
* Register definitions
@@ -30,35 +36,207 @@
#define RTC_RIS 0x14 /* Raw interrupt status register */
#define RTC_MIS 0x18 /* Masked interrupt status register */
#define RTC_ICR 0x1c /* Interrupt clear register */
+/* ST variants have additional timer functionality */
+#define RTC_TDR 0x20 /* Timer data read register */
+#define RTC_TLR 0x24 /* Timer data load register */
+#define RTC_TCR 0x28 /* Timer control register */
+#define RTC_YDR 0x30 /* Year data read register */
+#define RTC_YMR 0x34 /* Year match register */
+#define RTC_YLR 0x38 /* Year data load register */
+
+#define RTC_CR_CWEN (1 << 26) /* Clockwatch enable bit */
+
+#define RTC_TCR_EN (1 << 1) /* Periodic timer enable bit */
+
+/* Common bit definitions for Interrupt status and control registers */
+#define RTC_BIT_AI (1 << 0) /* Alarm interrupt bit */
+#define RTC_BIT_PI (1 << 1) /* Periodic interrupt bit. ST variants only. */
+
+/* Common bit definations for ST v2 for reading/writing time */
+#define RTC_SEC_SHIFT 0
+#define RTC_SEC_MASK (0x3F << RTC_SEC_SHIFT) /* Second [0-59] */
+#define RTC_MIN_SHIFT 6
+#define RTC_MIN_MASK (0x3F << RTC_MIN_SHIFT) /* Minute [0-59] */
+#define RTC_HOUR_SHIFT 12
+#define RTC_HOUR_MASK (0x1F << RTC_HOUR_SHIFT) /* Hour [0-23] */
+#define RTC_WDAY_SHIFT 17
+#define RTC_WDAY_MASK (0x7 << RTC_WDAY_SHIFT) /* Day of Week [1-7] 1=Sunday */
+#define RTC_MDAY_SHIFT 20
+#define RTC_MDAY_MASK (0x1F << RTC_MDAY_SHIFT) /* Day of Month [1-31] */
+#define RTC_MON_SHIFT 25
+#define RTC_MON_MASK (0xF << RTC_MON_SHIFT) /* Month [1-12] 1=January */
+
+#define RTC_TIMER_FREQ 32768
struct pl031_local {
struct rtc_device *rtc;
void __iomem *base;
+ u8 hw_designer;
+ u8 hw_revision:4;
};
-static irqreturn_t pl031_interrupt(int irq, void *dev_id)
+static int pl031_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct pl031_local *ldata = dev_get_drvdata(dev);
+ unsigned long imsc;
+
+ /* Clear any pending alarm interrupts. */
+ writel(RTC_BIT_AI, ldata->base + RTC_ICR);
+
+ imsc = readl(ldata->base + RTC_IMSC);
+
+ if (enabled == 1)
+ writel(imsc | RTC_BIT_AI, ldata->base + RTC_IMSC);
+ else
+ writel(imsc & ~RTC_BIT_AI, ldata->base + RTC_IMSC);
+
+ return 0;
+}
+
+/*
+ * Convert Gregorian date to ST v2 RTC format.
+ */
+static int pl031_stv2_tm_to_time(struct device *dev,
+ struct rtc_time *tm, unsigned long *st_time,
+ unsigned long *bcd_year)
+{
+ int year = tm->tm_year + 1900;
+ int wday = tm->tm_wday;
+
+ /* wday masking is not working in hardware so wday must be valid */
+ if (wday < -1 || wday > 6) {
+ dev_err(dev, "invalid wday value %d\n", tm->tm_wday);
+ return -EINVAL;
+ } else if (wday == -1) {
+ /* wday is not provided, calculate it here */
+ unsigned long time;
+ struct rtc_time calc_tm;
+
+ rtc_tm_to_time(tm, &time);
+ rtc_time_to_tm(time, &calc_tm);
+ wday = calc_tm.tm_wday;
+ }
+
+ *bcd_year = (bin2bcd(year % 100) | bin2bcd(year / 100) << 8);
+
+ *st_time = ((tm->tm_mon + 1) << RTC_MON_SHIFT)
+ | (tm->tm_mday << RTC_MDAY_SHIFT)
+ | ((wday + 1) << RTC_WDAY_SHIFT)
+ | (tm->tm_hour << RTC_HOUR_SHIFT)
+ | (tm->tm_min << RTC_MIN_SHIFT)
+ | (tm->tm_sec << RTC_SEC_SHIFT);
+
+ return 0;
+}
+
+/*
+ * Convert ST v2 RTC format to Gregorian date.
+ */
+static int pl031_stv2_time_to_tm(unsigned long st_time, unsigned long bcd_year,
+ struct rtc_time *tm)
+{
+ tm->tm_year = bcd2bin(bcd_year) + (bcd2bin(bcd_year >> 8) * 100);
+ tm->tm_mon = ((st_time & RTC_MON_MASK) >> RTC_MON_SHIFT) - 1;
+ tm->tm_mday = ((st_time & RTC_MDAY_MASK) >> RTC_MDAY_SHIFT);
+ tm->tm_wday = ((st_time & RTC_WDAY_MASK) >> RTC_WDAY_SHIFT) - 1;
+ tm->tm_hour = ((st_time & RTC_HOUR_MASK) >> RTC_HOUR_SHIFT);
+ tm->tm_min = ((st_time & RTC_MIN_MASK) >> RTC_MIN_SHIFT);
+ tm->tm_sec = ((st_time & RTC_SEC_MASK) >> RTC_SEC_SHIFT);
+
+ tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
+ tm->tm_year -= 1900;
+
+ return 0;
+}
+
+static int pl031_stv2_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct pl031_local *ldata = dev_get_drvdata(dev);
+
+ pl031_stv2_time_to_tm(readl(ldata->base + RTC_DR),
+ readl(ldata->base + RTC_YDR), tm);
+
+ return 0;
+}
+
+static int pl031_stv2_set_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned long time;
+ unsigned long bcd_year;
+ struct pl031_local *ldata = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pl031_stv2_tm_to_time(dev, tm, &time, &bcd_year);
+ if (ret == 0) {
+ writel(bcd_year, ldata->base + RTC_YLR);
+ writel(time, ldata->base + RTC_LR);
+ }
+
+ return ret;
+}
+
+static int pl031_stv2_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
- struct rtc_device *rtc = dev_id;
+ struct pl031_local *ldata = dev_get_drvdata(dev);
+ int ret;
- rtc_update_irq(rtc, 1, RTC_AF);
+ ret = pl031_stv2_time_to_tm(readl(ldata->base + RTC_MR),
+ readl(ldata->base + RTC_YMR), &alarm->time);
- return IRQ_HANDLED;
+ alarm->pending = readl(ldata->base + RTC_RIS) & RTC_BIT_AI;
+ alarm->enabled = readl(ldata->base + RTC_IMSC) & RTC_BIT_AI;
+
+ return ret;
}
-static int pl031_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+static int pl031_stv2_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct pl031_local *ldata = dev_get_drvdata(dev);
+ unsigned long time;
+ unsigned long bcd_year;
+ int ret;
+
+ /* At the moment, we can only deal with non-wildcarded alarm times. */
+ ret = rtc_valid_tm(&alarm->time);
+ if (ret == 0) {
+ ret = pl031_stv2_tm_to_time(dev, &alarm->time,
+ &time, &bcd_year);
+ if (ret == 0) {
+ writel(bcd_year, ldata->base + RTC_YMR);
+ writel(time, ldata->base + RTC_MR);
+
+ pl031_alarm_irq_enable(dev, alarm->enabled);
+ }
+ }
+
+ return ret;
+}
+
+static irqreturn_t pl031_interrupt(int irq, void *dev_id)
+{
+ struct pl031_local *ldata = dev_id;
+ unsigned long rtcmis;
+ unsigned long events = 0;
+
+ rtcmis = readl(ldata->base + RTC_MIS);
+ if (rtcmis) {
+ writel(rtcmis, ldata->base + RTC_ICR);
+
+ if (rtcmis & RTC_BIT_AI)
+ events |= (RTC_AF | RTC_IRQF);
+
+ /* Timer interrupt is only available in ST variants */
+ if ((rtcmis & RTC_BIT_PI) &&
+ (ldata->hw_designer == AMBA_VENDOR_ST))
+ events |= (RTC_PF | RTC_IRQF);
+
+ rtc_update_irq(ldata->rtc, 1, events);
- switch (cmd) {
- case RTC_AIE_OFF:
- writel(1, ldata->base + RTC_MIS);
- return 0;
- case RTC_AIE_ON:
- writel(0, ldata->base + RTC_MIS);
- return 0;
+ return IRQ_HANDLED;
}
- return -ENOIOCTLCMD;
+ return IRQ_NONE;
}
static int pl031_read_time(struct device *dev, struct rtc_time *tm)
@@ -74,11 +252,14 @@ static int pl031_set_time(struct device *dev, struct rtc_time *tm)
{
unsigned long time;
struct pl031_local *ldata = dev_get_drvdata(dev);
+ int ret;
- rtc_tm_to_time(tm, &time);
- writel(time, ldata->base + RTC_LR);
+ ret = rtc_tm_to_time(tm, &time);
- return 0;
+ if (ret == 0)
+ writel(time, ldata->base + RTC_LR);
+
+ return ret;
}
static int pl031_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
@@ -86,8 +267,9 @@ static int pl031_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
struct pl031_local *ldata = dev_get_drvdata(dev);
rtc_time_to_tm(readl(ldata->base + RTC_MR), &alarm->time);
- alarm->pending = readl(ldata->base + RTC_RIS);
- alarm->enabled = readl(ldata->base + RTC_IMSC);
+
+ alarm->pending = readl(ldata->base + RTC_RIS) & RTC_BIT_AI;
+ alarm->enabled = readl(ldata->base + RTC_IMSC) & RTC_BIT_AI;
return 0;
}
@@ -96,22 +278,71 @@ static int pl031_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct pl031_local *ldata = dev_get_drvdata(dev);
unsigned long time;
+ int ret;
+
+ /* At the moment, we can only deal with non-wildcarded alarm times. */
+ ret = rtc_valid_tm(&alarm->time);
+ if (ret == 0) {
+ ret = rtc_tm_to_time(&alarm->time, &time);
+ if (ret == 0) {
+ writel(time, ldata->base + RTC_MR);
+ pl031_alarm_irq_enable(dev, alarm->enabled);
+ }
+ }
+
+ return ret;
+}
+
+/* Periodic interrupt is only available in ST variants. */
+static int pl031_irq_set_state(struct device *dev, int enabled)
+{
+ struct pl031_local *ldata = dev_get_drvdata(dev);
+
+ if (enabled == 1) {
+ /* Clear any pending timer interrupt. */
+ writel(RTC_BIT_PI, ldata->base + RTC_ICR);
+
+ writel(readl(ldata->base + RTC_IMSC) | RTC_BIT_PI,
+ ldata->base + RTC_IMSC);
- rtc_tm_to_time(&alarm->time, &time);
+ /* Now start the timer */
+ writel(readl(ldata->base + RTC_TCR) | RTC_TCR_EN,
+ ldata->base + RTC_TCR);
- writel(time, ldata->base + RTC_MR);
- writel(!alarm->enabled, ldata->base + RTC_MIS);
+ } else {
+ writel(readl(ldata->base + RTC_IMSC) & (~RTC_BIT_PI),
+ ldata->base + RTC_IMSC);
+
+ /* Also stop the timer */
+ writel(readl(ldata->base + RTC_TCR) & (~RTC_TCR_EN),
+ ldata->base + RTC_TCR);
+ }
+ /* Wait at least 1 RTC32 clock cycle to ensure next access
+ * to RTC_TCR will succeed.
+ */
+ udelay(40);
return 0;
}
-static const struct rtc_class_ops pl031_ops = {
- .ioctl = pl031_ioctl,
- .read_time = pl031_read_time,
- .set_time = pl031_set_time,
- .read_alarm = pl031_read_alarm,
- .set_alarm = pl031_set_alarm,
-};
+static int pl031_irq_set_freq(struct device *dev, int freq)
+{
+ struct pl031_local *ldata = dev_get_drvdata(dev);
+
+ /* Cant set timer if it is already enabled */
+ if (readl(ldata->base + RTC_TCR) & RTC_TCR_EN) {
+ dev_err(dev, "can't change frequency while timer enabled\n");
+ return -EINVAL;
+ }
+
+ /* If self start bit in RTC_TCR is set timer will start here,
+ * but we never set that bit. Instead we start the timer when
+ * set_state is called with enabled == 1.
+ */
+ writel(RTC_TIMER_FREQ / freq, ldata->base + RTC_TLR);
+
+ return 0;
+}
static int pl031_remove(struct amba_device *adev)
{
@@ -131,18 +362,20 @@ static int pl031_probe(struct amba_device *adev, struct amba_id *id)
{
int ret;
struct pl031_local *ldata;
+ struct rtc_class_ops *ops = id->data;
ret = amba_request_regions(adev, NULL);
if (ret)
goto err_req;
- ldata = kmalloc(sizeof(struct pl031_local), GFP_KERNEL);
+ ldata = kzalloc(sizeof(struct pl031_local), GFP_KERNEL);
if (!ldata) {
ret = -ENOMEM;
goto out;
}
ldata->base = ioremap(adev->res.start, resource_size(&adev->res));
+
if (!ldata->base) {
ret = -ENOMEM;
goto out_no_remap;
@@ -150,24 +383,36 @@ static int pl031_probe(struct amba_device *adev, struct amba_id *id)
amba_set_drvdata(adev, ldata);
- if (request_irq(adev->irq[0], pl031_interrupt, IRQF_DISABLED,
- "rtc-pl031", ldata->rtc)) {
- ret = -EIO;
- goto out_no_irq;
- }
+ ldata->hw_designer = amba_manf(adev);
+ ldata->hw_revision = amba_rev(adev);
+
+ dev_dbg(&adev->dev, "designer ID = 0x%02x\n", ldata->hw_designer);
+ dev_dbg(&adev->dev, "revision = 0x%01x\n", ldata->hw_revision);
- ldata->rtc = rtc_device_register("pl031", &adev->dev, &pl031_ops,
- THIS_MODULE);
+ /* Enable the clockwatch on ST Variants */
+ if ((ldata->hw_designer == AMBA_VENDOR_ST) &&
+ (ldata->hw_revision > 1))
+ writel(readl(ldata->base + RTC_CR) | RTC_CR_CWEN,
+ ldata->base + RTC_CR);
+
+ ldata->rtc = rtc_device_register("pl031", &adev->dev, ops,
+ THIS_MODULE);
if (IS_ERR(ldata->rtc)) {
ret = PTR_ERR(ldata->rtc);
goto out_no_rtc;
}
+ if (request_irq(adev->irq[0], pl031_interrupt,
+ IRQF_DISABLED, "rtc-pl031", ldata)) {
+ ret = -EIO;
+ goto out_no_irq;
+ }
+
return 0;
-out_no_rtc:
- free_irq(adev->irq[0], ldata->rtc);
out_no_irq:
+ rtc_device_unregister(ldata->rtc);
+out_no_rtc:
iounmap(ldata->base);
amba_set_drvdata(adev, NULL);
out_no_remap:
@@ -175,13 +420,57 @@ out_no_remap:
out:
amba_release_regions(adev);
err_req:
+
return ret;
}
-static struct amba_id pl031_ids[] __initdata = {
+/* Operations for the original ARM version */
+static struct rtc_class_ops arm_pl031_ops = {
+ .read_time = pl031_read_time,
+ .set_time = pl031_set_time,
+ .read_alarm = pl031_read_alarm,
+ .set_alarm = pl031_set_alarm,
+ .alarm_irq_enable = pl031_alarm_irq_enable,
+};
+
+/* The First ST derivative */
+static struct rtc_class_ops stv1_pl031_ops = {
+ .read_time = pl031_read_time,
+ .set_time = pl031_set_time,
+ .read_alarm = pl031_read_alarm,
+ .set_alarm = pl031_set_alarm,
+ .alarm_irq_enable = pl031_alarm_irq_enable,
+ .irq_set_state = pl031_irq_set_state,
+ .irq_set_freq = pl031_irq_set_freq,
+};
+
+/* And the second ST derivative */
+static struct rtc_class_ops stv2_pl031_ops = {
+ .read_time = pl031_stv2_read_time,
+ .set_time = pl031_stv2_set_time,
+ .read_alarm = pl031_stv2_read_alarm,
+ .set_alarm = pl031_stv2_set_alarm,
+ .alarm_irq_enable = pl031_alarm_irq_enable,
+ .irq_set_state = pl031_irq_set_state,
+ .irq_set_freq = pl031_irq_set_freq,
+};
+
+static struct amba_id pl031_ids[] = {
{
.id = 0x00041031,
.mask = 0x000fffff,
+ .data = &arm_pl031_ops,
+ },
+ /* ST Micro variants */
+ {
+ .id = 0x00180031,
+ .mask = 0x00ffffff,
+ .data = &stv1_pl031_ops,
+ },
+ {
+ .id = 0x00280031,
+ .mask = 0x00ffffff,
+ .data = &stv2_pl031_ops,
},
{0, 0},
};
diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c
index e6351b743da6..29e867a1aaa8 100644
--- a/drivers/rtc/rtc-pxa.c
+++ b/drivers/rtc/rtc-pxa.c
@@ -26,6 +26,7 @@
#include <linux/seq_file.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/slab.h>
#include <mach/hardware.h>
@@ -86,7 +87,6 @@ struct pxa_rtc {
int irq_Alrm;
struct rtc_device *rtc;
spinlock_t lock; /* Protects this structure */
- struct rtc_time rtc_alarm;
};
static u32 ryxr_calc(struct rtc_time *tm)
@@ -235,32 +235,34 @@ static int pxa_periodic_irq_set_state(struct device *dev, int enabled)
return 0;
}
-static int pxa_rtc_ioctl(struct device *dev, unsigned int cmd,
- unsigned long arg)
+static int pxa_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev);
- int ret = 0;
spin_lock_irq(&pxa_rtc->lock);
- switch (cmd) {
- case RTC_AIE_OFF:
- rtsr_clear_bits(pxa_rtc, RTSR_RDALE1);
- break;
- case RTC_AIE_ON:
+
+ if (enabled)
rtsr_set_bits(pxa_rtc, RTSR_RDALE1);
- break;
- case RTC_UIE_OFF:
- rtsr_clear_bits(pxa_rtc, RTSR_HZE);
- break;
- case RTC_UIE_ON:
+ else
+ rtsr_clear_bits(pxa_rtc, RTSR_RDALE1);
+
+ spin_unlock_irq(&pxa_rtc->lock);
+ return 0;
+}
+
+static int pxa_update_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev);
+
+ spin_lock_irq(&pxa_rtc->lock);
+
+ if (enabled)
rtsr_set_bits(pxa_rtc, RTSR_HZE);
- break;
- default:
- ret = -ENOIOCTLCMD;
- }
+ else
+ rtsr_clear_bits(pxa_rtc, RTSR_HZE);
spin_unlock_irq(&pxa_rtc->lock);
- return ret;
+ return 0;
}
static int pxa_rtc_read_time(struct device *dev, struct rtc_time *tm)
@@ -339,11 +341,12 @@ static int pxa_rtc_proc(struct device *dev, struct seq_file *seq)
static const struct rtc_class_ops pxa_rtc_ops = {
.open = pxa_rtc_open,
.release = pxa_rtc_release,
- .ioctl = pxa_rtc_ioctl,
.read_time = pxa_rtc_read_time,
.set_time = pxa_rtc_set_time,
.read_alarm = pxa_rtc_read_alarm,
.set_alarm = pxa_rtc_set_alarm,
+ .alarm_irq_enable = pxa_alarm_irq_enable,
+ .update_irq_enable = pxa_update_irq_enable,
.proc = pxa_rtc_proc,
.irq_set_state = pxa_periodic_irq_set_state,
.irq_set_freq = pxa_periodic_irq_set_freq,
diff --git a/drivers/rtc/rtc-rp5c01.c b/drivers/rtc/rtc-rp5c01.c
index e1313feb060f..36eb66184461 100644
--- a/drivers/rtc/rtc-rp5c01.c
+++ b/drivers/rtc/rtc-rp5c01.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
+#include <linux/slab.h>
enum {
@@ -62,6 +63,8 @@ enum {
struct rp5c01_priv {
u32 __iomem *regs;
struct rtc_device *rtc;
+ spinlock_t lock; /* against concurrent RTC/NVRAM access */
+ struct bin_attribute nvram_attr;
};
static inline unsigned int rp5c01_read(struct rp5c01_priv *priv,
@@ -91,6 +94,7 @@ static int rp5c01_read_time(struct device *dev, struct rtc_time *tm)
{
struct rp5c01_priv *priv = dev_get_drvdata(dev);
+ spin_lock_irq(&priv->lock);
rp5c01_lock(priv);
tm->tm_sec = rp5c01_read(priv, RP5C01_10_SECOND) * 10 +
@@ -110,6 +114,7 @@ static int rp5c01_read_time(struct device *dev, struct rtc_time *tm)
tm->tm_year += 100;
rp5c01_unlock(priv);
+ spin_unlock_irq(&priv->lock);
return rtc_valid_tm(tm);
}
@@ -118,6 +123,7 @@ static int rp5c01_set_time(struct device *dev, struct rtc_time *tm)
{
struct rp5c01_priv *priv = dev_get_drvdata(dev);
+ spin_lock_irq(&priv->lock);
rp5c01_lock(priv);
rp5c01_write(priv, tm->tm_sec / 10, RP5C01_10_SECOND);
@@ -138,6 +144,7 @@ static int rp5c01_set_time(struct device *dev, struct rtc_time *tm)
rp5c01_write(priv, tm->tm_year % 10, RP5C01_1_YEAR);
rp5c01_unlock(priv);
+ spin_unlock_irq(&priv->lock);
return 0;
}
@@ -146,6 +153,72 @@ static const struct rtc_class_ops rp5c01_rtc_ops = {
.set_time = rp5c01_set_time,
};
+
+/*
+ * The NVRAM is organized as 2 blocks of 13 nibbles of 4 bits.
+ * We provide access to them like AmigaOS does: the high nibble of each 8-bit
+ * byte is stored in BLOCK10, the low nibble in BLOCK11.
+ */
+
+static ssize_t rp5c01_nvram_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t size)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct rp5c01_priv *priv = dev_get_drvdata(dev);
+ ssize_t count;
+
+ spin_lock_irq(&priv->lock);
+
+ for (count = 0; size > 0 && pos < RP5C01_MODE; count++, size--) {
+ u8 data;
+
+ rp5c01_write(priv,
+ RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK10,
+ RP5C01_MODE);
+ data = rp5c01_read(priv, pos) << 4;
+ rp5c01_write(priv,
+ RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK11,
+ RP5C01_MODE);
+ data |= rp5c01_read(priv, pos++);
+ rp5c01_write(priv, RP5C01_MODE_TIMER_EN | RP5C01_MODE_MODE01,
+ RP5C01_MODE);
+ *buf++ = data;
+ }
+
+ spin_unlock_irq(&priv->lock);
+ return count;
+}
+
+static ssize_t rp5c01_nvram_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t size)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct rp5c01_priv *priv = dev_get_drvdata(dev);
+ ssize_t count;
+
+ spin_lock_irq(&priv->lock);
+
+ for (count = 0; size > 0 && pos < RP5C01_MODE; count++, size--) {
+ u8 data = *buf++;
+
+ rp5c01_write(priv,
+ RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK10,
+ RP5C01_MODE);
+ rp5c01_write(priv, data >> 4, pos);
+ rp5c01_write(priv,
+ RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK11,
+ RP5C01_MODE);
+ rp5c01_write(priv, data & 0xf, pos++);
+ rp5c01_write(priv, RP5C01_MODE_TIMER_EN | RP5C01_MODE_MODE01,
+ RP5C01_MODE);
+ }
+
+ spin_unlock_irq(&priv->lock);
+ return count;
+}
+
static int __init rp5c01_rtc_probe(struct platform_device *dev)
{
struct resource *res;
@@ -167,6 +240,15 @@ static int __init rp5c01_rtc_probe(struct platform_device *dev)
goto out_free_priv;
}
+ sysfs_bin_attr_init(&priv->nvram_attr);
+ priv->nvram_attr.attr.name = "nvram";
+ priv->nvram_attr.attr.mode = S_IRUGO | S_IWUSR;
+ priv->nvram_attr.read = rp5c01_nvram_read;
+ priv->nvram_attr.write = rp5c01_nvram_write;
+ priv->nvram_attr.size = RP5C01_MODE;
+
+ spin_lock_init(&priv->lock);
+
rtc = rtc_device_register("rtc-rp5c01", &dev->dev, &rp5c01_rtc_ops,
THIS_MODULE);
if (IS_ERR(rtc)) {
@@ -176,8 +258,15 @@ static int __init rp5c01_rtc_probe(struct platform_device *dev)
priv->rtc = rtc;
platform_set_drvdata(dev, priv);
+
+ error = sysfs_create_bin_file(&dev->dev.kobj, &priv->nvram_attr);
+ if (error)
+ goto out_unregister;
+
return 0;
+out_unregister:
+ rtc_device_unregister(rtc);
out_unmap:
iounmap(priv->regs);
out_free_priv:
@@ -189,6 +278,7 @@ static int __exit rp5c01_rtc_remove(struct platform_device *dev)
{
struct rp5c01_priv *priv = platform_get_drvdata(dev);
+ sysfs_remove_bin_file(&dev->dev.kobj, &priv->nvram_attr);
rtc_device_unregister(priv->rtc);
iounmap(priv->regs);
kfree(priv);
diff --git a/drivers/rtc/rtc-rs5c313.c b/drivers/rtc/rtc-rs5c313.c
index e6ea3f5ee1eb..e3ff179b99ca 100644
--- a/drivers/rtc/rtc-rs5c313.c
+++ b/drivers/rtc/rtc-rs5c313.c
@@ -80,21 +80,21 @@
/* 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)
+#define RS5C313_CEENABLE __raw_writeb(RS5C313_CE_RTCCE, RS5C313_CE);
+#define RS5C313_CEDISABLE __raw_writeb(0x00, RS5C313_CE)
+#define RS5C313_MISCOP __raw_writeb(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);
+ __raw_writeb(__raw_readb(SCSMR1) & ~SCSMR1_CA, SCSMR1);
+ __raw_writeb(__raw_readb(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);
+ scsptr1_data = __raw_readb(SCSPTR1) | SCL; /* SCL:H */
+ __raw_writeb(scsptr1_data, SCSPTR1);
+ scsptr1_data = __raw_readb(SCSPTR1) | SCL_OEN; /* SCL output enable */
+ __raw_writeb(scsptr1_data, SCSPTR1);
RS5C313_CEDISABLE; /* CE:L */
}
@@ -106,21 +106,21 @@ static void rs5c313_write_data(unsigned char data)
/* SDA:Write Data */
scsptr1_data = (scsptr1_data & ~SDA) |
((((0x80 >> i) & data) >> (7 - i)) << 2);
- ctrl_outb(scsptr1_data, SCSPTR1);
+ __raw_writeb(scsptr1_data, SCSPTR1);
if (i == 0) {
scsptr1_data |= SDA_OEN; /* SDA:output enable */
- ctrl_outb(scsptr1_data, SCSPTR1);
+ __raw_writeb(scsptr1_data, SCSPTR1);
}
ndelay(700);
scsptr1_data &= ~SCL; /* SCL:L */
- ctrl_outb(scsptr1_data, SCSPTR1);
+ __raw_writeb(scsptr1_data, SCSPTR1);
ndelay(700);
scsptr1_data |= SCL; /* SCL:H */
- ctrl_outb(scsptr1_data, SCSPTR1);
+ __raw_writeb(scsptr1_data, SCSPTR1);
}
scsptr1_data &= ~SDA_OEN; /* SDA:output disable */
- ctrl_outb(scsptr1_data, SCSPTR1);
+ __raw_writeb(scsptr1_data, SCSPTR1);
}
static unsigned char rs5c313_read_data(void)
@@ -131,12 +131,12 @@ static unsigned char rs5c313_read_data(void)
for (i = 0; i < 8; i++) {
ndelay(700);
/* SDA:Read Data */
- data |= ((ctrl_inb(SCSPTR1) & SDA) >> 2) << (7 - i);
+ data |= ((__raw_readb(SCSPTR1) & SDA) >> 2) << (7 - i);
scsptr1_data &= ~SCL; /* SCL:L */
- ctrl_outb(scsptr1_data, SCSPTR1);
+ __raw_writeb(scsptr1_data, SCSPTR1);
ndelay(700);
scsptr1_data |= SCL; /* SCL:H */
- ctrl_outb(scsptr1_data, SCSPTR1);
+ __raw_writeb(scsptr1_data, SCSPTR1);
}
return data & 0x0F;
}
diff --git a/drivers/rtc/rtc-rs5c348.c b/drivers/rtc/rtc-rs5c348.c
index 2099037cb3ea..368d0e63cf83 100644
--- a/drivers/rtc/rtc-rs5c348.c
+++ b/drivers/rtc/rtc-rs5c348.c
@@ -19,6 +19,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
+#include <linux/slab.h>
#include <linux/rtc.h>
#include <linux/workqueue.h>
#include <linux/spi/spi.h>
diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c
index 2f2c68d476da..90cf0a6ff23e 100644
--- a/drivers/rtc/rtc-rs5c372.c
+++ b/drivers/rtc/rtc-rs5c372.c
@@ -13,6 +13,7 @@
#include <linux/i2c.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
+#include <linux/slab.h>
#define DRV_VERSION "0.6"
diff --git a/drivers/rtc/rtc-rx8025.c b/drivers/rtc/rtc-rx8025.c
index b1a29bcfdf13..1146e3522d3c 100644
--- a/drivers/rtc/rtc-rx8025.c
+++ b/drivers/rtc/rtc-rx8025.c
@@ -20,6 +20,7 @@
*/
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/slab.h>
#include <linux/init.h>
#include <linux/bcd.h>
#include <linux/i2c.h>
@@ -460,7 +461,7 @@ static struct rtc_class_ops rx8025_rtc_ops = {
* Clock precision adjustment support
*
* According to the RX8025 SA/NB application manual the frequency and
- * temperature charateristics can be approximated using the following
+ * temperature characteristics can be approximated using the following
* equation:
*
* df = a * (ut - t)**2
@@ -631,7 +632,6 @@ errout_reg:
rtc_device_unregister(rx8025->rtc);
errout_free:
- i2c_set_clientdata(client, NULL);
kfree(rx8025);
errout:
@@ -655,7 +655,6 @@ static int __devexit rx8025_remove(struct i2c_client *client)
rx8025_sysfs_unregister(&client->dev);
rtc_device_unregister(rx8025->rtc);
- i2c_set_clientdata(client, NULL);
kfree(rx8025);
return 0;
}
diff --git a/drivers/rtc/rtc-rx8581.c b/drivers/rtc/rtc-rx8581.c
index c9522f3bc21c..600b890a3c15 100644
--- a/drivers/rtc/rtc-rx8581.c
+++ b/drivers/rtc/rtc-rx8581.c
@@ -1,8 +1,8 @@
/*
* An I2C driver for the Epson RX8581 RTC
*
- * Author: Martyn Welch <martyn.welch@gefanuc.com>
- * Copyright 2008 GE Fanuc Intelligent Platforms Embedded Systems, Inc.
+ * Author: Martyn Welch <martyn.welch@ge.com>
+ * Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -168,7 +168,7 @@ static int rx8581_set_datetime(struct i2c_client *client, struct rtc_time *tm)
return -EIO;
}
- err = i2c_smbus_write_byte_data(client, RX8581_REG_FLAG,
+ err = i2c_smbus_write_byte_data(client, RX8581_REG_CTRL,
(data | RX8581_CTRL_STOP));
if (err < 0) {
dev_err(&client->dev, "Unable to write control register\n");
@@ -182,6 +182,20 @@ static int rx8581_set_datetime(struct i2c_client *client, struct rtc_time *tm)
return -EIO;
}
+ /* get VLF and clear it */
+ data = i2c_smbus_read_byte_data(client, RX8581_REG_FLAG);
+ if (data < 0) {
+ dev_err(&client->dev, "Unable to read flag register\n");
+ return -EIO;
+ }
+
+ err = i2c_smbus_write_byte_data(client, RX8581_REG_FLAG,
+ (data & ~(RX8581_FLAG_VLF)));
+ if (err != 0) {
+ dev_err(&client->dev, "Unable to write flag register\n");
+ return -EIO;
+ }
+
/* Restart the clock */
data = i2c_smbus_read_byte_data(client, RX8581_REG_CTRL);
if (data < 0) {
@@ -189,8 +203,8 @@ static int rx8581_set_datetime(struct i2c_client *client, struct rtc_time *tm)
return -EIO;
}
- err = i2c_smbus_write_byte_data(client, RX8581_REG_FLAG,
- (data | ~(RX8581_CTRL_STOP)));
+ err = i2c_smbus_write_byte_data(client, RX8581_REG_CTRL,
+ (data & ~(RX8581_CTRL_STOP)));
if (err != 0) {
dev_err(&client->dev, "Unable to write control register\n");
return -EIO;
@@ -272,7 +286,7 @@ static void __exit rx8581_exit(void)
i2c_del_driver(&rx8581_driver);
}
-MODULE_AUTHOR("Martyn Welch <martyn.welch@gefanuc.com>");
+MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com>");
MODULE_DESCRIPTION("Epson RX-8581 RTC driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/rtc/rtc-s35390a.c b/drivers/rtc/rtc-s35390a.c
index def4d396d0b0..f789e002c9b0 100644
--- a/drivers/rtc/rtc-s35390a.c
+++ b/drivers/rtc/rtc-s35390a.c
@@ -275,7 +275,6 @@ exit_dummy:
if (s35390a->client[i])
i2c_unregister_device(s35390a->client[i]);
kfree(s35390a);
- i2c_set_clientdata(client, NULL);
exit:
return err;
@@ -292,7 +291,6 @@ static int s35390a_remove(struct i2c_client *client)
rtc_device_unregister(s35390a->rtc);
kfree(s35390a);
- i2c_set_clientdata(client, NULL);
return 0;
}
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
index e0d7b9991505..cf953ecbfca9 100644
--- a/drivers/rtc/rtc-s3c.c
+++ b/drivers/rtc/rtc-s3c.c
@@ -1,5 +1,8 @@
/* drivers/rtc/rtc-s3c.c
*
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
* Copyright (c) 2004,2006 Simtec Electronics
* Ben Dooks, <ben@simtec.co.uk>
* http://armlinux.simtec.co.uk/
@@ -21,6 +24,7 @@
#include <linux/bcd.h>
#include <linux/clk.h>
#include <linux/log2.h>
+#include <linux/slab.h>
#include <mach/hardware.h>
#include <asm/uaccess.h>
@@ -28,14 +32,21 @@
#include <asm/irq.h>
#include <plat/regs-rtc.h>
+enum s3c_cpu_type {
+ TYPE_S3C2410,
+ TYPE_S3C64XX,
+};
+
/* I have yet to find an S3C implementation with more than one
* of these rtc blocks in */
static struct resource *s3c_rtc_mem;
+static struct clk *rtc_clk;
static void __iomem *s3c_rtc_base;
static int s3c_rtc_alarmno = NO_IRQ;
static int s3c_rtc_tickno = NO_IRQ;
+static enum s3c_cpu_type s3c_rtc_cpu_type;
static DEFINE_SPINLOCK(s3c_rtc_pie_lock);
@@ -46,6 +57,10 @@ static irqreturn_t s3c_rtc_alarmirq(int irq, void *id)
struct rtc_device *rdev = id;
rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF);
+
+ if (s3c_rtc_cpu_type == TYPE_S3C64XX)
+ writeb(S3C2410_INTP_ALM, s3c_rtc_base + S3C2410_INTP);
+
return IRQ_HANDLED;
}
@@ -54,6 +69,10 @@ static irqreturn_t s3c_rtc_tickirq(int irq, void *id)
struct rtc_device *rdev = id;
rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF);
+
+ if (s3c_rtc_cpu_type == TYPE_S3C64XX)
+ writeb(S3C2410_INTP_TIC, s3c_rtc_base + S3C2410_INTP);
+
return IRQ_HANDLED;
}
@@ -79,12 +98,25 @@ static int s3c_rtc_setpie(struct device *dev, int enabled)
pr_debug("%s: pie=%d\n", __func__, enabled);
spin_lock_irq(&s3c_rtc_pie_lock);
- tmp = readb(s3c_rtc_base + S3C2410_TICNT) & ~S3C2410_TICNT_ENABLE;
- if (enabled)
- tmp |= S3C2410_TICNT_ENABLE;
+ if (s3c_rtc_cpu_type == TYPE_S3C64XX) {
+ tmp = readw(s3c_rtc_base + S3C2410_RTCCON);
+ tmp &= ~S3C64XX_RTCCON_TICEN;
+
+ if (enabled)
+ tmp |= S3C64XX_RTCCON_TICEN;
+
+ writew(tmp, s3c_rtc_base + S3C2410_RTCCON);
+ } else {
+ tmp = readb(s3c_rtc_base + S3C2410_TICNT);
+ tmp &= ~S3C2410_TICNT_ENABLE;
+
+ if (enabled)
+ tmp |= S3C2410_TICNT_ENABLE;
+
+ writeb(tmp, s3c_rtc_base + S3C2410_TICNT);
+ }
- writeb(tmp, s3c_rtc_base + S3C2410_TICNT);
spin_unlock_irq(&s3c_rtc_pie_lock);
return 0;
@@ -92,17 +124,23 @@ static int s3c_rtc_setpie(struct device *dev, int enabled)
static int s3c_rtc_setfreq(struct device *dev, int freq)
{
- unsigned int tmp;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
+ unsigned int tmp = 0;
if (!is_power_of_2(freq))
return -EINVAL;
spin_lock_irq(&s3c_rtc_pie_lock);
- tmp = readb(s3c_rtc_base + S3C2410_TICNT) & S3C2410_TICNT_ENABLE;
- tmp |= (128 / freq)-1;
+ if (s3c_rtc_cpu_type == TYPE_S3C2410) {
+ tmp = readb(s3c_rtc_base + S3C2410_TICNT);
+ tmp &= S3C2410_TICNT_ENABLE;
+ }
+
+ tmp |= (rtc_dev->max_user_freq / freq)-1;
- writeb(tmp, s3c_rtc_base + S3C2410_TICNT);
+ writel(tmp, s3c_rtc_base + S3C2410_TICNT);
spin_unlock_irq(&s3c_rtc_pie_lock);
return 0;
@@ -133,8 +171,8 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
goto retry_get_time;
}
- pr_debug("read time %02x.%02x.%02x %02x/%02x/%02x\n",
- rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
+ pr_debug("read time %04d.%02d.%02d %02d:%02d:%02d\n",
+ 1900 + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);
@@ -147,7 +185,7 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
rtc_tm->tm_year += 100;
rtc_tm->tm_mon -= 1;
- return 0;
+ return rtc_valid_tm(rtc_tm);
}
static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
@@ -155,8 +193,8 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
void __iomem *base = s3c_rtc_base;
int year = tm->tm_year - 100;
- pr_debug("set time %02d.%02d.%02d %02d/%02d/%02d\n",
- tm->tm_year, tm->tm_mon, tm->tm_mday,
+ pr_debug("set time %04d.%02d.%02d %02d:%02d:%02d\n",
+ 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
/* we get around y2k by simply not supporting it */
@@ -193,9 +231,9 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0;
- pr_debug("read alarm %02x %02x.%02x.%02x %02x/%02x/%02x\n",
+ pr_debug("read alarm %d, %04d.%02d.%02d %02d:%02d:%02d\n",
alm_en,
- alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
+ 1900 + alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
@@ -204,34 +242,34 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
if (alm_en & S3C2410_RTCALM_SECEN)
alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec);
else
- alm_tm->tm_sec = 0xff;
+ alm_tm->tm_sec = -1;
if (alm_en & S3C2410_RTCALM_MINEN)
alm_tm->tm_min = bcd2bin(alm_tm->tm_min);
else
- alm_tm->tm_min = 0xff;
+ alm_tm->tm_min = -1;
if (alm_en & S3C2410_RTCALM_HOUREN)
alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour);
else
- alm_tm->tm_hour = 0xff;
+ alm_tm->tm_hour = -1;
if (alm_en & S3C2410_RTCALM_DAYEN)
alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday);
else
- alm_tm->tm_mday = 0xff;
+ alm_tm->tm_mday = -1;
if (alm_en & S3C2410_RTCALM_MONEN) {
alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon);
alm_tm->tm_mon -= 1;
} else {
- alm_tm->tm_mon = 0xff;
+ alm_tm->tm_mon = -1;
}
if (alm_en & S3C2410_RTCALM_YEAREN)
alm_tm->tm_year = bcd2bin(alm_tm->tm_year);
else
- alm_tm->tm_year = 0xffff;
+ alm_tm->tm_year = -1;
return 0;
}
@@ -242,10 +280,10 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
void __iomem *base = s3c_rtc_base;
unsigned int alrm_en;
- pr_debug("s3c_rtc_setalarm: %d, %02x/%02x/%02x %02x.%02x.%02x\n",
+ pr_debug("s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n",
alrm->enabled,
- tm->tm_mday & 0xff, tm->tm_mon & 0xff, tm->tm_year & 0xff,
- tm->tm_hour & 0xff, tm->tm_min & 0xff, tm->tm_sec);
+ 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
alrm_en = readb(base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN;
@@ -272,20 +310,22 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
s3c_rtc_setaie(alrm->enabled);
- if (alrm->enabled)
- enable_irq_wake(s3c_rtc_alarmno);
- else
- disable_irq_wake(s3c_rtc_alarmno);
-
return 0;
}
static int s3c_rtc_proc(struct device *dev, struct seq_file *seq)
{
- unsigned int ticnt = readb(s3c_rtc_base + S3C2410_TICNT);
+ unsigned int ticnt;
- seq_printf(seq, "periodic_IRQ\t: %s\n",
- (ticnt & S3C2410_TICNT_ENABLE) ? "yes" : "no" );
+ if (s3c_rtc_cpu_type == TYPE_S3C64XX) {
+ ticnt = readw(s3c_rtc_base + S3C2410_RTCCON);
+ ticnt &= S3C64XX_RTCCON_TICEN;
+ } else {
+ ticnt = readb(s3c_rtc_base + S3C2410_TICNT);
+ ticnt &= S3C2410_TICNT_ENABLE;
+ }
+
+ seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no");
return 0;
}
@@ -339,7 +379,8 @@ static const struct rtc_class_ops s3c_rtcops = {
.set_alarm = s3c_rtc_setalarm,
.irq_set_freq = s3c_rtc_setfreq,
.irq_set_state = s3c_rtc_setpie,
- .proc = s3c_rtc_proc,
+ .proc = s3c_rtc_proc,
+ .alarm_irq_enable = s3c_rtc_setaie,
};
static void s3c_rtc_enable(struct platform_device *pdev, int en)
@@ -351,33 +392,42 @@ static void s3c_rtc_enable(struct platform_device *pdev, int en)
return;
if (!en) {
- tmp = readb(base + S3C2410_RTCCON);
- writeb(tmp & ~S3C2410_RTCCON_RTCEN, base + S3C2410_RTCCON);
-
- tmp = readb(base + S3C2410_TICNT);
- writeb(tmp & ~S3C2410_TICNT_ENABLE, base + S3C2410_TICNT);
+ tmp = readw(base + S3C2410_RTCCON);
+ if (s3c_rtc_cpu_type == TYPE_S3C64XX)
+ tmp &= ~S3C64XX_RTCCON_TICEN;
+ tmp &= ~S3C2410_RTCCON_RTCEN;
+ writew(tmp, base + S3C2410_RTCCON);
+
+ if (s3c_rtc_cpu_type == TYPE_S3C2410) {
+ tmp = readb(base + S3C2410_TICNT);
+ tmp &= ~S3C2410_TICNT_ENABLE;
+ writeb(tmp, base + S3C2410_TICNT);
+ }
} else {
/* re-enable the device, and check it is ok */
- if ((readb(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0){
+ if ((readw(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0) {
dev_info(&pdev->dev, "rtc disabled, re-enabling\n");
- tmp = readb(base + S3C2410_RTCCON);
- writeb(tmp|S3C2410_RTCCON_RTCEN, base+S3C2410_RTCCON);
+ tmp = readw(base + S3C2410_RTCCON);
+ writew(tmp | S3C2410_RTCCON_RTCEN,
+ base + S3C2410_RTCCON);
}
- if ((readb(base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)){
+ if ((readw(base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)) {
dev_info(&pdev->dev, "removing RTCCON_CNTSEL\n");
- tmp = readb(base + S3C2410_RTCCON);
- writeb(tmp& ~S3C2410_RTCCON_CNTSEL, base+S3C2410_RTCCON);
+ tmp = readw(base + S3C2410_RTCCON);
+ writew(tmp & ~S3C2410_RTCCON_CNTSEL,
+ base + S3C2410_RTCCON);
}
- if ((readb(base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)){
+ if ((readw(base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)) {
dev_info(&pdev->dev, "removing RTCCON_CLKRST\n");
- tmp = readb(base + S3C2410_RTCCON);
- writeb(tmp & ~S3C2410_RTCCON_CLKRST, base+S3C2410_RTCCON);
+ tmp = readw(base + S3C2410_RTCCON);
+ writew(tmp & ~S3C2410_RTCCON_CLKRST,
+ base + S3C2410_RTCCON);
}
}
}
@@ -392,6 +442,10 @@ static int __devexit s3c_rtc_remove(struct platform_device *dev)
s3c_rtc_setpie(&dev->dev, 0);
s3c_rtc_setaie(0);
+ clk_disable(rtc_clk);
+ clk_put(rtc_clk);
+ rtc_clk = NULL;
+
iounmap(s3c_rtc_base);
release_resource(s3c_rtc_mem);
kfree(s3c_rtc_mem);
@@ -402,6 +456,7 @@ static int __devexit s3c_rtc_remove(struct platform_device *dev)
static int __devinit s3c_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
+ struct rtc_time rtc_tm;
struct resource *res;
int ret;
@@ -449,14 +504,22 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)
goto err_nomap;
}
+ rtc_clk = clk_get(&pdev->dev, "rtc");
+ if (IS_ERR(rtc_clk)) {
+ dev_err(&pdev->dev, "failed to find rtc clock source\n");
+ ret = PTR_ERR(rtc_clk);
+ rtc_clk = NULL;
+ goto err_clk;
+ }
+
+ clk_enable(rtc_clk);
+
/* check to see if everything is setup correctly */
s3c_rtc_enable(pdev, 1);
- pr_debug("s3c2410_rtc: RTCCON=%02x\n",
- readb(s3c_rtc_base + S3C2410_RTCCON));
-
- s3c_rtc_setfreq(&pdev->dev, 1);
+ pr_debug("s3c2410_rtc: RTCCON=%02x\n",
+ readw(s3c_rtc_base + S3C2410_RTCCON));
device_init_wakeup(&pdev->dev, 1);
@@ -471,13 +534,42 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)
goto err_nortc;
}
- rtc->max_user_freq = 128;
+ s3c_rtc_cpu_type = platform_get_device_id(pdev)->driver_data;
+
+ /* Check RTC Time */
+
+ s3c_rtc_gettime(NULL, &rtc_tm);
+
+ if (rtc_valid_tm(&rtc_tm)) {
+ rtc_tm.tm_year = 100;
+ rtc_tm.tm_mon = 0;
+ rtc_tm.tm_mday = 1;
+ rtc_tm.tm_hour = 0;
+ rtc_tm.tm_min = 0;
+ rtc_tm.tm_sec = 0;
+
+ s3c_rtc_settime(NULL, &rtc_tm);
+
+ dev_warn(&pdev->dev, "warning: invalid RTC value so initializing it\n");
+ }
+
+ if (s3c_rtc_cpu_type == TYPE_S3C64XX)
+ rtc->max_user_freq = 32768;
+ else
+ rtc->max_user_freq = 128;
platform_set_drvdata(pdev, rtc);
+
+ s3c_rtc_setfreq(&pdev->dev, 1);
+
return 0;
err_nortc:
s3c_rtc_enable(pdev, 0);
+ clk_disable(rtc_clk);
+ clk_put(rtc_clk);
+
+ err_clk:
iounmap(s3c_rtc_base);
err_nomap:
@@ -491,20 +583,38 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)
/* RTC Power management control */
-static int ticnt_save;
+static int ticnt_save, ticnt_en_save;
static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state)
{
/* save TICNT for anyone using periodic interrupts */
ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT);
+ if (s3c_rtc_cpu_type == TYPE_S3C64XX) {
+ ticnt_en_save = readw(s3c_rtc_base + S3C2410_RTCCON);
+ ticnt_en_save &= S3C64XX_RTCCON_TICEN;
+ }
s3c_rtc_enable(pdev, 0);
+
+ if (device_may_wakeup(&pdev->dev))
+ enable_irq_wake(s3c_rtc_alarmno);
+
return 0;
}
static int s3c_rtc_resume(struct platform_device *pdev)
{
+ unsigned int tmp;
+
s3c_rtc_enable(pdev, 1);
writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT);
+ if (s3c_rtc_cpu_type == TYPE_S3C64XX && ticnt_en_save) {
+ tmp = readw(s3c_rtc_base + S3C2410_RTCCON);
+ writew(tmp | ticnt_en_save, s3c_rtc_base + S3C2410_RTCCON);
+ }
+
+ if (device_may_wakeup(&pdev->dev))
+ disable_irq_wake(s3c_rtc_alarmno);
+
return 0;
}
#else
@@ -512,13 +622,27 @@ static int s3c_rtc_resume(struct platform_device *pdev)
#define s3c_rtc_resume NULL
#endif
-static struct platform_driver s3c2410_rtc_driver = {
+static struct platform_device_id s3c_rtc_driver_ids[] = {
+ {
+ .name = "s3c2410-rtc",
+ .driver_data = TYPE_S3C2410,
+ }, {
+ .name = "s3c64xx-rtc",
+ .driver_data = TYPE_S3C64XX,
+ },
+ { }
+};
+
+MODULE_DEVICE_TABLE(platform, s3c_rtc_driver_ids);
+
+static struct platform_driver s3c_rtc_driver = {
.probe = s3c_rtc_probe,
.remove = __devexit_p(s3c_rtc_remove),
.suspend = s3c_rtc_suspend,
.resume = s3c_rtc_resume,
+ .id_table = s3c_rtc_driver_ids,
.driver = {
- .name = "s3c2410-rtc",
+ .name = "s3c-rtc",
.owner = THIS_MODULE,
},
};
@@ -528,12 +652,12 @@ static char __initdata banner[] = "S3C24XX RTC, (c) 2004,2006 Simtec Electronics
static int __init s3c_rtc_init(void)
{
printk(banner);
- return platform_driver_register(&s3c2410_rtc_driver);
+ return platform_driver_register(&s3c_rtc_driver);
}
static void __exit s3c_rtc_exit(void)
{
- platform_driver_unregister(&s3c2410_rtc_driver);
+ platform_driver_unregister(&s3c_rtc_driver);
}
module_init(s3c_rtc_init);
diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c
index e95cc6f8d61e..5efbd5990ff8 100644
--- a/drivers/rtc/rtc-sh.c
+++ b/drivers/rtc/rtc-sh.c
@@ -26,6 +26,7 @@
#include <linux/io.h>
#include <linux/log2.h>
#include <linux/clk.h>
+#include <linux/slab.h>
#include <asm/rtc.h>
#define DRV_NAME "sh-rtc"
diff --git a/drivers/rtc/rtc-stk17ta8.c b/drivers/rtc/rtc-stk17ta8.c
index 67700831b5c9..3b943673cd3e 100644
--- a/drivers/rtc/rtc-stk17ta8.c
+++ b/drivers/rtc/rtc-stk17ta8.c
@@ -1,7 +1,7 @@
/*
* A RTC driver for the Simtek STK17TA8
*
- * By Thomas Hommel <thomas.hommel@gefanuc.com>
+ * By Thomas Hommel <thomas.hommel@ge.com>
*
* Based on the DS1553 driver from
* Atsushi Nemoto <anemo@mba.ocn.ne.jp>
@@ -14,6 +14,7 @@
#include <linux/bcd.h>
#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/gfp.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/interrupt.h>
@@ -243,7 +244,7 @@ static const struct rtc_class_ops stk17ta8_rtc_ops = {
.alarm_irq_enable = stk17ta8_rtc_alarm_irq_enable,
};
-static ssize_t stk17ta8_nvram_read(struct kobject *kobj,
+static ssize_t stk17ta8_nvram_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t pos, size_t size)
{
@@ -258,7 +259,7 @@ static ssize_t stk17ta8_nvram_read(struct kobject *kobj,
return count;
}
-static ssize_t stk17ta8_nvram_write(struct kobject *kobj,
+static ssize_t stk17ta8_nvram_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t pos, size_t size)
{
@@ -381,7 +382,7 @@ static __exit void stk17ta8_exit(void)
module_init(stk17ta8_init);
module_exit(stk17ta8_exit);
-MODULE_AUTHOR("Thomas Hommel <thomas.hommel@gefanuc.com>");
+MODULE_AUTHOR("Thomas Hommel <thomas.hommel@ge.com>");
MODULE_DESCRIPTION("Simtek STK17TA8 RTC driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/rtc/rtc-stmp3xxx.c b/drivers/rtc/rtc-stmp3xxx.c
index d7ce1a5c857d..7e7d0c806f2d 100644
--- a/drivers/rtc/rtc-stmp3xxx.c
+++ b/drivers/rtc/rtc-stmp3xxx.c
@@ -22,6 +22,7 @@
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/rtc.h>
+#include <linux/slab.h>
#include <mach/platform.h>
#include <mach/stmp3xxx.h>
diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c
index 7dd23a6fc825..380083ca572f 100644
--- a/drivers/rtc/rtc-sysfs.c
+++ b/drivers/rtc/rtc-sysfs.c
@@ -107,8 +107,9 @@ rtc_sysfs_show_hctosys(struct device *dev, struct device_attribute *attr,
char *buf)
{
#ifdef CONFIG_RTC_HCTOSYS_DEVICE
- if (strcmp(dev_name(&to_rtc_device(dev)->dev),
- CONFIG_RTC_HCTOSYS_DEVICE) == 0)
+ if (rtc_hctosys_ret == 0 &&
+ strcmp(dev_name(&to_rtc_device(dev)->dev),
+ CONFIG_RTC_HCTOSYS_DEVICE) == 0)
return sprintf(buf, "1\n");
else
#endif
diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c
index c6a83a2a722c..ed1b86828124 100644
--- a/drivers/rtc/rtc-twl.c
+++ b/drivers/rtc/rtc-twl.c
@@ -57,7 +57,7 @@ enum {
REG_RTC_COMP_LSB_REG,
REG_RTC_COMP_MSB_REG,
};
-const static u8 twl4030_rtc_reg_map[] = {
+static const u8 twl4030_rtc_reg_map[] = {
[REG_SECONDS_REG] = 0x00,
[REG_MINUTES_REG] = 0x01,
[REG_HOURS_REG] = 0x02,
@@ -80,7 +80,7 @@ const static u8 twl4030_rtc_reg_map[] = {
[REG_RTC_COMP_LSB_REG] = 0x10,
[REG_RTC_COMP_MSB_REG] = 0x11,
};
-const static u8 twl6030_rtc_reg_map[] = {
+static const u8 twl6030_rtc_reg_map[] = {
[REG_SECONDS_REG] = 0x00,
[REG_MINUTES_REG] = 0x01,
[REG_HOURS_REG] = 0x02,
diff --git a/drivers/rtc/rtc-tx4939.c b/drivers/rtc/rtc-tx4939.c
index 9ee81d8aa7c0..ec6313d15359 100644
--- a/drivers/rtc/rtc-tx4939.c
+++ b/drivers/rtc/rtc-tx4939.c
@@ -12,6 +12,7 @@
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/gfp.h>
#include <asm/txx9/tx4939.h>
struct tx4939rtc_plat_data {
@@ -187,7 +188,7 @@ static const struct rtc_class_ops tx4939_rtc_ops = {
.alarm_irq_enable = tx4939_rtc_alarm_irq_enable,
};
-static ssize_t tx4939_rtc_nvram_read(struct kobject *kobj,
+static ssize_t tx4939_rtc_nvram_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t pos, size_t size)
{
@@ -206,7 +207,7 @@ static ssize_t tx4939_rtc_nvram_read(struct kobject *kobj,
return count;
}
-static ssize_t tx4939_rtc_nvram_write(struct kobject *kobj,
+static ssize_t tx4939_rtc_nvram_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t pos, size_t size)
{
diff --git a/drivers/rtc/rtc-v3020.c b/drivers/rtc/rtc-v3020.c
index bed4cab07043..f71c3ce18036 100644
--- a/drivers/rtc/rtc-v3020.c
+++ b/drivers/rtc/rtc-v3020.c
@@ -28,6 +28,7 @@
#include <linux/rtc-v3020.h>
#include <linux/delay.h>
#include <linux/gpio.h>
+#include <linux/slab.h>
#include <linux/io.h>
diff --git a/drivers/rtc/rtc-wm831x.c b/drivers/rtc/rtc-wm831x.c
index 000c7e481e59..82931dc65c0b 100644
--- a/drivers/rtc/rtc-wm831x.c
+++ b/drivers/rtc/rtc-wm831x.c
@@ -16,6 +16,7 @@
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/rtc.h>
+#include <linux/slab.h>
#include <linux/bcd.h>
#include <linux/interrupt.h>
#include <linux/ioctl.h>
@@ -448,17 +449,17 @@ static int wm831x_rtc_probe(struct platform_device *pdev)
goto err;
}
- ret = wm831x_request_irq(wm831x, per_irq, wm831x_per_irq,
- IRQF_TRIGGER_RISING, "wm831x_rtc_per",
- wm831x_rtc);
+ ret = request_threaded_irq(per_irq, NULL, wm831x_per_irq,
+ IRQF_TRIGGER_RISING, "RTC period",
+ wm831x_rtc);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request periodic IRQ %d: %d\n",
per_irq, ret);
}
- ret = wm831x_request_irq(wm831x, alm_irq, wm831x_alm_irq,
- IRQF_TRIGGER_RISING, "wm831x_rtc_alm",
- wm831x_rtc);
+ ret = request_threaded_irq(alm_irq, NULL, wm831x_alm_irq,
+ IRQF_TRIGGER_RISING, "RTC alarm",
+ wm831x_rtc);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n",
alm_irq, ret);
@@ -477,8 +478,8 @@ static int __devexit wm831x_rtc_remove(struct platform_device *pdev)
int per_irq = platform_get_irq_byname(pdev, "PER");
int alm_irq = platform_get_irq_byname(pdev, "ALM");
- wm831x_free_irq(wm831x_rtc->wm831x, alm_irq, wm831x_rtc);
- wm831x_free_irq(wm831x_rtc->wm831x, per_irq, wm831x_rtc);
+ free_irq(alm_irq, wm831x_rtc);
+ free_irq(per_irq, wm831x_rtc);
rtc_device_unregister(wm831x_rtc->rtc);
kfree(wm831x_rtc);
diff --git a/drivers/rtc/rtc-wm8350.c b/drivers/rtc/rtc-wm8350.c
index f1e440521c54..3d0dc76b38af 100644
--- a/drivers/rtc/rtc-wm8350.c
+++ b/drivers/rtc/rtc-wm8350.c
@@ -307,11 +307,18 @@ static int wm8350_rtc_update_irq_enable(struct device *dev,
{
struct wm8350 *wm8350 = dev_get_drvdata(dev);
+ /* Suppress duplicate changes since genirq nests enable and
+ * disable calls. */
+ if (enabled == wm8350->rtc.update_enabled)
+ return 0;
+
if (enabled)
wm8350_unmask_irq(wm8350, WM8350_IRQ_RTC_SEC);
else
wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_SEC);
+ wm8350->rtc.update_enabled = enabled;
+
return 0;
}
@@ -478,8 +485,8 @@ static int __devexit wm8350_rtc_remove(struct platform_device *pdev)
struct wm8350 *wm8350 = platform_get_drvdata(pdev);
struct wm8350_rtc *wm_rtc = &wm8350->rtc;
- wm8350_free_irq(wm8350, WM8350_IRQ_RTC_SEC);
- wm8350_free_irq(wm8350, WM8350_IRQ_RTC_ALM);
+ wm8350_free_irq(wm8350, WM8350_IRQ_RTC_SEC, wm8350);
+ wm8350_free_irq(wm8350, WM8350_IRQ_RTC_ALM, wm8350);
rtc_device_unregister(wm_rtc->rtc);