diff options
Diffstat (limited to 'drivers/irqchip/irq-gic-v3-its.c')
-rw-r--r-- | drivers/irqchip/irq-gic-v3-its.c | 144 |
1 files changed, 41 insertions, 103 deletions
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index c00e2db351ba..26b55c53755f 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c | |||
@@ -30,14 +30,13 @@ | |||
30 | #include <linux/percpu.h> | 30 | #include <linux/percpu.h> |
31 | #include <linux/slab.h> | 31 | #include <linux/slab.h> |
32 | 32 | ||
33 | #include <linux/irqchip.h> | ||
33 | #include <linux/irqchip/arm-gic-v3.h> | 34 | #include <linux/irqchip/arm-gic-v3.h> |
34 | 35 | ||
35 | #include <asm/cacheflush.h> | 36 | #include <asm/cacheflush.h> |
36 | #include <asm/cputype.h> | 37 | #include <asm/cputype.h> |
37 | #include <asm/exception.h> | 38 | #include <asm/exception.h> |
38 | 39 | ||
39 | #include "irqchip.h" | ||
40 | |||
41 | #define ITS_FLAGS_CMDQ_NEEDS_FLUSHING (1 << 0) | 40 | #define ITS_FLAGS_CMDQ_NEEDS_FLUSHING (1 << 0) |
42 | 41 | ||
43 | #define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING (1 << 0) | 42 | #define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING (1 << 0) |
@@ -54,14 +53,12 @@ struct its_collection { | |||
54 | 53 | ||
55 | /* | 54 | /* |
56 | * The ITS structure - contains most of the infrastructure, with the | 55 | * The ITS structure - contains most of the infrastructure, with the |
57 | * msi_controller, the command queue, the collections, and the list of | 56 | * top-level MSI domain, the command queue, the collections, and the |
58 | * devices writing to it. | 57 | * list of devices writing to it. |
59 | */ | 58 | */ |
60 | struct its_node { | 59 | struct its_node { |
61 | raw_spinlock_t lock; | 60 | raw_spinlock_t lock; |
62 | struct list_head entry; | 61 | struct list_head entry; |
63 | struct msi_controller msi_chip; | ||
64 | struct irq_domain *domain; | ||
65 | void __iomem *base; | 62 | void __iomem *base; |
66 | unsigned long phys_base; | 63 | unsigned long phys_base; |
67 | struct its_cmd_block *cmd_base; | 64 | struct its_cmd_block *cmd_base; |
@@ -643,26 +640,6 @@ static struct irq_chip its_irq_chip = { | |||
643 | .irq_compose_msi_msg = its_irq_compose_msi_msg, | 640 | .irq_compose_msi_msg = its_irq_compose_msi_msg, |
644 | }; | 641 | }; |
645 | 642 | ||
646 | static void its_mask_msi_irq(struct irq_data *d) | ||
647 | { | ||
648 | pci_msi_mask_irq(d); | ||
649 | irq_chip_mask_parent(d); | ||
650 | } | ||
651 | |||
652 | static void its_unmask_msi_irq(struct irq_data *d) | ||
653 | { | ||
654 | pci_msi_unmask_irq(d); | ||
655 | irq_chip_unmask_parent(d); | ||
656 | } | ||
657 | |||
658 | static struct irq_chip its_msi_irq_chip = { | ||
659 | .name = "ITS-MSI", | ||
660 | .irq_unmask = its_unmask_msi_irq, | ||
661 | .irq_mask = its_mask_msi_irq, | ||
662 | .irq_eoi = irq_chip_eoi_parent, | ||
663 | .irq_write_msi_msg = pci_msi_domain_write_msg, | ||
664 | }; | ||
665 | |||
666 | /* | 643 | /* |
667 | * How we allocate LPIs: | 644 | * How we allocate LPIs: |
668 | * | 645 | * |
@@ -831,7 +808,7 @@ static void its_free_tables(struct its_node *its) | |||
831 | } | 808 | } |
832 | } | 809 | } |
833 | 810 | ||
834 | static int its_alloc_tables(struct its_node *its) | 811 | static int its_alloc_tables(const char *node_name, struct its_node *its) |
835 | { | 812 | { |
836 | int err; | 813 | int err; |
837 | int i; | 814 | int i; |
@@ -874,7 +851,7 @@ static int its_alloc_tables(struct its_node *its) | |||
874 | if (order >= MAX_ORDER) { | 851 | if (order >= MAX_ORDER) { |
875 | order = MAX_ORDER - 1; | 852 | order = MAX_ORDER - 1; |
876 | pr_warn("%s: Device Table too large, reduce its page order to %u\n", | 853 | pr_warn("%s: Device Table too large, reduce its page order to %u\n", |
877 | its->msi_chip.of_node->full_name, order); | 854 | node_name, order); |
878 | } | 855 | } |
879 | } | 856 | } |
880 | 857 | ||
@@ -944,7 +921,7 @@ retry_baser: | |||
944 | 921 | ||
945 | if (val != tmp) { | 922 | if (val != tmp) { |
946 | pr_err("ITS: %s: GITS_BASER%d doesn't stick: %lx %lx\n", | 923 | pr_err("ITS: %s: GITS_BASER%d doesn't stick: %lx %lx\n", |
947 | its->msi_chip.of_node->full_name, i, | 924 | node_name, i, |
948 | (unsigned long) val, (unsigned long) tmp); | 925 | (unsigned long) val, (unsigned long) tmp); |
949 | err = -ENXIO; | 926 | err = -ENXIO; |
950 | goto out_free; | 927 | goto out_free; |
@@ -1209,85 +1186,50 @@ static int its_alloc_device_irq(struct its_device *dev, irq_hw_number_t *hwirq) | |||
1209 | return 0; | 1186 | return 0; |
1210 | } | 1187 | } |
1211 | 1188 | ||
1212 | struct its_pci_alias { | ||
1213 | struct pci_dev *pdev; | ||
1214 | u32 dev_id; | ||
1215 | u32 count; | ||
1216 | }; | ||
1217 | |||
1218 | static int its_pci_msi_vec_count(struct pci_dev *pdev) | ||
1219 | { | ||
1220 | int msi, msix; | ||
1221 | |||
1222 | msi = max(pci_msi_vec_count(pdev), 0); | ||
1223 | msix = max(pci_msix_vec_count(pdev), 0); | ||
1224 | |||
1225 | return max(msi, msix); | ||
1226 | } | ||
1227 | |||
1228 | static int its_get_pci_alias(struct pci_dev *pdev, u16 alias, void *data) | ||
1229 | { | ||
1230 | struct its_pci_alias *dev_alias = data; | ||
1231 | |||
1232 | dev_alias->dev_id = alias; | ||
1233 | if (pdev != dev_alias->pdev) | ||
1234 | dev_alias->count += its_pci_msi_vec_count(dev_alias->pdev); | ||
1235 | |||
1236 | return 0; | ||
1237 | } | ||
1238 | |||
1239 | static int its_msi_prepare(struct irq_domain *domain, struct device *dev, | 1189 | static int its_msi_prepare(struct irq_domain *domain, struct device *dev, |
1240 | int nvec, msi_alloc_info_t *info) | 1190 | int nvec, msi_alloc_info_t *info) |
1241 | { | 1191 | { |
1242 | struct pci_dev *pdev; | ||
1243 | struct its_node *its; | 1192 | struct its_node *its; |
1244 | struct its_device *its_dev; | 1193 | struct its_device *its_dev; |
1245 | struct its_pci_alias dev_alias; | 1194 | struct msi_domain_info *msi_info; |
1246 | 1195 | u32 dev_id; | |
1247 | if (!dev_is_pci(dev)) | ||
1248 | return -EINVAL; | ||
1249 | 1196 | ||
1250 | pdev = to_pci_dev(dev); | 1197 | /* |
1251 | dev_alias.pdev = pdev; | 1198 | * We ignore "dev" entierely, and rely on the dev_id that has |
1252 | dev_alias.count = nvec; | 1199 | * been passed via the scratchpad. This limits this domain's |
1200 | * usefulness to upper layers that definitely know that they | ||
1201 | * are built on top of the ITS. | ||
1202 | */ | ||
1203 | dev_id = info->scratchpad[0].ul; | ||
1253 | 1204 | ||
1254 | pci_for_each_dma_alias(pdev, its_get_pci_alias, &dev_alias); | 1205 | msi_info = msi_get_domain_info(domain); |
1255 | its = domain->parent->host_data; | 1206 | its = msi_info->data; |
1256 | 1207 | ||
1257 | its_dev = its_find_device(its, dev_alias.dev_id); | 1208 | its_dev = its_find_device(its, dev_id); |
1258 | if (its_dev) { | 1209 | if (its_dev) { |
1259 | /* | 1210 | /* |
1260 | * We already have seen this ID, probably through | 1211 | * We already have seen this ID, probably through |
1261 | * another alias (PCI bridge of some sort). No need to | 1212 | * another alias (PCI bridge of some sort). No need to |
1262 | * create the device. | 1213 | * create the device. |
1263 | */ | 1214 | */ |
1264 | dev_dbg(dev, "Reusing ITT for devID %x\n", dev_alias.dev_id); | 1215 | pr_debug("Reusing ITT for devID %x\n", dev_id); |
1265 | goto out; | 1216 | goto out; |
1266 | } | 1217 | } |
1267 | 1218 | ||
1268 | its_dev = its_create_device(its, dev_alias.dev_id, dev_alias.count); | 1219 | its_dev = its_create_device(its, dev_id, nvec); |
1269 | if (!its_dev) | 1220 | if (!its_dev) |
1270 | return -ENOMEM; | 1221 | return -ENOMEM; |
1271 | 1222 | ||
1272 | dev_dbg(&pdev->dev, "ITT %d entries, %d bits\n", | 1223 | pr_debug("ITT %d entries, %d bits\n", nvec, ilog2(nvec)); |
1273 | dev_alias.count, ilog2(dev_alias.count)); | ||
1274 | out: | 1224 | out: |
1275 | info->scratchpad[0].ptr = its_dev; | 1225 | info->scratchpad[0].ptr = its_dev; |
1276 | info->scratchpad[1].ptr = dev; | ||
1277 | return 0; | 1226 | return 0; |
1278 | } | 1227 | } |
1279 | 1228 | ||
1280 | static struct msi_domain_ops its_pci_msi_ops = { | 1229 | static struct msi_domain_ops its_msi_domain_ops = { |
1281 | .msi_prepare = its_msi_prepare, | 1230 | .msi_prepare = its_msi_prepare, |
1282 | }; | 1231 | }; |
1283 | 1232 | ||
1284 | static struct msi_domain_info its_pci_msi_domain_info = { | ||
1285 | .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | | ||
1286 | MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX), | ||
1287 | .ops = &its_pci_msi_ops, | ||
1288 | .chip = &its_msi_irq_chip, | ||
1289 | }; | ||
1290 | |||
1291 | static int its_irq_gic_domain_alloc(struct irq_domain *domain, | 1233 | static int its_irq_gic_domain_alloc(struct irq_domain *domain, |
1292 | unsigned int virq, | 1234 | unsigned int virq, |
1293 | irq_hw_number_t hwirq) | 1235 | irq_hw_number_t hwirq) |
@@ -1323,9 +1265,9 @@ static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, | |||
1323 | 1265 | ||
1324 | irq_domain_set_hwirq_and_chip(domain, virq + i, | 1266 | irq_domain_set_hwirq_and_chip(domain, virq + i, |
1325 | hwirq, &its_irq_chip, its_dev); | 1267 | hwirq, &its_irq_chip, its_dev); |
1326 | dev_dbg(info->scratchpad[1].ptr, "ID:%d pID:%d vID:%d\n", | 1268 | pr_debug("ID:%d pID:%d vID:%d\n", |
1327 | (int)(hwirq - its_dev->event_map.lpi_base), | 1269 | (int)(hwirq - its_dev->event_map.lpi_base), |
1328 | (int)hwirq, virq + i); | 1270 | (int) hwirq, virq + i); |
1329 | } | 1271 | } |
1330 | 1272 | ||
1331 | return 0; | 1273 | return 0; |
@@ -1426,6 +1368,7 @@ static int its_probe(struct device_node *node, struct irq_domain *parent) | |||
1426 | struct resource res; | 1368 | struct resource res; |
1427 | struct its_node *its; | 1369 | struct its_node *its; |
1428 | void __iomem *its_base; | 1370 | void __iomem *its_base; |
1371 | struct irq_domain *inner_domain; | ||
1429 | u32 val; | 1372 | u32 val; |
1430 | u64 baser, tmp; | 1373 | u64 baser, tmp; |
1431 | int err; | 1374 | int err; |
@@ -1469,7 +1412,6 @@ static int its_probe(struct device_node *node, struct irq_domain *parent) | |||
1469 | INIT_LIST_HEAD(&its->its_device_list); | 1412 | INIT_LIST_HEAD(&its->its_device_list); |
1470 | its->base = its_base; | 1413 | its->base = its_base; |
1471 | its->phys_base = res.start; | 1414 | its->phys_base = res.start; |
1472 | its->msi_chip.of_node = node; | ||
1473 | its->ite_size = ((readl_relaxed(its_base + GITS_TYPER) >> 4) & 0xf) + 1; | 1415 | its->ite_size = ((readl_relaxed(its_base + GITS_TYPER) >> 4) & 0xf) + 1; |
1474 | 1416 | ||
1475 | its->cmd_base = kzalloc(ITS_CMD_QUEUE_SZ, GFP_KERNEL); | 1417 | its->cmd_base = kzalloc(ITS_CMD_QUEUE_SZ, GFP_KERNEL); |
@@ -1479,7 +1421,7 @@ static int its_probe(struct device_node *node, struct irq_domain *parent) | |||
1479 | } | 1421 | } |
1480 | its->cmd_write = its->cmd_base; | 1422 | its->cmd_write = its->cmd_base; |
1481 | 1423 | ||
1482 | err = its_alloc_tables(its); | 1424 | err = its_alloc_tables(node->full_name, its); |
1483 | if (err) | 1425 | if (err) |
1484 | goto out_free_cmd; | 1426 | goto out_free_cmd; |
1485 | 1427 | ||
@@ -1515,26 +1457,27 @@ static int its_probe(struct device_node *node, struct irq_domain *parent) | |||
1515 | writeq_relaxed(0, its->base + GITS_CWRITER); | 1457 | writeq_relaxed(0, its->base + GITS_CWRITER); |
1516 | writel_relaxed(GITS_CTLR_ENABLE, its->base + GITS_CTLR); | 1458 | writel_relaxed(GITS_CTLR_ENABLE, its->base + GITS_CTLR); |
1517 | 1459 | ||
1518 | if (of_property_read_bool(its->msi_chip.of_node, "msi-controller")) { | 1460 | if (of_property_read_bool(node, "msi-controller")) { |
1519 | its->domain = irq_domain_add_tree(NULL, &its_domain_ops, its); | 1461 | struct msi_domain_info *info; |
1520 | if (!its->domain) { | 1462 | |
1463 | info = kzalloc(sizeof(*info), GFP_KERNEL); | ||
1464 | if (!info) { | ||
1521 | err = -ENOMEM; | 1465 | err = -ENOMEM; |
1522 | goto out_free_tables; | 1466 | goto out_free_tables; |
1523 | } | 1467 | } |
1524 | 1468 | ||
1525 | its->domain->parent = parent; | 1469 | inner_domain = irq_domain_add_tree(node, &its_domain_ops, its); |
1526 | 1470 | if (!inner_domain) { | |
1527 | its->msi_chip.domain = pci_msi_create_irq_domain(node, | ||
1528 | &its_pci_msi_domain_info, | ||
1529 | its->domain); | ||
1530 | if (!its->msi_chip.domain) { | ||
1531 | err = -ENOMEM; | 1471 | err = -ENOMEM; |
1532 | goto out_free_domains; | 1472 | kfree(info); |
1473 | goto out_free_tables; | ||
1533 | } | 1474 | } |
1534 | 1475 | ||
1535 | err = of_pci_msi_chip_add(&its->msi_chip); | 1476 | inner_domain->parent = parent; |
1536 | if (err) | 1477 | inner_domain->bus_token = DOMAIN_BUS_NEXUS; |
1537 | goto out_free_domains; | 1478 | info->ops = &its_msi_domain_ops; |
1479 | info->data = its; | ||
1480 | inner_domain->host_data = info; | ||
1538 | } | 1481 | } |
1539 | 1482 | ||
1540 | spin_lock(&its_lock); | 1483 | spin_lock(&its_lock); |
@@ -1543,11 +1486,6 @@ static int its_probe(struct device_node *node, struct irq_domain *parent) | |||
1543 | 1486 | ||
1544 | return 0; | 1487 | return 0; |
1545 | 1488 | ||
1546 | out_free_domains: | ||
1547 | if (its->msi_chip.domain) | ||
1548 | irq_domain_remove(its->msi_chip.domain); | ||
1549 | if (its->domain) | ||
1550 | irq_domain_remove(its->domain); | ||
1551 | out_free_tables: | 1489 | out_free_tables: |
1552 | its_free_tables(its); | 1490 | its_free_tables(its); |
1553 | out_free_cmd: | 1491 | out_free_cmd: |