mirror of
https://github.com/Dasharo/linux.git
synced 2026-03-06 15:25:10 -08:00
Merge tag 'timers-core-2020-12-14' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull timers and timekeeping updates from Thomas Gleixner:
"Core:
- Robustness improvements for the NOHZ tick management
- Fixes and consolidation of the NTP/RTC synchronization code
- Small fixes and improvements in various places
- A set of function documentation udpates and fixes
Drivers:
- Cleanups and improvements in various clocksoure/event drivers
- Removal of the EZChip NPS clocksource driver as the platfrom
support was removed from ARC
- The usual set of new device tree binding and json conversions
- The RTC driver which have been acked by the RTC maintainer:
* fix a long standing bug in the MC146818 library code which can
cause reading garbage during the RTC internal update.
* changes related to the NTP/RTC consolidation work"
* tag 'timers-core-2020-12-14' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (46 commits)
ntp: Fix prototype in the !CONFIG_GENERIC_CMOS_UPDATE case
tick/sched: Make jiffies update quick check more robust
ntp: Consolidate the RTC update implementation
ntp: Make the RTC sync offset less obscure
ntp, rtc: Move rtc_set_ntp_time() to ntp code
ntp: Make the RTC synchronization more reliable
rtc: core: Make the sync offset default more realistic
rtc: cmos: Make rtc_cmos sync offset correct
rtc: mc146818: Reduce spinlock section in mc146818_set_time()
rtc: mc146818: Prevent reading garbage
clocksource/drivers/sh_cmt: Fix potential deadlock when calling runtime PM
clocksource/drivers/arm_arch_timer: Correct fault programming of CNTKCTL_EL1.EVNTI
clocksource/drivers/arm_arch_timer: Use stable count reader in erratum sne
clocksource/drivers/dw_apb_timer_of: Add error handling if no clock available
clocksource/drivers/riscv: Make RISCV_TIMER depends on RISCV_SBI
clocksource/drivers/ingenic: Fix section mismatch
clocksource/drivers/cadence_ttc: Fix memory leak in ttc_setup_clockevent()
dt-bindings: timer: renesas: tmu: Convert to json-schema
dt-bindings: timer: renesas: tmu: Document r8a774e1 bindings
clocksource/drivers/orion: Add missing clk_disable_unprepare() on error path
...
This commit is contained in:
@@ -1,49 +0,0 @@
|
||||
* Renesas R-Mobile/R-Car Timer Unit (TMU)
|
||||
|
||||
The TMU is a 32-bit timer/counter with configurable clock inputs and
|
||||
programmable compare match.
|
||||
|
||||
Channels share hardware resources but their counter and compare match value
|
||||
are independent. The TMU hardware supports up to three channels.
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible: must contain one or more of the following:
|
||||
- "renesas,tmu-r8a7740" for the r8a7740 TMU
|
||||
- "renesas,tmu-r8a774a1" for the r8a774A1 TMU
|
||||
- "renesas,tmu-r8a774b1" for the r8a774B1 TMU
|
||||
- "renesas,tmu-r8a774c0" for the r8a774C0 TMU
|
||||
- "renesas,tmu-r8a7778" for the r8a7778 TMU
|
||||
- "renesas,tmu-r8a7779" for the r8a7779 TMU
|
||||
- "renesas,tmu-r8a77970" for the r8a77970 TMU
|
||||
- "renesas,tmu-r8a77980" for the r8a77980 TMU
|
||||
- "renesas,tmu" for any TMU.
|
||||
This is a fallback for the above renesas,tmu-* entries
|
||||
|
||||
- reg: base address and length of the registers block for the timer module.
|
||||
|
||||
- interrupts: interrupt-specifier for the timer, one per channel.
|
||||
|
||||
- clocks: a list of phandle + clock-specifier pairs, one for each entry
|
||||
in clock-names.
|
||||
- clock-names: must contain "fck" for the functional clock.
|
||||
|
||||
Optional Properties:
|
||||
|
||||
- #renesas,channels: number of channels implemented by the timer, must be 2
|
||||
or 3 (if not specified the value defaults to 3).
|
||||
|
||||
|
||||
Example: R8A7779 (R-Car H1) TMU0 node
|
||||
|
||||
tmu0: timer@ffd80000 {
|
||||
compatible = "renesas,tmu-r8a7779", "renesas,tmu";
|
||||
reg = <0xffd80000 0x30>;
|
||||
interrupts = <0 32 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<0 33 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<0 34 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&mstp0_clks R8A7779_CLK_TMU0>;
|
||||
clock-names = "fck";
|
||||
|
||||
#renesas,channels = <3>;
|
||||
};
|
||||
99
Documentation/devicetree/bindings/timer/renesas,tmu.yaml
Normal file
99
Documentation/devicetree/bindings/timer/renesas,tmu.yaml
Normal file
@@ -0,0 +1,99 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/timer/renesas,tmu.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Renesas R-Mobile/R-Car Timer Unit (TMU)
|
||||
|
||||
maintainers:
|
||||
- Geert Uytterhoeven <geert+renesas@glider.be>
|
||||
- Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
|
||||
|
||||
description:
|
||||
The TMU is a 32-bit timer/counter with configurable clock inputs and
|
||||
programmable compare match.
|
||||
|
||||
Channels share hardware resources but their counter and compare match value
|
||||
are independent. The TMU hardware supports up to three channels.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- renesas,tmu-r8a7740 # R-Mobile A1
|
||||
- renesas,tmu-r8a774a1 # RZ/G2M
|
||||
- renesas,tmu-r8a774b1 # RZ/G2N
|
||||
- renesas,tmu-r8a774c0 # RZ/G2E
|
||||
- renesas,tmu-r8a774e1 # RZ/G2H
|
||||
- renesas,tmu-r8a7778 # R-Car M1A
|
||||
- renesas,tmu-r8a7779 # R-Car H1
|
||||
- renesas,tmu-r8a77970 # R-Car V3M
|
||||
- renesas,tmu-r8a77980 # R-Car V3H
|
||||
- const: renesas,tmu
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
minItems: 2
|
||||
maxItems: 3
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
const: fck
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
'#renesas,channels':
|
||||
description:
|
||||
Number of channels implemented by the timer.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum: [ 2, 3 ]
|
||||
default: 3
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
- power-domains
|
||||
|
||||
if:
|
||||
not:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- renesas,tmu-r8a7740
|
||||
- renesas,tmu-r8a7778
|
||||
- renesas,tmu-r8a7779
|
||||
then:
|
||||
required:
|
||||
- resets
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/r8a7779-clock.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/power/r8a7779-sysc.h>
|
||||
tmu0: timer@ffd80000 {
|
||||
compatible = "renesas,tmu-r8a7779", "renesas,tmu";
|
||||
reg = <0xffd80000 0x30>;
|
||||
interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&mstp0_clks R8A7779_CLK_TMU0>;
|
||||
clock-names = "fck";
|
||||
power-domains = <&sysc R8A7779_PD_ALWAYS_ON>;
|
||||
#renesas,channels = <3>;
|
||||
};
|
||||
@@ -275,16 +275,6 @@ config CLKSRC_TI_32K
|
||||
This option enables support for Texas Instruments 32.768 Hz clocksource
|
||||
available on many OMAP-like platforms.
|
||||
|
||||
config CLKSRC_NPS
|
||||
bool "NPS400 clocksource driver" if COMPILE_TEST
|
||||
depends on !PHYS_ADDR_T_64BIT
|
||||
select CLKSRC_MMIO
|
||||
select TIMER_OF if OF
|
||||
help
|
||||
NPS400 clocksource support.
|
||||
It has a 64-bit counter with update rate up to 1000MHz.
|
||||
This counter is accessed via couple of 32-bit memory-mapped registers.
|
||||
|
||||
config CLKSRC_STM32
|
||||
bool "Clocksource for STM32 SoCs" if !ARCH_STM32
|
||||
depends on OF && ARM && (ARCH_STM32 || COMPILE_TEST)
|
||||
@@ -654,7 +644,7 @@ config ATCPIT100_TIMER
|
||||
|
||||
config RISCV_TIMER
|
||||
bool "Timer for the RISC-V platform" if COMPILE_TEST
|
||||
depends on GENERIC_SCHED_CLOCK && RISCV
|
||||
depends on GENERIC_SCHED_CLOCK && RISCV && RISCV_SBI
|
||||
select TIMER_PROBE
|
||||
select TIMER_OF
|
||||
help
|
||||
|
||||
@@ -56,7 +56,6 @@ obj-$(CONFIG_CLKSRC_QCOM) += timer-qcom.o
|
||||
obj-$(CONFIG_MTK_TIMER) += timer-mediatek.o
|
||||
obj-$(CONFIG_CLKSRC_PISTACHIO) += timer-pistachio.o
|
||||
obj-$(CONFIG_CLKSRC_TI_32K) += timer-ti-32k.o
|
||||
obj-$(CONFIG_CLKSRC_NPS) += timer-nps.o
|
||||
obj-$(CONFIG_OXNAS_RPS_TIMER) += timer-oxnas-rps.o
|
||||
obj-$(CONFIG_OWL_TIMER) += timer-owl.o
|
||||
obj-$(CONFIG_MILBEAUT_TIMER) += timer-milbeaut.o
|
||||
|
||||
@@ -396,10 +396,10 @@ static void erratum_set_next_event_tval_generic(const int access, unsigned long
|
||||
ctrl &= ~ARCH_TIMER_CTRL_IT_MASK;
|
||||
|
||||
if (access == ARCH_TIMER_PHYS_ACCESS) {
|
||||
cval = evt + arch_counter_get_cntpct();
|
||||
cval = evt + arch_counter_get_cntpct_stable();
|
||||
write_sysreg(cval, cntp_cval_el0);
|
||||
} else {
|
||||
cval = evt + arch_counter_get_cntvct();
|
||||
cval = evt + arch_counter_get_cntvct_stable();
|
||||
write_sysreg(cval, cntv_cval_el0);
|
||||
}
|
||||
|
||||
@@ -822,15 +822,24 @@ static void arch_timer_evtstrm_enable(int divider)
|
||||
|
||||
static void arch_timer_configure_evtstream(void)
|
||||
{
|
||||
int evt_stream_div, pos;
|
||||
int evt_stream_div, lsb;
|
||||
|
||||
/*
|
||||
* As the event stream can at most be generated at half the frequency
|
||||
* of the counter, use half the frequency when computing the divider.
|
||||
*/
|
||||
evt_stream_div = arch_timer_rate / ARCH_TIMER_EVT_STREAM_FREQ / 2;
|
||||
|
||||
/*
|
||||
* Find the closest power of two to the divisor. If the adjacent bit
|
||||
* of lsb (last set bit, starts from 0) is set, then we use (lsb + 1).
|
||||
*/
|
||||
lsb = fls(evt_stream_div) - 1;
|
||||
if (lsb > 0 && (evt_stream_div & BIT(lsb - 1)))
|
||||
lsb++;
|
||||
|
||||
/* Find the closest power of two to the divisor */
|
||||
evt_stream_div = arch_timer_rate / ARCH_TIMER_EVT_STREAM_FREQ;
|
||||
pos = fls(evt_stream_div);
|
||||
if (pos > 1 && !(evt_stream_div & (1 << (pos - 2))))
|
||||
pos--;
|
||||
/* enable event stream */
|
||||
arch_timer_evtstrm_enable(min(pos, 15));
|
||||
arch_timer_evtstrm_enable(max(0, min(lsb, 15)));
|
||||
}
|
||||
|
||||
static void arch_counter_set_user_access(void)
|
||||
|
||||
@@ -14,12 +14,13 @@
|
||||
#include <linux/reset.h>
|
||||
#include <linux/sched_clock.h>
|
||||
|
||||
static void __init timer_get_base_and_rate(struct device_node *np,
|
||||
static int __init timer_get_base_and_rate(struct device_node *np,
|
||||
void __iomem **base, u32 *rate)
|
||||
{
|
||||
struct clk *timer_clk;
|
||||
struct clk *pclk;
|
||||
struct reset_control *rstc;
|
||||
int ret;
|
||||
|
||||
*base = of_iomap(np, 0);
|
||||
|
||||
@@ -46,55 +47,67 @@ static void __init timer_get_base_and_rate(struct device_node *np,
|
||||
pr_warn("pclk for %pOFn is present, but could not be activated\n",
|
||||
np);
|
||||
|
||||
if (!of_property_read_u32(np, "clock-freq", rate) &&
|
||||
!of_property_read_u32(np, "clock-frequency", rate))
|
||||
return 0;
|
||||
|
||||
timer_clk = of_clk_get_by_name(np, "timer");
|
||||
if (IS_ERR(timer_clk))
|
||||
goto try_clock_freq;
|
||||
return PTR_ERR(timer_clk);
|
||||
|
||||
if (!clk_prepare_enable(timer_clk)) {
|
||||
*rate = clk_get_rate(timer_clk);
|
||||
return;
|
||||
}
|
||||
ret = clk_prepare_enable(timer_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
try_clock_freq:
|
||||
if (of_property_read_u32(np, "clock-freq", rate) &&
|
||||
of_property_read_u32(np, "clock-frequency", rate))
|
||||
panic("No clock nor clock-frequency property for %pOFn", np);
|
||||
*rate = clk_get_rate(timer_clk);
|
||||
if (!(*rate))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __init add_clockevent(struct device_node *event_timer)
|
||||
static int __init add_clockevent(struct device_node *event_timer)
|
||||
{
|
||||
void __iomem *iobase;
|
||||
struct dw_apb_clock_event_device *ced;
|
||||
u32 irq, rate;
|
||||
int ret = 0;
|
||||
|
||||
irq = irq_of_parse_and_map(event_timer, 0);
|
||||
if (irq == 0)
|
||||
panic("No IRQ for clock event timer");
|
||||
|
||||
timer_get_base_and_rate(event_timer, &iobase, &rate);
|
||||
ret = timer_get_base_and_rate(event_timer, &iobase, &rate);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ced = dw_apb_clockevent_init(-1, event_timer->name, 300, iobase, irq,
|
||||
rate);
|
||||
if (!ced)
|
||||
panic("Unable to initialise clockevent device");
|
||||
return -EINVAL;
|
||||
|
||||
dw_apb_clockevent_register(ced);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __iomem *sched_io_base;
|
||||
static u32 sched_rate;
|
||||
|
||||
static void __init add_clocksource(struct device_node *source_timer)
|
||||
static int __init add_clocksource(struct device_node *source_timer)
|
||||
{
|
||||
void __iomem *iobase;
|
||||
struct dw_apb_clocksource *cs;
|
||||
u32 rate;
|
||||
int ret;
|
||||
|
||||
timer_get_base_and_rate(source_timer, &iobase, &rate);
|
||||
ret = timer_get_base_and_rate(source_timer, &iobase, &rate);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cs = dw_apb_clocksource_init(300, source_timer->name, iobase, rate);
|
||||
if (!cs)
|
||||
panic("Unable to initialise clocksource device");
|
||||
return -EINVAL;
|
||||
|
||||
dw_apb_clocksource_start(cs);
|
||||
dw_apb_clocksource_register(cs);
|
||||
@@ -106,6 +119,8 @@ static void __init add_clocksource(struct device_node *source_timer)
|
||||
*/
|
||||
sched_io_base = iobase + 0x04;
|
||||
sched_rate = rate;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u64 notrace read_sched_clock(void)
|
||||
@@ -146,10 +161,14 @@ static struct delay_timer dw_apb_delay_timer = {
|
||||
static int num_called;
|
||||
static int __init dw_apb_timer_init(struct device_node *timer)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (num_called) {
|
||||
case 1:
|
||||
pr_debug("%s: found clocksource timer\n", __func__);
|
||||
add_clocksource(timer);
|
||||
ret = add_clocksource(timer);
|
||||
if (ret)
|
||||
return ret;
|
||||
init_sched_clock();
|
||||
#ifdef CONFIG_ARM
|
||||
dw_apb_delay_timer.freq = sched_rate;
|
||||
@@ -158,7 +177,9 @@ static int __init dw_apb_timer_init(struct device_node *timer)
|
||||
break;
|
||||
default:
|
||||
pr_debug("%s: found clockevent timer\n", __func__);
|
||||
add_clockevent(timer);
|
||||
ret = add_clockevent(timer);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ static irqreturn_t ingenic_tcu_cevt_cb(int irq, void *dev_id)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static struct clk * __init ingenic_tcu_get_clock(struct device_node *np, int id)
|
||||
static struct clk *ingenic_tcu_get_clock(struct device_node *np, int id)
|
||||
{
|
||||
struct of_phandle_args args;
|
||||
|
||||
|
||||
@@ -319,7 +319,6 @@ static int sh_cmt_enable(struct sh_cmt_channel *ch)
|
||||
{
|
||||
int k, ret;
|
||||
|
||||
pm_runtime_get_sync(&ch->cmt->pdev->dev);
|
||||
dev_pm_syscore_device(&ch->cmt->pdev->dev, true);
|
||||
|
||||
/* enable clock */
|
||||
@@ -394,7 +393,6 @@ static void sh_cmt_disable(struct sh_cmt_channel *ch)
|
||||
clk_disable(ch->cmt->clk);
|
||||
|
||||
dev_pm_syscore_device(&ch->cmt->pdev->dev, false);
|
||||
pm_runtime_put(&ch->cmt->pdev->dev);
|
||||
}
|
||||
|
||||
/* private flags */
|
||||
@@ -562,10 +560,16 @@ static int sh_cmt_start(struct sh_cmt_channel *ch, unsigned long flag)
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
|
||||
if (flag & FLAG_CLOCKSOURCE)
|
||||
pm_runtime_get_sync(&ch->cmt->pdev->dev);
|
||||
|
||||
raw_spin_lock_irqsave(&ch->lock, flags);
|
||||
|
||||
if (!(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE)))
|
||||
if (!(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE))) {
|
||||
if (flag & FLAG_CLOCKEVENT)
|
||||
pm_runtime_get_sync(&ch->cmt->pdev->dev);
|
||||
ret = sh_cmt_enable(ch);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
goto out;
|
||||
@@ -590,14 +594,20 @@ static void sh_cmt_stop(struct sh_cmt_channel *ch, unsigned long flag)
|
||||
f = ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE);
|
||||
ch->flags &= ~flag;
|
||||
|
||||
if (f && !(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE)))
|
||||
if (f && !(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE))) {
|
||||
sh_cmt_disable(ch);
|
||||
if (flag & FLAG_CLOCKEVENT)
|
||||
pm_runtime_put(&ch->cmt->pdev->dev);
|
||||
}
|
||||
|
||||
/* adjust the timeout to maximum if only clocksource left */
|
||||
if ((flag == FLAG_CLOCKEVENT) && (ch->flags & FLAG_CLOCKSOURCE))
|
||||
__sh_cmt_set_next(ch, ch->max_match_value);
|
||||
|
||||
raw_spin_unlock_irqrestore(&ch->lock, flags);
|
||||
|
||||
if (flag & FLAG_CLOCKSOURCE)
|
||||
pm_runtime_put(&ch->cmt->pdev->dev);
|
||||
}
|
||||
|
||||
static struct sh_cmt_channel *cs_to_sh_cmt(struct clocksource *cs)
|
||||
|
||||
@@ -413,10 +413,8 @@ static int __init ttc_setup_clockevent(struct clk *clk,
|
||||
ttcce->ttc.clk = clk;
|
||||
|
||||
err = clk_prepare_enable(ttcce->ttc.clk);
|
||||
if (err) {
|
||||
kfree(ttcce);
|
||||
return err;
|
||||
}
|
||||
if (err)
|
||||
goto out_kfree;
|
||||
|
||||
ttcce->ttc.clk_rate_change_nb.notifier_call =
|
||||
ttc_rate_change_clockevent_cb;
|
||||
@@ -426,7 +424,7 @@ static int __init ttc_setup_clockevent(struct clk *clk,
|
||||
&ttcce->ttc.clk_rate_change_nb);
|
||||
if (err) {
|
||||
pr_warn("Unable to register clock notifier.\n");
|
||||
return err;
|
||||
goto out_kfree;
|
||||
}
|
||||
|
||||
ttcce->ttc.freq = clk_get_rate(ttcce->ttc.clk);
|
||||
@@ -455,15 +453,17 @@ static int __init ttc_setup_clockevent(struct clk *clk,
|
||||
|
||||
err = request_irq(irq, ttc_clock_event_interrupt,
|
||||
IRQF_TIMER, ttcce->ce.name, ttcce);
|
||||
if (err) {
|
||||
kfree(ttcce);
|
||||
return err;
|
||||
}
|
||||
if (err)
|
||||
goto out_kfree;
|
||||
|
||||
clockevents_config_and_register(&ttcce->ce,
|
||||
ttcce->ttc.freq / PRESCALE, 1, 0xfffe);
|
||||
|
||||
return 0;
|
||||
|
||||
out_kfree:
|
||||
kfree(ttcce);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __init ttc_timer_probe(struct platform_device *pdev)
|
||||
|
||||
@@ -1,284 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Mellanox Technologies. All rights reserved.
|
||||
*
|
||||
* This software is available to you under a choice of one of two
|
||||
* licenses. You may choose to be licensed under the terms of the GNU
|
||||
* General Public License (GPL) Version 2, available from the file
|
||||
* COPYING in the main directory of this source tree, or the
|
||||
* OpenIB.org BSD license below:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <soc/nps/common.h>
|
||||
|
||||
#define NPS_MSU_TICK_LOW 0xC8
|
||||
#define NPS_CLUSTER_OFFSET 8
|
||||
#define NPS_CLUSTER_NUM 16
|
||||
|
||||
/* This array is per cluster of CPUs (Each NPS400 cluster got 256 CPUs) */
|
||||
static void *nps_msu_reg_low_addr[NPS_CLUSTER_NUM] __read_mostly;
|
||||
|
||||
static int __init nps_get_timer_clk(struct device_node *node,
|
||||
unsigned long *timer_freq,
|
||||
struct clk **clk)
|
||||
{
|
||||
int ret;
|
||||
|
||||
*clk = of_clk_get(node, 0);
|
||||
ret = PTR_ERR_OR_ZERO(*clk);
|
||||
if (ret) {
|
||||
pr_err("timer missing clk\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(*clk);
|
||||
if (ret) {
|
||||
pr_err("Couldn't enable parent clk\n");
|
||||
clk_put(*clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*timer_freq = clk_get_rate(*clk);
|
||||
if (!(*timer_freq)) {
|
||||
pr_err("Couldn't get clk rate\n");
|
||||
clk_disable_unprepare(*clk);
|
||||
clk_put(*clk);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u64 nps_clksrc_read(struct clocksource *clksrc)
|
||||
{
|
||||
int cluster = raw_smp_processor_id() >> NPS_CLUSTER_OFFSET;
|
||||
|
||||
return (u64)ioread32be(nps_msu_reg_low_addr[cluster]);
|
||||
}
|
||||
|
||||
static int __init nps_setup_clocksource(struct device_node *node)
|
||||
{
|
||||
int ret, cluster;
|
||||
struct clk *clk;
|
||||
unsigned long nps_timer1_freq;
|
||||
|
||||
|
||||
for (cluster = 0; cluster < NPS_CLUSTER_NUM; cluster++)
|
||||
nps_msu_reg_low_addr[cluster] =
|
||||
nps_host_reg((cluster << NPS_CLUSTER_OFFSET),
|
||||
NPS_MSU_BLKID, NPS_MSU_TICK_LOW);
|
||||
|
||||
ret = nps_get_timer_clk(node, &nps_timer1_freq, &clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clocksource_mmio_init(nps_msu_reg_low_addr, "nps-tick",
|
||||
nps_timer1_freq, 300, 32, nps_clksrc_read);
|
||||
if (ret) {
|
||||
pr_err("Couldn't register clock source.\n");
|
||||
clk_disable_unprepare(clk);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
TIMER_OF_DECLARE(ezchip_nps400_clksrc, "ezchip,nps400-timer",
|
||||
nps_setup_clocksource);
|
||||
TIMER_OF_DECLARE(ezchip_nps400_clk_src, "ezchip,nps400-timer1",
|
||||
nps_setup_clocksource);
|
||||
|
||||
#ifdef CONFIG_EZNPS_MTM_EXT
|
||||
#include <soc/nps/mtm.h>
|
||||
|
||||
/* Timer related Aux registers */
|
||||
#define NPS_REG_TIMER0_TSI 0xFFFFF850
|
||||
#define NPS_REG_TIMER0_LIMIT 0x23
|
||||
#define NPS_REG_TIMER0_CTRL 0x22
|
||||
#define NPS_REG_TIMER0_CNT 0x21
|
||||
|
||||
/*
|
||||
* Interrupt Enabled (IE) - re-arm the timer
|
||||
* Not Halted (NH) - is cleared when working with JTAG (for debug)
|
||||
*/
|
||||
#define TIMER0_CTRL_IE BIT(0)
|
||||
#define TIMER0_CTRL_NH BIT(1)
|
||||
|
||||
static unsigned long nps_timer0_freq;
|
||||
static unsigned long nps_timer0_irq;
|
||||
|
||||
static void nps_clkevent_rm_thread(void)
|
||||
{
|
||||
int thread;
|
||||
unsigned int cflags, enabled_threads;
|
||||
|
||||
hw_schd_save(&cflags);
|
||||
|
||||
enabled_threads = read_aux_reg(NPS_REG_TIMER0_TSI);
|
||||
|
||||
/* remove thread from TSI1 */
|
||||
thread = read_aux_reg(CTOP_AUX_THREAD_ID);
|
||||
enabled_threads &= ~(1 << thread);
|
||||
write_aux_reg(NPS_REG_TIMER0_TSI, enabled_threads);
|
||||
|
||||
/* Acknowledge and if needed re-arm the timer */
|
||||
if (!enabled_threads)
|
||||
write_aux_reg(NPS_REG_TIMER0_CTRL, TIMER0_CTRL_NH);
|
||||
else
|
||||
write_aux_reg(NPS_REG_TIMER0_CTRL,
|
||||
TIMER0_CTRL_IE | TIMER0_CTRL_NH);
|
||||
|
||||
hw_schd_restore(cflags);
|
||||
}
|
||||
|
||||
static void nps_clkevent_add_thread(unsigned long delta)
|
||||
{
|
||||
int thread;
|
||||
unsigned int cflags, enabled_threads;
|
||||
|
||||
hw_schd_save(&cflags);
|
||||
|
||||
/* add thread to TSI1 */
|
||||
thread = read_aux_reg(CTOP_AUX_THREAD_ID);
|
||||
enabled_threads = read_aux_reg(NPS_REG_TIMER0_TSI);
|
||||
enabled_threads |= (1 << thread);
|
||||
write_aux_reg(NPS_REG_TIMER0_TSI, enabled_threads);
|
||||
|
||||
/* set next timer event */
|
||||
write_aux_reg(NPS_REG_TIMER0_LIMIT, delta);
|
||||
write_aux_reg(NPS_REG_TIMER0_CNT, 0);
|
||||
write_aux_reg(NPS_REG_TIMER0_CTRL,
|
||||
TIMER0_CTRL_IE | TIMER0_CTRL_NH);
|
||||
|
||||
hw_schd_restore(cflags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Whenever anyone tries to change modes, we just mask interrupts
|
||||
* and wait for the next event to get set.
|
||||
*/
|
||||
static int nps_clkevent_set_state(struct clock_event_device *dev)
|
||||
{
|
||||
nps_clkevent_rm_thread();
|
||||
disable_percpu_irq(nps_timer0_irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nps_clkevent_set_next_event(unsigned long delta,
|
||||
struct clock_event_device *dev)
|
||||
{
|
||||
nps_clkevent_add_thread(delta);
|
||||
enable_percpu_irq(nps_timer0_irq, IRQ_TYPE_NONE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DEFINE_PER_CPU(struct clock_event_device, nps_clockevent_device) = {
|
||||
.name = "NPS Timer0",
|
||||
.features = CLOCK_EVT_FEAT_ONESHOT,
|
||||
.rating = 300,
|
||||
.set_next_event = nps_clkevent_set_next_event,
|
||||
.set_state_oneshot = nps_clkevent_set_state,
|
||||
.set_state_oneshot_stopped = nps_clkevent_set_state,
|
||||
.set_state_shutdown = nps_clkevent_set_state,
|
||||
.tick_resume = nps_clkevent_set_state,
|
||||
};
|
||||
|
||||
static irqreturn_t timer_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct clock_event_device *evt = dev_id;
|
||||
|
||||
nps_clkevent_rm_thread();
|
||||
evt->event_handler(evt);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int nps_timer_starting_cpu(unsigned int cpu)
|
||||
{
|
||||
struct clock_event_device *evt = this_cpu_ptr(&nps_clockevent_device);
|
||||
|
||||
evt->cpumask = cpumask_of(smp_processor_id());
|
||||
|
||||
clockevents_config_and_register(evt, nps_timer0_freq, 0, ULONG_MAX);
|
||||
enable_percpu_irq(nps_timer0_irq, IRQ_TYPE_NONE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nps_timer_dying_cpu(unsigned int cpu)
|
||||
{
|
||||
disable_percpu_irq(nps_timer0_irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init nps_setup_clockevent(struct device_node *node)
|
||||
{
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
nps_timer0_irq = irq_of_parse_and_map(node, 0);
|
||||
if (nps_timer0_irq <= 0) {
|
||||
pr_err("clockevent: missing irq\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = nps_get_timer_clk(node, &nps_timer0_freq, &clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Needs apriori irq_set_percpu_devid() done in intc map function */
|
||||
ret = request_percpu_irq(nps_timer0_irq, timer_irq_handler,
|
||||
"Timer0 (per-cpu-tick)",
|
||||
&nps_clockevent_device);
|
||||
if (ret) {
|
||||
pr_err("Couldn't request irq\n");
|
||||
clk_disable_unprepare(clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = cpuhp_setup_state(CPUHP_AP_ARC_TIMER_STARTING,
|
||||
"clockevents/nps:starting",
|
||||
nps_timer_starting_cpu,
|
||||
nps_timer_dying_cpu);
|
||||
if (ret) {
|
||||
pr_err("Failed to setup hotplug state\n");
|
||||
clk_disable_unprepare(clk);
|
||||
free_percpu_irq(nps_timer0_irq, &nps_clockevent_device);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
TIMER_OF_DECLARE(ezchip_nps400_clk_evt, "ezchip,nps400-timer0",
|
||||
nps_setup_clockevent);
|
||||
#endif /* CONFIG_EZNPS_MTM_EXT */
|
||||
@@ -143,7 +143,8 @@ static int __init orion_timer_init(struct device_node *np)
|
||||
irq = irq_of_parse_and_map(np, 1);
|
||||
if (irq <= 0) {
|
||||
pr_err("%pOFn: unable to parse timer1 irq\n", np);
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto out_unprep_clk;
|
||||
}
|
||||
|
||||
rate = clk_get_rate(clk);
|
||||
@@ -160,7 +161,7 @@ static int __init orion_timer_init(struct device_node *np)
|
||||
clocksource_mmio_readl_down);
|
||||
if (ret) {
|
||||
pr_err("Failed to initialize mmio timer\n");
|
||||
return ret;
|
||||
goto out_unprep_clk;
|
||||
}
|
||||
|
||||
sched_clock_register(orion_read_sched_clock, 32, rate);
|
||||
@@ -170,7 +171,7 @@ static int __init orion_timer_init(struct device_node *np)
|
||||
"orion_event", NULL);
|
||||
if (ret) {
|
||||
pr_err("%pOFn: unable to setup irq\n", np);
|
||||
return ret;
|
||||
goto out_unprep_clk;
|
||||
}
|
||||
|
||||
ticks_per_jiffy = (clk_get_rate(clk) + HZ/2) / HZ;
|
||||
@@ -183,5 +184,9 @@ static int __init orion_timer_init(struct device_node *np)
|
||||
orion_delay_timer_init(rate);
|
||||
|
||||
return 0;
|
||||
|
||||
out_unprep_clk:
|
||||
clk_disable_unprepare(clk);
|
||||
return ret;
|
||||
}
|
||||
TIMER_OF_DECLARE(orion_timer, "marvell,orion-timer", orion_timer_init);
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
* Copyright (C) 1999 - 2003 ARM Limited
|
||||
* Copyright (C) 2000 Deep Blue Solutions Ltd
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/clockchips.h>
|
||||
@@ -34,8 +37,7 @@
|
||||
#define HISI_TIMER_BGLOAD 0x20
|
||||
#define HISI_TIMER_BGLOAD_H 0x24
|
||||
|
||||
|
||||
struct sp804_timer __initdata arm_sp804_timer = {
|
||||
static struct sp804_timer arm_sp804_timer __initdata = {
|
||||
.load = TIMER_LOAD,
|
||||
.value = TIMER_VALUE,
|
||||
.ctrl = TIMER_CTRL,
|
||||
@@ -44,7 +46,7 @@ struct sp804_timer __initdata arm_sp804_timer = {
|
||||
.width = 32,
|
||||
};
|
||||
|
||||
struct sp804_timer __initdata hisi_sp804_timer = {
|
||||
static struct sp804_timer hisi_sp804_timer __initdata = {
|
||||
.load = HISI_TIMER_LOAD,
|
||||
.load_h = HISI_TIMER_LOAD_H,
|
||||
.value = HISI_TIMER_VALUE,
|
||||
@@ -59,40 +61,23 @@ static struct sp804_clkevt sp804_clkevt[NR_TIMERS];
|
||||
|
||||
static long __init sp804_get_clock_rate(struct clk *clk, const char *name)
|
||||
{
|
||||
long rate;
|
||||
int err;
|
||||
|
||||
if (!clk)
|
||||
clk = clk_get_sys("sp804", name);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("sp804: %s clock not found: %ld\n", name, PTR_ERR(clk));
|
||||
pr_err("%s clock not found: %ld\n", name, PTR_ERR(clk));
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
err = clk_prepare(clk);
|
||||
err = clk_prepare_enable(clk);
|
||||
if (err) {
|
||||
pr_err("sp804: clock failed to prepare: %d\n", err);
|
||||
pr_err("clock failed to enable: %d\n", err);
|
||||
clk_put(clk);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = clk_enable(clk);
|
||||
if (err) {
|
||||
pr_err("sp804: clock failed to enable: %d\n", err);
|
||||
clk_unprepare(clk);
|
||||
clk_put(clk);
|
||||
return err;
|
||||
}
|
||||
|
||||
rate = clk_get_rate(clk);
|
||||
if (rate < 0) {
|
||||
pr_err("sp804: clock failed to get rate: %ld\n", rate);
|
||||
clk_disable(clk);
|
||||
clk_unprepare(clk);
|
||||
clk_put(clk);
|
||||
}
|
||||
|
||||
return rate;
|
||||
return clk_get_rate(clk);
|
||||
}
|
||||
|
||||
static struct sp804_clkevt * __init sp804_clkevt_get(void __iomem *base)
|
||||
@@ -117,10 +102,10 @@ static u64 notrace sp804_read(void)
|
||||
return ~readl_relaxed(sched_clkevt->value);
|
||||
}
|
||||
|
||||
int __init sp804_clocksource_and_sched_clock_init(void __iomem *base,
|
||||
const char *name,
|
||||
struct clk *clk,
|
||||
int use_sched_clock)
|
||||
static int __init sp804_clocksource_and_sched_clock_init(void __iomem *base,
|
||||
const char *name,
|
||||
struct clk *clk,
|
||||
int use_sched_clock)
|
||||
{
|
||||
long rate;
|
||||
struct sp804_clkevt *clkevt;
|
||||
@@ -216,8 +201,8 @@ static struct clock_event_device sp804_clockevent = {
|
||||
.rating = 300,
|
||||
};
|
||||
|
||||
int __init sp804_clockevents_init(void __iomem *base, unsigned int irq,
|
||||
struct clk *clk, const char *name)
|
||||
static int __init sp804_clockevents_init(void __iomem *base, unsigned int irq,
|
||||
struct clk *clk, const char *name)
|
||||
{
|
||||
struct clock_event_device *evt = &sp804_clockevent;
|
||||
long rate;
|
||||
@@ -236,7 +221,7 @@ int __init sp804_clockevents_init(void __iomem *base, unsigned int irq,
|
||||
|
||||
if (request_irq(irq, sp804_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL,
|
||||
"timer", &sp804_clockevent))
|
||||
pr_err("%s: request_irq() failed\n", "timer");
|
||||
pr_err("request_irq() failed\n");
|
||||
clockevents_config_and_register(evt, rate, 0xf, 0xffffffff);
|
||||
|
||||
return 0;
|
||||
@@ -298,7 +283,7 @@ static int __init sp804_of_init(struct device_node *np, struct sp804_timer *time
|
||||
if (of_clk_get_parent_count(np) == 3) {
|
||||
clk2 = of_clk_get(np, 1);
|
||||
if (IS_ERR(clk2)) {
|
||||
pr_err("sp804: %pOFn clock not found: %d\n", np,
|
||||
pr_err("%pOFn clock not found: %d\n", np,
|
||||
(int)PTR_ERR(clk2));
|
||||
clk2 = NULL;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
ccflags-$(CONFIG_RTC_DEBUG) := -DDEBUG
|
||||
|
||||
obj-$(CONFIG_RTC_LIB) += lib.o
|
||||
obj-$(CONFIG_RTC_SYSTOHC) += systohc.o
|
||||
obj-$(CONFIG_RTC_CLASS) += rtc-core.o
|
||||
obj-$(CONFIG_RTC_MC146818_LIB) += rtc-mc146818-lib.o
|
||||
rtc-core-y := class.o interface.o
|
||||
|
||||
@@ -200,8 +200,13 @@ static struct rtc_device *rtc_allocate_device(void)
|
||||
|
||||
device_initialize(&rtc->dev);
|
||||
|
||||
/* Drivers can revise this default after allocating the device. */
|
||||
rtc->set_offset_nsec = NSEC_PER_SEC / 2;
|
||||
/*
|
||||
* Drivers can revise this default after allocating the device.
|
||||
* The default is what most RTCs do: Increment seconds exactly one
|
||||
* second after the write happened. This adds a default transport
|
||||
* time of 5ms which is at least halfways close to reality.
|
||||
*/
|
||||
rtc->set_offset_nsec = NSEC_PER_SEC + 5 * NSEC_PER_MSEC;
|
||||
|
||||
rtc->irq_freq = 1;
|
||||
rtc->max_user_freq = 64;
|
||||
|
||||
@@ -868,6 +868,9 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
|
||||
if (retval)
|
||||
goto cleanup2;
|
||||
|
||||
/* Set the sync offset for the periodic 11min update correct */
|
||||
cmos_rtc.rtc->set_offset_nsec = NSEC_PER_SEC / 2;
|
||||
|
||||
/* export at least the first block of NVRAM */
|
||||
nvmem_cfg.size = address_space - NVRAM_OFFSET;
|
||||
if (rtc_nvmem_register(cmos_rtc.rtc, &nvmem_cfg))
|
||||
|
||||
@@ -8,41 +8,41 @@
|
||||
#include <linux/acpi.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Returns true if a clock update is in progress
|
||||
*/
|
||||
static inline unsigned char mc146818_is_updating(void)
|
||||
{
|
||||
unsigned char uip;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&rtc_lock, flags);
|
||||
uip = (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP);
|
||||
spin_unlock_irqrestore(&rtc_lock, flags);
|
||||
return uip;
|
||||
}
|
||||
|
||||
unsigned int mc146818_get_time(struct rtc_time *time)
|
||||
{
|
||||
unsigned char ctrl;
|
||||
unsigned long flags;
|
||||
unsigned char century = 0;
|
||||
bool retry;
|
||||
|
||||
#ifdef CONFIG_MACH_DECSTATION
|
||||
unsigned int real_year;
|
||||
#endif
|
||||
|
||||
again:
|
||||
spin_lock_irqsave(&rtc_lock, flags);
|
||||
/*
|
||||
* read RTC once any update in progress is done. The update
|
||||
* can take just over 2ms. We wait 20ms. There is no need to
|
||||
* to poll-wait (up to 1s - eeccch) for the falling edge of RTC_UIP.
|
||||
* If you need to know *exactly* when a second has started, enable
|
||||
* periodic update complete interrupts, (via ioctl) and then
|
||||
* immediately read /dev/rtc which will block until you get the IRQ.
|
||||
* Once the read clears, read the RTC time (again via ioctl). Easy.
|
||||
* Check whether there is an update in progress during which the
|
||||
* readout is unspecified. The maximum update time is ~2ms. Poll
|
||||
* every msec for completion.
|
||||
*
|
||||
* Store the second value before checking UIP so a long lasting NMI
|
||||
* which happens to hit after the UIP check cannot make an update
|
||||
* cycle invisible.
|
||||
*/
|
||||
if (mc146818_is_updating())
|
||||
mdelay(20);
|
||||
time->tm_sec = CMOS_READ(RTC_SECONDS);
|
||||
|
||||
if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) {
|
||||
spin_unlock_irqrestore(&rtc_lock, flags);
|
||||
mdelay(1);
|
||||
goto again;
|
||||
}
|
||||
|
||||
/* Revalidate the above readout */
|
||||
if (time->tm_sec != CMOS_READ(RTC_SECONDS)) {
|
||||
spin_unlock_irqrestore(&rtc_lock, flags);
|
||||
goto again;
|
||||
}
|
||||
|
||||
/*
|
||||
* Only the values that we read from the RTC are set. We leave
|
||||
@@ -50,8 +50,6 @@ unsigned int mc146818_get_time(struct rtc_time *time)
|
||||
* RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated
|
||||
* by the RTC when initially set to a non-zero value.
|
||||
*/
|
||||
spin_lock_irqsave(&rtc_lock, flags);
|
||||
time->tm_sec = CMOS_READ(RTC_SECONDS);
|
||||
time->tm_min = CMOS_READ(RTC_MINUTES);
|
||||
time->tm_hour = CMOS_READ(RTC_HOURS);
|
||||
time->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH);
|
||||
@@ -66,8 +64,24 @@ unsigned int mc146818_get_time(struct rtc_time *time)
|
||||
century = CMOS_READ(acpi_gbl_FADT.century);
|
||||
#endif
|
||||
ctrl = CMOS_READ(RTC_CONTROL);
|
||||
/*
|
||||
* Check for the UIP bit again. If it is set now then
|
||||
* the above values may contain garbage.
|
||||
*/
|
||||
retry = CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP;
|
||||
/*
|
||||
* A NMI might have interrupted the above sequence so check whether
|
||||
* the seconds value has changed which indicates that the NMI took
|
||||
* longer than the UIP bit was set. Unlikely, but possible and
|
||||
* there is also virt...
|
||||
*/
|
||||
retry |= time->tm_sec != CMOS_READ(RTC_SECONDS);
|
||||
|
||||
spin_unlock_irqrestore(&rtc_lock, flags);
|
||||
|
||||
if (retry)
|
||||
goto again;
|
||||
|
||||
if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
|
||||
{
|
||||
time->tm_sec = bcd2bin(time->tm_sec);
|
||||
@@ -121,7 +135,6 @@ int mc146818_set_time(struct rtc_time *time)
|
||||
if (yrs > 255) /* They are unsigned */
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&rtc_lock, flags);
|
||||
#ifdef CONFIG_MACH_DECSTATION
|
||||
real_yrs = yrs;
|
||||
leap_yr = ((!((yrs + 1900) % 4) && ((yrs + 1900) % 100)) ||
|
||||
@@ -150,10 +163,8 @@ int mc146818_set_time(struct rtc_time *time)
|
||||
/* These limits and adjustments are independent of
|
||||
* whether the chip is in binary mode or not.
|
||||
*/
|
||||
if (yrs > 169) {
|
||||
spin_unlock_irqrestore(&rtc_lock, flags);
|
||||
if (yrs > 169)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (yrs >= 100)
|
||||
yrs -= 100;
|
||||
@@ -169,6 +180,7 @@ int mc146818_set_time(struct rtc_time *time)
|
||||
century = bin2bcd(century);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&rtc_lock, flags);
|
||||
save_control = CMOS_READ(RTC_CONTROL);
|
||||
CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
|
||||
save_freq_select = CMOS_READ(RTC_FREQ_SELECT);
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/time.h>
|
||||
|
||||
/**
|
||||
* rtc_set_ntp_time - Save NTP synchronized time to the RTC
|
||||
* @now: Current time of day
|
||||
* @target_nsec: pointer for desired now->tv_nsec value
|
||||
*
|
||||
* Replacement for the NTP platform function update_persistent_clock64
|
||||
* that stores time for later retrieval by rtc_hctosys.
|
||||
*
|
||||
* Returns 0 on successful RTC update, -ENODEV if a RTC update is not
|
||||
* possible at all, and various other -errno for specific temporary failure
|
||||
* cases.
|
||||
*
|
||||
* -EPROTO is returned if now.tv_nsec is not close enough to *target_nsec.
|
||||
*
|
||||
* If temporary failure is indicated the caller should try again 'soon'
|
||||
*/
|
||||
int rtc_set_ntp_time(struct timespec64 now, unsigned long *target_nsec)
|
||||
{
|
||||
struct rtc_device *rtc;
|
||||
struct rtc_time tm;
|
||||
struct timespec64 to_set;
|
||||
int err = -ENODEV;
|
||||
bool ok;
|
||||
|
||||
rtc = rtc_class_open(CONFIG_RTC_SYSTOHC_DEVICE);
|
||||
if (!rtc)
|
||||
goto out_err;
|
||||
|
||||
if (!rtc->ops || !rtc->ops->set_time)
|
||||
goto out_close;
|
||||
|
||||
/* Compute the value of tv_nsec we require the caller to supply in
|
||||
* now.tv_nsec. This is the value such that (now +
|
||||
* set_offset_nsec).tv_nsec == 0.
|
||||
*/
|
||||
set_normalized_timespec64(&to_set, 0, -rtc->set_offset_nsec);
|
||||
*target_nsec = to_set.tv_nsec;
|
||||
|
||||
/* The ntp code must call this with the correct value in tv_nsec, if
|
||||
* it does not we update target_nsec and return EPROTO to make the ntp
|
||||
* code try again later.
|
||||
*/
|
||||
ok = rtc_tv_nsec_ok(rtc->set_offset_nsec, &to_set, &now);
|
||||
if (!ok) {
|
||||
err = -EPROTO;
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
rtc_time64_to_tm(to_set.tv_sec, &tm);
|
||||
|
||||
err = rtc_set_time(rtc, &tm);
|
||||
|
||||
out_close:
|
||||
rtc_class_close(rtc);
|
||||
out_err:
|
||||
return err;
|
||||
}
|
||||
@@ -1,12 +1,16 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* This header provides clock numbers for the ingenic,tcu DT binding.
|
||||
* This header provides clock numbers for the Ingenic OST DT binding.
|
||||
*/
|
||||
|
||||
#ifndef __DT_BINDINGS_CLOCK_INGENIC_OST_H__
|
||||
#define __DT_BINDINGS_CLOCK_INGENIC_OST_H__
|
||||
|
||||
#define OST_CLK_PERCPU_TIMER 0
|
||||
#define OST_CLK_GLOBAL_TIMER 1
|
||||
#define OST_CLK_PERCPU_TIMER 1
|
||||
#define OST_CLK_GLOBAL_TIMER 0
|
||||
#define OST_CLK_PERCPU_TIMER0 1
|
||||
#define OST_CLK_PERCPU_TIMER1 2
|
||||
#define OST_CLK_PERCPU_TIMER2 3
|
||||
#define OST_CLK_PERCPU_TIMER3 4
|
||||
|
||||
#endif /* __DT_BINDINGS_CLOCK_INGENIC_OST_H__ */
|
||||
|
||||
@@ -447,6 +447,10 @@ static inline void hrtimer_restart(struct hrtimer *timer)
|
||||
/* Query timers: */
|
||||
extern ktime_t __hrtimer_get_remaining(const struct hrtimer *timer, bool adjust);
|
||||
|
||||
/**
|
||||
* hrtimer_get_remaining - get remaining time for the timer
|
||||
* @timer: the timer to read
|
||||
*/
|
||||
static inline ktime_t hrtimer_get_remaining(const struct hrtimer *timer)
|
||||
{
|
||||
return __hrtimer_get_remaining(timer, false);
|
||||
@@ -458,7 +462,7 @@ extern u64 hrtimer_next_event_without(const struct hrtimer *exclude);
|
||||
extern bool hrtimer_active(const struct hrtimer *timer);
|
||||
|
||||
/**
|
||||
* hrtimer_is_queued = check, whether the timer is on one of the queues
|
||||
* hrtimer_is_queued - check, whether the timer is on one of the queues
|
||||
* @timer: Timer to check
|
||||
*
|
||||
* Returns: True if the timer is queued, false otherwise
|
||||
|
||||
@@ -110,13 +110,36 @@ struct rtc_device {
|
||||
/* Some hardware can't support UIE mode */
|
||||
int uie_unsupported;
|
||||
|
||||
/* Number of nsec it takes to set the RTC clock. This influences when
|
||||
* the set ops are called. An offset:
|
||||
* - of 0.5 s will call RTC set for wall clock time 10.0 s at 9.5 s
|
||||
* - of 1.5 s will call RTC set for wall clock time 10.0 s at 8.5 s
|
||||
* - of -0.5 s will call RTC set for wall clock time 10.0 s at 10.5 s
|
||||
/*
|
||||
* This offset specifies the update timing of the RTC.
|
||||
*
|
||||
* tsched t1 write(t2.tv_sec - 1sec)) t2 RTC increments seconds
|
||||
*
|
||||
* The offset defines how tsched is computed so that the write to
|
||||
* the RTC (t2.tv_sec - 1sec) is correct versus the time required
|
||||
* for the transport of the write and the time which the RTC needs
|
||||
* to increment seconds the first time after the write (t2).
|
||||
*
|
||||
* For direct accessible RTCs tsched ~= t1 because the write time
|
||||
* is negligible. For RTCs behind slow busses the transport time is
|
||||
* significant and has to be taken into account.
|
||||
*
|
||||
* The time between the write (t1) and the first increment after
|
||||
* the write (t2) is RTC specific. For a MC146818 RTC it's 500ms,
|
||||
* for many others it's exactly 1 second. Consult the datasheet.
|
||||
*
|
||||
* The value of this offset is also used to calculate the to be
|
||||
* written value (t2.tv_sec - 1sec) at tsched.
|
||||
*
|
||||
* The default value for this is NSEC_PER_SEC + 10 msec default
|
||||
* transport time. The offset can be adjusted by drivers so the
|
||||
* calculation for the to be written value at tsched becomes
|
||||
* correct:
|
||||
*
|
||||
* newval = tsched + set_offset_nsec - NSEC_PER_SEC
|
||||
* and (tsched + set_offset_nsec) % NSEC_PER_SEC == 0
|
||||
*/
|
||||
long set_offset_nsec;
|
||||
unsigned long set_offset_nsec;
|
||||
|
||||
bool registered;
|
||||
|
||||
@@ -165,7 +188,6 @@ int __rtc_register_device(struct module *owner, struct rtc_device *rtc);
|
||||
|
||||
extern int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm);
|
||||
extern int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm);
|
||||
extern int rtc_set_ntp_time(struct timespec64 now, unsigned long *target_nsec);
|
||||
int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm);
|
||||
extern int rtc_read_alarm(struct rtc_device *rtc,
|
||||
struct rtc_wkalrm *alrm);
|
||||
@@ -205,39 +227,6 @@ static inline bool is_leap_year(unsigned int year)
|
||||
return (!(year % 4) && (year % 100)) || !(year % 400);
|
||||
}
|
||||
|
||||
/* Determine if we can call to driver to set the time. Drivers can only be
|
||||
* called to set a second aligned time value, and the field set_offset_nsec
|
||||
* specifies how far away from the second aligned time to call the driver.
|
||||
*
|
||||
* This also computes 'to_set' which is the time we are trying to set, and has
|
||||
* a zero in tv_nsecs, such that:
|
||||
* to_set - set_delay_nsec == now +/- FUZZ
|
||||
*
|
||||
*/
|
||||
static inline bool rtc_tv_nsec_ok(s64 set_offset_nsec,
|
||||
struct timespec64 *to_set,
|
||||
const struct timespec64 *now)
|
||||
{
|
||||
/* Allowed error in tv_nsec, arbitarily set to 5 jiffies in ns. */
|
||||
const unsigned long TIME_SET_NSEC_FUZZ = TICK_NSEC * 5;
|
||||
struct timespec64 delay = {.tv_sec = 0,
|
||||
.tv_nsec = set_offset_nsec};
|
||||
|
||||
*to_set = timespec64_add(*now, delay);
|
||||
|
||||
if (to_set->tv_nsec < TIME_SET_NSEC_FUZZ) {
|
||||
to_set->tv_nsec = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (to_set->tv_nsec > NSEC_PER_SEC - TIME_SET_NSEC_FUZZ) {
|
||||
to_set->tv_sec++;
|
||||
to_set->tv_nsec = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#define rtc_register_device(device) \
|
||||
__rtc_register_device(THIS_MODULE, device)
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user