Commit bf644990 authored by Linus Torvalds's avatar Linus Torvalds

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()
parents b89e32cc 4402be2b
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.
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.
* 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 ...@@ -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 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 Check out tools/testing/selftests/timers/rtctest.c for an example usage of the
--------------- ioctl 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!
...@@ -1255,6 +1255,16 @@ config RTC_DRV_ZYNQMP ...@@ -1255,6 +1255,16 @@ config RTC_DRV_ZYNQMP
If you say yes here you get support for the RTC controller found on If you say yes here you get support for the RTC controller found on
Xilinx Zynq Ultrascale+ MPSoC. 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" comment "on-CPU RTC drivers"
config RTC_DRV_ASM9260 config RTC_DRV_ASM9260
...@@ -1392,13 +1402,6 @@ config RTC_DRV_PL031 ...@@ -1392,13 +1402,6 @@ config RTC_DRV_PL031
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called rtc-pl031. 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 config RTC_DRV_AT91RM9200
tristate "AT91RM9200 or some AT91SAM9 RTC" tristate "AT91RM9200 or some AT91SAM9 RTC"
depends on ARCH_AT91 || COMPILE_TEST depends on ARCH_AT91 || COMPILE_TEST
...@@ -1689,6 +1692,16 @@ config RTC_DRV_MXC ...@@ -1689,6 +1692,16 @@ config RTC_DRV_MXC
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-mxc". 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 config RTC_DRV_SNVS
tristate "Freescale SNVS RTC support" tristate "Freescale SNVS RTC support"
select REGMAP_MMIO select REGMAP_MMIO
......
...@@ -33,7 +33,6 @@ obj-$(CONFIG_RTC_DRV_AC100) += rtc-ac100.o ...@@ -33,7 +33,6 @@ obj-$(CONFIG_RTC_DRV_AC100) += rtc-ac100.o
obj-$(CONFIG_RTC_DRV_ARMADA38X) += rtc-armada38x.o obj-$(CONFIG_RTC_DRV_ARMADA38X) += rtc-armada38x.o
obj-$(CONFIG_RTC_DRV_AS3722) += rtc-as3722.o obj-$(CONFIG_RTC_DRV_AS3722) += rtc-as3722.o
obj-$(CONFIG_RTC_DRV_ASM9260) += rtc-asm9260.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_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
...@@ -44,6 +43,7 @@ obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.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_CMOS) += rtc-cmos.o
obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o
obj-$(CONFIG_RTC_DRV_CPCAP) += rtc-cpcap.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_DA9052) += rtc-da9052.o
obj-$(CONFIG_RTC_DRV_DA9055) += rtc-da9055.o obj-$(CONFIG_RTC_DRV_DA9055) += rtc-da9055.o
obj-$(CONFIG_RTC_DRV_DA9063) += rtc-da9063.o obj-$(CONFIG_RTC_DRV_DA9063) += rtc-da9063.o
...@@ -106,6 +106,7 @@ obj-$(CONFIG_RTC_DRV_MT6397) += rtc-mt6397.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_MT7622) += rtc-mt7622.o
obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o
obj-$(CONFIG_RTC_DRV_MXC) += rtc-mxc.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_NUC900) += rtc-nuc900.o
obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o
......
...@@ -569,6 +569,12 @@ static int ac100_rtc_probe(struct platform_device *pdev) ...@@ -569,6 +569,12 @@ static int ac100_rtc_probe(struct platform_device *pdev)
return chip->irq; 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, ret = devm_request_threaded_irq(&pdev->dev, chip->irq, NULL,
ac100_rtc_irq, ac100_rtc_irq,
IRQF_SHARED | IRQF_ONESHOT, IRQF_SHARED | IRQF_ONESHOT,
...@@ -588,17 +594,16 @@ static int ac100_rtc_probe(struct platform_device *pdev) ...@@ -588,17 +594,16 @@ static int ac100_rtc_probe(struct platform_device *pdev)
/* clear counter alarm pending interrupts */ /* clear counter alarm pending interrupts */
regmap_write(chip->regmap, AC100_ALM_INT_STA, AC100_ALM_INT_ENABLE); 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); ret = ac100_rtc_register_clks(chip);
if (ret) if (ret)
return 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"); dev_info(&pdev->dev, "RTC enabled\n");
return 0; return 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) ...@@ -253,7 +253,7 @@ static int brcmstb_waketmr_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, timer->irq, brcmstb_waketmr_irq, 0, ret = devm_request_irq(dev, timer->irq, brcmstb_waketmr_irq, 0,
"brcmstb-waketimer", timer); "brcmstb-waketimer", timer);
if (ret < 0) if (ret < 0)
return ret; goto err_clk;
timer->reboot_notifier.notifier_call = brcmstb_waketmr_reboot; timer->reboot_notifier.notifier_call = brcmstb_waketmr_reboot;
register_reboot_notifier(&timer->reboot_notifier); register_reboot_notifier(&timer->reboot_notifier);
...@@ -262,12 +262,21 @@ static int brcmstb_waketmr_probe(struct platform_device *pdev) ...@@ -262,12 +262,21 @@ static int brcmstb_waketmr_probe(struct platform_device *pdev)
&brcmstb_waketmr_ops, THIS_MODULE); &brcmstb_waketmr_ops, THIS_MODULE);
if (IS_ERR(timer->rtc)) { if (IS_ERR(timer->rtc)) {
dev_err(dev, "unable to register device\n"); dev_err(dev, "unable to register device\n");
unregister_reboot_notifier(&timer->reboot_notifier); ret = PTR_ERR(timer->rtc);
return PTR_ERR(timer->rtc); goto err_notifier;
} }
dev_info(dev, "registered, with irq %d\n", timer->irq); 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; return ret;
} }
......
/*
* 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, &current_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, &current_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, &current_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);
// 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) ...@@ -753,8 +753,10 @@ static int omap_rtc_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
rtc->base = devm_ioremap_resource(&pdev->dev, res); 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); return PTR_ERR(rtc->base);
}
platform_set_drvdata(pdev, rtc); platform_set_drvdata(pdev, rtc);
...@@ -887,6 +889,7 @@ static int omap_rtc_probe(struct platform_device *pdev) ...@@ -887,6 +889,7 @@ static int omap_rtc_probe(struct platform_device *pdev)
return 0; return 0;
err: err:
clk_disable_unprepare(rtc->clk);
device_init_wakeup(&pdev->dev, false); device_init_wakeup(&pdev->dev, false);
rtc->type->lock(rtc); rtc->type->lock(rtc);
pm_runtime_put_sync(&pdev->dev); pm_runtime_put_sync(&pdev->dev);
......
...@@ -95,7 +95,7 @@ static int rtc7301_wait_while_busy(struct rtc7301_priv *priv) ...@@ -95,7 +95,7 @@ static int rtc7301_wait_while_busy(struct rtc7301_priv *priv)
if (!(val & RTC7301_CONTROL_BUSY)) if (!(val & RTC7301_CONTROL_BUSY))
return 0; return 0;
usleep_range(200, 300); udelay(300);
} }
return -ETIMEDOUT; return -ETIMEDOUT;
...@@ -235,7 +235,7 @@ static int rtc7301_set_time(struct device *dev, struct rtc_time *tm) ...@@ -235,7 +235,7 @@ static int rtc7301_set_time(struct device *dev, struct rtc_time *tm)
spin_lock_irqsave(&priv->lock, flags); spin_lock_irqsave(&priv->lock, flags);
rtc7301_stop(priv); rtc7301_stop(priv);
usleep_range(200, 300); udelay(300);
rtc7301_select_bank(priv, 0); rtc7301_select_bank(priv, 0);
rtc7301_write_time(priv, tm, false); rtc7301_write_time(priv, tm, false);
rtc7301_start(priv); rtc7301_start(priv);
......
...@@ -164,17 +164,11 @@ static int r9701_probe(struct spi_device *spi) ...@@ -164,17 +164,11 @@ static int r9701_probe(struct spi_device *spi)
return 0; return 0;
} }
static int r9701_remove(struct spi_device *spi)
{
return 0;
}
static struct spi_driver r9701_driver = { static struct spi_driver r9701_driver = {
.driver = { .driver = {
.name = "rtc-r9701", .name = "rtc-r9701",
}, },
.probe = r9701_probe, .probe = r9701_probe,
.remove = r9701_remove,
}; };
module_spi_driver(r9701_driver); module_spi_driver(r9701_driver);
......
/* /*
* Copyright (C) Amelie Delaunay 2016 * Copyright (C) STMicroelectronics SA 2017
* Author: Amelie Delaunay <amelie.delaunay@st.com> * Author: Amelie Delaunay <amelie.delaunay@st.com> for STMicroelectronics.
* License terms: GNU General Public License (GPL), version 2 * License terms: GNU General Public License (GPL), version 2
*/ */
......
...@@ -201,8 +201,10 @@ static void __init sun6i_rtc_clk_init(struct device_node *node) ...@@ -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), clk_data = kzalloc(sizeof(*clk_data) + (sizeof(*clk_data->hws) * 2),
GFP_KERNEL); GFP_KERNEL);
if (!clk_data) if (!clk_data) {
kfree(rtc);
return; return;
}
spin_lock_init(&rtc->lock); spin_lock_init(&rtc->lock);
......
...@@ -291,6 +291,9 @@ enum host_event_code { ...@@ -291,6 +291,9 @@ enum host_event_code {
/* EC desires to change state of host-controlled USB mux */ /* EC desires to change state of host-controlled USB mux */
EC_HOST_EVENT_USB_MUX = 28, 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 * 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 * it reads back as set, then the entire event mask should be
...@@ -799,6 +802,8 @@ enum ec_feature_code { ...@@ -799,6 +802,8 @@ enum ec_feature_code {
EC_FEATURE_USB_MUX = 23, EC_FEATURE_USB_MUX = 23,
/* Motion Sensor code has an internal software FIFO */ /* Motion Sensor code has an internal software FIFO */
EC_FEATURE_MOTION_SENSE_FIFO = 24, 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)) #define EC_FEATURE_MASK_0(event_code) (1UL << (event_code % 32))
...@@ -1709,6 +1714,9 @@ struct ec_response_rtc { ...@@ -1709,6 +1714,9 @@ struct ec_response_rtc {
#define EC_CMD_RTC_SET_VALUE 0x46 #define EC_CMD_RTC_SET_VALUE 0x46
#define EC_CMD_RTC_SET_ALARM 0x47 #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 */ /* Port80 log access */
......
...@@ -87,7 +87,6 @@ struct rtc_class_ops { ...@@ -87,7 +87,6 @@ struct rtc_class_ops {
int (*set_offset)(struct device *, long offset); int (*set_offset)(struct device *, long offset);
}; };
#define RTC_DEVICE_NAME_SIZE 20
typedef struct rtc_task { typedef struct rtc_task {
void (*func)(void *private_data); void (*func)(void *private_data);
void *private_data; void *private_data;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment