forked from luck/tmp_suning_uos_patched
Merge branch 'irq/tegra-pmc' into irq/irqchip-next
Signed-off-by: Marc Zyngier <maz@kernel.org>
This commit is contained in:
commit
408f110ef6
|
@ -430,7 +430,18 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type)
|
||||||
else
|
else
|
||||||
irq_set_handler_locked(data, handle_edge_irq);
|
irq_set_handler_locked(data, handle_edge_irq);
|
||||||
|
|
||||||
return irq_chip_set_type_parent(data, type);
|
if (data->parent_data)
|
||||||
|
return irq_chip_set_type_parent(data, type);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra186_irq_set_wake(struct irq_data *data, unsigned int on)
|
||||||
|
{
|
||||||
|
if (data->parent_data)
|
||||||
|
return irq_chip_set_wake_parent(data, on);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tegra186_gpio_irq(struct irq_desc *desc)
|
static void tegra186_gpio_irq(struct irq_desc *desc)
|
||||||
|
@ -678,7 +689,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
|
||||||
gpio->intc.irq_mask = tegra186_irq_mask;
|
gpio->intc.irq_mask = tegra186_irq_mask;
|
||||||
gpio->intc.irq_unmask = tegra186_irq_unmask;
|
gpio->intc.irq_unmask = tegra186_irq_unmask;
|
||||||
gpio->intc.irq_set_type = tegra186_irq_set_type;
|
gpio->intc.irq_set_type = tegra186_irq_set_type;
|
||||||
gpio->intc.irq_set_wake = irq_chip_set_wake_parent;
|
gpio->intc.irq_set_wake = tegra186_irq_set_wake;
|
||||||
|
|
||||||
irq = &gpio->gpio.irq;
|
irq = &gpio->gpio.irq;
|
||||||
irq->chip = &gpio->intc;
|
irq->chip = &gpio->intc;
|
||||||
|
|
|
@ -1990,44 +1990,17 @@ static int tegra_pmc_irq_alloc(struct irq_domain *domain, unsigned int virq,
|
||||||
event->id,
|
event->id,
|
||||||
&pmc->irq, pmc);
|
&pmc->irq, pmc);
|
||||||
|
|
||||||
/*
|
/* GPIO hierarchies stop at the PMC level */
|
||||||
* GPIOs don't have an equivalent interrupt in the
|
if (!err && domain->parent)
|
||||||
* parent controller (GIC). However some code, such
|
err = irq_domain_disconnect_hierarchy(domain->parent,
|
||||||
* as the one in irq_get_irqchip_state(), require a
|
virq);
|
||||||
* valid IRQ chip to be set. Make sure that's the
|
|
||||||
* case by passing NULL here, which will install a
|
|
||||||
* dummy IRQ chip for the interrupt in the parent
|
|
||||||
* domain.
|
|
||||||
*/
|
|
||||||
if (domain->parent)
|
|
||||||
irq_domain_set_hwirq_and_chip(domain->parent,
|
|
||||||
virq, 0, NULL,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* If there is no wake-up event, there is no PMC mapping */
|
||||||
* For interrupts that don't have associated wake events, assign a
|
if (i == soc->num_wake_events)
|
||||||
* dummy hardware IRQ number. This is used in the ->irq_set_type()
|
err = irq_domain_disconnect_hierarchy(domain, virq);
|
||||||
* and ->irq_set_wake() callbacks to return early for these IRQs.
|
|
||||||
*/
|
|
||||||
if (i == soc->num_wake_events) {
|
|
||||||
err = irq_domain_set_hwirq_and_chip(domain, virq, ULONG_MAX,
|
|
||||||
&pmc->irq, pmc);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Interrupts without a wake event don't have a corresponding
|
|
||||||
* interrupt in the parent controller (GIC). Pass NULL for the
|
|
||||||
* chip here, which causes a dummy IRQ chip to be installed
|
|
||||||
* for the interrupt in the parent domain, to make this
|
|
||||||
* explicit.
|
|
||||||
*/
|
|
||||||
if (domain->parent)
|
|
||||||
irq_domain_set_hwirq_and_chip(domain->parent, virq, 0,
|
|
||||||
NULL, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -2043,9 +2016,6 @@ static int tegra210_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
|
||||||
unsigned int offset, bit;
|
unsigned int offset, bit;
|
||||||
u32 value;
|
u32 value;
|
||||||
|
|
||||||
if (data->hwirq == ULONG_MAX)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
offset = data->hwirq / 32;
|
offset = data->hwirq / 32;
|
||||||
bit = data->hwirq % 32;
|
bit = data->hwirq % 32;
|
||||||
|
|
||||||
|
@ -2080,9 +2050,6 @@ static int tegra210_pmc_irq_set_type(struct irq_data *data, unsigned int type)
|
||||||
unsigned int offset, bit;
|
unsigned int offset, bit;
|
||||||
u32 value;
|
u32 value;
|
||||||
|
|
||||||
if (data->hwirq == ULONG_MAX)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
offset = data->hwirq / 32;
|
offset = data->hwirq / 32;
|
||||||
bit = data->hwirq % 32;
|
bit = data->hwirq % 32;
|
||||||
|
|
||||||
|
@ -2123,10 +2090,6 @@ static int tegra186_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
|
||||||
unsigned int offset, bit;
|
unsigned int offset, bit;
|
||||||
u32 value;
|
u32 value;
|
||||||
|
|
||||||
/* nothing to do if there's no associated wake event */
|
|
||||||
if (WARN_ON(data->hwirq == ULONG_MAX))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
offset = data->hwirq / 32;
|
offset = data->hwirq / 32;
|
||||||
bit = data->hwirq % 32;
|
bit = data->hwirq % 32;
|
||||||
|
|
||||||
|
@ -2154,10 +2117,6 @@ static int tegra186_pmc_irq_set_type(struct irq_data *data, unsigned int type)
|
||||||
struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
|
struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
|
||||||
u32 value;
|
u32 value;
|
||||||
|
|
||||||
/* nothing to do if there's no associated wake event */
|
|
||||||
if (data->hwirq == ULONG_MAX)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
value = readl(pmc->wake + WAKE_AOWAKE_CNTRL(data->hwirq));
|
value = readl(pmc->wake + WAKE_AOWAKE_CNTRL(data->hwirq));
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -2184,6 +2143,34 @@ static int tegra186_pmc_irq_set_type(struct irq_data *data, unsigned int type)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void tegra_irq_mask_parent(struct irq_data *data)
|
||||||
|
{
|
||||||
|
if (data->parent_data)
|
||||||
|
irq_chip_mask_parent(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tegra_irq_unmask_parent(struct irq_data *data)
|
||||||
|
{
|
||||||
|
if (data->parent_data)
|
||||||
|
irq_chip_unmask_parent(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tegra_irq_eoi_parent(struct irq_data *data)
|
||||||
|
{
|
||||||
|
if (data->parent_data)
|
||||||
|
irq_chip_eoi_parent(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra_irq_set_affinity_parent(struct irq_data *data,
|
||||||
|
const struct cpumask *dest,
|
||||||
|
bool force)
|
||||||
|
{
|
||||||
|
if (data->parent_data)
|
||||||
|
return irq_chip_set_affinity_parent(data, dest, force);
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
static int tegra_pmc_irq_init(struct tegra_pmc *pmc)
|
static int tegra_pmc_irq_init(struct tegra_pmc *pmc)
|
||||||
{
|
{
|
||||||
struct irq_domain *parent = NULL;
|
struct irq_domain *parent = NULL;
|
||||||
|
@ -2199,10 +2186,10 @@ static int tegra_pmc_irq_init(struct tegra_pmc *pmc)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
pmc->irq.name = dev_name(pmc->dev);
|
pmc->irq.name = dev_name(pmc->dev);
|
||||||
pmc->irq.irq_mask = irq_chip_mask_parent;
|
pmc->irq.irq_mask = tegra_irq_mask_parent;
|
||||||
pmc->irq.irq_unmask = irq_chip_unmask_parent;
|
pmc->irq.irq_unmask = tegra_irq_unmask_parent;
|
||||||
pmc->irq.irq_eoi = irq_chip_eoi_parent;
|
pmc->irq.irq_eoi = tegra_irq_eoi_parent;
|
||||||
pmc->irq.irq_set_affinity = irq_chip_set_affinity_parent;
|
pmc->irq.irq_set_affinity = tegra_irq_set_affinity_parent;
|
||||||
pmc->irq.irq_set_type = pmc->soc->irq_set_type;
|
pmc->irq.irq_set_type = pmc->soc->irq_set_type;
|
||||||
pmc->irq.irq_set_wake = pmc->soc->irq_set_wake;
|
pmc->irq.irq_set_wake = pmc->soc->irq_set_wake;
|
||||||
|
|
||||||
|
|
|
@ -509,6 +509,9 @@ extern void irq_domain_free_irqs_parent(struct irq_domain *domain,
|
||||||
unsigned int irq_base,
|
unsigned int irq_base,
|
||||||
unsigned int nr_irqs);
|
unsigned int nr_irqs);
|
||||||
|
|
||||||
|
extern int irq_domain_disconnect_hierarchy(struct irq_domain *domain,
|
||||||
|
unsigned int virq);
|
||||||
|
|
||||||
static inline bool irq_domain_is_hierarchy(struct irq_domain *domain)
|
static inline bool irq_domain_is_hierarchy(struct irq_domain *domain)
|
||||||
{
|
{
|
||||||
return domain->flags & IRQ_DOMAIN_FLAG_HIERARCHY;
|
return domain->flags & IRQ_DOMAIN_FLAG_HIERARCHY;
|
||||||
|
|
|
@ -1136,6 +1136,17 @@ static struct irq_data *irq_domain_insert_irq_data(struct irq_domain *domain,
|
||||||
return irq_data;
|
return irq_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void __irq_domain_free_hierarchy(struct irq_data *irq_data)
|
||||||
|
{
|
||||||
|
struct irq_data *tmp;
|
||||||
|
|
||||||
|
while (irq_data) {
|
||||||
|
tmp = irq_data;
|
||||||
|
irq_data = irq_data->parent_data;
|
||||||
|
kfree(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void irq_domain_free_irq_data(unsigned int virq, unsigned int nr_irqs)
|
static void irq_domain_free_irq_data(unsigned int virq, unsigned int nr_irqs)
|
||||||
{
|
{
|
||||||
struct irq_data *irq_data, *tmp;
|
struct irq_data *irq_data, *tmp;
|
||||||
|
@ -1147,12 +1158,83 @@ static void irq_domain_free_irq_data(unsigned int virq, unsigned int nr_irqs)
|
||||||
irq_data->parent_data = NULL;
|
irq_data->parent_data = NULL;
|
||||||
irq_data->domain = NULL;
|
irq_data->domain = NULL;
|
||||||
|
|
||||||
while (tmp) {
|
__irq_domain_free_hierarchy(tmp);
|
||||||
irq_data = tmp;
|
}
|
||||||
tmp = tmp->parent_data;
|
}
|
||||||
kfree(irq_data);
|
|
||||||
|
/**
|
||||||
|
* irq_domain_disconnect_hierarchy - Mark the first unused level of a hierarchy
|
||||||
|
* @domain: IRQ domain from which the hierarchy is to be disconnected
|
||||||
|
* @virq: IRQ number where the hierarchy is to be trimmed
|
||||||
|
*
|
||||||
|
* Marks the @virq level belonging to @domain as disconnected.
|
||||||
|
* Returns -EINVAL if @virq doesn't have a valid irq_data pointing
|
||||||
|
* to @domain.
|
||||||
|
*
|
||||||
|
* Its only use is to be able to trim levels of hierarchy that do not
|
||||||
|
* have any real meaning for this interrupt, and that the driver marks
|
||||||
|
* as such from its .alloc() callback.
|
||||||
|
*/
|
||||||
|
int irq_domain_disconnect_hierarchy(struct irq_domain *domain,
|
||||||
|
unsigned int virq)
|
||||||
|
{
|
||||||
|
struct irq_data *irqd;
|
||||||
|
|
||||||
|
irqd = irq_domain_get_irq_data(domain, virq);
|
||||||
|
if (!irqd)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
irqd->chip = ERR_PTR(-ENOTCONN);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int irq_domain_trim_hierarchy(unsigned int virq)
|
||||||
|
{
|
||||||
|
struct irq_data *tail, *irqd, *irq_data;
|
||||||
|
|
||||||
|
irq_data = irq_get_irq_data(virq);
|
||||||
|
tail = NULL;
|
||||||
|
|
||||||
|
/* The first entry must have a valid irqchip */
|
||||||
|
if (!irq_data->chip || IS_ERR(irq_data->chip))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Validate that the irq_data chain is sane in the presence of
|
||||||
|
* a hierarchy trimming marker.
|
||||||
|
*/
|
||||||
|
for (irqd = irq_data->parent_data; irqd; irq_data = irqd, irqd = irqd->parent_data) {
|
||||||
|
/* Can't have a valid irqchip after a trim marker */
|
||||||
|
if (irqd->chip && tail)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Can't have an empty irqchip before a trim marker */
|
||||||
|
if (!irqd->chip && !tail)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (IS_ERR(irqd->chip)) {
|
||||||
|
/* Only -ENOTCONN is a valid trim marker */
|
||||||
|
if (PTR_ERR(irqd->chip) != -ENOTCONN)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
tail = irq_data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* No trim marker, nothing to do */
|
||||||
|
if (!tail)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
pr_info("IRQ%d: trimming hierarchy from %s\n",
|
||||||
|
virq, tail->parent_data->domain->name);
|
||||||
|
|
||||||
|
/* Sever the inner part of the hierarchy... */
|
||||||
|
irqd = tail;
|
||||||
|
tail = tail->parent_data;
|
||||||
|
irqd->parent_data = NULL;
|
||||||
|
__irq_domain_free_hierarchy(tail);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int irq_domain_alloc_irq_data(struct irq_domain *domain,
|
static int irq_domain_alloc_irq_data(struct irq_domain *domain,
|
||||||
|
@ -1362,6 +1444,15 @@ int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
|
||||||
mutex_unlock(&irq_domain_mutex);
|
mutex_unlock(&irq_domain_mutex);
|
||||||
goto out_free_irq_data;
|
goto out_free_irq_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < nr_irqs; i++) {
|
||||||
|
ret = irq_domain_trim_hierarchy(virq + i);
|
||||||
|
if (ret) {
|
||||||
|
mutex_unlock(&irq_domain_mutex);
|
||||||
|
goto out_free_irq_data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < nr_irqs; i++)
|
for (i = 0; i < nr_irqs; i++)
|
||||||
irq_domain_insert_irq(virq + i);
|
irq_domain_insert_irq(virq + i);
|
||||||
mutex_unlock(&irq_domain_mutex);
|
mutex_unlock(&irq_domain_mutex);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user