mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 12:28:41 +08:00
Merge commit 'ccbf62d8a284cf181ac28c8e8407dd077d90dd4b' into for-next
backmerge to avoid kernel/acct.c conflict
This commit is contained in:
commit
82df9c8beb
@ -54,7 +54,7 @@
|
||||
!Ikernel/sched/cpupri.c
|
||||
!Ikernel/sched/fair.c
|
||||
!Iinclude/linux/completion.h
|
||||
!Ekernel/timer.c
|
||||
!Ekernel/time/timer.c
|
||||
</sect1>
|
||||
<sect1><title>Wait queues and Wake events</title>
|
||||
!Iinclude/linux/wait.h
|
||||
@ -63,7 +63,7 @@
|
||||
<sect1><title>High-resolution timers</title>
|
||||
!Iinclude/linux/ktime.h
|
||||
!Iinclude/linux/hrtimer.h
|
||||
!Ekernel/hrtimer.c
|
||||
!Ekernel/time/hrtimer.c
|
||||
</sect1>
|
||||
<sect1><title>Workqueues and Kevents</title>
|
||||
!Ekernel/workqueue.c
|
||||
|
@ -0,0 +1,29 @@
|
||||
* Cirrus Logic CLPS711X Timer Counter
|
||||
|
||||
Required properties:
|
||||
- compatible: Shall contain "cirrus,clps711x-timer".
|
||||
- reg : Address and length of the register set.
|
||||
- interrupts: The interrupt number of the timer.
|
||||
- clocks : phandle of timer reference clock.
|
||||
|
||||
Note: Each timer should have an alias correctly numbered in "aliases" node.
|
||||
|
||||
Example:
|
||||
aliases {
|
||||
timer0 = &timer1;
|
||||
timer1 = &timer2;
|
||||
};
|
||||
|
||||
timer1: timer@80000300 {
|
||||
compatible = "cirrus,ep7312-timer", "cirrus,clps711x-timer";
|
||||
reg = <0x80000300 0x4>;
|
||||
interrupts = <8>;
|
||||
clocks = <&clks 5>;
|
||||
};
|
||||
|
||||
timer2: timer@80000340 {
|
||||
compatible = "cirrus,ep7312-timer", "cirrus,clps711x-timer";
|
||||
reg = <0x80000340 0x4>;
|
||||
interrupts = <9>;
|
||||
clocks = <&clks 6>;
|
||||
};
|
@ -0,0 +1,17 @@
|
||||
Mediatek MT6577, MT6572 and MT6589 Timers
|
||||
---------------------------------------
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "mediatek,mt6577-timer"
|
||||
- reg: Should contain location and length for timers register.
|
||||
- clocks: Clocks driving the timer hardware. This list should include two
|
||||
clocks. The order is system clock and as second clock the RTC clock.
|
||||
|
||||
Examples:
|
||||
|
||||
timer@10008000 {
|
||||
compatible = "mediatek,mt6577-timer";
|
||||
reg = <0x10008000 0x80>;
|
||||
interrupts = <GIC_SPI 113 IRQ_TYPE_LEVEL_LOW>;
|
||||
clocks = <&system_clk>, <&rtc_clk>;
|
||||
};
|
47
Documentation/devicetree/bindings/timer/renesas,cmt.txt
Normal file
47
Documentation/devicetree/bindings/timer/renesas,cmt.txt
Normal file
@ -0,0 +1,47 @@
|
||||
* Renesas R-Car Compare Match Timer (CMT)
|
||||
|
||||
The CMT is a multi-channel 16/32/48-bit timer/counter with configurable clock
|
||||
inputs and programmable compare match.
|
||||
|
||||
Channels share hardware resources but their counter and compare match value
|
||||
are independent. A particular CMT instance can implement only a subset of the
|
||||
channels supported by the CMT model. Channel indices represent the hardware
|
||||
position of the channel in the CMT and don't match the channel numbers in the
|
||||
datasheets.
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible: must contain one of the following.
|
||||
- "renesas,cmt-32" for the 32-bit CMT
|
||||
(CMT0 on sh7372, sh73a0 and r8a7740)
|
||||
- "renesas,cmt-32-fast" for the 32-bit CMT with fast clock support
|
||||
(CMT[234] on sh7372, sh73a0 and r8a7740)
|
||||
- "renesas,cmt-48" for the 48-bit CMT
|
||||
(CMT1 on sh7372, sh73a0 and r8a7740)
|
||||
- "renesas,cmt-48-gen2" for the second generation 48-bit CMT
|
||||
(CMT[01] on r8a73a4, r8a7790 and r8a7791)
|
||||
|
||||
- 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.
|
||||
|
||||
- renesas,channels-mask: bitmask of the available channels.
|
||||
|
||||
|
||||
Example: R8A7790 (R-Car H2) CMT0 node
|
||||
|
||||
CMT0 on R8A7790 implements hardware channels 5 and 6 only and names
|
||||
them channels 0 and 1 in the documentation.
|
||||
|
||||
cmt0: timer@ffca0000 {
|
||||
compatible = "renesas,cmt-48-gen2";
|
||||
reg = <0 0xffca0000 0 0x1004>;
|
||||
interrupts = <0 142 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<0 142 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&mstp1_clks R8A7790_CLK_CMT0>;
|
||||
clock-names = "fck";
|
||||
|
||||
renesas,channels-mask = <0x60>;
|
||||
};
|
39
Documentation/devicetree/bindings/timer/renesas,mtu2.txt
Normal file
39
Documentation/devicetree/bindings/timer/renesas,mtu2.txt
Normal file
@ -0,0 +1,39 @@
|
||||
* Renesas R-Car Multi-Function Timer Pulse Unit 2 (MTU2)
|
||||
|
||||
The MTU2 is a multi-purpose, multi-channel timer/counter with configurable
|
||||
clock inputs and programmable compare match.
|
||||
|
||||
Channels share hardware resources but their counter and compare match value
|
||||
are independent. The MTU2 hardware supports five channels indexed from 0 to 4.
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible: must contain "renesas,mtu2"
|
||||
|
||||
- reg: base address and length of the registers block for the timer module.
|
||||
|
||||
- interrupts: interrupt specifiers for the timer, one for each entry in
|
||||
interrupt-names.
|
||||
- interrupt-names: must contain one entry named "tgi?a" for each enabled
|
||||
channel, where "?" is the channel index expressed as one digit from "0" to
|
||||
"4".
|
||||
|
||||
- clocks: a list of phandle + clock-specifier pairs, one for each entry
|
||||
in clock-names.
|
||||
- clock-names: must contain "fck" for the functional clock.
|
||||
|
||||
|
||||
Example: R7S72100 (RZ/A1H) MTU2 node
|
||||
|
||||
mtu2: timer@fcff0000 {
|
||||
compatible = "renesas,mtu2";
|
||||
reg = <0xfcff0000 0x400>;
|
||||
interrupts = <0 139 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<0 146 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<0 150 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<0 154 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<0 159 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "tgi0a", "tgi1a", "tgi2a", "tgi3a", "tgi4a";
|
||||
clocks = <&mstp3_clks R7S72100_CLK_MTU2>;
|
||||
clock-names = "fck";
|
||||
};
|
39
Documentation/devicetree/bindings/timer/renesas,tmu.txt
Normal file
39
Documentation/devicetree/bindings/timer/renesas,tmu.txt
Normal file
@ -0,0 +1,39 @@
|
||||
* Renesas 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 "renesas,tmu"
|
||||
|
||||
- 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";
|
||||
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>;
|
||||
};
|
@ -77,6 +77,7 @@ lsi LSI Corp. (LSI Logic)
|
||||
lltc Linear Technology Corporation
|
||||
marvell Marvell Technology Group Ltd.
|
||||
maxim Maxim Integrated Products
|
||||
mediatek MediaTek Inc.
|
||||
micrel Micrel Inc.
|
||||
microchip Microchip Technology Inc.
|
||||
mosaixtech Mosaix Technologies, Inc.
|
||||
|
@ -1743,6 +1743,25 @@ pair provide additional information particular to the objects they represent.
|
||||
While the first three lines are mandatory and always printed, the rest is
|
||||
optional and may be omitted if no marks created yet.
|
||||
|
||||
Timerfd files
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
pos: 0
|
||||
flags: 02
|
||||
mnt_id: 9
|
||||
clockid: 0
|
||||
ticks: 0
|
||||
settime flags: 01
|
||||
it_value: (0, 49406829)
|
||||
it_interval: (1, 0)
|
||||
|
||||
where 'clockid' is the clock type and 'ticks' is the number of the timer expirations
|
||||
that have occurred [see timerfd_create(2) for details]. 'settime flags' are
|
||||
flags in octal form been used to setup the timer [see timerfd_settime(2) for
|
||||
details]. 'it_value' is remaining time until the timer exiration.
|
||||
'it_interval' is the interval for the timer. Note the timer might be set up
|
||||
with TIMER_ABSTIME option which will be shown in 'settime flags', but 'it_value'
|
||||
still exhibits timer's remaining time.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
Configuring procfs
|
||||
|
@ -4204,7 +4204,7 @@ L: linux-kernel@vger.kernel.org
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git timers/core
|
||||
S: Maintained
|
||||
F: Documentation/timers/
|
||||
F: kernel/hrtimer.c
|
||||
F: kernel/time/hrtimer.c
|
||||
F: kernel/time/clockevents.c
|
||||
F: kernel/time/tick*.*
|
||||
F: kernel/time/timer_*.c
|
||||
@ -7026,10 +7026,10 @@ POSIX CLOCKS and TIMERS
|
||||
M: Thomas Gleixner <tglx@linutronix.de>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git timers/core
|
||||
S: Supported
|
||||
S: Maintained
|
||||
F: fs/timerfd.c
|
||||
F: include/linux/timer*
|
||||
F: kernel/*timer*
|
||||
F: kernel/time/*timer*
|
||||
|
||||
POWER SUPPLY CLASS/SUBSYSTEM and DRIVERS
|
||||
M: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
|
||||
|
@ -65,7 +65,6 @@ config ARM
|
||||
select HAVE_UID16
|
||||
select HAVE_VIRT_CPU_ACCOUNTING_GEN
|
||||
select IRQ_FORCED_THREADING
|
||||
select KTIME_SCALAR
|
||||
select MODULES_USE_ELF_REL
|
||||
select NO_BOOTMEM
|
||||
select OLD_SIGACTION
|
||||
@ -635,6 +634,7 @@ config ARCH_PXA
|
||||
select AUTO_ZRELADDR
|
||||
select CLKDEV_LOOKUP
|
||||
select CLKSRC_MMIO
|
||||
select CLKSRC_OF
|
||||
select GENERIC_CLOCKEVENTS
|
||||
select GPIO_PXA
|
||||
select HAVE_IDE
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
# Common support (must be linked before board specific support)
|
||||
obj-y += clock.o devices.o generic.o irq.o \
|
||||
time.o reset.o
|
||||
reset.o
|
||||
obj-$(CONFIG_PM) += pm.o sleep.o standby.o
|
||||
|
||||
# Generic drivers that other drivers may depend upon
|
||||
|
@ -25,11 +25,13 @@
|
||||
#include <asm/mach/map.h>
|
||||
#include <asm/mach-types.h>
|
||||
|
||||
#include <mach/irqs.h>
|
||||
#include <mach/reset.h>
|
||||
#include <mach/smemc.h>
|
||||
#include <mach/pxa3xx-regs.h>
|
||||
|
||||
#include "generic.h"
|
||||
#include <clocksource/pxa.h>
|
||||
|
||||
void clear_reset_status(unsigned int mask)
|
||||
{
|
||||
@ -56,6 +58,15 @@ unsigned long get_clock_tick_rate(void)
|
||||
}
|
||||
EXPORT_SYMBOL(get_clock_tick_rate);
|
||||
|
||||
/*
|
||||
* For non device-tree builds, keep legacy timer init
|
||||
*/
|
||||
void pxa_timer_init(void)
|
||||
{
|
||||
pxa_timer_nodt_init(IRQ_OST0, io_p2v(0x40a00000),
|
||||
get_clock_tick_rate());
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the clock frequency as reflected by CCCR and the turbo flag.
|
||||
* We assume these values have been applied via a fcs.
|
||||
|
@ -1,162 +0,0 @@
|
||||
/*
|
||||
* arch/arm/mach-pxa/time.c
|
||||
*
|
||||
* PXA clocksource, clockevents, and OST interrupt handlers.
|
||||
* Copyright (c) 2007 by Bill Gatliff <bgat@billgatliff.com>.
|
||||
*
|
||||
* Derived from Nicolas Pitre's PXA timer handler Copyright (c) 2001
|
||||
* by MontaVista Software, Inc. (Nico, your code rocks!)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/sched_clock.h>
|
||||
|
||||
#include <asm/div64.h>
|
||||
#include <asm/mach/irq.h>
|
||||
#include <asm/mach/time.h>
|
||||
#include <mach/regs-ost.h>
|
||||
#include <mach/irqs.h>
|
||||
|
||||
/*
|
||||
* This is PXA's sched_clock implementation. This has a resolution
|
||||
* of at least 308 ns and a maximum value of 208 days.
|
||||
*
|
||||
* The return value is guaranteed to be monotonic in that range as
|
||||
* long as there is always less than 582 seconds between successive
|
||||
* calls to sched_clock() which should always be the case in practice.
|
||||
*/
|
||||
|
||||
static u64 notrace pxa_read_sched_clock(void)
|
||||
{
|
||||
return readl_relaxed(OSCR);
|
||||
}
|
||||
|
||||
|
||||
#define MIN_OSCR_DELTA 16
|
||||
|
||||
static irqreturn_t
|
||||
pxa_ost0_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct clock_event_device *c = dev_id;
|
||||
|
||||
/* Disarm the compare/match, signal the event. */
|
||||
writel_relaxed(readl_relaxed(OIER) & ~OIER_E0, OIER);
|
||||
writel_relaxed(OSSR_M0, OSSR);
|
||||
c->event_handler(c);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int
|
||||
pxa_osmr0_set_next_event(unsigned long delta, struct clock_event_device *dev)
|
||||
{
|
||||
unsigned long next, oscr;
|
||||
|
||||
writel_relaxed(readl_relaxed(OIER) | OIER_E0, OIER);
|
||||
next = readl_relaxed(OSCR) + delta;
|
||||
writel_relaxed(next, OSMR0);
|
||||
oscr = readl_relaxed(OSCR);
|
||||
|
||||
return (signed)(next - oscr) <= MIN_OSCR_DELTA ? -ETIME : 0;
|
||||
}
|
||||
|
||||
static void
|
||||
pxa_osmr0_set_mode(enum clock_event_mode mode, struct clock_event_device *dev)
|
||||
{
|
||||
switch (mode) {
|
||||
case CLOCK_EVT_MODE_ONESHOT:
|
||||
writel_relaxed(readl_relaxed(OIER) & ~OIER_E0, OIER);
|
||||
writel_relaxed(OSSR_M0, OSSR);
|
||||
break;
|
||||
|
||||
case CLOCK_EVT_MODE_UNUSED:
|
||||
case CLOCK_EVT_MODE_SHUTDOWN:
|
||||
/* initializing, released, or preparing for suspend */
|
||||
writel_relaxed(readl_relaxed(OIER) & ~OIER_E0, OIER);
|
||||
writel_relaxed(OSSR_M0, OSSR);
|
||||
break;
|
||||
|
||||
case CLOCK_EVT_MODE_RESUME:
|
||||
case CLOCK_EVT_MODE_PERIODIC:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static unsigned long osmr[4], oier, oscr;
|
||||
|
||||
static void pxa_timer_suspend(struct clock_event_device *cedev)
|
||||
{
|
||||
osmr[0] = readl_relaxed(OSMR0);
|
||||
osmr[1] = readl_relaxed(OSMR1);
|
||||
osmr[2] = readl_relaxed(OSMR2);
|
||||
osmr[3] = readl_relaxed(OSMR3);
|
||||
oier = readl_relaxed(OIER);
|
||||
oscr = readl_relaxed(OSCR);
|
||||
}
|
||||
|
||||
static void pxa_timer_resume(struct clock_event_device *cedev)
|
||||
{
|
||||
/*
|
||||
* Ensure that we have at least MIN_OSCR_DELTA between match
|
||||
* register 0 and the OSCR, to guarantee that we will receive
|
||||
* the one-shot timer interrupt. We adjust OSMR0 in preference
|
||||
* to OSCR to guarantee that OSCR is monotonically incrementing.
|
||||
*/
|
||||
if (osmr[0] - oscr < MIN_OSCR_DELTA)
|
||||
osmr[0] += MIN_OSCR_DELTA;
|
||||
|
||||
writel_relaxed(osmr[0], OSMR0);
|
||||
writel_relaxed(osmr[1], OSMR1);
|
||||
writel_relaxed(osmr[2], OSMR2);
|
||||
writel_relaxed(osmr[3], OSMR3);
|
||||
writel_relaxed(oier, OIER);
|
||||
writel_relaxed(oscr, OSCR);
|
||||
}
|
||||
#else
|
||||
#define pxa_timer_suspend NULL
|
||||
#define pxa_timer_resume NULL
|
||||
#endif
|
||||
|
||||
static struct clock_event_device ckevt_pxa_osmr0 = {
|
||||
.name = "osmr0",
|
||||
.features = CLOCK_EVT_FEAT_ONESHOT,
|
||||
.rating = 200,
|
||||
.set_next_event = pxa_osmr0_set_next_event,
|
||||
.set_mode = pxa_osmr0_set_mode,
|
||||
.suspend = pxa_timer_suspend,
|
||||
.resume = pxa_timer_resume,
|
||||
};
|
||||
|
||||
static struct irqaction pxa_ost0_irq = {
|
||||
.name = "ost0",
|
||||
.flags = IRQF_TIMER | IRQF_IRQPOLL,
|
||||
.handler = pxa_ost0_interrupt,
|
||||
.dev_id = &ckevt_pxa_osmr0,
|
||||
};
|
||||
|
||||
void __init pxa_timer_init(void)
|
||||
{
|
||||
unsigned long clock_tick_rate = get_clock_tick_rate();
|
||||
|
||||
writel_relaxed(0, OIER);
|
||||
writel_relaxed(OSSR_M0 | OSSR_M1 | OSSR_M2 | OSSR_M3, OSSR);
|
||||
|
||||
sched_clock_register(pxa_read_sched_clock, 32, clock_tick_rate);
|
||||
|
||||
ckevt_pxa_osmr0.cpumask = cpumask_of(0);
|
||||
|
||||
setup_irq(IRQ_OST0, &pxa_ost0_irq);
|
||||
|
||||
clocksource_mmio_init(OSCR, "oscr0", clock_tick_rate, 200, 32,
|
||||
clocksource_mmio_readl_up);
|
||||
clockevents_config_and_register(&ckevt_pxa_osmr0, clock_tick_rate,
|
||||
MIN_OSCR_DELTA * 2, 0x7fffffff);
|
||||
}
|
@ -23,7 +23,6 @@ config HEXAGON
|
||||
select GENERIC_IOMAP
|
||||
select GENERIC_SMP_IDLE_THREAD
|
||||
select STACKTRACE_SUPPORT
|
||||
select KTIME_SCALAR
|
||||
select GENERIC_CLOCKEVENTS
|
||||
select GENERIC_CLOCKEVENTS_BROADCAST
|
||||
select MODULES_USE_ELF_RELA
|
||||
|
@ -137,7 +137,6 @@ config S390
|
||||
select HAVE_SYSCALL_TRACEPOINTS
|
||||
select HAVE_UID16 if 32BIT
|
||||
select HAVE_VIRT_CPU_ACCOUNTING
|
||||
select KTIME_SCALAR if 32BIT
|
||||
select MODULES_USE_ELF_RELA
|
||||
select NO_BOOTMEM
|
||||
select OLD_SIGACTION
|
||||
|
@ -260,7 +260,6 @@ void update_vsyscall_tz(void)
|
||||
|
||||
void update_vsyscall(struct timekeeper *tk)
|
||||
{
|
||||
struct timespec wall_time = tk_xtime(tk);
|
||||
struct timespec *wtm = &tk->wall_to_monotonic;
|
||||
struct clocksource *clock = tk->clock;
|
||||
|
||||
@ -271,12 +270,12 @@ void update_vsyscall(struct timekeeper *tk)
|
||||
++vdso_data->tb_update_count;
|
||||
smp_wmb();
|
||||
vdso_data->xtime_tod_stamp = clock->cycle_last;
|
||||
vdso_data->xtime_clock_sec = wall_time.tv_sec;
|
||||
vdso_data->xtime_clock_nsec = wall_time.tv_nsec;
|
||||
vdso_data->xtime_clock_sec = tk->xtime_sec;
|
||||
vdso_data->xtime_clock_nsec = tk->xtime_nsec;
|
||||
vdso_data->wtom_clock_sec = wtm->tv_sec;
|
||||
vdso_data->wtom_clock_nsec = wtm->tv_nsec;
|
||||
vdso_data->mult = clock->mult;
|
||||
vdso_data->shift = clock->shift;
|
||||
vdso_data->mult = tk->mult;
|
||||
vdso_data->shift = tk->shift;
|
||||
smp_wmb();
|
||||
++vdso_data->tb_update_count;
|
||||
}
|
||||
|
@ -83,10 +83,11 @@ int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz)
|
||||
if (count & 1)
|
||||
continue;
|
||||
|
||||
cycles = (get_cycles() - vdso_data->xtime_tod_stamp);
|
||||
ns = (cycles * vdso_data->mult) >> vdso_data->shift;
|
||||
sec = vdso_data->xtime_clock_sec;
|
||||
ns += vdso_data->xtime_clock_nsec;
|
||||
cycles = get_cycles() - vdso_data->xtime_tod_stamp;
|
||||
ns = (cycles * vdso_data->mult) + vdso_data->xtime_clock_nsec;
|
||||
ns >>= vdso_data->shift;
|
||||
|
||||
if (ns >= NSEC_PER_SEC) {
|
||||
ns -= NSEC_PER_SEC;
|
||||
sec += 1;
|
||||
|
@ -111,7 +111,6 @@ config X86
|
||||
select ARCH_CLOCKSOURCE_DATA
|
||||
select GENERIC_CLOCKEVENTS_BROADCAST if X86_64 || (X86_32 && X86_LOCAL_APIC)
|
||||
select GENERIC_TIME_VSYSCALL
|
||||
select KTIME_SCALAR if X86_32
|
||||
select GENERIC_STRNCPY_FROM_USER
|
||||
select GENERIC_STRNLEN_USER
|
||||
select HAVE_CONTEXT_TRACKING if X86_64
|
||||
|
@ -1,3 +1,5 @@
|
||||
menu "Clock Source drivers"
|
||||
|
||||
config CLKSRC_OF
|
||||
bool
|
||||
|
||||
@ -125,6 +127,7 @@ config CLKSRC_METAG_GENERIC
|
||||
|
||||
config CLKSRC_EXYNOS_MCT
|
||||
def_bool y if ARCH_EXYNOS
|
||||
depends on !ARM64
|
||||
help
|
||||
Support for Multi Core Timer controller on Exynos SoCs.
|
||||
|
||||
@ -149,6 +152,11 @@ config VF_PIT_TIMER
|
||||
config SYS_SUPPORTS_SH_CMT
|
||||
bool
|
||||
|
||||
config MTK_TIMER
|
||||
select CLKSRC_OF
|
||||
select CLKSRC_MMIO
|
||||
bool
|
||||
|
||||
config SYS_SUPPORTS_SH_MTU2
|
||||
bool
|
||||
|
||||
@ -173,7 +181,7 @@ config SH_TIMER_MTU2
|
||||
default SYS_SUPPORTS_SH_MTU2
|
||||
help
|
||||
This enables build of a clockevent driver for the Multi-Function
|
||||
Timer Pulse Unit 2 (TMU2) hardware available on SoCs from Renesas.
|
||||
Timer Pulse Unit 2 (MTU2) hardware available on SoCs from Renesas.
|
||||
This hardware comes with 16 bit-timer registers.
|
||||
|
||||
config SH_TIMER_TMU
|
||||
@ -187,7 +195,7 @@ config SH_TIMER_TMU
|
||||
|
||||
config EM_TIMER_STI
|
||||
bool "Renesas STI timer driver" if COMPILE_TEST
|
||||
depends on GENERIC_CLOCKEVENTS
|
||||
depends on GENERIC_CLOCKEVENTS && HAS_IOMEM
|
||||
default SYS_SUPPORTS_EM_STI
|
||||
help
|
||||
This enables build of a clocksource and clockevent driver for
|
||||
@ -207,3 +215,5 @@ config CLKSRC_VERSATILE
|
||||
counter available in the "System Registers" block of
|
||||
ARM Versatile, RealView and Versatile Express reference
|
||||
platforms.
|
||||
|
||||
endmenu
|
||||
|
@ -16,9 +16,11 @@ obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o
|
||||
obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o
|
||||
obj-$(CONFIG_ORION_TIMER) += time-orion.o
|
||||
obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o
|
||||
obj-$(CONFIG_ARCH_CLPS711X) += clps711x-timer.o
|
||||
obj-$(CONFIG_ARCH_MARCO) += timer-marco.o
|
||||
obj-$(CONFIG_ARCH_MOXART) += moxart_timer.o
|
||||
obj-$(CONFIG_ARCH_MXS) += mxs_timer.o
|
||||
obj-$(CONFIG_ARCH_PXA) += pxa_timer.o
|
||||
obj-$(CONFIG_ARCH_PRIMA2) += timer-prima2.o
|
||||
obj-$(CONFIG_ARCH_U300) += timer-u300.o
|
||||
obj-$(CONFIG_SUN4I_TIMER) += sun4i_timer.o
|
||||
@ -34,6 +36,7 @@ obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o
|
||||
obj-$(CONFIG_FSL_FTM_TIMER) += fsl_ftm_timer.o
|
||||
obj-$(CONFIG_VF_PIT_TIMER) += vf_pit_timer.o
|
||||
obj-$(CONFIG_CLKSRC_QCOM) += qcom-timer.o
|
||||
obj-$(CONFIG_MTK_TIMER) += mtk_timer.o
|
||||
|
||||
obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
|
||||
obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o
|
||||
|
131
drivers/clocksource/clps711x-timer.c
Normal file
131
drivers/clocksource/clps711x-timer.c
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Cirrus Logic CLPS711X clocksource driver
|
||||
*
|
||||
* Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/sched_clock.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
enum {
|
||||
CLPS711X_CLKSRC_CLOCKSOURCE,
|
||||
CLPS711X_CLKSRC_CLOCKEVENT,
|
||||
};
|
||||
|
||||
static void __iomem *tcd;
|
||||
|
||||
static u64 notrace clps711x_sched_clock_read(void)
|
||||
{
|
||||
return ~readw(tcd);
|
||||
}
|
||||
|
||||
static int __init _clps711x_clksrc_init(struct clk *clock, void __iomem *base)
|
||||
{
|
||||
unsigned long rate;
|
||||
|
||||
if (!base)
|
||||
return -ENOMEM;
|
||||
if (IS_ERR(clock))
|
||||
return PTR_ERR(clock);
|
||||
|
||||
rate = clk_get_rate(clock);
|
||||
|
||||
tcd = base;
|
||||
|
||||
clocksource_mmio_init(tcd, "clps711x-clocksource", rate, 300, 16,
|
||||
clocksource_mmio_readw_down);
|
||||
|
||||
sched_clock_register(clps711x_sched_clock_read, 16, rate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t clps711x_timer_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct clock_event_device *evt = dev_id;
|
||||
|
||||
evt->event_handler(evt);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void clps711x_clockevent_set_mode(enum clock_event_mode mode,
|
||||
struct clock_event_device *evt)
|
||||
{
|
||||
}
|
||||
|
||||
static int __init _clps711x_clkevt_init(struct clk *clock, void __iomem *base,
|
||||
unsigned int irq)
|
||||
{
|
||||
struct clock_event_device *clkevt;
|
||||
unsigned long rate;
|
||||
|
||||
if (!irq)
|
||||
return -EINVAL;
|
||||
if (!base)
|
||||
return -ENOMEM;
|
||||
if (IS_ERR(clock))
|
||||
return PTR_ERR(clock);
|
||||
|
||||
clkevt = kzalloc(sizeof(*clkevt), GFP_KERNEL);
|
||||
if (!clkevt)
|
||||
return -ENOMEM;
|
||||
|
||||
rate = clk_get_rate(clock);
|
||||
|
||||
/* Set Timer prescaler */
|
||||
writew(DIV_ROUND_CLOSEST(rate, HZ), base);
|
||||
|
||||
clkevt->name = "clps711x-clockevent";
|
||||
clkevt->rating = 300;
|
||||
clkevt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_C3STOP;
|
||||
clkevt->set_mode = clps711x_clockevent_set_mode;
|
||||
clkevt->cpumask = cpumask_of(0);
|
||||
clockevents_config_and_register(clkevt, HZ, 0, 0);
|
||||
|
||||
return request_irq(irq, clps711x_timer_interrupt, IRQF_TIMER,
|
||||
"clps711x-timer", clkevt);
|
||||
}
|
||||
|
||||
void __init clps711x_clksrc_init(void __iomem *tc1_base, void __iomem *tc2_base,
|
||||
unsigned int irq)
|
||||
{
|
||||
struct clk *tc1 = clk_get_sys("clps711x-timer.0", NULL);
|
||||
struct clk *tc2 = clk_get_sys("clps711x-timer.1", NULL);
|
||||
|
||||
BUG_ON(_clps711x_clksrc_init(tc1, tc1_base));
|
||||
BUG_ON(_clps711x_clkevt_init(tc2, tc2_base, irq));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CLKSRC_OF
|
||||
static void __init clps711x_timer_init(struct device_node *np)
|
||||
{
|
||||
unsigned int irq = irq_of_parse_and_map(np, 0);
|
||||
struct clk *clock = of_clk_get(np, 0);
|
||||
void __iomem *base = of_iomap(np, 0);
|
||||
|
||||
switch (of_alias_get_id(np, "timer")) {
|
||||
case CLPS711X_CLKSRC_CLOCKSOURCE:
|
||||
BUG_ON(_clps711x_clksrc_init(clock, base));
|
||||
break;
|
||||
case CLPS711X_CLKSRC_CLOCKEVENT:
|
||||
BUG_ON(_clps711x_clkevt_init(clock, base, irq));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
CLOCKSOURCE_OF_DECLARE(clps711x, "cirrus,clps711x-timer", clps711x_timer_init);
|
||||
#endif
|
@ -94,7 +94,7 @@ static void exynos4_mct_write(unsigned int value, unsigned long offset)
|
||||
u32 mask;
|
||||
u32 i;
|
||||
|
||||
__raw_writel(value, reg_base + offset);
|
||||
writel_relaxed(value, reg_base + offset);
|
||||
|
||||
if (likely(offset >= EXYNOS4_MCT_L_BASE(0))) {
|
||||
stat_addr = (offset & ~EXYNOS4_MCT_L_MASK) + MCT_L_WSTAT_OFFSET;
|
||||
@ -144,8 +144,8 @@ static void exynos4_mct_write(unsigned int value, unsigned long offset)
|
||||
|
||||
/* Wait maximum 1 ms until written values are applied */
|
||||
for (i = 0; i < loops_per_jiffy / 1000 * HZ; i++)
|
||||
if (__raw_readl(reg_base + stat_addr) & mask) {
|
||||
__raw_writel(mask, reg_base + stat_addr);
|
||||
if (readl_relaxed(reg_base + stat_addr) & mask) {
|
||||
writel_relaxed(mask, reg_base + stat_addr);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -157,28 +157,51 @@ static void exynos4_mct_frc_start(void)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = __raw_readl(reg_base + EXYNOS4_MCT_G_TCON);
|
||||
reg = readl_relaxed(reg_base + EXYNOS4_MCT_G_TCON);
|
||||
reg |= MCT_G_TCON_START;
|
||||
exynos4_mct_write(reg, EXYNOS4_MCT_G_TCON);
|
||||
}
|
||||
|
||||
static cycle_t notrace _exynos4_frc_read(void)
|
||||
/**
|
||||
* exynos4_read_count_64 - Read all 64-bits of the global counter
|
||||
*
|
||||
* This will read all 64-bits of the global counter taking care to make sure
|
||||
* that the upper and lower half match. Note that reading the MCT can be quite
|
||||
* slow (hundreds of nanoseconds) so you should use the 32-bit (lower half
|
||||
* only) version when possible.
|
||||
*
|
||||
* Returns the number of cycles in the global counter.
|
||||
*/
|
||||
static u64 exynos4_read_count_64(void)
|
||||
{
|
||||
unsigned int lo, hi;
|
||||
u32 hi2 = __raw_readl(reg_base + EXYNOS4_MCT_G_CNT_U);
|
||||
u32 hi2 = readl_relaxed(reg_base + EXYNOS4_MCT_G_CNT_U);
|
||||
|
||||
do {
|
||||
hi = hi2;
|
||||
lo = __raw_readl(reg_base + EXYNOS4_MCT_G_CNT_L);
|
||||
hi2 = __raw_readl(reg_base + EXYNOS4_MCT_G_CNT_U);
|
||||
lo = readl_relaxed(reg_base + EXYNOS4_MCT_G_CNT_L);
|
||||
hi2 = readl_relaxed(reg_base + EXYNOS4_MCT_G_CNT_U);
|
||||
} while (hi != hi2);
|
||||
|
||||
return ((cycle_t)hi << 32) | lo;
|
||||
}
|
||||
|
||||
/**
|
||||
* exynos4_read_count_32 - Read the lower 32-bits of the global counter
|
||||
*
|
||||
* This will read just the lower 32-bits of the global counter. This is marked
|
||||
* as notrace so it can be used by the scheduler clock.
|
||||
*
|
||||
* Returns the number of cycles in the global counter (lower 32 bits).
|
||||
*/
|
||||
static u32 notrace exynos4_read_count_32(void)
|
||||
{
|
||||
return readl_relaxed(reg_base + EXYNOS4_MCT_G_CNT_L);
|
||||
}
|
||||
|
||||
static cycle_t exynos4_frc_read(struct clocksource *cs)
|
||||
{
|
||||
return _exynos4_frc_read();
|
||||
return exynos4_read_count_32();
|
||||
}
|
||||
|
||||
static void exynos4_frc_resume(struct clocksource *cs)
|
||||
@ -190,21 +213,23 @@ struct clocksource mct_frc = {
|
||||
.name = "mct-frc",
|
||||
.rating = 400,
|
||||
.read = exynos4_frc_read,
|
||||
.mask = CLOCKSOURCE_MASK(64),
|
||||
.mask = CLOCKSOURCE_MASK(32),
|
||||
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
||||
.resume = exynos4_frc_resume,
|
||||
};
|
||||
|
||||
static u64 notrace exynos4_read_sched_clock(void)
|
||||
{
|
||||
return _exynos4_frc_read();
|
||||
return exynos4_read_count_32();
|
||||
}
|
||||
|
||||
static struct delay_timer exynos4_delay_timer;
|
||||
|
||||
static cycles_t exynos4_read_current_timer(void)
|
||||
{
|
||||
return _exynos4_frc_read();
|
||||
BUILD_BUG_ON_MSG(sizeof(cycles_t) != sizeof(u32),
|
||||
"cycles_t needs to move to 32-bit for ARM64 usage");
|
||||
return exynos4_read_count_32();
|
||||
}
|
||||
|
||||
static void __init exynos4_clocksource_init(void)
|
||||
@ -218,14 +243,14 @@ static void __init exynos4_clocksource_init(void)
|
||||
if (clocksource_register_hz(&mct_frc, clk_rate))
|
||||
panic("%s: can't register clocksource\n", mct_frc.name);
|
||||
|
||||
sched_clock_register(exynos4_read_sched_clock, 64, clk_rate);
|
||||
sched_clock_register(exynos4_read_sched_clock, 32, clk_rate);
|
||||
}
|
||||
|
||||
static void exynos4_mct_comp0_stop(void)
|
||||
{
|
||||
unsigned int tcon;
|
||||
|
||||
tcon = __raw_readl(reg_base + EXYNOS4_MCT_G_TCON);
|
||||
tcon = readl_relaxed(reg_base + EXYNOS4_MCT_G_TCON);
|
||||
tcon &= ~(MCT_G_TCON_COMP0_ENABLE | MCT_G_TCON_COMP0_AUTO_INC);
|
||||
|
||||
exynos4_mct_write(tcon, EXYNOS4_MCT_G_TCON);
|
||||
@ -238,14 +263,14 @@ static void exynos4_mct_comp0_start(enum clock_event_mode mode,
|
||||
unsigned int tcon;
|
||||
cycle_t comp_cycle;
|
||||
|
||||
tcon = __raw_readl(reg_base + EXYNOS4_MCT_G_TCON);
|
||||
tcon = readl_relaxed(reg_base + EXYNOS4_MCT_G_TCON);
|
||||
|
||||
if (mode == CLOCK_EVT_MODE_PERIODIC) {
|
||||
tcon |= MCT_G_TCON_COMP0_AUTO_INC;
|
||||
exynos4_mct_write(cycles, EXYNOS4_MCT_G_COMP0_ADD_INCR);
|
||||
}
|
||||
|
||||
comp_cycle = exynos4_frc_read(&mct_frc) + cycles;
|
||||
comp_cycle = exynos4_read_count_64() + cycles;
|
||||
exynos4_mct_write((u32)comp_cycle, EXYNOS4_MCT_G_COMP0_L);
|
||||
exynos4_mct_write((u32)(comp_cycle >> 32), EXYNOS4_MCT_G_COMP0_U);
|
||||
|
||||
@ -327,7 +352,7 @@ static void exynos4_mct_tick_stop(struct mct_clock_event_device *mevt)
|
||||
unsigned long mask = MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START;
|
||||
unsigned long offset = mevt->base + MCT_L_TCON_OFFSET;
|
||||
|
||||
tmp = __raw_readl(reg_base + offset);
|
||||
tmp = readl_relaxed(reg_base + offset);
|
||||
if (tmp & mask) {
|
||||
tmp &= ~mask;
|
||||
exynos4_mct_write(tmp, offset);
|
||||
@ -349,7 +374,7 @@ static void exynos4_mct_tick_start(unsigned long cycles,
|
||||
/* enable MCT tick interrupt */
|
||||
exynos4_mct_write(0x1, mevt->base + MCT_L_INT_ENB_OFFSET);
|
||||
|
||||
tmp = __raw_readl(reg_base + mevt->base + MCT_L_TCON_OFFSET);
|
||||
tmp = readl_relaxed(reg_base + mevt->base + MCT_L_TCON_OFFSET);
|
||||
tmp |= MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START |
|
||||
MCT_L_TCON_INTERVAL_MODE;
|
||||
exynos4_mct_write(tmp, mevt->base + MCT_L_TCON_OFFSET);
|
||||
@ -401,7 +426,7 @@ static int exynos4_mct_tick_clear(struct mct_clock_event_device *mevt)
|
||||
exynos4_mct_tick_stop(mevt);
|
||||
|
||||
/* Clear the MCT tick interrupt */
|
||||
if (__raw_readl(reg_base + mevt->base + MCT_L_INT_CSTAT_OFFSET) & 1) {
|
||||
if (readl_relaxed(reg_base + mevt->base + MCT_L_INT_CSTAT_OFFSET) & 1) {
|
||||
exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET);
|
||||
return 1;
|
||||
} else {
|
||||
|
261
drivers/clocksource/mtk_timer.c
Normal file
261
drivers/clocksource/mtk_timer.c
Normal file
@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Mediatek SoCs General-Purpose Timer handling.
|
||||
*
|
||||
* Copyright (C) 2014 Matthias Brugger
|
||||
*
|
||||
* Matthias Brugger <matthias.bgg@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define GPT_IRQ_EN_REG 0x00
|
||||
#define GPT_IRQ_ENABLE(val) BIT((val) - 1)
|
||||
#define GPT_IRQ_ACK_REG 0x08
|
||||
#define GPT_IRQ_ACK(val) BIT((val) - 1)
|
||||
|
||||
#define TIMER_CTRL_REG(val) (0x10 * (val))
|
||||
#define TIMER_CTRL_OP(val) (((val) & 0x3) << 4)
|
||||
#define TIMER_CTRL_OP_ONESHOT (0)
|
||||
#define TIMER_CTRL_OP_REPEAT (1)
|
||||
#define TIMER_CTRL_OP_FREERUN (3)
|
||||
#define TIMER_CTRL_CLEAR (2)
|
||||
#define TIMER_CTRL_ENABLE (1)
|
||||
#define TIMER_CTRL_DISABLE (0)
|
||||
|
||||
#define TIMER_CLK_REG(val) (0x04 + (0x10 * (val)))
|
||||
#define TIMER_CLK_SRC(val) (((val) & 0x1) << 4)
|
||||
#define TIMER_CLK_SRC_SYS13M (0)
|
||||
#define TIMER_CLK_SRC_RTC32K (1)
|
||||
#define TIMER_CLK_DIV1 (0x0)
|
||||
#define TIMER_CLK_DIV2 (0x1)
|
||||
|
||||
#define TIMER_CNT_REG(val) (0x08 + (0x10 * (val)))
|
||||
#define TIMER_CMP_REG(val) (0x0C + (0x10 * (val)))
|
||||
|
||||
#define GPT_CLK_EVT 1
|
||||
#define GPT_CLK_SRC 2
|
||||
|
||||
struct mtk_clock_event_device {
|
||||
void __iomem *gpt_base;
|
||||
u32 ticks_per_jiffy;
|
||||
struct clock_event_device dev;
|
||||
};
|
||||
|
||||
static inline struct mtk_clock_event_device *to_mtk_clk(
|
||||
struct clock_event_device *c)
|
||||
{
|
||||
return container_of(c, struct mtk_clock_event_device, dev);
|
||||
}
|
||||
|
||||
static void mtk_clkevt_time_stop(struct mtk_clock_event_device *evt, u8 timer)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(evt->gpt_base + TIMER_CTRL_REG(timer));
|
||||
writel(val & ~TIMER_CTRL_ENABLE, evt->gpt_base +
|
||||
TIMER_CTRL_REG(timer));
|
||||
}
|
||||
|
||||
static void mtk_clkevt_time_setup(struct mtk_clock_event_device *evt,
|
||||
unsigned long delay, u8 timer)
|
||||
{
|
||||
writel(delay, evt->gpt_base + TIMER_CMP_REG(timer));
|
||||
}
|
||||
|
||||
static void mtk_clkevt_time_start(struct mtk_clock_event_device *evt,
|
||||
bool periodic, u8 timer)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* Acknowledge interrupt */
|
||||
writel(GPT_IRQ_ACK(timer), evt->gpt_base + GPT_IRQ_ACK_REG);
|
||||
|
||||
val = readl(evt->gpt_base + TIMER_CTRL_REG(timer));
|
||||
|
||||
/* Clear 2 bit timer operation mode field */
|
||||
val &= ~TIMER_CTRL_OP(0x3);
|
||||
|
||||
if (periodic)
|
||||
val |= TIMER_CTRL_OP(TIMER_CTRL_OP_REPEAT);
|
||||
else
|
||||
val |= TIMER_CTRL_OP(TIMER_CTRL_OP_ONESHOT);
|
||||
|
||||
writel(val | TIMER_CTRL_ENABLE | TIMER_CTRL_CLEAR,
|
||||
evt->gpt_base + TIMER_CTRL_REG(timer));
|
||||
}
|
||||
|
||||
static void mtk_clkevt_mode(enum clock_event_mode mode,
|
||||
struct clock_event_device *clk)
|
||||
{
|
||||
struct mtk_clock_event_device *evt = to_mtk_clk(clk);
|
||||
|
||||
mtk_clkevt_time_stop(evt, GPT_CLK_EVT);
|
||||
|
||||
switch (mode) {
|
||||
case CLOCK_EVT_MODE_PERIODIC:
|
||||
mtk_clkevt_time_setup(evt, evt->ticks_per_jiffy, GPT_CLK_EVT);
|
||||
mtk_clkevt_time_start(evt, true, GPT_CLK_EVT);
|
||||
break;
|
||||
case CLOCK_EVT_MODE_ONESHOT:
|
||||
/* Timer is enabled in set_next_event */
|
||||
break;
|
||||
case CLOCK_EVT_MODE_UNUSED:
|
||||
case CLOCK_EVT_MODE_SHUTDOWN:
|
||||
default:
|
||||
/* No more interrupts will occur as source is disabled */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int mtk_clkevt_next_event(unsigned long event,
|
||||
struct clock_event_device *clk)
|
||||
{
|
||||
struct mtk_clock_event_device *evt = to_mtk_clk(clk);
|
||||
|
||||
mtk_clkevt_time_stop(evt, GPT_CLK_EVT);
|
||||
mtk_clkevt_time_setup(evt, event, GPT_CLK_EVT);
|
||||
mtk_clkevt_time_start(evt, false, GPT_CLK_EVT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t mtk_timer_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct mtk_clock_event_device *evt = dev_id;
|
||||
|
||||
/* Acknowledge timer0 irq */
|
||||
writel(GPT_IRQ_ACK(GPT_CLK_EVT), evt->gpt_base + GPT_IRQ_ACK_REG);
|
||||
evt->dev.event_handler(&evt->dev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void mtk_timer_global_reset(struct mtk_clock_event_device *evt)
|
||||
{
|
||||
/* Disable all interrupts */
|
||||
writel(0x0, evt->gpt_base + GPT_IRQ_EN_REG);
|
||||
/* Acknowledge all interrupts */
|
||||
writel(0x3f, evt->gpt_base + GPT_IRQ_ACK_REG);
|
||||
}
|
||||
|
||||
static void
|
||||
mtk_timer_setup(struct mtk_clock_event_device *evt, u8 timer, u8 option)
|
||||
{
|
||||
writel(TIMER_CTRL_CLEAR | TIMER_CTRL_DISABLE,
|
||||
evt->gpt_base + TIMER_CTRL_REG(timer));
|
||||
|
||||
writel(TIMER_CLK_SRC(TIMER_CLK_SRC_SYS13M) | TIMER_CLK_DIV1,
|
||||
evt->gpt_base + TIMER_CLK_REG(timer));
|
||||
|
||||
writel(0x0, evt->gpt_base + TIMER_CMP_REG(timer));
|
||||
|
||||
writel(TIMER_CTRL_OP(option) | TIMER_CTRL_ENABLE,
|
||||
evt->gpt_base + TIMER_CTRL_REG(timer));
|
||||
}
|
||||
|
||||
static void mtk_timer_enable_irq(struct mtk_clock_event_device *evt, u8 timer)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(evt->gpt_base + GPT_IRQ_EN_REG);
|
||||
writel(val | GPT_IRQ_ENABLE(timer),
|
||||
evt->gpt_base + GPT_IRQ_EN_REG);
|
||||
}
|
||||
|
||||
static void __init mtk_timer_init(struct device_node *node)
|
||||
{
|
||||
struct mtk_clock_event_device *evt;
|
||||
struct resource res;
|
||||
unsigned long rate = 0;
|
||||
struct clk *clk;
|
||||
|
||||
evt = kzalloc(sizeof(*evt), GFP_KERNEL);
|
||||
if (!evt) {
|
||||
pr_warn("Can't allocate mtk clock event driver struct");
|
||||
return;
|
||||
}
|
||||
|
||||
evt->dev.name = "mtk_tick";
|
||||
evt->dev.rating = 300;
|
||||
evt->dev.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
|
||||
evt->dev.set_mode = mtk_clkevt_mode;
|
||||
evt->dev.set_next_event = mtk_clkevt_next_event;
|
||||
evt->dev.cpumask = cpu_possible_mask;
|
||||
|
||||
evt->gpt_base = of_io_request_and_map(node, 0, "mtk-timer");
|
||||
if (IS_ERR(evt->gpt_base)) {
|
||||
pr_warn("Can't get resource\n");
|
||||
return;
|
||||
}
|
||||
|
||||
evt->dev.irq = irq_of_parse_and_map(node, 0);
|
||||
if (evt->dev.irq <= 0) {
|
||||
pr_warn("Can't parse IRQ");
|
||||
goto err_mem;
|
||||
}
|
||||
|
||||
clk = of_clk_get(node, 0);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_warn("Can't get timer clock");
|
||||
goto err_irq;
|
||||
}
|
||||
|
||||
if (clk_prepare_enable(clk)) {
|
||||
pr_warn("Can't prepare clock");
|
||||
goto err_clk_put;
|
||||
}
|
||||
rate = clk_get_rate(clk);
|
||||
|
||||
if (request_irq(evt->dev.irq, mtk_timer_interrupt,
|
||||
IRQF_TIMER | IRQF_IRQPOLL, "mtk_timer", evt)) {
|
||||
pr_warn("failed to setup irq %d\n", evt->dev.irq);
|
||||
goto err_clk_disable;
|
||||
}
|
||||
|
||||
evt->ticks_per_jiffy = DIV_ROUND_UP(rate, HZ);
|
||||
|
||||
mtk_timer_global_reset(evt);
|
||||
|
||||
/* Configure clock source */
|
||||
mtk_timer_setup(evt, GPT_CLK_SRC, TIMER_CTRL_OP_FREERUN);
|
||||
clocksource_mmio_init(evt->gpt_base + TIMER_CNT_REG(GPT_CLK_SRC),
|
||||
node->name, rate, 300, 32, clocksource_mmio_readl_up);
|
||||
|
||||
/* Configure clock event */
|
||||
mtk_timer_setup(evt, GPT_CLK_EVT, TIMER_CTRL_OP_REPEAT);
|
||||
mtk_timer_enable_irq(evt, GPT_CLK_EVT);
|
||||
|
||||
clockevents_config_and_register(&evt->dev, rate, 0x3,
|
||||
0xffffffff);
|
||||
return;
|
||||
|
||||
err_clk_disable:
|
||||
clk_disable_unprepare(clk);
|
||||
err_clk_put:
|
||||
clk_put(clk);
|
||||
err_irq:
|
||||
irq_dispose_mapping(evt->dev.irq);
|
||||
err_mem:
|
||||
iounmap(evt->gpt_base);
|
||||
of_address_to_resource(node, 0, &res);
|
||||
release_mem_region(res.start, resource_size(&res));
|
||||
}
|
||||
CLOCKSOURCE_OF_DECLARE(mtk_mt6577, "mediatek,mt6577-timer", mtk_timer_init);
|
227
drivers/clocksource/pxa_timer.c
Normal file
227
drivers/clocksource/pxa_timer.c
Normal file
@ -0,0 +1,227 @@
|
||||
/*
|
||||
* arch/arm/mach-pxa/time.c
|
||||
*
|
||||
* PXA clocksource, clockevents, and OST interrupt handlers.
|
||||
* Copyright (c) 2007 by Bill Gatliff <bgat@billgatliff.com>.
|
||||
*
|
||||
* Derived from Nicolas Pitre's PXA timer handler Copyright (c) 2001
|
||||
* by MontaVista Software, Inc. (Nico, your code rocks!)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/sched_clock.h>
|
||||
|
||||
#include <asm/div64.h>
|
||||
|
||||
#define OSMR0 0x00 /* OS Timer 0 Match Register */
|
||||
#define OSMR1 0x04 /* OS Timer 1 Match Register */
|
||||
#define OSMR2 0x08 /* OS Timer 2 Match Register */
|
||||
#define OSMR3 0x0C /* OS Timer 3 Match Register */
|
||||
|
||||
#define OSCR 0x10 /* OS Timer Counter Register */
|
||||
#define OSSR 0x14 /* OS Timer Status Register */
|
||||
#define OWER 0x18 /* OS Timer Watchdog Enable Register */
|
||||
#define OIER 0x1C /* OS Timer Interrupt Enable Register */
|
||||
|
||||
#define OSSR_M3 (1 << 3) /* Match status channel 3 */
|
||||
#define OSSR_M2 (1 << 2) /* Match status channel 2 */
|
||||
#define OSSR_M1 (1 << 1) /* Match status channel 1 */
|
||||
#define OSSR_M0 (1 << 0) /* Match status channel 0 */
|
||||
|
||||
#define OIER_E0 (1 << 0) /* Interrupt enable channel 0 */
|
||||
|
||||
/*
|
||||
* This is PXA's sched_clock implementation. This has a resolution
|
||||
* of at least 308 ns and a maximum value of 208 days.
|
||||
*
|
||||
* The return value is guaranteed to be monotonic in that range as
|
||||
* long as there is always less than 582 seconds between successive
|
||||
* calls to sched_clock() which should always be the case in practice.
|
||||
*/
|
||||
|
||||
#define timer_readl(reg) readl_relaxed(timer_base + (reg))
|
||||
#define timer_writel(val, reg) writel_relaxed((val), timer_base + (reg))
|
||||
|
||||
static void __iomem *timer_base;
|
||||
|
||||
static u64 notrace pxa_read_sched_clock(void)
|
||||
{
|
||||
return timer_readl(OSCR);
|
||||
}
|
||||
|
||||
|
||||
#define MIN_OSCR_DELTA 16
|
||||
|
||||
static irqreturn_t
|
||||
pxa_ost0_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct clock_event_device *c = dev_id;
|
||||
|
||||
/* Disarm the compare/match, signal the event. */
|
||||
timer_writel(timer_readl(OIER) & ~OIER_E0, OIER);
|
||||
timer_writel(OSSR_M0, OSSR);
|
||||
c->event_handler(c);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int
|
||||
pxa_osmr0_set_next_event(unsigned long delta, struct clock_event_device *dev)
|
||||
{
|
||||
unsigned long next, oscr;
|
||||
|
||||
timer_writel(timer_readl(OIER) | OIER_E0, OIER);
|
||||
next = timer_readl(OSCR) + delta;
|
||||
timer_writel(next, OSMR0);
|
||||
oscr = timer_readl(OSCR);
|
||||
|
||||
return (signed)(next - oscr) <= MIN_OSCR_DELTA ? -ETIME : 0;
|
||||
}
|
||||
|
||||
static void
|
||||
pxa_osmr0_set_mode(enum clock_event_mode mode, struct clock_event_device *dev)
|
||||
{
|
||||
switch (mode) {
|
||||
case CLOCK_EVT_MODE_ONESHOT:
|
||||
timer_writel(timer_readl(OIER) & ~OIER_E0, OIER);
|
||||
timer_writel(OSSR_M0, OSSR);
|
||||
break;
|
||||
|
||||
case CLOCK_EVT_MODE_UNUSED:
|
||||
case CLOCK_EVT_MODE_SHUTDOWN:
|
||||
/* initializing, released, or preparing for suspend */
|
||||
timer_writel(timer_readl(OIER) & ~OIER_E0, OIER);
|
||||
timer_writel(OSSR_M0, OSSR);
|
||||
break;
|
||||
|
||||
case CLOCK_EVT_MODE_RESUME:
|
||||
case CLOCK_EVT_MODE_PERIODIC:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static unsigned long osmr[4], oier, oscr;
|
||||
|
||||
static void pxa_timer_suspend(struct clock_event_device *cedev)
|
||||
{
|
||||
osmr[0] = timer_readl(OSMR0);
|
||||
osmr[1] = timer_readl(OSMR1);
|
||||
osmr[2] = timer_readl(OSMR2);
|
||||
osmr[3] = timer_readl(OSMR3);
|
||||
oier = timer_readl(OIER);
|
||||
oscr = timer_readl(OSCR);
|
||||
}
|
||||
|
||||
static void pxa_timer_resume(struct clock_event_device *cedev)
|
||||
{
|
||||
/*
|
||||
* Ensure that we have at least MIN_OSCR_DELTA between match
|
||||
* register 0 and the OSCR, to guarantee that we will receive
|
||||
* the one-shot timer interrupt. We adjust OSMR0 in preference
|
||||
* to OSCR to guarantee that OSCR is monotonically incrementing.
|
||||
*/
|
||||
if (osmr[0] - oscr < MIN_OSCR_DELTA)
|
||||
osmr[0] += MIN_OSCR_DELTA;
|
||||
|
||||
timer_writel(osmr[0], OSMR0);
|
||||
timer_writel(osmr[1], OSMR1);
|
||||
timer_writel(osmr[2], OSMR2);
|
||||
timer_writel(osmr[3], OSMR3);
|
||||
timer_writel(oier, OIER);
|
||||
timer_writel(oscr, OSCR);
|
||||
}
|
||||
#else
|
||||
#define pxa_timer_suspend NULL
|
||||
#define pxa_timer_resume NULL
|
||||
#endif
|
||||
|
||||
static struct clock_event_device ckevt_pxa_osmr0 = {
|
||||
.name = "osmr0",
|
||||
.features = CLOCK_EVT_FEAT_ONESHOT,
|
||||
.rating = 200,
|
||||
.set_next_event = pxa_osmr0_set_next_event,
|
||||
.set_mode = pxa_osmr0_set_mode,
|
||||
.suspend = pxa_timer_suspend,
|
||||
.resume = pxa_timer_resume,
|
||||
};
|
||||
|
||||
static struct irqaction pxa_ost0_irq = {
|
||||
.name = "ost0",
|
||||
.flags = IRQF_TIMER | IRQF_IRQPOLL,
|
||||
.handler = pxa_ost0_interrupt,
|
||||
.dev_id = &ckevt_pxa_osmr0,
|
||||
};
|
||||
|
||||
static void pxa_timer_common_init(int irq, unsigned long clock_tick_rate)
|
||||
{
|
||||
timer_writel(0, OIER);
|
||||
timer_writel(OSSR_M0 | OSSR_M1 | OSSR_M2 | OSSR_M3, OSSR);
|
||||
|
||||
sched_clock_register(pxa_read_sched_clock, 32, clock_tick_rate);
|
||||
|
||||
ckevt_pxa_osmr0.cpumask = cpumask_of(0);
|
||||
|
||||
setup_irq(irq, &pxa_ost0_irq);
|
||||
|
||||
clocksource_mmio_init(timer_base + OSCR, "oscr0", clock_tick_rate, 200,
|
||||
32, clocksource_mmio_readl_up);
|
||||
clockevents_config_and_register(&ckevt_pxa_osmr0, clock_tick_rate,
|
||||
MIN_OSCR_DELTA * 2, 0x7fffffff);
|
||||
}
|
||||
|
||||
static void __init pxa_timer_dt_init(struct device_node *np)
|
||||
{
|
||||
struct clk *clk;
|
||||
int irq;
|
||||
|
||||
/* timer registers are shared with watchdog timer */
|
||||
timer_base = of_iomap(np, 0);
|
||||
if (!timer_base)
|
||||
panic("%s: unable to map resource\n", np->name);
|
||||
|
||||
clk = of_clk_get(np, 0);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_crit("%s: unable to get clk\n", np->name);
|
||||
return;
|
||||
}
|
||||
clk_prepare_enable(clk);
|
||||
|
||||
/* we are only interested in OS-timer0 irq */
|
||||
irq = irq_of_parse_and_map(np, 0);
|
||||
if (irq <= 0) {
|
||||
pr_crit("%s: unable to parse OS-timer0 irq\n", np->name);
|
||||
return;
|
||||
}
|
||||
|
||||
pxa_timer_common_init(irq, clk_get_rate(clk));
|
||||
}
|
||||
CLOCKSOURCE_OF_DECLARE(pxa_timer, "marvell,pxa-timer", pxa_timer_dt_init);
|
||||
|
||||
/*
|
||||
* Legacy timer init for non device-tree boards.
|
||||
*/
|
||||
void __init pxa_timer_nodt_init(int irq, void __iomem *base,
|
||||
unsigned long clock_tick_rate)
|
||||
{
|
||||
struct clk *clk;
|
||||
|
||||
timer_base = base;
|
||||
clk = clk_get(NULL, "OSTIMER0");
|
||||
if (clk && !IS_ERR(clk))
|
||||
clk_prepare_enable(clk);
|
||||
else
|
||||
pr_crit("%s: unable to get clk\n", __func__);
|
||||
|
||||
pxa_timer_common_init(irq, clock_tick_rate);
|
||||
}
|
@ -24,6 +24,7 @@
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
@ -114,14 +115,15 @@ struct sh_cmt_device {
|
||||
struct platform_device *pdev;
|
||||
|
||||
const struct sh_cmt_info *info;
|
||||
bool legacy;
|
||||
|
||||
void __iomem *mapbase_ch;
|
||||
void __iomem *mapbase;
|
||||
struct clk *clk;
|
||||
|
||||
raw_spinlock_t lock; /* Protect the shared start/stop register */
|
||||
|
||||
struct sh_cmt_channel *channels;
|
||||
unsigned int num_channels;
|
||||
unsigned int hw_channels;
|
||||
|
||||
bool has_clockevent;
|
||||
bool has_clocksource;
|
||||
@ -301,14 +303,12 @@ static unsigned long sh_cmt_get_counter(struct sh_cmt_channel *ch,
|
||||
return v2;
|
||||
}
|
||||
|
||||
static DEFINE_RAW_SPINLOCK(sh_cmt_lock);
|
||||
|
||||
static void sh_cmt_start_stop_ch(struct sh_cmt_channel *ch, int start)
|
||||
{
|
||||
unsigned long flags, value;
|
||||
|
||||
/* start stop register shared by multiple timer channels */
|
||||
raw_spin_lock_irqsave(&sh_cmt_lock, flags);
|
||||
raw_spin_lock_irqsave(&ch->cmt->lock, flags);
|
||||
value = sh_cmt_read_cmstr(ch);
|
||||
|
||||
if (start)
|
||||
@ -317,7 +317,7 @@ static void sh_cmt_start_stop_ch(struct sh_cmt_channel *ch, int start)
|
||||
value &= ~(1 << ch->timer_bit);
|
||||
|
||||
sh_cmt_write_cmstr(ch, value);
|
||||
raw_spin_unlock_irqrestore(&sh_cmt_lock, flags);
|
||||
raw_spin_unlock_irqrestore(&ch->cmt->lock, flags);
|
||||
}
|
||||
|
||||
static int sh_cmt_enable(struct sh_cmt_channel *ch, unsigned long *rate)
|
||||
@ -792,7 +792,7 @@ static int sh_cmt_register_clockevent(struct sh_cmt_channel *ch,
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
irq = platform_get_irq(ch->cmt->pdev, ch->cmt->legacy ? 0 : ch->index);
|
||||
irq = platform_get_irq(ch->cmt->pdev, ch->index);
|
||||
if (irq < 0) {
|
||||
dev_err(&ch->cmt->pdev->dev, "ch%u: failed to get irq\n",
|
||||
ch->index);
|
||||
@ -863,33 +863,26 @@ static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, unsigned int index,
|
||||
* Compute the address of the channel control register block. For the
|
||||
* timers with a per-channel start/stop register, compute its address
|
||||
* as well.
|
||||
*
|
||||
* For legacy configuration the address has been mapped explicitly.
|
||||
*/
|
||||
if (cmt->legacy) {
|
||||
ch->ioctrl = cmt->mapbase_ch;
|
||||
} else {
|
||||
switch (cmt->info->model) {
|
||||
case SH_CMT_16BIT:
|
||||
ch->ioctrl = cmt->mapbase + 2 + ch->hwidx * 6;
|
||||
break;
|
||||
case SH_CMT_32BIT:
|
||||
case SH_CMT_48BIT:
|
||||
ch->ioctrl = cmt->mapbase + 0x10 + ch->hwidx * 0x10;
|
||||
break;
|
||||
case SH_CMT_32BIT_FAST:
|
||||
/*
|
||||
* The 32-bit "fast" timer has a single channel at hwidx
|
||||
* 5 but is located at offset 0x40 instead of 0x60 for
|
||||
* some reason.
|
||||
*/
|
||||
ch->ioctrl = cmt->mapbase + 0x40;
|
||||
break;
|
||||
case SH_CMT_48BIT_GEN2:
|
||||
ch->iostart = cmt->mapbase + ch->hwidx * 0x100;
|
||||
ch->ioctrl = ch->iostart + 0x10;
|
||||
break;
|
||||
}
|
||||
switch (cmt->info->model) {
|
||||
case SH_CMT_16BIT:
|
||||
ch->ioctrl = cmt->mapbase + 2 + ch->hwidx * 6;
|
||||
break;
|
||||
case SH_CMT_32BIT:
|
||||
case SH_CMT_48BIT:
|
||||
ch->ioctrl = cmt->mapbase + 0x10 + ch->hwidx * 0x10;
|
||||
break;
|
||||
case SH_CMT_32BIT_FAST:
|
||||
/*
|
||||
* The 32-bit "fast" timer has a single channel at hwidx 5 but
|
||||
* is located at offset 0x40 instead of 0x60 for some reason.
|
||||
*/
|
||||
ch->ioctrl = cmt->mapbase + 0x40;
|
||||
break;
|
||||
case SH_CMT_48BIT_GEN2:
|
||||
ch->iostart = cmt->mapbase + ch->hwidx * 0x100;
|
||||
ch->ioctrl = ch->iostart + 0x10;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cmt->info->width == (sizeof(ch->max_match_value) * 8))
|
||||
@ -900,12 +893,7 @@ static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, unsigned int index,
|
||||
ch->match_value = ch->max_match_value;
|
||||
raw_spin_lock_init(&ch->lock);
|
||||
|
||||
if (cmt->legacy) {
|
||||
ch->timer_bit = ch->hwidx;
|
||||
} else {
|
||||
ch->timer_bit = cmt->info->model == SH_CMT_48BIT_GEN2
|
||||
? 0 : ch->hwidx;
|
||||
}
|
||||
ch->timer_bit = cmt->info->model == SH_CMT_48BIT_GEN2 ? 0 : ch->hwidx;
|
||||
|
||||
ret = sh_cmt_register(ch, dev_name(&cmt->pdev->dev),
|
||||
clockevent, clocksource);
|
||||
@ -938,75 +926,65 @@ static int sh_cmt_map_memory(struct sh_cmt_device *cmt)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sh_cmt_map_memory_legacy(struct sh_cmt_device *cmt)
|
||||
static const struct platform_device_id sh_cmt_id_table[] = {
|
||||
{ "sh-cmt-16", (kernel_ulong_t)&sh_cmt_info[SH_CMT_16BIT] },
|
||||
{ "sh-cmt-32", (kernel_ulong_t)&sh_cmt_info[SH_CMT_32BIT] },
|
||||
{ "sh-cmt-32-fast", (kernel_ulong_t)&sh_cmt_info[SH_CMT_32BIT_FAST] },
|
||||
{ "sh-cmt-48", (kernel_ulong_t)&sh_cmt_info[SH_CMT_48BIT] },
|
||||
{ "sh-cmt-48-gen2", (kernel_ulong_t)&sh_cmt_info[SH_CMT_48BIT_GEN2] },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, sh_cmt_id_table);
|
||||
|
||||
static const struct of_device_id sh_cmt_of_table[] __maybe_unused = {
|
||||
{ .compatible = "renesas,cmt-32", .data = &sh_cmt_info[SH_CMT_32BIT] },
|
||||
{ .compatible = "renesas,cmt-32-fast", .data = &sh_cmt_info[SH_CMT_32BIT_FAST] },
|
||||
{ .compatible = "renesas,cmt-48", .data = &sh_cmt_info[SH_CMT_48BIT] },
|
||||
{ .compatible = "renesas,cmt-48-gen2", .data = &sh_cmt_info[SH_CMT_48BIT_GEN2] },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sh_cmt_of_table);
|
||||
|
||||
static int sh_cmt_parse_dt(struct sh_cmt_device *cmt)
|
||||
{
|
||||
struct sh_timer_config *cfg = cmt->pdev->dev.platform_data;
|
||||
struct resource *res, *res2;
|
||||
struct device_node *np = cmt->pdev->dev.of_node;
|
||||
|
||||
/* map memory, let mapbase_ch point to our channel */
|
||||
res = platform_get_resource(cmt->pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&cmt->pdev->dev, "failed to get I/O memory\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
cmt->mapbase_ch = ioremap_nocache(res->start, resource_size(res));
|
||||
if (cmt->mapbase_ch == NULL) {
|
||||
dev_err(&cmt->pdev->dev, "failed to remap I/O memory\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/* optional resource for the shared timer start/stop register */
|
||||
res2 = platform_get_resource(cmt->pdev, IORESOURCE_MEM, 1);
|
||||
|
||||
/* map second resource for CMSTR */
|
||||
cmt->mapbase = ioremap_nocache(res2 ? res2->start :
|
||||
res->start - cfg->channel_offset,
|
||||
res2 ? resource_size(res2) : 2);
|
||||
if (cmt->mapbase == NULL) {
|
||||
dev_err(&cmt->pdev->dev, "failed to remap I/O second memory\n");
|
||||
iounmap(cmt->mapbase_ch);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/* identify the model based on the resources */
|
||||
if (resource_size(res) == 6)
|
||||
cmt->info = &sh_cmt_info[SH_CMT_16BIT];
|
||||
else if (res2 && (resource_size(res2) == 4))
|
||||
cmt->info = &sh_cmt_info[SH_CMT_48BIT_GEN2];
|
||||
else
|
||||
cmt->info = &sh_cmt_info[SH_CMT_32BIT];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sh_cmt_unmap_memory(struct sh_cmt_device *cmt)
|
||||
{
|
||||
iounmap(cmt->mapbase);
|
||||
if (cmt->mapbase_ch)
|
||||
iounmap(cmt->mapbase_ch);
|
||||
return of_property_read_u32(np, "renesas,channels-mask",
|
||||
&cmt->hw_channels);
|
||||
}
|
||||
|
||||
static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
|
||||
{
|
||||
struct sh_timer_config *cfg = pdev->dev.platform_data;
|
||||
const struct platform_device_id *id = pdev->id_entry;
|
||||
unsigned int hw_channels;
|
||||
unsigned int mask;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
memset(cmt, 0, sizeof(*cmt));
|
||||
cmt->pdev = pdev;
|
||||
raw_spin_lock_init(&cmt->lock);
|
||||
|
||||
if (!cfg) {
|
||||
if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
|
||||
const struct of_device_id *id;
|
||||
|
||||
id = of_match_node(sh_cmt_of_table, pdev->dev.of_node);
|
||||
cmt->info = id->data;
|
||||
|
||||
ret = sh_cmt_parse_dt(cmt);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if (pdev->dev.platform_data) {
|
||||
struct sh_timer_config *cfg = pdev->dev.platform_data;
|
||||
const struct platform_device_id *id = pdev->id_entry;
|
||||
|
||||
cmt->info = (const struct sh_cmt_info *)id->driver_data;
|
||||
cmt->hw_channels = cfg->channels_mask;
|
||||
} else {
|
||||
dev_err(&cmt->pdev->dev, "missing platform data\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
cmt->info = (const struct sh_cmt_info *)id->driver_data;
|
||||
cmt->legacy = cmt->info ? false : true;
|
||||
|
||||
/* Get hold of clock. */
|
||||
cmt->clk = clk_get(&cmt->pdev->dev, cmt->legacy ? "cmt_fck" : "fck");
|
||||
cmt->clk = clk_get(&cmt->pdev->dev, "fck");
|
||||
if (IS_ERR(cmt->clk)) {
|
||||
dev_err(&cmt->pdev->dev, "cannot get clock\n");
|
||||
return PTR_ERR(cmt->clk);
|
||||
@ -1016,28 +994,13 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
|
||||
if (ret < 0)
|
||||
goto err_clk_put;
|
||||
|
||||
/*
|
||||
* Map the memory resource(s). We need to support both the legacy
|
||||
* platform device configuration (with one device per channel) and the
|
||||
* new version (with multiple channels per device).
|
||||
*/
|
||||
if (cmt->legacy)
|
||||
ret = sh_cmt_map_memory_legacy(cmt);
|
||||
else
|
||||
ret = sh_cmt_map_memory(cmt);
|
||||
|
||||
/* Map the memory resource(s). */
|
||||
ret = sh_cmt_map_memory(cmt);
|
||||
if (ret < 0)
|
||||
goto err_clk_unprepare;
|
||||
|
||||
/* Allocate and setup the channels. */
|
||||
if (cmt->legacy) {
|
||||
cmt->num_channels = 1;
|
||||
hw_channels = 0;
|
||||
} else {
|
||||
cmt->num_channels = hweight8(cfg->channels_mask);
|
||||
hw_channels = cfg->channels_mask;
|
||||
}
|
||||
|
||||
cmt->num_channels = hweight8(cmt->hw_channels);
|
||||
cmt->channels = kzalloc(cmt->num_channels * sizeof(*cmt->channels),
|
||||
GFP_KERNEL);
|
||||
if (cmt->channels == NULL) {
|
||||
@ -1045,35 +1008,21 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
if (cmt->legacy) {
|
||||
ret = sh_cmt_setup_channel(&cmt->channels[0],
|
||||
cfg->timer_bit, cfg->timer_bit,
|
||||
cfg->clockevent_rating != 0,
|
||||
cfg->clocksource_rating != 0, cmt);
|
||||
/*
|
||||
* Use the first channel as a clock event device and the second channel
|
||||
* as a clock source. If only one channel is available use it for both.
|
||||
*/
|
||||
for (i = 0, mask = cmt->hw_channels; i < cmt->num_channels; ++i) {
|
||||
unsigned int hwidx = ffs(mask) - 1;
|
||||
bool clocksource = i == 1 || cmt->num_channels == 1;
|
||||
bool clockevent = i == 0;
|
||||
|
||||
ret = sh_cmt_setup_channel(&cmt->channels[i], i, hwidx,
|
||||
clockevent, clocksource, cmt);
|
||||
if (ret < 0)
|
||||
goto err_unmap;
|
||||
} else {
|
||||
unsigned int mask = hw_channels;
|
||||
unsigned int i;
|
||||
|
||||
/*
|
||||
* Use the first channel as a clock event device and the second
|
||||
* channel as a clock source. If only one channel is available
|
||||
* use it for both.
|
||||
*/
|
||||
for (i = 0; i < cmt->num_channels; ++i) {
|
||||
unsigned int hwidx = ffs(mask) - 1;
|
||||
bool clocksource = i == 1 || cmt->num_channels == 1;
|
||||
bool clockevent = i == 0;
|
||||
|
||||
ret = sh_cmt_setup_channel(&cmt->channels[i], i, hwidx,
|
||||
clockevent, clocksource,
|
||||
cmt);
|
||||
if (ret < 0)
|
||||
goto err_unmap;
|
||||
|
||||
mask &= ~(1 << hwidx);
|
||||
}
|
||||
mask &= ~(1 << hwidx);
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, cmt);
|
||||
@ -1082,7 +1031,7 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
|
||||
|
||||
err_unmap:
|
||||
kfree(cmt->channels);
|
||||
sh_cmt_unmap_memory(cmt);
|
||||
iounmap(cmt->mapbase);
|
||||
err_clk_unprepare:
|
||||
clk_unprepare(cmt->clk);
|
||||
err_clk_put:
|
||||
@ -1132,22 +1081,12 @@ static int sh_cmt_remove(struct platform_device *pdev)
|
||||
return -EBUSY; /* cannot unregister clockevent and clocksource */
|
||||
}
|
||||
|
||||
static const struct platform_device_id sh_cmt_id_table[] = {
|
||||
{ "sh_cmt", 0 },
|
||||
{ "sh-cmt-16", (kernel_ulong_t)&sh_cmt_info[SH_CMT_16BIT] },
|
||||
{ "sh-cmt-32", (kernel_ulong_t)&sh_cmt_info[SH_CMT_32BIT] },
|
||||
{ "sh-cmt-32-fast", (kernel_ulong_t)&sh_cmt_info[SH_CMT_32BIT_FAST] },
|
||||
{ "sh-cmt-48", (kernel_ulong_t)&sh_cmt_info[SH_CMT_48BIT] },
|
||||
{ "sh-cmt-48-gen2", (kernel_ulong_t)&sh_cmt_info[SH_CMT_48BIT_GEN2] },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, sh_cmt_id_table);
|
||||
|
||||
static struct platform_driver sh_cmt_device_driver = {
|
||||
.probe = sh_cmt_probe,
|
||||
.remove = sh_cmt_remove,
|
||||
.driver = {
|
||||
.name = "sh_cmt",
|
||||
.of_match_table = of_match_ptr(sh_cmt_of_table),
|
||||
},
|
||||
.id_table = sh_cmt_id_table,
|
||||
};
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
@ -37,7 +38,6 @@ struct sh_mtu2_channel {
|
||||
unsigned int index;
|
||||
|
||||
void __iomem *base;
|
||||
int irq;
|
||||
|
||||
struct clock_event_device ced;
|
||||
};
|
||||
@ -48,15 +48,14 @@ struct sh_mtu2_device {
|
||||
void __iomem *mapbase;
|
||||
struct clk *clk;
|
||||
|
||||
raw_spinlock_t lock; /* Protect the shared registers */
|
||||
|
||||
struct sh_mtu2_channel *channels;
|
||||
unsigned int num_channels;
|
||||
|
||||
bool legacy;
|
||||
bool has_clockevent;
|
||||
};
|
||||
|
||||
static DEFINE_RAW_SPINLOCK(sh_mtu2_lock);
|
||||
|
||||
#define TSTR -1 /* shared register */
|
||||
#define TCR 0 /* channel register */
|
||||
#define TMDR 1 /* channel register */
|
||||
@ -162,12 +161,8 @@ static inline unsigned long sh_mtu2_read(struct sh_mtu2_channel *ch, int reg_nr)
|
||||
{
|
||||
unsigned long offs;
|
||||
|
||||
if (reg_nr == TSTR) {
|
||||
if (ch->mtu->legacy)
|
||||
return ioread8(ch->mtu->mapbase);
|
||||
else
|
||||
return ioread8(ch->mtu->mapbase + 0x280);
|
||||
}
|
||||
if (reg_nr == TSTR)
|
||||
return ioread8(ch->mtu->mapbase + 0x280);
|
||||
|
||||
offs = mtu2_reg_offs[reg_nr];
|
||||
|
||||
@ -182,12 +177,8 @@ static inline void sh_mtu2_write(struct sh_mtu2_channel *ch, int reg_nr,
|
||||
{
|
||||
unsigned long offs;
|
||||
|
||||
if (reg_nr == TSTR) {
|
||||
if (ch->mtu->legacy)
|
||||
return iowrite8(value, ch->mtu->mapbase);
|
||||
else
|
||||
return iowrite8(value, ch->mtu->mapbase + 0x280);
|
||||
}
|
||||
if (reg_nr == TSTR)
|
||||
return iowrite8(value, ch->mtu->mapbase + 0x280);
|
||||
|
||||
offs = mtu2_reg_offs[reg_nr];
|
||||
|
||||
@ -202,7 +193,7 @@ static void sh_mtu2_start_stop_ch(struct sh_mtu2_channel *ch, int start)
|
||||
unsigned long flags, value;
|
||||
|
||||
/* start stop register shared by multiple timer channels */
|
||||
raw_spin_lock_irqsave(&sh_mtu2_lock, flags);
|
||||
raw_spin_lock_irqsave(&ch->mtu->lock, flags);
|
||||
value = sh_mtu2_read(ch, TSTR);
|
||||
|
||||
if (start)
|
||||
@ -211,7 +202,7 @@ static void sh_mtu2_start_stop_ch(struct sh_mtu2_channel *ch, int start)
|
||||
value &= ~(1 << ch->index);
|
||||
|
||||
sh_mtu2_write(ch, TSTR, value);
|
||||
raw_spin_unlock_irqrestore(&sh_mtu2_lock, flags);
|
||||
raw_spin_unlock_irqrestore(&ch->mtu->lock, flags);
|
||||
}
|
||||
|
||||
static int sh_mtu2_enable(struct sh_mtu2_channel *ch)
|
||||
@ -331,7 +322,6 @@ static void sh_mtu2_register_clockevent(struct sh_mtu2_channel *ch,
|
||||
const char *name)
|
||||
{
|
||||
struct clock_event_device *ced = &ch->ced;
|
||||
int ret;
|
||||
|
||||
ced->name = name;
|
||||
ced->features = CLOCK_EVT_FEAT_PERIODIC;
|
||||
@ -344,24 +334,12 @@ static void sh_mtu2_register_clockevent(struct sh_mtu2_channel *ch,
|
||||
dev_info(&ch->mtu->pdev->dev, "ch%u: used for clock events\n",
|
||||
ch->index);
|
||||
clockevents_register_device(ced);
|
||||
|
||||
ret = request_irq(ch->irq, sh_mtu2_interrupt,
|
||||
IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING,
|
||||
dev_name(&ch->mtu->pdev->dev), ch);
|
||||
if (ret) {
|
||||
dev_err(&ch->mtu->pdev->dev, "ch%u: failed to request irq %d\n",
|
||||
ch->index, ch->irq);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int sh_mtu2_register(struct sh_mtu2_channel *ch, const char *name,
|
||||
bool clockevent)
|
||||
static int sh_mtu2_register(struct sh_mtu2_channel *ch, const char *name)
|
||||
{
|
||||
if (clockevent) {
|
||||
ch->mtu->has_clockevent = true;
|
||||
sh_mtu2_register_clockevent(ch, name);
|
||||
}
|
||||
ch->mtu->has_clockevent = true;
|
||||
sh_mtu2_register_clockevent(ch, name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -372,40 +350,32 @@ static int sh_mtu2_setup_channel(struct sh_mtu2_channel *ch, unsigned int index,
|
||||
static const unsigned int channel_offsets[] = {
|
||||
0x300, 0x380, 0x000,
|
||||
};
|
||||
bool clockevent;
|
||||
char name[6];
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
ch->mtu = mtu;
|
||||
|
||||
if (mtu->legacy) {
|
||||
struct sh_timer_config *cfg = mtu->pdev->dev.platform_data;
|
||||
|
||||
clockevent = cfg->clockevent_rating != 0;
|
||||
|
||||
ch->irq = platform_get_irq(mtu->pdev, 0);
|
||||
ch->base = mtu->mapbase - cfg->channel_offset;
|
||||
ch->index = cfg->timer_bit;
|
||||
} else {
|
||||
char name[6];
|
||||
|
||||
clockevent = true;
|
||||
|
||||
sprintf(name, "tgi%ua", index);
|
||||
ch->irq = platform_get_irq_byname(mtu->pdev, name);
|
||||
ch->base = mtu->mapbase + channel_offsets[index];
|
||||
ch->index = index;
|
||||
}
|
||||
|
||||
if (ch->irq < 0) {
|
||||
sprintf(name, "tgi%ua", index);
|
||||
irq = platform_get_irq_byname(mtu->pdev, name);
|
||||
if (irq < 0) {
|
||||
/* Skip channels with no declared interrupt. */
|
||||
if (!mtu->legacy)
|
||||
return 0;
|
||||
|
||||
dev_err(&mtu->pdev->dev, "ch%u: failed to get irq\n",
|
||||
ch->index);
|
||||
return ch->irq;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sh_mtu2_register(ch, dev_name(&mtu->pdev->dev), clockevent);
|
||||
ret = request_irq(irq, sh_mtu2_interrupt,
|
||||
IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING,
|
||||
dev_name(&ch->mtu->pdev->dev), ch);
|
||||
if (ret) {
|
||||
dev_err(&ch->mtu->pdev->dev, "ch%u: failed to request irq %d\n",
|
||||
index, irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ch->base = mtu->mapbase + channel_offsets[index];
|
||||
ch->index = index;
|
||||
|
||||
return sh_mtu2_register(ch, dev_name(&mtu->pdev->dev));
|
||||
}
|
||||
|
||||
static int sh_mtu2_map_memory(struct sh_mtu2_device *mtu)
|
||||
@ -422,46 +392,21 @@ static int sh_mtu2_map_memory(struct sh_mtu2_device *mtu)
|
||||
if (mtu->mapbase == NULL)
|
||||
return -ENXIO;
|
||||
|
||||
/*
|
||||
* In legacy platform device configuration (with one device per channel)
|
||||
* the resource points to the channel base address.
|
||||
*/
|
||||
if (mtu->legacy) {
|
||||
struct sh_timer_config *cfg = mtu->pdev->dev.platform_data;
|
||||
mtu->mapbase += cfg->channel_offset;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sh_mtu2_unmap_memory(struct sh_mtu2_device *mtu)
|
||||
{
|
||||
if (mtu->legacy) {
|
||||
struct sh_timer_config *cfg = mtu->pdev->dev.platform_data;
|
||||
mtu->mapbase -= cfg->channel_offset;
|
||||
}
|
||||
|
||||
iounmap(mtu->mapbase);
|
||||
}
|
||||
|
||||
static int sh_mtu2_setup(struct sh_mtu2_device *mtu,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct sh_timer_config *cfg = pdev->dev.platform_data;
|
||||
const struct platform_device_id *id = pdev->id_entry;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
mtu->pdev = pdev;
|
||||
mtu->legacy = id->driver_data;
|
||||
|
||||
if (mtu->legacy && !cfg) {
|
||||
dev_err(&mtu->pdev->dev, "missing platform data\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
raw_spin_lock_init(&mtu->lock);
|
||||
|
||||
/* Get hold of clock. */
|
||||
mtu->clk = clk_get(&mtu->pdev->dev, mtu->legacy ? "mtu2_fck" : "fck");
|
||||
mtu->clk = clk_get(&mtu->pdev->dev, "fck");
|
||||
if (IS_ERR(mtu->clk)) {
|
||||
dev_err(&mtu->pdev->dev, "cannot get clock\n");
|
||||
return PTR_ERR(mtu->clk);
|
||||
@ -479,10 +424,7 @@ static int sh_mtu2_setup(struct sh_mtu2_device *mtu,
|
||||
}
|
||||
|
||||
/* Allocate and setup the channels. */
|
||||
if (mtu->legacy)
|
||||
mtu->num_channels = 1;
|
||||
else
|
||||
mtu->num_channels = 3;
|
||||
mtu->num_channels = 3;
|
||||
|
||||
mtu->channels = kzalloc(sizeof(*mtu->channels) * mtu->num_channels,
|
||||
GFP_KERNEL);
|
||||
@ -491,16 +433,10 @@ static int sh_mtu2_setup(struct sh_mtu2_device *mtu,
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
if (mtu->legacy) {
|
||||
ret = sh_mtu2_setup_channel(&mtu->channels[0], 0, mtu);
|
||||
for (i = 0; i < mtu->num_channels; ++i) {
|
||||
ret = sh_mtu2_setup_channel(&mtu->channels[i], i, mtu);
|
||||
if (ret < 0)
|
||||
goto err_unmap;
|
||||
} else {
|
||||
for (i = 0; i < mtu->num_channels; ++i) {
|
||||
ret = sh_mtu2_setup_channel(&mtu->channels[i], i, mtu);
|
||||
if (ret < 0)
|
||||
goto err_unmap;
|
||||
}
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, mtu);
|
||||
@ -509,7 +445,7 @@ static int sh_mtu2_setup(struct sh_mtu2_device *mtu,
|
||||
|
||||
err_unmap:
|
||||
kfree(mtu->channels);
|
||||
sh_mtu2_unmap_memory(mtu);
|
||||
iounmap(mtu->mapbase);
|
||||
err_clk_unprepare:
|
||||
clk_unprepare(mtu->clk);
|
||||
err_clk_put:
|
||||
@ -560,17 +496,23 @@ static int sh_mtu2_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
static const struct platform_device_id sh_mtu2_id_table[] = {
|
||||
{ "sh_mtu2", 1 },
|
||||
{ "sh-mtu2", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, sh_mtu2_id_table);
|
||||
|
||||
static const struct of_device_id sh_mtu2_of_table[] __maybe_unused = {
|
||||
{ .compatible = "renesas,mtu2" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sh_mtu2_of_table);
|
||||
|
||||
static struct platform_driver sh_mtu2_device_driver = {
|
||||
.probe = sh_mtu2_probe,
|
||||
.remove = sh_mtu2_remove,
|
||||
.driver = {
|
||||
.name = "sh_mtu2",
|
||||
.of_match_table = of_match_ptr(sh_mtu2_of_table),
|
||||
},
|
||||
.id_table = sh_mtu2_id_table,
|
||||
};
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
@ -32,7 +33,6 @@
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
enum sh_tmu_model {
|
||||
SH_TMU_LEGACY,
|
||||
SH_TMU,
|
||||
SH_TMU_SH3,
|
||||
};
|
||||
@ -62,6 +62,8 @@ struct sh_tmu_device {
|
||||
|
||||
enum sh_tmu_model model;
|
||||
|
||||
raw_spinlock_t lock; /* Protect the shared start/stop register */
|
||||
|
||||
struct sh_tmu_channel *channels;
|
||||
unsigned int num_channels;
|
||||
|
||||
@ -69,8 +71,6 @@ struct sh_tmu_device {
|
||||
bool has_clocksource;
|
||||
};
|
||||
|
||||
static DEFINE_RAW_SPINLOCK(sh_tmu_lock);
|
||||
|
||||
#define TSTR -1 /* shared register */
|
||||
#define TCOR 0 /* channel register */
|
||||
#define TCNT 1 /* channel register */
|
||||
@ -91,8 +91,6 @@ static inline unsigned long sh_tmu_read(struct sh_tmu_channel *ch, int reg_nr)
|
||||
|
||||
if (reg_nr == TSTR) {
|
||||
switch (ch->tmu->model) {
|
||||
case SH_TMU_LEGACY:
|
||||
return ioread8(ch->tmu->mapbase);
|
||||
case SH_TMU_SH3:
|
||||
return ioread8(ch->tmu->mapbase + 2);
|
||||
case SH_TMU:
|
||||
@ -115,8 +113,6 @@ static inline void sh_tmu_write(struct sh_tmu_channel *ch, int reg_nr,
|
||||
|
||||
if (reg_nr == TSTR) {
|
||||
switch (ch->tmu->model) {
|
||||
case SH_TMU_LEGACY:
|
||||
return iowrite8(value, ch->tmu->mapbase);
|
||||
case SH_TMU_SH3:
|
||||
return iowrite8(value, ch->tmu->mapbase + 2);
|
||||
case SH_TMU:
|
||||
@ -137,7 +133,7 @@ static void sh_tmu_start_stop_ch(struct sh_tmu_channel *ch, int start)
|
||||
unsigned long flags, value;
|
||||
|
||||
/* start stop register shared by multiple timer channels */
|
||||
raw_spin_lock_irqsave(&sh_tmu_lock, flags);
|
||||
raw_spin_lock_irqsave(&ch->tmu->lock, flags);
|
||||
value = sh_tmu_read(ch, TSTR);
|
||||
|
||||
if (start)
|
||||
@ -146,7 +142,7 @@ static void sh_tmu_start_stop_ch(struct sh_tmu_channel *ch, int start)
|
||||
value &= ~(1 << ch->index);
|
||||
|
||||
sh_tmu_write(ch, TSTR, value);
|
||||
raw_spin_unlock_irqrestore(&sh_tmu_lock, flags);
|
||||
raw_spin_unlock_irqrestore(&ch->tmu->lock, flags);
|
||||
}
|
||||
|
||||
static int __sh_tmu_enable(struct sh_tmu_channel *ch)
|
||||
@ -476,27 +472,12 @@ static int sh_tmu_channel_setup(struct sh_tmu_channel *ch, unsigned int index,
|
||||
return 0;
|
||||
|
||||
ch->tmu = tmu;
|
||||
ch->index = index;
|
||||
|
||||
if (tmu->model == SH_TMU_LEGACY) {
|
||||
struct sh_timer_config *cfg = tmu->pdev->dev.platform_data;
|
||||
|
||||
/*
|
||||
* The SH3 variant (SH770x, SH7705, SH7710 and SH7720) maps
|
||||
* channel registers blocks at base + 2 + 12 * index, while all
|
||||
* other variants map them at base + 4 + 12 * index. We can
|
||||
* compute the index by just dividing by 12, the 2 bytes or 4
|
||||
* bytes offset being hidden by the integer division.
|
||||
*/
|
||||
ch->index = cfg->channel_offset / 12;
|
||||
ch->base = tmu->mapbase + cfg->channel_offset;
|
||||
} else {
|
||||
ch->index = index;
|
||||
|
||||
if (tmu->model == SH_TMU_SH3)
|
||||
ch->base = tmu->mapbase + 4 + ch->index * 12;
|
||||
else
|
||||
ch->base = tmu->mapbase + 8 + ch->index * 12;
|
||||
}
|
||||
if (tmu->model == SH_TMU_SH3)
|
||||
ch->base = tmu->mapbase + 4 + ch->index * 12;
|
||||
else
|
||||
ch->base = tmu->mapbase + 8 + ch->index * 12;
|
||||
|
||||
ch->irq = platform_get_irq(tmu->pdev, index);
|
||||
if (ch->irq < 0) {
|
||||
@ -526,46 +507,53 @@ static int sh_tmu_map_memory(struct sh_tmu_device *tmu)
|
||||
if (tmu->mapbase == NULL)
|
||||
return -ENXIO;
|
||||
|
||||
/*
|
||||
* In legacy platform device configuration (with one device per channel)
|
||||
* the resource points to the channel base address.
|
||||
*/
|
||||
if (tmu->model == SH_TMU_LEGACY) {
|
||||
struct sh_timer_config *cfg = tmu->pdev->dev.platform_data;
|
||||
tmu->mapbase -= cfg->channel_offset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sh_tmu_parse_dt(struct sh_tmu_device *tmu)
|
||||
{
|
||||
struct device_node *np = tmu->pdev->dev.of_node;
|
||||
|
||||
tmu->model = SH_TMU;
|
||||
tmu->num_channels = 3;
|
||||
|
||||
of_property_read_u32(np, "#renesas,channels", &tmu->num_channels);
|
||||
|
||||
if (tmu->num_channels != 2 && tmu->num_channels != 3) {
|
||||
dev_err(&tmu->pdev->dev, "invalid number of channels %u\n",
|
||||
tmu->num_channels);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sh_tmu_unmap_memory(struct sh_tmu_device *tmu)
|
||||
{
|
||||
if (tmu->model == SH_TMU_LEGACY) {
|
||||
struct sh_timer_config *cfg = tmu->pdev->dev.platform_data;
|
||||
tmu->mapbase += cfg->channel_offset;
|
||||
}
|
||||
|
||||
iounmap(tmu->mapbase);
|
||||
}
|
||||
|
||||
static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev)
|
||||
{
|
||||
struct sh_timer_config *cfg = pdev->dev.platform_data;
|
||||
const struct platform_device_id *id = pdev->id_entry;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
if (!cfg) {
|
||||
tmu->pdev = pdev;
|
||||
|
||||
raw_spin_lock_init(&tmu->lock);
|
||||
|
||||
if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
|
||||
ret = sh_tmu_parse_dt(tmu);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if (pdev->dev.platform_data) {
|
||||
const struct platform_device_id *id = pdev->id_entry;
|
||||
struct sh_timer_config *cfg = pdev->dev.platform_data;
|
||||
|
||||
tmu->model = id->driver_data;
|
||||
tmu->num_channels = hweight8(cfg->channels_mask);
|
||||
} else {
|
||||
dev_err(&tmu->pdev->dev, "missing platform data\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
tmu->pdev = pdev;
|
||||
tmu->model = id->driver_data;
|
||||
|
||||
/* Get hold of clock. */
|
||||
tmu->clk = clk_get(&tmu->pdev->dev,
|
||||
tmu->model == SH_TMU_LEGACY ? "tmu_fck" : "fck");
|
||||
tmu->clk = clk_get(&tmu->pdev->dev, "fck");
|
||||
if (IS_ERR(tmu->clk)) {
|
||||
dev_err(&tmu->pdev->dev, "cannot get clock\n");
|
||||
return PTR_ERR(tmu->clk);
|
||||
@ -583,11 +571,6 @@ static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev)
|
||||
}
|
||||
|
||||
/* Allocate and setup the channels. */
|
||||
if (tmu->model == SH_TMU_LEGACY)
|
||||
tmu->num_channels = 1;
|
||||
else
|
||||
tmu->num_channels = hweight8(cfg->channels_mask);
|
||||
|
||||
tmu->channels = kzalloc(sizeof(*tmu->channels) * tmu->num_channels,
|
||||
GFP_KERNEL);
|
||||
if (tmu->channels == NULL) {
|
||||
@ -595,23 +578,15 @@ static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev)
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
if (tmu->model == SH_TMU_LEGACY) {
|
||||
ret = sh_tmu_channel_setup(&tmu->channels[0], 0,
|
||||
cfg->clockevent_rating != 0,
|
||||
cfg->clocksource_rating != 0, tmu);
|
||||
/*
|
||||
* Use the first channel as a clock event device and the second channel
|
||||
* as a clock source.
|
||||
*/
|
||||
for (i = 0; i < tmu->num_channels; ++i) {
|
||||
ret = sh_tmu_channel_setup(&tmu->channels[i], i,
|
||||
i == 0, i == 1, tmu);
|
||||
if (ret < 0)
|
||||
goto err_unmap;
|
||||
} else {
|
||||
/*
|
||||
* Use the first channel as a clock event device and the second
|
||||
* channel as a clock source.
|
||||
*/
|
||||
for (i = 0; i < tmu->num_channels; ++i) {
|
||||
ret = sh_tmu_channel_setup(&tmu->channels[i], i,
|
||||
i == 0, i == 1, tmu);
|
||||
if (ret < 0)
|
||||
goto err_unmap;
|
||||
}
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, tmu);
|
||||
@ -620,7 +595,7 @@ static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev)
|
||||
|
||||
err_unmap:
|
||||
kfree(tmu->channels);
|
||||
sh_tmu_unmap_memory(tmu);
|
||||
iounmap(tmu->mapbase);
|
||||
err_clk_unprepare:
|
||||
clk_unprepare(tmu->clk);
|
||||
err_clk_put:
|
||||
@ -671,18 +646,24 @@ static int sh_tmu_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
static const struct platform_device_id sh_tmu_id_table[] = {
|
||||
{ "sh_tmu", SH_TMU_LEGACY },
|
||||
{ "sh-tmu", SH_TMU },
|
||||
{ "sh-tmu-sh3", SH_TMU_SH3 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, sh_tmu_id_table);
|
||||
|
||||
static const struct of_device_id sh_tmu_of_table[] __maybe_unused = {
|
||||
{ .compatible = "renesas,tmu" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sh_tmu_of_table);
|
||||
|
||||
static struct platform_driver sh_tmu_device_driver = {
|
||||
.probe = sh_tmu_probe,
|
||||
.remove = sh_tmu_remove,
|
||||
.driver = {
|
||||
.name = "sh_tmu",
|
||||
.of_match_table = of_match_ptr(sh_tmu_of_table),
|
||||
},
|
||||
.id_table = sh_tmu_id_table,
|
||||
};
|
||||
|
@ -260,6 +260,9 @@ static void __init sirfsoc_marco_timer_init(struct device_node *np)
|
||||
|
||||
clk = of_clk_get(np, 0);
|
||||
BUG_ON(IS_ERR(clk));
|
||||
|
||||
BUG_ON(clk_prepare_enable(clk));
|
||||
|
||||
rate = clk_get_rate(clk);
|
||||
|
||||
BUG_ON(rate < MARCO_CLOCK_FREQ);
|
||||
|
@ -200,6 +200,9 @@ static void __init sirfsoc_prima2_timer_init(struct device_node *np)
|
||||
|
||||
clk = of_clk_get(np, 0);
|
||||
BUG_ON(IS_ERR(clk));
|
||||
|
||||
BUG_ON(clk_prepare_enable(clk));
|
||||
|
||||
rate = clk_get_rate(clk);
|
||||
|
||||
BUG_ON(rate < PRIMA2_CLOCK_FREQ);
|
||||
|
@ -1214,9 +1214,9 @@ static int ioctl_get_cycle_timer2(struct client *client, union ioctl_arg *arg)
|
||||
cycle_time = card->driver->read_csr(card, CSR_CYCLE_TIME);
|
||||
|
||||
switch (a->clk_id) {
|
||||
case CLOCK_REALTIME: getnstimeofday(&ts); break;
|
||||
case CLOCK_MONOTONIC: do_posix_clock_monotonic_gettime(&ts); break;
|
||||
case CLOCK_MONOTONIC_RAW: getrawmonotonic(&ts); break;
|
||||
case CLOCK_REALTIME: getnstimeofday(&ts); break;
|
||||
case CLOCK_MONOTONIC: ktime_get_ts(&ts); break;
|
||||
case CLOCK_MONOTONIC_RAW: getrawmonotonic(&ts); break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
@ -542,8 +542,8 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
|
||||
const struct drm_crtc *refcrtc,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
ktime_t stime, etime, mono_time_offset;
|
||||
struct timeval tv_etime;
|
||||
ktime_t stime, etime;
|
||||
int vbl_status;
|
||||
int vpos, hpos, i;
|
||||
int framedur_ns, linedur_ns, pixeldur_ns, delta_ns, duration_ns;
|
||||
@ -588,13 +588,6 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
|
||||
vbl_status = dev->driver->get_scanout_position(dev, crtc, flags, &vpos,
|
||||
&hpos, &stime, &etime);
|
||||
|
||||
/*
|
||||
* Get correction for CLOCK_MONOTONIC -> CLOCK_REALTIME if
|
||||
* CLOCK_REALTIME is requested.
|
||||
*/
|
||||
if (!drm_timestamp_monotonic)
|
||||
mono_time_offset = ktime_get_monotonic_offset();
|
||||
|
||||
/* Return as no-op if scanout query unsupported or failed. */
|
||||
if (!(vbl_status & DRM_SCANOUTPOS_VALID)) {
|
||||
DRM_DEBUG("crtc %d : scanoutpos query failed [%d].\n",
|
||||
@ -633,7 +626,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
|
||||
delta_ns = vpos * linedur_ns + hpos * pixeldur_ns;
|
||||
|
||||
if (!drm_timestamp_monotonic)
|
||||
etime = ktime_sub(etime, mono_time_offset);
|
||||
etime = ktime_mono_to_real(etime);
|
||||
|
||||
/* save this only for debugging purposes */
|
||||
tv_etime = ktime_to_timeval(etime);
|
||||
@ -664,10 +657,7 @@ static struct timeval get_drm_timestamp(void)
|
||||
{
|
||||
ktime_t now;
|
||||
|
||||
now = ktime_get();
|
||||
if (!drm_timestamp_monotonic)
|
||||
now = ktime_sub(now, ktime_get_monotonic_offset());
|
||||
|
||||
now = drm_timestamp_monotonic ? ktime_get() : ktime_get_real();
|
||||
return ktime_to_timeval(now);
|
||||
}
|
||||
|
||||
|
@ -108,9 +108,8 @@ static void evdev_queue_syn_dropped(struct evdev_client *client)
|
||||
struct input_event ev;
|
||||
ktime_t time;
|
||||
|
||||
time = ktime_get();
|
||||
if (client->clkid != CLOCK_MONOTONIC)
|
||||
time = ktime_sub(time, ktime_get_monotonic_offset());
|
||||
time = (client->clkid == CLOCK_MONOTONIC) ?
|
||||
ktime_get() : ktime_get_real();
|
||||
|
||||
ev.time = ktime_to_timeval(time);
|
||||
ev.type = EV_SYN;
|
||||
@ -202,7 +201,7 @@ static void evdev_events(struct input_handle *handle,
|
||||
ktime_t time_mono, time_real;
|
||||
|
||||
time_mono = ktime_get();
|
||||
time_real = ktime_sub(time_mono, ktime_get_monotonic_offset());
|
||||
time_real = ktime_mono_to_real(time_mono);
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
|
@ -702,6 +702,42 @@ void __iomem *of_iomap(struct device_node *np, int index)
|
||||
}
|
||||
EXPORT_SYMBOL(of_iomap);
|
||||
|
||||
/*
|
||||
* of_io_request_and_map - Requests a resource and maps the memory mapped IO
|
||||
* for a given device_node
|
||||
* @device: the device whose io range will be mapped
|
||||
* @index: index of the io range
|
||||
* @name: name of the resource
|
||||
*
|
||||
* Returns a pointer to the requested and mapped memory or an ERR_PTR() encoded
|
||||
* error code on failure. Usage example:
|
||||
*
|
||||
* base = of_io_request_and_map(node, 0, "foo");
|
||||
* if (IS_ERR(base))
|
||||
* return PTR_ERR(base);
|
||||
*/
|
||||
void __iomem *of_io_request_and_map(struct device_node *np, int index,
|
||||
char *name)
|
||||
{
|
||||
struct resource res;
|
||||
void __iomem *mem;
|
||||
|
||||
if (of_address_to_resource(np, index, &res))
|
||||
return IOMEM_ERR_PTR(-EINVAL);
|
||||
|
||||
if (!request_mem_region(res.start, resource_size(&res), name))
|
||||
return IOMEM_ERR_PTR(-EBUSY);
|
||||
|
||||
mem = ioremap(res.start, resource_size(&res));
|
||||
if (!mem) {
|
||||
release_mem_region(res.start, resource_size(&res));
|
||||
return IOMEM_ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
return mem;
|
||||
}
|
||||
EXPORT_SYMBOL(of_io_request_and_map);
|
||||
|
||||
/**
|
||||
* of_dma_get_range - Get DMA range info
|
||||
* @np: device node to get DMA range info
|
||||
|
@ -473,13 +473,8 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
|
||||
priority = task_prio(task);
|
||||
nice = task_nice(task);
|
||||
|
||||
/* Temporary variable needed for gcc-2.96 */
|
||||
/* convert timespec -> nsec*/
|
||||
start_time =
|
||||
(unsigned long long)task->real_start_time.tv_sec * NSEC_PER_SEC
|
||||
+ task->real_start_time.tv_nsec;
|
||||
/* convert nsec -> ticks */
|
||||
start_time = nsec_to_clock_t(start_time);
|
||||
start_time = nsec_to_clock_t(task->real_start_time);
|
||||
|
||||
seq_printf(m, "%d (%s) %c", pid_nr_ns(pid, ns), tcomm, state);
|
||||
seq_put_decimal_ll(m, ' ', ppid);
|
||||
|
77
fs/timerfd.c
77
fs/timerfd.c
@ -35,8 +35,9 @@ struct timerfd_ctx {
|
||||
ktime_t moffs;
|
||||
wait_queue_head_t wqh;
|
||||
u64 ticks;
|
||||
int expired;
|
||||
int clockid;
|
||||
short unsigned expired;
|
||||
short unsigned settime_flags; /* to show in fdinfo */
|
||||
struct rcu_head rcu;
|
||||
struct list_head clist;
|
||||
bool might_cancel;
|
||||
@ -92,7 +93,7 @@ static enum alarmtimer_restart timerfd_alarmproc(struct alarm *alarm,
|
||||
*/
|
||||
void timerfd_clock_was_set(void)
|
||||
{
|
||||
ktime_t moffs = ktime_get_monotonic_offset();
|
||||
ktime_t moffs = ktime_mono_to_real((ktime_t){ .tv64 = 0 });
|
||||
struct timerfd_ctx *ctx;
|
||||
unsigned long flags;
|
||||
|
||||
@ -125,7 +126,7 @@ static bool timerfd_canceled(struct timerfd_ctx *ctx)
|
||||
{
|
||||
if (!ctx->might_cancel || ctx->moffs.tv64 != KTIME_MAX)
|
||||
return false;
|
||||
ctx->moffs = ktime_get_monotonic_offset();
|
||||
ctx->moffs = ktime_mono_to_real((ktime_t){ .tv64 = 0 });
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -196,6 +197,8 @@ static int timerfd_setup(struct timerfd_ctx *ctx, int flags,
|
||||
if (timerfd_canceled(ctx))
|
||||
return -ECANCELED;
|
||||
}
|
||||
|
||||
ctx->settime_flags = flags & TFD_SETTIME_FLAGS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -284,11 +287,77 @@ static ssize_t timerfd_read(struct file *file, char __user *buf, size_t count,
|
||||
return res;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static int timerfd_show(struct seq_file *m, struct file *file)
|
||||
{
|
||||
struct timerfd_ctx *ctx = file->private_data;
|
||||
struct itimerspec t;
|
||||
|
||||
spin_lock_irq(&ctx->wqh.lock);
|
||||
t.it_value = ktime_to_timespec(timerfd_get_remaining(ctx));
|
||||
t.it_interval = ktime_to_timespec(ctx->tintv);
|
||||
spin_unlock_irq(&ctx->wqh.lock);
|
||||
|
||||
return seq_printf(m,
|
||||
"clockid: %d\n"
|
||||
"ticks: %llu\n"
|
||||
"settime flags: 0%o\n"
|
||||
"it_value: (%llu, %llu)\n"
|
||||
"it_interval: (%llu, %llu)\n",
|
||||
ctx->clockid, (unsigned long long)ctx->ticks,
|
||||
ctx->settime_flags,
|
||||
(unsigned long long)t.it_value.tv_sec,
|
||||
(unsigned long long)t.it_value.tv_nsec,
|
||||
(unsigned long long)t.it_interval.tv_sec,
|
||||
(unsigned long long)t.it_interval.tv_nsec);
|
||||
}
|
||||
#else
|
||||
#define timerfd_show NULL
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CHECKPOINT_RESTORE
|
||||
static long timerfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct timerfd_ctx *ctx = file->private_data;
|
||||
int ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case TFD_IOC_SET_TICKS: {
|
||||
u64 ticks;
|
||||
|
||||
if (copy_from_user(&ticks, (u64 __user *)arg, sizeof(ticks)))
|
||||
return -EFAULT;
|
||||
if (!ticks)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irq(&ctx->wqh.lock);
|
||||
if (!timerfd_canceled(ctx)) {
|
||||
ctx->ticks = ticks;
|
||||
if (ticks)
|
||||
wake_up_locked(&ctx->wqh);
|
||||
} else
|
||||
ret = -ECANCELED;
|
||||
spin_unlock_irq(&ctx->wqh.lock);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ret = -ENOTTY;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
#define timerfd_ioctl NULL
|
||||
#endif
|
||||
|
||||
static const struct file_operations timerfd_fops = {
|
||||
.release = timerfd_release,
|
||||
.poll = timerfd_poll,
|
||||
.read = timerfd_read,
|
||||
.llseek = noop_llseek,
|
||||
.show_fdinfo = timerfd_show,
|
||||
.unlocked_ioctl = timerfd_ioctl,
|
||||
};
|
||||
|
||||
static int timerfd_fget(int fd, struct fd *p)
|
||||
@ -336,7 +405,7 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags)
|
||||
else
|
||||
hrtimer_init(&ctx->t.tmr, clockid, HRTIMER_MODE_ABS);
|
||||
|
||||
ctx->moffs = ktime_get_monotonic_offset();
|
||||
ctx->moffs = ktime_mono_to_real((ktime_t){ .tv64 = 0 });
|
||||
|
||||
ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx,
|
||||
O_RDWR | (flags & TFD_SHARED_FCNTL_FLAGS));
|
||||
|
18
include/clocksource/pxa.h
Normal file
18
include/clocksource/pxa.h
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* PXA clocksource, clockevents, and OST interrupt handlers.
|
||||
*
|
||||
* Copyright (C) 2014 Robert Jarzmik
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _CLOCKSOURCE_PXA_H
|
||||
#define _CLOCKSOURCE_PXA_H
|
||||
|
||||
extern void pxa_timer_nodt_init(int irq, void __iomem *base,
|
||||
unsigned long clock_tick_rate);
|
||||
|
||||
#endif
|
@ -165,6 +165,7 @@ enum hrtimer_base_type {
|
||||
* struct hrtimer_cpu_base - the per cpu clock bases
|
||||
* @lock: lock protecting the base and associated clock bases
|
||||
* and timers
|
||||
* @cpu: cpu number
|
||||
* @active_bases: Bitfield to mark bases with active timers
|
||||
* @clock_was_set: Indicates that clock was set from irq context.
|
||||
* @expires_next: absolute time of the next event which was scheduled
|
||||
@ -179,6 +180,7 @@ enum hrtimer_base_type {
|
||||
*/
|
||||
struct hrtimer_cpu_base {
|
||||
raw_spinlock_t lock;
|
||||
unsigned int cpu;
|
||||
unsigned int active_bases;
|
||||
unsigned int clock_was_set;
|
||||
#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
@ -324,14 +326,6 @@ static inline void timerfd_clock_was_set(void) { }
|
||||
#endif
|
||||
extern void hrtimers_resume(void);
|
||||
|
||||
extern ktime_t ktime_get(void);
|
||||
extern ktime_t ktime_get_real(void);
|
||||
extern ktime_t ktime_get_boottime(void);
|
||||
extern ktime_t ktime_get_monotonic_offset(void);
|
||||
extern ktime_t ktime_get_clocktai(void);
|
||||
extern ktime_t ktime_get_update_offsets(ktime_t *offs_real, ktime_t *offs_boot,
|
||||
ktime_t *offs_tai);
|
||||
|
||||
DECLARE_PER_CPU(struct tick_device, tick_cpu_device);
|
||||
|
||||
|
||||
@ -452,12 +446,6 @@ extern void hrtimer_run_pending(void);
|
||||
/* Bootup initialization: */
|
||||
extern void __init hrtimers_init(void);
|
||||
|
||||
#if BITS_PER_LONG < 64
|
||||
extern u64 ktime_divns(const ktime_t kt, s64 div);
|
||||
#else /* BITS_PER_LONG < 64 */
|
||||
# define ktime_divns(kt, div) (u64)((kt).tv64 / (div))
|
||||
#endif
|
||||
|
||||
/* Show pending timers: */
|
||||
extern void sysrq_timer_list_show(void);
|
||||
|
||||
|
@ -58,6 +58,8 @@ static inline void devm_ioport_unmap(struct device *dev, void __iomem *addr)
|
||||
}
|
||||
#endif
|
||||
|
||||
#define IOMEM_ERR_PTR(err) (__force void __iomem *)ERR_PTR(err)
|
||||
|
||||
void __iomem *devm_ioremap(struct device *dev, resource_size_t offset,
|
||||
unsigned long size);
|
||||
void __iomem *devm_ioremap_nocache(struct device *dev, resource_size_t offset,
|
||||
|
@ -27,43 +27,19 @@
|
||||
/*
|
||||
* ktime_t:
|
||||
*
|
||||
* On 64-bit CPUs a single 64-bit variable is used to store the hrtimers
|
||||
* A single 64-bit variable is used to store the hrtimers
|
||||
* internal representation of time values in scalar nanoseconds. The
|
||||
* design plays out best on 64-bit CPUs, where most conversions are
|
||||
* NOPs and most arithmetic ktime_t operations are plain arithmetic
|
||||
* operations.
|
||||
*
|
||||
* On 32-bit CPUs an optimized representation of the timespec structure
|
||||
* is used to avoid expensive conversions from and to timespecs. The
|
||||
* endian-aware order of the tv struct members is chosen to allow
|
||||
* mathematical operations on the tv64 member of the union too, which
|
||||
* for certain operations produces better code.
|
||||
*
|
||||
* For architectures with efficient support for 64/32-bit conversions the
|
||||
* plain scalar nanosecond based representation can be selected by the
|
||||
* config switch CONFIG_KTIME_SCALAR.
|
||||
*/
|
||||
union ktime {
|
||||
s64 tv64;
|
||||
#if BITS_PER_LONG != 64 && !defined(CONFIG_KTIME_SCALAR)
|
||||
struct {
|
||||
# ifdef __BIG_ENDIAN
|
||||
s32 sec, nsec;
|
||||
# else
|
||||
s32 nsec, sec;
|
||||
# endif
|
||||
} tv;
|
||||
#endif
|
||||
};
|
||||
|
||||
typedef union ktime ktime_t; /* Kill this */
|
||||
|
||||
/*
|
||||
* ktime_t definitions when using the 64-bit scalar representation:
|
||||
*/
|
||||
|
||||
#if (BITS_PER_LONG == 64) || defined(CONFIG_KTIME_SCALAR)
|
||||
|
||||
/**
|
||||
* ktime_set - Set a ktime_t variable from a seconds/nanoseconds value
|
||||
* @secs: seconds to set
|
||||
@ -71,13 +47,12 @@ typedef union ktime ktime_t; /* Kill this */
|
||||
*
|
||||
* Return: The ktime_t representation of the value.
|
||||
*/
|
||||
static inline ktime_t ktime_set(const long secs, const unsigned long nsecs)
|
||||
static inline ktime_t ktime_set(const s64 secs, const unsigned long nsecs)
|
||||
{
|
||||
#if (BITS_PER_LONG == 64)
|
||||
if (unlikely(secs >= KTIME_SEC_MAX))
|
||||
return (ktime_t){ .tv64 = KTIME_MAX };
|
||||
#endif
|
||||
return (ktime_t) { .tv64 = (s64)secs * NSEC_PER_SEC + (s64)nsecs };
|
||||
|
||||
return (ktime_t) { .tv64 = secs * NSEC_PER_SEC + (s64)nsecs };
|
||||
}
|
||||
|
||||
/* Subtract two ktime_t variables. rem = lhs -rhs: */
|
||||
@ -108,6 +83,12 @@ static inline ktime_t timespec_to_ktime(struct timespec ts)
|
||||
return ktime_set(ts.tv_sec, ts.tv_nsec);
|
||||
}
|
||||
|
||||
/* convert a timespec64 to ktime_t format: */
|
||||
static inline ktime_t timespec64_to_ktime(struct timespec64 ts)
|
||||
{
|
||||
return ktime_set(ts.tv_sec, ts.tv_nsec);
|
||||
}
|
||||
|
||||
/* convert a timeval to ktime_t format: */
|
||||
static inline ktime_t timeval_to_ktime(struct timeval tv)
|
||||
{
|
||||
@ -117,159 +98,15 @@ static inline ktime_t timeval_to_ktime(struct timeval tv)
|
||||
/* Map the ktime_t to timespec conversion to ns_to_timespec function */
|
||||
#define ktime_to_timespec(kt) ns_to_timespec((kt).tv64)
|
||||
|
||||
/* Map the ktime_t to timespec conversion to ns_to_timespec function */
|
||||
#define ktime_to_timespec64(kt) ns_to_timespec64((kt).tv64)
|
||||
|
||||
/* Map the ktime_t to timeval conversion to ns_to_timeval function */
|
||||
#define ktime_to_timeval(kt) ns_to_timeval((kt).tv64)
|
||||
|
||||
/* Convert ktime_t to nanoseconds - NOP in the scalar storage format: */
|
||||
#define ktime_to_ns(kt) ((kt).tv64)
|
||||
|
||||
#else /* !((BITS_PER_LONG == 64) || defined(CONFIG_KTIME_SCALAR)) */
|
||||
|
||||
/*
|
||||
* Helper macros/inlines to get the ktime_t math right in the timespec
|
||||
* representation. The macros are sometimes ugly - their actual use is
|
||||
* pretty okay-ish, given the circumstances. We do all this for
|
||||
* performance reasons. The pure scalar nsec_t based code was nice and
|
||||
* simple, but created too many 64-bit / 32-bit conversions and divisions.
|
||||
*
|
||||
* Be especially aware that negative values are represented in a way
|
||||
* that the tv.sec field is negative and the tv.nsec field is greater
|
||||
* or equal to zero but less than nanoseconds per second. This is the
|
||||
* same representation which is used by timespecs.
|
||||
*
|
||||
* tv.sec < 0 and 0 >= tv.nsec < NSEC_PER_SEC
|
||||
*/
|
||||
|
||||
/* Set a ktime_t variable to a value in sec/nsec representation: */
|
||||
static inline ktime_t ktime_set(const long secs, const unsigned long nsecs)
|
||||
{
|
||||
return (ktime_t) { .tv = { .sec = secs, .nsec = nsecs } };
|
||||
}
|
||||
|
||||
/**
|
||||
* ktime_sub - subtract two ktime_t variables
|
||||
* @lhs: minuend
|
||||
* @rhs: subtrahend
|
||||
*
|
||||
* Return: The remainder of the subtraction.
|
||||
*/
|
||||
static inline ktime_t ktime_sub(const ktime_t lhs, const ktime_t rhs)
|
||||
{
|
||||
ktime_t res;
|
||||
|
||||
res.tv64 = lhs.tv64 - rhs.tv64;
|
||||
if (res.tv.nsec < 0)
|
||||
res.tv.nsec += NSEC_PER_SEC;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* ktime_add - add two ktime_t variables
|
||||
* @add1: addend1
|
||||
* @add2: addend2
|
||||
*
|
||||
* Return: The sum of @add1 and @add2.
|
||||
*/
|
||||
static inline ktime_t ktime_add(const ktime_t add1, const ktime_t add2)
|
||||
{
|
||||
ktime_t res;
|
||||
|
||||
res.tv64 = add1.tv64 + add2.tv64;
|
||||
/*
|
||||
* performance trick: the (u32) -NSEC gives 0x00000000Fxxxxxxx
|
||||
* so we subtract NSEC_PER_SEC and add 1 to the upper 32 bit.
|
||||
*
|
||||
* it's equivalent to:
|
||||
* tv.nsec -= NSEC_PER_SEC
|
||||
* tv.sec ++;
|
||||
*/
|
||||
if (res.tv.nsec >= NSEC_PER_SEC)
|
||||
res.tv64 += (u32)-NSEC_PER_SEC;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* ktime_add_ns - Add a scalar nanoseconds value to a ktime_t variable
|
||||
* @kt: addend
|
||||
* @nsec: the scalar nsec value to add
|
||||
*
|
||||
* Return: The sum of @kt and @nsec in ktime_t format.
|
||||
*/
|
||||
extern ktime_t ktime_add_ns(const ktime_t kt, u64 nsec);
|
||||
|
||||
/**
|
||||
* ktime_sub_ns - Subtract a scalar nanoseconds value from a ktime_t variable
|
||||
* @kt: minuend
|
||||
* @nsec: the scalar nsec value to subtract
|
||||
*
|
||||
* Return: The subtraction of @nsec from @kt in ktime_t format.
|
||||
*/
|
||||
extern ktime_t ktime_sub_ns(const ktime_t kt, u64 nsec);
|
||||
|
||||
/**
|
||||
* timespec_to_ktime - convert a timespec to ktime_t format
|
||||
* @ts: the timespec variable to convert
|
||||
*
|
||||
* Return: A ktime_t variable with the converted timespec value.
|
||||
*/
|
||||
static inline ktime_t timespec_to_ktime(const struct timespec ts)
|
||||
{
|
||||
return (ktime_t) { .tv = { .sec = (s32)ts.tv_sec,
|
||||
.nsec = (s32)ts.tv_nsec } };
|
||||
}
|
||||
|
||||
/**
|
||||
* timeval_to_ktime - convert a timeval to ktime_t format
|
||||
* @tv: the timeval variable to convert
|
||||
*
|
||||
* Return: A ktime_t variable with the converted timeval value.
|
||||
*/
|
||||
static inline ktime_t timeval_to_ktime(const struct timeval tv)
|
||||
{
|
||||
return (ktime_t) { .tv = { .sec = (s32)tv.tv_sec,
|
||||
.nsec = (s32)(tv.tv_usec *
|
||||
NSEC_PER_USEC) } };
|
||||
}
|
||||
|
||||
/**
|
||||
* ktime_to_timespec - convert a ktime_t variable to timespec format
|
||||
* @kt: the ktime_t variable to convert
|
||||
*
|
||||
* Return: The timespec representation of the ktime value.
|
||||
*/
|
||||
static inline struct timespec ktime_to_timespec(const ktime_t kt)
|
||||
{
|
||||
return (struct timespec) { .tv_sec = (time_t) kt.tv.sec,
|
||||
.tv_nsec = (long) kt.tv.nsec };
|
||||
}
|
||||
|
||||
/**
|
||||
* ktime_to_timeval - convert a ktime_t variable to timeval format
|
||||
* @kt: the ktime_t variable to convert
|
||||
*
|
||||
* Return: The timeval representation of the ktime value.
|
||||
*/
|
||||
static inline struct timeval ktime_to_timeval(const ktime_t kt)
|
||||
{
|
||||
return (struct timeval) {
|
||||
.tv_sec = (time_t) kt.tv.sec,
|
||||
.tv_usec = (suseconds_t) (kt.tv.nsec / NSEC_PER_USEC) };
|
||||
}
|
||||
|
||||
/**
|
||||
* ktime_to_ns - convert a ktime_t variable to scalar nanoseconds
|
||||
* @kt: the ktime_t variable to convert
|
||||
*
|
||||
* Return: The scalar nanoseconds representation of @kt.
|
||||
*/
|
||||
static inline s64 ktime_to_ns(const ktime_t kt)
|
||||
{
|
||||
return (s64) kt.tv.sec * NSEC_PER_SEC + kt.tv.nsec;
|
||||
}
|
||||
|
||||
#endif /* !((BITS_PER_LONG == 64) || defined(CONFIG_KTIME_SCALAR)) */
|
||||
|
||||
/**
|
||||
* ktime_equal - Compares two ktime_t variables to see if they are equal
|
||||
@ -328,16 +165,20 @@ static inline bool ktime_before(const ktime_t cmp1, const ktime_t cmp2)
|
||||
return ktime_compare(cmp1, cmp2) < 0;
|
||||
}
|
||||
|
||||
#if BITS_PER_LONG < 64
|
||||
extern u64 ktime_divns(const ktime_t kt, s64 div);
|
||||
#else /* BITS_PER_LONG < 64 */
|
||||
# define ktime_divns(kt, div) (u64)((kt).tv64 / (div))
|
||||
#endif
|
||||
|
||||
static inline s64 ktime_to_us(const ktime_t kt)
|
||||
{
|
||||
struct timeval tv = ktime_to_timeval(kt);
|
||||
return (s64) tv.tv_sec * USEC_PER_SEC + tv.tv_usec;
|
||||
return ktime_divns(kt, NSEC_PER_USEC);
|
||||
}
|
||||
|
||||
static inline s64 ktime_to_ms(const ktime_t kt)
|
||||
{
|
||||
struct timeval tv = ktime_to_timeval(kt);
|
||||
return (s64) tv.tv_sec * MSEC_PER_SEC + tv.tv_usec / USEC_PER_MSEC;
|
||||
return ktime_divns(kt, NSEC_PER_MSEC);
|
||||
}
|
||||
|
||||
static inline s64 ktime_us_delta(const ktime_t later, const ktime_t earlier)
|
||||
@ -381,6 +222,25 @@ static inline __must_check bool ktime_to_timespec_cond(const ktime_t kt,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ktime_to_timespec64_cond - convert a ktime_t variable to timespec64
|
||||
* format only if the variable contains data
|
||||
* @kt: the ktime_t variable to convert
|
||||
* @ts: the timespec variable to store the result in
|
||||
*
|
||||
* Return: %true if there was a successful conversion, %false if kt was 0.
|
||||
*/
|
||||
static inline __must_check bool ktime_to_timespec64_cond(const ktime_t kt,
|
||||
struct timespec64 *ts)
|
||||
{
|
||||
if (kt.tv64) {
|
||||
*ts = ktime_to_timespec64(kt);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The resolution of the clocks. The resolution value is returned in
|
||||
* the clock_getres() system call to give application programmers an
|
||||
@ -390,12 +250,6 @@ static inline __must_check bool ktime_to_timespec_cond(const ktime_t kt,
|
||||
#define LOW_RES_NSEC TICK_NSEC
|
||||
#define KTIME_LOW_RES (ktime_t){ .tv64 = LOW_RES_NSEC }
|
||||
|
||||
/* Get the monotonic time in timespec format: */
|
||||
extern void ktime_get_ts(struct timespec *ts);
|
||||
|
||||
/* Get the real (wall-) time in timespec format: */
|
||||
#define ktime_get_real_ts(ts) getnstimeofday(ts)
|
||||
|
||||
static inline ktime_t ns_to_ktime(u64 ns)
|
||||
{
|
||||
static const ktime_t ktime_zero = { .tv64 = 0 };
|
||||
@ -410,4 +264,6 @@ static inline ktime_t ms_to_ktime(u64 ms)
|
||||
return ktime_add_ms(ktime_zero, ms);
|
||||
}
|
||||
|
||||
# include <linux/timekeeping.h>
|
||||
|
||||
#endif
|
||||
|
@ -109,7 +109,12 @@ static inline bool of_dma_is_coherent(struct device_node *np)
|
||||
extern int of_address_to_resource(struct device_node *dev, int index,
|
||||
struct resource *r);
|
||||
void __iomem *of_iomap(struct device_node *node, int index);
|
||||
void __iomem *of_io_request_and_map(struct device_node *device,
|
||||
int index, char *name);
|
||||
#else
|
||||
|
||||
#include <linux/io.h>
|
||||
|
||||
static inline int of_address_to_resource(struct device_node *dev, int index,
|
||||
struct resource *r)
|
||||
{
|
||||
@ -120,6 +125,12 @@ static inline void __iomem *of_iomap(struct device_node *device, int index)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void __iomem *of_io_request_and_map(struct device_node *device,
|
||||
int index, char *name)
|
||||
{
|
||||
return IOMEM_ERR_PTR(-EINVAL);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_OF_ADDRESS) && defined(CONFIG_PCI)
|
||||
|
@ -1367,8 +1367,8 @@ struct task_struct {
|
||||
} vtime_snap_whence;
|
||||
#endif
|
||||
unsigned long nvcsw, nivcsw; /* context switch counts */
|
||||
struct timespec start_time; /* monotonic time */
|
||||
struct timespec real_start_time; /* boot based time */
|
||||
u64 start_time; /* monotonic time in nsec */
|
||||
u64 real_start_time; /* boot based time in nsec */
|
||||
/* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */
|
||||
unsigned long min_flt, maj_flt;
|
||||
|
||||
|
@ -2,11 +2,6 @@
|
||||
#define __SH_TIMER_H__
|
||||
|
||||
struct sh_timer_config {
|
||||
char *name;
|
||||
long channel_offset;
|
||||
int timer_bit;
|
||||
unsigned long clockevent_rating;
|
||||
unsigned long clocksource_rating;
|
||||
unsigned int channels_mask;
|
||||
};
|
||||
|
||||
|
@ -4,19 +4,10 @@
|
||||
# include <linux/cache.h>
|
||||
# include <linux/seqlock.h>
|
||||
# include <linux/math64.h>
|
||||
#include <uapi/linux/time.h>
|
||||
# include <linux/time64.h>
|
||||
|
||||
extern struct timezone sys_tz;
|
||||
|
||||
/* Parameters used to convert the timespec values: */
|
||||
#define MSEC_PER_SEC 1000L
|
||||
#define USEC_PER_MSEC 1000L
|
||||
#define NSEC_PER_USEC 1000L
|
||||
#define NSEC_PER_MSEC 1000000L
|
||||
#define USEC_PER_SEC 1000000L
|
||||
#define NSEC_PER_SEC 1000000000L
|
||||
#define FSEC_PER_SEC 1000000000000000LL
|
||||
|
||||
#define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
|
||||
|
||||
static inline int timespec_equal(const struct timespec *a,
|
||||
@ -84,13 +75,6 @@ static inline struct timespec timespec_sub(struct timespec lhs,
|
||||
return ts_delta;
|
||||
}
|
||||
|
||||
#define KTIME_MAX ((s64)~((u64)1 << 63))
|
||||
#if (BITS_PER_LONG == 64)
|
||||
# define KTIME_SEC_MAX (KTIME_MAX / NSEC_PER_SEC)
|
||||
#else
|
||||
# define KTIME_SEC_MAX LONG_MAX
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Returns true if the timespec is norm, false if denorm:
|
||||
*/
|
||||
@ -115,27 +99,7 @@ static inline bool timespec_valid_strict(const struct timespec *ts)
|
||||
return true;
|
||||
}
|
||||
|
||||
extern bool persistent_clock_exist;
|
||||
|
||||
static inline bool has_persistent_clock(void)
|
||||
{
|
||||
return persistent_clock_exist;
|
||||
}
|
||||
|
||||
extern void read_persistent_clock(struct timespec *ts);
|
||||
extern void read_boot_clock(struct timespec *ts);
|
||||
extern int persistent_clock_is_local;
|
||||
extern int update_persistent_clock(struct timespec now);
|
||||
void timekeeping_init(void);
|
||||
extern int timekeeping_suspended;
|
||||
|
||||
unsigned long get_seconds(void);
|
||||
struct timespec current_kernel_time(void);
|
||||
struct timespec __current_kernel_time(void); /* does not take xtime_lock */
|
||||
struct timespec get_monotonic_coarse(void);
|
||||
void get_xtime_and_monotonic_and_sleep_offset(struct timespec *xtim,
|
||||
struct timespec *wtom, struct timespec *sleep);
|
||||
void timekeeping_inject_sleeptime(struct timespec *delta);
|
||||
extern struct timespec timespec_trunc(struct timespec t, unsigned gran);
|
||||
|
||||
#define CURRENT_TIME (current_kernel_time())
|
||||
#define CURRENT_TIME_SEC ((struct timespec) { get_seconds(), 0 })
|
||||
@ -153,33 +117,14 @@ void timekeeping_inject_sleeptime(struct timespec *delta);
|
||||
extern u32 (*arch_gettimeoffset)(void);
|
||||
#endif
|
||||
|
||||
extern void do_gettimeofday(struct timeval *tv);
|
||||
extern int do_settimeofday(const struct timespec *tv);
|
||||
extern int do_sys_settimeofday(const struct timespec *tv,
|
||||
const struct timezone *tz);
|
||||
#define do_posix_clock_monotonic_gettime(ts) ktime_get_ts(ts)
|
||||
extern long do_utimes(int dfd, const char __user *filename, struct timespec *times, int flags);
|
||||
struct itimerval;
|
||||
extern int do_setitimer(int which, struct itimerval *value,
|
||||
struct itimerval *ovalue);
|
||||
extern unsigned int alarm_setitimer(unsigned int seconds);
|
||||
extern int do_getitimer(int which, struct itimerval *value);
|
||||
extern int __getnstimeofday(struct timespec *tv);
|
||||
extern void getnstimeofday(struct timespec *tv);
|
||||
extern void getrawmonotonic(struct timespec *ts);
|
||||
extern void getnstime_raw_and_real(struct timespec *ts_raw,
|
||||
struct timespec *ts_real);
|
||||
extern void getboottime(struct timespec *ts);
|
||||
extern void monotonic_to_bootbased(struct timespec *ts);
|
||||
extern void get_monotonic_boottime(struct timespec *ts);
|
||||
|
||||
extern struct timespec timespec_trunc(struct timespec t, unsigned gran);
|
||||
extern int timekeeping_valid_for_hres(void);
|
||||
extern u64 timekeeping_max_deferment(void);
|
||||
extern int timekeeping_inject_offset(struct timespec *ts);
|
||||
extern s32 timekeeping_get_tai_offset(void);
|
||||
extern void timekeeping_set_tai_offset(s32 tai_offset);
|
||||
extern void timekeeping_clocktai(struct timespec *ts);
|
||||
extern unsigned int alarm_setitimer(unsigned int seconds);
|
||||
|
||||
extern long do_utimes(int dfd, const char __user *filename, struct timespec *times, int flags);
|
||||
|
||||
struct tms;
|
||||
extern void do_sys_times(struct tms *);
|
||||
|
190
include/linux/time64.h
Normal file
190
include/linux/time64.h
Normal file
@ -0,0 +1,190 @@
|
||||
#ifndef _LINUX_TIME64_H
|
||||
#define _LINUX_TIME64_H
|
||||
|
||||
#include <uapi/linux/time.h>
|
||||
|
||||
typedef __s64 time64_t;
|
||||
|
||||
/*
|
||||
* This wants to go into uapi/linux/time.h once we agreed about the
|
||||
* userspace interfaces.
|
||||
*/
|
||||
#if __BITS_PER_LONG == 64
|
||||
# define timespec64 timespec
|
||||
#else
|
||||
struct timespec64 {
|
||||
time64_t tv_sec; /* seconds */
|
||||
long tv_nsec; /* nanoseconds */
|
||||
};
|
||||
#endif
|
||||
|
||||
/* Parameters used to convert the timespec values: */
|
||||
#define MSEC_PER_SEC 1000L
|
||||
#define USEC_PER_MSEC 1000L
|
||||
#define NSEC_PER_USEC 1000L
|
||||
#define NSEC_PER_MSEC 1000000L
|
||||
#define USEC_PER_SEC 1000000L
|
||||
#define NSEC_PER_SEC 1000000000L
|
||||
#define FSEC_PER_SEC 1000000000000000LL
|
||||
|
||||
/* Located here for timespec[64]_valid_strict */
|
||||
#define KTIME_MAX ((s64)~((u64)1 << 63))
|
||||
#define KTIME_SEC_MAX (KTIME_MAX / NSEC_PER_SEC)
|
||||
|
||||
#if __BITS_PER_LONG == 64
|
||||
|
||||
static inline struct timespec timespec64_to_timespec(const struct timespec64 ts64)
|
||||
{
|
||||
return ts64;
|
||||
}
|
||||
|
||||
static inline struct timespec64 timespec_to_timespec64(const struct timespec ts)
|
||||
{
|
||||
return ts;
|
||||
}
|
||||
|
||||
# define timespec64_equal timespec_equal
|
||||
# define timespec64_compare timespec_compare
|
||||
# define set_normalized_timespec64 set_normalized_timespec
|
||||
# define timespec64_add_safe timespec_add_safe
|
||||
# define timespec64_add timespec_add
|
||||
# define timespec64_sub timespec_sub
|
||||
# define timespec64_valid timespec_valid
|
||||
# define timespec64_valid_strict timespec_valid_strict
|
||||
# define timespec64_to_ns timespec_to_ns
|
||||
# define ns_to_timespec64 ns_to_timespec
|
||||
# define timespec64_add_ns timespec_add_ns
|
||||
|
||||
#else
|
||||
|
||||
static inline struct timespec timespec64_to_timespec(const struct timespec64 ts64)
|
||||
{
|
||||
struct timespec ret;
|
||||
|
||||
ret.tv_sec = (time_t)ts64.tv_sec;
|
||||
ret.tv_nsec = ts64.tv_nsec;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline struct timespec64 timespec_to_timespec64(const struct timespec ts)
|
||||
{
|
||||
struct timespec64 ret;
|
||||
|
||||
ret.tv_sec = ts.tv_sec;
|
||||
ret.tv_nsec = ts.tv_nsec;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int timespec64_equal(const struct timespec64 *a,
|
||||
const struct timespec64 *b)
|
||||
{
|
||||
return (a->tv_sec == b->tv_sec) && (a->tv_nsec == b->tv_nsec);
|
||||
}
|
||||
|
||||
/*
|
||||
* lhs < rhs: return <0
|
||||
* lhs == rhs: return 0
|
||||
* lhs > rhs: return >0
|
||||
*/
|
||||
static inline int timespec64_compare(const struct timespec64 *lhs, const struct timespec64 *rhs)
|
||||
{
|
||||
if (lhs->tv_sec < rhs->tv_sec)
|
||||
return -1;
|
||||
if (lhs->tv_sec > rhs->tv_sec)
|
||||
return 1;
|
||||
return lhs->tv_nsec - rhs->tv_nsec;
|
||||
}
|
||||
|
||||
extern void set_normalized_timespec64(struct timespec64 *ts, time64_t sec, s64 nsec);
|
||||
|
||||
/*
|
||||
* timespec64_add_safe assumes both values are positive and checks for
|
||||
* overflow. It will return TIME_T_MAX if the returned value would be
|
||||
* smaller then either of the arguments.
|
||||
*/
|
||||
extern struct timespec64 timespec64_add_safe(const struct timespec64 lhs,
|
||||
const struct timespec64 rhs);
|
||||
|
||||
|
||||
static inline struct timespec64 timespec64_add(struct timespec64 lhs,
|
||||
struct timespec64 rhs)
|
||||
{
|
||||
struct timespec64 ts_delta;
|
||||
set_normalized_timespec64(&ts_delta, lhs.tv_sec + rhs.tv_sec,
|
||||
lhs.tv_nsec + rhs.tv_nsec);
|
||||
return ts_delta;
|
||||
}
|
||||
|
||||
/*
|
||||
* sub = lhs - rhs, in normalized form
|
||||
*/
|
||||
static inline struct timespec64 timespec64_sub(struct timespec64 lhs,
|
||||
struct timespec64 rhs)
|
||||
{
|
||||
struct timespec64 ts_delta;
|
||||
set_normalized_timespec64(&ts_delta, lhs.tv_sec - rhs.tv_sec,
|
||||
lhs.tv_nsec - rhs.tv_nsec);
|
||||
return ts_delta;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if the timespec64 is norm, false if denorm:
|
||||
*/
|
||||
static inline bool timespec64_valid(const struct timespec64 *ts)
|
||||
{
|
||||
/* Dates before 1970 are bogus */
|
||||
if (ts->tv_sec < 0)
|
||||
return false;
|
||||
/* Can't have more nanoseconds then a second */
|
||||
if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool timespec64_valid_strict(const struct timespec64 *ts)
|
||||
{
|
||||
if (!timespec64_valid(ts))
|
||||
return false;
|
||||
/* Disallow values that could overflow ktime_t */
|
||||
if ((unsigned long long)ts->tv_sec >= KTIME_SEC_MAX)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* timespec64_to_ns - Convert timespec64 to nanoseconds
|
||||
* @ts: pointer to the timespec64 variable to be converted
|
||||
*
|
||||
* Returns the scalar nanosecond representation of the timespec64
|
||||
* parameter.
|
||||
*/
|
||||
static inline s64 timespec64_to_ns(const struct timespec64 *ts)
|
||||
{
|
||||
return ((s64) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec;
|
||||
}
|
||||
|
||||
/**
|
||||
* ns_to_timespec64 - Convert nanoseconds to timespec64
|
||||
* @nsec: the nanoseconds value to be converted
|
||||
*
|
||||
* Returns the timespec64 representation of the nsec parameter.
|
||||
*/
|
||||
extern struct timespec64 ns_to_timespec64(const s64 nsec);
|
||||
|
||||
/**
|
||||
* timespec64_add_ns - Adds nanoseconds to a timespec64
|
||||
* @a: pointer to timespec64 to be incremented
|
||||
* @ns: unsigned nanoseconds value to be added
|
||||
*
|
||||
* This must always be inlined because its used from the x86-64 vdso,
|
||||
* which cannot call other kernel functions.
|
||||
*/
|
||||
static __always_inline void timespec64_add_ns(struct timespec64 *a, u64 ns)
|
||||
{
|
||||
a->tv_sec += __iter_div_u64_rem(a->tv_nsec + ns, NSEC_PER_SEC, &ns);
|
||||
a->tv_nsec = ns;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* _LINUX_TIME64_H */
|
@ -10,7 +10,22 @@
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/time.h>
|
||||
|
||||
/* Structure holding internal timekeeping values. */
|
||||
/*
|
||||
* Structure holding internal timekeeping values.
|
||||
*
|
||||
* Note: wall_to_monotonic is what we need to add to xtime (or xtime
|
||||
* corrected for sub jiffie times) to get to monotonic time.
|
||||
* Monotonic is pegged at zero at system boot time, so
|
||||
* wall_to_monotonic will be negative, however, we will ALWAYS keep
|
||||
* the tv_nsec part positive so we can use the usual normalization.
|
||||
*
|
||||
* wall_to_monotonic is moved after resume from suspend for the
|
||||
* monotonic time not to jump. We need to add total_sleep_time to
|
||||
* wall_to_monotonic to get the real boot based time offset.
|
||||
*
|
||||
* - wall_to_monotonic is no longer the boot time, getboottime must be
|
||||
* used instead.
|
||||
*/
|
||||
struct timekeeper {
|
||||
/* Current clocksource used for timekeeping. */
|
||||
struct clocksource *clock;
|
||||
@ -18,6 +33,32 @@ struct timekeeper {
|
||||
u32 mult;
|
||||
/* The shift value of the current clocksource. */
|
||||
u32 shift;
|
||||
/* Clock shifted nano seconds */
|
||||
u64 xtime_nsec;
|
||||
|
||||
/* Monotonic base time */
|
||||
ktime_t base_mono;
|
||||
|
||||
/* Current CLOCK_REALTIME time in seconds */
|
||||
u64 xtime_sec;
|
||||
/* CLOCK_REALTIME to CLOCK_MONOTONIC offset */
|
||||
struct timespec64 wall_to_monotonic;
|
||||
|
||||
/* Offset clock monotonic -> clock realtime */
|
||||
ktime_t offs_real;
|
||||
/* Offset clock monotonic -> clock boottime */
|
||||
ktime_t offs_boot;
|
||||
/* Offset clock monotonic -> clock tai */
|
||||
ktime_t offs_tai;
|
||||
|
||||
/* time spent in suspend */
|
||||
struct timespec64 total_sleep_time;
|
||||
/* The current UTC to TAI offset in seconds */
|
||||
s32 tai_offset;
|
||||
|
||||
/* The raw monotonic time for the CLOCK_MONOTONIC_RAW posix clock. */
|
||||
struct timespec64 raw_time;
|
||||
|
||||
/* Number of clock cycles in one NTP interval. */
|
||||
cycle_t cycle_interval;
|
||||
/* Last cycle value (also stored in clock->cycle_last) */
|
||||
@ -29,58 +70,18 @@ struct timekeeper {
|
||||
/* Raw nano seconds accumulated per NTP interval. */
|
||||
u32 raw_interval;
|
||||
|
||||
/* Current CLOCK_REALTIME time in seconds */
|
||||
u64 xtime_sec;
|
||||
/* Clock shifted nano seconds */
|
||||
u64 xtime_nsec;
|
||||
|
||||
/* Difference between accumulated time and NTP time in ntp
|
||||
* shifted nano seconds. */
|
||||
s64 ntp_error;
|
||||
/* Shift conversion between clock shifted nano seconds and
|
||||
* ntp shifted nano seconds. */
|
||||
u32 ntp_error_shift;
|
||||
|
||||
/*
|
||||
* wall_to_monotonic is what we need to add to xtime (or xtime corrected
|
||||
* for sub jiffie times) to get to monotonic time. Monotonic is pegged
|
||||
* at zero at system boot time, so wall_to_monotonic will be negative,
|
||||
* however, we will ALWAYS keep the tv_nsec part positive so we can use
|
||||
* the usual normalization.
|
||||
*
|
||||
* wall_to_monotonic is moved after resume from suspend for the
|
||||
* monotonic time not to jump. We need to add total_sleep_time to
|
||||
* wall_to_monotonic to get the real boot based time offset.
|
||||
*
|
||||
* - wall_to_monotonic is no longer the boot time, getboottime must be
|
||||
* used instead.
|
||||
* Difference between accumulated time and NTP time in ntp
|
||||
* shifted nano seconds.
|
||||
*/
|
||||
struct timespec wall_to_monotonic;
|
||||
/* Offset clock monotonic -> clock realtime */
|
||||
ktime_t offs_real;
|
||||
/* time spent in suspend */
|
||||
struct timespec total_sleep_time;
|
||||
/* Offset clock monotonic -> clock boottime */
|
||||
ktime_t offs_boot;
|
||||
/* The raw monotonic time for the CLOCK_MONOTONIC_RAW posix clock. */
|
||||
struct timespec raw_time;
|
||||
/* The current UTC to TAI offset in seconds */
|
||||
s32 tai_offset;
|
||||
/* Offset clock monotonic -> clock tai */
|
||||
ktime_t offs_tai;
|
||||
|
||||
s64 ntp_error;
|
||||
/*
|
||||
* Shift conversion between clock shifted nano seconds and
|
||||
* ntp shifted nano seconds.
|
||||
*/
|
||||
u32 ntp_error_shift;
|
||||
};
|
||||
|
||||
static inline struct timespec tk_xtime(struct timekeeper *tk)
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
ts.tv_sec = tk->xtime_sec;
|
||||
ts.tv_nsec = (long)(tk->xtime_nsec >> tk->shift);
|
||||
return ts;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_GENERIC_TIME_VSYSCALL
|
||||
|
||||
extern void update_vsyscall(struct timekeeper *tk);
|
||||
@ -92,14 +93,6 @@ extern void update_vsyscall_old(struct timespec *ts, struct timespec *wtm,
|
||||
struct clocksource *c, u32 mult);
|
||||
extern void update_vsyscall_tz(void);
|
||||
|
||||
static inline void update_vsyscall(struct timekeeper *tk)
|
||||
{
|
||||
struct timespec xt;
|
||||
|
||||
xt = tk_xtime(tk);
|
||||
update_vsyscall_old(&xt, &tk->wall_to_monotonic, tk->clock, tk->mult);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline void update_vsyscall(struct timekeeper *tk)
|
||||
|
190
include/linux/timekeeping.h
Normal file
190
include/linux/timekeeping.h
Normal file
@ -0,0 +1,190 @@
|
||||
#ifndef _LINUX_TIMEKEEPING_H
|
||||
#define _LINUX_TIMEKEEPING_H
|
||||
|
||||
/* Included from linux/ktime.h */
|
||||
|
||||
void timekeeping_init(void);
|
||||
extern int timekeeping_suspended;
|
||||
|
||||
/*
|
||||
* Get and set timeofday
|
||||
*/
|
||||
extern void do_gettimeofday(struct timeval *tv);
|
||||
extern int do_settimeofday(const struct timespec *tv);
|
||||
extern int do_sys_settimeofday(const struct timespec *tv,
|
||||
const struct timezone *tz);
|
||||
|
||||
/*
|
||||
* Kernel time accessors
|
||||
*/
|
||||
unsigned long get_seconds(void);
|
||||
struct timespec current_kernel_time(void);
|
||||
/* does not take xtime_lock */
|
||||
struct timespec __current_kernel_time(void);
|
||||
|
||||
/*
|
||||
* timespec based interfaces
|
||||
*/
|
||||
struct timespec get_monotonic_coarse(void);
|
||||
extern void getrawmonotonic(struct timespec *ts);
|
||||
extern void monotonic_to_bootbased(struct timespec *ts);
|
||||
extern void get_monotonic_boottime(struct timespec *ts);
|
||||
extern void ktime_get_ts64(struct timespec64 *ts);
|
||||
|
||||
extern int __getnstimeofday64(struct timespec64 *tv);
|
||||
extern void getnstimeofday64(struct timespec64 *tv);
|
||||
|
||||
#if BITS_PER_LONG == 64
|
||||
static inline int __getnstimeofday(struct timespec *ts)
|
||||
{
|
||||
return __getnstimeofday64(ts);
|
||||
}
|
||||
|
||||
static inline void getnstimeofday(struct timespec *ts)
|
||||
{
|
||||
getnstimeofday64(ts);
|
||||
}
|
||||
|
||||
static inline void ktime_get_ts(struct timespec *ts)
|
||||
{
|
||||
ktime_get_ts64(ts);
|
||||
}
|
||||
|
||||
static inline void ktime_get_real_ts(struct timespec *ts)
|
||||
{
|
||||
getnstimeofday64(ts);
|
||||
}
|
||||
|
||||
#else
|
||||
static inline int __getnstimeofday(struct timespec *ts)
|
||||
{
|
||||
struct timespec64 ts64;
|
||||
int ret = __getnstimeofday64(&ts64);
|
||||
|
||||
*ts = timespec64_to_timespec(ts64);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void getnstimeofday(struct timespec *ts)
|
||||
{
|
||||
struct timespec64 ts64;
|
||||
|
||||
getnstimeofday64(&ts64);
|
||||
*ts = timespec64_to_timespec(ts64);
|
||||
}
|
||||
|
||||
static inline void ktime_get_ts(struct timespec *ts)
|
||||
{
|
||||
struct timespec64 ts64;
|
||||
|
||||
ktime_get_ts64(&ts64);
|
||||
*ts = timespec64_to_timespec(ts64);
|
||||
}
|
||||
|
||||
static inline void ktime_get_real_ts(struct timespec *ts)
|
||||
{
|
||||
struct timespec64 ts64;
|
||||
|
||||
getnstimeofday64(&ts64);
|
||||
*ts = timespec64_to_timespec(ts64);
|
||||
}
|
||||
#endif
|
||||
|
||||
extern void getboottime(struct timespec *ts);
|
||||
|
||||
#define do_posix_clock_monotonic_gettime(ts) ktime_get_ts(ts)
|
||||
#define ktime_get_real_ts64(ts) getnstimeofday64(ts)
|
||||
|
||||
/*
|
||||
* ktime_t based interfaces
|
||||
*/
|
||||
|
||||
enum tk_offsets {
|
||||
TK_OFFS_REAL,
|
||||
TK_OFFS_BOOT,
|
||||
TK_OFFS_TAI,
|
||||
TK_OFFS_MAX,
|
||||
};
|
||||
|
||||
extern ktime_t ktime_get(void);
|
||||
extern ktime_t ktime_get_with_offset(enum tk_offsets offs);
|
||||
extern ktime_t ktime_mono_to_any(ktime_t tmono, enum tk_offsets offs);
|
||||
|
||||
/**
|
||||
* ktime_get_real - get the real (wall-) time in ktime_t format
|
||||
*/
|
||||
static inline ktime_t ktime_get_real(void)
|
||||
{
|
||||
return ktime_get_with_offset(TK_OFFS_REAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* ktime_get_boottime - Returns monotonic time since boot in ktime_t format
|
||||
*
|
||||
* This is similar to CLOCK_MONTONIC/ktime_get, but also includes the
|
||||
* time spent in suspend.
|
||||
*/
|
||||
static inline ktime_t ktime_get_boottime(void)
|
||||
{
|
||||
return ktime_get_with_offset(TK_OFFS_BOOT);
|
||||
}
|
||||
|
||||
/**
|
||||
* ktime_get_clocktai - Returns the TAI time of day in ktime_t format
|
||||
*/
|
||||
static inline ktime_t ktime_get_clocktai(void)
|
||||
{
|
||||
return ktime_get_with_offset(TK_OFFS_TAI);
|
||||
}
|
||||
|
||||
/**
|
||||
* ktime_mono_to_real - Convert monotonic time to clock realtime
|
||||
*/
|
||||
static inline ktime_t ktime_mono_to_real(ktime_t mono)
|
||||
{
|
||||
return ktime_mono_to_any(mono, TK_OFFS_REAL);
|
||||
}
|
||||
|
||||
static inline u64 ktime_get_ns(void)
|
||||
{
|
||||
return ktime_to_ns(ktime_get());
|
||||
}
|
||||
|
||||
static inline u64 ktime_get_real_ns(void)
|
||||
{
|
||||
return ktime_to_ns(ktime_get_real());
|
||||
}
|
||||
|
||||
static inline u64 ktime_get_boot_ns(void)
|
||||
{
|
||||
return ktime_to_ns(ktime_get_boottime());
|
||||
}
|
||||
|
||||
/*
|
||||
* RTC specific
|
||||
*/
|
||||
extern void timekeeping_inject_sleeptime(struct timespec *delta);
|
||||
|
||||
/*
|
||||
* PPS accessor
|
||||
*/
|
||||
extern void getnstime_raw_and_real(struct timespec *ts_raw,
|
||||
struct timespec *ts_real);
|
||||
|
||||
/*
|
||||
* Persistent clock related interfaces
|
||||
*/
|
||||
extern bool persistent_clock_exist;
|
||||
extern int persistent_clock_is_local;
|
||||
|
||||
static inline bool has_persistent_clock(void)
|
||||
{
|
||||
return persistent_clock_exist;
|
||||
}
|
||||
|
||||
extern void read_persistent_clock(struct timespec *ts);
|
||||
extern void read_boot_clock(struct timespec *ts);
|
||||
extern int update_persistent_clock(struct timespec now);
|
||||
|
||||
|
||||
#endif
|
@ -11,6 +11,9 @@
|
||||
/* For O_CLOEXEC and O_NONBLOCK */
|
||||
#include <linux/fcntl.h>
|
||||
|
||||
/* For _IO helpers */
|
||||
#include <linux/ioctl.h>
|
||||
|
||||
/*
|
||||
* CAREFUL: Check include/asm-generic/fcntl.h when defining
|
||||
* new flags, since they might collide with O_* ones. We want
|
||||
@ -29,4 +32,6 @@
|
||||
/* Flags for timerfd_settime. */
|
||||
#define TFD_SETTIME_FLAGS (TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET)
|
||||
|
||||
#define TFD_IOC_SET_TICKS _IOW('T', 0, u64)
|
||||
|
||||
#endif /* _LINUX_TIMERFD_H */
|
||||
|
@ -3,12 +3,11 @@
|
||||
#
|
||||
|
||||
obj-y = fork.o exec_domain.o panic.o \
|
||||
cpu.o exit.o itimer.o time.o softirq.o resource.o \
|
||||
sysctl.o sysctl_binary.o capability.o ptrace.o timer.o user.o \
|
||||
cpu.o exit.o softirq.o resource.o \
|
||||
sysctl.o sysctl_binary.o capability.o ptrace.o user.o \
|
||||
signal.o sys.o kmod.o workqueue.o pid.o task_work.o \
|
||||
extable.o params.o posix-timers.o \
|
||||
kthread.o sys_ni.o posix-cpu-timers.o \
|
||||
hrtimer.o nsproxy.o \
|
||||
extable.o params.o \
|
||||
kthread.o sys_ni.o nsproxy.o \
|
||||
notifier.o ksysfs.o cred.o reboot.o \
|
||||
async.o range.o groups.o smpboot.o
|
||||
|
||||
@ -110,22 +109,6 @@ targets += config_data.h
|
||||
$(obj)/config_data.h: $(obj)/config_data.gz FORCE
|
||||
$(call filechk,ikconfiggz)
|
||||
|
||||
$(obj)/time.o: $(obj)/timeconst.h
|
||||
|
||||
quiet_cmd_hzfile = HZFILE $@
|
||||
cmd_hzfile = echo "hz=$(CONFIG_HZ)" > $@
|
||||
|
||||
targets += hz.bc
|
||||
$(obj)/hz.bc: $(objtree)/include/config/hz.h FORCE
|
||||
$(call if_changed,hzfile)
|
||||
|
||||
quiet_cmd_bc = BC $@
|
||||
cmd_bc = bc -q $(filter-out FORCE,$^) > $@
|
||||
|
||||
targets += timeconst.h
|
||||
$(obj)/timeconst.h: $(obj)/hz.bc $(src)/timeconst.bc FORCE
|
||||
$(call if_changed,bc)
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Roll all the X.509 certificates that we can find together and pull them into
|
||||
|
@ -458,9 +458,7 @@ static void do_acct_process(struct bsd_acct_struct *acct,
|
||||
acct_t ac;
|
||||
mm_segment_t fs;
|
||||
unsigned long flim;
|
||||
u64 elapsed;
|
||||
u64 run_time;
|
||||
struct timespec uptime;
|
||||
u64 elapsed, run_time;
|
||||
struct tty_struct *tty;
|
||||
const struct cred *orig_cred;
|
||||
|
||||
@ -484,10 +482,8 @@ static void do_acct_process(struct bsd_acct_struct *acct,
|
||||
strlcpy(ac.ac_comm, current->comm, sizeof(ac.ac_comm));
|
||||
|
||||
/* calculate run_time in nsec*/
|
||||
do_posix_clock_monotonic_gettime(&uptime);
|
||||
run_time = (u64)uptime.tv_sec*NSEC_PER_SEC + uptime.tv_nsec;
|
||||
run_time -= (u64)current->group_leader->start_time.tv_sec * NSEC_PER_SEC
|
||||
+ current->group_leader->start_time.tv_nsec;
|
||||
run_time = ktime_get_ns();
|
||||
run_time -= current->group_leader->start_time;
|
||||
/* convert nsec -> AHZ */
|
||||
elapsed = nsec_to_AHZ(run_time);
|
||||
#if ACCT_VERSION==3
|
||||
|
@ -2472,7 +2472,7 @@ static void kdb_gmtime(struct timespec *tv, struct kdb_tm *tm)
|
||||
static void kdb_sysinfo(struct sysinfo *val)
|
||||
{
|
||||
struct timespec uptime;
|
||||
do_posix_clock_monotonic_gettime(&uptime);
|
||||
ktime_get_ts(&uptime);
|
||||
memset(val, 0, sizeof(*val));
|
||||
val->uptime = uptime.tv_sec;
|
||||
val->loads[0] = avenrun[0];
|
||||
|
@ -45,16 +45,6 @@ void __delayacct_tsk_init(struct task_struct *tsk)
|
||||
spin_lock_init(&tsk->delays->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Start accounting for a delay statistic using
|
||||
* its starting timestamp (@start)
|
||||
*/
|
||||
|
||||
static inline void delayacct_start(struct timespec *start)
|
||||
{
|
||||
do_posix_clock_monotonic_gettime(start);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finish delay accounting for a statistic using
|
||||
* its timestamps (@start, @end), accumalator (@total) and @count
|
||||
@ -67,7 +57,7 @@ static void delayacct_end(struct timespec *start, struct timespec *end,
|
||||
s64 ns;
|
||||
unsigned long flags;
|
||||
|
||||
do_posix_clock_monotonic_gettime(end);
|
||||
ktime_get_ts(end);
|
||||
ts = timespec_sub(*end, *start);
|
||||
ns = timespec_to_ns(&ts);
|
||||
if (ns < 0)
|
||||
@ -81,7 +71,7 @@ static void delayacct_end(struct timespec *start, struct timespec *end,
|
||||
|
||||
void __delayacct_blkio_start(void)
|
||||
{
|
||||
delayacct_start(¤t->delays->blkio_start);
|
||||
ktime_get_ts(¤t->delays->blkio_start);
|
||||
}
|
||||
|
||||
void __delayacct_blkio_end(void)
|
||||
@ -169,7 +159,7 @@ __u64 __delayacct_blkio_ticks(struct task_struct *tsk)
|
||||
|
||||
void __delayacct_freepages_start(void)
|
||||
{
|
||||
delayacct_start(¤t->delays->freepages_start);
|
||||
ktime_get_ts(¤t->delays->freepages_start);
|
||||
}
|
||||
|
||||
void __delayacct_freepages_end(void)
|
||||
|
@ -1262,9 +1262,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
||||
|
||||
posix_cpu_timers_init(p);
|
||||
|
||||
do_posix_clock_monotonic_gettime(&p->start_time);
|
||||
p->real_start_time = p->start_time;
|
||||
monotonic_to_bootbased(&p->real_start_time);
|
||||
p->start_time = ktime_get_ns();
|
||||
p->real_start_time = ktime_get_boot_ns();
|
||||
p->io_context = NULL;
|
||||
p->audit_context = NULL;
|
||||
if (clone_flags & CLONE_THREAD)
|
||||
|
@ -20,10 +20,6 @@ config GENERIC_TIME_VSYSCALL
|
||||
config GENERIC_TIME_VSYSCALL_OLD
|
||||
bool
|
||||
|
||||
# ktime_t scalar 64bit nsec representation
|
||||
config KTIME_SCALAR
|
||||
bool
|
||||
|
||||
# Old style timekeeping
|
||||
config ARCH_USES_GETTIMEOFFSET
|
||||
bool
|
||||
|
@ -1,3 +1,4 @@
|
||||
obj-y += time.o timer.o hrtimer.o itimer.o posix-timers.o posix-cpu-timers.o
|
||||
obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o
|
||||
obj-y += timeconv.o posix-clock.o alarmtimer.o
|
||||
|
||||
@ -12,3 +13,21 @@ obj-$(CONFIG_TICK_ONESHOT) += tick-oneshot.o
|
||||
obj-$(CONFIG_TICK_ONESHOT) += tick-sched.o
|
||||
obj-$(CONFIG_TIMER_STATS) += timer_stats.o
|
||||
obj-$(CONFIG_DEBUG_FS) += timekeeping_debug.o
|
||||
obj-$(CONFIG_TEST_UDELAY) += udelay_test.o
|
||||
|
||||
$(obj)/time.o: $(obj)/timeconst.h
|
||||
|
||||
quiet_cmd_hzfile = HZFILE $@
|
||||
cmd_hzfile = echo "hz=$(CONFIG_HZ)" > $@
|
||||
|
||||
targets += hz.bc
|
||||
$(obj)/hz.bc: $(objtree)/include/config/hz.h FORCE
|
||||
$(call if_changed,hzfile)
|
||||
|
||||
quiet_cmd_bc = BC $@
|
||||
cmd_bc = bc -q $(filter-out FORCE,$^) > $@
|
||||
|
||||
targets += timeconst.h
|
||||
$(obj)/timeconst.h: $(obj)/hz.bc $(src)/timeconst.bc FORCE
|
||||
$(call if_changed,bc)
|
||||
|
||||
|
@ -54,6 +54,8 @@
|
||||
|
||||
#include <trace/events/timer.h>
|
||||
|
||||
#include "timekeeping.h"
|
||||
|
||||
/*
|
||||
* The timer bases:
|
||||
*
|
||||
@ -114,21 +116,18 @@ static inline int hrtimer_clockid_to_base(clockid_t clock_id)
|
||||
*/
|
||||
static void hrtimer_get_softirq_time(struct hrtimer_cpu_base *base)
|
||||
{
|
||||
ktime_t xtim, mono, boot;
|
||||
struct timespec xts, tom, slp;
|
||||
s32 tai_offset;
|
||||
ktime_t xtim, mono, boot, tai;
|
||||
ktime_t off_real, off_boot, off_tai;
|
||||
|
||||
get_xtime_and_monotonic_and_sleep_offset(&xts, &tom, &slp);
|
||||
tai_offset = timekeeping_get_tai_offset();
|
||||
mono = ktime_get_update_offsets_tick(&off_real, &off_boot, &off_tai);
|
||||
boot = ktime_add(mono, off_boot);
|
||||
xtim = ktime_add(mono, off_real);
|
||||
tai = ktime_add(xtim, off_tai);
|
||||
|
||||
xtim = timespec_to_ktime(xts);
|
||||
mono = ktime_add(xtim, timespec_to_ktime(tom));
|
||||
boot = ktime_add(mono, timespec_to_ktime(slp));
|
||||
base->clock_base[HRTIMER_BASE_REALTIME].softirq_time = xtim;
|
||||
base->clock_base[HRTIMER_BASE_MONOTONIC].softirq_time = mono;
|
||||
base->clock_base[HRTIMER_BASE_BOOTTIME].softirq_time = boot;
|
||||
base->clock_base[HRTIMER_BASE_TAI].softirq_time =
|
||||
ktime_add(xtim, ktime_set(tai_offset, 0));
|
||||
base->clock_base[HRTIMER_BASE_TAI].softirq_time = tai;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -264,60 +263,6 @@ lock_hrtimer_base(const struct hrtimer *timer, unsigned long *flags)
|
||||
* too large for inlining:
|
||||
*/
|
||||
#if BITS_PER_LONG < 64
|
||||
# ifndef CONFIG_KTIME_SCALAR
|
||||
/**
|
||||
* ktime_add_ns - Add a scalar nanoseconds value to a ktime_t variable
|
||||
* @kt: addend
|
||||
* @nsec: the scalar nsec value to add
|
||||
*
|
||||
* Returns the sum of kt and nsec in ktime_t format
|
||||
*/
|
||||
ktime_t ktime_add_ns(const ktime_t kt, u64 nsec)
|
||||
{
|
||||
ktime_t tmp;
|
||||
|
||||
if (likely(nsec < NSEC_PER_SEC)) {
|
||||
tmp.tv64 = nsec;
|
||||
} else {
|
||||
unsigned long rem = do_div(nsec, NSEC_PER_SEC);
|
||||
|
||||
/* Make sure nsec fits into long */
|
||||
if (unlikely(nsec > KTIME_SEC_MAX))
|
||||
return (ktime_t){ .tv64 = KTIME_MAX };
|
||||
|
||||
tmp = ktime_set((long)nsec, rem);
|
||||
}
|
||||
|
||||
return ktime_add(kt, tmp);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(ktime_add_ns);
|
||||
|
||||
/**
|
||||
* ktime_sub_ns - Subtract a scalar nanoseconds value from a ktime_t variable
|
||||
* @kt: minuend
|
||||
* @nsec: the scalar nsec value to subtract
|
||||
*
|
||||
* Returns the subtraction of @nsec from @kt in ktime_t format
|
||||
*/
|
||||
ktime_t ktime_sub_ns(const ktime_t kt, u64 nsec)
|
||||
{
|
||||
ktime_t tmp;
|
||||
|
||||
if (likely(nsec < NSEC_PER_SEC)) {
|
||||
tmp.tv64 = nsec;
|
||||
} else {
|
||||
unsigned long rem = do_div(nsec, NSEC_PER_SEC);
|
||||
|
||||
tmp = ktime_set((long)nsec, rem);
|
||||
}
|
||||
|
||||
return ktime_sub(kt, tmp);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(ktime_sub_ns);
|
||||
# endif /* !CONFIG_KTIME_SCALAR */
|
||||
|
||||
/*
|
||||
* Divide a ktime value by a nanosecond value
|
||||
*/
|
||||
@ -337,6 +282,7 @@ u64 ktime_divns(const ktime_t kt, s64 div)
|
||||
|
||||
return dclc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ktime_divns);
|
||||
#endif /* BITS_PER_LONG >= 64 */
|
||||
|
||||
/*
|
||||
@ -602,6 +548,11 @@ hrtimer_force_reprogram(struct hrtimer_cpu_base *cpu_base, int skip_equal)
|
||||
* timers, we have to check, whether it expires earlier than the timer for
|
||||
* which the clock event device was armed.
|
||||
*
|
||||
* Note, that in case the state has HRTIMER_STATE_CALLBACK set, no reprogramming
|
||||
* and no expiry check happens. The timer gets enqueued into the rbtree. The
|
||||
* reprogramming and expiry check is done in the hrtimer_interrupt or in the
|
||||
* softirq.
|
||||
*
|
||||
* Called with interrupts disabled and base->cpu_base.lock held
|
||||
*/
|
||||
static int hrtimer_reprogram(struct hrtimer *timer,
|
||||
@ -662,25 +613,13 @@ static inline void hrtimer_init_hres(struct hrtimer_cpu_base *base)
|
||||
base->hres_active = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* When High resolution timers are active, try to reprogram. Note, that in case
|
||||
* the state has HRTIMER_STATE_CALLBACK set, no reprogramming and no expiry
|
||||
* check happens. The timer gets enqueued into the rbtree. The reprogramming
|
||||
* and expiry check is done in the hrtimer_interrupt or in the softirq.
|
||||
*/
|
||||
static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer,
|
||||
struct hrtimer_clock_base *base)
|
||||
{
|
||||
return base->cpu_base->hres_active && hrtimer_reprogram(timer, base);
|
||||
}
|
||||
|
||||
static inline ktime_t hrtimer_update_base(struct hrtimer_cpu_base *base)
|
||||
{
|
||||
ktime_t *offs_real = &base->clock_base[HRTIMER_BASE_REALTIME].offset;
|
||||
ktime_t *offs_boot = &base->clock_base[HRTIMER_BASE_BOOTTIME].offset;
|
||||
ktime_t *offs_tai = &base->clock_base[HRTIMER_BASE_TAI].offset;
|
||||
|
||||
return ktime_get_update_offsets(offs_real, offs_boot, offs_tai);
|
||||
return ktime_get_update_offsets_now(offs_real, offs_boot, offs_tai);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -755,8 +694,8 @@ static inline int hrtimer_is_hres_enabled(void) { return 0; }
|
||||
static inline int hrtimer_switch_to_hres(void) { return 0; }
|
||||
static inline void
|
||||
hrtimer_force_reprogram(struct hrtimer_cpu_base *base, int skip_equal) { }
|
||||
static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer,
|
||||
struct hrtimer_clock_base *base)
|
||||
static inline int hrtimer_reprogram(struct hrtimer *timer,
|
||||
struct hrtimer_clock_base *base)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@ -1013,14 +952,25 @@ int __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
|
||||
|
||||
leftmost = enqueue_hrtimer(timer, new_base);
|
||||
|
||||
/*
|
||||
* Only allow reprogramming if the new base is on this CPU.
|
||||
* (it might still be on another CPU if the timer was pending)
|
||||
*
|
||||
* XXX send_remote_softirq() ?
|
||||
*/
|
||||
if (leftmost && new_base->cpu_base == &__get_cpu_var(hrtimer_bases)
|
||||
&& hrtimer_enqueue_reprogram(timer, new_base)) {
|
||||
if (!leftmost) {
|
||||
unlock_hrtimer_base(timer, &flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!hrtimer_is_hres_active(timer)) {
|
||||
/*
|
||||
* Kick to reschedule the next tick to handle the new timer
|
||||
* on dynticks target.
|
||||
*/
|
||||
wake_up_nohz_cpu(new_base->cpu_base->cpu);
|
||||
} else if (new_base->cpu_base == &__get_cpu_var(hrtimer_bases) &&
|
||||
hrtimer_reprogram(timer, new_base)) {
|
||||
/*
|
||||
* Only allow reprogramming if the new base is on this CPU.
|
||||
* (it might still be on another CPU if the timer was pending)
|
||||
*
|
||||
* XXX send_remote_softirq() ?
|
||||
*/
|
||||
if (wakeup) {
|
||||
/*
|
||||
* We need to drop cpu_base->lock to avoid a
|
||||
@ -1680,6 +1630,7 @@ static void init_hrtimers_cpu(int cpu)
|
||||
timerqueue_init_head(&cpu_base->clock_base[i].active);
|
||||
}
|
||||
|
||||
cpu_base->cpu = cpu;
|
||||
hrtimer_init_hres(cpu_base);
|
||||
}
|
||||
|
@ -466,7 +466,8 @@ static DECLARE_DELAYED_WORK(sync_cmos_work, sync_cmos_clock);
|
||||
|
||||
static void sync_cmos_clock(struct work_struct *work)
|
||||
{
|
||||
struct timespec now, next;
|
||||
struct timespec64 now;
|
||||
struct timespec next;
|
||||
int fail = 1;
|
||||
|
||||
/*
|
||||
@ -485,9 +486,9 @@ static void sync_cmos_clock(struct work_struct *work)
|
||||
return;
|
||||
}
|
||||
|
||||
getnstimeofday(&now);
|
||||
getnstimeofday64(&now);
|
||||
if (abs(now.tv_nsec - (NSEC_PER_SEC / 2)) <= tick_nsec * 5) {
|
||||
struct timespec adjust = now;
|
||||
struct timespec adjust = timespec64_to_timespec(now);
|
||||
|
||||
fail = -ENODEV;
|
||||
if (persistent_clock_is_local)
|
||||
@ -531,7 +532,7 @@ void ntp_notify_cmos_timer(void) { }
|
||||
/*
|
||||
* Propagate a new txc->status value into the NTP state:
|
||||
*/
|
||||
static inline void process_adj_status(struct timex *txc, struct timespec *ts)
|
||||
static inline void process_adj_status(struct timex *txc, struct timespec64 *ts)
|
||||
{
|
||||
if ((time_status & STA_PLL) && !(txc->status & STA_PLL)) {
|
||||
time_state = TIME_OK;
|
||||
@ -554,7 +555,7 @@ static inline void process_adj_status(struct timex *txc, struct timespec *ts)
|
||||
|
||||
|
||||
static inline void process_adjtimex_modes(struct timex *txc,
|
||||
struct timespec *ts,
|
||||
struct timespec64 *ts,
|
||||
s32 *time_tai)
|
||||
{
|
||||
if (txc->modes & ADJ_STATUS)
|
||||
@ -640,7 +641,7 @@ int ntp_validate_timex(struct timex *txc)
|
||||
* adjtimex mainly allows reading (and writing, if superuser) of
|
||||
* kernel time-keeping variables. used by xntpd.
|
||||
*/
|
||||
int __do_adjtimex(struct timex *txc, struct timespec *ts, s32 *time_tai)
|
||||
int __do_adjtimex(struct timex *txc, struct timespec64 *ts, s32 *time_tai)
|
||||
{
|
||||
int result;
|
||||
|
||||
@ -684,7 +685,7 @@ int __do_adjtimex(struct timex *txc, struct timespec *ts, s32 *time_tai)
|
||||
/* fill PPS status fields */
|
||||
pps_fill_timex(txc);
|
||||
|
||||
txc->time.tv_sec = ts->tv_sec;
|
||||
txc->time.tv_sec = (time_t)ts->tv_sec;
|
||||
txc->time.tv_usec = ts->tv_nsec;
|
||||
if (!(time_status & STA_NANO))
|
||||
txc->time.tv_usec /= NSEC_PER_USEC;
|
||||
|
@ -7,6 +7,6 @@ extern void ntp_clear(void);
|
||||
extern u64 ntp_tick_length(void);
|
||||
extern int second_overflow(unsigned long secs);
|
||||
extern int ntp_validate_timex(struct timex *);
|
||||
extern int __do_adjtimex(struct timex *, struct timespec *, s32 *);
|
||||
extern int __do_adjtimex(struct timex *, struct timespec64 *, s32 *);
|
||||
extern void __hardpps(const struct timespec *, const struct timespec *);
|
||||
#endif /* _LINUX_NTP_INTERNAL_H */
|
||||
|
@ -49,6 +49,8 @@
|
||||
#include <linux/export.h>
|
||||
#include <linux/hashtable.h>
|
||||
|
||||
#include "timekeeping.h"
|
||||
|
||||
/*
|
||||
* Management arrays for POSIX timers. Timers are now kept in static hash table
|
||||
* with 512 entries.
|
@ -4,6 +4,8 @@
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/tick.h>
|
||||
|
||||
#include "timekeeping.h"
|
||||
|
||||
extern seqlock_t jiffies_lock;
|
||||
|
||||
#define CS_NAME_LEN 32
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include <asm/unistd.h>
|
||||
|
||||
#include "timeconst.h"
|
||||
#include "timekeeping.h"
|
||||
|
||||
/*
|
||||
* The timezone where the local system is located. Used as a default by some
|
||||
@ -420,6 +421,68 @@ struct timeval ns_to_timeval(const s64 nsec)
|
||||
}
|
||||
EXPORT_SYMBOL(ns_to_timeval);
|
||||
|
||||
#if BITS_PER_LONG == 32
|
||||
/**
|
||||
* set_normalized_timespec - set timespec sec and nsec parts and normalize
|
||||
*
|
||||
* @ts: pointer to timespec variable to be set
|
||||
* @sec: seconds to set
|
||||
* @nsec: nanoseconds to set
|
||||
*
|
||||
* Set seconds and nanoseconds field of a timespec variable and
|
||||
* normalize to the timespec storage format
|
||||
*
|
||||
* Note: The tv_nsec part is always in the range of
|
||||
* 0 <= tv_nsec < NSEC_PER_SEC
|
||||
* For negative values only the tv_sec field is negative !
|
||||
*/
|
||||
void set_normalized_timespec64(struct timespec64 *ts, time64_t sec, s64 nsec)
|
||||
{
|
||||
while (nsec >= NSEC_PER_SEC) {
|
||||
/*
|
||||
* The following asm() prevents the compiler from
|
||||
* optimising this loop into a modulo operation. See
|
||||
* also __iter_div_u64_rem() in include/linux/time.h
|
||||
*/
|
||||
asm("" : "+rm"(nsec));
|
||||
nsec -= NSEC_PER_SEC;
|
||||
++sec;
|
||||
}
|
||||
while (nsec < 0) {
|
||||
asm("" : "+rm"(nsec));
|
||||
nsec += NSEC_PER_SEC;
|
||||
--sec;
|
||||
}
|
||||
ts->tv_sec = sec;
|
||||
ts->tv_nsec = nsec;
|
||||
}
|
||||
EXPORT_SYMBOL(set_normalized_timespec64);
|
||||
|
||||
/**
|
||||
* ns_to_timespec64 - Convert nanoseconds to timespec64
|
||||
* @nsec: the nanoseconds value to be converted
|
||||
*
|
||||
* Returns the timespec64 representation of the nsec parameter.
|
||||
*/
|
||||
struct timespec64 ns_to_timespec64(const s64 nsec)
|
||||
{
|
||||
struct timespec64 ts;
|
||||
s32 rem;
|
||||
|
||||
if (!nsec)
|
||||
return (struct timespec64) {0, 0};
|
||||
|
||||
ts.tv_sec = div_s64_rem(nsec, NSEC_PER_SEC, &rem);
|
||||
if (unlikely(rem < 0)) {
|
||||
ts.tv_sec--;
|
||||
rem += NSEC_PER_SEC;
|
||||
}
|
||||
ts.tv_nsec = rem;
|
||||
|
||||
return ts;
|
||||
}
|
||||
EXPORT_SYMBOL(ns_to_timespec64);
|
||||
#endif
|
||||
/*
|
||||
* When we convert to jiffies then we interpret incoming values
|
||||
* the following way:
|
||||
@ -694,6 +757,7 @@ unsigned long nsecs_to_jiffies(u64 n)
|
||||
{
|
||||
return (unsigned long)nsecs_to_jiffies64(n);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nsecs_to_jiffies);
|
||||
|
||||
/*
|
||||
* Add two timespec values and do a safety check for overflow.
|
File diff suppressed because it is too large
Load Diff
20
kernel/time/timekeeping.h
Normal file
20
kernel/time/timekeeping.h
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef _KERNEL_TIME_TIMEKEEPING_H
|
||||
#define _KERNEL_TIME_TIMEKEEPING_H
|
||||
/*
|
||||
* Internal interfaces for kernel/time/
|
||||
*/
|
||||
extern ktime_t ktime_get_update_offsets_tick(ktime_t *offs_real,
|
||||
ktime_t *offs_boot,
|
||||
ktime_t *offs_tai);
|
||||
extern ktime_t ktime_get_update_offsets_now(ktime_t *offs_real,
|
||||
ktime_t *offs_boot,
|
||||
ktime_t *offs_tai);
|
||||
|
||||
extern int timekeeping_valid_for_hres(void);
|
||||
extern u64 timekeeping_max_deferment(void);
|
||||
extern int timekeeping_inject_offset(struct timespec *ts);
|
||||
extern s32 timekeeping_get_tai_offset(void);
|
||||
extern void timekeeping_set_tai_offset(s32 tai_offset);
|
||||
extern void timekeeping_clocktai(struct timespec *ts);
|
||||
|
||||
#endif
|
@ -67,7 +67,7 @@ static int __init tk_debug_sleep_time_init(void)
|
||||
}
|
||||
late_initcall(tk_debug_sleep_time_init);
|
||||
|
||||
void tk_debug_account_sleep_time(struct timespec *t)
|
||||
void tk_debug_account_sleep_time(struct timespec64 *t)
|
||||
{
|
||||
sleep_time_bin[fls(t->tv_sec)]++;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include <linux/time.h>
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
extern void tk_debug_account_sleep_time(struct timespec *t);
|
||||
extern void tk_debug_account_sleep_time(struct timespec64 *t);
|
||||
#else
|
||||
#define tk_debug_account_sleep_time(x)
|
||||
#endif
|
||||
|
@ -82,6 +82,7 @@ struct tvec_base {
|
||||
unsigned long next_timer;
|
||||
unsigned long active_timers;
|
||||
unsigned long all_timers;
|
||||
int cpu;
|
||||
struct tvec_root tv1;
|
||||
struct tvec tv2;
|
||||
struct tvec tv3;
|
||||
@ -409,6 +410,22 @@ static void internal_add_timer(struct tvec_base *base, struct timer_list *timer)
|
||||
base->next_timer = timer->expires;
|
||||
}
|
||||
base->all_timers++;
|
||||
|
||||
/*
|
||||
* Check whether the other CPU is in dynticks mode and needs
|
||||
* to be triggered to reevaluate the timer wheel.
|
||||
* We are protected against the other CPU fiddling
|
||||
* with the timer by holding the timer base lock. This also
|
||||
* makes sure that a CPU on the way to stop its tick can not
|
||||
* evaluate the timer wheel.
|
||||
*
|
||||
* Spare the IPI for deferrable timers on idle targets though.
|
||||
* The next busy ticks will take care of it. Except full dynticks
|
||||
* require special care against races with idle_cpu(), lets deal
|
||||
* with that later.
|
||||
*/
|
||||
if (!tbase_get_deferrable(base) || tick_nohz_full_cpu(base->cpu))
|
||||
wake_up_nohz_cpu(base->cpu);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TIMER_STATS
|
||||
@ -948,22 +965,6 @@ void add_timer_on(struct timer_list *timer, int cpu)
|
||||
timer_set_base(timer, base);
|
||||
debug_activate(timer, timer->expires);
|
||||
internal_add_timer(base, timer);
|
||||
/*
|
||||
* Check whether the other CPU is in dynticks mode and needs
|
||||
* to be triggered to reevaluate the timer wheel.
|
||||
* We are protected against the other CPU fiddling
|
||||
* with the timer by holding the timer base lock. This also
|
||||
* makes sure that a CPU on the way to stop its tick can not
|
||||
* evaluate the timer wheel.
|
||||
*
|
||||
* Spare the IPI for deferrable timers on idle targets though.
|
||||
* The next busy ticks will take care of it. Except full dynticks
|
||||
* require special care against races with idle_cpu(), lets deal
|
||||
* with that later.
|
||||
*/
|
||||
if (!tbase_get_deferrable(timer->base) || tick_nohz_full_cpu(cpu))
|
||||
wake_up_nohz_cpu(cpu);
|
||||
|
||||
spin_unlock_irqrestore(&base->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(add_timer_on);
|
||||
@ -1568,6 +1569,7 @@ static int init_timers_cpu(int cpu)
|
||||
}
|
||||
spin_lock_init(&base->lock);
|
||||
tvec_base_done[cpu] = 1;
|
||||
base->cpu = cpu;
|
||||
} else {
|
||||
base = per_cpu(tvec_bases, cpu);
|
||||
}
|
168
kernel/time/udelay_test.c
Normal file
168
kernel/time/udelay_test.c
Normal file
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* udelay() test kernel module
|
||||
*
|
||||
* Test is executed by writing and reading to /sys/kernel/debug/udelay_test
|
||||
* Tests are configured by writing: USECS ITERATIONS
|
||||
* Tests are executed by reading from the same file.
|
||||
* Specifying usecs of 0 or negative values will run multiples tests.
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#define DEFAULT_ITERATIONS 100
|
||||
|
||||
#define DEBUGFS_FILENAME "udelay_test"
|
||||
|
||||
static DEFINE_MUTEX(udelay_test_lock);
|
||||
static struct dentry *udelay_test_debugfs_file;
|
||||
static int udelay_test_usecs;
|
||||
static int udelay_test_iterations = DEFAULT_ITERATIONS;
|
||||
|
||||
static int udelay_test_single(struct seq_file *s, int usecs, uint32_t iters)
|
||||
{
|
||||
int min = 0, max = 0, fail_count = 0;
|
||||
uint64_t sum = 0;
|
||||
uint64_t avg;
|
||||
int i;
|
||||
/* Allow udelay to be up to 0.5% fast */
|
||||
int allowed_error_ns = usecs * 5;
|
||||
|
||||
for (i = 0; i < iters; ++i) {
|
||||
struct timespec ts1, ts2;
|
||||
int time_passed;
|
||||
|
||||
ktime_get_ts(&ts1);
|
||||
udelay(usecs);
|
||||
ktime_get_ts(&ts2);
|
||||
time_passed = timespec_to_ns(&ts2) - timespec_to_ns(&ts1);
|
||||
|
||||
if (i == 0 || time_passed < min)
|
||||
min = time_passed;
|
||||
if (i == 0 || time_passed > max)
|
||||
max = time_passed;
|
||||
if ((time_passed + allowed_error_ns) / 1000 < usecs)
|
||||
++fail_count;
|
||||
WARN_ON(time_passed < 0);
|
||||
sum += time_passed;
|
||||
}
|
||||
|
||||
avg = sum;
|
||||
do_div(avg, iters);
|
||||
seq_printf(s, "%d usecs x %d: exp=%d allowed=%d min=%d avg=%lld max=%d",
|
||||
usecs, iters, usecs * 1000,
|
||||
(usecs * 1000) - allowed_error_ns, min, avg, max);
|
||||
if (fail_count)
|
||||
seq_printf(s, " FAIL=%d", fail_count);
|
||||
seq_puts(s, "\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int udelay_test_show(struct seq_file *s, void *v)
|
||||
{
|
||||
int usecs;
|
||||
int iters;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&udelay_test_lock);
|
||||
usecs = udelay_test_usecs;
|
||||
iters = udelay_test_iterations;
|
||||
mutex_unlock(&udelay_test_lock);
|
||||
|
||||
if (usecs > 0 && iters > 0) {
|
||||
return udelay_test_single(s, usecs, iters);
|
||||
} else if (usecs == 0) {
|
||||
struct timespec ts;
|
||||
|
||||
ktime_get_ts(&ts);
|
||||
seq_printf(s, "udelay() test (lpj=%ld kt=%ld.%09ld)\n",
|
||||
loops_per_jiffy, ts.tv_sec, ts.tv_nsec);
|
||||
seq_puts(s, "usage:\n");
|
||||
seq_puts(s, "echo USECS [ITERS] > " DEBUGFS_FILENAME "\n");
|
||||
seq_puts(s, "cat " DEBUGFS_FILENAME "\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int udelay_test_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, udelay_test_show, inode->i_private);
|
||||
}
|
||||
|
||||
static ssize_t udelay_test_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *pos)
|
||||
{
|
||||
char lbuf[32];
|
||||
int ret;
|
||||
int usecs;
|
||||
int iters;
|
||||
|
||||
if (count >= sizeof(lbuf))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(lbuf, buf, count))
|
||||
return -EFAULT;
|
||||
lbuf[count] = '\0';
|
||||
|
||||
ret = sscanf(lbuf, "%d %d", &usecs, &iters);
|
||||
if (ret < 1)
|
||||
return -EINVAL;
|
||||
else if (ret < 2)
|
||||
iters = DEFAULT_ITERATIONS;
|
||||
|
||||
mutex_lock(&udelay_test_lock);
|
||||
udelay_test_usecs = usecs;
|
||||
udelay_test_iterations = iters;
|
||||
mutex_unlock(&udelay_test_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations udelay_test_debugfs_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = udelay_test_open,
|
||||
.read = seq_read,
|
||||
.write = udelay_test_write,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int __init udelay_test_init(void)
|
||||
{
|
||||
mutex_lock(&udelay_test_lock);
|
||||
udelay_test_debugfs_file = debugfs_create_file(DEBUGFS_FILENAME,
|
||||
S_IRUSR, NULL, NULL, &udelay_test_debugfs_ops);
|
||||
mutex_unlock(&udelay_test_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(udelay_test_init);
|
||||
|
||||
static void __exit udelay_test_exit(void)
|
||||
{
|
||||
mutex_lock(&udelay_test_lock);
|
||||
debugfs_remove(udelay_test_debugfs_file);
|
||||
mutex_unlock(&udelay_test_lock);
|
||||
}
|
||||
|
||||
module_exit(udelay_test_exit);
|
||||
|
||||
MODULE_AUTHOR("David Riley <davidriley@chromium.org>");
|
||||
MODULE_LICENSE("GPL");
|
@ -31,20 +31,19 @@ void bacct_add_tsk(struct user_namespace *user_ns,
|
||||
struct taskstats *stats, struct task_struct *tsk)
|
||||
{
|
||||
const struct cred *tcred;
|
||||
struct timespec uptime, ts;
|
||||
cputime_t utime, stime, utimescaled, stimescaled;
|
||||
u64 ac_etime;
|
||||
u64 delta;
|
||||
|
||||
BUILD_BUG_ON(TS_COMM_LEN < TASK_COMM_LEN);
|
||||
|
||||
/* calculate task elapsed time in timespec */
|
||||
do_posix_clock_monotonic_gettime(&uptime);
|
||||
ts = timespec_sub(uptime, tsk->start_time);
|
||||
/* rebase elapsed time to usec (should never be negative) */
|
||||
ac_etime = timespec_to_ns(&ts);
|
||||
do_div(ac_etime, NSEC_PER_USEC);
|
||||
stats->ac_etime = ac_etime;
|
||||
stats->ac_btime = get_seconds() - ts.tv_sec;
|
||||
/* calculate task elapsed time in nsec */
|
||||
delta = ktime_get_ns() - tsk->start_time;
|
||||
/* Convert to micro seconds */
|
||||
do_div(delta, NSEC_PER_USEC);
|
||||
stats->ac_etime = delta;
|
||||
/* Convert to seconds for btime */
|
||||
do_div(delta, USEC_PER_SEC);
|
||||
stats->ac_btime = get_seconds() - delta;
|
||||
if (thread_group_leader(tsk)) {
|
||||
stats->ac_exitcode = tsk->exit_code;
|
||||
if (tsk->flags & PF_FORKNOEXEC)
|
||||
|
@ -1649,6 +1649,15 @@ config TEST_BPF
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config TEST_UDELAY
|
||||
tristate "udelay test driver"
|
||||
default n
|
||||
help
|
||||
This builds the "udelay_test" module that helps to make sure
|
||||
that udelay() is working properly.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
source "samples/Kconfig"
|
||||
|
||||
source "lib/Kconfig.kgdb"
|
||||
|
@ -86,8 +86,6 @@ void devm_iounmap(struct device *dev, void __iomem *addr)
|
||||
}
|
||||
EXPORT_SYMBOL(devm_iounmap);
|
||||
|
||||
#define IOMEM_ERR_PTR(err) (__force void __iomem *)ERR_PTR(err)
|
||||
|
||||
/**
|
||||
* devm_ioremap_resource() - check, request region, and ioremap resource
|
||||
* @dev: generic device to handle the resource for
|
||||
|
@ -155,11 +155,9 @@ static char *tomoyo_print_header(struct tomoyo_request_info *r)
|
||||
u8 i;
|
||||
if (!buffer)
|
||||
return NULL;
|
||||
{
|
||||
struct timeval tv;
|
||||
do_gettimeofday(&tv);
|
||||
tomoyo_convert_time(tv.tv_sec, &stamp);
|
||||
}
|
||||
|
||||
tomoyo_convert_time(get_seconds(), &stamp);
|
||||
|
||||
pos = snprintf(buffer, tomoyo_buffer_len - 1,
|
||||
"#%04u/%02u/%02u %02u:%02u:%02u# profile=%u mode=%s "
|
||||
"granted=%s (global-pid=%u) task={ pid=%u ppid=%u "
|
||||
|
@ -2267,13 +2267,11 @@ static unsigned int tomoyo_stat_modified[TOMOYO_MAX_POLICY_STAT];
|
||||
*/
|
||||
void tomoyo_update_stat(const u8 index)
|
||||
{
|
||||
struct timeval tv;
|
||||
do_gettimeofday(&tv);
|
||||
/*
|
||||
* I don't use atomic operations because race condition is not fatal.
|
||||
*/
|
||||
tomoyo_stat_updated[index]++;
|
||||
tomoyo_stat_modified[index] = tv.tv_sec;
|
||||
tomoyo_stat_modified[index] = get_seconds();
|
||||
}
|
||||
|
||||
/**
|
||||
|
66
tools/time/udelay_test.sh
Executable file
66
tools/time/udelay_test.sh
Executable file
@ -0,0 +1,66 @@
|
||||
#!/bin/bash
|
||||
|
||||
# udelay() test script
|
||||
#
|
||||
# Test is executed by writing and reading to /sys/kernel/debug/udelay_test
|
||||
# and exercises a variety of delays to ensure that udelay() is delaying
|
||||
# at least as long as requested (as compared to ktime).
|
||||
#
|
||||
# Copyright (C) 2014 Google, Inc.
|
||||
#
|
||||
# This software is licensed under the terms of the GNU General Public
|
||||
# License version 2, as published by the Free Software Foundation, and
|
||||
# may be copied, distributed, and modified under those terms.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
MODULE_NAME=udelay_test
|
||||
UDELAY_PATH=/sys/kernel/debug/udelay_test
|
||||
|
||||
setup()
|
||||
{
|
||||
/sbin/modprobe -q $MODULE_NAME
|
||||
tmp_file=`mktemp`
|
||||
}
|
||||
|
||||
test_one()
|
||||
{
|
||||
delay=$1
|
||||
echo $delay > $UDELAY_PATH
|
||||
tee -a $tmp_file < $UDELAY_PATH
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
if [ -f $tmp_file ]; then
|
||||
rm $tmp_file
|
||||
fi
|
||||
/sbin/modprobe -q -r $MODULE_NAME
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
setup
|
||||
|
||||
# Delay for a variety of times.
|
||||
# 1..200, 200..500 (by 10), 500..2000 (by 100)
|
||||
for (( delay = 1; delay < 200; delay += 1 )); do
|
||||
test_one $delay
|
||||
done
|
||||
for (( delay = 200; delay < 500; delay += 10 )); do
|
||||
test_one $delay
|
||||
done
|
||||
for (( delay = 500; delay <= 2000; delay += 100 )); do
|
||||
test_one $delay
|
||||
done
|
||||
|
||||
# Search for failures
|
||||
count=`grep -c FAIL $tmp_file`
|
||||
if [ $? -eq "0" ]; then
|
||||
echo "ERROR: $count delays failed to delay long enough"
|
||||
retcode=1
|
||||
fi
|
||||
|
||||
exit $retcode
|
Loading…
Reference in New Issue
Block a user