mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-06 10:57:46 +00:00
Renesas INTC External IRQ pin driver
This provides two new INTC drivers for use with Renesas ARM-based SoCs and makes use of them on the r8a7779 and sh73a0 SoCs. It has been agreed by the relevant parties, Thomas Gleixner, Magnus Damm, and myself that it would be best to merge this code through the renesas tree and thus through the arm-soc tree. This is based on: git://git.kernel.org/pub/scm/linux/kernel/git/horms/renesas.git renesas-soc-v3.10 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIcBAABAgAGBQJRR8KvAAoJENfPZGlqN0++MdQP/jxDtb26s5ud1Th3GOMrvWcb xFhZnnAp7wawPmbXT/l0lvMQSjTgPunTgUCD2RSp4CUKquG+sMNvmt+oxZT/WG81 VLbjicQWIdcODt/gkuff4U282s3Kk7joRdzfORUKSu3/tjfy1nBSyGdJBVaGmor4 NL0rbtuhnf3Jng4+u/UgtcsA6FVmnACf9T2m8Gpf9JbcnJXd/U7jurk9ZY8VvEti KsbmlfmfBXLVjyl0Glbik07Njs4Jta9NIZ6hj9Yrlb8p0/EqjbRvuqDn3nLLIKpd LhdkOLm7V7X2r7T4aDc1yvZxlqWTy15U/HJTnKxcW+Phl+p6mY7Kmiwab9ImQ5ZP hG+GjEcznHs9kDgxceKcj8CCKFrP3zH8w0k+PIb76jEG7ttWHbF/3mr35gWIXKYe duKrHoA9Y+qssB/rOZw0i4GNb2QXpyTHDpoFMXc9jDpRzJHBXwsrJDJTJQ6NQlfJ HhlCdLv3fUeyJpYBlzrFkqoyt1wHx++pkQnO0yEzGTetNXrNuUBuDqiJnbwfBznp wMNkQh4s3zc/Cn4HziLc78cWmoov42xSAuEcbnsD9Cg3wtfdtW+Hxj/SSGKOJZmV 3P0AJvPQgkz9Y61vviQhODpy88aUaZWMxB9lgvSkUJo+Egc7pmU3sfK8XMT6GTVH Nset/Au80YiU0LaJKKoV =Kwwk -----END PGP SIGNATURE----- Merge tag 'renesas-intc-external-irq-for-v3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/horms/renesas into next/drivers From Simon Horman <horms+renesas@verge.net.au>: Renesas INTC External IRQ pin driver This provides two new INTC drivers for use with Renesas ARM-based SoCs and makes use of them on the r8a7779 and sh73a0 SoCs. It has been agreed by the relevant parties, Thomas Gleixner, Magnus Damm, and myself that it would be best to merge this code through the renesas tree and thus through the arm-soc tree. This is based on: git://git.kernel.org/pub/scm/linux/kernel/git/horms/renesas.git renesas-soc-v3.10 * tag 'renesas-intc-external-irq-for-v3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/horms/renesas: irqchip: irqc: Add DT support irqchip: intc-irqpin: Initial DT support ARM: shmobile: Make r8a7779 INTC irqpin platform data static ARM: shmobile: Make sh73a0 INTC irqpin platform data static irqchip: Renesas IRQC driver irqchip: intc-irqpin: GPL header for platform data irqchip: intc-irqpin: Make use of devm functions irqchip: intc-irqpin: Add force comments irqchip: intc-irqpin: Cache mapped IRQ irqchip: intc-irqpin: Whitespace fixes ARM: shmobile: INTC External IRQ pin driver on r8a7779 ARM: shmobile: INTC External IRQ pin driver on sh73a0 ARM: shmobile: irq_pin() for static IRQ pin assignment irqchip: Renesas INTC External IRQ pin driver Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
commit
c16c4c4b12
26 changed files with 1393 additions and 440 deletions
98
arch/arm/boot/dts/r8a7779.dtsi
Normal file
98
arch/arm/boot/dts/r8a7779.dtsi
Normal file
|
@ -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>;
|
||||
};
|
||||
};
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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]),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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,11 +123,61 @@ 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(ncores);
|
||||
shmobile_smp_init_cpus(scu_get_core_count(shmobile_scu_base));
|
||||
}
|
||||
|
||||
#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,
|
||||
.smp_prepare_cpus = r8a7779_smp_prepare_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
|
||||
};
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
471
drivers/irqchip/irq-renesas-intc-irqpin.c
Normal file
471
drivers/irqchip/irq-renesas-intc-irqpin.c
Normal file
|
@ -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");
|
307
drivers/irqchip/irq-renesas-irqc.c
Normal file
307
drivers/irqchip/irq-renesas-irqc.c
Normal file
|
@ -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");
|
|
@ -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),
|
||||
|
|
29
include/linux/platform_data/irq-renesas-intc-irqpin.h
Normal file
29
include/linux/platform_data/irq-renesas-intc-irqpin.h
Normal file
|
@ -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__ */
|
27
include/linux/platform_data/irq-renesas-irqc.h
Normal file
27
include/linux/platform_data/irq-renesas-irqc.h
Normal file
|
@ -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__ */
|
Loading…
Reference in a new issue