Commit 12a9eeae authored by Brian Masney's avatar Brian Masney Committed by Linus Walleij

spmi: pmic-arb: convert to v2 irq interfaces to support hierarchical IRQ chips

Convert the spmi-pmic-arb IRQ code to use the version 2 IRQ interface
in order to support hierarchical IRQ chips. This is necessary so that
spmi-gpio can be setup as a hierarchical IRQ chip with pmic-arb as the
parent. IRQ chips in device tree should be usable from the start without
the consumer having to make an additional call to gpio[d]_to_irq() to
get the proper IRQ on the parent.

The old qpnpint_irq_domain_map function would hardcode the handler as
handle_level_irq, however qpnpint_irq_set_type would later override the
handler. Properly set the handler when the IRQ is mapped. This new code
doesn't return an error for IRQ_TYPE_NONE and preserves the existing
behavior of using handle_level_irq since there are some broken device
tree bindings that need to be corrected first.

Driver was tested on a LG Nexus 5 (hammerhead) phone.
Signed-off-by: default avatarBrian Masney <masneyb@onstation.org>
Reviewed-by: default avatarMarc Zyngier <marc.zyngier@arm.com>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent cfacef37
...@@ -666,7 +666,8 @@ static int qpnpint_get_irqchip_state(struct irq_data *d, ...@@ -666,7 +666,8 @@ static int qpnpint_get_irqchip_state(struct irq_data *d,
return 0; return 0;
} }
static int qpnpint_irq_request_resources(struct irq_data *d) static int qpnpint_irq_domain_activate(struct irq_domain *domain,
struct irq_data *d, bool reserve)
{ {
struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d); struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d);
u16 periph = hwirq_to_per(d->hwirq); u16 periph = hwirq_to_per(d->hwirq);
...@@ -692,27 +693,25 @@ static struct irq_chip pmic_arb_irqchip = { ...@@ -692,27 +693,25 @@ static struct irq_chip pmic_arb_irqchip = {
.irq_set_type = qpnpint_irq_set_type, .irq_set_type = qpnpint_irq_set_type,
.irq_set_wake = qpnpint_irq_set_wake, .irq_set_wake = qpnpint_irq_set_wake,
.irq_get_irqchip_state = qpnpint_get_irqchip_state, .irq_get_irqchip_state = qpnpint_get_irqchip_state,
.irq_request_resources = qpnpint_irq_request_resources,
.flags = IRQCHIP_MASK_ON_SUSPEND, .flags = IRQCHIP_MASK_ON_SUSPEND,
}; };
static int qpnpint_irq_domain_dt_translate(struct irq_domain *d, static int qpnpint_irq_domain_translate(struct irq_domain *d,
struct device_node *controller, struct irq_fwspec *fwspec,
const u32 *intspec, unsigned long *out_hwirq,
unsigned int intsize, unsigned int *out_type)
unsigned long *out_hwirq,
unsigned int *out_type)
{ {
struct spmi_pmic_arb *pmic_arb = d->host_data; struct spmi_pmic_arb *pmic_arb = d->host_data;
u32 *intspec = fwspec->param;
u16 apid, ppid; u16 apid, ppid;
int rc; int rc;
dev_dbg(&pmic_arb->spmic->dev, "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n", dev_dbg(&pmic_arb->spmic->dev, "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n",
intspec[0], intspec[1], intspec[2]); intspec[0], intspec[1], intspec[2]);
if (irq_domain_get_of_node(d) != controller) if (irq_domain_get_of_node(d) != pmic_arb->spmic->dev.of_node)
return -EINVAL; return -EINVAL;
if (intsize != 4) if (fwspec->param_count != 4)
return -EINVAL; return -EINVAL;
if (intspec[0] > 0xF || intspec[1] > 0xFF || intspec[2] > 0x7) if (intspec[0] > 0xF || intspec[1] > 0xFF || intspec[2] > 0x7)
return -EINVAL; return -EINVAL;
...@@ -740,17 +739,43 @@ static int qpnpint_irq_domain_dt_translate(struct irq_domain *d, ...@@ -740,17 +739,43 @@ static int qpnpint_irq_domain_dt_translate(struct irq_domain *d,
return 0; return 0;
} }
static int qpnpint_irq_domain_map(struct irq_domain *d,
unsigned int virq, static void qpnpint_irq_domain_map(struct spmi_pmic_arb *pmic_arb,
irq_hw_number_t hwirq) struct irq_domain *domain, unsigned int virq,
irq_hw_number_t hwirq, unsigned int type)
{ {
struct spmi_pmic_arb *pmic_arb = d->host_data; irq_flow_handler_t handler;
dev_dbg(&pmic_arb->spmic->dev, "virq = %u, hwirq = %lu, type = %u\n",
virq, hwirq, type);
if (type & IRQ_TYPE_EDGE_BOTH)
handler = handle_edge_irq;
else
handler = handle_level_irq;
irq_domain_set_info(domain, virq, hwirq, &pmic_arb_irqchip, pmic_arb,
handler, NULL, NULL);
}
static int qpnpint_irq_domain_alloc(struct irq_domain *domain,
unsigned int virq, unsigned int nr_irqs,
void *data)
{
struct spmi_pmic_arb *pmic_arb = domain->host_data;
struct irq_fwspec *fwspec = data;
irq_hw_number_t hwirq;
unsigned int type;
int ret, i;
ret = qpnpint_irq_domain_translate(domain, fwspec, &hwirq, &type);
if (ret)
return ret;
dev_dbg(&pmic_arb->spmic->dev, "virq = %u, hwirq = %lu\n", virq, hwirq); for (i = 0; i < nr_irqs; i++)
qpnpint_irq_domain_map(pmic_arb, domain, virq + i, hwirq + i,
type);
irq_set_chip_and_handler(virq, &pmic_arb_irqchip, handle_level_irq);
irq_set_chip_data(virq, d->host_data);
irq_set_noprobe(virq);
return 0; return 0;
} }
...@@ -1126,8 +1151,10 @@ static const struct pmic_arb_ver_ops pmic_arb_v5 = { ...@@ -1126,8 +1151,10 @@ static const struct pmic_arb_ver_ops pmic_arb_v5 = {
}; };
static const struct irq_domain_ops pmic_arb_irq_domain_ops = { static const struct irq_domain_ops pmic_arb_irq_domain_ops = {
.map = qpnpint_irq_domain_map, .activate = qpnpint_irq_domain_activate,
.xlate = qpnpint_irq_domain_dt_translate, .alloc = qpnpint_irq_domain_alloc,
.free = irq_domain_free_irqs_common,
.translate = qpnpint_irq_domain_translate,
}; };
static int spmi_pmic_arb_probe(struct platform_device *pdev) static int spmi_pmic_arb_probe(struct platform_device *pdev)
......
Markdown is supported
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