mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-13 00:29:50 +00:00
irqchip/gicv3-its: Split its_alloc_tables() into two functions
The function is getting out of control, it has too many goto statements and would be too complicated for adding a feature two-level device table. So, it is time for us to cleanup and move some of the logic to a separate function without affecting the existing functionality. Signed-off-by: Shanker Donthineni <shankerd@codeaurora.org> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
This commit is contained in:
parent
4b75c4598b
commit
9347359ad0
@ -56,13 +56,14 @@ struct its_collection {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The ITS_BASER structure - contains memory information and cached
|
* The ITS_BASER structure - contains memory information, cached
|
||||||
* value of BASER register configuration.
|
* value of BASER register configuration and ITS page size.
|
||||||
*/
|
*/
|
||||||
struct its_baser {
|
struct its_baser {
|
||||||
void *base;
|
void *base;
|
||||||
u64 val;
|
u64 val;
|
||||||
u32 order;
|
u32 order;
|
||||||
|
u32 psz;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -840,6 +841,110 @@ static void its_write_baser(struct its_node *its, struct its_baser *baser,
|
|||||||
baser->val = its_read_baser(its, baser);
|
baser->val = its_read_baser(its, baser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int its_setup_baser(struct its_node *its, struct its_baser *baser,
|
||||||
|
u64 cache, u64 shr, u32 psz, u32 order)
|
||||||
|
{
|
||||||
|
u64 val = its_read_baser(its, baser);
|
||||||
|
u64 esz = GITS_BASER_ENTRY_SIZE(val);
|
||||||
|
u64 type = GITS_BASER_TYPE(val);
|
||||||
|
u32 alloc_pages;
|
||||||
|
void *base;
|
||||||
|
u64 tmp;
|
||||||
|
|
||||||
|
retry_alloc_baser:
|
||||||
|
alloc_pages = (PAGE_ORDER_TO_SIZE(order) / psz);
|
||||||
|
if (alloc_pages > GITS_BASER_PAGES_MAX) {
|
||||||
|
pr_warn("ITS@%pa: %s too large, reduce ITS pages %u->%u\n",
|
||||||
|
&its->phys_base, its_base_type_string[type],
|
||||||
|
alloc_pages, GITS_BASER_PAGES_MAX);
|
||||||
|
alloc_pages = GITS_BASER_PAGES_MAX;
|
||||||
|
order = get_order(GITS_BASER_PAGES_MAX * psz);
|
||||||
|
}
|
||||||
|
|
||||||
|
base = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, order);
|
||||||
|
if (!base)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
retry_baser:
|
||||||
|
val = (virt_to_phys(base) |
|
||||||
|
(type << GITS_BASER_TYPE_SHIFT) |
|
||||||
|
((esz - 1) << GITS_BASER_ENTRY_SIZE_SHIFT) |
|
||||||
|
((alloc_pages - 1) << GITS_BASER_PAGES_SHIFT) |
|
||||||
|
cache |
|
||||||
|
shr |
|
||||||
|
GITS_BASER_VALID);
|
||||||
|
|
||||||
|
switch (psz) {
|
||||||
|
case SZ_4K:
|
||||||
|
val |= GITS_BASER_PAGE_SIZE_4K;
|
||||||
|
break;
|
||||||
|
case SZ_16K:
|
||||||
|
val |= GITS_BASER_PAGE_SIZE_16K;
|
||||||
|
break;
|
||||||
|
case SZ_64K:
|
||||||
|
val |= GITS_BASER_PAGE_SIZE_64K;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
its_write_baser(its, baser, val);
|
||||||
|
tmp = baser->val;
|
||||||
|
|
||||||
|
if ((val ^ tmp) & GITS_BASER_SHAREABILITY_MASK) {
|
||||||
|
/*
|
||||||
|
* Shareability didn't stick. Just use
|
||||||
|
* whatever the read reported, which is likely
|
||||||
|
* to be the only thing this redistributor
|
||||||
|
* supports. If that's zero, make it
|
||||||
|
* non-cacheable as well.
|
||||||
|
*/
|
||||||
|
shr = tmp & GITS_BASER_SHAREABILITY_MASK;
|
||||||
|
if (!shr) {
|
||||||
|
cache = GITS_BASER_nC;
|
||||||
|
__flush_dcache_area(base, PAGE_ORDER_TO_SIZE(order));
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
pr_err("ITS@%pa: %s doesn't stick: %lx %lx\n",
|
||||||
|
&its->phys_base, its_base_type_string[type],
|
||||||
|
(unsigned long) val, (unsigned long) tmp);
|
||||||
|
free_pages((unsigned long)base, order);
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
baser->order = order;
|
||||||
|
baser->base = base;
|
||||||
|
baser->psz = psz;
|
||||||
|
|
||||||
|
pr_info("ITS@%pa: allocated %d %s @%lx (psz %dK, shr %d)\n",
|
||||||
|
&its->phys_base, (int)(PAGE_ORDER_TO_SIZE(order) / esz),
|
||||||
|
its_base_type_string[type],
|
||||||
|
(unsigned long)virt_to_phys(base),
|
||||||
|
psz / SZ_1K, (int)shr >> GITS_BASER_SHAREABILITY_SHIFT);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void its_parse_baser_device(struct its_node *its, struct its_baser *baser,
|
static void its_parse_baser_device(struct its_node *its, struct its_baser *baser,
|
||||||
u32 *order)
|
u32 *order)
|
||||||
{
|
{
|
||||||
@ -879,25 +984,20 @@ static void its_free_tables(struct its_node *its)
|
|||||||
|
|
||||||
static int its_alloc_tables(const char *node_name, struct its_node *its)
|
static int its_alloc_tables(const char *node_name, struct its_node *its)
|
||||||
{
|
{
|
||||||
int err;
|
u64 typer = readq_relaxed(its->base + GITS_TYPER);
|
||||||
int i;
|
u32 ids = GITS_TYPER_DEVBITS(typer);
|
||||||
int psz = SZ_64K;
|
|
||||||
u64 shr = GITS_BASER_InnerShareable;
|
u64 shr = GITS_BASER_InnerShareable;
|
||||||
u64 cache;
|
u64 cache = GITS_BASER_WaWb;
|
||||||
u64 typer;
|
u32 psz = SZ_64K;
|
||||||
u32 ids;
|
int err, i;
|
||||||
|
|
||||||
if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_22375) {
|
if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_22375) {
|
||||||
/*
|
/*
|
||||||
* erratum 22375: only alloc 8MB table size
|
* erratum 22375: only alloc 8MB table size
|
||||||
* erratum 24313: ignore memory access type
|
* erratum 24313: ignore memory access type
|
||||||
*/
|
*/
|
||||||
cache = 0;
|
cache = GITS_BASER_nCnB;
|
||||||
ids = 0x14; /* 20 bits, 8MB */
|
ids = 0x14; /* 20 bits, 8MB */
|
||||||
} else {
|
|
||||||
cache = GITS_BASER_WaWb;
|
|
||||||
typer = readq_relaxed(its->base + GITS_TYPER);
|
|
||||||
ids = GITS_TYPER_DEVBITS(typer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
its->device_ids = ids;
|
its->device_ids = ids;
|
||||||
@ -906,11 +1006,7 @@ static int its_alloc_tables(const char *node_name, 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);
|
||||||
u64 entry_size = GITS_BASER_ENTRY_SIZE(val);
|
u32 order = get_order(psz);
|
||||||
int order = get_order(psz);
|
|
||||||
int alloc_pages;
|
|
||||||
u64 tmp;
|
|
||||||
void *base;
|
|
||||||
|
|
||||||
if (type == GITS_BASER_TYPE_NONE)
|
if (type == GITS_BASER_TYPE_NONE)
|
||||||
continue;
|
continue;
|
||||||
@ -918,105 +1014,19 @@ static int its_alloc_tables(const char *node_name, struct its_node *its)
|
|||||||
if (type == GITS_BASER_TYPE_DEVICE)
|
if (type == GITS_BASER_TYPE_DEVICE)
|
||||||
its_parse_baser_device(its, baser, &order);
|
its_parse_baser_device(its, baser, &order);
|
||||||
|
|
||||||
retry_alloc_baser:
|
err = its_setup_baser(its, baser, cache, shr, psz, order);
|
||||||
alloc_pages = (PAGE_ORDER_TO_SIZE(order) / psz);
|
if (err < 0) {
|
||||||
if (alloc_pages > GITS_BASER_PAGES_MAX) {
|
its_free_tables(its);
|
||||||
alloc_pages = GITS_BASER_PAGES_MAX;
|
return err;
|
||||||
order = get_order(GITS_BASER_PAGES_MAX * psz);
|
|
||||||
pr_warn("%s: Device Table too large, reduce its page order to %u (%u pages)\n",
|
|
||||||
node_name, order, alloc_pages);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
base = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, order);
|
/* Update settings which will be used for next BASERn */
|
||||||
if (!base) {
|
psz = baser->psz;
|
||||||
err = -ENOMEM;
|
cache = baser->val & GITS_BASER_CACHEABILITY_MASK;
|
||||||
goto out_free;
|
shr = baser->val & GITS_BASER_SHAREABILITY_MASK;
|
||||||
}
|
|
||||||
|
|
||||||
its->tables[i].base = base;
|
|
||||||
its->tables[i].order = order;
|
|
||||||
|
|
||||||
retry_baser:
|
|
||||||
val = (virt_to_phys(base) |
|
|
||||||
(type << GITS_BASER_TYPE_SHIFT) |
|
|
||||||
((entry_size - 1) << GITS_BASER_ENTRY_SIZE_SHIFT) |
|
|
||||||
cache |
|
|
||||||
shr |
|
|
||||||
GITS_BASER_VALID);
|
|
||||||
|
|
||||||
switch (psz) {
|
|
||||||
case SZ_4K:
|
|
||||||
val |= GITS_BASER_PAGE_SIZE_4K;
|
|
||||||
break;
|
|
||||||
case SZ_16K:
|
|
||||||
val |= GITS_BASER_PAGE_SIZE_16K;
|
|
||||||
break;
|
|
||||||
case SZ_64K:
|
|
||||||
val |= GITS_BASER_PAGE_SIZE_64K;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
val |= alloc_pages - 1;
|
|
||||||
|
|
||||||
its_write_baser_cache(its, baser, val);
|
|
||||||
tmp = baser->val;
|
|
||||||
|
|
||||||
if ((val ^ tmp) & GITS_BASER_SHAREABILITY_MASK) {
|
|
||||||
/*
|
|
||||||
* Shareability didn't stick. Just use
|
|
||||||
* whatever the read reported, which is likely
|
|
||||||
* to be the only thing this redistributor
|
|
||||||
* supports. If that's zero, make it
|
|
||||||
* non-cacheable as well.
|
|
||||||
*/
|
|
||||||
shr = tmp & GITS_BASER_SHAREABILITY_MASK;
|
|
||||||
if (!shr) {
|
|
||||||
cache = GITS_BASER_nC;
|
|
||||||
__flush_dcache_area(base, PAGE_ORDER_TO_SIZE(order));
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
its->tables[i].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) {
|
|
||||||
pr_err("ITS: %s: GITS_BASER%d doesn't stick: %lx %lx\n",
|
|
||||||
node_name, i,
|
|
||||||
(unsigned long) val, (unsigned long) tmp);
|
|
||||||
err = -ENXIO;
|
|
||||||
goto out_free;
|
|
||||||
}
|
|
||||||
|
|
||||||
pr_info("ITS: allocated %d %s @%lx (psz %dK, shr %d)\n",
|
|
||||||
(int)(PAGE_ORDER_TO_SIZE(order) / entry_size),
|
|
||||||
its_base_type_string[type],
|
|
||||||
(unsigned long)virt_to_phys(base),
|
|
||||||
psz / SZ_1K, (int)shr >> GITS_BASER_SHAREABILITY_SHIFT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_free:
|
|
||||||
its_free_tables(its);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int its_alloc_collections(struct its_node *its)
|
static int its_alloc_collections(struct its_node *its)
|
||||||
|
@ -228,6 +228,7 @@
|
|||||||
#define GITS_BASER_PAGE_SIZE_64K (2UL << GITS_BASER_PAGE_SIZE_SHIFT)
|
#define GITS_BASER_PAGE_SIZE_64K (2UL << GITS_BASER_PAGE_SIZE_SHIFT)
|
||||||
#define GITS_BASER_PAGE_SIZE_MASK (3UL << GITS_BASER_PAGE_SIZE_SHIFT)
|
#define GITS_BASER_PAGE_SIZE_MASK (3UL << GITS_BASER_PAGE_SIZE_SHIFT)
|
||||||
#define GITS_BASER_PAGES_MAX 256
|
#define GITS_BASER_PAGES_MAX 256
|
||||||
|
#define GITS_BASER_PAGES_SHIFT (0)
|
||||||
|
|
||||||
#define GITS_BASER_TYPE_NONE 0
|
#define GITS_BASER_TYPE_NONE 0
|
||||||
#define GITS_BASER_TYPE_DEVICE 1
|
#define GITS_BASER_TYPE_DEVICE 1
|
||||||
|
Loading…
x
Reference in New Issue
Block a user