staticvoid pdc_x1e_irq_enable_write(u32 bank, u32 enable)
{ void __iomem *base;
/* Remap the write access to work around a hardware bug on X1E */ switch (bank) { case 0 ... 1: /* Use previous DRV (client) region and shift to bank 3-4 */
base = pdc_prev_base;
bank += 3; break; case 2 ... 4: /* Use our own region and shift to bank 0-2 */
base = pdc_base;
bank -= 2; break; case 5: /* No fixup required for bank 5 */
base = pdc_base; break; default:
WARN_ON(1); return;
}
pdc_base_reg_write(base, IRQ_ENABLE_BANK, bank, enable);
}
/* * GIC does not handle falling edge or active low. To allow falling edge and * active low interrupts to be handled at GIC, PDC has an inverter that inverts * falling edge into a rising edge and active low into an active high. * For the inverter to work, the polarity bit in the IRQ_CONFIG register has to * set as per the table below. * Level sensitive active low LOW * Rising edge sensitive NOT USED * Falling edge sensitive LOW * Dual Edge sensitive NOT USED * Level sensitive active High HIGH * Falling Edge sensitive NOT USED * Rising edge sensitive HIGH * Dual Edge sensitive HIGH
*/ enum pdc_irq_config_bits {
PDC_LEVEL_LOW = 0b000,
PDC_EDGE_FALLING = 0b010,
PDC_LEVEL_HIGH = 0b100,
PDC_EDGE_RISING = 0b110,
PDC_EDGE_DUAL = 0b111,
};
/** * qcom_pdc_gic_set_type: Configure PDC for the interrupt * * @d: the interrupt data * @type: the interrupt type * * If @type is edge triggered, forward that as Rising edge as PDC * takes care of converting falling edge to rising edge signal * If @type is level, then forward that as level high as PDC * takes care of converting falling edge to rising edge signal
*/ staticint qcom_pdc_gic_set_type(struct irq_data *d, unsignedint type)
{ enum pdc_irq_config_bits pdc_type; enum pdc_irq_config_bits old_pdc_type; int ret;
switch (type) { case IRQ_TYPE_EDGE_RISING:
pdc_type = PDC_EDGE_RISING; break; case IRQ_TYPE_EDGE_FALLING:
pdc_type = PDC_EDGE_FALLING;
type = IRQ_TYPE_EDGE_RISING; break; case IRQ_TYPE_EDGE_BOTH:
pdc_type = PDC_EDGE_DUAL;
type = IRQ_TYPE_EDGE_RISING; break; case IRQ_TYPE_LEVEL_HIGH:
pdc_type = PDC_LEVEL_HIGH; break; case IRQ_TYPE_LEVEL_LOW:
pdc_type = PDC_LEVEL_LOW;
type = IRQ_TYPE_LEVEL_HIGH; break; default:
WARN_ON(1); return -EINVAL;
}
ret = irq_chip_set_type_parent(d, type); if (ret) return ret;
/* * When we change types the PDC can give a phantom interrupt. * Clear it. Specifically the phantom shows up when reconfiguring * polarity of interrupt without changing the state of the signal * but let's be consistent and clear it always. * * Doing this works because we have IRQCHIP_SET_TYPE_MASKED so the * interrupt will be cleared before the rest of the system sees it.
*/ if (old_pdc_type != pdc_type)
irq_chip_set_parent_state(d, IRQCHIP_STATE_PENDING, false);
staticint pdc_setup_pin_mapping(struct device_node *np)
{ int ret, n, i;
n = of_property_count_elems_of_size(np, "qcom,pdc-ranges", sizeof(u32)); if (n <= 0 || n % 3) return -EINVAL;
pdc_region_cnt = n / 3;
pdc_region = kcalloc(pdc_region_cnt, sizeof(*pdc_region), GFP_KERNEL); if (!pdc_region) {
pdc_region_cnt = 0; return -ENOMEM;
}
for (n = 0; n < pdc_region_cnt; n++) {
ret = of_property_read_u32_index(np, "qcom,pdc-ranges",
n * 3 + 0,
&pdc_region[n].pin_base); if (ret) return ret;
ret = of_property_read_u32_index(np, "qcom,pdc-ranges",
n * 3 + 1,
&pdc_region[n].parent_base); if (ret) return ret;
ret = of_property_read_u32_index(np, "qcom,pdc-ranges",
n * 3 + 2,
&pdc_region[n].cnt); if (ret) return ret;
for (i = 0; i < pdc_region[n].cnt; i++)
__pdc_enable_intr(i + pdc_region[n].pin_base, 0);
}
/* * PDC has multiple DRV regions, each one provides the same set of * registers for a particular client in the system. Due to a hardware * bug on X1E, some writes to the IRQ_ENABLE_BANK register must be * issued inside the previous region. This region belongs to * a different client and is not described in the device tree. Map the * region with the expected offset to preserve support for old DTs.
*/ if (of_device_is_compatible(node, "qcom,x1e80100-pdc")) {
pdc_prev_base = ioremap(res.start - PDC_DRV_OFFSET, IRQ_ENABLE_BANK_MAX); if (!pdc_prev_base) {
pr_err("%pOF: unable to map previous PDC DRV region\n", node); return -ENXIO;
}
pdc_x1e_quirk = true;
}
pdc_base = ioremap(res.start, res_size); if (!pdc_base) {
pr_err("%pOF: unable to map PDC registers\n", node);
ret = -ENXIO; goto fail;
}
pdc_version = pdc_reg_read(PDC_VERSION_REG, 0);
parent_domain = irq_find_host(parent); if (!parent_domain) {
pr_err("%pOF: unable to find PDC's parent domain\n", node);
ret = -ENXIO; goto fail;
}
ret = pdc_setup_pin_mapping(node); if (ret) {
pr_err("%pOF: failed to init PDC pin-hwirq mapping\n", node); goto fail;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.