2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2024-11-23 18:14:04 +08:00

RTC for 5.18

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
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEBqsFVZXh8s/0O5JiY6TcMGxwOjIFAmJGJ0wACgkQY6TcMGxw
 OjKAXQ/9EId70uY/kUQv2Ay2+NysfSFYpDoerf1V55vGyN+n/pxX14bUxTx0Ivse
 47ROcWSP94MSw9cExNvujkpY/V6xaQI3/jxWC60f5ngHzAwIArmmDdWMSLDmRJYi
 mg07Hp9bSTIgXVzBdlAJc6vSRDmznKB44TTrz6GvulLJROAy9jNJwefuxwsV9Ftl
 I6PcRtYGn5poPpp7yLi1IZ1m0gWJJVze9IsJWJjTRSzh9xAYnjOsMR5H8fyMLjh9
 1KjiHwV1c5En4HtrMrYPXOdeLTgNfCdfHaLRRhZRSE+dTK4551TbYbeG2GtKWkdR
 Xvybwik2kghde8+/sh6hhg59NGkfMkzLByc41P0VWfDEscyi3z+YXot2OOV+FfkI
 XpDPab+a2IQaGFYEa4sGVloFP4e9er5uRHRPkT6bX07plACMwV1wLSRqCK/RriV6
 HYImdP7/aA4t/OlmVdliyiyOZfO+oHX/z8hhkNbTJc5HpvgViDEKKEsOEef6OcRh
 j+p8ej6iaVt1wjYpfghHYanFzVPi98zGH0QMS9GmHbS+VetGYxzUVyKgzAsOMwYA
 RwTunNkQQqgiz+C4n2s/PDD4ZuNDFDFaEE+3RReaw/9LGkR007TCxwcNBEGde+B6
 1W4B9tNMGLI/hHulv7WYNrP/5Y/QPEuPiu+FxhwpNm5tDg1Ur70=
 =7E0a
 -----END PGP SIGNATURE-----

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
  ...
This commit is contained in:
Linus Torvalds 2022-04-01 09:37:18 -07:00
commit 6a34fdcca4
41 changed files with 1204 additions and 242 deletions

View File

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

View File

@ -1,25 +0,0 @@
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>;
};

View File

@ -0,0 +1,69 @@
# 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>;
};

View File

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

View File

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

View File

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

View File

