#include <xen/errno.h>
#include <xen/softirq.h>
#include <xen/list.h>
+#include <xen/device_tree.h>
#include <asm/p2m.h>
#include <asm/domain.h>
/* Access to the GIC Distributor registers through the fixmap */
#define GICD ((volatile uint32_t *) FIXMAP_ADDR(FIXMAP_GICD))
-#define GICC ((volatile uint32_t *) (FIXMAP_ADDR(FIXMAP_GICC1) \
- + (GIC_CR_OFFSET & 0xfff)))
-#define GICH ((volatile uint32_t *) (FIXMAP_ADDR(FIXMAP_GICH) \
- + (GIC_HR_OFFSET & 0xfff)))
+#define GICC ((volatile uint32_t *) FIXMAP_ADDR(FIXMAP_GICC1))
+#define GICH ((volatile uint32_t *) FIXMAP_ADDR(FIXMAP_GICH))
static void gic_restore_pending_irqs(struct vcpu *v);
/* Global state */
paddr_t dbase; /* Address of distributor registers */
paddr_t cbase; /* Address of CPU interface registers */
paddr_t hbase; /* Address of virtual interface registers */
+ paddr_t vbase; /* Address of virtual cpu interface registers */
unsigned int lines;
unsigned int cpus;
spinlock_t lock;
/* Set up the GIC */
void __init gic_init(void)
{
- /* XXX FIXME get this from devicetree */
- gic.dbase = GIC_BASE_ADDRESS + GIC_DR_OFFSET;
- gic.cbase = GIC_BASE_ADDRESS + GIC_CR_OFFSET;
- gic.hbase = GIC_BASE_ADDRESS + GIC_HR_OFFSET;
+ printk("GIC initialization:\n"
+ " gic_dist_addr=%"PRIpaddr"\n"
+ " gic_cpu_addr=%"PRIpaddr"\n"
+ " gic_hyp_addr=%"PRIpaddr"\n"
+ " gic_vcpu_addr=%"PRIpaddr"\n",
+ early_info.gic.gic_dist_addr, early_info.gic.gic_cpu_addr,
+ early_info.gic.gic_hyp_addr, early_info.gic.gic_vcpu_addr);
+ if ( !early_info.gic.gic_dist_addr ||
+ !early_info.gic.gic_cpu_addr ||
+ !early_info.gic.gic_hyp_addr ||
+ !early_info.gic.gic_vcpu_addr )
+ panic("the physical address of one of the GIC interfaces is missing\n");
+ if ( (early_info.gic.gic_dist_addr & ~PAGE_MASK) ||
+ (early_info.gic.gic_cpu_addr & ~PAGE_MASK) ||
+ (early_info.gic.gic_hyp_addr & ~PAGE_MASK) ||
+ (early_info.gic.gic_vcpu_addr & ~PAGE_MASK) )
+ panic("GIC interfaces not page aligned.\n");
+
+ gic.dbase = early_info.gic.gic_dist_addr;
+ gic.cbase = early_info.gic.gic_cpu_addr;
+ gic.hbase = early_info.gic.gic_hyp_addr;
+ gic.vbase = early_info.gic.gic_vcpu_addr;
set_fixmap(FIXMAP_GICD, gic.dbase >> PAGE_SHIFT, DEV_SHARED);
BUILD_BUG_ON(FIXMAP_ADDR(FIXMAP_GICC1) !=
FIXMAP_ADDR(FIXMAP_GICC2)-PAGE_SIZE);
{
/* map the gic virtual cpu interface in the gic cpu interface region of
* the guest */
- return map_mmio_regions(d, GIC_BASE_ADDRESS + GIC_CR_OFFSET,
- GIC_BASE_ADDRESS + GIC_CR_OFFSET + (2 * PAGE_SIZE) - 1,
- GIC_BASE_ADDRESS + GIC_VR_OFFSET);
+ return map_mmio_regions(d, gic.cbase,
+ gic.cbase + (2 * PAGE_SIZE) - 1,
+ gic.vbase);
}
static void maintenance_interrupt(int irq, void *dev_id, struct cpu_user_regs *regs)
return !strncmp(prop, match, len);
}
+bool_t device_tree_node_compatible(const void *fdt, int node, const char *match)
+{
+ int len, l;
+ const void *prop;
+
+ prop = fdt_getprop(fdt, node, "compatible", &len);
+ if ( prop == NULL )
+ return 0;
+
+ while ( len > 0 ) {
+ if ( !strcmp(prop, match) )
+ return 1;
+ l = strlen(prop) + 1;
+ prop += l;
+ len -= l;
+ }
+
+ return 0;
+}
+
+static int device_tree_nr_reg_ranges(const struct fdt_property *prop,
+ u32 address_cells, u32 size_cells)
+{
+ u32 reg_cells = address_cells + size_cells;
+ return fdt32_to_cpu(prop->len) / (reg_cells * sizeof(u32));
+}
+
static void __init get_val(const u32 **cell, u32 cells, u64 *val)
{
*val = 0;
u32 address_cells, u32 size_cells)
{
const struct fdt_property *prop;
- size_t reg_cells;
int i;
int banks;
const u32 *cell;
}
cell = (const u32 *)prop->data;
- reg_cells = address_cells + size_cells;
- banks = fdt32_to_cpu(prop->len) / (reg_cells * sizeof(u32));
+ banks = device_tree_nr_reg_ranges(prop, address_cells, size_cells);
for ( i = 0; i < banks && early_info.mem.nr_banks < NR_MEM_BANKS; i++ )
{
cpumask_set_cpu(start, &cpu_possible_map);
}
+static void __init process_gic_node(const void *fdt, int node,
+ const char *name,
+ u32 address_cells, u32 size_cells)
+{
+ const struct fdt_property *prop;
+ const u32 *cell;
+ paddr_t start, size;
+ int interfaces;
+
+ if ( address_cells < 1 || size_cells < 1 )
+ {
+ early_printk("fdt: node `%s': invalid #address-cells or #size-cells",
+ name);
+ return;
+ }
+
+ prop = fdt_get_property(fdt, node, "reg", NULL);
+ if ( !prop )
+ {
+ early_printk("fdt: node `%s': missing `reg' property\n", name);
+ return;
+ }
+
+ cell = (const u32 *)prop->data;
+ interfaces = device_tree_nr_reg_ranges(prop, address_cells, size_cells);
+ if ( interfaces < 4 )
+ {
+ early_printk("fdt: node `%s': not enough ranges\n", name);
+ return;
+ }
+ device_tree_get_reg(&cell, address_cells, size_cells, &start, &size);
+ early_info.gic.gic_dist_addr = start;
+ device_tree_get_reg(&cell, address_cells, size_cells, &start, &size);
+ early_info.gic.gic_cpu_addr = start;
+ device_tree_get_reg(&cell, address_cells, size_cells, &start, &size);
+ early_info.gic.gic_hyp_addr = start;
+ device_tree_get_reg(&cell, address_cells, size_cells, &start, &size);
+ early_info.gic.gic_vcpu_addr = start;
+}
+
static int __init early_scan_node(const void *fdt,
int node, const char *name, int depth,
u32 address_cells, u32 size_cells,
process_memory_node(fdt, node, name, address_cells, size_cells);
else if ( device_tree_type_matches(fdt, node, "cpu") )
process_cpu_node(fdt, node, name, address_cells, size_cells);
+ else if ( device_tree_node_compatible(fdt, node, "arm,cortex-a15-gic") )
+ process_gic_node(fdt, node, name, address_cells, size_cells);
return 0;
}