Merge branches 'timers-core-for-linus' and 'timers-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull timer updates - and a leftover fix - from Thomas Gleixner: "A rather large (commit wise) update from the timer side: - A bulk update to make compile tests work in the clocksource drivers - An overhaul of the h8300 timers - Some more Y2038 work - A few overflow prevention checks in the timekeeping/ntp code - The usual pile of fixes and improvements to the various clocksource/clockevent drivers and core code" Also: "A single fix for the posix-clock poll code which did not make it into 4.4" * 'timers-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (84 commits) clocksource/drivers/acpi_pm: Convert to pr_* macros clocksource: Make clocksource validation work for all clocksources timekeeping: Cap adjustments so they don't exceed the maxadj value ntp: Fix second_overflow's input parameter type to be 64bits ntp: Change time_reftime to time64_t and utilize 64bit __ktime_get_real_seconds timekeeping: Provide internal function __ktime_get_real_seconds clocksource/drivers/h8300: Use ioread / iowrite clocksource/drivers/h8300: Initializer cleanup. clocksource/drivers/h8300: Simplify delta handling clocksource/drivers/h8300: Fix timer not overflow case clocksource/drivers/h8300: Change to overflow interrupt clocksource/drivers/lpc32: Correct pr_err() output format clocksource/drivers/arm_global_timer: Fix suspend resume clocksource/drivers/pistachio: Fix wrong calculated clocksource read value clockevents/drivers/arm_global_timer: Use writel_relaxed in gt_compare_set clocksource/drivers/dw_apb_timer: Inline apbt_readl and apbt_writel clocksource/drivers/dw_apb_timer: Use {readl|writel}_relaxed in critical path clocksource/drivers/dw_apb_timer: Fix apbt_readl return types clocksource/drivers/tango-xtal: Replace code by clocksource_mmio_init clocksource/drivers/h8300: Increase the compilation test coverage ... * 'timers-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: posix-clock: Fix return code on the poll method's error path
This commit is contained in:
commit
b4cee21ee0
@ -9364,7 +9364,7 @@ M: Andreas Noever <andreas.noever@gmail.com>
|
|||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/thunderbolt/
|
F: drivers/thunderbolt/
|
||||||
|
|
||||||
TIMEKEEPING, CLOCKSOURCE CORE, NTP
|
TIMEKEEPING, CLOCKSOURCE CORE, NTP, ALARMTIMER
|
||||||
M: John Stultz <john.stultz@linaro.org>
|
M: John Stultz <john.stultz@linaro.org>
|
||||||
M: Thomas Gleixner <tglx@linutronix.de>
|
M: Thomas Gleixner <tglx@linutronix.de>
|
||||||
L: linux-kernel@vger.kernel.org
|
L: linux-kernel@vger.kernel.org
|
||||||
@ -9377,6 +9377,7 @@ F: include/uapi/linux/time.h
|
|||||||
F: include/uapi/linux/timex.h
|
F: include/uapi/linux/timex.h
|
||||||
F: kernel/time/clocksource.c
|
F: kernel/time/clocksource.c
|
||||||
F: kernel/time/time*.c
|
F: kernel/time/time*.c
|
||||||
|
F: kernel/time/alarmtimer.c
|
||||||
F: kernel/time/ntp.c
|
F: kernel/time/ntp.c
|
||||||
F: tools/testing/selftests/timers/
|
F: tools/testing/selftests/timers/
|
||||||
|
|
||||||
|
@ -611,6 +611,7 @@ config ARCH_PXA
|
|||||||
select AUTO_ZRELADDR
|
select AUTO_ZRELADDR
|
||||||
select COMMON_CLK
|
select COMMON_CLK
|
||||||
select CLKDEV_LOOKUP
|
select CLKDEV_LOOKUP
|
||||||
|
select CLKSRC_PXA
|
||||||
select CLKSRC_MMIO
|
select CLKSRC_MMIO
|
||||||
select CLKSRC_OF
|
select CLKSRC_OF
|
||||||
select GENERIC_CLOCKEVENTS
|
select GENERIC_CLOCKEVENTS
|
||||||
@ -650,6 +651,8 @@ config ARCH_SA1100
|
|||||||
select ARCH_SPARSEMEM_ENABLE
|
select ARCH_SPARSEMEM_ENABLE
|
||||||
select CLKDEV_LOOKUP
|
select CLKDEV_LOOKUP
|
||||||
select CLKSRC_MMIO
|
select CLKSRC_MMIO
|
||||||
|
select CLKSRC_PXA
|
||||||
|
select CLKSRC_OF if OF
|
||||||
select CPU_FREQ
|
select CPU_FREQ
|
||||||
select CPU_SA1100
|
select CPU_SA1100
|
||||||
select GENERIC_CLOCKEVENTS
|
select GENERIC_CLOCKEVENTS
|
||||||
|
@ -27,6 +27,7 @@ menuconfig ARCH_EXYNOS
|
|||||||
select SRAM
|
select SRAM
|
||||||
select THERMAL
|
select THERMAL
|
||||||
select MFD_SYSCON
|
select MFD_SYSCON
|
||||||
|
select CLKSRC_EXYNOS_MCT
|
||||||
help
|
help
|
||||||
Support for SAMSUNG EXYNOS SoCs (EXYNOS4/5)
|
Support for SAMSUNG EXYNOS SoCs (EXYNOS4/5)
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ menuconfig ARCH_STI
|
|||||||
select ARM_GIC
|
select ARM_GIC
|
||||||
select ST_IRQCHIP
|
select ST_IRQCHIP
|
||||||
select ARM_GLOBAL_TIMER
|
select ARM_GLOBAL_TIMER
|
||||||
|
select CLKSRC_ST_LPC
|
||||||
select PINCTRL
|
select PINCTRL
|
||||||
select PINCTRL_ST
|
select PINCTRL_ST
|
||||||
select MFD_SYSCON
|
select MFD_SYSCON
|
||||||
|
@ -32,6 +32,7 @@ config UX500_SOC_DB8500
|
|||||||
select PINCTRL_AB8540
|
select PINCTRL_AB8540
|
||||||
select REGULATOR
|
select REGULATOR
|
||||||
select REGULATOR_DB8500_PRCMU
|
select REGULATOR_DB8500_PRCMU
|
||||||
|
select CLKSRC_DBX500_PRCMU
|
||||||
select PM_GENERIC_DOMAINS if PM
|
select PM_GENERIC_DOMAINS if PM
|
||||||
|
|
||||||
config MACH_MOP500
|
config MACH_MOP500
|
||||||
|
@ -17,6 +17,7 @@ config H8300
|
|||||||
select HAVE_MEMBLOCK
|
select HAVE_MEMBLOCK
|
||||||
select HAVE_DMA_ATTRS
|
select HAVE_DMA_ATTRS
|
||||||
select CLKSRC_OF
|
select CLKSRC_OF
|
||||||
|
select H8300_TMR8
|
||||||
|
|
||||||
config RWSEM_GENERIC_SPINLOCK
|
config RWSEM_GENERIC_SPINLOCK
|
||||||
def_bool y
|
def_bool y
|
||||||
|
@ -3,40 +3,45 @@
|
|||||||
|
|
||||||
#ifdef __KERNEL__
|
#ifdef __KERNEL__
|
||||||
|
|
||||||
#include <asm-generic/io.h>
|
|
||||||
|
|
||||||
/* H8/300 internal I/O functions */
|
/* H8/300 internal I/O functions */
|
||||||
static inline unsigned char ctrl_inb(unsigned long addr)
|
|
||||||
|
#define __raw_readb __raw_readb
|
||||||
|
static inline u8 __raw_readb(const volatile void __iomem *addr)
|
||||||
{
|
{
|
||||||
return *(volatile unsigned char *)addr;
|
return *(volatile u8 *)addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned short ctrl_inw(unsigned long addr)
|
#define __raw_readw __raw_readw
|
||||||
|
static inline u16 __raw_readw(const volatile void __iomem *addr)
|
||||||
{
|
{
|
||||||
return *(volatile unsigned short *)addr;
|
return *(volatile u16 *)addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned long ctrl_inl(unsigned long addr)
|
#define __raw_readl __raw_readl
|
||||||
|
static inline u32 __raw_readl(const volatile void __iomem *addr)
|
||||||
{
|
{
|
||||||
return *(volatile unsigned long *)addr;
|
return *(volatile u32 *)addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void ctrl_outb(unsigned char b, unsigned long addr)
|
#define __raw_writeb __raw_writeb
|
||||||
|
static inline void __raw_writeb(u8 b, const volatile void __iomem *addr)
|
||||||
{
|
{
|
||||||
*(volatile unsigned char *)addr = b;
|
*(volatile u8 *)addr = b;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void ctrl_outw(unsigned short b, unsigned long addr)
|
#define __raw_writew __raw_writew
|
||||||
|
static inline void __raw_writew(u16 b, const volatile void __iomem *addr)
|
||||||
{
|
{
|
||||||
*(volatile unsigned short *)addr = b;
|
*(volatile u16 *)addr = b;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void ctrl_outl(unsigned long b, unsigned long addr)
|
#define __raw_writel __raw_writel
|
||||||
|
static inline void __raw_writel(u32 b, const volatile void __iomem *addr)
|
||||||
{
|
{
|
||||||
*(volatile unsigned long *)addr = b;
|
*(volatile u32 *)addr = b;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void ctrl_bclr(int b, unsigned char *addr)
|
static inline void ctrl_bclr(int b, void __iomem *addr)
|
||||||
{
|
{
|
||||||
if (__builtin_constant_p(b))
|
if (__builtin_constant_p(b))
|
||||||
__asm__("bclr %1,%0" : "+WU"(*addr): "i"(b));
|
__asm__("bclr %1,%0" : "+WU"(*addr): "i"(b));
|
||||||
@ -44,7 +49,7 @@ static inline void ctrl_bclr(int b, unsigned char *addr)
|
|||||||
__asm__("bclr %w1,%0" : "+WU"(*addr): "r"(b));
|
__asm__("bclr %w1,%0" : "+WU"(*addr): "r"(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void ctrl_bset(int b, unsigned char *addr)
|
static inline void ctrl_bset(int b, void __iomem *addr)
|
||||||
{
|
{
|
||||||
if (__builtin_constant_p(b))
|
if (__builtin_constant_p(b))
|
||||||
__asm__("bset %1,%0" : "+WU"(*addr): "i"(b));
|
__asm__("bset %1,%0" : "+WU"(*addr): "i"(b));
|
||||||
@ -52,6 +57,8 @@ static inline void ctrl_bset(int b, unsigned char *addr)
|
|||||||
__asm__("bset %w1,%0" : "+WU"(*addr): "r"(b));
|
__asm__("bset %w1,%0" : "+WU"(*addr): "r"(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include <asm-generic/io.h>
|
||||||
|
|
||||||
#endif /* __KERNEL__ */
|
#endif /* __KERNEL__ */
|
||||||
|
|
||||||
#endif /* _H8300_IO_H */
|
#endif /* _H8300_IO_H */
|
||||||
|
@ -207,14 +207,14 @@ device_initcall(device_probe);
|
|||||||
#define get_wait(base, addr) ({ \
|
#define get_wait(base, addr) ({ \
|
||||||
int baddr; \
|
int baddr; \
|
||||||
baddr = ((addr) / 0x200000 * 2); \
|
baddr = ((addr) / 0x200000 * 2); \
|
||||||
w *= (ctrl_inw((unsigned long)(base) + 2) & (3 << baddr)) + 1; \
|
w *= (readw((base) + 2) & (3 << baddr)) + 1; \
|
||||||
})
|
})
|
||||||
#endif
|
#endif
|
||||||
#if defined(CONFIG_CPU_H8S)
|
#if defined(CONFIG_CPU_H8S)
|
||||||
#define get_wait(base, addr) ({ \
|
#define get_wait(base, addr) ({ \
|
||||||
int baddr; \
|
int baddr; \
|
||||||
baddr = ((addr) / 0x200000 * 16); \
|
baddr = ((addr) / 0x200000 * 16); \
|
||||||
w *= (ctrl_inl((unsigned long)(base) + 2) & (7 << baddr)) + 1; \
|
w *= (readl((base) + 2) & (7 << baddr)) + 1; \
|
||||||
})
|
})
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -228,8 +228,8 @@ static __init int access_timing(void)
|
|||||||
|
|
||||||
bsc = of_find_compatible_node(NULL, NULL, "renesas,h8300-bsc");
|
bsc = of_find_compatible_node(NULL, NULL, "renesas,h8300-bsc");
|
||||||
base = of_iomap(bsc, 0);
|
base = of_iomap(bsc, 0);
|
||||||
w = (ctrl_inb((unsigned long)base + 0) & bit)?2:1;
|
w = (readb(base + 0) & bit)?2:1;
|
||||||
if (ctrl_inb((unsigned long)base + 1) & bit)
|
if (readb(base + 1) & bit)
|
||||||
w *= get_wait(base, addr);
|
w *= get_wait(base, addr);
|
||||||
else
|
else
|
||||||
w *= 2;
|
w *= 2;
|
||||||
|
@ -28,10 +28,16 @@ config CLKSRC_MMIO
|
|||||||
bool
|
bool
|
||||||
|
|
||||||
config DIGICOLOR_TIMER
|
config DIGICOLOR_TIMER
|
||||||
bool
|
bool "Digicolor timer driver" if COMPILE_TEST
|
||||||
|
depends on GENERIC_CLOCKEVENTS
|
||||||
|
help
|
||||||
|
Enables the support for the digicolor timer driver.
|
||||||
|
|
||||||
config DW_APB_TIMER
|
config DW_APB_TIMER
|
||||||
bool
|
bool "DW APB timer driver" if COMPILE_TEST
|
||||||
|
depends on GENERIC_CLOCKEVENTS
|
||||||
|
help
|
||||||
|
Enables the support for the dw_apb timer.
|
||||||
|
|
||||||
config DW_APB_TIMER_OF
|
config DW_APB_TIMER_OF
|
||||||
bool
|
bool
|
||||||
@ -39,47 +45,77 @@ config DW_APB_TIMER_OF
|
|||||||
select CLKSRC_OF
|
select CLKSRC_OF
|
||||||
|
|
||||||
config ROCKCHIP_TIMER
|
config ROCKCHIP_TIMER
|
||||||
bool
|
bool "Rockchip timer driver" if COMPILE_TEST
|
||||||
|
depends on ARM || ARM64
|
||||||
select CLKSRC_OF
|
select CLKSRC_OF
|
||||||
|
help
|
||||||
|
Enables the support for the rockchip timer driver.
|
||||||
|
|
||||||
config ARMADA_370_XP_TIMER
|
config ARMADA_370_XP_TIMER
|
||||||
bool
|
bool "Armada 370 and XP timer driver" if COMPILE_TEST
|
||||||
|
depends on ARM
|
||||||
select CLKSRC_OF
|
select CLKSRC_OF
|
||||||
|
help
|
||||||
|
Enables the support for the Armada 370 and XP timer driver.
|
||||||
|
|
||||||
config MESON6_TIMER
|
config MESON6_TIMER
|
||||||
bool
|
bool "Meson6 timer driver" if COMPILE_TEST
|
||||||
|
depends on GENERIC_CLOCKEVENTS
|
||||||
select CLKSRC_MMIO
|
select CLKSRC_MMIO
|
||||||
|
help
|
||||||
|
Enables the support for the Meson6 timer driver.
|
||||||
|
|
||||||
config ORION_TIMER
|
config ORION_TIMER
|
||||||
|
bool "Orion timer driver" if COMPILE_TEST
|
||||||
|
depends on ARM
|
||||||
select CLKSRC_OF
|
select CLKSRC_OF
|
||||||
select CLKSRC_MMIO
|
select CLKSRC_MMIO
|
||||||
bool
|
help
|
||||||
|
Enables the support for the Orion timer driver
|
||||||
|
|
||||||
config SUN4I_TIMER
|
config SUN4I_TIMER
|
||||||
|
bool "Sun4i timer driver" if COMPILE_TEST
|
||||||
|
depends on GENERIC_CLOCKEVENTS
|
||||||
select CLKSRC_MMIO
|
select CLKSRC_MMIO
|
||||||
bool
|
help
|
||||||
|
Enables support for the Sun4i timer.
|
||||||
|
|
||||||
config SUN5I_HSTIMER
|
config SUN5I_HSTIMER
|
||||||
|
bool "Sun5i timer driver" if COMPILE_TEST
|
||||||
select CLKSRC_MMIO
|
select CLKSRC_MMIO
|
||||||
bool
|
depends on COMMON_CLK
|
||||||
|
help
|
||||||
|
Enables support the Sun5i timer.
|
||||||
|
|
||||||
config TEGRA_TIMER
|
config TEGRA_TIMER
|
||||||
bool
|
bool "Tegra timer driver" if COMPILE_TEST
|
||||||
|
depends on ARM
|
||||||
|
help
|
||||||
|
Enables support for the Tegra driver.
|
||||||
|
|
||||||
config VT8500_TIMER
|
config VT8500_TIMER
|
||||||
bool
|
bool "VT8500 timer driver" if COMPILE_TEST
|
||||||
|
depends on GENERIC_CLOCKEVENTS
|
||||||
|
help
|
||||||
|
Enables support for the VT8500 driver.
|
||||||
|
|
||||||
config CADENCE_TTC_TIMER
|
config CADENCE_TTC_TIMER
|
||||||
bool
|
bool "Cadence TTC timer driver" if COMPILE_TEST
|
||||||
|
depends on COMMON_CLK
|
||||||
|
help
|
||||||
|
Enables support for the cadence ttc driver.
|
||||||
|
|
||||||
config ASM9260_TIMER
|
config ASM9260_TIMER
|
||||||
bool
|
bool "ASM9260 timer driver" if COMPILE_TEST
|
||||||
|
depends on GENERIC_CLOCKEVENTS
|
||||||
select CLKSRC_MMIO
|
select CLKSRC_MMIO
|
||||||
select CLKSRC_OF
|
select CLKSRC_OF
|
||||||
|
help
|
||||||
|
Enables support for the ASM9260 timer.
|
||||||
|
|
||||||
config CLKSRC_NOMADIK_MTU
|
config CLKSRC_NOMADIK_MTU
|
||||||
bool
|
bool "Nomakdik clocksource driver" if COMPILE_TEST
|
||||||
depends on (ARCH_NOMADIK || ARCH_U8500)
|
depends on ARM
|
||||||
select CLKSRC_MMIO
|
select CLKSRC_MMIO
|
||||||
help
|
help
|
||||||
Support for Multi Timer Unit. MTU provides access
|
Support for Multi Timer Unit. MTU provides access
|
||||||
@ -93,9 +129,8 @@ config CLKSRC_NOMADIK_MTU_SCHED_CLOCK
|
|||||||
Use the Multi Timer Unit as the sched_clock.
|
Use the Multi Timer Unit as the sched_clock.
|
||||||
|
|
||||||
config CLKSRC_DBX500_PRCMU
|
config CLKSRC_DBX500_PRCMU
|
||||||
bool "Clocksource PRCMU Timer"
|
bool "Clocksource PRCMU Timer" if COMPILE_TEST
|
||||||
depends on UX500_SOC_DB8500
|
depends on GENERIC_CLOCKEVENTS
|
||||||
default y
|
|
||||||
help
|
help
|
||||||
Use the always on PRCMU Timer as clocksource
|
Use the always on PRCMU Timer as clocksource
|
||||||
|
|
||||||
@ -116,13 +151,18 @@ config CLKSRC_EFM32
|
|||||||
event device.
|
event device.
|
||||||
|
|
||||||
config CLKSRC_LPC32XX
|
config CLKSRC_LPC32XX
|
||||||
bool
|
bool "Clocksource for LPC32XX" if COMPILE_TEST
|
||||||
|
depends on GENERIC_CLOCKEVENTS
|
||||||
select CLKSRC_MMIO
|
select CLKSRC_MMIO
|
||||||
select CLKSRC_OF
|
select CLKSRC_OF
|
||||||
|
help
|
||||||
|
Support for the LPC32XX clocksource.
|
||||||
|
|
||||||
config CLKSRC_PISTACHIO
|
config CLKSRC_PISTACHIO
|
||||||
bool
|
bool "Clocksource for Pistachio SoC" if COMPILE_TEST
|
||||||
select CLKSRC_OF
|
select CLKSRC_OF
|
||||||
|
help
|
||||||
|
Enables the clocksource for the Pistachio SoC.
|
||||||
|
|
||||||
config CLKSRC_TI_32K
|
config CLKSRC_TI_32K
|
||||||
bool "Texas Instruments 32.768 Hz Clocksource" if COMPILE_TEST
|
bool "Texas Instruments 32.768 Hz Clocksource" if COMPILE_TEST
|
||||||
@ -199,13 +239,14 @@ config CLKSRC_METAG_GENERIC
|
|||||||
This option enables support for the Meta per-thread timers.
|
This option enables support for the Meta per-thread timers.
|
||||||
|
|
||||||
config CLKSRC_EXYNOS_MCT
|
config CLKSRC_EXYNOS_MCT
|
||||||
def_bool y if ARCH_EXYNOS
|
bool "Exynos multi core timer driver" if COMPILE_TEST
|
||||||
depends on !ARM64
|
depends on ARM
|
||||||
help
|
help
|
||||||
Support for Multi Core Timer controller on Exynos SoCs.
|
Support for Multi Core Timer controller on Exynos SoCs.
|
||||||
|
|
||||||
config CLKSRC_SAMSUNG_PWM
|
config CLKSRC_SAMSUNG_PWM
|
||||||
bool
|
bool "PWM timer drvier for Samsung S3C, S5P" if COMPILE_TEST
|
||||||
|
depends on GENERIC_CLOCKEVENTS
|
||||||
help
|
help
|
||||||
This is a new clocksource driver for the PWM timer found in
|
This is a new clocksource driver for the PWM timer found in
|
||||||
Samsung S3C, S5P and Exynos SoCs, replacing an earlier driver
|
Samsung S3C, S5P and Exynos SoCs, replacing an earlier driver
|
||||||
@ -213,7 +254,8 @@ config CLKSRC_SAMSUNG_PWM
|
|||||||
needed only on systems that do not have the Exynos MCT available.
|
needed only on systems that do not have the Exynos MCT available.
|
||||||
|
|
||||||
config FSL_FTM_TIMER
|
config FSL_FTM_TIMER
|
||||||
bool
|
bool "Freescale FlexTimer Module driver" if COMPILE_TEST
|
||||||
|
depends on GENERIC_CLOCKEVENTS
|
||||||
help
|
help
|
||||||
Support for Freescale FlexTimer Module (FTM) timer.
|
Support for Freescale FlexTimer Module (FTM) timer.
|
||||||
|
|
||||||
@ -226,9 +268,12 @@ config SYS_SUPPORTS_SH_CMT
|
|||||||
bool
|
bool
|
||||||
|
|
||||||
config MTK_TIMER
|
config MTK_TIMER
|
||||||
|
bool "Mediatek timer driver" if COMPILE_TEST
|
||||||
|
depends on GENERIC_CLOCKEVENTS
|
||||||
select CLKSRC_OF
|
select CLKSRC_OF
|
||||||
select CLKSRC_MMIO
|
select CLKSRC_MMIO
|
||||||
bool
|
help
|
||||||
|
Support for Mediatek timer driver.
|
||||||
|
|
||||||
config SYS_SUPPORTS_SH_MTU2
|
config SYS_SUPPORTS_SH_MTU2
|
||||||
bool
|
bool
|
||||||
@ -279,7 +324,12 @@ config EM_TIMER_STI
|
|||||||
such as EMEV2 from former NEC Electronics.
|
such as EMEV2 from former NEC Electronics.
|
||||||
|
|
||||||
config CLKSRC_QCOM
|
config CLKSRC_QCOM
|
||||||
bool
|
bool "Qualcomm MSM timer" if COMPILE_TEST
|
||||||
|
depends on ARM
|
||||||
|
select CLKSRC_OF
|
||||||
|
help
|
||||||
|
This enables the clocksource and the per CPU clockevent driver for the
|
||||||
|
Qualcomm SoCs.
|
||||||
|
|
||||||
config CLKSRC_VERSATILE
|
config CLKSRC_VERSATILE
|
||||||
bool "ARM Versatile (Express) reference platforms clock source"
|
bool "ARM Versatile (Express) reference platforms clock source"
|
||||||
@ -298,21 +348,40 @@ config CLKSRC_MIPS_GIC
|
|||||||
select CLKSRC_OF
|
select CLKSRC_OF
|
||||||
|
|
||||||
config CLKSRC_TANGO_XTAL
|
config CLKSRC_TANGO_XTAL
|
||||||
bool
|
bool "Clocksource for Tango SoC" if COMPILE_TEST
|
||||||
|
depends on ARM
|
||||||
select CLKSRC_OF
|
select CLKSRC_OF
|
||||||
|
select CLKSRC_MMIO
|
||||||
|
help
|
||||||
|
This enables the clocksource for Tango SoC
|
||||||
|
|
||||||
config CLKSRC_PXA
|
config CLKSRC_PXA
|
||||||
def_bool y if ARCH_PXA || ARCH_SA1100
|
bool "Clocksource for PXA or SA-11x0 platform" if COMPILE_TEST
|
||||||
select CLKSRC_OF if OF
|
depends on GENERIC_CLOCKEVENTS
|
||||||
|
select CLKSRC_MMIO
|
||||||
help
|
help
|
||||||
This enables OST0 support available on PXA and SA-11x0
|
This enables OST0 support available on PXA and SA-11x0
|
||||||
platforms.
|
platforms.
|
||||||
|
|
||||||
|
config H8300_TMR8
|
||||||
|
bool "Clockevent timer for the H8300 platform" if COMPILE_TEST
|
||||||
|
depends on GENERIC_CLOCKEVENTS
|
||||||
|
help
|
||||||
|
This enables the 8 bits timer for the H8300 platform.
|
||||||
|
|
||||||
config H8300_TMR16
|
config H8300_TMR16
|
||||||
bool
|
bool "Clockevent timer for the H83069 platform" if COMPILE_TEST
|
||||||
|
depends on GENERIC_CLOCKEVENTS
|
||||||
|
help
|
||||||
|
This enables the 16 bits timer for the H8300 platform with the
|
||||||
|
H83069 cpu.
|
||||||
|
|
||||||
config H8300_TPU
|
config H8300_TPU
|
||||||
bool
|
bool "Clocksource for the H8300 platform" if COMPILE_TEST
|
||||||
|
depends on GENERIC_CLOCKEVENTS
|
||||||
|
help
|
||||||
|
This enables the clocksource for the H8300 platform with the
|
||||||
|
H8S2678 cpu.
|
||||||
|
|
||||||
config CLKSRC_IMX_GPT
|
config CLKSRC_IMX_GPT
|
||||||
bool "Clocksource using i.MX GPT" if COMPILE_TEST
|
bool "Clocksource using i.MX GPT" if COMPILE_TEST
|
||||||
@ -320,8 +389,7 @@ config CLKSRC_IMX_GPT
|
|||||||
select CLKSRC_MMIO
|
select CLKSRC_MMIO
|
||||||
|
|
||||||
config CLKSRC_ST_LPC
|
config CLKSRC_ST_LPC
|
||||||
bool
|
bool "Low power clocksource found in the LPC" if COMPILE_TEST
|
||||||
depends on ARCH_STI
|
|
||||||
select CLKSRC_OF if OF
|
select CLKSRC_OF if OF
|
||||||
help
|
help
|
||||||
Enable this option to use the Low Power controller timer
|
Enable this option to use the Low Power controller timer
|
||||||
|
@ -60,7 +60,7 @@ obj-$(CONFIG_CLKSRC_MIPS_GIC) += mips-gic-timer.o
|
|||||||
obj-$(CONFIG_CLKSRC_TANGO_XTAL) += tango_xtal.o
|
obj-$(CONFIG_CLKSRC_TANGO_XTAL) += tango_xtal.o
|
||||||
obj-$(CONFIG_CLKSRC_IMX_GPT) += timer-imx-gpt.o
|
obj-$(CONFIG_CLKSRC_IMX_GPT) += timer-imx-gpt.o
|
||||||
obj-$(CONFIG_ASM9260_TIMER) += asm9260_timer.o
|
obj-$(CONFIG_ASM9260_TIMER) += asm9260_timer.o
|
||||||
obj-$(CONFIG_H8300) += h8300_timer8.o
|
obj-$(CONFIG_H8300_TMR8) += h8300_timer8.o
|
||||||
obj-$(CONFIG_H8300_TMR16) += h8300_timer16.o
|
obj-$(CONFIG_H8300_TMR16) += h8300_timer16.o
|
||||||
obj-$(CONFIG_H8300_TPU) += h8300_tpu.o
|
obj-$(CONFIG_H8300_TPU) += h8300_tpu.o
|
||||||
obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o
|
obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o
|
||||||
|
@ -109,10 +109,8 @@ static void acpi_pm_check_blacklist(struct pci_dev *dev)
|
|||||||
|
|
||||||
/* the bug has been fixed in PIIX4M */
|
/* the bug has been fixed in PIIX4M */
|
||||||
if (dev->revision < 3) {
|
if (dev->revision < 3) {
|
||||||
printk(KERN_WARNING "* Found PM-Timer Bug on the chipset."
|
pr_warn("* Found PM-Timer Bug on the chipset. Due to workarounds for a bug,\n"
|
||||||
" Due to workarounds for a bug,\n"
|
"* this clock source is slow. Consider trying other clock sources\n");
|
||||||
"* this clock source is slow. Consider trying"
|
|
||||||
" other clock sources\n");
|
|
||||||
|
|
||||||
acpi_pm_need_workaround();
|
acpi_pm_need_workaround();
|
||||||
}
|
}
|
||||||
@ -125,12 +123,9 @@ static void acpi_pm_check_graylist(struct pci_dev *dev)
|
|||||||
if (acpi_pm_good)
|
if (acpi_pm_good)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
printk(KERN_WARNING "* The chipset may have PM-Timer Bug. Due to"
|
pr_warn("* The chipset may have PM-Timer Bug. Due to workarounds for a bug,\n"
|
||||||
" workarounds for a bug,\n"
|
"* this clock source is slow. If you are sure your timer does not have\n"
|
||||||
"* this clock source is slow. If you are sure your timer"
|
"* this bug, please use \"acpi_pm_good\" to disable the workaround\n");
|
||||||
" does not have\n"
|
|
||||||
"* this bug, please use \"acpi_pm_good\" to disable the"
|
|
||||||
" workaround\n");
|
|
||||||
|
|
||||||
acpi_pm_need_workaround();
|
acpi_pm_need_workaround();
|
||||||
}
|
}
|
||||||
@ -162,8 +157,7 @@ static int verify_pmtmr_rate(void)
|
|||||||
/* Check that the PMTMR delta is within 5% of what we expect */
|
/* Check that the PMTMR delta is within 5% of what we expect */
|
||||||
if (delta < (PMTMR_EXPECTED_RATE * 19) / 20 ||
|
if (delta < (PMTMR_EXPECTED_RATE * 19) / 20 ||
|
||||||
delta > (PMTMR_EXPECTED_RATE * 21) / 20) {
|
delta > (PMTMR_EXPECTED_RATE * 21) / 20) {
|
||||||
printk(KERN_INFO "PM-Timer running at invalid rate: %lu%% "
|
pr_info("PM-Timer running at invalid rate: %lu%% of normal - aborting.\n",
|
||||||
"of normal - aborting.\n",
|
|
||||||
100UL * delta / PMTMR_EXPECTED_RATE);
|
100UL * delta / PMTMR_EXPECTED_RATE);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -199,15 +193,14 @@ static int __init init_acpi_pm_clocksource(void)
|
|||||||
break;
|
break;
|
||||||
if ((value2 < value1) && ((value2) < 0xFFF))
|
if ((value2 < value1) && ((value2) < 0xFFF))
|
||||||
break;
|
break;
|
||||||
printk(KERN_INFO "PM-Timer had inconsistent results:"
|
pr_info("PM-Timer had inconsistent results: %#llx, %#llx - aborting.\n",
|
||||||
" %#llx, %#llx - aborting.\n",
|
|
||||||
value1, value2);
|
value1, value2);
|
||||||
pmtmr_ioport = 0;
|
pmtmr_ioport = 0;
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
if (i == ACPI_PM_READ_CHECKS) {
|
if (i == ACPI_PM_READ_CHECKS) {
|
||||||
printk(KERN_INFO "PM-Timer failed consistency check "
|
pr_info("PM-Timer failed consistency check (%#llx) - aborting.\n",
|
||||||
" (%#llx) - aborting.\n", value1);
|
value1);
|
||||||
pmtmr_ioport = 0;
|
pmtmr_ioport = 0;
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
@ -99,17 +99,17 @@ static void gt_compare_set(unsigned long delta, int periodic)
|
|||||||
|
|
||||||
counter += delta;
|
counter += delta;
|
||||||
ctrl = GT_CONTROL_TIMER_ENABLE;
|
ctrl = GT_CONTROL_TIMER_ENABLE;
|
||||||
writel(ctrl, gt_base + GT_CONTROL);
|
writel_relaxed(ctrl, gt_base + GT_CONTROL);
|
||||||
writel(lower_32_bits(counter), gt_base + GT_COMP0);
|
writel_relaxed(lower_32_bits(counter), gt_base + GT_COMP0);
|
||||||
writel(upper_32_bits(counter), gt_base + GT_COMP1);
|
writel_relaxed(upper_32_bits(counter), gt_base + GT_COMP1);
|
||||||
|
|
||||||
if (periodic) {
|
if (periodic) {
|
||||||
writel(delta, gt_base + GT_AUTO_INC);
|
writel_relaxed(delta, gt_base + GT_AUTO_INC);
|
||||||
ctrl |= GT_CONTROL_AUTO_INC;
|
ctrl |= GT_CONTROL_AUTO_INC;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctrl |= GT_CONTROL_COMP_ENABLE | GT_CONTROL_IRQ_ENABLE;
|
ctrl |= GT_CONTROL_COMP_ENABLE | GT_CONTROL_IRQ_ENABLE;
|
||||||
writel(ctrl, gt_base + GT_CONTROL);
|
writel_relaxed(ctrl, gt_base + GT_CONTROL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gt_clockevent_shutdown(struct clock_event_device *evt)
|
static int gt_clockevent_shutdown(struct clock_event_device *evt)
|
||||||
@ -195,12 +195,23 @@ static cycle_t gt_clocksource_read(struct clocksource *cs)
|
|||||||
return gt_counter_read();
|
return gt_counter_read();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void gt_resume(struct clocksource *cs)
|
||||||
|
{
|
||||||
|
unsigned long ctrl;
|
||||||
|
|
||||||
|
ctrl = readl(gt_base + GT_CONTROL);
|
||||||
|
if (!(ctrl & GT_CONTROL_TIMER_ENABLE))
|
||||||
|
/* re-enable timer on resume */
|
||||||
|
writel(GT_CONTROL_TIMER_ENABLE, gt_base + GT_CONTROL);
|
||||||
|
}
|
||||||
|
|
||||||
static struct clocksource gt_clocksource = {
|
static struct clocksource gt_clocksource = {
|
||||||
.name = "arm_global_timer",
|
.name = "arm_global_timer",
|
||||||
.rating = 300,
|
.rating = 300,
|
||||||
.read = gt_clocksource_read,
|
.read = gt_clocksource_read,
|
||||||
.mask = CLOCKSOURCE_MASK(64),
|
.mask = CLOCKSOURCE_MASK(64),
|
||||||
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
||||||
|
.resume = gt_resume,
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
|
#ifdef CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
|
||||||
|
@ -49,20 +49,31 @@ clocksource_to_dw_apb_clocksource(struct clocksource *cs)
|
|||||||
return container_of(cs, struct dw_apb_clocksource, cs);
|
return container_of(cs, struct dw_apb_clocksource, cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned long apbt_readl(struct dw_apb_timer *timer, unsigned long offs)
|
static inline u32 apbt_readl(struct dw_apb_timer *timer, unsigned long offs)
|
||||||
{
|
{
|
||||||
return readl(timer->base + offs);
|
return readl(timer->base + offs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void apbt_writel(struct dw_apb_timer *timer, unsigned long val,
|
static inline void apbt_writel(struct dw_apb_timer *timer, u32 val,
|
||||||
unsigned long offs)
|
unsigned long offs)
|
||||||
{
|
{
|
||||||
writel(val, timer->base + offs);
|
writel(val, timer->base + offs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline u32 apbt_readl_relaxed(struct dw_apb_timer *timer, unsigned long offs)
|
||||||
|
{
|
||||||
|
return readl_relaxed(timer->base + offs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void apbt_writel_relaxed(struct dw_apb_timer *timer, u32 val,
|
||||||
|
unsigned long offs)
|
||||||
|
{
|
||||||
|
writel_relaxed(val, timer->base + offs);
|
||||||
|
}
|
||||||
|
|
||||||
static void apbt_disable_int(struct dw_apb_timer *timer)
|
static void apbt_disable_int(struct dw_apb_timer *timer)
|
||||||
{
|
{
|
||||||
unsigned long ctrl = apbt_readl(timer, APBTMR_N_CONTROL);
|
u32 ctrl = apbt_readl(timer, APBTMR_N_CONTROL);
|
||||||
|
|
||||||
ctrl |= APBTMR_CONTROL_INT;
|
ctrl |= APBTMR_CONTROL_INT;
|
||||||
apbt_writel(timer, ctrl, APBTMR_N_CONTROL);
|
apbt_writel(timer, ctrl, APBTMR_N_CONTROL);
|
||||||
@ -81,7 +92,7 @@ void dw_apb_clockevent_pause(struct dw_apb_clock_event_device *dw_ced)
|
|||||||
|
|
||||||
static void apbt_eoi(struct dw_apb_timer *timer)
|
static void apbt_eoi(struct dw_apb_timer *timer)
|
||||||
{
|
{
|
||||||
apbt_readl(timer, APBTMR_N_EOI);
|
apbt_readl_relaxed(timer, APBTMR_N_EOI);
|
||||||
}
|
}
|
||||||
|
|
||||||
static irqreturn_t dw_apb_clockevent_irq(int irq, void *data)
|
static irqreturn_t dw_apb_clockevent_irq(int irq, void *data)
|
||||||
@ -103,7 +114,7 @@ static irqreturn_t dw_apb_clockevent_irq(int irq, void *data)
|
|||||||
|
|
||||||
static void apbt_enable_int(struct dw_apb_timer *timer)
|
static void apbt_enable_int(struct dw_apb_timer *timer)
|
||||||
{
|
{
|
||||||
unsigned long ctrl = apbt_readl(timer, APBTMR_N_CONTROL);
|
u32 ctrl = apbt_readl(timer, APBTMR_N_CONTROL);
|
||||||
/* clear pending intr */
|
/* clear pending intr */
|
||||||
apbt_readl(timer, APBTMR_N_EOI);
|
apbt_readl(timer, APBTMR_N_EOI);
|
||||||
ctrl &= ~APBTMR_CONTROL_INT;
|
ctrl &= ~APBTMR_CONTROL_INT;
|
||||||
@ -113,7 +124,7 @@ static void apbt_enable_int(struct dw_apb_timer *timer)
|
|||||||
static int apbt_shutdown(struct clock_event_device *evt)
|
static int apbt_shutdown(struct clock_event_device *evt)
|
||||||
{
|
{
|
||||||
struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt);
|
struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt);
|
||||||
unsigned long ctrl;
|
u32 ctrl;
|
||||||
|
|
||||||
pr_debug("%s CPU %d state=shutdown\n", __func__,
|
pr_debug("%s CPU %d state=shutdown\n", __func__,
|
||||||
cpumask_first(evt->cpumask));
|
cpumask_first(evt->cpumask));
|
||||||
@ -127,7 +138,7 @@ static int apbt_shutdown(struct clock_event_device *evt)
|
|||||||
static int apbt_set_oneshot(struct clock_event_device *evt)
|
static int apbt_set_oneshot(struct clock_event_device *evt)
|
||||||
{
|
{
|
||||||
struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt);
|
struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt);
|
||||||
unsigned long ctrl;
|
u32 ctrl;
|
||||||
|
|
||||||
pr_debug("%s CPU %d state=oneshot\n", __func__,
|
pr_debug("%s CPU %d state=oneshot\n", __func__,
|
||||||
cpumask_first(evt->cpumask));
|
cpumask_first(evt->cpumask));
|
||||||
@ -160,7 +171,7 @@ static int apbt_set_periodic(struct clock_event_device *evt)
|
|||||||
{
|
{
|
||||||
struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt);
|
struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt);
|
||||||
unsigned long period = DIV_ROUND_UP(dw_ced->timer.freq, HZ);
|
unsigned long period = DIV_ROUND_UP(dw_ced->timer.freq, HZ);
|
||||||
unsigned long ctrl;
|
u32 ctrl;
|
||||||
|
|
||||||
pr_debug("%s CPU %d state=periodic\n", __func__,
|
pr_debug("%s CPU %d state=periodic\n", __func__,
|
||||||
cpumask_first(evt->cpumask));
|
cpumask_first(evt->cpumask));
|
||||||
@ -196,17 +207,17 @@ static int apbt_resume(struct clock_event_device *evt)
|
|||||||
static int apbt_next_event(unsigned long delta,
|
static int apbt_next_event(unsigned long delta,
|
||||||
struct clock_event_device *evt)
|
struct clock_event_device *evt)
|
||||||
{
|
{
|
||||||
unsigned long ctrl;
|
u32 ctrl;
|
||||||
struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt);
|
struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt);
|
||||||
|
|
||||||
/* Disable timer */
|
/* Disable timer */
|
||||||
ctrl = apbt_readl(&dw_ced->timer, APBTMR_N_CONTROL);
|
ctrl = apbt_readl_relaxed(&dw_ced->timer, APBTMR_N_CONTROL);
|
||||||
ctrl &= ~APBTMR_CONTROL_ENABLE;
|
ctrl &= ~APBTMR_CONTROL_ENABLE;
|
||||||
apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
|
apbt_writel_relaxed(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
|
||||||
/* write new count */
|
/* write new count */
|
||||||
apbt_writel(&dw_ced->timer, delta, APBTMR_N_LOAD_COUNT);
|
apbt_writel_relaxed(&dw_ced->timer, delta, APBTMR_N_LOAD_COUNT);
|
||||||
ctrl |= APBTMR_CONTROL_ENABLE;
|
ctrl |= APBTMR_CONTROL_ENABLE;
|
||||||
apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
|
apbt_writel_relaxed(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -323,7 +334,7 @@ void dw_apb_clocksource_start(struct dw_apb_clocksource *dw_cs)
|
|||||||
* start count down from 0xffff_ffff. this is done by toggling the
|
* start count down from 0xffff_ffff. this is done by toggling the
|
||||||
* enable bit then load initial load count to ~0.
|
* enable bit then load initial load count to ~0.
|
||||||
*/
|
*/
|
||||||
unsigned long ctrl = apbt_readl(&dw_cs->timer, APBTMR_N_CONTROL);
|
u32 ctrl = apbt_readl(&dw_cs->timer, APBTMR_N_CONTROL);
|
||||||
|
|
||||||
ctrl &= ~APBTMR_CONTROL_ENABLE;
|
ctrl &= ~APBTMR_CONTROL_ENABLE;
|
||||||
apbt_writel(&dw_cs->timer, ctrl, APBTMR_N_CONTROL);
|
apbt_writel(&dw_cs->timer, ctrl, APBTMR_N_CONTROL);
|
||||||
@ -338,11 +349,12 @@ void dw_apb_clocksource_start(struct dw_apb_clocksource *dw_cs)
|
|||||||
|
|
||||||
static cycle_t __apbt_read_clocksource(struct clocksource *cs)
|
static cycle_t __apbt_read_clocksource(struct clocksource *cs)
|
||||||
{
|
{
|
||||||
unsigned long current_count;
|
u32 current_count;
|
||||||
struct dw_apb_clocksource *dw_cs =
|
struct dw_apb_clocksource *dw_cs =
|
||||||
clocksource_to_dw_apb_clocksource(cs);
|
clocksource_to_dw_apb_clocksource(cs);
|
||||||
|
|
||||||
current_count = apbt_readl(&dw_cs->timer, APBTMR_N_CURRENT_VALUE);
|
current_count = apbt_readl_relaxed(&dw_cs->timer,
|
||||||
|
APBTMR_N_CURRENT_VALUE);
|
||||||
|
|
||||||
return (cycle_t)~current_count;
|
return (cycle_t)~current_count;
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
#include <linux/delay.h>
|
||||||
#include <linux/dw_apb_timer.h>
|
#include <linux/dw_apb_timer.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
@ -130,6 +131,17 @@ static void __init init_sched_clock(void)
|
|||||||
sched_clock_register(read_sched_clock, 32, sched_rate);
|
sched_clock_register(read_sched_clock, 32, sched_rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_ARM
|
||||||
|
static unsigned long dw_apb_delay_timer_read(void)
|
||||||
|
{
|
||||||
|
return ~readl_relaxed(sched_io_base);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct delay_timer dw_apb_delay_timer = {
|
||||||
|
.read_current_timer = dw_apb_delay_timer_read,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
static int num_called;
|
static int num_called;
|
||||||
static void __init dw_apb_timer_init(struct device_node *timer)
|
static void __init dw_apb_timer_init(struct device_node *timer)
|
||||||
{
|
{
|
||||||
@ -142,6 +154,10 @@ static void __init dw_apb_timer_init(struct device_node *timer)
|
|||||||
pr_debug("%s: found clocksource timer\n", __func__);
|
pr_debug("%s: found clocksource timer\n", __func__);
|
||||||
add_clocksource(timer);
|
add_clocksource(timer);
|
||||||
init_sched_clock();
|
init_sched_clock();
|
||||||
|
#ifdef CONFIG_ARM
|
||||||
|
dw_apb_delay_timer.freq = sched_rate;
|
||||||
|
register_current_timer_delay(&dw_apb_delay_timer);
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -4,85 +4,56 @@
|
|||||||
* Copyright 2015 Yoshinori Sato <ysato@users.sourcefoge.jp>
|
* Copyright 2015 Yoshinori Sato <ysato@users.sourcefoge.jp>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/param.h>
|
|
||||||
#include <linux/string.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/platform_device.h>
|
|
||||||
#include <linux/clocksource.h>
|
#include <linux/clocksource.h>
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
#include <asm/segment.h>
|
#include <linux/of_irq.h>
|
||||||
#include <asm/irq.h>
|
|
||||||
|
|
||||||
#define TSTR 0
|
#define TSTR 0
|
||||||
#define TSNC 1
|
|
||||||
#define TMDR 2
|
|
||||||
#define TOLR 3
|
|
||||||
#define TISRA 4
|
|
||||||
#define TISRB 5
|
|
||||||
#define TISRC 6
|
#define TISRC 6
|
||||||
|
|
||||||
#define TCR 0
|
#define TCR 0
|
||||||
#define TIOR 1
|
|
||||||
#define TCNT 2
|
#define TCNT 2
|
||||||
#define GRA 4
|
|
||||||
#define GRB 6
|
|
||||||
|
|
||||||
#define FLAG_REPROGRAM (1 << 0)
|
#define bset(b, a) iowrite8(ioread8(a) | (1 << (b)), (a))
|
||||||
#define FLAG_SKIPEVENT (1 << 1)
|
#define bclr(b, a) iowrite8(ioread8(a) & ~(1 << (b)), (a))
|
||||||
#define FLAG_IRQCONTEXT (1 << 2)
|
|
||||||
#define FLAG_STARTED (1 << 3)
|
|
||||||
|
|
||||||
#define ONESHOT 0
|
|
||||||
#define PERIODIC 1
|
|
||||||
|
|
||||||
#define RELATIVE 0
|
|
||||||
#define ABSOLUTE 1
|
|
||||||
|
|
||||||
struct timer16_priv {
|
struct timer16_priv {
|
||||||
struct platform_device *pdev;
|
|
||||||
struct clocksource cs;
|
struct clocksource cs;
|
||||||
struct irqaction irqaction;
|
|
||||||
unsigned long total_cycles;
|
unsigned long total_cycles;
|
||||||
unsigned long mapbase;
|
void __iomem *mapbase;
|
||||||
unsigned long mapcommon;
|
void __iomem *mapcommon;
|
||||||
unsigned long flags;
|
|
||||||
unsigned short gra;
|
|
||||||
unsigned short cs_enabled;
|
unsigned short cs_enabled;
|
||||||
unsigned char enb;
|
unsigned char enb;
|
||||||
unsigned char imfa;
|
|
||||||
unsigned char imiea;
|
|
||||||
unsigned char ovf;
|
unsigned char ovf;
|
||||||
raw_spinlock_t lock;
|
unsigned char ovie;
|
||||||
struct clk *clk;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static unsigned long timer16_get_counter(struct timer16_priv *p)
|
static unsigned long timer16_get_counter(struct timer16_priv *p)
|
||||||
{
|
{
|
||||||
unsigned long v1, v2, v3;
|
unsigned short v1, v2, v3;
|
||||||
int o1, o2;
|
unsigned char o1, o2;
|
||||||
|
|
||||||
o1 = ctrl_inb(p->mapcommon + TISRC) & p->ovf;
|
o1 = ioread8(p->mapcommon + TISRC) & p->ovf;
|
||||||
|
|
||||||
/* Make sure the timer value is stable. Stolen from acpi_pm.c */
|
/* Make sure the timer value is stable. Stolen from acpi_pm.c */
|
||||||
do {
|
do {
|
||||||
o2 = o1;
|
o2 = o1;
|
||||||
v1 = ctrl_inw(p->mapbase + TCNT);
|
v1 = ioread16be(p->mapbase + TCNT);
|
||||||
v2 = ctrl_inw(p->mapbase + TCNT);
|
v2 = ioread16be(p->mapbase + TCNT);
|
||||||
v3 = ctrl_inw(p->mapbase + TCNT);
|
v3 = ioread16be(p->mapbase + TCNT);
|
||||||
o1 = ctrl_inb(p->mapcommon + TISRC) & p->ovf;
|
o1 = ioread8(p->mapcommon + TISRC) & p->ovf;
|
||||||
} while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3)
|
} while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3)
|
||||||
|| (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2)));
|
|| (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2)));
|
||||||
|
|
||||||
v2 |= 0x10000;
|
if (likely(!o1))
|
||||||
return v2;
|
return v2;
|
||||||
|
else
|
||||||
|
return v2 + 0x10000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -90,8 +61,7 @@ static irqreturn_t timer16_interrupt(int irq, void *dev_id)
|
|||||||
{
|
{
|
||||||
struct timer16_priv *p = (struct timer16_priv *)dev_id;
|
struct timer16_priv *p = (struct timer16_priv *)dev_id;
|
||||||
|
|
||||||
ctrl_outb(ctrl_inb(p->mapcommon + TISRA) & ~p->imfa,
|
bclr(p->ovf, p->mapcommon + TISRC);
|
||||||
p->mapcommon + TISRA);
|
|
||||||
p->total_cycles += 0x10000;
|
p->total_cycles += 0x10000;
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
@ -105,13 +75,10 @@ static inline struct timer16_priv *cs_to_priv(struct clocksource *cs)
|
|||||||
static cycle_t timer16_clocksource_read(struct clocksource *cs)
|
static cycle_t timer16_clocksource_read(struct clocksource *cs)
|
||||||
{
|
{
|
||||||
struct timer16_priv *p = cs_to_priv(cs);
|
struct timer16_priv *p = cs_to_priv(cs);
|
||||||
unsigned long flags, raw;
|
unsigned long raw, value;
|
||||||
unsigned long value;
|
|
||||||
|
|
||||||
raw_spin_lock_irqsave(&p->lock, flags);
|
|
||||||
value = p->total_cycles;
|
value = p->total_cycles;
|
||||||
raw = timer16_get_counter(p);
|
raw = timer16_get_counter(p);
|
||||||
raw_spin_unlock_irqrestore(&p->lock, flags);
|
|
||||||
|
|
||||||
return value + raw;
|
return value + raw;
|
||||||
}
|
}
|
||||||
@ -123,10 +90,10 @@ static int timer16_enable(struct clocksource *cs)
|
|||||||
WARN_ON(p->cs_enabled);
|
WARN_ON(p->cs_enabled);
|
||||||
|
|
||||||
p->total_cycles = 0;
|
p->total_cycles = 0;
|
||||||
ctrl_outw(0x0000, p->mapbase + TCNT);
|
iowrite16be(0x0000, p->mapbase + TCNT);
|
||||||
ctrl_outb(0x83, p->mapbase + TCR);
|
iowrite8(0x83, p->mapbase + TCR);
|
||||||
ctrl_outb(ctrl_inb(p->mapcommon + TSTR) | p->enb,
|
bset(p->ovie, p->mapcommon + TISRC);
|
||||||
p->mapcommon + TSTR);
|
bset(p->enb, p->mapcommon + TSTR);
|
||||||
|
|
||||||
p->cs_enabled = true;
|
p->cs_enabled = true;
|
||||||
return 0;
|
return 0;
|
||||||
@ -138,116 +105,83 @@ static void timer16_disable(struct clocksource *cs)
|
|||||||
|
|
||||||
WARN_ON(!p->cs_enabled);
|
WARN_ON(!p->cs_enabled);
|
||||||
|
|
||||||
ctrl_outb(ctrl_inb(p->mapcommon + TSTR) & ~p->enb,
|
bclr(p->ovie, p->mapcommon + TISRC);
|
||||||
p->mapcommon + TSTR);
|
bclr(p->enb, p->mapcommon + TSTR);
|
||||||
|
|
||||||
p->cs_enabled = false;
|
p->cs_enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct timer16_priv timer16_priv = {
|
||||||
|
.cs = {
|
||||||
|
.name = "h8300_16timer",
|
||||||
|
.rating = 200,
|
||||||
|
.read = timer16_clocksource_read,
|
||||||
|
.enable = timer16_enable,
|
||||||
|
.disable = timer16_disable,
|
||||||
|
.mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8),
|
||||||
|
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
#define REG_CH 0
|
#define REG_CH 0
|
||||||
#define REG_COMM 1
|
#define REG_COMM 1
|
||||||
|
|
||||||
static int timer16_setup(struct timer16_priv *p, struct platform_device *pdev)
|
static void __init h8300_16timer_init(struct device_node *node)
|
||||||
{
|
{
|
||||||
struct resource *res[2];
|
void __iomem *base[2];
|
||||||
int ret, irq;
|
int ret, irq;
|
||||||
unsigned int ch;
|
unsigned int ch;
|
||||||
|
struct clk *clk;
|
||||||
|
|
||||||
p->pdev = pdev;
|
clk = of_clk_get(node, 0);
|
||||||
|
if (IS_ERR(clk)) {
|
||||||
res[REG_CH] = platform_get_resource(p->pdev,
|
pr_err("failed to get clock for clocksource\n");
|
||||||
IORESOURCE_MEM, REG_CH);
|
return;
|
||||||
res[REG_COMM] = platform_get_resource(p->pdev,
|
|
||||||
IORESOURCE_MEM, REG_COMM);
|
|
||||||
if (!res[REG_CH] || !res[REG_COMM]) {
|
|
||||||
dev_err(&p->pdev->dev, "failed to get I/O memory\n");
|
|
||||||
return -ENXIO;
|
|
||||||
}
|
|
||||||
irq = platform_get_irq(p->pdev, 0);
|
|
||||||
if (irq < 0) {
|
|
||||||
dev_err(&p->pdev->dev, "failed to get irq\n");
|
|
||||||
return irq;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p->clk = clk_get(&p->pdev->dev, "fck");
|
base[REG_CH] = of_iomap(node, 0);
|
||||||
if (IS_ERR(p->clk)) {
|
if (!base[REG_CH]) {
|
||||||
dev_err(&p->pdev->dev, "can't get clk\n");
|
pr_err("failed to map registers for clocksource\n");
|
||||||
return PTR_ERR(p->clk);
|
goto free_clk;
|
||||||
}
|
}
|
||||||
of_property_read_u32(p->pdev->dev.of_node, "renesas,channel", &ch);
|
|
||||||
|
|
||||||
p->pdev = pdev;
|
base[REG_COMM] = of_iomap(node, 1);
|
||||||
p->mapbase = res[REG_CH]->start;
|
if (!base[REG_COMM]) {
|
||||||
p->mapcommon = res[REG_COMM]->start;
|
pr_err("failed to map registers for clocksource\n");
|
||||||
p->enb = 1 << ch;
|
goto unmap_ch;
|
||||||
p->imfa = 1 << ch;
|
}
|
||||||
p->imiea = 1 << (4 + ch);
|
|
||||||
p->cs.name = pdev->name;
|
irq = irq_of_parse_and_map(node, 0);
|
||||||
p->cs.rating = 200;
|
if (!irq) {
|
||||||
p->cs.read = timer16_clocksource_read;
|
pr_err("failed to get irq for clockevent\n");
|
||||||
p->cs.enable = timer16_enable;
|
goto unmap_comm;
|
||||||
p->cs.disable = timer16_disable;
|
}
|
||||||
p->cs.mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8);
|
|
||||||
p->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
|
of_property_read_u32(node, "renesas,channel", &ch);
|
||||||
|
|
||||||
|
timer16_priv.mapbase = base[REG_CH];
|
||||||
|
timer16_priv.mapcommon = base[REG_COMM];
|
||||||
|
timer16_priv.enb = ch;
|
||||||
|
timer16_priv.ovf = ch;
|
||||||
|
timer16_priv.ovie = 4 + ch;
|
||||||
|
|
||||||
ret = request_irq(irq, timer16_interrupt,
|
ret = request_irq(irq, timer16_interrupt,
|
||||||
IRQF_TIMER, pdev->name, p);
|
IRQF_TIMER, timer16_priv.cs.name, &timer16_priv);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&p->pdev->dev, "failed to request irq %d\n", irq);
|
pr_err("failed to request irq %d of clocksource\n", irq);
|
||||||
return ret;
|
goto unmap_comm;
|
||||||
}
|
}
|
||||||
|
|
||||||
clocksource_register_hz(&p->cs, clk_get_rate(p->clk) / 8);
|
clocksource_register_hz(&timer16_priv.cs,
|
||||||
|
clk_get_rate(clk) / 8);
|
||||||
|
return;
|
||||||
|
|
||||||
return 0;
|
unmap_comm:
|
||||||
|
iounmap(base[REG_COMM]);
|
||||||
|
unmap_ch:
|
||||||
|
iounmap(base[REG_CH]);
|
||||||
|
free_clk:
|
||||||
|
clk_put(clk);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int timer16_probe(struct platform_device *pdev)
|
CLOCKSOURCE_OF_DECLARE(h8300_16bit, "renesas,16bit-timer", h8300_16timer_init);
|
||||||
{
|
|
||||||
struct timer16_priv *p = platform_get_drvdata(pdev);
|
|
||||||
|
|
||||||
if (p) {
|
|
||||||
dev_info(&pdev->dev, "kept as earlytimer\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
|
|
||||||
if (!p)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
return timer16_setup(p, pdev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int timer16_remove(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
return -EBUSY;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct of_device_id timer16_of_table[] = {
|
|
||||||
{ .compatible = "renesas,16bit-timer" },
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
static struct platform_driver timer16_driver = {
|
|
||||||
.probe = timer16_probe,
|
|
||||||
.remove = timer16_remove,
|
|
||||||
.driver = {
|
|
||||||
.name = "h8300h-16timer",
|
|
||||||
.of_match_table = of_match_ptr(timer16_of_table),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static int __init timer16_init(void)
|
|
||||||
{
|
|
||||||
return platform_driver_register(&timer16_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit timer16_exit(void)
|
|
||||||
{
|
|
||||||
platform_driver_unregister(&timer16_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
subsys_initcall(timer16_init);
|
|
||||||
module_exit(timer16_exit);
|
|
||||||
MODULE_AUTHOR("Yoshinori Sato");
|
|
||||||
MODULE_DESCRIPTION("H8/300H 16bit Timer Driver");
|
|
||||||
MODULE_LICENSE("GPL v2");
|
|
||||||
|
@ -8,19 +8,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/platform_device.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/clockchips.h>
|
#include <linux/clockchips.h>
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
#include <asm/irq.h>
|
#include <linux/of_irq.h>
|
||||||
|
|
||||||
#define _8TCR 0
|
#define _8TCR 0
|
||||||
#define _8TCSR 2
|
#define _8TCSR 2
|
||||||
@ -28,126 +24,74 @@
|
|||||||
#define TCORB 6
|
#define TCORB 6
|
||||||
#define _8TCNT 8
|
#define _8TCNT 8
|
||||||
|
|
||||||
#define FLAG_REPROGRAM (1 << 0)
|
#define CMIEA 6
|
||||||
#define FLAG_SKIPEVENT (1 << 1)
|
#define CMFA 6
|
||||||
#define FLAG_IRQCONTEXT (1 << 2)
|
|
||||||
#define FLAG_STARTED (1 << 3)
|
#define FLAG_STARTED (1 << 3)
|
||||||
|
|
||||||
#define ONESHOT 0
|
#define SCALE 64
|
||||||
#define PERIODIC 1
|
|
||||||
|
|
||||||
#define RELATIVE 0
|
#define bset(b, a) iowrite8(ioread8(a) | (1 << (b)), (a))
|
||||||
#define ABSOLUTE 1
|
#define bclr(b, a) iowrite8(ioread8(a) & ~(1 << (b)), (a))
|
||||||
|
|
||||||
struct timer8_priv {
|
struct timer8_priv {
|
||||||
struct platform_device *pdev;
|
|
||||||
struct clock_event_device ced;
|
struct clock_event_device ced;
|
||||||
struct irqaction irqaction;
|
void __iomem *mapbase;
|
||||||
unsigned long mapbase;
|
|
||||||
raw_spinlock_t lock;
|
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
unsigned int rate;
|
unsigned int rate;
|
||||||
unsigned int tcora;
|
|
||||||
struct clk *pclk;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static unsigned long timer8_get_counter(struct timer8_priv *p)
|
|
||||||
{
|
|
||||||
unsigned long v1, v2, v3;
|
|
||||||
int o1, o2;
|
|
||||||
|
|
||||||
o1 = ctrl_inb(p->mapbase + _8TCSR) & 0x20;
|
|
||||||
|
|
||||||
/* Make sure the timer value is stable. Stolen from acpi_pm.c */
|
|
||||||
do {
|
|
||||||
o2 = o1;
|
|
||||||
v1 = ctrl_inw(p->mapbase + _8TCNT);
|
|
||||||
v2 = ctrl_inw(p->mapbase + _8TCNT);
|
|
||||||
v3 = ctrl_inw(p->mapbase + _8TCNT);
|
|
||||||
o1 = ctrl_inb(p->mapbase + _8TCSR) & 0x20;
|
|
||||||
} while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3)
|
|
||||||
|| (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2)));
|
|
||||||
|
|
||||||
v2 |= o1 << 10;
|
|
||||||
return v2;
|
|
||||||
}
|
|
||||||
|
|
||||||
static irqreturn_t timer8_interrupt(int irq, void *dev_id)
|
static irqreturn_t timer8_interrupt(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct timer8_priv *p = dev_id;
|
struct timer8_priv *p = dev_id;
|
||||||
|
|
||||||
ctrl_outb(ctrl_inb(p->mapbase + _8TCSR) & ~0x40,
|
|
||||||
p->mapbase + _8TCSR);
|
|
||||||
p->flags |= FLAG_IRQCONTEXT;
|
|
||||||
ctrl_outw(p->tcora, p->mapbase + TCORA);
|
|
||||||
if (!(p->flags & FLAG_SKIPEVENT)) {
|
|
||||||
if (clockevent_state_oneshot(&p->ced))
|
if (clockevent_state_oneshot(&p->ced))
|
||||||
ctrl_outw(0x0000, p->mapbase + _8TCR);
|
iowrite16be(0x0000, p->mapbase + _8TCR);
|
||||||
|
|
||||||
p->ced.event_handler(&p->ced);
|
p->ced.event_handler(&p->ced);
|
||||||
}
|
|
||||||
p->flags &= ~(FLAG_SKIPEVENT | FLAG_IRQCONTEXT);
|
bclr(CMFA, p->mapbase + _8TCSR);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void timer8_set_next(struct timer8_priv *p, unsigned long delta)
|
static void timer8_set_next(struct timer8_priv *p, unsigned long delta)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
|
||||||
unsigned long now;
|
|
||||||
|
|
||||||
raw_spin_lock_irqsave(&p->lock, flags);
|
|
||||||
if (delta >= 0x10000)
|
if (delta >= 0x10000)
|
||||||
dev_warn(&p->pdev->dev, "delta out of range\n");
|
pr_warn("delta out of range\n");
|
||||||
now = timer8_get_counter(p);
|
bclr(CMIEA, p->mapbase + _8TCR);
|
||||||
p->tcora = delta;
|
iowrite16be(delta, p->mapbase + TCORA);
|
||||||
ctrl_outb(ctrl_inb(p->mapbase + _8TCR) | 0x40, p->mapbase + _8TCR);
|
iowrite16be(0x0000, p->mapbase + _8TCNT);
|
||||||
if (delta > now)
|
bclr(CMFA, p->mapbase + _8TCSR);
|
||||||
ctrl_outw(delta, p->mapbase + TCORA);
|
bset(CMIEA, p->mapbase + _8TCR);
|
||||||
else
|
|
||||||
ctrl_outw(now + 1, p->mapbase + TCORA);
|
|
||||||
|
|
||||||
raw_spin_unlock_irqrestore(&p->lock, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int timer8_enable(struct timer8_priv *p)
|
static int timer8_enable(struct timer8_priv *p)
|
||||||
{
|
{
|
||||||
p->rate = clk_get_rate(p->pclk) / 64;
|
iowrite16be(0xffff, p->mapbase + TCORA);
|
||||||
ctrl_outw(0xffff, p->mapbase + TCORA);
|
iowrite16be(0x0000, p->mapbase + _8TCNT);
|
||||||
ctrl_outw(0x0000, p->mapbase + _8TCNT);
|
iowrite16be(0x0c02, p->mapbase + _8TCR);
|
||||||
ctrl_outw(0x0c02, p->mapbase + _8TCR);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int timer8_start(struct timer8_priv *p)
|
static int timer8_start(struct timer8_priv *p)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret;
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
raw_spin_lock_irqsave(&p->lock, flags);
|
if ((p->flags & FLAG_STARTED))
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (!(p->flags & FLAG_STARTED))
|
|
||||||
ret = timer8_enable(p);
|
ret = timer8_enable(p);
|
||||||
|
if (!ret)
|
||||||
if (ret)
|
|
||||||
goto out;
|
|
||||||
p->flags |= FLAG_STARTED;
|
p->flags |= FLAG_STARTED;
|
||||||
|
|
||||||
out:
|
|
||||||
raw_spin_unlock_irqrestore(&p->lock, flags);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void timer8_stop(struct timer8_priv *p)
|
static void timer8_stop(struct timer8_priv *p)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
iowrite16be(0x0000, p->mapbase + _8TCR);
|
||||||
|
|
||||||
raw_spin_lock_irqsave(&p->lock, flags);
|
|
||||||
|
|
||||||
ctrl_outw(0x0000, p->mapbase + _8TCR);
|
|
||||||
|
|
||||||
raw_spin_unlock_irqrestore(&p->lock, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct timer8_priv *ced_to_priv(struct clock_event_device *ced)
|
static inline struct timer8_priv *ced_to_priv(struct clock_event_device *ced)
|
||||||
@ -155,7 +99,7 @@ static inline struct timer8_priv *ced_to_priv(struct clock_event_device *ced)
|
|||||||
return container_of(ced, struct timer8_priv, ced);
|
return container_of(ced, struct timer8_priv, ced);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void timer8_clock_event_start(struct timer8_priv *p, int periodic)
|
static void timer8_clock_event_start(struct timer8_priv *p, unsigned long delta)
|
||||||
{
|
{
|
||||||
struct clock_event_device *ced = &p->ced;
|
struct clock_event_device *ced = &p->ced;
|
||||||
|
|
||||||
@ -166,7 +110,7 @@ static void timer8_clock_event_start(struct timer8_priv *p, int periodic)
|
|||||||
ced->max_delta_ns = clockevent_delta2ns(0xffff, ced);
|
ced->max_delta_ns = clockevent_delta2ns(0xffff, ced);
|
||||||
ced->min_delta_ns = clockevent_delta2ns(0x0001, ced);
|
ced->min_delta_ns = clockevent_delta2ns(0x0001, ced);
|
||||||
|
|
||||||
timer8_set_next(p, periodic?(p->rate + HZ/2) / HZ:0x10000);
|
timer8_set_next(p, delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int timer8_clock_event_shutdown(struct clock_event_device *ced)
|
static int timer8_clock_event_shutdown(struct clock_event_device *ced)
|
||||||
@ -179,9 +123,9 @@ static int timer8_clock_event_periodic(struct clock_event_device *ced)
|
|||||||
{
|
{
|
||||||
struct timer8_priv *p = ced_to_priv(ced);
|
struct timer8_priv *p = ced_to_priv(ced);
|
||||||
|
|
||||||
dev_info(&p->pdev->dev, "used for periodic clock events\n");
|
pr_info("%s: used for periodic clock events\n", ced->name);
|
||||||
timer8_stop(p);
|
timer8_stop(p);
|
||||||
timer8_clock_event_start(p, PERIODIC);
|
timer8_clock_event_start(p, (p->rate + HZ/2) / HZ);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -190,9 +134,9 @@ static int timer8_clock_event_oneshot(struct clock_event_device *ced)
|
|||||||
{
|
{
|
||||||
struct timer8_priv *p = ced_to_priv(ced);
|
struct timer8_priv *p = ced_to_priv(ced);
|
||||||
|
|
||||||
dev_info(&p->pdev->dev, "used for oneshot clock events\n");
|
pr_info("%s: used for oneshot clock events\n", ced->name);
|
||||||
timer8_stop(p);
|
timer8_stop(p);
|
||||||
timer8_clock_event_start(p, ONESHOT);
|
timer8_clock_event_start(p, 0x10000);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -208,110 +152,64 @@ static int timer8_clock_event_next(unsigned long delta,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int timer8_setup(struct timer8_priv *p,
|
static struct timer8_priv timer8_priv = {
|
||||||
struct platform_device *pdev)
|
.ced = {
|
||||||
|
.name = "h8300_8timer",
|
||||||
|
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
|
||||||
|
.rating = 200,
|
||||||
|
.set_next_event = timer8_clock_event_next,
|
||||||
|
.set_state_shutdown = timer8_clock_event_shutdown,
|
||||||
|
.set_state_periodic = timer8_clock_event_periodic,
|
||||||
|
.set_state_oneshot = timer8_clock_event_oneshot,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void __init h8300_8timer_init(struct device_node *node)
|
||||||
{
|
{
|
||||||
struct resource *res;
|
void __iomem *base;
|
||||||
int irq;
|
int irq;
|
||||||
int ret;
|
struct clk *clk;
|
||||||
|
|
||||||
p->pdev = pdev;
|
clk = of_clk_get(node, 0);
|
||||||
|
if (IS_ERR(clk)) {
|
||||||
res = platform_get_resource(p->pdev, IORESOURCE_MEM, 0);
|
pr_err("failed to get clock for clockevent\n");
|
||||||
if (!res) {
|
return;
|
||||||
dev_err(&p->pdev->dev, "failed to get I/O memory\n");
|
|
||||||
return -ENXIO;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
irq = platform_get_irq(p->pdev, 0);
|
base = of_iomap(node, 0);
|
||||||
if (irq < 0) {
|
if (!base) {
|
||||||
dev_err(&p->pdev->dev, "failed to get irq\n");
|
pr_err("failed to map registers for clockevent\n");
|
||||||
return -ENXIO;
|
goto free_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
p->mapbase = res->start;
|
irq = irq_of_parse_and_map(node, 0);
|
||||||
|
if (!irq) {
|
||||||
p->irqaction.name = dev_name(&p->pdev->dev);
|
pr_err("failed to get irq for clockevent\n");
|
||||||
p->irqaction.handler = timer8_interrupt;
|
goto unmap_reg;
|
||||||
p->irqaction.dev_id = p;
|
|
||||||
p->irqaction.flags = IRQF_TIMER;
|
|
||||||
|
|
||||||
p->pclk = clk_get(&p->pdev->dev, "fck");
|
|
||||||
if (IS_ERR(p->pclk)) {
|
|
||||||
dev_err(&p->pdev->dev, "can't get clk\n");
|
|
||||||
return PTR_ERR(p->pclk);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p->ced.name = pdev->name;
|
timer8_priv.mapbase = base;
|
||||||
p->ced.features = CLOCK_EVT_FEAT_PERIODIC |
|
|
||||||
CLOCK_EVT_FEAT_ONESHOT;
|
|
||||||
p->ced.rating = 200;
|
|
||||||
p->ced.cpumask = cpumask_of(0);
|
|
||||||
p->ced.set_next_event = timer8_clock_event_next;
|
|
||||||
p->ced.set_state_shutdown = timer8_clock_event_shutdown;
|
|
||||||
p->ced.set_state_periodic = timer8_clock_event_periodic;
|
|
||||||
p->ced.set_state_oneshot = timer8_clock_event_oneshot;
|
|
||||||
|
|
||||||
ret = setup_irq(irq, &p->irqaction);
|
timer8_priv.rate = clk_get_rate(clk) / SCALE;
|
||||||
if (ret < 0) {
|
if (!timer8_priv.rate) {
|
||||||
dev_err(&p->pdev->dev,
|
pr_err("Failed to get rate for the clocksource\n");
|
||||||
"failed to request irq %d\n", irq);
|
goto unmap_reg;
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
clockevents_register_device(&p->ced);
|
|
||||||
platform_set_drvdata(pdev, p);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int timer8_probe(struct platform_device *pdev)
|
if (request_irq(irq, timer8_interrupt, IRQF_TIMER,
|
||||||
{
|
timer8_priv.ced.name, &timer8_priv) < 0) {
|
||||||
struct timer8_priv *p = platform_get_drvdata(pdev);
|
pr_err("failed to request irq %d for clockevent\n", irq);
|
||||||
|
goto unmap_reg;
|
||||||
if (p) {
|
|
||||||
dev_info(&pdev->dev, "kept as earlytimer\n");
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
|
clockevents_config_and_register(&timer8_priv.ced,
|
||||||
if (!p)
|
timer8_priv.rate, 1, 0x0000ffff);
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
return timer8_setup(p, pdev);
|
return;
|
||||||
|
unmap_reg:
|
||||||
|
iounmap(base);
|
||||||
|
free_clk:
|
||||||
|
clk_put(clk);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int timer8_remove(struct platform_device *pdev)
|
CLOCKSOURCE_OF_DECLARE(h8300_8bit, "renesas,8bit-timer", h8300_8timer_init);
|
||||||
{
|
|
||||||
return -EBUSY;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct of_device_id timer8_of_table[] __maybe_unused = {
|
|
||||||
{ .compatible = "renesas,8bit-timer" },
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
|
|
||||||
MODULE_DEVICE_TABLE(of, timer8_of_table);
|
|
||||||
static struct platform_driver timer8_driver = {
|
|
||||||
.probe = timer8_probe,
|
|
||||||
.remove = timer8_remove,
|
|
||||||
.driver = {
|
|
||||||
.name = "h8300-8timer",
|
|
||||||
.of_match_table = of_match_ptr(timer8_of_table),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static int __init timer8_init(void)
|
|
||||||
{
|
|
||||||
return platform_driver_register(&timer8_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit timer8_exit(void)
|
|
||||||
{
|
|
||||||
platform_driver_unregister(&timer8_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
subsys_initcall(timer8_init);
|
|
||||||
module_exit(timer8_exit);
|
|
||||||
MODULE_AUTHOR("Yoshinori Sato");
|
|
||||||
MODULE_DESCRIPTION("H8/300 8bit Timer Driver");
|
|
||||||
MODULE_LICENSE("GPL v2");
|
|
||||||
|
@ -1,42 +1,30 @@
|
|||||||
/*
|
/*
|
||||||
* H8/300 TPU Driver
|
* H8S TPU Driver
|
||||||
*
|
*
|
||||||
* Copyright 2015 Yoshinori Sato <ysato@users.sourcefoge.jp>
|
* Copyright 2015 Yoshinori Sato <ysato@users.sourcefoge.jp>
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/interrupt.h>
|
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/platform_device.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/clocksource.h>
|
#include <linux/clocksource.h>
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
|
#include <linux/of_irq.h>
|
||||||
|
|
||||||
#include <asm/irq.h>
|
#define TCR 0x0
|
||||||
|
#define TSR 0x5
|
||||||
|
#define TCNT 0x6
|
||||||
|
|
||||||
#define TCR 0
|
#define TCFV 0x10
|
||||||
#define TMDR 1
|
|
||||||
#define TIOR 2
|
|
||||||
#define TER 4
|
|
||||||
#define TSR 5
|
|
||||||
#define TCNT 6
|
|
||||||
#define TGRA 8
|
|
||||||
#define TGRB 10
|
|
||||||
#define TGRC 12
|
|
||||||
#define TGRD 14
|
|
||||||
|
|
||||||
struct tpu_priv {
|
struct tpu_priv {
|
||||||
struct platform_device *pdev;
|
|
||||||
struct clocksource cs;
|
struct clocksource cs;
|
||||||
struct clk *clk;
|
void __iomem *mapbase1;
|
||||||
unsigned long mapbase1;
|
void __iomem *mapbase2;
|
||||||
unsigned long mapbase2;
|
|
||||||
raw_spinlock_t lock;
|
raw_spinlock_t lock;
|
||||||
unsigned int cs_enabled;
|
unsigned int cs_enabled;
|
||||||
};
|
};
|
||||||
@ -45,8 +33,8 @@ static inline unsigned long read_tcnt32(struct tpu_priv *p)
|
|||||||
{
|
{
|
||||||
unsigned long tcnt;
|
unsigned long tcnt;
|
||||||
|
|
||||||
tcnt = ctrl_inw(p->mapbase1 + TCNT) << 16;
|
tcnt = ioread16be(p->mapbase1 + TCNT) << 16;
|
||||||
tcnt |= ctrl_inw(p->mapbase2 + TCNT);
|
tcnt |= ioread16be(p->mapbase2 + TCNT);
|
||||||
return tcnt;
|
return tcnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +43,7 @@ static int tpu_get_counter(struct tpu_priv *p, unsigned long long *val)
|
|||||||
unsigned long v1, v2, v3;
|
unsigned long v1, v2, v3;
|
||||||
int o1, o2;
|
int o1, o2;
|
||||||
|
|
||||||
o1 = ctrl_inb(p->mapbase1 + TSR) & 0x10;
|
o1 = ioread8(p->mapbase1 + TSR) & TCFV;
|
||||||
|
|
||||||
/* Make sure the timer value is stable. Stolen from acpi_pm.c */
|
/* Make sure the timer value is stable. Stolen from acpi_pm.c */
|
||||||
do {
|
do {
|
||||||
@ -63,7 +51,7 @@ static int tpu_get_counter(struct tpu_priv *p, unsigned long long *val)
|
|||||||
v1 = read_tcnt32(p);
|
v1 = read_tcnt32(p);
|
||||||
v2 = read_tcnt32(p);
|
v2 = read_tcnt32(p);
|
||||||
v3 = read_tcnt32(p);
|
v3 = read_tcnt32(p);
|
||||||
o1 = ctrl_inb(p->mapbase1 + TSR) & 0x10;
|
o1 = ioread8(p->mapbase1 + TSR) & TCFV;
|
||||||
} while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3)
|
} while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3)
|
||||||
|| (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2)));
|
|| (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2)));
|
||||||
|
|
||||||
@ -96,10 +84,10 @@ static int tpu_clocksource_enable(struct clocksource *cs)
|
|||||||
|
|
||||||
WARN_ON(p->cs_enabled);
|
WARN_ON(p->cs_enabled);
|
||||||
|
|
||||||
ctrl_outw(0, p->mapbase1 + TCNT);
|
iowrite16be(0, p->mapbase1 + TCNT);
|
||||||
ctrl_outw(0, p->mapbase2 + TCNT);
|
iowrite16be(0, p->mapbase2 + TCNT);
|
||||||
ctrl_outb(0x0f, p->mapbase1 + TCR);
|
iowrite8(0x0f, p->mapbase1 + TCR);
|
||||||
ctrl_outb(0x03, p->mapbase2 + TCR);
|
iowrite8(0x03, p->mapbase2 + TCR);
|
||||||
|
|
||||||
p->cs_enabled = true;
|
p->cs_enabled = true;
|
||||||
return 0;
|
return 0;
|
||||||
@ -111,96 +99,59 @@ static void tpu_clocksource_disable(struct clocksource *cs)
|
|||||||
|
|
||||||
WARN_ON(!p->cs_enabled);
|
WARN_ON(!p->cs_enabled);
|
||||||
|
|
||||||
ctrl_outb(0, p->mapbase1 + TCR);
|
iowrite8(0, p->mapbase1 + TCR);
|
||||||
ctrl_outb(0, p->mapbase2 + TCR);
|
iowrite8(0, p->mapbase2 + TCR);
|
||||||
p->cs_enabled = false;
|
p->cs_enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct tpu_priv tpu_priv = {
|
||||||
|
.cs = {
|
||||||
|
.name = "H8S_TPU",
|
||||||
|
.rating = 200,
|
||||||
|
.read = tpu_clocksource_read,
|
||||||
|
.enable = tpu_clocksource_enable,
|
||||||
|
.disable = tpu_clocksource_disable,
|
||||||
|
.mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8),
|
||||||
|
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
#define CH_L 0
|
#define CH_L 0
|
||||||
#define CH_H 1
|
#define CH_H 1
|
||||||
|
|
||||||
static int __init tpu_setup(struct tpu_priv *p, struct platform_device *pdev)
|
static void __init h8300_tpu_init(struct device_node *node)
|
||||||
{
|
{
|
||||||
struct resource *res[2];
|
void __iomem *base[2];
|
||||||
|
struct clk *clk;
|
||||||
|
|
||||||
p->pdev = pdev;
|
clk = of_clk_get(node, 0);
|
||||||
|
if (IS_ERR(clk)) {
|
||||||
res[CH_L] = platform_get_resource(p->pdev, IORESOURCE_MEM, CH_L);
|
pr_err("failed to get clock for clocksource\n");
|
||||||
res[CH_H] = platform_get_resource(p->pdev, IORESOURCE_MEM, CH_H);
|
return;
|
||||||
if (!res[CH_L] || !res[CH_H]) {
|
|
||||||
dev_err(&p->pdev->dev, "failed to get I/O memory\n");
|
|
||||||
return -ENXIO;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p->clk = clk_get(&p->pdev->dev, "fck");
|
base[CH_L] = of_iomap(node, CH_L);
|
||||||
if (IS_ERR(p->clk)) {
|
if (!base[CH_L]) {
|
||||||
dev_err(&p->pdev->dev, "can't get clk\n");
|
pr_err("failed to map registers for clocksource\n");
|
||||||
return PTR_ERR(p->clk);
|
goto free_clk;
|
||||||
|
}
|
||||||
|
base[CH_H] = of_iomap(node, CH_H);
|
||||||
|
if (!base[CH_H]) {
|
||||||
|
pr_err("failed to map registers for clocksource\n");
|
||||||
|
goto unmap_L;
|
||||||
}
|
}
|
||||||
|
|
||||||
p->mapbase1 = res[CH_L]->start;
|
tpu_priv.mapbase1 = base[CH_L];
|
||||||
p->mapbase2 = res[CH_H]->start;
|
tpu_priv.mapbase2 = base[CH_H];
|
||||||
|
|
||||||
p->cs.name = pdev->name;
|
clocksource_register_hz(&tpu_priv.cs, clk_get_rate(clk) / 64);
|
||||||
p->cs.rating = 200;
|
|
||||||
p->cs.read = tpu_clocksource_read;
|
|
||||||
p->cs.enable = tpu_clocksource_enable;
|
|
||||||
p->cs.disable = tpu_clocksource_disable;
|
|
||||||
p->cs.mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8);
|
|
||||||
p->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
|
|
||||||
clocksource_register_hz(&p->cs, clk_get_rate(p->clk) / 64);
|
|
||||||
platform_set_drvdata(pdev, p);
|
|
||||||
|
|
||||||
return 0;
|
return;
|
||||||
|
|
||||||
|
unmap_L:
|
||||||
|
iounmap(base[CH_H]);
|
||||||
|
free_clk:
|
||||||
|
clk_put(clk);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tpu_probe(struct platform_device *pdev)
|
CLOCKSOURCE_OF_DECLARE(h8300_tpu, "renesas,tpu", h8300_tpu_init);
|
||||||
{
|
|
||||||
struct tpu_priv *p = platform_get_drvdata(pdev);
|
|
||||||
|
|
||||||
if (p) {
|
|
||||||
dev_info(&pdev->dev, "kept as earlytimer\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
|
|
||||||
if (!p)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
return tpu_setup(p, pdev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int tpu_remove(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
return -EBUSY;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct of_device_id tpu_of_table[] = {
|
|
||||||
{ .compatible = "renesas,tpu" },
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct platform_driver tpu_driver = {
|
|
||||||
.probe = tpu_probe,
|
|
||||||
.remove = tpu_remove,
|
|
||||||
.driver = {
|
|
||||||
.name = "h8s-tpu",
|
|
||||||
.of_match_table = of_match_ptr(tpu_of_table),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static int __init tpu_init(void)
|
|
||||||
{
|
|
||||||
return platform_driver_register(&tpu_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit tpu_exit(void)
|
|
||||||
{
|
|
||||||
platform_driver_unregister(&tpu_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
subsys_initcall(tpu_init);
|
|
||||||
module_exit(tpu_exit);
|
|
||||||
MODULE_AUTHOR("Yoshinori Sato");
|
|
||||||
MODULE_DESCRIPTION("H8S Timer Pulse Unit Driver");
|
|
||||||
MODULE_LICENSE("GPL v2");
|
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/clockchips.h>
|
#include <linux/clockchips.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
@ -187,10 +189,8 @@ static void __init mtk_timer_init(struct device_node *node)
|
|||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
|
|
||||||
evt = kzalloc(sizeof(*evt), GFP_KERNEL);
|
evt = kzalloc(sizeof(*evt), GFP_KERNEL);
|
||||||
if (!evt) {
|
if (!evt)
|
||||||
pr_warn("Can't allocate mtk clock event driver struct");
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
evt->dev.name = "mtk_tick";
|
evt->dev.name = "mtk_tick";
|
||||||
evt->dev.rating = 300;
|
evt->dev.rating = 300;
|
||||||
@ -204,31 +204,31 @@ static void __init mtk_timer_init(struct device_node *node)
|
|||||||
|
|
||||||
evt->gpt_base = of_io_request_and_map(node, 0, "mtk-timer");
|
evt->gpt_base = of_io_request_and_map(node, 0, "mtk-timer");
|
||||||
if (IS_ERR(evt->gpt_base)) {
|
if (IS_ERR(evt->gpt_base)) {
|
||||||
pr_warn("Can't get resource\n");
|
pr_err("Can't get resource\n");
|
||||||
return;
|
goto err_kzalloc;
|
||||||
}
|
}
|
||||||
|
|
||||||
evt->dev.irq = irq_of_parse_and_map(node, 0);
|
evt->dev.irq = irq_of_parse_and_map(node, 0);
|
||||||
if (evt->dev.irq <= 0) {
|
if (evt->dev.irq <= 0) {
|
||||||
pr_warn("Can't parse IRQ");
|
pr_err("Can't parse IRQ\n");
|
||||||
goto err_mem;
|
goto err_mem;
|
||||||
}
|
}
|
||||||
|
|
||||||
clk = of_clk_get(node, 0);
|
clk = of_clk_get(node, 0);
|
||||||
if (IS_ERR(clk)) {
|
if (IS_ERR(clk)) {
|
||||||
pr_warn("Can't get timer clock");
|
pr_err("Can't get timer clock\n");
|
||||||
goto err_irq;
|
goto err_irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clk_prepare_enable(clk)) {
|
if (clk_prepare_enable(clk)) {
|
||||||
pr_warn("Can't prepare clock");
|
pr_err("Can't prepare clock\n");
|
||||||
goto err_clk_put;
|
goto err_clk_put;
|
||||||
}
|
}
|
||||||
rate = clk_get_rate(clk);
|
rate = clk_get_rate(clk);
|
||||||
|
|
||||||
if (request_irq(evt->dev.irq, mtk_timer_interrupt,
|
if (request_irq(evt->dev.irq, mtk_timer_interrupt,
|
||||||
IRQF_TIMER | IRQF_IRQPOLL, "mtk_timer", evt)) {
|
IRQF_TIMER | IRQF_IRQPOLL, "mtk_timer", evt)) {
|
||||||
pr_warn("failed to setup irq %d\n", evt->dev.irq);
|
pr_err("failed to setup irq %d\n", evt->dev.irq);
|
||||||
goto err_clk_disable;
|
goto err_clk_disable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,5 +260,7 @@ err_mem:
|
|||||||
iounmap(evt->gpt_base);
|
iounmap(evt->gpt_base);
|
||||||
of_address_to_resource(node, 0, &res);
|
of_address_to_resource(node, 0, &res);
|
||||||
release_mem_region(res.start, resource_size(&res));
|
release_mem_region(res.start, resource_size(&res));
|
||||||
|
err_kzalloc:
|
||||||
|
kfree(evt);
|
||||||
}
|
}
|
||||||
CLOCKSOURCE_OF_DECLARE(mtk_mt6577, "mediatek,mt6577-timer", mtk_timer_init);
|
CLOCKSOURCE_OF_DECLARE(mtk_mt6577, "mediatek,mt6577-timer", mtk_timer_init);
|
||||||
|
@ -49,14 +49,12 @@ static inline void __iomem *rk_base(struct clock_event_device *ce)
|
|||||||
static inline void rk_timer_disable(struct clock_event_device *ce)
|
static inline void rk_timer_disable(struct clock_event_device *ce)
|
||||||
{
|
{
|
||||||
writel_relaxed(TIMER_DISABLE, rk_base(ce) + TIMER_CONTROL_REG);
|
writel_relaxed(TIMER_DISABLE, rk_base(ce) + TIMER_CONTROL_REG);
|
||||||
dsb();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void rk_timer_enable(struct clock_event_device *ce, u32 flags)
|
static inline void rk_timer_enable(struct clock_event_device *ce, u32 flags)
|
||||||
{
|
{
|
||||||
writel_relaxed(TIMER_ENABLE | TIMER_INT_UNMASK | flags,
|
writel_relaxed(TIMER_ENABLE | TIMER_INT_UNMASK | flags,
|
||||||
rk_base(ce) + TIMER_CONTROL_REG);
|
rk_base(ce) + TIMER_CONTROL_REG);
|
||||||
dsb();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rk_timer_update_counter(unsigned long cycles,
|
static void rk_timer_update_counter(unsigned long cycles,
|
||||||
@ -64,13 +62,11 @@ static void rk_timer_update_counter(unsigned long cycles,
|
|||||||
{
|
{
|
||||||
writel_relaxed(cycles, rk_base(ce) + TIMER_LOAD_COUNT0);
|
writel_relaxed(cycles, rk_base(ce) + TIMER_LOAD_COUNT0);
|
||||||
writel_relaxed(0, rk_base(ce) + TIMER_LOAD_COUNT1);
|
writel_relaxed(0, rk_base(ce) + TIMER_LOAD_COUNT1);
|
||||||
dsb();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rk_timer_interrupt_clear(struct clock_event_device *ce)
|
static void rk_timer_interrupt_clear(struct clock_event_device *ce)
|
||||||
{
|
{
|
||||||
writel_relaxed(1, rk_base(ce) + TIMER_INT_STATUS);
|
writel_relaxed(1, rk_base(ce) + TIMER_INT_STATUS);
|
||||||
dsb();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int rk_timer_set_next_event(unsigned long cycles,
|
static inline int rk_timer_set_next_event(unsigned long cycles,
|
||||||
@ -173,4 +169,5 @@ static void __init rk_timer_init(struct device_node *np)
|
|||||||
|
|
||||||
clockevents_config_and_register(ce, bc_timer.freq, 1, UINT_MAX);
|
clockevents_config_and_register(ce, bc_timer.freq, 1, UINT_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
CLOCKSOURCE_OF_DECLARE(rk_timer, "rockchip,rk3288-timer", rk_timer_init);
|
CLOCKSOURCE_OF_DECLARE(rk_timer, "rockchip,rk3288-timer", rk_timer_init);
|
||||||
|
@ -19,19 +19,6 @@ static u64 notrace read_sched_clock(void)
|
|||||||
return read_xtal_counter();
|
return read_xtal_counter();
|
||||||
}
|
}
|
||||||
|
|
||||||
static cycle_t read_clocksource(struct clocksource *cs)
|
|
||||||
{
|
|
||||||
return read_xtal_counter();
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct clocksource tango_xtal = {
|
|
||||||
.name = "tango-xtal",
|
|
||||||
.rating = 350,
|
|
||||||
.read = read_clocksource,
|
|
||||||
.mask = CLOCKSOURCE_MASK(32),
|
|
||||||
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void __init tango_clocksource_init(struct device_node *np)
|
static void __init tango_clocksource_init(struct device_node *np)
|
||||||
{
|
{
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
@ -53,8 +40,9 @@ static void __init tango_clocksource_init(struct device_node *np)
|
|||||||
delay_timer.freq = xtal_freq;
|
delay_timer.freq = xtal_freq;
|
||||||
delay_timer.read_current_timer = read_xtal_counter;
|
delay_timer.read_current_timer = read_xtal_counter;
|
||||||
|
|
||||||
ret = clocksource_register_hz(&tango_xtal, xtal_freq);
|
ret = clocksource_mmio_init(xtal_in_cnt, "tango-xtal", xtal_freq, 350,
|
||||||
if (ret != 0) {
|
32, clocksource_mmio_readl_up);
|
||||||
|
if (!ret) {
|
||||||
pr_err("%s: registration failed\n", np->full_name);
|
pr_err("%s: registration failed\n", np->full_name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,8 @@ static struct clock_event_device tegra_clockevent = {
|
|||||||
.name = "timer0",
|
.name = "timer0",
|
||||||
.rating = 300,
|
.rating = 300,
|
||||||
.features = CLOCK_EVT_FEAT_ONESHOT |
|
.features = CLOCK_EVT_FEAT_ONESHOT |
|
||||||
CLOCK_EVT_FEAT_PERIODIC,
|
CLOCK_EVT_FEAT_PERIODIC |
|
||||||
|
CLOCK_EVT_FEAT_DYNIRQ,
|
||||||
.set_next_event = tegra_timer_set_next_event,
|
.set_next_event = tegra_timer_set_next_event,
|
||||||
.set_state_shutdown = tegra_timer_shutdown,
|
.set_state_shutdown = tegra_timer_shutdown,
|
||||||
.set_state_periodic = tegra_timer_set_periodic,
|
.set_state_periodic = tegra_timer_set_periodic,
|
||||||
|
@ -125,7 +125,7 @@ static int __init lpc32xx_clocksource_init(struct device_node *np)
|
|||||||
|
|
||||||
clk = of_clk_get_by_name(np, "timerclk");
|
clk = of_clk_get_by_name(np, "timerclk");
|
||||||
if (IS_ERR(clk)) {
|
if (IS_ERR(clk)) {
|
||||||
pr_err("clock get failed (%lu)\n", PTR_ERR(clk));
|
pr_err("clock get failed (%ld)\n", PTR_ERR(clk));
|
||||||
return PTR_ERR(clk);
|
return PTR_ERR(clk);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,7 +184,7 @@ static int __init lpc32xx_clockevent_init(struct device_node *np)
|
|||||||
|
|
||||||
clk = of_clk_get_by_name(np, "timerclk");
|
clk = of_clk_get_by_name(np, "timerclk");
|
||||||
if (IS_ERR(clk)) {
|
if (IS_ERR(clk)) {
|
||||||
pr_err("clock get failed (%lu)\n", PTR_ERR(clk));
|
pr_err("clock get failed (%ld)\n", PTR_ERR(clk));
|
||||||
return PTR_ERR(clk);
|
return PTR_ERR(clk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ pistachio_clocksource_read_cycles(struct clocksource *cs)
|
|||||||
counter = gpt_readl(pcs->base, TIMER_CURRENT_VALUE, 0);
|
counter = gpt_readl(pcs->base, TIMER_CURRENT_VALUE, 0);
|
||||||
raw_spin_unlock_irqrestore(&pcs->lock, flags);
|
raw_spin_unlock_irqrestore(&pcs->lock, flags);
|
||||||
|
|
||||||
return ~(cycle_t)counter;
|
return (cycle_t)~counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u64 notrace pistachio_read_sched_clock(void)
|
static u64 notrace pistachio_read_sched_clock(void)
|
||||||
|
@ -152,13 +152,6 @@ static irqreturn_t sun5i_timer_interrupt(int irq, void *dev_id)
|
|||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static cycle_t sun5i_clksrc_read(struct clocksource *clksrc)
|
|
||||||
{
|
|
||||||
struct sun5i_timer_clksrc *cs = to_sun5i_timer_clksrc(clksrc);
|
|
||||||
|
|
||||||
return ~readl(cs->timer.base + TIMER_CNTVAL_LO_REG(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sun5i_rate_cb_clksrc(struct notifier_block *nb,
|
static int sun5i_rate_cb_clksrc(struct notifier_block *nb,
|
||||||
unsigned long event, void *data)
|
unsigned long event, void *data)
|
||||||
{
|
{
|
||||||
@ -217,13 +210,8 @@ static int __init sun5i_setup_clocksource(struct device_node *node,
|
|||||||
writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD,
|
writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD,
|
||||||
base + TIMER_CTL_REG(1));
|
base + TIMER_CTL_REG(1));
|
||||||
|
|
||||||
cs->clksrc.name = node->name;
|
ret = clocksource_mmio_init(base + TIMER_CNTVAL_LO_REG(1), node->name,
|
||||||
cs->clksrc.rating = 340;
|
rate, 340, 32, clocksource_mmio_readl_down);
|
||||||
cs->clksrc.read = sun5i_clksrc_read;
|
|
||||||
cs->clksrc.mask = CLOCKSOURCE_MASK(32);
|
|
||||||
cs->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS;
|
|
||||||
|
|
||||||
ret = clocksource_register_hz(&cs->clksrc, rate);
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_err("Couldn't register clock source.\n");
|
pr_err("Couldn't register clock source.\n");
|
||||||
goto err_remove_notifier;
|
goto err_remove_notifier;
|
||||||
|
@ -30,7 +30,6 @@
|
|||||||
#include <linux/clocksource.h>
|
#include <linux/clocksource.h>
|
||||||
#include <linux/clockchips.h>
|
#include <linux/clockchips.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <asm/mach/time.h>
|
|
||||||
|
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
|
@ -21,9 +21,9 @@ static const char ipr_bit[] = {
|
|||||||
10, 10, 10, 10, 9, 9, 9, 9,
|
10, 10, 10, 10, 9, 9, 9, 9,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void *intc_baseaddr;
|
static void __iomem *intc_baseaddr;
|
||||||
|
|
||||||
#define IPR ((unsigned long)intc_baseaddr + 6)
|
#define IPR (intc_baseaddr + 6)
|
||||||
|
|
||||||
static void h8300h_disable_irq(struct irq_data *data)
|
static void h8300h_disable_irq(struct irq_data *data)
|
||||||
{
|
{
|
||||||
@ -81,8 +81,8 @@ static int __init h8300h_intc_of_init(struct device_node *intc,
|
|||||||
BUG_ON(!intc_baseaddr);
|
BUG_ON(!intc_baseaddr);
|
||||||
|
|
||||||
/* All interrupt priority low */
|
/* All interrupt priority low */
|
||||||
ctrl_outb(0x00, IPR + 0);
|
writeb(0x00, IPR + 0);
|
||||||
ctrl_outb(0x00, IPR + 1);
|
writeb(0x00, IPR + 1);
|
||||||
|
|
||||||
domain = irq_domain_add_linear(intc, NR_IRQS, &irq_ops, NULL);
|
domain = irq_domain_add_linear(intc, NR_IRQS, &irq_ops, NULL);
|
||||||
BUG_ON(!domain);
|
BUG_ON(!domain);
|
||||||
|
@ -62,12 +62,18 @@ struct module;
|
|||||||
* @suspend: suspend function for the clocksource, if necessary
|
* @suspend: suspend function for the clocksource, if necessary
|
||||||
* @resume: resume function for the clocksource, if necessary
|
* @resume: resume function for the clocksource, if necessary
|
||||||
* @owner: module reference, must be set by clocksource in modules
|
* @owner: module reference, must be set by clocksource in modules
|
||||||
|
*
|
||||||
|
* Note: This struct is not used in hotpathes of the timekeeping code
|
||||||
|
* because the timekeeper caches the hot path fields in its own data
|
||||||
|
* structure, so no line cache alignment is required,
|
||||||
|
*
|
||||||
|
* The pointer to the clocksource itself is handed to the read
|
||||||
|
* callback. If you need extra information there you can wrap struct
|
||||||
|
* clocksource into your own struct. Depending on the amount of
|
||||||
|
* information you need you should consider to cache line align that
|
||||||
|
* structure.
|
||||||
*/
|
*/
|
||||||
struct clocksource {
|
struct clocksource {
|
||||||
/*
|
|
||||||
* Hotpath data, fits in a single cache line when the
|
|
||||||
* clocksource itself is cacheline aligned.
|
|
||||||
*/
|
|
||||||
cycle_t (*read)(struct clocksource *cs);
|
cycle_t (*read)(struct clocksource *cs);
|
||||||
cycle_t mask;
|
cycle_t mask;
|
||||||
u32 mult;
|
u32 mult;
|
||||||
@ -95,7 +101,7 @@ struct clocksource {
|
|||||||
cycle_t wd_last;
|
cycle_t wd_last;
|
||||||
#endif
|
#endif
|
||||||
struct module *owner;
|
struct module *owner;
|
||||||
} ____cacheline_aligned;
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Clock source flags bits::
|
* Clock source flags bits::
|
||||||
|
@ -10,11 +10,17 @@
|
|||||||
|
|
||||||
#ifdef CONFIG_GENERIC_SCHED_CLOCK
|
#ifdef CONFIG_GENERIC_SCHED_CLOCK
|
||||||
extern void sched_clock_postinit(void);
|
extern void sched_clock_postinit(void);
|
||||||
#else
|
|
||||||
static inline void sched_clock_postinit(void) { }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern void sched_clock_register(u64 (*read)(void), int bits,
|
extern void sched_clock_register(u64 (*read)(void), int bits,
|
||||||
unsigned long rate);
|
unsigned long rate);
|
||||||
|
#else
|
||||||
|
static inline void sched_clock_postinit(void) { }
|
||||||
|
|
||||||
|
static inline void sched_clock_register(u64 (*read)(void), int bits,
|
||||||
|
unsigned long rate)
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -125,6 +125,32 @@ static inline bool timeval_valid(const struct timeval *tv)
|
|||||||
|
|
||||||
extern struct timespec timespec_trunc(struct timespec t, unsigned gran);
|
extern struct timespec timespec_trunc(struct timespec t, unsigned gran);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Validates if a timespec/timeval used to inject a time offset is valid.
|
||||||
|
* Offsets can be postive or negative. The value of the timeval/timespec
|
||||||
|
* is the sum of its fields, but *NOTE*: the field tv_usec/tv_nsec must
|
||||||
|
* always be non-negative.
|
||||||
|
*/
|
||||||
|
static inline bool timeval_inject_offset_valid(const struct timeval *tv)
|
||||||
|
{
|
||||||
|
/* We don't check the tv_sec as it can be positive or negative */
|
||||||
|
|
||||||
|
/* Can't have more microseconds then a second */
|
||||||
|
if (tv->tv_usec < 0 || tv->tv_usec >= USEC_PER_SEC)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool timespec_inject_offset_valid(const struct timespec *ts)
|
||||||
|
{
|
||||||
|
/* We don't check the tv_sec as it can be positive or negative */
|
||||||
|
|
||||||
|
/* Can't have more nanoseconds then a second */
|
||||||
|
if (ts->tv_nsec < 0 || ts->tv_nsec >= NSEC_PER_SEC)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
#define CURRENT_TIME (current_kernel_time())
|
#define CURRENT_TIME (current_kernel_time())
|
||||||
#define CURRENT_TIME_SEC ((struct timespec) { get_seconds(), 0 })
|
#define CURRENT_TIME_SEC ((struct timespec) { get_seconds(), 0 })
|
||||||
|
|
||||||
|
@ -271,11 +271,27 @@ static int alarmtimer_suspend(struct device *dev)
|
|||||||
__pm_wakeup_event(ws, MSEC_PER_SEC);
|
__pm_wakeup_event(ws, MSEC_PER_SEC);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int alarmtimer_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct rtc_device *rtc;
|
||||||
|
|
||||||
|
rtc = alarmtimer_get_rtcdev();
|
||||||
|
if (rtc)
|
||||||
|
rtc_timer_cancel(rtc, &rtctimer);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
static int alarmtimer_suspend(struct device *dev)
|
static int alarmtimer_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int alarmtimer_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void alarmtimer_freezerset(ktime_t absexp, enum alarmtimer_type type)
|
static void alarmtimer_freezerset(ktime_t absexp, enum alarmtimer_type type)
|
||||||
@ -800,6 +816,7 @@ out:
|
|||||||
/* Suspend hook structures */
|
/* Suspend hook structures */
|
||||||
static const struct dev_pm_ops alarmtimer_pm_ops = {
|
static const struct dev_pm_ops alarmtimer_pm_ops = {
|
||||||
.suspend = alarmtimer_suspend,
|
.suspend = alarmtimer_suspend,
|
||||||
|
.resume = alarmtimer_resume,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct platform_driver alarmtimer_driver = {
|
static struct platform_driver alarmtimer_driver = {
|
||||||
|
@ -218,8 +218,8 @@ static void clocksource_watchdog(unsigned long data)
|
|||||||
|
|
||||||
/* Check the deviation from the watchdog clocksource. */
|
/* Check the deviation from the watchdog clocksource. */
|
||||||
if (abs(cs_nsec - wd_nsec) > WATCHDOG_THRESHOLD) {
|
if (abs(cs_nsec - wd_nsec) > WATCHDOG_THRESHOLD) {
|
||||||
pr_warn("timekeeping watchdog: Marking clocksource '%s' as unstable because the skew is too large:\n",
|
pr_warn("timekeeping watchdog on CPU%d: Marking clocksource '%s' as unstable because the skew is too large:\n",
|
||||||
cs->name);
|
smp_processor_id(), cs->name);
|
||||||
pr_warn(" '%s' wd_now: %llx wd_last: %llx mask: %llx\n",
|
pr_warn(" '%s' wd_now: %llx wd_last: %llx mask: %llx\n",
|
||||||
watchdog->name, wdnow, wdlast, watchdog->mask);
|
watchdog->name, wdnow, wdlast, watchdog->mask);
|
||||||
pr_warn(" '%s' cs_now: %llx cs_last: %llx mask: %llx\n",
|
pr_warn(" '%s' cs_now: %llx cs_last: %llx mask: %llx\n",
|
||||||
|
@ -16,8 +16,11 @@
|
|||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/rtc.h>
|
#include <linux/rtc.h>
|
||||||
|
#include <linux/math64.h>
|
||||||
|
|
||||||
#include "ntp_internal.h"
|
#include "ntp_internal.h"
|
||||||
|
#include "timekeeping_internal.h"
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NTP timekeeping variables:
|
* NTP timekeeping variables:
|
||||||
@ -70,7 +73,7 @@ static long time_esterror = NTP_PHASE_LIMIT;
|
|||||||
static s64 time_freq;
|
static s64 time_freq;
|
||||||
|
|
||||||
/* time at last adjustment (secs): */
|
/* time at last adjustment (secs): */
|
||||||
static long time_reftime;
|
static time64_t time_reftime;
|
||||||
|
|
||||||
static long time_adjust;
|
static long time_adjust;
|
||||||
|
|
||||||
@ -297,25 +300,27 @@ static void ntp_update_offset(long offset)
|
|||||||
if (!(time_status & STA_PLL))
|
if (!(time_status & STA_PLL))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!(time_status & STA_NANO))
|
if (!(time_status & STA_NANO)) {
|
||||||
|
/* Make sure the multiplication below won't overflow */
|
||||||
|
offset = clamp(offset, -USEC_PER_SEC, USEC_PER_SEC);
|
||||||
offset *= NSEC_PER_USEC;
|
offset *= NSEC_PER_USEC;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Scale the phase adjustment and
|
* Scale the phase adjustment and
|
||||||
* clamp to the operating range.
|
* clamp to the operating range.
|
||||||
*/
|
*/
|
||||||
offset = min(offset, MAXPHASE);
|
offset = clamp(offset, -MAXPHASE, MAXPHASE);
|
||||||
offset = max(offset, -MAXPHASE);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Select how the frequency is to be controlled
|
* Select how the frequency is to be controlled
|
||||||
* and in which mode (PLL or FLL).
|
* and in which mode (PLL or FLL).
|
||||||
*/
|
*/
|
||||||
secs = get_seconds() - time_reftime;
|
secs = (long)(__ktime_get_real_seconds() - time_reftime);
|
||||||
if (unlikely(time_status & STA_FREQHOLD))
|
if (unlikely(time_status & STA_FREQHOLD))
|
||||||
secs = 0;
|
secs = 0;
|
||||||
|
|
||||||
time_reftime = get_seconds();
|
time_reftime = __ktime_get_real_seconds();
|
||||||
|
|
||||||
offset64 = offset;
|
offset64 = offset;
|
||||||
freq_adj = ntp_update_offset_fll(offset64, secs);
|
freq_adj = ntp_update_offset_fll(offset64, secs);
|
||||||
@ -390,10 +395,11 @@ ktime_t ntp_get_next_leap(void)
|
|||||||
*
|
*
|
||||||
* Also handles leap second processing, and returns leap offset
|
* Also handles leap second processing, and returns leap offset
|
||||||
*/
|
*/
|
||||||
int second_overflow(unsigned long secs)
|
int second_overflow(time64_t secs)
|
||||||
{
|
{
|
||||||
s64 delta;
|
s64 delta;
|
||||||
int leap = 0;
|
int leap = 0;
|
||||||
|
s32 rem;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Leap second processing. If in leap-insert state at the end of the
|
* Leap second processing. If in leap-insert state at the end of the
|
||||||
@ -404,19 +410,19 @@ int second_overflow(unsigned long secs)
|
|||||||
case TIME_OK:
|
case TIME_OK:
|
||||||
if (time_status & STA_INS) {
|
if (time_status & STA_INS) {
|
||||||
time_state = TIME_INS;
|
time_state = TIME_INS;
|
||||||
ntp_next_leap_sec = secs + SECS_PER_DAY -
|
div_s64_rem(secs, SECS_PER_DAY, &rem);
|
||||||
(secs % SECS_PER_DAY);
|
ntp_next_leap_sec = secs + SECS_PER_DAY - rem;
|
||||||
} else if (time_status & STA_DEL) {
|
} else if (time_status & STA_DEL) {
|
||||||
time_state = TIME_DEL;
|
time_state = TIME_DEL;
|
||||||
ntp_next_leap_sec = secs + SECS_PER_DAY -
|
div_s64_rem(secs + 1, SECS_PER_DAY, &rem);
|
||||||
((secs+1) % SECS_PER_DAY);
|
ntp_next_leap_sec = secs + SECS_PER_DAY - rem;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TIME_INS:
|
case TIME_INS:
|
||||||
if (!(time_status & STA_INS)) {
|
if (!(time_status & STA_INS)) {
|
||||||
ntp_next_leap_sec = TIME64_MAX;
|
ntp_next_leap_sec = TIME64_MAX;
|
||||||
time_state = TIME_OK;
|
time_state = TIME_OK;
|
||||||
} else if (secs % SECS_PER_DAY == 0) {
|
} else if (secs == ntp_next_leap_sec) {
|
||||||
leap = -1;
|
leap = -1;
|
||||||
time_state = TIME_OOP;
|
time_state = TIME_OOP;
|
||||||
printk(KERN_NOTICE
|
printk(KERN_NOTICE
|
||||||
@ -427,7 +433,7 @@ int second_overflow(unsigned long secs)
|
|||||||
if (!(time_status & STA_DEL)) {
|
if (!(time_status & STA_DEL)) {
|
||||||
ntp_next_leap_sec = TIME64_MAX;
|
ntp_next_leap_sec = TIME64_MAX;
|
||||||
time_state = TIME_OK;
|
time_state = TIME_OK;
|
||||||
} else if ((secs + 1) % SECS_PER_DAY == 0) {
|
} else if (secs == ntp_next_leap_sec) {
|
||||||
leap = 1;
|
leap = 1;
|
||||||
ntp_next_leap_sec = TIME64_MAX;
|
ntp_next_leap_sec = TIME64_MAX;
|
||||||
time_state = TIME_WAIT;
|
time_state = TIME_WAIT;
|
||||||
@ -590,7 +596,7 @@ static inline void process_adj_status(struct timex *txc, struct timespec64 *ts)
|
|||||||
* reference time to current time.
|
* reference time to current time.
|
||||||
*/
|
*/
|
||||||
if (!(time_status & STA_PLL) && (txc->status & STA_PLL))
|
if (!(time_status & STA_PLL) && (txc->status & STA_PLL))
|
||||||
time_reftime = get_seconds();
|
time_reftime = __ktime_get_real_seconds();
|
||||||
|
|
||||||
/* only set allowed bits */
|
/* only set allowed bits */
|
||||||
time_status &= STA_RONLY;
|
time_status &= STA_RONLY;
|
||||||
@ -674,9 +680,15 @@ int ntp_validate_timex(struct timex *txc)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((txc->modes & ADJ_SETOFFSET) && (!capable(CAP_SYS_TIME)))
|
if (txc->modes & ADJ_SETOFFSET) {
|
||||||
|
/* In order to inject time, you gotta be super-user! */
|
||||||
|
if (!capable(CAP_SYS_TIME))
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
|
|
||||||
|
if (!timeval_inject_offset_valid(&txc->time))
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check for potential multiplication overflows that can
|
* Check for potential multiplication overflows that can
|
||||||
* only happen on 64-bit systems:
|
* only happen on 64-bit systems:
|
||||||
|
@ -6,7 +6,7 @@ extern void ntp_clear(void);
|
|||||||
/* Returns how long ticks are at present, in ns / 2^NTP_SCALE_SHIFT. */
|
/* Returns how long ticks are at present, in ns / 2^NTP_SCALE_SHIFT. */
|
||||||
extern u64 ntp_tick_length(void);
|
extern u64 ntp_tick_length(void);
|
||||||
extern ktime_t ntp_get_next_leap(void);
|
extern ktime_t ntp_get_next_leap(void);
|
||||||
extern int second_overflow(unsigned long secs);
|
extern int second_overflow(time64_t secs);
|
||||||
extern int ntp_validate_timex(struct timex *);
|
extern int ntp_validate_timex(struct timex *);
|
||||||
extern int __do_adjtimex(struct timex *, struct timespec64 *, s32 *);
|
extern int __do_adjtimex(struct timex *, struct timespec64 *, s32 *);
|
||||||
extern void __hardpps(const struct timespec64 *, const struct timespec64 *);
|
extern void __hardpps(const struct timespec64 *, const struct timespec64 *);
|
||||||
|
@ -69,10 +69,10 @@ static ssize_t posix_clock_read(struct file *fp, char __user *buf,
|
|||||||
static unsigned int posix_clock_poll(struct file *fp, poll_table *wait)
|
static unsigned int posix_clock_poll(struct file *fp, poll_table *wait)
|
||||||
{
|
{
|
||||||
struct posix_clock *clk = get_posix_clock(fp);
|
struct posix_clock *clk = get_posix_clock(fp);
|
||||||
int result = 0;
|
unsigned int result = 0;
|
||||||
|
|
||||||
if (!clk)
|
if (!clk)
|
||||||
return -ENODEV;
|
return POLLERR;
|
||||||
|
|
||||||
if (clk->ops.poll)
|
if (clk->ops.poll)
|
||||||
result = clk->ops.poll(clk, fp, wait);
|
result = clk->ops.poll(clk, fp, wait);
|
||||||
|
@ -603,15 +603,31 @@ static ktime_t tick_nohz_stop_sched_tick(struct tick_sched *ts,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* If the tick is due in the next period, keep it ticking or
|
* If the tick is due in the next period, keep it ticking or
|
||||||
* restart it proper.
|
* force prod the timer.
|
||||||
*/
|
*/
|
||||||
delta = next_tick - basemono;
|
delta = next_tick - basemono;
|
||||||
if (delta <= (u64)TICK_NSEC) {
|
if (delta <= (u64)TICK_NSEC) {
|
||||||
tick.tv64 = 0;
|
tick.tv64 = 0;
|
||||||
|
/*
|
||||||
|
* We've not stopped the tick yet, and there's a timer in the
|
||||||
|
* next period, so no point in stopping it either, bail.
|
||||||
|
*/
|
||||||
if (!ts->tick_stopped)
|
if (!ts->tick_stopped)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If, OTOH, we did stop it, but there's a pending (expired)
|
||||||
|
* timer reprogram the timer hardware to fire now.
|
||||||
|
*
|
||||||
|
* We will not restart the tick proper, just prod the timer
|
||||||
|
* hardware into firing an interrupt to process the pending
|
||||||
|
* timers. Just like tick_irq_exit() will not restart the tick
|
||||||
|
* for 'normal' interrupts.
|
||||||
|
*
|
||||||
|
* Only once we exit the idle loop will we re-enable the tick,
|
||||||
|
* see tick_nohz_idle_exit().
|
||||||
|
*/
|
||||||
if (delta == 0) {
|
if (delta == 0) {
|
||||||
/* Tick is stopped, but required now. Enforce it */
|
|
||||||
tick_nohz_restart(ts, now);
|
tick_nohz_restart(ts, now);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -305,8 +305,7 @@ static inline s64 timekeeping_get_ns(struct tk_read_base *tkr)
|
|||||||
|
|
||||||
delta = timekeeping_get_delta(tkr);
|
delta = timekeeping_get_delta(tkr);
|
||||||
|
|
||||||
nsec = delta * tkr->mult + tkr->xtime_nsec;
|
nsec = (delta * tkr->mult + tkr->xtime_nsec) >> tkr->shift;
|
||||||
nsec >>= tkr->shift;
|
|
||||||
|
|
||||||
/* If arch requires, add in get_arch_timeoffset() */
|
/* If arch requires, add in get_arch_timeoffset() */
|
||||||
return nsec + arch_gettimeoffset();
|
return nsec + arch_gettimeoffset();
|
||||||
@ -846,6 +845,19 @@ time64_t ktime_get_real_seconds(void)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(ktime_get_real_seconds);
|
EXPORT_SYMBOL_GPL(ktime_get_real_seconds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __ktime_get_real_seconds - The same as ktime_get_real_seconds
|
||||||
|
* but without the sequence counter protect. This internal function
|
||||||
|
* is called just when timekeeping lock is already held.
|
||||||
|
*/
|
||||||
|
time64_t __ktime_get_real_seconds(void)
|
||||||
|
{
|
||||||
|
struct timekeeper *tk = &tk_core.timekeeper;
|
||||||
|
|
||||||
|
return tk->xtime_sec;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef CONFIG_NTP_PPS
|
#ifdef CONFIG_NTP_PPS
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -959,7 +971,7 @@ int timekeeping_inject_offset(struct timespec *ts)
|
|||||||
struct timespec64 ts64, tmp;
|
struct timespec64 ts64, tmp;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC)
|
if (!timespec_inject_offset_valid(ts))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
ts64 = timespec_to_timespec64(*ts);
|
ts64 = timespec_to_timespec64(*ts);
|
||||||
@ -1592,9 +1604,12 @@ static __always_inline void timekeeping_freqadjust(struct timekeeper *tk,
|
|||||||
{
|
{
|
||||||
s64 interval = tk->cycle_interval;
|
s64 interval = tk->cycle_interval;
|
||||||
s64 xinterval = tk->xtime_interval;
|
s64 xinterval = tk->xtime_interval;
|
||||||
|
u32 base = tk->tkr_mono.clock->mult;
|
||||||
|
u32 max = tk->tkr_mono.clock->maxadj;
|
||||||
|
u32 cur_adj = tk->tkr_mono.mult;
|
||||||
s64 tick_error;
|
s64 tick_error;
|
||||||
bool negative;
|
bool negative;
|
||||||
u32 adj;
|
u32 adj_scale;
|
||||||
|
|
||||||
/* Remove any current error adj from freq calculation */
|
/* Remove any current error adj from freq calculation */
|
||||||
if (tk->ntp_err_mult)
|
if (tk->ntp_err_mult)
|
||||||
@ -1613,13 +1628,33 @@ static __always_inline void timekeeping_freqadjust(struct timekeeper *tk,
|
|||||||
/* preserve the direction of correction */
|
/* preserve the direction of correction */
|
||||||
negative = (tick_error < 0);
|
negative = (tick_error < 0);
|
||||||
|
|
||||||
/* Sort out the magnitude of the correction */
|
/* If any adjustment would pass the max, just return */
|
||||||
|
if (negative && (cur_adj - 1) <= (base - max))
|
||||||
|
return;
|
||||||
|
if (!negative && (cur_adj + 1) >= (base + max))
|
||||||
|
return;
|
||||||
|
/*
|
||||||
|
* Sort out the magnitude of the correction, but
|
||||||
|
* avoid making so large a correction that we go
|
||||||
|
* over the max adjustment.
|
||||||
|
*/
|
||||||
|
adj_scale = 0;
|
||||||
tick_error = abs(tick_error);
|
tick_error = abs(tick_error);
|
||||||
for (adj = 0; tick_error > interval; adj++)
|
while (tick_error > interval) {
|
||||||
|
u32 adj = 1 << (adj_scale + 1);
|
||||||
|
|
||||||
|
/* Check if adjustment gets us within 1 unit from the max */
|
||||||
|
if (negative && (cur_adj - adj) <= (base - max))
|
||||||
|
break;
|
||||||
|
if (!negative && (cur_adj + adj) >= (base + max))
|
||||||
|
break;
|
||||||
|
|
||||||
|
adj_scale++;
|
||||||
tick_error >>= 1;
|
tick_error >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* scale the corrections */
|
/* scale the corrections */
|
||||||
timekeeping_apply_adjustment(tk, offset, negative, adj);
|
timekeeping_apply_adjustment(tk, offset, negative, adj_scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -17,7 +17,11 @@ static inline cycle_t clocksource_delta(cycle_t now, cycle_t last, cycle_t mask)
|
|||||||
{
|
{
|
||||||
cycle_t ret = (now - last) & mask;
|
cycle_t ret = (now - last) & mask;
|
||||||
|
|
||||||
return (s64) ret > 0 ? ret : 0;
|
/*
|
||||||
|
* Prevent time going backwards by checking the MSB of mask in
|
||||||
|
* the result. If set, return 0.
|
||||||
|
*/
|
||||||
|
return ret & ~(mask >> 1) ? 0 : ret;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static inline cycle_t clocksource_delta(cycle_t now, cycle_t last, cycle_t mask)
|
static inline cycle_t clocksource_delta(cycle_t now, cycle_t last, cycle_t mask)
|
||||||
@ -26,4 +30,6 @@ static inline cycle_t clocksource_delta(cycle_t now, cycle_t last, cycle_t mask)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
extern time64_t __ktime_get_real_seconds(void);
|
||||||
|
|
||||||
#endif /* _TIMEKEEPING_INTERNAL_H */
|
#endif /* _TIMEKEEPING_INTERNAL_H */
|
||||||
|
@ -97,7 +97,7 @@ int get_cur_clocksource(char *buf, size_t size)
|
|||||||
int change_clocksource(char *clocksource)
|
int change_clocksource(char *clocksource)
|
||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
size_t size;
|
ssize_t size;
|
||||||
|
|
||||||
fd = open("/sys/devices/system/clocksource/clocksource0/current_clocksource", O_WRONLY);
|
fd = open("/sys/devices/system/clocksource/clocksource0/current_clocksource", O_WRONLY);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user