Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/7] usb: musb: add driver for control module
From: Arnd Bergmann @ 2013-01-15 13:41 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358239378-10030-1-git-send-email-kishon@ti.com>

On Tuesday 15 January 2013, Kishon Vijay Abraham I wrote:
> Added a new driver for the usb part of control module. This has an API
> to power on the USB2 phy and an API to write to the mailbox depending on
> whether MUSB has to act in host mode or in device mode.
> 
> Writing to control module registers for doing the above task which was
> previously done in omap glue and in omap-usb2 phy is removed.
> 
> Also added the dt data to get MUSB working in OMAP platforms.
> This series has patches for both drivers and ARCH folders, so If it has to
> be split I'll do it.
> 

The series looks good to me, I just had a minor comment on one patch.

One a somewhat related topic, I wonder whether there are any plans
on your side to change this driver to support multiple bus glues
to be built for one kernel image. With a multiplatform kernel, we
may need all of TUSB6010/OMAP2PLUS/DSPS/UX500 for instance.

	Arnd

^ permalink raw reply

* [RFC PATCH 1/7] drivers: usb: phy: add a new driver for usb part of control module
From: Arnd Bergmann @ 2013-01-15 13:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358239378-10030-2-git-send-email-kishon@ti.com>

On Tuesday 15 January 2013, Kishon Vijay Abraham I wrote:
> +OMAP CONTROL USB
> +
> +Required properties:
> + - compatible: Should be "ti,omap-control-usb"
> + - reg : Address and length of the register set for the device. It contains
> +   the address of "control_dev_conf" and "otghs_control".
> + - reg-names: The names of the register addresses corresponding to the registers
> +   filled in "reg".
> + - ti,has_mailbox: This is used to specify if the platform uses mailbox in
> +   control module.

I wonder whether we need to have a phandle here to connect the control device
to the actual usb device. What happens if you have multiple instances of
each?

	Arnd

^ permalink raw reply

* [PATCH] ARM: AM33XX: clock: SET_RATE_PARENT in lcd path
From: Afzal Mohammed @ 2013-01-15 13:34 UTC (permalink / raw)
  To: linux-arm-kernel

LCDC clock node is a one that does not have set rate capability. It
just passes on the rate that is sent downstream by it's parent. While
lcdc clock parent and it's grand parent - dpll_disp_m2_ck and
dpll_disp_ck has the capability to configure rate.

And the default rates provided by LCDC clock's ancestors are not
sufficient to obtain pixel clock for current LCDC use cases, hence
currently display would not work on AM335x SoC's (with driver
modifications in platfrom independent way).

Hence inform clock framework to propogate set rate for LCDC clock as
well as it's parent - dpll_disp_m2_ck. With this change, set rate on
LCDC clock would get propogated till dpll_disp_ck via dpll_disp_m2_ck,
hence allowing the driver (same driver is used in DaVinci too) to set
rates using LCDC clock without worrying about platform dependent clock
details.

Signed-off-by: Afzal Mohammed <afzal@ti.com>
---

Based on v3.8-rc3

 arch/arm/mach-omap2/cclock33xx_data.c |    9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/arch/arm/mach-omap2/cclock33xx_data.c b/arch/arm/mach-omap2/cclock33xx_data.c
index ea64ad6..b731216 100644
--- a/arch/arm/mach-omap2/cclock33xx_data.c
+++ b/arch/arm/mach-omap2/cclock33xx_data.c
@@ -284,9 +284,10 @@ DEFINE_STRUCT_CLK(dpll_disp_ck, dpll_core_ck_parents, dpll_ddr_ck_ops);
  * TODO: Add clksel here (sys_clkin, CORE_CLKOUTM6, PER_CLKOUTM2
  * and ALT_CLK1/2)
  */
-DEFINE_CLK_DIVIDER(dpll_disp_m2_ck, "dpll_disp_ck", &dpll_disp_ck, 0x0,
-		   AM33XX_CM_DIV_M2_DPLL_DISP, AM33XX_DPLL_CLKOUT_DIV_SHIFT,
-		   AM33XX_DPLL_CLKOUT_DIV_WIDTH, CLK_DIVIDER_ONE_BASED, NULL);
+DEFINE_CLK_DIVIDER(dpll_disp_m2_ck, "dpll_disp_ck", &dpll_disp_ck,
+		   CLK_SET_RATE_PARENT, AM33XX_CM_DIV_M2_DPLL_DISP,
+		   AM33XX_DPLL_CLKOUT_DIV_SHIFT, AM33XX_DPLL_CLKOUT_DIV_WIDTH,
+		   CLK_DIVIDER_ONE_BASED, NULL);
 
 /* DPLL_PER */
 static struct dpll_data dpll_per_dd = {
@@ -932,6 +933,8 @@ int __init am33xx_clk_init(void)
 		cpu_clkflg = CK_AM33XX;
 	}
 
