Dear RT folks! I'm pleased to announce the v5.0.7-rt5 patch set. Changes since v5.0.7-rt4: - Update "x86: load FPU registers on return to userland" from v7 to v9. - Update "clocksource: improve Atmel TCB timer driver" from v7 to latest post by Alexandre Belloni. I hope this works, my HW refuses to cooperate so I can't verify. - Avoid allocating a spin lock with disabled interrupts in i915. Known issues - A warning triggered in "rcu_note_context_switch" originated from SyS_timer_gettime(). The issue was always there, it is now visible. Reported by Grygorii Strashko and Daniel Wagner. - rcutorture is currently broken on -RT. Reported by Juri Lelli. The delta patch against v5.0.7-rt4 is appended below and can be found here: https://cdn.kernel.org/pub/linux/kernel/projects/rt/5.0/incr/patch-5.0.7-rt4-rt5.patch.xz You can get this release via the git tree at: git://git.kernel.org/pub/scm/linux/kernel/git/rt/linux-rt-devel.git v5.0.7-rt5 The RT patch against v5.0.7 can be found here: https://cdn.kernel.org/pub/linux/kernel/projects/rt/5.0/older/patch-5.0.7-rt5.patch.xz The split quilt queue is available at: https://cdn.kernel.org/pub/linux/kernel/projects/rt/5.0/older/patches-5.0.7-rt5.tar.xz Sebastian diff --git a/arch/arm/configs/at91_dt_defconfig b/arch/arm/configs/at91_dt_defconfig index f4b253bd05ede..c8876d0ca41a8 100644 --- a/arch/arm/configs/at91_dt_defconfig +++ b/arch/arm/configs/at91_dt_defconfig @@ -65,6 +65,7 @@ CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_COUNT=4 CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_ATMEL_TCLIB=y CONFIG_ATMEL_SSC=y CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y diff --git a/arch/arm/configs/sama5_defconfig b/arch/arm/configs/sama5_defconfig index c2dc35dfb3215..10ebc9481f72c 100644 --- a/arch/arm/configs/sama5_defconfig +++ b/arch/arm/configs/sama5_defconfig @@ -76,6 +76,7 @@ CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_COUNT=4 CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_ATMEL_TCLIB=y CONFIG_ATMEL_SSC=y CONFIG_EEPROM_AT24=y CONFIG_SCSI=y diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index fa493a86e2bb3..da1d97a06c53a 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig @@ -121,10 +121,8 @@ config ATMEL_CLOCKSOURCE_PIT config ATMEL_CLOCKSOURCE_TCB bool "Timer Counter Blocks (TCB) support" - depends on SOC_AT91RM9200 || SOC_AT91SAM9 || SOC_SAMA5 || COMPILE_TEST default SOC_AT91RM9200 || SOC_AT91SAM9 || SOC_SAMA5 - depends on !ATMEL_TCLIB - select ATMEL_ARM_TCB_CLKSRC + select ATMEL_TCB_CLKSRC help Select this to get a high precision clocksource based on a TC block with a 5+ MHz base clock rate. diff --git a/arch/x86/include/asm/fpu/internal.h b/arch/x86/include/asm/fpu/internal.h index 749ee389a1178..33e2294b5a675 100644 --- a/arch/x86/include/asm/fpu/internal.h +++ b/arch/x86/include/asm/fpu/internal.h @@ -14,6 +14,7 @@ #include <linux/compat.h> #include <linux/sched.h> #include <linux/slab.h> +#include <linux/mm.h> #include <asm/user.h> #include <asm/fpu/api.h> @@ -120,7 +121,7 @@ extern void fpstate_sanitize_xstate(struct fpu *fpu); err; \ }) -#define kernel_insn_norestore(insn, output, input...) \ +#define kernel_insn_err(insn, output, input...) \ ({ \ int err; \ asm volatile("1:" #insn "\n\t" \ @@ -141,6 +142,22 @@ extern void fpstate_sanitize_xstate(struct fpu *fpu); _ASM_EXTABLE_HANDLE(1b, 2b, ex_handler_fprestore) \ : output : input) +static inline int copy_fregs_to_user(struct fregs_state __user *fx) +{ + return user_insn(fnsave %[fx]; fwait, [fx] "=m" (*fx), "m" (*fx)); +} + +static inline int copy_fxregs_to_user(struct fxregs_state __user *fx) +{ + if (IS_ENABLED(CONFIG_X86_32)) + return user_insn(fxsave %[fx], [fx] "=m" (*fx), "m" (*fx)); + else if (IS_ENABLED(CONFIG_AS_FXSAVEQ)) + return user_insn(fxsaveq %[fx], [fx] "=m" (*fx), "m" (*fx)); + + /* See comment in copy_fxregs_to_kernel() below. */ + return user_insn(rex64/fxsave (%[fx]), "=m" (*fx), [fx] "R" (fx)); +} + static inline void copy_kernel_to_fxregs(struct fxregs_state *fx) { if (IS_ENABLED(CONFIG_X86_32)) { @@ -155,15 +172,23 @@ static inline void copy_kernel_to_fxregs(struct fxregs_state *fx) } } -static inline int copy_users_to_fxregs(struct fxregs_state *fx) +static inline int copy_kernel_to_fxregs_err(struct fxregs_state *fx) { if (IS_ENABLED(CONFIG_X86_32)) - return kernel_insn_norestore(fxrstor %[fx], "=m" (*fx), [fx] "m" (*fx)); + return kernel_insn_err(fxrstor %[fx], "=m" (*fx), [fx] "m" (*fx)); + else + return kernel_insn_err(fxrstorq %[fx], "=m" (*fx), [fx] "m" (*fx)); +} + +static inline int copy_user_to_fxregs(struct fxregs_state __user *fx) +{ + if (IS_ENABLED(CONFIG_X86_32)) + return user_insn(fxrstor %[fx], "=m" (*fx), [fx] "m" (*fx)); else if (IS_ENABLED(CONFIG_AS_FXSAVEQ)) - return kernel_insn_norestore(fxrstorq %[fx], "=m" (*fx), [fx] "m" (*fx)); + return user_insn(fxrstorq %[fx], "=m" (*fx), [fx] "m" (*fx)); /* See comment in copy_fxregs_to_kernel() below. */ - return kernel_insn_norestore(rex64/fxrstor (%[fx]), "=m" (*fx), [fx] "R" (fx), + return user_insn(rex64/fxrstor (%[fx]), "=m" (*fx), [fx] "R" (fx), "m" (*fx)); } @@ -172,9 +197,14 @@ static inline void copy_kernel_to_fregs(struct fregs_state *fx) kernel_insn(frstor %[fx], "=m" (*fx), [fx] "m" (*fx)); } -static inline int copy_users_to_fregs(struct fregs_state *fx) +static inline int copy_kernel_to_fregs_err(struct fregs_state *fx) { - return kernel_insn_norestore(frstor %[fx], "=m" (*fx), [fx] "m" (*fx)); + return kernel_insn_err(frstor %[fx], "=m" (*fx), [fx] "m" (*fx)); +} + +static inline int copy_user_to_fregs(struct fregs_state __user *fx) +{ + return user_insn(frstor %[fx], "=m" (*fx), [fx] "m" (*fx)); } static inline void copy_fxregs_to_kernel(struct fpu *fpu) @@ -351,11 +381,57 @@ static inline void copy_kernel_to_xregs(struct xregs_state *xstate, u64 mask) XSTATE_XRESTORE(xstate, lmask, hmask); } +/* + * Save xstate to user space xsave area. + * + * We don't use modified optimization because xrstor/xrstors might track + * a different application. + * + * We don't use compacted format xsave area for + * backward compatibility for old applications which don't understand + * compacted format of xsave area. + */ +static inline int copy_xregs_to_user(struct xregs_state __user *buf) +{ + int err; + + /* + * Clear the xsave header first, so that reserved fields are + * initialized to zero. + */ + err = __clear_user(&buf->header, sizeof(buf->header)); + if (unlikely(err)) + return -EFAULT; + + stac(); + XSTATE_OP(XSAVE, buf, -1, -1, err); + clac(); + + return err; +} + +/* + * Restore xstate from user space xsave area. + */ +static inline int copy_user_to_xregs(struct xregs_state __user *buf, u64 mask) +{ + struct xregs_state *xstate = ((__force struct xregs_state *)buf); + u32 lmask = mask; + u32 hmask = mask >> 32; + int err; + + stac(); + XSTATE_OP(XRSTOR, xstate, lmask, hmask, err); + clac(); + + return err; +} + /* * Restore xstate from kernel space xsave area, return an error code instead an * exception. */ -static inline int copy_users_to_xregs(struct xregs_state *xstate, u64 mask) +static inline int copy_kernel_to_xregs_err(struct xregs_state *xstate, u64 mask) { u32 lmask = mask; u32 hmask = mask >> 32; @@ -544,7 +620,7 @@ switch_fpu_prepare(struct fpu *old_fpu, int cpu) static inline void switch_fpu_finish(struct fpu *new_fpu) { struct pkru_state *pk; - u32 pkru_val = 0; + u32 pkru_val = init_pkru_value; if (!static_cpu_has(X86_FEATURE_FPU)) return; @@ -554,9 +630,12 @@ static inline void switch_fpu_finish(struct fpu *new_fpu) if (!cpu_feature_enabled(X86_FEATURE_OSPKE)) return; + /* + * PKRU state is switched eagerly because it needs to be valid before we + * return to userland e.g. for a copy_to_user() operation. + */ if (current->mm) { pk = get_xsave_addr(&new_fpu->state.xsave, XFEATURE_PKRU); - WARN_ON_ONCE(!pk); if (pk) pkru_val = pk->pkru; } diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h index 50a8399d223e9..58a3a68e1f114 100644 --- a/arch/x86/include/asm/pgtable.h +++ b/arch/x86/include/asm/pgtable.h @@ -129,7 +129,7 @@ static inline int pte_dirty(pte_t pte) static inline u32 read_pkru(void) { if (boot_cpu_has(X86_FEATURE_OSPKE)) - return __read_pkru(); + return __read_pkru_ins(); return 0; } @@ -1371,6 +1371,12 @@ static inline pmd_t pmd_swp_clear_soft_dirty(pmd_t pmd) #define PKRU_WD_BIT 0x2 #define PKRU_BITS_PER_PKEY 2 +#ifdef CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS +extern u32 init_pkru_value; +#else +#define init_pkru_value 0 +#endif + static inline bool __pkru_allows_read(u32 pkru, u16 pkey) { int pkru_pkey_bits = pkey * PKRU_BITS_PER_PKEY; diff --git a/arch/x86/include/asm/special_insns.h b/arch/x86/include/asm/special_insns.h index 2d3adeb268e38..28ffdf0c1add4 100644 --- a/arch/x86/include/asm/special_insns.h +++ b/arch/x86/include/asm/special_insns.h @@ -92,7 +92,7 @@ static inline void native_write_cr8(unsigned long val) #endif #ifdef CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS -static inline u32 __read_pkru(void) +static inline u32 __read_pkru_ins(void) { u32 ecx = 0; u32 edx, pkru; @@ -107,16 +107,10 @@ static inline u32 __read_pkru(void) return pkru; } -static inline void __write_pkru(u32 pkru) +static inline void __write_pkru_ins(u32 pkru) { u32 ecx = 0, edx = 0; - /* - * WRPKRU is relatively expensive compared to RDPKRU. - * Avoid WRPKRU when it would not change the value. - */ - if (pkru == __read_pkru()) - return; /* * "wrpkru" instruction. Loads contents in EAX to PKRU, * requires that ecx = edx = 0. @@ -124,8 +118,20 @@ static inline void __write_pkru(u32 pkru) asm volatile(".byte 0x0f,0x01,0xef\n\t" : : "a" (pkru), "c"(ecx), "d"(edx)); } + +static inline void __write_pkru(u32 pkru) +{ + /* + * WRPKRU is relatively expensive compared to RDPKRU. + * Avoid WRPKRU when it would not change the value. + */ + if (pkru == __read_pkru_ins()) + return; + __write_pkru_ins(pkru); +} + #else -static inline u32 __read_pkru(void) +static inline u32 __read_pkru_ins(void) { return 0; } diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index cb28e98a0659a..352fa19e63110 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -372,6 +372,8 @@ static bool pku_disabled; static __always_inline void setup_pku(struct cpuinfo_x86 *c) { + struct pkru_state *pk; + /* check the boot processor, plus compile options for PKU: */ if (!cpu_feature_enabled(X86_FEATURE_PKU)) return; @@ -382,6 +384,9 @@ static __always_inline void setup_pku(struct cpuinfo_x86 *c) return; cr4_set_bits(X86_CR4_PKE); + pk = get_xsave_addr(&init_fpstate.xsave, XFEATURE_PKRU); + if (pk) + pk->pkru = init_pkru_value; /* * Seting X86_CR4_PKE will cause the X86_FEATURE_OSPKE * cpuid bit to be set. We need to ensure that we diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c index 589fb27515e08..16f700d5b3a47 100644 --- a/arch/x86/kernel/fpu/signal.c +++ b/arch/x86/kernel/fpu/signal.c @@ -118,6 +118,22 @@ static inline int save_xstate_epilog(void __user *buf, int ia32_frame) return err; } +static inline int copy_fpregs_to_sigframe(struct xregs_state __user *buf) +{ + int err; + + if (use_xsave()) + err = copy_xregs_to_user(buf); + else if (use_fxsr()) + err = copy_fxregs_to_user((struct fxregs_state __user *) buf); + else + err = copy_fregs_to_user((struct fregs_state __user *) buf); + + if (unlikely(err) && __clear_user(buf, fpu_user_xstate_size)) + err = -EFAULT; + return err; +} + /* * Save the fpu, extended register state to the user signal frame. * @@ -128,8 +144,10 @@ static inline int save_xstate_epilog(void __user *buf, int ia32_frame) * buf == buf_fx for 64-bit frames and 32-bit fsave frame. * buf != buf_fx for 32-bit frames with fxstate. * - * Save the state to task's fpu->state and then copy it to the user frame - * pointed by the aligned pointer 'buf_fx'. + * Try to save it directly to the user frame with disabled page fault handler. + * If this fails then do the slow path where the FPU state is first saved to + * task's fpu->state and then copy it to the user frame pointed by the aligned + * pointer 'buf_fx'. * * If this is a 32-bit frame with fxstate, put a fsave header before * the aligned state at 'buf_fx'. @@ -143,6 +161,7 @@ int copy_fpstate_to_sigframe(void __user *buf, void __user *buf_fx, int size) struct xregs_state *xsave = &fpu->state.xsave; struct task_struct *tsk = current; int ia32_fxstate = (buf != buf_fx); + int ret = -EFAULT; ia32_fxstate &= (IS_ENABLED(CONFIG_X86_32) || IS_ENABLED(CONFIG_IA32_EMULATION)); @@ -157,21 +176,31 @@ int copy_fpstate_to_sigframe(void __user *buf, void __user *buf_fx, int size) fpregs_lock(); /* - * If we do not need to load the FPU registers at return to userspace - * then the CPU has the current state and we need to save it. Otherwise - * it is already done and we can skip it. + * Load the FPU register if they are not valid for the current task. + * With a valid FPU state we can attempt to save the state directly to + * userland's stack frame which will likely succeed. If it does not, do + * the slowpath. */ - if (!test_thread_flag(TIF_NEED_FPU_LOAD)) - copy_fpregs_to_fpstate(fpu); + if (test_thread_flag(TIF_NEED_FPU_LOAD)) + __fpregs_load_activate(); + pagefault_disable(); + ret = copy_fpregs_to_sigframe(buf_fx); + pagefault_enable(); + if (ret && !test_thread_flag(TIF_NEED_FPU_LOAD)) + copy_fpregs_to_fpstate(fpu); + set_thread_flag(TIF_NEED_FPU_LOAD); fpregs_unlock(); - if (using_compacted_format()) { - copy_xstate_to_user(buf_fx, xsave, 0, size); - } else { - fpstate_sanitize_xstate(fpu); - if (__copy_to_user(buf_fx, xsave, fpu_user_xstate_size)) - return -1; + if (ret) { + if (using_compacted_format()) { + if (copy_xstate_to_user(buf_fx, xsave, 0, size)) + return -1; + } else { + fpstate_sanitize_xstate(fpu); + if (__copy_to_user(buf_fx, xsave, fpu_user_xstate_size)) + return -1; + } } /* Save the fsave header for the 32-bit frames. */ @@ -221,6 +250,28 @@ sanitize_restored_xstate(union fpregs_state *state, } } +/* + * Restore the extended state if present. Otherwise, restore the FP/SSE state. + */ +static int copy_user_to_fpregs_zeroing(void __user *buf, u64 xbv, int fx_only) +{ + if (use_xsave()) { + if (fx_only) { + u64 init_bv = xfeatures_mask & ~XFEATURE_MASK_FPSSE; + copy_kernel_to_xregs(&init_fpstate.xsave, init_bv); + return copy_user_to_fxregs(buf); + } else { + u64 init_bv = xfeatures_mask & ~xbv; + if (unlikely(init_bv)) + copy_kernel_to_xregs(&init_fpstate.xsave, init_bv); + return copy_user_to_xregs(buf, xbv); + } + } else if (use_fxsr()) { + return copy_user_to_fxregs(buf); + } else + return copy_user_to_fregs(buf); +} + static int __fpu__restore_sig(void __user *buf, void __user *buf_fx, int size) { struct user_i387_ia32_struct *envp = NULL; @@ -287,7 +338,19 @@ static int __fpu__restore_sig(void __user *buf, void __user *buf_fx, int size) if (ret) goto err_out; envp = &env; + } else { + fpregs_lock(); + pagefault_disable(); + ret = copy_user_to_fpregs_zeroing(buf_fx, xfeatures, fx_only); + pagefault_enable(); + if (!ret) { + fpregs_mark_activate(); + fpregs_unlock(); + return 0; + } + fpregs_unlock(); } + if (use_xsave() && !fx_only) { u64 init_bv = xfeatures_mask & ~xfeatures; @@ -307,7 +370,7 @@ static int __fpu__restore_sig(void __user *buf, void __user *buf_fx, int size) fpregs_lock(); if (unlikely(init_bv)) copy_kernel_to_xregs(&init_fpstate.xsave, init_bv); - ret = copy_users_to_xregs(&fpu->state.xsave, xfeatures); + ret = copy_kernel_to_xregs_err(&fpu->state.xsave, xfeatures); } else if (use_fxsr()) { ret = __copy_from_user(&fpu->state.fxsave, buf_fx, state_size); @@ -324,13 +387,13 @@ static int __fpu__restore_sig(void __user *buf, void __user *buf_fx, int size) copy_kernel_to_xregs(&init_fpstate.xsave, init_bv); } - ret = copy_users_to_fxregs(&fpu->state.fxsave); + ret = copy_kernel_to_fxregs_err(&fpu->state.fxsave); } else { ret = __copy_from_user(&fpu->state.fsave, buf_fx, state_size); if (ret) goto err_out; fpregs_lock(); - ret = copy_users_to_fregs(buf_fx); + ret = copy_kernel_to_fregs_err(&fpu->state.fsave); } if (!ret) fpregs_mark_activate(); diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index a0a7708164291..eb694a0f7a22f 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -6630,7 +6630,7 @@ static void vmx_vcpu_run(struct kvm_vcpu *vcpu) */ if (static_cpu_has(X86_FEATURE_PKU) && kvm_read_cr4_bits(vcpu, X86_CR4_PKE)) { - vcpu->arch.pkru = __read_pkru(); + vcpu->arch.pkru = __read_pkru_ins(); if (vcpu->arch.pkru != vmx->host_pkru) __write_pkru(vmx->host_pkru); } diff --git a/arch/x86/mm/pkeys.c b/arch/x86/mm/pkeys.c index 50f65fc1b9a3f..1dcfc91c8f0c3 100644 --- a/arch/x86/mm/pkeys.c +++ b/arch/x86/mm/pkeys.c @@ -18,6 +18,7 @@ #include <asm/cpufeature.h> /* boot_cpu_has, ... */ #include <asm/mmu_context.h> /* vma_pkey() */ +#include <asm/fpu/internal.h> /* init_fpstate */ int __execute_only_pkey(struct mm_struct *mm) { @@ -126,7 +127,6 @@ int __arch_override_mprotect_pkey(struct vm_area_struct *vma, int prot, int pkey * in the process's lifetime will not accidentally get access * to data which is pkey-protected later on. */ -static u32 init_pkru_value = PKRU_AD_KEY( 1) | PKRU_AD_KEY( 2) | PKRU_AD_KEY( 3) | PKRU_AD_KEY( 4) | PKRU_AD_KEY( 5) | PKRU_AD_KEY( 6) | PKRU_AD_KEY( 7) | PKRU_AD_KEY( 8) | PKRU_AD_KEY( 9) | @@ -162,6 +162,7 @@ static ssize_t init_pkru_read_file(struct file *file, char __user *user_buf, static ssize_t init_pkru_write_file(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { + struct pkru_state *pk; char buf[32]; ssize_t len; u32 new_init_pkru; @@ -184,6 +185,10 @@ static ssize_t init_pkru_write_file(struct file *file, return -EINVAL; WRITE_ONCE(init_pkru_value, new_init_pkru); + pk = get_xsave_addr(&init_fpstate.xsave, XFEATURE_PKRU); + if (!pk) + return -EINVAL; + pk->pkru = new_init_pkru; return count; } diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 75990b60b72ca..cbf800096fdf5 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -398,11 +398,11 @@ config ARMV7M_SYSTICK This options enables support for the ARMv7M system timer unit config ATMEL_PIT - bool "Microchip ARM Periodic Interval Timer (PIT)" if COMPILE_TEST + bool "Atmel PIT support" if COMPILE_TEST + depends on HAS_IOMEM select TIMER_OF if OF help - This enables build of clocksource and clockevent driver for - the integrated PIT in Microchip ARM SoCs. + Support for the Periodic Interval Timer found on Atmel SoCs. config ATMEL_ST bool "Atmel ST timer support" if COMPILE_TEST @@ -412,13 +412,19 @@ config ATMEL_ST help Support for the Atmel ST timer. -config ATMEL_ARM_TCB_CLKSRC - bool "Microchip ARM TC Block" if COMPILE_TEST - select REGMAP_MMIO - depends on GENERIC_CLOCKEVENTS +config ATMEL_TCB_CLKSRC + bool "Atmel TC Block timer driver" if COMPILE_TEST + depends on HAS_IOMEM + select TIMER_OF if OF help - This enables build of clocksource and clockevent driver for - the integrated Timer Counter Blocks in Microchip ARM SoCs. + Support for Timer Counter Blocks on Atmel SoCs. + +config ATMEL_TCB_CLKSRC_USE_SLOW_CLOCK + bool "TC Block use 32 KiHz clock" + depends on ATMEL_TCB_CLKSRC + default y + help + Select this to use 32 KiHz base clock rate as TC block clock. config CLKSRC_EXYNOS_MCT bool "Exynos multi core timer driver" if COMPILE_TEST diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 4089469eee166..c93bd598955fb 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -3,8 +3,7 @@ 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_ATMEL_ARM_TCB_CLKSRC) += timer-atmel-tcb.o +obj-$(CONFIG_ATMEL_TCB_CLKSRC) += timer-atmel-tcb.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 diff --git a/drivers/clocksource/tcb_clksrc.c b/drivers/clocksource/tcb_clksrc.c deleted file mode 100644 index ba15242a60665..0000000000000 --- a/drivers/clocksource/tcb_clksrc.c +++ /dev/null @@ -1,464 +0,0 @@ -// 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. - * - * 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; - bool clk_enabled; - u32 freq; - 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); -} - -static u32 timer_clock; - -static void tc_clk_disable(struct clock_event_device *d) -{ - struct tc_clkevt_device *tcd = to_tc_clkevt(d); - - clk_disable(tcd->clk); - tcd->clk_enabled = false; -} - -static void tc_clk_enable(struct clock_event_device *d) -{ - struct tc_clkevt_device *tcd = to_tc_clkevt(d); - - if (tcd->clk_enabled) - return; - clk_enable(tcd->clk); - tcd->clk_enabled = true; -} - -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)); - return 0; -} - -static int tc_shutdown_clk_off(struct clock_event_device *d) -{ - tc_shutdown(d); - if (!clockevent_state_detached(d)) - tc_clk_disable(d); - - 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); - - tc_clk_enable(d); - - /* 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. - */ - tc_clk_enable(d); - - /* 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((tcd->freq + 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 */ -#ifdef CONFIG_ATMEL_TCB_CLKSRC_USE_SLOW_CLOCK - .rating = 125, -#else - .rating = 200, -#endif - .set_next_event = tc_next_event, - .set_state_shutdown = tc_shutdown_clk_off, - .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 divisor_idx) -{ - unsigned divisor = atmel_tc_divisors[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 = divisor_idx; - if (!divisor) - clkevt.freq = 32768; - else - clkevt.freq = clk_get_rate(t2_clk) / divisor; - - 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, clkevt.freq, 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 */ -#ifdef CONFIG_ATMEL_TCB_CLKSRC_USE_SLOW_CLOCK - ret = setup_clkevents(tc, clk32k_divisor_idx); -#else - ret = setup_clkevents(tc, best_divisor_idx); -#endif - 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); diff --git a/drivers/clocksource/timer-atmel-tcb.c b/drivers/clocksource/timer-atmel-tcb.c index 63ce3b69338a0..cfcc18902651a 100644 --- a/drivers/clocksource/timer-atmel-tcb.c +++ b/drivers/clocksource/timer-atmel-tcb.c @@ -1,468 +1,437 @@ // SPDX-License-Identifier: GPL-2.0 -#include <linux/clk.h> -#include <linux/clockchips.h> +#include <linux/init.h> #include <linux/clocksource.h> +#include <linux/clockchips.h> #include <linux/interrupt.h> -#include <linux/kernel.h> -#include <linux/mfd/syscon.h> +#include <linux/irq.h> + +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/ioport.h> +#include <linux/io.h> #include <linux/of_address.h> #include <linux/of_irq.h> -#include <linux/regmap.h> #include <linux/sched_clock.h> +#include <linux/syscore_ops.h> #include <soc/at91/atmel_tcb.h> -struct atmel_tcb_clksrc { - struct clocksource clksrc; - struct clock_event_device clkevt; - struct regmap *regmap; - void __iomem *base; - struct clk *clk[2]; - char name[20]; - int channels[2]; - int bits; - int irq; - struct { - u32 cmr; - u32 imr; - u32 rc; - bool clken; - } cache[2]; - u32 bmr_cache; - bool registered; - bool clk_enabled; -}; - -static struct atmel_tcb_clksrc tc, tce; - -static struct clk *tcb_clk_get(struct device_node *node, int channel) -{ - struct clk *clk; - char clk_name[] = "t0_clk"; - - clk_name[1] += channel; - clk = of_clk_get_by_name(node->parent, clk_name); - if (!IS_ERR(clk)) - return clk; - - return of_clk_get_by_name(node->parent, "t0_clk"); -} /* - * Clockevent device using its own channel - */ - -static void tc_clkevt2_clk_disable(struct clock_event_device *d) -{ - clk_disable(tce.clk[0]); - tce.clk_enabled = false; -} - -static void tc_clkevt2_clk_enable(struct clock_event_device *d) -{ - if (tce.clk_enabled) - return; - clk_enable(tce.clk[0]); - tce.clk_enabled = true; -} - -static int tc_clkevt2_stop(struct clock_event_device *d) -{ - writel(0xff, tce.base + ATMEL_TC_IDR(tce.channels[0])); - writel(ATMEL_TC_CCR_CLKDIS, tce.base + ATMEL_TC_CCR(tce.channels[0])); - - return 0; -} - -static int tc_clkevt2_shutdown(struct clock_event_device *d) -{ - tc_clkevt2_stop(d); - if (!clockevent_state_detached(d)) - tc_clkevt2_clk_disable(d); - - return 0; -} - -/* 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). + * We're configured to use a specific TC block, one that's not hooked + * up to external hardware, to provide a time solution: * - * A divided clock could be good for high resolution timers, since - * 30.5 usec resolution can seem "low". + * - 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. + * + * 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 int tc_clkevt2_set_oneshot(struct clock_event_device *d) + +static void __iomem *tcaddr; +static struct { - if (clockevent_state_oneshot(d) || clockevent_state_periodic(d)) - tc_clkevt2_stop(d); + u32 cmr; + u32 imr; + u32 rc; + bool clken; +} tcb_cache[3]; +static u32 bmr_cache; - tc_clkevt2_clk_enable(d); - - /* slow clock, count up to RC, then irq and stop */ - writel(ATMEL_TC_CMR_TCLK(4) | ATMEL_TC_CMR_CPCSTOP | - ATMEL_TC_CMR_WAVE | ATMEL_TC_CMR_WAVESEL_UPRC, - tce.base + ATMEL_TC_CMR(tce.channels[0])); - writel(ATMEL_TC_CPCS, tce.base + ATMEL_TC_IER(tce.channels[0])); - - return 0; -} - -static int tc_clkevt2_set_periodic(struct clock_event_device *d) -{ - if (clockevent_state_oneshot(d) || clockevent_state_periodic(d)) - tc_clkevt2_stop(d); - - /* By not making the gentime core emulate periodic mode on top - * of oneshot, we get lower overhead and improved accuracy. - */ - tc_clkevt2_clk_enable(d); - - /* slow clock, count up to RC, then irq and restart */ - writel(ATMEL_TC_CMR_TCLK(4) | ATMEL_TC_CMR_WAVE | - ATMEL_TC_CMR_WAVESEL_UPRC, - tce.base + ATMEL_TC_CMR(tce.channels[0])); - writel((32768 + HZ / 2) / HZ, tce.base + ATMEL_TC_RC(tce.channels[0])); - - /* Enable clock and interrupts on RC compare */ - writel(ATMEL_TC_CPCS, tce.base + ATMEL_TC_IER(tce.channels[0])); - writel(ATMEL_TC_CCR_CLKEN | ATMEL_TC_CCR_SWTRG, - tce.base + ATMEL_TC_CCR(tce.channels[0])); - - return 0; -} - -static int tc_clkevt2_next_event(unsigned long delta, - struct clock_event_device *d) -{ - writel(delta, tce.base + ATMEL_TC_RC(tce.channels[0])); - writel(ATMEL_TC_CCR_CLKEN | ATMEL_TC_CCR_SWTRG, - tce.base + ATMEL_TC_CCR(tce.channels[0])); - - return 0; -} - -static irqreturn_t tc_clkevt2_irq(int irq, void *handle) -{ - unsigned int sr; - - sr = readl(tce.base + ATMEL_TC_SR(tce.channels[0])); - if (sr & ATMEL_TC_CPCS) { - tce.clkevt.event_handler(&tce.clkevt); - return IRQ_HANDLED; - } - - return IRQ_NONE; -} - -static void tc_clkevt2_suspend(struct clock_event_device *d) -{ - tce.cache[0].cmr = readl(tce.base + ATMEL_TC_CMR(tce.channels[0])); - tce.cache[0].imr = readl(tce.base + ATMEL_TC_IMR(tce.channels[0])); - tce.cache[0].rc = readl(tce.base + ATMEL_TC_RC(tce.channels[0])); - tce.cache[0].clken = !!(readl(tce.base + ATMEL_TC_SR(tce.channels[0])) & - ATMEL_TC_CLKSTA); -} - -static void tc_clkevt2_resume(struct clock_event_device *d) -{ - /* Restore registers for the channel, RA and RB are not used */ - writel(tce.cache[0].cmr, tc.base + ATMEL_TC_CMR(tce.channels[0])); - writel(tce.cache[0].rc, tc.base + ATMEL_TC_RC(tce.channels[0])); - writel(0, tc.base + ATMEL_TC_RA(tce.channels[0])); - writel(0, tc.base + ATMEL_TC_RB(tce.channels[0])); - /* Disable all the interrupts */ - writel(0xff, tc.base + ATMEL_TC_IDR(tce.channels[0])); - /* Reenable interrupts that were enabled before suspending */ - writel(tce.cache[0].imr, tc.base + ATMEL_TC_IER(tce.channels[0])); - - /* Start the clock if it was used */ - if (tce.cache[0].clken) - writel(ATMEL_TC_CCR_CLKEN | ATMEL_TC_CCR_SWTRG, - tc.base + ATMEL_TC_CCR(tce.channels[0])); -} - -static int __init tc_clkevt_register(struct device_node *node, - struct regmap *regmap, void __iomem *base, - int channel, int irq, int bits) -{ - int ret; - struct clk *slow_clk; - - tce.regmap = regmap; - tce.base = base; - tce.channels[0] = channel; - tce.irq = irq; - - slow_clk = of_clk_get_by_name(node->parent, "slow_clk"); - if (IS_ERR(slow_clk)) - return PTR_ERR(slow_clk); - - ret = clk_prepare_enable(slow_clk); - if (ret) - return ret; - - tce.clk[0] = tcb_clk_get(node, tce.channels[0]); - if (IS_ERR(tce.clk[0])) { - ret = PTR_ERR(tce.clk[0]); - goto err_slow; - } - - snprintf(tce.name, sizeof(tce.name), "%s:%d", - kbasename(node->parent->full_name), channel); - tce.clkevt.cpumask = cpumask_of(0); - tce.clkevt.name = tce.name; - tce.clkevt.set_next_event = tc_clkevt2_next_event, - tce.clkevt.set_state_shutdown = tc_clkevt2_shutdown, - tce.clkevt.set_state_periodic = tc_clkevt2_set_periodic, - tce.clkevt.set_state_oneshot = tc_clkevt2_set_oneshot, - tce.clkevt.suspend = tc_clkevt2_suspend, - tce.clkevt.resume = tc_clkevt2_resume, - tce.clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; - tce.clkevt.rating = 140; - - /* try to enable clk to avoid future errors in mode change */ - ret = clk_prepare_enable(tce.clk[0]); - if (ret) - goto err_slow; - clk_disable(tce.clk[0]); - - clockevents_config_and_register(&tce.clkevt, 32768, 1, - CLOCKSOURCE_MASK(bits)); - - ret = request_irq(tce.irq, tc_clkevt2_irq, IRQF_TIMER | IRQF_SHARED, - tce.clkevt.name, &tce); - if (ret) - goto err_clk; - - tce.registered = true; - - return 0; - -err_clk: - clk_unprepare(tce.clk[0]); -err_slow: - clk_disable_unprepare(slow_clk); - - return ret; -} - -/* - * Clocksource and clockevent using the same channel(s) - */ static u64 tc_get_cycles(struct clocksource *cs) { - u32 lower, upper; + unsigned long flags; + u32 lower, upper; + raw_local_irq_save(flags); do { - upper = readl_relaxed(tc.base + ATMEL_TC_CV(tc.channels[1])); - lower = readl_relaxed(tc.base + ATMEL_TC_CV(tc.channels[0])); - } while (upper != readl_relaxed(tc.base + ATMEL_TC_CV(tc.channels[1]))); + 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(tc.base + ATMEL_TC_CV(tc.channels[0])); + 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 = { + .rating = 200, + .read = tc_get_cycles, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, + .suspend = tc_clksrc_suspend, + .resume = tc_clksrc_resume, +}; + static u64 notrace tc_sched_clock_read(void) { - return tc_get_cycles(&tc.clksrc); + return tc_get_cycles(&clksrc); } static u64 notrace tc_sched_clock_read32(void) { - return tc_get_cycles32(&tc.clksrc); + return tc_get_cycles32(&clksrc); } -static int tcb_clkevt_next_event(unsigned long delta, - struct clock_event_device *d) +#ifdef CONFIG_GENERIC_CLOCKEVENTS + +struct tc_clkevt_device { + struct clock_event_device clkevt; + struct clk *clk; + bool clk_enabled; + u32 freq; + void __iomem *regs; +}; + +static struct tc_clkevt_device *to_tc_clkevt(struct clock_event_device *clkevt) { - u32 old, next, cur; + return container_of(clkevt, struct tc_clkevt_device, clkevt); +} - old = readl(tc.base + ATMEL_TC_CV(tc.channels[0])); - next = old + delta; - writel(next, tc.base + ATMEL_TC_RC(tc.channels[0])); - cur = readl(tc.base + ATMEL_TC_CV(tc.channels[0])); +static u32 timer_clock; - /* check whether the delta elapsed while setting the register */ - if ((next < old && cur < old && cur > next) || - (next > old && (cur < old || cur > next))) { - /* - * Clear the CPCS bit in the status register to avoid - * generating a spurious interrupt next time a valid - * timer event is configured. - */ - old = readl(tc.base + ATMEL_TC_SR(tc.channels[0])); - return -ETIME; - } +static void tc_clk_disable(struct clock_event_device *d) +{ + struct tc_clkevt_device *tcd = to_tc_clkevt(d); - writel(ATMEL_TC_CPCS, tc.base + ATMEL_TC_IER(tc.channels[0])); + clk_disable(tcd->clk); + tcd->clk_enabled = false; +} + +static void tc_clk_enable(struct clock_event_device *d) +{ + struct tc_clkevt_device *tcd = to_tc_clkevt(d); + + if (tcd->clk_enabled) + return; + clk_enable(tcd->clk); + tcd->clk_enabled = true; +} + +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)); + return 0; +} + +static int tc_shutdown_clk_off(struct clock_event_device *d) +{ + tc_shutdown(d); + if (!clockevent_state_detached(d)) + tc_clk_disable(d); return 0; } -static irqreturn_t tc_clkevt_irq(int irq, void *handle) +static int tc_set_oneshot(struct clock_event_device *d) { - unsigned int sr; + struct tc_clkevt_device *tcd = to_tc_clkevt(d); + void __iomem *regs = tcd->regs; - sr = readl(tc.base + ATMEL_TC_SR(tc.channels[0])); + if (clockevent_state_oneshot(d) || clockevent_state_periodic(d)) + tc_shutdown(d); + + tc_clk_enable(d); + + /* 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. + */ + tc_clk_enable(d); + + /* 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((tcd->freq + 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 = { + .features = CLOCK_EVT_FEAT_PERIODIC | + CLOCK_EVT_FEAT_ONESHOT, + /* Should be lower than at91rm9200's system timer */ +#ifdef CONFIG_ATMEL_TCB_CLKSRC_USE_SLOW_CLOCK + .rating = 125, +#else + .rating = 200, +#endif + .set_next_event = tc_next_event, + .set_state_shutdown = tc_shutdown_clk_off, + .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) { - tc.clkevt.event_handler(&tc.clkevt); + dev->clkevt.event_handler(&dev->clkevt); return IRQ_HANDLED; } return IRQ_NONE; } -static int tcb_clkevt_oneshot(struct clock_event_device *dev) +static int __init setup_clkevents(struct atmel_tc *tc, int divisor_idx) { - if (clockevent_state_oneshot(dev)) - return 0; + unsigned divisor = atmel_tc_divisors[divisor_idx]; + int ret; + struct clk *t2_clk = tc->clk[2]; + int irq = tc->irq[2]; - /* - * Because both clockevent devices may share the same IRQ, we don't want - * the less likely one to stay requested - */ - return request_irq(tc.irq, tc_clkevt_irq, IRQF_TIMER | IRQF_SHARED, - tc.name, &tc); + 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 = divisor_idx; + if (!divisor) + clkevt.freq = 32768; + else + clkevt.freq = clk_get_rate(t2_clk) / divisor; + + 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, clkevt.freq, 1, 0xffff); + + return ret; } -static int tcb_clkevt_shutdown(struct clock_event_device *dev) +#else /* !CONFIG_GENERIC_CLOCKEVENTS */ + +static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) { - writel(0xff, tc.base + ATMEL_TC_IDR(tc.channels[0])); - if (tc.bits == 16) - writel(0xff, tc.base + ATMEL_TC_IDR(tc.channels[1])); - - if (!clockevent_state_detached(dev)) - free_irq(tc.irq, &tc); - + /* NOTHING */ return 0; } -static void __init tcb_setup_dual_chan(struct atmel_tcb_clksrc *tc, - int mck_divisor_idx) +#endif + +static void __init tcb_setup_dual_chan(struct atmel_tc *tc, int mck_divisor_idx) { - /* first channel: waveform mode, input mclk/8, clock TIOA on overflow */ + /* channel 0: waveform mode, input mclk/8, clock TIOA0 on overflow */ writel(mck_divisor_idx /* likely divide-by-8 */ - | ATMEL_TC_CMR_WAVE - | ATMEL_TC_CMR_WAVESEL_UP /* free-run */ - | ATMEL_TC_CMR_ACPA(SET) /* TIOA rises at 0 */ - | ATMEL_TC_CMR_ACPC(CLEAR), /* (duty cycle 50%) */ - tc->base + ATMEL_TC_CMR(tc->channels[0])); - writel(0x0000, tc->base + ATMEL_TC_RA(tc->channels[0])); - writel(0x8000, tc->base + ATMEL_TC_RC(tc->channels[0])); - writel(0xff, tc->base + ATMEL_TC_IDR(tc->channels[0])); /* no irqs */ - writel(ATMEL_TC_CCR_CLKEN, tc->base + ATMEL_TC_CCR(tc->channels[0])); + | 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)); - /* second channel: waveform mode, input TIOA */ - writel(ATMEL_TC_CMR_XC(tc->channels[1]) /* input: TIOA */ - | ATMEL_TC_CMR_WAVE - | ATMEL_TC_CMR_WAVESEL_UP, /* free-run */ - tc->base + ATMEL_TC_CMR(tc->channels[1])); - writel(0xff, tc->base + ATMEL_TC_IDR(tc->channels[1])); /* no irqs */ - writel(ATMEL_TC_CCR_CLKEN, tc->base + ATMEL_TC_CCR(tc->channels[1])); + /* 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 both channel, we assume the previous channel */ - regmap_write(tc->regmap, ATMEL_TC_BMR, - ATMEL_TC_BMR_TCXC(1 + tc->channels[1], tc->channels[1])); + /* chain channel 0 to channel 1*/ + writel(ATMEL_TC_TC1XC1S_TIOA0, tcaddr + ATMEL_TC_BMR); /* then reset all the timers */ - regmap_write(tc->regmap, ATMEL_TC_BCR, ATMEL_TC_BCR_SYNC); + writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); } -static void __init tcb_setup_single_chan(struct atmel_tcb_clksrc *tc, - int mck_divisor_idx) +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_CMR_WAVE - | ATMEL_TC_CMR_WAVESEL_UP, /* free-run */ - tc->base + ATMEL_TC_CMR(tc->channels[0])); - writel(0xff, tc->base + ATMEL_TC_IDR(tc->channels[0])); /* no irqs */ - writel(ATMEL_TC_CCR_CLKEN, tc->base + ATMEL_TC_CCR(tc->channels[0])); + | 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 */ - regmap_write(tc->regmap, ATMEL_TC_BCR, ATMEL_TC_BCR_SYNC); + writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); } -static void tc_clksrc_suspend(struct clocksource *cs) -{ - int i; - - for (i = 0; i < 1 + (tc.bits == 16); i++) { - tc.cache[i].cmr = readl(tc.base + ATMEL_TC_CMR(tc.channels[i])); - tc.cache[i].imr = readl(tc.base + ATMEL_TC_IMR(tc.channels[i])); - tc.cache[i].rc = readl(tc.base + ATMEL_TC_RC(tc.channels[i])); - tc.cache[i].clken = !!(readl(tc.base + - ATMEL_TC_SR(tc.channels[i])) & - ATMEL_TC_CLKSTA); - } - - if (tc.bits == 16) - regmap_read(tc.regmap, ATMEL_TC_BMR, &tc.bmr_cache); -} - -static void tc_clksrc_resume(struct clocksource *cs) -{ - int i; - - for (i = 0; i < 1 + (tc.bits == 16); i++) { - /* Restore registers for the channel, RA and RB are not used */ - writel(tc.cache[i].cmr, tc.base + ATMEL_TC_CMR(tc.channels[i])); - writel(tc.cache[i].rc, tc.base + ATMEL_TC_RC(tc.channels[i])); - writel(0, tc.base + ATMEL_TC_RA(tc.channels[i])); - writel(0, tc.base + ATMEL_TC_RB(tc.channels[i])); - /* Disable all the interrupts */ - writel(0xff, tc.base + ATMEL_TC_IDR(tc.channels[i])); - /* Reenable interrupts that were enabled before suspending */ - writel(tc.cache[i].imr, tc.base + ATMEL_TC_IER(tc.channels[i])); - - /* Start the clock if it was used */ - if (tc.cache[i].clken) - writel(ATMEL_TC_CCR_CLKEN, tc.base + - ATMEL_TC_CCR(tc.channels[i])); - } - - /* in case of dual channel, chain channels */ - if (tc.bits == 16) - regmap_write(tc.regmap, ATMEL_TC_BMR, tc.bmr_cache); - /* Finally, trigger all the channels*/ - regmap_write(tc.regmap, ATMEL_TC_BCR, ATMEL_TC_BCR_SYNC); -} - -static int __init tcb_clksrc_register(struct device_node *node, - struct regmap *regmap, void __iomem *base, - int channel, int channel1, int irq, - int bits) +static int __init tcb_clksrc_init(struct device_node *node) { + struct atmel_tc tc; + struct clk *t0_clk; + const struct of_device_id *match; + u64 (*tc_sched_clock)(void); + int irq; u32 rate, divided_rate = 0; int best_divisor_idx = -1; - int i, err = -1; - u64 (*tc_sched_clock)(void); + int clk32k_divisor_idx = -1; + int i; + int ret; - tc.regmap = regmap; - tc.base = base; - tc.channels[0] = channel; - tc.channels[1] = channel1; - tc.irq = irq; - tc.bits = bits; + /* Protect against multiple calls */ + if (tcaddr) + return 0; - tc.clk[0] = tcb_clk_get(node, tc.channels[0]); - if (IS_ERR(tc.clk[0])) - return PTR_ERR(tc.clk[0]); - err = clk_prepare_enable(tc.clk[0]); - if (err) { + tc.regs = of_iomap(node->parent, 0); + if (!tc.regs) + return -ENXIO; + + t0_clk = of_clk_get_by_name(node->parent, "t0_clk"); + if (IS_ERR(t0_clk)) + return PTR_ERR(t0_clk); + + tc.slow_clk = of_clk_get_by_name(node->parent, "slow_clk"); + if (IS_ERR(tc.slow_clk)) + return PTR_ERR(tc.slow_clk); + + irq = of_irq_get(node->parent, 0); + if (irq <= 0) + return -EINVAL; + + tc.clk[0] = t0_clk; + tc.clk[1] = of_clk_get_by_name(node->parent, "t1_clk"); + if (IS_ERR(tc.clk[1])) + tc.clk[1] = t0_clk; + tc.clk[2] = of_clk_get_by_name(node->parent, "t2_clk"); + if (IS_ERR(tc.clk[2])) + tc.clk[2] = t0_clk; + + tc.irq[0] = irq; + tc.irq[1] = of_irq_get(node->parent, 1); + if (tc.irq[1] <= 0) + tc.irq[1] = irq; + tc.irq[2] = of_irq_get(node->parent, 2); + if (tc.irq[2] <= 0) + tc.irq[2] = irq; + + match = of_match_node(atmel_tcb_dt_ids, node->parent); + tc.tcb_config = match->data; + + for (i = 0; i < ARRAY_SIZE(tc.irq); i++) + writel(ATMEL_TC_ALL_IRQ, tc.regs + ATMEL_TC_REG(i, IDR)); + + ret = clk_prepare_enable(t0_clk); + if (ret) { pr_debug("can't enable T0 clk\n"); - goto err_clk; + return ret; } /* How fast will we be counting? Pick something over 5 MHz. */ - rate = (u32)clk_get_rate(tc.clk[0]); - for (i = 0; i < 5; i++) { - unsigned int divisor = atmel_tc_divisors[i]; - unsigned int tmp; + rate = (u32) clk_get_rate(t0_clk); + for (i = 0; i < ARRAY_SIZE(atmel_tc_divisors); i++) { + unsigned divisor = atmel_tc_divisors[i]; + unsigned tmp; - if (!divisor) + /* 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); @@ -474,144 +443,63 @@ static int __init tcb_clksrc_register(struct device_node *node, best_divisor_idx = i; } - if (tc.bits == 32) { - tc.clksrc.read = tc_get_cycles32; + clksrc.name = kbasename(node->parent->full_name); + clkevt.clkevt.name = kbasename(node->parent->full_name); + pr_debug("%s at %d.%03d MHz\n", clksrc.name, divided_rate / 1000000, + ((divided_rate % 1000000) + 500) / 1000); + + tcaddr = tc.regs; + + if (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); tc_sched_clock = tc_sched_clock_read32; - snprintf(tc.name, sizeof(tc.name), "%s:%d", - kbasename(node->parent->full_name), tc.channels[0]); } else { - tc.clk[1] = tcb_clk_get(node, tc.channels[1]); - if (IS_ERR(tc.clk[1])) - goto err_disable_t0; - - err = clk_prepare_enable(tc.clk[1]); - if (err) { + /* we have 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_clk1; + goto err_disable_t0; } - tc.clksrc.read = tc_get_cycles, + /* setup both channel 0 & 1 */ tcb_setup_dual_chan(&tc, best_divisor_idx); tc_sched_clock = tc_sched_clock_read; - snprintf(tc.name, sizeof(tc.name), "%s:%d,%d", - kbasename(node->parent->full_name), tc.channels[0], - tc.channels[1]); } - pr_debug("%s at %d.%03d MHz\n", tc.name, - divided_rate / 1000000, - ((divided_rate + 500000) % 1000000) / 1000); - - tc.clksrc.name = tc.name; - tc.clksrc.suspend = tc_clksrc_suspend; - tc.clksrc.resume = tc_clksrc_resume; - tc.clksrc.rating = 200; - tc.clksrc.mask = CLOCKSOURCE_MASK(32); - tc.clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS; - - err = clocksource_register_hz(&tc.clksrc, divided_rate); - if (err) + /* and away we go! */ + ret = clocksource_register_hz(&clksrc, divided_rate); + if (ret) goto err_disable_t1; + /* channel 2: periodic and oneshot timer support */ +#ifdef CONFIG_ATMEL_TCB_CLKSRC_USE_SLOW_CLOCK + ret = setup_clkevents(&tc, clk32k_divisor_idx); +#else + ret = setup_clkevents(tc, best_divisor_idx); +#endif + if (ret) + goto err_unregister_clksrc; + sched_clock_register(tc_sched_clock, 32, divided_rate); - tc.registered = true; - - /* Set up and register clockevents */ - tc.clkevt.name = tc.name; - tc.clkevt.cpumask = cpumask_of(0); - tc.clkevt.set_next_event = tcb_clkevt_next_event; - tc.clkevt.set_state_oneshot = tcb_clkevt_oneshot; - tc.clkevt.set_state_shutdown = tcb_clkevt_shutdown; - tc.clkevt.features = CLOCK_EVT_FEAT_ONESHOT; - tc.clkevt.rating = 125; - - clockevents_config_and_register(&tc.clkevt, divided_rate, 1, - BIT(tc.bits) - 1); - return 0; +err_unregister_clksrc: + clocksource_unregister(&clksrc); + err_disable_t1: - if (tc.bits == 16) + if (tc.tcb_config->counter_width != 32) clk_disable_unprepare(tc.clk[1]); -err_clk1: - if (tc.bits == 16) - clk_put(tc.clk[1]); - err_disable_t0: - clk_disable_unprepare(tc.clk[0]); + clk_disable_unprepare(t0_clk); -err_clk: - clk_put(tc.clk[0]); + tcaddr = NULL; - pr_err("%s: unable to register clocksource/clockevent\n", - tc.clksrc.name); - - return err; -} - -static int __init tcb_clksrc_init(struct device_node *node) -{ - const struct of_device_id *match; - struct regmap *regmap; - void __iomem *tcb_base; - u32 channel; - int irq, err, chan1 = -1; - unsigned bits; - - if (tc.registered && tce.registered) - return -ENODEV; - - /* - * The regmap has to be used to access registers that are shared - * between channels on the same TCB but we keep direct IO access for - * the counters to avoid the impact on performance - */ - regmap = syscon_node_to_regmap(node->parent); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); - - tcb_base = of_iomap(node->parent, 0); - if (!tcb_base) { - pr_err("%s +%d %s\n", __FILE__, __LINE__, __func__); - return -ENXIO; - } - - match = of_match_node(atmel_tcb_dt_ids, node->parent); - bits = (uintptr_t)match->data; - - err = of_property_read_u32_index(node, "reg", 0, &channel); - if (err) - return err; - - irq = of_irq_get(node->parent, channel); - if (irq < 0) { - irq = of_irq_get(node->parent, 0); - if (irq < 0) - return irq; - } - - if (tc.registered) - return tc_clkevt_register(node, regmap, tcb_base, channel, irq, - bits); - - if (bits == 16) { - of_property_read_u32_index(node, "reg", 1, &chan1); - if (chan1 == -1) { - if (tce.registered) { - pr_err("%s: clocksource needs two channels\n", - node->parent->full_name); - return -EINVAL; - } else { - return tc_clkevt_register(node, regmap, - tcb_base, channel, - irq, bits); - } - } - } - - return tcb_clksrc_register(node, regmap, tcb_base, channel, chan1, irq, - bits); + return ret; } TIMER_OF_DECLARE(atmel_tcb_clksrc, "atmel,tcb-timer", tcb_clksrc_init); diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c index ca95ab2f4cfa3..8744d20ac1681 100644 --- a/drivers/gpu/drm/i915/i915_request.c +++ b/drivers/gpu/drm/i915/i915_request.c @@ -278,9 +278,7 @@ static void __retire_engine_request(struct intel_engine_cs *engine, GEM_BUG_ON(!i915_request_completed(rq)); - local_irq_disable(); - - spin_lock(&engine->timeline.lock); + spin_lock_irq(&engine->timeline.lock); GEM_BUG_ON(!list_is_first(&rq->link, &engine->timeline.requests)); list_del_init(&rq->link); spin_unlock(&engine->timeline.lock); @@ -294,9 +292,7 @@ static void __retire_engine_request(struct intel_engine_cs *engine, GEM_BUG_ON(!atomic_read(&rq->i915->gt_pm.rps.num_waiters)); atomic_dec(&rq->i915->gt_pm.rps.num_waiters); } - spin_unlock(&rq->lock); - - local_irq_enable(); + spin_unlock_irq(&rq->lock); /* * The backing object for the context is done after switching to the diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index fb6bdd90cad5a..ab3c8ba07f2ce 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -59,38 +59,6 @@ config ATMEL_TCLIB blocks found on many Atmel processors. This facilitates using these blocks by different drivers despite processor differences. -config ATMEL_TCB_CLKSRC - bool "TC Block Clocksource" - depends on ATMEL_TCLIB - default y - help - Select this to get a high precision clocksource based on a - TC block with a 5+ MHz base clock rate. Two timer channels - are combined to make a single 32-bit timer. - - When GENERIC_CLOCKEVENTS is defined, the third timer channel - may be used as a clock event device supporting oneshot mode. - -config ATMEL_TCB_CLKSRC_BLOCK - int - depends on ATMEL_TCB_CLKSRC - default 0 - range 0 1 - help - Some chips provide more than one TC block, so you have the - choice of which one to use for the clock framework. The other - TC can be used for other purposes, such as PWM generation and - interval timing. - -config ATMEL_TCB_CLKSRC_USE_SLOW_CLOCK - bool "TC Block use 32 KiHz clock" - depends on ATMEL_TCB_CLKSRC - default y - help - Select this to use 32 KiHz base clock rate as TC block clock - source for clock events. - - config DUMMY_IRQ tristate "Dummy IRQ handler" default n diff --git a/drivers/misc/atmel_tclib.c b/drivers/misc/atmel_tclib.c index ac24a4bd63f75..b610cc894cd82 100644 --- a/drivers/misc/atmel_tclib.c +++ b/drivers/misc/atmel_tclib.c @@ -1,4 +1,3 @@ -#include <linux/atmel_tc.h> #include <linux/clk.h> #include <linux/err.h> #include <linux/init.h> @@ -10,6 +9,7 @@ #include <linux/slab.h> #include <linux/export.h> #include <linux/of.h> +#include <soc/at91/atmel_tcb.h> /* * This is a thin library to solve the problem of how to portably allocate @@ -17,18 +17,6 @@ * share individual timers between different drivers. */ -#if defined(CONFIG_AVR32) -/* AVR32 has these divide PBB */ -const u8 atmel_tc_divisors[5] = { 0, 4, 8, 16, 32, }; -EXPORT_SYMBOL(atmel_tc_divisors); - -#elif defined(CONFIG_ARCH_AT91) -/* AT91 has these divide MCK */ -const u8 atmel_tc_divisors[5] = { 2, 8, 32, 128, 0, }; -EXPORT_SYMBOL(atmel_tc_divisors); - -#endif - static DEFINE_SPINLOCK(tc_list_lock); static LIST_HEAD(tc_list); @@ -80,26 +68,6 @@ void atmel_tc_free(struct atmel_tc *tc) EXPORT_SYMBOL_GPL(atmel_tc_free); #if defined(CONFIG_OF) -static struct atmel_tcb_config tcb_rm9200_config = { - .counter_width = 16, -}; - -static struct atmel_tcb_config tcb_sam9x5_config = { - .counter_width = 32, -}; - -static const struct of_device_id atmel_tcb_dt_ids[] = { - { - .compatible = "atmel,at91rm9200-tcb", - .data = &tcb_rm9200_config, - }, { - .compatible = "atmel,at91sam9x5-tcb", - .data = &tcb_sam9x5_config, - }, { - /* sentinel */ - } -}; - MODULE_DEVICE_TABLE(of, atmel_tcb_dt_ids); #endif @@ -111,6 +79,9 @@ static int __init tc_probe(struct platform_device *pdev) struct resource *r; unsigned int i; + if (of_get_child_count(pdev->dev.of_node)) + return 0; + irq = platform_get_irq(pdev, 0); if (irq < 0) return -EINVAL; diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c index 0d0f8376bc351..d7e92fd552e40 100644 --- a/drivers/pwm/pwm-atmel-tcb.c +++ b/drivers/pwm/pwm-atmel-tcb.c @@ -17,7 +17,7 @@ #include <linux/ioport.h> #include <linux/io.h> #include <linux/platform_device.h> -#include <linux/atmel_tc.h> +#include <soc/at91/atmel_tcb.h> #include <linux/pwm.h> #include <linux/of_device.h> #include <linux/slab.h> diff --git a/include/linux/atmel_tc.h b/include/linux/atmel_tc.h deleted file mode 100644 index 468fdfa643f0d..0000000000000 --- a/include/linux/atmel_tc.h +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Timer/Counter Unit (TC) registers. - * - * 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. - */ - -#ifndef ATMEL_TC_H -#define ATMEL_TC_H - -#include <linux/compiler.h> -#include <linux/list.h> - -/* - * Many 32-bit Atmel SOCs include one or more TC blocks, each of which holds - * three general-purpose 16-bit timers. These timers share one register bank. - * Depending on the SOC, each timer may have its own clock and IRQ, or those - * may be shared by the whole TC block. - * - * These TC blocks may have up to nine external pins: TCLK0..2 signals for - * clocks or clock gates, and per-timer TIOA and TIOB signals used for PWM - * or triggering. Those pins need to be set up for use with the TC block, - * else they will be used as GPIOs or for a different controller. - * - * Although we expect each TC block to have a platform_device node, those - * nodes are not what drivers bind to. Instead, they ask for a specific - * TC block, by number ... which is a common approach on systems with many - * timers. Then they use clk_get() and platform_get_irq() to get clock and - * IRQ resources. - */ - -struct clk; - -/** - * struct atmel_tcb_config - SoC data for a Timer/Counter Block - * @counter_width: size in bits of a timer counter register - */ -struct atmel_tcb_config { - size_t counter_width; -}; - -/** - * struct atmel_tc - information about a Timer/Counter Block - * @pdev: physical device - * @regs: mapping through which the I/O registers can be accessed - * @id: block id - * @tcb_config: configuration data from SoC - * @irq: irq for each of the three channels - * @clk: internal clock source for each of the three channels - * @node: list node, for tclib internal use - * @allocated: if already used, for tclib internal use - * - * On some platforms, each TC channel has its own clocks and IRQs, - * while on others, all TC channels share the same clock and IRQ. - * Drivers should clk_enable() all the clocks they need even though - * all the entries in @clk may point to the same physical clock. - * Likewise, drivers should request irqs independently for each - * channel, but they must use IRQF_SHARED in case some of the entries - * in @irq are actually the same IRQ. - */ -struct atmel_tc { - struct platform_device *pdev; - void __iomem *regs; - int id; - const struct atmel_tcb_config *tcb_config; - int irq[3]; - struct clk *clk[3]; - struct clk *slow_clk; - struct list_head node; - bool allocated; -}; - -extern struct atmel_tc *atmel_tc_alloc(unsigned block); -extern void atmel_tc_free(struct atmel_tc *tc); - -/* platform-specific ATMEL_TC_TIMER_CLOCKx divisors (0 means 32KiHz) */ -extern const u8 atmel_tc_divisors[5]; - - -/* - * Two registers have block-wide controls. These are: configuring the three - * "external" clocks (or event sources) used by the timer channels; and - * synchronizing the timers by resetting them all at once. - * - * "External" can mean "external to chip" using the TCLK0, TCLK1, or TCLK2 - * signals. Or, it can mean "external to timer", using the TIOA output from - * one of the other two timers that's being run in waveform mode. - */ - -#define ATMEL_TC_BCR 0xc0 /* TC Block Control Register */ -#define ATMEL_TC_SYNC (1 << 0) /* synchronize timers */ - -#define ATMEL_TC_BMR 0xc4 /* TC Block Mode Register */ -#define ATMEL_TC_TC0XC0S (3 << 0) /* external clock 0 source */ -#define ATMEL_TC_TC0XC0S_TCLK0 (0 << 0) -#define ATMEL_TC_TC0XC0S_NONE (1 << 0) -#define ATMEL_TC_TC0XC0S_TIOA1 (2 << 0) -#define ATMEL_TC_TC0XC0S_TIOA2 (3 << 0) -#define ATMEL_TC_TC1XC1S (3 << 2) /* external clock 1 source */ -#define ATMEL_TC_TC1XC1S_TCLK1 (0 << 2) -#define ATMEL_TC_TC1XC1S_NONE (1 << 2) -#define ATMEL_TC_TC1XC1S_TIOA0 (2 << 2) -#define ATMEL_TC_TC1XC1S_TIOA2 (3 << 2) -#define ATMEL_TC_TC2XC2S (3 << 4) /* external clock 2 source */ -#define ATMEL_TC_TC2XC2S_TCLK2 (0 << 4) -#define ATMEL_TC_TC2XC2S_NONE (1 << 4) -#define ATMEL_TC_TC2XC2S_TIOA0 (2 << 4) -#define ATMEL_TC_TC2XC2S_TIOA1 (3 << 4) - - -/* - * Each TC block has three "channels", each with one counter and controls. - * - * Note that the semantics of ATMEL_TC_TIMER_CLOCKx (input clock selection - * when it's not "external") is silicon-specific. AT91 platforms use one - * set of definitions; AVR32 platforms use a different set. Don't hard-wire - * such knowledge into your code, use the global "atmel_tc_divisors" ... - * where index N is the divisor for clock N+1, else zero to indicate it uses - * the 32 KiHz clock. - * - * The timers can be chained in various ways, and operated in "waveform" - * generation mode (including PWM) or "capture" mode (to time events). In - * both modes, behavior can be configured in many ways. - * - * Each timer has two I/O pins, TIOA and TIOB. Waveform mode uses TIOA as a - * PWM output, and TIOB as either another PWM or as a trigger. Capture mode - * uses them only as inputs. - */ -#define ATMEL_TC_CHAN(idx) ((idx)*0x40) -#define ATMEL_TC_REG(idx, reg) (ATMEL_TC_CHAN(idx) + ATMEL_TC_ ## reg) - -#define ATMEL_TC_CCR 0x00 /* Channel Control Register */ -#define ATMEL_TC_CLKEN (1 << 0) /* clock enable */ -#define ATMEL_TC_CLKDIS (1 << 1) /* clock disable */ -#define ATMEL_TC_SWTRG (1 << 2) /* software trigger */ - -#define ATMEL_TC_CMR 0x04 /* Channel Mode Register */ - -/* Both modes share some CMR bits */ -#define ATMEL_TC_TCCLKS (7 << 0) /* clock source */ -#define ATMEL_TC_TIMER_CLOCK1 (0 << 0) -#define ATMEL_TC_TIMER_CLOCK2 (1 << 0) -#define ATMEL_TC_TIMER_CLOCK3 (2 << 0) -#define ATMEL_TC_TIMER_CLOCK4 (3 << 0) -#define ATMEL_TC_TIMER_CLOCK5 (4 << 0) -#define ATMEL_TC_XC0 (5 << 0) -#define ATMEL_TC_XC1 (6 << 0) -#define ATMEL_TC_XC2 (7 << 0) -#define ATMEL_TC_CLKI (1 << 3) /* clock invert */ -#define ATMEL_TC_BURST (3 << 4) /* clock gating */ -#define ATMEL_TC_GATE_NONE (0 << 4) -#define ATMEL_TC_GATE_XC0 (1 << 4) -#define ATMEL_TC_GATE_XC1 (2 << 4) -#define ATMEL_TC_GATE_XC2 (3 << 4) -#define ATMEL_TC_WAVE (1 << 15) /* true = Waveform mode */ - -/* CAPTURE mode CMR bits */ -#define ATMEL_TC_LDBSTOP (1 << 6) /* counter stops on RB load */ -#define ATMEL_TC_LDBDIS (1 << 7) /* counter disable on RB load */ -#define ATMEL_TC_ETRGEDG (3 << 8) /* external trigger edge */ -#define ATMEL_TC_ETRGEDG_NONE (0 << 8) -#define ATMEL_TC_ETRGEDG_RISING (1 << 8) -#define ATMEL_TC_ETRGEDG_FALLING (2 << 8) -#define ATMEL_TC_ETRGEDG_BOTH (3 << 8) -#define ATMEL_TC_ABETRG (1 << 10) /* external trigger is TIOA? */ -#define ATMEL_TC_CPCTRG (1 << 14) /* RC compare trigger enable */ -#define ATMEL_TC_LDRA (3 << 16) /* RA loading edge (of TIOA) */ -#define ATMEL_TC_LDRA_NONE (0 << 16) -#define ATMEL_TC_LDRA_RISING (1 << 16) -#define ATMEL_TC_LDRA_FALLING (2 << 16) -#define ATMEL_TC_LDRA_BOTH (3 << 16) -#define ATMEL_TC_LDRB (3 << 18) /* RB loading edge (of TIOA) */ -#define ATMEL_TC_LDRB_NONE (0 << 18) -#define ATMEL_TC_LDRB_RISING (1 << 18) -#define ATMEL_TC_LDRB_FALLING (2 << 18) -#define ATMEL_TC_LDRB_BOTH (3 << 18) - -/* WAVEFORM mode CMR bits */ -#define ATMEL_TC_CPCSTOP (1 << 6) /* RC compare stops counter */ -#define ATMEL_TC_CPCDIS (1 << 7) /* RC compare disables counter */ -#define ATMEL_TC_EEVTEDG (3 << 8) /* external event edge */ -#define ATMEL_TC_EEVTEDG_NONE (0 << 8) -#define ATMEL_TC_EEVTEDG_RISING (1 << 8) -#define ATMEL_TC_EEVTEDG_FALLING (2 << 8) -#define ATMEL_TC_EEVTEDG_BOTH (3 << 8) -#define ATMEL_TC_EEVT (3 << 10) /* external event source */ -#define ATMEL_TC_EEVT_TIOB (0 << 10) -#define ATMEL_TC_EEVT_XC0 (1 << 10) -#define ATMEL_TC_EEVT_XC1 (2 << 10) -#define ATMEL_TC_EEVT_XC2 (3 << 10) -#define ATMEL_TC_ENETRG (1 << 12) /* external event is trigger */ -#define ATMEL_TC_WAVESEL (3 << 13) /* waveform type */ -#define ATMEL_TC_WAVESEL_UP (0 << 13) -#define ATMEL_TC_WAVESEL_UPDOWN (1 << 13) -#define ATMEL_TC_WAVESEL_UP_AUTO (2 << 13) -#define ATMEL_TC_WAVESEL_UPDOWN_AUTO (3 << 13) -#define ATMEL_TC_ACPA (3 << 16) /* RA compare changes TIOA */ -#define ATMEL_TC_ACPA_NONE (0 << 16) -#define ATMEL_TC_ACPA_SET (1 << 16) -#define ATMEL_TC_ACPA_CLEAR (2 << 16) -#define ATMEL_TC_ACPA_TOGGLE (3 << 16) -#define ATMEL_TC_ACPC (3 << 18) /* RC compare changes TIOA */ -#define ATMEL_TC_ACPC_NONE (0 << 18) -#define ATMEL_TC_ACPC_SET (1 << 18) -#define ATMEL_TC_ACPC_CLEAR (2 << 18) -#define ATMEL_TC_ACPC_TOGGLE (3 << 18) -#define ATMEL_TC_AEEVT (3 << 20) /* external event changes TIOA */ -#define ATMEL_TC_AEEVT_NONE (0 << 20) -#define ATMEL_TC_AEEVT_SET (1 << 20) -#define ATMEL_TC_AEEVT_CLEAR (2 << 20) -#define ATMEL_TC_AEEVT_TOGGLE (3 << 20) -#define ATMEL_TC_ASWTRG (3 << 22) /* software trigger changes TIOA */ -#define ATMEL_TC_ASWTRG_NONE (0 << 22) -#define ATMEL_TC_ASWTRG_SET (1 << 22) -#define ATMEL_TC_ASWTRG_CLEAR (2 << 22) -#define ATMEL_TC_ASWTRG_TOGGLE (3 << 22) -#define ATMEL_TC_BCPB (3 << 24) /* RB compare changes TIOB */ -#define ATMEL_TC_BCPB_NONE (0 << 24) -#define ATMEL_TC_BCPB_SET (1 << 24) -#define ATMEL_TC_BCPB_CLEAR (2 << 24) -#define ATMEL_TC_BCPB_TOGGLE (3 << 24) -#define ATMEL_TC_BCPC (3 << 26) /* RC compare changes TIOB */ -#define ATMEL_TC_BCPC_NONE (0 << 26) -#define ATMEL_TC_BCPC_SET (1 << 26) -#define ATMEL_TC_BCPC_CLEAR (2 << 26) -#define ATMEL_TC_BCPC_TOGGLE (3 << 26) -#define ATMEL_TC_BEEVT (3 << 28) /* external event changes TIOB */ -#define ATMEL_TC_BEEVT_NONE (0 << 28) -#define ATMEL_TC_BEEVT_SET (1 << 28) -#define ATMEL_TC_BEEVT_CLEAR (2 << 28) -#define ATMEL_TC_BEEVT_TOGGLE (3 << 28) -#define ATMEL_TC_BSWTRG (3 << 30) /* software trigger changes TIOB */ -#define ATMEL_TC_BSWTRG_NONE (0 << 30) -#define ATMEL_TC_BSWTRG_SET (1 << 30) -#define ATMEL_TC_BSWTRG_CLEAR (2 << 30) -#define ATMEL_TC_BSWTRG_TOGGLE (3 << 30) - -#define ATMEL_TC_CV 0x10 /* counter Value */ -#define ATMEL_TC_RA 0x14 /* register A */ -#define ATMEL_TC_RB 0x18 /* register B */ -#define ATMEL_TC_RC 0x1c /* register C */ - -#define ATMEL_TC_SR 0x20 /* status (read-only) */ -/* Status-only flags */ -#define ATMEL_TC_CLKSTA (1 << 16) /* clock enabled */ -#define ATMEL_TC_MTIOA (1 << 17) /* TIOA mirror */ -#define ATMEL_TC_MTIOB (1 << 18) /* TIOB mirror */ - -#define ATMEL_TC_IER 0x24 /* interrupt enable (write-only) */ -#define ATMEL_TC_IDR 0x28 /* interrupt disable (write-only) */ -#define ATMEL_TC_IMR 0x2c /* interrupt mask (read-only) */ - -/* Status and IRQ flags */ -#define ATMEL_TC_COVFS (1 << 0) /* counter overflow */ -#define ATMEL_TC_LOVRS (1 << 1) /* load overrun */ -#define ATMEL_TC_CPAS (1 << 2) /* RA compare */ -#define ATMEL_TC_CPBS (1 << 3) /* RB compare */ -#define ATMEL_TC_CPCS (1 << 4) /* RC compare */ -#define ATMEL_TC_LDRAS (1 << 5) /* RA loading */ -#define ATMEL_TC_LDRBS (1 << 6) /* RB loading */ -#define ATMEL_TC_ETRGS (1 << 7) /* external trigger */ -#define ATMEL_TC_ALL_IRQ (ATMEL_TC_COVFS | ATMEL_TC_LOVRS | \ - ATMEL_TC_CPAS | ATMEL_TC_CPBS | \ - ATMEL_TC_CPCS | ATMEL_TC_LDRAS | \ - ATMEL_TC_LDRBS | ATMEL_TC_ETRGS) \ - /* all IRQs */ - -#endif diff --git a/include/soc/at91/atmel_tcb.h b/include/soc/at91/atmel_tcb.h index 657e234b14832..cb0c5f53cd46c 100644 --- a/include/soc/at91/atmel_tcb.h +++ b/include/soc/at91/atmel_tcb.h @@ -1,183 +1,289 @@ -//SPDX-License-Identifier: GPL-2.0 -/* Copyright (C) 2018 Microchip */ +/* + * Timer/Counter Unit (TC) registers. + * + * 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. + */ #ifndef __SOC_ATMEL_TCB_H #define __SOC_ATMEL_TCB_H -/* Channel registers */ -#define ATMEL_TC_COFFS(c) ((c) * 0x40) -#define ATMEL_TC_CCR(c) ATMEL_TC_COFFS(c) -#define ATMEL_TC_CMR(c) (ATMEL_TC_COFFS(c) + 0x4) -#define ATMEL_TC_SMMR(c) (ATMEL_TC_COFFS(c) + 0x8) -#define ATMEL_TC_RAB(c) (ATMEL_TC_COFFS(c) + 0xc) -#define ATMEL_TC_CV(c) (ATMEL_TC_COFFS(c) + 0x10) -#define ATMEL_TC_RA(c) (ATMEL_TC_COFFS(c) + 0x14) -#define ATMEL_TC_RB(c) (ATMEL_TC_COFFS(c) + 0x18) -#define ATMEL_TC_RC(c) (ATMEL_TC_COFFS(c) + 0x1c) -#define ATMEL_TC_SR(c) (ATMEL_TC_COFFS(c) + 0x20) -#define ATMEL_TC_IER(c) (ATMEL_TC_COFFS(c) + 0x24) -#define ATMEL_TC_IDR(c) (ATMEL_TC_COFFS(c) + 0x28) -#define ATMEL_TC_IMR(c) (ATMEL_TC_COFFS(c) + 0x2c) -#define ATMEL_TC_EMR(c) (ATMEL_TC_COFFS(c) + 0x30) +#include <linux/compiler.h> +#include <linux/list.h> -/* Block registers */ -#define ATMEL_TC_BCR 0xc0 -#define ATMEL_TC_BMR 0xc4 -#define ATMEL_TC_QIER 0xc8 -#define ATMEL_TC_QIDR 0xcc -#define ATMEL_TC_QIMR 0xd0 -#define ATMEL_TC_QISR 0xd4 -#define ATMEL_TC_FMR 0xd8 -#define ATMEL_TC_WPMR 0xe4 +/* + * Many 32-bit Atmel SOCs include one or more TC blocks, each of which holds + * three general-purpose 16-bit timers. These timers share one register bank. + * Depending on the SOC, each timer may have its own clock and IRQ, or those + * may be shared by the whole TC block. + * + * These TC blocks may have up to nine external pins: TCLK0..2 signals for + * clocks or clock gates, and per-timer TIOA and TIOB signals used for PWM + * or triggering. Those pins need to be set up for use with the TC block, + * else they will be used as GPIOs or for a different controller. + * + * Although we expect each TC block to have a platform_device node, those + * nodes are not what drivers bind to. Instead, they ask for a specific + * TC block, by number ... which is a common approach on systems with many + * timers. Then they use clk_get() and platform_get_irq() to get clock and + * IRQ resources. + */ -/* CCR fields */ -#define ATMEL_TC_CCR_CLKEN BIT(0) -#define ATMEL_TC_CCR_CLKDIS BIT(1) -#define ATMEL_TC_CCR_SWTRG BIT(2) +struct clk; -/* Common CMR fields */ -#define ATMEL_TC_CMR_TCLKS_MSK GENMASK(2, 0) -#define ATMEL_TC_CMR_TCLK(x) (x) -#define ATMEL_TC_CMR_XC(x) ((x) + 5) -#define ATMEL_TC_CMR_CLKI BIT(3) -#define ATMEL_TC_CMR_BURST_MSK GENMASK(5, 4) -#define ATMEL_TC_CMR_BURST_XC(x) (((x) + 1) << 4) -#define ATMEL_TC_CMR_WAVE BIT(15) +/** + * struct atmel_tcb_config - SoC data for a Timer/Counter Block + * @counter_width: size in bits of a timer counter register + */ +struct atmel_tcb_config { + size_t counter_width; +}; -/* Capture mode CMR fields */ -#define ATMEL_TC_CMR_LDBSTOP BIT(6) -#define ATMEL_TC_CMR_LDBDIS BIT(7) -#define ATMEL_TC_CMR_ETRGEDG_MSK GENMASK(9, 8) -#define ATMEL_TC_CMR_ETRGEDG_NONE (0 << 8) -#define ATMEL_TC_CMR_ETRGEDG_RISING (1 << 8) -#define ATMEL_TC_CMR_ETRGEDG_FALLING (2 << 8) -#define ATMEL_TC_CMR_ETRGEDG_BOTH (3 << 8) -#define ATMEL_TC_CMR_ABETRG BIT(10) -#define ATMEL_TC_CMR_CPCTRG BIT(14) -#define ATMEL_TC_CMR_LDRA_MSK GENMASK(17, 16) -#define ATMEL_TC_CMR_LDRA_NONE (0 << 16) -#define ATMEL_TC_CMR_LDRA_RISING (1 << 16) -#define ATMEL_TC_CMR_LDRA_FALLING (2 << 16) -#define ATMEL_TC_CMR_LDRA_BOTH (3 << 16) -#define ATMEL_TC_CMR_LDRB_MSK GENMASK(19, 18) -#define ATMEL_TC_CMR_LDRB_NONE (0 << 18) -#define ATMEL_TC_CMR_LDRB_RISING (1 << 18) -#define ATMEL_TC_CMR_LDRB_FALLING (2 << 18) -#define ATMEL_TC_CMR_LDRB_BOTH (3 << 18) -#define ATMEL_TC_CMR_SBSMPLR_MSK GENMASK(22, 20) -#define ATMEL_TC_CMR_SBSMPLR(x) ((x) << 20) +/** + * struct atmel_tc - information about a Timer/Counter Block + * @pdev: physical device + * @regs: mapping through which the I/O registers can be accessed + * @id: block id + * @tcb_config: configuration data from SoC + * @irq: irq for each of the three channels + * @clk: internal clock source for each of the three channels + * @node: list node, for tclib internal use + * @allocated: if already used, for tclib internal use + * + * On some platforms, each TC channel has its own clocks and IRQs, + * while on others, all TC channels share the same clock and IRQ. + * Drivers should clk_enable() all the clocks they need even though + * all the entries in @clk may point to the same physical clock. + * Likewise, drivers should request irqs independently for each + * channel, but they must use IRQF_SHARED in case some of the entries + * in @irq are actually the same IRQ. + */ +struct atmel_tc { + struct platform_device *pdev; + void __iomem *regs; + int id; + const struct atmel_tcb_config *tcb_config; + int irq[3]; + struct clk *clk[3]; + struct clk *slow_clk; + struct list_head node; + bool allocated; +}; -/* Waveform mode CMR fields */ -#define ATMEL_TC_CMR_CPCSTOP BIT(6) -#define ATMEL_TC_CMR_CPCDIS BIT(7) -#define ATMEL_TC_CMR_EEVTEDG_MSK GENMASK(9, 8) -#define ATMEL_TC_CMR_EEVTEDG_NONE (0 << 8) -#define ATMEL_TC_CMR_EEVTEDG_RISING (1 << 8) -#define ATMEL_TC_CMR_EEVTEDG_FALLING (2 << 8) -#define ATMEL_TC_CMR_EEVTEDG_BOTH (3 << 8) -#define ATMEL_TC_CMR_EEVT_MSK GENMASK(11, 10) -#define ATMEL_TC_CMR_EEVT_XC(x) (((x) + 1) << 10) -#define ATMEL_TC_CMR_ENETRG BIT(12) -#define ATMEL_TC_CMR_WAVESEL_MSK GENMASK(14, 13) -#define ATMEL_TC_CMR_WAVESEL_UP (0 << 13) -#define ATMEL_TC_CMR_WAVESEL_UPDOWN (1 << 13) -#define ATMEL_TC_CMR_WAVESEL_UPRC (2 << 13) -#define ATMEL_TC_CMR_WAVESEL_UPDOWNRC (3 << 13) -#define ATMEL_TC_CMR_ACPA_MSK GENMASK(17, 16) -#define ATMEL_TC_CMR_ACPA(a) (ATMEL_TC_CMR_ACTION_##a << 16) -#define ATMEL_TC_CMR_ACPC_MSK GENMASK(19, 18) -#define ATMEL_TC_CMR_ACPC(a) (ATMEL_TC_CMR_ACTION_##a << 18) -#define ATMEL_TC_CMR_AEEVT_MSK GENMASK(21, 20) -#define ATMEL_TC_CMR_AEEVT(a) (ATMEL_TC_CMR_ACTION_##a << 20) -#define ATMEL_TC_CMR_ASWTRG_MSK GENMASK(23, 22) -#define ATMEL_TC_CMR_ASWTRG(a) (ATMEL_TC_CMR_ACTION_##a << 22) -#define ATMEL_TC_CMR_BCPB_MSK GENMASK(25, 24) -#define ATMEL_TC_CMR_BCPB(a) (ATMEL_TC_CMR_ACTION_##a << 24) -#define ATMEL_TC_CMR_BCPC_MSK GENMASK(27, 26) -#define ATMEL_TC_CMR_BCPC(a) (ATMEL_TC_CMR_ACTION_##a << 26) -#define ATMEL_TC_CMR_BEEVT_MSK GENMASK(29, 28) -#define ATMEL_TC_CMR_BEEVT(a) (ATMEL_TC_CMR_ACTION_##a << 28) -#define ATMEL_TC_CMR_BSWTRG_MSK GENMASK(31, 30) -#define ATMEL_TC_CMR_BSWTRG(a) (ATMEL_TC_CMR_ACTION_##a << 30) -#define ATMEL_TC_CMR_ACTION_NONE 0 -#define ATMEL_TC_CMR_ACTION_SET 1 -#define ATMEL_TC_CMR_ACTION_CLEAR 2 -#define ATMEL_TC_CMR_ACTION_TOGGLE 3 +extern struct atmel_tc *atmel_tc_alloc(unsigned block); +extern void atmel_tc_free(struct atmel_tc *tc); -/* SMMR fields */ -#define ATMEL_TC_SMMR_GCEN BIT(0) -#define ATMEL_TC_SMMR_DOWN BIT(1) +/* platform-specific ATMEL_TC_TIMER_CLOCKx divisors (0 means 32KiHz) */ +static const u8 atmel_tc_divisors[] = { 2, 8, 32, 128, 0, }; -/* SR/IER/IDR/IMR fields */ -#define ATMEL_TC_COVFS BIT(0) -#define ATMEL_TC_LOVRS BIT(1) -#define ATMEL_TC_CPAS BIT(2) -#define ATMEL_TC_CPBS BIT(3) -#define ATMEL_TC_CPCS BIT(4) -#define ATMEL_TC_LDRAS BIT(5) -#define ATMEL_TC_LDRBS BIT(6) -#define ATMEL_TC_ETRGS BIT(7) -#define ATMEL_TC_CLKSTA BIT(16) -#define ATMEL_TC_MTIOA BIT(17) -#define ATMEL_TC_MTIOB BIT(18) +static const struct atmel_tcb_config tcb_rm9200_config = { + .counter_width = 16, +}; -/* EMR fields */ -#define ATMEL_TC_EMR_TRIGSRCA_MSK GENMASK(1, 0) -#define ATMEL_TC_EMR_TRIGSRCA_TIOA 0 -#define ATMEL_TC_EMR_TRIGSRCA_PWMX 1 -#define ATMEL_TC_EMR_TRIGSRCB_MSK GENMASK(5, 4) -#define ATMEL_TC_EMR_TRIGSRCB_TIOB (0 << 4) -#define ATMEL_TC_EMR_TRIGSRCB_PWM (1 << 4) -#define ATMEL_TC_EMR_NOCLKDIV BIT(8) - -/* BCR fields */ -#define ATMEL_TC_BCR_SYNC BIT(0) - -/* BMR fields */ -#define ATMEL_TC_BMR_TCXC_MSK(c) GENMASK(((c) * 2) + 1, (c) * 2) -#define ATMEL_TC_BMR_TCXC(x, c) ((x) << (2 * (c))) -#define ATMEL_TC_BMR_QDEN BIT(8) -#define ATMEL_TC_BMR_POSEN BIT(9) -#define ATMEL_TC_BMR_SPEEDEN BIT(10) -#define ATMEL_TC_BMR_QDTRANS BIT(11) -#define ATMEL_TC_BMR_EDGPHA BIT(12) -#define ATMEL_TC_BMR_INVA BIT(13) -#define ATMEL_TC_BMR_INVB BIT(14) -#define ATMEL_TC_BMR_INVIDX BIT(15) -#define ATMEL_TC_BMR_SWAP BIT(16) -#define ATMEL_TC_BMR_IDXPHB BIT(17) -#define ATMEL_TC_BMR_AUTOC BIT(18) -#define ATMEL_TC_MAXFILT_MSK GENMASK(25, 20) -#define ATMEL_TC_MAXFILT(x) (((x) - 1) << 20) -#define ATMEL_TC_MAXCMP_MSK GENMASK(29, 26) -#define ATMEL_TC_MAXCMP(x) ((x) << 26) - -/* QEDC fields */ -#define ATMEL_TC_QEDC_IDX BIT(0) -#define ATMEL_TC_QEDC_DIRCHG BIT(1) -#define ATMEL_TC_QEDC_QERR BIT(2) -#define ATMEL_TC_QEDC_MPE BIT(3) -#define ATMEL_TC_QEDC_DIR BIT(8) - -/* FMR fields */ -#define ATMEL_TC_FMR_ENCF(x) BIT(x) - -/* WPMR fields */ -#define ATMEL_TC_WPMR_WPKEY (0x54494d << 8) -#define ATMEL_TC_WPMR_WPEN BIT(0) - -static const u8 atmel_tc_divisors[5] = { 2, 8, 32, 128, 0, }; +static const struct atmel_tcb_config tcb_sam9x5_config = { + .counter_width = 32, +}; static const struct of_device_id atmel_tcb_dt_ids[] = { { .compatible = "atmel,at91rm9200-tcb", - .data = (void *)16, + .data = &tcb_rm9200_config, }, { .compatible = "atmel,at91sam9x5-tcb", - .data = (void *)32, + .data = &tcb_sam9x5_config, }, { /* sentinel */ } }; -#endif /* __SOC_ATMEL_TCB_H */ +/* + * Two registers have block-wide controls. These are: configuring the three + * "external" clocks (or event sources) used by the timer channels; and + * synchronizing the timers by resetting them all at once. + * + * "External" can mean "external to chip" using the TCLK0, TCLK1, or TCLK2 + * signals. Or, it can mean "external to timer", using the TIOA output from + * one of the other two timers that's being run in waveform mode. + */ + +#define ATMEL_TC_BCR 0xc0 /* TC Block Control Register */ +#define ATMEL_TC_SYNC (1 << 0) /* synchronize timers */ + +#define ATMEL_TC_BMR 0xc4 /* TC Block Mode Register */ +#define ATMEL_TC_TC0XC0S (3 << 0) /* external clock 0 source */ +#define ATMEL_TC_TC0XC0S_TCLK0 (0 << 0) +#define ATMEL_TC_TC0XC0S_NONE (1 << 0) +#define ATMEL_TC_TC0XC0S_TIOA1 (2 << 0) +#define ATMEL_TC_TC0XC0S_TIOA2 (3 << 0) +#define ATMEL_TC_TC1XC1S (3 << 2) /* external clock 1 source */ +#define ATMEL_TC_TC1XC1S_TCLK1 (0 << 2) +#define ATMEL_TC_TC1XC1S_NONE (1 << 2) +#define ATMEL_TC_TC1XC1S_TIOA0 (2 << 2) +#define ATMEL_TC_TC1XC1S_TIOA2 (3 << 2) +#define ATMEL_TC_TC2XC2S (3 << 4) /* external clock 2 source */ +#define ATMEL_TC_TC2XC2S_TCLK2 (0 << 4) +#define ATMEL_TC_TC2XC2S_NONE (1 << 4) +#define ATMEL_TC_TC2XC2S_TIOA0 (2 << 4) +#define ATMEL_TC_TC2XC2S_TIOA1 (3 << 4) + + +/* + * Each TC block has three "channels", each with one counter and controls. + * + * Note that the semantics of ATMEL_TC_TIMER_CLOCKx (input clock selection + * when it's not "external") is silicon-specific. AT91 platforms use one + * set of definitions; AVR32 platforms use a different set. Don't hard-wire + * such knowledge into your code, use the global "atmel_tc_divisors" ... + * where index N is the divisor for clock N+1, else zero to indicate it uses + * the 32 KiHz clock. + * + * The timers can be chained in various ways, and operated in "waveform" + * generation mode (including PWM) or "capture" mode (to time events). In + * both modes, behavior can be configured in many ways. + * + * Each timer has two I/O pins, TIOA and TIOB. Waveform mode uses TIOA as a + * PWM output, and TIOB as either another PWM or as a trigger. Capture mode + * uses them only as inputs. + */ +#define ATMEL_TC_CHAN(idx) ((idx)*0x40) +#define ATMEL_TC_REG(idx, reg) (ATMEL_TC_CHAN(idx) + ATMEL_TC_ ## reg) + +#define ATMEL_TC_CCR 0x00 /* Channel Control Register */ +#define ATMEL_TC_CLKEN (1 << 0) /* clock enable */ +#define ATMEL_TC_CLKDIS (1 << 1) /* clock disable */ +#define ATMEL_TC_SWTRG (1 << 2) /* software trigger */ + +#define ATMEL_TC_CMR 0x04 /* Channel Mode Register */ + +/* Both modes share some CMR bits */ +#define ATMEL_TC_TCCLKS (7 << 0) /* clock source */ +#define ATMEL_TC_TIMER_CLOCK1 (0 << 0) +#define ATMEL_TC_TIMER_CLOCK2 (1 << 0) +#define ATMEL_TC_TIMER_CLOCK3 (2 << 0) +#define ATMEL_TC_TIMER_CLOCK4 (3 << 0) +#define ATMEL_TC_TIMER_CLOCK5 (4 << 0) +#define ATMEL_TC_XC0 (5 << 0) +#define ATMEL_TC_XC1 (6 << 0) +#define ATMEL_TC_XC2 (7 << 0) +#define ATMEL_TC_CLKI (1 << 3) /* clock invert */ +#define ATMEL_TC_BURST (3 << 4) /* clock gating */ +#define ATMEL_TC_GATE_NONE (0 << 4) +#define ATMEL_TC_GATE_XC0 (1 << 4) +#define ATMEL_TC_GATE_XC1 (2 << 4) +#define ATMEL_TC_GATE_XC2 (3 << 4) +#define ATMEL_TC_WAVE (1 << 15) /* true = Waveform mode */ + +/* CAPTURE mode CMR bits */ +#define ATMEL_TC_LDBSTOP (1 << 6) /* counter stops on RB load */ +#define ATMEL_TC_LDBDIS (1 << 7) /* counter disable on RB load */ +#define ATMEL_TC_ETRGEDG (3 << 8) /* external trigger edge */ +#define ATMEL_TC_ETRGEDG_NONE (0 << 8) +#define ATMEL_TC_ETRGEDG_RISING (1 << 8) +#define ATMEL_TC_ETRGEDG_FALLING (2 << 8) +#define ATMEL_TC_ETRGEDG_BOTH (3 << 8) +#define ATMEL_TC_ABETRG (1 << 10) /* external trigger is TIOA? */ +#define ATMEL_TC_CPCTRG (1 << 14) /* RC compare trigger enable */ +#define ATMEL_TC_LDRA (3 << 16) /* RA loading edge (of TIOA) */ +#define ATMEL_TC_LDRA_NONE (0 << 16) +#define ATMEL_TC_LDRA_RISING (1 << 16) +#define ATMEL_TC_LDRA_FALLING (2 << 16) +#define ATMEL_TC_LDRA_BOTH (3 << 16) +#define ATMEL_TC_LDRB (3 << 18) /* RB loading edge (of TIOA) */ +#define ATMEL_TC_LDRB_NONE (0 << 18) +#define ATMEL_TC_LDRB_RISING (1 << 18) +#define ATMEL_TC_LDRB_FALLING (2 << 18) +#define ATMEL_TC_LDRB_BOTH (3 << 18) + +/* WAVEFORM mode CMR bits */ +#define ATMEL_TC_CPCSTOP (1 << 6) /* RC compare stops counter */ +#define ATMEL_TC_CPCDIS (1 << 7) /* RC compare disables counter */ +#define ATMEL_TC_EEVTEDG (3 << 8) /* external event edge */ +#define ATMEL_TC_EEVTEDG_NONE (0 << 8) +#define ATMEL_TC_EEVTEDG_RISING (1 << 8) +#define ATMEL_TC_EEVTEDG_FALLING (2 << 8) +#define ATMEL_TC_EEVTEDG_BOTH (3 << 8) +#define ATMEL_TC_EEVT (3 << 10) /* external event source */ +#define ATMEL_TC_EEVT_TIOB (0 << 10) +#define ATMEL_TC_EEVT_XC0 (1 << 10) +#define ATMEL_TC_EEVT_XC1 (2 << 10) +#define ATMEL_TC_EEVT_XC2 (3 << 10) +#define ATMEL_TC_ENETRG (1 << 12) /* external event is trigger */ +#define ATMEL_TC_WAVESEL (3 << 13) /* waveform type */ +#define ATMEL_TC_WAVESEL_UP (0 << 13) +#define ATMEL_TC_WAVESEL_UPDOWN (1 << 13) +#define ATMEL_TC_WAVESEL_UP_AUTO (2 << 13) +#define ATMEL_TC_WAVESEL_UPDOWN_AUTO (3 << 13) +#define ATMEL_TC_ACPA (3 << 16) /* RA compare changes TIOA */ +#define ATMEL_TC_ACPA_NONE (0 << 16) +#define ATMEL_TC_ACPA_SET (1 << 16) +#define ATMEL_TC_ACPA_CLEAR (2 << 16) +#define ATMEL_TC_ACPA_TOGGLE (3 << 16) +#define ATMEL_TC_ACPC (3 << 18) /* RC compare changes TIOA */ +#define ATMEL_TC_ACPC_NONE (0 << 18) +#define ATMEL_TC_ACPC_SET (1 << 18) +#define ATMEL_TC_ACPC_CLEAR (2 << 18) +#define ATMEL_TC_ACPC_TOGGLE (3 << 18) +#define ATMEL_TC_AEEVT (3 << 20) /* external event changes TIOA */ +#define ATMEL_TC_AEEVT_NONE (0 << 20) +#define ATMEL_TC_AEEVT_SET (1 << 20) +#define ATMEL_TC_AEEVT_CLEAR (2 << 20) +#define ATMEL_TC_AEEVT_TOGGLE (3 << 20) +#define ATMEL_TC_ASWTRG (3 << 22) /* software trigger changes TIOA */ +#define ATMEL_TC_ASWTRG_NONE (0 << 22) +#define ATMEL_TC_ASWTRG_SET (1 << 22) +#define ATMEL_TC_ASWTRG_CLEAR (2 << 22) +#define ATMEL_TC_ASWTRG_TOGGLE (3 << 22) +#define ATMEL_TC_BCPB (3 << 24) /* RB compare changes TIOB */ +#define ATMEL_TC_BCPB_NONE (0 << 24) +#define ATMEL_TC_BCPB_SET (1 << 24) +#define ATMEL_TC_BCPB_CLEAR (2 << 24) +#define ATMEL_TC_BCPB_TOGGLE (3 << 24) +#define ATMEL_TC_BCPC (3 << 26) /* RC compare changes TIOB */ +#define ATMEL_TC_BCPC_NONE (0 << 26) +#define ATMEL_TC_BCPC_SET (1 << 26) +#define ATMEL_TC_BCPC_CLEAR (2 << 26) +#define ATMEL_TC_BCPC_TOGGLE (3 << 26) +#define ATMEL_TC_BEEVT (3 << 28) /* external event changes TIOB */ +#define ATMEL_TC_BEEVT_NONE (0 << 28) +#define ATMEL_TC_BEEVT_SET (1 << 28) +#define ATMEL_TC_BEEVT_CLEAR (2 << 28) +#define ATMEL_TC_BEEVT_TOGGLE (3 << 28) +#define ATMEL_TC_BSWTRG (3 << 30) /* software trigger changes TIOB */ +#define ATMEL_TC_BSWTRG_NONE (0 << 30) +#define ATMEL_TC_BSWTRG_SET (1 << 30) +#define ATMEL_TC_BSWTRG_CLEAR (2 << 30) +#define ATMEL_TC_BSWTRG_TOGGLE (3 << 30) + +#define ATMEL_TC_CV 0x10 /* counter Value */ +#define ATMEL_TC_RA 0x14 /* register A */ +#define ATMEL_TC_RB 0x18 /* register B */ +#define ATMEL_TC_RC 0x1c /* register C */ + +#define ATMEL_TC_SR 0x20 /* status (read-only) */ +/* Status-only flags */ +#define ATMEL_TC_CLKSTA (1 << 16) /* clock enabled */ +#define ATMEL_TC_MTIOA (1 << 17) /* TIOA mirror */ +#define ATMEL_TC_MTIOB (1 << 18) /* TIOB mirror */ + +#define ATMEL_TC_IER 0x24 /* interrupt enable (write-only) */ +#define ATMEL_TC_IDR 0x28 /* interrupt disable (write-only) */ +#define ATMEL_TC_IMR 0x2c /* interrupt mask (read-only) */ + +/* Status and IRQ flags */ +#define ATMEL_TC_COVFS (1 << 0) /* counter overflow */ +#define ATMEL_TC_LOVRS (1 << 1) /* load overrun */ +#define ATMEL_TC_CPAS (1 << 2) /* RA compare */ +#define ATMEL_TC_CPBS (1 << 3) /* RB compare */ +#define ATMEL_TC_CPCS (1 << 4) /* RC compare */ +#define ATMEL_TC_LDRAS (1 << 5) /* RA loading */ +#define ATMEL_TC_LDRBS (1 << 6) /* RB loading */ +#define ATMEL_TC_ETRGS (1 << 7) /* external trigger */ +#define ATMEL_TC_ALL_IRQ (ATMEL_TC_COVFS | ATMEL_TC_LOVRS | \ + ATMEL_TC_CPAS | ATMEL_TC_CPBS | \ + ATMEL_TC_CPCS | ATMEL_TC_LDRAS | \ + ATMEL_TC_LDRBS | ATMEL_TC_ETRGS) \ + /* all IRQs */ + +#endif diff --git a/localversion-rt b/localversion-rt index ad3da1bcab7e8..0efe7ba1930e1 100644 --- a/localversion-rt +++ b/localversion-rt @@ -1 +1 @@ --rt4 +-rt5