* [PATCH v3 07/11] ARM: KVM: fix handling of trapped 64bit coprocessor accesses
From: Marc Zyngier @ 2014-02-05 19:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1391630151-7875-1-git-send-email-marc.zyngier@arm.com>
Commit 240e99cbd00a (ARM: KVM: Fix 64-bit coprocessor handling)
changed the way we match the 64bit coprocessor access from
user space, but didn't update the trap handler for the same
set of registers.
The effect is that a trapped 64bit access is never matched, leading
to a fault being injected into the guest. This went unnoticed as we
didn't really trap any 64bit register so far.
Placing the CRm field of the access into the CRn field of the matching
structure fixes the problem. Also update the debug feature to emit the
expected string in case of failing match.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
---
arch/arm/kvm/coproc.c | 4 ++--
arch/arm/kvm/coproc.h | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/arch/arm/kvm/coproc.c b/arch/arm/kvm/coproc.c
index 78c0885..126c90d 100644
--- a/arch/arm/kvm/coproc.c
+++ b/arch/arm/kvm/coproc.c
@@ -443,7 +443,7 @@ int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run)
{
struct coproc_params params;
- params.CRm = (kvm_vcpu_get_hsr(vcpu) >> 1) & 0xf;
+ params.CRn = (kvm_vcpu_get_hsr(vcpu) >> 1) & 0xf;
params.Rt1 = (kvm_vcpu_get_hsr(vcpu) >> 5) & 0xf;
params.is_write = ((kvm_vcpu_get_hsr(vcpu) & 1) == 0);
params.is_64bit = true;
@@ -451,7 +451,7 @@ int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run)
params.Op1 = (kvm_vcpu_get_hsr(vcpu) >> 16) & 0xf;
params.Op2 = 0;
params.Rt2 = (kvm_vcpu_get_hsr(vcpu) >> 10) & 0xf;
- params.CRn = 0;
+ params.CRm = 0;
return emulate_cp15(vcpu, ¶ms);
}
diff --git a/arch/arm/kvm/coproc.h b/arch/arm/kvm/coproc.h
index 0461d5c..c5ad7ff 100644
--- a/arch/arm/kvm/coproc.h
+++ b/arch/arm/kvm/coproc.h
@@ -58,8 +58,8 @@ static inline void print_cp_instr(const struct coproc_params *p)
{
/* Look, we even formatted it for you to paste into the table! */
if (p->is_64bit) {
- kvm_pr_unimpl(" { CRm(%2lu), Op1(%2lu), is64, func_%s },\n",
- p->CRm, p->Op1, p->is_write ? "write" : "read");
+ kvm_pr_unimpl(" { CRm64(%2lu), Op1(%2lu), is64, func_%s },\n",
+ p->CRn, p->Op1, p->is_write ? "write" : "read");
} else {
kvm_pr_unimpl(" { CRn(%2lu), CRm(%2lu), Op1(%2lu), Op2(%2lu), is32,"
" func_%s },\n",
--
1.8.3.4
^ permalink raw reply related
* [PATCH v3 06/11] ARM: KVM: force cache clean on page fault when caches are off
From: Marc Zyngier @ 2014-02-05 19:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1391630151-7875-1-git-send-email-marc.zyngier@arm.com>
In order for a guest with caches disabled to observe data written
contained in a given page, we need to make sure that page is
committed to memory, and not just hanging in the cache (as guest
accesses are completely bypassing the cache until it decides to
enable it).
For this purpose, hook into the coherent_cache_guest_page
function and flush the region if the guest SCTLR
register doesn't show the MMU and caches as being enabled.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
---
arch/arm/include/asm/kvm_mmu.h | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/arch/arm/include/asm/kvm_mmu.h b/arch/arm/include/asm/kvm_mmu.h
index 0931cda..b62ca91 100644
--- a/arch/arm/include/asm/kvm_mmu.h
+++ b/arch/arm/include/asm/kvm_mmu.h
@@ -122,9 +122,19 @@ static inline void kvm_set_s2pmd_writable(pmd_t *pmd)
struct kvm;
+#define kvm_flush_dcache_to_poc(a,l) __cpuc_flush_dcache_area((a), (l))
+
+static inline bool vcpu_has_cache_enabled(struct kvm_vcpu *vcpu)
+{
+ return (vcpu->arch.cp15[c1_SCTLR] & 0b101) == 0b101;
+}
+
static inline void coherent_cache_guest_page(struct kvm_vcpu *vcpu, hva_t hva,
unsigned long size)
{
+ if (!vcpu_has_cache_enabled(vcpu))
+ kvm_flush_dcache_to_poc((void *)hva, size);
+
/*
* If we are going to insert an instruction page and the icache is
* either VIPT or PIPT, there is a potential problem where the host
@@ -145,7 +155,6 @@ static inline void coherent_cache_guest_page(struct kvm_vcpu *vcpu, hva_t hva,
}
}
-#define kvm_flush_dcache_to_poc(a,l) __cpuc_flush_dcache_area((a), (l))
#define kvm_virt_to_phys(x) virt_to_idmap((unsigned long)(x))
void stage2_flush_vm(struct kvm *kvm);
--
1.8.3.4
^ permalink raw reply related
* [PATCH v3 05/11] ARM: LPAE: provide an IPA capable pmd_addr_end
From: Marc Zyngier @ 2014-02-05 19:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1391630151-7875-1-git-send-email-marc.zyngier@arm.com>
The default pmd_addr_end macro uses an unsigned long to represent
the VA. When used with KVM and stage-2 translation, the VA is
actually an IPA, which is up to 40 bits. This also affect the
SMMU driver, which also deals with stage-2 translation.
Instead, provide an implementation that can cope with larger VAs
by using a u64 instead. This version will overload the default
one provided in include/asm-generic/pgtable.h.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
arch/arm/include/asm/pgtable-3level.h | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm/include/asm/pgtable-3level.h b/arch/arm/include/asm/pgtable-3level.h
index 03243f7..594867b 100644
--- a/arch/arm/include/asm/pgtable-3level.h
+++ b/arch/arm/include/asm/pgtable-3level.h
@@ -262,6 +262,11 @@ static inline int has_transparent_hugepage(void)
return 1;
}
+#define pmd_addr_end(addr, end) \
+({ u64 __boundary = ((addr) + PMD_SIZE) & PMD_MASK; \
+ (__boundary - 1 < (end) - 1)? __boundary: (end); \
+})
+
#endif /* __ASSEMBLY__ */
#endif /* _ASM_PGTABLE_3LEVEL_H */
--
1.8.3.4
^ permalink raw reply related
* [PATCH v3 04/11] arm64: KVM: flush VM pages before letting the guest enable caches
From: Marc Zyngier @ 2014-02-05 19:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1391630151-7875-1-git-send-email-marc.zyngier@arm.com>
When the guest runs with caches disabled (like in an early boot
sequence, for example), all the writes are diectly going to RAM,
bypassing the caches altogether.
Once the MMU and caches are enabled, whatever sits in the cache
becomes suddenly visible, which isn't what the guest expects.
A way to avoid this potential disaster is to invalidate the cache
when the MMU is being turned on. For this, we hook into the SCTLR_EL1
trapping code, and scan the stage-2 page tables, invalidating the
pages/sections that have already been mapped in.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
---
arch/arm/include/asm/kvm_mmu.h | 8 ++++
arch/arm/kvm/mmu.c | 93 ++++++++++++++++++++++++++++++++++++++++
arch/arm64/include/asm/kvm_mmu.h | 4 ++
arch/arm64/kvm/sys_regs.c | 5 ++-
4 files changed, 109 insertions(+), 1 deletion(-)
diff --git a/arch/arm/include/asm/kvm_mmu.h b/arch/arm/include/asm/kvm_mmu.h
index 6d0f3d3..0931cda 100644
--- a/arch/arm/include/asm/kvm_mmu.h
+++ b/arch/arm/include/asm/kvm_mmu.h
@@ -114,6 +114,12 @@ static inline void kvm_set_s2pmd_writable(pmd_t *pmd)
pmd_val(*pmd) |= L_PMD_S2_RDWR;
}
+/* Open coded pgd_addr_end that can deal with 64bit addresses */
+#define kvm_pgd_addr_end(addr, end) \
+({ u64 __boundary = ((addr) + PGDIR_SIZE) & PGDIR_MASK; \
+ (__boundary - 1 < (end) - 1)? __boundary: (end); \
+})
+
struct kvm;
static inline void coherent_cache_guest_page(struct kvm_vcpu *vcpu, hva_t hva,
@@ -142,6 +148,8 @@ static inline void coherent_cache_guest_page(struct kvm_vcpu *vcpu, hva_t hva,
#define kvm_flush_dcache_to_poc(a,l) __cpuc_flush_dcache_area((a), (l))
#define kvm_virt_to_phys(x) virt_to_idmap((unsigned long)(x))
+void stage2_flush_vm(struct kvm *kvm);
+
#endif /* !__ASSEMBLY__ */
#endif /* __ARM_KVM_MMU_H__ */
diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c
index fc71a8d..ea21b6a 100644
--- a/arch/arm/kvm/mmu.c
+++ b/arch/arm/kvm/mmu.c
@@ -187,6 +187,99 @@ static void unmap_range(struct kvm *kvm, pgd_t *pgdp,
}
}
+static void stage2_flush_ptes(struct kvm *kvm, pmd_t *pmd,
+ phys_addr_t addr, phys_addr_t end)
+{
+ pte_t *pte;
+
+ pte = pte_offset_kernel(pmd, addr);
+ do {
+ if (!pte_none(*pte)) {
+ hva_t hva = gfn_to_hva(kvm, addr >> PAGE_SHIFT);
+ kvm_flush_dcache_to_poc((void*)hva, PAGE_SIZE);
+ }
+ } while (pte++, addr += PAGE_SIZE, addr != end);
+}
+
+static void stage2_flush_pmds(struct kvm *kvm, pud_t *pud,
+ phys_addr_t addr, phys_addr_t end)
+{
+ pmd_t *pmd;
+ phys_addr_t next;
+
+ pmd = pmd_offset(pud, addr);
+ do {
+ next = pmd_addr_end(addr, end);
+ if (!pmd_none(*pmd)) {
+ if (kvm_pmd_huge(*pmd)) {
+ hva_t hva = gfn_to_hva(kvm, addr >> PAGE_SHIFT);
+ kvm_flush_dcache_to_poc((void*)hva, PMD_SIZE);
+ } else {
+ stage2_flush_ptes(kvm, pmd, addr, next);
+ }
+ }
+ } while (pmd++, addr = next, addr != end);
+}
+
+static void stage2_flush_puds(struct kvm *kvm, pgd_t *pgd,
+ phys_addr_t addr, phys_addr_t end)
+{
+ pud_t *pud;
+ phys_addr_t next;
+
+ pud = pud_offset(pgd, addr);
+ do {
+ next = pud_addr_end(addr, end);
+ if (!pud_none(*pud)) {
+ if (pud_huge(*pud)) {
+ hva_t hva = gfn_to_hva(kvm, addr >> PAGE_SHIFT);
+ kvm_flush_dcache_to_poc((void*)hva, PUD_SIZE);
+ } else {
+ stage2_flush_pmds(kvm, pud, addr, next);
+ }
+ }
+ } while(pud++, addr = next, addr != end);
+}
+
+static void stage2_flush_memslot(struct kvm *kvm,
+ struct kvm_memory_slot *memslot)
+{
+ phys_addr_t addr = memslot->base_gfn << PAGE_SHIFT;
+ phys_addr_t end = addr + PAGE_SIZE * memslot->npages;
+ phys_addr_t next;
+ pgd_t *pgd;
+
+ pgd = kvm->arch.pgd + pgd_index(addr);
+ do {
+ next = kvm_pgd_addr_end(addr, end);
+ stage2_flush_puds(kvm, pgd, addr, next);
+ } while (pgd++, addr = next, addr != end);
+}
+
+/**
+ * stage2_flush_vm - Invalidate cache for pages mapped in stage 2
+ * @kvm: The struct kvm pointer
+ *
+ * Go through the stage 2 page tables and invalidate any cache lines
+ * backing memory already mapped to the VM.
+ */
+void stage2_flush_vm(struct kvm *kvm)
+{
+ struct kvm_memslots *slots;
+ struct kvm_memory_slot *memslot;
+ int idx;
+
+ idx = srcu_read_lock(&kvm->srcu);
+ spin_lock(&kvm->mmu_lock);
+
+ slots = kvm_memslots(kvm);
+ kvm_for_each_memslot(memslot, slots)
+ stage2_flush_memslot(kvm, memslot);
+
+ spin_unlock(&kvm->mmu_lock);
+ srcu_read_unlock(&kvm->srcu, idx);
+}
+
/**
* free_boot_hyp_pgd - free HYP boot page tables
*
diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h
index 6eaf69b..e78d050 100644
--- a/arch/arm64/include/asm/kvm_mmu.h
+++ b/arch/arm64/include/asm/kvm_mmu.h
@@ -121,6 +121,8 @@ static inline void kvm_set_s2pmd_writable(pmd_t *pmd)
pmd_val(*pmd) |= PMD_S2_RDWR;
}
+#define kvm_pgd_addr_end(addr, end) pgd_addr_end(addr, end)
+
struct kvm;
#define kvm_flush_dcache_to_poc(a,l) __flush_dcache_area((a), (l))
@@ -146,5 +148,7 @@ static inline void coherent_cache_guest_page(struct kvm_vcpu *vcpu, hva_t hva,
#define kvm_virt_to_phys(x) __virt_to_phys((unsigned long)(x))
+void stage2_flush_vm(struct kvm *kvm);
+
#endif /* __ASSEMBLY__ */
#endif /* __ARM64_KVM_MMU_H__ */
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 2097e5e..0324458 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -27,6 +27,7 @@
#include <asm/kvm_host.h>
#include <asm/kvm_emulate.h>
#include <asm/kvm_coproc.h>
+#include <asm/kvm_mmu.h>
#include <asm/cacheflush.h>
#include <asm/cputype.h>
#include <trace/events/kvm.h>
@@ -154,8 +155,10 @@ static bool access_sctlr(struct kvm_vcpu *vcpu,
{
access_vm_reg(vcpu, p, r);
- if (vcpu_has_cache_enabled(vcpu)) /* MMU+Caches enabled? */
+ if (vcpu_has_cache_enabled(vcpu)) { /* MMU+Caches enabled? */
vcpu->arch.hcr_el2 &= ~HCR_TVM;
+ stage2_flush_vm(vcpu->kvm);
+ }
return true;
}
--
1.8.3.4
^ permalink raw reply related
* [PATCH v3 03/11] arm64: KVM: trap VM system registers until MMU and caches are ON
From: Marc Zyngier @ 2014-02-05 19:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1391630151-7875-1-git-send-email-marc.zyngier@arm.com>
In order to be able to detect the point where the guest enables
its MMU and caches, trap all the VM related system registers.
Once we see the guest enabling both the MMU and the caches, we
can go back to a saner mode of operation, which is to leave these
registers in complete control of the guest.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
---
arch/arm64/include/asm/kvm_arm.h | 3 +-
arch/arm64/include/asm/kvm_asm.h | 3 +-
arch/arm64/kvm/sys_regs.c | 90 ++++++++++++++++++++++++++++++++++------
3 files changed, 82 insertions(+), 14 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h
index c98ef47..fd0a651 100644
--- a/arch/arm64/include/asm/kvm_arm.h
+++ b/arch/arm64/include/asm/kvm_arm.h
@@ -62,6 +62,7 @@
* RW: 64bit by default, can be overriden for 32bit VMs
* TAC: Trap ACTLR
* TSC: Trap SMC
+ * TVM: Trap VM ops (until M+C set in SCTLR_EL1)
* TSW: Trap cache operations by set/way
* TWE: Trap WFE
* TWI: Trap WFI
@@ -74,7 +75,7 @@
* SWIO: Turn set/way invalidates into set/way clean+invalidate
*/
#define HCR_GUEST_FLAGS (HCR_TSC | HCR_TSW | HCR_TWE | HCR_TWI | HCR_VM | \
- HCR_BSU_IS | HCR_FB | HCR_TAC | \
+ HCR_TVM | HCR_BSU_IS | HCR_FB | HCR_TAC | \
HCR_AMO | HCR_IMO | HCR_FMO | \
HCR_SWIO | HCR_TIDCP | HCR_RW)
#define HCR_VIRT_EXCP_MASK (HCR_VA | HCR_VI | HCR_VF)
diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h
index 3d796b4..89d7796 100644
--- a/arch/arm64/include/asm/kvm_asm.h
+++ b/arch/arm64/include/asm/kvm_asm.h
@@ -81,7 +81,8 @@
#define c13_TID_URW (TPIDR_EL0 * 2) /* Thread ID, User R/W */
#define c13_TID_URO (TPIDRRO_EL0 * 2)/* Thread ID, User R/O */
#define c13_TID_PRIV (TPIDR_EL1 * 2) /* Thread ID, Privileged */
-#define c10_AMAIR (AMAIR_EL1 * 2) /* Aux Memory Attr Indirection Reg */
+#define c10_AMAIR0 (AMAIR_EL1 * 2) /* Aux Memory Attr Indirection Reg */
+#define c10_AMAIR1 (c10_AMAIR0 + 1)/* Aux Memory Attr Indirection Reg */
#define c14_CNTKCTL (CNTKCTL_EL1 * 2) /* Timer Control Register (PL1) */
#define NR_CP15_REGS (NR_SYS_REGS * 2)
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index bf03e0f..2097e5e 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -121,6 +121,46 @@ done:
}
/*
+ * Generic accessor for VM registers. Only called as long as HCR_TVM
+ * is set.
+ */
+static bool access_vm_reg(struct kvm_vcpu *vcpu,
+ const struct sys_reg_params *p,
+ const struct sys_reg_desc *r)
+{
+ unsigned long val;
+
+ BUG_ON(!p->is_write);
+
+ val = *vcpu_reg(vcpu, p->Rt);
+ if (!p->is_aarch32) {
+ vcpu_sys_reg(vcpu, r->reg) = val;
+ } else {
+ vcpu_cp15(vcpu, r->reg) = val & 0xffffffffUL;
+ if (!p->is_32bit)
+ vcpu_cp15(vcpu, r->reg + 1) = val >> 32;
+ }
+ return true;
+}
+
+/*
+ * SCTLR_EL1 accessor. Only called as long as HCR_TVM is set. If the
+ * guest enables the MMU, we stop trapping the VM sys_regs and leave
+ * it in complete control of the caches.
+ */
+static bool access_sctlr(struct kvm_vcpu *vcpu,
+ const struct sys_reg_params *p,
+ const struct sys_reg_desc *r)
+{
+ access_vm_reg(vcpu, p, r);
+
+ if (vcpu_has_cache_enabled(vcpu)) /* MMU+Caches enabled? */
+ vcpu->arch.hcr_el2 &= ~HCR_TVM;
+
+ return true;
+}
+
+/*
* We could trap ID_DFR0 and tell the guest we don't support performance
* monitoring. Unfortunately the patch to make the kernel check ID_DFR0 was
* NAKed, so it will read the PMCR anyway.
@@ -185,32 +225,32 @@ static const struct sys_reg_desc sys_reg_descs[] = {
NULL, reset_mpidr, MPIDR_EL1 },
/* SCTLR_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b0001), CRm(0b0000), Op2(0b000),
- NULL, reset_val, SCTLR_EL1, 0x00C50078 },
+ access_sctlr, reset_val, SCTLR_EL1, 0x00C50078 },
/* CPACR_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b0001), CRm(0b0000), Op2(0b010),
NULL, reset_val, CPACR_EL1, 0 },
/* TTBR0_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b0010), CRm(0b0000), Op2(0b000),
- NULL, reset_unknown, TTBR0_EL1 },
+ access_vm_reg, reset_unknown, TTBR0_EL1 },
/* TTBR1_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b0010), CRm(0b0000), Op2(0b001),
- NULL, reset_unknown, TTBR1_EL1 },
+ access_vm_reg, reset_unknown, TTBR1_EL1 },
/* TCR_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b0010), CRm(0b0000), Op2(0b010),
- NULL, reset_val, TCR_EL1, 0 },
+ access_vm_reg, reset_val, TCR_EL1, 0 },
/* AFSR0_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b0101), CRm(0b0001), Op2(0b000),
- NULL, reset_unknown, AFSR0_EL1 },
+ access_vm_reg, reset_unknown, AFSR0_EL1 },
/* AFSR1_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b0101), CRm(0b0001), Op2(0b001),
- NULL, reset_unknown, AFSR1_EL1 },
+ access_vm_reg, reset_unknown, AFSR1_EL1 },
/* ESR_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b0101), CRm(0b0010), Op2(0b000),
- NULL, reset_unknown, ESR_EL1 },
+ access_vm_reg, reset_unknown, ESR_EL1 },
/* FAR_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b0110), CRm(0b0000), Op2(0b000),
- NULL, reset_unknown, FAR_EL1 },
+ access_vm_reg, reset_unknown, FAR_EL1 },
/* PAR_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b0111), CRm(0b0100), Op2(0b000),
NULL, reset_unknown, PAR_EL1 },
@@ -224,17 +264,17 @@ static const struct sys_reg_desc sys_reg_descs[] = {
/* MAIR_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b1010), CRm(0b0010), Op2(0b000),
- NULL, reset_unknown, MAIR_EL1 },
+ access_vm_reg, reset_unknown, MAIR_EL1 },
/* AMAIR_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b1010), CRm(0b0011), Op2(0b000),
- NULL, reset_amair_el1, AMAIR_EL1 },
+ access_vm_reg, reset_amair_el1, AMAIR_EL1 },
/* VBAR_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b1100), CRm(0b0000), Op2(0b000),
NULL, reset_val, VBAR_EL1, 0 },
/* CONTEXTIDR_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b1101), CRm(0b0000), Op2(0b001),
- NULL, reset_val, CONTEXTIDR_EL1, 0 },
+ access_vm_reg, reset_val, CONTEXTIDR_EL1, 0 },
/* TPIDR_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b1101), CRm(0b0000), Op2(0b100),
NULL, reset_unknown, TPIDR_EL1 },
@@ -305,14 +345,32 @@ static const struct sys_reg_desc sys_reg_descs[] = {
NULL, reset_val, FPEXC32_EL2, 0x70 },
};
-/* Trapped cp15 registers */
+/*
+ * Trapped cp15 registers. TTBR0/TTBR1 get a double encoding,
+ * depending on the way they are accessed (as a 32bit or a 64bit
+ * register).
+ */
static const struct sys_reg_desc cp15_regs[] = {
+ { Op1( 0), CRn( 0), CRm( 2), Op2( 0), access_vm_reg, NULL, c2_TTBR0 },
+ { Op1( 0), CRn( 1), CRm( 0), Op2( 0), access_sctlr, NULL, c1_SCTLR },
+ { Op1( 0), CRn( 2), CRm( 0), Op2( 0), access_vm_reg, NULL, c2_TTBR0 },
+ { Op1( 0), CRn( 2), CRm( 0), Op2( 1), access_vm_reg, NULL, c2_TTBR1 },
+ { Op1( 0), CRn( 2), CRm( 0), Op2( 2), access_vm_reg, NULL, c2_TTBCR },
+ { Op1( 0), CRn( 3), CRm( 0), Op2( 0), access_vm_reg, NULL, c3_DACR },
+ { Op1( 0), CRn( 5), CRm( 0), Op2( 0), access_vm_reg, NULL, c5_DFSR },
+ { Op1( 0), CRn( 5), CRm( 0), Op2( 1), access_vm_reg, NULL, c5_IFSR },
+ { Op1( 0), CRn( 5), CRm( 1), Op2( 0), access_vm_reg, NULL, c5_ADFSR },
+ { Op1( 0), CRn( 5), CRm( 1), Op2( 1), access_vm_reg, NULL, c5_AIFSR },
+ { Op1( 0), CRn( 6), CRm( 0), Op2( 0), access_vm_reg, NULL, c6_DFAR },
+ { Op1( 0), CRn( 6), CRm( 0), Op2( 2), access_vm_reg, NULL, c6_IFAR },
+
/*
* DC{C,I,CI}SW operations:
*/
{ Op1( 0), CRn( 7), CRm( 6), Op2( 2), access_dcsw },
{ Op1( 0), CRn( 7), CRm(10), Op2( 2), access_dcsw },
{ Op1( 0), CRn( 7), CRm(14), Op2( 2), access_dcsw },
+
{ Op1( 0), CRn( 9), CRm(12), Op2( 0), pm_fake },
{ Op1( 0), CRn( 9), CRm(12), Op2( 1), pm_fake },
{ Op1( 0), CRn( 9), CRm(12), Op2( 2), pm_fake },
@@ -326,6 +384,14 @@ static const struct sys_reg_desc cp15_regs[] = {
{ Op1( 0), CRn( 9), CRm(14), Op2( 0), pm_fake },
{ Op1( 0), CRn( 9), CRm(14), Op2( 1), pm_fake },
{ Op1( 0), CRn( 9), CRm(14), Op2( 2), pm_fake },
+
+ { Op1( 0), CRn(10), CRm( 2), Op2( 0), access_vm_reg, NULL, c10_PRRR },
+ { Op1( 0), CRn(10), CRm( 2), Op2( 1), access_vm_reg, NULL, c10_NMRR },
+ { Op1( 0), CRn(10), CRm( 3), Op2( 0), access_vm_reg, NULL, c10_AMAIR0 },
+ { Op1( 0), CRn(10), CRm( 3), Op2( 1), access_vm_reg, NULL, c10_AMAIR1 },
+ { Op1( 0), CRn(13), CRm( 0), Op2( 1), access_vm_reg, NULL, c13_CID },
+
+ { Op1( 1), CRn( 0), CRm( 2), Op2( 0), access_vm_reg, NULL, c2_TTBR1 },
};
/* Target specific emulation tables */
--
1.8.3.4
^ permalink raw reply related
* [PATCH v3 02/11] arm64: KVM: allows discrimination of AArch32 sysreg access
From: Marc Zyngier @ 2014-02-05 19:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1391630151-7875-1-git-send-email-marc.zyngier@arm.com>
The current handling of AArch32 trapping is slightly less than
perfect, as it is not possible (from a handler point of view)
to distinguish it from an AArch64 access, nor to tell a 32bit
from a 64bit access either.
Fix this by introducing two additional flags:
- is_aarch32: true if the access was made in AArch32 mode
- is_32bit: true if is_aarch32 == true and a MCR/MRC instruction
was used to perform the access (as opposed to MCRR/MRRC).
This allows a handler to cover all the possible conditions in which
a system register gets trapped.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Acked-by: Christoffer Dall <christoffer.dall@linaro.org>
---
arch/arm64/kvm/sys_regs.c | 6 ++++++
arch/arm64/kvm/sys_regs.h | 2 ++
2 files changed, 8 insertions(+)
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 02e9d09..bf03e0f 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -437,6 +437,8 @@ int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run)
u32 hsr = kvm_vcpu_get_hsr(vcpu);
int Rt2 = (hsr >> 10) & 0xf;
+ params.is_aarch32 = true;
+ params.is_32bit = false;
params.CRm = (hsr >> 1) & 0xf;
params.Rt = (hsr >> 5) & 0xf;
params.is_write = ((hsr & 1) == 0);
@@ -480,6 +482,8 @@ int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run)
struct sys_reg_params params;
u32 hsr = kvm_vcpu_get_hsr(vcpu);
+ params.is_aarch32 = true;
+ params.is_32bit = true;
params.CRm = (hsr >> 1) & 0xf;
params.Rt = (hsr >> 5) & 0xf;
params.is_write = ((hsr & 1) == 0);
@@ -549,6 +553,8 @@ int kvm_handle_sys_reg(struct kvm_vcpu *vcpu, struct kvm_run *run)
struct sys_reg_params params;
unsigned long esr = kvm_vcpu_get_hsr(vcpu);
+ params.is_aarch32 = false;
+ params.is_32bit = false;
params.Op0 = (esr >> 20) & 3;
params.Op1 = (esr >> 14) & 0x7;
params.CRn = (esr >> 10) & 0xf;
diff --git a/arch/arm64/kvm/sys_regs.h b/arch/arm64/kvm/sys_regs.h
index d50d372..d411e25 100644
--- a/arch/arm64/kvm/sys_regs.h
+++ b/arch/arm64/kvm/sys_regs.h
@@ -30,6 +30,8 @@ struct sys_reg_params {
u8 Op2;
u8 Rt;
bool is_write;
+ bool is_aarch32;
+ bool is_32bit; /* Only valid if is_aarch32 is true */
};
struct sys_reg_desc {
--
1.8.3.4
^ permalink raw reply related
* [PATCH v3 01/11] arm64: KVM: force cache clean on page fault when caches are off
From: Marc Zyngier @ 2014-02-05 19:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1391630151-7875-1-git-send-email-marc.zyngier@arm.com>
In order for the guest with caches off to observe data written
contained in a given page, we need to make sure that page is
committed to memory, and not just hanging in the cache (as
guest accesses are completely bypassing the cache until it
decides to enable it).
For this purpose, hook into the coherent_icache_guest_page
function and flush the region if the guest SCTLR_EL1
register doesn't show the MMU and caches as being enabled.
The function also get renamed to coherent_cache_guest_page.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
---
arch/arm/include/asm/kvm_mmu.h | 4 ++--
arch/arm/kvm/mmu.c | 4 ++--
arch/arm64/include/asm/kvm_mmu.h | 16 ++++++++++++----
3 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/arch/arm/include/asm/kvm_mmu.h b/arch/arm/include/asm/kvm_mmu.h
index 2d122ad..6d0f3d3 100644
--- a/arch/arm/include/asm/kvm_mmu.h
+++ b/arch/arm/include/asm/kvm_mmu.h
@@ -116,8 +116,8 @@ static inline void kvm_set_s2pmd_writable(pmd_t *pmd)
struct kvm;
-static inline void coherent_icache_guest_page(struct kvm *kvm, hva_t hva,
- unsigned long size)
+static inline void coherent_cache_guest_page(struct kvm_vcpu *vcpu, hva_t hva,
+ unsigned long size)
{
/*
* If we are going to insert an instruction page and the icache is
diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c
index 7789857..fc71a8d 100644
--- a/arch/arm/kvm/mmu.c
+++ b/arch/arm/kvm/mmu.c
@@ -715,7 +715,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
kvm_set_s2pmd_writable(&new_pmd);
kvm_set_pfn_dirty(pfn);
}
- coherent_icache_guest_page(kvm, hva & PMD_MASK, PMD_SIZE);
+ coherent_cache_guest_page(vcpu, hva & PMD_MASK, PMD_SIZE);
ret = stage2_set_pmd_huge(kvm, memcache, fault_ipa, &new_pmd);
} else {
pte_t new_pte = pfn_pte(pfn, PAGE_S2);
@@ -723,7 +723,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
kvm_set_s2pte_writable(&new_pte);
kvm_set_pfn_dirty(pfn);
}
- coherent_icache_guest_page(kvm, hva, PAGE_SIZE);
+ coherent_cache_guest_page(vcpu, hva, PAGE_SIZE);
ret = stage2_set_pte(kvm, memcache, fault_ipa, &new_pte, false);
}
diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h
index 7f1f940..6eaf69b 100644
--- a/arch/arm64/include/asm/kvm_mmu.h
+++ b/arch/arm64/include/asm/kvm_mmu.h
@@ -106,7 +106,6 @@ static inline bool kvm_is_write_fault(unsigned long esr)
return true;
}
-static inline void kvm_clean_dcache_area(void *addr, size_t size) {}
static inline void kvm_clean_pgd(pgd_t *pgd) {}
static inline void kvm_clean_pmd_entry(pmd_t *pmd) {}
static inline void kvm_clean_pte(pte_t *pte) {}
@@ -124,9 +123,19 @@ static inline void kvm_set_s2pmd_writable(pmd_t *pmd)
struct kvm;
-static inline void coherent_icache_guest_page(struct kvm *kvm, hva_t hva,
- unsigned long size)
+#define kvm_flush_dcache_to_poc(a,l) __flush_dcache_area((a), (l))
+
+static inline bool vcpu_has_cache_enabled(struct kvm_vcpu *vcpu)
{
+ return (vcpu_sys_reg(vcpu, SCTLR_EL1) & 0b101) == 0b101;
+}
+
+static inline void coherent_cache_guest_page(struct kvm_vcpu *vcpu, hva_t hva,
+ unsigned long size)
+{
+ if (!vcpu_has_cache_enabled(vcpu))
+ kvm_flush_dcache_to_poc((void *)hva, size);
+
if (!icache_is_aliasing()) { /* PIPT */
flush_icache_range(hva, hva + size);
} else if (!icache_is_aivivt()) { /* non ASID-tagged VIVT */
@@ -135,7 +144,6 @@ static inline void coherent_icache_guest_page(struct kvm *kvm, hva_t hva,
}
}
-#define kvm_flush_dcache_to_poc(a,l) __flush_dcache_area((a), (l))
#define kvm_virt_to_phys(x) __virt_to_phys((unsigned long)(x))
#endif /* __ASSEMBLY__ */
--
1.8.3.4
^ permalink raw reply related
* [PATCH v3 00/11] arm/arm64: KVM: host cache maintenance when guest caches are off
From: Marc Zyngier @ 2014-02-05 19:55 UTC (permalink / raw)
To: linux-arm-kernel
When we run a guest with cache disabled, we don't flush the cache to
the Point of Coherency, hence possibly missing bits of data that have
been written in the cache, but have not yet reached memory.
We also have the opposite issue: when a guest enables its cache,
whatever sits in the cache is suddenly going to become visible,
shadowing whatever the guest has written into RAM.
There are several approaches to these issues:
- Using the DC bit when caches are off: this breaks guests assuming
caches off while doing DMA operations. Bootloaders, for example.
It also breaks the I-D coherency.
- Fetch the memory attributes on translation fault, and flush the
cache while handling the fault. This relies on using the PAR_EL1
register to obtain the Stage-1 memory attributes, and tends to be
slow.
- Detecting the translation faults occuring with MMU off (and
performing a cache clean), and trapping SCTLR_EL1 to detect the
moment when the guest is turning its caches on (and performing a
cache invalidation). Trapping of SCTLR_EL1 is then disabled to
ensure the best performance.
This patch series implements the last solution, for both arm and
arm64. Tested on TC2 (ARMv7) and FVP model (ARMv8).
>From v2 (http://www.spinics.net/lists/arm-kernel/msg302472.html):
- Addressed most (hopefully all) of Christoffer's comments
- Added a new LPAE pmd_addr_end to deal with 40bit IPAs
>From v1 (http://www.spinics.net/lists/kvm/msg99404.html):
- Fixed AArch32 VM handling on arm64 (Reported by Anup)
- Added ARMv7 support:
* Fixed a couple of issues regarding handling of 64bit cp15 regs
* Per-vcpu HCR
* Switching of AMAIR0 and AMAIR1
Marc Zyngier (11):
arm64: KVM: force cache clean on page fault when caches are off
arm64: KVM: allows discrimination of AArch32 sysreg access
arm64: KVM: trap VM system registers until MMU and caches are ON
arm64: KVM: flush VM pages before letting the guest enable caches
ARM: LPAE: provide an IPA capable pmd_addr_end
ARM: KVM: force cache clean on page fault when caches are off
ARM: KVM: fix handling of trapped 64bit coprocessor accesses
ARM: KVM: fix ordering of 64bit coprocessor accesses
ARM: KVM: introduce per-vcpu HYP Configuration Register
ARM: KVM: add world-switch for AMAIR{0,1}
ARM: KVM: trap VM system registers until MMU and caches are ON
arch/arm/include/asm/kvm_arm.h | 4 +-
arch/arm/include/asm/kvm_asm.h | 4 +-
arch/arm/include/asm/kvm_host.h | 9 ++--
arch/arm/include/asm/kvm_mmu.h | 23 ++++++--
arch/arm/include/asm/pgtable-3level.h | 5 ++
arch/arm/kernel/asm-offsets.c | 1 +
arch/arm/kvm/coproc.c | 84 ++++++++++++++++++++++-------
arch/arm/kvm/coproc.h | 14 +++--
arch/arm/kvm/coproc_a15.c | 2 +-
arch/arm/kvm/coproc_a7.c | 2 +-
arch/arm/kvm/guest.c | 1 +
arch/arm/kvm/interrupts_head.S | 21 +++++---
arch/arm/kvm/mmu.c | 97 +++++++++++++++++++++++++++++++++-
arch/arm64/include/asm/kvm_arm.h | 3 +-
arch/arm64/include/asm/kvm_asm.h | 3 +-
arch/arm64/include/asm/kvm_mmu.h | 20 +++++--
arch/arm64/kvm/sys_regs.c | 99 ++++++++++++++++++++++++++++++-----
arch/arm64/kvm/sys_regs.h | 2 +
18 files changed, 332 insertions(+), 62 deletions(-)
--
1.8.3.4
^ permalink raw reply
* [PATCH v4 2/2] memory: ti-aemif: add bindings for AEMIF driver
From: Ivan Khoronzhuk @ 2014-02-05 19:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1391629574-18955-1-git-send-email-ivan.khoronzhuk@ti.com>
Add bindings for TI Async External Memory Interface (AEMIF) controller.
The Async External Memory Interface (EMIF16/AEMIF) controller is intended to
provide a glue-less interface to a variety of asynchronous memory devices like
ASRA M, NOR and NAND memory. A total of 256M bytes of any of these memories
can be accessed via 4 chip selects with 64M byte access per chip select.
We are not encoding CS number in reg property, it's memory partition number.
The CS number is encoded for Davinci NAND node using standalone property
"ti,davinci-chipselect" and we need to provide two memory ranges to it,
as result we can't encode CS number in "reg" for AEMIF child devices
(NAND/NOR/etc), as it will break bindings compatibility.
In this patch, NAND node is used just as an example of child node.
Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>
---
.../bindings/memory-controllers/ti-aemif.txt | 210 +++++++++++++++++++++
1 file changed, 210 insertions(+)
create mode 100644 Documentation/devicetree/bindings/memory-controllers/ti-aemif.txt
diff --git a/Documentation/devicetree/bindings/memory-controllers/ti-aemif.txt b/Documentation/devicetree/bindings/memory-controllers/ti-aemif.txt
new file mode 100644
index 0000000..d1466dc
--- /dev/null
+++ b/Documentation/devicetree/bindings/memory-controllers/ti-aemif.txt
@@ -0,0 +1,210 @@
+* Device tree bindings for Texas instruments AEMIF controller
+
+The Async External Memory Interface (EMIF16/AEMIF) controller is intended to
+provide a glue-less interface to a variety of asynchronous memory devices like
+ASRA M, NOR and NAND memory. A total of 256M bytes of any of these memories
+can be accessed at any given time via four chip selects with 64M byte access
+per chip select. Synchronous memories such as DDR1 SD RAM, SDR SDRAM
+and Mobile SDR are not supported.
+
+Documentation:
+Davinci DM646x - http://www.ti.com/lit/ug/sprueq7c/sprueq7c.pdf
+OMAP-L138 (DA850) - http://www.ti.com/lit/ug/spruh77a/spruh77a.pdf
+Kestone - http://www.ti.com/lit/ug/sprugz3a/sprugz3a.pdf
+
+Required properties:
+
+- compatible: "ti,davinci-aemif"
+ "ti,keystone-aemif"
+ "ti,da850-aemif"
+
+- reg: contains offset/length value for AEMIF control registers
+ space.
+
+- #address-cells: Must be 2. The partition number has to be encoded in the
+ first address cell and it may accept values 0..N-1
+ (N - total number of partitions). It's recommended to
+ assign N-1 number for the control partition. The second
+ cell is the offset into the partition.
+
+- #size-cells: Must be set to 1.
+
+- ranges: Contains memory regions. There are two types of
+ ranges/partitions:
+ - CS-specific partition/range. If continuous, must be
+ set up to reflect the memory layout for 4 chipselects,
+ if not then additional range/partition can be added and
+ child device can select the proper one.
+ - control partition which is common for all CS
+ interfaces.
+
+- clocks: the clock feeding the controller clock. Required only
+ if clock tree data present in device tree.
+ See clock-bindings.txt
+
+- clock-names: clock name. It has to be "aemif". Required only if clock
+ tree data present in device tree, in another case don't
+ use it.
+ See clock-bindings.txt
+
+- clock-ranges: Empty property indicating that child nodes can inherit
+ named clocks. Required only if clock tree data present
+ in device tree.
+ See clock-bindings.txt
+
+
+Child chip-select (cs) nodes contain the memory devices nodes connected to
+such as NOR (e.g. cfi-flash) and NAND (ti,davinci-nand, see davinci-nand.txt).
+There might be board specific devices like FPGAs.
+
+Required child cs node properties:
+
+- #address-cells: Must be 2.
+
+- #size-cells: Must be 1.
+
+- ranges: Empty property indicating that child nodes can inherit
+ memory layout.
+
+- clock-ranges: Empty property indicating that child nodes can inherit
+ named clocks. Required only if clock tree data present
+ in device tree.
+
+- ti,cs-chipselect: number of chipselect. Indicates on the aemif driver
+ which chipselect is used for accessing the memory. For
+ compatibles "ti,davinci-aemif" and "ti,keystone-aemif"
+ it can be in range [0-3]. For compatible
+ "ti,da850-aemif" range is [2-5].
+
+Optional child cs node properties:
+
+- ti,bus-width: width of the asynchronous device's data bus
+ 8 or 16 if not preset 8
+
+- ti,cs-ss: enable/disable select strobe mode
+ In select strobe mode chip select behaves as
+ the strobe and is active only during the strobe
+ period. If present then enable.
+
+- ti,cs-ew: enable/disable extended wait mode
+ if set, the controller monitors the EMIFWAIT pin
+ mapped to that chip select to determine if the
+ device wants to extend the strobe period. If
+ present then enable.
+
+- ti,cs-ta: minimum turn around time, ns
+ Time between the end of one asynchronous memory
+ access and the start of another asynchronous
+ memory access. This delay is not incurred
+ between a read followed by read or a write
+ followed by a write to same chip select.
+
+- ti,cs-rsetup: read setup width, ns
+ Time between the beginning of a memory cycle
+ and the activation of read strobe.
+ Minimum value is 1 (0 treated as 1).
+
+- ti,cs-rstobe: read strobe width, ns
+ Time between the activation and deactivation of
+ the read strobe.
+ Minimum value is 1 (0 treated as 1).
+
+- ti,cs-rhold: read hold width, ns
+ Time between the deactivation of the read
+ strobe and the end of the cycle (which may be
+ either an address change or the deactivation of
+ the chip select signal.
+ Minimum value is 1 (0 treated as 1).
+
+- ti,cs-wsetup: write setup width, ns
+ Time between the beginning of a memory cycle
+ and the activation of write strobe.
+ Minimum value is 1 (0 treated as 1).
+
+- ti,cs-wstrobe: write strobe width, ns
+ Time between the activation and deactivation of
+ the write strobe.
+ Minimum value is 1 (0 treated as 1).
+
+- ti,cs-whold: write hold width, ns
+ Time between the deactivation of the write
+ strobe and the end of the cycle (which may be
+ either an address change or the deactivation of
+ the chip select signal.
+ Minimum value is 1 (0 treated as 1).
+
+If any of the above parameters are absent, current parameter value will be taken
+from the corresponding HW reg.
+
+Example for aemif, davinci nand and nor flash chip select shown below.
+
+memory-controller at 21000A00 {
+ compatible = "ti,davinci-aemif";
+ #address-cells = <2>;
+ #size-cells = <1>;
+ clocks = <&clkaemif 0>;
+ clock-names = "aemif";
+ clock-ranges;
+ reg = <0x2100A00 0x00000100>;
+ ranges = <0 0 0x70000000 0x10000000
+ 1 0 0x21000A00 0x0000100>;
+ /*
+ * Partition0: CS-specific memory range which is
+ * implemented as continuous physical memory region
+ * Partition1: control memory range
+ */
+
+ nand:cs2 {
+ #address-cells = <2>;
+ #size-cells = <1>;
+ clock-ranges;
+ ranges;
+
+ ti,cs-chipselect = <2>;
+ /* all timings in nanoseconds */
+ ti,cs-ta = <0>;
+ ti,cs-rhold = <7>;
+ ti,cs-rstrobe = <42>;
+ ti,cs-rsetup = <14>;
+ ti,cs-whold = <7>;
+ ti,cs-wstrobe = <42>;
+ ti,cs-wsetup = <14>;
+
+ nand at 0,0x8000000 {
+ compatible = "ti,davinci-nand";
+ reg = <0 0x8000000 0x4000000
+ 1 0x0000000 0x0000100>;
+ /*
+ * Partition0, offset 0x8000000, size 0x4000000
+ * Partition1, offset 0x0000000, size 0x0000100
+ */
+
+ .. see davinci-nand.txt
+ };
+ };
+
+ nor:cs0 {
+ #address-cells = <2>;
+ #size-cells = <1>;
+ clock-ranges;
+ ranges;
+
+ ti,cs-chipselect = <0>;
+ /* all timings in nanoseconds */
+ ti,cs-ta = <0>;
+ ti,cs-rhold = <8>;
+ ti,cs-rstrobe = <40>;
+ ti,cs-rsetup = <14>;
+ ti,cs-whold = <7>;
+ ti,cs-wstrobe = <40>;
+ ti,cs-wsetup = <14>;
+ ti,cs-asize = <1>;
+
+ flash at 0,0x0000000 {
+ compatible = "cfi-flash";
+ reg = <0 0x0000000 0x4000000>;
+
+ ...
+ };
+ };
+};
--
1.8.3.2
^ permalink raw reply related
* [PATCH v4 1/2] memory: ti-aemif: introduce AEMIF driver
From: Ivan Khoronzhuk @ 2014-02-05 19:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1391629574-18955-1-git-send-email-ivan.khoronzhuk@ti.com>
Add new AEMIF driver for EMIF16 Texas Instruments controller.
The EMIF16 module is intended to provide a glue-less interface to
a variety of asynchronous memory devices like ASRA M, NOR and NAND
memory. A total of 256M bytes of any of these memories can be
accessed at any given time via 4 chip selects with 64M byte access
per chip select.
Synchronous memories such as DDR1 SD RAM, SDR SDRAM and Mobile SDR
are not supported.
This controller is used on SoCs like Davinci, Keysone2
Acked-by: Santosh Shilimkar <santosh.shilimkar at ti.com
Signed-off-by: [initial author] Murali Karicheri <m-karicheri2@ti.com>
Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>
---
drivers/memory/Kconfig | 11 ++
drivers/memory/Makefile | 1 +
drivers/memory/ti-aemif.c | 429 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 441 insertions(+)
create mode 100644 drivers/memory/ti-aemif.c
diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
index 29a11db..7bc3982 100644
--- a/drivers/memory/Kconfig
+++ b/drivers/memory/Kconfig
@@ -7,6 +7,17 @@ menuconfig MEMORY
if MEMORY
+config TI_AEMIF
+ tristate "Texas Instruments AEMIF driver"
+ depends on (ARCH_DAVINCI || ARCH_KEYSTONE) && OF
+ help
+ This driver is for the AEMIF module available in Texas Instruments
+ SoCs. AEMIF stands for Asynchronous External Memory Interface and
+ is intended to provide a glue-less interface to a variety of
+ asynchronuous memory devices like ASRAM, NOR and NAND memory. A total
+ of 256M bytes of any of these memories can be accessed at a given
+ time via four chip selects with 64M byte access per chip select.
+
config TI_EMIF
tristate "Texas Instruments EMIF driver"
depends on ARCH_OMAP2PLUS
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
index 969d923..d4e150c 100644
--- a/drivers/memory/Makefile
+++ b/drivers/memory/Makefile
@@ -5,6 +5,7 @@
ifeq ($(CONFIG_DDR),y)
obj-$(CONFIG_OF) += of_memory.o
endif
+obj-$(CONFIG_TI_AEMIF) += ti-aemif.o
obj-$(CONFIG_TI_EMIF) += emif.o
obj-$(CONFIG_MVEBU_DEVBUS) += mvebu-devbus.o
obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o
diff --git a/drivers/memory/ti-aemif.c b/drivers/memory/ti-aemif.c
new file mode 100644
index 0000000..8d15d87
--- /dev/null
+++ b/drivers/memory/ti-aemif.c
@@ -0,0 +1,429 @@
+/*
+ * TI AEMIF driver
+ *
+ * Copyright (C) 2010 - 2013 Texas Instruments Incorporated. http://www.ti.com/
+ *
+ * Authors:
+ * Murali Karicheri <m-karicheri2@ti.com>
+ * Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#define TA_SHIFT 2
+#define RHOLD_SHIFT 4
+#define RSTROBE_SHIFT 7
+#define RSETUP_SHIFT 13
+#define WHOLD_SHIFT 17
+#define WSTROBE_SHIFT 20
+#define WSETUP_SHIFT 26
+#define EW_SHIFT 30
+#define SS_SHIFT 31
+
+#define TA(x) ((x) << TA_SHIFT)
+#define RHOLD(x) ((x) << RHOLD_SHIFT)
+#define RSTROBE(x) ((x) << RSTROBE_SHIFT)
+#define RSETUP(x) ((x) << RSETUP_SHIFT)
+#define WHOLD(x) ((x) << WHOLD_SHIFT)
+#define WSTROBE(x) ((x) << WSTROBE_SHIFT)
+#define WSETUP(x) ((x) << WSETUP_SHIFT)
+#define EW(x) ((x) << EW_SHIFT)
+#define SS(x) ((x) << SS_SHIFT)
+
+#define ASIZE_MAX 0x1
+#define TA_MAX 0x3
+#define RHOLD_MAX 0x7
+#define RSTROBE_MAX 0x3f
+#define RSETUP_MAX 0xf
+#define WHOLD_MAX 0x7
+#define WSTROBE_MAX 0x3f
+#define WSETUP_MAX 0xf
+#define EW_MAX 0x1
+#define SS_MAX 0x1
+#define NUM_CS 4
+
+#define TA_VAL(x) (((x) & TA(TA_MAX)) >> TA_SHIFT)
+#define RHOLD_VAL(x) (((x) & RHOLD(RHOLD_MAX)) >> RHOLD_SHIFT)
+#define RSTROBE_VAL(x) (((x) & RSTROBE(RSTROBE_MAX)) >> RSTROBE_SHIFT)
+#define RSETUP_VAL(x) (((x) & RSETUP(RSETUP_MAX)) >> RSETUP_SHIFT)
+#define WHOLD_VAL(x) (((x) & WHOLD(WHOLD_MAX)) >> WHOLD_SHIFT)
+#define WSTROBE_VAL(x) (((x) & WSTROBE(WSTROBE_MAX)) >> WSTROBE_SHIFT)
+#define WSETUP_VAL(x) (((x) & WSETUP(WSETUP_MAX)) >> WSETUP_SHIFT)
+#define EW_VAL(x) (((x) & EW(EW_MAX)) >> EW_SHIFT)
+#define SS_VAL(x) (((x) & SS(SS_MAX)) >> SS_SHIFT)
+
+#define NRCSR_OFFSET 0x00
+#define AWCCR_OFFSET 0x04
+#define A1CR_OFFSET 0x10
+
+#define ACR_ASIZE_MASK 0x3
+#define ACR_EW_MASK BIT(30)
+#define ACR_SS_MASK BIT(31)
+#define ASIZE_16BIT 1
+
+#define CONFIG_MASK (TA(TA_MAX) | \
+ RHOLD(RHOLD_MAX) | \
+ RSTROBE(RSTROBE_MAX) | \
+ RSETUP(RSETUP_MAX) | \
+ WHOLD(WHOLD_MAX) | \
+ WSTROBE(WSTROBE_MAX) | \
+ WSETUP(WSETUP_MAX) | \
+ EW(EW_MAX) | SS(SS_MAX) | \
+ ASIZE_MAX)
+
+#define DRV_NAME "ti-aemif"
+
+/**
+ * struct aemif_cs_data: structure to hold cs parameters
+ * @cs: chip-select number
+ * @wstrobe: write strobe width, ns
+ * @rstrobe: read strobe width, ns
+ * @wsetup: write setup width, ns
+ * @whold: write hold width, ns
+ * @rsetup: read setup width, ns
+ * @rhold: read hold width, ns
+ * @ta: minimum turn around time, ns
+ * @enable_ss: enable/disable select strobe mode
+ * @enable_ew: enable/disable extended wait mode
+ * @asize: width of the asynchronous device's data bus
+ */
+struct aemif_cs_data {
+ u8 cs;
+ u16 wstrobe;
+ u16 rstrobe;
+ u8 wsetup;
+ u8 whold;
+ u8 rsetup;
+ u8 rhold;
+ u8 ta;
+ u8 enable_ss;
+ u8 enable_ew;
+ u8 asize;
+};
+
+/**
+ * struct aemif_device: structure to hold device data
+ * @base: base address of AEMIF registers
+ * @clk: source clock
+ * @clk_rate: clock's rate in kHz
+ * @num_cs: number of assigned chip-selects
+ * @cs_offset: start number of cs nodes
+ * @cs_data: array of chip-select settings
+ */
+struct aemif_device {
+ void __iomem *base;
+ struct clk *clk;
+ unsigned long clk_rate;
+ u8 num_cs;
+ int cs_offset;
+ struct aemif_cs_data cs_data[NUM_CS];
+};
+
+/**
+ * aemif_calc_rate - calculate timing data.
+ * @pdev: platform device to calculate for
+ * @wanted: The cycle time needed in nanoseconds.
+ * @clk: The input clock rate in kHz.
+ * @max: The maximum divider value that can be programmed.
+ *
+ * On success, returns the calculated timing value minus 1 for easy
+ * programming into AEMIF timing registers, else negative errno.
+ */
+static int aemif_calc_rate(struct platform_device *pdev, int wanted,
+ unsigned long clk, int max)
+{
+ int result;
+
+ result = DIV_ROUND_UP((wanted * clk), NSEC_PER_MSEC) - 1;
+
+ dev_dbg(&pdev->dev, "%s: result %d from %ld, %d\n", __func__, result,
+ clk, wanted);
+
+ /* It is generally OK to have a more relaxed timing than requested... */
+ if (result < 0)
+ result = 0;
+
+ /* ... But configuring tighter timings is not an option. */
+ else if (result > max)
+ result = -EINVAL;
+
+ return result;
+}
+
+/**
+ * aemif_config_abus - configure async bus parameters
+ * @pdev: platform device to configure for
+ * @csnum: aemif chip select number
+ *
+ * This function programs the given timing values (in real clock) into the
+ * AEMIF registers taking the AEMIF clock into account.
+ *
+ * This function does not use any locking while programming the AEMIF
+ * because it is expected that there is only one user of a given
+ * chip-select.
+ *
+ * Returns 0 on success, else negative errno.
+ */
+static int aemif_config_abus(struct platform_device *pdev, int csnum)
+{
+ struct aemif_device *aemif = platform_get_drvdata(pdev);
+ struct aemif_cs_data *data = &aemif->cs_data[csnum];
+ int ta, rhold, rstrobe, rsetup, whold, wstrobe, wsetup;
+ unsigned long clk_rate = aemif->clk_rate;
+ unsigned offset;
+ u32 set, val;
+
+ offset = A1CR_OFFSET + (data->cs - aemif->cs_offset) * 4;
+
+ ta = aemif_calc_rate(pdev, data->ta, clk_rate, TA_MAX);
+ rhold = aemif_calc_rate(pdev, data->rhold, clk_rate, RHOLD_MAX);
+ rstrobe = aemif_calc_rate(pdev, data->rstrobe, clk_rate, RSTROBE_MAX);
+ rsetup = aemif_calc_rate(pdev, data->rsetup, clk_rate, RSETUP_MAX);
+ whold = aemif_calc_rate(pdev, data->whold, clk_rate, WHOLD_MAX);
+ wstrobe = aemif_calc_rate(pdev, data->wstrobe, clk_rate, WSTROBE_MAX);
+ wsetup = aemif_calc_rate(pdev, data->wsetup, clk_rate, WSETUP_MAX);
+
+ if (ta < 0 || rhold < 0 || rstrobe < 0 || rsetup < 0 ||
+ whold < 0 || wstrobe < 0 || wsetup < 0) {
+ dev_err(&pdev->dev, "%s: cannot get suitable timings\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ set = TA(ta) | RHOLD(rhold) | RSTROBE(rstrobe) | RSETUP(rsetup) |
+ WHOLD(whold) | WSTROBE(wstrobe) | WSETUP(wsetup);
+
+ set |= (data->asize & ACR_ASIZE_MASK);
+ if (data->enable_ew)
+ set |= ACR_EW_MASK;
+ if (data->enable_ss)
+ set |= ACR_SS_MASK;
+
+ val = readl(aemif->base + offset);
+ val &= ~CONFIG_MASK;
+ val |= set;
+ writel(val, aemif->base + offset);
+
+ return 0;
+}
+
+static inline int aemif_cycles_to_nsec(int val, unsigned long clk_rate)
+{
+ return ((val + 1) * NSEC_PER_MSEC) / clk_rate;
+}
+
+/**
+ * aemif_get_hw_params - function to read hw register values
+ * @pdev: platform device to read for
+ * @csnum: aemif chip select number
+ *
+ * This function reads the defaults from the registers and update
+ * the timing values. Required for get/set commands and also for
+ * the case when driver needs to use defaults in hardware.
+ */
+static void aemif_get_hw_params(struct platform_device *pdev, int csnum)
+{
+ struct aemif_device *aemif = platform_get_drvdata(pdev);
+ struct aemif_cs_data *data = &aemif->cs_data[csnum];
+ unsigned long clk_rate = aemif->clk_rate;
+ u32 val, offset;
+
+ offset = A1CR_OFFSET + (data->cs - aemif->cs_offset) * 4;
+ val = readl(aemif->base + offset);
+
+ data->ta = aemif_cycles_to_nsec(TA_VAL(val), clk_rate);
+ data->rhold = aemif_cycles_to_nsec(RHOLD_VAL(val), clk_rate);
+ data->rstrobe = aemif_cycles_to_nsec(RSTROBE_VAL(val), clk_rate);
+ data->rsetup = aemif_cycles_to_nsec(RSETUP_VAL(val), clk_rate);
+ data->whold = aemif_cycles_to_nsec(WHOLD_VAL(val), clk_rate);
+ data->wstrobe = aemif_cycles_to_nsec(WSTROBE_VAL(val), clk_rate);
+ data->wsetup = aemif_cycles_to_nsec(WSETUP_VAL(val), clk_rate);
+ data->enable_ew = EW_VAL(val);
+ data->enable_ss = SS_VAL(val);
+ data->asize = val & ASIZE_MAX;
+}
+
+/**
+ * of_aemif_parse_abus_config - parse CS configuration from DT
+ * @pdev: platform device to parse for
+ * @np: device node ptr
+ *
+ * This function update the emif async bus configuration based on the values
+ * configured in a cs device binding node.
+ */
+static int of_aemif_parse_abus_config(struct platform_device *pdev,
+ struct device_node *np)
+{
+ struct aemif_device *aemif = platform_get_drvdata(pdev);
+ struct aemif_cs_data *data;
+ u32 cs;
+ u32 val;
+
+ if (of_property_read_u32(np, "ti,cs-chipselect", &cs)) {
+ dev_dbg(&pdev->dev, "cs property is required");
+ return -EINVAL;
+ }
+
+ if (cs - aemif->cs_offset >= NUM_CS || cs < aemif->cs_offset) {
+ dev_dbg(&pdev->dev, "cs number is incorrect %d", cs);
+ return -EINVAL;
+ }
+
+ if (aemif->num_cs >= NUM_CS) {
+ dev_dbg(&pdev->dev, "cs count is more than %d", NUM_CS);
+ return -EINVAL;
+ }
+
+ data = &aemif->cs_data[aemif->num_cs];
+ data->cs = cs;
+
+ /* read the current value in the hw register */
+ aemif_get_hw_params(pdev, aemif->num_cs++);
+
+ /* override the values from device node */
+ if (!of_property_read_u32(np, "ti,cs-ta", &val))
+ data->ta = val;
+
+ if (!of_property_read_u32(np, "ti,cs-rhold", &val))
+ data->rhold = val;
+
+ if (!of_property_read_u32(np, "ti,cs-rstrobe", &val))
+ data->rstrobe = val;
+
+ if (!of_property_read_u32(np, "ti,cs-rsetup", &val))
+ data->rsetup = val;
+
+ if (!of_property_read_u32(np, "ti,cs-whold", &val))
+ data->whold = val;
+
+ if (!of_property_read_u32(np, "ti,cs-wstrobe", &val))
+ data->wstrobe = val;
+
+ if (!of_property_read_u32(np, "ti,cs-wsetup", &val))
+ data->wsetup = val;
+
+ if (!of_property_read_u32(np, "ti,bus-width", &val))
+ if (val == 16)
+ data->asize = 1;
+ data->enable_ew = of_property_read_bool(np, "ti,cs-ew");
+ data->enable_ss = of_property_read_bool(np, "ti,cs-ss");
+ return 0;
+}
+
+static const struct of_device_id aemif_of_match[] = {
+ { .compatible = "ti,davinci-aemif", },
+ { .compatible = "ti,da850-aemif", },
+ {},
+};
+
+static int aemif_probe(struct platform_device *pdev)
+{
+ int ret = -ENODEV, i;
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct device_node *child_np;
+ struct aemif_device *aemif;
+
+ if (np == NULL)
+ return 0;
+
+ aemif = devm_kzalloc(dev, sizeof(*aemif), GFP_KERNEL);
+ if (!aemif) {
+ dev_err(dev, "cannot allocate memory for aemif\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, aemif);
+
+ aemif->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(aemif->clk)) {
+ dev_err(dev, "cannot get clock 'aemif'\n");
+ return PTR_ERR(aemif->clk);
+ }
+
+ clk_prepare_enable(aemif->clk);
+ aemif->clk_rate = clk_get_rate(aemif->clk) / MSEC_PER_SEC;
+
+ if (of_device_is_compatible(np, "ti,da850-aemif"))
+ aemif->cs_offset = 2;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ aemif->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(aemif->base)) {
+ ret = PTR_ERR(aemif->base);
+ goto error;
+ }
+
+ /*
+ * For every controller device node, there is a cs device node that
+ * describe the bus configuration parameters. This functions iterate
+ * over these nodes and update the cs data array.
+ */
+ for_each_available_child_of_node(np, child_np) {
+ ret = of_aemif_parse_abus_config(pdev, child_np);
+ if (ret < 0)
+ goto error;
+ }
+
+ for (i = 0; i < aemif->num_cs; i++) {
+ ret = aemif_config_abus(pdev, i);
+ if (ret < 0) {
+ dev_err(dev, "Error configuring chip select %d\n",
+ aemif->cs_data[i].cs);
+ goto error;
+ }
+ }
+
+ /*
+ * Create a child devices explicitly from here to
+ * guarantee that the child will be probed after the AEMIF timing
+ * parameters are set.
+ */
+ for_each_available_child_of_node(np, child_np) {
+ ret = of_platform_populate(child_np, NULL, NULL, dev);
+ if (ret < 0)
+ goto error;
+ }
+
+ return 0;
+error:
+ clk_disable_unprepare(aemif->clk);
+ return ret;
+}
+
+static int aemif_remove(struct platform_device *pdev)
+{
+ struct aemif_device *aemif = platform_get_drvdata(pdev);
+ clk_disable_unprepare(aemif->clk);
+ return 0;
+}
+
+static struct platform_driver aemif_driver = {
+ .probe = aemif_probe,
+ .remove = aemif_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(aemif_of_match),
+ },
+};
+
+module_platform_driver(aemif_driver);
+
+MODULE_AUTHOR("Murali Karicheri <m-karicheri2@ti.com>");
+MODULE_AUTHOR("Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>");
+MODULE_DESCRIPTION("Texas Instruments AEMIF driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
--
1.8.3.2
^ permalink raw reply related
* [PATCH v4 0/2] Introduce AEMIF driver for Davinci/Keystone archs
From: Ivan Khoronzhuk @ 2014-02-05 19:46 UTC (permalink / raw)
To: linux-arm-kernel
These patches introduce Async External Memory Interface (EMIF16/AEMIF)
controller driver for Davinci/Keystone archs.
For more informations see documentation:
Davinci DM646x - http://www.ti.com/lit/ug/sprueq7c/sprueq7c.pdf
OMAP-L138 - http://www.ti.com/lit/ug/spruh77a/spruh77a.pdf
Kestone - http://www.ti.com/lit/ug/sprugz3a/sprugz3a.pdf
Based on
git://git.kernel.org/pub/scm/linux/kernel/git/ssantosh/linux-keystone.git
keystone/master
v3..v4:
rebased on latest of linux-keystone.git keystone/master
v2..v3 (https://lkml.org/lkml/2013/12/11/148):
- memory: ti-aemif: introduce AEMIF driver
changed to work with multiple AEMIF controllers
corrected "copyright" to "authors" in header
changed compatible "ti,omap-L138-aemif" to "ti,da850-aeimf"
used NULL in clk_get() instead of "aemif" name
driver can be build as loadable module
treat all child nodes as cs nodes, it makes code simpler
- memory: ti-aemif: add bindings for AEMIF driver
deleted direct link driver/memory/ti-aemif.c
clarified description of controller ranges property
changed compatible "ti,omap-L138-aemif" to "ti,da850-aeimf"
added cs number information in commit log
removed compatible property from cs node, it makes code simpler
v1..v2 (https://lkml.org/lkml/2013/11/21/170):
- memory: ti-aemif: introduce AEMIF driver
- memory: ti-aemif: add bindings for AEMIF driver
added ti.cs-chipselect property instead of representing chipselect
number in cs node name.
Ivan Khoronzhuk (2):
memory: ti-aemif: introduce AEMIF driver
memory: ti-aemif: add bindings for AEMIF driver
.../bindings/memory-controllers/ti-aemif.txt | 210 ++++++++++
drivers/memory/Kconfig | 11 +
drivers/memory/Makefile | 1 +
drivers/memory/ti-aemif.c | 429 +++++++++++++++++++++
4 files changed, 651 insertions(+)
create mode 100644 Documentation/devicetree/bindings/memory-controllers/ti-aemif.txt
create mode 100644 drivers/memory/ti-aemif.c
--
1.8.3.2
^ permalink raw reply
* [PATCH 2/3] PCI: ARM: add support for virtual PCI host controller
From: Peter Maydell @ 2014-02-05 19:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140205192733.GG25695@obsidianresearch.com>
On 5 February 2014 19:27, Jason Gunthorpe
<jgunthorpe@obsidianresearch.com> wrote:
> I can't think of a reason to support alternate MMIO layouts if
> kvmtool is the only user and can be changed.
I expect we'll get a QEMU implementation too at some point,
but we should be able to just make that match whatever you
decide is the correct layout and behaviour.
The only constraint QEMU has which kvmtool doesn't is that
we care about not randomly shuffling things around between
QEMU versions, so whatever we pick we're more or less
stuck with. So a layout which leaves scope for "supports
PCI now, may do PCI-e later, MSI after that" would be good.
(My PCI knowledge is very piecemeal, I hope that makes sense...)
thanks
-- PMM
^ permalink raw reply
* [PATCH 2/3] PCI: ARM: add support for virtual PCI host controller
From: Jason Gunthorpe @ 2014-02-05 19:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140205190947.GA22297@mudshark.cambridge.arm.com>
On Wed, Feb 05, 2014 at 07:09:47PM +0000, Will Deacon wrote:
> Hi Arnd,
>
> Thanks for having a look.
>
> On Tue, Feb 04, 2014 at 07:13:49PM +0000, Arnd Bergmann wrote:
> > On Tuesday 04 February 2014 16:53:03 Will Deacon wrote:
> > > +
> > > +- ranges : As described in IEEE Std 1275-1994, but must provide
> > > + at least a definition of the Configuration Space plus
> > > + one or both of IO and Memory Space.
> > > +
> >
> > I might need to reread the spec, but I think the config space is not
> > actually supposed to be in the 'ranges' of the host bridge at all,
> > and it should just be listed in the 'reg'.
>
> This wasn't at all clear to me (I listed it in the cover-letter as being
> something to sort out).
When we talked about this earlier on the DT bindings list the
consensus seemed to be that configuration MMIO ranges should only be
used if the underlying memory was exactly ECAM, and was not to be used
for random configuration related register blocks.
The rational being that generic code, upon seeing that ranges entry,
could just go ahead and assume ECAM mapping.
> Now, if "reg" is definitely the correct thing to do, is it simply a matter
> of putting the Configuration Space base address in there, or do we also need
> to do the rest of what ePAPR says (expansion ROM details, ...)? I don't like
> the idea of enumerating the entire bus in the DT when we don't need to.
If you use 'reg' then it is a private base address to your driver and
you can do whatever you want with it.
Most of the ePAPR things are only needed if the FW is going to
communicate detailed information to the OS, for an environment that
relies on Linux resource assignment and discovery you can just ignore
all that.
> > This won't allow extended config space. Why not just do the
> > regular mmconfig layout and make this:
> >
> > cfg_offset(bus, device, function, register) =
> > bus << 20 | device << 15 | function << 12 | register;
>
> Is it worth adding a DT property to support both, or is ECAM the only thing
> to care about? I'm happy either way, although I'll need to hack kvmtool to
> use the new scheme.
If you use ECAM then I wonder if your driver might be a generic SBSA
driver too?
I can't think of a reason to support alternate MMIO layouts if
kvmtool is the only user and can be changed.
> > I don't think we even want "virt" in the compatible string. The
> > binding should be generic enough that it can actually work with
> > real hardware.
>
> Sure. How about "arm,pci-generic" ? Alternatives are
> "arm,pcie-generic" or "linux,pci-generic".
arm,pci-ecam-generic ?
Regards,
Jason
^ permalink raw reply
* [PATCH v2 5/5] of: document bindings for reserved-memory nodes
From: Josh Cartwright @ 2014-02-05 19:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140205100750.58EFAC40A89@trevor.secretlab.ca>
On Wed, Feb 05, 2014 at 10:07:50AM +0000, Grant Likely wrote:
> On Tue, 04 Feb 2014 13:09:33 +0100, Marek Szyprowski <m.szyprowski@samsung.com> wrote:
> > From: Grant Likely <grant.likely@linaro.org>
> > +/reserved-memory node
> > +---------------------
> > +#address-cells, #size-cells (required) - standard definition
> > + - Should use the same values as the root node
> > +#memory-region-cells (required) - dictates number of cells used in the child
> > + nodes memory-region specifier
>
> I don't think this isn't defined well enough. These reserved regions may
> not have a driver attached to them, so there is no central agent to
> decide what the specifier means. That leaves the interpretation of
> the memory region in the hands of the client drivers. How do you see the
> specifier getting parsed and used?
I had a specific usecase in mind when I added it, although admittedly I
haven't yet worked through all of the details.
On MSM chips, there is a region of memory accessible from the various
processors on the SoC. This region is used for (among other things)
inter-processor communication. Inside this region, a heap allocation
protocol is implemented by software on all interested processors.
Consumers of this shared memory heap specify a 32-bit identifier and a
size, and are either given a matching preexisting chunk (for example,
another processor has already allocated a chunk with the corresponding
identifier), or are allocated a new chunk for that identifier out of the
region.
Given it's shared nature, this region has some specific requirements
about how it may be accessed by the kernel (specifically regarding
cacheability/how it's mapped), which means it at least needs _some_
representation in a reserved-memory node.
I had envisioned expressing the shared memory/consumer relationship in
the device tree:
reserved-memory {
smem : smem {
compatible = "qcom,shared-memory";
reg = <...>;
#memory-region-cells = <2>;
no-map;
};
};
consumer {
/* ... */;
memory-region = <&smem 0xDEADBEEF 0x1000>;
};
That is, the heap protocol implementation exists as a "driver" for the
smem reserved-memory node, and the consumer's 2-cell specifier is a
32-bit identifier and a size.
If your concern is for the case where a "qcom,shared-memory" node is
specified in a device tree, but the "driver" hasn't been built into the
kernel, then the appropriate behavior would be the same as the DMA/CMA
case: fallback to a default case of memblock_reserve/memblock_remove'ing
the region.
Would using reserved-memory in this way be outside the scope of what was
originally intended?
Thanks,
Josh
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
^ permalink raw reply
* [PATCH v3 3/6] misc: fuse: Add efuse driver for Tegra
From: Stephen Warren @ 2014-02-05 19:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390952176-30402-4-git-send-email-pdeschrijver@nvidia.com>
On 01/28/2014 04:36 PM, Peter De Schrijver wrote:
> Implement fuse driver for Tegra20, Tegra30, Tegra114 and Tegra124.
I assume most of this code is simply cut/paste from the existing code in
arch/arm/mach-tegra/? If so, "git format-patch -C" would have been
useful to highlight what changed when duplicating the files.
> diff --git a/Documentation/ABI/testing/sysfs-driver-tegra-fuse b/Documentation/ABI/testing/sysfs-driver-tegra-fuse
> +What: /sys/devices/*/<our-device>/fuse
> +Date: December 2013
> +Contact: Peter De Schrijver <pdeschrijver@nvidia.com>
> +Description: read-only access to the efuses on Tegra20, Tegra30, Tegra114
> + and Tegra124 SoC's from NVIDIA. The efuses contain write once
> + data programmed at the factory.
> +Users: any user space application which wants to read the efuses on
> + Tegra SoC's
Surely this file should describe the format of the file, since that's
part of the ABI too, right?
> diff --git a/drivers/misc/fuse/tegra/fuse-tegra20.c b/drivers/misc/fuse/tegra/fuse-tegra20.c
> +static int tegra20_fuse_probe(struct platform_device *pdev)
...
> + sku_info.revision = tegra_revision;
> + tegra20_init_speedo_data(&sku_info, &pdev->dev);
...
> +}
> +
> +static struct platform_driver tegra20_fuse_driver = {
> + .probe = tegra20_fuse_probe,
> + .driver = {
> + .name = "tegra20_fuse",
> + .owner = THIS_MODULE,
> + .of_match_table = tegra20_fuse_of_match,
> + }
> +};
> +
> +static int __init tegra20_fuse_init(void)
> +{
> + return platform_driver_register(&tegra20_fuse_driver);
> +}
> +postcore_initcall(tegra20_fuse_init);
That call to tegra20_init_speedo_data() now happens much later in boot.
Are you sure there's nothing that relies on data it sets up between when
tegra_fuse_init() is called (which is where it happens before this
series), and the somewhat arbitrary later time when this driver probes?
> diff --git a/drivers/misc/fuse/tegra/fuse-tegra30.c b/drivers/misc/fuse/tegra/fuse-tegra30.c
> +postcore_initcall(tegra30_fuse_init);
> +
There's a blank line at the end of the file. I thought checkpatch warned
about this? But actually it doesn't seem to at least in -f mode.
> diff --git a/drivers/misc/fuse/tegra/fuse.h b/drivers/misc/fuse/tegra/fuse.h
> +struct tegra_sku_info {
> + int sku_id;
> + int cpu_process_id;
> + int cpu_speedo_id;
> + int cpu_speedo_value;
> + int cpu_iddq_value;
> + int core_process_id;
> + int soc_speedo_id;
> + int gpu_speedo_id;
> + int gpu_process_id;
> + int gpu_speedo_value;
> + enum tegra_revision revision;
> +};
The only use of this appears to be to pass to tegra_fuse_create_sysfs()
which prints out the fields. Will there be more users in the future?
Otherwise, I'd be tempted to just print it out outside/before-calling
tegra_fuse_create_sysfs().
That said, I wonder if these values could/should be exposed in the sysfs
file to make it easier to interpret the fuses?
> diff --git a/drivers/misc/fuse/tegra/tegra114_speedo.c b/drivers/misc/fuse/tegra/tegra114_speedo.c
It might be nice to make these filenames consistent with the others,
e.g. fuse-speedo-tegraNNN.c/speedo-tegraNNN.c, or wrap them into
fuse-tegraNNN.c?
> diff --git a/drivers/misc/fuse/tegra/tegra30_speedo.c b/drivers/misc/fuse/tegra/tegra30_speedo.c
> +#define FUSE_SPEEDO_CALIB_0 0x14
> +#define FUSE_PACKAGE_INFO 0XFC
> +#define FUSE_TEST_PROG_VER 0X28
In arch/arm/mach-tegra/tegra30_speedo.c, those values are different:
#define FUSE_SPEEDO_CALIB_0 0x114
#define FUSE_PACKAGE_INFO 0X1FC
#define FUSE_TEST_PROG_VER 0X128
Was this change intentional? Perhaps it should be in a separate patch to
highlight the change, if it's an intentional bug-fix?
^ permalink raw reply
* [PATCH v5 02/20] clocksource: orion: Use atomic access for shared registers
From: Jason Cooper @ 2014-02-05 19:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390836440-12744-3-git-send-email-ezequiel.garcia@free-electrons.com>
Daniel,
On Mon, Jan 27, 2014 at 12:27:02PM -0300, Ezequiel Garcia wrote:
> Replace the driver-specific thread-safe shared register API
> by the recently introduced atomic_io_clear_set().
>
> Cc: Daniel Lezcano <daniel.lezcano@linaro.org>
> Tested-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
> Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
> ---
> drivers/clocksource/time-orion.c | 28 ++++++++++------------------
> 1 file changed, 10 insertions(+), 18 deletions(-)
The MMIO patch this depends on:
c5ca95b507c8 ARM: 7930/1: Introduce atomic MMIO modify
made it in to v3.14-rc1. It looks like this change is independent of
the rest of the watchdog series, so:
Acked-by: Jason Cooper <jason@lakedaemon.net>
thx,
Jason.
> diff --git a/drivers/clocksource/time-orion.c b/drivers/clocksource/time-orion.c
> index 9c7f018..3f14e56 100644
> --- a/drivers/clocksource/time-orion.c
> +++ b/drivers/clocksource/time-orion.c
> @@ -35,20 +35,6 @@
> #define ORION_ONESHOT_MAX 0xfffffffe
>
> static void __iomem *timer_base;
> -static DEFINE_SPINLOCK(timer_ctrl_lock);
> -
> -/*
> - * Thread-safe access to TIMER_CTRL register
> - * (shared with watchdog timer)
> - */
> -void orion_timer_ctrl_clrset(u32 clr, u32 set)
> -{
> - spin_lock(&timer_ctrl_lock);
> - writel((readl(timer_base + TIMER_CTRL) & ~clr) | set,
> - timer_base + TIMER_CTRL);
> - spin_unlock(&timer_ctrl_lock);
> -}
> -EXPORT_SYMBOL(orion_timer_ctrl_clrset);
>
> /*
> * Free-running clocksource handling.
> @@ -68,7 +54,8 @@ static int orion_clkevt_next_event(unsigned long delta,
> {
> /* setup and enable one-shot timer */
> writel(delta, timer_base + TIMER1_VAL);
> - orion_timer_ctrl_clrset(TIMER1_RELOAD_EN, TIMER1_EN);
> + atomic_io_modify(timer_base + TIMER_CTRL,
> + TIMER1_RELOAD_EN | TIMER1_EN, TIMER1_EN);
>
> return 0;
> }
> @@ -80,10 +67,13 @@ static void orion_clkevt_mode(enum clock_event_mode mode,
> /* setup and enable periodic timer at 1/HZ intervals */
> writel(ticks_per_jiffy - 1, timer_base + TIMER1_RELOAD);
> writel(ticks_per_jiffy - 1, timer_base + TIMER1_VAL);
> - orion_timer_ctrl_clrset(0, TIMER1_RELOAD_EN | TIMER1_EN);
> + atomic_io_modify(timer_base + TIMER_CTRL,
> + TIMER1_RELOAD_EN | TIMER1_EN,
> + TIMER1_RELOAD_EN | TIMER1_EN);
> } else {
> /* disable timer */
> - orion_timer_ctrl_clrset(TIMER1_RELOAD_EN | TIMER1_EN, 0);
> + atomic_io_modify(timer_base + TIMER_CTRL,
> + TIMER1_RELOAD_EN | TIMER1_EN, 0);
> }
> }
>
> @@ -131,7 +121,9 @@ static void __init orion_timer_init(struct device_node *np)
> /* setup timer0 as free-running clocksource */
> writel(~0, timer_base + TIMER0_VAL);
> writel(~0, timer_base + TIMER0_RELOAD);
> - orion_timer_ctrl_clrset(0, TIMER0_RELOAD_EN | TIMER0_EN);
> + atomic_io_modify(timer_base + TIMER_CTRL,
> + TIMER0_RELOAD_EN | TIMER0_EN,
> + TIMER0_RELOAD_EN | TIMER0_EN);
> clocksource_mmio_init(timer_base + TIMER0_VAL, "orion_clocksource",
> clk_get_rate(clk), 300, 32,
> clocksource_mmio_readl_down);
> --
> 1.8.1.5
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH 2/3] PCI: ARM: add support for virtual PCI host controller
From: Will Deacon @ 2014-02-05 19:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <3460536.Sh23gjDa6X@wuerfel>
Hi Arnd,
Thanks for having a look.
On Tue, Feb 04, 2014 at 07:13:49PM +0000, Arnd Bergmann wrote:
> On Tuesday 04 February 2014 16:53:03 Will Deacon wrote:
> > +
> > +- ranges : As described in IEEE Std 1275-1994, but must provide
> > + at least a definition of the Configuration Space plus
> > + one or both of IO and Memory Space.
> > +
>
> I might need to reread the spec, but I think the config space is not
> actually supposed to be in the 'ranges' of the host bridge at all,
> and it should just be listed in the 'reg'.
This wasn't at all clear to me (I listed it in the cover-letter as being
something to sort out).
> IIRC the reason why the config space is part of the three-cell address
> is so that you can have funky ways to say "memory space of the device
> with bus/dev/fn is actually translated to address X rather then Y".
>
> It's too late to change that for the other drivers now, after the
> binding is established.
The spec is based on the idea that open-firmware enumerates your entire PCI
bus topology, then provides the Conriguation Space address for each device
using a reg property.
Since:
(a) This doesn't match what we're planning to support
(b) Runs the risk of making the "reg" encoding something specific to this
driver
(c) Doesn't match how we describe Memory and IO Spaces
(d) There is already precendence in mainline
I chose to use "ranges" instead.
Now, if "reg" is definitely the correct thing to do, is it simply a matter
of putting the Configuration Space base address in there, or do we also need
to do the rest of what ePAPR says (expansion ROM details, ...)? I don't like
the idea of enumerating the entire bus in the DT when we don't need to.
> > +Configuration Space is assumed to be memory-mapped (as opposed to being
> > +accessed via an ioport) and laid out with a direct correspondence to the
> > +geography of a PCI bus address, by concatenating the various components
> > +to form a 24-bit offset:
> > +
> > + cfg_offset(bus, device, function, register) =
> > + bus << 16 | device << 11 | function << 8 | register
>
> This won't allow extended config space. Why not just do the
> regular mmconfig layout and make this:
>
> cfg_offset(bus, device, function, register) =
> bus << 20 | device << 15 | function << 12 | register;
Is it worth adding a DT property to support both, or is ECAM the only thing
to care about? I'm happy either way, although I'll need to hack kvmtool to
use the new scheme.
> > +static int virt_pci_setup(int nr, struct pci_sys_data *sys)
> > +{
> > + struct virt_pci *pci = sys->private_data;
> > +
> > + if (resource_type(&pci->io)) {
> > + pci_add_resource(&sys->resources, &pci->io);
> > + pci_ioremap_io(nr * resource_size(&pci->io), pci->io.start);
> > + }
>
> This should really compute an io_offset.
>
> > + if (resource_type(&pci->mem))
> > + pci_add_resource(&sys->resources, &pci->mem);
>
> and also a mem_offset, which is something different.
As somebody new to PCI, I'm afraid you've lost me here. Are you referring to
using pci_add_resource_offset instead, then removing my restriction on
having a single resource from the parsing code?
> > + pci->cfg_base = devm_ioremap_resource(pci->dev, &pci->cfg);
> > + return !IS_ERR(pci->cfg_base);
> > +}
> > +
> > +static const struct of_device_id virt_pci_of_match[] = {
> > + { .compatible = "linux,pci-virt" },
> > + { },
> > +};
> > +MODULE_DEVICE_TABLE(of, virt_pci_of_match);
>
> I don't think we even want "virt" in the compatible string. The
> binding should be generic enough that it can actually work with
> real hardware.
Sure. How about "arm,pci-generic" ? Alternatives are
"arm,pcie-generic" or "linux,pci-generic".
> > + for_each_of_pci_range(&parser, &range) {
> > + u32 restype = range.flags & IORESOURCE_TYPE_BITS;
> > +
> > + switch (restype) {
> > + case IORESOURCE_IO:
> > + if (resource_type(&pci->io))
> > + dev_warn(dev,
> > + "ignoring additional io resource\n");
> > + else
> > + of_pci_range_to_resource(&range, np, &pci->io);
> > + break;
> > + case IORESOURCE_MEM:
> > + if (resource_type(&pci->mem))
> > + dev_warn(dev,
> > + "ignoring additional mem resource\n");
> > + else
> > + of_pci_range_to_resource(&range, np, &pci->mem);
> > + break;
>
> This shows once more that the range parser code is suboptimal. So far
> every single driver got the I/O space wrong here, because the obvious
> way to write this function is also completely wrong.
I see you mentioned to Liviu that you should register a logical resource,
rather than physical resource returned from the parser. It seems odd that
I/O space appears to work with this code as-is (I've tested it on arm using
kvmtool by removing the memory bars).
> What you get out of "of_pci_range_to_resource(&range, np, &pci->io)"
> is not the resource you want to pass into pci_add_resource()
> later.
Do I need to open-code the resource translation from phys -> logical?
>
> > + memset(&hw, 0, sizeof(hw));
> > + hw.nr_controllers = 1;
> > + hw.private_data = (void **)&pci;
> > + hw.setup = virt_pci_setup;
> > + hw.map_irq = of_irq_parse_and_map_pci;
> > + hw.ops = &virt_pci_ops;
> > + pci_common_init_dev(dev, &hw);
>
> Since most fields here are constant, I'd just write this as
>
> struct hw_pci hw = {
> .nr_controllers = 1,
> .setup = virt_pci_setup,
> ...
> };
Can do.
Thanks,
Will
^ permalink raw reply
* [PATCH v5 00/20] Armada 370/XP watchdog support
From: Jason Cooper @ 2014-02-05 19:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390836440-12744-1-git-send-email-ezequiel.garcia@free-electrons.com>
Wim,
On Mon, Jan 27, 2014 at 12:27:00PM -0300, Ezequiel Garcia wrote:
> A new round, mostly fixing some minor nitpicks.
>
> This entire series depends on latest irqchip-orion fixes by Sebastian.
> Namely, this one: https://lkml.org/lkml/2014/1/23/594.
>
> How should we handle this dependency?
As this series depends on another branch we are handling (currently
mvebu-next/irqchip-fixes, destined for tglx's tree), would you mind if
we took this series through arm-soc with your Ack?
thx,
Jason.
>
> Changes from v4 are:
>
> * Provided better commit subject and commit log for patch 7:
> "watchdog: orion: Handle the interrupt so it's properly acked".
>
> * Corrected the misnamed fuction try_rstout_ioremap().
>
> * A bunch of s/interruption/interrupt fixes
>
> * Dropped the '0' as a valid IRQ in the platform_get_irq() check, given
> it should return a positive virq-space number.
>
> Changes from v3 are:
>
> * It wasn't nice to break DT compatibility by adding a second resource
> requirement, so we provided a fallback to use the RSTOUT address.
> All in all, the solution doesn't look too bad.
>
> * Added a full watchdog stop at driver probe time, *before* the call
> to request_irq().
>
> Notice that currently the request_irq() doesn't seem to clear the
> pending interrupt. This means the BRIDGE_CAUSE clear removal is
> still not safe.
>
> This should be fixed sooner than later and, of course, before this
> gets merged.
>
> * Rework the interrupt request, to use devm_request_irq() and
> avoid dealing with IRQ releasing.
>
> * Added proper clock error handling and fixed the probe() error path.
>
> * Typos and minor issues got fixed
>
> Changes from v2:
>
> * Add proper error checking on clk_prepare_enable() and return
> PTR_ERR instead of ENODEV. Suggested by Fabio Estevam.
>
> * After the usage of the atomic I/O and considering the watchdog core
> does its own serialization, the driver's spinlock was completely
> redundant and was removed. Also suggested by Fabio.
>
> * Instead of making the driver dependent on PLAT_ORION, added a dependency
> to ARCH_MVEBU. This was proposed by Sebastian and Andrew, given
> we're working on PLAT_ORION removal.
>
> Changes from v1:
>
> * Watchdog RSTOUT enable.
> While v1 enabled the RSTOUT at each machine initialization, Jason Gunthorpe
> later pointed out [2] that such enabling might lead to a spurious watchdog
> trigger, in the event of the watchdog expired event not being cleared.
>
> Therefore, the current patchset adds RSTOUT as a second address resource
> (or 'reg' entry in devicetree words) to allow different platforms specify
> the corresponding address of the register. This change allows to build the
> driver on multiplatforms builds as helps remove a mach-specific header.
>
> The drawback of this is that the DT backwards compatibility gets broken;
> this was timely discussed but no better solution was achieved or proposed.
>
> * BRIDGE CAUSE clear removal
> The watchdog cause clear should be done by the bridge irqchip driver, so
> it's fine to remove it from the watchdog driver and instead request the
> interrupt.
>
> However, there are still a few platforms (orion5x, and legacy
> kirkwood/dove) that doesn't have this bridge irqchip support enabled.
> On these platforms the bridge cause clear is simply *not* done.
>
> If we are paranoid about this, maybe we can simply add the clear on each
> mach-xxx/irq.c, together with the other irq is initialization.
>
> Once again, thanks to everyone who helped reviewing this.
>
> Ezequiel Garcia (20):
> ARM: Introduce atomic MMIO modify
> clocksource: orion: Use atomic access for shared registers
> watchdog: orion: Add clock error handling
> watchdog: orion: Use atomic access for shared registers
> watchdog: orion: Remove unused macros
> watchdog: orion: Make sure the watchdog is initially stopped
> watchdog: orion: Handle the interrupt so it's properly acked
> watchdog: orion: Make RSTOUT register a separate resource
> watchdog: orion: Remove unneeded BRIDGE_CAUSE clear
> watchdog: orion: Introduce an orion_watchdog device structure
> watchdog: orion: Introduce per-compatible of_device_id data
> watchdog: orion: Add per-compatible clock initialization
> watchdog: orion: Add per-compatible watchdog start implementation
> watchdog: orion: Add support for Armada 370 and Armada XP SoC
> ARM: mvebu: Enable Armada 370/XP watchdog in the devicetree
> ARM: kirkwood: Add RSTOUT 'reg' entry to devicetree
> ARM: dove: Enable Dove watchdog in the devicetree
> watchdog: orion: Enable the build on ARCH_MVEBU
> ARM: mvebu: Enable watchdog support in defconfig
> ARM: dove: Enable watchdog support in the defconfig
>
> .../devicetree/bindings/watchdog/marvel.txt | 8 +-
> arch/arm/boot/dts/armada-370-xp.dtsi | 4 +
> arch/arm/boot/dts/armada-370.dtsi | 5 +
> arch/arm/boot/dts/armada-xp.dtsi | 6 +
> arch/arm/boot/dts/dove.dtsi | 8 +
> arch/arm/boot/dts/kirkwood.dtsi | 2 +-
> arch/arm/configs/dove_defconfig | 2 +
> arch/arm/configs/mvebu_defconfig | 2 +
> arch/arm/include/asm/io.h | 6 +
> arch/arm/kernel/io.c | 35 ++
> arch/arm/mach-dove/include/mach/bridge-regs.h | 1 +
> arch/arm/mach-kirkwood/include/mach/bridge-regs.h | 1 +
> arch/arm/mach-mv78xx0/include/mach/bridge-regs.h | 1 +
> arch/arm/mach-orion5x/include/mach/bridge-regs.h | 1 +
> arch/arm/plat-orion/common.c | 10 +-
> drivers/clocksource/time-orion.c | 28 +-
> drivers/watchdog/Kconfig | 2 +-
> drivers/watchdog/orion_wdt.c | 369 ++++++++++++++++-----
> 18 files changed, 383 insertions(+), 108 deletions(-)
>
> --
> 1.8.1.5
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH v3 6/6] ARM: dts: AM33XX: Add ecap interrupt properties
From: Matt Porter @ 2014-02-05 19:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1391626901-31684-1-git-send-email-mporter@linaro.org>
Add missing interrupt properties to the ecap0, ecap1, and ecap2
nodes.
Signed-off-by: Matt Porter <mporter@linaro.org>
---
arch/arm/boot/dts/am33xx.dtsi | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
index 6d95d3d..b4139ba 100644
--- a/arch/arm/boot/dts/am33xx.dtsi
+++ b/arch/arm/boot/dts/am33xx.dtsi
@@ -582,6 +582,8 @@
compatible = "ti,am33xx-ecap";
#pwm-cells = <3>;
reg = <0x48300100 0x80>;
+ interrupts = <31>;
+ interrupt-names = "ecap0";
ti,hwmods = "ecap0";
status = "disabled";
};
@@ -610,6 +612,8 @@
compatible = "ti,am33xx-ecap";
#pwm-cells = <3>;
reg = <0x48302100 0x80>;
+ interrupts = <47>;
+ interrupt-names = "ecap1";
ti,hwmods = "ecap1";
status = "disabled";
};
@@ -638,6 +642,8 @@
compatible = "ti,am33xx-ecap";
#pwm-cells = <3>;
reg = <0x48304100 0x80>;
+ interrupts = <61>;
+ interrupt-names = "ecap2";
ti,hwmods = "ecap2";
status = "disabled";
};
--
1.8.4
^ permalink raw reply related
* [PATCH v3 5/6] pwm: enable TI PWMSS if the IIO tiecap driver is selected
From: Matt Porter @ 2014-02-05 19:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1391626901-31684-1-git-send-email-mporter@linaro.org>
The IIO TI ECAP driver depends on the TI PWMSS management
driver in this subsystem. Enable PWMSS when the IIO TI ECAP
driver is selected.
Signed-off-by: Matt Porter <mporter@linaro.org>
---
drivers/pwm/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 22f2f28..bd3cc65 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -219,7 +219,7 @@ config PWM_TIEHRPWM
config PWM_TIPWMSS
bool
- default y if SOC_AM33XX && (PWM_TIECAP || PWM_TIEHRPWM)
+ default y if SOC_AM33XX && (IIO_TIECAP || PWM_TIECAP || PWM_TIEHRPWM)
help
PWM Subsystem driver support for AM33xx SOC.
--
1.8.4
^ permalink raw reply related
* [PATCH v3 4/6] iio: Add ABI docs for pulse capture devices
From: Matt Porter @ 2014-02-05 19:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1391626901-31684-1-git-send-email-mporter@linaro.org>
Add standard ABI entries for pulse capture devices. Also add
a separate ABI entry for the TI ECAP driver polarity option.
Signed-off-by: Matt Porter <mporter@linaro.org>
---
Documentation/ABI/testing/sysfs-bus-iio | 18 ++++++++++++++++++
Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap | 9 +++++++++
2 files changed, 27 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap
diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index 6e02c50..918a201 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -210,6 +210,14 @@ Contact: linux-iio at vger.kernel.org
Description:
Scaled humidity measurement in milli percent.
+What: /sys/bus/iio/devices/iio:deviceX/in_pulseY_raw
+What: /sys/bus/iio/devices/iio:deviceX/in_pulse_raw
+KernelVersion: 3.15
+Contact: linux-iio at vger.kernel.org
+Description:
+ Raw pulse measurement from channel Y. Units after
+ application of scale and offset are nanoseconds.
+
What: /sys/bus/iio/devices/iio:deviceX/in_accel_offset
What: /sys/bus/iio/devices/iio:deviceX/in_accel_x_offset
What: /sys/bus/iio/devices/iio:deviceX/in_accel_y_offset
@@ -220,6 +228,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_tempY_offset
What: /sys/bus/iio/devices/iio:deviceX/in_temp_offset
What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_offset
What: /sys/bus/iio/devices/iio:deviceX/in_pressure_offset
+What: /sys/bus/iio/devices/iio:deviceX/in_pulseY_offset
+What: /sys/bus/iio/devices/iio:deviceX/in_pulse_offset
KernelVersion: 2.6.35
Contact: linux-iio at vger.kernel.org
Description:
@@ -251,6 +261,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_magn_y_scale
What: /sys/bus/iio/devices/iio:deviceX/in_magn_z_scale
What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_scale
What: /sys/bus/iio/devices/iio:deviceX/in_pressure_scale
+What: /sys/bus/iio/devices/iio:deviceX/in_pulseY_scale
+What: /sys/bus/iio/devices/iio:deviceX/in_pulse_scale
KernelVersion: 2.6.35
Contact: linux-iio at vger.kernel.org
Description:
@@ -784,6 +796,8 @@ What: /sys/.../iio:deviceX/scan_elements/in_incli_x_en
What: /sys/.../iio:deviceX/scan_elements/in_incli_y_en
What: /sys/.../iio:deviceX/scan_elements/in_pressureY_en
What: /sys/.../iio:deviceX/scan_elements/in_pressure_en
+What: /sys/.../iio:deviceX/scan_elements/in_pulseY_en
+What: /sys/.../iio:deviceX/scan_elements/in_pulse_en
KernelVersion: 2.6.37
Contact: linux-iio at vger.kernel.org
Description:
@@ -799,6 +813,8 @@ What: /sys/.../iio:deviceX/scan_elements/in_voltageY_supply_type
What: /sys/.../iio:deviceX/scan_elements/in_timestamp_type
What: /sys/.../iio:deviceX/scan_elements/in_pressureY_type
What: /sys/.../iio:deviceX/scan_elements/in_pressure_type
+What: /sys/.../iio:deviceX/scan_elements/in_pulseY_type
+What: /sys/.../iio:deviceX/scan_elements/in_pulse_type
KernelVersion: 2.6.37
Contact: linux-iio at vger.kernel.org
Description:
@@ -845,6 +861,8 @@ What: /sys/.../iio:deviceX/scan_elements/in_incli_y_index
What: /sys/.../iio:deviceX/scan_elements/in_timestamp_index
What: /sys/.../iio:deviceX/scan_elements/in_pressureY_index
What: /sys/.../iio:deviceX/scan_elements/in_pressure_index
+What: /sys/.../iio:deviceX/scan_elements/in_pulseY_index
+What: /sys/.../iio:deviceX/scan_elements/in_pulse_index
KernelVersion: 2.6.37
Contact: linux-iio at vger.kernel.org
Description:
diff --git a/Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap b/Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap
new file mode 100644
index 0000000..a9e4a9f
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap
@@ -0,0 +1,9 @@
+What: /sys/bus/iio/devices/iio:deviceX/pulse_polarityY
+What: /sys/bus/iio/devices/iio:deviceX/pulse_polarity
+Date: January 2014
+KernelVersion: 3.15
+Contact: Matt Porter <mporter@linaro.org>
+Description:
+ Get and set the polarity of the pulse signal to be captured
+ for channel Y. 1 indicates a high pulse signal and 0
+ indicates a low pulse signal.
--
1.8.4
^ permalink raw reply related
* [PATCH v3 3/6] iio: enable selection and build of pulse drivers
From: Matt Porter @ 2014-02-05 19:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1391626901-31684-1-git-send-email-mporter@linaro.org>
Add the pulse driver subdirectory when configuring and building
IIO.
Signed-off-by: Matt Porter <mporter@linaro.org>
---
drivers/iio/Kconfig | 1 +
drivers/iio/Makefile | 1 +
2 files changed, 2 insertions(+)
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 5dd0e12..286acc3 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -74,6 +74,7 @@ if IIO_TRIGGER
source "drivers/iio/trigger/Kconfig"
endif #IIO_TRIGGER
source "drivers/iio/pressure/Kconfig"
+source "drivers/iio/pulse/Kconfig"
source "drivers/iio/temperature/Kconfig"
endif # IIO
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 887d390..9a953c9 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -24,5 +24,6 @@ obj-y += light/
obj-y += magnetometer/
obj-y += orientation/
obj-y += pressure/
+obj-y += pulse/
obj-y += temperature/
obj-y += trigger/
--
1.8.4
^ permalink raw reply related
* [PATCH v3 2/6] iio: pulse: add TI ECAP driver
From: Matt Porter @ 2014-02-05 19:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1391626901-31684-1-git-send-email-mporter@linaro.org>
Adds support for capturing PWM signals using the TI ECAP peripheral.
This driver supports triggered buffer capture of pulses on multiple
ECAP instances. In addition, the driver supports configurable polarity
of the signal to be captured.
Signed-off-by: Matt Porter <mporter@linaro.org>
---
drivers/iio/pulse/Kconfig | 20 ++
drivers/iio/pulse/Makefile | 6 +
drivers/iio/pulse/tiecap.c | 491 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 517 insertions(+)
create mode 100644 drivers/iio/pulse/Kconfig
create mode 100644 drivers/iio/pulse/Makefile
create mode 100644 drivers/iio/pulse/tiecap.c
diff --git a/drivers/iio/pulse/Kconfig b/drivers/iio/pulse/Kconfig
new file mode 100644
index 0000000..9864d4b
--- /dev/null
+++ b/drivers/iio/pulse/Kconfig
@@ -0,0 +1,20 @@
+#
+# Pulse Capture Devices
+#
+# When adding new entries keep the list in alphabetical order
+
+menu "Pulse Capture Devices"
+
+config IIO_TIECAP
+ tristate "TI ECAP Pulse Capture"
+ depends on SOC_AM33XX
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ If you say yes here you get support for the TI ECAP peripheral
+ in pulse capture mode.
+
+ This driver can also be built as a module. If so, the module
+ will be called tiecap
+
+endmenu
diff --git a/drivers/iio/pulse/Makefile b/drivers/iio/pulse/Makefile
new file mode 100644
index 0000000..94d4b00
--- /dev/null
+++ b/drivers/iio/pulse/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for IIO PWM Capture Devices
+#
+
+# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_IIO_TIECAP) += tiecap.o
diff --git a/drivers/iio/pulse/tiecap.c b/drivers/iio/pulse/tiecap.c
new file mode 100644
index 0000000..fd96745
--- /dev/null
+++ b/drivers/iio/pulse/tiecap.c
@@ -0,0 +1,491 @@
+/*
+ * ECAP IIO pulse capture driver
+ *
+ * Copyright (C) 2014 Linaro Limited
+ * Author: Matt Porter <mporter@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include "../../pwm/pwm-tipwmss.h"
+
+/* ECAP regs and bits */
+#define CAP1 0x08
+#define CAP2 0x0c
+#define ECCTL1 0x28
+#define ECCTL1_RUN_FREE BIT(15)
+#define ECCTL1_CAPLDEN BIT(8)
+#define ECCTL1_CAP2POL BIT(2)
+#define ECCTL1_CTRRST1 BIT(1)
+#define ECCTL1_CAP1POL BIT(0)
+#define ECCTL2 0x2a
+#define ECCTL2_SYNCO_SEL_DIS BIT(7)
+#define ECCTL2_TSCTR_FREERUN BIT(4)
+#define ECCTL2_REARM BIT(3)
+#define ECCTL2_STOP_WRAP_2 BIT(1)
+#define ECEINT 0x2c
+#define ECFLG 0x2e
+#define ECCLR 0x30
+#define ECINT_CTRCMP BIT(7)
+#define ECINT_CTRPRD BIT(6)
+#define ECINT_CTROVF BIT(5)
+#define ECINT_CEVT4 BIT(4)
+#define ECINT_CEVT3 BIT(3)
+#define ECINT_CEVT2 BIT(2)
+#define ECINT_CEVT1 BIT(1)
+#define ECINT_ALL (ECINT_CTRCMP | \
+ ECINT_CTRPRD | \
+ ECINT_CTROVF | \
+ ECINT_CEVT4 | \
+ ECINT_CEVT3 | \
+ ECINT_CEVT2 | \
+ ECINT_CEVT1)
+
+/* ECAP driver flags */
+#define ECAP_POLARITY_HIGH BIT(1)
+#define ECAP_ENABLED BIT(0)
+
+struct ecap_context {
+ u32 cap1;
+ u32 cap2;
+ u16 ecctl1;
+ u16 ecctl2;
+ u16 eceint;
+};
+
+struct ecap_state {
+ unsigned long flags;
+ unsigned int clk_rate;
+ void __iomem *regs;
+ u32 *buf;
+ struct ecap_context ctx;
+};
+
+#define dev_to_ecap_state(d) iio_priv(dev_to_iio_dev(d))
+
+static const struct iio_chan_spec ecap_channels[] = {
+ {
+ .type = IIO_PULSE,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 32,
+ .storagebits = 32,
+ .endianness = IIO_LE,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(1)
+};
+
+static ssize_t ecap_attr_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ecap_state *state = dev_to_ecap_state(dev);
+
+ return sprintf(buf, "%d\n",
+ test_bit(ECAP_POLARITY_HIGH, &state->flags));
+}
+
+static ssize_t ecap_attr_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ int ret;
+ bool val;
+ struct ecap_state *state = dev_to_ecap_state(dev);
+
+ if (test_bit(ECAP_ENABLED, &state->flags))
+ return -EINVAL;
+
+ ret = strtobool(buf, &val);
+ if (ret)
+ return ret;
+
+ if (val)
+ set_bit(ECAP_POLARITY_HIGH, &state->flags);
+ else
+ clear_bit(ECAP_POLARITY_HIGH, &state->flags);
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(pulse_polarity, S_IRUGO | S_IWUSR,
+ ecap_attr_show, ecap_attr_store, 0);
+
+static struct attribute *ecap_attributes[] = {
+ &iio_dev_attr_pulse_polarity.dev_attr.attr,
+ NULL,
+};
+
+static struct attribute_group ecap_attribute_group = {
+ .attrs = ecap_attributes,
+};
+
+static int ecap_read_raw(struct iio_dev *idev,
+ struct iio_chan_spec const *ch, int *val,
+ int *val2, long mask)
+{
+ struct ecap_state *state = iio_priv(idev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ /*
+ * Always return 0 as a pulse width sample
+ * is only valid in a triggered condition
+ */
+ *val = 0;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = NSEC_PER_SEC / state->clk_rate;
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info ecap_info = {
+ .driver_module = THIS_MODULE,
+ .attrs = &ecap_attribute_group,
+ .read_raw = &ecap_read_raw,
+};
+
+static irqreturn_t ecap_trigger_handler(int irq, void *private)
+{
+ struct iio_poll_func *pf = private;
+ struct iio_dev *idev = pf->indio_dev;
+ struct ecap_state *state = iio_priv(idev);
+
+ /* Read pulse counter value */
+ *state->buf = readl(state->regs + CAP2);
+
+ iio_push_to_buffers_with_timestamp(idev, state->buf, iio_get_time_ns());
+
+ iio_trigger_notify_done(idev->trig);
+
+ return IRQ_HANDLED;
+};
+
+
+static const struct iio_trigger_ops iio_interrupt_trigger_ops = {
+ .owner = THIS_MODULE,
+};
+
+static irqreturn_t ecap_interrupt_handler(int irq, void *private)
+{
+ struct iio_dev *idev = private;
+ struct ecap_state *state = iio_priv(idev);
+ u16 ints;
+
+ iio_trigger_poll(idev->trig, 0);
+
+ /* Clear CAP2 interrupt */
+ ints = readw(state->regs + ECFLG);
+ if (ints & ECINT_CEVT2)
+ writew(ECINT_CEVT2, state->regs + ECCLR);
+ else
+ dev_warn(&idev->dev, "unhandled interrupt flagged: %04x\n",
+ ints);
+
+ return IRQ_HANDLED;
+}
+
+static int ecap_buffer_predisable(struct iio_dev *idev)
+{
+ struct ecap_state *state = iio_priv(idev);
+ int ret = 0;
+ u16 ecctl2;
+
+ /* Stop capture */
+ clear_bit(ECAP_ENABLED, &state->flags);
+ ecctl2 = readw(state->regs + ECCTL2) & ~ECCTL2_TSCTR_FREERUN;
+ writew(ecctl2, state->regs + ECCTL2);
+
+ /* Disable and clear all interrupts */
+ writew(0, state->regs + ECEINT);
+ writew(ECINT_ALL, state->regs + ECCLR);
+
+ ret = iio_triggered_buffer_predisable(idev);
+
+ pm_runtime_put_sync(idev->dev.parent);
+
+ return ret;
+}
+
+static int ecap_buffer_postenable(struct iio_dev *idev)
+{
+ struct ecap_state *state = iio_priv(idev);
+ int ret = 0;
+ u16 ecctl1, ecctl2;
+
+ pm_runtime_get_sync(idev->dev.parent);
+
+ /* Configure pulse polarity */
+ ecctl1 = readw(state->regs + ECCTL1);
+ if (test_bit(ECAP_POLARITY_HIGH, &state->flags)) {
+ /* CAP1 rising, CAP2 falling */
+ ecctl1 |= ECCTL1_CAP2POL;
+ ecctl1 &= ~ECCTL1_CAP1POL;
+ } else {
+ /* CAP1 falling, CAP2 rising */
+ ecctl1 &= ~ECCTL1_CAP2POL;
+ ecctl1 |= ECCTL1_CAP1POL;
+ }
+ writew(ecctl1, state->regs + ECCTL1);
+
+ /* Enable CAP2 interrupt */
+ writew(ECINT_CEVT2, state->regs + ECEINT);
+
+ /* Enable capture */
+ ecctl2 = readw(state->regs + ECCTL2);
+ ecctl2 |= ECCTL2_TSCTR_FREERUN | ECCTL2_REARM;
+ writew(ecctl2, state->regs + ECCTL2);
+ set_bit(ECAP_ENABLED, &state->flags);
+
+ ret = iio_triggered_buffer_postenable(idev);
+
+ return ret;
+}
+
+static const struct iio_buffer_setup_ops ecap_buffer_setup_ops = {
+ .postenable = &ecap_buffer_postenable,
+ .predisable = &ecap_buffer_predisable,
+};
+
+static void ecap_init_hw(struct iio_dev *idev)
+{
+ struct ecap_state *state = iio_priv(idev);
+
+ clear_bit(ECAP_ENABLED, &state->flags);
+ set_bit(ECAP_POLARITY_HIGH, &state->flags);
+
+ writew(ECCTL1_RUN_FREE | ECCTL1_CAPLDEN |
+ ECCTL1_CAP2POL | ECCTL1_CTRRST1,
+ state->regs + ECCTL1);
+
+ writew(ECCTL2_SYNCO_SEL_DIS | ECCTL2_STOP_WRAP_2,
+ state->regs + ECCTL2);
+}
+
+static const struct of_device_id ecap_of_ids[] = {
+ { .compatible = "ti,am33xx-ecap" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, ecap_of_ids);
+
+static int ecap_probe(struct platform_device *pdev)
+{
+ int irq, ret;
+ struct iio_dev *idev;
+ struct ecap_state *state;
+ struct resource *r;
+ struct clk *clk;
+ struct iio_trigger *trig;
+ u16 status;
+
+ idev = devm_iio_device_alloc(&pdev->dev, sizeof(struct ecap_state));
+ if (!idev)
+ return -ENOMEM;
+
+ state = iio_priv(idev);
+
+ clk = devm_clk_get(&pdev->dev, "fck");
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "failed to get clock\n");
+ return PTR_ERR(clk);
+ }
+
+ state->clk_rate = clk_get_rate(clk);
+ if (!state->clk_rate) {
+ dev_err(&pdev->dev, "failed to get clock rate\n");
+ return -EINVAL;
+ }
+
+ platform_set_drvdata(pdev, idev);
+
+ idev->dev.parent = &pdev->dev;
+ idev->name = dev_name(&pdev->dev);
+ idev->modes = INDIO_DIRECT_MODE;
+ idev->info = &ecap_info;
+ idev->channels = ecap_channels;
+ /* One h/w capture and one s/w timestamp channel per instance */
+ idev->num_channels = ARRAY_SIZE(ecap_channels);
+
+ trig = devm_iio_trigger_alloc(&pdev->dev, "%s-dev%d",
+ idev->name, idev->id);
+ if (!trig)
+ return -ENOMEM;
+ trig->dev.parent = idev->dev.parent;
+ iio_trigger_set_drvdata(trig, idev);
+ trig->ops = &iio_interrupt_trigger_ops;
+
+ ret = iio_trigger_register(trig);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register trigger\n");
+ return ret;
+ }
+
+ ret = iio_triggered_buffer_setup(idev, NULL,
+ &ecap_trigger_handler,
+ &ecap_buffer_setup_ops);
+ if (ret)
+ return ret;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "no irq is specified\n");
+ return irq;
+ }
+ ret = devm_request_irq(&pdev->dev, irq,
+ &ecap_interrupt_handler,
+ 0, dev_name(&pdev->dev), idev);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to request irq\n");
+ goto uninit_buffer;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ state->regs = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(state->regs)) {
+ dev_err(&pdev->dev, "unable to remap registers\n");
+ ret = PTR_ERR(state->regs);
+ goto uninit_buffer;
+ };
+
+ ret = iio_device_register(idev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "unable to register device\n");
+ goto uninit_buffer;
+ }
+
+ state->buf = devm_kzalloc(&idev->dev, idev->scan_bytes, GFP_KERNEL);
+ if (!state->buf) {
+ ret = -ENOMEM;
+ goto uninit_buffer;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
+
+ status = pwmss_submodule_state_change(pdev->dev.parent,
+ PWMSS_ECAPCLK_EN);
+ if (!(status & PWMSS_ECAPCLK_EN_ACK)) {
+ dev_err(&pdev->dev, "failed to enable PWMSS config space clock\n");
+ ret = -EINVAL;
+ goto pwmss_clk_failure;
+ }
+
+ ecap_init_hw(idev);
+
+ pm_runtime_put_sync(&pdev->dev);
+
+ return 0;
+
+pwmss_clk_failure:
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ iio_device_unregister(idev);
+
+uninit_buffer:
+ iio_triggered_buffer_cleanup(idev);
+
+ return ret;
+}
+
+static int ecap_remove(struct platform_device *pdev)
+{
+ struct iio_dev *idev = platform_get_drvdata(pdev);
+
+ pm_runtime_get_sync(&pdev->dev);
+
+ pwmss_submodule_state_change(pdev->dev.parent, PWMSS_ECAPCLK_STOP_REQ);
+
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ iio_device_unregister(idev);
+ iio_triggered_buffer_cleanup(idev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ecap_suspend(struct device *dev)
+{
+ struct ecap_state *state = dev_to_ecap_state(dev);
+
+ pm_runtime_get_sync(dev);
+ state->ctx.cap1 = readl(state->regs + CAP1);
+ state->ctx.cap2 = readl(state->regs + CAP2);
+ state->ctx.eceint = readw(state->regs + ECEINT);
+ state->ctx.ecctl1 = readw(state->regs + ECCTL1);
+ state->ctx.ecctl2 = readw(state->regs + ECCTL2);
+ pm_runtime_put_sync(dev);
+
+ /* If capture was active, disable ECAP */
+ if (test_bit(ECAP_ENABLED, &state->flags))
+ pm_runtime_put_sync(dev);
+
+ return 0;
+}
+
+static int ecap_resume(struct device *dev)
+{
+ struct ecap_state *state = dev_to_ecap_state(dev);
+
+ /* If capture was active, enable ECAP */
+ if (test_bit(ECAP_ENABLED, &state->flags))
+ pm_runtime_get_sync(dev);
+
+ pm_runtime_get_sync(dev);
+ writel(state->ctx.cap1, state->regs + CAP1);
+ writel(state->ctx.cap2, state->regs + CAP2);
+ writew(state->ctx.eceint, state->regs + ECEINT);
+ writew(state->ctx.ecctl1, state->regs + ECCTL1);
+ writew(state->ctx.ecctl2, state->regs + ECCTL2);
+ pm_runtime_put_sync(dev);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(ecap_pm_ops, ecap_suspend, ecap_resume);
+
+static struct platform_driver ecap_iio_driver = {
+ .driver = {
+ .name = "ecap",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(ecap_of_ids),
+ .pm = &ecap_pm_ops,
+ },
+ .probe = ecap_probe,
+ .remove = ecap_remove,
+};
+
+module_platform_driver(ecap_iio_driver);
+
+MODULE_DESCRIPTION("ECAP IIO pulse capture driver");
+MODULE_AUTHOR("Matt Porter <mporter@linaro.org>");
+MODULE_LICENSE("GPL");
--
1.8.4
^ permalink raw reply related
* [PATCH v3 1/6] iio: add support for pulse width capture devices
From: Matt Porter @ 2014-02-05 19:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1391626901-31684-1-git-send-email-mporter@linaro.org>
Add a channel type to support pulse width capture devices.
These devices capture the timing of a PWM signal based on a
configurable trigger
Signed-off-by: Matt Porter <mporter@linaro.org>
---
drivers/iio/industrialio-core.c | 1 +
include/linux/iio/types.h | 1 +
2 files changed, 2 insertions(+)
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index acc911a..6ea0cf8 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -70,6 +70,7 @@ static const char * const iio_chan_type_name_spec[] = {
[IIO_CCT] = "cct",
[IIO_PRESSURE] = "pressure",
[IIO_HUMIDITYRELATIVE] = "humidityrelative",
+ [IIO_PULSE] = "pulse",
};
static const char * const iio_modifier_names[] = {
diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
index 084d882..4fa8840 100644
--- a/include/linux/iio/types.h
+++ b/include/linux/iio/types.h
@@ -30,6 +30,7 @@ enum iio_chan_type {
IIO_CCT,
IIO_PRESSURE,
IIO_HUMIDITYRELATIVE,
+ IIO_PULSE,
};
enum iio_modifier {
--
1.8.4
^ permalink raw reply related
* [PATCH v3 0/6] IIO pulse capture support for TI ECAP
From: Matt Porter @ 2014-02-05 19:01 UTC (permalink / raw)
To: linux-arm-kernel
Changes since v2:
- Remove various unneeded field initialization
- Call iio_triggered_buffer_cleanup() on remove
Changes since v1:
- Rebased to 3.14-rc1
- Renamed in_pulse_polarity to pulse_polarity
- Added ABI entries for pulse devices and TI ECAP
This series adds support for PWM capture devices within IIO and
adds a TI ECAP IIO driver.
PWM capture devices are supported using a new IIO "pulse" channel type.
The IIO ECAP driver implements interrupt driven triggered buffer capture
only as raw sample reads are not applicable to this hardware.
Initially, the driver supports a single pulse width measurement with
configurable polarity. The ECAP hardware can support measurement of a
complete period and duty cycle but this is not yet implemented.
Matt Porter (6):
iio: add support for pulse width capture devices
iio: pulse: add TI ECAP driver
iio: enable selection and build of pulse drivers
iio: Add ABI docs for pulse capture devices
pwm: enable TI PWMSS if the IIO tiecap driver is selected
ARM: dts: AM33XX: Add ecap interrupt properties
Documentation/ABI/testing/sysfs-bus-iio | 18 +
.../ABI/testing/sysfs-bus-iio-pulse-tiecap | 9 +
arch/arm/boot/dts/am33xx.dtsi | 6 +
drivers/iio/Kconfig | 1 +
drivers/iio/Makefile | 1 +
drivers/iio/industrialio-core.c | 1 +
drivers/iio/pulse/Kconfig | 20 +
drivers/iio/pulse/Makefile | 6 +
drivers/iio/pulse/tiecap.c | 491 +++++++++++++++++++++
drivers/pwm/Kconfig | 2 +-
include/linux/iio/types.h | 1 +
11 files changed, 555 insertions(+), 1 deletion(-)
create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-pulse-tiecap
create mode 100644 drivers/iio/pulse/Kconfig
create mode 100644 drivers/iio/pulse/Makefile
create mode 100644 drivers/iio/pulse/tiecap.c
--
1.8.4
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox