Commit d5df9dc9 authored by Marc Zyngier's avatar Marc Zyngier

irqchip/gic-v3-its: Probe ITS page size for all GITS_BASERn registers

The GICv3 ITS driver assumes that once it has latched on a page size for
a given BASER register, it can use the same page size as the maximum
page size for all subsequent BASER registers.

Although it worked so far, nothing in the architecture guarantees this,
and Nianyao Tang hit this problem on some undisclosed implementation.

Let's bite the bullet and probe the the supported page size on all BASER
registers before starting to populate the tables. This simplifies the
setup a bit, at the expense of a few additional MMIO accesses.
Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
Reported-by: default avatarNianyao Tang <tangnianyao@huawei.com>
Tested-by: default avatarNianyao Tang <tangnianyao@huawei.com>
Link: https://lore.kernel.org/r/1584089195-63897-1-git-send-email-zhangshaokun@hisilicon.com
parent bd59b343
...@@ -2036,18 +2036,17 @@ static void its_write_baser(struct its_node *its, struct its_baser *baser, ...@@ -2036,18 +2036,17 @@ static void its_write_baser(struct its_node *its, struct its_baser *baser,
} }
static int its_setup_baser(struct its_node *its, struct its_baser *baser, static int its_setup_baser(struct its_node *its, struct its_baser *baser,
u64 cache, u64 shr, u32 psz, u32 order, u64 cache, u64 shr, u32 order, bool indirect)
bool indirect)
{ {
u64 val = its_read_baser(its, baser); u64 val = its_read_baser(its, baser);
u64 esz = GITS_BASER_ENTRY_SIZE(val); u64 esz = GITS_BASER_ENTRY_SIZE(val);
u64 type = GITS_BASER_TYPE(val); u64 type = GITS_BASER_TYPE(val);
u64 baser_phys, tmp; u64 baser_phys, tmp;
u32 alloc_pages; u32 alloc_pages, psz;
struct page *page; struct page *page;
void *base; void *base;
retry_alloc_baser: psz = baser->psz;
alloc_pages = (PAGE_ORDER_TO_SIZE(order) / psz); alloc_pages = (PAGE_ORDER_TO_SIZE(order) / psz);
if (alloc_pages > GITS_BASER_PAGES_MAX) { if (alloc_pages > GITS_BASER_PAGES_MAX) {
pr_warn("ITS@%pa: %s too large, reduce ITS pages %u->%u\n", pr_warn("ITS@%pa: %s too large, reduce ITS pages %u->%u\n",
...@@ -2120,25 +2119,6 @@ static int its_setup_baser(struct its_node *its, struct its_baser *baser, ...@@ -2120,25 +2119,6 @@ static int its_setup_baser(struct its_node *its, struct its_baser *baser,
goto retry_baser; goto retry_baser;
} }
if ((val ^ tmp) & GITS_BASER_PAGE_SIZE_MASK) {
/*
* Page size didn't stick. Let's try a smaller
* size and retry. If we reach 4K, then
* something is horribly wrong...
*/
free_pages((unsigned long)base, order);
baser->base = NULL;
switch (psz) {
case SZ_16K:
psz = SZ_4K;
goto retry_alloc_baser;
case SZ_64K:
psz = SZ_16K;
goto retry_alloc_baser;
}
}
if (val != tmp) { if (val != tmp) {
pr_err("ITS@%pa: %s doesn't stick: %llx %llx\n", pr_err("ITS@%pa: %s doesn't stick: %llx %llx\n",
&its->phys_base, its_base_type_string[type], &its->phys_base, its_base_type_string[type],
...@@ -2164,13 +2144,14 @@ static int its_setup_baser(struct its_node *its, struct its_baser *baser, ...@@ -2164,13 +2144,14 @@ static int its_setup_baser(struct its_node *its, struct its_baser *baser,
static bool its_parse_indirect_baser(struct its_node *its, static bool its_parse_indirect_baser(struct its_node *its,
struct its_baser *baser, struct its_baser *baser,
u32 psz, u32 *order, u32 ids) u32 *order, u32 ids)
{ {
u64 tmp = its_read_baser(its, baser); u64 tmp = its_read_baser(its, baser);
u64 type = GITS_BASER_TYPE(tmp); u64 type = GITS_BASER_TYPE(tmp);
u64 esz = GITS_BASER_ENTRY_SIZE(tmp); u64 esz = GITS_BASER_ENTRY_SIZE(tmp);
u64 val = GITS_BASER_InnerShareable | GITS_BASER_RaWaWb; u64 val = GITS_BASER_InnerShareable | GITS_BASER_RaWaWb;
u32 new_order = *order; u32 new_order = *order;
u32 psz = baser->psz;
bool indirect = false; bool indirect = false;
/* No need to enable Indirection if memory requirement < (psz*2)bytes */ /* No need to enable Indirection if memory requirement < (psz*2)bytes */
...@@ -2288,11 +2269,58 @@ static void its_free_tables(struct its_node *its) ...@@ -2288,11 +2269,58 @@ static void its_free_tables(struct its_node *its)
} }
} }
static int its_probe_baser_psz(struct its_node *its, struct its_baser *baser)
{
u64 psz = SZ_64K;
while (psz) {
u64 val, gpsz;
val = its_read_baser(its, baser);
val &= ~GITS_BASER_PAGE_SIZE_MASK;
switch (psz) {
case SZ_64K:
gpsz = GITS_BASER_PAGE_SIZE_64K;
break;
case SZ_16K:
gpsz = GITS_BASER_PAGE_SIZE_16K;
break;
case SZ_4K:
default:
gpsz = GITS_BASER_PAGE_SIZE_4K;
break;
}
gpsz >>= GITS_BASER_PAGE_SIZE_SHIFT;
val |= FIELD_PREP(GITS_BASER_PAGE_SIZE_MASK, gpsz);
its_write_baser(its, baser, val);
if (FIELD_GET(GITS_BASER_PAGE_SIZE_MASK, baser->val) == gpsz)
break;
switch (psz) {
case SZ_64K:
psz = SZ_16K;
break;
case SZ_16K:
psz = SZ_4K;
break;
case SZ_4K:
default:
return -1;
}
}
baser->psz = psz;
return 0;
}
static int its_alloc_tables(struct its_node *its) static int its_alloc_tables(struct its_node *its)
{ {
u64 shr = GITS_BASER_InnerShareable; u64 shr = GITS_BASER_InnerShareable;
u64 cache = GITS_BASER_RaWaWb; u64 cache = GITS_BASER_RaWaWb;
u32 psz = SZ_64K;
int err, i; int err, i;
if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_22375) if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_22375)
...@@ -2303,16 +2331,22 @@ static int its_alloc_tables(struct its_node *its) ...@@ -2303,16 +2331,22 @@ static int its_alloc_tables(struct its_node *its)
struct its_baser *baser = its->tables + i; struct its_baser *baser = its->tables + i;
u64 val = its_read_baser(its, baser); u64 val = its_read_baser(its, baser);
u64 type = GITS_BASER_TYPE(val); u64 type = GITS_BASER_TYPE(val);
u32 order = get_order(psz);
bool indirect = false; bool indirect = false;
u32 order;
switch (type) { if (type == GITS_BASER_TYPE_NONE)
case GITS_BASER_TYPE_NONE:
continue; continue;
if (its_probe_baser_psz(its, baser)) {
its_free_tables(its);
return -ENXIO;
}
order = get_order(baser->psz);
switch (type) {
case GITS_BASER_TYPE_DEVICE: case GITS_BASER_TYPE_DEVICE:
indirect = its_parse_indirect_baser(its, baser, indirect = its_parse_indirect_baser(its, baser, &order,
psz, &order,
device_ids(its)); device_ids(its));
break; break;
...@@ -2328,20 +2362,18 @@ static int its_alloc_tables(struct its_node *its) ...@@ -2328,20 +2362,18 @@ static int its_alloc_tables(struct its_node *its)
} }
} }
indirect = its_parse_indirect_baser(its, baser, indirect = its_parse_indirect_baser(its, baser, &order,
psz, &order,
ITS_MAX_VPEID_BITS); ITS_MAX_VPEID_BITS);
break; break;
} }
err = its_setup_baser(its, baser, cache, shr, psz, order, indirect); err = its_setup_baser(its, baser, cache, shr, order, indirect);
if (err < 0) { if (err < 0) {
its_free_tables(its); its_free_tables(its);
return err; return err;
} }
/* Update settings which will be used for next BASERn */ /* Update settings which will be used for next BASERn */
psz = baser->psz;
cache = baser->val & GITS_BASER_CACHEABILITY_MASK; cache = baser->val & GITS_BASER_CACHEABILITY_MASK;
shr = baser->val & GITS_BASER_SHAREABILITY_MASK; shr = baser->val & GITS_BASER_SHAREABILITY_MASK;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment