summaryrefslogtreecommitdiffstats
path: root/drivers/rtc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/Kconfig92
-rw-r--r--drivers/rtc/Makefile8
-rw-r--r--drivers/rtc/class.c4
-rw-r--r--drivers/rtc/rtc-ab3100.c2
-rw-r--r--drivers/rtc/rtc-ab8500.c103
-rw-r--r--drivers/rtc/rtc-bfin.c58
-rw-r--r--drivers/rtc/rtc-cmos.c6
-rw-r--r--drivers/rtc/rtc-ds3232.c505
-rw-r--r--drivers/rtc/rtc-fm3130.c183
-rw-r--r--drivers/rtc/rtc-imxdi.c519
-rw-r--r--drivers/rtc/rtc-isl12022.c327
-rw-r--r--drivers/rtc/rtc-jz4740.c380
-rw-r--r--drivers/rtc/rtc-lpc32xx.c414
-rw-r--r--drivers/rtc/rtc-m41t80.c16
-rw-r--r--drivers/rtc/rtc-m48t59.c5
-rw-r--r--drivers/rtc/rtc-m48t86.c2
-rw-r--r--drivers/rtc/rtc-max6900.c2
-rw-r--r--drivers/rtc/rtc-max8998.c300
-rw-r--r--drivers/rtc/rtc-mc13783.c428
-rw-r--r--drivers/rtc/rtc-mc13xxx.c437
-rw-r--r--drivers/rtc/rtc-mpc5121.c4
-rw-r--r--drivers/rtc/rtc-mxc.c6
-rw-r--r--drivers/rtc/rtc-nuc900.c66
-rw-r--r--drivers/rtc/rtc-omap.c12
-rw-r--r--drivers/rtc/rtc-pcf8563.c8
-rw-r--r--drivers/rtc/rtc-pl031.c5
-rw-r--r--drivers/rtc/rtc-pxa.c42
-rw-r--r--drivers/rtc/rtc-rp5c01.c89
-rw-r--r--drivers/rtc/rtc-rs5c313.c34
-rw-r--r--drivers/rtc/rtc-rx8025.c2
-rw-r--r--drivers/rtc/rtc-s3c.c139
31 files changed, 3482 insertions, 716 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 10ba12c8c5e0..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
@@ -185,6 +196,16 @@ config RTC_DRV_MAX8925
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
@@ -203,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
@@ -537,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
@@ -645,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 || ARCH_S3C64XX
+ depends on ARCH_S3C2410 || ARCH_S3C64XX || HAVE_S3C_RTC
help
RTC (Realtime Clock) driver for the clock inbuilt into the
Samsung S3C24XX series of SoCs. This can provide periodic
@@ -729,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
@@ -745,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
@@ -774,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.
@@ -889,11 +936,12 @@ 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"
@@ -905,4 +953,24 @@ config RTC_DRV_MPC5121
This driver can also be built as a module. If so, the module
will be called rtc-mpc5121.
+config RTC_DRV_JZ4740
+ tristate "Ingenic JZ4740 SoC"
+ depends on RTC_CLASS
+ depends on MACH_JZ4740
+ help
+ If you say yes here you get support for the Ingenic JZ4740 SoC RTC
+ controller.
+
+ This driver can also be buillt as a module. If so, the module
+ will be called rtc-jz4740.
+
+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 5adbba7cf89c..4c2832df4697 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -41,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
@@ -55,8 +60,9 @@ 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
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
index 565562ba6ac9..e6539cbabb35 100644
--- a/drivers/rtc/class.c
+++ b/drivers/rtc/class.c
@@ -158,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);
diff --git a/drivers/rtc/rtc-ab3100.c b/drivers/rtc/rtc-ab3100.c
index d26780ea254b..261a07e0fb24 100644
--- a/drivers/rtc/rtc-ab3100.c
+++ b/drivers/rtc/rtc-ab3100.c
@@ -235,6 +235,7 @@ static int __init ab3100_rtc_probe(struct platform_device *pdev)
err = PTR_ERR(rtc);
return err;
}
+ platform_set_drvdata(pdev, rtc);
return 0;
}
@@ -244,6 +245,7 @@ static int __exit ab3100_rtc_remove(struct platform_device *pdev)
struct rtc_device *rtc = platform_get_drvdata(pdev);
rtc_device_unregister(rtc);
+ platform_set_drvdata(pdev, NULL);
return 0;
}
diff --git a/drivers/rtc/rtc-ab8500.c b/drivers/rtc/rtc-ab8500.c
index 2fda03125e55..e346705aae92 100644
--- a/drivers/rtc/rtc-ab8500.c
+++ b/drivers/rtc/rtc-ab8500.c
@@ -14,26 +14,26 @@
#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 0x0F00
-#define AB8500_RTC_CC_CONF_REG 0x0F01
-#define AB8500_RTC_READ_REQ_REG 0x0F02
-#define AB8500_RTC_WATCH_TSECMID_REG 0x0F03
-#define AB8500_RTC_WATCH_TSECHI_REG 0x0F04
-#define AB8500_RTC_WATCH_TMIN_LOW_REG 0x0F05
-#define AB8500_RTC_WATCH_TMIN_MID_REG 0x0F06
-#define AB8500_RTC_WATCH_TMIN_HI_REG 0x0F07
-#define AB8500_RTC_ALRM_MIN_LOW_REG 0x0F08
-#define AB8500_RTC_ALRM_MIN_MID_REG 0x0F09
-#define AB8500_RTC_ALRM_MIN_HI_REG 0x0F0A
-#define AB8500_RTC_STAT_REG 0x0F0B
-#define AB8500_RTC_BKUP_CHG_REG 0x0F0C
-#define AB8500_RTC_FORCE_BKUP_REG 0x0F0D
-#define AB8500_RTC_CALIB_REG 0x0F0E
-#define AB8500_RTC_SWITCH_STAT_REG 0x0F0F
-#define AB8500_REV_REG 0x1080
+#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
@@ -46,13 +46,13 @@
#define COUNTS_PER_SEC (0xF000 / 60)
#define AB8500_RTC_EPOCH 2000
-static const unsigned long ab8500_rtc_time_regs[] = {
+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 unsigned long ab8500_rtc_alarm_regs[] = {
+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
};
@@ -76,29 +76,30 @@ static unsigned long get_elapsed_seconds(int year)
static int ab8500_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
- struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
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 = ab8500_write(ab8500, AB8500_RTC_READ_REQ_REG,
- RTC_READ_REQUEST);
+ 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 (ab8500->revision == 0) {
+ 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 = ab8500_read(ab8500, AB8500_RTC_READ_REQ_REG);
+ retval = abx500_get_register_interruptible(dev,
+ AB8500_RTC, AB8500_RTC_READ_REQ_REG, &value);
if (retval < 0)
return retval;
- if (!(retval & RTC_READ_REQUEST))
+ if (!(value & RTC_READ_REQUEST))
break;
msleep(1);
@@ -107,10 +108,11 @@ static int ab8500_rtc_read_time(struct device *dev, struct rtc_time *tm)
/* Read the Watchtime registers */
for (i = 0; i < ARRAY_SIZE(ab8500_rtc_time_regs); i++) {
- retval = ab8500_read(ab8500, 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] = retval;
+ buf[i] = value;
}
mins = (buf[0] << 16) | (buf[1] << 8) | buf[2];
@@ -128,7 +130,6 @@ static int ab8500_rtc_read_time(struct device *dev, struct rtc_time *tm)
static int ab8500_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
- struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
int retval, i;
unsigned char buf[ARRAY_SIZE(ab8500_rtc_time_regs)];
unsigned long no_secs, no_mins, secs = 0;
@@ -162,27 +163,29 @@ static int ab8500_rtc_set_time(struct device *dev, struct rtc_time *tm)
buf[0] = (no_mins >> 16) & 0xFF;
for (i = 0; i < ARRAY_SIZE(ab8500_rtc_time_regs); i++) {
- retval = ab8500_write(ab8500, ab8500_rtc_time_regs[i], buf[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 ab8500_write(ab8500, AB8500_RTC_READ_REQ_REG, RTC_WRITE_REQUEST);
+ 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)
{
- struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
int retval, i;
- int rtc_ctrl;
+ 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 */
- rtc_ctrl = ab8500_read(ab8500, AB8500_RTC_STAT_REG);
- if (rtc_ctrl < 0)
- return rtc_ctrl;
+ 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;
@@ -192,10 +195,11 @@ static int ab8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
alarm->pending = 0;
for (i = 0; i < ARRAY_SIZE(ab8500_rtc_alarm_regs); i++) {
- retval = ab8500_read(ab8500, 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] = retval;
+ buf[i] = value;
}
mins = (buf[0] << 16) | (buf[1] << 8) | (buf[2]);
@@ -211,15 +215,13 @@ static int ab8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
static int ab8500_rtc_irq_enable(struct device *dev, unsigned int enabled)
{
- struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
-
- return ab8500_set_bits(ab8500, AB8500_RTC_STAT_REG, RTC_ALARM_ENA,
- enabled ? RTC_ALARM_ENA : 0);
+ 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)
{
- struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
int retval, i;
unsigned char buf[ARRAY_SIZE(ab8500_rtc_alarm_regs)];
unsigned long mins, secs = 0;
@@ -247,7 +249,8 @@ static int ab8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
/* Set the alarm time */
for (i = 0; i < ARRAY_SIZE(ab8500_rtc_alarm_regs); i++) {
- retval = ab8500_write(ab8500, ab8500_rtc_alarm_regs[i], buf[i]);
+ retval = abx500_set_register_interruptible(dev, AB8500_RTC,
+ ab8500_rtc_alarm_regs[i], buf[i]);
if (retval < 0)
return retval;
}
@@ -276,10 +279,9 @@ static const struct rtc_class_ops ab8500_rtc_ops = {
static int __devinit ab8500_rtc_probe(struct platform_device *pdev)
{
- struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
int err;
struct rtc_device *rtc;
- int rtc_ctrl;
+ u8 rtc_ctrl;
int irq;
irq = platform_get_irq_byname(pdev, "ALARM");
@@ -287,17 +289,18 @@ static int __devinit ab8500_rtc_probe(struct platform_device *pdev)
return irq;
/* For RTC supply test */
- err = ab8500_set_bits(ab8500, AB8500_RTC_STAT_REG, RTC_STATUS_DATA,
- RTC_STATUS_DATA);
+ 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);
- rtc_ctrl = ab8500_read(ab8500, AB8500_RTC_STAT_REG);
- if (rtc_ctrl < 0)
- return rtc_ctrl;
+ 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)) {
diff --git a/drivers/rtc/rtc-bfin.c b/drivers/rtc/rtc-bfin.c
index 72b2bcc2c224..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/
*
@@ -183,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;
}
}
@@ -213,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;
}
@@ -422,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-cmos.c b/drivers/rtc/rtc-cmos.c
index 11b8ea29d2b7..5856167a0c90 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -970,7 +970,6 @@ static inline int cmos_poweroff(struct device *dev)
#include <linux/acpi.h>
-#ifdef CONFIG_PM
static u32 rtc_handler(void *context)
{
acpi_clear_event(ACPI_EVENT_RTC);
@@ -999,11 +998,6 @@ static void rtc_wake_off(struct device *dev)
{
acpi_disable_event(ACPI_EVENT_RTC, 0);
}
-#else
-#define rtc_wake_setup() do{}while(0)
-#define rtc_wake_on NULL
-#define rtc_wake_off NULL
-#endif
/* Every ACPI platform has a mc146818 compatible "cmos rtc". Here we find
* its device node and pass extra config data. This helps its driver use
diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c
new file mode 100644
index 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-fm3130.c b/drivers/rtc/rtc-fm3130.c
index ff6fce61ea41..4cf2e70c5078 100644
--- a/drivers/rtc/rtc-fm3130.c
+++ b/drivers/rtc/rtc-fm3130.c
@@ -52,8 +52,8 @@ struct fm3130 {
struct i2c_msg msg[4];
struct i2c_client *client;
struct rtc_device *rtc;
+ int alarm_valid;
int data_valid;
- int alarm;
};
static const struct i2c_device_id fm3130_id[] = {
{ "fm3130", 0 },
@@ -87,11 +87,7 @@ static void fm3130_rtc_mode(struct device *dev, int mode)
dev_dbg(dev, "invalid mode %d\n", mode);
break;
}
- /* Checking for alarm */
- if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AF) {
- fm3130->alarm = 1;
- fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF;
- }
+
i2c_smbus_write_byte_data(fm3130->client,
FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL]);
}
@@ -104,7 +100,7 @@ static int fm3130_get_time(struct device *dev, struct rtc_time *t)
if (!fm3130->data_valid) {
/* We have invalid data in RTC, probably due
to battery faults or other problems. Return EIO
- for now, it will allow us to set data later insted
+ for now, it will allow us to set data later instead
of error during probing which disables device */
return -EIO;
}
@@ -208,6 +204,17 @@ static int fm3130_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
struct fm3130 *fm3130 = dev_get_drvdata(dev);
int tmp;
struct rtc_time *tm = &alrm->time;
+
+ if (!fm3130->alarm_valid) {
+ /*
+ * We have invalid alarm in RTC, probably due to battery faults
+ * or other problems. Return EIO for now, it will allow us to
+ * set alarm value later instead of error during probing which
+ * disables device
+ */
+ return -EIO;
+ }
+
/* read the RTC alarm registers all at once */
tmp = i2c_transfer(to_i2c_adapter(fm3130->client->dev.parent),
&fm3130->msg[2], 2);
@@ -222,20 +229,31 @@ static int fm3130_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
fm3130->regs[FM3130_ALARM_DATE],
fm3130->regs[FM3130_ALARM_MONTHS]);
-
tm->tm_sec = bcd2bin(fm3130->regs[FM3130_ALARM_SECONDS] & 0x7F);
tm->tm_min = bcd2bin(fm3130->regs[FM3130_ALARM_MINUTES] & 0x7F);
tm->tm_hour = bcd2bin(fm3130->regs[FM3130_ALARM_HOURS] & 0x3F);
tm->tm_mday = bcd2bin(fm3130->regs[FM3130_ALARM_DATE] & 0x3F);
tm->tm_mon = bcd2bin(fm3130->regs[FM3130_ALARM_MONTHS] & 0x1F);
+
if (tm->tm_mon > 0)
tm->tm_mon -= 1; /* RTC is 1-12, tm_mon is 0-11 */
+
dev_dbg(dev, "%s secs=%d, mins=%d, "
"hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
"read alarm", tm->tm_sec, tm->tm_min,
tm->tm_hour, tm->tm_mday,
tm->tm_mon, tm->tm_year, tm->tm_wday);
+ /* check if alarm enabled */
+ fm3130->regs[FM3130_RTC_CONTROL] =
+ i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL);
+
+ if ((fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AEN) &&
+ (~fm3130->regs[FM3130_RTC_CONTROL] &
+ FM3130_RTC_CONTROL_BIT_CAL)) {
+ alrm->enabled = 1;
+ }
+
return 0;
}
@@ -251,25 +269,20 @@ static int fm3130_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
tm->tm_hour, tm->tm_mday,
tm->tm_mon, tm->tm_year, tm->tm_wday);
- if (tm->tm_sec != -1)
- fm3130->regs[FM3130_ALARM_SECONDS] =
- bin2bcd(tm->tm_sec) | 0x80;
+ fm3130->regs[FM3130_ALARM_SECONDS] =
+ (tm->tm_sec != -1) ? bin2bcd(tm->tm_sec) : 0x80;
- if (tm->tm_min != -1)
- fm3130->regs[FM3130_ALARM_MINUTES] =
- bin2bcd(tm->tm_min) | 0x80;
+ fm3130->regs[FM3130_ALARM_MINUTES] =
+ (tm->tm_min != -1) ? bin2bcd(tm->tm_min) : 0x80;
- if (tm->tm_hour != -1)
- fm3130->regs[FM3130_ALARM_HOURS] =
- bin2bcd(tm->tm_hour) | 0x80;
+ fm3130->regs[FM3130_ALARM_HOURS] =
+ (tm->tm_hour != -1) ? bin2bcd(tm->tm_hour) : 0x80;
- if (tm->tm_mday != -1)
- fm3130->regs[FM3130_ALARM_DATE] =
- bin2bcd(tm->tm_mday) | 0x80;
+ fm3130->regs[FM3130_ALARM_DATE] =
+ (tm->tm_mday != -1) ? bin2bcd(tm->tm_mday) : 0x80;
- if (tm->tm_mon != -1)
- fm3130->regs[FM3130_ALARM_MONTHS] =
- bin2bcd(tm->tm_mon + 1) | 0x80;
+ fm3130->regs[FM3130_ALARM_MONTHS] =
+ (tm->tm_mon != -1) ? bin2bcd(tm->tm_mon + 1) : 0x80;
dev_dbg(dev, "alarm write %02x %02x %02x %02x %02x\n",
fm3130->regs[FM3130_ALARM_SECONDS],
@@ -285,11 +298,8 @@ static int fm3130_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
}
fm3130->regs[FM3130_RTC_CONTROL] =
i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL);
- /* Checking for alarm */
- if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AF) {
- fm3130->alarm = 1;
- fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF;
- }
+
+ /* enable or disable alarm */
if (alrm->enabled) {
i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL,
(fm3130->regs[FM3130_RTC_CONTROL] &
@@ -298,16 +308,55 @@ static int fm3130_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
} else {
i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL,
fm3130->regs[FM3130_RTC_CONTROL] &
- ~(FM3130_RTC_CONTROL_BIT_AEN));
+ ~(FM3130_RTC_CONTROL_BIT_CAL) &
+ ~(FM3130_RTC_CONTROL_BIT_AEN));
}
+
+ /* We assume here that data is valid once written */
+ if (!fm3130->alarm_valid)
+ fm3130->alarm_valid = 1;
+
return 0;
}
+static int fm3130_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct fm3130 *fm3130 = dev_get_drvdata(dev);
+ int ret = 0;
+
+ fm3130->regs[FM3130_RTC_CONTROL] =
+ i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL);
+
+ dev_dbg(dev, "alarm_irq_enable: enable=%d, FM3130_RTC_CONTROL=%02x\n",
+ enabled, fm3130->regs[FM3130_RTC_CONTROL]);
+
+ switch (enabled) {
+ case 0: /* alarm off */
+ ret = i2c_smbus_write_byte_data(fm3130->client,
+ FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL] &
+ ~(FM3130_RTC_CONTROL_BIT_CAL) &
+ ~(FM3130_RTC_CONTROL_BIT_AEN));
+ break;
+ case 1: /* alarm on */
+ ret = i2c_smbus_write_byte_data(fm3130->client,
+ FM3130_RTC_CONTROL, (fm3130->regs[FM3130_RTC_CONTROL] &
+ ~(FM3130_RTC_CONTROL_BIT_CAL)) |
+ FM3130_RTC_CONTROL_BIT_AEN);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
static const struct rtc_class_ops fm3130_rtc_ops = {
.read_time = fm3130_get_time,
.set_time = fm3130_set_time,
.read_alarm = fm3130_read_alarm,
.set_alarm = fm3130_set_alarm,
+ .alarm_irq_enable = fm3130_alarm_irq_enable,
};
static struct i2c_driver fm3130_driver;
@@ -356,6 +405,7 @@ static int __devinit fm3130_probe(struct i2c_client *client,
fm3130->msg[3].len = FM3130_ALARM_REGS;
fm3130->msg[3].buf = &fm3130->regs[FM3130_ALARM_SECONDS];
+ fm3130->alarm_valid = 0;
fm3130->data_valid = 0;
tmp = i2c_transfer(adapter, fm3130->msg, 4);
@@ -370,12 +420,6 @@ static int __devinit fm3130_probe(struct i2c_client *client,
fm3130->regs[FM3130_CAL_CONTROL] =
i2c_smbus_read_byte_data(client, FM3130_CAL_CONTROL);
- /* Checking for alarm */
- if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AF) {
- fm3130->alarm = 1;
- fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF;
- }
-
/* Disabling calibration mode */
if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_CAL) {
i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL,
@@ -400,44 +444,79 @@ static int __devinit fm3130_probe(struct i2c_client *client,
fm3130->regs[FM3130_CAL_CONTROL] &
~(FM3130_CAL_CONTROL_BIT_nOSCEN));
- /* oscillator fault? clear flag, and warn */
- if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_LB)
+ /* low battery? clear flag, and warn */
+ if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_LB) {
+ i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL,
+ fm3130->regs[FM3130_RTC_CONTROL] &
+ ~(FM3130_RTC_CONTROL_BIT_LB));
dev_warn(&client->dev, "Low battery!\n");
+ }
- /* oscillator fault? clear flag, and warn */
+ /* check if Power On Reset bit is set */
if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_POR) {
i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL,
fm3130->regs[FM3130_RTC_CONTROL] &
~FM3130_RTC_CONTROL_BIT_POR);
- dev_warn(&client->dev, "SET TIME!\n");
+ dev_dbg(&client->dev, "POR bit is set\n");
}
/* ACS is controlled by alarm */
i2c_smbus_write_byte_data(client, FM3130_ALARM_WP_CONTROL, 0x80);
- /* TODO */
- /* TODO need to sanity check alarm */
- tmp = fm3130->regs[FM3130_RTC_SECONDS];
- tmp = bcd2bin(tmp & 0x7f);
- if (tmp > 60)
- goto exit_bad;
+ /* alarm registers sanity check */
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f);
+ if (tmp > 59)
+ goto bad_alarm;
+
tmp = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f);
- if (tmp > 60)
- goto exit_bad;
+ if (tmp > 59)
+ goto bad_alarm;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_HOURS] & 0x3f);
+ if (tmp > 23)
+ goto bad_alarm;
tmp = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f);
if (tmp == 0 || tmp > 31)
- goto exit_bad;
+ goto bad_alarm;
tmp = bcd2bin(fm3130->regs[FM3130_RTC_MONTHS] & 0x1f);
if (tmp == 0 || tmp > 12)
- goto exit_bad;
+ goto bad_alarm;
- tmp = fm3130->regs[FM3130_RTC_HOURS];
+ fm3130->alarm_valid = 1;
+
+bad_alarm:
+
+ /* clock registers sanity chek */
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f);
+ if (tmp > 59)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f);
+ if (tmp > 59)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_HOURS] & 0x3f);
+ if (tmp > 23)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_DAY] & 0x7);
+ if (tmp == 0 || tmp > 7)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f);
+ if (tmp == 0 || tmp > 31)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_MONTHS] & 0x1f);
+ if (tmp == 0 || tmp > 12)
+ goto bad_clock;
fm3130->data_valid = 1;
-exit_bad:
- if (!fm3130->data_valid)
+bad_clock:
+
+ if (!fm3130->data_valid || !fm3130->alarm_valid)
dev_dbg(&client->dev,
"%s: %02x %02x %02x %02x %02x %02x %02x %02x"
"%02x %02x %02x %02x %02x %02x %02x\n",
diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c
new file mode 100644
index 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-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 6dc4e6241418..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. */
@@ -677,9 +678,9 @@ static long wdt_unlocked_ioctl(struct file *file, unsigned int cmd,
{
int ret;
- lock_kernel();
+ mutex_lock(&m41t80_rtc_mutex);
ret = wdt_ioctl(file, cmd, arg);
- unlock_kernel();
+ mutex_unlock(&m41t80_rtc_mutex);
return ret;
}
@@ -693,16 +694,16 @@ static long wdt_unlocked_ioctl(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();
+ mutex_unlock(&m41t80_rtc_mutex);
return nonseekable_open(inode, file);
}
return -ENODEV;
@@ -748,6 +749,7 @@ static const struct file_operations wdt_fops = {
.write = wdt_write,
.open = wdt_open,
.release = wdt_release,
+ .llseek = no_llseek,
};
static struct miscdevice wdt_dev = {
diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c
index be8359fdb65a..a99a0b554eb8 100644
--- a/drivers/rtc/rtc-m48t59.c
+++ b/drivers/rtc/rtc-m48t59.c
@@ -105,7 +105,7 @@ static int m48t59_rtc_read_time(struct device *dev, struct rtc_time *tm)
dev_dbg(dev, "RTC read time %04d-%02d-%02d %02d/%02d/%02d\n",
tm->tm_year + 1900, tm->tm_mon, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
- return 0;
+ return rtc_valid_tm(tm);
}
static int m48t59_rtc_set_time(struct device *dev, struct rtc_time *tm)
@@ -196,7 +196,7 @@ static int m48t59_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
dev_dbg(dev, "RTC read alarm time %04d-%02d-%02d %02d/%02d/%02d\n",
tm->tm_year + 1900, tm->tm_mon, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
- return 0;
+ return rtc_valid_tm(tm);
}
/*
@@ -506,7 +506,6 @@ out:
free_irq(m48t59->irq, &pdev->dev);
if (m48t59->ioaddr)
iounmap(m48t59->ioaddr);
- if (m48t59)
kfree(m48t59);
return ret;
}
diff --git a/drivers/rtc/rtc-m48t86.c b/drivers/rtc/rtc-m48t86.c
index 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-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 675bfb515367..000000000000
--- a/drivers/rtc/rtc-mc13783.c
+++ /dev/null
@@ -1,428 +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/slab.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_irq_enable_unlocked(struct device *dev,
- unsigned int enabled, int irq)
-{
- struct mc13783_rtc *priv = dev_get_drvdata(dev);
- int (*func)(struct mc13783 *mc13783, int irq);
-
- if (!priv->valid)
- return -ENODATA;
-
- func = enabled ? mc13783_irq_unmask : mc13783_irq_mask;
- return func(priv->mc13783, irq);
-}
-
-static int mc13783_rtc_irq_enable(struct device *dev,
- unsigned int enabled, int irq)
-{
- struct mc13783_rtc *priv = dev_get_drvdata(dev);
- int ret;
-
- mc13783_lock(priv->mc13783);
-
- ret = mc13783_rtc_irq_enable_unlocked(dev, enabled, irq);
-
- mc13783_unlock(priv->mc13783);
-
- return ret;
-}
-
-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;
- unsigned int alarmseconds;
- int ret;
-
- seconds = secs % 86400;
- days = secs / 86400;
-
- mc13783_lock(priv->mc13783);
-
- /*
- * temporarily invalidate alarm to prevent triggering it when the day is
- * already updated while the time isn't yet.
- */
- ret = mc13783_reg_read(priv->mc13783, MC13783_RTCTODA, &alarmseconds);
- if (unlikely(ret))
- goto out;
-
- if (alarmseconds < 86400) {
- ret = mc13783_reg_write(priv->mc13783,
- MC13783_RTCTODA, 0x1ffff);
- if (unlikely(ret))
- goto out;
- }
-
- /*
- * 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;
-
- /* restore alarm */
- if (alarmseconds < 86400) {
- ret = mc13783_reg_write(priv->mc13783,
- MC13783_RTCTODA, alarmseconds);
- if (unlikely(ret))
- goto out;
- }
-
- ret = mc13783_irq_ack(priv->mc13783, MC13783_IRQ_RTCRST);
- if (unlikely(ret))
- goto out;
-
- ret = mc13783_irq_unmask(priv->mc13783, MC13783_IRQ_RTCRST);
-out:
- priv->valid = !ret;
-
- mc13783_unlock(priv->mc13783);
-
- return ret;
-}
-
-static int mc13783_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
-{
- struct mc13783_rtc *priv = dev_get_drvdata(dev);
- unsigned seconds, days;
- unsigned long s1970;
- int enabled, pending;
- int ret;
-
- mc13783_lock(priv->mc13783);
-
- ret = mc13783_reg_read(priv->mc13783, MC13783_RTCTODA, &seconds);
- if (unlikely(ret))
- goto out;
- if (seconds >= 86400) {
- ret = -ENODATA;
- goto out;
- }
-
- ret = mc13783_reg_read(priv->mc13783, MC13783_RTCDAY, &days);
- if (unlikely(ret))
- goto out;
-
- ret = mc13783_irq_status(priv->mc13783, MC13783_IRQ_TODA,
- &enabled, &pending);
-
-out:
- mc13783_unlock(priv->mc13783);
-
- 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 mc13783_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
-{
- struct mc13783_rtc *priv = dev_get_drvdata(dev);
- unsigned long s1970;
- unsigned seconds, days;
- int ret;
-
- mc13783_lock(priv->mc13783);
-
- /* disable alarm to prevent false triggering */
- ret = mc13783_reg_write(priv->mc13783, MC13783_RTCTODA, 0x1ffff);
- if (unlikely(ret))
- goto out;
-
- ret = mc13783_irq_ack(priv->mc13783, MC13783_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 = mc13783_rtc_irq_enable_unlocked(dev, alarm->enabled,
- MC13783_IRQ_TODA);
- if (unlikely(ret))
- goto out;
-
- seconds = s1970 % 86400;
- days = s1970 / 86400;
-
- ret = mc13783_reg_write(priv->mc13783, MC13783_RTCDAYA, days);
- if (unlikely(ret))
- goto out;
-
- ret = mc13783_reg_write(priv->mc13783, MC13783_RTCTODA, seconds);
-
-out:
- mc13783_unlock(priv->mc13783);
-
- return ret;
-}
-
-static irqreturn_t mc13783_rtc_alarm_handler(int irq, void *dev)
-{
- struct mc13783_rtc *priv = dev;
- struct mc13783 *mc13783 = priv->mc13783;
-
- dev_dbg(&priv->rtc->dev, "Alarm\n");
-
- rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_AF);
-
- mc13783_irq_ack(mc13783, irq);
-
- return IRQ_HANDLED;
-}
-
-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_irq_ack(mc13783, irq);
-
- return IRQ_HANDLED;
-}
-
-static int mc13783_rtc_update_irq_enable(struct device *dev,
- unsigned int enabled)
-{
- return mc13783_rtc_irq_enable(dev, enabled, MC13783_IRQ_1HZ);
-}
-
-static int mc13783_rtc_alarm_irq_enable(struct device *dev,
- unsigned int enabled)
-{
- return mc13783_rtc_irq_enable(dev, enabled, MC13783_IRQ_TODA);
-}
-
-static const struct rtc_class_ops mc13783_rtc_ops = {
- .read_time = mc13783_rtc_read_time,
- .set_mmss = mc13783_rtc_set_mmss,
- .read_alarm = mc13783_rtc_read_alarm,
- .set_alarm = mc13783_rtc_set_alarm,
- .alarm_irq_enable = mc13783_rtc_alarm_irq_enable,
- .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_irq_mask(mc13783, irq);
-
- return IRQ_HANDLED;
-}
-
-static int __devinit mc13783_rtc_probe(struct platform_device *pdev)
-{
- int ret;
- struct mc13783_rtc *priv;
- struct mc13783 *mc13783;
- int rtcrst_pending;
-
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- mc13783 = dev_get_drvdata(pdev->dev.parent);
- priv->mc13783 = mc13783;
-
- platform_set_drvdata(pdev, priv);
-
- mc13783_lock(mc13783);
-
- ret = mc13783_irq_request(mc13783, MC13783_IRQ_RTCRST,
- mc13783_rtc_reset_handler, DRIVER_NAME, priv);
- if (ret)
- goto err_reset_irq_request;
-
- ret = mc13783_irq_status(mc13783, MC13783_IRQ_RTCRST,
- NULL, &rtcrst_pending);
- if (ret)
- goto err_reset_irq_status;
-
- priv->valid = !rtcrst_pending;
-
- ret = mc13783_irq_request_nounmask(mc13783, MC13783_IRQ_1HZ,
- mc13783_rtc_update_handler, DRIVER_NAME, priv);
- if (ret)
- goto err_update_irq_request;
-
- ret = mc13783_irq_request_nounmask(mc13783, MC13783_IRQ_TODA,
- mc13783_rtc_alarm_handler, DRIVER_NAME, priv);
- if (ret)
- goto err_alarm_irq_request;
-
- 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_irq_free(mc13783, MC13783_IRQ_TODA, priv);
-err_alarm_irq_request:
-
- mc13783_irq_free(mc13783, MC13783_IRQ_1HZ, priv);
-err_update_irq_request:
-
-err_reset_irq_status:
-
- mc13783_irq_free(mc13783, MC13783_IRQ_RTCRST, priv);
-err_reset_irq_request:
-
- platform_set_drvdata(pdev, NULL);
- kfree(priv);
- }
-
- mc13783_unlock(mc13783);
-
- return ret;
-}
-
-static int __devexit mc13783_rtc_remove(struct platform_device *pdev)
-{
- struct mc13783_rtc *priv = platform_get_drvdata(pdev);
-
- mc13783_lock(priv->mc13783);
-
- rtc_device_unregister(priv->rtc);
-
- mc13783_irq_free(priv->mc13783, MC13783_IRQ_TODA, priv);
- 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
index db5d8c416d26..dfcdf0901d21 100644
--- a/drivers/rtc/rtc-mpc5121.c
+++ b/drivers/rtc/rtc-mpc5121.c
@@ -268,7 +268,7 @@ static const struct rtc_class_ops mpc5121_rtc_ops = {
.update_irq_enable = mpc5121_rtc_update_irq_enable,
};
-static int __devinit mpc5121_rtc_probe(struct of_device *op,
+static int __devinit mpc5121_rtc_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct mpc5121_rtc_data *rtc;
@@ -338,7 +338,7 @@ out_free:
return err;
}
-static int __devexit mpc5121_rtc_remove(struct of_device *op)
+static int __devexit mpc5121_rtc_remove(struct platform_device *op)
{
struct mpc5121_rtc_data *rtc = dev_get_drvdata(&op->dev);
struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c
index 25ec921db07c..0b06c1e03fd5 100644
--- a/drivers/rtc/rtc-mxc.c
+++ b/drivers/rtc/rtc-mxc.c
@@ -83,12 +83,6 @@ struct rtc_plat_data {
void __iomem *ioaddr;
int irq;
struct clk *clk;
- unsigned int irqen;
- int alrm_sec;
- int alrm_min;
- int alrm_hour;
- int alrm_mday;
- struct timespec mxc_rtc_delta;
struct rtc_time g_rtc_alarm;
};
diff --git a/drivers/rtc/rtc-nuc900.c b/drivers/rtc/rtc-nuc900.c
index a351bd5d8176..ddb0857e15a4 100644
--- a/drivers/rtc/rtc-nuc900.c
+++ b/drivers/rtc/rtc-nuc900.c
@@ -85,25 +85,24 @@ static irqreturn_t nuc900_rtc_interrupt(int irq, void *_rtc)
static int *check_rtc_access_enable(struct nuc900_rtc *nuc900_rtc)
{
- unsigned int i;
+ unsigned int timeout = 0x1000;
__raw_writel(INIRRESET, nuc900_rtc->rtc_reg + REG_RTC_INIR);
mdelay(10);
__raw_writel(AERPOWERON, nuc900_rtc->rtc_reg + REG_RTC_AER);
- for (i = 0; i < 1000; i++) {
- if (__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB)
- return 0;
- }
+ while (!(__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB)
+ && timeout--)
+ mdelay(1);
- if ((__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB) == 0x0)
- return ERR_PTR(-ENODEV);
+ if (!timeout)
+ return ERR_PTR(-EPERM);
- return ERR_PTR(-EPERM);
+ return 0;
}
-static void nuc900_rtc_bcd2bin(unsigned int timereg,
+static int nuc900_rtc_bcd2bin(unsigned int timereg,
unsigned int calreg, struct rtc_time *tm)
{
tm->tm_mday = bcd2bin(calreg >> 0);
@@ -114,15 +113,21 @@ static void nuc900_rtc_bcd2bin(unsigned int timereg,
tm->tm_min = bcd2bin(timereg >> 8);
tm->tm_hour = bcd2bin(timereg >> 16);
- rtc_valid_tm(tm);
+ return rtc_valid_tm(tm);
}
-static void nuc900_rtc_bin2bcd(struct rtc_time *settm,
+static void nuc900_rtc_bin2bcd(struct device *dev, struct rtc_time *settm,
struct nuc900_bcd_time *gettm)
{
gettm->bcd_mday = bin2bcd(settm->tm_mday) << 0;
gettm->bcd_mon = bin2bcd(settm->tm_mon) << 8;
- gettm->bcd_year = bin2bcd(settm->tm_year - 100) << 16;
+
+ if (settm->tm_year < 100) {
+ dev_warn(dev, "The year will be between 1970-1999, right?\n");
+ gettm->bcd_year = bin2bcd(settm->tm_year) << 16;
+ } else {
+ gettm->bcd_year = bin2bcd(settm->tm_year - 100) << 16;
+ }
gettm->bcd_sec = bin2bcd(settm->tm_sec) << 0;
gettm->bcd_min = bin2bcd(settm->tm_min) << 8;
@@ -165,9 +170,7 @@ static int nuc900_rtc_read_time(struct device *dev, struct rtc_time *tm)
timeval = __raw_readl(rtc->rtc_reg + REG_RTC_TLR);
clrval = __raw_readl(rtc->rtc_reg + REG_RTC_CLR);
- nuc900_rtc_bcd2bin(timeval, clrval, tm);
-
- return 0;
+ return nuc900_rtc_bcd2bin(timeval, clrval, tm);
}
static int nuc900_rtc_set_time(struct device *dev, struct rtc_time *tm)
@@ -177,7 +180,7 @@ static int nuc900_rtc_set_time(struct device *dev, struct rtc_time *tm)
unsigned long val;
int *err;
- nuc900_rtc_bin2bcd(tm, &gettm);
+ nuc900_rtc_bin2bcd(dev, tm, &gettm);
err = check_rtc_access_enable(rtc);
if (IS_ERR(err))
@@ -200,9 +203,7 @@ static int nuc900_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
timeval = __raw_readl(rtc->rtc_reg + REG_RTC_TAR);
carval = __raw_readl(rtc->rtc_reg + REG_RTC_CAR);
- nuc900_rtc_bcd2bin(timeval, carval, &alrm->time);
-
- return 0;
+ return nuc900_rtc_bcd2bin(timeval, carval, &alrm->time);
}
static int nuc900_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
@@ -212,7 +213,7 @@ static int nuc900_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
unsigned long val;
int *err;
- nuc900_rtc_bin2bcd(&alrm->time, &tm);
+ nuc900_rtc_bin2bcd(dev, &alrm->time, &tm);
err = check_rtc_access_enable(rtc);
if (IS_ERR(err))
@@ -268,29 +269,30 @@ static int __devinit nuc900_rtc_probe(struct platform_device *pdev)
goto fail2;
}
- nuc900_rtc->irq_num = platform_get_irq(pdev, 0);
- if (request_irq(nuc900_rtc->irq_num, nuc900_rtc_interrupt,
- IRQF_DISABLED, "nuc900rtc", nuc900_rtc)) {
- dev_err(&pdev->dev, "NUC900 RTC request irq failed\n");
- err = -EBUSY;
- goto fail3;
- }
+ platform_set_drvdata(pdev, nuc900_rtc);
nuc900_rtc->rtcdev = rtc_device_register(pdev->name, &pdev->dev,
&nuc900_rtc_ops, THIS_MODULE);
if (IS_ERR(nuc900_rtc->rtcdev)) {
- dev_err(&pdev->dev, "rtc device register faild\n");
+ 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);
@@ -302,8 +304,8 @@ static int __devexit nuc900_rtc_remove(struct platform_device *pdev)
struct nuc900_rtc *nuc900_rtc = platform_get_drvdata(pdev);
struct resource *res;
- rtc_device_unregister(nuc900_rtc->rtcdev);
free_irq(nuc900_rtc->irq_num, nuc900_rtc);
+ rtc_device_unregister(nuc900_rtc->rtcdev);
iounmap(nuc900_rtc->rtc_reg);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
diff --git a/drivers/rtc/rtc-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-pcf8563.c b/drivers/rtc/rtc-pcf8563.c
index 1af42b4a6f59..b42c0c679266 100644
--- a/drivers/rtc/rtc-pcf8563.c
+++ b/drivers/rtc/rtc-pcf8563.c
@@ -172,14 +172,6 @@ static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm)
return 0;
}
-struct pcf8563_limit
-{
- unsigned char reg;
- unsigned char mask;
- unsigned char min;
- unsigned char max;
-};
-
static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
return pcf8563_get_datetime(to_i2c_client(dev), tm);
diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c
index 3587d9922f28..b7a6690e5b35 100644
--- a/drivers/rtc/rtc-pl031.c
+++ b/drivers/rtc/rtc-pl031.c
@@ -23,7 +23,6 @@
#include <linux/io.h>
#include <linux/bcd.h>
#include <linux/delay.h>
-#include <linux/version.h>
#include <linux/slab.h>
/*
@@ -404,7 +403,7 @@ static int pl031_probe(struct amba_device *adev, struct amba_id *id)
}
if (request_irq(adev->irq[0], pl031_interrupt,
- IRQF_DISABLED | IRQF_SHARED, "rtc-pl031", ldata)) {
+ IRQF_DISABLED, "rtc-pl031", ldata)) {
ret = -EIO;
goto out_no_irq;
}
@@ -456,7 +455,7 @@ static struct rtc_class_ops stv2_pl031_ops = {
.irq_set_freq = pl031_irq_set_freq,
};
-static struct amba_id pl031_ids[] __initdata = {
+static struct amba_id pl031_ids[] = {
{
.id = 0x00041031,
.mask = 0x000fffff,
diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c
index e9c6fa035989..29e867a1aaa8 100644
--- a/drivers/rtc/rtc-pxa.c
+++ b/drivers/rtc/rtc-pxa.c
@@ -87,7 +87,6 @@ struct pxa_rtc {
int irq_Alrm;
struct rtc_device *rtc;
spinlock_t lock; /* Protects this structure */
- struct rtc_time rtc_alarm;
};
static u32 ryxr_calc(struct rtc_time *tm)
@@ -236,32 +235,34 @@ static int pxa_periodic_irq_set_state(struct device *dev, int enabled)
return 0;
}
-static int pxa_rtc_ioctl(struct device *dev, unsigned int cmd,
- unsigned long arg)
+static int pxa_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev);
- int ret = 0;
spin_lock_irq(&pxa_rtc->lock);
- switch (cmd) {
- case RTC_AIE_OFF:
- rtsr_clear_bits(pxa_rtc, RTSR_RDALE1);
- break;
- case RTC_AIE_ON:
+
+ if (enabled)
rtsr_set_bits(pxa_rtc, RTSR_RDALE1);
- break;
- case RTC_UIE_OFF:
- rtsr_clear_bits(pxa_rtc, RTSR_HZE);
- break;
- case RTC_UIE_ON:
+ else
+ rtsr_clear_bits(pxa_rtc, RTSR_RDALE1);
+
+ spin_unlock_irq(&pxa_rtc->lock);
+ return 0;
+}
+
+static int pxa_update_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev);
+
+ spin_lock_irq(&pxa_rtc->lock);
+
+ if (enabled)
rtsr_set_bits(pxa_rtc, RTSR_HZE);
- break;
- default:
- ret = -ENOIOCTLCMD;
- }
+ else
+ rtsr_clear_bits(pxa_rtc, RTSR_HZE);
spin_unlock_irq(&pxa_rtc->lock);
- return ret;
+ return 0;
}
static int pxa_rtc_read_time(struct device *dev, struct rtc_time *tm)
@@ -340,11 +341,12 @@ static int pxa_rtc_proc(struct device *dev, struct seq_file *seq)
static const struct rtc_class_ops pxa_rtc_ops = {
.open = pxa_rtc_open,
.release = pxa_rtc_release,
- .ioctl = pxa_rtc_ioctl,
.read_time = pxa_rtc_read_time,
.set_time = pxa_rtc_set_time,
.read_alarm = pxa_rtc_read_alarm,
.set_alarm = pxa_rtc_set_alarm,
+ .alarm_irq_enable = pxa_alarm_irq_enable,
+ .update_irq_enable = pxa_update_irq_enable,
.proc = pxa_rtc_proc,
.irq_set_state = pxa_periodic_irq_set_state,
.irq_set_freq = pxa_periodic_irq_set_freq,
diff --git a/drivers/rtc/rtc-rp5c01.c b/drivers/rtc/rtc-rp5c01.c
index a95f733bb15a..36eb66184461 100644
--- a/drivers/rtc/rtc-rp5c01.c
+++ b/drivers/rtc/rtc-rp5c01.c
@@ -63,6 +63,8 @@ enum {
struct rp5c01_priv {
u32 __iomem *regs;
struct rtc_device *rtc;
+ spinlock_t lock; /* against concurrent RTC/NVRAM access */
+ struct bin_attribute nvram_attr;
};
static inline unsigned int rp5c01_read(struct rp5c01_priv *priv,
@@ -92,6 +94,7 @@ static int rp5c01_read_time(struct device *dev, struct rtc_time *tm)
{
struct rp5c01_priv *priv = dev_get_drvdata(dev);
+ spin_lock_irq(&priv->lock);
rp5c01_lock(priv);
tm->tm_sec = rp5c01_read(priv, RP5C01_10_SECOND) * 10 +
@@ -111,6 +114,7 @@ static int rp5c01_read_time(struct device *dev, struct rtc_time *tm)
tm->tm_year += 100;
rp5c01_unlock(priv);
+ spin_unlock_irq(&priv->lock);
return rtc_valid_tm(tm);
}
@@ -119,6 +123,7 @@ static int rp5c01_set_time(struct device *dev, struct rtc_time *tm)
{
struct rp5c01_priv *priv = dev_get_drvdata(dev);
+ spin_lock_irq(&priv->lock);
rp5c01_lock(priv);
rp5c01_write(priv, tm->tm_sec / 10, RP5C01_10_SECOND);
@@ -139,6 +144,7 @@ static int rp5c01_set_time(struct device *dev, struct rtc_time *tm)
rp5c01_write(priv, tm->tm_year % 10, RP5C01_1_YEAR);
rp5c01_unlock(priv);
+ spin_unlock_irq(&priv->lock);
return 0;
}
@@ -147,6 +153,72 @@ static const struct rtc_class_ops rp5c01_rtc_ops = {
.set_time = rp5c01_set_time,
};
+
+/*
+ * The NVRAM is organized as 2 blocks of 13 nibbles of 4 bits.
+ * We provide access to them like AmigaOS does: the high nibble of each 8-bit
+ * byte is stored in BLOCK10, the low nibble in BLOCK11.
+ */
+
+static ssize_t rp5c01_nvram_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t size)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct rp5c01_priv *priv = dev_get_drvdata(dev);
+ ssize_t count;
+
+ spin_lock_irq(&priv->lock);
+
+ for (count = 0; size > 0 && pos < RP5C01_MODE; count++, size--) {
+ u8 data;
+
+ rp5c01_write(priv,
+ RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK10,
+ RP5C01_MODE);
+ data = rp5c01_read(priv, pos) << 4;
+ rp5c01_write(priv,
+ RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK11,
+ RP5C01_MODE);
+ data |= rp5c01_read(priv, pos++);
+ rp5c01_write(priv, RP5C01_MODE_TIMER_EN | RP5C01_MODE_MODE01,
+ RP5C01_MODE);
+ *buf++ = data;
+ }
+
+ spin_unlock_irq(&priv->lock);
+ return count;
+}
+
+static ssize_t rp5c01_nvram_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t size)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct rp5c01_priv *priv = dev_get_drvdata(dev);
+ ssize_t count;
+
+ spin_lock_irq(&priv->lock);
+
+ for (count = 0; size > 0 && pos < RP5C01_MODE; count++, size--) {
+ u8 data = *buf++;
+
+ rp5c01_write(priv,
+ RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK10,
+ RP5C01_MODE);
+ rp5c01_write(priv, data >> 4, pos);
+ rp5c01_write(priv,
+ RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK11,
+ RP5C01_MODE);
+ rp5c01_write(priv, data & 0xf, pos++);
+ rp5c01_write(priv, RP5C01_MODE_TIMER_EN | RP5C01_MODE_MODE01,
+ RP5C01_MODE);
+ }
+
+ spin_unlock_irq(&priv->lock);
+ return count;
+}
+
static int __init rp5c01_rtc_probe(struct platform_device *dev)
{
struct resource *res;
@@ -168,6 +240,15 @@ static int __init rp5c01_rtc_probe(struct platform_device *dev)
goto out_free_priv;
}
+ sysfs_bin_attr_init(&priv->nvram_attr);
+ priv->nvram_attr.attr.name = "nvram";
+ priv->nvram_attr.attr.mode = S_IRUGO | S_IWUSR;
+ priv->nvram_attr.read = rp5c01_nvram_read;
+ priv->nvram_attr.write = rp5c01_nvram_write;
+ priv->nvram_attr.size = RP5C01_MODE;
+
+ spin_lock_init(&priv->lock);
+
rtc = rtc_device_register("rtc-rp5c01", &dev->dev, &rp5c01_rtc_ops,
THIS_MODULE);
if (IS_ERR(rtc)) {
@@ -177,8 +258,15 @@ static int __init rp5c01_rtc_probe(struct platform_device *dev)
priv->rtc = rtc;
platform_set_drvdata(dev, priv);
+
+ error = sysfs_create_bin_file(&dev->dev.kobj, &priv->nvram_attr);
+ if (error)
+ goto out_unregister;
+
return 0;
+out_unregister:
+ rtc_device_unregister(rtc);
out_unmap:
iounmap(priv->regs);
out_free_priv:
@@ -190,6 +278,7 @@ static int __exit rp5c01_rtc_remove(struct platform_device *dev)
{
struct rp5c01_priv *priv = platform_get_drvdata(dev);
+ sysfs_remove_bin_file(&dev->dev.kobj, &priv->nvram_attr);
rtc_device_unregister(priv->rtc);
iounmap(priv->regs);
kfree(priv);
diff --git a/drivers/rtc/rtc-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-rx8025.c b/drivers/rtc/rtc-rx8025.c
index 789f62f9b47d..1146e3522d3c 100644
--- a/drivers/rtc/rtc-rx8025.c
+++ b/drivers/rtc/rtc-rx8025.c
@@ -461,7 +461,7 @@ static struct rtc_class_ops rx8025_rtc_ops = {
* Clock precision adjustment support
*
* According to the RX8025 SA/NB application manual the frequency and
- * temperature charateristics can be approximated using the following
+ * temperature characteristics can be approximated using the following
* equation:
*
* df = a * (ut - t)**2
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
index 70b68d35f969..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/
@@ -39,6 +42,7 @@ enum s3c_cpu_type {
static struct resource *s3c_rtc_mem;
+static struct clk *rtc_clk;
static void __iomem *s3c_rtc_base;
static int s3c_rtc_alarmno = NO_IRQ;
static int s3c_rtc_tickno = NO_IRQ;
@@ -53,6 +57,10 @@ static irqreturn_t s3c_rtc_alarmirq(int irq, void *id)
struct rtc_device *rdev = id;
rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF);
+
+ if (s3c_rtc_cpu_type == TYPE_S3C64XX)
+ writeb(S3C2410_INTP_ALM, s3c_rtc_base + S3C2410_INTP);
+
return IRQ_HANDLED;
}
@@ -61,6 +69,10 @@ static irqreturn_t s3c_rtc_tickirq(int irq, void *id)
struct rtc_device *rdev = id;
rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF);
+
+ if (s3c_rtc_cpu_type == TYPE_S3C64XX)
+ writeb(S3C2410_INTP_TIC, s3c_rtc_base + S3C2410_INTP);
+
return IRQ_HANDLED;
}
@@ -88,13 +100,13 @@ static int s3c_rtc_setpie(struct device *dev, int enabled)
spin_lock_irq(&s3c_rtc_pie_lock);
if (s3c_rtc_cpu_type == TYPE_S3C64XX) {
- tmp = readb(s3c_rtc_base + S3C2410_RTCCON);
+ tmp = readw(s3c_rtc_base + S3C2410_RTCCON);
tmp &= ~S3C64XX_RTCCON_TICEN;
if (enabled)
tmp |= S3C64XX_RTCCON_TICEN;
- writeb(tmp, s3c_rtc_base + S3C2410_RTCCON);
+ writew(tmp, s3c_rtc_base + S3C2410_RTCCON);
} else {
tmp = readb(s3c_rtc_base + S3C2410_TICNT);
tmp &= ~S3C2410_TICNT_ENABLE;
@@ -128,7 +140,7 @@ static int s3c_rtc_setfreq(struct device *dev, int freq)
tmp |= (rtc_dev->max_user_freq / freq)-1;
- writeb(tmp, s3c_rtc_base + S3C2410_TICNT);
+ writel(tmp, s3c_rtc_base + S3C2410_TICNT);
spin_unlock_irq(&s3c_rtc_pie_lock);
return 0;
@@ -159,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);
@@ -173,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)
@@ -181,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 */
@@ -219,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);
@@ -230,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;
}
@@ -268,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;
@@ -298,11 +310,6 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
s3c_rtc_setaie(alrm->enabled);
- if (alrm->enabled)
- enable_irq_wake(s3c_rtc_alarmno);
- else
- disable_irq_wake(s3c_rtc_alarmno);
-
return 0;
}
@@ -311,7 +318,7 @@ static int s3c_rtc_proc(struct device *dev, struct seq_file *seq)
unsigned int ticnt;
if (s3c_rtc_cpu_type == TYPE_S3C64XX) {
- ticnt = readb(s3c_rtc_base + S3C2410_RTCCON);
+ ticnt = readw(s3c_rtc_base + S3C2410_RTCCON);
ticnt &= S3C64XX_RTCCON_TICEN;
} else {
ticnt = readb(s3c_rtc_base + S3C2410_TICNT);
@@ -372,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)
@@ -384,11 +392,11 @@ static void s3c_rtc_enable(struct platform_device *pdev, int en)
return;
if (!en) {
- tmp = readb(base + S3C2410_RTCCON);
+ tmp = readw(base + S3C2410_RTCCON);
if (s3c_rtc_cpu_type == TYPE_S3C64XX)
tmp &= ~S3C64XX_RTCCON_TICEN;
tmp &= ~S3C2410_RTCCON_RTCEN;
- writeb(tmp, base + S3C2410_RTCCON);
+ writew(tmp, base + S3C2410_RTCCON);
if (s3c_rtc_cpu_type == TYPE_S3C2410) {
tmp = readb(base + S3C2410_TICNT);
@@ -398,25 +406,28 @@ static void s3c_rtc_enable(struct platform_device *pdev, int en)
} 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);
}
}
}
@@ -431,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);
@@ -441,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;
@@ -488,12 +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));
+ pr_debug("s3c2410_rtc: RTCCON=%02x\n",
+ readw(s3c_rtc_base + S3C2410_RTCCON));
device_init_wakeup(&pdev->dev, 1);
@@ -510,6 +536,23 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)
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
@@ -523,6 +566,10 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)
err_nortc:
s3c_rtc_enable(pdev, 0);
+ clk_disable(rtc_clk);
+ clk_put(rtc_clk);
+
+ err_clk:
iounmap(s3c_rtc_base);
err_nomap:
@@ -543,10 +590,14 @@ 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 = readb(s3c_rtc_base + S3C2410_RTCCON);
+ 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;
}
@@ -557,9 +608,13 @@ static int s3c_rtc_resume(struct platform_device *pdev)
s3c_rtc_enable(pdev, 1);
writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT);
if (s3c_rtc_cpu_type == TYPE_S3C64XX && ticnt_en_save) {
- tmp = readb(s3c_rtc_base + S3C2410_RTCCON);
- writeb(tmp | ticnt_en_save, s3c_rtc_base + S3C2410_RTCCON);
+ 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