Skip to content

Commit 408f110

Browse files
author
Marc Zyngier
committed
Merge branch 'irq/tegra-pmc' into irq/irqchip-next
Signed-off-by: Marc Zyngier <maz@kernel.org>
2 parents 04e8c5b + c351ab7 commit 408f110

4 files changed

Lines changed: 150 additions & 58 deletions

File tree

drivers/gpio/gpio-tegra186.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,18 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type)
430430
else
431431
irq_set_handler_locked(data, handle_edge_irq);
432432

433-
return irq_chip_set_type_parent(data, type);
433+
if (data->parent_data)
434+
return irq_chip_set_type_parent(data, type);
435+
436+
return 0;
437+
}
438+
439+
static int tegra186_irq_set_wake(struct irq_data *data, unsigned int on)
440+
{
441+
if (data->parent_data)
442+
return irq_chip_set_wake_parent(data, on);
443+
444+
return 0;
434445
}
435446

436447
static void tegra186_gpio_irq(struct irq_desc *desc)
@@ -678,7 +689,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
678689
gpio->intc.irq_mask = tegra186_irq_mask;
679690
gpio->intc.irq_unmask = tegra186_irq_unmask;
680691
gpio->intc.irq_set_type = tegra186_irq_set_type;
681-
gpio->intc.irq_set_wake = irq_chip_set_wake_parent;
692+
gpio->intc.irq_set_wake = tegra186_irq_set_wake;
682693

683694
irq = &gpio->gpio.irq;
684695
irq->chip = &gpio->intc;

drivers/soc/tegra/pmc.c

Lines changed: 39 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1990,44 +1990,17 @@ static int tegra_pmc_irq_alloc(struct irq_domain *domain, unsigned int virq,
19901990
event->id,
19911991
&pmc->irq, pmc);
19921992

1993-
/*
1994-
* GPIOs don't have an equivalent interrupt in the
1995-
* parent controller (GIC). However some code, such
1996-
* as the one in irq_get_irqchip_state(), require a
1997-
* valid IRQ chip to be set. Make sure that's the
1998-
* case by passing NULL here, which will install a
1999-
* dummy IRQ chip for the interrupt in the parent
2000-
* domain.
2001-
*/
2002-
if (domain->parent)
2003-
irq_domain_set_hwirq_and_chip(domain->parent,
2004-
virq, 0, NULL,
2005-
NULL);
2006-
1993+
/* GPIO hierarchies stop at the PMC level */
1994+
if (!err && domain->parent)
1995+
err = irq_domain_disconnect_hierarchy(domain->parent,
1996+
virq);
20071997
break;
20081998
}
20091999
}
20102000

2011-
/*
2012-
* For interrupts that don't have associated wake events, assign a
2013-
* dummy hardware IRQ number. This is used in the ->irq_set_type()
2014-
* and ->irq_set_wake() callbacks to return early for these IRQs.
2015-
*/
2016-
if (i == soc->num_wake_events) {
2017-
err = irq_domain_set_hwirq_and_chip(domain, virq, ULONG_MAX,
2018-
&pmc->irq, pmc);
2019-
2020-
/*
2021-
* Interrupts without a wake event don't have a corresponding
2022-
* interrupt in the parent controller (GIC). Pass NULL for the
2023-
* chip here, which causes a dummy IRQ chip to be installed
2024-
* for the interrupt in the parent domain, to make this
2025-
* explicit.
2026-
*/
2027-
if (domain->parent)
2028-
irq_domain_set_hwirq_and_chip(domain->parent, virq, 0,
2029-
NULL, NULL);
2030-
}
2001+
/* If there is no wake-up event, there is no PMC mapping */
2002+
if (i == soc->num_wake_events)
2003+
err = irq_domain_disconnect_hierarchy(domain, virq);
20312004

20322005
return err;
20332006
}
@@ -2043,9 +2016,6 @@ static int tegra210_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
20432016
unsigned int offset, bit;
20442017
u32 value;
20452018

2046-
if (data->hwirq == ULONG_MAX)
2047-
return 0;
2048-
20492019
offset = data->hwirq / 32;
20502020
bit = data->hwirq % 32;
20512021

@@ -2080,9 +2050,6 @@ static int tegra210_pmc_irq_set_type(struct irq_data *data, unsigned int type)
20802050
unsigned int offset, bit;
20812051
u32 value;
20822052

2083-
if (data->hwirq == ULONG_MAX)
2084-
return 0;
2085-
20862053
offset = data->hwirq / 32;
20872054
bit = data->hwirq % 32;
20882055

@@ -2123,10 +2090,6 @@ static int tegra186_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
21232090
unsigned int offset, bit;
21242091
u32 value;
21252092

2126-
/* nothing to do if there's no associated wake event */
2127-
if (WARN_ON(data->hwirq == ULONG_MAX))
2128-
return 0;
2129-
21302093
offset = data->hwirq / 32;
21312094
bit = data->hwirq % 32;
21322095

@@ -2154,10 +2117,6 @@ static int tegra186_pmc_irq_set_type(struct irq_data *data, unsigned int type)
21542117
struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
21552118
u32 value;
21562119

2157-
/* nothing to do if there's no associated wake event */
2158-
if (data->hwirq == ULONG_MAX)
2159-
return 0;
2160-
21612120
value = readl(pmc->wake + WAKE_AOWAKE_CNTRL(data->hwirq));
21622121

21632122
switch (type) {
@@ -2184,6 +2143,34 @@ static int tegra186_pmc_irq_set_type(struct irq_data *data, unsigned int type)
21842143
return 0;
21852144
}
21862145

2146+
static void tegra_irq_mask_parent(struct irq_data *data)
2147+
{
2148+
if (data->parent_data)
2149+
irq_chip_mask_parent(data);
2150+
}
2151+
2152+
static void tegra_irq_unmask_parent(struct irq_data *data)
2153+
{
2154+
if (data->parent_data)
2155+
irq_chip_unmask_parent(data);
2156+
}
2157+
2158+
static void tegra_irq_eoi_parent(struct irq_data *data)
2159+
{
2160+
if (data->parent_data)
2161+
irq_chip_eoi_parent(data);
2162+
}
2163+
2164+
static int tegra_irq_set_affinity_parent(struct irq_data *data,
2165+
const struct cpumask *dest,
2166+
bool force)
2167+
{
2168+
if (data->parent_data)
2169+
return irq_chip_set_affinity_parent(data, dest, force);
2170+
2171+
return -EINVAL;
2172+
}
2173+
21872174
static int tegra_pmc_irq_init(struct tegra_pmc *pmc)
21882175
{
21892176
struct irq_domain *parent = NULL;
@@ -2199,10 +2186,10 @@ static int tegra_pmc_irq_init(struct tegra_pmc *pmc)
21992186
return 0;
22002187

22012188
pmc->irq.name = dev_name(pmc->dev);
2202-
pmc->irq.irq_mask = irq_chip_mask_parent;
2203-
pmc->irq.irq_unmask = irq_chip_unmask_parent;
2204-
pmc->irq.irq_eoi = irq_chip_eoi_parent;
2205-
pmc->irq.irq_set_affinity = irq_chip_set_affinity_parent;
2189+
pmc->irq.irq_mask = tegra_irq_mask_parent;
2190+
pmc->irq.irq_unmask = tegra_irq_unmask_parent;
2191+
pmc->irq.irq_eoi = tegra_irq_eoi_parent;
2192+
pmc->irq.irq_set_affinity = tegra_irq_set_affinity_parent;
22062193
pmc->irq.irq_set_type = pmc->soc->irq_set_type;
22072194
pmc->irq.irq_set_wake = pmc->soc->irq_set_wake;
22082195

include/linux/irqdomain.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,9 @@ extern void irq_domain_free_irqs_parent(struct irq_domain *domain,
509509
unsigned int irq_base,
510510
unsigned int nr_irqs);
511511

512+
extern int irq_domain_disconnect_hierarchy(struct irq_domain *domain,
513+
unsigned int virq);
514+
512515
static inline bool irq_domain_is_hierarchy(struct irq_domain *domain)
513516
{
514517
return domain->flags & IRQ_DOMAIN_FLAG_HIERARCHY;

kernel/irq/irqdomain.c

Lines changed: 95 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,6 +1136,17 @@ static struct irq_data *irq_domain_insert_irq_data(struct irq_domain *domain,
11361136
return irq_data;
11371137
}
11381138

1139+
static void __irq_domain_free_hierarchy(struct irq_data *irq_data)
1140+
{
1141+
struct irq_data *tmp;
1142+
1143+
while (irq_data) {
1144+
tmp = irq_data;
1145+
irq_data = irq_data->parent_data;
1146+
kfree(tmp);
1147+
}
1148+
}
1149+
11391150
static void irq_domain_free_irq_data(unsigned int virq, unsigned int nr_irqs)
11401151
{
11411152
struct irq_data *irq_data, *tmp;
@@ -1147,12 +1158,83 @@ static void irq_domain_free_irq_data(unsigned int virq, unsigned int nr_irqs)
11471158
irq_data->parent_data = NULL;
11481159
irq_data->domain = NULL;
11491160

1150-
while (tmp) {
1151-
irq_data = tmp;
1152-
tmp = tmp->parent_data;
1153-
kfree(irq_data);
1161+
__irq_domain_free_hierarchy(tmp);
1162+
}
1163+
}
1164+
1165+
/**
1166+
* irq_domain_disconnect_hierarchy - Mark the first unused level of a hierarchy
1167+
* @domain: IRQ domain from which the hierarchy is to be disconnected
1168+
* @virq: IRQ number where the hierarchy is to be trimmed
1169+
*
1170+
* Marks the @virq level belonging to @domain as disconnected.
1171+
* Returns -EINVAL if @virq doesn't have a valid irq_data pointing
1172+
* to @domain.
1173+
*
1174+
* Its only use is to be able to trim levels of hierarchy that do not
1175+
* have any real meaning for this interrupt, and that the driver marks
1176+
* as such from its .alloc() callback.
1177+
*/
1178+
int irq_domain_disconnect_hierarchy(struct irq_domain *domain,
1179+
unsigned int virq)
1180+
{
1181+
struct irq_data *irqd;
1182+
1183+
irqd = irq_domain_get_irq_data(domain, virq);
1184+
if (!irqd)
1185+
return -EINVAL;
1186+
1187+
irqd->chip = ERR_PTR(-ENOTCONN);
1188+
return 0;
1189+
}
1190+
1191+
static int irq_domain_trim_hierarchy(unsigned int virq)
1192+
{
1193+
struct irq_data *tail, *irqd, *irq_data;
1194+
1195+
irq_data = irq_get_irq_data(virq);
1196+
tail = NULL;
1197+
1198+
/* The first entry must have a valid irqchip */
1199+
if (!irq_data->chip || IS_ERR(irq_data->chip))
1200+
return -EINVAL;
1201+
1202+
/*
1203+
* Validate that the irq_data chain is sane in the presence of
1204+
* a hierarchy trimming marker.
1205+
*/
1206+
for (irqd = irq_data->parent_data; irqd; irq_data = irqd, irqd = irqd->parent_data) {
1207+
/* Can't have a valid irqchip after a trim marker */
1208+
if (irqd->chip && tail)
1209+
return -EINVAL;
1210+
1211+
/* Can't have an empty irqchip before a trim marker */
1212+
if (!irqd->chip && !tail)
1213+
return -EINVAL;
1214+
1215+
if (IS_ERR(irqd->chip)) {
1216+
/* Only -ENOTCONN is a valid trim marker */
1217+
if (PTR_ERR(irqd->chip) != -ENOTCONN)
1218+
return -EINVAL;
1219+
1220+
tail = irq_data;
11541221
}
11551222
}
1223+
1224+
/* No trim marker, nothing to do */
1225+
if (!tail)
1226+
return 0;
1227+
1228+
pr_info("IRQ%d: trimming hierarchy from %s\n",
1229+
virq, tail->parent_data->domain->name);
1230+
1231+
/* Sever the inner part of the hierarchy... */
1232+
irqd = tail;
1233+
tail = tail->parent_data;
1234+
irqd->parent_data = NULL;
1235+
__irq_domain_free_hierarchy(tail);
1236+
1237+
return 0;
11561238
}
11571239

11581240
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,
13621444
mutex_unlock(&irq_domain_mutex);
13631445
goto out_free_irq_data;
13641446
}
1447+
1448+
for (i = 0; i < nr_irqs; i++) {
1449+
ret = irq_domain_trim_hierarchy(virq + i);
1450+
if (ret) {
1451+
mutex_unlock(&irq_domain_mutex);
1452+
goto out_free_irq_data;
1453+
}
1454+
}
1455+
13651456
for (i = 0; i < nr_irqs; i++)
13661457
irq_domain_insert_irq(virq + i);
13671458
mutex_unlock(&irq_domain_mutex);

0 commit comments

Comments
 (0)