aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/irqchip
diff options
context:
space:
mode:
authorMarc Zyngier <marc.zyngier@arm.com>2014-11-24 09:35:16 -0500
committerJason Cooper <jason@lakedaemon.net>2014-11-26 10:55:14 -0500
commitb48ac83d6bbc20a973c3e8133fd1ebda873d026a (patch)
treef592163f05badbefbba54110f89e647145b06881 /drivers/irqchip
parent84a6a2e7fc18dae444c5c88cc6af8878552867a5 (diff)
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>
Diffstat (limited to 'drivers/irqchip')
-rw-r--r--drivers/irqchip/irq-gic-v3-its.c176
1 files changed, 176 insertions, 0 deletions
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index d687fd43fbbb..532c6df89992 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -587,12 +587,47 @@ static int its_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
587 return IRQ_SET_MASK_OK_DONE; 587 return IRQ_SET_MASK_OK_DONE;
588} 588}
589 589
590static 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
590static struct irq_chip its_irq_chip = { 604static struct irq_chip its_irq_chip = {
591 .name = "ITS", 605 .name = "ITS",
592 .irq_mask = its_mask_irq, 606 .irq_mask = its_mask_irq,
593 .irq_unmask = its_unmask_irq, 607 .irq_unmask = its_unmask_irq,
594 .irq_eoi = its_eoi_irq, 608 .irq_eoi = its_eoi_irq,
595 .irq_set_affinity = its_set_affinity, 609 .irq_set_affinity = its_set_affinity,
610 .irq_compose_msi_msg = its_irq_compose_msi_msg,
611};
612
613static void its_mask_msi_irq(struct irq_data *d)
614{
615 pci_msi_mask_irq(d);
616 irq_chip_mask_parent(d);
617}
618
619static void its_unmask_msi_irq(struct irq_data *d)
620{
621 pci_msi_unmask_irq(d);
622 irq_chip_unmask_parent(d);
623}
624
625static 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,
596}; 631};
597 632
598/* 633/*
@@ -1055,3 +1090,144 @@ static void its_free_device(struct its_device *its_dev)
1055 kfree(its_dev->itt); 1090 kfree(its_dev->itt);
1056 kfree(its_dev); 1091 kfree(its_dev);
1057} 1092}
1093
1094static 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
1111static 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
1141static struct msi_domain_ops its_pci_msi_ops = {
1142 .msi_prepare = its_msi_prepare,
1143};
1144
1145static 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
1152static 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
1167static 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
1194static 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
1230static const struct irq_domain_ops its_domain_ops = {
1231 .alloc = its_irq_domain_alloc,
1232 .free = its_irq_domain_free,
1233};