summaryrefslogtreecommitdiffstats
path: root/drivers/rtc
diff options
context:
space:
mode:
authorJiri Kosina <jkosina@suse.cz>2014-11-20 14:42:02 +0100
committerJiri Kosina <jkosina@suse.cz>2014-11-20 14:42:02 +0100
commita02001086bbfb4da35d1228bebc2f1b442db455f (patch)
tree62ab47936cef06fd08657ca5b6cd1df98c19be57 /drivers/rtc
parenteff264efeeb0898408e8c9df72d8a32621035bed (diff)
parentfc14f9c1272f62c3e8d01300f52467c0d9af50f9 (diff)
Merge Linus' tree to be be to apply submitted patches to newer code than
current trivial.git base
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/Kconfig73
-rw-r--r--drivers/rtc/Makefile8
-rw-r--r--drivers/rtc/class.c16
-rw-r--r--drivers/rtc/interface.c2
-rw-r--r--drivers/rtc/rtc-au1xxx.c18
-rw-r--r--drivers/rtc/rtc-bq32k.c62
-rw-r--r--drivers/rtc/rtc-cmos.c5
-rw-r--r--drivers/rtc/rtc-da9063.c54
-rw-r--r--drivers/rtc/rtc-ds1307.c67
-rw-r--r--drivers/rtc/rtc-ds1343.c75
-rw-r--r--drivers/rtc/rtc-ds1742.c2
-rw-r--r--drivers/rtc/rtc-efi-platform.c31
-rw-r--r--drivers/rtc/rtc-efi.c34
-rw-r--r--drivers/rtc/rtc-isl12022.c12
-rw-r--r--drivers/rtc/rtc-max77686.c167
-rw-r--r--drivers/rtc/rtc-max77802.c502
-rw-r--r--drivers/rtc/rtc-mpc5121.c2
-rw-r--r--drivers/rtc/rtc-pcf85063.c204
-rw-r--r--drivers/rtc/rtc-pcf8563.c231
-rw-r--r--drivers/rtc/rtc-pcf8583.c18
-rw-r--r--drivers/rtc/rtc-pm8xxx.c222
-rw-r--r--drivers/rtc/rtc-rk808.c414
-rw-r--r--drivers/rtc/rtc-rs5c372.c11
-rw-r--r--drivers/rtc/rtc-s3c.c855
-rw-r--r--drivers/rtc/rtc-s5m.c21
-rw-r--r--drivers/rtc/rtc-sun6i.c447
-rw-r--r--drivers/rtc/rtc-tps65910.c4
27 files changed, 2916 insertions, 641 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 0754f5c7cb3..6dd12ddbabc 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -288,6 +288,26 @@ config RTC_DRV_MAX77686
This driver can also be built as a module. If so, the module
will be called rtc-max77686.
+config RTC_DRV_RK808
+ tristate "Rockchip RK808 RTC"
+ depends on MFD_RK808
+ help
+ If you say yes here you will get support for the
+ RTC of RK808 PMIC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rk808-rtc.
+
+config RTC_DRV_MAX77802
+ tristate "Maxim 77802 RTC"
+ depends on MFD_MAX77686
+ help
+ If you say yes here you will get support for the
+ RTC of Maxim MAX77802 PMIC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-max77802.
+
config RTC_DRV_RS5C372
tristate "Ricoh R2025S/D, RS5C372A/B, RV5C386, RV5C387A"
help
@@ -373,6 +393,14 @@ config RTC_DRV_PCF8563
This driver can also be built as a module. If so, the module
will be called rtc-pcf8563.
+config RTC_DRV_PCF85063
+ tristate "nxp PCF85063"
+ help
+ If you say yes here you get support for the PCF85063 RTC chip
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-pcf85063.
+
config RTC_DRV_PCF8583
tristate "Philips PCF8583"
help
@@ -724,6 +752,7 @@ config RTC_DRV_DS1216
config RTC_DRV_DS1286
tristate "Dallas DS1286"
+ depends on HAS_IOMEM
help
If you say yes here you get support for the Dallas DS1286 RTC chips.
@@ -735,6 +764,7 @@ config RTC_DRV_DS1302
config RTC_DRV_DS1511
tristate "Dallas DS1511"
+ depends on HAS_IOMEM
help
If you say yes here you get support for the
Dallas DS1511 timekeeping/watchdog chip.
@@ -744,6 +774,7 @@ config RTC_DRV_DS1511
config RTC_DRV_DS1553
tristate "Maxim/Dallas DS1553"
+ depends on HAS_IOMEM
help
If you say yes here you get support for the
Maxim/Dallas DS1553 timekeeping chip.
@@ -753,6 +784,7 @@ config RTC_DRV_DS1553
config RTC_DRV_DS1742
tristate "Maxim/Dallas DS1742/1743"
+ depends on HAS_IOMEM
help
If you say yes here you get support for the
Maxim/Dallas DS1742/1743 timekeeping chip.
@@ -760,6 +792,15 @@ config RTC_DRV_DS1742
This driver can also be built as a module. If so, the module
will be called rtc-ds1742.
+config RTC_DRV_DS2404
+ tristate "Maxim/Dallas DS2404"
+ help
+ If you say yes here you get support for the
+ Dallas DS2404 RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds2404.
+
config RTC_DRV_DA9052
tristate "Dialog DA9052/DA9053 RTC"
depends on PMIC_DA9052
@@ -789,7 +830,7 @@ config RTC_DRV_DA9063
config RTC_DRV_EFI
tristate "EFI RTC"
- depends on IA64
+ depends on EFI && !X86
help
If you say yes here you will get support for the EFI
Real Time Clock.
@@ -799,6 +840,7 @@ config RTC_DRV_EFI
config RTC_DRV_STK17TA8
tristate "Simtek STK17TA8"
+ depends on HAS_IOMEM
help
If you say yes here you get support for the
Simtek STK17TA8 timekeeping chip.
@@ -817,6 +859,7 @@ config RTC_DRV_M48T86
config RTC_DRV_M48T35
tristate "ST M48T35"
+ depends on HAS_IOMEM
help
If you say Y here you will get support for the
ST M48T35 RTC chip.
@@ -826,6 +869,7 @@ config RTC_DRV_M48T35
config RTC_DRV_M48T59
tristate "ST M48T59/M48T08/M48T02"
+ depends on HAS_IOMEM
help
If you say Y here you will get support for the
ST M48T59 RTC chip and compatible ST M48T08 and M48T02.
@@ -838,6 +882,7 @@ config RTC_DRV_M48T59
config RTC_DRV_MSM6242
tristate "Oki MSM6242"
+ depends on HAS_IOMEM
help
If you say yes here you get support for the Oki MSM6242
timekeeping chip. It is used in some Amiga models (e.g. A2000).
@@ -847,6 +892,7 @@ config RTC_DRV_MSM6242
config RTC_DRV_BQ4802
tristate "TI BQ4802"
+ depends on HAS_IOMEM
help
If you say Y here you will get support for the TI
BQ4802 RTC chip.
@@ -856,6 +902,7 @@ config RTC_DRV_BQ4802
config RTC_DRV_RP5C01
tristate "Ricoh RP5C01"
+ depends on HAS_IOMEM
help
If you say yes here you get support for the Ricoh RP5C01
timekeeping chip. It is used in some Amiga models (e.g. A3000
@@ -873,15 +920,6 @@ config RTC_DRV_V3020
This driver can also be built as a module. If so, the module
will be called rtc-v3020.
-config RTC_DRV_DS2404
- tristate "Dallas DS2404"
- help
- If you say yes here you get support for the
- Dallas DS2404 RTC chip.
-
- This driver can also be built as a module. If so, the module
- will be called rtc-ds2404.
-
config RTC_DRV_WM831X
tristate "Wolfson Microelectronics WM831x RTC"
depends on MFD_WM831X
@@ -1167,9 +1205,16 @@ config RTC_DRV_SUN4V
If you say Y here you will get support for the Hypervisor
based RTC on SUN4V systems.
+config RTC_DRV_SUN6I
+ tristate "Allwinner A31 RTC"
+ depends on MACH_SUN6I || MACH_SUN8I
+ help
+ If you say Y here you will get support for the RTC found on
+ Allwinner A31.
+
config RTC_DRV_SUNXI
tristate "Allwinner sun4i/sun7i RTC"
- depends on ARCH_SUNXI
+ depends on MACH_SUN4I || MACH_SUN7I
help
If you say Y here you will get support for the RTC found on
Allwinner A10/A20.
@@ -1190,7 +1235,7 @@ config RTC_DRV_TX4939
config RTC_DRV_MV
tristate "Marvell SoC RTC"
- depends on ARCH_KIRKWOOD || ARCH_DOVE || ARCH_MVEBU
+ depends on ARCH_DOVE || ARCH_MVEBU
help
If you say yes here you will get support for the in-chip RTC
that can be found in some of Marvell's SoC devices, such as
@@ -1275,7 +1320,7 @@ config RTC_DRV_LPC32XX
config RTC_DRV_PM8XXX
tristate "Qualcomm PMIC8XXX RTC"
- depends on MFD_PM8XXX
+ depends on MFD_PM8XXX || MFD_SPMI_PMIC
help
If you say yes here you get support for the
Qualcomm PMIC8XXX RTC.
@@ -1349,6 +1394,7 @@ config RTC_DRV_SIRFSOC
config RTC_DRV_MOXART
tristate "MOXA ART RTC"
+ depends on ARCH_MOXART || COMPILE_TEST
help
If you say yes here you get support for the MOXA ART
RTC module.
@@ -1358,6 +1404,7 @@ config RTC_DRV_MOXART
config RTC_DRV_XGENE
tristate "APM X-Gene RTC"
+ depends on HAS_IOMEM
help
If you say yes here you get support for the APM X-Gene SoC real time
clock.
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 70347d041d1..b188323c096 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -10,6 +10,10 @@ obj-$(CONFIG_RTC_SYSTOHC) += systohc.o
obj-$(CONFIG_RTC_CLASS) += rtc-core.o
rtc-core-y := class.o interface.o
+ifdef CONFIG_RTC_DRV_EFI
+rtc-core-y += rtc-efi-platform.o
+endif
+
rtc-core-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o
rtc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o
rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o
@@ -81,6 +85,7 @@ obj-$(CONFIG_RTC_DRV_MAX8998) += rtc-max8998.o
obj-$(CONFIG_RTC_DRV_MAX8997) += rtc-max8997.o
obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
obj-$(CONFIG_RTC_DRV_MAX77686) += rtc-max77686.o
+obj-$(CONFIG_RTC_DRV_MAX77802) += rtc-max77802.o
obj-$(CONFIG_RTC_DRV_MC13XXX) += rtc-mc13xxx.o
obj-$(CONFIG_RTC_DRV_MCP795) += rtc-mcp795.o
obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o
@@ -93,6 +98,7 @@ obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o
obj-$(CONFIG_RTC_DRV_PCF2127) += rtc-pcf2127.o
obj-$(CONFIG_RTC_DRV_PCF8523) += rtc-pcf8523.o
obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o
+obj-$(CONFIG_RTC_DRV_PCF85063) += rtc-pcf85063.o
obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o
obj-$(CONFIG_RTC_DRV_PCF2123) += rtc-pcf2123.o
obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o
@@ -104,6 +110,7 @@ obj-$(CONFIG_RTC_DRV_PUV3) += rtc-puv3.o
obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.o
obj-$(CONFIG_RTC_DRV_R9701) += rtc-r9701.o
obj-$(CONFIG_RTC_DRV_RC5T583) += rtc-rc5t583.o
+obj-$(CONFIG_RTC_DRV_RK808) += rtc-rk808.o
obj-$(CONFIG_RTC_DRV_RP5C01) += rtc-rp5c01.o
obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o
obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o
@@ -123,6 +130,7 @@ obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o
obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o
obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o
obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o
+obj-$(CONFIG_RTC_DRV_SUN6I) += rtc-sun6i.o
obj-$(CONFIG_RTC_DRV_SUNXI) += rtc-sunxi.o
obj-$(CONFIG_RTC_DRV_TEGRA) += rtc-tegra.o
obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
index 589351ef75d..38e26be705b 100644
--- a/drivers/rtc/class.c
+++ b/drivers/rtc/class.c
@@ -53,6 +53,7 @@ static int rtc_suspend(struct device *dev)
struct rtc_device *rtc = to_rtc_device(dev);
struct rtc_time tm;
struct timespec delta, delta_delta;
+ int err;
if (has_persistent_clock())
return 0;
@@ -61,7 +62,12 @@ static int rtc_suspend(struct device *dev)
return 0;
/* snapshot the current RTC and system time at suspend*/
- rtc_read_time(rtc, &tm);
+ err = rtc_read_time(rtc, &tm);
+ if (err < 0) {
+ pr_debug("%s: fail to read rtc time\n", dev_name(&rtc->dev));
+ return 0;
+ }
+
getnstimeofday(&old_system);
rtc_tm_to_time(&tm, &old_rtc.tv_sec);
@@ -94,6 +100,7 @@ static int rtc_resume(struct device *dev)
struct rtc_time tm;
struct timespec new_system, new_rtc;
struct timespec sleep_time;
+ int err;
if (has_persistent_clock())
return 0;
@@ -104,7 +111,12 @@ static int rtc_resume(struct device *dev)
/* snapshot the current rtc and system time at resume */
getnstimeofday(&new_system);
- rtc_read_time(rtc, &tm);
+ err = rtc_read_time(rtc, &tm);
+ if (err < 0) {
+ pr_debug("%s: fail to read rtc time\n", dev_name(&rtc->dev));
+ return 0;
+ }
+
if (rtc_valid_tm(&tm) != 0) {
pr_debug("%s: bogus resume time\n", dev_name(&rtc->dev));
return 0;
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 5813fa52c3d..5b2717f5daf 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -348,6 +348,8 @@ static int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
/* Make sure we're not setting alarms in the past */
err = __rtc_read_time(rtc, &tm);
+ if (err)
+ return err;
rtc_tm_to_time(&tm, &now);
if (scheduled <= now)
return -ETIME;
diff --git a/drivers/rtc/rtc-au1xxx.c b/drivers/rtc/rtc-au1xxx.c
index ed526a192ce..fd25e2374d4 100644
--- a/drivers/rtc/rtc-au1xxx.c
+++ b/drivers/rtc/rtc-au1xxx.c
@@ -32,7 +32,7 @@ static int au1xtoy_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
unsigned long t;
- t = au_readl(SYS_TOYREAD);
+ t = alchemy_rdsys(AU1000_SYS_TOYREAD);
rtc_time_to_tm(t, tm);
@@ -45,13 +45,12 @@ static int au1xtoy_rtc_set_time(struct device *dev, struct rtc_time *tm)
rtc_tm_to_time(tm, &t);
- au_writel(t, SYS_TOYWRITE);
- au_sync();
+ alchemy_wrsys(t, AU1000_SYS_TOYWRITE);
/* wait for the pending register write to succeed. This can
* take up to 6 seconds...
*/
- while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S)
+ while (alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_C0S)
msleep(1);
return 0;
@@ -68,7 +67,7 @@ static int au1xtoy_rtc_probe(struct platform_device *pdev)
unsigned long t;
int ret;
- t = au_readl(SYS_COUNTER_CNTRL);
+ t = alchemy_rdsys(AU1000_SYS_CNTRCTRL);
if (!(t & CNTR_OK)) {
dev_err(&pdev->dev, "counters not working; aborting.\n");
ret = -ENODEV;
@@ -78,10 +77,10 @@ static int au1xtoy_rtc_probe(struct platform_device *pdev)
ret = -ETIMEDOUT;
/* set counter0 tickrate to 1Hz if necessary */
- if (au_readl(SYS_TOYTRIM) != 32767) {
+ if (alchemy_rdsys(AU1000_SYS_TOYTRIM) != 32767) {
/* wait until hardware gives access to TRIM register */
t = 0x00100000;
- while ((au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T0S) && --t)
+ while ((alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_T0S) && --t)
msleep(1);
if (!t) {
@@ -93,12 +92,11 @@ static int au1xtoy_rtc_probe(struct platform_device *pdev)
}
/* set 1Hz TOY tick rate */
- au_writel(32767, SYS_TOYTRIM);
- au_sync();
+ alchemy_wrsys(32767, AU1000_SYS_TOYTRIM);
}
/* wait until the hardware allows writes to the counter reg */
- while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S)
+ while (alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_C0S)
msleep(1);
rtcdev = devm_rtc_device_register(&pdev->dev, "rtc-au1xxx",
diff --git a/drivers/rtc/rtc-bq32k.c b/drivers/rtc/rtc-bq32k.c
index c74bf0dc52c..92679df6d6e 100644
--- a/drivers/rtc/rtc-bq32k.c
+++ b/drivers/rtc/rtc-bq32k.c
@@ -2,10 +2,14 @@
* Driver for TI BQ32000 RTC.
*
* Copyright (C) 2009 Semihalf.
+ * Copyright (C) 2014 Pavel Machek <pavel@denx.de>
*
* 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.
+ *
+ * You can get hardware description at
+ * http://www.ti.com/lit/ds/symlink/bq32000.pdf
*/
#include <linux/module.h>
@@ -27,6 +31,10 @@
#define BQ32K_CENT 0x40 /* Century flag */
#define BQ32K_CENT_EN 0x80 /* Century flag enable bit */
+#define BQ32K_CALIBRATION 0x07 /* CAL_CFG1, calibration and control */
+#define BQ32K_TCH2 0x08 /* Trickle charge enable */
+#define BQ32K_CFG2 0x09 /* Trickle charger control */
+
struct bq32k_regs {
uint8_t seconds;
uint8_t minutes;
@@ -122,6 +130,57 @@ static const struct rtc_class_ops bq32k_rtc_ops = {
.set_time = bq32k_rtc_set_time,
};
+static int trickle_charger_of_init(struct device *dev, struct device_node *node)
+{
+ unsigned char reg;
+ int error;
+ u32 ohms = 0;
+
+ if (of_property_read_u32(node, "trickle-resistor-ohms" , &ohms))
+ return 0;
+
+ switch (ohms) {
+ case 180+940:
+ /*
+ * TCHE[3:0] == 0x05, TCH2 == 1, TCFE == 0 (charging
+ * over diode and 940ohm resistor)
+ */
+
+ if (of_property_read_bool(node, "trickle-diode-disable")) {
+ dev_err(dev, "diode and resistor mismatch\n");
+ return -EINVAL;
+ }
+ reg = 0x05;
+ break;
+
+ case 180+20000:
+ /* diode disabled */
+
+ if (!of_property_read_bool(node, "trickle-diode-disable")) {
+ dev_err(dev, "bq32k: diode and resistor mismatch\n");
+ return -EINVAL;
+ }
+ reg = 0x45;
+ break;
+
+ default:
+ dev_err(dev, "invalid resistor value (%d)\n", ohms);
+ return -EINVAL;
+ }
+
+ error = bq32k_write(dev, &reg, BQ32K_CFG2, 1);
+ if (error)
+ return error;
+
+ reg = 0x20;
+ error = bq32k_write(dev, &reg, BQ32K_TCH2, 1);
+ if (error)
+ return error;
+
+ dev_info(dev, "Enabled trickle RTC battery charge.\n");
+ return 0;
+}
+
static int bq32k_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -153,6 +212,9 @@ static int bq32k_probe(struct i2c_client *client,
if (error)
return error;
+ if (client && client->dev.of_node)
+ trickle_charger_of_init(dev, client->dev.of_node);
+
rtc = devm_rtc_device_register(&client->dev, bq32k_driver.driver.name,
&bq32k_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc))
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index b0e4a3eb33c..5b2e76159b4 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -856,7 +856,7 @@ static void __exit cmos_do_remove(struct device *dev)
cmos->dev = NULL;
}
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM
static int cmos_suspend(struct device *dev)
{
@@ -907,6 +907,8 @@ static inline int cmos_poweroff(struct device *dev)
return cmos_suspend(dev);
}
+#ifdef CONFIG_PM_SLEEP
+
static int cmos_resume(struct device *dev)
{
struct cmos_rtc *cmos = dev_get_drvdata(dev);
@@ -954,6 +956,7 @@ static int cmos_resume(struct device *dev)
return 0;
}
+#endif
#else
static inline int cmos_poweroff(struct device *dev)
diff --git a/drivers/rtc/rtc-da9063.c b/drivers/rtc/rtc-da9063.c
index 595393098b0..731ed1a97f5 100644
--- a/drivers/rtc/rtc-da9063.c
+++ b/drivers/rtc/rtc-da9063.c
@@ -29,6 +29,8 @@
#define YEARS_FROM_DA9063(year) ((year) + 100)
#define MONTHS_FROM_DA9063(month) ((month) - 1)
+#define RTC_ALARM_DATA_LEN (DA9063_AD_REG_ALARM_Y - DA9063_AD_REG_ALARM_MI + 1)
+
#define RTC_DATA_LEN (DA9063_REG_COUNT_Y - DA9063_REG_COUNT_S + 1)
#define RTC_SEC 0
#define RTC_MIN 1
@@ -42,6 +44,10 @@ struct da9063_rtc {
struct da9063 *hw;
struct rtc_time alarm_time;
bool rtc_sync;
+ int alarm_year;
+ int alarm_start;
+ int alarm_len;
+ int data_start;
};
static void da9063_data_to_tm(u8 *data, struct rtc_time *tm)
@@ -83,7 +89,7 @@ static int da9063_rtc_stop_alarm(struct device *dev)
{
struct da9063_rtc *rtc = dev_get_drvdata(dev);
- return regmap_update_bits(rtc->hw->regmap, DA9063_REG_ALARM_Y,
+ return regmap_update_bits(rtc->hw->regmap, rtc->alarm_year,
DA9063_ALARM_ON, 0);
}
@@ -91,7 +97,7 @@ static int da9063_rtc_start_alarm(struct device *dev)
{
struct da9063_rtc *rtc = dev_get_drvdata(dev);
- return regmap_update_bits(rtc->hw->regmap, DA9063_REG_ALARM_Y,
+ return regmap_update_bits(rtc->hw->regmap, rtc->alarm_year,
DA9063_ALARM_ON, DA9063_ALARM_ON);
}
@@ -151,8 +157,9 @@ static int da9063_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
int ret;
unsigned int val;
- ret = regmap_bulk_read(rtc->hw->regmap, DA9063_REG_ALARM_S,
- &data[RTC_SEC], RTC_DATA_LEN);
+ data[RTC_SEC] = 0;
+ ret = regmap_bulk_read(rtc->hw->regmap, rtc->alarm_start,
+ &data[rtc->data_start], rtc->alarm_len);
if (ret < 0)
return ret;
@@ -186,14 +193,14 @@ static int da9063_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
return ret;
}
- ret = regmap_bulk_write(rtc->hw->regmap, DA9063_REG_ALARM_S,
- data, RTC_DATA_LEN);
+ ret = regmap_bulk_write(rtc->hw->regmap, rtc->alarm_start,
+ &data[rtc->data_start], rtc->alarm_len);
if (ret < 0) {
dev_err(dev, "Failed to write alarm: %d\n", ret);
return ret;
}
- rtc->alarm_time = alrm->time;
+ da9063_data_to_tm(data, &rtc->alarm_time);
if (alrm->enabled) {
ret = da9063_rtc_start_alarm(dev);
@@ -218,7 +225,7 @@ static irqreturn_t da9063_alarm_event(int irq, void *data)
{
struct da9063_rtc *rtc = data;
- regmap_update_bits(rtc->hw->regmap, DA9063_REG_ALARM_Y,
+ regmap_update_bits(rtc->hw->regmap, rtc->alarm_year,
DA9063_ALARM_ON, 0);
rtc->rtc_sync = true;
@@ -257,7 +264,23 @@ static int da9063_rtc_probe(struct platform_device *pdev)
goto err;
}
- ret = regmap_update_bits(da9063->regmap, DA9063_REG_ALARM_S,
+ rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ if (da9063->variant_code == PMIC_DA9063_AD) {
+ rtc->alarm_year = DA9063_AD_REG_ALARM_Y;
+ rtc->alarm_start = DA9063_AD_REG_ALARM_MI;
+ rtc->alarm_len = RTC_ALARM_DATA_LEN;
+ rtc->data_start = RTC_MIN;
+ } else {
+ rtc->alarm_year = DA9063_BB_REG_ALARM_Y;
+ rtc->alarm_start = DA9063_BB_REG_ALARM_S;
+ rtc->alarm_len = RTC_DATA_LEN;
+ rtc->data_start = RTC_SEC;
+ }
+
+ ret = regmap_update_bits(da9063->regmap, rtc->alarm_start,
DA9063_ALARM_STATUS_TICK | DA9063_ALARM_STATUS_ALARM,
0);
if (ret < 0) {
@@ -265,7 +288,7 @@ static int da9063_rtc_probe(struct platform_device *pdev)
goto err;
}
- ret = regmap_update_bits(da9063->regmap, DA9063_REG_ALARM_S,
+ ret = regmap_update_bits(da9063->regmap, rtc->alarm_start,
DA9063_ALARM_STATUS_ALARM,
DA9063_ALARM_STATUS_ALARM);
if (ret < 0) {
@@ -273,25 +296,22 @@ static int da9063_rtc_probe(struct platform_device *pdev)
goto err;
}
- ret = regmap_update_bits(da9063->regmap, DA9063_REG_ALARM_Y,
+ ret = regmap_update_bits(da9063->regmap, rtc->alarm_year,
DA9063_TICK_ON, 0);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to disable TICKs\n");
goto err;
}
- ret = regmap_bulk_read(da9063->regmap, DA9063_REG_ALARM_S,
- data, RTC_DATA_LEN);
+ data[RTC_SEC] = 0;
+ ret = regmap_bulk_read(da9063->regmap, rtc->alarm_start,
+ &data[rtc->data_start], rtc->alarm_len);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to read initial alarm data: %d\n",
ret);
goto err;
}
- rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
- if (!rtc)
- return -ENOMEM;
-
platform_set_drvdata(pdev, rtc);
irq_alarm = platform_get_irq_byname(pdev, "ALARM");
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index f03d5ba96db..bb43cf703ef 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -126,9 +126,14 @@ struct chip_desc {
u16 nvram_offset;
u16 nvram_size;
u16 trickle_charger_reg;
+ u8 trickle_charger_setup;
+ u8 (*do_trickle_setup)(struct i2c_client *, uint32_t, bool);
};
-static const struct chip_desc chips[last_ds_type] = {
+static u8 do_trickle_setup_ds1339(struct i2c_client *,
+ uint32_t ohms, bool diode);
+
+static struct chip_desc chips[last_ds_type] = {
[ds_1307] = {
.nvram_offset = 8,
.nvram_size = 56,
@@ -143,6 +148,7 @@ static const struct chip_desc chips[last_ds_type] = {
[ds_1339] = {
.alarm = 1,
.trickle_charger_reg = 0x10,
+ .do_trickle_setup = &do_trickle_setup_ds1339,
},
[ds_1340] = {
.trickle_charger_reg = 0x08,
@@ -833,15 +839,58 @@ ds1307_nvram_write(struct file *filp, struct kobject *kobj,
return count;
}
+
/*----------------------------------------------------------------------*/
+static u8 do_trickle_setup_ds1339(struct i2c_client *client,
+ uint32_t ohms, bool diode)
+{
+ u8 setup = (diode) ? DS1307_TRICKLE_CHARGER_DIODE :
+ DS1307_TRICKLE_CHARGER_NO_DIODE;
+
+ switch (ohms) {
+ case 250:
+ setup |= DS1307_TRICKLE_CHARGER_250_OHM;
+ break;
+ case 2000:
+ setup |= DS1307_TRICKLE_CHARGER_2K_OHM;
+ break;
+ case 4000:
+ setup |= DS1307_TRICKLE_CHARGER_4K_OHM;
+ break;
+ default:
+ dev_warn(&client->dev,
+ "Unsupported ohm value %u in dt\n", ohms);
+ return 0;
+ }
+ return setup;
+}
+
+static void ds1307_trickle_of_init(struct i2c_client *client,
+ struct chip_desc *chip)
+{
+ uint32_t ohms = 0;
+ bool diode = true;
+
+ if (!chip->do_trickle_setup)
+ goto out;
+ if (of_property_read_u32(client->dev.of_node, "trickle-resistor-ohms" , &ohms))
+ goto out;
+ if (of_property_read_bool(client->dev.of_node, "trickle-diode-disable"))
+ diode = false;
+ chip->trickle_charger_setup = chip->do_trickle_setup(client,
+ ohms, diode);
+out:
+ return;
+}
+
static int ds1307_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct ds1307 *ds1307;
int err = -ENODEV;
int tmp;
- const struct chip_desc *chip = &chips[id->driver_data];
+ struct chip_desc *chip = &chips[id->driver_data];
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
bool want_irq = false;
unsigned char *buf;
@@ -866,9 +915,19 @@ static int ds1307_probe(struct i2c_client *client,
ds1307->client = client;
ds1307->type = id->driver_data;
- if (pdata && pdata->trickle_charger_setup && chip->trickle_charger_reg)
+ if (!pdata && client->dev.of_node)
+ ds1307_trickle_of_init(client, chip);
+ else if (pdata && pdata->trickle_charger_setup)
+ chip->trickle_charger_setup = pdata->trickle_charger_setup;
+
+ if (chip->trickle_charger_setup && chip->trickle_charger_reg) {
+ dev_dbg(&client->dev, "writing trickle charger info 0x%x to 0x%x\n",
+ DS13XX_TRICKLE_CHARGER_MAGIC | chip->trickle_charger_setup,
+ chip->trickle_charger_reg);
i2c_smbus_write_byte_data(client, chip->trickle_charger_reg,
- DS13XX_TRICKLE_CHARGER_MAGIC | pdata->trickle_charger_setup);
+ DS13XX_TRICKLE_CHARGER_MAGIC |
+ chip->trickle_charger_setup);
+ }
buf = ds1307->regs;
if (i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) {
diff --git a/drivers/rtc/rtc-ds1343.c b/drivers/rtc/rtc-ds1343.c
index c3719189dd9..ae9f997223b 100644
--- a/drivers/rtc/rtc-ds1343.c
+++ b/drivers/rtc/rtc-ds1343.c
@@ -4,6 +4,7 @@
* Real Time Clock
*
* Author : Raghavendra Chandra Ganiga <ravi23ganiga@gmail.com>
+ * Ankur Srivastava <sankurece@gmail.com> : DS1343 Nvram Support
*
* 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
@@ -45,6 +46,9 @@
#define DS1343_CONTROL_REG 0x0F
#define DS1343_STATUS_REG 0x10
#define DS1343_TRICKLE_REG 0x11
+#define DS1343_NVRAM 0x20
+
+#define DS1343_NVRAM_LEN 96
/* DS1343 Control Registers bits */
#define DS1343_EOSC 0x80
@@ -149,6 +153,64 @@ static ssize_t ds1343_store_glitchfilter(struct device *dev,
static DEVICE_ATTR(glitch_filter, S_IRUGO | S_IWUSR, ds1343_show_glitchfilter,
ds1343_store_glitchfilter);
+static ssize_t ds1343_nvram_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ int ret;
+ unsigned char address;
+ struct device *dev = kobj_to_dev(kobj);
+ struct ds1343_priv *priv = dev_get_drvdata(dev);
+
+ if (unlikely(!count))
+ return count;
+
+ if ((count + off) > DS1343_NVRAM_LEN)
+ count = DS1343_NVRAM_LEN - off;
+
+ address = DS1343_NVRAM + off;
+
+ ret = regmap_bulk_write(priv->map, address, buf, count);
+ if (ret < 0)
+ dev_err(&priv->spi->dev, "Error in nvram write %d", ret);
+
+ return (ret < 0) ? ret : count;
+}
+
+
+static ssize_t ds1343_nvram_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ int ret;
+ unsigned char address;
+ struct device *dev = kobj_to_dev(kobj);
+ struct ds1343_priv *priv = dev_get_drvdata(dev);
+
+ if (unlikely(!count))
+ return count;
+
+ if ((count + off) > DS1343_NVRAM_LEN)
+ count = DS1343_NVRAM_LEN - off;
+
+ address = DS1343_NVRAM + off;
+
+ ret = regmap_bulk_read(priv->map, address, buf, count);
+ if (ret < 0)
+ dev_err(&priv->spi->dev, "Error in nvram read %d\n", ret);
+
+ return (ret < 0) ? ret : count;
+}
+
+
+static struct bin_attribute nvram_attr = {
+ .attr.name = "nvram",
+ .attr.mode = S_IRUGO | S_IWUSR,
+ .read = ds1343_nvram_read,
+ .write = ds1343_nvram_write,
+ .size = DS1343_NVRAM_LEN,
+};
+
static ssize_t ds1343_show_alarmstatus(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -274,12 +336,16 @@ static int ds1343_sysfs_register(struct device *dev)
if (err)
goto error1;
+ err = device_create_bin_file(dev, &nvram_attr);
+ if (err)
+ goto error2;
+
if (priv->irq <= 0)
return err;
err = device_create_file(dev, &dev_attr_alarm_mode);
if (err)
- goto error2;
+ goto error3;
err = device_create_file(dev, &dev_attr_alarm_status);
if (!err)
@@ -287,6 +353,9 @@ static int ds1343_sysfs_register(struct device *dev)
device_remove_file(dev, &dev_attr_alarm_mode);
+error3:
+ device_remove_bin_file(dev, &nvram_attr);
+
error2:
device_remove_file(dev, &dev_attr_trickle_charger);
@@ -302,6 +371,7 @@ static void ds1343_sysfs_unregister(struct device *dev)
device_remove_file(dev, &dev_attr_glitch_filter);
device_remove_file(dev, &dev_attr_trickle_charger);
+ device_remove_bin_file(dev, &nvram_attr);
if (priv->irq <= 0)
return;
@@ -684,6 +754,7 @@ static struct spi_driver ds1343_driver = {
module_spi_driver(ds1343_driver);
MODULE_DESCRIPTION("DS1343 RTC SPI Driver");
-MODULE_AUTHOR("Raghavendra Chandra Ganiga <ravi23ganiga@gmail.com>");
+MODULE_AUTHOR("Raghavendra Chandra Ganiga <ravi23ganiga@gmail.com>,"
+ "Ankur Srivastava <sankurece@gmail.com>");
MODULE_LICENSE("GPL v2");
MODULE_VERSION(DS1343_DRV_VERSION);
diff --git a/drivers/rtc/rtc-ds1742.c b/drivers/rtc/rtc-ds1742.c
index c6b2191a412..9822715db8b 100644
--- a/drivers/rtc/rtc-ds1742.c
+++ b/drivers/rtc/rtc-ds1742.c
@@ -231,7 +231,7 @@ static struct platform_driver ds1742_rtc_driver = {
.driver = {
.name = "rtc-ds1742",
.owner = THIS_MODULE,
- .of_match_table = ds1742_rtc_of_match,
+ .of_match_table = of_match_ptr(ds1742_rtc_of_match),
},
};
diff --git a/drivers/rtc/rtc-efi-platform.c b/drivers/rtc/rtc-efi-platform.c
new file mode 100644
index 00000000000..b40fbe332af
--- /dev/null
+++ b/drivers/rtc/rtc-efi-platform.c
@@ -0,0 +1,31 @@
+/*
+ * Moved from arch/ia64/kernel/time.c
+ *
+ * Copyright (C) 1998-2003 Hewlett-Packard Co
+ * Stephane Eranian <eranian@hpl.hp.com>
+ * David Mosberger <davidm@hpl.hp.com>
+ * Copyright (C) 1999 Don Dugger <don.dugger@intel.com>
+ * Copyright (C) 1999-2000 VA Linux Systems
+ * Copyright (C) 1999-2000 Walt Drummond <drummond@valinux.com>
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/efi.h>
+#include <linux/platform_device.h>
+
+static struct platform_device rtc_efi_dev = {
+ .name = "rtc-efi",
+ .id = -1,
+};
+
+static int __init rtc_init(void)
+{
+ if (efi_enabled(EFI_RUNTIME_SERVICES))
+ if (platform_device_register(&rtc_efi_dev) < 0)
+ pr_err("unable to register rtc device...\n");
+
+ /* not necessarily an error */
+ return 0;
+}
+module_init(rtc_init);
diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
index c4c38431012..53b589dc34e 100644
--- a/drivers/rtc/rtc-efi.c
+++ b/drivers/rtc/rtc-efi.c
@@ -17,6 +17,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/stringify.h>
#include <linux/time.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
@@ -48,8 +49,8 @@ compute_wday(efi_time_t *eft)
int y;
int ndays = 0;
- if (eft->year < 1998) {
- pr_err("EFI year < 1998, invalid date\n");
+ if (eft->year < EFI_RTC_EPOCH) {
+ pr_err("EFI year < " __stringify(EFI_RTC_EPOCH) ", invalid date\n");
return -1;
}
@@ -78,19 +79,36 @@ convert_to_efi_time(struct rtc_time *wtime, efi_time_t *eft)
eft->timezone = EFI_UNSPECIFIED_TIMEZONE;
}
-static void
+static bool
convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime)
{
memset(wtime, 0, sizeof(*wtime));
+
+ if (eft->second >= 60)
+ return false;
wtime->tm_sec = eft->second;
+
+ if (eft->minute >= 60)
+ return false;
wtime->tm_min = eft->minute;
+
+ if (eft->hour >= 24)
+ return false;
wtime->tm_hour = eft->hour;
+
+ if (!eft->day || eft->day > 31)
+ return false;
wtime->tm_mday = eft->day;
+
+ if (!eft->month || eft->month > 12)
+ return false;
wtime->tm_mon = eft->month - 1;
wtime->tm_year = eft->year - 1900;
/* day of the week [0-6], Sunday=0 */
wtime->tm_wday = compute_wday(eft);
+ if (wtime->tm_wday < 0)
+ return false;
/* day in the year [1-365]*/
wtime->tm_yday = compute_yday(eft);
@@ -106,6 +124,8 @@ convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime)
default:
wtime->tm_isdst = -1;
}
+
+ return true;
}
static int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
@@ -122,7 +142,8 @@ static int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
if (status != EFI_SUCCESS)
return -EINVAL;
- convert_from_efi_time(&eft, &wkalrm->time);
+ if (!convert_from_efi_time(&eft, &wkalrm->time))
+ return -EIO;
return rtc_valid_tm(&wkalrm->time);
}
@@ -163,7 +184,8 @@ static int efi_read_time(struct device *dev, struct rtc_time *tm)
return -EINVAL;
}
- convert_from_efi_time(&eft, tm);
+ if (!convert_from_efi_time(&eft, tm))
+ return -EIO;
return rtc_valid_tm(tm);
}
@@ -210,6 +232,8 @@ static struct platform_driver efi_rtc_driver = {
module_platform_driver_probe(efi_rtc_driver, efi_rtc_probe);
+MODULE_ALIAS("platform:rtc-efi");
MODULE_AUTHOR("dann frazier <dannf@hp.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EFI RTC driver");
+MODULE_ALIAS("platform:rtc-efi");
diff --git a/drivers/rtc/rtc-isl12022.c b/drivers/rtc/rtc-isl12022.c
index 03b89112942..ee3ba7e6b45 100644
--- a/drivers/rtc/rtc-isl12022.c
+++ b/drivers/rtc/rtc-isl12022.c
@@ -17,6 +17,8 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#define DRV_VERSION "0.1"
@@ -271,6 +273,13 @@ static int isl12022_probe(struct i2c_client *client,
return PTR_ERR_OR_ZERO(isl12022->rtc);
}
+#ifdef CONFIG_OF
+static const struct of_device_id isl12022_dt_match[] = {
+ { .compatible = "isl,isl12022" },
+ { },
+};
+#endif
+
static const struct i2c_device_id isl12022_id[] = {
{ "isl12022", 0 },
{ }
@@ -280,6 +289,9 @@ MODULE_DEVICE_TABLE(i2c, isl12022_id);
static struct i2c_driver isl12022_driver = {
.driver = {
.name = "rtc-isl12022",
+#ifdef CONFIG_OF
+ .of_match_table = of_match_ptr(isl12022_dt_match),
+#endif
},
.probe = isl12022_probe,
.id_table = isl12022_id,
diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c
index 9efe118a28b..cf73e969c8c 100644
--- a/drivers/rtc/rtc-max77686.c
+++ b/drivers/rtc/rtc-max77686.c
@@ -32,15 +32,6 @@
#define RTC_UDR_MASK (1 << RTC_UDR_SHIFT)
#define RTC_RBUDR_SHIFT 4
#define RTC_RBUDR_MASK (1 << RTC_RBUDR_SHIFT)
-/* WTSR and SMPL Register */
-#define WTSRT_SHIFT 0
-#define SMPLT_SHIFT 2
-#define WTSR_EN_SHIFT 6
-#define SMPL_EN_SHIFT 7
-#define WTSRT_MASK (3 << WTSRT_SHIFT)
-#define SMPLT_MASK (3 << SMPLT_SHIFT)
-#define WTSR_EN_MASK (1 << WTSR_EN_SHIFT)
-#define SMPL_EN_MASK (1 << SMPL_EN_SHIFT)
/* RTC Hour register */
#define HOUR_PM_SHIFT 6
#define HOUR_PM_MASK (1 << HOUR_PM_SHIFT)
@@ -49,7 +40,6 @@
#define ALARM_ENABLE_MASK (1 << ALARM_ENABLE_SHIFT)
#define MAX77686_RTC_UPDATE_DELAY 16
-#undef MAX77686_RTC_WTSR_SMPL
enum {
RTC_SEC = 0,
@@ -80,16 +70,6 @@ enum MAX77686_RTC_OP {
MAX77686_RTC_READ,
};
-static inline int max77686_rtc_calculate_wday(u8 shifted)
-{
- int counter = -1;
- while (shifted) {
- shifted >>= 1;
- counter++;
- }
- return counter;
-}
-
static void max77686_rtc_data_to_tm(u8 *data, struct rtc_time *tm,
int rtc_24hr_mode)
{
@@ -103,7 +83,8 @@ static void max77686_rtc_data_to_tm(u8 *data, struct rtc_time *tm,
tm->tm_hour += 12;
}
- tm->tm_wday = max77686_rtc_calculate_wday(data[RTC_WEEKDAY] & 0x7f);
+ /* Only a single bit is set in data[], so fls() would be equivalent */
+ tm->tm_wday = ffs(data[RTC_WEEKDAY] & 0x7f) - 1;
tm->tm_mday = data[RTC_DATE] & 0x1f;
tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1;
tm->tm_year = (data[RTC_YEAR] & 0x7f) + 100;
@@ -412,64 +393,6 @@ static const struct rtc_class_ops max77686_rtc_ops = {
.alarm_irq_enable = max77686_rtc_alarm_irq_enable,
};
-#ifdef MAX77686_RTC_WTSR_SMPL
-static void max77686_rtc_enable_wtsr(struct max77686_rtc_info *info, bool enable)
-{
- int ret;
- unsigned int val, mask;
-
- if (enable)
- val = (1 << WTSR_EN_SHIFT) | (3 << WTSRT_SHIFT);
- else
- val = 0;
-
- mask = WTSR_EN_MASK | WTSRT_MASK;
-
- dev_info(info->dev, "%s: %s WTSR\n", __func__,
- enable ? "enable" : "disable");
-
- ret = regmap_update_bits(info->max77686->rtc_regmap,
- MAX77686_WTSR_SMPL_CNTL, mask, val);
- if (ret < 0) {
- dev_err(info->dev, "%s: fail to update WTSR reg(%d)\n",
- __func__, ret);
- return;
- }
-
- max77686_rtc_update(info, MAX77686_RTC_WRITE);
-}
-
-static void max77686_rtc_enable_smpl(struct max77686_rtc_info *info, bool enable)
-{
- int ret;
- unsigned int val, mask;
-
- if (enable)
- val = (1 << SMPL_EN_SHIFT) | (0 << SMPLT_SHIFT);
- else
- val = 0;
-
- mask = SMPL_EN_MASK | SMPLT_MASK;
-
- dev_info(info->dev, "%s: %s SMPL\n", __func__,
- enable ? "enable" : "disable");
-
- ret = regmap_update_bits(info->max77686->rtc_regmap,
- MAX77686_WTSR_SMPL_CNTL, mask, val);
- if (ret < 0) {
- dev_err(info->dev, "%s: fail to update SMPL reg(%d)\n",
- __func__, ret);
- return;
- }
-
- max77686_rtc_update(info, MAX77686_RTC_WRITE);
-
- val = 0;
- regmap_read(info->max77686->rtc_regmap, MAX77686_WTSR_SMPL_CNTL, &val);
- dev_info(info->dev, "%s: WTSR_SMPL(0x%02x)\n", __func__, val);
-}
-#endif /* MAX77686_RTC_WTSR_SMPL */
-
static int max77686_rtc_init_reg(struct max77686_rtc_info *info)
{
u8 data[2];
@@ -492,16 +415,11 @@ static int max77686_rtc_init_reg(struct max77686_rtc_info *info)
return ret;
}
-static struct regmap_config max77686_rtc_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
-};
-
static int max77686_rtc_probe(struct platform_device *pdev)
{
struct max77686_dev *max77686 = dev_get_drvdata(pdev->dev.parent);
struct max77686_rtc_info *info;
- int ret, virq;
+ int ret;
dev_info(&pdev->dev, "%s\n", __func__);
@@ -514,14 +432,7 @@ static int max77686_rtc_probe(struct platform_device *pdev)
info->dev = &pdev->dev;
info->max77686 = max77686;
info->rtc = max77686->rtc;
- info->max77686->rtc_regmap = devm_regmap_init_i2c(info->max77686->rtc,
- &max77686_rtc_regmap_config);
- if (IS_ERR(info->max77686->rtc_regmap)) {
- ret = PTR_ERR(info->max77686->rtc_regmap);
- dev_err(info->max77686->dev, "Failed to allocate register map: %d\n",
- ret);
- return ret;
- }
+
platform_set_drvdata(pdev, info);
ret = max77686_rtc_init_reg(info);
@@ -531,34 +442,34 @@ static int max77686_rtc_probe(struct platform_device *pdev)
goto err_rtc;
}
-#ifdef MAX77686_RTC_WTSR_SMPL
- max77686_rtc_enable_wtsr(info, true);
- max77686_rtc_enable_smpl(info, true);
-#endif
-
device_init_wakeup(&pdev->dev, 1);
info->rtc_dev = devm_rtc_device_register(&pdev->dev, "max77686-rtc",
&max77686_rtc_ops, THIS_MODULE);
if (IS_ERR(info->rtc_dev)) {
- dev_info(&pdev->dev, "%s: fail\n", __func__);
-
ret = PTR_ERR(info->rtc_dev);
dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
if (ret == 0)
ret = -EINVAL;
goto err_rtc;
}
- virq = irq_create_mapping(max77686->irq_domain, MAX77686_RTCIRQ_RTCA1);
- if (!virq) {
+
+ if (!max77686->rtc_irq_data) {
+ ret = -EINVAL;
+ dev_err(&pdev->dev, "%s: no RTC regmap IRQ chip\n", __func__);
+ goto err_rtc;
+ }
+
+ info->virq = regmap_irq_get_virq(max77686->rtc_irq_data,
+ MAX77686_RTCIRQ_RTCA1);
+ if (!info->virq) {
ret = -ENXIO;
goto err_rtc;
}
- info->virq = virq;
- ret = devm_request_threaded_irq(&pdev->dev, virq, NULL,
- max77686_rtc_alarm_irq, 0, "rtc-alarm0", info);
+ ret = devm_request_threaded_irq(&pdev->dev, info->virq, NULL,
+ max77686_rtc_alarm_irq, 0, "rtc-alarm1", info);
if (ret < 0)
dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
info->virq, ret);
@@ -567,33 +478,33 @@ err_rtc:
return ret;
}
-static void max77686_rtc_shutdown(struct platform_device *pdev)
+#ifdef CONFIG_PM_SLEEP
+static int max77686_rtc_suspend(struct device *dev)
{
-#ifdef MAX77686_RTC_WTSR_SMPL
- struct max77686_rtc_info *info = platform_get_drvdata(pdev);
- int i;
- u8 val = 0;
-
- for (i = 0; i < 3; i++) {
- max77686_rtc_enable_wtsr(info, false);
- regmap_read(info->max77686->rtc_regmap, MAX77686_WTSR_SMPL_CNTL, &val);
- dev_info(info->dev, "%s: WTSR_SMPL reg(0x%02x)\n", __func__,
- val);
- if (val & WTSR_EN_MASK) {
- dev_emerg(info->dev, "%s: fail to disable WTSR\n",
- __func__);
- } else {
- dev_info(info->dev, "%s: success to disable WTSR\n",
- __func__);
- break;
- }
+ if (device_may_wakeup(dev)) {
+ struct max77686_rtc_info *info = dev_get_drvdata(dev);
+
+ return enable_irq_wake(info->virq);
}
- /* Disable SMPL when power off */
- max77686_rtc_enable_smpl(info, false);
-#endif /* MAX77686_RTC_WTSR_SMPL */
+ return 0;
}
+static int max77686_rtc_resume(struct device *dev)
+{
+ if (device_may_wakeup(dev)) {
+ struct max77686_rtc_info *info = dev_get_drvdata(dev);
+
+ return disable_irq_wake(info->virq);
+ }
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(max77686_rtc_pm_ops,
+ max77686_rtc_suspend, max77686_rtc_resume);
+
static const struct platform_device_id rtc_id[] = {
{ "max77686-rtc", 0 },
{},
@@ -603,9 +514,9 @@ static struct platform_driver max77686_rtc_driver = {
.driver = {
.name = "max77686-rtc",
.owner = THIS_MODULE,
+ .pm = &max77686_rtc_pm_ops,
},
.probe = max77686_rtc_probe,
- .shutdown = max77686_rtc_shutdown,
.id_table = rtc_id,
};
diff --git a/drivers/rtc/rtc-max77802.c b/drivers/rtc/rtc-max77802.c
new file mode 100644
index 00000000000..566471335b3
--- /dev/null
+++ b/drivers/rtc/rtc-max77802.c
@@ -0,0 +1,502 @@
+/*
+ * RTC driver for Maxim MAX77802
+ *
+ * Copyright (C) 2013 Google, Inc
+ *
+ * Copyright (C) 2012 Samsung Electronics Co.Ltd
+ *
+ * based on rtc-max8997.c
+ *
+ * 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/slab.h>
+#include <linux/rtc.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/max77686-private.h>
+#include <linux/irqdomain.h>
+#include <linux/regmap.h>
+
+/* RTC Control Register */
+#define BCD_EN_SHIFT 0
+#define BCD_EN_MASK (1 << BCD_EN_SHIFT)
+#define MODEL24_SHIFT 1
+#define MODEL24_MASK (1 << MODEL24_SHIFT)
+/* RTC Update Register1 */
+#define RTC_UDR_SHIFT 0
+#define RTC_UDR_MASK (1 << RTC_UDR_SHIFT)
+#define RTC_RBUDR_SHIFT 4
+#define RTC_RBUDR_MASK (1 << RTC_RBUDR_SHIFT)
+/* RTC Hour register */
+#define HOUR_PM_SHIFT 6
+#define HOUR_PM_MASK (1 << HOUR_PM_SHIFT)
+/* RTC Alarm Enable */
+#define ALARM_ENABLE_SHIFT 7
+#define ALARM_ENABLE_MASK (1 << ALARM_ENABLE_SHIFT)
+
+/* For the RTCAE1 register, we write this value to enable the alarm */
+#define ALARM_ENABLE_VALUE 0x77
+
+#define MAX77802_RTC_UPDATE_DELAY_US 200
+
+enum {
+ RTC_SEC = 0,
+ RTC_MIN,
+ RTC_HOUR,
+ RTC_WEEKDAY,
+ RTC_MONTH,
+ RTC_YEAR,
+ RTC_DATE,
+ RTC_NR_TIME
+};
+
+struct max77802_rtc_info {
+ struct device *dev;
+ struct max77686_dev *max77802;
+ struct i2c_client *rtc;
+ struct rtc_device *rtc_dev;
+ struct mutex lock;
+
+ struct regmap *regmap;
+
+ int virq;
+ int rtc_24hr_mode;
+};
+
+enum MAX77802_RTC_OP {
+ MAX77802_RTC_WRITE,
+ MAX77802_RTC_READ,
+};
+
+static void max77802_rtc_data_to_tm(u8 *data, struct rtc_time *tm,
+ int rtc_24hr_mode)
+{
+ tm->tm_sec = data[RTC_SEC] & 0xff;
+ tm->tm_min = data[RTC_MIN] & 0xff;
+ if (rtc_24hr_mode)
+ tm->tm_hour = data[RTC_HOUR] & 0x1f;
+ else {
+ tm->tm_hour = data[RTC_HOUR] & 0x0f;
+ if (data[RTC_HOUR] & HOUR_PM_MASK)
+ tm->tm_hour += 12;
+ }
+
+ /* Only a single bit is set in data[], so fls() would be equivalent */
+ tm->tm_wday = ffs(data[RTC_WEEKDAY] & 0xff) - 1;
+ tm->tm_mday = data[RTC_DATE] & 0x1f;
+ tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1;
+
+ tm->tm_year = data[RTC_YEAR] & 0xff;
+ tm->tm_yday = 0;
+ tm->tm_isdst = 0;
+}
+
+static int max77802_rtc_tm_to_data(struct rtc_time *tm, u8 *data)
+{
+ data[RTC_SEC] = tm->tm_sec;
+ data[RTC_MIN] = tm->tm_min;
+ data[RTC_HOUR] = tm->tm_hour;
+ data[RTC_WEEKDAY] = 1 << tm->tm_wday;
+ data[RTC_DATE] = tm->tm_mday;
+ data[RTC_MONTH] = tm->tm_mon + 1;
+ data[RTC_YEAR] = tm->tm_year;
+
+ return 0;
+}
+
+static int max77802_rtc_update(struct max77802_rtc_info *info,
+ enum MAX77802_RTC_OP op)
+{
+ int ret;
+ unsigned int data;
+
+ if (op == MAX77802_RTC_WRITE)
+ data = 1 << RTC_UDR_SHIFT;
+ else
+ data = 1 << RTC_RBUDR_SHIFT;
+
+ ret = regmap_update_bits(info->max77802->regmap,
+ MAX77802_RTC_UPDATE0, data, data);
+ if (ret < 0)
+ dev_err(info->dev, "%s: fail to write update reg(ret=%d, data=0x%x)\n",
+ __func__, ret, data);
+ else {
+ /* Minimum delay required before RTC update. */
+ usleep_range(MAX77802_RTC_UPDATE_DELAY_US,
+ MAX77802_RTC_UPDATE_DELAY_US * 2);
+ }
+
+ return ret;
+}
+
+static int max77802_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max77802_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[RTC_NR_TIME];
+ int ret;
+
+ mutex_lock(&info->lock);
+
+ ret = max77802_rtc_update(info, MAX77802_RTC_READ);
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_bulk_read(info->max77802->regmap,
+ MAX77802_RTC_SEC, data, RTC_NR_TIME);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to read time reg(%d)\n", __func__,
+ ret);
+ goto out;
+ }
+
+ max77802_rtc_data_to_tm(data, tm, info->rtc_24hr_mode);
+
+ ret = rtc_valid_tm(tm);
+
+out:
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int max77802_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max77802_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[RTC_NR_TIME];
+ int ret;
+
+ ret = max77802_rtc_tm_to_data(tm, data);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&info->lock);
+
+ ret = regmap_bulk_write(info->max77802->regmap,
+ MAX77802_RTC_SEC, data, RTC_NR_TIME);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to write time reg(%d)\n", __func__,
+ ret);
+ goto out;
+ }
+
+ ret = max77802_rtc_update(info, MAX77802_RTC_WRITE);
+
+out:
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int max77802_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max77802_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[RTC_NR_TIME];
+ unsigned int val;
+ int ret;
+
+ mutex_lock(&info->lock);
+
+ ret = max77802_rtc_update(info, MAX77802_RTC_READ);
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_bulk_read(info->max77802->regmap,
+ MAX77802_ALARM1_SEC, data, RTC_NR_TIME);
+ if (ret < 0) {
+ dev_err(info->dev, "%s:%d fail to read alarm reg(%d)\n",
+ __func__, __LINE__, ret);
+ goto out;
+ }
+
+ max77802_rtc_data_to_tm(data, &alrm->time, info->rtc_24hr_mode);
+
+ alrm->enabled = 0;
+ ret = regmap_read(info->max77802->regmap,
+ MAX77802_RTC_AE1, &val);
+ if (ret < 0) {
+ dev_err(info->dev, "%s:%d fail to read alarm enable(%d)\n",
+ __func__, __LINE__, ret);
+ goto out;
+ }
+ if (val)
+ alrm->enabled = 1;
+
+ alrm->pending = 0;
+ ret = regmap_read(info->max77802->regmap, MAX77802_REG_STATUS2, &val);
+ if (ret < 0) {
+ dev_err(info->dev, "%s:%d fail to read status2 reg(%d)\n",
+ __func__, __LINE__, ret);
+ goto out;
+ }
+
+ if (val & (1 << 2)) /* RTCA1 */
+ alrm->pending = 1;
+
+out:
+ mutex_unlock(&info->lock);
+ return 0;
+}
+
+static int max77802_rtc_stop_alarm(struct max77802_rtc_info *info)
+{
+ int ret;
+
+ if (!mutex_is_locked(&info->lock))
+ dev_warn(info->dev, "%s: should have mutex locked\n", __func__);
+
+ ret = max77802_rtc_update(info, MAX77802_RTC_READ);
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_write(info->max77802->regmap,
+ MAX77802_RTC_AE1, 0);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
+ __func__, ret);
+ goto out;
+ }
+
+ ret = max77802_rtc_update(info, MAX77802_RTC_WRITE);
+out:
+ return ret;
+}
+
+static int max77802_rtc_start_alarm(struct max77802_rtc_info *info)
+{
+ int ret;
+
+ if (!mutex_is_locked(&info->lock))
+ dev_warn(info->dev, "%s: should have mutex locked\n",
+ __func__);
+
+ ret = max77802_rtc_update(info, MAX77802_RTC_READ);
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_write(info->max77802->regmap,
+ MAX77802_RTC_AE1,
+ ALARM_ENABLE_VALUE);
+
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to read alarm reg(%d)\n",
+ __func__, ret);
+ goto out;
+ }
+
+ ret = max77802_rtc_update(info, MAX77802_RTC_WRITE);
+out:
+ return ret;
+}
+
+static int max77802_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max77802_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[RTC_NR_TIME];
+ int ret;
+
+ ret = max77802_rtc_tm_to_data(&alrm->time, data);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&info->lock);
+
+ ret = max77802_rtc_stop_alarm(info);
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_bulk_write(info->max77802->regmap,
+ MAX77802_ALARM1_SEC, data, RTC_NR_TIME);
+
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
+ __func__, ret);
+ goto out;
+ }
+
+ ret = max77802_rtc_update(info, MAX77802_RTC_WRITE);
+ if (ret < 0)
+ goto out;
+
+ if (alrm->enabled)
+ ret = max77802_rtc_start_alarm(info);
+out:
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int max77802_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct max77802_rtc_info *info = dev_get_drvdata(dev);
+ int ret;
+
+ mutex_lock(&info->lock);
+ if (enabled)
+ ret = max77802_rtc_start_alarm(info);
+ else
+ ret = max77802_rtc_stop_alarm(info);
+ mutex_unlock(&info->lock);
+
+ return ret;
+}
+
+static irqreturn_t max77802_rtc_alarm_irq(int irq, void *data)
+{
+ struct max77802_rtc_info *info = data;
+
+ dev_dbg(info->dev, "%s:irq(%d)\n", __func__, irq);
+
+ rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops max77802_rtc_ops = {
+ .read_time = max77802_rtc_read_time,
+ .set_time = max77802_rtc_set_time,
+ .read_alarm = max77802_rtc_read_alarm,
+ .set_alarm = max77802_rtc_set_alarm,
+ .alarm_irq_enable = max77802_rtc_alarm_irq_enable,
+};
+
+static int max77802_rtc_init_reg(struct max77802_rtc_info *info)
+{
+ u8 data[2];
+ int ret;
+
+ max77802_rtc_update(info, MAX77802_RTC_READ);
+
+ /* Set RTC control register : Binary mode, 24hour mdoe */
+ data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
+ data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
+
+ info->rtc_24hr_mode = 1;
+
+ ret = regmap_bulk_write(info->max77802->regmap,
+ MAX77802_RTC_CONTROLM, data, ARRAY_SIZE(data));
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to write controlm reg(%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = max77802_rtc_update(info, MAX77802_RTC_WRITE);
+ return ret;
+}
+
+static int max77802_rtc_probe(struct platform_device *pdev)
+{
+ struct max77686_dev *max77802 = dev_get_drvdata(pdev->dev.parent);
+ struct max77802_rtc_info *info;
+ int ret;
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ info = devm_kzalloc(&pdev->dev, sizeof(struct max77802_rtc_info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ mutex_init(&info->lock);
+ info->dev = &pdev->dev;
+ info->max77802 = max77802;
+ info->rtc = max77802->i2c;
+
+ platform_set_drvdata(pdev, info);
+
+ ret = max77802_rtc_init_reg(info);
+
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to initialize RTC reg:%d\n", ret);
+ return ret;
+ }
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ info->rtc_dev = devm_rtc_device_register(&pdev->dev, "max77802-rtc",
+ &max77802_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);
+ if (ret == 0)
+ ret = -EINVAL;
+ return ret;
+ }
+
+ if (!max77802->rtc_irq_data) {
+ dev_err(&pdev->dev, "No RTC regmap IRQ chip\n");
+ return -EINVAL;
+ }
+
+ info->virq = regmap_irq_get_virq(max77802->rtc_irq_data,
+ MAX77686_RTCIRQ_RTCA1);
+
+ if (info->virq <= 0) {
+ dev_err(&pdev->dev, "Failed to get virtual IRQ %d\n",
+ MAX77686_RTCIRQ_RTCA1);
+ return -EINVAL;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, info->virq, NULL,
+ max77802_rtc_alarm_irq, 0, "rtc-alarm1",
+ info);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
+ info->virq, ret);
+
+ return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int max77802_rtc_suspend(struct device *dev)
+{
+ if (device_may_wakeup(dev)) {
+ struct max77802_rtc_info *info = dev_get_drvdata(dev);
+
+ return enable_irq_wake(info->virq);
+ }
+
+ return 0;
+}
+
+static int max77802_rtc_resume(struct device *dev)
+{
+ if (device_may_wakeup(dev)) {
+ struct max77802_rtc_info *info = dev_get_drvdata(dev);
+
+ return disable_irq_wake(info->virq);
+ }
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(max77802_rtc_pm_ops,
+ max77802_rtc_suspend, max77802_rtc_resume);
+
+static const struct platform_device_id rtc_id[] = {
+ { "max77802-rtc", 0 },
+ {},
+};
+
+static struct platform_driver max77802_rtc_driver = {
+ .driver = {
+ .name = "max77802-rtc",
+ .owner = THIS_MODULE,
+ .pm = &max77802_rtc_pm_ops,
+ },
+ .probe = max77802_rtc_probe,
+ .id_table = rtc_id,
+};
+
+module_platform_driver(max77802_rtc_driver);
+
+MODULE_DESCRIPTION("Maxim MAX77802 RTC driver");
+MODULE_AUTHOR("Simon Glass <sjg@chromium.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c
index dc4f14255cc..3b965ad6f4d 100644
--- a/drivers/rtc/rtc-mpc5121.c
+++ b/drivers/rtc/rtc-mpc5121.c
@@ -401,7 +401,7 @@ static int mpc5121_rtc_remove(struct platform_device *op)
}
#ifdef CONFIG_OF
-static struct of_device_id mpc5121_rtc_match[] = {
+static const struct of_device_id mpc5121_rtc_match[] = {
{ .compatible = "fsl,mpc5121-rtc", },
{ .compatible = "fsl,mpc5200-rtc", },
{},
diff --git a/drivers/rtc/rtc-pcf85063.c b/drivers/rtc/rtc-pcf85063.c
new file mode 100644
index 00000000000..6a12bf62c50
--- /dev/null
+++ b/drivers/rtc/rtc-pcf85063.c
@@ -0,0 +1,204 @@
+/*
+ * An I2C driver for the PCF85063 RTC
+ * Copyright 2014 Rose Technology
+ *
+ * Author: Søren Andersen <san@rosetechnology.dk>
+ * Maintainers: http://www.nslu2-linux.org/
+ *
+ * based on the other drivers in this same directory.
+ *
+ * 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/module.h>
+
+#define DRV_VERSION "0.0.1"
+
+#define PCF85063_REG_CTRL1 0x00 /* status */
+#define PCF85063_REG_CTRL2 0x01
+
+#define PCF85063_REG_SC 0x04 /* datetime */
+#define PCF85063_REG_MN 0x05
+#define PCF85063_REG_HR 0x06
+#define PCF85063_REG_DM 0x07
+#define PCF85063_REG_DW 0x08
+#define PCF85063_REG_MO 0x09
+#define PCF85063_REG_YR 0x0A
+
+#define PCF85063_MO_C 0x80 /* century */
+
+static struct i2c_driver pcf85063_driver;
+
+struct pcf85063 {
+ struct rtc_device *rtc;
+ int c_polarity; /* 0: MO_C=1 means 19xx, otherwise MO_C=1 means 20xx */
+ int voltage_low; /* indicates if a low_voltage was detected */
+};
+
+/*
+ * In the routines that deal directly with the pcf85063 hardware, we use
+ * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
+ */
+static int pcf85063_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ struct pcf85063 *pcf85063 = i2c_get_clientdata(client);
+ unsigned char buf[13] = { PCF85063_REG_CTRL1 };
+ struct i2c_msg msgs[] = {
+ {/* setup read ptr */
+ .addr = client->addr,
+ .len = 1,
+ .buf = buf
+ },
+ {/* read status + date */
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 13,
+ .buf = buf
+ },
+ };
+
+ /* read registers */
+ if ((i2c_transfer(client->adapter, msgs, 2)) != 2) {
+ dev_err(&client->dev, "%s: read error\n", __func__);
+ return -EIO;
+ }
+
+ tm->tm_sec = bcd2bin(buf[PCF85063_REG_SC] & 0x7F);
+ tm->tm_min = bcd2bin(buf[PCF85063_REG_MN] & 0x7F);
+ tm->tm_hour = bcd2bin(buf[PCF85063_REG_HR] & 0x3F); /* rtc hr 0-23 */
+ tm->tm_mday = bcd2bin(buf[PCF85063_REG_DM] & 0x3F);
+ tm->tm_wday = buf[PCF85063_REG_DW] & 0x07;
+ tm->tm_mon = bcd2bin(buf[PCF85063_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
+ tm->tm_year = bcd2bin(buf[PCF85063_REG_YR]);
+ if (tm->tm_year < 70)
+ tm->tm_year += 100; /* assume we are in 1970...2069 */
+ /* detect the polarity heuristically. see note above. */
+ pcf85063->c_polarity = (buf[PCF85063_REG_MO] & PCF85063_MO_C) ?
+ (tm->tm_year >= 100) : (tm->tm_year < 100);
+
+ /* 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/time is not valid.\n");
+
+ return 0;
+}
+
+static int pcf85063_set_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ int i = 0, err = 0;
+ unsigned char buf[11];
+
+ /* Control & status */
+ buf[PCF85063_REG_CTRL1] = 0;
+ buf[PCF85063_REG_CTRL2] = 5;
+
+ /* hours, minutes and seconds */
+ buf[PCF85063_REG_SC] = bin2bcd(tm->tm_sec) & 0x7F;
+
+ buf[PCF85063_REG_MN] = bin2bcd(tm->tm_min);
+ buf[PCF85063_REG_HR] = bin2bcd(tm->tm_hour);
+
+ /* Day of month, 1 - 31 */
+ buf[PCF85063_REG_DM] = bin2bcd(tm->tm_mday);
+
+ /* Day, 0 - 6 */
+ buf[PCF85063_REG_DW] = tm->tm_wday & 0x07;
+
+ /* month, 1 - 12 */
+ buf[PCF85063_REG_MO] = bin2bcd(tm->tm_mon + 1);
+
+ /* year and century */
+ buf[PCF85063_REG_YR] = bin2bcd(tm->tm_year % 100);
+
+ /* write register's data */
+ for (i = 0; i < sizeof(buf); i++) {
+ unsigned char data[2] = { i, buf[i] };
+
+ 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;
+}
+
+static int pcf85063_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ return pcf85063_get_datetime(to_i2c_client(dev), tm);
+}
+
+static int pcf85063_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ return pcf85063_set_datetime(to_i2c_client(dev), tm);
+}
+
+static const struct rtc_class_ops pcf85063_rtc_ops = {
+ .read_time = pcf85063_rtc_read_time,
+ .set_time = pcf85063_rtc_set_time
+};
+
+static int pcf85063_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pcf85063 *pcf85063;
+
+ dev_dbg(&client->dev, "%s\n", __func__);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ pcf85063 = devm_kzalloc(&client->dev, sizeof(struct pcf85063),
+ GFP_KERNEL);
+ if (!pcf85063)
+ return -ENOMEM;
+
+ dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
+
+ i2c_set_clientdata(client, pcf85063);
+
+ pcf85063->rtc = devm_rtc_device_register(&client->dev,
+ pcf85063_driver.driver.name,
+ &pcf85063_rtc_ops, THIS_MODULE);
+
+ return PTR_ERR_OR_ZERO(pcf85063->rtc);
+}
+
+static const struct i2c_device_id pcf85063_id[] = {
+ { "pcf85063", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pcf85063_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcf85063_of_match[] = {
+ { .compatible = "nxp,pcf85063" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, pcf85063_of_match);
+#endif
+
+static struct i2c_driver pcf85063_driver = {
+ .driver = {
+ .name = "rtc-pcf85063",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(pcf85063_of_match),
+ },
+ .probe = pcf85063_probe,
+ .id_table = pcf85063_id,
+};
+
+module_i2c_driver(pcf85063_driver);
+
+MODULE_AUTHOR("Søren Andersen <san@rosetechnology.dk>");
+MODULE_DESCRIPTION("PCF85063 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c
index 63b558c4819..c2ef0a22ee9 100644
--- a/drivers/rtc/rtc-pcf8563.c
+++ b/drivers/rtc/rtc-pcf8563.c
@@ -26,6 +26,8 @@
#define PCF8563_REG_ST1 0x00 /* status */
#define PCF8563_REG_ST2 0x01
+#define PCF8563_BIT_AIE (1 << 1)
+#define PCF8563_BIT_AF (1 << 3)
#define PCF8563_REG_SC 0x02 /* datetime */
#define PCF8563_REG_MN 0x03
@@ -36,9 +38,6 @@
#define PCF8563_REG_YR 0x08
#define PCF8563_REG_AMN 0x09 /* alarm */
-#define PCF8563_REG_AHR 0x0A
-#define PCF8563_REG_ADM 0x0B
-#define PCF8563_REG_ADW 0x0C
#define PCF8563_REG_CLKO 0x0D /* clock out */
#define PCF8563_REG_TMRC 0x0E /* timer control */
@@ -67,37 +66,133 @@ struct pcf8563 {
*/
int c_polarity; /* 0: MO_C=1 means 19xx, otherwise MO_C=1 means 20xx */
int voltage_low; /* incicates if a low_voltage was detected */
+
+ struct i2c_client *client;
};
-/*
- * In the routines that deal directly with the pcf8563 hardware, we use
- * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
- */
-static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+static int pcf8563_read_block_data(struct i2c_client *client, unsigned char reg,
+ unsigned char length, unsigned char *buf)
{
- struct pcf8563 *pcf8563 = i2c_get_clientdata(client);
- unsigned char buf[13] = { PCF8563_REG_ST1 };
-
struct i2c_msg msgs[] = {
{/* setup read ptr */
.addr = client->addr,
.len = 1,
- .buf = buf
+ .buf = &reg,
},
- {/* read status + date */
+ {
.addr = client->addr,
.flags = I2C_M_RD,
- .len = 13,
+ .len = length,
.buf = buf
},
};
- /* read registers */
if ((i2c_transfer(client->adapter, msgs, 2)) != 2) {
dev_err(&client->dev, "%s: read error\n", __func__);
return -EIO;
}
+ return 0;
+}
+
+static int pcf8563_write_block_data(struct i2c_client *client,
+ unsigned char reg, unsigned char length,
+ unsigned char *buf)
+{
+ int i, err;
+
+ for (i = 0; i < length; i++) {
+ unsigned char data[2] = { reg + i, buf[i] };
+
+ 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;
+}
+
+static int pcf8563_set_alarm_mode(struct i2c_client *client, bool on)
+{
+ unsigned char buf[2];
+ int err;
+
+ err = pcf8563_read_block_data(client, PCF8563_REG_ST2, 1, buf + 1);
+ if (err < 0)
+ return err;
+
+ if (on)
+ buf[1] |= PCF8563_BIT_AIE;
+ else
+ buf[1] &= ~PCF8563_BIT_AIE;
+
+ buf[1] &= ~PCF8563_BIT_AF;
+ buf[0] = PCF8563_REG_ST2;
+
+ err = pcf8563_write_block_data(client, PCF8563_REG_ST2, 1, buf + 1);
+ if (err < 0) {
+ dev_err(&client->dev, "%s: write error\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int pcf8563_get_alarm_mode(struct i2c_client *client, unsigned char *en,
+ unsigned char *pen)
+{
+ unsigned char buf;
+ int err;
+
+ err = pcf8563_read_block_data(client, PCF8563_REG_ST2, 1, &buf);
+ if (err)
+ return err;
+
+ if (en)
+ *en = !!(buf & PCF8563_BIT_AIE);
+ if (pen)
+ *pen = !!(buf & PCF8563_BIT_AF);
+
+ return 0;
+}
+
+static irqreturn_t pcf8563_irq(int irq, void *dev_id)
+{
+ struct pcf8563 *pcf8563 = i2c_get_clientdata(dev_id);
+ int err;
+ char pending;
+
+ err = pcf8563_get_alarm_mode(pcf8563->client, NULL, &pending);
+ if (err)
+ return IRQ_NONE;
+
+ if (pending) {
+ rtc_update_irq(pcf8563->rtc, 1, RTC_IRQF | RTC_AF);
+ pcf8563_set_alarm_mode(pcf8563->client, 1);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+/*
+ * In the routines that deal directly with the pcf8563 hardware, we use
+ * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
+ */
+static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ struct pcf8563 *pcf8563 = i2c_get_clientdata(client);
+ unsigned char buf[9];
+ int err;
+
+ err = pcf8563_read_block_data(client, PCF8563_REG_ST1, 9, buf);
+ if (err)
+ return err;
+
if (buf[PCF8563_REG_SC] & PCF8563_SC_LV) {
pcf8563->voltage_low = 1;
dev_info(&client->dev,
@@ -144,7 +239,7 @@ static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm)
static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm)
{
struct pcf8563 *pcf8563 = i2c_get_clientdata(client);
- int i, err;
+ int err;
unsigned char buf[9];
dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
@@ -170,19 +265,10 @@ static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm)
buf[PCF8563_REG_DW] = tm->tm_wday & 0x07;
- /* write register's data */
- for (i = 0; i < 7; i++) {
- unsigned char data[2] = { PCF8563_REG_SC + i,
- buf[PCF8563_REG_SC + i] };
-
- 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;
- }
- }
+ err = pcf8563_write_block_data(client, PCF8563_REG_SC,
+ 9 - PCF8563_REG_SC, buf + PCF8563_REG_SC);
+ if (err)
+ return err;
return 0;
}
@@ -235,16 +321,83 @@ static int pcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm)
return pcf8563_set_datetime(to_i2c_client(dev), tm);
}
+static int pcf8563_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned char buf[4];
+ int err;
+
+ err = pcf8563_read_block_data(client, PCF8563_REG_AMN, 4, buf);
+ if (err)
+ return err;
+
+ dev_dbg(&client->dev,
+ "%s: raw data is min=%02x, hr=%02x, mday=%02x, wday=%02x\n",
+ __func__, buf[0], buf[1], buf[2], buf[3]);
+
+ tm->time.tm_min = bcd2bin(buf[0] & 0x7F);
+ tm->time.tm_hour = bcd2bin(buf[1] & 0x7F);
+ tm->time.tm_mday = bcd2bin(buf[2] & 0x1F);
+ tm->time.tm_wday = bcd2bin(buf[3] & 0x7);
+ tm->time.tm_mon = -1;
+ tm->time.tm_year = -1;
+ tm->time.tm_yday = -1;
+ tm->time.tm_isdst = -1;
+
+ err = pcf8563_get_alarm_mode(client, &tm->enabled, &tm->pending);
+ if (err < 0)
+ return err;
+
+ dev_dbg(&client->dev, "%s: tm is mins=%d, hours=%d, mday=%d, wday=%d,"
+ " enabled=%d, pending=%d\n", __func__, tm->time.tm_min,
+ tm->time.tm_hour, tm->time.tm_mday, tm->time.tm_wday,
+ tm->enabled, tm->pending);
+
+ return 0;
+}
+
+static int pcf8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned char buf[4];
+ int err;
+
+ dev_dbg(dev, "%s, min=%d hour=%d wday=%d mday=%d "
+ "enabled=%d pending=%d\n", __func__,
+ tm->time.tm_min, tm->time.tm_hour, tm->time.tm_wday,
+ tm->time.tm_mday, tm->enabled, tm->pending);
+
+ buf[0] = bin2bcd(tm->time.tm_min);
+ buf[1] = bin2bcd(tm->time.tm_hour);
+ buf[2] = bin2bcd(tm->time.tm_mday);
+ buf[3] = tm->time.tm_wday & 0x07;
+
+ err = pcf8563_write_block_data(client, PCF8563_REG_AMN, 4, buf);
+ if (err)
+ return err;
+
+ return pcf8563_set_alarm_mode(client, 1);
+}
+
+static int pcf8563_irq_enable(struct device *dev, unsigned int enabled)
+{
+ return pcf8563_set_alarm_mode(to_i2c_client(dev), !!enabled);
+}
+
static const struct rtc_class_ops pcf8563_rtc_ops = {
.ioctl = pcf8563_rtc_ioctl,
.read_time = pcf8563_rtc_read_time,
.set_time = pcf8563_rtc_set_time,
+ .read_alarm = pcf8563_rtc_read_alarm,
+ .set_alarm = pcf8563_rtc_set_alarm,
+ .alarm_irq_enable = pcf8563_irq_enable,
};
static int pcf8563_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct pcf8563 *pcf8563;
+ int err;
dev_dbg(&client->dev, "%s\n", __func__);
@@ -259,12 +412,30 @@ static int pcf8563_probe(struct i2c_client *client,
dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
i2c_set_clientdata(client, pcf8563);
+ pcf8563->client = client;
+ device_set_wakeup_capable(&client->dev, 1);
pcf8563->rtc = devm_rtc_device_register(&client->dev,
pcf8563_driver.driver.name,
&pcf8563_rtc_ops, THIS_MODULE);
- return PTR_ERR_OR_ZERO(pcf8563->rtc);
+ if (IS_ERR(pcf8563->rtc))
+ return PTR_ERR(pcf8563->rtc);
+
+ if (client->irq > 0) {
+ err = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, pcf8563_irq,
+ IRQF_SHARED|IRQF_ONESHOT|IRQF_TRIGGER_FALLING,
+ pcf8563->rtc->name, client);
+ if (err) {
+ dev_err(&client->dev, "unable to request IRQ %d\n",
+ client->irq);
+ return err;
+ }
+
+ }
+
+ return 0;
}
static const struct i2c_device_id pcf8563_id[] = {
diff --git a/drivers/rtc/rtc-pcf8583.c b/drivers/rtc/rtc-pcf8583.c
index c2639845186..5911a6dca29 100644
--- a/drivers/rtc/rtc-pcf8583.c
+++ b/drivers/rtc/rtc-pcf8583.c
@@ -176,7 +176,11 @@ static int pcf8583_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct i2c_client *client = to_i2c_client(dev);
unsigned char ctrl, year[2];
- struct rtc_mem mem = { CMOS_YEAR, sizeof(year), year };
+ struct rtc_mem mem = {
+ .loc = CMOS_YEAR,
+ .nr = sizeof(year),
+ .data = year
+ };
int real_year, year_offset, err;
/*
@@ -222,8 +226,16 @@ static int pcf8583_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct i2c_client *client = to_i2c_client(dev);
unsigned char year[2], chk;
- struct rtc_mem cmos_year = { CMOS_YEAR, sizeof(year), year };
- struct rtc_mem cmos_check = { CMOS_CHECKSUM, 1, &chk };
+ struct rtc_mem cmos_year = {
+ .loc = CMOS_YEAR,
+ .nr = sizeof(year),
+ .data = year
+ };
+ struct rtc_mem cmos_check = {
+ .loc = CMOS_CHECKSUM,
+ .nr = 1,
+ .data = &chk
+ };
unsigned int proper_year = tm->tm_year + 1900;
int ret;
diff --git a/drivers/rtc/rtc-pm8xxx.c b/drivers/rtc/rtc-pm8xxx.c
index 197699f358c..5adcf111fc1 100644
--- a/drivers/rtc/rtc-pm8xxx.c
+++ b/drivers/rtc/rtc-pm8xxx.c
@@ -27,21 +27,36 @@
/* RTC_CTRL register bit fields */
#define PM8xxx_RTC_ENABLE BIT(7)
-#define PM8xxx_RTC_ALARM_ENABLE BIT(1)
#define PM8xxx_RTC_ALARM_CLEAR BIT(0)
#define NUM_8_BIT_RTC_REGS 0x4
/**
+ * struct pm8xxx_rtc_regs - describe RTC registers per PMIC versions
+ * @ctrl: base address of control register
+ * @write: base address of write register
+ * @read: base address of read register
+ * @alarm_ctrl: base address of alarm control register
+ * @alarm_ctrl2: base address of alarm control2 register
+ * @alarm_rw: base address of alarm read-write register
+ * @alarm_en: alarm enable mask
+ */
+struct pm8xxx_rtc_regs {
+ unsigned int ctrl;
+ unsigned int write;
+ unsigned int read;
+ unsigned int alarm_ctrl;
+ unsigned int alarm_ctrl2;
+ unsigned int alarm_rw;
+ unsigned int alarm_en;
+};
+
+/**
* struct pm8xxx_rtc - rtc driver internal structure
* @rtc: rtc device for this driver.
* @regmap: regmap used to access RTC registers
* @allow_set_time: indicates whether writing to the RTC is allowed
* @rtc_alarm_irq: rtc alarm irq number.
- * @rtc_base: address of rtc control register.
- * @rtc_read_base: base address of read registers.
- * @rtc_write_base: base address of write registers.
- * @alarm_rw_base: base address of alarm registers.
* @ctrl_reg: rtc control register.
* @rtc_dev: device structure.
* @ctrl_reg_lock: spinlock protecting access to ctrl_reg.
@@ -51,11 +66,7 @@ struct pm8xxx_rtc {
struct regmap *regmap;
bool allow_set_time;
int rtc_alarm_irq;
- int rtc_base;
- int rtc_read_base;
- int rtc_write_base;
- int alarm_rw_base;
- u8 ctrl_reg;
+ const struct pm8xxx_rtc_regs *regs;
struct device *rtc_dev;
spinlock_t ctrl_reg_lock;
};
@@ -71,8 +82,10 @@ static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
int rc, i;
unsigned long secs, irq_flags;
- u8 value[NUM_8_BIT_RTC_REGS], alarm_enabled = 0, ctrl_reg;
+ u8 value[NUM_8_BIT_RTC_REGS], alarm_enabled = 0;
+ unsigned int ctrl_reg;
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
+ const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
if (!rtc_dd->allow_set_time)
return -EACCES;
@@ -87,30 +100,30 @@ static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm)
dev_dbg(dev, "Seconds value to be written to RTC = %lu\n", secs);
spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags);
- ctrl_reg = rtc_dd->ctrl_reg;
- if (ctrl_reg & PM8xxx_RTC_ALARM_ENABLE) {
+ rc = regmap_read(rtc_dd->regmap, regs->ctrl, &ctrl_reg);
+ if (rc)
+ goto rtc_rw_fail;
+
+ if (ctrl_reg & regs->alarm_en) {
alarm_enabled = 1;
- ctrl_reg &= ~PM8xxx_RTC_ALARM_ENABLE;
- rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_base, ctrl_reg);
+ ctrl_reg &= ~regs->alarm_en;
+ rc = regmap_write(rtc_dd->regmap, regs->ctrl, ctrl_reg);
if (rc) {
dev_err(dev, "Write to RTC control register failed\n");
goto rtc_rw_fail;
}
- rtc_dd->ctrl_reg = ctrl_reg;
- } else {
- spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
}
/* Write 0 to Byte[0] */
- rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_write_base, 0);
+ rc = regmap_write(rtc_dd->regmap, regs->write, 0);
if (rc) {
dev_err(dev, "Write to RTC write data register failed\n");
goto rtc_rw_fail;
}
/* Write Byte[1], Byte[2], Byte[3] */
- rc = regmap_bulk_write(rtc_dd->regmap, rtc_dd->rtc_write_base + 1,
+ rc = regmap_bulk_write(rtc_dd->regmap, regs->write + 1,
&value[1], sizeof(value) - 1);
if (rc) {
dev_err(dev, "Write to RTC write data register failed\n");
@@ -118,25 +131,23 @@ static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm)
}
/* Write Byte[0] */
- rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_write_base, value[0]);
+ rc = regmap_write(rtc_dd->regmap, regs->write, value[0]);
if (rc) {
dev_err(dev, "Write to RTC write data register failed\n");
goto rtc_rw_fail;
}
if (alarm_enabled) {
- ctrl_reg |= PM8xxx_RTC_ALARM_ENABLE;
- rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_base, ctrl_reg);
+ ctrl_reg |= regs->alarm_en;
+ rc = regmap_write(rtc_dd->regmap, regs->ctrl, ctrl_reg);
if (rc) {
dev_err(dev, "Write to RTC control register failed\n");
goto rtc_rw_fail;
}
- rtc_dd->ctrl_reg = ctrl_reg;
}
rtc_rw_fail:
- if (alarm_enabled)
- spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
+ spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
return rc;
}
@@ -148,9 +159,9 @@ static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm)
unsigned long secs;
unsigned int reg;
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
+ const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
- rc = regmap_bulk_read(rtc_dd->regmap, rtc_dd->rtc_read_base,
- value, sizeof(value));
+ rc = regmap_bulk_read(rtc_dd->regmap, regs->read, value, sizeof(value));
if (rc) {
dev_err(dev, "RTC read data register failed\n");
return rc;
@@ -160,14 +171,14 @@ static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm)
* Read the LSB again and check if there has been a carry over.
* If there is, redo the read operation.
*/
- rc = regmap_read(rtc_dd->regmap, rtc_dd->rtc_read_base, &reg);
+ rc = regmap_read(rtc_dd->regmap, regs->read, &reg);
if (rc < 0) {
dev_err(dev, "RTC read data register failed\n");
return rc;
}
if (unlikely(reg < value[0])) {
- rc = regmap_bulk_read(rtc_dd->regmap, rtc_dd->rtc_read_base,
+ rc = regmap_bulk_read(rtc_dd->regmap, regs->read,
value, sizeof(value));
if (rc) {
dev_err(dev, "RTC read data register failed\n");
@@ -195,9 +206,11 @@ static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm)
static int pm8xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
int rc, i;
- u8 value[NUM_8_BIT_RTC_REGS], ctrl_reg;
+ u8 value[NUM_8_BIT_RTC_REGS];
+ unsigned int ctrl_reg;
unsigned long secs, irq_flags;
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
+ const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
rtc_tm_to_time(&alarm->time, &secs);
@@ -208,28 +221,28 @@ static int pm8xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags);
- rc = regmap_bulk_write(rtc_dd->regmap, rtc_dd->alarm_rw_base, value,
+ rc = regmap_bulk_write(rtc_dd->regmap, regs->alarm_rw, value,
sizeof(value));
if (rc) {
dev_err(dev, "Write to RTC ALARM register failed\n");
goto rtc_rw_fail;
}
- ctrl_reg = rtc_dd->ctrl_reg;
+ rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl, &ctrl_reg);
+ if (rc)
+ goto rtc_rw_fail;
if (alarm->enabled)
- ctrl_reg |= PM8xxx_RTC_ALARM_ENABLE;
+ ctrl_reg |= regs->alarm_en;
else
- ctrl_reg &= ~PM8xxx_RTC_ALARM_ENABLE;
+ ctrl_reg &= ~regs->alarm_en;
- rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_base, ctrl_reg);
+ rc = regmap_write(rtc_dd->regmap, regs->alarm_ctrl, ctrl_reg);
if (rc) {
- dev_err(dev, "Write to RTC control register failed\n");
+ dev_err(dev, "Write to RTC alarm control register failed\n");
goto rtc_rw_fail;
}
- rtc_dd->ctrl_reg = ctrl_reg;
-
dev_dbg(dev, "Alarm Set for h:r:s=%d:%d:%d, d/m/y=%d/%d/%d\n",
alarm->time.tm_hour, alarm->time.tm_min,
alarm->time.tm_sec, alarm->time.tm_mday,
@@ -245,8 +258,9 @@ static int pm8xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
u8 value[NUM_8_BIT_RTC_REGS];
unsigned long secs;
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
+ const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
- rc = regmap_bulk_read(rtc_dd->regmap, rtc_dd->alarm_rw_base, value,
+ rc = regmap_bulk_read(rtc_dd->regmap, regs->alarm_rw, value,
sizeof(value));
if (rc) {
dev_err(dev, "RTC alarm time read failed\n");
@@ -276,25 +290,26 @@ static int pm8xxx_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
int rc;
unsigned long irq_flags;
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
- u8 ctrl_reg;
+ const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
+ unsigned int ctrl_reg;
spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags);
- ctrl_reg = rtc_dd->ctrl_reg;
+ rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl, &ctrl_reg);
+ if (rc)
+ goto rtc_rw_fail;
if (enable)
- ctrl_reg |= PM8xxx_RTC_ALARM_ENABLE;
+ ctrl_reg |= regs->alarm_en;
else
- ctrl_reg &= ~PM8xxx_RTC_ALARM_ENABLE;
+ ctrl_reg &= ~regs->alarm_en;
- rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_base, ctrl_reg);
+ rc = regmap_write(rtc_dd->regmap, regs->alarm_ctrl, ctrl_reg);
if (rc) {
dev_err(dev, "Write to RTC control register failed\n");
goto rtc_rw_fail;
}
- rtc_dd->ctrl_reg = ctrl_reg;
-
rtc_rw_fail:
spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
return rc;
@@ -311,6 +326,7 @@ static const struct rtc_class_ops pm8xxx_rtc_ops = {
static irqreturn_t pm8xxx_alarm_trigger(int irq, void *dev_id)
{
struct pm8xxx_rtc *rtc_dd = dev_id;
+ const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
unsigned int ctrl_reg;
int rc;
unsigned long irq_flags;
@@ -320,48 +336,100 @@ static irqreturn_t pm8xxx_alarm_trigger(int irq, void *dev_id)
spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags);
/* Clear the alarm enable bit */
- ctrl_reg = rtc_dd->ctrl_reg;
- ctrl_reg &= ~PM8xxx_RTC_ALARM_ENABLE;
+ rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl, &ctrl_reg);
+ if (rc) {
+ spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
+ goto rtc_alarm_handled;
+ }
+
+ ctrl_reg &= ~regs->alarm_en;
- rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_base, ctrl_reg);
+ rc = regmap_write(rtc_dd->regmap, regs->alarm_ctrl, ctrl_reg);
if (rc) {
spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
dev_err(rtc_dd->rtc_dev,
- "Write to RTC control register failed\n");
+ "Write to alarm control register failed\n");
goto rtc_alarm_handled;
}
- rtc_dd->ctrl_reg = ctrl_reg;
spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
/* Clear RTC alarm register */
- rc = regmap_read(rtc_dd->regmap,
- rtc_dd->rtc_base + PM8XXX_ALARM_CTRL_OFFSET,
- &ctrl_reg);
+ rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl2, &ctrl_reg);
if (rc) {
dev_err(rtc_dd->rtc_dev,
- "RTC Alarm control register read failed\n");
+ "RTC Alarm control2 register read failed\n");
goto rtc_alarm_handled;
}
- ctrl_reg &= ~PM8xxx_RTC_ALARM_CLEAR;
- rc = regmap_write(rtc_dd->regmap,
- rtc_dd->rtc_base + PM8XXX_ALARM_CTRL_OFFSET,
- ctrl_reg);
+ ctrl_reg |= PM8xxx_RTC_ALARM_CLEAR;
+ rc = regmap_write(rtc_dd->regmap, regs->alarm_ctrl2, ctrl_reg);
if (rc)
dev_err(rtc_dd->rtc_dev,
- "Write to RTC Alarm control register failed\n");
+ "Write to RTC Alarm control2 register failed\n");
rtc_alarm_handled:
return IRQ_HANDLED;
}
+static int pm8xxx_rtc_enable(struct pm8xxx_rtc *rtc_dd)
+{
+ const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
+ unsigned int ctrl_reg;
+ int rc;
+
+ /* Check if the RTC is on, else turn it on */
+ rc = regmap_read(rtc_dd->regmap, regs->ctrl, &ctrl_reg);
+ if (rc)
+ return rc;
+
+ if (!(ctrl_reg & PM8xxx_RTC_ENABLE)) {
+ ctrl_reg |= PM8xxx_RTC_ENABLE;
+ rc = regmap_write(rtc_dd->regmap, regs->ctrl, ctrl_reg);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+static const struct pm8xxx_rtc_regs pm8921_regs = {
+ .ctrl = 0x11d,
+ .write = 0x11f,
+ .read = 0x123,
+ .alarm_rw = 0x127,
+ .alarm_ctrl = 0x11d,
+ .alarm_ctrl2 = 0x11e,
+ .alarm_en = BIT(1),
+};
+
+static const struct pm8xxx_rtc_regs pm8058_regs = {
+ .ctrl = 0x1e8,
+ .write = 0x1ea,
+ .read = 0x1ee,
+ .alarm_rw = 0x1f2,
+ .alarm_ctrl = 0x1e8,
+ .alarm_ctrl2 = 0x1e9,
+ .alarm_en = BIT(1),
+};
+
+static const struct pm8xxx_rtc_regs pm8941_regs = {
+ .ctrl = 0x6046,
+ .write = 0x6040,
+ .read = 0x6048,
+ .alarm_rw = 0x6140,
+ .alarm_ctrl = 0x6146,
+ .alarm_ctrl2 = 0x6148,
+ .alarm_en = BIT(7),
+};
+
/*
* Hardcoded RTC bases until IORESOURCE_REG mapping is figured out
*/
static const struct of_device_id pm8xxx_id_table[] = {
- { .compatible = "qcom,pm8921-rtc", .data = (void *) 0x11D },
- { .compatible = "qcom,pm8058-rtc", .data = (void *) 0x1E8 },
+ { .compatible = "qcom,pm8921-rtc", .data = &pm8921_regs },
+ { .compatible = "qcom,pm8058-rtc", .data = &pm8058_regs },
+ { .compatible = "qcom,pm8941-rtc", .data = &pm8941_regs },
{ },
};
MODULE_DEVICE_TABLE(of, pm8xxx_id_table);
@@ -369,7 +437,6 @@ MODULE_DEVICE_TABLE(of, pm8xxx_id_table);
static int pm8xxx_rtc_probe(struct platform_device *pdev)
{
int rc;
- unsigned int ctrl_reg;
struct pm8xxx_rtc *rtc_dd;
const struct of_device_id *match;
@@ -399,33 +466,12 @@ static int pm8xxx_rtc_probe(struct platform_device *pdev)
rtc_dd->allow_set_time = of_property_read_bool(pdev->dev.of_node,
"allow-set-time");
- rtc_dd->rtc_base = (long) match->data;
-
- /* Setup RTC register addresses */
- rtc_dd->rtc_write_base = rtc_dd->rtc_base + PM8XXX_RTC_WRITE_OFFSET;
- rtc_dd->rtc_read_base = rtc_dd->rtc_base + PM8XXX_RTC_READ_OFFSET;
- rtc_dd->alarm_rw_base = rtc_dd->rtc_base + PM8XXX_ALARM_RW_OFFSET;
-
+ rtc_dd->regs = match->data;
rtc_dd->rtc_dev = &pdev->dev;
- /* Check if the RTC is on, else turn it on */
- rc = regmap_read(rtc_dd->regmap, rtc_dd->rtc_base, &ctrl_reg);
- if (rc) {
- dev_err(&pdev->dev, "RTC control register read failed!\n");
+ rc = pm8xxx_rtc_enable(rtc_dd);
+ if (rc)
return rc;
- }
-
- if (!(ctrl_reg & PM8xxx_RTC_ENABLE)) {
- ctrl_reg |= PM8xxx_RTC_ENABLE;
- rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_base, ctrl_reg);
- if (rc) {
- dev_err(&pdev->dev,
- "Write to RTC control register failed\n");
- return rc;
- }
- }
-
- rtc_dd->ctrl_reg = ctrl_reg;
platform_set_drvdata(pdev, rtc_dd);
diff --git a/drivers/rtc/rtc-rk808.c b/drivers/rtc/rtc-rk808.c
new file mode 100644
index 00000000000..df42257668a
--- /dev/null
+++ b/drivers/rtc/rtc-rk808.c
@@ -0,0 +1,414 @@
+/*
+ * RTC driver for Rockchip RK808
+ *
+ * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * Author: Chris Zhong <zyw@rock-chips.com>
+ * Author: Zhang Qing <zhangqing@rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/mfd/rk808.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+
+/* RTC_CTRL_REG bitfields */
+#define BIT_RTC_CTRL_REG_STOP_RTC_M BIT(0)
+
+/* RK808 has a shadowed register for saving a "frozen" RTC time.
+ * When user setting "GET_TIME" to 1, the time will save in this shadowed
+ * register. If set "READSEL" to 1, user read rtc time register, actually
+ * get the time of that moment. If we need the real time, clr this bit.
+ */
+#define BIT_RTC_CTRL_REG_RTC_GET_TIME BIT(6)
+#define BIT_RTC_CTRL_REG_RTC_READSEL_M BIT(7)
+#define BIT_RTC_INTERRUPTS_REG_IT_ALARM_M BIT(3)
+#define RTC_STATUS_MASK 0xFE
+
+#define SECONDS_REG_MSK 0x7F
+#define MINUTES_REG_MAK 0x7F
+#define HOURS_REG_MSK 0x3F
+#define DAYS_REG_MSK 0x3F
+#define MONTHS_REG_MSK 0x1F
+#define YEARS_REG_MSK 0xFF
+#define WEEKS_REG_MSK 0x7
+
+/* REG_SECONDS_REG through REG_YEARS_REG is how many registers? */
+
+#define NUM_TIME_REGS (RK808_WEEKS_REG - RK808_SECONDS_REG + 1)
+#define NUM_ALARM_REGS (RK808_ALARM_YEARS_REG - RK808_ALARM_SECONDS_REG + 1)
+
+struct rk808_rtc {
+ struct rk808 *rk808;
+ struct rtc_device *rtc;
+ int irq;
+};
+
+/* Read current time and date in RTC */
+static int rk808_rtc_readtime(struct device *dev, struct rtc_time *tm)
+{
+ struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);
+ struct rk808 *rk808 = rk808_rtc->rk808;
+ u8 rtc_data[NUM_TIME_REGS];
+ int ret;
+
+ /* Force an update of the shadowed registers right now */
+ ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
+ BIT_RTC_CTRL_REG_RTC_GET_TIME,
+ 0);
+ if (ret) {
+ dev_err(dev, "Failed to update bits rtc_ctrl: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
+ BIT_RTC_CTRL_REG_RTC_GET_TIME,
+ BIT_RTC_CTRL_REG_RTC_GET_TIME);
+ if (ret) {
+ dev_err(dev, "Failed to update bits rtc_ctrl: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_bulk_read(rk808->regmap, RK808_SECONDS_REG,
+ rtc_data, NUM_TIME_REGS);
+ if (ret) {
+ dev_err(dev, "Failed to bulk read rtc_data: %d\n", ret);
+ return ret;
+ }
+
+ tm->tm_sec = bcd2bin(rtc_data[0] & SECONDS_REG_MSK);
+ tm->tm_min = bcd2bin(rtc_data[1] & MINUTES_REG_MAK);
+ tm->tm_hour = bcd2bin(rtc_data[2] & HOURS_REG_MSK);
+ tm->tm_mday = bcd2bin(rtc_data[3] & DAYS_REG_MSK);
+ tm->tm_mon = (bcd2bin(rtc_data[4] & MONTHS_REG_MSK)) - 1;
+ tm->tm_year = (bcd2bin(rtc_data[5] & YEARS_REG_MSK)) + 100;
+ tm->tm_wday = bcd2bin(rtc_data[6] & WEEKS_REG_MSK);
+ dev_dbg(dev, "RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n",
+ 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_wday, tm->tm_hour , tm->tm_min, tm->tm_sec);
+
+ return ret;
+}
+
+/* Set current time and date in RTC */
+static int rk808_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);
+ struct rk808 *rk808 = rk808_rtc->rk808;
+ u8 rtc_data[NUM_TIME_REGS];
+ int ret;
+
+ rtc_data[0] = bin2bcd(tm->tm_sec);
+ rtc_data[1] = bin2bcd(tm->tm_min);
+ rtc_data[2] = bin2bcd(tm->tm_hour);
+ rtc_data[3] = bin2bcd(tm->tm_mday);
+ rtc_data[4] = bin2bcd(tm->tm_mon + 1);
+ rtc_data[5] = bin2bcd(tm->tm_year - 100);
+ rtc_data[6] = bin2bcd(tm->tm_wday);
+ dev_dbg(dev, "set RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n",
+ 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_wday, tm->tm_hour , tm->tm_min, tm->tm_sec);
+
+ /* Stop RTC while updating the RTC registers */
+ ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
+ BIT_RTC_CTRL_REG_STOP_RTC_M,
+ BIT_RTC_CTRL_REG_STOP_RTC_M);
+ if (ret) {
+ dev_err(dev, "Failed to update RTC control: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_bulk_write(rk808->regmap, RK808_SECONDS_REG,
+ rtc_data, NUM_TIME_REGS);
+ if (ret) {
+ dev_err(dev, "Failed to bull write rtc_data: %d\n", ret);
+ return ret;
+ }
+ /* Start RTC again */
+ ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
+ BIT_RTC_CTRL_REG_STOP_RTC_M, 0);
+ if (ret) {
+ dev_err(dev, "Failed to update RTC control: %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+/* Read alarm time and date in RTC */
+static int rk808_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);
+ struct rk808 *rk808 = rk808_rtc->rk808;
+ u8 alrm_data[NUM_ALARM_REGS];
+ uint32_t int_reg;
+ int ret;
+
+ ret = regmap_bulk_read(rk808->regmap, RK808_ALARM_SECONDS_REG,
+ alrm_data, NUM_ALARM_REGS);
+
+ alrm->time.tm_sec = bcd2bin(alrm_data[0] & SECONDS_REG_MSK);
+ alrm->time.tm_min = bcd2bin(alrm_data[1] & MINUTES_REG_MAK);
+ alrm->time.tm_hour = bcd2bin(alrm_data[2] & HOURS_REG_MSK);
+ alrm->time.tm_mday = bcd2bin(alrm_data[3] & DAYS_REG_MSK);
+ alrm->time.tm_mon = (bcd2bin(alrm_data[4] & MONTHS_REG_MSK)) - 1;
+ alrm->time.tm_year = (bcd2bin(alrm_data[5] & YEARS_REG_MSK)) + 100;
+
+ ret = regmap_read(rk808->regmap, RK808_RTC_INT_REG, &int_reg);
+ if (ret) {
+ dev_err(dev, "Failed to read RTC INT REG: %d\n", ret);
+ return ret;
+ }
+
+ dev_dbg(dev, "alrm read RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n",
+ 1900 + alrm->time.tm_year, alrm->time.tm_mon + 1,
+ alrm->time.tm_mday, alrm->time.tm_wday, alrm->time.tm_hour,
+ alrm->time.tm_min, alrm->time.tm_sec);
+
+ alrm->enabled = (int_reg & BIT_RTC_INTERRUPTS_REG_IT_ALARM_M) ? 1 : 0;
+
+ return 0;
+}
+
+static int rk808_rtc_stop_alarm(struct rk808_rtc *rk808_rtc)
+{
+ struct rk808 *rk808 = rk808_rtc->rk808;
+ int ret;
+
+ ret = regmap_update_bits(rk808->regmap, RK808_RTC_INT_REG,
+ BIT_RTC_INTERRUPTS_REG_IT_ALARM_M, 0);
+
+ return ret;
+}
+
+static int rk808_rtc_start_alarm(struct rk808_rtc *rk808_rtc)
+{
+ struct rk808 *rk808 = rk808_rtc->rk808;
+ int ret;
+
+ ret = regmap_update_bits(rk808->regmap, RK808_RTC_INT_REG,
+ BIT_RTC_INTERRUPTS_REG_IT_ALARM_M,
+ BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
+
+ return ret;
+}
+
+static int rk808_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);
+ struct rk808 *rk808 = rk808_rtc->rk808;
+ u8 alrm_data[NUM_ALARM_REGS];
+ int ret;
+
+ ret = rk808_rtc_stop_alarm(rk808_rtc);
+ if (ret) {
+ dev_err(dev, "Failed to stop alarm: %d\n", ret);
+ return ret;
+ }
+ dev_dbg(dev, "alrm set RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n",
+ 1900 + alrm->time.tm_year, alrm->time.tm_mon + 1,
+ alrm->time.tm_mday, alrm->time.tm_wday, alrm->time.tm_hour,
+ alrm->time.tm_min, alrm->time.tm_sec);
+
+ alrm_data[0] = bin2bcd(alrm->time.tm_sec);
+ alrm_data[1] = bin2bcd(alrm->time.tm_min);
+ alrm_data[2] = bin2bcd(alrm->time.tm_hour);
+ alrm_data[3] = bin2bcd(alrm->time.tm_mday);
+ alrm_data[4] = bin2bcd(alrm->time.tm_mon + 1);
+ alrm_data[5] = bin2bcd(alrm->time.tm_year - 100);
+
+ ret = regmap_bulk_write(rk808->regmap, RK808_ALARM_SECONDS_REG,
+ alrm_data, NUM_ALARM_REGS);
+ if (ret) {
+ dev_err(dev, "Failed to bulk write: %d\n", ret);
+ return ret;
+ }
+ if (alrm->enabled) {
+ ret = rk808_rtc_start_alarm(rk808_rtc);
+ if (ret) {
+ dev_err(dev, "Failed to start alarm: %d\n", ret);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int rk808_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);
+
+ if (enabled)
+ return rk808_rtc_start_alarm(rk808_rtc);
+
+ return rk808_rtc_stop_alarm(rk808_rtc);
+}
+
+/*
+ * We will just handle setting the frequency and make use the framework for
+ * reading the periodic interupts.
+ *
+ * @freq: Current periodic IRQ freq:
+ * bit 0: every second
+ * bit 1: every minute
+ * bit 2: every hour
+ * bit 3: every day
+ */
+static irqreturn_t rk808_alarm_irq(int irq, void *data)
+{
+ struct rk808_rtc *rk808_rtc = data;
+ struct rk808 *rk808 = rk808_rtc->rk808;
+ struct i2c_client *client = rk808->i2c;
+ int ret;
+
+ ret = regmap_write(rk808->regmap, RK808_RTC_STATUS_REG,
+ RTC_STATUS_MASK);
+ if (ret) {
+ dev_err(&client->dev,
+ "%s:Failed to update RTC status: %d\n", __func__, ret);
+ return ret;
+ }
+
+ rtc_update_irq(rk808_rtc->rtc, 1, RTC_IRQF | RTC_AF);
+ dev_dbg(&client->dev,
+ "%s:irq=%d\n", __func__, irq);
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops rk808_rtc_ops = {
+ .read_time = rk808_rtc_readtime,
+ .set_time = rk808_rtc_set_time,
+ .read_alarm = rk808_rtc_readalarm,
+ .set_alarm = rk808_rtc_setalarm,
+ .alarm_irq_enable = rk808_rtc_alarm_irq_enable,
+};
+
+#ifdef CONFIG_PM_SLEEP
+/* Turn off the alarm if it should not be a wake source. */
+static int rk808_rtc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rk808_rtc *rk808_rtc = dev_get_drvdata(&pdev->dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(rk808_rtc->irq);
+
+ return 0;
+}
+
+/* Enable the alarm if it should be enabled (in case it was disabled to
+ * prevent use as a wake source).
+ */
+static int rk808_rtc_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rk808_rtc *rk808_rtc = dev_get_drvdata(&pdev->dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(rk808_rtc->irq);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(rk808_rtc_pm_ops,
+ rk808_rtc_suspend, rk808_rtc_resume);
+
+static int rk808_rtc_probe(struct platform_device *pdev)
+{
+ struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent);
+ struct rk808_rtc *rk808_rtc;
+ struct rtc_time tm;
+ int ret;
+
+ rk808_rtc = devm_kzalloc(&pdev->dev, sizeof(*rk808_rtc), GFP_KERNEL);
+ if (rk808_rtc == NULL)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, rk808_rtc);
+ rk808_rtc->rk808 = rk808;
+
+ /* start rtc running by default, and use shadowed timer. */
+ ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
+ BIT_RTC_CTRL_REG_STOP_RTC_M |
+ BIT_RTC_CTRL_REG_RTC_READSEL_M,
+ BIT_RTC_CTRL_REG_RTC_READSEL_M);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to update RTC control: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(rk808->regmap, RK808_RTC_STATUS_REG,
+ RTC_STATUS_MASK);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to write RTC status: %d\n", ret);
+ return ret;
+ }
+
+ /* set init time */
+ ret = rk808_rtc_readtime(&pdev->dev, &tm);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to read RTC time\n");
+ return ret;
+ }
+ ret = rtc_valid_tm(&tm);
+ if (ret)
+ dev_warn(&pdev->dev, "invalid date/time\n");
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ rk808_rtc->rtc = devm_rtc_device_register(&pdev->dev, "rk808-rtc",
+ &rk808_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rk808_rtc->rtc)) {
+ ret = PTR_ERR(rk808_rtc->rtc);
+ return ret;
+ }
+
+ rk808_rtc->irq = platform_get_irq(pdev, 0);
+ if (rk808_rtc->irq < 0) {
+ if (rk808_rtc->irq != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Wake up is not possible as irq = %d\n",
+ rk808_rtc->irq);
+ return rk808_rtc->irq;
+ }
+
+ /* request alarm irq of rk808 */
+ ret = devm_request_threaded_irq(&pdev->dev, rk808_rtc->irq, NULL,
+ rk808_alarm_irq, 0,
+ "RTC alarm", rk808_rtc);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n",
+ rk808_rtc->irq, ret);
+ }
+
+ return ret;
+}
+
+static struct platform_driver rk808_rtc_driver = {
+ .probe = rk808_rtc_probe,
+ .driver = {
+ .name = "rk808-rtc",
+ .pm = &rk808_rtc_pm_ops,
+ },
+};
+
+module_platform_driver(rk808_rtc_driver);
+
+MODULE_DESCRIPTION("RTC driver for the rk808 series PMICs");
+MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
+MODULE_AUTHOR("Zhang Qing <zhangqing@rock-chips.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:rk808-rtc");
diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c
index ccf54f06396..28871cd7e3b 100644
--- a/drivers/rtc/rtc-rs5c372.c
+++ b/drivers/rtc/rtc-rs5c372.c
@@ -142,12 +142,11 @@ static int rs5c_get_regs(struct rs5c372 *rs5c)
}
dev_dbg(&client->dev,
- "%02x %02x %02x (%02x) %02x %02x %02x (%02x), "
- "%02x %02x %02x, %02x %02x %02x; %02x %02x\n",
- rs5c->regs[0], rs5c->regs[1], rs5c->regs[2], rs5c->regs[3],
- rs5c->regs[4], rs5c->regs[5], rs5c->regs[6], rs5c->regs[7],
- rs5c->regs[8], rs5c->regs[9], rs5c->regs[10], rs5c->regs[11],
- rs5c->regs[12], rs5c->regs[13], rs5c->regs[14], rs5c->regs[15]);
+ "%3ph (%02x) %3ph (%02x), %3ph, %3ph; %02x %02x\n",
+ rs5c->regs + 0, rs5c->regs[3],
+ rs5c->regs + 4, rs5c->regs[7],
+ rs5c->regs + 8, rs5c->regs + 11,
+ rs5c->regs[14], rs5c->regs[15]);
return 0;
}
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
index 4958a363b2c..806072238c0 100644
--- a/drivers/rtc/rtc-s3c.c
+++ b/drivers/rtc/rtc-s3c.c
@@ -32,155 +32,150 @@
#include <asm/irq.h>
#include "rtc-s3c.h"
-enum s3c_cpu_type {
- TYPE_S3C2410,
- TYPE_S3C2416,
- TYPE_S3C2443,
- TYPE_S3C64XX,
-};
+struct s3c_rtc {
+ struct device *dev;
+ struct rtc_device *rtc;
-struct s3c_rtc_drv_data {
- int cpu_type;
-};
+ void __iomem *base;
+ struct clk *rtc_clk;
+ struct clk *rtc_src_clk;
+ bool enabled;
+
+ struct s3c_rtc_data *data;
-/* I have yet to find an S3C implementation with more than one
- * of these rtc blocks in */
+ int irq_alarm;
+ int irq_tick;
-static struct clk *rtc_clk;
-static void __iomem *s3c_rtc_base;
-static int s3c_rtc_alarmno;
-static int s3c_rtc_tickno;
-static enum s3c_cpu_type s3c_rtc_cpu_type;
+ spinlock_t pie_lock;
+ spinlock_t alarm_clk_lock;
-static DEFINE_SPINLOCK(s3c_rtc_pie_lock);
+ int ticnt_save, ticnt_en_save;
+ bool wake_en;
+};
+
+struct s3c_rtc_data {
+ int max_user_freq;
+ bool needs_src_clk;
+
+ void (*irq_handler) (struct s3c_rtc *info, int mask);
+ void (*set_freq) (struct s3c_rtc *info, int freq);
+ void (*enable_tick) (struct s3c_rtc *info, struct seq_file *seq);
+ void (*select_tick_clk) (struct s3c_rtc *info);
+ void (*save_tick_cnt) (struct s3c_rtc *info);
+ void (*restore_tick_cnt) (struct s3c_rtc *info);
+ void (*enable) (struct s3c_rtc *info);
+ void (*disable) (struct s3c_rtc *info);
+};
-static void s3c_rtc_alarm_clk_enable(bool enable)
+static void s3c_rtc_alarm_clk_enable(struct s3c_rtc *info, bool enable)
{
- static DEFINE_SPINLOCK(s3c_rtc_alarm_clk_lock);
- static bool alarm_clk_enabled;
unsigned long irq_flags;
- spin_lock_irqsave(&s3c_rtc_alarm_clk_lock, irq_flags);
+ spin_lock_irqsave(&info->alarm_clk_lock, irq_flags);
if (enable) {
- if (!alarm_clk_enabled) {
- clk_enable(rtc_clk);
- alarm_clk_enabled = true;
+ if (!info->enabled) {
+ clk_enable(info->rtc_clk);
+ if (info->data->needs_src_clk)
+ clk_enable(info->rtc_src_clk);
+ info->enabled = true;
}
} else {
- if (alarm_clk_enabled) {
- clk_disable(rtc_clk);
- alarm_clk_enabled = false;
+ if (info->enabled) {
+ if (info->data->needs_src_clk)
+ clk_disable(info->rtc_src_clk);
+ clk_disable(info->rtc_clk);
+ info->enabled = false;
}
}
- spin_unlock_irqrestore(&s3c_rtc_alarm_clk_lock, irq_flags);
+ spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags);
}
/* IRQ Handlers */
-
-static irqreturn_t s3c_rtc_alarmirq(int irq, void *id)
+static irqreturn_t s3c_rtc_tickirq(int irq, void *id)
{
- struct rtc_device *rdev = id;
-
- clk_enable(rtc_clk);
- 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);
-
- clk_disable(rtc_clk);
+ struct s3c_rtc *info = (struct s3c_rtc *)id;
- s3c_rtc_alarm_clk_enable(false);
+ if (info->data->irq_handler)
+ info->data->irq_handler(info, S3C2410_INTP_TIC);
return IRQ_HANDLED;
}
-static irqreturn_t s3c_rtc_tickirq(int irq, void *id)
+static irqreturn_t s3c_rtc_alarmirq(int irq, void *id)
{
- struct rtc_device *rdev = id;
+ struct s3c_rtc *info = (struct s3c_rtc *)id;
- clk_enable(rtc_clk);
- rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF);
+ if (info->data->irq_handler)
+ info->data->irq_handler(info, S3C2410_INTP_ALM);
- if (s3c_rtc_cpu_type == TYPE_S3C64XX)
- writeb(S3C2410_INTP_TIC, s3c_rtc_base + S3C2410_INTP);
-
- clk_disable(rtc_clk);
return IRQ_HANDLED;
}
/* Update control registers */
static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
{
+ struct s3c_rtc *info = dev_get_drvdata(dev);
unsigned int tmp;
- dev_dbg(dev, "%s: aie=%d\n", __func__, enabled);
+ dev_dbg(info->dev, "%s: aie=%d\n", __func__, enabled);
- clk_enable(rtc_clk);
- tmp = readb(s3c_rtc_base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN;
+ clk_enable(info->rtc_clk);
+ if (info->data->needs_src_clk)
+ clk_enable(info->rtc_src_clk);
+ tmp = readb(info->base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN;
if (enabled)
tmp |= S3C2410_RTCALM_ALMEN;
- writeb(tmp, s3c_rtc_base + S3C2410_RTCALM);
- clk_disable(rtc_clk);
+ writeb(tmp, info->base + S3C2410_RTCALM);
+ if (info->data->needs_src_clk)
+ clk_disable(info->rtc_src_clk);
+ clk_disable(info->rtc_clk);
- s3c_rtc_alarm_clk_enable(enabled);
+ s3c_rtc_alarm_clk_enable(info, enabled);
return 0;
}
-static int s3c_rtc_setfreq(struct device *dev, int freq)
+/* Set RTC frequency */
+static int s3c_rtc_setfreq(struct s3c_rtc *info, int freq)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
- unsigned int tmp = 0;
- int val;
-
if (!is_power_of_2(freq))
return -EINVAL;
- clk_enable(rtc_clk);
- spin_lock_irq(&s3c_rtc_pie_lock);
+ clk_enable(info->rtc_clk);
+ if (info->data->needs_src_clk)
+ clk_enable(info->rtc_src_clk);
+ spin_lock_irq(&info->pie_lock);
- if (s3c_rtc_cpu_type != TYPE_S3C64XX) {
- tmp = readb(s3c_rtc_base + S3C2410_TICNT);
- tmp &= S3C2410_TICNT_ENABLE;
- }
+ if (info->data->set_freq)
+ info->data->set_freq(info, freq);
- val = (rtc_dev->max_user_freq / freq) - 1;
-
- if (s3c_rtc_cpu_type == TYPE_S3C2416 || s3c_rtc_cpu_type == TYPE_S3C2443) {
- tmp |= S3C2443_TICNT_PART(val);
- writel(S3C2443_TICNT1_PART(val), s3c_rtc_base + S3C2443_TICNT1);
-
- if (s3c_rtc_cpu_type == TYPE_S3C2416)
- writel(S3C2416_TICNT2_PART(val), s3c_rtc_base + S3C2416_TICNT2);
- } else {
- tmp |= val;
- }
-
- writel(tmp, s3c_rtc_base + S3C2410_TICNT);
- spin_unlock_irq(&s3c_rtc_pie_lock);
- clk_disable(rtc_clk);
+ spin_unlock_irq(&info->pie_lock);
+ if (info->data->needs_src_clk)
+ clk_disable(info->rtc_src_clk);
+ clk_disable(info->rtc_clk);
return 0;
}
/* Time read/write */
-
static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
{
+ struct s3c_rtc *info = dev_get_drvdata(dev);
unsigned int have_retried = 0;
- void __iomem *base = s3c_rtc_base;
- clk_enable(rtc_clk);
+ clk_enable(info->rtc_clk);
+ if (info->data->needs_src_clk)
+ clk_enable(info->rtc_src_clk);
+
retry_get_time:
- rtc_tm->tm_min = readb(base + S3C2410_RTCMIN);
- rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR);
- rtc_tm->tm_mday = readb(base + S3C2410_RTCDATE);
- rtc_tm->tm_mon = readb(base + S3C2410_RTCMON);
- rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR);
- rtc_tm->tm_sec = readb(base + S3C2410_RTCSEC);
+ rtc_tm->tm_min = readb(info->base + S3C2410_RTCMIN);
+ rtc_tm->tm_hour = readb(info->base + S3C2410_RTCHOUR);
+ rtc_tm->tm_mday = readb(info->base + S3C2410_RTCDATE);
+ rtc_tm->tm_mon = readb(info->base + S3C2410_RTCMON);
+ rtc_tm->tm_year = readb(info->base + S3C2410_RTCYEAR);
+ rtc_tm->tm_sec = readb(info->base + S3C2410_RTCSEC);
/* the only way to work out whether the system was mid-update
* when we read it is to check the second counter, and if it
@@ -207,13 +202,16 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
rtc_tm->tm_mon -= 1;
- clk_disable(rtc_clk);
+ if (info->data->needs_src_clk)
+ clk_disable(info->rtc_src_clk);
+ clk_disable(info->rtc_clk);
+
return rtc_valid_tm(rtc_tm);
}
static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
{
- void __iomem *base = s3c_rtc_base;
+ struct s3c_rtc *info = dev_get_drvdata(dev);
int year = tm->tm_year - 100;
dev_dbg(dev, "set time %04d.%02d.%02d %02d:%02d:%02d\n",
@@ -227,33 +225,42 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
return -EINVAL;
}
- clk_enable(rtc_clk);
- writeb(bin2bcd(tm->tm_sec), base + S3C2410_RTCSEC);
- writeb(bin2bcd(tm->tm_min), base + S3C2410_RTCMIN);
- writeb(bin2bcd(tm->tm_hour), base + S3C2410_RTCHOUR);
- writeb(bin2bcd(tm->tm_mday), base + S3C2410_RTCDATE);
- writeb(bin2bcd(tm->tm_mon + 1), base + S3C2410_RTCMON);
- writeb(bin2bcd(year), base + S3C2410_RTCYEAR);
- clk_disable(rtc_clk);
+ clk_enable(info->rtc_clk);
+ if (info->data->needs_src_clk)
+ clk_enable(info->rtc_src_clk);
+
+ writeb(bin2bcd(tm->tm_sec), info->base + S3C2410_RTCSEC);
+ writeb(bin2bcd(tm->tm_min), info->base + S3C2410_RTCMIN);
+ writeb(bin2bcd(tm->tm_hour), info->base + S3C2410_RTCHOUR);
+ writeb(bin2bcd(tm->tm_mday), info->base + S3C2410_RTCDATE);
+ writeb(bin2bcd(tm->tm_mon + 1), info->base + S3C2410_RTCMON);
+ writeb(bin2bcd(year), info->base + S3C2410_RTCYEAR);
+
+ if (info->data->needs_src_clk)
+ clk_disable(info->rtc_src_clk);
+ clk_disable(info->rtc_clk);
return 0;
}
static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
{
+ struct s3c_rtc *info = dev_get_drvdata(dev);
struct rtc_time *alm_tm = &alrm->time;
- void __iomem *base = s3c_rtc_base;
unsigned int alm_en;
- clk_enable(rtc_clk);
- alm_tm->tm_sec = readb(base + S3C2410_ALMSEC);
- alm_tm->tm_min = readb(base + S3C2410_ALMMIN);
- alm_tm->tm_hour = readb(base + S3C2410_ALMHOUR);
- alm_tm->tm_mon = readb(base + S3C2410_ALMMON);
- alm_tm->tm_mday = readb(base + S3C2410_ALMDATE);
- alm_tm->tm_year = readb(base + S3C2410_ALMYEAR);
+ clk_enable(info->rtc_clk);
+ if (info->data->needs_src_clk)
+ clk_enable(info->rtc_src_clk);
- alm_en = readb(base + S3C2410_RTCALM);
+ alm_tm->tm_sec = readb(info->base + S3C2410_ALMSEC);
+ alm_tm->tm_min = readb(info->base + S3C2410_ALMMIN);
+ alm_tm->tm_hour = readb(info->base + S3C2410_ALMHOUR);
+ alm_tm->tm_mon = readb(info->base + S3C2410_ALMMON);
+ alm_tm->tm_mday = readb(info->base + S3C2410_ALMDATE);
+ alm_tm->tm_year = readb(info->base + S3C2410_ALMYEAR);
+
+ alm_en = readb(info->base + S3C2410_RTCALM);
alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0;
@@ -297,65 +304,74 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
else
alm_tm->tm_year = -1;
- clk_disable(rtc_clk);
+ if (info->data->needs_src_clk)
+ clk_disable(info->rtc_src_clk);
+ clk_disable(info->rtc_clk);
+
return 0;
}
static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
{
+ struct s3c_rtc *info = dev_get_drvdata(dev);
struct rtc_time *tm = &alrm->time;
- void __iomem *base = s3c_rtc_base;
unsigned int alrm_en;
- clk_enable(rtc_clk);
+ clk_enable(info->rtc_clk);
+ if (info->data->needs_src_clk)
+ clk_enable(info->rtc_src_clk);
+
dev_dbg(dev, "s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n",
alrm->enabled,
1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
- alrm_en = readb(base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN;
- writeb(0x00, base + S3C2410_RTCALM);
+ alrm_en = readb(info->base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN;
+ writeb(0x00, info->base + S3C2410_RTCALM);
if (tm->tm_sec < 60 && tm->tm_sec >= 0) {
alrm_en |= S3C2410_RTCALM_SECEN;
- writeb(bin2bcd(tm->tm_sec), base + S3C2410_ALMSEC);
+ writeb(bin2bcd(tm->tm_sec), info->base + S3C2410_ALMSEC);
}
if (tm->tm_min < 60 && tm->tm_min >= 0) {
alrm_en |= S3C2410_RTCALM_MINEN;
- writeb(bin2bcd(tm->tm_min), base + S3C2410_ALMMIN);
+ writeb(bin2bcd(tm->tm_min), info->base + S3C2410_ALMMIN);
}
if (tm->tm_hour < 24 && tm->tm_hour >= 0) {
alrm_en |= S3C2410_RTCALM_HOUREN;
- writeb(bin2bcd(tm->tm_hour), base + S3C2410_ALMHOUR);
+ writeb(bin2bcd(tm->tm_hour), info->base + S3C2410_ALMHOUR);
}
dev_dbg(dev, "setting S3C2410_RTCALM to %08x\n", alrm_en);
- writeb(alrm_en, base + S3C2410_RTCALM);
+ writeb(alrm_en, info->base + S3C2410_RTCALM);
s3c_rtc_setaie(dev, alrm->enabled);
- clk_disable(rtc_clk);
+ if (info->data->needs_src_clk)
+ clk_disable(info->rtc_src_clk);
+ clk_disable(info->rtc_clk);
+
return 0;
}
static int s3c_rtc_proc(struct device *dev, struct seq_file *seq)
{
- unsigned int ticnt;
+ struct s3c_rtc *info = dev_get_drvdata(dev);
- clk_enable(rtc_clk);
- if (s3c_rtc_cpu_type == TYPE_S3C64XX) {
- ticnt = readw(s3c_rtc_base + S3C2410_RTCCON);
- ticnt &= S3C64XX_RTCCON_TICEN;
- } else {
- ticnt = readb(s3c_rtc_base + S3C2410_TICNT);
- ticnt &= S3C2410_TICNT_ENABLE;
- }
+ clk_enable(info->rtc_clk);
+ if (info->data->needs_src_clk)
+ clk_enable(info->rtc_src_clk);
+
+ if (info->data->enable_tick)
+ info->data->enable_tick(info, seq);
+
+ if (info->data->needs_src_clk)
+ clk_disable(info->rtc_src_clk);
+ clk_disable(info->rtc_clk);
- seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no");
- clk_disable(rtc_clk);
return 0;
}
@@ -368,152 +384,201 @@ static const struct rtc_class_ops s3c_rtcops = {
.alarm_irq_enable = s3c_rtc_setaie,
};
-static void s3c_rtc_enable(struct platform_device *pdev, int en)
+static void s3c24xx_rtc_enable(struct s3c_rtc *info)
{
- void __iomem *base = s3c_rtc_base;
- unsigned int tmp;
+ unsigned int con, tmp;
- if (s3c_rtc_base == NULL)
- return;
-
- clk_enable(rtc_clk);
- if (!en) {
- tmp = readw(base + S3C2410_RTCCON);
- if (s3c_rtc_cpu_type == TYPE_S3C64XX)
- tmp &= ~S3C64XX_RTCCON_TICEN;
- tmp &= ~S3C2410_RTCCON_RTCEN;
- writew(tmp, base + S3C2410_RTCCON);
-
- if (s3c_rtc_cpu_type != TYPE_S3C64XX) {
- tmp = readb(base + S3C2410_TICNT);
- tmp &= ~S3C2410_TICNT_ENABLE;
- writeb(tmp, base + S3C2410_TICNT);
- }
- } else {
- /* re-enable the device, and check it is ok */
+ clk_enable(info->rtc_clk);
+ if (info->data->needs_src_clk)
+ clk_enable(info->rtc_src_clk);
- if ((readw(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0) {
- dev_info(&pdev->dev, "rtc disabled, re-enabling\n");
+ con = readw(info->base + S3C2410_RTCCON);
+ /* re-enable the device, and check it is ok */
+ if ((con & S3C2410_RTCCON_RTCEN) == 0) {
+ dev_info(info->dev, "rtc disabled, re-enabling\n");
- tmp = readw(base + S3C2410_RTCCON);
- writew(tmp | S3C2410_RTCCON_RTCEN,
- base + S3C2410_RTCCON);
- }
+ tmp = readw(info->base + S3C2410_RTCCON);
+ writew(tmp | S3C2410_RTCCON_RTCEN,
+ info->base + S3C2410_RTCCON);
+ }
- if ((readw(base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)) {
- dev_info(&pdev->dev, "removing RTCCON_CNTSEL\n");
+ if (con & S3C2410_RTCCON_CNTSEL) {
+ dev_info(info->dev, "removing RTCCON_CNTSEL\n");
- tmp = readw(base + S3C2410_RTCCON);
- writew(tmp & ~S3C2410_RTCCON_CNTSEL,
- base + S3C2410_RTCCON);
- }
+ tmp = readw(info->base + S3C2410_RTCCON);
+ writew(tmp & ~S3C2410_RTCCON_CNTSEL,
+ info->base + S3C2410_RTCCON);
+ }
- if ((readw(base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)) {
- dev_info(&pdev->dev, "removing RTCCON_CLKRST\n");
+ if (con & S3C2410_RTCCON_CLKRST) {
+ dev_info(info->dev, "removing RTCCON_CLKRST\n");
- tmp = readw(base + S3C2410_RTCCON);
- writew(tmp & ~S3C2410_RTCCON_CLKRST,
- base + S3C2410_RTCCON);
- }
+ tmp = readw(info->base + S3C2410_RTCCON);
+ writew(tmp & ~S3C2410_RTCCON_CLKRST,
+ info->base + S3C2410_RTCCON);
}
- clk_disable(rtc_clk);
+
+ if (info->data->needs_src_clk)
+ clk_disable(info->rtc_src_clk);
+ clk_disable(info->rtc_clk);
}
-static int s3c_rtc_remove(struct platform_device *dev)
+static void s3c24xx_rtc_disable(struct s3c_rtc *info)
{
- s3c_rtc_setaie(&dev->dev, 0);
+ unsigned int con;
+
+ clk_enable(info->rtc_clk);
+ if (info->data->needs_src_clk)
+ clk_enable(info->rtc_src_clk);
+
+ con = readw(info->base + S3C2410_RTCCON);
+ con &= ~S3C2410_RTCCON_RTCEN;
+ writew(con, info->base + S3C2410_RTCCON);
- clk_unprepare(rtc_clk);
- rtc_clk = NULL;
+ con = readb(info->base + S3C2410_TICNT);
+ con &= ~S3C2410_TICNT_ENABLE;
+ writeb(con, info->base + S3C2410_TICNT);
+
+ if (info->data->needs_src_clk)
+ clk_disable(info->rtc_src_clk);
+ clk_disable(info->rtc_clk);
+}
+
+static void s3c6410_rtc_disable(struct s3c_rtc *info)
+{
+ unsigned int con;
+
+ clk_enable(info->rtc_clk);
+ if (info->data->needs_src_clk)
+ clk_enable(info->rtc_src_clk);
+
+ con = readw(info->base + S3C2410_RTCCON);
+ con &= ~S3C64XX_RTCCON_TICEN;
+ con &= ~S3C2410_RTCCON_RTCEN;
+ writew(con, info->base + S3C2410_RTCCON);
+
+ if (info->data->needs_src_clk)
+ clk_disable(info->rtc_src_clk);
+ clk_disable(info->rtc_clk);
+}
+
+static int s3c_rtc_remove(struct platform_device *pdev)
+{
+ struct s3c_rtc *info = platform_get_drvdata(pdev);
+
+ s3c_rtc_setaie(info->dev, 0);
+
+ clk_unprepare(info->rtc_clk);
+ info->rtc_clk = NULL;
return 0;
}
static const struct of_device_id s3c_rtc_dt_match[];
-static inline int s3c_rtc_get_driver_data(struct platform_device *pdev)
+static struct s3c_rtc_data *s3c_rtc_get_data(struct platform_device *pdev)
{
-#ifdef CONFIG_OF
- struct s3c_rtc_drv_data *data;
- if (pdev->dev.of_node) {
- const struct of_device_id *match;
- match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node);
- data = (struct s3c_rtc_drv_data *) match->data;
- return data->cpu_type;
- }
-#endif
- return platform_get_device_id(pdev)->driver_data;
+ const struct of_device_id *match;
+
+ match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node);
+ return (struct s3c_rtc_data *)match->data;
}
static int s3c_rtc_probe(struct platform_device *pdev)
{
- struct rtc_device *rtc;
+ struct s3c_rtc *info = NULL;
struct rtc_time rtc_tm;
struct resource *res;
int ret;
- int tmp;
- dev_dbg(&pdev->dev, "%s: probe=%p\n", __func__, pdev);
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
/* find the IRQs */
-
- s3c_rtc_tickno = platform_get_irq(pdev, 1);
- if (s3c_rtc_tickno < 0) {
+ info->irq_tick = platform_get_irq(pdev, 1);
+ if (info->irq_tick < 0) {
dev_err(&pdev->dev, "no irq for rtc tick\n");
- return s3c_rtc_tickno;
+ return info->irq_tick;
+ }
+
+ info->dev = &pdev->dev;
+ info->data = s3c_rtc_get_data(pdev);
+ if (!info->data) {
+ dev_err(&pdev->dev, "failed getting s3c_rtc_data\n");
+ return -EINVAL;
}
+ spin_lock_init(&info->pie_lock);
+ spin_lock_init(&info->alarm_clk_lock);
+
+ platform_set_drvdata(pdev, info);
- s3c_rtc_alarmno = platform_get_irq(pdev, 0);
- if (s3c_rtc_alarmno < 0) {
+ info->irq_alarm = platform_get_irq(pdev, 0);
+ if (info->irq_alarm < 0) {
dev_err(&pdev->dev, "no irq for alarm\n");
- return s3c_rtc_alarmno;
+ return info->irq_alarm;
}
dev_dbg(&pdev->dev, "s3c2410_rtc: tick irq %d, alarm irq %d\n",
- s3c_rtc_tickno, s3c_rtc_alarmno);
+ info->irq_tick, info->irq_alarm);
/* get the memory region */
-
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- s3c_rtc_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(s3c_rtc_base))
- return PTR_ERR(s3c_rtc_base);
-
- rtc_clk = devm_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;
- return ret;
+ info->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(info->base))
+ return PTR_ERR(info->base);
+
+ info->rtc_clk = devm_clk_get(&pdev->dev, "rtc");
+ if (IS_ERR(info->rtc_clk)) {
+ dev_err(&pdev->dev, "failed to find rtc clock\n");
+ return PTR_ERR(info->rtc_clk);
+ }
+ clk_prepare_enable(info->rtc_clk);
+
+ if (info->data->needs_src_clk) {
+ info->rtc_src_clk = devm_clk_get(&pdev->dev, "rtc_src");
+ if (IS_ERR(info->rtc_src_clk)) {
+ dev_err(&pdev->dev,
+ "failed to find rtc source clock\n");
+ return PTR_ERR(info->rtc_src_clk);
+ }
+ clk_prepare_enable(info->rtc_src_clk);
}
-
- clk_prepare_enable(rtc_clk);
/* check to see if everything is setup correctly */
-
- s3c_rtc_enable(pdev, 1);
+ if (info->data->enable)
+ info->data->enable(info);
dev_dbg(&pdev->dev, "s3c2410_rtc: RTCCON=%02x\n",
- readw(s3c_rtc_base + S3C2410_RTCCON));
+ readw(info->base + S3C2410_RTCCON));
device_init_wakeup(&pdev->dev, 1);
/* register RTC and exit */
-
- rtc = devm_rtc_device_register(&pdev->dev, "s3c", &s3c_rtcops,
+ info->rtc = devm_rtc_device_register(&pdev->dev, "s3c", &s3c_rtcops,
THIS_MODULE);
-
- if (IS_ERR(rtc)) {
+ if (IS_ERR(info->rtc)) {
dev_err(&pdev->dev, "cannot attach rtc\n");
- ret = PTR_ERR(rtc);
+ ret = PTR_ERR(info->rtc);
goto err_nortc;
}
- s3c_rtc_cpu_type = s3c_rtc_get_driver_data(pdev);
+ ret = devm_request_irq(&pdev->dev, info->irq_alarm, s3c_rtc_alarmirq,
+ 0, "s3c2410-rtc alarm", info);
+ if (ret) {
+ dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_alarm, ret);
+ goto err_nortc;
+ }
- /* Check RTC Time */
+ ret = devm_request_irq(&pdev->dev, info->irq_tick, s3c_rtc_tickirq,
+ 0, "s3c2410-rtc tick", info);
+ if (ret) {
+ dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_tick, ret);
+ goto err_nortc;
+ }
- s3c_rtc_gettime(NULL, &rtc_tm);
+ /* Check RTC Time */
+ s3c_rtc_gettime(&pdev->dev, &rtc_tm);
if (rtc_valid_tm(&rtc_tm)) {
rtc_tm.tm_year = 100;
@@ -523,163 +588,312 @@ static int s3c_rtc_probe(struct platform_device *pdev)
rtc_tm.tm_min = 0;
rtc_tm.tm_sec = 0;
- s3c_rtc_settime(NULL, &rtc_tm);
+ s3c_rtc_settime(&pdev->dev, &rtc_tm);
dev_warn(&pdev->dev, "warning: invalid RTC value so initializing it\n");
}
- if (s3c_rtc_cpu_type != TYPE_S3C2410)
- rtc->max_user_freq = 32768;
- else
- rtc->max_user_freq = 128;
+ if (info->data->select_tick_clk)
+ info->data->select_tick_clk(info);
- if (s3c_rtc_cpu_type == TYPE_S3C2416 || s3c_rtc_cpu_type == TYPE_S3C2443) {
- tmp = readw(s3c_rtc_base + S3C2410_RTCCON);
- tmp |= S3C2443_RTCCON_TICSEL;
- writew(tmp, s3c_rtc_base + S3C2410_RTCCON);
- }
+ s3c_rtc_setfreq(info, 1);
- platform_set_drvdata(pdev, rtc);
-
- s3c_rtc_setfreq(&pdev->dev, 1);
-
- ret = devm_request_irq(&pdev->dev, s3c_rtc_alarmno, s3c_rtc_alarmirq,
- 0, "s3c2410-rtc alarm", rtc);
- if (ret) {
- dev_err(&pdev->dev, "IRQ%d error %d\n", s3c_rtc_alarmno, ret);
- goto err_nortc;
- }
-
- ret = devm_request_irq(&pdev->dev, s3c_rtc_tickno, s3c_rtc_tickirq,
- 0, "s3c2410-rtc tick", rtc);
- if (ret) {
- dev_err(&pdev->dev, "IRQ%d error %d\n", s3c_rtc_tickno, ret);
- goto err_nortc;
- }
-
- clk_disable(rtc_clk);
+ if (info->data->needs_src_clk)
+ clk_disable(info->rtc_src_clk);
+ clk_disable(info->rtc_clk);
return 0;
err_nortc:
- s3c_rtc_enable(pdev, 0);
- clk_disable_unprepare(rtc_clk);
+ if (info->data->disable)
+ info->data->disable(info);
+ clk_disable_unprepare(info->rtc_clk);
return ret;
}
#ifdef CONFIG_PM_SLEEP
-/* RTC Power management control */
-
-static int ticnt_save, ticnt_en_save;
-static bool wake_en;
static int s3c_rtc_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
+ struct s3c_rtc *info = dev_get_drvdata(dev);
+
+ clk_enable(info->rtc_clk);
+ if (info->data->needs_src_clk)
+ clk_enable(info->rtc_src_clk);
- clk_enable(rtc_clk);
/* save TICNT for anyone using periodic interrupts */
- if (s3c_rtc_cpu_type == TYPE_S3C64XX) {
- ticnt_en_save = readw(s3c_rtc_base + S3C2410_RTCCON);
- ticnt_en_save &= S3C64XX_RTCCON_TICEN;
- ticnt_save = readl(s3c_rtc_base + S3C2410_TICNT);
- } else {
- ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT);
- }
- s3c_rtc_enable(pdev, 0);
+ if (info->data->save_tick_cnt)
+ info->data->save_tick_cnt(info);
- if (device_may_wakeup(dev) && !wake_en) {
- if (enable_irq_wake(s3c_rtc_alarmno) == 0)
- wake_en = true;
+ if (info->data->disable)
+ info->data->disable(info);
+
+ if (device_may_wakeup(dev) && !info->wake_en) {
+ if (enable_irq_wake(info->irq_alarm) == 0)
+ info->wake_en = true;
else
dev_err(dev, "enable_irq_wake failed\n");
}
- clk_disable(rtc_clk);
+
+ if (info->data->needs_src_clk)
+ clk_disable(info->rtc_src_clk);
+ clk_disable(info->rtc_clk);
return 0;
}
static int s3c_rtc_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- unsigned int tmp;
+ struct s3c_rtc *info = dev_get_drvdata(dev);
- clk_enable(rtc_clk);
- s3c_rtc_enable(pdev, 1);
- if (s3c_rtc_cpu_type == TYPE_S3C64XX) {
- writel(ticnt_save, s3c_rtc_base + S3C2410_TICNT);
- if (ticnt_en_save) {
- tmp = readw(s3c_rtc_base + S3C2410_RTCCON);
- writew(tmp | ticnt_en_save,
- s3c_rtc_base + S3C2410_RTCCON);
- }
- } else {
- writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT);
- }
+ clk_enable(info->rtc_clk);
+ if (info->data->needs_src_clk)
+ clk_enable(info->rtc_src_clk);
+
+ if (info->data->enable)
+ info->data->enable(info);
+
+ if (info->data->restore_tick_cnt)
+ info->data->restore_tick_cnt(info);
- if (device_may_wakeup(dev) && wake_en) {
- disable_irq_wake(s3c_rtc_alarmno);
- wake_en = false;
+ if (device_may_wakeup(dev) && info->wake_en) {
+ disable_irq_wake(info->irq_alarm);
+ info->wake_en = false;
}
- clk_disable(rtc_clk);
+
+ if (info->data->needs_src_clk)
+ clk_disable(info->rtc_src_clk);
+ clk_disable(info->rtc_clk);
return 0;
}
#endif
-
static SIMPLE_DEV_PM_OPS(s3c_rtc_pm_ops, s3c_rtc_suspend, s3c_rtc_resume);
-#ifdef CONFIG_OF
-static struct s3c_rtc_drv_data s3c_rtc_drv_data_array[] = {
- [TYPE_S3C2410] = { TYPE_S3C2410 },
- [TYPE_S3C2416] = { TYPE_S3C2416 },
- [TYPE_S3C2443] = { TYPE_S3C2443 },
- [TYPE_S3C64XX] = { TYPE_S3C64XX },
+static void s3c24xx_rtc_irq(struct s3c_rtc *info, int mask)
+{
+ clk_enable(info->rtc_clk);
+ if (info->data->needs_src_clk)
+ clk_enable(info->rtc_src_clk);
+ rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF);
+ if (info->data->needs_src_clk)
+ clk_disable(info->rtc_src_clk);
+ clk_disable(info->rtc_clk);
+
+ s3c_rtc_alarm_clk_enable(info, false);
+}
+
+static void s3c6410_rtc_irq(struct s3c_rtc *info, int mask)
+{
+ clk_enable(info->rtc_clk);
+ if (info->data->needs_src_clk)
+ clk_enable(info->rtc_src_clk);
+ rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF);
+ writeb(mask, info->base + S3C2410_INTP);
+ if (info->data->needs_src_clk)
+ clk_disable(info->rtc_src_clk);
+ clk_disable(info->rtc_clk);
+
+ s3c_rtc_alarm_clk_enable(info, false);
+}
+
+static void s3c2410_rtc_setfreq(struct s3c_rtc *info, int freq)
+{
+ unsigned int tmp = 0;
+ int val;
+
+ tmp = readb(info->base + S3C2410_TICNT);
+ tmp &= S3C2410_TICNT_ENABLE;
+
+ val = (info->rtc->max_user_freq / freq) - 1;
+ tmp |= val;
+
+ writel(tmp, info->base + S3C2410_TICNT);
+}
+
+static void s3c2416_rtc_setfreq(struct s3c_rtc *info, int freq)
+{
+ unsigned int tmp = 0;
+ int val;
+
+ tmp = readb(info->base + S3C2410_TICNT);
+ tmp &= S3C2410_TICNT_ENABLE;
+
+ val = (info->rtc->max_user_freq / freq) - 1;
+
+ tmp |= S3C2443_TICNT_PART(val);
+ writel(S3C2443_TICNT1_PART(val), info->base + S3C2443_TICNT1);
+
+ writel(S3C2416_TICNT2_PART(val), info->base + S3C2416_TICNT2);
+
+ writel(tmp, info->base + S3C2410_TICNT);
+}
+
+static void s3c2443_rtc_setfreq(struct s3c_rtc *info, int freq)
+{
+ unsigned int tmp = 0;
+ int val;
+
+ tmp = readb(info->base + S3C2410_TICNT);
+ tmp &= S3C2410_TICNT_ENABLE;
+
+ val = (info->rtc->max_user_freq / freq) - 1;
+
+ tmp |= S3C2443_TICNT_PART(val);
+ writel(S3C2443_TICNT1_PART(val), info->base + S3C2443_TICNT1);
+
+ writel(tmp, info->base + S3C2410_TICNT);
+}
+
+static void s3c6410_rtc_setfreq(struct s3c_rtc *info, int freq)
+{
+ int val;
+
+ val = (info->rtc->max_user_freq / freq) - 1;
+ writel(val, info->base + S3C2410_TICNT);
+}
+
+static void s3c24xx_rtc_enable_tick(struct s3c_rtc *info, struct seq_file *seq)
+{
+ unsigned int ticnt;
+
+ ticnt = readb(info->base + S3C2410_TICNT);
+ ticnt &= S3C2410_TICNT_ENABLE;
+
+ seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no");
+}
+
+static void s3c2416_rtc_select_tick_clk(struct s3c_rtc *info)
+{
+ unsigned int con;
+
+ con = readw(info->base + S3C2410_RTCCON);
+ con |= S3C2443_RTCCON_TICSEL;
+ writew(con, info->base + S3C2410_RTCCON);
+}
+
+static void s3c6410_rtc_enable_tick(struct s3c_rtc *info, struct seq_file *seq)
+{
+ unsigned int ticnt;
+
+ ticnt = readw(info->base + S3C2410_RTCCON);
+ ticnt &= S3C64XX_RTCCON_TICEN;
+
+ seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no");
+}
+
+static void s3c24xx_rtc_save_tick_cnt(struct s3c_rtc *info)
+{
+ info->ticnt_save = readb(info->base + S3C2410_TICNT);
+}
+
+static void s3c24xx_rtc_restore_tick_cnt(struct s3c_rtc *info)
+{
+ writeb(info->ticnt_save, info->base + S3C2410_TICNT);
+}
+
+static void s3c6410_rtc_save_tick_cnt(struct s3c_rtc *info)
+{
+ info->ticnt_en_save = readw(info->base + S3C2410_RTCCON);
+ info->ticnt_en_save &= S3C64XX_RTCCON_TICEN;
+ info->ticnt_save = readl(info->base + S3C2410_TICNT);
+}
+
+static void s3c6410_rtc_restore_tick_cnt(struct s3c_rtc *info)
+{
+ unsigned int con;
+
+ writel(info->ticnt_save, info->base + S3C2410_TICNT);
+ if (info->ticnt_en_save) {
+ con = readw(info->base + S3C2410_RTCCON);
+ writew(con | info->ticnt_en_save,
+ info->base + S3C2410_RTCCON);
+ }
+}
+
+static struct s3c_rtc_data const s3c2410_rtc_data = {
+ .max_user_freq = 128,
+ .irq_handler = s3c24xx_rtc_irq,
+ .set_freq = s3c2410_rtc_setfreq,
+ .enable_tick = s3c24xx_rtc_enable_tick,
+ .save_tick_cnt = s3c24xx_rtc_save_tick_cnt,
+ .restore_tick_cnt = s3c24xx_rtc_restore_tick_cnt,
+ .enable = s3c24xx_rtc_enable,
+ .disable = s3c24xx_rtc_disable,
+};
+
+static struct s3c_rtc_data const s3c2416_rtc_data = {
+ .max_user_freq = 32768,
+ .irq_handler = s3c24xx_rtc_irq,
+ .set_freq = s3c2416_rtc_setfreq,
+ .enable_tick = s3c24xx_rtc_enable_tick,
+ .select_tick_clk = s3c2416_rtc_select_tick_clk,
+ .save_tick_cnt = s3c24xx_rtc_save_tick_cnt,
+ .restore_tick_cnt = s3c24xx_rtc_restore_tick_cnt,
+ .enable = s3c24xx_rtc_enable,
+ .disable = s3c24xx_rtc_disable,
+};
+
+static struct s3c_rtc_data const s3c2443_rtc_data = {
+ .max_user_freq = 32768,
+ .irq_handler = s3c24xx_rtc_irq,
+ .set_freq = s3c2443_rtc_setfreq,
+ .enable_tick = s3c24xx_rtc_enable_tick,
+ .select_tick_clk = s3c2416_rtc_select_tick_clk,
+ .save_tick_cnt = s3c24xx_rtc_save_tick_cnt,
+ .restore_tick_cnt = s3c24xx_rtc_restore_tick_cnt,
+ .enable = s3c24xx_rtc_enable,
+ .disable = s3c24xx_rtc_disable,
+};
+
+static struct s3c_rtc_data const s3c6410_rtc_data = {
+ .max_user_freq = 32768,
+ .irq_handler = s3c6410_rtc_irq,
+ .set_freq = s3c6410_rtc_setfreq,
+ .enable_tick = s3c6410_rtc_enable_tick,
+ .save_tick_cnt = s3c6410_rtc_save_tick_cnt,
+ .restore_tick_cnt = s3c6410_rtc_restore_tick_cnt,
+ .enable = s3c24xx_rtc_enable,
+ .disable = s3c6410_rtc_disable,
+};
+
+static struct s3c_rtc_data const exynos3250_rtc_data = {
+ .max_user_freq = 32768,
+ .needs_src_clk = true,
+ .irq_handler = s3c6410_rtc_irq,
+ .set_freq = s3c6410_rtc_setfreq,
+ .enable_tick = s3c6410_rtc_enable_tick,
+ .save_tick_cnt = s3c6410_rtc_save_tick_cnt,
+ .restore_tick_cnt = s3c6410_rtc_restore_tick_cnt,
+ .enable = s3c24xx_rtc_enable,
+ .disable = s3c6410_rtc_disable,
};
static const struct of_device_id s3c_rtc_dt_match[] = {
{
.compatible = "samsung,s3c2410-rtc",
- .data = &s3c_rtc_drv_data_array[TYPE_S3C2410],
+ .data = (void *)&s3c2410_rtc_data,
}, {
.compatible = "samsung,s3c2416-rtc",
- .data = &s3c_rtc_drv_data_array[TYPE_S3C2416],
+ .data = (void *)&s3c2416_rtc_data,
}, {
.compatible = "samsung,s3c2443-rtc",
- .data = &s3c_rtc_drv_data_array[TYPE_S3C2443],
+ .data = (void *)&s3c2443_rtc_data,
}, {
.compatible = "samsung,s3c6410-rtc",
- .data = &s3c_rtc_drv_data_array[TYPE_S3C64XX],
- },
- {},
-};
-MODULE_DEVICE_TABLE(of, s3c_rtc_dt_match);
-#endif
-
-static struct platform_device_id s3c_rtc_driver_ids[] = {
- {
- .name = "s3c2410-rtc",
- .driver_data = TYPE_S3C2410,
- }, {
- .name = "s3c2416-rtc",
- .driver_data = TYPE_S3C2416,
- }, {
- .name = "s3c2443-rtc",
- .driver_data = TYPE_S3C2443,
+ .data = (void *)&s3c6410_rtc_data,
}, {
- .name = "s3c64xx-rtc",
- .driver_data = TYPE_S3C64XX,
+ .compatible = "samsung,exynos3250-rtc",
+ .data = (void *)&exynos3250_rtc_data,
},
- { }
+ { /* sentinel */ },
};
-
-MODULE_DEVICE_TABLE(platform, s3c_rtc_driver_ids);
+MODULE_DEVICE_TABLE(of, s3c_rtc_dt_match);
static struct platform_driver s3c_rtc_driver = {
.probe = s3c_rtc_probe,
.remove = s3c_rtc_remove,
- .id_table = s3c_rtc_driver_ids,
.driver = {
.name = "s3c-rtc",
.owner = THIS_MODULE,
@@ -687,7 +901,6 @@ static struct platform_driver s3c_rtc_driver = {
.of_match_table = of_match_ptr(s3c_rtc_dt_match),
},
};
-
module_platform_driver(s3c_rtc_driver);
MODULE_DESCRIPTION("Samsung S3C RTC Driver");
diff --git a/drivers/rtc/rtc-s5m.c b/drivers/rtc/rtc-s5m.c
index 8f06250a038..8754c33361e 100644
--- a/drivers/rtc/rtc-s5m.c
+++ b/drivers/rtc/rtc-s5m.c
@@ -717,12 +717,14 @@ static int s5m_rtc_probe(struct platform_device *pdev)
info->device_type = s5m87xx->device_type;
info->wtsr_smpl = s5m87xx->wtsr_smpl;
- info->irq = regmap_irq_get_virq(s5m87xx->irq_data, alarm_irq);
- if (info->irq <= 0) {
- ret = -EINVAL;
- dev_err(&pdev->dev, "Failed to get virtual IRQ %d\n",
+ if (s5m87xx->irq_data) {
+ info->irq = regmap_irq_get_virq(s5m87xx->irq_data, alarm_irq);
+ if (info->irq <= 0) {
+ ret = -EINVAL;
+ dev_err(&pdev->dev, "Failed to get virtual IRQ %d\n",
alarm_irq);
- goto err;
+ goto err;
+ }
}
platform_set_drvdata(pdev, info);
@@ -744,6 +746,11 @@ static int s5m_rtc_probe(struct platform_device *pdev)
goto err;
}
+ if (!info->irq) {
+ dev_info(&pdev->dev, "Alarm IRQ not available\n");
+ return 0;
+ }
+
ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL,
s5m_rtc_alarm_irq, 0, "rtc-alarm0",
info);
@@ -802,7 +809,7 @@ static int s5m_rtc_resume(struct device *dev)
struct s5m_rtc_info *info = dev_get_drvdata(dev);
int ret = 0;
- if (device_may_wakeup(dev))
+ if (info->irq && device_may_wakeup(dev))
ret = disable_irq_wake(info->irq);
return ret;
@@ -813,7 +820,7 @@ static int s5m_rtc_suspend(struct device *dev)
struct s5m_rtc_info *info = dev_get_drvdata(dev);
int ret = 0;
- if (device_may_wakeup(dev))
+ if (info->irq && device_may_wakeup(dev))
ret = enable_irq_wake(info->irq);
return ret;
diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
new file mode 100644
index 00000000000..c169a2cd472
--- /dev/null
+++ b/drivers/rtc/rtc-sun6i.c
@@ -0,0 +1,447 @@
+/*
+ * An RTC driver for Allwinner A31/A23
+ *
+ * Copyright (c) 2014, Chen-Yu Tsai <wens@csie.org>
+ *
+ * based on rtc-sunxi.c
+ *
+ * An RTC driver for Allwinner A10/A20
+ *
+ * Copyright (c) 2013, Carlo Caione <carlo.caione@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/types.h>
+
+/* Control register */
+#define SUN6I_LOSC_CTRL 0x0000
+#define SUN6I_LOSC_CTRL_ALM_DHMS_ACC BIT(9)
+#define SUN6I_LOSC_CTRL_RTC_HMS_ACC BIT(8)
+#define SUN6I_LOSC_CTRL_RTC_YMD_ACC BIT(7)
+#define SUN6I_LOSC_CTRL_ACC_MASK GENMASK(9, 7)
+
+/* RTC */
+#define SUN6I_RTC_YMD 0x0010
+#define SUN6I_RTC_HMS 0x0014
+
+/* Alarm 0 (counter) */
+#define SUN6I_ALRM_COUNTER 0x0020
+#define SUN6I_ALRM_CUR_VAL 0x0024
+#define SUN6I_ALRM_EN 0x0028
+#define SUN6I_ALRM_EN_CNT_EN BIT(0)
+#define SUN6I_ALRM_IRQ_EN 0x002c
+#define SUN6I_ALRM_IRQ_EN_CNT_IRQ_EN BIT(0)
+#define SUN6I_ALRM_IRQ_STA 0x0030
+#define SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND BIT(0)
+
+/* Alarm 1 (wall clock) */
+#define SUN6I_ALRM1_EN 0x0044
+#define SUN6I_ALRM1_IRQ_EN 0x0048
+#define SUN6I_ALRM1_IRQ_STA 0x004c
+#define SUN6I_ALRM1_IRQ_STA_WEEK_IRQ_PEND BIT(0)
+
+/* Alarm config */
+#define SUN6I_ALARM_CONFIG 0x0050
+#define SUN6I_ALARM_CONFIG_WAKEUP BIT(0)
+
+/*
+ * Get date values
+ */
+#define SUN6I_DATE_GET_DAY_VALUE(x) ((x) & 0x0000001f)
+#define SUN6I_DATE_GET_MON_VALUE(x) (((x) & 0x00000f00) >> 8)
+#define SUN6I_DATE_GET_YEAR_VALUE(x) (((x) & 0x003f0000) >> 16)
+#define SUN6I_LEAP_GET_VALUE(x) (((x) & 0x00400000) >> 22)
+
+/*
+ * Get time values
+ */
+#define SUN6I_TIME_GET_SEC_VALUE(x) ((x) & 0x0000003f)
+#define SUN6I_TIME_GET_MIN_VALUE(x) (((x) & 0x00003f00) >> 8)
+#define SUN6I_TIME_GET_HOUR_VALUE(x) (((x) & 0x001f0000) >> 16)
+
+/*
+ * Set date values
+ */
+#define SUN6I_DATE_SET_DAY_VALUE(x) ((x) & 0x0000001f)
+#define SUN6I_DATE_SET_MON_VALUE(x) ((x) << 8 & 0x00000f00)
+#define SUN6I_DATE_SET_YEAR_VALUE(x) ((x) << 16 & 0x003f0000)
+#define SUN6I_LEAP_SET_VALUE(x) ((x) << 22 & 0x00400000)
+
+/*
+ * Set time values
+ */
+#define SUN6I_TIME_SET_SEC_VALUE(x) ((x) & 0x0000003f)
+#define SUN6I_TIME_SET_MIN_VALUE(x) ((x) << 8 & 0x00003f00)
+#define SUN6I_TIME_SET_HOUR_VALUE(x) ((x) << 16 & 0x001f0000)
+
+/*
+ * The year parameter passed to the driver is usually an offset relative to
+ * the year 1900. This macro is used to convert this offset to another one
+ * relative to the minimum year allowed by the hardware.
+ *
+ * The year range is 1970 - 2033. This range is selected to match Allwinner's
+ * driver, even though it is somewhat limited.
+ */
+#define SUN6I_YEAR_MIN 1970
+#define SUN6I_YEAR_MAX 2033
+#define SUN6I_YEAR_OFF (SUN6I_YEAR_MIN - 1900)
+
+struct sun6i_rtc_dev {
+ struct rtc_device *rtc;
+ struct device *dev;
+ void __iomem *base;
+ int irq;
+ unsigned long alarm;
+};
+
+static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id)
+{
+ struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id;
+ u32 val;
+
+ val = readl(chip->base + SUN6I_ALRM_IRQ_STA);
+
+ if (val & SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND) {
+ val |= SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND;
+ writel(val, chip->base + SUN6I_ALRM_IRQ_STA);
+
+ rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF);
+
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip)
+{
+ u32 alrm_val = 0;
+ u32 alrm_irq_val = 0;
+ u32 alrm_wake_val = 0;
+
+ if (to) {
+ alrm_val = SUN6I_ALRM_EN_CNT_EN;
+ alrm_irq_val = SUN6I_ALRM_IRQ_EN_CNT_IRQ_EN;
+ alrm_wake_val = SUN6I_ALARM_CONFIG_WAKEUP;
+ } else {
+ writel(SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND,
+ chip->base + SUN6I_ALRM_IRQ_STA);
+ }
+
+ writel(alrm_val, chip->base + SUN6I_ALRM_EN);
+ writel(alrm_irq_val, chip->base + SUN6I_ALRM_IRQ_EN);
+ writel(alrm_wake_val, chip->base + SUN6I_ALARM_CONFIG);
+}
+
+static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
+{
+ struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
+ u32 date, time;
+
+ /*
+ * read again in case it changes
+ */
+ do {
+ date = readl(chip->base + SUN6I_RTC_YMD);
+ time = readl(chip->base + SUN6I_RTC_HMS);
+ } while ((date != readl(chip->base + SUN6I_RTC_YMD)) ||
+ (time != readl(chip->base + SUN6I_RTC_HMS)));
+
+ rtc_tm->tm_sec = SUN6I_TIME_GET_SEC_VALUE(time);
+ rtc_tm->tm_min = SUN6I_TIME_GET_MIN_VALUE(time);
+ rtc_tm->tm_hour = SUN6I_TIME_GET_HOUR_VALUE(time);
+
+ rtc_tm->tm_mday = SUN6I_DATE_GET_DAY_VALUE(date);
+ rtc_tm->tm_mon = SUN6I_DATE_GET_MON_VALUE(date);
+ rtc_tm->tm_year = SUN6I_DATE_GET_YEAR_VALUE(date);
+
+ rtc_tm->tm_mon -= 1;
+
+ /*
+ * switch from (data_year->min)-relative offset to
+ * a (1900)-relative one
+ */
+ rtc_tm->tm_year += SUN6I_YEAR_OFF;
+
+ return rtc_valid_tm(rtc_tm);
+}
+
+static int sun6i_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
+ u32 alrm_st;
+ u32 alrm_en;
+
+ alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN);
+ alrm_st = readl(chip->base + SUN6I_ALRM_IRQ_STA);
+ wkalrm->enabled = !!(alrm_en & SUN6I_ALRM_EN_CNT_EN);
+ wkalrm->pending = !!(alrm_st & SUN6I_ALRM_EN_CNT_EN);
+ rtc_time_to_tm(chip->alarm, &wkalrm->time);
+
+ return 0;
+}
+
+static int sun6i_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
+ struct rtc_time *alrm_tm = &wkalrm->time;
+ struct rtc_time tm_now;
+ unsigned long time_now = 0;
+ unsigned long time_set = 0;
+ unsigned long time_gap = 0;
+ int ret = 0;
+
+ ret = sun6i_rtc_gettime(dev, &tm_now);
+ if (ret < 0) {
+ dev_err(dev, "Error in getting time\n");
+ return -EINVAL;
+ }
+
+ rtc_tm_to_time(alrm_tm, &time_set);
+ rtc_tm_to_time(&tm_now, &time_now);
+ if (time_set <= time_now) {
+ dev_err(dev, "Date to set in the past\n");
+ return -EINVAL;
+ }
+
+ time_gap = time_set - time_now;
+
+ if (time_gap > U32_MAX) {
+ dev_err(dev, "Date too far in the future\n");
+ return -EINVAL;
+ }
+
+ sun6i_rtc_setaie(0, chip);
+ writel(0, chip->base + SUN6I_ALRM_COUNTER);
+ usleep_range(100, 300);
+
+ writel(time_gap, chip->base + SUN6I_ALRM_COUNTER);
+ chip->alarm = time_set;
+
+ sun6i_rtc_setaie(wkalrm->enabled, chip);
+
+ return 0;
+}
+
+static int sun6i_rtc_wait(struct sun6i_rtc_dev *chip, int offset,
+ unsigned int mask, unsigned int ms_timeout)
+{
+ const unsigned long timeout = jiffies + msecs_to_jiffies(ms_timeout);
+ u32 reg;
+
+ do {
+ reg = readl(chip->base + offset);
+ reg &= mask;
+
+ if (!reg)
+ return 0;
+
+ } while (time_before(jiffies, timeout));
+
+ return -ETIMEDOUT;
+}
+
+static int sun6i_rtc_settime(struct device *dev, struct rtc_time *rtc_tm)
+{
+ struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
+ u32 date = 0;
+ u32 time = 0;
+ int year;
+
+ year = rtc_tm->tm_year + 1900;
+ if (year < SUN6I_YEAR_MIN || year > SUN6I_YEAR_MAX) {
+ dev_err(dev, "rtc only supports year in range %d - %d\n",
+ SUN6I_YEAR_MIN, SUN6I_YEAR_MAX);
+ return -EINVAL;
+ }
+
+ rtc_tm->tm_year -= SUN6I_YEAR_OFF;
+ rtc_tm->tm_mon += 1;
+
+ date = SUN6I_DATE_SET_DAY_VALUE(rtc_tm->tm_mday) |
+ SUN6I_DATE_SET_MON_VALUE(rtc_tm->tm_mon) |
+ SUN6I_DATE_SET_YEAR_VALUE(rtc_tm->tm_year);
+
+ if (is_leap_year(year))
+ date |= SUN6I_LEAP_SET_VALUE(1);
+
+ time = SUN6I_TIME_SET_SEC_VALUE(rtc_tm->tm_sec) |
+ SUN6I_TIME_SET_MIN_VALUE(rtc_tm->tm_min) |
+ SUN6I_TIME_SET_HOUR_VALUE(rtc_tm->tm_hour);
+
+ /* Check whether registers are writable */
+ if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL,
+ SUN6I_LOSC_CTRL_ACC_MASK, 50)) {
+ dev_err(dev, "rtc is still busy.\n");
+ return -EBUSY;
+ }
+
+ writel(time, chip->base + SUN6I_RTC_HMS);
+
+ /*
+ * After writing the RTC HH-MM-SS register, the
+ * SUN6I_LOSC_CTRL_RTC_HMS_ACC bit is set and it will not
+ * be cleared until the real writing operation is finished
+ */
+
+ if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL,
+ SUN6I_LOSC_CTRL_RTC_HMS_ACC, 50)) {
+ dev_err(dev, "Failed to set rtc time.\n");
+ return -ETIMEDOUT;
+ }
+
+ writel(date, chip->base + SUN6I_RTC_YMD);
+
+ /*
+ * After writing the RTC YY-MM-DD register, the
+ * SUN6I_LOSC_CTRL_RTC_YMD_ACC bit is set and it will not
+ * be cleared until the real writing operation is finished
+ */
+
+ if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL,
+ SUN6I_LOSC_CTRL_RTC_YMD_ACC, 50)) {
+ dev_err(dev, "Failed to set rtc time.\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int sun6i_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
+
+ if (!enabled)
+ sun6i_rtc_setaie(enabled, chip);
+
+ return 0;
+}
+
+static const struct rtc_class_ops sun6i_rtc_ops = {
+ .read_time = sun6i_rtc_gettime,
+ .set_time = sun6i_rtc_settime,
+ .read_alarm = sun6i_rtc_getalarm,
+ .set_alarm = sun6i_rtc_setalarm,
+ .alarm_irq_enable = sun6i_rtc_alarm_irq_enable
+};
+
+static int sun6i_rtc_probe(struct platform_device *pdev)
+{
+ struct sun6i_rtc_dev *chip;
+ struct resource *res;
+ int ret;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, chip);
+ chip->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ chip->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(chip->base))
+ return PTR_ERR(chip->base);
+
+ chip->irq = platform_get_irq(pdev, 0);
+ if (chip->irq < 0) {
+ dev_err(&pdev->dev, "No IRQ resource\n");
+ return chip->irq;
+ }
+
+ ret = devm_request_irq(&pdev->dev, chip->irq, sun6i_rtc_alarmirq,
+ 0, dev_name(&pdev->dev), chip);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not request IRQ\n");
+ return ret;
+ }
+
+ /* clear the alarm counter value */
+ writel(0, chip->base + SUN6I_ALRM_COUNTER);
+
+ /* disable counter alarm */
+ writel(0, chip->base + SUN6I_ALRM_EN);
+
+ /* disable counter alarm interrupt */
+ writel(0, chip->base + SUN6I_ALRM_IRQ_EN);
+
+ /* disable week alarm */
+ writel(0, chip->base + SUN6I_ALRM1_EN);
+
+ /* disable week alarm interrupt */
+ writel(0, chip->base + SUN6I_ALRM1_IRQ_EN);
+
+ /* clear counter alarm pending interrupts */
+ writel(SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND,
+ chip->base + SUN6I_ALRM_IRQ_STA);
+
+ /* clear week alarm pending interrupts */
+ writel(SUN6I_ALRM1_IRQ_STA_WEEK_IRQ_PEND,
+ chip->base + SUN6I_ALRM1_IRQ_STA);
+
+ /* disable alarm wakeup */
+ writel(0, chip->base + SUN6I_ALARM_CONFIG);
+
+ chip->rtc = rtc_device_register("rtc-sun6i", &pdev->dev,
+ &sun6i_rtc_ops, THIS_MODULE);
+ if (IS_ERR(chip->rtc)) {
+ dev_err(&pdev->dev, "unable to register device\n");
+ return PTR_ERR(chip->rtc);
+ }
+
+ dev_info(&pdev->dev, "RTC enabled\n");
+
+ return 0;
+}
+
+static int sun6i_rtc_remove(struct platform_device *pdev)
+{
+ struct sun6i_rtc_dev *chip = platform_get_drvdata(pdev);
+
+ rtc_device_unregister(chip->rtc);
+
+ return 0;
+}
+
+static const struct of_device_id sun6i_rtc_dt_ids[] = {
+ { .compatible = "allwinner,sun6i-a31-rtc" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids);
+
+static struct platform_driver sun6i_rtc_driver = {
+ .probe = sun6i_rtc_probe,
+ .remove = sun6i_rtc_remove,
+ .driver = {
+ .name = "sun6i-rtc",
+ .of_match_table = sun6i_rtc_dt_ids,
+ },
+};
+
+module_platform_driver(sun6i_rtc_driver);
+
+MODULE_DESCRIPTION("sun6i RTC driver");
+MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-tps65910.c b/drivers/rtc/rtc-tps65910.c
index 7af00208d63..2583349fbde 100644
--- a/drivers/rtc/rtc-tps65910.c
+++ b/drivers/rtc/rtc-tps65910.c
@@ -258,6 +258,8 @@ static int tps65910_rtc_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
+ platform_set_drvdata(pdev, tps_rtc);
+
irq = platform_get_irq(pdev, 0);
if (irq <= 0) {
dev_warn(&pdev->dev, "Wake up is not possible as irq = %d\n",
@@ -283,8 +285,6 @@ static int tps65910_rtc_probe(struct platform_device *pdev)
return ret;
}
- platform_set_drvdata(pdev, tps_rtc);
-
return 0;
}