@ -0,0 +1,395 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright (c) 2021 Samuel Holland <samuel@sholland.org>
//
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/clk/sunxi-ng.h>
#include "ccu_common.h"
#include "ccu_div.h"
#include "ccu_gate.h"
#include "ccu_mux.h"
#include "ccu-sun6i-rtc.h"
#define IOSC_ACCURACY 300000000 /* 30% */
#define IOSC_RATE 16000000
#define LOSC_RATE 32768
#define LOSC_RATE_SHIFT 15
#define LOSC_CTRL_REG 0x0
#define LOSC_CTRL_KEY 0x16aa0000
#define IOSC_32K_CLK_DIV_REG 0x8
#define IOSC_32K_CLK_DIV GENMASK(4, 0)
#define IOSC_32K_PRE_DIV 32
#define IOSC_CLK_CALI_REG 0xc
#define IOSC_CLK_CALI_DIV_ONES 22
#define IOSC_CLK_CALI_EN BIT(1)
#define IOSC_CLK_CALI_SRC_SEL BIT(0)
#define LOSC_OUT_GATING_REG 0x60
#define DCXO_CTRL_REG 0x160
#define DCXO_CTRL_CLK16M_RC_EN BIT(0)
struct sun6i_rtc_match_data {
bool have_ext_osc32k : 1;
bool have_iosc_calibration : 1;
bool rtc_32k_single_parent : 1;
const struct clk_parent_data *osc32k_fanout_parents;
u8 osc32k_fanout_nparents;
};
static bool have_iosc_calibration;
static int ccu_iosc_enable(struct clk_hw *hw)
{
struct ccu_common *cm = hw_to_ccu_common(hw);
return ccu_gate_helper_enable(cm, DCXO_CTRL_CLK16M_RC_EN);
}
static void ccu_iosc_disable(struct clk_hw *hw)
{
struct ccu_common *cm = hw_to_ccu_common(hw);
return ccu_gate_helper_disable(cm, DCXO_CTRL_CLK16M_RC_EN);
}
static int ccu_iosc_is_enabled(struct clk_hw *hw)
{
struct ccu_common *cm = hw_to_ccu_common(hw);
return ccu_gate_helper_is_enabled(cm, DCXO_CTRL_CLK16M_RC_EN);
}
static unsigned long ccu_iosc_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct ccu_common *cm = hw_to_ccu_common(hw);
if (have_iosc_calibration) {
u32 reg = readl(cm->base + IOSC_CLK_CALI_REG);
/*
* Recover the IOSC frequency by shifting the ones place of
* (fixed-point divider * 32768) into bit zero.
*/
if (reg & IOSC_CLK_CALI_EN)
return reg >> (IOSC_CLK_CALI_DIV_ONES - LOSC_RATE_SHIFT);
}
return IOSC_RATE;
}
static unsigned long ccu_iosc_recalc_accuracy(struct clk_hw *hw,
unsigned long parent_accuracy)
{
return IOSC_ACCURACY;
}
static const struct clk_ops ccu_iosc_ops = {
.enable = ccu_iosc_enable,
.disable = ccu_iosc_disable,
.is_enabled = ccu_iosc_is_enabled,
.recalc_rate = ccu_iosc_recalc_rate,
.recalc_accuracy = ccu_iosc_recalc_accuracy,
};
static struct ccu_common iosc_clk = {
.reg = DCXO_CTRL_REG,
.hw.init = CLK_HW_INIT_NO_PARENT("iosc", &ccu_iosc_ops,
CLK_GET_RATE_NOCACHE),
};
static int ccu_iosc_32k_prepare(struct clk_hw *hw)
{
struct ccu_common *cm = hw_to_ccu_common(hw);
u32 val;
if (!have_iosc_calibration)
return 0;
val = readl(cm->base + IOSC_CLK_CALI_REG);
writel(val | IOSC_CLK_CALI_EN | IOSC_CLK_CALI_SRC_SEL,
cm->base + IOSC_CLK_CALI_REG);
return 0;
}
static void ccu_iosc_32k_unprepare(struct clk_hw *hw)
{
struct ccu_common *cm = hw_to_ccu_common(hw);
u32 val;
if (!have_iosc_calibration)
return;
val = readl(cm->base + IOSC_CLK_CALI_REG);
writel(val & ~(IOSC_CLK_CALI_EN | IOSC_CLK_CALI_SRC_SEL),
cm->base + IOSC_CLK_CALI_REG);
}
static unsigned long ccu_iosc_32k_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct ccu_common *cm = hw_to_ccu_common(hw);
u32 val;
if (have_iosc_calibration) {
val = readl(cm->base + IOSC_CLK_CALI_REG);
/* Assume the calibrated 32k clock is accurate. */
if (val & IOSC_CLK_CALI_SRC_SEL)
return LOSC_RATE;
}
val = readl(cm->base + IOSC_32K_CLK_DIV_REG) & IOSC_32K_CLK_DIV;
return parent_rate / IOSC_32K_PRE_DIV / (val + 1);
}
static unsigned long ccu_iosc_32k_recalc_accuracy(struct clk_hw *hw,
unsigned long parent_accuracy)
{
struct ccu_common *cm = hw_to_ccu_common(hw);
u32 val;
if (have_iosc_calibration) {
val = readl(cm->base + IOSC_CLK_CALI_REG);
/* Assume the calibrated 32k clock is accurate. */
if (val & IOSC_CLK_CALI_SRC_SEL)
return 0;
}
return parent_accuracy;
}
static const struct clk_ops ccu_iosc_32k_ops = {
.prepare = ccu_iosc_32k_prepare,
.unprepare = ccu_iosc_32k_unprepare,
.recalc_rate = ccu_iosc_32k_recalc_rate,
.recalc_accuracy = ccu_iosc_32k_recalc_accuracy,
};
static struct ccu_common iosc_32k_clk = {
.hw.init = CLK_HW_INIT_HW("iosc-32k", &iosc_clk.hw,
&ccu_iosc_32k_ops,
CLK_GET_RATE_NOCACHE),
};
static const struct clk_hw *ext_osc32k[] = { NULL }; /* updated during probe */
static SUNXI_CCU_GATE_HWS(ext_osc32k_gate_clk, "ext-osc32k-gate",
ext_osc32k, 0x0, BIT(4), 0);
static const struct clk_hw *osc32k_parents[] = {
&iosc_32k_clk.hw,
&ext_osc32k_gate_clk.common.hw
};
static struct clk_init_data osc32k_init_data = {
.name = "osc32k",
.ops = &ccu_mux_ops,
.parent_hws = osc32k_parents,
.num_parents = ARRAY_SIZE(osc32k_parents), /* updated during probe */
};
static struct ccu_mux osc32k_clk = {
.mux = _SUNXI_CCU_MUX(0, 1),
.common = {
.reg = LOSC_CTRL_REG,
.features = CCU_FEATURE_KEY_FIELD,
.hw.init = &osc32k_init_data,
},
};
/* This falls back to the global name for fwnodes without a named reference. */
static const struct clk_parent_data osc24M[] = {
{ .fw_name = "hosc", .name = "osc24M" }
};
static struct ccu_gate osc24M_32k_clk = {
.enable = BIT(16),
.common = {
.reg = LOSC_OUT_GATING_REG,
.prediv = 750,
.features = CCU_FEATURE_ALL_PREDIV,
.hw.init = CLK_HW_INIT_PARENTS_DATA("osc24M-32k", osc24M,
&ccu_gate_ops, 0),
},
};
static const struct clk_hw *rtc_32k_parents[] = {
&osc32k_clk.common.hw,
&osc24M_32k_clk.common.hw
};
static struct clk_init_data rtc_32k_init_data = {
.name = "rtc-32k",
.ops = &ccu_mux_ops,
.parent_hws = rtc_32k_parents,
.num_parents = ARRAY_SIZE(rtc_32k_parents), /* updated during probe */
};
static struct ccu_mux rtc_32k_clk = {
.mux = _SUNXI_CCU_MUX(1, 1),
.common = {
.reg = LOSC_CTRL_REG,
.features = CCU_FEATURE_KEY_FIELD,
.hw.init = &rtc_32k_init_data,
},
};
static struct clk_init_data osc32k_fanout_init_data = {
.name = "osc32k-fanout",
.ops = &ccu_mux_ops,
/* parents are set during probe */
};
static struct ccu_mux osc32k_fanout_clk = {
.enable = BIT(0),
.mux = _SUNXI_CCU_MUX(1, 2),
.common = {
.reg = LOSC_OUT_GATING_REG,
.hw.init = &osc32k_fanout_init_data,
},
};
static struct ccu_common *sun6i_rtc_ccu_clks[] = {
&iosc_clk,
&iosc_32k_clk,
&ext_osc32k_gate_clk.common,
&osc32k_clk.common,
&osc24M_32k_clk.common,
&rtc_32k_clk.common,
&osc32k_fanout_clk.common,
};
static struct clk_hw_onecell_data sun6i_rtc_ccu_hw_clks = {
.num = CLK_NUMBER,
.hws = {
[CLK_OSC32K] = &osc32k_clk.common.hw,
[CLK_OSC32K_FANOUT] = &osc32k_fanout_clk.common.hw,
[CLK_IOSC] = &iosc_clk.hw,
[CLK_IOSC_32K] = &iosc_32k_clk.hw,
[CLK_EXT_OSC32K_GATE] = &ext_osc32k_gate_clk.common.hw,
[CLK_OSC24M_32K] = &osc24M_32k_clk.common.hw,
[CLK_RTC_32K] = &rtc_32k_clk.common.hw,
},
};
static const struct sunxi_ccu_desc sun6i_rtc_ccu_desc = {
.ccu_clks = sun6i_rtc_ccu_clks,
.num_ccu_clks = ARRAY_SIZE(sun6i_rtc_ccu_clks),
.hw_clks = &sun6i_rtc_ccu_hw_clks,
};
static const struct clk_parent_data sun50i_h6_osc32k_fanout_parents[] = {
{ .hw = &osc32k_clk.common.hw },
};
static const struct clk_parent_data sun50i_h616_osc32k_fanout_parents[] = {
{ .hw = &osc32k_clk.common.hw },
{ .fw_name = "pll-32k" },
{ .hw = &osc24M_32k_clk.common.hw }
};
static const struct clk_parent_data sun50i_r329_osc32k_fanout_parents[] = {
{ .hw = &osc32k_clk.common.hw },
{ .hw = &ext_osc32k_gate_clk.common.hw },
{ .hw = &osc24M_32k_clk.common.hw }
};
static const struct sun6i_rtc_match_data sun50i_h6_rtc_ccu_data = {
.have_ext_osc32k = true,
.have_iosc_calibration = true,
.osc32k_fanout_parents = sun50i_h6_osc32k_fanout_parents,
.osc32k_fanout_nparents = ARRAY_SIZE(sun50i_h6_osc32k_fanout_parents),
};
static const struct sun6i_rtc_match_data sun50i_h616_rtc_ccu_data = {
.have_iosc_calibration = true,
.rtc_32k_single_parent = true,
.osc32k_fanout_parents = sun50i_h616_osc32k_fanout_parents,
.osc32k_fanout_nparents = ARRAY_SIZE(sun50i_h616_osc32k_fanout_parents),
};
static const struct sun6i_rtc_match_data sun50i_r329_rtc_ccu_data = {
.have_ext_osc32k = true,
.osc32k_fanout_parents = sun50i_r329_osc32k_fanout_parents,
.osc32k_fanout_nparents = ARRAY_SIZE(sun50i_r329_osc32k_fanout_parents),
};
static const struct of_device_id sun6i_rtc_ccu_match[] = {
{
.compatible = "allwinner,sun50i-h6-rtc",
.data = &sun50i_h6_rtc_ccu_data,
},
{
.compatible = "allwinner,sun50i-h616-rtc",
.data = &sun50i_h616_rtc_ccu_data,
},
{
.compatible = "allwinner,sun50i-r329-rtc",
.data = &sun50i_r329_rtc_ccu_data,
},
};
int sun6i_rtc_ccu_probe(struct device *dev, void __iomem *reg)
{
const struct sun6i_rtc_match_data *data;
struct clk *ext_osc32k_clk = NULL;
const struct of_device_id *match;
/* This driver is only used for newer variants of the hardware. */
match = of_match_device(sun6i_rtc_ccu_match, dev);
if (!match)
return 0;
data = match->data;
have_iosc_calibration = data->have_iosc_calibration;
if (data->have_ext_osc32k) {
const char *fw_name;
/* ext-osc32k was the only input clock in the old binding. */
fw_name = of_property_read_bool(dev->of_node, "clock-names")
? "ext-osc32k" : NULL;
ext_osc32k_clk = devm_clk_get_optional(dev, fw_name);
if (IS_ERR(ext_osc32k_clk))
return PTR_ERR(ext_osc32k_clk);
}
if (ext_osc32k_clk) {
/* Link ext-osc32k-gate to its parent. */
*ext_osc32k = __clk_get_hw(ext_osc32k_clk);
} else {
/* ext-osc32k-gate is an orphan, so do not register it. */
sun6i_rtc_ccu_hw_clks.hws[CLK_EXT_OSC32K_GATE] = NULL;
osc32k_init_data.num_parents = 1;
}
if (data->rtc_32k_single_parent)
rtc_32k_init_data.num_parents = 1;
osc32k_fanout_init_data.parent_data = data->osc32k_fanout_parents;
osc32k_fanout_init_data.num_parents = data->osc32k_fanout_nparents;
return devm_sunxi_ccu_probe(dev, reg, &sun6i_rtc_ccu_desc);
}
MODULE_IMPORT_NS(SUNXI_CCU);
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,15 @@
/* 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 */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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[] = {

View File

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

View File

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

View File

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

View File

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

362
drivers/rtc/rtc-optee.c Normal file
View File

@ -0,0 +1,362 @@
// 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");

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
}
rc = devm_rtc_register_device(rtc_dd->rtc);
if (rc)
return rc;
#ifdef CONFIG_PM_SLEEP
static int pm8xxx_rtc_resume(struct device *dev)
{
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
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,
},
};

View File

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

View File

@ -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 (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;
if (time_gap > U32_MAX) {
dev_err(dev, "Date too far in the future\n");
return -EINVAL;
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);

View File

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

View File

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

View File

@ -0,0 +1,10 @@
/* 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_ */

View File

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

View File

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

View File

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

View File

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

View File

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