* [ANNOUNCE] v5.0.7-rt5
@ 2019-04-12 21:23 Sebastian Andrzej Siewior
0 siblings, 0 replies; only message in thread
From: Sebastian Andrzej Siewior @ 2019-04-12 21:23 UTC (permalink / raw)
To: Thomas Gleixner; +Cc: LKML, linux-rt-users, Steven Rostedt
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
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2019-04-12 21:23 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-04-12 21:23 [ANNOUNCE] v5.0.7-rt5 Sebastian Andrzej Siewior
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.