diff options
author | Guodong Xu <guodong.xu@linaro.org> | 2014-02-27 23:47:43 +0800 |
---|---|---|
committer | Guodong Xu <guodong.xu@linaro.org> | 2014-02-27 23:47:43 +0800 |
commit | 6370e9d677b756b790c2fa46868a51babd887cb2 (patch) | |
tree | 45f07ee17443c3d57d6e79c7accf3425d6239f11 | |
parent | 3c30851ceaf0afe879cda5bfe0548657c7345c38 (diff) | |
parent | ae2f2e3d38a35829a824d7b23ee24cd12c153351 (diff) |
Merge remote-tracking branch 'origin/integration-hilt-d01' into integration-hilt-d01
* origin/integration-hilt-d01:
ARM: hisi: reserve bootwrapper space for HiP04
ARM: hisi: add reboot for hip04
ARM: dts: remove skeleton dtsi
ARM: config: LTP requires loop block device
ARM: hisi: remove L2 cache in P04 config
ARM: config: always enable DEBUG_LL & EARLY_PRINTK
irq: gic: support HiP04 registers
ARM: config: support 16CPUs in HiP04
ARM: dts: enable SMP for HiP04
ARM: mcpm: change max clusters to 4
irq: gic: extends the cpu interface to 16
irq: gic: use mask field in GICC_IAR
ARM: hisi: move hip04 out of hisilicon driver
-rw-r--r-- | arch/arm/boot/dts/hip04.dtsi | 42 | ||||
-rw-r--r-- | arch/arm/configs/hip04_defconfig | 6 | ||||
-rw-r--r-- | arch/arm/include/asm/mcpm.h | 2 | ||||
-rw-r--r-- | arch/arm/kvm/interrupts_head.S | 12 | ||||
-rw-r--r-- | arch/arm/mach-hisi/Kconfig | 2 | ||||
-rw-r--r-- | arch/arm/mach-hisi/Makefile | 3 | ||||
-rw-r--r-- | arch/arm/mach-hisi/hip04.c | 382 | ||||
-rw-r--r-- | arch/arm/mach-hisi/hisilicon.c | 37 | ||||
-rw-r--r-- | drivers/irqchip/irq-gic.c | 84 | ||||
-rw-r--r-- | include/linux/irqchip/arm-gic.h | 2 | ||||
-rw-r--r-- | include/linux/irqchip/hip04-gic.h | 32 | ||||
-rw-r--r-- | virt/kvm/arm/vgic.c | 5 |
12 files changed, 531 insertions, 78 deletions
diff --git a/arch/arm/boot/dts/hip04.dtsi b/arch/arm/boot/dts/hip04.dtsi index c7d3bc59047f..834dde46df41 100644 --- a/arch/arm/boot/dts/hip04.dtsi +++ b/arch/arm/boot/dts/hip04.dtsi @@ -11,7 +11,6 @@ * publishhed by the Free Software Foundation. */ -#include "skeleton.dtsi" #include <dt-bindings/clock/hip04-clock.h> / { @@ -27,11 +26,42 @@ #address-cells = <1>; #size-cells = <0>; - cpu@0 { + cpu-map { + cluster0 { + core0 { + cpu = <&CPU0>; + }; + core1 { + cpu = <&CPU1>; + }; + core2 { + cpu = <&CPU2>; + }; + core3 { + cpu = <&CPU3>; + }; + }; + }; + CPU0: cpu@0 { device_type = "cpu"; compatible = "arm,cortex-a15"; reg = <0>; }; + CPU1: cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <1>; + }; + CPU2: cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <2>; + }; + CPU3: cpu@3 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <3>; + }; }; soc { @@ -44,7 +74,7 @@ ranges = <0 0 0xe0000000 0x10000000>; gic: interrupt-controller@c01000 { - compatible = "arm,cortex-a15-gic"; + compatible = "hisilicon,hip04-gic"; #interrupt-cells = <3>; #address-cells = <0>; interrupt-controller; @@ -53,6 +83,12 @@ reg = <0xc01000 0x1000>, <0xc02000 0x1000>; }; + mcpm: mcpm { + compatible = "hisilicon,hip04-mcpm"; + reg = <0x100 0x1000>, <0x3e00000 0x00100000>, + <0x302a000 0x1000>; + }; + clock: clock { compatible = "hisilicon,hip04-clock"; /* FIXME: the base of clock controller */ diff --git a/arch/arm/configs/hip04_defconfig b/arch/arm/configs/hip04_defconfig index aadb31736cab..473dc5bcaae8 100644 --- a/arch/arm/configs/hip04_defconfig +++ b/arch/arm/configs/hip04_defconfig @@ -8,6 +8,7 @@ CONFIG_BLK_DEV_INITRD=y CONFIG_RD_GZIP=y CONFIG_ARCH_HIP04=y CONFIG_SMP=y +CONFIG_NR_CPUS=16 CONFIG_PREEMPT=y CONFIG_AEABI=y CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y @@ -25,6 +26,9 @@ CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y +CONFIG_BLK_DEV=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 CONFIG_BLK_DEV_SD=y CONFIG_ATA=y CONFIG_SATA_AHCI_PLATFORM=y @@ -52,5 +56,7 @@ CONFIG_ROOT_NFS=y CONFIG_PRINTK_TIME=y CONFIG_DEBUG_FS=y CONFIG_DEBUG_KERNEL=y +CONFIG_DEBUG_LL=y +CONFIG_EARLY_PRINTK=y CONFIG_LOCKUP_DETECTOR=y CONFIG_DEBUG_USER=y diff --git a/arch/arm/include/asm/mcpm.h b/arch/arm/include/asm/mcpm.h index 608516ebabfe..68f82cf323d2 100644 --- a/arch/arm/include/asm/mcpm.h +++ b/arch/arm/include/asm/mcpm.h @@ -20,7 +20,7 @@ * to consider dynamic allocation. */ #define MAX_CPUS_PER_CLUSTER 4 -#define MAX_NR_CLUSTERS 2 +#define MAX_NR_CLUSTERS 4 #ifndef __ASSEMBLY__ diff --git a/arch/arm/kvm/interrupts_head.S b/arch/arm/kvm/interrupts_head.S index 6f18695a09cb..5b86191121a8 100644 --- a/arch/arm/kvm/interrupts_head.S +++ b/arch/arm/kvm/interrupts_head.S @@ -1,4 +1,5 @@ #include <linux/irqchip/arm-gic.h> +#include <linux/irqchip/hip04-gic.h> #define VCPU_USR_REG(_reg_nr) (VCPU_USR_REGS + (_reg_nr * 4)) #define VCPU_USR_SP (VCPU_USR_REG(13)) @@ -411,7 +412,11 @@ vcpu .req r0 @ vcpu pointer always in r0 ldr r7, [r2, #GICH_EISR1] ldr r8, [r2, #GICH_ELRSR0] ldr r9, [r2, #GICH_ELRSR1] - ldr r10, [r2, #GICH_APR] + /* The GICH_APR offset is different between ARM_GIC and HIP04_GIC */ + ldr r10, =BSYM(arm_gic_im) + tst r10, #ARM_GIC_IMPLEMENTATION + ldreq r10, [r2, #GICH_APR] + ldrne r10, [r2, #HIP04_GICH_APR] str r3, [r11, #VGIC_CPU_HCR] str r4, [r11, #VGIC_CPU_VMCR] @@ -427,7 +432,10 @@ vcpu .req r0 @ vcpu pointer always in r0 str r5, [r2, #GICH_HCR] /* Save list registers */ - add r2, r2, #GICH_LR0 + ldr r10, =BSYM(arm_gic_im) + tst r10, #ARM_GIC_IMPLEMENTATION + addeq r2, r2, #GICH_LR0 + addne r2, r2, #HIP04_GICH_LR0 add r3, r11, #VGIC_CPU_LR ldr r4, [r11, #VGIC_CPU_NR_LR] 1: ldr r6, [r2], #4 diff --git a/arch/arm/mach-hisi/Kconfig b/arch/arm/mach-hisi/Kconfig index 33852ca2dd61..2468678fe406 100644 --- a/arch/arm/mach-hisi/Kconfig +++ b/arch/arm/mach-hisi/Kconfig @@ -25,11 +25,11 @@ config ARCH_HIP04 select ARM_GIC select ARM_LPAE select ARM_TIMER_SP804 - select CACHE_L2X0 select CLKSRC_OF select GENERIC_CLOCKEVENTS select HAVE_ARM_ARCH_TIMER select HAVE_SMP + select MCPM select SMP help Support for Hisilicon HiP0x processor family diff --git a/arch/arm/mach-hisi/Makefile b/arch/arm/mach-hisi/Makefile index 6870058d0a48..31a5511f7cc7 100644 --- a/arch/arm/mach-hisi/Makefile +++ b/arch/arm/mach-hisi/Makefile @@ -2,6 +2,7 @@ # Makefile for Hisilicon processors family # -obj-y += hisilicon.o +obj-$(CONFIG_ARCH_HI3xxx) += hisilicon.o +obj-$(CONFIG_ARCH_HIP04) += hip04.o obj-$(CONFIG_SMP) += platsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o diff --git a/arch/arm/mach-hisi/hip04.c b/arch/arm/mach-hisi/hip04.c new file mode 100644 index 000000000000..3997db38fcb4 --- /dev/null +++ b/arch/arm/mach-hisi/hip04.c @@ -0,0 +1,382 @@ +/* + * (Hisilicon's HiP04 SoC) flattened device tree enabled machine + * + * Copyright (c) 2013-2014 Hisilicon Ltd. + * Copyright (c) 2013-2014 Linaro Ltd. + * + * Author: Haojian Zhuang <haojian.zhuang@linaro.org> + * + * 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/ahci_platform.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/irqchip/arm-gic.h> +#include <linux/memblock.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/reboot.h> +#include <linux/slab.h> + +#include <asm/cp15.h> +#include <asm/cputype.h> +#include <asm/mcpm.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> + +#include "ahci_vsemiphy.c" + +#define BOOTWRAPPER_PHYS 0x10c00000 +#define BOOTWRAPPER_MAGIC 0xa5a5a5a5 +#define BOOTWRAPPER_SIZE 0x00010000 + +/* bits definition in SC_CPU_RESET_REQ[x]/SC_CPU_RESET_DREQ[x] + * 1 -- unreset; 0 -- reset + */ +#define CORE_RESET_BIT(x) (1 << x) +#define NEON_RESET_BIT(x) (1 << (x + 4)) +#define CORE_DEBUG_RESET_BIT(x) (1 << (x + 9)) +#define CLUSTER_L2_RESET_BIT (1 << 8) +#define CLUSTER_DEBUG_RESET_BIT (1 << 13) + +/* + * bits definition in SC_CPU_RESET_STATUS[x] + * 1 -- reset status; 0 -- unreset status + */ +#define CORE_RESET_STATUS(x) (1 << x) +#define NEON_RESET_STATUS(x) (1 << (x + 4)) +#define CORE_DEBUG_RESET_STATUS(x) (1 << (x + 9)) +#define CLUSTER_L2_RESET_STATUS (1 << 8) +#define CLUSTER_DEBUG_RESET_STATUS (1 << 13) +#define CORE_WFI_STATUS(x) (1 << (x + 16)) +#define CORE_WFE_STATUS(x) (1 << (x + 20)) +#define CORE_DEBUG_ACK(x) (1 << (x + 24)) + +#define SC_CPU_RESET_REQ(x) (0x520 + (x << 3)) /* reset */ +#define SC_CPU_RESET_DREQ(x) (0x524 + (x << 3)) /* unreset */ +#define SC_CPU_RESET_STATUS(x) (0x1520 + (x << 3)) + +#define FAB_SF_MODE 0x0c +#define FAB_SF_INVLD 0x10 + +/* bits definition in FB_SF_INVLD */ +#define FB_SF_INVLD_START (1 << 8) + +#define HIP04_MAX_CLUSTERS 4 +#define HIP04_MAX_CPUS_PER_CLUSTER 4 + +static void __iomem *relocation = NULL, *sysctrl = NULL, *fabric = NULL; +static int hip04_cpu_table[HIP04_MAX_CLUSTERS][HIP04_MAX_CPUS_PER_CLUSTER]; +static DEFINE_SPINLOCK(boot_lock); + +static void __iomem *gb3 = NULL; /* gpio bank3 to control watchdog */ + +static bool hip04_cluster_down(unsigned int cluster) +{ + int i; + + for (i = 0; i < HIP04_MAX_CPUS_PER_CLUSTER; i++) + if (hip04_cpu_table[cluster][i]) + return false; + return true; +} + +static void hip04_set_snoop_filter(unsigned int cluster, unsigned int on) +{ + unsigned long data; + + if (!fabric) + return; + data = readl_relaxed(fabric + FAB_SF_MODE); + if (on) + data |= 1 << cluster; + else + data &= ~(1 << cluster); + writel_relaxed(data, fabric + FAB_SF_MODE); + while (1) { + if (data == readl_relaxed(fabric + FAB_SF_MODE)) + break; + } +} + +static int hip04_mcpm_power_up(unsigned int cpu, unsigned int cluster) +{ + unsigned long data, mask; + + if (!relocation || !sysctrl) + return -ENODEV; + if (cluster >= HIP04_MAX_CLUSTERS || cpu >= HIP04_MAX_CPUS_PER_CLUSTER) + return -EINVAL; + + spin_lock(&boot_lock); + writel_relaxed(BOOTWRAPPER_PHYS, relocation); + writel_relaxed(BOOTWRAPPER_MAGIC, relocation + 4); + writel_relaxed(virt_to_phys(mcpm_entry_point), relocation + 8); + writel_relaxed(0, relocation + 12); + + if (hip04_cluster_down(cluster)) { + data = CLUSTER_L2_RESET_BIT | CLUSTER_DEBUG_RESET_BIT; + writel_relaxed(data, sysctrl + SC_CPU_RESET_DREQ(cluster)); + do { + mask = CLUSTER_L2_RESET_STATUS | \ + CLUSTER_DEBUG_RESET_STATUS; + data = readl_relaxed(sysctrl + \ + SC_CPU_RESET_STATUS(cluster)); + } while (data & mask); + hip04_set_snoop_filter(cluster, 1); + } + + hip04_cpu_table[cluster][cpu]++; + + data = CORE_RESET_BIT(cpu) | NEON_RESET_BIT(cpu) | \ + CORE_DEBUG_RESET_BIT(cpu); + writel_relaxed(data, sysctrl + SC_CPU_RESET_DREQ(cluster)); + spin_unlock(&boot_lock); + + return 0; +} + +static void hip04_mcpm_power_down(void) +{ + unsigned int mpidr, cpu, cluster; + unsigned int v; + + spin_lock(&boot_lock); + spin_unlock(&boot_lock); + + mpidr = read_cpuid_mpidr(); + cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); + cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); + + local_irq_disable(); + gic_cpu_if_down(); + + __mcpm_cpu_down(cpu, cluster); + + asm volatile( + " mrc p15, 0, %0, c1, c0, 0\n" + " bic %0, %0, %1\n" + " mcr p15, 0, %0, c1, c0, 0\n" + : "=&r" (v) + : "Ir" (CR_C) + : "cc"); + + flush_cache_louis(); + + asm volatile( + /* + * Turn off coherency + */ + " mrc p15, 0, %0, c1, c0, 1\n" + " bic %0, %0, %1\n" + " mcr p15, 0, %0, c1, c0, 1\n" + : "=&r" (v) + : "Ir" (0x40) + : "cc"); + + isb(); + dsb(); + +#if 0 + /* system hang when writing to SC_CPU_RESET_REQ(cluster) register */ + data = CORE_RESET_BIT(cpu) | NEON_RESET_BIT(cpu); + writel_relaxed(data, sysctrl + SC_CPU_RESET_REQ(cluster)); +#endif +} + +static int hip04_mcpm_power_down_finish(unsigned int cpu, unsigned int cluster) +{ + int ret = -EBUSY; + + spin_lock(&boot_lock); + BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP); + __mcpm_cpu_going_down(cpu, cluster); + + hip04_cpu_table[cluster][cpu]--; + if (hip04_cpu_table[cluster][cpu]) { + pr_err("Cluster %d CPU%d is still running\n", cluster, cpu); + goto out; + } + ret = 0; +out: + spin_unlock(&boot_lock); + return ret; +} + +static void hip04_mcpm_powered_up(void) +{ + if (!relocation) + return; + spin_lock(&boot_lock); + writel_relaxed(0, relocation); + writel_relaxed(0, relocation + 4); + writel_relaxed(0, relocation + 8); + writel_relaxed(0, relocation + 12); + spin_unlock(&boot_lock); +} + +static const struct mcpm_platform_ops hip04_mcpm_ops = { + .power_up = hip04_mcpm_power_up, + .power_down = hip04_mcpm_power_down, + .power_down_finish = hip04_mcpm_power_down_finish, + .powered_up = hip04_mcpm_powered_up, +}; + +static bool __init hip04_cpu_table_init(void) +{ + unsigned int mpidr, cpu, cluster; + + mpidr = read_cpuid_mpidr(); + cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); + cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); + + if (cluster >= HIP04_MAX_CLUSTERS || + cpu >= HIP04_MAX_CPUS_PER_CLUSTER) { + pr_err("%s: boot CPU is out of bound!\n", __func__); + return false; + } + hip04_set_snoop_filter(cluster, 1); + hip04_cpu_table[cluster][cpu] = 1; + return true; +} + +static int __init hip04_mcpm_init(void) +{ + struct device_node *np; + int ret = -ENODEV; + + np = of_find_compatible_node(NULL, NULL, "hisilicon,hip04-mcpm"); + if (!np) { + pr_err("failed to find hisilicon,hip04-mcpm node\n"); + goto err; + } + relocation = of_iomap(np, 0); + if (!relocation) { + pr_err("failed to get relocation space\n"); + ret = -ENOMEM; + goto err; + } + sysctrl = of_iomap(np, 1); + if (!sysctrl) { + pr_err("failed to get sysctrl base\n"); + ret = -ENOMEM; + goto err_sysctrl; + } + fabric = of_iomap(np, 2); + if (!fabric) { + pr_err("failed to get fabric base\n"); + ret = -ENOMEM; + goto err_fabric; + } + if (!hip04_cpu_table_init()) + return -EINVAL; + ret = mcpm_platform_register(&hip04_mcpm_ops); + if (!ret) { + mcpm_sync_init(NULL); + pr_info("HiP04 MCPM initialized\n"); + } + return ret; +err_fabric: + iounmap(sysctrl); +err_sysctrl: + iounmap(relocation); +err: + return ret; +} +early_initcall(hip04_mcpm_init); + +bool __init hip04_smp_init_ops(void) +{ + mcpm_smp_set_ops(); + return true; +} + +static const char *hip04_compat[] __initconst = { + "hisilicon,hip04-d01", + NULL, +}; + +#define HIP04_SATA_BASE (0xea000000) + +static int sata_vsemiphy_init(struct device *dev, void __iomem *addr) +{ + hi_vsemi_init(addr); + return 0; +} + +static struct ahci_platform_data hip04_sata_pdata = { + .init = sata_vsemiphy_init, +}; + +static struct of_dev_auxdata hip04_auxdata_lookup[] __initdata = { + OF_DEV_AUXDATA("hisilicon,hisi-ahci", HIP04_SATA_BASE, + NULL, &hip04_sata_pdata), + {}, +}; + +static void __init hip04_init_machine(void) +{ + unsigned int data, mask; + of_platform_populate(NULL, of_default_bus_match_table, + hip04_auxdata_lookup, NULL); + + gb3 = ioremap(0xe4003000, 0x1000); + if (!gb3) { + pr_err("failed to map GB3\n"); + return; + } + mask = 0xffdfffff; + /* read GPIO3_SWPORT_DDR */ + data = readl_relaxed(gb3 + 4); + if (!(data & ~mask)) { + /* switch GPIO direction from IN to OUT */ + writel_relaxed(data | ~mask, gb3 + 4); + } + data = readl_relaxed(gb3); + /* write high to GPIO117 to disable watchdog */ + writel_relaxed(data | ~mask, gb3); +} + +static void hip04_restart(enum reboot_mode mode, const char *cmd) +{ + unsigned int data, mask; + + if (!gb3) { + pr_err("GB3 isn't initialized\n"); + return; + } + mask = 0xffdfffff; + /* read GPIO3_SWPORT_DDR */ + data = readl_relaxed(gb3 + 4); + if (!(data & ~mask)) { + /* switch GPIO direction from IN to OUT */ + writel_relaxed(data | ~mask, gb3 + 4); + } + data = readl_relaxed(gb3); + /* write low to GPIO117 */ + writel_relaxed(data & mask, gb3); + udelay(100); + /* write high to GPIO117 to disable watchdog */ + writel_relaxed(data | ~mask, gb3); + udelay(100); + /* write low to GPIO117 */ + writel_relaxed(data & mask, gb3); + udelay(100); +} + +static void __init hip04_reserve(void) +{ + memblock_reserve(BOOTWRAPPER_PHYS, BOOTWRAPPER_SIZE); +} + +DT_MACHINE_START(HIP01, "Hisilicon HiP04 (Flattened Device Tree)") + .dt_compat = hip04_compat, + .smp_init = smp_init_ops(hip04_smp_init_ops), + .init_machine = hip04_init_machine, + .restart = hip04_restart, + .reserve = hip04_reserve, +MACHINE_END diff --git a/arch/arm/mach-hisi/hisilicon.c b/arch/arm/mach-hisi/hisilicon.c index bf36cf42ab00..741faf3e7100 100644 --- a/arch/arm/mach-hisi/hisilicon.c +++ b/arch/arm/mach-hisi/hisilicon.c @@ -16,7 +16,6 @@ #include <linux/irqchip.h> #include <linux/of_address.h> #include <linux/of_platform.h> -#include <linux/ahci_platform.h> #include <asm/proc-fns.h> @@ -24,7 +23,6 @@ #include <asm/mach/map.h> #include "core.h" -#include "ahci_vsemiphy.c" #define HI3620_SYSCTRL_PHYS_BASE 0xfc802000 #define HI3620_SYSCTRL_VIRT_BASE 0xfe802000 @@ -90,38 +88,3 @@ DT_MACHINE_START(HI3620, "Hisilicon Hi3620 (Flattened Device Tree)") .smp = smp_ops(hi3xxx_smp_ops), .restart = hi3xxx_restart, MACHINE_END - - -#define HIP04_SATA_BASE (0xea000000) - -static int sata_vsemiphy_init(struct device *dev, void __iomem *addr) -{ - hi_vsemi_init(addr); - return 0; -} - -static struct ahci_platform_data hip04_sata_pdata = { - .init = sata_vsemiphy_init, -}; - -static struct of_dev_auxdata hip04_auxdata_lookup[] __initdata = { - OF_DEV_AUXDATA("hisilicon,hisi-ahci", HIP04_SATA_BASE, - NULL, &hip04_sata_pdata), - {}, -}; - -static void __init hip04_init_machine(void) -{ - of_platform_populate(NULL, of_default_bus_match_table, - hip04_auxdata_lookup, NULL); -} - -static const char *hip04_compat[] __initconst = { - "hisilicon,hip04-d01", - NULL, -}; - -DT_MACHINE_START(HIP01, "Hisilicon HiP04 (Flattened Device Tree)") - .dt_compat = hip04_compat, - .init_machine = hip04_init_machine, -MACHINE_END diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 9031171c141b..f2cc7eb4c3a8 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -41,6 +41,7 @@ #include <linux/slab.h> #include <linux/irqchip/chained_irq.h> #include <linux/irqchip/arm-gic.h> +#include <linux/irqchip/hip04-gic.h> #include <asm/irq.h> #include <asm/exception.h> @@ -76,9 +77,13 @@ static DEFINE_RAW_SPINLOCK(irq_controller_lock); * The GIC mapping of CPU interfaces does not necessarily match * the logical CPU numbering. Let's use a mapping as returned * by the GIC itself. + * + * Hisilicon HiP04 extends the number of CPU interface from 8 to 16. */ -#define NR_GIC_CPU_IF 8 -static u8 gic_cpu_map[NR_GIC_CPU_IF] __read_mostly; +#define MAX_NR_GIC_CPU_IF 16 +static u16 gic_cpu_map[MAX_NR_GIC_CPU_IF] __read_mostly; +static int nr_gic_cpu_if = 8; /* The standard GIC supports 8 CPUs */ +unsigned long arm_gic_im = ARM_GIC_IMPLEMENTATION; /* * Supported arch specific GIC irq extension. @@ -245,16 +250,19 @@ static int gic_retrigger(struct irq_data *d) static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, bool force) { - void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + (gic_irq(d) & ~3); - unsigned int shift = (gic_irq(d) % 4) * 8; + void __iomem *reg; + unsigned int shift, step; unsigned int cpu = cpumask_any_and(mask_val, cpu_online_mask); u32 val, mask, bit; - - if (cpu >= NR_GIC_CPU_IF || cpu >= nr_cpu_ids) + if (cpu >= nr_gic_cpu_if || cpu >= nr_cpu_ids) return -EINVAL; + step = BITS_PER_LONG / nr_gic_cpu_if; + shift = (gic_irq(d) % step) * nr_gic_cpu_if; + reg = gic_dist_base(d) + GIC_DIST_TARGET + (gic_irq(d) / step * 4); + raw_spin_lock(&irq_controller_lock); - mask = 0xff << shift; + mask = ((1 << nr_gic_cpu_if) - 1) << shift; bit = gic_cpu_map[cpu] << shift; val = readl_relaxed(reg) & ~mask; writel_relaxed(val | bit, reg); @@ -287,7 +295,7 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs do { irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK); - irqnr = irqstat & ~0x1c00; + irqnr = irqstat & GICC_IAR_INTID; if (likely(irqnr > 15 && irqnr < 1021)) { irqnr = irq_find_mapping(gic->domain, irqnr); @@ -354,15 +362,17 @@ void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq) irq_set_chained_handler(irq, gic_handle_cascade_irq); } -static u8 gic_get_cpumask(struct gic_chip_data *gic) +static u16 gic_get_cpumask(struct gic_chip_data *gic) { void __iomem *base = gic_data_dist_base(gic); - u32 mask, i; - - for (i = mask = 0; i < 32; i += 4) { - mask = readl_relaxed(base + GIC_DIST_TARGET + i); - mask |= mask >> 16; - mask |= mask >> 8; + u32 mask, i, j, step, nr; + + /* get the number of CPU fields in GIC_DIST_TARGET register */ + step = BITS_PER_LONG / nr_gic_cpu_if; + for (i = mask = 0; i < 32; i += step) { + mask = readl_relaxed(base + GIC_DIST_TARGET + i / step * 4); + for (j = BITS_PER_LONG >> 1; j >= nr_gic_cpu_if; j >>= 1) + mask |= mask >> j; if (mask) break; } @@ -375,7 +385,7 @@ static u8 gic_get_cpumask(struct gic_chip_data *gic) static void __init gic_dist_init(struct gic_chip_data *gic) { - unsigned int i; + unsigned int i, step; u32 cpumask; unsigned int gic_irqs = gic->gic_irqs; void __iomem *base = gic_data_dist_base(gic); @@ -392,10 +402,11 @@ static void __init gic_dist_init(struct gic_chip_data *gic) * Set all global interrupts to this CPU only. */ cpumask = gic_get_cpumask(gic); - cpumask |= cpumask << 8; - cpumask |= cpumask << 16; - for (i = 32; i < gic_irqs; i += 4) - writel_relaxed(cpumask, base + GIC_DIST_TARGET + i * 4 / 4); + for (i = nr_gic_cpu_if; i < BITS_PER_LONG; i <<= 1) + cpumask |= cpumask << i; + step = BITS_PER_LONG / nr_gic_cpu_if; + for (i = 32; i < gic_irqs; i += step) + writel_relaxed(cpumask, base + GIC_DIST_TARGET + i / step * 4); /* * Set priority on all global interrupts. @@ -423,7 +434,7 @@ static void gic_cpu_init(struct gic_chip_data *gic) /* * Get what the GIC says our CPU mask is. */ - BUG_ON(cpu >= NR_GIC_CPU_IF); + BUG_ON(cpu >= nr_gic_cpu_if); cpu_mask = gic_get_cpumask(gic); gic_cpu_map[cpu] = cpu_mask; @@ -431,7 +442,7 @@ static void gic_cpu_init(struct gic_chip_data *gic) * Clear our mask from the other map entries in case they're * still undefined. */ - for (i = 0; i < NR_GIC_CPU_IF; i++) + for (i = 0; i < nr_gic_cpu_if; i++) if (i != cpu) gic_cpu_map[i] &= ~cpu_mask; @@ -469,7 +480,7 @@ static void gic_dist_save(unsigned int gic_nr) { unsigned int gic_irqs; void __iomem *dist_base; - int i; + int i, step; if (gic_nr >= MAX_GIC_NR) BUG(); @@ -484,7 +495,8 @@ static void gic_dist_save(unsigned int gic_nr) gic_data[gic_nr].saved_spi_conf[i] = readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4); - for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++) + step = BITS_PER_LONG / nr_gic_cpu_if; + for (i = 0; i < DIV_ROUND_UP(gic_irqs, step); i++) gic_data[gic_nr].saved_spi_target[i] = readl_relaxed(dist_base + GIC_DIST_TARGET + i * 4); @@ -503,7 +515,7 @@ static void gic_dist_save(unsigned int gic_nr) static void gic_dist_restore(unsigned int gic_nr) { unsigned int gic_irqs; - unsigned int i; + unsigned int i, step; void __iomem *dist_base; if (gic_nr >= MAX_GIC_NR) @@ -525,7 +537,8 @@ static void gic_dist_restore(unsigned int gic_nr) writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4); - for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++) + step = BITS_PER_LONG / nr_gic_cpu_if; + for (i = 0; i < DIV_ROUND_UP(gic_irqs, step); i++) writel_relaxed(gic_data[gic_nr].saved_spi_target[i], dist_base + GIC_DIST_TARGET + i * 4); @@ -666,8 +679,8 @@ void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) dsb(); /* this always happens on GIC0 */ - writel_relaxed(map << 16 | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT); - + writel_relaxed(map << (8 + 16 - nr_gic_cpu_if) | irq, + gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT); raw_spin_unlock_irqrestore(&irq_controller_lock, flags); } #endif @@ -681,7 +694,7 @@ void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) */ void gic_send_sgi(unsigned int cpu_id, unsigned int irq) { - BUG_ON(cpu_id >= NR_GIC_CPU_IF); + BUG_ON(cpu_id >= nr_gic_cpu_if); cpu_id = 1 << cpu_id; /* this always happens on GIC0 */ writel_relaxed((cpu_id << 16) | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT); @@ -700,7 +713,7 @@ int gic_get_cpu_id(unsigned int cpu) { unsigned int cpu_bit; - if (cpu >= NR_GIC_CPU_IF) + if (cpu >= nr_gic_cpu_if) return -1; cpu_bit = gic_cpu_map[cpu]; if (cpu_bit & (cpu_bit - 1)) @@ -920,8 +933,8 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, * Initialize the CPU interface map to all CPUs. * It will be refined as each CPU probes its ID. */ - for (i = 0; i < NR_GIC_CPU_IF; i++) - gic_cpu_map[i] = 0xff; + for (i = 0; i < nr_gic_cpu_if; i++) + gic_cpu_map[i] = (1 << MAX_NR_GIC_CPU_IF) - 1; /* * For primary GICs, skip over SGIs. @@ -983,6 +996,12 @@ int __init gic_of_init(struct device_node *node, struct device_node *parent) if (WARN_ON(!node)) return -ENODEV; + /* HiP04 supports 16 CPUs at most */ + if (of_device_is_compatible(node, "hisilicon,hip04-gic")) { + nr_gic_cpu_if = 16; + arm_gic_im = HIP04_GIC_IMPLEMENTATION; + } + dist_base = of_iomap(node, 0); WARN(!dist_base, "unable to map gic dist registers\n"); @@ -1005,6 +1024,7 @@ int __init gic_of_init(struct device_node *node, struct device_node *parent) } IRQCHIP_DECLARE(cortex_a15_gic, "arm,cortex-a15-gic", gic_of_init); IRQCHIP_DECLARE(cortex_a9_gic, "arm,cortex-a9-gic", gic_of_init); +IRQCHIP_DECLARE(hip04_gic, "hisilicon,hip04-gic", gic_of_init); IRQCHIP_DECLARE(msm_8660_qgic, "qcom,msm-8660-qgic", gic_of_init); IRQCHIP_DECLARE(msm_qgic2, "qcom,msm-qgic2", gic_of_init); diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h index cac496b1e279..287fe1852727 100644 --- a/include/linux/irqchip/arm-gic.h +++ b/include/linux/irqchip/arm-gic.h @@ -18,6 +18,8 @@ #define GIC_CPU_RUNNINGPRI 0x14 #define GIC_CPU_HIGHPRI 0x18 +#define GICC_IAR_INTID 0x3ff + #define GIC_DIST_CTRL 0x000 #define GIC_DIST_CTR 0x004 #define GIC_DIST_IGROUP 0x080 diff --git a/include/linux/irqchip/hip04-gic.h b/include/linux/irqchip/hip04-gic.h new file mode 100644 index 000000000000..7f9f06f1e0db --- /dev/null +++ b/include/linux/irqchip/hip04-gic.h @@ -0,0 +1,32 @@ +/* + * include/linux/irqchip/hip04-gic.h + * + * Copyright (C) 2013-2014 Hisilicon Limited, All Rights Reserved. + * Copyright (C) 2013-2014 Linaro Limited, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __LINUX_IRQCHIP_HIP04_GIC_H +#define __LINUX_IRQCHIP_HIP04_GIC_H + +/* GIC Implementation */ +#define ARM_GIC_IMPLEMENTATION (1 << 0) +#define HIP04_GIC_IMPLEMENTATION (1 << 1) + +#define HIP04_GIC_DIST_TARGET 0x800 + +#define HIP04_GIC_DIST_SGI_PENDING_CLEAR 0xf40 +#define HIP04_GIC_DIST_SGI_PENDING_SET 0xf80 + +#define HIP04_GICH_APR 0x70 +#define HIP04_GICH_LR0 0x80 + +#define HIP04_GICH_LR_PHYSID_CPUID (0xf << GICH_LR_PHYSID_CPUID_SHIFT) + +#ifndef __ASSEMBLY__ +extern unsigned long arm_gic_im; +#endif + +#endif /* __LINUX_IRQCHIP_HIP04_GIC_H */ diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c index 685fc72fc751..51a5821f576a 100644 --- a/virt/kvm/arm/vgic.c +++ b/virt/kvm/arm/vgic.c @@ -26,6 +26,7 @@ #include <linux/of_irq.h> #include <linux/irqchip/arm-gic.h> +#include <linux/irqchip/hip04-gic.h> #include <asm/kvm_emulate.h> #include <asm/kvm_arm.h> @@ -825,7 +826,9 @@ static void vgic_update_state(struct kvm *kvm) } #define LR_CPUID(lr) \ - (((lr) & GICH_LR_PHYSID_CPUID) >> GICH_LR_PHYSID_CPUID_SHIFT) + ((arm_gic_im & ARM_GIC_IMPLEMENTATION) ? \ + ((lr) & GICH_LR_PHYSID_CPUID) >> GICH_LR_PHYSID_CPUID_SHIFT : \ + ((lr) & HIP04_GICH_LR_PHYSID_CPUID) >> GICH_LR_PHYSID_CPUID_SHIFT) #define MK_LR_PEND(src, irq) \ (GICH_LR_PENDING_BIT | ((src) << GICH_LR_PHYSID_CPUID_SHIFT) | (irq)) |