diff options
26 files changed, 1393 insertions, 440 deletions
diff --git a/arch/arm/boot/dts/r8a7779.dtsi b/arch/arm/boot/dts/r8a7779.dtsi new file mode 100644 index 00000000000..fe5c6f21327 --- /dev/null +++ b/arch/arm/boot/dts/r8a7779.dtsi @@ -0,0 +1,98 @@ +/* + * Device Tree Source for Renesas r8a7779 + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Copyright (C) 2013 Simon Horman + * + * This file is licensed under the terms of the GNU General Public License + * version 2.  This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +/include/ "skeleton.dtsi" + +/ { +	compatible = "renesas,r8a7779"; + +	cpus { +		#address-cells = <1>; +		#size-cells = <0>; + +		cpu@0 { +			device_type = "cpu"; +			compatible = "arm,cortex-a9"; +			reg = <0>; +		}; +		cpu@1 { +			device_type = "cpu"; +			compatible = "arm,cortex-a9"; +			reg = <1>; +		}; +		cpu@2 { +			device_type = "cpu"; +			compatible = "arm,cortex-a9"; +			reg = <2>; +		}; +		cpu@3 { +			device_type = "cpu"; +			compatible = "arm,cortex-a9"; +			reg = <3>; +		}; +	}; + +        gic: interrupt-controller@f0001000 { +                compatible = "arm,cortex-a9-gic"; +                #interrupt-cells = <3>; +                interrupt-controller; +                reg = <0xf0001000 0x1000>, +                      <0xf0000100 0x100>; +        }; + +	i2c0: i2c@0xffc70000 { +		#address-cells = <1>; +		#size-cells = <0>; +		compatible = "renesas,rmobile-iic"; +		reg = <0xffc70000 0x1000>; +		interrupt-parent = <&gic>; +		interrupts = <0 79 0x4>; +	}; + +	i2c1: i2c@0xffc71000 { +		#address-cells = <1>; +		#size-cells = <0>; +		compatible = "renesas,rmobile-iic"; +		reg = <0xffc71000 0x1000>; +		interrupt-parent = <&gic>; +		interrupts = <0 82 0x4>; +	}; + +	i2c2: i2c@0xffc72000 { +		#address-cells = <1>; +		#size-cells = <0>; +		compatible = "renesas,rmobile-iic"; +		reg = <0xffc72000 0x1000>; +		interrupt-parent = <&gic>; +		interrupts = <0 80 0x4>; +	}; + +	i2c3: i2c@0xffc73000 { +		#address-cells = <1>; +		#size-cells = <0>; +		compatible = "renesas,rmobile-iic"; +		reg = <0xffc73000 0x1000>; +		interrupt-parent = <&gic>; +		interrupts = <0 81 0x4>; +	}; + +	thermal@ffc48000 { +		compatible = "renesas,rcar-thermal"; +		reg = <0xffc48000 0x38>; +	}; + +	sata: sata@fc600000 { +		compatible = "renesas,rcar-sata"; +		reg = <0xfc600000 0x2000>; +		interrupt-parent = <&gic>; +		interrupts = <0 100 0x4>; +	}; +}; diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig index 9255546e7bf..75d413c004b 100644 --- a/arch/arm/mach-shmobile/Kconfig +++ b/arch/arm/mach-shmobile/Kconfig @@ -16,6 +16,7 @@ config ARCH_SH73A0  	select CPU_V7  	select I2C  	select SH_CLK_CPG +	select RENESAS_INTC_IRQPIN  config ARCH_R8A7740  	bool "R-Mobile A1 (R8A77400)" @@ -31,6 +32,7 @@ config ARCH_R8A7779  	select SH_CLK_CPG  	select USB_ARCH_HAS_EHCI  	select USB_ARCH_HAS_OHCI +	select RENESAS_INTC_IRQPIN  config ARCH_EMEV2  	bool "Emma Mobile EV2" diff --git a/arch/arm/mach-shmobile/Makefile b/arch/arm/mach-shmobile/Makefile index e1fac57514b..b646ff4d742 100644 --- a/arch/arm/mach-shmobile/Makefile +++ b/arch/arm/mach-shmobile/Makefile @@ -14,10 +14,9 @@ obj-$(CONFIG_ARCH_EMEV2)	+= setup-emev2.o clock-emev2.o  # SMP objects  smp-y				:= platsmp.o headsmp.o -smp-$(CONFIG_HOTPLUG_CPU)	+= hotplug.o -smp-$(CONFIG_ARCH_SH73A0)	+= smp-sh73a0.o headsmp-sh73a0.o -smp-$(CONFIG_ARCH_R8A7779)	+= smp-r8a7779.o -smp-$(CONFIG_ARCH_EMEV2)	+= smp-emev2.o +smp-$(CONFIG_ARCH_SH73A0)	+= smp-sh73a0.o headsmp-scu.o +smp-$(CONFIG_ARCH_R8A7779)	+= smp-r8a7779.o headsmp-scu.o +smp-$(CONFIG_ARCH_EMEV2)	+= smp-emev2.o headsmp-scu.o  # IRQ objects  obj-$(CONFIG_ARCH_SH7372)	+= entry-intc.o diff --git a/arch/arm/mach-shmobile/board-kzm9g.c b/arch/arm/mach-shmobile/board-kzm9g.c index 7f3a6b7e7b7..d34d12ae496 100644 --- a/arch/arm/mach-shmobile/board-kzm9g.c +++ b/arch/arm/mach-shmobile/board-kzm9g.c @@ -81,7 +81,7 @@ static struct resource smsc9221_resources[] = {  		.flags	= IORESOURCE_MEM,  	},  	[1] = { -		.start	= intcs_evt2irq(0x260), /* IRQ3 */ +		.start	= irq_pin(3), /* IRQ3 */  		.flags	= IORESOURCE_IRQ,  	},  }; @@ -115,7 +115,7 @@ static struct resource usb_resources[] = {  		.flags	= IORESOURCE_MEM,  	},  	[1] = { -		.start	= intcs_evt2irq(0x220), /* IRQ1 */ +		.start	= irq_pin(1), /* IRQ1 */  		.flags	= IORESOURCE_IRQ,  	},  }; @@ -138,7 +138,7 @@ struct usbhs_private {  	struct renesas_usbhs_platform_info info;  }; -#define IRQ15			intcs_evt2irq(0x03e0) +#define IRQ15			irq_pin(15)  #define USB_PHY_MODE		(1 << 4)  #define USB_PHY_INT_EN		((1 << 3) | (1 << 2))  #define USB_PHY_ON		(1 << 1) @@ -563,25 +563,25 @@ static struct i2c_board_info i2c0_devices[] = {  	},  	{  		I2C_BOARD_INFO("ak8975", 0x0c), -		.irq = intcs_evt2irq(0x3380), /* IRQ28 */ +		.irq = irq_pin(28), /* IRQ28 */  	},  	{  		I2C_BOARD_INFO("adxl34x", 0x1d), -		.irq = intcs_evt2irq(0x3340), /* IRQ26 */ +		.irq = irq_pin(26), /* IRQ26 */  	},  };  static struct i2c_board_info i2c1_devices[] = {  	{  		I2C_BOARD_INFO("st1232-ts", 0x55), -		.irq = intcs_evt2irq(0x300), /* IRQ8 */ +		.irq = irq_pin(8), /* IRQ8 */  	},  };  static struct i2c_board_info i2c3_devices[] = {  	{  		I2C_BOARD_INFO("pcf8575", 0x20), -		.irq		= intcs_evt2irq(0x3260), /* IRQ19 */ +		.irq = irq_pin(19), /* IRQ19 */  		.platform_data = &pcf8575_pdata,  	},  }; diff --git a/arch/arm/mach-shmobile/clock-r8a7740.c b/arch/arm/mach-shmobile/clock-r8a7740.c index 19ce885a3b4..1feb9a2286a 100644 --- a/arch/arm/mach-shmobile/clock-r8a7740.c +++ b/arch/arm/mach-shmobile/clock-r8a7740.c @@ -593,29 +593,42 @@ static struct clk_lookup lookups[] = {  	CLKDEV_DEV_ID("sh_mobile_ceu.1",	&mstp_clks[MSTP128]),  	CLKDEV_DEV_ID("sh-sci.4",		&mstp_clks[MSTP200]), +	CLKDEV_DEV_ID("e6c80000.sci",		&mstp_clks[MSTP200]),  	CLKDEV_DEV_ID("sh-sci.3",		&mstp_clks[MSTP201]), +	CLKDEV_DEV_ID("e6c70000.sci",		&mstp_clks[MSTP201]),  	CLKDEV_DEV_ID("sh-sci.2",		&mstp_clks[MSTP202]), +	CLKDEV_DEV_ID("e6c60000.sci",		&mstp_clks[MSTP202]),  	CLKDEV_DEV_ID("sh-sci.1",		&mstp_clks[MSTP203]), +	CLKDEV_DEV_ID("e6c50000.sci",		&mstp_clks[MSTP203]),  	CLKDEV_DEV_ID("sh-sci.0",		&mstp_clks[MSTP204]), +	CLKDEV_DEV_ID("e6c40000.sci",		&mstp_clks[MSTP204]),  	CLKDEV_DEV_ID("sh-sci.8",		&mstp_clks[MSTP206]), +	CLKDEV_DEV_ID("e6c30000.sci",		&mstp_clks[MSTP206]),  	CLKDEV_DEV_ID("sh-sci.5",		&mstp_clks[MSTP207]), +	CLKDEV_DEV_ID("e6cb0000.sci",		&mstp_clks[MSTP207]),  	CLKDEV_DEV_ID("sh-dma-engine.3",	&mstp_clks[MSTP214]),  	CLKDEV_DEV_ID("sh-dma-engine.2",	&mstp_clks[MSTP216]),  	CLKDEV_DEV_ID("sh-dma-engine.1",	&mstp_clks[MSTP217]),  	CLKDEV_DEV_ID("sh-dma-engine.0",	&mstp_clks[MSTP218]),  	CLKDEV_DEV_ID("sh-sci.7",		&mstp_clks[MSTP222]), +	CLKDEV_DEV_ID("e6cd0000.sci",		&mstp_clks[MSTP222]),  	CLKDEV_DEV_ID("sh-sci.6",		&mstp_clks[MSTP230]), +	CLKDEV_DEV_ID("e6cc0000.sci",		&mstp_clks[MSTP230]),  	CLKDEV_DEV_ID("sh_cmt.10",		&mstp_clks[MSTP329]),  	CLKDEV_DEV_ID("sh_fsi2",		&mstp_clks[MSTP328]),  	CLKDEV_DEV_ID("i2c-sh_mobile.1",	&mstp_clks[MSTP323]),  	CLKDEV_DEV_ID("renesas_usbhs",		&mstp_clks[MSTP320]),  	CLKDEV_DEV_ID("sh_mobile_sdhi.0",	&mstp_clks[MSTP314]), +	CLKDEV_DEV_ID("e6850000.sdhi",          &mstp_clks[MSTP314]),  	CLKDEV_DEV_ID("sh_mobile_sdhi.1",	&mstp_clks[MSTP313]), +	CLKDEV_DEV_ID("e6860000.sdhi",          &mstp_clks[MSTP313]),  	CLKDEV_DEV_ID("sh_mmcif",		&mstp_clks[MSTP312]), +	CLKDEV_DEV_ID("e6bd0000.mmcif",         &mstp_clks[MSTP312]),  	CLKDEV_DEV_ID("sh-eth",			&mstp_clks[MSTP309]),  	CLKDEV_DEV_ID("sh_mobile_sdhi.2",	&mstp_clks[MSTP415]), +	CLKDEV_DEV_ID("e6870000.sdhi",          &mstp_clks[MSTP415]),  	/* ICK */  	CLKDEV_ICK_ID("host",	"renesas_usbhs",	&mstp_clks[MSTP416]), diff --git a/arch/arm/mach-shmobile/clock-r8a7779.c b/arch/arm/mach-shmobile/clock-r8a7779.c index 1db36537255..d9edeaf6600 100644 --- a/arch/arm/mach-shmobile/clock-r8a7779.c +++ b/arch/arm/mach-shmobile/clock-r8a7779.c @@ -87,7 +87,8 @@ static struct clk div4_clks[DIV4_NR] = {  };  enum { MSTP323, MSTP322, MSTP321, MSTP320, -	MSTP101, MSTP100, +	MSTP115, +	MSTP103, MSTP101, MSTP100,  	MSTP030,  	MSTP029, MSTP028, MSTP027, MSTP026, MSTP025, MSTP024, MSTP023, MSTP022, MSTP021,  	MSTP016, MSTP015, MSTP014, @@ -99,6 +100,8 @@ static struct clk mstp_clks[MSTP_NR] = {  	[MSTP322] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 22, 0), /* SDHI1 */  	[MSTP321] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 21, 0), /* SDHI2 */  	[MSTP320] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 20, 0), /* SDHI3 */ +	[MSTP115] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 15, 0), /* SATA */ +	[MSTP103] = SH_CLK_MSTP32(&div4_clks[DIV4_S], MSTPCR1,  3, 0), /* DU */  	[MSTP101] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1,  1, 0), /* USB2 */  	[MSTP100] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1,  0, 0), /* USB0/1 */  	[MSTP030] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 30, 0), /* I2C0 */ @@ -156,6 +159,8 @@ static struct clk_lookup lookups[] = {  	CLKDEV_CON_ID("peripheral_clk",	&div4_clks[DIV4_P]),  	/* MSTP32 clocks */ +	CLKDEV_DEV_ID("sata_rcar", &mstp_clks[MSTP115]), /* SATA */ +	CLKDEV_DEV_ID("fc600000.sata", &mstp_clks[MSTP115]), /* SATA w/DT */  	CLKDEV_DEV_ID("ehci-platform.1", &mstp_clks[MSTP101]), /* USB EHCI port2 */  	CLKDEV_DEV_ID("ohci-platform.1", &mstp_clks[MSTP101]), /* USB OHCI port2 */  	CLKDEV_DEV_ID("ehci-platform.0", &mstp_clks[MSTP100]), /* USB EHCI port0/1 */ @@ -180,6 +185,7 @@ static struct clk_lookup lookups[] = {  	CLKDEV_DEV_ID("sh_mobile_sdhi.1", &mstp_clks[MSTP322]), /* SDHI1 */  	CLKDEV_DEV_ID("sh_mobile_sdhi.2", &mstp_clks[MSTP321]), /* SDHI2 */  	CLKDEV_DEV_ID("sh_mobile_sdhi.3", &mstp_clks[MSTP320]), /* SDHI3 */ +	CLKDEV_DEV_ID("rcar-du.0", &mstp_clks[MSTP103]), /* DU */  };  void __init r8a7779_clock_init(void) diff --git a/arch/arm/mach-shmobile/clock-sh73a0.c b/arch/arm/mach-shmobile/clock-sh73a0.c index afa5423a0f9..71843dd39e1 100644 --- a/arch/arm/mach-shmobile/clock-sh73a0.c +++ b/arch/arm/mach-shmobile/clock-sh73a0.c @@ -265,12 +265,12 @@ enum { DIV4_I, DIV4_ZG, DIV4_M3, DIV4_B, DIV4_M1, DIV4_M2,  static struct clk div4_clks[DIV4_NR] = {  	[DIV4_I] = DIV4(FRQCRA, 20, 0xdff, CLK_ENABLE_ON_INIT), -	[DIV4_ZG] = DIV4(FRQCRA, 16, 0xd7f, CLK_ENABLE_ON_INIT), +	[DIV4_ZG] = SH_CLK_DIV4(&pll0_clk, FRQCRA, 16, 0xd7f, CLK_ENABLE_ON_INIT),  	[DIV4_M3] = DIV4(FRQCRA, 12, 0x1dff, CLK_ENABLE_ON_INIT),  	[DIV4_B] = DIV4(FRQCRA, 8, 0xdff, CLK_ENABLE_ON_INIT),  	[DIV4_M1] = DIV4(FRQCRA, 4, 0x1dff, 0),  	[DIV4_M2] = DIV4(FRQCRA, 0, 0x1dff, 0), -	[DIV4_Z] = DIV4(FRQCRB, 24, 0x97f, 0), +	[DIV4_Z] = SH_CLK_DIV4(&pll0_clk, FRQCRB, 24, 0x97f, 0),  	[DIV4_ZTR] = DIV4(FRQCRB, 20, 0xdff, 0),  	[DIV4_ZT] = DIV4(FRQCRB, 16, 0xdff, 0),  	[DIV4_ZX] = DIV4(FRQCRB, 12, 0xdff, 0), @@ -581,10 +581,13 @@ static struct clk_lookup lookups[] = {  	CLKDEV_DEV_ID("e6822000.i2c", &mstp_clks[MSTP323]), /* I2C1 */  	CLKDEV_DEV_ID("renesas_usbhs", &mstp_clks[MSTP322]), /* USB */  	CLKDEV_DEV_ID("sh_mobile_sdhi.0", &mstp_clks[MSTP314]), /* SDHI0 */ +	CLKDEV_DEV_ID("ee100000.sdhi", &mstp_clks[MSTP314]), /* SDHI0 */  	CLKDEV_DEV_ID("sh_mobile_sdhi.1", &mstp_clks[MSTP313]), /* SDHI1 */ +	CLKDEV_DEV_ID("ee120000.sdhi", &mstp_clks[MSTP313]), /* SDHI1 */  	CLKDEV_DEV_ID("sh_mmcif.0", &mstp_clks[MSTP312]), /* MMCIF0 */  	CLKDEV_DEV_ID("e6bd0000.mmcif", &mstp_clks[MSTP312]), /* MMCIF0 */  	CLKDEV_DEV_ID("sh_mobile_sdhi.2", &mstp_clks[MSTP311]), /* SDHI2 */ +	CLKDEV_DEV_ID("ee140000.sdhi", &mstp_clks[MSTP311]), /* SDHI2 */  	CLKDEV_DEV_ID("leds-renesas-tpu.12", &mstp_clks[MSTP303]), /* TPU1 */  	CLKDEV_DEV_ID("leds-renesas-tpu.21", &mstp_clks[MSTP302]), /* TPU2 */  	CLKDEV_DEV_ID("leds-renesas-tpu.30", &mstp_clks[MSTP301]), /* TPU3 */ diff --git a/arch/arm/mach-shmobile/headsmp-sh73a0.S b/arch/arm/mach-shmobile/headsmp-scu.S index bec4c0d9b71..7d113f898e7 100644 --- a/arch/arm/mach-shmobile/headsmp-sh73a0.S +++ b/arch/arm/mach-shmobile/headsmp-scu.S @@ -1,5 +1,5 @@  /* - * SMP support for SoC sh73a0 + * Shared SCU setup for mach-shmobile   *   * Copyright (C) 2012 Bastian Hecht   * @@ -35,11 +35,12 @@   * the physical address as the MMU is still turned off.   */  	.align	12 -ENTRY(sh73a0_secondary_vector) +ENTRY(shmobile_secondary_vector_scu)  	mrc     p15, 0, r0, c0, c0, 5	@ read MIPDR  	and	r0, r0, #3		@ mask out cpu ID  	lsl	r0, r0, #3		@ we will shift by cpu_id * 8 bits -	mov	r1, #0xf0000000		@ SCU base address +	ldr	r1, 2f +	ldr	r1, [r1]		@ SCU base address  	ldr	r2, [r1, #8]		@ SCU Power Status Register  	mov	r3, #3  	bic	r2, r2, r3, lsl r0	@ Clear bits of our CPU (Run Mode) @@ -47,4 +48,10 @@ ENTRY(sh73a0_secondary_vector)  	ldr	pc, 1f  1:	.long shmobile_invalidate_start - PAGE_OFFSET + PLAT_PHYS_OFFSET -ENDPROC(sh73a0_secondary_vector) +2:	.long shmobile_scu_base - PAGE_OFFSET + PLAT_PHYS_OFFSET +ENDPROC(shmobile_secondary_vector_scu) + +	.text +	.globl	shmobile_scu_base +shmobile_scu_base: +	.space	4 diff --git a/arch/arm/mach-shmobile/hotplug.c b/arch/arm/mach-shmobile/hotplug.c deleted file mode 100644 index a1524e3367b..00000000000 --- a/arch/arm/mach-shmobile/hotplug.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * SMP support for R-Mobile / SH-Mobile - * - * Copyright (C) 2010  Magnus Damm - * - * Based on realview, Copyright (C) 2002 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. - */ -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/smp.h> -#include <linux/cpumask.h> -#include <linux/delay.h> -#include <linux/of.h> -#include <mach/common.h> -#include <mach/r8a7779.h> -#include <mach/emev2.h> -#include <asm/cacheflush.h> -#include <asm/mach-types.h> - -static cpumask_t dead_cpus; - -void shmobile_cpu_die(unsigned int cpu) -{ -	/* hardware shutdown code running on the CPU that is being offlined */ -	flush_cache_all(); -	dsb(); - -	/* notify platform_cpu_kill() that hardware shutdown is finished */ -	cpumask_set_cpu(cpu, &dead_cpus); - -	/* wait for SoC code in platform_cpu_kill() to shut off CPU core -	 * power. CPU bring up starts from the reset vector. -	 */ -	while (1) { -		/* -		 * here's the WFI -		 */ -		asm(".word	0xe320f003\n" -		    : -		    : -		    : "memory", "cc"); -	} -} - -int shmobile_cpu_disable(unsigned int cpu) -{ -	cpumask_clear_cpu(cpu, &dead_cpus); -	/* -	 * we don't allow CPU 0 to be shutdown (it is still too special -	 * e.g. clock tick interrupts) -	 */ -	return cpu == 0 ? -EPERM : 0; -} - -int shmobile_cpu_disable_any(unsigned int cpu) -{ -	cpumask_clear_cpu(cpu, &dead_cpus); -	return 0; -} - -int shmobile_cpu_is_dead(unsigned int cpu) -{ -	return cpumask_test_cpu(cpu, &dead_cpus); -} diff --git a/arch/arm/mach-shmobile/include/mach/common.h b/arch/arm/mach-shmobile/include/mach/common.h index e48606d8a2b..03f73def2fc 100644 --- a/arch/arm/mach-shmobile/include/mach/common.h +++ b/arch/arm/mach-shmobile/include/mach/common.h @@ -8,6 +8,7 @@ extern void shmobile_setup_delay(unsigned int max_cpu_core_mhz,  struct twd_local_timer;  extern void shmobile_setup_console(void);  extern void shmobile_secondary_vector(void); +extern void shmobile_secondary_vector_scu(void);  struct clk;  extern int shmobile_clk_init(void);  extern void shmobile_handle_irq_intc(struct pt_regs *); @@ -33,23 +34,23 @@ extern int sh7372_do_idle_sysc(unsigned long sleep_mode);  extern struct clk sh7372_extal1_clk;  extern struct clk sh7372_extal2_clk; +extern void sh73a0_init_delay(void);  extern void sh73a0_init_irq(void);  extern void sh73a0_init_irq_dt(void);  extern void sh73a0_map_io(void);  extern void sh73a0_earlytimer_init(void);  extern void sh73a0_add_early_devices(void); -extern void sh73a0_add_early_devices_dt(void);  extern void sh73a0_add_standard_devices(void);  extern void sh73a0_add_standard_devices_dt(void);  extern void sh73a0_clock_init(void);  extern void sh73a0_pinmux_init(void);  extern void sh73a0_pm_init(void); -extern void sh73a0_secondary_vector(void);  extern struct clk sh73a0_extal1_clk;  extern struct clk sh73a0_extal2_clk;  extern struct clk sh73a0_extcki_clk;  extern struct clk sh73a0_extalr_clk; +extern void r8a7740_meram_workaround(void);  extern void r8a7740_init_irq(void);  extern void r8a7740_map_io(void);  extern void r8a7740_add_early_devices(void); @@ -58,16 +59,18 @@ extern void r8a7740_clock_init(u8 md_ck);  extern void r8a7740_pinmux_init(void);  extern void r8a7740_pm_init(void); +extern void r8a7779_init_delay(void);  extern void r8a7779_init_irq(void); +extern void r8a7779_init_irq_extpin(int irlm); +extern void r8a7779_init_irq_dt(void);  extern void r8a7779_map_io(void);  extern void r8a7779_earlytimer_init(void);  extern void r8a7779_add_early_devices(void);  extern void r8a7779_add_standard_devices(void); +extern void r8a7779_add_standard_devices_dt(void);  extern void r8a7779_clock_init(void);  extern void r8a7779_pinmux_init(void);  extern void r8a7779_pm_init(void); -extern void r8a7740_meram_workaround(void); -  extern void r8a7779_register_twd(void);  #ifdef CONFIG_SUSPEND @@ -82,16 +85,7 @@ int shmobile_cpuidle_init(void);  static inline int shmobile_cpuidle_init(void) { return 0; }  #endif -extern void shmobile_cpu_die(unsigned int cpu); -extern int shmobile_cpu_disable(unsigned int cpu); -extern int shmobile_cpu_disable_any(unsigned int cpu); - -#ifdef CONFIG_HOTPLUG_CPU -extern int shmobile_cpu_is_dead(unsigned int cpu); -#else -static inline int shmobile_cpu_is_dead(unsigned int cpu) { return 1; } -#endif - +extern void __iomem *shmobile_scu_base;  extern void shmobile_smp_init_cpus(unsigned int ncores);  static inline void __init shmobile_init_late(void) diff --git a/arch/arm/mach-shmobile/include/mach/irqs.h b/arch/arm/mach-shmobile/include/mach/irqs.h index 06a5da3c305..b2074e2acb1 100644 --- a/arch/arm/mach-shmobile/include/mach/irqs.h +++ b/arch/arm/mach-shmobile/include/mach/irqs.h @@ -5,10 +5,15 @@  /* GIC */  #define gic_spi(nr)		((nr) + 32) +#define gic_iid(nr)		(nr) /* ICCIAR / interrupt ID */  /* INTCS */  #define INTCS_VECT_BASE		0x3400  #define INTCS_VECT(n, vect)	INTC_VECT((n), INTCS_VECT_BASE + (vect))  #define intcs_evt2irq(evt)	evt2irq(INTCS_VECT_BASE + (evt)) +/* External IRQ pins */ +#define IRQPIN_BASE		2000 +#define irq_pin(nr)		((nr) + IRQPIN_BASE) +  #endif /* __ASM_MACH_IRQS_H */ diff --git a/arch/arm/mach-shmobile/intc-r8a7779.c b/arch/arm/mach-shmobile/intc-r8a7779.c index 8807c27f71f..b86dc890872 100644 --- a/arch/arm/mach-shmobile/intc-r8a7779.c +++ b/arch/arm/mach-shmobile/intc-r8a7779.c @@ -19,12 +19,16 @@   */  #include <linux/kernel.h>  #include <linux/init.h> +#include <linux/platform_device.h>  #include <linux/interrupt.h>  #include <linux/irq.h>  #include <linux/io.h>  #include <linux/irqchip/arm-gic.h> +#include <linux/platform_data/irq-renesas-intc-irqpin.h> +#include <linux/irqchip.h>  #include <mach/common.h>  #include <mach/intc.h> +#include <mach/irqs.h>  #include <mach/r8a7779.h>  #include <asm/mach-types.h>  #include <asm/mach/arch.h> @@ -38,18 +42,61 @@  #define INT2NTSR0 IOMEM(0xfe700060)  #define INT2NTSR1 IOMEM(0xfe700064) +static struct renesas_intc_irqpin_config irqpin0_platform_data = { +	.irq_base = irq_pin(0), /* IRQ0 -> IRQ3 */ +	.sense_bitfield_width = 2, +}; + +static struct resource irqpin0_resources[] = { +	DEFINE_RES_MEM(0xfe78001c, 4), /* ICR1 */ +	DEFINE_RES_MEM(0xfe780010, 4), /* INTPRI */ +	DEFINE_RES_MEM(0xfe780024, 4), /* INTREQ */ +	DEFINE_RES_MEM(0xfe780044, 4), /* INTMSK0 */ +	DEFINE_RES_MEM(0xfe780064, 4), /* INTMSKCLR0 */ +	DEFINE_RES_IRQ(gic_spi(27)), /* IRQ0 */ +	DEFINE_RES_IRQ(gic_spi(28)), /* IRQ1 */ +	DEFINE_RES_IRQ(gic_spi(29)), /* IRQ2 */ +	DEFINE_RES_IRQ(gic_spi(30)), /* IRQ3 */ +}; + +static struct platform_device irqpin0_device = { +	.name		= "renesas_intc_irqpin", +	.id		= 0, +	.resource	= irqpin0_resources, +	.num_resources	= ARRAY_SIZE(irqpin0_resources), +	.dev		= { +		.platform_data	= &irqpin0_platform_data, +	}, +}; + +void __init r8a7779_init_irq_extpin(int irlm) +{ +	void __iomem *icr0 = ioremap_nocache(0xfe780000, PAGE_SIZE); +	unsigned long tmp; + +	if (icr0) { +		tmp = ioread32(icr0); +		if (irlm) +			tmp |= 1 << 23; /* IRQ0 -> IRQ3 as individual pins */ +		else +			tmp &= ~(1 << 23); /* IRL mode - not supported */ +		tmp |= (1 << 21); /* LVLMODE = 1 */ +		iowrite32(tmp, icr0); +		iounmap(icr0); + +		if (irlm) +			platform_device_register(&irqpin0_device); +	} else +		pr_warn("r8a7779: unable to setup external irq pin mode\n"); +} +  static int r8a7779_set_wake(struct irq_data *data, unsigned int on)  {  	return 0; /* always allow wakeup */  } -void __init r8a7779_init_irq(void) +static void __init r8a7779_init_irq_common(void)  { -	void __iomem *gic_dist_base = IOMEM(0xf0001000); -	void __iomem *gic_cpu_base = IOMEM(0xf0000100); - -	/* use GIC to handle interrupts */ -	gic_init(0, 29, gic_dist_base, gic_cpu_base);  	gic_arch_extn.irq_set_wake = r8a7779_set_wake;  	/* route all interrupts to ARM */ @@ -63,3 +110,22 @@ void __init r8a7779_init_irq(void)  	__raw_writel(0xbffffffc, INT2SMSKCR3);  	__raw_writel(0x003fee3f, INT2SMSKCR4);  } + +void __init r8a7779_init_irq(void) +{ +	void __iomem *gic_dist_base = IOMEM(0xf0001000); +	void __iomem *gic_cpu_base = IOMEM(0xf0000100); + +	/* use GIC to handle interrupts */ +	gic_init(0, 29, gic_dist_base, gic_cpu_base); + +	r8a7779_init_irq_common(); +} + +#ifdef CONFIG_OF +void __init r8a7779_init_irq_dt(void) +{ +	irqchip_init(); +	r8a7779_init_irq_common(); +} +#endif diff --git a/arch/arm/mach-shmobile/intc-sh73a0.c b/arch/arm/mach-shmobile/intc-sh73a0.c index 91faba666d4..19a26f4579b 100644 --- a/arch/arm/mach-shmobile/intc-sh73a0.c +++ b/arch/arm/mach-shmobile/intc-sh73a0.c @@ -260,108 +260,6 @@ static int sh73a0_set_wake(struct irq_data *data, unsigned int on)  	return 0; /* always allow wakeup */  } -#define RELOC_BASE 0x1200 - -/* INTCA IRQ pins at INTCS + RELOC_BASE to make space for GIC+INTC handling */ -#define INTCS_VECT_RELOC(n, vect) INTCS_VECT((n), (vect) + RELOC_BASE) - -INTC_IRQ_PINS_32(intca_irq_pins, 0xe6900000, -		 INTCS_VECT_RELOC, "sh73a0-intca-irq-pins"); - -static int to_gic_irq(struct irq_data *data) -{ -	unsigned int vect = irq2evt(data->irq) - INTCS_VECT_BASE; - -	if (vect >= 0x3200) -		vect -= 0x3000; -	else -		vect -= 0x0200; - -	return gic_spi((vect >> 5) + 1); -} - -static int to_intca_reloc_irq(struct irq_data *data) -{ -	return data->irq + (RELOC_BASE >> 5); -} - -#define irq_cb(cb, irq) irq_get_chip(irq)->cb(irq_get_irq_data(irq)) -#define irq_cbp(cb, irq, p...) irq_get_chip(irq)->cb(irq_get_irq_data(irq), p) - -static void intca_gic_enable(struct irq_data *data) -{ -	irq_cb(irq_unmask, to_intca_reloc_irq(data)); -	irq_cb(irq_unmask, to_gic_irq(data)); -} - -static void intca_gic_disable(struct irq_data *data) -{ -	irq_cb(irq_mask, to_gic_irq(data)); -	irq_cb(irq_mask, to_intca_reloc_irq(data)); -} - -static void intca_gic_mask_ack(struct irq_data *data) -{ -	irq_cb(irq_mask, to_gic_irq(data)); -	irq_cb(irq_mask_ack, to_intca_reloc_irq(data)); -} - -static void intca_gic_eoi(struct irq_data *data) -{ -	irq_cb(irq_eoi, to_gic_irq(data)); -} - -static int intca_gic_set_type(struct irq_data *data, unsigned int type) -{ -	return irq_cbp(irq_set_type, to_intca_reloc_irq(data), type); -} - -#ifdef CONFIG_SMP -static int intca_gic_set_affinity(struct irq_data *data, -				  const struct cpumask *cpumask, -				  bool force) -{ -	return irq_cbp(irq_set_affinity, to_gic_irq(data), cpumask, force); -} -#endif - -struct irq_chip intca_gic_irq_chip = { -	.name			= "INTCA-GIC", -	.irq_mask		= intca_gic_disable, -	.irq_unmask		= intca_gic_enable, -	.irq_mask_ack		= intca_gic_mask_ack, -	.irq_eoi		= intca_gic_eoi, -	.irq_enable		= intca_gic_enable, -	.irq_disable		= intca_gic_disable, -	.irq_shutdown		= intca_gic_disable, -	.irq_set_type		= intca_gic_set_type, -	.irq_set_wake		= sh73a0_set_wake, -#ifdef CONFIG_SMP -	.irq_set_affinity	= intca_gic_set_affinity, -#endif -}; - -static int to_intc_vect(int irq) -{ -	unsigned int irq_pin = irq - gic_spi(1); -	unsigned int offs; - -	if (irq_pin < 16) -		offs = 0x0200; -	else -		offs = 0x3000; - -	return offs + (irq_pin << 5); -} - -static irqreturn_t sh73a0_irq_pin_demux(int irq, void *dev_id) -{ -	generic_handle_irq(intcs_evt2irq(to_intc_vect(irq))); -	return IRQ_HANDLED; -} - -static struct irqaction sh73a0_irq_pin_cascade[32]; -  #define PINTER0_PHYS 0xe69000a0  #define PINTER1_PHYS 0xe69000a4  #define PINTER0_VIRT IOMEM(0xe69000a0) @@ -422,13 +320,11 @@ void __init sh73a0_init_irq(void)  	void __iomem *gic_dist_base = IOMEM(0xf0001000);  	void __iomem *gic_cpu_base = IOMEM(0xf0000100);  	void __iomem *intevtsa = ioremap_nocache(0xffd20100, PAGE_SIZE); -	int k, n;  	gic_init(0, 29, gic_dist_base, gic_cpu_base);  	gic_arch_extn.irq_set_wake = sh73a0_set_wake;  	register_intc_controller(&intcs_desc); -	register_intc_controller(&intca_irq_pins_desc);  	register_intc_controller(&intc_pint0_desc);  	register_intc_controller(&intc_pint1_desc); @@ -438,19 +334,6 @@ void __init sh73a0_init_irq(void)  	sh73a0_intcs_cascade.dev_id = intevtsa;  	setup_irq(gic_spi(50), &sh73a0_intcs_cascade); -	/* IRQ pins require special handling through INTCA and GIC */ -	for (k = 0; k < 32; k++) { -		sh73a0_irq_pin_cascade[k].name = "INTCA-GIC cascade"; -		sh73a0_irq_pin_cascade[k].handler = sh73a0_irq_pin_demux; -		setup_irq(gic_spi(1 + k), &sh73a0_irq_pin_cascade[k]); - -		n = intcs_evt2irq(to_intc_vect(gic_spi(1 + k))); -		WARN_ON(irq_alloc_desc_at(n, numa_node_id()) != n); -		irq_set_chip_and_handler_name(n, &intca_gic_irq_chip, -					      handle_level_irq, "level"); -		set_irq_flags(n, IRQF_VALID); /* yuck */ -	} -  	/* PINT pins are sanely tied to the GIC as SPI */  	sh73a0_pint0_cascade.name = "PINT0 cascade";  	sh73a0_pint0_cascade.handler = sh73a0_pint0_demux; @@ -460,11 +343,3 @@ void __init sh73a0_init_irq(void)  	sh73a0_pint1_cascade.handler = sh73a0_pint1_demux;  	setup_irq(gic_spi(34), &sh73a0_pint1_cascade);  } - -#ifdef CONFIG_OF -void __init sh73a0_init_irq_dt(void) -{ -	irqchip_init(); -	gic_arch_extn.irq_set_wake = sh73a0_set_wake; -} -#endif diff --git a/arch/arm/mach-shmobile/setup-emev2.c b/arch/arm/mach-shmobile/setup-emev2.c index 47662a581c0..e4545c15272 100644 --- a/arch/arm/mach-shmobile/setup-emev2.c +++ b/arch/arm/mach-shmobile/setup-emev2.c @@ -404,7 +404,7 @@ void __init emev2_add_standard_devices(void)  			     ARRAY_SIZE(emev2_late_devices));  } -void __init emev2_init_delay(void) +static void __init emev2_init_delay(void)  {  	shmobile_setup_delay(533, 1, 3); /* Cortex-A9 @ 533MHz */  } @@ -439,7 +439,7 @@ static const struct of_dev_auxdata emev2_auxdata_lookup[] __initconst = {  	{ }  }; -void __init emev2_add_standard_devices_dt(void) +static void __init emev2_add_standard_devices_dt(void)  {  	of_platform_populate(NULL, of_default_bus_match_table,  			     emev2_auxdata_lookup, NULL); diff --git a/arch/arm/mach-shmobile/setup-r8a7779.c b/arch/arm/mach-shmobile/setup-r8a7779.c index c54ff9b29fe..042df35e71a 100644 --- a/arch/arm/mach-shmobile/setup-r8a7779.c +++ b/arch/arm/mach-shmobile/setup-r8a7779.c @@ -21,6 +21,7 @@  #include <linux/init.h>  #include <linux/interrupt.h>  #include <linux/irq.h> +#include <linux/of_platform.h>  #include <linux/platform_device.h>  #include <linux/delay.h>  #include <linux/input.h> @@ -28,6 +29,7 @@  #include <linux/serial_sci.h>  #include <linux/sh_intc.h>  #include <linux/sh_timer.h> +#include <linux/dma-mapping.h>  #include <mach/hardware.h>  #include <mach/irqs.h>  #include <mach/r8a7779.h> @@ -91,7 +93,7 @@ static struct plat_sci_port scif0_platform_data = {  	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_CKE1,  	.scbrr_algo_id	= SCBRR_ALGO_2,  	.type		= PORT_SCIF, -	.irqs		= SCIx_IRQ_MUXED(gic_spi(88)), +	.irqs		= SCIx_IRQ_MUXED(gic_iid(0x78)),  };  static struct platform_device scif0_device = { @@ -108,7 +110,7 @@ static struct plat_sci_port scif1_platform_data = {  	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_CKE1,  	.scbrr_algo_id	= SCBRR_ALGO_2,  	.type		= PORT_SCIF, -	.irqs		= SCIx_IRQ_MUXED(gic_spi(89)), +	.irqs		= SCIx_IRQ_MUXED(gic_iid(0x79)),  };  static struct platform_device scif1_device = { @@ -125,7 +127,7 @@ static struct plat_sci_port scif2_platform_data = {  	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_CKE1,  	.scbrr_algo_id	= SCBRR_ALGO_2,  	.type		= PORT_SCIF, -	.irqs		= SCIx_IRQ_MUXED(gic_spi(90)), +	.irqs		= SCIx_IRQ_MUXED(gic_iid(0x7a)),  };  static struct platform_device scif2_device = { @@ -142,7 +144,7 @@ static struct plat_sci_port scif3_platform_data = {  	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_CKE1,  	.scbrr_algo_id	= SCBRR_ALGO_2,  	.type		= PORT_SCIF, -	.irqs		= SCIx_IRQ_MUXED(gic_spi(91)), +	.irqs		= SCIx_IRQ_MUXED(gic_iid(0x7b)),  };  static struct platform_device scif3_device = { @@ -159,7 +161,7 @@ static struct plat_sci_port scif4_platform_data = {  	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_CKE1,  	.scbrr_algo_id	= SCBRR_ALGO_2,  	.type		= PORT_SCIF, -	.irqs		= SCIx_IRQ_MUXED(gic_spi(92)), +	.irqs		= SCIx_IRQ_MUXED(gic_iid(0x7c)),  };  static struct platform_device scif4_device = { @@ -176,7 +178,7 @@ static struct plat_sci_port scif5_platform_data = {  	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_CKE1,  	.scbrr_algo_id	= SCBRR_ALGO_2,  	.type		= PORT_SCIF, -	.irqs		= SCIx_IRQ_MUXED(gic_spi(93)), +	.irqs		= SCIx_IRQ_MUXED(gic_iid(0x7d)),  };  static struct platform_device scif5_device = { @@ -203,7 +205,7 @@ static struct resource tmu00_resources[] = {  		.flags	= IORESOURCE_MEM,  	},  	[1] = { -		.start	= gic_spi(32), +		.start	= gic_iid(0x40),  		.flags	= IORESOURCE_IRQ,  	},  }; @@ -233,7 +235,7 @@ static struct resource tmu01_resources[] = {  		.flags	= IORESOURCE_MEM,  	},  	[1] = { -		.start	= gic_spi(33), +		.start	= gic_iid(0x41),  		.flags	= IORESOURCE_IRQ,  	},  }; @@ -255,7 +257,7 @@ static struct resource rcar_i2c0_res[] = {  		.end    = 0xffc70fff,  		.flags  = IORESOURCE_MEM,  	}, { -		.start  = gic_spi(79), +		.start  = gic_iid(0x6f),  		.flags  = IORESOURCE_IRQ,  	},  }; @@ -273,7 +275,7 @@ static struct resource rcar_i2c1_res[] = {  		.end    = 0xffc71fff,  		.flags  = IORESOURCE_MEM,  	}, { -		.start  = gic_spi(82), +		.start  = gic_iid(0x72),  		.flags  = IORESOURCE_IRQ,  	},  }; @@ -291,7 +293,7 @@ static struct resource rcar_i2c2_res[] = {  		.end    = 0xffc72fff,  		.flags  = IORESOURCE_MEM,  	}, { -		.start  = gic_spi(80), +		.start  = gic_iid(0x70),  		.flags  = IORESOURCE_IRQ,  	},  }; @@ -309,7 +311,7 @@ static struct resource rcar_i2c3_res[] = {  		.end    = 0xffc73fff,  		.flags  = IORESOURCE_MEM,  	}, { -		.start  = gic_spi(81), +		.start  = gic_iid(0x71),  		.flags  = IORESOURCE_IRQ,  	},  }; @@ -321,7 +323,31 @@ static struct platform_device i2c3_device = {  	.num_resources	= ARRAY_SIZE(rcar_i2c3_res),  }; -static struct platform_device *r8a7779_early_devices[] __initdata = { +static struct resource sata_resources[] = { +	[0] = { +		.name	= "rcar-sata", +		.start	= 0xfc600000, +		.end	= 0xfc601fff, +		.flags	= IORESOURCE_MEM, +	}, +	[1] = { +		.start	= gic_iid(0x84), +		.flags	= IORESOURCE_IRQ, +	}, +}; + +static struct platform_device sata_device = { +	.name		= "sata_rcar", +	.id		= -1, +	.resource	= sata_resources, +	.num_resources	= ARRAY_SIZE(sata_resources), +	.dev		= { +		.dma_mask		= &sata_device.dev.coherent_dma_mask, +		.coherent_dma_mask	= DMA_BIT_MASK(32), +	}, +}; + +static struct platform_device *r8a7779_devices_dt[] __initdata = {  	&scif0_device,  	&scif1_device,  	&scif2_device, @@ -330,13 +356,14 @@ static struct platform_device *r8a7779_early_devices[] __initdata = {  	&scif5_device,  	&tmu00_device,  	&tmu01_device, +}; + +static struct platform_device *r8a7779_late_devices[] __initdata = {  	&i2c0_device,  	&i2c1_device,  	&i2c2_device,  	&i2c3_device, -}; - -static struct platform_device *r8a7779_late_devices[] __initdata = { +	&sata_device,  };  void __init r8a7779_add_standard_devices(void) @@ -349,8 +376,8 @@ void __init r8a7779_add_standard_devices(void)  	r8a7779_init_pm_domains(); -	platform_add_devices(r8a7779_early_devices, -			    ARRAY_SIZE(r8a7779_early_devices)); +	platform_add_devices(r8a7779_devices_dt, +			    ARRAY_SIZE(r8a7779_devices_dt));  	platform_add_devices(r8a7779_late_devices,  			    ARRAY_SIZE(r8a7779_late_devices));  } @@ -367,8 +394,8 @@ void __init r8a7779_earlytimer_init(void)  void __init r8a7779_add_early_devices(void)  { -	early_platform_add_devices(r8a7779_early_devices, -				   ARRAY_SIZE(r8a7779_early_devices)); +	early_platform_add_devices(r8a7779_devices_dt, +				   ARRAY_SIZE(r8a7779_devices_dt));  	/* Early serial console setup is not included here due to  	 * memory map collisions. The SCIF serial ports in r8a7779 @@ -386,3 +413,40 @@ void __init r8a7779_add_early_devices(void)  	 * command line in case of the marzen board.  	 */  } + +#ifdef CONFIG_USE_OF +void __init r8a7779_init_delay(void) +{ +	shmobile_setup_delay(1000, 2, 4); /* Cortex-A9 @ 1000MHz */ +} + +static const struct of_dev_auxdata r8a7779_auxdata_lookup[] __initconst = { +	{}, +}; + +void __init r8a7779_add_standard_devices_dt(void) +{ +	/* clocks are setup late during boot in the case of DT */ +	r8a7779_clock_init(); + +	platform_add_devices(r8a7779_devices_dt, +			     ARRAY_SIZE(r8a7779_devices_dt)); +	of_platform_populate(NULL, of_default_bus_match_table, +			     r8a7779_auxdata_lookup, NULL); +} + +static const char *r8a7779_compat_dt[] __initdata = { +	"renesas,r8a7779", +	NULL, +}; + +DT_MACHINE_START(R8A7779_DT, "Generic R8A7779 (Flattened Device Tree)") +	.map_io		= r8a7779_map_io, +	.init_early	= r8a7779_init_delay, +	.nr_irqs	= NR_IRQS_LEGACY, +	.init_irq	= r8a7779_init_irq_dt, +	.init_machine	= r8a7779_add_standard_devices_dt, +	.init_time	= shmobile_timer_init, +	.dt_compat	= r8a7779_compat_dt, +MACHINE_END +#endif /* CONFIG_USE_OF */ diff --git a/arch/arm/mach-shmobile/setup-sh73a0.c b/arch/arm/mach-shmobile/setup-sh73a0.c index bdab575f88b..e8cd93a5c55 100644 --- a/arch/arm/mach-shmobile/setup-sh73a0.c +++ b/arch/arm/mach-shmobile/setup-sh73a0.c @@ -22,6 +22,7 @@  #include <linux/init.h>  #include <linux/interrupt.h>  #include <linux/irq.h> +#include <linux/irqchip.h>  #include <linux/platform_device.h>  #include <linux/of_platform.h>  #include <linux/delay.h> @@ -32,6 +33,7 @@  #include <linux/sh_intc.h>  #include <linux/sh_timer.h>  #include <linux/platform_data/sh_ipmmu.h> +#include <linux/platform_data/irq-renesas-intc-irqpin.h>  #include <mach/dma-register.h>  #include <mach/hardware.h>  #include <mach/irqs.h> @@ -810,7 +812,128 @@ static struct platform_device ipmmu_device = {  	.num_resources  = ARRAY_SIZE(ipmmu_resources),  }; -static struct platform_device *sh73a0_early_devices_dt[] __initdata = { +static struct renesas_intc_irqpin_config irqpin0_platform_data = { +	.irq_base = irq_pin(0), /* IRQ0 -> IRQ7 */ +}; + +static struct resource irqpin0_resources[] = { +	DEFINE_RES_MEM(0xe6900000, 4), /* ICR1A */ +	DEFINE_RES_MEM(0xe6900010, 4), /* INTPRI00A */ +	DEFINE_RES_MEM(0xe6900020, 1), /* INTREQ00A */ +	DEFINE_RES_MEM(0xe6900040, 1), /* INTMSK00A */ +	DEFINE_RES_MEM(0xe6900060, 1), /* INTMSKCLR00A */ +	DEFINE_RES_IRQ(gic_spi(1)), /* IRQ0 */ +	DEFINE_RES_IRQ(gic_spi(2)), /* IRQ1 */ +	DEFINE_RES_IRQ(gic_spi(3)), /* IRQ2 */ +	DEFINE_RES_IRQ(gic_spi(4)), /* IRQ3 */ +	DEFINE_RES_IRQ(gic_spi(5)), /* IRQ4 */ +	DEFINE_RES_IRQ(gic_spi(6)), /* IRQ5 */ +	DEFINE_RES_IRQ(gic_spi(7)), /* IRQ6 */ +	DEFINE_RES_IRQ(gic_spi(8)), /* IRQ7 */ +}; + +static struct platform_device irqpin0_device = { +	.name		= "renesas_intc_irqpin", +	.id		= 0, +	.resource	= irqpin0_resources, +	.num_resources	= ARRAY_SIZE(irqpin0_resources), +	.dev		= { +		.platform_data	= &irqpin0_platform_data, +	}, +}; + +static struct renesas_intc_irqpin_config irqpin1_platform_data = { +	.irq_base = irq_pin(8), /* IRQ8 -> IRQ15 */ +	.control_parent = true, /* Disable spurious IRQ10 */ +}; + +static struct resource irqpin1_resources[] = { +	DEFINE_RES_MEM(0xe6900004, 4), /* ICR2A */ +	DEFINE_RES_MEM(0xe6900014, 4), /* INTPRI10A */ +	DEFINE_RES_MEM(0xe6900024, 1), /* INTREQ10A */ +	DEFINE_RES_MEM(0xe6900044, 1), /* INTMSK10A */ +	DEFINE_RES_MEM(0xe6900064, 1), /* INTMSKCLR10A */ +	DEFINE_RES_IRQ(gic_spi(9)), /* IRQ8 */ +	DEFINE_RES_IRQ(gic_spi(10)), /* IRQ9 */ +	DEFINE_RES_IRQ(gic_spi(11)), /* IRQ10 */ +	DEFINE_RES_IRQ(gic_spi(12)), /* IRQ11 */ +	DEFINE_RES_IRQ(gic_spi(13)), /* IRQ12 */ +	DEFINE_RES_IRQ(gic_spi(14)), /* IRQ13 */ +	DEFINE_RES_IRQ(gic_spi(15)), /* IRQ14 */ +	DEFINE_RES_IRQ(gic_spi(16)), /* IRQ15 */ +}; + +static struct platform_device irqpin1_device = { +	.name		= "renesas_intc_irqpin", +	.id		= 1, +	.resource	= irqpin1_resources, +	.num_resources	= ARRAY_SIZE(irqpin1_resources), +	.dev		= { +		.platform_data	= &irqpin1_platform_data, +	}, +}; + +static struct renesas_intc_irqpin_config irqpin2_platform_data = { +	.irq_base = irq_pin(16), /* IRQ16 -> IRQ23 */ +}; + +static struct resource irqpin2_resources[] = { +	DEFINE_RES_MEM(0xe6900008, 4), /* ICR3A */ +	DEFINE_RES_MEM(0xe6900018, 4), /* INTPRI20A */ +	DEFINE_RES_MEM(0xe6900028, 1), /* INTREQ20A */ +	DEFINE_RES_MEM(0xe6900048, 1), /* INTMSK20A */ +	DEFINE_RES_MEM(0xe6900068, 1), /* INTMSKCLR20A */ +	DEFINE_RES_IRQ(gic_spi(17)), /* IRQ16 */ +	DEFINE_RES_IRQ(gic_spi(18)), /* IRQ17 */ +	DEFINE_RES_IRQ(gic_spi(19)), /* IRQ18 */ +	DEFINE_RES_IRQ(gic_spi(20)), /* IRQ19 */ +	DEFINE_RES_IRQ(gic_spi(21)), /* IRQ20 */ +	DEFINE_RES_IRQ(gic_spi(22)), /* IRQ21 */ +	DEFINE_RES_IRQ(gic_spi(23)), /* IRQ22 */ +	DEFINE_RES_IRQ(gic_spi(24)), /* IRQ23 */ +}; + +static struct platform_device irqpin2_device = { +	.name		= "renesas_intc_irqpin", +	.id		= 2, +	.resource	= irqpin2_resources, +	.num_resources	= ARRAY_SIZE(irqpin2_resources), +	.dev		= { +		.platform_data	= &irqpin2_platform_data, +	}, +}; + +static struct renesas_intc_irqpin_config irqpin3_platform_data = { +	.irq_base = irq_pin(24), /* IRQ24 -> IRQ31 */ +}; + +static struct resource irqpin3_resources[] = { +	DEFINE_RES_MEM(0xe690000c, 4), /* ICR4A */ +	DEFINE_RES_MEM(0xe690001c, 4), /* INTPRI30A */ +	DEFINE_RES_MEM(0xe690002c, 1), /* INTREQ30A */ +	DEFINE_RES_MEM(0xe690004c, 1), /* INTMSK30A */ +	DEFINE_RES_MEM(0xe690006c, 1), /* INTMSKCLR30A */ +	DEFINE_RES_IRQ(gic_spi(25)), /* IRQ24 */ +	DEFINE_RES_IRQ(gic_spi(26)), /* IRQ25 */ +	DEFINE_RES_IRQ(gic_spi(27)), /* IRQ26 */ +	DEFINE_RES_IRQ(gic_spi(28)), /* IRQ27 */ +	DEFINE_RES_IRQ(gic_spi(29)), /* IRQ28 */ +	DEFINE_RES_IRQ(gic_spi(30)), /* IRQ29 */ +	DEFINE_RES_IRQ(gic_spi(31)), /* IRQ30 */ +	DEFINE_RES_IRQ(gic_spi(32)), /* IRQ31 */ +}; + +static struct platform_device irqpin3_device = { +	.name		= "renesas_intc_irqpin", +	.id		= 3, +	.resource	= irqpin3_resources, +	.num_resources	= ARRAY_SIZE(irqpin3_resources), +	.dev		= { +		.platform_data	= &irqpin3_platform_data, +	}, +}; + +static struct platform_device *sh73a0_devices_dt[] __initdata = {  	&scif0_device,  	&scif1_device,  	&scif2_device, @@ -838,6 +961,10 @@ static struct platform_device *sh73a0_late_devices[] __initdata = {  	&dma0_device,  	&mpdma0_device,  	&pmu_device, +	&irqpin0_device, +	&irqpin1_device, +	&irqpin2_device, +	&irqpin3_device,  };  #define SRCR2          IOMEM(0xe61580b0) @@ -847,8 +974,8 @@ void __init sh73a0_add_standard_devices(void)  	/* Clear software reset bit on SY-DMAC module */  	__raw_writel(__raw_readl(SRCR2) & ~(1 << 18), SRCR2); -	platform_add_devices(sh73a0_early_devices_dt, -			    ARRAY_SIZE(sh73a0_early_devices_dt)); +	platform_add_devices(sh73a0_devices_dt, +			    ARRAY_SIZE(sh73a0_devices_dt));  	platform_add_devices(sh73a0_early_devices,  			    ARRAY_SIZE(sh73a0_early_devices));  	platform_add_devices(sh73a0_late_devices, @@ -867,8 +994,8 @@ void __init sh73a0_earlytimer_init(void)  void __init sh73a0_add_early_devices(void)  { -	early_platform_add_devices(sh73a0_early_devices_dt, -				   ARRAY_SIZE(sh73a0_early_devices_dt)); +	early_platform_add_devices(sh73a0_devices_dt, +				   ARRAY_SIZE(sh73a0_devices_dt));  	early_platform_add_devices(sh73a0_early_devices,  				   ARRAY_SIZE(sh73a0_early_devices)); @@ -878,23 +1005,9 @@ void __init sh73a0_add_early_devices(void)  #ifdef CONFIG_USE_OF -/* Please note that the clock initialisation shcheme used in - * sh73a0_add_early_devices_dt() and sh73a0_add_standard_devices_dt() - * does not work with SMP as there is a yet to be resolved lock-up in - * workqueue initialisation. - * - * CONFIG_SMP should be disabled when using this code. - */ - -void __init sh73a0_add_early_devices_dt(void) +void __init sh73a0_init_delay(void)  {  	shmobile_setup_delay(1196, 44, 46); /* Cortex-A9 @ 1196MHz */ - -	early_platform_add_devices(sh73a0_early_devices_dt, -				   ARRAY_SIZE(sh73a0_early_devices_dt)); - -	/* setup early console here as well */ -	shmobile_setup_console();  }  static const struct of_dev_auxdata sh73a0_auxdata_lookup[] __initconst = { @@ -906,8 +1019,8 @@ void __init sh73a0_add_standard_devices_dt(void)  	/* clocks are setup late during boot in the case of DT */  	sh73a0_clock_init(); -	platform_add_devices(sh73a0_early_devices_dt, -			     ARRAY_SIZE(sh73a0_early_devices_dt)); +	platform_add_devices(sh73a0_devices_dt, +			     ARRAY_SIZE(sh73a0_devices_dt));  	of_platform_populate(NULL, of_default_bus_match_table,  			     sh73a0_auxdata_lookup, NULL);  } @@ -918,10 +1031,11 @@ static const char *sh73a0_boards_compat_dt[] __initdata = {  };  DT_MACHINE_START(SH73A0_DT, "Generic SH73A0 (Flattened Device Tree)") +	.smp		= smp_ops(sh73a0_smp_ops),  	.map_io		= sh73a0_map_io, -	.init_early	= sh73a0_add_early_devices_dt, +	.init_early	= sh73a0_init_delay,  	.nr_irqs	= NR_IRQS_LEGACY, -	.init_irq	= sh73a0_init_irq_dt, +	.init_irq	= irqchip_init,  	.init_machine	= sh73a0_add_standard_devices_dt,  	.init_time	= shmobile_timer_init,  	.dt_compat	= sh73a0_boards_compat_dt, diff --git a/arch/arm/mach-shmobile/smp-emev2.c b/arch/arm/mach-shmobile/smp-emev2.c index 953eb1f9388..8225c16b371 100644 --- a/arch/arm/mach-shmobile/smp-emev2.c +++ b/arch/arm/mach-shmobile/smp-emev2.c @@ -28,63 +28,9 @@  #include <mach/emev2.h>  #include <asm/smp_plat.h>  #include <asm/smp_scu.h> -#include <asm/cacheflush.h>  #define EMEV2_SCU_BASE 0x1e000000 -static DEFINE_SPINLOCK(scu_lock); -static void __iomem *scu_base; - -static void modify_scu_cpu_psr(unsigned long set, unsigned long clr) -{ -	unsigned long tmp; - -	/* we assume this code is running on a different cpu -	 * than the one that is changing coherency setting */ -	spin_lock(&scu_lock); -	tmp = readl(scu_base + 8); -	tmp &= ~clr; -	tmp |= set; -	writel(tmp, scu_base + 8); -	spin_unlock(&scu_lock); - -} - -static unsigned int __init emev2_get_core_count(void) -{ -	if (!scu_base) { -		scu_base = ioremap(EMEV2_SCU_BASE, PAGE_SIZE); -		emev2_clock_init(); /* need ioremapped SMU */ -	} - -	WARN_ON_ONCE(!scu_base); - -	return scu_base ? scu_get_core_count(scu_base) : 1; -} - -static int emev2_platform_cpu_kill(unsigned int cpu) -{ -	return 0; /* not supported yet */ -} - -static int __maybe_unused emev2_cpu_kill(unsigned int cpu) -{ -	int k; - -	/* this function is running on another CPU than the offline target, -	 * here we need wait for shutdown code in platform_cpu_die() to -	 * finish before asking SoC-specific code to power off the CPU core. -	 */ -	for (k = 0; k < 1000; k++) { -		if (shmobile_cpu_is_dead(cpu)) -			return emev2_platform_cpu_kill(cpu); -		mdelay(1); -	} - -	return 0; -} - -  static void __cpuinit emev2_secondary_init(unsigned int cpu)  {  	gic_secondary_init(0); @@ -92,31 +38,30 @@ static void __cpuinit emev2_secondary_init(unsigned int cpu)  static int __cpuinit emev2_boot_secondary(unsigned int cpu, struct task_struct *idle)  { -	cpu = cpu_logical_map(cpu); - -	/* enable cache coherency */ -	modify_scu_cpu_psr(0, 3 << (cpu * 8)); - -	/* Tell ROM loader about our vector (in headsmp.S) */ -	emev2_set_boot_vector(__pa(shmobile_secondary_vector)); - -	arch_send_wakeup_ipi_mask(cpumask_of(cpu)); +	arch_send_wakeup_ipi_mask(cpumask_of(cpu_logical_map(cpu)));  	return 0;  }  static void __init emev2_smp_prepare_cpus(unsigned int max_cpus)  { -	int cpu = cpu_logical_map(0); +	scu_enable(shmobile_scu_base); -	scu_enable(scu_base); +	/* Tell ROM loader about our vector (in headsmp-scu.S) */ +	emev2_set_boot_vector(__pa(shmobile_secondary_vector_scu)); -	/* enable cache coherency on CPU0 */ -	modify_scu_cpu_psr(0, 3 << (cpu * 8)); +	/* enable cache coherency on booting CPU */ +	scu_power_mode(shmobile_scu_base, SCU_PM_NORMAL);  }  static void __init emev2_smp_init_cpus(void)  { -	unsigned int ncores = emev2_get_core_count(); +	unsigned int ncores; + +	/* setup EMEV2 specific SCU base */ +	shmobile_scu_base = ioremap(EMEV2_SCU_BASE, PAGE_SIZE); +	emev2_clock_init(); /* need ioremapped SMU */ + +	ncores = shmobile_scu_base ? scu_get_core_count(shmobile_scu_base) : 1;  	shmobile_smp_init_cpus(ncores);  } @@ -126,9 +71,4 @@ struct smp_operations emev2_smp_ops __initdata = {  	.smp_prepare_cpus	= emev2_smp_prepare_cpus,  	.smp_secondary_init	= emev2_secondary_init,  	.smp_boot_secondary	= emev2_boot_secondary, -#ifdef CONFIG_HOTPLUG_CPU -	.cpu_kill		= emev2_cpu_kill, -	.cpu_die		= shmobile_cpu_die, -	.cpu_disable		= shmobile_cpu_disable, -#endif  }; diff --git a/arch/arm/mach-shmobile/smp-r8a7779.c b/arch/arm/mach-shmobile/smp-r8a7779.c index 3a4acf23edc..ea4535a5c4e 100644 --- a/arch/arm/mach-shmobile/smp-r8a7779.c +++ b/arch/arm/mach-shmobile/smp-r8a7779.c @@ -26,11 +26,13 @@  #include <linux/irqchip/arm-gic.h>  #include <mach/common.h>  #include <mach/r8a7779.h> +#include <asm/cacheflush.h>  #include <asm/smp_plat.h>  #include <asm/smp_scu.h>  #include <asm/smp_twd.h>  #define AVECR IOMEM(0xfe700040) +#define R8A7779_SCU_BASE 0xf0000000  static struct r8a7779_pm_ch r8a7779_ch_cpu1 = {  	.chan_offs = 0x40, /* PWRSR0 .. PWRER0 */ @@ -56,44 +58,14 @@ static struct r8a7779_pm_ch *r8a7779_ch_cpu[4] = {  	[3] = &r8a7779_ch_cpu3,  }; -static void __iomem *scu_base_addr(void) -{ -	return (void __iomem *)0xf0000000; -} - -static DEFINE_SPINLOCK(scu_lock); -static unsigned long tmp; -  #ifdef CONFIG_HAVE_ARM_TWD -static DEFINE_TWD_LOCAL_TIMER(twd_local_timer, 0xf0000600, 29); - +static DEFINE_TWD_LOCAL_TIMER(twd_local_timer, R8A7779_SCU_BASE + 0x600, 29);  void __init r8a7779_register_twd(void)  {  	twd_local_timer_register(&twd_local_timer);  }  #endif -static void modify_scu_cpu_psr(unsigned long set, unsigned long clr) -{ -	void __iomem *scu_base = scu_base_addr(); - -	spin_lock(&scu_lock); -	tmp = __raw_readl(scu_base + 8); -	tmp &= ~clr; -	tmp |= set; -	spin_unlock(&scu_lock); - -	/* disable cache coherency after releasing the lock */ -	__raw_writel(tmp, scu_base + 8); -} - -static unsigned int __init r8a7779_get_core_count(void) -{ -	void __iomem *scu_base = scu_base_addr(); - -	return scu_get_core_count(scu_base); -} -  static int r8a7779_platform_cpu_kill(unsigned int cpu)  {  	struct r8a7779_pm_ch *ch = NULL; @@ -101,9 +73,6 @@ static int r8a7779_platform_cpu_kill(unsigned int cpu)  	cpu = cpu_logical_map(cpu); -	/* disable cache coherency */ -	modify_scu_cpu_psr(3 << (cpu * 8), 0); -  	if (cpu < ARRAY_SIZE(r8a7779_ch_cpu))  		ch = r8a7779_ch_cpu[cpu]; @@ -113,25 +82,6 @@ static int r8a7779_platform_cpu_kill(unsigned int cpu)  	return ret ? ret : 1;  } -static int __maybe_unused r8a7779_cpu_kill(unsigned int cpu) -{ -	int k; - -	/* this function is running on another CPU than the offline target, -	 * here we need wait for shutdown code in platform_cpu_die() to -	 * finish before asking SoC-specific code to power off the CPU core. -	 */ -	for (k = 0; k < 1000; k++) { -		if (shmobile_cpu_is_dead(cpu)) -			return r8a7779_platform_cpu_kill(cpu); - -		mdelay(1); -	} - -	return 0; -} - -  static void __cpuinit r8a7779_secondary_init(unsigned int cpu)  {  	gic_secondary_init(0); @@ -144,9 +94,6 @@ static int __cpuinit r8a7779_boot_secondary(unsigned int cpu, struct task_struct  	cpu = cpu_logical_map(cpu); -	/* enable cache coherency */ -	modify_scu_cpu_psr(0, 3 << (cpu * 8)); -  	if (cpu < ARRAY_SIZE(r8a7779_ch_cpu))  		ch = r8a7779_ch_cpu[cpu]; @@ -158,15 +105,13 @@ static int __cpuinit r8a7779_boot_secondary(unsigned int cpu, struct task_struct  static void __init r8a7779_smp_prepare_cpus(unsigned int max_cpus)  { -	int cpu = cpu_logical_map(0); +	scu_enable(shmobile_scu_base); -	scu_enable(scu_base_addr()); +	/* Map the reset vector (in headsmp-scu.S) */ +	__raw_writel(__pa(shmobile_secondary_vector_scu), AVECR); -	/* Map the reset vector (in headsmp.S) */ -	__raw_writel(__pa(shmobile_secondary_vector), AVECR); - -	/* enable cache coherency on CPU0 */ -	modify_scu_cpu_psr(0, 3 << (cpu * 8)); +	/* enable cache coherency on booting CPU */ +	scu_power_mode(shmobile_scu_base, SCU_PM_NORMAL);  	r8a7779_pm_init(); @@ -178,10 +123,60 @@ static void __init r8a7779_smp_prepare_cpus(unsigned int max_cpus)  static void __init r8a7779_smp_init_cpus(void)  { -	unsigned int ncores = r8a7779_get_core_count(); +	/* setup r8a7779 specific SCU base */ +	shmobile_scu_base = IOMEM(R8A7779_SCU_BASE); + +	shmobile_smp_init_cpus(scu_get_core_count(shmobile_scu_base)); +} -	shmobile_smp_init_cpus(ncores); +#ifdef CONFIG_HOTPLUG_CPU +static int r8a7779_scu_psr_core_disabled(int cpu) +{ +	unsigned long mask = 3 << (cpu * 8); + +	if ((__raw_readl(shmobile_scu_base + 8) & mask) == mask) +		return 1; + +	return 0; +} + +static int r8a7779_cpu_kill(unsigned int cpu) +{ +	int k; + +	/* this function is running on another CPU than the offline target, +	 * here we need wait for shutdown code in platform_cpu_die() to +	 * finish before asking SoC-specific code to power off the CPU core. +	 */ +	for (k = 0; k < 1000; k++) { +		if (r8a7779_scu_psr_core_disabled(cpu)) +			return r8a7779_platform_cpu_kill(cpu); + +		mdelay(1); +	} + +	return 0; +} + +static void r8a7779_cpu_die(unsigned int cpu) +{ +	dsb(); +	flush_cache_all(); + +	/* disable cache coherency */ +	scu_power_mode(shmobile_scu_base, SCU_PM_POWEROFF); + +	/* Endless loop until power off from r8a7779_cpu_kill() */ +	while (1) +		cpu_do_idle(); +} + +static int r8a7779_cpu_disable(unsigned int cpu) +{ +	/* only CPU1->3 have power domains, do not allow hotplug of CPU0 */ +	return cpu == 0 ? -EPERM : 0;  } +#endif /* CONFIG_HOTPLUG_CPU */  struct smp_operations r8a7779_smp_ops  __initdata = {  	.smp_init_cpus		= r8a7779_smp_init_cpus, @@ -190,7 +185,7 @@ struct smp_operations r8a7779_smp_ops  __initdata = {  	.smp_boot_secondary	= r8a7779_boot_secondary,  #ifdef CONFIG_HOTPLUG_CPU  	.cpu_kill		= r8a7779_cpu_kill, -	.cpu_die		= shmobile_cpu_die, -	.cpu_disable		= shmobile_cpu_disable, +	.cpu_die		= r8a7779_cpu_die, +	.cpu_disable		= r8a7779_cpu_disable,  #endif  }; diff --git a/arch/arm/mach-shmobile/smp-sh73a0.c b/arch/arm/mach-shmobile/smp-sh73a0.c index acb46a94ccd..5ae502b1643 100644 --- a/arch/arm/mach-shmobile/smp-sh73a0.c +++ b/arch/arm/mach-shmobile/smp-sh73a0.c @@ -39,26 +39,16 @@  #define PSTR_SHUTDOWN_MODE	3 -static void __iomem *scu_base_addr(void) -{ -	return (void __iomem *)0xf0000000; -} +#define SH73A0_SCU_BASE 0xf0000000  #ifdef CONFIG_HAVE_ARM_TWD -static DEFINE_TWD_LOCAL_TIMER(twd_local_timer, 0xf0000600, 29); +static DEFINE_TWD_LOCAL_TIMER(twd_local_timer, SH73A0_SCU_BASE + 0x600, 29);  void __init sh73a0_register_twd(void)  {  	twd_local_timer_register(&twd_local_timer);  }  #endif -static unsigned int __init sh73a0_get_core_count(void) -{ -	void __iomem *scu_base = scu_base_addr(); - -	return scu_get_core_count(scu_base); -} -  static void __cpuinit sh73a0_secondary_init(unsigned int cpu)  {  	gic_secondary_init(0); @@ -78,21 +68,22 @@ static int __cpuinit sh73a0_boot_secondary(unsigned int cpu, struct task_struct  static void __init sh73a0_smp_prepare_cpus(unsigned int max_cpus)  { -	scu_enable(scu_base_addr()); +	scu_enable(shmobile_scu_base); -	/* Map the reset vector (in headsmp-sh73a0.S) */ +	/* Map the reset vector (in headsmp-scu.S) */  	__raw_writel(0, APARMBAREA);      /* 4k */ -	__raw_writel(__pa(sh73a0_secondary_vector), SBAR); +	__raw_writel(__pa(shmobile_secondary_vector_scu), SBAR);  	/* enable cache coherency on booting CPU */ -	scu_power_mode(scu_base_addr(), SCU_PM_NORMAL); +	scu_power_mode(shmobile_scu_base, SCU_PM_NORMAL);  }  static void __init sh73a0_smp_init_cpus(void)  { -	unsigned int ncores = sh73a0_get_core_count(); +	/* setup sh73a0 specific SCU base */ +	shmobile_scu_base = IOMEM(SH73A0_SCU_BASE); -	shmobile_smp_init_cpus(ncores); +	shmobile_smp_init_cpus(scu_get_core_count(shmobile_scu_base));  }  #ifdef CONFIG_HOTPLUG_CPU @@ -128,11 +119,16 @@ static void sh73a0_cpu_die(unsigned int cpu)  	flush_cache_all();  	/* Set power off mode. This takes the CPU out of the MP cluster */ -	scu_power_mode(scu_base_addr(), SCU_PM_POWEROFF); +	scu_power_mode(shmobile_scu_base, SCU_PM_POWEROFF);  	/* Enter shutdown mode */  	cpu_do_idle();  } + +static int sh73a0_cpu_disable(unsigned int cpu) +{ +	return 0; /* CPU0 and CPU1 supported */ +}  #endif /* CONFIG_HOTPLUG_CPU */  struct smp_operations sh73a0_smp_ops __initdata = { @@ -143,6 +139,6 @@ struct smp_operations sh73a0_smp_ops __initdata = {  #ifdef CONFIG_HOTPLUG_CPU  	.cpu_kill		= sh73a0_cpu_kill,  	.cpu_die		= sh73a0_cpu_die, -	.cpu_disable		= shmobile_cpu_disable_any, +	.cpu_disable		= sh73a0_cpu_disable,  #endif  }; diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index a350969e5ef..4a33351c25d 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -25,6 +25,14 @@ config ARM_VIC_NR  	  The maximum number of VICs available in the system, for  	  power management. +config RENESAS_INTC_IRQPIN +	bool +	select IRQ_DOMAIN + +config RENESAS_IRQC +	bool +	select IRQ_DOMAIN +  config VERSATILE_FPGA_IRQ  	bool  	select IRQ_DOMAIN diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 98e3b87bdf1..e41ceb9bec2 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -8,4 +8,6 @@ obj-$(CONFIG_ARCH_SUNXI)		+= irq-sunxi.o  obj-$(CONFIG_ARCH_SPEAR3XX)		+= spear-shirq.o  obj-$(CONFIG_ARM_GIC)			+= irq-gic.o  obj-$(CONFIG_ARM_VIC)			+= irq-vic.o +obj-$(CONFIG_RENESAS_INTC_IRQPIN)	+= irq-renesas-intc-irqpin.o +obj-$(CONFIG_RENESAS_IRQC)		+= irq-renesas-irqc.o  obj-$(CONFIG_VERSATILE_FPGA_IRQ)	+= irq-versatile-fpga.o diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c new file mode 100644 index 00000000000..fd5dabc2235 --- /dev/null +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c @@ -0,0 +1,471 @@ +/* + * Renesas INTC External IRQ Pin Driver + * + *  Copyright (C) 2013 Magnus Damm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + */ + +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/platform_data/irq-renesas-intc-irqpin.h> + +#define INTC_IRQPIN_MAX 8 /* maximum 8 interrupts per driver instance */ + +#define INTC_IRQPIN_REG_SENSE 0 /* ICRn */ +#define INTC_IRQPIN_REG_PRIO 1 /* INTPRInn */ +#define INTC_IRQPIN_REG_SOURCE 2 /* INTREQnn */ +#define INTC_IRQPIN_REG_MASK 3 /* INTMSKnn */ +#define INTC_IRQPIN_REG_CLEAR 4 /* INTMSKCLRnn */ +#define INTC_IRQPIN_REG_NR 5 + +/* INTC external IRQ PIN hardware register access: + * + * SENSE is read-write 32-bit with 2-bits or 4-bits per IRQ (*) + * PRIO is read-write 32-bit with 4-bits per IRQ (**) + * SOURCE is read-only 32-bit or 8-bit with 1-bit per IRQ (***) + * MASK is write-only 32-bit or 8-bit with 1-bit per IRQ (***) + * CLEAR is write-only 32-bit or 8-bit with 1-bit per IRQ (***) + * + * (*) May be accessed by more than one driver instance - lock needed + * (**) Read-modify-write access by one driver instance - lock needed + * (***) Accessed by one driver instance only - no locking needed + */ + +struct intc_irqpin_iomem { +	void __iomem *iomem; +	unsigned long (*read)(void __iomem *iomem); +	void (*write)(void __iomem *iomem, unsigned long data); +	int width; +}; + +struct intc_irqpin_irq { +	int hw_irq; +	int requested_irq; +	int domain_irq; +	struct intc_irqpin_priv *p; +}; + +struct intc_irqpin_priv { +	struct intc_irqpin_iomem iomem[INTC_IRQPIN_REG_NR]; +	struct intc_irqpin_irq irq[INTC_IRQPIN_MAX]; +	struct renesas_intc_irqpin_config config; +	unsigned int number_of_irqs; +	struct platform_device *pdev; +	struct irq_chip irq_chip; +	struct irq_domain *irq_domain; +}; + +static unsigned long intc_irqpin_read32(void __iomem *iomem) +{ +	return ioread32(iomem); +} + +static unsigned long intc_irqpin_read8(void __iomem *iomem) +{ +	return ioread8(iomem); +} + +static void intc_irqpin_write32(void __iomem *iomem, unsigned long data) +{ +	iowrite32(data, iomem); +} + +static void intc_irqpin_write8(void __iomem *iomem, unsigned long data) +{ +	iowrite8(data, iomem); +} + +static inline unsigned long intc_irqpin_read(struct intc_irqpin_priv *p, +					     int reg) +{ +	struct intc_irqpin_iomem *i = &p->iomem[reg]; + +	return i->read(i->iomem); +} + +static inline void intc_irqpin_write(struct intc_irqpin_priv *p, +				     int reg, unsigned long data) +{ +	struct intc_irqpin_iomem *i = &p->iomem[reg]; + +	i->write(i->iomem, data); +} + +static inline unsigned long intc_irqpin_hwirq_mask(struct intc_irqpin_priv *p, +						   int reg, int hw_irq) +{ +	return BIT((p->iomem[reg].width - 1) - hw_irq); +} + +static inline void intc_irqpin_irq_write_hwirq(struct intc_irqpin_priv *p, +					       int reg, int hw_irq) +{ +	intc_irqpin_write(p, reg, intc_irqpin_hwirq_mask(p, reg, hw_irq)); +} + +static DEFINE_RAW_SPINLOCK(intc_irqpin_lock); /* only used by slow path */ + +static void intc_irqpin_read_modify_write(struct intc_irqpin_priv *p, +					  int reg, int shift, +					  int width, int value) +{ +	unsigned long flags; +	unsigned long tmp; + +	raw_spin_lock_irqsave(&intc_irqpin_lock, flags); + +	tmp = intc_irqpin_read(p, reg); +	tmp &= ~(((1 << width) - 1) << shift); +	tmp |= value << shift; +	intc_irqpin_write(p, reg, tmp); + +	raw_spin_unlock_irqrestore(&intc_irqpin_lock, flags); +} + +static void intc_irqpin_mask_unmask_prio(struct intc_irqpin_priv *p, +					 int irq, int do_mask) +{ +	int bitfield_width = 4; /* PRIO assumed to have fixed bitfield width */ +	int shift = (7 - irq) * bitfield_width; /* PRIO assumed to be 32-bit */ + +	intc_irqpin_read_modify_write(p, INTC_IRQPIN_REG_PRIO, +				      shift, bitfield_width, +				      do_mask ? 0 : (1 << bitfield_width) - 1); +} + +static int intc_irqpin_set_sense(struct intc_irqpin_priv *p, int irq, int value) +{ +	int bitfield_width = p->config.sense_bitfield_width; +	int shift = (7 - irq) * bitfield_width; /* SENSE assumed to be 32-bit */ + +	dev_dbg(&p->pdev->dev, "sense irq = %d, mode = %d\n", irq, value); + +	if (value >= (1 << bitfield_width)) +		return -EINVAL; + +	intc_irqpin_read_modify_write(p, INTC_IRQPIN_REG_SENSE, shift, +				      bitfield_width, value); +	return 0; +} + +static void intc_irqpin_dbg(struct intc_irqpin_irq *i, char *str) +{ +	dev_dbg(&i->p->pdev->dev, "%s (%d:%d:%d)\n", +		str, i->requested_irq, i->hw_irq, i->domain_irq); +} + +static void intc_irqpin_irq_enable(struct irq_data *d) +{ +	struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); +	int hw_irq = irqd_to_hwirq(d); + +	intc_irqpin_dbg(&p->irq[hw_irq], "enable"); +	intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_CLEAR, hw_irq); +} + +static void intc_irqpin_irq_disable(struct irq_data *d) +{ +	struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); +	int hw_irq = irqd_to_hwirq(d); + +	intc_irqpin_dbg(&p->irq[hw_irq], "disable"); +	intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_MASK, hw_irq); +} + +static void intc_irqpin_irq_enable_force(struct irq_data *d) +{ +	struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); +	int irq = p->irq[irqd_to_hwirq(d)].requested_irq; + +	intc_irqpin_irq_enable(d); + +	/* enable interrupt through parent interrupt controller, +	 * assumes non-shared interrupt with 1:1 mapping +	 * needed for busted IRQs on some SoCs like sh73a0 +	 */ +	irq_get_chip(irq)->irq_unmask(irq_get_irq_data(irq)); +} + +static void intc_irqpin_irq_disable_force(struct irq_data *d) +{ +	struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); +	int irq = p->irq[irqd_to_hwirq(d)].requested_irq; + +	/* disable interrupt through parent interrupt controller, +	 * assumes non-shared interrupt with 1:1 mapping +	 * needed for busted IRQs on some SoCs like sh73a0 +	 */ +	irq_get_chip(irq)->irq_mask(irq_get_irq_data(irq)); +	intc_irqpin_irq_disable(d); +} + +#define INTC_IRQ_SENSE_VALID 0x10 +#define INTC_IRQ_SENSE(x) (x + INTC_IRQ_SENSE_VALID) + +static unsigned char intc_irqpin_sense[IRQ_TYPE_SENSE_MASK + 1] = { +	[IRQ_TYPE_EDGE_FALLING] = INTC_IRQ_SENSE(0x00), +	[IRQ_TYPE_EDGE_RISING] = INTC_IRQ_SENSE(0x01), +	[IRQ_TYPE_LEVEL_LOW] = INTC_IRQ_SENSE(0x02), +	[IRQ_TYPE_LEVEL_HIGH] = INTC_IRQ_SENSE(0x03), +	[IRQ_TYPE_EDGE_BOTH] = INTC_IRQ_SENSE(0x04), +}; + +static int intc_irqpin_irq_set_type(struct irq_data *d, unsigned int type) +{ +	unsigned char value = intc_irqpin_sense[type & IRQ_TYPE_SENSE_MASK]; +	struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); + +	if (!(value & INTC_IRQ_SENSE_VALID)) +		return -EINVAL; + +	return intc_irqpin_set_sense(p, irqd_to_hwirq(d), +				     value ^ INTC_IRQ_SENSE_VALID); +} + +static irqreturn_t intc_irqpin_irq_handler(int irq, void *dev_id) +{ +	struct intc_irqpin_irq *i = dev_id; +	struct intc_irqpin_priv *p = i->p; +	unsigned long bit; + +	intc_irqpin_dbg(i, "demux1"); +	bit = intc_irqpin_hwirq_mask(p, INTC_IRQPIN_REG_SOURCE, i->hw_irq); + +	if (intc_irqpin_read(p, INTC_IRQPIN_REG_SOURCE) & bit) { +		intc_irqpin_write(p, INTC_IRQPIN_REG_SOURCE, ~bit); +		intc_irqpin_dbg(i, "demux2"); +		generic_handle_irq(i->domain_irq); +		return IRQ_HANDLED; +	} +	return IRQ_NONE; +} + +static int intc_irqpin_irq_domain_map(struct irq_domain *h, unsigned int virq, +				      irq_hw_number_t hw) +{ +	struct intc_irqpin_priv *p = h->host_data; + +	p->irq[hw].domain_irq = virq; +	p->irq[hw].hw_irq = hw; + +	intc_irqpin_dbg(&p->irq[hw], "map"); +	irq_set_chip_data(virq, h->host_data); +	irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq); +	set_irq_flags(virq, IRQF_VALID); /* kill me now */ +	return 0; +} + +static struct irq_domain_ops intc_irqpin_irq_domain_ops = { +	.map	= intc_irqpin_irq_domain_map, +	.xlate  = irq_domain_xlate_twocell, +}; + +static int intc_irqpin_probe(struct platform_device *pdev) +{ +	struct renesas_intc_irqpin_config *pdata = pdev->dev.platform_data; +	struct intc_irqpin_priv *p; +	struct intc_irqpin_iomem *i; +	struct resource *io[INTC_IRQPIN_REG_NR]; +	struct resource *irq; +	struct irq_chip *irq_chip; +	void (*enable_fn)(struct irq_data *d); +	void (*disable_fn)(struct irq_data *d); +	const char *name = dev_name(&pdev->dev); +	int ret; +	int k; + +	p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); +	if (!p) { +		dev_err(&pdev->dev, "failed to allocate driver data\n"); +		ret = -ENOMEM; +		goto err0; +	} + +	/* deal with driver instance configuration */ +	if (pdata) +		memcpy(&p->config, pdata, sizeof(*pdata)); +	if (!p->config.sense_bitfield_width) +		p->config.sense_bitfield_width = 4; /* default to 4 bits */ + +	p->pdev = pdev; +	platform_set_drvdata(pdev, p); + +	/* get hold of manadatory IOMEM */ +	for (k = 0; k < INTC_IRQPIN_REG_NR; k++) { +		io[k] = platform_get_resource(pdev, IORESOURCE_MEM, k); +		if (!io[k]) { +			dev_err(&pdev->dev, "not enough IOMEM resources\n"); +			ret = -EINVAL; +			goto err0; +		} +	} + +	/* allow any number of IRQs between 1 and INTC_IRQPIN_MAX */ +	for (k = 0; k < INTC_IRQPIN_MAX; k++) { +		irq = platform_get_resource(pdev, IORESOURCE_IRQ, k); +		if (!irq) +			break; + +		p->irq[k].p = p; +		p->irq[k].requested_irq = irq->start; +	} + +	p->number_of_irqs = k; +	if (p->number_of_irqs < 1) { +		dev_err(&pdev->dev, "not enough IRQ resources\n"); +		ret = -EINVAL; +		goto err0; +	} + +	/* ioremap IOMEM and setup read/write callbacks */ +	for (k = 0; k < INTC_IRQPIN_REG_NR; k++) { +		i = &p->iomem[k]; + +		switch (resource_size(io[k])) { +		case 1: +			i->width = 8; +			i->read = intc_irqpin_read8; +			i->write = intc_irqpin_write8; +			break; +		case 4: +			i->width = 32; +			i->read = intc_irqpin_read32; +			i->write = intc_irqpin_write32; +			break; +		default: +			dev_err(&pdev->dev, "IOMEM size mismatch\n"); +			ret = -EINVAL; +			goto err0; +		} + +		i->iomem = devm_ioremap_nocache(&pdev->dev, io[k]->start, +						resource_size(io[k])); +		if (!i->iomem) { +			dev_err(&pdev->dev, "failed to remap IOMEM\n"); +			ret = -ENXIO; +			goto err0; +		} +	} + +	/* mask all interrupts using priority */ +	for (k = 0; k < p->number_of_irqs; k++) +		intc_irqpin_mask_unmask_prio(p, k, 1); + +	/* use more severe masking method if requested */ +	if (p->config.control_parent) { +		enable_fn = intc_irqpin_irq_enable_force; +		disable_fn = intc_irqpin_irq_disable_force; +	} else { +		enable_fn = intc_irqpin_irq_enable; +		disable_fn = intc_irqpin_irq_disable; +	} + +	irq_chip = &p->irq_chip; +	irq_chip->name = name; +	irq_chip->irq_mask = disable_fn; +	irq_chip->irq_unmask = enable_fn; +	irq_chip->irq_enable = enable_fn; +	irq_chip->irq_disable = disable_fn; +	irq_chip->irq_set_type = intc_irqpin_irq_set_type; +	irq_chip->flags	= IRQCHIP_SKIP_SET_WAKE; + +	p->irq_domain = irq_domain_add_simple(pdev->dev.of_node, +					      p->number_of_irqs, +					      p->config.irq_base, +					      &intc_irqpin_irq_domain_ops, p); +	if (!p->irq_domain) { +		ret = -ENXIO; +		dev_err(&pdev->dev, "cannot initialize irq domain\n"); +		goto err0; +	} + +	/* request and set priority on interrupts one by one */ +	for (k = 0; k < p->number_of_irqs; k++) { +		if (devm_request_irq(&pdev->dev, p->irq[k].requested_irq, +				     intc_irqpin_irq_handler, +				     0, name, &p->irq[k])) { +			dev_err(&pdev->dev, "failed to request low IRQ\n"); +			ret = -ENOENT; +			goto err1; +		} +		intc_irqpin_mask_unmask_prio(p, k, 0); +	} + +	dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs); + +	/* warn in case of mismatch if irq base is specified */ +	if (p->config.irq_base) { +		if (p->config.irq_base != p->irq[0].domain_irq) +			dev_warn(&pdev->dev, "irq base mismatch (%d/%d)\n", +				 p->config.irq_base, p->irq[0].domain_irq); +	} + +	return 0; + +err1: +	irq_domain_remove(p->irq_domain); +err0: +	return ret; +} + +static int intc_irqpin_remove(struct platform_device *pdev) +{ +	struct intc_irqpin_priv *p = platform_get_drvdata(pdev); + +	irq_domain_remove(p->irq_domain); + +	return 0; +} + +static const struct of_device_id intc_irqpin_dt_ids[] = { +	{ .compatible = "renesas,intc-irqpin", }, +	{}, +}; +MODULE_DEVICE_TABLE(of, intc_irqpin_dt_ids); + +static struct platform_driver intc_irqpin_device_driver = { +	.probe		= intc_irqpin_probe, +	.remove		= intc_irqpin_remove, +	.driver		= { +		.name	= "renesas_intc_irqpin", +		.of_match_table = intc_irqpin_dt_ids, +		.owner  = THIS_MODULE, +	} +}; + +static int __init intc_irqpin_init(void) +{ +	return platform_driver_register(&intc_irqpin_device_driver); +} +postcore_initcall(intc_irqpin_init); + +static void __exit intc_irqpin_exit(void) +{ +	platform_driver_unregister(&intc_irqpin_device_driver); +} +module_exit(intc_irqpin_exit); + +MODULE_AUTHOR("Magnus Damm"); +MODULE_DESCRIPTION("Renesas INTC External IRQ Pin Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/irqchip/irq-renesas-irqc.c b/drivers/irqchip/irq-renesas-irqc.c new file mode 100644 index 00000000000..927bff373aa --- /dev/null +++ b/drivers/irqchip/irq-renesas-irqc.c @@ -0,0 +1,307 @@ +/* + * Renesas IRQC Driver + * + *  Copyright (C) 2013 Magnus Damm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + */ + +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/platform_data/irq-renesas-irqc.h> + +#define IRQC_IRQ_MAX 32 /* maximum 32 interrupts per driver instance */ + +#define IRQC_REQ_STS 0x00 +#define IRQC_EN_STS 0x04 +#define IRQC_EN_SET 0x08 +#define IRQC_INT_CPU_BASE(n) (0x000 + ((n) * 0x10)) +#define DETECT_STATUS 0x100 +#define IRQC_CONFIG(n) (0x180 + ((n) * 0x04)) + +struct irqc_irq { +	int hw_irq; +	int requested_irq; +	int domain_irq; +	struct irqc_priv *p; +}; + +struct irqc_priv { +	void __iomem *iomem; +	void __iomem *cpu_int_base; +	struct irqc_irq irq[IRQC_IRQ_MAX]; +	struct renesas_irqc_config config; +	unsigned int number_of_irqs; +	struct platform_device *pdev; +	struct irq_chip irq_chip; +	struct irq_domain *irq_domain; +}; + +static void irqc_dbg(struct irqc_irq *i, char *str) +{ +	dev_dbg(&i->p->pdev->dev, "%s (%d:%d:%d)\n", +		str, i->requested_irq, i->hw_irq, i->domain_irq); +} + +static void irqc_irq_enable(struct irq_data *d) +{ +	struct irqc_priv *p = irq_data_get_irq_chip_data(d); +	int hw_irq = irqd_to_hwirq(d); + +	irqc_dbg(&p->irq[hw_irq], "enable"); +	iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_SET); +} + +static void irqc_irq_disable(struct irq_data *d) +{ +	struct irqc_priv *p = irq_data_get_irq_chip_data(d); +	int hw_irq = irqd_to_hwirq(d); + +	irqc_dbg(&p->irq[hw_irq], "disable"); +	iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_STS); +} + +#define INTC_IRQ_SENSE_VALID 0x10 +#define INTC_IRQ_SENSE(x) (x + INTC_IRQ_SENSE_VALID) + +static unsigned char irqc_sense[IRQ_TYPE_SENSE_MASK + 1] = { +	[IRQ_TYPE_LEVEL_LOW] = INTC_IRQ_SENSE(0x01), +	[IRQ_TYPE_LEVEL_HIGH] = INTC_IRQ_SENSE(0x02), +	[IRQ_TYPE_EDGE_FALLING] = INTC_IRQ_SENSE(0x04), /* Synchronous */ +	[IRQ_TYPE_EDGE_RISING] = INTC_IRQ_SENSE(0x08), /* Synchronous */ +	[IRQ_TYPE_EDGE_BOTH] = INTC_IRQ_SENSE(0x0c),  /* Synchronous */ +}; + +static int irqc_irq_set_type(struct irq_data *d, unsigned int type) +{ +	struct irqc_priv *p = irq_data_get_irq_chip_data(d); +	int hw_irq = irqd_to_hwirq(d); +	unsigned char value = irqc_sense[type & IRQ_TYPE_SENSE_MASK]; +	unsigned long tmp; + +	irqc_dbg(&p->irq[hw_irq], "sense"); + +	if (!(value & INTC_IRQ_SENSE_VALID)) +		return -EINVAL; + +	tmp = ioread32(p->iomem + IRQC_CONFIG(hw_irq)); +	tmp &= ~0x3f; +	tmp |= value ^ INTC_IRQ_SENSE_VALID; +	iowrite32(tmp, p->iomem + IRQC_CONFIG(hw_irq)); +	return 0; +} + +static irqreturn_t irqc_irq_handler(int irq, void *dev_id) +{ +	struct irqc_irq *i = dev_id; +	struct irqc_priv *p = i->p; +	unsigned long bit = BIT(i->hw_irq); + +	irqc_dbg(i, "demux1"); + +	if (ioread32(p->iomem + DETECT_STATUS) & bit) { +		iowrite32(bit, p->iomem + DETECT_STATUS); +		irqc_dbg(i, "demux2"); +		generic_handle_irq(i->domain_irq); +		return IRQ_HANDLED; +	} +	return IRQ_NONE; +} + +static int irqc_irq_domain_map(struct irq_domain *h, unsigned int virq, +			       irq_hw_number_t hw) +{ +	struct irqc_priv *p = h->host_data; + +	p->irq[hw].domain_irq = virq; +	p->irq[hw].hw_irq = hw; + +	irqc_dbg(&p->irq[hw], "map"); +	irq_set_chip_data(virq, h->host_data); +	irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq); +	set_irq_flags(virq, IRQF_VALID); /* kill me now */ +	return 0; +} + +static struct irq_domain_ops irqc_irq_domain_ops = { +	.map	= irqc_irq_domain_map, +	.xlate  = irq_domain_xlate_twocell, +}; + +static int irqc_probe(struct platform_device *pdev) +{ +	struct renesas_irqc_config *pdata = pdev->dev.platform_data; +	struct irqc_priv *p; +	struct resource *io; +	struct resource *irq; +	struct irq_chip *irq_chip; +	const char *name = dev_name(&pdev->dev); +	int ret; +	int k; + +	p = kzalloc(sizeof(*p), GFP_KERNEL); +	if (!p) { +		dev_err(&pdev->dev, "failed to allocate driver data\n"); +		ret = -ENOMEM; +		goto err0; +	} + +	/* deal with driver instance configuration */ +	if (pdata) +		memcpy(&p->config, pdata, sizeof(*pdata)); + +	p->pdev = pdev; +	platform_set_drvdata(pdev, p); + +	/* get hold of manadatory IOMEM */ +	io = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (!io) { +		dev_err(&pdev->dev, "not enough IOMEM resources\n"); +		ret = -EINVAL; +		goto err1; +	} + +	/* allow any number of IRQs between 1 and IRQC_IRQ_MAX */ +	for (k = 0; k < IRQC_IRQ_MAX; k++) { +		irq = platform_get_resource(pdev, IORESOURCE_IRQ, k); +		if (!irq) +			break; + +		p->irq[k].p = p; +		p->irq[k].requested_irq = irq->start; +	} + +	p->number_of_irqs = k; +	if (p->number_of_irqs < 1) { +		dev_err(&pdev->dev, "not enough IRQ resources\n"); +		ret = -EINVAL; +		goto err1; +	} + +	/* ioremap IOMEM and setup read/write callbacks */ +	p->iomem = ioremap_nocache(io->start, resource_size(io)); +	if (!p->iomem) { +		dev_err(&pdev->dev, "failed to remap IOMEM\n"); +		ret = -ENXIO; +		goto err2; +	} + +	p->cpu_int_base = p->iomem + IRQC_INT_CPU_BASE(0); /* SYS-SPI */ + +	irq_chip = &p->irq_chip; +	irq_chip->name = name; +	irq_chip->irq_mask = irqc_irq_disable; +	irq_chip->irq_unmask = irqc_irq_enable; +	irq_chip->irq_enable = irqc_irq_enable; +	irq_chip->irq_disable = irqc_irq_disable; +	irq_chip->irq_set_type = irqc_irq_set_type; +	irq_chip->flags	= IRQCHIP_SKIP_SET_WAKE; + +	p->irq_domain = irq_domain_add_simple(pdev->dev.of_node, +					      p->number_of_irqs, +					      p->config.irq_base, +					      &irqc_irq_domain_ops, p); +	if (!p->irq_domain) { +		ret = -ENXIO; +		dev_err(&pdev->dev, "cannot initialize irq domain\n"); +		goto err2; +	} + +	/* request interrupts one by one */ +	for (k = 0; k < p->number_of_irqs; k++) { +		if (request_irq(p->irq[k].requested_irq, irqc_irq_handler, +				0, name, &p->irq[k])) { +			dev_err(&pdev->dev, "failed to request IRQ\n"); +			ret = -ENOENT; +			goto err3; +		} +	} + +	dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs); + +	/* warn in case of mismatch if irq base is specified */ +	if (p->config.irq_base) { +		if (p->config.irq_base != p->irq[0].domain_irq) +			dev_warn(&pdev->dev, "irq base mismatch (%d/%d)\n", +				 p->config.irq_base, p->irq[0].domain_irq); +	} + +	return 0; +err3: +	for (; k >= 0; k--) +		free_irq(p->irq[k - 1].requested_irq, &p->irq[k - 1]); + +	irq_domain_remove(p->irq_domain); +err2: +	iounmap(p->iomem); +err1: +	kfree(p); +err0: +	return ret; +} + +static int irqc_remove(struct platform_device *pdev) +{ +	struct irqc_priv *p = platform_get_drvdata(pdev); +	int k; + +	for (k = 0; k < p->number_of_irqs; k++) +		free_irq(p->irq[k].requested_irq, &p->irq[k]); + +	irq_domain_remove(p->irq_domain); +	iounmap(p->iomem); +	kfree(p); +	return 0; +} + +static const struct of_device_id irqc_dt_ids[] = { +	{ .compatible = "renesas,irqc", }, +	{}, +}; +MODULE_DEVICE_TABLE(of, irqc_dt_ids); + +static struct platform_driver irqc_device_driver = { +	.probe		= irqc_probe, +	.remove		= irqc_remove, +	.driver		= { +		.name	= "renesas_irqc", +		.of_match_table	= irqc_dt_ids, +		.owner	= THIS_MODULE, +	} +}; + +static int __init irqc_init(void) +{ +	return platform_driver_register(&irqc_device_driver); +} +postcore_initcall(irqc_init); + +static void __exit irqc_exit(void) +{ +	platform_driver_unregister(&irqc_device_driver); +} +module_exit(irqc_exit); + +MODULE_AUTHOR("Magnus Damm"); +MODULE_DESCRIPTION("Renesas IRQC Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/sh-pfc/pfc-sh73a0.c b/drivers/pinctrl/sh-pfc/pfc-sh73a0.c index 709008e9412..6f15c03077a 100644 --- a/drivers/pinctrl/sh-pfc/pfc-sh73a0.c +++ b/drivers/pinctrl/sh-pfc/pfc-sh73a0.c @@ -2733,9 +2733,9 @@ static struct pinmux_data_reg pinmux_data_regs[] = {  	{ },  }; -/* IRQ pins through INTCS with IRQ0->15 from 0x200 and IRQ16-31 from 0x3200 */ -#define EXT_IRQ16L(n) intcs_evt2irq(0x200 + ((n) << 5)) -#define EXT_IRQ16H(n) intcs_evt2irq(0x3200 + ((n - 16) << 5)) +/* External IRQ pins mapped at IRQPIN_BASE */ +#define EXT_IRQ16L(n) irq_pin(n) +#define EXT_IRQ16H(n) irq_pin(n)  static struct pinmux_irq pinmux_irqs[] = {  	PINMUX_IRQ(EXT_IRQ16H(19), PORT9_FN0), diff --git a/include/linux/platform_data/irq-renesas-intc-irqpin.h b/include/linux/platform_data/irq-renesas-intc-irqpin.h new file mode 100644 index 00000000000..e4cb911066a --- /dev/null +++ b/include/linux/platform_data/irq-renesas-intc-irqpin.h @@ -0,0 +1,29 @@ +/* + * Renesas INTC External IRQ Pin Driver + * + *  Copyright (C) 2013 Magnus Damm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + */ + +#ifndef __IRQ_RENESAS_INTC_IRQPIN_H__ +#define __IRQ_RENESAS_INTC_IRQPIN_H__ + +struct renesas_intc_irqpin_config { +	unsigned int sense_bitfield_width; +	unsigned int irq_base; +	bool control_parent; +}; + +#endif /* __IRQ_RENESAS_INTC_IRQPIN_H__ */ diff --git a/include/linux/platform_data/irq-renesas-irqc.h b/include/linux/platform_data/irq-renesas-irqc.h new file mode 100644 index 00000000000..3ae17b3e00e --- /dev/null +++ b/include/linux/platform_data/irq-renesas-irqc.h @@ -0,0 +1,27 @@ +/* + * Renesas IRQC Driver + * + *  Copyright (C) 2013 Magnus Damm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + */ + +#ifndef __IRQ_RENESAS_IRQC_H__ +#define __IRQ_RENESAS_IRQC_H__ + +struct renesas_irqc_config { +	unsigned int irq_base; +}; + +#endif /* __IRQ_RENESAS_IRQC_H__ */  |