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:
Linus Torvalds
2018-02-02 14:19:19 -08:00
18 changed files with 1049 additions and 397 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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");

View File

@@ -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;
}

413
drivers/rtc/rtc-cros-ec.c Normal file
View File

@@ -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, &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);

419
drivers/rtc/rtc-mxc_v2.c Normal file
View File

@@ -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");

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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
*/

View File

@@ -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);