Commit 6a34fdcc authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'rtc-5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux

Pull RTC updates from Alexandre Belloni:
 "The bulk of the patches are about replacing the uie_unsupported struct
  rtc_device member by a feature bit.

  Subsystem:

   - remove uie_unsupported, all users have been converted to clear
     RTC_FEATURE_UPDATE_INTERRUPT and provide a reason

   - RTCs with an alarm with a resolution of a minute are now letting
     the core handle rounding down the alarm time

   - fix use-after-free on device removal

  New driver:

   - OP-TEE RTC PTA

  Drivers:

   - sun6i: Add H616 support

   - cmos: Fix the AltCentury for AMD platforms

   - spear: set range"

* tag 'rtc-5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux: (56 commits)
  rtc: check if __rtc_read_time was successful
  rtc: gamecube: Fix refcount leak in gamecube_rtc_read_offset_from_sram
  rtc: mc146818-lib: Fix the AltCentury for AMD platforms
  rtc: optee: add RTC driver for OP-TEE RTC PTA
  rtc: pm8xxx: Return -ENODEV if set_time disallowed
  rtc: pm8xxx: Attach wake irq to device
  clk: sunxi-ng: sun6i-rtc: include clk/sunxi-ng.h
  rtc: remove uie_unsupported
  rtc: xgene: stop using uie_unsupported
  rtc: hym8563: switch to RTC_FEATURE_UPDATE_INTERRUPT
  rtc: hym8563: let the core handle the alarm resolution
  rtc: hym8563: switch to devm_rtc_allocate_device
  rtc: efi: switch to RTC_FEATURE_UPDATE_INTERRUPT
  rtc: efi: switch to devm_rtc_allocate_device
  rtc: add new RTC_FEATURE_ALARM_WAKEUP_ONLY feature
  rtc: spear: fix spear_rtc_read_time
  rtc: spear: drop uie_unsupported
  rtc: spear: set range
  rtc: spear: switch to devm_rtc_allocate_device
  rtc: pcf8563: switch to RTC_FEATURE_UPDATE_INTERRUPT
  ...
