diff options
author | Marc Zyngier <marc.zyngier@arm.com> | 2014-11-24 09:35:17 -0500 |
---|---|---|
committer | Jason Cooper <jason@lakedaemon.net> | 2014-11-26 10:55:14 -0500 |
commit | 4c21f3c26ecc25c5520628eef8e900a36e6c6ab4 (patch) | |
tree | 2760c93d0cceb451be7dde0e093a8f193e3684c9 /drivers/irqchip/irq-gic-v3-its.c | |
parent | b48ac83d6bbc20a973c3e8133fd1ebda873d026a (diff) |
irqchip: GICv3: ITS: DT probing and initialization
Add the code that probes the ITS from the device tree,
and initialize it.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Link: https://lkml.kernel.org/r/1416839720-18400-11-git-send-email-marc.zyngier@arm.com
Signed-off-by: Jason Cooper <jason@lakedaemon.net>
Diffstat (limited to 'drivers/irqchip/irq-gic-v3-its.c')
-rw-r--r-- | drivers/irqchip/irq-gic-v3-its.c | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 532c6df89992..e9d16151eed6 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c | |||
@@ -1231,3 +1231,172 @@ static const struct irq_domain_ops its_domain_ops = { | |||
1231 | .alloc = its_irq_domain_alloc, | 1231 | .alloc = its_irq_domain_alloc, |
1232 | .free = its_irq_domain_free, | 1232 | .free = its_irq_domain_free, |
1233 | }; | 1233 | }; |
1234 | |||
1235 | static int its_probe(struct device_node *node, struct irq_domain *parent) | ||
1236 | { | ||
1237 | struct resource res; | ||
1238 | struct its_node *its; | ||
1239 | void __iomem *its_base; | ||
1240 | u32 val; | ||
1241 | u64 baser, tmp; | ||
1242 | int err; | ||
1243 | |||
1244 | err = of_address_to_resource(node, 0, &res); | ||
1245 | if (err) { | ||
1246 | pr_warn("%s: no regs?\n", node->full_name); | ||
1247 | return -ENXIO; | ||
1248 | } | ||
1249 | |||
1250 | its_base = ioremap(res.start, resource_size(&res)); | ||
1251 | if (!its_base) { | ||
1252 | pr_warn("%s: unable to map registers\n", node->full_name); | ||
1253 | return -ENOMEM; | ||
1254 | } | ||
1255 | |||
1256 | val = readl_relaxed(its_base + GITS_PIDR2) & GIC_PIDR2_ARCH_MASK; | ||
1257 | if (val != 0x30 && val != 0x40) { | ||
1258 | pr_warn("%s: no ITS detected, giving up\n", node->full_name); | ||
1259 | err = -ENODEV; | ||
1260 | goto out_unmap; | ||
1261 | } | ||
1262 | |||
1263 | pr_info("ITS: %s\n", node->full_name); | ||
1264 | |||
1265 | its = kzalloc(sizeof(*its), GFP_KERNEL); | ||
1266 | if (!its) { | ||
1267 | err = -ENOMEM; | ||
1268 | goto out_unmap; | ||
1269 | } | ||
1270 | |||
1271 | raw_spin_lock_init(&its->lock); | ||
1272 | INIT_LIST_HEAD(&its->entry); | ||
1273 | INIT_LIST_HEAD(&its->its_device_list); | ||
1274 | its->base = its_base; | ||
1275 | its->phys_base = res.start; | ||
1276 | its->msi_chip.of_node = node; | ||
1277 | its->ite_size = ((readl_relaxed(its_base + GITS_TYPER) >> 4) & 0xf) + 1; | ||
1278 | |||
1279 | its->cmd_base = kzalloc(ITS_CMD_QUEUE_SZ, GFP_KERNEL); | ||
1280 | if (!its->cmd_base) { | ||
1281 | err = -ENOMEM; | ||
1282 | goto out_free_its; | ||
1283 | } | ||
1284 | its->cmd_write = its->cmd_base; | ||
1285 | |||
1286 | err = its_alloc_tables(its); | ||
1287 | if (err) | ||
1288 | goto out_free_cmd; | ||
1289 | |||
1290 | err = its_alloc_collections(its); | ||
1291 | if (err) | ||
1292 | goto out_free_tables; | ||
1293 | |||
1294 | baser = (virt_to_phys(its->cmd_base) | | ||
1295 | GITS_CBASER_WaWb | | ||
1296 | GITS_CBASER_InnerShareable | | ||
1297 | (ITS_CMD_QUEUE_SZ / SZ_4K - 1) | | ||
1298 | GITS_CBASER_VALID); | ||
1299 | |||
1300 | writeq_relaxed(baser, its->base + GITS_CBASER); | ||
1301 | tmp = readq_relaxed(its->base + GITS_CBASER); | ||
1302 | writeq_relaxed(0, its->base + GITS_CWRITER); | ||
1303 | writel_relaxed(1, its->base + GITS_CTLR); | ||
1304 | |||
1305 | if ((tmp ^ baser) & GITS_BASER_SHAREABILITY_MASK) { | ||
1306 | pr_info("ITS: using cache flushing for cmd queue\n"); | ||
1307 | its->flags |= ITS_FLAGS_CMDQ_NEEDS_FLUSHING; | ||
1308 | } | ||
1309 | |||
1310 | if (of_property_read_bool(its->msi_chip.of_node, "msi-controller")) { | ||
1311 | its->domain = irq_domain_add_tree(NULL, &its_domain_ops, its); | ||
1312 | if (!its->domain) { | ||
1313 | err = -ENOMEM; | ||
1314 | goto out_free_tables; | ||
1315 | } | ||
1316 | |||
1317 | its->domain->parent = parent; | ||
1318 | |||
1319 | its->msi_chip.domain = pci_msi_create_irq_domain(node, | ||
1320 | &its_pci_msi_domain_info, | ||
1321 | its->domain); | ||
1322 | if (!its->msi_chip.domain) { | ||
1323 | err = -ENOMEM; | ||
1324 | goto out_free_domains; | ||
1325 | } | ||
1326 | |||
1327 | err = of_pci_msi_chip_add(&its->msi_chip); | ||
1328 | if (err) | ||
1329 | goto out_free_domains; | ||
1330 | } | ||
1331 | |||
1332 | spin_lock(&its_lock); | ||
1333 | list_add(&its->entry, &its_nodes); | ||
1334 | spin_unlock(&its_lock); | ||
1335 | |||
1336 | return 0; | ||
1337 | |||
1338 | out_free_domains: | ||
1339 | if (its->msi_chip.domain) | ||
1340 | irq_domain_remove(its->msi_chip.domain); | ||
1341 | if (its->domain) | ||
1342 | irq_domain_remove(its->domain); | ||
1343 | out_free_tables: | ||
1344 | its_free_tables(its); | ||
1345 | out_free_cmd: | ||
1346 | kfree(its->cmd_base); | ||
1347 | out_free_its: | ||
1348 | kfree(its); | ||
1349 | out_unmap: | ||
1350 | iounmap(its_base); | ||
1351 | pr_err("ITS: failed probing %s (%d)\n", node->full_name, err); | ||
1352 | return err; | ||
1353 | } | ||
1354 | |||
1355 | static bool gic_rdists_supports_plpis(void) | ||
1356 | { | ||
1357 | return !!(readl_relaxed(gic_data_rdist_rd_base() + GICR_TYPER) & GICR_TYPER_PLPIS); | ||
1358 | } | ||
1359 | |||
1360 | int its_cpu_init(void) | ||
1361 | { | ||
1362 | if (!gic_rdists_supports_plpis()) { | ||
1363 | pr_info("CPU%d: LPIs not supported\n", smp_processor_id()); | ||
1364 | return -ENXIO; | ||
1365 | } | ||
1366 | |||
1367 | if (!list_empty(&its_nodes)) { | ||
1368 | its_cpu_init_lpis(); | ||
1369 | its_cpu_init_collection(); | ||
1370 | } | ||
1371 | |||
1372 | return 0; | ||
1373 | } | ||
1374 | |||
1375 | static struct of_device_id its_device_id[] = { | ||
1376 | { .compatible = "arm,gic-v3-its", }, | ||
1377 | {}, | ||
1378 | }; | ||
1379 | |||
1380 | int its_init(struct device_node *node, struct rdists *rdists, | ||
1381 | struct irq_domain *parent_domain) | ||
1382 | { | ||
1383 | struct device_node *np; | ||
1384 | |||
1385 | for (np = of_find_matching_node(node, its_device_id); np; | ||
1386 | np = of_find_matching_node(np, its_device_id)) { | ||
1387 | its_probe(np, parent_domain); | ||
1388 | } | ||
1389 | |||
1390 | if (list_empty(&its_nodes)) { | ||
1391 | pr_warn("ITS: No ITS available, not enabling LPIs\n"); | ||
1392 | return -ENXIO; | ||
1393 | } | ||
1394 | |||
1395 | gic_rdists = rdists; | ||
1396 | gic_root_node = node; | ||
1397 | |||
1398 | its_alloc_lpi_tables(); | ||
1399 | its_lpi_init(rdists->id_bits); | ||
1400 | |||
1401 | return 0; | ||
1402 | } | ||