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:
commit
6a34fdcca4
@ -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
|
||||
|
||||
|
@ -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>;
|
||||
};
|
@ -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>;
|
||||
};
|
@ -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
|
||||
|
395
drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
Normal file
395
drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
Normal 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");
|
15
drivers/clk/sunxi-ng/ccu-sun6i-rtc.h
Normal file
15
drivers/clk/sunxi-ng/ccu-sun6i-rtc.h
Normal 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 */
|
@ -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);
|
||||
}
|
||||
|
362
drivers/rtc/rtc-optee.c
Normal file
362
drivers/rtc/rtc-optee.c
Normal 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");
|
@ -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);
|
||||
}
|
||||
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,
|
||||
},
|
||||
};
|
||||
|
@ -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 (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);
|
||||
|
@ -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;
|
||||
|
||||
|
10
include/dt-bindings/clock/sun6i-rtc.h
Normal file
10
include/dt-bindings/clock/sun6i-rtc.h
Normal 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_ */
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user