* [PATCH v2 05/10] ARM: KVM: VGIC virtual CPU interface management
From: Christoffer Dall @ 2012-10-01 9:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20121001091244.49503.96318.stgit@ubuntu>
From: Marc Zyngier <marc.zyngier@arm.com>
Add VGIC virtual CPU interface code, picking pending interrupts
from the distributor and stashing them in the VGIC control interface
list registers.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Christoffer Dall <c.dall@virtualopensystems.com>
---
arch/arm/include/asm/kvm_vgic.h | 41 +++++++
arch/arm/kvm/vgic.c | 224 +++++++++++++++++++++++++++++++++++++++
2 files changed, 264 insertions(+), 1 deletion(-)
diff --git a/arch/arm/include/asm/kvm_vgic.h b/arch/arm/include/asm/kvm_vgic.h
index a82699f..bb67076 100644
--- a/arch/arm/include/asm/kvm_vgic.h
+++ b/arch/arm/include/asm/kvm_vgic.h
@@ -193,17 +193,58 @@ struct vgic_dist {
};
struct vgic_cpu {
+#ifdef CONFIG_KVM_ARM_VGIC
+ /* per IRQ to LR mapping */
+ u8 vgic_irq_lr_map[VGIC_NR_IRQS];
+
+ /* Pending interrupts on this VCPU */
+ DECLARE_BITMAP( pending, VGIC_NR_IRQS);
+
+ /* Bitmap of used/free list registers */
+ DECLARE_BITMAP( lr_used, 64);
+
+ /* Number of list registers on this CPU */
+ int nr_lr;
+
+ /* CPU vif control registers for world switch */
+ u32 vgic_hcr;
+ u32 vgic_vmcr;
+ u32 vgic_misr; /* Saved only */
+ u32 vgic_eisr[2]; /* Saved only */
+ u32 vgic_elrsr[2]; /* Saved only */
+ u32 vgic_apr;
+ u32 vgic_lr[64]; /* A15 has only 4... */
+#endif
};
+#define VGIC_HCR_EN (1 << 0)
+#define VGIC_HCR_UIE (1 << 1)
+
+#define VGIC_LR_VIRTUALID (0x3ff << 0)
+#define VGIC_LR_PHYSID_CPUID (7 << 10)
+#define VGIC_LR_STATE (3 << 28)
+#define VGIC_LR_PENDING_BIT (1 << 28)
+#define VGIC_LR_ACTIVE_BIT (1 << 29)
+#define VGIC_LR_EOI (1 << 19)
+
+#define VGIC_MISR_EOI (1 << 0)
+#define VGIC_MISR_U (1 << 1)
+
+#define LR_EMPTY 0xff
+
struct kvm;
struct kvm_vcpu;
struct kvm_run;
struct kvm_exit_mmio;
#ifdef CONFIG_KVM_ARM_VGIC
+void kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu);
+void kvm_vgic_sync_from_cpu(struct kvm_vcpu *vcpu);
+int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu);
bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
struct kvm_exit_mmio *mmio);
+#define irqchip_in_kernel(k) (!!((k)->arch.vgic.vctrl_base))
#else
static inline int kvm_vgic_hyp_init(void)
{
diff --git a/arch/arm/kvm/vgic.c b/arch/arm/kvm/vgic.c
index a870596..2b90785 100644
--- a/arch/arm/kvm/vgic.c
+++ b/arch/arm/kvm/vgic.c
@@ -584,7 +584,25 @@ static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg)
static int compute_pending_for_cpu(struct kvm_vcpu *vcpu)
{
- return 0;
+ struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+ unsigned long *pending, *enabled, *pend;
+ int vcpu_id;
+
+ vcpu_id = vcpu->vcpu_id;
+ pend = vcpu->arch.vgic_cpu.pending;
+
+ pending = vgic_bitmap_get_cpu_map(&dist->irq_state, vcpu_id);
+ enabled = vgic_bitmap_get_cpu_map(&dist->irq_enabled, vcpu_id);
+ bitmap_and(pend, pending, enabled, 32);
+
+ pending = vgic_bitmap_get_shared_map(&dist->irq_state);
+ enabled = vgic_bitmap_get_shared_map(&dist->irq_enabled);
+ bitmap_and(pend + 1, pending, enabled, VGIC_NR_SHARED_IRQS);
+ bitmap_and(pend + 1, pend + 1,
+ vgic_bitmap_get_shared_map(&dist->irq_spi_target[vcpu_id]),
+ VGIC_NR_SHARED_IRQS);
+
+ return (find_first_bit(pend, VGIC_NR_IRQS) < VGIC_NR_IRQS);
}
/*
@@ -609,3 +627,207 @@ static void vgic_update_state(struct kvm *kvm)
}
}
}
+
+#define LR_PHYSID(lr) (((lr) & VGIC_LR_PHYSID_CPUID) >> 10)
+#define MK_LR_PEND(src, irq) (VGIC_LR_PENDING_BIT | ((src) << 10) | (irq))
+/*
+ * Queue an interrupt to a CPU virtual interface. Return true on success,
+ * or false if it wasn't possible to queue it.
+ */
+static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
+{
+ struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+ struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+ int lr, is_level;
+
+ /* Sanitize the input... */
+ BUG_ON(sgi_source_id & ~7);
+ BUG_ON(sgi_source_id && irq > 15);
+ BUG_ON(irq >= VGIC_NR_IRQS);
+
+ kvm_debug("Queue IRQ%d\n", irq);
+
+ lr = vgic_cpu->vgic_irq_lr_map[irq];
+ is_level = !vgic_irq_is_edge(dist, irq);
+
+ /* Do we have an active interrupt for the same CPUID? */
+ if (lr != LR_EMPTY &&
+ (LR_PHYSID(vgic_cpu->vgic_lr[lr]) == sgi_source_id)) {
+ kvm_debug("LR%d piggyback for IRQ%d %x\n", lr, irq, vgic_cpu->vgic_lr[lr]);
+ BUG_ON(!test_bit(lr, vgic_cpu->lr_used));
+ vgic_cpu->vgic_lr[lr] |= VGIC_LR_PENDING_BIT;
+ if (is_level)
+ vgic_cpu->vgic_lr[lr] |= VGIC_LR_EOI;
+ return true;
+ }
+
+ /* Try to use another LR for this interrupt */
+ lr = find_first_bit((unsigned long *)vgic_cpu->vgic_elrsr,
+ vgic_cpu->nr_lr);
+ if (lr >= vgic_cpu->nr_lr)
+ return false;
+
+ kvm_debug("LR%d allocated for IRQ%d %x\n", lr, irq, sgi_source_id);
+ vgic_cpu->vgic_lr[lr] = MK_LR_PEND(sgi_source_id, irq);
+ if (is_level)
+ vgic_cpu->vgic_lr[lr] |= VGIC_LR_EOI;
+
+ vgic_cpu->vgic_irq_lr_map[irq] = lr;
+ clear_bit(lr, (unsigned long *)vgic_cpu->vgic_elrsr);
+ set_bit(lr, vgic_cpu->lr_used);
+
+ return true;
+}
+
+/*
+ * Fill the list registers with pending interrupts before running the
+ * guest.
+ */
+static void __kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu)
+{
+ struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+ struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+ unsigned long *pending;
+ int i, c, vcpu_id;
+ int overflow = 0;
+
+ vcpu_id = vcpu->vcpu_id;
+
+ /*
+ * We may not have any pending interrupt, or the interrupts
+ * may have been serviced from another vcpu. In all cases,
+ * move along.
+ */
+ if (!kvm_vgic_vcpu_pending_irq(vcpu)) {
+ pr_debug("CPU%d has no pending interrupt\n", vcpu_id);
+ goto epilog;
+ }
+
+ /* SGIs */
+ pending = vgic_bitmap_get_cpu_map(&dist->irq_state, vcpu_id);
+ for_each_set_bit(i, vgic_cpu->pending, 16) {
+ unsigned long sources;
+
+ sources = dist->irq_sgi_sources[vcpu_id][i];
+ for_each_set_bit(c, &sources, 8) {
+ if (!vgic_queue_irq(vcpu, c, i)) {
+ overflow = 1;
+ continue;
+ }
+
+ clear_bit(c, &sources);
+ }
+
+ if (!sources)
+ clear_bit(i, pending);
+
+ dist->irq_sgi_sources[vcpu_id][i] = sources;
+ }
+
+ /* PPIs */
+ for_each_set_bit_from(i, vgic_cpu->pending, 32) {
+ if (!vgic_queue_irq(vcpu, 0, i)) {
+ overflow = 1;
+ continue;
+ }
+
+ clear_bit(i, pending);
+ }
+
+
+ /* SPIs */
+ pending = vgic_bitmap_get_shared_map(&dist->irq_state);
+ for_each_set_bit_from(i, vgic_cpu->pending, VGIC_NR_IRQS) {
+ if (vgic_bitmap_get_irq_val(&dist->irq_active, 0, i))
+ continue; /* level interrupt, already queued */
+
+ if (!vgic_queue_irq(vcpu, 0, i)) {
+ overflow = 1;
+ continue;
+ }
+
+ /* Immediate clear on edge, set active on level */
+ if (vgic_irq_is_edge(dist, i))
+ clear_bit(i - 32, pending);
+ else
+ vgic_bitmap_set_irq_val(&dist->irq_active, 0, i, 1);
+ }
+
+epilog:
+ if (overflow)
+ vgic_cpu->vgic_hcr |= VGIC_HCR_UIE;
+ else {
+ vgic_cpu->vgic_hcr &= ~VGIC_HCR_UIE;
+ /*
+ * We're about to run this VCPU, and we've consumed
+ * everything the distributor had in store for
+ * us. Claim we don't have anything pending. We'll
+ * adjust that if needed while exiting.
+ */
+ clear_bit(vcpu_id, &dist->irq_pending_on_cpu);
+ }
+}
+
+/*
+ * Sync back the VGIC state after a guest run. We do not really touch
+ * the distributor here (the irq_pending_on_cpu bit is safe to set),
+ * so there is no need for taking its lock.
+ */
+static void __kvm_vgic_sync_from_cpu(struct kvm_vcpu *vcpu)
+{
+ struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+ struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+ int lr, pending;
+
+ /* Clear mappings for empty LRs */
+ for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_elrsr,
+ vgic_cpu->nr_lr) {
+ int irq;
+
+ if (!test_and_clear_bit(lr, vgic_cpu->lr_used))
+ continue;
+
+ irq = vgic_cpu->vgic_lr[lr] & VGIC_LR_VIRTUALID;
+
+ BUG_ON(irq >= VGIC_NR_IRQS);
+ vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY;
+ }
+
+ /* Check if we still have something up our sleeve... */
+ pending = find_first_zero_bit((unsigned long *)vgic_cpu->vgic_elrsr,
+ vgic_cpu->nr_lr);
+ if (pending < vgic_cpu->nr_lr) {
+ set_bit(vcpu->vcpu_id, &dist->irq_pending_on_cpu);
+ smp_mb();
+ }
+}
+
+void kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu)
+{
+ struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+
+ if (!irqchip_in_kernel(vcpu->kvm))
+ return;
+
+ spin_lock(&dist->lock);
+ __kvm_vgic_sync_to_cpu(vcpu);
+ spin_unlock(&dist->lock);
+}
+
+void kvm_vgic_sync_from_cpu(struct kvm_vcpu *vcpu)
+{
+ if (!irqchip_in_kernel(vcpu->kvm))
+ return;
+
+ __kvm_vgic_sync_from_cpu(vcpu);
+}
+
+int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu)
+{
+ struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+
+ if (!irqchip_in_kernel(vcpu->kvm))
+ return 0;
+
+ return test_bit(vcpu->vcpu_id, &dist->irq_pending_on_cpu);
+}
^ permalink raw reply related
* [PATCH v2 06/10] ARM: KVM: VGIC interrupt injection
From: Christoffer Dall @ 2012-10-01 9:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20121001091244.49503.96318.stgit@ubuntu>
From: Marc Zyngier <marc.zyngier@arm.com>
Plug the interrupt injection code. Interrupts can now be generated
from user space.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Christoffer Dall <c.dall@virtualopensystems.com>
---
arch/arm/include/asm/kvm_vgic.h | 8 ++++
arch/arm/kvm/arm.c | 36 ++++++++++++++++++
arch/arm/kvm/vgic.c | 77 +++++++++++++++++++++++++++++++++++++++
3 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/arch/arm/include/asm/kvm_vgic.h b/arch/arm/include/asm/kvm_vgic.h
index bb67076..9740f1f 100644
--- a/arch/arm/include/asm/kvm_vgic.h
+++ b/arch/arm/include/asm/kvm_vgic.h
@@ -240,6 +240,8 @@ struct kvm_exit_mmio;
#ifdef CONFIG_KVM_ARM_VGIC
void kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu);
void kvm_vgic_sync_from_cpu(struct kvm_vcpu *vcpu);
+int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num,
+ bool level);
int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu);
bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
struct kvm_exit_mmio *mmio);
@@ -260,6 +262,12 @@ static inline void kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu) {}
static inline void kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu) {}
static inline void kvm_vgic_sync_from_cpu(struct kvm_vcpu *vcpu) {}
+static inline int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid,
+ const struct kvm_irq_level *irq)
+{
+ return 0;
+}
+
static inline int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu)
{
return 0;
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index cf13340..be593220 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -758,10 +758,31 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level)
switch (irq_type) {
case KVM_ARM_IRQ_TYPE_CPU:
+ if (irqchip_in_kernel(kvm))
+ return -ENXIO;
+
if (irq_num > KVM_ARM_IRQ_CPU_FIQ)
return -EINVAL;
return vcpu_interrupt_line(vcpu, irq_num, level);
+#ifdef CONFIG_KVM_ARM_VGIC
+ case KVM_ARM_IRQ_TYPE_PPI:
+ if (!irqchip_in_kernel(kvm))
+ return -ENXIO;
+
+ if (irq_num < 16 || irq_num > 31)
+ return -EINVAL;
+
+ return kvm_vgic_inject_irq(kvm, vcpu->vcpu_id, irq_num, level);
+ case KVM_ARM_IRQ_TYPE_SPI:
+ if (!irqchip_in_kernel(kvm))
+ return -ENXIO;
+
+ if (irq_num < 32 || irq_num > KVM_ARM_IRQ_GIC_MAX)
+ return -EINVAL;
+
+ return kvm_vgic_inject_irq(kvm, 0, irq_num, level);
+#endif
}
return -EINVAL;
@@ -821,7 +842,20 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
long kvm_arch_vm_ioctl(struct file *filp,
unsigned int ioctl, unsigned long arg)
{
- return -EINVAL;
+
+ switch (ioctl) {
+#ifdef CONFIG_KVM_ARM_VGIC
+ case KVM_CREATE_IRQCHIP: {
+ struct kvm *kvm = filp->private_data;
+ if (vgic_present)
+ return kvm_vgic_init(kvm);
+ else
+ return -EINVAL;
+ }
+#endif
+ default:
+ return -EINVAL;
+ }
}
static void cpu_init_hyp_mode(void *vector)
diff --git a/arch/arm/kvm/vgic.c b/arch/arm/kvm/vgic.c
index 2b90785..b52d4c2 100644
--- a/arch/arm/kvm/vgic.c
+++ b/arch/arm/kvm/vgic.c
@@ -72,6 +72,7 @@
#define ACCESS_WRITE_MASK(x) ((x) & (3 << 1))
static void vgic_update_state(struct kvm *kvm);
+static void vgic_kick_vcpus(struct kvm *kvm);
static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg);
static inline int vgic_irq_is_edge(struct vgic_dist *dist, int irq)
@@ -539,6 +540,9 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exi
kvm_prepare_mmio(run, mmio);
kvm_handle_mmio_return(vcpu, run);
+ if (updated_state)
+ vgic_kick_vcpus(vcpu->kvm);
+
return true;
}
@@ -831,3 +835,76 @@ int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu)
return test_bit(vcpu->vcpu_id, &dist->irq_pending_on_cpu);
}
+
+static void vgic_kick_vcpus(struct kvm *kvm)
+{
+ struct kvm_vcpu *vcpu;
+ int c;
+
+ /*
+ * We've injected an interrupt, time to find out who deserves
+ * a good kick...
+ */
+ kvm_for_each_vcpu(c, vcpu, kvm) {
+ if (kvm_vgic_vcpu_pending_irq(vcpu))
+ kvm_vcpu_kick(vcpu);
+ }
+}
+
+static bool vgic_update_irq_state(struct kvm *kvm, int cpuid,
+ unsigned int irq_num, bool level)
+{
+ struct vgic_dist *dist = &kvm->arch.vgic;
+ struct kvm_vcpu *vcpu;
+ int is_edge, is_level, state;
+ int pend, enabled;
+
+ spin_lock(&dist->lock);
+
+ is_edge = vgic_irq_is_edge(dist, irq_num);
+ is_level = !is_edge;
+ state = vgic_bitmap_get_irq_val(&dist->irq_state, cpuid, irq_num);
+
+ /*
+ * Only inject an interrupt if:
+ * - level triggered and we change level
+ * - edge triggered and we have a rising edge
+ */
+ if ((is_level && !(state ^ level)) || (is_edge && (state || !level))) {
+ spin_unlock(&dist->lock);
+ return false;
+ }
+
+ vgic_bitmap_set_irq_val(&dist->irq_state, cpuid, irq_num, level);
+
+ enabled = vgic_bitmap_get_irq_val(&dist->irq_enabled, cpuid, irq_num);
+ pend = level && enabled;
+
+ if (irq_num >= 32) {
+ cpuid = dist->irq_spi_cpu[irq_num - 32];
+ pend &= vgic_bitmap_get_irq_val(&dist->irq_spi_target[cpuid],
+ 0, irq_num);
+ }
+
+ kvm_debug("Inject IRQ%d level %d CPU%d\n", irq_num, level, cpuid);
+
+ vcpu = kvm_get_vcpu(kvm, cpuid);
+ if (pend) {
+ set_bit(irq_num, vcpu->arch.vgic_cpu.pending);
+ set_bit(cpuid, &dist->irq_pending_on_cpu);
+ } else
+ clear_bit(irq_num, vcpu->arch.vgic_cpu.pending);
+
+ spin_unlock(&dist->lock);
+
+ return true;
+}
+
+int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num,
+ bool level)
+{
+ if (vgic_update_irq_state(kvm, cpuid, irq_num, level))
+ vgic_kick_vcpus(kvm);
+
+ return 0;
+}
^ permalink raw reply related
* [PATCH v2 07/10] ARM: KVM: VGIC control interface world switch
From: Christoffer Dall @ 2012-10-01 9:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20121001091244.49503.96318.stgit@ubuntu>
From: Marc Zyngier <marc.zyngier@arm.com>
Enable the VGIC control interface to be save-restored on world switch.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Christoffer Dall <c.dall@virtualopensystems.com>
---
arch/arm/include/asm/kvm_arm.h | 12 +++++++
arch/arm/kernel/asm-offsets.c | 12 +++++++
arch/arm/kvm/interrupts_head.S | 68 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 92 insertions(+)
diff --git a/arch/arm/include/asm/kvm_arm.h b/arch/arm/include/asm/kvm_arm.h
index 4f1bb01..e1e39d6 100644
--- a/arch/arm/include/asm/kvm_arm.h
+++ b/arch/arm/include/asm/kvm_arm.h
@@ -188,4 +188,16 @@
#define HSR_EC_DABT (0x24)
#define HSR_EC_DABT_HYP (0x25)
+/* GICH offsets */
+#define GICH_HCR 0x0
+#define GICH_VTR 0x4
+#define GICH_VMCR 0x8
+#define GICH_MISR 0x10
+#define GICH_EISR0 0x20
+#define GICH_EISR1 0x24
+#define GICH_ELRSR0 0x30
+#define GICH_ELRSR1 0x34
+#define GICH_APR 0xf0
+#define GICH_LR0 0x100
+
#endif /* __ARM_KVM_ARM_H__ */
diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c
index d020e6aa..1f4a894 100644
--- a/arch/arm/kernel/asm-offsets.c
+++ b/arch/arm/kernel/asm-offsets.c
@@ -166,6 +166,18 @@ int main(void)
DEFINE(VCPU_HIFAR, offsetof(struct kvm_vcpu, arch.hifar));
DEFINE(VCPU_HPFAR, offsetof(struct kvm_vcpu, arch.hpfar));
DEFINE(VCPU_HYP_PC, offsetof(struct kvm_vcpu, arch.hyp_pc));
+#ifdef CONFIG_KVM_ARM_VGIC
+ DEFINE(VCPU_VGIC_CPU, offsetof(struct kvm_vcpu, arch.vgic_cpu));
+ DEFINE(VGIC_CPU_HCR, offsetof(struct vgic_cpu, vgic_hcr));
+ DEFINE(VGIC_CPU_VMCR, offsetof(struct vgic_cpu, vgic_vmcr));
+ DEFINE(VGIC_CPU_MISR, offsetof(struct vgic_cpu, vgic_misr));
+ DEFINE(VGIC_CPU_EISR, offsetof(struct vgic_cpu, vgic_eisr));
+ DEFINE(VGIC_CPU_ELRSR, offsetof(struct vgic_cpu, vgic_elrsr));
+ DEFINE(VGIC_CPU_APR, offsetof(struct vgic_cpu, vgic_apr));
+ DEFINE(VGIC_CPU_LR, offsetof(struct vgic_cpu, vgic_lr));
+ DEFINE(VGIC_CPU_NR_LR, offsetof(struct vgic_cpu, nr_lr));
+ DEFINE(KVM_VGIC_VCTRL, offsetof(struct kvm, arch.vgic.vctrl_base));
+#endif
DEFINE(KVM_VTTBR, offsetof(struct kvm, arch.vttbr));
#endif
return 0;
diff --git a/arch/arm/kvm/interrupts_head.S b/arch/arm/kvm/interrupts_head.S
index b29bd62..e44aea6 100644
--- a/arch/arm/kvm/interrupts_head.S
+++ b/arch/arm/kvm/interrupts_head.S
@@ -225,6 +225,45 @@
* @vcpup: Register pointing to VCPU struct
*/
.macro save_vgic_state vcpup
+#ifdef CONFIG_KVM_ARM_VGIC
+ /* Get VGIC VCTRL base into r2 */
+ ldr r2, [\vcpup, #VCPU_KVM]
+ ldr r2, [r2, #KVM_VGIC_VCTRL]
+ cmp r2, #0
+ beq 2f
+
+ /* Compute the address of struct vgic_cpu */
+ add r11, \vcpup, #VCPU_VGIC_CPU
+
+ /* Save all interesting registers */
+ ldr r3, [r2, #GICH_HCR]
+ ldr r4, [r2, #GICH_VMCR]
+ ldr r5, [r2, #GICH_MISR]
+ ldr r6, [r2, #GICH_EISR0]
+ ldr r7, [r2, #GICH_EISR1]
+ ldr r8, [r2, #GICH_ELRSR0]
+ ldr r9, [r2, #GICH_ELRSR1]
+ ldr r10, [r2, #GICH_APR]
+
+ str r3, [r11, #VGIC_CPU_HCR]
+ str r4, [r11, #VGIC_CPU_VMCR]
+ str r5, [r11, #VGIC_CPU_MISR]
+ str r6, [r11, #VGIC_CPU_EISR]
+ str r7, [r11, #(VGIC_CPU_EISR + 4)]
+ str r8, [r11, #VGIC_CPU_ELRSR]
+ str r9, [r11, #(VGIC_CPU_ELRSR + 4)]
+ str r10, [r11, #VGIC_CPU_APR]
+
+ /* Save list registers */
+ add r2, r2, #GICH_LR0
+ add r3, r11, #VGIC_CPU_LR
+ ldr r4, [r11, #VGIC_CPU_NR_LR]
+1: ldr r6, [r2], #4
+ str r6, [r3], #4
+ subs r4, r4, #1
+ bne 1b
+2:
+#endif
.endm
/*
@@ -232,6 +271,35 @@
* @vcpup: Register pointing to VCPU struct
*/
.macro restore_vgic_state vcpup
+#ifdef CONFIG_KVM_ARM_VGIC
+ /* Get VGIC VCTRL base into r2 */
+ ldr r2, [\vcpup, #VCPU_KVM]
+ ldr r2, [r2, #KVM_VGIC_VCTRL]
+ cmp r2, #0
+ beq 2f
+
+ /* Compute the address of struct vgic_cpu */
+ add r11, \vcpup, #VCPU_VGIC_CPU
+
+ /* We only restore a minimal set of registers */
+ ldr r3, [r11, #VGIC_CPU_HCR]
+ ldr r4, [r11, #VGIC_CPU_VMCR]
+ ldr r8, [r11, #VGIC_CPU_APR]
+
+ str r3, [r2, #GICH_HCR]
+ str r4, [r2, #GICH_VMCR]
+ str r8, [r2, #GICH_APR]
+
+ /* Restore list registers */
+ add r2, r2, #GICH_LR0
+ add r3, r11, #VGIC_CPU_LR
+ ldr r4, [r11, #VGIC_CPU_NR_LR]
+1: ldr r6, [r3], #4
+ str r6, [r2], #4
+ subs r4, r4, #1
+ bne 1b
+2:
+#endif
.endm
/* Configures the HSTR (Hyp System Trap Register) on entry/return
^ permalink raw reply related
* [PATCH v2 08/10] ARM: KVM: VGIC initialisation code
From: Christoffer Dall @ 2012-10-01 9:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20121001091244.49503.96318.stgit@ubuntu>
From: Marc Zyngier <marc.zyngier@arm.com>
Add the init code for the hypervisor, the virtual machine, and
the virtual CPUs.
An interrupt handler is also wired to allow the VGIC maintenance
interrupts, used to deal with level triggered interrupts and LR
underflows.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Christoffer Dall <c.dall@virtualopensystems.com>
---
arch/arm/include/asm/kvm_vgic.h | 3 +
arch/arm/kvm/arm.c | 8 +-
arch/arm/kvm/vgic.c | 199 +++++++++++++++++++++++++++++++++++++++
3 files changed, 208 insertions(+), 2 deletions(-)
diff --git a/arch/arm/include/asm/kvm_vgic.h b/arch/arm/include/asm/kvm_vgic.h
index 9740f1f..c8327f3 100644
--- a/arch/arm/include/asm/kvm_vgic.h
+++ b/arch/arm/include/asm/kvm_vgic.h
@@ -238,6 +238,9 @@ struct kvm_run;
struct kvm_exit_mmio;
#ifdef CONFIG_KVM_ARM_VGIC
+int kvm_vgic_hyp_init(void);
+int kvm_vgic_init(struct kvm *kvm);
+void kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu);
void kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu);
void kvm_vgic_sync_from_cpu(struct kvm_vcpu *vcpu);
int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num,
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index be593220..f88fd18 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -61,6 +61,8 @@ static atomic64_t kvm_vmid_gen = ATOMIC64_INIT(1);
static u8 kvm_next_vmid;
static DEFINE_SPINLOCK(kvm_vmid_lock);
+static bool vgic_present;
+
static void kvm_arm_set_running_vcpu(struct kvm_vcpu *vcpu)
{
BUG_ON(preemptible());
@@ -185,6 +187,8 @@ int kvm_dev_ioctl_check_extension(long ext)
switch (ext) {
#ifdef CONFIG_KVM_ARM_VGIC
case KVM_CAP_IRQCHIP:
+ r = vgic_present;
+ break;
#endif
case KVM_CAP_USER_MEMORY:
case KVM_CAP_DESTROY_MEMORY_REGION_WORKS:
@@ -992,8 +996,8 @@ static int init_hyp_mode(void)
* Init HYP view of VGIC
*/
err = kvm_vgic_hyp_init();
- if (err)
- goto out_free_mappings;
+ if (!err)
+ vgic_present = true;
return 0;
out_free_vfp:
diff --git a/arch/arm/kvm/vgic.c b/arch/arm/kvm/vgic.c
index b52d4c2..fc2a138 100644
--- a/arch/arm/kvm/vgic.c
+++ b/arch/arm/kvm/vgic.c
@@ -20,7 +20,14 @@
#include <linux/kvm_host.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
#include <asm/kvm_emulate.h>
+#include <asm/hardware/gic.h>
+#include <asm/kvm_arm.h>
+#include <asm/kvm_mmu.h>
/*
* How the whole thing works (courtesy of Christoffer Dall):
@@ -61,6 +68,13 @@
/* Temporary hacks, need to be provided by userspace emulation */
#define VGIC_DIST_BASE 0x2c001000
#define VGIC_DIST_SIZE 0x1000
+#define VGIC_CPU_BASE 0x2c002000
+#define VGIC_CPU_SIZE 0x2000
+
+/* Virtual control interface base address */
+static void __iomem *vgic_vctrl_base;
+
+static struct device_node *vgic_node;
#define ACCESS_READ_VALUE (1 << 0)
#define ACCESS_READ_RAZ (0 << 0)
@@ -908,3 +922,188 @@ int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num,
return 0;
}
+
+static irqreturn_t vgic_maintenance_handler(int irq, void *data)
+{
+ struct kvm_vcpu *vcpu = *(struct kvm_vcpu **)data;
+ struct vgic_dist *dist;
+ struct vgic_cpu *vgic_cpu;
+
+ if (WARN(!vcpu,
+ "VGIC interrupt on CPU %d with no vcpu\n", smp_processor_id()))
+ return IRQ_HANDLED;
+
+ vgic_cpu = &vcpu->arch.vgic_cpu;
+ dist = &vcpu->kvm->arch.vgic;
+ kvm_debug("MISR = %08x\n", vgic_cpu->vgic_misr);
+
+ /*
+ * We do not need to take the distributor lock here, since the only
+ * action we perform is clearing the irq_active_bit for an EOIed
+ * level interrupt. There is a potential race with
+ * the queuing of an interrupt in __kvm_sync_to_cpu(), where we check
+ * if the interrupt is already active. Two possibilities:
+ *
+ * - The queuing is occuring on the same vcpu: cannot happen, as we're
+ * already in the context of this vcpu, and executing the handler
+ * - The interrupt has been migrated to another vcpu, and we ignore
+ * this interrupt for this run. Big deal. It is still pending though,
+ * and will get considered when this vcpu exits.
+ */
+ if (vgic_cpu->vgic_misr & VGIC_MISR_EOI) {
+ /*
+ * Some level interrupts have been EOIed. Clear their
+ * active bit.
+ */
+ int lr, irq;
+
+ for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_eisr,
+ vgic_cpu->nr_lr) {
+ irq = vgic_cpu->vgic_lr[lr] & VGIC_LR_VIRTUALID;
+
+ vgic_bitmap_set_irq_val(&dist->irq_active,
+ vcpu->vcpu_id, irq, 0);
+ vgic_cpu->vgic_lr[lr] &= ~VGIC_LR_EOI;
+ writel_relaxed(vgic_cpu->vgic_lr[lr],
+ dist->vctrl_base + GICH_LR0 + (lr << 2));
+ }
+ }
+
+ if (vgic_cpu->vgic_misr & VGIC_MISR_U) {
+ vgic_cpu->vgic_hcr &= ~VGIC_HCR_UIE;
+ writel_relaxed(vgic_cpu->vgic_hcr, dist->vctrl_base + GICH_HCR);
+ }
+
+ return IRQ_HANDLED;
+}
+
+void kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu)
+{
+ struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+ struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+ u32 reg;
+ int i;
+
+ if (!irqchip_in_kernel(vcpu->kvm))
+ return;
+
+ for (i = 0; i < VGIC_NR_IRQS; i++) {
+ if (i < 16)
+ vgic_bitmap_set_irq_val(&dist->irq_enabled,
+ vcpu->vcpu_id, i, 1);
+ if (i < 32)
+ vgic_bitmap_set_irq_val(&dist->irq_cfg,
+ vcpu->vcpu_id, i, 1);
+
+ vgic_cpu->vgic_irq_lr_map[i] = LR_EMPTY;
+ }
+
+ BUG_ON(!vcpu->kvm->arch.vgic.vctrl_base);
+ reg = readl_relaxed(vcpu->kvm->arch.vgic.vctrl_base + GICH_VTR);
+ vgic_cpu->nr_lr = (reg & 0x1f) + 1;
+
+ reg = readl_relaxed(vcpu->kvm->arch.vgic.vctrl_base + GICH_VMCR);
+ vgic_cpu->vgic_vmcr = reg | (0x1f << 27); /* Priority */
+
+ vgic_cpu->vgic_hcr |= VGIC_HCR_EN; /* Get the show on the road... */
+}
+
+static void vgic_init_maintenance_interrupt(void *info)
+{
+ unsigned int *irqp = info;
+
+ enable_percpu_irq(*irqp, 0);
+}
+
+int kvm_vgic_hyp_init(void)
+{
+ int ret;
+ unsigned int irq;
+ struct resource vctrl_res;
+
+ vgic_node = of_find_compatible_node(NULL, NULL, "arm,cortex-a15-gic");
+ if (!vgic_node)
+ return -ENODEV;
+
+ irq = irq_of_parse_and_map(vgic_node, 0);
+ if (!irq)
+ return -ENXIO;
+
+ ret = request_percpu_irq(irq, vgic_maintenance_handler,
+ "vgic", kvm_get_running_vcpus());
+ if (ret) {
+ kvm_err("Cannot register interrupt %d\n", irq);
+ return ret;
+ }
+
+ ret = of_address_to_resource(vgic_node, 2, &vctrl_res);
+ if (ret) {
+ kvm_err("Cannot obtain VCTRL resource\n");
+ goto out_free_irq;
+ }
+
+ vgic_vctrl_base = of_iomap(vgic_node, 2);
+ if (!vgic_vctrl_base) {
+ kvm_err("Cannot ioremap VCTRL\n");
+ ret = -ENOMEM;
+ goto out_free_irq;
+ }
+
+ ret = create_hyp_io_mappings(vgic_vctrl_base,
+ vgic_vctrl_base + resource_size(&vctrl_res),
+ vctrl_res.start);
+ if (ret) {
+ kvm_err("Cannot map VCTRL into hyp\n");
+ goto out_unmap;
+ }
+
+ kvm_info("%s@%llx IRQ%d\n", vgic_node->name, vctrl_res.start, irq);
+ on_each_cpu(vgic_init_maintenance_interrupt, &irq, 1);
+
+ return 0;
+
+out_unmap:
+ iounmap(vgic_vctrl_base);
+out_free_irq:
+ free_percpu_irq(irq, kvm_get_running_vcpus());
+
+ return ret;
+}
+
+int kvm_vgic_init(struct kvm *kvm)
+{
+ int ret, i;
+ struct resource vcpu_res;
+
+ mutex_lock(&kvm->lock);
+
+ if (of_address_to_resource(vgic_node, 3, &vcpu_res)) {
+ kvm_err("Cannot obtain VCPU resource\n");
+ ret = -ENXIO;
+ goto out;
+ }
+
+ if (atomic_read(&kvm->online_vcpus) || kvm->arch.vgic.vctrl_base) {
+ ret = -EEXIST;
+ goto out;
+ }
+
+ spin_lock_init(&kvm->arch.vgic.lock);
+ kvm->arch.vgic.vctrl_base = vgic_vctrl_base;
+ kvm->arch.vgic.vgic_dist_base = VGIC_DIST_BASE;
+ kvm->arch.vgic.vgic_dist_size = VGIC_DIST_SIZE;
+
+ ret = kvm_phys_addr_ioremap(kvm, VGIC_CPU_BASE,
+ vcpu_res.start, VGIC_CPU_SIZE);
+ if (ret) {
+ kvm_err("Unable to remap VGIC CPU to VCPU\n");
+ goto out;
+ }
+
+ for (i = 32; i < VGIC_NR_IRQS; i += 4)
+ vgic_set_target_reg(kvm, 0, i);
+
+out:
+ mutex_unlock(&kvm->lock);
+ return ret;
+}
^ permalink raw reply related
* [PATCH v2 09/10] ARM: KVM: vgic: reduce the number of vcpu kick
From: Christoffer Dall @ 2012-10-01 9:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20121001091244.49503.96318.stgit@ubuntu>
From: Marc Zyngier <marc.zyngier@arm.com>
If we have level interrupts already programmed to fire on a vcpu,
there is no reason to kick it after injecting a new interrupt,
as we're guaranteed that we'll exit when the level interrupt will
be EOId (VGIC_LR_EOI is set).
The exit will force a reload of the VGIC, injecting the new interrupts.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Christoffer Dall <c.dall@virtualopensystems.com>
---
arch/arm/include/asm/kvm_vgic.h | 10 ++++++++++
arch/arm/kvm/arm.c | 10 +++++++++-
arch/arm/kvm/vgic.c | 10 ++++++++--
3 files changed, 27 insertions(+), 3 deletions(-)
diff --git a/arch/arm/include/asm/kvm_vgic.h b/arch/arm/include/asm/kvm_vgic.h
index c8327f3..588c637 100644
--- a/arch/arm/include/asm/kvm_vgic.h
+++ b/arch/arm/include/asm/kvm_vgic.h
@@ -214,6 +214,9 @@ struct vgic_cpu {
u32 vgic_elrsr[2]; /* Saved only */
u32 vgic_apr;
u32 vgic_lr[64]; /* A15 has only 4... */
+
+ /* Number of level-triggered interrupt in progress */
+ atomic_t irq_active_count;
#endif
};
@@ -250,6 +253,8 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
struct kvm_exit_mmio *mmio);
#define irqchip_in_kernel(k) (!!((k)->arch.vgic.vctrl_base))
+#define vgic_active_irq(v) (atomic_read(&(v)->arch.vgic_cpu.irq_active_count) == 0)
+
#else
static inline int kvm_vgic_hyp_init(void)
{
@@ -286,6 +291,11 @@ static inline int irqchip_in_kernel(struct kvm *kvm)
{
return 0;
}
+
+static inline int vgic_active_irq(struct kvm_vcpu *vcpu)
+{
+ return 0;
+}
#endif
#endif
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index f88fd18..b03e604 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -94,7 +94,15 @@ int kvm_arch_hardware_enable(void *garbage)
int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu)
{
- return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE;
+ if (kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE) {
+ if (vgic_active_irq(vcpu) &&
+ cmpxchg(&vcpu->mode, EXITING_GUEST_MODE, IN_GUEST_MODE) == EXITING_GUEST_MODE)
+ return 0;
+
+ return 1;
+ }
+
+ return 0;
}
void kvm_arch_hardware_disable(void *garbage)
diff --git a/arch/arm/kvm/vgic.c b/arch/arm/kvm/vgic.c
index fc2a138..63fe0dd 100644
--- a/arch/arm/kvm/vgic.c
+++ b/arch/arm/kvm/vgic.c
@@ -674,8 +674,10 @@ static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
kvm_debug("LR%d piggyback for IRQ%d %x\n", lr, irq, vgic_cpu->vgic_lr[lr]);
BUG_ON(!test_bit(lr, vgic_cpu->lr_used));
vgic_cpu->vgic_lr[lr] |= VGIC_LR_PENDING_BIT;
- if (is_level)
+ if (is_level) {
vgic_cpu->vgic_lr[lr] |= VGIC_LR_EOI;
+ atomic_inc(&vgic_cpu->irq_active_count);
+ }
return true;
}
@@ -687,8 +689,10 @@ static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
kvm_debug("LR%d allocated for IRQ%d %x\n", lr, irq, sgi_source_id);
vgic_cpu->vgic_lr[lr] = MK_LR_PEND(sgi_source_id, irq);
- if (is_level)
+ if (is_level) {
vgic_cpu->vgic_lr[lr] |= VGIC_LR_EOI;
+ atomic_inc(&vgic_cpu->irq_active_count);
+ }
vgic_cpu->vgic_irq_lr_map[irq] = lr;
clear_bit(lr, (unsigned long *)vgic_cpu->vgic_elrsr);
@@ -963,6 +967,8 @@ static irqreturn_t vgic_maintenance_handler(int irq, void *data)
vgic_bitmap_set_irq_val(&dist->irq_active,
vcpu->vcpu_id, irq, 0);
+ atomic_dec(&vgic_cpu->irq_active_count);
+ smp_mb();
vgic_cpu->vgic_lr[lr] &= ~VGIC_LR_EOI;
writel_relaxed(vgic_cpu->vgic_lr[lr],
dist->vctrl_base + GICH_LR0 + (lr << 2));
^ permalink raw reply related
* [PATCH v2 10/10] ARM: KVM: Add VGIC configuration option
From: Christoffer Dall @ 2012-10-01 9:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20121001091244.49503.96318.stgit@ubuntu>
From: Marc Zyngier <marc.zyngier@arm.com>
It is now possible to select the VGIC configuration option.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Christoffer Dall <c.dall@virtualopensystems.com>
---
arch/arm/kvm/Kconfig | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/arm/kvm/Kconfig b/arch/arm/kvm/Kconfig
index 47c5500..867551e 100644
--- a/arch/arm/kvm/Kconfig
+++ b/arch/arm/kvm/Kconfig
@@ -40,6 +40,13 @@ config KVM_ARM_HOST
---help---
Provides host support for ARM processors.
+config KVM_ARM_VGIC
+ bool "KVM support for Virtual GIC"
+ depends on KVM_ARM_HOST && OF
+ select HAVE_KVM_IRQCHIP
+ ---help---
+ Adds support for a hardware assisted, in-kernel GIC emulation.
+
source drivers/virtio/Kconfig
endif # VIRTUALIZATION
^ permalink raw reply related
* [PATCH v2 0/5] KVM/ARM Architected Timers support
From: Christoffer Dall @ 2012-10-01 9:15 UTC (permalink / raw)
To: linux-arm-kernel
The following series implements support for the architected generic
timers for KVM/ARM.
This is an unmodified repost of the previously submitted series.
This patch series can also be pulled from:
git://github.com/virtualopensystems/linux-kvm-arm.git
branch: kvm-arm-v12-vgic-timers
---
Marc Zyngier (5):
ARM: arch_timers: switch to physical timers if HYP mode is available
ARM: KVM: arch_timers: Add minimal infrastructure
ARM: KVM: arch_timers: Add guest timer core support
ARM: KVM: arch_timers: Add timer world switch
ARM: KVM: arch_timers: Wire the init code and config option
arch/arm/include/asm/kvm_arch_timer.h | 95 +++++++++++++++
arch/arm/include/asm/kvm_host.h | 5 +
arch/arm/kernel/arch_timer.c | 7 +
arch/arm/kernel/asm-offsets.c | 8 +
arch/arm/kvm/Kconfig | 7 +
arch/arm/kvm/Makefile | 1
arch/arm/kvm/arm.c | 14 ++
arch/arm/kvm/interrupts.S | 2
arch/arm/kvm/interrupts_head.S | 60 ++++++++++
arch/arm/kvm/reset.c | 9 +
arch/arm/kvm/timer.c | 204 +++++++++++++++++++++++++++++++++
arch/arm/kvm/vgic.c | 4 +
12 files changed, 415 insertions(+), 1 deletion(-)
create mode 100644 arch/arm/include/asm/kvm_arch_timer.h
create mode 100644 arch/arm/kvm/timer.c
--
^ permalink raw reply
* [PATCH v2 1/5] ARM: arch_timers: switch to physical timers if HYP mode is available
From: Christoffer Dall @ 2012-10-01 9:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20121001091528.49694.69990.stgit@ubuntu>
From: Marc Zyngier <marc.zyngier@arm.com>
If we're booted in HYP mode, it is possible that we'll run some
kind of virtualized environment. In this case, it is a better to
switch to the physical timers, and leave the virtual timers to
guests.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
arch/arm/kernel/arch_timer.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/arch/arm/kernel/arch_timer.c b/arch/arm/kernel/arch_timer.c
index c04c2a6..8a30186 100644
--- a/arch/arm/kernel/arch_timer.c
+++ b/arch/arm/kernel/arch_timer.c
@@ -25,6 +25,7 @@
#include <asm/arch_timer.h>
#include <asm/system_info.h>
#include <asm/sched_clock.h>
+#include <asm/virt.h>
static unsigned long arch_timer_rate;
@@ -490,10 +491,14 @@ int __init arch_timer_of_register(void)
arch_timer_ppi[i] = irq_of_parse_and_map(np, i);
/*
+ * If HYP mode is available, we know that the physical timer
+ * has been configured to be accessible from PL1. Use it, so
+ * that a guest can use the virtual timer instead.
+ *
* If no interrupt provided for virtual timer, we'll have to
* stick to the physical timer. It'd better be accessible...
*/
- if (!arch_timer_ppi[VIRT_PPI]) {
+ if (is_hyp_mode_available() || !arch_timer_ppi[VIRT_PPI]) {
arch_timer_use_virtual = false;
if (!arch_timer_ppi[PHYS_SECURE_PPI] ||
^ permalink raw reply related
* [PATCH v2 2/5] ARM: KVM: arch_timers: Add minimal infrastructure
From: Christoffer Dall @ 2012-10-01 9:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20121001091528.49694.69990.stgit@ubuntu>
From: Marc Zyngier <marc.zyngier@arm.com>
Add some very minimal architected timer related infrastructure.
For the moment, we just provide empty structures, and enable/disable
access to the physical timer across world switch.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Christoffer Dall <c.dall@virtualopensystems.com>
---
arch/arm/include/asm/kvm_arch_timer.h | 45 +++++++++++++++++++++++++++++++++
arch/arm/include/asm/kvm_host.h | 5 ++++
arch/arm/kvm/interrupts.S | 2 +
arch/arm/kvm/interrupts_head.S | 19 ++++++++++++++
4 files changed, 71 insertions(+)
create mode 100644 arch/arm/include/asm/kvm_arch_timer.h
diff --git a/arch/arm/include/asm/kvm_arch_timer.h b/arch/arm/include/asm/kvm_arch_timer.h
new file mode 100644
index 0000000..513b852
--- /dev/null
+++ b/arch/arm/include/asm/kvm_arch_timer.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ * Author: Marc Zyngier <marc.zyngier@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __ASM_ARM_KVM_ARCH_TIMER_H
+#define __ASM_ARM_KVM_ARCH_TIMER_H
+
+struct arch_timer_kvm {
+};
+
+struct arch_timer_cpu {
+};
+
+#ifndef CONFIG_KVM_ARM_TIMER
+static inline int kvm_timer_hyp_init(void)
+{
+ return 0;
+};
+
+static inline int kvm_timer_init(struct kvm *kvm)
+{
+ return 0;
+}
+
+static inline void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu) {}
+static inline void kvm_timer_sync_to_cpu(struct kvm_vcpu *vcpu) {}
+static inline void kvm_timer_sync_from_cpu(struct kvm_vcpu *vcpu) {}
+static inline void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu) {}
+#endif
+
+#endif
diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h
index d65faea..96cae84 100644
--- a/arch/arm/include/asm/kvm_host.h
+++ b/arch/arm/include/asm/kvm_host.h
@@ -23,6 +23,7 @@
#include <asm/kvm_asm.h>
#include <asm/fpstate.h>
#include <asm/kvm_vgic.h>
+#include <asm/kvm_arch_timer.h>
#define KVM_MAX_VCPUS NR_CPUS
#define KVM_MEMORY_SLOTS 32
@@ -56,6 +57,9 @@ struct kvm_arch {
/* Interrupt controller */
struct vgic_dist vgic;
+
+ /* Timer */
+ struct arch_timer_kvm timer;
};
#define KVM_NR_MEM_OBJS 40
@@ -93,6 +97,7 @@ struct kvm_vcpu_arch {
/* VGIC state */
struct vgic_cpu vgic_cpu;
+ struct arch_timer_cpu timer_cpu;
/*
* Anything that is not used directly from assembly code goes
diff --git a/arch/arm/kvm/interrupts.S b/arch/arm/kvm/interrupts.S
index 914c7f2..0e7afd6 100644
--- a/arch/arm/kvm/interrupts.S
+++ b/arch/arm/kvm/interrupts.S
@@ -105,6 +105,7 @@ ENTRY(__kvm_vcpu_run)
store_mode_state sp, fiq
restore_vgic_state r0
+ restore_timer_state r0
@ Store hardware CP15 state and load guest state
read_cp15_state
@@ -223,6 +224,7 @@ after_vfp_restore:
read_cp15_state 1, r1
write_cp15_state
+ save_timer_state r1
save_vgic_state r1
load_mode_state sp, fiq
diff --git a/arch/arm/kvm/interrupts_head.S b/arch/arm/kvm/interrupts_head.S
index e44aea6..4703035 100644
--- a/arch/arm/kvm/interrupts_head.S
+++ b/arch/arm/kvm/interrupts_head.S
@@ -302,6 +302,25 @@
#endif
.endm
+#define CNTHCTL_PL1PCTEN (1 << 0)
+#define CNTHCTL_PL1PCEN (1 << 1)
+
+.macro save_timer_state vcpup
+ @ Allow physical timer/counter access for the host
+ mrc p15, 4, r2, c14, c1, 0 @ CNTHCTL
+ orr r2, r2, #(CNTHCTL_PL1PCEN | CNTHCTL_PL1PCTEN)
+ mcr p15, 4, r2, c14, c1, 0 @ CNTHCTL
+.endm
+
+.macro restore_timer_state vcpup
+ @ Disallow physical timer access for the guest
+ @ Physical counter access is allowed
+ mrc p15, 4, r2, c14, c1, 0 @ CNTHCTL
+ orr r2, r2, #CNTHCTL_PL1PCTEN
+ bic r2, r2, #CNTHCTL_PL1PCEN
+ mcr p15, 4, r2, c14, c1, 0 @ CNTHCTL
+.endm
+
/* Configures the HSTR (Hyp System Trap Register) on entry/return
* (hardware reset value is 0) */
.macro set_hstr entry
^ permalink raw reply related
* [PATCH v2 3/5] ARM: KVM: arch_timers: Add guest timer core support
From: Christoffer Dall @ 2012-10-01 9:16 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20121001091528.49694.69990.stgit@ubuntu>
From: Marc Zyngier <marc.zyngier@arm.com>
We can inject a timer interrupt into the guest as a result of
three possible events:
- The virtual timer interrupt has fired while we were still
executing the guest
- The timer interrupt hasn't fired, but it expired while we
were doing the world switch
- A hrtimer we programmed earlier has fired
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Christoffer Dall <c.dall@virtualopensystems.com>
---
arch/arm/include/asm/kvm_arch_timer.h | 52 ++++++++
arch/arm/kvm/reset.c | 9 +
arch/arm/kvm/timer.c | 204 +++++++++++++++++++++++++++++++++
3 files changed, 264 insertions(+), 1 deletion(-)
create mode 100644 arch/arm/kvm/timer.c
diff --git a/arch/arm/include/asm/kvm_arch_timer.h b/arch/arm/include/asm/kvm_arch_timer.h
index 513b852..512ee39 100644
--- a/arch/arm/include/asm/kvm_arch_timer.h
+++ b/arch/arm/include/asm/kvm_arch_timer.h
@@ -19,13 +19,63 @@
#ifndef __ASM_ARM_KVM_ARCH_TIMER_H
#define __ASM_ARM_KVM_ARCH_TIMER_H
+#include <linux/clocksource.h>
+#include <linux/hrtimer.h>
+#include <linux/workqueue.h>
+
struct arch_timer_kvm {
+#ifdef CONFIG_KVM_ARM_TIMER
+ /* Is the timer enabled */
+ bool enabled;
+
+ /*
+ * Virtual offset (kernel access it through cntvoff, HYP code
+ * access it as two 32bit values).
+ */
+ union {
+ cycle_t cntvoff;
+ struct {
+ u32 low; /* Restored only */
+ u32 high; /* Restored only */
+ } cntvoff32;
+ };
+#endif
};
struct arch_timer_cpu {
+#ifdef CONFIG_KVM_ARM_TIMER
+ /* Background timer used when the guest is not running */
+ struct hrtimer timer;
+
+ /* Work queued with the above timer expires */
+ struct work_struct expired;
+
+ /* Background timer active */
+ bool armed;
+
+ /* Timer IRQ */
+ const struct kvm_irq_level *irq;
+
+ /* Registers: control register, timer value */
+ u32 cntv_ctl; /* Saved/restored */
+ union {
+ cycle_t cntv_cval;
+ struct {
+ u32 low; /* Saved/restored */
+ u32 high; /* Saved/restored */
+ } cntv_cval32;
+ };
+#endif
};
-#ifndef CONFIG_KVM_ARM_TIMER
+#ifdef CONFIG_KVM_ARM_TIMER
+int kvm_timer_hyp_init(void);
+int kvm_timer_init(struct kvm *kvm);
+void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu);
+void kvm_timer_sync_to_cpu(struct kvm_vcpu *vcpu);
+void kvm_timer_sync_from_cpu(struct kvm_vcpu *vcpu);
+void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu);
+#else
static inline int kvm_timer_hyp_init(void)
{
return 0;
diff --git a/arch/arm/kvm/reset.c b/arch/arm/kvm/reset.c
index 290a13d..bb17def 100644
--- a/arch/arm/kvm/reset.c
+++ b/arch/arm/kvm/reset.c
@@ -37,6 +37,12 @@ static struct kvm_regs a15_regs_reset = {
.cpsr = SVC_MODE | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT,
};
+#ifdef CONFIG_KVM_ARM_TIMER
+static const struct kvm_irq_level a15_virt_timer_ppi = {
+ .irq = 27, /* A7/A15 specific */
+ .level = 1,
+};
+#endif
/*******************************************************************************
* Exported reset function
@@ -59,6 +65,9 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
return -EINVAL;
cpu_reset = &a15_regs_reset;
vcpu->arch.midr = read_cpuid_id();
+#ifdef CONFIG_KVM_ARM_TIMER
+ vcpu->arch.timer_cpu.irq = &a15_virt_timer_ppi;
+#endif
break;
default:
return -ENODEV;
diff --git a/arch/arm/kvm/timer.c b/arch/arm/kvm/timer.c
new file mode 100644
index 0000000..a241298
--- /dev/null
+++ b/arch/arm/kvm/timer.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ * Author: Marc Zyngier <marc.zyngier@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/of_irq.h>
+#include <linux/kvm.h>
+#include <linux/kvm_host.h>
+#include <linux/interrupt.h>
+
+#include <asm/arch_timer.h>
+
+#include <asm/kvm_vgic.h>
+#include <asm/kvm_arch_timer.h>
+
+static struct timecounter *timecounter;
+static struct workqueue_struct *wqueue;
+
+static cycle_t kvm_phys_timer_read(void)
+{
+ return timecounter->cc->read(timecounter->cc);
+}
+
+static void kvm_timer_inject_irq(struct kvm_vcpu *vcpu)
+{
+ struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
+
+ timer->cntv_ctl |= 1 << 1; /* Mask the interrupt in the guest */
+ kvm_vgic_inject_irq(vcpu->kvm, vcpu->vcpu_id,
+ vcpu->arch.timer_cpu.irq->irq,
+ vcpu->arch.timer_cpu.irq->level);
+}
+
+static irqreturn_t kvm_arch_timer_handler(int irq, void *dev_id)
+{
+ struct kvm_vcpu *vcpu = *(struct kvm_vcpu **)dev_id;
+
+ /*
+ * We disable the timer in the world switch and let it be
+ * handled by kvm_timer_sync_from_cpu(). Getting a timer
+ * interrupt at this point is a sure sign of some major
+ * breakage.
+ */
+ pr_warn("Unexpected interrupt %d on vcpu %p\n", irq, vcpu);
+ return IRQ_HANDLED;
+}
+
+static void kvm_timer_inject_irq_work(struct work_struct *work)
+{
+ struct kvm_vcpu *vcpu;
+
+ vcpu = container_of(work, struct kvm_vcpu, arch.timer_cpu.expired);
+ vcpu->arch.timer_cpu.armed = false;
+ kvm_timer_inject_irq(vcpu);
+}
+
+static enum hrtimer_restart kvm_timer_expire(struct hrtimer *hrt)
+{
+ struct arch_timer_cpu *timer;
+ timer = container_of(hrt, struct arch_timer_cpu, timer);
+ queue_work(wqueue, &timer->expired);
+ return HRTIMER_NORESTART;
+}
+
+void kvm_timer_sync_to_cpu(struct kvm_vcpu *vcpu)
+{
+ struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
+
+ /*
+ * We're about to run this vcpu again, so there is no need to
+ * keep the background timer running, as we're about to
+ * populate the CPU timer again.
+ */
+ if (timer->armed) {
+ hrtimer_cancel(&timer->timer);
+ cancel_work_sync(&timer->expired);
+ timer->armed = false;
+ }
+}
+
+void kvm_timer_sync_from_cpu(struct kvm_vcpu *vcpu)
+{
+ struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
+ cycle_t cval, now;
+ u64 ns;
+
+ /* Check if the timer is enabled and unmasked first */
+ if ((timer->cntv_ctl & 3) != 1)
+ return;
+
+ cval = timer->cntv_cval;
+ now = kvm_phys_timer_read() - vcpu->kvm->arch.timer.cntvoff;
+
+ BUG_ON(timer->armed);
+
+ if (cval <= now) {
+ /*
+ * Timer has already expired while we were not
+ * looking. Inject the interrupt and carry on.
+ */
+ kvm_timer_inject_irq(vcpu);
+ return;
+ }
+
+ timer->armed = true;
+ ns = cyclecounter_cyc2ns(timecounter->cc, cval - now);
+ hrtimer_start(&timer->timer, ktime_add_ns(ktime_get(), ns),
+ HRTIMER_MODE_ABS);
+}
+
+void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu)
+{
+ struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
+
+ INIT_WORK(&timer->expired, kvm_timer_inject_irq_work);
+ hrtimer_init(&timer->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+ timer->timer.function = kvm_timer_expire;
+}
+
+static void kvm_timer_init_interrupt(void *info)
+{
+ unsigned int *irqp = info;
+
+ enable_percpu_irq(*irqp, 0);
+}
+
+
+static const struct of_device_id arch_timer_of_match[] = {
+ { .compatible = "arm,armv7-timer", },
+ {},
+};
+
+int kvm_timer_hyp_init(void)
+{
+ struct device_node *np;
+ unsigned int ppi;
+ int err;
+
+ timecounter = arch_timer_get_timecounter();
+ if (!timecounter)
+ return -ENODEV;
+
+ np = of_find_matching_node(NULL, arch_timer_of_match);
+ if (!np) {
+ kvm_err("kvm_arch_timer: can't find DT node\n");
+ return -ENODEV;
+ }
+
+ ppi = irq_of_parse_and_map(np, 2);
+ if (!ppi) {
+ kvm_err("kvm_arch_timer: no virtual timer interrupt\n");
+ return -EINVAL;
+ }
+
+ err = request_percpu_irq(ppi, kvm_arch_timer_handler,
+ "kvm guest timer", kvm_get_running_vcpus());
+ if (err) {
+ kvm_err("kvm_arch_timer: can't request interrupt %d (%d)\n",
+ ppi, err);
+ return err;
+ }
+
+ wqueue = create_singlethread_workqueue("kvm_arch_timer");
+ if (!wqueue) {
+ free_percpu_irq(ppi, kvm_get_running_vcpus());
+ return -ENOMEM;
+ }
+
+ kvm_info("%s IRQ%d\n", np->name, ppi);
+ on_each_cpu(kvm_timer_init_interrupt, &ppi, 1);
+
+ return 0;
+}
+
+void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu)
+{
+ struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
+
+ hrtimer_cancel(&timer->timer);
+ cancel_work_sync(&timer->expired);
+}
+
+int kvm_timer_init(struct kvm *kvm)
+{
+ if (timecounter && wqueue) {
+ kvm->arch.timer.cntvoff = kvm_phys_timer_read();
+ kvm->arch.timer.enabled = 1;
+ }
+
+ return 0;
+}
^ permalink raw reply related
* [PATCH v2 4/5] ARM: KVM: arch_timers: Add timer world switch
From: Christoffer Dall @ 2012-10-01 9:16 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20121001091528.49694.69990.stgit@ubuntu>
From: Marc Zyngier <marc.zyngier@arm.com>
Do the necessary save/restore dance for the timers in the world
switch code. In the process, allow the guest to read the physical
counter, which is useful for its own clock_event_device.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Christoffer Dall <c.dall@virtualopensystems.com>
---
arch/arm/kernel/asm-offsets.c | 8 ++++++++
arch/arm/kvm/arm.c | 3 +++
arch/arm/kvm/interrupts_head.S | 41 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 52 insertions(+)
diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c
index 1f4a894..1c4181e 100644
--- a/arch/arm/kernel/asm-offsets.c
+++ b/arch/arm/kernel/asm-offsets.c
@@ -176,6 +176,14 @@ int main(void)
DEFINE(VGIC_CPU_APR, offsetof(struct vgic_cpu, vgic_apr));
DEFINE(VGIC_CPU_LR, offsetof(struct vgic_cpu, vgic_lr));
DEFINE(VGIC_CPU_NR_LR, offsetof(struct vgic_cpu, nr_lr));
+#ifdef CONFIG_KVM_ARM_TIMER
+ DEFINE(VCPU_TIMER_CNTV_CTL, offsetof(struct kvm_vcpu, arch.timer_cpu.cntv_ctl));
+ DEFINE(VCPU_TIMER_CNTV_CVALH, offsetof(struct kvm_vcpu, arch.timer_cpu.cntv_cval32.high));
+ DEFINE(VCPU_TIMER_CNTV_CVALL, offsetof(struct kvm_vcpu, arch.timer_cpu.cntv_cval32.low));
+ DEFINE(KVM_TIMER_CNTVOFF_H, offsetof(struct kvm, arch.timer.cntvoff32.high));
+ DEFINE(KVM_TIMER_CNTVOFF_L, offsetof(struct kvm, arch.timer.cntvoff32.low));
+ DEFINE(KVM_TIMER_ENABLED, offsetof(struct kvm, arch.timer.enabled));
+#endif
DEFINE(KVM_VGIC_VCTRL, offsetof(struct kvm, arch.vgic.vctrl_base));
#endif
DEFINE(KVM_VTTBR, offsetof(struct kvm, arch.vttbr));
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index b03e604..c62bf28 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -649,6 +649,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
update_vttbr(vcpu->kvm);
kvm_vgic_sync_to_cpu(vcpu);
+ kvm_timer_sync_to_cpu(vcpu);
local_irq_disable();
@@ -662,6 +663,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
if (ret <= 0 || need_new_vmid_gen(vcpu->kvm)) {
local_irq_enable();
+ kvm_timer_sync_from_cpu(vcpu);
kvm_vgic_sync_from_cpu(vcpu);
continue;
}
@@ -701,6 +703,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
* Back from guest
*************************************************************/
+ kvm_timer_sync_from_cpu(vcpu);
kvm_vgic_sync_from_cpu(vcpu);
ret = handle_exit(vcpu, run, ret);
diff --git a/arch/arm/kvm/interrupts_head.S b/arch/arm/kvm/interrupts_head.S
index 4703035..514a75a 100644
--- a/arch/arm/kvm/interrupts_head.S
+++ b/arch/arm/kvm/interrupts_head.S
@@ -306,6 +306,25 @@
#define CNTHCTL_PL1PCEN (1 << 1)
.macro save_timer_state vcpup
+#ifdef CONFIG_KVM_ARM_TIMER
+ ldr r4, [\vcpup, #VCPU_KVM]
+ ldr r2, [r4, #KVM_TIMER_ENABLED]
+ cmp r2, #0
+ beq 1f
+
+ mrc p15, 0, r2, c14, c3, 1 @ CNTV_CTL
+ and r2, #3
+ str r2, [\vcpup, #VCPU_TIMER_CNTV_CTL]
+ bic r2, #1 @ Clear ENABLE
+ mcr p15, 0, r2, c14, c3, 1 @ CNTV_CTL
+ isb
+
+ mrrc p15, 3, r2, r3, c14 @ CNTV_CVAL
+ str r3, [\vcpup, #VCPU_TIMER_CNTV_CVALH]
+ str r2, [\vcpup, #VCPU_TIMER_CNTV_CVALL]
+
+1:
+#endif
@ Allow physical timer/counter access for the host
mrc p15, 4, r2, c14, c1, 0 @ CNTHCTL
orr r2, r2, #(CNTHCTL_PL1PCEN | CNTHCTL_PL1PCTEN)
@@ -319,6 +338,28 @@
orr r2, r2, #CNTHCTL_PL1PCTEN
bic r2, r2, #CNTHCTL_PL1PCEN
mcr p15, 4, r2, c14, c1, 0 @ CNTHCTL
+
+#ifdef CONFIG_KVM_ARM_TIMER
+ ldr r4, [\vcpup, #VCPU_KVM]
+ ldr r2, [r4, #KVM_TIMER_ENABLED]
+ cmp r2, #0
+ beq 1f
+
+ ldr r3, [r4, #KVM_TIMER_CNTVOFF_H]
+ ldr r2, [r4, #KVM_TIMER_CNTVOFF_L]
+ mcrr p15, 4, r2, r3, c14 @ CNTVOFF
+ isb
+
+ ldr r3, [\vcpup, #VCPU_TIMER_CNTV_CVALH]
+ ldr r2, [\vcpup, #VCPU_TIMER_CNTV_CVALL]
+ mcrr p15, 3, r2, r3, c14 @ CNTV_CVAL
+
+ ldr r2, [\vcpup, #VCPU_TIMER_CNTV_CTL]
+ and r2, #3
+ mcr p15, 0, r2, c14, c3, 1 @ CNTV_CTL
+ isb
+1:
+#endif
.endm
/* Configures the HSTR (Hyp System Trap Register) on entry/return
^ permalink raw reply related
* [PATCH v2 5/5] ARM: KVM: arch_timers: Wire the init code and config option
From: Christoffer Dall @ 2012-10-01 9:16 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20121001091528.49694.69990.stgit@ubuntu>
From: Marc Zyngier <marc.zyngier@arm.com>
It is now possible to select CONFIG_KVM_ARM_TIMER to enable the
KVM architected timer support.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Christoffer Dall <c.dall@virtualopensystems.com>
---
arch/arm/kvm/Kconfig | 7 +++++++
arch/arm/kvm/Makefile | 1 +
arch/arm/kvm/arm.c | 11 +++++++++++
arch/arm/kvm/vgic.c | 4 ++++
4 files changed, 23 insertions(+)
diff --git a/arch/arm/kvm/Kconfig b/arch/arm/kvm/Kconfig
index 867551e..ade2673 100644
--- a/arch/arm/kvm/Kconfig
+++ b/arch/arm/kvm/Kconfig
@@ -47,6 +47,13 @@ config KVM_ARM_VGIC
---help---
Adds support for a hardware assisted, in-kernel GIC emulation.
+config KVM_ARM_TIMER
+ bool "KVM support for Architected Timers"
+ depends on KVM_ARM_VGIC && ARM_ARCH_TIMER
+ select HAVE_KVM_IRQCHIP
+ ---help---
+ Adds support for the Architected Timers in virtual machines
+
source drivers/virtio/Kconfig
endif # VIRTUALIZATION
diff --git a/arch/arm/kvm/Makefile b/arch/arm/kvm/Makefile
index 89608c0..8f4aa02 100644
--- a/arch/arm/kvm/Makefile
+++ b/arch/arm/kvm/Makefile
@@ -21,3 +21,4 @@ obj-$(CONFIG_KVM_ARM_HOST) += $(addprefix ../../../virt/kvm/, kvm_main.o coalesc
obj-$(CONFIG_KVM_ARM_HOST) += arm.o guest.o mmu.o emulate.o reset.o
obj-$(CONFIG_KVM_ARM_HOST) += coproc.o coproc_a15.o
obj-$(CONFIG_KVM_ARM_VGIC) += vgic.o
+obj-$(CONFIG_KVM_ARM_TIMER) += timer.o
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index c62bf28..f8c377b 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -283,6 +283,7 @@ out:
void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu)
{
kvm_mmu_free_memory_caches(vcpu);
+ kvm_timer_vcpu_terminate(vcpu);
kmem_cache_free(kvm_vcpu_cache, vcpu);
}
@@ -320,6 +321,9 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
/* Set up VGIC */
kvm_vgic_vcpu_init(vcpu);
+ /* Set up the timer */
+ kvm_timer_vcpu_init(vcpu);
+
return 0;
}
@@ -1010,6 +1014,13 @@ static int init_hyp_mode(void)
if (!err)
vgic_present = true;
+ /*
+ * Init HYP architected timer support
+ */
+ err = kvm_timer_hyp_init();
+ if (err)
+ goto out_free_mappings;
+
return 0;
out_free_vfp:
free_percpu(kvm_host_vfp_state);
diff --git a/arch/arm/kvm/vgic.c b/arch/arm/kvm/vgic.c
index 63fe0dd..494d94d 100644
--- a/arch/arm/kvm/vgic.c
+++ b/arch/arm/kvm/vgic.c
@@ -1111,5 +1111,9 @@ int kvm_vgic_init(struct kvm *kvm)
out:
mutex_unlock(&kvm->lock);
+
+ if (!ret)
+ kvm_timer_init(kvm);
+
return ret;
}
^ permalink raw reply related
* [PATCH V3 0/8] ARM: OMAP4: Add PMU Support
From: Will Deacon @ 2012-10-01 9:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <5060D462.4040209@ti.com>
On Mon, Sep 24, 2012 at 10:45:06PM +0100, Jon Hunter wrote:
> On 09/20/2012 04:09 PM, Will Deacon wrote:
> > On Thu, Sep 20, 2012 at 06:17:02PM +0100, Paul Walmsley wrote:
> >> Have queued most of these for 3.7 with the exception of the OMAP4430
> >> CTI-related patches (which look to me like 3.8 material) and the PM
> >> runtime suspend/resume patch (which looks to me like 3.7-rc material) --
> >> assuming this series makes it in for 3.7 ...
> >
> > Ok, thanks for queueing what you did.
> >
> > Jon -- could you pick the pieces up from what's left please and shout if
> > you need anything from me?
>
> Yes will do.
Great, thanks! Do you want me to do anything with my perf/omap4 branch? Now
that we have 3.6, I can try rebasing it but I don't know if it's worth the
effort if some of the patches are being reworked. Of course, I'm happy to
pick newer versions if they're available.
Cheers,
Will
^ permalink raw reply
* [PATCH 1/4] mfd: ab8500: add devicetree support for fuelgauge
From: Lee Jones @ 2012-10-01 9:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1349064513-31301-2-git-send-email-rajanikanth.hv@stericsson.com>
On Mon, 01 Oct 2012, Rajanikanth H.V wrote:
> From: "Rajanikanth H.V" <rajanikanth.hv@stericsson.com>
>
> - This patch adds device tree support for fuelguage driver
> - optimize bm devices platform_data usage and of_probe(...)
> Note: of_probe() routine for battery managed devices is made
> common across all bm drivers.
>
> Signed-off-by: Rajanikanth H.V <rajanikanth.hv@stericsson.com>
> ---
> Documentation/devicetree/bindings/mfd/ab8500.txt | 8 +-
> .../devicetree/bindings/power_supply/ab8500/fg.txt | 86 +++
> arch/arm/boot/dts/dbx5x0.dtsi | 22 +-
> drivers/mfd/ab8500-core.c | 1 +
> drivers/power/Makefile | 2 +-
> drivers/power/ab8500_bmdata.c | 549 ++++++++++++++++++++
> drivers/power/ab8500_btemp.c | 4 +-
> drivers/power/ab8500_charger.c | 4 +-
> drivers/power/ab8500_fg.c | 76 ++-
> drivers/power/abx500_chargalg.c | 4 +-
> include/linux/mfd/abx500.h | 37 +-
> include/linux/mfd/abx500/ab8500-bm.h | 7 +
> 12 files changed, 744 insertions(+), 56 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/power_supply/ab8500/fg.txt
> create mode 100644 drivers/power/ab8500_bmdata.c
>
> diff --git a/Documentation/devicetree/bindings/mfd/ab8500.txt b/Documentation/devicetree/bindings/mfd/ab8500.txt
> index ce83c8d..762dc11 100644
> --- a/Documentation/devicetree/bindings/mfd/ab8500.txt
> +++ b/Documentation/devicetree/bindings/mfd/ab8500.txt
> @@ -24,7 +24,13 @@ ab8500-bm : : : Battery Manager
> ab8500-btemp : : : Battery Temperature
> ab8500-charger : : : Battery Charger
> ab8500-codec : : : Audio Codec
> -ab8500-fg : : : Fuel Gauge
> +ab8500-fg : : vddadc : Fuel Gauge
> + : NCONV_ACCU : : Accumulate N Sample Conversion
> + : BATT_OVV : : Battery Over Voltage
> + : LOW_BAT_F : : LOW threshold battery voltage
> + : CC_INT_CALIB : : Counter Counter Internal Calibration
I think you mean: Coulomb Counter.
> + : CCEOC : : Coulomb Counter End of Conversion
> + : : :
Random empty entry.
> ab8500-gpadc : HW_CONV_END : vddadc : Analogue to Digital Converter
> SW_CONV_END : :
> ab8500-gpio : : : GPIO Controller
> diff --git a/Documentation/devicetree/bindings/power_supply/ab8500/fg.txt b/Documentation/devicetree/bindings/power_supply/ab8500/fg.txt
> new file mode 100644
> index 0000000..caa33b0
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/power_supply/ab8500/fg.txt
> @@ -0,0 +1,86 @@
> +=== AB8500 Fuel Gauge Driver ===
> +
> +AB8500 is a mixed signal multimedia and power management
> +device comprising: power and energy-management-module,
> +wall-charger, usb-charger, audio codec, general purpose adc,
> +tvout, clock management and sim card interface.
> +
> +Fuel-guage support is part of energy-management-module, the other
Spelling.
> +components of this module are:
> +main-charger, usb-combo-charger and Battery temperature monitoring.
> +
> +The properties below describes the node for fuel guage driver.
Spelling.
> +
> +Required Properties:
> +- compatible = "stericsson,ab8500-fg"
> +- interface-name:
> + Name of the controller/driver which is part of energy-management-module
> +- supplied-to:
Still not sure about this property, or your justification for use.
> + This property shall have dependent nodes which represent other
> + energy-management-module.
Plural?
> + This is a logical binding w.r.t power supply events
Proper English please, no slang.
> + across energy-management-module drivers where-in, the
Ill placed comma?
> + runtime battery properties are shared along with uevent
> + notification.
Plural?
> + ref: di->fg.external_power_changed =
> + ab8500_fg_external_power_changed;
> + ab8500_fg.c
> +
> + Need for this property:
> + energy-management-module driver updates power-supply properties
> + which are subset of events listed in 'enum power_supply_property',
> + ref: power_supply.h file
> + Event handler invokes power supply change notifier
> + which in-turn invokes registered power supply class call-back
> + based on the 'supplied-to' string.
> + ref:
> + power_supply_changed_work(..) ./drivers/power/power_supply_core.c
> + di->fg_psy.external_power_changed
> +
> + example:
> + ab8500-fg {
> + /* dependent energy management modules */
> + supplied-to = <&ab8500_chargalg &ab8500_usb>;
> + };
> +
> + ab8500_battery_info: ab8500_bat_type {
> + battery-type = <2>;
> + thermistor-on-batctrl = <1>;
You have this as a bool here, and ...
> + };
> +
> +Other dependent node for fuel-gauge is:
> + ab8500_battery_info: ab8500_bat_type {
> + };
> + This node will provide information on 'thermistor interface' and
> + 'battery technology type' used.
> +
> +Properties of this node are:
> +thermistor-on-batctrl:
> + A boolean value indicating thermistor interface to battery
> +
> + Note:
> + 'btemp' and 'batctrl' are the pins interfaced for battery temperature
> + measurement, 'btemp' signal is used when NTC(negative temperature
> + coefficient) resister is interfaced external to battery whereas
> + 'batctrl' pin is used when NTC resister is internal to battery.
> +
> + e.g:
> + ab8500_battery_info: ab8500_bat_type {
> + thermistor-on-batctrl;
... a standard property here. I suggest you drop the bool value.
> + };
> + indiactes: NTC resister is internal to battery, 'batctrl' is used
> + for thermal measurement.
> +
> + The absence of property 'thermal-on-batctrl' indicates
> + NTC resister is external to battery and 'btemp' signal is used
> + for thermal measurement.
> +
> +battery-type:
> + This shall be the battery manufacturing technology type,
> + allowed types are:
> + "UNKNOWN" "NiMH" "LION" "LIPO" "LiFe" "NiCd" "LiMn"
> + e.g:
> + ab8500_battery_info: ab8500_bat_type {
> + battery-name = "LION";
> + }
> +
> diff --git a/arch/arm/boot/dts/dbx5x0.dtsi b/arch/arm/boot/dts/dbx5x0.dtsi
> index 748ba7a..bd22c56 100644
> --- a/arch/arm/boot/dts/dbx5x0.dtsi
> +++ b/arch/arm/boot/dts/dbx5x0.dtsi
> @@ -352,8 +352,28 @@
> vddadc-supply = <&ab8500_ldo_tvout_reg>;
> };
>
> - ab8500-usb {
> + ab8500_battery_info: ab8500_bat_type {
> + battery-name = "LION";
All new properties have to be documented.
Vendor specific properties should be prepended with the vendor name, so
either write a generic binding document for all to use or prefix with
'stericsson,".
> + thermistor-on-batctrl;
> + };
> +
> + ab8500_chargalg: ab8500_chalg {
> + compatible = "stericsson,ab8500-chargalg";
> + interface-name = "ab8500_chargalg";
Same with all of your new properties (I'll stop mentioning them now).
> + battery-info = <&ab8500_battery_info>;
> + supplied-to = <&ab8500_fuel_gauge>;
Weren't you going to reverse this logic to be more inline with how
the reset of Device Tree works?
> + };
> +
> + ab8500_fuel_gauge: ab8500_fg {
> + compatible = "stericsson,ab8500-fg";
> + interface-name = "ab8500_fg";
> + battery-info = <&ab8500_battery_info>;
> + supplied-to = <&ab8500_chargalg &ab8500_usb>;
As above.
> + };
> +
> + ab8500_usb: ab8500_usb_if {
What does 'if' mean?
> compatible = "stericsson,ab8500-usb";
> + interface-name = "ab8500_usb";
Why is this required?
> interrupts = < 90 0x4
> 96 0x4
> 14 0x4
> diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
> index 1667c77..6c3d7c2 100644
> --- a/drivers/mfd/ab8500-core.c
> +++ b/drivers/mfd/ab8500-core.c
> @@ -1051,6 +1051,7 @@ static struct mfd_cell __devinitdata ab8500_bm_devs[] = {
> },
> {
> .name = "ab8500-fg",
> + .of_compatible = "stericsson,ab8500-fg",
> .num_resources = ARRAY_SIZE(ab8500_fg_resources),
> .resources = ab8500_fg_resources,
> },
> diff --git a/drivers/power/Makefile b/drivers/power/Makefile
> index ee58afb..2c58d4e 100644
> --- a/drivers/power/Makefile
> +++ b/drivers/power/Makefile
> @@ -34,7 +34,7 @@ obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o
> obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
> obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o
> obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o
> -obj-$(CONFIG_AB8500_BM) += ab8500_charger.o ab8500_btemp.o ab8500_fg.o abx500_chargalg.o
> +obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o abx500_chargalg.o
> obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o
> obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o
> obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o
> diff --git a/drivers/power/ab8500_bmdata.c b/drivers/power/ab8500_bmdata.c
> new file mode 100644
> index 0000000..d0def3b
> --- /dev/null
> +++ b/drivers/power/ab8500_bmdata.c
> @@ -0,0 +1,549 @@
> +#include <linux/export.h>
> +#include <linux/power_supply.h>
> +#include <linux/of.h>
> +#include <linux/mfd/abx500.h>
> +#include <linux/mfd/abx500/ab8500.h>
> +#include <linux/mfd/abx500/ab8500-bm.h>
> +
> +/*
> + * These are the defined batteries that uses a NTC and ID resistor placed
> + * inside of the battery pack.
> + * Note that the res_to_temp table must be strictly sorted by falling resistance
> + * values to work.
> + */
> +static struct abx500_res_to_temp temp_tbl_A_thermistor[] = {
> + {-5, 53407},
> + { 0, 48594},
> + { 5, 43804},
> + {10, 39188},
> + {15, 34870},
> + {20, 30933},
> + {25, 27422},
> + {30, 24347},
> + {35, 21694},
> + {40, 19431},
> + {45, 17517},
> + {50, 15908},
> + {55, 14561},
> + {60, 13437},
> + {65, 12500},
> +};
> +static struct abx500_res_to_temp temp_tbl_B_thermistor[] = {
> + {-5, 165418},
> + { 0, 159024},
> + { 5, 151921},
> + {10, 144300},
> + {15, 136424},
> + {20, 128565},
> + {25, 120978},
> + {30, 113875},
> + {35, 107397},
> + {40, 101629},
> + {45, 96592},
> + {50, 92253},
> + {55, 88569},
> + {60, 85461},
> + {65, 82869},
> +};
> +static struct abx500_v_to_cap cap_tbl_A_thermistor[] = {
> + {4171, 100},
> + {4114, 95},
> + {4009, 83},
> + {3947, 74},
> + {3907, 67},
> + {3863, 59},
> + {3830, 56},
> + {3813, 53},
> + {3791, 46},
> + {3771, 33},
> + {3754, 25},
> + {3735, 20},
> + {3717, 17},
> + {3681, 13},
> + {3664, 8},
> + {3651, 6},
> + {3635, 5},
> + {3560, 3},
> + {3408, 1},
> + {3247, 0},
> +};
> +static struct abx500_v_to_cap cap_tbl_B_thermistor[] = {
> + {4161, 100},
> + {4124, 98},
> + {4044, 90},
> + {4003, 85},
> + {3966, 80},
> + {3933, 75},
> + {3888, 67},
> + {3849, 60},
> + {3813, 55},
> + {3787, 47},
> + {3772, 30},
> + {3751, 25},
> + {3718, 20},
> + {3681, 16},
> + {3660, 14},
> + {3589, 10},
> + {3546, 7},
> + {3495, 4},
> + {3404, 2},
> + {3250, 0},
> +};
> +
> +static struct abx500_v_to_cap cap_tbl[] = {
> + {4186, 100},
> + {4163, 99},
> + {4114, 95},
> + {4068, 90},
> + {3990, 80},
> + {3926, 70},
> + {3898, 65},
> + {3866, 60},
> + {3833, 55},
> + {3812, 50},
> + {3787, 40},
> + {3768, 30},
> + {3747, 25},
> + {3730, 20},
> + {3705, 15},
> + {3699, 14},
> + {3684, 12},
> + {3672, 9},
> + {3657, 7},
> + {3638, 6},
> + {3556, 4},
> + {3424, 2},
> + {3317, 1},
> + {3094, 0},
> +};
> +
> +/*
> + * Note that the res_to_temp table must be strictly sorted by falling
> + * resistance values to work.
> + */
> +static struct abx500_res_to_temp temp_tbl[] = {
> + {-5, 214834},
> + { 0, 162943},
> + { 5, 124820},
> + {10, 96520},
> + {15, 75306},
> + {20, 59254},
> + {25, 47000},
> + {30, 37566},
> + {35, 30245},
> + {40, 24520},
> + {45, 20010},
> + {50, 16432},
> + {55, 13576},
> + {60, 11280},
> + {65, 9425},
> +};
> +
> +/*
> + * Note that the batres_vs_temp table must be strictly sorted by falling
> + * temperature values to work.
> + */
> +struct batres_vs_temp temp_to_batres_tbl_thermistor[] = {
> + { 40, 120},
> + { 30, 135},
> + { 20, 165},
> + { 10, 230},
> + { 00, 325},
> + {-10, 445},
> + {-20, 595},
> +};
> +
> +/*
> + * Note that the batres_vs_temp table must be strictly sorted by falling
> + * temperature values to work.
> + */
> +struct batres_vs_temp temp_to_batres_tbl_ext_thermistor[] = {
> + { 60, 300},
> + { 30, 300},
> + { 20, 300},
> + { 10, 300},
> + { 00, 300},
> + {-10, 300},
> + {-20, 300},
> +};
> +
> +/* battery resistance table for LI ION 9100 battery */
> +struct batres_vs_temp temp_to_batres_tbl_9100[] = {
> + { 60, 180},
> + { 30, 180},
> + { 20, 180},
> + { 10, 180},
> + { 00, 180},
> + {-10, 180},
> + {-20, 180},
> +};
> +
> +struct abx500_battery_type bat_type_thermistor[] = {
> +[BATTERY_UNKNOWN] = {
> + /* First element always represent the UNKNOWN battery */
> + .name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
> + .resis_high = 0,
> + .resis_low = 0,
> + .battery_resistance = 300,
> + .charge_full_design = 612,
> + .nominal_voltage = 3700,
> + .termination_vol = 4050,
> + .termination_curr = 200,
> + .recharge_vol = 3990,
> + .normal_cur_lvl = 400,
> + .normal_vol_lvl = 4100,
> + .maint_a_cur_lvl = 400,
> + .maint_a_vol_lvl = 4050,
> + .maint_a_chg_timer_h = 60,
> + .maint_b_cur_lvl = 400,
> + .maint_b_vol_lvl = 4000,
> + .maint_b_chg_timer_h = 200,
> + .low_high_cur_lvl = 300,
> + .low_high_vol_lvl = 4000,
> + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
> + .r_to_t_tbl = temp_tbl,
> + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
> + .v_to_cap_tbl = cap_tbl,
> + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
> + .batres_tbl = temp_to_batres_tbl_thermistor,
> +},
> +{
> + .name = POWER_SUPPLY_TECHNOLOGY_LIPO,
> + .resis_high = 53407,
> + .resis_low = 12500,
> + .battery_resistance = 300,
> + .charge_full_design = 900,
> + .nominal_voltage = 3600,
> + .termination_vol = 4150,
> + .termination_curr = 80,
> + .recharge_vol = 4130,
> + .normal_cur_lvl = 700,
> + .normal_vol_lvl = 4200,
> + .maint_a_cur_lvl = 600,
> + .maint_a_vol_lvl = 4150,
> + .maint_a_chg_timer_h = 60,
> + .maint_b_cur_lvl = 600,
> + .maint_b_vol_lvl = 4100,
> + .maint_b_chg_timer_h = 200,
> + .low_high_cur_lvl = 300,
> + .low_high_vol_lvl = 4000,
> + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_A_thermistor),
> + .r_to_t_tbl = temp_tbl_A_thermistor,
> + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_A_thermistor),
> + .v_to_cap_tbl = cap_tbl_A_thermistor,
> + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
> + .batres_tbl = temp_to_batres_tbl_thermistor,
> +
> +},
> +{
> + .name = POWER_SUPPLY_TECHNOLOGY_LIPO,
> + .resis_high = 165418,
> + .resis_low = 82869,
> + .battery_resistance = 300,
> + .charge_full_design = 900,
> + .nominal_voltage = 3600,
> + .termination_vol = 4150,
> + .termination_curr = 80,
> + .recharge_vol = 4130,
> + .normal_cur_lvl = 700,
> + .normal_vol_lvl = 4200,
> + .maint_a_cur_lvl = 600,
> + .maint_a_vol_lvl = 4150,
> + .maint_a_chg_timer_h = 60,
> + .maint_b_cur_lvl = 600,
> + .maint_b_vol_lvl = 4100,
> + .maint_b_chg_timer_h = 200,
> + .low_high_cur_lvl = 300,
> + .low_high_vol_lvl = 4000,
> + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_B_thermistor),
> + .r_to_t_tbl = temp_tbl_B_thermistor,
> + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_B_thermistor),
> + .v_to_cap_tbl = cap_tbl_B_thermistor,
> + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
> + .batres_tbl = temp_to_batres_tbl_thermistor,
> +},
> +};
> +
> +struct abx500_battery_type bat_type_ext_thermistor[] = {
> +[BATTERY_UNKNOWN] = {
> + /* First element always represent the UNKNOWN battery */
> + .name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
> + .resis_high = 0,
> + .resis_low = 0,
> + .battery_resistance = 300,
> + .charge_full_design = 612,
> + .nominal_voltage = 3700,
> + .termination_vol = 4050,
> + .termination_curr = 200,
> + .recharge_vol = 3990,
> + .normal_cur_lvl = 400,
> + .normal_vol_lvl = 4100,
> + .maint_a_cur_lvl = 400,
> + .maint_a_vol_lvl = 4050,
> + .maint_a_chg_timer_h = 60,
> + .maint_b_cur_lvl = 400,
> + .maint_b_vol_lvl = 4000,
> + .maint_b_chg_timer_h = 200,
> + .low_high_cur_lvl = 300,
> + .low_high_vol_lvl = 4000,
> + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
> + .r_to_t_tbl = temp_tbl,
> + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
> + .v_to_cap_tbl = cap_tbl,
> + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
> + .batres_tbl = temp_to_batres_tbl_thermistor,
> +},
> +/*
> + * These are the batteries that doesn't have an internal NTC resistor to measure
> + * its temperature. The temperature in this case is measure with a NTC placed
> + * near the battery but on the PCB.
> + */
> +{
> + .name = POWER_SUPPLY_TECHNOLOGY_LIPO,
> + .resis_high = 76000,
> + .resis_low = 53000,
> + .battery_resistance = 300,
> + .charge_full_design = 900,
> + .nominal_voltage = 3700,
> + .termination_vol = 4150,
> + .termination_curr = 100,
> + .recharge_vol = 4130,
> + .normal_cur_lvl = 700,
> + .normal_vol_lvl = 4200,
> + .maint_a_cur_lvl = 600,
> + .maint_a_vol_lvl = 4150,
> + .maint_a_chg_timer_h = 60,
> + .maint_b_cur_lvl = 600,
> + .maint_b_vol_lvl = 4100,
> + .maint_b_chg_timer_h = 200,
> + .low_high_cur_lvl = 300,
> + .low_high_vol_lvl = 4000,
> + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
> + .r_to_t_tbl = temp_tbl,
> + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
> + .v_to_cap_tbl = cap_tbl,
> + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
> + .batres_tbl = temp_to_batres_tbl_thermistor,
> +},
> +{
> + .name = POWER_SUPPLY_TECHNOLOGY_LION,
> + .resis_high = 30000,
> + .resis_low = 10000,
> + .battery_resistance = 300,
> + .charge_full_design = 950,
> + .nominal_voltage = 3700,
> + .termination_vol = 4150,
> + .termination_curr = 100,
> + .recharge_vol = 4130,
> + .normal_cur_lvl = 700,
> + .normal_vol_lvl = 4200,
> + .maint_a_cur_lvl = 600,
> + .maint_a_vol_lvl = 4150,
> + .maint_a_chg_timer_h = 60,
> + .maint_b_cur_lvl = 600,
> + .maint_b_vol_lvl = 4100,
> + .maint_b_chg_timer_h = 200,
> + .low_high_cur_lvl = 300,
> + .low_high_vol_lvl = 4000,
> + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
> + .r_to_t_tbl = temp_tbl,
> + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
> + .v_to_cap_tbl = cap_tbl,
> + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
> + .batres_tbl = temp_to_batres_tbl_thermistor,
> +},
> +{
> + .name = POWER_SUPPLY_TECHNOLOGY_LION,
> + .resis_high = 95000,
> + .resis_low = 76001,
> + .battery_resistance = 300,
> + .charge_full_design = 950,
> + .nominal_voltage = 3700,
> + .termination_vol = 4150,
> + .termination_curr = 100,
> + .recharge_vol = 4130,
> + .normal_cur_lvl = 700,
> + .normal_vol_lvl = 4200,
> + .maint_a_cur_lvl = 600,
> + .maint_a_vol_lvl = 4150,
> + .maint_a_chg_timer_h = 60,
> + .maint_b_cur_lvl = 600,
> + .maint_b_vol_lvl = 4100,
> + .maint_b_chg_timer_h = 200,
> + .low_high_cur_lvl = 300,
> + .low_high_vol_lvl = 4000,
> + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
> + .r_to_t_tbl = temp_tbl,
> + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
> + .v_to_cap_tbl = cap_tbl,
> + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
> + .batres_tbl = temp_to_batres_tbl_thermistor,
> +},
> +};
> +
> +static const struct abx500_bm_capacity_levels cap_levels = {
> + .critical = 2,
> + .low = 10,
> + .normal = 70,
> + .high = 95,
> + .full = 100,
> +};
> +
> +static const struct abx500_fg_parameters fg = {
> + .recovery_sleep_timer = 10,
> + .recovery_total_time = 100,
> + .init_timer = 1,
> + .init_discard_time = 5,
> + .init_total_time = 40,
> + .high_curr_time = 60,
> + .accu_charging = 30,
> + .accu_high_curr = 30,
> + .high_curr_threshold = 50,
> + .lowbat_threshold = 3100,
> + .battok_falling_th_sel0 = 2860,
> + .battok_raising_th_sel1 = 2860,
> + .user_cap_limit = 15,
> + .maint_thres = 97,
> +};
> +
> +static const struct abx500_maxim_parameters maxi_params = {
> + .ena_maxi = true,
> + .chg_curr = 910,
> + .wait_cycles = 10,
> + .charger_curr_step = 100,
> +};
> +
> +static const struct abx500_bm_charger_parameters chg = {
> + .usb_volt_max = 5500,
> + .usb_curr_max = 1500,
> + .ac_volt_max = 7500,
> + .ac_curr_max = 1500,
> +};
> +
> +struct abx500_bm_data ab8500_bm_data = {
> + .temp_under = 3,
> + .temp_low = 8,
> + .temp_high = 43,
> + .temp_over = 48,
> + .main_safety_tmr_h = 4,
> + .temp_interval_chg = 20,
> + .temp_interval_nochg = 120,
> + .usb_safety_tmr_h = 4,
> + .bkup_bat_v = BUP_VCH_SEL_2P6V,
> + .bkup_bat_i = BUP_ICH_SEL_150UA,
> + .no_maintenance = false,
> + .adc_therm = ABx500_ADC_THERM_BATCTRL,
> + .chg_unknown_bat = false,
> + .enable_overshoot = false,
> + .fg_res = 100,
> + .cap_levels = &cap_levels,
> + .bat_type = bat_type_thermistor,
> + .n_btypes = 3,
> + .batt_id = 0,
> + .interval_charging = 5,
> + .interval_not_charging = 120,
> + .temp_hysteresis = 3,
> + .gnd_lift_resistance = 34,
> + .maxi = &maxi_params,
> + .chg_params = &chg,
> + .fg_params = &fg,
> +};
> +
> +int __devinit
> +bmdevs_of_probe(struct device *dev,
> + struct device_node *np,
> + struct abx500_bm_plat_data *pdata)
> +{
> + int i, ret = 0, thermistor = NTC_INTERNAL;
> + const __be32 *ph;
> + const char *bat_tech;
> + struct abx500_bm_data *bat;
> + struct abx500_battery_type *btype;
> + struct device_node *np_bat_supply;
> + struct abx500_bmdevs_plat_data *plat_data = pdata->bmdev_pdata;
<nit>
This spacing is uncharacteristic of Linux drivers.
Usually, struct declarations come first.
</nit>
> + /* get phandle to 'supplied-to' node */
I thought you were going to reverse this?
> + ph = of_get_property(np, "supplied-to", &plat_data->num_supplicants);
> + if (ph == NULL) {
if (!ph) {
> + dev_err(dev, "no supplied_to property specified\n");
> + return -EINVAL;
> + }
> + plat_data->num_supplicants /= sizeof(int);
> + plat_data->supplied_to =
> + devm_kzalloc(dev, plat_data->num_supplicants *
> + sizeof(const char *), GFP_KERNEL);
> + if (plat_data->supplied_to == NULL) {
> + dev_err(dev, "%s no mem for supplied-to\n", __func__);
> + return -ENOMEM;
> + }
> + for (i = 0; i < plat_data->num_supplicants; ++i) {
> + np_bat_supply = of_find_node_by_phandle(be32_to_cpup(ph) + i);
Use: of_parse_phandle(np, "supplied-to", i) instead.
> + if (np_bat_supply == NULL) {
if (!np_bat_supply) {
> + dev_err(dev, "invalid supplied_to property\n");
> + return -EINVAL;
> + }
> + ret = of_property_read_string(np_bat_supply, "interface-name",
> + (const char **)(plat_data->supplied_to + i));
> + if (ret < 0) {
> + of_node_put(np_bat_supply);
> + dev_err(dev, "supply/interface name not found\n");
> + return ret;
> + }
> + dev_dbg(dev, "%s power supply interface_name:%s\n",
> + __func__, *(plat_data->supplied_to + i));
> + }
<remove>
> + /* get phandle to 'battery-info' node */
> + ph = of_get_property(np, "battery-info", NULL);
> + if (ph == NULL) {
> + dev_err(dev, "missing property battery-info\n");
> + return -EINVAL;
> + }
> + np_bat_supply = of_find_node_by_phandle(be32_to_cpup(ph));
</remove>
... and replace with: np_bat_supply = of_parse_phandle(np, "battery-info", 0) instead.
> + if (np_bat_supply == NULL) {
if (!np_bat_supply) {
I'll not mention this again.
> + dev_err(dev, "invalid battery-info node\n");
> + return -EINVAL;
> + }
> + if (of_property_read_bool(np_bat_supply,
> + "thermistor-on-batctrl") == false){
Replace with:
if (of_get_property(np_bat_supply, "thermistor-on-batctr", NULL))
np_bat_supply = true;
<remove>
> + dev_warn(dev, "missing property thermistor-on-batctrl\n");
> + thermistor = NTC_EXTERNAL;
> + }
</remove>
> + pdata->battery = &ab8500_bm_data;
> + bat = pdata->battery;
Why not: bat = &ab8500_bm_data
Or just use ab8500_bm_data in its own right?
> + if (thermistor == NTC_EXTERNAL) {
> + bat->n_btypes = 4;
> + bat->bat_type = bat_type_ext_thermistor;
> + bat->adc_therm = ABx500_ADC_THERM_BATTEMP;
> + }
> + ret = of_property_read_string(np_bat_supply, "battery-name", &bat_tech);
> + if (ret < 0) {
> + dev_warn(dev, "missing property battery-name/type\n");
> + bat_tech = "UNKNOWN";
> + }
> + of_node_put(np_bat_supply);
> + if (strcmp(bat_tech, "LION") == 0) {
> + bat->no_maintenance = true;
> + bat->chg_unknown_bat = true;
> + bat->bat_type[BATTERY_UNKNOWN].charge_full_design = 2600;
> + bat->bat_type[BATTERY_UNKNOWN].termination_vol = 4150;
> + bat->bat_type[BATTERY_UNKNOWN].recharge_vol = 4130;
> + bat->bat_type[BATTERY_UNKNOWN].normal_cur_lvl = 520;
> + bat->bat_type[BATTERY_UNKNOWN].normal_vol_lvl = 4200;
> + }
> + /* select the battery resolution table */
> + for (i = 0; i < bat->n_btypes; ++i) {
> + btype = (bat->bat_type + i);
> + if (thermistor == NTC_EXTERNAL) {
> + btype->batres_tbl =
> + temp_to_batres_tbl_ext_thermistor;
> + } else if (strcmp(bat_tech, "LION") == 0) {
Isn't strncmp safer, since you know the size of the comparison?
> + btype->batres_tbl =
> + temp_to_batres_tbl_9100;
> + } else {
> + btype->batres_tbl =
> + temp_to_batres_tbl_thermistor;
> + }
> + }
> + return 0;
> +}
> +EXPORT_SYMBOL(bmdevs_of_probe);
Why are you exporting?
> diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c
> index bba3cca..8e427e7 100644
> --- a/drivers/power/ab8500_btemp.c
> +++ b/drivers/power/ab8500_btemp.c
> @@ -93,7 +93,7 @@ struct ab8500_btemp {
> struct ab8500 *parent;
> struct ab8500_gpadc *gpadc;
> struct ab8500_fg *fg;
> - struct abx500_btemp_platform_data *pdata;
> + struct abx500_bmdevs_plat_data *pdata;
> struct abx500_bm_data *bat;
> struct power_supply btemp_psy;
> struct ab8500_btemp_events events;
> @@ -982,7 +982,7 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev)
> di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
>
> /* get btemp specific platform data */
> - di->pdata = plat_data->btemp;
> + di->pdata = plat_data->bmdev_pdata;
> if (!di->pdata) {
> dev_err(di->dev, "no btemp platform data supplied\n");
> ret = -EINVAL;
> diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
> index d4f0c98..5ff0d83 100644
> --- a/drivers/power/ab8500_charger.c
> +++ b/drivers/power/ab8500_charger.c
> @@ -220,7 +220,7 @@ struct ab8500_charger {
> bool autopower;
> struct ab8500 *parent;
> struct ab8500_gpadc *gpadc;
> - struct abx500_charger_platform_data *pdata;
> + struct abx500_bmdevs_plat_data *pdata;
> struct abx500_bm_data *bat;
> struct ab8500_charger_event_flags flags;
> struct ab8500_charger_usb_state usb_state;
> @@ -2555,7 +2555,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
> spin_lock_init(&di->usb_state.usb_lock);
>
> /* get charger specific platform data */
> - di->pdata = plat_data->charger;
> + di->pdata = plat_data->bmdev_pdata;
> if (!di->pdata) {
> dev_err(di->dev, "no charger platform data supplied\n");
> ret = -EINVAL;
> diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
> index bf02225..96741b8 100644
> --- a/drivers/power/ab8500_fg.c
> +++ b/drivers/power/ab8500_fg.c
> @@ -22,15 +22,14 @@
> #include <linux/platform_device.h>
> #include <linux/power_supply.h>
> #include <linux/kobject.h>
> -#include <linux/mfd/abx500/ab8500.h>
> -#include <linux/mfd/abx500.h>
> #include <linux/slab.h>
> -#include <linux/mfd/abx500/ab8500-bm.h>
> #include <linux/delay.h>
> -#include <linux/mfd/abx500/ab8500-gpadc.h>
> -#include <linux/mfd/abx500.h>
> #include <linux/time.h>
> #include <linux/completion.h>
> +#include <linux/mfd/abx500.h>
> +#include <linux/mfd/abx500/ab8500.h>
> +#include <linux/mfd/abx500/ab8500-bm.h>
> +#include <linux/mfd/abx500/ab8500-gpadc.h>
>
> #define MILLI_TO_MICRO 1000
> #define FG_LSB_IN_MA 1627
> @@ -212,7 +211,7 @@ struct ab8500_fg {
> struct ab8500_fg_avg_cap avg_cap;
> struct ab8500 *parent;
> struct ab8500_gpadc *gpadc;
> - struct abx500_fg_platform_data *pdata;
> + struct abx500_bmdevs_plat_data *pdata;
> struct abx500_bm_data *bat;
> struct power_supply fg_psy;
> struct workqueue_struct *fg_wq;
> @@ -544,14 +543,14 @@ cc_err:
> ret = abx500_set_register_interruptible(di->dev,
> AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU,
> SEC_TO_SAMPLE(10));
> - if (ret)
> + if (ret < 0)
I don't 'think' this change is required. abx500_set_register_interruptible
will only return !0 on error.
> goto fail;
>
> /* Start the CC */
> ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
> AB8500_RTC_CC_CONF_REG,
> (CC_DEEP_SLEEP_ENA | CC_PWR_UP_ENA));
> - if (ret)
> + if (ret < 0)
> goto fail;
> } else {
> di->turn_off_fg = false;
> @@ -2429,7 +2428,6 @@ static int __devexit ab8500_fg_remove(struct platform_device *pdev)
> flush_scheduled_work();
> power_supply_unregister(&di->fg_psy);
> platform_set_drvdata(pdev, NULL);
> - kfree(di);
> return ret;
> }
>
> @@ -2446,18 +2444,47 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev)
> {
> int i, irq;
> int ret = 0;
> - struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data;
> + struct abx500_bm_plat_data *plat_data
> + = pdev->dev.platform_data;
> + struct device_node *np = pdev->dev.of_node;
> struct ab8500_fg *di;
>
> + di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
> + if (!di) {
> + dev_err(&pdev->dev, "%s no mem for ab8500_fg\n", __func__);
> + return -ENOMEM;
> + }
> + if (np) {
> + if (!plat_data) {
Change these around.
if (!plat_data) {
if (np) {
<snip>
} else {
<ERROR>
}
}
> + plat_data =
> + devm_kzalloc(&pdev->dev, sizeof(*plat_data),
> + GFP_KERNEL);
> + if (!plat_data) {
> + dev_err(&pdev->dev,
> + "%s no mem for plat_data\n", __func__);
> + return -ENOMEM;
> + }
> + plat_data->bmdev_pdata = devm_kzalloc(&pdev->dev,
> + sizeof(*plat_data->bmdev_pdata), GFP_KERNEL);
> + if (!plat_data->bmdev_pdata) {
> + dev_err(&pdev->dev,
> + "%s no mem for pdata->fg\n",
> + __func__);
> + return -ENOMEM;
> + }
> + }
> + ret = bmdevs_of_probe(&pdev->dev, np, plat_data);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "failed to get platform data\n");
> + return ret;
> + }
> + }
<remove>
> if (!plat_data) {
> - dev_err(&pdev->dev, "No platform data\n");
> + dev_err(&pdev->dev,
> + "%s no fg platform data found\n", __func__);
> return -EINVAL;
> }
</remove>
> - di = kzalloc(sizeof(*di), GFP_KERNEL);
> - if (!di)
> - return -ENOMEM;
> -
> mutex_init(&di->cc_lock);
>
> /* get parent data */
> @@ -2466,19 +2493,17 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev)
> di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
>
> /* get fg specific platform data */
> - di->pdata = plat_data->fg;
> + di->pdata = plat_data->bmdev_pdata;
> if (!di->pdata) {
> dev_err(di->dev, "no fg platform data supplied\n");
> - ret = -EINVAL;
> - goto free_device_info;
> + return -EINVAL;
> }
>
> /* get battery specific platform data */
> di->bat = plat_data->battery;
> if (!di->bat) {
> dev_err(di->dev, "no battery platform data supplied\n");
> - ret = -EINVAL;
> - goto free_device_info;
> + return -EINVAL;
> }
>
> di->fg_psy.name = "ab8500_fg";
> @@ -2506,7 +2531,7 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev)
> di->fg_wq = create_singlethread_workqueue("ab8500_fg_wq");
> if (di->fg_wq == NULL) {
> dev_err(di->dev, "failed to create work queue\n");
> - goto free_device_info;
> + return -ENOMEM;
> }
>
> /* Init work for running the fg algorithm instantly */
> @@ -2605,12 +2630,14 @@ free_irq:
> }
> free_inst_curr_wq:
> destroy_workqueue(di->fg_wq);
> -free_device_info:
> - kfree(di);
> -
> return ret;
> }
>
> +static const struct of_device_id ab8500_fg_match[] = {
> + {.compatible = "stericsson,ab8500-fg",},
<nit>
Spaces:
{ .compatible = "stericsson,ab8500-fg", },
</nit>
> + {},
> +};
> +
> static struct platform_driver ab8500_fg_driver = {
> .probe = ab8500_fg_probe,
> .remove = __devexit_p(ab8500_fg_remove),
> @@ -2619,6 +2646,7 @@ static struct platform_driver ab8500_fg_driver = {
> .driver = {
> .name = "ab8500-fg",
> .owner = THIS_MODULE,
> + .of_match_table = ab8500_fg_match,
> },
> };
>
> diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
> index 804b88c..ba548e4 100644
> --- a/drivers/power/abx500_chargalg.c
> +++ b/drivers/power/abx500_chargalg.c
> @@ -231,7 +231,7 @@ struct abx500_chargalg {
> struct abx500_chargalg_charger_info chg_info;
> struct abx500_chargalg_battery_data batt_data;
> struct abx500_chargalg_suspension_status susp_status;
> - struct abx500_chargalg_platform_data *pdata;
> + struct abx500_bmdevs_plat_data *pdata;
> struct abx500_bm_data *bat;
> struct power_supply chargalg_psy;
> struct ux500_charger *ac_chg;
> @@ -1814,7 +1814,7 @@ static int __devinit abx500_chargalg_probe(struct platform_device *pdev)
> di->dev = &pdev->dev;
>
> plat_data = pdev->dev.platform_data;
> - di->pdata = plat_data->chargalg;
> + di->pdata = plat_data->bmdev_pdata;
> di->bat = plat_data->battery;
>
> /* chargalg supply */
> diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h
> index 1318ca6..286f8ac 100644
> --- a/include/linux/mfd/abx500.h
> +++ b/include/linux/mfd/abx500.h
> @@ -382,39 +382,30 @@ struct abx500_bm_data {
> int gnd_lift_resistance;
> const struct abx500_maxim_parameters *maxi;
> const struct abx500_bm_capacity_levels *cap_levels;
> - const struct abx500_battery_type *bat_type;
> + struct abx500_battery_type *bat_type;
> const struct abx500_bm_charger_parameters *chg_params;
> const struct abx500_fg_parameters *fg_params;
> };
>
> -struct abx500_chargalg_platform_data {
> - char **supplied_to;
> - size_t num_supplicants;
> +struct abx500_bmdevs_plat_data {
> + char **supplied_to;
> + size_t num_supplicants;
> + bool autopower_cfg;
> };
>
> -struct abx500_charger_platform_data {
> - char **supplied_to;
> - size_t num_supplicants;
> - bool autopower_cfg;
> -};
> -
> -struct abx500_btemp_platform_data {
> - char **supplied_to;
> - size_t num_supplicants;
> +struct abx500_bm_plat_data {
> + struct abx500_bm_data *battery;
> + struct abx500_bmdevs_plat_data *bmdev_pdata;
> };
>
> -struct abx500_fg_platform_data {
> - char **supplied_to;
> - size_t num_supplicants;
> +enum {
> + NTC_EXTERNAL = 0,
> + NTC_INTERNAL,
> };
>
> -struct abx500_bm_plat_data {
> - struct abx500_bm_data *battery;
> - struct abx500_charger_platform_data *charger;
> - struct abx500_btemp_platform_data *btemp;
> - struct abx500_fg_platform_data *fg;
> - struct abx500_chargalg_platform_data *chargalg;
> -};
> +int bmdevs_of_probe(struct device *dev,
> + struct device_node *np,
> + struct abx500_bm_plat_data *pdata);
>
> int abx500_set_register_interruptible(struct device *dev, u8 bank, u8 reg,
> u8 value);
> diff --git a/include/linux/mfd/abx500/ab8500-bm.h b/include/linux/mfd/abx500/ab8500-bm.h
> index 44310c9..d15b7f1 100644
> --- a/include/linux/mfd/abx500/ab8500-bm.h
> +++ b/include/linux/mfd/abx500/ab8500-bm.h
> @@ -422,6 +422,13 @@ struct ab8500_chargalg_platform_data {
> struct ab8500_btemp;
> struct ab8500_gpadc;
> struct ab8500_fg;
> +
> +extern struct abx500_bm_data ab8500_bm_data;
> +extern struct abx500_battery_type bat_type_ext_thermistor[];
> +extern struct batres_vs_temp temp_to_batres_tbl_ext_thermistor[];
> +extern struct batres_vs_temp temp_to_batres_tbl_9100[];
> +extern struct batres_vs_temp temp_to_batres_tbl_thermistor[];
> +
> #ifdef CONFIG_AB8500_BM
> void ab8500_fg_reinit(void);
> void ab8500_charger_usb_state_changed(u8 bm_usb_state, u16 mA);
> --
> 1.7.9.5
>
--
Lee Jones
Linaro ST-Ericsson Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* [PATCH 1/4] mfd: ab8500: add devicetree support for fuelgauge
From: Rajanikanth HV @ 2012-10-01 9:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20121001094929.GB6682@gmail.com>
did you have a look at arnd and anton comments regarding
'supplied-to' and boolean property
On Monday 01 October 2012 03:19 PM, Lee Jones wrote:
> On Mon, 01 Oct 2012, Rajanikanth H.V wrote:
>
>> From: "Rajanikanth H.V" <rajanikanth.hv@stericsson.com>
>>
>> - This patch adds device tree support for fuelguage driver
>> - optimize bm devices platform_data usage and of_probe(...)
>> Note: of_probe() routine for battery managed devices is made
>> common across all bm drivers.
>>
>> Signed-off-by: Rajanikanth H.V <rajanikanth.hv@stericsson.com>
>> ---
>> Documentation/devicetree/bindings/mfd/ab8500.txt | 8 +-
>> .../devicetree/bindings/power_supply/ab8500/fg.txt | 86 +++
>> arch/arm/boot/dts/dbx5x0.dtsi | 22 +-
>> drivers/mfd/ab8500-core.c | 1 +
>> drivers/power/Makefile | 2 +-
>> drivers/power/ab8500_bmdata.c | 549 ++++++++++++++++++++
>> drivers/power/ab8500_btemp.c | 4 +-
>> drivers/power/ab8500_charger.c | 4 +-
>> drivers/power/ab8500_fg.c | 76 ++-
>> drivers/power/abx500_chargalg.c | 4 +-
>> include/linux/mfd/abx500.h | 37 +-
>> include/linux/mfd/abx500/ab8500-bm.h | 7 +
>> 12 files changed, 744 insertions(+), 56 deletions(-)
>> create mode 100644 Documentation/devicetree/bindings/power_supply/ab8500/fg.txt
>> create mode 100644 drivers/power/ab8500_bmdata.c
>>
>> diff --git a/Documentation/devicetree/bindings/mfd/ab8500.txt b/Documentation/devicetree/bindings/mfd/ab8500.txt
>> index ce83c8d..762dc11 100644
>> --- a/Documentation/devicetree/bindings/mfd/ab8500.txt
>> +++ b/Documentation/devicetree/bindings/mfd/ab8500.txt
>> @@ -24,7 +24,13 @@ ab8500-bm : : : Battery Manager
>> ab8500-btemp : : : Battery Temperature
>> ab8500-charger : : : Battery Charger
>> ab8500-codec : : : Audio Codec
>> -ab8500-fg : : : Fuel Gauge
>> +ab8500-fg : : vddadc : Fuel Gauge
>> + : NCONV_ACCU : : Accumulate N Sample Conversion
>> + : BATT_OVV : : Battery Over Voltage
>> + : LOW_BAT_F : : LOW threshold battery voltage
>> + : CC_INT_CALIB : : Counter Counter Internal Calibration
>
> I think you mean: Coulomb Counter.
>
>> + : CCEOC : : Coulomb Counter End of Conversion
>> + : : :
>
> Random empty entry.
>
>> ab8500-gpadc : HW_CONV_END : vddadc : Analogue to Digital Converter
>> SW_CONV_END : :
>> ab8500-gpio : : : GPIO Controller
>> diff --git a/Documentation/devicetree/bindings/power_supply/ab8500/fg.txt b/Documentation/devicetree/bindings/power_supply/ab8500/fg.txt
>> new file mode 100644
>> index 0000000..caa33b0
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/power_supply/ab8500/fg.txt
>> @@ -0,0 +1,86 @@
>> +=== AB8500 Fuel Gauge Driver ===
>> +
>> +AB8500 is a mixed signal multimedia and power management
>> +device comprising: power and energy-management-module,
>> +wall-charger, usb-charger, audio codec, general purpose adc,
>> +tvout, clock management and sim card interface.
>> +
>> +Fuel-guage support is part of energy-management-module, the other
>
> Spelling.
>
>> +components of this module are:
>> +main-charger, usb-combo-charger and Battery temperature monitoring.
>> +
>> +The properties below describes the node for fuel guage driver.
>
> Spelling.
>
>> +
>> +Required Properties:
>> +- compatible = "stericsson,ab8500-fg"
>> +- interface-name:
>> + Name of the controller/driver which is part of energy-management-module
>> +- supplied-to:
>
> Still not sure about this property, or your justification for use.
>
>> + This property shall have dependent nodes which represent other
>> + energy-management-module.
>
> Plural?
>
>> + This is a logical binding w.r.t power supply events
>
> Proper English please, no slang.
>
>> + across energy-management-module drivers where-in, the
>
> Ill placed comma?
>
>> + runtime battery properties are shared along with uevent
>> + notification.
>
> Plural?
>
>> + ref: di->fg.external_power_changed =
>> + ab8500_fg_external_power_changed;
>> + ab8500_fg.c
>> +
>> + Need for this property:
>> + energy-management-module driver updates power-supply properties
>> + which are subset of events listed in 'enum power_supply_property',
>> + ref: power_supply.h file
>> + Event handler invokes power supply change notifier
>> + which in-turn invokes registered power supply class call-back
>> + based on the 'supplied-to' string.
>> + ref:
>> + power_supply_changed_work(..) ./drivers/power/power_supply_core.c
>> + di->fg_psy.external_power_changed
>> +
>> + example:
>> + ab8500-fg {
>> + /* dependent energy management modules */
>> + supplied-to = <&ab8500_chargalg &ab8500_usb>;
>> + };
>> +
>> + ab8500_battery_info: ab8500_bat_type {
>> + battery-type = <2>;
>> + thermistor-on-batctrl = <1>;
>
> You have this as a bool here, and ...
>> + };
>> +
>> +Other dependent node for fuel-gauge is:
>> + ab8500_battery_info: ab8500_bat_type {
>> + };
>> + This node will provide information on 'thermistor interface' and
>> + 'battery technology type' used.
>> +
>> +Properties of this node are:
>> +thermistor-on-batctrl:
>> + A boolean value indicating thermistor interface to battery
>> +
>> + Note:
>> + 'btemp' and 'batctrl' are the pins interfaced for battery temperature
>> + measurement, 'btemp' signal is used when NTC(negative temperature
>> + coefficient) resister is interfaced external to battery whereas
>> + 'batctrl' pin is used when NTC resister is internal to battery.
>> +
>> + e.g:
>> + ab8500_battery_info: ab8500_bat_type {
>> + thermistor-on-batctrl;
>
> ... a standard property here. I suggest you drop the bool value.
>
>> + };
>> + indiactes: NTC resister is internal to battery, 'batctrl' is used
>> + for thermal measurement.
>> +
>> + The absence of property 'thermal-on-batctrl' indicates
>> + NTC resister is external to battery and 'btemp' signal is used
>> + for thermal measurement.
>> +
>> +battery-type:
>> + This shall be the battery manufacturing technology type,
>> + allowed types are:
>> + "UNKNOWN" "NiMH" "LION" "LIPO" "LiFe" "NiCd" "LiMn"
>> + e.g:
>> + ab8500_battery_info: ab8500_bat_type {
>> + battery-name = "LION";
>> + }
>> +
>> diff --git a/arch/arm/boot/dts/dbx5x0.dtsi b/arch/arm/boot/dts/dbx5x0.dtsi
>> index 748ba7a..bd22c56 100644
>> --- a/arch/arm/boot/dts/dbx5x0.dtsi
>> +++ b/arch/arm/boot/dts/dbx5x0.dtsi
>> @@ -352,8 +352,28 @@
>> vddadc-supply = <&ab8500_ldo_tvout_reg>;
>> };
>>
>> - ab8500-usb {
>> + ab8500_battery_info: ab8500_bat_type {
>> + battery-name = "LION";
>
> All new properties have to be documented.
>
> Vendor specific properties should be prepended with the vendor name, so
> either write a generic binding document for all to use or prefix with
> 'stericsson,".
>
>> + thermistor-on-batctrl;
>> + };
>> +
>> + ab8500_chargalg: ab8500_chalg {
>> + compatible = "stericsson,ab8500-chargalg";
>> + interface-name = "ab8500_chargalg";
>
> Same with all of your new properties (I'll stop mentioning them now).
>
>> + battery-info = <&ab8500_battery_info>;
>> + supplied-to = <&ab8500_fuel_gauge>;
>
> Weren't you going to reverse this logic to be more inline with how
> the reset of Device Tree works?
>
>> + };
>> +
>> + ab8500_fuel_gauge: ab8500_fg {
>> + compatible = "stericsson,ab8500-fg";
>> + interface-name = "ab8500_fg";
>> + battery-info = <&ab8500_battery_info>;
>> + supplied-to = <&ab8500_chargalg &ab8500_usb>;
>
> As above.
>
>> + };
>> +
>> + ab8500_usb: ab8500_usb_if {
>
> What does 'if' mean?
>
>> compatible = "stericsson,ab8500-usb";
>> + interface-name = "ab8500_usb";
>
> Why is this required?
>
>> interrupts = < 90 0x4
>> 96 0x4
>> 14 0x4
>> diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
>> index 1667c77..6c3d7c2 100644
>> --- a/drivers/mfd/ab8500-core.c
>> +++ b/drivers/mfd/ab8500-core.c
>> @@ -1051,6 +1051,7 @@ static struct mfd_cell __devinitdata ab8500_bm_devs[] = {
>> },
>> {
>> .name = "ab8500-fg",
>> + .of_compatible = "stericsson,ab8500-fg",
>> .num_resources = ARRAY_SIZE(ab8500_fg_resources),
>> .resources = ab8500_fg_resources,
>> },
>> diff --git a/drivers/power/Makefile b/drivers/power/Makefile
>> index ee58afb..2c58d4e 100644
>> --- a/drivers/power/Makefile
>> +++ b/drivers/power/Makefile
>> @@ -34,7 +34,7 @@ obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o
>> obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
>> obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o
>> obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o
>> -obj-$(CONFIG_AB8500_BM) += ab8500_charger.o ab8500_btemp.o ab8500_fg.o abx500_chargalg.o
>> +obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o abx500_chargalg.o
>> obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o
>> obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o
>> obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o
>> diff --git a/drivers/power/ab8500_bmdata.c b/drivers/power/ab8500_bmdata.c
>> new file mode 100644
>> index 0000000..d0def3b
>> --- /dev/null
>> +++ b/drivers/power/ab8500_bmdata.c
>> @@ -0,0 +1,549 @@
>> +#include <linux/export.h>
>> +#include <linux/power_supply.h>
>> +#include <linux/of.h>
>> +#include <linux/mfd/abx500.h>
>> +#include <linux/mfd/abx500/ab8500.h>
>> +#include <linux/mfd/abx500/ab8500-bm.h>
>> +
>> +/*
>> + * These are the defined batteries that uses a NTC and ID resistor placed
>> + * inside of the battery pack.
>> + * Note that the res_to_temp table must be strictly sorted by falling resistance
>> + * values to work.
>> + */
>> +static struct abx500_res_to_temp temp_tbl_A_thermistor[] = {
>> + {-5, 53407},
>> + { 0, 48594},
>> + { 5, 43804},
>> + {10, 39188},
>> + {15, 34870},
>> + {20, 30933},
>> + {25, 27422},
>> + {30, 24347},
>> + {35, 21694},
>> + {40, 19431},
>> + {45, 17517},
>> + {50, 15908},
>> + {55, 14561},
>> + {60, 13437},
>> + {65, 12500},
>> +};
>> +static struct abx500_res_to_temp temp_tbl_B_thermistor[] = {
>> + {-5, 165418},
>> + { 0, 159024},
>> + { 5, 151921},
>> + {10, 144300},
>> + {15, 136424},
>> + {20, 128565},
>> + {25, 120978},
>> + {30, 113875},
>> + {35, 107397},
>> + {40, 101629},
>> + {45, 96592},
>> + {50, 92253},
>> + {55, 88569},
>> + {60, 85461},
>> + {65, 82869},
>> +};
>> +static struct abx500_v_to_cap cap_tbl_A_thermistor[] = {
>> + {4171, 100},
>> + {4114, 95},
>> + {4009, 83},
>> + {3947, 74},
>> + {3907, 67},
>> + {3863, 59},
>> + {3830, 56},
>> + {3813, 53},
>> + {3791, 46},
>> + {3771, 33},
>> + {3754, 25},
>> + {3735, 20},
>> + {3717, 17},
>> + {3681, 13},
>> + {3664, 8},
>> + {3651, 6},
>> + {3635, 5},
>> + {3560, 3},
>> + {3408, 1},
>> + {3247, 0},
>> +};
>> +static struct abx500_v_to_cap cap_tbl_B_thermistor[] = {
>> + {4161, 100},
>> + {4124, 98},
>> + {4044, 90},
>> + {4003, 85},
>> + {3966, 80},
>> + {3933, 75},
>> + {3888, 67},
>> + {3849, 60},
>> + {3813, 55},
>> + {3787, 47},
>> + {3772, 30},
>> + {3751, 25},
>> + {3718, 20},
>> + {3681, 16},
>> + {3660, 14},
>> + {3589, 10},
>> + {3546, 7},
>> + {3495, 4},
>> + {3404, 2},
>> + {3250, 0},
>> +};
>> +
>> +static struct abx500_v_to_cap cap_tbl[] = {
>> + {4186, 100},
>> + {4163, 99},
>> + {4114, 95},
>> + {4068, 90},
>> + {3990, 80},
>> + {3926, 70},
>> + {3898, 65},
>> + {3866, 60},
>> + {3833, 55},
>> + {3812, 50},
>> + {3787, 40},
>> + {3768, 30},
>> + {3747, 25},
>> + {3730, 20},
>> + {3705, 15},
>> + {3699, 14},
>> + {3684, 12},
>> + {3672, 9},
>> + {3657, 7},
>> + {3638, 6},
>> + {3556, 4},
>> + {3424, 2},
>> + {3317, 1},
>> + {3094, 0},
>> +};
>> +
>> +/*
>> + * Note that the res_to_temp table must be strictly sorted by falling
>> + * resistance values to work.
>> + */
>> +static struct abx500_res_to_temp temp_tbl[] = {
>> + {-5, 214834},
>> + { 0, 162943},
>> + { 5, 124820},
>> + {10, 96520},
>> + {15, 75306},
>> + {20, 59254},
>> + {25, 47000},
>> + {30, 37566},
>> + {35, 30245},
>> + {40, 24520},
>> + {45, 20010},
>> + {50, 16432},
>> + {55, 13576},
>> + {60, 11280},
>> + {65, 9425},
>> +};
>> +
>> +/*
>> + * Note that the batres_vs_temp table must be strictly sorted by falling
>> + * temperature values to work.
>> + */
>> +struct batres_vs_temp temp_to_batres_tbl_thermistor[] = {
>> + { 40, 120},
>> + { 30, 135},
>> + { 20, 165},
>> + { 10, 230},
>> + { 00, 325},
>> + {-10, 445},
>> + {-20, 595},
>> +};
>> +
>> +/*
>> + * Note that the batres_vs_temp table must be strictly sorted by falling
>> + * temperature values to work.
>> + */
>> +struct batres_vs_temp temp_to_batres_tbl_ext_thermistor[] = {
>> + { 60, 300},
>> + { 30, 300},
>> + { 20, 300},
>> + { 10, 300},
>> + { 00, 300},
>> + {-10, 300},
>> + {-20, 300},
>> +};
>> +
>> +/* battery resistance table for LI ION 9100 battery */
>> +struct batres_vs_temp temp_to_batres_tbl_9100[] = {
>> + { 60, 180},
>> + { 30, 180},
>> + { 20, 180},
>> + { 10, 180},
>> + { 00, 180},
>> + {-10, 180},
>> + {-20, 180},
>> +};
>> +
>> +struct abx500_battery_type bat_type_thermistor[] = {
>> +[BATTERY_UNKNOWN] = {
>> + /* First element always represent the UNKNOWN battery */
>> + .name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
>> + .resis_high = 0,
>> + .resis_low = 0,
>> + .battery_resistance = 300,
>> + .charge_full_design = 612,
>> + .nominal_voltage = 3700,
>> + .termination_vol = 4050,
>> + .termination_curr = 200,
>> + .recharge_vol = 3990,
>> + .normal_cur_lvl = 400,
>> + .normal_vol_lvl = 4100,
>> + .maint_a_cur_lvl = 400,
>> + .maint_a_vol_lvl = 4050,
>> + .maint_a_chg_timer_h = 60,
>> + .maint_b_cur_lvl = 400,
>> + .maint_b_vol_lvl = 4000,
>> + .maint_b_chg_timer_h = 200,
>> + .low_high_cur_lvl = 300,
>> + .low_high_vol_lvl = 4000,
>> + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
>> + .r_to_t_tbl = temp_tbl,
>> + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
>> + .v_to_cap_tbl = cap_tbl,
>> + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
>> + .batres_tbl = temp_to_batres_tbl_thermistor,
>> +},
>> +{
>> + .name = POWER_SUPPLY_TECHNOLOGY_LIPO,
>> + .resis_high = 53407,
>> + .resis_low = 12500,
>> + .battery_resistance = 300,
>> + .charge_full_design = 900,
>> + .nominal_voltage = 3600,
>> + .termination_vol = 4150,
>> + .termination_curr = 80,
>> + .recharge_vol = 4130,
>> + .normal_cur_lvl = 700,
>> + .normal_vol_lvl = 4200,
>> + .maint_a_cur_lvl = 600,
>> + .maint_a_vol_lvl = 4150,
>> + .maint_a_chg_timer_h = 60,
>> + .maint_b_cur_lvl = 600,
>> + .maint_b_vol_lvl = 4100,
>> + .maint_b_chg_timer_h = 200,
>> + .low_high_cur_lvl = 300,
>> + .low_high_vol_lvl = 4000,
>> + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_A_thermistor),
>> + .r_to_t_tbl = temp_tbl_A_thermistor,
>> + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_A_thermistor),
>> + .v_to_cap_tbl = cap_tbl_A_thermistor,
>> + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
>> + .batres_tbl = temp_to_batres_tbl_thermistor,
>> +
>> +},
>> +{
>> + .name = POWER_SUPPLY_TECHNOLOGY_LIPO,
>> + .resis_high = 165418,
>> + .resis_low = 82869,
>> + .battery_resistance = 300,
>> + .charge_full_design = 900,
>> + .nominal_voltage = 3600,
>> + .termination_vol = 4150,
>> + .termination_curr = 80,
>> + .recharge_vol = 4130,
>> + .normal_cur_lvl = 700,
>> + .normal_vol_lvl = 4200,
>> + .maint_a_cur_lvl = 600,
>> + .maint_a_vol_lvl = 4150,
>> + .maint_a_chg_timer_h = 60,
>> + .maint_b_cur_lvl = 600,
>> + .maint_b_vol_lvl = 4100,
>> + .maint_b_chg_timer_h = 200,
>> + .low_high_cur_lvl = 300,
>> + .low_high_vol_lvl = 4000,
>> + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_B_thermistor),
>> + .r_to_t_tbl = temp_tbl_B_thermistor,
>> + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_B_thermistor),
>> + .v_to_cap_tbl = cap_tbl_B_thermistor,
>> + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
>> + .batres_tbl = temp_to_batres_tbl_thermistor,
>> +},
>> +};
>> +
>> +struct abx500_battery_type bat_type_ext_thermistor[] = {
>> +[BATTERY_UNKNOWN] = {
>> + /* First element always represent the UNKNOWN battery */
>> + .name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
>> + .resis_high = 0,
>> + .resis_low = 0,
>> + .battery_resistance = 300,
>> + .charge_full_design = 612,
>> + .nominal_voltage = 3700,
>> + .termination_vol = 4050,
>> + .termination_curr = 200,
>> + .recharge_vol = 3990,
>> + .normal_cur_lvl = 400,
>> + .normal_vol_lvl = 4100,
>> + .maint_a_cur_lvl = 400,
>> + .maint_a_vol_lvl = 4050,
>> + .maint_a_chg_timer_h = 60,
>> + .maint_b_cur_lvl = 400,
>> + .maint_b_vol_lvl = 4000,
>> + .maint_b_chg_timer_h = 200,
>> + .low_high_cur_lvl = 300,
>> + .low_high_vol_lvl = 4000,
>> + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
>> + .r_to_t_tbl = temp_tbl,
>> + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
>> + .v_to_cap_tbl = cap_tbl,
>> + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
>> + .batres_tbl = temp_to_batres_tbl_thermistor,
>> +},
>> +/*
>> + * These are the batteries that doesn't have an internal NTC resistor to measure
>> + * its temperature. The temperature in this case is measure with a NTC placed
>> + * near the battery but on the PCB.
>> + */
>> +{
>> + .name = POWER_SUPPLY_TECHNOLOGY_LIPO,
>> + .resis_high = 76000,
>> + .resis_low = 53000,
>> + .battery_resistance = 300,
>> + .charge_full_design = 900,
>> + .nominal_voltage = 3700,
>> + .termination_vol = 4150,
>> + .termination_curr = 100,
>> + .recharge_vol = 4130,
>> + .normal_cur_lvl = 700,
>> + .normal_vol_lvl = 4200,
>> + .maint_a_cur_lvl = 600,
>> + .maint_a_vol_lvl = 4150,
>> + .maint_a_chg_timer_h = 60,
>> + .maint_b_cur_lvl = 600,
>> + .maint_b_vol_lvl = 4100,
>> + .maint_b_chg_timer_h = 200,
>> + .low_high_cur_lvl = 300,
>> + .low_high_vol_lvl = 4000,
>> + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
>> + .r_to_t_tbl = temp_tbl,
>> + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
>> + .v_to_cap_tbl = cap_tbl,
>> + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
>> + .batres_tbl = temp_to_batres_tbl_thermistor,
>> +},
>> +{
>> + .name = POWER_SUPPLY_TECHNOLOGY_LION,
>> + .resis_high = 30000,
>> + .resis_low = 10000,
>> + .battery_resistance = 300,
>> + .charge_full_design = 950,
>> + .nominal_voltage = 3700,
>> + .termination_vol = 4150,
>> + .termination_curr = 100,
>> + .recharge_vol = 4130,
>> + .normal_cur_lvl = 700,
>> + .normal_vol_lvl = 4200,
>> + .maint_a_cur_lvl = 600,
>> + .maint_a_vol_lvl = 4150,
>> + .maint_a_chg_timer_h = 60,
>> + .maint_b_cur_lvl = 600,
>> + .maint_b_vol_lvl = 4100,
>> + .maint_b_chg_timer_h = 200,
>> + .low_high_cur_lvl = 300,
>> + .low_high_vol_lvl = 4000,
>> + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
>> + .r_to_t_tbl = temp_tbl,
>> + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
>> + .v_to_cap_tbl = cap_tbl,
>> + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
>> + .batres_tbl = temp_to_batres_tbl_thermistor,
>> +},
>> +{
>> + .name = POWER_SUPPLY_TECHNOLOGY_LION,
>> + .resis_high = 95000,
>> + .resis_low = 76001,
>> + .battery_resistance = 300,
>> + .charge_full_design = 950,
>> + .nominal_voltage = 3700,
>> + .termination_vol = 4150,
>> + .termination_curr = 100,
>> + .recharge_vol = 4130,
>> + .normal_cur_lvl = 700,
>> + .normal_vol_lvl = 4200,
>> + .maint_a_cur_lvl = 600,
>> + .maint_a_vol_lvl = 4150,
>> + .maint_a_chg_timer_h = 60,
>> + .maint_b_cur_lvl = 600,
>> + .maint_b_vol_lvl = 4100,
>> + .maint_b_chg_timer_h = 200,
>> + .low_high_cur_lvl = 300,
>> + .low_high_vol_lvl = 4000,
>> + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
>> + .r_to_t_tbl = temp_tbl,
>> + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
>> + .v_to_cap_tbl = cap_tbl,
>> + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
>> + .batres_tbl = temp_to_batres_tbl_thermistor,
>> +},
>> +};
>> +
>> +static const struct abx500_bm_capacity_levels cap_levels = {
>> + .critical = 2,
>> + .low = 10,
>> + .normal = 70,
>> + .high = 95,
>> + .full = 100,
>> +};
>> +
>> +static const struct abx500_fg_parameters fg = {
>> + .recovery_sleep_timer = 10,
>> + .recovery_total_time = 100,
>> + .init_timer = 1,
>> + .init_discard_time = 5,
>> + .init_total_time = 40,
>> + .high_curr_time = 60,
>> + .accu_charging = 30,
>> + .accu_high_curr = 30,
>> + .high_curr_threshold = 50,
>> + .lowbat_threshold = 3100,
>> + .battok_falling_th_sel0 = 2860,
>> + .battok_raising_th_sel1 = 2860,
>> + .user_cap_limit = 15,
>> + .maint_thres = 97,
>> +};
>> +
>> +static const struct abx500_maxim_parameters maxi_params = {
>> + .ena_maxi = true,
>> + .chg_curr = 910,
>> + .wait_cycles = 10,
>> + .charger_curr_step = 100,
>> +};
>> +
>> +static const struct abx500_bm_charger_parameters chg = {
>> + .usb_volt_max = 5500,
>> + .usb_curr_max = 1500,
>> + .ac_volt_max = 7500,
>> + .ac_curr_max = 1500,
>> +};
>> +
>> +struct abx500_bm_data ab8500_bm_data = {
>> + .temp_under = 3,
>> + .temp_low = 8,
>> + .temp_high = 43,
>> + .temp_over = 48,
>> + .main_safety_tmr_h = 4,
>> + .temp_interval_chg = 20,
>> + .temp_interval_nochg = 120,
>> + .usb_safety_tmr_h = 4,
>> + .bkup_bat_v = BUP_VCH_SEL_2P6V,
>> + .bkup_bat_i = BUP_ICH_SEL_150UA,
>> + .no_maintenance = false,
>> + .adc_therm = ABx500_ADC_THERM_BATCTRL,
>> + .chg_unknown_bat = false,
>> + .enable_overshoot = false,
>> + .fg_res = 100,
>> + .cap_levels = &cap_levels,
>> + .bat_type = bat_type_thermistor,
>> + .n_btypes = 3,
>> + .batt_id = 0,
>> + .interval_charging = 5,
>> + .interval_not_charging = 120,
>> + .temp_hysteresis = 3,
>> + .gnd_lift_resistance = 34,
>> + .maxi = &maxi_params,
>> + .chg_params = &chg,
>> + .fg_params = &fg,
>> +};
>> +
>> +int __devinit
>> +bmdevs_of_probe(struct device *dev,
>> + struct device_node *np,
>> + struct abx500_bm_plat_data *pdata)
>> +{
>> + int i, ret = 0, thermistor = NTC_INTERNAL;
>> + const __be32 *ph;
>> + const char *bat_tech;
>> + struct abx500_bm_data *bat;
>> + struct abx500_battery_type *btype;
>> + struct device_node *np_bat_supply;
>> + struct abx500_bmdevs_plat_data *plat_data = pdata->bmdev_pdata;
>
> <nit>
>
> This spacing is uncharacteristic of Linux drivers.
>
> Usually, struct declarations come first.
>
> </nit>
>
>> + /* get phandle to 'supplied-to' node */
>
> I thought you were going to reverse this?
>
>> + ph = of_get_property(np, "supplied-to", &plat_data->num_supplicants);
>> + if (ph == NULL) {
>
> if (!ph) {
>
>> + dev_err(dev, "no supplied_to property specified\n");
>> + return -EINVAL;
>> + }
>> + plat_data->num_supplicants /= sizeof(int);
>> + plat_data->supplied_to =
>> + devm_kzalloc(dev, plat_data->num_supplicants *
>> + sizeof(const char *), GFP_KERNEL);
>> + if (plat_data->supplied_to == NULL) {
>> + dev_err(dev, "%s no mem for supplied-to\n", __func__);
>> + return -ENOMEM;
>> + }
>> + for (i = 0; i < plat_data->num_supplicants; ++i) {
>> + np_bat_supply = of_find_node_by_phandle(be32_to_cpup(ph) + i);
>
> Use: of_parse_phandle(np, "supplied-to", i) instead.
>
>> + if (np_bat_supply == NULL) {
>
> if (!np_bat_supply) {
>
>> + dev_err(dev, "invalid supplied_to property\n");
>> + return -EINVAL;
>> + }
>> + ret = of_property_read_string(np_bat_supply, "interface-name",
>> + (const char **)(plat_data->supplied_to + i));
>> + if (ret < 0) {
>> + of_node_put(np_bat_supply);
>> + dev_err(dev, "supply/interface name not found\n");
>> + return ret;
>> + }
>> + dev_dbg(dev, "%s power supply interface_name:%s\n",
>> + __func__, *(plat_data->supplied_to + i));
>> + }
>
> <remove>
>
>> + /* get phandle to 'battery-info' node */
>> + ph = of_get_property(np, "battery-info", NULL);
>> + if (ph == NULL) {
>> + dev_err(dev, "missing property battery-info\n");
>> + return -EINVAL;
>> + }
>> + np_bat_supply = of_find_node_by_phandle(be32_to_cpup(ph));
>
> </remove>
>
> ... and replace with: np_bat_supply = of_parse_phandle(np, "battery-info", 0) instead.
>
>> + if (np_bat_supply == NULL) {
>
> if (!np_bat_supply) {
>
> I'll not mention this again.
>
>> + dev_err(dev, "invalid battery-info node\n");
>> + return -EINVAL;
>> + }
>> + if (of_property_read_bool(np_bat_supply,
>> + "thermistor-on-batctrl") == false){
>
> Replace with:
> if (of_get_property(np_bat_supply, "thermistor-on-batctr", NULL))
> np_bat_supply = true;
>
> <remove>
>
>> + dev_warn(dev, "missing property thermistor-on-batctrl\n");
>> + thermistor = NTC_EXTERNAL;
>> + }
>
> </remove>
>
>> + pdata->battery = &ab8500_bm_data;
>> + bat = pdata->battery;
>
> Why not: bat = &ab8500_bm_data
>
> Or just use ab8500_bm_data in its own right?
>
>> + if (thermistor == NTC_EXTERNAL) {
>> + bat->n_btypes = 4;
>> + bat->bat_type = bat_type_ext_thermistor;
>> + bat->adc_therm = ABx500_ADC_THERM_BATTEMP;
>> + }
>> + ret = of_property_read_string(np_bat_supply, "battery-name", &bat_tech);
>> + if (ret < 0) {
>> + dev_warn(dev, "missing property battery-name/type\n");
>> + bat_tech = "UNKNOWN";
>> + }
>> + of_node_put(np_bat_supply);
>> + if (strcmp(bat_tech, "LION") == 0) {
>> + bat->no_maintenance = true;
>> + bat->chg_unknown_bat = true;
>> + bat->bat_type[BATTERY_UNKNOWN].charge_full_design = 2600;
>> + bat->bat_type[BATTERY_UNKNOWN].termination_vol = 4150;
>> + bat->bat_type[BATTERY_UNKNOWN].recharge_vol = 4130;
>> + bat->bat_type[BATTERY_UNKNOWN].normal_cur_lvl = 520;
>> + bat->bat_type[BATTERY_UNKNOWN].normal_vol_lvl = 4200;
>> + }
>> + /* select the battery resolution table */
>> + for (i = 0; i < bat->n_btypes; ++i) {
>> + btype = (bat->bat_type + i);
>> + if (thermistor == NTC_EXTERNAL) {
>> + btype->batres_tbl =
>> + temp_to_batres_tbl_ext_thermistor;
>> + } else if (strcmp(bat_tech, "LION") == 0) {
>
> Isn't strncmp safer, since you know the size of the comparison?
>
>> + btype->batres_tbl =
>> + temp_to_batres_tbl_9100;
>> + } else {
>> + btype->batres_tbl =
>> + temp_to_batres_tbl_thermistor;
>> + }
>> + }
>> + return 0;
>> +}
>> +EXPORT_SYMBOL(bmdevs_of_probe);
>
> Why are you exporting?
>
>> diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c
>> index bba3cca..8e427e7 100644
>> --- a/drivers/power/ab8500_btemp.c
>> +++ b/drivers/power/ab8500_btemp.c
>> @@ -93,7 +93,7 @@ struct ab8500_btemp {
>> struct ab8500 *parent;
>> struct ab8500_gpadc *gpadc;
>> struct ab8500_fg *fg;
>> - struct abx500_btemp_platform_data *pdata;
>> + struct abx500_bmdevs_plat_data *pdata;
>> struct abx500_bm_data *bat;
>> struct power_supply btemp_psy;
>> struct ab8500_btemp_events events;
>> @@ -982,7 +982,7 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev)
>> di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
>>
>> /* get btemp specific platform data */
>> - di->pdata = plat_data->btemp;
>> + di->pdata = plat_data->bmdev_pdata;
>> if (!di->pdata) {
>> dev_err(di->dev, "no btemp platform data supplied\n");
>> ret = -EINVAL;
>> diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
>> index d4f0c98..5ff0d83 100644
>> --- a/drivers/power/ab8500_charger.c
>> +++ b/drivers/power/ab8500_charger.c
>> @@ -220,7 +220,7 @@ struct ab8500_charger {
>> bool autopower;
>> struct ab8500 *parent;
>> struct ab8500_gpadc *gpadc;
>> - struct abx500_charger_platform_data *pdata;
>> + struct abx500_bmdevs_plat_data *pdata;
>> struct abx500_bm_data *bat;
>> struct ab8500_charger_event_flags flags;
>> struct ab8500_charger_usb_state usb_state;
>> @@ -2555,7 +2555,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
>> spin_lock_init(&di->usb_state.usb_lock);
>>
>> /* get charger specific platform data */
>> - di->pdata = plat_data->charger;
>> + di->pdata = plat_data->bmdev_pdata;
>> if (!di->pdata) {
>> dev_err(di->dev, "no charger platform data supplied\n");
>> ret = -EINVAL;
>> diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
>> index bf02225..96741b8 100644
>> --- a/drivers/power/ab8500_fg.c
>> +++ b/drivers/power/ab8500_fg.c
>> @@ -22,15 +22,14 @@
>> #include <linux/platform_device.h>
>> #include <linux/power_supply.h>
>> #include <linux/kobject.h>
>> -#include <linux/mfd/abx500/ab8500.h>
>> -#include <linux/mfd/abx500.h>
>> #include <linux/slab.h>
>> -#include <linux/mfd/abx500/ab8500-bm.h>
>> #include <linux/delay.h>
>> -#include <linux/mfd/abx500/ab8500-gpadc.h>
>> -#include <linux/mfd/abx500.h>
>> #include <linux/time.h>
>> #include <linux/completion.h>
>> +#include <linux/mfd/abx500.h>
>> +#include <linux/mfd/abx500/ab8500.h>
>> +#include <linux/mfd/abx500/ab8500-bm.h>
>> +#include <linux/mfd/abx500/ab8500-gpadc.h>
>>
>> #define MILLI_TO_MICRO 1000
>> #define FG_LSB_IN_MA 1627
>> @@ -212,7 +211,7 @@ struct ab8500_fg {
>> struct ab8500_fg_avg_cap avg_cap;
>> struct ab8500 *parent;
>> struct ab8500_gpadc *gpadc;
>> - struct abx500_fg_platform_data *pdata;
>> + struct abx500_bmdevs_plat_data *pdata;
>> struct abx500_bm_data *bat;
>> struct power_supply fg_psy;
>> struct workqueue_struct *fg_wq;
>> @@ -544,14 +543,14 @@ cc_err:
>> ret = abx500_set_register_interruptible(di->dev,
>> AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU,
>> SEC_TO_SAMPLE(10));
>> - if (ret)
>> + if (ret < 0)
>
> I don't 'think' this change is required. abx500_set_register_interruptible
> will only return !0 on error.
>
>> goto fail;
>>
>> /* Start the CC */
>> ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
>> AB8500_RTC_CC_CONF_REG,
>> (CC_DEEP_SLEEP_ENA | CC_PWR_UP_ENA));
>> - if (ret)
>> + if (ret < 0)
>> goto fail;
>> } else {
>> di->turn_off_fg = false;
>> @@ -2429,7 +2428,6 @@ static int __devexit ab8500_fg_remove(struct platform_device *pdev)
>> flush_scheduled_work();
>> power_supply_unregister(&di->fg_psy);
>> platform_set_drvdata(pdev, NULL);
>> - kfree(di);
>> return ret;
>> }
>>
>> @@ -2446,18 +2444,47 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev)
>> {
>> int i, irq;
>> int ret = 0;
>> - struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data;
>> + struct abx500_bm_plat_data *plat_data
>> + = pdev->dev.platform_data;
>> + struct device_node *np = pdev->dev.of_node;
>> struct ab8500_fg *di;
>>
>> + di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
>> + if (!di) {
>> + dev_err(&pdev->dev, "%s no mem for ab8500_fg\n", __func__);
>> + return -ENOMEM;
>> + }
>> + if (np) {
>> + if (!plat_data) {
>
> Change these around.
>
> if (!plat_data) {
> if (np) {
> <snip>
> } else {
> <ERROR>
> }
> }
>
>> + plat_data =
>> + devm_kzalloc(&pdev->dev, sizeof(*plat_data),
>> + GFP_KERNEL);
>> + if (!plat_data) {
>> + dev_err(&pdev->dev,
>> + "%s no mem for plat_data\n", __func__);
>> + return -ENOMEM;
>> + }
>> + plat_data->bmdev_pdata = devm_kzalloc(&pdev->dev,
>> + sizeof(*plat_data->bmdev_pdata), GFP_KERNEL);
>> + if (!plat_data->bmdev_pdata) {
>> + dev_err(&pdev->dev,
>> + "%s no mem for pdata->fg\n",
>> + __func__);
>> + return -ENOMEM;
>> + }
>> + }
>> + ret = bmdevs_of_probe(&pdev->dev, np, plat_data);
>> + if (ret < 0) {
>> + dev_err(&pdev->dev, "failed to get platform data\n");
>> + return ret;
>> + }
>> + }
>
> <remove>
>
>> if (!plat_data) {
>> - dev_err(&pdev->dev, "No platform data\n");
>> + dev_err(&pdev->dev,
>> + "%s no fg platform data found\n", __func__);
>> return -EINVAL;
>> }
> </remove>
>
>> - di = kzalloc(sizeof(*di), GFP_KERNEL);
>> - if (!di)
>> - return -ENOMEM;
>> -
>> mutex_init(&di->cc_lock);
>>
>> /* get parent data */
>> @@ -2466,19 +2493,17 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev)
>> di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
>>
>> /* get fg specific platform data */
>> - di->pdata = plat_data->fg;
>> + di->pdata = plat_data->bmdev_pdata;
>> if (!di->pdata) {
>> dev_err(di->dev, "no fg platform data supplied\n");
>> - ret = -EINVAL;
>> - goto free_device_info;
>> + return -EINVAL;
>> }
>>
>> /* get battery specific platform data */
>> di->bat = plat_data->battery;
>> if (!di->bat) {
>> dev_err(di->dev, "no battery platform data supplied\n");
>> - ret = -EINVAL;
>> - goto free_device_info;
>> + return -EINVAL;
>> }
>>
>> di->fg_psy.name = "ab8500_fg";
>> @@ -2506,7 +2531,7 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev)
>> di->fg_wq = create_singlethread_workqueue("ab8500_fg_wq");
>> if (di->fg_wq == NULL) {
>> dev_err(di->dev, "failed to create work queue\n");
>> - goto free_device_info;
>> + return -ENOMEM;
>> }
>>
>> /* Init work for running the fg algorithm instantly */
>> @@ -2605,12 +2630,14 @@ free_irq:
>> }
>> free_inst_curr_wq:
>> destroy_workqueue(di->fg_wq);
>> -free_device_info:
>> - kfree(di);
>> -
>> return ret;
>> }
>>
>> +static const struct of_device_id ab8500_fg_match[] = {
>> + {.compatible = "stericsson,ab8500-fg",},
>
> <nit>
>
> Spaces:
>
> { .compatible = "stericsson,ab8500-fg", },
>
> </nit>
>
>> + {},
>> +};
>> +
>> static struct platform_driver ab8500_fg_driver = {
>> .probe = ab8500_fg_probe,
>> .remove = __devexit_p(ab8500_fg_remove),
>> @@ -2619,6 +2646,7 @@ static struct platform_driver ab8500_fg_driver = {
>> .driver = {
>> .name = "ab8500-fg",
>> .owner = THIS_MODULE,
>> + .of_match_table = ab8500_fg_match,
>> },
>> };
>>
>> diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
>> index 804b88c..ba548e4 100644
>> --- a/drivers/power/abx500_chargalg.c
>> +++ b/drivers/power/abx500_chargalg.c
>> @@ -231,7 +231,7 @@ struct abx500_chargalg {
>> struct abx500_chargalg_charger_info chg_info;
>> struct abx500_chargalg_battery_data batt_data;
>> struct abx500_chargalg_suspension_status susp_status;
>> - struct abx500_chargalg_platform_data *pdata;
>> + struct abx500_bmdevs_plat_data *pdata;
>> struct abx500_bm_data *bat;
>> struct power_supply chargalg_psy;
>> struct ux500_charger *ac_chg;
>> @@ -1814,7 +1814,7 @@ static int __devinit abx500_chargalg_probe(struct platform_device *pdev)
>> di->dev = &pdev->dev;
>>
>> plat_data = pdev->dev.platform_data;
>> - di->pdata = plat_data->chargalg;
>> + di->pdata = plat_data->bmdev_pdata;
>> di->bat = plat_data->battery;
>>
>> /* chargalg supply */
>> diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h
>> index 1318ca6..286f8ac 100644
>> --- a/include/linux/mfd/abx500.h
>> +++ b/include/linux/mfd/abx500.h
>> @@ -382,39 +382,30 @@ struct abx500_bm_data {
>> int gnd_lift_resistance;
>> const struct abx500_maxim_parameters *maxi;
>> const struct abx500_bm_capacity_levels *cap_levels;
>> - const struct abx500_battery_type *bat_type;
>> + struct abx500_battery_type *bat_type;
>> const struct abx500_bm_charger_parameters *chg_params;
>> const struct abx500_fg_parameters *fg_params;
>> };
>>
>> -struct abx500_chargalg_platform_data {
>> - char **supplied_to;
>> - size_t num_supplicants;
>> +struct abx500_bmdevs_plat_data {
>> + char **supplied_to;
>> + size_t num_supplicants;
>> + bool autopower_cfg;
>> };
>>
>> -struct abx500_charger_platform_data {
>> - char **supplied_to;
>> - size_t num_supplicants;
>> - bool autopower_cfg;
>> -};
>> -
>> -struct abx500_btemp_platform_data {
>> - char **supplied_to;
>> - size_t num_supplicants;
>> +struct abx500_bm_plat_data {
>> + struct abx500_bm_data *battery;
>> + struct abx500_bmdevs_plat_data *bmdev_pdata;
>> };
>>
>> -struct abx500_fg_platform_data {
>> - char **supplied_to;
>> - size_t num_supplicants;
>> +enum {
>> + NTC_EXTERNAL = 0,
>> + NTC_INTERNAL,
>> };
>>
>> -struct abx500_bm_plat_data {
>> - struct abx500_bm_data *battery;
>> - struct abx500_charger_platform_data *charger;
>> - struct abx500_btemp_platform_data *btemp;
>> - struct abx500_fg_platform_data *fg;
>> - struct abx500_chargalg_platform_data *chargalg;
>> -};
>> +int bmdevs_of_probe(struct device *dev,
>> + struct device_node *np,
>> + struct abx500_bm_plat_data *pdata);
>>
>> int abx500_set_register_interruptible(struct device *dev, u8 bank, u8 reg,
>> u8 value);
>> diff --git a/include/linux/mfd/abx500/ab8500-bm.h b/include/linux/mfd/abx500/ab8500-bm.h
>> index 44310c9..d15b7f1 100644
>> --- a/include/linux/mfd/abx500/ab8500-bm.h
>> +++ b/include/linux/mfd/abx500/ab8500-bm.h
>> @@ -422,6 +422,13 @@ struct ab8500_chargalg_platform_data {
>> struct ab8500_btemp;
>> struct ab8500_gpadc;
>> struct ab8500_fg;
>> +
>> +extern struct abx500_bm_data ab8500_bm_data;
>> +extern struct abx500_battery_type bat_type_ext_thermistor[];
>> +extern struct batres_vs_temp temp_to_batres_tbl_ext_thermistor[];
>> +extern struct batres_vs_temp temp_to_batres_tbl_9100[];
>> +extern struct batres_vs_temp temp_to_batres_tbl_thermistor[];
>> +
>> #ifdef CONFIG_AB8500_BM
>> void ab8500_fg_reinit(void);
>> void ab8500_charger_usb_state_changed(u8 bm_usb_state, u16 mA);
>> --
>> 1.7.9.5
>>
>
> --
> Lee Jones
> Linaro ST-Ericsson Landing Team Lead
> Linaro.org ? Open source software for ARM SoCs
> Follow Linaro: Facebook | Twitter | Blog
>
^ permalink raw reply
* [PATCH 1/4] mfd: ab8500: add devicetree support for fuelgauge
From: Lee Jones @ 2012-10-01 10:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20121001094929.GB6682@gmail.com>
Sorry, some mistakes:
> > From: "Rajanikanth H.V" <rajanikanth.hv@stericsson.com>
> >
> > - This patch adds device tree support for fuelguage driver
> > - optimize bm devices platform_data usage and of_probe(...)
> > Note: of_probe() routine for battery managed devices is made
> > common across all bm drivers.
Spelling errors in here.
> > + dev_err(dev, "invalid battery-info node\n");
> > + return -EINVAL;
> > + }
> > + if (of_property_read_bool(np_bat_supply,
> > + "thermistor-on-batctrl") == false){
>
> Replace with:
> if (of_get_property(np_bat_supply, "thermistor-on-batctr", NULL))
> np_bat_supply = true;
This should be:
if (of_get_property(np_bat_supply, "thermistor-on-batctr", NULL))
thermistor = NTC_INTERNAL;
else
thermistor = NTC_EXTERNAL;
> <remove>
>
> > + dev_warn(dev, "missing property thermistor-on-batctrl\n");
> > + thermistor = NTC_EXTERNAL;
> > + }
>
> </remove>
--
Lee Jones
Linaro ST-Ericsson Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* [GIT PULL] ARM: mvebu: cache-l2x0 DT for v3.7
From: Gregory CLEMENT @ 2012-10-01 10:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20121001000644.GG17765@titan.lakedaemon.net>
On 10/01/2012 02:06 AM, Jason Cooper wrote:
> Russell,
>
> On Sun, Sep 30, 2012 at 09:01:36AM +0100, Russell King - ARM Linux wrote:
>> On Sat, Sep 29, 2012 at 08:55:03PM -0700, Olof Johansson wrote:
>>> On Sat, Sep 29, 2012 at 09:50:12PM -0400, Jason Cooper wrote:
>>>> Depends:
>>>> - Based on arm-soc/late/kirkwood
>>>> - Patches going through rmk's patch tracker:
>>>> - arm: cache-l2x0: make outer_cache_fns a field of l2x0_of_data
>>>> - arm: cache-l2x0: add an optional register to save/restore
>>>> - arm: cache-l2x0: add support for Aurora L2 cache ctrl
>>>>
>>>> - feature-use dependency only. No build breakage or failed boot
>>>> should occur. Until patches from rmk's tracker land, driver
>>>> won't know about compatible property, so it's like the driver
>>>> isn't there.
>>>>
>>>> Adds:
>>>> - cache-l2x0 DT entry for Marvell Armada (mvebu) SoCs
>>>
>>> Hi Jason,
>>>
>>> I'd prefer to hold off on this until we know that Russell is happy with
>>> the l2x0 changes, just in case this part of the series might need revising
>>> (i.e. bindings changes). Please resend when he has picked them up!
>>
>> Well, there's no patches ever existing in the patch tracker with a
>> summary line as you list above - here are the last three containing
>> "l2x0":
>>
>> 7398/1 l2x0: only write to debug 3.4-rc3 20 Apr Applied
>> registers on PL310 2012
>> 7429/1 cache-l2x0: add a const qualifier 3.4 22 Jun Discarded
>> 2012
>> 7507/1 cache-l2x0.c: save the final aux 3.6-rc4 03 Sep Applied
>> ctrl value for resuming 2012
>>
>> and it's probably now too late as I'm not going to be able to apply
>> any patches for the rest of the week, and as the -rc's have been
>> released over the weekend, we could see -final today.
>
> Yes, I responded to Gregory (the author) in the thread with the patches
> asking him to push them into your tracker. Unfortunately, he doesn't
> seem to be available. So, yes, this is v3.8.
They are now in the patch tracker:
7545/1 ache-l2x0: make outer_cache_fns a field of l2x0_of_data
7546/1 cache-l2x0: add an optional register to save/restore
7547/1 cache-l2x0: add support for Aurora L2 cache ctrl
>
> Once I see these first three patches make it in, I'll resubmit the
> pullrq.
>
> thx,
>
> Jason.
>
--
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
^ permalink raw reply
* [PATCH 1/4] mfd: ab8500: add devicetree support for fuelgauge
From: Lee Jones @ 2012-10-01 10:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <50696991.2080103@stericsson.com>
> did you have a look at arnd and anton comments regarding
> 'supplied-to' and boolean property
Try to keep your comments inline, situated below the relevant comment.
> >> + ab8500_battery_info: ab8500_bat_type {
> >> + battery-type = <2>;
> >> + thermistor-on-batctrl = <1>;
> > You have this as a bool here, and ...
> >> + e.g:
> >> + ab8500_battery_info: ab8500_bat_type {
> >> + thermistor-on-batctrl;
> > ... a standard property here. I suggest you drop the bool value.
I'm guessing it's just the documentation that needs amending.
--
Lee Jones
Linaro ST-Ericsson Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* [PATCH 0/7] Next instalment of Device Tree work for ST-Ericsson
From: Lee Jones @ 2012-10-01 10:43 UTC (permalink / raw)
To: linux-arm-kernel
Contained in this patch are some serious clean-ups for HREF related
Device Tree code. We make clear definition between pre-v60 and post-
v60 HREFs with the creation of their own DTS files. We also start to
take out most of the device registration calls which were duplicated
to aid with step-by-step DT enablement. There are also some small
fixes for the Snowball DTS which were uncovered during this
enablement phase.
arch/arm/boot/dts/dbx5x0.dtsi | 12 +-
arch/arm/boot/dts/href.dtsi | 274 ++++++++++++++++++++++++++++++++++++
arch/arm/boot/dts/hrefprev60.dts | 31 ++++
arch/arm/boot/dts/hrefv60plus.dts | 210 +--------------------------
arch/arm/boot/dts/snowball.dts | 6 +-
arch/arm/mach-ux500/board-mop500.c | 24 ----
6 files changed, 318 insertions(+), 239 deletions(-)
^ permalink raw reply
* [PATCH 1/7] ARM: ux500: Elaborate on SDI device node names in Device Tree
From: Lee Jones @ 2012-10-01 10:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1349088228-8582-1-git-send-email-lee.jones@linaro.org>
In the DB8500 Reference Manual SDI devices are described as being
part of peripheral blocks. This is more in line with how these devices
are actually represented in hardware, so here we detail which
peripheral block each of the SDI devices belong in the node name.
Requested-by: Linus Walleij <linus.walleij@linaro.org>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
arch/arm/boot/dts/dbx5x0.dtsi | 12 ++++++------
arch/arm/boot/dts/snowball.dts | 4 ++--
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/arch/arm/boot/dts/dbx5x0.dtsi b/arch/arm/boot/dts/dbx5x0.dtsi
index c8c39dd..07a085b 100644
--- a/arch/arm/boot/dts/dbx5x0.dtsi
+++ b/arch/arm/boot/dts/dbx5x0.dtsi
@@ -536,37 +536,37 @@
status = "disabled";
};
- sdi at 80126000 {
+ sdi0_per1 at 80126000 {
compatible = "arm,pl18x", "arm,primecell";
reg = <0x80126000 0x1000>;
interrupts = <0 60 0x4>;
status = "disabled";
};
- sdi at 80118000 {
+ sdi1_per2 at 80118000 {
compatible = "arm,pl18x", "arm,primecell";
reg = <0x80118000 0x1000>;
interrupts = <0 50 0x4>;
status = "disabled";
};
- sdi at 80005000 {
+ sdi2_per3 at 80005000 {
compatible = "arm,pl18x", "arm,primecell";
reg = <0x80005000 0x1000>;
interrupts = <0 41 0x4>;
status = "disabled";
};
- sdi at 80119000 {
+ sdi3_per2 at 80119000 {
compatible = "arm,pl18x", "arm,primecell";
reg = <0x80119000 0x1000>;
interrupts = <0 59 0x4>;
status = "disabled";
};
- sdi at 80114000 {
+ sdi4_per2 at 80114000 {
compatible = "arm,pl18x", "arm,primecell";
reg = <0x80114000 0x1000>;
interrupts = <0 99 0x4>;
status = "disabled";
};
- sdi at 80008000 {
+ sdi5_per3 at 80008000 {
compatible = "arm,pl18x", "arm,primecell";
reg = <0x80114000 0x1000>;
interrupts = <0 100 0x4>;
diff --git a/arch/arm/boot/dts/snowball.dts b/arch/arm/boot/dts/snowball.dts
index 639b8e9..9a37244 100644
--- a/arch/arm/boot/dts/snowball.dts
+++ b/arch/arm/boot/dts/snowball.dts
@@ -120,7 +120,7 @@
};
// External Micro SD slot
- sdi at 80126000 {
+ sdi0_per1 at 80126000 {
arm,primecell-periphid = <0x10480180>;
max-frequency = <50000000>;
bus-width = <8>;
@@ -134,7 +134,7 @@
};
// On-board eMMC
- sdi at 80114000 {
+ sdi4_per2 at 80114000 {
arm,primecell-periphid = <0x10480180>;
max-frequency = <50000000>;
bus-width = <8>;
--
1.7.9.5
^ permalink raw reply related
* [PATCH 2/7] ARM: ux500: Add SDI (MMC) support to the HREF Device Tree
From: Lee Jones @ 2012-10-01 10:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1349088228-8582-1-git-send-email-lee.jones@linaro.org>
Here we add the Device Tree nodes which are required to successfully
probe the MMCI driver which will enable the four cards available on
ST-Ericsson's HREF hardware development platform.
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
arch/arm/boot/dts/hrefv60plus.dts | 44 ++++++++++++++++++++++++++++++++++++
arch/arm/mach-ux500/board-mop500.c | 1 -
2 files changed, 44 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/hrefv60plus.dts b/arch/arm/boot/dts/hrefv60plus.dts
index e2213204..3fd83c6 100644
--- a/arch/arm/boot/dts/hrefv60plus.dts
+++ b/arch/arm/boot/dts/hrefv60plus.dts
@@ -77,6 +77,50 @@
};
};
+ // External Micro SD slot
+ sdi0_per1 at 80126000 {
+ arm,primecell-periphid = <0x10480180>;
+ max-frequency = <50000000>;
+ bus-width = <4>;
+ mmc-cap-sd-highspeed;
+ mmc-cap-mmc-highspeed;
+ vmmc-supply = <&ab8500_ldo_aux3_reg>;
+
+ cd-gpios = <&tc3589x_gpio 3 0x4>;
+
+ status = "okay";
+ };
+
+ // WLAN SDIO channel
+ sdi1_per2 at 80118000 {
+ arm,primecell-periphid = <0x10480180>;
+ max-frequency = <50000000>;
+ bus-width = <4>;
+
+ status = "okay";
+ };
+
+ // PoP:ed eMMC
+ sdi2_per3 at 80005000 {
+ arm,primecell-periphid = <0x10480180>;
+ max-frequency = <50000000>;
+ bus-width = <8>;
+ mmc-cap-mmc-highspeed;
+
+ status = "okay";
+ };
+
+ // On-board eMMC
+ sdi4_per2 at 80114000 {
+ arm,primecell-periphid = <0x10480180>;
+ max-frequency = <50000000>;
+ bus-width = <8>;
+ mmc-cap-mmc-highspeed;
+ vmmc-supply = <&ab8500_ldo_aux2_reg>;
+
+ status = "okay";
+ };
+
sound {
compatible = "stericsson,snd-soc-mop500";
diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c
index 4eedf1c..38e53b2 100644
--- a/arch/arm/mach-ux500/board-mop500.c
+++ b/arch/arm/mach-ux500/board-mop500.c
@@ -818,7 +818,6 @@ static void __init u8500_init_machine(void)
platform_add_devices(mop500_platform_devs,
ARRAY_SIZE(mop500_platform_devs));
- mop500_sdi_init(parent);
mop500_audio_init(parent);
i2c0_devs = ARRAY_SIZE(mop500_i2c0_devices);
i2c_register_board_info(0, mop500_i2c0_devices, i2c0_devs);
--
1.7.9.5
^ permalink raw reply related
* [PATCH 3/7] ARM: ux500: Correct Snowball's external SD/MMC slot's bus width
From: Lee Jones @ 2012-10-01 10:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1349088228-8582-1-git-send-email-lee.jones@linaro.org>
Snowball's external SD/MMC slot is only capable of 4 bit data
transfer, not 8 bits as previously thought. This is a simple
fixup moving to the correct settings.
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
arch/arm/boot/dts/snowball.dts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/snowball.dts b/arch/arm/boot/dts/snowball.dts
index 9a37244..6a2b58b 100644
--- a/arch/arm/boot/dts/snowball.dts
+++ b/arch/arm/boot/dts/snowball.dts
@@ -123,7 +123,7 @@
sdi0_per1 at 80126000 {
arm,primecell-periphid = <0x10480180>;
max-frequency = <50000000>;
- bus-width = <8>;
+ bus-width = <4>;
mmc-cap-mmc-highspeed;
vmmc-supply = <&ab8500_ldo_aux3_reg>;
--
1.7.9.5
^ permalink raw reply related
* [PATCH 4/7] ARM: ux500: Create a DTS hierarchy for ST-Ericsson's HREF boards
From: Lee Jones @ 2012-10-01 10:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1349088228-8582-1-git-send-email-lee.jones@linaro.org>
There are two main versions of the ST-Ericsson hardware reference board,
each with subtle differences. These versions are described in current
platform data as 'mop500' for HREF v1.1 & v2.0 and 'hrefv60+' for latter
incarnations. However, the boards have much in common, so this patch
creates a .dtsi (Device Tree Source Include) file which describes the
similarities between the two boards and individual 'hrefprev60' and
'hrefv60plus' .dts files which depict the differences.
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
arch/arm/boot/dts/href.dtsi | 262 +++++++++++++++++++++++++++++++++++++
arch/arm/boot/dts/hrefv60plus.dts | 256 +-----------------------------------
2 files changed, 264 insertions(+), 254 deletions(-)
create mode 100644 arch/arm/boot/dts/href.dtsi
diff --git a/arch/arm/boot/dts/href.dtsi b/arch/arm/boot/dts/href.dtsi
new file mode 100644
index 0000000..27baa44
--- /dev/null
+++ b/arch/arm/boot/dts/href.dtsi
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2012 ST-Ericsson AB
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/include/ "dbx5x0.dtsi"
+
+/ {
+ memory {
+ reg = <0x00000000 0x20000000>;
+ };
+
+ soc-u9500 {
+ uart at 80120000 {
+ status = "okay";
+ };
+
+ uart at 80121000 {
+ status = "okay";
+ };
+
+ uart at 80007000 {
+ status = "okay";
+ };
+
+ i2c at 80004000 {
+ tc3589x at 42 {
+ compatible = "tc3589x";
+ reg = <0x42>;
+ interrupt-parent = <&gpio6>;
+ interrupts = <25 0x1>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+
+ tc3589x_gpio: tc3589x_gpio {
+ compatible = "tc3589x-gpio";
+ interrupts = <0 0x1>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+ };
+ };
+
+ i2c at 80128000 {
+ lp5521 at 0x33 {
+ compatible = "lp5521";
+ reg = <0x33>;
+ };
+
+ lp5521 at 0x34 {
+ compatible = "lp5521";
+ reg = <0x34>;
+ };
+
+ bh1780 at 0x29 {
+ compatible = "rohm,bh1780gli";
+ reg = <0x33>;
+ };
+ };
+
+ // External Micro SD slot
+ sdi0_per1 at 80126000 {
+ arm,primecell-periphid = <0x10480180>;
+ max-frequency = <50000000>;
+ bus-width = <4>;
+ mmc-cap-sd-highspeed;
+ mmc-cap-mmc-highspeed;
+ vmmc-supply = <&ab8500_ldo_aux3_reg>;
+
+ cd-gpios = <&tc3589x_gpio 3 0x4>;
+
+ status = "okay";
+ };
+
+ // WLAN SDIO channel
+ sdi1_per2 at 80118000 {
+ arm,primecell-periphid = <0x10480180>;
+ max-frequency = <50000000>;
+ bus-width = <4>;
+
+ status = "okay";
+ };
+
+ // PoP:ed eMMC
+ sdi2_per3 at 80005000 {
+ arm,primecell-periphid = <0x10480180>;
+ max-frequency = <50000000>;
+ bus-width = <8>;
+ mmc-cap-mmc-highspeed;
+
+ status = "okay";
+ };
+
+ // On-board eMMC
+ sdi4_per2 at 80114000 {
+ arm,primecell-periphid = <0x10480180>;
+ max-frequency = <50000000>;
+ bus-width = <8>;
+ mmc-cap-mmc-highspeed;
+ vmmc-supply = <&ab8500_ldo_aux2_reg>;
+
+ status = "okay";
+ };
+
+ sound {
+ compatible = "stericsson,snd-soc-mop500";
+
+ stericsson,cpu-dai = <&msp1 &msp3>;
+ stericsson,audio-codec = <&codec>;
+ };
+
+ msp1: msp at 80124000 {
+ status = "okay";
+ };
+
+ msp3: msp at 80125000 {
+ status = "okay";
+ };
+
+ prcmu at 80157000 {
+ db8500-prcmu-regulators {
+ db8500_vape_reg: db8500_vape {
+ regulator-name = "db8500-vape";
+ };
+
+ db8500_varm_reg: db8500_varm {
+ regulator-name = "db8500-varm";
+ };
+
+ db8500_vmodem_reg: db8500_vmodem {
+ regulator-name = "db8500-vmodem";
+ };
+
+ db8500_vpll_reg: db8500_vpll {
+ regulator-name = "db8500-vpll";
+ };
+
+ db8500_vsmps1_reg: db8500_vsmps1 {
+ regulator-name = "db8500-vsmps1";
+ };
+
+ db8500_vsmps2_reg: db8500_vsmps2 {
+ regulator-name = "db8500-vsmps2";
+ };
+
+ db8500_vsmps3_reg: db8500_vsmps3 {
+ regulator-name = "db8500-vsmps3";
+ };
+
+ db8500_vrf1_reg: db8500_vrf1 {
+ regulator-name = "db8500-vrf1";
+ };
+
+ db8500_sva_mmdsp_reg: db8500_sva_mmdsp {
+ regulator-name = "db8500-sva-mmdsp";
+ };
+
+ db8500_sva_mmdsp_ret_reg: db8500_sva_mmdsp_ret {
+ regulator-name = "db8500-sva-mmdsp-ret";
+ };
+
+ db8500_sva_pipe_reg: db8500_sva_pipe {
+ regulator-name = "db8500_sva_pipe";
+ };
+
+ db8500_sia_mmdsp_reg: db8500_sia_mmdsp {
+ regulator-name = "db8500_sia_mmdsp";
+ };
+
+ db8500_sia_mmdsp_ret_reg: db8500_sia_mmdsp_ret {
+ regulator-name = "db8500-sia-mmdsp-ret";
+ };
+
+ db8500_sia_pipe_reg: db8500_sia_pipe {
+ regulator-name = "db8500-sia-pipe";
+ };
+
+ db8500_sga_reg: db8500_sga {
+ regulator-name = "db8500-sga";
+ };
+
+ db8500_b2r2_mcde_reg: db8500_b2r2_mcde {
+ regulator-name = "db8500-b2r2-mcde";
+ };
+
+ db8500_esram12_reg: db8500_esram12 {
+ regulator-name = "db8500-esram12";
+ };
+
+ db8500_esram12_ret_reg: db8500_esram12_ret {
+ regulator-name = "db8500-esram12-ret";
+ };
+
+ db8500_esram34_reg: db8500_esram34 {
+ regulator-name = "db8500-esram34";
+ };
+
+ db8500_esram34_ret_reg: db8500_esram34_ret {
+ regulator-name = "db8500-esram34-ret";
+ };
+ };
+
+ ab8500 at 5 {
+ ab8500-regulators {
+ ab8500_ldo_aux1_reg: ab8500_ldo_aux1 {
+ regulator-name = "V-DISPLAY";
+ };
+
+ ab8500_ldo_aux2_reg: ab8500_ldo_aux2 {
+ regulator-name = "V-eMMC1";
+ };
+
+ ab8500_ldo_aux3_reg: ab8500_ldo_aux3 {
+ regulator-name = "V-MMC-SD";
+ };
+
+ ab8500_ldo_initcore_reg: ab8500_ldo_initcore {
+ regulator-name = "V-INTCORE";
+ };
+
+ ab8500_ldo_tvout_reg: ab8500_ldo_tvout {
+ regulator-name = "V-TVOUT";
+ };
+
+ ab8500_ldo_usb_reg: ab8500_ldo_usb {
+ regulator-name = "dummy";
+ };
+
+ ab8500_ldo_audio_reg: ab8500_ldo_audio {
+ regulator-name = "V-AUD";
+ };
+
+ ab8500_ldo_anamic1_reg: ab8500_ldo_anamic1 {
+ regulator-name = "V-AMIC1";
+ };
+
+ ab8500_ldo_amamic2_reg: ab8500_ldo_amamic2 {
+ regulator-name = "V-AMIC2";
+ };
+
+ ab8500_ldo_dmic_reg: ab8500_ldo_dmic {
+ regulator-name = "V-DMIC";
+ };
+
+ ab8500_ldo_ana_reg: ab8500_ldo_ana {
+ regulator-name = "V-CSI/DSI";
+ };
+ };
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/hrefv60plus.dts b/arch/arm/boot/dts/hrefv60plus.dts
index 3fd83c6..2a85893 100644
--- a/arch/arm/boot/dts/hrefv60plus.dts
+++ b/arch/arm/boot/dts/hrefv60plus.dts
@@ -11,261 +11,9 @@
/dts-v1/;
/include/ "dbx5x0.dtsi"
+/include/ "href.dtsi"
/ {
- model = "ST-Ericsson HREF platform with Device Tree";
+ model = "ST-Ericsson HREF (v60+) platform with Device Tree";
compatible = "st-ericsson,hrefv60+";
-
- memory {
- reg = <0x00000000 0x20000000>;
- };
-
- soc-u9500 {
- uart at 80120000 {
- status = "okay";
- };
-
- uart at 80121000 {
- status = "okay";
- };
-
- uart at 80007000 {
- status = "okay";
- };
-
- i2c at 80004000 {
- tc3589x at 42 {
- compatible = "tc3589x";
- reg = <0x42>;
- interrupt-parent = <&gpio6>;
- interrupts = <25 0x1>;
-
- interrupt-controller;
- #interrupt-cells = <2>;
-
- tc3589x_gpio: tc3589x_gpio {
- compatible = "tc3589x-gpio";
- interrupts = <0 0x1>;
-
- interrupt-controller;
- #interrupt-cells = <2>;
- gpio-controller;
- #gpio-cells = <2>;
- };
- };
-
- tps61052 at 33 {
- compatible = "tps61052";
- reg = <0x33>;
- };
- };
-
- i2c at 80128000 {
- lp5521 at 0x33 {
- compatible = "lp5521";
- reg = <0x33>;
- };
-
- lp5521 at 0x34 {
- compatible = "lp5521";
- reg = <0x34>;
- };
-
- bh1780 at 0x29 {
- compatible = "rohm,bh1780gli";
- reg = <0x33>;
- };
- };
-
- // External Micro SD slot
- sdi0_per1 at 80126000 {
- arm,primecell-periphid = <0x10480180>;
- max-frequency = <50000000>;
- bus-width = <4>;
- mmc-cap-sd-highspeed;
- mmc-cap-mmc-highspeed;
- vmmc-supply = <&ab8500_ldo_aux3_reg>;
-
- cd-gpios = <&tc3589x_gpio 3 0x4>;
-
- status = "okay";
- };
-
- // WLAN SDIO channel
- sdi1_per2 at 80118000 {
- arm,primecell-periphid = <0x10480180>;
- max-frequency = <50000000>;
- bus-width = <4>;
-
- status = "okay";
- };
-
- // PoP:ed eMMC
- sdi2_per3 at 80005000 {
- arm,primecell-periphid = <0x10480180>;
- max-frequency = <50000000>;
- bus-width = <8>;
- mmc-cap-mmc-highspeed;
-
- status = "okay";
- };
-
- // On-board eMMC
- sdi4_per2 at 80114000 {
- arm,primecell-periphid = <0x10480180>;
- max-frequency = <50000000>;
- bus-width = <8>;
- mmc-cap-mmc-highspeed;
- vmmc-supply = <&ab8500_ldo_aux2_reg>;
-
- status = "okay";
- };
-
- sound {
- compatible = "stericsson,snd-soc-mop500";
-
- stericsson,cpu-dai = <&msp1 &msp3>;
- stericsson,audio-codec = <&codec>;
- };
-
- msp1: msp at 80124000 {
- status = "okay";
- };
-
- msp3: msp at 80125000 {
- status = "okay";
- };
-
- prcmu at 80157000 {
- db8500-prcmu-regulators {
- db8500_vape_reg: db8500_vape {
- regulator-name = "db8500-vape";
- };
-
- db8500_varm_reg: db8500_varm {
- regulator-name = "db8500-varm";
- };
-
- db8500_vmodem_reg: db8500_vmodem {
- regulator-name = "db8500-vmodem";
- };
-
- db8500_vpll_reg: db8500_vpll {
- regulator-name = "db8500-vpll";
- };
-
- db8500_vsmps1_reg: db8500_vsmps1 {
- regulator-name = "db8500-vsmps1";
- };
-
- db8500_vsmps2_reg: db8500_vsmps2 {
- regulator-name = "db8500-vsmps2";
- };
-
- db8500_vsmps3_reg: db8500_vsmps3 {
- regulator-name = "db8500-vsmps3";
- };
-
- db8500_vrf1_reg: db8500_vrf1 {
- regulator-name = "db8500-vrf1";
- };
-
- db8500_sva_mmdsp_reg: db8500_sva_mmdsp {
- regulator-name = "db8500-sva-mmdsp";
- };
-
- db8500_sva_mmdsp_ret_reg: db8500_sva_mmdsp_ret {
- regulator-name = "db8500-sva-mmdsp-ret";
- };
-
- db8500_sva_pipe_reg: db8500_sva_pipe {
- regulator-name = "db8500_sva_pipe";
- };
-
- db8500_sia_mmdsp_reg: db8500_sia_mmdsp {
- regulator-name = "db8500_sia_mmdsp";
- };
-
- db8500_sia_mmdsp_ret_reg: db8500_sia_mmdsp_ret {
- regulator-name = "db8500-sia-mmdsp-ret";
- };
-
- db8500_sia_pipe_reg: db8500_sia_pipe {
- regulator-name = "db8500-sia-pipe";
- };
-
- db8500_sga_reg: db8500_sga {
- regulator-name = "db8500-sga";
- };
-
- db8500_b2r2_mcde_reg: db8500_b2r2_mcde {
- regulator-name = "db8500-b2r2-mcde";
- };
-
- db8500_esram12_reg: db8500_esram12 {
- regulator-name = "db8500-esram12";
- };
-
- db8500_esram12_ret_reg: db8500_esram12_ret {
- regulator-name = "db8500-esram12-ret";
- };
-
- db8500_esram34_reg: db8500_esram34 {
- regulator-name = "db8500-esram34";
- };
-
- db8500_esram34_ret_reg: db8500_esram34_ret {
- regulator-name = "db8500-esram34-ret";
- };
- };
-
- ab8500 at 5 {
- ab8500-regulators {
- ab8500_ldo_aux1_reg: ab8500_ldo_aux1 {
- regulator-name = "V-DISPLAY";
- };
-
- ab8500_ldo_aux2_reg: ab8500_ldo_aux2 {
- regulator-name = "V-eMMC1";
- };
-
- ab8500_ldo_aux3_reg: ab8500_ldo_aux3 {
- regulator-name = "V-MMC-SD";
- };
-
- ab8500_ldo_initcore_reg: ab8500_ldo_initcore {
- regulator-name = "V-INTCORE";
- };
-
- ab8500_ldo_tvout_reg: ab8500_ldo_tvout {
- regulator-name = "V-TVOUT";
- };
-
- ab8500_ldo_usb_reg: ab8500_ldo_usb {
- regulator-name = "dummy";
- };
-
- ab8500_ldo_audio_reg: ab8500_ldo_audio {
- regulator-name = "V-AUD";
- };
-
- ab8500_ldo_anamic1_reg: ab8500_ldo_anamic1 {
- regulator-name = "V-AMIC1";
- };
-
- ab8500_ldo_amamic2_reg: ab8500_ldo_amamic2 {
- regulator-name = "V-AMIC2";
- };
-
- ab8500_ldo_dmic_reg: ab8500_ldo_dmic {
- regulator-name = "V-DMIC";
- };
-
- ab8500_ldo_ana_reg: ab8500_ldo_ana {
- regulator-name = "V-CSI/DSI";
- };
- };
- };
- };
- };
};
--
1.7.9.5
^ permalink raw reply related
* [PATCH 5/7] ARM: ux500: Create a Device Tree for early HREFs
From: Lee Jones @ 2012-10-01 10:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1349088228-8582-1-git-send-email-lee.jones@linaro.org>
In ux500 platform code we currently support a number of devices.
Two of these devices are fairly similar, but have key differences.
Early (pre-v60) HREFs and the more up-to-date versions (v60+), and
they both need to be supported by Device Tree. Here we apply a DT
source file for the earlier versions.
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
arch/arm/boot/dts/hrefprev60.dts | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
create mode 100644 arch/arm/boot/dts/hrefprev60.dts
diff --git a/arch/arm/boot/dts/hrefprev60.dts b/arch/arm/boot/dts/hrefprev60.dts
new file mode 100644
index 0000000..2dd28b9
--- /dev/null
+++ b/arch/arm/boot/dts/hrefprev60.dts
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2012 ST-Ericsson AB
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+/include/ "dbx5x0.dtsi"
+/include/ "href.dtsi"
+
+/ {
+ model = "ST-Ericsson HREF (pre-v60) platform with Device Tree";
+ compatible = "st-ericsson,mop500";
+
+ soc-u9500 {
+ i2c at 80004000 {
+ tps61052 at 33 {
+ compatible = "tps61052";
+ reg = <0x33>;
+ };
+ };
+ };
+};
--
1.7.9.5
^ permalink raw reply related
* [PATCH 6/7] ARM: ux500: Add support for gpio-keys and Proximity Sensor for HREF
From: Lee Jones @ 2012-10-01 10:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1349088228-8582-1-git-send-email-lee.jones@linaro.org>
Here we ensure the SFH7741 Proximity Sensor is registered through
gpio-keys when booting with Device Tree enabled.
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
arch/arm/boot/dts/href.dtsi | 11 +++++++++++
arch/arm/boot/dts/hrefprev60.dts | 6 ++++++
arch/arm/boot/dts/hrefv60plus.dts | 6 ++++++
3 files changed, 23 insertions(+)
diff --git a/arch/arm/boot/dts/href.dtsi b/arch/arm/boot/dts/href.dtsi
index 27baa44..592fb9d 100644
--- a/arch/arm/boot/dts/href.dtsi
+++ b/arch/arm/boot/dts/href.dtsi
@@ -16,6 +16,17 @@
reg = <0x00000000 0x20000000>;
};
+ gpio_keys {
+ compatible = "gpio-keys";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ button at 1 {
+ linux,code = <11>;
+ label = "SFH7741 Proximity Sensor";
+ };
+ };
+
soc-u9500 {
uart at 80120000 {
status = "okay";
diff --git a/arch/arm/boot/dts/hrefprev60.dts b/arch/arm/boot/dts/hrefprev60.dts
index 2dd28b9..0756f97 100644
--- a/arch/arm/boot/dts/hrefprev60.dts
+++ b/arch/arm/boot/dts/hrefprev60.dts
@@ -17,6 +17,12 @@
model = "ST-Ericsson HREF (pre-v60) platform with Device Tree";
compatible = "st-ericsson,mop500";
+ gpio_keys {
+ button at 1 {
+ gpios = <&tc3589x_gpio 7 0x4>;
+ };
+ };
+
soc-u9500 {
i2c at 80004000 {
tps61052 at 33 {
diff --git a/arch/arm/boot/dts/hrefv60plus.dts b/arch/arm/boot/dts/hrefv60plus.dts
index 2a85893..4b867b2 100644
--- a/arch/arm/boot/dts/hrefv60plus.dts
+++ b/arch/arm/boot/dts/hrefv60plus.dts
@@ -16,4 +16,10 @@
/ {
model = "ST-Ericsson HREF (v60+) platform with Device Tree";
compatible = "st-ericsson,hrefv60+";
+
+ gpio_keys {
+ button at 1 {
+ gpios = <&gpio6 25 0x4>;
+ };
+ };
};
--
1.7.9.5
^ permalink raw reply related
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