diff mbox

[RFC,2/2] GPIO: add gpiolib and irqchip for CSR SiRFprimaII GPIO controller

Message ID 1311667993-27189-3-git-send-email-bs14@csr.com (mailing list archive)
State New, archived
Headers show

Commit Message

Barry Song July 26, 2011, 8:13 a.m. UTC
From: Yuping Luo <yuping.luo@csr.com>

Signed-off-by: Yuping Luo <yuping.luo@csr.com>
Signed-off-by: Barry Song <baohua.song@csr.com>
---
 arch/arm/mach-prima2/include/mach/gpio.h |   34 ++
 drivers/gpio/Kconfig                     |    6 +
 drivers/gpio/Makefile                    |    1 +
 drivers/gpio/gpio-sirf.c                 |  529 ++++++++++++++++++++++++++++++
 4 files changed, 570 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-prima2/include/mach/gpio.h
 create mode 100644 drivers/gpio/gpio-sirf.c

Comments

Russell King - ARM Linux July 26, 2011, 10:09 a.m. UTC | #1
On Tue, Jul 26, 2011 at 01:13:13AM -0700, Barry Song wrote:
> diff --git a/arch/arm/mach-prima2/include/mach/gpio.h b/arch/arm/mach-prima2/include/mach/gpio.h
> new file mode 100644
> index 0000000..25673b1
> --- /dev/null
> +++ b/arch/arm/mach-prima2/include/mach/gpio.h
> @@ -0,0 +1,34 @@
> +/*
> + * arch/arm/mach-prima2/include/mach/gpio.h
> + *
> + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
> + *
> + * Licensed under GPLv2 or later.
> + */
> +
> +#ifndef __MACH_GPIO_H
> +#define __MACH_GPIO_H
> +
> +#include <mach/irqs.h>
> +
> +#ifndef CONFIG_GPIO_SIRFCPLD
> +#define ARCH_NR_GPIOS	(SIRFSOC_GPIO_BANK_SIZE * SIRFSOC_GPIO_NO_OF_BANKS)
> +#else
> +#define ARCH_NR_GPIOS	(SIRFSOC_GPIO_BANK_SIZE * SIRFSOC_GPIO_NO_OF_BANKS + \
> +	SIRFSOC_GPIO_CPLD_SIZE + SIRFSOC_GPIO_IO_CPLD_SIZE + \
> +	SIRFSOC_GPIO_HS_CPLD_SIZE)
> +#endif
> +
> +#include <linux/errno.h>

Why do you need linux/errno.h?  asm-generic/gpio.h already includes this.

> +#include <asm-generic/gpio.h>
> +
> +#define gpio_get_value	__gpio_get_value
> +#define gpio_set_value	__gpio_set_value
> +#define gpio_cansleep	__gpio_cansleep

Hmm, yet another trivial gpio implementation.  We have 24 others just like
this.  Well, mainline does... I have just one.  Patches after the merge
window closes.
Russell King - ARM Linux July 26, 2011, 10:46 a.m. UTC | #2
On Tue, Jul 26, 2011 at 11:09:28AM +0100, Russell King - ARM Linux wrote:
> Hmm, yet another trivial gpio implementation.  We have 24 others just like
> this.  Well, mainline does... I have just one.  Patches after the merge
> window closes.

As an additional note to this: we _really_ need to get on top of the
includes of mach/gpio.h rather than linux/gpio.h - there are around 139
files in mainline (this morning) which include mach/gpio.h directly.

There's one which includes both asm/gpio.h and mach/gpio.h, which is
silly when you look at what asm/gpio.h contains - maybe one attempt
to include mach/gpio.h wasn't enough!

These includes get in the way of consolidating stuff out of mach/gpio.h
into asm/gpio.h - moving stuff out of mach/gpio.h could take it out of
view of these files, thereby causing build errors.

What is even less clear is whether changing these mach/gpio.h includes
to linux/gpio.h is correct or not.

So, what I'd like all platform and all soc maintainers to do is to:

	grep -rl '<mach/gpio.h>' arch/arm drivers

and investigate their files from that list which they're responsible for,
and submit patches to _me_ to fix these includes up (preferably to use
linux/gpio.h).  I'd also like them to do a better job at spotting these
while reviewing patches on the list to help reduce new occurances of
this.

Thanks.
Colin Cross July 26, 2011, 6:32 p.m. UTC | #3
On Tue, Jul 26, 2011 at 3:46 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Tue, Jul 26, 2011 at 11:09:28AM +0100, Russell King - ARM Linux wrote:
>> Hmm, yet another trivial gpio implementation.  We have 24 others just like
>> this.  Well, mainline does... I have just one.  Patches after the merge
>> window closes.
>
> As an additional note to this: we _really_ need to get on top of the
> includes of mach/gpio.h rather than linux/gpio.h - there are around 139
> files in mainline (this morning) which include mach/gpio.h directly.
>
> There's one which includes both asm/gpio.h and mach/gpio.h, which is
> silly when you look at what asm/gpio.h contains - maybe one attempt
> to include mach/gpio.h wasn't enough!
>
> These includes get in the way of consolidating stuff out of mach/gpio.h
> into asm/gpio.h - moving stuff out of mach/gpio.h could take it out of
> view of these files, thereby causing build errors.
>
> What is even less clear is whether changing these mach/gpio.h includes
> to linux/gpio.h is correct or not.
>
> So, what I'd like all platform and all soc maintainers to do is to:
>
>        grep -rl '<mach/gpio.h>' arch/arm drivers
>
> and investigate their files from that list which they're responsible for,
> and submit patches to _me_ to fix these includes up (preferably to use
> linux/gpio.h).  I'd also like them to do a better job at spotting these
> while reviewing patches on the list to help reduce new occurances of
> this.
>
> Thanks.

Tegra has four uses of mach/gpio.h:
arch/arm/mach-tegra/board-trimslice-pinmux.c
arch/arm/mach-tegra/board-paz00.c
arch/arm/mach-tegra/board-trimslice.c
drivers/mmc/host/sdhci-tegra.c

