@@ -23,6 +23,8 @@
#define GICD_CTLR_ENABLE_G1A (1U << 1)
#define GICD_CTLR_ENABLE_G1 (1U << 0)
+#define GICD_IROUTER 0x6000
+
/* Re-Distributor registers, offsets from RD_base */
#define GICR_TYPER 0x0008
@@ -74,5 +74,13 @@ extern void gic_write_eoir(u32 irqstat);
extern void gic_ipi_send_single(int irq, int cpu);
extern void gic_ipi_send_mask(int irq, const cpumask_t *dest);
+void gic_set_irq_bit(int irq, int offset);
+void gic_enable_irq(int irq);
+void gic_disable_irq(int irq);
+void gic_set_irq_priority(int irq, u8 prio);
+void gic_set_irq_target(int irq, int cpu);
+void gic_set_irq_group(int irq, int group);
+int gic_get_irq_group(int irq);
+
#endif /* !__ASSEMBLY__ */
#endif /* _ASMARM_GIC_H_ */
@@ -146,3 +146,93 @@ void gic_ipi_send_mask(int irq, const cpumask_t *dest)
assert(gic_common_ops && gic_common_ops->ipi_send_mask);
gic_common_ops->ipi_send_mask(irq, dest);
}
+
+enum gic_bit_access {
+ ACCESS_READ,
+ ACCESS_SET,
+ ACCESS_RMW
+};
+
+static u8 gic_masked_irq_bits(int irq, int offset, int bits, u8 value,
+ enum gic_bit_access access)
+{
+ void *base;
+ int split = 32 / bits;
+ int shift = (irq % split) * bits;
+ u32 reg = 0, mask = ((1U << bits) - 1) << shift;
+
+ switch (gic_version()) {
+ case 2:
+ base = gicv2_dist_base();
+ break;
+ case 3:
+ if (irq < 32)
+ base = gicv3_sgi_base();
+ else
+ base = gicv3_dist_base();
+ break;
+ default:
+ return 0;
+ }
+ base += offset + (irq / split) * 4;
+
+ switch (access) {
+ case ACCESS_READ:
+ return (readl(base) & mask) >> shift;
+ case ACCESS_SET:
+ reg = 0;
+ break;
+ case ACCESS_RMW:
+ reg = readl(base) & ~mask;
+ break;
+ }
+
+ writel(reg | ((u32)value << shift), base);
+
+ return 0;
+}
+
+void gic_set_irq_bit(int irq, int offset)
+{
+ gic_masked_irq_bits(irq, offset, 1, 1, ACCESS_SET);
+}
+
+void gic_enable_irq(int irq)
+{
+ gic_set_irq_bit(irq, GICD_ISENABLER);
+}
+
+void gic_disable_irq(int irq)
+{
+ gic_set_irq_bit(irq, GICD_ICENABLER);
+}
+
+void gic_set_irq_priority(int irq, u8 prio)
+{
+ gic_masked_irq_bits(irq, GICD_IPRIORITYR, 8, prio, ACCESS_RMW);
+}
+
+void gic_set_irq_target(int irq, int cpu)
+{
+ if (irq < 32)
+ return;
+
+ if (gic_version() == 2) {
+ gic_masked_irq_bits(irq, GICD_ITARGETSR, 8, 1U << cpu,
+ ACCESS_RMW);
+
+ return;
+ }
+
+ writeq(cpus[cpu], gicv3_dist_base() + GICD_IROUTER + irq * 8);
+}
+
+void gic_set_irq_group(int irq, int group)
+{
+ gic_masked_irq_bits(irq, GICD_IGROUPR, 1, group, ACCESS_RMW);
+}
+
+int gic_get_irq_group(int irq)
+{
+ return gic_masked_irq_bits(irq, GICD_IGROUPR, 1, 0, ACCESS_READ);
+}