RTC for 4.16
Subsystem: - Move ABI documentation to Documentation/ABI New driver: - NXP i.MX53 SRTC - Chrome OS EC RTC Drivers: - Remove at32ap700x - Many fixes in various error paths -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEXx9Viay1+e7J/aM4AyWl4gNJNJIFAlp0P/wACgkQAyWl4gNJ NJLKnQ/+N2b1O5urndPRbxxFOYixNtsNUav7nqslsewWtKJ0q+vSlzkiFXPWIIqm 7KFoMtp3ZnV30t4QkB3A2SHjIJxo2vSwSkEld6ZpMMaMA2ZRo710eNg7oJanwArG 3aE6aNY24X/8/fc4zBeNxdeE8wWHYZU40tdkoHctUQSfyb2K2gGQD+/mjJV11Q/s +ULx3bqcjnfMWDn5MKMyoxIZiAKwrljtc9dhdK03aVvgCic7cApPHV4W4ksFPGJR GuTo/TrKUELi2GxZpbLtoX03K7+NM5nlLeCIBmPKmmCvO60Ie1hGIoaOn5F3jkSR 8/1OlkUuN3HCn0L67jKH9ujt+NvZnUzl1AEtzN5nx435Ikt7GJGqJyHbljmoNfnw LhfZVYhD1y5v0SsJ4lG0KYVL57elTCLQqqnqiLSGBjNsYOOceM7iwwxrMCIMvnHA NWSqDMWmJvEl4jRK1Rm0z867JYUW4EN+43YnkMZt8D61LQrYRa5A9s0FRqzfrHjz bQgR2KcYPGiHvAJ/D97BUpKbEk4bwrU+Y6gaHyWl1v1uGysP9nA7fCpMbkSov2GA 04od8FIyiDhpKEmZQapfXLqokgiuDN9Wt5IczVQ3ZgOAZCvneFAojxLwyLqJ0+3D xu1wOVrQx3UilA59QqWCC2EiyVWDLEtrnSXjYJCPy/QSfaH7uOI= =TjE4 -----END PGP SIGNATURE----- Merge tag 'rtc-4.16' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux Pull RTC updates from Alexandre Belloni: "Not much this cycle. I've pushed the at32ap700x removal late but it is unlikely to cause any issues. Summary: Subsystem: - Move ABI documentation to Documentation/ABI New driver: - NXP i.MX53 SRTC - Chrome OS EC RTC Drivers: - Remove at32ap700x - Many fixes in various error paths" * tag 'rtc-4.16' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux: rtc: remove rtc-at32ap700x Documentation: rtc: move iotcl interface documentation to ABI Documentation: rtc: add sysfs file permissions Documentation: rtc: move sysfs documentation to ABI rtc: mxc_v2: remove __exit annotation rtc: mxc_v2: Remove unnecessary platform_get_resource() error check rtc: add mxc driver for i.MX53 SRTC dt-bindings: rtc: add bindings for i.MX53 SRTC rtc: r7301: Fix a possible sleep-in-atomic bug in rtc7301_set_time rtc: r7301: Fix a possible sleep-in-atomic bug in rtc7301_read_time rtc: omap: fix unbalanced clk_prepare_enable/clk_disable_unprepare rtc: ac100: Fix multiple race conditions rtc: sun6i: ensure rtc is kfree'd on error rtc: cros-ec: add cros-ec-rtc driver. mfd: cros_ec: Introduce RTC commands and events definitions. rtc: stm32: Fix copyright rtc: Remove unused RTC_DEVICE_NAME_SIZE rtc: r9701: Remove r9701_remove function rtc: brcmstb-waketimer: fix error handling in brcmstb_waketmr_probe()
This commit is contained in:
Коммит
bf644990a7
|
@ -0,0 +1,42 @@
|
|||
What: /dev/rtcX
|
||||
Date: April 2005
|
||||
KernelVersion: 2.6.12
|
||||
Contact: linux-rtc@vger.kernel.org
|
||||
Description:
|
||||
The ioctl interface to drivers for real-time clocks (RTCs).
|
||||
Following actions are supported:
|
||||
|
||||
* RTC_RD_TIME, RTC_SET_TIME: Read or set the RTC time. Time
|
||||
format is a Gregorian calendar date and 24 hour wall clock
|
||||
time.
|
||||
|
||||
* RTC_AIE_ON, RTC_AIE_OFF: Enable or disable the alarm interrupt
|
||||
for RTCs that support alarms
|
||||
|
||||
* RTC_ALM_READ, RTC_ALM_SET: Read or set the alarm time for
|
||||
RTCs that support alarms. Can be set upto 24 hours in the
|
||||
future. Requires a separate RTC_AIE_ON call to enable the
|
||||
alarm interrupt. (Prefer to use RTC_WKALM_*)
|
||||
|
||||
* RTC_WKALM_RD, RTC_WKALM_SET: For RTCs that support a more
|
||||
powerful interface, which can issue alarms beyond 24 hours and
|
||||
enable IRQs in the same request.
|
||||
|
||||
* RTC_PIE_ON, RTC_PIE_OFF: Enable or disable the periodic
|
||||
interrupt for RTCs that support periodic interrupts.
|
||||
|
||||
* RTC_UIE_ON, RTC_UIE_OFF: Enable or disable the update
|
||||
interrupt for RTCs that support it.
|
||||
|
||||
* RTC_IRQP_READ, RTC_IRQP_SET: Read or set the frequency for
|
||||
periodic interrupts for RTCs that support periodic interrupts.
|
||||
Requires a separate RTC_PIE_ON call to enable the periodic
|
||||
interrupts.
|
||||
|
||||
The ioctl() calls supported by the older /dev/rtc interface are
|
||||
also supported by the newer RTC class framework. However,
|
||||
because the chips and systems are not standardized, some PC/AT
|
||||
functionality might not be provided. And in the same way, some
|
||||
newer features -- including those enabled by ACPI -- are exposed
|
||||
by the RTC class framework, but can't be supported by the older
|
||||
driver.
|
|
@ -0,0 +1,91 @@
|
|||
What: /sys/class/rtc/
|
||||
Date: March 2006
|
||||
KernelVersion: 2.6.17
|
||||
Contact: linux-rtc@vger.kernel.org
|
||||
Description:
|
||||
The rtc/ class subdirectory belongs to the RTC subsystem.
|
||||
|
||||
What: /sys/class/rtc/rtcX/
|
||||
Date: March 2006
|
||||
KernelVersion: 2.6.17
|
||||
Contact: linux-rtc@vger.kernel.org
|
||||
Description:
|
||||
The /sys/class/rtc/rtc{0,1,2,3,...} directories correspond
|
||||
to each RTC device.
|
||||
|
||||
What: /sys/class/rtc/rtcX/date
|
||||
Date: March 2006
|
||||
KernelVersion: 2.6.17
|
||||
Contact: linux-rtc@vger.kernel.org
|
||||
Description:
|
||||
(RO) RTC-provided date in YYYY-MM-DD format
|
||||
|
||||
What: /sys/class/rtc/rtcX/hctosys
|
||||
Date: September 2009
|
||||
KernelVersion: 2.6.32
|
||||
Contact: linux-rtc@vger.kernel.org
|
||||
Description:
|
||||
(RO) 1 if the RTC provided the system time at boot via the
|
||||
CONFIG_RTC_HCTOSYS kernel option, 0 otherwise
|
||||
|
||||
What: /sys/class/rtc/rtcX/max_user_freq
|
||||
Date: October 2007
|
||||
KernelVersion: 2.6.24
|
||||
Contact: linux-rtc@vger.kernel.org
|
||||
Description:
|
||||
(RW) The maximum interrupt rate an unprivileged user may request
|
||||
from this RTC.
|
||||
|
||||
What: /sys/class/rtc/rtcX/name
|
||||
Date: March 2006
|
||||
KernelVersion: 2.6.17
|
||||
Contact: linux-rtc@vger.kernel.org
|
||||
Description:
|
||||
(RO) The name of the RTC corresponding to this sysfs directory
|
||||
|
||||
What: /sys/class/rtc/rtcX/since_epoch
|
||||
Date: March 2006
|
||||
KernelVersion: 2.6.17
|
||||
Contact: linux-rtc@vger.kernel.org
|
||||
Description:
|
||||
(RO) RTC-provided time as the number of seconds since the epoch
|
||||
|
||||
What: /sys/class/rtc/rtcX/time
|
||||
Date: March 2006
|
||||
KernelVersion: 2.6.17
|
||||
Contact: linux-rtc@vger.kernel.org
|
||||
Description:
|
||||
(RO) RTC-provided time in 24-hour notation (hh:mm:ss)
|
||||
|
||||
What: /sys/class/rtc/rtcX/*/nvmem
|
||||
Date: February 2016
|
||||
KernelVersion: 4.6
|
||||
Contact: linux-rtc@vger.kernel.org
|
||||
Description:
|
||||
(RW) The non volatile storage exported as a raw file, as
|
||||
described in Documentation/nvmem/nvmem.txt
|
||||
|
||||
What: /sys/class/rtc/rtcX/offset
|
||||
Date: February 2016
|
||||
KernelVersion: 4.6
|
||||
Contact: linux-rtc@vger.kernel.org
|
||||
Description:
|
||||
(RW) The amount which the rtc clock has been adjusted in
|
||||
firmware. Visible only if the driver supports clock offset
|
||||
adjustment. 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 billion ticks. A positive value makes a day pass more
|
||||
slowly, longer, and a negative value makes a day pass more
|
||||
quickly.
|
||||
|
||||
What: /sys/class/rtc/rtcX/wakealarm
|
||||
Date: February 2007
|
||||
KernelVersion: 2.6.20
|
||||
Contact: linux-rtc@vger.kernel.org
|
||||
Description:
|
||||
(RW) The time at which the clock will generate a system wakeup
|
||||
event. This is a one shot wakeup event, so must be reset after
|
||||
wake if a daily wakeup is required. Format is seconds since the
|
||||
epoch by default, or if there's a leading +, seconds in the
|
||||
future, or if there is a leading +=, seconds ahead of the
|
||||
current alarm.
|
|
@ -0,0 +1,17 @@
|
|||
* i.MX53 Secure Real Time Clock (SRTC)
|
||||
|
||||
Required properties:
|
||||
- compatible: should be: "fsl,imx53-rtc"
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- clocks: should contain the phandle for the rtc clock
|
||||
- interrupts: rtc alarm interrupt
|
||||
|
||||
Example:
|
||||
|
||||
rtc@53fa4000 {
|
||||
compatible = "fsl,imx53-rtc";
|
||||
reg = <0x53fa4000 0x4000>;
|
||||
interrupts = <24>;
|
||||
clocks = <&clks IMX5_CLK_SRTC_GATE>;
|
||||
};
|
|
@ -136,82 +136,5 @@ 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
|
||||
other tasks, because of its greater functionality.
|
||||
|
||||
SYSFS interface
|
||||
---------------
|
||||
|
||||
The sysfs interface under /sys/class/rtc/rtcN provides access to various
|
||||
rtc attributes without requiring the use of ioctls. All dates and times
|
||||
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
|
||||
CONFIG_RTC_HCTOSYS kernel option, 0 otherwise
|
||||
max_user_freq The maximum interrupt rate an unprivileged user may request
|
||||
from this RTC.
|
||||
name The name of the RTC corresponding to this sysfs directory
|
||||
since_epoch The number of seconds since the epoch according to the RTC
|
||||
time RTC-provided time
|
||||
wakealarm The time at which the clock will generate a system wakeup
|
||||
event. This is a one shot wakeup event, so must be reset
|
||||
after wake if a daily wakeup is required. Format is seconds
|
||||
since the epoch by default, or if there's a leading +, seconds
|
||||
in the future, or if there is a leading +=, seconds ahead of
|
||||
the current alarm.
|
||||
offset The amount which the rtc clock has been adjusted in firmware.
|
||||
Visible only if the driver supports clock offset adjustment.
|
||||
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
|
||||
billion ticks. A positive value makes a day pass more slowly,
|
||||
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
|
||||
---------------
|
||||
|
||||
The ioctl() calls supported by /dev/rtc are also supported by the RTC class
|
||||
framework. However, because the chips and systems are not standardized,
|
||||
some PC/AT functionality might not be provided. And in the same way, some
|
||||
newer features -- including those enabled by ACPI -- are exposed by the
|
||||
RTC class framework, but can't be supported by the older driver.
|
||||
|
||||
* RTC_RD_TIME, RTC_SET_TIME ... every RTC supports at least reading
|
||||
time, returning the result as a Gregorian calendar date and 24 hour
|
||||
wall clock time. To be most useful, this time may also be updated.
|
||||
|
||||
* RTC_AIE_ON, RTC_AIE_OFF, RTC_ALM_SET, RTC_ALM_READ ... when the RTC
|
||||
is connected to an IRQ line, it can often issue an alarm IRQ up to
|
||||
24 hours in the future. (Use RTC_WKALM_* by preference.)
|
||||
|
||||
* RTC_WKALM_SET, RTC_WKALM_RD ... RTCs that can issue alarms beyond
|
||||
the next 24 hours use a slightly more powerful API, which supports
|
||||
setting the longer alarm time and enabling its IRQ using a single
|
||||
request (using the same model as EFI firmware).
|
||||
|
||||
* RTC_UIE_ON, RTC_UIE_OFF ... if the RTC offers IRQs, the RTC framework
|
||||
will emulate this mechanism.
|
||||
|
||||
* RTC_PIE_ON, RTC_PIE_OFF, RTC_IRQP_SET, RTC_IRQP_READ ... these icotls
|
||||
are emulated via a kernel hrtimer.
|
||||
|
||||
In many cases, the RTC alarm can be a system wake event, used to force
|
||||
Linux out of a low power sleep state (or hibernation) back to a fully
|
||||
operational state. For example, a system could enter a deep power saving
|
||||
state until it's time to execute some scheduled tasks.
|
||||
|
||||
Note that many of these ioctls are handled by the common rtc-dev interface.
|
||||
Some common examples:
|
||||
|
||||
* RTC_RD_TIME, RTC_SET_TIME: the read_time/set_time functions will be
|
||||
called with appropriate values.
|
||||
|
||||
* RTC_ALM_SET, RTC_ALM_READ, RTC_WKALM_SET, RTC_WKALM_RD: gets or sets
|
||||
the alarm rtc_timer. May call the set_alarm driver function.
|
||||
|
||||
* RTC_IRQP_SET, RTC_IRQP_READ: These are emulated by the generic code.
|
||||
|
||||
* RTC_PIE_ON, RTC_PIE_OFF: These are also emulated by the generic code.
|
||||
|
||||
If all else fails, check out the tools/testing/selftests/timers/rtctest.c test!
|
||||
Check out tools/testing/selftests/timers/rtctest.c for an example usage of the
|
||||
ioctl interface.
|
||||
|
|
|
@ -1255,6 +1255,16 @@ config RTC_DRV_ZYNQMP
|
|||
If you say yes here you get support for the RTC controller found on
|
||||
Xilinx Zynq Ultrascale+ MPSoC.
|
||||
|
||||
config RTC_DRV_CROS_EC
|
||||
tristate "Chrome OS EC RTC driver"
|
||||
depends on MFD_CROS_EC
|
||||
help
|
||||
If you say yes here you will get support for the
|
||||
Chrome OS Embedded Controller's RTC.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-cros-ec.
|
||||
|
||||
comment "on-CPU RTC drivers"
|
||||
|
||||
config RTC_DRV_ASM9260
|
||||
|
@ -1392,13 +1402,6 @@ config RTC_DRV_PL031
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called rtc-pl031.
|
||||
|
||||
config RTC_DRV_AT32AP700X
|
||||
tristate "AT32AP700X series RTC"
|
||||
depends on PLATFORM_AT32AP || COMPILE_TEST
|
||||
help
|
||||
Driver for the internal RTC (Realtime Clock) on Atmel AVR32
|
||||
AT32AP700x family processors.
|
||||
|
||||
config RTC_DRV_AT91RM9200
|
||||
tristate "AT91RM9200 or some AT91SAM9 RTC"
|
||||
depends on ARCH_AT91 || COMPILE_TEST
|
||||
|
@ -1689,6 +1692,16 @@ config RTC_DRV_MXC
|
|||
This driver can also be built as a module, if so, the module
|
||||
will be called "rtc-mxc".
|
||||
|
||||
config RTC_DRV_MXC_V2
|
||||
tristate "Freescale MXC Real Time Clock for i.MX53"
|
||||
depends on ARCH_MXC
|
||||
help
|
||||
If you say yes here you get support for the Freescale MXC
|
||||
SRTC module in i.MX53 processor.
|
||||
|
||||
This driver can also be built as a module, if so, the module
|
||||
will be called "rtc-mxc_v2".
|
||||
|
||||
config RTC_DRV_SNVS
|
||||
tristate "Freescale SNVS RTC support"
|
||||
select REGMAP_MMIO
|
||||
|
|
|
@ -33,7 +33,6 @@ obj-$(CONFIG_RTC_DRV_AC100) += rtc-ac100.o
|
|||
obj-$(CONFIG_RTC_DRV_ARMADA38X) += rtc-armada38x.o
|
||||
obj-$(CONFIG_RTC_DRV_AS3722) += rtc-as3722.o
|
||||
obj-$(CONFIG_RTC_DRV_ASM9260) += rtc-asm9260.o
|
||||
obj-$(CONFIG_RTC_DRV_AT32AP700X)+= rtc-at32ap700x.o
|
||||
obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
|
||||
obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o
|
||||
obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o
|
||||
|
@ -44,6 +43,7 @@ obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o
|
|||
obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
|
||||
obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o
|
||||
obj-$(CONFIG_RTC_DRV_CPCAP) += rtc-cpcap.o
|
||||
obj-$(CONFIG_RTC_DRV_CROS_EC) += rtc-cros-ec.o
|
||||
obj-$(CONFIG_RTC_DRV_DA9052) += rtc-da9052.o
|
||||
obj-$(CONFIG_RTC_DRV_DA9055) += rtc-da9055.o
|
||||
obj-$(CONFIG_RTC_DRV_DA9063) += rtc-da9063.o
|
||||
|
@ -106,6 +106,7 @@ obj-$(CONFIG_RTC_DRV_MT6397) += rtc-mt6397.o
|
|||
obj-$(CONFIG_RTC_DRV_MT7622) += rtc-mt7622.o
|
||||
obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o
|
||||
obj-$(CONFIG_RTC_DRV_MXC) += rtc-mxc.o
|
||||
obj-$(CONFIG_RTC_DRV_MXC_V2) += rtc-mxc_v2.o
|
||||
obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o
|
||||
obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
|
||||
obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o
|
||||
|
|
|
@ -569,6 +569,12 @@ static int ac100_rtc_probe(struct platform_device *pdev)
|
|||
return chip->irq;
|
||||
}
|
||||
|
||||
chip->rtc = devm_rtc_allocate_device(&pdev->dev);
|
||||
if (IS_ERR(chip->rtc))
|
||||
return PTR_ERR(chip->rtc);
|
||||
|
||||
chip->rtc->ops = &ac100_rtc_ops;
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, chip->irq, NULL,
|
||||
ac100_rtc_irq,
|
||||
IRQF_SHARED | IRQF_ONESHOT,
|
||||
|
@ -588,17 +594,16 @@ static int ac100_rtc_probe(struct platform_device *pdev)
|
|||
/* clear counter alarm pending interrupts */
|
||||
regmap_write(chip->regmap, AC100_ALM_INT_STA, AC100_ALM_INT_ENABLE);
|
||||
|
||||
chip->rtc = devm_rtc_device_register(&pdev->dev, "rtc-ac100",
|
||||
&ac100_rtc_ops, THIS_MODULE);
|
||||
if (IS_ERR(chip->rtc)) {
|
||||
dev_err(&pdev->dev, "unable to register device\n");
|
||||
return PTR_ERR(chip->rtc);
|
||||
}
|
||||
|
||||
ret = ac100_rtc_register_clks(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rtc_register_device(chip->rtc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to register device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "RTC enabled\n");
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -1,287 +0,0 @@
|
|||
/*
|
||||
* An RTC driver for the AVR32 AT32AP700x processor series.
|
||||
*
|
||||
* Copyright (C) 2007 Atmel Corporation
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
/*
|
||||
* This is a bare-bones RTC. It runs during most system sleep states, but has
|
||||
* no battery backup and gets reset during system restart. It must be
|
||||
* initialized from an external clock (network, I2C, etc) before it can be of
|
||||
* much use.
|
||||
*
|
||||
* The alarm functionality is limited by the hardware, not supporting
|
||||
* periodic interrupts.
|
||||
*/
|
||||
|
||||
#define RTC_CTRL 0x00
|
||||
#define RTC_CTRL_EN 0
|
||||
#define RTC_CTRL_PCLR 1
|
||||
#define RTC_CTRL_TOPEN 2
|
||||
#define RTC_CTRL_PSEL 8
|
||||
|
||||
#define RTC_VAL 0x04
|
||||
|
||||
#define RTC_TOP 0x08
|
||||
|
||||
#define RTC_IER 0x10
|
||||
#define RTC_IER_TOPI 0
|
||||
|
||||
#define RTC_IDR 0x14
|
||||
#define RTC_IDR_TOPI 0
|
||||
|
||||
#define RTC_IMR 0x18
|
||||
#define RTC_IMR_TOPI 0
|
||||
|
||||
#define RTC_ISR 0x1c
|
||||
#define RTC_ISR_TOPI 0
|
||||
|
||||
#define RTC_ICR 0x20
|
||||
#define RTC_ICR_TOPI 0
|
||||
|
||||
#define RTC_BIT(name) (1 << RTC_##name)
|
||||
#define RTC_BF(name, value) ((value) << RTC_##name)
|
||||
|
||||
#define rtc_readl(dev, reg) \
|
||||
__raw_readl((dev)->regs + RTC_##reg)
|
||||
#define rtc_writel(dev, reg, value) \
|
||||
__raw_writel((value), (dev)->regs + RTC_##reg)
|
||||
|
||||
struct rtc_at32ap700x {
|
||||
struct rtc_device *rtc;
|
||||
void __iomem *regs;
|
||||
unsigned long alarm_time;
|
||||
unsigned long irq;
|
||||
/* Protect against concurrent register access. */
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static int at32_rtc_readtime(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct rtc_at32ap700x *rtc = dev_get_drvdata(dev);
|
||||
unsigned long now;
|
||||
|
||||
now = rtc_readl(rtc, VAL);
|
||||
rtc_time_to_tm(now, tm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at32_rtc_settime(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct rtc_at32ap700x *rtc = dev_get_drvdata(dev);
|
||||
unsigned long now;
|
||||
int ret;
|
||||
|
||||
ret = rtc_tm_to_time(tm, &now);
|
||||
if (ret == 0)
|
||||
rtc_writel(rtc, VAL, now);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int at32_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct rtc_at32ap700x *rtc = dev_get_drvdata(dev);
|
||||
|
||||
spin_lock_irq(&rtc->lock);
|
||||
rtc_time_to_tm(rtc->alarm_time, &alrm->time);
|
||||
alrm->enabled = rtc_readl(rtc, IMR) & RTC_BIT(IMR_TOPI) ? 1 : 0;
|
||||
alrm->pending = rtc_readl(rtc, ISR) & RTC_BIT(ISR_TOPI) ? 1 : 0;
|
||||
spin_unlock_irq(&rtc->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at32_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct rtc_at32ap700x *rtc = dev_get_drvdata(dev);
|
||||
unsigned long rtc_unix_time;
|
||||
unsigned long alarm_unix_time;
|
||||
int ret;
|
||||
|
||||
rtc_unix_time = rtc_readl(rtc, VAL);
|
||||
|
||||
ret = rtc_tm_to_time(&alrm->time, &alarm_unix_time);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (alarm_unix_time < rtc_unix_time)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irq(&rtc->lock);
|
||||
rtc->alarm_time = alarm_unix_time;
|
||||
rtc_writel(rtc, TOP, rtc->alarm_time);
|
||||
if (alrm->enabled)
|
||||
rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL)
|
||||
| RTC_BIT(CTRL_TOPEN));
|
||||
else
|
||||
rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL)
|
||||
& ~RTC_BIT(CTRL_TOPEN));
|
||||
spin_unlock_irq(&rtc->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int at32_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct rtc_at32ap700x *rtc = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irq(&rtc->lock);
|
||||
|
||||
if (enabled) {
|
||||
if (rtc_readl(rtc, VAL) > rtc->alarm_time) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL)
|
||||
| RTC_BIT(CTRL_TOPEN));
|
||||
rtc_writel(rtc, ICR, RTC_BIT(ICR_TOPI));
|
||||
rtc_writel(rtc, IER, RTC_BIT(IER_TOPI));
|
||||
} else {
|
||||
rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL)
|
||||
& ~RTC_BIT(CTRL_TOPEN));
|
||||
rtc_writel(rtc, IDR, RTC_BIT(IDR_TOPI));
|
||||
rtc_writel(rtc, ICR, RTC_BIT(ICR_TOPI));
|
||||
}
|
||||
out:
|
||||
spin_unlock_irq(&rtc->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t at32_rtc_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct rtc_at32ap700x *rtc = (struct rtc_at32ap700x *)dev_id;
|
||||
unsigned long isr = rtc_readl(rtc, ISR);
|
||||
unsigned long events = 0;
|
||||
int ret = IRQ_NONE;
|
||||
|
||||
spin_lock(&rtc->lock);
|
||||
|
||||
if (isr & RTC_BIT(ISR_TOPI)) {
|
||||
rtc_writel(rtc, ICR, RTC_BIT(ICR_TOPI));
|
||||
rtc_writel(rtc, IDR, RTC_BIT(IDR_TOPI));
|
||||
rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL)
|
||||
& ~RTC_BIT(CTRL_TOPEN));
|
||||
rtc_writel(rtc, VAL, rtc->alarm_time);
|
||||
events = RTC_AF | RTC_IRQF;
|
||||
rtc_update_irq(rtc->rtc, 1, events);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
spin_unlock(&rtc->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops at32_rtc_ops = {
|
||||
.read_time = at32_rtc_readtime,
|
||||
.set_time = at32_rtc_settime,
|
||||
.read_alarm = at32_rtc_readalarm,
|
||||
.set_alarm = at32_rtc_setalarm,
|
||||
.alarm_irq_enable = at32_rtc_alarm_irq_enable,
|
||||
};
|
||||
|
||||
static int __init at32_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *regs;
|
||||
struct rtc_at32ap700x *rtc;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
rtc = devm_kzalloc(&pdev->dev, sizeof(struct rtc_at32ap700x),
|
||||
GFP_KERNEL);
|
||||
if (!rtc)
|
||||
return -ENOMEM;
|
||||
|
||||
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!regs) {
|
||||
dev_dbg(&pdev->dev, "no mmio resource defined\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0) {
|
||||
dev_dbg(&pdev->dev, "could not get irq\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
rtc->irq = irq;
|
||||
rtc->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs));
|
||||
if (!rtc->regs) {
|
||||
dev_dbg(&pdev->dev, "could not map I/O memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
spin_lock_init(&rtc->lock);
|
||||
|
||||
/*
|
||||
* Maybe init RTC: count from zero at 1 Hz, disable wrap irq.
|
||||
*
|
||||
* Do not reset VAL register, as it can hold an old time
|
||||
* from last JTAG reset.
|
||||
*/
|
||||
if (!(rtc_readl(rtc, CTRL) & RTC_BIT(CTRL_EN))) {
|
||||
rtc_writel(rtc, CTRL, RTC_BIT(CTRL_PCLR));
|
||||
rtc_writel(rtc, IDR, RTC_BIT(IDR_TOPI));
|
||||
rtc_writel(rtc, CTRL, RTC_BF(CTRL_PSEL, 0xe)
|
||||
| RTC_BIT(CTRL_EN));
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, at32_rtc_interrupt, IRQF_SHARED,
|
||||
"rtc", rtc);
|
||||
if (ret) {
|
||||
dev_dbg(&pdev->dev, "could not request irq %d\n", irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, rtc);
|
||||
|
||||
rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
|
||||
&at32_rtc_ops, THIS_MODULE);
|
||||
if (IS_ERR(rtc->rtc)) {
|
||||
dev_dbg(&pdev->dev, "could not register rtc device\n");
|
||||
return PTR_ERR(rtc->rtc);
|
||||
}
|
||||
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
|
||||
dev_info(&pdev->dev, "Atmel RTC for AT32AP700x at %08lx irq %ld\n",
|
||||
(unsigned long)rtc->regs, rtc->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __exit at32_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
device_init_wakeup(&pdev->dev, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_ALIAS("platform:at32ap700x_rtc");
|
||||
|
||||
static struct platform_driver at32_rtc_driver = {
|
||||
.remove = __exit_p(at32_rtc_remove),
|
||||
.driver = {
|
||||
.name = "at32ap700x_rtc",
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver_probe(at32_rtc_driver, at32_rtc_probe);
|
||||
|
||||
MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
|
||||
MODULE_DESCRIPTION("Real time clock for AVR32 AT32AP700x");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -253,7 +253,7 @@ static int brcmstb_waketmr_probe(struct platform_device *pdev)
|
|||
ret = devm_request_irq(dev, timer->irq, brcmstb_waketmr_irq, 0,
|
||||
"brcmstb-waketimer", timer);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto err_clk;
|
||||
|
||||
timer->reboot_notifier.notifier_call = brcmstb_waketmr_reboot;
|
||||
register_reboot_notifier(&timer->reboot_notifier);
|
||||
|
@ -262,12 +262,21 @@ static int brcmstb_waketmr_probe(struct platform_device *pdev)
|
|||
&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);
|
||||
ret = PTR_ERR(timer->rtc);
|
||||
goto err_notifier;
|
||||
}
|
||||
|
||||
dev_info(dev, "registered, with irq %d\n", timer->irq);
|
||||
|
||||
return 0;
|
||||
|
||||
err_notifier:
|
||||
unregister_reboot_notifier(&timer->reboot_notifier);
|
||||
|
||||
err_clk:
|
||||
if (timer->clk)
|
||||
clk_disable_unprepare(timer->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,413 @@
|
|||
/*
|
||||
* RTC driver for Chrome OS Embedded Controller
|
||||
*
|
||||
* Copyright (c) 2017, Google, Inc
|
||||
*
|
||||
* Author: Stephen Barber <smbarber@chromium.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/cros_ec.h>
|
||||
#include <linux/mfd/cros_ec_commands.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define DRV_NAME "cros-ec-rtc"
|
||||
|
||||
/**
|
||||
* struct cros_ec_rtc - Driver data for EC RTC
|
||||
*
|
||||
* @cros_ec: Pointer to EC device
|
||||
* @rtc: Pointer to RTC device
|
||||
* @notifier: Notifier info for responding to EC events
|
||||
* @saved_alarm: Alarm to restore when interrupts are reenabled
|
||||
*/
|
||||
struct cros_ec_rtc {
|
||||
struct cros_ec_device *cros_ec;
|
||||
struct rtc_device *rtc;
|
||||
struct notifier_block notifier;
|
||||
u32 saved_alarm;
|
||||
};
|
||||
|
||||
static int cros_ec_rtc_get(struct cros_ec_device *cros_ec, u32 command,
|
||||
u32 *response)
|
||||
{
|
||||
int ret;
|
||||
struct {
|
||||
struct cros_ec_command msg;
|
||||
struct ec_response_rtc data;
|
||||
} __packed msg;
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.msg.command = command;
|
||||
msg.msg.insize = sizeof(msg.data);
|
||||
|
||||
ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg);
|
||||
if (ret < 0) {
|
||||
dev_err(cros_ec->dev,
|
||||
"error getting %s from EC: %d\n",
|
||||
command == EC_CMD_RTC_GET_VALUE ? "time" : "alarm",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*response = msg.data.time;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cros_ec_rtc_set(struct cros_ec_device *cros_ec, u32 command,
|
||||
u32 param)
|
||||
{
|
||||
int ret = 0;
|
||||
struct {
|
||||
struct cros_ec_command msg;
|
||||
struct ec_response_rtc data;
|
||||
} __packed msg;
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.msg.command = command;
|
||||
msg.msg.outsize = sizeof(msg.data);
|
||||
msg.data.time = param;
|
||||
|
||||
ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg);
|
||||
if (ret < 0) {
|
||||
dev_err(cros_ec->dev, "error setting %s on EC: %d\n",
|
||||
command == EC_CMD_RTC_SET_VALUE ? "time" : "alarm",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read the current time from the EC. */
|
||||
static int cros_ec_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev);
|
||||
struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec;
|
||||
int ret;
|
||||
u32 time;
|
||||
|
||||
ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, &time);
|
||||
if (ret) {
|
||||
dev_err(dev, "error getting time: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
rtc_time64_to_tm(time, tm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set the current EC time. */
|
||||
static int cros_ec_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev);
|
||||
struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec;
|
||||
int ret;
|
||||
time64_t time;
|
||||
|
||||
time = rtc_tm_to_time64(tm);
|
||||
if (time < 0 || time > U32_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_VALUE, (u32)time);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "error setting time: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read alarm time from RTC. */
|
||||
static int cros_ec_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev);
|
||||
struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec;
|
||||
int ret;
|
||||
u32 current_time, alarm_offset;
|
||||
|
||||
/*
|
||||
* The EC host command for getting the alarm is relative (i.e. 5
|
||||
* seconds from now) whereas rtc_wkalrm is absolute. Get the current
|
||||
* RTC time first so we can calculate the relative time.
|
||||
*/
|
||||
ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, ¤t_time);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "error getting time: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_ALARM, &alarm_offset);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "error getting alarm: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
rtc_time64_to_tm(current_time + alarm_offset, &alrm->time);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set the EC's RTC alarm. */
|
||||
static int cros_ec_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev);
|
||||
struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec;
|
||||
int ret;
|
||||
time64_t alarm_time;
|
||||
u32 current_time, alarm_offset;
|
||||
|
||||
/*
|
||||
* The EC host command for setting the alarm is relative
|
||||
* (i.e. 5 seconds from now) whereas rtc_wkalrm is absolute.
|
||||
* Get the current RTC time first so we can calculate the
|
||||
* relative time.
|
||||
*/
|
||||
ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, ¤t_time);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "error getting time: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
alarm_time = rtc_tm_to_time64(&alrm->time);
|
||||
|
||||
if (alarm_time < 0 || alarm_time > U32_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
if (!alrm->enabled) {
|
||||
/*
|
||||
* If the alarm is being disabled, send an alarm
|
||||
* clear command.
|
||||
*/
|
||||
alarm_offset = EC_RTC_ALARM_CLEAR;
|
||||
cros_ec_rtc->saved_alarm = (u32)alarm_time;
|
||||
} else {
|
||||
/* Don't set an alarm in the past. */
|
||||
if ((u32)alarm_time < current_time)
|
||||
alarm_offset = EC_RTC_ALARM_CLEAR;
|
||||
else
|
||||
alarm_offset = (u32)alarm_time - current_time;
|
||||
}
|
||||
|
||||
ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, alarm_offset);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "error setting alarm: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cros_ec_rtc_alarm_irq_enable(struct device *dev,
|
||||
unsigned int enabled)
|
||||
{
|
||||
struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev);
|
||||
struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec;
|
||||
int ret;
|
||||
u32 current_time, alarm_offset, alarm_value;
|
||||
|
||||
ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, ¤t_time);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "error getting time: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
/* Restore saved alarm if it's still in the future. */
|
||||
if (cros_ec_rtc->saved_alarm < current_time)
|
||||
alarm_offset = EC_RTC_ALARM_CLEAR;
|
||||
else
|
||||
alarm_offset = cros_ec_rtc->saved_alarm - current_time;
|
||||
|
||||
ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM,
|
||||
alarm_offset);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "error restoring alarm: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
/* Disable alarm, saving the old alarm value. */
|
||||
ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_ALARM,
|
||||
&alarm_offset);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "error saving alarm: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
alarm_value = current_time + alarm_offset;
|
||||
|
||||
/*
|
||||
* If the current EC alarm is already past, we don't want
|
||||
* to set an alarm when we go through the alarm irq enable
|
||||
* path.
|
||||
*/
|
||||
if (alarm_value < current_time)
|
||||
cros_ec_rtc->saved_alarm = EC_RTC_ALARM_CLEAR;
|
||||
else
|
||||
cros_ec_rtc->saved_alarm = alarm_value;
|
||||
|
||||
alarm_offset = EC_RTC_ALARM_CLEAR;
|
||||
ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM,
|
||||
alarm_offset);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "error disabling alarm: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cros_ec_rtc_event(struct notifier_block *nb,
|
||||
unsigned long queued_during_suspend,
|
||||
void *_notify)
|
||||
{
|
||||
struct cros_ec_rtc *cros_ec_rtc;
|
||||
struct rtc_device *rtc;
|
||||
struct cros_ec_device *cros_ec;
|
||||
u32 host_event;
|
||||
|
||||
cros_ec_rtc = container_of(nb, struct cros_ec_rtc, notifier);
|
||||
rtc = cros_ec_rtc->rtc;
|
||||
cros_ec = cros_ec_rtc->cros_ec;
|
||||
|
||||
host_event = cros_ec_get_host_event(cros_ec);
|
||||
if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_RTC)) {
|
||||
rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF);
|
||||
return NOTIFY_OK;
|
||||
} else {
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops cros_ec_rtc_ops = {
|
||||
.read_time = cros_ec_rtc_read_time,
|
||||
.set_time = cros_ec_rtc_set_time,
|
||||
.read_alarm = cros_ec_rtc_read_alarm,
|
||||
.set_alarm = cros_ec_rtc_set_alarm,
|
||||
.alarm_irq_enable = cros_ec_rtc_alarm_irq_enable,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int cros_ec_rtc_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
enable_irq_wake(cros_ec_rtc->cros_ec->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cros_ec_rtc_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
disable_irq_wake(cros_ec_rtc->cros_ec->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(cros_ec_rtc_pm_ops, cros_ec_rtc_suspend,
|
||||
cros_ec_rtc_resume);
|
||||
|
||||
static int cros_ec_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent);
|
||||
struct cros_ec_device *cros_ec = ec_dev->ec_dev;
|
||||
struct cros_ec_rtc *cros_ec_rtc;
|
||||
struct rtc_time tm;
|
||||
int ret;
|
||||
|
||||
cros_ec_rtc = devm_kzalloc(&pdev->dev, sizeof(*cros_ec_rtc),
|
||||
GFP_KERNEL);
|
||||
if (!cros_ec_rtc)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, cros_ec_rtc);
|
||||
cros_ec_rtc->cros_ec = cros_ec;
|
||||
|
||||
/* Get initial time */
|
||||
ret = cros_ec_rtc_read_time(&pdev->dev, &tm);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to read RTC time\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = device_init_wakeup(&pdev->dev, 1);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to initialize wakeup\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
cros_ec_rtc->rtc = devm_rtc_device_register(&pdev->dev, DRV_NAME,
|
||||
&cros_ec_rtc_ops,
|
||||
THIS_MODULE);
|
||||
if (IS_ERR(cros_ec_rtc->rtc)) {
|
||||
ret = PTR_ERR(cros_ec_rtc->rtc);
|
||||
dev_err(&pdev->dev, "failed to register rtc device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get RTC events from the EC. */
|
||||
cros_ec_rtc->notifier.notifier_call = cros_ec_rtc_event;
|
||||
ret = blocking_notifier_chain_register(&cros_ec->event_notifier,
|
||||
&cros_ec_rtc->notifier);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register notifier\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cros_ec_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct cros_ec_rtc *cros_ec_rtc = platform_get_drvdata(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
|
||||
ret = blocking_notifier_chain_unregister(
|
||||
&cros_ec_rtc->cros_ec->event_notifier,
|
||||
&cros_ec_rtc->notifier);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to unregister notifier\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver cros_ec_rtc_driver = {
|
||||
.probe = cros_ec_rtc_probe,
|
||||
.remove = cros_ec_rtc_remove,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.pm = &cros_ec_rtc_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(cros_ec_rtc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("RTC driver for Chrome OS ECs");
|
||||
MODULE_AUTHOR("Stephen Barber <smbarber@chromium.org>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
|
@ -0,0 +1,419 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Real Time Clock (RTC) Driver for i.MX53
|
||||
* Copyright (c) 2004-2011 Freescale Semiconductor, Inc.
|
||||
* Copyright (c) 2017 Beckhoff Automation GmbH & Co. KG
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rtc.h>
|
||||
|
||||
#define SRTC_LPPDR_INIT 0x41736166 /* init for glitch detect */
|
||||
|
||||
#define SRTC_LPCR_EN_LP BIT(3) /* lp enable */
|
||||
#define SRTC_LPCR_WAE BIT(4) /* lp wakeup alarm enable */
|
||||
#define SRTC_LPCR_ALP BIT(7) /* lp alarm flag */
|
||||
#define SRTC_LPCR_NSA BIT(11) /* lp non secure access */
|
||||
#define SRTC_LPCR_NVE BIT(14) /* lp non valid state exit bit */
|
||||
#define SRTC_LPCR_IE BIT(15) /* lp init state exit bit */
|
||||
|
||||
#define SRTC_LPSR_ALP BIT(3) /* lp alarm flag */
|
||||
#define SRTC_LPSR_NVES BIT(14) /* lp non-valid state exit status */
|
||||
#define SRTC_LPSR_IES BIT(15) /* lp init state exit status */
|
||||
|
||||
#define SRTC_LPSCMR 0x00 /* LP Secure Counter MSB Reg */
|
||||
#define SRTC_LPSCLR 0x04 /* LP Secure Counter LSB Reg */
|
||||
#define SRTC_LPSAR 0x08 /* LP Secure Alarm Reg */
|
||||
#define SRTC_LPCR 0x10 /* LP Control Reg */
|
||||
#define SRTC_LPSR 0x14 /* LP Status Reg */
|
||||
#define SRTC_LPPDR 0x18 /* LP Power Supply Glitch Detector Reg */
|
||||
|
||||
/* max. number of retries to read registers, 120 was max during test */
|
||||
#define REG_READ_TIMEOUT 2000
|
||||
|
||||
struct mxc_rtc_data {
|
||||
struct rtc_device *rtc;
|
||||
void __iomem *ioaddr;
|
||||
struct clk *clk;
|
||||
spinlock_t lock; /* protects register access */
|
||||
int irq;
|
||||
};
|
||||
|
||||
/*
|
||||
* This function does write synchronization for writes to the lp srtc block.
|
||||
* To take care of the asynchronous CKIL clock, all writes from the IP domain
|
||||
* will be synchronized to the CKIL domain.
|
||||
* The caller should hold the pdata->lock
|
||||
*/
|
||||
static void mxc_rtc_sync_lp_locked(struct device *dev, void __iomem *ioaddr)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/* Wait for 3 CKIL cycles */
|
||||
for (i = 0; i < 3; i++) {
|
||||
const u32 count = readl(ioaddr + SRTC_LPSCLR);
|
||||
unsigned int timeout = REG_READ_TIMEOUT;
|
||||
|
||||
while ((readl(ioaddr + SRTC_LPSCLR)) == count) {
|
||||
if (!--timeout) {
|
||||
dev_err_once(dev, "SRTC_LPSCLR stuck! Check your hw.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This function is the RTC interrupt service routine. */
|
||||
static irqreturn_t mxc_rtc_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct device *dev = dev_id;
|
||||
struct mxc_rtc_data *pdata = dev_get_drvdata(dev);
|
||||
void __iomem *ioaddr = pdata->ioaddr;
|
||||
unsigned long flags;
|
||||
u32 lp_status;
|
||||
u32 lp_cr;
|
||||
|
||||
spin_lock_irqsave(&pdata->lock, flags);
|
||||
if (clk_enable(pdata->clk)) {
|
||||
spin_unlock_irqrestore(&pdata->lock, flags);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
lp_status = readl(ioaddr + SRTC_LPSR);
|
||||
lp_cr = readl(ioaddr + SRTC_LPCR);
|
||||
|
||||
/* update irq data & counter */
|
||||
if (lp_status & SRTC_LPSR_ALP) {
|
||||
if (lp_cr & SRTC_LPCR_ALP)
|
||||
rtc_update_irq(pdata->rtc, 1, RTC_AF | RTC_IRQF);
|
||||
|
||||
/* disable further lp alarm interrupts */
|
||||
lp_cr &= ~(SRTC_LPCR_ALP | SRTC_LPCR_WAE);
|
||||
}
|
||||
|
||||
/* Update interrupt enables */
|
||||
writel(lp_cr, ioaddr + SRTC_LPCR);
|
||||
|
||||
/* clear interrupt status */
|
||||
writel(lp_status, ioaddr + SRTC_LPSR);
|
||||
|
||||
mxc_rtc_sync_lp_locked(dev, ioaddr);
|
||||
clk_disable(pdata->clk);
|
||||
spin_unlock_irqrestore(&pdata->lock, flags);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable clk and aquire spinlock
|
||||
* @return 0 if successful; non-zero otherwise.
|
||||
*/
|
||||
static int mxc_rtc_lock(struct mxc_rtc_data *const pdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
spin_lock_irq(&pdata->lock);
|
||||
ret = clk_enable(pdata->clk);
|
||||
if (ret) {
|
||||
spin_unlock_irq(&pdata->lock);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxc_rtc_unlock(struct mxc_rtc_data *const pdata)
|
||||
{
|
||||
clk_disable(pdata->clk);
|
||||
spin_unlock_irq(&pdata->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function reads the current RTC time into tm in Gregorian date.
|
||||
*
|
||||
* @param tm contains the RTC time value upon return
|
||||
*
|
||||
* @return 0 if successful; non-zero otherwise.
|
||||
*/
|
||||
static int mxc_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct mxc_rtc_data *pdata = dev_get_drvdata(dev);
|
||||
const int clk_failed = clk_enable(pdata->clk);
|
||||
|
||||
if (!clk_failed) {
|
||||
const time64_t now = readl(pdata->ioaddr + SRTC_LPSCMR);
|
||||
|
||||
rtc_time64_to_tm(now, tm);
|
||||
clk_disable(pdata->clk);
|
||||
return 0;
|
||||
}
|
||||
return clk_failed;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function sets the internal RTC time based on tm in Gregorian date.
|
||||
*
|
||||
* @param tm the time value to be set in the RTC
|
||||
*
|
||||
* @return 0 if successful; non-zero otherwise.
|
||||
*/
|
||||
static int mxc_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct mxc_rtc_data *pdata = dev_get_drvdata(dev);
|
||||
time64_t time = rtc_tm_to_time64(tm);
|
||||
int ret;
|
||||
|
||||
if (time > U32_MAX) {
|
||||
dev_err(dev, "RTC exceeded by %llus\n", time - U32_MAX);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = mxc_rtc_lock(pdata);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
writel(time, pdata->ioaddr + SRTC_LPSCMR);
|
||||
mxc_rtc_sync_lp_locked(dev, pdata->ioaddr);
|
||||
return mxc_rtc_unlock(pdata);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function reads the current alarm value into the passed in \b alrm
|
||||
* argument. It updates the \b alrm's pending field value based on the whether
|
||||
* an alarm interrupt occurs or not.
|
||||
*
|
||||
* @param alrm contains the RTC alarm value upon return
|
||||
*
|
||||
* @return 0 if successful; non-zero otherwise.
|
||||
*/
|
||||
static int mxc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct mxc_rtc_data *pdata = dev_get_drvdata(dev);
|
||||
void __iomem *ioaddr = pdata->ioaddr;
|
||||
int ret;
|
||||
|
||||
ret = mxc_rtc_lock(pdata);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rtc_time_to_tm(readl(ioaddr + SRTC_LPSAR), &alrm->time);
|
||||
alrm->pending = !!(readl(ioaddr + SRTC_LPSR) & SRTC_LPSR_ALP);
|
||||
return mxc_rtc_unlock(pdata);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable/Disable alarm interrupt
|
||||
* The caller should hold the pdata->lock
|
||||
*/
|
||||
static void mxc_rtc_alarm_irq_enable_locked(struct mxc_rtc_data *pdata,
|
||||
unsigned int enable)
|
||||
{
|
||||
u32 lp_cr = readl(pdata->ioaddr + SRTC_LPCR);
|
||||
|
||||
if (enable)
|
||||
lp_cr |= (SRTC_LPCR_ALP | SRTC_LPCR_WAE);
|
||||
else
|
||||
lp_cr &= ~(SRTC_LPCR_ALP | SRTC_LPCR_WAE);
|
||||
|
||||
writel(lp_cr, pdata->ioaddr + SRTC_LPCR);
|
||||
}
|
||||
|
||||
static int mxc_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
|
||||
{
|
||||
struct mxc_rtc_data *pdata = dev_get_drvdata(dev);
|
||||
int ret = mxc_rtc_lock(pdata);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mxc_rtc_alarm_irq_enable_locked(pdata, enable);
|
||||
return mxc_rtc_unlock(pdata);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function sets the RTC alarm based on passed in alrm.
|
||||
*
|
||||
* @param alrm the alarm value to be set in the RTC
|
||||
*
|
||||
* @return 0 if successful; non-zero otherwise.
|
||||
*/
|
||||
static int mxc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
const time64_t time = rtc_tm_to_time64(&alrm->time);
|
||||
struct mxc_rtc_data *pdata = dev_get_drvdata(dev);
|
||||
int ret = mxc_rtc_lock(pdata);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (time > U32_MAX) {
|
||||
dev_err(dev, "Hopefully I am out of service by then :-(\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel((u32)time, pdata->ioaddr + SRTC_LPSAR);
|
||||
|
||||
/* clear alarm interrupt status bit */
|
||||
writel(SRTC_LPSR_ALP, pdata->ioaddr + SRTC_LPSR);
|
||||
mxc_rtc_sync_lp_locked(dev, pdata->ioaddr);
|
||||
|
||||
mxc_rtc_alarm_irq_enable_locked(pdata, alrm->enabled);
|
||||
mxc_rtc_sync_lp_locked(dev, pdata->ioaddr);
|
||||
mxc_rtc_unlock(pdata);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops mxc_rtc_ops = {
|
||||
.read_time = mxc_rtc_read_time,
|
||||
.set_time = mxc_rtc_set_time,
|
||||
.read_alarm = mxc_rtc_read_alarm,
|
||||
.set_alarm = mxc_rtc_set_alarm,
|
||||
.alarm_irq_enable = mxc_rtc_alarm_irq_enable,
|
||||
};
|
||||
|
||||
static int mxc_rtc_wait_for_flag(void *__iomem ioaddr, int flag)
|
||||
{
|
||||
unsigned int timeout = REG_READ_TIMEOUT;
|
||||
|
||||
while (!(readl(ioaddr) & flag)) {
|
||||
if (!--timeout)
|
||||
return -EBUSY;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxc_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mxc_rtc_data *pdata;
|
||||
struct resource *res;
|
||||
void __iomem *ioaddr;
|
||||
int ret = 0;
|
||||
|
||||
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
pdata->ioaddr = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(pdata->ioaddr))
|
||||
return PTR_ERR(pdata->ioaddr);
|
||||
|
||||
ioaddr = pdata->ioaddr;
|
||||
|
||||
pdata->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(pdata->clk)) {
|
||||
dev_err(&pdev->dev, "unable to get rtc clock!\n");
|
||||
return PTR_ERR(pdata->clk);
|
||||
}
|
||||
|
||||
spin_lock_init(&pdata->lock);
|
||||
pdata->irq = platform_get_irq(pdev, 0);
|
||||
if (pdata->irq < 0)
|
||||
return pdata->irq;
|
||||
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
|
||||
ret = clk_prepare_enable(pdata->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
/* initialize glitch detect */
|
||||
writel(SRTC_LPPDR_INIT, ioaddr + SRTC_LPPDR);
|
||||
|
||||
/* clear lp interrupt status */
|
||||
writel(0xFFFFFFFF, ioaddr + SRTC_LPSR);
|
||||
|
||||
/* move out of init state */
|
||||
writel((SRTC_LPCR_IE | SRTC_LPCR_NSA), ioaddr + SRTC_LPCR);
|
||||
ret = mxc_rtc_wait_for_flag(ioaddr + SRTC_LPSR, SRTC_LPSR_IES);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Timeout waiting for SRTC_LPSR_IES\n");
|
||||
clk_disable_unprepare(pdata->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* move out of non-valid state */
|
||||
writel((SRTC_LPCR_IE | SRTC_LPCR_NVE | SRTC_LPCR_NSA |
|
||||
SRTC_LPCR_EN_LP), ioaddr + SRTC_LPCR);
|
||||
ret = mxc_rtc_wait_for_flag(ioaddr + SRTC_LPSR, SRTC_LPSR_NVES);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Timeout waiting for SRTC_LPSR_NVES\n");
|
||||
clk_disable_unprepare(pdata->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
clk_disable(pdata->clk);
|
||||
platform_set_drvdata(pdev, pdata);
|
||||
ret =
|
||||
devm_request_irq(&pdev->dev, pdata->irq, mxc_rtc_interrupt, 0,
|
||||
pdev->name, &pdev->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "interrupt not available.\n");
|
||||
clk_unprepare(pdata->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pdata->rtc =
|
||||
devm_rtc_device_register(&pdev->dev, pdev->name, &mxc_rtc_ops,
|
||||
THIS_MODULE);
|
||||
if (IS_ERR(pdata->rtc)) {
|
||||
clk_unprepare(pdata->clk);
|
||||
return PTR_ERR(pdata->rtc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxc_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mxc_rtc_data *pdata = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable_unprepare(pdata->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mxc_rtc_suspend(struct device *dev)
|
||||
{
|
||||
struct mxc_rtc_data *pdata = dev_get_drvdata(dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
enable_irq_wake(pdata->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxc_rtc_resume(struct device *dev)
|
||||
{
|
||||
struct mxc_rtc_data *pdata = dev_get_drvdata(dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
disable_irq_wake(pdata->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(mxc_rtc_pm_ops, mxc_rtc_suspend, mxc_rtc_resume);
|
||||
|
||||
static const struct of_device_id mxc_ids[] = {
|
||||
{ .compatible = "fsl,imx53-rtc", },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver mxc_rtc_driver = {
|
||||
.driver = {
|
||||
.name = "mxc_rtc_v2",
|
||||
.of_match_table = mxc_ids,
|
||||
.pm = &mxc_rtc_pm_ops,
|
||||
},
|
||||
.probe = mxc_rtc_probe,
|
||||
.remove = mxc_rtc_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(mxc_rtc_driver);
|
||||
|
||||
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||
MODULE_DESCRIPTION("Real Time Clock (RTC) Driver for i.MX53");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -753,8 +753,10 @@ static int omap_rtc_probe(struct platform_device *pdev)
|
|||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
rtc->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(rtc->base))
|
||||
if (IS_ERR(rtc->base)) {
|
||||
clk_disable_unprepare(rtc->clk);
|
||||
return PTR_ERR(rtc->base);
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, rtc);
|
||||
|
||||
|
@ -887,6 +889,7 @@ static int omap_rtc_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
|
||||
err:
|
||||
clk_disable_unprepare(rtc->clk);
|
||||
device_init_wakeup(&pdev->dev, false);
|
||||
rtc->type->lock(rtc);
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
|
|
|
@ -95,7 +95,7 @@ static int rtc7301_wait_while_busy(struct rtc7301_priv *priv)
|
|||
if (!(val & RTC7301_CONTROL_BUSY))
|
||||
return 0;
|
||||
|
||||
usleep_range(200, 300);
|
||||
udelay(300);
|
||||
}
|
||||
|
||||
return -ETIMEDOUT;
|
||||
|
@ -235,7 +235,7 @@ static int rtc7301_set_time(struct device *dev, struct rtc_time *tm)
|
|||
spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
rtc7301_stop(priv);
|
||||
usleep_range(200, 300);
|
||||
udelay(300);
|
||||
rtc7301_select_bank(priv, 0);
|
||||
rtc7301_write_time(priv, tm, false);
|
||||
rtc7301_start(priv);
|
||||
|
|
|
@ -164,17 +164,11 @@ static int r9701_probe(struct spi_device *spi)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int r9701_remove(struct spi_device *spi)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver r9701_driver = {
|
||||
.driver = {
|
||||
.name = "rtc-r9701",
|
||||
},
|
||||
.probe = r9701_probe,
|
||||
.remove = r9701_remove,
|
||||
};
|
||||
|
||||
module_spi_driver(r9701_driver);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Copyright (C) Amelie Delaunay 2016
|
||||
* Author: Amelie Delaunay <amelie.delaunay@st.com>
|
||||
* Copyright (C) STMicroelectronics SA 2017
|
||||
* Author: Amelie Delaunay <amelie.delaunay@st.com> for STMicroelectronics.
|
||||
* License terms: GNU General Public License (GPL), version 2
|
||||
*/
|
||||
|
||||
|
|
|
@ -201,8 +201,10 @@ static void __init sun6i_rtc_clk_init(struct device_node *node)
|
|||
|
||||
clk_data = kzalloc(sizeof(*clk_data) + (sizeof(*clk_data->hws) * 2),
|
||||
GFP_KERNEL);
|
||||
if (!clk_data)
|
||||
if (!clk_data) {
|
||||
kfree(rtc);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_init(&rtc->lock);
|
||||
|
||||
|
|
|
@ -291,6 +291,9 @@ enum host_event_code {
|
|||
/* EC desires to change state of host-controlled USB mux */
|
||||
EC_HOST_EVENT_USB_MUX = 28,
|
||||
|
||||
/* EC RTC event occurred */
|
||||
EC_HOST_EVENT_RTC = 26,
|
||||
|
||||
/*
|
||||
* The high bit of the event mask is not used as a host event code. If
|
||||
* it reads back as set, then the entire event mask should be
|
||||
|
@ -799,6 +802,8 @@ enum ec_feature_code {
|
|||
EC_FEATURE_USB_MUX = 23,
|
||||
/* Motion Sensor code has an internal software FIFO */
|
||||
EC_FEATURE_MOTION_SENSE_FIFO = 24,
|
||||
/* EC has RTC feature that can be controlled by host commands */
|
||||
EC_FEATURE_RTC = 27,
|
||||
};
|
||||
|
||||
#define EC_FEATURE_MASK_0(event_code) (1UL << (event_code % 32))
|
||||
|
@ -1709,6 +1714,9 @@ struct ec_response_rtc {
|
|||
#define EC_CMD_RTC_SET_VALUE 0x46
|
||||
#define EC_CMD_RTC_SET_ALARM 0x47
|
||||
|
||||
/* Pass as param to SET_ALARM to clear the current alarm */
|
||||
#define EC_RTC_ALARM_CLEAR 0
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Port80 log access */
|
||||
|
||||
|
|
|
@ -87,7 +87,6 @@ struct rtc_class_ops {
|
|||
int (*set_offset)(struct device *, long offset);
|
||||
};
|
||||
|
||||
#define RTC_DEVICE_NAME_SIZE 20
|
||||
typedef struct rtc_task {
|
||||
void (*func)(void *private_data);
|
||||
void *private_data;
|
||||
|
|
Загрузка…
Ссылка в новой задаче