Each of these files calls tegra_gpio_enable/tegra_gpio_disable, which
flip the pin in/out of gpio mode.  Including linux/gpio.h and
mach/gpio.h was deliberate, so that when linux/gpio.h stops causing
mach/gpio.h to be included, the tegra gpio apis are still available.
Should I move the tegra gpio apis to a separate include and drop
mach/gpio.h?
Russell King - ARM Linux July 26, 2011, 6:39 p.m. UTC | #4
On Tue, Jul 26, 2011 at 11:32:22AM -0700, Colin Cross wrote:
> On Tue, Jul 26, 2011 at 3:46 AM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> > On Tue, Jul 26, 2011 at 11:09:28AM +0100, Russell King - ARM Linux wrote:
> >> Hmm, yet another trivial gpio implementation.  We have 24 others just like
> >> this.  Well, mainline does... I have just one.  Patches after the merge
> >> window closes.
> >
> > As an additional note to this: we _really_ need to get on top of the
> > includes of mach/gpio.h rather than linux/gpio.h - there are around 139
> > files in mainline (this morning) which include mach/gpio.h directly.
> >
> > There's one which includes both asm/gpio.h and mach/gpio.h, which is
> > silly when you look at what asm/gpio.h contains - maybe one attempt
> > to include mach/gpio.h wasn't enough!
> >
> > These includes get in the way of consolidating stuff out of mach/gpio.h
> > into asm/gpio.h - moving stuff out of mach/gpio.h could take it out of
> > view of these files, thereby causing build errors.
> >
> > What is even less clear is whether changing these mach/gpio.h includes
> > to linux/gpio.h is correct or not.
> >
> > So, what I'd like all platform and all soc maintainers to do is to:
> >
> >        grep -rl '<mach/gpio.h>' arch/arm drivers
> >
> > and investigate their files from that list which they're responsible for,
> > and submit patches to _me_ to fix these includes up (preferably to use
> > linux/gpio.h).  I'd also like them to do a better job at spotting these
> > while reviewing patches on the list to help reduce new occurances of
> > this.
> >
> > Thanks.
> 
> Tegra has four uses of mach/gpio.h:
> arch/arm/mach-tegra/board-trimslice-pinmux.c
> arch/arm/mach-tegra/board-paz00.c
> arch/arm/mach-tegra/board-trimslice.c
> drivers/mmc/host/sdhci-tegra.c
> 
> Each of these files calls tegra_gpio_enable/tegra_gpio_disable, which
> flip the pin in/out of gpio mode.  Including linux/gpio.h and
> mach/gpio.h was deliberate, so that when linux/gpio.h stops causing
> mach/gpio.h to be included, the tegra gpio apis are still available.
> Should I move the tegra gpio apis to a separate include and drop
> mach/gpio.h?

I don't think there's any plans to break the:

linux/gpio.h -> asm/gpio.h -> mach/gpio.h

include path at the moment, as platforms do define ARCH_NR_GPIO,
which has to be done before asm-generic/gpiolib.h is included.

The problem which is there at the moment is that moving stuff out of
mach/gpio.h is *dangerous* because soo much stuff includes mach/gpio.h,
it's impossible to tell whether moving stuff like gpio_set_value,
gpio_get_value, etc into asm/gpio.h is going to break anything - and
there's no way I can get sufficient coverage to check that.

So, the only way I can think of sorting this out is to forcefully
change all mach/gpio.h to be linux/gpio.h first, before starting
to migrate stuff out of mach/gpio.h.

What we may do later is make the include of mach/gpio.h conditional,
and those platforms with their own specific stuff would continue to
include that.  Those which don't need mach/gpio.h can then live on
without it.
Nicolas Pitre July 26, 2011, 7:10 p.m. UTC | #5
On Tue, 26 Jul 2011, Russell King - ARM Linux wrote:

> I don't think there's any plans to break the:
> 
> linux/gpio.h -> asm/gpio.h -> mach/gpio.h
> 
> include path at the moment, as platforms do define ARCH_NR_GPIO,
> which has to be done before asm-generic/gpiolib.h is included.

Well, this GPIO business is the biggest hurdle towards a single kernel 
image that can support multiple SOCs at the moment, and the only one for 
which I have no solution yet.  Anything that could help removing 
mach/gpio.h from asm/gpio.h would be welcome.


Nicolas
Barry Song July 27, 2011, 2:08 a.m. UTC | #6
2011/7/27 Nicolas Pitre <nicolas.pitre@linaro.org>:
> On Tue, 26 Jul 2011, Russell King - ARM Linux wrote:
>
>> I don't think there's any plans to break the:
>>
>> linux/gpio.h -> asm/gpio.h -> mach/gpio.h
>>
>> include path at the moment, as platforms do define ARCH_NR_GPIO,
>> which has to be done before asm-generic/gpiolib.h is included.
>
> Well, this GPIO business is the biggest hurdle towards a single kernel
> image that can support multiple SOCs at the moment, and the only one for
> which I have no solution yet.  Anything that could help removing
> mach/gpio.h from asm/gpio.h would be welcome.

After reading all the following patches,

1. use CONFIG_PHYS_OFFSET to prepare for removal of a bunch of
<mach/memory.h> files

2. move from ARM_DMA_ZONE_SIZE to mdesc->dma_zone_size

3. convert boot_params to atag_offset

Have i lost anything for the work on 1 and 2? considering some
platforms need CONSISTENT_DMA_SIZE more than the default 2MB in
arch/arm/include/asm/memory.h, we can't remove all memory.h only by
defining CONFIG_PHYS_OFFSET and moving ARM_DMA_ZONE_SIZE to
mdesc->dma_zone_size. So CONSISTENT_DMA_SIZE should also be a left
issue except gpio.h.

>
>
> Nicolas
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
Nicolas Pitre July 27, 2011, 2:51 a.m. UTC | #7
On Wed, 27 Jul 2011, Barry Song wrote:

> 2011/7/27 Nicolas Pitre <nicolas.pitre@linaro.org>:
> > On Tue, 26 Jul 2011, Russell King - ARM Linux wrote:
> >
> >> I don't think there's any plans to break the:
> >>
> >> linux/gpio.h -> asm/gpio.h -> mach/gpio.h
> >>
> >> include path at the moment, as platforms do define ARCH_NR_GPIO,
> >> which has to be done before asm-generic/gpiolib.h is included.
> >
> > Well, this GPIO business is the biggest hurdle towards a single kernel
> > image that can support multiple SOCs at the moment, and the only one for
> > which I have no solution yet.  Anything that could help removing
> > mach/gpio.h from asm/gpio.h would be welcome.
> 
> After reading all the following patches,
> 
> 1. use CONFIG_PHYS_OFFSET to prepare for removal of a bunch of
> <mach/memory.h> files
> 
> 2. move from ARM_DMA_ZONE_SIZE to mdesc->dma_zone_size
> 
> 3. convert boot_params to atag_offset
> 
> Have i lost anything for the work on 1 and 2? considering some
> platforms need CONSISTENT_DMA_SIZE more than the default 2MB in
> arch/arm/include/asm/memory.h, we can't remove all memory.h only by
> defining CONFIG_PHYS_OFFSET and moving ARM_DMA_ZONE_SIZE to
> mdesc->dma_zone_size. So CONSISTENT_DMA_SIZE should also be a left
> issue except gpio.h.

CONSISTENT_DMA_SIZE can be solved the same way as ARM_DMA_ZONE_SIZE, 
plus a few changes in dma-mapping.c to dynamically allocate the 
consistent_pte array.

But that's far from the end of it.  In a nutshell:

- We have to get rid of mach/vmalloc.h (trivial)

- We have to kill every SOC specific definition for CLOCK_TICK_RATE and 
  remove mach/timex.h.

- All mach/io.h need a similar refactoring.

- asm/irq.h must stop including mach/irqs.h, and everyone should 
  initialize mdesc->nr_irqs

- mach/entry-macro.S should be translated into something that hooks to 
  mdesc->handle_irq.

- mach/system.h content needs to be moved to out-of-line indirect calls.

- A multy-SOC kernel binary might not support CONFIG_DEBUG_LL out of the 
  box.

- And there are a few odd cases we probably might not find sufficient 
  motivation to fix them and simply exclude them from a single image 
  config.

But there is mach/gpio.h which is the real bummer at the moment.


Nicolas
Linus Walleij July 29, 2011, 3:58 p.m. UTC | #8
2011/7/26 Nicolas Pitre <nicolas.pitre@linaro.org>:
> On Tue, 26 Jul 2011, Russell King - ARM Linux wrote:
>
>> I don't think there's any plans to break the:
>>
>> linux/gpio.h -> asm/gpio.h -> mach/gpio.h
>>
>> include path at the moment, as platforms do define ARCH_NR_GPIO,
>> which has to be done before asm-generic/gpiolib.h is included.
>
> Well, this GPIO business is the biggest hurdle towards a single kernel
> image that can support multiple SOCs at the moment, and the only one for
> which I have no solution yet.  Anything that could help removing
> mach/gpio.h from asm/gpio.h would be welcome.

I've been trying to address it somewhat by converting existing users to
use struct gpio_chip and struct irq_chip.

However I ran into a brick wall as soon as an advanced GPIO controller
such as those in U8500 or the OMAPs needed anything custom apart
from what is done in the API in <linux/gpio.h>, such as configuring pin
bias and muxing pins.

Different alternatives have been floated to LKML, also a post
of two different approaches for patches resolving the issue
in two different ways (see subject [PATCH 0/2] RFC:
gpio: driver-local pin configuration)

Due to lack of interest i.e. no ACKing nor NACKing of the patches,
nor any approach being chosen by the subsystem maintainer, I'm
mainly resting my case, intially we (me & Grant) planned to have
a solution for this early in the 3.1 cycle but that has failed.

A third solution is to complete the pincfg subsystem that I'm
working on and which will naturally (hopefully) split off the
controversial parts of GPIO drivers. Sadly this will involve a bit of
upfront overhead work :-/

Thanks,
Linus Walleij
Barry Song Aug. 8, 2011, 9:09 a.m. UTC | #9
2011/7/26 Russell King - ARM Linux <linux@arm.linux.org.uk>:
> On Tue, Jul 26, 2011 at 01:13:13AM -0700, Barry Song wrote:
>> diff --git a/arch/arm/mach-prima2/include/mach/gpio.h b/arch/arm/mach-prima2/include/mach/gpio.h
>> new file mode 100644
>> index 0000000..25673b1
>> --- /dev/null
>> +++ b/arch/arm/mach-prima2/include/mach/gpio.h
>> @@ -0,0 +1,34 @@
>> +/*
>> + * arch/arm/mach-prima2/include/mach/gpio.h
>> + *
>> + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
>> + *
>> + * Licensed under GPLv2 or later.
>> + */
>> +
>> +#ifndef __MACH_GPIO_H
>> +#define __MACH_GPIO_H
>> +
>> +#include <mach/irqs.h>
>> +
>> +#ifndef CONFIG_GPIO_SIRFCPLD
>> +#define ARCH_NR_GPIOS        (SIRFSOC_GPIO_BANK_SIZE * SIRFSOC_GPIO_NO_OF_BANKS)
>> +#else
>> +#define ARCH_NR_GPIOS        (SIRFSOC_GPIO_BANK_SIZE * SIRFSOC_GPIO_NO_OF_BANKS + \
>> +     SIRFSOC_GPIO_CPLD_SIZE + SIRFSOC_GPIO_IO_CPLD_SIZE + \
>> +     SIRFSOC_GPIO_HS_CPLD_SIZE)
>> +#endif
>> +
>> +#include <linux/errno.h>
>
> Why do you need linux/errno.h?  asm-generic/gpio.h already includes this.
>
>> +#include <asm-generic/gpio.h>
>> +
>> +#define gpio_get_value       __gpio_get_value
>> +#define gpio_set_value       __gpio_set_value
>> +#define gpio_cansleep        __gpio_cansleep
>
> Hmm, yet another trivial gpio implementation.  We have 24 others just like
> this.  Well, mainline does... I have just one.

Ok.
this gpio driver includes two parts: gpio chip and irq chip.  For gpio
chip, we could import gpio-generic and delete
sirfsoc_gpio_direction_input, sirfsoc_gpio_get_value,
sirfsoc_gpio_direction_output and so on.
For the part of irq chip, i don't see much benefit from generic-irq.
all of sirfsoc_gpio_irq_ack, sirfsoc_gpio_irq_mask and
sirfsoc_gpio_irq_unmask are CSR-specific.  irq_gc_ack_set_bit,
irq_gc_mask_clr_bit and irq_gc_mask_set_bit are not practicable to
this chip.

-barry
Linus Walleij Aug. 8, 2011, 9:32 a.m. UTC | #10
2011/7/26 Russell King - ARM Linux <linux@arm.linux.org.uk>:
> [Barry]
>> +#include <asm-generic/gpio.h>
>> +
>> +#define gpio_get_value       __gpio_get_value
>> +#define gpio_set_value       __gpio_set_value
>> +#define gpio_cansleep        __gpio_cansleep
>
> Hmm, yet another trivial gpio implementation.  We have 24 others just like
> this.  Well, mainline does... I have just one.  Patches after the merge
> window closes.

Hm, that sounds very good, now the merge window is closed, can you
post them? Me and Ben have some stash of GPIO patches as well so
we may just as well base them atop of your cleanup.

Thanks,
Linus Walleij
Barry Song Aug. 9, 2011, 7:22 a.m. UTC | #11
2011/7/29 Linus Walleij <linus.ml.walleij@gmail.com>:
> 2011/7/26 Nicolas Pitre <nicolas.pitre@linaro.org>:
>> On Tue, 26 Jul 2011, Russell King - ARM Linux wrote:
>>
>>> I don't think there's any plans to break the:
>>>
>>> linux/gpio.h -> asm/gpio.h -> mach/gpio.h
>>>
>>> include path at the moment, as platforms do define ARCH_NR_GPIO,
>>> which has to be done before asm-generic/gpiolib.h is included.
>>
>> Well, this GPIO business is the biggest hurdle towards a single kernel
>> image that can support multiple SOCs at the moment, and the only one for
>> which I have no solution yet.  Anything that could help removing
>> mach/gpio.h from asm/gpio.h would be welcome.
>
> I've been trying to address it somewhat by converting existing users to
> use struct gpio_chip and struct irq_chip.
>
> However I ran into a brick wall as soon as an advanced GPIO controller
> such as those in U8500 or the OMAPs needed anything custom apart
> from what is done in the API in <linux/gpio.h>, such as configuring pin
> bias and muxing pins.

The patch wants to eliminate the requirement of self-defined
SoC-specific gpio APIs. The intent is that you want to delete files
arch/arm/mach-xxx/include/mach/gpio.h.

I guess same issues exist for other hardwares except GPIO. For
example, almost every different SoC has different DMA API, for
example:
mach-bcmring/dma.c:EXPORT_SYMBOL(dma_alloc_descriptor_ring);
mach-davinci/dma.c:EXPORT_SYMBOL(edma_alloc_channel);
mach-davinci/dma.c:EXPORT_SYMBOL(edma_clear_event);
mach-imx/dma-v1.c:EXPORT_SYMBOL(imx_dma_setup_single);
mach-s3c64xx/dma.c:EXPORT_SYMBOL(s3c2410_dma_enqueue);

Do we also want to delete all arch/arm/mach-xxx/include/mach/dma.h?

Or do we want to delete the whole arch/arm/mach-xxx/include/mach directory?

If not, it is probably that SoC can still hold some chip-specific APIs
in arch/arm/mach-xxx/include/mach/yyy.h.

>
> Different alternatives have been floated to LKML, also a post
> of two different approaches for patches resolving the issue
> in two different ways (see subject [PATCH 0/2] RFC:
> gpio: driver-local pin configuration)
>
> Due to lack of interest i.e. no ACKing nor NACKing of the patches,
> nor any approach being chosen by the subsystem maintainer, I'm
> mainly resting my case, intially we (me & Grant) planned to have
> a solution for this early in the 3.1 cycle but that has failed.
>
> A third solution is to complete the pincfg subsystem that I'm
> working on and which will naturally (hopefully) split off the
> controversial parts of GPIO drivers. Sadly this will involve a bit of
> upfront overhead work :-/
>
> Thanks,
> Linus Walleij
Arnd Bergmann Aug. 9, 2011, 1:15 p.m. UTC | #12
On Tuesday 09 August 2011, Barry Song wrote:
> I guess same issues exist for other hardwares except GPIO. For
> example, almost every different SoC has different DMA API, for
> example:
> mach-bcmring/dma.c:EXPORT_SYMBOL(dma_alloc_descriptor_ring);
> mach-davinci/dma.c:EXPORT_SYMBOL(edma_alloc_channel);
> mach-davinci/dma.c:EXPORT_SYMBOL(edma_clear_event);
> mach-imx/dma-v1.c:EXPORT_SYMBOL(imx_dma_setup_single);
> mach-s3c64xx/dma.c:EXPORT_SYMBOL(s3c2410_dma_enqueue);
> 
> Do we also want to delete all arch/arm/mach-xxx/include/mach/dma.h?

We need to eliminate all header files that have conflicting names
and that are used by drivers or other header files outside of mach-*.
How we get there is very different for each of these headers.

> Or do we want to delete the whole arch/arm/mach-xxx/include/mach directory?

Ideally, yes. However, that will take a lot of time. Meanwhile, we should
rename the individual header files to have unique names for each API,
and ensure that the symbols don't clash.

> If not, it is probably that SoC can still hold some chip-specific APIs
> in arch/arm/mach-xxx/include/mach/yyy.h.

Can you name an example? Most of the ones I can think of will not work
in a multiplatform kernel.

	Arnd
Barry Song Aug. 11, 2011, 2:25 a.m. UTC | #13
2011/8/9 Arnd Bergmann <arnd@arndb.de>:
> On Tuesday 09 August 2011, Barry Song wrote:
>> I guess same issues exist for other hardwares except GPIO. For
>> example, almost every different SoC has different DMA API, for
>> example:
>> mach-bcmring/dma.c:EXPORT_SYMBOL(dma_alloc_descriptor_ring);
>> mach-davinci/dma.c:EXPORT_SYMBOL(edma_alloc_channel);
>> mach-davinci/dma.c:EXPORT_SYMBOL(edma_clear_event);
>> mach-imx/dma-v1.c:EXPORT_SYMBOL(imx_dma_setup_single);
>> mach-s3c64xx/dma.c:EXPORT_SYMBOL(s3c2410_dma_enqueue);
>>
>> Do we also want to delete all arch/arm/mach-xxx/include/mach/dma.h?
>
> We need to eliminate all header files that have conflicting names
> and that are used by drivers or other header files outside of mach-*.
> How we get there is very different for each of these headers.

now every SoCs have different DMA APIs due to different DMA controller:

1. Tegra(dma has a queue with enqueue and dequeue API):
int tegra_dma_enqueue_req(struct tegra_dma_channel *ch,
        struct tegra_dma_req *req);
int tegra_dma_dequeue_req(struct tegra_dma_channel *ch,
        struct tegra_dma_req *req);
void tegra_dma_dequeue(struct tegra_dma_channel *ch);
void tegra_dma_flush(struct tegra_dma_channel *ch);

bool tegra_dma_is_req_inflight(struct tegra_dma_channel *ch,
        struct tegra_dma_req *req);
bool tegra_dma_is_empty(struct tegra_dma_channel *ch);

struct tegra_dma_channel *tegra_dma_allocate_channel(int mode);
void tegra_dma_free_channel(struct tegra_dma_channel *ch);


2. samsung(with queue and request/free, ctrl):
/* s3c2410_dma_request
 *
 * request a dma channel exclusivley
*/

extern int s3c2410_dma_request(enum dma_ch channel,
                               struct s3c2410_dma_client *, void *dev);


/* s3c2410_dma_ctrl
 *
 * change the state of the dma channel
*/

extern int s3c2410_dma_ctrl(enum dma_ch channel, enum s3c2410_chan_op op);

/* s3c2410_dma_setflags
 *
 * set the channel's flags to a given state
*/

extern int s3c2410_dma_setflags(enum dma_ch channel,
                                unsigned int flags);

/* s3c2410_dma_free
 *
 * free the dma channel (will also abort any outstanding operations)
*/

extern int s3c2410_dma_free(enum dma_ch channel, struct s3c2410_dma_client *);

/* s3c2410_dma_enqueue
 *
 * place the given buffer onto the queue of operations for the channel.
 * The buffer must be allocated from dma coherent memory, or the Dcache/WB
 * drained before the buffer is given to the DMA system.

*/

extern int s3c2410_dma_enqueue(enum dma_ch channel, void *id,
                               dma_addr_t data, int size);

/* s3c2410_dma_config
 *
 * configure the dma channel
*/

extern int s3c2410_dma_config(enum dma_ch channel, int xferunit);


3. PXA(pretty simple)
int __init pxa_init_dma(int irq, int num_ch);

int pxa_request_dma (char *name,
                         pxa_dma_prio prio,
                         void (*irq_handler)(int, void *),
                         void *data);

void pxa_free_dma (int dma_ch);


4. qualcomm msm(with queue cmd and exec cmd):
void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd);
void msm_dmov_stop_cmd(unsigned id, struct msm_dmov_cmd *cmd, int graceful);
int msm_dmov_exec_cmd(unsigned id, unsigned int cmdptr);

5. TI omap(with request/free/enable/param config/mode set....)
extern void omap_set_dma_priority(int lch, int dst_port, int priority);
extern int omap_request_dma(int dev_id, const char *dev_name,
                        void (*callback)(int lch, u16 ch_status, void *data),
                        void *data, int *dma_ch);
