RTC for 4.13
Subsystem: - expose non volatile RAM using nvmem instead of open coding in many drivers. Unfortunately, this option has to be enabled by default to not break existing users. - rtctest can now test for cutoff dates, showing when an RTC will start failing to properly save time and date. - new RTC registration functions to remove race conditions in drivers Newly supported RTCs: - Broadcom STB wake-timer - Epson RX8130CE - Maxim IC DS1308 - STMicroelectronics STM32H7 Drivers: - ds1307: use regmap, use nvmem, more cleanups - ds3232: temperature reading support - gemini: renamed to ftrtc010 - m41t80: use CCF to expose the clock - rv8803: use nvmem - s3c: many cleanups - st-lpc: fix y2106 bug -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEXx9Viay1+e7J/aM4AyWl4gNJNJIFAllnMUIACgkQAyWl4gNJ NJJGNxAAqMTrggkF6KTvFCVAoMHdkeAxuoyigwCH8BCm2gOm5Qj8ZodZxndcl3Gb dWG+c1pHf4KXrz59h6ZGI4qFgIpCyJpjGpyJs0Pvt6gY7YIqHrEa1nvcrPO7DaWw fPPcszyiymDOsb6d+wJzriA2ISJUHy7Kf6FUb0fjQLoYNl7ezgzdV6+dvePOPcW1 kaAfRX8XqrkECrDFFHlX1Szb78qGhcUB1TmWFW+hadICTguBLX/fro0DKWRw2POQ y3cHKqMzFhTD1+jkp26o535x/D9CWDXzLmLvRF5tBQ0X7V2UIGchj4aNEHT0Ruwx YlGzB3WDwfj/Jl+VALmY27mplf71z5ppJRhaFn84OWrJmvjS/2EF9TCCBc4XvzzX dH/5nvPyNrUYnayTTCXiPhN3p4ivywHXqA9gkHcWb3BagNIpuvwNVnJT/Sxz3Y5R Gt2zGl07NKQ1EtEThQEIBOMXy9nJ2PVJdQFmLehj1PfxX+Gbs42tWBILzl4n1rgT yUFLMGw1Y0/h39jw7t+uKM7v0aXPHOXLrwaDKIj+c4ffVXD8IALhgG7BL4dOQPSF rRPKi5QNYJMnuBeKHJrFlq7xWqHRVUfTFh16eyYvwGLGWiUuGe9akhlabl6bE8jG fm3TlHPNieGMObXijwEVePkY6z7E0CLE+d1iQsDK6ZgO/z3pdOo= =QDxE -----END PGP SIGNATURE----- Merge tag 'rtc-4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux Pull RTC updates from Alexandre Belloni: "Here is the pull-request for the RTC subsystem for 4.13. Subsystem: - expose non volatile RAM using nvmem instead of open coding in many drivers. Unfortunately, this option has to be enabled by default to not break existing users. - rtctest can now test for cutoff dates, showing when an RTC will start failing to properly save time and date. - new RTC registration functions to remove race conditions in drivers Newly supported RTCs: - Broadcom STB wake-timer - Epson RX8130CE - Maxim IC DS1308 - STMicroelectronics STM32H7 Drivers: - ds1307: use regmap, use nvmem, more cleanups - ds3232: temperature reading support - gemini: renamed to ftrtc010 - m41t80: use CCF to expose the clock - rv8803: use nvmem - s3c: many cleanups - st-lpc: fix y2106 bug" * tag 'rtc-4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux: (51 commits) rtc: Remove wrong deprecation comment nvmem: include linux/err.h from header rtc: st-lpc: make it robust against y2038/2106 bug rtc: rtctest: add check for problematic dates tools: timer: add rtctest_setdate rtc: ds1307: remove ds1307_remove rtc: ds1307: use generic nvmem rtc: ds1307: switch to rtc_register_device rtc: rv8803: remove rv8803_remove rtc: rv8803: use generic nvmem support rtc: rv8803: switch to rtc_register_device rtc: add generic nvmem support rtc: at91rm9200: remove race condition rtc: introduce new registration method rtc: class separate id allocation from registration rtc: class separate device allocation from registration rtc: stm32: add STM32H7 RTC support dt-bindings: rtc: stm32: add support for STM32H7 rtc: ds1307: add ds1308 variant rtc: ds3232: add temperature support ...
This commit is contained in:
commit
3a00be1923
@ -0,0 +1,22 @@
|
|||||||
|
Broadcom STB wake-up Timer
|
||||||
|
|
||||||
|
The Broadcom STB wake-up timer provides a 27Mhz resolution timer, with the
|
||||||
|
ability to wake up the system from low-power suspend/standby modes.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible : should contain "brcm,brcmstb-waketimer"
|
||||||
|
- reg : the register start and length for the WKTMR block
|
||||||
|
- interrupts : The TIMER interrupt
|
||||||
|
- interrupt-parent: The phandle to the Always-On (AON) Power Management (PM) L2
|
||||||
|
interrupt controller node
|
||||||
|
- clocks : The phandle to the UPG fixed clock (27Mhz domain)
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
waketimer@f0411580 {
|
||||||
|
compatible = "brcm,brcmstb-waketimer";
|
||||||
|
reg = <0xf0411580 0x14>;
|
||||||
|
interrupts = <0x3>;
|
||||||
|
interrupt-parent = <&aon_pm_l2_intc>;
|
||||||
|
clocks = <&upg_fixed>;
|
||||||
|
};
|
@ -1,14 +0,0 @@
|
|||||||
* Cortina Systems Gemini RTC
|
|
||||||
|
|
||||||
Gemini SoC real-time clock.
|
|
||||||
|
|
||||||
Required properties:
|
|
||||||
- compatible : Should be "cortina,gemini-rtc"
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
rtc@45000000 {
|
|
||||||
compatible = "cortina,gemini-rtc";
|
|
||||||
reg = <0x45000000 0x100>;
|
|
||||||
interrupts = <17 IRQ_TYPE_LEVEL_HIGH>;
|
|
||||||
};
|
|
28
Documentation/devicetree/bindings/rtc/faraday,ftrtc010.txt
Normal file
28
Documentation/devicetree/bindings/rtc/faraday,ftrtc010.txt
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
* Faraday Technology FTRTC010 Real Time Clock
|
||||||
|
|
||||||
|
This RTC appears in for example the Storlink Gemini family of
|
||||||
|
SoCs.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible : Should be one of:
|
||||||
|
"faraday,ftrtc010"
|
||||||
|
"cortina,gemini-rtc", "faraday,ftrtc010"
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- clocks: when present should contain clock references to the
|
||||||
|
PCLK and EXTCLK clocks. Faraday calls the later CLK1HZ and
|
||||||
|
says the clock should be 1 Hz, but implementers actually seem
|
||||||
|
to choose different clocks here, like Cortina who chose
|
||||||
|
32768 Hz (a typical low-power clock).
|
||||||
|
- clock-names: should name the clocks "PCLK" and "EXTCLK"
|
||||||
|
respectively.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
rtc@45000000 {
|
||||||
|
compatible = "cortina,gemini-rtc";
|
||||||
|
reg = <0x45000000 0x100>;
|
||||||
|
interrupts = <17 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
clocks = <&foo 0>, <&foo 1>;
|
||||||
|
clock-names = "PCLK", "EXTCLK";
|
||||||
|
};
|
@ -1,17 +1,25 @@
|
|||||||
STM32 Real Time Clock
|
STM32 Real Time Clock
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible: "st,stm32-rtc".
|
- compatible: can be either "st,stm32-rtc" or "st,stm32h7-rtc", depending on
|
||||||
|
the device is compatible with stm32(f4/f7) or stm32h7.
|
||||||
- reg: address range of rtc register set.
|
- reg: address range of rtc register set.
|
||||||
- clocks: reference to the clock entry ck_rtc.
|
- clocks: can use up to two clocks, depending on part used:
|
||||||
|
- "rtc_ck": RTC clock source.
|
||||||
|
It is required on stm32(f4/f7) and stm32h7.
|
||||||
|
- "pclk": RTC APB interface clock.
|
||||||
|
It is not present on stm32(f4/f7).
|
||||||
|
It is required on stm32h7.
|
||||||
|
- clock-names: must be "rtc_ck" and "pclk".
|
||||||
|
It is required only on stm32h7.
|
||||||
- interrupt-parent: phandle for the interrupt controller.
|
- interrupt-parent: phandle for the interrupt controller.
|
||||||
- interrupts: rtc alarm interrupt.
|
- interrupts: rtc alarm interrupt.
|
||||||
- st,syscfg: phandle for pwrcfg, mandatory to disable/enable backup domain
|
- st,syscfg: phandle for pwrcfg, mandatory to disable/enable backup domain
|
||||||
(RTC registers) write protection.
|
(RTC registers) write protection.
|
||||||
|
|
||||||
Optional properties (to override default ck_rtc parent clock):
|
Optional properties (to override default rtc_ck parent clock):
|
||||||
- assigned-clocks: reference to the ck_rtc clock entry.
|
- assigned-clocks: reference to the rtc_ck clock entry.
|
||||||
- assigned-clock-parents: phandle of the new parent clock of ck_rtc.
|
- assigned-clock-parents: phandle of the new parent clock of rtc_ck.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@ -25,3 +33,17 @@ Example:
|
|||||||
interrupts = <17 1>;
|
interrupts = <17 1>;
|
||||||
st,syscfg = <&pwrcfg>;
|
st,syscfg = <&pwrcfg>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
rtc: rtc@58004000 {
|
||||||
|
compatible = "st,stm32h7-rtc";
|
||||||
|
reg = <0x58004000 0x400>;
|
||||||
|
clocks = <&rcc RTCAPB_CK>, <&rcc RTC_CK>;
|
||||||
|
clock-names = "pclk", "rtc_ck";
|
||||||
|
assigned-clocks = <&rcc RTC_CK>;
|
||||||
|
assigned-clock-parents = <&rcc LSE_CK>;
|
||||||
|
interrupt-parent = <&exti>;
|
||||||
|
interrupts = <17 1>;
|
||||||
|
interrupt-names = "alarm";
|
||||||
|
st,syscfg = <&pwrcfg>;
|
||||||
|
status = "disabled";
|
||||||
|
};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
=======================================
|
||||||
Real Time Clock (RTC) Drivers for Linux
|
Real Time Clock (RTC) Drivers for Linux
|
||||||
=======================================
|
=======================================
|
||||||
|
|
||||||
When Linux developers talk about a "Real Time Clock", they usually mean
|
When Linux developers talk about a "Real Time Clock", they usually mean
|
||||||
something that tracks wall clock time and is battery backed so that it
|
something that tracks wall clock time and is battery backed so that it
|
||||||
@ -32,8 +32,8 @@ only issue an alarm up to 24 hours in the future, other hardware may
|
|||||||
be able to schedule one any time in the upcoming century.
|
be able to schedule one any time in the upcoming century.
|
||||||
|
|
||||||
|
|
||||||
Old PC/AT-Compatible driver: /dev/rtc
|
Old PC/AT-Compatible driver: /dev/rtc
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
|
||||||
All PCs (even Alpha machines) have a Real Time Clock built into them.
|
All PCs (even Alpha machines) have a Real Time Clock built into them.
|
||||||
Usually they are built into the chipset of the computer, but some may
|
Usually they are built into the chipset of the computer, but some may
|
||||||
@ -105,8 +105,8 @@ that will be using this driver. See the code at the end of this document.
|
|||||||
(The original /dev/rtc driver was written by Paul Gortmaker.)
|
(The original /dev/rtc driver was written by Paul Gortmaker.)
|
||||||
|
|
||||||
|
|
||||||
New portable "RTC Class" drivers: /dev/rtcN
|
New portable "RTC Class" drivers: /dev/rtcN
|
||||||
--------------------------------------------
|
--------------------------------------------
|
||||||
|
|
||||||
Because Linux supports many non-ACPI and non-PC platforms, some of which
|
Because Linux supports many non-ACPI and non-PC platforms, some of which
|
||||||
have more than one RTC style clock, it needed a more portable solution
|
have more than one RTC style clock, it needed a more portable solution
|
||||||
@ -136,35 +136,39 @@ a high functionality RTC is integrated into the SOC. That system might read
|
|||||||
the system clock from the discrete RTC, but use the integrated one for all
|
the system clock from the discrete RTC, but use the integrated one for all
|
||||||
other tasks, because of its greater functionality.
|
other tasks, because of its greater functionality.
|
||||||
|
|
||||||
SYSFS INTERFACE
|
SYSFS interface
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
The sysfs interface under /sys/class/rtc/rtcN provides access to various
|
The sysfs interface under /sys/class/rtc/rtcN provides access to various
|
||||||
rtc attributes without requiring the use of ioctls. All dates and times
|
rtc attributes without requiring the use of ioctls. All dates and times
|
||||||
are in the RTC's timezone, rather than in system time.
|
are in the RTC's timezone, rather than in system time.
|
||||||
|
|
||||||
date: RTC-provided date
|
================ ==============================================================
|
||||||
hctosys: 1 if the RTC provided the system time at boot via the
|
date RTC-provided date
|
||||||
|
hctosys 1 if the RTC provided the system time at boot via the
|
||||||
CONFIG_RTC_HCTOSYS kernel option, 0 otherwise
|
CONFIG_RTC_HCTOSYS kernel option, 0 otherwise
|
||||||
max_user_freq: The maximum interrupt rate an unprivileged user may request
|
max_user_freq The maximum interrupt rate an unprivileged user may request
|
||||||
from this RTC.
|
from this RTC.
|
||||||
name: The name of the RTC corresponding to this sysfs directory
|
name The name of the RTC corresponding to this sysfs directory
|
||||||
since_epoch: The number of seconds since the epoch according to the RTC
|
since_epoch The number of seconds since the epoch according to the RTC
|
||||||
time: RTC-provided time
|
time RTC-provided time
|
||||||
wakealarm: The time at which the clock will generate a system wakeup
|
wakealarm The time at which the clock will generate a system wakeup
|
||||||
event. This is a one shot wakeup event, so must be reset
|
event. This is a one shot wakeup event, so must be reset
|
||||||
after wake if a daily wakeup is required. Format is seconds since
|
after wake if a daily wakeup is required. Format is seconds
|
||||||
the epoch by default, or if there's a leading +, seconds in the
|
since the epoch by default, or if there's a leading +, seconds
|
||||||
future, or if there is a leading +=, seconds ahead of the current
|
in the future, or if there is a leading +=, seconds ahead of
|
||||||
alarm.
|
the current alarm.
|
||||||
offset: The amount which the rtc clock has been adjusted in firmware.
|
offset The amount which the rtc clock has been adjusted in firmware.
|
||||||
Visible only if the driver supports clock offset adjustment.
|
Visible only if the driver supports clock offset adjustment.
|
||||||
The unit is parts per billion, i.e. The number of clock ticks
|
The unit is parts per billion, i.e. The number of clock ticks
|
||||||
which are added to or removed from the rtc's base clock per
|
which are added to or removed from the rtc's base clock per
|
||||||
billion ticks. A positive value makes a day pass more slowly,
|
billion ticks. A positive value makes a day pass more slowly,
|
||||||
longer, and a negative value makes a day pass more quickly.
|
longer, and a negative value makes a day pass more quickly.
|
||||||
|
*/nvmem The non volatile storage exported as a raw file, as described
|
||||||
|
in Documentation/nvmem/nvmem.txt
|
||||||
|
================ ==============================================================
|
||||||
|
|
||||||
IOCTL INTERFACE
|
IOCTL interface
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
The ioctl() calls supported by /dev/rtc are also supported by the RTC class
|
The ioctl() calls supported by /dev/rtc are also supported by the RTC class
|
||||||
|
@ -1255,7 +1255,7 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
|||||||
T: git git://github.com/ulli-kroll/linux.git
|
T: git git://github.com/ulli-kroll/linux.git
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: arch/arm/mach-gemini/
|
F: arch/arm/mach-gemini/
|
||||||
F: drivers/rtc/rtc-gemini.c
|
F: drivers/rtc/rtc-ftrtc010.c
|
||||||
|
|
||||||
ARM/CSR SIRFPRIMA2 MACHINE SUPPORT
|
ARM/CSR SIRFPRIMA2 MACHINE SUPPORT
|
||||||
M: Barry Song <baohua@kernel.org>
|
M: Barry Song <baohua@kernel.org>
|
||||||
|
@ -77,6 +77,14 @@ config RTC_DEBUG
|
|||||||
Say yes here to enable debugging support in the RTC framework
|
Say yes here to enable debugging support in the RTC framework
|
||||||
and individual RTC drivers.
|
and individual RTC drivers.
|
||||||
|
|
||||||
|
config RTC_NVMEM
|
||||||
|
bool "RTC non volatile storage support"
|
||||||
|
select NVMEM
|
||||||
|
default RTC_CLASS
|
||||||
|
help
|
||||||
|
Say yes here to add support for the non volatile (often battery
|
||||||
|
backed) storage present on RTCs.
|
||||||
|
|
||||||
comment "RTC interfaces"
|
comment "RTC interfaces"
|
||||||
|
|
||||||
config RTC_INTF_SYSFS
|
config RTC_INTF_SYSFS
|
||||||
@ -197,6 +205,17 @@ config RTC_DRV_AC100
|
|||||||
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-ac100.
|
will be called rtc-ac100.
|
||||||
|
|
||||||
|
config RTC_DRV_BRCMSTB
|
||||||
|
tristate "Broadcom STB wake-timer"
|
||||||
|
depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST
|
||||||
|
default ARCH_BRCMSTB || BMIPS_GENERIC
|
||||||
|
help
|
||||||
|
If you say yes here you get support for the wake-timer found on
|
||||||
|
Broadcom STB SoCs (BCM7xxx).
|
||||||
|
|
||||||
|
This driver can also be built as a module. If so, the module will
|
||||||
|
be called rtc-brcmstb-waketimer.
|
||||||
|
|
||||||
config RTC_DRV_AS3722
|
config RTC_DRV_AS3722
|
||||||
tristate "ams AS3722 RTC driver"
|
tristate "ams AS3722 RTC driver"
|
||||||
depends on MFD_AS3722
|
depends on MFD_AS3722
|
||||||
@ -791,6 +810,14 @@ config RTC_DRV_DS3232
|
|||||||
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-ds3232.
|
will be called rtc-ds3232.
|
||||||
|
|
||||||
|
config RTC_DRV_DS3232_HWMON
|
||||||
|
bool "HWMON support for Dallas/Maxim DS3232/DS3234"
|
||||||
|
depends on RTC_DRV_DS3232 && HWMON && !(RTC_DRV_DS3232=y && HWMON=m)
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Say Y here if you want to expose temperature sensor data on
|
||||||
|
rtc-ds3232
|
||||||
|
|
||||||
config RTC_DRV_PCF2127
|
config RTC_DRV_PCF2127
|
||||||
tristate "NXP PCF2127"
|
tristate "NXP PCF2127"
|
||||||
depends on RTC_I2C_AND_SPI
|
depends on RTC_I2C_AND_SPI
|
||||||
@ -1484,16 +1511,16 @@ config RTC_DRV_ARMADA38X
|
|||||||
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 armada38x-rtc.
|
will be called armada38x-rtc.
|
||||||
|
|
||||||
config RTC_DRV_GEMINI
|
config RTC_DRV_FTRTC010
|
||||||
tristate "Gemini SoC RTC"
|
tristate "Faraday Technology FTRTC010 RTC"
|
||||||
depends on ARCH_GEMINI || COMPILE_TEST
|
|
||||||
depends on HAS_IOMEM
|
depends on HAS_IOMEM
|
||||||
|
default ARCH_GEMINI
|
||||||
help
|
help
|
||||||
If you say Y here you will get support for the
|
If you say Y here you will get support for the
|
||||||
RTC found on Gemini SoC's.
|
Faraday Technolog FTRTC010 found on e.g. Gemini SoC's.
|
||||||
|
|
||||||
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-gemini.
|
will be called rtc-ftrtc010.
|
||||||
|
|
||||||
config RTC_DRV_PS3
|
config RTC_DRV_PS3
|
||||||
tristate "PS3 RTC"
|
tristate "PS3 RTC"
|
||||||
|
@ -15,6 +15,7 @@ ifdef CONFIG_RTC_DRV_EFI
|
|||||||
rtc-core-y += rtc-efi-platform.o
|
rtc-core-y += rtc-efi-platform.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
rtc-core-$(CONFIG_RTC_NVMEM) += nvmem.o
|
||||||
rtc-core-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o
|
rtc-core-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o
|
||||||
rtc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o
|
rtc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o
|
||||||
rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o
|
rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o
|
||||||
@ -36,6 +37,7 @@ obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
|
|||||||
obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o
|
obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o
|
||||||
obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o
|
obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o
|
||||||
obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o
|
obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o
|
||||||
|
obj-$(CONFIG_RTC_DRV_BRCMSTB) += rtc-brcmstb-waketimer.o
|
||||||
obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o
|
obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o
|
||||||
obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o
|
obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o
|
||||||
obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
|
obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
|
||||||
@ -67,7 +69,7 @@ obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
|
|||||||
obj-$(CONFIG_RTC_DRV_EM3027) += rtc-em3027.o
|
obj-$(CONFIG_RTC_DRV_EM3027) += rtc-em3027.o
|
||||||
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
|
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
|
||||||
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
|
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
|
||||||
obj-$(CONFIG_RTC_DRV_GEMINI) += rtc-gemini.o
|
obj-$(CONFIG_RTC_DRV_FTRTC010) += rtc-ftrtc010.o
|
||||||
obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o
|
obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o
|
||||||
obj-$(CONFIG_RTC_DRV_HID_SENSOR_TIME) += rtc-hid-sensor-time.o
|
obj-$(CONFIG_RTC_DRV_HID_SENSOR_TIME) += rtc-hid-sensor-time.o
|
||||||
obj-$(CONFIG_RTC_DRV_HYM8563) += rtc-hym8563.o
|
obj-$(CONFIG_RTC_DRV_HYM8563) += rtc-hym8563.o
|
||||||
|
@ -150,59 +150,19 @@ static SIMPLE_DEV_PM_OPS(rtc_class_dev_pm_ops, rtc_suspend, rtc_resume);
|
|||||||
#define RTC_CLASS_DEV_PM_OPS NULL
|
#define RTC_CLASS_DEV_PM_OPS NULL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Ensure the caller will set the id before releasing the device */
|
||||||
/**
|
static struct rtc_device *rtc_allocate_device(void)
|
||||||
* rtc_device_register - register w/ RTC class
|
|
||||||
* @dev: the device to register
|
|
||||||
*
|
|
||||||
* rtc_device_unregister() must be called when the class device is no
|
|
||||||
* longer needed.
|
|
||||||
*
|
|
||||||
* Returns the pointer to the new struct class device.
|
|
||||||
*/
|
|
||||||
struct rtc_device *rtc_device_register(const char *name, struct device *dev,
|
|
||||||
const struct rtc_class_ops *ops,
|
|
||||||
struct module *owner)
|
|
||||||
{
|
{
|
||||||
struct rtc_device *rtc;
|
struct rtc_device *rtc;
|
||||||
struct rtc_wkalrm alrm;
|
|
||||||
int of_id = -1, id = -1, err;
|
|
||||||
|
|
||||||
if (dev->of_node)
|
rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
|
||||||
of_id = of_alias_get_id(dev->of_node, "rtc");
|
if (!rtc)
|
||||||
else if (dev->parent && dev->parent->of_node)
|
return NULL;
|
||||||
of_id = of_alias_get_id(dev->parent->of_node, "rtc");
|
|
||||||
|
|
||||||
if (of_id >= 0) {
|
|
||||||
id = ida_simple_get(&rtc_ida, of_id, of_id + 1,
|
|
||||||
GFP_KERNEL);
|
|
||||||
if (id < 0)
|
|
||||||
dev_warn(dev, "/aliases ID %d not available\n",
|
|
||||||
of_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id < 0) {
|
|
||||||
id = ida_simple_get(&rtc_ida, 0, 0, GFP_KERNEL);
|
|
||||||
if (id < 0) {
|
|
||||||
err = id;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
|
|
||||||
if (rtc == NULL) {
|
|
||||||
err = -ENOMEM;
|
|
||||||
goto exit_ida;
|
|
||||||
}
|
|
||||||
|
|
||||||
device_initialize(&rtc->dev);
|
device_initialize(&rtc->dev);
|
||||||
|
|
||||||
rtc->id = id;
|
|
||||||
rtc->ops = ops;
|
|
||||||
rtc->owner = owner;
|
|
||||||
rtc->irq_freq = 1;
|
rtc->irq_freq = 1;
|
||||||
rtc->max_user_freq = 64;
|
rtc->max_user_freq = 64;
|
||||||
rtc->dev.parent = dev;
|
|
||||||
rtc->dev.class = rtc_class;
|
rtc->dev.class = rtc_class;
|
||||||
rtc->dev.groups = rtc_get_dev_attribute_groups();
|
rtc->dev.groups = rtc_get_dev_attribute_groups();
|
||||||
rtc->dev.release = rtc_device_release;
|
rtc->dev.release = rtc_device_release;
|
||||||
@ -224,7 +184,64 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev,
|
|||||||
rtc->pie_timer.function = rtc_pie_update_irq;
|
rtc->pie_timer.function = rtc_pie_update_irq;
|
||||||
rtc->pie_enabled = 0;
|
rtc->pie_enabled = 0;
|
||||||
|
|
||||||
strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);
|
return rtc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rtc_device_get_id(struct device *dev)
|
||||||
|
{
|
||||||
|
int of_id = -1, id = -1;
|
||||||
|
|
||||||
|
if (dev->of_node)
|
||||||
|
of_id = of_alias_get_id(dev->of_node, "rtc");
|
||||||
|
else if (dev->parent && dev->parent->of_node)
|
||||||
|
of_id = of_alias_get_id(dev->parent->of_node, "rtc");
|
||||||
|
|
||||||
|
if (of_id >= 0) {
|
||||||
|
id = ida_simple_get(&rtc_ida, of_id, of_id + 1, GFP_KERNEL);
|
||||||
|
if (id < 0)
|
||||||
|
dev_warn(dev, "/aliases ID %d not available\n", of_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id < 0)
|
||||||
|
id = ida_simple_get(&rtc_ida, 0, 0, GFP_KERNEL);
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rtc_device_register - register w/ RTC class
|
||||||
|
* @dev: the device to register
|
||||||
|
*
|
||||||
|
* rtc_device_unregister() must be called when the class device is no
|
||||||
|
* longer needed.
|
||||||
|
*
|
||||||
|
* Returns the pointer to the new struct class device.
|
||||||
|
*/
|
||||||
|
struct rtc_device *rtc_device_register(const char *name, struct device *dev,
|
||||||
|
const struct rtc_class_ops *ops,
|
||||||
|
struct module *owner)
|
||||||
|
{
|
||||||
|
struct rtc_device *rtc;
|
||||||
|
struct rtc_wkalrm alrm;
|
||||||
|
int id, err;
|
||||||
|
|
||||||
|
id = rtc_device_get_id(dev);
|
||||||
|
if (id < 0) {
|
||||||
|
err = id;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
rtc = rtc_allocate_device();
|
||||||
|
if (!rtc) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto exit_ida;
|
||||||
|
}
|
||||||
|
|
||||||
|
rtc->id = id;
|
||||||
|
rtc->ops = ops;
|
||||||
|
rtc->owner = owner;
|
||||||
|
rtc->dev.parent = dev;
|
||||||
|
|
||||||
dev_set_name(&rtc->dev, "rtc%d", id);
|
dev_set_name(&rtc->dev, "rtc%d", id);
|
||||||
|
|
||||||
/* Check to see if there is an ALARM already set in hw */
|
/* Check to see if there is an ALARM already set in hw */
|
||||||
@ -238,20 +255,20 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev,
|
|||||||
err = cdev_device_add(&rtc->char_dev, &rtc->dev);
|
err = cdev_device_add(&rtc->char_dev, &rtc->dev);
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_warn(&rtc->dev, "%s: failed to add char device %d:%d\n",
|
dev_warn(&rtc->dev, "%s: failed to add char device %d:%d\n",
|
||||||
rtc->name, MAJOR(rtc->dev.devt), rtc->id);
|
name, MAJOR(rtc->dev.devt), rtc->id);
|
||||||
|
|
||||||
/* This will free both memory and the ID */
|
/* This will free both memory and the ID */
|
||||||
put_device(&rtc->dev);
|
put_device(&rtc->dev);
|
||||||
goto exit;
|
goto exit;
|
||||||
} else {
|
} else {
|
||||||
dev_dbg(&rtc->dev, "%s: dev (%d:%d)\n", rtc->name,
|
dev_dbg(&rtc->dev, "%s: dev (%d:%d)\n", name,
|
||||||
MAJOR(rtc->dev.devt), rtc->id);
|
MAJOR(rtc->dev.devt), rtc->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
rtc_proc_add_device(rtc);
|
rtc_proc_add_device(rtc);
|
||||||
|
|
||||||
dev_info(dev, "rtc core: registered %s as %s\n",
|
dev_info(dev, "rtc core: registered %s as %s\n",
|
||||||
rtc->name, dev_name(&rtc->dev));
|
name, dev_name(&rtc->dev));
|
||||||
|
|
||||||
return rtc;
|
return rtc;
|
||||||
|
|
||||||
@ -273,6 +290,8 @@ EXPORT_SYMBOL_GPL(rtc_device_register);
|
|||||||
*/
|
*/
|
||||||
void rtc_device_unregister(struct rtc_device *rtc)
|
void rtc_device_unregister(struct rtc_device *rtc)
|
||||||
{
|
{
|
||||||
|
rtc_nvmem_unregister(rtc);
|
||||||
|
|
||||||
mutex_lock(&rtc->ops_lock);
|
mutex_lock(&rtc->ops_lock);
|
||||||
/*
|
/*
|
||||||
* Remove innards of this RTC, then disable it, before
|
* Remove innards of this RTC, then disable it, before
|
||||||
@ -356,6 +375,91 @@ void devm_rtc_device_unregister(struct device *dev, struct rtc_device *rtc)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(devm_rtc_device_unregister);
|
EXPORT_SYMBOL_GPL(devm_rtc_device_unregister);
|
||||||
|
|
||||||
|
static void devm_rtc_release_device(struct device *dev, void *res)
|
||||||
|
{
|
||||||
|
struct rtc_device *rtc = *(struct rtc_device **)res;
|
||||||
|
|
||||||
|
if (rtc->registered)
|
||||||
|
rtc_device_unregister(rtc);
|
||||||
|
else
|
||||||
|
put_device(&rtc->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct rtc_device *devm_rtc_allocate_device(struct device *dev)
|
||||||
|
{
|
||||||
|
struct rtc_device **ptr, *rtc;
|
||||||
|
int id, err;
|
||||||
|
|
||||||
|
id = rtc_device_get_id(dev);
|
||||||
|
if (id < 0)
|
||||||
|
return ERR_PTR(id);
|
||||||
|
|
||||||
|
ptr = devres_alloc(devm_rtc_release_device, sizeof(*ptr), GFP_KERNEL);
|
||||||
|
if (!ptr) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto exit_ida;
|
||||||
|
}
|
||||||
|
|
||||||
|
rtc = rtc_allocate_device();
|
||||||
|
if (!rtc) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto exit_devres;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ptr = rtc;
|
||||||
|
devres_add(dev, ptr);
|
||||||
|
|
||||||
|
rtc->id = id;
|
||||||
|
rtc->dev.parent = dev;
|
||||||
|
dev_set_name(&rtc->dev, "rtc%d", id);
|
||||||
|
|
||||||
|
return rtc;
|
||||||
|
|
||||||
|
exit_devres:
|
||||||
|
devres_free(ptr);
|
||||||
|
exit_ida:
|
||||||
|
ida_simple_remove(&rtc_ida, id);
|
||||||
|
return ERR_PTR(err);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(devm_rtc_allocate_device);
|
||||||
|
|
||||||
|
int __rtc_register_device(struct module *owner, struct rtc_device *rtc)
|
||||||
|
{
|
||||||
|
struct rtc_wkalrm alrm;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!rtc->ops)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
rtc->owner = owner;
|
||||||
|
|
||||||
|
/* Check to see if there is an ALARM already set in hw */
|
||||||
|
err = __rtc_read_alarm(rtc, &alrm);
|
||||||
|
if (!err && !rtc_valid_tm(&alrm.time))
|
||||||
|
rtc_initialize_alarm(rtc, &alrm);
|
||||||
|
|
||||||
|
rtc_dev_prepare(rtc);
|
||||||
|
|
||||||
|
err = cdev_device_add(&rtc->char_dev, &rtc->dev);
|
||||||
|
if (err)
|
||||||
|
dev_warn(rtc->dev.parent, "failed to add char device %d:%d\n",
|
||||||
|
MAJOR(rtc->dev.devt), rtc->id);
|
||||||
|
else
|
||||||
|
dev_dbg(rtc->dev.parent, "char device (%d:%d)\n",
|
||||||
|
MAJOR(rtc->dev.devt), rtc->id);
|
||||||
|
|
||||||
|
rtc_proc_add_device(rtc);
|
||||||
|
|
||||||
|
rtc_nvmem_register(rtc);
|
||||||
|
|
||||||
|
rtc->registered = true;
|
||||||
|
dev_info(rtc->dev.parent, "registered as %s\n",
|
||||||
|
dev_name(&rtc->dev));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(__rtc_register_device);
|
||||||
|
|
||||||
static int __init rtc_init(void)
|
static int __init rtc_init(void)
|
||||||
{
|
{
|
||||||
rtc_class = class_create(THIS_MODULE, "rtc");
|
rtc_class = class_create(THIS_MODULE, "rtc");
|
||||||
|
@ -227,6 +227,13 @@ int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
|
|||||||
missing = year;
|
missing = year;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Can't proceed if alarm is still invalid after replacing
|
||||||
|
* missing fields.
|
||||||
|
*/
|
||||||
|
err = rtc_valid_tm(&alarm->time);
|
||||||
|
if (err)
|
||||||
|
goto done;
|
||||||
|
|
||||||
/* with luck, no rollover is needed */
|
/* with luck, no rollover is needed */
|
||||||
t_now = rtc_tm_to_time64(&now);
|
t_now = rtc_tm_to_time64(&now);
|
||||||
t_alm = rtc_tm_to_time64(&alarm->time);
|
t_alm = rtc_tm_to_time64(&alarm->time);
|
||||||
@ -278,9 +285,9 @@ int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
|
|||||||
dev_warn(&rtc->dev, "alarm rollover not handled\n");
|
dev_warn(&rtc->dev, "alarm rollover not handled\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
|
||||||
err = rtc_valid_tm(&alarm->time);
|
err = rtc_valid_tm(&alarm->time);
|
||||||
|
|
||||||
|
done:
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_warn(&rtc->dev, "invalid alarm value: %d-%d-%d %d:%d:%d\n",
|
dev_warn(&rtc->dev, "invalid alarm value: %d-%d-%d %d:%d:%d\n",
|
||||||
alarm->time.tm_year + 1900, alarm->time.tm_mon + 1,
|
alarm->time.tm_year + 1900, alarm->time.tm_mon + 1,
|
||||||
|
113
drivers/rtc/nvmem.c
Normal file
113
drivers/rtc/nvmem.c
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* RTC subsystem, nvmem interface
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Alexandre Belloni
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/nvmem-consumer.h>
|
||||||
|
#include <linux/rtc.h>
|
||||||
|
#include <linux/sysfs.h>
|
||||||
|
|
||||||
|
#include "rtc-core.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Deprecated ABI compatibility, this should be removed at some point
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const char nvram_warning[] = "Deprecated ABI, please use nvmem";
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
rtc_nvram_read(struct file *filp, struct kobject *kobj,
|
||||||
|
struct bin_attribute *attr,
|
||||||
|
char *buf, loff_t off, size_t count)
|
||||||
|
{
|
||||||
|
struct rtc_device *rtc = attr->private;
|
||||||
|
|
||||||
|
dev_warn_once(kobj_to_dev(kobj), nvram_warning);
|
||||||
|
|
||||||
|
return nvmem_device_read(rtc->nvmem, off, count, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
rtc_nvram_write(struct file *filp, struct kobject *kobj,
|
||||||
|
struct bin_attribute *attr,
|
||||||
|
char *buf, loff_t off, size_t count)
|
||||||
|
{
|
||||||
|
struct rtc_device *rtc = attr->private;
|
||||||
|
|
||||||
|
dev_warn_once(kobj_to_dev(kobj), nvram_warning);
|
||||||
|
|
||||||
|
return nvmem_device_write(rtc->nvmem, off, count, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rtc_nvram_register(struct rtc_device *rtc)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
rtc->nvram = devm_kzalloc(rtc->dev.parent,
|
||||||
|
sizeof(struct bin_attribute),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!rtc->nvram)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
rtc->nvram->attr.name = "nvram";
|
||||||
|
rtc->nvram->attr.mode = 0644;
|
||||||
|
rtc->nvram->private = rtc;
|
||||||
|
|
||||||
|
sysfs_bin_attr_init(rtc->nvram);
|
||||||
|
|
||||||
|
rtc->nvram->read = rtc_nvram_read;
|
||||||
|
rtc->nvram->write = rtc_nvram_write;
|
||||||
|
rtc->nvram->size = rtc->nvmem_config->size;
|
||||||
|
|
||||||
|
err = sysfs_create_bin_file(&rtc->dev.parent->kobj,
|
||||||
|
rtc->nvram);
|
||||||
|
if (err) {
|
||||||
|
devm_kfree(rtc->dev.parent, rtc->nvram);
|
||||||
|
rtc->nvram = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rtc_nvram_unregister(struct rtc_device *rtc)
|
||||||
|
{
|
||||||
|
sysfs_remove_bin_file(&rtc->dev.parent->kobj, rtc->nvram);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* New ABI, uses nvmem
|
||||||
|
*/
|
||||||
|
void rtc_nvmem_register(struct rtc_device *rtc)
|
||||||
|
{
|
||||||
|
if (!rtc->nvmem_config)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rtc->nvmem_config->dev = &rtc->dev;
|
||||||
|
rtc->nvmem_config->owner = rtc->owner;
|
||||||
|
rtc->nvmem = nvmem_register(rtc->nvmem_config);
|
||||||
|
if (IS_ERR_OR_NULL(rtc->nvmem))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Register the old ABI */
|
||||||
|
if (rtc->nvram_old_abi)
|
||||||
|
rtc_nvram_register(rtc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rtc_nvmem_unregister(struct rtc_device *rtc)
|
||||||
|
{
|
||||||
|
if (IS_ERR_OR_NULL(rtc->nvmem))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* unregister the old ABI */
|
||||||
|
if (rtc->nvram)
|
||||||
|
rtc_nvram_unregister(rtc);
|
||||||
|
|
||||||
|
nvmem_unregister(rtc->nvmem);
|
||||||
|
}
|
@ -409,6 +409,11 @@ static int __init at91_rtc_probe(struct platform_device *pdev)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rtc = devm_rtc_allocate_device(&pdev->dev);
|
||||||
|
if (IS_ERR(rtc))
|
||||||
|
return PTR_ERR(rtc);
|
||||||
|
platform_set_drvdata(pdev, rtc);
|
||||||
|
|
||||||
sclk = devm_clk_get(&pdev->dev, NULL);
|
sclk = devm_clk_get(&pdev->dev, NULL);
|
||||||
if (IS_ERR(sclk))
|
if (IS_ERR(sclk))
|
||||||
return PTR_ERR(sclk);
|
return PTR_ERR(sclk);
|
||||||
@ -441,13 +446,10 @@ static int __init at91_rtc_probe(struct platform_device *pdev)
|
|||||||
if (!device_can_wakeup(&pdev->dev))
|
if (!device_can_wakeup(&pdev->dev))
|
||||||
device_init_wakeup(&pdev->dev, 1);
|
device_init_wakeup(&pdev->dev, 1);
|
||||||
|
|
||||||
rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
|
rtc->ops = &at91_rtc_ops;
|
||||||
&at91_rtc_ops, THIS_MODULE);
|
ret = rtc_register_device(rtc);
|
||||||
if (IS_ERR(rtc)) {
|
if (ret)
|
||||||
ret = PTR_ERR(rtc);
|
|
||||||
goto err_clk;
|
goto err_clk;
|
||||||
}
|
|
||||||
platform_set_drvdata(pdev, rtc);
|
|
||||||
|
|
||||||
/* enable SECEV interrupt in order to initialize at91_rtc_upd_rdy
|
/* enable SECEV interrupt in order to initialize at91_rtc_upd_rdy
|
||||||
* completion.
|
* completion.
|
||||||
|
330
drivers/rtc/rtc-brcmstb-waketimer.c
Normal file
330
drivers/rtc/rtc-brcmstb-waketimer.c
Normal file
@ -0,0 +1,330 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2014-2017 Broadcom
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/irqreturn.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm.h>
|
||||||
|
#include <linux/pm_wakeup.h>
|
||||||
|
#include <linux/reboot.h>
|
||||||
|
#include <linux/rtc.h>
|
||||||
|
#include <linux/stat.h>
|
||||||
|
#include <linux/suspend.h>
|
||||||
|
|
||||||
|
struct brcmstb_waketmr {
|
||||||
|
struct rtc_device *rtc;
|
||||||
|
struct device *dev;
|
||||||
|
void __iomem *base;
|
||||||
|
int irq;
|
||||||
|
struct notifier_block reboot_notifier;
|
||||||
|
struct clk *clk;
|
||||||
|
u32 rate;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define BRCMSTB_WKTMR_EVENT 0x00
|
||||||
|
#define BRCMSTB_WKTMR_COUNTER 0x04
|
||||||
|
#define BRCMSTB_WKTMR_ALARM 0x08
|
||||||
|
#define BRCMSTB_WKTMR_PRESCALER 0x0C
|
||||||
|
#define BRCMSTB_WKTMR_PRESCALER_VAL 0x10
|
||||||
|
|
||||||
|
#define BRCMSTB_WKTMR_DEFAULT_FREQ 27000000
|
||||||
|
|
||||||
|
static inline void brcmstb_waketmr_clear_alarm(struct brcmstb_waketmr *timer)
|
||||||
|
{
|
||||||
|
writel_relaxed(1, timer->base + BRCMSTB_WKTMR_EVENT);
|
||||||
|
(void)readl_relaxed(timer->base + BRCMSTB_WKTMR_EVENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void brcmstb_waketmr_set_alarm(struct brcmstb_waketmr *timer,
|
||||||
|
unsigned int secs)
|
||||||
|
{
|
||||||
|
brcmstb_waketmr_clear_alarm(timer);
|
||||||
|
|
||||||
|
writel_relaxed(secs + 1, timer->base + BRCMSTB_WKTMR_ALARM);
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t brcmstb_waketmr_irq(int irq, void *data)
|
||||||
|
{
|
||||||
|
struct brcmstb_waketmr *timer = data;
|
||||||
|
|
||||||
|
pm_wakeup_event(timer->dev, 0);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wktmr_time {
|
||||||
|
u32 sec;
|
||||||
|
u32 pre;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void wktmr_read(struct brcmstb_waketmr *timer,
|
||||||
|
struct wktmr_time *t)
|
||||||
|
{
|
||||||
|
u32 tmp;
|
||||||
|
|
||||||
|
do {
|
||||||
|
t->sec = readl_relaxed(timer->base + BRCMSTB_WKTMR_COUNTER);
|
||||||
|
tmp = readl_relaxed(timer->base + BRCMSTB_WKTMR_PRESCALER_VAL);
|
||||||
|
} while (tmp >= timer->rate);
|
||||||
|
|
||||||
|
t->pre = timer->rate - tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int brcmstb_waketmr_prepare_suspend(struct brcmstb_waketmr *timer)
|
||||||
|
{
|
||||||
|
struct device *dev = timer->dev;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (device_may_wakeup(dev)) {
|
||||||
|
ret = enable_irq_wake(timer->irq);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to enable wake-up interrupt\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If enabled as a wakeup-source, arm the timer when powering off */
|
||||||
|
static int brcmstb_waketmr_reboot(struct notifier_block *nb,
|
||||||
|
unsigned long action, void *data)
|
||||||
|
{
|
||||||
|
struct brcmstb_waketmr *timer;
|
||||||
|
|
||||||
|
timer = container_of(nb, struct brcmstb_waketmr, reboot_notifier);
|
||||||
|
|
||||||
|
/* Set timer for cold boot */
|
||||||
|
if (action == SYS_POWER_OFF)
|
||||||
|
brcmstb_waketmr_prepare_suspend(timer);
|
||||||
|
|
||||||
|
return NOTIFY_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int brcmstb_waketmr_gettime(struct device *dev,
|
||||||
|
struct rtc_time *tm)
|
||||||
|
{
|
||||||
|
struct brcmstb_waketmr *timer = dev_get_drvdata(dev);
|
||||||
|
struct wktmr_time now;
|
||||||
|
|
||||||
|
wktmr_read(timer, &now);
|
||||||
|
|
||||||
|
rtc_time_to_tm(now.sec, tm);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int brcmstb_waketmr_settime(struct device *dev,
|
||||||
|
struct rtc_time *tm)
|
||||||
|
{
|
||||||
|
struct brcmstb_waketmr *timer = dev_get_drvdata(dev);
|
||||||
|
time64_t sec;
|
||||||
|
|
||||||
|
sec = rtc_tm_to_time64(tm);
|
||||||
|
|
||||||
|
if (sec > U32_MAX || sec < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
writel_relaxed(sec, timer->base + BRCMSTB_WKTMR_COUNTER);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int brcmstb_waketmr_getalarm(struct device *dev,
|
||||||
|
struct rtc_wkalrm *alarm)
|
||||||
|
{
|
||||||
|
struct brcmstb_waketmr *timer = dev_get_drvdata(dev);
|
||||||
|
time64_t sec;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
sec = readl_relaxed(timer->base + BRCMSTB_WKTMR_ALARM);
|
||||||
|
if (sec != 0) {
|
||||||
|
/* Alarm is enabled */
|
||||||
|
alarm->enabled = 1;
|
||||||
|
rtc_time64_to_tm(sec, &alarm->time);
|
||||||
|
}
|
||||||
|
|
||||||
|
reg = readl_relaxed(timer->base + BRCMSTB_WKTMR_EVENT);
|
||||||
|
alarm->pending = !!(reg & 1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int brcmstb_waketmr_setalarm(struct device *dev,
|
||||||
|
struct rtc_wkalrm *alarm)
|
||||||
|
{
|
||||||
|
struct brcmstb_waketmr *timer = dev_get_drvdata(dev);
|
||||||
|
time64_t sec;
|
||||||
|
|
||||||
|
if (alarm->enabled)
|
||||||
|
sec = rtc_tm_to_time64(&alarm->time);
|
||||||
|
else
|
||||||
|
sec = 0;
|
||||||
|
|
||||||
|
if (sec > U32_MAX || sec < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
brcmstb_waketmr_set_alarm(timer, sec);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Does not do much but keep the RTC class happy. We always support
|
||||||
|
* alarms.
|
||||||
|
*/
|
||||||
|
static int brcmstb_waketmr_alarm_enable(struct device *dev,
|
||||||
|
unsigned int enabled)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct rtc_class_ops brcmstb_waketmr_ops = {
|
||||||
|
.read_time = brcmstb_waketmr_gettime,
|
||||||
|
.set_time = brcmstb_waketmr_settime,
|
||||||
|
.read_alarm = brcmstb_waketmr_getalarm,
|
||||||
|
.set_alarm = brcmstb_waketmr_setalarm,
|
||||||
|
.alarm_irq_enable = brcmstb_waketmr_alarm_enable,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int brcmstb_waketmr_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct brcmstb_waketmr *timer;
|
||||||
|
struct resource *res;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
timer = devm_kzalloc(dev, sizeof(*timer), GFP_KERNEL);
|
||||||
|
if (!timer)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, timer);
|
||||||
|
timer->dev = dev;
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
timer->base = devm_ioremap_resource(dev, res);
|
||||||
|
if (IS_ERR(timer->base))
|
||||||
|
return PTR_ERR(timer->base);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set wakeup capability before requesting wakeup interrupt, so we can
|
||||||
|
* process boot-time "wakeups" (e.g., from S5 soft-off)
|
||||||
|
*/
|
||||||
|
device_set_wakeup_capable(dev, true);
|
||||||
|
device_wakeup_enable(dev);
|
||||||
|
|
||||||
|
timer->irq = platform_get_irq(pdev, 0);
|
||||||
|
if (timer->irq < 0)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
timer->clk = devm_clk_get(dev, NULL);
|
||||||
|
if (!IS_ERR(timer->clk)) {
|
||||||
|
ret = clk_prepare_enable(timer->clk);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
timer->rate = clk_get_rate(timer->clk);
|
||||||
|
if (!timer->rate)
|
||||||
|
timer->rate = BRCMSTB_WKTMR_DEFAULT_FREQ;
|
||||||
|
} else {
|
||||||
|
timer->rate = BRCMSTB_WKTMR_DEFAULT_FREQ;
|
||||||
|
timer->clk = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = devm_request_irq(dev, timer->irq, brcmstb_waketmr_irq, 0,
|
||||||
|
"brcmstb-waketimer", timer);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
timer->reboot_notifier.notifier_call = brcmstb_waketmr_reboot;
|
||||||
|
register_reboot_notifier(&timer->reboot_notifier);
|
||||||
|
|
||||||
|
timer->rtc = rtc_device_register("brcmstb-waketmr", dev,
|
||||||
|
&brcmstb_waketmr_ops, THIS_MODULE);
|
||||||
|
if (IS_ERR(timer->rtc)) {
|
||||||
|
dev_err(dev, "unable to register device\n");
|
||||||
|
unregister_reboot_notifier(&timer->reboot_notifier);
|
||||||
|
return PTR_ERR(timer->rtc);
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_info(dev, "registered, with irq %d\n", timer->irq);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int brcmstb_waketmr_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct brcmstb_waketmr *timer = dev_get_drvdata(&pdev->dev);
|
||||||
|
|
||||||
|
unregister_reboot_notifier(&timer->reboot_notifier);
|
||||||
|
rtc_device_unregister(timer->rtc);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
static int brcmstb_waketmr_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct brcmstb_waketmr *timer = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
return brcmstb_waketmr_prepare_suspend(timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int brcmstb_waketmr_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct brcmstb_waketmr *timer = dev_get_drvdata(dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!device_may_wakeup(dev))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = disable_irq_wake(timer->irq);
|
||||||
|
|
||||||
|
brcmstb_waketmr_clear_alarm(timer);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_PM_SLEEP */
|
||||||
|
|
||||||
|
static SIMPLE_DEV_PM_OPS(brcmstb_waketmr_pm_ops,
|
||||||
|
brcmstb_waketmr_suspend, brcmstb_waketmr_resume);
|
||||||
|
|
||||||
|
static const struct of_device_id brcmstb_waketmr_of_match[] = {
|
||||||
|
{ .compatible = "brcm,brcmstb-waketimer" },
|
||||||
|
{ /* sentinel */ },
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct platform_driver brcmstb_waketmr_driver = {
|
||||||
|
.probe = brcmstb_waketmr_probe,
|
||||||
|
.remove = brcmstb_waketmr_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "brcmstb-waketimer",
|
||||||
|
.pm = &brcmstb_waketmr_pm_ops,
|
||||||
|
.of_match_table = of_match_ptr(brcmstb_waketmr_of_match),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
module_platform_driver(brcmstb_waketmr_driver);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
MODULE_AUTHOR("Brian Norris");
|
||||||
|
MODULE_AUTHOR("Markus Mayer");
|
||||||
|
MODULE_DESCRIPTION("Wake-up timer driver for STB chips");
|
@ -45,3 +45,11 @@ static inline const struct attribute_group **rtc_get_dev_attribute_groups(void)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_RTC_NVMEM
|
||||||
|
void rtc_nvmem_register(struct rtc_device *rtc);
|
||||||
|
void rtc_nvmem_unregister(struct rtc_device *rtc);
|
||||||
|
#else
|
||||||
|
static inline void rtc_nvmem_register(struct rtc_device *rtc) {}
|
||||||
|
static inline void rtc_nvmem_unregister(struct rtc_device *rtc) {}
|
||||||
|
#endif
|
||||||
|
@ -464,7 +464,7 @@ void rtc_dev_prepare(struct rtc_device *rtc)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (rtc->id >= RTC_DEV_MAX) {
|
if (rtc->id >= RTC_DEV_MAX) {
|
||||||
dev_dbg(&rtc->dev, "%s: too many RTC devices\n", rtc->name);
|
dev_dbg(&rtc->dev, "too many RTC devices\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -22,6 +22,7 @@
|
|||||||
#include <linux/bcd.h>
|
#include <linux/bcd.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/hwmon.h>
|
||||||
|
|
||||||
#define DS3232_REG_SECONDS 0x00
|
#define DS3232_REG_SECONDS 0x00
|
||||||
#define DS3232_REG_MINUTES 0x01
|
#define DS3232_REG_MINUTES 0x01
|
||||||
@ -46,6 +47,8 @@
|
|||||||
# define DS3232_REG_SR_A2F 0x02
|
# define DS3232_REG_SR_A2F 0x02
|
||||||
# define DS3232_REG_SR_A1F 0x01
|
# define DS3232_REG_SR_A1F 0x01
|
||||||
|
|
||||||
|
#define DS3232_REG_TEMPERATURE 0x11
|
||||||
|
|
||||||
struct ds3232 {
|
struct ds3232 {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
@ -275,6 +278,120 @@ static int ds3232_update_alarm(struct device *dev, unsigned int enabled)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Temperature sensor support for ds3232/ds3234 devices.
|
||||||
|
* A user-initiated temperature conversion is not started by this function,
|
||||||
|
* so the temperature is updated once every 64 seconds.
|
||||||
|
*/
|
||||||
|
static int ds3232_hwmon_read_temp(struct device *dev, long int *mC)
|
||||||
|
{
|
||||||
|
struct ds3232 *ds3232 = dev_get_drvdata(dev);
|
||||||
|
u8 temp_buf[2];
|
||||||
|
s16 temp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = regmap_bulk_read(ds3232->regmap, DS3232_REG_TEMPERATURE, temp_buf,
|
||||||
|
sizeof(temp_buf));
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Temperature is represented as a 10-bit code with a resolution of
|
||||||
|
* 0.25 degree celsius and encoded in two's complement format.
|
||||||
|
*/
|
||||||
|
temp = (temp_buf[0] << 8) | temp_buf[1];
|
||||||
|
temp >>= 6;
|
||||||
|
*mC = temp * 250;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static umode_t ds3232_hwmon_is_visible(const void *data,
|
||||||
|
enum hwmon_sensor_types type,
|
||||||
|
u32 attr, int channel)
|
||||||
|
{
|
||||||
|
if (type != hwmon_temp)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
switch (attr) {
|
||||||
|
case hwmon_temp_input:
|
||||||
|
return 0444;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ds3232_hwmon_read(struct device *dev,
|
||||||
|
enum hwmon_sensor_types type,
|
||||||
|
u32 attr, int channel, long *temp)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
switch (attr) {
|
||||||
|
case hwmon_temp_input:
|
||||||
|
err = ds3232_hwmon_read_temp(dev, temp);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
err = -EOPNOTSUPP;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 ds3232_hwmon_chip_config[] = {
|
||||||
|
HWMON_C_REGISTER_TZ,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct hwmon_channel_info ds3232_hwmon_chip = {
|
||||||
|
.type = hwmon_chip,
|
||||||
|
.config = ds3232_hwmon_chip_config,
|
||||||
|
};
|
||||||
|
|
||||||
|
static u32 ds3232_hwmon_temp_config[] = {
|
||||||
|
HWMON_T_INPUT,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct hwmon_channel_info ds3232_hwmon_temp = {
|
||||||
|
.type = hwmon_temp,
|
||||||
|
.config = ds3232_hwmon_temp_config,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct hwmon_channel_info *ds3232_hwmon_info[] = {
|
||||||
|
&ds3232_hwmon_chip,
|
||||||
|
&ds3232_hwmon_temp,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct hwmon_ops ds3232_hwmon_hwmon_ops = {
|
||||||
|
.is_visible = ds3232_hwmon_is_visible,
|
||||||
|
.read = ds3232_hwmon_read,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct hwmon_chip_info ds3232_hwmon_chip_info = {
|
||||||
|
.ops = &ds3232_hwmon_hwmon_ops,
|
||||||
|
.info = ds3232_hwmon_info,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void ds3232_hwmon_register(struct device *dev, const char *name)
|
||||||
|
{
|
||||||
|
struct ds3232 *ds3232 = dev_get_drvdata(dev);
|
||||||
|
struct device *hwmon_dev;
|
||||||
|
|
||||||
|
if (!IS_ENABLED(CONFIG_RTC_DRV_DS3232_HWMON))
|
||||||
|
return;
|
||||||
|
|
||||||
|
hwmon_dev = devm_hwmon_device_register_with_info(dev, name, ds3232,
|
||||||
|
&ds3232_hwmon_chip_info,
|
||||||
|
NULL);
|
||||||
|
if (IS_ERR(hwmon_dev)) {
|
||||||
|
dev_err(dev, "unable to register hwmon device %ld\n",
|
||||||
|
PTR_ERR(hwmon_dev));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int ds3232_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
static int ds3232_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||||
{
|
{
|
||||||
struct ds3232 *ds3232 = dev_get_drvdata(dev);
|
struct ds3232 *ds3232 = dev_get_drvdata(dev);
|
||||||
@ -366,6 +483,8 @@ static int ds3232_probe(struct device *dev, struct regmap *regmap, int irq,
|
|||||||
if (ds3232->irq > 0)
|
if (ds3232->irq > 0)
|
||||||
device_init_wakeup(dev, 1);
|
device_init_wakeup(dev, 1);
|
||||||
|
|
||||||
|
ds3232_hwmon_register(dev, name);
|
||||||
|
|
||||||
ds3232->rtc = devm_rtc_device_register(dev, name, &ds3232_rtc_ops,
|
ds3232->rtc = devm_rtc_device_register(dev, name, &ds3232_rtc_ops,
|
||||||
THIS_MODULE);
|
THIS_MODULE);
|
||||||
if (IS_ERR(ds3232->rtc))
|
if (IS_ERR(ds3232->rtc))
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Gemini OnChip RTC
|
* Faraday Technology FTRTC010 driver
|
||||||
*
|
*
|
||||||
* Copyright (C) 2009 Janos Laube <janos.dev@gmail.com>
|
* Copyright (C) 2009 Janos Laube <janos.dev@gmail.com>
|
||||||
*
|
*
|
||||||
@ -26,33 +26,36 @@
|
|||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
|
||||||
#define DRV_NAME "rtc-gemini"
|
#define DRV_NAME "rtc-ftrtc010"
|
||||||
|
|
||||||
MODULE_AUTHOR("Hans Ulli Kroll <ulli.kroll@googlemail.com>");
|
MODULE_AUTHOR("Hans Ulli Kroll <ulli.kroll@googlemail.com>");
|
||||||
MODULE_DESCRIPTION("RTC driver for Gemini SoC");
|
MODULE_DESCRIPTION("RTC driver for Gemini SoC");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_ALIAS("platform:" DRV_NAME);
|
MODULE_ALIAS("platform:" DRV_NAME);
|
||||||
|
|
||||||
struct gemini_rtc {
|
struct ftrtc010_rtc {
|
||||||
struct rtc_device *rtc_dev;
|
struct rtc_device *rtc_dev;
|
||||||
void __iomem *rtc_base;
|
void __iomem *rtc_base;
|
||||||
int rtc_irq;
|
int rtc_irq;
|
||||||
|
struct clk *pclk;
|
||||||
|
struct clk *extclk;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum gemini_rtc_offsets {
|
enum ftrtc010_rtc_offsets {
|
||||||
GEMINI_RTC_SECOND = 0x00,
|
FTRTC010_RTC_SECOND = 0x00,
|
||||||
GEMINI_RTC_MINUTE = 0x04,
|
FTRTC010_RTC_MINUTE = 0x04,
|
||||||
GEMINI_RTC_HOUR = 0x08,
|
FTRTC010_RTC_HOUR = 0x08,
|
||||||
GEMINI_RTC_DAYS = 0x0C,
|
FTRTC010_RTC_DAYS = 0x0C,
|
||||||
GEMINI_RTC_ALARM_SECOND = 0x10,
|
FTRTC010_RTC_ALARM_SECOND = 0x10,
|
||||||
GEMINI_RTC_ALARM_MINUTE = 0x14,
|
FTRTC010_RTC_ALARM_MINUTE = 0x14,
|
||||||
GEMINI_RTC_ALARM_HOUR = 0x18,
|
FTRTC010_RTC_ALARM_HOUR = 0x18,
|
||||||
GEMINI_RTC_RECORD = 0x1C,
|
FTRTC010_RTC_RECORD = 0x1C,
|
||||||
GEMINI_RTC_CR = 0x20
|
FTRTC010_RTC_CR = 0x20,
|
||||||
};
|
};
|
||||||
|
|
||||||
static irqreturn_t gemini_rtc_interrupt(int irq, void *dev)
|
static irqreturn_t ftrtc010_rtc_interrupt(int irq, void *dev)
|
||||||
{
|
{
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
@ -66,18 +69,18 @@ static irqreturn_t gemini_rtc_interrupt(int irq, void *dev)
|
|||||||
* the same thing, without the rtc-lib.c calls.
|
* the same thing, without the rtc-lib.c calls.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int gemini_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
static int ftrtc010_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||||
{
|
{
|
||||||
struct gemini_rtc *rtc = dev_get_drvdata(dev);
|
struct ftrtc010_rtc *rtc = dev_get_drvdata(dev);
|
||||||
|
|
||||||
unsigned int days, hour, min, sec;
|
unsigned int days, hour, min, sec;
|
||||||
unsigned long offset, time;
|
unsigned long offset, time;
|
||||||
|
|
||||||
sec = readl(rtc->rtc_base + GEMINI_RTC_SECOND);
|
sec = readl(rtc->rtc_base + FTRTC010_RTC_SECOND);
|
||||||
min = readl(rtc->rtc_base + GEMINI_RTC_MINUTE);
|
min = readl(rtc->rtc_base + FTRTC010_RTC_MINUTE);
|
||||||
hour = readl(rtc->rtc_base + GEMINI_RTC_HOUR);
|
hour = readl(rtc->rtc_base + FTRTC010_RTC_HOUR);
|
||||||
days = readl(rtc->rtc_base + GEMINI_RTC_DAYS);
|
days = readl(rtc->rtc_base + FTRTC010_RTC_DAYS);
|
||||||
offset = readl(rtc->rtc_base + GEMINI_RTC_RECORD);
|
offset = readl(rtc->rtc_base + FTRTC010_RTC_RECORD);
|
||||||
|
|
||||||
time = offset + days * 86400 + hour * 3600 + min * 60 + sec;
|
time = offset + days * 86400 + hour * 3600 + min * 60 + sec;
|
||||||
|
|
||||||
@ -86,9 +89,9 @@ static int gemini_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gemini_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
static int ftrtc010_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||||
{
|
{
|
||||||
struct gemini_rtc *rtc = dev_get_drvdata(dev);
|
struct ftrtc010_rtc *rtc = dev_get_drvdata(dev);
|
||||||
unsigned int sec, min, hour, day;
|
unsigned int sec, min, hour, day;
|
||||||
unsigned long offset, time;
|
unsigned long offset, time;
|
||||||
|
|
||||||
@ -97,27 +100,27 @@ static int gemini_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
|||||||
|
|
||||||
rtc_tm_to_time(tm, &time);
|
rtc_tm_to_time(tm, &time);
|
||||||
|
|
||||||
sec = readl(rtc->rtc_base + GEMINI_RTC_SECOND);
|
sec = readl(rtc->rtc_base + FTRTC010_RTC_SECOND);
|
||||||
min = readl(rtc->rtc_base + GEMINI_RTC_MINUTE);
|
min = readl(rtc->rtc_base + FTRTC010_RTC_MINUTE);
|
||||||
hour = readl(rtc->rtc_base + GEMINI_RTC_HOUR);
|
hour = readl(rtc->rtc_base + FTRTC010_RTC_HOUR);
|
||||||
day = readl(rtc->rtc_base + GEMINI_RTC_DAYS);
|
day = readl(rtc->rtc_base + FTRTC010_RTC_DAYS);
|
||||||
|
|
||||||
offset = time - (day * 86400 + hour * 3600 + min * 60 + sec);
|
offset = time - (day * 86400 + hour * 3600 + min * 60 + sec);
|
||||||
|
|
||||||
writel(offset, rtc->rtc_base + GEMINI_RTC_RECORD);
|
writel(offset, rtc->rtc_base + FTRTC010_RTC_RECORD);
|
||||||
writel(0x01, rtc->rtc_base + GEMINI_RTC_CR);
|
writel(0x01, rtc->rtc_base + FTRTC010_RTC_CR);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct rtc_class_ops gemini_rtc_ops = {
|
static const struct rtc_class_ops ftrtc010_rtc_ops = {
|
||||||
.read_time = gemini_rtc_read_time,
|
.read_time = ftrtc010_rtc_read_time,
|
||||||
.set_time = gemini_rtc_set_time,
|
.set_time = ftrtc010_rtc_set_time,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int gemini_rtc_probe(struct platform_device *pdev)
|
static int ftrtc010_rtc_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct gemini_rtc *rtc;
|
struct ftrtc010_rtc *rtc;
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
int ret;
|
int ret;
|
||||||
@ -127,6 +130,27 @@ static int gemini_rtc_probe(struct platform_device *pdev)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
platform_set_drvdata(pdev, rtc);
|
platform_set_drvdata(pdev, rtc);
|
||||||
|
|
||||||
|
rtc->pclk = devm_clk_get(dev, "PCLK");
|
||||||
|
if (IS_ERR(rtc->pclk)) {
|
||||||
|
dev_err(dev, "could not get PCLK\n");
|
||||||
|
} else {
|
||||||
|
ret = clk_prepare_enable(rtc->pclk);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to enable PCLK\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rtc->extclk = devm_clk_get(dev, "EXTCLK");
|
||||||
|
if (IS_ERR(rtc->extclk)) {
|
||||||
|
dev_err(dev, "could not get EXTCLK\n");
|
||||||
|
} else {
|
||||||
|
ret = clk_prepare_enable(rtc->extclk);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to enable EXTCLK\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||||
if (!res)
|
if (!res)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
@ -142,38 +166,43 @@ static int gemini_rtc_probe(struct platform_device *pdev)
|
|||||||
if (!rtc->rtc_base)
|
if (!rtc->rtc_base)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
ret = devm_request_irq(dev, rtc->rtc_irq, gemini_rtc_interrupt,
|
ret = devm_request_irq(dev, rtc->rtc_irq, ftrtc010_rtc_interrupt,
|
||||||
IRQF_SHARED, pdev->name, dev);
|
IRQF_SHARED, pdev->name, dev);
|
||||||
if (unlikely(ret))
|
if (unlikely(ret))
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
rtc->rtc_dev = rtc_device_register(pdev->name, dev,
|
rtc->rtc_dev = rtc_device_register(pdev->name, dev,
|
||||||
&gemini_rtc_ops, THIS_MODULE);
|
&ftrtc010_rtc_ops, THIS_MODULE);
|
||||||
return PTR_ERR_OR_ZERO(rtc->rtc_dev);
|
return PTR_ERR_OR_ZERO(rtc->rtc_dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gemini_rtc_remove(struct platform_device *pdev)
|
static int ftrtc010_rtc_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct gemini_rtc *rtc = platform_get_drvdata(pdev);
|
struct ftrtc010_rtc *rtc = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
if (!IS_ERR(rtc->extclk))
|
||||||
|
clk_disable_unprepare(rtc->extclk);
|
||||||
|
if (!IS_ERR(rtc->pclk))
|
||||||
|
clk_disable_unprepare(rtc->pclk);
|
||||||
rtc_device_unregister(rtc->rtc_dev);
|
rtc_device_unregister(rtc->rtc_dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct of_device_id gemini_rtc_dt_match[] = {
|
static const struct of_device_id ftrtc010_rtc_dt_match[] = {
|
||||||
{ .compatible = "cortina,gemini-rtc" },
|
{ .compatible = "cortina,gemini-rtc" },
|
||||||
|
{ .compatible = "faraday,ftrtc010" },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, gemini_rtc_dt_match);
|
MODULE_DEVICE_TABLE(of, ftrtc010_rtc_dt_match);
|
||||||
|
|
||||||
static struct platform_driver gemini_rtc_driver = {
|
static struct platform_driver ftrtc010_rtc_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRV_NAME,
|
.name = DRV_NAME,
|
||||||
.of_match_table = gemini_rtc_dt_match,
|
.of_match_table = ftrtc010_rtc_dt_match,
|
||||||
},
|
},
|
||||||
.probe = gemini_rtc_probe,
|
.probe = ftrtc010_rtc_probe,
|
||||||
.remove = gemini_rtc_remove,
|
.remove = ftrtc010_rtc_remove,
|
||||||
};
|
};
|
||||||
|
|
||||||
module_platform_driver_probe(gemini_rtc_driver, gemini_rtc_probe);
|
module_platform_driver_probe(ftrtc010_rtc_driver, ftrtc010_rtc_probe);
|
@ -16,6 +16,7 @@
|
|||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/bcd.h>
|
#include <linux/bcd.h>
|
||||||
|
#include <linux/clk-provider.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
@ -53,6 +54,8 @@
|
|||||||
#define M41T80_ALARM_REG_SIZE \
|
#define M41T80_ALARM_REG_SIZE \
|
||||||
(M41T80_REG_ALARM_SEC + 1 - M41T80_REG_ALARM_MON)
|
(M41T80_REG_ALARM_SEC + 1 - M41T80_REG_ALARM_MON)
|
||||||
|
|
||||||
|
#define M41T80_SQW_MAX_FREQ 32768
|
||||||
|
|
||||||
#define M41T80_SEC_ST BIT(7) /* ST: Stop Bit */
|
#define M41T80_SEC_ST BIT(7) /* ST: Stop Bit */
|
||||||
#define M41T80_ALMON_AFE BIT(7) /* AFE: AF Enable Bit */
|
#define M41T80_ALMON_AFE BIT(7) /* AFE: AF Enable Bit */
|
||||||
#define M41T80_ALMON_SQWE BIT(6) /* SQWE: SQW Enable Bit */
|
#define M41T80_ALMON_SQWE BIT(6) /* SQWE: SQW Enable Bit */
|
||||||
@ -147,7 +150,11 @@ MODULE_DEVICE_TABLE(of, m41t80_of_match);
|
|||||||
|
|
||||||
struct m41t80_data {
|
struct m41t80_data {
|
||||||
unsigned long features;
|
unsigned long features;
|
||||||
|
struct i2c_client *client;
|
||||||
struct rtc_device *rtc;
|
struct rtc_device *rtc;
|
||||||
|
#ifdef CONFIG_COMMON_CLK
|
||||||
|
struct clk_hw sqw;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static irqreturn_t m41t80_handle_irq(int irq, void *dev_id)
|
static irqreturn_t m41t80_handle_irq(int irq, void *dev_id)
|
||||||
@ -227,6 +234,7 @@ static int m41t80_get_datetime(struct i2c_client *client,
|
|||||||
/* Sets the given date and time to the real time clock. */
|
/* Sets the given date and time to the real time clock. */
|
||||||
static int m41t80_set_datetime(struct i2c_client *client, struct rtc_time *tm)
|
static int m41t80_set_datetime(struct i2c_client *client, struct rtc_time *tm)
|
||||||
{
|
{
|
||||||
|
struct m41t80_data *clientdata = i2c_get_clientdata(client);
|
||||||
unsigned char buf[8];
|
unsigned char buf[8];
|
||||||
int err, flags;
|
int err, flags;
|
||||||
|
|
||||||
@ -242,6 +250,17 @@ static int m41t80_set_datetime(struct i2c_client *client, struct rtc_time *tm)
|
|||||||
buf[M41T80_REG_YEAR] = bin2bcd(tm->tm_year - 100);
|
buf[M41T80_REG_YEAR] = bin2bcd(tm->tm_year - 100);
|
||||||
buf[M41T80_REG_WDAY] = tm->tm_wday;
|
buf[M41T80_REG_WDAY] = tm->tm_wday;
|
||||||
|
|
||||||
|
/* If the square wave output is controlled in the weekday register */
|
||||||
|
if (clientdata->features & M41T80_FEATURE_SQ_ALT) {
|
||||||
|
int val;
|
||||||
|
|
||||||
|
val = i2c_smbus_read_byte_data(client, M41T80_REG_WDAY);
|
||||||
|
if (val < 0)
|
||||||
|
return val;
|
||||||
|
|
||||||
|
buf[M41T80_REG_WDAY] |= (val & 0xf0);
|
||||||
|
}
|
||||||
|
|
||||||
err = i2c_smbus_write_i2c_block_data(client, M41T80_REG_SSEC,
|
err = i2c_smbus_write_i2c_block_data(client, M41T80_REG_SSEC,
|
||||||
sizeof(buf), buf);
|
sizeof(buf), buf);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
@ -332,6 +351,9 @@ static int m41t80_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Keep SQWE bit value */
|
||||||
|
alarmvals[0] |= (ret & M41T80_ALMON_SQWE);
|
||||||
|
|
||||||
ret = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS);
|
ret = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
@ -431,96 +453,8 @@ static ssize_t flags_show(struct device *dev,
|
|||||||
}
|
}
|
||||||
static DEVICE_ATTR_RO(flags);
|
static DEVICE_ATTR_RO(flags);
|
||||||
|
|
||||||
static ssize_t sqwfreq_show(struct device *dev,
|
|
||||||
struct device_attribute *attr, char *buf)
|
|
||||||
{
|
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
|
||||||
struct m41t80_data *clientdata = i2c_get_clientdata(client);
|
|
||||||
int val, reg_sqw;
|
|
||||||
|
|
||||||
if (!(clientdata->features & M41T80_FEATURE_SQ))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
reg_sqw = M41T80_REG_SQW;
|
|
||||||
if (clientdata->features & M41T80_FEATURE_SQ_ALT)
|
|
||||||
reg_sqw = M41T80_REG_WDAY;
|
|
||||||
val = i2c_smbus_read_byte_data(client, reg_sqw);
|
|
||||||
if (val < 0)
|
|
||||||
return val;
|
|
||||||
val = (val >> 4) & 0xf;
|
|
||||||
switch (val) {
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
val = 32768;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
val = 32768 >> val;
|
|
||||||
}
|
|
||||||
return sprintf(buf, "%d\n", val);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t sqwfreq_store(struct device *dev,
|
|
||||||
struct device_attribute *attr,
|
|
||||||
const char *buf, size_t count)
|
|
||||||
{
|
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
|
||||||
struct m41t80_data *clientdata = i2c_get_clientdata(client);
|
|
||||||
int almon, sqw, reg_sqw, rc;
|
|
||||||
unsigned long val;
|
|
||||||
|
|
||||||
rc = kstrtoul(buf, 0, &val);
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
if (!(clientdata->features & M41T80_FEATURE_SQ))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (val) {
|
|
||||||
if (!is_power_of_2(val))
|
|
||||||
return -EINVAL;
|
|
||||||
val = ilog2(val);
|
|
||||||
if (val == 15)
|
|
||||||
val = 1;
|
|
||||||
else if (val < 14)
|
|
||||||
val = 15 - val;
|
|
||||||
else
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
/* disable SQW, set SQW frequency & re-enable */
|
|
||||||
almon = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
|
|
||||||
if (almon < 0)
|
|
||||||
return almon;
|
|
||||||
reg_sqw = M41T80_REG_SQW;
|
|
||||||
if (clientdata->features & M41T80_FEATURE_SQ_ALT)
|
|
||||||
reg_sqw = M41T80_REG_WDAY;
|
|
||||||
sqw = i2c_smbus_read_byte_data(client, reg_sqw);
|
|
||||||
if (sqw < 0)
|
|
||||||
return sqw;
|
|
||||||
sqw = (sqw & 0x0f) | (val << 4);
|
|
||||||
|
|
||||||
rc = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
|
|
||||||
almon & ~M41T80_ALMON_SQWE);
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
if (val) {
|
|
||||||
rc = i2c_smbus_write_byte_data(client, reg_sqw, sqw);
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
rc = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
|
|
||||||
almon | M41T80_ALMON_SQWE);
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
static DEVICE_ATTR_RW(sqwfreq);
|
|
||||||
|
|
||||||
static struct attribute *attrs[] = {
|
static struct attribute *attrs[] = {
|
||||||
&dev_attr_flags.attr,
|
&dev_attr_flags.attr,
|
||||||
&dev_attr_sqwfreq.attr,
|
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -528,6 +462,166 @@ static struct attribute_group attr_group = {
|
|||||||
.attrs = attrs,
|
.attrs = attrs,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMMON_CLK
|
||||||
|
#define sqw_to_m41t80_data(_hw) container_of(_hw, struct m41t80_data, sqw)
|
||||||
|
|
||||||
|
static unsigned long m41t80_sqw_recalc_rate(struct clk_hw *hw,
|
||||||
|
unsigned long parent_rate)
|
||||||
|
{
|
||||||
|
struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw);
|
||||||
|
struct i2c_client *client = m41t80->client;
|
||||||
|
int reg_sqw = (m41t80->features & M41T80_FEATURE_SQ_ALT) ?
|
||||||
|
M41T80_REG_WDAY : M41T80_REG_SQW;
|
||||||
|
int ret = i2c_smbus_read_byte_data(client, reg_sqw);
|
||||||
|
unsigned long val = M41T80_SQW_MAX_FREQ;
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret >>= 4;
|
||||||
|
if (ret == 0)
|
||||||
|
val = 0;
|
||||||
|
else if (ret > 1)
|
||||||
|
val = val / (1 << ret);
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long m41t80_sqw_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||||
|
unsigned long *prate)
|
||||||
|
{
|
||||||
|
int i, freq = M41T80_SQW_MAX_FREQ;
|
||||||
|
|
||||||
|
if (freq <= rate)
|
||||||
|
return freq;
|
||||||
|
|
||||||
|
for (i = 2; i <= ilog2(M41T80_SQW_MAX_FREQ); i++) {
|
||||||
|
freq /= 1 << i;
|
||||||
|
if (freq <= rate)
|
||||||
|
return freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int m41t80_sqw_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||||
|
unsigned long parent_rate)
|
||||||
|
{
|
||||||
|
struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw);
|
||||||
|
struct i2c_client *client = m41t80->client;
|
||||||
|
int reg_sqw = (m41t80->features & M41T80_FEATURE_SQ_ALT) ?
|
||||||
|
M41T80_REG_WDAY : M41T80_REG_SQW;
|
||||||
|
int reg, ret, val = 0;
|
||||||
|
|
||||||
|
if (rate) {
|
||||||
|
if (!is_power_of_2(rate))
|
||||||
|
return -EINVAL;
|
||||||
|
val = ilog2(rate);
|
||||||
|
if (val == ilog2(M41T80_SQW_MAX_FREQ))
|
||||||
|
val = 1;
|
||||||
|
else if (val < (ilog2(M41T80_SQW_MAX_FREQ) - 1))
|
||||||
|
val = ilog2(M41T80_SQW_MAX_FREQ) - val;
|
||||||
|
else
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
reg = i2c_smbus_read_byte_data(client, reg_sqw);
|
||||||
|
if (reg < 0)
|
||||||
|
return reg;
|
||||||
|
|
||||||
|
reg = (reg & 0x0f) | (val << 4);
|
||||||
|
|
||||||
|
ret = i2c_smbus_write_byte_data(client, reg_sqw, reg);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int m41t80_sqw_control(struct clk_hw *hw, bool enable)
|
||||||
|
{
|
||||||
|
struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw);
|
||||||
|
struct i2c_client *client = m41t80->client;
|
||||||
|
int ret = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (enable)
|
||||||
|
ret |= M41T80_ALMON_SQWE;
|
||||||
|
else
|
||||||
|
ret &= ~M41T80_ALMON_SQWE;
|
||||||
|
|
||||||
|
return i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int m41t80_sqw_prepare(struct clk_hw *hw)
|
||||||
|
{
|
||||||
|
return m41t80_sqw_control(hw, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void m41t80_sqw_unprepare(struct clk_hw *hw)
|
||||||
|
{
|
||||||
|
m41t80_sqw_control(hw, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int m41t80_sqw_is_prepared(struct clk_hw *hw)
|
||||||
|
{
|
||||||
|
struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw);
|
||||||
|
struct i2c_client *client = m41t80->client;
|
||||||
|
int ret = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return !!(ret & M41T80_ALMON_SQWE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct clk_ops m41t80_sqw_ops = {
|
||||||
|
.prepare = m41t80_sqw_prepare,
|
||||||
|
.unprepare = m41t80_sqw_unprepare,
|
||||||
|
.is_prepared = m41t80_sqw_is_prepared,
|
||||||
|
.recalc_rate = m41t80_sqw_recalc_rate,
|
||||||
|
.round_rate = m41t80_sqw_round_rate,
|
||||||
|
.set_rate = m41t80_sqw_set_rate,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct clk *m41t80_sqw_register_clk(struct m41t80_data *m41t80)
|
||||||
|
{
|
||||||
|
struct i2c_client *client = m41t80->client;
|
||||||
|
struct device_node *node = client->dev.of_node;
|
||||||
|
struct clk *clk;
|
||||||
|
struct clk_init_data init;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* First disable the clock */
|
||||||
|
ret = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
|
||||||
|
if (ret < 0)
|
||||||
|
return ERR_PTR(ret);
|
||||||
|
ret = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
|
||||||
|
ret & ~(M41T80_ALMON_SQWE));
|
||||||
|
if (ret < 0)
|
||||||
|
return ERR_PTR(ret);
|
||||||
|
|
||||||
|
init.name = "m41t80-sqw";
|
||||||
|
init.ops = &m41t80_sqw_ops;
|
||||||
|
init.flags = 0;
|
||||||
|
init.parent_names = NULL;
|
||||||
|
init.num_parents = 0;
|
||||||
|
m41t80->sqw.init = &init;
|
||||||
|
|
||||||
|
/* optional override of the clockname */
|
||||||
|
of_property_read_string(node, "clock-output-names", &init.name);
|
||||||
|
|
||||||
|
/* register the clock */
|
||||||
|
clk = clk_register(&client->dev, &m41t80->sqw);
|
||||||
|
if (!IS_ERR(clk))
|
||||||
|
of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||||
|
|
||||||
|
return clk;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_RTC_DRV_M41T80_WDT
|
#ifdef CONFIG_RTC_DRV_M41T80_WDT
|
||||||
/*
|
/*
|
||||||
*****************************************************************************
|
*****************************************************************************
|
||||||
@ -845,6 +939,7 @@ static int m41t80_probe(struct i2c_client *client,
|
|||||||
if (!m41t80_data)
|
if (!m41t80_data)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
m41t80_data->client = client;
|
||||||
if (client->dev.of_node)
|
if (client->dev.of_node)
|
||||||
m41t80_data->features = (unsigned long)
|
m41t80_data->features = (unsigned long)
|
||||||
of_device_get_match_data(&client->dev);
|
of_device_get_match_data(&client->dev);
|
||||||
@ -936,6 +1031,10 @@ static int m41t80_probe(struct i2c_client *client,
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_COMMON_CLK
|
||||||
|
if (m41t80_data->features & M41T80_FEATURE_SQ)
|
||||||
|
m41t80_sqw_register_clk(m41t80_data);
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -43,17 +43,6 @@
|
|||||||
|
|
||||||
#define MAX_PIE_NUM 9
|
#define MAX_PIE_NUM 9
|
||||||
#define MAX_PIE_FREQ 512
|
#define MAX_PIE_FREQ 512
|
||||||
static const u32 PIE_BIT_DEF[MAX_PIE_NUM][2] = {
|
|
||||||
{ 2, RTC_2HZ_BIT },
|
|
||||||
{ 4, RTC_SAM0_BIT },
|
|
||||||
{ 8, RTC_SAM1_BIT },
|
|
||||||
{ 16, RTC_SAM2_BIT },
|
|
||||||
{ 32, RTC_SAM3_BIT },
|
|
||||||
{ 64, RTC_SAM4_BIT },
|
|
||||||
{ 128, RTC_SAM5_BIT },
|
|
||||||
{ 256, RTC_SAM6_BIT },
|
|
||||||
{ MAX_PIE_FREQ, RTC_SAM7_BIT },
|
|
||||||
};
|
|
||||||
|
|
||||||
#define MXC_RTC_TIME 0
|
#define MXC_RTC_TIME 0
|
||||||
#define MXC_RTC_ALARM 1
|
#define MXC_RTC_ALARM 1
|
||||||
|
@ -93,7 +93,7 @@ static int *check_rtc_access_enable(struct nuc900_rtc *nuc900_rtc)
|
|||||||
__raw_writel(AERPOWERON, nuc900_rtc->rtc_reg + REG_RTC_AER);
|
__raw_writel(AERPOWERON, nuc900_rtc->rtc_reg + REG_RTC_AER);
|
||||||
|
|
||||||
while (!(__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB)
|
while (!(__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB)
|
||||||
&& timeout--)
|
&& --timeout)
|
||||||
mdelay(1);
|
mdelay(1);
|
||||||
|
|
||||||
if (!timeout)
|
if (!timeout)
|
||||||
|
@ -142,6 +142,16 @@ static int opal_get_tpo_time(struct device *dev, struct rtc_wkalrm *alarm)
|
|||||||
|
|
||||||
y_m_d = be32_to_cpu(__y_m_d);
|
y_m_d = be32_to_cpu(__y_m_d);
|
||||||
h_m_s_ms = ((u64)be32_to_cpu(__h_m) << 32);
|
h_m_s_ms = ((u64)be32_to_cpu(__h_m) << 32);
|
||||||
|
|
||||||
|
/* check if no alarm is set */
|
||||||
|
if (y_m_d == 0 && h_m_s_ms == 0) {
|
||||||
|
pr_debug("No alarm is set\n");
|
||||||
|
rc = -ENOENT;
|
||||||
|
goto exit;
|
||||||
|
} else {
|
||||||
|
pr_debug("Alarm set to %x %llx\n", y_m_d, h_m_s_ms);
|
||||||
|
}
|
||||||
|
|
||||||
opal_to_tm(y_m_d, h_m_s_ms, &alarm->time);
|
opal_to_tm(y_m_d, h_m_s_ms, &alarm->time);
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
@ -157,7 +167,14 @@ static int opal_set_tpo_time(struct device *dev, struct rtc_wkalrm *alarm)
|
|||||||
u32 y_m_d = 0;
|
u32 y_m_d = 0;
|
||||||
int token, rc;
|
int token, rc;
|
||||||
|
|
||||||
tm_to_opal(&alarm->time, &y_m_d, &h_m_s_ms);
|
/* if alarm is enabled */
|
||||||
|
if (alarm->enabled) {
|
||||||
|
tm_to_opal(&alarm->time, &y_m_d, &h_m_s_ms);
|
||||||
|
pr_debug("Alarm set to %x %llx\n", y_m_d, h_m_s_ms);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
pr_debug("Alarm getting disabled\n");
|
||||||
|
}
|
||||||
|
|
||||||
token = opal_async_get_token_interruptible();
|
token = opal_async_get_token_interruptible();
|
||||||
if (token < 0) {
|
if (token < 0) {
|
||||||
@ -190,6 +207,18 @@ exit:
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int opal_tpo_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||||
|
{
|
||||||
|
struct rtc_wkalrm alarm = { .enabled = 0 };
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TPO is automatically enabled when opal_set_tpo_time() is called with
|
||||||
|
* non-zero rtc-time. We only handle disable case which needs to be
|
||||||
|
* explicitly told to opal.
|
||||||
|
*/
|
||||||
|
return enabled ? 0 : opal_set_tpo_time(dev, &alarm);
|
||||||
|
}
|
||||||
|
|
||||||
static struct rtc_class_ops opal_rtc_ops = {
|
static struct rtc_class_ops opal_rtc_ops = {
|
||||||
.read_time = opal_get_rtc_time,
|
.read_time = opal_get_rtc_time,
|
||||||
.set_time = opal_set_rtc_time,
|
.set_time = opal_set_rtc_time,
|
||||||
@ -205,6 +234,7 @@ static int opal_rtc_probe(struct platform_device *pdev)
|
|||||||
device_set_wakeup_capable(&pdev->dev, true);
|
device_set_wakeup_capable(&pdev->dev, true);
|
||||||
opal_rtc_ops.read_alarm = opal_get_tpo_time;
|
opal_rtc_ops.read_alarm = opal_get_tpo_time;
|
||||||
opal_rtc_ops.set_alarm = opal_set_tpo_time;
|
opal_rtc_ops.set_alarm = opal_set_tpo_time;
|
||||||
|
opal_rtc_ops.alarm_irq_enable = opal_tpo_alarm_irq_enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
rtc = devm_rtc_device_register(&pdev->dev, DRVNAME, &opal_rtc_ops,
|
rtc = devm_rtc_device_register(&pdev->dev, DRVNAME, &opal_rtc_ops,
|
||||||
|
@ -606,7 +606,7 @@ static int pcf8563_probe(struct i2c_client *client,
|
|||||||
err = devm_request_threaded_irq(&client->dev, client->irq,
|
err = devm_request_threaded_irq(&client->dev, client->irq,
|
||||||
NULL, pcf8563_irq,
|
NULL, pcf8563_irq,
|
||||||
IRQF_SHARED|IRQF_ONESHOT|IRQF_TRIGGER_FALLING,
|
IRQF_SHARED|IRQF_ONESHOT|IRQF_TRIGGER_FALLING,
|
||||||
pcf8563->rtc->name, client);
|
pcf8563_driver.driver.name, client);
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(&client->dev, "unable to request IRQ %d\n",
|
dev_err(&client->dev, "unable to request IRQ %d\n",
|
||||||
client->irq);
|
client->irq);
|
||||||
|
@ -68,6 +68,7 @@ struct rv8803_data {
|
|||||||
struct mutex flags_lock;
|
struct mutex flags_lock;
|
||||||
u8 ctrl;
|
u8 ctrl;
|
||||||
enum rv8803_type type;
|
enum rv8803_type type;
|
||||||
|
struct nvmem_config nvmem_cfg;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int rv8803_read_reg(const struct i2c_client *client, u8 reg)
|
static int rv8803_read_reg(const struct i2c_client *client, u8 reg)
|
||||||
@ -460,48 +461,32 @@ static int rv8803_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t rv8803_nvram_write(struct file *filp, struct kobject *kobj,
|
static int rv8803_nvram_write(void *priv, unsigned int offset, void *val,
|
||||||
struct bin_attribute *attr,
|
size_t bytes)
|
||||||
char *buf, loff_t off, size_t count)
|
|
||||||
{
|
{
|
||||||
struct device *dev = kobj_to_dev(kobj);
|
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = rv8803_write_reg(client, RV8803_RAM, buf[0]);
|
ret = rv8803_write_reg(priv, RV8803_RAM, *(u8 *)val);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
return 1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t rv8803_nvram_read(struct file *filp, struct kobject *kobj,
|
static int rv8803_nvram_read(void *priv, unsigned int offset,
|
||||||
struct bin_attribute *attr,
|
void *val, size_t bytes)
|
||||||
char *buf, loff_t off, size_t count)
|
|
||||||
{
|
{
|
||||||
struct device *dev = kobj_to_dev(kobj);
|
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = rv8803_read_reg(client, RV8803_RAM);
|
ret = rv8803_read_reg(priv, RV8803_RAM);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
buf[0] = ret;
|
*(u8 *)val = ret;
|
||||||
|
|
||||||
return 1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct bin_attribute rv8803_nvram_attr = {
|
|
||||||
.attr = {
|
|
||||||
.name = "nvram",
|
|
||||||
.mode = S_IRUGO | S_IWUSR,
|
|
||||||
},
|
|
||||||
.size = 1,
|
|
||||||
.read = rv8803_nvram_read,
|
|
||||||
.write = rv8803_nvram_write,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct rtc_class_ops rv8803_rtc_ops = {
|
static struct rtc_class_ops rv8803_rtc_ops = {
|
||||||
.read_time = rv8803_get_time,
|
.read_time = rv8803_get_time,
|
||||||
.set_time = rv8803_set_time,
|
.set_time = rv8803_set_time,
|
||||||
@ -577,6 +562,11 @@ static int rv8803_probe(struct i2c_client *client,
|
|||||||
if (flags & RV8803_FLAG_AF)
|
if (flags & RV8803_FLAG_AF)
|
||||||
dev_warn(&client->dev, "An alarm maybe have been missed.\n");
|
dev_warn(&client->dev, "An alarm maybe have been missed.\n");
|
||||||
|
|
||||||
|
rv8803->rtc = devm_rtc_allocate_device(&client->dev);
|
||||||
|
if (IS_ERR(rv8803->rtc)) {
|
||||||
|
return PTR_ERR(rv8803->rtc);
|
||||||
|
}
|
||||||
|
|
||||||
if (client->irq > 0) {
|
if (client->irq > 0) {
|
||||||
err = devm_request_threaded_irq(&client->dev, client->irq,
|
err = devm_request_threaded_irq(&client->dev, client->irq,
|
||||||
NULL, rv8803_handle_irq,
|
NULL, rv8803_handle_irq,
|
||||||
@ -592,12 +582,20 @@ static int rv8803_probe(struct i2c_client *client,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rv8803->rtc = devm_rtc_device_register(&client->dev, client->name,
|
rv8803->nvmem_cfg.name = "rv8803_nvram",
|
||||||
&rv8803_rtc_ops, THIS_MODULE);
|
rv8803->nvmem_cfg.word_size = 1,
|
||||||
if (IS_ERR(rv8803->rtc)) {
|
rv8803->nvmem_cfg.stride = 1,
|
||||||
dev_err(&client->dev, "unable to register the class device\n");
|
rv8803->nvmem_cfg.size = 1,
|
||||||
return PTR_ERR(rv8803->rtc);
|
rv8803->nvmem_cfg.reg_read = rv8803_nvram_read,
|
||||||
}
|
rv8803->nvmem_cfg.reg_write = rv8803_nvram_write,
|
||||||
|
rv8803->nvmem_cfg.priv = client;
|
||||||
|
|
||||||
|
rv8803->rtc->ops = &rv8803_rtc_ops;
|
||||||
|
rv8803->rtc->nvmem_config = &rv8803->nvmem_cfg;
|
||||||
|
rv8803->rtc->nvram_old_abi = true;
|
||||||
|
err = rtc_register_device(rv8803->rtc);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
err = rv8803_write_reg(rv8803->client, RV8803_EXT, RV8803_EXT_WADA);
|
err = rv8803_write_reg(rv8803->client, RV8803_EXT, RV8803_EXT_WADA);
|
||||||
if (err)
|
if (err)
|
||||||
@ -609,22 +607,11 @@ static int rv8803_probe(struct i2c_client *client,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = device_create_bin_file(&client->dev, &rv8803_nvram_attr);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
rv8803->rtc->max_user_freq = 1;
|
rv8803->rtc->max_user_freq = 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rv8803_remove(struct i2c_client *client)
|
|
||||||
{
|
|
||||||
device_remove_bin_file(&client->dev, &rv8803_nvram_attr);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct i2c_device_id rv8803_id[] = {
|
static const struct i2c_device_id rv8803_id[] = {
|
||||||
{ "rv8803", rv_8803 },
|
{ "rv8803", rv_8803 },
|
||||||
{ "rx8900", rx_8900 },
|
{ "rx8900", rx_8900 },
|
||||||
@ -651,7 +638,6 @@ static struct i2c_driver rv8803_driver = {
|
|||||||
.of_match_table = of_match_ptr(rv8803_of_match),
|
.of_match_table = of_match_ptr(rv8803_of_match),
|
||||||
},
|
},
|
||||||
.probe = rv8803_probe,
|
.probe = rv8803_probe,
|
||||||
.remove = rv8803_remove,
|
|
||||||
.id_table = rv8803_id,
|
.id_table = rv8803_id,
|
||||||
};
|
};
|
||||||
module_i2c_driver(rv8803_driver);
|
module_i2c_driver(rv8803_driver);
|
||||||
|
@ -41,7 +41,7 @@ struct s3c_rtc {
|
|||||||
struct clk *rtc_src_clk;
|
struct clk *rtc_src_clk;
|
||||||
bool clk_disabled;
|
bool clk_disabled;
|
||||||
|
|
||||||
struct s3c_rtc_data *data;
|
const struct s3c_rtc_data *data;
|
||||||
|
|
||||||
int irq_alarm;
|
int irq_alarm;
|
||||||
int irq_tick;
|
int irq_tick;
|
||||||
@ -49,7 +49,8 @@ struct s3c_rtc {
|
|||||||
spinlock_t pie_lock;
|
spinlock_t pie_lock;
|
||||||
spinlock_t alarm_clk_lock;
|
spinlock_t alarm_clk_lock;
|
||||||
|
|
||||||
int ticnt_save, ticnt_en_save;
|
int ticnt_save;
|
||||||
|
int ticnt_en_save;
|
||||||
bool wake_en;
|
bool wake_en;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -67,18 +68,32 @@ struct s3c_rtc_data {
|
|||||||
void (*disable) (struct s3c_rtc *info);
|
void (*disable) (struct s3c_rtc *info);
|
||||||
};
|
};
|
||||||
|
|
||||||
static void s3c_rtc_enable_clk(struct s3c_rtc *info)
|
static int s3c_rtc_enable_clk(struct s3c_rtc *info)
|
||||||
{
|
{
|
||||||
unsigned long irq_flags;
|
unsigned long irq_flags;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
spin_lock_irqsave(&info->alarm_clk_lock, irq_flags);
|
spin_lock_irqsave(&info->alarm_clk_lock, irq_flags);
|
||||||
|
|
||||||
if (info->clk_disabled) {
|
if (info->clk_disabled) {
|
||||||
clk_enable(info->rtc_clk);
|
ret = clk_enable(info->rtc_clk);
|
||||||
if (info->data->needs_src_clk)
|
if (ret)
|
||||||
clk_enable(info->rtc_src_clk);
|
goto out;
|
||||||
|
|
||||||
|
if (info->data->needs_src_clk) {
|
||||||
|
ret = clk_enable(info->rtc_src_clk);
|
||||||
|
if (ret) {
|
||||||
|
clk_disable(info->rtc_clk);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
info->clk_disabled = false;
|
info->clk_disabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags);
|
spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void s3c_rtc_disable_clk(struct s3c_rtc *info)
|
static void s3c_rtc_disable_clk(struct s3c_rtc *info)
|
||||||
@ -121,10 +136,13 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
|
|||||||
{
|
{
|
||||||
struct s3c_rtc *info = dev_get_drvdata(dev);
|
struct s3c_rtc *info = dev_get_drvdata(dev);
|
||||||
unsigned int tmp;
|
unsigned int tmp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
dev_dbg(info->dev, "%s: aie=%d\n", __func__, enabled);
|
dev_dbg(info->dev, "%s: aie=%d\n", __func__, enabled);
|
||||||
|
|
||||||
s3c_rtc_enable_clk(info);
|
ret = s3c_rtc_enable_clk(info);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
tmp = readb(info->base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN;
|
tmp = readb(info->base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN;
|
||||||
|
|
||||||
@ -135,10 +153,13 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
|
|||||||
|
|
||||||
s3c_rtc_disable_clk(info);
|
s3c_rtc_disable_clk(info);
|
||||||
|
|
||||||
if (enabled)
|
if (enabled) {
|
||||||
s3c_rtc_enable_clk(info);
|
ret = s3c_rtc_enable_clk(info);
|
||||||
else
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
} else {
|
||||||
s3c_rtc_disable_clk(info);
|
s3c_rtc_disable_clk(info);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -146,10 +167,14 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
|
|||||||
/* Set RTC frequency */
|
/* Set RTC frequency */
|
||||||
static int s3c_rtc_setfreq(struct s3c_rtc *info, int freq)
|
static int s3c_rtc_setfreq(struct s3c_rtc *info, int freq)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (!is_power_of_2(freq))
|
if (!is_power_of_2(freq))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
s3c_rtc_enable_clk(info);
|
ret = s3c_rtc_enable_clk(info);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
spin_lock_irq(&info->pie_lock);
|
spin_lock_irq(&info->pie_lock);
|
||||||
|
|
||||||
if (info->data->set_freq)
|
if (info->data->set_freq)
|
||||||
@ -166,10 +191,13 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
|
|||||||
{
|
{
|
||||||
struct s3c_rtc *info = dev_get_drvdata(dev);
|
struct s3c_rtc *info = dev_get_drvdata(dev);
|
||||||
unsigned int have_retried = 0;
|
unsigned int have_retried = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
s3c_rtc_enable_clk(info);
|
ret = s3c_rtc_enable_clk(info);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
retry_get_time:
|
retry_get_time:
|
||||||
rtc_tm->tm_min = readb(info->base + S3C2410_RTCMIN);
|
rtc_tm->tm_min = readb(info->base + S3C2410_RTCMIN);
|
||||||
rtc_tm->tm_hour = readb(info->base + S3C2410_RTCHOUR);
|
rtc_tm->tm_hour = readb(info->base + S3C2410_RTCHOUR);
|
||||||
rtc_tm->tm_mday = readb(info->base + S3C2410_RTCDATE);
|
rtc_tm->tm_mday = readb(info->base + S3C2410_RTCDATE);
|
||||||
@ -199,8 +227,8 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
|
|||||||
rtc_tm->tm_year += 100;
|
rtc_tm->tm_year += 100;
|
||||||
|
|
||||||
dev_dbg(dev, "read time %04d.%02d.%02d %02d:%02d:%02d\n",
|
dev_dbg(dev, "read time %04d.%02d.%02d %02d:%02d:%02d\n",
|
||||||
1900 + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
|
1900 + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
|
||||||
rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
|
rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
|
||||||
|
|
||||||
rtc_tm->tm_mon -= 1;
|
rtc_tm->tm_mon -= 1;
|
||||||
|
|
||||||
@ -211,10 +239,11 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
|
|||||||
{
|
{
|
||||||
struct s3c_rtc *info = dev_get_drvdata(dev);
|
struct s3c_rtc *info = dev_get_drvdata(dev);
|
||||||
int year = tm->tm_year - 100;
|
int year = tm->tm_year - 100;
|
||||||
|
int ret;
|
||||||
|
|
||||||
dev_dbg(dev, "set time %04d.%02d.%02d %02d:%02d:%02d\n",
|
dev_dbg(dev, "set time %04d.%02d.%02d %02d:%02d:%02d\n",
|
||||||
1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
|
1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
|
||||||
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||||
|
|
||||||
/* we get around y2k by simply not supporting it */
|
/* we get around y2k by simply not supporting it */
|
||||||
|
|
||||||
@ -223,7 +252,9 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
s3c_rtc_enable_clk(info);
|
ret = s3c_rtc_enable_clk(info);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
writeb(bin2bcd(tm->tm_sec), info->base + S3C2410_RTCSEC);
|
writeb(bin2bcd(tm->tm_sec), info->base + S3C2410_RTCSEC);
|
||||||
writeb(bin2bcd(tm->tm_min), info->base + S3C2410_RTCMIN);
|
writeb(bin2bcd(tm->tm_min), info->base + S3C2410_RTCMIN);
|
||||||
@ -242,8 +273,11 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||||||
struct s3c_rtc *info = dev_get_drvdata(dev);
|
struct s3c_rtc *info = dev_get_drvdata(dev);
|
||||||
struct rtc_time *alm_tm = &alrm->time;
|
struct rtc_time *alm_tm = &alrm->time;
|
||||||
unsigned int alm_en;
|
unsigned int alm_en;
|
||||||
|
int ret;
|
||||||
|
|
||||||
s3c_rtc_enable_clk(info);
|
ret = s3c_rtc_enable_clk(info);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
alm_tm->tm_sec = readb(info->base + S3C2410_ALMSEC);
|
alm_tm->tm_sec = readb(info->base + S3C2410_ALMSEC);
|
||||||
alm_tm->tm_min = readb(info->base + S3C2410_ALMMIN);
|
alm_tm->tm_min = readb(info->base + S3C2410_ALMMIN);
|
||||||
@ -259,9 +293,9 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||||||
alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0;
|
alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0;
|
||||||
|
|
||||||
dev_dbg(dev, "read alarm %d, %04d.%02d.%02d %02d:%02d:%02d\n",
|
dev_dbg(dev, "read alarm %d, %04d.%02d.%02d %02d:%02d:%02d\n",
|
||||||
alm_en,
|
alm_en,
|
||||||
1900 + alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
|
1900 + alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
|
||||||
alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
|
alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
|
||||||
|
|
||||||
/* decode the alarm enable field */
|
/* decode the alarm enable field */
|
||||||
if (alm_en & S3C2410_RTCALM_SECEN)
|
if (alm_en & S3C2410_RTCALM_SECEN)
|
||||||
@ -292,14 +326,17 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||||||
struct s3c_rtc *info = dev_get_drvdata(dev);
|
struct s3c_rtc *info = dev_get_drvdata(dev);
|
||||||
struct rtc_time *tm = &alrm->time;
|
struct rtc_time *tm = &alrm->time;
|
||||||
unsigned int alrm_en;
|
unsigned int alrm_en;
|
||||||
|
int ret;
|
||||||
int year = tm->tm_year - 100;
|
int year = tm->tm_year - 100;
|
||||||
|
|
||||||
dev_dbg(dev, "s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n",
|
dev_dbg(dev, "s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n",
|
||||||
alrm->enabled,
|
alrm->enabled,
|
||||||
1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
|
1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
|
||||||
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||||
|
|
||||||
s3c_rtc_enable_clk(info);
|
ret = s3c_rtc_enable_clk(info);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
alrm_en = readb(info->base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN;
|
alrm_en = readb(info->base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN;
|
||||||
writeb(0x00, info->base + S3C2410_RTCALM);
|
writeb(0x00, info->base + S3C2410_RTCALM);
|
||||||
@ -348,8 +385,11 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||||||
static int s3c_rtc_proc(struct device *dev, struct seq_file *seq)
|
static int s3c_rtc_proc(struct device *dev, struct seq_file *seq)
|
||||||
{
|
{
|
||||||
struct s3c_rtc *info = dev_get_drvdata(dev);
|
struct s3c_rtc *info = dev_get_drvdata(dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
s3c_rtc_enable_clk(info);
|
ret = s3c_rtc_enable_clk(info);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
if (info->data->enable_tick)
|
if (info->data->enable_tick)
|
||||||
info->data->enable_tick(info, seq);
|
info->data->enable_tick(info, seq);
|
||||||
@ -378,8 +418,7 @@ static void s3c24xx_rtc_enable(struct s3c_rtc *info)
|
|||||||
dev_info(info->dev, "rtc disabled, re-enabling\n");
|
dev_info(info->dev, "rtc disabled, re-enabling\n");
|
||||||
|
|
||||||
tmp = readw(info->base + S3C2410_RTCCON);
|
tmp = readw(info->base + S3C2410_RTCCON);
|
||||||
writew(tmp | S3C2410_RTCCON_RTCEN,
|
writew(tmp | S3C2410_RTCCON_RTCEN, info->base + S3C2410_RTCCON);
|
||||||
info->base + S3C2410_RTCCON);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (con & S3C2410_RTCCON_CNTSEL) {
|
if (con & S3C2410_RTCCON_CNTSEL) {
|
||||||
@ -387,7 +426,7 @@ static void s3c24xx_rtc_enable(struct s3c_rtc *info)
|
|||||||
|
|
||||||
tmp = readw(info->base + S3C2410_RTCCON);
|
tmp = readw(info->base + S3C2410_RTCCON);
|
||||||
writew(tmp & ~S3C2410_RTCCON_CNTSEL,
|
writew(tmp & ~S3C2410_RTCCON_CNTSEL,
|
||||||
info->base + S3C2410_RTCCON);
|
info->base + S3C2410_RTCCON);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (con & S3C2410_RTCCON_CLKRST) {
|
if (con & S3C2410_RTCCON_CLKRST) {
|
||||||
@ -395,7 +434,7 @@ static void s3c24xx_rtc_enable(struct s3c_rtc *info)
|
|||||||
|
|
||||||
tmp = readw(info->base + S3C2410_RTCCON);
|
tmp = readw(info->base + S3C2410_RTCCON);
|
||||||
writew(tmp & ~S3C2410_RTCCON_CLKRST,
|
writew(tmp & ~S3C2410_RTCCON_CLKRST,
|
||||||
info->base + S3C2410_RTCCON);
|
info->base + S3C2410_RTCCON);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -437,12 +476,12 @@ static int s3c_rtc_remove(struct platform_device *pdev)
|
|||||||
|
|
||||||
static const struct of_device_id s3c_rtc_dt_match[];
|
static const struct of_device_id s3c_rtc_dt_match[];
|
||||||
|
|
||||||
static struct s3c_rtc_data *s3c_rtc_get_data(struct platform_device *pdev)
|
static const struct s3c_rtc_data *s3c_rtc_get_data(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
const struct of_device_id *match;
|
const struct of_device_id *match;
|
||||||
|
|
||||||
match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node);
|
match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node);
|
||||||
return (struct s3c_rtc_data *)match->data;
|
return match->data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int s3c_rtc_probe(struct platform_device *pdev)
|
static int s3c_rtc_probe(struct platform_device *pdev)
|
||||||
@ -481,7 +520,7 @@ static int s3c_rtc_probe(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
dev_dbg(&pdev->dev, "s3c2410_rtc: tick irq %d, alarm irq %d\n",
|
dev_dbg(&pdev->dev, "s3c2410_rtc: tick irq %d, alarm irq %d\n",
|
||||||
info->irq_tick, info->irq_alarm);
|
info->irq_tick, info->irq_alarm);
|
||||||
|
|
||||||
/* get the memory region */
|
/* get the memory region */
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
@ -498,7 +537,9 @@ static int s3c_rtc_probe(struct platform_device *pdev)
|
|||||||
dev_dbg(&pdev->dev, "probe deferred due to missing rtc clk\n");
|
dev_dbg(&pdev->dev, "probe deferred due to missing rtc clk\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
clk_prepare_enable(info->rtc_clk);
|
ret = clk_prepare_enable(info->rtc_clk);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
if (info->data->needs_src_clk) {
|
if (info->data->needs_src_clk) {
|
||||||
info->rtc_src_clk = devm_clk_get(&pdev->dev, "rtc_src");
|
info->rtc_src_clk = devm_clk_get(&pdev->dev, "rtc_src");
|
||||||
@ -510,10 +551,11 @@ static int s3c_rtc_probe(struct platform_device *pdev)
|
|||||||
else
|
else
|
||||||
dev_dbg(&pdev->dev,
|
dev_dbg(&pdev->dev,
|
||||||
"probe deferred due to missing rtc src clk\n");
|
"probe deferred due to missing rtc src clk\n");
|
||||||
clk_disable_unprepare(info->rtc_clk);
|
goto err_src_clk;
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
clk_prepare_enable(info->rtc_src_clk);
|
ret = clk_prepare_enable(info->rtc_src_clk);
|
||||||
|
if (ret)
|
||||||
|
goto err_src_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check to see if everything is setup correctly */
|
/* check to see if everything is setup correctly */
|
||||||
@ -521,7 +563,7 @@ static int s3c_rtc_probe(struct platform_device *pdev)
|
|||||||
info->data->enable(info);
|
info->data->enable(info);
|
||||||
|
|
||||||
dev_dbg(&pdev->dev, "s3c2410_rtc: RTCCON=%02x\n",
|
dev_dbg(&pdev->dev, "s3c2410_rtc: RTCCON=%02x\n",
|
||||||
readw(info->base + S3C2410_RTCCON));
|
readw(info->base + S3C2410_RTCCON));
|
||||||
|
|
||||||
device_init_wakeup(&pdev->dev, 1);
|
device_init_wakeup(&pdev->dev, 1);
|
||||||
|
|
||||||
@ -541,7 +583,7 @@ static int s3c_rtc_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
/* register RTC and exit */
|
/* register RTC and exit */
|
||||||
info->rtc = devm_rtc_device_register(&pdev->dev, "s3c", &s3c_rtcops,
|
info->rtc = devm_rtc_device_register(&pdev->dev, "s3c", &s3c_rtcops,
|
||||||
THIS_MODULE);
|
THIS_MODULE);
|
||||||
if (IS_ERR(info->rtc)) {
|
if (IS_ERR(info->rtc)) {
|
||||||
dev_err(&pdev->dev, "cannot attach rtc\n");
|
dev_err(&pdev->dev, "cannot attach rtc\n");
|
||||||
ret = PTR_ERR(info->rtc);
|
ret = PTR_ERR(info->rtc);
|
||||||
@ -549,14 +591,14 @@ static int s3c_rtc_probe(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret = devm_request_irq(&pdev->dev, info->irq_alarm, s3c_rtc_alarmirq,
|
ret = devm_request_irq(&pdev->dev, info->irq_alarm, s3c_rtc_alarmirq,
|
||||||
0, "s3c2410-rtc alarm", info);
|
0, "s3c2410-rtc alarm", info);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_alarm, ret);
|
dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_alarm, ret);
|
||||||
goto err_nortc;
|
goto err_nortc;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = devm_request_irq(&pdev->dev, info->irq_tick, s3c_rtc_tickirq,
|
ret = devm_request_irq(&pdev->dev, info->irq_tick, s3c_rtc_tickirq,
|
||||||
0, "s3c2410-rtc tick", info);
|
0, "s3c2410-rtc tick", info);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_tick, ret);
|
dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_tick, ret);
|
||||||
goto err_nortc;
|
goto err_nortc;
|
||||||
@ -569,12 +611,13 @@ static int s3c_rtc_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_nortc:
|
err_nortc:
|
||||||
if (info->data->disable)
|
if (info->data->disable)
|
||||||
info->data->disable(info);
|
info->data->disable(info);
|
||||||
|
|
||||||
if (info->data->needs_src_clk)
|
if (info->data->needs_src_clk)
|
||||||
clk_disable_unprepare(info->rtc_src_clk);
|
clk_disable_unprepare(info->rtc_src_clk);
|
||||||
|
err_src_clk:
|
||||||
clk_disable_unprepare(info->rtc_clk);
|
clk_disable_unprepare(info->rtc_clk);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -585,8 +628,11 @@ static int s3c_rtc_probe(struct platform_device *pdev)
|
|||||||
static int s3c_rtc_suspend(struct device *dev)
|
static int s3c_rtc_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct s3c_rtc *info = dev_get_drvdata(dev);
|
struct s3c_rtc *info = dev_get_drvdata(dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
s3c_rtc_enable_clk(info);
|
ret = s3c_rtc_enable_clk(info);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
/* save TICNT for anyone using periodic interrupts */
|
/* save TICNT for anyone using periodic interrupts */
|
||||||
if (info->data->save_tick_cnt)
|
if (info->data->save_tick_cnt)
|
||||||
@ -747,8 +793,7 @@ static void s3c6410_rtc_restore_tick_cnt(struct s3c_rtc *info)
|
|||||||
writel(info->ticnt_save, info->base + S3C2410_TICNT);
|
writel(info->ticnt_save, info->base + S3C2410_TICNT);
|
||||||
if (info->ticnt_en_save) {
|
if (info->ticnt_en_save) {
|
||||||
con = readw(info->base + S3C2410_RTCCON);
|
con = readw(info->base + S3C2410_RTCCON);
|
||||||
writew(con | info->ticnt_en_save,
|
writew(con | info->ticnt_en_save, info->base + S3C2410_RTCCON);
|
||||||
info->base + S3C2410_RTCCON);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -802,19 +847,19 @@ static struct s3c_rtc_data const s3c6410_rtc_data = {
|
|||||||
static const struct of_device_id s3c_rtc_dt_match[] = {
|
static const struct of_device_id s3c_rtc_dt_match[] = {
|
||||||
{
|
{
|
||||||
.compatible = "samsung,s3c2410-rtc",
|
.compatible = "samsung,s3c2410-rtc",
|
||||||
.data = (void *)&s3c2410_rtc_data,
|
.data = &s3c2410_rtc_data,
|
||||||
}, {
|
}, {
|
||||||
.compatible = "samsung,s3c2416-rtc",
|
.compatible = "samsung,s3c2416-rtc",
|
||||||
.data = (void *)&s3c2416_rtc_data,
|
.data = &s3c2416_rtc_data,
|
||||||
}, {
|
}, {
|
||||||
.compatible = "samsung,s3c2443-rtc",
|
.compatible = "samsung,s3c2443-rtc",
|
||||||
.data = (void *)&s3c2443_rtc_data,
|
.data = &s3c2443_rtc_data,
|
||||||
}, {
|
}, {
|
||||||
.compatible = "samsung,s3c6410-rtc",
|
.compatible = "samsung,s3c6410-rtc",
|
||||||
.data = (void *)&s3c6410_rtc_data,
|
.data = &s3c6410_rtc_data,
|
||||||
}, {
|
}, {
|
||||||
.compatible = "samsung,exynos3250-rtc",
|
.compatible = "samsung,exynos3250-rtc",
|
||||||
.data = (void *)&s3c6410_rtc_data,
|
.data = &s3c6410_rtc_data,
|
||||||
},
|
},
|
||||||
{ /* sentinel */ },
|
{ /* sentinel */ },
|
||||||
};
|
};
|
||||||
|
@ -99,7 +99,7 @@ static int st_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
|||||||
|
|
||||||
lpt = ((unsigned long long)lpt_msb << 32) | lpt_lsb;
|
lpt = ((unsigned long long)lpt_msb << 32) | lpt_lsb;
|
||||||
do_div(lpt, rtc->clkrate);
|
do_div(lpt, rtc->clkrate);
|
||||||
rtc_time_to_tm(lpt, tm);
|
rtc_time64_to_tm(lpt, tm);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -107,13 +107,10 @@ static int st_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
|||||||
static int st_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
static int st_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||||
{
|
{
|
||||||
struct st_rtc *rtc = dev_get_drvdata(dev);
|
struct st_rtc *rtc = dev_get_drvdata(dev);
|
||||||
unsigned long long lpt;
|
unsigned long long lpt, secs;
|
||||||
unsigned long secs, flags;
|
unsigned long flags;
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = rtc_tm_to_time(tm, &secs);
|
secs = rtc_tm_to_time64(tm);
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
lpt = (unsigned long long)secs * rtc->clkrate;
|
lpt = (unsigned long long)secs * rtc->clkrate;
|
||||||
|
|
||||||
@ -161,13 +158,13 @@ static int st_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *t)
|
|||||||
{
|
{
|
||||||
struct st_rtc *rtc = dev_get_drvdata(dev);
|
struct st_rtc *rtc = dev_get_drvdata(dev);
|
||||||
struct rtc_time now;
|
struct rtc_time now;
|
||||||
unsigned long now_secs;
|
unsigned long long now_secs;
|
||||||
unsigned long alarm_secs;
|
unsigned long long alarm_secs;
|
||||||
unsigned long long lpa;
|
unsigned long long lpa;
|
||||||
|
|
||||||
st_rtc_read_time(dev, &now);
|
st_rtc_read_time(dev, &now);
|
||||||
rtc_tm_to_time(&now, &now_secs);
|
now_secs = rtc_tm_to_time64(&now);
|
||||||
rtc_tm_to_time(&t->time, &alarm_secs);
|
alarm_secs = rtc_tm_to_time64(&t->time);
|
||||||
|
|
||||||
/* Invalid alarm time */
|
/* Invalid alarm time */
|
||||||
if (now_secs > alarm_secs)
|
if (now_secs > alarm_secs)
|
||||||
|
@ -94,11 +94,17 @@
|
|||||||
/* STM32_PWR_CR bit field */
|
/* STM32_PWR_CR bit field */
|
||||||
#define PWR_CR_DBP BIT(8)
|
#define PWR_CR_DBP BIT(8)
|
||||||
|
|
||||||
|
struct stm32_rtc_data {
|
||||||
|
bool has_pclk;
|
||||||
|
};
|
||||||
|
|
||||||
struct stm32_rtc {
|
struct stm32_rtc {
|
||||||
struct rtc_device *rtc_dev;
|
struct rtc_device *rtc_dev;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
struct regmap *dbp;
|
struct regmap *dbp;
|
||||||
struct clk *ck_rtc;
|
struct stm32_rtc_data *data;
|
||||||
|
struct clk *pclk;
|
||||||
|
struct clk *rtc_ck;
|
||||||
int irq_alarm;
|
int irq_alarm;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -122,9 +128,9 @@ static int stm32_rtc_enter_init_mode(struct stm32_rtc *rtc)
|
|||||||
writel_relaxed(isr, rtc->base + STM32_RTC_ISR);
|
writel_relaxed(isr, rtc->base + STM32_RTC_ISR);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* It takes around 2 ck_rtc clock cycles to enter in
|
* It takes around 2 rtc_ck clock cycles to enter in
|
||||||
* initialization phase mode (and have INITF flag set). As
|
* initialization phase mode (and have INITF flag set). As
|
||||||
* slowest ck_rtc frequency may be 32kHz and highest should be
|
* slowest rtc_ck frequency may be 32kHz and highest should be
|
||||||
* 1MHz, we poll every 10 us with a timeout of 100ms.
|
* 1MHz, we poll every 10 us with a timeout of 100ms.
|
||||||
*/
|
*/
|
||||||
return readl_relaxed_poll_timeout_atomic(
|
return readl_relaxed_poll_timeout_atomic(
|
||||||
@ -153,7 +159,7 @@ static int stm32_rtc_wait_sync(struct stm32_rtc *rtc)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Wait for RSF to be set to ensure the calendar registers are
|
* Wait for RSF to be set to ensure the calendar registers are
|
||||||
* synchronised, it takes around 2 ck_rtc clock cycles
|
* synchronised, it takes around 2 rtc_ck clock cycles
|
||||||
*/
|
*/
|
||||||
return readl_relaxed_poll_timeout_atomic(rtc->base + STM32_RTC_ISR,
|
return readl_relaxed_poll_timeout_atomic(rtc->base + STM32_RTC_ISR,
|
||||||
isr,
|
isr,
|
||||||
@ -456,7 +462,7 @@ static int stm32_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Poll Alarm write flag to be sure that Alarm update is allowed: it
|
* Poll Alarm write flag to be sure that Alarm update is allowed: it
|
||||||
* takes around 2 ck_rtc clock cycles
|
* takes around 2 rtc_ck clock cycles
|
||||||
*/
|
*/
|
||||||
ret = readl_relaxed_poll_timeout_atomic(rtc->base + STM32_RTC_ISR,
|
ret = readl_relaxed_poll_timeout_atomic(rtc->base + STM32_RTC_ISR,
|
||||||
isr,
|
isr,
|
||||||
@ -490,8 +496,17 @@ static const struct rtc_class_ops stm32_rtc_ops = {
|
|||||||
.alarm_irq_enable = stm32_rtc_alarm_irq_enable,
|
.alarm_irq_enable = stm32_rtc_alarm_irq_enable,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct stm32_rtc_data stm32_rtc_data = {
|
||||||
|
.has_pclk = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct stm32_rtc_data stm32h7_rtc_data = {
|
||||||
|
.has_pclk = true,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct of_device_id stm32_rtc_of_match[] = {
|
static const struct of_device_id stm32_rtc_of_match[] = {
|
||||||
{ .compatible = "st,stm32-rtc" },
|
{ .compatible = "st,stm32-rtc", .data = &stm32_rtc_data },
|
||||||
|
{ .compatible = "st,stm32h7-rtc", .data = &stm32h7_rtc_data },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, stm32_rtc_of_match);
|
MODULE_DEVICE_TABLE(of, stm32_rtc_of_match);
|
||||||
@ -503,7 +518,7 @@ static int stm32_rtc_init(struct platform_device *pdev,
|
|||||||
unsigned int rate;
|
unsigned int rate;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
rate = clk_get_rate(rtc->ck_rtc);
|
rate = clk_get_rate(rtc->rtc_ck);
|
||||||
|
|
||||||
/* Find prediv_a and prediv_s to obtain the 1Hz calendar clock */
|
/* Find prediv_a and prediv_s to obtain the 1Hz calendar clock */
|
||||||
pred_a_max = STM32_RTC_PRER_PRED_A >> STM32_RTC_PRER_PRED_A_SHIFT;
|
pred_a_max = STM32_RTC_PRER_PRED_A >> STM32_RTC_PRER_PRED_A_SHIFT;
|
||||||
@ -524,7 +539,7 @@ static int stm32_rtc_init(struct platform_device *pdev,
|
|||||||
pred_a = pred_a_max;
|
pred_a = pred_a_max;
|
||||||
pred_s = (rate / (pred_a + 1)) - 1;
|
pred_s = (rate / (pred_a + 1)) - 1;
|
||||||
|
|
||||||
dev_warn(&pdev->dev, "ck_rtc is %s\n",
|
dev_warn(&pdev->dev, "rtc_ck is %s\n",
|
||||||
(rate < ((pred_a + 1) * (pred_s + 1))) ?
|
(rate < ((pred_a + 1) * (pred_s + 1))) ?
|
||||||
"fast" : "slow");
|
"fast" : "slow");
|
||||||
}
|
}
|
||||||
@ -561,6 +576,7 @@ static int stm32_rtc_probe(struct platform_device *pdev)
|
|||||||
{
|
{
|
||||||
struct stm32_rtc *rtc;
|
struct stm32_rtc *rtc;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
const struct of_device_id *match;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
|
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
|
||||||
@ -579,15 +595,34 @@ static int stm32_rtc_probe(struct platform_device *pdev)
|
|||||||
return PTR_ERR(rtc->dbp);
|
return PTR_ERR(rtc->dbp);
|
||||||
}
|
}
|
||||||
|
|
||||||
rtc->ck_rtc = devm_clk_get(&pdev->dev, NULL);
|
match = of_match_device(stm32_rtc_of_match, &pdev->dev);
|
||||||
if (IS_ERR(rtc->ck_rtc)) {
|
rtc->data = (struct stm32_rtc_data *)match->data;
|
||||||
dev_err(&pdev->dev, "no ck_rtc clock");
|
|
||||||
return PTR_ERR(rtc->ck_rtc);
|
if (!rtc->data->has_pclk) {
|
||||||
|
rtc->pclk = NULL;
|
||||||
|
rtc->rtc_ck = devm_clk_get(&pdev->dev, NULL);
|
||||||
|
} else {
|
||||||
|
rtc->pclk = devm_clk_get(&pdev->dev, "pclk");
|
||||||
|
if (IS_ERR(rtc->pclk)) {
|
||||||
|
dev_err(&pdev->dev, "no pclk clock");
|
||||||
|
return PTR_ERR(rtc->pclk);
|
||||||
|
}
|
||||||
|
rtc->rtc_ck = devm_clk_get(&pdev->dev, "rtc_ck");
|
||||||
|
}
|
||||||
|
if (IS_ERR(rtc->rtc_ck)) {
|
||||||
|
dev_err(&pdev->dev, "no rtc_ck clock");
|
||||||
|
return PTR_ERR(rtc->rtc_ck);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = clk_prepare_enable(rtc->ck_rtc);
|
if (rtc->data->has_pclk) {
|
||||||
|
ret = clk_prepare_enable(rtc->pclk);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(rtc->rtc_ck);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
goto err;
|
||||||
|
|
||||||
regmap_update_bits(rtc->dbp, PWR_CR, PWR_CR_DBP, PWR_CR_DBP);
|
regmap_update_bits(rtc->dbp, PWR_CR, PWR_CR_DBP, PWR_CR_DBP);
|
||||||
|
|
||||||
@ -595,7 +630,7 @@ static int stm32_rtc_probe(struct platform_device *pdev)
|
|||||||
* After a system reset, RTC_ISR.INITS flag can be read to check if
|
* After a system reset, RTC_ISR.INITS flag can be read to check if
|
||||||
* the calendar has been initalized or not. INITS flag is reset by a
|
* the calendar has been initalized or not. INITS flag is reset by a
|
||||||
* power-on reset (no vbat, no power-supply). It is not reset if
|
* power-on reset (no vbat, no power-supply). It is not reset if
|
||||||
* ck_rtc parent clock has changed (so RTC prescalers need to be
|
* rtc_ck parent clock has changed (so RTC prescalers need to be
|
||||||
* changed). That's why we cannot rely on this flag to know if RTC
|
* changed). That's why we cannot rely on this flag to know if RTC
|
||||||
* init has to be done.
|
* init has to be done.
|
||||||
*/
|
*/
|
||||||
@ -646,7 +681,9 @@ static int stm32_rtc_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
err:
|
err:
|
||||||
clk_disable_unprepare(rtc->ck_rtc);
|
if (rtc->data->has_pclk)
|
||||||
|
clk_disable_unprepare(rtc->pclk);
|
||||||
|
clk_disable_unprepare(rtc->rtc_ck);
|
||||||
|
|
||||||
regmap_update_bits(rtc->dbp, PWR_CR, PWR_CR_DBP, 0);
|
regmap_update_bits(rtc->dbp, PWR_CR, PWR_CR_DBP, 0);
|
||||||
|
|
||||||
@ -667,7 +704,9 @@ static int stm32_rtc_remove(struct platform_device *pdev)
|
|||||||
writel_relaxed(cr, rtc->base + STM32_RTC_CR);
|
writel_relaxed(cr, rtc->base + STM32_RTC_CR);
|
||||||
stm32_rtc_wpr_lock(rtc);
|
stm32_rtc_wpr_lock(rtc);
|
||||||
|
|
||||||
clk_disable_unprepare(rtc->ck_rtc);
|
clk_disable_unprepare(rtc->rtc_ck);
|
||||||
|
if (rtc->data->has_pclk)
|
||||||
|
clk_disable_unprepare(rtc->pclk);
|
||||||
|
|
||||||
/* Enable backup domain write protection */
|
/* Enable backup domain write protection */
|
||||||
regmap_update_bits(rtc->dbp, PWR_CR, PWR_CR_DBP, 0);
|
regmap_update_bits(rtc->dbp, PWR_CR, PWR_CR_DBP, 0);
|
||||||
@ -682,6 +721,9 @@ static int stm32_rtc_suspend(struct device *dev)
|
|||||||
{
|
{
|
||||||
struct stm32_rtc *rtc = dev_get_drvdata(dev);
|
struct stm32_rtc *rtc = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
if (rtc->data->has_pclk)
|
||||||
|
clk_disable_unprepare(rtc->pclk);
|
||||||
|
|
||||||
if (device_may_wakeup(dev))
|
if (device_may_wakeup(dev))
|
||||||
return enable_irq_wake(rtc->irq_alarm);
|
return enable_irq_wake(rtc->irq_alarm);
|
||||||
|
|
||||||
@ -693,6 +735,12 @@ static int stm32_rtc_resume(struct device *dev)
|
|||||||
struct stm32_rtc *rtc = dev_get_drvdata(dev);
|
struct stm32_rtc *rtc = dev_get_drvdata(dev);
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
|
if (rtc->data->has_pclk) {
|
||||||
|
ret = clk_prepare_enable(rtc->pclk);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
ret = stm32_rtc_wait_sync(rtc);
|
ret = stm32_rtc_wait_sync(rtc);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -27,7 +27,8 @@
|
|||||||
static ssize_t
|
static ssize_t
|
||||||
name_show(struct device *dev, struct device_attribute *attr, char *buf)
|
name_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
return sprintf(buf, "%s\n", to_rtc_device(dev)->name);
|
return sprintf(buf, "%s %s\n", dev_driver_string(dev->parent),
|
||||||
|
dev_name(dev->parent));
|
||||||
}
|
}
|
||||||
static DEVICE_ATTR_RO(name);
|
static DEVICE_ATTR_RO(name);
|
||||||
|
|
||||||
|
@ -12,6 +12,9 @@
|
|||||||
#ifndef _LINUX_NVMEM_PROVIDER_H
|
#ifndef _LINUX_NVMEM_PROVIDER_H
|
||||||
#define _LINUX_NVMEM_PROVIDER_H
|
#define _LINUX_NVMEM_PROVIDER_H
|
||||||
|
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
|
|
||||||
struct nvmem_device;
|
struct nvmem_device;
|
||||||
struct nvmem_cell_info;
|
struct nvmem_cell_info;
|
||||||
typedef int (*nvmem_reg_read_t)(void *priv, unsigned int offset,
|
typedef int (*nvmem_reg_read_t)(void *priv, unsigned int offset,
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/nvmem-provider.h>
|
||||||
#include <uapi/linux/rtc.h>
|
#include <uapi/linux/rtc.h>
|
||||||
|
|
||||||
extern int rtc_month_days(unsigned int month, unsigned int year);
|
extern int rtc_month_days(unsigned int month, unsigned int year);
|
||||||
@ -32,17 +33,11 @@ static inline time64_t rtc_tm_sub(struct rtc_time *lhs, struct rtc_time *rhs)
|
|||||||
return rtc_tm_to_time64(lhs) - rtc_tm_to_time64(rhs);
|
return rtc_tm_to_time64(lhs) - rtc_tm_to_time64(rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Deprecated. Use rtc_time64_to_tm().
|
|
||||||
*/
|
|
||||||
static inline void rtc_time_to_tm(unsigned long time, struct rtc_time *tm)
|
static inline void rtc_time_to_tm(unsigned long time, struct rtc_time *tm)
|
||||||
{
|
{
|
||||||
rtc_time64_to_tm(time, tm);
|
rtc_time64_to_tm(time, tm);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Deprecated. Use rtc_tm_to_time64().
|
|
||||||
*/
|
|
||||||
static inline int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time)
|
static inline int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time)
|
||||||
{
|
{
|
||||||
*time = rtc_tm_to_time64(tm);
|
*time = rtc_tm_to_time64(tm);
|
||||||
@ -116,7 +111,6 @@ struct rtc_device {
|
|||||||
struct module *owner;
|
struct module *owner;
|
||||||
|
|
||||||
int id;
|
int id;
|
||||||
char name[RTC_DEVICE_NAME_SIZE];
|
|
||||||
|
|
||||||
const struct rtc_class_ops *ops;
|
const struct rtc_class_ops *ops;
|
||||||
struct mutex ops_lock;
|
struct mutex ops_lock;
|
||||||
@ -143,6 +137,14 @@ struct rtc_device {
|
|||||||
/* Some hardware can't support UIE mode */
|
/* Some hardware can't support UIE mode */
|
||||||
int uie_unsupported;
|
int uie_unsupported;
|
||||||
|
|
||||||
|
bool registered;
|
||||||
|
|
||||||
|
struct nvmem_config *nvmem_config;
|
||||||
|
struct nvmem_device *nvmem;
|
||||||
|
/* Old ABI support */
|
||||||
|
bool nvram_old_abi;
|
||||||
|
struct bin_attribute *nvram;
|
||||||
|
|
||||||
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
|
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
|
||||||
struct work_struct uie_task;
|
struct work_struct uie_task;
|
||||||
struct timer_list uie_timer;
|
struct timer_list uie_timer;
|
||||||
@ -164,6 +166,8 @@ extern struct rtc_device *devm_rtc_device_register(struct device *dev,
|
|||||||
const char *name,
|
const char *name,
|
||||||
const struct rtc_class_ops *ops,
|
const struct rtc_class_ops *ops,
|
||||||
struct module *owner);
|
struct module *owner);
|
||||||
|
struct rtc_device *devm_rtc_allocate_device(struct device *dev);
|
||||||
|
int __rtc_register_device(struct module *owner, struct rtc_device *rtc);
|
||||||
extern void rtc_device_unregister(struct rtc_device *rtc);
|
extern void rtc_device_unregister(struct rtc_device *rtc);
|
||||||
extern void devm_rtc_device_unregister(struct device *dev,
|
extern void devm_rtc_device_unregister(struct device *dev,
|
||||||
struct rtc_device *rtc);
|
struct rtc_device *rtc);
|
||||||
@ -219,6 +223,9 @@ static inline bool is_leap_year(unsigned int year)
|
|||||||
return (!(year % 4) && (year % 100)) || !(year % 400);
|
return (!(year % 4) && (year % 100)) || !(year % 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define rtc_register_device(device) \
|
||||||
|
__rtc_register_device(THIS_MODULE, device)
|
||||||
|
|
||||||
#ifdef CONFIG_RTC_HCTOSYS_DEVICE
|
#ifdef CONFIG_RTC_HCTOSYS_DEVICE
|
||||||
extern int rtc_hctosys_ret;
|
extern int rtc_hctosys_ret;
|
||||||
#else
|
#else
|
||||||
|
@ -9,7 +9,7 @@ TEST_GEN_PROGS = posix_timers nanosleep nsleep-lat set-timer-lat mqueue-lat \
|
|||||||
|
|
||||||
TEST_GEN_PROGS_EXTENDED = alarmtimer-suspend valid-adjtimex adjtick change_skew \
|
TEST_GEN_PROGS_EXTENDED = alarmtimer-suspend valid-adjtimex adjtick change_skew \
|
||||||
skew_consistency clocksource-switch freq-step leap-a-day \
|
skew_consistency clocksource-switch freq-step leap-a-day \
|
||||||
leapcrash set-tai set-2038 set-tz
|
leapcrash set-tai set-2038 set-tz rtctest_setdate
|
||||||
|
|
||||||
|
|
||||||
include ../lib.mk
|
include ../lib.mk
|
||||||
|
@ -21,6 +21,9 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
|
#ifndef ARRAY_SIZE
|
||||||
|
# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This expects the new RTC class driver framework, working with
|
* This expects the new RTC class driver framework, working with
|
||||||
@ -29,23 +32,84 @@
|
|||||||
*/
|
*/
|
||||||
static const char default_rtc[] = "/dev/rtc0";
|
static const char default_rtc[] = "/dev/rtc0";
|
||||||
|
|
||||||
|
static struct rtc_time cutoff_dates[] = {
|
||||||
|
{
|
||||||
|
.tm_year = 70, /* 1970 -1900 */
|
||||||
|
.tm_mday = 1,
|
||||||
|
},
|
||||||
|
/* signed time_t 19/01/2038 3:14:08 */
|
||||||
|
{
|
||||||
|
.tm_year = 138,
|
||||||
|
.tm_mday = 19,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.tm_year = 138,
|
||||||
|
.tm_mday = 20,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.tm_year = 199, /* 2099 -1900 */
|
||||||
|
.tm_mday = 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.tm_year = 200, /* 2100 -1900 */
|
||||||
|
.tm_mday = 1,
|
||||||
|
},
|
||||||
|
/* unsigned time_t 07/02/2106 7:28:15*/
|
||||||
|
{
|
||||||
|
.tm_year = 205,
|
||||||
|
.tm_mon = 1,
|
||||||
|
.tm_mday = 7,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.tm_year = 206,
|
||||||
|
.tm_mon = 1,
|
||||||
|
.tm_mday = 8,
|
||||||
|
},
|
||||||
|
/* signed time on 64bit in nanoseconds 12/04/2262 01:47:16*/
|
||||||
|
{
|
||||||
|
.tm_year = 362,
|
||||||
|
.tm_mon = 3,
|
||||||
|
.tm_mday = 12,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.tm_year = 362, /* 2262 -1900 */
|
||||||
|
.tm_mon = 3,
|
||||||
|
.tm_mday = 13,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int compare_dates(struct rtc_time *a, struct rtc_time *b)
|
||||||
|
{
|
||||||
|
if (a->tm_year != b->tm_year ||
|
||||||
|
a->tm_mon != b->tm_mon ||
|
||||||
|
a->tm_mday != b->tm_mday ||
|
||||||
|
a->tm_hour != b->tm_hour ||
|
||||||
|
a->tm_min != b->tm_min ||
|
||||||
|
((b->tm_sec - a->tm_sec) > 1))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
int i, fd, retval, irqcount = 0;
|
int i, fd, retval, irqcount = 0, dangerous = 0;
|
||||||
unsigned long tmp, data;
|
unsigned long tmp, data;
|
||||||
struct rtc_time rtc_tm;
|
struct rtc_time rtc_tm;
|
||||||
const char *rtc = default_rtc;
|
const char *rtc = default_rtc;
|
||||||
struct timeval start, end, diff;
|
struct timeval start, end, diff;
|
||||||
|
|
||||||
switch (argc) {
|
switch (argc) {
|
||||||
|
case 3:
|
||||||
|
if (*argv[2] == 'd')
|
||||||
|
dangerous = 1;
|
||||||
case 2:
|
case 2:
|
||||||
rtc = argv[1];
|
rtc = argv[1];
|
||||||
/* FALLTHROUGH */
|
/* FALLTHROUGH */
|
||||||
case 1:
|
case 1:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "usage: rtctest [rtcdev]\n");
|
fprintf(stderr, "usage: rtctest [rtcdev] [d]\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,7 +266,7 @@ test_PIE:
|
|||||||
/* not all RTCs support periodic IRQs */
|
/* not all RTCs support periodic IRQs */
|
||||||
if (errno == EINVAL) {
|
if (errno == EINVAL) {
|
||||||
fprintf(stderr, "\nNo periodic IRQ support\n");
|
fprintf(stderr, "\nNo periodic IRQ support\n");
|
||||||
goto done;
|
goto test_DATE;
|
||||||
}
|
}
|
||||||
perror("RTC_IRQP_READ ioctl");
|
perror("RTC_IRQP_READ ioctl");
|
||||||
exit(errno);
|
exit(errno);
|
||||||
@ -221,7 +285,7 @@ test_PIE:
|
|||||||
if (errno == EINVAL) {
|
if (errno == EINVAL) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"\n...Periodic IRQ rate is fixed\n");
|
"\n...Periodic IRQ rate is fixed\n");
|
||||||
goto done;
|
goto test_DATE;
|
||||||
}
|
}
|
||||||
perror("RTC_IRQP_SET ioctl");
|
perror("RTC_IRQP_SET ioctl");
|
||||||
exit(errno);
|
exit(errno);
|
||||||
@ -269,6 +333,62 @@ test_PIE:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test_DATE:
|
||||||
|
if (!dangerous)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
fprintf(stderr, "\nTesting problematic dates\n");
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(cutoff_dates); i++) {
|
||||||
|
struct rtc_time current;
|
||||||
|
|
||||||
|
/* Write the new date in RTC */
|
||||||
|
retval = ioctl(fd, RTC_SET_TIME, &cutoff_dates[i]);
|
||||||
|
if (retval == -1) {
|
||||||
|
perror("RTC_SET_TIME ioctl");
|
||||||
|
close(fd);
|
||||||
|
exit(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read back */
|
||||||
|
retval = ioctl(fd, RTC_RD_TIME, ¤t);
|
||||||
|
if (retval == -1) {
|
||||||
|
perror("RTC_RD_TIME ioctl");
|
||||||
|
exit(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(compare_dates(&cutoff_dates[i], ¤t)) {
|
||||||
|
fprintf(stderr,"Setting date %d failed\n",
|
||||||
|
cutoff_dates[i].tm_year + 1900);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
cutoff_dates[i].tm_sec += 5;
|
||||||
|
|
||||||
|
/* Write the new alarm in RTC */
|
||||||
|
retval = ioctl(fd, RTC_ALM_SET, &cutoff_dates[i]);
|
||||||
|
if (retval == -1) {
|
||||||
|
perror("RTC_ALM_SET ioctl");
|
||||||
|
close(fd);
|
||||||
|
exit(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read back */
|
||||||
|
retval = ioctl(fd, RTC_ALM_READ, ¤t);
|
||||||
|
if (retval == -1) {
|
||||||
|
perror("RTC_ALM_READ ioctl");
|
||||||
|
exit(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(compare_dates(&cutoff_dates[i], ¤t)) {
|
||||||
|
fprintf(stderr,"Setting alarm %d failed\n",
|
||||||
|
cutoff_dates[i].tm_year + 1900);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "Setting year %d is OK \n",
|
||||||
|
cutoff_dates[i].tm_year + 1900);
|
||||||
|
}
|
||||||
done:
|
done:
|
||||||
fprintf(stderr, "\n\n\t\t\t *** Test complete ***\n");
|
fprintf(stderr, "\n\n\t\t\t *** Test complete ***\n");
|
||||||
|
|
||||||
|
86
tools/testing/selftests/timers/rtctest_setdate.c
Normal file
86
tools/testing/selftests/timers/rtctest_setdate.c
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/* Real Time Clock Driver Test
|
||||||
|
* by: Benjamin Gaignard (benjamin.gaignard@linaro.org)
|
||||||
|
*
|
||||||
|
* To build
|
||||||
|
* gcc rtctest_setdate.c -o rtctest_setdate
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <linux/rtc.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
static const char default_time[] = "00:00:00";
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int fd, retval;
|
||||||
|
struct rtc_time new, current;
|
||||||
|
const char *rtc, *date;
|
||||||
|
const char *time = default_time;
|
||||||
|
|
||||||
|
switch (argc) {
|
||||||
|
case 4:
|
||||||
|
time = argv[3];
|
||||||
|
/* FALLTHROUGH */
|
||||||
|
case 3:
|
||||||
|
date = argv[2];
|
||||||
|
rtc = argv[1];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "usage: rtctest_setdate <rtcdev> <DD-MM-YYYY> [HH:MM:SS]\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = open(rtc, O_RDONLY);
|
||||||
|
if (fd == -1) {
|
||||||
|
perror(rtc);
|
||||||
|
exit(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
sscanf(date, "%d-%d-%d", &new.tm_mday, &new.tm_mon, &new.tm_year);
|
||||||
|
new.tm_mon -= 1;
|
||||||
|
new.tm_year -= 1900;
|
||||||
|
sscanf(time, "%d:%d:%d", &new.tm_hour, &new.tm_min, &new.tm_sec);
|
||||||
|
|
||||||
|
fprintf(stderr, "Test will set RTC date/time to %d-%d-%d, %02d:%02d:%02d.\n",
|
||||||
|
new.tm_mday, new.tm_mon + 1, new.tm_year + 1900,
|
||||||
|
new.tm_hour, new.tm_min, new.tm_sec);
|
||||||
|
|
||||||
|
/* Write the new date in RTC */
|
||||||
|
retval = ioctl(fd, RTC_SET_TIME, &new);
|
||||||
|
if (retval == -1) {
|
||||||
|
perror("RTC_SET_TIME ioctl");
|
||||||
|
close(fd);
|
||||||
|
exit(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read back */
|
||||||
|
retval = ioctl(fd, RTC_RD_TIME, ¤t);
|
||||||
|
if (retval == -1) {
|
||||||
|
perror("RTC_RD_TIME ioctl");
|
||||||
|
exit(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "\n\nCurrent RTC date/time is %d-%d-%d, %02d:%02d:%02d.\n",
|
||||||
|
current.tm_mday, current.tm_mon + 1, current.tm_year + 1900,
|
||||||
|
current.tm_hour, current.tm_min, current.tm_sec);
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user