[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20190412212313.blex53rgtd3qyg77@linutronix.de>
Date: Fri, 12 Apr 2019 23:23:13 +0200
From: Sebastian Andrzej Siewior <bigeasy@...utronix.de>
To: Thomas Gleixner <tglx@...utronix.de>
Cc: LKML <linux-kernel@...r.kernel.org>,
linux-rt-users <linux-rt-users@...r.kernel.org>,
Steven Rostedt <rostedt@...dmis.org>
Subject: [ANNOUNCE] v5.0.7-rt5
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
Powered by blists - more mailing lists