extern void omap_enable_dma_irq(int ch, u16 irq_bits);
extern void omap_disable_dma_irq(int ch, u16 irq_bits);
extern void omap_free_dma(int ch);
extern void omap_start_dma(int lch);
extern void omap_stop_dma(int lch);
extern void omap_set_dma_transfer_params(int lch, int data_type,
                                         int elem_count, int frame_count,
                                         int sync_mode,
                                         int dma_trigger, int src_or_dst_synch);
extern void omap_set_dma_color_mode(int lch, enum omap_dma_color_mode mode,
                                    u32 color);
extern void omap_set_dma_write_mode(int lch, enum omap_dma_write_mode mode);
extern void omap_set_dma_channel_mode(int lch, enum omap_dma_channel_mode mode);

extern void omap_set_dma_src_params(int lch, int src_port, int src_amode,
                                    unsigned long src_start,
                                    int src_ei, int src_fi);
extern void omap_set_dma_src_index(int lch, int eidx, int fidx);
extern void omap_set_dma_src_data_pack(int lch, int enable);
extern void omap_set_dma_src_burst_mode(int lch,
                                        enum omap_dma_burst_mode burst_mode);

extern void omap_set_dma_dest_params(int lch, int dest_port, int dest_amode,
                                     unsigned long dest_start,
                                     int dst_ei, int dst_fi);