+	lcd_gclk.flags |= CLK_SET_RATE_PARENT;
+
 	for (c = am33xx_clks; c < am33xx_clks + ARRAY_SIZE(am33xx_clks); c++) {
 		if (c->cpu & cpu_clkflg) {
 			clkdev_add(&c->lk);
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH v5 13/14] KVM: ARM: Handle I/O aborts
From: Gleb Natapov @ 2013-01-15 13:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <50F559C4.8080900@arm.com>

On Tue, Jan 15, 2013 at 01:29:40PM +0000, Marc Zyngier wrote:
> On 15/01/13 13:18, Gleb Natapov wrote:
> > On Tue, Jan 08, 2013 at 01:40:05PM -0500, Christoffer Dall wrote:
> >> When the guest accesses I/O memory this will create data abort
> >> exceptions and they are handled by decoding the HSR information
> >> (physical address, read/write, length, register) and forwarding reads
> >> and writes to QEMU which performs the device emulation.
> >>
> >> Certain classes of load/store operations do not support the syndrome
> >> information provided in the HSR and we therefore must be able to fetch
> >> the offending instruction from guest memory and decode it manually.
> >>
> >> We only support instruction decoding for valid reasonable MMIO operations
> >> where trapping them do not provide sufficient information in the HSR (no
> >> 16-bit Thumb instructions provide register writeback that we care about).
> >>
> >> The following instruction types are NOT supported for MMIO operations
> >> despite the HSR not containing decode info:
> >>  - any Load/Store multiple
> >>  - any load/store exclusive
> >>  - any load/store dual
> >>  - anything with the PC as the dest register
> >>
> >> This requires changing the general flow somewhat since new calls to run
> >> the VCPU must check if there's a pending MMIO load and perform the write
> >> after userspace has made the data available.
> >>
> >> Rusty Russell fixed a horrible race pointed out by Ben Herrenschmidt:
> >> (1) Guest complicated mmio instruction traps.
> >> (2) The hardware doesn't tell us enough, so we need to read the actual
> >>     instruction which was being exectuted.
> >> (3) KVM maps the instruction virtual address to a physical address.
> >> (4) The guest (SMP) swaps out that page, and fills it with something else.
> >> (5) We read the physical address, but now that's the wrong thing.
> > How can this happen?! The guest cannot reuse physical page before it
> > flushes it from all vcpus tlb cache. For that it needs to send
> > synchronous IPI to all vcpus and IPI will not be processed by a vcpu
> > while it does emulation.
> 
> I don't know how this works on x86, but a KVM/ARM guest can definitely
> handle an IPI.
> 
How can a vcpu handle an IPI while it is not in a guest mode?

> Furthermore, TLB invalidation doesn't require an IPI on ARMv7 (unless
> we're doing some set/way operation which is handled separately).
> 
What prevents a page to be swapped out while code is fetched from it?
 
--
			Gleb.

^ permalink raw reply

* [PATCH v5 03/14] KVM: ARM: Initial skeleton to compile KVM support
From: Gleb Natapov @ 2013-01-15 13:32 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CANM98qJoOxG_g7Dz+65vfGmzi3MOXqE=AjbVBtimeqLL9TgV8w@mail.gmail.com>

On Mon, Jan 14, 2013 at 05:17:31PM -0500, Christoffer Dall wrote:
> On Mon, Jan 14, 2013 at 1:49 PM, Gleb Natapov <gleb@redhat.com> wrote:
> > A couple of general question about ABI. If they were already answered
> > just refer me to the previous discussion.
> >
> > On Tue, Jan 08, 2013 at 01:38:55PM -0500, Christoffer Dall wrote:
> >> diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
> >> index a4df553..4237c27 100644
> >> --- a/Documentation/virtual/kvm/api.txt
> >> +++ b/Documentation/virtual/kvm/api.txt
> >> @@ -293,7 +293,7 @@ kvm_run' (see below).
> >>  4.11 KVM_GET_REGS
> >>
> >>  Capability: basic
> >> -Architectures: all
> >> +Architectures: all except ARM
> >>  Type: vcpu ioctl
> >>  Parameters: struct kvm_regs (out)
> >>  Returns: 0 on success, -1 on error
> >> @@ -314,7 +314,7 @@ struct kvm_regs {
> >>  4.12 KVM_SET_REGS
> >>
> >>  Capability: basic
> >> -Architectures: all
> >> +Architectures: all except ARM
> >>  Type: vcpu ioctl
> >>  Parameters: struct kvm_regs (in)
> >>  Returns: 0 on success, -1 on error
> >> @@ -600,7 +600,7 @@ struct kvm_fpu {
> >>  4.24 KVM_CREATE_IRQCHIP
> > Why KVM_GET_REGS/KVM_SET_REGS are not usable for arm?
> >
> 
> We use the ONE_REG API instead and we don't want to support two
> separate APIs to user space.
> 
I suppose fetching all registers is not anywhere on a fast path in
userspace :)

> >>
> >>  Capability: KVM_CAP_IRQCHIP
> >> -Architectures: x86, ia64
> >> +Architectures: x86, ia64, ARM
> >>  Type: vm ioctl
> >>  Parameters: none
> >>  Returns: 0 on success, -1 on error
> >> @@ -608,7 +608,8 @@ Returns: 0 on success, -1 on error
> >>  Creates an interrupt controller model in the kernel.  On x86, creates a virtual
> >>  ioapic, a virtual PIC (two PICs, nested), and sets up future vcpus to have a
> >>  local APIC.  IRQ routing for GSIs 0-15 is set to both PIC and IOAPIC; GSI 16-23
> >> -only go to the IOAPIC.  On ia64, a IOSAPIC is created.
> >> +only go to the IOAPIC.  On ia64, a IOSAPIC is created. On ARM, a GIC is
> >> +created.
> >>
> >>
> >>  4.25 KVM_IRQ_LINE
> >> @@ -1775,6 +1776,14 @@ registers, find a list below:
> >>    PPC   | KVM_REG_PPC_VPA_DTL   | 128
> >>    PPC   | KVM_REG_PPC_EPCR   | 32
> >>
> >> +ARM registers are mapped using the lower 32 bits.  The upper 16 of that
> >> +is the register group type, or coprocessor number:
> >> +
> >> +ARM core registers have the following id bit patterns:
> >> +  0x4002 0000 0010 <index into the kvm_regs struct:16>
> >> +
> >> +
> >> +
> >>  4.69 KVM_GET_ONE_REG
> >>
> >>  Capability: KVM_CAP_ONE_REG
> >> @@ -2127,6 +2136,46 @@ written, then `n_invalid' invalid entries, invalidating any previously
> >>  valid entries found.
> >>
> >>
> >> +4.77 KVM_ARM_VCPU_INIT
> >> +
> >> +Capability: basic
> >> +Architectures: arm
> >> +Type: vcpu ioctl
> >> +Parameters: struct struct kvm_vcpu_init (in)
> >> +Returns: 0 on success; -1 on error
> >> +Errors:
> >> +  EINVAL:    the target is unknown, or the combination of features is invalid.
> >> +  ENOENT:    a features bit specified is unknown.
> >> +
> >> +This tells KVM what type of CPU to present to the guest, and what
> >> +optional features it should have.  This will cause a reset of the cpu
> >> +registers to their initial values.  If this is not called, KVM_RUN will
> >> +return ENOEXEC for that vcpu.
> >> +
> > Can different vcpus of the same VM be of different type?
> >
> 
> In the future yes. For example, if we ever want to virtualize a
> Big.Little system.
> 
> >> +Note that because some registers reflect machine topology, all vcpus
> >> +should be created before this ioctl is invoked.
> > How cpu hot plug suppose to work?
> >
> 
> Those CPUs would be added from the beginning, but not powered on, and
> would be powered on later on, I suppose.  See
> https://lists.cs.columbia.edu/pipermail/kvmarm/2013-January/004617.html.
> 
When we suggested similar "hot plug" for x86, people started screaming
how they suppose to know when they create a VM how much vcpus they will
need in the future. In short people who are asking for hot plug (on x86
at least) do not like such solution.

> 
> >> +
> >> +
> >> +4.78 KVM_GET_REG_LIST
> >> +
> >> +Capability: basic
> >> +Architectures: arm
> >> +Type: vcpu ioctl
> >> +Parameters: struct kvm_reg_list (in/out)
> >> +Returns: 0 on success; -1 on error
> >> +Errors:
> >> +  E2BIG:     the reg index list is too big to fit in the array specified by
> >> +             the user (the number required will be written into n).
> >> +
> >> +struct kvm_reg_list {
> >> +     __u64 n; /* number of registers in reg[] */
> >> +     __u64 reg[0];
> >> +};
> >> +
> >> +This ioctl returns the guest registers that are supported for the
> >> +KVM_GET_ONE_REG/KVM_SET_ONE_REG calls.
> >> +
> >> +
> > Doesn't userspace know what registers are supported by each CPU type?
> >
> It would know about core registers, but there is a huge space of
> co-processors, and we don't emulate all of them or support
> getting/setting all of them yet. Surely this is something that will
> change over time and we want user space to be able to discover the
> available registers for backwards compatibility, migration, etc.
> 
> -Christoffer

--
			Gleb.

^ permalink raw reply

* [PATCH RESEND 6/6 v13] gpio: Add block gpio to several gpio drivers
From: Roland Stigge @ 2013-01-15 13:30 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <50F5573D.4060007@atmel.com>

On 01/15/2013 02:18 PM, Nicolas Ferre wrote:
>> --- linux-2.6.orig/drivers/pinctrl/pinctrl-at91.c
>> +++ linux-2.6/drivers/pinctrl/pinctrl-at91.c
>> @@ -49,6 +49,7 @@ struct at91_gpio_chip {
>>       struct clk        *clock;        /* associated clock */
>>       struct irq_domain    *domain;    /* associated irq domain */
>>       struct at91_pinctrl_mux_ops *ops;    /* ops */
>> +    unsigned long        mask_cache;    /* cached mask for block gpio */
>>   };
>>
>>   #define to_at91_gpio_chip(c) container_of(c, struct at91_gpio_chip,
>> chip)
>> @@ -1125,6 +1126,32 @@ static void at91_gpio_set(struct gpio_ch
>>       writel_relaxed(mask, pio + (val ? PIO_SODR : PIO_CODR));
>>   }
>>
>> +static unsigned long at91_gpio_get_block(struct gpio_chip *chip,
>> +                     unsigned long mask)
>> +{
>> +    struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
>> +    void __iomem *pio = at91_gpio->regbase;
>> +    u32 pdsr;
>> +
>> +    pdsr = __raw_readl(pio + PIO_PDSR);
> 
> Maybe you should use readl_relaxed() here as it is used in the
> at91_gpio_[get|set]() functions.

Thanks for the note! Seem to have missed this when forward porting in
pinctrl-at91.c was due.

Will include the respective change in a subsequent update.

Roland

^ permalink raw reply

* [PATCH v5 13/14] KVM: ARM: Handle I/O aborts
From: Marc Zyngier @ 2013-01-15 13:29 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20130115131855.GL11529@redhat.com>

On 15/01/13 13:18, Gleb Natapov wrote:
> On Tue, Jan 08, 2013 at 01:40:05PM -0500, Christoffer Dall wrote:
>> When the guest accesses I/O memory this will create data abort
>> exceptions and they are handled by decoding the HSR information
>> (physical address, read/write, length, register) and forwarding reads
>> and writes to QEMU which performs the device emulation.
>>
>> Certain classes of load/store operations do not support the syndrome
>> information provided in the HSR and we therefore must be able to fetch
>> the offending instruction from guest memory and decode it manually.
>>
>> We only support instruction decoding for valid reasonable MMIO operations
>> where trapping them do not provide sufficient information in the HSR (no
>> 16-bit Thumb instructions provide register writeback that we care about).
>>
>> The following instruction types are NOT supported for MMIO operations
>> despite the HSR not containing decode info:
>>  - any Load/Store multiple
>>  - any load/store exclusive
>>  - any load/store dual
>>  - anything with the PC as the dest register
>>
>> This requires changing the general flow somewhat since new calls to run
>> the VCPU must check if there's a pending MMIO load and perform the write
>> after userspace has made the data available.
>>
>> Rusty Russell fixed a horrible race pointed out by Ben Herrenschmidt:
>> (1) Guest complicated mmio instruction traps.
>> (2) The hardware doesn't tell us enough, so we need to read the actual
>>     instruction which was being exectuted.
>> (3) KVM maps the instruction virtual address to a physical address.
>> (4) The guest (SMP) swaps out that page, and fills it with something else.
>> (5) We read the physical address, but now that's the wrong thing.
> How can this happen?! The guest cannot reuse physical page before it
> flushes it from all vcpus tlb cache. For that it needs to send
> synchronous IPI to all vcpus and IPI will not be processed by a vcpu
> while it does emulation.

I don't know how this works on x86, but a KVM/ARM guest can definitely
handle an IPI.

Furthermore, TLB invalidation doesn't require an IPI on ARMv7 (unless
we're doing some set/way operation which is handled separately).

	M.
-- 
Jazz is not dead. It just smells funny...

^ permalink raw reply

* [PATCH 04/18] power: ab8500_fg: Replace msleep() with usleep_range() for greater accuracy
From: Arnd Bergmann @ 2013-01-15 13:23 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20130115084821.GT12385@gmail.com>

On Tuesday 15 January 2013, Lee Jones wrote:
> > > @@ -956,7 +956,7 @@ static int ab8500_fg_load_comp_volt_to_capacity(struct ab8500_fg *di)
> > >     do {
> > >             vbat += ab8500_fg_bat_voltage(di);
> > >             i++;
> > > -           msleep(5);
> > > +           usleep_range(5000, 5001);
> > 
> > If you're going to give a range that small
> > you might as well use usleep instead.
> > 
> > Otherwise, add some tolerance to allow any
> > other coalesced wakeup to occur.
> 
> I can't increase the tolerance, as I don't know how that would
> effect the running of the system, and the person who would know
> is off on parental leave.

The function only averages the voltage between a couple of readings.
It won't change much if those register reads are slightly more
uniformly timed. Note that the thread can still be preempted for
a much longer time if anything else is running, and the entire
interrupt handling in this driver looks so fragile that I would
not rely on the interrupt actually happening at the right time
anyway. I think it should first be debugged properly to remove
the need for the enable_irq/disable_irq calls.

	Arnd

^ permalink raw reply

* [PATCH v5 13/14] KVM: ARM: Handle I/O aborts
From: Gleb Natapov @ 2013-01-15 13:18 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20130108184005.46302.38495.stgit@ubuntu>

On Tue, Jan 08, 2013 at 01:40:05PM -0500, Christoffer Dall wrote:
> When the guest accesses I/O memory this will create data abort
> exceptions and they are handled by decoding the HSR information
> (physical address, read/write, length, register) and forwarding reads
> and writes to QEMU which performs the device emulation.
> 
> Certain classes of load/store operations do not support the syndrome
> information provided in the HSR and we therefore must be able to fetch
> the offending instruction from guest memory and decode it manually.
> 
> We only support instruction decoding for valid reasonable MMIO operations
> where trapping them do not provide sufficient information in the HSR (no
> 16-bit Thumb instructions provide register writeback that we care about).
> 
> The following instruction types are NOT supported for MMIO operations
> despite the HSR not containing decode info:
>  - any Load/Store multiple
>  - any load/store exclusive
>  - any load/store dual
>  - anything with the PC as the dest register
> 
> This requires changing the general flow somewhat since new calls to run
> the VCPU must check if there's a pending MMIO load and perform the write
> after userspace has made the data available.
> 
> Rusty Russell fixed a horrible race pointed out by Ben Herrenschmidt:
> (1) Guest complicated mmio instruction traps.
> (2) The hardware doesn't tell us enough, so we need to read the actual
>     instruction which was being exectuted.
> (3) KVM maps the instruction virtual address to a physical address.
> (4) The guest (SMP) swaps out that page, and fills it with something else.
> (5) We read the physical address, but now that's the wrong thing.
How can this happen?! The guest cannot reuse physical page before it
flushes it from all vcpus tlb cache. For that it needs to send
synchronous IPI to all vcpus and IPI will not be processed by a vcpu
while it does emulation.

> 
> Reviewed-by: Marcelo Tosatti <mtosatti@redhat.com>
> Signed-off-by: Rusty Russell <rusty.russell@linaro.org>
> 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     |    3 
>  arch/arm/include/asm/kvm_asm.h     |    2 
>  arch/arm/include/asm/kvm_decode.h  |   47 ++++
>  arch/arm/include/asm/kvm_emulate.h |    8 +
>  arch/arm/include/asm/kvm_host.h    |    7 +
>  arch/arm/include/asm/kvm_mmio.h    |   51 ++++
>  arch/arm/kvm/Makefile              |    2 
>  arch/arm/kvm/arm.c                 |   14 +
>  arch/arm/kvm/decode.c              |  462 ++++++++++++++++++++++++++++++++++++
>  arch/arm/kvm/emulate.c             |  169 +++++++++++++
>  arch/arm/kvm/interrupts.S          |   38 +++
>  arch/arm/kvm/mmio.c                |  154 ++++++++++++
>  arch/arm/kvm/mmu.c                 |    7 -
>  arch/arm/kvm/trace.h               |   21 ++
>  14 files changed, 981 insertions(+), 4 deletions(-)
>  create mode 100644 arch/arm/include/asm/kvm_decode.h
>  create mode 100644 arch/arm/include/asm/kvm_mmio.h
>  create mode 100644 arch/arm/kvm/decode.c
>  create mode 100644 arch/arm/kvm/mmio.c
> 
> diff --git a/arch/arm/include/asm/kvm_arm.h b/arch/arm/include/asm/kvm_arm.h
> index 3ff6f22..151c4ce 100644
> --- a/arch/arm/include/asm/kvm_arm.h
> +++ b/arch/arm/include/asm/kvm_arm.h
> @@ -173,8 +173,11 @@
>  #define HSR_ISS		(HSR_IL - 1)
>  #define HSR_ISV_SHIFT	(24)
>  #define HSR_ISV		(1U << HSR_ISV_SHIFT)
> +#define HSR_SRT_SHIFT	(16)
> +#define HSR_SRT_MASK	(0xf << HSR_SRT_SHIFT)
>  #define HSR_FSC		(0x3f)
>  #define HSR_FSC_TYPE	(0x3c)
> +#define HSR_SSE		(1 << 21)
>  #define HSR_WNR		(1 << 6)
>  #define HSR_CV_SHIFT	(24)
>  #define HSR_CV		(1U << HSR_CV_SHIFT)
> diff --git a/arch/arm/include/asm/kvm_asm.h b/arch/arm/include/asm/kvm_asm.h
> index 5e06e81..58d787b 100644
> --- a/arch/arm/include/asm/kvm_asm.h
> +++ b/arch/arm/include/asm/kvm_asm.h
> @@ -77,6 +77,8 @@ extern void __kvm_flush_vm_context(void);
>  extern void __kvm_tlb_flush_vmid(struct kvm *kvm);
>  
>  extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu);
> +
> +extern u64 __kvm_va_to_pa(struct kvm_vcpu *vcpu, u32 va, bool priv);
>  #endif
>  
>  #endif /* __ARM_KVM_ASM_H__ */
> diff --git a/arch/arm/include/asm/kvm_decode.h b/arch/arm/include/asm/kvm_decode.h
> new file mode 100644
> index 0000000..3c37cb9
> --- /dev/null
> +++ b/arch/arm/include/asm/kvm_decode.h
> @@ -0,0 +1,47 @@
> +/*
> + * Copyright (C) 2012 - Virtual Open Systems and Columbia University
> + * Author: Christoffer Dall <c.dall@virtualopensystems.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, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
> + */
> +
> +#ifndef __ARM_KVM_DECODE_H__
> +#define __ARM_KVM_DECODE_H__
> +
> +#include <linux/types.h>
> +
> +struct kvm_vcpu;
> +struct kvm_exit_mmio;
> +
> +struct kvm_decode {
> +	struct pt_regs *regs;
> +	unsigned long fault_addr;
> +	unsigned long rt;
> +	bool sign_extend;
> +};
> +
> +int kvm_decode_load_store(struct kvm_decode *decode, unsigned long instr,
> +			  struct kvm_exit_mmio *mmio);
> +
> +static inline unsigned long *kvm_decode_reg(struct kvm_decode *decode, int reg)
> +{
> +	return &decode->regs->uregs[reg];
> +}
> +
> +static inline unsigned long *kvm_decode_cpsr(struct kvm_decode *decode)
> +{
> +	return &decode->regs->ARM_cpsr;
> +}
> +
> +#endif /* __ARM_KVM_DECODE_H__ */
> diff --git a/arch/arm/include/asm/kvm_emulate.h b/arch/arm/include/asm/kvm_emulate.h
> index 01a755b..375795b 100644
> --- a/arch/arm/include/asm/kvm_emulate.h
> +++ b/arch/arm/include/asm/kvm_emulate.h
> @@ -21,11 +21,14 @@
>  
>  #include <linux/kvm_host.h>
>  #include <asm/kvm_asm.h>
> +#include <asm/kvm_mmio.h>
>  
>  u32 *vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num);
>  u32 *vcpu_spsr(struct kvm_vcpu *vcpu);
>  
>  int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run);
> +int kvm_emulate_mmio_ls(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
> +			struct kvm_exit_mmio *mmio);
>  void kvm_skip_instr(struct kvm_vcpu *vcpu, bool is_wide_instr);
>  void kvm_inject_undefined(struct kvm_vcpu *vcpu);
>  void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr);
> @@ -53,4 +56,9 @@ static inline bool vcpu_mode_priv(struct kvm_vcpu *vcpu)
>  	return cpsr_mode > USR_MODE;;
>  }
>  
> +static inline bool kvm_vcpu_reg_is_pc(struct kvm_vcpu *vcpu, int reg)
> +{
> +	return reg == 15;
> +}
> +
>  #endif /* __ARM_KVM_EMULATE_H__ */
> diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h
> index 6cc8933..ca40795 100644
> --- a/arch/arm/include/asm/kvm_host.h
> +++ b/arch/arm/include/asm/kvm_host.h
> @@ -22,6 +22,7 @@
>  #include <asm/kvm.h>
>  #include <asm/kvm_asm.h>
>  #include <asm/fpstate.h>
> +#include <asm/kvm_decode.h>
>  
>  #define KVM_MAX_VCPUS CONFIG_KVM_ARM_MAX_VCPUS
>  #define KVM_USER_MEM_SLOTS 32
> @@ -99,6 +100,12 @@ struct kvm_vcpu_arch {
>  	int last_pcpu;
>  	cpumask_t require_dcache_flush;
>  
> +	/* Don't run the guest: see copy_current_insn() */
> +	bool pause;
> +
> +	/* IO related fields */
> +	struct kvm_decode mmio_decode;
> +
>  	/* Interrupt related fields */
>  	u32 irq_lines;		/* IRQ and FIQ levels */
>  
> diff --git a/arch/arm/include/asm/kvm_mmio.h b/arch/arm/include/asm/kvm_mmio.h
> new file mode 100644
> index 0000000..31ab9f5
> --- /dev/null
> +++ b/arch/arm/include/asm/kvm_mmio.h
> @@ -0,0 +1,51 @@
> +/*
> + * Copyright (C) 2012 - Virtual Open Systems and Columbia University
> + * Author: Christoffer Dall <c.dall@virtualopensystems.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, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
> + */
> +
> +#ifndef __ARM_KVM_MMIO_H__
> +#define __ARM_KVM_MMIO_H__
> +
> +#include <linux/kvm_host.h>
> +#include <asm/kvm_asm.h>
> +#include <asm/kvm_arm.h>
> +
> +/*
> + * The in-kernel MMIO emulation code wants to use a copy of run->mmio,
> + * which is an anonymous type. Use our own type instead.
> + */
> +struct kvm_exit_mmio {
> +	phys_addr_t	phys_addr;
> +	u8		data[8];
> +	u32		len;
> +	bool		is_write;
> +};
> +
> +static inline void kvm_prepare_mmio(struct kvm_run *run,
> +				    struct kvm_exit_mmio *mmio)
> +{
> +	run->mmio.phys_addr	= mmio->phys_addr;
> +	run->mmio.len		= mmio->len;
> +	run->mmio.is_write	= mmio->is_write;
> +	memcpy(run->mmio.data, mmio->data, mmio->len);
> +	run->exit_reason	= KVM_EXIT_MMIO;
> +}
> +
> +int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run);
> +int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,
> +		 phys_addr_t fault_ipa, struct kvm_memory_slot *memslot);
> +
> +#endif	/* __ARM_KVM_MMIO_H__ */
> diff --git a/arch/arm/kvm/Makefile b/arch/arm/kvm/Makefile
> index 88edce6..44a5f4b 100644
> --- a/arch/arm/kvm/Makefile
> +++ b/arch/arm/kvm/Makefile
> @@ -18,4 +18,4 @@ kvm-arm-y = $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o)
>  
>  obj-y += kvm-arm.o init.o interrupts.o
>  obj-y += arm.o guest.o mmu.o emulate.o reset.o
> -obj-y += coproc.o coproc_a15.o
> +obj-y += coproc.o coproc_a15.o mmio.o decode.o
> diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
> index 0b4ffcf..f42d828 100644
> --- a/arch/arm/kvm/arm.c
> +++ b/arch/arm/kvm/arm.c
> @@ -614,6 +614,12 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
>  	if (unlikely(vcpu->arch.target < 0))
>  		return -ENOEXEC;
>  
> +	if (run->exit_reason == KVM_EXIT_MMIO) {
> +		ret = kvm_handle_mmio_return(vcpu, vcpu->run);
> +		if (ret)
> +			return ret;
> +	}
> +
>  	if (vcpu->sigset_active)
>  		sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved);
>  
> @@ -649,7 +655,13 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
>  		kvm_guest_enter();
>  		vcpu->mode = IN_GUEST_MODE;
>  
> -		ret = kvm_call_hyp(__kvm_vcpu_run, vcpu);
> +		smp_mb(); /* set mode before reading vcpu->arch.pause */
> +		if (unlikely(vcpu->arch.pause)) {
> +			/* This means ignore, try again. */
> +			ret = ARM_EXCEPTION_IRQ;
> +		} else {
> +			ret = kvm_call_hyp(__kvm_vcpu_run, vcpu);
> +		}
>  
>  		vcpu->mode = OUTSIDE_GUEST_MODE;
>  		vcpu->arch.last_pcpu = smp_processor_id();
> diff --git a/arch/arm/kvm/decode.c b/arch/arm/kvm/decode.c
> new file mode 100644
> index 0000000..469cf14
> --- /dev/null
> +++ b/arch/arm/kvm/decode.c
> @@ -0,0 +1,462 @@
> +/*
> + * Copyright (C) 2012 - Virtual Open Systems and Columbia University
> + * Author: Christoffer Dall <c.dall@virtualopensystems.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, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
> + */
> +#include <linux/kvm_host.h>
> +#include <asm/kvm_mmio.h>
> +#include <asm/kvm_emulate.h>
> +#include <asm/kvm_decode.h>
> +#include <trace/events/kvm.h>
> +
> +#include "trace.h"
> +
> +struct arm_instr {
> +	/* Instruction decoding */
> +	u32 opc;
> +	u32 opc_mask;
> +
> +	/* Decoding for the register write back */
> +	bool register_form;
> +	u32 imm;
> +	u8 Rm;
> +	u8 type;
> +	u8 shift_n;
> +
> +	/* Common decoding */
> +	u8 len;
> +	bool sign_extend;
> +	bool w;
> +
> +	bool (*decode)(struct kvm_decode *decode, struct kvm_exit_mmio *mmio,
> +		       unsigned long instr, struct arm_instr *ai);
> +};
> +
> +enum SRType {
> +	SRType_LSL,
> +	SRType_LSR,
> +	SRType_ASR,
> +	SRType_ROR,
> +	SRType_RRX
> +};
> +
> +/* Modelled after DecodeImmShift() in the ARM ARM */
> +static enum SRType decode_imm_shift(u8 type, u8 imm5, u8 *amount)
> +{
> +	switch (type) {
> +	case 0x0:
> +		*amount = imm5;
> +		return SRType_LSL;
> +	case 0x1:
> +		*amount = (imm5 == 0) ? 32 : imm5;
> +		return SRType_LSR;
> +	case 0x2:
> +		*amount = (imm5 == 0) ? 32 : imm5;
> +		return SRType_ASR;
> +	case 0x3:
> +		if (imm5 == 0) {
> +			*amount = 1;
> +			return SRType_RRX;
> +		} else {
> +			*amount = imm5;
> +			return SRType_ROR;
> +		}
> +	}
> +
> +	return SRType_LSL;
> +}
> +
> +/* Modelled after Shift() in the ARM ARM */
> +static u32 shift(u32 value, u8 N, enum SRType type, u8 amount, bool carry_in)
> +{
> +	u32 mask = (1 << N) - 1;
> +	s32 svalue = (s32)value;
> +
> +	BUG_ON(N > 32);
> +	BUG_ON(type == SRType_RRX && amount != 1);
> +	BUG_ON(amount > N);
> +
> +	if (amount == 0)
> +		return value;
> +
> +	switch (type) {
> +	case SRType_LSL:
> +		value <<= amount;
> +		break;
> +	case SRType_LSR:
> +		 value >>= amount;
> +		break;
> +	case SRType_ASR:
> +		if (value & (1 << (N - 1)))
> +			svalue |= ((-1UL) << N);
> +		value = svalue >> amount;
> +		break;
> +	case SRType_ROR:
> +		value = (value >> amount) | (value << (N - amount));
> +		break;
> +	case SRType_RRX: {
> +		u32 C = (carry_in) ? 1 : 0;
> +		value = (value >> 1) | (C << (N - 1));
> +		break;
> +	}
> +	}
> +
> +	return value & mask;
> +}
> +
> +static bool decode_arm_wb(struct kvm_decode *decode, struct kvm_exit_mmio *mmio,
> +			  unsigned long instr, const struct arm_instr *ai)
> +{
> +	u8 Rt = (instr >> 12) & 0xf;
> +	u8 Rn = (instr >> 16) & 0xf;
> +	u8 W = (instr >> 21) & 1;
> +	u8 U = (instr >> 23) & 1;
> +	u8 P = (instr >> 24) & 1;
> +	u32 base_addr = *kvm_decode_reg(decode, Rn);
> +	u32 offset_addr, offset;
> +
> +	/*
> +	 * Technically this is allowed in certain circumstances,
> +	 * but we don't support it.
> +	 */
> +	if (Rt == 15 || Rn == 15)
> +		return false;
> +
> +	if (P && !W) {
> +		kvm_err("Decoding operation with valid ISV?\n");
> +		return false;
> +	}
> +
> +	decode->rt = Rt;
> +
> +	if (ai->register_form) {
> +		/* Register operation */
> +		enum SRType s_type;
> +		u8 shift_n = 0;
> +		bool c_bit = *kvm_decode_cpsr(decode) & PSR_C_BIT;
> +		u32 s_reg = *kvm_decode_reg(decode, ai->Rm);
> +
> +		s_type = decode_imm_shift(ai->type, ai->shift_n, &shift_n);
> +		offset = shift(s_reg, 5, s_type, shift_n, c_bit);
> +	} else {
> +		/* Immediate operation */
> +		offset = ai->imm;
> +	}
> +
> +	/* Handle Writeback */
> +	if (U)
> +		offset_addr = base_addr + offset;
> +	else
> +		offset_addr = base_addr - offset;
> +	*kvm_decode_reg(decode, Rn) = offset_addr;
> +	return true;
> +}
> +
> +static bool decode_arm_ls(struct kvm_decode *decode, struct kvm_exit_mmio *mmio,
> +			  unsigned long instr, struct arm_instr *ai)
> +{
> +	u8 A = (instr >> 25) & 1;
> +
> +	mmio->is_write = ai->w;
> +	mmio->len = ai->len;
> +	decode->sign_extend = false;
> +
> +	ai->register_form = A;
> +	ai->imm = instr & 0xfff;
> +	ai->Rm = instr & 0xf;
> +	ai->type = (instr >> 5) & 0x3;
> +	ai->shift_n = (instr >> 7) & 0x1f;
> +
> +	return decode_arm_wb(decode, mmio, instr, ai);
> +}
> +
> +static bool decode_arm_extra(struct kvm_decode *decode,
> +			     struct kvm_exit_mmio *mmio,
> +			     unsigned long instr, struct arm_instr *ai)
> +{
> +	mmio->is_write = ai->w;
> +	mmio->len = ai->len;
> +	decode->sign_extend = ai->sign_extend;
> +
> +	ai->register_form = !((instr >> 22) & 1);
> +	ai->imm = ((instr >> 4) & 0xf0) | (instr & 0xf);
> +	ai->Rm = instr & 0xf;
> +	ai->type = 0; /* SRType_LSL */
> +	ai->shift_n = 0;
> +
> +	return decode_arm_wb(decode, mmio, instr, ai);
> +}
> +
> +/*
> + * The encodings in this table assumes that a fault was generated where the
> + * ISV field in the HSR was clear, and the decoding information was invalid,
> + * which means that a register write-back occurred, the PC was used as the
> + * destination or a load/store multiple operation was used. Since the latter
> + * two cases are crazy for MMIO on the guest side, we simply inject a fault
> + * when this happens and support the common case.
> + *
> + * We treat unpriviledged loads and stores of words and bytes like all other
> + * loads and stores as their encodings mandate the W bit set and the P bit
> + * clear.
> + */
> +static const struct arm_instr arm_instr[] = {
> +	/**************** Load/Store Word and Byte **********************/
> +	/* Store word with writeback */
> +	{ .opc = 0x04000000, .opc_mask = 0x0c500000, .len = 4, .w = true,
> +		.sign_extend = false, .decode = decode_arm_ls },
> +	/* Store byte with writeback */
> +	{ .opc = 0x04400000, .opc_mask = 0x0c500000, .len = 1, .w = true,
> +		.sign_extend = false, .decode = decode_arm_ls },
> +	/* Load word with writeback */
> +	{ .opc = 0x04100000, .opc_mask = 0x0c500000, .len = 4, .w = false,
> +		.sign_extend = false, .decode = decode_arm_ls },
> +	/* Load byte with writeback */
> +	{ .opc = 0x04500000, .opc_mask = 0x0c500000, .len = 1, .w = false,
> +		.sign_extend = false, .decode = decode_arm_ls },
> +
> +	/*************** Extra load/store instructions ******************/
> +
> +	/* Store halfword with writeback */
> +	{ .opc = 0x000000b0, .opc_mask = 0x0c1000f0, .len = 2, .w = true,
> +		.sign_extend = false, .decode = decode_arm_extra },
> +	/* Load halfword with writeback */
> +	{ .opc = 0x001000b0, .opc_mask = 0x0c1000f0, .len = 2, .w = false,
> +		.sign_extend = false, .decode = decode_arm_extra },
> +
> +	/* Load dual with writeback */
> +	{ .opc = 0x000000d0, .opc_mask = 0x0c1000f0, .len = 8, .w = false,
> +		.sign_extend = false, .decode = decode_arm_extra },
> +	/* Load signed byte with writeback */
> +	{ .opc = 0x001000d0, .opc_mask = 0x0c1000f0, .len = 1, .w = false,
> +		.sign_extend = true,  .decode = decode_arm_extra },
> +
> +	/* Store dual with writeback */
> +	{ .opc = 0x000000f0, .opc_mask = 0x0c1000f0, .len = 8, .w = true,
> +		.sign_extend = false, .decode = decode_arm_extra },
> +	/* Load signed halfword with writeback */
> +	{ .opc = 0x001000f0, .opc_mask = 0x0c1000f0, .len = 2, .w = false,
> +		.sign_extend = true,  .decode = decode_arm_extra },
> +
> +	/* Store halfword unprivileged */
> +	{ .opc = 0x002000b0, .opc_mask = 0x0f3000f0, .len = 2, .w = true,
> +		.sign_extend = false, .decode = decode_arm_extra },
> +	/* Load halfword unprivileged */
> +	{ .opc = 0x003000b0, .opc_mask = 0x0f3000f0, .len = 2, .w = false,
> +		.sign_extend = false, .decode = decode_arm_extra },
> +	/* Load signed byte unprivileged */
> +	{ .opc = 0x003000d0, .opc_mask = 0x0f3000f0, .len = 1, .w = false,
> +		.sign_extend = true , .decode = decode_arm_extra },
> +	/* Load signed halfword unprivileged */
> +	{ .opc = 0x003000d0, .opc_mask = 0x0f3000f0, .len = 2, .w = false,
> +		.sign_extend = true , .decode = decode_arm_extra },
> +};
> +
> +static bool kvm_decode_arm_ls(struct kvm_decode *decode, unsigned long instr,
> +			      struct kvm_exit_mmio *mmio)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(arm_instr); i++) {
> +		const struct arm_instr *ai = &arm_instr[i];
> +		if ((instr & ai->opc_mask) == ai->opc) {
> +			struct arm_instr ai_copy = *ai;
> +			return ai->decode(decode, mmio, instr, &ai_copy);
> +		}
> +	}
> +	return false;
> +}
> +
> +struct thumb_instr {
> +	bool is32;
> +
> +	u8 opcode;
> +	u8 opcode_mask;
> +	u8 op2;
> +	u8 op2_mask;
> +
> +	bool (*decode)(struct kvm_decode *decode, struct kvm_exit_mmio *mmio,
> +		       unsigned long instr, const struct thumb_instr *ti);
> +};
> +
> +static bool decode_thumb_wb(struct kvm_decode *decode,
> +			    struct kvm_exit_mmio *mmio,
> +			    unsigned long instr)
> +{
> +	bool P = (instr >> 10) & 1;
> +	bool U = (instr >> 9) & 1;
> +	u8 imm8 = instr & 0xff;
> +	u32 offset_addr = decode->fault_addr;
> +	u8 Rn = (instr >> 16) & 0xf;
> +
> +	decode->rt = (instr >> 12) & 0xf;
> +
> +	if (Rn == 15)
> +		return false;
> +
> +	/* Handle Writeback */
> +	if (!P && U)
> +		*kvm_decode_reg(decode, Rn) = offset_addr + imm8;
> +	else if (!P && !U)
> +		*kvm_decode_reg(decode, Rn) = offset_addr - imm8;
> +	return true;
> +}
> +
> +static bool decode_thumb_str(struct kvm_decode *decode,
> +			     struct kvm_exit_mmio *mmio,
> +			     unsigned long instr, const struct thumb_instr *ti)
> +{
> +	u8 op1 = (instr >> (16 + 5)) & 0x7;
> +	u8 op2 = (instr >> 6) & 0x3f;
> +
> +	mmio->is_write = true;
> +	decode->sign_extend = false;
> +
> +	switch (op1) {
> +	case 0x0: mmio->len = 1; break;
> +	case 0x1: mmio->len = 2; break;
> +	case 0x2: mmio->len = 4; break;
> +	default:
> +		  return false; /* Only register write-back versions! */
> +	}
> +
> +	if ((op2 & 0x24) == 0x24) {
> +		/* STRB (immediate, thumb, W=1) */
> +		return decode_thumb_wb(decode, mmio, instr);
> +	}
> +
> +	return false;
> +}
> +
> +static bool decode_thumb_ldr(struct kvm_decode *decode,
> +			     struct kvm_exit_mmio *mmio,
> +			     unsigned long instr, const struct thumb_instr *ti)
> +{
> +	u8 op1 = (instr >> (16 + 7)) & 0x3;
> +	u8 op2 = (instr >> 6) & 0x3f;
> +
> +	mmio->is_write = false;
> +
> +	switch (ti->op2 & 0x7) {
> +	case 0x1: mmio->len = 1; break;
> +	case 0x3: mmio->len = 2; break;
> +	case 0x5: mmio->len = 4; break;
> +	}
> +
> +	if (op1 == 0x0)
> +		decode->sign_extend = false;
> +	else if (op1 == 0x2 && (ti->op2 & 0x7) != 0x5)
> +		decode->sign_extend = true;
> +	else
> +		return false; /* Only register write-back versions! */
> +
> +	if ((op2 & 0x24) == 0x24) {
> +		/* LDR{S}X (immediate, thumb, W=1) */
> +		return decode_thumb_wb(decode, mmio, instr);
> +	}
> +
> +	return false;
> +}
> +
> +/*
> + * We only support instruction decoding for valid reasonable MMIO operations
> + * where trapping them do not provide sufficient information in the HSR (no
> + * 16-bit Thumb instructions provide register writeback that we care about).
> + *
> + * The following instruciton types are NOT supported for MMIO operations
> + * despite the HSR not containing decode info:
> + *  - any Load/Store multiple
> + *  - any load/store exclusive
> + *  - any load/store dual
> + *  - anything with the PC as the dest register
> + */
> +static const struct thumb_instr thumb_instr[] = {
> +	/**************** 32-bit Thumb instructions **********************/
> +	/* Store single data item:	Op1 == 11, Op2 == 000xxx0 */
> +	{ .is32 = true,  .opcode = 3, .op2 = 0x00, .op2_mask = 0x71,
> +						decode_thumb_str	},
> +
> +	/* Load byte:			Op1 == 11, Op2 == 00xx001 */
> +	{ .is32 = true,  .opcode = 3, .op2 = 0x01, .op2_mask = 0x67,
> +						decode_thumb_ldr	},
> +
> +	/* Load halfword:		Op1 == 11, Op2 == 00xx011 */
> +	{ .is32 = true,  .opcode = 3, .op2 = 0x03, .op2_mask = 0x67,
> +						decode_thumb_ldr	},
> +
> +	/* Load word:			Op1 == 11, Op2 == 00xx101 */
> +	{ .is32 = true,  .opcode = 3, .op2 = 0x05, .op2_mask = 0x67,
> +						decode_thumb_ldr	},
> +};
> +
> +
> +
> +static bool kvm_decode_thumb_ls(struct kvm_decode *decode, unsigned long instr,
> +				struct kvm_exit_mmio *mmio)
> +{
> +	bool is32 = is_wide_instruction(instr);
> +	bool is16 = !is32;
> +	struct thumb_instr tinstr; /* re-use to pass on already decoded info */
> +	int i;
> +
> +	if (is16) {
> +		tinstr.opcode = (instr >> 10) & 0x3f;
> +	} else {
> +		tinstr.opcode = (instr >> (16 + 11)) & 0x3;
> +		tinstr.op2 = (instr >> (16 + 4)) & 0x7f;
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(thumb_instr); i++) {
> +		const struct thumb_instr *ti = &thumb_instr[i];
> +		if (ti->is32 != is32)
> +			continue;
> +
> +		if (is16) {
> +			if ((tinstr.opcode & ti->opcode_mask) != ti->opcode)
> +				continue;
> +		} else {
> +			if (ti->opcode != tinstr.opcode)
> +				continue;
> +			if ((ti->op2_mask & tinstr.op2) != ti->op2)
> +				continue;
> +		}
> +
> +		return ti->decode(decode, mmio, instr, &tinstr);
> +	}
> +
> +	return false;
> +}
> +
> +/**
> + * kvm_decode_load_store - decodes load/store instructions
> + * @decode: reads regs and fault_addr, writes rt and sign_extend
> + * @instr:  instruction to decode
> + * @mmio:   fills in len and is_write
> + *
> + * Decode load/store instructions with HSR ISV clear. The code assumes that
> + * this was indeed a KVM fault and therefore assumes registers write back for
> + * single load/store operations and does not support using the PC as the
> + * destination register.
> + */
> +int kvm_decode_load_store(struct kvm_decode *decode, unsigned long instr,
> +			  struct kvm_exit_mmio *mmio)
> +{
> +	bool is_thumb;
> +
> +	is_thumb = !!(*kvm_decode_cpsr(decode) & PSR_T_BIT);
> +	if (!is_thumb)
> +		return kvm_decode_arm_ls(decode, instr, mmio) ? 0 : 1;
> +	else
> +		return kvm_decode_thumb_ls(decode, instr, mmio) ? 0 : 1;
> +}
> diff --git a/arch/arm/kvm/emulate.c b/arch/arm/kvm/emulate.c
> index d61450a..ad743b7 100644
> --- a/arch/arm/kvm/emulate.c
> +++ b/arch/arm/kvm/emulate.c
> @@ -20,6 +20,7 @@
>  #include <linux/kvm_host.h>
>  #include <asm/kvm_arm.h>
>  #include <asm/kvm_emulate.h>
> +#include <asm/kvm_decode.h>
>  #include <trace/events/kvm.h>
>  
>  #include "trace.h"
> @@ -176,6 +177,174 @@ int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run)
>  	return 1;
>  }
>  
> +static u64 kvm_va_to_pa(struct kvm_vcpu *vcpu, u32 va, bool priv)
> +{
> +	return kvm_call_hyp(__kvm_va_to_pa, vcpu, va, priv);
> +}
> +
> +/**
> + * copy_from_guest_va - copy memory from guest (very slow!)
> + * @vcpu:	vcpu pointer
> + * @dest:	memory to copy into
> + * @gva:	virtual address in guest to copy from
> + * @len:	length to copy
> + * @priv:	use guest PL1 (ie. kernel) mappings
> + *              otherwise use guest PL0 mappings.
> + *
> + * Returns true on success, false on failure (unlikely, but retry).
> + */
> +static bool copy_from_guest_va(struct kvm_vcpu *vcpu,
> +			       void *dest, unsigned long gva, size_t len,
> +			       bool priv)
> +{
> +	u64 par;
> +	phys_addr_t pc_ipa;
> +	int err;
> +
> +	BUG_ON((gva & PAGE_MASK) != ((gva + len) & PAGE_MASK));
> +	par = kvm_va_to_pa(vcpu, gva & PAGE_MASK, priv);
> +	if (par & 1) {
> +		kvm_err("IO abort from invalid instruction address"
> +			" %#lx!\n", gva);
> +		return false;
> +	}
> +
> +	BUG_ON(!(par & (1U << 11)));
> +	pc_ipa = par & PAGE_MASK & ((1ULL << 32) - 1);
> +	pc_ipa += gva & ~PAGE_MASK;
> +
> +
> +	err = kvm_read_guest(vcpu->kvm, pc_ipa, dest, len);
> +	if (unlikely(err))
> +		return false;
> +
> +	return true;
> +}
> +
> +/*
> + * We have to be very careful copying memory from a running (ie. SMP) guest.
> + * Another CPU may remap the page (eg. swap out a userspace text page) as we
> + * read the instruction.  Unlike normal hardware operation, to emulate an
> + * instruction we map the virtual to physical address then read that memory
> + * as separate steps, thus not atomic.
> + *
> + * Fortunately this is so rare (we don't usually need the instruction), we
> + * can go very slowly and noone will mind.
> + */
> +static bool copy_current_insn(struct kvm_vcpu *vcpu, unsigned long *instr)
> +{
> +	int i;
> +	bool ret;
> +	struct kvm_vcpu *v;
> +	bool is_thumb;
> +	size_t instr_len;
> +
> +	/* Don't cross with IPIs in kvm_main.c */
> +	spin_lock(&vcpu->kvm->mmu_lock);
> +
> +	/* Tell them all to pause, so no more will enter guest. */
> +	kvm_for_each_vcpu(i, v, vcpu->kvm)
> +		v->arch.pause = true;
> +
> +	/* Set ->pause before we read ->mode */
> +	smp_mb();
> +
> +	/* Kick out any which are still running. */
> +	kvm_for_each_vcpu(i, v, vcpu->kvm) {
> +		/* Guest could exit now, making cpu wrong. That's OK. */
> +		if (kvm_vcpu_exiting_guest_mode(v) == IN_GUEST_MODE) {
> +			force_vm_exit(get_cpu_mask(v->cpu));
> +		}
> +	}
> +
> +
> +	is_thumb = !!(*vcpu_cpsr(vcpu) & PSR_T_BIT);
> +	instr_len = (is_thumb) ? 2 : 4;
> +
> +	BUG_ON(!is_thumb && *vcpu_pc(vcpu) & 0x3);
> +
> +	/* Now guest isn't running, we can va->pa map and copy atomically. */
> +	ret = copy_from_guest_va(vcpu, instr, *vcpu_pc(vcpu), instr_len,
> +				 vcpu_mode_priv(vcpu));
> +	if (!ret)
> +		goto out;
> +
> +	/* A 32-bit thumb2 instruction can actually go over a page boundary! */
> +	if (is_thumb && is_wide_instruction(*instr)) {
> +		*instr = *instr << 16;
> +		ret = copy_from_guest_va(vcpu, instr, *vcpu_pc(vcpu) + 2, 2,
> +					 vcpu_mode_priv(vcpu));
> +	}
> +
> +out:
> +	/* Release them all. */
> +	kvm_for_each_vcpu(i, v, vcpu->kvm)
> +		v->arch.pause = false;
> +
> +	spin_unlock(&vcpu->kvm->mmu_lock);
> +
> +	return ret;
> +}
> +
> +/**
> + * kvm_emulate_mmio_ls - emulates load/store instructions made to I/O memory
> + * @vcpu:	The vcpu pointer
> + * @fault_ipa:	The IPA that caused the 2nd stage fault
> + * @mmio:      Pointer to struct to hold decode information
> + *
> + * Some load/store instructions cannot be emulated using the information
> + * presented in the HSR, for instance, register write-back instructions are not
> + * supported. We therefore need to fetch the instruction, decode it, and then
> + * emulate its behavior.
> + *
> + * Handles emulation of load/store instructions which cannot be emulated through
> + * information found in the HSR on faults. It is necessary in this case to
> + * simply decode the offending instruction in software and determine the
> + * required operands.
> + */
> +int kvm_emulate_mmio_ls(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
> +			struct kvm_exit_mmio *mmio)
> +{
> +	unsigned long instr = 0;
> +	struct pt_regs current_regs;
> +	struct kvm_decode *decode = &vcpu->arch.mmio_decode;
> +	int ret;
> +
> +	trace_kvm_mmio_emulate(*vcpu_pc(vcpu), instr, *vcpu_cpsr(vcpu));
> +
> +	/* If it fails (SMP race?), we reenter guest for it to retry. */
> +	if (!copy_current_insn(vcpu, &instr))
> +		return 1;
> +
> +	mmio->phys_addr = fault_ipa;
> +
> +	memcpy(&current_regs, &vcpu->arch.regs.usr_regs, sizeof(current_regs));
> +	current_regs.ARM_sp = *vcpu_reg(vcpu, 13);
> +	current_regs.ARM_lr = *vcpu_reg(vcpu, 14);
> +
> +	decode->regs = &current_regs;
> +	decode->fault_addr = vcpu->arch.hxfar;
> +	ret = kvm_decode_load_store(decode, instr, mmio);
> +	if (ret) {
> +		kvm_debug("Insrn. decode error: %#08lx (cpsr: %#08x"
> +			  "pc: %#08x)\n",
> +			  instr, *vcpu_cpsr(vcpu), *vcpu_pc(vcpu));
> +		kvm_inject_dabt(vcpu, vcpu->arch.hxfar);
> +		return ret;
> +	}
> +
> +	memcpy(&vcpu->arch.regs.usr_regs, &current_regs, sizeof(current_regs));
> +	*vcpu_reg(vcpu, 13) = current_regs.ARM_sp;
> +	*vcpu_reg(vcpu, 14) = current_regs.ARM_lr;
> +
> +	/*
> +	 * The MMIO instruction is emulated and should not be re-executed
> +	 * in the guest.
> +	 */
> +	kvm_skip_instr(vcpu, is_wide_instruction(instr));
> +	return 0;
> +}
> +
>  /**
>   * adjust_itstate - adjust ITSTATE when emulating instructions in IT-block
>   * @vcpu:	The VCPU pointer
> diff --git a/arch/arm/kvm/interrupts.S b/arch/arm/kvm/interrupts.S
> index 08adcd5..45570b8 100644
> --- a/arch/arm/kvm/interrupts.S
> +++ b/arch/arm/kvm/interrupts.S
> @@ -192,6 +192,44 @@ after_vfp_restore:
>  	mov	r0, r1			@ Return the return code
>  	bx	lr			@ return to IOCTL
>  
> +
> +/********************************************************************
> + * Translate VA to PA
> + *
> + * u64 __kvm_va_to_pa(struct kvm_vcpu *vcpu, u32 va, bool priv)
> + *
> + * Arguments:
> + *  r0: pointer to vcpu struct
> + *  r1: virtual address to map (rounded to page)
> + *  r2: 1 = P1 (read) mapping, 0 = P0 (read) mapping.
> + * Returns 64 bit PAR value.
> + */
> +ENTRY(__kvm_va_to_pa)
> +	push	{r4-r12}
> +
> +	@ Fold flag into r1, easier than using stack.
> +	cmp	r2, #0
> +	movne	r2, #1
> +	orr	r1, r1, r2
> +
> +	@ This swaps too many registers, but we're in the slow path anyway.
> +	read_cp15_state store_to_vcpu = 0
> +	write_cp15_state read_from_vcpu = 1
> +
> +	ands	r2, r1, #1
> +	bic	r1, r1, r2
> +	mcrne	p15, 0, r1, c7, c8, 0	@ VA to PA, ATS1CPR
> +	mcreq	p15, 0, r1, c7, c8, 2	@ VA to PA, ATS1CUR
> +	isb
> +
> +	@ Restore host state.
> +	read_cp15_state store_to_vcpu = 1
> +	write_cp15_state read_from_vcpu = 0
> +
> +	mrrc	p15, 0, r0, r1, c7	@ PAR
> +	pop	{r4-r12}
> +	bx	lr
> +
>  ENTRY(kvm_call_hyp)
>  	hvc	#0
>  	bx	lr
> diff --git a/arch/arm/kvm/mmio.c b/arch/arm/kvm/mmio.c
> new file mode 100644
> index 0000000..d6a4ca0
> --- /dev/null
> +++ b/arch/arm/kvm/mmio.c
> @@ -0,0 +1,154 @@
> +/*
> + * Copyright (C) 2012 - Virtual Open Systems and Columbia University
> + * Author: Christoffer Dall <c.dall@virtualopensystems.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, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
> + */
> +
> +#include <asm/kvm_mmio.h>
> +#include <asm/kvm_emulate.h>
> +#include <asm/kvm_decode.h>
> +#include <trace/events/kvm.h>
> +
> +#include "trace.h"
> +
> +/**
> + * kvm_handle_mmio_return -- Handle MMIO loads after user space emulation
> + * @vcpu: The VCPU pointer
> + * @run:  The VCPU run struct containing the mmio data
> + *
> + * This should only be called after returning from userspace for MMIO load
> + * emulation.
> + */
> +int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run)
> +{
> +	__u32 *dest;
> +	unsigned int len;
> +	int mask;
> +
> +	if (!run->mmio.is_write) {
> +		dest = vcpu_reg(vcpu, vcpu->arch.mmio_decode.rt);
> +		memset(dest, 0, sizeof(int));
> +
> +		len = run->mmio.len;
> +		if (len > 4)
> +			return -EINVAL;
> +
> +		memcpy(dest, run->mmio.data, len);
> +
> +		trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr,
> +				*((u64 *)run->mmio.data));
> +
> +		if (vcpu->arch.mmio_decode.sign_extend && len < 4) {
> +			mask = 1U << ((len * 8) - 1);
> +			*dest = (*dest ^ mask) - mask;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int decode_hsr(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
> +		      struct kvm_exit_mmio *mmio)
> +{
> +	unsigned long rt, len;
> +	bool is_write, sign_extend;
> +
> +	if ((vcpu->arch.hsr >> 8) & 1) {
> +		/* cache operation on I/O addr, tell guest unsupported */
> +		kvm_inject_dabt(vcpu, vcpu->arch.hxfar);
> +		return 1;
> +	}
> +
> +	if ((vcpu->arch.hsr >> 7) & 1) {
> +		/* page table accesses IO mem: tell guest to fix its TTBR */
> +		kvm_inject_dabt(vcpu, vcpu->arch.hxfar);
> +		return 1;
> +	}
> +
> +	switch ((vcpu->arch.hsr >> 22) & 0x3) {
> +	case 0:
> +		len = 1;
> +		break;
> +	case 1:
> +		len = 2;
> +		break;
> +	case 2:
> +		len = 4;
> +		break;
> +	default:
> +		kvm_err("Hardware is weird: SAS 0b11 is reserved\n");
> +		return -EFAULT;
> +	}
> +
> +	is_write = vcpu->arch.hsr & HSR_WNR;
> +	sign_extend = vcpu->arch.hsr & HSR_SSE;
> +	rt = (vcpu->arch.hsr & HSR_SRT_MASK) >> HSR_SRT_SHIFT;
> +
> +	if (kvm_vcpu_reg_is_pc(vcpu, rt)) {
> +		/* IO memory trying to read/write pc */
> +		kvm_inject_pabt(vcpu, vcpu->arch.hxfar);
> +		return 1;
> +	}
> +
> +	mmio->is_write = is_write;
> +	mmio->phys_addr = fault_ipa;
> +	mmio->len = len;
> +	vcpu->arch.mmio_decode.sign_extend = sign_extend;
> +	vcpu->arch.mmio_decode.rt = rt;
> +
> +	/*
> +	 * The MMIO instruction is emulated and should not be re-executed
> +	 * in the guest.
> +	 */
> +	kvm_skip_instr(vcpu, (vcpu->arch.hsr >> 25) & 1);
> +	return 0;
> +}
> +
> +int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,
> +		 phys_addr_t fault_ipa, struct kvm_memory_slot *memslot)
> +{
> +	struct kvm_exit_mmio mmio;
> +	unsigned long rt;
> +	int ret;
> +
> +	/*
> +	 * Prepare MMIO operation. First stash it in a private
> +	 * structure that we can use for in-kernel emulation. If the
> +	 * kernel can't handle it, copy it into run->mmio and let user
> +	 * space do its magic.
> +	 */
> +
> +	if (vcpu->arch.hsr & HSR_ISV) {
> +		ret = decode_hsr(vcpu, fault_ipa, &mmio);
> +		if (ret)
> +			return ret;
> +	} else {
> +		ret = kvm_emulate_mmio_ls(vcpu, fault_ipa, &mmio);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	rt = vcpu->arch.mmio_decode.rt;
> +	trace_kvm_mmio((mmio.is_write) ? KVM_TRACE_MMIO_WRITE :
> +					 KVM_TRACE_MMIO_READ_UNSATISFIED,
> +			mmio.len, fault_ipa,
> +			(mmio.is_write) ? *vcpu_reg(vcpu, rt) : 0);
> +
> +	if (mmio.is_write)
> +		memcpy(mmio.data, vcpu_reg(vcpu, rt), mmio.len);
> +
> +	kvm_prepare_mmio(run, &mmio);
> +	return 0;
> +}
> diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c
> index 0ce0e77..2a83ac9 100644
> --- a/arch/arm/kvm/mmu.c
> +++ b/arch/arm/kvm/mmu.c
> @@ -19,11 +19,13 @@
>  #include <linux/mman.h>
>  #include <linux/kvm_host.h>
>  #include <linux/io.h>
> +#include <trace/events/kvm.h>
>  #include <asm/idmap.h>
>  #include <asm/pgalloc.h>
>  #include <asm/cacheflush.h>
>  #include <asm/kvm_arm.h>
>  #include <asm/kvm_mmu.h>
> +#include <asm/kvm_mmio.h>
>  #include <asm/kvm_asm.h>
>  #include <asm/kvm_emulate.h>
>  #include <asm/mach/map.h>
> @@ -620,8 +622,9 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run)
>  			return -EFAULT;
>  		}
>  
> -		kvm_pr_unimpl("I/O address abort...");
> -		return 0;
> +		/* Adjust page offset */
> +		fault_ipa |= vcpu->arch.hxfar & ~PAGE_MASK;
> +		return io_mem_abort(vcpu, run, fault_ipa, memslot);
>  	}
>  
>  	memslot = gfn_to_memslot(vcpu->kvm, gfn);
> diff --git a/arch/arm/kvm/trace.h b/arch/arm/kvm/trace.h
> index 5d65751..cd52640 100644
> --- a/arch/arm/kvm/trace.h
> +++ b/arch/arm/kvm/trace.h
> @@ -90,6 +90,27 @@ TRACE_EVENT(kvm_irq_line,
>  		  __entry->type, __entry->vcpu_idx, __entry->irq_num, __entry->level)
>  );
>  
> +TRACE_EVENT(kvm_mmio_emulate,
> +	TP_PROTO(unsigned long vcpu_pc, unsigned long instr,
> +		 unsigned long cpsr),
> +	TP_ARGS(vcpu_pc, instr, cpsr),
> +
> +	TP_STRUCT__entry(
> +		__field(	unsigned long,	vcpu_pc		)
> +		__field(	unsigned long,	instr		)
> +		__field(	unsigned long,	cpsr		)
> +	),
> +
> +	TP_fast_assign(
> +		__entry->vcpu_pc		= vcpu_pc;
> +		__entry->instr			= instr;
> +		__entry->cpsr			= cpsr;
> +	),
> +
> +	TP_printk("Emulate MMIO at: 0x%08lx (instr: %08lx, cpsr: %08lx)",
> +		  __entry->vcpu_pc, __entry->instr, __entry->cpsr)
> +);
> +
>  /* Architecturally implementation defined CP15 register access */
>  TRACE_EVENT(kvm_emulate_cp15_imp,
>  	TP_PROTO(unsigned long Op1, unsigned long Rt1, unsigned long CRn,
> 
> --
> To unsubscribe from this list: send the line "unsubscribe kvm" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

--
			Gleb.

^ permalink raw reply

* [PATCH RESEND 6/6 v13] gpio: Add block gpio to several gpio drivers
From: Nicolas Ferre @ 2013-01-15 13:18 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358250716-21986-7-git-send-email-stigge@antcom.de>

Hi,

Le 15/01/2013 12:51, Roland Stigge a ?crit :
> This patch adds block GPIO support to several gpio drivers.
>
> This implements block GPIO only for some selected drivers since block GPIO is
> an optional feature which may not be suitable for every GPIO hardware. (With
> automatic fallback to the single GPIO functions if not available in a driver.)
>
> Signed-off-by: Roland Stigge <stigge@antcom.de>
>
> ---
>   drivers/gpio/Kconfig              |    2
>   drivers/gpio/gpio-em.c            |   23 ++++++++++
>   drivers/gpio/gpio-ge.c            |   29 +++++++++++++
>   drivers/gpio/gpio-generic.c       |   56 +++++++++++++++++++++++++
>   drivers/gpio/gpio-ks8695.c        |   34 +++++++++++++++
>   drivers/gpio/gpio-lpc32xx.c       |   82 ++++++++++++++++++++++++++++++++++++++
>   drivers/gpio/gpio-max730x.c       |   61 ++++++++++++++++++++++++++++
>   drivers/gpio/gpio-max732x.c       |   59 +++++++++++++++++++++++++++
>   drivers/gpio/gpio-mc33880.c       |   16 +++++++
>   drivers/gpio/gpio-ml-ioh.c        |   27 ++++++++++++
>   drivers/gpio/gpio-mm-lantiq.c     |   22 ++++++++++
>   drivers/gpio/gpio-mpc5200.c       |   64 +++++++++++++++++++++++++++++
>   drivers/gpio/gpio-mpc8xxx.c       |   41 +++++++++++++++++++
>   drivers/gpio/gpio-pca953x.c       |   64 +++++++++++++++++++++++++++++
>   drivers/gpio/gpio-pcf857x.c       |   24 +++++++++++
>   drivers/gpio/gpio-pch.c           |   27 ++++++++++++
>   drivers/gpio/gpio-pl061.c         |   17 +++++++
>   drivers/gpio/gpio-sa1100.c        |   20 +++++++++
>   drivers/gpio/gpio-samsung.c       |   31 ++++++++++++++
>   drivers/gpio/gpio-twl6040.c       |   32 ++++++++++++++
>   drivers/gpio/gpio-ucb1400.c       |   23 ++++++++++
>   drivers/gpio/gpio-vt8500.c        |   24 +++++++++++
>   drivers/gpio/gpio-xilinx.c        |   44 ++++++++++++++++++++
>   drivers/pinctrl/pinctrl-at91.c    |   29 +++++++++++++

I do not want to delay the process of inclusion for this patch series. 
But I have a little question on AT91 driver modification...

>   drivers/pinctrl/pinctrl-nomadik.c |   36 ++++++++++++++++
>   25 files changed, 887 insertions(+)

[..]

> --- linux-2.6.orig/drivers/pinctrl/pinctrl-at91.c
> +++ linux-2.6/drivers/pinctrl/pinctrl-at91.c
> @@ -49,6 +49,7 @@ struct at91_gpio_chip {
>   	struct clk		*clock;		/* associated clock */
>   	struct irq_domain	*domain;	/* associated irq domain */
>   	struct at91_pinctrl_mux_ops *ops;	/* ops */
> +	unsigned long		mask_cache;	/* cached mask for block gpio */
>   };
>
>   #define to_at91_gpio_chip(c) container_of(c, struct at91_gpio_chip, chip)
> @@ -1125,6 +1126,32 @@ static void at91_gpio_set(struct gpio_ch
>   	writel_relaxed(mask, pio + (val ? PIO_SODR : PIO_CODR));
>   }
>
> +static unsigned long at91_gpio_get_block(struct gpio_chip *chip,
> +					 unsigned long mask)
> +{
> +	struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
> +	void __iomem *pio = at91_gpio->regbase;
> +	u32 pdsr;
> +
> +	pdsr = __raw_readl(pio + PIO_PDSR);

Maybe you should use readl_relaxed() here as it is used in the 
at91_gpio_[get|set]() functions.


> +	return pdsr & mask;
> +}
> +
> +static void at91_gpio_set_block(struct gpio_chip *chip, unsigned long mask,
> +				unsigned long val)
> +{
> +	struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
> +	void __iomem *pio = at91_gpio->regbase;
> +
> +	/* Do synchronous data output with a single write access */
> +	if (mask != at91_gpio->mask_cache) {
> +		at91_gpio->mask_cache = mask;
> +		__raw_writel(~mask, pio + PIO_OWDR);
> +		__raw_writel(mask, pio + PIO_OWER);
> +	}
> +	__raw_writel(val, pio + PIO_ODSR);

Ditto.

> +}
> +
>   static int at91_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
>   				int val)
>   {
> @@ -1435,8 +1462,10 @@ static struct gpio_chip at91_gpio_templa
>   	.free			= at91_gpio_free,
>   	.direction_input	= at91_gpio_direction_input,
>   	.get			= at91_gpio_get,
> +	.get_block		= at91_gpio_get_block,
>   	.direction_output	= at91_gpio_direction_output,
>   	.set			= at91_gpio_set,
> +	.set_block		= at91_gpio_set_block,
>   	.to_irq			= at91_gpio_to_irq,
>   	.dbg_show		= at91_gpio_dbg_show,
>   	.can_sleep		= 0,
> --- linux-2.6.orig/drivers/pinctrl/pinctrl-nomadik.c
> +++ linux-2.6/drivers/pinctrl/pinctrl-nomadik.c

[..]

Otherwise, seems ok to me ; for AT91 part:

Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>

Best regards,
-- 
Nicolas Ferre

^ permalink raw reply

* [PATCH] ARM: remove unnecessary 'select GENERIC_GPIO'
From: Shawn Guo @ 2013-01-15 13:13 UTC (permalink / raw)
  To: linux-arm-kernel

The only use of GENERIC_GPIO (defined by architecture) in GPIO subsystem
is being selected by GPIOLIB.  Also there are no any use of the option
at architecture level. Only two sub-architectures shmobile and orion
really use the option as below.

  arch/arm/mach-shmobile/Makefile:obj-$(CONFIG_GENERIC_GPIO)      += $(pfc-y)
  arch/arm/plat-orion/Makefile:orion-gpio-$(CONFIG_GENERIC_GPIO) += gpio.o

Remove all those unnecessary sub-architecture level selection of
GENERIC_GPIO, which are there only for confusing people.

Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
---
 arch/arm/Kconfig                |    9 ---------
 arch/arm/mach-bcm/Kconfig       |    1 -
 arch/arm/mach-picoxcell/Kconfig |    1 -
 arch/arm/mach-vt8500/Kconfig    |    1 -
 4 files changed, 12 deletions(-)

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 67874b8..c217521 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -347,7 +347,6 @@ config ARCH_BCM2835
 	select COMMON_CLK
 	select CPU_V6
 	select GENERIC_CLOCKEVENTS
-	select GENERIC_GPIO
 	select MULTI_IRQ_HANDLER
 	select PINCTRL
 	select PINCTRL_BCM2835
@@ -644,7 +643,6 @@ config ARCH_TEGRA
 	select CLKSRC_MMIO
 	select COMMON_CLK
 	select GENERIC_CLOCKEVENTS
-	select GENERIC_GPIO
 	select HAVE_CLK
 	select HAVE_SMP
 	select MIGHT_HAVE_CACHE_L2X0
@@ -744,7 +742,6 @@ config ARCH_S3C24XX
 	select ARCH_HAS_CPUFREQ
 	select ARCH_USES_GETTIMEOFFSET
 	select CLKDEV_LOOKUP
-	select GENERIC_GPIO
 	select HAVE_CLK
 	select HAVE_S3C2410_I2C if I2C
 	select HAVE_S3C2410_WATCHDOG if WATCHDOG
@@ -787,7 +784,6 @@ config ARCH_S5P64X0
 	select CLKSRC_MMIO
 	select CPU_V6
 	select GENERIC_CLOCKEVENTS
-	select GENERIC_GPIO
 	select HAVE_CLK
 	select HAVE_S3C2410_I2C if I2C
 	select HAVE_S3C2410_WATCHDOG if WATCHDOG
@@ -802,7 +798,6 @@ config ARCH_S5PC100
 	select ARCH_USES_GETTIMEOFFSET
 	select CLKDEV_LOOKUP
 	select CPU_V7
-	select GENERIC_GPIO
 	select HAVE_CLK
 	select HAVE_S3C2410_I2C if I2C
 	select HAVE_S3C2410_WATCHDOG if WATCHDOG
@@ -820,7 +815,6 @@ config ARCH_S5PV210
 	select CLKSRC_MMIO
 	select CPU_V7
 	select GENERIC_CLOCKEVENTS
-	select GENERIC_GPIO
 	select HAVE_CLK
 	select HAVE_S3C2410_I2C if I2C
 	select HAVE_S3C2410_WATCHDOG if WATCHDOG
@@ -838,7 +832,6 @@ config ARCH_EXYNOS
 	select CLKDEV_LOOKUP
 	select CPU_V7
 	select GENERIC_CLOCKEVENTS
-	select GENERIC_GPIO
 	select HAVE_CLK
 	select HAVE_S3C2410_I2C if I2C
 	select HAVE_S3C2410_WATCHDOG if WATCHDOG
@@ -873,7 +866,6 @@ config ARCH_U300
 	select COMMON_CLK
 	select CPU_ARM926T
 	select GENERIC_CLOCKEVENTS
-	select GENERIC_GPIO
 	select HAVE_TCM
 	select SPARSE_IRQ
 	help
@@ -957,7 +949,6 @@ config ARCH_VT8500_SINGLE
 	select COMMON_CLK
 	select CPU_ARM926T
 	select GENERIC_CLOCKEVENTS
-	select GENERIC_GPIO
 	select HAVE_CLK
 	select MULTI_IRQ_HANDLER
 	select SPARSE_IRQ
diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig
index 48705c1..bf02471 100644
--- a/arch/arm/mach-bcm/Kconfig
+++ b/arch/arm/mach-bcm/Kconfig
@@ -7,7 +7,6 @@ config ARCH_BCM
 	select ARM_GIC
 	select CPU_V7
 	select GENERIC_CLOCKEVENTS
-	select GENERIC_GPIO
 	select GENERIC_TIME
 	select GPIO_BCM
 	select SPARSE_IRQ
diff --git a/arch/arm/mach-picoxcell/Kconfig b/arch/arm/mach-picoxcell/Kconfig
index 868796f..13bae78 100644
--- a/arch/arm/mach-picoxcell/Kconfig
+++ b/arch/arm/mach-picoxcell/Kconfig
@@ -7,7 +7,6 @@ config ARCH_PICOXCELL
 	select DW_APB_TIMER
 	select DW_APB_TIMER_OF
 	select GENERIC_CLOCKEVENTS
-	select GENERIC_GPIO
 	select HAVE_TCM
 	select NO_IOPORT
 	select SPARSE_IRQ
diff --git a/arch/arm/mach-vt8500/Kconfig b/arch/arm/mach-vt8500/Kconfig
index 2ed0b7d..8464497 100644
--- a/arch/arm/mach-vt8500/Kconfig
+++ b/arch/arm/mach-vt8500/Kconfig
@@ -6,7 +6,6 @@ config ARCH_VT8500
 	select CLKDEV_LOOKUP
 	select CPU_ARM926T
 	select GENERIC_CLOCKEVENTS
-	select GENERIC_GPIO
 	select HAVE_CLK
 	help
 	  Support for VIA/WonderMedia VT8500/WM85xx System-on-Chip.
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH v7 1/3] ARM: make cr_alignment read-only #ifndef CONFIG_CPU_CP15
From: Jonathan Austin @ 2013-01-15 13:08 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1350462872-16805-2-git-send-email-u.kleine-koenig@pengutronix.de>

Hi Uwe,

On 17/10/12 09:34, Uwe Kleine-K?nig wrote:
> This makes cr_alignment a constant 0 to break code that tries to modify
> the value as it's likely that it's built on wrong assumption when
> CONFIG_CPU_CP15 isn't defined. For code that is only reading the value 0
> is more or less a fine value to report.
>

Without the context of some of the discussion that was had on the list 
about how/why to do this, this description is a bit confusing...

I found myself asking "Why do we not #ifdef out uses of cr_alignment 
based on CONFIG_CPU_CP15" - a question which it seems is answered by you 
and Nicolas here:

http://lists.infradead.org/pipermail/linux-arm-kernel/2012-March/089968.html

So, perhaps it would help to have a little bit more context in this 
commit message?

I know that the cr_alignment stuff is currently tied up with 
CONFIG_CPU_15, but I wonder if you looked at using the CCR.UNALIGN_TRP 
bit and wiring that in to alignment trapping code? Is it something you 
think would make sense for the work you're doing?

Also, see a couple of minor style/commenting points below...

> Signed-off-by: Uwe Kleine-K?nig <u.kleine-koenig@pengutronix.de>
> ---
> unchanged since v6
>   arch/arm/include/asm/cp15.h   |   11 ++++++++++-
>   arch/arm/kernel/head-common.S |    9 +++++++--
>   arch/arm/mm/alignment.c       |    2 ++
>   arch/arm/mm/mmu.c             |   17 +++++++++++++++++
>   4 files changed, 36 insertions(+), 3 deletions(-)
>
> diff --git a/arch/arm/include/asm/cp15.h b/arch/arm/include/asm/cp15.h
> index 5ef4d80..d814435 100644
> --- a/arch/arm/include/asm/cp15.h
> +++ b/arch/arm/include/asm/cp15.h
> @@ -42,6 +42,8 @@
>   #define vectors_high()	(0)
>   #endif
>
> +#ifdef CONFIG_CPU_CP15
> +
>   extern unsigned long cr_no_alignment;	/* defined in entry-armv.S */
>   extern unsigned long cr_alignment;	/* defined in entry-armv.S */
>
> @@ -82,6 +84,13 @@ static inline void set_copro_access(unsigned int val)
>   	isb();
>   }
>
> -#endif
> +#else /* ifdef CONFIG_CPU_CP15 */
> +
> +#define cr_no_alignment	UL(0)
> +#define cr_alignment	UL(0)
> +
> +#endif /* ifdef CONFIG_CPU_CP15 / else */
> +
> +#endif /* ifndef __ASSEMBLY__ */
>
>   #endif
> diff --git a/arch/arm/kernel/head-common.S b/arch/arm/kernel/head-common.S
> index 854bd22..2f560c5 100644
> --- a/arch/arm/kernel/head-common.S
> +++ b/arch/arm/kernel/head-common.S
> @@ -98,8 +98,9 @@ __mmap_switched:
>   	str	r9, [r4]			@ Save processor ID
>   	str	r1, [r5]			@ Save machine type
>   	str	r2, [r6]			@ Save atags pointer
> -	bic	r4, r0, #CR_A			@ Clear 'A' bit
> -	stmia	r7, {r0, r4}			@ Save control register values
> +	cmp	r7, #0
> +	bicne	r4, r0, #CR_A			@ Clear 'A' bit
> +	stmneia	r7, {r0, r4}			@ Save control register values
>   	b	start_kernel
>   ENDPROC(__mmap_switched)
>
> @@ -113,7 +114,11 @@ __mmap_switched_data:
>   	.long	processor_id			@ r4
>   	.long	__machine_arch_type		@ r5
>   	.long	__atags_pointer			@ r6
> +#ifdef CONFIG_CPU_CP15
>   	.long	cr_alignment			@ r7
> +#else
> +	.long	0

This value still gets loaded in to r7, so it might be worth keeping the 
comments going... They certainly help when reading the code.

> +#endif
>   	.long	init_thread_union + THREAD_START_SP @ sp
>   	.size	__mmap_switched_data, . - __mmap_switched_data
>
> diff --git a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c
> index b9f60eb..5748094 100644
> --- a/arch/arm/mm/alignment.c
> +++ b/arch/arm/mm/alignment.c
> @@ -962,12 +962,14 @@ static int __init alignment_init(void)
>   		return -ENOMEM;
>   #endif
>
> +#ifdef CONFIG_CPU_CP15
>   	if (cpu_is_v6_unaligned()) {
>   		cr_alignment &= ~CR_A;
>   		cr_no_alignment &= ~CR_A;
>   		set_cr(cr_alignment);
>   		ai_usermode = safe_usermode(ai_usermode, false);
>   	}
> +#endif
>
>   	hook_fault_code(FAULT_CODE_ALIGNMENT, do_alignment, SIGBUS, BUS_ADRALN,
>   			"alignment exception");
> diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
> index 941dfb9..b675918 100644
> --- a/arch/arm/mm/mmu.c
> +++ b/arch/arm/mm/mmu.c
> @@ -97,6 +97,7 @@ static struct cachepolicy cache_policies[] __initdata = {
>   	}
>   };
>
> +#ifdef CONFIG_CPU_CP15
>   /*
>    * These are useful for identifying cache coherency
>    * problems by allowing the cache or the cache and
> @@ -195,6 +196,22 @@ void adjust_cr(unsigned long mask, unsigned long set)
>   }
>   #endif
>
> +#else

When you read this file in its complete form there's a lot of code 
(including more preprocessor stuff) between the #ifdef and the #else.

Could you add

#else /* ifdef CONFIG_CPU_CP15 */

like you have in the other cases?

Jonny

^ permalink raw reply

* [PATCH 2/6] arm: mvebu: Enable USB controllers on Armada 370 evaluation board
From: Arnd Bergmann @ 2013-01-15 13:07 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358243670-11935-2-git-send-email-ezequiel.garcia@free-electrons.com>

On Tuesday 15 January 2013, Ezequiel Garcia wrote:
> Cc: Lior Amsalem <alior@marvell.com>
> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> Cc: Gregory CLEMENT <gregory.clement@free-electrons.com>
> Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>

The patches look good, but when you have four trivial patches
doing the same thing in different files, and you decide that it's
not worth writing a changelog for them, they should probably
go into a single patch.

	Arnd

^ permalink raw reply

* [PATCH 12/26] mfd: ab8500-debugfs: Use NULL to initialise remaining NULL pointer
From: Mark Brown @ 2013-01-15 13:06 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358254566-12419-13-git-send-email-lee.jones@linaro.org>

On Tue, Jan 15, 2013 at 12:55:52PM +0000, Lee Jones wrote:
> Partly for coding style reasons, but mostly because sparse warns on it.
> 
> This patch is a completion of a previous patch my Mark Brown.

Typo there :)

Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>

^ permalink raw reply

* [PATCH 5/5] ARM: exynos: enable/disable cpuidle when cpu1 is down/up
From: Daniel Lezcano @ 2013-01-15 12:56 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CADGdYn4eRbkZt_Rshqg-a_CoAmF5LBobzT3=3h09syaH0z6uJA@mail.gmail.com>

On 01/10/2013 11:33 PM, amit daniel kachhap wrote:
> On Thu, Jan 10, 2013 at 1:32 PM, Daniel Lezcano <daniel.lezcano@free.fr> wrote:
>> On 01/10/2013 09:07 PM, amit daniel kachhap wrote:
>>> Hi Daniel,
>>
>> Hi Amit Daniel,
>>
>>> This hotplug noifiers looks fine. I suppose it should add extra state
>>> C1 in cpu0. If it is done like below than for normal cases(when all
>>> cpu's are online) there wont be any statistics for C0 state
>>
>> I guess you meant state 0 which is WFI, right ?
>> C0 state is the intel semantic for cpu fully turned on.
> Yes I meant C0 as wfi
>>
>>> also which
>>> is required. Other patches look good.
>>
>> Ok, that makes sense to have statistics even if they are only doing WFI.
>>
>> Then the patch 4/5 is not ok, no ?
> yes I suppose patch 4 and patch 5 are related and depends how you
> frame patch 5. I think it is better to create C0/C1 sysfs and other
> things in the beginning because it is a filesystem call and may
> increase the cpu hotplug time which is not worth. May be if cpuidle
> framework exposes some API to enable/disable states then it is better.
> 
> For patch 1,2 and 3,
> Acked-by: Amit Daniel Kachhap <amit.daniel@samsung.com>

Hi Kukjin,

is it possible to take these patches [1-3/5] ?

The patches [3-4/5] could be ignored.

Thanks
  -- Daniel

-- 
 <http://www.linaro.org/> Linaro.org ? Open source software for ARM SoCs

Follow Linaro:  <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog

^ permalink raw reply

* [PATCH 26/26] mfd: ab8500-gpadc: Use new ab8500_gpadc_get() with name parameter
From: Lee Jones @ 2013-01-15 12:56 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358254566-12419-1-git-send-email-lee.jones@linaro.org>

From: Philippe Langlais <philippe.langlais@linaro.org>

The new format of ab8500_gpadc_get() accepts a device name as a
parameter to specify which device to retrieve. This patch
enforces the use of that new format.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Philippe Langlais <philippe.langlais@linaro.org>
---
 drivers/mfd/ab8500-debugfs.c |   26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c
index 4147984..f7e6d11 100644
--- a/drivers/mfd/ab8500-debugfs.c
+++ b/drivers/mfd/ab8500-debugfs.c
@@ -806,7 +806,7 @@ static int ab8500_gpadc_bat_ctrl_print(struct seq_file *s, void *p)
 	int bat_ctrl_convert;
 	struct ab8500_gpadc *gpadc;
 
-	gpadc = ab8500_gpadc_get();
+	gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
 	bat_ctrl_raw = ab8500_gpadc_read_raw(gpadc, BAT_CTRL);
 	bat_ctrl_convert = ab8500_gpadc_ad_to_voltage(gpadc,
 			BAT_CTRL, bat_ctrl_raw);
@@ -834,7 +834,7 @@ static int ab8500_gpadc_btemp_ball_print(struct seq_file *s, void *p)
 	int btemp_ball_convert;
 	struct ab8500_gpadc *gpadc;
 
-	gpadc = ab8500_gpadc_get();
+	gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
 	btemp_ball_raw = ab8500_gpadc_read_raw(gpadc, BTEMP_BALL);
 	btemp_ball_convert = ab8500_gpadc_ad_to_voltage(gpadc, BTEMP_BALL,
 			btemp_ball_raw);
@@ -863,7 +863,7 @@ static int ab8500_gpadc_main_charger_v_print(struct seq_file *s, void *p)
 	int main_charger_v_convert;
 	struct ab8500_gpadc *gpadc;
 
-	gpadc = ab8500_gpadc_get();
+	gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
 	main_charger_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_V);
 	main_charger_v_convert = ab8500_gpadc_ad_to_voltage(gpadc,
 			MAIN_CHARGER_V, main_charger_v_raw);
@@ -893,7 +893,7 @@ static int ab8500_gpadc_acc_detect1_print(struct seq_file *s, void *p)
 	int acc_detect1_convert;
 	struct ab8500_gpadc *gpadc;
 
-	gpadc = ab8500_gpadc_get();
+	gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
 	acc_detect1_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT1);
 	acc_detect1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ACC_DETECT1,
 			acc_detect1_raw);
@@ -923,7 +923,7 @@ static int ab8500_gpadc_acc_detect2_print(struct seq_file *s, void *p)
 	int acc_detect2_convert;
 	struct ab8500_gpadc *gpadc;
 
-	gpadc = ab8500_gpadc_get();
+	gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
 	acc_detect2_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT2);
 	acc_detect2_convert = ab8500_gpadc_ad_to_voltage(gpadc,
 	    ACC_DETECT2, acc_detect2_raw);
@@ -953,7 +953,7 @@ static int ab8500_gpadc_aux1_print(struct seq_file *s, void *p)
 	int aux1_convert;
 	struct ab8500_gpadc *gpadc;
 
-	gpadc = ab8500_gpadc_get();
+	gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
 	aux1_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX1);
 	aux1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX1,
 			aux1_raw);
@@ -981,7 +981,7 @@ static int ab8500_gpadc_aux2_print(struct seq_file *s, void *p)
 	int aux2_convert;
 	struct ab8500_gpadc *gpadc;
 
-	gpadc = ab8500_gpadc_get();
+	gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
 	aux2_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX2);
 	aux2_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX2,
 			aux2_raw);
@@ -1009,7 +1009,7 @@ static int ab8500_gpadc_main_bat_v_print(struct seq_file *s, void *p)
 	int main_bat_v_convert;
 	struct ab8500_gpadc *gpadc;
 
-	gpadc = ab8500_gpadc_get();
+	gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
 	main_bat_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_BAT_V);
 	main_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, MAIN_BAT_V,
 			main_bat_v_raw);
@@ -1038,7 +1038,7 @@ static int ab8500_gpadc_vbus_v_print(struct seq_file *s, void *p)
 	int vbus_v_convert;
 	struct ab8500_gpadc *gpadc;
 
-	gpadc = ab8500_gpadc_get();
+	gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
 	vbus_v_raw = ab8500_gpadc_read_raw(gpadc, VBUS_V);
 	vbus_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, VBUS_V,
 			vbus_v_raw);
@@ -1066,7 +1066,7 @@ static int ab8500_gpadc_main_charger_c_print(struct seq_file *s, void *p)
 	int main_charger_c_convert;
 	struct ab8500_gpadc *gpadc;
 
-	gpadc = ab8500_gpadc_get();
+	gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
 	main_charger_c_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_C);
 	main_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc,
 			MAIN_CHARGER_C, main_charger_c_raw);
@@ -1096,7 +1096,7 @@ static int ab8500_gpadc_usb_charger_c_print(struct seq_file *s, void *p)
 	int usb_charger_c_convert;
 	struct ab8500_gpadc *gpadc;
 
-	gpadc = ab8500_gpadc_get();
+	gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
 	usb_charger_c_raw = ab8500_gpadc_read_raw(gpadc, USB_CHARGER_C);
 	usb_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc,
 	    USB_CHARGER_C, usb_charger_c_raw);
@@ -1126,7 +1126,7 @@ static int ab8500_gpadc_bk_bat_v_print(struct seq_file *s, void *p)
 	int bk_bat_v_convert;
 	struct ab8500_gpadc *gpadc;
 
-	gpadc = ab8500_gpadc_get();
+	gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
 	bk_bat_v_raw = ab8500_gpadc_read_raw(gpadc, BK_BAT_V);
 	bk_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc,
 			BK_BAT_V, bk_bat_v_raw);
@@ -1154,7 +1154,7 @@ static int ab8500_gpadc_die_temp_print(struct seq_file *s, void *p)
 	int die_temp_convert;
 	struct ab8500_gpadc *gpadc;
 
-	gpadc = ab8500_gpadc_get();
+	gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
 	die_temp_raw = ab8500_gpadc_read_raw(gpadc, DIE_TEMP);
 	die_temp_convert = ab8500_gpadc_ad_to_voltage(gpadc, DIE_TEMP,
 			die_temp_raw);
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 25/26] mfd: ab8500-gpadc: Reduce conversion timeout
From: Lee Jones @ 2013-01-15 12:56 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358254566-12419-1-git-send-email-lee.jones@linaro.org>

From: Jonas Aaberg <jonas.aberg@stericsson.com>

Reduce the conversion timeout from 2s to 0.5s

Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com>
Reviewed-by: Karl KOMIEROWSKI <karl.komierowski@stericsson.com>
---
 drivers/mfd/ab8500-gpadc.c |    7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c
index 1fd8f78..a7a6c77 100644
--- a/drivers/mfd/ab8500-gpadc.c
+++ b/drivers/mfd/ab8500-gpadc.c
@@ -86,6 +86,8 @@
 /* Time in ms before disabling regulator */
 #define GPADC_AUDOSUSPEND_DELAY		1
 
+#define CONVERSION_TIME			500 /* ms */
+
 enum cal_channels {
 	ADC_INPUT_VMAIN = 0,
 	ADC_INPUT_BTEMP,
@@ -372,7 +374,8 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
 		goto out;
 	}
 	/* wait for completion of conversion */
-	if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete, 2*HZ)) {
+	if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete,
+					 msecs_to_jiffies(CONVERSION_TIME))) {
 		dev_err(gpadc->dev,
 			"timeout: didn't receive GPADC conversion interrupt\n");
 		ret = -EINVAL;
@@ -598,8 +601,6 @@ static int ab8500_gpadc_runtime_resume(struct device *dev)
 
 static int ab8500_gpadc_runtime_idle(struct device *dev)
 {
-	struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
-
 	pm_runtime_suspend(dev);
 	return 0;
 }
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 24/26] mfd: ab8500-debugfs: sizeof() mismatch bugfix
From: Lee Jones @ 2013-01-15 12:56 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358254566-12419-1-git-send-email-lee.jones@linaro.org>

From: Ashok G <ashok.g@stericsson.com>

Simple pointer error fix to obtain the expected sizeof() result.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Ashok G <ashok.g@stericsson.com>
Reviewed-by: Mattias WALLIN <mattias.wallin@stericsson.com>
---
 drivers/mfd/ab8500-debugfs.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c
index 8e405af..4147984 100644
--- a/drivers/mfd/ab8500-debugfs.c
+++ b/drivers/mfd/ab8500-debugfs.c
@@ -1565,7 +1565,7 @@ static int __devinit ab8500_debug_probe(struct platform_device *plf)
 	ab8500 = dev_get_drvdata(plf->dev.parent);
 	num_irqs = ab8500->mask_size;
 
-	irq_count = kzalloc(sizeof(irq_count)*num_irqs, GFP_KERNEL);
+	irq_count = kzalloc(sizeof(*irq_count)*num_irqs, GFP_KERNEL);
 	if (!irq_count)
 		return -ENOMEM;
 
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 23/26] mfd: ab8500-pwm: Enable support for AB8505 PWMLED blink
From: Lee Jones @ 2013-01-15 12:56 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358254566-12419-1-git-send-email-lee.jones@linaro.org>

From: Naga Radhesh <naga.radheshy@stericsson.com>

Enable support for PWM OUT LED blinking for AB8505. Instead of
having 3 pwm instances from ab8500 core file add it as platform data.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Naga Radhesh <naga.radheshy@stericsson.com>
Reviewed-by: Arun MURTHY <arun.murthy@stericsson.com>
Reviewed-by: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com>
---
 drivers/mfd/ab8500-core.c          |   10 --
 drivers/misc/ab8500-pwm.c          |  282 ++++++++++++++++++++++++++++++++++++
 include/linux/mfd/ab8500/pwmleds.h |   20 +++
 include/linux/mfd/abx500/ab8500.h  |    2 +
 4 files changed, 304 insertions(+), 10 deletions(-)
 create mode 100644 drivers/misc/ab8500-pwm.c
 create mode 100644 include/linux/mfd/ab8500/pwmleds.h

diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index 580adf3..c758469 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -1015,16 +1015,6 @@ static struct mfd_cell __devinitdata abx500_common_devs[] = {
 		.id = 1,
 	},
 	{
-		.name = "ab8500-pwm",
-		.of_compatible = "stericsson,ab8500-pwm",
-		.id = 2,
-	},
-	{
-		.name = "ab8500-pwm",
-		.of_compatible = "stericsson,ab8500-pwm",
-		.id = 3,
-	},
-	{
 		.name = "ab8500-leds",
 		.of_compatible = "stericsson,ab8500-leds",
 	},
diff --git a/drivers/misc/ab8500-pwm.c b/drivers/misc/ab8500-pwm.c
new file mode 100644
index 0000000..d7a6276
--- /dev/null
+++ b/drivers/misc/ab8500-pwm.c
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Arun R Murthy <arun.murthy@stericsson.com>
+ * License terms: GNU General Public License (GPL) version 2
+ */
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/pwm.h>
+#include <linux/clk.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/module.h>
+#include <linux/mfd/ab8500/pwmleds.h>
+/*
+ * PWM Out generators
+ * Bank: 0x10
+ */
+#define AB8500_PWM_OUT_CTRL1_REG	0x60
+#define AB8500_PWM_OUT_CTRL2_REG	0x61
+#define AB8500_PWM_OUT_CTRL7_REG	0x66
+#define AB8505_PWM_OUT_BLINK_CTRL1_REG  0x68
+#define AB8505_PWM_OUT_BLINK_CTRL4_REG  0x6B
+#define AB8505_PWM_OUT_BLINK_CTRL_DUTYBIT 4
+#define AB8505_PWM_OUT_BLINK_DUTYMASK (0x0F << AB8505_PWM_OUT_BLINK_CTRL_DUTYBIT)
+
+
+/* backlight driver constants */
+#define ENABLE_PWM			1
+#define DISABLE_PWM			0
+
+struct pwm_device {
+	struct device *dev;
+	struct list_head node;
+	struct clk *clk;
+	const char *label;
+	unsigned int pwm_id;
+	unsigned int blink_en;
+	struct ab8500 *parent;
+	bool clk_enabled;
+};
+
+static LIST_HEAD(pwm_list);
+
+int pwm_config_blink(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+	int ret;
+	unsigned int value;
+	u8 reg;
+	if ((!is_ab8505(pwm->parent)) || (!pwm->blink_en)) {
+		dev_err(pwm->dev, "setting blinking for this "
+					"device not supported\n");
+		return -EINVAL;
+	}
+	/*
+	 * get the period value that is to be written to
+	 * AB8500_PWM_OUT_BLINK_CTRL1 REGS[0:2]
+	 */
+	value = period_ns & 0x07;
+	/*
+	 * get blink duty value to be written to
+	 * AB8500_PWM_OUT_BLINK_CTRL REGS[7:4]
+	 */
+	value |= ((duty_ns << AB8505_PWM_OUT_BLINK_CTRL_DUTYBIT) &
+					AB8505_PWM_OUT_BLINK_DUTYMASK);
+
+	reg = AB8505_PWM_OUT_BLINK_CTRL1_REG + (pwm->pwm_id - 1);
+
+	ret = abx500_set_register_interruptible(pwm->dev, AB8500_MISC,
+			reg, (u8)value);
+	if (ret < 0)
+		dev_err(pwm->dev, "%s: Failed to config PWM blink,Error %d\n",
+							pwm->label, ret);
+	return ret;
+}
+
+int pwm_blink_ctrl(struct pwm_device *pwm , int enable)
+{
+	int ret;
+
+	if ((!is_ab8505(pwm->parent)) || (!pwm->blink_en)) {
+		dev_err(pwm->dev, "setting blinking for this "
+					"device not supported\n");
+		return -EINVAL;
+	}
+	/*
+	 * Enable/disable blinking feature for corresponding PWMOUT
+	 * channel depending on value of enable.
+	 */
+	ret = abx500_mask_and_set_register_interruptible(pwm->dev,
+			AB8500_MISC, AB8505_PWM_OUT_BLINK_CTRL4_REG,
+			1 << (pwm->pwm_id-1), enable << (pwm->pwm_id-1));
+	if (ret < 0)
+		dev_err(pwm->dev, "%s: Failed to control PWM blink,Error %d\n",
+							pwm->label, ret);
+	return ret;
+}
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+	int ret = 0;
+	unsigned int higher_val, lower_val;
+	u8 reg;
+
+	/*
+	 * get the first 8 bits that are be written to
+	 * AB8500_PWM_OUT_CTRL1_REG[0:7]
+	 */
+	lower_val = duty_ns & 0x00FF;
+	/*
+	 * get bits [9:10] that are to be written to
+	 * AB8500_PWM_OUT_CTRL2_REG[0:1]
+	 */
+	higher_val = ((duty_ns & 0x0300) >> 8);
+
+	reg = AB8500_PWM_OUT_CTRL1_REG + ((pwm->pwm_id - 1) * 2);
+
+	ret = abx500_set_register_interruptible(pwm->dev, AB8500_MISC,
+			reg, (u8)lower_val);
+	if (ret < 0)
+		return ret;
+	ret = abx500_set_register_interruptible(pwm->dev, AB8500_MISC,
+			(reg + 1), (u8)higher_val);
+
+	return ret;
+}
+EXPORT_SYMBOL(pwm_config);
+
+int pwm_enable(struct pwm_device *pwm)
+{
+	int ret;
+
+	if (!pwm->clk_enabled) {
+		ret = clk_enable(pwm->clk);
+		if (ret < 0) {
+			dev_err(pwm->dev, "failed to enable clock\n");
+			return ret;
+		}
+		pwm->clk_enabled = true;
+	}
+	ret = abx500_mask_and_set_register_interruptible(pwm->dev,
+				AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG,
+				1 << (pwm->pwm_id-1), 1 << (pwm->pwm_id-1));
+	if (ret < 0)
+		dev_err(pwm->dev, "%s: Failed to enable PWM, Error %d\n",
+							pwm->label, ret);
+	return ret;
+}
+EXPORT_SYMBOL(pwm_enable);
+
+void pwm_disable(struct pwm_device *pwm)
+{
+	int ret;
+
+	ret = abx500_mask_and_set_register_interruptible(pwm->dev,
+				AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG,
+				1 << (pwm->pwm_id-1), DISABLE_PWM);
+	/*
+	 * Workaround to set PWM in disable.
+	 * If enable bit is not toggled the PWM might output 50/50 duty cycle
+	 * even though it should be disabled
+	 */
+	ret &= abx500_mask_and_set_register_interruptible(pwm->dev,
+				AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG,
+				1 << (pwm->pwm_id-1),
+				ENABLE_PWM << (pwm->pwm_id-1));
+	ret &= abx500_mask_and_set_register_interruptible(pwm->dev,
+				AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG,
+				1 << (pwm->pwm_id-1), DISABLE_PWM);
+
+	if (ret < 0)
+		dev_err(pwm->dev, "%s: Failed to disable PWM, Error %d\n",
+							pwm->label, ret);
+	if (pwm->clk_enabled) {
+		clk_disable(pwm->clk);
+		pwm->clk_enabled = false;
+	}
+
+	return;
+}
+EXPORT_SYMBOL(pwm_disable);
+
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+	struct pwm_device *pwm;
+	list_for_each_entry(pwm, &pwm_list, node) {
+		if (pwm->pwm_id == pwm_id) {
+			pwm->label = label;
+			pwm->pwm_id = pwm_id;
+			return pwm;
+		}
+	}
+
+	return ERR_PTR(-ENOENT);
+}
+EXPORT_SYMBOL(pwm_request);
+
+void pwm_free(struct pwm_device *pwm)
+{
+	pwm_disable(pwm);
+}
+EXPORT_SYMBOL(pwm_free);
+
+static int __devinit ab8500_pwm_probe(struct platform_device *pdev)
+{
+	struct ab8500 *parent = dev_get_drvdata(pdev->dev.parent);
+	struct ab8500_platform_data *plat = dev_get_platdata(parent->dev);
+	struct ab8500_pwmled_platform_data *pdata = plat->pwmled;
+	struct pwm_device *pwm;
+	int ret = 0 , i;
+
+	/*
+	 * Nothing to be done in probe, this is required to get the
+	 * device which is required for ab8500 read and write
+	 */
+	pwm = kzalloc(((sizeof(struct pwm_device)) * pdata->num_pwm),
+						 GFP_KERNEL);
+	if (pwm == NULL) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		return -ENOMEM;
+	}
+	for (i = 0; i < pdata->num_pwm; i++) {
+		pwm[i].dev = &pdev->dev;
+		pwm[i].parent = parent;
+		pwm[i].blink_en = pdata->leds[i].blink_en;
+		pwm[i].pwm_id = pdata->leds[i].pwm_id;
+		list_add_tail(&pwm[i].node, &pwm_list);
+	}
+	pwm->clk = clk_get(pwm->dev, NULL);
+	if (IS_ERR(pwm->clk)) {
+		dev_err(pwm->dev, "clock request failed\n");
+		ret = PTR_ERR(pwm->clk);
+		goto fail;
+	}
+	platform_set_drvdata(pdev, pwm);
+	pwm->clk_enabled = false;
+	dev_dbg(pwm->dev, "pwm probe successful\n");
+	return ret;
+
+fail:
+	list_del(&pwm->node);
+	kfree(pwm);
+	return ret;
+}
+
+static int __devexit ab8500_pwm_remove(struct platform_device *pdev)
+{
+	struct pwm_device *pwm = platform_get_drvdata(pdev);
+
+	list_del(&pwm->node);
+	clk_put(pwm->clk);
+	dev_dbg(&pdev->dev, "pwm driver removed\n");
+	kfree(pwm);
+	return 0;
+}
+
+static struct platform_driver ab8500_pwm_driver = {
+	.driver = {
+		.name = "ab8500-pwm",
+		.owner = THIS_MODULE,
+	},
+	.probe = ab8500_pwm_probe,
+	.remove = __devexit_p(ab8500_pwm_remove),
+};
+
+static int __init ab8500_pwm_init(void)
+{
+	return platform_driver_register(&ab8500_pwm_driver);
+}
+
+static void __exit ab8500_pwm_exit(void)
+{
+	platform_driver_unregister(&ab8500_pwm_driver);
+}
+
+subsys_initcall(ab8500_pwm_init);
+module_exit(ab8500_pwm_exit);
+MODULE_AUTHOR("Arun MURTHY <arun.murthy@stericsson.com>");
+MODULE_DESCRIPTION("AB8500 Pulse Width Modulation Driver");
+MODULE_ALIAS("platform:ab8500-pwm");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/ab8500/pwmleds.h b/include/linux/mfd/ab8500/pwmleds.h
new file mode 100644
index 0000000..e316582
--- /dev/null
+++ b/include/linux/mfd/ab8500/pwmleds.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright ST-Ericsson 2012.
+ *
+ * Author: Naga Radhesh <naga.radheshy@stericsson.com>
+ * Licensed under GPLv2.
+ */
+#ifndef _AB8500_PWMLED_H
+#define _AB8500_PWMLED_H
+
+struct ab8500_led_pwm {
+	int	pwm_id;
+	int	blink_en;
+};
+
+struct ab8500_pwmled_platform_data {
+	int	num_pwm;
+	struct	ab8500_led_pwm *leds;
+};
+
+#endif
diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h
index 83de910..62cbe65 100644
--- a/include/linux/mfd/abx500/ab8500.h
+++ b/include/linux/mfd/abx500/ab8500.h
@@ -281,6 +281,7 @@ struct ab8500_sysctrl_platform_data;
  * @regulator_reg_init: regulator init registers
  * @num_regulator: number of regulators
  * @regulator: machine-specific constraints for regulators
+ * @pwmled: machine-specific pwmled data
  */
 struct ab8500_platform_data {
 	int irq_base;
@@ -293,6 +294,7 @@ struct ab8500_platform_data {
 	struct ab8500_gpio_platform_data *gpio;
 	struct ab8500_codec_platform_data *codec;
 	struct ab8500_sysctrl_platform_data *sysctrl;
+	struct ab8500_pwmled_platform_data *pwmled;
 };
 
 extern int __devinit ab8500_init(struct ab8500 *ab8500,
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 22/26] mfd: ab8500-gpadc: Add runtime pm support
From: Lee Jones @ 2013-01-15 12:56 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358254566-12419-1-git-send-email-lee.jones@linaro.org>

From: Jonas Aaberg <jonas.aberg@stericsson.com>

Add runtime pm support to speed up multiple ADC reads in a row.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com>
Reviewed-by: Ulf HANSSON <ulf.hansson@stericsson.com>
---
 drivers/mfd/ab8500-gpadc.c |   70 ++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 64 insertions(+), 6 deletions(-)

diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c
index 6f97c599..1fd8f78 100644
--- a/drivers/mfd/ab8500-gpadc.c
+++ b/drivers/mfd/ab8500-gpadc.c
@@ -12,6 +12,7 @@
 #include <linux/interrupt.h>
 #include <linux/spinlock.h>
 #include <linux/delay.h>
+#include <linux/pm_runtime.h>
 #include <linux/platform_device.h>
 #include <linux/completion.h>
 #include <linux/regulator/consumer.h>
@@ -82,6 +83,9 @@
 /* This is used to not lose precision when dividing to get gain and offset */
 #define CALIB_SCALE			1000
 
+/* Time in ms before disabling regulator */
+#define GPADC_AUDOSUSPEND_DELAY		1
+
 enum cal_channels {
 	ADC_INPUT_VMAIN = 0,
 	ADC_INPUT_BTEMP,
@@ -282,8 +286,9 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
 		return -ENODEV;
 
 	mutex_lock(&gpadc->ab8500_gpadc_lock);
+
 	/* Enable VTVout LDO this is required for GPADC */
-	regulator_enable(gpadc->regu);
+	pm_runtime_get_sync(gpadc->dev);
 
 	/* Check if ADC is not busy, lock and proceed */
 	do {
@@ -397,8 +402,10 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
 		dev_err(gpadc->dev, "gpadc_conversion: disable gpadc failed\n");
 		goto out;
 	}
-	/* Disable VTVout LDO this is required for GPADC */
-	regulator_disable(gpadc->regu);
+
+	pm_runtime_mark_last_busy(gpadc->dev);
+	pm_runtime_put_autosuspend(gpadc->dev);
+
 	mutex_unlock(&gpadc->ab8500_gpadc_lock);
 
 	return (high_data << 8) | low_data;
@@ -412,7 +419,9 @@ out:
 	 */
 	(void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
 		AB8500_GPADC_CTRL1_REG, DIS_GPADC);
-	regulator_disable(gpadc->regu);
+
+	pm_runtime_put(gpadc->dev);
+
 	mutex_unlock(&gpadc->ab8500_gpadc_lock);
 	dev_err(gpadc->dev,
 		"gpadc_conversion: Failed to AD convert channel %d\n", channel);
@@ -571,6 +580,30 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
 		gpadc->cal_data[ADC_INPUT_VBAT].offset);
 }
 
+static int ab8500_gpadc_runtime_suspend(struct device *dev)
+{
+	struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
+
+	regulator_disable(gpadc->regu);
+	return 0;
+}
+
+static int ab8500_gpadc_runtime_resume(struct device *dev)
+{
+	struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
+
+	regulator_enable(gpadc->regu);
+	return 0;
+}
+
+static int ab8500_gpadc_runtime_idle(struct device *dev)
+{
+	struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
+
+	pm_runtime_suspend(dev);
+	return 0;
+}
+
 static int __devinit ab8500_gpadc_probe(struct platform_device *pdev)
 {
 	int ret = 0;
@@ -615,6 +648,16 @@ static int __devinit ab8500_gpadc_probe(struct platform_device *pdev)
 		dev_err(gpadc->dev, "failed to get vtvout LDO\n");
 		goto fail_irq;
 	}
+
+	platform_set_drvdata(pdev, gpadc);
+
+	regulator_enable(gpadc->regu);
+
+	pm_runtime_set_autosuspend_delay(gpadc->dev, GPADC_AUDOSUSPEND_DELAY);
+	pm_runtime_use_autosuspend(gpadc->dev);
+	pm_runtime_set_active(gpadc->dev);
+	pm_runtime_enable(gpadc->dev);
+
 	ab8500_gpadc_read_calibration_data(gpadc);
 	list_add_tail(&gpadc->node, &ab8500_gpadc_list);
 	dev_dbg(gpadc->dev, "probe success\n");
@@ -635,19 +678,34 @@ static int __devexit ab8500_gpadc_remove(struct platform_device *pdev)
 	list_del(&gpadc->node);
 	/* remove interrupt  - completion of Sw ADC conversion */
 	free_irq(gpadc->irq, gpadc);
-	/* disable VTVout LDO that is being used by GPADC */
-	regulator_put(gpadc->regu);
+
+	pm_runtime_get_sync(gpadc->dev);
+	pm_runtime_disable(gpadc->dev);
+
+	regulator_disable(gpadc->regu);
+
+	pm_runtime_set_suspended(gpadc->dev);
+
+	pm_runtime_put_noidle(gpadc->dev);
+
 	kfree(gpadc);
 	gpadc = NULL;
 	return 0;
 }
 
+static const struct dev_pm_ops ab8500_gpadc_pm_ops = {
+	SET_RUNTIME_PM_OPS(ab8500_gpadc_runtime_suspend,
+			   ab8500_gpadc_runtime_resume,
+			   ab8500_gpadc_runtime_idle)
+};
+
 static struct platform_driver ab8500_gpadc_driver = {
 	.probe = ab8500_gpadc_probe,
 	.remove = __devexit_p(ab8500_gpadc_remove),
 	.driver = {
 		.name = "ab8500-gpadc",
 		.owner = THIS_MODULE,
+		.pm = &ab8500_gpadc_pm_ops,
 	},
 };
 
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 21/26] mfd: ab8500-debugfs: Add interrupt debug
From: Lee Jones @ 2013-01-15 12:56 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358254566-12419-1-git-send-email-lee.jones@linaro.org>

From: Bengt Jonsson <bengt.g.jonsson@stericsson.com>

This patch adds an entry in debugfs to check number of interrupts
from the AB.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Bengt Jonsson <bengt.g.jonsson@stericsson.com>
Reviewed-by: Rabin VINCENT <rabin.vincent@stericsson.com>
---
 drivers/mfd/ab8500-core.c         |    1 +
 drivers/mfd/ab8500-debugfs.c      |   49 +++++++++++++++++++++++++++++++++++++
 include/linux/mfd/abx500/ab8500.h |    2 ++
 3 files changed, 52 insertions(+)

diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index 0f84fc0..580adf3 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -521,6 +521,7 @@ static irqreturn_t ab8500_irq(int irq, void *dev)
 			int virq = ab8500_irq_get_virq(ab8500, line);
 
 			handle_nested_irq(virq);
+			ab8500_debug_register_interrupt(line);
 			value &= ~(1 << bit);
 
 		} while (value);
diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c
index d23b6cc..8e405af 100644
--- a/drivers/mfd/ab8500-debugfs.c
+++ b/drivers/mfd/ab8500-debugfs.c
@@ -737,6 +737,35 @@ static ssize_t ab8500_val_write(struct file *file,
 }
 
 /*
+ * Interrupt status
+ */
+static u32 num_interrupts[AB8500_MAX_NR_IRQS];
+static int num_interrupt_lines;
+
+void ab8500_debug_register_interrupt(int line)
+{
+	if (line < num_interrupt_lines)
+		num_interrupts[line]++;
+}
+
+static int ab8500_interrupts_print(struct seq_file *s, void *p)
+{
+	int line;
+
+	seq_printf(s, "irq:  number of\n");
+
+	for (line = 0; line < num_interrupt_lines; line++)
+		seq_printf(s, "%3i:  %6i\n", line, num_interrupts[line]);
+
+	return 0;
+}
+
+static int ab8500_interrupts_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ab8500_interrupts_print, inode->i_private);
+}
+
+/*
  * - HWREG DB8500 formated routines
  */
 static int ab8500_hwreg_print(struct seq_file *s, void *d)
@@ -1487,6 +1516,14 @@ static const struct file_operations ab8500_val_fops = {
 	.owner = THIS_MODULE,
 };
 
+static const struct file_operations ab8500_interrupts_fops = {
+	.open = ab8500_interrupts_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.owner = THIS_MODULE,
+};
+
 static const struct file_operations ab8500_subscribe_fops = {
 	.open = ab8500_subscribe_unsubscribe_open,
 	.write = ab8500_subscribe_write,
@@ -1595,6 +1632,18 @@ static int __devinit ab8500_debug_probe(struct platform_device *plf)
 	if (!file)
 		goto err;
 
+	if (is_ab8500(ab8500))
+		num_interrupt_lines = AB8500_NR_IRQS;
+	else if (is_ab8505(ab8500))
+		num_interrupt_lines = AB8505_NR_IRQS;
+	else if (is_ab9540(ab8500))
+		num_interrupt_lines = AB9540_NR_IRQS;
+
+	file = debugfs_create_file("interrupts", (S_IRUGO),
+	    ab8500_dir, &plf->dev, &ab8500_interrupts_fops);
+	if (!file)
+		goto err;
+
 	file = debugfs_create_file("irq-unsubscribe", (S_IRUGO | S_IWUSR),
 	    ab8500_dir, &plf->dev, &ab8500_unsubscribe_fops);
 	if (!file)
diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h
index 4c8d966..83de910 100644
--- a/include/linux/mfd/abx500/ab8500.h
+++ b/include/linux/mfd/abx500/ab8500.h
@@ -347,8 +347,10 @@ static inline int is_ab8500_2p0(struct ab8500 *ab)
 
 #ifdef CONFIG_AB8500_DEBUG
 void ab8500_dump_all_banks(struct device *dev);
+void ab8500_debug_register_interrupt(int line);
 #else
 static inline void ab8500_dump_all_banks(struct device *dev) {}
+static inline void ab8500_debug_register_interrupt(int line) {}
 #endif
 
 #endif /* MFD_AB8500_H */
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 20/26] mfd ab8500-gpadc: Introduce new AB version detection
From: Lee Jones @ 2013-01-15 12:56 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358254566-12419-1-git-send-email-lee.jones@linaro.org>

From: Michel JAOUEN <michel.jaouen@stericsson.com>

Add support for AB8505 and AB9540

Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Maxime Coquelin <maxime.coquelin@stericsson.com>
Signed-off-by: Bengt Jonsson <bengt.g.jonsson@stericsson.com>
Reviewed-by: Rabin VINCENT <rabin.vincent@stericsson.com>
---
 drivers/mfd/ab8500-gpadc.c |   15 ++++-----------
 1 file changed, 4 insertions(+), 11 deletions(-)

diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c
index 3883b93..6f97c599 100644
--- a/drivers/mfd/ab8500-gpadc.c
+++ b/drivers/mfd/ab8500-gpadc.c
@@ -102,10 +102,10 @@ struct adc_cal_data {
 
 /**
  * struct ab8500_gpadc - AB8500 GPADC device information
- * @chip_id			ABB chip id
  * @dev:			pointer to the struct device
  * @node:			a list of AB8500 GPADCs, hence prepared for
 				reentrance
+ * @parent:			pointer to the struct ab8500
  * @ab8500_gpadc_complete:	pointer to the struct completion, to indicate
  *				the completion of gpadc conversion
  * @ab8500_gpadc_lock:		structure of type mutex
@@ -114,9 +114,9 @@ struct adc_cal_data {
  * @cal_data			array of ADC calibration data structs
  */
 struct ab8500_gpadc {
-	u8 chip_id;
 	struct device *dev;
 	struct list_head node;
+	struct ab8500 *parent;
 	struct completion ab8500_gpadc_complete;
 	struct mutex ab8500_gpadc_lock;
 	struct regulator *regu;
@@ -332,7 +332,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
 			EN_BUF | EN_ICHAR);
 		break;
 	case BTEMP_BALL:
-		if (gpadc->chip_id >= AB8500_CUT3P0) {
+		if (!is_ab8500_2p0_or_earlier(gpadc->parent)) {
 			/* Turn on btemp pull-up on ABB 3.0 */
 			ret = abx500_mask_and_set_register_interruptible(
 				gpadc->dev,
@@ -591,6 +591,7 @@ static int __devinit ab8500_gpadc_probe(struct platform_device *pdev)
 	}
 
 	gpadc->dev = &pdev->dev;
+	gpadc->parent = dev_get_drvdata(pdev->dev.parent);
 	mutex_init(&gpadc->ab8500_gpadc_lock);
 
 	/* Initialize completion used to notify completion of conversion */
@@ -607,14 +608,6 @@ static int __devinit ab8500_gpadc_probe(struct platform_device *pdev)
 		goto fail;
 	}
 
-	/* Get Chip ID of the ABB ASIC  */
-	ret = abx500_get_chip_id(gpadc->dev);
-	if (ret < 0) {
-		dev_err(gpadc->dev, "failed to get chip ID\n");
-		goto fail_irq;
-	}
-	gpadc->chip_id = (u8) ret;
-
 	/* VTVout LDO used to power up ab8500-GPADC */
 	gpadc->regu = regulator_get(&pdev->dev, "vddadc");
 	if (IS_ERR(gpadc->regu)) {
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 19/26] mfd: ab8500-debugfs: Allow number of IRQs to be provided more dynamically
From: Lee Jones @ 2013-01-15 12:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358254566-12419-1-git-send-email-lee.jones@linaro.org>

From: Linus Walleij <linus.walleij@stericsson.com>

With the introduction of new AB* platforms, it's important to allow
as much code reuse as possible. By allowing a system's number of IRQs
to be dynamically passed, we can reuse almost all of the -debugfs
driver.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Linus Walleij <linus.walleij@stericsson.com>
---
 drivers/mfd/ab8500-debugfs.c |   51 +++++++++++++++++++++++++++++++++---------
 1 file changed, 41 insertions(+), 10 deletions(-)

diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c
index ec57e43..d23b6cc 100644
--- a/drivers/mfd/ab8500-debugfs.c
+++ b/drivers/mfd/ab8500-debugfs.c
@@ -94,10 +94,11 @@ static u32 debug_address;
 
 static int irq_first;
 static int irq_last;
-static u32 irq_count[AB8500_NR_IRQS];
+static u32 *irq_count;
+static int num_irqs;
 
-static struct device_attribute *dev_attr[AB8500_NR_IRQS];
-static char *event_name[AB8500_NR_IRQS];
+static struct device_attribute **dev_attr;
+static char **event_name;
 
 /**
  * struct ab8500_reg_range
@@ -483,7 +484,7 @@ static irqreturn_t ab8500_debug_handler(int irq, void *data)
 	struct kobject *kobj = (struct kobject *)data;
 	unsigned int irq_abb = irq - irq_first;
 
-	if (irq_abb < AB8500_NR_IRQS)
+	if (irq_abb < num_irqs)
 		irq_count[irq_abb]++;
 	/*
 	 * This makes it possible to use poll for events (POLLPRI | POLLERR)
@@ -1338,7 +1339,7 @@ static ssize_t show_irq(struct device *dev,
 		return err;
 
 	irq_index = name - irq_first;
-	if (irq_index >= AB8500_NR_IRQS)
+	if (irq_index >= num_irqs)
 		return -EINVAL;
 	else
 		return sprintf(buf, "%u\n", irq_count[irq_index]);
@@ -1374,7 +1375,7 @@ static ssize_t ab8500_subscribe_write(struct file *file,
 	}
 
 	irq_index = user_val - irq_first;
-	if (irq_index >= AB8500_NR_IRQS)
+	if (irq_index >= num_irqs)
 		return -EINVAL;
 
 	/*
@@ -1438,7 +1439,7 @@ static ssize_t ab8500_unsubscribe_write(struct file *file,
 	}
 
 	irq_index = user_val - irq_first;
-	if (irq_index >= AB8500_NR_IRQS)
+	if (irq_index >= num_irqs)
 		return -EINVAL;
 
 	/* Set irq count to 0 when unsubscribe */
@@ -1519,21 +1520,40 @@ static struct dentry *ab8500_gpadc_dir;
 static int __devinit ab8500_debug_probe(struct platform_device *plf)
 {
 	struct dentry *file;
+	int ret = -ENOMEM;
+	struct ab8500 *ab8500;
 	debug_bank = AB8500_MISC;
 	debug_address = AB8500_REV_REG & 0x00FF;
 
+	ab8500 = dev_get_drvdata(plf->dev.parent);
+	num_irqs = ab8500->mask_size;
+
+	irq_count = kzalloc(sizeof(irq_count)*num_irqs, GFP_KERNEL);
+	if (!irq_count)
+		return -ENOMEM;
+
+	dev_attr = kzalloc(sizeof(*dev_attr)*num_irqs,GFP_KERNEL);
+	if (!dev_attr)
+		goto out_freeirq_count;
+
+	event_name = kzalloc(sizeof(*event_name)*num_irqs, GFP_KERNEL);
+	if (!event_name)
+		goto out_freedev_attr;
+
 	irq_first = platform_get_irq_byname(plf, "IRQ_FIRST");
 	if (irq_first < 0) {
 		dev_err(&plf->dev, "First irq not found, err %d\n",
 				irq_first);
-		return irq_first;
+		ret = irq_first;
+		goto out_freeevent_name;
 	}
 
 	irq_last = platform_get_irq_byname(plf, "IRQ_LAST");
 	if (irq_last < 0) {
 		dev_err(&plf->dev, "Last irq not found, err %d\n",
 				irq_last);
-		return irq_last;
+		ret = irq_last;
+                goto out_freeevent_name;
 	}
 
 	ab8500_dir = debugfs_create_dir(AB8500_NAME_STRING, NULL);
@@ -1656,12 +1676,23 @@ err:
 	if (ab8500_dir)
 		debugfs_remove_recursive(ab8500_dir);
 	dev_err(&plf->dev, "failed to create debugfs entries.\n");
-	return -ENOMEM;
+out_freeevent_name:
+	kfree(event_name);
+out_freedev_attr:
+	kfree(dev_attr);
+out_freeirq_count:
+	kfree(irq_count);
+
+	return ret;
 }
 
 static int __devexit ab8500_debug_remove(struct platform_device *plf)
 {
 	debugfs_remove_recursive(ab8500_dir);
+	kfree(event_name);
+	kfree(dev_attr);
+	kfree(irq_count);
+
 	return 0;
 }
 
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 18/26] mfd: ab8500-core: Allow the possibility to dump all AB8500 registers
From: Lee Jones @ 2013-01-15 12:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358254566-12419-1-git-send-email-lee.jones@linaro.org>

From: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>

Implement an API so that a user may dump all AB8500 registers
via debugfs file access.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>
Reviewed-by: Linus WALLEIJ <linus.walleij@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
 drivers/mfd/ab8500-core.c         |    1 +
 drivers/mfd/ab8500-debugfs.c      |   13 +++++++++++++
 include/linux/mfd/abx500/ab8500.h |    6 ++++++
 3 files changed, 20 insertions(+)

diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index 1667c77..0f84fc0 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -319,6 +319,7 @@ static struct abx500_ops ab8500_ops = {
 	.mask_and_set_register = ab8500_mask_and_set_register,
 	.event_registers_startup_state_get = NULL,
 	.startup_irq_enabled = NULL,
+	.dump_all_banks = ab8500_dump_all_banks,
 };
 
 static void ab8500_irq_lock(struct irq_data *data)
diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c
index a1ce55e..ec57e43 100644
--- a/drivers/mfd/ab8500-debugfs.c
+++ b/drivers/mfd/ab8500-debugfs.c
@@ -581,6 +581,19 @@ static int ab8500_print_all_banks(struct seq_file *s, void *p)
 	return 0;
 }
 
+/* Dump registers to kernel log */
+void ab8500_dump_all_banks(struct device *dev)
+{
+	unsigned int i;
+
+	printk(KERN_INFO"ab8500 register values:\n");
+
+	for (i = 1; i < AB8500_NUM_BANKS; i++) {
+		printk(KERN_INFO" bank %u:\n", i);
+		ab8500_registers_print(dev, i, NULL);
+	}
+}
+
 static int ab8500_all_banks_open(struct inode *inode, struct file *file)
 {
 	struct seq_file *s;
diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h
index 3f4e6d1..4c8d966 100644
--- a/include/linux/mfd/abx500/ab8500.h
+++ b/include/linux/mfd/abx500/ab8500.h
@@ -345,4 +345,10 @@ static inline int is_ab8500_2p0(struct ab8500 *ab)
 	return (is_ab8500(ab) && (ab->chip_id == AB8500_CUT2P0));
 }
 
+#ifdef CONFIG_AB8500_DEBUG
+void ab8500_dump_all_banks(struct device *dev);
+#else
+static inline void ab8500_dump_all_banks(struct device *dev) {}
+#endif
+
 #endif /* MFD_AB8500_H */
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 17/26] mfd: ab8500-debugfs: add debugfs node to read all registers
From: Lee Jones @ 2013-01-15 12:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358254566-12419-1-git-send-email-lee.jones@linaro.org>

From: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>

Update the ab8500_registers_print() to reuse it from multiple places.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>
Reviewed-by: Linus WALLEIJ <linus.walleij@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
 drivers/mfd/ab8500-debugfs.c |   95 +++++++++++++++++++++++++++++++++++-------
 1 file changed, 81 insertions(+), 14 deletions(-)

diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c
index a231faf..a1ce55e 100644
--- a/drivers/mfd/ab8500-debugfs.c
+++ b/drivers/mfd/ab8500-debugfs.c
@@ -495,15 +495,12 @@ static irqreturn_t ab8500_debug_handler(int irq, void *data)
 	return IRQ_HANDLED;
 }
 
-static int ab8500_registers_print(struct seq_file *s, void *p)
+/* Prints to seq_file or log_buf */
+static int ab8500_registers_print(struct device *dev, u32 bank,
+				struct seq_file *s)
 {
-	struct device *dev = s->private;
 	unsigned int i;
-	u32 bank = debug_bank;
-
-	seq_printf(s, AB8500_NAME_STRING " register values:\n");
 
-	seq_printf(s, " bank %u:\n", bank);
 	for (i = 0; i < debug_ranges[bank].num_ranges; i++) {
 		u32 reg;
 
@@ -520,22 +517,42 @@ static int ab8500_registers_print(struct seq_file *s, void *p)
 				return err;
 			}
 
-			err = seq_printf(s, "  [%u/0x%02X]: 0x%02X\n", bank,
-				reg, value);
-			if (err < 0) {
-				dev_err(dev, "seq_printf overflow\n");
-				/* Error is not returned here since
-				 * the output is wanted in any case */
-				return 0;
+			if (s) {
+				err = seq_printf(s, "  [%u/0x%02X]: 0x%02X\n",
+					bank, reg, value);
+				if (err < 0) {
+					dev_err(dev,
+					"seq_printf overflow bank=%d reg=%d\n",
+						bank, reg);
+					/* Error is not returned here since
+					 * the output is wanted in any case */
+					return 0;
+				}
+			} else {
+				printk(KERN_INFO" [%u/0x%02X]: 0x%02X\n", bank,
+					reg, value);
 			}
 		}
 	}
 	return 0;
 }
 
+static int ab8500_print_bank_registers(struct seq_file *s, void *p)
+{
+	struct device *dev = s->private;
+	u32 bank = debug_bank;
+
+	seq_printf(s, AB8500_NAME_STRING " register values:\n");
+
+	seq_printf(s, " bank %u:\n", bank);
+
+	ab8500_registers_print(dev, bank, s);
+	return 0;
+}
+
 static int ab8500_registers_open(struct inode *inode, struct file *file)
 {
-	return single_open(file, ab8500_registers_print, inode->i_private);
+	return single_open(file, ab8500_print_bank_registers, inode->i_private);
 }
 
 static const struct file_operations ab8500_registers_fops = {
@@ -546,6 +563,51 @@ static const struct file_operations ab8500_registers_fops = {
 	.owner = THIS_MODULE,
 };
 
+static int ab8500_print_all_banks(struct seq_file *s, void *p)
+{
+	struct device *dev = s->private;
+	unsigned int i;
+	int err;
+
+	seq_printf(s, AB8500_NAME_STRING " register values:\n");
+
+	for (i = 1; i < AB8500_NUM_BANKS; i++) {
+		err = seq_printf(s, " bank %u:\n", i);
+		if (err < 0)
+			dev_err(dev, "seq_printf overflow, bank=%d\n", i);
+
+		ab8500_registers_print(dev, i, s);
+	}
+	return 0;
+}
+
+static int ab8500_all_banks_open(struct inode *inode, struct file *file)
+{
+	struct seq_file *s;
+	int err;
+
+	err = single_open(file, ab8500_print_all_banks, inode->i_private);
+	if (!err) {
+		/* Default buf size in seq_read is not enough */
+		s = (struct seq_file *)file->private_data;
+		s->size = (PAGE_SIZE * 2);
+		s->buf = kmalloc(s->size, GFP_KERNEL);
+		if (!s->buf) {
+			single_release(inode, file);
+			err = -ENOMEM;
+		}
+	}
+	return err;
+}
+
+static const struct file_operations ab8500_all_banks_fops = {
+	.open = ab8500_all_banks_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.owner = THIS_MODULE,
+};
+
 static int ab8500_bank_print(struct seq_file *s, void *p)
 {
 	return seq_printf(s, "%d\n", debug_bank);
@@ -1475,6 +1537,11 @@ static int __devinit ab8500_debug_probe(struct platform_device *plf)
 	if (!file)
 		goto err;
 
+	file = debugfs_create_file("all-banks", S_IRUGO,
+	    ab8500_dir, &plf->dev, &ab8500_all_banks_fops);
+	if (!file)
+		goto err;
+
 	file = debugfs_create_file("register-bank", (S_IRUGO | S_IWUSR),
 	    ab8500_dir, &plf->dev, &ab8500_bank_fops);
 	if (!file)
-- 
1.7.9.5

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox