RTC for 4.10
Subsystem: - non-modular drivers are now explicitly non-modular New driver: - Epson Toyocom rtc-7301sf/dg Drivers: - cmos: reject unsupported alarm values wrt the RTC capabilities - ds1307: ACPI support - jz4740: DT support, jz4780 handling, can now be used as a system power controller - mcp795: many fixes, in particular proper month handling - twl: driver is now DT only -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEl0I5XWmUIrwBfFMm2KKDO9oT4sIFAlhXIxkACgkQ2KKDO9oT 4sKJIQ/+MxwjMs0CZ8744orSTkX5AJTOwGcwg+SEmp23Ht0nV0SCrAkkndC3HamM 9MwT0qVmL2rgiqyeSRAjdVVIt+UFJeGXMuBhc5UBqSomjXIqfN9nA0DXuddKx/at ZwWtPIN4HyWS5Uetn/FTXC9scBa5+2bJEYdB3ocC/QNgcCErINzPBJZEsduaxajK AUIOhHPWn9D2cDzIxPMplPyVSWXUI3WXiF2mvgi/VAB21StQoKY6KkJV+u6Q+56t IdJeKaAP+bF535T66wl/yY1KNhkRwF6M0qFs+qR5htoxzS6zx6hW+aRibvrIAP3/ YiAQj2L7hOjW1ky0H1rEUpjTYFxWmOx2AWZJ3ubxzveF6pz0Qn1TTrzOHVkelaHB iuuYrxXMmC84qmHrxIdrkZdH2eu2Fm12/D1VME6bjdD4BApkEHjKebGVS4F9XaMi Pdbb4olEslZL+XEZXkuqmopl7g1/Wf34IrCskNDoUx7t+JsCjrA+hXMVeqwl3e8m Edcv103l1Wkivv9kHZEgx8IwOeti5d77z+QUvQzHYKK28o8zQii/3zlvQzJ/6gnE M20vRv7cptVL4GmZd4ebFB2GOUteSfnOJJAwKZ3ipbZaGtNSs1nhAqTpg9uw4OEr rPlRJJw5Cov1ctV+dBuVhLmzStBg3PJj2fkZ4qjdYgeiu2wZAV0= =z4SX -----END PGP SIGNATURE----- Merge tag 'rtc-4.10' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux Pull RTC updates from Alexandre Belloni: "Subsystem: - non-modular drivers are now explicitly non-modular New driver: - Epson Toyocom rtc-7301sf/dg Drivers: - cmos: reject unsupported alarm values wrt the RTC capabilities - ds1307: ACPI support - jz4740: DT support, jz4780 handling, can now be used as a system power controller - mcp795: many fixes, in particular proper month handling - twl: driver is now DT only" * tag 'rtc-4.10' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux: (31 commits) rtc: mcp795: Fix whitespace and indentation. rtc: mcp795: Prefer using the BIT() macro. rtc: mcp795: fix month write resetting date to 1. rtc: mcp795: fix time range difference between linux and RTC chip. rtc: mcp795: fix bitmask value for leap year (LP). rtc: mcp795: use bcd2bin/bin2bcd. rtc: add support for EPSON TOYOCOM RTC-7301SF/DG rtc: ds1307: Add ACPI support rtc: imxdi: (trivial) fix a typo rtc: ds1374: Merge conditional + WARN_ON() rtc: twl: make driver DT only rtc: twl: kill static variables rtc: fix typos in Kconfig rtc: jz4740: make the driver builtin only rtc: jz4740: remove unused EXPORT_SYMBOL Documentation: bindings: fix twl-rtc documentation rtc: Enable compile testing for Maxim and Samsung drivers MIPS: jz4740: Remove obsolete code MIPS: qi_lb60: Probe RTC driver from DT and use it as power controller MIPS: jz4740: DTS: Probe the jz4740-rtc driver from devicetree ...
This commit is contained in:
commit
b0b3a37b90
@ -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
|
Date: Oct 2011
|
||||||
KernelVersion: 3.0
|
KernelVersion: 3.0
|
||||||
Contact: Mark Godfrey <mark.godfrey@stericsson.com>
|
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.
|
calibrate the AB8500.s 32KHz Real Time Clock.
|
||||||
Every 60 seconds the AB8500 will correct the RTC's value
|
Every 60 seconds the AB8500 will correct the RTC's value
|
||||||
by adding to it the value of this attribute.
|
by adding to it the value of this attribute.
|
||||||
|
16
Documentation/devicetree/bindings/rtc/epson,rtc7301.txt
Normal file
16
Documentation/devicetree/bindings/rtc/epson,rtc7301.txt
Normal file
@ -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>;
|
||||||
|
};
|
37
Documentation/devicetree/bindings/rtc/ingenic,jz4740-rtc.txt
Normal file
37
Documentation/devicetree/bindings/rtc/ingenic,jz4740-rtc.txt
Normal file
@ -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>;
|
||||||
|
};
|
@ -1,12 +1,11 @@
|
|||||||
* TI twl RTC
|
* Texas Instruments TWL4030/6030 RTC
|
||||||
|
|
||||||
The TWL family (twl4030/6030) contains a RTC.
|
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible : Should be twl4030-rtc
|
- compatible : Should be "ti,twl4030-rtc"
|
||||||
|
- interrupts : Should be the interrupt number.
|
||||||
|
|
||||||
Examples:
|
Example:
|
||||||
|
rtc {
|
||||||
rtc@0 {
|
compatible = "ti,twl4030-rtc";
|
||||||
compatible = "ti,twl4030-rtc";
|
interrupts = <11>;
|
||||||
};
|
};
|
||||||
|
@ -44,6 +44,17 @@
|
|||||||
#clock-cells = <1>;
|
#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 {
|
uart0: serial@10030000 {
|
||||||
compatible = "ingenic,jz4740-uart";
|
compatible = "ingenic,jz4740-uart";
|
||||||
reg = <0x10030000 0x100>;
|
reg = <0x10030000 0x100>;
|
||||||
|
@ -13,3 +13,7 @@
|
|||||||
&ext {
|
&ext {
|
||||||
clock-frequency = <12000000>;
|
clock-frequency = <12000000>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
&rtc_dev {
|
||||||
|
system-power-controller;
|
||||||
|
};
|
||||||
|
@ -22,7 +22,6 @@
|
|||||||
extern struct platform_device jz4740_udc_device;
|
extern struct platform_device jz4740_udc_device;
|
||||||
extern struct platform_device jz4740_udc_xceiv_device;
|
extern struct platform_device jz4740_udc_xceiv_device;
|
||||||
extern struct platform_device jz4740_mmc_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_i2c_device;
|
||||||
extern struct platform_device jz4740_nand_device;
|
extern struct platform_device jz4740_nand_device;
|
||||||
extern struct platform_device jz4740_framebuffer_device;
|
extern struct platform_device jz4740_framebuffer_device;
|
||||||
|
@ -438,7 +438,6 @@ static struct platform_device *jz_platform_devices[] __initdata = {
|
|||||||
&jz4740_pcm_device,
|
&jz4740_pcm_device,
|
||||||
&jz4740_i2s_device,
|
&jz4740_i2s_device,
|
||||||
&jz4740_codec_device,
|
&jz4740_codec_device,
|
||||||
&jz4740_rtc_device,
|
|
||||||
&jz4740_adc_device,
|
&jz4740_adc_device,
|
||||||
&jz4740_pwm_device,
|
&jz4740_pwm_device,
|
||||||
&jz4740_dma_device,
|
&jz4740_dma_device,
|
||||||
|
@ -88,27 +88,6 @@ struct platform_device jz4740_mmc_device = {
|
|||||||
.resource = jz4740_mmc_resources,
|
.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 */
|
/* I2C controller */
|
||||||
static struct resource jz4740_i2c_resources[] = {
|
static struct resource jz4740_i2c_resources[] = {
|
||||||
{
|
{
|
||||||
|
@ -57,71 +57,8 @@ static void jz4740_restart(char *command)
|
|||||||
jz4740_halt();
|
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)
|
void jz4740_reset_init(void)
|
||||||
{
|
{
|
||||||
_machine_restart = jz4740_restart;
|
_machine_restart = jz4740_restart;
|
||||||
_machine_halt = jz4740_halt;
|
_machine_halt = jz4740_halt;
|
||||||
pm_power_off = jz4740_power_off;
|
|
||||||
}
|
}
|
||||||
|
@ -303,7 +303,7 @@ config RTC_DRV_MAX6900
|
|||||||
|
|
||||||
config RTC_DRV_MAX8907
|
config RTC_DRV_MAX8907
|
||||||
tristate "Maxim MAX8907"
|
tristate "Maxim MAX8907"
|
||||||
depends on MFD_MAX8907
|
depends on MFD_MAX8907 || COMPILE_TEST
|
||||||
help
|
help
|
||||||
If you say yes here you will get support for the
|
If you say yes here you will get support for the
|
||||||
RTC of Maxim MAX8907 PMIC.
|
RTC of Maxim MAX8907 PMIC.
|
||||||
@ -343,7 +343,7 @@ config RTC_DRV_MAX8997
|
|||||||
|
|
||||||
config RTC_DRV_MAX77686
|
config RTC_DRV_MAX77686
|
||||||
tristate "Maxim MAX77686"
|
tristate "Maxim MAX77686"
|
||||||
depends on MFD_MAX77686 || MFD_MAX77620
|
depends on MFD_MAX77686 || MFD_MAX77620 || COMPILE_TEST
|
||||||
help
|
help
|
||||||
If you say yes here you will get support for the
|
If you say yes here you will get support for the
|
||||||
RTC of Maxim MAX77686/MAX77620/MAX77802 PMIC.
|
RTC of Maxim MAX77686/MAX77620/MAX77802 PMIC.
|
||||||
@ -481,6 +481,7 @@ config RTC_DRV_TWL92330
|
|||||||
config RTC_DRV_TWL4030
|
config RTC_DRV_TWL4030
|
||||||
tristate "TI TWL4030/TWL5030/TWL6030/TPS659x0"
|
tristate "TI TWL4030/TWL5030/TWL6030/TPS659x0"
|
||||||
depends on TWL4030_CORE
|
depends on TWL4030_CORE
|
||||||
|
depends on OF
|
||||||
help
|
help
|
||||||
If you say yes here you get support for the RTC on the
|
If you say yes here you get support for the RTC on the
|
||||||
TWL4030/TWL5030/TWL6030 family chips, used mostly with OMAP3 platforms.
|
TWL4030/TWL5030/TWL6030 family chips, used mostly with OMAP3 platforms.
|
||||||
@ -602,7 +603,8 @@ config RTC_DRV_RV8803
|
|||||||
|
|
||||||
config RTC_DRV_S5M
|
config RTC_DRV_S5M
|
||||||
tristate "Samsung S2M/S5M series"
|
tristate "Samsung S2M/S5M series"
|
||||||
depends on MFD_SEC_CORE
|
depends on MFD_SEC_CORE || COMPILE_TEST
|
||||||
|
select REGMAP_IRQ
|
||||||
help
|
help
|
||||||
If you say yes here you will get support for the
|
If you say yes here you will get support for the
|
||||||
RTC of Samsung S2MPS14 and S5M PMIC series.
|
RTC of Samsung S2MPS14 and S5M PMIC series.
|
||||||
@ -820,8 +822,8 @@ config RTC_DRV_RV3029_HWMON
|
|||||||
|
|
||||||
comment "Platform RTC drivers"
|
comment "Platform RTC drivers"
|
||||||
|
|
||||||
# this 'CMOS' RTC driver is arch dependent because <asm-generic/rtc.h>
|
# this 'CMOS' RTC driver is arch dependent because it requires
|
||||||
# requires <asm/mc146818rtc.h> defining CMOS_READ/CMOS_WRITE, and a
|
# <asm/mc146818rtc.h> defining CMOS_READ/CMOS_WRITE, and a
|
||||||
# global rtc_lock ... it's not yet just another platform_device.
|
# global rtc_lock ... it's not yet just another platform_device.
|
||||||
|
|
||||||
config RTC_DRV_CMOS
|
config RTC_DRV_CMOS
|
||||||
@ -1549,14 +1551,11 @@ config RTC_DRV_MPC5121
|
|||||||
will be called rtc-mpc5121.
|
will be called rtc-mpc5121.
|
||||||
|
|
||||||
config RTC_DRV_JZ4740
|
config RTC_DRV_JZ4740
|
||||||
tristate "Ingenic JZ4740 SoC"
|
bool "Ingenic JZ4740 SoC"
|
||||||
depends on MACH_JZ4740 || COMPILE_TEST
|
depends on MACH_INGENIC || COMPILE_TEST
|
||||||
help
|
help
|
||||||
If you say yes here you get support for the Ingenic JZ4740 SoC RTC
|
If you say yes here you get support for the Ingenic JZ47xx SoCs RTC
|
||||||
controller.
|
controllers.
|
||||||
|
|
||||||
This driver can also be buillt as a module. If so, the module
|
|
||||||
will be called rtc-jz4740.
|
|
||||||
|
|
||||||
config RTC_DRV_LPC24XX
|
config RTC_DRV_LPC24XX
|
||||||
tristate "NXP RTC for LPC178x/18xx/408x/43xx"
|
tristate "NXP RTC for LPC178x/18xx/408x/43xx"
|
||||||
@ -1567,7 +1566,7 @@ config RTC_DRV_LPC24XX
|
|||||||
NXP LPC178x/18xx/408x/43xx devices.
|
NXP LPC178x/18xx/408x/43xx devices.
|
||||||
|
|
||||||
If you have one of the devices above enable this driver to use
|
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.
|
so, the module will be called rtc-lpc24xx.
|
||||||
|
|
||||||
config RTC_DRV_LPC32XX
|
config RTC_DRV_LPC32XX
|
||||||
@ -1576,7 +1575,7 @@ config RTC_DRV_LPC32XX
|
|||||||
help
|
help
|
||||||
This enables support for the NXP RTC in the LPC32XX
|
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.
|
will be called rtc-lpc32xx.
|
||||||
|
|
||||||
config RTC_DRV_PM8XXX
|
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
|
This driver can also be built as a module. If so, the module
|
||||||
will be called rtc-pic32
|
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"
|
comment "HID Sensor RTC drivers"
|
||||||
|
|
||||||
config RTC_DRV_HID_SENSOR_TIME
|
config RTC_DRV_HID_SENSOR_TIME
|
||||||
|
@ -120,6 +120,7 @@ obj-$(CONFIG_RTC_DRV_PM8XXX) += rtc-pm8xxx.o
|
|||||||
obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o
|
obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o
|
||||||
obj-$(CONFIG_RTC_DRV_PUV3) += rtc-puv3.o
|
obj-$(CONFIG_RTC_DRV_PUV3) += rtc-puv3.o
|
||||||
obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.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_R9701) += rtc-r9701.o
|
||||||
obj-$(CONFIG_RTC_DRV_RC5T583) += rtc-rc5t583.o
|
obj-$(CONFIG_RTC_DRV_RC5T583) += rtc-rc5t583.o
|
||||||
obj-$(CONFIG_RTC_DRV_RK808) += rtc-rk808.o
|
obj-$(CONFIG_RTC_DRV_RK808) += rtc-rk808.o
|
||||||
|
@ -332,14 +332,86 @@ static void cmos_irq_disable(struct cmos_rtc *cmos, unsigned char mask)
|
|||||||
cmos_checkintr(cmos, rtc_control);
|
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)
|
static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
|
||||||
{
|
{
|
||||||
struct cmos_rtc *cmos = dev_get_drvdata(dev);
|
struct cmos_rtc *cmos = dev_get_drvdata(dev);
|
||||||
unsigned char mon, mday, hrs, min, sec, rtc_control;
|
unsigned char mon, mday, hrs, min, sec, rtc_control;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (!is_valid_irq(cmos->irq))
|
if (!is_valid_irq(cmos->irq))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
|
ret = cmos_validate_alarm(dev, t);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
mon = t->time.tm_mon + 1;
|
mon = t->time.tm_mon + 1;
|
||||||
mday = t->time.tm_mday;
|
mday = t->time.tm_mday;
|
||||||
hrs = t->time.tm_hour;
|
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);
|
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)) {
|
if (is_valid_irq(rtc_irq) && !(rtc_control & RTC_24H)) {
|
||||||
dev_warn(dev, "only 24-hr supported\n");
|
dev_warn(dev, "only 24-hr supported\n");
|
||||||
retval = -ENXIO;
|
retval = -ENXIO;
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
* published by the Free Software Foundation.
|
* published by the Free Software Foundation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/acpi.h>
|
||||||
#include <linux/bcd.h>
|
#include <linux/bcd.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
@ -191,6 +192,26 @@ static const struct i2c_device_id ds1307_id[] = {
|
|||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(i2c, 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
|
#define BLOCK_DATA_MAX_TRIES 10
|
||||||
@ -874,17 +895,17 @@ static u8 do_trickle_setup_ds1339(struct i2c_client *client,
|
|||||||
return setup;
|
return setup;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ds1307_trickle_of_init(struct i2c_client *client,
|
static void ds1307_trickle_init(struct i2c_client *client,
|
||||||
struct chip_desc *chip)
|
struct chip_desc *chip)
|
||||||
{
|
{
|
||||||
uint32_t ohms = 0;
|
uint32_t ohms = 0;
|
||||||
bool diode = true;
|
bool diode = true;
|
||||||
|
|
||||||
if (!chip->do_trickle_setup)
|
if (!chip->do_trickle_setup)
|
||||||
goto out;
|
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;
|
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;
|
diode = false;
|
||||||
chip->trickle_charger_setup = chip->do_trickle_setup(client,
|
chip->trickle_charger_setup = chip->do_trickle_setup(client,
|
||||||
ohms, diode);
|
ohms, diode);
|
||||||
@ -1268,7 +1289,7 @@ static int ds1307_probe(struct i2c_client *client,
|
|||||||
struct ds1307 *ds1307;
|
struct ds1307 *ds1307;
|
||||||
int err = -ENODEV;
|
int err = -ENODEV;
|
||||||
int tmp, wday;
|
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);
|
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||||
bool want_irq = false;
|
bool want_irq = false;
|
||||||
bool ds1307_can_wakeup_device = false;
|
bool ds1307_can_wakeup_device = false;
|
||||||
@ -1297,11 +1318,23 @@ static int ds1307_probe(struct i2c_client *client,
|
|||||||
i2c_set_clientdata(client, ds1307);
|
i2c_set_clientdata(client, ds1307);
|
||||||
|
|
||||||
ds1307->client = client;
|
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;
|
||||||
|
|
||||||
if (!pdata && client->dev.of_node)
|
acpi_id = acpi_match_device(ACPI_PTR(ds1307_acpi_ids),
|
||||||
ds1307_trickle_of_init(client, chip);
|
&client->dev);
|
||||||
else if (pdata && pdata->trickle_charger_setup)
|
if (!acpi_id)
|
||||||
|
return -ENODEV;
|
||||||
|
chip = &chips[acpi_id->driver_data];
|
||||||
|
ds1307->type = acpi_id->driver_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pdata)
|
||||||
|
ds1307_trickle_init(client, chip);
|
||||||
|
else if (pdata->trickle_charger_setup)
|
||||||
chip->trickle_charger_setup = pdata->trickle_charger_setup;
|
chip->trickle_charger_setup = pdata->trickle_charger_setup;
|
||||||
|
|
||||||
if (chip->trickle_charger_setup && chip->trickle_charger_reg) {
|
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 = {
|
static struct i2c_driver ds1307_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "rtc-ds1307",
|
.name = "rtc-ds1307",
|
||||||
|
.acpi_match_table = ACPI_PTR(ds1307_acpi_ids),
|
||||||
},
|
},
|
||||||
.probe = ds1307_probe,
|
.probe = ds1307_probe,
|
||||||
.remove = ds1307_remove,
|
.remove = ds1307_remove,
|
||||||
|
@ -89,10 +89,8 @@ static int ds1374_read_rtc(struct i2c_client *client, u32 *time,
|
|||||||
int ret;
|
int ret;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (nbytes > 4) {
|
if (WARN_ON(nbytes > 4))
|
||||||
WARN_ON(1);
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
|
||||||
|
|
||||||
ret = i2c_smbus_read_i2c_block_data(client, reg, nbytes, buf);
|
ret = i2c_smbus_read_i2c_block_data(client, reg, nbytes, buf);
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
#define DSR_ETAD (1 << 21) /* External tamper A detected */
|
#define DSR_ETAD (1 << 21) /* External tamper A detected */
|
||||||
#define DSR_EBD (1 << 20) /* External boot detected */
|
#define DSR_EBD (1 << 20) /* External boot detected */
|
||||||
#define DSR_SAD (1 << 19) /* SCC alarm 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_CTD (1 << 17) /* Clock tamper detected */
|
||||||
#define DSR_VTD (1 << 16) /* Voltage tamper detected */
|
#define DSR_VTD (1 << 16) /* Voltage tamper detected */
|
||||||
#define DSR_WBF (1 << 10) /* Write Busy Flag (synchronous) */
|
#define DSR_WBF (1 << 10) /* Write Busy Flag (synchronous) */
|
||||||
|
@ -14,10 +14,12 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/reboot.h>
|
||||||
#include <linux/rtc.h>
|
#include <linux/rtc.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
@ -27,8 +29,14 @@
|
|||||||
#define JZ_REG_RTC_SEC_ALARM 0x08
|
#define JZ_REG_RTC_SEC_ALARM 0x08
|
||||||
#define JZ_REG_RTC_REGULATOR 0x0C
|
#define JZ_REG_RTC_REGULATOR 0x0C
|
||||||
#define JZ_REG_RTC_HIBERNATE 0x20
|
#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
|
#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_WRDY BIT(7)
|
||||||
#define JZ_RTC_CTRL_1HZ BIT(6)
|
#define JZ_RTC_CTRL_1HZ BIT(6)
|
||||||
#define JZ_RTC_CTRL_1HZ_IRQ BIT(5)
|
#define JZ_RTC_CTRL_1HZ_IRQ BIT(5)
|
||||||
@ -37,16 +45,34 @@
|
|||||||
#define JZ_RTC_CTRL_AE BIT(2)
|
#define JZ_RTC_CTRL_AE BIT(2)
|
||||||
#define JZ_RTC_CTRL_ENABLE BIT(0)
|
#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 {
|
struct jz4740_rtc {
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
|
enum jz4740_rtc_type type;
|
||||||
|
|
||||||
struct rtc_device *rtc;
|
struct rtc_device *rtc;
|
||||||
|
struct clk *clk;
|
||||||
|
|
||||||
int irq;
|
int irq;
|
||||||
|
|
||||||
spinlock_t lock;
|
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)
|
static inline uint32_t jz4740_rtc_reg_read(struct jz4740_rtc *rtc, size_t reg)
|
||||||
{
|
{
|
||||||
return readl(rtc->base + 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;
|
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,
|
static inline int jz4740_rtc_reg_write(struct jz4740_rtc *rtc, size_t reg,
|
||||||
uint32_t val)
|
uint32_t val)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret = 0;
|
||||||
ret = jz4740_rtc_wait_write_ready(rtc);
|
|
||||||
|
if (rtc->type >= ID_JZ4780)
|
||||||
|
ret = jz4780_rtc_enable_write(rtc);
|
||||||
|
if (ret == 0)
|
||||||
|
ret = jz4740_rtc_wait_write_ready(rtc);
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
writel(val, rtc->base + reg);
|
writel(val, rtc->base + reg);
|
||||||
|
|
||||||
@ -203,12 +251,57 @@ static irqreturn_t jz4740_rtc_irq(int irq, void *data)
|
|||||||
return IRQ_HANDLED;
|
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);
|
struct jz4740_rtc *rtc = dev_get_drvdata(dev);
|
||||||
jz4740_rtc_reg_write(rtc, JZ_REG_RTC_HIBERNATE, 1);
|
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)
|
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;
|
struct jz4740_rtc *rtc;
|
||||||
uint32_t scratchpad;
|
uint32_t scratchpad;
|
||||||
struct resource *mem;
|
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);
|
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
|
||||||
if (!rtc)
|
if (!rtc)
|
||||||
return -ENOMEM;
|
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);
|
rtc->irq = platform_get_irq(pdev, 0);
|
||||||
if (rtc->irq < 0) {
|
if (rtc->irq < 0) {
|
||||||
dev_err(&pdev->dev, "Failed to get platform irq\n");
|
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))
|
if (IS_ERR(rtc->base))
|
||||||
return PTR_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);
|
spin_lock_init(&rtc->lock);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, rtc);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,17 +424,20 @@ static const struct dev_pm_ops jz4740_pm_ops = {
|
|||||||
#define JZ4740_RTC_PM_OPS NULL
|
#define JZ4740_RTC_PM_OPS NULL
|
||||||
#endif /* CONFIG_PM */
|
#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 = {
|
static struct platform_driver jz4740_rtc_driver = {
|
||||||
.probe = jz4740_rtc_probe,
|
.probe = jz4740_rtc_probe,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "jz4740-rtc",
|
.name = "jz4740-rtc",
|
||||||
.pm = JZ4740_RTC_PM_OPS,
|
.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);
|
builtin_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");
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
* published by the Free Software Foundation.
|
* published by the Free Software Foundation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/export.h>
|
||||||
#include <linux/rtc.h>
|
#include <linux/rtc.h>
|
||||||
|
|
||||||
static const unsigned char rtc_days_in_month[] = {
|
static const unsigned char rtc_days_in_month[] = {
|
||||||
@ -148,5 +148,3 @@ struct rtc_time rtc_ktime_to_tm(ktime_t kt)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(rtc_ktime_to_tm);
|
EXPORT_SYMBOL_GPL(rtc_ktime_to_tm);
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
* it under the terms of the GNU General Public License version 2 as
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
* published by the Free Software Foundation.
|
* published by the Free Software Foundation.
|
||||||
*
|
*
|
||||||
* */
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
@ -21,6 +21,8 @@
|
|||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/rtc.h>
|
#include <linux/rtc.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
#include <linux/bcd.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
|
||||||
/* MCP795 Instructions, see datasheet table 3-1 */
|
/* MCP795 Instructions, see datasheet table 3-1 */
|
||||||
#define MCP795_EEREAD 0x03
|
#define MCP795_EEREAD 0x03
|
||||||
@ -29,7 +31,7 @@
|
|||||||
#define MCP795_EEWREN 0x06
|
#define MCP795_EEWREN 0x06
|
||||||
#define MCP795_SRREAD 0x05
|
#define MCP795_SRREAD 0x05
|
||||||
#define MCP795_SRWRITE 0x01
|
#define MCP795_SRWRITE 0x01
|
||||||
#define MCP795_READ 0x13
|
#define MCP795_READ 0x13
|
||||||
#define MCP795_WRITE 0x12
|
#define MCP795_WRITE 0x12
|
||||||
#define MCP795_UNLOCK 0x14
|
#define MCP795_UNLOCK 0x14
|
||||||
#define MCP795_IDWRITE 0x32
|
#define MCP795_IDWRITE 0x32
|
||||||
@ -37,8 +39,17 @@
|
|||||||
#define MCP795_CLRWDT 0x44
|
#define MCP795_CLRWDT 0x44
|
||||||
#define MCP795_CLRRAM 0x54
|
#define MCP795_CLRRAM 0x54
|
||||||
|
|
||||||
#define MCP795_ST_BIT 0x80
|
/* MCP795 RTCC registers, see datasheet table 4-1 */
|
||||||
#define MCP795_24_BIT 0x40
|
#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)
|
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;
|
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)
|
static int mcp795_set_time(struct device *dev, struct rtc_time *tim)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
u8 data[7];
|
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 */
|
/* 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)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
data[0] = (data[0] & 0x80) | ((tim->tm_sec / 10) << 4) | (tim->tm_sec % 10);
|
data[0] = (data[0] & 0x80) | bin2bcd(tim->tm_sec);
|
||||||
data[1] = (data[1] & 0x80) | ((tim->tm_min / 10) << 4) | (tim->tm_min % 10);
|
data[1] = (data[1] & 0x80) | bin2bcd(tim->tm_min);
|
||||||
data[2] = ((tim->tm_hour / 10) << 4) | (tim->tm_hour % 10);
|
data[2] = bin2bcd(tim->tm_hour);
|
||||||
data[4] = ((tim->tm_mday / 10) << 4) | ((tim->tm_mday) % 10);
|
data[4] = bin2bcd(tim->tm_mday);
|
||||||
data[5] = (data[5] & 0x10) | (tim->tm_mon / 10) | (tim->tm_mon % 10);
|
data[5] = (data[5] & MCP795_LP_BIT) | bin2bcd(tim->tm_mon + 1);
|
||||||
|
|
||||||
if (tim->tm_year > 100)
|
if (tim->tm_year > 100)
|
||||||
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);
|
||||||
|
|
||||||
ret = mcp795_rtcc_write(dev, 0x01, data, sizeof(data));
|
/* 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, 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)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -132,17 +210,17 @@ static int mcp795_read_time(struct device *dev, struct rtc_time *tim)
|
|||||||
int ret;
|
int ret;
|
||||||
u8 data[7];
|
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)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
tim->tm_sec = ((data[0] & 0x70) >> 4) * 10 + (data[0] & 0x0f);
|
tim->tm_sec = bcd2bin(data[0] & 0x7F);
|
||||||
tim->tm_min = ((data[1] & 0x70) >> 4) * 10 + (data[1] & 0x0f);
|
tim->tm_min = bcd2bin(data[1] & 0x7F);
|
||||||
tim->tm_hour = ((data[2] & 0x30) >> 4) * 10 + (data[2] & 0x0f);
|
tim->tm_hour = bcd2bin(data[2] & 0x3F);
|
||||||
tim->tm_mday = ((data[4] & 0x30) >> 4) * 10 + (data[4] & 0x0f);
|
tim->tm_mday = bcd2bin(data[4] & 0x3F);
|
||||||
tim->tm_mon = ((data[5] & 0x10) >> 4) * 10 + (data[5] & 0x0f);
|
tim->tm_mon = bcd2bin(data[5] & 0x1F) - 1;
|
||||||
tim->tm_year = ((data[6] & 0xf0) >> 4) * 10 + (data[6] & 0x0f) + 100; /* Assume we are in 20xx */
|
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",
|
dev_dbg(dev, "Read from mcp795: %04d-%02d-%02d %02d:%02d:%02d\n",
|
||||||
tim->tm_year + 1900, tim->tm_mon, tim->tm_mday,
|
tim->tm_year + 1900, tim->tm_mon, tim->tm_mday,
|
||||||
@ -169,13 +247,13 @@ static int mcp795_probe(struct spi_device *spi)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Start the oscillator */
|
/* Start the oscillator but don't set the value of EXTOSC bit */
|
||||||
mcp795_rtcc_set_bits(&spi->dev, 0x01, MCP795_ST_BIT, MCP795_ST_BIT);
|
mcp795_start_oscillator(&spi->dev, NULL);
|
||||||
/* Clear the 12 hour mode flag*/
|
/* Clear the 12 hour mode flag*/
|
||||||
mcp795_rtcc_set_bits(&spi->dev, 0x03, MCP795_24_BIT, 0);
|
mcp795_rtcc_set_bits(&spi->dev, 0x03, MCP795_24_BIT, 0);
|
||||||
|
|
||||||
rtc = devm_rtc_device_register(&spi->dev, "rtc-mcp795",
|
rtc = devm_rtc_device_register(&spi->dev, "rtc-mcp795",
|
||||||
&mcp795_rtc_ops, THIS_MODULE);
|
&mcp795_rtc_ops, THIS_MODULE);
|
||||||
if (IS_ERR(rtc))
|
if (IS_ERR(rtc))
|
||||||
return PTR_ERR(rtc);
|
return PTR_ERR(rtc);
|
||||||
|
|
||||||
|
@ -191,12 +191,19 @@ static int pcf85063_probe(struct i2c_client *client,
|
|||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
struct rtc_device *rtc;
|
struct rtc_device *rtc;
|
||||||
|
int err;
|
||||||
|
|
||||||
dev_dbg(&client->dev, "%s\n", __func__);
|
dev_dbg(&client->dev, "%s\n", __func__);
|
||||||
|
|
||||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||||
return -ENODEV;
|
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,
|
rtc = devm_rtc_device_register(&client->dev,
|
||||||
pcf85063_driver.driver.name,
|
pcf85063_driver.driver.name,
|
||||||
&pcf85063_rtc_ops, THIS_MODULE);
|
&pcf85063_rtc_ops, THIS_MODULE);
|
||||||
|
453
drivers/rtc/rtc-r7301.c
Normal file
453
drivers/rtc/rtc-r7301.c
Normal file
@ -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");
|
@ -1,20 +1,18 @@
|
|||||||
/* rtc-starfire.c: Starfire platform RTC driver.
|
/* rtc-starfire.c: Starfire platform RTC driver.
|
||||||
|
*
|
||||||
|
* Author: David S. Miller
|
||||||
|
* License: GPL
|
||||||
*
|
*
|
||||||
* Copyright (C) 2008 David S. Miller <davem@davemloft.net>
|
* Copyright (C) 2008 David S. Miller <davem@davemloft.net>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/rtc.h>
|
#include <linux/rtc.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
#include <asm/oplib.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 u32 starfire_get_time(void)
|
||||||
{
|
{
|
||||||
static char obp_gettod[32];
|
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);
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
/* rtc-sun4v.c: Hypervisor based RTC for SUN4V systems.
|
/* rtc-sun4v.c: Hypervisor based RTC for SUN4V systems.
|
||||||
|
*
|
||||||
|
* Author: David S. Miller
|
||||||
|
* License: GPL
|
||||||
*
|
*
|
||||||
* Copyright (C) 2008 David S. Miller <davem@davemloft.net>
|
* Copyright (C) 2008 David S. Miller <davem@davemloft.net>
|
||||||
*/
|
*/
|
||||||
@ -6,7 +9,6 @@
|
|||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/rtc.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);
|
builtin_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");
|
|
||||||
|
@ -33,6 +33,10 @@
|
|||||||
|
|
||||||
#include <linux/i2c/twl.h>
|
#include <linux/i2c/twl.h>
|
||||||
|
|
||||||
|
enum twl_class {
|
||||||
|
TWL_4030 = 0,
|
||||||
|
TWL_6030,
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* RTC block register offsets (use TWL_MODULE_RTC)
|
* RTC block register offsets (use TWL_MODULE_RTC)
|
||||||
@ -136,16 +140,30 @@ static const u8 twl6030_rtc_reg_map[] = {
|
|||||||
#define ALL_TIME_REGS 6
|
#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.
|
* 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;
|
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)
|
if (ret < 0)
|
||||||
pr_err("Could not read TWL register %X - error %d\n", reg, ret);
|
pr_err("Could not read TWL register %X - error %d\n", reg, ret);
|
||||||
return ret;
|
return ret;
|
||||||
@ -154,40 +172,34 @@ static int twl_rtc_read_u8(u8 *data, u8 reg)
|
|||||||
/*
|
/*
|
||||||
* Supports 1 byte write to TWL RTC registers.
|
* 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;
|
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)
|
if (ret < 0)
|
||||||
pr_err("Could not write TWL register %X - error %d\n",
|
pr_err("Could not write TWL register %X - error %d\n",
|
||||||
reg, ret);
|
reg, ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* 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.
|
* 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;
|
unsigned char val;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* if the bit is set, return from here */
|
/* if the bit is set, return from here */
|
||||||
if (rtc_irq_bits & bit)
|
if (twl_rtc->rtc_irq_bits & bit)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
val = rtc_irq_bits | bit;
|
val = twl_rtc->rtc_irq_bits | bit;
|
||||||
val &= ~BIT_RTC_INTERRUPTS_REG_EVERY_M;
|
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)
|
if (ret == 0)
|
||||||
rtc_irq_bits = val;
|
twl_rtc->rtc_irq_bits = val;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -195,19 +207,19 @@ static int set_rtc_irq_bit(unsigned char bit)
|
|||||||
/*
|
/*
|
||||||
* Disable update and/or alarm interrupts.
|
* 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;
|
unsigned char val;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* if the bit is clear, return from here */
|
/* if the bit is clear, return from here */
|
||||||
if (!(rtc_irq_bits & bit))
|
if (!(twl_rtc->rtc_irq_bits & bit))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
val = rtc_irq_bits & ~bit;
|
val = twl_rtc->rtc_irq_bits & ~bit;
|
||||||
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)
|
if (ret == 0)
|
||||||
rtc_irq_bits = val;
|
twl_rtc->rtc_irq_bits = val;
|
||||||
|
|
||||||
return ret;
|
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)
|
static int twl_rtc_alarm_irq_enable(struct device *dev, unsigned enabled)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev = to_platform_device(dev);
|
struct platform_device *pdev = to_platform_device(dev);
|
||||||
|
struct twl_rtc *twl_rtc = dev_get_drvdata(dev);
|
||||||
int irq = platform_get_irq(pdev, 0);
|
int irq = platform_get_irq(pdev, 0);
|
||||||
static bool twl_rtc_wake_enabled;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
ret = set_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
|
ret = set_rtc_irq_bit(twl_rtc,
|
||||||
if (device_can_wakeup(dev) && !twl_rtc_wake_enabled) {
|
BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
|
||||||
|
if (device_can_wakeup(dev) && !twl_rtc->wake_enabled) {
|
||||||
enable_irq_wake(irq);
|
enable_irq_wake(irq);
|
||||||
twl_rtc_wake_enabled = true;
|
twl_rtc->wake_enabled = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ret = mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
|
ret = mask_rtc_irq_bit(twl_rtc,
|
||||||
if (twl_rtc_wake_enabled) {
|
BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
|
||||||
|
if (twl_rtc->wake_enabled) {
|
||||||
disable_irq_wake(irq);
|
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)
|
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];
|
unsigned char rtc_data[ALL_TIME_REGS];
|
||||||
int ret;
|
int ret;
|
||||||
u8 save_control;
|
u8 save_control;
|
||||||
u8 rtc_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) {
|
if (ret < 0) {
|
||||||
dev_err(dev, "%s: reading CTRL_REG, error %d\n", __func__, ret);
|
dev_err(dev, "%s: reading CTRL_REG, error %d\n", __func__, ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
/* for twl6030/32 make sure BIT_RTC_CTRL_REG_GET_TIME_M is clear */
|
/* 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) {
|
if (save_control & BIT_RTC_CTRL_REG_GET_TIME_M) {
|
||||||
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) {
|
if (ret < 0) {
|
||||||
dev_err(dev, "%s clr GET_TIME, error %d\n",
|
dev_err(dev, "%s clr GET_TIME, error %d\n",
|
||||||
__func__, ret);
|
__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;
|
rtc_control = save_control | BIT_RTC_CTRL_REG_GET_TIME_M;
|
||||||
|
|
||||||
/* for twl6030/32 enable read access to static shadowed registers */
|
/* 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;
|
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) {
|
if (ret < 0) {
|
||||||
dev_err(dev, "%s: writing CTRL_REG, error %d\n", __func__, ret);
|
dev_err(dev, "%s: writing CTRL_REG, error %d\n", __func__, ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = twl_i2c_read(TWL_MODULE_RTC, rtc_data,
|
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) {
|
if (ret < 0) {
|
||||||
dev_err(dev, "%s: reading data, error %d\n", __func__, ret);
|
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 */
|
/* for twl6030 restore original state of rtc control register */
|
||||||
if (twl_class_is_6030()) {
|
if (twl_rtc->class == TWL_6030) {
|
||||||
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) {
|
if (ret < 0) {
|
||||||
dev_err(dev, "%s: restore CTRL_REG, error %d\n",
|
dev_err(dev, "%s: restore CTRL_REG, error %d\n",
|
||||||
__func__, ret);
|
__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)
|
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 save_control;
|
||||||
unsigned char rtc_data[ALL_TIME_REGS];
|
unsigned char rtc_data[ALL_TIME_REGS];
|
||||||
int ret;
|
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);
|
rtc_data[5] = bin2bcd(tm->tm_year - 100);
|
||||||
|
|
||||||
/* Stop RTC while updating the TC registers */
|
/* 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)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
save_control &= ~BIT_RTC_CTRL_REG_STOP_RTC_M;
|
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)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/* update all the time registers in one shot */
|
/* update all the time registers in one shot */
|
||||||
ret = twl_i2c_write(TWL_MODULE_RTC, rtc_data,
|
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) {
|
if (ret < 0) {
|
||||||
dev_err(dev, "rtc_set_time error %d\n", ret);
|
dev_err(dev, "rtc_set_time error %d\n", ret);
|
||||||
goto out;
|
goto out;
|
||||||
@ -344,7 +361,7 @@ static int twl_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
|||||||
|
|
||||||
/* Start back RTC */
|
/* Start back RTC */
|
||||||
save_control |= BIT_RTC_CTRL_REG_STOP_RTC_M;
|
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:
|
out:
|
||||||
return ret;
|
return ret;
|
||||||
@ -355,11 +372,12 @@ out:
|
|||||||
*/
|
*/
|
||||||
static int twl_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
|
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];
|
unsigned char rtc_data[ALL_TIME_REGS];
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = twl_i2c_read(TWL_MODULE_RTC, rtc_data,
|
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) {
|
if (ret < 0) {
|
||||||
dev_err(dev, "rtc_read_alarm error %d\n", ret);
|
dev_err(dev, "rtc_read_alarm error %d\n", ret);
|
||||||
return 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;
|
alm->time.tm_year = bcd2bin(rtc_data[5]) + 100;
|
||||||
|
|
||||||
/* report cached alarm enable state */
|
/* 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;
|
alm->enabled = 1;
|
||||||
|
|
||||||
return ret;
|
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)
|
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];
|
unsigned char alarm_data[ALL_TIME_REGS];
|
||||||
int ret;
|
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 */
|
/* update all the alarm registers in one shot */
|
||||||
ret = twl_i2c_write(TWL_MODULE_RTC, alarm_data,
|
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) {
|
if (ret) {
|
||||||
dev_err(dev, "rtc_set_alarm error %d\n", ret);
|
dev_err(dev, "rtc_set_alarm error %d\n", ret);
|
||||||
goto out;
|
goto out;
|
||||||
@ -410,14 +430,15 @@ out:
|
|||||||
return ret;
|
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;
|
unsigned long events;
|
||||||
int ret = IRQ_NONE;
|
int ret = IRQ_NONE;
|
||||||
int res;
|
int res;
|
||||||
u8 rd_reg;
|
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)
|
if (res)
|
||||||
goto out;
|
goto out;
|
||||||
/*
|
/*
|
||||||
@ -431,12 +452,12 @@ static irqreturn_t twl_rtc_interrupt(int irq, void *rtc)
|
|||||||
else
|
else
|
||||||
events = RTC_IRQF | RTC_PF;
|
events = RTC_IRQF | RTC_PF;
|
||||||
|
|
||||||
res = twl_rtc_write_u8(BIT_RTC_STATUS_REG_ALARM_M,
|
res = twl_rtc_write_u8(twl_rtc, BIT_RTC_STATUS_REG_ALARM_M,
|
||||||
REG_RTC_STATUS_REG);
|
REG_RTC_STATUS_REG);
|
||||||
if (res)
|
if (res)
|
||||||
goto out;
|
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
|
/* Clear on Read enabled. RTC_IT bit of TWL4030_INT_PWR_ISR1
|
||||||
* needs 2 reads to clear the interrupt. One read is done in
|
* needs 2 reads to clear the interrupt. One read is done in
|
||||||
* do_twl_pwrirq(). Doing the second read, to clear
|
* 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 */
|
/* Notify RTC core on event */
|
||||||
rtc_update_irq(rtc, 1, events);
|
rtc_update_irq(twl_rtc->rtc, 1, events);
|
||||||
|
|
||||||
ret = IRQ_HANDLED;
|
ret = IRQ_HANDLED;
|
||||||
out:
|
out:
|
||||||
@ -474,21 +495,36 @@ static const struct rtc_class_ops twl_rtc_ops = {
|
|||||||
|
|
||||||
static int twl_rtc_probe(struct platform_device *pdev)
|
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 ret = -EINVAL;
|
||||||
int irq = platform_get_irq(pdev, 0);
|
int irq = platform_get_irq(pdev, 0);
|
||||||
u8 rd_reg;
|
u8 rd_reg;
|
||||||
|
|
||||||
|
if (!np) {
|
||||||
|
dev_err(&pdev->dev, "no DT info\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
if (irq <= 0)
|
if (irq <= 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* Initialize the register map */
|
twl_rtc = devm_kzalloc(&pdev->dev, sizeof(*twl_rtc), GFP_KERNEL);
|
||||||
if (twl_class_is_4030())
|
if (!twl_rtc)
|
||||||
rtc_reg_map = (u8 *)twl4030_rtc_reg_map;
|
return -ENOMEM;
|
||||||
else
|
|
||||||
rtc_reg_map = (u8 *)twl6030_rtc_reg_map;
|
|
||||||
|
|
||||||
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)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -499,11 +535,11 @@ static int twl_rtc_probe(struct platform_device *pdev)
|
|||||||
dev_warn(&pdev->dev, "Pending Alarm interrupt detected.\n");
|
dev_warn(&pdev->dev, "Pending Alarm interrupt detected.\n");
|
||||||
|
|
||||||
/* Clear RTC Power up reset and pending alarm interrupts */
|
/* 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)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (twl_class_is_6030()) {
|
if (twl_rtc->class == TWL_6030) {
|
||||||
twl6030_interrupt_unmask(TWL6030_RTC_INT_MASK,
|
twl6030_interrupt_unmask(TWL6030_RTC_INT_MASK,
|
||||||
REG_INT_MSK_LINE_A);
|
REG_INT_MSK_LINE_A);
|
||||||
twl6030_interrupt_unmask(TWL6030_RTC_INT_MASK,
|
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");
|
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)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* ensure interrupts are disabled, bootloaders can be strange */
|
/* 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)
|
if (ret < 0)
|
||||||
dev_warn(&pdev->dev, "unable to disable interrupt\n");
|
dev_warn(&pdev->dev, "unable to disable interrupt\n");
|
||||||
|
|
||||||
/* init cached IRQ enable bits */
|
/* 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)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, twl_rtc);
|
||||||
device_init_wakeup(&pdev->dev, 1);
|
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);
|
&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",
|
dev_err(&pdev->dev, "can't register RTC device, err %ld\n",
|
||||||
PTR_ERR(rtc));
|
PTR_ERR(twl_rtc->rtc));
|
||||||
return PTR_ERR(rtc);
|
return PTR_ERR(twl_rtc->rtc);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
|
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
|
||||||
twl_rtc_interrupt,
|
twl_rtc_interrupt,
|
||||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||||
dev_name(&rtc->dev), rtc);
|
dev_name(&twl_rtc->rtc->dev), twl_rtc);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "IRQ is not free.\n");
|
dev_err(&pdev->dev, "IRQ is not free.\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
platform_set_drvdata(pdev, rtc);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -554,10 +592,12 @@ static int twl_rtc_probe(struct platform_device *pdev)
|
|||||||
*/
|
*/
|
||||||
static int twl_rtc_remove(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 */
|
/* leave rtc running, but disable irqs */
|
||||||
mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
|
mask_rtc_irq_bit(twl_rtc, BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
|
||||||
mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
|
mask_rtc_irq_bit(twl_rtc, BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
|
||||||
if (twl_class_is_6030()) {
|
if (twl_rtc->class == TWL_6030) {
|
||||||
twl6030_interrupt_mask(TWL6030_RTC_INT_MASK,
|
twl6030_interrupt_mask(TWL6030_RTC_INT_MASK,
|
||||||
REG_INT_MSK_LINE_A);
|
REG_INT_MSK_LINE_A);
|
||||||
twl6030_interrupt_mask(TWL6030_RTC_INT_MASK,
|
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)
|
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
|
/* mask timer interrupts, but leave alarm interrupts on to enable
|
||||||
power-on when alarm is triggered */
|
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
|
#ifdef CONFIG_PM_SLEEP
|
||||||
static unsigned char irqstat;
|
|
||||||
|
|
||||||
static int twl_rtc_suspend(struct device *dev)
|
static int twl_rtc_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
irqstat = rtc_irq_bits;
|
struct twl_rtc *twl_rtc = dev_get_drvdata(dev);
|
||||||
|
|
||||||
mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
|
twl_rtc->irqstat = twl_rtc->rtc_irq_bits;
|
||||||
|
|
||||||
|
mask_rtc_irq_bit(twl_rtc, BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int twl_rtc_resume(struct device *dev)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static SIMPLE_DEV_PM_OPS(twl_rtc_pm_ops, twl_rtc_suspend, twl_rtc_resume);
|
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[] = {
|
static const struct of_device_id twl_rtc_of_match[] = {
|
||||||
{.compatible = "ti,twl4030-rtc", },
|
{.compatible = "ti,twl4030-rtc", },
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, twl_rtc_of_match);
|
MODULE_DEVICE_TABLE(of, twl_rtc_of_match);
|
||||||
#endif
|
|
||||||
|
|
||||||
MODULE_ALIAS("platform:twl_rtc");
|
|
||||||
|
|
||||||
static struct platform_driver twl4030rtc_driver = {
|
static struct platform_driver twl4030rtc_driver = {
|
||||||
.probe = twl_rtc_probe,
|
.probe = twl_rtc_probe,
|
||||||
@ -611,7 +651,7 @@ static struct platform_driver twl4030rtc_driver = {
|
|||||||
.driver = {
|
.driver = {
|
||||||
.name = "twl_rtc",
|
.name = "twl_rtc",
|
||||||
.pm = &twl_rtc_pm_ops,
|
.pm = &twl_rtc_pm_ops,
|
||||||
.of_match_table = of_match_ptr(twl_rtc_of_match),
|
.of_match_table = twl_rtc_of_match,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user