extern void omap_set_dma_dest_index(int lch, int eidx, int fidx);
extern void omap_set_dma_dest_data_pack(int lch, int enable);
extern void omap_set_dma_dest_burst_mode(int lch,


we might figure out some common DMA API framework so that all SoCs can
use. i think:

group1 : request/free/enable/disable/set_mode/set_priority are common
to all, but set_mode/set_priority can be empty.
group2:  enqueue/dequeue  are common to DMAC with queues
group3:  many param configurations.
group4:  other DMA controller with special work mode which can't be
covered by group1-3.
for example, prima2 has a 2D dma controller,  In 2-D DMA, the system
memory space is interpreted as a 2-D layout instead of a linear 1-D
layout. More specifically, the system memory can be considered as
multiple data lines. The length of the data line is determined by the
user-selected DMA_WIDTH register. The user can specify a data window
that the user wants to access using four parameters:
? Start address
? X length
? Y length
? Width

we might also want a dma_chip with many callbacks like gpio and irq,
then move all dma drivers to drivers/dma just like Linus W is moving
all gpio drivers to drivers/gpio. How do you think?

>
>> Or do we want to delete the whole arch/arm/mach-xxx/include/mach directory?
>
> Ideally, yes. However, that will take a lot of time. Meanwhile, we should
> rename the individual header files to have unique names for each API,
> and ensure that the symbols don't clash.
>
>> If not, it is probably that SoC can still hold some chip-specific APIs
>> in arch/arm/mach-xxx/include/mach/yyy.h.
>
> Can you name an example? Most of the ones I can think of will not work
> in a multiplatform kernel.

as i have posted the sirfsoc rtciobrg, we can't read rtc and pm
controller directly since they are not in memory space and any one of
i2c/spi/usb/pci busses, and we are reading them by the following
strange APIs indirectly:
extern void sirfsoc_rtc_iobrg_besyncing(void);
extern u32 sirfsoc_rtc_iobrg_readl(u32 addr);
extern void sirfsoc_rtc_iobrg_writel(u32 val, u32 addr);
now i have placed these APIs in a head file
include/linux/rtc/sirfsoc_rtciobrg.h not in
arch/arm/mach-prima2/include/mach considering we are tending to delete
the dir.

>
>        Arnd
>

-barry
Russell King - ARM Linux Aug. 11, 2011, 8:30 a.m. UTC | #14
On Thu, Aug 11, 2011 at 10:25:58AM +0800, Barry Song wrote:
> now every SoCs have different DMA APIs due to different DMA controller:

We already have a solution to this.  It's called dmaengine.  See
drivers/dma, include/linux/dmaengine.h and Documentation/dmaengine.txt
There is work going on at present to convert the OMAP and Samsung SoC
specific DMA support to dmaengine.
diff mbox

Patch

diff --git a/arch/arm/mach-prima2/include/mach/gpio.h b/arch/arm/mach-prima2/include/mach/gpio.h
new file mode 100644
index 0000000..25673b1
--- /dev/null
+++ b/arch/arm/mach-prima2/include/mach/gpio.h
@@ -0,0 +1,34 @@ 
+/*
+ * arch/arm/mach-prima2/include/mach/gpio.h
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef __MACH_GPIO_H
+#define __MACH_GPIO_H
+
+#include <mach/irqs.h>
+
+#ifndef CONFIG_GPIO_SIRFCPLD
+#define ARCH_NR_GPIOS	(SIRFSOC_GPIO_BANK_SIZE * SIRFSOC_GPIO_NO_OF_BANKS)
+#else
+#define ARCH_NR_GPIOS	(SIRFSOC_GPIO_BANK_SIZE * SIRFSOC_GPIO_NO_OF_BANKS + \
+	SIRFSOC_GPIO_CPLD_SIZE + SIRFSOC_GPIO_IO_CPLD_SIZE + \
+	SIRFSOC_GPIO_HS_CPLD_SIZE)
+#endif
+
+#include <linux/errno.h>
+#include <asm-generic/gpio.h>
+
+#define gpio_get_value	__gpio_get_value
+#define gpio_set_value	__gpio_set_value
+#define gpio_cansleep	__gpio_cansleep
+#define gpio_to_irq	__gpio_to_irq
+
+void gpio_set_pull(unsigned gpio, int enable);
+void gpio_pull_down(unsigned gpio);
+void gpio_pull_up(unsigned gpio);
+
+#endif
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 3634986..c5da284 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -162,6 +162,12 @@  config GPIO_SCH
 	  The Intel Tunnel Creek processor has 5 GPIOs powered by the
 	  core power rail and 9 from suspend power supply.
 
+config GPIO_SIRF
+	bool "CSR SiRFprimaII GPIO"
+	default y
+	help
+	  Say yes here to support GPIO interface on CSR SiRFprimaII.
+
 config GPIO_VX855
 	tristate "VIA VX855/VX875 GPIO"
 	depends on MFD_SUPPORT && PCI
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 7207112..f4ffd0e 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -42,6 +42,7 @@  obj-$(CONFIG_GPIO_S5PC100)	+= gpio-s5pc100.o
 obj-$(CONFIG_GPIO_S5PV210)	+= gpio-s5pv210.o
 
 obj-$(CONFIG_GPIO_SCH)		+= gpio-sch.o
+obj-$(CONFIG_GPIO_SIRF)		+= gpio-sirf.o
 obj-$(CONFIG_GPIO_STMPE)	+= gpio-stmpe.o
 obj-$(CONFIG_GPIO_SX150X)	+= gpio-sx150x.o
 obj-$(CONFIG_GPIO_TC3589X)	+= gpio-tc3589x.o
diff --git a/drivers/gpio/gpio-sirf.c b/drivers/gpio/gpio-sirf.c
new file mode 100644
index 0000000..34b5820
--- /dev/null
+++ b/drivers/gpio/gpio-sirf.c
@@ -0,0 +1,529 @@ 
+/*
+ * GPIO controller driver for CSR SiRFprimaII
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <mach/regs-gpio.h>
+#include <mach/gpio.h>
+#include <mach/pinmux.h>
+
+#define SIRFSOC_IRQ_SIRFSOC_GPIO_GROUP0         43
+#define SIRFSOC_IRQ_SIRFSOC_GPIO_GROUP1         44
+#define SIRFSOC_IRQ_SIRFSOC_GPIO_GROUP2         45
+#define SIRFSOC_IRQ_SIRFSOC_GPIO_GROUP3         46
+#define SIRFSOC_IRQ_SIRFSOC_GPIO_GROUP4         47
+
+struct gpio_bank {
+	u8 group;
+	u16 irq;
+	u32 paden_bk_map;
+	u8 wake_mask;
+
+	spinlock_t lock;
+	struct gpio_chip chip;
+};
+
+static struct gpio_bank sirfsoc_gpio_bank[SIRFSOC_GPIO_NO_OF_BANKS] = {
+	{.group = 0, .irq = SIRFSOC_IRQ_SIRFSOC_GPIO_GROUP0,},
+	{.group = 1, .irq = SIRFSOC_IRQ_SIRFSOC_GPIO_GROUP1,},
+	{.group = 2, .irq = SIRFSOC_IRQ_SIRFSOC_GPIO_GROUP2,},
+	{.group = 3, .irq = SIRFSOC_IRQ_SIRFSOC_GPIO_GROUP3,},
+	{.group = 4, .irq = SIRFSOC_IRQ_SIRFSOC_GPIO_GROUP4,}
+};
+
+static DEFINE_SPINLOCK(gpio_lock);
+
+static struct gpio_bank *sirfsoc_irq_to_bank(unsigned int irq)
+{
+	int bk;
+
+	if ((irq < SIRFSOC_GPIO_IRQ_START) || (irq >= SIRFSOC_GPIO_IRQ_END)) {
+		printk(KERN_ALERT " Invalid GPIO IRQ/Bank: %d\n", irq);
+		return NULL;
+	}
+
+	bk = (irq - SIRFSOC_GPIO_IRQ_START) / SIRFSOC_GPIO_BANK_SIZE;
+	return &sirfsoc_gpio_bank[bk];
+}
+
+static int sirfsoc_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	return SIRFSOC_GPIO_IRQ_START + (chip->base + offset);
+}
+
+static int sirfsoc_irq_to_indx(unsigned int irq)
+{
+	return (irq - SIRFSOC_GPIO_IRQ_START) % SIRFSOC_GPIO_BANK_SIZE;
+}
+
+static struct gpio_bank *sirfsoc_gpio_to_bank(unsigned int gpio)
+{
+	unsigned int bk = gpio / SIRFSOC_GPIO_BANK_SIZE;
+
+	if (bk >= SIRFSOC_GPIO_NO_OF_BANKS) {
+		printk(KERN_ALERT "Invalid GPIO Number: %d\n", gpio);
+		return NULL;
+	} else {
+		return &sirfsoc_gpio_bank[bk];
+	}
+}
+
+static int sirfsoc_gpio_to_indx(unsigned int gpio)
+{
+	return gpio % SIRFSOC_GPIO_BANK_SIZE;
+}
+
+static void sirfsoc_gpio_irq_ack(struct irq_data *d)
+{
+	struct gpio_bank *bank = sirfsoc_irq_to_bank(d->irq);
+	int index = sirfsoc_irq_to_indx(d->irq);
+	u32 status, offset;
+	unsigned long flags;
+
+	if (bank != NULL) {
+		offset = SIRFSOC_GPIO_CTRL(bank->group, index);
+		spin_lock_irqsave(&gpio_lock, flags);
+
+		status = readl(sirfsoc_gpio_pinmux_base + offset);
+
+		writel(status, sirfsoc_gpio_pinmux_base + offset);
+		pr_debug("%s: ack gpio group %d index %d, status %#x\n",
+			__func__, bank->group, index,
+			readl(sirfsoc_gpio_pinmux_base + offset));
+		spin_unlock_irqrestore(&gpio_lock, flags);
+	}
+
+}
+
+static void _sirfsoc_gpio_irq_mask(unsigned int irq)
+{
+	struct gpio_bank *bank = sirfsoc_irq_to_bank(irq);
+	int index = sirfsoc_irq_to_indx(irq);
+	u32 status, offset;
+	unsigned long flags;
+
+	if (bank != NULL) {
+		pr_debug("%s: unmask gpio group %d index %d\n", __func__,
+			bank->group, index);
+		offset = SIRFSOC_GPIO_CTRL(bank->group, index);
+		spin_lock_irqsave(&gpio_lock, flags);
+		status = readl(sirfsoc_gpio_pinmux_base + offset);
+
+		status &= ~SIRFSOC_GPIO_CTL_INTR_EN_MASK;
+		status &= ~SIRFSOC_GPIO_CTL_INTR_STS_MASK;
+
+		writel(status, sirfsoc_gpio_pinmux_base + offset);
+
+		spin_unlock_irqrestore(&gpio_lock, flags);
+	}
+}
+
+
+static void sirfsoc_gpio_irq_mask(struct irq_data *d)
+{
+	_sirfsoc_gpio_irq_mask(d->irq);
+}
+
+static void sirfsoc_gpio_irq_unmask(struct irq_data *d)
+{
+	struct gpio_bank *bank = sirfsoc_irq_to_bank(d->irq);
+	int index = sirfsoc_irq_to_indx(d->irq);
+	u32 status, offset;
+	unsigned long flags;
+
+	if (bank != NULL) {
+		pr_debug("%s: unmask gpio group %d index %d\n", __func__,
+			bank->group, index);
+		offset = SIRFSOC_GPIO_CTRL(bank->group, index);
+
+		spin_lock_irqsave(&gpio_lock, flags);
+		status = readl(sirfsoc_gpio_pinmux_base + offset);
+
+		status &= ~SIRFSOC_GPIO_CTL_INTR_STS_MASK;
+		status |= SIRFSOC_GPIO_CTL_INTR_EN_MASK;
+
+		writel(status, sirfsoc_gpio_pinmux_base + offset);
+		spin_unlock_irqrestore(&gpio_lock, flags);
+	}
+}
+
+static int sirfsoc_gpio_irq_type(struct irq_data *d, unsigned type)
+{
+	struct gpio_bank *bank = sirfsoc_irq_to_bank(d->irq);
+	int index = sirfsoc_irq_to_indx(d->irq);
+	u32 status, offset;
+	unsigned long flags;
+
+	if (bank == NULL)
+		return -EINVAL;
+
+	offset = SIRFSOC_GPIO_CTRL(bank->group, index);
+	spin_lock_irqsave(&gpio_lock, flags);
+	status = readl(sirfsoc_gpio_pinmux_base + offset);
+	status &= ~SIRFSOC_GPIO_CTL_INTR_STS_MASK;
+
+	switch (type) {
+	case IRQ_TYPE_NONE:
+		break;
+	case IRQ_TYPE_EDGE_RISING:
+		status |= (SIRFSOC_GPIO_CTL_INTR_HIGH_MASK | SIRFSOC_GPIO_CTL_INTR_TYPE_MASK);
+		status &= ~SIRFSOC_GPIO_CTL_INTR_LOW_MASK;
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		status &= ~SIRFSOC_GPIO_CTL_INTR_HIGH_MASK;
+		status |= (SIRFSOC_GPIO_CTL_INTR_LOW_MASK | SIRFSOC_GPIO_CTL_INTR_TYPE_MASK);
+		break;
+	case IRQ_TYPE_EDGE_BOTH:
+		status |=
+			(SIRFSOC_GPIO_CTL_INTR_HIGH_MASK | SIRFSOC_GPIO_CTL_INTR_LOW_MASK |
+			 SIRFSOC_GPIO_CTL_INTR_TYPE_MASK);
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		status &= ~(SIRFSOC_GPIO_CTL_INTR_HIGH_MASK | SIRFSOC_GPIO_CTL_INTR_TYPE_MASK);
+		status |= SIRFSOC_GPIO_CTL_INTR_LOW_MASK;
+		break;
+	case IRQ_TYPE_LEVEL_HIGH:
+		status |= SIRFSOC_GPIO_CTL_INTR_HIGH_MASK;
+		status &= ~(SIRFSOC_GPIO_CTL_INTR_LOW_MASK | SIRFSOC_GPIO_CTL_INTR_TYPE_MASK);
+		break;
+	}
+
+	writel(status, sirfsoc_gpio_pinmux_base + offset);
+
+	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	return 0;
+}
+
+static struct irq_chip sirfsoc_irq_chip = {
+	.name = "SiRF SoC GPIO IRQ",
+	.irq_ack = sirfsoc_gpio_irq_ack,
+	.irq_mask = sirfsoc_gpio_irq_mask,
+	.irq_unmask = sirfsoc_gpio_irq_unmask,
+	.irq_set_type = sirfsoc_gpio_irq_type,
+};
+
+static void sirfsoc_gpio_handle_irq(unsigned int irq, struct irq_desc *desc)
+{
+	struct gpio_bank *bank = NULL;
+	u32 status, ctrl;
+	int i, index = 0;
+
+	pr_debug("%s: irq %d\n", __func__, irq);
+
+	for (i = 0; i < SIRFSOC_GPIO_NO_OF_BANKS; i++) {
+		if (sirfsoc_gpio_bank[i].irq == irq) {
+			bank = &sirfsoc_gpio_bank[i];
+			break;
+		}
+	}
+
+	if (bank == NULL) {
+		printk(KERN_ALERT " Invalid GPIO IRQ/Bank: %d\n", irq);
+		handle_bad_irq(irq, desc);
+		return;
+	}
+
+	status = readl(sirfsoc_gpio_pinmux_base + SIRFSOC_GPIO_INT_STATUS(bank->group));
+	if (!status) {
+		printk(KERN_WARNING
+			"%s: gpio group %d status %#x no interrupt is flaged\n",
+			__func__, bank->group, status);
+		handle_bad_irq(irq, desc);
+		return;
+	}
+
+	while (status) {
+		ctrl = readl(sirfsoc_gpio_pinmux_base + SIRFSOC_GPIO_CTRL(bank->group, index));
+
+		/*
+		 * Here we must check whether the corresponding GPIO's interrupt
+		 * has been enabled, otherwise just skip it
+		 */
+		if ((status & 0x1) && (ctrl & SIRFSOC_GPIO_CTL_INTR_EN_MASK)) {
+			pr_debug("%s: gpio group %d index %d happens\n",
+				__func__, bank->group, index);
+			irq =
+				(SIRFSOC_GPIO_IRQ_START +
+				 (bank->group * SIRFSOC_GPIO_BANK_SIZE)) + index;
+			generic_handle_irq(irq);
+		}
+
+		index++;
+		status = status >> 1;
+	}
+
+	return;
+}
+
+static inline void sirfsoc_gpio_set_input(struct gpio_bank *bank, unsigned ctrl_offset)
+{
+	u32 status;
+	status = readl(sirfsoc_gpio_pinmux_base + ctrl_offset);
+	status &= ~SIRFSOC_GPIO_CTL_OUT_EN_MASK;
+	writel(status, sirfsoc_gpio_pinmux_base + ctrl_offset);
+}
+
+static int sirfsoc_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+	struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip);
+	unsigned long flags;
+
+	spin_lock_irqsave(&bank->lock, flags);
+
+	/*set direction as input and disable/mask irq */
+	sirfsoc_gpio_set_input(bank, SIRFSOC_GPIO_CTRL(bank->group, offset));
+	_sirfsoc_gpio_irq_mask(sirfsoc_gpio_to_irq(chip, offset));
+	sirfsoc_get_gpio(bank->group, offset);
+
+	spin_unlock_irqrestore(&bank->lock, flags);
+	return 0;
+}
+
+static void sirfsoc_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+	struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip);
+	unsigned long flags;
+
+	spin_lock_irqsave(&bank->lock, flags);
+
+	/*disable irq */
+	_sirfsoc_gpio_irq_mask(sirfsoc_gpio_to_irq(chip, offset));
+
+	/*set gpio to input */
+	sirfsoc_gpio_set_input(bank, SIRFSOC_GPIO_CTRL(bank->group, offset));
+
+	if (bank->paden_bk_map & (1 << offset))
+		sirfsoc_get_gpio(bank->group, offset);
+	else
+		sirfsoc_put_gpio(bank->group, offset);
+}
+
+static int sirfsoc_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
+{
+	struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip);
+	int index = sirfsoc_gpio_to_indx(gpio);
+	unsigned long flags;
+	unsigned offset;
+
+	offset = SIRFSOC_GPIO_CTRL(bank->group, index);
+	spin_lock_irqsave(&bank->lock, flags);
+	sirfsoc_gpio_set_input(bank, offset);
+	spin_unlock_irqrestore(&bank->lock, flags);
+	return 0;
+}
+
+static inline void sirfsoc_gpio_set_output(struct gpio_bank *bank, unsigned offset,
+	int value)
+{
+	u32 status;
+
+	status = readl(sirfsoc_gpio_pinmux_base + offset);
+	if (value)
+		status |= SIRFSOC_GPIO_CTL_DATAOUT_MASK;
+	else
+		status &= ~SIRFSOC_GPIO_CTL_DATAOUT_MASK;
+
+	status &= ~SIRFSOC_GPIO_CTL_INTR_EN_MASK;
+	status |= SIRFSOC_GPIO_CTL_OUT_EN_MASK;
+
+	writel(status, sirfsoc_gpio_pinmux_base + offset);
+}
+
+static int sirfsoc_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, int value)
+{
+	struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip);
+	int index = sirfsoc_gpio_to_indx(gpio);
+	u32 offset;
+	unsigned long flags;
+
+	offset = SIRFSOC_GPIO_CTRL(bank->group, index);
+	spin_lock_irqsave(&gpio_lock, flags);
+	sirfsoc_gpio_set_output(bank, offset, value);
+	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	return 0;
+}
+
+void gpio_set_pull(unsigned gpio, int enable)
+{
+	struct gpio_bank *bank = sirfsoc_gpio_to_bank(gpio);
+	int index = sirfsoc_gpio_to_indx(gpio);
+	u32 status, offset;
+	unsigned long flags;
+
+	BUG_ON(!bank);
+
+	offset = SIRFSOC_GPIO_CTRL(bank->group, index);
+
+	spin_lock_irqsave(&gpio_lock, flags);
+
+	status = readl(sirfsoc_gpio_pinmux_base + offset);
+
+	if (enable)
+		status |= SIRFSOC_GPIO_CTL_PULL_MASK;
+	else
+		status &= ~SIRFSOC_GPIO_CTL_PULL_MASK;
+
+	writel(status, sirfsoc_gpio_pinmux_base + offset);
+
+	spin_unlock_irqrestore(&gpio_lock, flags);
+}
+EXPORT_SYMBOL(gpio_set_pull);
+
+void gpio_pull_up(unsigned int gpio)
+{
+	struct gpio_bank *bank = sirfsoc_gpio_to_bank(gpio);
+	int index = sirfsoc_gpio_to_indx(gpio);
+	u32 status, offset;
+	unsigned long flags;
+
+	BUG_ON(!bank);
+
+	gpio_set_pull(gpio, 1);
+
+	offset = SIRFSOC_GPIO_CTRL(bank->group, index);
+
+	spin_lock_irqsave(&gpio_lock, flags);
+
+	status = readl(sirfsoc_gpio_pinmux_base + offset);
+	status |= SIRFSOC_GPIO_CTL_PULL_HIGH;
+	writel(status, sirfsoc_gpio_pinmux_base + offset);
+
+	spin_unlock_irqrestore(&gpio_lock, flags);
+}
+EXPORT_SYMBOL(gpio_pull_up);
+
+void gpio_pull_down(unsigned int gpio)
+{
+	struct gpio_bank *bank = sirfsoc_gpio_to_bank(gpio);
+	int index = sirfsoc_gpio_to_indx(gpio);
+	u32 status, offset;
+	unsigned long flags;
+
+	BUG_ON(!bank);
+
+	gpio_set_pull(gpio, 1);
+
+	offset = SIRFSOC_GPIO_CTRL(bank->group, index);
+
+	spin_lock_irqsave(&gpio_lock, flags);
+
+	status = readl(sirfsoc_gpio_pinmux_base + offset);
+	status &= ~SIRFSOC_GPIO_CTL_PULL_HIGH;
+	writel(status, sirfsoc_gpio_pinmux_base + offset);
+
+	spin_unlock_irqrestore(&gpio_lock, flags);
+}
+EXPORT_SYMBOL(gpio_pull_down);
+
+static int sirfsoc_gpio_get_value(struct gpio_chip *chip, unsigned offset)
+{
+	struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip);
+	u32 status;
+
+	status = readl(sirfsoc_gpio_pinmux_base + SIRFSOC_GPIO_CTRL(bank->group, offset));
+
+	return !!(status & SIRFSOC_GPIO_CTL_DATAIN_MASK);
+}
+
+static void sirfsoc_gpio_set_value(struct gpio_chip *chip, unsigned offset,
+	int value)
+{
+	struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip);
+	u32 status;
+
+	status = readl(sirfsoc_gpio_pinmux_base + SIRFSOC_GPIO_CTRL(bank->group, offset));
+	if (value)
+		status |= SIRFSOC_GPIO_CTL_DATAOUT_MASK;
+	else
+		status &= ~SIRFSOC_GPIO_CTL_DATAOUT_MASK;
+	writel(status, sirfsoc_gpio_pinmux_base + SIRFSOC_GPIO_CTRL(bank->group, offset));
+}
+
+static int sirfsoc_gpio_probe(struct platform_device *pdev)
+{
+	int i;
+
+	struct gpio_bank *bank;
+	int gpio = 0;
+
+	for (i = 0; i < SIRFSOC_GPIO_NO_OF_BANKS; i++) {
+		bank = &sirfsoc_gpio_bank[i];
+		spin_lock_init(&bank->lock);
+		bank->paden_bk_map = readl(sirfsoc_gpio_pinmux_base + SIRFSOC_GPIO_PAD_EN(i));
+		irq_set_chained_handler(bank->irq, sirfsoc_gpio_handle_irq);
+
+		bank->chip.request = sirfsoc_gpio_request;
+		bank->chip.free = sirfsoc_gpio_free;
+		bank->chip.direction_input = sirfsoc_gpio_direction_input;
+		bank->chip.get = sirfsoc_gpio_get_value;
+		bank->chip.direction_output = sirfsoc_gpio_direction_output;
+		bank->chip.set = sirfsoc_gpio_set_value;
+		bank->chip.to_irq = sirfsoc_gpio_to_irq;
+
+		bank->chip.label = "gpio";
+		bank->chip.base = gpio;
+
+		gpio += SIRFSOC_GPIO_BANK_SIZE;
+		bank->chip.ngpio = SIRFSOC_GPIO_BANK_SIZE;
+		gpiochip_add(&bank->chip);
+	}
+
+	for (i = SIRFSOC_GPIO_IRQ_START; i < SIRFSOC_GPIO_IRQ_END; i++) {
+		irq_set_chip(i, &sirfsoc_irq_chip);
+		irq_set_handler(i, handle_level_irq);
+		set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
+	}
+
+	return 0;
+}
+
+static int sirfsoc_gpio_remove(struct platform_device *dev)
+{
+	return 0;
+}
+
+static const struct of_device_id sirf_gpio_of_match[] = {
+	{.compatible = "sirf,prima2-gpio", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, sirf_gpio_of_match);
+
+static struct platform_driver sirfsoc_gpio_driver = {
+	.driver		= {
+		.name	= "sirfsoc_gpio",
+		.of_match_table = sirf_gpio_of_match,
+	},
+	.probe		= sirfsoc_gpio_probe,
+	.remove		= sirfsoc_gpio_remove,
+};
+
+static int __init sirfsoc_gpio_init(void)
+{
+	return platform_driver_register(&sirfsoc_gpio_driver);
+}
+core_initcall(sirfsoc_gpio_init);
+
+static void __exit sirfsoc_gpio_exit(void)
+{
+	platform_driver_unregister(&sirfsoc_gpio_driver);
+}
+module_exit(sirfsoc_gpio_exit);
+
+MODULE_DESCRIPTION("SiRFSoC gpio driver");
+MODULE_AUTHOR("Yuping Luo <yuping.luo@csr.com>, Barry Song <baohua.song@csr.com>");
+MODULE_LICENSE("GPL");