Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 07/12] ARM: KVM: VGIC virtual CPU interface management
From: Marc Zyngier @ 2013-01-15 11:00 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CANM98qJ_Pb5bT0k7bAhv6i1YTj6GxvNE6vvY49OZrqsKpF3xOA@mail.gmail.com>

On 14/01/13 22:02, Christoffer Dall wrote:
> On Mon, Jan 14, 2013 at 10:42 AM, Will Deacon <will.deacon@arm.com> wrote:
>> On Tue, Jan 08, 2013 at 06:42:11PM +0000, Christoffer Dall wrote:
>>> From: Marc Zyngier <marc.zyngier@arm.com>
>>>
>>> Add VGIC virtual CPU interface code, picking pending interrupts
>>> from the distributor and stashing them in the VGIC control interface
>>> list registers.
>>>
>>> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
>>> Signed-off-by: Christoffer Dall <c.dall@virtualopensystems.com>
>>> ---
>>>  arch/arm/include/asm/kvm_vgic.h |   30 ++++
>>>  arch/arm/kvm/vgic.c             |  327 +++++++++++++++++++++++++++++++++++++++
>>>  2 files changed, 356 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/arch/arm/include/asm/kvm_vgic.h b/arch/arm/include/asm/kvm_vgic.h
>>> index 9ff0d9c..b3133c4 100644
>>> --- a/arch/arm/include/asm/kvm_vgic.h
>>> +++ b/arch/arm/include/asm/kvm_vgic.h
>>> @@ -110,8 +110,33 @@ struct vgic_dist {
>>>  };
>>>
>>>  struct vgic_cpu {
>>> +#ifdef CONFIG_KVM_ARM_VGIC
>>> +       /* per IRQ to LR mapping */
>>> +       u8              vgic_irq_lr_map[VGIC_NR_IRQS];
>>> +
>>> +       /* Pending interrupts on this VCPU */
>>> +       DECLARE_BITMAP( pending_percpu, VGIC_NR_PRIVATE_IRQS);
>>> +       DECLARE_BITMAP( pending_shared, VGIC_NR_SHARED_IRQS);
>>> +
>>> +       /* Bitmap of used/free list registers */
>>> +       DECLARE_BITMAP( lr_used, 64);
>>> +
>>> +       /* Number of list registers on this CPU */
>>> +       int             nr_lr;
>>> +
>>> +       /* CPU vif control registers for world switch */
>>> +       u32             vgic_hcr;
>>> +       u32             vgic_vmcr;
>>> +       u32             vgic_misr;      /* Saved only */
>>> +       u32             vgic_eisr[2];   /* Saved only */
>>> +       u32             vgic_elrsr[2];  /* Saved only */
>>> +       u32             vgic_apr;
>>> +       u32             vgic_lr[64];    /* A15 has only 4... */
>>
>> Have a #define for the maximum number of list registers.
>>
>>> +#endif
>>>  };
>>>
>>> +#define LR_EMPTY       0xff
>>> +
>>>  struct kvm;
>>>  struct kvm_vcpu;
>>>  struct kvm_run;
>>> @@ -119,9 +144,14 @@ struct kvm_exit_mmio;
>>>
>>>  #ifdef CONFIG_KVM_ARM_VGIC
>>>  int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr);
>>> +void kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu);
>>> +void kvm_vgic_sync_from_cpu(struct kvm_vcpu *vcpu);
>>
>> Same comment as for the arch timer (flush/sync).
>>
>>> +int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu);
>>>  bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
>>>                       struct kvm_exit_mmio *mmio);
>>>
>>> +#define irqchip_in_kernel(k)   (!!((k)->arch.vgic.vctrl_base))
>>> +
>>>  #else
>>>  static inline int kvm_vgic_hyp_init(void)
>>>  {
>>> diff --git a/arch/arm/kvm/vgic.c b/arch/arm/kvm/vgic.c
>>> index bd2bd7f..58237d5 100644
>>> --- a/arch/arm/kvm/vgic.c
>>> +++ b/arch/arm/kvm/vgic.c
>>> @@ -152,6 +152,34 @@ static int vgic_irq_is_enabled(struct kvm_vcpu *vcpu, int irq)
>>>         return vgic_bitmap_get_irq_val(&dist->irq_enabled, vcpu->vcpu_id, irq);
>>>  }
>>>
>>> +static int vgic_irq_is_active(struct kvm_vcpu *vcpu, int irq)
>>> +{
>>> +       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
>>> +
>>> +       return vgic_bitmap_get_irq_val(&dist->irq_active, vcpu->vcpu_id, irq);
>>> +}
>>> +
>>> +static void vgic_irq_set_active(struct kvm_vcpu *vcpu, int irq)
>>> +{
>>> +       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
>>> +
>>> +       vgic_bitmap_set_irq_val(&dist->irq_active, vcpu->vcpu_id, irq, 1);
>>> +}
>>> +
>>> +static void vgic_irq_clear_active(struct kvm_vcpu *vcpu, int irq)
>>> +{
>>> +       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
>>> +
>>> +       vgic_bitmap_set_irq_val(&dist->irq_active, vcpu->vcpu_id, irq, 0);
>>> +}
>>> +
>>> +static int vgic_dist_irq_is_pending(struct kvm_vcpu *vcpu, int irq)
>>> +{
>>> +       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
>>> +
>>> +       return vgic_bitmap_get_irq_val(&dist->irq_state, vcpu->vcpu_id, irq);
>>> +}
>>> +
>>>  static void vgic_dist_irq_set(struct kvm_vcpu *vcpu, int irq)
>>>  {
>>>         struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
>>> @@ -711,7 +739,30 @@ static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg)
>>>
>>>  static int compute_pending_for_cpu(struct kvm_vcpu *vcpu)
>>>  {
>>> -       return 0;
>>> +       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
>>> +       unsigned long *pending, *enabled, *pend_percpu, *pend_shared;
>>> +       unsigned long pending_private, pending_shared;
>>> +       int vcpu_id;
>>> +
>>> +       vcpu_id = vcpu->vcpu_id;
>>> +       pend_percpu = vcpu->arch.vgic_cpu.pending_percpu;
>>> +       pend_shared = vcpu->arch.vgic_cpu.pending_shared;
>>> +
>>> +       pending = vgic_bitmap_get_cpu_map(&dist->irq_state, vcpu_id);
>>> +       enabled = vgic_bitmap_get_cpu_map(&dist->irq_enabled, vcpu_id);
>>> +       bitmap_and(pend_percpu, pending, enabled, VGIC_NR_PRIVATE_IRQS);
>>> +
>>> +       pending = vgic_bitmap_get_shared_map(&dist->irq_state);
>>> +       enabled = vgic_bitmap_get_shared_map(&dist->irq_enabled);
>>> +       bitmap_and(pend_shared, pending, enabled, VGIC_NR_SHARED_IRQS);
>>> +       bitmap_and(pend_shared, pend_shared,
>>> +                  vgic_bitmap_get_shared_map(&dist->irq_spi_target[vcpu_id]),
>>> +                  VGIC_NR_SHARED_IRQS);
>>> +
>>> +       pending_private = find_first_bit(pend_percpu, VGIC_NR_PRIVATE_IRQS);
>>> +       pending_shared = find_first_bit(pend_shared, VGIC_NR_SHARED_IRQS);
>>> +       return (pending_private < VGIC_NR_PRIVATE_IRQS ||
>>> +               pending_shared < VGIC_NR_SHARED_IRQS);
>>>  }
>>>
>>>  /*
>>> @@ -737,6 +788,280 @@ static void vgic_update_state(struct kvm *kvm)
>>>         }
>>>  }
>>>
>>> +#define LR_CPUID(lr)   \
>>> +       (((lr) & GICH_LR_PHYSID_CPUID) >> GICH_LR_PHYSID_CPUID_SHIFT)
>>> +#define MK_LR_PEND(src, irq)   \
>>> +       (GICH_LR_PENDING_BIT | ((src) << GICH_LR_PHYSID_CPUID_SHIFT) | (irq))
>>> +/*
>>> + * Queue an interrupt to a CPU virtual interface. Return true on success,
>>> + * or false if it wasn't possible to queue it.
>>> + */
>>> +static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
>>> +{
>>> +       struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
>>> +       int lr;
>>> +
>>> +       /* Sanitize the input... */
>>> +       BUG_ON(sgi_source_id & ~7);
>>> +       BUG_ON(sgi_source_id && irq > 15);
>>
>> You can use your new NR_SGIS definition here.
>>
> 
> This should address the remaining comments:
> 
> commit 43957095ec5476beb198f4c4630dfc3e2f3951db
> Author: Christoffer Dall <c.dall@virtualopensystems.com>
> Date:   Mon Jan 14 16:59:38 2013 -0500
> 
>     KVM: ARM: vgic: Define VGIC_MAX_LRS
> 
>     Define maximum number of link registers we can handle instead of using
>     literals in the code.  If an architecture reports more link registers
>     than we support, only use the number we can support.
> 
>     Signed-off-by: Christoffer Dall <c.dall@virtualopensystems.com>
> 
> diff --git a/arch/arm/include/asm/kvm_vgic.h b/arch/arm/include/asm/kvm_vgic.h
> index 1ace491..f9d1977 100644
> --- a/arch/arm/include/asm/kvm_vgic.h
> +++ b/arch/arm/include/asm/kvm_vgic.h
> @@ -33,6 +33,7 @@
>  #define VGIC_NR_PRIVATE_IRQS	(VGIC_NR_SGIS + VGIC_NR_PPIS)
>  #define VGIC_NR_SHARED_IRQS	(VGIC_NR_IRQS - VGIC_NR_PRIVATE_IRQS)
>  #define VGIC_MAX_CPUS		KVM_MAX_VCPUS
> +#define VGIC_MAX_LRS		64

Consider this instead (for the reason below)
#define VGIC_MAX_LRS	(1 << 7)

>  /* Sanity checks... */
>  #if (VGIC_MAX_CPUS > 8)
> @@ -120,7 +121,7 @@ struct vgic_cpu {
>  	DECLARE_BITMAP(	pending_shared, VGIC_NR_SHARED_IRQS);
> 
>  	/* Bitmap of used/free list registers */
> -	DECLARE_BITMAP(	lr_used, 64);
> +	DECLARE_BITMAP(	lr_used, VGIC_MAX_LRS);
> 
>  	/* Number of list registers on this CPU */
>  	int		nr_lr;
> @@ -132,7 +133,7 @@ struct vgic_cpu {
>  	u32		vgic_eisr[2];	/* Saved only */
>  	u32		vgic_elrsr[2];	/* Saved only */
>  	u32		vgic_apr;
> -	u32		vgic_lr[64];	/* A15 has only 4... */
> +	u32		vgic_lr[VGIC_MAX_LRS];
>  #endif
>  };
> 
> diff --git a/arch/arm/kvm/vgic.c b/arch/arm/kvm/vgic.c
> index a0d283c..90a99fd 100644
> --- a/arch/arm/kvm/vgic.c
> +++ b/arch/arm/kvm/vgic.c
> @@ -1345,6 +1345,8 @@ int kvm_vgic_hyp_init(void)
> 
>  	vgic_nr_lr = readl_relaxed(vgic_vctrl_base + GICH_VTR);
>  	vgic_nr_lr = (vgic_nr_lr & 0x1f) + 1;

There is a bug here. It should be:
        vgic_nr_lr = (vgic_nr_lr & 0x2f) + 1;

> +	if (vgic_nr_lr > VGIC_MAX_LRS)
> +		vgic_nr_lr = VGIC_MAX_LRS; /* TODO: Clear remaining LRs */

Why? VGIC_MAX_LRS isn't a configurable value, but a maximum value
defined by the specification. This is the maximum you can fit in a 6 bit
field, plus one (1 << 7, exactly).

>  	ret = create_hyp_io_mappings(vgic_vctrl_base,
>  				     vgic_vctrl_base + resource_size(&vctrl_res),
> --
> 
> Thanks,
> -Christoffer
> 


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

^ permalink raw reply

* [PATCH v5 2/4] ARM: KVM: arch_timers: Add guest timer core support
From: Marc Zyngier @ 2013-01-15 11:07 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CANM98qJZH0q-TU942pA363g4h-CENUPiA=b5D0hJyBwrHQ0g7A@mail.gmail.com>

On 14/01/13 19:19, Christoffer Dall wrote:
> On Mon, Jan 14, 2013 at 10:18 AM, Will Deacon <will.deacon@arm.com> wrote:
>> On Tue, Jan 08, 2013 at 06:43:20PM +0000, Christoffer Dall wrote:
>>> From: Marc Zyngier <marc.zyngier@arm.com>
>>>
>>> Add some the architected timer related infrastructure, and support timer
>>> interrupt injection, which can happen as a resultof three possible
>>> events:
>>>
>>> - The virtual timer interrupt has fired while we were still
>>>   executing the guest
>>> - The timer interrupt hasn't fired, but it expired while we
>>>   were doing the world switch
>>> - A hrtimer we programmed earlier has fired
>>
>> [...]
>>
>>> +void kvm_timer_sync_to_cpu(struct kvm_vcpu *vcpu)
>>> +{
>>> +       struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
>>> +
>>> +       /*
>>> +        * We're about to run this vcpu again, so there is no need to
>>> +        * keep the background timer running, as we're about to
>>> +        * populate the CPU timer again.
>>> +        */
>>> +       timer_disarm(timer);
>>> +}
>>> +
>>> +void kvm_timer_sync_from_cpu(struct kvm_vcpu *vcpu)
>>> +{
>>> +       struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
>>> +       cycle_t cval, now;
>>> +       u64 ns;
>>> +
>>> +       /* Check if the timer is enabled and unmasked first */
>>> +       if ((timer->cntv_ctl & 3) != 1)
>>> +               return;
>>> +
>>> +       cval = timer->cntv_cval;
>>> +       now = kvm_phys_timer_read() - vcpu->kvm->arch.timer.cntvoff;
>>> +
>>> +       BUG_ON(timer_is_armed(timer));
>>> +
>>> +       if (cval <= now) {
>>> +               /*
>>> +                * Timer has already expired while we were not
>>> +                * looking. Inject the interrupt and carry on.
>>> +                */
>>> +               kvm_timer_inject_irq(vcpu);
>>> +               return;
>>> +       }
>>> +
>>> +       ns = cyclecounter_cyc2ns(timecounter->cc, cval - now);
>>> +       timer_arm(timer, ns);
>>> +}
>>
>> Please use flush/sync terminology to match the rest of arch/arm/.
>>
> ok, the following fixes this for both timers and the vgic:
> 
> commit 1b68f39459dbc797f6766c103edf2c1053984161
> Author: Christoffer Dall <c.dall@virtualopensystems.com>
> Date:   Mon Jan 14 14:16:31 2013 -0500
> 
>     KVM: ARM: vgic: use sync/flush terminology
> 
>     Use sync/flush for saving state to/from CPUs to be consistent with
>     other uses in arch/arm.

Sync and flush on their own are pretty inexpressive. Consider changing
it to {flush,sync}_hwstate, so we're consistent with what VFP does.

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

^ permalink raw reply

* [PATCH v5 3/4] ARM: KVM: arch_timers: Add timer world switch
From: Marc Zyngier @ 2013-01-15 11:10 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CANM98qLsq7=PRNu4-5-N2GaAuLFOUfpy-yEWb6LpZri3WQ8JwA@mail.gmail.com>

On 14/01/13 22:08, Christoffer Dall wrote:
> On Mon, Jan 14, 2013 at 12:51 PM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>> On 14/01/13 15:21, Will Deacon wrote:
>>> On Tue, Jan 08, 2013 at 06:43:27PM +0000, Christoffer Dall wrote:
>>>> From: Marc Zyngier <marc.zyngier@arm.com>
>>>>
>>>> Do the necessary save/restore dance for the timers in the world
>>>> switch code. In the process, allow the guest to read the physical
>>>> counter, which is useful for its own clock_event_device.
>>>
>>> [...]
>>>
>>>> @@ -476,6 +513,7 @@ vcpu     .req    r0              @ vcpu pointer always in r0
>>>>   * for the host.
>>>>   *
>>>>   * Assumes vcpu pointer in vcpu reg
>>>> + * Clobbers r2-r4
>>>>   */
>>>>  .macro restore_timer_state
>>>>      @ Disallow physical timer access for the guest
>>>> @@ -484,6 +522,30 @@ vcpu    .req    r0              @ vcpu pointer always in r0
>>>>      orr     r2, r2, #CNTHCTL_PL1PCTEN
>>>>      bic     r2, r2, #CNTHCTL_PL1PCEN
>>>>      mcr     p15, 4, r2, c14, c1, 0  @ CNTHCTL
>>>> +
>>>> +#ifdef CONFIG_KVM_ARM_TIMER
>>>> +    ldr     r4, [vcpu, #VCPU_KVM]
>>>> +    ldr     r2, [r4, #KVM_TIMER_ENABLED]
>>>> +    cmp     r2, #0
>>>> +    beq     1f
>>>> +
>>>> +    ldr     r2, [r4, #KVM_TIMER_CNTVOFF]
>>>> +    ldr     r3, [r4, #(KVM_TIMER_CNTVOFF + 4)]
>>>> +    mcrr    p15, 4, r2, r3, c14     @ CNTVOFF
>>>> +    isb
>>>> +
>>>> +    ldr     r4, =VCPU_TIMER_CNTV_CVAL
>>>> +    add     vcpu, vcpu, r4
>>>> +    ldrd    r2, r3, [vcpu]
>>>> +    sub     vcpu, vcpu, r4
>>>> +    mcrr    p15, 3, r2, r3, c14     @ CNTV_CVAL
>>>> +
>>>> +    ldr     r2, [vcpu, #VCPU_TIMER_CNTV_CTL]
>>>> +    and     r2, r2, #3
>>>> +    mcr     p15, 0, r2, c14, c3, 1  @ CNTV_CTL
>>>> +    isb
>>>
>>> How many of these isbs are actually needed, given that we're going to make
>>> an exception return to the guest? The last one certainly looks redundant and
>>> I can't see the need for ordering CNTVOFF vs CNTV_CVAL. I can see an
>>> argument to putting one *before* CNTV_CTL, but you don't have one there!
>>
>> CNTVOFF directly influences whether or not CNTV_CVAL will trigger or
>> not. Maybe I'm just being paranoid and moving the isb after CNTV_CVAL is
>> enough.
>>
>> The last one is definitively superfluous.
>>
> 
> can't we also get rid of the isb on the return path then?
> 
> do you agree with this patch:
> 
> diff --git a/arch/arm/kvm/interrupts_head.S b/arch/arm/kvm/interrupts_head.S
> index 57cfa84..7e6eedf 100644
> --- a/arch/arm/kvm/interrupts_head.S
> +++ b/arch/arm/kvm/interrupts_head.S
> @@ -492,7 +492,6 @@ vcpu	.req	r0		@ vcpu pointer always in r0
>  	str	r2, [vcpu, #VCPU_TIMER_CNTV_CTL]
>  	bic	r2, #1			@ Clear ENABLE
>  	mcr	p15, 0, r2, c14, c3, 1	@ CNTV_CTL
> -	isb

This isb is required. Otherwise you have no guarantee about how recent
the next CNTV_CVAL read is (could be speculated).

>  	mrrc	p15, 3, r2, r3, c14	@ CNTV_CVAL
>  	ldr	r4, =VCPU_TIMER_CNTV_CVAL
> @@ -532,18 +531,17 @@ vcpu	.req	r0		@ vcpu pointer always in r0
>  	ldr	r2, [r4, #KVM_TIMER_CNTVOFF]
>  	ldr	r3, [r4, #(KVM_TIMER_CNTVOFF + 4)]
>  	mcrr	p15, 4, r2, r3, c14	@ CNTVOFF
> -	isb
> 
>  	ldr	r4, =VCPU_TIMER_CNTV_CVAL
>  	add	vcpu, vcpu, r4
>  	ldrd	r2, r3, [vcpu]
>  	sub	vcpu, vcpu, r4
>  	mcrr	p15, 3, r2, r3, c14	@ CNTV_CVAL
> +	isb
> 
>  	ldr	r2, [vcpu, #VCPU_TIMER_CNTV_CTL]
>  	and	r2, r2, #3
>  	mcr	p15, 0, r2, c14, c3, 1	@ CNTV_CTL
> -	isb
>  1:
>  #endif
>  .endm

This part is OK.

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

^ permalink raw reply

* [PATCH V2 3/3] ARM: davinci: da850: add NAND driver entries
From: Sekhar Nori @ 2013-01-15 11:11 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <F3DBB1B3EF102E4994C89758CFCA32412B96F9@DBDE01.ent.ti.com>

On 1/15/2013 4:06 PM, Kumar, Anil wrote:
> On Thu, Jan 10, 2013 at 17:49:13, Nori, Sekhar wrote:
>> On 1/10/2013 1:07 PM, Kumar, Anil wrote:
>>> On Wed, Jan 09, 2013 at 18:17:46, Nori, Sekhar wrote:
>>
>>> I do not think that it is good idea to move NAND pin mux information
>>> into da850.dtsi because this information is evm specific.
>>> if we will use this approach then we must use the same approach for
>>> other modules also as ASoC etc.   
>>
>> Why do you consider this EVM specific. IOW, which pins do you see
>> changing on another board? BTW, if there are additional pins needed than
>> what are listed, we can always add more pinux entries in the .dts files.
>> The pins present in dtsi file should the base case.
>  
> Ok, we can use this approach for DaVinci as its SoC modules do not
> have multiple pin configuration option. I will do the changes in 
> next patch series.

You could do this even if this was the case by defining multiple pin groups.

Thanks,
Sekhar

^ permalink raw reply

* [PATCH 06/16] ARM: b.L: generic SMP secondary bringup and hotplug support
From: Achin Gupta @ 2013-01-15 11:18 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <50F4F807.3010806@ti.com>

Hi Santosh,

On Tue, Jan 15, 2013 at 6:32 AM, Santosh Shilimkar
<santosh.shilimkar@ti.com> wrote:
> On Monday 14 January 2013 11:35 PM, Achin Gupta wrote:
>>
>> Hi Santosh,
>>
>> On Fri, Jan 11, 2013 at 6:02 PM, Santosh Shilimkar
>> <santosh.shilimkar@ti.com> wrote:
>>>
>>> On Thursday 10 January 2013 05:50 AM, Nicolas Pitre wrote:
>>>>
>>>>
>>>> Now that the b.L power API is in place, we can use it for SMP secondary
>>>> bringup and CPU hotplug in a generic fashion.
>>>>
>>>> Signed-off-by: Nicolas Pitre <nico@linaro.org>
>>>> ---
>>>>    arch/arm/common/Makefile     |  2 +-
>>>>    arch/arm/common/bL_platsmp.c | 79
>>>> ++++++++++++++++++++++++++++++++++++++++++++
>>>>    2 files changed, 80 insertions(+), 1 deletion(-)
>>>>    create mode 100644 arch/arm/common/bL_platsmp.c
>>>>
>>>> diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile
>>>> index 894c2ddf9b..59b36db7cc 100644
>>>> --- a/arch/arm/common/Makefile
>>>> +++ b/arch/arm/common/Makefile
>>>> @@ -15,4 +15,4 @@ obj-$(CONFIG_PCI_HOST_ITE8152)  += it8152.o
>>>>    obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o
>>>>    obj-$(CONFIG_FIQ_GLUE)                += fiq_glue.o fiq_glue_setup.o
>>>>    obj-$(CONFIG_FIQ_DEBUGGER)    += fiq_debugger.o
>>>> -obj-$(CONFIG_BIG_LITTLE)       += bL_head.o bL_entry.o vlock.o
>>>> +obj-$(CONFIG_BIG_LITTLE)       += bL_head.o bL_entry.o bL_platsmp.o
>>>> vlock.o
>>>> diff --git a/arch/arm/common/bL_platsmp.c b/arch/arm/common/bL_platsmp.c
>>>> new file mode 100644
>>>> index 0000000000..0acb9f4685
>>>> --- /dev/null
>>>> +++ b/arch/arm/common/bL_platsmp.c
>>>> @@ -0,0 +1,79 @@
>>>> +/*
>>>> + * linux/arch/arm/mach-vexpress/bL_platsmp.c
>>>> + *
>>>> + * Created by:  Nicolas Pitre, November 2012
>>>> + * Copyright:   (C) 2012  Linaro Limited
>>>> + *
>>>> + * 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.
>>>> + *
>>>> + * Code to handle secondary CPU bringup and hotplug for the bL power
>>>> API.
>>>> + */
>>>> +
>>>> +#include <linux/init.h>
>>>> +#include <linux/smp.h>
>>>> +
>>>> +#include <asm/bL_entry.h>
>>>> +#include <asm/smp_plat.h>
>>>> +#include <asm/hardware/gic.h>
>>>> +
>>>> +static void __init simple_smp_init_cpus(void)
>>>> +{
>>>> +       set_smp_cross_call(gic_raise_softirq);
>>>> +}
>>>> +
>>>> +static int __cpuinit bL_boot_secondary(unsigned int cpu, struct
>>>> task_struct *idle)
>>>> +{
>>>> +       unsigned int pcpu, pcluster, ret;
>>>> +       extern void secondary_startup(void);
>>>> +
>>>> +       pcpu = cpu_logical_map(cpu) & 0xff;
>>>> +       pcluster = (cpu_logical_map(cpu) >> 8) & 0xff;
>>>> +       pr_debug("%s: logical CPU %d is physical CPU %d cluster %d\n",
>>>> +                __func__, cpu, pcpu, pcluster);
>>>> +
>>>> +       bL_set_entry_vector(pcpu, pcluster, NULL);
>>>> +       ret = bL_cpu_power_up(pcpu, pcluster);
>>>> +       if (ret)
>>>> +               return ret;
>>>> +       bL_set_entry_vector(pcpu, pcluster, secondary_startup);
>>>> +       gic_raise_softirq(cpumask_of(cpu), 0);
>>>> +       sev();
>>>
>>>
>>> softirq() should be enough to break a CPU if it is in standby with
>>> wfe state. Is that additional sev() needed here ?
>>
>>
>> Not if the target cpu has its I & F bits disabled and that would be the
>> case with a secondary waiting to be woken up
>>
> This is interesting since CPU is actually in standby state and this
> was not my understanding so far. Your statement at least contradicts
> the ARM ARM (B1.8.12 Wait For Interrupt)
> -----------------------
> The processor can remain in the WFI low-power state until it is reset, or it
> detects one of the following WFI wake-up
> events:
> ? a physical IRQ interrupt, regardless of the value of the CPSR.I bit
> ? a physical FIQ interrupt, regardless of the value of the CPSR.F bit
> ----------------------------------
>
> Are you referring to some new behavior on latest ARMv7 CPUs ?

You are abs right about the 'wfi' behaviour. I was talking about the effect
of interrupts on a cpu thats in 'wfe'.

The power up process takes place in two steps. The first step involves
sending an ipi which will either:

a. cause the power controller to bring the processor out of reset
b. cause the processor to exit from wfi (most probably in the bootloader code)

The cpu then enters Linux (bL_entry_point) and after doing any cluster setup
waits in 'wfe' if its 'bL_entry_vector' has not been set as yet. The
'sev' is meant
to poke the cpu once this has been done.

Its not required in this case as we have already set 'bL_entry_vector' , issued
a barrier & flushed the cache line. So if the incoming cpu sees a 0 in
its vector
then that would be a symptom of a different problem.

Thanks,
Achin

> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* [PATCH] ARM: KVM: avoid add/sub on vcpu pointer
From: Marc Zyngier @ 2013-01-15 11:20 UTC (permalink / raw)
  To: linux-arm-kernel

Doing arithmetic on the vcpu pointer can be avoided by using
another register as a base. Saves a few instructions on the
switch path.

Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
 arch/arm/kvm/interrupts_head.S | 32 ++++++++++++++------------------
 1 file changed, 14 insertions(+), 18 deletions(-)

diff --git a/arch/arm/kvm/interrupts_head.S b/arch/arm/kvm/interrupts_head.S
index 0e423be..d2442f9 100644
--- a/arch/arm/kvm/interrupts_head.S
+++ b/arch/arm/kvm/interrupts_head.S
@@ -264,11 +264,10 @@ vcpu	.req	r0		@ vcpu pointer always in r0
 	str	r3, [vcpu, #CP15_OFFSET(c1_CPACR)]
 	str	r4, [vcpu, #CP15_OFFSET(c2_TTBCR)]
 	str	r5, [vcpu, #CP15_OFFSET(c3_DACR)]
-	add	vcpu, vcpu, #CP15_OFFSET(c2_TTBR0)
-	strd	r6, r7, [vcpu]
-	add	vcpu, vcpu, #CP15_OFFSET(c2_TTBR1) - CP15_OFFSET(c2_TTBR0)
-	strd	r8, r9, [vcpu]
-	sub	vcpu, vcpu, #CP15_OFFSET(c2_TTBR1)
+	add	r2, vcpu, #CP15_OFFSET(c2_TTBR0)
+	strd	r6, r7, [r2]
+	add	r2, vcpu, #CP15_OFFSET(c2_TTBR1)
+	strd	r8, r9, [r2]
 	str	r10, [vcpu, #CP15_OFFSET(c10_PRRR)]
 	str	r11, [vcpu, #CP15_OFFSET(c10_NMRR)]
 	str	r12, [vcpu, #CP15_OFFSET(c0_CSSELR)]
@@ -362,11 +361,10 @@ vcpu	.req	r0		@ vcpu pointer always in r0
 	ldr	r3, [vcpu, #CP15_OFFSET(c1_CPACR)]
 	ldr	r4, [vcpu, #CP15_OFFSET(c2_TTBCR)]
 	ldr	r5, [vcpu, #CP15_OFFSET(c3_DACR)]
-	add	vcpu, vcpu, #CP15_OFFSET(c2_TTBR0)
-	ldrd	r6, r7, [vcpu]
-	add	vcpu, vcpu, #CP15_OFFSET(c2_TTBR1) - CP15_OFFSET(c2_TTBR0)
-	ldrd	r8, r9, [vcpu]
-	sub	vcpu, vcpu, #CP15_OFFSET(c2_TTBR1)
+	add	r12, vcpu, #CP15_OFFSET(c2_TTBR0)
+	ldrd	r6, r7, [r12]
+	add	r12, vcpu, #CP15_OFFSET(c2_TTBR1)
+	ldrd	r8, r9, [r12]
 	ldr	r10, [vcpu, #CP15_OFFSET(c10_PRRR)]
 	ldr	r11, [vcpu, #CP15_OFFSET(c10_NMRR)]
 	ldr	r12, [vcpu, #CP15_OFFSET(c0_CSSELR)]
@@ -479,7 +477,7 @@ vcpu	.req	r0		@ vcpu pointer always in r0
  * for the host.
  *
  * Assumes vcpu pointer in vcpu reg
- * Clobbers r2-r4
+ * Clobbers r2-r5
  */
 .macro save_timer_state
 #ifdef CONFIG_KVM_ARM_TIMER
@@ -496,9 +494,8 @@ vcpu	.req	r0		@ vcpu pointer always in r0
 
 	mrrc	p15, 3, r2, r3, c14	@ CNTV_CVAL
 	ldr	r4, =VCPU_TIMER_CNTV_CVAL
-	add	vcpu, vcpu, r4
-	strd	r2, r3, [vcpu]
-	sub	vcpu, vcpu, r4
+	add	r5, vcpu, r4
+	strd	r2, r3, [r5]
 
 1:
 #endif
@@ -513,7 +510,7 @@ vcpu	.req	r0		@ vcpu pointer always in r0
  * for the host.
  *
  * Assumes vcpu pointer in vcpu reg
- * Clobbers r2-r4
+ * Clobbers r2-r5
  */
 .macro restore_timer_state
 	@ Disallow physical timer access for the guest
@@ -534,9 +531,8 @@ vcpu	.req	r0		@ vcpu pointer always in r0
 	mcrr	p15, 4, r2, r3, c14	@ CNTVOFF
 
 	ldr	r4, =VCPU_TIMER_CNTV_CVAL
-	add	vcpu, vcpu, r4
-	ldrd	r2, r3, [vcpu]
-	sub	vcpu, vcpu, r4
+	add	r5, vcpu, r4
+	ldrd	r2, r3, [r5]
 	mcrr	p15, 3, r2, r3, c14	@ CNTV_CVAL
 	isb
 
-- 
1.8.1

^ permalink raw reply related

* [PATCHv2 1/4] clockevents: Add generic timer broadcast receiver
From: Thomas Gleixner @ 2013-01-15 11:24 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20130114153612.GF7990@e106331-lin.cambridge.arm.com>

On Mon, 14 Jan 2013, Mark Rutland wrote:
> On Mon, Jan 14, 2013 at 02:17:26PM +0000, Thomas Gleixner wrote:
> > > I thought this previously also [1], but I couldn't find any path such that a
> > > tick_cpu_device would have an evtdev without an event_handler. We always set the
> > > handler before setting evtdev, and alway wipe evtdev before wiping the handler.
> > > 
> > > Have I missed something?
> > 
> > That's an x86 specific issue. Though we could try and make that
> > functionality completely generic.
> 
> Just to check: is the evt->event_handler check necessary?

For x86 yes. See the comment.

Thanks,

	tglx

^ permalink raw reply

* Early kernel hang with big DTB appended
From: Tomasz Figa @ 2013-01-15 11:26 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <alpine.LFD.2.02.1301141655570.6300@xanadu.home>

Hi Nicolas,

On Monday 14 of January 2013 17:13:09 Nicolas Pitre wrote:
> On Fri, 11 Jan 2013, Sascha Hauer wrote:
> > On Thu, Jan 03, 2013 at 04:55:00PM +0100, Tomasz Figa wrote:
> > > Hi,
> > > 
> > > I'm observing strange behavior when booting 3.8-rc1 and -rc2 with
> > > appended DTB. The kernel hangs very early when the DTB is bigger
> > > than some threshold somewhere around 24 KiB. With fullest possible
> > > low level UART debugging (and printk patched to use printascii) I'm
> > > receiving following output:
> > > 
> > > Uncompressing Linux... done, booting the kernel.
> > > Booting Linux on physical CPU 0xa00
> > > Linux version 3.8.0-rc1-00073-gdf6efca-dirty (t.figa at amdc1227) (gcc
> > > version 4.5.2 (Gentoo 4.5.2 p1.2, pie-0.4.5) ) #2 SMP PREEMPT Thu
> > > Jan 3
> > > 15:37:35 CET 2013
> > > CPU: ARMv7 Processor [413fc090] revision 0 (ARMv7), cr=10c53c7d
> > > CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction
> > > cache
> > > 
> > > I tested on two Exynos-based boards (exynos4210-trats and one
> > > internal
> > > exynos4412-based board) and same happens on both.
> > > 
> > > Do you have any ideas?
> > 
> > Another thing besides the things already mentioned is that the dtb may
> > not cross a 1MiB boundary. The Kernel uses a single 1Mib section
> > (aligned to 1Mib) to initially map the dtb. Once you cross that
> > boundary parts of the dtb won't be accessible for the Kernel anymore.
> 
> Crap.  You're right.  This patch should fix this issue.
> 
> @Tomasz: please could you confirm this fixes your initial problem?

I just tested the patch and it fixes the problem indeed. The kernel
now boots successfully after applying the patch, while undoing it
makes the kernel fail to boot again. Thanks.

Tested-by: Tomasz Figa <t.figa@samsung.com>

Best regards,
-- 
Tomasz Figa
Samsung Poland R&D Center
SW Solution Development, Linux Platform

^ permalink raw reply

* [PATCH 06/16] ARM: b.L: generic SMP secondary bringup and hotplug support
From: Santosh Shilimkar @ 2013-01-15 11:26 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAMpgmWCzCxwenDs7XPh7D+shEJcj_ObWcUGZG-xMtd15XL-3Vg@mail.gmail.com>

On Tuesday 15 January 2013 04:48 PM, Achin Gupta wrote:
> Hi Santosh,
>
> On Tue, Jan 15, 2013 at 6:32 AM, Santosh Shilimkar
> <santosh.shilimkar@ti.com> wrote:
>> On Monday 14 January 2013 11:35 PM, Achin Gupta wrote:
>>>
>>> Hi Santosh,
>>>
>>> On Fri, Jan 11, 2013 at 6:02 PM, Santosh Shilimkar
>>> <santosh.shilimkar@ti.com> wrote:
>>>>
>>>> On Thursday 10 January 2013 05:50 AM, Nicolas Pitre wrote:
>>>>>
>>>>>
>>>>> Now that the b.L power API is in place, we can use it for SMP secondary
>>>>> bringup and CPU hotplug in a generic fashion.
>>>>>
>>>>> Signed-off-by: Nicolas Pitre <nico@linaro.org>
>>>>> ---
>>>>>     arch/arm/common/Makefile     |  2 +-
>>>>>     arch/arm/common/bL_platsmp.c | 79
>>>>> ++++++++++++++++++++++++++++++++++++++++++++
>>>>>     2 files changed, 80 insertions(+), 1 deletion(-)
>>>>>     create mode 100644 arch/arm/common/bL_platsmp.c
>>>>>
>>>>> diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile
>>>>> index 894c2ddf9b..59b36db7cc 100644
>>>>> --- a/arch/arm/common/Makefile
>>>>> +++ b/arch/arm/common/Makefile
>>>>> @@ -15,4 +15,4 @@ obj-$(CONFIG_PCI_HOST_ITE8152)  += it8152.o
>>>>>     obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o
>>>>>     obj-$(CONFIG_FIQ_GLUE)                += fiq_glue.o fiq_glue_setup.o
>>>>>     obj-$(CONFIG_FIQ_DEBUGGER)    += fiq_debugger.o
>>>>> -obj-$(CONFIG_BIG_LITTLE)       += bL_head.o bL_entry.o vlock.o
>>>>> +obj-$(CONFIG_BIG_LITTLE)       += bL_head.o bL_entry.o bL_platsmp.o
>>>>> vlock.o
>>>>> diff --git a/arch/arm/common/bL_platsmp.c b/arch/arm/common/bL_platsmp.c
>>>>> new file mode 100644
>>>>> index 0000000000..0acb9f4685
>>>>> --- /dev/null
>>>>> +++ b/arch/arm/common/bL_platsmp.c
>>>>> @@ -0,0 +1,79 @@
>>>>> +/*
>>>>> + * linux/arch/arm/mach-vexpress/bL_platsmp.c
>>>>> + *
>>>>> + * Created by:  Nicolas Pitre, November 2012
>>>>> + * Copyright:   (C) 2012  Linaro Limited
>>>>> + *
>>>>> + * 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.
>>>>> + *
>>>>> + * Code to handle secondary CPU bringup and hotplug for the bL power
>>>>> API.
>>>>> + */
>>>>> +
>>>>> +#include <linux/init.h>
>>>>> +#include <linux/smp.h>
>>>>> +
>>>>> +#include <asm/bL_entry.h>
>>>>> +#include <asm/smp_plat.h>
>>>>> +#include <asm/hardware/gic.h>
>>>>> +
>>>>> +static void __init simple_smp_init_cpus(void)
>>>>> +{
>>>>> +       set_smp_cross_call(gic_raise_softirq);
>>>>> +}
>>>>> +
>>>>> +static int __cpuinit bL_boot_secondary(unsigned int cpu, struct
>>>>> task_struct *idle)
>>>>> +{
>>>>> +       unsigned int pcpu, pcluster, ret;
>>>>> +       extern void secondary_startup(void);
>>>>> +
>>>>> +       pcpu = cpu_logical_map(cpu) & 0xff;
>>>>> +       pcluster = (cpu_logical_map(cpu) >> 8) & 0xff;
>>>>> +       pr_debug("%s: logical CPU %d is physical CPU %d cluster %d\n",
>>>>> +                __func__, cpu, pcpu, pcluster);
>>>>> +
>>>>> +       bL_set_entry_vector(pcpu, pcluster, NULL);
>>>>> +       ret = bL_cpu_power_up(pcpu, pcluster);
>>>>> +       if (ret)
>>>>> +               return ret;
>>>>> +       bL_set_entry_vector(pcpu, pcluster, secondary_startup);
>>>>> +       gic_raise_softirq(cpumask_of(cpu), 0);
>>>>> +       sev();
>>>>
>>>>
>>>> softirq() should be enough to break a CPU if it is in standby with
>>>> wfe state. Is that additional sev() needed here ?
>>>
>>>
>>> Not if the target cpu has its I & F bits disabled and that would be the
>>> case with a secondary waiting to be woken up
>>>
>> This is interesting since CPU is actually in standby state and this
>> was not my understanding so far. Your statement at least contradicts
>> the ARM ARM (B1.8.12 Wait For Interrupt)
>> -----------------------
>> The processor can remain in the WFI low-power state until it is reset, or it
>> detects one of the following WFI wake-up
>> events:
>> ? a physical IRQ interrupt, regardless of the value of the CPSR.I bit
>> ? a physical FIQ interrupt, regardless of the value of the CPSR.F bit
>> ----------------------------------
>>
>> Are you referring to some new behavior on latest ARMv7 CPUs ?
>
> You are abs right about the 'wfi' behaviour. I was talking about the effect
> of interrupts on a cpu thats in 'wfe'.
>
> The power up process takes place in two steps. The first step involves
> sending an ipi which will either:
>
> a. cause the power controller to bring the processor out of reset
> b. cause the processor to exit from wfi (most probably in the bootloader code)
>
> The cpu then enters Linux (bL_entry_point) and after doing any cluster setup
> waits in 'wfe' if its 'bL_entry_vector' has not been set as yet. The
> 'sev' is meant
> to poke the cpu once this has been done.
>
Thanks for additional information. Its clear to me now.

Regards
Santosh

^ permalink raw reply

* [PATCH v2 1/4] ARM i.MX6: use reserved bit for mxs phy clock gate
From: Shawn Guo @ 2013-01-15 11:33 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358230117-19443-1-git-send-email-peter.chen@freescale.com>

On Tue, Jan 15, 2013 at 02:08:34PM +0800, Peter Chen wrote:
> For mxs-phy user i.mx6q, the PHY's clock is controlled by
> hardware automatically, the software only needs to enable it
> at probe, disable it at remove. During the runtime,
> we don't need to control it. So for the usbphy clk policy:
> 
> - Keep refcount for usbphy as clk framework needs to know if
> it is off or on.

Just checked the reference manual, it seems that not only bit 6
(EN_USB_CLKS) but also bit 12 (POWER) are controlled by USB hardware.
If this is true, I think that PLL3 and PLL7 are really designed for
USB PHY use only.  Do you actually see other real users of these two
PLLs on imx6q besides USB PHY?  If not, we can just provide a dummy
clock to mxs-phy driver on imx6q, and keep real clocks usbphy1 and
usbphy2 there for platform init function to enable them if USB support
is built in.  Then, we can save all these dirty hacks.  Thoughts?

Shawn

> - Use reserved bit, in that case, clk_enable/disable will
> only update refcount, but without any hardware effects.
> 
> Signed-off-by: Peter Chen <peter.chen@freescale.com>
> ---
> Changes for v2:
> - Use reserved bit for usb phy clk control
> 
>  arch/arm/mach-imx/clk-imx6q.c |   10 ++++++++--
>  1 files changed, 8 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c
> index 7f2c10c..85dcc89 100644
> --- a/arch/arm/mach-imx/clk-imx6q.c
> +++ b/arch/arm/mach-imx/clk-imx6q.c
> @@ -208,8 +208,14 @@ int __init mx6q_clocks_init(void)
>  	clk[pll7_usb_host] = imx_clk_pllv3(IMX_PLLV3_USB,	"pll7_usb_host","osc", base + 0x20, 0x3);
>  	clk[pll8_mlb]      = imx_clk_pllv3(IMX_PLLV3_MLB,	"pll8_mlb",	"osc", base + 0xd0, 0x0);
>  
> -	clk[usbphy1] = imx_clk_gate("usbphy1", "pll3_usb_otg", base + 0x10, 6);
> -	clk[usbphy2] = imx_clk_gate("usbphy2", "pll7_usb_host", base + 0x20, 6);
> +	/*
> +	 * Bit 20 is the reserved and read-only bit, we do this only for:
> +	 * - Do nothing for usbphy clk_enable/disable
> +	 * - Keep refcount when do usbphy clk_enable/disable, in that case,
> +	 * the clk framework can know the USB phy clk is on or off
> +	 */
> +	clk[usbphy1] = imx_clk_gate("usbphy1", "pll3_usb_otg", base + 0x10, 20);
> +	clk[usbphy2] = imx_clk_gate("usbphy2", "pll7_usb_host", base + 0x20, 20);
>  
>  	clk[sata_ref] = imx_clk_fixed_factor("sata_ref", "pll6_enet", 1, 5);
>  	clk[pcie_ref] = imx_clk_fixed_factor("pcie_ref", "pll6_enet", 1, 4);
> -- 
> 1.7.0.4
> 
> 

^ permalink raw reply

* [PATCH V2 2/6] ARM: tegra20: cpuidle: add powered-down state for secondary CPU
From: Lorenzo Pieralisi @ 2013-01-15 11:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358218844.8513.30.camel@jlo-ubuntu-64.nvidia.com>

On Tue, Jan 15, 2013 at 03:00:44AM +0000, Joseph Lo wrote:
> On Fri, 2013-01-11 at 20:24 +0800, Lorenzo Pieralisi wrote:
> > On Fri, Jan 11, 2013 at 07:20:29AM +0000, Joseph Lo wrote:
> > > On Wed, 2012-12-05 at 18:50 +0800, Lorenzo Pieralisi wrote:
> > > > On Wed, Dec 05, 2012 at 10:01:49AM +0000, Joseph Lo wrote:
> > > > > The powered-down state of Tegra20 requires power gating both CPU cores.
> > > > > When the secondary CPU requests to enter powered-down state, it saves
> > > > > its own contexts and then enters WFI. The Tegra20 had a limition to
> > > > > power down both CPU cores. The secondary CPU must waits for CPU0 in
> > > > > powered-down state too. If the secondary CPU be woken up before CPU0
> > > > > entering powered-down state, then it needs to restore its CPU states
> > > > > and waits for next chance.
> > > > >
> > > > > Be aware of that, you may see the legacy power state "LP2" in the code
> > > > > which is exactly the same meaning of "CPU power down".
> > > > >
> > > > > Based on the work by:
> > > > > Colin Cross <ccross@android.com>
> > > > > Gary King <gking@nvidia.com>
> > > > >
> > > > > Signed-off-by: Joseph Lo <josephl@nvidia.com>
> > > > > +
> > > > > +#ifdef CONFIG_PM_SLEEP
> > > > > +/*
> > > > > + * tegra_pen_lock
> > > > > + *
> > > > > + * spinlock implementation with no atomic test-and-set and no coherence
> > > > > + * using Peterson's algorithm on strongly-ordered registers
> > > > > + * used to synchronize a cpu waking up from wfi with entering lp2 on idle
> > > > > + *
> > > > > + * SCRATCH37 = r1 = !turn (inverted from Peterson's algorithm)
> > > > > + * on cpu 0:
> > > > > + * SCRATCH38 = r2 = flag[0]
> > > > > + * SCRATCH39 = r3 = flag[1]
> > > > > + * on cpu1:
> > > > > + * SCRATCH39 = r2 = flag[1]
> > > > > + * SCRATCH38 = r3 = flag[0]
> > > > > + *
> > > > > + * must be called with MMU on
> > > > > + * corrupts r0-r3, r12
> > > > > + */
> > > > > +ENTRY(tegra_pen_lock)
> > > > > +       mov32   r3, TEGRA_PMC_VIRT
> > > > > +       cpu_id  r0
> > > > > +       add     r1, r3, #PMC_SCRATCH37
> > > > > +       cmp     r0, #0
> > > > > +       addeq   r2, r3, #PMC_SCRATCH38
> > > > > +       addeq   r3, r3, #PMC_SCRATCH39
> > > > > +       addne   r2, r3, #PMC_SCRATCH39
> > > > > +       addne   r3, r3, #PMC_SCRATCH38
> > > > > +
> > > > > +       mov     r12, #1
> > > > > +       str     r12, [r2]               @ flag[cpu] = 1
> > > > > +       dsb
> > > > > +       str     r12, [r1]               @ !turn = cpu
> > > > > +1:     dsb
> > > > > +       ldr     r12, [r3]
> > > > > +       cmp     r12, #1                 @ flag[!cpu] == 1?
> > > > > +       ldreq   r12, [r1]
> > > > > +       cmpeq   r12, r0                 @ !turn == cpu?
> > > > > +       beq     1b                      @ while !turn == cpu && flag[!cpu] == 1
> > > > > +
> > > > > +       mov     pc, lr                  @ locked
> > > > > +ENDPROC(tegra_pen_lock)
> > > > > +
> > > > > +ENTRY(tegra_pen_unlock)
> > > > > +       dsb
> > > > > +       mov32   r3, TEGRA_PMC_VIRT
> > > > > +       cpu_id  r0
> > > > > +       cmp     r0, #0
> > > > > +       addeq   r2, r3, #PMC_SCRATCH38
> > > > > +       addne   r2, r3, #PMC_SCRATCH39
> > > > > +       mov     r12, #0
> > > > > +       str     r12, [r2]
> > > > > +       mov     pc, lr
> > > > > +ENDPROC(tegra_pen_unlock)
> > > > 
> > > > There is an ongoing work to make this locking scheme for MMU/coherency off
> > > > paths ARM generic, and we do not want to merge yet another platform specific
> > > > locking mechanism. I will point you to the patchset when it hits LAK.
> > > > 
> > > 
> > > You did mention there is an ARM generic locking scheme for MMU/coherency
> > > off case before. Do you mean the patch below?
> > > 
> > > https://patchwork.kernel.org/patch/1957911/
> > > https://patchwork.kernel.org/patch/1957901/
> > 
> > Those are used for first-man election when multiple CPUs come out of
> > idle at once.
> > 
> > You should have a look at the entire series and in particular:
> > 
> > https://patchwork.kernel.org/patch/1957891/
> > https://patchwork.kernel.org/patch/1957951/
> > 
> > > I gave it a review today. Looks it can't fit our usage for CPU idle
> > > powered-down mode on Tegra20.
> > > 
> > > The generic mechanism only can be used when CPUs in non coherent world.
> > > But our usage needs the mechanism could be used in both coherent and non
> > > coherent case. OK. The case is we need to sync the status about the CPU1
> > > was ready to power down. In this case, the CPU0 is still in coherent
> > > world but the CPU1 isn't. So we need the locking scheme could be still
> > > safe in this situation.
> > 
> > I know, I implemented something of that sort for an A9 based development
> > platform, that's why I pointed you to this new patchset.
> > 
> > Have a look at the series, it should do what you want.
> > 
> Hi Lorenzo,
> 
> May I upstream this stuff first? I can promise to you I will re-work a
> common CPUs and cluster power sync wrappers (platform_power_ops) for all
> Tegra series that based on the generic framework. Because we didn't have
> this work in Tegra tree yet and we indeed need it for supporting cluster
> power down and switching.
> 
> But it need lots of time for re-work, testing and verification. And I
> have lots of patches that need the function of cpu suspend that be
> introduced in this patch series to support platform suspend for Tegra.
> 
> So I am asking the permission here for upstream this series first and I
> will continue the job to come out a common CPUs and cluster power sync
> wrappers for Tegra that we indeed need it to support cluster power down
> and switching.

I understand that and you should not ask me permission :-) since I am
not a maintainer. I just wanted to save you some effort, you will end
up rewriting the whole thing anyway IMHO and I think the power API would
have saved you time if it were there before.

Again, it is not my call, I am more than happy to review your code but
that's all I can do.

If you need help to integrate the power API please do ask questions.

Lorenzo

^ permalink raw reply

* rtc: vt8500: Fix year field in vt8500_rtc_set_time
From: Greg KH @ 2013-01-15 11:38 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358234563.6868.13.camel@gitbox>

On Tue, Jan 15, 2013 at 08:22:43PM +1300, Tony Prisk wrote:
> Alessandro,
> 
> This patch was sent out at the same time as two other RTC fixes for
> vt8500 but this one seems to have stopped somewhere along the way while
> the other two have been accepted.
> 
> The patch is showing in the RTC patch system along with the other two:
> http://patchwork.ozlabs.org/patch/208756/
> 
> 
> Greg,
> How did the first two patches find their way to you - did you pick them
> up off one of the lists?

I have no idea, normally rtc patches go through Andrew Morton.

^ permalink raw reply

* [PATCH RESEND 0/6 v13] gpio: Add block GPIO
From: Roland Stigge @ 2013-01-15 11:51 UTC (permalink / raw)
  To: linux-arm-kernel

This set of patches adds:

* Block GPIO API to gpiolib
* Sysfs support for GPIO API, to provide userland access
* Device interface for userland access (alternative to sysfs)
* Devicetree support to instantiate GPIO blocks via DT
* Example implementations in several gpio drivers since they need
  special accessor functions for block wise GPIO access (only for some
  selected drivers since the feature is optional and not suitable for
  all GPIO hardware)
* Fix for race condition in gpiolib on device creation

Signed-off-by: Roland Stigge <stigge@antcom.de>
Tested by: Wolfgang Grandegger <wg@grandegger.com>
--

Testing branch available at: git://git.antcom.de/linux-2.6.git blockgpio

Changes since v12:
* Added support for gpio drivers ge, ks8695, mc33880, ml-ioh, mpc5200 and
  mpc8xxx
* Added support for pinctrl driver at91

Changes since v11:
* Removed support for at91, since this one is currently in the migration
  process to pinctrl implementation
* Fixed mask setting via sysfs
* Device interface: declare gpio_block_fop_poll() static

Changes since v10:
* gpioblock-of.c: Catch <1 specified GPIOs
* gpioblock-of.c: Fixed memory leak in probe()'s error path
* gpioblock-of.c: Fixed block node's refcount via of_node_put()
* Added driver support for samsung, mm-lantiq and sa1100
* Added driver support for at91 (by Wolfgang Grandegger)
* Added pinctrl driver support for pinctl-nomadik

Changed since v9:
* Changed IRQ flags for device interface's poll() from IRQF_TRIGGER_FALLING
  to IRQF_SHARED
* Use block name on gpio_request
* Added spinlock for ISR manipulated data (dev interface / poll())

Changed since v8:
* Added poll() function to dev interface for IRQ handling
* Fixed gpio_block_register() in case of missing CONFIG_GPIO_SYSFS
* Fixed mask handling in sysfs interface
* Fixed get_user/put_user pointer (cast)

Changes since v7:
* Fixed error in API documentation (name arg missing in prototype)
* Fixed compile error if sysfs is missing: Moved gpio_block_is_output()
  out of sysfs-ifdef
* Added error handling to gpio_block_register()
* Added mask attribute to sysfs (although dev interface exists now)

Changes since v6:
* Changed realloc to list handling
* Added mask to kernel API
* Added device interface as userspace API
* Changed sizeof() to BITS_PER_LONG
* Fixed multiline comment style
* Documented direction setting by the user
* Documented order of handled chips in case of multiple chips in one block

Changes since v5:
* Documented sysfs: elaborated on "exported" and "values" attributes
* Documented sysfs: gpiochip is a separate class now
* Aggregated driver support patches for block gpio into one single patch
* Added gpio block driver support for twl6040 and pch

Changes since v4:
* Documented word width
* Bugfix: export/unexport on register/unregister
* Using default dev_attrs for gpio_block_class
* Fix gpiolib: race condition on device creation
* Added driver support for ucb14500, vt8500, xilinx

Changes since v3:
* Added driver support for pca953x, em, pl061, max732x, pcf857x
* Coding style improvements
* Fixed krealloc memory leak in error case
* sysfs: values in hex
* Register blocks in a list
* Narrowing lock scope
* Use S_IWUSR and S_IRUGO instead of direct octal values
* Use for_each_set_bit()
* Change from unsigned to unsigned long for masks and values

Changes since v2:
* Added sysfs support
* Added devicetree support
* Added support for lpc32xx, generic
* Added functions for GPIO block registration
* Added more error checking
* Bit remapping bugfix

Changes since v1:
* API change to 32/64 bit word, bit masks

Thanks to Ryan Mallon, Linus Walleij, Stijn Devriendt, Jean-Christophe
Plagniol-Villard, Mark Brown, Greg Kroah-Hartman, Grant Likely, Stefan
Roese, Wolfgang Grandegger, Tobias Rutz, Fengguang Wu and Mark Rutland
for reviewing and testing!

Roland Stigge (6):
 gpio: Add a block GPIO API to gpiolib
 gpio: Add sysfs support to block GPIO API
 gpio: Add userland device interface to block GPIO
 gpiolib: Fix default attributes for class
 gpio: Add device tree support to block GPIO API
 gpio: Add block gpio to several gpio drivers

 Documentation/ABI/testing/dev-gpioblock               |   34 
 Documentation/ABI/testing/sysfs-gpio                  |   31 
 Documentation/devicetree/bindings/gpio/gpio-block.txt |   36 
 Documentation/gpio.txt                                |   58 +
 drivers/gpio/Kconfig                                  |    2 
 drivers/gpio/Makefile                                 |    1 
 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/gpio/gpioblock-of.c                           |  100 ++
 drivers/gpio/gpiolib.c                                |  782 ++++++++++++++++--
 drivers/pinctrl/pinctrl-at91.c                        |   29 
 drivers/pinctrl/pinctrl-nomadik.c                     |   36 
 include/asm-generic/gpio.h                            |   28 
 include/linux/gpio.h                                  |  125 ++
 34 files changed, 2035 insertions(+), 47 deletions(-)

^ permalink raw reply

* [PATCH RESEND 1/6 v13] gpio: Add a block GPIO API to gpiolib
From: Roland Stigge @ 2013-01-15 11:51 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358250716-21986-1-git-send-email-stigge@antcom.de>

The recurring task of providing simultaneous access to GPIO lines (especially
for bit banging protocols) needs an appropriate API.

This patch adds a kernel internal "Block GPIO" API that enables simultaneous
access to several GPIOs. This is done by abstracting GPIOs to an n-bit word:
Once requested, it provides access to a group of GPIOs which can range over
multiple GPIO chips.

Signed-off-by: Roland Stigge <stigge@antcom.de>
---

 Documentation/gpio.txt     |   58 +++++++++++
 drivers/gpio/gpiolib.c     |  227 +++++++++++++++++++++++++++++++++++++++++++++
 include/asm-generic/gpio.h |   17 +++
 include/linux/gpio.h       |   97 +++++++++++++++++++
 4 files changed, 399 insertions(+)

--- linux-2.6.orig/Documentation/gpio.txt
+++ linux-2.6/Documentation/gpio.txt
@@ -481,6 +481,64 @@ exact name string of pinctrl device has
 argument to this routine.
 
 
+Block GPIO
+----------
+
+The above described interface concentrates on handling single GPIOs.  However,
+in applications where it is critical to set several GPIOs at once, this
+interface doesn't work well, e.g. bit-banging protocols via grouped GPIO lines.
+Consider a GPIO controller that is connected via a slow I2C line. When
+switching two or more GPIOs one after another, there can be considerable time
+between those events. This is solved by an interface called Block GPIO:
+
+struct gpio_block *gpio_block_create(unsigned int *gpios, size_t size,
+				     const char *name);
+
+This creates a new block of GPIOs as a list of GPIO numbers with the specified
+size which are accessible via the returned struct gpio_block and the accessor
+functions described below. Please note that you need to request the GPIOs
+separately via gpio_request(). Similarly, the direction of the used GPIOs need
+to be set by the user before using the block. An arbitrary list of globally
+valid GPIOs can be specified, even ranging over several gpio_chips. Actual
+handling of I/O operations will be done on a best effort base, i.e.
+simultaneous I/O only where possible by hardware and implemented in the
+respective GPIO driver. The number of GPIOs in one block is limited to the
+number of bits in an unsigned long, or BITS_PER_LONG, of the respective
+platform, i.e. typically at least 32 on a 32 bit system, and at least 64 on a
+64 bit system. However, several blocks can be defined at once.
+
+unsigned long gpio_block_get(struct gpio_block *block, unsigned long mask);
+void gpio_block_set(struct gpio_block *block, unsigned long mask,
+		    unsigned long values);
+
+With those accessor functions, setting and getting the GPIO values is possible,
+analogous to gpio_get_value() and gpio_set_value(). Each bit in the return
+value of gpio_block_get() and in the value argument of gpio_block_set()
+corresponds to a bit specified on gpio_block_create(). The mask parameters
+specify which bits in the block are acted upon. This is useful to let some bits
+untouched when doing a set operation on the block.
+
+Block operations in hardware are only possible where the respective GPIO driver
+implements it, falling back to using single GPIO operations where the driver
+only implements single GPIO access.
+
+If a GPIO block includes GPIOs from several chips, the chips are handled one
+after another in the order of first specification in the list of GPIOs defined
+in the GPIO block, starting with bit 0. Note that in this case, you typically
+can't assume simultaneous access.
+
+void gpio_block_free(struct gpio_block *block);
+
+After the GPIO block isn't used anymore, it should be free'd via
+gpio_block_free().
+
+int gpio_block_register(struct gpio_block *block);
+void gpio_block_unregister(struct gpio_block *block);
+
+These functions can be used to register a GPIO block. Blocks registered this
+way will be available via userspace API.
+
+
 What do these conventions omit?
 ===============================
 One of the biggest things these conventions omit is pin multiplexing, since
--- linux-2.6.orig/drivers/gpio/gpiolib.c
+++ linux-2.6/drivers/gpio/gpiolib.c
@@ -83,6 +83,8 @@ static inline void desc_set_label(struct
 #endif
 }
 
+static LIST_HEAD(gpio_block_list);
+
 /* Warn when drivers omit gpio_request() calls -- legal but ill-advised
  * when setting direction, and otherwise illegal.  Until board setup code
  * and drivers use explicit requests everywhere (which won't happen when
@@ -217,6 +219,16 @@ static int gpio_get_direction(unsigned g
 	return status;
 }
 
+static bool gpio_block_is_output(struct gpio_block *block)
+{
+	int i;
+
+	for (i = 0; i < block->ngpio; i++)
+		if (!test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags))
+			return false;
+	return true;
+}
+
 #ifdef CONFIG_GPIO_SYSFS
 
 /* lock protects against unexport_gpio() being called while
@@ -1799,6 +1811,221 @@ void __gpio_set_value(unsigned gpio, int
 }
 EXPORT_SYMBOL_GPL(__gpio_set_value);
 
+static struct gpio_block_chip *gpio_block_chip_get(struct gpio_block *block,
+						   struct gpio_chip *gc)
+{
+	struct gpio_block_chip *i;
+
+	list_for_each_entry(i, &block->gbc_list, list)
+		if (i->gc == gc)
+			return i;
+	return NULL;
+}
+
+struct gpio_block *gpio_block_create(unsigned *gpios, size_t size,
+				     const char *name)
+{
+	struct gpio_block *block;
+	struct gpio_block_chip *gbc;
+	struct gpio_remap *remap;
+	int i;
+
+	if (size < 1 || size > BITS_PER_LONG)
+		return ERR_PTR(-EINVAL);
+
+	for (i = 0; i < size; i++)
+		if (!gpio_is_valid(gpios[i]))
+			return ERR_PTR(-EINVAL);
+
+	block = kzalloc(sizeof(struct gpio_block), GFP_KERNEL);
+	if (!block)
+		return ERR_PTR(-ENOMEM);
+
+	block->name = name;
+	block->ngpio = size;
+	INIT_LIST_HEAD(&block->gbc_list);
+	block->gpio = kzalloc(sizeof(*block->gpio) * size, GFP_KERNEL);
+	if (!block->gpio)
+		goto err;
+
+	memcpy(block->gpio, gpios, sizeof(*block->gpio) * size);
+
+	for (i = 0; i < size; i++) {
+		struct gpio_chip *gc = gpio_to_chip(gpios[i]);
+		int bit = gpios[i] - gc->base;
+		gbc = gpio_block_chip_get(block, gc);
+
+		if (!gbc) {
+			gbc = kzalloc(sizeof(struct gpio_block_chip),
+				      GFP_KERNEL);
+			if (!gbc)
+				goto err;
+			list_add_tail(&gbc->list, &block->gbc_list);
+			gbc->gc = gc;
+			INIT_LIST_HEAD(&gbc->remap_list);
+		}
+
+		/*
+		 * represents the mask necessary on calls to the driver's
+		 * .get_block() and .set_block()
+		 */
+		gbc->mask |= BIT(bit);
+
+		/*
+		 * collect gpios that are specified together, represented by
+		 * neighboring bits
+		 *
+		 * get last element and create new element if list empty or
+		 * new element necessary
+		 */
+		remap = list_entry(gbc->remap_list.prev, struct gpio_remap,
+				   list);
+		if (list_empty(&gbc->remap_list) ||
+		    (bit - i != remap->offset)) {
+			remap = kzalloc(sizeof(struct gpio_remap), GFP_KERNEL);
+			if (!remap)
+				goto err;
+			list_add_tail(&remap->list, &gbc->remap_list);
+			remap->offset = bit - i;
+		}
+
+		/*
+		 * represents the mask necessary for bit reordering between
+		 * gpio_block (i.e. as specified on gpio_block_get() and
+		 * gpio_block_set()) and gpio_chip domain (i.e. as specified on
+		 * the driver's .set_block() and .get_block())
+		 */
+		remap->mask |= BIT(i);
+	}
+
+	return block;
+err:
+	gpio_block_free(block);
+	return ERR_PTR(-ENOMEM);
+}
+EXPORT_SYMBOL_GPL(gpio_block_create);
+
+void gpio_block_free(struct gpio_block *block)
+{
+	struct list_head *i, *itmp, *j, *jtmp;
+	struct gpio_block_chip *gbc;
+	struct gpio_remap *remap;
+
+	list_for_each_safe(i, itmp, &block->gbc_list) {
+		gbc = list_entry(i, struct gpio_block_chip, list);
+		list_del(i);
+		list_for_each_safe(j, jtmp, &gbc->remap_list) {
+		       remap = list_entry(j, struct gpio_remap, list);
+		       list_del(j);
+		       kfree(remap);
+		}
+		kfree(gbc);
+	}
+
+	kfree(block->gpio);
+	kfree(block);
+}
+EXPORT_SYMBOL_GPL(gpio_block_free);
+
+unsigned long gpio_block_get(const struct gpio_block *block, unsigned long mask)
+{
+	struct gpio_block_chip *gbc;
+	unsigned long values = 0;
+
+	list_for_each_entry(gbc, &block->gbc_list, list) {
+		struct gpio_remap *remap;
+		int i;
+		unsigned long remapped = 0;
+		unsigned long remapped_mask = 0;
+
+		list_for_each_entry(remap, &gbc->remap_list, list)
+			remapped_mask |= (mask & remap->mask) << remap->offset;
+		remapped_mask &= gbc->mask;
+
+		if (!remapped_mask)
+			continue;
+
+		if (gbc->gc->get_block)
+			remapped = gbc->gc->get_block(gbc->gc, remapped_mask);
+		else
+			/* emulate */
+			for_each_set_bit(i, &remapped_mask, BITS_PER_LONG)
+				remapped |= gbc->gc->get(gbc->gc,
+						gbc->gc->base + i) << i;
+
+		list_for_each_entry(remap, &gbc->remap_list, list)
+			values |= (remapped >> remap->offset) & remap->mask;
+	}
+
+	return values;
+}
+EXPORT_SYMBOL_GPL(gpio_block_get);
+
+void gpio_block_set(struct gpio_block *block, unsigned long mask,
+		    unsigned long values)
+{
+	struct gpio_block_chip *gbc;
+
+	list_for_each_entry(gbc, &block->gbc_list, list) {
+		struct gpio_remap *remap;
+		int i;
+		unsigned long remapped = 0;
+		unsigned long remapped_mask = 0;
+
+		list_for_each_entry(remap, &gbc->remap_list, list) {
+			remapped |= (values & remap->mask) << remap->offset;
+			remapped_mask |= (mask & remap->mask) << remap->offset;
+		}
+		remapped_mask &= gbc->mask;
+
+		if (!remapped_mask)
+			continue;
+
+		if (gbc->gc->set_block)
+			gbc->gc->set_block(gbc->gc, remapped_mask, remapped);
+		else
+			/* emulate */
+			for_each_set_bit(i, &remapped_mask, BITS_PER_LONG)
+				gbc->gc->set(gbc->gc, gbc->gc->base + i,
+					     (remapped >> i) & 1);
+	}
+}
+EXPORT_SYMBOL_GPL(gpio_block_set);
+
+struct gpio_block *gpio_block_find_by_name(const char *name)
+{
+	struct gpio_block *i;
+
+	list_for_each_entry(i, &gpio_block_list, list)
+		if (!strcmp(i->name, name))
+			return i;
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(gpio_block_find_by_name);
+
+int gpio_block_register(struct gpio_block *block)
+{
+	if (gpio_block_find_by_name(block->name))
+		return -EBUSY;
+
+	list_add(&block->list, &gpio_block_list);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gpio_block_register);
+
+void gpio_block_unregister(struct gpio_block *block)
+{
+	struct gpio_block *i;
+
+	list_for_each_entry(i, &gpio_block_list, list)
+		if (i == block) {
+			list_del(&i->list);
+			break;
+		}
+}
+EXPORT_SYMBOL_GPL(gpio_block_unregister);
+
 /**
  * __gpio_cansleep() - report whether gpio value access will sleep
  * @gpio: gpio in question
--- linux-2.6.orig/include/asm-generic/gpio.h
+++ linux-2.6/include/asm-generic/gpio.h
@@ -44,6 +44,7 @@ static inline bool gpio_is_valid(int num
 
 struct device;
 struct gpio;
+struct gpio_block;
 struct seq_file;
 struct module;
 struct device_node;
@@ -109,6 +110,8 @@ struct gpio_chip {
 						unsigned offset);
 	int			(*get)(struct gpio_chip *chip,
 						unsigned offset);
+	unsigned long		(*get_block)(struct gpio_chip *chip,
+					     unsigned long mask);
 	int			(*direction_output)(struct gpio_chip *chip,
 						unsigned offset, int value);
 	int			(*set_debounce)(struct gpio_chip *chip,
@@ -116,6 +119,9 @@ struct gpio_chip {
 
 	void			(*set)(struct gpio_chip *chip,
 						unsigned offset, int value);
+	void			(*set_block)(struct gpio_chip *chip,
+					     unsigned long mask,
+					     unsigned long values);
 
 	int			(*to_irq)(struct gpio_chip *chip,
 						unsigned offset);
@@ -184,6 +190,17 @@ extern void gpio_set_value_cansleep(unsi
 extern int __gpio_get_value(unsigned gpio);
 extern void __gpio_set_value(unsigned gpio, int value);
 
+extern struct gpio_block *gpio_block_create(unsigned *gpio, size_t size,
+					    const char *name);
+extern void gpio_block_free(struct gpio_block *block);
+extern unsigned long gpio_block_get(const struct gpio_block *block,
+				    unsigned long mask);
+extern void gpio_block_set(struct gpio_block *block, unsigned long mask,
+			   unsigned long values);
+extern struct gpio_block *gpio_block_find_by_name(const char *name);
+extern int gpio_block_register(struct gpio_block *block);
+extern void gpio_block_unregister(struct gpio_block *block);
+
 extern int __gpio_cansleep(unsigned gpio);
 
 extern int __gpio_to_irq(unsigned gpio);
--- linux-2.6.orig/include/linux/gpio.h
+++ linux-2.6/include/linux/gpio.h
@@ -2,6 +2,8 @@
 #define __LINUX_GPIO_H
 
 #include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/list.h>
 
 /* see Documentation/gpio.txt */
 
@@ -39,6 +41,64 @@ struct gpio {
 	const char	*label;
 };
 
+/**
+ * struct gpio_remap - a structure for describing a bit mapping
+ * @mask:	a bit mask, relevant for a (partial) mapping
+ * @offset:	how many bits to shift to the left (negative: to the right)
+ * @list:	several remappings are internally collected in a list
+ *
+ * When we are mapping bit values from one word to another (here: from GPIO
+ * block domain to GPIO driver domain) we first mask them out with mask and
+ * shift them as specified with offset. More complicated mappings are done by
+ * grouping several of those structs and adding the results together.
+ */
+struct gpio_remap {
+	unsigned long		mask;
+	int			offset;
+
+	struct list_head	list;
+};
+
+/**
+ * struct gpio_block_chip - a structure representing chip specific data in a
+ *                          gpio block
+ * @gc:          the chip
+ * @remap_list:  list of remappings, there are several necessary if the bits
+ *               are not consecutive
+ * @mask:        chip specific mask, used for propagating to the driver's
+ *               get_block() and set_block() functions
+ * @list:        list collecting potentially multiple chips in one block
+ *
+ * This structure holds information about remapping and masking of gpios within
+ * one chip. There can be several of those in one block.
+ */
+struct gpio_block_chip {
+	struct gpio_chip	*gc;
+	struct list_head	remap_list;
+	unsigned long		mask;
+
+	struct list_head	list;
+};
+
+/**
+ * struct gpio_block - a structure describing a list of GPIOs for simultaneous
+ *                     operations
+ * @gbc_list:   list of chips in this block, typically just one
+ * @name:       the name of this block
+ * @ngpio:      number of gpios in this block
+ * @gpio:       list of gpios in this block
+ * @list:       global list of blocks, maintained by gpiolib
+ */
+struct gpio_block {
+	struct list_head	gbc_list;
+	const char		*name;
+
+	int			ngpio;
+	unsigned		*gpio;
+
+	struct list_head	list;
+};
+
 #ifdef CONFIG_GENERIC_GPIO
 
 #ifdef CONFIG_ARCH_HAVE_CUSTOM_GPIO_H
@@ -169,6 +229,43 @@ static inline void gpio_set_value(unsign
 	WARN_ON(1);
 }
 
+static inline
+struct gpio_block *gpio_block_create(unsigned *gpios, size_t size,
+				     const char *name)
+{
+	WARN_ON(1);
+	return NULL;
+}
+
+static inline void gpio_block_free(struct gpio_block *block)
+{
+	WARN_ON(1);
+}
+
+static inline unsigned long gpio_block_get(const struct gpio_block *block,
+					   unsigned long mask)
+{
+	WARN_ON(1);
+	return 0;
+}
+
+static inline void gpio_block_set(struct gpio_block *block, unsigned long mask,
+				  unsigned long values)
+{
+	WARN_ON(1);
+}
+
+static inline int gpio_block_register(struct gpio_block *block)
+{
+	WARN_ON(1);
+	return 0;
+}
+
+static inline void gpio_block_unregister(struct gpio_block *block)
+{
+	WARN_ON(1);
+}
+
 static inline int gpio_cansleep(unsigned gpio)
 {
 	/* GPIO can never have been requested or set as {in,out}put */

^ permalink raw reply

* [PATCH RESEND 2/6 v13] gpio: Add sysfs support to block GPIO API
From: Roland Stigge @ 2013-01-15 11:51 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358250716-21986-1-git-send-email-stigge@antcom.de>

This patch adds sysfs support to the block GPIO API.

Signed-off-by: Roland Stigge <stigge@antcom.de>

---
 Documentation/ABI/testing/sysfs-gpio |   20 ++
 drivers/gpio/gpiolib.c               |  252 ++++++++++++++++++++++++++++++++++-
 include/asm-generic/gpio.h           |   11 +
 include/linux/gpio.h                 |   15 ++
 4 files changed, 297 insertions(+), 1 deletion(-)

--- linux-2.6.orig/Documentation/ABI/testing/sysfs-gpio
+++ linux-2.6/Documentation/ABI/testing/sysfs-gpio
@@ -25,3 +25,23 @@ Description:
 	    /label ... (r/o) descriptive, not necessarily unique
 	    /ngpio ... (r/o) number of GPIOs; numbered N to N + (ngpio - 1)
 
+What:		/sys/class/gpioblock/
+Date:		October 2012
+KernelVersion:	3.7
+Contact:	Roland Stigge <stigge@antcom.de>
+Description:
+
+  Block GPIO devices are visible in sysfs as soon as they are registered
+  (e.g. via devicetree definition). For actual I/O use, their "exported"
+  boolean attribute must be set to "1". Then, the attribute "values" is
+  created and at the same time, the GPIOs in the block are requested for
+  exclusive use by sysfs.
+
+    /sys/class/gpioblock
+	/BLOCKNAME ... for each GPIO block name
+	    /ngpio ... (r/o) number of GPIOs in this group
+	    /exported ... sysfs export state of this group (0, 1)
+	    /value ... current value as 32 or 64 bit integer in hex
+                       (only available if /exported is 1)
+	    /mask ... current mask as 32 or 64 bit integer in hex
+                       (only available if /exported is 1)
--- linux-2.6.orig/drivers/gpio/gpiolib.c
+++ linux-2.6/drivers/gpio/gpiolib.c
@@ -224,7 +224,8 @@ static bool gpio_block_is_output(struct
 	int i;
 
 	for (i = 0; i < block->ngpio; i++)
-		if (!test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags))
+		if ((block->cur_mask & BIT(i)) &&
+		    !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags))
 			return false;
 	return true;
 }
@@ -1020,6 +1021,240 @@ static void gpiochip_unexport(struct gpi
 				chip->label, status);
 }
 
+static ssize_t gpio_block_ngpio_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	const struct gpio_block	*block = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", block->ngpio);
+}
+
+static ssize_t gpio_block_value_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	const struct gpio_block *block = dev_get_drvdata(dev);
+
+	return sprintf(buf, sizeof(unsigned long) == 4 ? "0x%08lx\n" :
+		       "0x%016lx\n", gpio_block_get(block, block->cur_mask));
+}
+
+static ssize_t gpio_block_value_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t size)
+{
+	ssize_t			status;
+	struct gpio_block	*block = dev_get_drvdata(dev);
+	unsigned long		value;
+
+	status = kstrtoul(buf, 0, &value);
+	if (status == 0) {
+		mutex_lock(&sysfs_lock);
+		if (gpio_block_is_output(block)) {
+			gpio_block_set(block, block->cur_mask, value);
+			status = size;
+		} else {
+			status = -EPERM;
+		}
+		mutex_unlock(&sysfs_lock);
+	}
+	return status;
+}
+
+static struct device_attribute
+dev_attr_block_value = __ATTR(value, S_IWUSR | S_IRUGO, gpio_block_value_show,
+			      gpio_block_value_store);
+
+static ssize_t gpio_block_mask_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	const struct gpio_block *block = dev_get_drvdata(dev);
+
+	return sprintf(buf, sizeof(unsigned long) == 4 ? "0x%08lx\n" :
+		       "0x%016lx\n", block->cur_mask);
+}
+
+static ssize_t gpio_block_mask_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t size)
+{
+	ssize_t			status;
+	struct gpio_block	*block = dev_get_drvdata(dev);
+	unsigned long		mask;
+
+	status = kstrtoul(buf, 0, &mask);
+	if (status == 0) {
+		block->cur_mask = mask;
+		status = size;
+	}
+	return status;
+}
+
+static struct device_attribute
+dev_attr_block_mask = __ATTR(mask, S_IWUSR | S_IRUGO, gpio_block_mask_show,
+			     gpio_block_mask_store);
+
+static struct class gpio_block_class;
+
+static int gpio_block_value_is_exported(struct gpio_block *block)
+{
+	struct device		*dev;
+	struct sysfs_dirent	*sd = NULL;
+
+	mutex_lock(&sysfs_lock);
+	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
+	if (!dev)
+		goto out;
+
+	sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value");
+
+out:
+	mutex_unlock(&sysfs_lock);
+	return !!sd;
+}
+
+static ssize_t gpio_block_exported_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct gpio_block	*block = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", gpio_block_value_is_exported(block));
+}
+
+static int gpio_block_value_export(struct gpio_block *block)
+{
+	struct device	*dev;
+	int		status;
+	int		i;
+
+	mutex_lock(&sysfs_lock);
+
+	for (i = 0; i < block->ngpio; i++) {
+		status = gpio_request(block->gpio[i], "sysfs");
+		if (status)
+			goto out1;
+	}
+
+	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
+	if (!dev) {
+		status = -ENODEV;
+		goto out1;
+	}
+
+	status = device_create_file(dev, &dev_attr_block_value);
+	if (status)
+		goto out1;
+
+	status = device_create_file(dev, &dev_attr_block_mask);
+	if (status)
+		goto out2;
+
+	mutex_unlock(&sysfs_lock);
+	return 0;
+
+out2:
+	device_remove_file(dev, &dev_attr_block_value);
+out1:
+	while (--i >= 0)
+		gpio_free(block->gpio[i]);
+
+	mutex_unlock(&sysfs_lock);
+	return status;
+}
+
+static int gpio_block_value_unexport(struct gpio_block *block)
+{
+	struct device	*dev;
+	int		i;
+
+	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
+	if (!dev)
+		return -ENODEV;
+
+	for (i = 0; i < block->ngpio; i++)
+		gpio_free(block->gpio[i]);
+
+	device_remove_file(dev, &dev_attr_block_value);
+	device_remove_file(dev, &dev_attr_block_mask);
+
+	return 0;
+}
+
+static ssize_t gpio_block_exported_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t size)
+{
+	long	value;
+	int	status;
+	struct	gpio_block *block = dev_get_drvdata(dev);
+	int	exported = gpio_block_value_is_exported(block);
+
+	status = kstrtoul(buf, 0, &value);
+	if (status < 0)
+		goto err;
+
+	if (value != exported) {
+		if (value)
+			status = gpio_block_value_export(block);
+		else
+			status = gpio_block_value_unexport(block);
+		if (!status)
+			status = size;
+	} else {
+		status = size;
+	}
+err:
+	return status;
+}
+
+static struct device_attribute gpio_block_attrs[] = {
+	__ATTR(exported, S_IWUSR | S_IRUGO, gpio_block_exported_show,
+	       gpio_block_exported_store),
+	__ATTR(ngpio, S_IRUGO, gpio_block_ngpio_show, NULL),
+	__ATTR_NULL,
+};
+
+static struct class gpio_block_class = {
+	.name =         "gpioblock",
+	.owner =        THIS_MODULE,
+
+	.dev_attrs =  gpio_block_attrs,
+};
+
+int gpio_block_export(struct gpio_block *block)
+{
+	int		status = 0;
+	struct device	*dev;
+
+	/* can't export until sysfs is available ... */
+	if (!gpio_block_class.p) {
+		pr_debug("%s: called too early!\n", __func__);
+		return -ENOENT;
+	}
+
+	mutex_lock(&sysfs_lock);
+	dev = device_create(&gpio_block_class, NULL, MKDEV(0, 0), block,
+			    block->name);
+	if (IS_ERR(dev))
+		status = PTR_ERR(dev);
+	mutex_unlock(&sysfs_lock);
+
+	return status;
+}
+EXPORT_SYMBOL_GPL(gpio_block_export);
+
+void gpio_block_unexport(struct gpio_block *block)
+{
+	struct device *dev;
+
+	mutex_lock(&sysfs_lock);
+	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
+	if (dev)
+		device_unregister(dev);
+	mutex_unlock(&sysfs_lock);
+}
+EXPORT_SYMBOL_GPL(gpio_block_unexport);
+
 static int __init gpiolib_sysfs_init(void)
 {
 	int		status;
@@ -1030,6 +1265,10 @@ static int __init gpiolib_sysfs_init(voi
 	if (status < 0)
 		return status;
 
+	status = class_register(&gpio_block_class);
+	if (status < 0)
+		return status;
+
 	/* Scan and register the gpio_chips which registered very
 	 * early (e.g. before the class_register above was called).
 	 *
@@ -1843,6 +2082,7 @@ struct gpio_block *gpio_block_create(uns
 
 	block->name = name;
 	block->ngpio = size;
+	block->cur_mask = ~0;
 	INIT_LIST_HEAD(&block->gbc_list);
 	block->gpio = kzalloc(sizeof(*block->gpio) * size, GFP_KERNEL);
 	if (!block->gpio)
@@ -2005,12 +2245,21 @@ EXPORT_SYMBOL_GPL(gpio_block_find_by_nam
 
 int gpio_block_register(struct gpio_block *block)
 {
+	int ret;
+
 	if (gpio_block_find_by_name(block->name))
 		return -EBUSY;
 
 	list_add(&block->list, &gpio_block_list);
 
+	ret = gpio_block_export(block);
+	if (ret)
+		goto err1;
+
 	return 0;
+err1:
+	list_del(&block->list);
+	return ret;
 }
 EXPORT_SYMBOL_GPL(gpio_block_register);
 
@@ -2021,6 +2270,7 @@ void gpio_block_unregister(struct gpio_b
 	list_for_each_entry(i, &gpio_block_list, list)
 		if (i == block) {
 			list_del(&i->list);
+			gpio_block_unexport(block);
 			break;
 		}
 }
--- linux-2.6.orig/include/asm-generic/gpio.h
+++ linux-2.6/include/asm-generic/gpio.h
@@ -226,6 +226,8 @@ extern int gpio_export_link(struct devic
 			unsigned gpio);
 extern int gpio_sysfs_set_active_low(unsigned gpio, int value);
 extern void gpio_unexport(unsigned gpio);
+extern int gpio_block_export(struct gpio_block *block);
+extern void gpio_block_unexport(struct gpio_block *block);
 
 #endif	/* CONFIG_GPIO_SYSFS */
 
@@ -285,6 +287,15 @@ static inline int gpio_sysfs_set_active_
 static inline void gpio_unexport(unsigned gpio)
 {
 }
+
+static inline int gpio_block_export(struct gpio_block *block)
+{
+	return -ENOSYS;
+}
+
+static inline void gpio_block_unexport(struct gpio_block *block)
+{
+}
 #endif	/* CONFIG_GPIO_SYSFS */
 
 #ifdef CONFIG_PINCTRL
--- linux-2.6.orig/include/linux/gpio.h
+++ linux-2.6/include/linux/gpio.h
@@ -88,6 +88,7 @@ struct gpio_block_chip {
  * @ngpio:      number of gpios in this block
  * @gpio:       list of gpios in this block
  * @list:       global list of blocks, maintained by gpiolib
+ * @cur_mask:   currently used gpio mask used by userspace API
  */
 struct gpio_block {
 	struct list_head	gbc_list;
@@ -97,6 +98,7 @@ struct gpio_block {
 	unsigned		*gpio;
 
 	struct list_head	list;
+	unsigned long		cur_mask;
 };
 
 #ifdef CONFIG_GENERIC_GPIO
@@ -314,6 +316,19 @@ static inline void gpio_unexport(unsigne
 	WARN_ON(1);
 }
 
+static inline int gpio_block_export(struct gpio_block *block)
+{
+	/* GPIO block can never have been requested or set as {in,out}put */
+	WARN_ON(1);
+	return -EINVAL;
+}
+
+static inline void gpio_block_unexport(struct gpio_block *block)
+{
+	/* GPIO block can never have been exported */
+	WARN_ON(1);
+}
+
 static inline int gpio_to_irq(unsigned gpio)
 {
 	/* GPIO can never have been requested or set as input */

^ permalink raw reply

* [PATCH RESEND 3/6 v13] gpio: Add userland device interface to block GPIO
From: Roland Stigge @ 2013-01-15 11:51 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358250716-21986-1-git-send-email-stigge@antcom.de>

This patch adds a character device interface to the block GPIO system.

Signed-off-by: Roland Stigge <stigge@antcom.de>
---
 Documentation/ABI/testing/dev-gpioblock |   34 ++++
 drivers/gpio/gpiolib.c                  |  225 +++++++++++++++++++++++++++++++-
 include/linux/gpio.h                    |   13 +
 3 files changed, 271 insertions(+), 1 deletion(-)

--- /dev/null
+++ linux-2.6/Documentation/ABI/testing/dev-gpioblock
@@ -0,0 +1,34 @@
+What:		/dev/<gpioblock>
+Date:		Nov 2012
+KernelVersion:	3.7
+Contact:	Roland Stigge <stigge@antcom.de>
+Description:	The /dev/<gpioblock> character device node provides userspace
+		access to GPIO blocks, named exactly as the block, e.g.
+		/dev/block0.
+
+		Reading:
+		When reading sizeof(unsigned long) bytes from the device, the
+		current state of the block, masked by the current mask (see
+		below) can be obtained as a word. When the device is opened
+		with O_NONBLOCK, read() always returns with data immediately,
+		otherwise it blocks until data is available, see IRQ handling
+		below.
+
+		Writing:
+		By writing sizeof(unsigned long) bytes to the device, the
+		current state of the block can be set. This operation is
+		masked by the current mask (see below).
+
+		IRQ handling:
+		When one or more IRQs in the block are IRQ capable, you can
+		poll() on the device to check/wait for this IRQ. If no IRQ
+		is available, poll() returns -ENOSYS and userspace needs to
+		(busy) poll itself if necessary.
+
+		Setting the mask (default: all bits set):
+		By doing an ioctl(fd, 0, &mask) with an unsigned long mask, the
+		current mask for read and write operations on this gpio block
+		can be set.
+
+		See also Documentation/gpio.txt for an explanation of block
+		GPIO.
--- linux-2.6.orig/drivers/gpio/gpiolib.c
+++ linux-2.6/drivers/gpio/gpiolib.c
@@ -11,6 +11,8 @@
 #include <linux/of_gpio.h>
 #include <linux/idr.h>
 #include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/poll.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/gpio.h>
@@ -2243,6 +2245,207 @@ struct gpio_block *gpio_block_find_by_na
 }
 EXPORT_SYMBOL_GPL(gpio_block_find_by_name);
 
+static struct gpio_block *gpio_block_find_by_minor(int minor)
+{
+	struct gpio_block *i;
+
+	list_for_each_entry(i, &gpio_block_list, list)
+		if (i->miscdev.minor == minor)
+			return i;
+	return NULL;
+}
+
+static bool gpio_block_is_irq_duplicate(struct gpio_block *block, int index)
+{
+	int irq = gpio_to_irq(block->gpio[index]);
+	int i;
+
+	for (i = 0; i < index; i++)
+		if (gpio_to_irq(block->gpio[i]) == irq)
+			return true;
+	return false;
+}
+
+static irqreturn_t gpio_block_irq_handler(int irq, void *dev)
+{
+	struct gpio_block *block = dev;
+
+	wake_up_interruptible(&block->wait_queue);
+	block->got_int = true;
+
+	return IRQ_HANDLED;
+}
+
+static int gpio_block_fop_open(struct inode *in, struct file *f)
+{
+	int i;
+	struct gpio_block *block = gpio_block_find_by_minor(MINOR(in->i_rdev));
+	int status;
+	int irq;
+
+	if (!block)
+		return -ENOENT;
+
+	block->irq_controlled = false;
+	block->got_int = false;
+	spin_lock_init(&block->lock);
+	init_waitqueue_head(&block->wait_queue);
+	f->private_data = block;
+
+	for (i = 0; i < block->ngpio; i++) {
+		status = gpio_request(block->gpio[i], block->name);
+		if (status)
+			goto err1;
+
+		irq = gpio_to_irq(block->gpio[i]);
+		if (irq >= 0 &&
+		    !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags) &&
+		    !gpio_block_is_irq_duplicate(block, i)) {
+			status = request_irq(irq, gpio_block_irq_handler,
+					     IRQF_SHARED,
+					     block->name, block);
+			if (status)
+				goto err2;
+
+			block->irq_controlled = true;
+		}
+	}
+
+	return 0;
+
+err1:
+	while (i > 0) {
+		i--;
+
+		irq = gpio_to_irq(block->gpio[i]);
+		if (irq >= 0 &&
+		    !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags) &&
+		    !gpio_block_is_irq_duplicate(block, i))
+			free_irq(irq, block);
+err2:
+		gpio_free(block->gpio[i]);
+	}
+	return status;
+}
+
+static int gpio_block_fop_release(struct inode *in, struct file *f)
+{
+	int i;
+	struct gpio_block *block = (struct gpio_block *)f->private_data;
+
+	for (i = 0; i < block->ngpio; i++) {
+		int irq = gpio_to_irq(block->gpio[i]);
+
+		if (irq >= 0 &&
+		    !test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags) &&
+		    !gpio_block_is_irq_duplicate(block, i))
+			free_irq(irq, block);
+
+		gpio_free(block->gpio[i]);
+	}
+
+	return 0;
+}
+
+static int got_int(struct gpio_block *block)
+{
+	unsigned long flags;
+	int result;
+
+	spin_lock_irqsave(&block->lock, flags);
+	result = block->got_int;
+	spin_unlock_irqrestore(&block->lock, flags);
+
+	return result;
+}
+
+static ssize_t gpio_block_fop_read(struct file *f, char __user *buf, size_t n,
+				   loff_t *offset)
+{
+	struct gpio_block *block = (struct gpio_block *)f->private_data;
+	int err;
+	unsigned long flags;
+
+	if (block->irq_controlled) {
+		if (!(f->f_flags & O_NONBLOCK))
+			wait_event_interruptible(block->wait_queue,
+						 got_int(block));
+		spin_lock_irqsave(&block->lock, flags);
+		block->got_int = 0;
+		spin_unlock_irqrestore(&block->lock, flags);
+	}
+
+	if (n >= sizeof(unsigned long)) {
+		unsigned long values = gpio_block_get(block, block->cur_mask);
+
+		err = put_user(values, (unsigned long __user *)buf);
+		if (err)
+			return err;
+
+		return sizeof(unsigned long);
+	}
+	return 0;
+}
+
+static ssize_t gpio_block_fop_write(struct file *f, const char __user *buf,
+				    size_t n, loff_t *offset)
+{
+	struct gpio_block *block = (struct gpio_block *)f->private_data;
+	int err;
+
+	if (n >= sizeof(unsigned long)) {
+		unsigned long values;
+
+		err = get_user(values, (unsigned long __user *)buf);
+		if (err)
+			return err;
+		if (gpio_block_is_output(block))
+			gpio_block_set(block, block->cur_mask, values);
+		else
+			return -EPERM;
+		return sizeof(unsigned long);
+	}
+	return 0;
+}
+
+static long gpio_block_fop_ioctl(struct file *f, unsigned int cmd,
+				 unsigned long arg)
+{
+	struct gpio_block *block = (struct gpio_block *)f->private_data;
+	unsigned long __user *x = (unsigned long __user *)arg;
+
+	if (cmd == 0)
+		return get_user(block->cur_mask, x);
+
+	return -EINVAL;
+}
+
+static unsigned int gpio_block_fop_poll(struct file *f,
+					struct poll_table_struct *pt)
+{
+	struct gpio_block *block = (struct gpio_block *)f->private_data;
+
+	if (!block->irq_controlled)
+		return -ENOSYS;
+
+	if (!got_int(block))
+		poll_wait(f, &block->wait_queue, pt);
+
+	if (got_int(block))
+		return POLLIN;
+
+	return 0;
+}
+
+static const struct file_operations gpio_block_fops = {
+	.open = gpio_block_fop_open,
+	.release = gpio_block_fop_release,
+	.read = gpio_block_fop_read,
+	.write = gpio_block_fop_write,
+	.unlocked_ioctl = gpio_block_fop_ioctl,
+	.poll = gpio_block_fop_poll,
+};
+
 int gpio_block_register(struct gpio_block *block)
 {
 	int ret;
@@ -2253,12 +2456,31 @@ int gpio_block_register(struct gpio_bloc
 	list_add(&block->list, &gpio_block_list);
 
 	ret = gpio_block_export(block);
-	if (ret)
+
+	/*
+	 * ret == ENOSYS is the case where GPIO_SYSFS is deactivated. In this
+	 * case, we can continue safely anyway, since we can provide the device
+	 * interface.
+	 */
+	if (ret && ret != -ENOSYS)
 		goto err1;
 
+	block->miscdev.name = block->name;
+	block->miscdev.nodename = block->name;
+	block->miscdev.minor = MISC_DYNAMIC_MINOR;
+	block->miscdev.fops = &gpio_block_fops;
+	block->miscdev.mode = S_IWUSR | S_IRUGO;
+
+	ret = misc_register(&block->miscdev);
+	if (ret)
+		goto err2;
+
 	return 0;
+err2:
+	gpio_block_unexport(block);
 err1:
 	list_del(&block->list);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(gpio_block_register);
@@ -2271,6 +2493,7 @@ void gpio_block_unregister(struct gpio_b
 		if (i == block) {
 			list_del(&i->list);
 			gpio_block_unexport(block);
+			misc_deregister(&block->miscdev);
 			break;
 		}
 }
--- linux-2.6.orig/include/linux/gpio.h
+++ linux-2.6/include/linux/gpio.h
@@ -4,6 +4,9 @@
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
 
 /* see Documentation/gpio.txt */
 
@@ -89,6 +92,11 @@ struct gpio_block_chip {
  * @gpio:       list of gpios in this block
  * @list:       global list of blocks, maintained by gpiolib
  * @cur_mask:   currently used gpio mask used by userspace API
+ * @miscdev:    userspace API: device
+ * @wait_queue: userspace API: wait queue waiting for IRQ
+ * @irq_controlled: userspace API: flag: using IRQ or not
+ * @got_int:    userspace API: change detection via IRQ
+ * @lock:	userspace API: spinlock for IRQ manipulated data
  */
 struct gpio_block {
 	struct list_head	gbc_list;
@@ -99,6 +107,11 @@ struct gpio_block {
 
 	struct list_head	list;
 	unsigned long		cur_mask;
+	struct miscdevice	miscdev;
+	wait_queue_head_t	wait_queue;
+	bool			irq_controlled;
+	bool			got_int;
+	spinlock_t		lock;
 };
 
 #ifdef CONFIG_GENERIC_GPIO

^ permalink raw reply

* [PATCH RESEND 4/6 v13] gpiolib: Fix default attributes for class
From: Roland Stigge @ 2013-01-15 11:51 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358250716-21986-1-git-send-email-stigge@antcom.de>

There is a race condition between creating a gpio or gpiochip device and adding
default attributes. This patch fixes this by defining the default attributes as
dev_attrs of the class. For this, it was necessary to create a separate
gpiochip_class besides gpio_class.

Signed-off-by: Roland Stigge <stigge@antcom.de>
---
 Documentation/ABI/testing/sysfs-gpio |   11 ++++
 drivers/gpio/gpiolib.c               |   78 ++++++++++++++---------------------
 2 files changed, 44 insertions(+), 45 deletions(-)

--- linux-2.6.orig/Documentation/ABI/testing/sysfs-gpio
+++ linux-2.6/Documentation/ABI/testing/sysfs-gpio
@@ -20,6 +20,17 @@ Description:
 	    /value ... always readable, writes fail for input GPIOs
 	    /direction ... r/w as: in, out (default low); write: high, low
 	    /edge ... r/w as: none, falling, rising, both
+
+What:		/sys/class/gpiochip/
+Date:		October 2012
+KernelVersion:	3.7
+Contact:	Roland Stigge <stigge@antcom.de>
+Description:
+
+  Each gpiochip is represented by a separate device having the following
+  attributes:
+
+    /sys/class/gpiochip
 	/gpiochipN ... for each gpiochip; #N is its first GPIO
 	    /base ... (r/o) same as N
 	    /label ... (r/o) descriptive, not necessarily unique
--- linux-2.6.orig/drivers/gpio/gpiolib.c
+++ linux-2.6/drivers/gpio/gpiolib.c
@@ -362,9 +362,6 @@ static ssize_t gpio_value_store(struct d
 	return status;
 }
 
-static const DEVICE_ATTR(value, 0644,
-		gpio_value_show, gpio_value_store);
-
 static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
 {
 	struct sysfs_dirent	*value_sd = priv;
@@ -585,19 +582,6 @@ static ssize_t gpio_active_low_store(str
 	return status ? : size;
 }
 
-static const DEVICE_ATTR(active_low, 0644,
-		gpio_active_low_show, gpio_active_low_store);
-
-static const struct attribute *gpio_attrs[] = {
-	&dev_attr_value.attr,
-	&dev_attr_active_low.attr,
-	NULL,
-};
-
-static const struct attribute_group gpio_attr_group = {
-	.attrs = (struct attribute **) gpio_attrs,
-};
-
 /*
  * /sys/class/gpio/gpiochipN/
  *   /base ... matching gpio_chip.base (N)
@@ -612,7 +596,6 @@ static ssize_t chip_base_show(struct dev
 
 	return sprintf(buf, "%d\n", chip->base);
 }
-static DEVICE_ATTR(base, 0444, chip_base_show, NULL);
 
 static ssize_t chip_label_show(struct device *dev,
 			       struct device_attribute *attr, char *buf)
@@ -621,7 +604,6 @@ static ssize_t chip_label_show(struct de
 
 	return sprintf(buf, "%s\n", chip->label ? : "");
 }
-static DEVICE_ATTR(label, 0444, chip_label_show, NULL);
 
 static ssize_t chip_ngpio_show(struct device *dev,
 			       struct device_attribute *attr, char *buf)
@@ -630,18 +612,6 @@ static ssize_t chip_ngpio_show(struct de
 
 	return sprintf(buf, "%u\n", chip->ngpio);
 }
-static DEVICE_ATTR(ngpio, 0444, chip_ngpio_show, NULL);
-
-static const struct attribute *gpiochip_attrs[] = {
-	&dev_attr_base.attr,
-	&dev_attr_label.attr,
-	&dev_attr_ngpio.attr,
-	NULL,
-};
-
-static const struct attribute_group gpiochip_attr_group = {
-	.attrs = (struct attribute **) gpiochip_attrs,
-};
 
 /*
  * /sys/class/gpio/export ... write-only
@@ -720,11 +690,32 @@ static struct class_attribute gpio_class
 	__ATTR_NULL,
 };
 
+static struct device_attribute gpio_attrs[] = {
+	__ATTR(active_low, 0644, gpio_active_low_show, gpio_active_low_store),
+	__ATTR(value, 0644, gpio_value_show, gpio_value_store),
+	__ATTR_NULL,
+};
+
 static struct class gpio_class = {
 	.name =		"gpio",
 	.owner =	THIS_MODULE,
 
-	.class_attrs =	gpio_class_attrs,
+	.class_attrs = gpio_class_attrs,
+	.dev_attrs = gpio_attrs,
+};
+
+static struct device_attribute gpiochip_attrs[] = {
+	__ATTR(label, 0444, chip_label_show, NULL),
+	__ATTR(base, 0444, chip_base_show, NULL),
+	__ATTR(ngpio, 0444, chip_ngpio_show, NULL),
+	__ATTR_NULL,
+};
+
+static struct class gpiochip_class = {
+	.name =		"gpiochip",
+	.owner =	THIS_MODULE,
+
+	.dev_attrs =	gpiochip_attrs,
 };
 
 
@@ -791,10 +782,6 @@ int gpio_export(unsigned gpio, bool dire
 		goto fail_unlock;
 	}
 
-	status = sysfs_create_group(&dev->kobj, &gpio_attr_group);
-	if (status)
-		goto fail_unregister_device;
-
 	if (direction_may_change) {
 		status = device_create_file(dev, &dev_attr_direction);
 		if (status)
@@ -962,25 +949,22 @@ EXPORT_SYMBOL_GPL(gpio_unexport);
 
 static int gpiochip_export(struct gpio_chip *chip)
 {
-	int		status;
+	int		status = 0;
 	struct device	*dev;
 
 	/* Many systems register gpio chips for SOC support very early,
 	 * before driver model support is available.  In those cases we
 	 * export this later, in gpiolib_sysfs_init() ... here we just
-	 * verify that _some_ field of gpio_class got initialized.
+	 * verify that _some_ field of gpiochip_class got initialized.
 	 */
-	if (!gpio_class.p)
+	if (!gpiochip_class.p)
 		return 0;
 
 	/* use chip->base for the ID; it's already known to be unique */
 	mutex_lock(&sysfs_lock);
-	dev = device_create(&gpio_class, chip->dev, MKDEV(0, 0), chip,
-				"gpiochip%d", chip->base);
-	if (!IS_ERR(dev)) {
-		status = sysfs_create_group(&dev->kobj,
-				&gpiochip_attr_group);
-	} else
+	dev = device_create(&gpiochip_class, chip->dev, MKDEV(0, 0), chip,
+			    "gpiochip%d", chip->base);
+	if (IS_ERR(dev))
 		status = PTR_ERR(dev);
 	chip->exported = (status == 0);
 	mutex_unlock(&sysfs_lock);
@@ -1008,7 +992,7 @@ static void gpiochip_unexport(struct gpi
 	struct device		*dev;
 
 	mutex_lock(&sysfs_lock);
-	dev = class_find_device(&gpio_class, NULL, chip, match_export);
+	dev = class_find_device(&gpiochip_class, NULL, chip, match_export);
 	if (dev) {
 		put_device(dev);
 		device_unregister(dev);
@@ -1267,6 +1251,10 @@ static int __init gpiolib_sysfs_init(voi
 	if (status < 0)
 		return status;
 
+	status = class_register(&gpiochip_class);
+	if (status < 0)
+		return status;
+
 	status = class_register(&gpio_block_class);
 	if (status < 0)
 		return status;

^ permalink raw reply

* [PATCH RESEND 5/6 v13] gpio: Add device tree support to block GPIO API
From: Roland Stigge @ 2013-01-15 11:51 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358250716-21986-1-git-send-email-stigge@antcom.de>

This patch adds device tree support to the block GPIO API.

Signed-off-by: Roland Stigge <stigge@antcom.de>

---
 Documentation/devicetree/bindings/gpio/gpio-block.txt |   36 ++++++
 drivers/gpio/Makefile                                 |    1 
 drivers/gpio/gpioblock-of.c                           |  100 ++++++++++++++++++
 3 files changed, 137 insertions(+)

--- /dev/null
+++ linux-2.6/Documentation/devicetree/bindings/gpio/gpio-block.txt
@@ -0,0 +1,36 @@
+Block GPIO definition
+=====================
+
+This binding specifies arbitrary blocks of gpios, combining gpios from one or
+more GPIO controllers together, to form a word for r/w access.
+
+Required property:
+- compatible: must be "linux,gpio-block"
+
+Required subnodes:
+- the name will be the registered name of the block
+- property "gpios" is a list of gpios for the respective block
+
+Example:
+
+        blockgpio {
+                compatible = "linux,gpio-block";
+
+                block0 {
+                        gpios = <&gpio 3 0 0>,
+                                <&gpio 3 1 0>;
+                };
+                block1 {
+                        gpios = <&gpio 4 1 0>,
+                                <&gpio 4 3 0>,
+                                <&gpio 4 2 0>,
+                                <&gpio 4 4 0>,
+                                <&gpio 4 5 0>,
+                                <&gpio 4 6 0>,
+                                <&gpio 4 7 0>,
+                                <&gpio 4 8 0>,
+                                <&gpio 4 9 0>,
+                                <&gpio 4 10 0>,
+                                <&gpio 4 19 0>;
+                };
+        };
--- linux-2.6.orig/drivers/gpio/Makefile
+++ linux-2.6/drivers/gpio/Makefile
@@ -5,6 +5,7 @@ ccflags-$(CONFIG_DEBUG_GPIO)	+= -DDEBUG
 obj-$(CONFIG_GPIOLIB)		+= gpiolib.o devres.o
 obj-$(CONFIG_OF_GPIO)		+= gpiolib-of.o
 obj-$(CONFIG_GPIO_ACPI)		+= gpiolib-acpi.o
+obj-$(CONFIG_OF_GPIO)		+= gpioblock-of.o
 
 # Device drivers. Generally keep list sorted alphabetically
 obj-$(CONFIG_GPIO_GENERIC)	+= gpio-generic.o
--- /dev/null
+++ linux-2.6/drivers/gpio/gpioblock-of.c
@@ -0,0 +1,100 @@
+/*
+ * OF implementation for Block GPIO
+ *
+ * Copyright (C) 2012 Roland Stigge <stigge@antcom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+static int __devinit gpioblock_of_probe(struct platform_device *pdev)
+{
+	struct device_node *block;
+	unsigned *gpios;
+	int ngpio;
+	int ret;
+	struct gpio_block *gb;
+
+	for_each_available_child_of_node(pdev->dev.of_node, block) {
+		int i;
+
+		ngpio = of_gpio_count(block);
+		if (ngpio < 1) {
+			ret = -ENODEV;
+			goto err1;
+		}
+
+		gpios = kzalloc(ngpio * sizeof(*gpios), GFP_KERNEL);
+		if (!gpios) {
+			ret = -ENOMEM;
+			goto err1;
+		}
+
+		for (i = 0; i < ngpio; i++) {
+			ret = of_get_gpio(block, i);
+			if (ret < 0)
+				goto err2; /* expect -EPROBE_DEFER */
+			gpios[i] = ret;
+		}
+		gb = gpio_block_create(gpios, ngpio, block->name);
+		if (IS_ERR(gb)) {
+			dev_err(&pdev->dev,
+				"Error creating GPIO block from device tree\n");
+			ret = PTR_ERR(gb);
+			goto err2;
+		}
+		ret = gpio_block_register(gb);
+		if (ret < 0)
+			goto err3;
+
+		kfree(gpios);
+		dev_info(&pdev->dev, "Registered gpio block %s: %d gpios\n",
+			 block->name, ngpio);
+	}
+	return 0;
+
+err3:
+	gpio_block_free(gb);
+err2:
+	kfree(gpios);
+err1:
+	of_node_put(block);
+	return ret;
+}
+
+#ifdef CONFIG_OF
+static struct of_device_id gpioblock_of_match[] __devinitdata = {
+	{ .compatible = "linux,gpio-block", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, gpioblock_of_match);
+#endif
+
+static struct platform_driver gpioblock_of_driver = {
+	.driver	= {
+		.name = "gpio-block",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(gpioblock_of_match),
+
+	},
+	.probe	= gpioblock_of_probe,
+};
+
+module_platform_driver(gpioblock_of_driver);
+
+MODULE_DESCRIPTION("GPIO Block driver");
+MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:gpioblock-of");

^ permalink raw reply

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

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 +++++++++++++
 drivers/pinctrl/pinctrl-nomadik.c |   36 ++++++++++++++++
 25 files changed, 887 insertions(+)

--- linux-2.6.orig/drivers/gpio/Kconfig
+++ linux-2.6/drivers/gpio/Kconfig
@@ -144,11 +144,13 @@ config GPIO_MM_LANTIQ
 config GPIO_MPC5200
 	def_bool y
 	depends on PPC_MPC52xx
+	select BITREVERSE
 
 config GPIO_MPC8XXX
 	bool "MPC512x/MPC8xxx GPIO support"
 	depends on PPC_MPC512x || PPC_MPC831x || PPC_MPC834x || PPC_MPC837x || \
 		   FSL_SOC_BOOKE || PPC_86xx
+	select BITREVERSE
 	help
 	  Say Y here if you're going to use hardware that connects to the
 	  MPC512x/831x/834x/837x/8572/8610 GPIOs.
--- linux-2.6.orig/drivers/gpio/gpio-em.c
+++ linux-2.6/drivers/gpio/gpio-em.c
@@ -202,6 +202,27 @@ static void em_gio_set(struct gpio_chip
 		__em_gio_set(chip, GIO_OH, offset - 16, value);
 }
 
+static unsigned long em_gio_get_block(struct gpio_chip *chip,
+				      unsigned long mask)
+{
+	return (int)(em_gio_read(gpio_to_priv(chip), GIO_I) & mask);
+}
+
+static void em_gio_set_block(struct gpio_chip *chip, unsigned long mask,
+			     unsigned long values)
+{
+	unsigned long mask_ol = mask & 0xFFFF;
+	unsigned long mask_oh = mask >> 16;
+
+	unsigned long values_ol = values & mask_ol;
+	unsigned long values_oh = (values >> 16) & mask_oh;
+
+	em_gio_write(gpio_to_priv(chip), GIO_OL,
+		     mask_ol << 16 | values_ol);
+	em_gio_write(gpio_to_priv(chip), GIO_OH,
+		     mask_oh << 16 | values_oh);
+}
+
 static int em_gio_direction_output(struct gpio_chip *chip, unsigned offset,
 				   int value)
 {
@@ -282,8 +303,10 @@ static int em_gio_probe(struct platform_
 	gpio_chip = &p->gpio_chip;
 	gpio_chip->direction_input = em_gio_direction_input;
 	gpio_chip->get = em_gio_get;
+	gpio_chip->get_block = em_gio_get_block;
 	gpio_chip->direction_output = em_gio_direction_output;
 	gpio_chip->set = em_gio_set;
+	gpio_chip->set_block = em_gio_set_block;
 	gpio_chip->to_irq = em_gio_to_irq;
 	gpio_chip->label = name;
 	gpio_chip->owner = THIS_MODULE;
--- linux-2.6.orig/drivers/gpio/gpio-ge.c
+++ linux-2.6/drivers/gpio/gpio-ge.c
@@ -100,6 +100,29 @@ static void gef_gpio_set(struct gpio_chi
 	_gef_gpio_set(mmchip->regs + GEF_GPIO_OUT, offset, value);
 }
 
+static unsigned long gef_gpio_get_block(struct gpio_chip *chip,
+					unsigned long mask)
+{
+	struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip);
+
+	return ioread32be(mmchip->regs + GEF_GPIO_IN) & mask;
+}
+
+static void gef_gpio_set_block(struct gpio_chip *chip,
+			       unsigned long mask, unsigned long values)
+{
+	struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip);
+	void __iomem *reg = mmchip->regs + GEF_GPIO_OUT;
+	unsigned int data;
+
+	data = ioread32be(reg);
+
+	data &= ~mask;
+	data |= values & mask;
+
+	iowrite32be(data, reg);
+}
+
 static int __init gef_gpio_init(void)
 {
 	struct device_node *np;
@@ -125,6 +148,8 @@ static int __init gef_gpio_init(void)
 		gef_gpio_chip->gc.direction_output = gef_gpio_dir_out;
 		gef_gpio_chip->gc.get = gef_gpio_get;
 		gef_gpio_chip->gc.set = gef_gpio_set;
+		gef_gpio_chip->gc.get_block = gef_gpio_get_block;
+		gef_gpio_chip->gc.set_block = gef_gpio_set_block;
 
 		/* This function adds a memory mapped GPIO chip */
 		retval = of_mm_gpiochip_add(np, gef_gpio_chip);
@@ -153,6 +178,8 @@ static int __init gef_gpio_init(void)
 		gef_gpio_chip->gc.direction_output = gef_gpio_dir_out;
 		gef_gpio_chip->gc.get = gef_gpio_get;
 		gef_gpio_chip->gc.set = gef_gpio_set;
+		gef_gpio_chip->gc.get_block = gef_gpio_get_block;
+		gef_gpio_chip->gc.set_block = gef_gpio_set_block;
 
 		/* This function adds a memory mapped GPIO chip */
 		retval = of_mm_gpiochip_add(np, gef_gpio_chip);
@@ -181,6 +208,8 @@ static int __init gef_gpio_init(void)
 		gef_gpio_chip->gc.direction_output = gef_gpio_dir_out;
 		gef_gpio_chip->gc.get = gef_gpio_get;
 		gef_gpio_chip->gc.set = gef_gpio_set;
+		gef_gpio_chip->gc.get_block = gef_gpio_get_block;
+		gef_gpio_chip->gc.set_block = gef_gpio_set_block;
 
 		/* This function adds a memory mapped GPIO chip */
 		retval = of_mm_gpiochip_add(np, gef_gpio_chip);
--- linux-2.6.orig/drivers/gpio/gpio-generic.c
+++ linux-2.6/drivers/gpio/gpio-generic.c
@@ -122,6 +122,13 @@ static int bgpio_get(struct gpio_chip *g
 	return bgc->read_reg(bgc->reg_dat) & bgc->pin2mask(bgc, gpio);
 }
 
+static unsigned long bgpio_get_block(struct gpio_chip *gc, unsigned long mask)
+{
+	struct bgpio_chip *bgc = to_bgpio_chip(gc);
+
+	return bgc->read_reg(bgc->reg_dat) & mask;
+}
+
 static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
 {
 	struct bgpio_chip *bgc = to_bgpio_chip(gc);
@@ -170,6 +177,51 @@ static void bgpio_set_set(struct gpio_ch
 	spin_unlock_irqrestore(&bgc->lock, flags);
 }
 
+static void bgpio_set_block(struct gpio_chip *gc, unsigned long mask,
+			    unsigned long values)
+{
+	struct bgpio_chip *bgc = to_bgpio_chip(gc);
+	unsigned long flags;
+
+	spin_lock_irqsave(&bgc->lock, flags);
+
+	bgc->data &= ~mask;
+	bgc->data |= values & mask;
+
+	bgc->write_reg(bgc->reg_dat, bgc->data);
+
+	spin_unlock_irqrestore(&bgc->lock, flags);
+}
+
+static void bgpio_set_with_clear_block(struct gpio_chip *gc, unsigned long mask,
+				       unsigned long values)
+{
+	struct bgpio_chip *bgc = to_bgpio_chip(gc);
+	unsigned long set_bits = values & mask;
+	unsigned long clr_bits = ~values & mask;
+
+	if (set_bits)
+		bgc->write_reg(bgc->reg_set, set_bits);
+	if (clr_bits)
+		bgc->write_reg(bgc->reg_set, clr_bits);
+}
+
+static void bgpio_set_set_block(struct gpio_chip *gc, unsigned long mask,
+				unsigned long values)
+{
+	struct bgpio_chip *bgc = to_bgpio_chip(gc);
+	unsigned long flags;
+
+	spin_lock_irqsave(&bgc->lock, flags);
+
+	bgc->data &= ~mask;
+	bgc->data |= values & mask;
+
+	bgc->write_reg(bgc->reg_set, bgc->data);
+
+	spin_unlock_irqrestore(&bgc->lock, flags);
+}
+
 static int bgpio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio)
 {
 	return 0;
@@ -317,14 +369,18 @@ static int bgpio_setup_io(struct bgpio_c
 		bgc->reg_set = set;
 		bgc->reg_clr = clr;
 		bgc->gc.set = bgpio_set_with_clear;
+		bgc->gc.set_block = bgpio_set_with_clear_block;
 	} else if (set && !clr) {
 		bgc->reg_set = set;
 		bgc->gc.set = bgpio_set_set;
+		bgc->gc.set_block = bgpio_set_set_block;
 	} else {
 		bgc->gc.set = bgpio_set;
+		bgc->gc.set_block = bgpio_set_block;
 	}
 
 	bgc->gc.get = bgpio_get;
+	bgc->gc.get_block = bgpio_get_block;
 
 	return 0;
 }
--- linux-2.6.orig/drivers/gpio/gpio-ks8695.c
+++ linux-2.6/drivers/gpio/gpio-ks8695.c
@@ -195,6 +195,38 @@ static int ks8695_gpio_get_value(struct
 
 
 /*
+ * Set the state of GPIO lines simultaneously.
+ */
+static void ks8695_gpio_set_block(struct gpio_chip *gc,
+				  unsigned long mask, unsigned long values)
+{
+	unsigned long x, flags;
+
+	local_irq_save(flags);
+
+	/* set output line state */
+	x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPD);
+
+	x &= ~mask;
+	x |= values & mask;
+
+	__raw_writel(x, KS8695_GPIO_VA + KS8695_IOPD);
+
+	local_irq_restore(flags);
+}
+
+
+/*
+ * Read the state of GPIO lines simultaneously.
+ */
+static unsigned long ks8695_gpio_get_block(struct gpio_chip *gc,
+					   unsigned long mask)
+{
+	return __raw_readl(KS8695_GPIO_VA + KS8695_IOPD) & mask;
+}
+
+
+/*
  * Map GPIO line to IRQ number.
  */
 static int ks8695_gpio_to_irq(struct gpio_chip *gc, unsigned int pin)
@@ -225,6 +257,8 @@ static struct gpio_chip ks8695_gpio_chip
 	.direction_output	= ks8695_gpio_direction_output,
 	.get			= ks8695_gpio_get_value,
 	.set			= ks8695_gpio_set_value,
+	.get_block		= ks8695_gpio_get_block,
+	.set_block		= ks8695_gpio_set_block,
 	.to_irq			= ks8695_gpio_to_irq,
 	.base			= 0,
 	.ngpio			= 16,
--- linux-2.6.orig/drivers/gpio/gpio-lpc32xx.c
+++ linux-2.6/drivers/gpio/gpio-lpc32xx.c
@@ -297,6 +297,22 @@ static int lpc32xx_gpio_get_value_p3(str
 	return __get_gpio_state_p3(group, pin);
 }
 
+static unsigned long lpc32xx_gpio_get_block_p3(struct gpio_chip *chip,
+					       unsigned long mask)
+{
+	struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+	u32 bits = __raw_readl(group->gpio_grp->inp_state);
+
+	/* In P3_INP_STATE, the 6 GPIOs are scattered over the register,
+	 * rearranging to bits 0-5
+	 */
+	bits >>= 10;
+	bits &= 0x401F;
+	bits |= (bits & 0x4000) >> 9;
+
+	return bits & mask & 0x3F;
+}
+
 static int lpc32xx_gpi_get_value(struct gpio_chip *chip, unsigned pin)
 {
 	struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
@@ -304,6 +320,15 @@ static int lpc32xx_gpi_get_value(struct
 	return __get_gpi_state_p3(group, pin);
 }
 
+static unsigned long lpc32xx_gpi_get_block(struct gpio_chip *chip,
+					   unsigned long mask)
+{
+	struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+	u32 bits = __raw_readl(group->gpio_grp->inp_state);
+
+	return bits & mask;
+}
+
 static int lpc32xx_gpio_dir_output_p012(struct gpio_chip *chip, unsigned pin,
 	int value)
 {
@@ -351,6 +376,27 @@ static void lpc32xx_gpio_set_value_p3(st
 	__set_gpio_level_p3(group, pin, value);
 }
 
+static void lpc32xx_gpio_set_block_p3(struct gpio_chip *chip,
+				      unsigned long mask,
+				      unsigned long values)
+{
+	struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+	u32 set_bits = values & mask;
+	u32 clr_bits = ~values & mask;
+
+	/* States of GPIO 0-5 start at bit 25 */
+	set_bits <<= 25;
+	clr_bits <<= 25;
+
+	/* Note: On LPC32xx, GPOs can only be set at once or cleared at once,
+	 *       but not set and cleared@once
+	 */
+	if (set_bits)
+		__raw_writel(set_bits, group->gpio_grp->outp_set);
+	if (clr_bits)
+		__raw_writel(clr_bits, group->gpio_grp->outp_clr);
+}
+
 static void lpc32xx_gpo_set_value(struct gpio_chip *chip, unsigned pin,
 	int value)
 {
@@ -366,6 +412,31 @@ static int lpc32xx_gpo_get_value(struct
 	return __get_gpo_state_p3(group, pin);
 }
 
+static void lpc32xx_gpo_set_block(struct gpio_chip *chip, unsigned long mask,
+				  unsigned long values)
+{
+	struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+	u32 set_bits = values & mask;
+	u32 clr_bits = ~values & mask;
+
+	/* Note: On LPC32xx, GPOs can only be set at once or cleared at once,
+	 *       but not set and cleared at once
+	 */
+	if (set_bits)
+		__raw_writel(set_bits, group->gpio_grp->outp_set);
+	if (clr_bits)
+		__raw_writel(clr_bits, group->gpio_grp->outp_clr);
+}
+
+static unsigned long lpc32xx_gpo_get_block(struct gpio_chip *chip,
+					   unsigned long mask)
+{
+	struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+	u32 bits = __raw_readl(group->gpio_grp->outp_state);
+
+	return bits & mask;
+}
+
 static int lpc32xx_gpio_request(struct gpio_chip *chip, unsigned pin)
 {
 	if (pin < chip->ngpio)
@@ -440,8 +511,10 @@ static struct lpc32xx_gpio_chip lpc32xx_
 			.label			= "gpio_p0",
 			.direction_input	= lpc32xx_gpio_dir_input_p012,
 			.get			= lpc32xx_gpio_get_value_p012,
+			.get_block		= lpc32xx_gpi_get_block,
 			.direction_output	= lpc32xx_gpio_dir_output_p012,
 			.set			= lpc32xx_gpio_set_value_p012,
+			.set_block		= lpc32xx_gpo_set_block,
 			.request		= lpc32xx_gpio_request,
 			.to_irq			= lpc32xx_gpio_to_irq_p01,
 			.base			= LPC32XX_GPIO_P0_GRP,
@@ -456,8 +529,10 @@ static struct lpc32xx_gpio_chip lpc32xx_
 			.label			= "gpio_p1",
 			.direction_input	= lpc32xx_gpio_dir_input_p012,
 			.get			= lpc32xx_gpio_get_value_p012,
+			.get_block		= lpc32xx_gpi_get_block,
 			.direction_output	= lpc32xx_gpio_dir_output_p012,
 			.set			= lpc32xx_gpio_set_value_p012,
+			.set_block		= lpc32xx_gpo_set_block,
 			.request		= lpc32xx_gpio_request,
 			.to_irq			= lpc32xx_gpio_to_irq_p01,
 			.base			= LPC32XX_GPIO_P1_GRP,
@@ -472,8 +547,10 @@ static struct lpc32xx_gpio_chip lpc32xx_
 			.label			= "gpio_p2",
 			.direction_input	= lpc32xx_gpio_dir_input_p012,
 			.get			= lpc32xx_gpio_get_value_p012,
+			.get_block		= lpc32xx_gpi_get_block,
 			.direction_output	= lpc32xx_gpio_dir_output_p012,
 			.set			= lpc32xx_gpio_set_value_p012,
+			.set_block		= lpc32xx_gpo_set_block,
 			.request		= lpc32xx_gpio_request,
 			.base			= LPC32XX_GPIO_P2_GRP,
 			.ngpio			= LPC32XX_GPIO_P2_MAX,
@@ -487,8 +564,10 @@ static struct lpc32xx_gpio_chip lpc32xx_
 			.label			= "gpio_p3",
 			.direction_input	= lpc32xx_gpio_dir_input_p3,
 			.get			= lpc32xx_gpio_get_value_p3,
+			.get_block		= lpc32xx_gpio_get_block_p3,
 			.direction_output	= lpc32xx_gpio_dir_output_p3,
 			.set			= lpc32xx_gpio_set_value_p3,
+			.set_block		= lpc32xx_gpio_set_block_p3,
 			.request		= lpc32xx_gpio_request,
 			.to_irq			= lpc32xx_gpio_to_irq_gpio_p3,
 			.base			= LPC32XX_GPIO_P3_GRP,
@@ -503,6 +582,7 @@ static struct lpc32xx_gpio_chip lpc32xx_
 			.label			= "gpi_p3",
 			.direction_input	= lpc32xx_gpio_dir_in_always,
 			.get			= lpc32xx_gpi_get_value,
+			.get_block		= lpc32xx_gpi_get_block,
 			.request		= lpc32xx_gpio_request,
 			.to_irq			= lpc32xx_gpio_to_irq_gpi_p3,
 			.base			= LPC32XX_GPI_P3_GRP,
@@ -517,7 +597,9 @@ static struct lpc32xx_gpio_chip lpc32xx_
 			.label			= "gpo_p3",
 			.direction_output	= lpc32xx_gpio_dir_out_always,
 			.set			= lpc32xx_gpo_set_value,
+			.set_block		= lpc32xx_gpo_set_block,
 			.get			= lpc32xx_gpo_get_value,
+			.get_block		= lpc32xx_gpo_get_block,
 			.request		= lpc32xx_gpio_request,
 			.base			= LPC32XX_GPO_P3_GRP,
 			.ngpio			= LPC32XX_GPO_P3_MAX,
--- linux-2.6.orig/drivers/gpio/gpio-max730x.c
+++ linux-2.6/drivers/gpio/gpio-max730x.c
@@ -146,6 +146,44 @@ static int max7301_get(struct gpio_chip
 	return level;
 }
 
+static unsigned long max7301_get_block(struct gpio_chip *chip,
+				       unsigned long mask)
+{
+	struct max7301 *ts = container_of(chip, struct max7301, chip);
+	int i, j;
+	unsigned long result = 0;
+
+	for (i = 0; i < 4; i++) {
+		if ((mask >> (i * 8)) & 0xFF) { /* i/o only if necessary */
+			u8 in_level = ts->read(ts->dev, 0x44 + i * 8);
+			u8 in_mask = 0;
+			u8 out_level = (ts->out_level >> (i * 8 + 4)) & 0xFF;
+			u8 out_mask = 0;
+
+			for (j = 0; j < 8; j++) {
+				int offset = 4 + i * 8 + j;
+				int config = (ts->port_config[offset >> 2] >>
+					      ((offset & 3) << 1)) &
+					PIN_CONFIG_MASK;
+
+				switch (config) {
+				case PIN_CONFIG_OUT:
+					out_mask |= BIT(j);
+					break;
+				case PIN_CONFIG_IN_WO_PULLUP:
+				case PIN_CONFIG_IN_PULLUP:
+					in_mask |= BIT(j);
+				}
+			}
+
+			result |= ((unsigned long)(in_level & in_mask) |
+				   (out_level & out_mask)) << (i * 8);
+		}
+	}
+
+	return result & mask;
+}
+
 static void max7301_set(struct gpio_chip *chip, unsigned offset, int value)
 {
 	struct max7301 *ts = container_of(chip, struct max7301, chip);
@@ -160,6 +198,27 @@ static void max7301_set(struct gpio_chip
 	mutex_unlock(&ts->lock);
 }
 
+static void max7301_set_block(struct gpio_chip *chip, unsigned long mask,
+			      unsigned long values)
+{
+	struct max7301 *ts = container_of(chip, struct max7301, chip);
+	unsigned long changes;
+	int i;
+
+	mutex_lock(&ts->lock);
+
+	changes = (ts->out_level ^ (values << 4)) & (mask << 4);
+	ts->out_level ^= changes;
+
+	for (i = 0; i < 4; i++) {
+		if ((changes >> (i * 8 + 4)) & 0xFF) /* i/o only on change */
+			ts->write(ts->dev, 0x44 + i * 8,
+				  (ts->out_level >> (i * 8 + 4)) & 0xFF);
+	}
+
+	mutex_unlock(&ts->lock);
+}
+
 int __max730x_probe(struct max7301 *ts)
 {
 	struct device *dev = ts->dev;
@@ -184,8 +243,10 @@ int __max730x_probe(struct max7301 *ts)
 
 	ts->chip.direction_input = max7301_direction_input;
 	ts->chip.get = max7301_get;
+	ts->chip.get_block = max7301_get_block;
 	ts->chip.direction_output = max7301_direction_output;
 	ts->chip.set = max7301_set;
+	ts->chip.set_block = max7301_set_block;
 
 	ts->chip.ngpio = PIN_NUMBER;
 	ts->chip.can_sleep = 1;
--- linux-2.6.orig/drivers/gpio/gpio-max732x.c
+++ linux-2.6/drivers/gpio/gpio-max732x.c
@@ -219,6 +219,63 @@ out:
 	mutex_unlock(&chip->lock);
 }
 
+static unsigned long max732x_gpio_get_block(struct gpio_chip *gc,
+					    unsigned long mask)
+{
+	struct max732x_chip *chip;
+	uint8_t lo = 0;
+	uint8_t hi = 0;
+	int ret;
+
+	chip = container_of(gc, struct max732x_chip, gpio_chip);
+
+	if (mask & 0xFF) {
+		ret = max732x_readb(chip, is_group_a(chip, 0), &lo);
+		if (ret < 0)
+			return 0;
+	}
+	if (mask & 0xFF00) {
+		ret = max732x_readb(chip, is_group_a(chip, 8), &hi);
+		if (ret < 0)
+			return 0;
+	}
+
+	return (((unsigned long)hi << 8) | lo) & mask;
+}
+
+static void max732x_gpio_set_block(struct gpio_chip *gc, unsigned long mask,
+				   unsigned long values)
+{
+	struct max732x_chip *chip;
+	uint8_t reg_out;
+	uint8_t lo_mask = mask & 0xFF;
+	uint8_t hi_mask = (mask >> 8) & 0xFF;
+	int ret;
+
+	chip = container_of(gc, struct max732x_chip, gpio_chip);
+
+	mutex_lock(&chip->lock);
+
+	if (lo_mask) {
+		reg_out = (chip->reg_out[0] & ~lo_mask) | (values & lo_mask);
+		ret = max732x_writeb(chip, is_group_a(chip, 0), reg_out);
+		if (ret < 0)
+			goto out;
+		chip->reg_out[0] = reg_out;
+	}
+	if (hi_mask) {
+		reg_out = (chip->reg_out[1] & ~hi_mask) |
+			((values >> 8) & hi_mask);
+		ret = max732x_writeb(chip, is_group_a(chip, 8), reg_out);
+		if (ret < 0)
+			goto out;
+		chip->reg_out[1] = reg_out;
+	}
+
+out:
+	mutex_unlock(&chip->lock);
+}
+
 static int max732x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
 {
 	struct max732x_chip *chip;
@@ -562,8 +619,10 @@ static int max732x_setup_gpio(struct max
 	if (chip->dir_output) {
 		gc->direction_output = max732x_gpio_direction_output;
 		gc->set = max732x_gpio_set_value;
+		gc->set_block = max732x_gpio_set_block;
 	}
 	gc->get = max732x_gpio_get_value;
+	gc->get_block = max732x_gpio_get_block;
 	gc->can_sleep = 1;
 
 	gc->base = gpio_start;
--- linux-2.6.orig/drivers/gpio/gpio-mc33880.c
+++ linux-2.6/drivers/gpio/gpio-mc33880.c
@@ -80,6 +80,21 @@ static void mc33880_set(struct gpio_chip
 	mutex_unlock(&mc->lock);
 }
 
+static void mc33880_set_block(struct gpio_chip *chip, unsigned long mask,
+			      unsigned long values)
+{
+	struct mc33880 *mc = container_of(chip, struct mc33880, chip);
+
+	mutex_lock(&mc->lock);
+
+	mc->port_config &= ~mask;
+	mc->port_config |= values & mask;
+
+	mc33880_write_config(mc);
+
+	mutex_unlock(&mc->lock);
+}
+
 static int mc33880_probe(struct spi_device *spi)
 {
 	struct mc33880 *mc;
@@ -113,6 +128,7 @@ static int mc33880_probe(struct spi_devi
 
 	mc->chip.label = DRIVER_NAME,
 	mc->chip.set = mc33880_set;
+	mc->chip.set_block = mc33880_set_block;
 	mc->chip.base = pdata->base;
 	mc->chip.ngpio = PIN_NUMBER;
 	mc->chip.can_sleep = 1;
--- linux-2.6.orig/drivers/gpio/gpio-ml-ioh.c
+++ linux-2.6/drivers/gpio/gpio-ml-ioh.c
@@ -127,6 +127,31 @@ static int ioh_gpio_get(struct gpio_chip
 	return ioread32(&chip->reg->regs[chip->ch].pi) & (1 << nr);
 }
 
+static void ioh_gpio_set_block(struct gpio_chip *gpio, unsigned long mask,
+			       unsigned long values)
+{
+	u32 reg_val;
+	struct ioh_gpio *chip =	container_of(gpio, struct ioh_gpio, gpio);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->spinlock, flags);
+	reg_val = ioread32(&chip->reg->regs[chip->ch].po);
+
+	reg_val &= ~mask;
+	reg_val |= values & mask;
+
+	iowrite32(reg_val, &chip->reg->regs[chip->ch].po);
+	spin_unlock_irqrestore(&chip->spinlock, flags);
+}
+
+static unsigned long ioh_gpio_get_block(struct gpio_chip *gpio,
+					unsigned long mask)
+{
+	struct ioh_gpio *chip =	container_of(gpio, struct ioh_gpio, gpio);
+
+	return ioread32(&chip->reg->regs[chip->ch].pi) & mask;
+}
+
 static int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
 				     int val)
 {
@@ -237,8 +262,10 @@ static void ioh_gpio_setup(struct ioh_gp
 	gpio->owner = THIS_MODULE;
 	gpio->direction_input = ioh_gpio_direction_input;
 	gpio->get = ioh_gpio_get;
+	gpio->get_block = ioh_gpio_get_block;
 	gpio->direction_output = ioh_gpio_direction_output;
 	gpio->set = ioh_gpio_set;
+	gpio->set_block = ioh_gpio_set_block;
 	gpio->dbg_show = NULL;
 	gpio->base = -1;
 	gpio->ngpio = num_port;
--- linux-2.6.orig/drivers/gpio/gpio-mm-lantiq.c
+++ linux-2.6/drivers/gpio/gpio-mm-lantiq.c
@@ -73,6 +73,27 @@ static void ltq_mm_set(struct gpio_chip
 }
 
 /**
+ * ltq_mm_set_block() - gpio_chip->set_block - set gpios simultaneously.
+ * @gc:     Pointer to gpio_chip device structure.
+ * @mask:   Bit map of masked GPIOs in this gpio_chip.
+ * @values: Values to be written to the specified signals.
+ *
+ * Set the shadow values and call ltq_mm_apply.
+ */
+static void ltq_mm_set_block(struct gpio_chip *gc, unsigned long mask,
+			     unsigned long values)
+{
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+	struct ltq_mm *chip =
+		container_of(mm_gc, struct ltq_mm, mmchip);
+
+	chip->shadow &= ~mask;
+	chip->shadow |= values & mask;
+
+	ltq_mm_apply(chip);
+}
+
+/**
  * ltq_mm_dir_out() - gpio_chip->dir_out - set gpio direction.
  * @gc:     Pointer to gpio_chip device structure.
  * @gpio:   GPIO signal number.
@@ -122,6 +143,7 @@ static int ltq_mm_probe(struct platform_
 	chip->mmchip.gc.label = "gpio-mm-ltq";
 	chip->mmchip.gc.direction_output = ltq_mm_dir_out;
 	chip->mmchip.gc.set = ltq_mm_set;
+	chip->mmchip.gc.set_block = ltq_mm_set_block;
 	chip->mmchip.save_regs = ltq_mm_save_regs;
 
 	/* store the shadow value if one was passed by the devicetree */
--- linux-2.6.orig/drivers/gpio/gpio-mpc5200.c
+++ linux-2.6/drivers/gpio/gpio-mpc5200.c
@@ -24,6 +24,7 @@
 #include <linux/io.h>
 #include <linux/of_platform.h>
 #include <linux/module.h>
+#include <linux/bitrev.h>
 
 #include <asm/gpio.h>
 #include <asm/mpc52xx.h>
@@ -97,6 +98,35 @@ mpc52xx_wkup_gpio_set(struct gpio_chip *
 	pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val);
 }
 
+static unsigned long mpc52xx_wkup_gpio_get_block(struct gpio_chip *gc,
+						 unsigned long mask)
+{
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+	struct mpc52xx_gpio_wkup __iomem *regs = mm_gc->regs;
+
+	return bitrev8(in_8(&regs->wkup_ival)) & mask;
+}
+
+static void
+mpc52xx_wkup_gpio_set_block(struct gpio_chip *gc, unsigned long mask,
+			    unsigned long values)
+{
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+	struct mpc52xx_gpiochip *chip = container_of(mm_gc,
+			struct mpc52xx_gpiochip, mmchip);
+	struct mpc52xx_gpio_wkup __iomem *regs = mm_gc->regs;
+	unsigned long flags;
+
+	spin_lock_irqsave(&gpio_lock, flags);
+
+	chip->shadow_dvo &= ~bitrev8(mask);
+	chip->shadow_dvo |= bitrev8(values & mask);
+
+	out_8(&regs->wkup_dvo, chip->shadow_dvo);
+
+	spin_unlock_irqrestore(&gpio_lock, flags);
+}
+
 static int mpc52xx_wkup_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
 {
 	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
@@ -166,6 +196,8 @@ static int mpc52xx_wkup_gpiochip_probe(s
 	gc->direction_output = mpc52xx_wkup_gpio_dir_out;
 	gc->get              = mpc52xx_wkup_gpio_get;
 	gc->set              = mpc52xx_wkup_gpio_set;
+	gc->get_block        = mpc52xx_wkup_gpio_get_block;
+	gc->set_block        = mpc52xx_wkup_gpio_set_block;
 
 	ret = of_mm_gpiochip_add(ofdev->dev.of_node, &chip->mmchip);
 	if (ret)
@@ -256,6 +288,36 @@ mpc52xx_simple_gpio_set(struct gpio_chip
 	pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val);
 }
 
+static unsigned long mpc52xx_simple_gpio_get_block(struct gpio_chip *gc,
+						   unsigned long mask)
+{
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+	struct mpc52xx_gpio __iomem *regs = mm_gc->regs;
+
+	return bitrev32(in_be32(&regs->simple_ival)) & mask;
+}
+
+static void
+mpc52xx_simple_gpio_set_block(struct gpio_chip *gc, unsigned long mask,
+			      unsigned long values)
+{
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+	struct mpc52xx_gpiochip *chip = container_of(mm_gc,
+						     struct mpc52xx_gpiochip,
+						     mmchip);
+	struct mpc52xx_gpio __iomem *regs = mm_gc->regs;
+	unsigned long flags;
+
+	spin_lock_irqsave(&gpio_lock, flags);
+
+	chip->shadow_dvo &= ~bitrev32(mask);
+	chip->shadow_dvo |= bitrev32(values & mask);
+
+	out_be32(&regs->simple_dvo, chip->shadow_dvo);
+
+	spin_unlock_irqrestore(&gpio_lock, flags);
+}
+
 static int mpc52xx_simple_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
 {
 	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
@@ -326,6 +388,8 @@ static int mpc52xx_simple_gpiochip_probe
 	gc->direction_output = mpc52xx_simple_gpio_dir_out;
 	gc->get              = mpc52xx_simple_gpio_get;
 	gc->set              = mpc52xx_simple_gpio_set;
+	gc->get_block        = mpc52xx_simple_gpio_get_block;
+	gc->set_block        = mpc52xx_simple_gpio_set_block;
 
 	ret = of_mm_gpiochip_add(ofdev->dev.of_node, &chip->mmchip);
 	if (ret)
--- linux-2.6.orig/drivers/gpio/gpio-mpc8xxx.c
+++ linux-2.6/drivers/gpio/gpio-mpc8xxx.c
@@ -17,6 +17,7 @@
 #include <linux/gpio.h>
 #include <linux/slab.h>
 #include <linux/irq.h>
+#include <linux/bitrev.h>
 
 #define MPC8XXX_GPIO_PINS	32
 
@@ -100,6 +101,43 @@ static void mpc8xxx_gpio_set(struct gpio
 	spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
 }
 
+static unsigned long mpc8572_gpio_get_block(struct gpio_chip *gc,
+					    unsigned long mask)
+{
+	u32 val;
+	struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
+	struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm);
+
+	val = in_be32(mm->regs + GPIO_DAT) & ~in_be32(mm->regs + GPIO_DIR);
+
+	return bitrev32(val | mpc8xxx_gc->data) & mask;
+}
+
+static unsigned long mpc8xxx_gpio_get_block(struct gpio_chip *gc,
+					    unsigned long mask)
+{
+	struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
+
+	return bitrev32(in_be32(mm->regs + GPIO_DAT)) & mask;
+}
+
+static void mpc8xxx_gpio_set_block(struct gpio_chip *gc, unsigned long mask,
+				   unsigned long values)
+{
+	struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
+	struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm);
+	unsigned long flags;
+
+	spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
+
+	mpc8xxx_gc->data &= ~bitrev32(mask);
+	mpc8xxx_gc->data |= bitrev32(values | mask);
+
+	out_be32(mm->regs + GPIO_DAT, mpc8xxx_gc->data);
+
+	spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
+}
+
 static int mpc8xxx_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
 {
 	struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
@@ -340,6 +378,9 @@ static void __init mpc8xxx_add_controlle
 	gc->get = of_device_is_compatible(np, "fsl,mpc8572-gpio") ?
 		mpc8572_gpio_get : mpc8xxx_gpio_get;
 	gc->set = mpc8xxx_gpio_set;
+	gc->get_block = of_device_is_compatible(np, "fsl,mpc8572-gpio") ?
+		mpc8572_gpio_get_block : mpc8xxx_gpio_get_block;
+	gc->set_block = mpc8xxx_gpio_set_block;
 	gc->to_irq = mpc8xxx_gpio_to_irq;
 
 	ret = of_mm_gpiochip_add(np, mm_gc);
--- linux-2.6.orig/drivers/gpio/gpio-pca953x.c
+++ linux-2.6/drivers/gpio/gpio-pca953x.c
@@ -302,6 +302,68 @@ exit:
 	mutex_unlock(&chip->i2c_lock);
 }
 
+static unsigned long pca953x_gpio_get_block(struct gpio_chip *gc,
+					    unsigned long mask)
+{
+	struct pca953x_chip *chip;
+	u32 reg_val;
+	int ret, offset = 0;
+
+	chip = container_of(gc, struct pca953x_chip, gpio_chip);
+
+	mutex_lock(&chip->i2c_lock);
+	switch (chip->chip_type) {
+	case PCA953X_TYPE:
+		offset = PCA953X_INPUT;
+		break;
+	case PCA957X_TYPE:
+		offset = PCA957X_IN;
+		break;
+	}
+	ret = pca953x_read_reg(chip, offset, &reg_val);
+	mutex_unlock(&chip->i2c_lock);
+	if (ret < 0) {
+		/* NOTE:  diagnostic already emitted; that's all we should
+		 * do unless gpio_*_value_cansleep() calls become different
+		 * from their nonsleeping siblings (and report faults).
+		 */
+		return 0;
+	}
+
+	return reg_val & mask;
+}
+
+static void pca953x_gpio_set_block(struct gpio_chip *gc, unsigned long mask,
+				   unsigned long values)
+{
+	struct pca953x_chip *chip;
+	u32 reg_val;
+	int ret, offset = 0;
+
+	chip = container_of(gc, struct pca953x_chip, gpio_chip);
+
+	mutex_lock(&chip->i2c_lock);
+
+	reg_val = chip->reg_output & ~mask;
+	reg_val |= values & mask;
+
+	switch (chip->chip_type) {
+	case PCA953X_TYPE:
+		offset = PCA953X_OUTPUT;
+		break;
+	case PCA957X_TYPE:
+		offset = PCA957X_OUT;
+		break;
+	}
+	ret = pca953x_write_reg(chip, offset, reg_val);
+	if (ret)
+		goto exit;
+
+	chip->reg_output = reg_val;
+exit:
+	mutex_unlock(&chip->i2c_lock);
+}
+
 static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios)
 {
 	struct gpio_chip *gc;
@@ -312,6 +374,8 @@ static void pca953x_setup_gpio(struct pc
 	gc->direction_output = pca953x_gpio_direction_output;
 	gc->get = pca953x_gpio_get_value;
 	gc->set = pca953x_gpio_set_value;
+	gc->get_block = pca953x_gpio_get_block;
+	gc->set_block = pca953x_gpio_set_block;
 	gc->can_sleep = 1;
 
 	gc->base = chip->gpio_start;
--- linux-2.6.orig/drivers/gpio/gpio-pcf857x.c
+++ linux-2.6/drivers/gpio/gpio-pcf857x.c
@@ -158,6 +158,28 @@ static void pcf857x_set(struct gpio_chip
 	pcf857x_output(chip, offset, value);
 }
 
+static unsigned long pcf857x_get_block(struct gpio_chip *chip,
+				       unsigned long mask)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	int		value;
+
+	value = gpio->read(gpio->client);
+	return (value < 0) ? 0 : (value & mask);
+}
+
+static void pcf857x_set_block(struct gpio_chip *chip, unsigned long mask,
+			      unsigned long values)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+
+	mutex_lock(&gpio->lock);
+	gpio->out &= ~mask;
+	gpio->out |= values & mask;
+	gpio->write(gpio->client, gpio->out);
+	mutex_unlock(&gpio->lock);
+}
+
 /*-------------------------------------------------------------------------*/
 
 static int pcf857x_to_irq(struct gpio_chip *chip, unsigned offset)
@@ -280,6 +302,8 @@ static int pcf857x_probe(struct i2c_clie
 	gpio->chip.owner		= THIS_MODULE;
 	gpio->chip.get			= pcf857x_get;
 	gpio->chip.set			= pcf857x_set;
+	gpio->chip.get_block		= pcf857x_get_block;
+	gpio->chip.set_block		= pcf857x_set_block;
 	gpio->chip.direction_input	= pcf857x_input;
 	gpio->chip.direction_output	= pcf857x_output;
 	gpio->chip.ngpio		= id->driver_data;
--- linux-2.6.orig/drivers/gpio/gpio-pch.c
+++ linux-2.6/drivers/gpio/gpio-pch.c
@@ -122,6 +122,23 @@ static void pch_gpio_set(struct gpio_chi
 	spin_unlock_irqrestore(&chip->spinlock, flags);
 }
 
+static void pch_gpio_set_block(struct gpio_chip *gpio, unsigned long mask,
+			       unsigned long values)
+{
+	u32 reg_val;
+	struct pch_gpio *chip =	container_of(gpio, struct pch_gpio, gpio);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->spinlock, flags);
+	reg_val = ioread32(&chip->reg->po);
+
+	reg_val &= ~mask;
+	reg_val |= values & mask;
+
+	iowrite32(reg_val, &chip->reg->po);
+	spin_unlock_irqrestore(&chip->spinlock, flags);
+}
+
 static int pch_gpio_get(struct gpio_chip *gpio, unsigned nr)
 {
 	struct pch_gpio *chip =	container_of(gpio, struct pch_gpio, gpio);
@@ -129,6 +146,14 @@ static int pch_gpio_get(struct gpio_chip
 	return ioread32(&chip->reg->pi) & (1 << nr);
 }
 
+static unsigned long pch_gpio_get_block(struct gpio_chip *gpio,
+					unsigned long mask)
+{
+	struct pch_gpio *chip =	container_of(gpio, struct pch_gpio, gpio);
+
+	return ioread32(&chip->reg->pi) & mask;
+}
+
 static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
 				     int val)
 {
@@ -219,8 +244,10 @@ static void pch_gpio_setup(struct pch_gp
 	gpio->owner = THIS_MODULE;
 	gpio->direction_input = pch_gpio_direction_input;
 	gpio->get = pch_gpio_get;
+	gpio->get_block = pch_gpio_get_block;
 	gpio->direction_output = pch_gpio_direction_output;
 	gpio->set = pch_gpio_set;
+	gpio->set_block = pch_gpio_set_block;
 	gpio->dbg_show = NULL;
 	gpio->base = -1;
 	gpio->ngpio = gpio_pins[chip->ioh];
--- linux-2.6.orig/drivers/gpio/gpio-pl061.c
+++ linux-2.6/drivers/gpio/gpio-pl061.c
@@ -118,6 +118,21 @@ static void pl061_set_value(struct gpio_
 	writeb(!!value << offset, chip->base + (1 << (offset + 2)));
 }
 
+static unsigned long pl061_get_block(struct gpio_chip *gc, unsigned long mask)
+{
+	struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
+
+	return !!readb(chip->base + (mask << 2));
+}
+
+static void pl061_set_block(struct gpio_chip *gc, unsigned long mask,
+			    unsigned long values)
+{
+	struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
+
+	writeb(values & mask, chip->base + (mask << 2));
+}
+
 static int pl061_to_irq(struct gpio_chip *gc, unsigned offset)
 {
 	struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
@@ -246,6 +261,8 @@ static int pl061_probe(struct amba_devic
 	chip->gc.direction_output = pl061_direction_output;
 	chip->gc.get = pl061_get_value;
 	chip->gc.set = pl061_set_value;
+	chip->gc.get_block = pl061_get_block;
+	chip->gc.set_block = pl061_set_block;
 	chip->gc.to_irq = pl061_to_irq;
 	chip->gc.ngpio = PL061_GPIO_NR;
 	chip->gc.label = dev_name(dev);
--- linux-2.6.orig/drivers/gpio/gpio-sa1100.c
+++ linux-2.6/drivers/gpio/gpio-sa1100.c
@@ -27,6 +27,24 @@ static void sa1100_gpio_set(struct gpio_
 		GPCR = GPIO_GPIO(offset);
 }
 
+static unsigned long sa1100_gpio_get_block(struct gpio_chip *chip,
+					   unsigned long mask)
+{
+	return GPLR & mask;
+}
+
+static void sa1100_gpio_set_block(struct gpio_chip *chip, unsigned long mask,
+				  unsigned long values)
+{
+	unsigned long set_bits = values & mask;
+	unsigned long clr_bits = ~values & mask;
+
+	if (set_bits)
+		GPSR = set_bits;
+	if (clr_bits)
+		GPCR = clr_bits;
+}
+
 static int sa1100_direction_input(struct gpio_chip *chip, unsigned offset)
 {
 	unsigned long flags;
@@ -59,6 +77,8 @@ static struct gpio_chip sa1100_gpio_chip
 	.direction_output	= sa1100_direction_output,
 	.set			= sa1100_gpio_set,
 	.get			= sa1100_gpio_get,
+	.set_block		= sa1100_gpio_set_block,
+	.get_block		= sa1100_gpio_get_block,
 	.to_irq			= sa1100_to_irq,
 	.base			= 0,
 	.ngpio			= GPIO_MAX + 1,
--- linux-2.6.orig/drivers/gpio/gpio-samsung.c
+++ linux-2.6/drivers/gpio/gpio-samsung.c
@@ -862,6 +862,33 @@ static int samsung_gpiolib_get(struct gp
 	return val;
 }
 
+static void samsung_gpiolib_set_block(struct gpio_chip *chip,
+				      unsigned long mask,
+				      unsigned long values)
+{
+	struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
+	void __iomem *base = ourchip->base;
+	unsigned long flags;
+	unsigned long dat;
+
+	samsung_gpio_lock(ourchip, flags);
+
+	dat = __raw_readl(base + 0x04);
+	dat &= ~mask;
+	dat |= values & mask;
+	__raw_writel(dat, base + 0x04);
+
+	samsung_gpio_unlock(ourchip, flags);
+}
+
+static unsigned long samsung_gpiolib_get_block(struct gpio_chip *chip,
+					       unsigned long mask)
+{
+	struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip);
+
+	return __raw_readl(ourchip->base + 0x04) & mask;
+}
+
 /*
  * CONFIG_S3C_GPIO_TRACK enables the tracking of the s3c specific gpios
  * for use with the configuration calls, and other parts of the s3c gpiolib
@@ -919,6 +946,10 @@ static void __init samsung_gpiolib_add(s
 		gc->set = samsung_gpiolib_set;
 	if (!gc->get)
 		gc->get = samsung_gpiolib_get;
+	if (!gc->set_block)
+		gc->set_block = samsung_gpiolib_set_block;
+	if (!gc->get_block)
+		gc->get_block = samsung_gpiolib_get_block;
 
 #ifdef CONFIG_PM
 	if (chip->pm != NULL) {
--- linux-2.6.orig/drivers/gpio/gpio-twl6040.c
+++ linux-2.6/drivers/gpio/gpio-twl6040.c
@@ -46,6 +46,19 @@ static int twl6040gpo_get(struct gpio_ch
 	return (ret >> offset) & 1;
 }
 
+static unsigned long twl6040gpo_get_block(struct gpio_chip *chip,
+					  unsigned long mask)
+{
+	struct twl6040 *twl6040 = dev_get_drvdata(chip->dev->parent);
+	int ret = 0;
+
+	ret = twl6040_reg_read(twl6040, TWL6040_REG_GPOCTL);
+	if (ret < 0)
+		return 0;
+
+	return ret & mask;
+}
+
 static int twl6040gpo_direction_out(struct gpio_chip *chip, unsigned offset,
 				    int value)
 {
@@ -71,12 +84,31 @@ static void twl6040gpo_set(struct gpio_c
 	twl6040_reg_write(twl6040, TWL6040_REG_GPOCTL, gpoctl);
 }
 
+static void twl6040gpo_set_block(struct gpio_chip *chip, unsigned long mask,
+				 unsigned long values)
+{
+	struct twl6040 *twl6040 = dev_get_drvdata(chip->dev->parent);
+	int ret;
+	u8 gpoctl;
+
+	ret = twl6040_reg_read(twl6040, TWL6040_REG_GPOCTL);
+	if (ret < 0)
+		return;
+
+	gpoctl = ret & ~mask;
+	gpoctl |= values & mask;
+
+	twl6040_reg_write(twl6040, TWL6040_REG_GPOCTL, gpoctl);
+}
+
 static struct gpio_chip twl6040gpo_chip = {
 	.label			= "twl6040",
 	.owner			= THIS_MODULE,
 	.get			= twl6040gpo_get,
+	.get_block		= twl6040gpo_get_block,
 	.direction_output	= twl6040gpo_direction_out,
 	.set			= twl6040gpo_set,
+	.set_block		= twl6040gpo_set_block,
 	.can_sleep		= 1,
 };
 
--- linux-2.6.orig/drivers/gpio/gpio-ucb1400.c
+++ linux-2.6/drivers/gpio/gpio-ucb1400.c
@@ -45,6 +45,27 @@ static void ucb1400_gpio_set(struct gpio
 	ucb1400_gpio_set_value(gpio->ac97, off, val);
 }
 
+static unsigned long ucb1400_gpio_get_block(struct gpio_chip *gc,
+					    unsigned long mask)
+{
+	struct ucb1400_gpio *gpio;
+	gpio = container_of(gc, struct ucb1400_gpio, gc);
+	return ucb1400_reg_read(gpio->ac97, UCB_IO_DATA) & mask;
+}
+
+static void ucb1400_gpio_set_block(struct gpio_chip *gc, unsigned long mask,
+				   unsigned long values)
+{
+	struct ucb1400_gpio *gpio;
+	u16 tmp;
+	gpio = container_of(gc, struct ucb1400_gpio, gc);
+
+	tmp = ucb1400_reg_read(gpio->ac97, UCB_IO_DATA) & ~mask;
+	tmp |= values & mask;
+
+	ucb1400_reg_write(gpio->ac97, UCB_IO_DATA, tmp);
+}
+
 static int ucb1400_gpio_probe(struct platform_device *dev)
 {
 	struct ucb1400_gpio *ucb = dev->dev.platform_data;
@@ -66,6 +87,8 @@ static int ucb1400_gpio_probe(struct pla
 	ucb->gc.direction_output = ucb1400_gpio_dir_out;
 	ucb->gc.get = ucb1400_gpio_get;
 	ucb->gc.set = ucb1400_gpio_set;
+	ucb->gc.get_block = ucb1400_gpio_get_block;
+	ucb->gc.set_block = ucb1400_gpio_set_block;
 	ucb->gc.can_sleep = 1;
 
 	err = gpiochip_add(&ucb->gc);
--- linux-2.6.orig/drivers/gpio/gpio-vt8500.c
+++ linux-2.6/drivers/gpio/gpio-vt8500.c
@@ -211,6 +211,28 @@ static void vt8500_gpio_set_value(struct
 	writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->data_out);
 }
 
+static unsigned long vt8500_gpio_get_block(struct gpio_chip *chip,
+					   unsigned long mask)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	return readl_relaxed(vt8500_chip->base + vt8500_chip->regs->data_in) &
+								mask;
+}
+
+static void vt8500_gpio_set_block(struct gpio_chip *chip, unsigned long mask,
+				  unsigned long  values)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	u32 val = readl_relaxed(vt8500_chip->base +
+				vt8500_chip->regs->data_out);
+	val &= ~mask;
+	val |= values & mask;
+
+	writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->data_out);
+}
+
 static int vt8500_of_xlate(struct gpio_chip *gc,
 			    const struct of_phandle_args *gpiospec, u32 *flags)
 {
@@ -253,6 +275,8 @@ static int vt8500_add_chips(struct platf
 		chip->direction_output = vt8500_gpio_direction_output;
 		chip->get = vt8500_gpio_get_value;
 		chip->set = vt8500_gpio_set_value;
+		chip->get_block = vt8500_gpio_get_block;
+		chip->set_block = vt8500_gpio_set_block;
 		chip->can_sleep = 0;
 		chip->base = pin_cnt;
 		chip->ngpio = data->banks[i].ngpio;
--- linux-2.6.orig/drivers/gpio/gpio-xilinx.c
+++ linux-2.6/drivers/gpio/gpio-xilinx.c
@@ -49,6 +49,21 @@ static int xgpio_get(struct gpio_chip *g
 }
 
 /**
+ * xgpio_get_block - Read a block of specified signals of the GPIO device.
+ * @gc:     Pointer to gpio_chip device structure.
+ * @mask:   Bit mask (bit 0 == 0th GPIO) for GPIOs to get
+ *
+ * This function reads a block of specified signal of the GPIO device, returned
+ * as a bit mask, each bit representing a GPIO
+ */
+static unsigned long xgpio_get_block(struct gpio_chip *gc, unsigned long mask)
+{
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+
+	return in_be32(mm_gc->regs + XGPIO_DATA_OFFSET) & mask;
+}
+
+/**
  * xgpio_set - Write the specified signal of the GPIO device.
  * @gc:     Pointer to gpio_chip device structure.
  * @gpio:   GPIO signal number.
@@ -77,6 +92,33 @@ static void xgpio_set(struct gpio_chip *
 }
 
 /**
+ * xgpio_set_block - Write a block of specified signals of the GPIO device.
+ * @gc:     Pointer to gpio_chip device structure.
+ * @mask:   Bit mask (bit 0 == 0th GPIO) for GPIOs to set
+ * @values: Bit mapped values
+ *
+ * This function writes the specified values in to the specified signals of the
+ * GPIO device.
+ */
+static void xgpio_set_block(struct gpio_chip *gc, unsigned long mask,
+			    unsigned long values)
+{
+	unsigned long flags;
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+	struct xgpio_instance *chip =
+	    container_of(mm_gc, struct xgpio_instance, mmchip);
+
+	spin_lock_irqsave(&chip->gpio_lock, flags);
+
+	chip->gpio_state &= ~mask;
+	chip->gpio_state |= mask & values;
+
+	out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state);
+
+	spin_unlock_irqrestore(&chip->gpio_lock, flags);
+}
+
+/**
  * xgpio_dir_in - Set the direction of the specified GPIO signal as input.
  * @gc:     Pointer to gpio_chip device structure.
  * @gpio:   GPIO signal number.
@@ -195,6 +237,8 @@ static int xgpio_of_probe(struct device_
 	chip->mmchip.gc.direction_output = xgpio_dir_out;
 	chip->mmchip.gc.get = xgpio_get;
 	chip->mmchip.gc.set = xgpio_set;
+	chip->mmchip.gc.get_block = xgpio_get_block;
+	chip->mmchip.gc.set_block = xgpio_set_block;
 
 	chip->mmchip.save_regs = xgpio_save_regs;
 
--- 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);
+	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);
+}
+
 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
@@ -1063,6 +1063,40 @@ static void nmk_gpio_set_output(struct g
 	clk_disable(nmk_chip->clk);
 }
 
+static unsigned long nmk_gpio_get_block(struct gpio_chip *chip,
+					unsigned long mask)
+{
+	struct nmk_gpio_chip *nmk_chip =
+		container_of(chip, struct nmk_gpio_chip, chip);
+	unsigned long values;
+
+	clk_enable(nmk_chip->clk);
+
+	values = readl(nmk_chip->addr + NMK_GPIO_DAT);
+
+	clk_disable(nmk_chip->clk);
+
+	return values & mask;
+}
+
+static void nmk_gpio_set_block(struct gpio_chip *chip, unsigned long mask,
+			       unsigned long values)
+{
+	struct nmk_gpio_chip *nmk_chip =
+		container_of(chip, struct nmk_gpio_chip, chip);
+	unsigned long set_bits = values & mask;
+	unsigned long clr_bits = ~values & mask;
+
+	clk_enable(nmk_chip->clk);
+
+	if (set_bits)
+		writel(set_bits, nmk_chip->addr + NMK_GPIO_DATS);
+	if (clr_bits)
+		writel(clr_bits, nmk_chip->addr + NMK_GPIO_DATC);
+
+	clk_disable(nmk_chip->clk);
+}
+
 static int nmk_gpio_make_output(struct gpio_chip *chip, unsigned offset,
 				int val)
 {
@@ -1182,8 +1216,10 @@ static struct gpio_chip nmk_gpio_templat
 	.free			= nmk_gpio_free,
 	.direction_input	= nmk_gpio_make_input,
 	.get			= nmk_gpio_get_input,
+	.get_block		= nmk_gpio_get_block,
 	.direction_output	= nmk_gpio_make_output,
 	.set			= nmk_gpio_set_output,
+	.set_block		= nmk_gpio_set_block,
 	.to_irq			= nmk_gpio_to_irq,
 	.dbg_show		= nmk_gpio_dbg_show,
 	.can_sleep		= 0,

^ permalink raw reply

* [PATCHv2 1/4] clockevents: Add generic timer broadcast receiver
From: Mark Rutland @ 2013-01-15 12:00 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <alpine.LFD.2.02.1301151224300.7475@ionos>

On Tue, Jan 15, 2013 at 11:24:53AM +0000, Thomas Gleixner wrote:
> On Mon, 14 Jan 2013, Mark Rutland wrote:
> > On Mon, Jan 14, 2013 at 02:17:26PM +0000, Thomas Gleixner wrote:
> > > > I thought this previously also [1], but I couldn't find any path such that a
> > > > tick_cpu_device would have an evtdev without an event_handler. We always set the
> > > > handler before setting evtdev, and alway wipe evtdev before wiping the handler.
> > > > 
> > > > Have I missed something?
> > > 
> > > That's an x86 specific issue. Though we could try and make that
> > > functionality completely generic.
> > 
> > Just to check: is the evt->event_handler check necessary?
> 
> For x86 yes. See the comment.

Ah, sorry. I misunderstood on my initial reading.

I've posted a version with the evt->event_handler check restored, and the
tick_receive_broadcast stub removed when !CONFIG_GENERIC_CLOCKEVENTS_BROADCAST:

https://lkml.org/lkml/2013/1/14/276

The generic clockevents patches (based on v3.8-rc3) can also be found at
git://linux-arm.org/linux-mr.git tags/timer-broadcast-v3-core

> Thanks,
> 
> 	tglx
> 

Thanks,
Mark.

^ permalink raw reply

* [PATCH 10/14] PCI: tegra: Move PCIe driver to drivers/pci/host
From: Thierry Reding @ 2013-01-15 12:08 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20130114095706.GA23467@arm.com>

On Mon, Jan 14, 2013 at 09:57:07AM +0000, Andrew Murray wrote:
> On Sun, Jan 13, 2013 at 09:58:06AM +0000, Thierry Reding wrote:
> > On Sat, Jan 12, 2013 at 09:12:25PM +0000, Arnd Bergmann wrote:
> > > On Saturday 12 January 2013, Thierry Reding wrote:
> > > > > I already hinted at that in one of the other subthreads. Having such a
> > > > > multiplex would also allow the driver to be built as a module. I had
> > > > > already thought about this when I was working on an earlier version of
> > > > > these patches. Basically these would be two ops attached to the host
> > > > > bridge, and the generic arch_setup_msi_irq() could then look that up
> > > > > given the struct pci_dev that is passed to it and call this new per-
> > > > > host bridge .setup_msi_irq().
> > > > 
> > > > struct pci_ops looks like a good place to put these. They'll be
> > > > available from each struct pci_bus, so should be easy to call from
> > > > arch_setup_msi_irq().
> > > > 
> > > > Any objections?
> > > > 
> > > 
> > > struct pci_ops has a long history of being specifically about
> > > config space read/write operations, so on the one hand it does
> > > not feel like the right place to put interrupt specific operations,
> > > but on the other hand, the name sounds appropriate and I cannot
> > > think of any other place to put this, so it's fine with me.
> > > 
> > > The only alternative I can think of is to introduce a new
> > > structure next to it in struct pci_bus, but that feels a bit
> > > pointless. Maybe Bjorn has a preference one way or the other.
> > 
> > The name pci_ops is certainly generic enough. Also the comment above the
> > structure declaration says "Low-level architecture-dependent routines",
> > which applies to the MSI functions as well.
> 
> I've previously looked into this. It seems that architectures handle this
> in different ways, some use vector tables, others use a multiplex and others
> just let the end user implement the callback directly.
> 
> I've made an attempt to find a more common way. Though my implementation, which
> I will try to share later today for reference provides a registration function
> in drivers/pci/msi.c to provide implementations of the
> (setup|teardown)_msi_irq(s) ops. This seems slightly better than the current
> approach and doesn't break existing users - but is still ugly.
> 
> At present the PCI and MSI frameworks are largely uncoupled from each other and
> so I was keen to not pollute PCI structures (e.g. pci_ops) with MSI ops. Just
> because most PCI host bridges also provide MSI support I don't think there is a
> reason why they should always come as a pair or be provided by the same chip.
> 
> Perhaps the solution is to support MSI controller drivers and a means to
> associate them with PCI host controller drivers?

I'm not sure I follow you're reasoning here. Is it possible to use MSIs
without PCI? If not then I think there's little sense in keeping the
implementations separate.

Furthermore, if MSI controller and PCI host bridge are separate entities
how do you look up the MSI controller given a PCI device?

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130115/7c267b3a/attachment-0001.sig>

^ permalink raw reply

* [kvmarm] [PATCH v5 06/14] KVM: ARM: Inject IRQs and FIQs from userspace
From: Peter Maydell @ 2013-01-15 12:15 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20130115095622.GJ11529@redhat.com>

On 15 January 2013 09:56, Gleb Natapov <gleb@redhat.com> wrote:
> On Tue, Jan 08, 2013 at 01:39:17PM -0500, Christoffer Dall wrote:
>> From: Christoffer Dall <cdall@cs.columbia.edu>
>>
>> All interrupt injection is now based on the VM ioctl KVM_IRQ_LINE.  This
>> works semantically well for the GIC as we in fact raise/lower a line on
>> a machine component (the gic).  The IOCTL uses the follwing struct.
>>
>> struct kvm_irq_level {
>>       union {
>>               __u32 irq;     /* GSI */
>>               __s32 status;  /* not used for KVM_IRQ_LEVEL */
>>       };
>>       __u32 level;           /* 0 or 1 */
>> };
>>
>> ARM can signal an interrupt either at the CPU level, or at the in-kernel irqchip
> CPU level interrupt should use KVM_INTERRUPT instead.

No, that would be wrong. KVM_INTERRUPT is for interrupts which must be
delivered synchronously to the CPU. KVM_IRQ_LINE is for interrupts which
can be fed to the kernel asynchronously. It happens that on x86 "must be
delivered synchronously" and "not going to in kernel irqchip" are the same, but
this isn't true for other archs. For ARM all our interrupts can be fed
to the kernel
asynchronously, and so we use KVM_IRQ_LINE in all cases.

There was a big discussion thread about this on kvm and qemu-devel last
July (and we cleaned up some of the QEMU code to not smoosh together
all these different concepts under "do I have an irqchip or not?").

>> (GIC), and for in-kernel irqchip can tell the GIC to use PPIs designated for
>> specific cpus.  The irq field is interpreted like this:
>>
> Haven't read about GIC yet. Is PPI an interrupt that device can send
> directly to a specific CPU? Can we model that with irq routing like we do
> for MSI?

There is no routing involved -- you are raising a specific signal
line (which happens to result in prodding a particular CPU), that's all.

-- PMM

^ permalink raw reply

* [GIT PULL] hyp-boot fixes for 3.8
From: Will Deacon @ 2013-01-15 12:17 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Russell,

As I mentioned previously, a bunch of fixes to the hyp-boot code have been
accumulating in the branch I originally targeted for 3.9. Given that these
should all go to stable, I've split the fixes out for 3.8 and put them on
their own branch for you to pull (see below).

Cheers,

Will

--->8

The following changes since commit 9931faca02c604c22335f5a935a501bb2ace6e20:

  Linux 3.8-rc3 (2013-01-09 18:59:55 -0800)

are available in the git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/will/linux.git for-rmk/virt/hyp-boot/fixes

for you to fetch changes up to d01723479e6a6c70c83295f7847477a016d5e14a:

  ARM: virt: simplify __hyp_stub_install epilog (2013-01-10 21:08:55 +0000)

----------------------------------------------------------------
Dave Martin (1):
      ARM: virt: Avoid bx instruction for compatibility with <=ARMv4

Marc Zyngier (2):
      ARM: virt: boot secondary CPUs through the right entry point
      ARM: virt: simplify __hyp_stub_install epilog

 arch/arm/kernel/head.S     |  2 +-
 arch/arm/kernel/hyp-stub.S | 18 ++++++------------
 2 files changed, 7 insertions(+), 13 deletions(-)

^ permalink raw reply

* [PATCH 1/3] MTD: at91: atmel_nand: for PMECC, add code to choose the ecc bits and sector size according to the ONFI parameter ECC requirement.
From: Artem Bityutskiy @ 2013-01-15 12:26 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1355912840-29501-2-git-send-email-josh.wu@atmel.com>

I cannot compile this patch:

ERROR (phandle_references): Reference to non-existent node or label "pinctrl_ssc0_tx"

ERROR: Input tree has errors, aborting (use -f to force output)
make[2]: *** [arch/arm/boot/dts/at91sam9g20ek.dtb] Error 2


On Wed, 2012-12-19 at 18:27 +0800, Josh Wu wrote:
> This patch will check NAND flash's ecc minimum requirement in ONFI parameter. If it is equal or smaller than pmecc-cap in dtsi, then use ecc_bits in ONFI. otherwise, return an error since pmecc-cap in dtsi don't meet the ecc minimum reqirement.
> 
> This patch also check sector size (codeword) requirement in ONFI. If it is equal or bigger than sector_size in dtsi, then use the one of ONFI. otherwise return error.
> 
> Currently we don't support to read the ECC parameter in ONFI extended parameter page. So in that case we just use the value specified in dts.
> 
> For non-ONFI nand flash, we assume the minimum ecc requirement is 2bits in 512 bytes.
> 
> Signed-off-by: Josh Wu <josh.wu@atmel.com>

Please, wrap the lines like kernel developers usually do.

-- 
Best Regards,
Artem Bityutskiy
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: This is a digitally signed message part
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130115/49c8872d/attachment.sig>

^ permalink raw reply

* [PATCH v5 6/9] ARM: davinci: Remoteproc driver support for OMAP-L138 DSP
From: Sekhar Nori @ 2013-01-15 12:29 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAK=WgbbShpqxS7zrDs97HkjpyWq0xzW3b9KHLBBs_V3YzdJKiA@mail.gmail.com>

On 1/15/2013 3:33 PM, Ohad Ben-Cohen wrote:
> On Tue, Jan 15, 2013 at 11:15 AM, Sekhar Nori <nsekhar@ti.com> wrote:
>> Right, and platform data is not the way to achieve this.
> 
> How do you suggest to handle platforms with multiple different remote
> processors (driven by the same driver) ?
> 
> Requiring the user to indicate the fw name for each processor is
> somewhat error prone/cumbersome.

May be rproc_alloc() could auto-assign the firmware name to something
like 'rproc%d-fw' if firmware name passed to it is NULL?

Thanks,
Sekhar

^ permalink raw reply


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