Initial commit: Linux kernel 4.19 for Unplugged OS

This commit is contained in:
ziv committed 2025-10-06 13:52:42 +00:00
commit d30235b04c
80519 files changed
+35800681

No files matched your search

+646
View File
@@ -0,0 +1,646 @@
menu "Clock Source drivers"
depends on GENERIC_CLOCKEVENTS
config TIMER_OF
bool
select TIMER_PROBE
config TIMER_ACPI
bool
select TIMER_PROBE
config TIMER_PROBE
bool
config CLKSRC_I8253
bool
config CLKEVT_I8253
bool
config I8253_LOCK
bool
config OMAP_DM_TIMER
bool
config CLKBLD_I8253
def_bool y if CLKSRC_I8253 || CLKEVT_I8253 || I8253_LOCK
config CLKSRC_MMIO
bool
config BCM2835_TIMER
bool "BCM2835 timer driver" if COMPILE_TEST
select CLKSRC_MMIO
help
Enables the support for the BCM2835 timer driver.
config BCM_KONA_TIMER
bool "BCM mobile timer driver" if COMPILE_TEST
select CLKSRC_MMIO
help
Enables the support for the BCM Kona mobile timer driver.
config DIGICOLOR_TIMER
bool "Digicolor timer driver" if COMPILE_TEST
select CLKSRC_MMIO
depends on HAS_IOMEM
help
Enables the support for the digicolor timer driver.
config DW_APB_TIMER
bool "DW APB timer driver" if COMPILE_TEST
help
Enables the support for the dw_apb timer.
config DW_APB_TIMER_OF
bool
select DW_APB_TIMER
select TIMER_OF
config FTTMR010_TIMER
bool "Faraday Technology timer driver" if COMPILE_TEST
depends on HAS_IOMEM
select CLKSRC_MMIO
select TIMER_OF
select MFD_SYSCON
help
Enables support for the Faraday Technology timer block
FTTMR010.
config ROCKCHIP_TIMER
bool "Rockchip timer driver" if COMPILE_TEST
depends on ARM || ARM64
select TIMER_OF
select CLKSRC_MMIO
help
Enables the support for the rockchip timer driver.
config ARMADA_370_XP_TIMER
bool "Armada 370 and XP timer driver" if COMPILE_TEST
depends on ARM
select TIMER_OF
select CLKSRC_MMIO
help
Enables the support for the Armada 370 and XP timer driver.
config MESON6_TIMER
bool "Meson6 timer driver" if COMPILE_TEST
select CLKSRC_MMIO
help
Enables the support for the Meson6 timer driver.
config ORION_TIMER
bool "Orion timer driver" if COMPILE_TEST
depends on ARM
select TIMER_OF
select CLKSRC_MMIO
help
Enables the support for the Orion timer driver
config OWL_TIMER
bool "Owl timer driver" if COMPILE_TEST
select CLKSRC_MMIO
help
Enables the support for the Actions Semi Owl timer driver.
config SUN4I_TIMER
bool "Sun4i timer driver" if COMPILE_TEST
depends on HAS_IOMEM
select CLKSRC_MMIO
select TIMER_OF
help
Enables support for the Sun4i timer.
config SUN5I_HSTIMER
bool "Sun5i timer driver" if COMPILE_TEST
select CLKSRC_MMIO
depends on COMMON_CLK
help
Enables support the Sun5i timer.
config TEGRA_TIMER
bool "Tegra timer driver" if COMPILE_TEST
select CLKSRC_MMIO
depends on ARM
help
Enables support for the Tegra driver.
config VT8500_TIMER
bool "VT8500 timer driver" if COMPILE_TEST
depends on HAS_IOMEM
help
Enables support for the VT8500 driver.
config NPCM7XX_TIMER
bool "NPCM7xx timer driver" if COMPILE_TEST
depends on HAS_IOMEM
select TIMER_OF
select CLKSRC_MMIO
help
Enable 24-bit TIMER0 and TIMER1 counters in the NPCM7xx architecture,
While TIMER0 serves as clockevent and TIMER1 serves as clocksource.
config CADENCE_TTC_TIMER
bool "Cadence TTC timer driver" if COMPILE_TEST
depends on COMMON_CLK
help
Enables support for the cadence ttc driver.
config ASM9260_TIMER
bool "ASM9260 timer driver" if COMPILE_TEST
select CLKSRC_MMIO
select TIMER_OF
help
Enables support for the ASM9260 timer.
config CLKSRC_NOMADIK_MTU
bool "Nomakdik clocksource driver" if COMPILE_TEST
depends on ARM
select CLKSRC_MMIO
help
Support for Multi Timer Unit. MTU provides access
to multiple interrupt generating programmable
32-bit free running decrementing counters.
config CLKSRC_NOMADIK_MTU_SCHED_CLOCK
bool
depends on CLKSRC_NOMADIK_MTU
help
Use the Multi Timer Unit as the sched_clock.
config CLKSRC_DBX500_PRCMU
bool "Clocksource PRCMU Timer" if COMPILE_TEST
depends on HAS_IOMEM
help
Use the always on PRCMU Timer as clocksource
config CLPS711X_TIMER
bool "Cirrus logic timer driver" if COMPILE_TEST
select CLKSRC_MMIO
help
Enables support for the Cirrus Logic PS711 timer.
config ATLAS7_TIMER
bool "Atlas7 timer driver" if COMPILE_TEST
select CLKSRC_MMIO
help
Enables support for the Atlas7 timer.
config MXS_TIMER
bool "Mxs timer driver" if COMPILE_TEST
select CLKSRC_MMIO
select STMP_DEVICE
help
Enables support for the Mxs timer.
config PRIMA2_TIMER
bool "Prima2 timer driver" if COMPILE_TEST
select CLKSRC_MMIO
help
Enables support for the Prima2 timer.
config U300_TIMER
bool "U300 timer driver" if COMPILE_TEST
depends on ARM
select CLKSRC_MMIO
help
Enables support for the U300 timer.
config NSPIRE_TIMER
bool "NSpire timer driver" if COMPILE_TEST
select CLKSRC_MMIO
help
Enables support for the Nspire timer.
config KEYSTONE_TIMER
bool "Keystone timer driver" if COMPILE_TEST
depends on ARM || ARM64
select CLKSRC_MMIO
help
Enables support for the Keystone timer.
config INTEGRATOR_AP_TIMER
bool "Integrator-ap timer driver" if COMPILE_TEST
select CLKSRC_MMIO
help
Enables support for the Integrator-ap timer.
config CLKSRC_DBX500_PRCMU_SCHED_CLOCK
bool "Clocksource PRCMU Timer sched_clock"
depends on (CLKSRC_DBX500_PRCMU && !CLKSRC_NOMADIK_MTU_SCHED_CLOCK)
default y
help
Use the always on PRCMU Timer as sched_clock
config CLKSRC_EFM32
bool "Clocksource for Energy Micro's EFM32 SoCs" if !ARCH_EFM32
depends on OF && ARM && (ARCH_EFM32 || COMPILE_TEST)
select CLKSRC_MMIO
default ARCH_EFM32
help
Support to use the timers of EFM32 SoCs as clock source and clock
event device.
config CLKSRC_LPC32XX
bool "Clocksource for LPC32XX" if COMPILE_TEST
depends on HAS_IOMEM
depends on ARM
select CLKSRC_MMIO
select TIMER_OF
help
Support for the LPC32XX clocksource.
config CLKSRC_PISTACHIO
bool "Clocksource for Pistachio SoC" if COMPILE_TEST
depends on HAS_IOMEM
select TIMER_OF
help
Enables the clocksource for the Pistachio SoC.
config CLKSRC_TI_32K
bool "Texas Instruments 32.768 Hz Clocksource" if COMPILE_TEST
depends on GENERIC_SCHED_CLOCK
select TIMER_OF if OF
help
This option enables support for Texas Instruments 32.768 Hz clocksource
available on many OMAP-like platforms.
config CLKSRC_NPS
bool "NPS400 clocksource driver" if COMPILE_TEST
depends on !PHYS_ADDR_T_64BIT
select CLKSRC_MMIO
select TIMER_OF if OF
help
NPS400 clocksource support.
Got 64 bit counter with update rate up to 1000MHz.
This counter is accessed via couple of 32 bit memory mapped registers.
config CLKSRC_STM32
bool "Clocksource for STM32 SoCs" if !ARCH_STM32
depends on OF && ARM && (ARCH_STM32 || COMPILE_TEST)
select CLKSRC_MMIO
select TIMER_OF
config CLKSRC_MPS2
bool "Clocksource for MPS2 SoCs" if COMPILE_TEST
depends on GENERIC_SCHED_CLOCK
select CLKSRC_MMIO
select TIMER_OF
config ARC_TIMERS
bool "Support for 32-bit TIMERn counters in ARC Cores" if COMPILE_TEST
depends on GENERIC_SCHED_CLOCK
select TIMER_OF
help
These are legacy 32-bit TIMER0 and TIMER1 counters found on all ARC cores
(ARC700 as well as ARC HS38).
TIMER0 serves as clockevent while TIMER1 provides clocksource
config ARC_TIMERS_64BIT
bool "Support for 64-bit counters in ARC HS38 cores" if COMPILE_TEST
depends on ARC_TIMERS
select TIMER_OF
help
This enables 2 different 64-bit timers: RTC (for UP) and GFRC (for SMP)
RTC is implemented inside the core, while GFRC sits outside the core in
ARConnect IP block. Driver automatically picks one of them for clocksource
as appropriate.
config ARM_ARCH_TIMER
bool
select TIMER_OF if OF
select TIMER_ACPI if ACPI
config ARM_ARCH_TIMER_EVTSTREAM
bool "Enable ARM architected timer event stream generation by default"
default y if ARM_ARCH_TIMER
depends on ARM_ARCH_TIMER
help
This option enables support by default for event stream generation
based on the ARM architected timer. It is used for waking up CPUs
executing the wfe instruction at a frequency represented as a
power-of-2 divisor of the clock rate. The behaviour can also be
overridden on the command line using the
clocksource.arm_arch_timer.evtstream parameter.
The main use of the event stream is wfe-based timeouts of userspace
locking implementations. It might also be useful for imposing timeout
on wfe to safeguard against any programming errors in case an expected
event is not generated.
This must be disabled for hardware validation purposes to detect any
hardware anomalies of missing events.
config ARM_ARCH_TIMER_OOL_WORKAROUND
bool
config FSL_ERRATUM_A008585
bool "Workaround for Freescale/NXP Erratum A-008585"
default y
depends on ARM_ARCH_TIMER && ARM64
select ARM_ARCH_TIMER_OOL_WORKAROUND
help
This option enables a workaround for Freescale/NXP Erratum
A-008585 ("ARM generic timer may contain an erroneous
value"). The workaround will only be active if the
fsl,erratum-a008585 property is found in the timer node.
config HISILICON_ERRATUM_161010101
bool "Workaround for Hisilicon Erratum 161010101"
default y
select ARM_ARCH_TIMER_OOL_WORKAROUND
depends on ARM_ARCH_TIMER && ARM64
help
This option enables a workaround for Hisilicon Erratum
161010101. The workaround will be active if the hisilicon,erratum-161010101
property is found in the timer node.
config ARM64_ERRATUM_858921
bool "Workaround for Cortex-A73 erratum 858921"
default y
select ARM_ARCH_TIMER_OOL_WORKAROUND
depends on ARM_ARCH_TIMER && ARM64
help
This option enables a workaround applicable to Cortex-A73
(all versions), whose counter may return incorrect values.
The workaround will be dynamically enabled when an affected
core is detected.
config SUN50I_ERRATUM_UNKNOWN1
bool "Workaround for Allwinner A64 erratum UNKNOWN1"
default y
depends on ARM_ARCH_TIMER && ARM64 && ARCH_SUNXI
select ARM_ARCH_TIMER_OOL_WORKAROUND
help
This option enables a workaround for instability in the timer on
the Allwinner A64 SoC. The workaround will only be active if the
allwinner,erratum-unknown1 property is found in the timer node.
config ARM_GLOBAL_TIMER
bool "Support for the ARM global timer" if COMPILE_TEST
select TIMER_OF if OF
depends on ARM
help
This options enables support for the ARM global timer unit
config ARM_TIMER_SP804
bool "Support for Dual Timer SP804 module"
depends on GENERIC_SCHED_CLOCK && CLKDEV_LOOKUP
select CLKSRC_MMIO
select TIMER_OF if OF
config CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
bool
depends on ARM_GLOBAL_TIMER
default y
help
Use ARM global timer clock source as sched_clock
config ARMV7M_SYSTICK
bool "Support for the ARMv7M system time" if COMPILE_TEST
select TIMER_OF if OF
select CLKSRC_MMIO
help
This options enables support for the ARMv7M system timer unit
config ATMEL_PIT
select TIMER_OF if OF
def_bool SOC_AT91SAM9 || SOC_SAMA5
config ATMEL_ST
bool "Atmel ST timer support" if COMPILE_TEST
depends on HAS_IOMEM
select TIMER_OF
select MFD_SYSCON
help
Support for the Atmel ST timer.
config CLKSRC_EXYNOS_MCT
bool "Exynos multi core timer driver" if COMPILE_TEST
depends on ARM || ARM64
help
Support for Multi Core Timer controller on Exynos SoCs.
config CLKSRC_SAMSUNG_PWM
bool "PWM timer driver for Samsung S3C, S5P" if COMPILE_TEST
depends on HAS_IOMEM
help
This is a new clocksource driver for the PWM timer found in
Samsung S3C, S5P and Exynos SoCs, replacing an earlier driver
for all devicetree enabled platforms. This driver will be
needed only on systems that do not have the Exynos MCT available.
config FSL_FTM_TIMER
bool "Freescale FlexTimer Module driver" if COMPILE_TEST
depends on HAS_IOMEM
select CLKSRC_MMIO
help
Support for Freescale FlexTimer Module (FTM) timer.
config VF_PIT_TIMER
bool
select CLKSRC_MMIO
help
Support for Period Interrupt Timer on Freescale Vybrid Family SoCs.
config OXNAS_RPS_TIMER
bool "Oxford Semiconductor OXNAS RPS Timers driver" if COMPILE_TEST
select TIMER_OF
select CLKSRC_MMIO
help
This enables support for the Oxford Semiconductor OXNAS RPS timers.
config SYS_SUPPORTS_SH_CMT
bool
config MTK_TIMER
bool "Mediatek timer driver" if COMPILE_TEST
depends on HAS_IOMEM
select TIMER_OF
select CLKSRC_MMIO
help
Support for Mediatek timer driver.
config MTK_TIMER_APXGPT_V1
bool "Mediatek apxgpt driver" if COMPILE_TEST
depends on GENERIC_CLOCKEVENTS && HAS_IOMEM
select CLKSRC_OF
select CLKSRC_MMIO
help
Support for Mediatek NON-IPM gpt timer driver.
used for tick broadcast.
resolve some issue that gpt interrupt masked.
add important debug info.
config SPRD_TIMER
bool "Spreadtrum timer driver" if EXPERT
depends on HAS_IOMEM
depends on (ARCH_SPRD || COMPILE_TEST)
default ARCH_SPRD
select TIMER_OF
help
Enables support for the Spreadtrum timer driver.
config SYS_SUPPORTS_SH_MTU2
bool
config SYS_SUPPORTS_SH_TMU
bool
config SYS_SUPPORTS_EM_STI
bool
config CLKSRC_JCORE_PIT
bool "J-Core PIT timer driver" if COMPILE_TEST
depends on OF
depends on HAS_IOMEM
select CLKSRC_MMIO
help
This enables build of clocksource and clockevent driver for
the integrated PIT in the J-Core synthesizable, open source SoC.
config SH_TIMER_CMT
bool "Renesas CMT timer driver" if COMPILE_TEST
depends on HAS_IOMEM
default SYS_SUPPORTS_SH_CMT
help
This enables build of a clocksource and clockevent driver for
the Compare Match Timer (CMT) hardware available in 16/32/48-bit
variants on a wide range of Mobile and Automotive SoCs from Renesas.
config SH_TIMER_MTU2
bool "Renesas MTU2 timer driver" if COMPILE_TEST
depends on HAS_IOMEM
default SYS_SUPPORTS_SH_MTU2
help
This enables build of a clockevent driver for the Multi-Function
Timer Pulse Unit 2 (MTU2) hardware available on SoCs from Renesas.
This hardware comes with 16 bit-timer registers.
config RENESAS_OSTM
bool "Renesas OSTM timer driver" if COMPILE_TEST
select CLKSRC_MMIO
help
Enables the support for the Renesas OSTM.
config SH_TIMER_TMU
bool "Renesas TMU timer driver" if COMPILE_TEST
depends on HAS_IOMEM
default SYS_SUPPORTS_SH_TMU
help
This enables build of a clocksource and clockevent driver for
the 32-bit Timer Unit (TMU) hardware available on a wide range
SoCs from Renesas.
config EM_TIMER_STI
bool "Renesas STI timer driver" if COMPILE_TEST
depends on HAS_IOMEM
default SYS_SUPPORTS_EM_STI
help
This enables build of a clocksource and clockevent driver for
the 48-bit System Timer (STI) hardware available on a SoCs
such as EMEV2 from former NEC Electronics.
config CLKSRC_QCOM
bool "Qualcomm MSM timer" if COMPILE_TEST
depends on ARM
select TIMER_OF
help
This enables the clocksource and the per CPU clockevent driver for the
Qualcomm SoCs.
config CLKSRC_VERSATILE
bool "ARM Versatile (Express) reference platforms clock source" if COMPILE_TEST
depends on GENERIC_SCHED_CLOCK && !ARCH_USES_GETTIMEOFFSET
select TIMER_OF
default y if MFD_VEXPRESS_SYSREG
help
This option enables clock source based on free running
counter available in the "System Registers" block of
ARM Versatile, RealView and Versatile Express reference
platforms.
config CLKSRC_MIPS_GIC
bool
depends on MIPS_GIC
select TIMER_OF
config CLKSRC_TANGO_XTAL
bool "Clocksource for Tango SoC" if COMPILE_TEST
depends on ARM
select TIMER_OF
select CLKSRC_MMIO
help
This enables the clocksource for Tango SoC
config CLKSRC_PXA
bool "Clocksource for PXA or SA-11x0 platform" if COMPILE_TEST
depends on HAS_IOMEM
select CLKSRC_MMIO
help
This enables OST0 support available on PXA and SA-11x0
platforms.
config H8300_TMR8
bool "Clockevent timer for the H8300 platform" if COMPILE_TEST
depends on HAS_IOMEM
help
This enables the 8 bits timer for the H8300 platform.
config H8300_TMR16
bool "Clockevent timer for the H83069 platform" if COMPILE_TEST
depends on HAS_IOMEM
help
This enables the 16 bits timer for the H8300 platform with the
H83069 cpu.
config H8300_TPU
bool "Clocksource for the H8300 platform" if COMPILE_TEST
depends on HAS_IOMEM
help
This enables the clocksource for the H8300 platform with the
H8S2678 cpu.
config CLKSRC_IMX_GPT
bool "Clocksource using i.MX GPT" if COMPILE_TEST
depends on ARM && CLKDEV_LOOKUP
select CLKSRC_MMIO
config CLKSRC_IMX_TPM
bool "Clocksource using i.MX TPM" if COMPILE_TEST
depends on ARM && CLKDEV_LOOKUP
select CLKSRC_MMIO
help
Enable this option to use IMX Timer/PWM Module (TPM) timer as
clocksource.
config CLKSRC_ST_LPC
bool "Low power clocksource found in the LPC" if COMPILE_TEST
select TIMER_OF if OF
depends on HAS_IOMEM
select CLKSRC_MMIO
help
Enable this option to use the Low Power controller timer
as clocksource.
config ATCPIT100_TIMER
bool "ATCPIT100 timer driver"
depends on NDS32 || COMPILE_TEST
depends on HAS_IOMEM
select TIMER_OF
default NDS32
help
This option enables support for the Andestech ATCPIT100 timers.
config RISCV_TIMER
bool "Timer for the RISC-V platform"
depends on RISCV
default y
select TIMER_PROBE
select TIMER_OF
help
This enables the per-hart timer built into all RISC-V systems, which
is accessed via both the SBI and the rdcycle instruction. This is
required for all RISC-V systems.
endmenu
+86
View File
@@ -0,0 +1,86 @@
# SPDX-License-Identifier: GPL-2.0
# For timer-mediatek debug info
ccflags-y += -I$(srctree)/drivers/misc/mediatek/include
obj-$(CONFIG_TIMER_OF) += timer-of.o
obj-$(CONFIG_TIMER_PROBE) += timer-probe.o
obj-$(CONFIG_ATMEL_PIT) += timer-atmel-pit.o
obj-$(CONFIG_ATMEL_ST) += timer-atmel-st.o
obj-$(CONFIG_ATMEL_TCB_CLKSRC) += tcb_clksrc.o
obj-$(CONFIG_X86_PM_TIMER) += acpi_pm.o
obj-$(CONFIG_SCx200HR_TIMER) += scx200_hrt.o
obj-$(CONFIG_CS5535_CLOCK_EVENT_SRC) += cs5535-clockevt.o
obj-$(CONFIG_CLKSRC_JCORE_PIT) += jcore-pit.o
obj-$(CONFIG_SH_TIMER_CMT) += sh_cmt.o
obj-$(CONFIG_SH_TIMER_MTU2) += sh_mtu2.o
obj-$(CONFIG_RENESAS_OSTM) += renesas-ostm.o
obj-$(CONFIG_SH_TIMER_TMU) += sh_tmu.o
obj-$(CONFIG_EM_TIMER_STI) += em_sti.o
obj-$(CONFIG_CLKBLD_I8253) += i8253.o
obj-$(CONFIG_CLKSRC_MMIO) += mmio.o
obj-$(CONFIG_DIGICOLOR_TIMER) += timer-digicolor.o
obj-$(CONFIG_OMAP_DM_TIMER) += timer-ti-dm.o
obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o
obj-$(CONFIG_DW_APB_TIMER_OF) += dw_apb_timer_of.o
obj-$(CONFIG_FTTMR010_TIMER) += timer-fttmr010.o
obj-$(CONFIG_ROCKCHIP_TIMER) += rockchip_timer.o
obj-$(CONFIG_CLKSRC_NOMADIK_MTU) += nomadik-mtu.o
obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o
obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o
obj-$(CONFIG_ORION_TIMER) += time-orion.o
obj-$(CONFIG_BCM2835_TIMER) += bcm2835_timer.o
obj-$(CONFIG_CLPS711X_TIMER) += clps711x-timer.o
obj-$(CONFIG_ATLAS7_TIMER) += timer-atlas7.o
obj-$(CONFIG_MXS_TIMER) += mxs_timer.o
obj-$(CONFIG_CLKSRC_PXA) += pxa_timer.o
obj-$(CONFIG_PRIMA2_TIMER) += timer-prima2.o
obj-$(CONFIG_U300_TIMER) += timer-u300.o
obj-$(CONFIG_SUN4I_TIMER) += sun4i_timer.o
obj-$(CONFIG_SUN5I_HSTIMER) += timer-sun5i.o
obj-$(CONFIG_MESON6_TIMER) += meson6_timer.o
obj-$(CONFIG_TEGRA_TIMER) += tegra20_timer.o
obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o
obj-$(CONFIG_NSPIRE_TIMER) += zevio-timer.o
obj-$(CONFIG_BCM_KONA_TIMER) += bcm_kona_timer.o
obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o
obj-$(CONFIG_CLKSRC_EFM32) += time-efm32.o
obj-$(CONFIG_CLKSRC_STM32) += timer-stm32.o
obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o
obj-$(CONFIG_CLKSRC_LPC32XX) += time-lpc32xx.o
obj-$(CONFIG_CLKSRC_MPS2) += mps2-timer.o
obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o
obj-$(CONFIG_FSL_FTM_TIMER) += fsl_ftm_timer.o
obj-$(CONFIG_VF_PIT_TIMER) += vf_pit_timer.o
obj-$(CONFIG_CLKSRC_QCOM) += qcom-timer.o
obj-$(CONFIG_MTK_TIMER) += timer-mediatek.o
obj-$(CONFIG_MTK_TIMER_APXGPT_V1) += mtk_apxgpt.o
obj-$(CONFIG_CLKSRC_PISTACHIO) += time-pistachio.o
obj-$(CONFIG_CLKSRC_TI_32K) += timer-ti-32k.o
obj-$(CONFIG_CLKSRC_NPS) += timer-nps.o
obj-$(CONFIG_OXNAS_RPS_TIMER) += timer-oxnas-rps.o
obj-$(CONFIG_OWL_TIMER) += owl-timer.o
obj-$(CONFIG_SPRD_TIMER) += timer-sprd.o
obj-$(CONFIG_NPCM7XX_TIMER) += timer-npcm7xx.o
obj-$(CONFIG_ARC_TIMERS) += arc_timer.o
obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o
obj-$(CONFIG_ARMV7M_SYSTICK) += armv7m_systick.o
obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp804.o
obj-$(CONFIG_ARCH_HAS_TICK_BROADCAST) += dummy_timer.o
obj-$(CONFIG_KEYSTONE_TIMER) += timer-keystone.o
obj-$(CONFIG_INTEGRATOR_AP_TIMER) += timer-integrator-ap.o
obj-$(CONFIG_CLKSRC_VERSATILE) += versatile.o
obj-$(CONFIG_CLKSRC_MIPS_GIC) += mips-gic-timer.o
obj-$(CONFIG_CLKSRC_TANGO_XTAL) += tango_xtal.o
obj-$(CONFIG_CLKSRC_IMX_GPT) += timer-imx-gpt.o
obj-$(CONFIG_CLKSRC_IMX_TPM) += timer-imx-tpm.o
obj-$(CONFIG_ASM9260_TIMER) += asm9260_timer.o
obj-$(CONFIG_H8300_TMR8) += h8300_timer8.o
obj-$(CONFIG_H8300_TMR16) += h8300_timer16.o
obj-$(CONFIG_H8300_TPU) += h8300_tpu.o
obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o
obj-$(CONFIG_X86_NUMACHIP) += numachip.o
obj-$(CONFIG_ATCPIT100_TIMER) += timer-atcpit100.o
obj-$(CONFIG_RISCV_TIMER) += riscv_timer.o
+242
View File
@@ -0,0 +1,242 @@
/*
* linux/drivers/clocksource/acpi_pm.c
*
* This file contains the ACPI PM based clocksource.
*
* This code was largely moved from the i386 timer_pm.c file
* which was (C) Dominik Brodowski <linux@brodo.de> 2003
* and contained the following comments:
*
* Driver to use the Power Management Timer (PMTMR) available in some
* southbridges as primary timing source for the Linux kernel.
*
* Based on parts of linux/drivers/acpi/hardware/hwtimer.c, timer_pit.c,
* timer_hpet.c, and on Arjan van de Ven's implementation for 2.4.
*
* This file is licensed under the GPL v2.
*/
#include <linux/acpi_pmtmr.h>
#include <linux/clocksource.h>
#include <linux/timex.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <asm/io.h>
/*
* The I/O port the PMTMR resides at.
* The location is detected during setup_arch(),
* in arch/i386/kernel/acpi/boot.c
*/
u32 pmtmr_ioport __read_mostly;
static inline u32 read_pmtmr(void)
{
/* mask the output to 24 bits */
return inl(pmtmr_ioport) & ACPI_PM_MASK;
}
u32 acpi_pm_read_verified(void)
{
u32 v1 = 0, v2 = 0, v3 = 0;
/*
* It has been reported that because of various broken
* chipsets (ICH4, PIIX4 and PIIX4E) where the ACPI PM clock
* source is not latched, you must read it multiple
* times to ensure a safe value is read:
*/
do {
v1 = read_pmtmr();
v2 = read_pmtmr();
v3 = read_pmtmr();
} while (unlikely((v1 > v2 && v1 < v3) || (v2 > v3 && v2 < v1)
|| (v3 > v1 && v3 < v2)));
return v2;
}
static u64 acpi_pm_read(struct clocksource *cs)
{
return (u64)read_pmtmr();
}
static struct clocksource clocksource_acpi_pm = {
.name = "acpi_pm",
.rating = 200,
.read = acpi_pm_read,
.mask = (u64)ACPI_PM_MASK,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
#ifdef CONFIG_PCI
static int acpi_pm_good;
static int __init acpi_pm_good_setup(char *__str)
{
acpi_pm_good = 1;
return 1;
}
__setup("acpi_pm_good", acpi_pm_good_setup);
static u64 acpi_pm_read_slow(struct clocksource *cs)
{
return (u64)acpi_pm_read_verified();
}
static inline void acpi_pm_need_workaround(void)
{
clocksource_acpi_pm.read = acpi_pm_read_slow;
clocksource_acpi_pm.rating = 120;
}
/*
* PIIX4 Errata:
*
* The power management timer may return improper results when read.
* Although the timer value settles properly after incrementing,
* while incrementing there is a 3 ns window every 69.8 ns where the
* timer value is indeterminate (a 4.2% chance that the data will be
* incorrect when read). As a result, the ACPI free running count up
* timer specification is violated due to erroneous reads.
*/
static void acpi_pm_check_blacklist(struct pci_dev *dev)
{
if (acpi_pm_good)
return;
/* the bug has been fixed in PIIX4M */
if (dev->revision < 3) {
pr_warn("* Found PM-Timer Bug on the chipset. Due to workarounds for a bug,\n"
"* this clock source is slow. Consider trying other clock sources\n");
acpi_pm_need_workaround();
}
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3,
acpi_pm_check_blacklist);
static void acpi_pm_check_graylist(struct pci_dev *dev)
{
if (acpi_pm_good)
return;
pr_warn("* The chipset may have PM-Timer Bug. Due to workarounds for a bug,\n"
"* this clock source is slow. If you are sure your timer does not have\n"
"* this bug, please use \"acpi_pm_good\" to disable the workaround\n");
acpi_pm_need_workaround();
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0,
acpi_pm_check_graylist);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_LE,
acpi_pm_check_graylist);
#endif
#ifndef CONFIG_X86_64
#include <asm/mach_timer.h>
#define PMTMR_EXPECTED_RATE \
((CALIBRATE_LATCH * (PMTMR_TICKS_PER_SEC >> 10)) / (PIT_TICK_RATE>>10))
/*
* Some boards have the PMTMR running way too fast. We check
* the PMTMR rate against PIT channel 2 to catch these cases.
*/
static int verify_pmtmr_rate(void)
{
u64 value1, value2;
unsigned long count, delta;
mach_prepare_counter();
value1 = clocksource_acpi_pm.read(&clocksource_acpi_pm);
mach_countup(&count);
value2 = clocksource_acpi_pm.read(&clocksource_acpi_pm);
delta = (value2 - value1) & ACPI_PM_MASK;
/* Check that the PMTMR delta is within 5% of what we expect */
if (delta < (PMTMR_EXPECTED_RATE * 19) / 20 ||
delta > (PMTMR_EXPECTED_RATE * 21) / 20) {
pr_info("PM-Timer running at invalid rate: %lu%% of normal - aborting.\n",
100UL * delta / PMTMR_EXPECTED_RATE);
return -1;
}
return 0;
}
#else
#define verify_pmtmr_rate() (0)
#endif
/* Number of monotonicity checks to perform during initialization */
#define ACPI_PM_MONOTONICITY_CHECKS 10
/* Number of reads we try to get two different values */
#define ACPI_PM_READ_CHECKS 10000
static int __init init_acpi_pm_clocksource(void)
{
u64 value1, value2;
unsigned int i, j = 0;
if (!pmtmr_ioport)
return -ENODEV;
/* "verify" this timing source: */
for (j = 0; j < ACPI_PM_MONOTONICITY_CHECKS; j++) {
udelay(100 * j);
value1 = clocksource_acpi_pm.read(&clocksource_acpi_pm);
for (i = 0; i < ACPI_PM_READ_CHECKS; i++) {
value2 = clocksource_acpi_pm.read(&clocksource_acpi_pm);
if (value2 == value1)
continue;
if (value2 > value1)
break;
if ((value2 < value1) && ((value2) < 0xFFF))
break;
pr_info("PM-Timer had inconsistent results: %#llx, %#llx - aborting.\n",
value1, value2);
pmtmr_ioport = 0;
return -EINVAL;
}
if (i == ACPI_PM_READ_CHECKS) {
pr_info("PM-Timer failed consistency check (%#llx) - aborting.\n",
value1);
pmtmr_ioport = 0;
return -ENODEV;
}
}
if (verify_pmtmr_rate() != 0){
pmtmr_ioport = 0;
return -ENODEV;
}
return clocksource_register_hz(&clocksource_acpi_pm,
PMTMR_TICKS_PER_SEC);
}
/* We use fs_initcall because we want the PCI fixups to have run
* but we still need to load before device_initcall
*/
fs_initcall(init_acpi_pm_clocksource);
/*
* Allow an override of the IOPort. Stupid BIOSes do not tell us about
* the PMTimer, but we might know where it is.
*/
static int __init parse_pmtmr(char *arg)
{
unsigned int base;
int ret;
ret = kstrtouint(arg, 16, &base);
if (ret)
return ret;
pr_info("PMTMR IOPort override: 0x%04x -> 0x%04x\n", pmtmr_ioport,
base);
pmtmr_ioport = base;
return 1;
}
__setup("pmtmr=", parse_pmtmr);
+377
View File
@@ -0,0 +1,377 @@
/*
* Copyright (C) 2016-17 Synopsys, Inc. (www.synopsys.com)
* Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* ARC700 has two 32bit independent prog Timers: TIMER0 and TIMER1, Each can be
* programmed to go from @count to @limit and optionally interrupt.
* We've designated TIMER0 for clockevents and TIMER1 for clocksource
*
* ARCv2 based HS38 cores have RTC (in-core) and GFRC (inside ARConnect/MCIP)
* which are suitable for UP and SMP based clocksources respectively
*/
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/cpu.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/sched_clock.h>
#include <soc/arc/timers.h>
#include <soc/arc/mcip.h>
static unsigned long arc_timer_freq;
static int noinline arc_get_timer_clk(struct device_node *node)
{
struct clk *clk;
int ret;
clk = of_clk_get(node, 0);
if (IS_ERR(clk)) {
pr_err("timer missing clk\n");
return PTR_ERR(clk);
}
ret = clk_prepare_enable(clk);
if (ret) {
pr_err("Couldn't enable parent clk\n");
return ret;
}
arc_timer_freq = clk_get_rate(clk);
return 0;
}
/********** Clock Source Device *********/
#ifdef CONFIG_ARC_TIMERS_64BIT
static u64 arc_read_gfrc(struct clocksource *cs)
{
unsigned long flags;
u32 l, h;
/*
* From a programming model pov, there seems to be just one instance of
* MCIP_CMD/MCIP_READBACK however micro-architecturally there's
* an instance PER ARC CORE (not per cluster), and there are dedicated
* hardware decode logic (per core) inside ARConnect to handle
* simultaneous read/write accesses from cores via those two registers.
* So several concurrent commands to ARConnect are OK if they are
* trying to access two different sub-components (like GFRC,
* inter-core interrupt, etc...). HW also supports simultaneously
* accessing GFRC by multiple cores.
* That's why it is safe to disable hard interrupts on the local CPU
* before access to GFRC instead of taking global MCIP spinlock
* defined in arch/arc/kernel/mcip.c
*/
local_irq_save(flags);
__mcip_cmd(CMD_GFRC_READ_LO, 0);
l = read_aux_reg(ARC_REG_MCIP_READBACK);
__mcip_cmd(CMD_GFRC_READ_HI, 0);
h = read_aux_reg(ARC_REG_MCIP_READBACK);
local_irq_restore(flags);
return (((u64)h) << 32) | l;
}
static notrace u64 arc_gfrc_clock_read(void)
{
return arc_read_gfrc(NULL);
}
static struct clocksource arc_counter_gfrc = {
.name = "ARConnect GFRC",
.rating = 400,
.read = arc_read_gfrc,
.mask = CLOCKSOURCE_MASK(64),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static int __init arc_cs_setup_gfrc(struct device_node *node)
{
struct mcip_bcr mp;
int ret;
READ_BCR(ARC_REG_MCIP_BCR, mp);
if (!mp.gfrc) {
pr_warn("Global-64-bit-Ctr clocksource not detected\n");
return -ENXIO;
}
ret = arc_get_timer_clk(node);
if (ret)
return ret;
sched_clock_register(arc_gfrc_clock_read, 64, arc_timer_freq);
return clocksource_register_hz(&arc_counter_gfrc, arc_timer_freq);
}
TIMER_OF_DECLARE(arc_gfrc, "snps,archs-timer-gfrc", arc_cs_setup_gfrc);
#define AUX_RTC_CTRL 0x103
#define AUX_RTC_LOW 0x104
#define AUX_RTC_HIGH 0x105
static u64 arc_read_rtc(struct clocksource *cs)
{
unsigned long status;
u32 l, h;
/*
* hardware has an internal state machine which tracks readout of
* low/high and updates the CTRL.status if
* - interrupt/exception taken between the two reads
* - high increments after low has been read
*/
do {
l = read_aux_reg(AUX_RTC_LOW);
h = read_aux_reg(AUX_RTC_HIGH);
status = read_aux_reg(AUX_RTC_CTRL);
} while (!(status & _BITUL(31)));
return (((u64)h) << 32) | l;
}
static notrace u64 arc_rtc_clock_read(void)
{
return arc_read_rtc(NULL);
}
static struct clocksource arc_counter_rtc = {
.name = "ARCv2 RTC",
.rating = 350,
.read = arc_read_rtc,
.mask = CLOCKSOURCE_MASK(64),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static int __init arc_cs_setup_rtc(struct device_node *node)
{
struct bcr_timer timer;
int ret;
READ_BCR(ARC_REG_TIMERS_BCR, timer);
if (!timer.rtc) {
pr_warn("Local-64-bit-Ctr clocksource not detected\n");
return -ENXIO;
}
/* Local to CPU hence not usable in SMP */
if (IS_ENABLED(CONFIG_SMP)) {
pr_warn("Local-64-bit-Ctr not usable in SMP\n");
return -EINVAL;
}
ret = arc_get_timer_clk(node);
if (ret)
return ret;
write_aux_reg(AUX_RTC_CTRL, 1);
sched_clock_register(arc_rtc_clock_read, 64, arc_timer_freq);
return clocksource_register_hz(&arc_counter_rtc, arc_timer_freq);
}
TIMER_OF_DECLARE(arc_rtc, "snps,archs-timer-rtc", arc_cs_setup_rtc);
#endif
/*
* 32bit TIMER1 to keep counting monotonically and wraparound
*/
static u64 arc_read_timer1(struct clocksource *cs)
{
return (u64) read_aux_reg(ARC_REG_TIMER1_CNT);
}
static notrace u64 arc_timer1_clock_read(void)
{
return arc_read_timer1(NULL);
}
static struct clocksource arc_counter_timer1 = {
.name = "ARC Timer1",
.rating = 300,
.read = arc_read_timer1,
.mask = CLOCKSOURCE_MASK(32),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static int __init arc_cs_setup_timer1(struct device_node *node)
{
int ret;
/* Local to CPU hence not usable in SMP */
if (IS_ENABLED(CONFIG_SMP))
return -EINVAL;
ret = arc_get_timer_clk(node);
if (ret)
return ret;
write_aux_reg(ARC_REG_TIMER1_LIMIT, ARC_TIMERN_MAX);
write_aux_reg(ARC_REG_TIMER1_CNT, 0);
write_aux_reg(ARC_REG_TIMER1_CTRL, TIMER_CTRL_NH);
sched_clock_register(arc_timer1_clock_read, 32, arc_timer_freq);
return clocksource_register_hz(&arc_counter_timer1, arc_timer_freq);
}
/********** Clock Event Device *********/
static int arc_timer_irq;
/*
* Arm the timer to interrupt after @cycles
* The distinction for oneshot/periodic is done in arc_event_timer_ack() below
*/
static void arc_timer_event_setup(unsigned int cycles)
{
write_aux_reg(ARC_REG_TIMER0_LIMIT, cycles);
write_aux_reg(ARC_REG_TIMER0_CNT, 0); /* start from 0 */
write_aux_reg(ARC_REG_TIMER0_CTRL, TIMER_CTRL_IE | TIMER_CTRL_NH);
}
static int arc_clkevent_set_next_event(unsigned long delta,
struct clock_event_device *dev)
{
arc_timer_event_setup(delta);
return 0;
}
static int arc_clkevent_set_periodic(struct clock_event_device *dev)
{
/*
* At X Hz, 1 sec = 1000ms -> X cycles;
* 10ms -> X / 100 cycles
*/
arc_timer_event_setup(arc_timer_freq / HZ);
return 0;
}
static DEFINE_PER_CPU(struct clock_event_device, arc_clockevent_device) = {
.name = "ARC Timer0",
.features = CLOCK_EVT_FEAT_ONESHOT |
CLOCK_EVT_FEAT_PERIODIC,
.rating = 300,
.set_next_event = arc_clkevent_set_next_event,
.set_state_periodic = arc_clkevent_set_periodic,
};
static irqreturn_t timer_irq_handler(int irq, void *dev_id)
{
/*
* Note that generic IRQ core could have passed @evt for @dev_id if
* irq_set_chip_and_handler() asked for handle_percpu_devid_irq()
*/
struct clock_event_device *evt = this_cpu_ptr(&arc_clockevent_device);
int irq_reenable = clockevent_state_periodic(evt);
/*
* 1. ACK the interrupt
* - For ARC700, any write to CTRL reg ACKs it, so just rewrite
* Count when [N]ot [H]alted bit.
* - For HS3x, it is a bit subtle. On taken count-down interrupt,
* IP bit [3] is set, which needs to be cleared for ACK'ing.
* The write below can only update the other two bits, hence
* explicitly clears IP bit
* 2. Re-arm interrupt if periodic by writing to IE bit [0]
*/
write_aux_reg(ARC_REG_TIMER0_CTRL, irq_reenable | TIMER_CTRL_NH);
evt->event_handler(evt);
return IRQ_HANDLED;
}
static int arc_timer_starting_cpu(unsigned int cpu)
{
struct clock_event_device *evt = this_cpu_ptr(&arc_clockevent_device);
evt->cpumask = cpumask_of(smp_processor_id());
clockevents_config_and_register(evt, arc_timer_freq, 0, ARC_TIMERN_MAX);
enable_percpu_irq(arc_timer_irq, 0);
return 0;
}
static int arc_timer_dying_cpu(unsigned int cpu)
{
disable_percpu_irq(arc_timer_irq);
return 0;
}
/*
* clockevent setup for boot CPU
*/
static int __init arc_clockevent_setup(struct device_node *node)
{
struct clock_event_device *evt = this_cpu_ptr(&arc_clockevent_device);
int ret;
arc_timer_irq = irq_of_parse_and_map(node, 0);
if (arc_timer_irq <= 0) {
pr_err("clockevent: missing irq\n");
return -EINVAL;
}
ret = arc_get_timer_clk(node);
if (ret) {
pr_err("clockevent: missing clk\n");
return ret;
}
/* Needs apriori irq_set_percpu_devid() done in intc map function */
ret = request_percpu_irq(arc_timer_irq, timer_irq_handler,
"Timer0 (per-cpu-tick)", evt);
if (ret) {
pr_err("clockevent: unable to request irq\n");
return ret;
}
ret = cpuhp_setup_state(CPUHP_AP_ARC_TIMER_STARTING,
"clockevents/arc/timer:starting",
arc_timer_starting_cpu,
arc_timer_dying_cpu);
if (ret) {
pr_err("Failed to setup hotplug state\n");
return ret;
}
return 0;
}
static int __init arc_of_timer_init(struct device_node *np)
{
static int init_count = 0;
int ret;
if (!init_count) {
init_count = 1;
ret = arc_clockevent_setup(np);
} else {
ret = arc_cs_setup_timer1(np);
}
return ret;
}
TIMER_OF_DECLARE(arc_clkevt, "snps,arc-timer", arc_of_timer_init);
File diff suppressed because it is too large. Load diff
+343
View File
@@ -0,0 +1,343 @@
/*
* drivers/clocksource/arm_global_timer.c
*
* Copyright (C) 2013 STMicroelectronics (R&D) Limited.
* Author: Stuart Menefy <stuart.menefy@st.com>
* Author: Srinivas Kandagatla <srinivas.kandagatla@st.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/cpu.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/sched_clock.h>
#include <asm/cputype.h>
#define GT_COUNTER0 0x00
#define GT_COUNTER1 0x04
#define GT_CONTROL 0x08
#define GT_CONTROL_TIMER_ENABLE BIT(0) /* this bit is NOT banked */
#define GT_CONTROL_COMP_ENABLE BIT(1) /* banked */
#define GT_CONTROL_IRQ_ENABLE BIT(2) /* banked */
#define GT_CONTROL_AUTO_INC BIT(3) /* banked */
#define GT_INT_STATUS 0x0c
#define GT_INT_STATUS_EVENT_FLAG BIT(0)
#define GT_COMP0 0x10
#define GT_COMP1 0x14
#define GT_AUTO_INC 0x18
/*
* We are expecting to be clocked by the ARM peripheral clock.
*
* Note: it is assumed we are using a prescaler value of zero, so this is
* the units for all operations.
*/
static void __iomem *gt_base;
static unsigned long gt_clk_rate;
static int gt_ppi;
static struct clock_event_device __percpu *gt_evt;
/*
* To get the value from the Global Timer Counter register proceed as follows:
* 1. Read the upper 32-bit timer counter register
* 2. Read the lower 32-bit timer counter register
* 3. Read the upper 32-bit timer counter register again. If the value is
* different to the 32-bit upper value read previously, go back to step 2.
* Otherwise the 64-bit timer counter value is correct.
*/
static u64 notrace _gt_counter_read(void)
{
u64 counter;
u32 lower;
u32 upper, old_upper;
upper = readl_relaxed(gt_base + GT_COUNTER1);
do {
old_upper = upper;
lower = readl_relaxed(gt_base + GT_COUNTER0);
upper = readl_relaxed(gt_base + GT_COUNTER1);
} while (upper != old_upper);
counter = upper;
counter <<= 32;
counter |= lower;
return counter;
}
static u64 gt_counter_read(void)
{
return _gt_counter_read();
}
/**
* To ensure that updates to comparator value register do not set the
* Interrupt Status Register proceed as follows:
* 1. Clear the Comp Enable bit in the Timer Control Register.
* 2. Write the lower 32-bit Comparator Value Register.
* 3. Write the upper 32-bit Comparator Value Register.
* 4. Set the Comp Enable bit and, if necessary, the IRQ enable bit.
*/
static void gt_compare_set(unsigned long delta, int periodic)
{
u64 counter = gt_counter_read();
unsigned long ctrl;
counter += delta;
ctrl = GT_CONTROL_TIMER_ENABLE;
writel_relaxed(ctrl, gt_base + GT_CONTROL);
writel_relaxed(lower_32_bits(counter), gt_base + GT_COMP0);
writel_relaxed(upper_32_bits(counter), gt_base + GT_COMP1);
if (periodic) {
writel_relaxed(delta, gt_base + GT_AUTO_INC);
ctrl |= GT_CONTROL_AUTO_INC;
}
ctrl |= GT_CONTROL_COMP_ENABLE | GT_CONTROL_IRQ_ENABLE;
writel_relaxed(ctrl, gt_base + GT_CONTROL);
}
static int gt_clockevent_shutdown(struct clock_event_device *evt)
{
unsigned long ctrl;
ctrl = readl(gt_base + GT_CONTROL);
ctrl &= ~(GT_CONTROL_COMP_ENABLE | GT_CONTROL_IRQ_ENABLE |
GT_CONTROL_AUTO_INC);
writel(ctrl, gt_base + GT_CONTROL);
return 0;
}
static int gt_clockevent_set_periodic(struct clock_event_device *evt)
{
gt_compare_set(DIV_ROUND_CLOSEST(gt_clk_rate, HZ), 1);
return 0;
}
static int gt_clockevent_set_next_event(unsigned long evt,
struct clock_event_device *unused)
{
gt_compare_set(evt, 0);
return 0;
}
static irqreturn_t gt_clockevent_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
if (!(readl_relaxed(gt_base + GT_INT_STATUS) &
GT_INT_STATUS_EVENT_FLAG))
return IRQ_NONE;
/**
* ERRATA 740657( Global Timer can send 2 interrupts for
* the same event in single-shot mode)
* Workaround:
* Either disable single-shot mode.
* Or
* Modify the Interrupt Handler to avoid the
* offending sequence. This is achieved by clearing
* the Global Timer flag _after_ having incremented
* the Comparator register value to a higher value.
*/
if (clockevent_state_oneshot(evt))
gt_compare_set(ULONG_MAX, 0);
writel_relaxed(GT_INT_STATUS_EVENT_FLAG, gt_base + GT_INT_STATUS);
evt->event_handler(evt);
return IRQ_HANDLED;
}
static int gt_starting_cpu(unsigned int cpu)
{
struct clock_event_device *clk = this_cpu_ptr(gt_evt);
clk->name = "arm_global_timer";
clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT |
CLOCK_EVT_FEAT_PERCPU;
clk->set_state_shutdown = gt_clockevent_shutdown;
clk->set_state_periodic = gt_clockevent_set_periodic;
clk->set_state_oneshot = gt_clockevent_shutdown;
clk->set_state_oneshot_stopped = gt_clockevent_shutdown;
clk->set_next_event = gt_clockevent_set_next_event;
clk->cpumask = cpumask_of(cpu);
clk->rating = 300;
clk->irq = gt_ppi;
clockevents_config_and_register(clk, gt_clk_rate,
1, 0xffffffff);
enable_percpu_irq(clk->irq, IRQ_TYPE_NONE);
return 0;
}
static int gt_dying_cpu(unsigned int cpu)
{
struct clock_event_device *clk = this_cpu_ptr(gt_evt);
gt_clockevent_shutdown(clk);
disable_percpu_irq(clk->irq);
return 0;
}
static u64 gt_clocksource_read(struct clocksource *cs)
{
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 = {
.name = "arm_global_timer",
.rating = 300,
.read = gt_clocksource_read,
.mask = CLOCKSOURCE_MASK(64),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
.resume = gt_resume,
};
#ifdef CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
static u64 notrace gt_sched_clock_read(void)
{
return _gt_counter_read();
}
#endif
static unsigned long gt_read_long(void)
{
return readl_relaxed(gt_base + GT_COUNTER0);
}
static struct delay_timer gt_delay_timer = {
.read_current_timer = gt_read_long,
};
static void __init gt_delay_timer_init(void)
{
gt_delay_timer.freq = gt_clk_rate;
register_current_timer_delay(&gt_delay_timer);
}
static int __init gt_clocksource_init(void)
{
writel(0, gt_base + GT_CONTROL);
writel(0, gt_base + GT_COUNTER0);
writel(0, gt_base + GT_COUNTER1);
/* enables timer on all the cores */
writel(GT_CONTROL_TIMER_ENABLE, gt_base + GT_CONTROL);
#ifdef CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
sched_clock_register(gt_sched_clock_read, 64, gt_clk_rate);
#endif
return clocksource_register_hz(&gt_clocksource, gt_clk_rate);
}
static int __init global_timer_of_register(struct device_node *np)
{
struct clk *gt_clk;
int err = 0;
/*
* In A9 r2p0 the comparators for each processor with the global timer
* fire when the timer value is greater than or equal to. In previous
* revisions the comparators fired when the timer value was equal to.
*/
if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9
&& (read_cpuid_id() & 0xf0000f) < 0x200000) {
pr_warn("global-timer: non support for this cpu version.\n");
return -ENOSYS;
}
gt_ppi = irq_of_parse_and_map(np, 0);
if (!gt_ppi) {
pr_warn("global-timer: unable to parse irq\n");
return -EINVAL;
}
gt_base = of_iomap(np, 0);
if (!gt_base) {
pr_warn("global-timer: invalid base address\n");
return -ENXIO;
}
gt_clk = of_clk_get(np, 0);
if (!IS_ERR(gt_clk)) {
err = clk_prepare_enable(gt_clk);
if (err)
goto out_unmap;
} else {
pr_warn("global-timer: clk not found\n");
err = -EINVAL;
goto out_unmap;
}
gt_clk_rate = clk_get_rate(gt_clk);
gt_evt = alloc_percpu(struct clock_event_device);
if (!gt_evt) {
pr_warn("global-timer: can't allocate memory\n");
err = -ENOMEM;
goto out_clk;
}
err = request_percpu_irq(gt_ppi, gt_clockevent_interrupt,
"gt", gt_evt);
if (err) {
pr_warn("global-timer: can't register interrupt %d (%d)\n",
gt_ppi, err);
goto out_free;
}
/* Register and immediately configure the timer on the boot CPU */
err = gt_clocksource_init();
if (err)
goto out_irq;
err = cpuhp_setup_state(CPUHP_AP_ARM_GLOBAL_TIMER_STARTING,
"clockevents/arm/global_timer:starting",
gt_starting_cpu, gt_dying_cpu);
if (err)
goto out_irq;
gt_delay_timer_init();
return 0;
out_irq:
free_percpu_irq(gt_ppi, gt_evt);
out_free:
free_percpu(gt_evt);
out_clk:
clk_disable_unprepare(gt_clk);
out_unmap:
iounmap(gt_base);
WARN(err, "ARM Global timer register failed (%d)\n", err);
return err;
}
/* Only tested on r2p2 and r3p0 */
TIMER_OF_DECLARE(arm_gt, "arm,cortex-a9-global-timer",
global_timer_of_register);
+86
View File
@@ -0,0 +1,86 @@
/*
* Copyright (C) Maxime Coquelin 2015
* Author: Maxime Coquelin <mcoquelin.stm32@gmail.com>
* License terms: GNU General Public License (GPL), version 2
*/
#include <linux/kernel.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/clk.h>
#include <linux/bitops.h>
#define SYST_CSR 0x00
#define SYST_RVR 0x04
#define SYST_CVR 0x08
#define SYST_CALIB 0x0c
#define SYST_CSR_ENABLE BIT(0)
#define SYSTICK_LOAD_RELOAD_MASK 0x00FFFFFF
static int __init system_timer_of_register(struct device_node *np)
{
struct clk *clk = NULL;
void __iomem *base;
u32 rate;
int ret;
base = of_iomap(np, 0);
if (!base) {
pr_warn("system-timer: invalid base address\n");
return -ENXIO;
}
ret = of_property_read_u32(np, "clock-frequency", &rate);
if (ret) {
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
goto out_unmap;
}
ret = clk_prepare_enable(clk);
if (ret)
goto out_clk_put;
rate = clk_get_rate(clk);
if (!rate) {
ret = -EINVAL;
goto out_clk_disable;
}
}
writel_relaxed(SYSTICK_LOAD_RELOAD_MASK, base + SYST_RVR);
writel_relaxed(SYST_CSR_ENABLE, base + SYST_CSR);
ret = clocksource_mmio_init(base + SYST_CVR, "arm_system_timer", rate,
200, 24, clocksource_mmio_readl_down);
if (ret) {
pr_err("failed to init clocksource (%d)\n", ret);
if (clk)
goto out_clk_disable;
else
goto out_unmap;
}
pr_info("ARM System timer initialized as clocksource\n");
return 0;
out_clk_disable:
clk_disable_unprepare(clk);
out_clk_put:
clk_put(clk);
out_unmap:
iounmap(base);
pr_warn("ARM System timer register failed (%d)\n", ret);
return ret;
}
TIMER_OF_DECLARE(arm_systick, "arm,armv7m-systick",
system_timer_of_register);
+246
View File
@@ -0,0 +1,246 @@
/*
* Copyright (C) 2014 Oleksij Rempel <linux@rempel-privat.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/clk.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/bitops.h>
#define DRIVER_NAME "asm9260-timer"
/*
* this device provide 4 offsets for each register:
* 0x0 - plain read write mode
* 0x4 - set mode, OR logic.
* 0x8 - clr mode, XOR logic.
* 0xc - togle mode.
*/
#define SET_REG 4
#define CLR_REG 8
#define HW_IR 0x0000 /* RW. Interrupt */
#define BM_IR_CR0 BIT(4)
#define BM_IR_MR3 BIT(3)
#define BM_IR_MR2 BIT(2)
#define BM_IR_MR1 BIT(1)
#define BM_IR_MR0 BIT(0)
#define HW_TCR 0x0010 /* RW. Timer controller */
/* BM_C*_RST
* Timer Counter and the Prescale Counter are synchronously reset on the
* next positive edge of PCLK. The counters remain reset until TCR[1] is
* returned to zero. */
#define BM_C3_RST BIT(7)
#define BM_C2_RST BIT(6)
#define BM_C1_RST BIT(5)
#define BM_C0_RST BIT(4)
/* BM_C*_EN
* 1 - Timer Counter and Prescale Counter are enabled for counting
* 0 - counters are disabled */
#define BM_C3_EN BIT(3)
#define BM_C2_EN BIT(2)
#define BM_C1_EN BIT(1)
#define BM_C0_EN BIT(0)
#define HW_DIR 0x0020 /* RW. Direction? */
/* 00 - count up
* 01 - count down
* 10 - ?? 2^n/2 */
#define BM_DIR_COUNT_UP 0
#define BM_DIR_COUNT_DOWN 1
#define BM_DIR0_SHIFT 0
#define BM_DIR1_SHIFT 4
#define BM_DIR2_SHIFT 8
#define BM_DIR3_SHIFT 12
#define BM_DIR_DEFAULT (BM_DIR_COUNT_UP << BM_DIR0_SHIFT | \
BM_DIR_COUNT_UP << BM_DIR1_SHIFT | \
BM_DIR_COUNT_UP << BM_DIR2_SHIFT | \
BM_DIR_COUNT_UP << BM_DIR3_SHIFT)
#define HW_TC0 0x0030 /* RO. Timer counter 0 */
/* HW_TC*. Timer counter owerflow (0xffff.ffff to 0x0000.0000) do not generate
* interrupt. This registers can be used to detect overflow */
#define HW_TC1 0x0040
#define HW_TC2 0x0050
#define HW_TC3 0x0060
#define HW_PR 0x0070 /* RW. prescaler */
#define BM_PR_DISABLE 0
#define HW_PC 0x0080 /* RO. Prescaler counter */
#define HW_MCR 0x0090 /* RW. Match control */
/* enable interrupt on match */
#define BM_MCR_INT_EN(n) (1 << (n * 3 + 0))
/* enable TC reset on match */
#define BM_MCR_RES_EN(n) (1 << (n * 3 + 1))
/* enable stop TC on match */
#define BM_MCR_STOP_EN(n) (1 << (n * 3 + 2))
#define HW_MR0 0x00a0 /* RW. Match reg */
#define HW_MR1 0x00b0
#define HW_MR2 0x00C0
#define HW_MR3 0x00D0
#define HW_CTCR 0x0180 /* Counter control */
#define BM_CTCR0_SHIFT 0
#define BM_CTCR1_SHIFT 2
#define BM_CTCR2_SHIFT 4
#define BM_CTCR3_SHIFT 6
#define BM_CTCR_TM 0 /* Timer mode. Every rising PCLK edge. */
#define BM_CTCR_DEFAULT (BM_CTCR_TM << BM_CTCR0_SHIFT | \
BM_CTCR_TM << BM_CTCR1_SHIFT | \
BM_CTCR_TM << BM_CTCR2_SHIFT | \
BM_CTCR_TM << BM_CTCR3_SHIFT)
static struct asm9260_timer_priv {
void __iomem *base;
unsigned long ticks_per_jiffy;
} priv;
static int asm9260_timer_set_next_event(unsigned long delta,
struct clock_event_device *evt)
{
/* configure match count for TC0 */
writel_relaxed(delta, priv.base + HW_MR0);
/* enable TC0 */
writel_relaxed(BM_C0_EN, priv.base + HW_TCR + SET_REG);
return 0;
}
static inline void __asm9260_timer_shutdown(struct clock_event_device *evt)
{
/* stop timer0 */
writel_relaxed(BM_C0_EN, priv.base + HW_TCR + CLR_REG);
}
static int asm9260_timer_shutdown(struct clock_event_device *evt)
{
__asm9260_timer_shutdown(evt);
return 0;
}
static int asm9260_timer_set_oneshot(struct clock_event_device *evt)
{
__asm9260_timer_shutdown(evt);
/* enable reset and stop on match */
writel_relaxed(BM_MCR_RES_EN(0) | BM_MCR_STOP_EN(0),
priv.base + HW_MCR + SET_REG);
return 0;
}
static int asm9260_timer_set_periodic(struct clock_event_device *evt)
{
__asm9260_timer_shutdown(evt);
/* disable reset and stop on match */
writel_relaxed(BM_MCR_RES_EN(0) | BM_MCR_STOP_EN(0),
priv.base + HW_MCR + CLR_REG);
/* configure match count for TC0 */
writel_relaxed(priv.ticks_per_jiffy, priv.base + HW_MR0);
/* enable TC0 */
writel_relaxed(BM_C0_EN, priv.base + HW_TCR + SET_REG);
return 0;
}
static struct clock_event_device event_dev = {
.name = DRIVER_NAME,
.rating = 200,
.features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT,
.set_next_event = asm9260_timer_set_next_event,
.set_state_shutdown = asm9260_timer_shutdown,
.set_state_periodic = asm9260_timer_set_periodic,
.set_state_oneshot = asm9260_timer_set_oneshot,
.tick_resume = asm9260_timer_shutdown,
};
static irqreturn_t asm9260_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
evt->event_handler(evt);
writel_relaxed(BM_IR_MR0, priv.base + HW_IR);
return IRQ_HANDLED;
}
/*
* ---------------------------------------------------------------------------
* Timer initialization
* ---------------------------------------------------------------------------
*/
static int __init asm9260_timer_init(struct device_node *np)
{
int irq;
struct clk *clk;
int ret;
unsigned long rate;
priv.base = of_io_request_and_map(np, 0, np->name);
if (IS_ERR(priv.base)) {
pr_err("%s: unable to map resource\n", np->name);
return PTR_ERR(priv.base);
}
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
pr_err("Failed to get clk!\n");
return PTR_ERR(clk);
}
ret = clk_prepare_enable(clk);
if (ret) {
pr_err("Failed to enable clk!\n");
return ret;
}
irq = irq_of_parse_and_map(np, 0);
ret = request_irq(irq, asm9260_timer_interrupt, IRQF_TIMER,
DRIVER_NAME, &event_dev);
if (ret) {
pr_err("Failed to setup irq!\n");
return ret;
}
/* set all timers for count-up */
writel_relaxed(BM_DIR_DEFAULT, priv.base + HW_DIR);
/* disable divider */
writel_relaxed(BM_PR_DISABLE, priv.base + HW_PR);
/* make sure all timers use every rising PCLK edge. */
writel_relaxed(BM_CTCR_DEFAULT, priv.base + HW_CTCR);
/* enable interrupt for TC0 and clean setting for all other lines */
writel_relaxed(BM_MCR_INT_EN(0) , priv.base + HW_MCR);
rate = clk_get_rate(clk);
clocksource_mmio_init(priv.base + HW_TC1, DRIVER_NAME, rate,
200, 32, clocksource_mmio_readl_up);
/* Seems like we can't use counter without match register even if
* actions for MR are disabled. So, set MR to max value. */
writel_relaxed(0xffffffff, priv.base + HW_MR1);
/* enable TC1 */
writel_relaxed(BM_C1_EN, priv.base + HW_TCR + SET_REG);
priv.ticks_per_jiffy = DIV_ROUND_CLOSEST(rate, HZ);
event_dev.cpumask = cpumask_of(0);
clockevents_config_and_register(&event_dev, rate, 0x2c00, 0xfffffffe);
return 0;
}
TIMER_OF_DECLARE(asm9260_timer, "alphascale,asm9260-timer",
asm9260_timer_init);
+154
View File
@@ -0,0 +1,154 @@
/*
* Copyright 2012 Simon Arlott
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/bitops.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/sched_clock.h>
#include <asm/irq.h>
#define REG_CONTROL 0x00
#define REG_COUNTER_LO 0x04
#define REG_COUNTER_HI 0x08
#define REG_COMPARE(n) (0x0c + (n) * 4)
#define MAX_TIMER 3
#define DEFAULT_TIMER 3
struct bcm2835_timer {
void __iomem *control;
void __iomem *compare;
int match_mask;
struct clock_event_device evt;
struct irqaction act;
};
static void __iomem *system_clock __read_mostly;
static u64 notrace bcm2835_sched_read(void)
{
return readl_relaxed(system_clock);
}
static int bcm2835_time_set_next_event(unsigned long event,
struct clock_event_device *evt_dev)
{
struct bcm2835_timer *timer = container_of(evt_dev,
struct bcm2835_timer, evt);
writel_relaxed(readl_relaxed(system_clock) + event,
timer->compare);
return 0;
}
static irqreturn_t bcm2835_time_interrupt(int irq, void *dev_id)
{
struct bcm2835_timer *timer = dev_id;
void (*event_handler)(struct clock_event_device *);
if (readl_relaxed(timer->control) & timer->match_mask) {
writel_relaxed(timer->match_mask, timer->control);
event_handler = READ_ONCE(timer->evt.event_handler);
if (event_handler)
event_handler(&timer->evt);
return IRQ_HANDLED;
} else {
return IRQ_NONE;
}
}
static int __init bcm2835_timer_init(struct device_node *node)
{
void __iomem *base;
u32 freq;
int irq, ret;
struct bcm2835_timer *timer;
base = of_iomap(node, 0);
if (!base) {
pr_err("Can't remap registers\n");
return -ENXIO;
}
ret = of_property_read_u32(node, "clock-frequency", &freq);
if (ret) {
pr_err("Can't read clock-frequency\n");
goto err_iounmap;
}
system_clock = base + REG_COUNTER_LO;
sched_clock_register(bcm2835_sched_read, 32, freq);
clocksource_mmio_init(base + REG_COUNTER_LO, node->name,
freq, 300, 32, clocksource_mmio_readl_up);
irq = irq_of_parse_and_map(node, DEFAULT_TIMER);
if (irq <= 0) {
pr_err("Can't parse IRQ\n");
ret = -EINVAL;
goto err_iounmap;
}
timer = kzalloc(sizeof(*timer), GFP_KERNEL);
if (!timer) {
ret = -ENOMEM;
goto err_iounmap;
}
timer->control = base + REG_CONTROL;
timer->compare = base + REG_COMPARE(DEFAULT_TIMER);
timer->match_mask = BIT(DEFAULT_TIMER);
timer->evt.name = node->name;
timer->evt.rating = 300;
timer->evt.features = CLOCK_EVT_FEAT_ONESHOT;
timer->evt.set_next_event = bcm2835_time_set_next_event;
timer->evt.cpumask = cpumask_of(0);
timer->act.name = node->name;
timer->act.flags = IRQF_TIMER | IRQF_SHARED;
timer->act.dev_id = timer;
timer->act.handler = bcm2835_time_interrupt;
ret = setup_irq(irq, &timer->act);
if (ret) {
pr_err("Can't set up timer IRQ\n");
goto err_timer_free;
}
clockevents_config_and_register(&timer->evt, freq, 0xf, 0xffffffff);
pr_info("bcm2835: system timer (irq = %d)\n", irq);
return 0;
err_timer_free:
kfree(timer);
err_iounmap:
iounmap(base);
return ret;
}
TIMER_OF_DECLARE(bcm2835, "brcm,bcm2835-system-timer",
bcm2835_timer_init);
+206
View File
@@ -0,0 +1,206 @@
/*
* Copyright (C) 2012 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/clockchips.h>
#include <linux/types.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#define KONA_GPTIMER_STCS_OFFSET 0x00000000
#define KONA_GPTIMER_STCLO_OFFSET 0x00000004
#define KONA_GPTIMER_STCHI_OFFSET 0x00000008
#define KONA_GPTIMER_STCM0_OFFSET 0x0000000C
#define KONA_GPTIMER_STCS_TIMER_MATCH_SHIFT 0
#define KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT 4
struct kona_bcm_timers {
int tmr_irq;
void __iomem *tmr_regs;
};
static struct kona_bcm_timers timers;
static u32 arch_timer_rate;
/*
* We use the peripheral timers for system tick, the cpu global timer for
* profile tick
*/
static void kona_timer_disable_and_clear(void __iomem *base)
{
uint32_t reg;
/*
* clear and disable interrupts
* We are using compare/match register 0 for our system interrupts
*/
reg = readl(base + KONA_GPTIMER_STCS_OFFSET);
/* Clear compare (0) interrupt */
reg |= 1 << KONA_GPTIMER_STCS_TIMER_MATCH_SHIFT;
/* disable compare */
reg &= ~(1 << KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT);
writel(reg, base + KONA_GPTIMER_STCS_OFFSET);
}
static int
kona_timer_get_counter(void __iomem *timer_base, uint32_t *msw, uint32_t *lsw)
{
int loop_limit = 3;
/*
* Read 64-bit free running counter
* 1. Read hi-word
* 2. Read low-word
* 3. Read hi-word again
* 4.1
* if new hi-word is not equal to previously read hi-word, then
* start from #1
* 4.2
* if new hi-word is equal to previously read hi-word then stop.
*/
do {
*msw = readl(timer_base + KONA_GPTIMER_STCHI_OFFSET);
*lsw = readl(timer_base + KONA_GPTIMER_STCLO_OFFSET);
if (*msw == readl(timer_base + KONA_GPTIMER_STCHI_OFFSET))
break;
} while (--loop_limit);
if (!loop_limit) {
pr_err("bcm_kona_timer: getting counter failed.\n");
pr_err(" Timer will be impacted\n");
return -ETIMEDOUT;
}
return 0;
}
static int kona_timer_set_next_event(unsigned long clc,
struct clock_event_device *unused)
{
/*
* timer (0) is disabled by the timer interrupt already
* so, here we reload the next event value and re-enable
* the timer.
*
* This way, we are potentially losing the time between
* timer-interrupt->set_next_event. CPU local timers, when
* they come in should get rid of skew.
*/
uint32_t lsw, msw;
uint32_t reg;
int ret;
ret = kona_timer_get_counter(timers.tmr_regs, &msw, &lsw);
if (ret)
return ret;
/* Load the "next" event tick value */
writel(lsw + clc, timers.tmr_regs + KONA_GPTIMER_STCM0_OFFSET);
/* Enable compare */
reg = readl(timers.tmr_regs + KONA_GPTIMER_STCS_OFFSET);
reg |= (1 << KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT);
writel(reg, timers.tmr_regs + KONA_GPTIMER_STCS_OFFSET);
return 0;
}
static int kona_timer_shutdown(struct clock_event_device *evt)
{
kona_timer_disable_and_clear(timers.tmr_regs);
return 0;
}
static struct clock_event_device kona_clockevent_timer = {
.name = "timer 1",
.features = CLOCK_EVT_FEAT_ONESHOT,
.set_next_event = kona_timer_set_next_event,
.set_state_shutdown = kona_timer_shutdown,
.tick_resume = kona_timer_shutdown,
};
static void __init kona_timer_clockevents_init(void)
{
kona_clockevent_timer.cpumask = cpumask_of(0);
clockevents_config_and_register(&kona_clockevent_timer,
arch_timer_rate, 6, 0xffffffff);
}
static irqreturn_t kona_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = &kona_clockevent_timer;
kona_timer_disable_and_clear(timers.tmr_regs);
evt->event_handler(evt);
return IRQ_HANDLED;
}
static struct irqaction kona_timer_irq = {
.name = "Kona Timer Tick",
.flags = IRQF_TIMER,
.handler = kona_timer_interrupt,
};
static int __init kona_timer_init(struct device_node *node)
{
u32 freq;
struct clk *external_clk;
external_clk = of_clk_get_by_name(node, NULL);
if (!IS_ERR(external_clk)) {
arch_timer_rate = clk_get_rate(external_clk);
clk_prepare_enable(external_clk);
} else if (!of_property_read_u32(node, "clock-frequency", &freq)) {
arch_timer_rate = freq;
} else {
pr_err("Kona Timer v1 unable to determine clock-frequency\n");
return -EINVAL;
}
/* Setup IRQ numbers */
timers.tmr_irq = irq_of_parse_and_map(node, 0);
/* Setup IO addresses */
timers.tmr_regs = of_iomap(node, 0);
kona_timer_disable_and_clear(timers.tmr_regs);
kona_timer_clockevents_init();
setup_irq(timers.tmr_irq, &kona_timer_irq);
kona_timer_set_next_event((arch_timer_rate / HZ), NULL);
return 0;
}
TIMER_OF_DECLARE(brcm_kona, "brcm,kona-timer", kona_timer_init);
/*
* bcm,kona-timer is deprecated by brcm,kona-timer
* being kept here for driver compatibility
*/
TIMER_OF_DECLARE(bcm_kona, "bcm,kona-timer", kona_timer_init);
+543
View File
@@ -0,0 +1,543 @@
/*
* This file contains driver for the Cadence Triple Timer Counter Rev 06
*
* Copyright (C) 2011-2013 Xilinx
*
* based on arch/mips/kernel/time.c timer driver
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/sched_clock.h>
/*
* This driver configures the 2 16/32-bit count-up timers as follows:
*
* T1: Timer 1, clocksource for generic timekeeping
* T2: Timer 2, clockevent source for hrtimers
* T3: Timer 3, <unused>
*
* The input frequency to the timer module for emulation is 2.5MHz which is
* common to all the timer channels (T1, T2, and T3). With a pre-scaler of 32,
* the timers are clocked at 78.125KHz (12.8 us resolution).
* The input frequency to the timer module in silicon is configurable and
* obtained from device tree. The pre-scaler of 32 is used.
*/
/*
* Timer Register Offset Definitions of Timer 1, Increment base address by 4
* and use same offsets for Timer 2
*/
#define TTC_CLK_CNTRL_OFFSET 0x00 /* Clock Control Reg, RW */
#define TTC_CNT_CNTRL_OFFSET 0x0C /* Counter Control Reg, RW */
#define TTC_COUNT_VAL_OFFSET 0x18 /* Counter Value Reg, RO */
#define TTC_INTR_VAL_OFFSET 0x24 /* Interval Count Reg, RW */
#define TTC_ISR_OFFSET 0x54 /* Interrupt Status Reg, RO */
#define TTC_IER_OFFSET 0x60 /* Interrupt Enable Reg, RW */
#define TTC_CNT_CNTRL_DISABLE_MASK 0x1
#define TTC_CLK_CNTRL_CSRC_MASK (1 << 5) /* clock source */
#define TTC_CLK_CNTRL_PSV_MASK 0x1e
#define TTC_CLK_CNTRL_PSV_SHIFT 1
/*
* Setup the timers to use pre-scaling, using a fixed value for now that will
* work across most input frequency, but it may need to be more dynamic
*/
#define PRESCALE_EXPONENT 11 /* 2 ^ PRESCALE_EXPONENT = PRESCALE */
#define PRESCALE 2048 /* The exponent must match this */
#define CLK_CNTRL_PRESCALE ((PRESCALE_EXPONENT - 1) << 1)
#define CLK_CNTRL_PRESCALE_EN 1
#define CNT_CNTRL_RESET (1 << 4)
#define MAX_F_ERR 50
/**
* struct ttc_timer - This definition defines local timer structure
*
* @base_addr: Base address of timer
* @freq: Timer input clock frequency
* @clk: Associated clock source
* @clk_rate_change_nb Notifier block for clock rate changes
*/
struct ttc_timer {
void __iomem *base_addr;
unsigned long freq;
struct clk *clk;
struct notifier_block clk_rate_change_nb;
};
#define to_ttc_timer(x) \
container_of(x, struct ttc_timer, clk_rate_change_nb)
struct ttc_timer_clocksource {
u32 scale_clk_ctrl_reg_old;
u32 scale_clk_ctrl_reg_new;
struct ttc_timer ttc;
struct clocksource cs;
};
#define to_ttc_timer_clksrc(x) \
container_of(x, struct ttc_timer_clocksource, cs)
struct ttc_timer_clockevent {
struct ttc_timer ttc;
struct clock_event_device ce;
};
#define to_ttc_timer_clkevent(x) \
container_of(x, struct ttc_timer_clockevent, ce)
static void __iomem *ttc_sched_clock_val_reg;
/**
* ttc_set_interval - Set the timer interval value
*
* @timer: Pointer to the timer instance
* @cycles: Timer interval ticks
**/
static void ttc_set_interval(struct ttc_timer *timer,
unsigned long cycles)
{
u32 ctrl_reg;
/* Disable the counter, set the counter value and re-enable counter */
ctrl_reg = readl_relaxed(timer->base_addr + TTC_CNT_CNTRL_OFFSET);
ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK;
writel_relaxed(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);
writel_relaxed(cycles, timer->base_addr + TTC_INTR_VAL_OFFSET);
/*
* Reset the counter (0x10) so that it starts from 0, one-shot
* mode makes this needed for timing to be right.
*/
ctrl_reg |= CNT_CNTRL_RESET;
ctrl_reg &= ~TTC_CNT_CNTRL_DISABLE_MASK;
writel_relaxed(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);
}
/**
* ttc_clock_event_interrupt - Clock event timer interrupt handler
*
* @irq: IRQ number of the Timer
* @dev_id: void pointer to the ttc_timer instance
*
* returns: Always IRQ_HANDLED - success
**/
static irqreturn_t ttc_clock_event_interrupt(int irq, void *dev_id)
{
struct ttc_timer_clockevent *ttce = dev_id;
struct ttc_timer *timer = &ttce->ttc;
/* Acknowledge the interrupt and call event handler */
readl_relaxed(timer->base_addr + TTC_ISR_OFFSET);
ttce->ce.event_handler(&ttce->ce);
return IRQ_HANDLED;
}
/**
* __ttc_clocksource_read - Reads the timer counter register
*
* returns: Current timer counter register value
**/
static u64 __ttc_clocksource_read(struct clocksource *cs)
{
struct ttc_timer *timer = &to_ttc_timer_clksrc(cs)->ttc;
return (u64)readl_relaxed(timer->base_addr +
TTC_COUNT_VAL_OFFSET);
}
static u64 notrace ttc_sched_clock_read(void)
{
return readl_relaxed(ttc_sched_clock_val_reg);
}
/**
* ttc_set_next_event - Sets the time interval for next event
*
* @cycles: Timer interval ticks
* @evt: Address of clock event instance
*
* returns: Always 0 - success
**/
static int ttc_set_next_event(unsigned long cycles,
struct clock_event_device *evt)
{
struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt);
struct ttc_timer *timer = &ttce->ttc;
ttc_set_interval(timer, cycles);
return 0;
}
/**
* ttc_set_{shutdown|oneshot|periodic} - Sets the state of timer
*
* @evt: Address of clock event instance
**/
static int ttc_shutdown(struct clock_event_device *evt)
{
struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt);
struct ttc_timer *timer = &ttce->ttc;
u32 ctrl_reg;
ctrl_reg = readl_relaxed(timer->base_addr + TTC_CNT_CNTRL_OFFSET);
ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK;
writel_relaxed(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);
return 0;
}
static int ttc_set_periodic(struct clock_event_device *evt)
{
struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt);
struct ttc_timer *timer = &ttce->ttc;
ttc_set_interval(timer,
DIV_ROUND_CLOSEST(ttce->ttc.freq, PRESCALE * HZ));
return 0;
}
static int ttc_resume(struct clock_event_device *evt)
{
struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt);
struct ttc_timer *timer = &ttce->ttc;
u32 ctrl_reg;
ctrl_reg = readl_relaxed(timer->base_addr + TTC_CNT_CNTRL_OFFSET);
ctrl_reg &= ~TTC_CNT_CNTRL_DISABLE_MASK;
writel_relaxed(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);
return 0;
}
static int ttc_rate_change_clocksource_cb(struct notifier_block *nb,
unsigned long event, void *data)
{
struct clk_notifier_data *ndata = data;
struct ttc_timer *ttc = to_ttc_timer(nb);
struct ttc_timer_clocksource *ttccs = container_of(ttc,
struct ttc_timer_clocksource, ttc);
switch (event) {
case PRE_RATE_CHANGE:
{
u32 psv;
unsigned long factor, rate_low, rate_high;
if (ndata->new_rate > ndata->old_rate) {
factor = DIV_ROUND_CLOSEST(ndata->new_rate,
ndata->old_rate);
rate_low = ndata->old_rate;
rate_high = ndata->new_rate;
} else {
factor = DIV_ROUND_CLOSEST(ndata->old_rate,
ndata->new_rate);
rate_low = ndata->new_rate;
rate_high = ndata->old_rate;
}
if (!is_power_of_2(factor))
return NOTIFY_BAD;
if (abs(rate_high - (factor * rate_low)) > MAX_F_ERR)
return NOTIFY_BAD;
factor = __ilog2_u32(factor);
/*
* store timer clock ctrl register so we can restore it in case
* of an abort.
*/
ttccs->scale_clk_ctrl_reg_old =
readl_relaxed(ttccs->ttc.base_addr +
TTC_CLK_CNTRL_OFFSET);
psv = (ttccs->scale_clk_ctrl_reg_old &
TTC_CLK_CNTRL_PSV_MASK) >>
TTC_CLK_CNTRL_PSV_SHIFT;
if (ndata->new_rate < ndata->old_rate)
psv -= factor;
else
psv += factor;
/* prescaler within legal range? */
if (psv & ~(TTC_CLK_CNTRL_PSV_MASK >> TTC_CLK_CNTRL_PSV_SHIFT))
return NOTIFY_BAD;
ttccs->scale_clk_ctrl_reg_new = ttccs->scale_clk_ctrl_reg_old &
~TTC_CLK_CNTRL_PSV_MASK;
ttccs->scale_clk_ctrl_reg_new |= psv << TTC_CLK_CNTRL_PSV_SHIFT;
/* scale down: adjust divider in post-change notification */
if (ndata->new_rate < ndata->old_rate)
return NOTIFY_DONE;
/* scale up: adjust divider now - before frequency change */
writel_relaxed(ttccs->scale_clk_ctrl_reg_new,
ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
break;
}
case POST_RATE_CHANGE:
/* scale up: pre-change notification did the adjustment */
if (ndata->new_rate > ndata->old_rate)
return NOTIFY_OK;
/* scale down: adjust divider now - after frequency change */
writel_relaxed(ttccs->scale_clk_ctrl_reg_new,
ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
break;
case ABORT_RATE_CHANGE:
/* we have to undo the adjustment in case we scale up */
if (ndata->new_rate < ndata->old_rate)
return NOTIFY_OK;
/* restore original register value */
writel_relaxed(ttccs->scale_clk_ctrl_reg_old,
ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
/* fall through */
default:
return NOTIFY_DONE;
}
return NOTIFY_DONE;
}
static int __init ttc_setup_clocksource(struct clk *clk, void __iomem *base,
u32 timer_width)
{
struct ttc_timer_clocksource *ttccs;
int err;
ttccs = kzalloc(sizeof(*ttccs), GFP_KERNEL);
if (!ttccs)
return -ENOMEM;
ttccs->ttc.clk = clk;
err = clk_prepare_enable(ttccs->ttc.clk);
if (err) {
kfree(ttccs);
return err;
}
ttccs->ttc.freq = clk_get_rate(ttccs->ttc.clk);
ttccs->ttc.clk_rate_change_nb.notifier_call =
ttc_rate_change_clocksource_cb;
ttccs->ttc.clk_rate_change_nb.next = NULL;
err = clk_notifier_register(ttccs->ttc.clk,
&ttccs->ttc.clk_rate_change_nb);
if (err)
pr_warn("Unable to register clock notifier.\n");
ttccs->ttc.base_addr = base;
ttccs->cs.name = "ttc_clocksource";
ttccs->cs.rating = 200;
ttccs->cs.read = __ttc_clocksource_read;
ttccs->cs.mask = CLOCKSOURCE_MASK(timer_width);
ttccs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
/*
* Setup the clock source counter to be an incrementing counter
* with no interrupt and it rolls over at 0xFFFF. Pre-scale
* it by 32 also. Let it start running now.
*/
writel_relaxed(0x0, ttccs->ttc.base_addr + TTC_IER_OFFSET);
writel_relaxed(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
writel_relaxed(CNT_CNTRL_RESET,
ttccs->ttc.base_addr + TTC_CNT_CNTRL_OFFSET);
err = clocksource_register_hz(&ttccs->cs, ttccs->ttc.freq / PRESCALE);
if (err) {
kfree(ttccs);
return err;
}
ttc_sched_clock_val_reg = base + TTC_COUNT_VAL_OFFSET;
sched_clock_register(ttc_sched_clock_read, timer_width,
ttccs->ttc.freq / PRESCALE);
return 0;
}
static int ttc_rate_change_clockevent_cb(struct notifier_block *nb,
unsigned long event, void *data)
{
struct clk_notifier_data *ndata = data;
struct ttc_timer *ttc = to_ttc_timer(nb);
struct ttc_timer_clockevent *ttcce = container_of(ttc,
struct ttc_timer_clockevent, ttc);
switch (event) {
case POST_RATE_CHANGE:
/* update cached frequency */
ttc->freq = ndata->new_rate;
clockevents_update_freq(&ttcce->ce, ndata->new_rate / PRESCALE);
/* fall through */
case PRE_RATE_CHANGE:
case ABORT_RATE_CHANGE:
default:
return NOTIFY_DONE;
}
}
static int __init ttc_setup_clockevent(struct clk *clk,
void __iomem *base, u32 irq)
{
struct ttc_timer_clockevent *ttcce;
int err;
ttcce = kzalloc(sizeof(*ttcce), GFP_KERNEL);
if (!ttcce)
return -ENOMEM;
ttcce->ttc.clk = clk;
err = clk_prepare_enable(ttcce->ttc.clk);
if (err)
goto out_kfree;
ttcce->ttc.clk_rate_change_nb.notifier_call =
ttc_rate_change_clockevent_cb;
ttcce->ttc.clk_rate_change_nb.next = NULL;
err = clk_notifier_register(ttcce->ttc.clk,
&ttcce->ttc.clk_rate_change_nb);
if (err) {
pr_warn("Unable to register clock notifier.\n");
goto out_kfree;
}
ttcce->ttc.freq = clk_get_rate(ttcce->ttc.clk);
ttcce->ttc.base_addr = base;
ttcce->ce.name = "ttc_clockevent";
ttcce->ce.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
ttcce->ce.set_next_event = ttc_set_next_event;
ttcce->ce.set_state_shutdown = ttc_shutdown;
ttcce->ce.set_state_periodic = ttc_set_periodic;
ttcce->ce.set_state_oneshot = ttc_shutdown;
ttcce->ce.tick_resume = ttc_resume;
ttcce->ce.rating = 200;
ttcce->ce.irq = irq;
ttcce->ce.cpumask = cpu_possible_mask;
/*
* Setup the clock event timer to be an interval timer which
* is prescaled by 32 using the interval interrupt. Leave it
* disabled for now.
*/
writel_relaxed(0x23, ttcce->ttc.base_addr + TTC_CNT_CNTRL_OFFSET);
writel_relaxed(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN,
ttcce->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
writel_relaxed(0x1, ttcce->ttc.base_addr + TTC_IER_OFFSET);
err = request_irq(irq, ttc_clock_event_interrupt,
IRQF_TIMER, ttcce->ce.name, ttcce);
if (err)
goto out_kfree;
clockevents_config_and_register(&ttcce->ce,
ttcce->ttc.freq / PRESCALE, 1, 0xfffe);
return 0;
out_kfree:
kfree(ttcce);
return err;
}
/**
* ttc_timer_init - Initialize the timer
*
* Initializes the timer hardware and register the clock source and clock event
* timers with Linux kernal timer framework
*/
static int __init ttc_timer_init(struct device_node *timer)
{
unsigned int irq;
void __iomem *timer_baseaddr;
struct clk *clk_cs, *clk_ce;
static int initialized;
int clksel, ret;
u32 timer_width = 16;
if (initialized)
return 0;
initialized = 1;
/*
* Get the 1st Triple Timer Counter (TTC) block from the device tree
* and use it. Note that the event timer uses the interrupt and it's the
* 2nd TTC hence the irq_of_parse_and_map(,1)
*/
timer_baseaddr = of_iomap(timer, 0);
if (!timer_baseaddr) {
pr_err("ERROR: invalid timer base address\n");
return -ENXIO;
}
irq = irq_of_parse_and_map(timer, 1);
if (irq <= 0) {
pr_err("ERROR: invalid interrupt number\n");
return -EINVAL;
}
of_property_read_u32(timer, "timer-width", &timer_width);
clksel = readl_relaxed(timer_baseaddr + TTC_CLK_CNTRL_OFFSET);
clksel = !!(clksel & TTC_CLK_CNTRL_CSRC_MASK);
clk_cs = of_clk_get(timer, clksel);
if (IS_ERR(clk_cs)) {
pr_err("ERROR: timer input clock not found\n");
return PTR_ERR(clk_cs);
}
clksel = readl_relaxed(timer_baseaddr + 4 + TTC_CLK_CNTRL_OFFSET);
clksel = !!(clksel & TTC_CLK_CNTRL_CSRC_MASK);
clk_ce = of_clk_get(timer, clksel);
if (IS_ERR(clk_ce)) {
pr_err("ERROR: timer input clock not found\n");
return PTR_ERR(clk_ce);
}
ret = ttc_setup_clocksource(clk_cs, timer_baseaddr, timer_width);
if (ret)
return ret;
ret = ttc_setup_clockevent(clk_ce, timer_baseaddr + 4, irq);
if (ret)
return ret;
pr_info("%s #0 at %p, irq=%d\n", timer->name, timer_baseaddr, irq);
return 0;
}
TIMER_OF_DECLARE(ttc, "cdns,ttc", ttc_timer_init);
+90
View File
@@ -0,0 +1,90 @@
/*
* Copyright (C) ST-Ericsson SA 2011
*
* License Terms: GNU General Public License v2
* Author: Mattias Wallin <mattias.wallin@stericsson.com> for ST-Ericsson
* Author: Sundar Iyer for ST-Ericsson
* sched_clock implementation is based on:
* plat-nomadik/timer.c Linus Walleij <linus.walleij@stericsson.com>
*
* DBx500-PRCMU Timer
* The PRCMU has 5 timers which are available in a always-on
* power domain. We use the Timer 4 for our always-on clock
* source on DB8500.
*/
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/clockchips.h>
#include <linux/sched_clock.h>
#define RATE_32K 32768
#define TIMER_MODE_CONTINOUS 0x1
#define TIMER_DOWNCOUNT_VAL 0xffffffff
#define PRCMU_TIMER_REF 0
#define PRCMU_TIMER_DOWNCOUNT 0x4
#define PRCMU_TIMER_MODE 0x8
#define SCHED_CLOCK_MIN_WRAP 131072 /* 2^32 / 32768 */
static void __iomem *clksrc_dbx500_timer_base;
static u64 notrace clksrc_dbx500_prcmu_read(struct clocksource *cs)
{
void __iomem *base = clksrc_dbx500_timer_base;
u32 count, count2;
do {
count = readl_relaxed(base + PRCMU_TIMER_DOWNCOUNT);
count2 = readl_relaxed(base + PRCMU_TIMER_DOWNCOUNT);
} while (count2 != count);
/* Negate because the timer is a decrementing counter */
return ~count;
}
static struct clocksource clocksource_dbx500_prcmu = {
.name = "dbx500-prcmu-timer",
.rating = 300,
.read = clksrc_dbx500_prcmu_read,
.mask = CLOCKSOURCE_MASK(32),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
#ifdef CONFIG_CLKSRC_DBX500_PRCMU_SCHED_CLOCK
static u64 notrace dbx500_prcmu_sched_clock_read(void)
{
if (unlikely(!clksrc_dbx500_timer_base))
return 0;
return clksrc_dbx500_prcmu_read(&clocksource_dbx500_prcmu);
}
#endif
static int __init clksrc_dbx500_prcmu_init(struct device_node *node)
{
clksrc_dbx500_timer_base = of_iomap(node, 0);
/*
* The A9 sub system expects the timer to be configured as
* a continous looping timer.
* The PRCMU should configure it but if it for some reason
* don't we do it here.
*/
if (readl(clksrc_dbx500_timer_base + PRCMU_TIMER_MODE) !=
TIMER_MODE_CONTINOUS) {
writel(TIMER_MODE_CONTINOUS,
clksrc_dbx500_timer_base + PRCMU_TIMER_MODE);
writel(TIMER_DOWNCOUNT_VAL,
clksrc_dbx500_timer_base + PRCMU_TIMER_REF);
}
#ifdef CONFIG_CLKSRC_DBX500_PRCMU_SCHED_CLOCK
sched_clock_register(dbx500_prcmu_sched_clock_read, 32, RATE_32K);
#endif
return clocksource_register_hz(&clocksource_dbx500_prcmu, RATE_32K);
}
TIMER_OF_DECLARE(dbx500_prcmu, "stericsson,db8500-prcmu-timer-4",
clksrc_dbx500_prcmu_init);
+135
View File
@@ -0,0 +1,135 @@
/*
* Clocksource using the Low Power Timer found in the Low Power Controller (LPC)
*
* Copyright (C) 2015 STMicroelectronics – All Rights Reserved
*
* Author(s): Francesco Virlinzi <francesco.virlinzi@st.com>
* Ajit Pal Singh <ajitpal.singh@st.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/clk.h>
#include <linux/clocksource.h>
#include <linux/init.h>
#include <linux/of_address.h>
#include <linux/sched_clock.h>
#include <linux/slab.h>
#include <dt-bindings/mfd/st-lpc.h>
/* Low Power Timer */
#define LPC_LPT_LSB_OFF 0x400
#define LPC_LPT_MSB_OFF 0x404
#define LPC_LPT_START_OFF 0x408
static struct st_clksrc_ddata {
struct clk *clk;
void __iomem *base;
} ddata;
static void __init st_clksrc_reset(void)
{
writel_relaxed(0, ddata.base + LPC_LPT_START_OFF);
writel_relaxed(0, ddata.base + LPC_LPT_MSB_OFF);
writel_relaxed(0, ddata.base + LPC_LPT_LSB_OFF);
writel_relaxed(1, ddata.base + LPC_LPT_START_OFF);
}
static u64 notrace st_clksrc_sched_clock_read(void)
{
return (u64)readl_relaxed(ddata.base + LPC_LPT_LSB_OFF);
}
static int __init st_clksrc_init(void)
{
unsigned long rate;
int ret;
st_clksrc_reset();
rate = clk_get_rate(ddata.clk);
sched_clock_register(st_clksrc_sched_clock_read, 32, rate);
ret = clocksource_mmio_init(ddata.base + LPC_LPT_LSB_OFF,
"clksrc-st-lpc", rate, 300, 32,
clocksource_mmio_readl_up);
if (ret) {
pr_err("clksrc-st-lpc: Failed to register clocksource\n");
return ret;
}
return 0;
}
static int __init st_clksrc_setup_clk(struct device_node *np)
{
struct clk *clk;
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
pr_err("clksrc-st-lpc: Failed to get LPC clock\n");
return PTR_ERR(clk);
}
if (clk_prepare_enable(clk)) {
pr_err("clksrc-st-lpc: Failed to enable LPC clock\n");
return -EINVAL;
}
if (!clk_get_rate(clk)) {
pr_err("clksrc-st-lpc: Failed to get LPC clock rate\n");
clk_disable_unprepare(clk);
return -EINVAL;
}
ddata.clk = clk;
return 0;
}
static int __init st_clksrc_of_register(struct device_node *np)
{
int ret;
uint32_t mode;
ret = of_property_read_u32(np, "st,lpc-mode", &mode);
if (ret) {
pr_err("clksrc-st-lpc: An LPC mode must be provided\n");
return ret;
}
/* LPC can either run as a Clocksource or in RTC or WDT mode */
if (mode != ST_LPC_MODE_CLKSRC)
return 0;
ddata.base = of_iomap(np, 0);
if (!ddata.base) {
pr_err("clksrc-st-lpc: Unable to map iomem\n");
return -ENXIO;
}
ret = st_clksrc_setup_clk(np);
if (ret) {
iounmap(ddata.base);
return ret;
}
ret = st_clksrc_init();
if (ret) {
clk_disable_unprepare(ddata.clk);
clk_put(ddata.clk);
iounmap(ddata.base);
return ret;
}
pr_info("clksrc-st-lpc: clocksource initialised - running @ %luHz\n",
clk_get_rate(ddata.clk));
return ret;
}
TIMER_OF_DECLARE(ddata, "st,stih407-lpc", st_clksrc_of_register);
+123
View File
@@ -0,0 +1,123 @@
/*
* Cirrus Logic CLPS711X clocksource driver
*
* Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/sched_clock.h>
#include <linux/slab.h>
enum {
CLPS711X_CLKSRC_CLOCKSOURCE,
CLPS711X_CLKSRC_CLOCKEVENT,
};
static void __iomem *tcd;
static u64 notrace clps711x_sched_clock_read(void)
{
return ~readw(tcd);
}
static int __init _clps711x_clksrc_init(struct clk *clock, void __iomem *base)
{
unsigned long rate;
if (!base)
return -ENOMEM;
if (IS_ERR(clock))
return PTR_ERR(clock);
rate = clk_get_rate(clock);
tcd = base;
clocksource_mmio_init(tcd, "clps711x-clocksource", rate, 300, 16,
clocksource_mmio_readw_down);
sched_clock_register(clps711x_sched_clock_read, 16, rate);
return 0;
}
static irqreturn_t clps711x_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
evt->event_handler(evt);
return IRQ_HANDLED;
}
static int __init _clps711x_clkevt_init(struct clk *clock, void __iomem *base,
unsigned int irq)
{
struct clock_event_device *clkevt;
unsigned long rate;
if (!irq)
return -EINVAL;
if (!base)
return -ENOMEM;
if (IS_ERR(clock))
return PTR_ERR(clock);
clkevt = kzalloc(sizeof(*clkevt), GFP_KERNEL);
if (!clkevt)
return -ENOMEM;
rate = clk_get_rate(clock);
/* Set Timer prescaler */
writew(DIV_ROUND_CLOSEST(rate, HZ), base);
clkevt->name = "clps711x-clockevent";
clkevt->rating = 300;
clkevt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_C3STOP;
clkevt->cpumask = cpumask_of(0);
clockevents_config_and_register(clkevt, HZ, 0, 0);
return request_irq(irq, clps711x_timer_interrupt, IRQF_TIMER,
"clps711x-timer", clkevt);
}
void __init clps711x_clksrc_init(void __iomem *tc1_base, void __iomem *tc2_base,
unsigned int irq)
{
struct clk *tc1 = clk_get_sys("clps711x-timer.0", NULL);
struct clk *tc2 = clk_get_sys("clps711x-timer.1", NULL);
BUG_ON(_clps711x_clksrc_init(tc1, tc1_base));
BUG_ON(_clps711x_clkevt_init(tc2, tc2_base, irq));
}
#ifdef CONFIG_TIMER_OF
static int __init clps711x_timer_init(struct device_node *np)
{
unsigned int irq = irq_of_parse_and_map(np, 0);
struct clk *clock = of_clk_get(np, 0);
void __iomem *base = of_iomap(np, 0);
switch (of_alias_get_id(np, "timer")) {
case CLPS711X_CLKSRC_CLOCKSOURCE:
return _clps711x_clksrc_init(clock, base);
case CLPS711X_CLKSRC_CLOCKEVENT:
return _clps711x_clkevt_init(clock, base, irq);
default:
return -EINVAL;
}
}
TIMER_OF_DECLARE(clps711x, "cirrus,ep7209-timer", clps711x_timer_init);
#endif
+196
View File
@@ -0,0 +1,196 @@
/*
* Clock event driver for the CS5535/CS5536
*
* Copyright (C) 2006, Advanced Micro Devices, Inc.
* Copyright (C) 2007 Andres Salomon <dilinger@debian.org>
* Copyright (C) 2009 Andres Salomon <dilinger@collabora.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
* The MFGPTs are documented in AMD Geode CS5536 Companion Device Data Book.
*/
#include <linux/kernel.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/cs5535.h>
#include <linux/clockchips.h>
#define DRV_NAME "cs5535-clockevt"
static int timer_irq;
module_param_hw_named(irq, timer_irq, int, irq, 0644);
MODULE_PARM_DESC(irq, "Which IRQ to use for the clock source MFGPT ticks.");
/*
* We are using the 32.768kHz input clock - it's the only one that has the
* ranges we find desirable. The following table lists the suitable
* divisors and the associated Hz, minimum interval and the maximum interval:
*
* Divisor Hz Min Delta (s) Max Delta (s)
* 1 32768 .00048828125 2.000
* 2 16384 .0009765625 4.000
* 4 8192 .001953125 8.000
* 8 4096 .00390625 16.000
* 16 2048 .0078125 32.000
* 32 1024 .015625 64.000
* 64 512 .03125 128.000
* 128 256 .0625 256.000
* 256 128 .125 512.000
*/
static struct cs5535_mfgpt_timer *cs5535_event_clock;
/* Selected from the table above */
#define MFGPT_DIVISOR 16
#define MFGPT_SCALE 4 /* divisor = 2^(scale) */
#define MFGPT_HZ (32768 / MFGPT_DIVISOR)
#define MFGPT_PERIODIC (MFGPT_HZ / HZ)
/*
* The MFGPT timers on the CS5536 provide us with suitable timers to use
* as clock event sources - not as good as a HPET or APIC, but certainly
* better than the PIT. This isn't a general purpose MFGPT driver, but
* a simplified one designed specifically to act as a clock event source.
* For full details about the MFGPT, please consult the CS5536 data sheet.
*/
static void disable_timer(struct cs5535_mfgpt_timer *timer)
{
/* avoid races by clearing CMP1 and CMP2 unconditionally */
cs5535_mfgpt_write(timer, MFGPT_REG_SETUP,
(uint16_t) ~MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP1 |
MFGPT_SETUP_CMP2);
}
static void start_timer(struct cs5535_mfgpt_timer *timer, uint16_t delta)
{
cs5535_mfgpt_write(timer, MFGPT_REG_CMP2, delta);
cs5535_mfgpt_write(timer, MFGPT_REG_COUNTER, 0);
cs5535_mfgpt_write(timer, MFGPT_REG_SETUP,
MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP2);
}
static int mfgpt_shutdown(struct clock_event_device *evt)
{
disable_timer(cs5535_event_clock);
return 0;
}
static int mfgpt_set_periodic(struct clock_event_device *evt)
{
disable_timer(cs5535_event_clock);
start_timer(cs5535_event_clock, MFGPT_PERIODIC);
return 0;
}
static int mfgpt_next_event(unsigned long delta, struct clock_event_device *evt)
{
start_timer(cs5535_event_clock, delta);
return 0;
}
static struct clock_event_device cs5535_clockevent = {
.name = DRV_NAME,
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
.set_state_shutdown = mfgpt_shutdown,
.set_state_periodic = mfgpt_set_periodic,
.set_state_oneshot = mfgpt_shutdown,
.tick_resume = mfgpt_shutdown,
.set_next_event = mfgpt_next_event,
.rating = 250,
};
static irqreturn_t mfgpt_tick(int irq, void *dev_id)
{
uint16_t val = cs5535_mfgpt_read(cs5535_event_clock, MFGPT_REG_SETUP);
/* See if the interrupt was for us */
if (!(val & (MFGPT_SETUP_SETUP | MFGPT_SETUP_CMP2 | MFGPT_SETUP_CMP1)))
return IRQ_NONE;
/* Turn off the clock (and clear the event) */
disable_timer(cs5535_event_clock);
if (clockevent_state_detached(&cs5535_clockevent) ||
clockevent_state_shutdown(&cs5535_clockevent))
return IRQ_HANDLED;
/* Clear the counter */
cs5535_mfgpt_write(cs5535_event_clock, MFGPT_REG_COUNTER, 0);
/* Restart the clock in periodic mode */
if (clockevent_state_periodic(&cs5535_clockevent))
cs5535_mfgpt_write(cs5535_event_clock, MFGPT_REG_SETUP,
MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP2);
cs5535_clockevent.event_handler(&cs5535_clockevent);
return IRQ_HANDLED;
}
static struct irqaction mfgptirq = {
.handler = mfgpt_tick,
.flags = IRQF_NOBALANCING | IRQF_TIMER | IRQF_SHARED,
.name = DRV_NAME,
};
static int __init cs5535_mfgpt_init(void)
{
struct cs5535_mfgpt_timer *timer;
int ret;
uint16_t val;
timer = cs5535_mfgpt_alloc_timer(MFGPT_TIMER_ANY, MFGPT_DOMAIN_WORKING);
if (!timer) {
printk(KERN_ERR DRV_NAME ": Could not allocate MFGPT timer\n");
return -ENODEV;
}
cs5535_event_clock = timer;
/* Set up the IRQ on the MFGPT side */
if (cs5535_mfgpt_setup_irq(timer, MFGPT_CMP2, &timer_irq)) {
printk(KERN_ERR DRV_NAME ": Could not set up IRQ %d\n",
timer_irq);
goto err_timer;
}
/* And register it with the kernel */
ret = setup_irq(timer_irq, &mfgptirq);
if (ret) {
printk(KERN_ERR DRV_NAME ": Unable to set up the interrupt.\n");
goto err_irq;
}
/* Set the clock scale and enable the event mode for CMP2 */
val = MFGPT_SCALE | (3 << 8);
cs5535_mfgpt_write(cs5535_event_clock, MFGPT_REG_SETUP, val);
/* Set up the clock event */
printk(KERN_INFO DRV_NAME
": Registering MFGPT timer as a clock event, using IRQ %d\n",
timer_irq);
clockevents_config_and_register(&cs5535_clockevent, MFGPT_HZ,
0xF, 0xFFFE);
return 0;
err_irq:
cs5535_mfgpt_release_irq(cs5535_event_clock, MFGPT_CMP2, &timer_irq);
err_timer:
cs5535_mfgpt_free_timer(cs5535_event_clock);
printk(KERN_ERR DRV_NAME ": Unable to set up the MFGPT clock source\n");
return -EIO;
}
module_init(cs5535_mfgpt_init);
MODULE_AUTHOR("Andres Salomon <dilinger@queued.net>");
MODULE_DESCRIPTION("CS5535/CS5536 MFGPT clock event driver");
MODULE_LICENSE("GPL");
+40
View File
@@ -0,0 +1,40 @@
/*
* linux/drivers/clocksource/dummy_timer.c
*
* Copyright (C) 2013 ARM Ltd.
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clockchips.h>
#include <linux/cpu.h>
#include <linux/init.h>
#include <linux/percpu.h>
#include <linux/cpumask.h>
static DEFINE_PER_CPU(struct clock_event_device, dummy_timer_evt);
static int dummy_timer_starting_cpu(unsigned int cpu)
{
struct clock_event_device *evt = per_cpu_ptr(&dummy_timer_evt, cpu);
evt->name = "dummy_timer";
evt->features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT |
CLOCK_EVT_FEAT_DUMMY;
evt->rating = 100;
evt->cpumask = cpumask_of(cpu);
clockevents_register_device(evt);
return 0;
}
static int __init dummy_timer_register(void)
{
return cpuhp_setup_state(CPUHP_AP_DUMMY_TIMER_STARTING,
"clockevents/dummy_timer:starting",
dummy_timer_starting_cpu, NULL);
}
early_initcall(dummy_timer_register);
+425
View File
@@ -0,0 +1,425 @@
/*
* (C) Copyright 2009 Intel Corporation
* Author: Jacob Pan (jacob.jun.pan@intel.com)
*
* Shared with ARM platforms, Jamie Iles, Picochip 2011
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Support for the Synopsys DesignWare APB Timers.
*/
#include <linux/dw_apb_timer.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/slab.h>
#define APBT_MIN_PERIOD 4
#define APBT_MIN_DELTA_USEC 200
#define APBTMR_N_LOAD_COUNT 0x00
#define APBTMR_N_CURRENT_VALUE 0x04
#define APBTMR_N_CONTROL 0x08
#define APBTMR_N_EOI 0x0c
#define APBTMR_N_INT_STATUS 0x10
#define APBTMRS_INT_STATUS 0xa0
#define APBTMRS_EOI 0xa4
#define APBTMRS_RAW_INT_STATUS 0xa8
#define APBTMRS_COMP_VERSION 0xac
#define APBTMR_CONTROL_ENABLE (1 << 0)
/* 1: periodic, 0:free running. */
#define APBTMR_CONTROL_MODE_PERIODIC (1 << 1)
#define APBTMR_CONTROL_INT (1 << 2)
static inline struct dw_apb_clock_event_device *
ced_to_dw_apb_ced(struct clock_event_device *evt)
{
return container_of(evt, struct dw_apb_clock_event_device, ced);
}
static inline struct dw_apb_clocksource *
clocksource_to_dw_apb_clocksource(struct clocksource *cs)
{
return container_of(cs, struct dw_apb_clocksource, cs);
}
static inline u32 apbt_readl(struct dw_apb_timer *timer, unsigned long offs)
{
return readl(timer->base + offs);
}
static inline void apbt_writel(struct dw_apb_timer *timer, u32 val,
unsigned long 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)
{
u32 ctrl = apbt_readl(timer, APBTMR_N_CONTROL);
ctrl |= APBTMR_CONTROL_INT;
apbt_writel(timer, ctrl, APBTMR_N_CONTROL);
}
/**
* dw_apb_clockevent_pause() - stop the clock_event_device from running
*
* @dw_ced: The APB clock to stop generating events.
*/
void dw_apb_clockevent_pause(struct dw_apb_clock_event_device *dw_ced)
{
disable_irq(dw_ced->timer.irq);
apbt_disable_int(&dw_ced->timer);
}
static void apbt_eoi(struct dw_apb_timer *timer)
{
apbt_readl_relaxed(timer, APBTMR_N_EOI);
}
static irqreturn_t dw_apb_clockevent_irq(int irq, void *data)
{
struct clock_event_device *evt = data;
struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt);
if (!evt->event_handler) {
pr_info("Spurious APBT timer interrupt %d\n", irq);
return IRQ_NONE;
}
if (dw_ced->eoi)
dw_ced->eoi(&dw_ced->timer);
evt->event_handler(evt);
return IRQ_HANDLED;
}
static void apbt_enable_int(struct dw_apb_timer *timer)
{
u32 ctrl = apbt_readl(timer, APBTMR_N_CONTROL);
/* clear pending intr */
apbt_readl(timer, APBTMR_N_EOI);
ctrl &= ~APBTMR_CONTROL_INT;
apbt_writel(timer, ctrl, APBTMR_N_CONTROL);
}
static int apbt_shutdown(struct clock_event_device *evt)
{
struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt);
u32 ctrl;
pr_debug("%s CPU %d state=shutdown\n", __func__,
cpumask_first(evt->cpumask));
ctrl = apbt_readl(&dw_ced->timer, APBTMR_N_CONTROL);
ctrl &= ~APBTMR_CONTROL_ENABLE;
apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
return 0;
}
static int apbt_set_oneshot(struct clock_event_device *evt)
{
struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt);
u32 ctrl;
pr_debug("%s CPU %d state=oneshot\n", __func__,
cpumask_first(evt->cpumask));
ctrl = apbt_readl(&dw_ced->timer, APBTMR_N_CONTROL);
/*
* set free running mode, this mode will let timer reload max
* timeout which will give time (3min on 25MHz clock) to rearm
* the next event, therefore emulate the one-shot mode.
*/
ctrl &= ~APBTMR_CONTROL_ENABLE;
ctrl &= ~APBTMR_CONTROL_MODE_PERIODIC;
apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
/* write again to set free running mode */
apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
/*
* DW APB p. 46, load counter with all 1s before starting free
* running mode.
*/
apbt_writel(&dw_ced->timer, ~0, APBTMR_N_LOAD_COUNT);
ctrl &= ~APBTMR_CONTROL_INT;
ctrl |= APBTMR_CONTROL_ENABLE;
apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
return 0;
}
static int apbt_set_periodic(struct clock_event_device *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);
u32 ctrl;
pr_debug("%s CPU %d state=periodic\n", __func__,
cpumask_first(evt->cpumask));
ctrl = apbt_readl(&dw_ced->timer, APBTMR_N_CONTROL);
ctrl |= APBTMR_CONTROL_MODE_PERIODIC;
apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
/*
* DW APB p. 46, have to disable timer before load counter,
* may cause sync problem.
*/
ctrl &= ~APBTMR_CONTROL_ENABLE;
apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
udelay(1);
pr_debug("Setting clock period %lu for HZ %d\n", period, HZ);
apbt_writel(&dw_ced->timer, period, APBTMR_N_LOAD_COUNT);
ctrl |= APBTMR_CONTROL_ENABLE;
apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
return 0;
}
static int apbt_resume(struct clock_event_device *evt)
{
struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt);
pr_debug("%s CPU %d state=resume\n", __func__,
cpumask_first(evt->cpumask));
apbt_enable_int(&dw_ced->timer);
return 0;
}
static int apbt_next_event(unsigned long delta,
struct clock_event_device *evt)
{
u32 ctrl;
struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt);
/* Disable timer */
ctrl = apbt_readl_relaxed(&dw_ced->timer, APBTMR_N_CONTROL);
ctrl &= ~APBTMR_CONTROL_ENABLE;
apbt_writel_relaxed(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
/* write new count */
apbt_writel_relaxed(&dw_ced->timer, delta, APBTMR_N_LOAD_COUNT);
ctrl |= APBTMR_CONTROL_ENABLE;
apbt_writel_relaxed(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
return 0;
}
/**
* dw_apb_clockevent_init() - use an APB timer as a clock_event_device
*
* @cpu: The CPU the events will be targeted at or -1 if CPU affiliation
* isn't required.
* @name: The name used for the timer and the IRQ for it.
* @rating: The rating to give the timer.
* @base: I/O base for the timer registers.
* @irq: The interrupt number to use for the timer.
* @freq: The frequency that the timer counts at.
*
* This creates a clock_event_device for using with the generic clock layer
* but does not start and register it. This should be done with
* dw_apb_clockevent_register() as the next step. If this is the first time
* it has been called for a timer then the IRQ will be requested, if not it
* just be enabled to allow CPU hotplug to avoid repeatedly requesting and
* releasing the IRQ.
*/
struct dw_apb_clock_event_device *
dw_apb_clockevent_init(int cpu, const char *name, unsigned rating,
void __iomem *base, int irq, unsigned long freq)
{
struct dw_apb_clock_event_device *dw_ced =
kzalloc(sizeof(*dw_ced), GFP_KERNEL);
int err;
if (!dw_ced)
return NULL;
dw_ced->timer.base = base;
dw_ced->timer.irq = irq;
dw_ced->timer.freq = freq;
clockevents_calc_mult_shift(&dw_ced->ced, freq, APBT_MIN_PERIOD);
dw_ced->ced.max_delta_ns = clockevent_delta2ns(0x7fffffff,
&dw_ced->ced);
dw_ced->ced.max_delta_ticks = 0x7fffffff;
dw_ced->ced.min_delta_ns = clockevent_delta2ns(5000, &dw_ced->ced);
dw_ced->ced.min_delta_ticks = 5000;
dw_ced->ced.cpumask = cpu < 0 ? cpu_possible_mask : cpumask_of(cpu);
dw_ced->ced.features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_DYNIRQ;
dw_ced->ced.set_state_shutdown = apbt_shutdown;
dw_ced->ced.set_state_periodic = apbt_set_periodic;
dw_ced->ced.set_state_oneshot = apbt_set_oneshot;
dw_ced->ced.set_state_oneshot_stopped = apbt_shutdown;
dw_ced->ced.tick_resume = apbt_resume;
dw_ced->ced.set_next_event = apbt_next_event;
dw_ced->ced.irq = dw_ced->timer.irq;
dw_ced->ced.rating = rating;
dw_ced->ced.name = name;
dw_ced->irqaction.name = dw_ced->ced.name;
dw_ced->irqaction.handler = dw_apb_clockevent_irq;
dw_ced->irqaction.dev_id = &dw_ced->ced;
dw_ced->irqaction.irq = irq;
dw_ced->irqaction.flags = IRQF_TIMER | IRQF_IRQPOLL |
IRQF_NOBALANCING;
dw_ced->eoi = apbt_eoi;
err = setup_irq(irq, &dw_ced->irqaction);
if (err) {
pr_err("failed to request timer irq\n");
kfree(dw_ced);
dw_ced = NULL;
}
return dw_ced;
}
/**
* dw_apb_clockevent_resume() - resume a clock that has been paused.
*
* @dw_ced: The APB clock to resume.
*/
void dw_apb_clockevent_resume(struct dw_apb_clock_event_device *dw_ced)
{
enable_irq(dw_ced->timer.irq);
}
/**
* dw_apb_clockevent_stop() - stop the clock_event_device and release the IRQ.
*
* @dw_ced: The APB clock to stop generating the events.
*/
void dw_apb_clockevent_stop(struct dw_apb_clock_event_device *dw_ced)
{
free_irq(dw_ced->timer.irq, &dw_ced->ced);
}
/**
* dw_apb_clockevent_register() - register the clock with the generic layer
*
* @dw_ced: The APB clock to register as a clock_event_device.
*/
void dw_apb_clockevent_register(struct dw_apb_clock_event_device *dw_ced)
{
apbt_writel(&dw_ced->timer, 0, APBTMR_N_CONTROL);
clockevents_register_device(&dw_ced->ced);
apbt_enable_int(&dw_ced->timer);
}
/**
* dw_apb_clocksource_start() - start the clocksource counting.
*
* @dw_cs: The clocksource to start.
*
* This is used to start the clocksource before registration and can be used
* to enable calibration of timers.
*/
void dw_apb_clocksource_start(struct dw_apb_clocksource *dw_cs)
{
/*
* start count down from 0xffff_ffff. this is done by toggling the
* enable bit then load initial load count to ~0.
*/
u32 ctrl = apbt_readl(&dw_cs->timer, APBTMR_N_CONTROL);
ctrl &= ~APBTMR_CONTROL_ENABLE;
apbt_writel(&dw_cs->timer, ctrl, APBTMR_N_CONTROL);
apbt_writel(&dw_cs->timer, ~0, APBTMR_N_LOAD_COUNT);
/* enable, mask interrupt */
ctrl &= ~APBTMR_CONTROL_MODE_PERIODIC;
ctrl |= (APBTMR_CONTROL_ENABLE | APBTMR_CONTROL_INT);
apbt_writel(&dw_cs->timer, ctrl, APBTMR_N_CONTROL);
/* read it once to get cached counter value initialized */
dw_apb_clocksource_read(dw_cs);
}
static u64 __apbt_read_clocksource(struct clocksource *cs)
{
u32 current_count;
struct dw_apb_clocksource *dw_cs =
clocksource_to_dw_apb_clocksource(cs);
current_count = apbt_readl_relaxed(&dw_cs->timer,
APBTMR_N_CURRENT_VALUE);
return (u64)~current_count;
}
static void apbt_restart_clocksource(struct clocksource *cs)
{
struct dw_apb_clocksource *dw_cs =
clocksource_to_dw_apb_clocksource(cs);
dw_apb_clocksource_start(dw_cs);
}
/**
* dw_apb_clocksource_init() - use an APB timer as a clocksource.
*
* @rating: The rating to give the clocksource.
* @name: The name for the clocksource.
* @base: The I/O base for the timer registers.
* @freq: The frequency that the timer counts at.
*
* This creates a clocksource using an APB timer but does not yet register it
* with the clocksource system. This should be done with
* dw_apb_clocksource_register() as the next step.
*/
struct dw_apb_clocksource *
dw_apb_clocksource_init(unsigned rating, const char *name, void __iomem *base,
unsigned long freq)
{
struct dw_apb_clocksource *dw_cs = kzalloc(sizeof(*dw_cs), GFP_KERNEL);
if (!dw_cs)
return NULL;
dw_cs->timer.base = base;
dw_cs->timer.freq = freq;
dw_cs->cs.name = name;
dw_cs->cs.rating = rating;
dw_cs->cs.read = __apbt_read_clocksource;
dw_cs->cs.mask = CLOCKSOURCE_MASK(32);
dw_cs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
dw_cs->cs.resume = apbt_restart_clocksource;
return dw_cs;
}
/**
* dw_apb_clocksource_register() - register the APB clocksource.
*
* @dw_cs: The clocksource to register.
*/
void dw_apb_clocksource_register(struct dw_apb_clocksource *dw_cs)
{
clocksource_register_hz(&dw_cs->cs, dw_cs->timer.freq);
}
/**
* dw_apb_clocksource_read() - read the current value of a clocksource.
*
* @dw_cs: The clocksource to read.
*/
u64 dw_apb_clocksource_read(struct dw_apb_clocksource *dw_cs)
{
return (u64)~apbt_readl(&dw_cs->timer, APBTMR_N_CURRENT_VALUE);
}
+171
View File
@@ -0,0 +1,171 @@
/*
* Copyright (C) 2012 Altera Corporation
* Copyright (c) 2011 Picochip Ltd., Jamie Iles
*
* Modified from mach-picoxcell/time.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/delay.h>
#include <linux/dw_apb_timer.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/clk.h>
#include <linux/sched_clock.h>
static void __init timer_get_base_and_rate(struct device_node *np,
void __iomem **base, u32 *rate)
{
struct clk *timer_clk;
struct clk *pclk;
*base = of_iomap(np, 0);
if (!*base)
panic("Unable to map regs for %s", np->name);
/*
* Not all implementations use a periphal clock, so don't panic
* if it's not present
*/
pclk = of_clk_get_by_name(np, "pclk");
if (!IS_ERR(pclk))
if (clk_prepare_enable(pclk))
pr_warn("pclk for %s is present, but could not be activated\n",
np->name);
timer_clk = of_clk_get_by_name(np, "timer");
if (IS_ERR(timer_clk))
goto try_clock_freq;
if (!clk_prepare_enable(timer_clk)) {
*rate = clk_get_rate(timer_clk);
return;
}
try_clock_freq:
if (of_property_read_u32(np, "clock-freq", rate) &&
of_property_read_u32(np, "clock-frequency", rate))
panic("No clock nor clock-frequency property for %s", np->name);
}
static void __init add_clockevent(struct device_node *event_timer)
{
void __iomem *iobase;
struct dw_apb_clock_event_device *ced;
u32 irq, rate;
irq = irq_of_parse_and_map(event_timer, 0);
if (irq == 0)
panic("No IRQ for clock event timer");
timer_get_base_and_rate(event_timer, &iobase, &rate);
ced = dw_apb_clockevent_init(0, event_timer->name, 300, iobase, irq,
rate);
if (!ced)
panic("Unable to initialise clockevent device");
dw_apb_clockevent_register(ced);
}
static void __iomem *sched_io_base;
static u32 sched_rate;
static void __init add_clocksource(struct device_node *source_timer)
{
void __iomem *iobase;
struct dw_apb_clocksource *cs;
u32 rate;
timer_get_base_and_rate(source_timer, &iobase, &rate);
cs = dw_apb_clocksource_init(300, source_timer->name, iobase, rate);
if (!cs)
panic("Unable to initialise clocksource device");
dw_apb_clocksource_start(cs);
dw_apb_clocksource_register(cs);
/*
* Fallback to use the clocksource as sched_clock if no separate
* timer is found. sched_io_base then points to the current_value
* register of the clocksource timer.
*/
sched_io_base = iobase + 0x04;
sched_rate = rate;
}
static u64 notrace read_sched_clock(void)
{
return ~readl_relaxed(sched_io_base);
}
static const struct of_device_id sptimer_ids[] __initconst = {
{ .compatible = "picochip,pc3x2-rtc" },
{ /* Sentinel */ },
};
static void __init init_sched_clock(void)
{
struct device_node *sched_timer;
sched_timer = of_find_matching_node(NULL, sptimer_ids);
if (sched_timer) {
timer_get_base_and_rate(sched_timer, &sched_io_base,
&sched_rate);
of_node_put(sched_timer);
}
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 __init dw_apb_timer_init(struct device_node *timer)
{
switch (num_called) {
case 1:
pr_debug("%s: found clocksource timer\n", __func__);
add_clocksource(timer);
init_sched_clock();
#ifdef CONFIG_ARM
dw_apb_delay_timer.freq = sched_rate;
register_current_timer_delay(&dw_apb_delay_timer);
#endif
break;
default:
pr_debug("%s: found clockevent timer\n", __func__);
add_clockevent(timer);
break;
}
num_called++;
return 0;
}
TIMER_OF_DECLARE(pc3x2_timer, "picochip,pc3x2-timer", dw_apb_timer_init);
TIMER_OF_DECLARE(apb_timer_osc, "snps,dw-apb-timer-osc", dw_apb_timer_init);
TIMER_OF_DECLARE(apb_timer_sp, "snps,dw-apb-timer-sp", dw_apb_timer_init);
TIMER_OF_DECLARE(apb_timer, "snps,dw-apb-timer", dw_apb_timer_init);
+388
View File
@@ -0,0 +1,388 @@
/*
* Emma Mobile Timer Support - STI
*
* Copyright (C) 2012 Magnus Damm
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/irq.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/slab.h>
#include <linux/module.h>
enum { USER_CLOCKSOURCE, USER_CLOCKEVENT, USER_NR };
struct em_sti_priv {
void __iomem *base;
struct clk *clk;
struct platform_device *pdev;
unsigned int active[USER_NR];
unsigned long rate;
raw_spinlock_t lock;
struct clock_event_device ced;
struct clocksource cs;
};
#define STI_CONTROL 0x00
#define STI_COMPA_H 0x10
#define STI_COMPA_L 0x14
#define STI_COMPB_H 0x18
#define STI_COMPB_L 0x1c
#define STI_COUNT_H 0x20
#define STI_COUNT_L 0x24
#define STI_COUNT_RAW_H 0x28
#define STI_COUNT_RAW_L 0x2c
#define STI_SET_H 0x30
#define STI_SET_L 0x34
#define STI_INTSTATUS 0x40
#define STI_INTRAWSTATUS 0x44
#define STI_INTENSET 0x48
#define STI_INTENCLR 0x4c
#define STI_INTFFCLR 0x50
static inline unsigned long em_sti_read(struct em_sti_priv *p, int offs)
{
return ioread32(p->base + offs);
}
static inline void em_sti_write(struct em_sti_priv *p, int offs,
unsigned long value)
{
iowrite32(value, p->base + offs);
}
static int em_sti_enable(struct em_sti_priv *p)
{
int ret;
/* enable clock */
ret = clk_enable(p->clk);
if (ret) {
dev_err(&p->pdev->dev, "cannot enable clock\n");
return ret;
}
/* reset the counter */
em_sti_write(p, STI_SET_H, 0x40000000);
em_sti_write(p, STI_SET_L, 0x00000000);
/* mask and clear pending interrupts */
em_sti_write(p, STI_INTENCLR, 3);
em_sti_write(p, STI_INTFFCLR, 3);
/* enable updates of counter registers */
em_sti_write(p, STI_CONTROL, 1);
return 0;
}
static void em_sti_disable(struct em_sti_priv *p)
{
/* mask interrupts */
em_sti_write(p, STI_INTENCLR, 3);
/* stop clock */
clk_disable(p->clk);
}
static u64 em_sti_count(struct em_sti_priv *p)
{
u64 ticks;
unsigned long flags;
/* the STI hardware buffers the 48-bit count, but to
* break it out into two 32-bit access the registers
* must be accessed in a certain order.
* Always read STI_COUNT_H before STI_COUNT_L.
*/
raw_spin_lock_irqsave(&p->lock, flags);
ticks = (u64)(em_sti_read(p, STI_COUNT_H) & 0xffff) << 32;
ticks |= em_sti_read(p, STI_COUNT_L);
raw_spin_unlock_irqrestore(&p->lock, flags);
return ticks;
}
static u64 em_sti_set_next(struct em_sti_priv *p, u64 next)
{
unsigned long flags;
raw_spin_lock_irqsave(&p->lock, flags);
/* mask compare A interrupt */
em_sti_write(p, STI_INTENCLR, 1);
/* update compare A value */
em_sti_write(p, STI_COMPA_H, next >> 32);
em_sti_write(p, STI_COMPA_L, next & 0xffffffff);
/* clear compare A interrupt source */
em_sti_write(p, STI_INTFFCLR, 1);
/* unmask compare A interrupt */
em_sti_write(p, STI_INTENSET, 1);
raw_spin_unlock_irqrestore(&p->lock, flags);
return next;
}
static irqreturn_t em_sti_interrupt(int irq, void *dev_id)
{
struct em_sti_priv *p = dev_id;
p->ced.event_handler(&p->ced);
return IRQ_HANDLED;
}
static int em_sti_start(struct em_sti_priv *p, unsigned int user)
{
unsigned long flags;
int used_before;
int ret = 0;
raw_spin_lock_irqsave(&p->lock, flags);
used_before = p->active[USER_CLOCKSOURCE] | p->active[USER_CLOCKEVENT];
if (!used_before)
ret = em_sti_enable(p);
if (!ret)
p->active[user] = 1;
raw_spin_unlock_irqrestore(&p->lock, flags);
return ret;
}
static void em_sti_stop(struct em_sti_priv *p, unsigned int user)
{
unsigned long flags;
int used_before, used_after;
raw_spin_lock_irqsave(&p->lock, flags);
used_before = p->active[USER_CLOCKSOURCE] | p->active[USER_CLOCKEVENT];
p->active[user] = 0;
used_after = p->active[USER_CLOCKSOURCE] | p->active[USER_CLOCKEVENT];
if (used_before && !used_after)
em_sti_disable(p);
raw_spin_unlock_irqrestore(&p->lock, flags);
}
static struct em_sti_priv *cs_to_em_sti(struct clocksource *cs)
{
return container_of(cs, struct em_sti_priv, cs);
}
static u64 em_sti_clocksource_read(struct clocksource *cs)
{
return em_sti_count(cs_to_em_sti(cs));
}
static int em_sti_clocksource_enable(struct clocksource *cs)
{
struct em_sti_priv *p = cs_to_em_sti(cs);
return em_sti_start(p, USER_CLOCKSOURCE);
}
static void em_sti_clocksource_disable(struct clocksource *cs)
{
em_sti_stop(cs_to_em_sti(cs), USER_CLOCKSOURCE);
}
static void em_sti_clocksource_resume(struct clocksource *cs)
{
em_sti_clocksource_enable(cs);
}
static int em_sti_register_clocksource(struct em_sti_priv *p)
{
struct clocksource *cs = &p->cs;
cs->name = dev_name(&p->pdev->dev);
cs->rating = 200;
cs->read = em_sti_clocksource_read;
cs->enable = em_sti_clocksource_enable;
cs->disable = em_sti_clocksource_disable;
cs->suspend = em_sti_clocksource_disable;
cs->resume = em_sti_clocksource_resume;
cs->mask = CLOCKSOURCE_MASK(48);
cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
dev_info(&p->pdev->dev, "used as clock source\n");
clocksource_register_hz(cs, p->rate);
return 0;
}
static struct em_sti_priv *ced_to_em_sti(struct clock_event_device *ced)
{
return container_of(ced, struct em_sti_priv, ced);
}
static int em_sti_clock_event_shutdown(struct clock_event_device *ced)
{
struct em_sti_priv *p = ced_to_em_sti(ced);
em_sti_stop(p, USER_CLOCKEVENT);
return 0;
}
static int em_sti_clock_event_set_oneshot(struct clock_event_device *ced)
{
struct em_sti_priv *p = ced_to_em_sti(ced);
dev_info(&p->pdev->dev, "used for oneshot clock events\n");
em_sti_start(p, USER_CLOCKEVENT);
return 0;
}
static int em_sti_clock_event_next(unsigned long delta,
struct clock_event_device *ced)
{
struct em_sti_priv *p = ced_to_em_sti(ced);
u64 next;
int safe;
next = em_sti_set_next(p, em_sti_count(p) + delta);
safe = em_sti_count(p) < (next - 1);
return !safe;
}
static void em_sti_register_clockevent(struct em_sti_priv *p)
{
struct clock_event_device *ced = &p->ced;
ced->name = dev_name(&p->pdev->dev);
ced->features = CLOCK_EVT_FEAT_ONESHOT;
ced->rating = 200;
ced->cpumask = cpu_possible_mask;
ced->set_next_event = em_sti_clock_event_next;
ced->set_state_shutdown = em_sti_clock_event_shutdown;
ced->set_state_oneshot = em_sti_clock_event_set_oneshot;
dev_info(&p->pdev->dev, "used for clock events\n");
clockevents_config_and_register(ced, p->rate, 2, 0xffffffff);
}
static int em_sti_probe(struct platform_device *pdev)
{
struct em_sti_priv *p;
struct resource *res;
int irq;
int ret;
p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
if (p == NULL)
return -ENOMEM;
p->pdev = pdev;
platform_set_drvdata(pdev, p);
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "failed to get irq\n");
return irq;
}
/* map memory, let base point to the STI instance */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
p->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(p->base))
return PTR_ERR(p->base);
ret = devm_request_irq(&pdev->dev, irq, em_sti_interrupt,
IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING,
dev_name(&pdev->dev), p);
if (ret) {
dev_err(&pdev->dev, "failed to request low IRQ\n");
return ret;
}
/* get hold of clock */
p->clk = devm_clk_get(&pdev->dev, "sclk");
if (IS_ERR(p->clk)) {
dev_err(&pdev->dev, "cannot get clock\n");
return PTR_ERR(p->clk);
}
ret = clk_prepare(p->clk);
if (ret < 0) {
dev_err(&pdev->dev, "cannot prepare clock\n");
return ret;
}
ret = clk_enable(p->clk);
if (ret < 0) {
dev_err(&p->pdev->dev, "cannot enable clock\n");
clk_unprepare(p->clk);
return ret;
}
p->rate = clk_get_rate(p->clk);
clk_disable(p->clk);
raw_spin_lock_init(&p->lock);
em_sti_register_clockevent(p);
em_sti_register_clocksource(p);
return 0;
}
static int em_sti_remove(struct platform_device *pdev)
{
return -EBUSY; /* cannot unregister clockevent and clocksource */
}
static const struct of_device_id em_sti_dt_ids[] = {
{ .compatible = "renesas,em-sti", },
{},
};
MODULE_DEVICE_TABLE(of, em_sti_dt_ids);
static struct platform_driver em_sti_device_driver = {
.probe = em_sti_probe,
.remove = em_sti_remove,
.driver = {
.name = "em_sti",
.of_match_table = em_sti_dt_ids,
}
};
static int __init em_sti_init(void)
{
return platform_driver_register(&em_sti_device_driver);
}
static void __exit em_sti_exit(void)
{
platform_driver_unregister(&em_sti_device_driver);
}
subsys_initcall(em_sti_init);
module_exit(em_sti_exit);
MODULE_AUTHOR("Magnus Damm");
MODULE_DESCRIPTION("Renesas Emma Mobile STI Timer Driver");
MODULE_LICENSE("GPL v2");
+627
View File
@@ -0,0 +1,627 @@
/* linux/arch/arm/mach-exynos4/mct.c
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* EXYNOS4 MCT(Multi-Core Timer) support
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/cpu.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/percpu.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/clocksource.h>
#include <linux/sched_clock.h>
#define EXYNOS4_MCTREG(x) (x)
#define EXYNOS4_MCT_G_CNT_L EXYNOS4_MCTREG(0x100)
#define EXYNOS4_MCT_G_CNT_U EXYNOS4_MCTREG(0x104)
#define EXYNOS4_MCT_G_CNT_WSTAT EXYNOS4_MCTREG(0x110)
#define EXYNOS4_MCT_G_COMP0_L EXYNOS4_MCTREG(0x200)
#define EXYNOS4_MCT_G_COMP0_U EXYNOS4_MCTREG(0x204)
#define EXYNOS4_MCT_G_COMP0_ADD_INCR EXYNOS4_MCTREG(0x208)
#define EXYNOS4_MCT_G_TCON EXYNOS4_MCTREG(0x240)
#define EXYNOS4_MCT_G_INT_CSTAT EXYNOS4_MCTREG(0x244)
#define EXYNOS4_MCT_G_INT_ENB EXYNOS4_MCTREG(0x248)
#define EXYNOS4_MCT_G_WSTAT EXYNOS4_MCTREG(0x24C)
#define _EXYNOS4_MCT_L_BASE EXYNOS4_MCTREG(0x300)
#define EXYNOS4_MCT_L_BASE(x) (_EXYNOS4_MCT_L_BASE + (0x100 * x))
#define EXYNOS4_MCT_L_MASK (0xffffff00)
#define MCT_L_TCNTB_OFFSET (0x00)
#define MCT_L_ICNTB_OFFSET (0x08)
#define MCT_L_TCON_OFFSET (0x20)
#define MCT_L_INT_CSTAT_OFFSET (0x30)
#define MCT_L_INT_ENB_OFFSET (0x34)
#define MCT_L_WSTAT_OFFSET (0x40)
#define MCT_G_TCON_START (1 << 8)
#define MCT_G_TCON_COMP0_AUTO_INC (1 << 1)
#define MCT_G_TCON_COMP0_ENABLE (1 << 0)
#define MCT_L_TCON_INTERVAL_MODE (1 << 2)
#define MCT_L_TCON_INT_START (1 << 1)
#define MCT_L_TCON_TIMER_START (1 << 0)
#define TICK_BASE_CNT 1
enum {
MCT_INT_SPI,
MCT_INT_PPI
};
enum {
MCT_G0_IRQ,
MCT_G1_IRQ,
MCT_G2_IRQ,
MCT_G3_IRQ,
MCT_L0_IRQ,
MCT_L1_IRQ,
MCT_L2_IRQ,
MCT_L3_IRQ,
MCT_L4_IRQ,
MCT_L5_IRQ,
MCT_L6_IRQ,
MCT_L7_IRQ,
MCT_NR_IRQS,
};
static void __iomem *reg_base;
static unsigned long clk_rate;
static unsigned int mct_int_type;
static int mct_irqs[MCT_NR_IRQS];
struct mct_clock_event_device {
struct clock_event_device evt;
unsigned long base;
char name[10];
};
static void exynos4_mct_write(unsigned int value, unsigned long offset)
{
unsigned long stat_addr;
u32 mask;
u32 i;
writel_relaxed(value, reg_base + offset);
if (likely(offset >= EXYNOS4_MCT_L_BASE(0))) {
stat_addr = (offset & EXYNOS4_MCT_L_MASK) + MCT_L_WSTAT_OFFSET;
switch (offset & ~EXYNOS4_MCT_L_MASK) {
case MCT_L_TCON_OFFSET:
mask = 1 << 3; /* L_TCON write status */
break;
case MCT_L_ICNTB_OFFSET:
mask = 1 << 1; /* L_ICNTB write status */
break;
case MCT_L_TCNTB_OFFSET:
mask = 1 << 0; /* L_TCNTB write status */
break;
default:
return;
}
} else {
switch (offset) {
case EXYNOS4_MCT_G_TCON:
stat_addr = EXYNOS4_MCT_G_WSTAT;
mask = 1 << 16; /* G_TCON write status */
break;
case EXYNOS4_MCT_G_COMP0_L:
stat_addr = EXYNOS4_MCT_G_WSTAT;
mask = 1 << 0; /* G_COMP0_L write status */
break;
case EXYNOS4_MCT_G_COMP0_U:
stat_addr = EXYNOS4_MCT_G_WSTAT;
mask = 1 << 1; /* G_COMP0_U write status */
break;
case EXYNOS4_MCT_G_COMP0_ADD_INCR:
stat_addr = EXYNOS4_MCT_G_WSTAT;
mask = 1 << 2; /* G_COMP0_ADD_INCR w status */
break;
case EXYNOS4_MCT_G_CNT_L:
stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;
mask = 1 << 0; /* G_CNT_L write status */
break;
case EXYNOS4_MCT_G_CNT_U:
stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;
mask = 1 << 1; /* G_CNT_U write status */
break;
default:
return;
}
}
/* Wait maximum 1 ms until written values are applied */
for (i = 0; i < loops_per_jiffy / 1000 * HZ; i++)
if (readl_relaxed(reg_base + stat_addr) & mask) {
writel_relaxed(mask, reg_base + stat_addr);
return;
}
panic("MCT hangs after writing %d (offset:0x%lx)\n", value, offset);
}
/* Clocksource handling */
static void exynos4_mct_frc_start(void)
{
u32 reg;
reg = readl_relaxed(reg_base + EXYNOS4_MCT_G_TCON);
reg |= MCT_G_TCON_START;
exynos4_mct_write(reg, EXYNOS4_MCT_G_TCON);
}
/**
* exynos4_read_count_64 - Read all 64-bits of the global counter
*
* This will read all 64-bits of the global counter taking care to make sure
* that the upper and lower half match. Note that reading the MCT can be quite
* slow (hundreds of nanoseconds) so you should use the 32-bit (lower half
* only) version when possible.
*
* Returns the number of cycles in the global counter.
*/
static u64 exynos4_read_count_64(void)
{
unsigned int lo, hi;
u32 hi2 = readl_relaxed(reg_base + EXYNOS4_MCT_G_CNT_U);
do {
hi = hi2;
lo = readl_relaxed(reg_base + EXYNOS4_MCT_G_CNT_L);
hi2 = readl_relaxed(reg_base + EXYNOS4_MCT_G_CNT_U);
} while (hi != hi2);
return ((u64)hi << 32) | lo;
}
/**
* exynos4_read_count_32 - Read the lower 32-bits of the global counter
*
* This will read just the lower 32-bits of the global counter. This is marked
* as notrace so it can be used by the scheduler clock.
*
* Returns the number of cycles in the global counter (lower 32 bits).
*/
static u32 notrace exynos4_read_count_32(void)
{
return readl_relaxed(reg_base + EXYNOS4_MCT_G_CNT_L);
}
static u64 exynos4_frc_read(struct clocksource *cs)
{
return exynos4_read_count_32();
}
static void exynos4_frc_resume(struct clocksource *cs)
{
exynos4_mct_frc_start();
}
static struct clocksource mct_frc = {
.name = "mct-frc",
.rating = 450, /* use value higher than ARM arch timer */
.read = exynos4_frc_read,
.mask = CLOCKSOURCE_MASK(32),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
.resume = exynos4_frc_resume,
};
static u64 notrace exynos4_read_sched_clock(void)
{
return exynos4_read_count_32();
}
#if defined(CONFIG_ARM)
static struct delay_timer exynos4_delay_timer;
static cycles_t exynos4_read_current_timer(void)
{
BUILD_BUG_ON_MSG(sizeof(cycles_t) != sizeof(u32),
"cycles_t needs to move to 32-bit for ARM64 usage");
return exynos4_read_count_32();
}
#endif
static int __init exynos4_clocksource_init(void)
{
exynos4_mct_frc_start();
#if defined(CONFIG_ARM)
exynos4_delay_timer.read_current_timer = &exynos4_read_current_timer;
exynos4_delay_timer.freq = clk_rate;
register_current_timer_delay(&exynos4_delay_timer);
#endif
if (clocksource_register_hz(&mct_frc, clk_rate))
panic("%s: can't register clocksource\n", mct_frc.name);
sched_clock_register(exynos4_read_sched_clock, 32, clk_rate);
return 0;
}
static void exynos4_mct_comp0_stop(void)
{
unsigned int tcon;
tcon = readl_relaxed(reg_base + EXYNOS4_MCT_G_TCON);
tcon &= ~(MCT_G_TCON_COMP0_ENABLE | MCT_G_TCON_COMP0_AUTO_INC);
exynos4_mct_write(tcon, EXYNOS4_MCT_G_TCON);
exynos4_mct_write(0, EXYNOS4_MCT_G_INT_ENB);
}
static void exynos4_mct_comp0_start(bool periodic, unsigned long cycles)
{
unsigned int tcon;
u64 comp_cycle;
tcon = readl_relaxed(reg_base + EXYNOS4_MCT_G_TCON);
if (periodic) {
tcon |= MCT_G_TCON_COMP0_AUTO_INC;
exynos4_mct_write(cycles, EXYNOS4_MCT_G_COMP0_ADD_INCR);
}
comp_cycle = exynos4_read_count_64() + cycles;
exynos4_mct_write((u32)comp_cycle, EXYNOS4_MCT_G_COMP0_L);
exynos4_mct_write((u32)(comp_cycle >> 32), EXYNOS4_MCT_G_COMP0_U);
exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_ENB);
tcon |= MCT_G_TCON_COMP0_ENABLE;
exynos4_mct_write(tcon , EXYNOS4_MCT_G_TCON);
}
static int exynos4_comp_set_next_event(unsigned long cycles,
struct clock_event_device *evt)
{
exynos4_mct_comp0_start(false, cycles);
return 0;
}
static int mct_set_state_shutdown(struct clock_event_device *evt)
{
exynos4_mct_comp0_stop();
return 0;
}
static int mct_set_state_periodic(struct clock_event_device *evt)
{
unsigned long cycles_per_jiffy;
cycles_per_jiffy = (((unsigned long long)NSEC_PER_SEC / HZ * evt->mult)
>> evt->shift);
exynos4_mct_comp0_stop();
exynos4_mct_comp0_start(true, cycles_per_jiffy);
return 0;
}
static struct clock_event_device mct_comp_device = {
.name = "mct-comp",
.features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT,
.rating = 250,
.set_next_event = exynos4_comp_set_next_event,
.set_state_periodic = mct_set_state_periodic,
.set_state_shutdown = mct_set_state_shutdown,
.set_state_oneshot = mct_set_state_shutdown,
.set_state_oneshot_stopped = mct_set_state_shutdown,
.tick_resume = mct_set_state_shutdown,
};
static irqreturn_t exynos4_mct_comp_isr(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_CSTAT);
evt->event_handler(evt);
return IRQ_HANDLED;
}
static struct irqaction mct_comp_event_irq = {
.name = "mct_comp_irq",
.flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = exynos4_mct_comp_isr,
.dev_id = &mct_comp_device,
};
static int exynos4_clockevent_init(void)
{
mct_comp_device.cpumask = cpumask_of(0);
clockevents_config_and_register(&mct_comp_device, clk_rate,
0xf, 0xffffffff);
setup_irq(mct_irqs[MCT_G0_IRQ], &mct_comp_event_irq);
return 0;
}
static DEFINE_PER_CPU(struct mct_clock_event_device, percpu_mct_tick);
/* Clock event handling */
static void exynos4_mct_tick_stop(struct mct_clock_event_device *mevt)
{
unsigned long tmp;
unsigned long mask = MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START;
unsigned long offset = mevt->base + MCT_L_TCON_OFFSET;
tmp = readl_relaxed(reg_base + offset);
if (tmp & mask) {
tmp &= ~mask;
exynos4_mct_write(tmp, offset);
}
}
static void exynos4_mct_tick_start(unsigned long cycles,
struct mct_clock_event_device *mevt)
{
unsigned long tmp;
exynos4_mct_tick_stop(mevt);
tmp = (1 << 31) | cycles; /* MCT_L_UPDATE_ICNTB */
/* update interrupt count buffer */
exynos4_mct_write(tmp, mevt->base + MCT_L_ICNTB_OFFSET);
/* enable MCT tick interrupt */
exynos4_mct_write(0x1, mevt->base + MCT_L_INT_ENB_OFFSET);
tmp = readl_relaxed(reg_base + mevt->base + MCT_L_TCON_OFFSET);
tmp |= MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START |
MCT_L_TCON_INTERVAL_MODE;
exynos4_mct_write(tmp, mevt->base + MCT_L_TCON_OFFSET);
}
static void exynos4_mct_tick_clear(struct mct_clock_event_device *mevt)
{
/* Clear the MCT tick interrupt */
if (readl_relaxed(reg_base + mevt->base + MCT_L_INT_CSTAT_OFFSET) & 1)
exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET);
}
static int exynos4_tick_set_next_event(unsigned long cycles,
struct clock_event_device *evt)
{
struct mct_clock_event_device *mevt;
mevt = container_of(evt, struct mct_clock_event_device, evt);
exynos4_mct_tick_start(cycles, mevt);
return 0;
}
static int set_state_shutdown(struct clock_event_device *evt)
{
struct mct_clock_event_device *mevt;
mevt = container_of(evt, struct mct_clock_event_device, evt);
exynos4_mct_tick_stop(mevt);
exynos4_mct_tick_clear(mevt);
return 0;
}
static int set_state_periodic(struct clock_event_device *evt)
{
struct mct_clock_event_device *mevt;
unsigned long cycles_per_jiffy;
mevt = container_of(evt, struct mct_clock_event_device, evt);
cycles_per_jiffy = (((unsigned long long)NSEC_PER_SEC / HZ * evt->mult)
>> evt->shift);
exynos4_mct_tick_stop(mevt);
exynos4_mct_tick_start(cycles_per_jiffy, mevt);
return 0;
}
static irqreturn_t exynos4_mct_tick_isr(int irq, void *dev_id)
{
struct mct_clock_event_device *mevt = dev_id;
struct clock_event_device *evt = &mevt->evt;
/*
* This is for supporting oneshot mode.
* Mct would generate interrupt periodically
* without explicit stopping.
*/
if (!clockevent_state_periodic(&mevt->evt))
exynos4_mct_tick_stop(mevt);
exynos4_mct_tick_clear(mevt);
evt->event_handler(evt);
return IRQ_HANDLED;
}
static int exynos4_mct_starting_cpu(unsigned int cpu)
{
struct mct_clock_event_device *mevt =
per_cpu_ptr(&percpu_mct_tick, cpu);
struct clock_event_device *evt = &mevt->evt;
mevt->base = EXYNOS4_MCT_L_BASE(cpu);
snprintf(mevt->name, sizeof(mevt->name), "mct_tick%d", cpu);
evt->name = mevt->name;
evt->cpumask = cpumask_of(cpu);
evt->set_next_event = exynos4_tick_set_next_event;
evt->set_state_periodic = set_state_periodic;
evt->set_state_shutdown = set_state_shutdown;
evt->set_state_oneshot = set_state_shutdown;
evt->set_state_oneshot_stopped = set_state_shutdown;
evt->tick_resume = set_state_shutdown;
evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
evt->rating = 500; /* use value higher than ARM arch timer */
exynos4_mct_write(TICK_BASE_CNT, mevt->base + MCT_L_TCNTB_OFFSET);
if (mct_int_type == MCT_INT_SPI) {
if (evt->irq == -1)
return -EIO;
irq_force_affinity(evt->irq, cpumask_of(cpu));
enable_irq(evt->irq);
} else {
enable_percpu_irq(mct_irqs[MCT_L0_IRQ], 0);
}
clockevents_config_and_register(evt, clk_rate / (TICK_BASE_CNT + 1),
0xf, 0x7fffffff);
return 0;
}
static int exynos4_mct_dying_cpu(unsigned int cpu)
{
struct mct_clock_event_device *mevt =
per_cpu_ptr(&percpu_mct_tick, cpu);
struct clock_event_device *evt = &mevt->evt;
evt->set_state_shutdown(evt);
if (mct_int_type == MCT_INT_SPI) {
if (evt->irq != -1)
disable_irq_nosync(evt->irq);
exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET);
} else {
disable_percpu_irq(mct_irqs[MCT_L0_IRQ]);
}
return 0;
}
static int __init exynos4_timer_resources(struct device_node *np, void __iomem *base)
{
int err, cpu;
struct clk *mct_clk, *tick_clk;
tick_clk = np ? of_clk_get_by_name(np, "fin_pll") :
clk_get(NULL, "fin_pll");
if (IS_ERR(tick_clk))
panic("%s: unable to determine tick clock rate\n", __func__);
clk_rate = clk_get_rate(tick_clk);
mct_clk = np ? of_clk_get_by_name(np, "mct") : clk_get(NULL, "mct");
if (IS_ERR(mct_clk))
panic("%s: unable to retrieve mct clock instance\n", __func__);
clk_prepare_enable(mct_clk);
reg_base = base;
if (!reg_base)
panic("%s: unable to ioremap mct address space\n", __func__);
if (mct_int_type == MCT_INT_PPI) {
err = request_percpu_irq(mct_irqs[MCT_L0_IRQ],
exynos4_mct_tick_isr, "MCT",
&percpu_mct_tick);
WARN(err, "MCT: can't request IRQ %d (%d)\n",
mct_irqs[MCT_L0_IRQ], err);
} else {
for_each_possible_cpu(cpu) {
int mct_irq = mct_irqs[MCT_L0_IRQ + cpu];
struct mct_clock_event_device *pcpu_mevt =
per_cpu_ptr(&percpu_mct_tick, cpu);
pcpu_mevt->evt.irq = -1;
irq_set_status_flags(mct_irq, IRQ_NOAUTOEN);
if (request_irq(mct_irq,
exynos4_mct_tick_isr,
IRQF_TIMER | IRQF_NOBALANCING,
pcpu_mevt->name, pcpu_mevt)) {
pr_err("exynos-mct: cannot register IRQ (cpu%d)\n",
cpu);
continue;
}
pcpu_mevt->evt.irq = mct_irq;
}
}
/* Install hotplug callbacks which configure the timer on this CPU */
err = cpuhp_setup_state(CPUHP_AP_EXYNOS4_MCT_TIMER_STARTING,
"clockevents/exynos4/mct_timer:starting",
exynos4_mct_starting_cpu,
exynos4_mct_dying_cpu);
if (err)
goto out_irq;
return 0;
out_irq:
if (mct_int_type == MCT_INT_PPI) {
free_percpu_irq(mct_irqs[MCT_L0_IRQ], &percpu_mct_tick);
} else {
for_each_possible_cpu(cpu) {
struct mct_clock_event_device *pcpu_mevt =
per_cpu_ptr(&percpu_mct_tick, cpu);
if (pcpu_mevt->evt.irq != -1) {
free_irq(pcpu_mevt->evt.irq, pcpu_mevt);
pcpu_mevt->evt.irq = -1;
}
}
}
return err;
}
static int __init mct_init_dt(struct device_node *np, unsigned int int_type)
{
u32 nr_irqs, i;
int ret;
mct_int_type = int_type;
/* This driver uses only one global timer interrupt */
mct_irqs[MCT_G0_IRQ] = irq_of_parse_and_map(np, MCT_G0_IRQ);
/*
* Find out the number of local irqs specified. The local
* timer irqs are specified after the four global timer
* irqs are specified.
*/
#ifdef CONFIG_OF
nr_irqs = of_irq_count(np);
#else
nr_irqs = 0;
#endif
for (i = MCT_L0_IRQ; i < nr_irqs; i++)
mct_irqs[i] = irq_of_parse_and_map(np, i);
ret = exynos4_timer_resources(np, of_iomap(np, 0));
if (ret)
return ret;
ret = exynos4_clocksource_init();
if (ret)
return ret;
return exynos4_clockevent_init();
}
static int __init mct_init_spi(struct device_node *np)
{
return mct_init_dt(np, MCT_INT_SPI);
}
static int __init mct_init_ppi(struct device_node *np)
{
return mct_init_dt(np, MCT_INT_PPI);
}
TIMER_OF_DECLARE(exynos4210, "samsung,exynos4210-mct", mct_init_spi);
TIMER_OF_DECLARE(exynos4412, "samsung,exynos4412-mct", mct_init_ppi);
+376
View File
@@ -0,0 +1,376 @@
/*
* Freescale FlexTimer Module (FTM) timer driver.
*
* Copyright 2014 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*/
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/sched_clock.h>
#include <linux/slab.h>
#define FTM_SC 0x00
#define FTM_SC_CLK_SHIFT 3
#define FTM_SC_CLK_MASK (0x3 << FTM_SC_CLK_SHIFT)
#define FTM_SC_CLK(c) ((c) << FTM_SC_CLK_SHIFT)
#define FTM_SC_PS_MASK 0x7
#define FTM_SC_TOIE BIT(6)
#define FTM_SC_TOF BIT(7)
#define FTM_CNT 0x04
#define FTM_MOD 0x08
#define FTM_CNTIN 0x4C
#define FTM_PS_MAX 7
struct ftm_clock_device {
void __iomem *clksrc_base;
void __iomem *clkevt_base;
unsigned long periodic_cyc;
unsigned long ps;
bool big_endian;
};
static struct ftm_clock_device *priv;
static inline u32 ftm_readl(void __iomem *addr)
{
if (priv->big_endian)
return ioread32be(addr);
else
return ioread32(addr);
}
static inline void ftm_writel(u32 val, void __iomem *addr)
{
if (priv->big_endian)
iowrite32be(val, addr);
else
iowrite32(val, addr);
}
static inline void ftm_counter_enable(void __iomem *base)
{
u32 val;
/* select and enable counter clock source */
val = ftm_readl(base + FTM_SC);
val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
val |= priv->ps | FTM_SC_CLK(1);
ftm_writel(val, base + FTM_SC);
}
static inline void ftm_counter_disable(void __iomem *base)
{
u32 val;
/* disable counter clock source */
val = ftm_readl(base + FTM_SC);
val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
ftm_writel(val, base + FTM_SC);
}
static inline void ftm_irq_acknowledge(void __iomem *base)
{
u32 val;
val = ftm_readl(base + FTM_SC);
val &= ~FTM_SC_TOF;
ftm_writel(val, base + FTM_SC);
}
static inline void ftm_irq_enable(void __iomem *base)
{
u32 val;
val = ftm_readl(base + FTM_SC);
val |= FTM_SC_TOIE;
ftm_writel(val, base + FTM_SC);
}
static inline void ftm_irq_disable(void __iomem *base)
{
u32 val;
val = ftm_readl(base + FTM_SC);
val &= ~FTM_SC_TOIE;
ftm_writel(val, base + FTM_SC);
}
static inline void ftm_reset_counter(void __iomem *base)
{
/*
* The CNT register contains the FTM counter value.
* Reset clears the CNT register. Writing any value to COUNT
* updates the counter with its initial value, CNTIN.
*/
ftm_writel(0x00, base + FTM_CNT);
}
static u64 notrace ftm_read_sched_clock(void)
{
return ftm_readl(priv->clksrc_base + FTM_CNT);
}
static int ftm_set_next_event(unsigned long delta,
struct clock_event_device *unused)
{
/*
* The CNNIN and MOD are all double buffer registers, writing
* to the MOD register latches the value into a buffer. The MOD
* register is updated with the value of its write buffer with
* the following scenario:
* a, the counter source clock is diabled.
*/
ftm_counter_disable(priv->clkevt_base);
/* Force the value of CNTIN to be loaded into the FTM counter */
ftm_reset_counter(priv->clkevt_base);
/*
* The counter increments until the value of MOD is reached,
* at which point the counter is reloaded with the value of CNTIN.
* The TOF (the overflow flag) bit is set when the FTM counter
* changes from MOD to CNTIN. So we should using the delta - 1.
*/
ftm_writel(delta - 1, priv->clkevt_base + FTM_MOD);
ftm_counter_enable(priv->clkevt_base);
ftm_irq_enable(priv->clkevt_base);
return 0;
}
static int ftm_set_oneshot(struct clock_event_device *evt)
{
ftm_counter_disable(priv->clkevt_base);
return 0;
}
static int ftm_set_periodic(struct clock_event_device *evt)
{
ftm_set_next_event(priv->periodic_cyc, evt);
return 0;
}
static irqreturn_t ftm_evt_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
ftm_irq_acknowledge(priv->clkevt_base);
if (likely(clockevent_state_oneshot(evt))) {
ftm_irq_disable(priv->clkevt_base);
ftm_counter_disable(priv->clkevt_base);
}
evt->event_handler(evt);
return IRQ_HANDLED;
}
static struct clock_event_device ftm_clockevent = {
.name = "Freescale ftm timer",
.features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT,
.set_state_periodic = ftm_set_periodic,
.set_state_oneshot = ftm_set_oneshot,
.set_next_event = ftm_set_next_event,
.rating = 300,
};
static struct irqaction ftm_timer_irq = {
.name = "Freescale ftm timer",
.flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = ftm_evt_interrupt,
.dev_id = &ftm_clockevent,
};
static int __init ftm_clockevent_init(unsigned long freq, int irq)
{
int err;
ftm_writel(0x00, priv->clkevt_base + FTM_CNTIN);
ftm_writel(~0u, priv->clkevt_base + FTM_MOD);
ftm_reset_counter(priv->clkevt_base);
err = setup_irq(irq, &ftm_timer_irq);
if (err) {
pr_err("ftm: setup irq failed: %d\n", err);
return err;
}
ftm_clockevent.cpumask = cpumask_of(0);
ftm_clockevent.irq = irq;
clockevents_config_and_register(&ftm_clockevent,
freq / (1 << priv->ps),
1, 0xffff);
ftm_counter_enable(priv->clkevt_base);
return 0;
}
static int __init ftm_clocksource_init(unsigned long freq)
{
int err;
ftm_writel(0x00, priv->clksrc_base + FTM_CNTIN);
ftm_writel(~0u, priv->clksrc_base + FTM_MOD);
ftm_reset_counter(priv->clksrc_base);
sched_clock_register(ftm_read_sched_clock, 16, freq / (1 << priv->ps));
err = clocksource_mmio_init(priv->clksrc_base + FTM_CNT, "fsl-ftm",
freq / (1 << priv->ps), 300, 16,
clocksource_mmio_readl_up);
if (err) {
pr_err("ftm: init clock source mmio failed: %d\n", err);
return err;
}
ftm_counter_enable(priv->clksrc_base);
return 0;
}
static int __init __ftm_clk_init(struct device_node *np, char *cnt_name,
char *ftm_name)
{
struct clk *clk;
int err;
clk = of_clk_get_by_name(np, cnt_name);
if (IS_ERR(clk)) {
pr_err("ftm: Cannot get \"%s\": %ld\n", cnt_name, PTR_ERR(clk));
return PTR_ERR(clk);
}
err = clk_prepare_enable(clk);
if (err) {
pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n",
cnt_name, err);
return err;
}
clk = of_clk_get_by_name(np, ftm_name);
if (IS_ERR(clk)) {
pr_err("ftm: Cannot get \"%s\": %ld\n", ftm_name, PTR_ERR(clk));
return PTR_ERR(clk);
}
err = clk_prepare_enable(clk);
if (err)
pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n",
ftm_name, err);
return clk_get_rate(clk);
}
static unsigned long __init ftm_clk_init(struct device_node *np)
{
long freq;
freq = __ftm_clk_init(np, "ftm-evt-counter-en", "ftm-evt");
if (freq <= 0)
return 0;
freq = __ftm_clk_init(np, "ftm-src-counter-en", "ftm-src");
if (freq <= 0)
return 0;
return freq;
}
static int __init ftm_calc_closest_round_cyc(unsigned long freq)
{
priv->ps = 0;
/* The counter register is only using the lower 16 bits, and
* if the 'freq' value is to big here, then the periodic_cyc
* may exceed 0xFFFF.
*/
do {
priv->periodic_cyc = DIV_ROUND_CLOSEST(freq,
HZ * (1 << priv->ps++));
} while (priv->periodic_cyc > 0xFFFF);
if (priv->ps > FTM_PS_MAX) {
pr_err("ftm: the prescaler is %lu > %d\n",
priv->ps, FTM_PS_MAX);
return -EINVAL;
}
return 0;
}
static int __init ftm_timer_init(struct device_node *np)
{
unsigned long freq;
int ret, irq;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
ret = -ENXIO;
priv->clkevt_base = of_iomap(np, 0);
if (!priv->clkevt_base) {
pr_err("ftm: unable to map event timer registers\n");
goto err_clkevt;
}
priv->clksrc_base = of_iomap(np, 1);
if (!priv->clksrc_base) {
pr_err("ftm: unable to map source timer registers\n");
goto err_clksrc;
}
ret = -EINVAL;
irq = irq_of_parse_and_map(np, 0);
if (irq <= 0) {
pr_err("ftm: unable to get IRQ from DT, %d\n", irq);
goto err;
}
priv->big_endian = of_property_read_bool(np, "big-endian");
freq = ftm_clk_init(np);
if (!freq)
goto err;
ret = ftm_calc_closest_round_cyc(freq);
if (ret)
goto err;
ret = ftm_clocksource_init(freq);
if (ret)
goto err;
ret = ftm_clockevent_init(freq, irq);
if (ret)
goto err;
return 0;
err:
iounmap(priv->clksrc_base);
err_clksrc:
iounmap(priv->clkevt_base);
err_clkevt:
kfree(priv);
return ret;
}
TIMER_OF_DECLARE(flextimer, "fsl,ftm-timer", ftm_timer_init);
+192
View File
@@ -0,0 +1,192 @@
// SPDX-License-Identifier: GPL-2.0
/*
* H8/300 16bit Timer driver
*
* Copyright 2015 Yoshinori Sato <ysato@users.sourcefoge.jp>
*/
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/clocksource.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#define TSTR 0
#define TISRC 6
#define TCR 0
#define TCNT 2
#define bset(b, a) iowrite8(ioread8(a) | (1 << (b)), (a))
#define bclr(b, a) iowrite8(ioread8(a) & ~(1 << (b)), (a))
struct timer16_priv {
struct clocksource cs;
unsigned long total_cycles;
void __iomem *mapbase;
void __iomem *mapcommon;
unsigned short cs_enabled;
unsigned char enb;
unsigned char ovf;
unsigned char ovie;
};
static unsigned long timer16_get_counter(struct timer16_priv *p)
{
unsigned short v1, v2, v3;
unsigned char o1, o2;
o1 = ioread8(p->mapcommon + TISRC) & p->ovf;
/* Make sure the timer value is stable. Stolen from acpi_pm.c */
do {
o2 = o1;
v1 = ioread16be(p->mapbase + TCNT);
v2 = ioread16be(p->mapbase + TCNT);
v3 = ioread16be(p->mapbase + TCNT);
o1 = ioread8(p->mapcommon + TISRC) & p->ovf;
} while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3)
|| (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2)));
if (likely(!o1))
return v2;
else
return v2 + 0x10000;
}
static irqreturn_t timer16_interrupt(int irq, void *dev_id)
{
struct timer16_priv *p = (struct timer16_priv *)dev_id;
bclr(p->ovf, p->mapcommon + TISRC);
p->total_cycles += 0x10000;
return IRQ_HANDLED;
}
static inline struct timer16_priv *cs_to_priv(struct clocksource *cs)
{
return container_of(cs, struct timer16_priv, cs);
}
static u64 timer16_clocksource_read(struct clocksource *cs)
{
struct timer16_priv *p = cs_to_priv(cs);
unsigned long raw, value;
value = p->total_cycles;
raw = timer16_get_counter(p);
return value + raw;
}
static int timer16_enable(struct clocksource *cs)
{
struct timer16_priv *p = cs_to_priv(cs);
WARN_ON(p->cs_enabled);
p->total_cycles = 0;
iowrite16be(0x0000, p->mapbase + TCNT);
iowrite8(0x83, p->mapbase + TCR);
bset(p->ovie, p->mapcommon + TISRC);
bset(p->enb, p->mapcommon + TSTR);
p->cs_enabled = true;
return 0;
}
static void timer16_disable(struct clocksource *cs)
{
struct timer16_priv *p = cs_to_priv(cs);
WARN_ON(!p->cs_enabled);
bclr(p->ovie, p->mapcommon + TISRC);
bclr(p->enb, p->mapcommon + TSTR);
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_COMM 1
static int __init h8300_16timer_init(struct device_node *node)
{
void __iomem *base[2];
int ret, irq;
unsigned int ch;
struct clk *clk;
clk = of_clk_get(node, 0);
if (IS_ERR(clk)) {
pr_err("failed to get clock for clocksource\n");
return PTR_ERR(clk);
}
ret = -ENXIO;
base[REG_CH] = of_iomap(node, 0);
if (!base[REG_CH]) {
pr_err("failed to map registers for clocksource\n");
goto free_clk;
}
base[REG_COMM] = of_iomap(node, 1);
if (!base[REG_COMM]) {
pr_err("failed to map registers for clocksource\n");
goto unmap_ch;
}
ret = -EINVAL;
irq = irq_of_parse_and_map(node, 0);
if (!irq) {
pr_err("failed to get irq for clockevent\n");
goto unmap_comm;
}
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,
IRQF_TIMER, timer16_priv.cs.name, &timer16_priv);
if (ret < 0) {
pr_err("failed to request irq %d of clocksource\n", irq);
goto unmap_comm;
}
clocksource_register_hz(&timer16_priv.cs,
clk_get_rate(clk) / 8);
return 0;
unmap_comm:
iounmap(base[REG_COMM]);
unmap_ch:
iounmap(base[REG_CH]);
free_clk:
clk_put(clk);
return ret;
}
TIMER_OF_DECLARE(h8300_16bit, "renesas,16bit-timer",
h8300_16timer_init);
+211
View File
@@ -0,0 +1,211 @@
// SPDX-License-Identifier: GPL-2.0
/*
* linux/arch/h8300/kernel/cpu/timer/timer8.c
*
* Yoshinori Sato <ysato@users.sourcefoge.jp>
*
* 8bit Timer driver
*
*/
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/clockchips.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#define _8TCR 0
#define _8TCSR 2
#define TCORA 4
#define TCORB 6
#define _8TCNT 8
#define CMIEA 6
#define CMFA 6
#define FLAG_STARTED (1 << 3)
#define SCALE 64
#define bset(b, a) iowrite8(ioread8(a) | (1 << (b)), (a))
#define bclr(b, a) iowrite8(ioread8(a) & ~(1 << (b)), (a))
struct timer8_priv {
struct clock_event_device ced;
void __iomem *mapbase;
unsigned long flags;
unsigned int rate;
};
static irqreturn_t timer8_interrupt(int irq, void *dev_id)
{
struct timer8_priv *p = dev_id;
if (clockevent_state_oneshot(&p->ced))
iowrite16be(0x0000, p->mapbase + _8TCR);
p->ced.event_handler(&p->ced);
bclr(CMFA, p->mapbase + _8TCSR);
return IRQ_HANDLED;
}
static void timer8_set_next(struct timer8_priv *p, unsigned long delta)
{
if (delta >= 0x10000)
pr_warn("delta out of range\n");
bclr(CMIEA, p->mapbase + _8TCR);
iowrite16be(delta, p->mapbase + TCORA);
iowrite16be(0x0000, p->mapbase + _8TCNT);
bclr(CMFA, p->mapbase + _8TCSR);
bset(CMIEA, p->mapbase + _8TCR);
}
static int timer8_enable(struct timer8_priv *p)
{
iowrite16be(0xffff, p->mapbase + TCORA);
iowrite16be(0x0000, p->mapbase + _8TCNT);
iowrite16be(0x0c02, p->mapbase + _8TCR);
return 0;
}
static int timer8_start(struct timer8_priv *p)
{
int ret;
if ((p->flags & FLAG_STARTED))
return 0;
ret = timer8_enable(p);
if (!ret)
p->flags |= FLAG_STARTED;
return ret;
}
static void timer8_stop(struct timer8_priv *p)
{
iowrite16be(0x0000, p->mapbase + _8TCR);
}
static inline struct timer8_priv *ced_to_priv(struct clock_event_device *ced)
{
return container_of(ced, struct timer8_priv, ced);
}
static void timer8_clock_event_start(struct timer8_priv *p, unsigned long delta)
{
timer8_start(p);
timer8_set_next(p, delta);
}
static int timer8_clock_event_shutdown(struct clock_event_device *ced)
{
timer8_stop(ced_to_priv(ced));
return 0;
}
static int timer8_clock_event_periodic(struct clock_event_device *ced)
{
struct timer8_priv *p = ced_to_priv(ced);
pr_info("%s: used for periodic clock events\n", ced->name);
timer8_stop(p);
timer8_clock_event_start(p, (p->rate + HZ/2) / HZ);
return 0;
}
static int timer8_clock_event_oneshot(struct clock_event_device *ced)
{
struct timer8_priv *p = ced_to_priv(ced);
pr_info("%s: used for oneshot clock events\n", ced->name);
timer8_stop(p);
timer8_clock_event_start(p, 0x10000);
return 0;
}
static int timer8_clock_event_next(unsigned long delta,
struct clock_event_device *ced)
{
struct timer8_priv *p = ced_to_priv(ced);
BUG_ON(!clockevent_state_oneshot(ced));
timer8_set_next(p, delta - 1);
return 0;
}
static struct timer8_priv timer8_priv = {
.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 int __init h8300_8timer_init(struct device_node *node)
{
void __iomem *base;
int irq, ret;
struct clk *clk;
clk = of_clk_get(node, 0);
if (IS_ERR(clk)) {
pr_err("failed to get clock for clockevent\n");
return PTR_ERR(clk);
}
ret = -ENXIO;
base = of_iomap(node, 0);
if (!base) {
pr_err("failed to map registers for clockevent\n");
goto free_clk;
}
ret = -EINVAL;
irq = irq_of_parse_and_map(node, 0);
if (!irq) {
pr_err("failed to get irq for clockevent\n");
goto unmap_reg;
}
timer8_priv.mapbase = base;
timer8_priv.rate = clk_get_rate(clk) / SCALE;
if (!timer8_priv.rate) {
pr_err("Failed to get rate for the clocksource\n");
goto unmap_reg;
}
if (request_irq(irq, timer8_interrupt, IRQF_TIMER,
timer8_priv.ced.name, &timer8_priv) < 0) {
pr_err("failed to request irq %d for clockevent\n", irq);
goto unmap_reg;
}
clockevents_config_and_register(&timer8_priv.ced,
timer8_priv.rate, 1, 0x0000ffff);
return 0;
unmap_reg:
iounmap(base);
free_clk:
clk_put(clk);
return ret;
}
TIMER_OF_DECLARE(h8300_8bit, "renesas,8bit-timer", h8300_8timer_init);
+158
View File
@@ -0,0 +1,158 @@
// SPDX-License-Identifier: GPL-2.0
/*
* H8S TPU Driver
*
* Copyright 2015 Yoshinori Sato <ysato@users.sourcefoge.jp>
*
*/
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/clocksource.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#define TCR 0x0
#define TSR 0x5
#define TCNT 0x6
#define TCFV 0x10
struct tpu_priv {
struct clocksource cs;
void __iomem *mapbase1;
void __iomem *mapbase2;
raw_spinlock_t lock;
unsigned int cs_enabled;
};
static inline unsigned long read_tcnt32(struct tpu_priv *p)
{
unsigned long tcnt;
tcnt = ioread16be(p->mapbase1 + TCNT) << 16;
tcnt |= ioread16be(p->mapbase2 + TCNT);
return tcnt;
}
static int tpu_get_counter(struct tpu_priv *p, unsigned long long *val)
{
unsigned long v1, v2, v3;
int o1, o2;
o1 = ioread8(p->mapbase1 + TSR) & TCFV;
/* Make sure the timer value is stable. Stolen from acpi_pm.c */
do {
o2 = o1;
v1 = read_tcnt32(p);
v2 = read_tcnt32(p);
v3 = read_tcnt32(p);
o1 = ioread8(p->mapbase1 + TSR) & TCFV;
} while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3)
|| (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2)));
*val = v2;
return o1;
}
static inline struct tpu_priv *cs_to_priv(struct clocksource *cs)
{
return container_of(cs, struct tpu_priv, cs);
}
static u64 tpu_clocksource_read(struct clocksource *cs)
{
struct tpu_priv *p = cs_to_priv(cs);
unsigned long flags;
unsigned long long value;
raw_spin_lock_irqsave(&p->lock, flags);
if (tpu_get_counter(p, &value))
value += 0x100000000;
raw_spin_unlock_irqrestore(&p->lock, flags);
return value;
}
static int tpu_clocksource_enable(struct clocksource *cs)
{
struct tpu_priv *p = cs_to_priv(cs);
WARN_ON(p->cs_enabled);
iowrite16be(0, p->mapbase1 + TCNT);
iowrite16be(0, p->mapbase2 + TCNT);
iowrite8(0x0f, p->mapbase1 + TCR);
iowrite8(0x03, p->mapbase2 + TCR);
p->cs_enabled = true;
return 0;
}
static void tpu_clocksource_disable(struct clocksource *cs)
{
struct tpu_priv *p = cs_to_priv(cs);
WARN_ON(!p->cs_enabled);
iowrite8(0, p->mapbase1 + TCR);
iowrite8(0, p->mapbase2 + TCR);
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_H 1
static int __init h8300_tpu_init(struct device_node *node)
{
void __iomem *base[2];
struct clk *clk;
int ret = -ENXIO;
clk = of_clk_get(node, 0);
if (IS_ERR(clk)) {
pr_err("failed to get clock for clocksource\n");
return PTR_ERR(clk);
}
base[CH_L] = of_iomap(node, CH_L);
if (!base[CH_L]) {
pr_err("failed to map registers for clocksource\n");
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;
}
tpu_priv.mapbase1 = base[CH_L];
tpu_priv.mapbase2 = base[CH_H];
return clocksource_register_hz(&tpu_priv.cs, clk_get_rate(clk) / 64);
unmap_L:
iounmap(base[CH_H]);
free_clk:
clk_put(clk);
return ret;
}
TIMER_OF_DECLARE(h8300_tpu, "renesas,tpu", h8300_tpu_init);
+196
View File
@@ -0,0 +1,196 @@
// SPDX-License-Identifier: GPL-2.0
/*
* i8253 PIT clocksource
*/
#include <linux/clockchips.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/spinlock.h>
#include <linux/timex.h>
#include <linux/module.h>
#include <linux/i8253.h>
#include <linux/smp.h>
/*
* Protects access to I/O ports
*
* 0040-0043 : timer0, i8253 / i8254
* 0061-0061 : NMI Control Register which contains two speaker control bits.
*/
DEFINE_RAW_SPINLOCK(i8253_lock);
EXPORT_SYMBOL(i8253_lock);
/*
* Handle PIT quirk in pit_shutdown() where zeroing the counter register
* restarts the PIT, negating the shutdown. On platforms with the quirk,
* platform specific code can set this to false.
*/
bool i8253_clear_counter_on_shutdown __ro_after_init = true;
#ifdef CONFIG_CLKSRC_I8253
/*
* Since the PIT overflows every tick, its not very useful
* to just read by itself. So use jiffies to emulate a free
* running counter:
*/
static u64 i8253_read(struct clocksource *cs)
{
static int old_count;
static u32 old_jifs;
unsigned long flags;
int count;
u32 jifs;
raw_spin_lock_irqsave(&i8253_lock, flags);
/*
* Although our caller may have the read side of jiffies_lock,
* this is now a seqlock, and we are cheating in this routine
* by having side effects on state that we cannot undo if
* there is a collision on the seqlock and our caller has to
* retry. (Namely, old_jifs and old_count.) So we must treat
* jiffies as volatile despite the lock. We read jiffies
* before latching the timer count to guarantee that although
* the jiffies value might be older than the count (that is,
* the counter may underflow between the last point where
* jiffies was incremented and the point where we latch the
* count), it cannot be newer.
*/
jifs = jiffies;
outb_p(0x00, PIT_MODE); /* latch the count ASAP */
count = inb_p(PIT_CH0); /* read the latched count */
count |= inb_p(PIT_CH0) << 8;
/* VIA686a test code... reset the latch if count > max + 1 */
if (count > PIT_LATCH) {
outb_p(0x34, PIT_MODE);
outb_p(PIT_LATCH & 0xff, PIT_CH0);
outb_p(PIT_LATCH >> 8, PIT_CH0);
count = PIT_LATCH - 1;
}
/*
* It's possible for count to appear to go the wrong way for a
* couple of reasons:
*
* 1. The timer counter underflows, but we haven't handled the
* resulting interrupt and incremented jiffies yet.
* 2. Hardware problem with the timer, not giving us continuous time,
* the counter does small "jumps" upwards on some Pentium systems,
* (see c't 95/10 page 335 for Neptun bug.)
*
* Previous attempts to handle these cases intelligently were
* buggy, so we just do the simple thing now.
*/
if (count > old_count && jifs == old_jifs)
count = old_count;
old_count = count;
old_jifs = jifs;
raw_spin_unlock_irqrestore(&i8253_lock, flags);
count = (PIT_LATCH - 1) - count;
return (u64)(jifs * PIT_LATCH) + count;
}
static struct clocksource i8253_cs = {
.name = "pit",
.rating = 110,
.read = i8253_read,
.mask = CLOCKSOURCE_MASK(32),
};
int __init clocksource_i8253_init(void)
{
return clocksource_register_hz(&i8253_cs, PIT_TICK_RATE);
}
#endif
#ifdef CONFIG_CLKEVT_I8253
static int pit_shutdown(struct clock_event_device *evt)
{
if (!clockevent_state_oneshot(evt) && !clockevent_state_periodic(evt))
return 0;
raw_spin_lock(&i8253_lock);
outb_p(0x30, PIT_MODE);
if (i8253_clear_counter_on_shutdown) {
outb_p(0, PIT_CH0);
outb_p(0, PIT_CH0);
}
raw_spin_unlock(&i8253_lock);
return 0;
}
static int pit_set_oneshot(struct clock_event_device *evt)
{
raw_spin_lock(&i8253_lock);
outb_p(0x38, PIT_MODE);
raw_spin_unlock(&i8253_lock);
return 0;
}
static int pit_set_periodic(struct clock_event_device *evt)
{
raw_spin_lock(&i8253_lock);
/* binary, mode 2, LSB/MSB, ch 0 */
outb_p(0x34, PIT_MODE);
outb_p(PIT_LATCH & 0xff, PIT_CH0); /* LSB */
outb_p(PIT_LATCH >> 8, PIT_CH0); /* MSB */
raw_spin_unlock(&i8253_lock);
return 0;
}
/*
* Program the next event in oneshot mode
*
* Delta is given in PIT ticks
*/
static int pit_next_event(unsigned long delta, struct clock_event_device *evt)
{
raw_spin_lock(&i8253_lock);
outb_p(delta & 0xff , PIT_CH0); /* LSB */
outb_p(delta >> 8 , PIT_CH0); /* MSB */
raw_spin_unlock(&i8253_lock);
return 0;
}
/*
* On UP the PIT can serve all of the possible timer functions. On SMP systems
* it can be solely used for the global tick.
*/
struct clock_event_device i8253_clockevent = {
.name = "pit",
.features = CLOCK_EVT_FEAT_PERIODIC,
.set_state_shutdown = pit_shutdown,
.set_state_periodic = pit_set_periodic,
.set_next_event = pit_next_event,
};
/*
* Initialize the conversion factor and the min/max deltas of the clock event
* structure and register the clock event source with the framework.
*/
void __init clockevent_i8253_init(bool oneshot)
{
if (oneshot) {
i8253_clockevent.features |= CLOCK_EVT_FEAT_ONESHOT;
i8253_clockevent.set_state_oneshot = pit_set_oneshot;
}
/*
* Start pit with the boot cpu mask. x86 might make it global
* when it is used as broadcast device later.
*/
i8253_clockevent.cpumask = cpumask_of(smp_processor_id());
clockevents_config_and_register(&i8253_clockevent, PIT_TICK_RATE,
0xF, 0x7FFF);
}
#endif
+249
View File
@@ -0,0 +1,249 @@
/*
* J-Core SoC PIT/clocksource driver
*
* Copyright (C) 2015-2016 Smart Energy Instruments, Inc.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/sched_clock.h>
#include <linux/cpu.h>
#include <linux/cpuhotplug.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#define PIT_IRQ_SHIFT 12
#define PIT_PRIO_SHIFT 20
#define PIT_ENABLE_SHIFT 26
#define PIT_PRIO_MASK 0xf
#define REG_PITEN 0x00
#define REG_THROT 0x10
#define REG_COUNT 0x14
#define REG_BUSPD 0x18
#define REG_SECHI 0x20
#define REG_SECLO 0x24
#define REG_NSEC 0x28
struct jcore_pit {
struct clock_event_device ced;
void __iomem *base;
unsigned long periodic_delta;
u32 enable_val;
};
static void __iomem *jcore_pit_base;
static struct jcore_pit __percpu *jcore_pit_percpu;
static notrace u64 jcore_sched_clock_read(void)
{
u32 seclo, nsec, seclo0;
__iomem void *base = jcore_pit_base;
seclo = readl(base + REG_SECLO);
do {
seclo0 = seclo;
nsec = readl(base + REG_NSEC);
seclo = readl(base + REG_SECLO);
} while (seclo0 != seclo);
return seclo * NSEC_PER_SEC + nsec;
}
static u64 jcore_clocksource_read(struct clocksource *cs)
{
return jcore_sched_clock_read();
}
static int jcore_pit_disable(struct jcore_pit *pit)
{
writel(0, pit->base + REG_PITEN);
return 0;
}
static int jcore_pit_set(unsigned long delta, struct jcore_pit *pit)
{
jcore_pit_disable(pit);
writel(delta, pit->base + REG_THROT);
writel(pit->enable_val, pit->base + REG_PITEN);
return 0;
}
static int jcore_pit_set_state_shutdown(struct clock_event_device *ced)
{
struct jcore_pit *pit = container_of(ced, struct jcore_pit, ced);
return jcore_pit_disable(pit);
}
static int jcore_pit_set_state_oneshot(struct clock_event_device *ced)
{
struct jcore_pit *pit = container_of(ced, struct jcore_pit, ced);
return jcore_pit_disable(pit);
}
static int jcore_pit_set_state_periodic(struct clock_event_device *ced)
{
struct jcore_pit *pit = container_of(ced, struct jcore_pit, ced);
return jcore_pit_set(pit->periodic_delta, pit);
}
static int jcore_pit_set_next_event(unsigned long delta,
struct clock_event_device *ced)
{
struct jcore_pit *pit = container_of(ced, struct jcore_pit, ced);
return jcore_pit_set(delta, pit);
}
static int jcore_pit_local_init(unsigned cpu)
{
struct jcore_pit *pit = this_cpu_ptr(jcore_pit_percpu);
unsigned buspd, freq;
pr_info("Local J-Core PIT init on cpu %u\n", cpu);
buspd = readl(pit->base + REG_BUSPD);
freq = DIV_ROUND_CLOSEST(NSEC_PER_SEC, buspd);
pit->periodic_delta = DIV_ROUND_CLOSEST(NSEC_PER_SEC, HZ * buspd);
clockevents_config_and_register(&pit->ced, freq, 1, ULONG_MAX);
return 0;
}
static irqreturn_t jcore_timer_interrupt(int irq, void *dev_id)
{
struct jcore_pit *pit = this_cpu_ptr(dev_id);
if (clockevent_state_oneshot(&pit->ced))
jcore_pit_disable(pit);
pit->ced.event_handler(&pit->ced);
return IRQ_HANDLED;
}
static int __init jcore_pit_init(struct device_node *node)
{
int err;
unsigned pit_irq, cpu;
unsigned long hwirq;
u32 irqprio, enable_val;
jcore_pit_base = of_iomap(node, 0);
if (!jcore_pit_base) {
pr_err("Error: Cannot map base address for J-Core PIT\n");
return -ENXIO;
}
pit_irq = irq_of_parse_and_map(node, 0);
if (!pit_irq) {
pr_err("Error: J-Core PIT has no IRQ\n");
return -ENXIO;
}
pr_info("Initializing J-Core PIT at %p IRQ %d\n",
jcore_pit_base, pit_irq);
err = clocksource_mmio_init(jcore_pit_base, "jcore_pit_cs",
NSEC_PER_SEC, 400, 32,
jcore_clocksource_read);
if (err) {
pr_err("Error registering clocksource device: %d\n", err);
return err;
}
sched_clock_register(jcore_sched_clock_read, 32, NSEC_PER_SEC);
jcore_pit_percpu = alloc_percpu(struct jcore_pit);
if (!jcore_pit_percpu) {
pr_err("Failed to allocate memory for clock event device\n");
return -ENOMEM;
}
err = request_irq(pit_irq, jcore_timer_interrupt,
IRQF_TIMER | IRQF_PERCPU,
"jcore_pit", jcore_pit_percpu);
if (err) {
pr_err("pit irq request failed: %d\n", err);
free_percpu(jcore_pit_percpu);
return err;
}
/*
* The J-Core PIT is not hard-wired to a particular IRQ, but
* integrated with the interrupt controller such that the IRQ it
* generates is programmable, as follows:
*
* The bit layout of the PIT enable register is:
*
* .....e..ppppiiiiiiii............
*
* where the .'s indicate unrelated/unused bits, e is enable,
* p is priority, and i is hard irq number.
*
* For the PIT included in AIC1 (obsolete but still in use),
* any hard irq (trap number) can be programmed via the 8
* iiiiiiii bits, and a priority (0-15) is programmable
* separately in the pppp bits.
*
* For the PIT included in AIC2 (current), the programming
* interface is equivalent modulo interrupt mapping. This is
* why a different compatible tag was not used. However only
* traps 64-127 (the ones actually intended to be used for
* interrupts, rather than syscalls/exceptions/etc.) can be
* programmed (the high 2 bits of i are ignored) and the
* priority pppp is <<2'd and or'd onto the irq number. This
* choice seems to have been made on the hardware engineering
* side under an assumption that preserving old AIC1 priority
* mappings was important. Future models will likely ignore
* the pppp field.
*/
hwirq = irq_get_irq_data(pit_irq)->hwirq;
irqprio = (hwirq >> 2) & PIT_PRIO_MASK;
enable_val = (1U << PIT_ENABLE_SHIFT)
| (hwirq << PIT_IRQ_SHIFT)
| (irqprio << PIT_PRIO_SHIFT);
for_each_present_cpu(cpu) {
struct jcore_pit *pit = per_cpu_ptr(jcore_pit_percpu, cpu);
pit->base = of_iomap(node, cpu);
if (!pit->base) {
pr_err("Unable to map PIT for cpu %u\n", cpu);
continue;
}
pit->ced.name = "jcore_pit";
pit->ced.features = CLOCK_EVT_FEAT_PERIODIC
| CLOCK_EVT_FEAT_ONESHOT
| CLOCK_EVT_FEAT_PERCPU;
pit->ced.cpumask = cpumask_of(cpu);
pit->ced.rating = 400;
pit->ced.irq = pit_irq;
pit->ced.set_state_shutdown = jcore_pit_set_state_shutdown;
pit->ced.set_state_periodic = jcore_pit_set_state_periodic;
pit->ced.set_state_oneshot = jcore_pit_set_state_oneshot;
pit->ced.set_next_event = jcore_pit_set_next_event;
pit->enable_val = enable_val;
}
cpuhp_setup_state(CPUHP_AP_JCORE_TIMER_STARTING,
"clockevents/jcore:starting",
jcore_pit_local_init, NULL);
return 0;
}
TIMER_OF_DECLARE(jcore_pit, "jcore,pit", jcore_pit_init);
+178
View File
@@ -0,0 +1,178 @@
/*
* Amlogic Meson6 SoCs timer handling.
*
* Copyright (C) 2014 Carlo Caione <carlo@caione.org>
*
* Based on code from Amlogic, Inc
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqreturn.h>
#include <linux/sched_clock.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#define CED_ID 0
#define CSD_ID 4
#define TIMER_ISA_MUX 0
#define TIMER_ISA_VAL(t) (((t) + 1) << 2)
#define TIMER_INPUT_BIT(t) (2 * (t))
#define TIMER_ENABLE_BIT(t) (16 + (t))
#define TIMER_PERIODIC_BIT(t) (12 + (t))
#define TIMER_CED_INPUT_MASK (3UL << TIMER_INPUT_BIT(CED_ID))
#define TIMER_CSD_INPUT_MASK (7UL << TIMER_INPUT_BIT(CSD_ID))
#define TIMER_CED_UNIT_1US 0
#define TIMER_CSD_UNIT_1US 1
static void __iomem *timer_base;
static u64 notrace meson6_timer_sched_read(void)
{
return (u64)readl(timer_base + TIMER_ISA_VAL(CSD_ID));
}
static void meson6_clkevt_time_stop(unsigned char timer)
{
u32 val = readl(timer_base + TIMER_ISA_MUX);
writel(val & ~TIMER_ENABLE_BIT(timer), timer_base + TIMER_ISA_MUX);
}
static void meson6_clkevt_time_setup(unsigned char timer, unsigned long delay)
{
writel(delay, timer_base + TIMER_ISA_VAL(timer));
}
static void meson6_clkevt_time_start(unsigned char timer, bool periodic)
{
u32 val = readl(timer_base + TIMER_ISA_MUX);
if (periodic)
val |= TIMER_PERIODIC_BIT(timer);
else
val &= ~TIMER_PERIODIC_BIT(timer);
writel(val | TIMER_ENABLE_BIT(timer), timer_base + TIMER_ISA_MUX);
}
static int meson6_shutdown(struct clock_event_device *evt)
{
meson6_clkevt_time_stop(CED_ID);
return 0;
}
static int meson6_set_oneshot(struct clock_event_device *evt)
{
meson6_clkevt_time_stop(CED_ID);
meson6_clkevt_time_start(CED_ID, false);
return 0;
}
static int meson6_set_periodic(struct clock_event_device *evt)
{
meson6_clkevt_time_stop(CED_ID);
meson6_clkevt_time_setup(CED_ID, USEC_PER_SEC / HZ - 1);
meson6_clkevt_time_start(CED_ID, true);
return 0;
}
static int meson6_clkevt_next_event(unsigned long evt,
struct clock_event_device *unused)
{
meson6_clkevt_time_stop(CED_ID);
meson6_clkevt_time_setup(CED_ID, evt);
meson6_clkevt_time_start(CED_ID, false);
return 0;
}
static struct clock_event_device meson6_clockevent = {
.name = "meson6_tick",
.rating = 400,
.features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT,
.set_state_shutdown = meson6_shutdown,
.set_state_periodic = meson6_set_periodic,
.set_state_oneshot = meson6_set_oneshot,
.tick_resume = meson6_shutdown,
.set_next_event = meson6_clkevt_next_event,
};
static irqreturn_t meson6_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = (struct clock_event_device *)dev_id;
evt->event_handler(evt);
return IRQ_HANDLED;
}
static struct irqaction meson6_timer_irq = {
.name = "meson6_timer",
.flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = meson6_timer_interrupt,
.dev_id = &meson6_clockevent,
};
static int __init meson6_timer_init(struct device_node *node)
{
u32 val;
int ret, irq;
timer_base = of_io_request_and_map(node, 0, "meson6-timer");
if (IS_ERR(timer_base)) {
pr_err("Can't map registers\n");
return -ENXIO;
}
irq = irq_of_parse_and_map(node, 0);
if (irq <= 0) {
pr_err("Can't parse IRQ\n");
return -EINVAL;
}
/* Set 1us for timer E */
val = readl(timer_base + TIMER_ISA_MUX);
val &= ~TIMER_CSD_INPUT_MASK;
val |= TIMER_CSD_UNIT_1US << TIMER_INPUT_BIT(CSD_ID);
writel(val, timer_base + TIMER_ISA_MUX);
sched_clock_register(meson6_timer_sched_read, 32, USEC_PER_SEC);
clocksource_mmio_init(timer_base + TIMER_ISA_VAL(CSD_ID), node->name,
1000 * 1000, 300, 32, clocksource_mmio_readl_up);
/* Timer A base 1us */
val &= ~TIMER_CED_INPUT_MASK;
val |= TIMER_CED_UNIT_1US << TIMER_INPUT_BIT(CED_ID);
writel(val, timer_base + TIMER_ISA_MUX);
/* Stop the timer A */
meson6_clkevt_time_stop(CED_ID);
ret = setup_irq(irq, &meson6_timer_irq);
if (ret) {
pr_warn("failed to setup irq %d\n", irq);
return ret;
}
meson6_clockevent.cpumask = cpu_possible_mask;
meson6_clockevent.irq = irq;
clockevents_config_and_register(&meson6_clockevent, USEC_PER_SEC,
1, 0xfffe);
return 0;
}
TIMER_OF_DECLARE(meson6, "amlogic,meson6-timer",
meson6_timer_init);
+234
View File
@@ -0,0 +1,234 @@
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved.
*/
#define pr_fmt(fmt) "mips-gic-timer: " fmt
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/cpu.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/notifier.h>
#include <linux/of_irq.h>
#include <linux/percpu.h>
#include <linux/smp.h>
#include <linux/time.h>
#include <asm/mips-cps.h>
static DEFINE_PER_CPU(struct clock_event_device, gic_clockevent_device);
static int gic_timer_irq;
static unsigned int gic_frequency;
static u64 notrace gic_read_count(void)
{
unsigned int hi, hi2, lo;
if (mips_cm_is64)
return read_gic_counter();
do {
hi = read_gic_counter_32h();
lo = read_gic_counter_32l();
hi2 = read_gic_counter_32h();
} while (hi2 != hi);
return (((u64) hi) << 32) + lo;
}
static int gic_next_event(unsigned long delta, struct clock_event_device *evt)
{
int cpu = cpumask_first(evt->cpumask);
u64 cnt;
int res;
cnt = gic_read_count();
cnt += (u64)delta;
if (cpu == raw_smp_processor_id()) {
write_gic_vl_compare(cnt);
} else {
write_gic_vl_other(mips_cm_vp_id(cpu));
write_gic_vo_compare(cnt);
}
res = ((int)(gic_read_count() - cnt) >= 0) ? -ETIME : 0;
return res;
}
static irqreturn_t gic_compare_interrupt(int irq, void *dev_id)
{
struct clock_event_device *cd = dev_id;
write_gic_vl_compare(read_gic_vl_compare());
cd->event_handler(cd);
return IRQ_HANDLED;
}
struct irqaction gic_compare_irqaction = {
.handler = gic_compare_interrupt,
.percpu_dev_id = &gic_clockevent_device,
.flags = IRQF_PERCPU | IRQF_TIMER,
.name = "timer",
};
static void gic_clockevent_cpu_init(unsigned int cpu,
struct clock_event_device *cd)
{
cd->name = "MIPS GIC";
cd->features = CLOCK_EVT_FEAT_ONESHOT |
CLOCK_EVT_FEAT_C3STOP;
cd->rating = 350;
cd->irq = gic_timer_irq;
cd->cpumask = cpumask_of(cpu);
cd->set_next_event = gic_next_event;
clockevents_config_and_register(cd, gic_frequency, 0x300, 0x7fffffff);
enable_percpu_irq(gic_timer_irq, IRQ_TYPE_NONE);
}
static void gic_clockevent_cpu_exit(struct clock_event_device *cd)
{
disable_percpu_irq(gic_timer_irq);
}
static void gic_update_frequency(void *data)
{
unsigned long rate = (unsigned long)data;
clockevents_update_freq(this_cpu_ptr(&gic_clockevent_device), rate);
}
static int gic_starting_cpu(unsigned int cpu)
{
gic_clockevent_cpu_init(cpu, this_cpu_ptr(&gic_clockevent_device));
return 0;
}
static int gic_clk_notifier(struct notifier_block *nb, unsigned long action,
void *data)
{
struct clk_notifier_data *cnd = data;
if (action == POST_RATE_CHANGE)
on_each_cpu(gic_update_frequency, (void *)cnd->new_rate, 1);
return NOTIFY_OK;
}
static int gic_dying_cpu(unsigned int cpu)
{
gic_clockevent_cpu_exit(this_cpu_ptr(&gic_clockevent_device));
return 0;
}
static struct notifier_block gic_clk_nb = {
.notifier_call = gic_clk_notifier,
};
static int gic_clockevent_init(void)
{
int ret;
if (!gic_frequency)
return -ENXIO;
ret = setup_percpu_irq(gic_timer_irq, &gic_compare_irqaction);
if (ret < 0) {
pr_err("IRQ %d setup failed (%d)\n", gic_timer_irq, ret);
return ret;
}
cpuhp_setup_state(CPUHP_AP_MIPS_GIC_TIMER_STARTING,
"clockevents/mips/gic/timer:starting",
gic_starting_cpu, gic_dying_cpu);
return 0;
}
static u64 gic_hpt_read(struct clocksource *cs)
{
return gic_read_count();
}
static struct clocksource gic_clocksource = {
.name = "GIC",
.read = gic_hpt_read,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
.archdata = { .vdso_clock_mode = VDSO_CLOCK_GIC },
};
static int __init __gic_clocksource_init(void)
{
unsigned int count_width;
int ret;
/* Set clocksource mask. */
count_width = read_gic_config() & GIC_CONFIG_COUNTBITS;
count_width >>= __ffs(GIC_CONFIG_COUNTBITS);
count_width *= 4;
count_width += 32;
gic_clocksource.mask = CLOCKSOURCE_MASK(count_width);
/* Calculate a somewhat reasonable rating value. */
gic_clocksource.rating = 200 + gic_frequency / 10000000;
ret = clocksource_register_hz(&gic_clocksource, gic_frequency);
if (ret < 0)
pr_warn("Unable to register clocksource\n");
return ret;
}
static int __init gic_clocksource_of_init(struct device_node *node)
{
struct clk *clk;
int ret;
if (!mips_gic_present() || !node->parent ||
!of_device_is_compatible(node->parent, "mti,gic")) {
pr_warn("No DT definition\n");
return -ENXIO;
}
clk = of_clk_get(node, 0);
if (!IS_ERR(clk)) {
ret = clk_prepare_enable(clk);
if (ret < 0) {
pr_err("Failed to enable clock\n");
clk_put(clk);
return ret;
}
gic_frequency = clk_get_rate(clk);
} else if (of_property_read_u32(node, "clock-frequency",
&gic_frequency)) {
pr_err("Frequency not specified\n");
return -EINVAL;
}
gic_timer_irq = irq_of_parse_and_map(node, 0);
if (!gic_timer_irq) {
pr_err("IRQ not specified\n");
return -EINVAL;
}
ret = __gic_clocksource_init();
if (ret)
return ret;
ret = gic_clockevent_init();
if (!ret && !IS_ERR(clk)) {
if (clk_notifier_register(clk, &gic_clk_nb) < 0)
pr_warn("Unable to register clock notifier\n");
}
/* And finally start the counter */
clear_gic_config(GIC_CONFIG_COUNTSTOP);
return 0;
}
TIMER_OF_DECLARE(mips_gic_timer, "mti,gic-timer",
gic_clocksource_of_init);
+73
View File
@@ -0,0 +1,73 @@
/*
* Generic MMIO clocksource support
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clocksource.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
struct clocksource_mmio {
void __iomem *reg;
struct clocksource clksrc;
};
static inline struct clocksource_mmio *to_mmio_clksrc(struct clocksource *c)
{
return container_of(c, struct clocksource_mmio, clksrc);
}
u64 clocksource_mmio_readl_up(struct clocksource *c)
{
return (u64)readl_relaxed(to_mmio_clksrc(c)->reg);
}
u64 clocksource_mmio_readl_down(struct clocksource *c)
{
return ~(u64)readl_relaxed(to_mmio_clksrc(c)->reg) & c->mask;
}
u64 clocksource_mmio_readw_up(struct clocksource *c)
{
return (u64)readw_relaxed(to_mmio_clksrc(c)->reg);
}
u64 clocksource_mmio_readw_down(struct clocksource *c)
{
return ~(u64)readw_relaxed(to_mmio_clksrc(c)->reg) & c->mask;
}
/**
* clocksource_mmio_init - Initialize a simple mmio based clocksource
* @base: Virtual address of the clock readout register
* @name: Name of the clocksource
* @hz: Frequency of the clocksource in Hz
* @rating: Rating of the clocksource
* @bits: Number of valid bits
* @read: One of clocksource_mmio_read*() above
*/
int __init clocksource_mmio_init(void __iomem *base, const char *name,
unsigned long hz, int rating, unsigned bits,
u64 (*read)(struct clocksource *))
{
struct clocksource_mmio *cs;
if (bits > 64 || bits < 16)
return -EINVAL;
cs = kzalloc(sizeof(struct clocksource_mmio), GFP_KERNEL);
if (!cs)
return -ENOMEM;
cs->reg = base;
cs->clksrc.name = name;
cs->clksrc.rating = rating;
cs->clksrc.read = read;
cs->clksrc.mask = CLOCKSOURCE_MASK(bits);
cs->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS;
return clocksource_register_hz(&cs->clksrc, hz);
}
+277
View File
@@ -0,0 +1,277 @@
/*
* Copyright (C) 2015 ARM Limited
*
* Author: Vladimir Murzin <vladimir.murzin@arm.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/of_address.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/sched_clock.h>
#include <linux/slab.h>
#define TIMER_CTRL 0x0
#define TIMER_CTRL_ENABLE BIT(0)
#define TIMER_CTRL_IE BIT(3)
#define TIMER_VALUE 0x4
#define TIMER_RELOAD 0x8
#define TIMER_INT 0xc
struct clockevent_mps2 {
void __iomem *reg;
u32 clock_count_per_tick;
struct clock_event_device clkevt;
};
static void __iomem *sched_clock_base;
static u64 notrace mps2_sched_read(void)
{
return ~readl_relaxed(sched_clock_base + TIMER_VALUE);
}
static inline struct clockevent_mps2 *to_mps2_clkevt(struct clock_event_device *c)
{
return container_of(c, struct clockevent_mps2, clkevt);
}
static void clockevent_mps2_writel(u32 val, struct clock_event_device *c, u32 offset)
{
writel_relaxed(val, to_mps2_clkevt(c)->reg + offset);
}
static int mps2_timer_shutdown(struct clock_event_device *ce)
{
clockevent_mps2_writel(0, ce, TIMER_RELOAD);
clockevent_mps2_writel(0, ce, TIMER_CTRL);
return 0;
}
static int mps2_timer_set_next_event(unsigned long next, struct clock_event_device *ce)
{
clockevent_mps2_writel(next, ce, TIMER_VALUE);
clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce, TIMER_CTRL);
return 0;
}
static int mps2_timer_set_periodic(struct clock_event_device *ce)
{
u32 clock_count_per_tick = to_mps2_clkevt(ce)->clock_count_per_tick;
clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_RELOAD);
clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_VALUE);
clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce, TIMER_CTRL);
return 0;
}
static irqreturn_t mps2_timer_interrupt(int irq, void *dev_id)
{
struct clockevent_mps2 *ce = dev_id;
u32 status = readl_relaxed(ce->reg + TIMER_INT);
if (!status) {
pr_warn("spurious interrupt\n");
return IRQ_NONE;
}
writel_relaxed(1, ce->reg + TIMER_INT);
ce->clkevt.event_handler(&ce->clkevt);
return IRQ_HANDLED;
}
static int __init mps2_clockevent_init(struct device_node *np)
{
void __iomem *base;
struct clk *clk = NULL;
struct clockevent_mps2 *ce;
u32 rate;
int irq, ret;
const char *name = "mps2-clkevt";
ret = of_property_read_u32(np, "clock-frequency", &rate);
if (ret) {
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
pr_err("failed to get clock for clockevent: %d\n", ret);
goto out;
}
ret = clk_prepare_enable(clk);
if (ret) {
pr_err("failed to enable clock for clockevent: %d\n", ret);
goto out_clk_put;
}
rate = clk_get_rate(clk);
}
base = of_iomap(np, 0);
if (!base) {
ret = -EADDRNOTAVAIL;
pr_err("failed to map register for clockevent: %d\n", ret);
goto out_clk_disable;
}
irq = irq_of_parse_and_map(np, 0);
if (!irq) {
ret = -ENOENT;
pr_err("failed to get irq for clockevent: %d\n", ret);
goto out_iounmap;
}
ce = kzalloc(sizeof(*ce), GFP_KERNEL);
if (!ce) {
ret = -ENOMEM;
goto out_iounmap;
}
ce->reg = base;
ce->clock_count_per_tick = DIV_ROUND_CLOSEST(rate, HZ);
ce->clkevt.irq = irq;
ce->clkevt.name = name;
ce->clkevt.rating = 200;
ce->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
ce->clkevt.cpumask = cpu_possible_mask;
ce->clkevt.set_state_shutdown = mps2_timer_shutdown,
ce->clkevt.set_state_periodic = mps2_timer_set_periodic,
ce->clkevt.set_state_oneshot = mps2_timer_shutdown,
ce->clkevt.set_next_event = mps2_timer_set_next_event;
/* Ensure timer is disabled */
writel_relaxed(0, base + TIMER_CTRL);
ret = request_irq(irq, mps2_timer_interrupt, IRQF_TIMER, name, ce);
if (ret) {
pr_err("failed to request irq for clockevent: %d\n", ret);
goto out_kfree;
}
clockevents_config_and_register(&ce->clkevt, rate, 0xf, 0xffffffff);
return 0;
out_kfree:
kfree(ce);
out_iounmap:
iounmap(base);
out_clk_disable:
/* clk_{disable, unprepare, put}() can handle NULL as a parameter */
clk_disable_unprepare(clk);
out_clk_put:
clk_put(clk);
out:
return ret;
}
static int __init mps2_clocksource_init(struct device_node *np)
{
void __iomem *base;
struct clk *clk = NULL;
u32 rate;
int ret;
const char *name = "mps2-clksrc";
ret = of_property_read_u32(np, "clock-frequency", &rate);
if (ret) {
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
pr_err("failed to get clock for clocksource: %d\n", ret);
goto out;
}
ret = clk_prepare_enable(clk);
if (ret) {
pr_err("failed to enable clock for clocksource: %d\n", ret);
goto out_clk_put;
}
rate = clk_get_rate(clk);
}
base = of_iomap(np, 0);
if (!base) {
ret = -EADDRNOTAVAIL;
pr_err("failed to map register for clocksource: %d\n", ret);
goto out_clk_disable;
}
/* Ensure timer is disabled */
writel_relaxed(0, base + TIMER_CTRL);
/* ... and set it up as free-running clocksource */
writel_relaxed(0xffffffff, base + TIMER_VALUE);
writel_relaxed(0xffffffff, base + TIMER_RELOAD);
writel_relaxed(TIMER_CTRL_ENABLE, base + TIMER_CTRL);
ret = clocksource_mmio_init(base + TIMER_VALUE, name,
rate, 200, 32,
clocksource_mmio_readl_down);
if (ret) {
pr_err("failed to init clocksource: %d\n", ret);
goto out_iounmap;
}
sched_clock_base = base;
sched_clock_register(mps2_sched_read, 32, rate);
return 0;
out_iounmap:
iounmap(base);
out_clk_disable:
/* clk_{disable, unprepare, put}() can handle NULL as a parameter */
clk_disable_unprepare(clk);
out_clk_put:
clk_put(clk);
out:
return ret;
}
static int __init mps2_timer_init(struct device_node *np)
{
static int has_clocksource, has_clockevent;
int ret;
if (!has_clocksource) {
ret = mps2_clocksource_init(np);
if (!ret) {
has_clocksource = 1;
return 0;
}
}
if (!has_clockevent) {
ret = mps2_clockevent_init(np);
if (!ret) {
has_clockevent = 1;
return 0;
}
}
return 0;
}
TIMER_OF_DECLARE(mps2_timer, "arm,mps2-timer", mps2_timer_init);
File diff suppressed because it is too large. Load diff
+279
View File
@@ -0,0 +1,279 @@
// SPDX-License-Identifier: GPL-2.0+
//
// Copyright (C) 2000-2001 Deep Blue Solutions
// Copyright (C) 2002 Shane Nay (shane@minirl.com)
// Copyright (C) 2006-2007 Pavel Pisa (ppisa@pikron.com)
// Copyright (C) 2008 Juergen Beisert (kernel@pengutronix.de)
// Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved.
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/clockchips.h>
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/stmp_device.h>
#include <linux/sched_clock.h>
/*
* There are 2 versions of the timrot on Freescale MXS-based SoCs.
* The v1 on MX23 only gets 16 bits counter, while v2 on MX28
* extends the counter to 32 bits.
*
* The implementation uses two timers, one for clock_event and
* another for clocksource. MX28 uses timrot 0 and 1, while MX23
* uses 0 and 2.
*/
#define MX23_TIMROT_VERSION_OFFSET 0x0a0
#define MX28_TIMROT_VERSION_OFFSET 0x120
#define BP_TIMROT_MAJOR_VERSION 24
#define BV_TIMROT_VERSION_1 0x01
#define BV_TIMROT_VERSION_2 0x02
#define timrot_is_v1() (timrot_major_version == BV_TIMROT_VERSION_1)
/*
* There are 4 registers for each timrotv2 instance, and 2 registers
* for each timrotv1. So address step 0x40 in macros below strides
* one instance of timrotv2 while two instances of timrotv1.
*
* As the result, HW_TIMROT_XXXn(1) defines the address of timrot1
* on MX28 while timrot2 on MX23.
*/
/* common between v1 and v2 */
#define HW_TIMROT_ROTCTRL 0x00
#define HW_TIMROT_TIMCTRLn(n) (0x20 + (n) * 0x40)
/* v1 only */
#define HW_TIMROT_TIMCOUNTn(n) (0x30 + (n) * 0x40)
/* v2 only */
#define HW_TIMROT_RUNNING_COUNTn(n) (0x30 + (n) * 0x40)
#define HW_TIMROT_FIXED_COUNTn(n) (0x40 + (n) * 0x40)
#define BM_TIMROT_TIMCTRLn_RELOAD (1 << 6)
#define BM_TIMROT_TIMCTRLn_UPDATE (1 << 7)
#define BM_TIMROT_TIMCTRLn_IRQ_EN (1 << 14)
#define BM_TIMROT_TIMCTRLn_IRQ (1 << 15)
#define BP_TIMROT_TIMCTRLn_SELECT 0
#define BV_TIMROTv1_TIMCTRLn_SELECT__32KHZ_XTAL 0x8
#define BV_TIMROTv2_TIMCTRLn_SELECT__32KHZ_XTAL 0xb
#define BV_TIMROTv2_TIMCTRLn_SELECT__TICK_ALWAYS 0xf
static struct clock_event_device mxs_clockevent_device;
static void __iomem *mxs_timrot_base;
static u32 timrot_major_version;
static inline void timrot_irq_disable(void)
{
__raw_writel(BM_TIMROT_TIMCTRLn_IRQ_EN, mxs_timrot_base +
HW_TIMROT_TIMCTRLn(0) + STMP_OFFSET_REG_CLR);
}
static inline void timrot_irq_enable(void)
{
__raw_writel(BM_TIMROT_TIMCTRLn_IRQ_EN, mxs_timrot_base +
HW_TIMROT_TIMCTRLn(0) + STMP_OFFSET_REG_SET);
}
static void timrot_irq_acknowledge(void)
{
__raw_writel(BM_TIMROT_TIMCTRLn_IRQ, mxs_timrot_base +
HW_TIMROT_TIMCTRLn(0) + STMP_OFFSET_REG_CLR);
}
static u64 timrotv1_get_cycles(struct clocksource *cs)
{
return ~((__raw_readl(mxs_timrot_base + HW_TIMROT_TIMCOUNTn(1))
& 0xffff0000) >> 16);
}
static int timrotv1_set_next_event(unsigned long evt,
struct clock_event_device *dev)
{
/* timrot decrements the count */
__raw_writel(evt, mxs_timrot_base + HW_TIMROT_TIMCOUNTn(0));
return 0;
}
static int timrotv2_set_next_event(unsigned long evt,
struct clock_event_device *dev)
{
/* timrot decrements the count */
__raw_writel(evt, mxs_timrot_base + HW_TIMROT_FIXED_COUNTn(0));
return 0;
}
static irqreturn_t mxs_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
timrot_irq_acknowledge();
evt->event_handler(evt);
return IRQ_HANDLED;
}
static struct irqaction mxs_timer_irq = {
.name = "MXS Timer Tick",
.dev_id = &mxs_clockevent_device,
.flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = mxs_timer_interrupt,
};
static void mxs_irq_clear(char *state)
{
/* Disable interrupt in timer module */
timrot_irq_disable();
/* Set event time into the furthest future */
if (timrot_is_v1())
__raw_writel(0xffff, mxs_timrot_base + HW_TIMROT_TIMCOUNTn(1));
else
__raw_writel(0xffffffff,
mxs_timrot_base + HW_TIMROT_FIXED_COUNTn(1));
/* Clear pending interrupt */
timrot_irq_acknowledge();
pr_debug("%s: changing mode to %s\n", __func__, state);
}
static int mxs_shutdown(struct clock_event_device *evt)
{
mxs_irq_clear("shutdown");
return 0;
}
static int mxs_set_oneshot(struct clock_event_device *evt)
{
if (clockevent_state_oneshot(evt))
mxs_irq_clear("oneshot");
timrot_irq_enable();
return 0;
}
static struct clock_event_device mxs_clockevent_device = {
.name = "mxs_timrot",
.features = CLOCK_EVT_FEAT_ONESHOT,
.set_state_shutdown = mxs_shutdown,
.set_state_oneshot = mxs_set_oneshot,
.tick_resume = mxs_shutdown,
.set_next_event = timrotv2_set_next_event,
.rating = 200,
};
static int __init mxs_clockevent_init(struct clk *timer_clk)
{
if (timrot_is_v1())
mxs_clockevent_device.set_next_event = timrotv1_set_next_event;
mxs_clockevent_device.cpumask = cpumask_of(0);
clockevents_config_and_register(&mxs_clockevent_device,
clk_get_rate(timer_clk),
timrot_is_v1() ? 0xf : 0x2,
timrot_is_v1() ? 0xfffe : 0xfffffffe);
return 0;
}
static struct clocksource clocksource_mxs = {
.name = "mxs_timer",
.rating = 200,
.read = timrotv1_get_cycles,
.mask = CLOCKSOURCE_MASK(16),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static u64 notrace mxs_read_sched_clock_v2(void)
{
return ~readl_relaxed(mxs_timrot_base + HW_TIMROT_RUNNING_COUNTn(1));
}
static int __init mxs_clocksource_init(struct clk *timer_clk)
{
unsigned int c = clk_get_rate(timer_clk);
if (timrot_is_v1())
clocksource_register_hz(&clocksource_mxs, c);
else {
clocksource_mmio_init(mxs_timrot_base + HW_TIMROT_RUNNING_COUNTn(1),
"mxs_timer", c, 200, 32, clocksource_mmio_readl_down);
sched_clock_register(mxs_read_sched_clock_v2, 32, c);
}
return 0;
}
static int __init mxs_timer_init(struct device_node *np)
{
struct clk *timer_clk;
int irq, ret;
mxs_timrot_base = of_iomap(np, 0);
WARN_ON(!mxs_timrot_base);
timer_clk = of_clk_get(np, 0);
if (IS_ERR(timer_clk)) {
pr_err("%s: failed to get clk\n", __func__);
return PTR_ERR(timer_clk);
}
ret = clk_prepare_enable(timer_clk);
if (ret)
return ret;
/*
* Initialize timers to a known state
*/
stmp_reset_block(mxs_timrot_base + HW_TIMROT_ROTCTRL);
/* get timrot version */
timrot_major_version = __raw_readl(mxs_timrot_base +
(of_device_is_compatible(np, "fsl,imx23-timrot") ?
MX23_TIMROT_VERSION_OFFSET :
MX28_TIMROT_VERSION_OFFSET));
timrot_major_version >>= BP_TIMROT_MAJOR_VERSION;
/* one for clock_event */
__raw_writel((timrot_is_v1() ?
BV_TIMROTv1_TIMCTRLn_SELECT__32KHZ_XTAL :
BV_TIMROTv2_TIMCTRLn_SELECT__TICK_ALWAYS) |
BM_TIMROT_TIMCTRLn_UPDATE |
BM_TIMROT_TIMCTRLn_IRQ_EN,
mxs_timrot_base + HW_TIMROT_TIMCTRLn(0));
/* another for clocksource */
__raw_writel((timrot_is_v1() ?
BV_TIMROTv1_TIMCTRLn_SELECT__32KHZ_XTAL :
BV_TIMROTv2_TIMCTRLn_SELECT__TICK_ALWAYS) |
BM_TIMROT_TIMCTRLn_RELOAD,
mxs_timrot_base + HW_TIMROT_TIMCTRLn(1));
/* set clocksource timer fixed count to the maximum */
if (timrot_is_v1())
__raw_writel(0xffff,
mxs_timrot_base + HW_TIMROT_TIMCOUNTn(1));
else
__raw_writel(0xffffffff,
mxs_timrot_base + HW_TIMROT_FIXED_COUNTn(1));
/* init and register the timer to the framework */
ret = mxs_clocksource_init(timer_clk);
if (ret)
return ret;
ret = mxs_clockevent_init(timer_clk);
if (ret)
return ret;
/* Make irqs happen */
irq = irq_of_parse_and_map(np, 0);
if (irq <= 0)
return -EINVAL;
return setup_irq(irq, &mxs_timer_irq);
}
TIMER_OF_DECLARE(mxs, "fsl,timrot", mxs_timer_init);
+288
View File
@@ -0,0 +1,288 @@
/*
* Copyright (C) 2008 STMicroelectronics
* Copyright (C) 2010 Alessandro Rubini
* Copyright (C) 2010 Linus Walleij for ST-Ericsson
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/clk.h>
#include <linux/jiffies.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/sched_clock.h>
#include <asm/mach/time.h>
/*
* The MTU device hosts four different counters, with 4 set of
* registers. These are register names.
*/
#define MTU_IMSC 0x00 /* Interrupt mask set/clear */
#define MTU_RIS 0x04 /* Raw interrupt status */
#define MTU_MIS 0x08 /* Masked interrupt status */
#define MTU_ICR 0x0C /* Interrupt clear register */
/* per-timer registers take 0..3 as argument */
#define MTU_LR(x) (0x10 + 0x10 * (x) + 0x00) /* Load value */
#define MTU_VAL(x) (0x10 + 0x10 * (x) + 0x04) /* Current value */
#define MTU_CR(x) (0x10 + 0x10 * (x) + 0x08) /* Control reg */
#define MTU_BGLR(x) (0x10 + 0x10 * (x) + 0x0c) /* At next overflow */
/* bits for the control register */
#define MTU_CRn_ENA 0x80
#define MTU_CRn_PERIODIC 0x40 /* if 0 = free-running */
#define MTU_CRn_PRESCALE_MASK 0x0c
#define MTU_CRn_PRESCALE_1 0x00
#define MTU_CRn_PRESCALE_16 0x04
#define MTU_CRn_PRESCALE_256 0x08
#define MTU_CRn_32BITS 0x02
#define MTU_CRn_ONESHOT 0x01 /* if 0 = wraps reloading from BGLR*/
/* Other registers are usual amba/primecell registers, currently not used */
#define MTU_ITCR 0xff0
#define MTU_ITOP 0xff4
#define MTU_PERIPH_ID0 0xfe0
#define MTU_PERIPH_ID1 0xfe4
#define MTU_PERIPH_ID2 0xfe8
#define MTU_PERIPH_ID3 0xfeC
#define MTU_PCELL0 0xff0
#define MTU_PCELL1 0xff4
#define MTU_PCELL2 0xff8
#define MTU_PCELL3 0xffC
static void __iomem *mtu_base;
static bool clkevt_periodic;
static u32 clk_prescale;
static u32 nmdk_cycle; /* write-once */
static struct delay_timer mtu_delay_timer;
#ifdef CONFIG_CLKSRC_NOMADIK_MTU_SCHED_CLOCK
/*
* Override the global weak sched_clock symbol with this
* local implementation which uses the clocksource to get some
* better resolution when scheduling the kernel.
*/
static u64 notrace nomadik_read_sched_clock(void)
{
if (unlikely(!mtu_base))
return 0;
return -readl(mtu_base + MTU_VAL(0));
}
#endif
static unsigned long nmdk_timer_read_current_timer(void)
{
return ~readl_relaxed(mtu_base + MTU_VAL(0));
}
/* Clockevent device: use one-shot mode */
static int nmdk_clkevt_next(unsigned long evt, struct clock_event_device *ev)
{
writel(1 << 1, mtu_base + MTU_IMSC);
writel(evt, mtu_base + MTU_LR(1));
/* Load highest value, enable device, enable interrupts */
writel(MTU_CRn_ONESHOT | clk_prescale |
MTU_CRn_32BITS | MTU_CRn_ENA,
mtu_base + MTU_CR(1));
return 0;
}
static void nmdk_clkevt_reset(void)
{
if (clkevt_periodic) {
/* Timer: configure load and background-load, and fire it up */
writel(nmdk_cycle, mtu_base + MTU_LR(1));
writel(nmdk_cycle, mtu_base + MTU_BGLR(1));
writel(MTU_CRn_PERIODIC | clk_prescale |
MTU_CRn_32BITS | MTU_CRn_ENA,
mtu_base + MTU_CR(1));
writel(1 << 1, mtu_base + MTU_IMSC);
} else {
/* Generate an interrupt to start the clockevent again */
(void) nmdk_clkevt_next(nmdk_cycle, NULL);
}
}
static int nmdk_clkevt_shutdown(struct clock_event_device *evt)
{
writel(0, mtu_base + MTU_IMSC);
/* disable timer */
writel(0, mtu_base + MTU_CR(1));
/* load some high default value */
writel(0xffffffff, mtu_base + MTU_LR(1));
return 0;
}
static int nmdk_clkevt_set_oneshot(struct clock_event_device *evt)
{
clkevt_periodic = false;
return 0;
}
static int nmdk_clkevt_set_periodic(struct clock_event_device *evt)
{
clkevt_periodic = true;
nmdk_clkevt_reset();
return 0;
}
static void nmdk_clksrc_reset(void)
{
/* Disable */
writel(0, mtu_base + MTU_CR(0));
/* ClockSource: configure load and background-load, and fire it up */
writel(nmdk_cycle, mtu_base + MTU_LR(0));
writel(nmdk_cycle, mtu_base + MTU_BGLR(0));
writel(clk_prescale | MTU_CRn_32BITS | MTU_CRn_ENA,
mtu_base + MTU_CR(0));
}
static void nmdk_clkevt_resume(struct clock_event_device *cedev)
{
nmdk_clkevt_reset();
nmdk_clksrc_reset();
}
static struct clock_event_device nmdk_clkevt = {
.name = "mtu_1",
.features = CLOCK_EVT_FEAT_ONESHOT |
CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_DYNIRQ,
.rating = 200,
.set_state_shutdown = nmdk_clkevt_shutdown,
.set_state_periodic = nmdk_clkevt_set_periodic,
.set_state_oneshot = nmdk_clkevt_set_oneshot,
.set_next_event = nmdk_clkevt_next,
.resume = nmdk_clkevt_resume,
};
/*
* IRQ Handler for timer 1 of the MTU block.
*/
static irqreturn_t nmdk_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evdev = dev_id;
writel(1 << 1, mtu_base + MTU_ICR); /* Interrupt clear reg */
evdev->event_handler(evdev);
return IRQ_HANDLED;
}
static struct irqaction nmdk_timer_irq = {
.name = "Nomadik Timer Tick",
.flags = IRQF_TIMER,
.handler = nmdk_timer_interrupt,
.dev_id = &nmdk_clkevt,
};
static int __init nmdk_timer_init(void __iomem *base, int irq,
struct clk *pclk, struct clk *clk)
{
unsigned long rate;
int ret;
mtu_base = base;
BUG_ON(clk_prepare_enable(pclk));
BUG_ON(clk_prepare_enable(clk));
/*
* Tick rate is 2.4MHz for Nomadik and 2.4Mhz, 100MHz or 133 MHz
* for ux500.
* Use a divide-by-16 counter if the tick rate is more than 32MHz.
* At 32 MHz, the timer (with 32 bit counter) can be programmed
* to wake-up at a max 127s a head in time. Dividing a 2.4 MHz timer
* with 16 gives too low timer resolution.
*/
rate = clk_get_rate(clk);
if (rate > 32000000) {
rate /= 16;
clk_prescale = MTU_CRn_PRESCALE_16;
} else {
clk_prescale = MTU_CRn_PRESCALE_1;
}
/* Cycles for periodic mode */
nmdk_cycle = DIV_ROUND_CLOSEST(rate, HZ);
/* Timer 0 is the free running clocksource */
nmdk_clksrc_reset();
ret = clocksource_mmio_init(mtu_base + MTU_VAL(0), "mtu_0",
rate, 200, 32, clocksource_mmio_readl_down);
if (ret) {
pr_err("timer: failed to initialize clock source %s\n", "mtu_0");
return ret;
}
#ifdef CONFIG_CLKSRC_NOMADIK_MTU_SCHED_CLOCK
sched_clock_register(nomadik_read_sched_clock, 32, rate);
#endif
/* Timer 1 is used for events, register irq and clockevents */
setup_irq(irq, &nmdk_timer_irq);
nmdk_clkevt.cpumask = cpumask_of(0);
nmdk_clkevt.irq = irq;
clockevents_config_and_register(&nmdk_clkevt, rate, 2, 0xffffffffU);
mtu_delay_timer.read_current_timer = &nmdk_timer_read_current_timer;
mtu_delay_timer.freq = rate;
register_current_timer_delay(&mtu_delay_timer);
return 0;
}
static int __init nmdk_timer_of_init(struct device_node *node)
{
struct clk *pclk;
struct clk *clk;
void __iomem *base;
int irq;
base = of_iomap(node, 0);
if (!base) {
pr_err("Can't remap registers\n");
return -ENXIO;
}
pclk = of_clk_get_by_name(node, "apb_pclk");
if (IS_ERR(pclk)) {
pr_err("could not get apb_pclk\n");
return PTR_ERR(pclk);
}
clk = of_clk_get_by_name(node, "timclk");
if (IS_ERR(clk)) {
pr_err("could not get timclk\n");
return PTR_ERR(clk);
}
irq = irq_of_parse_and_map(node, 0);
if (irq <= 0) {
pr_err("Can't parse IRQ\n");
return -EINVAL;
}
return nmdk_timer_init(base, irq, pclk, clk);
}
TIMER_OF_DECLARE(nomadik_mtu, "st,nomadik-mtu",
nmdk_timer_of_init);
+97
View File
@@ -0,0 +1,97 @@
/*
*
* Copyright (C) 2015 Numascale AS. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/clockchips.h>
#include <asm/irq.h>
#include <asm/numachip/numachip.h>
#include <asm/numachip/numachip_csr.h>
static DEFINE_PER_CPU(struct clock_event_device, numachip2_ced);
static cycles_t numachip2_timer_read(struct clocksource *cs)
{
return numachip2_read64_lcsr(NUMACHIP2_TIMER_NOW);
}
static struct clocksource numachip2_clocksource = {
.name = "numachip2",
.rating = 295,
.read = numachip2_timer_read,
.mask = CLOCKSOURCE_MASK(64),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
.mult = 1,
.shift = 0,
};
static int numachip2_set_next_event(unsigned long delta, struct clock_event_device *ced)
{
numachip2_write64_lcsr(NUMACHIP2_TIMER_DEADLINE + numachip2_timer(),
delta);
return 0;
}
static const struct clock_event_device numachip2_clockevent __initconst = {
.name = "numachip2",
.rating = 400,
.set_next_event = numachip2_set_next_event,
.features = CLOCK_EVT_FEAT_ONESHOT,
.mult = 1,
.shift = 0,
.min_delta_ns = 1250,
.min_delta_ticks = 1250,
.max_delta_ns = LONG_MAX,
.max_delta_ticks = LONG_MAX,
};
static void numachip_timer_interrupt(void)
{
struct clock_event_device *ced = this_cpu_ptr(&numachip2_ced);
ced->event_handler(ced);
}
static __init void numachip_timer_each(struct work_struct *work)
{
unsigned local_apicid = __this_cpu_read(x86_cpu_to_apicid) & 0xff;
struct clock_event_device *ced = this_cpu_ptr(&numachip2_ced);
/* Setup IPI vector to local core and relative timing mode */
numachip2_write64_lcsr(NUMACHIP2_TIMER_INT + numachip2_timer(),
(3 << 22) | (X86_PLATFORM_IPI_VECTOR << 14) |
(local_apicid << 6));
*ced = numachip2_clockevent;
ced->cpumask = cpumask_of(smp_processor_id());
clockevents_register_device(ced);
}
static int __init numachip_timer_init(void)
{
if (numachip_system != 2)
return -ENODEV;
/* Reset timer */
numachip2_write64_lcsr(NUMACHIP2_TIMER_RESET, 0);
clocksource_register_hz(&numachip2_clocksource, NSEC_PER_SEC);
/* Setup per-cpu clockevents */
x86_platform_ipi_callback = numachip_timer_interrupt;
schedule_on_each_cpu(&numachip_timer_each);
return 0;
}
arch_initcall(numachip_timer_init);
+173
View File
@@ -0,0 +1,173 @@
/*
* Actions Semi Owl timer
*
* Copyright 2012 Actions Semi Inc.
* Author: Actions Semi, Inc.
*
* Copyright (c) 2017 SUSE Linux GmbH
* Author: Andreas Färber
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqreturn.h>
#include <linux/sched_clock.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#define OWL_Tx_CTL 0x0
#define OWL_Tx_CMP 0x4
#define OWL_Tx_VAL 0x8
#define OWL_Tx_CTL_PD BIT(0)
#define OWL_Tx_CTL_INTEN BIT(1)
#define OWL_Tx_CTL_EN BIT(2)
static void __iomem *owl_timer_base;
static void __iomem *owl_clksrc_base;
static void __iomem *owl_clkevt_base;
static inline void owl_timer_reset(void __iomem *base)
{
writel(0, base + OWL_Tx_CTL);
writel(0, base + OWL_Tx_VAL);
writel(0, base + OWL_Tx_CMP);
}
static inline void owl_timer_set_enabled(void __iomem *base, bool enabled)
{
u32 ctl = readl(base + OWL_Tx_CTL);
/* PD bit is cleared when set */
ctl &= ~OWL_Tx_CTL_PD;
if (enabled)
ctl |= OWL_Tx_CTL_EN;
else
ctl &= ~OWL_Tx_CTL_EN;
writel(ctl, base + OWL_Tx_CTL);
}
static u64 notrace owl_timer_sched_read(void)
{
return (u64)readl(owl_clksrc_base + OWL_Tx_VAL);
}
static int owl_timer_set_state_shutdown(struct clock_event_device *evt)
{
owl_timer_set_enabled(owl_clkevt_base, false);
return 0;
}
static int owl_timer_set_state_oneshot(struct clock_event_device *evt)
{
owl_timer_reset(owl_clkevt_base);
return 0;
}
static int owl_timer_tick_resume(struct clock_event_device *evt)
{
return 0;
}
static int owl_timer_set_next_event(unsigned long evt,
struct clock_event_device *ev)
{
void __iomem *base = owl_clkevt_base;
owl_timer_set_enabled(base, false);
writel(OWL_Tx_CTL_INTEN, base + OWL_Tx_CTL);
writel(0, base + OWL_Tx_VAL);
writel(evt, base + OWL_Tx_CMP);
owl_timer_set_enabled(base, true);
return 0;
}
static struct clock_event_device owl_clockevent = {
.name = "owl_tick",
.rating = 200,
.features = CLOCK_EVT_FEAT_ONESHOT |
CLOCK_EVT_FEAT_DYNIRQ,
.set_state_shutdown = owl_timer_set_state_shutdown,
.set_state_oneshot = owl_timer_set_state_oneshot,
.tick_resume = owl_timer_tick_resume,
.set_next_event = owl_timer_set_next_event,
};
static irqreturn_t owl_timer1_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = (struct clock_event_device *)dev_id;
writel(OWL_Tx_CTL_PD, owl_clkevt_base + OWL_Tx_CTL);
evt->event_handler(evt);
return IRQ_HANDLED;
}
static int __init owl_timer_init(struct device_node *node)
{
struct clk *clk;
unsigned long rate;
int timer1_irq, ret;
owl_timer_base = of_io_request_and_map(node, 0, "owl-timer");
if (IS_ERR(owl_timer_base)) {
pr_err("Can't map timer registers\n");
return PTR_ERR(owl_timer_base);
}
owl_clksrc_base = owl_timer_base + 0x08;
owl_clkevt_base = owl_timer_base + 0x14;
timer1_irq = of_irq_get_byname(node, "timer1");
if (timer1_irq <= 0) {
pr_err("Can't parse timer1 IRQ\n");
return -EINVAL;
}
clk = of_clk_get(node, 0);
if (IS_ERR(clk))
return PTR_ERR(clk);
rate = clk_get_rate(clk);
owl_timer_reset(owl_clksrc_base);
owl_timer_set_enabled(owl_clksrc_base, true);
sched_clock_register(owl_timer_sched_read, 32, rate);
clocksource_mmio_init(owl_clksrc_base + OWL_Tx_VAL, node->name,
rate, 200, 32, clocksource_mmio_readl_up);
owl_timer_reset(owl_clkevt_base);
ret = request_irq(timer1_irq, owl_timer1_interrupt, IRQF_TIMER,
"owl-timer", &owl_clockevent);
if (ret) {
pr_err("failed to request irq %d\n", timer1_irq);
return ret;
}
owl_clockevent.cpumask = cpumask_of(0);
owl_clockevent.irq = timer1_irq;
clockevents_config_and_register(&owl_clockevent, rate,
0xf, 0xffffffff);
return 0;
}
TIMER_OF_DECLARE(owl_s500, "actions,s500-timer", owl_timer_init);
TIMER_OF_DECLARE(owl_s700, "actions,s700-timer", owl_timer_init);
TIMER_OF_DECLARE(owl_s900, "actions,s900-timer", owl_timer_init);
+236
View File
@@ -0,0 +1,236 @@
/*
* arch/arm/mach-pxa/time.c
*
* PXA clocksource, clockevents, and OST interrupt handlers.
* Copyright (c) 2007 by Bill Gatliff <bgat@billgatliff.com>.
*
* Derived from Nicolas Pitre's PXA timer handler Copyright (c) 2001
* by MontaVista Software, Inc. (Nico, your code rocks!)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/sched/clock.h>
#include <linux/sched_clock.h>
#include <clocksource/pxa.h>
#include <asm/div64.h>
#define OSMR0 0x00 /* OS Timer 0 Match Register */
#define OSMR1 0x04 /* OS Timer 1 Match Register */
#define OSMR2 0x08 /* OS Timer 2 Match Register */
#define OSMR3 0x0C /* OS Timer 3 Match Register */
#define OSCR 0x10 /* OS Timer Counter Register */
#define OSSR 0x14 /* OS Timer Status Register */
#define OWER 0x18 /* OS Timer Watchdog Enable Register */
#define OIER 0x1C /* OS Timer Interrupt Enable Register */
#define OSSR_M3 (1 << 3) /* Match status channel 3 */
#define OSSR_M2 (1 << 2) /* Match status channel 2 */
#define OSSR_M1 (1 << 1) /* Match status channel 1 */
#define OSSR_M0 (1 << 0) /* Match status channel 0 */
#define OIER_E0 (1 << 0) /* Interrupt enable channel 0 */
/*
* This is PXA's sched_clock implementation. This has a resolution
* of at least 308 ns and a maximum value of 208 days.
*
* The return value is guaranteed to be monotonic in that range as
* long as there is always less than 582 seconds between successive
* calls to sched_clock() which should always be the case in practice.
*/
#define timer_readl(reg) readl_relaxed(timer_base + (reg))
#define timer_writel(val, reg) writel_relaxed((val), timer_base + (reg))
static void __iomem *timer_base;
static u64 notrace pxa_read_sched_clock(void)
{
return timer_readl(OSCR);
}
#define MIN_OSCR_DELTA 16
static irqreturn_t
pxa_ost0_interrupt(int irq, void *dev_id)
{
struct clock_event_device *c = dev_id;
/* Disarm the compare/match, signal the event. */
timer_writel(timer_readl(OIER) & ~OIER_E0, OIER);
timer_writel(OSSR_M0, OSSR);
c->event_handler(c);
return IRQ_HANDLED;
}
static int
pxa_osmr0_set_next_event(unsigned long delta, struct clock_event_device *dev)
{
unsigned long next, oscr;
timer_writel(timer_readl(OIER) | OIER_E0, OIER);
next = timer_readl(OSCR) + delta;
timer_writel(next, OSMR0);
oscr = timer_readl(OSCR);
return (signed)(next - oscr) <= MIN_OSCR_DELTA ? -ETIME : 0;
}
static int pxa_osmr0_shutdown(struct clock_event_device *evt)
{
/* initializing, released, or preparing for suspend */
timer_writel(timer_readl(OIER) & ~OIER_E0, OIER);
timer_writel(OSSR_M0, OSSR);
return 0;
}
#ifdef CONFIG_PM
static unsigned long osmr[4], oier, oscr;
static void pxa_timer_suspend(struct clock_event_device *cedev)
{
osmr[0] = timer_readl(OSMR0);
osmr[1] = timer_readl(OSMR1);
osmr[2] = timer_readl(OSMR2);
osmr[3] = timer_readl(OSMR3);
oier = timer_readl(OIER);
oscr = timer_readl(OSCR);
}
static void pxa_timer_resume(struct clock_event_device *cedev)
{
/*
* Ensure that we have at least MIN_OSCR_DELTA between match
* register 0 and the OSCR, to guarantee that we will receive
* the one-shot timer interrupt. We adjust OSMR0 in preference
* to OSCR to guarantee that OSCR is monotonically incrementing.
*/
if (osmr[0] - oscr < MIN_OSCR_DELTA)
osmr[0] += MIN_OSCR_DELTA;
timer_writel(osmr[0], OSMR0);
timer_writel(osmr[1], OSMR1);
timer_writel(osmr[2], OSMR2);
timer_writel(osmr[3], OSMR3);
timer_writel(oier, OIER);
timer_writel(oscr, OSCR);
}
#else
#define pxa_timer_suspend NULL
#define pxa_timer_resume NULL
#endif
static struct clock_event_device ckevt_pxa_osmr0 = {
.name = "osmr0",
.features = CLOCK_EVT_FEAT_ONESHOT,
.rating = 200,
.set_next_event = pxa_osmr0_set_next_event,
.set_state_shutdown = pxa_osmr0_shutdown,
.set_state_oneshot = pxa_osmr0_shutdown,
.suspend = pxa_timer_suspend,
.resume = pxa_timer_resume,
};
static struct irqaction pxa_ost0_irq = {
.name = "ost0",
.flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = pxa_ost0_interrupt,
.dev_id = &ckevt_pxa_osmr0,
};
static int __init pxa_timer_common_init(int irq, unsigned long clock_tick_rate)
{
int ret;
timer_writel(0, OIER);
timer_writel(OSSR_M0 | OSSR_M1 | OSSR_M2 | OSSR_M3, OSSR);
sched_clock_register(pxa_read_sched_clock, 32, clock_tick_rate);
ckevt_pxa_osmr0.cpumask = cpumask_of(0);
ret = setup_irq(irq, &pxa_ost0_irq);
if (ret) {
pr_err("Failed to setup irq\n");
return ret;
}
ret = clocksource_mmio_init(timer_base + OSCR, "oscr0", clock_tick_rate, 200,
32, clocksource_mmio_readl_up);
if (ret) {
pr_err("Failed to init clocksource\n");
return ret;
}
clockevents_config_and_register(&ckevt_pxa_osmr0, clock_tick_rate,
MIN_OSCR_DELTA * 2, 0x7fffffff);
return 0;
}
static int __init pxa_timer_dt_init(struct device_node *np)
{
struct clk *clk;
int irq, ret;
/* timer registers are shared with watchdog timer */
timer_base = of_iomap(np, 0);
if (!timer_base) {
pr_err("%s: unable to map resource\n", np->name);
return -ENXIO;
}
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
pr_crit("%s: unable to get clk\n", np->name);
return PTR_ERR(clk);
}
ret = clk_prepare_enable(clk);
if (ret) {
pr_crit("Failed to prepare clock\n");
return ret;
}
/* we are only interested in OS-timer0 irq */
irq = irq_of_parse_and_map(np, 0);
if (irq <= 0) {
pr_crit("%s: unable to parse OS-timer0 irq\n", np->name);
return -EINVAL;
}
return pxa_timer_common_init(irq, clk_get_rate(clk));
}
TIMER_OF_DECLARE(pxa_timer, "marvell,pxa-timer", pxa_timer_dt_init);
/*
* Legacy timer init for non device-tree boards.
*/
void __init pxa_timer_nodt_init(int irq, void __iomem *base)
{
struct clk *clk;
timer_base = base;
clk = clk_get(NULL, "OSTIMER0");
if (clk && !IS_ERR(clk)) {
clk_prepare_enable(clk);
pxa_timer_common_init(irq, clk_get_rate(clk));
} else {
pr_crit("%s: unable to get clk\n", __func__);
}
}
+258
View File
@@ -0,0 +1,258 @@
/*
*
* Copyright (C) 2007 Google, Inc.
* Copyright (c) 2009-2012,2014, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/cpu.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/sched_clock.h>
#include <asm/delay.h>
#define TIMER_MATCH_VAL 0x0000
#define TIMER_COUNT_VAL 0x0004
#define TIMER_ENABLE 0x0008
#define TIMER_ENABLE_CLR_ON_MATCH_EN BIT(1)
#define TIMER_ENABLE_EN BIT(0)
#define TIMER_CLEAR 0x000C
#define DGT_CLK_CTL 0x10
#define DGT_CLK_CTL_DIV_4 0x3
#define TIMER_STS_GPT0_CLR_PEND BIT(10)
#define GPT_HZ 32768
static void __iomem *event_base;
static void __iomem *sts_base;
static irqreturn_t msm_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
/* Stop the timer tick */
if (clockevent_state_oneshot(evt)) {
u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE);
ctrl &= ~TIMER_ENABLE_EN;
writel_relaxed(ctrl, event_base + TIMER_ENABLE);
}
evt->event_handler(evt);
return IRQ_HANDLED;
}
static int msm_timer_set_next_event(unsigned long cycles,
struct clock_event_device *evt)
{
u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE);
ctrl &= ~TIMER_ENABLE_EN;
writel_relaxed(ctrl, event_base + TIMER_ENABLE);
writel_relaxed(ctrl, event_base + TIMER_CLEAR);
writel_relaxed(cycles, event_base + TIMER_MATCH_VAL);
if (sts_base)
while (readl_relaxed(sts_base) & TIMER_STS_GPT0_CLR_PEND)
cpu_relax();
writel_relaxed(ctrl | TIMER_ENABLE_EN, event_base + TIMER_ENABLE);
return 0;
}
static int msm_timer_shutdown(struct clock_event_device *evt)
{
u32 ctrl;
ctrl = readl_relaxed(event_base + TIMER_ENABLE);
ctrl &= ~(TIMER_ENABLE_EN | TIMER_ENABLE_CLR_ON_MATCH_EN);
writel_relaxed(ctrl, event_base + TIMER_ENABLE);
return 0;
}
static struct clock_event_device __percpu *msm_evt;
static void __iomem *source_base;
static notrace u64 msm_read_timer_count(struct clocksource *cs)
{
return readl_relaxed(source_base + TIMER_COUNT_VAL);
}
static struct clocksource msm_clocksource = {
.name = "dg_timer",
.rating = 300,
.read = msm_read_timer_count,
.mask = CLOCKSOURCE_MASK(32),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static int msm_timer_irq;
static int msm_timer_has_ppi;
static int msm_local_timer_starting_cpu(unsigned int cpu)
{
struct clock_event_device *evt = per_cpu_ptr(msm_evt, cpu);
int err;
evt->irq = msm_timer_irq;
evt->name = "msm_timer";
evt->features = CLOCK_EVT_FEAT_ONESHOT;
evt->rating = 200;
evt->set_state_shutdown = msm_timer_shutdown;
evt->set_state_oneshot = msm_timer_shutdown;
evt->tick_resume = msm_timer_shutdown;
evt->set_next_event = msm_timer_set_next_event;
evt->cpumask = cpumask_of(cpu);
clockevents_config_and_register(evt, GPT_HZ, 4, 0xffffffff);
if (msm_timer_has_ppi) {
enable_percpu_irq(evt->irq, IRQ_TYPE_EDGE_RISING);
} else {
err = request_irq(evt->irq, msm_timer_interrupt,
IRQF_TIMER | IRQF_NOBALANCING |
IRQF_TRIGGER_RISING, "gp_timer", evt);
if (err)
pr_err("request_irq failed\n");
}
return 0;
}
static int msm_local_timer_dying_cpu(unsigned int cpu)
{
struct clock_event_device *evt = per_cpu_ptr(msm_evt, cpu);
evt->set_state_shutdown(evt);
disable_percpu_irq(evt->irq);
return 0;
}
static u64 notrace msm_sched_clock_read(void)
{
return msm_clocksource.read(&msm_clocksource);
}
static unsigned long msm_read_current_timer(void)
{
return msm_clocksource.read(&msm_clocksource);
}
static struct delay_timer msm_delay_timer = {
.read_current_timer = msm_read_current_timer,
};
static int __init msm_timer_init(u32 dgt_hz, int sched_bits, int irq,
bool percpu)
{
struct clocksource *cs = &msm_clocksource;
int res = 0;
msm_timer_irq = irq;
msm_timer_has_ppi = percpu;
msm_evt = alloc_percpu(struct clock_event_device);
if (!msm_evt) {
pr_err("memory allocation failed for clockevents\n");
goto err;
}
if (percpu)
res = request_percpu_irq(irq, msm_timer_interrupt,
"gp_timer", msm_evt);
if (res) {
pr_err("request_percpu_irq failed\n");
} else {
/* Install and invoke hotplug callbacks */
res = cpuhp_setup_state(CPUHP_AP_QCOM_TIMER_STARTING,
"clockevents/qcom/timer:starting",
msm_local_timer_starting_cpu,
msm_local_timer_dying_cpu);
if (res) {
free_percpu_irq(irq, msm_evt);
goto err;
}
}
err:
writel_relaxed(TIMER_ENABLE_EN, source_base + TIMER_ENABLE);
res = clocksource_register_hz(cs, dgt_hz);
if (res)
pr_err("clocksource_register failed\n");
sched_clock_register(msm_sched_clock_read, sched_bits, dgt_hz);
msm_delay_timer.freq = dgt_hz;
register_current_timer_delay(&msm_delay_timer);
return res;
}
static int __init msm_dt_timer_init(struct device_node *np)
{
u32 freq;
int irq, ret;
struct resource res;
u32 percpu_offset;
void __iomem *base;
void __iomem *cpu0_base;
base = of_iomap(np, 0);
if (!base) {
pr_err("Failed to map event base\n");
return -ENXIO;
}
/* We use GPT0 for the clockevent */
irq = irq_of_parse_and_map(np, 1);
if (irq <= 0) {
pr_err("Can't get irq\n");
return -EINVAL;
}
/* We use CPU0's DGT for the clocksource */
if (of_property_read_u32(np, "cpu-offset", &percpu_offset))
percpu_offset = 0;
ret = of_address_to_resource(np, 0, &res);
if (ret) {
pr_err("Failed to parse DGT resource\n");
return ret;
}
cpu0_base = ioremap(res.start + percpu_offset, resource_size(&res));
if (!cpu0_base) {
pr_err("Failed to map source base\n");
return -EINVAL;
}
if (of_property_read_u32(np, "clock-frequency", &freq)) {
pr_err("Unknown frequency\n");
return -EINVAL;
}
event_base = base + 0x4;
sts_base = base + 0x88;
source_base = cpu0_base + 0x24;
freq /= 4;
writel_relaxed(DGT_CLK_CTL_DIV_4, source_base + DGT_CLK_CTL);
return msm_timer_init(freq, 32, irq, !!percpu_offset);
}
TIMER_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init);
TIMER_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init);
+265
View File
@@ -0,0 +1,265 @@
/*
* Renesas Timer Support - OSTM
*
* Copyright (C) 2017 Renesas Electronics America, Inc.
* Copyright (C) 2017 Chris Brandt
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/sched_clock.h>
#include <linux/slab.h>
/*
* The OSTM contains independent channels.
* The first OSTM channel probed will be set up as a free running
* clocksource. Additionally we will use this clocksource for the system
* schedule timer sched_clock().
*
* The second (or more) channel probed will be set up as an interrupt
* driven clock event.
*/
struct ostm_device {
void __iomem *base;
unsigned long ticks_per_jiffy;
struct clock_event_device ced;
};
static void __iomem *system_clock; /* For sched_clock() */
/* OSTM REGISTERS */
#define OSTM_CMP 0x000 /* RW,32 */
#define OSTM_CNT 0x004 /* R,32 */
#define OSTM_TE 0x010 /* R,8 */
#define OSTM_TS 0x014 /* W,8 */
#define OSTM_TT 0x018 /* W,8 */
#define OSTM_CTL 0x020 /* RW,8 */
#define TE 0x01
#define TS 0x01
#define TT 0x01
#define CTL_PERIODIC 0x00
#define CTL_ONESHOT 0x02
#define CTL_FREERUN 0x02
static struct ostm_device *ced_to_ostm(struct clock_event_device *ced)
{
return container_of(ced, struct ostm_device, ced);
}
static void ostm_timer_stop(struct ostm_device *ostm)
{
if (readb(ostm->base + OSTM_TE) & TE) {
writeb(TT, ostm->base + OSTM_TT);
/*
* Read back the register simply to confirm the write operation
* has completed since I/O writes can sometimes get queued by
* the bus architecture.
*/
while (readb(ostm->base + OSTM_TE) & TE)
;
}
}
static int __init ostm_init_clksrc(struct ostm_device *ostm, unsigned long rate)
{
/*
* irq not used (clock sources don't use interrupts)
*/
ostm_timer_stop(ostm);
writel(0, ostm->base + OSTM_CMP);
writeb(CTL_FREERUN, ostm->base + OSTM_CTL);
writeb(TS, ostm->base + OSTM_TS);
return clocksource_mmio_init(ostm->base + OSTM_CNT,
"ostm", rate,
300, 32, clocksource_mmio_readl_up);
}
static u64 notrace ostm_read_sched_clock(void)
{
return readl(system_clock);
}
static void __init ostm_init_sched_clock(struct ostm_device *ostm,
unsigned long rate)
{
system_clock = ostm->base + OSTM_CNT;
sched_clock_register(ostm_read_sched_clock, 32, rate);
}
static int ostm_clock_event_next(unsigned long delta,
struct clock_event_device *ced)
{
struct ostm_device *ostm = ced_to_ostm(ced);
ostm_timer_stop(ostm);
writel(delta, ostm->base + OSTM_CMP);
writeb(CTL_ONESHOT, ostm->base + OSTM_CTL);
writeb(TS, ostm->base + OSTM_TS);
return 0;
}
static int ostm_shutdown(struct clock_event_device *ced)
{
struct ostm_device *ostm = ced_to_ostm(ced);
ostm_timer_stop(ostm);
return 0;
}
static int ostm_set_periodic(struct clock_event_device *ced)
{
struct ostm_device *ostm = ced_to_ostm(ced);
if (clockevent_state_oneshot(ced) || clockevent_state_periodic(ced))
ostm_timer_stop(ostm);
writel(ostm->ticks_per_jiffy - 1, ostm->base + OSTM_CMP);
writeb(CTL_PERIODIC, ostm->base + OSTM_CTL);
writeb(TS, ostm->base + OSTM_TS);
return 0;
}
static int ostm_set_oneshot(struct clock_event_device *ced)
{
struct ostm_device *ostm = ced_to_ostm(ced);
ostm_timer_stop(ostm);
return 0;
}
static irqreturn_t ostm_timer_interrupt(int irq, void *dev_id)
{
struct ostm_device *ostm = dev_id;
if (clockevent_state_oneshot(&ostm->ced))
ostm_timer_stop(ostm);
/* notify clockevent layer */
if (ostm->ced.event_handler)
ostm->ced.event_handler(&ostm->ced);
return IRQ_HANDLED;
}
static int __init ostm_init_clkevt(struct ostm_device *ostm, int irq,
unsigned long rate)
{
struct clock_event_device *ced = &ostm->ced;
int ret = -ENXIO;
ret = request_irq(irq, ostm_timer_interrupt,
IRQF_TIMER | IRQF_IRQPOLL,
"ostm", ostm);
if (ret) {
pr_err("ostm: failed to request irq\n");
return ret;
}
ced->name = "ostm";
ced->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC;
ced->set_state_shutdown = ostm_shutdown;
ced->set_state_periodic = ostm_set_periodic;
ced->set_state_oneshot = ostm_set_oneshot;
ced->set_next_event = ostm_clock_event_next;
ced->shift = 32;
ced->rating = 300;
ced->cpumask = cpumask_of(0);
clockevents_config_and_register(ced, rate, 0xf, 0xffffffff);
return 0;
}
static int __init ostm_init(struct device_node *np)
{
struct ostm_device *ostm;
int ret = -EFAULT;
struct clk *ostm_clk = NULL;
int irq;
unsigned long rate;
ostm = kzalloc(sizeof(*ostm), GFP_KERNEL);
if (!ostm)
return -ENOMEM;
ostm->base = of_iomap(np, 0);
if (!ostm->base) {
pr_err("ostm: failed to remap I/O memory\n");
goto err;
}
irq = irq_of_parse_and_map(np, 0);
if (irq < 0) {
pr_err("ostm: Failed to get irq\n");
goto err;
}
ostm_clk = of_clk_get(np, 0);
if (IS_ERR(ostm_clk)) {
pr_err("ostm: Failed to get clock\n");
ostm_clk = NULL;
goto err;
}
ret = clk_prepare_enable(ostm_clk);
if (ret) {
pr_err("ostm: Failed to enable clock\n");
goto err;
}
rate = clk_get_rate(ostm_clk);
ostm->ticks_per_jiffy = (rate + HZ / 2) / HZ;
/*
* First probed device will be used as system clocksource. Any
* additional devices will be used as clock events.
*/
if (!system_clock) {
ret = ostm_init_clksrc(ostm, rate);
if (!ret) {
ostm_init_sched_clock(ostm, rate);
pr_info("ostm: used for clocksource\n");
}
} else {
ret = ostm_init_clkevt(ostm, irq, rate);
if (!ret)
pr_info("ostm: used for clock events\n");
}
err:
if (ret) {
clk_disable_unprepare(ostm_clk);
iounmap(ostm->base);
kfree(ostm);
return ret;
}
return 0;
}
TIMER_OF_DECLARE(ostm, "renesas,ostm", ostm_init);
+105
View File
@@ -0,0 +1,105 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2012 Regents of the University of California
* Copyright (C) 2017 SiFive
*/
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/cpu.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/sbi.h>
/*
* All RISC-V systems have a timer attached to every hart. These timers can be
* read by the 'rdcycle' pseudo instruction, and can use the SBI to setup
* events. In order to abstract the architecture-specific timer reading and
* setting functions away from the clock event insertion code, we provide
* function pointers to the clockevent subsystem that perform two basic
* operations: rdtime() reads the timer on the current CPU, and
* next_event(delta) sets the next timer event to 'delta' cycles in the future.
* As the timers are inherently a per-cpu resource, these callbacks perform
* operations on the current hart. There is guaranteed to be exactly one timer
* per hart on all RISC-V systems.
*/
static int riscv_clock_next_event(unsigned long delta,
struct clock_event_device *ce)
{
csr_set(sie, SIE_STIE);
sbi_set_timer(get_cycles64() + delta);
return 0;
}
static DEFINE_PER_CPU(struct clock_event_device, riscv_clock_event) = {
.name = "riscv_timer_clockevent",
.features = CLOCK_EVT_FEAT_ONESHOT,
.rating = 100,
.set_next_event = riscv_clock_next_event,
};
/*
* It is guaranteed that all the timers across all the harts are synchronized
* within one tick of each other, so while this could technically go
* backwards when hopping between CPUs, practically it won't happen.
*/
static unsigned long long riscv_clocksource_rdtime(struct clocksource *cs)
{
return get_cycles64();
}
static DEFINE_PER_CPU(struct clocksource, riscv_clocksource) = {
.name = "riscv_clocksource",
.rating = 300,
.mask = CLOCKSOURCE_MASK(BITS_PER_LONG),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
.read = riscv_clocksource_rdtime,
};
static int riscv_timer_starting_cpu(unsigned int cpu)
{
struct clock_event_device *ce = per_cpu_ptr(&riscv_clock_event, cpu);
ce->cpumask = cpumask_of(cpu);
clockevents_config_and_register(ce, riscv_timebase, 100, 0x7fffffff);
csr_set(sie, SIE_STIE);
return 0;
}
static int riscv_timer_dying_cpu(unsigned int cpu)
{
csr_clear(sie, SIE_STIE);
return 0;
}
/* called directly from the low-level interrupt handler */
void riscv_timer_interrupt(void)
{
struct clock_event_device *evdev = this_cpu_ptr(&riscv_clock_event);
csr_clear(sie, SIE_STIE);
evdev->event_handler(evdev);
}
static int __init riscv_timer_init_dt(struct device_node *n)
{
int cpu_id = riscv_of_processor_hart(n), error;
struct clocksource *cs;
if (cpu_id != smp_processor_id())
return 0;
cs = per_cpu_ptr(&riscv_clocksource, cpu_id);
clocksource_register_hz(cs, riscv_timebase);
error = cpuhp_setup_state(CPUHP_AP_RISCV_TIMER_STARTING,
"clockevents/riscv/timer:starting",
riscv_timer_starting_cpu, riscv_timer_dying_cpu);
if (error)
pr_err("RISCV timer register failed [%d] for cpu = [%d]\n",
error, cpu_id);
return error;
}
TIMER_OF_DECLARE(riscv_timer, "riscv", riscv_timer_init_dt);
+307
View File
@@ -0,0 +1,307 @@
/*
* Rockchip timer support
*
* Copyright (C) Daniel Lezcano <daniel.lezcano@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/sched_clock.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#define TIMER_NAME "rk_timer"
#define TIMER_LOAD_COUNT0 0x00
#define TIMER_LOAD_COUNT1 0x04
#define TIMER_CURRENT_VALUE0 0x08
#define TIMER_CURRENT_VALUE1 0x0C
#define TIMER_CONTROL_REG3288 0x10
#define TIMER_CONTROL_REG3399 0x1c
#define TIMER_INT_STATUS 0x18
#define TIMER_DISABLE 0x0
#define TIMER_ENABLE 0x1
#define TIMER_MODE_FREE_RUNNING (0 << 1)
#define TIMER_MODE_USER_DEFINED_COUNT (1 << 1)
#define TIMER_INT_UNMASK (1 << 2)
struct rk_timer {
void __iomem *base;
void __iomem *ctrl;
struct clk *clk;
struct clk *pclk;
u32 freq;
int irq;
};
struct rk_clkevt {
struct clock_event_device ce;
struct rk_timer timer;
};
static struct rk_clkevt *rk_clkevt;
static struct rk_timer *rk_clksrc;
static inline struct rk_timer *rk_timer(struct clock_event_device *ce)
{
return &container_of(ce, struct rk_clkevt, ce)->timer;
}
static inline void rk_timer_disable(struct rk_timer *timer)
{
writel_relaxed(TIMER_DISABLE, timer->ctrl);
}
static inline void rk_timer_enable(struct rk_timer *timer, u32 flags)
{
writel_relaxed(TIMER_ENABLE | flags, timer->ctrl);
}
static void rk_timer_update_counter(unsigned long cycles,
struct rk_timer *timer)
{
writel_relaxed(cycles, timer->base + TIMER_LOAD_COUNT0);
writel_relaxed(0, timer->base + TIMER_LOAD_COUNT1);
}
static void rk_timer_interrupt_clear(struct rk_timer *timer)
{
writel_relaxed(1, timer->base + TIMER_INT_STATUS);
}
static inline int rk_timer_set_next_event(unsigned long cycles,
struct clock_event_device *ce)
{
struct rk_timer *timer = rk_timer(ce);
rk_timer_disable(timer);
rk_timer_update_counter(cycles, timer);
rk_timer_enable(timer, TIMER_MODE_USER_DEFINED_COUNT |
TIMER_INT_UNMASK);
return 0;
}
static int rk_timer_shutdown(struct clock_event_device *ce)
{
struct rk_timer *timer = rk_timer(ce);
rk_timer_disable(timer);
return 0;
}
static int rk_timer_set_periodic(struct clock_event_device *ce)
{
struct rk_timer *timer = rk_timer(ce);
rk_timer_disable(timer);
rk_timer_update_counter(timer->freq / HZ - 1, timer);
rk_timer_enable(timer, TIMER_MODE_FREE_RUNNING | TIMER_INT_UNMASK);
return 0;
}
static irqreturn_t rk_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *ce = dev_id;
struct rk_timer *timer = rk_timer(ce);
rk_timer_interrupt_clear(timer);
if (clockevent_state_oneshot(ce))
rk_timer_disable(timer);
ce->event_handler(ce);
return IRQ_HANDLED;
}
static u64 notrace rk_timer_sched_read(void)
{
return ~readl_relaxed(rk_clksrc->base + TIMER_CURRENT_VALUE0);
}
static int __init
rk_timer_probe(struct rk_timer *timer, struct device_node *np)
{
struct clk *timer_clk;
struct clk *pclk;
int ret = -EINVAL, irq;
u32 ctrl_reg = TIMER_CONTROL_REG3288;
timer->base = of_iomap(np, 0);
if (!timer->base) {
pr_err("Failed to get base address for '%s'\n", TIMER_NAME);
return -ENXIO;
}
if (of_device_is_compatible(np, "rockchip,rk3399-timer"))
ctrl_reg = TIMER_CONTROL_REG3399;
timer->ctrl = timer->base + ctrl_reg;
pclk = of_clk_get_by_name(np, "pclk");
if (IS_ERR(pclk)) {
ret = PTR_ERR(pclk);
pr_err("Failed to get pclk for '%s'\n", TIMER_NAME);
goto out_unmap;
}
ret = clk_prepare_enable(pclk);
if (ret) {
pr_err("Failed to enable pclk for '%s'\n", TIMER_NAME);
goto out_unmap;
}
timer->pclk = pclk;
timer_clk = of_clk_get_by_name(np, "timer");
if (IS_ERR(timer_clk)) {
ret = PTR_ERR(timer_clk);
pr_err("Failed to get timer clock for '%s'\n", TIMER_NAME);
goto out_timer_clk;
}
ret = clk_prepare_enable(timer_clk);
if (ret) {
pr_err("Failed to enable timer clock\n");
goto out_timer_clk;
}
timer->clk = timer_clk;
timer->freq = clk_get_rate(timer_clk);
irq = irq_of_parse_and_map(np, 0);
if (!irq) {
ret = -EINVAL;
pr_err("Failed to map interrupts for '%s'\n", TIMER_NAME);
goto out_irq;
}
timer->irq = irq;
rk_timer_interrupt_clear(timer);
rk_timer_disable(timer);
return 0;
out_irq:
clk_disable_unprepare(timer_clk);
out_timer_clk:
clk_disable_unprepare(pclk);
out_unmap:
iounmap(timer->base);
return ret;
}
static void __init rk_timer_cleanup(struct rk_timer *timer)
{
clk_disable_unprepare(timer->clk);
clk_disable_unprepare(timer->pclk);
iounmap(timer->base);
}
static int __init rk_clkevt_init(struct device_node *np)
{
struct clock_event_device *ce;
int ret = -EINVAL;
rk_clkevt = kzalloc(sizeof(struct rk_clkevt), GFP_KERNEL);
if (!rk_clkevt) {
ret = -ENOMEM;
goto out;
}
ret = rk_timer_probe(&rk_clkevt->timer, np);
if (ret)
goto out_probe;
ce = &rk_clkevt->ce;
ce->name = TIMER_NAME;
ce->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT |
CLOCK_EVT_FEAT_DYNIRQ;
ce->set_next_event = rk_timer_set_next_event;
ce->set_state_shutdown = rk_timer_shutdown;
ce->set_state_periodic = rk_timer_set_periodic;
ce->irq = rk_clkevt->timer.irq;
ce->cpumask = cpu_possible_mask;
ce->rating = 250;
ret = request_irq(rk_clkevt->timer.irq, rk_timer_interrupt, IRQF_TIMER,
TIMER_NAME, ce);
if (ret) {
pr_err("Failed to initialize '%s': %d\n",
TIMER_NAME, ret);
goto out_irq;
}
clockevents_config_and_register(&rk_clkevt->ce,
rk_clkevt->timer.freq, 1, UINT_MAX);
return 0;
out_irq:
rk_timer_cleanup(&rk_clkevt->timer);
out_probe:
kfree(rk_clkevt);
out:
/* Leave rk_clkevt not NULL to prevent future init */
rk_clkevt = ERR_PTR(ret);
return ret;
}
static int __init rk_clksrc_init(struct device_node *np)
{
int ret = -EINVAL;
rk_clksrc = kzalloc(sizeof(struct rk_timer), GFP_KERNEL);
if (!rk_clksrc) {
ret = -ENOMEM;
goto out;
}
ret = rk_timer_probe(rk_clksrc, np);
if (ret)
goto out_probe;
rk_timer_update_counter(UINT_MAX, rk_clksrc);
rk_timer_enable(rk_clksrc, 0);
ret = clocksource_mmio_init(rk_clksrc->base + TIMER_CURRENT_VALUE0,
TIMER_NAME, rk_clksrc->freq, 250, 32,
clocksource_mmio_readl_down);
if (ret) {
pr_err("Failed to register clocksource\n");
goto out_clocksource;
}
sched_clock_register(rk_timer_sched_read, 32, rk_clksrc->freq);
return 0;
out_clocksource:
rk_timer_cleanup(rk_clksrc);
out_probe:
kfree(rk_clksrc);
out:
/* Leave rk_clksrc not NULL to prevent future init */
rk_clksrc = ERR_PTR(ret);
return ret;
}
static int __init rk_timer_init(struct device_node *np)
{
if (!rk_clkevt)
return rk_clkevt_init(np);
if (!rk_clksrc)
return rk_clksrc_init(np);
pr_err("Too many timer definitions for '%s'\n", TIMER_NAME);
return -EINVAL;
}
TIMER_OF_DECLARE(rk3288_timer, "rockchip,rk3288-timer", rk_timer_init);
TIMER_OF_DECLARE(rk3399_timer, "rockchip,rk3399-timer", rk_timer_init);
+509
View File
@@ -0,0 +1,509 @@
/*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* samsung - Common hr-timer support (s3c and s5p)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/sched_clock.h>
#include <clocksource/samsung_pwm.h>
/*
* Clocksource driver
*/
#define REG_TCFG0 0x00
#define REG_TCFG1 0x04
#define REG_TCON 0x08
#define REG_TINT_CSTAT 0x44
#define REG_TCNTB(chan) (0x0c + 12 * (chan))
#define REG_TCMPB(chan) (0x10 + 12 * (chan))
#define TCFG0_PRESCALER_MASK 0xff
#define TCFG0_PRESCALER1_SHIFT 8
#define TCFG1_SHIFT(x) ((x) * 4)
#define TCFG1_MUX_MASK 0xf
/*
* Each channel occupies 4 bits in TCON register, but there is a gap of 4
* bits (one channel) after channel 0, so channels have different numbering
* when accessing TCON register.
*
* In addition, the location of autoreload bit for channel 4 (TCON channel 5)
* in its set of bits is 2 as opposed to 3 for other channels.
*/
#define TCON_START(chan) (1 << (4 * (chan) + 0))
#define TCON_MANUALUPDATE(chan) (1 << (4 * (chan) + 1))
#define TCON_INVERT(chan) (1 << (4 * (chan) + 2))
#define _TCON_AUTORELOAD(chan) (1 << (4 * (chan) + 3))
#define _TCON_AUTORELOAD4(chan) (1 << (4 * (chan) + 2))
#define TCON_AUTORELOAD(chan) \
((chan < 5) ? _TCON_AUTORELOAD(chan) : _TCON_AUTORELOAD4(chan))
DEFINE_SPINLOCK(samsung_pwm_lock);
EXPORT_SYMBOL(samsung_pwm_lock);
struct samsung_pwm_clocksource {
void __iomem *base;
void __iomem *source_reg;
unsigned int irq[SAMSUNG_PWM_NUM];
struct samsung_pwm_variant variant;
struct clk *timerclk;
unsigned int event_id;
unsigned int source_id;
unsigned int tcnt_max;
unsigned int tscaler_div;
unsigned int tdiv;
unsigned long clock_count_per_tick;
};
static struct samsung_pwm_clocksource pwm;
static void samsung_timer_set_prescale(unsigned int channel, u16 prescale)
{
unsigned long flags;
u8 shift = 0;
u32 reg;
if (channel >= 2)
shift = TCFG0_PRESCALER1_SHIFT;
spin_lock_irqsave(&samsung_pwm_lock, flags);
reg = readl(pwm.base + REG_TCFG0);
reg &= ~(TCFG0_PRESCALER_MASK << shift);
reg |= (prescale - 1) << shift;
writel(reg, pwm.base + REG_TCFG0);
spin_unlock_irqrestore(&samsung_pwm_lock, flags);
}
static void samsung_timer_set_divisor(unsigned int channel, u8 divisor)
{
u8 shift = TCFG1_SHIFT(channel);
unsigned long flags;
u32 reg;
u8 bits;
bits = (fls(divisor) - 1) - pwm.variant.div_base;
spin_lock_irqsave(&samsung_pwm_lock, flags);
reg = readl(pwm.base + REG_TCFG1);
reg &= ~(TCFG1_MUX_MASK << shift);
reg |= bits << shift;
writel(reg, pwm.base + REG_TCFG1);
spin_unlock_irqrestore(&samsung_pwm_lock, flags);
}
static void samsung_time_stop(unsigned int channel)
{
unsigned long tcon;
unsigned long flags;
if (channel > 0)
++channel;
spin_lock_irqsave(&samsung_pwm_lock, flags);
tcon = readl_relaxed(pwm.base + REG_TCON);
tcon &= ~TCON_START(channel);
writel_relaxed(tcon, pwm.base + REG_TCON);
spin_unlock_irqrestore(&samsung_pwm_lock, flags);
}
static void samsung_time_setup(unsigned int channel, unsigned long tcnt)
{
unsigned long tcon;
unsigned long flags;
unsigned int tcon_chan = channel;
if (tcon_chan > 0)
++tcon_chan;
spin_lock_irqsave(&samsung_pwm_lock, flags);
tcon = readl_relaxed(pwm.base + REG_TCON);
tcon &= ~(TCON_START(tcon_chan) | TCON_AUTORELOAD(tcon_chan));
tcon |= TCON_MANUALUPDATE(tcon_chan);
writel_relaxed(tcnt, pwm.base + REG_TCNTB(channel));
writel_relaxed(tcnt, pwm.base + REG_TCMPB(channel));
writel_relaxed(tcon, pwm.base + REG_TCON);
spin_unlock_irqrestore(&samsung_pwm_lock, flags);
}
static void samsung_time_start(unsigned int channel, bool periodic)
{
unsigned long tcon;
unsigned long flags;
if (channel > 0)
++channel;
spin_lock_irqsave(&samsung_pwm_lock, flags);
tcon = readl_relaxed(pwm.base + REG_TCON);
tcon &= ~TCON_MANUALUPDATE(channel);
tcon |= TCON_START(channel);
if (periodic)
tcon |= TCON_AUTORELOAD(channel);
else
tcon &= ~TCON_AUTORELOAD(channel);
writel_relaxed(tcon, pwm.base + REG_TCON);
spin_unlock_irqrestore(&samsung_pwm_lock, flags);
}
static int samsung_set_next_event(unsigned long cycles,
struct clock_event_device *evt)
{
/*
* This check is needed to account for internal rounding
* errors inside clockevents core, which might result in
* passing cycles = 0, which in turn would not generate any
* timer interrupt and hang the system.
*
* Another solution would be to set up the clockevent device
* with min_delta = 2, but this would unnecessarily increase
* the minimum sleep period.
*/
if (!cycles)
cycles = 1;
samsung_time_setup(pwm.event_id, cycles);
samsung_time_start(pwm.event_id, false);
return 0;
}
static int samsung_shutdown(struct clock_event_device *evt)
{
samsung_time_stop(pwm.event_id);
return 0;
}
static int samsung_set_periodic(struct clock_event_device *evt)
{
samsung_time_stop(pwm.event_id);
samsung_time_setup(pwm.event_id, pwm.clock_count_per_tick - 1);
samsung_time_start(pwm.event_id, true);
return 0;
}
static void samsung_clockevent_resume(struct clock_event_device *cev)
{
samsung_timer_set_prescale(pwm.event_id, pwm.tscaler_div);
samsung_timer_set_divisor(pwm.event_id, pwm.tdiv);
if (pwm.variant.has_tint_cstat) {
u32 mask = (1 << pwm.event_id);
writel(mask | (mask << 5), pwm.base + REG_TINT_CSTAT);
}
}
static struct clock_event_device time_event_device = {
.name = "samsung_event_timer",
.features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT,
.rating = 200,
.set_next_event = samsung_set_next_event,
.set_state_shutdown = samsung_shutdown,
.set_state_periodic = samsung_set_periodic,
.set_state_oneshot = samsung_shutdown,
.tick_resume = samsung_shutdown,
.resume = samsung_clockevent_resume,
};
static irqreturn_t samsung_clock_event_isr(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
if (pwm.variant.has_tint_cstat) {
u32 mask = (1 << pwm.event_id);
writel(mask | (mask << 5), pwm.base + REG_TINT_CSTAT);
}
evt->event_handler(evt);
return IRQ_HANDLED;
}
static struct irqaction samsung_clock_event_irq = {
.name = "samsung_time_irq",
.flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = samsung_clock_event_isr,
.dev_id = &time_event_device,
};
static void __init samsung_clockevent_init(void)
{
unsigned long pclk;
unsigned long clock_rate;
unsigned int irq_number;
pclk = clk_get_rate(pwm.timerclk);
samsung_timer_set_prescale(pwm.event_id, pwm.tscaler_div);
samsung_timer_set_divisor(pwm.event_id, pwm.tdiv);
clock_rate = pclk / (pwm.tscaler_div * pwm.tdiv);
pwm.clock_count_per_tick = clock_rate / HZ;
time_event_device.cpumask = cpumask_of(0);
clockevents_config_and_register(&time_event_device,
clock_rate, 1, pwm.tcnt_max);
irq_number = pwm.irq[pwm.event_id];
setup_irq(irq_number, &samsung_clock_event_irq);
if (pwm.variant.has_tint_cstat) {
u32 mask = (1 << pwm.event_id);
writel(mask | (mask << 5), pwm.base + REG_TINT_CSTAT);
}
}
static void samsung_clocksource_suspend(struct clocksource *cs)
{
samsung_time_stop(pwm.source_id);
}
static void samsung_clocksource_resume(struct clocksource *cs)
{
samsung_timer_set_prescale(pwm.source_id, pwm.tscaler_div);
samsung_timer_set_divisor(pwm.source_id, pwm.tdiv);
samsung_time_setup(pwm.source_id, pwm.tcnt_max);
samsung_time_start(pwm.source_id, true);
}
static u64 notrace samsung_clocksource_read(struct clocksource *c)
{
return ~readl_relaxed(pwm.source_reg);
}
static struct clocksource samsung_clocksource = {
.name = "samsung_clocksource_timer",
.rating = 250,
.read = samsung_clocksource_read,
.suspend = samsung_clocksource_suspend,
.resume = samsung_clocksource_resume,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
/*
* Override the global weak sched_clock symbol with this
* local implementation which uses the clocksource to get some
* better resolution when scheduling the kernel. We accept that
* this wraps around for now, since it is just a relative time
* stamp. (Inspired by U300 implementation.)
*/
static u64 notrace samsung_read_sched_clock(void)
{
return samsung_clocksource_read(NULL);
}
static int __init samsung_clocksource_init(void)
{
unsigned long pclk;
unsigned long clock_rate;
pclk = clk_get_rate(pwm.timerclk);
samsung_timer_set_prescale(pwm.source_id, pwm.tscaler_div);
samsung_timer_set_divisor(pwm.source_id, pwm.tdiv);
clock_rate = pclk / (pwm.tscaler_div * pwm.tdiv);
samsung_time_setup(pwm.source_id, pwm.tcnt_max);
samsung_time_start(pwm.source_id, true);
if (pwm.source_id == 4)
pwm.source_reg = pwm.base + 0x40;
else
pwm.source_reg = pwm.base + pwm.source_id * 0x0c + 0x14;
sched_clock_register(samsung_read_sched_clock,
pwm.variant.bits, clock_rate);
samsung_clocksource.mask = CLOCKSOURCE_MASK(pwm.variant.bits);
return clocksource_register_hz(&samsung_clocksource, clock_rate);
}
static void __init samsung_timer_resources(void)
{
clk_prepare_enable(pwm.timerclk);
pwm.tcnt_max = (1UL << pwm.variant.bits) - 1;
if (pwm.variant.bits == 16) {
pwm.tscaler_div = 25;
pwm.tdiv = 2;
} else {
pwm.tscaler_div = 2;
pwm.tdiv = 1;
}
}
/*
* PWM master driver
*/
static int __init _samsung_pwm_clocksource_init(void)
{
u8 mask;
int channel;
mask = ~pwm.variant.output_mask & ((1 << SAMSUNG_PWM_NUM) - 1);
channel = fls(mask) - 1;
if (channel < 0) {
pr_crit("failed to find PWM channel for clocksource\n");
return -EINVAL;
}
pwm.source_id = channel;
mask &= ~(1 << channel);
channel = fls(mask) - 1;
if (channel < 0) {
pr_crit("failed to find PWM channel for clock event\n");
return -EINVAL;
}
pwm.event_id = channel;
samsung_timer_resources();
samsung_clockevent_init();
return samsung_clocksource_init();
}
void __init samsung_pwm_clocksource_init(void __iomem *base,
unsigned int *irqs, struct samsung_pwm_variant *variant)
{
pwm.base = base;
memcpy(&pwm.variant, variant, sizeof(pwm.variant));
memcpy(pwm.irq, irqs, SAMSUNG_PWM_NUM * sizeof(*irqs));
pwm.timerclk = clk_get(NULL, "timers");
if (IS_ERR(pwm.timerclk))
panic("failed to get timers clock for timer");
_samsung_pwm_clocksource_init();
}
#ifdef CONFIG_TIMER_OF
static int __init samsung_pwm_alloc(struct device_node *np,
const struct samsung_pwm_variant *variant)
{
struct property *prop;
const __be32 *cur;
u32 val;
int i;
memcpy(&pwm.variant, variant, sizeof(pwm.variant));
for (i = 0; i < SAMSUNG_PWM_NUM; ++i)
pwm.irq[i] = irq_of_parse_and_map(np, i);
of_property_for_each_u32(np, "samsung,pwm-outputs", prop, cur, val) {
if (val >= SAMSUNG_PWM_NUM) {
pr_warning("%s: invalid channel index in samsung,pwm-outputs property\n",
__func__);
continue;
}
pwm.variant.output_mask |= 1 << val;
}
pwm.base = of_iomap(np, 0);
if (!pwm.base) {
pr_err("%s: failed to map PWM registers\n", __func__);
return -ENXIO;
}
pwm.timerclk = of_clk_get_by_name(np, "timers");
if (IS_ERR(pwm.timerclk)) {
pr_crit("failed to get timers clock for timer\n");
return PTR_ERR(pwm.timerclk);
}
return _samsung_pwm_clocksource_init();
}
static const struct samsung_pwm_variant s3c24xx_variant = {
.bits = 16,
.div_base = 1,
.has_tint_cstat = false,
.tclk_mask = (1 << 4),
};
static int __init s3c2410_pwm_clocksource_init(struct device_node *np)
{
return samsung_pwm_alloc(np, &s3c24xx_variant);
}
TIMER_OF_DECLARE(s3c2410_pwm, "samsung,s3c2410-pwm", s3c2410_pwm_clocksource_init);
static const struct samsung_pwm_variant s3c64xx_variant = {
.bits = 32,
.div_base = 0,
.has_tint_cstat = true,
.tclk_mask = (1 << 7) | (1 << 6) | (1 << 5),
};
static int __init s3c64xx_pwm_clocksource_init(struct device_node *np)
{
return samsung_pwm_alloc(np, &s3c64xx_variant);
}
TIMER_OF_DECLARE(s3c6400_pwm, "samsung,s3c6400-pwm", s3c64xx_pwm_clocksource_init);
static const struct samsung_pwm_variant s5p64x0_variant = {
.bits = 32,
.div_base = 0,
.has_tint_cstat = true,
.tclk_mask = 0,
};
static int __init s5p64x0_pwm_clocksource_init(struct device_node *np)
{
return samsung_pwm_alloc(np, &s5p64x0_variant);
}
TIMER_OF_DECLARE(s5p6440_pwm, "samsung,s5p6440-pwm", s5p64x0_pwm_clocksource_init);
static const struct samsung_pwm_variant s5p_variant = {
.bits = 32,
.div_base = 0,
.has_tint_cstat = true,
.tclk_mask = (1 << 5),
};
static int __init s5p_pwm_clocksource_init(struct device_node *np)
{
return samsung_pwm_alloc(np, &s5p_variant);
}
TIMER_OF_DECLARE(s5pc100_pwm, "samsung,s5pc100-pwm", s5p_pwm_clocksource_init);
#endif
+93
View File
@@ -0,0 +1,93 @@
/*
* Copyright (C) 2006 Jim Cromie
*
* This is a clocksource driver for the Geode SCx200's 1 or 27 MHz
* high-resolution timer. The Geode SC-1100 (at least) has a buggy
* time stamp counter (TSC), which loses time unless 'idle=poll' is
* given as a boot-arg. In its absence, the Generic Timekeeping code
* will detect and de-rate the bad TSC, allowing this timer to take
* over timekeeping duties.
*
* Based on work by John Stultz, and Ted Phelps (in a 2.6.12-rc6 patch)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*/
#include <linux/clocksource.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/scx200.h>
#define NAME "scx200_hrt"
static int mhz27;
module_param(mhz27, int, 0); /* load time only */
MODULE_PARM_DESC(mhz27, "count at 27.0 MHz (default is 1.0 MHz)");
static int ppm;
module_param(ppm, int, 0); /* load time only */
MODULE_PARM_DESC(ppm, "+-adjust to actual XO freq (ppm)");
/* HiRes Timer configuration register address */
#define SCx200_TMCNFG_OFFSET (SCx200_TIMER_OFFSET + 5)
/* and config settings */
#define HR_TMEN (1 << 0) /* timer interrupt enable */
#define HR_TMCLKSEL (1 << 1) /* 1|0 counts at 27|1 MHz */
#define HR_TM27MPD (1 << 2) /* 1 turns off input clock (power-down) */
/* The base timer frequency, * 27 if selected */
#define HRT_FREQ 1000000
static u64 read_hrt(struct clocksource *cs)
{
/* Read the timer value */
return (u64) inl(scx200_cb_base + SCx200_TIMER_OFFSET);
}
static struct clocksource cs_hrt = {
.name = "scx200_hrt",
.rating = 250,
.read = read_hrt,
.mask = CLOCKSOURCE_MASK(32),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
/* mult, shift are set based on mhz27 flag */
};
static int __init init_hrt_clocksource(void)
{
u32 freq;
/* Make sure scx200 has initialized the configuration block */
if (!scx200_cb_present())
return -ENODEV;
/* Reserve the timer's ISA io-region for ourselves */
if (!request_region(scx200_cb_base + SCx200_TIMER_OFFSET,
SCx200_TIMER_SIZE,
"NatSemi SCx200 High-Resolution Timer")) {
pr_warn("unable to lock timer region\n");
return -ENODEV;
}
/* write timer config */
outb(HR_TMEN | (mhz27 ? HR_TMCLKSEL : 0),
scx200_cb_base + SCx200_TMCNFG_OFFSET);
freq = (HRT_FREQ + ppm);
if (mhz27)
freq *= 27;
pr_info("enabling scx200 high-res timer (%s MHz +%d ppm)\n", mhz27 ? "27":"1", ppm);
return clocksource_register_hz(&cs_hrt, freq);
}
module_init(init_hrt_clocksource);
MODULE_AUTHOR("Jim Cromie <jim.cromie@gmail.com>");
MODULE_DESCRIPTION("clocksource on SCx200 HiRes Timer");
MODULE_LICENSE("GPL");
File diff suppressed because it is too large. Load diff
+528
View File
@@ -0,0 +1,528 @@
/*
* SuperH Timer Support - MTU2
*
* Copyright (C) 2009 Magnus Damm
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/sh_timer.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
struct sh_mtu2_device;
struct sh_mtu2_channel {
struct sh_mtu2_device *mtu;
unsigned int index;
void __iomem *base;
struct clock_event_device ced;
};
struct sh_mtu2_device {
struct platform_device *pdev;
void __iomem *mapbase;
struct clk *clk;
raw_spinlock_t lock; /* Protect the shared registers */
struct sh_mtu2_channel *channels;
unsigned int num_channels;
bool has_clockevent;
};
#define TSTR -1 /* shared register */
#define TCR 0 /* channel register */
#define TMDR 1 /* channel register */
#define TIOR 2 /* channel register */
#define TIER 3 /* channel register */
#define TSR 4 /* channel register */
#define TCNT 5 /* channel register */
#define TGR 6 /* channel register */
#define TCR_CCLR_NONE (0 << 5)
#define TCR_CCLR_TGRA (1 << 5)
#define TCR_CCLR_TGRB (2 << 5)
#define TCR_CCLR_SYNC (3 << 5)
#define TCR_CCLR_TGRC (5 << 5)
#define TCR_CCLR_TGRD (6 << 5)
#define TCR_CCLR_MASK (7 << 5)
#define TCR_CKEG_RISING (0 << 3)
#define TCR_CKEG_FALLING (1 << 3)
#define TCR_CKEG_BOTH (2 << 3)
#define TCR_CKEG_MASK (3 << 3)
/* Values 4 to 7 are channel-dependent */
#define TCR_TPSC_P1 (0 << 0)
#define TCR_TPSC_P4 (1 << 0)
#define TCR_TPSC_P16 (2 << 0)
#define TCR_TPSC_P64 (3 << 0)
#define TCR_TPSC_CH0_TCLKA (4 << 0)
#define TCR_TPSC_CH0_TCLKB (5 << 0)
#define TCR_TPSC_CH0_TCLKC (6 << 0)
#define TCR_TPSC_CH0_TCLKD (7 << 0)
#define TCR_TPSC_CH1_TCLKA (4 << 0)
#define TCR_TPSC_CH1_TCLKB (5 << 0)
#define TCR_TPSC_CH1_P256 (6 << 0)
#define TCR_TPSC_CH1_TCNT2 (7 << 0)
#define TCR_TPSC_CH2_TCLKA (4 << 0)
#define TCR_TPSC_CH2_TCLKB (5 << 0)
#define TCR_TPSC_CH2_TCLKC (6 << 0)
#define TCR_TPSC_CH2_P1024 (7 << 0)
#define TCR_TPSC_CH34_P256 (4 << 0)
#define TCR_TPSC_CH34_P1024 (5 << 0)
#define TCR_TPSC_CH34_TCLKA (6 << 0)
#define TCR_TPSC_CH34_TCLKB (7 << 0)
#define TCR_TPSC_MASK (7 << 0)
#define TMDR_BFE (1 << 6)
#define TMDR_BFB (1 << 5)
#define TMDR_BFA (1 << 4)
#define TMDR_MD_NORMAL (0 << 0)
#define TMDR_MD_PWM_1 (2 << 0)
#define TMDR_MD_PWM_2 (3 << 0)
#define TMDR_MD_PHASE_1 (4 << 0)
#define TMDR_MD_PHASE_2 (5 << 0)
#define TMDR_MD_PHASE_3 (6 << 0)
#define TMDR_MD_PHASE_4 (7 << 0)
#define TMDR_MD_PWM_SYNC (8 << 0)
#define TMDR_MD_PWM_COMP_CREST (13 << 0)
#define TMDR_MD_PWM_COMP_TROUGH (14 << 0)
#define TMDR_MD_PWM_COMP_BOTH (15 << 0)
#define TMDR_MD_MASK (15 << 0)
#define TIOC_IOCH(n) ((n) << 4)
#define TIOC_IOCL(n) ((n) << 0)
#define TIOR_OC_RETAIN (0 << 0)
#define TIOR_OC_0_CLEAR (1 << 0)
#define TIOR_OC_0_SET (2 << 0)
#define TIOR_OC_0_TOGGLE (3 << 0)
#define TIOR_OC_1_CLEAR (5 << 0)
#define TIOR_OC_1_SET (6 << 0)
#define TIOR_OC_1_TOGGLE (7 << 0)
#define TIOR_IC_RISING (8 << 0)
#define TIOR_IC_FALLING (9 << 0)
#define TIOR_IC_BOTH (10 << 0)
#define TIOR_IC_TCNT (12 << 0)
#define TIOR_MASK (15 << 0)
#define TIER_TTGE (1 << 7)
#define TIER_TTGE2 (1 << 6)
#define TIER_TCIEU (1 << 5)
#define TIER_TCIEV (1 << 4)
#define TIER_TGIED (1 << 3)
#define TIER_TGIEC (1 << 2)
#define TIER_TGIEB (1 << 1)
#define TIER_TGIEA (1 << 0)
#define TSR_TCFD (1 << 7)
#define TSR_TCFU (1 << 5)
#define TSR_TCFV (1 << 4)
#define TSR_TGFD (1 << 3)
#define TSR_TGFC (1 << 2)
#define TSR_TGFB (1 << 1)
#define TSR_TGFA (1 << 0)
static unsigned long mtu2_reg_offs[] = {
[TCR] = 0,
[TMDR] = 1,
[TIOR] = 2,
[TIER] = 4,
[TSR] = 5,
[TCNT] = 6,
[TGR] = 8,
};
static inline unsigned long sh_mtu2_read(struct sh_mtu2_channel *ch, int reg_nr)
{
unsigned long offs;
if (reg_nr == TSTR)
return ioread8(ch->mtu->mapbase + 0x280);
offs = mtu2_reg_offs[reg_nr];
if ((reg_nr == TCNT) || (reg_nr == TGR))
return ioread16(ch->base + offs);
else
return ioread8(ch->base + offs);
}
static inline void sh_mtu2_write(struct sh_mtu2_channel *ch, int reg_nr,
unsigned long value)
{
unsigned long offs;
if (reg_nr == TSTR)
return iowrite8(value, ch->mtu->mapbase + 0x280);
offs = mtu2_reg_offs[reg_nr];
if ((reg_nr == TCNT) || (reg_nr == TGR))
iowrite16(value, ch->base + offs);
else
iowrite8(value, ch->base + offs);
}
static void sh_mtu2_start_stop_ch(struct sh_mtu2_channel *ch, int start)
{
unsigned long flags, value;
/* start stop register shared by multiple timer channels */
raw_spin_lock_irqsave(&ch->mtu->lock, flags);
value = sh_mtu2_read(ch, TSTR);
if (start)
value |= 1 << ch->index;
else
value &= ~(1 << ch->index);
sh_mtu2_write(ch, TSTR, value);
raw_spin_unlock_irqrestore(&ch->mtu->lock, flags);
}
static int sh_mtu2_enable(struct sh_mtu2_channel *ch)
{
unsigned long periodic;
unsigned long rate;
int ret;
pm_runtime_get_sync(&ch->mtu->pdev->dev);
dev_pm_syscore_device(&ch->mtu->pdev->dev, true);
/* enable clock */
ret = clk_enable(ch->mtu->clk);
if (ret) {
dev_err(&ch->mtu->pdev->dev, "ch%u: cannot enable clock\n",
ch->index);
return ret;
}
/* make sure channel is disabled */
sh_mtu2_start_stop_ch(ch, 0);
rate = clk_get_rate(ch->mtu->clk) / 64;
periodic = (rate + HZ/2) / HZ;
/*
* "Periodic Counter Operation"
* Clear on TGRA compare match, divide clock by 64.
*/
sh_mtu2_write(ch, TCR, TCR_CCLR_TGRA | TCR_TPSC_P64);
sh_mtu2_write(ch, TIOR, TIOC_IOCH(TIOR_OC_0_CLEAR) |
TIOC_IOCL(TIOR_OC_0_CLEAR));
sh_mtu2_write(ch, TGR, periodic);
sh_mtu2_write(ch, TCNT, 0);
sh_mtu2_write(ch, TMDR, TMDR_MD_NORMAL);
sh_mtu2_write(ch, TIER, TIER_TGIEA);
/* enable channel */
sh_mtu2_start_stop_ch(ch, 1);
return 0;
}
static void sh_mtu2_disable(struct sh_mtu2_channel *ch)
{
/* disable channel */
sh_mtu2_start_stop_ch(ch, 0);
/* stop clock */
clk_disable(ch->mtu->clk);
dev_pm_syscore_device(&ch->mtu->pdev->dev, false);
pm_runtime_put(&ch->mtu->pdev->dev);
}
static irqreturn_t sh_mtu2_interrupt(int irq, void *dev_id)
{
struct sh_mtu2_channel *ch = dev_id;
/* acknowledge interrupt */
sh_mtu2_read(ch, TSR);
sh_mtu2_write(ch, TSR, ~TSR_TGFA);
/* notify clockevent layer */
ch->ced.event_handler(&ch->ced);
return IRQ_HANDLED;
}
static struct sh_mtu2_channel *ced_to_sh_mtu2(struct clock_event_device *ced)
{
return container_of(ced, struct sh_mtu2_channel, ced);
}
static int sh_mtu2_clock_event_shutdown(struct clock_event_device *ced)
{
struct sh_mtu2_channel *ch = ced_to_sh_mtu2(ced);
if (clockevent_state_periodic(ced))
sh_mtu2_disable(ch);
return 0;
}
static int sh_mtu2_clock_event_set_periodic(struct clock_event_device *ced)
{
struct sh_mtu2_channel *ch = ced_to_sh_mtu2(ced);
if (clockevent_state_periodic(ced))
sh_mtu2_disable(ch);
dev_info(&ch->mtu->pdev->dev, "ch%u: used for periodic clock events\n",
ch->index);
sh_mtu2_enable(ch);
return 0;
}
static void sh_mtu2_clock_event_suspend(struct clock_event_device *ced)
{
pm_genpd_syscore_poweroff(&ced_to_sh_mtu2(ced)->mtu->pdev->dev);
}
static void sh_mtu2_clock_event_resume(struct clock_event_device *ced)
{
pm_genpd_syscore_poweron(&ced_to_sh_mtu2(ced)->mtu->pdev->dev);
}
static void sh_mtu2_register_clockevent(struct sh_mtu2_channel *ch,
const char *name)
{
struct clock_event_device *ced = &ch->ced;
ced->name = name;
ced->features = CLOCK_EVT_FEAT_PERIODIC;
ced->rating = 200;
ced->cpumask = cpu_possible_mask;
ced->set_state_shutdown = sh_mtu2_clock_event_shutdown;
ced->set_state_periodic = sh_mtu2_clock_event_set_periodic;
ced->suspend = sh_mtu2_clock_event_suspend;
ced->resume = sh_mtu2_clock_event_resume;
dev_info(&ch->mtu->pdev->dev, "ch%u: used for clock events\n",
ch->index);
clockevents_register_device(ced);
}
static int sh_mtu2_register(struct sh_mtu2_channel *ch, const char *name)
{
ch->mtu->has_clockevent = true;
sh_mtu2_register_clockevent(ch, name);
return 0;
}
static int sh_mtu2_setup_channel(struct sh_mtu2_channel *ch, unsigned int index,
struct sh_mtu2_device *mtu)
{
static const unsigned int channel_offsets[] = {
0x300, 0x380, 0x000,
};
char name[6];
int irq;
int ret;
ch->mtu = mtu;
sprintf(name, "tgi%ua", index);
irq = platform_get_irq_byname(mtu->pdev, name);
if (irq < 0) {
/* Skip channels with no declared interrupt. */
return 0;
}
ret = request_irq(irq, sh_mtu2_interrupt,
IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING,
dev_name(&ch->mtu->pdev->dev), ch);
if (ret) {
dev_err(&ch->mtu->pdev->dev, "ch%u: failed to request irq %d\n",
index, irq);
return ret;
}
ch->base = mtu->mapbase + channel_offsets[index];
ch->index = index;
return sh_mtu2_register(ch, dev_name(&mtu->pdev->dev));
}
static int sh_mtu2_map_memory(struct sh_mtu2_device *mtu)
{
struct resource *res;
res = platform_get_resource(mtu->pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&mtu->pdev->dev, "failed to get I/O memory\n");
return -ENXIO;
}
mtu->mapbase = ioremap_nocache(res->start, resource_size(res));
if (mtu->mapbase == NULL)
return -ENXIO;
return 0;
}
static int sh_mtu2_setup(struct sh_mtu2_device *mtu,
struct platform_device *pdev)
{
unsigned int i;
int ret;
mtu->pdev = pdev;
raw_spin_lock_init(&mtu->lock);
/* Get hold of clock. */
mtu->clk = clk_get(&mtu->pdev->dev, "fck");
if (IS_ERR(mtu->clk)) {
dev_err(&mtu->pdev->dev, "cannot get clock\n");
return PTR_ERR(mtu->clk);
}
ret = clk_prepare(mtu->clk);
if (ret < 0)
goto err_clk_put;
/* Map the memory resource. */
ret = sh_mtu2_map_memory(mtu);
if (ret < 0) {
dev_err(&mtu->pdev->dev, "failed to remap I/O memory\n");
goto err_clk_unprepare;
}
/* Allocate and setup the channels. */
mtu->num_channels = 3;
mtu->channels = kcalloc(mtu->num_channels, sizeof(*mtu->channels),
GFP_KERNEL);
if (mtu->channels == NULL) {
ret = -ENOMEM;
goto err_unmap;
}
for (i = 0; i < mtu->num_channels; ++i) {
ret = sh_mtu2_setup_channel(&mtu->channels[i], i, mtu);
if (ret < 0)
goto err_unmap;
}
platform_set_drvdata(pdev, mtu);
return 0;
err_unmap:
kfree(mtu->channels);
iounmap(mtu->mapbase);
err_clk_unprepare:
clk_unprepare(mtu->clk);
err_clk_put:
clk_put(mtu->clk);
return ret;
}
static int sh_mtu2_probe(struct platform_device *pdev)
{
struct sh_mtu2_device *mtu = platform_get_drvdata(pdev);
int ret;
if (!is_early_platform_device(pdev)) {
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
}
if (mtu) {
dev_info(&pdev->dev, "kept as earlytimer\n");
goto out;
}
mtu = kzalloc(sizeof(*mtu), GFP_KERNEL);
if (mtu == NULL)
return -ENOMEM;
ret = sh_mtu2_setup(mtu, pdev);
if (ret) {
kfree(mtu);
pm_runtime_idle(&pdev->dev);
return ret;
}
if (is_early_platform_device(pdev))
return 0;
out:
if (mtu->has_clockevent)
pm_runtime_irq_safe(&pdev->dev);
else
pm_runtime_idle(&pdev->dev);
return 0;
}
static int sh_mtu2_remove(struct platform_device *pdev)
{
return -EBUSY; /* cannot unregister clockevent */
}
static const struct platform_device_id sh_mtu2_id_table[] = {
{ "sh-mtu2", 0 },
{ },
};
MODULE_DEVICE_TABLE(platform, sh_mtu2_id_table);
static const struct of_device_id sh_mtu2_of_table[] __maybe_unused = {
{ .compatible = "renesas,mtu2" },
{ }
};
MODULE_DEVICE_TABLE(of, sh_mtu2_of_table);
static struct platform_driver sh_mtu2_device_driver = {
.probe = sh_mtu2_probe,
.remove = sh_mtu2_remove,
.driver = {
.name = "sh_mtu2",
.of_match_table = of_match_ptr(sh_mtu2_of_table),
},
.id_table = sh_mtu2_id_table,
};
static int __init sh_mtu2_init(void)
{
return platform_driver_register(&sh_mtu2_device_driver);
}
static void __exit sh_mtu2_exit(void)
{
platform_driver_unregister(&sh_mtu2_device_driver);
}
early_platform_init("earlytimer", &sh_mtu2_device_driver);
subsys_initcall(sh_mtu2_init);
module_exit(sh_mtu2_exit);
MODULE_AUTHOR("Magnus Damm");
MODULE_DESCRIPTION("SuperH MTU2 Timer Driver");
MODULE_LICENSE("GPL v2");
+685
View File
@@ -0,0 +1,685 @@
/*
* SuperH Timer Support - TMU
*
* Copyright (C) 2009 Magnus Damm
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/sh_timer.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
enum sh_tmu_model {
SH_TMU,
SH_TMU_SH3,
};
struct sh_tmu_device;
struct sh_tmu_channel {
struct sh_tmu_device *tmu;
unsigned int index;
void __iomem *base;
int irq;
unsigned long periodic;
struct clock_event_device ced;
struct clocksource cs;
bool cs_enabled;
unsigned int enable_count;
};
struct sh_tmu_device {
struct platform_device *pdev;
void __iomem *mapbase;
struct clk *clk;
unsigned long rate;
enum sh_tmu_model model;
raw_spinlock_t lock; /* Protect the shared start/stop register */
struct sh_tmu_channel *channels;
unsigned int num_channels;
bool has_clockevent;
bool has_clocksource;
};
#define TSTR -1 /* shared register */
#define TCOR 0 /* channel register */
#define TCNT 1 /* channel register */
#define TCR 2 /* channel register */
#define TCR_UNF (1 << 8)
#define TCR_UNIE (1 << 5)
#define TCR_TPSC_CLK4 (0 << 0)
#define TCR_TPSC_CLK16 (1 << 0)
#define TCR_TPSC_CLK64 (2 << 0)
#define TCR_TPSC_CLK256 (3 << 0)
#define TCR_TPSC_CLK1024 (4 << 0)
#define TCR_TPSC_MASK (7 << 0)
static inline unsigned long sh_tmu_read(struct sh_tmu_channel *ch, int reg_nr)
{
unsigned long offs;
if (reg_nr == TSTR) {
switch (ch->tmu->model) {
case SH_TMU_SH3:
return ioread8(ch->tmu->mapbase + 2);
case SH_TMU:
return ioread8(ch->tmu->mapbase + 4);
}
}
offs = reg_nr << 2;
if (reg_nr == TCR)
return ioread16(ch->base + offs);
else
return ioread32(ch->base + offs);
}
static inline void sh_tmu_write(struct sh_tmu_channel *ch, int reg_nr,
unsigned long value)
{
unsigned long offs;
if (reg_nr == TSTR) {
switch (ch->tmu->model) {
case SH_TMU_SH3:
return iowrite8(value, ch->tmu->mapbase + 2);
case SH_TMU:
return iowrite8(value, ch->tmu->mapbase + 4);
}
}
offs = reg_nr << 2;
if (reg_nr == TCR)
iowrite16(value, ch->base + offs);
else
iowrite32(value, ch->base + offs);
}
static void sh_tmu_start_stop_ch(struct sh_tmu_channel *ch, int start)
{
unsigned long flags, value;
/* start stop register shared by multiple timer channels */
raw_spin_lock_irqsave(&ch->tmu->lock, flags);
value = sh_tmu_read(ch, TSTR);
if (start)
value |= 1 << ch->index;
else
value &= ~(1 << ch->index);
sh_tmu_write(ch, TSTR, value);
raw_spin_unlock_irqrestore(&ch->tmu->lock, flags);
}
static int __sh_tmu_enable(struct sh_tmu_channel *ch)
{
int ret;
/* enable clock */
ret = clk_enable(ch->tmu->clk);
if (ret) {
dev_err(&ch->tmu->pdev->dev, "ch%u: cannot enable clock\n",
ch->index);
return ret;
}
/* make sure channel is disabled */
sh_tmu_start_stop_ch(ch, 0);
/* maximum timeout */
sh_tmu_write(ch, TCOR, 0xffffffff);
sh_tmu_write(ch, TCNT, 0xffffffff);
/* configure channel to parent clock / 4, irq off */
sh_tmu_write(ch, TCR, TCR_TPSC_CLK4);
/* enable channel */
sh_tmu_start_stop_ch(ch, 1);
return 0;
}
static int sh_tmu_enable(struct sh_tmu_channel *ch)
{
if (ch->enable_count++ > 0)
return 0;
pm_runtime_get_sync(&ch->tmu->pdev->dev);
dev_pm_syscore_device(&ch->tmu->pdev->dev, true);
return __sh_tmu_enable(ch);
}
static void __sh_tmu_disable(struct sh_tmu_channel *ch)
{
/* disable channel */
sh_tmu_start_stop_ch(ch, 0);
/* disable interrupts in TMU block */
sh_tmu_write(ch, TCR, TCR_TPSC_CLK4);
/* stop clock */
clk_disable(ch->tmu->clk);
}
static void sh_tmu_disable(struct sh_tmu_channel *ch)
{
if (WARN_ON(ch->enable_count == 0))
return;
if (--ch->enable_count > 0)
return;
__sh_tmu_disable(ch);
dev_pm_syscore_device(&ch->tmu->pdev->dev, false);
pm_runtime_put(&ch->tmu->pdev->dev);
}
static void sh_tmu_set_next(struct sh_tmu_channel *ch, unsigned long delta,
int periodic)
{
/* stop timer */
sh_tmu_start_stop_ch(ch, 0);
/* acknowledge interrupt */
sh_tmu_read(ch, TCR);
/* enable interrupt */
sh_tmu_write(ch, TCR, TCR_UNIE | TCR_TPSC_CLK4);
/* reload delta value in case of periodic timer */
if (periodic)
sh_tmu_write(ch, TCOR, delta);
else
sh_tmu_write(ch, TCOR, 0xffffffff);
sh_tmu_write(ch, TCNT, delta);
/* start timer */
sh_tmu_start_stop_ch(ch, 1);
}
static irqreturn_t sh_tmu_interrupt(int irq, void *dev_id)
{
struct sh_tmu_channel *ch = dev_id;
/* disable or acknowledge interrupt */
if (clockevent_state_oneshot(&ch->ced))
sh_tmu_write(ch, TCR, TCR_TPSC_CLK4);
else
sh_tmu_write(ch, TCR, TCR_UNIE | TCR_TPSC_CLK4);
/* notify clockevent layer */
ch->ced.event_handler(&ch->ced);
return IRQ_HANDLED;
}
static struct sh_tmu_channel *cs_to_sh_tmu(struct clocksource *cs)
{
return container_of(cs, struct sh_tmu_channel, cs);
}
static u64 sh_tmu_clocksource_read(struct clocksource *cs)
{
struct sh_tmu_channel *ch = cs_to_sh_tmu(cs);
return sh_tmu_read(ch, TCNT) ^ 0xffffffff;
}
static int sh_tmu_clocksource_enable(struct clocksource *cs)
{
struct sh_tmu_channel *ch = cs_to_sh_tmu(cs);
int ret;
if (WARN_ON(ch->cs_enabled))
return 0;
ret = sh_tmu_enable(ch);
if (!ret)
ch->cs_enabled = true;
return ret;
}
static void sh_tmu_clocksource_disable(struct clocksource *cs)
{
struct sh_tmu_channel *ch = cs_to_sh_tmu(cs);
if (WARN_ON(!ch->cs_enabled))
return;
sh_tmu_disable(ch);
ch->cs_enabled = false;
}
static void sh_tmu_clocksource_suspend(struct clocksource *cs)
{
struct sh_tmu_channel *ch = cs_to_sh_tmu(cs);
if (!ch->cs_enabled)
return;
if (--ch->enable_count == 0) {
__sh_tmu_disable(ch);
pm_genpd_syscore_poweroff(&ch->tmu->pdev->dev);
}
}
static void sh_tmu_clocksource_resume(struct clocksource *cs)
{
struct sh_tmu_channel *ch = cs_to_sh_tmu(cs);
if (!ch->cs_enabled)
return;
if (ch->enable_count++ == 0) {
pm_genpd_syscore_poweron(&ch->tmu->pdev->dev);
__sh_tmu_enable(ch);
}
}
static int sh_tmu_register_clocksource(struct sh_tmu_channel *ch,
const char *name)
{
struct clocksource *cs = &ch->cs;
cs->name = name;
cs->rating = 200;
cs->read = sh_tmu_clocksource_read;
cs->enable = sh_tmu_clocksource_enable;
cs->disable = sh_tmu_clocksource_disable;
cs->suspend = sh_tmu_clocksource_suspend;
cs->resume = sh_tmu_clocksource_resume;
cs->mask = CLOCKSOURCE_MASK(32);
cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
dev_info(&ch->tmu->pdev->dev, "ch%u: used as clock source\n",
ch->index);
clocksource_register_hz(cs, ch->tmu->rate);
return 0;
}
static struct sh_tmu_channel *ced_to_sh_tmu(struct clock_event_device *ced)
{
return container_of(ced, struct sh_tmu_channel, ced);
}
static void sh_tmu_clock_event_start(struct sh_tmu_channel *ch, int periodic)
{
sh_tmu_enable(ch);
if (periodic) {
ch->periodic = (ch->tmu->rate + HZ/2) / HZ;
sh_tmu_set_next(ch, ch->periodic, 1);
}
}
static int sh_tmu_clock_event_shutdown(struct clock_event_device *ced)
{
struct sh_tmu_channel *ch = ced_to_sh_tmu(ced);
if (clockevent_state_oneshot(ced) || clockevent_state_periodic(ced))
sh_tmu_disable(ch);
return 0;
}
static int sh_tmu_clock_event_set_state(struct clock_event_device *ced,
int periodic)
{
struct sh_tmu_channel *ch = ced_to_sh_tmu(ced);
/* deal with old setting first */
if (clockevent_state_oneshot(ced) || clockevent_state_periodic(ced))
sh_tmu_disable(ch);
dev_info(&ch->tmu->pdev->dev, "ch%u: used for %s clock events\n",
ch->index, periodic ? "periodic" : "oneshot");
sh_tmu_clock_event_start(ch, periodic);
return 0;
}
static int sh_tmu_clock_event_set_oneshot(struct clock_event_device *ced)
{
return sh_tmu_clock_event_set_state(ced, 0);
}
static int sh_tmu_clock_event_set_periodic(struct clock_event_device *ced)
{
return sh_tmu_clock_event_set_state(ced, 1);
}
static int sh_tmu_clock_event_next(unsigned long delta,
struct clock_event_device *ced)
{
struct sh_tmu_channel *ch = ced_to_sh_tmu(ced);
BUG_ON(!clockevent_state_oneshot(ced));
/* program new delta value */
sh_tmu_set_next(ch, delta, 0);
return 0;
}
static void sh_tmu_clock_event_suspend(struct clock_event_device *ced)
{
pm_genpd_syscore_poweroff(&ced_to_sh_tmu(ced)->tmu->pdev->dev);
}
static void sh_tmu_clock_event_resume(struct clock_event_device *ced)
{
pm_genpd_syscore_poweron(&ced_to_sh_tmu(ced)->tmu->pdev->dev);
}
static void sh_tmu_register_clockevent(struct sh_tmu_channel *ch,
const char *name)
{
struct clock_event_device *ced = &ch->ced;
int ret;
ced->name = name;
ced->features = CLOCK_EVT_FEAT_PERIODIC;
ced->features |= CLOCK_EVT_FEAT_ONESHOT;
ced->rating = 200;
ced->cpumask = cpu_possible_mask;
ced->set_next_event = sh_tmu_clock_event_next;
ced->set_state_shutdown = sh_tmu_clock_event_shutdown;
ced->set_state_periodic = sh_tmu_clock_event_set_periodic;
ced->set_state_oneshot = sh_tmu_clock_event_set_oneshot;
ced->suspend = sh_tmu_clock_event_suspend;
ced->resume = sh_tmu_clock_event_resume;
dev_info(&ch->tmu->pdev->dev, "ch%u: used for clock events\n",
ch->index);
clockevents_config_and_register(ced, ch->tmu->rate, 0x300, 0xffffffff);
ret = request_irq(ch->irq, sh_tmu_interrupt,
IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING,
dev_name(&ch->tmu->pdev->dev), ch);
if (ret) {
dev_err(&ch->tmu->pdev->dev, "ch%u: failed to request irq %d\n",
ch->index, ch->irq);
return;
}
}
static int sh_tmu_register(struct sh_tmu_channel *ch, const char *name,
bool clockevent, bool clocksource)
{
if (clockevent) {
ch->tmu->has_clockevent = true;
sh_tmu_register_clockevent(ch, name);
} else if (clocksource) {
ch->tmu->has_clocksource = true;
sh_tmu_register_clocksource(ch, name);
}
return 0;
}
static int sh_tmu_channel_setup(struct sh_tmu_channel *ch, unsigned int index,
bool clockevent, bool clocksource,
struct sh_tmu_device *tmu)
{
/* Skip unused channels. */
if (!clockevent && !clocksource)
return 0;
ch->tmu = tmu;
ch->index = index;
if (tmu->model == SH_TMU_SH3)
ch->base = tmu->mapbase + 4 + ch->index * 12;
else
ch->base = tmu->mapbase + 8 + ch->index * 12;
ch->irq = platform_get_irq(tmu->pdev, index);
if (ch->irq < 0) {
dev_err(&tmu->pdev->dev, "ch%u: failed to get irq\n",
ch->index);
return ch->irq;
}
ch->cs_enabled = false;
ch->enable_count = 0;
return sh_tmu_register(ch, dev_name(&tmu->pdev->dev),
clockevent, clocksource);
}
static int sh_tmu_map_memory(struct sh_tmu_device *tmu)
{
struct resource *res;
res = platform_get_resource(tmu->pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&tmu->pdev->dev, "failed to get I/O memory\n");
return -ENXIO;
}
tmu->mapbase = ioremap_nocache(res->start, resource_size(res));
if (tmu->mapbase == NULL)
return -ENXIO;
return 0;
}
static int sh_tmu_parse_dt(struct sh_tmu_device *tmu)
{
struct device_node *np = tmu->pdev->dev.of_node;
tmu->model = SH_TMU;
tmu->num_channels = 3;
of_property_read_u32(np, "#renesas,channels", &tmu->num_channels);
if (tmu->num_channels != 2 && tmu->num_channels != 3) {
dev_err(&tmu->pdev->dev, "invalid number of channels %u\n",
tmu->num_channels);
return -EINVAL;
}
return 0;
}
static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev)
{
unsigned int i;
int ret;
tmu->pdev = pdev;
raw_spin_lock_init(&tmu->lock);
if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
ret = sh_tmu_parse_dt(tmu);
if (ret < 0)
return ret;
} else if (pdev->dev.platform_data) {
const struct platform_device_id *id = pdev->id_entry;
struct sh_timer_config *cfg = pdev->dev.platform_data;
tmu->model = id->driver_data;
tmu->num_channels = hweight8(cfg->channels_mask);
} else {
dev_err(&tmu->pdev->dev, "missing platform data\n");
return -ENXIO;
}
/* Get hold of clock. */
tmu->clk = clk_get(&tmu->pdev->dev, "fck");
if (IS_ERR(tmu->clk)) {
dev_err(&tmu->pdev->dev, "cannot get clock\n");
return PTR_ERR(tmu->clk);
}
ret = clk_prepare(tmu->clk);
if (ret < 0)
goto err_clk_put;
/* Determine clock rate. */
ret = clk_enable(tmu->clk);
if (ret < 0)
goto err_clk_unprepare;
tmu->rate = clk_get_rate(tmu->clk) / 4;
clk_disable(tmu->clk);
/* Map the memory resource. */
ret = sh_tmu_map_memory(tmu);
if (ret < 0) {
dev_err(&tmu->pdev->dev, "failed to remap I/O memory\n");
goto err_clk_unprepare;
}
/* Allocate and setup the channels. */
tmu->channels = kcalloc(tmu->num_channels, sizeof(*tmu->channels),
GFP_KERNEL);
if (tmu->channels == NULL) {
ret = -ENOMEM;
goto err_unmap;
}
/*
* Use the first channel as a clock event device and the second channel
* as a clock source.
*/
for (i = 0; i < tmu->num_channels; ++i) {
ret = sh_tmu_channel_setup(&tmu->channels[i], i,
i == 0, i == 1, tmu);
if (ret < 0)
goto err_unmap;
}
platform_set_drvdata(pdev, tmu);
return 0;
err_unmap:
kfree(tmu->channels);
iounmap(tmu->mapbase);
err_clk_unprepare:
clk_unprepare(tmu->clk);
err_clk_put:
clk_put(tmu->clk);
return ret;
}
static int sh_tmu_probe(struct platform_device *pdev)
{
struct sh_tmu_device *tmu = platform_get_drvdata(pdev);
int ret;
if (!is_early_platform_device(pdev)) {
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
}
if (tmu) {
dev_info(&pdev->dev, "kept as earlytimer\n");
goto out;
}
tmu = kzalloc(sizeof(*tmu), GFP_KERNEL);
if (tmu == NULL)
return -ENOMEM;
ret = sh_tmu_setup(tmu, pdev);
if (ret) {
kfree(tmu);
pm_runtime_idle(&pdev->dev);
return ret;
}
if (is_early_platform_device(pdev))
return 0;
out:
if (tmu->has_clockevent || tmu->has_clocksource)
pm_runtime_irq_safe(&pdev->dev);
else
pm_runtime_idle(&pdev->dev);
return 0;
}
static int sh_tmu_remove(struct platform_device *pdev)
{
return -EBUSY; /* cannot unregister clockevent and clocksource */
}
static const struct platform_device_id sh_tmu_id_table[] = {
{ "sh-tmu", SH_TMU },
{ "sh-tmu-sh3", SH_TMU_SH3 },
{ }
};
MODULE_DEVICE_TABLE(platform, sh_tmu_id_table);
static const struct of_device_id sh_tmu_of_table[] __maybe_unused = {
{ .compatible = "renesas,tmu" },
{ }
};
MODULE_DEVICE_TABLE(of, sh_tmu_of_table);
static struct platform_driver sh_tmu_device_driver = {
.probe = sh_tmu_probe,
.remove = sh_tmu_remove,
.driver = {
.name = "sh_tmu",
.of_match_table = of_match_ptr(sh_tmu_of_table),
},
.id_table = sh_tmu_id_table,
};
static int __init sh_tmu_init(void)
{
return platform_driver_register(&sh_tmu_device_driver);
}
static void __exit sh_tmu_exit(void)
{
platform_driver_unregister(&sh_tmu_device_driver);
}
early_platform_init("earlytimer", &sh_tmu_device_driver);
subsys_initcall(sh_tmu_init);
module_exit(sh_tmu_exit);
MODULE_AUTHOR("Magnus Damm");
MODULE_DESCRIPTION("SuperH TMU Timer Driver");
MODULE_LICENSE("GPL v2");
+220
View File
@@ -0,0 +1,220 @@
/*
* Allwinner A1X SoCs timer handling.
*
* Copyright (C) 2012 Maxime Ripard
*
* Maxime Ripard <maxime.ripard@free-electrons.com>
*
* Based on code from
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* Benn Huang <benn@allwinnertech.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqreturn.h>
#include <linux/sched_clock.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include "timer-of.h"
#define TIMER_IRQ_EN_REG 0x00
#define TIMER_IRQ_EN(val) BIT(val)
#define TIMER_IRQ_ST_REG 0x04
#define TIMER_CTL_REG(val) (0x10 * val + 0x10)
#define TIMER_CTL_ENABLE BIT(0)
#define TIMER_CTL_RELOAD BIT(1)
#define TIMER_CTL_CLK_SRC(val) (((val) & 0x3) << 2)
#define TIMER_CTL_CLK_SRC_OSC24M (1)
#define TIMER_CTL_CLK_PRES(val) (((val) & 0x7) << 4)
#define TIMER_CTL_ONESHOT BIT(7)
#define TIMER_INTVAL_REG(val) (0x10 * (val) + 0x14)
#define TIMER_CNTVAL_REG(val) (0x10 * (val) + 0x18)
#define TIMER_SYNC_TICKS 3
/*
* When we disable a timer, we need to wait at least for 2 cycles of
* the timer source clock. We will use for that the clocksource timer
* that is already setup and runs at the same frequency than the other
* timers, and we never will be disabled.
*/
static void sun4i_clkevt_sync(void __iomem *base)
{
u32 old = readl(base + TIMER_CNTVAL_REG(1));
while ((old - readl(base + TIMER_CNTVAL_REG(1))) < TIMER_SYNC_TICKS)
cpu_relax();
}
static void sun4i_clkevt_time_stop(void __iomem *base, u8 timer)
{
u32 val = readl(base + TIMER_CTL_REG(timer));
writel(val & ~TIMER_CTL_ENABLE, base + TIMER_CTL_REG(timer));
sun4i_clkevt_sync(base);
}
static void sun4i_clkevt_time_setup(void __iomem *base, u8 timer,
unsigned long delay)
{
writel(delay, base + TIMER_INTVAL_REG(timer));
}
static void sun4i_clkevt_time_start(void __iomem *base, u8 timer,
bool periodic)
{
u32 val = readl(base + TIMER_CTL_REG(timer));
if (periodic)
val &= ~TIMER_CTL_ONESHOT;
else
val |= TIMER_CTL_ONESHOT;
writel(val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD,
base + TIMER_CTL_REG(timer));
}
static int sun4i_clkevt_shutdown(struct clock_event_device *evt)
{
struct timer_of *to = to_timer_of(evt);
sun4i_clkevt_time_stop(timer_of_base(to), 0);
return 0;
}
static int sun4i_clkevt_set_oneshot(struct clock_event_device *evt)
{
struct timer_of *to = to_timer_of(evt);
sun4i_clkevt_time_stop(timer_of_base(to), 0);
sun4i_clkevt_time_start(timer_of_base(to), 0, false);
return 0;
}
static int sun4i_clkevt_set_periodic(struct clock_event_device *evt)
{
struct timer_of *to = to_timer_of(evt);
sun4i_clkevt_time_stop(timer_of_base(to), 0);
sun4i_clkevt_time_setup(timer_of_base(to), 0, timer_of_period(to));
sun4i_clkevt_time_start(timer_of_base(to), 0, true);
return 0;
}
static int sun4i_clkevt_next_event(unsigned long evt,
struct clock_event_device *clkevt)
{
struct timer_of *to = to_timer_of(clkevt);
sun4i_clkevt_time_stop(timer_of_base(to), 0);
sun4i_clkevt_time_setup(timer_of_base(to), 0, evt - TIMER_SYNC_TICKS);
sun4i_clkevt_time_start(timer_of_base(to), 0, false);
return 0;
}
static void sun4i_timer_clear_interrupt(void __iomem *base)
{
writel(TIMER_IRQ_EN(0), base + TIMER_IRQ_ST_REG);
}
static irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = (struct clock_event_device *)dev_id;
struct timer_of *to = to_timer_of(evt);
sun4i_timer_clear_interrupt(timer_of_base(to));
evt->event_handler(evt);
return IRQ_HANDLED;
}
static struct timer_of to = {
.flags = TIMER_OF_IRQ | TIMER_OF_CLOCK | TIMER_OF_BASE,
.clkevt = {
.name = "sun4i_tick",
.rating = 350,
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
.set_state_shutdown = sun4i_clkevt_shutdown,
.set_state_periodic = sun4i_clkevt_set_periodic,
.set_state_oneshot = sun4i_clkevt_set_oneshot,
.tick_resume = sun4i_clkevt_shutdown,
.set_next_event = sun4i_clkevt_next_event,
.cpumask = cpu_possible_mask,
},
.of_irq = {
.handler = sun4i_timer_interrupt,
.flags = IRQF_TIMER | IRQF_IRQPOLL,
},
};
static u64 notrace sun4i_timer_sched_read(void)
{
return ~readl(timer_of_base(&to) + TIMER_CNTVAL_REG(1));
}
static int __init sun4i_timer_init(struct device_node *node)
{
int ret;
u32 val;
ret = timer_of_init(node, &to);
if (ret)
return ret;
writel(~0, timer_of_base(&to) + TIMER_INTVAL_REG(1));
writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD |
TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M),
timer_of_base(&to) + TIMER_CTL_REG(1));
/*
* sched_clock_register does not have priorities, and on sun6i and
* later there is a better sched_clock registered by arm_arch_timer.c
*/
if (of_machine_is_compatible("allwinner,sun4i-a10") ||
of_machine_is_compatible("allwinner,sun5i-a13") ||
of_machine_is_compatible("allwinner,sun5i-a10s"))
sched_clock_register(sun4i_timer_sched_read, 32,
timer_of_rate(&to));
ret = clocksource_mmio_init(timer_of_base(&to) + TIMER_CNTVAL_REG(1),
node->name, timer_of_rate(&to), 350, 32,
clocksource_mmio_readl_down);
if (ret) {
pr_err("Failed to register clocksource\n");
return ret;
}
writel(TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M),
timer_of_base(&to) + TIMER_CTL_REG(0));
/* Make sure timer is stopped before playing with interrupts */
sun4i_clkevt_time_stop(timer_of_base(&to), 0);
/* clear timer0 interrupt */
sun4i_timer_clear_interrupt(timer_of_base(&to));
clockevents_config_and_register(&to.clkevt, timer_of_rate(&to),
TIMER_SYNC_TICKS, 0xffffffff);
/* Enable timer0 interrupt */
val = readl(timer_of_base(&to) + TIMER_IRQ_EN_REG);
writel(val | TIMER_IRQ_EN(0), timer_of_base(&to) + TIMER_IRQ_EN_REG);
return ret;
}
TIMER_OF_DECLARE(sun4i, "allwinner,sun4i-a10-timer",
sun4i_timer_init);
+57
View File
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/clocksource.h>
#include <linux/sched_clock.h>
#include <linux/of_address.h>
#include <linux/printk.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/clk.h>
static void __iomem *xtal_in_cnt;
static struct delay_timer delay_timer;
static unsigned long notrace read_xtal_counter(void)
{
return readl_relaxed(xtal_in_cnt);
}
static u64 notrace read_sched_clock(void)
{
return read_xtal_counter();
}
static int __init tango_clocksource_init(struct device_node *np)
{
struct clk *clk;
int xtal_freq, ret;
xtal_in_cnt = of_iomap(np, 0);
if (xtal_in_cnt == NULL) {
pr_err("%pOF: invalid address\n", np);
return -ENXIO;
}
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
pr_err("%pOF: invalid clock\n", np);
return PTR_ERR(clk);
}
xtal_freq = clk_get_rate(clk);
delay_timer.freq = xtal_freq;
delay_timer.read_current_timer = read_xtal_counter;
ret = clocksource_mmio_init(xtal_in_cnt, "tango-xtal", xtal_freq, 350,
32, clocksource_mmio_readl_up);
if (ret) {
pr_err("%pOF: registration failed\n", np);
return ret;
}
sched_clock_register(read_sched_clock, 32, xtal_freq);
register_current_timer_delay(&delay_timer);
return 0;
}
TIMER_OF_DECLARE(tango, "sigma,tick-counter", tango_clocksource_init);
+433
View File
@@ -0,0 +1,433 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/init.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/syscore_ops.h>
#include <linux/atmel_tc.h>
/*
* We're configured to use a specific TC block, one that's not hooked
* up to external hardware, to provide a time solution:
*
* - Two channels combine to create a free-running 32 bit counter
* with a base rate of 5+ MHz, packaged as a clocksource (with
* resolution better than 200 nsec).
* - Some chips support 32 bit counter. A single channel is used for
* this 32 bit free-running counter. the second channel is not used.
*
* - The third channel may be used to provide a 16-bit clockevent
* source, used in either periodic or oneshot mode. This runs
* at 32 KiHZ, and can handle delays of up to two seconds.
*
* A boot clocksource and clockevent source are also currently needed,
* unless the relevant platforms (ARM/AT91, AVR32/AT32) are changed so
* this code can be used when init_timers() is called, well before most
* devices are set up. (Some low end AT91 parts, which can run uClinux,
* have only the timers in one TC block... they currently don't support
* the tclib code, because of that initialization issue.)
*
* REVISIT behavior during system suspend states... we should disable
* all clocks and save the power. Easily done for clockevent devices,
* but clocksources won't necessarily get the needed notifications.
* For deeper system sleep states, this will be mandatory...
*/
static void __iomem *tcaddr;
static struct
{
u32 cmr;
u32 imr;
u32 rc;
bool clken;
} tcb_cache[3];
static u32 bmr_cache;
static u64 tc_get_cycles(struct clocksource *cs)
{
unsigned long flags;
u32 lower, upper;
raw_local_irq_save(flags);
do {
upper = readl_relaxed(tcaddr + ATMEL_TC_REG(1, CV));
lower = readl_relaxed(tcaddr + ATMEL_TC_REG(0, CV));
} while (upper != readl_relaxed(tcaddr + ATMEL_TC_REG(1, CV)));
raw_local_irq_restore(flags);
return (upper << 16) | lower;
}
static u64 tc_get_cycles32(struct clocksource *cs)
{
return readl_relaxed(tcaddr + ATMEL_TC_REG(0, CV));
}
void tc_clksrc_suspend(struct clocksource *cs)
{
int i;
for (i = 0; i < ARRAY_SIZE(tcb_cache); i++) {
tcb_cache[i].cmr = readl(tcaddr + ATMEL_TC_REG(i, CMR));
tcb_cache[i].imr = readl(tcaddr + ATMEL_TC_REG(i, IMR));
tcb_cache[i].rc = readl(tcaddr + ATMEL_TC_REG(i, RC));
tcb_cache[i].clken = !!(readl(tcaddr + ATMEL_TC_REG(i, SR)) &
ATMEL_TC_CLKSTA);
}
bmr_cache = readl(tcaddr + ATMEL_TC_BMR);
}
void tc_clksrc_resume(struct clocksource *cs)
{
int i;
for (i = 0; i < ARRAY_SIZE(tcb_cache); i++) {
/* Restore registers for the channel, RA and RB are not used */
writel(tcb_cache[i].cmr, tcaddr + ATMEL_TC_REG(i, CMR));
writel(tcb_cache[i].rc, tcaddr + ATMEL_TC_REG(i, RC));
writel(0, tcaddr + ATMEL_TC_REG(i, RA));
writel(0, tcaddr + ATMEL_TC_REG(i, RB));
/* Disable all the interrupts */
writel(0xff, tcaddr + ATMEL_TC_REG(i, IDR));
/* Reenable interrupts that were enabled before suspending */
writel(tcb_cache[i].imr, tcaddr + ATMEL_TC_REG(i, IER));
/* Start the clock if it was used */
if (tcb_cache[i].clken)
writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(i, CCR));
}
/* Dual channel, chain channels */
writel(bmr_cache, tcaddr + ATMEL_TC_BMR);
/* Finally, trigger all the channels*/
writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR);
}
static struct clocksource clksrc = {
.name = "tcb_clksrc",
.rating = 200,
.read = tc_get_cycles,
.mask = CLOCKSOURCE_MASK(32),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
.suspend = tc_clksrc_suspend,
.resume = tc_clksrc_resume,
};
#ifdef CONFIG_GENERIC_CLOCKEVENTS
struct tc_clkevt_device {
struct clock_event_device clkevt;
struct clk *clk;
void __iomem *regs;
};
static struct tc_clkevt_device *to_tc_clkevt(struct clock_event_device *clkevt)
{
return container_of(clkevt, struct tc_clkevt_device, clkevt);
}
/* For now, we always use the 32K clock ... this optimizes for NO_HZ,
* because using one of the divided clocks would usually mean the
* tick rate can never be less than several dozen Hz (vs 0.5 Hz).
*
* A divided clock could be good for high resolution timers, since
* 30.5 usec resolution can seem "low".
*/
static u32 timer_clock;
static int tc_shutdown(struct clock_event_device *d)
{
struct tc_clkevt_device *tcd = to_tc_clkevt(d);
void __iomem *regs = tcd->regs;
writel(0xff, regs + ATMEL_TC_REG(2, IDR));
writel(ATMEL_TC_CLKDIS, regs + ATMEL_TC_REG(2, CCR));
if (!clockevent_state_detached(d))
clk_disable(tcd->clk);
return 0;
}
static int tc_set_oneshot(struct clock_event_device *d)
{
struct tc_clkevt_device *tcd = to_tc_clkevt(d);
void __iomem *regs = tcd->regs;
if (clockevent_state_oneshot(d) || clockevent_state_periodic(d))
tc_shutdown(d);
clk_enable(tcd->clk);
/* slow clock, count up to RC, then irq and stop */
writel(timer_clock | ATMEL_TC_CPCSTOP | ATMEL_TC_WAVE |
ATMEL_TC_WAVESEL_UP_AUTO, regs + ATMEL_TC_REG(2, CMR));
writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER));
/* set_next_event() configures and starts the timer */
return 0;
}
static int tc_set_periodic(struct clock_event_device *d)
{
struct tc_clkevt_device *tcd = to_tc_clkevt(d);
void __iomem *regs = tcd->regs;
if (clockevent_state_oneshot(d) || clockevent_state_periodic(d))
tc_shutdown(d);
/* By not making the gentime core emulate periodic mode on top
* of oneshot, we get lower overhead and improved accuracy.
*/
clk_enable(tcd->clk);
/* slow clock, count up to RC, then irq and restart */
writel(timer_clock | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO,
regs + ATMEL_TC_REG(2, CMR));
writel((32768 + HZ / 2) / HZ, tcaddr + ATMEL_TC_REG(2, RC));
/* Enable clock and interrupts on RC compare */
writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER));
/* go go gadget! */
writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG, regs +
ATMEL_TC_REG(2, CCR));
return 0;
}
static int tc_next_event(unsigned long delta, struct clock_event_device *d)
{
writel_relaxed(delta, tcaddr + ATMEL_TC_REG(2, RC));
/* go go gadget! */
writel_relaxed(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
tcaddr + ATMEL_TC_REG(2, CCR));
return 0;
}
static struct tc_clkevt_device clkevt = {
.clkevt = {
.name = "tc_clkevt",
.features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT,
/* Should be lower than at91rm9200's system timer */
.rating = 125,
.set_next_event = tc_next_event,
.set_state_shutdown = tc_shutdown,
.set_state_periodic = tc_set_periodic,
.set_state_oneshot = tc_set_oneshot,
},
};
static irqreturn_t ch2_irq(int irq, void *handle)
{
struct tc_clkevt_device *dev = handle;
unsigned int sr;
sr = readl_relaxed(dev->regs + ATMEL_TC_REG(2, SR));
if (sr & ATMEL_TC_CPCS) {
dev->clkevt.event_handler(&dev->clkevt);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx)
{
int ret;
struct clk *t2_clk = tc->clk[2];
int irq = tc->irq[2];
ret = clk_prepare_enable(tc->slow_clk);
if (ret)
return ret;
/* try to enable t2 clk to avoid future errors in mode change */
ret = clk_prepare_enable(t2_clk);
if (ret) {
clk_disable_unprepare(tc->slow_clk);
return ret;
}
clk_disable(t2_clk);
clkevt.regs = tc->regs;
clkevt.clk = t2_clk;
timer_clock = clk32k_divisor_idx;
clkevt.clkevt.cpumask = cpumask_of(0);
ret = request_irq(irq, ch2_irq, IRQF_TIMER, "tc_clkevt", &clkevt);
if (ret) {
clk_unprepare(t2_clk);
clk_disable_unprepare(tc->slow_clk);
return ret;
}
clockevents_config_and_register(&clkevt.clkevt, 32768, 1, 0xffff);
return ret;
}
#else /* !CONFIG_GENERIC_CLOCKEVENTS */
static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx)
{
/* NOTHING */
return 0;
}
#endif
static void __init tcb_setup_dual_chan(struct atmel_tc *tc, int mck_divisor_idx)
{
/* channel 0: waveform mode, input mclk/8, clock TIOA0 on overflow */
writel(mck_divisor_idx /* likely divide-by-8 */
| ATMEL_TC_WAVE
| ATMEL_TC_WAVESEL_UP /* free-run */
| ATMEL_TC_ACPA_SET /* TIOA0 rises at 0 */
| ATMEL_TC_ACPC_CLEAR, /* (duty cycle 50%) */
tcaddr + ATMEL_TC_REG(0, CMR));
writel(0x0000, tcaddr + ATMEL_TC_REG(0, RA));
writel(0x8000, tcaddr + ATMEL_TC_REG(0, RC));
writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */
writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR));
/* channel 1: waveform mode, input TIOA0 */
writel(ATMEL_TC_XC1 /* input: TIOA0 */
| ATMEL_TC_WAVE
| ATMEL_TC_WAVESEL_UP, /* free-run */
tcaddr + ATMEL_TC_REG(1, CMR));
writel(0xff, tcaddr + ATMEL_TC_REG(1, IDR)); /* no irqs */
writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(1, CCR));
/* chain channel 0 to channel 1*/
writel(ATMEL_TC_TC1XC1S_TIOA0, tcaddr + ATMEL_TC_BMR);
/* then reset all the timers */
writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR);
}
static void __init tcb_setup_single_chan(struct atmel_tc *tc, int mck_divisor_idx)
{
/* channel 0: waveform mode, input mclk/8 */
writel(mck_divisor_idx /* likely divide-by-8 */
| ATMEL_TC_WAVE
| ATMEL_TC_WAVESEL_UP, /* free-run */
tcaddr + ATMEL_TC_REG(0, CMR));
writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */
writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR));
/* then reset all the timers */
writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR);
}
static int __init tcb_clksrc_init(void)
{
static char bootinfo[] __initdata
= KERN_DEBUG "%s: tc%d at %d.%03d MHz\n";
struct platform_device *pdev;
struct atmel_tc *tc;
struct clk *t0_clk;
u32 rate, divided_rate = 0;
int best_divisor_idx = -1;
int clk32k_divisor_idx = -1;
int i;
int ret;
tc = atmel_tc_alloc(CONFIG_ATMEL_TCB_CLKSRC_BLOCK);
if (!tc) {
pr_debug("can't alloc TC for clocksource\n");
return -ENODEV;
}
tcaddr = tc->regs;
pdev = tc->pdev;
t0_clk = tc->clk[0];
ret = clk_prepare_enable(t0_clk);
if (ret) {
pr_debug("can't enable T0 clk\n");
goto err_free_tc;
}
/* How fast will we be counting? Pick something over 5 MHz. */
rate = (u32) clk_get_rate(t0_clk);
for (i = 0; i < 5; i++) {
unsigned divisor = atmel_tc_divisors[i];
unsigned tmp;
/* remember 32 KiHz clock for later */
if (!divisor) {
clk32k_divisor_idx = i;
continue;
}
tmp = rate / divisor;
pr_debug("TC: %u / %-3u [%d] --> %u\n", rate, divisor, i, tmp);
if (best_divisor_idx > 0) {
if (tmp < 5 * 1000 * 1000)
continue;
}
divided_rate = tmp;
best_divisor_idx = i;
}
printk(bootinfo, clksrc.name, CONFIG_ATMEL_TCB_CLKSRC_BLOCK,
divided_rate / 1000000,
((divided_rate % 1000000) + 500) / 1000);
if (tc->tcb_config && tc->tcb_config->counter_width == 32) {
/* use apropriate function to read 32 bit counter */
clksrc.read = tc_get_cycles32;
/* setup ony channel 0 */
tcb_setup_single_chan(tc, best_divisor_idx);
} else {
/* tclib will give us three clocks no matter what the
* underlying platform supports.
*/
ret = clk_prepare_enable(tc->clk[1]);
if (ret) {
pr_debug("can't enable T1 clk\n");
goto err_disable_t0;
}
/* setup both channel 0 & 1 */
tcb_setup_dual_chan(tc, best_divisor_idx);
}
/* and away we go! */
ret = clocksource_register_hz(&clksrc, divided_rate);
if (ret)
goto err_disable_t1;
/* channel 2: periodic and oneshot timer support */
ret = setup_clkevents(tc, clk32k_divisor_idx);
if (ret)
goto err_unregister_clksrc;
return 0;
err_unregister_clksrc:
clocksource_unregister(&clksrc);
err_disable_t1:
if (!tc->tcb_config || tc->tcb_config->counter_width != 32)
clk_disable_unprepare(tc->clk[1]);
err_disable_t0:
clk_disable_unprepare(t0_clk);
err_free_tc:
atmel_tc_free(tc);
return ret;
}
arch_initcall(tcb_clksrc_init);
+264
View File
@@ -0,0 +1,264 @@
/*
* Copyright (C) 2010 Google, Inc.
*
* Author:
* Colin Cross <ccross@google.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/init.h>
#include <linux/err.h>
#include <linux/time.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/sched_clock.h>
#include <linux/delay.h>
#include <asm/mach/time.h>
#include <asm/smp_twd.h>
#define RTC_SECONDS 0x08
#define RTC_SHADOW_SECONDS 0x0c
#define RTC_MILLISECONDS 0x10
#define TIMERUS_CNTR_1US 0x10
#define TIMERUS_USEC_CFG 0x14
#define TIMERUS_CNTR_FREEZE 0x4c
#define TIMER1_BASE 0x0
#define TIMER2_BASE 0x8
#define TIMER3_BASE 0x50
#define TIMER4_BASE 0x58
#define TIMER_PTV 0x0
#define TIMER_PCR 0x4
static void __iomem *timer_reg_base;
static void __iomem *rtc_base;
static struct timespec64 persistent_ts;
static u64 persistent_ms, last_persistent_ms;
static struct delay_timer tegra_delay_timer;
#define timer_writel(value, reg) \
writel_relaxed(value, timer_reg_base + (reg))
#define timer_readl(reg) \
readl_relaxed(timer_reg_base + (reg))
static int tegra_timer_set_next_event(unsigned long cycles,
struct clock_event_device *evt)
{
u32 reg;
reg = 0x80000000 | ((cycles > 1) ? (cycles-1) : 0);
timer_writel(reg, TIMER3_BASE + TIMER_PTV);
return 0;
}
static inline void timer_shutdown(struct clock_event_device *evt)
{
timer_writel(0, TIMER3_BASE + TIMER_PTV);
}
static int tegra_timer_shutdown(struct clock_event_device *evt)
{
timer_shutdown(evt);
return 0;
}
static int tegra_timer_set_periodic(struct clock_event_device *evt)
{
u32 reg = 0xC0000000 | ((1000000 / HZ) - 1);
timer_shutdown(evt);
timer_writel(reg, TIMER3_BASE + TIMER_PTV);
return 0;
}
static struct clock_event_device tegra_clockevent = {
.name = "timer0",
.rating = 300,
.features = CLOCK_EVT_FEAT_ONESHOT |
CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_DYNIRQ,
.set_next_event = tegra_timer_set_next_event,
.set_state_shutdown = tegra_timer_shutdown,
.set_state_periodic = tegra_timer_set_periodic,
.set_state_oneshot = tegra_timer_shutdown,
.tick_resume = tegra_timer_shutdown,
};
static u64 notrace tegra_read_sched_clock(void)
{
return timer_readl(TIMERUS_CNTR_1US);
}
/*
* tegra_rtc_read - Reads the Tegra RTC registers
* Care must be taken that this funciton is not called while the
* tegra_rtc driver could be executing to avoid race conditions
* on the RTC shadow register
*/
static u64 tegra_rtc_read_ms(void)
{
u32 ms = readl(rtc_base + RTC_MILLISECONDS);
u32 s = readl(rtc_base + RTC_SHADOW_SECONDS);
return (u64)s * MSEC_PER_SEC + ms;
}
/*
* tegra_read_persistent_clock64 - Return time from a persistent clock.
*
* Reads the time from a source which isn't disabled during PM, the
* 32k sync timer. Convert the cycles elapsed since last read into
* nsecs and adds to a monotonically increasing timespec64.
* Care must be taken that this funciton is not called while the
* tegra_rtc driver could be executing to avoid race conditions
* on the RTC shadow register
*/
static void tegra_read_persistent_clock64(struct timespec64 *ts)
{
u64 delta;
last_persistent_ms = persistent_ms;
persistent_ms = tegra_rtc_read_ms();
delta = persistent_ms - last_persistent_ms;
timespec64_add_ns(&persistent_ts, delta * NSEC_PER_MSEC);
*ts = persistent_ts;
}
static unsigned long tegra_delay_timer_read_counter_long(void)
{
return readl(timer_reg_base + TIMERUS_CNTR_1US);
}
static irqreturn_t tegra_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = (struct clock_event_device *)dev_id;
timer_writel(1<<30, TIMER3_BASE + TIMER_PCR);
evt->event_handler(evt);
return IRQ_HANDLED;
}
static struct irqaction tegra_timer_irq = {
.name = "timer0",
.flags = IRQF_TIMER | IRQF_TRIGGER_HIGH,
.handler = tegra_timer_interrupt,
.dev_id = &tegra_clockevent,
};
static int __init tegra20_init_timer(struct device_node *np)
{
struct clk *clk;
unsigned long rate;
int ret;
timer_reg_base = of_iomap(np, 0);
if (!timer_reg_base) {
pr_err("Can't map timer registers\n");
return -ENXIO;
}
tegra_timer_irq.irq = irq_of_parse_and_map(np, 2);
if (tegra_timer_irq.irq <= 0) {
pr_err("Failed to map timer IRQ\n");
return -EINVAL;
}
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
pr_warn("Unable to get timer clock. Assuming 12Mhz input clock.\n");
rate = 12000000;
} else {
clk_prepare_enable(clk);
rate = clk_get_rate(clk);
}
switch (rate) {
case 12000000:
timer_writel(0x000b, TIMERUS_USEC_CFG);
break;
case 13000000:
timer_writel(0x000c, TIMERUS_USEC_CFG);
break;
case 19200000:
timer_writel(0x045f, TIMERUS_USEC_CFG);
break;
case 26000000:
timer_writel(0x0019, TIMERUS_USEC_CFG);
break;
default:
WARN(1, "Unknown clock rate");
}
sched_clock_register(tegra_read_sched_clock, 32, 1000000);
ret = clocksource_mmio_init(timer_reg_base + TIMERUS_CNTR_1US,
"timer_us", 1000000, 300, 32,
clocksource_mmio_readl_up);
if (ret) {
pr_err("Failed to register clocksource\n");
return ret;
}
tegra_delay_timer.read_current_timer =
tegra_delay_timer_read_counter_long;
tegra_delay_timer.freq = 1000000;
register_current_timer_delay(&tegra_delay_timer);
ret = setup_irq(tegra_timer_irq.irq, &tegra_timer_irq);
if (ret) {
pr_err("Failed to register timer IRQ: %d\n", ret);
return ret;
}
tegra_clockevent.cpumask = cpu_possible_mask;
tegra_clockevent.irq = tegra_timer_irq.irq;
clockevents_config_and_register(&tegra_clockevent, 1000000,
0x1, 0x1fffffff);
return 0;
}
TIMER_OF_DECLARE(tegra20_timer, "nvidia,tegra20-timer", tegra20_init_timer);
static int __init tegra20_init_rtc(struct device_node *np)
{
struct clk *clk;
rtc_base = of_iomap(np, 0);
if (!rtc_base) {
pr_err("Can't map RTC registers\n");
return -ENXIO;
}
/*
* rtc registers are used by read_persistent_clock, keep the rtc clock
* enabled
*/
clk = of_clk_get(np, 0);
if (IS_ERR(clk))
pr_warn("Unable to get rtc-tegra clock\n");
else
clk_prepare_enable(clk);
return register_persistent_clock(tegra_read_persistent_clock64);
}
TIMER_OF_DECLARE(tegra20_rtc, "nvidia,tegra20-rtc", tegra20_init_rtc);
+416
View File
@@ -0,0 +1,416 @@
/*
* Marvell Armada 370/XP SoC timer handling.
*
* Copyright (C) 2012 Marvell
*
* Lior Amsalem <alior@marvell.com>
* Gregory CLEMENT <gregory.clement@free-electrons.com>
* Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*
* Timer 0 is used as free-running clocksource, while timer 1 is
* used as clock_event_device.
*
* ---
* Clocksource driver for Armada 370 and Armada XP SoC.
* This driver implements one compatible string for each SoC, given
* each has its own characteristics:
*
* * Armada 370 has no 25 MHz fixed timer.
*
* * Armada XP cannot work properly without such 25 MHz fixed timer as
* doing otherwise leads to using a clocksource whose frequency varies
* when doing cpufreq frequency changes.
*
* See Documentation/devicetree/bindings/timer/marvell,armada-370-xp-timer.txt
*/
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/cpu.h>
#include <linux/timer.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/sched_clock.h>
#include <linux/percpu.h>
#include <linux/syscore_ops.h>
#include <asm/delay.h>
/*
* Timer block registers.
*/
#define TIMER_CTRL_OFF 0x0000
#define TIMER0_EN BIT(0)
#define TIMER0_RELOAD_EN BIT(1)
#define TIMER0_25MHZ BIT(11)
#define TIMER0_DIV(div) ((div) << 19)
#define TIMER1_EN BIT(2)
#define TIMER1_RELOAD_EN BIT(3)
#define TIMER1_25MHZ BIT(12)
#define TIMER1_DIV(div) ((div) << 22)
#define TIMER_EVENTS_STATUS 0x0004
#define TIMER0_CLR_MASK (~0x1)
#define TIMER1_CLR_MASK (~0x100)
#define TIMER0_RELOAD_OFF 0x0010
#define TIMER0_VAL_OFF 0x0014
#define TIMER1_RELOAD_OFF 0x0018
#define TIMER1_VAL_OFF 0x001c
#define LCL_TIMER_EVENTS_STATUS 0x0028
/* Global timers are connected to the coherency fabric clock, and the
below divider reduces their incrementing frequency. */
#define TIMER_DIVIDER_SHIFT 5
#define TIMER_DIVIDER (1 << TIMER_DIVIDER_SHIFT)
/*
* SoC-specific data.
*/
static void __iomem *timer_base, *local_base;
static unsigned int timer_clk;
static bool timer25Mhz = true;
static u32 enable_mask;
/*
* Number of timer ticks per jiffy.
*/
static u32 ticks_per_jiffy;
static struct clock_event_device __percpu *armada_370_xp_evt;
static void local_timer_ctrl_clrset(u32 clr, u32 set)
{
writel((readl(local_base + TIMER_CTRL_OFF) & ~clr) | set,
local_base + TIMER_CTRL_OFF);
}
static u64 notrace armada_370_xp_read_sched_clock(void)
{
return ~readl(timer_base + TIMER0_VAL_OFF);
}
/*
* Clockevent handling.
*/
static int
armada_370_xp_clkevt_next_event(unsigned long delta,
struct clock_event_device *dev)
{
/*
* Clear clockevent timer interrupt.
*/
writel(TIMER0_CLR_MASK, local_base + LCL_TIMER_EVENTS_STATUS);
/*
* Setup new clockevent timer value.
*/
writel(delta, local_base + TIMER0_VAL_OFF);
/*
* Enable the timer.
*/
local_timer_ctrl_clrset(TIMER0_RELOAD_EN, enable_mask);
return 0;
}
static int armada_370_xp_clkevt_shutdown(struct clock_event_device *evt)
{
/*
* Disable timer.
*/
local_timer_ctrl_clrset(TIMER0_EN, 0);
/*
* ACK pending timer interrupt.
*/
writel(TIMER0_CLR_MASK, local_base + LCL_TIMER_EVENTS_STATUS);
return 0;
}
static int armada_370_xp_clkevt_set_periodic(struct clock_event_device *evt)
{
/*
* Setup timer to fire at 1/HZ intervals.
*/
writel(ticks_per_jiffy - 1, local_base + TIMER0_RELOAD_OFF);
writel(ticks_per_jiffy - 1, local_base + TIMER0_VAL_OFF);
/*
* Enable timer.
*/
local_timer_ctrl_clrset(0, TIMER0_RELOAD_EN | enable_mask);
return 0;
}
static int armada_370_xp_clkevt_irq;
static irqreturn_t armada_370_xp_timer_interrupt(int irq, void *dev_id)
{
/*
* ACK timer interrupt and call event handler.
*/
struct clock_event_device *evt = dev_id;
writel(TIMER0_CLR_MASK, local_base + LCL_TIMER_EVENTS_STATUS);
evt->event_handler(evt);
return IRQ_HANDLED;
}
/*
* Setup the local clock events for a CPU.
*/
static int armada_370_xp_timer_starting_cpu(unsigned int cpu)
{
struct clock_event_device *evt = per_cpu_ptr(armada_370_xp_evt, cpu);
u32 clr = 0, set = 0;
if (timer25Mhz)
set = TIMER0_25MHZ;
else
clr = TIMER0_25MHZ;
local_timer_ctrl_clrset(clr, set);
evt->name = "armada_370_xp_per_cpu_tick",
evt->features = CLOCK_EVT_FEAT_ONESHOT |
CLOCK_EVT_FEAT_PERIODIC;
evt->shift = 32,
evt->rating = 300,
evt->set_next_event = armada_370_xp_clkevt_next_event,
evt->set_state_shutdown = armada_370_xp_clkevt_shutdown;
evt->set_state_periodic = armada_370_xp_clkevt_set_periodic;
evt->set_state_oneshot = armada_370_xp_clkevt_shutdown;
evt->tick_resume = armada_370_xp_clkevt_shutdown;
evt->irq = armada_370_xp_clkevt_irq;
evt->cpumask = cpumask_of(cpu);
clockevents_config_and_register(evt, timer_clk, 1, 0xfffffffe);
enable_percpu_irq(evt->irq, 0);
return 0;
}
static int armada_370_xp_timer_dying_cpu(unsigned int cpu)
{
struct clock_event_device *evt = per_cpu_ptr(armada_370_xp_evt, cpu);
evt->set_state_shutdown(evt);
disable_percpu_irq(evt->irq);
return 0;
}
static u32 timer0_ctrl_reg, timer0_local_ctrl_reg;
static int armada_370_xp_timer_suspend(void)
{
timer0_ctrl_reg = readl(timer_base + TIMER_CTRL_OFF);
timer0_local_ctrl_reg = readl(local_base + TIMER_CTRL_OFF);
return 0;
}
static void armada_370_xp_timer_resume(void)
{
writel(0xffffffff, timer_base + TIMER0_VAL_OFF);
writel(0xffffffff, timer_base + TIMER0_RELOAD_OFF);
writel(timer0_ctrl_reg, timer_base + TIMER_CTRL_OFF);
writel(timer0_local_ctrl_reg, local_base + TIMER_CTRL_OFF);
}
static struct syscore_ops armada_370_xp_timer_syscore_ops = {
.suspend = armada_370_xp_timer_suspend,
.resume = armada_370_xp_timer_resume,
};
static unsigned long armada_370_delay_timer_read(void)
{
return ~readl(timer_base + TIMER0_VAL_OFF);
}
static struct delay_timer armada_370_delay_timer = {
.read_current_timer = armada_370_delay_timer_read,
};
static int __init armada_370_xp_timer_common_init(struct device_node *np)
{
u32 clr = 0, set = 0;
int res;
timer_base = of_iomap(np, 0);
if (!timer_base) {
pr_err("Failed to iomap\n");
return -ENXIO;
}
local_base = of_iomap(np, 1);
if (!local_base) {
pr_err("Failed to iomap\n");
return -ENXIO;
}
if (timer25Mhz) {
set = TIMER0_25MHZ;
enable_mask = TIMER0_EN;
} else {
clr = TIMER0_25MHZ;
enable_mask = TIMER0_EN | TIMER0_DIV(TIMER_DIVIDER_SHIFT);
}
atomic_io_modify(timer_base + TIMER_CTRL_OFF, clr | set, set);
local_timer_ctrl_clrset(clr, set);
/*
* We use timer 0 as clocksource, and private(local) timer 0
* for clockevents
*/
armada_370_xp_clkevt_irq = irq_of_parse_and_map(np, 4);
ticks_per_jiffy = (timer_clk + HZ / 2) / HZ;
/*
* Setup free-running clocksource timer (interrupts
* disabled).
*/
writel(0xffffffff, timer_base + TIMER0_VAL_OFF);
writel(0xffffffff, timer_base + TIMER0_RELOAD_OFF);
atomic_io_modify(timer_base + TIMER_CTRL_OFF,
TIMER0_RELOAD_EN | enable_mask,
TIMER0_RELOAD_EN | enable_mask);
armada_370_delay_timer.freq = timer_clk;
register_current_timer_delay(&armada_370_delay_timer);
/*
* Set scale and timer for sched_clock.
*/
sched_clock_register(armada_370_xp_read_sched_clock, 32, timer_clk);
res = clocksource_mmio_init(timer_base + TIMER0_VAL_OFF,
"armada_370_xp_clocksource",
timer_clk, 300, 32, clocksource_mmio_readl_down);
if (res) {
pr_err("Failed to initialize clocksource mmio\n");
return res;
}
armada_370_xp_evt = alloc_percpu(struct clock_event_device);
if (!armada_370_xp_evt)
return -ENOMEM;
/*
* Setup clockevent timer (interrupt-driven).
*/
res = request_percpu_irq(armada_370_xp_clkevt_irq,
armada_370_xp_timer_interrupt,
"armada_370_xp_per_cpu_tick",
armada_370_xp_evt);
/* Immediately configure the timer on the boot CPU */
if (res) {
pr_err("Failed to request percpu irq\n");
return res;
}
res = cpuhp_setup_state(CPUHP_AP_ARMADA_TIMER_STARTING,
"clockevents/armada:starting",
armada_370_xp_timer_starting_cpu,
armada_370_xp_timer_dying_cpu);
if (res) {
pr_err("Failed to setup hotplug state and timer\n");
return res;
}
register_syscore_ops(&armada_370_xp_timer_syscore_ops);
return 0;
}
static int __init armada_xp_timer_init(struct device_node *np)
{
struct clk *clk = of_clk_get_by_name(np, "fixed");
int ret;
if (IS_ERR(clk)) {
pr_err("Failed to get clock\n");
return PTR_ERR(clk);
}
ret = clk_prepare_enable(clk);
if (ret)
return ret;
timer_clk = clk_get_rate(clk);
return armada_370_xp_timer_common_init(np);
}
TIMER_OF_DECLARE(armada_xp, "marvell,armada-xp-timer",
armada_xp_timer_init);
static int __init armada_375_timer_init(struct device_node *np)
{
struct clk *clk;
int ret;
clk = of_clk_get_by_name(np, "fixed");
if (!IS_ERR(clk)) {
ret = clk_prepare_enable(clk);
if (ret)
return ret;
timer_clk = clk_get_rate(clk);
} else {
/*
* This fallback is required in order to retain proper
* devicetree backwards compatibility.
*/
clk = of_clk_get(np, 0);
/* Must have at least a clock */
if (IS_ERR(clk)) {
pr_err("Failed to get clock\n");
return PTR_ERR(clk);
}
ret = clk_prepare_enable(clk);
if (ret)
return ret;
timer_clk = clk_get_rate(clk) / TIMER_DIVIDER;
timer25Mhz = false;
}
return armada_370_xp_timer_common_init(np);
}
TIMER_OF_DECLARE(armada_375, "marvell,armada-375-timer",
armada_375_timer_init);
static int __init armada_370_timer_init(struct device_node *np)
{
struct clk *clk;
int ret;
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
pr_err("Failed to get clock\n");
return PTR_ERR(clk);
}
ret = clk_prepare_enable(clk);
if (ret)
return ret;
timer_clk = clk_get_rate(clk) / TIMER_DIVIDER;
timer25Mhz = false;
return armada_370_xp_timer_common_init(np);
}
TIMER_OF_DECLARE(armada_370, "marvell,armada-370-timer",
armada_370_timer_init);
+287
View File
@@ -0,0 +1,287 @@
/*
* Copyright (C) 2013 Pengutronix
* Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License version 2 as published by the
* Free Software Foundation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/clk.h>
#define TIMERn_CTRL 0x00
#define TIMERn_CTRL_PRESC(val) (((val) & 0xf) << 24)
#define TIMERn_CTRL_PRESC_1024 TIMERn_CTRL_PRESC(10)
#define TIMERn_CTRL_CLKSEL(val) (((val) & 0x3) << 16)
#define TIMERn_CTRL_CLKSEL_PRESCHFPERCLK TIMERn_CTRL_CLKSEL(0)
#define TIMERn_CTRL_OSMEN 0x00000010
#define TIMERn_CTRL_MODE(val) (((val) & 0x3) << 0)
#define TIMERn_CTRL_MODE_UP TIMERn_CTRL_MODE(0)
#define TIMERn_CTRL_MODE_DOWN TIMERn_CTRL_MODE(1)
#define TIMERn_CMD 0x04
#define TIMERn_CMD_START 0x00000001
#define TIMERn_CMD_STOP 0x00000002
#define TIMERn_IEN 0x0c
#define TIMERn_IF 0x10
#define TIMERn_IFS 0x14
#define TIMERn_IFC 0x18
#define TIMERn_IRQ_UF 0x00000002
#define TIMERn_TOP 0x1c
#define TIMERn_CNT 0x24
struct efm32_clock_event_ddata {
struct clock_event_device evtdev;
void __iomem *base;
unsigned periodic_top;
};
static int efm32_clock_event_shutdown(struct clock_event_device *evtdev)
{
struct efm32_clock_event_ddata *ddata =
container_of(evtdev, struct efm32_clock_event_ddata, evtdev);
writel_relaxed(TIMERn_CMD_STOP, ddata->base + TIMERn_CMD);
return 0;
}
static int efm32_clock_event_set_oneshot(struct clock_event_device *evtdev)
{
struct efm32_clock_event_ddata *ddata =
container_of(evtdev, struct efm32_clock_event_ddata, evtdev);
writel_relaxed(TIMERn_CMD_STOP, ddata->base + TIMERn_CMD);
writel_relaxed(TIMERn_CTRL_PRESC_1024 |
TIMERn_CTRL_CLKSEL_PRESCHFPERCLK |
TIMERn_CTRL_OSMEN |
TIMERn_CTRL_MODE_DOWN,
ddata->base + TIMERn_CTRL);
return 0;
}
static int efm32_clock_event_set_periodic(struct clock_event_device *evtdev)
{
struct efm32_clock_event_ddata *ddata =
container_of(evtdev, struct efm32_clock_event_ddata, evtdev);
writel_relaxed(TIMERn_CMD_STOP, ddata->base + TIMERn_CMD);
writel_relaxed(ddata->periodic_top, ddata->base + TIMERn_TOP);
writel_relaxed(TIMERn_CTRL_PRESC_1024 |
TIMERn_CTRL_CLKSEL_PRESCHFPERCLK |
TIMERn_CTRL_MODE_DOWN,
ddata->base + TIMERn_CTRL);
writel_relaxed(TIMERn_CMD_START, ddata->base + TIMERn_CMD);
return 0;
}
static int efm32_clock_event_set_next_event(unsigned long evt,
struct clock_event_device *evtdev)
{
struct efm32_clock_event_ddata *ddata =
container_of(evtdev, struct efm32_clock_event_ddata, evtdev);
writel_relaxed(TIMERn_CMD_STOP, ddata->base + TIMERn_CMD);
writel_relaxed(evt, ddata->base + TIMERn_CNT);
writel_relaxed(TIMERn_CMD_START, ddata->base + TIMERn_CMD);
return 0;
}
static irqreturn_t efm32_clock_event_handler(int irq, void *dev_id)
{
struct efm32_clock_event_ddata *ddata = dev_id;
writel_relaxed(TIMERn_IRQ_UF, ddata->base + TIMERn_IFC);
ddata->evtdev.event_handler(&ddata->evtdev);
return IRQ_HANDLED;
}
static struct efm32_clock_event_ddata clock_event_ddata = {
.evtdev = {
.name = "efm32 clockevent",
.features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
.set_state_shutdown = efm32_clock_event_shutdown,
.set_state_periodic = efm32_clock_event_set_periodic,
.set_state_oneshot = efm32_clock_event_set_oneshot,
.set_next_event = efm32_clock_event_set_next_event,
.rating = 200,
},
};
static struct irqaction efm32_clock_event_irq = {
.name = "efm32 clockevent",
.flags = IRQF_TIMER,
.handler = efm32_clock_event_handler,
.dev_id = &clock_event_ddata,
};
static int __init efm32_clocksource_init(struct device_node *np)
{
struct clk *clk;
void __iomem *base;
unsigned long rate;
int ret;
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
pr_err("failed to get clock for clocksource (%d)\n", ret);
goto err_clk_get;
}
ret = clk_prepare_enable(clk);
if (ret) {
pr_err("failed to enable timer clock for clocksource (%d)\n",
ret);
goto err_clk_enable;
}
rate = clk_get_rate(clk);
base = of_iomap(np, 0);
if (!base) {
ret = -EADDRNOTAVAIL;
pr_err("failed to map registers for clocksource\n");
goto err_iomap;
}
writel_relaxed(TIMERn_CTRL_PRESC_1024 |
TIMERn_CTRL_CLKSEL_PRESCHFPERCLK |
TIMERn_CTRL_MODE_UP, base + TIMERn_CTRL);
writel_relaxed(TIMERn_CMD_START, base + TIMERn_CMD);
ret = clocksource_mmio_init(base + TIMERn_CNT, "efm32 timer",
DIV_ROUND_CLOSEST(rate, 1024), 200, 16,
clocksource_mmio_readl_up);
if (ret) {
pr_err("failed to init clocksource (%d)\n", ret);
goto err_clocksource_init;
}
return 0;
err_clocksource_init:
iounmap(base);
err_iomap:
clk_disable_unprepare(clk);
err_clk_enable:
clk_put(clk);
err_clk_get:
return ret;
}
static int __init efm32_clockevent_init(struct device_node *np)
{
struct clk *clk;
void __iomem *base;
unsigned long rate;
int irq;
int ret;
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
pr_err("failed to get clock for clockevent (%d)\n", ret);
goto err_clk_get;
}
ret = clk_prepare_enable(clk);
if (ret) {
pr_err("failed to enable timer clock for clockevent (%d)\n",
ret);
goto err_clk_enable;
}
rate = clk_get_rate(clk);
base = of_iomap(np, 0);
if (!base) {
ret = -EADDRNOTAVAIL;
pr_err("failed to map registers for clockevent\n");
goto err_iomap;
}
irq = irq_of_parse_and_map(np, 0);
if (!irq) {
ret = -ENOENT;
pr_err("failed to get irq for clockevent\n");
goto err_get_irq;
}
writel_relaxed(TIMERn_IRQ_UF, base + TIMERn_IEN);
clock_event_ddata.base = base;
clock_event_ddata.periodic_top = DIV_ROUND_CLOSEST(rate, 1024 * HZ);
clockevents_config_and_register(&clock_event_ddata.evtdev,
DIV_ROUND_CLOSEST(rate, 1024),
0xf, 0xffff);
ret = setup_irq(irq, &efm32_clock_event_irq);
if (ret) {
pr_err("Failed setup irq\n");
goto err_setup_irq;
}
return 0;
err_setup_irq:
err_get_irq:
iounmap(base);
err_iomap:
clk_disable_unprepare(clk);
err_clk_enable:
clk_put(clk);
err_clk_get:
return ret;
}
/*
* This function asserts that we have exactly one clocksource and one
* clock_event_device in the end.
*/
static int __init efm32_timer_init(struct device_node *np)
{
static int has_clocksource, has_clockevent;
int ret = 0;
if (!has_clocksource) {
ret = efm32_clocksource_init(np);
if (!ret) {
has_clocksource = 1;
return 0;
}
}
if (!has_clockevent) {
ret = efm32_clockevent_init(np);
if (!ret) {
has_clockevent = 1;
return 0;
}
}
return ret;
}
TIMER_OF_DECLARE(efm32compat, "efm32,timer", efm32_timer_init);
TIMER_OF_DECLARE(efm32, "energymicro,efm32-timer", efm32_timer_init);
+314
View File
@@ -0,0 +1,314 @@
/*
* Clocksource driver for NXP LPC32xx/18xx/43xx timer
*
* Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
*
* Based on:
* time-efm32 Copyright (C) 2013 Pengutronix
* mach-lpc32xx/timer.c Copyright (C) 2009 - 2010 NXP Semiconductors
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/sched_clock.h>
#define LPC32XX_TIMER_IR 0x000
#define LPC32XX_TIMER_IR_MR0INT BIT(0)
#define LPC32XX_TIMER_TCR 0x004
#define LPC32XX_TIMER_TCR_CEN BIT(0)
#define LPC32XX_TIMER_TCR_CRST BIT(1)
#define LPC32XX_TIMER_TC 0x008
#define LPC32XX_TIMER_PR 0x00c
#define LPC32XX_TIMER_MCR 0x014
#define LPC32XX_TIMER_MCR_MR0I BIT(0)
#define LPC32XX_TIMER_MCR_MR0R BIT(1)
#define LPC32XX_TIMER_MCR_MR0S BIT(2)
#define LPC32XX_TIMER_MR0 0x018
#define LPC32XX_TIMER_CTCR 0x070
struct lpc32xx_clock_event_ddata {
struct clock_event_device evtdev;
void __iomem *base;
u32 ticks_per_jiffy;
};
/* Needed for the sched clock */
static void __iomem *clocksource_timer_counter;
static u64 notrace lpc32xx_read_sched_clock(void)
{
return readl(clocksource_timer_counter);
}
static unsigned long lpc32xx_delay_timer_read(void)
{
return readl(clocksource_timer_counter);
}
static struct delay_timer lpc32xx_delay_timer = {
.read_current_timer = lpc32xx_delay_timer_read,
};
static int lpc32xx_clkevt_next_event(unsigned long delta,
struct clock_event_device *evtdev)
{
struct lpc32xx_clock_event_ddata *ddata =
container_of(evtdev, struct lpc32xx_clock_event_ddata, evtdev);
/*
* Place timer in reset and program the delta in the match
* channel 0 (MR0). When the timer counter matches the value
* in MR0 register the match will trigger an interrupt.
* After setup the timer is released from reset and enabled.
*/
writel_relaxed(LPC32XX_TIMER_TCR_CRST, ddata->base + LPC32XX_TIMER_TCR);
writel_relaxed(delta, ddata->base + LPC32XX_TIMER_MR0);
writel_relaxed(LPC32XX_TIMER_TCR_CEN, ddata->base + LPC32XX_TIMER_TCR);
return 0;
}
static int lpc32xx_clkevt_shutdown(struct clock_event_device *evtdev)
{
struct lpc32xx_clock_event_ddata *ddata =
container_of(evtdev, struct lpc32xx_clock_event_ddata, evtdev);
/* Disable the timer */
writel_relaxed(0, ddata->base + LPC32XX_TIMER_TCR);
return 0;
}
static int lpc32xx_clkevt_oneshot(struct clock_event_device *evtdev)
{
struct lpc32xx_clock_event_ddata *ddata =
container_of(evtdev, struct lpc32xx_clock_event_ddata, evtdev);
/*
* When using oneshot, we must also disable the timer
* to wait for the first call to set_next_event().
*/
writel_relaxed(0, ddata->base + LPC32XX_TIMER_TCR);
/* Enable interrupt, reset on match and stop on match (MCR). */
writel_relaxed(LPC32XX_TIMER_MCR_MR0I | LPC32XX_TIMER_MCR_MR0R |
LPC32XX_TIMER_MCR_MR0S, ddata->base + LPC32XX_TIMER_MCR);
return 0;
}
static int lpc32xx_clkevt_periodic(struct clock_event_device *evtdev)
{
struct lpc32xx_clock_event_ddata *ddata =
container_of(evtdev, struct lpc32xx_clock_event_ddata, evtdev);
/* Enable interrupt and reset on match. */
writel_relaxed(LPC32XX_TIMER_MCR_MR0I | LPC32XX_TIMER_MCR_MR0R,
ddata->base + LPC32XX_TIMER_MCR);
/*
* Place timer in reset and program the delta in the match
* channel 0 (MR0).
*/
writel_relaxed(LPC32XX_TIMER_TCR_CRST, ddata->base + LPC32XX_TIMER_TCR);
writel_relaxed(ddata->ticks_per_jiffy, ddata->base + LPC32XX_TIMER_MR0);
writel_relaxed(LPC32XX_TIMER_TCR_CEN, ddata->base + LPC32XX_TIMER_TCR);
return 0;
}
static irqreturn_t lpc32xx_clock_event_handler(int irq, void *dev_id)
{
struct lpc32xx_clock_event_ddata *ddata = dev_id;
/* Clear match on channel 0 */
writel_relaxed(LPC32XX_TIMER_IR_MR0INT, ddata->base + LPC32XX_TIMER_IR);
ddata->evtdev.event_handler(&ddata->evtdev);
return IRQ_HANDLED;
}
static struct lpc32xx_clock_event_ddata lpc32xx_clk_event_ddata = {
.evtdev = {
.name = "lpc3220 clockevent",
.features = CLOCK_EVT_FEAT_ONESHOT |
CLOCK_EVT_FEAT_PERIODIC,
.rating = 300,
.set_next_event = lpc32xx_clkevt_next_event,
.set_state_shutdown = lpc32xx_clkevt_shutdown,
.set_state_oneshot = lpc32xx_clkevt_oneshot,
.set_state_periodic = lpc32xx_clkevt_periodic,
},
};
static int __init lpc32xx_clocksource_init(struct device_node *np)
{
void __iomem *base;
unsigned long rate;
struct clk *clk;
int ret;
clk = of_clk_get_by_name(np, "timerclk");
if (IS_ERR(clk)) {
pr_err("clock get failed (%ld)\n", PTR_ERR(clk));
return PTR_ERR(clk);
}
ret = clk_prepare_enable(clk);
if (ret) {
pr_err("clock enable failed (%d)\n", ret);
goto err_clk_enable;
}
base = of_iomap(np, 0);
if (!base) {
pr_err("unable to map registers\n");
ret = -EADDRNOTAVAIL;
goto err_iomap;
}
/*
* Disable and reset timer then set it to free running timer
* mode (CTCR) with no prescaler (PR) or match operations (MCR).
* After setup the timer is released from reset and enabled.
*/
writel_relaxed(LPC32XX_TIMER_TCR_CRST, base + LPC32XX_TIMER_TCR);
writel_relaxed(0, base + LPC32XX_TIMER_PR);
writel_relaxed(0, base + LPC32XX_TIMER_MCR);
writel_relaxed(0, base + LPC32XX_TIMER_CTCR);
writel_relaxed(LPC32XX_TIMER_TCR_CEN, base + LPC32XX_TIMER_TCR);
rate = clk_get_rate(clk);
ret = clocksource_mmio_init(base + LPC32XX_TIMER_TC, "lpc3220 timer",
rate, 300, 32, clocksource_mmio_readl_up);
if (ret) {
pr_err("failed to init clocksource (%d)\n", ret);
goto err_clocksource_init;
}
clocksource_timer_counter = base + LPC32XX_TIMER_TC;
lpc32xx_delay_timer.freq = rate;
register_current_timer_delay(&lpc32xx_delay_timer);
sched_clock_register(lpc32xx_read_sched_clock, 32, rate);
return 0;
err_clocksource_init:
iounmap(base);
err_iomap:
clk_disable_unprepare(clk);
err_clk_enable:
clk_put(clk);
return ret;
}
static int __init lpc32xx_clockevent_init(struct device_node *np)
{
void __iomem *base;
unsigned long rate;
struct clk *clk;
int ret, irq;
clk = of_clk_get_by_name(np, "timerclk");
if (IS_ERR(clk)) {
pr_err("clock get failed (%ld)\n", PTR_ERR(clk));
return PTR_ERR(clk);
}
ret = clk_prepare_enable(clk);
if (ret) {
pr_err("clock enable failed (%d)\n", ret);
goto err_clk_enable;
}
base = of_iomap(np, 0);
if (!base) {
pr_err("unable to map registers\n");
ret = -EADDRNOTAVAIL;
goto err_iomap;
}
irq = irq_of_parse_and_map(np, 0);
if (!irq) {
pr_err("get irq failed\n");
ret = -ENOENT;
goto err_irq;
}
/*
* Disable timer and clear any pending interrupt (IR) on match
* channel 0 (MR0). Clear the prescaler as it's not used.
*/
writel_relaxed(0, base + LPC32XX_TIMER_TCR);
writel_relaxed(0, base + LPC32XX_TIMER_PR);
writel_relaxed(0, base + LPC32XX_TIMER_CTCR);
writel_relaxed(LPC32XX_TIMER_IR_MR0INT, base + LPC32XX_TIMER_IR);
rate = clk_get_rate(clk);
lpc32xx_clk_event_ddata.base = base;
lpc32xx_clk_event_ddata.ticks_per_jiffy = DIV_ROUND_CLOSEST(rate, HZ);
clockevents_config_and_register(&lpc32xx_clk_event_ddata.evtdev,
rate, 1, -1);
ret = request_irq(irq, lpc32xx_clock_event_handler,
IRQF_TIMER | IRQF_IRQPOLL, "lpc3220 clockevent",
&lpc32xx_clk_event_ddata);
if (ret) {
pr_err("request irq failed\n");
goto err_irq;
}
return 0;
err_irq:
iounmap(base);
err_iomap:
clk_disable_unprepare(clk);
err_clk_enable:
clk_put(clk);
return ret;
}
/*
* This function asserts that we have exactly one clocksource and one
* clock_event_device in the end.
*/
static int __init lpc32xx_timer_init(struct device_node *np)
{
static int has_clocksource, has_clockevent;
int ret = 0;
if (!has_clocksource) {
ret = lpc32xx_clocksource_init(np);
if (!ret) {
has_clocksource = 1;
return 0;
}
}
if (!has_clockevent) {
ret = lpc32xx_clockevent_init(np);
if (!ret) {
has_clockevent = 1;
return 0;
}
}
return ret;
}
TIMER_OF_DECLARE(lpc32xx_timer, "nxp,lpc3220-timer", lpc32xx_timer_init);
+192
View File
@@ -0,0 +1,192 @@
/*
* Marvell Orion SoC timer handling.
*
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*
* Timer 0 is used as free-running clocksource, while timer 1 is
* used as clock_event_device.
*/
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/spinlock.h>
#include <linux/sched_clock.h>
#define TIMER_CTRL 0x00
#define TIMER0_EN BIT(0)
#define TIMER0_RELOAD_EN BIT(1)
#define TIMER1_EN BIT(2)
#define TIMER1_RELOAD_EN BIT(3)
#define TIMER0_RELOAD 0x10
#define TIMER0_VAL 0x14
#define TIMER1_RELOAD 0x18
#define TIMER1_VAL 0x1c
#define ORION_ONESHOT_MIN 1
#define ORION_ONESHOT_MAX 0xfffffffe
static void __iomem *timer_base;
static unsigned long notrace orion_read_timer(void)
{
return ~readl(timer_base + TIMER0_VAL);
}
static struct delay_timer orion_delay_timer = {
.read_current_timer = orion_read_timer,
};
static void orion_delay_timer_init(unsigned long rate)
{
orion_delay_timer.freq = rate;
register_current_timer_delay(&orion_delay_timer);
}
/*
* Free-running clocksource handling.
*/
static u64 notrace orion_read_sched_clock(void)
{
return ~readl(timer_base + TIMER0_VAL);
}
/*
* Clockevent handling.
*/
static u32 ticks_per_jiffy;
static int orion_clkevt_next_event(unsigned long delta,
struct clock_event_device *dev)
{
/* setup and enable one-shot timer */
writel(delta, timer_base + TIMER1_VAL);
atomic_io_modify(timer_base + TIMER_CTRL,
TIMER1_RELOAD_EN | TIMER1_EN, TIMER1_EN);
return 0;
}
static int orion_clkevt_shutdown(struct clock_event_device *dev)
{
/* disable timer */
atomic_io_modify(timer_base + TIMER_CTRL,
TIMER1_RELOAD_EN | TIMER1_EN, 0);
return 0;
}
static int orion_clkevt_set_periodic(struct clock_event_device *dev)
{
/* setup and enable periodic timer at 1/HZ intervals */
writel(ticks_per_jiffy - 1, timer_base + TIMER1_RELOAD);
writel(ticks_per_jiffy - 1, timer_base + TIMER1_VAL);
atomic_io_modify(timer_base + TIMER_CTRL,
TIMER1_RELOAD_EN | TIMER1_EN,
TIMER1_RELOAD_EN | TIMER1_EN);
return 0;
}
static struct clock_event_device orion_clkevt = {
.name = "orion_event",
.features = CLOCK_EVT_FEAT_ONESHOT |
CLOCK_EVT_FEAT_PERIODIC,
.shift = 32,
.rating = 300,
.set_next_event = orion_clkevt_next_event,
.set_state_shutdown = orion_clkevt_shutdown,
.set_state_periodic = orion_clkevt_set_periodic,
.set_state_oneshot = orion_clkevt_shutdown,
.tick_resume = orion_clkevt_shutdown,
};
static irqreturn_t orion_clkevt_irq_handler(int irq, void *dev_id)
{
orion_clkevt.event_handler(&orion_clkevt);
return IRQ_HANDLED;
}
static struct irqaction orion_clkevt_irq = {
.name = "orion_event",
.flags = IRQF_TIMER,
.handler = orion_clkevt_irq_handler,
};
static int __init orion_timer_init(struct device_node *np)
{
unsigned long rate;
struct clk *clk;
int irq, ret;
/* timer registers are shared with watchdog timer */
timer_base = of_iomap(np, 0);
if (!timer_base) {
pr_err("%s: unable to map resource\n", np->name);
return -ENXIO;
}
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
pr_err("%s: unable to get clk\n", np->name);
return PTR_ERR(clk);
}
ret = clk_prepare_enable(clk);
if (ret) {
pr_err("Failed to prepare clock\n");
return ret;
}
/* we are only interested in timer1 irq */
irq = irq_of_parse_and_map(np, 1);
if (irq <= 0) {
pr_err("%s: unable to parse timer1 irq\n", np->name);
return -EINVAL;
}
rate = clk_get_rate(clk);
/* setup timer0 as free-running clocksource */
writel(~0, timer_base + TIMER0_VAL);
writel(~0, timer_base + TIMER0_RELOAD);
atomic_io_modify(timer_base + TIMER_CTRL,
TIMER0_RELOAD_EN | TIMER0_EN,
TIMER0_RELOAD_EN | TIMER0_EN);
ret = clocksource_mmio_init(timer_base + TIMER0_VAL,
"orion_clocksource", rate, 300, 32,
clocksource_mmio_readl_down);
if (ret) {
pr_err("Failed to initialize mmio timer\n");
return ret;
}
sched_clock_register(orion_read_sched_clock, 32, rate);
/* setup timer1 as clockevent timer */
ret = setup_irq(irq, &orion_clkevt_irq);
if (ret) {
pr_err("%s: unable to setup irq\n", np->name);
return ret;
}
ticks_per_jiffy = (clk_get_rate(clk) + HZ/2) / HZ;
orion_clkevt.cpumask = cpumask_of(0);
orion_clkevt.irq = irq;
clockevents_config_and_register(&orion_clkevt, rate,
ORION_ONESHOT_MIN, ORION_ONESHOT_MAX);
orion_delay_timer_init(rate);
return 0;
}
TIMER_OF_DECLARE(orion_timer, "marvell,orion-timer", orion_timer_init);
+218
View File
@@ -0,0 +1,218 @@
/*
* Pistachio clocksource based on general-purpose timers
*
* Copyright (C) 2015 Imagination Technologies
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/clk.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/sched_clock.h>
#include <linux/time.h>
/* Top level reg */
#define CR_TIMER_CTRL_CFG 0x00
#define TIMER_ME_GLOBAL BIT(0)
#define CR_TIMER_REV 0x10
/* Timer specific registers */
#define TIMER_CFG 0x20
#define TIMER_ME_LOCAL BIT(0)
#define TIMER_RELOAD_VALUE 0x24
#define TIMER_CURRENT_VALUE 0x28
#define TIMER_CURRENT_OVERFLOW_VALUE 0x2C
#define TIMER_IRQ_STATUS 0x30
#define TIMER_IRQ_CLEAR 0x34
#define TIMER_IRQ_MASK 0x38
#define PERIP_TIMER_CONTROL 0x90
/* Timer specific configuration Values */
#define RELOAD_VALUE 0xffffffff
struct pistachio_clocksource {
void __iomem *base;
raw_spinlock_t lock;
struct clocksource cs;
};
static struct pistachio_clocksource pcs_gpt;
#define to_pistachio_clocksource(cs) \
container_of(cs, struct pistachio_clocksource, cs)
static inline u32 gpt_readl(void __iomem *base, u32 offset, u32 gpt_id)
{
return readl(base + 0x20 * gpt_id + offset);
}
static inline void gpt_writel(void __iomem *base, u32 value, u32 offset,
u32 gpt_id)
{
writel(value, base + 0x20 * gpt_id + offset);
}
static u64 notrace
pistachio_clocksource_read_cycles(struct clocksource *cs)
{
struct pistachio_clocksource *pcs = to_pistachio_clocksource(cs);
u32 counter, overflw;
unsigned long flags;
/*
* The counter value is only refreshed after the overflow value is read.
* And they must be read in strict order, hence raw spin lock added.
*/
raw_spin_lock_irqsave(&pcs->lock, flags);
overflw = gpt_readl(pcs->base, TIMER_CURRENT_OVERFLOW_VALUE, 0);
counter = gpt_readl(pcs->base, TIMER_CURRENT_VALUE, 0);
raw_spin_unlock_irqrestore(&pcs->lock, flags);
return (u64)~counter;
}
static u64 notrace pistachio_read_sched_clock(void)
{
return pistachio_clocksource_read_cycles(&pcs_gpt.cs);
}
static void pistachio_clksrc_set_mode(struct clocksource *cs, int timeridx,
int enable)
{
struct pistachio_clocksource *pcs = to_pistachio_clocksource(cs);
u32 val;
val = gpt_readl(pcs->base, TIMER_CFG, timeridx);
if (enable)
val |= TIMER_ME_LOCAL;
else
val &= ~TIMER_ME_LOCAL;
gpt_writel(pcs->base, val, TIMER_CFG, timeridx);
}
static void pistachio_clksrc_enable(struct clocksource *cs, int timeridx)
{
struct pistachio_clocksource *pcs = to_pistachio_clocksource(cs);
/* Disable GPT local before loading reload value */
pistachio_clksrc_set_mode(cs, timeridx, false);
gpt_writel(pcs->base, RELOAD_VALUE, TIMER_RELOAD_VALUE, timeridx);
pistachio_clksrc_set_mode(cs, timeridx, true);
}
static void pistachio_clksrc_disable(struct clocksource *cs, int timeridx)
{
/* Disable GPT local */
pistachio_clksrc_set_mode(cs, timeridx, false);
}
static int pistachio_clocksource_enable(struct clocksource *cs)
{
pistachio_clksrc_enable(cs, 0);
return 0;
}
static void pistachio_clocksource_disable(struct clocksource *cs)
{
pistachio_clksrc_disable(cs, 0);
}
/* Desirable clock source for pistachio platform */
static struct pistachio_clocksource pcs_gpt = {
.cs = {
.name = "gptimer",
.rating = 300,
.enable = pistachio_clocksource_enable,
.disable = pistachio_clocksource_disable,
.read = pistachio_clocksource_read_cycles,
.mask = CLOCKSOURCE_MASK(32),
.flags = CLOCK_SOURCE_IS_CONTINUOUS |
CLOCK_SOURCE_SUSPEND_NONSTOP,
},
};
static int __init pistachio_clksrc_of_init(struct device_node *node)
{
struct clk *sys_clk, *fast_clk;
struct regmap *periph_regs;
unsigned long rate;
int ret;
pcs_gpt.base = of_iomap(node, 0);
if (!pcs_gpt.base) {
pr_err("cannot iomap\n");
return -ENXIO;
}
periph_regs = syscon_regmap_lookup_by_phandle(node, "img,cr-periph");
if (IS_ERR(periph_regs)) {
pr_err("cannot get peripheral regmap (%ld)\n",
PTR_ERR(periph_regs));
return PTR_ERR(periph_regs);
}
/* Switch to using the fast counter clock */
ret = regmap_update_bits(periph_regs, PERIP_TIMER_CONTROL,
0xf, 0x0);
if (ret)
return ret;
sys_clk = of_clk_get_by_name(node, "sys");
if (IS_ERR(sys_clk)) {
pr_err("clock get failed (%ld)\n", PTR_ERR(sys_clk));
return PTR_ERR(sys_clk);
}
fast_clk = of_clk_get_by_name(node, "fast");
if (IS_ERR(fast_clk)) {
pr_err("clock get failed (%lu)\n", PTR_ERR(fast_clk));
return PTR_ERR(fast_clk);
}
ret = clk_prepare_enable(sys_clk);
if (ret < 0) {
pr_err("failed to enable clock (%d)\n", ret);
return ret;
}
ret = clk_prepare_enable(fast_clk);
if (ret < 0) {
pr_err("failed to enable clock (%d)\n", ret);
clk_disable_unprepare(sys_clk);
return ret;
}
rate = clk_get_rate(fast_clk);
/* Disable irq's for clocksource usage */
gpt_writel(pcs_gpt.base, 0, TIMER_IRQ_MASK, 0);
gpt_writel(pcs_gpt.base, 0, TIMER_IRQ_MASK, 1);
gpt_writel(pcs_gpt.base, 0, TIMER_IRQ_MASK, 2);
gpt_writel(pcs_gpt.base, 0, TIMER_IRQ_MASK, 3);
/* Enable timer block */
writel(TIMER_ME_GLOBAL, pcs_gpt.base);
raw_spin_lock_init(&pcs_gpt.lock);
sched_clock_register(pistachio_read_sched_clock, 32, rate);
return clocksource_register_hz(&pcs_gpt.cs, rate);
}
TIMER_OF_DECLARE(pistachio_gptimer, "img,pistachio-gptimer",
pistachio_clksrc_of_init);
+266
View File
@@ -0,0 +1,266 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2005-2017 Andes Technology Corporation
/*
* Andestech ATCPIT100 Timer Device Driver Implementation
* Rick Chen, Andes Technology Corporation <rick@andestech.com>
*
*/
#include <linux/irq.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/cpufreq.h>
#include <linux/sched.h>
#include <linux/sched_clock.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include "timer-of.h"
#ifdef CONFIG_NDS32
#include <asm/vdso_timer_info.h>
#endif
/*
* Definition of register offsets
*/
/* ID and Revision Register */
#define ID_REV 0x0
/* Configuration Register */
#define CFG 0x10
/* Interrupt Enable Register */
#define INT_EN 0x14
#define CH_INT_EN(c, i) ((1<<i)<<(4*c))
#define CH0INT0EN 0x01
/* Interrupt Status Register */
#define INT_STA 0x18
#define CH0INT0 0x01
/* Channel Enable Register */
#define CH_EN 0x1C
#define CH0TMR0EN 0x1
#define CH1TMR0EN 0x10
/* Channel 0 , 1 Control Register */
#define CH0_CTL (0x20)
#define CH1_CTL (0x20 + 0x10)
/* Channel clock source , bit 3 , 0:External clock , 1:APB clock */
#define APB_CLK BIT(3)
/* Channel mode , bit 0~2 */
#define TMR_32 0x1
#define TMR_16 0x2
#define TMR_8 0x3
/* Channel 0 , 1 Reload Register */
#define CH0_REL (0x24)
#define CH1_REL (0x24 + 0x10)
/* Channel 0 , 1 Counter Register */
#define CH0_CNT (0x28)
#define CH1_CNT (0x28 + 0x10)
#define TIMER_SYNC_TICKS 3
static void atcpit100_ch1_tmr0_en(void __iomem *base)
{
writel(~0, base + CH1_REL);
writel(APB_CLK|TMR_32, base + CH1_CTL);
}
static void atcpit100_ch0_tmr0_en(void __iomem *base)
{
writel(APB_CLK|TMR_32, base + CH0_CTL);
}
static void atcpit100_clkevt_time_setup(void __iomem *base, unsigned long delay)
{
writel(delay, base + CH0_CNT);
writel(delay, base + CH0_REL);
}
static void atcpit100_timer_clear_interrupt(void __iomem *base)
{
u32 val;
val = readl(base + INT_STA);
writel(val | CH0INT0, base + INT_STA);
}
static void atcpit100_clocksource_start(void __iomem *base)
{
u32 val;
val = readl(base + CH_EN);
writel(val | CH1TMR0EN, base + CH_EN);
}
static void atcpit100_clkevt_time_start(void __iomem *base)
{
u32 val;
val = readl(base + CH_EN);
writel(val | CH0TMR0EN, base + CH_EN);
}
static void atcpit100_clkevt_time_stop(void __iomem *base)
{
u32 val;
atcpit100_timer_clear_interrupt(base);
val = readl(base + CH_EN);
writel(val & ~CH0TMR0EN, base + CH_EN);
}
static int atcpit100_clkevt_next_event(unsigned long evt,
struct clock_event_device *clkevt)
{
u32 val;
struct timer_of *to = to_timer_of(clkevt);
val = readl(timer_of_base(to) + CH_EN);
writel(val & ~CH0TMR0EN, timer_of_base(to) + CH_EN);
writel(evt, timer_of_base(to) + CH0_REL);
writel(val | CH0TMR0EN, timer_of_base(to) + CH_EN);
return 0;
}
static int atcpit100_clkevt_set_periodic(struct clock_event_device *evt)
{
struct timer_of *to = to_timer_of(evt);
atcpit100_clkevt_time_setup(timer_of_base(to), timer_of_period(to));
atcpit100_clkevt_time_start(timer_of_base(to));
return 0;
}
static int atcpit100_clkevt_shutdown(struct clock_event_device *evt)
{
struct timer_of *to = to_timer_of(evt);
atcpit100_clkevt_time_stop(timer_of_base(to));
return 0;
}
static int atcpit100_clkevt_set_oneshot(struct clock_event_device *evt)
{
struct timer_of *to = to_timer_of(evt);
u32 val;
writel(~0x0, timer_of_base(to) + CH0_REL);
val = readl(timer_of_base(to) + CH_EN);
writel(val | CH0TMR0EN, timer_of_base(to) + CH_EN);
return 0;
}
static irqreturn_t atcpit100_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = (struct clock_event_device *)dev_id;
struct timer_of *to = to_timer_of(evt);
atcpit100_timer_clear_interrupt(timer_of_base(to));
evt->event_handler(evt);
return IRQ_HANDLED;
}
static struct timer_of to = {
.flags = TIMER_OF_IRQ | TIMER_OF_CLOCK | TIMER_OF_BASE,
.clkevt = {
.name = "atcpit100_tick",
.rating = 300,
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
.set_state_shutdown = atcpit100_clkevt_shutdown,
.set_state_periodic = atcpit100_clkevt_set_periodic,
.set_state_oneshot = atcpit100_clkevt_set_oneshot,
.tick_resume = atcpit100_clkevt_shutdown,
.set_next_event = atcpit100_clkevt_next_event,
.cpumask = cpu_possible_mask,
},
.of_irq = {
.handler = atcpit100_timer_interrupt,
.flags = IRQF_TIMER | IRQF_IRQPOLL,
},
/*
* FIXME: we currently only support clocking using PCLK
* and using EXTCLK is not supported in the driver.
*/
.of_clk = {
.name = "PCLK",
}
};
static u64 notrace atcpit100_timer_sched_read(void)
{
return ~readl(timer_of_base(&to) + CH1_CNT);
}
#ifdef CONFIG_NDS32
static void fill_vdso_need_info(struct device_node *node)
{
struct resource timer_res;
of_address_to_resource(node, 0, &timer_res);
timer_info.mapping_base = (unsigned long)timer_res.start;
timer_info.cycle_count_down = true;
timer_info.cycle_count_reg_offset = CH1_CNT;
}
#endif
static int __init atcpit100_timer_init(struct device_node *node)
{
int ret;
u32 val;
void __iomem *base;
ret = timer_of_init(node, &to);
if (ret)
return ret;
base = timer_of_base(&to);
sched_clock_register(atcpit100_timer_sched_read, 32,
timer_of_rate(&to));
ret = clocksource_mmio_init(base + CH1_CNT,
node->name, timer_of_rate(&to), 300, 32,
clocksource_mmio_readl_down);
if (ret) {
pr_err("Failed to register clocksource\n");
return ret;
}
/* clear channel 0 timer0 interrupt */
atcpit100_timer_clear_interrupt(base);
clockevents_config_and_register(&to.clkevt, timer_of_rate(&to),
TIMER_SYNC_TICKS, 0xffffffff);
atcpit100_ch0_tmr0_en(base);
atcpit100_ch1_tmr0_en(base);
atcpit100_clocksource_start(base);
atcpit100_clkevt_time_start(base);
/* Enable channel 0 timer0 interrupt */
val = readl(base + INT_EN);
writel(val | CH0INT0EN, base + INT_EN);
#ifdef CONFIG_NDS32
fill_vdso_need_info(node);
#endif
return ret;
}
TIMER_OF_DECLARE(atcpit100, "andestech,atcpit100", atcpit100_timer_init);
+286
View File
@@ -0,0 +1,286 @@
/*
* System timer for CSR SiRFprimaII
*
* Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
*
* Licensed under GPLv2 or later.
*/
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/cpu.h>
#include <linux/bitops.h>
#include <linux/irq.h>
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/sched_clock.h>
#define SIRFSOC_TIMER_32COUNTER_0_CTRL 0x0000
#define SIRFSOC_TIMER_32COUNTER_1_CTRL 0x0004
#define SIRFSOC_TIMER_MATCH_0 0x0018
#define SIRFSOC_TIMER_MATCH_1 0x001c
#define SIRFSOC_TIMER_COUNTER_0 0x0048
#define SIRFSOC_TIMER_COUNTER_1 0x004c
#define SIRFSOC_TIMER_INTR_STATUS 0x0060
#define SIRFSOC_TIMER_WATCHDOG_EN 0x0064
#define SIRFSOC_TIMER_64COUNTER_CTRL 0x0068
#define SIRFSOC_TIMER_64COUNTER_LO 0x006c
#define SIRFSOC_TIMER_64COUNTER_HI 0x0070
#define SIRFSOC_TIMER_64COUNTER_LOAD_LO 0x0074
#define SIRFSOC_TIMER_64COUNTER_LOAD_HI 0x0078
#define SIRFSOC_TIMER_64COUNTER_RLATCHED_LO 0x007c
#define SIRFSOC_TIMER_64COUNTER_RLATCHED_HI 0x0080
#define SIRFSOC_TIMER_REG_CNT 6
static unsigned long atlas7_timer_rate;
static const u32 sirfsoc_timer_reg_list[SIRFSOC_TIMER_REG_CNT] = {
SIRFSOC_TIMER_WATCHDOG_EN,
SIRFSOC_TIMER_32COUNTER_0_CTRL,
SIRFSOC_TIMER_32COUNTER_1_CTRL,
SIRFSOC_TIMER_64COUNTER_CTRL,
SIRFSOC_TIMER_64COUNTER_RLATCHED_LO,
SIRFSOC_TIMER_64COUNTER_RLATCHED_HI,
};
static u32 sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT];
static void __iomem *sirfsoc_timer_base;
/* disable count and interrupt */
static inline void sirfsoc_timer_count_disable(int idx)
{
writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx) & ~0x7,
sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx);
}
/* enable count and interrupt */
static inline void sirfsoc_timer_count_enable(int idx)
{
writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx) | 0x3,
sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx);
}
/* timer interrupt handler */
static irqreturn_t sirfsoc_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *ce = dev_id;
int cpu = smp_processor_id();
/* clear timer interrupt */
writel_relaxed(BIT(cpu), sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS);
if (clockevent_state_oneshot(ce))
sirfsoc_timer_count_disable(cpu);
ce->event_handler(ce);
return IRQ_HANDLED;
}
/* read 64-bit timer counter */
static u64 sirfsoc_timer_read(struct clocksource *cs)
{
u64 cycles;
writel_relaxed((readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) |
BIT(0)) & ~BIT(1), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
cycles = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_RLATCHED_HI);
cycles = (cycles << 32) | readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_RLATCHED_LO);
return cycles;
}
static int sirfsoc_timer_set_next_event(unsigned long delta,
struct clock_event_device *ce)
{
int cpu = smp_processor_id();
/* disable timer first, then modify the related registers */
sirfsoc_timer_count_disable(cpu);
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_0 +
4 * cpu);
writel_relaxed(delta, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_0 +
4 * cpu);
/* enable the tick */
sirfsoc_timer_count_enable(cpu);
return 0;
}
/* Oneshot is enabled in set_next_event */
static int sirfsoc_timer_shutdown(struct clock_event_device *evt)
{
sirfsoc_timer_count_disable(smp_processor_id());
return 0;
}
static void sirfsoc_clocksource_suspend(struct clocksource *cs)
{
int i;
for (i = 0; i < SIRFSOC_TIMER_REG_CNT; i++)
sirfsoc_timer_reg_val[i] = readl_relaxed(sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
}
static void sirfsoc_clocksource_resume(struct clocksource *cs)
{
int i;
for (i = 0; i < SIRFSOC_TIMER_REG_CNT - 2; i++)
writel_relaxed(sirfsoc_timer_reg_val[i], sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 2],
sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_LO);
writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 1],
sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_HI);
writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) |
BIT(1) | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
}
static struct clock_event_device __percpu *sirfsoc_clockevent;
static struct clocksource sirfsoc_clocksource = {
.name = "sirfsoc_clocksource",
.rating = 200,
.mask = CLOCKSOURCE_MASK(64),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
.read = sirfsoc_timer_read,
.suspend = sirfsoc_clocksource_suspend,
.resume = sirfsoc_clocksource_resume,
};
static struct irqaction sirfsoc_timer_irq = {
.name = "sirfsoc_timer0",
.flags = IRQF_TIMER | IRQF_NOBALANCING,
.handler = sirfsoc_timer_interrupt,
};
static struct irqaction sirfsoc_timer1_irq = {
.name = "sirfsoc_timer1",
.flags = IRQF_TIMER | IRQF_NOBALANCING,
.handler = sirfsoc_timer_interrupt,
};
static int sirfsoc_local_timer_starting_cpu(unsigned int cpu)
{
struct clock_event_device *ce = per_cpu_ptr(sirfsoc_clockevent, cpu);
struct irqaction *action;
if (cpu == 0)
action = &sirfsoc_timer_irq;
else
action = &sirfsoc_timer1_irq;
ce->irq = action->irq;
ce->name = "local_timer";
ce->features = CLOCK_EVT_FEAT_ONESHOT;
ce->rating = 200;
ce->set_state_shutdown = sirfsoc_timer_shutdown;
ce->set_state_oneshot = sirfsoc_timer_shutdown;
ce->tick_resume = sirfsoc_timer_shutdown;
ce->set_next_event = sirfsoc_timer_set_next_event;
clockevents_calc_mult_shift(ce, atlas7_timer_rate, 60);
ce->max_delta_ns = clockevent_delta2ns(-2, ce);
ce->max_delta_ticks = (unsigned long)-2;
ce->min_delta_ns = clockevent_delta2ns(2, ce);
ce->min_delta_ticks = 2;
ce->cpumask = cpumask_of(cpu);
action->dev_id = ce;
BUG_ON(setup_irq(ce->irq, action));
irq_force_affinity(action->irq, cpumask_of(cpu));
clockevents_register_device(ce);
return 0;
}
static int sirfsoc_local_timer_dying_cpu(unsigned int cpu)
{
sirfsoc_timer_count_disable(1);
if (cpu == 0)
remove_irq(sirfsoc_timer_irq.irq, &sirfsoc_timer_irq);
else
remove_irq(sirfsoc_timer1_irq.irq, &sirfsoc_timer1_irq);
return 0;
}
static int __init sirfsoc_clockevent_init(void)
{
sirfsoc_clockevent = alloc_percpu(struct clock_event_device);
BUG_ON(!sirfsoc_clockevent);
/* Install and invoke hotplug callbacks */
return cpuhp_setup_state(CPUHP_AP_MARCO_TIMER_STARTING,
"clockevents/marco:starting",
sirfsoc_local_timer_starting_cpu,
sirfsoc_local_timer_dying_cpu);
}
/* initialize the kernel jiffy timer source */
static int __init sirfsoc_atlas7_timer_init(struct device_node *np)
{
struct clk *clk;
clk = of_clk_get(np, 0);
BUG_ON(IS_ERR(clk));
BUG_ON(clk_prepare_enable(clk));
atlas7_timer_rate = clk_get_rate(clk);
/* timer dividers: 0, not divided */
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL);
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_1_CTRL);
/* Initialize timer counters to 0 */
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_LO);
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_HI);
writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) |
BIT(1) | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_0);
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_1);
/* Clear all interrupts */
writel_relaxed(0xFFFF, sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS);
BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, atlas7_timer_rate));
return sirfsoc_clockevent_init();
}
static int __init sirfsoc_of_timer_init(struct device_node *np)
{
sirfsoc_timer_base = of_iomap(np, 0);
if (!sirfsoc_timer_base) {
pr_err("unable to map timer cpu registers\n");
return -ENXIO;
}
sirfsoc_timer_irq.irq = irq_of_parse_and_map(np, 0);
if (!sirfsoc_timer_irq.irq) {
pr_err("No irq passed for timer0 via DT\n");
return -EINVAL;
}
sirfsoc_timer1_irq.irq = irq_of_parse_and_map(np, 1);
if (!sirfsoc_timer1_irq.irq) {
pr_err("No irq passed for timer1 via DT\n");
return -EINVAL;
}
return sirfsoc_atlas7_timer_init(np);
}
TIMER_OF_DECLARE(sirfsoc_atlas7_timer, "sirf,atlas7-tick", sirfsoc_of_timer_init);
+267
View File
@@ -0,0 +1,267 @@
/*
* at91sam926x_time.c - Periodic Interval Timer (PIT) for at91sam926x
*
* Copyright (C) 2005-2006 M. Amine SAYA, ATMEL Rousset, France
* Revision 2005 M. Nicolas Diremdjian, ATMEL Rousset, France
* Converted to ClockSource/ClockEvents by David Brownell.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#define pr_fmt(fmt) "AT91: PIT: " fmt
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#define AT91_PIT_MR 0x00 /* Mode Register */
#define AT91_PIT_PITIEN BIT(25) /* Timer Interrupt Enable */
#define AT91_PIT_PITEN BIT(24) /* Timer Enabled */
#define AT91_PIT_PIV GENMASK(19, 0) /* Periodic Interval Value */
#define AT91_PIT_SR 0x04 /* Status Register */
#define AT91_PIT_PITS BIT(0) /* Timer Status */
#define AT91_PIT_PIVR 0x08 /* Periodic Interval Value Register */
#define AT91_PIT_PIIR 0x0c /* Periodic Interval Image Register */
#define AT91_PIT_PICNT GENMASK(31, 20) /* Interval Counter */
#define AT91_PIT_CPIV GENMASK(19, 0) /* Inverval Value */
#define PIT_CPIV(x) ((x) & AT91_PIT_CPIV)
#define PIT_PICNT(x) (((x) & AT91_PIT_PICNT) >> 20)
struct pit_data {
struct clock_event_device clkevt;
struct clocksource clksrc;
void __iomem *base;
u32 cycle;
u32 cnt;
unsigned int irq;
struct clk *mck;
};
static inline struct pit_data *clksrc_to_pit_data(struct clocksource *clksrc)
{
return container_of(clksrc, struct pit_data, clksrc);
}
static inline struct pit_data *clkevt_to_pit_data(struct clock_event_device *clkevt)
{
return container_of(clkevt, struct pit_data, clkevt);
}
static inline unsigned int pit_read(void __iomem *base, unsigned int reg_offset)
{
return readl_relaxed(base + reg_offset);
}
static inline void pit_write(void __iomem *base, unsigned int reg_offset, unsigned long value)
{
writel_relaxed(value, base + reg_offset);
}
/*
* Clocksource: just a monotonic counter of MCK/16 cycles.
* We don't care whether or not PIT irqs are enabled.
*/
static u64 read_pit_clk(struct clocksource *cs)
{
struct pit_data *data = clksrc_to_pit_data(cs);
unsigned long flags;
u32 elapsed;
u32 t;
raw_local_irq_save(flags);
elapsed = data->cnt;
t = pit_read(data->base, AT91_PIT_PIIR);
raw_local_irq_restore(flags);
elapsed += PIT_PICNT(t) * data->cycle;
elapsed += PIT_CPIV(t);
return elapsed;
}
static int pit_clkevt_shutdown(struct clock_event_device *dev)
{
struct pit_data *data = clkevt_to_pit_data(dev);
/* disable irq, leaving the clocksource active */
pit_write(data->base, AT91_PIT_MR, (data->cycle - 1) | AT91_PIT_PITEN);
return 0;
}
/*
* Clockevent device: interrupts every 1/HZ (== pit_cycles * MCK/16)
*/
static int pit_clkevt_set_periodic(struct clock_event_device *dev)
{
struct pit_data *data = clkevt_to_pit_data(dev);
/* update clocksource counter */
data->cnt += data->cycle * PIT_PICNT(pit_read(data->base, AT91_PIT_PIVR));
pit_write(data->base, AT91_PIT_MR,
(data->cycle - 1) | AT91_PIT_PITEN | AT91_PIT_PITIEN);
return 0;
}
static void at91sam926x_pit_suspend(struct clock_event_device *cedev)
{
struct pit_data *data = clkevt_to_pit_data(cedev);
/* Disable timer */
pit_write(data->base, AT91_PIT_MR, 0);
}
static void at91sam926x_pit_reset(struct pit_data *data)
{
/* Disable timer and irqs */
pit_write(data->base, AT91_PIT_MR, 0);
/* Clear any pending interrupts, wait for PIT to stop counting */
while (PIT_CPIV(pit_read(data->base, AT91_PIT_PIVR)) != 0)
cpu_relax();
/* Start PIT but don't enable IRQ */
pit_write(data->base, AT91_PIT_MR,
(data->cycle - 1) | AT91_PIT_PITEN);
}
static void at91sam926x_pit_resume(struct clock_event_device *cedev)
{
struct pit_data *data = clkevt_to_pit_data(cedev);
at91sam926x_pit_reset(data);
}
/*
* IRQ handler for the timer.
*/
static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id)
{
struct pit_data *data = dev_id;
/* The PIT interrupt may be disabled, and is shared */
if (clockevent_state_periodic(&data->clkevt) &&
(pit_read(data->base, AT91_PIT_SR) & AT91_PIT_PITS)) {
/* Get number of ticks performed before irq, and ack it */
data->cnt += data->cycle * PIT_PICNT(pit_read(data->base,
AT91_PIT_PIVR));
data->clkevt.event_handler(&data->clkevt);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
/*
* Set up both clocksource and clockevent support.
*/
static int __init at91sam926x_pit_dt_init(struct device_node *node)
{
unsigned long pit_rate;
unsigned bits;
int ret;
struct pit_data *data;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->base = of_iomap(node, 0);
if (!data->base) {
pr_err("Could not map PIT address\n");
ret = -ENXIO;
goto exit;
}
data->mck = of_clk_get(node, 0);
if (IS_ERR(data->mck)) {
pr_err("Unable to get mck clk\n");
ret = PTR_ERR(data->mck);
goto exit;
}
ret = clk_prepare_enable(data->mck);
if (ret) {
pr_err("Unable to enable mck\n");
goto exit;
}
/* Get the interrupts property */
data->irq = irq_of_parse_and_map(node, 0);
if (!data->irq) {
pr_err("Unable to get IRQ from DT\n");
ret = -EINVAL;
goto exit;
}
/*
* Use our actual MCK to figure out how many MCK/16 ticks per
* 1/HZ period (instead of a compile-time constant LATCH).
*/
pit_rate = clk_get_rate(data->mck) / 16;
data->cycle = DIV_ROUND_CLOSEST(pit_rate, HZ);
WARN_ON(((data->cycle - 1) & ~AT91_PIT_PIV) != 0);
/* Initialize and enable the timer */
at91sam926x_pit_reset(data);
/*
* Register clocksource. The high order bits of PIV are unused,
* so this isn't a 32-bit counter unless we get clockevent irqs.
*/
bits = 12 /* PICNT */ + ilog2(data->cycle) /* PIV */;
data->clksrc.mask = CLOCKSOURCE_MASK(bits);
data->clksrc.name = "pit";
data->clksrc.rating = 175;
data->clksrc.read = read_pit_clk;
data->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS;
ret = clocksource_register_hz(&data->clksrc, pit_rate);
if (ret) {
pr_err("Failed to register clocksource\n");
goto exit;
}
/* Set up irq handler */
ret = request_irq(data->irq, at91sam926x_pit_interrupt,
IRQF_SHARED | IRQF_TIMER | IRQF_IRQPOLL,
"at91_tick", data);
if (ret) {
pr_err("Unable to setup IRQ\n");
clocksource_unregister(&data->clksrc);
goto exit;
}
/* Set up and register clockevents */
data->clkevt.name = "pit";
data->clkevt.features = CLOCK_EVT_FEAT_PERIODIC;
data->clkevt.shift = 32;
data->clkevt.mult = div_sc(pit_rate, NSEC_PER_SEC, data->clkevt.shift);
data->clkevt.rating = 100;
data->clkevt.cpumask = cpumask_of(0);
data->clkevt.set_state_shutdown = pit_clkevt_shutdown;
data->clkevt.set_state_periodic = pit_clkevt_set_periodic;
data->clkevt.resume = at91sam926x_pit_resume;
data->clkevt.suspend = at91sam926x_pit_suspend;
clockevents_register_device(&data->clkevt);
return 0;
exit:
kfree(data);
return ret;
}
TIMER_OF_DECLARE(at91sam926x_pit, "atmel,at91sam9260-pit",
at91sam926x_pit_dt_init);
+264
View File
@@ -0,0 +1,264 @@
/*
* linux/arch/arm/mach-at91/at91rm9200_time.c
*
* Copyright (C) 2003 SAN People
* Copyright (C) 2003 ATMEL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/export.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/atmel-st.h>
#include <linux/of_irq.h>
#include <linux/regmap.h>
static unsigned long last_crtr;
static u32 irqmask;
static struct clock_event_device clkevt;
static struct regmap *regmap_st;
static int timer_latch;
/*
* The ST_CRTR is updated asynchronously to the master clock ... but
* the updates as seen by the CPU don't seem to be strictly monotonic.
* Waiting until we read the same value twice avoids glitching.
*/
static inline unsigned long read_CRTR(void)
{
unsigned int x1, x2;
regmap_read(regmap_st, AT91_ST_CRTR, &x1);
do {
regmap_read(regmap_st, AT91_ST_CRTR, &x2);
if (x1 == x2)
break;
x1 = x2;
} while (1);
return x1;
}
/*
* IRQ handler for the timer.
*/
static irqreturn_t at91rm9200_timer_interrupt(int irq, void *dev_id)
{
u32 sr;
regmap_read(regmap_st, AT91_ST_SR, &sr);
sr &= irqmask;
/*
* irqs should be disabled here, but as the irq is shared they are only
* guaranteed to be off if the timer irq is registered first.
*/
WARN_ON_ONCE(!irqs_disabled());
/* simulate "oneshot" timer with alarm */
if (sr & AT91_ST_ALMS) {
clkevt.event_handler(&clkevt);
return IRQ_HANDLED;
}
/* periodic mode should handle delayed ticks */
if (sr & AT91_ST_PITS) {
u32 crtr = read_CRTR();
while (((crtr - last_crtr) & AT91_ST_CRTV) >= timer_latch) {
last_crtr += timer_latch;
clkevt.event_handler(&clkevt);
}
return IRQ_HANDLED;
}
/* this irq is shared ... */
return IRQ_NONE;
}
static u64 read_clk32k(struct clocksource *cs)
{
return read_CRTR();
}
static struct clocksource clk32k = {
.name = "32k_counter",
.rating = 150,
.read = read_clk32k,
.mask = CLOCKSOURCE_MASK(20),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static void clkdev32k_disable_and_flush_irq(void)
{
unsigned int val;
/* Disable and flush pending timer interrupts */
regmap_write(regmap_st, AT91_ST_IDR, AT91_ST_PITS | AT91_ST_ALMS);
regmap_read(regmap_st, AT91_ST_SR, &val);
last_crtr = read_CRTR();
}
static int clkevt32k_shutdown(struct clock_event_device *evt)
{
clkdev32k_disable_and_flush_irq();
irqmask = 0;
regmap_write(regmap_st, AT91_ST_IER, irqmask);
return 0;
}
static int clkevt32k_set_oneshot(struct clock_event_device *dev)
{
clkdev32k_disable_and_flush_irq();
/*
* ALM for oneshot irqs, set by next_event()
* before 32 seconds have passed.
*/
irqmask = AT91_ST_ALMS;
regmap_write(regmap_st, AT91_ST_RTAR, last_crtr);
regmap_write(regmap_st, AT91_ST_IER, irqmask);
return 0;
}
static int clkevt32k_set_periodic(struct clock_event_device *dev)
{
clkdev32k_disable_and_flush_irq();
/* PIT for periodic irqs; fixed rate of 1/HZ */
irqmask = AT91_ST_PITS;
regmap_write(regmap_st, AT91_ST_PIMR, timer_latch);
regmap_write(regmap_st, AT91_ST_IER, irqmask);
return 0;
}
static int
clkevt32k_next_event(unsigned long delta, struct clock_event_device *dev)
{
u32 alm;
int status = 0;
unsigned int val;
BUG_ON(delta < 2);
/* The alarm IRQ uses absolute time (now+delta), not the relative
* time (delta) in our calling convention. Like all clockevents
* using such "match" hardware, we have a race to defend against.
*
* Our defense here is to have set up the clockevent device so the
* delta is at least two. That way we never end up writing RTAR
* with the value then held in CRTR ... which would mean the match
* wouldn't trigger until 32 seconds later, after CRTR wraps.
*/
alm = read_CRTR();
/* Cancel any pending alarm; flush any pending IRQ */
regmap_write(regmap_st, AT91_ST_RTAR, alm);
regmap_read(regmap_st, AT91_ST_SR, &val);
/* Schedule alarm by writing RTAR. */
alm += delta;
regmap_write(regmap_st, AT91_ST_RTAR, alm);
return status;
}
static struct clock_event_device clkevt = {
.name = "at91_tick",
.features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT,
.rating = 150,
.set_next_event = clkevt32k_next_event,
.set_state_shutdown = clkevt32k_shutdown,
.set_state_periodic = clkevt32k_set_periodic,
.set_state_oneshot = clkevt32k_set_oneshot,
.tick_resume = clkevt32k_shutdown,
};
/*
* ST (system timer) module supports both clockevents and clocksource.
*/
static int __init atmel_st_timer_init(struct device_node *node)
{
struct clk *sclk;
unsigned int sclk_rate, val;
int irq, ret;
regmap_st = syscon_node_to_regmap(node);
if (IS_ERR(regmap_st)) {
pr_err("Unable to get regmap\n");
return PTR_ERR(regmap_st);
}
/* Disable all timer interrupts, and clear any pending ones */
regmap_write(regmap_st, AT91_ST_IDR,
AT91_ST_PITS | AT91_ST_WDOVF | AT91_ST_RTTINC | AT91_ST_ALMS);
regmap_read(regmap_st, AT91_ST_SR, &val);
/* Get the interrupts property */
irq = irq_of_parse_and_map(node, 0);
if (!irq) {
pr_err("Unable to get IRQ from DT\n");
return -EINVAL;
}
/* Make IRQs happen for the system timer */
ret = request_irq(irq, at91rm9200_timer_interrupt,
IRQF_SHARED | IRQF_TIMER | IRQF_IRQPOLL,
"at91_tick", regmap_st);
if (ret) {
pr_err("Unable to setup IRQ\n");
return ret;
}
sclk = of_clk_get(node, 0);
if (IS_ERR(sclk)) {
pr_err("Unable to get slow clock\n");
return PTR_ERR(sclk);
}
ret = clk_prepare_enable(sclk);
if (ret) {
pr_err("Could not enable slow clock\n");
return ret;
}
sclk_rate = clk_get_rate(sclk);
if (!sclk_rate) {
pr_err("Invalid slow clock rate\n");
return -EINVAL;
}
timer_latch = (sclk_rate + HZ / 2) / HZ;
/* The 32KiHz "Slow Clock" (tick every 30517.58 nanoseconds) is used
* directly for the clocksource and all clockevents, after adjusting
* its prescaler from the 1 Hz default.
*/
regmap_write(regmap_st, AT91_ST_RTMR, 1);
/* Setup timer clockevent, with minimum of two ticks (important!!) */
clkevt.cpumask = cpumask_of(0);
clockevents_config_and_register(&clkevt, sclk_rate,
2, AT91_ST_ALMV);
/* register clocksource */
return clocksource_register_hz(&clk32k, sclk_rate);
}
TIMER_OF_DECLARE(atmel_st_timer, "atmel,at91rm9200-st",
atmel_st_timer_init);
+207
View File
@@ -0,0 +1,207 @@
/*
* Conexant Digicolor timer driver
*
* Author: Baruch Siach <baruch@tkos.co.il>
*
* Copyright (C) 2014 Paradox Innovation Ltd.
*
* Based on:
* Allwinner SoCs hstimer driver
*
* Copyright (C) 2013 Maxime Ripard
*
* Maxime Ripard <maxime.ripard@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
/*
* Conexant Digicolor SoCs have 8 configurable timers, named from "Timer A" to
* "Timer H". Timer A is the only one with watchdog support, so it is dedicated
* to the watchdog driver. This driver uses Timer B for sched_clock(), and
* Timer C for clockevents.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqreturn.h>
#include <linux/sched/clock.h>
#include <linux/sched_clock.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
enum {
TIMER_A,
TIMER_B,
TIMER_C,
TIMER_D,
TIMER_E,
TIMER_F,
TIMER_G,
TIMER_H,
};
#define CONTROL(t) ((t)*8)
#define COUNT(t) ((t)*8 + 4)
#define CONTROL_DISABLE 0
#define CONTROL_ENABLE BIT(0)
#define CONTROL_MODE(m) ((m) << 4)
#define CONTROL_MODE_ONESHOT CONTROL_MODE(1)
#define CONTROL_MODE_PERIODIC CONTROL_MODE(2)
struct digicolor_timer {
struct clock_event_device ce;
void __iomem *base;
u32 ticks_per_jiffy;
int timer_id; /* one of TIMER_* */
};
static struct digicolor_timer *dc_timer(struct clock_event_device *ce)
{
return container_of(ce, struct digicolor_timer, ce);
}
static inline void dc_timer_disable(struct clock_event_device *ce)
{
struct digicolor_timer *dt = dc_timer(ce);
writeb(CONTROL_DISABLE, dt->base + CONTROL(dt->timer_id));
}
static inline void dc_timer_enable(struct clock_event_device *ce, u32 mode)
{
struct digicolor_timer *dt = dc_timer(ce);
writeb(CONTROL_ENABLE | mode, dt->base + CONTROL(dt->timer_id));
}
static inline void dc_timer_set_count(struct clock_event_device *ce,
unsigned long count)
{
struct digicolor_timer *dt = dc_timer(ce);
writel(count, dt->base + COUNT(dt->timer_id));
}
static int digicolor_clkevt_shutdown(struct clock_event_device *ce)
{
dc_timer_disable(ce);
return 0;
}
static int digicolor_clkevt_set_oneshot(struct clock_event_device *ce)
{
dc_timer_disable(ce);
dc_timer_enable(ce, CONTROL_MODE_ONESHOT);
return 0;
}
static int digicolor_clkevt_set_periodic(struct clock_event_device *ce)
{
struct digicolor_timer *dt = dc_timer(ce);
dc_timer_disable(ce);
dc_timer_set_count(ce, dt->ticks_per_jiffy);
dc_timer_enable(ce, CONTROL_MODE_PERIODIC);
return 0;
}
static int digicolor_clkevt_next_event(unsigned long evt,
struct clock_event_device *ce)
{
dc_timer_disable(ce);
dc_timer_set_count(ce, evt);
dc_timer_enable(ce, CONTROL_MODE_ONESHOT);
return 0;
}
static struct digicolor_timer dc_timer_dev = {
.ce = {
.name = "digicolor_tick",
.rating = 340,
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
.set_state_shutdown = digicolor_clkevt_shutdown,
.set_state_periodic = digicolor_clkevt_set_periodic,
.set_state_oneshot = digicolor_clkevt_set_oneshot,
.tick_resume = digicolor_clkevt_shutdown,
.set_next_event = digicolor_clkevt_next_event,
},
.timer_id = TIMER_C,
};
static irqreturn_t digicolor_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
evt->event_handler(evt);
return IRQ_HANDLED;
}
static u64 notrace digicolor_timer_sched_read(void)
{
return ~readl(dc_timer_dev.base + COUNT(TIMER_B));
}
static int __init digicolor_timer_init(struct device_node *node)
{
unsigned long rate;
struct clk *clk;
int ret, irq;
/*
* timer registers are shared with the watchdog timer;
* don't map exclusively
*/
dc_timer_dev.base = of_iomap(node, 0);
if (!dc_timer_dev.base) {
pr_err("Can't map registers\n");
return -ENXIO;
}
irq = irq_of_parse_and_map(node, dc_timer_dev.timer_id);
if (irq <= 0) {
pr_err("Can't parse IRQ\n");
return -EINVAL;
}
clk = of_clk_get(node, 0);
if (IS_ERR(clk)) {
pr_err("Can't get timer clock\n");
return PTR_ERR(clk);
}
clk_prepare_enable(clk);
rate = clk_get_rate(clk);
dc_timer_dev.ticks_per_jiffy = DIV_ROUND_UP(rate, HZ);
writeb(CONTROL_DISABLE, dc_timer_dev.base + CONTROL(TIMER_B));
writel(UINT_MAX, dc_timer_dev.base + COUNT(TIMER_B));
writeb(CONTROL_ENABLE, dc_timer_dev.base + CONTROL(TIMER_B));
sched_clock_register(digicolor_timer_sched_read, 32, rate);
clocksource_mmio_init(dc_timer_dev.base + COUNT(TIMER_B), node->name,
rate, 340, 32, clocksource_mmio_readl_down);
ret = request_irq(irq, digicolor_timer_interrupt,
IRQF_TIMER | IRQF_IRQPOLL, "digicolor_timerC",
&dc_timer_dev.ce);
if (ret) {
pr_warn("request of timer irq %d failed (%d)\n", irq, ret);
return ret;
}
dc_timer_dev.ce.cpumask = cpu_possible_mask;
dc_timer_dev.ce.irq = irq;
clockevents_config_and_register(&dc_timer_dev.ce, rate, 0, 0xffffffff);
return 0;
}
TIMER_OF_DECLARE(conexant_digicolor, "cnxt,cx92755-timer",
digicolor_timer_init);
+421
View File
@@ -0,0 +1,421 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Faraday Technology FTTMR010 timer driver
* Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
*
* Based on a rewrite of arch/arm/mach-gemini/timer.c:
* Copyright (C) 2001-2006 Storlink, Corp.
* Copyright (C) 2008-2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
*/
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/sched_clock.h>
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/bitops.h>
#include <linux/delay.h>
/*
* Register definitions common for all the timer variants.
*/
#define TIMER1_COUNT (0x00)
#define TIMER1_LOAD (0x04)
#define TIMER1_MATCH1 (0x08)
#define TIMER1_MATCH2 (0x0c)
#define TIMER2_COUNT (0x10)
#define TIMER2_LOAD (0x14)
#define TIMER2_MATCH1 (0x18)
#define TIMER2_MATCH2 (0x1c)
#define TIMER3_COUNT (0x20)
#define TIMER3_LOAD (0x24)
#define TIMER3_MATCH1 (0x28)
#define TIMER3_MATCH2 (0x2c)
#define TIMER_CR (0x30)
/*
* Control register (TMC30) bit fields for fttmr010/gemini/moxart timers.
*/
#define TIMER_1_CR_ENABLE BIT(0)
#define TIMER_1_CR_CLOCK BIT(1)
#define TIMER_1_CR_INT BIT(2)
#define TIMER_2_CR_ENABLE BIT(3)
#define TIMER_2_CR_CLOCK BIT(4)
#define TIMER_2_CR_INT BIT(5)
#define TIMER_3_CR_ENABLE BIT(6)
#define TIMER_3_CR_CLOCK BIT(7)
#define TIMER_3_CR_INT BIT(8)
#define TIMER_1_CR_UPDOWN BIT(9)
#define TIMER_2_CR_UPDOWN BIT(10)
#define TIMER_3_CR_UPDOWN BIT(11)
/*
* Control register (TMC30) bit fields for aspeed ast2400/ast2500 timers.
* The aspeed timers move bits around in the control register and lacks
* bits for setting the timer to count upwards.
*/
#define TIMER_1_CR_ASPEED_ENABLE BIT(0)
#define TIMER_1_CR_ASPEED_CLOCK BIT(1)
#define TIMER_1_CR_ASPEED_INT BIT(2)
#define TIMER_2_CR_ASPEED_ENABLE BIT(4)
#define TIMER_2_CR_ASPEED_CLOCK BIT(5)
#define TIMER_2_CR_ASPEED_INT BIT(6)
#define TIMER_3_CR_ASPEED_ENABLE BIT(8)
#define TIMER_3_CR_ASPEED_CLOCK BIT(9)
#define TIMER_3_CR_ASPEED_INT BIT(10)
/*
* Interrupt status/mask register definitions for fttmr010/gemini/moxart
* timers.
* The registers don't exist and they are not needed on aspeed timers
* because:
* - aspeed timer overflow interrupt is controlled by bits in Control
* Register (TMC30).
* - aspeed timers always generate interrupt when either one of the
* Match registers equals to Status register.
*/
#define TIMER_INTR_STATE (0x34)
#define TIMER_INTR_MASK (0x38)
#define TIMER_1_INT_MATCH1 BIT(0)
#define TIMER_1_INT_MATCH2 BIT(1)
#define TIMER_1_INT_OVERFLOW BIT(2)
#define TIMER_2_INT_MATCH1 BIT(3)
#define TIMER_2_INT_MATCH2 BIT(4)
#define TIMER_2_INT_OVERFLOW BIT(5)
#define TIMER_3_INT_MATCH1 BIT(6)
#define TIMER_3_INT_MATCH2 BIT(7)
#define TIMER_3_INT_OVERFLOW BIT(8)
#define TIMER_INT_ALL_MASK 0x1ff
struct fttmr010 {
void __iomem *base;
unsigned int tick_rate;
bool is_aspeed;
u32 t1_enable_val;
struct clock_event_device clkevt;
#ifdef CONFIG_ARM
struct delay_timer delay_timer;
#endif
};
/*
* A local singleton used by sched_clock and delay timer reads, which are
* fast and stateless
*/
static struct fttmr010 *local_fttmr;
static inline struct fttmr010 *to_fttmr010(struct clock_event_device *evt)
{
return container_of(evt, struct fttmr010, clkevt);
}
static unsigned long fttmr010_read_current_timer_up(void)
{
return readl(local_fttmr->base + TIMER2_COUNT);
}
static unsigned long fttmr010_read_current_timer_down(void)
{
return ~readl(local_fttmr->base + TIMER2_COUNT);
}
static u64 notrace fttmr010_read_sched_clock_up(void)
{
return fttmr010_read_current_timer_up();
}
static u64 notrace fttmr010_read_sched_clock_down(void)
{
return fttmr010_read_current_timer_down();
}
static int fttmr010_timer_set_next_event(unsigned long cycles,
struct clock_event_device *evt)
{
struct fttmr010 *fttmr010 = to_fttmr010(evt);
u32 cr;
/* Stop */
cr = readl(fttmr010->base + TIMER_CR);
cr &= ~fttmr010->t1_enable_val;
writel(cr, fttmr010->base + TIMER_CR);
if (fttmr010->is_aspeed) {
/*
* ASPEED Timer Controller will load TIMER1_LOAD register
* into TIMER1_COUNT register when the timer is re-enabled.
*/
writel(cycles, fttmr010->base + TIMER1_LOAD);
} else {
/* Setup the match register forward in time */
cr = readl(fttmr010->base + TIMER1_COUNT);
writel(cr + cycles, fttmr010->base + TIMER1_MATCH1);
}
/* Start */
cr = readl(fttmr010->base + TIMER_CR);
cr |= fttmr010->t1_enable_val;
writel(cr, fttmr010->base + TIMER_CR);
return 0;
}
static int fttmr010_timer_shutdown(struct clock_event_device *evt)
{
struct fttmr010 *fttmr010 = to_fttmr010(evt);
u32 cr;
/* Stop */
cr = readl(fttmr010->base + TIMER_CR);
cr &= ~fttmr010->t1_enable_val;
writel(cr, fttmr010->base + TIMER_CR);
return 0;
}
static int fttmr010_timer_set_oneshot(struct clock_event_device *evt)
{
struct fttmr010 *fttmr010 = to_fttmr010(evt);
u32 cr;
/* Stop */
cr = readl(fttmr010->base + TIMER_CR);
cr &= ~fttmr010->t1_enable_val;
writel(cr, fttmr010->base + TIMER_CR);
/* Setup counter start from 0 or ~0 */
writel(0, fttmr010->base + TIMER1_COUNT);
if (fttmr010->is_aspeed) {
writel(~0, fttmr010->base + TIMER1_LOAD);
} else {
writel(0, fttmr010->base + TIMER1_LOAD);
/* Enable interrupt */
cr = readl(fttmr010->base + TIMER_INTR_MASK);
cr &= ~(TIMER_1_INT_OVERFLOW | TIMER_1_INT_MATCH2);
cr |= TIMER_1_INT_MATCH1;
writel(cr, fttmr010->base + TIMER_INTR_MASK);
}
return 0;
}
static int fttmr010_timer_set_periodic(struct clock_event_device *evt)
{
struct fttmr010 *fttmr010 = to_fttmr010(evt);
u32 period = DIV_ROUND_CLOSEST(fttmr010->tick_rate, HZ);
u32 cr;
/* Stop */
cr = readl(fttmr010->base + TIMER_CR);
cr &= ~fttmr010->t1_enable_val;
writel(cr, fttmr010->base + TIMER_CR);
/* Setup timer to fire at 1/HZ intervals. */
if (fttmr010->is_aspeed) {
writel(period, fttmr010->base + TIMER1_LOAD);
} else {
cr = 0xffffffff - (period - 1);
writel(cr, fttmr010->base + TIMER1_COUNT);
writel(cr, fttmr010->base + TIMER1_LOAD);
/* Enable interrupt on overflow */
cr = readl(fttmr010->base + TIMER_INTR_MASK);
cr &= ~(TIMER_1_INT_MATCH1 | TIMER_1_INT_MATCH2);
cr |= TIMER_1_INT_OVERFLOW;
writel(cr, fttmr010->base + TIMER_INTR_MASK);
}
/* Start the timer */
cr = readl(fttmr010->base + TIMER_CR);
cr |= fttmr010->t1_enable_val;
writel(cr, fttmr010->base + TIMER_CR);
return 0;
}
/*
* IRQ handler for the timer
*/
static irqreturn_t fttmr010_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
evt->event_handler(evt);
return IRQ_HANDLED;
}
static int __init fttmr010_common_init(struct device_node *np, bool is_aspeed)
{
struct fttmr010 *fttmr010;
int irq;
struct clk *clk;
int ret;
u32 val;
/*
* These implementations require a clock reference.
* FIXME: we currently only support clocking using PCLK
* and using EXTCLK is not supported in the driver.
*/
clk = of_clk_get_by_name(np, "PCLK");
if (IS_ERR(clk)) {
pr_err("could not get PCLK\n");
return PTR_ERR(clk);
}
ret = clk_prepare_enable(clk);
if (ret) {
pr_err("failed to enable PCLK\n");
return ret;
}
fttmr010 = kzalloc(sizeof(*fttmr010), GFP_KERNEL);
if (!fttmr010) {
ret = -ENOMEM;
goto out_disable_clock;
}
fttmr010->tick_rate = clk_get_rate(clk);
fttmr010->base = of_iomap(np, 0);
if (!fttmr010->base) {
pr_err("Can't remap registers\n");
ret = -ENXIO;
goto out_free;
}
/* IRQ for timer 1 */
irq = irq_of_parse_and_map(np, 0);
if (irq <= 0) {
pr_err("Can't parse IRQ\n");
ret = -EINVAL;
goto out_unmap;
}
/*
* The Aspeed timers move bits around in the control register.
*/
if (is_aspeed) {
fttmr010->t1_enable_val = TIMER_1_CR_ASPEED_ENABLE |
TIMER_1_CR_ASPEED_INT;
fttmr010->is_aspeed = true;
} else {
fttmr010->t1_enable_val = TIMER_1_CR_ENABLE | TIMER_1_CR_INT;
/*
* Reset the interrupt mask and status
*/
writel(TIMER_INT_ALL_MASK, fttmr010->base + TIMER_INTR_MASK);
writel(0, fttmr010->base + TIMER_INTR_STATE);
}
/*
* Enable timer 1 count up, timer 2 count up, except on Aspeed,
* where everything just counts down.
*/
if (is_aspeed)
val = TIMER_2_CR_ASPEED_ENABLE;
else {
val = TIMER_2_CR_ENABLE | TIMER_1_CR_UPDOWN |
TIMER_2_CR_UPDOWN;
}
writel(val, fttmr010->base + TIMER_CR);
/*
* Setup free-running clocksource timer (interrupts
* disabled.)
*/
local_fttmr = fttmr010;
writel(0, fttmr010->base + TIMER2_COUNT);
writel(0, fttmr010->base + TIMER2_MATCH1);
writel(0, fttmr010->base + TIMER2_MATCH2);
if (fttmr010->is_aspeed) {
writel(~0, fttmr010->base + TIMER2_LOAD);
clocksource_mmio_init(fttmr010->base + TIMER2_COUNT,
"FTTMR010-TIMER2",
fttmr010->tick_rate,
300, 32, clocksource_mmio_readl_down);
sched_clock_register(fttmr010_read_sched_clock_down, 32,
fttmr010->tick_rate);
} else {
writel(0, fttmr010->base + TIMER2_LOAD);
clocksource_mmio_init(fttmr010->base + TIMER2_COUNT,
"FTTMR010-TIMER2",
fttmr010->tick_rate,
300, 32, clocksource_mmio_readl_up);
sched_clock_register(fttmr010_read_sched_clock_up, 32,
fttmr010->tick_rate);
}
/*
* Setup clockevent timer (interrupt-driven) on timer 1.
*/
writel(0, fttmr010->base + TIMER1_COUNT);
writel(0, fttmr010->base + TIMER1_LOAD);
writel(0, fttmr010->base + TIMER1_MATCH1);
writel(0, fttmr010->base + TIMER1_MATCH2);
ret = request_irq(irq, fttmr010_timer_interrupt, IRQF_TIMER,
"FTTMR010-TIMER1", &fttmr010->clkevt);
if (ret) {
pr_err("FTTMR010-TIMER1 no IRQ\n");
goto out_unmap;
}
fttmr010->clkevt.name = "FTTMR010-TIMER1";
/* Reasonably fast and accurate clock event */
fttmr010->clkevt.rating = 300;
fttmr010->clkevt.features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT;
fttmr010->clkevt.set_next_event = fttmr010_timer_set_next_event;
fttmr010->clkevt.set_state_shutdown = fttmr010_timer_shutdown;
fttmr010->clkevt.set_state_periodic = fttmr010_timer_set_periodic;
fttmr010->clkevt.set_state_oneshot = fttmr010_timer_set_oneshot;
fttmr010->clkevt.tick_resume = fttmr010_timer_shutdown;
fttmr010->clkevt.cpumask = cpumask_of(0);
fttmr010->clkevt.irq = irq;
clockevents_config_and_register(&fttmr010->clkevt,
fttmr010->tick_rate,
1, 0xffffffff);
#ifdef CONFIG_ARM
/* Also use this timer for delays */
if (fttmr010->is_aspeed)
fttmr010->delay_timer.read_current_timer =
fttmr010_read_current_timer_down;
else
fttmr010->delay_timer.read_current_timer =
fttmr010_read_current_timer_up;
fttmr010->delay_timer.freq = fttmr010->tick_rate;
register_current_timer_delay(&fttmr010->delay_timer);
#endif
return 0;
out_unmap:
iounmap(fttmr010->base);
out_free:
kfree(fttmr010);
out_disable_clock:
clk_disable_unprepare(clk);
return ret;
}
static __init int aspeed_timer_init(struct device_node *np)
{
return fttmr010_common_init(np, true);
}
static __init int fttmr010_timer_init(struct device_node *np)
{
return fttmr010_common_init(np, false);
}
TIMER_OF_DECLARE(fttmr010, "faraday,fttmr010", fttmr010_timer_init);
TIMER_OF_DECLARE(gemini, "cortina,gemini-timer", fttmr010_timer_init);
TIMER_OF_DECLARE(moxart, "moxa,moxart-timer", fttmr010_timer_init);
TIMER_OF_DECLARE(ast2400, "aspeed,ast2400-timer", aspeed_timer_init);
TIMER_OF_DECLARE(ast2500, "aspeed,ast2500-timer", aspeed_timer_init);
+543
View File
@@ -0,0 +1,543 @@
// SPDX-License-Identifier: GPL-2.0+
//
// Copyright (C) 2000-2001 Deep Blue Solutions
// Copyright (C) 2002 Shane Nay (shane@minirl.com)
// Copyright (C) 2006-2007 Pavel Pisa (ppisa@pikron.com)
// Copyright (C) 2008 Juergen Beisert (kernel@pengutronix.de)
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/clockchips.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/sched_clock.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <soc/imx/timer.h>
/*
* There are 4 versions of the timer hardware on Freescale MXC hardware.
* - MX1/MXL
* - MX21, MX27.
* - MX25, MX31, MX35, MX37, MX51, MX6Q(rev1.0)
* - MX6DL, MX6SX, MX6Q(rev1.1+)
*/
/* defines common for all i.MX */
#define MXC_TCTL 0x00
#define MXC_TCTL_TEN (1 << 0) /* Enable module */
#define MXC_TPRER 0x04
/* MX1, MX21, MX27 */
#define MX1_2_TCTL_CLK_PCLK1 (1 << 1)
#define MX1_2_TCTL_IRQEN (1 << 4)
#define MX1_2_TCTL_FRR (1 << 8)
#define MX1_2_TCMP 0x08
#define MX1_2_TCN 0x10
#define MX1_2_TSTAT 0x14
/* MX21, MX27 */
#define MX2_TSTAT_CAPT (1 << 1)
#define MX2_TSTAT_COMP (1 << 0)
/* MX31, MX35, MX25, MX5, MX6 */
#define V2_TCTL_WAITEN (1 << 3) /* Wait enable mode */
#define V2_TCTL_CLK_IPG (1 << 6)
#define V2_TCTL_CLK_PER (2 << 6)
#define V2_TCTL_CLK_OSC_DIV8 (5 << 6)
#define V2_TCTL_FRR (1 << 9)
#define V2_TCTL_24MEN (1 << 10)
#define V2_TPRER_PRE24M 12
#define V2_IR 0x0c
#define V2_TSTAT 0x08
#define V2_TSTAT_OF1 (1 << 0)
#define V2_TCN 0x24
#define V2_TCMP 0x10
#define V2_TIMER_RATE_OSC_DIV8 3000000
struct imx_timer {
enum imx_gpt_type type;
void __iomem *base;
int irq;
struct clk *clk_per;
struct clk *clk_ipg;
const struct imx_gpt_data *gpt;
struct clock_event_device ced;
struct irqaction act;
};
struct imx_gpt_data {
int reg_tstat;
int reg_tcn;
int reg_tcmp;
void (*gpt_setup_tctl)(struct imx_timer *imxtm);
void (*gpt_irq_enable)(struct imx_timer *imxtm);
void (*gpt_irq_disable)(struct imx_timer *imxtm);
void (*gpt_irq_acknowledge)(struct imx_timer *imxtm);
int (*set_next_event)(unsigned long evt,
struct clock_event_device *ced);
};
static inline struct imx_timer *to_imx_timer(struct clock_event_device *ced)
{
return container_of(ced, struct imx_timer, ced);
}
static void imx1_gpt_irq_disable(struct imx_timer *imxtm)
{
unsigned int tmp;
tmp = readl_relaxed(imxtm->base + MXC_TCTL);
writel_relaxed(tmp & ~MX1_2_TCTL_IRQEN, imxtm->base + MXC_TCTL);
}
#define imx21_gpt_irq_disable imx1_gpt_irq_disable
static void imx31_gpt_irq_disable(struct imx_timer *imxtm)
{
writel_relaxed(0, imxtm->base + V2_IR);
}
#define imx6dl_gpt_irq_disable imx31_gpt_irq_disable
static void imx1_gpt_irq_enable(struct imx_timer *imxtm)
{
unsigned int tmp;
tmp = readl_relaxed(imxtm->base + MXC_TCTL);
writel_relaxed(tmp | MX1_2_TCTL_IRQEN, imxtm->base + MXC_TCTL);
}
#define imx21_gpt_irq_enable imx1_gpt_irq_enable
static void imx31_gpt_irq_enable(struct imx_timer *imxtm)
{
writel_relaxed(1<<0, imxtm->base + V2_IR);
}
#define imx6dl_gpt_irq_enable imx31_gpt_irq_enable
static void imx1_gpt_irq_acknowledge(struct imx_timer *imxtm)
{
writel_relaxed(0, imxtm->base + MX1_2_TSTAT);
}
static void imx21_gpt_irq_acknowledge(struct imx_timer *imxtm)
{
writel_relaxed(MX2_TSTAT_CAPT | MX2_TSTAT_COMP,
imxtm->base + MX1_2_TSTAT);
}
static void imx31_gpt_irq_acknowledge(struct imx_timer *imxtm)
{
writel_relaxed(V2_TSTAT_OF1, imxtm->base + V2_TSTAT);
}
#define imx6dl_gpt_irq_acknowledge imx31_gpt_irq_acknowledge
static void __iomem *sched_clock_reg;
static u64 notrace mxc_read_sched_clock(void)
{
return sched_clock_reg ? readl_relaxed(sched_clock_reg) : 0;
}
static struct delay_timer imx_delay_timer;
static unsigned long imx_read_current_timer(void)
{
return readl_relaxed(sched_clock_reg);
}
static int __init mxc_clocksource_init(struct imx_timer *imxtm)
{
unsigned int c = clk_get_rate(imxtm->clk_per);
void __iomem *reg = imxtm->base + imxtm->gpt->reg_tcn;
imx_delay_timer.read_current_timer = &imx_read_current_timer;
imx_delay_timer.freq = c;
register_current_timer_delay(&imx_delay_timer);
sched_clock_reg = reg;
sched_clock_register(mxc_read_sched_clock, 32, c);
return clocksource_mmio_init(reg, "mxc_timer1", c, 200, 32,
clocksource_mmio_readl_up);
}
/* clock event */
static int mx1_2_set_next_event(unsigned long evt,
struct clock_event_device *ced)
{
struct imx_timer *imxtm = to_imx_timer(ced);
unsigned long tcmp;
tcmp = readl_relaxed(imxtm->base + MX1_2_TCN) + evt;
writel_relaxed(tcmp, imxtm->base + MX1_2_TCMP);
return (int)(tcmp - readl_relaxed(imxtm->base + MX1_2_TCN)) < 0 ?
-ETIME : 0;
}
static int v2_set_next_event(unsigned long evt,
struct clock_event_device *ced)
{
struct imx_timer *imxtm = to_imx_timer(ced);
unsigned long tcmp;
tcmp = readl_relaxed(imxtm->base + V2_TCN) + evt;
writel_relaxed(tcmp, imxtm->base + V2_TCMP);
return evt < 0x7fffffff &&
(int)(tcmp - readl_relaxed(imxtm->base + V2_TCN)) < 0 ?
-ETIME : 0;
}
static int mxc_shutdown(struct clock_event_device *ced)
{
struct imx_timer *imxtm = to_imx_timer(ced);
unsigned long flags;
u32 tcn;
/*
* The timer interrupt generation is disabled at least
* for enough time to call mxc_set_next_event()
*/
local_irq_save(flags);
/* Disable interrupt in GPT module */
imxtm->gpt->gpt_irq_disable(imxtm);
tcn = readl_relaxed(imxtm->base + imxtm->gpt->reg_tcn);
/* Set event time into far-far future */
writel_relaxed(tcn - 3, imxtm->base + imxtm->gpt->reg_tcmp);
/* Clear pending interrupt */
imxtm->gpt->gpt_irq_acknowledge(imxtm);
#ifdef DEBUG
printk(KERN_INFO "%s: changing mode\n", __func__);
#endif /* DEBUG */
local_irq_restore(flags);
return 0;
}
static int mxc_set_oneshot(struct clock_event_device *ced)
{
struct imx_timer *imxtm = to_imx_timer(ced);
unsigned long flags;
/*
* The timer interrupt generation is disabled at least
* for enough time to call mxc_set_next_event()
*/
local_irq_save(flags);
/* Disable interrupt in GPT module */
imxtm->gpt->gpt_irq_disable(imxtm);
if (!clockevent_state_oneshot(ced)) {
u32 tcn = readl_relaxed(imxtm->base + imxtm->gpt->reg_tcn);
/* Set event time into far-far future */
writel_relaxed(tcn - 3, imxtm->base + imxtm->gpt->reg_tcmp);
/* Clear pending interrupt */
imxtm->gpt->gpt_irq_acknowledge(imxtm);
}
#ifdef DEBUG
printk(KERN_INFO "%s: changing mode\n", __func__);
#endif /* DEBUG */
/*
* Do not put overhead of interrupt enable/disable into
* mxc_set_next_event(), the core has about 4 minutes
* to call mxc_set_next_event() or shutdown clock after
* mode switching
*/
imxtm->gpt->gpt_irq_enable(imxtm);
local_irq_restore(flags);
return 0;
}
/*
* IRQ handler for the timer
*/
static irqreturn_t mxc_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *ced = dev_id;
struct imx_timer *imxtm = to_imx_timer(ced);
uint32_t tstat;
tstat = readl_relaxed(imxtm->base + imxtm->gpt->reg_tstat);
imxtm->gpt->gpt_irq_acknowledge(imxtm);
ced->event_handler(ced);
return IRQ_HANDLED;
}
static int __init mxc_clockevent_init(struct imx_timer *imxtm)
{
struct clock_event_device *ced = &imxtm->ced;
struct irqaction *act = &imxtm->act;
ced->name = "mxc_timer1";
ced->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_DYNIRQ;
ced->set_state_shutdown = mxc_shutdown;
ced->set_state_oneshot = mxc_set_oneshot;
ced->tick_resume = mxc_shutdown;
ced->set_next_event = imxtm->gpt->set_next_event;
ced->rating = 200;
ced->cpumask = cpumask_of(0);
ced->irq = imxtm->irq;
clockevents_config_and_register(ced, clk_get_rate(imxtm->clk_per),
0xff, 0xfffffffe);
act->name = "i.MX Timer Tick";
act->flags = IRQF_TIMER | IRQF_IRQPOLL;
act->handler = mxc_timer_interrupt;
act->dev_id = ced;
return setup_irq(imxtm->irq, act);
}
static void imx1_gpt_setup_tctl(struct imx_timer *imxtm)
{
u32 tctl_val;
tctl_val = MX1_2_TCTL_FRR | MX1_2_TCTL_CLK_PCLK1 | MXC_TCTL_TEN;
writel_relaxed(tctl_val, imxtm->base + MXC_TCTL);
}
#define imx21_gpt_setup_tctl imx1_gpt_setup_tctl
static void imx31_gpt_setup_tctl(struct imx_timer *imxtm)
{
u32 tctl_val;
tctl_val = V2_TCTL_FRR | V2_TCTL_WAITEN | MXC_TCTL_TEN;
if (clk_get_rate(imxtm->clk_per) == V2_TIMER_RATE_OSC_DIV8)
tctl_val |= V2_TCTL_CLK_OSC_DIV8;
else
tctl_val |= V2_TCTL_CLK_PER;
writel_relaxed(tctl_val, imxtm->base + MXC_TCTL);
}
static void imx6dl_gpt_setup_tctl(struct imx_timer *imxtm)
{
u32 tctl_val;
tctl_val = V2_TCTL_FRR | V2_TCTL_WAITEN | MXC_TCTL_TEN;
if (clk_get_rate(imxtm->clk_per) == V2_TIMER_RATE_OSC_DIV8) {
tctl_val |= V2_TCTL_CLK_OSC_DIV8;
/* 24 / 8 = 3 MHz */
writel_relaxed(7 << V2_TPRER_PRE24M, imxtm->base + MXC_TPRER);
tctl_val |= V2_TCTL_24MEN;
} else {
tctl_val |= V2_TCTL_CLK_PER;
}
writel_relaxed(tctl_val, imxtm->base + MXC_TCTL);
}
static const struct imx_gpt_data imx1_gpt_data = {
.reg_tstat = MX1_2_TSTAT,
.reg_tcn = MX1_2_TCN,
.reg_tcmp = MX1_2_TCMP,
.gpt_irq_enable = imx1_gpt_irq_enable,
.gpt_irq_disable = imx1_gpt_irq_disable,
.gpt_irq_acknowledge = imx1_gpt_irq_acknowledge,
.gpt_setup_tctl = imx1_gpt_setup_tctl,
.set_next_event = mx1_2_set_next_event,
};
static const struct imx_gpt_data imx21_gpt_data = {
.reg_tstat = MX1_2_TSTAT,
.reg_tcn = MX1_2_TCN,
.reg_tcmp = MX1_2_TCMP,
.gpt_irq_enable = imx21_gpt_irq_enable,
.gpt_irq_disable = imx21_gpt_irq_disable,
.gpt_irq_acknowledge = imx21_gpt_irq_acknowledge,
.gpt_setup_tctl = imx21_gpt_setup_tctl,
.set_next_event = mx1_2_set_next_event,
};
static const struct imx_gpt_data imx31_gpt_data = {
.reg_tstat = V2_TSTAT,
.reg_tcn = V2_TCN,
.reg_tcmp = V2_TCMP,
.gpt_irq_enable = imx31_gpt_irq_enable,
.gpt_irq_disable = imx31_gpt_irq_disable,
.gpt_irq_acknowledge = imx31_gpt_irq_acknowledge,
.gpt_setup_tctl = imx31_gpt_setup_tctl,
.set_next_event = v2_set_next_event,
};
static const struct imx_gpt_data imx6dl_gpt_data = {
.reg_tstat = V2_TSTAT,
.reg_tcn = V2_TCN,
.reg_tcmp = V2_TCMP,
.gpt_irq_enable = imx6dl_gpt_irq_enable,
.gpt_irq_disable = imx6dl_gpt_irq_disable,
.gpt_irq_acknowledge = imx6dl_gpt_irq_acknowledge,
.gpt_setup_tctl = imx6dl_gpt_setup_tctl,
.set_next_event = v2_set_next_event,
};
static int __init _mxc_timer_init(struct imx_timer *imxtm)
{
int ret;
switch (imxtm->type) {
case GPT_TYPE_IMX1:
imxtm->gpt = &imx1_gpt_data;
break;
case GPT_TYPE_IMX21:
imxtm->gpt = &imx21_gpt_data;
break;
case GPT_TYPE_IMX31:
imxtm->gpt = &imx31_gpt_data;
break;
case GPT_TYPE_IMX6DL:
imxtm->gpt = &imx6dl_gpt_data;
break;
default:
return -EINVAL;
}
if (IS_ERR(imxtm->clk_per)) {
pr_err("i.MX timer: unable to get clk\n");
return PTR_ERR(imxtm->clk_per);
}
if (!IS_ERR(imxtm->clk_ipg))
clk_prepare_enable(imxtm->clk_ipg);
clk_prepare_enable(imxtm->clk_per);
/*
* Initialise to a known state (all timers off, and timing reset)
*/
writel_relaxed(0, imxtm->base + MXC_TCTL);
writel_relaxed(0, imxtm->base + MXC_TPRER); /* see datasheet note */
imxtm->gpt->gpt_setup_tctl(imxtm);
/* init and register the timer to the framework */
ret = mxc_clocksource_init(imxtm);
if (ret)
return ret;
return mxc_clockevent_init(imxtm);
}
void __init mxc_timer_init(unsigned long pbase, int irq, enum imx_gpt_type type)
{
struct imx_timer *imxtm;
imxtm = kzalloc(sizeof(*imxtm), GFP_KERNEL);
BUG_ON(!imxtm);
imxtm->clk_per = clk_get_sys("imx-gpt.0", "per");
imxtm->clk_ipg = clk_get_sys("imx-gpt.0", "ipg");
imxtm->base = ioremap(pbase, SZ_4K);
BUG_ON(!imxtm->base);
imxtm->type = type;
imxtm->irq = irq;
_mxc_timer_init(imxtm);
}
static int __init mxc_timer_init_dt(struct device_node *np, enum imx_gpt_type type)
{
struct imx_timer *imxtm;
static int initialized;
int ret;
/* Support one instance only */
if (initialized)
return 0;
imxtm = kzalloc(sizeof(*imxtm), GFP_KERNEL);
if (!imxtm)
return -ENOMEM;
imxtm->base = of_iomap(np, 0);
if (!imxtm->base)
return -ENXIO;
imxtm->irq = irq_of_parse_and_map(np, 0);
if (imxtm->irq <= 0)
return -EINVAL;
imxtm->clk_ipg = of_clk_get_by_name(np, "ipg");
/* Try osc_per first, and fall back to per otherwise */
imxtm->clk_per = of_clk_get_by_name(np, "osc_per");
if (IS_ERR(imxtm->clk_per))
imxtm->clk_per = of_clk_get_by_name(np, "per");
imxtm->type = type;
ret = _mxc_timer_init(imxtm);
if (ret)
return ret;
initialized = 1;
return 0;
}
static int __init imx1_timer_init_dt(struct device_node *np)
{
return mxc_timer_init_dt(np, GPT_TYPE_IMX1);
}
static int __init imx21_timer_init_dt(struct device_node *np)
{
return mxc_timer_init_dt(np, GPT_TYPE_IMX21);
}
static int __init imx31_timer_init_dt(struct device_node *np)
{
enum imx_gpt_type type = GPT_TYPE_IMX31;
/*
* We were using the same compatible string for i.MX6Q/D and i.MX6DL/S
* GPT device, while they actually have different programming model.
* This is a workaround to keep the existing i.MX6DL/S DTBs continue
* working with the new kernel.
*/
if (of_machine_is_compatible("fsl,imx6dl"))
type = GPT_TYPE_IMX6DL;
return mxc_timer_init_dt(np, type);
}
static int __init imx6dl_timer_init_dt(struct device_node *np)
{
return mxc_timer_init_dt(np, GPT_TYPE_IMX6DL);
}
TIMER_OF_DECLARE(imx1_timer, "fsl,imx1-gpt", imx1_timer_init_dt);
TIMER_OF_DECLARE(imx21_timer, "fsl,imx21-gpt", imx21_timer_init_dt);
TIMER_OF_DECLARE(imx27_timer, "fsl,imx27-gpt", imx21_timer_init_dt);
TIMER_OF_DECLARE(imx31_timer, "fsl,imx31-gpt", imx31_timer_init_dt);
TIMER_OF_DECLARE(imx25_timer, "fsl,imx25-gpt", imx31_timer_init_dt);
TIMER_OF_DECLARE(imx50_timer, "fsl,imx50-gpt", imx31_timer_init_dt);
TIMER_OF_DECLARE(imx51_timer, "fsl,imx51-gpt", imx31_timer_init_dt);
TIMER_OF_DECLARE(imx53_timer, "fsl,imx53-gpt", imx31_timer_init_dt);
TIMER_OF_DECLARE(imx6q_timer, "fsl,imx6q-gpt", imx31_timer_init_dt);
TIMER_OF_DECLARE(imx6dl_timer, "fsl,imx6dl-gpt", imx6dl_timer_init_dt);
TIMER_OF_DECLARE(imx6sl_timer, "fsl,imx6sl-gpt", imx6dl_timer_init_dt);
TIMER_OF_DECLARE(imx6sx_timer, "fsl,imx6sx-gpt", imx6dl_timer_init_dt);
+257
View File
@@ -0,0 +1,257 @@
// SPDX-License-Identifier: GPL-2.0+
//
// Copyright 2016 Freescale Semiconductor, Inc.
// Copyright 2017 NXP
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/sched_clock.h>
#define TPM_PARAM 0x4
#define TPM_PARAM_WIDTH_SHIFT 16
#define TPM_PARAM_WIDTH_MASK (0xff << 16)
#define TPM_SC 0x10
#define TPM_SC_CMOD_INC_PER_CNT (0x1 << 3)
#define TPM_SC_CMOD_DIV_DEFAULT 0x3
#define TPM_SC_CMOD_DIV_MAX 0x7
#define TPM_SC_TOF_MASK (0x1 << 7)
#define TPM_CNT 0x14
#define TPM_MOD 0x18
#define TPM_STATUS 0x1c
#define TPM_STATUS_CH0F BIT(0)
#define TPM_C0SC 0x20
#define TPM_C0SC_CHIE BIT(6)
#define TPM_C0SC_MODE_SHIFT 2
#define TPM_C0SC_MODE_MASK 0x3c
#define TPM_C0SC_MODE_SW_COMPARE 0x4
#define TPM_C0SC_CHF_MASK (0x1 << 7)
#define TPM_C0V 0x24
static int counter_width;
static int rating;
static void __iomem *timer_base;
static struct clock_event_device clockevent_tpm;
static inline void tpm_timer_disable(void)
{
unsigned int val;
/* channel disable */
val = readl(timer_base + TPM_C0SC);
val &= ~(TPM_C0SC_MODE_MASK | TPM_C0SC_CHIE);
writel(val, timer_base + TPM_C0SC);
}
static inline void tpm_timer_enable(void)
{
unsigned int val;
/* channel enabled in sw compare mode */
val = readl(timer_base + TPM_C0SC);
val |= (TPM_C0SC_MODE_SW_COMPARE << TPM_C0SC_MODE_SHIFT) |
TPM_C0SC_CHIE;
writel(val, timer_base + TPM_C0SC);
}
static inline void tpm_irq_acknowledge(void)
{
writel(TPM_STATUS_CH0F, timer_base + TPM_STATUS);
}
static struct delay_timer tpm_delay_timer;
static inline unsigned long tpm_read_counter(void)
{
return readl(timer_base + TPM_CNT);
}
static unsigned long tpm_read_current_timer(void)
{
return tpm_read_counter();
}
static u64 notrace tpm_read_sched_clock(void)
{
return tpm_read_counter();
}
static int __init tpm_clocksource_init(unsigned long rate)
{
tpm_delay_timer.read_current_timer = &tpm_read_current_timer;
tpm_delay_timer.freq = rate;
register_current_timer_delay(&tpm_delay_timer);
sched_clock_register(tpm_read_sched_clock, counter_width, rate);
return clocksource_mmio_init(timer_base + TPM_CNT, "imx-tpm",
rate, rating, counter_width,
clocksource_mmio_readl_up);
}
static int tpm_set_next_event(unsigned long delta,
struct clock_event_device *evt)
{
unsigned long next, now;
next = tpm_read_counter();
next += delta;
writel(next, timer_base + TPM_C0V);
now = tpm_read_counter();
/*
* NOTE: We observed in a very small probability, the bus fabric
* contention between GPU and A7 may results a few cycles delay
* of writing CNT registers which may cause the min_delta event got
* missed, so we need add a ETIME check here in case it happened.
*/
return (int)(next - now) <= 0 ? -ETIME : 0;
}
static int tpm_set_state_oneshot(struct clock_event_device *evt)
{
tpm_timer_enable();
return 0;
}
static int tpm_set_state_shutdown(struct clock_event_device *evt)
{
tpm_timer_disable();
return 0;
}
static irqreturn_t tpm_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
tpm_irq_acknowledge();
evt->event_handler(evt);
return IRQ_HANDLED;
}
static struct clock_event_device clockevent_tpm = {
.name = "i.MX7ULP TPM Timer",
.features = CLOCK_EVT_FEAT_ONESHOT,
.set_state_oneshot = tpm_set_state_oneshot,
.set_next_event = tpm_set_next_event,
.set_state_shutdown = tpm_set_state_shutdown,
};
static int __init tpm_clockevent_init(unsigned long rate, int irq)
{
int ret;
ret = request_irq(irq, tpm_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL,
"i.MX7ULP TPM Timer", &clockevent_tpm);
clockevent_tpm.rating = rating;
clockevent_tpm.cpumask = cpumask_of(0);
clockevent_tpm.irq = irq;
clockevents_config_and_register(&clockevent_tpm, rate, 300,
GENMASK(counter_width - 1, 1));
return ret;
}
static int __init tpm_timer_init(struct device_node *np)
{
struct clk *ipg, *per;
int irq, ret;
u32 rate;
timer_base = of_iomap(np, 0);
if (!timer_base) {
pr_err("tpm: failed to get base address\n");
return -ENXIO;
}
irq = irq_of_parse_and_map(np, 0);
if (!irq) {
pr_err("tpm: failed to get irq\n");
ret = -ENOENT;
goto err_iomap;
}
ipg = of_clk_get_by_name(np, "ipg");
per = of_clk_get_by_name(np, "per");
if (IS_ERR(ipg) || IS_ERR(per)) {
pr_err("tpm: failed to get ipg or per clk\n");
ret = -ENODEV;
goto err_clk_get;
}
/* enable clk before accessing registers */
ret = clk_prepare_enable(ipg);
if (ret) {
pr_err("tpm: ipg clock enable failed (%d)\n", ret);
goto err_clk_get;
}
ret = clk_prepare_enable(per);
if (ret) {
pr_err("tpm: per clock enable failed (%d)\n", ret);
goto err_per_clk_enable;
}
counter_width = (readl(timer_base + TPM_PARAM) & TPM_PARAM_WIDTH_MASK)
>> TPM_PARAM_WIDTH_SHIFT;
/* use rating 200 for 32-bit counter and 150 for 16-bit counter */
rating = counter_width == 0x20 ? 200 : 150;
/*
* Initialize tpm module to a known state
* 1) Counter disabled
* 2) TPM counter operates in up counting mode
* 3) Timer Overflow Interrupt disabled
* 4) Channel0 disabled
* 5) DMA transfers disabled
*/
/* make sure counter is disabled */
writel(0, timer_base + TPM_SC);
/* TOF is W1C */
writel(TPM_SC_TOF_MASK, timer_base + TPM_SC);
writel(0, timer_base + TPM_CNT);
/* CHF is W1C */
writel(TPM_C0SC_CHF_MASK, timer_base + TPM_C0SC);
/*
* increase per cnt,
* div 8 for 32-bit counter and div 128 for 16-bit counter
*/
writel(TPM_SC_CMOD_INC_PER_CNT |
(counter_width == 0x20 ?
TPM_SC_CMOD_DIV_DEFAULT : TPM_SC_CMOD_DIV_MAX),
timer_base + TPM_SC);
/* set MOD register to maximum for free running mode */
writel(GENMASK(counter_width - 1, 0), timer_base + TPM_MOD);
rate = clk_get_rate(per) >> 3;
ret = tpm_clocksource_init(rate);
if (ret)
goto err_per_clk_enable;
ret = tpm_clockevent_init(rate, irq);
if (ret)
goto err_per_clk_enable;
return 0;
err_per_clk_enable:
clk_disable_unprepare(ipg);
err_clk_get:
clk_put(per);
clk_put(ipg);
err_iomap:
iounmap(timer_base);
return ret;
}
TIMER_OF_DECLARE(imx7ulp, "fsl,imx7ulp-tpm", tpm_timer_init);
+243
View File
@@ -0,0 +1,243 @@
/*
* Integrator/AP timer driver
* Copyright (C) 2000-2003 Deep Blue Solutions Ltd
* Copyright (c) 2014, Linaro Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/clk.h>
#include <linux/clocksource.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/sched_clock.h>
#include "timer-sp.h"
static void __iomem * sched_clk_base;
static u64 notrace integrator_read_sched_clock(void)
{
return -readl(sched_clk_base + TIMER_VALUE);
}
static int __init integrator_clocksource_init(unsigned long inrate,
void __iomem *base)
{
u32 ctrl = TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC;
unsigned long rate = inrate;
int ret;
if (rate >= 1500000) {
rate /= 16;
ctrl |= TIMER_CTRL_DIV16;
}
writel(0xffff, base + TIMER_LOAD);
writel(ctrl, base + TIMER_CTRL);
ret = clocksource_mmio_init(base + TIMER_VALUE, "timer2",
rate, 200, 16, clocksource_mmio_readl_down);
if (ret)
return ret;
sched_clk_base = base;
sched_clock_register(integrator_read_sched_clock, 16, rate);
return 0;
}
static unsigned long timer_reload;
static void __iomem * clkevt_base;
/*
* IRQ handler for the timer
*/
static irqreturn_t integrator_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
/* clear the interrupt */
writel(1, clkevt_base + TIMER_INTCLR);
evt->event_handler(evt);
return IRQ_HANDLED;
}
static int clkevt_shutdown(struct clock_event_device *evt)
{
u32 ctrl = readl(clkevt_base + TIMER_CTRL) & ~TIMER_CTRL_ENABLE;
/* Disable timer */
writel(ctrl, clkevt_base + TIMER_CTRL);
return 0;
}
static int clkevt_set_oneshot(struct clock_event_device *evt)
{
u32 ctrl = readl(clkevt_base + TIMER_CTRL) &
~(TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC);
/* Leave the timer disabled, .set_next_event will enable it */
writel(ctrl, clkevt_base + TIMER_CTRL);
return 0;
}
static int clkevt_set_periodic(struct clock_event_device *evt)
{
u32 ctrl = readl(clkevt_base + TIMER_CTRL) & ~TIMER_CTRL_ENABLE;
/* Disable timer */
writel(ctrl, clkevt_base + TIMER_CTRL);
/* Enable the timer and start the periodic tick */
writel(timer_reload, clkevt_base + TIMER_LOAD);
ctrl |= TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE;
writel(ctrl, clkevt_base + TIMER_CTRL);
return 0;
}
static int clkevt_set_next_event(unsigned long next, struct clock_event_device *evt)
{
unsigned long ctrl = readl(clkevt_base + TIMER_CTRL);
writel(ctrl & ~TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL);
writel(next, clkevt_base + TIMER_LOAD);
writel(ctrl | TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL);
return 0;
}
static struct clock_event_device integrator_clockevent = {
.name = "timer1",
.features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT,
.set_state_shutdown = clkevt_shutdown,
.set_state_periodic = clkevt_set_periodic,
.set_state_oneshot = clkevt_set_oneshot,
.tick_resume = clkevt_shutdown,
.set_next_event = clkevt_set_next_event,
.rating = 300,
};
static struct irqaction integrator_timer_irq = {
.name = "timer",
.flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = integrator_timer_interrupt,
.dev_id = &integrator_clockevent,
};
static int integrator_clockevent_init(unsigned long inrate,
void __iomem *base, int irq)
{
unsigned long rate = inrate;
unsigned int ctrl = 0;
int ret;
clkevt_base = base;
/* Calculate and program a divisor */
if (rate > 0x100000 * HZ) {
rate /= 256;
ctrl |= TIMER_CTRL_DIV256;
} else if (rate > 0x10000 * HZ) {
rate /= 16;
ctrl |= TIMER_CTRL_DIV16;
}
timer_reload = rate / HZ;
writel(ctrl, clkevt_base + TIMER_CTRL);
ret = setup_irq(irq, &integrator_timer_irq);
if (ret)
return ret;
clockevents_config_and_register(&integrator_clockevent,
rate,
1,
0xffffU);
return 0;
}
static int __init integrator_ap_timer_init_of(struct device_node *node)
{
const char *path;
void __iomem *base;
int err;
int irq;
struct clk *clk;
unsigned long rate;
struct device_node *alias_node;
base = of_io_request_and_map(node, 0, "integrator-timer");
if (IS_ERR(base))
return PTR_ERR(base);
clk = of_clk_get(node, 0);
if (IS_ERR(clk)) {
pr_err("No clock for %s\n", node->name);
return PTR_ERR(clk);
}
clk_prepare_enable(clk);
rate = clk_get_rate(clk);
writel(0, base + TIMER_CTRL);
err = of_property_read_string(of_aliases,
"arm,timer-primary", &path);
if (err) {
pr_warn("Failed to read property\n");
return err;
}
alias_node = of_find_node_by_path(path);
/*
* The pointer is used as an identifier not as a pointer, we
* can drop the refcount on the of__node immediately after
* getting it.
*/
of_node_put(alias_node);
if (node == alias_node)
/* The primary timer lacks IRQ, use as clocksource */
return integrator_clocksource_init(rate, base);
err = of_property_read_string(of_aliases,
"arm,timer-secondary", &path);
if (err) {
pr_warn("Failed to read property\n");
return err;
}
alias_node = of_find_node_by_path(path);
of_node_put(alias_node);
if (node == alias_node) {
/* The secondary timer will drive the clock event */
irq = irq_of_parse_and_map(node, 0);
return integrator_clockevent_init(rate, base, irq);
}
pr_info("Timer @%p unused\n", base);
clk_disable_unprepare(clk);
return 0;
}
TIMER_OF_DECLARE(integrator_ap_timer, "arm,integrator-timer",
integrator_ap_timer_init_of);
+230
View File
@@ -0,0 +1,230 @@
/*
* Keystone broadcast clock-event
*
* Copyright 2013 Texas Instruments, Inc.
*
* Author: Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/interrupt.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#define TIMER_NAME "timer-keystone"
/* Timer register offsets */
#define TIM12 0x10
#define TIM34 0x14
#define PRD12 0x18
#define PRD34 0x1c
#define TCR 0x20
#define TGCR 0x24
#define INTCTLSTAT 0x44
/* Timer register bitfields */
#define TCR_ENAMODE_MASK 0xC0
#define TCR_ENAMODE_ONESHOT_MASK 0x40
#define TCR_ENAMODE_PERIODIC_MASK 0x80
#define TGCR_TIM_UNRESET_MASK 0x03
#define INTCTLSTAT_ENINT_MASK 0x01
/**
* struct keystone_timer: holds timer's data
* @base: timer memory base address
* @hz_period: cycles per HZ period
* @event_dev: event device based on timer
*/
static struct keystone_timer {
void __iomem *base;
unsigned long hz_period;
struct clock_event_device event_dev;
} timer;
static inline u32 keystone_timer_readl(unsigned long rg)
{
return readl_relaxed(timer.base + rg);
}
static inline void keystone_timer_writel(u32 val, unsigned long rg)
{
writel_relaxed(val, timer.base + rg);
}
/**
* keystone_timer_barrier: write memory barrier
* use explicit barrier to avoid using readl/writel non relaxed function
* variants, because in our case non relaxed variants hide the true places
* where barrier is needed.
*/
static inline void keystone_timer_barrier(void)
{
__iowmb();
}
/**
* keystone_timer_config: configures timer to work in oneshot/periodic modes.
* @ mask: mask of the mode to configure
* @ period: cycles number to configure for
*/
static int keystone_timer_config(u64 period, int mask)
{
u32 tcr;
u32 off;
tcr = keystone_timer_readl(TCR);
off = tcr & ~(TCR_ENAMODE_MASK);
/* set enable mode */
tcr |= mask;
/* disable timer */
keystone_timer_writel(off, TCR);
/* here we have to be sure the timer has been disabled */
keystone_timer_barrier();
/* reset counter to zero, set new period */
keystone_timer_writel(0, TIM12);
keystone_timer_writel(0, TIM34);
keystone_timer_writel(period & 0xffffffff, PRD12);
keystone_timer_writel(period >> 32, PRD34);
/*
* enable timer
* here we have to be sure that CNTLO, CNTHI, PRDLO, PRDHI registers
* have been written.
*/
keystone_timer_barrier();
keystone_timer_writel(tcr, TCR);
return 0;
}
static void keystone_timer_disable(void)
{
u32 tcr;
tcr = keystone_timer_readl(TCR);
/* disable timer */
tcr &= ~(TCR_ENAMODE_MASK);
keystone_timer_writel(tcr, TCR);
}
static irqreturn_t keystone_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
evt->event_handler(evt);
return IRQ_HANDLED;
}
static int keystone_set_next_event(unsigned long cycles,
struct clock_event_device *evt)
{
return keystone_timer_config(cycles, TCR_ENAMODE_ONESHOT_MASK);
}
static int keystone_shutdown(struct clock_event_device *evt)
{
keystone_timer_disable();
return 0;
}
static int keystone_set_periodic(struct clock_event_device *evt)
{
keystone_timer_config(timer.hz_period, TCR_ENAMODE_PERIODIC_MASK);
return 0;
}
static int __init keystone_timer_init(struct device_node *np)
{
struct clock_event_device *event_dev = &timer.event_dev;
unsigned long rate;
struct clk *clk;
int irq, error;
irq = irq_of_parse_and_map(np, 0);
if (!irq) {
pr_err("%s: failed to map interrupts\n", __func__);
return -EINVAL;
}
timer.base = of_iomap(np, 0);
if (!timer.base) {
pr_err("%s: failed to map registers\n", __func__);
return -ENXIO;
}
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
pr_err("%s: failed to get clock\n", __func__);
iounmap(timer.base);
return PTR_ERR(clk);
}
error = clk_prepare_enable(clk);
if (error) {
pr_err("%s: failed to enable clock\n", __func__);
goto err;
}
rate = clk_get_rate(clk);
/* disable, use internal clock source */
keystone_timer_writel(0, TCR);
/* here we have to be sure the timer has been disabled */
keystone_timer_barrier();
/* reset timer as 64-bit, no pre-scaler, plus features are disabled */
keystone_timer_writel(0, TGCR);
/* unreset timer */
keystone_timer_writel(TGCR_TIM_UNRESET_MASK, TGCR);
/* init counter to zero */
keystone_timer_writel(0, TIM12);
keystone_timer_writel(0, TIM34);
timer.hz_period = DIV_ROUND_UP(rate, HZ);
/* enable timer interrupts */
keystone_timer_writel(INTCTLSTAT_ENINT_MASK, INTCTLSTAT);
error = request_irq(irq, keystone_timer_interrupt, IRQF_TIMER,
TIMER_NAME, event_dev);
if (error) {
pr_err("%s: failed to setup irq\n", __func__);
goto err;
}
/* setup clockevent */
event_dev->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
event_dev->set_next_event = keystone_set_next_event;
event_dev->set_state_shutdown = keystone_shutdown;
event_dev->set_state_periodic = keystone_set_periodic;
event_dev->set_state_oneshot = keystone_shutdown;
event_dev->cpumask = cpu_possible_mask;
event_dev->owner = THIS_MODULE;
event_dev->name = TIMER_NAME;
event_dev->irq = irq;
clockevents_config_and_register(event_dev, rate, 1, ULONG_MAX);
pr_info("keystone timer clock @%lu Hz\n", rate);
return 0;
err:
clk_put(clk);
iounmap(timer.base);
return error;
}
TIMER_OF_DECLARE(keystone_timer, "ti,keystone-timer",
keystone_timer_init);
+422
View File
@@ -0,0 +1,422 @@
/*
* Mediatek SoCs General-Purpose Timer handling.
*
* Copyright (C) 2014 Matthias Brugger
*
* Matthias Brugger <matthias.bgg@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/of.h>
#include <linux/sched_clock.h>
#include <linux/slab.h>
#include "timer-of.h"
#define TIMER_CLK_EVT (1)
#define TIMER_CLK_SRC (2)
#define TIMER_SYNC_TICKS (3)
/* gpt */
#define GPT_IRQ_EN_REG 0x00
#define GPT_IRQ_ENABLE(val) BIT((val) - 1)
#define GPT_IRQ_ACK_REG 0x08
#define GPT_IRQ_ACK(val) BIT((val) - 1)
#define GPT_CTRL_REG(val) (0x10 * (val))
#define GPT_CTRL_OP(val) (((val) & 0x3) << 4)
#define GPT_CTRL_OP_ONESHOT (0)
#define GPT_CTRL_OP_REPEAT (1)
#define GPT_CTRL_OP_FREERUN (3)
#define GPT_CTRL_CLEAR (2)
#define GPT_CTRL_ENABLE (1)
#define GPT_CTRL_DISABLE (0)
#define GPT_CLK_REG(val) (0x04 + (0x10 * (val)))
#define GPT_CLK_SRC(val) (((val) & 0x1) << 4)
#define GPT_CLK_SRC_SYS13M (0)
#define GPT_CLK_SRC_RTC32K (1)
#define GPT_CLK_DIV1 (0x0)
#define GPT_CLK_DIV2 (0x1)
#define GPT_CNT_REG(val) (0x08 + (0x10 * (val)))
#define GPT_CMP_REG(val) (0x0C + (0x10 * (val)))
/* system timer */
#define SYST_BASE (0x40)
#define SYST_CON (SYST_BASE + 0x0)
#define SYST_VAL (SYST_BASE + 0x4)
#define SYST_CON_REG(to) (timer_of_base(to) + SYST_CON)
#define SYST_VAL_REG(to) (timer_of_base(to) + SYST_VAL)
/*
* SYST_CON_EN: Clock enable. Shall be set to
* - Start timer countdown.
* - Allow timeout ticks being updated.
* - Allow changing interrupt functions.
*
* SYST_CON_IRQ_EN: Set to allow interrupt.
*
* SYST_CON_IRQ_CLR: Set to clear interrupt.
*/
#define SYST_CON_EN BIT(0)
#define SYST_CON_IRQ_EN BIT(1)
#define SYST_CON_IRQ_CLR BIT(4)
#ifdef CONFIG_MTK_AEE_IPANIC
#define SYST_DEBUG
#endif
#ifdef SYST_DEBUG
#include <linux/sched/clock.h>
#include <mt-plat/mboot_params.h>
static char dbg_buf[128];
static unsigned int dbg_setevt_cpu;
static uint64_t t_setevt_in;
static uint64_t t_hdl_in;
static uint64_t t_setevt_ticks;
#define aee_log(fmt, ...) \
do { \
memset(dbg_buf, 0, sizeof(dbg_buf)); \
if (snprintf(dbg_buf, sizeof(dbg_buf), fmt, ##__VA_ARGS__) > 0) \
aee_sram_fiq_log(dbg_buf); \
} while (0)
#endif
static void __iomem *gpt_sched_reg __read_mostly;
static void mtk_syst_ack_irq(struct timer_of *to)
{
/* Clear and disable interrupt */
writel(SYST_CON_IRQ_CLR | SYST_CON_EN, SYST_CON_REG(to));
}
static irqreturn_t mtk_syst_handler(int irq, void *dev_id)
{
struct clock_event_device *clkevt = dev_id;
struct timer_of *to = to_timer_of(clkevt);
#ifdef SYST_DEBUG
t_hdl_in = sched_clock();
#endif
mtk_syst_ack_irq(to);
clkevt->event_handler(clkevt);
return IRQ_HANDLED;
}
static int mtk_syst_clkevt_next_event(unsigned long ticks,
struct clock_event_device *clkevt)
{
struct timer_of *to = to_timer_of(clkevt);
#ifdef SYST_DEBUG
t_setevt_in = sched_clock();
t_setevt_ticks = ticks;
dbg_setevt_cpu = smp_processor_id();
#endif
/* Enable clock to allow timeout tick update later */
writel(SYST_CON_EN, SYST_CON_REG(to));
/*
* Write new timeout ticks. Timer shall start countdown
* after timeout ticks are updated.
*/
writel(ticks, SYST_VAL_REG(to));
/* Enable interrupt */
writel(SYST_CON_EN | SYST_CON_IRQ_EN, SYST_CON_REG(to));
return 0;
}
static int mtk_syst_clkevt_shutdown(struct clock_event_device *clkevt)
{
/* Disable timer */
writel(0, SYST_CON_REG(to_timer_of(clkevt)));
return 0;
}
static int mtk_syst_clkevt_resume(struct clock_event_device *clkevt)
{
return mtk_syst_clkevt_shutdown(clkevt);
}
static int mtk_syst_clkevt_oneshot(struct clock_event_device *clkevt)
{
return 0;
}
static u64 notrace mtk_gpt_read_sched_clock(void)
{
return readl_relaxed(gpt_sched_reg);
}
static void mtk_gpt_clkevt_time_stop(struct timer_of *to, u8 timer)
{
u32 val;
/*
* support 32k clock when deepidle, should first use 13m clock config
* timer, then second use 32k clock trigger timer.
*/
if (to->private_data)
writel(GPT_CLK_SRC(GPT_CLK_SRC_SYS13M) | GPT_CLK_DIV1,
timer_of_base(to) + GPT_CLK_REG(timer));
val = readl(timer_of_base(to) + GPT_CTRL_REG(timer));
writel(val & ~GPT_CTRL_ENABLE, timer_of_base(to) +
GPT_CTRL_REG(timer));
}
static void mtk_gpt_clkevt_time_setup(struct timer_of *to,
unsigned long delay, u8 timer)
{
writel(delay, timer_of_base(to) + GPT_CMP_REG(timer));
}
static void mtk_gpt_clkevt_time_start(struct timer_of *to,
bool periodic, u8 timer)
{
u32 val;
/* Acknowledge interrupt */
writel(GPT_IRQ_ACK(timer), timer_of_base(to) + GPT_IRQ_ACK_REG);
/*
* support 32k clock when deepidle, should first use 13m clock config
* timer, then second use 32k clock trigger timer.
*/
if (to->private_data)
writel(GPT_CLK_SRC(GPT_CLK_SRC_RTC32K) | GPT_CLK_DIV1,
timer_of_base(to) + GPT_CLK_REG(timer));
val = readl(timer_of_base(to) + GPT_CTRL_REG(timer));
/* Clear 2 bit timer operation mode field */
val &= ~GPT_CTRL_OP(0x3);
if (periodic)
val |= GPT_CTRL_OP(GPT_CTRL_OP_REPEAT);
else
val |= GPT_CTRL_OP(GPT_CTRL_OP_ONESHOT);
writel(val | GPT_CTRL_ENABLE | GPT_CTRL_CLEAR,
timer_of_base(to) + GPT_CTRL_REG(timer));
}
static int mtk_gpt_clkevt_shutdown(struct clock_event_device *clk)
{
mtk_gpt_clkevt_time_stop(to_timer_of(clk), TIMER_CLK_EVT);
return 0;
}
static int mtk_gpt_clkevt_set_periodic(struct clock_event_device *clk)
{
struct timer_of *to = to_timer_of(clk);
mtk_gpt_clkevt_time_stop(to, TIMER_CLK_EVT);
mtk_gpt_clkevt_time_setup(to, to->of_clk.period, TIMER_CLK_EVT);
mtk_gpt_clkevt_time_start(to, true, TIMER_CLK_EVT);
return 0;
}
static int mtk_gpt_clkevt_next_event(unsigned long event,
struct clock_event_device *clk)
{
struct timer_of *to = to_timer_of(clk);
mtk_gpt_clkevt_time_stop(to, TIMER_CLK_EVT);
mtk_gpt_clkevt_time_setup(to, event, TIMER_CLK_EVT);
mtk_gpt_clkevt_time_start(to, false, TIMER_CLK_EVT);
return 0;
}
static irqreturn_t mtk_gpt_interrupt(int irq, void *dev_id)
{
struct clock_event_device *clkevt = (struct clock_event_device *)dev_id;
struct timer_of *to = to_timer_of(clkevt);
/* Acknowledge timer0 irq */
writel(GPT_IRQ_ACK(TIMER_CLK_EVT), timer_of_base(to) + GPT_IRQ_ACK_REG);
clkevt->event_handler(clkevt);
return IRQ_HANDLED;
}
static void
__init mtk_gpt_setup(struct timer_of *to, u8 timer, u8 option, u8 clksrc,
bool enable)
{
u32 val;
writel(GPT_CTRL_CLEAR | GPT_CTRL_DISABLE,
timer_of_base(to) + GPT_CTRL_REG(timer));
writel(GPT_CLK_SRC(clksrc) | GPT_CLK_DIV1,
timer_of_base(to) + GPT_CLK_REG(timer));
writel(0x0, timer_of_base(to) + GPT_CMP_REG(timer));
val = GPT_CTRL_OP(option);
if (enable)
val |= GPT_CTRL_ENABLE;
writel(val, timer_of_base(to) + GPT_CTRL_REG(timer));
}
static void mtk_gpt_enable_irq(struct timer_of *to, u8 timer)
{
u32 val;
/* Disable all interrupts */
writel(0x0, timer_of_base(to) + GPT_IRQ_EN_REG);
/* Acknowledge all spurious pending interrupts */
writel(0x3f, timer_of_base(to) + GPT_IRQ_ACK_REG);
val = readl(timer_of_base(to) + GPT_IRQ_EN_REG);
writel(val | GPT_IRQ_ENABLE(timer),
timer_of_base(to) + GPT_IRQ_EN_REG);
}
static struct timer_of to = {
.flags = TIMER_OF_IRQ | TIMER_OF_BASE | TIMER_OF_CLOCK,
.clkevt = {
.name = "mtk-clkevt",
.rating = 300,
.cpumask = cpu_possible_mask,
},
.of_irq = {
.flags = IRQF_TIMER | IRQF_IRQPOLL,
},
};
static int __init mtk_syst_init(struct device_node *node)
{
int ret;
to.clkevt.features = CLOCK_EVT_FEAT_DYNIRQ | CLOCK_EVT_FEAT_ONESHOT;
to.clkevt.set_state_shutdown = mtk_syst_clkevt_shutdown;
to.clkevt.set_state_oneshot = mtk_syst_clkevt_oneshot;
to.clkevt.tick_resume = mtk_syst_clkevt_resume;
to.clkevt.set_next_event = mtk_syst_clkevt_next_event;
to.of_irq.handler = mtk_syst_handler;
ret = timer_of_init(node, &to);
if (ret)
return ret;
clockevents_config_and_register(&to.clkevt, timer_of_rate(&to),
TIMER_SYNC_TICKS, 0xffffffff);
return 0;
}
static int __init mtk_gpt_init(struct device_node *node)
{
int ret;
int has_clk32k;
struct clk *clk_bus;
/*
* Sometimes, there is a "bus clk" used as GPT clock gate. It must be
* guaranteed to be opened so that GPT continues to work.
*/
clk_bus = of_clk_get_by_name(node, "bus");
if (!IS_ERR(clk_bus)) {
ret = clk_prepare_enable(clk_bus);
if (ret)
pr_debug("prepare bus clk fail.\n");
}
/*
* CLOCK_EVT_FEAT_DYNIRQ: Core shall set the interrupt affinity
* dynamically in broadcast mode.
* CLOCK_EVT_FEAT_ONESHOT: Use one-shot mode for tick broadcast.
*/
to.clkevt.features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT |
CLOCK_EVT_FEAT_DYNIRQ;
to.clkevt.set_state_shutdown = mtk_gpt_clkevt_shutdown;
to.clkevt.set_state_periodic = mtk_gpt_clkevt_set_periodic;
to.clkevt.set_state_oneshot = mtk_gpt_clkevt_shutdown;
to.clkevt.tick_resume = mtk_gpt_clkevt_shutdown;
to.clkevt.set_next_event = mtk_gpt_clkevt_next_event;
to.of_irq.handler = mtk_gpt_interrupt;
/* Use rtc-clk as clock source for clk-evt source if it is exists. */
has_clk32k = of_property_match_string(node, "clock-names", "clk32k");
if (has_clk32k >= 0)
to.of_clk.name = "clk32k";
ret = timer_of_init(node, &to);
if (ret)
return ret;
/* save rtc-clk as .private_data */
to.private_data = (has_clk32k < 0) ? NULL : (void *)to.of_clk.clk;
/* Configure clock source */
mtk_gpt_setup(&to, TIMER_CLK_SRC, GPT_CTRL_OP_FREERUN,
(has_clk32k < 0) ? GPT_CLK_SRC_SYS13M :
GPT_CLK_SRC_RTC32K, true);
clocksource_mmio_init(timer_of_base(&to) + GPT_CNT_REG(TIMER_CLK_SRC),
node->name, timer_of_rate(&to), 300, 32,
clocksource_mmio_readl_up);
gpt_sched_reg = timer_of_base(&to) + GPT_CNT_REG(TIMER_CLK_SRC);
sched_clock_register(mtk_gpt_read_sched_clock, 32, timer_of_rate(&to));
/* Configure clock event */
mtk_gpt_setup(&to, TIMER_CLK_EVT, GPT_CTRL_OP_REPEAT,
(has_clk32k < 0) ? GPT_CLK_SRC_SYS13M :
GPT_CLK_SRC_RTC32K, false);
clockevents_config_and_register(&to.clkevt, timer_of_rate(&to),
TIMER_SYNC_TICKS, 0xffffffff);
mtk_gpt_enable_irq(&to, TIMER_CLK_EVT);
return 0;
}
TIMER_OF_DECLARE(mtk_mt6577, "mediatek,mt6577-timer", mtk_gpt_init);
TIMER_OF_DECLARE(mtk_mt6765, "mediatek,mt6765-timer", mtk_syst_init);
#ifdef SYST_DEBUG
void mtk_timer_clkevt_aee_dump(void)
{
/* interrupt, clkevt handler and set next time */
aee_log("[timer-mtk]\n");
aee_log("int handler entry: %llu\n", t_hdl_in);
aee_log("set next event entry: %llu\n", t_setevt_in);
aee_log("set next event ticks: %llu\n", t_setevt_ticks);
aee_log("set next event cpu: %u\n", dbg_setevt_cpu);
aee_log("CON: 0x%x\n", readl(SYST_CON_REG(&to)));
aee_log("VAL: 0x%x\n", readl(SYST_VAL_REG(&to)));
}
#endif
+215
View File
@@ -0,0 +1,215 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2014-2018 Nuvoton Technologies tomer.maimon@nuvoton.com
* All rights reserved.
*
* Copyright 2017 Google, Inc.
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/clockchips.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include "timer-of.h"
/* Timers registers */
#define NPCM7XX_REG_TCSR0 0x0 /* Timer 0 Control and Status Register */
#define NPCM7XX_REG_TICR0 0x8 /* Timer 0 Initial Count Register */
#define NPCM7XX_REG_TCSR1 0x4 /* Timer 1 Control and Status Register */
#define NPCM7XX_REG_TICR1 0xc /* Timer 1 Initial Count Register */
#define NPCM7XX_REG_TDR1 0x14 /* Timer 1 Data Register */
#define NPCM7XX_REG_TISR 0x18 /* Timer Interrupt Status Register */
/* Timers control */
#define NPCM7XX_Tx_RESETINT 0x1f
#define NPCM7XX_Tx_PERIOD BIT(27)
#define NPCM7XX_Tx_INTEN BIT(29)
#define NPCM7XX_Tx_COUNTEN BIT(30)
#define NPCM7XX_Tx_ONESHOT 0x0
#define NPCM7XX_Tx_OPER GENMASK(3, 27)
#define NPCM7XX_Tx_MIN_PRESCALE 0x1
#define NPCM7XX_Tx_TDR_MASK_BITS 24
#define NPCM7XX_Tx_MAX_CNT 0xFFFFFF
#define NPCM7XX_T0_CLR_INT 0x1
#define NPCM7XX_Tx_CLR_CSR 0x0
/* Timers operating mode */
#define NPCM7XX_START_PERIODIC_Tx (NPCM7XX_Tx_PERIOD | NPCM7XX_Tx_COUNTEN | \
NPCM7XX_Tx_INTEN | \
NPCM7XX_Tx_MIN_PRESCALE)
#define NPCM7XX_START_ONESHOT_Tx (NPCM7XX_Tx_ONESHOT | NPCM7XX_Tx_COUNTEN | \
NPCM7XX_Tx_INTEN | \
NPCM7XX_Tx_MIN_PRESCALE)
#define NPCM7XX_START_Tx (NPCM7XX_Tx_COUNTEN | NPCM7XX_Tx_PERIOD | \
NPCM7XX_Tx_MIN_PRESCALE)
#define NPCM7XX_DEFAULT_CSR (NPCM7XX_Tx_CLR_CSR | NPCM7XX_Tx_MIN_PRESCALE)
static int npcm7xx_timer_resume(struct clock_event_device *evt)
{
struct timer_of *to = to_timer_of(evt);
u32 val;
val = readl(timer_of_base(to) + NPCM7XX_REG_TCSR0);
val |= NPCM7XX_Tx_COUNTEN;
writel(val, timer_of_base(to) + NPCM7XX_REG_TCSR0);
return 0;
}
static int npcm7xx_timer_shutdown(struct clock_event_device *evt)
{
struct timer_of *to = to_timer_of(evt);
u32 val;
val = readl(timer_of_base(to) + NPCM7XX_REG_TCSR0);
val &= ~NPCM7XX_Tx_COUNTEN;
writel(val, timer_of_base(to) + NPCM7XX_REG_TCSR0);
return 0;
}
static int npcm7xx_timer_oneshot(struct clock_event_device *evt)
{
struct timer_of *to = to_timer_of(evt);
u32 val;
val = readl(timer_of_base(to) + NPCM7XX_REG_TCSR0);
val &= ~NPCM7XX_Tx_OPER;
val = readl(timer_of_base(to) + NPCM7XX_REG_TCSR0);
val |= NPCM7XX_START_ONESHOT_Tx;
writel(val, timer_of_base(to) + NPCM7XX_REG_TCSR0);
return 0;
}
static int npcm7xx_timer_periodic(struct clock_event_device *evt)
{
struct timer_of *to = to_timer_of(evt);
u32 val;
val = readl(timer_of_base(to) + NPCM7XX_REG_TCSR0);
val &= ~NPCM7XX_Tx_OPER;
writel(timer_of_period(to), timer_of_base(to) + NPCM7XX_REG_TICR0);
val |= NPCM7XX_START_PERIODIC_Tx;
writel(val, timer_of_base(to) + NPCM7XX_REG_TCSR0);
return 0;
}
static int npcm7xx_clockevent_set_next_event(unsigned long evt,
struct clock_event_device *clk)
{
struct timer_of *to = to_timer_of(clk);
u32 val;
writel(evt, timer_of_base(to) + NPCM7XX_REG_TICR0);
val = readl(timer_of_base(to) + NPCM7XX_REG_TCSR0);
val |= NPCM7XX_START_Tx;
writel(val, timer_of_base(to) + NPCM7XX_REG_TCSR0);
return 0;
}
static irqreturn_t npcm7xx_timer0_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = (struct clock_event_device *)dev_id;
struct timer_of *to = to_timer_of(evt);
writel(NPCM7XX_T0_CLR_INT, timer_of_base(to) + NPCM7XX_REG_TISR);
evt->event_handler(evt);
return IRQ_HANDLED;
}
static struct timer_of npcm7xx_to = {
.flags = TIMER_OF_IRQ | TIMER_OF_BASE | TIMER_OF_CLOCK,
.clkevt = {
.name = "npcm7xx-timer0",
.features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT,
.set_next_event = npcm7xx_clockevent_set_next_event,
.set_state_shutdown = npcm7xx_timer_shutdown,
.set_state_periodic = npcm7xx_timer_periodic,
.set_state_oneshot = npcm7xx_timer_oneshot,
.tick_resume = npcm7xx_timer_resume,
.rating = 300,
},
.of_irq = {
.handler = npcm7xx_timer0_interrupt,
.flags = IRQF_TIMER | IRQF_IRQPOLL,
},
};
static void __init npcm7xx_clockevents_init(void)
{
writel(NPCM7XX_DEFAULT_CSR,
timer_of_base(&npcm7xx_to) + NPCM7XX_REG_TCSR0);
writel(NPCM7XX_Tx_RESETINT,
timer_of_base(&npcm7xx_to) + NPCM7XX_REG_TISR);
npcm7xx_to.clkevt.cpumask = cpumask_of(0);
clockevents_config_and_register(&npcm7xx_to.clkevt,
timer_of_rate(&npcm7xx_to),
0x1, NPCM7XX_Tx_MAX_CNT);
}
static void __init npcm7xx_clocksource_init(void)
{
u32 val;
writel(NPCM7XX_DEFAULT_CSR,
timer_of_base(&npcm7xx_to) + NPCM7XX_REG_TCSR1);
writel(NPCM7XX_Tx_MAX_CNT,
timer_of_base(&npcm7xx_to) + NPCM7XX_REG_TICR1);
val = readl(timer_of_base(&npcm7xx_to) + NPCM7XX_REG_TCSR1);
val |= NPCM7XX_START_Tx;
writel(val, timer_of_base(&npcm7xx_to) + NPCM7XX_REG_TCSR1);
clocksource_mmio_init(timer_of_base(&npcm7xx_to) +
NPCM7XX_REG_TDR1,
"npcm7xx-timer1", timer_of_rate(&npcm7xx_to),
200, (unsigned int)NPCM7XX_Tx_TDR_MASK_BITS,
clocksource_mmio_readl_down);
}
static int __init npcm7xx_timer_init(struct device_node *np)
{
int ret;
ret = timer_of_init(np, &npcm7xx_to);
if (ret)
return ret;
/* Clock input is divided by PRESCALE + 1 before it is fed */
/* to the counter */
npcm7xx_to.of_clk.rate = npcm7xx_to.of_clk.rate /
(NPCM7XX_Tx_MIN_PRESCALE + 1);
npcm7xx_clocksource_init();
npcm7xx_clockevents_init();
pr_info("Enabling NPCM7xx clocksource timer base: %px, IRQ: %d ",
timer_of_base(&npcm7xx_to), timer_of_irq(&npcm7xx_to));
return 0;
}
TIMER_OF_DECLARE(npcm7xx, "nuvoton,npcm750-timer", npcm7xx_timer_init);
+284
View File
@@ -0,0 +1,284 @@
/*
* Copyright (c) 2016, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <linux/interrupt.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/cpu.h>
#include <soc/nps/common.h>
#define NPS_MSU_TICK_LOW 0xC8
#define NPS_CLUSTER_OFFSET 8
#define NPS_CLUSTER_NUM 16
/* This array is per cluster of CPUs (Each NPS400 cluster got 256 CPUs) */
static void *nps_msu_reg_low_addr[NPS_CLUSTER_NUM] __read_mostly;
static int __init nps_get_timer_clk(struct device_node *node,
unsigned long *timer_freq,
struct clk **clk)
{
int ret;
*clk = of_clk_get(node, 0);
ret = PTR_ERR_OR_ZERO(*clk);
if (ret) {
pr_err("timer missing clk\n");
return ret;
}
ret = clk_prepare_enable(*clk);
if (ret) {
pr_err("Couldn't enable parent clk\n");
clk_put(*clk);
return ret;
}
*timer_freq = clk_get_rate(*clk);
if (!(*timer_freq)) {
pr_err("Couldn't get clk rate\n");
clk_disable_unprepare(*clk);
clk_put(*clk);
return -EINVAL;
}
return 0;
}
static u64 nps_clksrc_read(struct clocksource *clksrc)
{
int cluster = raw_smp_processor_id() >> NPS_CLUSTER_OFFSET;
return (u64)ioread32be(nps_msu_reg_low_addr[cluster]);
}
static int __init nps_setup_clocksource(struct device_node *node)
{
int ret, cluster;
struct clk *clk;
unsigned long nps_timer1_freq;
for (cluster = 0; cluster < NPS_CLUSTER_NUM; cluster++)
nps_msu_reg_low_addr[cluster] =
nps_host_reg((cluster << NPS_CLUSTER_OFFSET),
NPS_MSU_BLKID, NPS_MSU_TICK_LOW);
ret = nps_get_timer_clk(node, &nps_timer1_freq, &clk);
if (ret)
return ret;
ret = clocksource_mmio_init(nps_msu_reg_low_addr, "nps-tick",
nps_timer1_freq, 300, 32, nps_clksrc_read);
if (ret) {
pr_err("Couldn't register clock source.\n");
clk_disable_unprepare(clk);
}
return ret;
}
TIMER_OF_DECLARE(ezchip_nps400_clksrc, "ezchip,nps400-timer",
nps_setup_clocksource);
TIMER_OF_DECLARE(ezchip_nps400_clk_src, "ezchip,nps400-timer1",
nps_setup_clocksource);
#ifdef CONFIG_EZNPS_MTM_EXT
#include <soc/nps/mtm.h>
/* Timer related Aux registers */
#define NPS_REG_TIMER0_TSI 0xFFFFF850
#define NPS_REG_TIMER0_LIMIT 0x23
#define NPS_REG_TIMER0_CTRL 0x22
#define NPS_REG_TIMER0_CNT 0x21
/*
* Interrupt Enabled (IE) - re-arm the timer
* Not Halted (NH) - is cleared when working with JTAG (for debug)
*/
#define TIMER0_CTRL_IE BIT(0)
#define TIMER0_CTRL_NH BIT(1)
static unsigned long nps_timer0_freq;
static unsigned long nps_timer0_irq;
static void nps_clkevent_rm_thread(void)
{
int thread;
unsigned int cflags, enabled_threads;
hw_schd_save(&cflags);
enabled_threads = read_aux_reg(NPS_REG_TIMER0_TSI);
/* remove thread from TSI1 */
thread = read_aux_reg(CTOP_AUX_THREAD_ID);
enabled_threads &= ~(1 << thread);
write_aux_reg(NPS_REG_TIMER0_TSI, enabled_threads);
/* Acknowledge and if needed re-arm the timer */
if (!enabled_threads)
write_aux_reg(NPS_REG_TIMER0_CTRL, TIMER0_CTRL_NH);
else
write_aux_reg(NPS_REG_TIMER0_CTRL,
TIMER0_CTRL_IE | TIMER0_CTRL_NH);
hw_schd_restore(cflags);
}
static void nps_clkevent_add_thread(unsigned long delta)
{
int thread;
unsigned int cflags, enabled_threads;
hw_schd_save(&cflags);
/* add thread to TSI1 */
thread = read_aux_reg(CTOP_AUX_THREAD_ID);
enabled_threads = read_aux_reg(NPS_REG_TIMER0_TSI);
enabled_threads |= (1 << thread);
write_aux_reg(NPS_REG_TIMER0_TSI, enabled_threads);
/* set next timer event */
write_aux_reg(NPS_REG_TIMER0_LIMIT, delta);
write_aux_reg(NPS_REG_TIMER0_CNT, 0);
write_aux_reg(NPS_REG_TIMER0_CTRL,
TIMER0_CTRL_IE | TIMER0_CTRL_NH);
hw_schd_restore(cflags);
}
/*
* Whenever anyone tries to change modes, we just mask interrupts
* and wait for the next event to get set.
*/
static int nps_clkevent_set_state(struct clock_event_device *dev)
{
nps_clkevent_rm_thread();
disable_percpu_irq(nps_timer0_irq);
return 0;
}
static int nps_clkevent_set_next_event(unsigned long delta,
struct clock_event_device *dev)
{
nps_clkevent_add_thread(delta);
enable_percpu_irq(nps_timer0_irq, IRQ_TYPE_NONE);
return 0;
}
static DEFINE_PER_CPU(struct clock_event_device, nps_clockevent_device) = {
.name = "NPS Timer0",
.features = CLOCK_EVT_FEAT_ONESHOT,
.rating = 300,
.set_next_event = nps_clkevent_set_next_event,
.set_state_oneshot = nps_clkevent_set_state,
.set_state_oneshot_stopped = nps_clkevent_set_state,
.set_state_shutdown = nps_clkevent_set_state,
.tick_resume = nps_clkevent_set_state,
};
static irqreturn_t timer_irq_handler(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
nps_clkevent_rm_thread();
evt->event_handler(evt);
return IRQ_HANDLED;
}
static int nps_timer_starting_cpu(unsigned int cpu)
{
struct clock_event_device *evt = this_cpu_ptr(&nps_clockevent_device);
evt->cpumask = cpumask_of(smp_processor_id());
clockevents_config_and_register(evt, nps_timer0_freq, 0, ULONG_MAX);
enable_percpu_irq(nps_timer0_irq, IRQ_TYPE_NONE);
return 0;
}
static int nps_timer_dying_cpu(unsigned int cpu)
{
disable_percpu_irq(nps_timer0_irq);
return 0;
}
static int __init nps_setup_clockevent(struct device_node *node)
{
struct clk *clk;
int ret;
nps_timer0_irq = irq_of_parse_and_map(node, 0);
if (nps_timer0_irq <= 0) {
pr_err("clockevent: missing irq\n");
return -EINVAL;
}
ret = nps_get_timer_clk(node, &nps_timer0_freq, &clk);
if (ret)
return ret;
/* Needs apriori irq_set_percpu_devid() done in intc map function */
ret = request_percpu_irq(nps_timer0_irq, timer_irq_handler,
"Timer0 (per-cpu-tick)",
&nps_clockevent_device);
if (ret) {
pr_err("Couldn't request irq\n");
clk_disable_unprepare(clk);
return ret;
}
ret = cpuhp_setup_state(CPUHP_AP_ARC_TIMER_STARTING,
"clockevents/nps:starting",
nps_timer_starting_cpu,
nps_timer_dying_cpu);
if (ret) {
pr_err("Failed to setup hotplug state\n");
clk_disable_unprepare(clk);
free_percpu_irq(nps_timer0_irq, &nps_clockevent_device);
return ret;
}
return 0;
}
TIMER_OF_DECLARE(ezchip_nps400_clk_evt, "ezchip,nps400-timer0",
nps_setup_clockevent);
#endif /* CONFIG_EZNPS_MTM_EXT */
+237
View File
@@ -0,0 +1,237 @@
/*
* Copyright (c) 2017, Linaro Ltd. All rights reserved.
*
* Author: Daniel Lezcano <daniel.lezcano@linaro.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#include "timer-of.h"
/**
* timer_of_irq_exit - Release the interrupt
* @of_irq: an of_timer_irq structure pointer
*
* Free the irq resource
*/
static __init void timer_of_irq_exit(struct of_timer_irq *of_irq)
{
struct timer_of *to = container_of(of_irq, struct timer_of, of_irq);
struct clock_event_device *clkevt = &to->clkevt;
of_irq->percpu ? free_percpu_irq(of_irq->irq, clkevt) :
free_irq(of_irq->irq, clkevt);
}
/**
* timer_of_irq_init - Request the interrupt
* @np: a device tree node pointer
* @of_irq: an of_timer_irq structure pointer
*
* Get the interrupt number from the DT from its definition and
* request it. The interrupt is gotten by falling back the following way:
*
* - Get interrupt number by name
* - Get interrupt number by index
*
* When the interrupt is per CPU, 'request_percpu_irq()' is called,
* otherwise 'request_irq()' is used.
*
* Returns 0 on success, < 0 otherwise
*/
static __init int timer_of_irq_init(struct device_node *np,
struct of_timer_irq *of_irq)
{
int ret;
struct timer_of *to = container_of(of_irq, struct timer_of, of_irq);
struct clock_event_device *clkevt = &to->clkevt;
if (of_irq->name) {
of_irq->irq = ret = of_irq_get_byname(np, of_irq->name);
if (ret < 0) {
pr_err("Failed to get interrupt %s for %s\n",
of_irq->name, np->full_name);
return ret;
}
} else {
of_irq->irq = irq_of_parse_and_map(np, of_irq->index);
}
if (!of_irq->irq) {
pr_err("Failed to map interrupt for %pOF\n", np);
return -EINVAL;
}
ret = of_irq->percpu ?
request_percpu_irq(of_irq->irq, of_irq->handler,
np->full_name, clkevt) :
request_irq(of_irq->irq, of_irq->handler,
of_irq->flags ? of_irq->flags : IRQF_TIMER,
np->full_name, clkevt);
if (ret) {
pr_err("Failed to request irq %d for %pOF\n", of_irq->irq, np);
return ret;
}
clkevt->irq = of_irq->irq;
return 0;
}
/**
* timer_of_clk_exit - Release the clock resources
* @of_clk: a of_timer_clk structure pointer
*
* Disables and releases the refcount on the clk
*/
static __init void timer_of_clk_exit(struct of_timer_clk *of_clk)
{
of_clk->rate = 0;
clk_disable_unprepare(of_clk->clk);
clk_put(of_clk->clk);
}
/**
* timer_of_clk_init - Initialize the clock resources
* @np: a device tree node pointer
* @of_clk: a of_timer_clk structure pointer
*
* Get the clock by name or by index, enable it and get the rate
*
* Returns 0 on success, < 0 otherwise
*/
static __init int timer_of_clk_init(struct device_node *np,
struct of_timer_clk *of_clk)
{
int ret;
of_clk->clk = of_clk->name ? of_clk_get_by_name(np, of_clk->name) :
of_clk_get(np, of_clk->index);
if (IS_ERR(of_clk->clk)) {
pr_err("Failed to get clock for %pOF\n", np);
return PTR_ERR(of_clk->clk);
}
ret = clk_prepare_enable(of_clk->clk);
if (ret) {
pr_err("Failed for enable clock for %pOF\n", np);
goto out_clk_put;
}
of_clk->rate = clk_get_rate(of_clk->clk);
if (!of_clk->rate) {
ret = -EINVAL;
pr_err("Failed to get clock rate for %pOF\n", np);
goto out_clk_disable;
}
of_clk->period = DIV_ROUND_UP(of_clk->rate, HZ);
out:
return ret;
out_clk_disable:
clk_disable_unprepare(of_clk->clk);
out_clk_put:
clk_put(of_clk->clk);
goto out;
}
static __init void timer_of_base_exit(struct of_timer_base *of_base)
{
iounmap(of_base->base);
}
static __init int timer_of_base_init(struct device_node *np,
struct of_timer_base *of_base)
{
of_base->base = of_base->name ?
of_io_request_and_map(np, of_base->index, of_base->name) :
of_iomap(np, of_base->index);
if (IS_ERR(of_base->base)) {
pr_err("Failed to iomap (%s)\n", of_base->name);
return PTR_ERR(of_base->base);
}
return 0;
}
int __init timer_of_init(struct device_node *np, struct timer_of *to)
{
int ret = -EINVAL;
int flags = 0;
if (to->flags & TIMER_OF_BASE) {
ret = timer_of_base_init(np, &to->of_base);
if (ret)
goto out_fail;
flags |= TIMER_OF_BASE;
}
if (to->flags & TIMER_OF_CLOCK) {
ret = timer_of_clk_init(np, &to->of_clk);
if (ret)
goto out_fail;
flags |= TIMER_OF_CLOCK;
}
if (to->flags & TIMER_OF_IRQ) {
ret = timer_of_irq_init(np, &to->of_irq);
if (ret)
goto out_fail;
flags |= TIMER_OF_IRQ;
}
if (!to->clkevt.name)
to->clkevt.name = np->full_name;
to->np = np;
return ret;
out_fail:
if (flags & TIMER_OF_IRQ)
timer_of_irq_exit(&to->of_irq);
if (flags & TIMER_OF_CLOCK)
timer_of_clk_exit(&to->of_clk);
if (flags & TIMER_OF_BASE)
timer_of_base_exit(&to->of_base);
return ret;
}
/**
* timer_of_cleanup - release timer_of ressources
* @to: timer_of structure
*
* Release the ressources that has been used in timer_of_init().
* This function should be called in init error cases
*/
void __init timer_of_cleanup(struct timer_of *to)
{
if (to->flags & TIMER_OF_IRQ)
timer_of_irq_exit(&to->of_irq);
if (to->flags & TIMER_OF_CLOCK)
timer_of_clk_exit(&to->of_clk);
if (to->flags & TIMER_OF_BASE)
timer_of_base_exit(&to->of_base);
}
+74
View File
@@ -0,0 +1,74 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __TIMER_OF_H__
#define __TIMER_OF_H__
#include <linux/clockchips.h>
#define TIMER_OF_BASE 0x1
#define TIMER_OF_CLOCK 0x2
#define TIMER_OF_IRQ 0x4
struct of_timer_irq {
int irq;
int index;
int percpu;
const char *name;
unsigned long flags;
irq_handler_t handler;
};
struct of_timer_base {
void __iomem *base;
const char *name;
int index;
};
struct of_timer_clk {
struct clk *clk;
const char *name;
int index;
unsigned long rate;
unsigned long period;
};
struct timer_of {
unsigned int flags;
struct device_node *np;
struct clock_event_device clkevt;
struct of_timer_base of_base;
struct of_timer_irq of_irq;
struct of_timer_clk of_clk;
void *private_data;
};
static inline struct timer_of *to_timer_of(struct clock_event_device *clkevt)
{
return container_of(clkevt, struct timer_of, clkevt);
}
static inline void __iomem *timer_of_base(struct timer_of *to)
{
return to->of_base.base;
}
static inline int timer_of_irq(struct timer_of *to)
{
return to->of_irq.irq;
}
static inline unsigned long timer_of_rate(struct timer_of *to)
{
return to->of_clk.rate;
}
static inline unsigned long timer_of_period(struct timer_of *to)
{
return to->of_clk.period;
}
extern int __init timer_of_init(struct device_node *np,
struct timer_of *to);
extern void __init timer_of_cleanup(struct timer_of *to);
#endif
+299
View File
@@ -0,0 +1,299 @@
/*
* drivers/clocksource/timer-oxnas-rps.c
*
* Copyright (C) 2009 Oxford Semiconductor Ltd
* Copyright (C) 2013 Ma Haijun <mahaijuns@gmail.com>
* Copyright (C) 2016 Neil Armstrong <narmstrong@baylibre.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/clockchips.h>
#include <linux/sched_clock.h>
/* TIMER1 used as tick
* TIMER2 used as clocksource
*/
/* Registers definitions */
#define TIMER_LOAD_REG 0x0
#define TIMER_CURR_REG 0x4
#define TIMER_CTRL_REG 0x8
#define TIMER_CLRINT_REG 0xC
#define TIMER_BITS 24
#define TIMER_MAX_VAL (BIT(TIMER_BITS) - 1)
#define TIMER_PERIODIC BIT(6)
#define TIMER_ENABLE BIT(7)
#define TIMER_DIV1 (0)
#define TIMER_DIV16 (1 << 2)
#define TIMER_DIV256 (2 << 2)
#define TIMER1_REG_OFFSET 0
#define TIMER2_REG_OFFSET 0x20
/* Clockevent & Clocksource data */
struct oxnas_rps_timer {
struct clock_event_device clkevent;
void __iomem *clksrc_base;
void __iomem *clkevt_base;
unsigned long timer_period;
unsigned int timer_prescaler;
struct clk *clk;
int irq;
};
static irqreturn_t oxnas_rps_timer_irq(int irq, void *dev_id)
{
struct oxnas_rps_timer *rps = dev_id;
writel_relaxed(0, rps->clkevt_base + TIMER_CLRINT_REG);
rps->clkevent.event_handler(&rps->clkevent);
return IRQ_HANDLED;
}
static void oxnas_rps_timer_config(struct oxnas_rps_timer *rps,
unsigned long period,
unsigned int periodic)
{
uint32_t cfg = rps->timer_prescaler;
if (period)
cfg |= TIMER_ENABLE;
if (periodic)
cfg |= TIMER_PERIODIC;
writel_relaxed(period, rps->clkevt_base + TIMER_LOAD_REG);
writel_relaxed(cfg, rps->clkevt_base + TIMER_CTRL_REG);
}
static int oxnas_rps_timer_shutdown(struct clock_event_device *evt)
{
struct oxnas_rps_timer *rps =
container_of(evt, struct oxnas_rps_timer, clkevent);
oxnas_rps_timer_config(rps, 0, 0);
return 0;
}
static int oxnas_rps_timer_set_periodic(struct clock_event_device *evt)
{
struct oxnas_rps_timer *rps =
container_of(evt, struct oxnas_rps_timer, clkevent);
oxnas_rps_timer_config(rps, rps->timer_period, 1);
return 0;
}
static int oxnas_rps_timer_set_oneshot(struct clock_event_device *evt)
{
struct oxnas_rps_timer *rps =
container_of(evt, struct oxnas_rps_timer, clkevent);
oxnas_rps_timer_config(rps, rps->timer_period, 0);
return 0;
}
static int oxnas_rps_timer_next_event(unsigned long delta,
struct clock_event_device *evt)
{
struct oxnas_rps_timer *rps =
container_of(evt, struct oxnas_rps_timer, clkevent);
oxnas_rps_timer_config(rps, delta, 0);
return 0;
}
static int __init oxnas_rps_clockevent_init(struct oxnas_rps_timer *rps)
{
ulong clk_rate = clk_get_rate(rps->clk);
ulong timer_rate;
/* Start with prescaler 1 */
rps->timer_prescaler = TIMER_DIV1;
rps->timer_period = DIV_ROUND_UP(clk_rate, HZ);
timer_rate = clk_rate;
if (rps->timer_period > TIMER_MAX_VAL) {
rps->timer_prescaler = TIMER_DIV16;
timer_rate = clk_rate / 16;
rps->timer_period = DIV_ROUND_UP(timer_rate, HZ);
}
if (rps->timer_period > TIMER_MAX_VAL) {
rps->timer_prescaler = TIMER_DIV256;
timer_rate = clk_rate / 256;
rps->timer_period = DIV_ROUND_UP(timer_rate, HZ);
}
rps->clkevent.name = "oxnas-rps";
rps->clkevent.features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT |
CLOCK_EVT_FEAT_DYNIRQ;
rps->clkevent.tick_resume = oxnas_rps_timer_shutdown;
rps->clkevent.set_state_shutdown = oxnas_rps_timer_shutdown;
rps->clkevent.set_state_periodic = oxnas_rps_timer_set_periodic;
rps->clkevent.set_state_oneshot = oxnas_rps_timer_set_oneshot;
rps->clkevent.set_next_event = oxnas_rps_timer_next_event;
rps->clkevent.rating = 200;
rps->clkevent.cpumask = cpu_possible_mask;
rps->clkevent.irq = rps->irq;
clockevents_config_and_register(&rps->clkevent,
timer_rate,
1,
TIMER_MAX_VAL);
pr_info("Registered clock event rate %luHz prescaler %x period %lu\n",
clk_rate,
rps->timer_prescaler,
rps->timer_period);
return 0;
}
/* Clocksource */
static void __iomem *timer_sched_base;
static u64 notrace oxnas_rps_read_sched_clock(void)
{
return ~readl_relaxed(timer_sched_base);
}
static int __init oxnas_rps_clocksource_init(struct oxnas_rps_timer *rps)
{
ulong clk_rate = clk_get_rate(rps->clk);
int ret;
/* use prescale 16 */
clk_rate = clk_rate / 16;
writel_relaxed(TIMER_MAX_VAL, rps->clksrc_base + TIMER_LOAD_REG);
writel_relaxed(TIMER_PERIODIC | TIMER_ENABLE | TIMER_DIV16,
rps->clksrc_base + TIMER_CTRL_REG);
timer_sched_base = rps->clksrc_base + TIMER_CURR_REG;
sched_clock_register(oxnas_rps_read_sched_clock,
TIMER_BITS, clk_rate);
ret = clocksource_mmio_init(timer_sched_base,
"oxnas_rps_clocksource_timer",
clk_rate, 250, TIMER_BITS,
clocksource_mmio_readl_down);
if (WARN_ON(ret)) {
pr_err("can't register clocksource\n");
return ret;
}
pr_info("Registered clocksource rate %luHz\n", clk_rate);
return 0;
}
static int __init oxnas_rps_timer_init(struct device_node *np)
{
struct oxnas_rps_timer *rps;
void __iomem *base;
int ret;
rps = kzalloc(sizeof(*rps), GFP_KERNEL);
if (!rps)
return -ENOMEM;
rps->clk = of_clk_get(np, 0);
if (IS_ERR(rps->clk)) {
ret = PTR_ERR(rps->clk);
goto err_alloc;
}
ret = clk_prepare_enable(rps->clk);
if (ret)
goto err_clk;
base = of_iomap(np, 0);
if (!base) {
ret = -ENXIO;
goto err_clk_prepare;
}
rps->irq = irq_of_parse_and_map(np, 0);
if (rps->irq < 0) {
ret = -EINVAL;
goto err_iomap;
}
rps->clkevt_base = base + TIMER1_REG_OFFSET;
rps->clksrc_base = base + TIMER2_REG_OFFSET;
/* Disable timers */
writel_relaxed(0, rps->clkevt_base + TIMER_CTRL_REG);
writel_relaxed(0, rps->clksrc_base + TIMER_CTRL_REG);
writel_relaxed(0, rps->clkevt_base + TIMER_LOAD_REG);
writel_relaxed(0, rps->clksrc_base + TIMER_LOAD_REG);
writel_relaxed(0, rps->clkevt_base + TIMER_CLRINT_REG);
writel_relaxed(0, rps->clksrc_base + TIMER_CLRINT_REG);
ret = request_irq(rps->irq, oxnas_rps_timer_irq,
IRQF_TIMER | IRQF_IRQPOLL,
"rps-timer", rps);
if (ret)
goto err_iomap;
ret = oxnas_rps_clocksource_init(rps);
if (ret)
goto err_irqreq;
ret = oxnas_rps_clockevent_init(rps);
if (ret)
goto err_irqreq;
return 0;
err_irqreq:
free_irq(rps->irq, rps);
err_iomap:
iounmap(base);
err_clk_prepare:
clk_disable_unprepare(rps->clk);
err_clk:
clk_put(rps->clk);
err_alloc:
kfree(rps);
return ret;
}
TIMER_OF_DECLARE(ox810se_rps,
"oxsemi,ox810se-rps-timer", oxnas_rps_timer_init);
TIMER_OF_DECLARE(ox820_rps,
"oxsemi,ox820-rps-timer", oxnas_rps_timer_init);
+249
View File
@@ -0,0 +1,249 @@
/*
* System timer for CSR SiRFprimaII
*
* Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
*
* Licensed under GPLv2 or later.
*/
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/bitops.h>
#include <linux/irq.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/sched_clock.h>
#define PRIMA2_CLOCK_FREQ 1000000
#define SIRFSOC_TIMER_COUNTER_LO 0x0000
#define SIRFSOC_TIMER_COUNTER_HI 0x0004
#define SIRFSOC_TIMER_MATCH_0 0x0008
#define SIRFSOC_TIMER_MATCH_1 0x000C
#define SIRFSOC_TIMER_MATCH_2 0x0010
#define SIRFSOC_TIMER_MATCH_3 0x0014
#define SIRFSOC_TIMER_MATCH_4 0x0018
#define SIRFSOC_TIMER_MATCH_5 0x001C
#define SIRFSOC_TIMER_STATUS 0x0020
#define SIRFSOC_TIMER_INT_EN 0x0024
#define SIRFSOC_TIMER_WATCHDOG_EN 0x0028
#define SIRFSOC_TIMER_DIV 0x002C
#define SIRFSOC_TIMER_LATCH 0x0030
#define SIRFSOC_TIMER_LATCHED_LO 0x0034
#define SIRFSOC_TIMER_LATCHED_HI 0x0038
#define SIRFSOC_TIMER_WDT_INDEX 5
#define SIRFSOC_TIMER_LATCH_BIT BIT(0)
#define SIRFSOC_TIMER_REG_CNT 11
static const u32 sirfsoc_timer_reg_list[SIRFSOC_TIMER_REG_CNT] = {
SIRFSOC_TIMER_MATCH_0, SIRFSOC_TIMER_MATCH_1, SIRFSOC_TIMER_MATCH_2,
SIRFSOC_TIMER_MATCH_3, SIRFSOC_TIMER_MATCH_4, SIRFSOC_TIMER_MATCH_5,
SIRFSOC_TIMER_INT_EN, SIRFSOC_TIMER_WATCHDOG_EN, SIRFSOC_TIMER_DIV,
SIRFSOC_TIMER_LATCHED_LO, SIRFSOC_TIMER_LATCHED_HI,
};
static u32 sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT];
static void __iomem *sirfsoc_timer_base;
/* timer0 interrupt handler */
static irqreturn_t sirfsoc_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *ce = dev_id;
WARN_ON(!(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_STATUS) &
BIT(0)));
/* clear timer0 interrupt */
writel_relaxed(BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_STATUS);
ce->event_handler(ce);
return IRQ_HANDLED;
}
/* read 64-bit timer counter */
static u64 notrace sirfsoc_timer_read(struct clocksource *cs)
{
u64 cycles;
/* latch the 64-bit timer counter */
writel_relaxed(SIRFSOC_TIMER_LATCH_BIT,
sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
cycles = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_HI);
cycles = (cycles << 32) |
readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO);
return cycles;
}
static int sirfsoc_timer_set_next_event(unsigned long delta,
struct clock_event_device *ce)
{
unsigned long now, next;
writel_relaxed(SIRFSOC_TIMER_LATCH_BIT,
sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
now = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO);
next = now + delta;
writel_relaxed(next, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_0);
writel_relaxed(SIRFSOC_TIMER_LATCH_BIT,
sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
now = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO);
return next - now > delta ? -ETIME : 0;
}
static int sirfsoc_timer_shutdown(struct clock_event_device *evt)
{
u32 val = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
writel_relaxed(val & ~BIT(0),
sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
return 0;
}
static int sirfsoc_timer_set_oneshot(struct clock_event_device *evt)
{
u32 val = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
writel_relaxed(val | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
return 0;
}
static void sirfsoc_clocksource_suspend(struct clocksource *cs)
{
int i;
writel_relaxed(SIRFSOC_TIMER_LATCH_BIT,
sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
for (i = 0; i < SIRFSOC_TIMER_REG_CNT; i++)
sirfsoc_timer_reg_val[i] =
readl_relaxed(sirfsoc_timer_base +
sirfsoc_timer_reg_list[i]);
}
static void sirfsoc_clocksource_resume(struct clocksource *cs)
{
int i;
for (i = 0; i < SIRFSOC_TIMER_REG_CNT - 2; i++)
writel_relaxed(sirfsoc_timer_reg_val[i],
sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 2],
sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO);
writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 1],
sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI);
}
static struct clock_event_device sirfsoc_clockevent = {
.name = "sirfsoc_clockevent",
.rating = 200,
.features = CLOCK_EVT_FEAT_ONESHOT,
.set_state_shutdown = sirfsoc_timer_shutdown,
.set_state_oneshot = sirfsoc_timer_set_oneshot,
.set_next_event = sirfsoc_timer_set_next_event,
};
static struct clocksource sirfsoc_clocksource = {
.name = "sirfsoc_clocksource",
.rating = 200,
.mask = CLOCKSOURCE_MASK(64),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
.read = sirfsoc_timer_read,
.suspend = sirfsoc_clocksource_suspend,
.resume = sirfsoc_clocksource_resume,
};
static struct irqaction sirfsoc_timer_irq = {
.name = "sirfsoc_timer0",
.flags = IRQF_TIMER,
.irq = 0,
.handler = sirfsoc_timer_interrupt,
.dev_id = &sirfsoc_clockevent,
};
/* Overwrite weak default sched_clock with more precise one */
static u64 notrace sirfsoc_read_sched_clock(void)
{
return sirfsoc_timer_read(NULL);
}
static void __init sirfsoc_clockevent_init(void)
{
sirfsoc_clockevent.cpumask = cpumask_of(0);
clockevents_config_and_register(&sirfsoc_clockevent, PRIMA2_CLOCK_FREQ,
2, -2);
}
/* initialize the kernel jiffy timer source */
static int __init sirfsoc_prima2_timer_init(struct device_node *np)
{
unsigned long rate;
struct clk *clk;
int ret;
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
pr_err("Failed to get clock\n");
return PTR_ERR(clk);
}
ret = clk_prepare_enable(clk);
if (ret) {
pr_err("Failed to enable clock\n");
return ret;
}
rate = clk_get_rate(clk);
if (rate < PRIMA2_CLOCK_FREQ || rate % PRIMA2_CLOCK_FREQ) {
pr_err("Invalid clock rate\n");
return -EINVAL;
}
sirfsoc_timer_base = of_iomap(np, 0);
if (!sirfsoc_timer_base) {
pr_err("unable to map timer cpu registers\n");
return -ENXIO;
}
sirfsoc_timer_irq.irq = irq_of_parse_and_map(np, 0);
writel_relaxed(rate / PRIMA2_CLOCK_FREQ / 2 - 1,
sirfsoc_timer_base + SIRFSOC_TIMER_DIV);
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO);
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI);
writel_relaxed(BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_STATUS);
ret = clocksource_register_hz(&sirfsoc_clocksource, PRIMA2_CLOCK_FREQ);
if (ret) {
pr_err("Failed to register clocksource\n");
return ret;
}
sched_clock_register(sirfsoc_read_sched_clock, 64, PRIMA2_CLOCK_FREQ);
ret = setup_irq(sirfsoc_timer_irq.irq, &sirfsoc_timer_irq);
if (ret) {
pr_err("Failed to setup irq\n");
return ret;
}
sirfsoc_clockevent_init();
return 0;
}
TIMER_OF_DECLARE(sirfsoc_prima2_timer,
"sirf,prima2-tick", sirfsoc_prima2_timer_init);
+54
View File
@@ -0,0 +1,54 @@
/*
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/acpi.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/clocksource.h>
extern struct of_device_id __timer_of_table[];
static const struct of_device_id __timer_of_table_sentinel
__used __section(__timer_of_table_end);
void __init timer_probe(void)
{
struct device_node *np;
const struct of_device_id *match;
of_init_fn_1_ret init_func_ret;
unsigned timers = 0;
int ret;
for_each_matching_node_and_match(np, __timer_of_table, &match) {
if (!of_device_is_available(np))
continue;
init_func_ret = match->data;
ret = init_func_ret(np);
if (ret) {
pr_err("Failed to initialize '%pOF': %d\n", np, ret);
continue;
}
timers++;
}
timers += acpi_probe_device_table(timer);
if (!timers)
pr_crit("%s: no matching timers found\n", __func__);
}
+31
View File
@@ -0,0 +1,31 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* ARM timer implementation, found in Integrator, Versatile and Realview
* platforms. Not all platforms support all registers and bits in these
* registers, so we mark them with A for Integrator AP, C for Integrator
* CP, V for Versatile and R for Realview.
*
* Integrator AP has 16-bit timers, Integrator CP, Versatile and Realview
* can have 16-bit or 32-bit selectable via a bit in the control register.
*
* Every SP804 contains two identical timers.
*/
#define TIMER_1_BASE 0x00
#define TIMER_2_BASE 0x20
#define TIMER_LOAD 0x00 /* ACVR rw */
#define TIMER_VALUE 0x04 /* ACVR ro */
#define TIMER_CTRL 0x08 /* ACVR rw */
#define TIMER_CTRL_ONESHOT (1 << 0) /* CVR */
#define TIMER_CTRL_32BIT (1 << 1) /* CVR */
#define TIMER_CTRL_DIV1 (0 << 2) /* ACVR */
#define TIMER_CTRL_DIV16 (1 << 2) /* ACVR */
#define TIMER_CTRL_DIV256 (2 << 2) /* ACVR */
#define TIMER_CTRL_IE (1 << 5) /* VR */
#define TIMER_CTRL_PERIODIC (1 << 6) /* ACVR */
#define TIMER_CTRL_ENABLE (1 << 7) /* ACVR */
#define TIMER_INTCLR 0x0c /* ACVR wo */
#define TIMER_RIS 0x10 /* CVR ro */
#define TIMER_MIS 0x14 /* CVR ro */
#define TIMER_BGLOAD 0x18 /* CVR rw */
+339
View File
@@ -0,0 +1,339 @@
/*
* linux/drivers/clocksource/timer-sp.c
*
* Copyright (C) 1999 - 2003 ARM Limited
* Copyright (C) 2000 Deep Blue Solutions Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/clk.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_clk.h>
#include <linux/of_irq.h>
#include <linux/sched_clock.h>
#include <clocksource/timer-sp804.h>
#include "timer-sp.h"
static long __init sp804_get_clock_rate(struct clk *clk)
{
long rate;
int err;
err = clk_prepare(clk);
if (err) {
pr_err("sp804: clock failed to prepare: %d\n", err);
clk_put(clk);
return err;
}
err = clk_enable(clk);
if (err) {
pr_err("sp804: clock failed to enable: %d\n", err);
clk_unprepare(clk);
clk_put(clk);
return err;
}
rate = clk_get_rate(clk);
if (rate < 0) {
pr_err("sp804: clock failed to get rate: %ld\n", rate);
clk_disable(clk);
clk_unprepare(clk);
clk_put(clk);
}
return rate;
}
static void __iomem *sched_clock_base;
static u64 notrace sp804_read(void)
{
return ~readl_relaxed(sched_clock_base + TIMER_VALUE);
}
void __init sp804_timer_disable(void __iomem *base)
{
writel(0, base + TIMER_CTRL);
}
int __init __sp804_clocksource_and_sched_clock_init(void __iomem *base,
const char *name,
struct clk *clk,
int use_sched_clock)
{
long rate;
if (!clk) {
clk = clk_get_sys("sp804", name);
if (IS_ERR(clk)) {
pr_err("sp804: clock not found: %d\n",
(int)PTR_ERR(clk));
return PTR_ERR(clk);
}
}
rate = sp804_get_clock_rate(clk);
if (rate < 0)
return -EINVAL;
/* setup timer 0 as free-running clocksource */
writel(0, base + TIMER_CTRL);
writel(0xffffffff, base + TIMER_LOAD);
writel(0xffffffff, base + TIMER_VALUE);
writel(TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC,
base + TIMER_CTRL);
clocksource_mmio_init(base + TIMER_VALUE, name,
rate, 200, 32, clocksource_mmio_readl_down);
if (use_sched_clock) {
sched_clock_base = base;
sched_clock_register(sp804_read, 32, rate);
}
return 0;
}
static void __iomem *clkevt_base;
static unsigned long clkevt_reload;
/*
* IRQ handler for the timer
*/
static irqreturn_t sp804_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
/* clear the interrupt */
writel(1, clkevt_base + TIMER_INTCLR);
evt->event_handler(evt);
return IRQ_HANDLED;
}
static inline void timer_shutdown(struct clock_event_device *evt)
{
writel(0, clkevt_base + TIMER_CTRL);
}
static int sp804_shutdown(struct clock_event_device *evt)
{
timer_shutdown(evt);
return 0;
}
static int sp804_set_periodic(struct clock_event_device *evt)
{
unsigned long ctrl = TIMER_CTRL_32BIT | TIMER_CTRL_IE |
TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE;
timer_shutdown(evt);
writel(clkevt_reload, clkevt_base + TIMER_LOAD);
writel(ctrl, clkevt_base + TIMER_CTRL);
return 0;
}
static int sp804_set_next_event(unsigned long next,
struct clock_event_device *evt)
{
unsigned long ctrl = TIMER_CTRL_32BIT | TIMER_CTRL_IE |
TIMER_CTRL_ONESHOT | TIMER_CTRL_ENABLE;
writel(next, clkevt_base + TIMER_LOAD);
writel(ctrl, clkevt_base + TIMER_CTRL);
return 0;
}
static struct clock_event_device sp804_clockevent = {
.features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT |
CLOCK_EVT_FEAT_DYNIRQ,
.set_state_shutdown = sp804_shutdown,
.set_state_periodic = sp804_set_periodic,
.set_state_oneshot = sp804_shutdown,
.tick_resume = sp804_shutdown,
.set_next_event = sp804_set_next_event,
.rating = 300,
};
static struct irqaction sp804_timer_irq = {
.name = "timer",
.flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = sp804_timer_interrupt,
.dev_id = &sp804_clockevent,
};
int __init __sp804_clockevents_init(void __iomem *base, unsigned int irq, struct clk *clk, const char *name)
{
struct clock_event_device *evt = &sp804_clockevent;
long rate;
if (!clk)
clk = clk_get_sys("sp804", name);
if (IS_ERR(clk)) {
pr_err("sp804: %s clock not found: %d\n", name,
(int)PTR_ERR(clk));
return PTR_ERR(clk);
}
rate = sp804_get_clock_rate(clk);
if (rate < 0)
return -EINVAL;
clkevt_base = base;
clkevt_reload = DIV_ROUND_CLOSEST(rate, HZ);
evt->name = name;
evt->irq = irq;
evt->cpumask = cpu_possible_mask;
writel(0, base + TIMER_CTRL);
setup_irq(irq, &sp804_timer_irq);
clockevents_config_and_register(evt, rate, 0xf, 0xffffffff);
return 0;
}
static int __init sp804_of_init(struct device_node *np)
{
static bool initialized = false;
void __iomem *base;
int irq, ret = -EINVAL;
u32 irq_num = 0;
struct clk *clk1, *clk2;
const char *name = of_get_property(np, "compatible", NULL);
base = of_iomap(np, 0);
if (!base)
return -ENXIO;
/* Ensure timers are disabled */
writel(0, base + TIMER_CTRL);
writel(0, base + TIMER_2_BASE + TIMER_CTRL);
if (initialized || !of_device_is_available(np)) {
ret = -EINVAL;
goto err;
}
clk1 = of_clk_get(np, 0);
if (IS_ERR(clk1))
clk1 = NULL;
/* Get the 2nd clock if the timer has 3 timer clocks */
if (of_clk_get_parent_count(np) == 3) {
clk2 = of_clk_get(np, 1);
if (IS_ERR(clk2)) {
pr_err("sp804: %s clock not found: %d\n", np->name,
(int)PTR_ERR(clk2));
clk2 = NULL;
}
} else
clk2 = clk1;
irq = irq_of_parse_and_map(np, 0);
if (irq <= 0)
goto err;
of_property_read_u32(np, "arm,sp804-has-irq", &irq_num);
if (irq_num == 2) {
ret = __sp804_clockevents_init(base + TIMER_2_BASE, irq, clk2, name);
if (ret)
goto err;
ret = __sp804_clocksource_and_sched_clock_init(base, name, clk1, 1);
if (ret)
goto err;
} else {
ret = __sp804_clockevents_init(base, irq, clk1 , name);
if (ret)
goto err;
ret =__sp804_clocksource_and_sched_clock_init(base + TIMER_2_BASE,
name, clk2, 1);
if (ret)
goto err;
}
initialized = true;
return 0;
err:
iounmap(base);
return ret;
}
TIMER_OF_DECLARE(sp804, "arm,sp804", sp804_of_init);
static int __init integrator_cp_of_init(struct device_node *np)
{
static int init_count = 0;
void __iomem *base;
int irq, ret = -EINVAL;
const char *name = of_get_property(np, "compatible", NULL);
struct clk *clk;
base = of_iomap(np, 0);
if (!base) {
pr_err("Failed to iomap\n");
return -ENXIO;
}
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
pr_err("Failed to get clock\n");
return PTR_ERR(clk);
}
/* Ensure timer is disabled */
writel(0, base + TIMER_CTRL);
if (init_count == 2 || !of_device_is_available(np))
goto err;
if (!init_count) {
ret = __sp804_clocksource_and_sched_clock_init(base, name, clk, 0);
if (ret)
goto err;
} else {
irq = irq_of_parse_and_map(np, 0);
if (irq <= 0)
goto err;
ret = __sp804_clockevents_init(base, irq, clk, name);
if (ret)
goto err;
}
init_count++;
return 0;
err:
iounmap(base);
return ret;
}
TIMER_OF_DECLARE(intcp, "arm,integrator-cp-timer", integrator_cp_of_init);
+209
View File
@@ -0,0 +1,209 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2017 Spreadtrum Communications Inc.
*/
#include <linux/init.h>
#include <linux/interrupt.h>
#include "timer-of.h"
#define TIMER_NAME "sprd_timer"
#define TIMER_LOAD_LO 0x0
#define TIMER_LOAD_HI 0x4
#define TIMER_VALUE_LO 0x8
#define TIMER_VALUE_HI 0xc
#define TIMER_CTL 0x10
#define TIMER_CTL_PERIOD_MODE BIT(0)
#define TIMER_CTL_ENABLE BIT(1)
#define TIMER_CTL_64BIT_WIDTH BIT(16)
#define TIMER_INT 0x14
#define TIMER_INT_EN BIT(0)
#define TIMER_INT_RAW_STS BIT(1)
#define TIMER_INT_MASK_STS BIT(2)
#define TIMER_INT_CLR BIT(3)
#define TIMER_VALUE_SHDW_LO 0x18
#define TIMER_VALUE_SHDW_HI 0x1c
#define TIMER_VALUE_LO_MASK GENMASK(31, 0)
static void sprd_timer_enable(void __iomem *base, u32 flag)
{
u32 val = readl_relaxed(base + TIMER_CTL);
val |= TIMER_CTL_ENABLE;
if (flag & TIMER_CTL_64BIT_WIDTH)
val |= TIMER_CTL_64BIT_WIDTH;
else
val &= ~TIMER_CTL_64BIT_WIDTH;
if (flag & TIMER_CTL_PERIOD_MODE)
val |= TIMER_CTL_PERIOD_MODE;
else
val &= ~TIMER_CTL_PERIOD_MODE;
writel_relaxed(val, base + TIMER_CTL);
}
static void sprd_timer_disable(void __iomem *base)
{
u32 val = readl_relaxed(base + TIMER_CTL);
val &= ~TIMER_CTL_ENABLE;
writel_relaxed(val, base + TIMER_CTL);
}
static void sprd_timer_update_counter(void __iomem *base, unsigned long cycles)
{
writel_relaxed(cycles & TIMER_VALUE_LO_MASK, base + TIMER_LOAD_LO);
writel_relaxed(0, base + TIMER_LOAD_HI);
}
static void sprd_timer_enable_interrupt(void __iomem *base)
{
writel_relaxed(TIMER_INT_EN, base + TIMER_INT);
}
static void sprd_timer_clear_interrupt(void __iomem *base)
{
u32 val = readl_relaxed(base + TIMER_INT);
val |= TIMER_INT_CLR;
writel_relaxed(val, base + TIMER_INT);
}
static int sprd_timer_set_next_event(unsigned long cycles,
struct clock_event_device *ce)
{
struct timer_of *to = to_timer_of(ce);
sprd_timer_disable(timer_of_base(to));
sprd_timer_update_counter(timer_of_base(to), cycles);
sprd_timer_enable(timer_of_base(to), 0);
return 0;
}
static int sprd_timer_set_periodic(struct clock_event_device *ce)
{
struct timer_of *to = to_timer_of(ce);
sprd_timer_disable(timer_of_base(to));
sprd_timer_update_counter(timer_of_base(to), timer_of_period(to));
sprd_timer_enable(timer_of_base(to), TIMER_CTL_PERIOD_MODE);
return 0;
}
static int sprd_timer_shutdown(struct clock_event_device *ce)
{
struct timer_of *to = to_timer_of(ce);
sprd_timer_disable(timer_of_base(to));
return 0;
}
static irqreturn_t sprd_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *ce = (struct clock_event_device *)dev_id;
struct timer_of *to = to_timer_of(ce);
sprd_timer_clear_interrupt(timer_of_base(to));
if (clockevent_state_oneshot(ce))
sprd_timer_disable(timer_of_base(to));
ce->event_handler(ce);
return IRQ_HANDLED;
}
static struct timer_of to = {
.flags = TIMER_OF_IRQ | TIMER_OF_BASE | TIMER_OF_CLOCK,
.clkevt = {
.name = TIMER_NAME,
.rating = 300,
.features = CLOCK_EVT_FEAT_DYNIRQ | CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT,
.set_state_shutdown = sprd_timer_shutdown,
.set_state_periodic = sprd_timer_set_periodic,
.set_next_event = sprd_timer_set_next_event,
.cpumask = cpu_possible_mask,
},
.of_irq = {
.handler = sprd_timer_interrupt,
.flags = IRQF_TIMER | IRQF_IRQPOLL,
},
};
static int __init sprd_timer_init(struct device_node *np)
{
int ret;
ret = timer_of_init(np, &to);
if (ret)
return ret;
sprd_timer_enable_interrupt(timer_of_base(&to));
clockevents_config_and_register(&to.clkevt, timer_of_rate(&to),
1, UINT_MAX);
return 0;
}
static struct timer_of suspend_to = {
.flags = TIMER_OF_BASE | TIMER_OF_CLOCK,
};
static u64 sprd_suspend_timer_read(struct clocksource *cs)
{
return ~(u64)readl_relaxed(timer_of_base(&suspend_to) +
TIMER_VALUE_SHDW_LO) & cs->mask;
}
static int sprd_suspend_timer_enable(struct clocksource *cs)
{
sprd_timer_update_counter(timer_of_base(&suspend_to),
TIMER_VALUE_LO_MASK);
sprd_timer_enable(timer_of_base(&suspend_to), TIMER_CTL_PERIOD_MODE);
return 0;
}
static void sprd_suspend_timer_disable(struct clocksource *cs)
{
sprd_timer_disable(timer_of_base(&suspend_to));
}
static struct clocksource suspend_clocksource = {
.name = "sprd_suspend_timer",
.rating = 200,
.read = sprd_suspend_timer_read,
.enable = sprd_suspend_timer_enable,
.disable = sprd_suspend_timer_disable,
.mask = CLOCKSOURCE_MASK(32),
.flags = CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_SUSPEND_NONSTOP,
};
static int __init sprd_suspend_timer_init(struct device_node *np)
{
int ret;
ret = timer_of_init(np, &suspend_to);
if (ret)
return ret;
clocksource_register_hz(&suspend_clocksource,
timer_of_rate(&suspend_to));
return 0;
}
TIMER_OF_DECLARE(sc9860_timer, "sprd,sc9860-timer", sprd_timer_init);
TIMER_OF_DECLARE(sc9860_persistent_timer, "sprd,sc9860-suspend-timer",
sprd_suspend_timer_init);
+336
View File
@@ -0,0 +1,336 @@
/*
* Copyright (C) Maxime Coquelin 2015
* Author: Maxime Coquelin <mcoquelin.stm32@gmail.com>
* License terms: GNU General Public License (GPL), version 2
*
* Inspired by time-efm32.c from Uwe Kleine-Koenig
*/
#include <linux/kernel.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/clk.h>
#include <linux/reset.h>
#include <linux/sched_clock.h>
#include <linux/slab.h>
#include "timer-of.h"
#define TIM_CR1 0x00
#define TIM_DIER 0x0c
#define TIM_SR 0x10
#define TIM_EGR 0x14
#define TIM_CNT 0x24
#define TIM_PSC 0x28
#define TIM_ARR 0x2c
#define TIM_CCR1 0x34
#define TIM_CR1_CEN BIT(0)
#define TIM_CR1_UDIS BIT(1)
#define TIM_CR1_OPM BIT(3)
#define TIM_CR1_ARPE BIT(7)
#define TIM_DIER_UIE BIT(0)
#define TIM_DIER_CC1IE BIT(1)
#define TIM_SR_UIF BIT(0)
#define TIM_EGR_UG BIT(0)
#define TIM_PSC_MAX USHRT_MAX
#define TIM_PSC_CLKRATE 10000
struct stm32_timer_private {
int bits;
};
/**
* stm32_timer_of_bits_set - set accessor helper
* @to: a timer_of structure pointer
* @bits: the number of bits (16 or 32)
*
* Accessor helper to set the number of bits in the timer-of private
* structure.
*
*/
static void stm32_timer_of_bits_set(struct timer_of *to, int bits)
{
struct stm32_timer_private *pd = to->private_data;
pd->bits = bits;
}
/**
* stm32_timer_of_bits_get - get accessor helper
* @to: a timer_of structure pointer
*
* Accessor helper to get the number of bits in the timer-of private
* structure.
*
* Returns an integer corresponding to the number of bits.
*/
static int stm32_timer_of_bits_get(struct timer_of *to)
{
struct stm32_timer_private *pd = to->private_data;
return pd->bits;
}
static void __iomem *stm32_timer_cnt __read_mostly;
static u64 notrace stm32_read_sched_clock(void)
{
return readl_relaxed(stm32_timer_cnt);
}
static struct delay_timer stm32_timer_delay;
static unsigned long stm32_read_delay(void)
{
return readl_relaxed(stm32_timer_cnt);
}
static void stm32_clock_event_disable(struct timer_of *to)
{
writel_relaxed(0, timer_of_base(to) + TIM_DIER);
}
/**
* stm32_timer_start - Start the counter without event
* @to: a timer_of structure pointer
*
* Start the timer in order to have the counter reset and start
* incrementing but disable interrupt event when there is a counter
* overflow. By default, the counter direction is used as upcounter.
*/
static void stm32_timer_start(struct timer_of *to)
{
writel_relaxed(TIM_CR1_UDIS | TIM_CR1_CEN, timer_of_base(to) + TIM_CR1);
}
static int stm32_clock_event_shutdown(struct clock_event_device *clkevt)
{
struct timer_of *to = to_timer_of(clkevt);
stm32_clock_event_disable(to);
return 0;
}
static int stm32_clock_event_set_next_event(unsigned long evt,
struct clock_event_device *clkevt)
{
struct timer_of *to = to_timer_of(clkevt);
unsigned long now, next;
next = readl_relaxed(timer_of_base(to) + TIM_CNT) + evt;
writel_relaxed(next, timer_of_base(to) + TIM_CCR1);
now = readl_relaxed(timer_of_base(to) + TIM_CNT);
if ((next - now) > evt)
return -ETIME;
writel_relaxed(TIM_DIER_CC1IE, timer_of_base(to) + TIM_DIER);
return 0;
}
static int stm32_clock_event_set_periodic(struct clock_event_device *clkevt)
{
struct timer_of *to = to_timer_of(clkevt);
stm32_timer_start(to);
return stm32_clock_event_set_next_event(timer_of_period(to), clkevt);
}
static int stm32_clock_event_set_oneshot(struct clock_event_device *clkevt)
{
struct timer_of *to = to_timer_of(clkevt);
stm32_timer_start(to);
return 0;
}
static irqreturn_t stm32_clock_event_handler(int irq, void *dev_id)
{
struct clock_event_device *clkevt = (struct clock_event_device *)dev_id;
struct timer_of *to = to_timer_of(clkevt);
writel_relaxed(0, timer_of_base(to) + TIM_SR);
if (clockevent_state_periodic(clkevt))
stm32_clock_event_set_periodic(clkevt);
else
stm32_clock_event_shutdown(clkevt);
clkevt->event_handler(clkevt);
return IRQ_HANDLED;
}
/**
* stm32_timer_width - Sort out the timer width (32/16)
* @to: a pointer to a timer-of structure
*
* Write the 32-bit max value and read/return the result. If the timer
* is 32 bits wide, the result will be UINT_MAX, otherwise it will
* be truncated by the 16-bit register to USHRT_MAX.
*
*/
static void __init stm32_timer_set_width(struct timer_of *to)
{
u32 width;
writel_relaxed(UINT_MAX, timer_of_base(to) + TIM_ARR);
width = readl_relaxed(timer_of_base(to) + TIM_ARR);
stm32_timer_of_bits_set(to, width == UINT_MAX ? 32 : 16);
}
/**
* stm32_timer_set_prescaler - Compute and set the prescaler register
* @to: a pointer to a timer-of structure
*
* Depending on the timer width, compute the prescaler to always
* target a 10MHz timer rate for 16 bits. 32-bit timers are
* considered precise and long enough to not use the prescaler.
*/
static void __init stm32_timer_set_prescaler(struct timer_of *to)
{
int prescaler = 1;
if (stm32_timer_of_bits_get(to) != 32) {
prescaler = DIV_ROUND_CLOSEST(timer_of_rate(to),
TIM_PSC_CLKRATE);
/*
* The prescaler register is an u16, the variable
* can't be greater than TIM_PSC_MAX, let's cap it in
* this case.
*/
prescaler = prescaler < TIM_PSC_MAX ? prescaler : TIM_PSC_MAX;
}
writel_relaxed(prescaler - 1, timer_of_base(to) + TIM_PSC);
writel_relaxed(TIM_EGR_UG, timer_of_base(to) + TIM_EGR);
writel_relaxed(0, timer_of_base(to) + TIM_SR);
/* Adjust rate and period given the prescaler value */
to->of_clk.rate = DIV_ROUND_CLOSEST(to->of_clk.rate, prescaler);
to->of_clk.period = DIV_ROUND_UP(to->of_clk.rate, HZ);
}
static int __init stm32_clocksource_init(struct timer_of *to)
{
u32 bits = stm32_timer_of_bits_get(to);
const char *name = to->np->full_name;
/*
* This driver allows to register several timers and relies on
* the generic time framework to select the right one.
* However, nothing allows to do the same for the
* sched_clock. We are not interested in a sched_clock for the
* 16-bit timers but only for the 32-bit one, so if no 32-bit
* timer is registered yet, we select this 32-bit timer as a
* sched_clock.
*/
if (bits == 32 && !stm32_timer_cnt) {
/*
* Start immediately the counter as we will be using
* it right after.
*/
stm32_timer_start(to);
stm32_timer_cnt = timer_of_base(to) + TIM_CNT;
sched_clock_register(stm32_read_sched_clock, bits, timer_of_rate(to));
pr_info("%s: STM32 sched_clock registered\n", name);
stm32_timer_delay.read_current_timer = stm32_read_delay;
stm32_timer_delay.freq = timer_of_rate(to);
register_current_timer_delay(&stm32_timer_delay);
pr_info("%s: STM32 delay timer registered\n", name);
}
return clocksource_mmio_init(timer_of_base(to) + TIM_CNT, name,
timer_of_rate(to), bits == 32 ? 250 : 100,
bits, clocksource_mmio_readl_up);
}
static void __init stm32_clockevent_init(struct timer_of *to)
{
u32 bits = stm32_timer_of_bits_get(to);
to->clkevt.name = to->np->full_name;
to->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
to->clkevt.set_state_shutdown = stm32_clock_event_shutdown;
to->clkevt.set_state_periodic = stm32_clock_event_set_periodic;
to->clkevt.set_state_oneshot = stm32_clock_event_set_oneshot;
to->clkevt.tick_resume = stm32_clock_event_shutdown;
to->clkevt.set_next_event = stm32_clock_event_set_next_event;
to->clkevt.rating = bits == 32 ? 250 : 100;
clockevents_config_and_register(&to->clkevt, timer_of_rate(to), 0x1,
(1 << bits) - 1);
pr_info("%pOF: STM32 clockevent driver initialized (%d bits)\n",
to->np, bits);
}
static int __init stm32_timer_init(struct device_node *node)
{
struct reset_control *rstc;
struct timer_of *to;
int ret;
to = kzalloc(sizeof(*to), GFP_KERNEL);
if (!to)
return -ENOMEM;
to->flags = TIMER_OF_IRQ | TIMER_OF_CLOCK | TIMER_OF_BASE;
to->of_irq.handler = stm32_clock_event_handler;
ret = timer_of_init(node, to);
if (ret)
goto err;
to->private_data = kzalloc(sizeof(struct stm32_timer_private),
GFP_KERNEL);
if (!to->private_data) {
ret = -ENOMEM;
goto deinit;
}
rstc = of_reset_control_get(node, NULL);
if (!IS_ERR(rstc)) {
reset_control_assert(rstc);
reset_control_deassert(rstc);
}
stm32_timer_set_width(to);
stm32_timer_set_prescaler(to);
ret = stm32_clocksource_init(to);
if (ret)
goto deinit;
stm32_clockevent_init(to);
return 0;
deinit:
timer_of_cleanup(to);
err:
kfree(to);
return ret;
}
TIMER_OF_DECLARE(stm32, "st,stm32-timer", stm32_timer_init);
+375
View File
@@ -0,0 +1,375 @@
/*
* Allwinner SoCs hstimer driver.
*
* Copyright (C) 2013 Maxime Ripard
*
* Maxime Ripard <maxime.ripard@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqreturn.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#define TIMER_IRQ_EN_REG 0x00
#define TIMER_IRQ_EN(val) BIT(val)
#define TIMER_IRQ_ST_REG 0x04
#define TIMER_CTL_REG(val) (0x20 * (val) + 0x10)
#define TIMER_CTL_ENABLE BIT(0)
#define TIMER_CTL_RELOAD BIT(1)
#define TIMER_CTL_CLK_PRES(val) (((val) & 0x7) << 4)
#define TIMER_CTL_ONESHOT BIT(7)
#define TIMER_INTVAL_LO_REG(val) (0x20 * (val) + 0x14)
#define TIMER_INTVAL_HI_REG(val) (0x20 * (val) + 0x18)
#define TIMER_CNTVAL_LO_REG(val) (0x20 * (val) + 0x1c)
#define TIMER_CNTVAL_HI_REG(val) (0x20 * (val) + 0x20)
#define TIMER_SYNC_TICKS 3
struct sun5i_timer {
void __iomem *base;
struct clk *clk;
struct notifier_block clk_rate_cb;
u32 ticks_per_jiffy;
};
#define to_sun5i_timer(x) \
container_of(x, struct sun5i_timer, clk_rate_cb)
struct sun5i_timer_clksrc {
struct sun5i_timer timer;
struct clocksource clksrc;
};
#define to_sun5i_timer_clksrc(x) \
container_of(x, struct sun5i_timer_clksrc, clksrc)
struct sun5i_timer_clkevt {
struct sun5i_timer timer;
struct clock_event_device clkevt;
};
#define to_sun5i_timer_clkevt(x) \
container_of(x, struct sun5i_timer_clkevt, clkevt)
/*
* When we disable a timer, we need to wait at least for 2 cycles of
* the timer source clock. We will use for that the clocksource timer
* that is already setup and runs at the same frequency than the other
* timers, and we never will be disabled.
*/
static void sun5i_clkevt_sync(struct sun5i_timer_clkevt *ce)
{
u32 old = readl(ce->timer.base + TIMER_CNTVAL_LO_REG(1));
while ((old - readl(ce->timer.base + TIMER_CNTVAL_LO_REG(1))) < TIMER_SYNC_TICKS)
cpu_relax();
}
static void sun5i_clkevt_time_stop(struct sun5i_timer_clkevt *ce, u8 timer)
{
u32 val = readl(ce->timer.base + TIMER_CTL_REG(timer));
writel(val & ~TIMER_CTL_ENABLE, ce->timer.base + TIMER_CTL_REG(timer));
sun5i_clkevt_sync(ce);
}
static void sun5i_clkevt_time_setup(struct sun5i_timer_clkevt *ce, u8 timer, u32 delay)
{
writel(delay, ce->timer.base + TIMER_INTVAL_LO_REG(timer));
}
static void sun5i_clkevt_time_start(struct sun5i_timer_clkevt *ce, u8 timer, bool periodic)
{
u32 val = readl(ce->timer.base + TIMER_CTL_REG(timer));
if (periodic)
val &= ~TIMER_CTL_ONESHOT;
else
val |= TIMER_CTL_ONESHOT;
writel(val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD,
ce->timer.base + TIMER_CTL_REG(timer));
}
static int sun5i_clkevt_shutdown(struct clock_event_device *clkevt)
{
struct sun5i_timer_clkevt *ce = to_sun5i_timer_clkevt(clkevt);
sun5i_clkevt_time_stop(ce, 0);
return 0;
}
static int sun5i_clkevt_set_oneshot(struct clock_event_device *clkevt)
{
struct sun5i_timer_clkevt *ce = to_sun5i_timer_clkevt(clkevt);
sun5i_clkevt_time_stop(ce, 0);
sun5i_clkevt_time_start(ce, 0, false);
return 0;
}
static int sun5i_clkevt_set_periodic(struct clock_event_device *clkevt)
{
struct sun5i_timer_clkevt *ce = to_sun5i_timer_clkevt(clkevt);
sun5i_clkevt_time_stop(ce, 0);
sun5i_clkevt_time_setup(ce, 0, ce->timer.ticks_per_jiffy);
sun5i_clkevt_time_start(ce, 0, true);
return 0;
}
static int sun5i_clkevt_next_event(unsigned long evt,
struct clock_event_device *clkevt)
{
struct sun5i_timer_clkevt *ce = to_sun5i_timer_clkevt(clkevt);
sun5i_clkevt_time_stop(ce, 0);
sun5i_clkevt_time_setup(ce, 0, evt - TIMER_SYNC_TICKS);
sun5i_clkevt_time_start(ce, 0, false);
return 0;
}
static irqreturn_t sun5i_timer_interrupt(int irq, void *dev_id)
{
struct sun5i_timer_clkevt *ce = (struct sun5i_timer_clkevt *)dev_id;
writel(0x1, ce->timer.base + TIMER_IRQ_ST_REG);
ce->clkevt.event_handler(&ce->clkevt);
return IRQ_HANDLED;
}
static u64 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,
unsigned long event, void *data)
{
struct clk_notifier_data *ndata = data;
struct sun5i_timer *timer = to_sun5i_timer(nb);
struct sun5i_timer_clksrc *cs = container_of(timer, struct sun5i_timer_clksrc, timer);
switch (event) {
case PRE_RATE_CHANGE:
clocksource_unregister(&cs->clksrc);
break;
case POST_RATE_CHANGE:
clocksource_register_hz(&cs->clksrc, ndata->new_rate);
break;
default:
break;
}
return NOTIFY_DONE;
}
static int __init sun5i_setup_clocksource(struct device_node *node,
void __iomem *base,
struct clk *clk, int irq)
{
struct sun5i_timer_clksrc *cs;
unsigned long rate;
int ret;
cs = kzalloc(sizeof(*cs), GFP_KERNEL);
if (!cs)
return -ENOMEM;
ret = clk_prepare_enable(clk);
if (ret) {
pr_err("Couldn't enable parent clock\n");
goto err_free;
}
rate = clk_get_rate(clk);
if (!rate) {
pr_err("Couldn't get parent clock rate\n");
ret = -EINVAL;
goto err_disable_clk;
}
cs->timer.base = base;
cs->timer.clk = clk;
cs->timer.clk_rate_cb.notifier_call = sun5i_rate_cb_clksrc;
cs->timer.clk_rate_cb.next = NULL;
ret = clk_notifier_register(clk, &cs->timer.clk_rate_cb);
if (ret) {
pr_err("Unable to register clock notifier.\n");
goto err_disable_clk;
}
writel(~0, base + TIMER_INTVAL_LO_REG(1));
writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD,
base + TIMER_CTL_REG(1));
cs->clksrc.name = node->name;
cs->clksrc.rating = 340;
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) {
pr_err("Couldn't register clock source.\n");
goto err_remove_notifier;
}
return 0;
err_remove_notifier:
clk_notifier_unregister(clk, &cs->timer.clk_rate_cb);
err_disable_clk:
clk_disable_unprepare(clk);
err_free:
kfree(cs);
return ret;
}
static int sun5i_rate_cb_clkevt(struct notifier_block *nb,
unsigned long event, void *data)
{
struct clk_notifier_data *ndata = data;
struct sun5i_timer *timer = to_sun5i_timer(nb);
struct sun5i_timer_clkevt *ce = container_of(timer, struct sun5i_timer_clkevt, timer);
if (event == POST_RATE_CHANGE) {
clockevents_update_freq(&ce->clkevt, ndata->new_rate);
ce->timer.ticks_per_jiffy = DIV_ROUND_UP(ndata->new_rate, HZ);
}
return NOTIFY_DONE;
}
static int __init sun5i_setup_clockevent(struct device_node *node, void __iomem *base,
struct clk *clk, int irq)
{
struct sun5i_timer_clkevt *ce;
unsigned long rate;
int ret;
u32 val;
ce = kzalloc(sizeof(*ce), GFP_KERNEL);
if (!ce)
return -ENOMEM;
ret = clk_prepare_enable(clk);
if (ret) {
pr_err("Couldn't enable parent clock\n");
goto err_free;
}
rate = clk_get_rate(clk);
if (!rate) {
pr_err("Couldn't get parent clock rate\n");
ret = -EINVAL;
goto err_disable_clk;
}
ce->timer.base = base;
ce->timer.ticks_per_jiffy = DIV_ROUND_UP(rate, HZ);
ce->timer.clk = clk;
ce->timer.clk_rate_cb.notifier_call = sun5i_rate_cb_clkevt;
ce->timer.clk_rate_cb.next = NULL;
ret = clk_notifier_register(clk, &ce->timer.clk_rate_cb);
if (ret) {
pr_err("Unable to register clock notifier.\n");
goto err_disable_clk;
}
ce->clkevt.name = node->name;
ce->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
ce->clkevt.set_next_event = sun5i_clkevt_next_event;
ce->clkevt.set_state_shutdown = sun5i_clkevt_shutdown;
ce->clkevt.set_state_periodic = sun5i_clkevt_set_periodic;
ce->clkevt.set_state_oneshot = sun5i_clkevt_set_oneshot;
ce->clkevt.tick_resume = sun5i_clkevt_shutdown;
ce->clkevt.rating = 340;
ce->clkevt.irq = irq;
ce->clkevt.cpumask = cpu_possible_mask;
/* Enable timer0 interrupt */
val = readl(base + TIMER_IRQ_EN_REG);
writel(val | TIMER_IRQ_EN(0), base + TIMER_IRQ_EN_REG);
clockevents_config_and_register(&ce->clkevt, rate,
TIMER_SYNC_TICKS, 0xffffffff);
ret = request_irq(irq, sun5i_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL,
"sun5i_timer0", ce);
if (ret) {
pr_err("Unable to register interrupt\n");
goto err_remove_notifier;
}
return 0;
err_remove_notifier:
clk_notifier_unregister(clk, &ce->timer.clk_rate_cb);
err_disable_clk:
clk_disable_unprepare(clk);
err_free:
kfree(ce);
return ret;
}
static int __init sun5i_timer_init(struct device_node *node)
{
struct reset_control *rstc;
void __iomem *timer_base;
struct clk *clk;
int irq, ret;
timer_base = of_io_request_and_map(node, 0, of_node_full_name(node));
if (IS_ERR(timer_base)) {
pr_err("Can't map registers\n");
return PTR_ERR(timer_base);
}
irq = irq_of_parse_and_map(node, 0);
if (irq <= 0) {
pr_err("Can't parse IRQ\n");
return -EINVAL;
}
clk = of_clk_get(node, 0);
if (IS_ERR(clk)) {
pr_err("Can't get timer clock\n");
return PTR_ERR(clk);
}
rstc = of_reset_control_get(node, NULL);
if (!IS_ERR(rstc))
reset_control_deassert(rstc);
ret = sun5i_setup_clocksource(node, timer_base, clk, irq);
if (ret)
return ret;
return sun5i_setup_clockevent(node, timer_base, clk, irq);
}
TIMER_OF_DECLARE(sun5i_a13, "allwinner,sun5i-a13-hstimer",
sun5i_timer_init);
TIMER_OF_DECLARE(sun7i_a20, "allwinner,sun7i-a20-hstimer",
sun5i_timer_init);
+130
View File
@@ -0,0 +1,130 @@
/**
* timer-ti-32k.c - OMAP2 32k Timer Support
*
* Copyright (C) 2009 Nokia Corporation
*
* Update to use new clocksource/clockevent layers
* Author: Kevin Hilman, MontaVista Software, Inc. <source@mvista.com>
* Copyright (C) 2007 MontaVista Software, Inc.
*
* Original driver:
* Copyright (C) 2005 Nokia Corporation
* Author: Paul Mundt <paul.mundt@nokia.com>
* Juha Yrjölä <juha.yrjola@nokia.com>
* OMAP Dual-mode timer framework support by Timo Teras
*
* Some parts based off of TI's 24xx code:
*
* Copyright (C) 2004-2009 Texas Instruments, Inc.
*
* Roughly modelled after the OMAP1 MPU timer code.
* Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com>
*
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 of
* the License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/init.h>
#include <linux/time.h>
#include <linux/sched_clock.h>
#include <linux/clocksource.h>
#include <linux/of.h>
#include <linux/of_address.h>
/*
* 32KHz clocksource ... always available, on pretty most chips except
* OMAP 730 and 1510. Other timers could be used as clocksources, with
* higher resolution in free-running counter modes (e.g. 12 MHz xtal),
* but systems won't necessarily want to spend resources that way.
*/
#define OMAP2_32KSYNCNT_REV_OFF 0x0
#define OMAP2_32KSYNCNT_REV_SCHEME (0x3 << 30)
#define OMAP2_32KSYNCNT_CR_OFF_LOW 0x10
#define OMAP2_32KSYNCNT_CR_OFF_HIGH 0x30
struct ti_32k {
void __iomem *base;
void __iomem *counter;
struct clocksource cs;
};
static inline struct ti_32k *to_ti_32k(struct clocksource *cs)
{
return container_of(cs, struct ti_32k, cs);
}
static u64 notrace ti_32k_read_cycles(struct clocksource *cs)
{
struct ti_32k *ti = to_ti_32k(cs);
return (u64)readl_relaxed(ti->counter);
}
static struct ti_32k ti_32k_timer = {
.cs = {
.name = "32k_counter",
.rating = 250,
.read = ti_32k_read_cycles,
.mask = CLOCKSOURCE_MASK(32),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
},
};
static u64 notrace omap_32k_read_sched_clock(void)
{
return ti_32k_read_cycles(&ti_32k_timer.cs);
}
static int __init ti_32k_timer_init(struct device_node *np)
{
int ret;
ti_32k_timer.base = of_iomap(np, 0);
if (!ti_32k_timer.base) {
pr_err("Can't ioremap 32k timer base\n");
return -ENXIO;
}
if (!of_machine_is_compatible("ti,am43"))
ti_32k_timer.cs.flags |= CLOCK_SOURCE_SUSPEND_NONSTOP;
ti_32k_timer.counter = ti_32k_timer.base;
/*
* 32k sync Counter IP register offsets vary between the highlander
* version and the legacy ones.
*
* The 'SCHEME' bits(30-31) of the revision register is used to identify
* the version.
*/
if (readl_relaxed(ti_32k_timer.base + OMAP2_32KSYNCNT_REV_OFF) &
OMAP2_32KSYNCNT_REV_SCHEME)
ti_32k_timer.counter += OMAP2_32KSYNCNT_CR_OFF_HIGH;
else
ti_32k_timer.counter += OMAP2_32KSYNCNT_CR_OFF_LOW;
ret = clocksource_register_hz(&ti_32k_timer.cs, 32768);
if (ret) {
pr_err("32k_counter: can't register clocksource\n");
return ret;
}
sched_clock_register(omap_32k_read_sched_clock, 32, 32768);
pr_info("OMAP clocksource: 32k_counter at 32768 Hz\n");
return 0;
}
TIMER_OF_DECLARE(ti_32k_timer, "ti,omap-counter32k",
ti_32k_timer_init);
File diff suppressed because it is too large. Load diff
+462
View File
@@ -0,0 +1,462 @@
/*
* Copyright (C) 2007-2009 ST-Ericsson AB
* License terms: GNU General Public License (GPL) version 2
* Timer COH 901 328, runs the OS timer interrupt.
* Author: Linus Walleij <linus.walleij@stericsson.com>
*/
#include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/timex.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/types.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/sched_clock.h>
/* Generic stuff */
#include <asm/mach/map.h>
#include <asm/mach/time.h>
/*
* APP side special timer registers
* This timer contains four timers which can fire an interrupt each.
* OS (operating system) timer @ 32768 Hz
* DD (device driver) timer @ 1 kHz
* GP1 (general purpose 1) timer @ 1MHz
* GP2 (general purpose 2) timer @ 1MHz
*/
/* Reset OS Timer 32bit (-/W) */
#define U300_TIMER_APP_ROST (0x0000)
#define U300_TIMER_APP_ROST_TIMER_RESET (0x00000000)
/* Enable OS Timer 32bit (-/W) */
#define U300_TIMER_APP_EOST (0x0004)
#define U300_TIMER_APP_EOST_TIMER_ENABLE (0x00000000)
/* Disable OS Timer 32bit (-/W) */
#define U300_TIMER_APP_DOST (0x0008)
#define U300_TIMER_APP_DOST_TIMER_DISABLE (0x00000000)
/* OS Timer Mode Register 32bit (-/W) */
#define U300_TIMER_APP_SOSTM (0x000c)
#define U300_TIMER_APP_SOSTM_MODE_CONTINUOUS (0x00000000)
#define U300_TIMER_APP_SOSTM_MODE_ONE_SHOT (0x00000001)
/* OS Timer Status Register 32bit (R/-) */
#define U300_TIMER_APP_OSTS (0x0010)
#define U300_TIMER_APP_OSTS_TIMER_STATE_MASK (0x0000000F)
#define U300_TIMER_APP_OSTS_TIMER_STATE_IDLE (0x00000001)
#define U300_TIMER_APP_OSTS_TIMER_STATE_ACTIVE (0x00000002)
#define U300_TIMER_APP_OSTS_ENABLE_IND (0x00000010)
#define U300_TIMER_APP_OSTS_MODE_MASK (0x00000020)
#define U300_TIMER_APP_OSTS_MODE_CONTINUOUS (0x00000000)
#define U300_TIMER_APP_OSTS_MODE_ONE_SHOT (0x00000020)
#define U300_TIMER_APP_OSTS_IRQ_ENABLED_IND (0x00000040)
#define U300_TIMER_APP_OSTS_IRQ_PENDING_IND (0x00000080)
/* OS Timer Current Count Register 32bit (R/-) */
#define U300_TIMER_APP_OSTCC (0x0014)
/* OS Timer Terminal Count Register 32bit (R/W) */
#define U300_TIMER_APP_OSTTC (0x0018)
/* OS Timer Interrupt Enable Register 32bit (-/W) */
#define U300_TIMER_APP_OSTIE (0x001c)
#define U300_TIMER_APP_OSTIE_IRQ_DISABLE (0x00000000)
#define U300_TIMER_APP_OSTIE_IRQ_ENABLE (0x00000001)
/* OS Timer Interrupt Acknowledge Register 32bit (-/W) */
#define U300_TIMER_APP_OSTIA (0x0020)
#define U300_TIMER_APP_OSTIA_IRQ_ACK (0x00000080)
/* Reset DD Timer 32bit (-/W) */
#define U300_TIMER_APP_RDDT (0x0040)
#define U300_TIMER_APP_RDDT_TIMER_RESET (0x00000000)
/* Enable DD Timer 32bit (-/W) */
#define U300_TIMER_APP_EDDT (0x0044)
#define U300_TIMER_APP_EDDT_TIMER_ENABLE (0x00000000)
/* Disable DD Timer 32bit (-/W) */
#define U300_TIMER_APP_DDDT (0x0048)
#define U300_TIMER_APP_DDDT_TIMER_DISABLE (0x00000000)
/* DD Timer Mode Register 32bit (-/W) */
#define U300_TIMER_APP_SDDTM (0x004c)
#define U300_TIMER_APP_SDDTM_MODE_CONTINUOUS (0x00000000)
#define U300_TIMER_APP_SDDTM_MODE_ONE_SHOT (0x00000001)
/* DD Timer Status Register 32bit (R/-) */
#define U300_TIMER_APP_DDTS (0x0050)
#define U300_TIMER_APP_DDTS_TIMER_STATE_MASK (0x0000000F)
#define U300_TIMER_APP_DDTS_TIMER_STATE_IDLE (0x00000001)
#define U300_TIMER_APP_DDTS_TIMER_STATE_ACTIVE (0x00000002)
#define U300_TIMER_APP_DDTS_ENABLE_IND (0x00000010)
#define U300_TIMER_APP_DDTS_MODE_MASK (0x00000020)
#define U300_TIMER_APP_DDTS_MODE_CONTINUOUS (0x00000000)
#define U300_TIMER_APP_DDTS_MODE_ONE_SHOT (0x00000020)
#define U300_TIMER_APP_DDTS_IRQ_ENABLED_IND (0x00000040)
#define U300_TIMER_APP_DDTS_IRQ_PENDING_IND (0x00000080)
/* DD Timer Current Count Register 32bit (R/-) */
#define U300_TIMER_APP_DDTCC (0x0054)
/* DD Timer Terminal Count Register 32bit (R/W) */
#define U300_TIMER_APP_DDTTC (0x0058)
/* DD Timer Interrupt Enable Register 32bit (-/W) */
#define U300_TIMER_APP_DDTIE (0x005c)
#define U300_TIMER_APP_DDTIE_IRQ_DISABLE (0x00000000)
#define U300_TIMER_APP_DDTIE_IRQ_ENABLE (0x00000001)
/* DD Timer Interrupt Acknowledge Register 32bit (-/W) */
#define U300_TIMER_APP_DDTIA (0x0060)
#define U300_TIMER_APP_DDTIA_IRQ_ACK (0x00000080)
/* Reset GP1 Timer 32bit (-/W) */
#define U300_TIMER_APP_RGPT1 (0x0080)
#define U300_TIMER_APP_RGPT1_TIMER_RESET (0x00000000)
/* Enable GP1 Timer 32bit (-/W) */
#define U300_TIMER_APP_EGPT1 (0x0084)
#define U300_TIMER_APP_EGPT1_TIMER_ENABLE (0x00000000)
/* Disable GP1 Timer 32bit (-/W) */
#define U300_TIMER_APP_DGPT1 (0x0088)
#define U300_TIMER_APP_DGPT1_TIMER_DISABLE (0x00000000)
/* GP1 Timer Mode Register 32bit (-/W) */
#define U300_TIMER_APP_SGPT1M (0x008c)
#define U300_TIMER_APP_SGPT1M_MODE_CONTINUOUS (0x00000000)
#define U300_TIMER_APP_SGPT1M_MODE_ONE_SHOT (0x00000001)
/* GP1 Timer Status Register 32bit (R/-) */
#define U300_TIMER_APP_GPT1S (0x0090)
#define U300_TIMER_APP_GPT1S_TIMER_STATE_MASK (0x0000000F)
#define U300_TIMER_APP_GPT1S_TIMER_STATE_IDLE (0x00000001)
#define U300_TIMER_APP_GPT1S_TIMER_STATE_ACTIVE (0x00000002)
#define U300_TIMER_APP_GPT1S_ENABLE_IND (0x00000010)
#define U300_TIMER_APP_GPT1S_MODE_MASK (0x00000020)
#define U300_TIMER_APP_GPT1S_MODE_CONTINUOUS (0x00000000)
#define U300_TIMER_APP_GPT1S_MODE_ONE_SHOT (0x00000020)
#define U300_TIMER_APP_GPT1S_IRQ_ENABLED_IND (0x00000040)
#define U300_TIMER_APP_GPT1S_IRQ_PENDING_IND (0x00000080)
/* GP1 Timer Current Count Register 32bit (R/-) */
#define U300_TIMER_APP_GPT1CC (0x0094)
/* GP1 Timer Terminal Count Register 32bit (R/W) */
#define U300_TIMER_APP_GPT1TC (0x0098)
/* GP1 Timer Interrupt Enable Register 32bit (-/W) */
#define U300_TIMER_APP_GPT1IE (0x009c)
#define U300_TIMER_APP_GPT1IE_IRQ_DISABLE (0x00000000)
#define U300_TIMER_APP_GPT1IE_IRQ_ENABLE (0x00000001)
/* GP1 Timer Interrupt Acknowledge Register 32bit (-/W) */
#define U300_TIMER_APP_GPT1IA (0x00a0)
#define U300_TIMER_APP_GPT1IA_IRQ_ACK (0x00000080)
/* Reset GP2 Timer 32bit (-/W) */
#define U300_TIMER_APP_RGPT2 (0x00c0)
#define U300_TIMER_APP_RGPT2_TIMER_RESET (0x00000000)
/* Enable GP2 Timer 32bit (-/W) */
#define U300_TIMER_APP_EGPT2 (0x00c4)
#define U300_TIMER_APP_EGPT2_TIMER_ENABLE (0x00000000)
/* Disable GP2 Timer 32bit (-/W) */
#define U300_TIMER_APP_DGPT2 (0x00c8)
#define U300_TIMER_APP_DGPT2_TIMER_DISABLE (0x00000000)
/* GP2 Timer Mode Register 32bit (-/W) */
#define U300_TIMER_APP_SGPT2M (0x00cc)
#define U300_TIMER_APP_SGPT2M_MODE_CONTINUOUS (0x00000000)
#define U300_TIMER_APP_SGPT2M_MODE_ONE_SHOT (0x00000001)
/* GP2 Timer Status Register 32bit (R/-) */
#define U300_TIMER_APP_GPT2S (0x00d0)
#define U300_TIMER_APP_GPT2S_TIMER_STATE_MASK (0x0000000F)
#define U300_TIMER_APP_GPT2S_TIMER_STATE_IDLE (0x00000001)
#define U300_TIMER_APP_GPT2S_TIMER_STATE_ACTIVE (0x00000002)
#define U300_TIMER_APP_GPT2S_ENABLE_IND (0x00000010)
#define U300_TIMER_APP_GPT2S_MODE_MASK (0x00000020)
#define U300_TIMER_APP_GPT2S_MODE_CONTINUOUS (0x00000000)
#define U300_TIMER_APP_GPT2S_MODE_ONE_SHOT (0x00000020)
#define U300_TIMER_APP_GPT2S_IRQ_ENABLED_IND (0x00000040)
#define U300_TIMER_APP_GPT2S_IRQ_PENDING_IND (0x00000080)
/* GP2 Timer Current Count Register 32bit (R/-) */
#define U300_TIMER_APP_GPT2CC (0x00d4)
/* GP2 Timer Terminal Count Register 32bit (R/W) */
#define U300_TIMER_APP_GPT2TC (0x00d8)
/* GP2 Timer Interrupt Enable Register 32bit (-/W) */
#define U300_TIMER_APP_GPT2IE (0x00dc)
#define U300_TIMER_APP_GPT2IE_IRQ_DISABLE (0x00000000)
#define U300_TIMER_APP_GPT2IE_IRQ_ENABLE (0x00000001)
/* GP2 Timer Interrupt Acknowledge Register 32bit (-/W) */
#define U300_TIMER_APP_GPT2IA (0x00e0)
#define U300_TIMER_APP_GPT2IA_IRQ_ACK (0x00000080)
/* Clock request control register - all four timers */
#define U300_TIMER_APP_CRC (0x100)
#define U300_TIMER_APP_CRC_CLOCK_REQUEST_ENABLE (0x00000001)
static void __iomem *u300_timer_base;
struct u300_clockevent_data {
struct clock_event_device cevd;
unsigned ticks_per_jiffy;
};
static int u300_shutdown(struct clock_event_device *evt)
{
/* Disable interrupts on GP1 */
writel(U300_TIMER_APP_GPT1IE_IRQ_DISABLE,
u300_timer_base + U300_TIMER_APP_GPT1IE);
/* Disable GP1 */
writel(U300_TIMER_APP_DGPT1_TIMER_DISABLE,
u300_timer_base + U300_TIMER_APP_DGPT1);
return 0;
}
/*
* If we have oneshot timer active, the oneshot scheduling function
* u300_set_next_event() is called immediately after.
*/
static int u300_set_oneshot(struct clock_event_device *evt)
{
/* Just return; here? */
/*
* The actual event will be programmed by the next event hook,
* so we just set a dummy value somewhere at the end of the
* universe here.
*/
/* Disable interrupts on GPT1 */
writel(U300_TIMER_APP_GPT1IE_IRQ_DISABLE,
u300_timer_base + U300_TIMER_APP_GPT1IE);
/* Disable GP1 while we're reprogramming it. */
writel(U300_TIMER_APP_DGPT1_TIMER_DISABLE,
u300_timer_base + U300_TIMER_APP_DGPT1);
/*
* Expire far in the future, u300_set_next_event() will be
* called soon...
*/
writel(0xFFFFFFFF, u300_timer_base + U300_TIMER_APP_GPT1TC);
/* We run one shot per tick here! */
writel(U300_TIMER_APP_SGPT1M_MODE_ONE_SHOT,
u300_timer_base + U300_TIMER_APP_SGPT1M);
/* Enable interrupts for this timer */
writel(U300_TIMER_APP_GPT1IE_IRQ_ENABLE,
u300_timer_base + U300_TIMER_APP_GPT1IE);
/* Enable timer */
writel(U300_TIMER_APP_EGPT1_TIMER_ENABLE,
u300_timer_base + U300_TIMER_APP_EGPT1);
return 0;
}
static int u300_set_periodic(struct clock_event_device *evt)
{
struct u300_clockevent_data *cevdata =
container_of(evt, struct u300_clockevent_data, cevd);
/* Disable interrupts on GPT1 */
writel(U300_TIMER_APP_GPT1IE_IRQ_DISABLE,
u300_timer_base + U300_TIMER_APP_GPT1IE);
/* Disable GP1 while we're reprogramming it. */
writel(U300_TIMER_APP_DGPT1_TIMER_DISABLE,
u300_timer_base + U300_TIMER_APP_DGPT1);
/*
* Set the periodic mode to a certain number of ticks per
* jiffy.
*/
writel(cevdata->ticks_per_jiffy,
u300_timer_base + U300_TIMER_APP_GPT1TC);
/*
* Set continuous mode, so the timer keeps triggering
* interrupts.
*/
writel(U300_TIMER_APP_SGPT1M_MODE_CONTINUOUS,
u300_timer_base + U300_TIMER_APP_SGPT1M);
/* Enable timer interrupts */
writel(U300_TIMER_APP_GPT1IE_IRQ_ENABLE,
u300_timer_base + U300_TIMER_APP_GPT1IE);
/* Then enable the OS timer again */
writel(U300_TIMER_APP_EGPT1_TIMER_ENABLE,
u300_timer_base + U300_TIMER_APP_EGPT1);
return 0;
}
/*
* The app timer in one shot mode obviously has to be reprogrammed
* in EXACTLY this sequence to work properly. Do NOT try to e.g. replace
* the interrupt disable + timer disable commands with a reset command,
* it will fail miserably. Apparently (and I found this the hard way)
* the timer is very sensitive to the instruction order, though you don't
* get that impression from the data sheet.
*/
static int u300_set_next_event(unsigned long cycles,
struct clock_event_device *evt)
{
/* Disable interrupts on GPT1 */
writel(U300_TIMER_APP_GPT1IE_IRQ_DISABLE,
u300_timer_base + U300_TIMER_APP_GPT1IE);
/* Disable GP1 while we're reprogramming it. */
writel(U300_TIMER_APP_DGPT1_TIMER_DISABLE,
u300_timer_base + U300_TIMER_APP_DGPT1);
/* Reset the General Purpose timer 1. */
writel(U300_TIMER_APP_RGPT1_TIMER_RESET,
u300_timer_base + U300_TIMER_APP_RGPT1);
/* IRQ in n * cycles */
writel(cycles, u300_timer_base + U300_TIMER_APP_GPT1TC);
/*
* We run one shot per tick here! (This is necessary to reconfigure,
* the timer will tilt if you don't!)
*/
writel(U300_TIMER_APP_SGPT1M_MODE_ONE_SHOT,
u300_timer_base + U300_TIMER_APP_SGPT1M);
/* Enable timer interrupts */
writel(U300_TIMER_APP_GPT1IE_IRQ_ENABLE,
u300_timer_base + U300_TIMER_APP_GPT1IE);
/* Then enable the OS timer again */
writel(U300_TIMER_APP_EGPT1_TIMER_ENABLE,
u300_timer_base + U300_TIMER_APP_EGPT1);
return 0;
}
static struct u300_clockevent_data u300_clockevent_data = {
/* Use general purpose timer 1 as clock event */
.cevd = {
.name = "GPT1",
/* Reasonably fast and accurate clock event */
.rating = 300,
.features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT,
.set_next_event = u300_set_next_event,
.set_state_shutdown = u300_shutdown,
.set_state_periodic = u300_set_periodic,
.set_state_oneshot = u300_set_oneshot,
},
};
/* Clock event timer interrupt handler */
static irqreturn_t u300_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = &u300_clockevent_data.cevd;
/* ACK/Clear timer IRQ for the APP GPT1 Timer */
writel(U300_TIMER_APP_GPT1IA_IRQ_ACK,
u300_timer_base + U300_TIMER_APP_GPT1IA);
evt->event_handler(evt);
return IRQ_HANDLED;
}
static struct irqaction u300_timer_irq = {
.name = "U300 Timer Tick",
.flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = u300_timer_interrupt,
};
/*
* Override the global weak sched_clock symbol with this
* local implementation which uses the clocksource to get some
* better resolution when scheduling the kernel. We accept that
* this wraps around for now, since it is just a relative time
* stamp. (Inspired by OMAP implementation.)
*/
static u64 notrace u300_read_sched_clock(void)
{
return readl(u300_timer_base + U300_TIMER_APP_GPT2CC);
}
static unsigned long u300_read_current_timer(void)
{
return readl(u300_timer_base + U300_TIMER_APP_GPT2CC);
}
static struct delay_timer u300_delay_timer;
/*
* This sets up the system timers, clock source and clock event.
*/
static int __init u300_timer_init_of(struct device_node *np)
{
unsigned int irq;
struct clk *clk;
unsigned long rate;
int ret;
u300_timer_base = of_iomap(np, 0);
if (!u300_timer_base) {
pr_err("could not ioremap system timer\n");
return -ENXIO;
}
/* Get the IRQ for the GP1 timer */
irq = irq_of_parse_and_map(np, 2);
if (!irq) {
pr_err("no IRQ for system timer\n");
return -EINVAL;
}
pr_info("U300 GP1 timer @ base: %p, IRQ: %u\n", u300_timer_base, irq);
/* Clock the interrupt controller */
clk = of_clk_get(np, 0);
if (IS_ERR(clk))
return PTR_ERR(clk);
ret = clk_prepare_enable(clk);
if (ret)
return ret;
rate = clk_get_rate(clk);
u300_clockevent_data.ticks_per_jiffy = DIV_ROUND_CLOSEST(rate, HZ);
sched_clock_register(u300_read_sched_clock, 32, rate);
u300_delay_timer.read_current_timer = &u300_read_current_timer;
u300_delay_timer.freq = rate;
register_current_timer_delay(&u300_delay_timer);
/*
* Disable the "OS" and "DD" timers - these are designed for Symbian!
* Example usage in cnh1601578 cpu subsystem pd_timer_app.c
*/
writel(U300_TIMER_APP_CRC_CLOCK_REQUEST_ENABLE,
u300_timer_base + U300_TIMER_APP_CRC);
writel(U300_TIMER_APP_ROST_TIMER_RESET,
u300_timer_base + U300_TIMER_APP_ROST);
writel(U300_TIMER_APP_DOST_TIMER_DISABLE,
u300_timer_base + U300_TIMER_APP_DOST);
writel(U300_TIMER_APP_RDDT_TIMER_RESET,
u300_timer_base + U300_TIMER_APP_RDDT);
writel(U300_TIMER_APP_DDDT_TIMER_DISABLE,
u300_timer_base + U300_TIMER_APP_DDDT);
/* Reset the General Purpose timer 1. */
writel(U300_TIMER_APP_RGPT1_TIMER_RESET,
u300_timer_base + U300_TIMER_APP_RGPT1);
/* Set up the IRQ handler */
ret = setup_irq(irq, &u300_timer_irq);
if (ret)
return ret;
/* Reset the General Purpose timer 2 */
writel(U300_TIMER_APP_RGPT2_TIMER_RESET,
u300_timer_base + U300_TIMER_APP_RGPT2);
/* Set this timer to run around forever */
writel(0xFFFFFFFFU, u300_timer_base + U300_TIMER_APP_GPT2TC);
/* Set continuous mode so it wraps around */
writel(U300_TIMER_APP_SGPT2M_MODE_CONTINUOUS,
u300_timer_base + U300_TIMER_APP_SGPT2M);
/* Disable timer interrupts */
writel(U300_TIMER_APP_GPT2IE_IRQ_DISABLE,
u300_timer_base + U300_TIMER_APP_GPT2IE);
/* Then enable the GP2 timer to use as a free running us counter */
writel(U300_TIMER_APP_EGPT2_TIMER_ENABLE,
u300_timer_base + U300_TIMER_APP_EGPT2);
/* Use general purpose timer 2 as clock source */
ret = clocksource_mmio_init(u300_timer_base + U300_TIMER_APP_GPT2CC,
"GPT2", rate, 300, 32, clocksource_mmio_readl_up);
if (ret) {
pr_err("timer: failed to initialize U300 clock source\n");
return ret;
}
/* Configure and register the clockevent */
clockevents_config_and_register(&u300_clockevent_data.cevd, rate,
1, 0xffffffff);
/*
* TODO: init and register the rest of the timers too, they can be
* used by hrtimers!
*/
return 0;
}
TIMER_OF_DECLARE(u300_timer, "stericsson,u300-apptimer",
u300_timer_init_of);
+44
View File
@@ -0,0 +1,44 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Copyright (C) 2014 ARM Limited
*/
#include <linux/clocksource.h>
#include <linux/io.h>
#include <linux/of_address.h>
#include <linux/sched_clock.h>
#define SYS_24MHZ 0x05c
static void __iomem *versatile_sys_24mhz;
static u64 notrace versatile_sys_24mhz_read(void)
{
return readl(versatile_sys_24mhz);
}
static int __init versatile_sched_clock_init(struct device_node *node)
{
void __iomem *base = of_iomap(node, 0);
if (!base)
return -ENXIO;
versatile_sys_24mhz = base + SYS_24MHZ;
sched_clock_register(versatile_sys_24mhz_read, 32, 24000000);
return 0;
}
TIMER_OF_DECLARE(vexpress, "arm,vexpress-sysreg",
versatile_sched_clock_init);
TIMER_OF_DECLARE(versatile, "arm,versatile-sysreg",
versatile_sched_clock_init);
+204
View File
@@ -0,0 +1,204 @@
/*
* Copyright 2012-2013 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*/
#include <linux/interrupt.h>
#include <linux/clockchips.h>
#include <linux/clk.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/sched_clock.h>
/*
* Each pit takes 0x10 Bytes register space
*/
#define PITMCR 0x00
#define PIT0_OFFSET 0x100
#define PITn_OFFSET(n) (PIT0_OFFSET + 0x10 * (n))
#define PITLDVAL 0x00
#define PITCVAL 0x04
#define PITTCTRL 0x08
#define PITTFLG 0x0c
#define PITMCR_MDIS (0x1 << 1)
#define PITTCTRL_TEN (0x1 << 0)
#define PITTCTRL_TIE (0x1 << 1)
#define PITCTRL_CHN (0x1 << 2)
#define PITTFLG_TIF 0x1
static void __iomem *clksrc_base;
static void __iomem *clkevt_base;
static unsigned long cycle_per_jiffy;
static inline void pit_timer_enable(void)
{
__raw_writel(PITTCTRL_TEN | PITTCTRL_TIE, clkevt_base + PITTCTRL);
}
static inline void pit_timer_disable(void)
{
__raw_writel(0, clkevt_base + PITTCTRL);
}
static inline void pit_irq_acknowledge(void)
{
__raw_writel(PITTFLG_TIF, clkevt_base + PITTFLG);
}
static u64 notrace pit_read_sched_clock(void)
{
return ~__raw_readl(clksrc_base + PITCVAL);
}
static int __init pit_clocksource_init(unsigned long rate)
{
/* set the max load value and start the clock source counter */
__raw_writel(0, clksrc_base + PITTCTRL);
__raw_writel(~0UL, clksrc_base + PITLDVAL);
__raw_writel(PITTCTRL_TEN, clksrc_base + PITTCTRL);
sched_clock_register(pit_read_sched_clock, 32, rate);
return clocksource_mmio_init(clksrc_base + PITCVAL, "vf-pit", rate,
300, 32, clocksource_mmio_readl_down);
}
static int pit_set_next_event(unsigned long delta,
struct clock_event_device *unused)
{
/*
* set a new value to PITLDVAL register will not restart the timer,
* to abort the current cycle and start a timer period with the new
* value, the timer must be disabled and enabled again.
* and the PITLAVAL should be set to delta minus one according to pit
* hardware requirement.
*/
pit_timer_disable();
__raw_writel(delta - 1, clkevt_base + PITLDVAL);
pit_timer_enable();
return 0;
}
static int pit_shutdown(struct clock_event_device *evt)
{
pit_timer_disable();
return 0;
}
static int pit_set_periodic(struct clock_event_device *evt)
{
pit_set_next_event(cycle_per_jiffy, evt);
return 0;
}
static irqreturn_t pit_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
pit_irq_acknowledge();
/*
* pit hardware doesn't support oneshot, it will generate an interrupt
* and reload the counter value from PITLDVAL when PITCVAL reach zero,
* and start the counter again. So software need to disable the timer
* to stop the counter loop in ONESHOT mode.
*/
if (likely(clockevent_state_oneshot(evt)))
pit_timer_disable();
evt->event_handler(evt);
return IRQ_HANDLED;
}
static struct clock_event_device clockevent_pit = {
.name = "VF pit timer",
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
.set_state_shutdown = pit_shutdown,
.set_state_periodic = pit_set_periodic,
.set_next_event = pit_set_next_event,
.rating = 300,
};
static struct irqaction pit_timer_irq = {
.name = "VF pit timer",
.flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = pit_timer_interrupt,
.dev_id = &clockevent_pit,
};
static int __init pit_clockevent_init(unsigned long rate, int irq)
{
__raw_writel(0, clkevt_base + PITTCTRL);
__raw_writel(PITTFLG_TIF, clkevt_base + PITTFLG);
BUG_ON(setup_irq(irq, &pit_timer_irq));
clockevent_pit.cpumask = cpumask_of(0);
clockevent_pit.irq = irq;
/*
* The value for the LDVAL register trigger is calculated as:
* LDVAL trigger = (period / clock period) - 1
* The pit is a 32-bit down count timer, when the conter value
* reaches 0, it will generate an interrupt, thus the minimal
* LDVAL trigger value is 1. And then the min_delta is
* minimal LDVAL trigger value + 1, and the max_delta is full 32-bit.
*/
clockevents_config_and_register(&clockevent_pit, rate, 2, 0xffffffff);
return 0;
}
static int __init pit_timer_init(struct device_node *np)
{
struct clk *pit_clk;
void __iomem *timer_base;
unsigned long clk_rate;
int irq, ret;
timer_base = of_iomap(np, 0);
if (!timer_base) {
pr_err("Failed to iomap\n");
return -ENXIO;
}
/*
* PIT0 and PIT1 can be chained to build a 64-bit timer,
* so choose PIT2 as clocksource, PIT3 as clockevent device,
* and leave PIT0 and PIT1 unused for anyone else who needs them.
*/
clksrc_base = timer_base + PITn_OFFSET(2);
clkevt_base = timer_base + PITn_OFFSET(3);
irq = irq_of_parse_and_map(np, 0);
if (irq <= 0)
return -EINVAL;
pit_clk = of_clk_get(np, 0);
if (IS_ERR(pit_clk))
return PTR_ERR(pit_clk);
ret = clk_prepare_enable(pit_clk);
if (ret)
return ret;
clk_rate = clk_get_rate(pit_clk);
cycle_per_jiffy = clk_rate / (HZ);
/* enable the pit module */
__raw_writel(~PITMCR_MDIS, timer_base + PITMCR);
ret = pit_clocksource_init(clk_rate);
if (ret)
return ret;
return pit_clockevent_init(clk_rate, irq);
}
TIMER_OF_DECLARE(vf610, "fsl,vf610-pit", pit_timer_init);
+168
View File
@@ -0,0 +1,168 @@
/*
* arch/arm/mach-vt8500/timer.c
*
* Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
* Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* This file is copied and modified from the original timer.c provided by
* Alexey Charkov. Minor changes have been made for Device Tree Support.
*/
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#define VT8500_TIMER_OFFSET 0x0100
#define VT8500_TIMER_HZ 3000000
#define TIMER_MATCH_VAL 0x0000
#define TIMER_COUNT_VAL 0x0010
#define TIMER_STATUS_VAL 0x0014
#define TIMER_IER_VAL 0x001c /* interrupt enable */
#define TIMER_CTRL_VAL 0x0020
#define TIMER_AS_VAL 0x0024 /* access status */
#define TIMER_COUNT_R_ACTIVE (1 << 5) /* not ready for read */
#define TIMER_COUNT_W_ACTIVE (1 << 4) /* not ready for write */
#define TIMER_MATCH_W_ACTIVE (1 << 0) /* not ready for write */
#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
#define MIN_OSCR_DELTA 16
static void __iomem *regbase;
static u64 vt8500_timer_read(struct clocksource *cs)
{
int loops = msecs_to_loops(10);
writel(3, regbase + TIMER_CTRL_VAL);
while ((readl((regbase + TIMER_AS_VAL)) & TIMER_COUNT_R_ACTIVE)
&& --loops)
cpu_relax();
return readl(regbase + TIMER_COUNT_VAL);
}
static struct clocksource clocksource = {
.name = "vt8500_timer",
.rating = 200,
.read = vt8500_timer_read,
.mask = CLOCKSOURCE_MASK(32),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static int vt8500_timer_set_next_event(unsigned long cycles,
struct clock_event_device *evt)
{
int loops = msecs_to_loops(10);
u64 alarm = clocksource.read(&clocksource) + cycles;
while ((readl(regbase + TIMER_AS_VAL) & TIMER_MATCH_W_ACTIVE)
&& --loops)
cpu_relax();
writel((unsigned long)alarm, regbase + TIMER_MATCH_VAL);
if ((signed)(alarm - clocksource.read(&clocksource)) <= MIN_OSCR_DELTA)
return -ETIME;
writel(1, regbase + TIMER_IER_VAL);
return 0;
}
static int vt8500_shutdown(struct clock_event_device *evt)
{
writel(readl(regbase + TIMER_CTRL_VAL) | 1, regbase + TIMER_CTRL_VAL);
writel(0, regbase + TIMER_IER_VAL);
return 0;
}
static struct clock_event_device clockevent = {
.name = "vt8500_timer",
.features = CLOCK_EVT_FEAT_ONESHOT,
.rating = 200,
.set_next_event = vt8500_timer_set_next_event,
.set_state_shutdown = vt8500_shutdown,
.set_state_oneshot = vt8500_shutdown,
};
static irqreturn_t vt8500_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
writel(0xf, regbase + TIMER_STATUS_VAL);
evt->event_handler(evt);
return IRQ_HANDLED;
}
static struct irqaction irq = {
.name = "vt8500_timer",
.flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = vt8500_timer_interrupt,
.dev_id = &clockevent,
};
static int __init vt8500_timer_init(struct device_node *np)
{
int timer_irq, ret;
regbase = of_iomap(np, 0);
if (!regbase) {
pr_err("%s: Missing iobase description in Device Tree\n",
__func__);
return -ENXIO;
}
timer_irq = irq_of_parse_and_map(np, 0);
if (!timer_irq) {
pr_err("%s: Missing irq description in Device Tree\n",
__func__);
return -EINVAL;
}
writel(1, regbase + TIMER_CTRL_VAL);
writel(0xf, regbase + TIMER_STATUS_VAL);
writel(~0, regbase + TIMER_MATCH_VAL);
ret = clocksource_register_hz(&clocksource, VT8500_TIMER_HZ);
if (ret) {
pr_err("%s: vt8500_timer_init: clocksource_register failed for %s\n",
__func__, clocksource.name);
return ret;
}
clockevent.cpumask = cpumask_of(0);
ret = setup_irq(timer_irq, &irq);
if (ret) {
pr_err("%s: setup_irq failed for %s\n", __func__,
clockevent.name);
return ret;
}
clockevents_config_and_register(&clockevent, VT8500_TIMER_HZ,
MIN_OSCR_DELTA * 2, 0xf0000000);
return 0;
}
TIMER_OF_DECLARE(vt8500, "via,vt8500-timer", vt8500_timer_init);
+218
View File
@@ -0,0 +1,218 @@
/*
* linux/drivers/clocksource/zevio-timer.c
*
* Copyright (C) 2013 Daniel Tang <tangrs@tangrs.id.au>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
*/
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/cpumask.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#define IO_CURRENT_VAL 0x00
#define IO_DIVIDER 0x04
#define IO_CONTROL 0x08
#define IO_TIMER1 0x00
#define IO_TIMER2 0x0C
#define IO_MATCH_BEGIN 0x18
#define IO_MATCH(x) (IO_MATCH_BEGIN + ((x) << 2))
#define IO_INTR_STS 0x00
#define IO_INTR_ACK 0x00
#define IO_INTR_MSK 0x04
#define CNTL_STOP_TIMER (1 << 4)
#define CNTL_RUN_TIMER (0 << 4)
#define CNTL_INC (1 << 3)
#define CNTL_DEC (0 << 3)
#define CNTL_TOZERO 0
#define CNTL_MATCH(x) ((x) + 1)
#define CNTL_FOREVER 7
/* There are 6 match registers but we only use one. */
#define TIMER_MATCH 0
#define TIMER_INTR_MSK (1 << (TIMER_MATCH))
#define TIMER_INTR_ALL 0x3F
struct zevio_timer {
void __iomem *base;
void __iomem *timer1, *timer2;
void __iomem *interrupt_regs;
struct clk *clk;
struct clock_event_device clkevt;
struct irqaction clkevt_irq;
char clocksource_name[64];
char clockevent_name[64];
};
static int zevio_timer_set_event(unsigned long delta,
struct clock_event_device *dev)
{
struct zevio_timer *timer = container_of(dev, struct zevio_timer,
clkevt);
writel(delta, timer->timer1 + IO_CURRENT_VAL);
writel(CNTL_RUN_TIMER | CNTL_DEC | CNTL_MATCH(TIMER_MATCH),
timer->timer1 + IO_CONTROL);
return 0;
}
static int zevio_timer_shutdown(struct clock_event_device *dev)
{
struct zevio_timer *timer = container_of(dev, struct zevio_timer,
clkevt);
/* Disable timer interrupts */
writel(0, timer->interrupt_regs + IO_INTR_MSK);
writel(TIMER_INTR_ALL, timer->interrupt_regs + IO_INTR_ACK);
/* Stop timer */
writel(CNTL_STOP_TIMER, timer->timer1 + IO_CONTROL);
return 0;
}
static int zevio_timer_set_oneshot(struct clock_event_device *dev)
{
struct zevio_timer *timer = container_of(dev, struct zevio_timer,
clkevt);
/* Enable timer interrupts */
writel(TIMER_INTR_MSK, timer->interrupt_regs + IO_INTR_MSK);
writel(TIMER_INTR_ALL, timer->interrupt_regs + IO_INTR_ACK);
return 0;
}
static irqreturn_t zevio_timer_interrupt(int irq, void *dev_id)
{
struct zevio_timer *timer = dev_id;
u32 intr;
intr = readl(timer->interrupt_regs + IO_INTR_ACK);
if (!(intr & TIMER_INTR_MSK))
return IRQ_NONE;
writel(TIMER_INTR_MSK, timer->interrupt_regs + IO_INTR_ACK);
writel(CNTL_STOP_TIMER, timer->timer1 + IO_CONTROL);
if (timer->clkevt.event_handler)
timer->clkevt.event_handler(&timer->clkevt);
return IRQ_HANDLED;
}
static int __init zevio_timer_add(struct device_node *node)
{
struct zevio_timer *timer;
struct resource res;
int irqnr, ret;
timer = kzalloc(sizeof(*timer), GFP_KERNEL);
if (!timer)
return -ENOMEM;
timer->base = of_iomap(node, 0);
if (!timer->base) {
ret = -EINVAL;
goto error_free;
}
timer->timer1 = timer->base + IO_TIMER1;
timer->timer2 = timer->base + IO_TIMER2;
timer->clk = of_clk_get(node, 0);
if (IS_ERR(timer->clk)) {
ret = PTR_ERR(timer->clk);
pr_err("Timer clock not found! (error %d)\n", ret);
goto error_unmap;
}
timer->interrupt_regs = of_iomap(node, 1);
irqnr = irq_of_parse_and_map(node, 0);
of_address_to_resource(node, 0, &res);
scnprintf(timer->clocksource_name, sizeof(timer->clocksource_name),
"%llx.%s_clocksource",
(unsigned long long)res.start, node->name);
scnprintf(timer->clockevent_name, sizeof(timer->clockevent_name),
"%llx.%s_clockevent",
(unsigned long long)res.start, node->name);
if (timer->interrupt_regs && irqnr) {
timer->clkevt.name = timer->clockevent_name;
timer->clkevt.set_next_event = zevio_timer_set_event;
timer->clkevt.set_state_shutdown = zevio_timer_shutdown;
timer->clkevt.set_state_oneshot = zevio_timer_set_oneshot;
timer->clkevt.tick_resume = zevio_timer_set_oneshot;
timer->clkevt.rating = 200;
timer->clkevt.cpumask = cpu_possible_mask;
timer->clkevt.features = CLOCK_EVT_FEAT_ONESHOT;
timer->clkevt.irq = irqnr;
writel(CNTL_STOP_TIMER, timer->timer1 + IO_CONTROL);
writel(0, timer->timer1 + IO_DIVIDER);
/* Start with timer interrupts disabled */
writel(0, timer->interrupt_regs + IO_INTR_MSK);
writel(TIMER_INTR_ALL, timer->interrupt_regs + IO_INTR_ACK);
/* Interrupt to occur when timer value matches 0 */
writel(0, timer->base + IO_MATCH(TIMER_MATCH));
timer->clkevt_irq.name = timer->clockevent_name;
timer->clkevt_irq.handler = zevio_timer_interrupt;
timer->clkevt_irq.dev_id = timer;
timer->clkevt_irq.flags = IRQF_TIMER | IRQF_IRQPOLL;
setup_irq(irqnr, &timer->clkevt_irq);
clockevents_config_and_register(&timer->clkevt,
clk_get_rate(timer->clk), 0x0001, 0xffff);
pr_info("Added %s as clockevent\n", timer->clockevent_name);
}
writel(CNTL_STOP_TIMER, timer->timer2 + IO_CONTROL);
writel(0, timer->timer2 + IO_CURRENT_VAL);
writel(0, timer->timer2 + IO_DIVIDER);
writel(CNTL_RUN_TIMER | CNTL_FOREVER | CNTL_INC,
timer->timer2 + IO_CONTROL);
clocksource_mmio_init(timer->timer2 + IO_CURRENT_VAL,
timer->clocksource_name,
clk_get_rate(timer->clk),
200, 16,
clocksource_mmio_readw_up);
pr_info("Added %s as clocksource\n", timer->clocksource_name);
return 0;
error_unmap:
iounmap(timer->base);
error_free:
kfree(timer);
return ret;
}
static int __init zevio_timer_init(struct device_node *node)
{
return zevio_timer_add(node);
}
TIMER_OF_DECLARE(zevio_timer, "lsi,zevio-timer", zevio_timer_init);