Commit 91a6c3ce authored by Andrew Jones's avatar Andrew Jones
Browse files

arm/arm64: add initial gicv3 support


Reviewed-by: default avatarAlex Bennée <alex.bennee@linaro.org>
Reviewed-by: default avatarEric Auger <eric.auger@redhat.com>
Signed-off-by: Andrew Jones's avatarAndrew Jones <drjones@redhat.com>
parent 845d3f1f
......@@ -49,7 +49,7 @@ cflatobjs += lib/arm/bitops.o
cflatobjs += lib/arm/psci.o
cflatobjs += lib/arm/smp.o
cflatobjs += lib/arm/delay.o
cflatobjs += lib/arm/gic.o
cflatobjs += lib/arm/gic.o lib/arm/gic-v2.o lib/arm/gic-v3.o
libeabi = lib/arm/libeabi.a
eabiobjs = lib/arm/eabi_compat.o
......
/*
* All ripped off from arch/arm/include/asm/arch_gicv3.h
*
* Copyright (C) 2016, Red Hat Inc, Andrew Jones <drjones@redhat.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.
*/
#ifndef _ASMARM_ARCH_GICV3_H_
#define _ASMARM_ARCH_GICV3_H_
#ifndef __ASSEMBLY__
#include <libcflat.h>
#include <asm/sysreg.h>
#include <asm/barrier.h>
#include <asm/io.h>
#define ICC_PMR __ACCESS_CP15(c4, 0, c6, 0)
#define ICC_IGRPEN1 __ACCESS_CP15(c12, 0, c12, 7)
static inline void gicv3_write_pmr(u32 val)
{
write_sysreg(val, ICC_PMR);
}
static inline void gicv3_write_grpen1(u32 val)
{
write_sysreg(val, ICC_IGRPEN1);
isb();
}
/*
* We may access GICR_TYPER and GITS_TYPER by reading both the TYPER
* offset and the following offset (+ 4) and then combining them to
* form a 64-bit address.
*/
static inline u64 gicv3_read_typer(const volatile void __iomem *addr)
{
u64 val = readl(addr);
val |= (u64)readl(addr + 4) << 32;
return val;
}
#endif /* !__ASSEMBLY__ */
#endif /* _ASMARM_ARCH_GICV3_H_ */
/*
* All GIC* defines are lifted from include/linux/irqchip/arm-gic-v3.h
*
* Copyright (C) 2016, Red Hat Inc, Andrew Jones <drjones@redhat.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.
*/
#ifndef _ASMARM_GIC_V3_H_
#define _ASMARM_GIC_V3_H_
#ifndef _ASMARM_GIC_H_
#error Do not directly include <asm/gic-v3.h>. Include <asm/gic.h>
#endif
/*
* Distributor registers
*
* We expect to be run in Non-secure mode, thus we define the
* group1 enable bits with respect to that view.
*/
#define GICD_CTLR_RWP (1U << 31)
#define GICD_CTLR_ARE_NS (1U << 4)
#define GICD_CTLR_ENABLE_G1A (1U << 1)
#define GICD_CTLR_ENABLE_G1 (1U << 0)
/* Re-Distributor registers, offsets from RD_base */
#define GICR_TYPER 0x0008
#define GICR_TYPER_LAST (1U << 4)
/* Re-Distributor registers, offsets from SGI_base */
#define GICR_IGROUPR0 GICD_IGROUPR
#define GICR_ISENABLER0 GICD_ISENABLER
#define GICR_IPRIORITYR0 GICD_IPRIORITYR
#include <asm/arch_gicv3.h>
#ifndef __ASSEMBLY__
#include <asm/setup.h>
#include <asm/processor.h>
#include <asm/delay.h>
#include <asm/smp.h>
#include <asm/io.h>
struct gicv3_data {
void *dist_base;
void *redist_base[NR_CPUS];
unsigned int irq_nr;
};
extern struct gicv3_data gicv3_data;
#define gicv3_dist_base() (gicv3_data.dist_base)
#define gicv3_redist_base() (gicv3_data.redist_base[smp_processor_id()])
#define gicv3_sgi_base() (gicv3_data.redist_base[smp_processor_id()] + SZ_64K)
extern int gicv3_init(void);
extern void gicv3_enable_defaults(void);
extern void gicv3_set_redist_base(size_t stride);
static inline void gicv3_do_wait_for_rwp(void *base)
{
int count = 100000; /* 1s */
while (readl(base + GICD_CTLR) & GICD_CTLR_RWP) {
if (!--count) {
printf("GICv3: RWP timeout!\n");
abort();
}
cpu_relax();
udelay(10);
};
}
static inline void gicv3_dist_wait_for_rwp(void)
{
gicv3_do_wait_for_rwp(gicv3_dist_base());
}
static inline void gicv3_redist_wait_for_uwp(void)
{
/*
* We can build on gic_do_wait_for_rwp, which uses GICD_ registers
* because GICD_CTLR == GICR_CTLR and GICD_CTLR_RWP == GICR_CTLR_UWP
*/
gicv3_do_wait_for_rwp(gicv3_redist_base());
}
static inline u32 mpidr_compress(u64 mpidr)
{
u64 compressed = mpidr & MPIDR_HWID_BITMASK;
compressed = (((compressed >> 32) & 0xff) << 24) | compressed;
return compressed;
}
static inline u64 mpidr_uncompress(u32 compressed)
{
u64 mpidr = ((u64)compressed >> 24) << 32;
mpidr |= compressed & MPIDR_HWID_BITMASK;
return mpidr;
}
#endif /* !__ASSEMBLY__ */
#endif /* _ASMARM_GIC_V3_H_ */
......@@ -6,11 +6,11 @@
#ifndef _ASMARM_GIC_H_
#define _ASMARM_GIC_H_
#include <asm/gic-v2.h>
/* Distributor registers */
#define GICD_CTLR 0x0000
#define GICD_TYPER 0x0004
#define GICD_IGROUPR 0x0080
#define GICD_ISENABLER 0x0100
#define GICD_IPRIORITYR 0x0400
#define GICD_SGIR 0x0f00
......@@ -28,6 +28,9 @@
#define GICC_INT_PRI_THRESHOLD 0xf0
#define GICC_INT_SPURIOUS 0x3ff
#include <asm/gic-v2.h>
#include <asm/gic-v3.h>
#ifndef __ASSEMBLY__
/*
......
/*
* Copyright (C) 2016, Red Hat Inc, Andrew Jones <drjones@redhat.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.
*/
#include <asm/gic.h>
#include <asm/io.h>
void gicv2_enable_defaults(void)
{
void *dist = gicv2_dist_base();
void *cpu_base = gicv2_cpu_base();
unsigned int i;
gicv2_data.irq_nr = GICD_TYPER_IRQS(readl(dist + GICD_TYPER));
if (gicv2_data.irq_nr > 1020)
gicv2_data.irq_nr = 1020;
for (i = 0; i < gicv2_data.irq_nr; i += 4)
writel(GICD_INT_DEF_PRI_X4, dist + GICD_IPRIORITYR + i);
writel(GICD_INT_EN_SET_SGI, dist + GICD_ISENABLER + 0);
writel(GICD_ENABLE, dist + GICD_CTLR);
writel(GICC_INT_PRI_THRESHOLD, cpu_base + GICC_PMR);
writel(GICC_ENABLE, cpu_base + GICC_CTLR);
}
/*
* Copyright (C) 2016, Red Hat Inc, Andrew Jones <drjones@redhat.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.
*/
#include <asm/gic.h>
#include <asm/io.h>
void gicv3_set_redist_base(size_t stride)
{
u32 aff = mpidr_compress(get_mpidr());
void *ptr = gicv3_data.redist_base[0];
u64 typer;
do {
typer = gicv3_read_typer(ptr + GICR_TYPER);
if ((typer >> 32) == aff) {
gicv3_redist_base() = ptr;
return;
}
ptr += stride; /* skip RD_base, SGI_base, etc. */
} while (!(typer & GICR_TYPER_LAST));
/* should never reach here */
assert(0);
}
void gicv3_enable_defaults(void)
{
void *dist = gicv3_dist_base();
void *sgi_base;
unsigned int i;
gicv3_data.irq_nr = GICD_TYPER_IRQS(readl(dist + GICD_TYPER));
if (gicv3_data.irq_nr > 1020)
gicv3_data.irq_nr = 1020;
writel(0, dist + GICD_CTLR);
gicv3_dist_wait_for_rwp();
writel(GICD_CTLR_ARE_NS | GICD_CTLR_ENABLE_G1A | GICD_CTLR_ENABLE_G1,
dist + GICD_CTLR);
gicv3_dist_wait_for_rwp();
for (i = 0; i < gicv3_data.irq_nr; i += 4)
writel(~0, dist + GICD_IGROUPR + i);
if (!gicv3_redist_base())
gicv3_set_redist_base(SZ_64K * 2);
sgi_base = gicv3_sgi_base();
writel(~0, sgi_base + GICR_IGROUPR0);
for (i = 0; i < 16; i += 4)
writel(GICD_INT_DEF_PRI_X4, sgi_base + GICR_IPRIORITYR0 + i);
writel(GICD_INT_EN_SET_SGI, sgi_base + GICR_ISENABLER0);
gicv3_write_pmr(GICC_INT_PRI_THRESHOLD);
gicv3_write_grpen1(1);
}
......@@ -8,9 +8,11 @@
#include <asm/io.h>
struct gicv2_data gicv2_data;
struct gicv3_data gicv3_data;
/*
* Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt
* Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt
*/
static bool
gic_get_dt_bases(const char *compatible, void **base1, void **base2)
......@@ -48,29 +50,17 @@ int gicv2_init(void)
&gicv2_data.dist_base, &gicv2_data.cpu_base);
}
int gicv3_init(void)
{
return gic_get_dt_bases("arm,gic-v3", &gicv3_data.dist_base,
&gicv3_data.redist_base[0]);
}
int gic_init(void)
{
if (gicv2_init())
return 2;
else if (gicv3_init())
return 3;
return 0;
}
void gicv2_enable_defaults(void)
{
void *dist = gicv2_dist_base();
void *cpu_base = gicv2_cpu_base();
unsigned int i;
gicv2_data.irq_nr = GICD_TYPER_IRQS(readl(dist + GICD_TYPER));
if (gicv2_data.irq_nr > 1020)
gicv2_data.irq_nr = 1020;
for (i = 0; i < gicv2_data.irq_nr; i += 4)
writel(GICD_INT_DEF_PRI_X4, dist + GICD_IPRIORITYR + i);
writel(GICD_INT_EN_SET_SGI, dist + GICD_ISENABLER + 0);
writel(GICD_ENABLE, dist + GICD_CTLR);
writel(GICC_INT_PRI_THRESHOLD, cpu_base + GICC_PMR);
writel(GICC_ENABLE, cpu_base + GICC_CTLR);
}
/*
* All ripped off from arch/arm64/include/asm/arch_gicv3.h
*
* Copyright (C) 2016, Red Hat Inc, Andrew Jones <drjones@redhat.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.
*/
#ifndef _ASMARM64_ARCH_GICV3_H_
#define _ASMARM64_ARCH_GICV3_H_
#include <asm/sysreg.h>
#define ICC_PMR_EL1 sys_reg(3, 0, 4, 6, 0)
#define ICC_GRPEN1_EL1 sys_reg(3, 0, 12, 12, 7)
#ifndef __ASSEMBLY__
#include <libcflat.h>
#include <asm/barrier.h>
/*
* Low-level accessors
*
* These system registers are 32 bits, but we make sure that the compiler
* sets the GP register's most significant bits to 0 with an explicit cast.
*/
static inline void gicv3_write_pmr(u32 val)
{
asm volatile("msr_s " xstr(ICC_PMR_EL1) ", %0" : : "r" ((u64)val));
}
static inline void gicv3_write_grpen1(u32 val)
{
asm volatile("msr_s " xstr(ICC_GRPEN1_EL1) ", %0" : : "r" ((u64)val));
isb();
}
#define gicv3_read_typer(c) readq(c)
#endif /* !__ASSEMBLY__ */
#endif /* _ASMARM64_ARCH_GICV3_H_ */
#include "../../arm/asm/gic-v3.h"
......@@ -8,7 +8,23 @@
#ifndef _ASMARM64_SYSREG_H_
#define _ASMARM64_SYSREG_H_
#ifndef __ASSEMBLY__
#define sys_reg(op0, op1, crn, crm, op2) \
((((op0)&3)<<19)|((op1)<<16)|((crn)<<12)|((crm)<<8)|((op2)<<5))
#ifdef __ASSEMBLY__
.irp num,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30
.equ .L__reg_num_x\num, \num
.endr
.equ .L__reg_num_xzr, 31
.macro mrs_s, rt, sreg
.inst 0xd5200000|(\sreg)|(.L__reg_num_\rt)
.endm
.macro msr_s, sreg, rt
.inst 0xd5000000|(\sreg)|(.L__reg_num_\rt)
.endm
#else
#include <libcflat.h>
#define read_sysreg(r) ({ \
......@@ -22,5 +38,19 @@
asm volatile("msr " xstr(r) ", %x0" : : "rZ" (__val)); \
} while (0)
#endif /* !__ASSEMBLY__ */
asm(
" .irp num,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30\n"
" .equ .L__reg_num_x\\num, \\num\n"
" .endr\n"
" .equ .L__reg_num_xzr, 31\n"
"\n"
" .macro mrs_s, rt, sreg\n"
" .inst 0xd5200000|(\\sreg)|(.L__reg_num_\\rt)\n"
" .endm\n"
"\n"
" .macro msr_s, sreg, rt\n"
" .inst 0xd5000000|(\\sreg)|(.L__reg_num_\\rt)\n"
" .endm\n"
);
#endif /* __ASSEMBLY__ */
#endif /* _ASMARM64_SYSREG_H_ */
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment