/* * Copyright (C) 2020, Red Hat Inc, Eric Auger * * This work is licensed under the terms of the GNU LGPL, version 2. */ #include #include void its_parse_typer(void) { u64 typer = readq(gicv3_its_base() + GITS_TYPER); struct its_typer *t = &its_data.typer; t->ite_size = ((typer & GITS_TYPER_ITT_ENTRY_SIZE) >> GITS_TYPER_ITT_ENTRY_SIZE_SHIFT) + 1; t->pta = typer & GITS_TYPER_PTA; t->eventid_bits = ((typer & GITS_TYPER_IDBITS) >> GITS_TYPER_IDBITS_SHIFT) + 1; t->deviceid_bits = ((typer & GITS_TYPER_DEVBITS) >> GITS_TYPER_DEVBITS_SHIFT) + 1; if (typer & GITS_TYPER_CIL) t->collid_bits = ((typer & GITS_TYPER_CIDBITS) >> GITS_TYPER_CIDBITS_SHIFT) + 1; else t->collid_bits = 16; t->virt_lpi = typer & GITS_TYPER_VLPIS; t->phys_lpi = typer & GITS_TYPER_PLPIS; } int its_baser_lookup(int type, struct its_baser *baser) { int i; for (i = 0; i < GITS_BASER_NR_REGS; i++) { void *reg_addr = gicv3_its_base() + GITS_BASER + i * 8; u64 val = readq(reg_addr); if (GITS_BASER_TYPE(val) == type) { assert((val & GITS_BASER_PAGE_SIZE_MASK) == GITS_BASER_PAGE_SIZE_64K); baser->esz = GITS_BASER_ENTRY_SIZE(val); baser->indirect = val & GITS_BASER_INDIRECT; baser->index = i; return 0; } } return -1; } /* * Allocate the BASER table (a single page of size @baser->psz) * and set the BASER valid */ static void its_baser_alloc_table(struct its_baser *baser, size_t size) { unsigned long order = get_order(size >> PAGE_SHIFT); void *reg_addr = gicv3_its_base() + GITS_BASER + baser->index * 8; u64 val = readq(reg_addr); baser->table_addr = alloc_pages(order); val |= virt_to_phys(baser->table_addr) | GITS_BASER_VALID; writeq(val, reg_addr); } /* * init_cmd_queue - Allocate the command queue and initialize * CBASER, CWRITER */ static void its_cmd_queue_init(void) { unsigned long order = get_order(SZ_64K >> PAGE_SHIFT); u64 cbaser; its_data.cmd_base = alloc_pages(order); cbaser = virt_to_phys(its_data.cmd_base) | (SZ_64K / SZ_4K - 1) | GITS_CBASER_VALID; writeq(cbaser, its_data.base + GITS_CBASER); its_data.cmd_write = its_data.cmd_base; writeq(0, its_data.base + GITS_CWRITER); } void its_init(void) { if (!its_data.base) return; its_parse_typer(); assert(!its_baser_lookup(GITS_BASER_TYPE_DEVICE, &its_data.device_baser)); assert(!its_baser_lookup(GITS_BASER_TYPE_COLLECTION, &its_data.coll_baser)); its_baser_alloc_table(&its_data.device_baser, SZ_64K); its_baser_alloc_table(&its_data.coll_baser, SZ_64K); its_cmd_queue_init(); } /* must be called after gicv3_enable_defaults */ void its_enable_defaults(void) { int cpu; /* Allocate LPI config and pending tables */ gicv3_lpi_alloc_tables(); for_each_online_cpu(cpu) gicv3_lpi_rdist_enable(cpu); writel(GITS_CTLR_ENABLE, its_data.base + GITS_CTLR); } struct its_device *its_create_device(u32 device_id, int nr_ites) { struct its_device *new; unsigned long n; assert(its_data.nr_devices < GITS_MAX_DEVICES); new = &its_data.devices[its_data.nr_devices]; new->device_id = device_id; new->nr_ites = nr_ites; n = (its_data.typer.ite_size * nr_ites) >> PAGE_SHIFT; new->itt = alloc_pages(get_order(n)); its_data.nr_devices++; return new; } struct its_collection *its_create_collection(u16 col_id, u32 pe) { struct its_collection *new; assert(its_data.nr_collections < GITS_MAX_COLLECTIONS); new = &its_data.collections[its_data.nr_collections]; new->col_id = col_id; if (its_data.typer.pta) new->target_address = (u64)gicv3_data.redist_base[pe]; else new->target_address = pe << 16; its_data.nr_collections++; return new; } struct its_device *its_get_device(u32 id) { int i; for (i = 0; i < GITS_MAX_DEVICES; i++) { if (its_data.devices[i].device_id == id) return &its_data.devices[i]; } assert(0); } struct its_collection *its_get_collection(u32 id) { int i; for (i = 0; i < GITS_MAX_COLLECTIONS; i++) { if (its_data.collections[i].col_id == id) return &its_data.collections[i]; } assert(0); }