* [PATCH v1 5/6] usb: otg: add device tree support to otg library
From: Mark Rutland @ 2013-01-22 10:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358848694-20145-6-git-send-email-kishon@ti.com>
> +struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev,
> + const char *phandle, u8 index)
> +{
> + struct usb_phy *phy = NULL, **ptr;
> + unsigned long flags;
> + struct device_node *node;
> +
> + if (!dev->of_node) {
> + dev_dbg(dev, "device does not have a device node entry\n");
> + return ERR_PTR(-EINVAL);
> + }
> +
> + node = of_parse_phandle(dev->of_node, phandle, index);
> + if (!node) {
> + dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle,
> + dev->of_node->full_name);
> + return ERR_PTR(-ENODEV);
> + }
At any point past this, node's refcount is incremented (done automatically by
of_parse_phandle => of_find_node_by_phandle).
> +
> + ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL);
> + if (!ptr) {
> + dev_dbg(dev, "failed to allocate memory for devres\n");
> + return ERR_PTR(-ENOMEM);
> + }
> +
> + spin_lock_irqsave(&phy_lock, flags);
> +
> + phy = __of_usb_find_phy(node);
> + if (IS_ERR(phy) || !try_module_get(phy->dev->driver->owner)) {
> + phy = ERR_PTR(-EPROBE_DEFER);
> + devres_free(ptr);
> + goto err0;
> + }
> +
> + *ptr = phy;
> + devres_add(dev, ptr);
> +
> + get_device(phy->dev);
> +
> +err0:
> + spin_unlock_irqrestore(&phy_lock, flags);
> +
> + return phy;
> +}
This means that on all of the exit paths, node's refcount is left incremented
incorrectly. You'll need an of_node_put(node) on each exit path.
Thanks,
Mark.
^ permalink raw reply
* [PATCH V2 2/2] mmc: mmci: Move ios_handler functionality into the driver
From: Russell King - ARM Linux @ 2013-01-22 10:37 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20130122102010.GL6857@gmail.com>
On Tue, Jan 22, 2013 at 10:20:10AM +0000, Lee Jones wrote:
> On Tue, 22 Jan 2013, Russell King - ARM Linux wrote:
>
> > On Tue, Jan 22, 2013 at 10:53:42AM +0100, Linus Walleij wrote:
> > > On Tue, Jan 22, 2013 at 10:00 AM, Lee Jones <lee.jones@linaro.org> wrote:
> > >
> > > >> From: Lee Jones <lee.jones@linaro.org>
> > > >>
> > > >> There are currently two instances of the ios_handler being used.
> > > >> Both of which mearly toy with some regulator settings. Now there
> > > >> is a GPIO regulator API, we can use that instead, and lessen the
> > > >> per platform burden. By doing this, we also become more Device
> > > >> Tree compatible.
> > > >
> > > > Russell,
> > > >
> > > > Why is this patch in your tree with Ulf as the Author?
> > >
> > > This is because of the way Russell's patch tracker works, it sets
> > > Author: to the name of the person using the patch tracker and
> > > discards the From: field in the beginning of the patch which
> > > git am will conversely respect.
> >
> > Actually, the reverse. It does now respect the From: line, but the
> > From: line will be ignored for all notifications about the patch
> > because the patch system was never built to parse the actual comments
> > when sending out the email notifications.
>
> So what do I have to do to reaffirm myself as the author?
I'd need to recommit the patch with the right information, which isn't
that easy to do.
^ permalink raw reply
* [PATCH 2/5] ARM: dts: twl6030: Add PWM support
From: Benoit Cousson @ 2013-01-22 10:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358519794-13214-3-git-send-email-peter.ujfalusi@ti.com>
Hi Peter,
Just one minor typo here.
On 01/18/2013 03:36 PM, Peter Ujfalusi wrote:
> Enable support for the PWMs and LED as PWM drivers.
>
> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
> ---
> arch/arm/boot/dts/twl6030.dtsi | 12 ++++++++++++
> 1 file changed, 12 insertions(+)
>
> diff --git a/arch/arm/boot/dts/twl6030.dtsi b/arch/arm/boot/dts/twl6030.dtsi
> index 9996cfc..d9b8b21 100644
> --- a/arch/arm/boot/dts/twl6030.dtsi
> +++ b/arch/arm/boot/dts/twl6030.dtsi
> @@ -91,4 +91,16 @@
> compatible = "ti,twl6030-usb";
> interrupts = <4>, <10>;
> };
> +
> + twl_pwm: pwm {
> + /* provides two PWMs (id 0, 1 for PWM1 and PWM2) */
> + compatible = "ti,twl6030-pwm";
> + #pwm-cells = <2>;
> + };
> +
> + twl_pwmled: pwmled {
> + /* provides one PWM (id 0 for Charing indicator LED) */
typo: charging.
Regards,
Benoit
> + compatible = "ti,twl6030-pwmled";
> + #pwm-cells = <2>;
> + };
> };
>
^ permalink raw reply
* [PATCH v7 09/15] gpio: pl061: set initcall level to module init
From: Russell King - ARM Linux @ 2013-01-22 10:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CACRpkdY4MHLjs1Lnj+8Ts6oZOjG14pDssCQ09PX-a_5nGRs1sg@mail.gmail.com>
On Tue, Jan 22, 2013 at 10:42:11AM +0100, Linus Walleij wrote:
> On Mon, Jan 21, 2013 at 5:24 PM, Pawel Moll <pawel.moll@arm.com> wrote:
>
> > Hm. Doesn't this make the MMCI probing depending on the module_init
> > order? As in: wouldn't it make the mmci probe completely fail (not even
> > defer it) if it was called before the pl061? In that case even your,
> > Linus, hack with inverting the CD status wouldn't work...
>
> According to Haojian it works, but for sure the MMCI driver *should*
> (on the eternal list of SHOULDDO) bail out and return any
Rather than talking about what should and should not, why not look at
the code - it only takes a few moments to check what the behaviour
would be. And it is correct - errors are correctly propagated out of
the probe function.
That's hardly surprising given who's supposed to be the maintainer of
that driver.
^ permalink raw reply
* [PATCH 2/5] ARM: dts: twl6030: Add PWM support
From: Peter Ujfalusi @ 2013-01-22 10:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <50FE6C5E.50802@ti.com>
HI Benoit,
On 01/22/2013 11:39 AM, Benoit Cousson wrote:
>> +
>> + twl_pwm: pwm {
>> + /* provides two PWMs (id 0, 1 for PWM1 and PWM2) */
>> + compatible = "ti,twl6030-pwm";
>> + #pwm-cells = <2>;
>> + };
>> +
>> + twl_pwmled: pwmled {
>> + /* provides one PWM (id 0 for Charing indicator LED) */
>
> typo: charging.
Aargh. Corrected in the branch.
Thanks,
P?ter
^ permalink raw reply
* [PATCH 2/5] ARM: dts: twl6030: Add PWM support
From: Benoit Cousson @ 2013-01-22 10:54 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <50FE6EB7.8070605@ti.com>
On 01/22/2013 11:49 AM, Peter Ujfalusi wrote:
> HI Benoit,
>
> On 01/22/2013 11:39 AM, Benoit Cousson wrote:
>>> +
>>> + twl_pwm: pwm {
>>> + /* provides two PWMs (id 0, 1 for PWM1 and PWM2) */
>>> + compatible = "ti,twl6030-pwm";
>>> + #pwm-cells = <2>;
>>> + };
>>> +
>>> + twl_pwmled: pwmled {
>>> + /* provides one PWM (id 0 for Charing indicator LED) */
>>
>> typo: charging.
>
> Aargh. Corrected in the branch.
Thanks,
Pulled and pushed into git://git.kernel.org/pub/scm/linux/kernel/git/bcousson/linux-omap-dt.git for_3.9/dts
Regards,
Benoit
^ permalink raw reply
* [PATCH 06/15] clk: export __clk_get_name
From: Fabio Estevam @ 2013-01-22 10:54 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358788568-11137-7-git-send-email-arnd@arndb.de>
On Mon, Jan 21, 2013 at 3:15 PM, Arnd Bergmann <arnd@arndb.de> wrote:
> The __clk_get_name is used by drivers that provide their
> own clocks, which have traditionally all been built-in.
> The new imx drm driver however provides clocks but can
> be built as a module, so we need to export __clk_get_name.
>
> While this is only a staging driver, it's likely that
> there will be others like it in the future.
>
> Without this patch, building ARM allmodconfig results in:
>
> ERROR: "__clk_get_name" [drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.ko] undefined!
>
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> Cc: Sascha Hauer <s.hauer@pengutronix.de>
> Cc: Mike Turquette <mturquette@linaro.org>
A fix from Niels de Vos has already been applied into clk-next:
https://patchwork.kernel.org/patch/1871981/
^ permalink raw reply
* [PATCH V2 2/2] mmc: mmci: Move ios_handler functionality into the driver
From: Lee Jones @ 2013-01-22 10:56 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20130122103723.GI23505@n2100.arm.linux.org.uk>
On Tue, 22 Jan 2013, Russell King - ARM Linux wrote:
> On Tue, Jan 22, 2013 at 10:20:10AM +0000, Lee Jones wrote:
> > On Tue, 22 Jan 2013, Russell King - ARM Linux wrote:
> >
> > > On Tue, Jan 22, 2013 at 10:53:42AM +0100, Linus Walleij wrote:
> > > > On Tue, Jan 22, 2013 at 10:00 AM, Lee Jones <lee.jones@linaro.org> wrote:
> > > >
> > > > >> From: Lee Jones <lee.jones@linaro.org>
> > > > >>
> > > > >> There are currently two instances of the ios_handler being used.
> > > > >> Both of which mearly toy with some regulator settings. Now there
> > > > >> is a GPIO regulator API, we can use that instead, and lessen the
> > > > >> per platform burden. By doing this, we also become more Device
> > > > >> Tree compatible.
> > > > >
> > > > > Russell,
> > > > >
> > > > > Why is this patch in your tree with Ulf as the Author?
> > > >
> > > > This is because of the way Russell's patch tracker works, it sets
> > > > Author: to the name of the person using the patch tracker and
> > > > discards the From: field in the beginning of the patch which
> > > > git am will conversely respect.
> > >
> > > Actually, the reverse. It does now respect the From: line, but the
> > > From: line will be ignored for all notifications about the patch
> > > because the patch system was never built to parse the actual comments
> > > when sending out the email notifications.
> >
> > So what do I have to do to reaffirm myself as the author?
>
> I'd need to recommit the patch with the right information, which isn't
> that easy to do.
Are you saying that you won't do it? :)
Is there anything I can do to make the process easier?
--
Lee Jones
Linaro ST-Ericsson Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* [PATCH] ARM: Versatile Express: extend the MPIDR range used for pen release check
From: Lorenzo Pieralisi @ 2013-01-22 10:56 UTC (permalink / raw)
To: linux-arm-kernel
In ARM multi-cluster systems the MPIDR affinity level 0 cannot be used as a
single cpu identifier, affinity levels 1 and 2 must be taken into account as
well.
This patch extends the MPIDR usage to affinity levels 1 and 2 in versatile
secondary cores start up code in order to compare the passed pen_release
value with the full-blown affinity mask.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com>
---
arch/arm/plat-versatile/headsmp.S | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/plat-versatile/headsmp.S b/arch/arm/plat-versatile/headsmp.S
index dd703ef..b178d44 100644
--- a/arch/arm/plat-versatile/headsmp.S
+++ b/arch/arm/plat-versatile/headsmp.S
@@ -20,7 +20,7 @@
*/
ENTRY(versatile_secondary_startup)
mrc p15, 0, r0, c0, c0, 5
- and r0, r0, #15
+ bic r0, #0xff000000
adr r4, 1f
ldmia r4, {r5, r6}
sub r4, r4, r5
--
1.7.12
^ permalink raw reply related
* [PATCH] ARM: architected timers: allow dt based discovery using clocksource_of_init
From: Mark Rutland @ 2013-01-22 11:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAJuYYwSfH8jTterd6aNc_Aw81PBschfCUf=REKB35=Y4a=i_7A@mail.gmail.com>
On Mon, Jan 21, 2013 at 06:30:47PM +0000, Thomas Abraham wrote:
> Hi Stephen,
>
> On 21 January 2013 09:21, Stephen Warren <swarren@wwwdotorg.org> wrote:
> > On 01/20/2013 06:22 PM, Thomas Abraham wrote:
> >> Add an entry in __clksrc_of_table so that ARMv7 architected timer is
> >> discoverable using call to clocksource_of_init.
> >
> >> diff --git a/arch/arm/kernel/arch_timer.c b/arch/arm/kernel/arch_timer.c
> >
> >> +#ifdef CONFIG_CLKSRC_OF
> >> +CLOCKSOURCE_OF_DECLARE(armv7_timer, "arm,armv7-timer", arch_timer_of_register)
> >> +#endif
> >
> > I wonder if we shouldn't enhance include/linux/clocksource.h to define
> > CLOCKSOURCE_OF_DECLARE even when !CONFIG_CLKSRC_OF; that way, drivers
> > wouldn't need that ifdef.
>
> Yes, it will be helpful to have a !CONFIG_CLKSRC_OF version of
> CLOCKSOURCE_OF_DECLARE. And can CONFIG_CLKSRC_OF be enabled by default
> for all ARM platforms?
>
> Thanks,
> Thomas.
Thomas,
Once the above is worked out, could you send me the updated patch with a
pointer to whichever {branch,patch}(es) I need for CLKSRC_OF?
Thanks,
Mark.
^ permalink raw reply
* [PATCH] ARM: davinci: da850 evm: pass platform data for adv7343 encoder
From: Prabhakar Lad @ 2013-01-22 11:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358237081-2584-1-git-send-email-prabhakar.lad@ti.com>
Sekhar,
On Tue, Jan 15, 2013 at 1:34 PM, Lad, Prabhakar
<prabhakar.csengg@gmail.com> wrote:
> Without this patch the adv7343 encoder was being set to default
> configuration which caused display not to work on this board.
> This patch passes the necessary platform data required for adv7343
> encoder to work on da850 evm.
>
> Signed-off-by: Lad, Prabhakar <prabhakar.lad@ti.com>
> ---
> This patch is dependent on http://patchwork.linuxtv.org/patch/16272/
>
Since this patch depends on media [1] , I would like to get this patch
through media tree as fix for 3.8. Could you review and ACK it ?
[1] http://patchwork.linuxtv.org/patch/16391/
Regards,
--Prabhakar
> arch/arm/mach-davinci/board-da850-evm.c | 13 +++++++++++++
> 1 files changed, 13 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c
> index 0299915..d0e3ec3 100644
> --- a/arch/arm/mach-davinci/board-da850-evm.c
> +++ b/arch/arm/mach-davinci/board-da850-evm.c
> @@ -1256,11 +1256,24 @@ static struct vpif_capture_config da850_vpif_capture_config = {
> };
>
> /* VPIF display configuration */
> +
> +static struct adv7343_platform_data adv7343_pdata = {
> + .mode_config = {
> + .dac_3 = 1,
> + .dac_2 = 1,
> + .dac_1 = 1,
> + },
> + .sd_config = {
> + .sd_dac_out1 = 1,
> + },
> +};
> +
> static struct vpif_subdev_info da850_vpif_subdev[] = {
> {
> .name = "adv7343",
> .board_info = {
> I2C_BOARD_INFO("adv7343", 0x2a),
> + .platform_data = &adv7343_pdata,
> },
> },
> };
> --
> 1.7.4.1
>
^ permalink raw reply
* [PATCH] ARM: davinci: da850 evm: pass platform data for adv7343 encoder
From: Sekhar Nori @ 2013-01-22 12:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CA+V-a8t26rMT+Prz-p5627AYiv2Nndr57mxE4E6F9UJ3CkRzng@mail.gmail.com>
On 1/22/2013 5:27 PM, Prabhakar Lad wrote:
> Sekhar,
>
> On Tue, Jan 15, 2013 at 1:34 PM, Lad, Prabhakar
> <prabhakar.csengg@gmail.com> wrote:
>> Without this patch the adv7343 encoder was being set to default
>> configuration which caused display not to work on this board.
>> This patch passes the necessary platform data required for adv7343
>> encoder to work on da850 evm.
>>
>> Signed-off-by: Lad, Prabhakar <prabhakar.lad@ti.com>
>> ---
>> This patch is dependent on http://patchwork.linuxtv.org/patch/16272/
>>
> Since this patch depends on media [1] , I would like to get this patch
> through media tree as fix for 3.8. Could you review and ACK it ?
The patch looks good to me. Feel free to add my
Acked-by: Sekhar Nori <nsekhar@ti.com>
and merge through the media tree to ease the dependencies.
Thanks,
Sekhar
>
> [1] http://patchwork.linuxtv.org/patch/16391/
>
> Regards,
> --Prabhakar
>
>> arch/arm/mach-davinci/board-da850-evm.c | 13 +++++++++++++
>> 1 files changed, 13 insertions(+), 0 deletions(-)
>>
>> diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c
>> index 0299915..d0e3ec3 100644
>> --- a/arch/arm/mach-davinci/board-da850-evm.c
>> +++ b/arch/arm/mach-davinci/board-da850-evm.c
>> @@ -1256,11 +1256,24 @@ static struct vpif_capture_config da850_vpif_capture_config = {
>> };
>>
>> /* VPIF display configuration */
>> +
>> +static struct adv7343_platform_data adv7343_pdata = {
>> + .mode_config = {
>> + .dac_3 = 1,
>> + .dac_2 = 1,
>> + .dac_1 = 1,
>> + },
>> + .sd_config = {
>> + .sd_dac_out1 = 1,
>> + },
>> +};
>> +
>> static struct vpif_subdev_info da850_vpif_subdev[] = {
>> {
>> .name = "adv7343",
>> .board_info = {
>> I2C_BOARD_INFO("adv7343", 0x2a),
>> + .platform_data = &adv7343_pdata,
>> },
>> },
>> };
>> --
>> 1.7.4.1
>>
>
>
^ permalink raw reply
* [PATCH v5 9/9] ARM: davinci: da850: Added dsp clock definition
From: Sekhar Nori @ 2013-01-22 12:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1357863807-380-10-git-send-email-rtivy@ti.com>
Rob,
On 1/11/2013 5:53 AM, Robert Tivy wrote:
> Added dsp clock definition, keyed to "davinci-rproc.0".
>
> Signed-off-by: Robert Tivy <rtivy@ti.com>
> ---
> arch/arm/mach-davinci/da850.c | 9 +++++++++
> 1 file changed, 9 insertions(+)
>
> diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c
> index 097fcb2..50107c5 100644
> --- a/arch/arm/mach-davinci/da850.c
> +++ b/arch/arm/mach-davinci/da850.c
> @@ -375,6 +375,14 @@ static struct clk sata_clk = {
> .flags = PSC_FORCE,
> };
>
> +static struct clk dsp_clk = {
> + .name = "dsp",
> + .parent = &pll0_sysclk1,
> + .domain = DAVINCI_GPSC_DSPDOMAIN,
> + .lpsc = DA8XX_LPSC0_GEM,
> + .flags = PSC_LRST,
> +};
> +
> static struct clk_lookup da850_clks[] = {
> CLK(NULL, "ref", &ref_clk),
> CLK(NULL, "pll0", &pll0_clk),
> @@ -421,6 +429,7 @@ static struct clk_lookup da850_clks[] = {
> CLK("spi_davinci.1", NULL, &spi1_clk),
> CLK("vpif", NULL, &vpif_clk),
> CLK("ahci", NULL, &sata_clk),
> + CLK("davinci-rproc.0", NULL, &dsp_clk),
Adding this clock node without the having the driver probed leads to
kernel hang while booting. With CONFIG_DAVINCI_RESET_CLOCKS=n, the
kernel boot fine. It looks like there is some trouble disabling the
clock if it is not used. Can you please check this issue?
Thanks,
Sekhar
^ permalink raw reply
* [stericsson:dbx500-prcmu-mailbox 1/10] drivers/mailbox/mailbox.c:41:39: error: 'CONFIG_OMAP_MBOX_KFIFO_SIZE' undeclared here (not in a function)
From: kbuild test robot @ 2013-01-22 12:05 UTC (permalink / raw)
To: linux-arm-kernel
tree: git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-stericsson.git dbx500-prcmu-mailbox
head: 5a9f6660cdca2b31e9e51d096a815ce2c12c29d7
commit: 9f5fa59c7f06a3ff7c3ede2816bbfaa96573be62 [1/10] mailbox: OMAP: introduce mailbox framework
config: make ARCH=x86_64 allmodconfig
Note: the stericsson/dbx500-prcmu-mailbox HEAD 5a9f666 builds fine.
It only hurts bisectibility.
All error/warnings:
>> drivers/mailbox/mailbox.c:41:39: error: 'CONFIG_OMAP_MBOX_KFIFO_SIZE' undeclared here (not in a function)
drivers/mailbox/mailbox.c: In function 'mbox_rx_work':
>> drivers/mailbox/mailbox.c:155:5: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
vim +/CONFIG_OMAP_MBOX_KFIFO_SIZE +41 drivers/mailbox/mailbox.c
35
36 static struct omap_mbox **mboxes;
37
38 static int mbox_configured;
39 static DEFINE_MUTEX(mbox_configured_lock);
40
> 41 static unsigned int mbox_kfifo_size = CONFIG_OMAP_MBOX_KFIFO_SIZE;
42 module_param(mbox_kfifo_size, uint, S_IRUGO);
43 MODULE_PARM_DESC(mbox_kfifo_size, "Size of omap's mailbox kfifo (bytes)");
44
45 /* Mailbox FIFO handle functions */
46 static inline mbox_msg_t mbox_fifo_read(struct omap_mbox *mbox)
47 {
48 return mbox->ops->fifo_read(mbox);
49 }
50 static inline void mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg)
51 {
52 mbox->ops->fifo_write(mbox, msg);
53 }
54 static inline int mbox_fifo_empty(struct omap_mbox *mbox)
55 {
56 return mbox->ops->fifo_empty(mbox);
57 }
58 static inline int mbox_fifo_full(struct omap_mbox *mbox)
59 {
60 return mbox->ops->fifo_full(mbox);
61 }
62
63 /* Mailbox IRQ handle functions */
64 static inline void ack_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
65 {
66 if (mbox->ops->ack_irq)
67 mbox->ops->ack_irq(mbox, irq);
68 }
69 static inline int is_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
70 {
71 return mbox->ops->is_irq(mbox, irq);
72 }
73
74 /*
75 * message sender
76 */
77 static int __mbox_poll_for_space(struct omap_mbox *mbox)
78 {
79 int ret = 0, i = 1000;
80
81 while (mbox_fifo_full(mbox)) {
82 if (mbox->ops->type == OMAP_MBOX_TYPE2)
83 return -1;
84 if (--i == 0)
85 return -1;
86 udelay(1);
87 }
88 return ret;
89 }
90
91 int omap_mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg)
92 {
93 struct omap_mbox_queue *mq = mbox->txq;
94 int ret = 0, len;
95
96 spin_lock_bh(&mq->lock);
97
98 if (kfifo_avail(&mq->fifo) < sizeof(msg)) {
99 ret = -ENOMEM;
100 goto out;
101 }
102
103 if (kfifo_is_empty(&mq->fifo) && !__mbox_poll_for_space(mbox)) {
104 mbox_fifo_write(mbox, msg);
105 goto out;
106 }
107
108 len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
109 WARN_ON(len != sizeof(msg));
110
111 tasklet_schedule(&mbox->txq->tasklet);
112
113 out:
114 spin_unlock_bh(&mq->lock);
115 return ret;
116 }
117 EXPORT_SYMBOL(omap_mbox_msg_send);
118
119 static void mbox_tx_tasklet(unsigned long tx_data)
120 {
121 struct omap_mbox *mbox = (struct omap_mbox *)tx_data;
122 struct omap_mbox_queue *mq = mbox->txq;
123 mbox_msg_t msg;
124 int ret;
125
126 while (kfifo_len(&mq->fifo)) {
127 if (__mbox_poll_for_space(mbox)) {
128 omap_mbox_enable_irq(mbox, IRQ_TX);
129 break;
130 }
131
132 ret = kfifo_out(&mq->fifo, (unsigned char *)&msg,
133 sizeof(msg));
134 WARN_ON(ret != sizeof(msg));
135
136 mbox_fifo_write(mbox, msg);
137 }
138 }
139
140 /*
141 * Message receiver(workqueue)
142 */
143 static void mbox_rx_work(struct work_struct *work)
144 {
145 struct omap_mbox_queue *mq =
146 container_of(work, struct omap_mbox_queue, work);
147 mbox_msg_t msg;
148 int len;
149
150 while (kfifo_len(&mq->fifo) >= sizeof(msg)) {
151 len = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
152 WARN_ON(len != sizeof(msg));
153
154 blocking_notifier_call_chain(&mq->mbox->notifier, len,
155 (void *)msg);
156 spin_lock_irq(&mq->lock);
157 if (mq->full) {
158 mq->full = false;
---
0-DAY kernel build testing backend Open Source Technology Center
http://lists.01.org/mailman/listinfo/kbuild Intel Corporation
^ permalink raw reply
* [PATCH 0/6 v14] gpio: Add block GPIO
From: Roland Stigge @ 2013-01-22 12:06 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
Applies to v3.8-rc4
Changes since v13:
* Adjusted __raw_readl to readl_relaxed, __raw_writel to writel_relaxed
(forward porting of pinctrl-at91.c)
* Adjusted to mainline change: Removed __devinit and __devinitdata
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, Mark Rutland and
Nicolas Ferre 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 1/6 v14] gpio: Add a block GPIO API to gpiolib
From: Roland Stigge @ 2013-01-22 12:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358856404-8975-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 2/6 v14] gpio: Add sysfs support to block GPIO API
From: Roland Stigge @ 2013-01-22 12:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358856404-8975-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 3/6 v14] gpio: Add userland device interface to block GPIO
From: Roland Stigge @ 2013-01-22 12:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358856404-8975-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 4/6 v14] gpiolib: Fix default attributes for class
From: Roland Stigge @ 2013-01-22 12:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358856404-8975-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 2/2] ARM: mxs: Add OF props for MX23 LRADC
From: Jonathan Cameron @ 2013-01-22 12:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358798722-25102-2-git-send-email-marex@denx.de>
On 01/21/2013 08:05 PM, Marek Vasut wrote:
> Add interrupt mapping and compatible string for MX23 LRADC.
Series looks fine to me, but obviously I'd like an ACK for this one
from Shawn before taking it.
Thanks,
Jonathan
>
> Signed-off-by: Marek Vasut <marex@denx.de>
> Cc: Fabio Estevam <fabio.estevam@freescale.com>
> Cc: Shawn Guo <shawn.guo@linaro.org>
> ---
> arch/arm/boot/dts/imx23.dtsi | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/arch/arm/boot/dts/imx23.dtsi b/arch/arm/boot/dts/imx23.dtsi
> index 65415c5..56afcf4 100644
> --- a/arch/arm/boot/dts/imx23.dtsi
> +++ b/arch/arm/boot/dts/imx23.dtsi
> @@ -391,7 +391,9 @@
> };
>
> lradc at 80050000 {
> + compatible = "fsl,imx23-lradc";
> reg = <0x80050000 0x2000>;
> + interrupts = <36 37 38 39 40 41 42 43 44>;
> status = "disabled";
> };
>
>
^ permalink raw reply
* [PATCH 5/6 v14] gpio: Add device tree support to block GPIO API
From: Roland Stigge @ 2013-01-22 12:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358856404-8975-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 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[] = {
+ { .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 6/6 v14] gpio: Add block gpio to several gpio drivers
From: Roland Stigge @ 2013-01-22 12:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358856404-8975-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>
For pinctrl-at91.c:
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
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(®s->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(®s->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(®s->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(®s->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, ®_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)
@@ -1124,6 +1125,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 = readl_relaxed(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;
+ writel_relaxed(~mask, pio + PIO_OWDR);
+ writel_relaxed(mask, pio + PIO_OWER);
+ }
+ writel_relaxed(val, pio + PIO_ODSR);
+}
+
static int at91_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
int val)
{
@@ -1434,8 +1461,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
@@ -1069,6 +1069,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)
{
@@ -1188,8 +1222,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
* [PATCH 1/2] iio: mxs: Add MX23 support into the IIO driver
From: Jonathan Cameron @ 2013-01-22 12:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <201301212249.11173.marex@denx.de>
On 01/21/2013 09:49 PM, Marek Vasut wrote:
> Dear Lars-Peter Clausen,
>
>> On 01/21/2013 10:32 PM, Marek Vasut wrote:
>>> Dear Micha? Miros?aw,
>>>
>>>> 2013/1/21 Marek Vasut <marex@denx.de>:
>>>>> Dear Micha? Miros?aw,
>>>>>
>>>>>> 2013/1/21 Marek Vasut <marex@denx.de>:
>>>>>>> This patch adds support for i.MX23 into the LRADC driver. The LRADC
>>>>>>> block on MX23 is not much different from the one on MX28, thus this
>>>>>>> is only a few changes fixing the parts that are specific to MX23.
>>>>>>
>>>>>> [...]
>>>>>>
>>>>>>> +struct mxs_lradc_of_config {
>>>>>>> + const int irq_count;
>>>>>>> + const char * const *irq_name;
>>>>>>> +};
>>>>>>> +
>>>>>>> +static const struct mxs_lradc_of_config const mxs_lradc_of_config[]
>>>>>>> = { + [IMX23_LRADC] = {
>>>>>>> + .irq_count = ARRAY_SIZE(mx23_lradc_irq_names),
>>>>>>> + .irq_name = mx23_lradc_irq_names,
>>>>>>> + },
>>>>>>> + [IMX28_LRADC] = {
>>>>>>> + .irq_count = ARRAY_SIZE(mx28_lradc_irq_names),
>>>>>>> + .irq_name = mx28_lradc_irq_names,
>>>>>>> + },
>>>>>>> +};
>>>>>>> +
>>>>>>>
>>>>>>> enum mxs_lradc_ts {
>>>>>>>
>>>>>>> MXS_LRADC_TOUCHSCREEN_NONE = 0,
>>>>>>> MXS_LRADC_TOUCHSCREEN_4WIRE,
>>>>>>>
>>>>>>> @@ -857,8 +890,19 @@ static void mxs_lradc_hw_stop(struct mxs_lradc
>>>>>>> *lradc)
>>>>>>>
>>>>>>> writel(0, lradc->base + LRADC_DELAY(i));
>>>>>>>
>>>>>>> }
>>>>>>>
>>>>>>> +static const struct of_device_id mxs_lradc_dt_ids[] = {
>>>>>>> + { .compatible = "fsl,imx23-lradc", .data = (void
>>>>>>> *)IMX23_LRADC, }, + { .compatible = "fsl,imx28-lradc", .data =
>>>>>>> (void
>>>>>>> *)IMX28_LRADC, }, + { /* sentinel */ }
>>>>>>> +};
>>>>>>> +MODULE_DEVICE_TABLE(of, mxs_lradc_dt_ids);
>>>>>>> +
>>>>>>
>>>>>> Why not s/(void \*)\(IMX.._LRADC\)/\&mxs_lradc_of_config[\1]/ ?
>>>>>
>>>>> Check the register layout, it differs between MX23 and MX28, that's one
>>>>> reason, since were we to access differently placed registers, we can do
>>>>> it easily as in the SSP/I2C drivers.
>>>>>
>>>>> Moreover, there are some features on the MX28 that are not on the MX23
>>>>> (like voltage treshold triggers and touchbuttons), with this setup, we
>>>>> can easily check what we're running at at runtime and determine to
>>>>> disallow these.
>>>>>
>>>>> From my point of view, using the number (IMX23_LRADC / IMX28_LRADC) is
>>>>> much more convenient in the long run.
>>>>
>>>> I'm asking, because you don't use this number anywhere other than in
>>>> mxs_lradc_probe()
>>>> and there only to dereference the irq-names table. After that the
>>>> structure and number
>>>> are forgotten.
>>>
>>> Certainly, so far it's used only this way. But please see my argument
>>> about register layout, that's why I went down this road of abstraction.
>>
>> You'll probably be better of by putting these differences into the
>> mxs_lradc_of_config struct as well, instead of adding switch statements
>> here and there throughout the code.
>
> Certainly. All that is needed is in place now.
>
All look sane to me and Marek has answered all the questions as far as I can see.
I'll take these once I get a response from Shawn (unless someone convinces me
otherwise ;)
^ permalink raw reply
* [PATCH v3 0/9] ARM: PRIMA2: bringup new CSR SiRFmarco SMP SoC
From: Barry Song @ 2013-01-22 12:19 UTC (permalink / raw)
To: linux-arm-kernel
change list:
-v3:
handle feedbacks from Mark Rutland, thanks, Mark!
1. cleanup marco.dtsi and marco-evb.dts according to Mark Rutland's feedback
- "sirf,marco-cb" come before "sirf,marco"; both compatible strings were
documented
- change lcd0 at N to lcd0_N -- ePAPR says the unit-address should match the
reg property, and if there's no reg property the unit-address must be
omitted.
- and so on
2. used designated initializers and delete magic number in l2x0.c
3. Runaway commit message for "enable AUTO_ZRELADDR for SIRF in Kconfig"
4. use arm_dt_init_cpu_maps to get cpu mapping instead of reading scu
5. delete some repeated codes for timer0 and timer1 of marco
-v2:
1. cleanup marco.dtsi according to Mark Rutland's feedback
2. use of_device_id::data to handle the difference of l2x0 aux setting
according to Mark Rutland's feedback
3. rebase to the newest timer/cleanup branch, removing sys_timer
-v1:
1. Marco has different OS timer hardware with Prima2, so add a new timer-marco
2. add platsmp.c, headsmp.S and hotplug.c for MPcore support
3. some hardwares have changed, like rstc, so use of_compatible to branch Prima2
and Marco
4. add initial .dtsi for Marco SoC and initial .dts for the EVB
5. use GIC for Marco instead of Prima2's IRQ controller
6. add DEBUG_LL uart ports for Prima2 and Marco debug ports
Barry Song (9):
ARM: PRIMA2: add CSR SiRFmarco device tree .dts
ARM: PRIMA2: enable AUTO_ZRELADDR for SIRF in Kconfig
ARM: PRIMA2: initialize l2x0 according to mach from DT
ARM: PRIMA2: mv timer to timer-prima2 as we will add timer-marco
ARM: PRIMA2: rstc: enable the support for Marco
ARM: PRIMA2: rtciobg: it is also compatible with marco
ARM: PRIMA2: irq: make prima2 irq can work even we enable GIC for
Marco
ARM: PRIMA2: add new SiRFmarco SMP SoC infrastructures
ARM: PRIMA2: provide two DEBUG_LL ports for prima2 and marco
Documentation/devicetree/bindings/arm/sirf.txt | 10 +-
arch/arm/Kconfig | 1 +
arch/arm/Kconfig.debug | 14 +
arch/arm/boot/dts/Makefile | 1 +
arch/arm/boot/dts/marco-evb.dts | 54 ++
arch/arm/boot/dts/marco.dtsi | 756 ++++++++++++++++++++++
arch/arm/configs/prima2_defconfig | 3 +
arch/arm/mach-prima2/Kconfig | 10 +
arch/arm/mach-prima2/Makefile | 5 +-
arch/arm/mach-prima2/common.c | 45 ++-
arch/arm/mach-prima2/common.h | 15 +-
arch/arm/mach-prima2/headsmp.S | 79 +++
arch/arm/mach-prima2/hotplug.c | 41 ++
arch/arm/mach-prima2/include/mach/irqs.h | 4 +-
arch/arm/mach-prima2/include/mach/uart.h | 6 +
arch/arm/mach-prima2/include/mach/uncompress.h | 3 +
arch/arm/mach-prima2/irq.c | 16 +-
arch/arm/mach-prima2/l2x0.c | 29 +-
arch/arm/mach-prima2/platsmp.c | 163 +++++
arch/arm/mach-prima2/rstc.c | 45 +-
arch/arm/mach-prima2/rtciobrg.c | 1 +
arch/arm/mach-prima2/timer-marco.c | 316 +++++++++
arch/arm/mach-prima2/{timer.c => timer-prima2.c} | 6 +-
23 files changed, 1588 insertions(+), 35 deletions(-)
create mode 100644 arch/arm/boot/dts/marco-evb.dts
create mode 100644 arch/arm/boot/dts/marco.dtsi
create mode 100644 arch/arm/mach-prima2/headsmp.S
create mode 100644 arch/arm/mach-prima2/hotplug.c
create mode 100644 arch/arm/mach-prima2/platsmp.c
create mode 100644 arch/arm/mach-prima2/timer-marco.c
rename arch/arm/mach-prima2/{timer.c => timer-prima2.c} (98%)
--
1.7.5.4
Member of the CSR plc group of companies. CSR plc registered in England and Wales, registered number 4187346, registered office Churchill House, Cambridge Business Park, Cowley Road, Cambridge, CB4 0WZ, United Kingdom
More information can be found at www.csr.com. Follow CSR on Twitter at http://twitter.com/CSR_PLC and read our blog at www.csr.com/blog
^ permalink raw reply
* OMAP baseline test results for v3.8-rc4
From: Peter Korsgaard @ 2013-01-22 12:19 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20130122095218.GD2637@n2100.arm.linux.org.uk>
>>>>> "Russell" == Russell King <- ARM Linux <linux@arm.linux.org.uk>> writes:
Russell> Don't rely on that. Remember, if the compressed image
Russell> occupies the same location as the decompressed kernel, the
Russell> decompressor will copy the data to a different location in RAM
Russell> first - I think at RAM offset + 32K + decompressed kernel
Russell> size.
Ok, but this is with the exact same kernel image loaded at the same
address for the two cases. The only difference is U-Boot version.
Russell> So yes, please try the patch in the link above.
I did. No visible difference. Also not with changing the uImage load
address (2M/16M/32M from start of RAM).
--
Bye, Peter Korsgaard
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox