Skip to content

Commit b48ac83

Browse files
Marc ZyngierJason Cooper
authored andcommitted
irqchip: GICv3: ITS: MSI support
Now, the bit of code that allow us to use the ITS as a MSI controller. Both MSI and MSI-X are supported. Signed-off-by: Marc Zyngier <marc.zyngier@arm.com> Link: https://lkml.kernel.org/r/1416839720-18400-10-git-send-email-marc.zyngier@arm.com Signed-off-by: Jason Cooper <jason@lakedaemon.net>
1 parent 84a6a2e commit b48ac83

File tree

2 files changed

+182
-0
lines changed

2 files changed

+182
-0
lines changed

drivers/irqchip/irq-gic-v3-its.c

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,12 +587,47 @@ static int its_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
587587
return IRQ_SET_MASK_OK_DONE;
588588
}
589589

590+
static void its_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
591+
{
592+
struct its_device *its_dev = irq_data_get_irq_chip_data(d);
593+
struct its_node *its;
594+
u64 addr;
595+
596+
its = its_dev->its;
597+
addr = its->phys_base + GITS_TRANSLATER;
598+
599+
msg->address_lo = addr & ((1UL << 32) - 1);
600+
msg->address_hi = addr >> 32;
601+
msg->data = its_get_event_id(d);
602+
}
603+
590604
static struct irq_chip its_irq_chip = {
591605
.name = "ITS",
592606
.irq_mask = its_mask_irq,
593607
.irq_unmask = its_unmask_irq,
594608
.irq_eoi = its_eoi_irq,
595609
.irq_set_affinity = its_set_affinity,
610+
.irq_compose_msi_msg = its_irq_compose_msi_msg,
611+
};
612+
613+
static void its_mask_msi_irq(struct irq_data *d)
614+
{
615+
pci_msi_mask_irq(d);
616+
irq_chip_mask_parent(d);
617+
}
618+
619+
static void its_unmask_msi_irq(struct irq_data *d)
620+
{
621+
pci_msi_unmask_irq(d);
622+
irq_chip_unmask_parent(d);
623+
}
624+
625+
static struct irq_chip its_msi_irq_chip = {
626+
.name = "ITS-MSI",
627+
.irq_unmask = its_unmask_msi_irq,
628+
.irq_mask = its_mask_msi_irq,
629+
.irq_eoi = irq_chip_eoi_parent,
630+
.irq_write_msi_msg = pci_msi_domain_write_msg,
596631
};
597632

