Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [alsa-devel] [PATCH 2/6] ASoC: codecs: AK4641 depends on GPIOLIB
From: Arnd Bergmann @ 2011-10-03 14:47 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20111003143553.GG23811@sirena.org.uk>

On Monday 03 October 2011 15:35:53 Mark Brown wrote:
> On Mon, Oct 03, 2011 at 02:45:27PM +0100, Russell King - ARM Linux wrote:
> > On Sun, Oct 02, 2011 at 09:41:07PM +0100, Mark Brown wrote:
> 
> > > No, gpiolib is one implementation of the GPIO API but if platforms want
> > > to go and define their own that's currently OK (personally I think at
> > > this point we should just be converting all the stragglers over to
> > > gpiolib).  As things stand we shouldn't have dependencies on a
> > > particular implementation of the API.
> 
> > Then it should depend on GENERIC_GPIO (not to be confused with GPIO_GENERIC,
> > the generic gpiolib driver), which is the symbol meaning that the GPIO API
> > is provided by something.
> 
> Not for devices like this where the GPIOs are an optional thing the
> driver can use, a dependency is far too strong.  Devices like that
> should be able to rely on the stubs.

FWIW, while trying to reproduce this (I still could not), I stumbled over a
different build error with

CONFIG_ARCH_PRIMA2=y
CONFIG_SND_SOC_ALL_CODECS=m
CONFIG_SND_SOC_WM1250_EV1=m

sound/soc/codecs/wm1250-ev1.c:32:14: error: array type has incomplete element type
sound/soc/codecs/wm1250-ev1.c: In function 'wm1250_ev1_pdata':
sound/soc/codecs/wm1250-ev1.c:126:87: error: negative width in bit-field '<anonymous>'
sound/soc/codecs/wm1250-ev1.c:134:111: error: negative width in bit-field '<anonymous>'
sound/soc/codecs/wm1250-ev1.c: In function 'wm1250_ev1_free':
sound/soc/codecs/wm1250-ev1.c:155:103: error: negative width in bit-field '<anonymous>'

	Arnd

^ permalink raw reply

* [PATCH V2 2/2] ARM: SAMSUNG: Cleanup resources by using macro
From: Arnd Bergmann @ 2011-10-03 15:01 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <005901cc817e$51a56d00$f4f04700$%kim@samsung.com>

On Monday 03 October 2011 12:41:22 Kukjin Kim wrote:
> This patch adds macro SAMSUNG_RES_MEM, SAMSUNG_RES_IRQ
> and so on to cleanup regarding 'struct resource' by using
> defined helpers at <linux/ioport.h>.
> 
> Cc: Ben Dooks <ben-linux@fluff.org>
> Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>

When we introduced those macros, it was specifically so that platforms
would be migrated to use them instead of providing their own.

