summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-class-rtc-rtc0-device-rtc_calibration5
-rw-r--r--Documentation/devicetree/bindings/rtc/epson,rtc7301.txt16
-rw-r--r--Documentation/devicetree/bindings/rtc/ingenic,jz4740-rtc.txt37
-rw-r--r--Documentation/devicetree/bindings/rtc/twl-rtc.txt19
-rw-r--r--arch/mips/boot/dts/ingenic/jz4740.dtsi11
-rw-r--r--arch/mips/boot/dts/ingenic/qi_lb60.dts4
-rw-r--r--arch/mips/include/asm/mach-jz4740/platform.h1
-rw-r--r--arch/mips/jz4740/board-qi_lb60.c1
-rw-r--r--arch/mips/jz4740/platform.c21
-rw-r--r--arch/mips/jz4740/reset.c63
-rw-r--r--drivers/rtc/Kconfig38
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-cmos.c75
-rw-r--r--drivers/rtc/rtc-ds1307.c52
-rw-r--r--drivers/rtc/rtc-ds1374.c4
-rw-r--r--drivers/rtc/rtc-imxdi.c2
-rw-r--r--drivers/rtc/rtc-jz4740.c154
-rw-r--r--drivers/rtc/rtc-lib.c4
-rw-r--r--drivers/rtc/rtc-mcp795.c122
-rw-r--r--drivers/rtc/rtc-pcf85063.c7
-rw-r--r--drivers/rtc/rtc-r7301.c453
-rw-r--r--drivers/rtc/rtc-starfire.c10
-rw-r--r--drivers/rtc/rtc-sun4v.c10
-rw-r--r--drivers/rtc/rtc-twl.c202
24 files changed, 1055 insertions, 257 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-rtc-rtc0-device-rtc_calibration b/Documentation/ABI/testing/sysfs-class-rtc-rtc0-device-rtc_calibration
index 4cf1e72222d9..ec950c93e5c6 100644
--- a/Documentation/ABI/testing/sysfs-class-rtc-rtc0-device-rtc_calibration
+++ b/Documentation/ABI/testing/sysfs-class-rtc-rtc0-device-rtc_calibration
@@ -1,8 +1,9 @@
-What: Attribute for calibrating ST-Ericsson AB8500 Real Time Clock
+What: /sys/class/rtc/rtc0/device/rtc_calibration
Date: Oct 2011
KernelVersion: 3.0
Contact: Mark Godfrey <mark.godfrey@stericsson.com>
-Description: The rtc_calibration attribute allows the userspace to
+Description: Attribute for calibrating ST-Ericsson AB8500 Real Time Clock
+ The rtc_calibration attribute allows the userspace to
calibrate the AB8500.s 32KHz Real Time Clock.
Every 60 seconds the AB8500 will correct the RTC's value
by adding to it the value of this attribute.
diff --git a/Documentation/devicetree/bindings/rtc/epson,rtc7301.txt b/Documentation/devicetree/bindings/rtc/epson,rtc7301.txt
new file mode 100644
index 000000000000..5f9df3f1467c
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/epson,rtc7301.txt
@@ -0,0 +1,16 @@
+EPSON TOYOCOM RTC-7301SF/DG
+
+Required properties:
+
+- compatible: Should be "epson,rtc7301sf" or "epson,rtc7301dg"
+- reg: Specifies base physical address and size of the registers.
+- interrupts: A single interrupt specifier.
+
+Example:
+
+rtc: rtc@44a00000 {
+ compatible = "epson,rtc7301dg";
+ reg = <0x44a00000 0x10000>;
+ interrupt-parent = <&axi_intc_0>;
+ interrupts = <3 2>;
+};
diff --git a/Documentation/devicetree/bindings/rtc/ingenic,jz4740-rtc.txt b/Documentation/devicetree/bindings/rtc/ingenic,jz4740-rtc.txt
new file mode 100644
index 000000000000..41c7ae18fd7b
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/ingenic,jz4740-rtc.txt
@@ -0,0 +1,37 @@
+JZ4740 and similar SoCs real-time clock driver
+
+Required properties:
+
+- compatible: One of:
+ - "ingenic,jz4740-rtc" - for use with the JZ4740 SoC
+ - "ingenic,jz4780-rtc" - for use with the JZ4780 SoC
+- reg: Address range of rtc register set
+- interrupts: IRQ number for the alarm interrupt
+- clocks: phandle to the "rtc" clock
+- clock-names: must be "rtc"
+
+Optional properties:
+- system-power-controller: To use this component as the
+ system power controller
+- reset-pin-assert-time-ms: Reset pin low-level assertion
+ time after wakeup (default 60ms; range 0-125ms if RTC clock
+ at 32 kHz)
+- min-wakeup-pin-assert-time-ms: Minimum wakeup pin assertion
+ time (default 100ms; range 0-2s if RTC clock at 32 kHz)
+
+Example:
+
+rtc@10003000 {
+ compatible = "ingenic,jz4740-rtc";
+ reg = <0x10003000 0x40>;
+
+ interrupt-parent = <&intc>;
+ interrupts = <32>;
+
+ clocks = <&rtc_clock>;
+ clock-names = "rtc";
+
+ system-power-controller;
+ reset-pin-assert-time-ms = <60>;
+ min-wakeup-pin-assert-time-ms = <100>;
+};
diff --git a/Documentation/devicetree/bindings/rtc/twl-rtc.txt b/Documentation/devicetree/bindings/rtc/twl-rtc.txt
index 596e0c97be7a..8f9a94f2f896 100644
--- a/Documentation/devicetree/bindings/rtc/twl-rtc.txt
+++ b/Documentation/devicetree/bindings/rtc/twl-rtc.txt
@@ -1,12 +1,11 @@
-* TI twl RTC
-
-The TWL family (twl4030/6030) contains a RTC.
+* Texas Instruments TWL4030/6030 RTC
Required properties:
-- compatible : Should be twl4030-rtc
-
-Examples:
-
-rtc@0 {
- compatible = "ti,twl4030-rtc";
-};
+- compatible : Should be "ti,twl4030-rtc"
+- interrupts : Should be the interrupt number.
+
+Example:
+ rtc {
+ compatible = "ti,twl4030-rtc";
+ interrupts = <11>;
+ };
diff --git a/arch/mips/boot/dts/ingenic/jz4740.dtsi b/arch/mips/boot/dts/ingenic/jz4740.dtsi
index f6ae6ed9c4b1..3e1587f1f77a 100644
--- a/arch/mips/boot/dts/ingenic/jz4740.dtsi
+++ b/arch/mips/boot/dts/ingenic/jz4740.dtsi
@@ -44,6 +44,17 @@
#clock-cells = <1>;
};
+ rtc_dev: rtc@10003000 {
+ compatible = "ingenic,jz4740-rtc";
+ reg = <0x10003000 0x40>;
+
+ interrupt-parent = <&intc>;
+ interrupts = <15>;
+
+ clocks = <&cgu JZ4740_CLK_RTC>;
+ clock-names = "rtc";
+ };
+
uart0: serial@10030000 {
compatible = "ingenic,jz4740-uart";
reg = <0x10030000 0x100>;
diff --git a/arch/mips/boot/dts/ingenic/qi_lb60.dts b/arch/mips/boot/dts/ingenic/qi_lb60.dts
index 2414d63ae818..be1a7d3a3e1b 100644
--- a/arch/mips/boot/dts/ingenic/qi_lb60.dts
+++ b/arch/mips/boot/dts/ingenic/qi_lb60.dts
@@ -13,3 +13,7 @@
&ext {
clock-frequency = <12000000>;
};
+
+&rtc_dev {
+ system-power-controller;
+};
diff --git a/arch/mips/include/asm/mach-jz4740/platform.h b/arch/mips/include/asm/mach-jz4740/platform.h
index 073b8bfbb3b3..3645974b7f65 100644
--- a/arch/mips/include/asm/mach-jz4740/platform.h
+++ b/arch/mips/include/asm/mach-jz4740/platform.h
@@ -22,7 +22,6 @@
extern struct platform_device jz4740_udc_device;
extern struct platform_device jz4740_udc_xceiv_device;
extern struct platform_device jz4740_mmc_device;
-extern struct platform_device jz4740_rtc_device;
extern struct platform_device jz4740_i2c_device;
extern struct platform_device jz4740_nand_device;
extern struct platform_device jz4740_framebuffer_device;
diff --git a/arch/mips/jz4740/board-qi_lb60.c b/arch/mips/jz4740/board-qi_lb60.c
index 258fd03c9ef5..a5bd94b95263 100644
--- a/arch/mips/jz4740/board-qi_lb60.c
+++ b/arch/mips/jz4740/board-qi_lb60.c
@@ -438,7 +438,6 @@ static struct platform_device *jz_platform_devices[] __initdata = {
&jz4740_pcm_device,
&jz4740_i2s_device,
&jz4740_codec_device,
- &jz4740_rtc_device,
&jz4740_adc_device,
&jz4740_pwm_device,
&jz4740_dma_device,
diff --git a/arch/mips/jz4740/platform.c b/arch/mips/jz4740/platform.c
index 2f1dab35c061..5b7cdd67a9d9 100644
--- a/arch/mips/jz4740/platform.c
+++ b/arch/mips/jz4740/platform.c
@@ -88,27 +88,6 @@ struct platform_device jz4740_mmc_device = {
.resource = jz4740_mmc_resources,
};
-/* RTC controller */
-static struct resource jz4740_rtc_resources[] = {
- {
- .start = JZ4740_RTC_BASE_ADDR,
- .end = JZ4740_RTC_BASE_ADDR + 0x38 - 1,
- .flags = IORESOURCE_MEM,
- },
- {
- .start = JZ4740_IRQ_RTC,
- .end = JZ4740_IRQ_RTC,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-struct platform_device jz4740_rtc_device = {
- .name = "jz4740-rtc",
- .id = -1,
- .num_resources = ARRAY_SIZE(jz4740_rtc_resources),
- .resource = jz4740_rtc_resources,
-};
-
/* I2C controller */
static struct resource jz4740_i2c_resources[] = {
{
diff --git a/arch/mips/jz4740/reset.c b/arch/mips/jz4740/reset.c
index 954e669c9e6b..67780c4b6573 100644
--- a/arch/mips/jz4740/reset.c
+++ b/arch/mips/jz4740/reset.c
@@ -57,71 +57,8 @@ static void jz4740_restart(char *command)
jz4740_halt();
}
-#define JZ_REG_RTC_CTRL 0x00
-#define JZ_REG_RTC_HIBERNATE 0x20
-#define JZ_REG_RTC_WAKEUP_FILTER 0x24
-#define JZ_REG_RTC_RESET_COUNTER 0x28
-
-#define JZ_RTC_CTRL_WRDY BIT(7)
-#define JZ_RTC_WAKEUP_FILTER_MASK 0x0000FFE0
-#define JZ_RTC_RESET_COUNTER_MASK 0x00000FE0
-
-static inline void jz4740_rtc_wait_ready(void __iomem *rtc_base)
-{
- uint32_t ctrl;
-
- do {
- ctrl = readl(rtc_base + JZ_REG_RTC_CTRL);
- } while (!(ctrl & JZ_RTC_CTRL_WRDY));
-}
-
-static void jz4740_power_off(void)
-{
- void __iomem *rtc_base = ioremap(JZ4740_RTC_BASE_ADDR, 0x38);
- unsigned long wakeup_filter_ticks;
- unsigned long reset_counter_ticks;
- struct clk *rtc_clk;
- unsigned long rtc_rate;
-
- rtc_clk = clk_get(NULL, "rtc");
- if (IS_ERR(rtc_clk))
- panic("unable to get RTC clock");
- rtc_rate = clk_get_rate(rtc_clk);
- clk_put(rtc_clk);
-
- /*
- * Set minimum wakeup pin assertion time: 100 ms.
- * Range is 0 to 2 sec if RTC is clocked at 32 kHz.
- */
- wakeup_filter_ticks = (100 * rtc_rate) / 1000;
- if (wakeup_filter_ticks < JZ_RTC_WAKEUP_FILTER_MASK)
- wakeup_filter_ticks &= JZ_RTC_WAKEUP_FILTER_MASK;
- else
- wakeup_filter_ticks = JZ_RTC_WAKEUP_FILTER_MASK;
- jz4740_rtc_wait_ready(rtc_base);
- writel(wakeup_filter_ticks, rtc_base + JZ_REG_RTC_WAKEUP_FILTER);
-
- /*
- * Set reset pin low-level assertion time after wakeup: 60 ms.
- * Range is 0 to 125 ms if RTC is clocked at 32 kHz.
- */
- reset_counter_ticks = (60 * rtc_rate) / 1000;
- if (reset_counter_ticks < JZ_RTC_RESET_COUNTER_MASK)
- reset_counter_ticks &= JZ_RTC_RESET_COUNTER_MASK;
- else
- reset_counter_ticks = JZ_RTC_RESET_COUNTER_MASK;
- jz4740_rtc_wait_ready(rtc_base);
- writel(reset_counter_ticks, rtc_base + JZ_REG_RTC_RESET_COUNTER);
-
- jz4740_rtc_wait_ready(rtc_base);
- writel(1, rtc_base + JZ_REG_RTC_HIBERNATE);
-
- jz4740_halt();
-}
-
void jz4740_reset_init(void)
{
_machine_restart = jz4740_restart;
_machine_halt = jz4740_halt;
- pm_power_off = jz4740_power_off;
}
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index e859d148aba9..c93c5a8fba32 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -303,7 +303,7 @@ config RTC_DRV_MAX6900
config RTC_DRV_MAX8907
tristate "Maxim MAX8907"
- depends on MFD_MAX8907
+ depends on MFD_MAX8907 || COMPILE_TEST
help
If you say yes here you will get support for the
RTC of Maxim MAX8907 PMIC.
@@ -343,7 +343,7 @@ config RTC_DRV_MAX8997
config RTC_DRV_MAX77686
tristate "Maxim MAX77686"
- depends on MFD_MAX77686 || MFD_MAX77620
+ depends on MFD_MAX77686 || MFD_MAX77620 || COMPILE_TEST
help
If you say yes here you will get support for the
RTC of Maxim MAX77686/MAX77620/MAX77802 PMIC.
@@ -481,6 +481,7 @@ config RTC_DRV_TWL92330
config RTC_DRV_TWL4030
tristate "TI TWL4030/TWL5030/TWL6030/TPS659x0"
depends on TWL4030_CORE
+ depends on OF
help
If you say yes here you get support for the RTC on the
TWL4030/TWL5030/TWL6030 family chips, used mostly with OMAP3 platforms.
@@ -602,7 +603,8 @@ config RTC_DRV_RV8803
config RTC_DRV_S5M
tristate "Samsung S2M/S5M series"
- depends on MFD_SEC_CORE
+ depends on MFD_SEC_CORE || COMPILE_TEST
+ select REGMAP_IRQ
help
If you say yes here you will get support for the
RTC of Samsung S2MPS14 and S5M PMIC series.
@@ -820,8 +822,8 @@ config RTC_DRV_RV3029_HWMON
comment "Platform RTC drivers"
-# this 'CMOS' RTC driver is arch dependent because <asm-generic/rtc.h>
-# requires <asm/mc146818rtc.h> defining CMOS_READ/CMOS_WRITE, and a
+# this 'CMOS' RTC driver is arch dependent because it requires
+# <asm/mc146818rtc.h> defining CMOS_READ/CMOS_WRITE, and a
# global rtc_lock ... it's not yet just another platform_device.
config RTC_DRV_CMOS
@@ -1549,14 +1551,11 @@ config RTC_DRV_MPC5121
will be called rtc-mpc5121.
config RTC_DRV_JZ4740
- tristate "Ingenic JZ4740 SoC"
- depends on MACH_JZ4740 || COMPILE_TEST
+ bool "Ingenic JZ4740 SoC"
+ depends on MACH_INGENIC || COMPILE_TEST
help
- If you say yes here you get support for the Ingenic JZ4740 SoC RTC
- controller.
-
- This driver can also be buillt as a module. If so, the module
- will be called rtc-jz4740.
+ If you say yes here you get support for the Ingenic JZ47xx SoCs RTC
+ controllers.
config RTC_DRV_LPC24XX
tristate "NXP RTC for LPC178x/18xx/408x/43xx"
@@ -1567,7 +1566,7 @@ config RTC_DRV_LPC24XX
NXP LPC178x/18xx/408x/43xx devices.
If you have one of the devices above enable this driver to use
- the hardware RTC. This driver can also be buillt as a module. If
+ the hardware RTC. This driver can also be built as a module. If
so, the module will be called rtc-lpc24xx.
config RTC_DRV_LPC32XX
@@ -1576,7 +1575,7 @@ config RTC_DRV_LPC32XX
help
This enables support for the NXP RTC in the LPC32XX
- This driver can also be buillt as a module. If so, the module
+ This driver can also be built as a module. If so, the module
will be called rtc-lpc32xx.
config RTC_DRV_PM8XXX
@@ -1706,6 +1705,17 @@ config RTC_DRV_PIC32
This driver can also be built as a module. If so, the module
will be called rtc-pic32
+config RTC_DRV_R7301
+ tristate "EPSON TOYOCOM RTC-7301SF/DG"
+ select REGMAP_MMIO
+ depends on OF && HAS_IOMEM
+ help
+ If you say yes here you get support for the EPSON TOYOCOM
+ RTC-7301SF/DG chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-r7301.
+
comment "HID Sensor RTC drivers"
config RTC_DRV_HID_SENSOR_TIME
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 1ac694a330c8..f13ab1c5c222 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -120,6 +120,7 @@ obj-$(CONFIG_RTC_DRV_PM8XXX) += rtc-pm8xxx.o
obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o
obj-$(CONFIG_RTC_DRV_PUV3) += rtc-puv3.o
obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.o
+obj-$(CONFIG_RTC_DRV_R7301) += rtc-r7301.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
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index 38aa8e1906c2..f4a96dbdabf2 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -332,14 +332,86 @@ static void cmos_irq_disable(struct cmos_rtc *cmos, unsigned char mask)
cmos_checkintr(cmos, rtc_control);
}
+static int cmos_validate_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+ struct cmos_rtc *cmos = dev_get_drvdata(dev);
+ struct rtc_time now;
+
+ cmos_read_time(dev, &now);
+
+ if (!cmos->day_alrm) {
+ time64_t t_max_date;
+ time64_t t_alrm;
+
+ t_max_date = rtc_tm_to_time64(&now);
+ t_max_date += 24 * 60 * 60 - 1;
+ t_alrm = rtc_tm_to_time64(&t->time);
+ if (t_alrm > t_max_date) {
+ dev_err(dev,
+ "Alarms can be up to one day in the future\n");
+ return -EINVAL;
+ }
+ } else if (!cmos->mon_alrm) {
+ struct rtc_time max_date = now;
+ time64_t t_max_date;
+ time64_t t_alrm;
+ int max_mday;
+
+ if (max_date.tm_mon == 11) {
+ max_date.tm_mon = 0;
+ max_date.tm_year += 1;
+ } else {
+ max_date.tm_mon += 1;
+ }
+ max_mday = rtc_month_days(max_date.tm_mon, max_date.tm_year);
+ if (max_date.tm_mday > max_mday)
+ max_date.tm_mday = max_mday;
+
+ t_max_date = rtc_tm_to_time64(&max_date);
+ t_max_date -= 1;
+ t_alrm = rtc_tm_to_time64(&t->time);
+ if (t_alrm > t_max_date) {
+ dev_err(dev,
+ "Alarms can be up to one month in the future\n");
+ return -EINVAL;
+ }
+ } else {
+ struct rtc_time max_date = now;
+ time64_t t_max_date;
+ time64_t t_alrm;
+ int max_mday;
+
+ max_date.tm_year += 1;
+ max_mday = rtc_month_days(max_date.tm_mon, max_date.tm_year);
+ if (max_date.tm_mday > max_mday)
+ max_date.tm_mday = max_mday;
+
+ t_max_date = rtc_tm_to_time64(&max_date);
+ t_max_date -= 1;
+ t_alrm = rtc_tm_to_time64(&t->time);
+ if (t_alrm > t_max_date) {
+ dev_err(dev,
+ "Alarms can be up to one year in the future\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
{
struct cmos_rtc *cmos = dev_get_drvdata(dev);
unsigned char mon, mday, hrs, min, sec, rtc_control;
+ int ret;
if (!is_valid_irq(cmos->irq))
return -EIO;
+ ret = cmos_validate_alarm(dev, t);
+ if (ret < 0)
+ return ret;
+
mon = t->time.tm_mon + 1;
mday = t->time.tm_mday;
hrs = t->time.tm_hour;
@@ -707,9 +779,6 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
spin_unlock_irq(&rtc_lock);
- /* FIXME:
- * <asm-generic/rtc.h> doesn't know 12-hour mode either.
- */
if (is_valid_irq(rtc_irq) && !(rtc_control & RTC_24H)) {
dev_warn(dev, "only 24-hr supported\n");
retval = -ENXIO;
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index 4e31036ee259..4ad97be48043 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -11,6 +11,7 @@
* published by the Free Software Foundation.
*/
+#include <linux/acpi.h>
#include <linux/bcd.h>
#include <linux/i2c.h>
#include <linux/init.h>
@@ -191,6 +192,26 @@ static const struct i2c_device_id ds1307_id[] = {
};
MODULE_DEVICE_TABLE(i2c, ds1307_id);
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id ds1307_acpi_ids[] = {
+ { .id = "DS1307", .driver_data = ds_1307 },
+ { .id = "DS1337", .driver_data = ds_1337 },
+ { .id = "DS1338", .driver_data = ds_1338 },
+ { .id = "DS1339", .driver_data = ds_1339 },
+ { .id = "DS1388", .driver_data = ds_1388 },
+ { .id = "DS1340", .driver_data = ds_1340 },
+ { .id = "DS3231", .driver_data = ds_3231 },
+ { .id = "M41T00", .driver_data = m41t00 },
+ { .id = "MCP7940X", .driver_data = mcp794xx },
+ { .id = "MCP7941X", .driver_data = mcp794xx },
+ { .id = "PT7C4338", .driver_data = ds_1307 },
+ { .id = "RX8025", .driver_data = rx_8025 },
+ { .id = "ISL12057", .driver_data = ds_1337 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, ds1307_acpi_ids);
+#endif
+
/*----------------------------------------------------------------------*/
#define BLOCK_DATA_MAX_TRIES 10
@@ -874,17 +895,17 @@ static u8 do_trickle_setup_ds1339(struct i2c_client *client,
return setup;
}
-static void ds1307_trickle_of_init(struct i2c_client *client,
- struct chip_desc *chip)
+static void ds1307_trickle_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))
+ if (device_property_read_u32(&client->dev, "trickle-resistor-ohms", &ohms))
goto out;
- if (of_property_read_bool(client->dev.of_node, "trickle-diode-disable"))
+ if (device_property_read_bool(&client->dev, "trickle-diode-disable"))
diode = false;
chip->trickle_charger_setup = chip->do_trickle_setup(client,
ohms, diode);
@@ -1268,7 +1289,7 @@ static int ds1307_probe(struct i2c_client *client,
struct ds1307 *ds1307;
int err = -ENODEV;
int tmp, wday;
- struct chip_desc *chip = &chips[id->driver_data];
+ struct chip_desc *chip;
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
bool want_irq = false;
bool ds1307_can_wakeup_device = false;
@@ -1297,11 +1318,23 @@ static int ds1307_probe(struct i2c_client *client,
i2c_set_clientdata(client, ds1307);
ds1307->client = client;
- ds1307->type = id->driver_data;
+ if (id) {
+ chip = &chips[id->driver_data];
+ ds1307->type = id->driver_data;
+ } else {
+ const struct acpi_device_id *acpi_id;
+
+ acpi_id = acpi_match_device(ACPI_PTR(ds1307_acpi_ids),
+ &client->dev);
+ if (!acpi_id)
+ return -ENODEV;
+ chip = &chips[acpi_id->driver_data];
+ ds1307->type = acpi_id->driver_data;
+ }
- if (!pdata && client->dev.of_node)
- ds1307_trickle_of_init(client, chip);
- else if (pdata && pdata->trickle_charger_setup)
+ if (!pdata)
+ ds1307_trickle_init(client, chip);
+ else if (pdata->trickle_charger_setup)
chip->trickle_charger_setup = pdata->trickle_charger_setup;
if (chip->trickle_charger_setup && chip->trickle_charger_reg) {
@@ -1678,6 +1711,7 @@ static int ds1307_remove(struct i2c_client *client)
static struct i2c_driver ds1307_driver = {
.driver = {
.name = "rtc-ds1307",
+ .acpi_match_table = ACPI_PTR(ds1307_acpi_ids),
},
.probe = ds1307_probe,
.remove = ds1307_remove,
diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c
index 3b3049c8c9e0..52429f0a57cc 100644
--- a/drivers/rtc/rtc-ds1374.c
+++ b/drivers/rtc/rtc-ds1374.c
@@ -89,10 +89,8 @@ static int ds1374_read_rtc(struct i2c_client *client, u32 *time,
int ret;
int i;
- if (nbytes > 4) {
- WARN_ON(1);
+ if (WARN_ON(nbytes > 4))
return -EINVAL;
- }
ret = i2c_smbus_read_i2c_block_data(client, reg, nbytes, buf);
diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c
index 8d8049bdfaf6..67b56b80dc70 100644
--- a/drivers/rtc/rtc-imxdi.c
+++ b/drivers/rtc/rtc-imxdi.c
@@ -67,7 +67,7 @@
#define DSR_ETAD (1 << 21) /* External tamper A detected */
#define DSR_EBD (1 << 20) /* External boot detected */
#define DSR_SAD (1 << 19) /* SCC alarm detected */
-#define DSR_TTD (1 << 18) /* Temperatur tamper detected */
+#define DSR_TTD (1 << 18) /* Temperature tamper detected */
#define DSR_CTD (1 << 17) /* Clock tamper detected */
#define DSR_VTD (1 << 16) /* Voltage tamper detected */
#define DSR_WBF (1 << 10) /* Write Busy Flag (synchronous) */
diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c
index 5e14651b71a8..72918c1ba092 100644
--- a/drivers/rtc/rtc-jz4740.c
+++ b/drivers/rtc/rtc-jz4740.c
@@ -14,10 +14,12 @@
*
*/
+#include <linux/clk.h>
#include <linux/io.h>
#include <linux/kernel.h>
-#include <linux/module.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/reboot.h>
#include <linux/rtc.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
@@ -27,8 +29,14 @@
#define JZ_REG_RTC_SEC_ALARM 0x08
#define JZ_REG_RTC_REGULATOR 0x0C
#define JZ_REG_RTC_HIBERNATE 0x20
+#define JZ_REG_RTC_WAKEUP_FILTER 0x24
+#define JZ_REG_RTC_RESET_COUNTER 0x28
#define JZ_REG_RTC_SCRATCHPAD 0x34
+/* The following are present on the jz4780 */
+#define JZ_REG_RTC_WENR 0x3C
+#define JZ_RTC_WENR_WEN BIT(31)
+
#define JZ_RTC_CTRL_WRDY BIT(7)
#define JZ_RTC_CTRL_1HZ BIT(6)
#define JZ_RTC_CTRL_1HZ_IRQ BIT(5)
@@ -37,16 +45,34 @@
#define JZ_RTC_CTRL_AE BIT(2)
#define JZ_RTC_CTRL_ENABLE BIT(0)
+/* Magic value to enable writes on jz4780 */
+#define JZ_RTC_WENR_MAGIC 0xA55A
+
+#define JZ_RTC_WAKEUP_FILTER_MASK 0x0000FFE0
+#define JZ_RTC_RESET_COUNTER_MASK 0x00000FE0
+
+enum jz4740_rtc_type {
+ ID_JZ4740,
+ ID_JZ4780,
+};
+
struct jz4740_rtc {
void __iomem *base;
+ enum jz4740_rtc_type type;
struct rtc_device *rtc;
+ struct clk *clk;
int irq;
spinlock_t lock;
+
+ unsigned int min_wakeup_pin_assert_time;
+ unsigned int reset_pin_assert_time;
};
+static struct device *dev_for_power_off;
+
static inline uint32_t jz4740_rtc_reg_read(struct jz4740_rtc *rtc, size_t reg)
{
return readl(rtc->base + reg);
@@ -64,11 +90,33 @@ static int jz4740_rtc_wait_write_ready(struct jz4740_rtc *rtc)
return timeout ? 0 : -EIO;
}
+static inline int jz4780_rtc_enable_write(struct jz4740_rtc *rtc)
+{
+ uint32_t ctrl;
+ int ret, timeout = 1000;
+
+ ret = jz4740_rtc_wait_write_ready(rtc);
+ if (ret != 0)
+ return ret;
+
+ writel(JZ_RTC_WENR_MAGIC, rtc->base + JZ_REG_RTC_WENR);
+
+ do {
+ ctrl = readl(rtc->base + JZ_REG_RTC_WENR);
+ } while (!(ctrl & JZ_RTC_WENR_WEN) && --timeout);
+
+ return timeout ? 0 : -EIO;
+}
+
static inline int jz4740_rtc_reg_write(struct jz4740_rtc *rtc, size_t reg,
uint32_t val)
{
- int ret;
- ret = jz4740_rtc_wait_write_ready(rtc);
+ int ret = 0;
+
+ if (rtc->type >= ID_JZ4780)
+ ret = jz4780_rtc_enable_write(rtc);
+ if (ret == 0)
+ ret = jz4740_rtc_wait_write_ready(rtc);
if (ret == 0)
writel(val, rtc->base + reg);
@@ -203,12 +251,57 @@ static irqreturn_t jz4740_rtc_irq(int irq, void *data)
return IRQ_HANDLED;
}
-void jz4740_rtc_poweroff(struct device *dev)
+static void jz4740_rtc_poweroff(struct device *dev)
{
struct jz4740_rtc *rtc = dev_get_drvdata(dev);
jz4740_rtc_reg_write(rtc, JZ_REG_RTC_HIBERNATE, 1);
}
-EXPORT_SYMBOL_GPL(jz4740_rtc_poweroff);
+
+static void jz4740_rtc_power_off(void)
+{
+ struct jz4740_rtc *rtc = dev_get_drvdata(dev_for_power_off);
+ unsigned long rtc_rate;
+ unsigned long wakeup_filter_ticks;
+ unsigned long reset_counter_ticks;
+
+ clk_prepare_enable(rtc->clk);
+
+ rtc_rate = clk_get_rate(rtc->clk);
+
+ /*
+ * Set minimum wakeup pin assertion time: 100 ms.
+ * Range is 0 to 2 sec if RTC is clocked at 32 kHz.
+ */
+ wakeup_filter_ticks =
+ (rtc->min_wakeup_pin_assert_time * rtc_rate) / 1000;
+ if (wakeup_filter_ticks < JZ_RTC_WAKEUP_FILTER_MASK)
+ wakeup_filter_ticks &= JZ_RTC_WAKEUP_FILTER_MASK;
+ else
+ wakeup_filter_ticks = JZ_RTC_WAKEUP_FILTER_MASK;
+ jz4740_rtc_reg_write(rtc,
+ JZ_REG_RTC_WAKEUP_FILTER, wakeup_filter_ticks);
+
+ /*
+ * Set reset pin low-level assertion time after wakeup: 60 ms.
+ * Range is 0 to 125 ms if RTC is clocked at 32 kHz.
+ */
+ reset_counter_ticks = (rtc->reset_pin_assert_time * rtc_rate) / 1000;
+ if (reset_counter_ticks < JZ_RTC_RESET_COUNTER_MASK)
+ reset_counter_ticks &= JZ_RTC_RESET_COUNTER_MASK;
+ else
+ reset_counter_ticks = JZ_RTC_RESET_COUNTER_MASK;
+ jz4740_rtc_reg_write(rtc,
+ JZ_REG_RTC_RESET_COUNTER, reset_counter_ticks);
+
+ jz4740_rtc_poweroff(dev_for_power_off);
+ machine_halt();
+}
+
+static const struct of_device_id jz4740_rtc_of_match[] = {
+ { .compatible = "ingenic,jz4740-rtc", .data = (void *)ID_JZ4740 },
+ { .compatible = "ingenic,jz4780-rtc", .data = (void *)ID_JZ4780 },
+ {},
+};
static int jz4740_rtc_probe(struct platform_device *pdev)
{
@@ -216,11 +309,20 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
struct jz4740_rtc *rtc;
uint32_t scratchpad;
struct resource *mem;
+ const struct platform_device_id *id = platform_get_device_id(pdev);
+ const struct of_device_id *of_id = of_match_device(
+ jz4740_rtc_of_match, &pdev->dev);
+ struct device_node *np = pdev->dev.of_node;
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
if (!rtc)
return -ENOMEM;
+ if (of_id)
+ rtc->type = (enum jz4740_rtc_type)of_id->data;
+ else
+ rtc->type = id->driver_data;
+
rtc->irq = platform_get_irq(pdev, 0);
if (rtc->irq < 0) {
dev_err(&pdev->dev, "Failed to get platform irq\n");
@@ -232,6 +334,12 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
if (IS_ERR(rtc->base))
return PTR_ERR(rtc->base);
+ rtc->clk = devm_clk_get(&pdev->dev, "rtc");
+ if (IS_ERR(rtc->clk)) {
+ dev_err(&pdev->dev, "Failed to get RTC clock\n");
+ return PTR_ERR(rtc->clk);
+ }
+
spin_lock_init(&rtc->lock);
platform_set_drvdata(pdev, rtc);
@@ -263,6 +371,27 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
}
}
+ if (np && of_device_is_system_power_controller(np)) {
+ if (!pm_power_off) {
+ /* Default: 60ms */
+ rtc->reset_pin_assert_time = 60;
+ of_property_read_u32(np, "reset-pin-assert-time-ms",
+ &rtc->reset_pin_assert_time);
+
+ /* Default: 100ms */
+ rtc->min_wakeup_pin_assert_time = 100;
+ of_property_read_u32(np,
+ "min-wakeup-pin-assert-time-ms",
+ &rtc->min_wakeup_pin_assert_time);
+
+ dev_for_power_off = &pdev->dev;
+ pm_power_off = jz4740_rtc_power_off;
+ } else {
+ dev_warn(&pdev->dev,
+ "Poweroff handler already present!\n");
+ }
+ }
+
return 0;
}
@@ -295,17 +424,20 @@ static const struct dev_pm_ops jz4740_pm_ops = {
#define JZ4740_RTC_PM_OPS NULL
#endif /* CONFIG_PM */
+static const struct platform_device_id jz4740_rtc_ids[] = {
+ { "jz4740-rtc", ID_JZ4740 },
+ { "jz4780-rtc", ID_JZ4780 },
+ {}
+};
+
static struct platform_driver jz4740_rtc_driver = {
.probe = jz4740_rtc_probe,
.driver = {
.name = "jz4740-rtc",
.pm = JZ4740_RTC_PM_OPS,
+ .of_match_table = of_match_ptr(jz4740_rtc_of_match),
},
+ .id_table = jz4740_rtc_ids,
};
-module_platform_driver(jz4740_rtc_driver);
-
-MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("RTC driver for the JZ4740 SoC\n");
-MODULE_ALIAS("platform:jz4740-rtc");
+builtin_platform_driver(jz4740_rtc_driver);
diff --git a/drivers/rtc/rtc-lib.c b/drivers/rtc/rtc-lib.c
index e6bfb9c42a10..1ae7da5cfc60 100644
--- a/drivers/rtc/rtc-lib.c
+++ b/drivers/rtc/rtc-lib.c
@@ -11,7 +11,7 @@
* published by the Free Software Foundation.
*/
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/rtc.h>
static const unsigned char rtc_days_in_month[] = {
@@ -148,5 +148,3 @@ struct rtc_time rtc_ktime_to_tm(ktime_t kt)
return ret;
}
EXPORT_SYMBOL_GPL(rtc_ktime_to_tm);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-mcp795.c b/drivers/rtc/rtc-mcp795.c
index 4021fd04cb0a..ce75e421ba00 100644
--- a/drivers/rtc/rtc-mcp795.c
+++ b/drivers/rtc/rtc-mcp795.c
@@ -12,7 +12,7 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
- * */
+ */
#include <linux/module.h>
#include <linux/kernel.h>
@@ -21,6 +21,8 @@
#include <linux/spi/spi.h>
#include <linux/rtc.h>
#include <linux/of.h>
+#include <linux/bcd.h>
+#include <linux/delay.h>
/* MCP795 Instructions, see datasheet table 3-1 */
#define MCP795_EEREAD 0x03
@@ -29,7 +31,7 @@
#define MCP795_EEWREN 0x06
#define MCP795_SRREAD 0x05
#define MCP795_SRWRITE 0x01
-#define MCP795_READ 0x13
+#define MCP795_READ 0x13
#define MCP795_WRITE 0x12
#define MCP795_UNLOCK 0x14
#define MCP795_IDWRITE 0x32
@@ -37,8 +39,17 @@
#define MCP795_CLRWDT 0x44
#define MCP795_CLRRAM 0x54
-#define MCP795_ST_BIT 0x80
-#define MCP795_24_BIT 0x40
+/* MCP795 RTCC registers, see datasheet table 4-1 */
+#define MCP795_REG_SECONDS 0x01
+#define MCP795_REG_DAY 0x04
+#define MCP795_REG_MONTH 0x06
+#define MCP795_REG_CONTROL 0x08
+
+#define MCP795_ST_BIT BIT(7)
+#define MCP795_24_BIT BIT(6)
+#define MCP795_LP_BIT BIT(5)
+#define MCP795_EXTOSC_BIT BIT(3)
+#define MCP795_OSCON_BIT BIT(5)
static int mcp795_rtcc_read(struct device *dev, u8 addr, u8 *buf, u8 count)
{
@@ -93,30 +104,97 @@ static int mcp795_rtcc_set_bits(struct device *dev, u8 addr, u8 mask, u8 state)
return ret;
}
+static int mcp795_stop_oscillator(struct device *dev, bool *extosc)
+{
+ int retries = 5;
+ int ret;
+ u8 data;
+
+ ret = mcp795_rtcc_set_bits(dev, MCP795_REG_SECONDS, MCP795_ST_BIT, 0);
+ if (ret)
+ return ret;
+ ret = mcp795_rtcc_read(dev, MCP795_REG_CONTROL, &data, 1);
+ if (ret)
+ return ret;
+ *extosc = !!(data & MCP795_EXTOSC_BIT);
+ ret = mcp795_rtcc_set_bits(
+ dev, MCP795_REG_CONTROL, MCP795_EXTOSC_BIT, 0);
+ if (ret)
+ return ret;
+ /* wait for the OSCON bit to clear */
+ do {
+ usleep_range(700, 800);
+ ret = mcp795_rtcc_read(dev, MCP795_REG_DAY, &data, 1);
+ if (ret)
+ break;
+ if (!(data & MCP795_OSCON_BIT))
+ break;
+
+ } while (--retries);
+
+ return !retries ? -EIO : ret;
+}
+
+static int mcp795_start_oscillator(struct device *dev, bool *extosc)
+{
+ if (extosc) {
+ u8 data = *extosc ? MCP795_EXTOSC_BIT : 0;
+ int ret;
+
+ ret = mcp795_rtcc_set_bits(
+ dev, MCP795_REG_CONTROL, MCP795_EXTOSC_BIT, data);
+ if (ret)
+ return ret;
+ }
+ return mcp795_rtcc_set_bits(
+ dev, MCP795_REG_SECONDS, MCP795_ST_BIT, MCP795_ST_BIT);
+}
+
static int mcp795_set_time(struct device *dev, struct rtc_time *tim)
{
int ret;
u8 data[7];
+ bool extosc;
+
+ /* Stop RTC and store current value of EXTOSC bit */
+ ret = mcp795_stop_oscillator(dev, &extosc);
+ if (ret)
+ return ret;
/* Read first, so we can leave config bits untouched */
- ret = mcp795_rtcc_read(dev, 0x01, data, sizeof(data));
+ ret = mcp795_rtcc_read(dev, MCP795_REG_SECONDS, data, sizeof(data));
if (ret)
return ret;
- data[0] = (data[0] & 0x80) | ((tim->tm_sec / 10) << 4) | (tim->tm_sec % 10);
- data[1] = (data[1] & 0x80) | ((tim->tm_min / 10) << 4) | (tim->tm_min % 10);
- data[2] = ((tim->tm_hour / 10) << 4) | (tim->tm_hour % 10);
- data[4] = ((tim->tm_mday / 10) << 4) | ((tim->tm_mday) % 10);
- data[5] = (data[5] & 0x10) | (tim->tm_mon / 10) | (tim->tm_mon % 10);
+ data[0] = (data[0] & 0x80) | bin2bcd(tim->tm_sec);
+ data[1] = (data[1] & 0x80) | bin2bcd(tim->tm_min);
+ data[2] = bin2bcd(tim->tm_hour);
+ data[4] = bin2bcd(tim->tm_mday);
+ data[5] = (data[5] & MCP795_LP_BIT) | bin2bcd(tim->tm_mon + 1);
if (tim->tm_year > 100)
tim->tm_year -= 100;
- data[6] = ((tim->tm_year / 10) << 4) | (tim->tm_year % 10);
+ data[6] = bin2bcd(tim->tm_year);
+
+ /* Always write the date and month using a separate Write command.
+ * This is a workaround for a know silicon issue that some combinations
+ * of date and month values may result in the date being reset to 1.
+ */
+ ret = mcp795_rtcc_write(dev, MCP795_REG_SECONDS, data, 5);
+ if (ret)
+ return ret;
- ret = mcp795_rtcc_write(dev, 0x01, data, sizeof(data));
+ ret = mcp795_rtcc_write(dev, MCP795_REG_MONTH, &data[5], 2);
+ if (ret)
+ return ret;
+ /* Start back RTC and restore previous value of EXTOSC bit.
+ * There is no need to clear EXTOSC bit when the previous value was 0
+ * because it was already cleared when stopping the RTC oscillator.
+ */
+ ret = mcp795_start_oscillator(dev, extosc ? &extosc : NULL);
if (ret)
return ret;
@@ -132,17 +210,17 @@ static int mcp795_read_time(struct device *dev, struct rtc_time *tim)
int ret;
u8 data[7];
- ret = mcp795_rtcc_read(dev, 0x01, data, sizeof(data));
+ ret = mcp795_rtcc_read(dev, MCP795_REG_SECONDS, data, sizeof(data));
if (ret)
return ret;
- tim->tm_sec = ((data[0] & 0x70) >> 4) * 10 + (data[0] & 0x0f);
- tim->tm_min = ((data[1] & 0x70) >> 4) * 10 + (data[1] & 0x0f);
- tim->tm_hour = ((data[2] & 0x30) >> 4) * 10 + (data[2] & 0x0f);
- tim->tm_mday = ((data[4] & 0x30) >> 4) * 10 + (data[4] & 0x0f);
- tim->tm_mon = ((data[5] & 0x10) >> 4) * 10 + (data[5] & 0x0f);
- tim->tm_year = ((data[6] & 0xf0) >> 4) * 10 + (data[6] & 0x0f) + 100; /* Assume we are in 20xx */
+ tim->tm_sec = bcd2bin(data[0] & 0x7F);
+ tim->tm_min = bcd2bin(data[1] & 0x7F);
+ tim->tm_hour = bcd2bin(data[2] & 0x3F);
+ tim->tm_mday = bcd2bin(data[4] & 0x3F);
+ tim->tm_mon = bcd2bin(data[5] & 0x1F) - 1;
+ tim->tm_year = bcd2bin(data[6]) + 100; /* Assume we are in 20xx */
dev_dbg(dev, "Read from mcp795: %04d-%02d-%02d %02d:%02d:%02d\n",
tim->tm_year + 1900, tim->tm_mon, tim->tm_mday,
@@ -169,13 +247,13 @@ static int mcp795_probe(struct spi_device *spi)
return ret;
}
- /* Start the oscillator */
- mcp795_rtcc_set_bits(&spi->dev, 0x01, MCP795_ST_BIT, MCP795_ST_BIT);
+ /* Start the oscillator but don't set the value of EXTOSC bit */
+ mcp795_start_oscillator(&spi->dev, NULL);
/* Clear the 12 hour mode flag*/
mcp795_rtcc_set_bits(&spi->dev, 0x03, MCP795_24_BIT, 0);
rtc = devm_rtc_device_register(&spi->dev, "rtc-mcp795",
- &mcp795_rtc_ops, THIS_MODULE);
+ &mcp795_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc))
return PTR_ERR(rtc);
diff --git a/drivers/rtc/rtc-pcf85063.c b/drivers/rtc/rtc-pcf85063.c
index efb0a08ac117..a06dff994c83 100644
--- a/drivers/rtc/rtc-pcf85063.c
+++ b/drivers/rtc/rtc-pcf85063.c
@@ -191,12 +191,19 @@ static int pcf85063_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct rtc_device *rtc;
+ int err;
dev_dbg(&client->dev, "%s\n", __func__);
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -ENODEV;
+ err = i2c_smbus_read_byte_data(client, PCF85063_REG_CTRL1);
+ if (err < 0) {
+ dev_err(&client->dev, "RTC chip is not present\n");
+ return err;
+ }
+
rtc = devm_rtc_device_register(&client->dev,
pcf85063_driver.driver.name,
&pcf85063_rtc_ops, THIS_MODULE);
diff --git a/drivers/rtc/rtc-r7301.c b/drivers/rtc/rtc-r7301.c
new file mode 100644
index 000000000000..28d540885f3d
--- /dev/null
+++ b/drivers/rtc/rtc-r7301.c
@@ -0,0 +1,453 @@
+/*
+ * EPSON TOYOCOM RTC-7301SF/DG Driver
+ *
+ * Copyright (c) 2016 Akinobu Mita <akinobu.mita@gmail.com>
+ *
+ * Based on rtc-rp5c01.c
+ *
+ * Datasheet: http://www5.epsondevice.com/en/products/parallel/rtc7301sf.html
+ */
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+
+#define DRV_NAME "rtc-r7301"
+
+#define RTC7301_1_SEC 0x0 /* Bank 0 and Band 1 */
+#define RTC7301_10_SEC 0x1 /* Bank 0 and Band 1 */
+#define RTC7301_AE BIT(3)
+#define RTC7301_1_MIN 0x2 /* Bank 0 and Band 1 */
+#define RTC7301_10_MIN 0x3 /* Bank 0 and Band 1 */
+#define RTC7301_1_HOUR 0x4 /* Bank 0 and Band 1 */
+#define RTC7301_10_HOUR 0x5 /* Bank 0 and Band 1 */
+#define RTC7301_DAY_OF_WEEK 0x6 /* Bank 0 and Band 1 */
+#define RTC7301_1_DAY 0x7 /* Bank 0 and Band 1 */
+#define RTC7301_10_DAY 0x8 /* Bank 0 and Band 1 */
+#define RTC7301_1_MONTH 0x9 /* Bank 0 */
+#define RTC7301_10_MONTH 0xa /* Bank 0 */
+#define RTC7301_1_YEAR 0xb /* Bank 0 */
+#define RTC7301_10_YEAR 0xc /* Bank 0 */
+#define RTC7301_100_YEAR 0xd /* Bank 0 */
+#define RTC7301_1000_YEAR 0xe /* Bank 0 */
+#define RTC7301_ALARM_CONTROL 0xe /* Bank 1 */
+#define RTC7301_ALARM_CONTROL_AIE BIT(0)
+#define RTC7301_ALARM_CONTROL_AF BIT(1)
+#define RTC7301_TIMER_CONTROL 0xe /* Bank 2 */
+#define RTC7301_TIMER_CONTROL_TIE BIT(0)
+#define RTC7301_TIMER_CONTROL_TF BIT(1)
+#define RTC7301_CONTROL 0xf /* All banks */
+#define RTC7301_CONTROL_BUSY BIT(0)
+#define RTC7301_CONTROL_STOP BIT(1)
+#define RTC7301_CONTROL_BANK_SEL_0 BIT(2)
+#define RTC7301_CONTROL_BANK_SEL_1 BIT(3)
+
+struct rtc7301_priv {
+ struct regmap *regmap;
+ int irq;
+ spinlock_t lock;
+ u8 bank;
+};
+
+static const struct regmap_config rtc7301_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .reg_stride = 4,
+};
+
+static u8 rtc7301_read(struct rtc7301_priv *priv, unsigned int reg)
+{
+ int reg_stride = regmap_get_reg_stride(priv->regmap);
+ unsigned int val;
+
+ regmap_read(priv->regmap, reg_stride * reg, &val);
+
+ return val & 0xf;
+}
+
+static void rtc7301_write(struct rtc7301_priv *priv, u8 val, unsigned int reg)
+{
+ int reg_stride = regmap_get_reg_stride(priv->regmap);
+
+ regmap_write(priv->regmap, reg_stride * reg, val);
+}
+
+static void rtc7301_update_bits(struct rtc7301_priv *priv, unsigned int reg,
+ u8 mask, u8 val)
+{
+ int reg_stride = regmap_get_reg_stride(priv->regmap);
+
+ regmap_update_bits(priv->regmap, reg_stride * reg, mask, val);
+}
+
+static int rtc7301_wait_while_busy(struct rtc7301_priv *priv)
+{
+ int retries = 100;
+
+ while (retries-- > 0) {
+ u8 val;
+
+ val = rtc7301_read(priv, RTC7301_CONTROL);
+ if (!(val & RTC7301_CONTROL_BUSY))
+ return 0;
+
+ usleep_range(200, 300);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static void rtc7301_stop(struct rtc7301_priv *priv)
+{
+ rtc7301_update_bits(priv, RTC7301_CONTROL, RTC7301_CONTROL_STOP,
+ RTC7301_CONTROL_STOP);
+}
+
+static void rtc7301_start(struct rtc7301_priv *priv)
+{
+ rtc7301_update_bits(priv, RTC7301_CONTROL, RTC7301_CONTROL_STOP, 0);
+}
+
+static void rtc7301_select_bank(struct rtc7301_priv *priv, u8 bank)
+{
+ u8 val = 0;
+
+ if (bank == priv->bank)
+ return;
+
+ if (bank & BIT(0))
+ val |= RTC7301_CONTROL_BANK_SEL_0;
+ if (bank & BIT(1))
+ val |= RTC7301_CONTROL_BANK_SEL_1;
+
+ rtc7301_update_bits(priv, RTC7301_CONTROL,
+ RTC7301_CONTROL_BANK_SEL_0 |
+ RTC7301_CONTROL_BANK_SEL_1, val);
+
+ priv->bank = bank;
+}
+
+static void rtc7301_get_time(struct rtc7301_priv *priv, struct rtc_time *tm,
+ bool alarm)
+{
+ int year;
+
+ tm->tm_sec = rtc7301_read(priv, RTC7301_1_SEC);
+ tm->tm_sec += (rtc7301_read(priv, RTC7301_10_SEC) & ~RTC7301_AE) * 10;
+ tm->tm_min = rtc7301_read(priv, RTC7301_1_MIN);
+ tm->tm_min += (rtc7301_read(priv, RTC7301_10_MIN) & ~RTC7301_AE) * 10;
+ tm->tm_hour = rtc7301_read(priv, RTC7301_1_HOUR);
+ tm->tm_hour += (rtc7301_read(priv, RTC7301_10_HOUR) & ~RTC7301_AE) * 10;
+ tm->tm_mday = rtc7301_read(priv, RTC7301_1_DAY);
+ tm->tm_mday += (rtc7301_read(priv, RTC7301_10_DAY) & ~RTC7301_AE) * 10;
+
+ if (alarm) {
+ tm->tm_wday = -1;
+ tm->tm_mon = -1;
+ tm->tm_year = -1;
+ tm->tm_yday = -1;
+ tm->tm_isdst = -1;
+ return;
+ }
+
+ tm->tm_wday = (rtc7301_read(priv, RTC7301_DAY_OF_WEEK) & ~RTC7301_AE);
+ tm->tm_mon = rtc7301_read(priv, RTC7301_10_MONTH) * 10 +
+ rtc7301_read(priv, RTC7301_1_MONTH) - 1;
+ year = rtc7301_read(priv, RTC7301_1000_YEAR) * 1000 +
+ rtc7301_read(priv, RTC7301_100_YEAR) * 100 +
+ rtc7301_read(priv, RTC7301_10_YEAR) * 10 +
+ rtc7301_read(priv, RTC7301_1_YEAR);
+
+ tm->tm_year = year - 1900;
+}
+
+static void rtc7301_write_time(struct rtc7301_priv *priv, struct rtc_time *tm,
+ bool alarm)
+{
+ int year;
+
+ rtc7301_write(priv, tm->tm_sec % 10, RTC7301_1_SEC);
+ rtc7301_write(priv, tm->tm_sec / 10, RTC7301_10_SEC);
+
+ rtc7301_write(priv, tm->tm_min % 10, RTC7301_1_MIN);
+ rtc7301_write(priv, tm->tm_min / 10, RTC7301_10_MIN);
+
+ rtc7301_write(priv, tm->tm_hour % 10, RTC7301_1_HOUR);
+ rtc7301_write(priv, tm->tm_hour / 10, RTC7301_10_HOUR);
+
+ rtc7301_write(priv, tm->tm_mday % 10, RTC7301_1_DAY);
+ rtc7301_write(priv, tm->tm_mday / 10, RTC7301_10_DAY);
+
+ /* Don't care for alarm register */
+ rtc7301_write(priv, alarm ? RTC7301_AE : tm->tm_wday,
+ RTC7301_DAY_OF_WEEK);
+
+ if (alarm)
+ return;
+
+ rtc7301_write(priv, (tm->tm_mon + 1) % 10, RTC7301_1_MONTH);
+ rtc7301_write(priv, (tm->tm_mon + 1) / 10, RTC7301_10_MONTH);
+
+ year = tm->tm_year + 1900;
+
+ rtc7301_write(priv, year % 10, RTC7301_1_YEAR);
+ rtc7301_write(priv, (year / 10) % 10, RTC7301_10_YEAR);
+ rtc7301_write(priv, (year / 100) % 10, RTC7301_100_YEAR);
+ rtc7301_write(priv, year / 1000, RTC7301_1000_YEAR);
+}
+
+static void rtc7301_alarm_irq(struct rtc7301_priv *priv, unsigned int enabled)
+{
+ rtc7301_update_bits(priv, RTC7301_ALARM_CONTROL,
+ RTC7301_ALARM_CONTROL_AF |
+ RTC7301_ALARM_CONTROL_AIE,
+ enabled ? RTC7301_ALARM_CONTROL_AIE : 0);
+}
+
+static int rtc7301_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct rtc7301_priv *priv = dev_get_drvdata(dev);
+ unsigned long flags;
+ int err;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ rtc7301_select_bank(priv, 0);
+
+ err = rtc7301_wait_while_busy(priv);
+ if (!err)
+ rtc7301_get_time(priv, tm, false);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return err ? err : rtc_valid_tm(tm);
+}
+
+static int rtc7301_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct rtc7301_priv *priv = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ rtc7301_stop(priv);
+ usleep_range(200, 300);
+ rtc7301_select_bank(priv, 0);
+ rtc7301_write_time(priv, tm, false);
+ rtc7301_start(priv);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static int rtc7301_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct rtc7301_priv *priv = dev_get_drvdata(dev);
+ unsigned long flags;
+ u8 alrm_ctrl;
+
+ if (priv->irq <= 0)
+ return -EINVAL;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ rtc7301_select_bank(priv, 1);
+ rtc7301_get_time(priv, &alarm->time, true);
+
+ alrm_ctrl = rtc7301_read(priv, RTC7301_ALARM_CONTROL);
+
+ alarm->enabled = !!(alrm_ctrl & RTC7301_ALARM_CONTROL_AIE);
+ alarm->pending = !!(alrm_ctrl & RTC7301_ALARM_CONTROL_AF);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static int rtc7301_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct rtc7301_priv *priv = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ if (priv->irq <= 0)
+ return -EINVAL;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ rtc7301_select_bank(priv, 1);
+ rtc7301_write_time(priv, &alarm->time, true);
+ rtc7301_alarm_irq(priv, alarm->enabled);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static int rtc7301_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct rtc7301_priv *priv = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ if (priv->irq <= 0)
+ return -EINVAL;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ rtc7301_select_bank(priv, 1);
+ rtc7301_alarm_irq(priv, enabled);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static const struct rtc_class_ops rtc7301_rtc_ops = {
+ .read_time = rtc7301_read_time,
+ .set_time = rtc7301_set_time,
+ .read_alarm = rtc7301_read_alarm,
+ .set_alarm = rtc7301_set_alarm,
+ .alarm_irq_enable = rtc7301_alarm_irq_enable,
+};
+
+static irqreturn_t rtc7301_irq_handler(int irq, void *dev_id)
+{
+ struct rtc_device *rtc = dev_id;
+ struct rtc7301_priv *priv = dev_get_drvdata(rtc->dev.parent);
+ unsigned long flags;
+ irqreturn_t ret = IRQ_NONE;
+ u8 alrm_ctrl;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ rtc7301_select_bank(priv, 1);
+
+ alrm_ctrl = rtc7301_read(priv, RTC7301_ALARM_CONTROL);
+ if (alrm_ctrl & RTC7301_ALARM_CONTROL_AF) {
+ ret = IRQ_HANDLED;
+ rtc7301_alarm_irq(priv, false);
+ rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF);
+ }
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return ret;
+}
+
+static void rtc7301_init(struct rtc7301_priv *priv)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ rtc7301_select_bank(priv, 2);
+ rtc7301_write(priv, 0, RTC7301_TIMER_CONTROL);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static int __init rtc7301_rtc_probe(struct platform_device *dev)
+{
+ struct resource *res;
+ void __iomem *regs;
+ struct rtc7301_priv *priv;
+ struct rtc_device *rtc;
+ int ret;
+
+ res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ regs = devm_ioremap_resource(&dev->dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ priv->regmap = devm_regmap_init_mmio(&dev->dev, regs,
+ &rtc7301_regmap_config);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ priv->irq = platform_get_irq(dev, 0);
+
+ spin_lock_init(&priv->lock);
+ priv->bank = -1;
+
+ rtc7301_init(priv);
+
+ platform_set_drvdata(dev, priv);
+
+ rtc = devm_rtc_device_register(&dev->dev, DRV_NAME, &rtc7301_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ if (priv->irq > 0) {
+ ret = devm_request_irq(&dev->dev, priv->irq,
+ rtc7301_irq_handler, IRQF_SHARED,
+ dev_name(&dev->dev), rtc);
+ if (ret) {
+ priv->irq = 0;
+ dev_err(&dev->dev, "unable to request IRQ\n");
+ } else {
+ device_set_wakeup_capable(&dev->dev, true);
+ }
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int rtc7301_suspend(struct device *dev)
+{
+ struct rtc7301_priv *priv = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(priv->irq);
+
+ return 0;
+}
+
+static int rtc7301_resume(struct device *dev)
+{
+ struct rtc7301_priv *priv = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(priv->irq);
+
+ return 0;
+}
+
+#endif
+
+static SIMPLE_DEV_PM_OPS(rtc7301_pm_ops, rtc7301_suspend, rtc7301_resume);
+
+static const struct of_device_id rtc7301_dt_match[] = {
+ { .compatible = "epson,rtc7301sf" },
+ { .compatible = "epson,rtc7301dg" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rtc7301_dt_match);
+
+static struct platform_driver rtc7301_rtc_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = rtc7301_dt_match,
+ .pm = &rtc7301_pm_ops,
+ },
+};
+
+module_platform_driver_probe(rtc7301_rtc_driver, rtc7301_rtc_probe);
+
+MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("EPSON TOYOCOM RTC-7301SF/DG Driver");
+MODULE_ALIAS("platform:rtc-r7301");
diff --git a/drivers/rtc/rtc-starfire.c b/drivers/rtc/rtc-starfire.c
index 83a057a03060..7fc36973fa33 100644
--- a/drivers/rtc/rtc-starfire.c
+++ b/drivers/rtc/rtc-starfire.c
@@ -1,20 +1,18 @@
/* rtc-starfire.c: Starfire platform RTC driver.
*
+ * Author: David S. Miller
+ * License: GPL
+ *
* Copyright (C) 2008 David S. Miller <davem@davemloft.net>
*/
#include <linux/kernel.h>
-#include <linux/module.h>
#include <linux/init.h>
#include <linux/rtc.h>
#include <linux/platform_device.h>
#include <asm/oplib.h>
-MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
-MODULE_DESCRIPTION("Starfire RTC driver");
-MODULE_LICENSE("GPL");
-
static u32 starfire_get_time(void)
{
static char obp_gettod[32];
@@ -57,4 +55,4 @@ static struct platform_driver starfire_rtc_driver = {
},
};
-module_platform_driver_probe(starfire_rtc_driver, starfire_rtc_probe);
+builtin_platform_driver_probe(starfire_rtc_driver, starfire_rtc_probe);
diff --git a/drivers/rtc/rtc-sun4v.c b/drivers/rtc/rtc-sun4v.c
index 7c696c12f28f..11bc562eba5d 100644
--- a/drivers/rtc/rtc-sun4v.c
+++ b/drivers/rtc/rtc-sun4v.c
@@ -1,12 +1,14 @@
/* rtc-sun4v.c: Hypervisor based RTC for SUN4V systems.
*
+ * Author: David S. Miller
+ * License: GPL
+ *
* Copyright (C) 2008 David S. Miller <davem@davemloft.net>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
-#include <linux/module.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/rtc.h>
@@ -98,8 +100,4 @@ static struct platform_driver sun4v_rtc_driver = {
},
};
-module_platform_driver_probe(sun4v_rtc_driver, sun4v_rtc_probe);
-
-MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
-MODULE_DESCRIPTION("SUN4V RTC driver");
-MODULE_LICENSE("GPL");
+builtin_platform_driver_probe(sun4v_rtc_driver, sun4v_rtc_probe);
diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c
index 176720b7b9e5..c18c39212ce6 100644
--- a/drivers/rtc/rtc-twl.c
+++ b/drivers/rtc/rtc-twl.c
@@ -33,6 +33,10 @@
#include <linux/i2c/twl.h>
+enum twl_class {
+ TWL_4030 = 0,
+ TWL_6030,
+};
/*
* RTC block register offsets (use TWL_MODULE_RTC)
@@ -136,16 +140,30 @@ static const u8 twl6030_rtc_reg_map[] = {
#define ALL_TIME_REGS 6
/*----------------------------------------------------------------------*/
-static u8 *rtc_reg_map;
+struct twl_rtc {
+ struct device *dev;
+ struct rtc_device *rtc;
+ u8 *reg_map;
+ /*
+ * Cache the value for timer/alarm interrupts register; this is
+ * only changed by callers holding rtc ops lock (or resume).
+ */
+ unsigned char rtc_irq_bits;
+ bool wake_enabled;
+#ifdef CONFIG_PM_SLEEP
+ unsigned char irqstat;
+#endif
+ enum twl_class class;
+};
/*
* Supports 1 byte read from TWL RTC register.
*/
-static int twl_rtc_read_u8(u8 *data, u8 reg)
+static int twl_rtc_read_u8(struct twl_rtc *twl_rtc, u8 *data, u8 reg)
{
int ret;
- ret = twl_i2c_read_u8(TWL_MODULE_RTC, data, (rtc_reg_map[reg]));
+ ret = twl_i2c_read_u8(TWL_MODULE_RTC, data, (twl_rtc->reg_map[reg]));
if (ret < 0)
pr_err("Could not read TWL register %X - error %d\n", reg, ret);
return ret;
@@ -154,11 +172,11 @@ static int twl_rtc_read_u8(u8 *data, u8 reg)
/*
* Supports 1 byte write to TWL RTC registers.
*/
-static int twl_rtc_write_u8(u8 data, u8 reg)
+static int twl_rtc_write_u8(struct twl_rtc *twl_rtc, u8 data, u8 reg)
{
int ret;
- ret = twl_i2c_write_u8(TWL_MODULE_RTC, data, (rtc_reg_map[reg]));
+ ret = twl_i2c_write_u8(TWL_MODULE_RTC, data, (twl_rtc->reg_map[reg]));
if (ret < 0)
pr_err("Could not write TWL register %X - error %d\n",
reg, ret);
@@ -166,28 +184,22 @@ static int twl_rtc_write_u8(u8 data, u8 reg)
}
/*
- * Cache the value for timer/alarm interrupts register; this is
- * only changed by callers holding rtc ops lock (or resume).
- */
-static unsigned char rtc_irq_bits;
-
-/*
* Enable 1/second update and/or alarm interrupts.
*/
-static int set_rtc_irq_bit(unsigned char bit)
+static int set_rtc_irq_bit(struct twl_rtc *twl_rtc, unsigned char bit)
{
unsigned char val;
int ret;
/* if the bit is set, return from here */
- if (rtc_irq_bits & bit)
+ if (twl_rtc->rtc_irq_bits & bit)
return 0;
- val = rtc_irq_bits | bit;
+ val = twl_rtc->rtc_irq_bits | bit;
val &= ~BIT_RTC_INTERRUPTS_REG_EVERY_M;
- ret = twl_rtc_write_u8(val, REG_RTC_INTERRUPTS_REG);
+ ret = twl_rtc_write_u8(twl_rtc, val, REG_RTC_INTERRUPTS_REG);
if (ret == 0)
- rtc_irq_bits = val;
+ twl_rtc->rtc_irq_bits = val;
return ret;
}
@@ -195,19 +207,19 @@ static int set_rtc_irq_bit(unsigned char bit)
/*
* Disable update and/or alarm interrupts.
*/
-static int mask_rtc_irq_bit(unsigned char bit)
+static int mask_rtc_irq_bit(struct twl_rtc *twl_rtc, unsigned char bit)
{
unsigned char val;
int ret;
/* if the bit is clear, return from here */
- if (!(rtc_irq_bits & bit))
+ if (!(twl_rtc->rtc_irq_bits & bit))
return 0;
- val = rtc_irq_bits & ~bit;
- ret = twl_rtc_write_u8(val, REG_RTC_INTERRUPTS_REG);
+ val = twl_rtc->rtc_irq_bits & ~bit;
+ ret = twl_rtc_write_u8(twl_rtc, val, REG_RTC_INTERRUPTS_REG);
if (ret == 0)
- rtc_irq_bits = val;
+ twl_rtc->rtc_irq_bits = val;
return ret;
}
@@ -215,21 +227,23 @@ static int mask_rtc_irq_bit(unsigned char bit)
static int twl_rtc_alarm_irq_enable(struct device *dev, unsigned enabled)
{
struct platform_device *pdev = to_platform_device(dev);
+ struct twl_rtc *twl_rtc = dev_get_drvdata(dev);
int irq = platform_get_irq(pdev, 0);
- static bool twl_rtc_wake_enabled;
int ret;
if (enabled) {
- ret = set_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
- if (device_can_wakeup(dev) && !twl_rtc_wake_enabled) {
+ ret = set_rtc_irq_bit(twl_rtc,
+ BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
+ if (device_can_wakeup(dev) && !twl_rtc->wake_enabled) {
enable_irq_wake(irq);
- twl_rtc_wake_enabled = true;
+ twl_rtc->wake_enabled = true;
}
} else {
- ret = mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
- if (twl_rtc_wake_enabled) {
+ ret = mask_rtc_irq_bit(twl_rtc,
+ BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
+ if (twl_rtc->wake_enabled) {
disable_irq_wake(irq);
- twl_rtc_wake_enabled = false;
+ twl_rtc->wake_enabled = false;
}
}
@@ -247,21 +261,23 @@ static int twl_rtc_alarm_irq_enable(struct device *dev, unsigned enabled)
*/
static int twl_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
+ struct twl_rtc *twl_rtc = dev_get_drvdata(dev);
unsigned char rtc_data[ALL_TIME_REGS];
int ret;
u8 save_control;
u8 rtc_control;
- ret = twl_rtc_read_u8(&save_control, REG_RTC_CTRL_REG);
+ ret = twl_rtc_read_u8(twl_rtc, &save_control, REG_RTC_CTRL_REG);
if (ret < 0) {
dev_err(dev, "%s: reading CTRL_REG, error %d\n", __func__, ret);
return ret;
}
/* for twl6030/32 make sure BIT_RTC_CTRL_REG_GET_TIME_M is clear */
- if (twl_class_is_6030()) {
+ if (twl_rtc->class == TWL_6030) {
if (save_control & BIT_RTC_CTRL_REG_GET_TIME_M) {
save_control &= ~BIT_RTC_CTRL_REG_GET_TIME_M;
- ret = twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
+ ret = twl_rtc_write_u8(twl_rtc, save_control,
+ REG_RTC_CTRL_REG);
if (ret < 0) {
dev_err(dev, "%s clr GET_TIME, error %d\n",
__func__, ret);
@@ -274,17 +290,17 @@ static int twl_rtc_read_time(struct device *dev, struct rtc_time *tm)
rtc_control = save_control | BIT_RTC_CTRL_REG_GET_TIME_M;
/* for twl6030/32 enable read access to static shadowed registers */
- if (twl_class_is_6030())
+ if (twl_rtc->class == TWL_6030)
rtc_control |= BIT_RTC_CTRL_REG_RTC_V_OPT;
- ret = twl_rtc_write_u8(rtc_control, REG_RTC_CTRL_REG);
+ ret = twl_rtc_write_u8(twl_rtc, rtc_control, REG_RTC_CTRL_REG);
if (ret < 0) {
dev_err(dev, "%s: writing CTRL_REG, error %d\n", __func__, ret);
return ret;
}
ret = twl_i2c_read(TWL_MODULE_RTC, rtc_data,
- (rtc_reg_map[REG_SECONDS_REG]), ALL_TIME_REGS);
+ (twl_rtc->reg_map[REG_SECONDS_REG]), ALL_TIME_REGS);
if (ret < 0) {
dev_err(dev, "%s: reading data, error %d\n", __func__, ret);
@@ -292,8 +308,8 @@ static int twl_rtc_read_time(struct device *dev, struct rtc_time *tm)
}
/* for twl6030 restore original state of rtc control register */
- if (twl_class_is_6030()) {
- ret = twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
+ if (twl_rtc->class == TWL_6030) {
+ ret = twl_rtc_write_u8(twl_rtc, save_control, REG_RTC_CTRL_REG);
if (ret < 0) {
dev_err(dev, "%s: restore CTRL_REG, error %d\n",
__func__, ret);
@@ -313,6 +329,7 @@ static int twl_rtc_read_time(struct device *dev, struct rtc_time *tm)
static int twl_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
+ struct twl_rtc *twl_rtc = dev_get_drvdata(dev);
unsigned char save_control;
unsigned char rtc_data[ALL_TIME_REGS];
int ret;
@@ -325,18 +342,18 @@ static int twl_rtc_set_time(struct device *dev, struct rtc_time *tm)
rtc_data[5] = bin2bcd(tm->tm_year - 100);
/* Stop RTC while updating the TC registers */
- ret = twl_rtc_read_u8(&save_control, REG_RTC_CTRL_REG);
+ ret = twl_rtc_read_u8(twl_rtc, &save_control, REG_RTC_CTRL_REG);
if (ret < 0)
goto out;
save_control &= ~BIT_RTC_CTRL_REG_STOP_RTC_M;
- ret = twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
+ ret = twl_rtc_write_u8(twl_rtc, save_control, REG_RTC_CTRL_REG);
if (ret < 0)
goto out;
/* update all the time registers in one shot */
ret = twl_i2c_write(TWL_MODULE_RTC, rtc_data,
- (rtc_reg_map[REG_SECONDS_REG]), ALL_TIME_REGS);
+ (twl_rtc->reg_map[REG_SECONDS_REG]), ALL_TIME_REGS);
if (ret < 0) {
dev_err(dev, "rtc_set_time error %d\n", ret);
goto out;
@@ -344,7 +361,7 @@ static int twl_rtc_set_time(struct device *dev, struct rtc_time *tm)
/* Start back RTC */
save_control |= BIT_RTC_CTRL_REG_STOP_RTC_M;
- ret = twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
+ ret = twl_rtc_write_u8(twl_rtc, save_control, REG_RTC_CTRL_REG);
out:
return ret;
@@ -355,11 +372,12 @@ out:
*/
static int twl_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
{
+ struct twl_rtc *twl_rtc = dev_get_drvdata(dev);
unsigned char rtc_data[ALL_TIME_REGS];
int ret;
ret = twl_i2c_read(TWL_MODULE_RTC, rtc_data,
- (rtc_reg_map[REG_ALARM_SECONDS_REG]), ALL_TIME_REGS);
+ twl_rtc->reg_map[REG_ALARM_SECONDS_REG], ALL_TIME_REGS);
if (ret < 0) {
dev_err(dev, "rtc_read_alarm error %d\n", ret);
return ret;
@@ -374,7 +392,7 @@ static int twl_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
alm->time.tm_year = bcd2bin(rtc_data[5]) + 100;
/* report cached alarm enable state */
- if (rtc_irq_bits & BIT_RTC_INTERRUPTS_REG_IT_ALARM_M)
+ if (twl_rtc->rtc_irq_bits & BIT_RTC_INTERRUPTS_REG_IT_ALARM_M)
alm->enabled = 1;
return ret;
@@ -382,6 +400,8 @@ static int twl_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
static int twl_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
{
+ struct twl_rtc *twl_rtc = dev_get_drvdata(dev);
+
unsigned char alarm_data[ALL_TIME_REGS];
int ret;
@@ -398,7 +418,7 @@ static int twl_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
/* update all the alarm registers in one shot */
ret = twl_i2c_write(TWL_MODULE_RTC, alarm_data,
- (rtc_reg_map[REG_ALARM_SECONDS_REG]), ALL_TIME_REGS);
+ twl_rtc->reg_map[REG_ALARM_SECONDS_REG], ALL_TIME_REGS);
if (ret) {
dev_err(dev, "rtc_set_alarm error %d\n", ret);
goto out;
@@ -410,14 +430,15 @@ out:
return ret;
}
-static irqreturn_t twl_rtc_interrupt(int irq, void *rtc)
+static irqreturn_t twl_rtc_interrupt(int irq, void *data)
{
+ struct twl_rtc *twl_rtc = data;
unsigned long events;
int ret = IRQ_NONE;
int res;
u8 rd_reg;
- res = twl_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG);
+ res = twl_rtc_read_u8(twl_rtc, &rd_reg, REG_RTC_STATUS_REG);
if (res)
goto out;
/*
@@ -431,12 +452,12 @@ static irqreturn_t twl_rtc_interrupt(int irq, void *rtc)
else
events = RTC_IRQF | RTC_PF;
- res = twl_rtc_write_u8(BIT_RTC_STATUS_REG_ALARM_M,
- REG_RTC_STATUS_REG);
+ res = twl_rtc_write_u8(twl_rtc, BIT_RTC_STATUS_REG_ALARM_M,
+ REG_RTC_STATUS_REG);
if (res)
goto out;
- if (twl_class_is_4030()) {
+ if (twl_rtc->class == TWL_4030) {
/* Clear on Read enabled. RTC_IT bit of TWL4030_INT_PWR_ISR1
* needs 2 reads to clear the interrupt. One read is done in
* do_twl_pwrirq(). Doing the second read, to clear
@@ -455,7 +476,7 @@ static irqreturn_t twl_rtc_interrupt(int irq, void *rtc)
}
/* Notify RTC core on event */
- rtc_update_irq(rtc, 1, events);
+ rtc_update_irq(twl_rtc->rtc, 1, events);
ret = IRQ_HANDLED;
out:
@@ -474,21 +495,36 @@ static const struct rtc_class_ops twl_rtc_ops = {
static int twl_rtc_probe(struct platform_device *pdev)
{
- struct rtc_device *rtc;
+ struct twl_rtc *twl_rtc;
+ struct device_node *np = pdev->dev.of_node;
int ret = -EINVAL;
int irq = platform_get_irq(pdev, 0);
u8 rd_reg;
+ if (!np) {
+ dev_err(&pdev->dev, "no DT info\n");
+ return -EINVAL;
+ }
+
if (irq <= 0)
return ret;
- /* Initialize the register map */
- if (twl_class_is_4030())
- rtc_reg_map = (u8 *)twl4030_rtc_reg_map;
- else
- rtc_reg_map = (u8 *)twl6030_rtc_reg_map;
+ twl_rtc = devm_kzalloc(&pdev->dev, sizeof(*twl_rtc), GFP_KERNEL);
+ if (!twl_rtc)
+ return -ENOMEM;
- ret = twl_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG);
+ if (twl_class_is_4030()) {
+ twl_rtc->class = TWL_4030;
+ twl_rtc->reg_map = (u8 *)twl4030_rtc_reg_map;
+ } else if (twl_class_is_6030()) {
+ twl_rtc->class = TWL_6030;
+ twl_rtc->reg_map = (u8 *)twl6030_rtc_reg_map;
+ } else {
+ dev_err(&pdev->dev, "TWL Class not supported.\n");
+ return -EINVAL;
+ }
+
+ ret = twl_rtc_read_u8(twl_rtc, &rd_reg, REG_RTC_STATUS_REG);
if (ret < 0)
return ret;
@@ -499,11 +535,11 @@ static int twl_rtc_probe(struct platform_device *pdev)
dev_warn(&pdev->dev, "Pending Alarm interrupt detected.\n");
/* Clear RTC Power up reset and pending alarm interrupts */
- ret = twl_rtc_write_u8(rd_reg, REG_RTC_STATUS_REG);
+ ret = twl_rtc_write_u8(twl_rtc, rd_reg, REG_RTC_STATUS_REG);
if (ret < 0)
return ret;
- if (twl_class_is_6030()) {
+ if (twl_rtc->class == TWL_6030) {
twl6030_interrupt_unmask(TWL6030_RTC_INT_MASK,
REG_INT_MSK_LINE_A);
twl6030_interrupt_unmask(TWL6030_RTC_INT_MASK,
@@ -511,40 +547,42 @@ static int twl_rtc_probe(struct platform_device *pdev)
}
dev_info(&pdev->dev, "Enabling TWL-RTC\n");
- ret = twl_rtc_write_u8(BIT_RTC_CTRL_REG_STOP_RTC_M, REG_RTC_CTRL_REG);
+ ret = twl_rtc_write_u8(twl_rtc, BIT_RTC_CTRL_REG_STOP_RTC_M,
+ REG_RTC_CTRL_REG);
if (ret < 0)
return ret;
/* ensure interrupts are disabled, bootloaders can be strange */
- ret = twl_rtc_write_u8(0, REG_RTC_INTERRUPTS_REG);
+ ret = twl_rtc_write_u8(twl_rtc, 0, REG_RTC_INTERRUPTS_REG);
if (ret < 0)
dev_warn(&pdev->dev, "unable to disable interrupt\n");
/* init cached IRQ enable bits */
- ret = twl_rtc_read_u8(&rtc_irq_bits, REG_RTC_INTERRUPTS_REG);
+ ret = twl_rtc_read_u8(twl_rtc, &twl_rtc->rtc_irq_bits,
+ REG_RTC_INTERRUPTS_REG);
if (ret < 0)
return ret;
+ platform_set_drvdata(pdev, twl_rtc);
device_init_wakeup(&pdev->dev, 1);
- rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ twl_rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
&twl_rtc_ops, THIS_MODULE);
- if (IS_ERR(rtc)) {
+ if (IS_ERR(twl_rtc->rtc)) {
dev_err(&pdev->dev, "can't register RTC device, err %ld\n",
- PTR_ERR(rtc));
- return PTR_ERR(rtc);
+ PTR_ERR(twl_rtc->rtc));
+ return PTR_ERR(twl_rtc->rtc);
}
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
twl_rtc_interrupt,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
- dev_name(&rtc->dev), rtc);
+ dev_name(&twl_rtc->rtc->dev), twl_rtc);
if (ret < 0) {
dev_err(&pdev->dev, "IRQ is not free.\n");
return ret;
}
- platform_set_drvdata(pdev, rtc);
return 0;
}
@@ -554,10 +592,12 @@ static int twl_rtc_probe(struct platform_device *pdev)
*/
static int twl_rtc_remove(struct platform_device *pdev)
{
+ struct twl_rtc *twl_rtc = platform_get_drvdata(pdev);
+
/* leave rtc running, but disable irqs */
- mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
- mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
- if (twl_class_is_6030()) {
+ mask_rtc_irq_bit(twl_rtc, BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
+ mask_rtc_irq_bit(twl_rtc, BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
+ if (twl_rtc->class == TWL_6030) {
twl6030_interrupt_mask(TWL6030_RTC_INT_MASK,
REG_INT_MSK_LINE_A);
twl6030_interrupt_mask(TWL6030_RTC_INT_MASK,
@@ -569,40 +609,40 @@ static int twl_rtc_remove(struct platform_device *pdev)
static void twl_rtc_shutdown(struct platform_device *pdev)
{
+ struct twl_rtc *twl_rtc = platform_get_drvdata(pdev);
+
/* mask timer interrupts, but leave alarm interrupts on to enable
power-on when alarm is triggered */
- mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
+ mask_rtc_irq_bit(twl_rtc, BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
}
#ifdef CONFIG_PM_SLEEP
-static unsigned char irqstat;
-
static int twl_rtc_suspend(struct device *dev)
{
- irqstat = rtc_irq_bits;
+ struct twl_rtc *twl_rtc = dev_get_drvdata(dev);
+
+ twl_rtc->irqstat = twl_rtc->rtc_irq_bits;
- mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
+ mask_rtc_irq_bit(twl_rtc, BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
return 0;
}
static int twl_rtc_resume(struct device *dev)
{
- set_rtc_irq_bit(irqstat);
+ struct twl_rtc *twl_rtc = dev_get_drvdata(dev);
+
+ set_rtc_irq_bit(twl_rtc, twl_rtc->irqstat);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(twl_rtc_pm_ops, twl_rtc_suspend, twl_rtc_resume);
-#ifdef CONFIG_OF
static const struct of_device_id twl_rtc_of_match[] = {
{.compatible = "ti,twl4030-rtc", },
{ },
};
MODULE_DEVICE_TABLE(of, twl_rtc_of_match);
-#endif
-
-MODULE_ALIAS("platform:twl_rtc");
static struct platform_driver twl4030rtc_driver = {
.probe = twl_rtc_probe,
@@ -611,7 +651,7 @@ static struct platform_driver twl4030rtc_driver = {
.driver = {
.name = "twl_rtc",
.pm = &twl_rtc_pm_ops,
- .of_match_table = of_match_ptr(twl_rtc_of_match),
+ .of_match_table = twl_rtc_of_match,
},
};