598633
/*
@@ -1055,3 +1090,144 @@ static void its_free_device(struct its_device *its_dev)
10551090
kfree(its_dev->itt);
10561091
kfree(its_dev);
10571092
}
1093+
1094+
static int its_alloc_device_irq(struct its_device *dev, irq_hw_number_t *hwirq)
1095+
{
1096+
int idx;
1097+
1098+
idx = find_first_zero_bit(dev->lpi_map, dev->nr_lpis);
1099+
if (idx == dev->nr_lpis)
1100+
return -ENOSPC;
1101+
1102+
*hwirq = dev->lpi_base + idx;
1103+
set_bit(idx, dev->lpi_map);
1104+
1105+
/* Map the GIC irq ID to the device */
1106+
its_send_mapvi(dev, *hwirq, idx);
1107+
1108+
return 0;
1109+
}
1110+
1111+
static int its_msi_prepare(struct irq_domain *domain, struct device *dev,
1112+
int nvec, msi_alloc_info_t *info)
1113+
{
1114+
struct pci_dev *pdev;
1115+
struct its_node *its;
1116+
u32 dev_id;
1117+
struct its_device *its_dev;
1118+
1119+
if (!dev_is_pci(dev))
1120+
return -EINVAL;
1121+
1122+
pdev = to_pci_dev(dev);
1123+
dev_id = PCI_DEVID(pdev->bus->number, pdev->devfn);
1124+
its = domain->parent->host_data;
1125+
1126+
its_dev = its_find_device(its, dev_id);
1127+
if (WARN_ON(its_dev))
1128+
return -EINVAL;
1129+
1130+
its_dev = its_create_device(its, dev_id, nvec);
1131+
if (!its_dev)
1132+
return -ENOMEM;
1133+
1134+
dev_dbg(&pdev->dev, "ITT %d entries, %d bits\n", nvec, ilog2(nvec));
1135+
1136+
info->scratchpad[0].ptr = its_dev;
1137+
info->scratchpad[1].ptr = dev;
1138+
return 0;
1139+
}
1140+
1141+
static struct msi_domain_ops its_pci_msi_ops = {
1142+
.msi_prepare = its_msi_prepare,
1143+
};
1144+
1145+
static struct msi_domain_info its_pci_msi_domain_info = {
1146+
.flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
1147+
MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX),
1148+
.ops = &its_pci_msi_ops,
1149+
.chip = &its_msi_irq_chip,
1150+
};
1151+
1152+
static int its_irq_gic_domain_alloc(struct irq_domain *domain,
1153+
unsigned int virq,
1154+
irq_hw_number_t hwirq)
1155+
{
1156+
struct of_phandle_args args;
1157+
1158+
args.np = domain->parent->of_node;
1159+
args.args_count = 3;
1160+
args.args[0] = GIC_IRQ_TYPE_LPI;
1161+
args.args[1] = hwirq;
1162+
args.args[2] = IRQ_TYPE_EDGE_RISING;
1163+
1164+
return irq_domain_alloc_irqs_parent(domain, virq, 1, &args);
1165+
}
1166+
1167+
static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
1168+
unsigned int nr_irqs, void *args)
1169+
{
1170+
msi_alloc_info_t *info = args;
1171+
struct its_device *its_dev = info->scratchpad[0].ptr;
1172+
irq_hw_number_t hwirq;
1173+
int err;
1174+
int i;
1175+
1176+
for (i = 0; i < nr_irqs; i++) {
1177+
err = its_alloc_device_irq(its_dev, &hwirq);
1178+
if (err)
1179+
return err;
1180+
1181+
err = its_irq_gic_domain_alloc(domain, virq + i, hwirq);
1182+
if (err)
1183+
return err;
1184+
1185+
irq_domain_set_hwirq_and_chip(domain, virq + i,
1186+
hwirq, &its_irq_chip, its_dev);
1187+
dev_dbg(info->scratchpad[1].ptr, "ID:%d pID:%d vID:%d\n",
1188+
(int)(hwirq - its_dev->lpi_base), (int)hwirq, virq + i);
1189+
}
1190+
1191+
return 0;
1192+
}
1193+
1194+
static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq,
1195+
unsigned int nr_irqs)
1196+
{
1197+
struct irq_data *d = irq_domain_get_irq_data(domain, virq);
1198+
struct its_device *its_dev = irq_data_get_irq_chip_data(d);
1199+
int i;
1200+
1201+
for (i = 0; i < nr_irqs; i++) {
1202+
struct irq_data *data = irq_domain_get_irq_data(domain,
1203+
virq + i);
1204+
int event = its_get_event_id(data);
1205+
1206+
/* Stop the delivery of interrupts */
1207+
its_send_discard(its_dev, event);
1208+
1209+
/* Mark interrupt index as unused */
1210+
clear_bit(event, its_dev->lpi_map);
1211+
1212+
/* Nuke the entry in the domain */
1213+
irq_domain_reset_irq_data(d);
1214+
}
1215+
1216+
/* If all interrupts have been freed, start mopping the floor */
1217+
if (bitmap_empty(its_dev->lpi_map, its_dev->nr_lpis)) {
1218+
its_lpi_free(its_dev->lpi_map,
1219+
its_dev->lpi_base,
1220+
its_dev->nr_lpis);
1221+
1222+
/* Unmap device/itt */
1223+
its_send_mapd(its_dev, 0);
1224+
its_free_device(its_dev);
1225+
}
1226+
1227+
irq_domain_free_irqs_parent(domain, virq, nr_irqs);
1228+
}
1229+
1230+
static const struct irq_domain_ops its_domain_ops = {
1231+
.alloc = its_irq_domain_alloc,
1232+
.free = its_irq_domain_free,
1233+
};

include/linux/irqchip/arm-gic-v3.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,12 @@
295295

296296
#include <linux/stringify.h>
297297

298+
/*
299+
* We need a value to serve as a irq-type for LPIs. Choose one that will
300+
* hopefully pique the interest of the reviewer.
301+
*/
302+
#define GIC_IRQ_TYPE_LPI 0xa110c8ed
303+
298304
struct rdists {
299305
struct {
300306
void __iomem *rd_base;

0 commit comments

Comments
 (0)