parents e8b767f5 915593a7
......@@ -16,16 +16,22 @@ properties:
compatible:
oneOf:
- const: allwinner,sun6i-a31-rtc
- const: allwinner,sun8i-a23-rtc
- const: allwinner,sun8i-h3-rtc
- const: allwinner,sun8i-r40-rtc
- const: allwinner,sun8i-v3-rtc
- const: allwinner,sun50i-h5-rtc
- enum:
- allwinner,sun6i-a31-rtc
- allwinner,sun8i-a23-rtc
- allwinner,sun8i-h3-rtc
- allwinner,sun8i-r40-rtc
- allwinner,sun8i-v3-rtc
- allwinner,sun50i-h5-rtc
- allwinner,sun50i-h6-rtc
- allwinner,sun50i-h616-rtc
- allwinner,sun50i-r329-rtc
- items:
- const: allwinner,sun50i-a64-rtc
- const: allwinner,sun8i-h3-rtc
- const: allwinner,sun50i-h6-rtc
- items:
- const: allwinner,sun20i-d1-rtc
- const: allwinner,sun50i-r329-rtc
reg:
maxItems: 1
......@@ -37,7 +43,12 @@ properties:
- description: RTC Alarm 1
clocks:
maxItems: 1
minItems: 1
maxItems: 4
clock-names:
minItems: 1
maxItems: 4
clock-output-names:
minItems: 1
......@@ -85,6 +96,7 @@ allOf:
enum:
- allwinner,sun8i-h3-rtc
- allwinner,sun50i-h5-rtc
- allwinner,sun50i-h6-rtc
then:
properties:
......@@ -96,19 +108,68 @@ allOf:
properties:
compatible:
contains:
const: allwinner,sun50i-h6-rtc
const: allwinner,sun50i-h616-rtc
then:
properties:
clock-output-names:
clocks:
minItems: 3
maxItems: 3
items:
- description: Bus clock for register access
- description: 24 MHz oscillator
- description: 32 kHz clock from the CCU
clock-names:
minItems: 3
maxItems: 3
items:
- const: bus
- const: hosc
- const: pll-32k
required:
- clocks
- clock-names
- if:
properties:
compatible:
contains:
const: allwinner,sun8i-r40-rtc
const: allwinner,sun50i-r329-rtc
then:
properties:
clocks:
minItems: 3
maxItems: 4
items:
- description: Bus clock for register access
- description: 24 MHz oscillator
- description: AHB parent for internal SPI clock
- description: External 32768 Hz oscillator
clock-names:
minItems: 3
maxItems: 4
items:
- const: bus
- const: hosc
- const: ahb
- const: ext-osc32k
required:
- clocks
- clock-names
- if:
properties:
compatible:
contains:
enum:
- allwinner,sun8i-r40-rtc
- allwinner,sun50i-h616-rtc
- allwinner,sun50i-r329-rtc
then:
properties:
......@@ -127,7 +188,6 @@ required:
- compatible
- reg
- interrupts
- clock-output-names
additionalProperties: false
......
Atmel AT91SAM9260 Real Time Timer
Required properties:
- compatible: should be one of the following:
- "atmel,at91sam9260-rtt"
- "microchip,sam9x60-rtt", "atmel,at91sam9260-rtt"
- reg: should encode the memory region of the RTT controller
- interrupts: rtt alarm/event interrupt
- clocks: should contain the 32 KHz slow clk that will drive the RTT block.
- atmel,rtt-rtc-time-reg: should encode the GPBR register used to store
the time base when the RTT is used as an RTC.
The first cell should point to the GPBR node and the second one
encode the offset within the GPBR block (or in other words, the
GPBR register used to store the time base).
Example:
rtt@fffffd20 {
compatible = "atmel,at91sam9260-rtt";
reg = <0xfffffd20 0x10>;
interrupts = <1 4 7>;
clocks = <&clk32k>;
atmel,rtt-rtc-time-reg = <&gpbr 0x0>;
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
# Copyright (C) 2022 Microchip Technology, Inc. and its subsidiaries
%YAML 1.2
---
$id: http://devicetree.org/schemas/rtc/atmel,at91sam9260-rtt.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Atmel AT91 RTT Device Tree Bindings
allOf:
- $ref: "rtc.yaml#"
maintainers:
- Alexandre Belloni <alexandre.belloni@bootlin.com>
properties:
compatible:
oneOf:
- items:
- const: atmel,at91sam9260-rtt
- items:
- const: microchip,sam9x60-rtt
- const: atmel,at91sam9260-rtt
- items:
- const: microchip,sama7g5-rtt
- const: microchip,sam9x60-rtt
- const: atmel,at91sam9260-rtt
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
atmel,rtt-rtc-time-reg:
$ref: /schemas/types.yaml#/definitions/phandle-array
items:
- items:
- description: Phandle to the GPBR node.
- description: Offset within the GPBR block.
description:
Should encode the GPBR register used to store the time base when the
RTT is used as an RTC. The first cell should point to the GPBR node
and the second one encodes the offset within the GPBR block (or in
other words, the GPBR register used to store the time base).
required:
- compatible
- reg
- interrupts
- clocks
- atmel,rtt-rtc-time-reg
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
rtc@fffffd20 {
compatible = "atmel,at91sam9260-rtt";
reg = <0xfffffd20 0x10>;
interrupts = <1 IRQ_TYPE_LEVEL_HIGH 7>;
clocks = <&clk32k>;
atmel,rtt-rtc-time-reg = <&gpbr 0x0>;
};
......@@ -14622,6 +14622,12 @@ L: op-tee@lists.trustedfirmware.org
S: Maintained
F: drivers/char/hw_random/optee-rng.c
OP-TEE RTC DRIVER
M: Clément Léger <clement.leger@bootlin.com>
L: linux-rtc@vger.kernel.org
S: Maintained
F: drivers/rtc/rtc-optee.c
OPA-VNIC DRIVER
M: Dennis Dalessandro <dennis.dalessandro@cornelisnetworks.com>
M: Mike Marciniszyn <mike.marciniszyn@cornelisnetworks.com>
......
......@@ -69,6 +69,11 @@ config SUN6I_A31_CCU
default MACH_SUN6I
depends on MACH_SUN6I || COMPILE_TEST
config SUN6I_RTC_CCU
tristate "Support for the Allwinner H616/R329 RTC CCU"
default ARCH_SUNXI
depends on ARCH_SUNXI || COMPILE_TEST
config SUN8I_A23_CCU
tristate "Support for the Allwinner A23 CCU"
default MACH_SUN8I
......
......@@ -36,6 +36,7 @@ obj-$(CONFIG_SUN50I_H616_CCU) += sun50i-h616-ccu.o
obj-$(CONFIG_SUN4I_A10_CCU) += sun4i-a10-ccu.o
obj-$(CONFIG_SUN5I_CCU) += sun5i-ccu.o
obj-$(CONFIG_SUN6I_A31_CCU) += sun6i-a31-ccu.o
obj-$(CONFIG_SUN6I_RTC_CCU) += sun6i-rtc-ccu.o
obj-$(CONFIG_SUN8I_A23_CCU) += sun8i-a23-ccu.o
obj-$(CONFIG_SUN8I_A33_CCU) += sun8i-a33-ccu.o
obj-$(CONFIG_SUN8I_A83T_CCU) += sun8i-a83t-ccu.o
......@@ -60,6 +61,7 @@ sun50i-h616-ccu-y += ccu-sun50i-h616.o
sun4i-a10-ccu-y += ccu-sun4i-a10.o
sun5i-ccu-y += ccu-sun5i.o
sun6i-a31-ccu-y += ccu-sun6i-a31.o
sun6i-rtc-ccu-y += ccu-sun6i-rtc.o
sun8i-a23-ccu-y += ccu-sun8i-a23.o
sun8i-a33-ccu-y += ccu-sun8i-a33.o
sun8i-a83t-ccu-y += ccu-sun8i-a83t.o
......
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _CCU_SUN6I_RTC_H
#define _CCU_SUN6I_RTC_H
#include <dt-bindings/clock/sun6i-rtc.h>
#define CLK_IOSC_32K 3
#define CLK_EXT_OSC32K_GATE 4
#define CLK_OSC24M_32K 5
#define CLK_RTC_32K 6
#define CLK_NUMBER (CLK_RTC_32K + 1)
#endif /* _CCU_SUN6I_RTC_H */
......@@ -17,6 +17,7 @@
#define CCU_FEATURE_LOCK_REG BIT(5)
#define CCU_FEATURE_MMC_TIMING_SWITCH BIT(6)
#define CCU_FEATURE_SIGMA_DELTA_MOD BIT(7)
#define CCU_FEATURE_KEY_FIELD BIT(8)
/* MMC timing mode switch bit */
#define CCU_MMC_NEW_TIMING_MODE BIT(30)
......
......@@ -12,6 +12,8 @@
#include "ccu_gate.h"
#include "ccu_mux.h"
#define CCU_MUX_KEY_VALUE 0x16aa0000
static u16 ccu_mux_get_prediv(struct ccu_common *common,
struct ccu_mux_internal *cm,
int parent_index)
......@@ -191,6 +193,11 @@ int ccu_mux_helper_set_parent(struct ccu_common *common,
spin_lock_irqsave(common->lock, flags);
reg = readl(common->base + common->reg);
/* The key field always reads as zero. */
if (common->features & CCU_FEATURE_KEY_FIELD)
reg |= CCU_MUX_KEY_VALUE;
reg &= ~GENMASK(cm->width + cm->shift - 1, cm->shift);
writel(reg | (index << cm->shift), common->base + common->reg);
......
......@@ -1293,6 +1293,16 @@ config RTC_DRV_OPAL
This driver can also be built as a module. If so, the module
will be called rtc-opal.
config RTC_DRV_OPTEE
tristate "OP-TEE based RTC driver"
depends on OPTEE
help
Select this to get support for OP-TEE based RTC control on SoCs where
RTC are not accessible to the normal world (Linux).
This driver can also be built as a module. If so, the module
will be called rtc-optee.
config RTC_DRV_ZYNQMP
tristate "Xilinx Zynq Ultrascale+ MPSoC RTC"
depends on OF && HAS_IOMEM
......
......@@ -115,6 +115,7 @@ obj-$(CONFIG_RTC_DRV_GAMECUBE) += rtc-gamecube.o
obj-$(CONFIG_RTC_DRV_NTXEC) += rtc-ntxec.o
obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o
obj-$(CONFIG_RTC_DRV_OPTEE) += rtc-optee.o
obj-$(CONFIG_RTC_DRV_PALMAS) += rtc-palmas.o
obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o
obj-$(CONFIG_RTC_DRV_PCF2123) += rtc-pcf2123.o
......
......@@ -26,6 +26,15 @@ struct class *rtc_class;
static void rtc_device_release(struct device *dev)
{
struct rtc_device *rtc = to_rtc_device(dev);
struct timerqueue_head *head = &rtc->timerqueue;
struct timerqueue_node *node;
mutex_lock(&rtc->ops_lock);
while ((node = timerqueue_getnext(head)))
timerqueue_del(head, node);
mutex_unlock(&rtc->ops_lock);
cancel_work_sync(&rtc->irqwork);
ida_simple_remove(&rtc_ida, rtc->id);
mutex_destroy(&rtc->ops_lock);
......@@ -390,9 +399,6 @@ int __devm_rtc_register_device(struct module *owner, struct rtc_device *rtc)
if (!rtc->ops->set_alarm)
clear_bit(RTC_FEATURE_ALARM, rtc->features);
if (rtc->uie_unsupported)
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features);
if (rtc->ops->set_offset)
set_bit(RTC_FEATURE_CORRECTION, rtc->features);
......
......@@ -804,9 +804,13 @@ static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer)
struct timerqueue_node *next = timerqueue_getnext(&rtc->timerqueue);
struct rtc_time tm;
ktime_t now;
int err;
err = __rtc_read_time(rtc, &tm);
if (err)
return err;
timer->enabled = 1;
__rtc_read_time(rtc, &tm);
now = rtc_tm_to_ktime(tm);
/* Skip over expired timers */
......@@ -820,7 +824,6 @@ static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer)
trace_rtc_timer_enqueue(timer);
if (!next || ktime_before(timer->node.expires, next->expires)) {
struct rtc_wkalrm alarm;
int err;
alarm.time = rtc_ktime_to_tm(timer->node.expires);
alarm.enabled = 1;
......
......@@ -1955,7 +1955,7 @@ static int ds1307_probe(struct i2c_client *client,
dev_info(ds1307->dev,
"'wakeup-source' is set, request for an IRQ is disabled!\n");
/* We cannot support UIE mode if we do not have an IRQ line */
ds1307->rtc->uie_unsupported = 1;
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, ds1307->rtc->features);
}
if (want_irq) {
......
......@@ -1273,7 +1273,7 @@ ds1685_rtc_probe(struct platform_device *pdev)
/* See if the platform doesn't support UIE. */
if (pdata->uie_unsupported)
rtc_dev->uie_unsupported = 1;
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc_dev->features);
rtc->dev = rtc_dev;
......@@ -1285,13 +1285,10 @@ ds1685_rtc_probe(struct platform_device *pdev)
* there won't be an automatic way of notifying the kernel about it,
* unless ctrlc is explicitly polled.
*/
if (!pdata->no_irq) {
ret = platform_get_irq(pdev, 0);
if (ret <= 0)
return ret;
rtc->irq_num = ret;
rtc->irq_num = platform_get_irq(pdev, 0);
if (rtc->irq_num <= 0) {
clear_bit(RTC_FEATURE_ALARM, rtc_dev->features);
} else {
/* Request an IRQ. */
ret = devm_request_threaded_irq(&pdev->dev, rtc->irq_num,
NULL, ds1685_rtc_irq_handler,
......@@ -1305,7 +1302,6 @@ ds1685_rtc_probe(struct platform_device *pdev)
rtc->irq_num = 0;
}
}
rtc->no_irq = pdata->no_irq;
/* Setup complete. */
ds1685_rtc_switch_to_bank0(rtc);
......@@ -1394,7 +1390,7 @@ ds1685_rtc_poweroff(struct platform_device *pdev)
* have been taken care of by the shutdown scripts and this
* is the final function call.
*/
if (!rtc->no_irq)
if (rtc->irq_num)
disable_irq_nosync(rtc->irq_num);
/* Oscillator must be on and the countdown chain enabled. */
......
......@@ -261,15 +261,17 @@ static int __init efi_rtc_probe(struct platform_device *dev)
if (efi.get_time(&eft, &cap) != EFI_SUCCESS)
return -ENODEV;
rtc = devm_rtc_device_register(&dev->dev, "rtc-efi", &efi_rtc_ops,
THIS_MODULE);
rtc = devm_rtc_allocate_device(&dev->dev);
if (IS_ERR(rtc))
return PTR_ERR(rtc);
rtc->uie_unsupported = 1;
platform_set_drvdata(dev, rtc);
return 0;
rtc->ops = &efi_rtc_ops;
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features);
set_bit(RTC_FEATURE_ALARM_WAKEUP_ONLY, rtc->features);
return devm_rtc_register_device(rtc);
}
static struct platform_driver efi_rtc_driver = {
......
......@@ -235,6 +235,7 @@ static int gamecube_rtc_read_offset_from_sram(struct priv *d)
}
ret = of_address_to_resource(np, 0, &res);
of_node_put(np);
if (ret) {
pr_err("no io memory range found\n");
return -1;
......
......@@ -220,24 +220,6 @@ static int hym8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
u8 buf[4];
int ret;
/*
* The alarm has no seconds so deal with it
*/
if (alm_tm->tm_sec) {
alm_tm->tm_sec = 0;
alm_tm->tm_min++;
if (alm_tm->tm_min >= 60) {
alm_tm->tm_min = 0;
alm_tm->tm_hour++;
if (alm_tm->tm_hour >= 24) {
alm_tm->tm_hour = 0;
alm_tm->tm_mday++;
if (alm_tm->tm_mday > 31)
alm_tm->tm_mday = 0;
}
}
}
ret = i2c_smbus_read_byte_data(client, HYM8563_CTL2);
if (ret < 0)
return ret;
......@@ -523,6 +505,10 @@ static int hym8563_probe(struct i2c_client *client,
if (!hym8563)
return -ENOMEM;
hym8563->rtc = devm_rtc_allocate_device(&client->dev);
if (IS_ERR(hym8563->rtc))
return PTR_ERR(hym8563->rtc);
hym8563->client = client;
i2c_set_clientdata(client, hym8563);
......@@ -557,19 +543,15 @@ static int hym8563_probe(struct i2c_client *client,
dev_dbg(&client->dev, "rtc information is %s\n",
(ret & HYM8563_SEC_VL) ? "invalid" : "valid");
hym8563->rtc = devm_rtc_device_register(&client->dev, client->name,
&hym8563_rtc_ops, THIS_MODULE);
if (IS_ERR(hym8563->rtc))
return PTR_ERR(hym8563->rtc);
/* the hym8563 alarm only supports a minute accuracy */
hym8563->rtc->uie_unsupported = 1;
hym8563->rtc->ops = &hym8563_rtc_ops;
set_bit(RTC_FEATURE_ALARM_RES_MINUTE, hym8563->rtc->features);
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, hym8563->rtc->features);
#ifdef CONFIG_COMMON_CLK
hym8563_clkout_register_clk(hym8563);
#endif
return 0;
return devm_rtc_register_device(hym8563->rtc);
}
static const struct i2c_device_id hym8563_id[] = {
......
......@@ -932,10 +932,8 @@ static int m41t80_probe(struct i2c_client *client,
m41t80_data->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
m41t80_data->rtc->range_max = RTC_TIMESTAMP_END_2099;
if (client->irq <= 0) {
/* We cannot support UIE mode if we do not have an IRQ line */
m41t80_data->rtc->uie_unsupported = 1;
}
if (client->irq <= 0)
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, m41t80_data->rtc->features);
/* Make sure HT (Halt Update) bit is cleared */
rc = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_HOUR);
......
......@@ -176,6 +176,17 @@ int mc146818_get_time(struct rtc_time *time)
}
EXPORT_SYMBOL_GPL(mc146818_get_time);
/* AMD systems don't allow access to AltCentury with DV1 */
static bool apply_amd_register_a_behavior(void)
{
#ifdef CONFIG_X86
if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD ||
boot_cpu_data.x86_vendor == X86_VENDOR_HYGON)
return true;
#endif
return false;
}
/* Set the current date and time in the real time clock. */
int mc146818_set_time(struct rtc_time *time)
{
......@@ -232,8 +243,10 @@ int mc146818_set_time(struct rtc_time *time)
if (yrs >= 100)
yrs -= 100;
if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY)
|| RTC_ALWAYS_BCD) {
spin_lock_irqsave(&rtc_lock, flags);
save_control = CMOS_READ(RTC_CONTROL);
spin_unlock_irqrestore(&rtc_lock, flags);
if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
sec = bin2bcd(sec);
min = bin2bcd(min);
hrs = bin2bcd(hrs);
......@@ -247,7 +260,10 @@ int mc146818_set_time(struct rtc_time *time)
save_control = CMOS_READ(RTC_CONTROL);
CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
save_freq_select = CMOS_READ(RTC_FREQ_SELECT);
CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
if (apply_amd_register_a_behavior())
CMOS_WRITE((save_freq_select & ~RTC_AMD_BANK_SELECT), RTC_FREQ_SELECT);
else
CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
#ifdef CONFIG_MACH_DECSTATION
CMOS_WRITE(real_yrs, RTC_DEC_YEAR);
......
......@@ -210,20 +210,6 @@ static int mpc5121_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
/*
* the alarm has no seconds so deal with it
*/
if (alarm->time.tm_sec) {
alarm->time.tm_sec = 0;
alarm->time.tm_min++;
if (alarm->time.tm_min >= 60) {
alarm->time.tm_min = 0;
alarm->time.tm_hour++;
if (alarm->time.tm_hour >= 24)
alarm->time.tm_hour = 0;
}
}
alarm->time.tm_mday = -1;
alarm->time.tm_mon = -1;
alarm->time.tm_year = -1;
......@@ -349,7 +335,8 @@ static int mpc5121_rtc_probe(struct platform_device *op)
}
rtc->rtc->ops = &mpc5200_rtc_ops;
rtc->rtc->uie_unsupported = 1;
set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->rtc->features);
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->rtc->features);
rtc->rtc->range_min = RTC_TIMESTAMP_BEGIN_0000;
rtc->rtc->range_max = 65733206399ULL; /* 4052-12-31 23:59:59 */
......
......@@ -250,7 +250,7 @@ static int opal_rtc_probe(struct platform_device *pdev)
rtc->ops = &opal_rtc_ops;
rtc->range_min = RTC_TIMESTAMP_BEGIN_0000;
rtc->range_max = RTC_TIMESTAMP_END_9999;
rtc->uie_unsupported = 1;
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features);
return devm_rtc_register_device(rtc);
}
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2022 Microchip.
*/
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/rtc.h>
#include <linux/tee_drv.h>
#define RTC_INFO_VERSION 0x1
#define TA_CMD_RTC_GET_INFO 0x0
#define TA_CMD_RTC_GET_TIME 0x1
#define TA_CMD_RTC_SET_TIME 0x2
#define TA_CMD_RTC_GET_OFFSET 0x3
#define TA_CMD_RTC_SET_OFFSET 0x4
#define TA_RTC_FEATURE_CORRECTION BIT(0)
struct optee_rtc_time {
u32 tm_sec;
u32 tm_min;
u32 tm_hour;
u32 tm_mday;
u32 tm_mon;
u32 tm_year;
u32 tm_wday;
};
struct optee_rtc_info {
u64 version;
u64 features;
struct optee_rtc_time range_min;
struct optee_rtc_time range_max;
};
/**
* struct optee_rtc - OP-TEE RTC private data
* @dev: OP-TEE based RTC device.
* @ctx: OP-TEE context handler.
* @session_id: RTC TA session identifier.
* @shm: Memory pool shared with RTC device.
* @features: Bitfield of RTC features
*/
struct optee_rtc {
struct device *dev;
struct tee_context *ctx;
u32 session_id;
struct tee_shm *shm;
u64 features;
};
static int optee_rtc_readtime(struct device *dev, struct rtc_time *tm)
{
struct optee_rtc *priv = dev_get_drvdata(dev);
struct tee_ioctl_invoke_arg inv_arg = {0};
struct optee_rtc_time *optee_tm;
struct tee_param param[4] = {0};
int ret;
inv_arg.func = TA_CMD_RTC_GET_TIME;
inv_arg.session = priv->session_id;
inv_arg.num_params = 4;
/* Fill invoke cmd params */
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;
param[0].u.memref.shm = priv->shm;
param[0].u.memref.size = sizeof(struct optee_rtc_time);
ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);
if (ret < 0 || inv_arg.ret != 0)
return ret ? ret : -EPROTO;
optee_tm = tee_shm_get_va(priv->shm, 0);
if (IS_ERR(optee_tm))
return PTR_ERR(optee_tm);
if (param[0].u.memref.size != sizeof(*optee_tm))
return -EPROTO;
tm->tm_sec = optee_tm->tm_sec;
tm->tm_min = optee_tm->tm_min;
tm->tm_hour = optee_tm->tm_hour;
tm->tm_mday = optee_tm->tm_mday;
tm->tm_mon = optee_tm->tm_mon;
tm->tm_year = optee_tm->tm_year - 1900;
tm->tm_wday = optee_tm->tm_wday;
tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
return 0;
}
static int optee_rtc_settime(struct device *dev, struct rtc_time *tm)
{
struct optee_rtc *priv = dev_get_drvdata(dev);
struct tee_ioctl_invoke_arg inv_arg = {0};
struct tee_param param[4] = {0};
struct optee_rtc_time optee_tm;
void *rtc_data;
int ret;
optee_tm.tm_sec = tm->tm_sec;
optee_tm.tm_min = tm->tm_min;
optee_tm.tm_hour = tm->tm_hour;
optee_tm.tm_mday = tm->tm_mday;
optee_tm.tm_mon = tm->tm_mon;
optee_tm.tm_year = tm->tm_year + 1900;
optee_tm.tm_wday = tm->tm_wday;
inv_arg.func = TA_CMD_RTC_SET_TIME;
inv_arg.session = priv->session_id;
inv_arg.num_params = 4;
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
param[0].u.memref.shm = priv->shm;
param[0].u.memref.size = sizeof(struct optee_rtc_time);
rtc_data = tee_shm_get_va(priv->shm, 0);
if (IS_ERR(rtc_data))
return PTR_ERR(rtc_data);
memcpy(rtc_data, &optee_tm, sizeof(struct optee_rtc_time));
ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);
if (ret < 0 || inv_arg.ret != 0)
return ret ? ret : -EPROTO;
return 0;
}
static int optee_rtc_readoffset(struct device *dev, long *offset)
{
struct optee_rtc *priv = dev_get_drvdata(dev);
struct tee_ioctl_invoke_arg inv_arg = {0};
struct tee_param param[4] = {0};
int ret;
if (!(priv->features & TA_RTC_FEATURE_CORRECTION))
return -EOPNOTSUPP;
inv_arg.func = TA_CMD_RTC_GET_OFFSET;
inv_arg.session = priv->session_id;
inv_arg.num_params = 4;
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);
if (ret < 0 || inv_arg.ret != 0)
return ret ? ret : -EPROTO;
*offset = param[0].u.value.a;
return 0;
}
static int optee_rtc_setoffset(struct device *dev, long offset)
{
struct optee_rtc *priv = dev_get_drvdata(dev);
struct tee_ioctl_invoke_arg inv_arg = {0};
struct tee_param param[4] = {0};
int ret;
if (!(priv->features & TA_RTC_FEATURE_CORRECTION))
return -EOPNOTSUPP;
inv_arg.func = TA_CMD_RTC_SET_OFFSET;
inv_arg.session = priv->session_id;
inv_arg.num_params = 4;
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
param[0].u.value.a = offset;
ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);
if (ret < 0 || inv_arg.ret != 0)
return ret ? ret : -EPROTO;
return 0;
}
static const struct rtc_class_ops optee_rtc_ops = {
.read_time = optee_rtc_readtime,
.set_time = optee_rtc_settime,
.set_offset = optee_rtc_setoffset,
.read_offset = optee_rtc_readoffset,
};
static int optee_rtc_read_info(struct device *dev, struct rtc_device *rtc,
u64 *features)
{
struct optee_rtc *priv = dev_get_drvdata(dev);
struct tee_ioctl_invoke_arg inv_arg = {0};
struct tee_param param[4] = {0};
struct optee_rtc_info *info;
struct optee_rtc_time *tm;
int ret;
inv_arg.func = TA_CMD_RTC_GET_INFO;
inv_arg.session = priv->session_id;
inv_arg.num_params = 4;
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;
param[0].u.memref.shm = priv->shm;
param[0].u.memref.size = sizeof(*info);
ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);
if (ret < 0 || inv_arg.ret != 0)
return ret ? ret : -EPROTO;
info = tee_shm_get_va(priv->shm, 0);
if (IS_ERR(info))
return PTR_ERR(info);
if (param[0].u.memref.size != sizeof(*info))
return -EPROTO;
if (info->version != RTC_INFO_VERSION)
return -EPROTO;
*features = info->features;
tm = &info->range_min;
rtc->range_min = mktime64(tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min,
tm->tm_sec);
tm = &info->range_max;
rtc->range_max = mktime64(tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min,
tm->tm_sec);
return 0;
}
static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
{
if (ver->impl_id == TEE_IMPL_ID_OPTEE)
return 1;
else
return 0;
}
static int optee_rtc_probe(struct device *dev)
{
struct tee_client_device *rtc_device = to_tee_client_device(dev);
struct tee_ioctl_open_session_arg sess_arg;
struct optee_rtc *priv;
struct rtc_device *rtc;
struct tee_shm *shm;
int ret, err;
memset(&sess_arg, 0, sizeof(sess_arg));
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
rtc = devm_rtc_allocate_device(dev);
if (IS_ERR(rtc))
return PTR_ERR(rtc);
/* Open context with TEE driver */
priv->ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, NULL);
if (IS_ERR(priv->ctx))
return -ENODEV;
/* Open session with rtc Trusted App */
export_uuid(sess_arg.uuid, &rtc_device->id.uuid);
sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL;
ret = tee_client_open_session(priv->ctx, &sess_arg, NULL);
if (ret < 0 || sess_arg.ret != 0) {
dev_err(dev, "tee_client_open_session failed, err: %x\n", sess_arg.ret);
err = -EINVAL;
goto out_ctx;
}
priv->session_id = sess_arg.session;
shm = tee_shm_alloc_kernel_buf(priv->ctx, sizeof(struct optee_rtc_info));
if (IS_ERR(shm)) {
dev_err(priv->dev, "tee_shm_alloc_kernel_buf failed\n");
err = PTR_ERR(shm);
goto out_sess;
}
priv->shm = shm;
priv->dev = dev;
dev_set_drvdata(dev, priv);
rtc->ops = &optee_rtc_ops;
err = optee_rtc_read_info(dev, rtc, &priv->features);
if (err) {
dev_err(dev, "Failed to get RTC features from OP-TEE\n");
goto out_shm;
}
err = devm_rtc_register_device(rtc);
if (err)
goto out_shm;
/*
* We must clear this bit after registering because rtc_register_device
* will set it if it sees that .set_offset is provided.
*/
if (!(priv->features & TA_RTC_FEATURE_CORRECTION))
clear_bit(RTC_FEATURE_CORRECTION, rtc->features);
return 0;
out_shm:
tee_shm_free(priv->shm);
out_sess:
tee_client_close_session(priv->ctx, priv->session_id);
out_ctx:
tee_client_close_context(priv->ctx);
return err;
}
static int optee_rtc_remove(struct device *dev)
{
struct optee_rtc *priv = dev_get_drvdata(dev);
tee_client_close_session(priv->ctx, priv->session_id);
tee_client_close_context(priv->ctx);
return 0;
}
static const struct tee_client_device_id optee_rtc_id_table[] = {
{UUID_INIT(0xf389f8c8, 0x845f, 0x496c,
0x8b, 0xbe, 0xd6, 0x4b, 0xd2, 0x4c, 0x92, 0xfd)},
{}
};
MODULE_DEVICE_TABLE(tee, optee_rtc_id_table);
static struct tee_client_driver optee_rtc_driver = {
.id_table = optee_rtc_id_table,
.driver = {
.name = "optee_rtc",
.bus = &tee_bus_type,
.probe = optee_rtc_probe,
.remove = optee_rtc_remove,
},
};
static int __init optee_rtc_mod_init(void)
{
return driver_register(&optee_rtc_driver.driver);
}
static void __exit optee_rtc_mod_exit(void)
{
driver_unregister(&optee_rtc_driver.driver);
}
module_init(optee_rtc_mod_init);
module_exit(optee_rtc_mod_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Clément Léger <clement.leger@bootlin.com>");
MODULE_DESCRIPTION("OP-TEE based RTC driver");
......@@ -427,7 +427,8 @@ static int pcf2123_probe(struct spi_device *spi)
* support to this driver to generate interrupts more than once
* per minute.
*/
rtc->uie_unsupported = 1;
set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->features);
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features);
rtc->ops = &pcf2123_rtc_ops;
rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
rtc->range_max = RTC_TIMESTAMP_END_2099;
......
......@@ -374,7 +374,8 @@ static int pcf2127_watchdog_init(struct device *dev, struct pcf2127 *pcf2127)
static int pcf2127_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
unsigned int buf[5], ctrl2;
u8 buf[5];
unsigned int ctrl2;
int ret;
ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL2, &ctrl2);
......@@ -655,13 +656,25 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
pcf2127->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
pcf2127->rtc->range_max = RTC_TIMESTAMP_END_2099;
pcf2127->rtc->set_start_time = true; /* Sets actual start to 1970 */
pcf2127->rtc->uie_unsupported = 1;
set_bit(RTC_FEATURE_ALARM_RES_2S, pcf2127->rtc->features);
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, pcf2127->rtc->features);
clear_bit(RTC_FEATURE_ALARM, pcf2127->rtc->features);
if (alarm_irq > 0) {
unsigned long flags;
/*
* If flags = 0, devm_request_threaded_irq() will use IRQ flags
* obtained from device tree.
*/
if (dev_fwnode(dev))
flags = 0;
else
flags = IRQF_TRIGGER_LOW;
ret = devm_request_threaded_irq(dev, alarm_irq, NULL,
pcf2127_rtc_irq,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
flags | IRQF_ONESHOT,
dev_name(dev), dev);
if (ret) {
dev_err(dev, "failed to request alarm irq\n");
......
......@@ -616,7 +616,8 @@ static int pcf85063_probe(struct i2c_client *client)
pcf85063->rtc->ops = &pcf85063_rtc_ops;
pcf85063->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
pcf85063->rtc->range_max = RTC_TIMESTAMP_END_2099;
pcf85063->rtc->uie_unsupported = 1;
set_bit(RTC_FEATURE_ALARM_RES_2S, pcf85063->rtc->features);
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, pcf85063->rtc->features);
clear_bit(RTC_FEATURE_ALARM, pcf85063->rtc->features);
if (config->has_alarms && client->irq > 0) {
......
......@@ -212,14 +212,6 @@ static int pcf8523_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm)
if (err < 0)
return err;
/* The alarm has no seconds, round up to nearest minute */
if (tm->time.tm_sec) {
time64_t alarm_time = rtc_tm_to_time64(&tm->time);
alarm_time += 60 - tm->time.tm_sec;
rtc_time64_to_tm(alarm_time, &tm->time);
}
regs[0] = bin2bcd(tm->time.tm_min);
regs[1] = bin2bcd(tm->time.tm_hour);
regs[2] = bin2bcd(tm->time.tm_mday);
......@@ -240,9 +232,9 @@ static int pcf8523_param_get(struct device *dev, struct rtc_param *param)
{
struct pcf8523 *pcf8523 = dev_get_drvdata(dev);
int ret;
u32 value;
switch(param->param) {
u32 value;
case RTC_PARAM_BACKUP_SWITCH_MODE:
ret = regmap_read(pcf8523->regmap, PCF8523_REG_CONTROL3, &value);
......@@ -279,9 +271,9 @@ static int pcf8523_param_get(struct device *dev, struct rtc_param *param)
static int pcf8523_param_set(struct device *dev, struct rtc_param *param)
{
struct pcf8523 *pcf8523 = dev_get_drvdata(dev);
u8 mode;
switch(param->param) {
u8 mode;
case RTC_PARAM_BACKUP_SWITCH_MODE:
switch (param->uvalue) {
case RTC_BSM_DISABLED:
......@@ -450,7 +442,8 @@ static int pcf8523_probe(struct i2c_client *client,
rtc->ops = &pcf8523_rtc_ops;
rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
rtc->range_max = RTC_TIMESTAMP_END_2099;
rtc->uie_unsupported = 1;
set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->features);
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features);
if (client->irq > 0) {
err = regmap_write(pcf8523->regmap, PCF8523_TMR_CLKOUT_CTRL, 0x38);
......
......@@ -330,19 +330,6 @@ static int pcf8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm)
unsigned char buf[4];
int err;
/* The alarm has no seconds, round up to nearest minute */
if (tm->time.tm_sec) {
time64_t alarm_time = rtc_tm_to_time64(&tm->time);
alarm_time += 60 - tm->time.tm_sec;
rtc_time64_to_tm(alarm_time, &tm->time);
}
dev_dbg(dev, "%s, min=%d hour=%d wday=%d mday=%d "
"enabled=%d pending=%d\n", __func__,
tm->time.tm_min, tm->time.tm_hour, tm->time.tm_wday,
tm->time.tm_mday, tm->enabled, tm->pending);
buf[0] = bin2bcd(tm->time.tm_min);
buf[1] = bin2bcd(tm->time.tm_hour);
buf[2] = bin2bcd(tm->time.tm_mday);
......@@ -565,7 +552,8 @@ static int pcf8563_probe(struct i2c_client *client,
pcf8563->rtc->ops = &pcf8563_rtc_ops;
/* the pcf8563 alarm only supports a minute accuracy */
pcf8563->rtc->uie_unsupported = 1;
set_bit(RTC_FEATURE_ALARM_RES_MINUTE, pcf8563->rtc->features);
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, pcf8563->rtc->features);
pcf8563->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
pcf8563->rtc->range_max = RTC_TIMESTAMP_END_2099;
pcf8563->rtc->set_start_time = true;
......
......@@ -350,9 +350,6 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
}
}
if (!adev->irq[0])
clear_bit(RTC_FEATURE_ALARM, ldata->rtc->features);
device_init_wakeup(&adev->dev, true);
ldata->rtc = devm_rtc_allocate_device(&adev->dev);
if (IS_ERR(ldata->rtc)) {
......@@ -360,6 +357,9 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
goto out;
}
if (!adev->irq[0])
clear_bit(RTC_FEATURE_ALARM, ldata->rtc->features);
ldata->rtc->ops = ops;
ldata->rtc->range_min = vendor->range_min;
ldata->rtc->range_max = vendor->range_max;
......
......@@ -7,6 +7,7 @@
#include <linux/rtc.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/pm_wakeirq.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
......@@ -83,7 +84,7 @@ static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm)
const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
if (!rtc_dd->allow_set_time)
return -EACCES;
return -ENODEV;
secs = rtc_tm_to_time64(tm);
......@@ -527,40 +528,28 @@ static int pm8xxx_rtc_probe(struct platform_device *pdev)
return rc;
}
return devm_rtc_register_device(rtc_dd->rtc);
}
#ifdef CONFIG_PM_SLEEP
static int pm8xxx_rtc_resume(struct device *dev)
{
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
rc = devm_rtc_register_device(rtc_dd->rtc);
if (rc)
return rc;
if (device_may_wakeup(dev))
disable_irq_wake(rtc_dd->rtc_alarm_irq);
rc = dev_pm_set_wake_irq(&pdev->dev, rtc_dd->rtc_alarm_irq);
if (rc)
return rc;
return 0;
}
static int pm8xxx_rtc_suspend(struct device *dev)
static int pm8xxx_remove(struct platform_device *pdev)
{
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
enable_irq_wake(rtc_dd->rtc_alarm_irq);
dev_pm_clear_wake_irq(&pdev->dev);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(pm8xxx_rtc_pm_ops,
pm8xxx_rtc_suspend,
pm8xxx_rtc_resume);
static struct platform_driver pm8xxx_rtc_driver = {
.probe = pm8xxx_rtc_probe,
.remove = pm8xxx_remove,
.driver = {
.name = "rtc-pm8xxx",
.pm = &pm8xxx_rtc_pm_ops,
.of_match_table = pm8xxx_id_table,
},
};
......
......@@ -204,8 +204,10 @@ static int spear_rtc_read_time(struct device *dev, struct rtc_time *tm)
/* we don't report wday/yday/isdst ... */
rtc_wait_not_busy(config);
time = readl(config->ioaddr + TIME_REG);
date = readl(config->ioaddr + DATE_REG);
do {
time = readl(config->ioaddr + TIME_REG);
date = readl(config->ioaddr + DATE_REG);
} while (time == readl(config->ioaddr + TIME_REG));
tm->tm_sec = (time >> SECOND_SHIFT) & SECOND_MASK;
tm->tm_min = (time >> MINUTE_SHIFT) & MIN_MASK;
tm->tm_hour = (time >> HOUR_SHIFT) & HOUR_MASK;
......@@ -352,6 +354,10 @@ static int spear_rtc_probe(struct platform_device *pdev)
if (!config)
return -ENOMEM;
config->rtc = devm_rtc_allocate_device(&pdev->dev);
if (IS_ERR(config->rtc))
return PTR_ERR(config->rtc);
/* alarm irqs */
irq = platform_get_irq(pdev, 0);
if (irq < 0)
......@@ -380,16 +386,13 @@ static int spear_rtc_probe(struct platform_device *pdev)
spin_lock_init(&config->lock);
platform_set_drvdata(pdev, config);
config->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
&spear_rtc_ops, THIS_MODULE);
if (IS_ERR(config->rtc)) {
dev_err(&pdev->dev, "can't register RTC device, err %ld\n",
PTR_ERR(config->rtc));
status = PTR_ERR(config->rtc);
goto err_disable_clock;
}
config->rtc->ops = &spear_rtc_ops;
config->rtc->range_min = RTC_TIMESTAMP_BEGIN_0000;
config->rtc->range_min = RTC_TIMESTAMP_END_9999;
config->rtc->uie_unsupported = 1;
status = devm_rtc_register_device(config->rtc);
if (status)
goto err_disable_clock;
if (!device_can_wakeup(&pdev->dev))
device_init_wakeup(&pdev->dev, 1);
......
......@@ -13,6 +13,7 @@
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/clk/sunxi-ng.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/fs.h>
......@@ -48,7 +49,8 @@
/* Alarm 0 (counter) */
#define SUN6I_ALRM_COUNTER 0x0020
#define SUN6I_ALRM_CUR_VAL 0x0024
/* This holds the remaining alarm seconds on older SoCs (current value) */
#define SUN6I_ALRM_COUNTER_HMS 0x0024
#define SUN6I_ALRM_EN 0x0028
#define SUN6I_ALRM_EN_CNT_EN BIT(0)
#define SUN6I_ALRM_IRQ_EN 0x002c
......@@ -110,6 +112,8 @@
#define SUN6I_YEAR_MIN 1970
#define SUN6I_YEAR_OFF (SUN6I_YEAR_MIN - 1900)
#define SECS_PER_DAY (24 * 3600ULL)
/*
* There are other differences between models, including:
*
......@@ -133,12 +137,15 @@ struct sun6i_rtc_clk_data {
unsigned int has_auto_swt : 1;
};
#define RTC_LINEAR_DAY BIT(0)
struct sun6i_rtc_dev {
struct rtc_device *rtc;
const struct sun6i_rtc_clk_data *data;
void __iomem *base;
int irq;
unsigned long alarm;
time64_t alarm;
unsigned long flags;
struct clk_hw hw;
struct clk_hw *int_osc;
......@@ -363,23 +370,6 @@ CLK_OF_DECLARE_DRIVER(sun8i_h3_rtc_clk, "allwinner,sun8i-h3-rtc",
CLK_OF_DECLARE_DRIVER(sun50i_h5_rtc_clk, "allwinner,sun50i-h5-rtc",
sun8i_h3_rtc_clk_init);
static const struct sun6i_rtc_clk_data sun50i_h6_rtc_data = {
.rc_osc_rate = 16000000,
.fixed_prescaler = 32,
.has_prescaler = 1,
.has_out_clk = 1,
.export_iosc = 1,
.has_losc_en = 1,
.has_auto_swt = 1,
};
static void __init sun50i_h6_rtc_clk_init(struct device_node *node)
{
sun6i_rtc_clk_init(node, &sun50i_h6_rtc_data);
}
CLK_OF_DECLARE_DRIVER(sun50i_h6_rtc_clk, "allwinner,sun50i-h6-rtc",
sun50i_h6_rtc_clk_init);
/*
* The R40 user manual is self-conflicting on whether the prescaler is
* fixed or configurable. The clock diagram shows it as fixed, but there
......@@ -467,22 +457,30 @@ static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
} while ((date != readl(chip->base + SUN6I_RTC_YMD)) ||
(time != readl(chip->base + SUN6I_RTC_HMS)));
if (chip->flags & RTC_LINEAR_DAY) {
/*
* Newer chips store a linear day number, the manual
* does not mandate any epoch base. The BSP driver uses
* the UNIX epoch, let's just copy that, as it's the
* easiest anyway.
*/
rtc_time64_to_tm((date & 0xffff) * SECS_PER_DAY, rtc_tm);
} else {
rtc_tm->tm_mday = SUN6I_DATE_GET_DAY_VALUE(date);
rtc_tm->tm_mon = SUN6I_DATE_GET_MON_VALUE(date) - 1;
rtc_tm->tm_year = SUN6I_DATE_GET_YEAR_VALUE(date);
/*
* switch from (data_year->min)-relative offset to
* a (1900)-relative one
*/
rtc_tm->tm_year += SUN6I_YEAR_OFF;
}
rtc_tm->tm_sec = SUN6I_TIME_GET_SEC_VALUE(time);
rtc_tm->tm_min = SUN6I_TIME_GET_MIN_VALUE(time);
rtc_tm->tm_hour = SUN6I_TIME_GET_HOUR_VALUE(time);
rtc_tm->tm_mday = SUN6I_DATE_GET_DAY_VALUE(date);
rtc_tm->tm_mon = SUN6I_DATE_GET_MON_VALUE(date);
rtc_tm->tm_year = SUN6I_DATE_GET_YEAR_VALUE(date);
rtc_tm->tm_mon -= 1;
/*
* switch from (data_year->min)-relative offset to
* a (1900)-relative one
*/
rtc_tm->tm_year += SUN6I_YEAR_OFF;
return 0;
}
......@@ -510,36 +508,54 @@ static int sun6i_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
struct rtc_time *alrm_tm = &wkalrm->time;
struct rtc_time tm_now;
unsigned long time_now = 0;
unsigned long time_set = 0;
unsigned long time_gap = 0;
int ret = 0;
ret = sun6i_rtc_gettime(dev, &tm_now);
if (ret < 0) {
dev_err(dev, "Error in getting time\n");
return -EINVAL;
}
time64_t time_set;
u32 counter_val, counter_val_hms;
int ret;
time_set = rtc_tm_to_time64(alrm_tm);
time_now = rtc_tm_to_time64(&tm_now);
if (time_set <= time_now) {
dev_err(dev, "Date to set in the past\n");
return -EINVAL;
}
time_gap = time_set - time_now;
if (time_gap > U32_MAX) {
dev_err(dev, "Date too far in the future\n");
return -EINVAL;
if (chip->flags & RTC_LINEAR_DAY) {
/*
* The alarm registers hold the actual alarm time, encoded
* in the same way (linear day + HMS) as the current time.
*/
counter_val_hms = SUN6I_TIME_SET_SEC_VALUE(alrm_tm->tm_sec) |
SUN6I_TIME_SET_MIN_VALUE(alrm_tm->tm_min) |
SUN6I_TIME_SET_HOUR_VALUE(alrm_tm->tm_hour);
/* The division will cut off the H:M:S part of alrm_tm. */
counter_val = div_u64(rtc_tm_to_time64(alrm_tm), SECS_PER_DAY);
} else {
/* The alarm register holds the number of seconds left. */
time64_t time_now;
ret = sun6i_rtc_gettime(dev, &tm_now);
if (ret < 0) {
dev_err(dev, "Error in getting time\n");
return -EINVAL;
}
time_now = rtc_tm_to_time64(&tm_now);
if (time_set <= time_now) {
dev_err(dev, "Date to set in the past\n");
return -EINVAL;
}
if ((time_set - time_now) > U32_MAX) {
dev_err(dev, "Date too far in the future\n");
return -EINVAL;
}
counter_val = time_set - time_now;
}
sun6i_rtc_setaie(0, chip);
writel(0, chip->base + SUN6I_ALRM_COUNTER);
if (chip->flags & RTC_LINEAR_DAY)
writel(0, chip->base + SUN6I_ALRM_COUNTER_HMS);
usleep_range(100, 300);
writel(time_gap, chip->base + SUN6I_ALRM_COUNTER);
writel(counter_val, chip->base + SUN6I_ALRM_COUNTER);
if (chip->flags & RTC_LINEAR_DAY)
writel(counter_val_hms, chip->base + SUN6I_ALRM_COUNTER_HMS);
chip->alarm = time_set;
sun6i_rtc_setaie(wkalrm->enabled, chip);
......@@ -571,20 +587,25 @@ static int sun6i_rtc_settime(struct device *dev, struct rtc_time *rtc_tm)
u32 date = 0;
u32 time = 0;
rtc_tm->tm_year -= SUN6I_YEAR_OFF;
rtc_tm->tm_mon += 1;
date = SUN6I_DATE_SET_DAY_VALUE(rtc_tm->tm_mday) |
SUN6I_DATE_SET_MON_VALUE(rtc_tm->tm_mon) |
SUN6I_DATE_SET_YEAR_VALUE(rtc_tm->tm_year);
if (is_leap_year(rtc_tm->tm_year + SUN6I_YEAR_MIN))
date |= SUN6I_LEAP_SET_VALUE(1);
time = SUN6I_TIME_SET_SEC_VALUE(rtc_tm->tm_sec) |
SUN6I_TIME_SET_MIN_VALUE(rtc_tm->tm_min) |
SUN6I_TIME_SET_HOUR_VALUE(rtc_tm->tm_hour);
if (chip->flags & RTC_LINEAR_DAY) {
/* The division will cut off the H:M:S part of rtc_tm. */
date = div_u64(rtc_tm_to_time64(rtc_tm), SECS_PER_DAY);
} else {
rtc_tm->tm_year -= SUN6I_YEAR_OFF;
rtc_tm->tm_mon += 1;
date = SUN6I_DATE_SET_DAY_VALUE(rtc_tm->tm_mday) |
SUN6I_DATE_SET_MON_VALUE(rtc_tm->tm_mon) |
SUN6I_DATE_SET_YEAR_VALUE(rtc_tm->tm_year);
if (is_leap_year(rtc_tm->tm_year + SUN6I_YEAR_MIN))
date |= SUN6I_LEAP_SET_VALUE(1);
}
/* Check whether registers are writable */
if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL,
SUN6I_LOSC_CTRL_ACC_MASK, 50)) {
......@@ -668,11 +689,35 @@ static int sun6i_rtc_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(sun6i_rtc_pm_ops,
sun6i_rtc_suspend, sun6i_rtc_resume);
static void sun6i_rtc_bus_clk_cleanup(void *data)
{
struct clk *bus_clk = data;
clk_disable_unprepare(bus_clk);
}
static int sun6i_rtc_probe(struct platform_device *pdev)
{
struct sun6i_rtc_dev *chip = sun6i_rtc;
struct device *dev = &pdev->dev;
struct clk *bus_clk;
int ret;
bus_clk = devm_clk_get_optional(dev, "bus");
if (IS_ERR(bus_clk))
return PTR_ERR(bus_clk);
if (bus_clk) {
ret = clk_prepare_enable(bus_clk);
if (ret)
return ret;
ret = devm_add_action_or_reset(dev, sun6i_rtc_bus_clk_cleanup,
bus_clk);
if (ret)
return ret;
}
if (!chip) {
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
......@@ -683,10 +728,18 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
chip->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(chip->base))
return PTR_ERR(chip->base);
if (IS_REACHABLE(CONFIG_SUN6I_RTC_CCU)) {
ret = sun6i_rtc_ccu_probe(dev, chip->base);
if (ret)
return ret;
}
}
platform_set_drvdata(pdev, chip);
chip->flags = (unsigned long)of_device_get_match_data(&pdev->dev);
chip->irq = platform_get_irq(pdev, 0);
if (chip->irq < 0)
return chip->irq;
......@@ -733,7 +786,10 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
return PTR_ERR(chip->rtc);
chip->rtc->ops = &sun6i_rtc_ops;
chip->rtc->range_max = 2019686399LL; /* 2033-12-31 23:59:59 */
if (chip->flags & RTC_LINEAR_DAY)
chip->rtc->range_max = (65536 * SECS_PER_DAY) - 1;
else
chip->rtc->range_max = 2019686399LL; /* 2033-12-31 23:59:59 */
ret = devm_rtc_register_device(chip->rtc);
if (ret)
......@@ -758,6 +814,8 @@ static const struct of_device_id sun6i_rtc_dt_ids[] = {
{ .compatible = "allwinner,sun8i-v3-rtc" },
{ .compatible = "allwinner,sun50i-h5-rtc" },
{ .compatible = "allwinner,sun50i-h6-rtc" },
{ .compatible = "allwinner,sun50i-h616-rtc",
.data = (void *)RTC_LINEAR_DAY },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids);
......
......@@ -432,14 +432,21 @@ static int wm8350_rtc_probe(struct platform_device *pdev)
return ret;
}
wm8350_register_irq(wm8350, WM8350_IRQ_RTC_SEC,
ret = wm8350_register_irq(wm8350, WM8350_IRQ_RTC_SEC,
wm8350_rtc_update_handler, 0,
"RTC Seconds", wm8350);
if (ret)
return ret;
wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_SEC);
wm8350_register_irq(wm8350, WM8350_IRQ_RTC_ALM,
ret = wm8350_register_irq(wm8350, WM8350_IRQ_RTC_ALM,
wm8350_rtc_alarm_handler, 0,
"RTC Alarm", wm8350);
if (ret) {
wm8350_free_irq(wm8350, WM8350_IRQ_RTC_SEC, wm8350);
return ret;
}
return 0;
}
......
......@@ -180,8 +180,6 @@ static int xgene_rtc_probe(struct platform_device *pdev)
return ret;
}
/* HW does not support update faster than 1 seconds */
pdata->rtc->uie_unsupported = 1;
pdata->rtc->ops = &xgene_rtc_ops;
pdata->rtc->range_max = U32_MAX;
......
/* SPDX-License-Identifier: (GPL-2.0+ or MIT) */
#ifndef _DT_BINDINGS_CLK_SUN6I_RTC_H_
#define _DT_BINDINGS_CLK_SUN6I_RTC_H_
#define CLK_OSC32K 0
#define CLK_OSC32K_FANOUT 1
#define CLK_IOSC 2
#endif /* _DT_BINDINGS_CLK_SUN6I_RTC_H_ */
......@@ -9,4 +9,6 @@
int sunxi_ccu_set_mmc_timing_mode(struct clk *clk, bool new_mode);
int sunxi_ccu_get_mmc_timing_mode(struct clk *clk);
int sun6i_rtc_ccu_probe(struct device *dev, void __iomem *reg);
#endif
......@@ -86,6 +86,8 @@ struct cmos_rtc_board_info {
/* 2 values for divider stage reset, others for "testing purposes only" */
# define RTC_DIV_RESET1 0x60
# define RTC_DIV_RESET2 0x70
/* In AMD BKDG bit 5 and 6 are reserved, bit 4 is for select dv0 bank */
# define RTC_AMD_BANK_SELECT 0x10
/* Periodic intr. / Square wave rate select. 0=none, 1=32.8kHz,... 15=2Hz */
# define RTC_RATE_SELECT 0x0F
......
......@@ -110,8 +110,6 @@ struct rtc_device {
struct hrtimer pie_timer; /* sub second exp, so needs hrtimer */
int pie_enabled;
struct work_struct irqwork;
/* Some hardware can't support UIE mode */
int uie_unsupported;
/*
* This offset specifies the update timing of the RTC.
......
......@@ -46,7 +46,6 @@ struct ds1685_priv {
u32 regstep;
int irq_num;
bool bcd_mode;
bool no_irq;
u8 (*read)(struct ds1685_priv *, int);
void (*write)(struct ds1685_priv *, int, u8);
void (*prepare_poweroff)(void);
......
......@@ -133,7 +133,8 @@ struct rtc_param {
#define RTC_FEATURE_UPDATE_INTERRUPT 4
#define RTC_FEATURE_CORRECTION 5
#define RTC_FEATURE_BACKUP_SWITCH_MODE 6
#define RTC_FEATURE_CNT 7
#define RTC_FEATURE_ALARM_WAKEUP_ONLY 7
#define RTC_FEATURE_CNT 8
/* parameter list */
#define RTC_PARAM_FEATURES 0
......
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