|
@@ -39,6 +39,8 @@ REG32(GICINT135_EN, 0x700)
|
|
|
REG32(GICINT135_STATUS, 0x704)
|
|
|
REG32(GICINT136_EN, 0x800)
|
|
|
REG32(GICINT136_STATUS, 0x804)
|
|
|
+REG32(GICINT192_201_EN, 0xB00)
|
|
|
+REG32(GICINT192_201_STATUS, 0xB04)
|
|
|
|
|
|
static const AspeedINTCIRQ *aspeed_intc_get_irq(AspeedINTCClass *aic,
|
|
|
uint32_t reg)
|
|
@@ -112,9 +114,55 @@ static void aspeed_intc_set_irq_handler(AspeedINTCState *s,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static void aspeed_intc_set_irq_handler_multi_outpins(AspeedINTCState *s,
|
|
|
+ const AspeedINTCIRQ *intc_irq, uint32_t select)
|
|
|
+{
|
|
|
+ const char *name = object_get_typename(OBJECT(s));
|
|
|
+ uint32_t status_reg;
|
|
|
+ int num_outpins;
|
|
|
+ int outpin_idx;
|
|
|
+ int inpin_idx;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ num_outpins = intc_irq->num_outpins;
|
|
|
+ status_reg = intc_irq->status_reg;
|
|
|
+ outpin_idx = intc_irq->outpin_idx;
|
|
|
+ inpin_idx = intc_irq->inpin_idx;
|
|
|
+
|
|
|
+ for (i = 0; i < num_outpins; i++) {
|
|
|
+ if (select & BIT(i)) {
|
|
|
+ if (s->mask[inpin_idx] & BIT(i) ||
|
|
|
+ s->regs[status_reg] & BIT(i)) {
|
|
|
+ /*
|
|
|
+ * a. mask bit is not 0 means in ISR mode sources interrupt
|
|
|
+ * routine are executing.
|
|
|
+ * b. status bit is not 0 means previous source interrupt
|
|
|
+ * does not be executed, yet.
|
|
|
+ *
|
|
|
+ * save source interrupt to pending bit.
|
|
|
+ */
|
|
|
+ s->pending[inpin_idx] |= BIT(i);
|
|
|
+ trace_aspeed_intc_pending_irq(name, inpin_idx,
|
|
|
+ s->pending[inpin_idx]);
|
|
|
+ } else {
|
|
|
+ /*
|
|
|
+ * notify firmware which source interrupt are coming
|
|
|
+ * by setting status bit
|
|
|
+ */
|
|
|
+ s->regs[status_reg] |= BIT(i);
|
|
|
+ trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx + i,
|
|
|
+ s->regs[status_reg]);
|
|
|
+ aspeed_intc_update(s, inpin_idx, outpin_idx + i, 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
- * GICINT128 to GICINT136 map 1:1 to input and output IRQs 0 to 8.
|
|
|
- * The value of input IRQ should be between 0 and the number of inputs.
|
|
|
+ * GICINT192_201 maps 1:10 to input IRQ 0 and output IRQs 0 to 9.
|
|
|
+ * GICINT128 to GICINT136 map 1:1 to input IRQs 1 to 9 and output
|
|
|
+ * IRQs 10 to 18. The value of input IRQ should be between 0 and
|
|
|
+ * the number of input pins.
|
|
|
*/
|
|
|
static void aspeed_intc_set_irq(void *opaque, int irq, int level)
|
|
|
{
|
|
@@ -124,12 +172,14 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level)
|
|
|
const AspeedINTCIRQ *intc_irq;
|
|
|
uint32_t select = 0;
|
|
|
uint32_t enable;
|
|
|
+ int num_outpins;
|
|
|
int inpin_idx;
|
|
|
int i;
|
|
|
|
|
|
assert(irq < aic->num_inpins);
|
|
|
|
|
|
intc_irq = &aic->irq_table[irq];
|
|
|
+ num_outpins = intc_irq->num_outpins;
|
|
|
inpin_idx = intc_irq->inpin_idx;
|
|
|
trace_aspeed_intc_set_irq(name, inpin_idx, level);
|
|
|
enable = s->enable[inpin_idx];
|
|
@@ -151,7 +201,11 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level)
|
|
|
}
|
|
|
|
|
|
trace_aspeed_intc_select(name, select);
|
|
|
- aspeed_intc_set_irq_handler(s, intc_irq, select);
|
|
|
+ if (num_outpins > 1) {
|
|
|
+ aspeed_intc_set_irq_handler_multi_outpins(s, intc_irq, select);
|
|
|
+ } else {
|
|
|
+ aspeed_intc_set_irq_handler(s, intc_irq, select);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset,
|
|
@@ -261,6 +315,66 @@ static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static void aspeed_intc_status_handler_multi_outpins(AspeedINTCState *s,
|
|
|
+ hwaddr offset, uint64_t data)
|
|
|
+{
|
|
|
+ const char *name = object_get_typename(OBJECT(s));
|
|
|
+ AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
|
|
|
+ const AspeedINTCIRQ *intc_irq;
|
|
|
+ uint32_t reg = offset >> 2;
|
|
|
+ int num_outpins;
|
|
|
+ int outpin_idx;
|
|
|
+ int inpin_idx;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (!data) {
|
|
|
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid data 0\n", __func__);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ intc_irq = aspeed_intc_get_irq(aic, reg);
|
|
|
+ num_outpins = intc_irq->num_outpins;
|
|
|
+ outpin_idx = intc_irq->outpin_idx;
|
|
|
+ inpin_idx = intc_irq->inpin_idx;
|
|
|
+ assert(inpin_idx < aic->num_inpins);
|
|
|
+
|
|
|
+ /* clear status */
|
|
|
+ s->regs[reg] &= ~data;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The status registers are used for notify sources ISR are executed.
|
|
|
+ * If one source ISR is executed, it will clear one bit.
|
|
|
+ * If it clear all bits, it means to initialize this register status
|
|
|
+ * rather than sources ISR are executed.
|
|
|
+ */
|
|
|
+ if (data == 0xffffffff) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < num_outpins; i++) {
|
|
|
+ /* All source ISR executions are done from a specific bit */
|
|
|
+ if (data & BIT(i)) {
|
|
|
+ trace_aspeed_intc_all_isr_done_bit(name, inpin_idx, i);
|
|
|
+ if (s->pending[inpin_idx] & BIT(i)) {
|
|
|
+ /*
|
|
|
+ * Handle pending source interrupt.
|
|
|
+ * Notify firmware which source interrupt is pending
|
|
|
+ * by setting the status bit.
|
|
|
+ */
|
|
|
+ s->regs[reg] |= BIT(i);
|
|
|
+ s->pending[inpin_idx] &= ~BIT(i);
|
|
|
+ trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx + i,
|
|
|
+ s->regs[reg]);
|
|
|
+ aspeed_intc_update(s, inpin_idx, outpin_idx + i, 1);
|
|
|
+ } else {
|
|
|
+ /* clear irq for the specific bit */
|
|
|
+ trace_aspeed_intc_clear_irq(name, inpin_idx, outpin_idx + i, 0);
|
|
|
+ aspeed_intc_update(s, inpin_idx, outpin_idx + i, 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static uint64_t aspeed_intc_read(void *opaque, hwaddr offset, unsigned int size)
|
|
|
{
|
|
|
AspeedINTCState *s = ASPEED_INTC(opaque);
|
|
@@ -293,6 +407,7 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data,
|
|
|
case R_GICINT134_EN:
|
|
|
case R_GICINT135_EN:
|
|
|
case R_GICINT136_EN:
|
|
|
+ case R_GICINT192_201_EN:
|
|
|
aspeed_intc_enable_handler(s, offset, data);
|
|
|
break;
|
|
|
case R_GICINT128_STATUS:
|
|
@@ -306,6 +421,9 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data,
|
|
|
case R_GICINT136_STATUS:
|
|
|
aspeed_intc_status_handler(s, offset, data);
|
|
|
break;
|
|
|
+ case R_GICINT192_201_STATUS:
|
|
|
+ aspeed_intc_status_handler_multi_outpins(s, offset, data);
|
|
|
+ break;
|
|
|
default:
|
|
|
s->regs[reg] = data;
|
|
|
break;
|
|
@@ -415,15 +533,16 @@ static const TypeInfo aspeed_intc_info = {
|
|
|
};
|
|
|
|
|
|
static AspeedINTCIRQ aspeed_2700_intc_irqs[ASPEED_INTC_MAX_INPINS] = {
|
|
|
- {0, 0, 1, R_GICINT128_EN, R_GICINT128_STATUS},
|
|
|
- {1, 1, 1, R_GICINT129_EN, R_GICINT129_STATUS},
|
|
|
- {2, 2, 1, R_GICINT130_EN, R_GICINT130_STATUS},
|
|
|
- {3, 3, 1, R_GICINT131_EN, R_GICINT131_STATUS},
|
|
|
- {4, 4, 1, R_GICINT132_EN, R_GICINT132_STATUS},
|
|
|
- {5, 5, 1, R_GICINT133_EN, R_GICINT133_STATUS},
|
|
|
- {6, 6, 1, R_GICINT134_EN, R_GICINT134_STATUS},
|
|
|
- {7, 7, 1, R_GICINT135_EN, R_GICINT135_STATUS},
|
|
|
- {8, 8, 1, R_GICINT136_EN, R_GICINT136_STATUS},
|
|
|
+ {0, 0, 10, R_GICINT192_201_EN, R_GICINT192_201_STATUS},
|
|
|
+ {1, 10, 1, R_GICINT128_EN, R_GICINT128_STATUS},
|
|
|
+ {2, 11, 1, R_GICINT129_EN, R_GICINT129_STATUS},
|
|
|
+ {3, 12, 1, R_GICINT130_EN, R_GICINT130_STATUS},
|
|
|
+ {4, 13, 1, R_GICINT131_EN, R_GICINT131_STATUS},
|
|
|
+ {5, 14, 1, R_GICINT132_EN, R_GICINT132_STATUS},
|
|
|
+ {6, 15, 1, R_GICINT133_EN, R_GICINT133_STATUS},
|
|
|
+ {7, 16, 1, R_GICINT134_EN, R_GICINT134_STATUS},
|
|
|
+ {8, 17, 1, R_GICINT135_EN, R_GICINT135_STATUS},
|
|
|
+ {9, 18, 1, R_GICINT136_EN, R_GICINT136_STATUS},
|
|
|
};
|
|
|
|
|
|
static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data)
|
|
@@ -433,10 +552,10 @@ static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data)
|
|
|
|
|
|
dc->desc = "ASPEED 2700 INTC Controller";
|
|
|
aic->num_lines = 32;
|
|
|
- aic->num_inpins = 9;
|
|
|
- aic->num_outpins = 9;
|
|
|
+ aic->num_inpins = 10;
|
|
|
+ aic->num_outpins = 19;
|
|
|
aic->mem_size = 0x4000;
|
|
|
- aic->nr_regs = 0x808 >> 2;
|
|
|
+ aic->nr_regs = 0xB08 >> 2;
|
|
|
aic->reg_offset = 0x1000;
|
|
|
aic->irq_table = aspeed_2700_intc_irqs;
|
|
|
aic->irq_table_count = ARRAY_SIZE(aspeed_2700_intc_irqs);
|