> index 8f19241..a506831 100644
> --- a/arch/arm/plat-samsung/include/plat/devs.h
> +++ b/arch/arm/plat-samsung/include/plat/devs.h
> @@ -18,6 +18,17 @@
>  
>  #include <linux/platform_device.h>
>  
> +#define SAMSUNG_RES_MEM(soc, ip, sz)   DEFINE_RES_MEM(soc##_PA_##ip, sz)
> +#define SAMSUNG_RES_IRQ(ip)            DEFINE_RES_IRQ(IRQ_##ip)
> +
> +#define SAMSUNG_RES_MEM_NAMED(soc, ip, sz, name)       \
> +                               DEFINE_RES_MEM_NAMED(soc##_PA_##ip, sz, name)
> +#define SAMSUNG_RES_IRQ_NAMED(ip, name)                        \
> +                               DEFINE_RES_IRQ_NAMED(IRQ_##ip, name)
> +#define SAMSUNG_RES_DMA_NAMED(ch, name)                        \
> +                               DEFINE_RES_DMA_NAMED(DMACH_##ch, name)

I think the string concatenation really just obfuscates the code, and
it does not actually save much at all. When you replace

+       [0] = SAMSUNG_RES_MEM(S3C, WDT, SZ_1K),
+       [1] = SAMSUNG_RES_IRQ(WDT),

with

+       [0] = DEFINE_RES_MEM(S3C_PA_WDT, SZ_1K),
+       [1] = DEFINE_RES_IRQ(IRQ_WDT),

you need practically no extra space, but you gain the advantages that

* Someone using grep for DEFINE_RES_MEM finds all memory resources without
  having to look up what your macros do an where they are used.
* Someone using grep to look for S3C_PA_WDT finds the place where it is used.
* Someone reading the resource definition immediately knows what the
  macro does if familiar with other platforms using that macro.

	Arnd

^ permalink raw reply

* [PATCH v6 01/16] OMAP2+: hwmod: Add API to enable IO ring wakeup.
From: Vishwanath Sripathy @ 2011-10-03 15:10 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <4E871883.4010207@ti.com>

---snip--
> > +{
> > +	struct omap_device_pad *pad;
> > +	int ret = -EINVAL, j;
> > +
> > +	if (oh->mux&&  oh->mux->enabled) {
> > +		for (j = 0; j<  oh->mux->nr_pads_dynamic; j++) {
> > +			pad = oh->mux->pads_dynamic[j];
> > +			if (pad->flags&  OMAP_DEVICE_PAD_WAKEUP) {
> > +				if (set_wake)
> > +					pad->idle |= OMAP_WAKEUP_EN;
> > +				else
> > +					pad->idle&= ~OMAP_WAKEUP_EN;
>
> I think apart from enabling/disabling the IO wakeup's at the pad
> level, there is also a need to trigger the IO daisy chain control
> (Wu clock) by programming the PRCM.PM_WKEN_WKUP[16] EN_IO_CHAIN
> bit and waiting on the PRCM.PM_WKST_WKUP[16] ST_IO_CHAIN) bit,
> which is done by the omap3_enable/disable_io_chain function.
> This is still done in the cpuidle path, but it makes sense to
> move that over here, since it should be done every time a pad
> level wakeup is enabled or disabled.
>
> See section 3.5.7.2.2 I/O Wake-Up Mechanism of 36xx TRM revA.
>
> "The I/O wake-up scheme is enabled by triggering the I/O daisy chain
> control (Wu clock) by
> programming a dedicated register (PRCM.PM_WKEN_WKUP[16] EN_IO_CHAIN)
> in
> the PRCM module.Software must wait for the I/O daisy chain to
> complete
> before it transitions the PER domain to a
> nonfunctional state. This is done by polling a dedicated status bit
> in
> the PRCM module
> (PRCM.PM_WKST_WKUP[16] ST_IO_CHAIN). This status bit must be cleared
> by
> software when the bit is
> read to 1."
I am working on adding Daisy chain support via hwmod mux framework.
I will post the patches soon.

Regards
Vishwa

^ permalink raw reply

* [alsa-devel] [PATCH 2/6] ASoC: codecs: AK4641 depends on GPIOLIB
From: Mark Brown @ 2011-10-03 15:20 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1343003.MpQlxoEPa6@wuerfel>

On Mon, Oct 03, 2011 at 04:47:07PM +0200, Arnd Bergmann wrote:

> sound/soc/codecs/wm1250-ev1.c:32:14: error: array type has incomplete element type

OK, that's the gpio_request_ stuff not being implemented thing that I
have seen some reports of.  Looks like that's not been stubbed out, but
for Prima2 the best fix is just to turn on gpiolib since there's no
excuse for a new platform to not use it.

^ permalink raw reply

* [PATCH v2 1/7] clk: Add a generic clock infrastructure
From: Rob Herring @ 2011-10-03 15:24 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20111003142524.GN3731@opensource.wolfsonmicro.com>

On 10/03/2011 09:25 AM, Mark Brown wrote:
> On Mon, Oct 03, 2011 at 09:17:30AM -0500, Rob Herring wrote:
>> On 09/22/2011 05:26 PM, Mike Turquette wrote:
> 
> A lot of stuff that should really have been cut plus...
> 
>>> +	if (clk->ops->get_parent)
>>> +		/* We don't to lock against prepare/enable here, as
>>> +		 * the clock is not yet accessible from anywhere */
>>> +		clk->parent = clk->ops->get_parent(clk->hw);
> 
>> I don't think this is going to work. This implies that the parent clock
>> is already registered. For simple clk trees, that's probably not an
>> issue, but for chips with lots of muxing it will be impossible to get
>> the order correct for all cases. This is not an issue today as most
>> clocks are statically created.
> 
>> I think what is needed is a 2 stage init. The 1st stage to create all
>> the clocks and a 2nd stage to build the tree once all clocks are created.
> 
>> Tracking the parents using struct clk_hw instead would help as long as
>> clocks are still statically allocated. However, that won't help for
>> devicetree.
> 
> This isn't in any way specific to clocks, right now the likely solution
> looks to be Grant's changes for retrying probe() as new devices come on
> line.  With that devices can return a code from their probe() which
> tells the driver core that they couldn't get all the resources they need
> and that it should retry the probe() if more devices come on-line.

Except SOC clocks are initialized very early before timers are up and
there can be a very high number of dependencies (every clock except
fixed clocks). With the driver probe retry, retrying is the exception,
not the rule.

Retrying would require every caller to maintain a list of clks to
retry. With 2 stages, you can move that into the core clock code.

There are not typically a large number of board-level/driver created
clocks, so ensuring correct register order is not really a problem. In
cases where there is a cross-driver dependency, the probe retry is a
good solution.

Rob

^ permalink raw reply

* [alsa-devel] [PATCH 2/6] ASoC: codecs: AK4641 depends on GPIOLIB
From: Arnd Bergmann @ 2011-10-03 16:19 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20111003152002.GP3731@opensource.wolfsonmicro.com>

On Monday 03 October 2011 16:20:02 Mark Brown wrote:
> On Mon, Oct 03, 2011 at 04:47:07PM +0200, Arnd Bergmann wrote:
> 
> > sound/soc/codecs/wm1250-ev1.c:32:14: error: array type has incomplete element type
> 
> OK, that's the gpio_request_ stuff not being implemented thing that I
> have seen some reports of.  Looks like that's not been stubbed out, but
> for Prima2 the best fix is just to turn on gpiolib since there's no
> excuse for a new platform to not use it.

With the latest changes that Russell did in this direction, we can probably
set ARCH_WANT_OPTIONAL_GPIOLIB on ARM for all platforms that don't
provide their own gpio implementation or already require gpiolib.

However, I see no reason to force-enable gpiolib on platforms that
don't actually have any GPIO. On those, you would still get the same
problem with this code in the wm1250-ev1 driver:

        for (i = 0; i < ARRAY_SIZE(wm1250->gpios); i++) {
                wm1250->gpios[i].gpio = pdata->gpios[i];
                wm1250->gpios[i].label = wm1250_gpio_names[i];
                wm1250->gpios[i].flags = GPIOF_OUT_INIT_LOW;
        }

When the GPIO API is stubbed out, the definition of struct gpio
is empty, so you cannot access the members, which seems to be
intentional behavior.

In order to make that work, I think we need one of the two patches
below.

	Arnd

--- a/include/linux/gpio.h
+++ b/include/linux/gpio.h
@@ -24,7 +24,11 @@
 #include <linux/errno.h>
 
 struct device;
-struct gpio;
+struct gpio {
+       unsigned        gpio;
+       unsigned long   flags;
+       const char      *label;
+};
 struct gpio_chip;
 
 /*


--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -58,7 +58,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_UDA134X
 	select SND_SOC_UDA1380 if I2C
 	select SND_SOC_WL1273 if MFD_WL1273_CORE
-	select SND_SOC_WM1250_EV1 if I2C
+	select SND_SOC_WM1250_EV1 if I2C && GENERIC_GPIO
 	select SND_SOC_WM2000 if I2C
 	select SND_SOC_WM5100 if I2C
 	select SND_SOC_WM8350 if MFD_WM8350

^ permalink raw reply

* [PATCH] arm/tegra: select AUTO_ZRELADDR by default
From: Stephen Warren @ 2011-10-03 16:22 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20111003131352.GX21166@tbergstrom-lnx.Nvidia.com>

Peter De Schrijver wrote at Monday, October 03, 2011 7:14 AM:
> Ignore this one.

I assume only ignore this copy of the patch because it's a duplicate;
the patch itself is still needed, right?

-- 
nvpublic

^ permalink raw reply

* [PATCH v2 1/7] clk: Add a generic clock infrastructure
From: Mark Brown @ 2011-10-03 16:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <4E89D3C4.8090001@gmail.com>

On Mon, Oct 03, 2011 at 10:24:52AM -0500, Rob Herring wrote:
> On 10/03/2011 09:25 AM, Mark Brown wrote:

> > This isn't in any way specific to clocks, right now the likely solution
> > looks to be Grant's changes for retrying probe() as new devices come on
> > line.  With that devices can return a code from their probe() which
> > tells the driver core that they couldn't get all the resources they need
> > and that it should retry the probe() if more devices come on-line.

> Except SOC clocks are initialized very early before timers are up and
> there can be a very high number of dependencies (every clock except
> fixed clocks). With the driver probe retry, retrying is the exception,
> not the rule.

> Retrying would require every caller to maintain a list of clks to
> retry. With 2 stages, you can move that into the core clock code.

They don't need to maintain a list of clocks to retry, they need to
unwind when probe() fails.  But yes.

> There are not typically a large number of board-level/driver created
> clocks, so ensuring correct register order is not really a problem. In
> cases where there is a cross-driver dependency, the probe retry is a
> good solution.

I dunno, I get the impression that some of this is due to the current
limitations of the clock API rather than due to a lack of clocks -
perhaps that's specific to the applications I look at, though.
applications 

^ permalink raw reply

* [alsa-devel] [PATCH 2/6] ASoC: codecs: AK4641 depends on GPIOLIB
From: Mark Brown @ 2011-10-03 16:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <9353092.2X2OeIUYGE@wuerfel>

On Mon, Oct 03, 2011 at 06:19:48PM +0200, Arnd Bergmann wrote:

> When the GPIO API is stubbed out, the definition of struct gpio
> is empty, so you cannot access the members, which seems to be
> intentional behavior.

That doesn't seem terribly helpful, it means drivers either need to
ifdef or not use the bulk functions.

>  struct device;
> -struct gpio;
> +struct gpio {
> +       unsigned        gpio;
> +       unsigned long   flags;
> +       const char      *label;
> +};

This looks much more sensible to me.

>  	select SND_SOC_WL1273 if MFD_WL1273_CORE
> -	select SND_SOC_WM1250_EV1 if I2C
> +	select SND_SOC_WM1250_EV1 if I2C && GENERIC_GPIO

Or ifdefs in the driver.

^ permalink raw reply

* [PATCH v2 1/7] clk: Add a generic clock infrastructure
From: Russell King - ARM Linux @ 2011-10-03 16:43 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20111003163107.GQ3731@opensource.wolfsonmicro.com>

On Mon, Oct 03, 2011 at 05:31:08PM +0100, Mark Brown wrote:
> I dunno, I get the impression that some of this is due to the current
> limitations of the clock API rather than due to a lack of clocks -
> perhaps that's specific to the applications I look at, though.
> applications 

The clk API per-se has nothing to do with how clocks are registered.
There are two things that are the clk API:

1. clkdev - which deals with translating devices + connection IDs to
   struct clk.  This has no ordering requirements wrt requiring
   parents to be "initialized" before their children (it has no care
   about that at all because it's not within its definition.)

   For this, registration is about connecting device + connection IDs
   to a struct clk.

2. the driver API, defining how the opaque struct clk is looked up,
   obtained and then manipulated.  This has no 'registration' stuff.

So, whether clocks are a tree or flat is unspecified.  It's unspecified
whether there's any particular order required.

In fact, with a clock tree, it's entirely possible that only the leaf
clocks will be 'registered' with clkdev.  How the rest of the clock tree
is initialized is beyond the scope of the driver clk API.

^ permalink raw reply

* [PATCH v3 3/3] ARM: OMAP: TI814X: Create board support and enable build for TI8148 EVM
From: Pedanekar, Hemant @ 2011-10-03 16:45 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <4E88542B.4010206@compulab.co.il>

Hi Igor,

Igor Grinberg wrote on Sunday, October 02, 2011 5:38 PM:

> Hi Hemant,
> 
> On 09/29/11 04:09, Hemant Pedanekar wrote:
>> This patch adds minimal support and build configuration for TI8148 EVM.
>> Also adds support for low level debugging on UART1 console on the EVM.
>> 
>> Note that existing TI8168 EVM file (board-ti8168evm.c) is updated with
>> machine info for TI8148 EVM and renamed as board-ti81xxevm.c.
> 
> Should we really rename the existing file?
> Shouldn't we just stick to the name of the file submitted first?
> (e.g. board-ti8168evm.c) and just add the support for the new
> TI8148 EVM in to the existing file?

But won't this be misleading?

Thanks.

   Hemant

> Because, I don't see any real necessity in renaming that file.
> Also, it will spare the changes in Makefile.
>

[...]

^ permalink raw reply

* [PATCH] ARM: remove bashism in check for multiple zreladdrs
From: Rabin Vincent @ 2011-10-03 16:52 UTC (permalink / raw)
  To: linux-arm-kernel

Get rid of this complaint from dash:

    AS      arch/arm/boot/compressed/lib1funcs.o
  /bin/sh: 1: [: y: unexpected operator
    LD      arch/arm/boot/compressed/vmlinux

Cc: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Rabin Vincent <rabin@rab.in>
---
 arch/arm/boot/compressed/Makefile |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index a53a333..a6b30b3 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -140,7 +140,7 @@ bad_syms=$$($(CROSS_COMPILE)nm $@ | sed -n 's/^.\{8\} [bc] \(.*\)/\1/p') && \
     echo "$$bad_syms" >&2; rm -f $@; false )
 
 check_for_multiple_zreladdr = \
-if [ $(words $(ZRELADDR)) -gt 1 -a "$(CONFIG_AUTO_ZRELADDR)" == "" ]; then \
+if [ $(words $(ZRELADDR)) -gt 1 -a "$(CONFIG_AUTO_ZRELADDR)" = "" ]; then \
 	echo 'multiple zreladdrs: $(ZRELADDR)'; \
 	echo 'This needs CONFIG_AUTO_ZRELADDR to be set'; \
 	false; \
-- 
1.7.5.4

^ permalink raw reply related

* [PATCH 1/9] OMAP2xxx: HWMOD: Fix DSS reset
From: Paul Walmsley @ 2011-10-03 16:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1314102499-30407-2-git-send-email-tomi.valkeinen@ti.com>

Hi

On Tue, 23 Aug 2011, Tomi Valkeinen wrote:

> DSS needs all DSS clocks to be enabled to be able to finish reset
> properly. Before v3.1-rc1 the omapdss driver was managing clocks and
> resets correctly. However, when omapdss started using runtime PM at
> v3.1-rc1, the responsibility for the reset moved to HWMOD framework.
> 
> HWMOD framework does not currently enable all the DSS clocks when
> resetting the DSS hardware. This hasn't caused any problems so far, but
> we may just have been lucky.
> 
> This patch sets HWMOD_CONTROL_OPT_CLKS_IN_RESET for dss_core in OMAP2xxx
> HWMOD data, fixing the issue.
> 
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
> ---
>  arch/arm/mach-omap2/omap_hwmod_2420_data.c |    5 +++++
>  arch/arm/mach-omap2/omap_hwmod_2430_data.c |    5 +++++
>  2 files changed, 10 insertions(+), 0 deletions(-)
> 
> diff --git a/arch/arm/mach-omap2/omap_hwmod_2420_data.c b/arch/arm/mach-omap2/omap_hwmod_2420_data.c
> index a015c69..8fe373d 100644
> --- a/arch/arm/mach-omap2/omap_hwmod_2420_data.c
> +++ b/arch/arm/mach-omap2/omap_hwmod_2420_data.c
> @@ -874,12 +874,17 @@ static struct omap_hwmod_ocp_if *omap2420_dss_slaves[] = {
>  };
>  
>  static struct omap_hwmod_opt_clk dss_opt_clks[] = {
> +	/*
> +	 * The DSS HW needs all DSS clocks enabled during reset. The dss_core
> +	 * driver does not use these clocks.
> +	 */
>  	{ .role = "tv_clk", .clk = "dss_54m_fck" },
>  	{ .role = "sys_clk", .clk = "dss2_fck" },
>  };
>  
>  static struct omap_hwmod omap2420_dss_core_hwmod = {
>  	.name		= "dss_core",
> +	.flags		= HWMOD_CONTROL_OPT_CLKS_IN_RESET,
>  	.class		= &omap2_dss_hwmod_class,
>  	.main_clk	= "dss1_fck", /* instead of dss_fck */
>  	.sdma_reqs	= omap2xxx_dss_sdma_chs,
> diff --git a/arch/arm/mach-omap2/omap_hwmod_2430_data.c b/arch/arm/mach-omap2/omap_hwmod_2430_data.c
> index 16743c7..7a5e7f5 100644
> --- a/arch/arm/mach-omap2/omap_hwmod_2430_data.c
> +++ b/arch/arm/mach-omap2/omap_hwmod_2430_data.c
> @@ -940,12 +940,17 @@ static struct omap_hwmod_ocp_if *omap2430_dss_slaves[] = {
>  };
>  
>  static struct omap_hwmod_opt_clk dss_opt_clks[] = {
> +	/*
> +	 * The DSS HW needs all DSS clocks enabled during reset. The dss_core
> +	 * driver does not use these clocks.
> +	 */
>  	{ .role = "tv_clk", .clk = "dss_54m_fck" },
>  	{ .role = "sys_clk", .clk = "dss2_fck" },
>  };
>  
>  static struct omap_hwmod omap2430_dss_core_hwmod = {
>  	.name		= "dss_core",
> +	.flags		= HWMOD_CONTROL_OPT_CLKS_IN_RESET,
>  	.class		= &omap2_dss_hwmod_class,
>  	.main_clk	= "dss1_fck", /* instead of dss_fck */
>  	.sdma_reqs	= omap2xxx_dss_sdma_chs,
> -- 
> 1.7.4.1

This patch adds an extra 'flags' field, causing conflicts with the 
existing ones for these hwmods, and causing sparse warnings.  Updated 
patch below.


- Paul

From: Tomi Valkeinen <tomi.valkeinen@ti.com>
Date: Tue, 23 Aug 2011 15:28:11 +0300
Subject: [PATCH] OMAP2xxx: HWMOD: Fix DSS reset

DSS needs all DSS clocks to be enabled to be able to finish reset
properly. Before v3.1-rc1 the omapdss driver was managing clocks and
resets correctly. However, when omapdss started using runtime PM at
v3.1-rc1, the responsibility for the reset moved to HWMOD framework.

HWMOD framework does not currently enable all the DSS clocks when
resetting the DSS hardware. This hasn't caused any problems so far, but
we may just have been lucky.

This patch sets HWMOD_CONTROL_OPT_CLKS_IN_RESET for dss_core in OMAP2xxx
HWMOD data, fixing the issue.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
[paul at pwsan.com: merged duplicate .flags fields]
Signed-off-by: Paul Walmsley <paul@pwsan.com>
---
 arch/arm/mach-omap2/omap_hwmod_2420_data.c |    6 +++++-
 arch/arm/mach-omap2/omap_hwmod_2430_data.c |    6 +++++-
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-omap2/omap_hwmod_2420_data.c b/arch/arm/mach-omap2/omap_hwmod_2420_data.c
index a015c69..e8a9b73 100644
--- a/arch/arm/mach-omap2/omap_hwmod_2420_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_2420_data.c
@@ -874,6 +874,10 @@ static struct omap_hwmod_ocp_if *omap2420_dss_slaves[] = {
 };
 
 static struct omap_hwmod_opt_clk dss_opt_clks[] = {
+	/*
+	 * The DSS HW needs all DSS clocks enabled during reset. The dss_core
+	 * driver does not use these clocks.
+	 */
 	{ .role = "tv_clk", .clk = "dss_54m_fck" },
 	{ .role = "sys_clk", .clk = "dss2_fck" },
 };
@@ -899,7 +903,7 @@ static struct omap_hwmod omap2420_dss_core_hwmod = {
 	.masters	= omap2420_dss_masters,
 	.masters_cnt	= ARRAY_SIZE(omap2420_dss_masters),
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP2420),
-	.flags		= HWMOD_NO_IDLEST,
+	.flags		= HWMOD_NO_IDLEST | HWMOD_CONTROL_OPT_CLKS_IN_RESET,
 };
 
 /* l4_core -> dss_dispc */
diff --git a/arch/arm/mach-omap2/omap_hwmod_2430_data.c b/arch/arm/mach-omap2/omap_hwmod_2430_data.c
index 16743c7..b7486f4 100644
--- a/arch/arm/mach-omap2/omap_hwmod_2430_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_2430_data.c
@@ -940,6 +940,10 @@ static struct omap_hwmod_ocp_if *omap2430_dss_slaves[] = {
 };
 
 static struct omap_hwmod_opt_clk dss_opt_clks[] = {
+	/*
+	 * The DSS HW needs all DSS clocks enabled during reset. The dss_core
+	 * driver does not use these clocks.
+	 */
 	{ .role = "tv_clk", .clk = "dss_54m_fck" },
 	{ .role = "sys_clk", .clk = "dss2_fck" },
 };
@@ -965,7 +969,7 @@ static struct omap_hwmod omap2430_dss_core_hwmod = {
 	.masters	= omap2430_dss_masters,
 	.masters_cnt	= ARRAY_SIZE(omap2430_dss_masters),
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP2430),
-	.flags		= HWMOD_NO_IDLEST,
+	.flags		= HWMOD_NO_IDLEST | HWMOD_CONTROL_OPT_CLKS_IN_RESET,
 };
 
 /* l4_core -> dss_dispc */
-- 
1.7.6.3

^ permalink raw reply related

* [PATCH 3/9] OMAP3: HWMOD: Fix DSS reset
From: Paul Walmsley @ 2011-10-03 16:54 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1314102499-30407-4-git-send-email-tomi.valkeinen@ti.com>

Hi

On Tue, 23 Aug 2011, Tomi Valkeinen wrote:

> DSS needs all DSS clocks to be enabled to be able to finish reset
> properly. Before v3.1-rc1 the omapdss driver was managing clocks and
> resets correctly. However, when omapdss started using runtime PM at
> v3.1-rc1, the responsibility for the reset moved to HWMOD framework.
> 
> HWMOD framework does not currently enable all the DSS clocks when
> resetting the DSS hardware. This hasn't caused any problems so far, but
> we may just have been lucky.
> 
> dss_core's opt-clocks is also missing dss_96m_fck, which is a DSS clock
> present only on OMAP3430, and thus required on OMAP3430 to finish the
> reset.
> 
> This patch sets HWMOD_CONTROL_OPT_CLKS_IN_RESET and adds the dss_96m_fck
> opt-clock for dss_core in OMAP3 HWMOD data, fixing the issue.
> 
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
> ---
>  arch/arm/mach-omap2/omap_hwmod_3xxx_data.c |   11 +++++++++--
>  1 files changed, 9 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
> index 25bf43b..484eda3 100644
> --- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
> +++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
> @@ -1365,13 +1365,19 @@ static struct omap_hwmod_ocp_if *omap3xxx_dss_slaves[] = {
>  };
>  
>  static struct omap_hwmod_opt_clk dss_opt_clks[] = {
> -	{ .role = "tv_clk", .clk = "dss_tv_fck" },
> -	{ .role = "video_clk", .clk = "dss_96m_fck" },
> +	/*
> +	 * The DSS HW needs all DSS clocks enabled during reset. The dss_core
> +	 * driver does not use these clocks.
> +	 */
>  	{ .role = "sys_clk", .clk = "dss2_alwon_fck" },
> +	{ .role = "tv_clk", .clk = "dss_tv_fck" },
> +	/* required only on OMAP3430 */
> +	{ .role = "tv_dac_clk", .clk = "dss_96m_fck" },
>  };
>  
>  static struct omap_hwmod omap3430es1_dss_core_hwmod = {
>  	.name		= "dss_core",
> +	.flags		= HWMOD_CONTROL_OPT_CLKS_IN_RESET,
>  	.class		= &omap2_dss_hwmod_class,
>  	.main_clk	= "dss1_alwon_fck", /* instead of dss_fck */
>  	.sdma_reqs	= omap3xxx_dss_sdma_chs,
> @@ -1396,6 +1402,7 @@ static struct omap_hwmod omap3430es1_dss_core_hwmod = {
>  
>  static struct omap_hwmod omap3xxx_dss_core_hwmod = {
>  	.name		= "dss_core",
> +	.flags		= HWMOD_CONTROL_OPT_CLKS_IN_RESET,
>  	.class		= &omap2_dss_hwmod_class,
>  	.main_clk	= "dss1_alwon_fck", /* instead of dss_fck */
>  	.sdma_reqs	= omap3xxx_dss_sdma_chs,
> -- 
> 1.7.4.1


This patch adds an extra 'flags' field, overriding the existing ones for  
these hwmods, and causing sparse warnings.  Updated patch below.


- Paul

From: Tomi Valkeinen <tomi.valkeinen@ti.com>
Date: Tue, 23 Aug 2011 15:28:13 +0300
Subject: [PATCH] OMAP3: HWMOD: Fix DSS reset

DSS needs all DSS clocks to be enabled to be able to finish reset
properly. Before v3.1-rc1 the omapdss driver was managing clocks and
resets correctly. However, when omapdss started using runtime PM at
v3.1-rc1, the responsibility for the reset moved to HWMOD framework.

HWMOD framework does not currently enable all the DSS clocks when
resetting the DSS hardware. This hasn't caused any problems so far, but
we may just have been lucky.

dss_core's opt-clocks is also missing dss_96m_fck, which is a DSS clock
present only on OMAP3430, and thus required on OMAP3430 to finish the
reset.

This patch sets HWMOD_CONTROL_OPT_CLKS_IN_RESET and adds the dss_96m_fck
opt-clock for dss_core in OMAP3 HWMOD data, fixing the issue.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
[paul at pwsan.com: merged duplicate .flags fields]
Signed-off-by: Paul Walmsley <paul@pwsan.com>
---
 arch/arm/mach-omap2/omap_hwmod_3xxx_data.c |   12 +++++++++---
 1 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
index 25bf43b..99a0db0 100644
--- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
@@ -1365,9 +1365,14 @@ static struct omap_hwmod_ocp_if *omap3xxx_dss_slaves[] = {
 };
 
 static struct omap_hwmod_opt_clk dss_opt_clks[] = {
-	{ .role = "tv_clk", .clk = "dss_tv_fck" },
-	{ .role = "video_clk", .clk = "dss_96m_fck" },
+	/*
+	 * The DSS HW needs all DSS clocks enabled during reset. The dss_core
+	 * driver does not use these clocks.
+	 */
 	{ .role = "sys_clk", .clk = "dss2_alwon_fck" },
+	{ .role = "tv_clk", .clk = "dss_tv_fck" },
+	/* required only on OMAP3430 */
+	{ .role = "tv_dac_clk", .clk = "dss_96m_fck" },
 };
 
 static struct omap_hwmod omap3430es1_dss_core_hwmod = {
@@ -1391,11 +1396,12 @@ static struct omap_hwmod omap3430es1_dss_core_hwmod = {
 	.masters	= omap3xxx_dss_masters,
 	.masters_cnt	= ARRAY_SIZE(omap3xxx_dss_masters),
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP3430ES1),
-	.flags		= HWMOD_NO_IDLEST,
+	.flags		= HWMOD_NO_IDLEST | HWMOD_CONTROL_OPT_CLKS_IN_RESET,
 };
 
 static struct omap_hwmod omap3xxx_dss_core_hwmod = {
 	.name		= "dss_core",
+	.flags		= HWMOD_CONTROL_OPT_CLKS_IN_RESET,
 	.class		= &omap2_dss_hwmod_class,
 	.main_clk	= "dss1_alwon_fck", /* instead of dss_fck */
 	.sdma_reqs	= omap3xxx_dss_sdma_chs,
-- 
1.7.6.3

^ permalink raw reply related

* [PATCH v4 0/4] genirq: handling GIC per-cpu interrupts
From: Marc Zyngier @ 2011-10-03 17:04 UTC (permalink / raw)
  To: linux-arm-kernel

The current GIC per-cpu interrupts (aka PPIs) suffer from a number of
problems:

- They use a completely separate scheme to handle the interrupts,
  mostly because the PPI concept doesn't really match the kernel view
  of an interrupt.
- PPIs can only be used by the timer code, unless we add more low-level
  assembly code.
- The local timer code can only be used by devices generating PPIs,
  and not SPIs.
- At least one platform (msm) has started implementing its own
  alternative scheme.
- Some low-level code gets duplicated, as usual...

The proposed solution is to handle the PPIs using the same path as
SPIs. A new core API is added to deal with per-cpu interrupts in a
less awkward way. The local timer code is updated to reflect these
changes.

The core API changes are based on an initial idea by Thomas Gleixner.

Tested on ARM Versatile Express (Cortex A15), ARM RealView PB11MP,
OMAP4 (Panda) and Tegra (Harmony). Patch series against next-20110930.

The two first patches can also be found in Thomas' tree:

	git://tesla.tglx.de/git/linux-2.6-tip irq/core

Changelog:

^ permalink raw reply

* [PATCH v4 1/4] genirq: Add support for per-cpu dev_id interrupts
From: Marc Zyngier @ 2011-10-03 17:04 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1317661446-2876-1-git-send-email-marc.zyngier@arm.com>

The ARM GIC interrupt controller offers per CPU interrupts (PPIs),
which are usually used to connect local timers to each core. Each CPU
has its own private interface to the GIC, and only sees the PPIs that
are directly connect to it.

While these timers are separate devices and have a separate interrupt
line to a core, they all use the same IRQ number.

For these devices, request_irq() is not the right API as it assumes
that an IRQ number is visible by a number of CPUs (through the
affinity setting), but makes it very awkward to express that an IRQ
number can be handled by all CPUs, and yet be a different interrupt
line on each CPU, requiring a different dev_id cookie to be passed
back to the handler.

The *_percpu_irq() functions is designed to overcome these
limitations, by providing a per-cpu dev_id vector:

int request_percpu_irq(unsigned int irq, irq_handler_t handler,
		   const char *devname, void __percpu *percpu_dev_id);
void free_percpu_irq(unsigned int, void __percpu *);
int setup_percpu_irq(unsigned int irq, struct irqaction *new);
void remove_percpu_irq(unsigned int irq, struct irqaction *act);
void enable_percpu_irq(unsigned int irq);
void disable_percpu_irq(unsigned int irq);

The API has a number of limitations:
- no interrupt sharing
- no threading
- common handler across all the CPUs

Once the interrupt is requested using setup_percpu_irq() or
request_percpu_irq(), it must be enabled by each core that wishes its
local interrupt to be delivered.

Based on an initial patch by Thomas Gleixner.

Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Cc: linux-arm-kernel at lists.infradead.org
Link: http://lkml.kernel.org/r/1316793788-14500-2-git-send-email-marc.zyngier at arm.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
 include/linux/interrupt.h |   38 ++++++---
 include/linux/irq.h       |   16 ++++-
 include/linux/irqdesc.h   |    1 +
 kernel/irq/chip.c         |   64 +++++++++++++--
 kernel/irq/internals.h    |   19 +++-
 kernel/irq/irqdesc.c      |   32 +++++++-
 kernel/irq/manage.c       |  202 +++++++++++++++++++++++++++++++++++++++++++--
 kernel/irq/settings.h     |    7 ++
 8 files changed, 345 insertions(+), 34 deletions(-)

diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index a103732..1cdfd09 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -95,6 +95,7 @@ typedef irqreturn_t (*irq_handler_t)(int, void *);
  * @flags:	flags (see IRQF_* above)
  * @name:	name of the device
  * @dev_id:	cookie to identify the device
+ * @percpu_dev_id:	cookie to identify the device
  * @next:	pointer to the next irqaction for shared interrupts
  * @irq:	interrupt number
  * @dir:	pointer to the proc/irq/NN/name entry
@@ -104,17 +105,18 @@ typedef irqreturn_t (*irq_handler_t)(int, void *);
  * @thread_mask:	bitmask for keeping track of @thread activity
  */
 struct irqaction {
-	irq_handler_t handler;
-	unsigned long flags;
-	void *dev_id;
-	struct irqaction *next;
-	int irq;
-	irq_handler_t thread_fn;
-	struct task_struct *thread;
-	unsigned long thread_flags;
-	unsigned long thread_mask;
-	const char *name;
-	struct proc_dir_entry *dir;
+	irq_handler_t		handler;
+	unsigned long		flags;
+	void			*dev_id;
+	void __percpu		*percpu_dev_id;
+	struct irqaction	*next;
+	int			irq;
+	irq_handler_t		thread_fn;
+	struct task_struct	*thread;
+	unsigned long		thread_flags;
+	unsigned long		thread_mask;
+	const char		*name;
+	struct proc_dir_entry	*dir;
 } ____cacheline_internodealigned_in_smp;
 
 extern irqreturn_t no_action(int cpl, void *dev_id);
@@ -136,6 +138,10 @@ extern int __must_check
 request_any_context_irq(unsigned int irq, irq_handler_t handler,
 			unsigned long flags, const char *name, void *dev_id);
 
+extern int __must_check
+request_percpu_irq(unsigned int irq, irq_handler_t handler,
+		   const char *devname, void __percpu *percpu_dev_id);
+
 extern void exit_irq_thread(void);
 #else
 
@@ -164,10 +170,18 @@ request_any_context_irq(unsigned int irq, irq_handler_t handler,
 	return request_irq(irq, handler, flags, name, dev_id);
 }
 
+static inline int __must_check
+request_percpu_irq(unsigned int irq, irq_handler_t handler,
+		   const char *devname, void __percpu *percpu_dev_id)
+{
+	return request_irq(irq, handler, 0, devname, percpu_dev_id);
+}
+
 static inline void exit_irq_thread(void) { }
 #endif
 
 extern void free_irq(unsigned int, void *);
+extern void free_percpu_irq(unsigned int, void __percpu *);
 
 struct device;
 
@@ -207,7 +221,9 @@ extern void devm_free_irq(struct device *dev, unsigned int irq, void *dev_id);
 
 extern void disable_irq_nosync(unsigned int irq);
 extern void disable_irq(unsigned int irq);
+extern void disable_percpu_irq(unsigned int irq);
 extern void enable_irq(unsigned int irq);
+extern void enable_percpu_irq(unsigned int irq);
 
 /* The following three functions are for the core kernel use only. */
 #ifdef CONFIG_GENERIC_HARDIRQS
diff --git a/include/linux/irq.h b/include/linux/irq.h
index f776f94..bff29c5 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -66,6 +66,7 @@ typedef	void (*irq_preflow_handler_t)(struct irq_data *data);
  * IRQ_NO_BALANCING		- Interrupt cannot be balanced (affinity set)
  * IRQ_MOVE_PCNTXT		- Interrupt can be migrated from process context
  * IRQ_NESTED_TRHEAD		- Interrupt nests into another thread
+ * IRQ_PER_CPU_DEVID		- Dev_id is a per-cpu variable
  */
 enum {
 	IRQ_TYPE_NONE		= 0x00000000,
@@ -88,12 +89,13 @@ enum {
 	IRQ_MOVE_PCNTXT		= (1 << 14),
 	IRQ_NESTED_THREAD	= (1 << 15),
 	IRQ_NOTHREAD		= (1 << 16),
+	IRQ_PER_CPU_DEVID	= (1 << 17),
 };
 
 #define IRQF_MODIFY_MASK	\
 	(IRQ_TYPE_SENSE_MASK | IRQ_NOPROBE | IRQ_NOREQUEST | \
 	 IRQ_NOAUTOEN | IRQ_MOVE_PCNTXT | IRQ_LEVEL | IRQ_NO_BALANCING | \
-	 IRQ_PER_CPU | IRQ_NESTED_THREAD)
+	 IRQ_PER_CPU | IRQ_NESTED_THREAD | IRQ_NOTHREAD | IRQ_PER_CPU_DEVID)
 
 #define IRQ_NO_BALANCING_MASK	(IRQ_PER_CPU | IRQ_NO_BALANCING)
 
@@ -367,6 +369,8 @@ enum {
 struct irqaction;
 extern int setup_irq(unsigned int irq, struct irqaction *new);
 extern void remove_irq(unsigned int irq, struct irqaction *act);
+extern int setup_percpu_irq(unsigned int irq, struct irqaction *new);
+extern void remove_percpu_irq(unsigned int irq, struct irqaction *act);
 
 extern void irq_cpu_online(void);
 extern void irq_cpu_offline(void);
@@ -394,6 +398,7 @@ extern void handle_edge_irq(unsigned int irq, struct irq_desc *desc);
 extern void handle_edge_eoi_irq(unsigned int irq, struct irq_desc *desc);
 extern void handle_simple_irq(unsigned int irq, struct irq_desc *desc);
 extern void handle_percpu_irq(unsigned int irq, struct irq_desc *desc);
+extern void handle_percpu_devid_irq(unsigned int irq, struct irq_desc *desc);
 extern void handle_bad_irq(unsigned int irq, struct irq_desc *desc);
 extern void handle_nested_irq(unsigned int irq);
 
@@ -422,6 +427,8 @@ static inline void irq_set_chip_and_handler(unsigned int irq, struct irq_chip *c
 	irq_set_chip_and_handler_name(irq, chip, handle, NULL);
 }
 
+extern int irq_set_percpu_devid(unsigned int irq);
+
 extern void
 __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
 		  const char *name);
@@ -483,6 +490,13 @@ static inline void irq_set_nested_thread(unsigned int irq, bool nest)
 		irq_clear_status_flags(irq, IRQ_NESTED_THREAD);
 }
 
+static inline void irq_set_percpu_devid_flags(unsigned int irq)
+{
+	irq_set_status_flags(irq,
+			     IRQ_NOAUTOEN | IRQ_PER_CPU | IRQ_NOTHREAD |
+			     IRQ_NOPROBE | IRQ_PER_CPU_DEVID);
+}
+
 /* Handle dynamic irq creation and destruction */
 extern unsigned int create_irq_nr(unsigned int irq_want, int node);
 extern int create_irq(void);
diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h
index d9a9f61..f1e2527 100644
--- a/include/linux/irqdesc.h
+++ b/include/linux/irqdesc.h
@@ -54,6 +54,7 @@ struct irq_desc {
 	unsigned long		last_unhandled;	/* Aging timer for unhandled count */
 	unsigned int		irqs_unhandled;
 	raw_spinlock_t		lock;
+	struct cpumask		*percpu_enabled;
 #ifdef CONFIG_SMP
 	const struct cpumask	*affinity_hint;
 	struct irq_affinity_notify *affinity_notify;
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index dc5114b..f7c543a 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -26,7 +26,7 @@
 int irq_set_chip(unsigned int irq, struct irq_chip *chip)
 {
 	unsigned long flags;
-	struct irq_desc *desc = irq_get_desc_lock(irq, &flags);
+	struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
 
 	if (!desc)
 		return -EINVAL;
@@ -54,7 +54,7 @@ EXPORT_SYMBOL(irq_set_chip);
 int irq_set_irq_type(unsigned int irq, unsigned int type)
 {
 	unsigned long flags;
-	struct irq_desc *desc = irq_get_desc_buslock(irq, &flags);
+	struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
 	int ret = 0;
 
 	if (!desc)
@@ -78,7 +78,7 @@ EXPORT_SYMBOL(irq_set_irq_type);
 int irq_set_handler_data(unsigned int irq, void *data)
 {
 	unsigned long flags;
-	struct irq_desc *desc = irq_get_desc_lock(irq, &flags);
+	struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
 
 	if (!desc)
 		return -EINVAL;
@@ -98,7 +98,7 @@ EXPORT_SYMBOL(irq_set_handler_data);
 int irq_set_msi_desc(unsigned int irq, struct msi_desc *entry)
 {
 	unsigned long flags;
-	struct irq_desc *desc = irq_get_desc_lock(irq, &flags);
+	struct irq_desc *desc = irq_get_desc_lock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
 
 	if (!desc)
 		return -EINVAL;
@@ -119,7 +119,7 @@ int irq_set_msi_desc(unsigned int irq, struct msi_desc *entry)
 int irq_set_chip_data(unsigned int irq, void *data)
 {
 	unsigned long flags;
-	struct irq_desc *desc = irq_get_desc_lock(irq, &flags);
+	struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
 
 	if (!desc)
 		return -EINVAL;
@@ -204,6 +204,24 @@ void irq_disable(struct irq_desc *desc)
 	}
 }
 
+void irq_percpu_enable(struct irq_desc *desc, unsigned int cpu)
+{
+	if (desc->irq_data.chip->irq_enable)
+		desc->irq_data.chip->irq_enable(&desc->irq_data);
+	else
+		desc->irq_data.chip->irq_unmask(&desc->irq_data);
+	cpumask_set_cpu(cpu, desc->percpu_enabled);
+}
+
+void irq_percpu_disable(struct irq_desc *desc, unsigned int cpu)
+{
+	if (desc->irq_data.chip->irq_disable)
+		desc->irq_data.chip->irq_disable(&desc->irq_data);
+	else
+		desc->irq_data.chip->irq_mask(&desc->irq_data);
+	cpumask_clear_cpu(cpu, desc->percpu_enabled);
+}
+
 static inline void mask_ack_irq(struct irq_desc *desc)
 {
 	if (desc->irq_data.chip->irq_mask_ack)
@@ -544,12 +562,44 @@ handle_percpu_irq(unsigned int irq, struct irq_desc *desc)
 		chip->irq_eoi(&desc->irq_data);
 }
 
+/**
+ * handle_percpu_devid_irq - Per CPU local irq handler with per cpu dev ids
+ * @irq:	the interrupt number
+ * @desc:	the interrupt description structure for this irq
+ *
+ * Per CPU interrupts on SMP machines without locking requirements. Same as
+ * handle_percpu_irq() above but with the following extras:
+ *
+ * action->percpu_dev_id is a pointer to percpu variables which
+ * contain the real device id for the cpu on which this handler is
+ * called
+ */
+void handle_percpu_devid_irq(unsigned int irq, struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct irqaction *action = desc->action;
+	void *dev_id = __this_cpu_ptr(action->percpu_dev_id);
+	irqreturn_t res;
+
+	kstat_incr_irqs_this_cpu(irq, desc);
+
+	if (chip->irq_ack)
+		chip->irq_ack(&desc->irq_data);
+
+	trace_irq_handler_entry(irq, action);
+	res = action->handler(irq, dev_id);
+	trace_irq_handler_exit(irq, action, res);
+
+	if (chip->irq_eoi)
+		chip->irq_eoi(&desc->irq_data);
+}
+
 void
 __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
 		  const char *name)
 {
 	unsigned long flags;
-	struct irq_desc *desc = irq_get_desc_buslock(irq, &flags);
+	struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);
 
 	if (!desc)
 		return;
@@ -593,7 +643,7 @@ irq_set_chip_and_handler_name(unsigned int irq, struct irq_chip *chip,
 void irq_modify_status(unsigned int irq, unsigned long clr, unsigned long set)
 {
 	unsigned long flags;
-	struct irq_desc *desc = irq_get_desc_lock(irq, &flags);
+	struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
 
 	if (!desc)
 		return;
diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h
index 6546431..a73dd6c 100644
--- a/kernel/irq/internals.h
+++ b/kernel/irq/internals.h
@@ -71,6 +71,8 @@ extern int irq_startup(struct irq_desc *desc);
 extern void irq_shutdown(struct irq_desc *desc);
 extern void irq_enable(struct irq_desc *desc);
 extern void irq_disable(struct irq_desc *desc);
+extern void irq_percpu_enable(struct irq_desc *desc, unsigned int cpu);
+extern void irq_percpu_disable(struct irq_desc *desc, unsigned int cpu);
 extern void mask_irq(struct irq_desc *desc);
 extern void unmask_irq(struct irq_desc *desc);
 
@@ -114,14 +116,21 @@ static inline void chip_bus_sync_unlock(struct irq_desc *desc)
 		desc->irq_data.chip->irq_bus_sync_unlock(&desc->irq_data);
 }
 
+#define _IRQ_DESC_CHECK		(1 << 0)
+#define _IRQ_DESC_PERCPU	(1 << 1)
+
+#define IRQ_GET_DESC_CHECK_GLOBAL	(_IRQ_DESC_CHECK)
+#define IRQ_GET_DESC_CHECK_PERCPU	(_IRQ_DESC_CHECK | _IRQ_DESC_PERCPU)
+
 struct irq_desc *
-__irq_get_desc_lock(unsigned int irq, unsigned long *flags, bool bus);
+__irq_get_desc_lock(unsigned int irq, unsigned long *flags, bool bus,
+		    unsigned int check);
 void __irq_put_desc_unlock(struct irq_desc *desc, unsigned long flags, bool bus);
 
 static inline struct irq_desc *
-irq_get_desc_buslock(unsigned int irq, unsigned long *flags)
+irq_get_desc_buslock(unsigned int irq, unsigned long *flags, unsigned int check)
 {
-	return __irq_get_desc_lock(irq, flags, true);
+	return __irq_get_desc_lock(irq, flags, true, check);
 }
 
 static inline void
@@ -131,9 +140,9 @@ irq_put_desc_busunlock(struct irq_desc *desc, unsigned long flags)
 }
 
 static inline struct irq_desc *
-irq_get_desc_lock(unsigned int irq, unsigned long *flags)
+irq_get_desc_lock(unsigned int irq, unsigned long *flags, unsigned int check)
 {
-	return __irq_get_desc_lock(irq, flags, false);
+	return __irq_get_desc_lock(irq, flags, false, check);
 }
 
 static inline void
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
index 9888f7e..d86e254 100644
--- a/kernel/irq/irqdesc.c
+++ b/kernel/irq/irqdesc.c
@@ -424,11 +424,22 @@ unsigned int irq_get_next_irq(unsigned int offset)
 }
 
 struct irq_desc *
-__irq_get_desc_lock(unsigned int irq, unsigned long *flags, bool bus)
+__irq_get_desc_lock(unsigned int irq, unsigned long *flags, bool bus,
+		    unsigned int check)
 {
 	struct irq_desc *desc = irq_to_desc(irq);
 
 	if (desc) {
+		if (check & _IRQ_DESC_CHECK) {
+			if ((check & _IRQ_DESC_PERCPU) &&
+			    !irq_settings_is_per_cpu_devid(desc))
+				return NULL;
+
+			if (!(check & _IRQ_DESC_PERCPU) &&
+			    irq_settings_is_per_cpu_devid(desc))
+				return NULL;
+		}
+
 		if (bus)
 			chip_bus_lock(desc);
 		raw_spin_lock_irqsave(&desc->lock, *flags);
@@ -443,6 +454,25 @@ void __irq_put_desc_unlock(struct irq_desc *desc, unsigned long flags, bool bus)
 		chip_bus_sync_unlock(desc);
 }
 
+int irq_set_percpu_devid(unsigned int irq)
+{
+	struct irq_desc *desc = irq_to_desc(irq);
+
+	if (!desc)
+		return -EINVAL;
+
+	if (desc->percpu_enabled)
+		return -EINVAL;
+
+	desc->percpu_enabled = kzalloc(sizeof(*desc->percpu_enabled), GFP_KERNEL);
+
+	if (!desc->percpu_enabled)
+		return -ENOMEM;
+
+	irq_set_percpu_devid_flags(irq);
+	return 0;
+}
+
 /**
  * dynamic_irq_cleanup - cleanup a dynamically allocated irq
  * @irq:	irq number to initialize
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 7e1a3ed..7b4b156 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -195,7 +195,7 @@ int irq_set_affinity(unsigned int irq, const struct cpumask *mask)
 int irq_set_affinity_hint(unsigned int irq, const struct cpumask *m)
 {
 	unsigned long flags;
-	struct irq_desc *desc = irq_get_desc_lock(irq, &flags);
+	struct irq_desc *desc = irq_get_desc_lock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
 
 	if (!desc)
 		return -EINVAL;
@@ -356,7 +356,7 @@ void __disable_irq(struct irq_desc *desc, unsigned int irq, bool suspend)
 static int __disable_irq_nosync(unsigned int irq)
 {
 	unsigned long flags;
-	struct irq_desc *desc = irq_get_desc_buslock(irq, &flags);
+	struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
 
 	if (!desc)
 		return -EINVAL;
@@ -448,7 +448,7 @@ void __enable_irq(struct irq_desc *desc, unsigned int irq, bool resume)
 void enable_irq(unsigned int irq)
 {
 	unsigned long flags;
-	struct irq_desc *desc = irq_get_desc_buslock(irq, &flags);
+	struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
 
 	if (!desc)
 		return;
@@ -491,7 +491,7 @@ static int set_irq_wake_real(unsigned int irq, unsigned int on)
 int irq_set_irq_wake(unsigned int irq, unsigned int on)
 {
 	unsigned long flags;
-	struct irq_desc *desc = irq_get_desc_buslock(irq, &flags);
+	struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
 	int ret = 0;
 
 	if (!desc)
@@ -532,7 +532,7 @@ EXPORT_SYMBOL(irq_set_irq_wake);
 int can_request_irq(unsigned int irq, unsigned long irqflags)
 {
 	unsigned long flags;
-	struct irq_desc *desc = irq_get_desc_lock(irq, &flags);
+	struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
 	int canrequest = 0;
 
 	if (!desc)
@@ -1121,6 +1121,8 @@ int setup_irq(unsigned int irq, struct irqaction *act)
 	int retval;
 	struct irq_desc *desc = irq_to_desc(irq);
 
+	if (WARN_ON(irq_settings_is_per_cpu_devid(desc)))
+		return -EINVAL;
 	chip_bus_lock(desc);
 	retval = __setup_irq(irq, desc, act);
 	chip_bus_sync_unlock(desc);
@@ -1129,7 +1131,7 @@ int setup_irq(unsigned int irq, struct irqaction *act)
 }
 EXPORT_SYMBOL_GPL(setup_irq);
 
- /*
+/*
  * Internal function to unregister an irqaction - used to free
  * regular and special interrupts that are part of the architecture.
  */
@@ -1227,7 +1229,10 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
  */
 void remove_irq(unsigned int irq, struct irqaction *act)
 {
-	__free_irq(irq, act->dev_id);
+	struct irq_desc *desc = irq_to_desc(irq);
+
+	if (desc && !WARN_ON(irq_settings_is_per_cpu_devid(desc)))
+	    __free_irq(irq, act->dev_id);
 }
 EXPORT_SYMBOL_GPL(remove_irq);
 
@@ -1249,7 +1254,7 @@ void free_irq(unsigned int irq, void *dev_id)
 {
 	struct irq_desc *desc = irq_to_desc(irq);
 
-	if (!desc)
+	if (!desc || WARN_ON(irq_settings_is_per_cpu_devid(desc)))
 		return;
 
 #ifdef CONFIG_SMP
@@ -1327,7 +1332,8 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler,
 	if (!desc)
 		return -EINVAL;
 
-	if (!irq_settings_can_request(desc))
+	if (!irq_settings_can_request(desc) ||
+	    WARN_ON(irq_settings_is_per_cpu_devid(desc)))
 		return -EINVAL;
 
 	if (!handler) {
@@ -1412,3 +1418,181 @@ int request_any_context_irq(unsigned int irq, irq_handler_t handler,
 	return !ret ? IRQC_IS_HARDIRQ : ret;
 }
 EXPORT_SYMBOL_GPL(request_any_context_irq);
+
+void enable_percpu_irq(unsigned int irq)
+{
+	unsigned int cpu = smp_processor_id();
+	unsigned long flags;
+	struct irq_desc *desc = irq_get_desc_lock(irq, &flags, IRQ_GET_DESC_CHECK_PERCPU);
+
+	if (!desc)
+		return;
+
+	irq_percpu_enable(desc, cpu);
+	irq_put_desc_unlock(desc, flags);
+}
+
+void disable_percpu_irq(unsigned int irq)
+{
+	unsigned int cpu = smp_processor_id();
+	unsigned long flags;
+	struct irq_desc *desc = irq_get_desc_lock(irq, &flags, IRQ_GET_DESC_CHECK_PERCPU);
+
+	if (!desc)
+		return;
+
+	irq_percpu_disable(desc, cpu);
+	irq_put_desc_unlock(desc, flags);
+}
+
+/*
+ * Internal function to unregister a percpu irqaction.
+ */
+static struct irqaction *__free_percpu_irq(unsigned int irq, void __percpu *dev_id)
+{
+	struct irq_desc *desc = irq_to_desc(irq);
+	struct irqaction *action;
+	unsigned long flags;
+
+	WARN(in_interrupt(), "Trying to free IRQ %d from IRQ context!\n", irq);
+
+	if (!desc)
+		return NULL;
+
+	raw_spin_lock_irqsave(&desc->lock, flags);
+
+	action = desc->action;
+	if (!action || action->percpu_dev_id != dev_id) {
+		WARN(1, "Trying to free already-free IRQ %d\n", irq);
+		goto bad;
+	}
+
+	if (!cpumask_empty(desc->percpu_enabled)) {
+		WARN(1, "percpu IRQ %d still enabled on CPU%d!\n",
+		     irq, cpumask_first(desc->percpu_enabled));
+		goto bad;
+	}
+
+	/* Found it - now remove it from the list of entries: */
+	desc->action = NULL;
+
+	raw_spin_unlock_irqrestore(&desc->lock, flags);
+
+	unregister_handler_proc(irq, action);
+
+	module_put(desc->owner);
+	return action;
+
+bad:
+	raw_spin_unlock_irqrestore(&desc->lock, flags);
+	return NULL;
+}
+
+/**
+ *	remove_percpu_irq - free a per-cpu interrupt
+ *	@irq: Interrupt line to free
+ *	@act: irqaction for the interrupt
+ *
+ * Used to remove interrupts statically setup by the early boot process.
+ */
+void remove_percpu_irq(unsigned int irq, struct irqaction *act)
+{
+	struct irq_desc *desc = irq_to_desc(irq);
+
+	if (desc && irq_settings_is_per_cpu_devid(desc))
+	    __free_percpu_irq(irq, act->percpu_dev_id);
+}
+
+/**
+ *	free_percpu_irq - free an interrupt allocated with request_percpu_irq
+ *	@irq: Interrupt line to free
+ *	@dev_id: Device identity to free
+ *
+ *	Remove a percpu interrupt handler. The handler is removed, but
+ *	the interrupt line is not disabled. This must be done on each
+ *	CPU before calling this function. The function does not return
+ *	until any executing interrupts for this IRQ have completed.
+ *
+ *	This function must not be called from interrupt context.
+ */
+void free_percpu_irq(unsigned int irq, void __percpu *dev_id)
+{
+	struct irq_desc *desc = irq_to_desc(irq);
+
+	if (!desc || !irq_settings_is_per_cpu_devid(desc))
+		return;
+
+	chip_bus_lock(desc);
+	kfree(__free_percpu_irq(irq, dev_id));
+	chip_bus_sync_unlock(desc);
+}
+
+/**
+ *	setup_percpu_irq - setup a per-cpu interrupt
+ *	@irq: Interrupt line to setup
+ *	@act: irqaction for the interrupt
+ *
+ * Used to statically setup per-cpu interrupts in the early boot process.
+ */
+int setup_percpu_irq(unsigned int irq, struct irqaction *act)
+{
+	struct irq_desc *desc = irq_to_desc(irq);
+	int retval;
+
+	if (!desc || !irq_settings_is_per_cpu_devid(desc))
+		return -EINVAL;
+	chip_bus_lock(desc);
+	retval = __setup_irq(irq, desc, act);
+	chip_bus_sync_unlock(desc);
+
+	return retval;
+}
+
+/**
+ *	request_percpu_irq - allocate a percpu interrupt line
+ *	@irq: Interrupt line to allocate
+ *	@handler: Function to be called when the IRQ occurs.
+ *	@devname: An ascii name for the claiming device
+ *	@dev_id: A percpu cookie passed back to the handler function
+ *
+ *	This call allocates interrupt resources, but doesn't
+ *	automatically enable the interrupt. It has to be done on each
+ *	CPU using enable_percpu_irq().
+ *
+ *	Dev_id must be globally unique. It is a per-cpu variable, and
+ *	the handler gets called with the interrupted CPU's instance of
+ *	that variable.
+ */
+int request_percpu_irq(unsigned int irq, irq_handler_t handler,
+		       const char *devname, void __percpu *dev_id)
+{
+	struct irqaction *action;
+	struct irq_desc *desc;
+	int retval;
+
+	if (!dev_id)
+		return -EINVAL;
+
+	desc = irq_to_desc(irq);
+	if (!desc || !irq_settings_can_request(desc) ||
+	    !irq_settings_is_per_cpu_devid(desc))
+		return -EINVAL;
+
+	action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
+	if (!action)
+		return -ENOMEM;
+
+	action->handler = handler;
+	action->flags = IRQF_PERCPU;
+	action->name = devname;
+	action->percpu_dev_id = dev_id;
+
+	chip_bus_lock(desc);
+	retval = __setup_irq(irq, desc, action);
+	chip_bus_sync_unlock(desc);
+
+	if (retval)
+		kfree(action);
+
+	return retval;
+}
diff --git a/kernel/irq/settings.h b/kernel/irq/settings.h
index f166783..1162f10 100644
--- a/kernel/irq/settings.h
+++ b/kernel/irq/settings.h
@@ -13,6 +13,7 @@ enum {
 	_IRQ_MOVE_PCNTXT	= IRQ_MOVE_PCNTXT,
 	_IRQ_NO_BALANCING	= IRQ_NO_BALANCING,
 	_IRQ_NESTED_THREAD	= IRQ_NESTED_THREAD,
+	_IRQ_PER_CPU_DEVID	= IRQ_PER_CPU_DEVID,
 	_IRQF_MODIFY_MASK	= IRQF_MODIFY_MASK,
 };
 
@@ -24,6 +25,7 @@ enum {
 #define IRQ_NOTHREAD		GOT_YOU_MORON
 #define IRQ_NOAUTOEN		GOT_YOU_MORON
 #define IRQ_NESTED_THREAD	GOT_YOU_MORON
+#define IRQ_PER_CPU_DEVID	GOT_YOU_MORON
 #undef IRQF_MODIFY_MASK
 #define IRQF_MODIFY_MASK	GOT_YOU_MORON
 
@@ -39,6 +41,11 @@ static inline bool irq_settings_is_per_cpu(struct irq_desc *desc)
 	return desc->status_use_accessors & _IRQ_PER_CPU;
 }
 
+static inline bool irq_settings_is_per_cpu_devid(struct irq_desc *desc)
+{
+	return desc->status_use_accessors & _IRQ_PER_CPU_DEVID;
+}
+
 static inline void irq_settings_set_per_cpu(struct irq_desc *desc)
 {
 	desc->status_use_accessors |= _IRQ_PER_CPU;
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH v4 2/4] genirq: percpu: allow interrupt type to be set at enable time
From: Marc Zyngier @ 2011-10-03 17:04 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1317661446-2876-1-git-send-email-marc.zyngier@arm.com>

As request_percpu_irq() doesn't allow for a percpu interrupt to have
its type configured (it is generally impossible to configure it on all
CPUs at once), add a 'type' argument to enable_percpu_irq().

This allows some low-level, board specific init code to be switched to
a generic API.

[ tglx: Added WARN_ON argument ]

Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Cc: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
 include/linux/interrupt.h |    2 +-
 kernel/irq/manage.c       |   15 ++++++++++++++-
 2 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index 1cdfd09..664544f 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -223,7 +223,7 @@ extern void disable_irq_nosync(unsigned int irq);
 extern void disable_irq(unsigned int irq);
 extern void disable_percpu_irq(unsigned int irq);
 extern void enable_irq(unsigned int irq);
-extern void enable_percpu_irq(unsigned int irq);
+extern void enable_percpu_irq(unsigned int irq, unsigned int type);
 
 /* The following three functions are for the core kernel use only. */
 #ifdef CONFIG_GENERIC_HARDIRQS
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 7b4b156..2bc8686 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -1419,7 +1419,7 @@ int request_any_context_irq(unsigned int irq, irq_handler_t handler,
 }
 EXPORT_SYMBOL_GPL(request_any_context_irq);
 
-void enable_percpu_irq(unsigned int irq)
+void enable_percpu_irq(unsigned int irq, unsigned int type)
 {
 	unsigned int cpu = smp_processor_id();
 	unsigned long flags;
@@ -1428,7 +1428,20 @@ void enable_percpu_irq(unsigned int irq)
 	if (!desc)
 		return;
 
+	type &= IRQ_TYPE_SENSE_MASK;
+	if (type != IRQ_TYPE_NONE) {
+		int ret;
+
+		ret = __irq_set_trigger(desc, irq, type);
+
+		if (ret) {
+			WARN(1, "failed to set type for IRQ%d\n, irq");
+			goto out;
+		}
+	}
+
 	irq_percpu_enable(desc, cpu);
+out:
 	irq_put_desc_unlock(desc, flags);
 }
 
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH v4 3/4] ARM: gic: consolidate PPI handling
From: Marc Zyngier @ 2011-10-03 17:04 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1317661446-2876-1-git-send-email-marc.zyngier@arm.com>

PPI handling is a bit of an odd beast. It uses its own low level
handling code and is hardwired to the local timers (hence lacking
a registration interface).

Instead, switch the low handling to the normal SPI handling code.
PPIs are handled by the handle_percpu_devid_irq flow.

This also allows the removal of some duplicated code.

Cc: Kukjin Kim <kgene.kim@samsung.com>
Cc: David Brown <davidb@codeaurora.org>
Cc: Bryan Huntsman <bryanh@codeaurora.org>
Cc: Tony Lindgren <tony@atomide.com>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: Magnus Damm <magnus.damm@gmail.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Acked-by: David Brown <davidb@codeaurora.org>
Tested-by: David Brown <davidb@codeaurora.org>
Tested-by: Shawn Guo <shawn.guo@linaro.org>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
 arch/arm/common/Kconfig                           |    1 +
 arch/arm/common/gic.c                             |   70 +++++++++++++++++++-
 arch/arm/include/asm/entry-macro-multi.S          |    7 --
 arch/arm/include/asm/hardirq.h                    |    3 -
 arch/arm/include/asm/hardware/entry-macro-gic.S   |   19 +-----
 arch/arm/include/asm/localtimer.h                 |    6 +-
 arch/arm/include/asm/smp.h                        |    5 --
 arch/arm/kernel/irq.c                             |    3 -
 arch/arm/kernel/smp.c                             |   27 ++------
 arch/arm/mach-exynos4/include/mach/entry-macro.S  |    6 +--
 arch/arm/mach-msm/board-msm8x60.c                 |   11 ---
 arch/arm/mach-msm/include/mach/entry-macro-qgic.S |   73 +--------------------
 arch/arm/mach-omap2/include/mach/entry-macro.S    |   14 +----
 arch/arm/mach-shmobile/entry-intc.S               |    3 -
 arch/arm/mach-shmobile/include/mach/entry-macro.S |    3 -
 15 files changed, 84 insertions(+), 167 deletions(-)

diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig
index 4b71766..114a432 100644
--- a/arch/arm/common/Kconfig
+++ b/arch/arm/common/Kconfig
@@ -1,4 +1,5 @@
 config ARM_GIC
+	select IRQ_PERCPU_DEVID
 	bool
 
 config ARM_VIC
diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c
index bdbb3f7..9b83077 100644
--- a/arch/arm/common/gic.c
+++ b/arch/arm/common/gic.c
@@ -28,10 +28,14 @@
 #include <linux/smp.h>
 #include <linux/cpumask.h>
 #include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/percpu.h>
+#include <linux/slab.h>
 
 #include <asm/irq.h>
 #include <asm/mach/irq.h>
 #include <asm/hardware/gic.h>
+#include <asm/localtimer.h>
 
 static DEFINE_RAW_SPINLOCK(irq_controller_lock);
 
@@ -255,6 +259,32 @@ void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq)
 	irq_set_chained_handler(irq, gic_handle_cascade_irq);
 }
 
+#ifdef CONFIG_LOCAL_TIMERS
+#define gic_ppi_handler		percpu_timer_handler
+#else
+static irqreturn_t gic_ppi_handler(int irq, void *dev_id)
+{
+	return IRQ_NONE;
+}
+#endif
+
+#define PPI_IRQACT(nr)						\
+	{							\
+		.handler	= gic_ppi_handler,		\
+		.flags		= IRQF_PERCPU | IRQF_TIMER,	\
+		.irq		= nr,				\
+		.name		= "PPI-" # nr,			\
+	}
+
+static struct irqaction ppi_irqaction_template[16] __initdata = {
+	PPI_IRQACT(0),  PPI_IRQACT(1),  PPI_IRQACT(2),  PPI_IRQACT(3),
+	PPI_IRQACT(4),  PPI_IRQACT(5),  PPI_IRQACT(6),  PPI_IRQACT(7),
+	PPI_IRQACT(8),  PPI_IRQACT(9),  PPI_IRQACT(10), PPI_IRQACT(11),
+	PPI_IRQACT(12), PPI_IRQACT(13), PPI_IRQACT(14), PPI_IRQACT(15),
+};
+
+static struct irqaction *ppi_irqaction;
+
 static void __init gic_dist_init(struct gic_chip_data *gic,
 	unsigned int irq_start)
 {
@@ -262,6 +292,7 @@ static void __init gic_dist_init(struct gic_chip_data *gic,
 	u32 cpumask;
 	void __iomem *base = gic->dist_base;
 	u32 cpu = 0;
+	u32 nrppis = 0, ppi_base = 0;
 
 #ifdef CONFIG_SMP
 	cpu = cpu_logical_map(smp_processor_id());
@@ -283,6 +314,28 @@ static void __init gic_dist_init(struct gic_chip_data *gic,
 		gic_irqs = 1020;
 
 	/*
+	 * Nobody would be insane enough to use PPIs on a secondary
+	 * GIC, right?
+	 */
+	if (gic == &gic_data[0]) {
+		nrppis = 16 - (irq_start & 15);
+		ppi_base = gic->irq_offset + 32 - nrppis;
+
+		ppi_irqaction = kmemdup(&ppi_irqaction_template[16 - nrppis],
+					sizeof(*ppi_irqaction) * nrppis,
+					GFP_KERNEL);
+
+		if (nrppis && !ppi_irqaction) {
+			pr_err("GIC: Can't allocate PPI memory");
+			nrppis = 0;
+			ppi_base = 0;
+		}
+	}
+
+	pr_info("Configuring GIC with %d sources (%d PPIs)\n",
+		gic_irqs, (gic == &gic_data[0]) ? nrppis : 0);
+
+	/*
 	 * Set all global interrupts to be level triggered, active low.
 	 */
 	for (i = 32; i < gic_irqs; i += 16)
@@ -317,7 +370,22 @@ static void __init gic_dist_init(struct gic_chip_data *gic,
 	/*
 	 * Setup the Linux IRQ subsystem.
 	 */
-	for (i = irq_start; i < irq_limit; i++) {
+	for (i = 0; i < nrppis; i++) {
+		int ppi = i + ppi_base;
+		int err;
+
+		irq_set_percpu_devid(ppi);
+		irq_set_chip_and_handler(ppi, &gic_chip,
+					 handle_percpu_devid_irq);
+		irq_set_chip_data(ppi, gic);
+		set_irq_flags(ppi, IRQF_VALID | IRQF_NOAUTOEN);
+
+		err = setup_percpu_irq(ppi, &ppi_irqaction[i]);
+		if (err)
+			pr_err("GIC: can't setup PPI%d (%d)\n", ppi, err);
+	}
+
+	for (i = irq_start + nrppis; i < irq_limit; i++) {
 		irq_set_chip_and_handler(i, &gic_chip, handle_fasteoi_irq);
 		irq_set_chip_data(i, gic);
 		set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
diff --git a/arch/arm/include/asm/entry-macro-multi.S b/arch/arm/include/asm/entry-macro-multi.S
index 2f1e209..88d6181 100644
--- a/arch/arm/include/asm/entry-macro-multi.S
+++ b/arch/arm/include/asm/entry-macro-multi.S
@@ -25,13 +25,6 @@
 	movne	r1, sp
 	adrne	lr, BSYM(1b)
 	bne	do_IPI
-
-#ifdef CONFIG_LOCAL_TIMERS
-	test_for_ltirq r0, r2, r6, lr
-	movne	r0, sp
-	adrne	lr, BSYM(1b)
-	bne	do_local_timer
-#endif
 #endif
 9997:
 	.endm
diff --git a/arch/arm/include/asm/hardirq.h b/arch/arm/include/asm/hardirq.h
index 89ad180..ddf07a9 100644
--- a/arch/arm/include/asm/hardirq.h
+++ b/arch/arm/include/asm/hardirq.h
@@ -9,9 +9,6 @@
 
 typedef struct {
 	unsigned int __softirq_pending;
-#ifdef CONFIG_LOCAL_TIMERS
-	unsigned int local_timer_irqs;
-#endif
 #ifdef CONFIG_SMP
 	unsigned int ipi_irqs[NR_IPI];
 #endif
diff --git a/arch/arm/include/asm/hardware/entry-macro-gic.S b/arch/arm/include/asm/hardware/entry-macro-gic.S
index c115b82..74ebc80 100644
--- a/arch/arm/include/asm/hardware/entry-macro-gic.S
+++ b/arch/arm/include/asm/hardware/entry-macro-gic.S
@@ -22,15 +22,11 @@
  * interrupt controller spec.  To wit:
  *
  * Interrupts 0-15 are IPI
- * 16-28 are reserved
- * 29-31 are local.  We allow 30 to be used for the watchdog.
+ * 16-31 are local.  We allow 30 to be used for the watchdog.
  * 32-1020 are global
  * 1021-1022 are reserved
  * 1023 is "spurious" (no interrupt)
  *
- * For now, we ignore all local interrupts so only return an interrupt if it's
- * between 30 and 1020.  The test_for_ipi routine below will pick up on IPIs.
- *
  * A simple read from the controller will tell us the number of the highest
  * priority enabled interrupt.  We then just need to check whether it is in the
  * valid range for an IRQ (30-1020 inclusive).
@@ -43,7 +39,7 @@
 
 	ldr	\tmp, =1021
 	bic     \irqnr, \irqstat, #0x1c00
-	cmp     \irqnr, #29
+	cmp     \irqnr, #15
 	cmpcc	\irqnr, \irqnr
 	cmpne	\irqnr, \tmp
 	cmpcs	\irqnr, \irqnr
@@ -62,14 +58,3 @@
 	strcc	\irqstat, [\base, #GIC_CPU_EOI]
 	cmpcs	\irqnr, \irqnr
 	.endm
-
-/* As above, this assumes that irqstat and base are preserved.. */
-
-	.macro test_for_ltirq, irqnr, irqstat, base, tmp
-	bic	\irqnr, \irqstat, #0x1c00
-	mov 	\tmp, #0
-	cmp	\irqnr, #29
-	moveq	\tmp, #1
-	streq	\irqstat, [\base, #GIC_CPU_EOI]
-	cmp	\tmp, #0
-	.endm
diff --git a/arch/arm/include/asm/localtimer.h b/arch/arm/include/asm/localtimer.h
index 080d74f..e3663f7 100644
--- a/arch/arm/include/asm/localtimer.h
+++ b/arch/arm/include/asm/localtimer.h
@@ -10,6 +10,8 @@
 #ifndef __ASM_ARM_LOCALTIMER_H
 #define __ASM_ARM_LOCALTIMER_H
 
+#include <linux/interrupt.h>
+
 struct clock_event_device;
 
 /*
@@ -18,9 +20,9 @@ struct clock_event_device;
 void percpu_timer_setup(void);
 
 /*
- * Called from assembly, this is the local timer IRQ handler
+ * Per-cpu timer IRQ handler
  */
-asmlinkage void do_local_timer(struct pt_regs *);
+irqreturn_t percpu_timer_handler(int irq, void *dev_id);
 
 
 #ifdef CONFIG_LOCAL_TIMERS
diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h
index bf25484..83f9b57 100644
--- a/arch/arm/include/asm/smp.h
+++ b/arch/arm/include/asm/smp.h
@@ -79,9 +79,4 @@ extern void cpu_die(void);
 extern void arch_send_call_function_single_ipi(int cpu);
 extern void arch_send_call_function_ipi_mask(const struct cpumask *mask);
 
-/*
- * show local interrupt info
- */
-extern void show_local_irqs(struct seq_file *, int);
-
 #endif /* ifndef __ASM_ARM_SMP_H */
diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c
index e20a5d0..8e744f6 100644
--- a/arch/arm/kernel/irq.c
+++ b/arch/arm/kernel/irq.c
@@ -58,9 +58,6 @@ int arch_show_interrupts(struct seq_file *p, int prec)
 #ifdef CONFIG_SMP
 	show_ipi_list(p, prec);
 #endif
-#ifdef CONFIG_LOCAL_TIMERS
-	show_local_irqs(p, prec);
-#endif
 	seq_printf(p, "%*s: %10lu\n", prec, "Err", irq_err_count);
 	return 0;
 }
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index c594ed2..979404a 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -504,10 +504,6 @@ u64 smp_irq_stat_cpu(unsigned int cpu)
 	for (i = 0; i < NR_IPI; i++)
 		sum += __get_irq_stat(cpu, ipi_irqs[i]);
 
-#ifdef CONFIG_LOCAL_TIMERS
-	sum += __get_irq_stat(cpu, local_timer_irqs);
-#endif
-
 	return sum;
 }
 
@@ -525,29 +521,16 @@ static void ipi_timer(void)
 }
 
 #ifdef CONFIG_LOCAL_TIMERS
-asmlinkage void __exception_irq_entry do_local_timer(struct pt_regs *regs)
+irqreturn_t percpu_timer_handler(int irq, void *dev_id)
 {
-	struct pt_regs *old_regs = set_irq_regs(regs);
-	int cpu = smp_processor_id();
+	struct clock_event_device *evt = &__get_cpu_var(percpu_clockevent);
 
 	if (local_timer_ack()) {
-		__inc_irq_stat(cpu, local_timer_irqs);
-		ipi_timer();
+		evt->event_handler(evt);
+		return IRQ_HANDLED;
 	}
 
-	set_irq_regs(old_regs);
-}
-
-void show_local_irqs(struct seq_file *p, int prec)
-{
-	unsigned int cpu;
-
-	seq_printf(p, "%*s: ", prec, "LOC");
-
-	for_each_present_cpu(cpu)
-		seq_printf(p, "%10u ", __get_irq_stat(cpu, local_timer_irqs));
-
-	seq_printf(p, " Local timer interrupts\n");
+	return IRQ_NONE;
 }
 #endif
 
diff --git a/arch/arm/mach-exynos4/include/mach/entry-macro.S b/arch/arm/mach-exynos4/include/mach/entry-macro.S
index d7a1e28..807d05d 100644
--- a/arch/arm/mach-exynos4/include/mach/entry-macro.S
+++ b/arch/arm/mach-exynos4/include/mach/entry-macro.S
@@ -55,7 +55,7 @@
 
 		bic     \irqnr, \irqstat, #0x1c00
 
-		cmp     \irqnr, #29
+		cmp     \irqnr, #15
 		cmpcc	\irqnr, \irqnr
 		cmpne	\irqnr, \tmp
 		cmpcs	\irqnr, \irqnr
@@ -77,7 +77,3 @@
 		cmpcs	\irqnr, \irqnr
 		.endm
 
-		/* As above, this assumes that irqstat and base are preserved.. */
-
-		.macro test_for_ltirq, irqnr, irqstat, base, tmp
-		.endm
diff --git a/arch/arm/mach-msm/board-msm8x60.c b/arch/arm/mach-msm/board-msm8x60.c
index a4bd858..30c6b58 100644
--- a/arch/arm/mach-msm/board-msm8x60.c
+++ b/arch/arm/mach-msm/board-msm8x60.c
@@ -53,8 +53,6 @@ static void __init msm8x60_map_io(void)
 
 static void __init msm8x60_init_irq(void)
 {
-	unsigned int i;
-
 	gic_init(0, GIC_PPI_START, MSM_QGIC_DIST_BASE,
 		 (void *)MSM_QGIC_CPU_BASE);
 
@@ -66,15 +64,6 @@ static void __init msm8x60_init_irq(void)
 	 */
 	if (!machine_is_msm8x60_sim())
 		writel(0x0000FFFF, MSM_QGIC_DIST_BASE + GIC_DIST_ENABLE_SET);
-
-	/* FIXME: Not installing AVS_SVICINT and AVS_SVICINTSWDONE yet
-	 * as they are configured as level, which does not play nice with
-	 * handle_percpu_irq.
-	 */
-	for (i = GIC_PPI_START; i < GIC_SPI_START; i++) {
-		if (i != AVS_SVICINT && i != AVS_SVICINTSWDONE)
-			irq_set_handler(i, handle_percpu_irq);
-	}
 }
 
 static void __init msm8x60_init(void)
diff --git a/arch/arm/mach-msm/include/mach/entry-macro-qgic.S b/arch/arm/mach-msm/include/mach/entry-macro-qgic.S
index 1246715..717076f 100644
--- a/arch/arm/mach-msm/include/mach/entry-macro-qgic.S
+++ b/arch/arm/mach-msm/include/mach/entry-macro-qgic.S
@@ -8,81 +8,10 @@
  * warranty of any kind, whether express or implied.
  */
 
-#include <mach/hardware.h>
-#include <asm/hardware/gic.h>
+#include <asm/hardware/entry-macro-gic.S>
 
 	.macro	disable_fiq
 	.endm
 
-	.macro  get_irqnr_preamble, base, tmp
-	ldr	\base, =gic_cpu_base_addr
-	ldr	\base, [\base]
-	.endm
-
 	.macro  arch_ret_to_user, tmp1, tmp2
 	.endm
-
-	/*
-	 * The interrupt numbering scheme is defined in the
-	 * interrupt controller spec.  To wit:
-	 *
-	 * Migrated the code from ARM MP port to be more consistent
-	 * with interrupt processing , the following still holds true
-	 * however, all interrupts are treated the same regardless of
-	 * if they are local IPI or PPI
-	 *
-	 * Interrupts 0-15 are IPI
-	 * 16-31 are PPI
-	 *   (16-18 are the timers)
-	 * 32-1020 are global
-	 * 1021-1022 are reserved
-	 * 1023 is "spurious" (no interrupt)
-	 *
-	 * A simple read from the controller will tell us the number of the
-	 * highest priority enabled interrupt.  We then just need to check
-	 * whether it is in the valid range for an IRQ (0-1020 inclusive).
-	 *
-	 * Base ARM code assumes that the local (private) peripheral interrupts
-	 * are not valid, we treat them differently, in that the privates are
-	 * handled like normal shared interrupts with the exception that only
-	 * one processor can register the interrupt and the handler must be
-	 * the same for all processors.
-	 */
-
-	.macro  get_irqnr_and_base, irqnr, irqstat, base, tmp
-
-	ldr  \irqstat, [\base, #GIC_CPU_INTACK] /* bits 12-10 =srcCPU,
-						   9-0 =int # */
-
-	bic     \irqnr, \irqstat, #0x1c00	@mask src
-	cmp     \irqnr, #15
-	ldr		\tmp, =1021
-	cmpcc	\irqnr, \irqnr
-	cmpne	\irqnr, \tmp
-	cmpcs	\irqnr, \irqnr
-
-	.endm
-
-	/* We assume that irqstat (the raw value of the IRQ acknowledge
-	 * register) is preserved from the macro above.
-	 * If there is an IPI, we immediately signal end of interrupt on the
-	 * controller, since this requires the original irqstat value which
-	 * we won't easily be able to recreate later.
-	 */
-	.macro test_for_ipi, irqnr, irqstat, base, tmp
-    bic \irqnr, \irqstat, #0x1c00
-    cmp \irqnr, #16
-    strcc   \irqstat, [\base, #GIC_CPU_EOI]
-    cmpcs   \irqnr, \irqnr
-	.endm
-
-	/* As above, this assumes that irqstat and base are preserved.. */
-
-	.macro test_for_ltirq, irqnr, irqstat, base, tmp
-    bic \irqnr, \irqstat, #0x1c00
-    mov     \tmp, #0
-    cmp \irqnr, #16
-    moveq   \tmp, #1
-    streq   \irqstat, [\base, #GIC_CPU_EOI]
-    cmp \tmp, #0
-	.endm
diff --git a/arch/arm/mach-omap2/include/mach/entry-macro.S b/arch/arm/mach-omap2/include/mach/entry-macro.S
index ceb8b7e..feb90a1 100644
--- a/arch/arm/mach-omap2/include/mach/entry-macro.S
+++ b/arch/arm/mach-omap2/include/mach/entry-macro.S
@@ -78,7 +78,7 @@
 4401:		ldr     \irqstat, [\base, #GIC_CPU_INTACK]
 		ldr     \tmp, =1021
 		bic     \irqnr, \irqstat, #0x1c00
-		cmp     \irqnr, #29
+		cmp     \irqnr, #15
 		cmpcc   \irqnr, \irqnr
 		cmpne   \irqnr, \tmp
 		cmpcs   \irqnr, \irqnr
@@ -101,18 +101,6 @@
 		it	cs
 		cmpcs	\irqnr, \irqnr
 		.endm
-
-		/* As above, this assumes that irqstat and base are preserved */
-
-		.macro test_for_ltirq, irqnr, irqstat, base, tmp
-		bic	\irqnr, \irqstat, #0x1c00
-		mov 	\tmp, #0
-		cmp	\irqnr, #29
-		itt	eq
-		moveq	\tmp, #1
-		streq	\irqstat, [\base, #GIC_CPU_EOI]
-		cmp	\tmp, #0
-		.endm
 #endif	/* CONFIG_SMP */
 
 #else	/* MULTI_OMAP2 */
diff --git a/arch/arm/mach-shmobile/entry-intc.S b/arch/arm/mach-shmobile/entry-intc.S
index cac0a7a..1a1c00c 100644
--- a/arch/arm/mach-shmobile/entry-intc.S
+++ b/arch/arm/mach-shmobile/entry-intc.S
@@ -51,7 +51,4 @@
 	.macro  test_for_ipi, irqnr, irqstat, base, tmp
 	.endm
 
-	.macro  test_for_ltirq, irqnr, irqstat, base, tmp
-	.endm
-
 	arch_irq_handler shmobile_handle_irq_intc
diff --git a/arch/arm/mach-shmobile/include/mach/entry-macro.S b/arch/arm/mach-shmobile/include/mach/entry-macro.S
index d791f10..8d4a416 100644
--- a/arch/arm/mach-shmobile/include/mach/entry-macro.S
+++ b/arch/arm/mach-shmobile/include/mach/entry-macro.S
@@ -27,8 +27,5 @@
 	.macro  test_for_ipi, irqnr, irqstat, base, tmp
 	.endm
 
-	.macro  test_for_ltirq, irqnr, irqstat, base, tmp
-	.endm
-
 	.macro  arch_ret_to_user, tmp1, tmp2
 	.endm
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH v4 4/4] ARM: gic, local timers: use the request_percpu_irq() interface
From: Marc Zyngier @ 2011-10-03 17:04 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1317661446-2876-1-git-send-email-marc.zyngier@arm.com>

This patch remove the hardcoded link between local timers and PPIs,
and convert the PPI users (TWD and MSM timers) to the new *_percpu_irq
interface. Also some collateral cleanup (local_timer_ack() is gone,
and the interrupt handler is strictly private to each driver).

PPIs are now useable for more than just the local timers.

Additional testing by David Brown (msm8250 and msm8660) and
Shawn Guo (imx6q).

Cc: David Brown <davidb@codeaurora.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Acked-by: David Brown <davidb@codeaurora.org>
Tested-by: David Brown <davidb@codeaurora.org>
Tested-by: Shawn Guo <shawn.guo@linaro.org>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
 arch/arm/common/gic.c               |   52 --------------------------
 arch/arm/include/asm/hardware/gic.h |    1 -
 arch/arm/include/asm/localtimer.h   |   17 ++++-----
 arch/arm/include/asm/smp_twd.h      |    2 +-
 arch/arm/kernel/smp.c               |   16 +--------
 arch/arm/kernel/smp_twd.c           |   47 +++++++++++++++++++++++-
 arch/arm/mach-exynos4/mct.c         |    5 ---
 arch/arm/mach-msm/timer.c           |   69 ++++++++++++++++++++---------------
 8 files changed, 94 insertions(+), 115 deletions(-)

diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c
index 9b83077..a3f335c 100644
--- a/arch/arm/common/gic.c
+++ b/arch/arm/common/gic.c
@@ -35,7 +35,6 @@
 #include <asm/irq.h>
 #include <asm/mach/irq.h>
 #include <asm/hardware/gic.h>
-#include <asm/localtimer.h>
 
 static DEFINE_RAW_SPINLOCK(irq_controller_lock);
 
@@ -259,32 +258,6 @@ void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq)
 	irq_set_chained_handler(irq, gic_handle_cascade_irq);
 }
 
-#ifdef CONFIG_LOCAL_TIMERS
-#define gic_ppi_handler		percpu_timer_handler
-#else
-static irqreturn_t gic_ppi_handler(int irq, void *dev_id)
-{
-	return IRQ_NONE;
-}
-#endif
-
-#define PPI_IRQACT(nr)						\
-	{							\
-		.handler	= gic_ppi_handler,		\
-		.flags		= IRQF_PERCPU | IRQF_TIMER,	\
-		.irq		= nr,				\
-		.name		= "PPI-" # nr,			\
-	}
-
-static struct irqaction ppi_irqaction_template[16] __initdata = {
-	PPI_IRQACT(0),  PPI_IRQACT(1),  PPI_IRQACT(2),  PPI_IRQACT(3),
-	PPI_IRQACT(4),  PPI_IRQACT(5),  PPI_IRQACT(6),  PPI_IRQACT(7),
-	PPI_IRQACT(8),  PPI_IRQACT(9),  PPI_IRQACT(10), PPI_IRQACT(11),
-	PPI_IRQACT(12), PPI_IRQACT(13), PPI_IRQACT(14), PPI_IRQACT(15),
-};
-
-static struct irqaction *ppi_irqaction;
-
 static void __init gic_dist_init(struct gic_chip_data *gic,
 	unsigned int irq_start)
 {
@@ -320,16 +293,6 @@ static void __init gic_dist_init(struct gic_chip_data *gic,
 	if (gic == &gic_data[0]) {
 		nrppis = 16 - (irq_start & 15);
 		ppi_base = gic->irq_offset + 32 - nrppis;
-
-		ppi_irqaction = kmemdup(&ppi_irqaction_template[16 - nrppis],
-					sizeof(*ppi_irqaction) * nrppis,
-					GFP_KERNEL);
-
-		if (nrppis && !ppi_irqaction) {
-			pr_err("GIC: Can't allocate PPI memory");
-			nrppis = 0;
-			ppi_base = 0;
-		}
 	}
 
 	pr_info("Configuring GIC with %d sources (%d PPIs)\n",
@@ -372,17 +335,12 @@ static void __init gic_dist_init(struct gic_chip_data *gic,
 	 */
 	for (i = 0; i < nrppis; i++) {
 		int ppi = i + ppi_base;
-		int err;
 
 		irq_set_percpu_devid(ppi);
 		irq_set_chip_and_handler(ppi, &gic_chip,
 					 handle_percpu_devid_irq);
 		irq_set_chip_data(ppi, gic);
 		set_irq_flags(ppi, IRQF_VALID | IRQF_NOAUTOEN);
-
-		err = setup_percpu_irq(ppi, &ppi_irqaction[i]);
-		if (err)
-			pr_err("GIC: can't setup PPI%d (%d)\n", ppi, err);
 	}
 
 	for (i = irq_start + nrppis; i < irq_limit; i++) {
@@ -443,16 +401,6 @@ void __cpuinit gic_secondary_init(unsigned int gic_nr)
 	gic_cpu_init(&gic_data[gic_nr]);
 }
 
-void __cpuinit gic_enable_ppi(unsigned int irq)
-{
-	unsigned long flags;
-
-	local_irq_save(flags);
-	irq_set_status_flags(irq, IRQ_NOPROBE);
-	gic_unmask_irq(irq_get_irq_data(irq));
-	local_irq_restore(flags);
-}
-
 #ifdef CONFIG_SMP
 void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
 {
diff --git a/arch/arm/include/asm/hardware/gic.h b/arch/arm/include/asm/hardware/gic.h
index 435d3f8..2dadd50 100644
--- a/arch/arm/include/asm/hardware/gic.h
+++ b/arch/arm/include/asm/hardware/gic.h
@@ -40,7 +40,6 @@ void gic_init(unsigned int, unsigned int, void __iomem *, void __iomem *);
 void gic_secondary_init(unsigned int);
 void gic_cascade_irq(unsigned int gic_nr, unsigned int irq);
 void gic_raise_softirq(const struct cpumask *mask, unsigned int irq);
-void gic_enable_ppi(unsigned int);
 
 struct gic_chip_data {
 	unsigned int irq_offset;
diff --git a/arch/arm/include/asm/localtimer.h b/arch/arm/include/asm/localtimer.h
index e3663f7..f5e1cec 100644
--- a/arch/arm/include/asm/localtimer.h
+++ b/arch/arm/include/asm/localtimer.h
@@ -19,27 +19,20 @@ struct clock_event_device;
  */
 void percpu_timer_setup(void);
 
-/*
- * Per-cpu timer IRQ handler
- */
-irqreturn_t percpu_timer_handler(int irq, void *dev_id);
-
-
 #ifdef CONFIG_LOCAL_TIMERS
 
 #ifdef CONFIG_HAVE_ARM_TWD
 
 #include "smp_twd.h"
 
-#define local_timer_ack()	twd_timer_ack()
+#define local_timer_stop(c)	twd_timer_stop((c))
 
 #else
 
 /*
- * Platform provides this to acknowledge a local timer IRQ.
- * Returns true if the local timer IRQ is to be processed.
+ * Stop the local timer
  */
-int local_timer_ack(void);
+void local_timer_stop(struct clock_event_device *);
 
 #endif
 
@@ -54,6 +47,10 @@ static inline int local_timer_setup(struct clock_event_device *evt)
 {
 	return -ENXIO;
 }
+
+static inline void local_timer_stop(struct clock_event_device *evt)
+{
+}
 #endif
 
 #endif
diff --git a/arch/arm/include/asm/smp_twd.h b/arch/arm/include/asm/smp_twd.h
index fed9981..ef9ffba 100644
--- a/arch/arm/include/asm/smp_twd.h
+++ b/arch/arm/include/asm/smp_twd.h
@@ -22,7 +22,7 @@ struct clock_event_device;
 
 extern void __iomem *twd_base;
 
-int twd_timer_ack(void);
 void twd_timer_setup(struct clock_event_device *);
+void twd_timer_stop(struct clock_event_device *);
 
 #endif
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index 979404a..df5bc0a 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -520,20 +520,6 @@ static void ipi_timer(void)
 	irq_exit();
 }
 
-#ifdef CONFIG_LOCAL_TIMERS
-irqreturn_t percpu_timer_handler(int irq, void *dev_id)
-{
-	struct clock_event_device *evt = &__get_cpu_var(percpu_clockevent);
-
-	if (local_timer_ack()) {
-		evt->event_handler(evt);
-		return IRQ_HANDLED;
-	}
-
-	return IRQ_NONE;
-}
-#endif
-
 #ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
 static void smp_timer_broadcast(const struct cpumask *mask)
 {
@@ -584,7 +570,7 @@ static void percpu_timer_stop(void)
 	unsigned int cpu = smp_processor_id();
 	struct clock_event_device *evt = &per_cpu(percpu_clockevent, cpu);
 
-	evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt);
+	local_timer_stop(evt);
 }
 #endif
 
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c
index 01c1862..a8a6682 100644
--- a/arch/arm/kernel/smp_twd.c
+++ b/arch/arm/kernel/smp_twd.c
@@ -19,6 +19,7 @@
 #include <linux/io.h>
 
 #include <asm/smp_twd.h>
+#include <asm/localtimer.h>
 #include <asm/hardware/gic.h>
 
 /* set up by the platform code */
@@ -26,6 +27,8 @@ void __iomem *twd_base;
 
 static unsigned long twd_timer_rate;
 
+static struct clock_event_device __percpu **twd_evt;
+
 static void twd_set_mode(enum clock_event_mode mode,
 			struct clock_event_device *clk)
 {
@@ -80,6 +83,12 @@ int twd_timer_ack(void)
 	return 0;
 }
 
+void twd_timer_stop(struct clock_event_device *clk)
+{
+	twd_set_mode(CLOCK_EVT_MODE_UNUSED, clk);
+	disable_percpu_irq(clk->irq);
+}
+
 static void __cpuinit twd_calibrate_rate(void)
 {
 	unsigned long count;
@@ -119,11 +128,43 @@ static void __cpuinit twd_calibrate_rate(void)
 	}
 }
 
+static irqreturn_t twd_handler(int irq, void *dev_id)
+{
+	struct clock_event_device *evt = *(struct clock_event_device **)dev_id;
+
+	if (twd_timer_ack()) {
+		evt->event_handler(evt);
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
 /*
  * Setup the local clock events for a CPU.
  */
 void __cpuinit twd_timer_setup(struct clock_event_device *clk)
 {
+	struct clock_event_device **this_cpu_clk;
+
+	if (!twd_evt) {
+		int err;
+
+		twd_evt = alloc_percpu(struct clock_event_device *);
+		if (!twd_evt) {
+			pr_err("twd: can't allocate memory\n");
+			return;
+		}
+
+		err = request_percpu_irq(clk->irq, twd_handler,
+					 "twd", twd_evt);
+		if (err) {
+			pr_err("twd: can't register interrupt %d (%d)\n",
+			       clk->irq, err);
+			return;
+		}
+	}
+
 	twd_calibrate_rate();
 
 	clk->name = "local_timer";
@@ -137,8 +178,10 @@ void __cpuinit twd_timer_setup(struct clock_event_device *clk)
 	clk->max_delta_ns = clockevent_delta2ns(0xffffffff, clk);
 	clk->min_delta_ns = clockevent_delta2ns(0xf, clk);
 
+	this_cpu_clk = __this_cpu_ptr(twd_evt);
+	*this_cpu_clk = clk;
+
 	clockevents_register_device(clk);
 
-	/* Make sure our local interrupt controller has this enabled */
-	gic_enable_ppi(clk->irq);
+	enable_percpu_irq(clk->irq, 0);
 }
diff --git a/arch/arm/mach-exynos4/mct.c b/arch/arm/mach-exynos4/mct.c
index ddd8686..b3a0016 100644
--- a/arch/arm/mach-exynos4/mct.c
+++ b/arch/arm/mach-exynos4/mct.c
@@ -402,11 +402,6 @@ int __cpuinit local_timer_setup(struct clock_event_device *evt)
 	return 0;
 }
 
-int local_timer_ack(void)
-{
-	return 0;
-}
-
 #endif /* CONFIG_LOCAL_TIMERS */
 
 static void __init exynos4_timer_resources(void)
diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c
index 63621f1..afeeca5 100644
--- a/arch/arm/mach-msm/timer.c
+++ b/arch/arm/mach-msm/timer.c
@@ -71,12 +71,16 @@ enum timer_location {
 struct msm_clock {
 	struct clock_event_device   clockevent;
 	struct clocksource          clocksource;
-	struct irqaction            irq;
+	unsigned int		    irq;
 	void __iomem                *regbase;
 	uint32_t                    freq;
 	uint32_t                    shift;
 	void __iomem                *global_counter;
 	void __iomem                *local_counter;
+	union {
+		struct clock_event_device		*evt;
+		struct clock_event_device __percpu	**percpu_evt;
+	};		
 };
 
 enum {
@@ -87,13 +91,10 @@ enum {
 
 
 static struct msm_clock msm_clocks[];
-static struct clock_event_device *local_clock_event;
 
 static irqreturn_t msm_timer_interrupt(int irq, void *dev_id)
 {
-	struct clock_event_device *evt = dev_id;
-	if (smp_processor_id() != 0)
-		evt = local_clock_event;
+	struct clock_event_device *evt = *(struct clock_event_device **)dev_id;
 	if (evt->event_handler == NULL)
 		return IRQ_HANDLED;
 	evt->event_handler(evt);
@@ -171,13 +172,7 @@ static struct msm_clock msm_clocks[] = {
 			.mask           = CLOCKSOURCE_MASK(32),
 			.flags          = CLOCK_SOURCE_IS_CONTINUOUS,
 		},
-		.irq = {
-			.name    = "gp_timer",
-			.flags   = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_RISING,
-			.handler = msm_timer_interrupt,
-			.dev_id  = &msm_clocks[0].clockevent,
-			.irq     = INT_GP_TIMER_EXP
-		},
+		.irq = INT_GP_TIMER_EXP,
 		.freq = GPT_HZ,
 	},
 	[MSM_CLOCK_DGT] = {
@@ -196,13 +191,7 @@ static struct msm_clock msm_clocks[] = {
 			.mask           = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)),
 			.flags          = CLOCK_SOURCE_IS_CONTINUOUS,
 		},
-		.irq = {
-			.name    = "dg_timer",
-			.flags   = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_RISING,
-			.handler = msm_timer_interrupt,
-			.dev_id  = &msm_clocks[1].clockevent,
-			.irq     = INT_DEBUG_TIMER_EXP
-		},
+		.irq = INT_DEBUG_TIMER_EXP,
 		.freq = DGT_HZ >> MSM_DGT_SHIFT,
 		.shift = MSM_DGT_SHIFT,
 	}
@@ -261,10 +250,30 @@ static void __init msm_timer_init(void)
 			printk(KERN_ERR "msm_timer_init: clocksource_register "
 			       "failed for %s\n", cs->name);
 
-		res = setup_irq(clock->irq.irq, &clock->irq);
+		ce->irq = clock->irq;
+		if (cpu_is_msm8x60() || cpu_is_msm8960()) {
+			clock->percpu_evt = alloc_percpu(struct clock_event_device *);
+			if (!clock->percpu_evt) {
+				pr_err("msm_timer_init: memory allocation "
+				       "failed for %s\n", ce->name);
+				continue;
+			}
+
+			*__this_cpu_ptr(clock->percpu_evt) = ce;
+			res = request_percpu_irq(ce->irq, msm_timer_interrupt,
+						 ce->name, clock->percpu_evt);
+			if (!res)
+				enable_percpu_irq(ce->irq, 0);
+		} else {
+			clock->evt = ce;
+			res = request_irq(ce->irq, msm_timer_interrupt,
+					  IRQF_TIMER | IRQF_NOBALANCING | IRQF_TRIGGER_RISING,
+					  ce->name, &clock->evt);
+		}
+
 		if (res)
-			printk(KERN_ERR "msm_timer_init: setup_irq "
-			       "failed for %s\n", cs->name);
+			pr_err("msm_timer_init: request_irq failed for %s\n",
+			       ce->name);
 
 		clockevents_register_device(ce);
 	}
@@ -273,6 +282,7 @@ static void __init msm_timer_init(void)
 #ifdef CONFIG_SMP
 int __cpuinit local_timer_setup(struct clock_event_device *evt)
 {
+	static bool local_timer_inited;
 	struct msm_clock *clock = &msm_clocks[MSM_GLOBAL_TIMER];
 
 	/* Use existing clock_event for cpu 0 */
@@ -281,12 +291,13 @@ int __cpuinit local_timer_setup(struct clock_event_device *evt)
 
 	writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL);
 
-	if (!local_clock_event) {
+	if (!local_timer_inited) {
 		writel(0, clock->regbase  + TIMER_ENABLE);
 		writel(0, clock->regbase + TIMER_CLEAR);
 		writel(~0, clock->regbase + TIMER_MATCH_VAL);
+		local_timer_inited = true;
 	}
-	evt->irq = clock->irq.irq;
+	evt->irq = clock->irq;
 	evt->name = "local_timer";
 	evt->features = CLOCK_EVT_FEAT_ONESHOT;
 	evt->rating = clock->clockevent.rating;
@@ -298,17 +309,17 @@ int __cpuinit local_timer_setup(struct clock_event_device *evt)
 		clockevent_delta2ns(0xf0000000 >> clock->shift, evt);
 	evt->min_delta_ns = clockevent_delta2ns(4, evt);
 
-	local_clock_event = evt;
-
-	gic_enable_ppi(clock->irq.irq);
+	*__this_cpu_ptr(clock->percpu_evt) = evt;
+	enable_percpu_irq(evt->irq, 0);
 
 	clockevents_register_device(evt);
 	return 0;
 }
 
-inline int local_timer_ack(void)
+void local_timer_stop(struct clock_event_device *evt)
 {
-	return 1;
+	evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt);
+	disable_percpu_irq(evt->irq);
 }
 
 #endif
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH v2 1/7] clk: Add a generic clock infrastructure
From: Mark Brown @ 2011-10-03 17:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20111003164309.GA19925@n2100.arm.linux.org.uk>

On Mon, Oct 03, 2011 at 05:43:09PM +0100, Russell King - ARM Linux wrote:
> On Mon, Oct 03, 2011 at 05:31:08PM +0100, Mark Brown wrote:

[Not being many off-SoC clocks]
> > I dunno, I get the impression that some of this is due to the current
> > limitations of the clock API rather than due to a lack of clocks -
> > perhaps that's specific to the applications I look at, though.
> > applications 

> The clk API per-se has nothing to do with how clocks are registered.
> There are two things that are the clk API:

Right, it's consumer side only which is the limitation I'm talking about
- at the minute it's only possible to use the clk API for on-SoC clocks
(or at least clocks that the SoC cares about), we don't have a clock API
for cross platform clock providers.  I don't know if getting a cross
platform implementation will change that situation much.

^ permalink raw reply

* [PATCH v2] OMAP2PLUS: DSS: Ensure DSS works correctly if display is enabled in bootloader
From: Paul Walmsley @ 2011-10-03 17:21 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1317630070.2026.27.camel@deskari>

Hi Tomi,

On Mon, 3 Oct 2011, Tomi Valkeinen wrote:

> On Sun, 2011-10-02 at 22:45 -0600, Paul Walmsley wrote:
> > On Mon, 12 Sep 2011, Archit Taneja wrote:
> > 
> > > Resetting DISPC when a DISPC output is enabled causes the DSS to go into an
> > > inconsistent state. Thus if the bootloader has enabled a display, the hwmod code
> > > cannot reset the DISPC module just like that, but the outputs need to be
> > > disabled first.
> > > 
> > > Add function dispc_disable_outputs() which disables all active overlay manager
> > > and ensure all frame transfers are completed.
> > > 
> > > Modify omap_dss_reset() to call this function and clear DSS_CONTROL,
> > > DSS_SDI_CONTROL and DSS_PLL_CONTROL so that DSS is in a clean state when the
> > > DSS2 driver starts.
> > > 
> > > This resolves the hang issue(caused by a L3 error during boot) seen on the
> > > beagle board C3, which has a factory bootloader that enables display. The issue
> > > is resolved with this patch.
> 
> <snip>
> 
> > +struct omap_dss_dispc_dev_attr {
> > +	u8	manager_count;
> > +	bool	has_framedonetv_irq;
> > +};
> > +
> > +#endif
> > diff --git a/arch/arm/mach-omap2/omap_hwmod_2420_data.c b/arch/arm/mach-omap2/omap_hwmod_2420_data.c
> > index 09d9395..8e32cb3 100644
> > --- a/arch/arm/mach-omap2/omap_hwmod_2420_data.c
> > +++ b/arch/arm/mach-omap2/omap_hwmod_2420_data.c
> > @@ -945,6 +945,7 @@ static struct omap_hwmod omap2420_dss_dispc_hwmod = {
> >  	.slaves_cnt	= ARRAY_SIZE(omap2420_dss_dispc_slaves),
> >  	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP2420),
> >  	.flags		= HWMOD_NO_IDLEST,
> > +	.dev_attr	= &omap2_3_dss_dispc_dev_attr
> >  };
> 
> I didn't know you can add arbitrary data like that to hwmods. What kind
> of data is it meant for? 

It's intended for data that's specific to the integration of that IP block 
on a given SoC.

In an ideal world, Linux could just read some registers from the IP block 
to determine these.  Many of our IP blocks have a REVISION register.  But 
many types of integration-specific data are not identified in that 
register. And often when IP blocks are revised, the hardware people seem 
to forget to update the REVISION register :-(.  So we need some way to 
supply this information in software.

In terms of concrete uses, one use would be to identify IP block 
features that may be enabled on certain instances of the IP.  For example, 
on OMAPs, some DMTIMER blocks have 1ms tick adjustment support; others do 
not.  As far as I know, there's no way for the driver to determine this 
from the IP block.

The dev_attr data is intended to be fairly high-level functional data.

> Can the data be used by the driver, or is it meant just for arch stuff?

It can be used by both.  But if it's intended for use by the driver, then 
the dev_attr data needs to be copied into struct platform_data by the 
arch-specific code.  This is because the drivers themselves should have no 
direct dependencies on hwmod data or code, in case the IP block is used on 
a non-OMAP SoC.

For example, if you look at arch/arm/mach-omap2/hsmmc.c around line 471, 
you can see the arch-specific code copying dev_attr data into 
platform_data.

> I'm wondering this as we have a complex mechanism in the dss driver to
> find out about the differences of DSS hardware
> (drivers/video/omap2/dss/dss_features.[ch]). The dss_features system is
> currently part of the driver, but should be moved under arch/arm/*omap*
> at some point, and this hwmod dev_attr sounds like it could possibly be
> a right place to handle these.
> 
> I looked at how the dev_attrs are used, and all of them seemed to be
> very small, a few fields at max. The DSS features set is, on the other
> hand, quite big amount of data, and meant for the driver.

Just from a brief look, it looks like much of that data would be good 
candidates to pass via the dev_attr mechanism.  The reg_fields would be one 
exception: it would be better (in terms of dev_attr) to simply pass data 
like ".reg_field_layout = 1" or ".reg_field_layout = 2", and then select 
between those tables in the driver code.

Beno?t is right, though, that you might want to take the upcoming DT 
migration into account in your plans.


- Paul

^ permalink raw reply

* [PATCH v2] OMAP2PLUS: DSS: Ensure DSS works correctly if display is enabled in bootloader
From: Paul Walmsley @ 2011-10-03 17:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <4E898468.2060508@ti.com>

+ devicetree-discuss, lkml

On Mon, 3 Oct 2011, Cousson, Benoit wrote:

> But at that time, device tree was not there...
> Now, the whole dev_attr stuff will be replaced because device tree is able to
> provide the driver any kind of custom information that can be retrieved
> directly from the driver without having to use a pdata in between. So I'm not
> sure it worth spending too much time on that feature stuff.
> 
> As an example here is the ongoing GPIO DT migration:
> http://www.mail-archive.com/linux-omap at vger.kernel.org/msg56505.html
> 
> 3.2 will have the basic DT support using hwmod as a backend, but the idea is
> that for 3.3, we start removing some information from hwmod to rely on device
> tree only.

One comment here though -- and I will make this comment on the original 
series too -- is that we should avoid adding direct DT dependencies into 
the driver.

Specifically, these of_get_property() and of_property*() calls in the 
driver aren't right.

We need some way of doing this that is completely independent from the 
device data format.  Some way that does not care whether the input data is 
coming from DT, platform_data, ACPI, or whatever the new flavor of the 
year will be next year.  Or we need to declare that these of_*() calls are 
not DT-specific, and define them as hooks that the device data format code 
can handle as it pleases.

Otherwise we'll need shim layers for each new data format in the driver 
code and that will be a huge and unnecessary mess.


- Paul

^ permalink raw reply

* [PATCH v4 00/10] Per SoC descriptor
From: Marc Zyngier @ 2011-10-03 17:35 UTC (permalink / raw)
  To: linux-arm-kernel

This patch series introduces a per-soc descriptor which should, in the
end, contain most of the SoC specific operations.

This first patch series introduces the arm_soc_desc structure, adds
per-soc SMP and CPU hotplug operations, and converts all SMP platform
to this new scheme.

Patches against next-20110930. Tested on VExpress (A5 and A15),
RealView EB-11MP, OMAP4 (Panda) and Tegra2 (Harmony).

Changelog:

^ permalink raw reply

* [PATCH v4 01/10] ARM: SoC: Introduce per SoC descriptor
From: Marc Zyngier @ 2011-10-03 17:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1317663356-5114-1-git-send-email-marc.zyngier@arm.com>

The ARM core code expects the various SoCs to hide their
implementation differences behind a well established API.
The various sub-arch-specific bit are often either at
the machine descriptor level, or provided at link time
by the sub-arch code.

The SoC descriptor is a container that holds the SoC
specific bits that can be moved away from the machine
descriptor as well as an indirection point for the
global symbols (SMP and CPU hotplug support, for example).

This patch introduce this SoC descriptor, with the only field
being the name of the SoC.

Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Nicolas Pitre <nico@fluxnic.net>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
 arch/arm/include/asm/mach/arch.h |    2 ++
 arch/arm/include/asm/soc.h       |   21 +++++++++++++++++++++
 arch/arm/kernel/setup.c          |    9 +++++++++
 3 files changed, 32 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/include/asm/soc.h

diff --git a/arch/arm/include/asm/mach/arch.h b/arch/arm/include/asm/mach/arch.h
index 7d19425..a366432 100644
--- a/arch/arm/include/asm/mach/arch.h
+++ b/arch/arm/include/asm/mach/arch.h
@@ -13,6 +13,7 @@
 struct tag;
 struct meminfo;
 struct sys_timer;
+struct arm_soc_desc;
 
 struct machine_desc {
 	unsigned int		nr;		/* architecture number	*/
@@ -34,6 +35,7 @@ struct machine_desc {
 	unsigned int		reserve_lp1 :1;	/* never has lp1	*/
 	unsigned int		reserve_lp2 :1;	/* never has lp2	*/
 	unsigned int		soft_reboot :1;	/* soft reboot		*/
+	struct arm_soc_desc	*soc;		/* SoC descriptor	*/
 	void			(*fixup)(struct tag *, char **,
 					 struct meminfo *);
 	void			(*reserve)(void);/* reserve mem blocks	*/
diff --git a/arch/arm/include/asm/soc.h b/arch/arm/include/asm/soc.h
new file mode 100644
index 0000000..ce92784
--- /dev/null
+++ b/arch/arm/include/asm/soc.h
@@ -0,0 +1,21 @@
+/*
+ *  linux/arch/arm/include/asm/soc.h
+ *
+ *  Copyright (C) 2011 ARM Ltd.
+ *  All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __ASM_ARM_SOC_H
+#define __ASM_ARM_SOC_H
+
+struct arm_soc_desc {
+	const char		*name;
+};
+
+extern const struct arm_soc_desc	*soc_desc;
+
+#endif	/* __ASM_ARM_SOC_H */
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index d399777..34ffb2e 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -45,6 +45,7 @@
 #include <asm/cachetype.h>
 #include <asm/tlbflush.h>
 #include <asm/system.h>
+#include <asm/soc.h>
 
 #include <asm/prom.h>
 #include <asm/mach/arch.h>
@@ -140,6 +141,8 @@ static const char *cpu_name;
 static const char *machine_name;
 static char __initdata cmd_line[COMMAND_LINE_SIZE];
 struct machine_desc *machine_desc __initdata;
+const struct arm_soc_desc *soc_desc;
+static struct arm_soc_desc __soc_desc __read_mostly;
 
 static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;
 static union { char c[4]; unsigned long l; } endian_test __initdata = { { 'l', '?', '?', 'b' } };
@@ -913,6 +916,12 @@ void __init setup_arch(char **cmdline_p)
 		mdesc = setup_machine_tags(machine_arch_type);
 	machine_desc = mdesc;
 	machine_name = mdesc->name;
+	if (mdesc->soc) {
+		__soc_desc = *mdesc->soc;
+		soc_desc = &__soc_desc;
+		pr_info("SoC: %s\n", soc_desc->name);
+	} else
+		soc_desc = NULL;
 
 	if (mdesc->soft_reboot)
 		reboot_setup("s");
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH v4 02/10] ARM: SoC: Add per SoC SMP and CPU hotplug operations
From: Marc Zyngier @ 2011-10-03 17:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1317663356-5114-1-git-send-email-marc.zyngier@arm.com>

Populate the SoC descriptor structure with the SMP and CPU hotplug
operations. To allow the kernel to continue building, the platform
hooks are defined as weak symbols which are overrided by the
platform code. Once all platforms are converted, the "weak" attribute
will be removed and the function made static.

Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Nicolas Pitre <nico@fluxnic.net>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
 arch/arm/include/asm/soc.h |   48 ++++++++++++++++++++++++++++++++++++++++++-
 arch/arm/kernel/setup.c    |   29 ++++++++++++++++++++++---
 arch/arm/kernel/smp.c      |   47 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 118 insertions(+), 6 deletions(-)

diff --git a/arch/arm/include/asm/soc.h b/arch/arm/include/asm/soc.h
index ce92784..f1dd657 100644
--- a/arch/arm/include/asm/soc.h
+++ b/arch/arm/include/asm/soc.h
@@ -12,10 +12,54 @@
 #ifndef __ASM_ARM_SOC_H
 #define __ASM_ARM_SOC_H
 
+struct task_struct;
+
+struct arm_soc_smp_init_ops {
+	/*
+	 * Setup the set of possible CPUs (via set_cpu_possible)
+	 */
+	void (*smp_init_cpus)(void);
+	/*
+	 * Initialize cpu_possible map, and enable coherency
+	 */
+	void (*smp_prepare_cpus)(unsigned int max_cpus);
+};
+
+struct arm_soc_smp_ops {
+	/*
+	 * Perform platform specific initialisation of the specified CPU.
+	 */
+	void (*smp_secondary_init)(unsigned int cpu);
+	/*
+	 * Boot a secondary CPU, and assign it the specified idle task.
+	 * This also gives us the initial stack to use for this CPU.
+	 */
+	int  (*smp_boot_secondary)(unsigned int cpu, struct task_struct *idle);
+#ifdef CONFIG_HOTPLUG_CPU
+	int  (*cpu_kill)(unsigned int cpu);
+	void (*cpu_die)(unsigned int cpu);
+	int  (*cpu_disable)(unsigned int cpu);
+#endif
+};
+
 struct arm_soc_desc {
-	const char		*name;
+	const char			*name;
+#ifdef CONFIG_SMP
+	struct arm_soc_smp_init_ops	*smp_init_ops;
+	struct arm_soc_smp_ops		*smp_ops;
+#endif
 };
 
-extern const struct arm_soc_desc	*soc_desc;
+#ifdef CONFIG_SMP
+#define soc_smp_init_ops(ops)	.smp_init_ops = &(ops),
+#define soc_smp_ops(ops)	.smp_ops = &(ops),
+#else
+#define soc_smp_init_ops(ops)	/* empty */
+#define soc_smp_ops(ops)	/* empty */
+#endif
+
+extern const struct arm_soc_desc		*soc_desc;
+extern const struct arm_soc_smp_init_ops	*soc_smp_init_ops;
+extern const struct arm_soc_smp_ops		*soc_smp_ops;
 
 #endif	/* __ASM_ARM_SOC_H */
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index 34ffb2e..351ae18 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -141,8 +141,12 @@ static const char *cpu_name;
 static const char *machine_name;
 static char __initdata cmd_line[COMMAND_LINE_SIZE];
 struct machine_desc *machine_desc __initdata;
-const struct arm_soc_desc *soc_desc;
-static struct arm_soc_desc __soc_desc __read_mostly;
+const struct arm_soc_desc *soc_desc __initdata;
+#ifdef CONFIG_SMP
+const struct arm_soc_smp_init_ops *soc_smp_init_ops  __initdata;
+const struct arm_soc_smp_ops *soc_smp_ops  __cpuinitdata;
+static struct arm_soc_smp_ops __soc_smp_ops __cpuinitdata;
+#endif
 
 static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;
 static union { char c[4]; unsigned long l; } endian_test __initdata = { { 'l', '?', '?', 'b' } };
@@ -917,11 +921,28 @@ void __init setup_arch(char **cmdline_p)
 	machine_desc = mdesc;
 	machine_name = mdesc->name;
 	if (mdesc->soc) {
-		__soc_desc = *mdesc->soc;
-		soc_desc = &__soc_desc;
+		soc_desc = mdesc->soc;
 		pr_info("SoC: %s\n", soc_desc->name);
 	} else
 		soc_desc = NULL;
+#ifdef CONFIG_SMP
+	if (soc_desc && soc_desc->smp_init_ops)
+		soc_smp_init_ops = soc_desc->smp_init_ops;
+	else
+		soc_smp_ops = NULL;
+
+	/*
+	 * Warning: we're copying an __initdata structure into a
+	 * __cpuinitdata structure. We *know* it is valid because only
+	 * __cpuinit (or more persistant) functions should be pointed
+	 * to by soc_smp_ops. Still, this is borderline ugly.
+	 */
+	if (soc_desc && soc_desc->smp_ops) {
+		__soc_smp_ops = *soc_desc->smp_ops;
+		soc_smp_ops = &__soc_smp_ops;
+	} else
+		soc_smp_ops = NULL;
+#endif
 
 	if (mdesc->soft_reboot)
 		reboot_setup("s");
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index 8bb30c2..e08d2e8 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -28,6 +28,7 @@
 #include <linux/completion.h>
 
 #include <linux/atomic.h>
+#include <asm/soc.h>
 #include <asm/cacheflush.h>
 #include <asm/cpu.h>
 #include <asm/cputype.h>
@@ -155,9 +156,55 @@ int __cpuinit __cpu_up(unsigned int cpu)
 	return ret;
 }
 
+/* SoC helpers */
+void __attribute__((weak)) __init smp_init_cpus(void)
+{
+	if (soc_smp_init_ops && soc_smp_init_ops->smp_init_cpus)
+		soc_smp_init_ops->smp_init_cpus();
+}
+
+void __attribute__((weak)) __init platform_smp_prepare_cpus(unsigned int max_cpus)
+{
+	if (soc_smp_ops && soc_smp_init_ops->smp_prepare_cpus)
+		soc_smp_init_ops->smp_prepare_cpus(max_cpus);
+}
+
+void __attribute__((weak)) __cpuinit platform_secondary_init(unsigned int cpu)
+{
+	if (soc_smp_ops && soc_smp_ops->smp_secondary_init)
+		soc_smp_ops->smp_secondary_init(cpu);
+}
+
+int __attribute__((weak)) __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+	if (soc_smp_ops && soc_smp_ops->smp_boot_secondary)
+		return soc_smp_ops->smp_boot_secondary(cpu, idle);
+	return -ENOSYS;
+}
+
 #ifdef CONFIG_HOTPLUG_CPU
 static void percpu_timer_stop(void);
 
+int __attribute__((weak)) __cpuinit platform_cpu_kill(unsigned int cpu)
+{
+	if (soc_smp_ops && soc_smp_ops->cpu_kill)
+		return soc_smp_ops->cpu_kill(cpu);
+	return 0;
+}
+
+void __attribute__((weak)) __cpuinit platform_cpu_die(unsigned int cpu)
+{
+	if (soc_smp_ops && soc_smp_ops->cpu_die)
+		soc_smp_ops->cpu_die(cpu);
+}
+
+int __attribute__((weak)) __cpuinit platform_cpu_disable(unsigned int cpu)
+{
+	if (soc_smp_ops && soc_smp_ops->cpu_disable)
+		return soc_smp_ops->cpu_disable(cpu);
+	return -EPERM;
+}
+
 /*
  * __cpu_disable runs on the processor to be shutdown.
  */
-- 
1.7.0.4

^ permalink raw reply related


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