fdt: Support for ISA busses
Support ISA busses in much the same way as Linux does. This allows for ISA bus addresses to be translated, and only if CONFIG_OF_ISA_BUS is selected in order to avoid including the code in builds which won't need it. Signed-off-by: Paul Burton <paul.burton@imgtec.com> Reviewed-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
parent
fc15b9beed
commit
0a222d53d8
|
@ -964,10 +964,21 @@ static void of_dump_addr(const char *s, const fdt32_t *addr, int na)
|
||||||
static void of_dump_addr(const char *s, const fdt32_t *addr, int na) { }
|
static void of_dump_addr(const char *s, const fdt32_t *addr, int na) { }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Callbacks for bus specific translators */
|
/**
|
||||||
|
* struct of_bus - Callbacks for bus specific translators
|
||||||
|
* @match: Return non-zero if the node whose parent is at
|
||||||
|
* parentoffset in the FDT blob corresponds to a bus
|
||||||
|
* of this type, otherwise return zero. If NULL a match
|
||||||
|
* is assumed.
|
||||||
|
*
|
||||||
|
* Each bus type will include a struct of_bus in the of_busses array,
|
||||||
|
* providing implementations of some or all of the functions used to
|
||||||
|
* match the bus & handle address translation for its children.
|
||||||
|
*/
|
||||||
struct of_bus {
|
struct of_bus {
|
||||||
const char *name;
|
const char *name;
|
||||||
const char *addresses;
|
const char *addresses;
|
||||||
|
int (*match)(void *blob, int parentoffset);
|
||||||
void (*count_cells)(void *blob, int parentoffset,
|
void (*count_cells)(void *blob, int parentoffset,
|
||||||
int *addrc, int *sizec);
|
int *addrc, int *sizec);
|
||||||
u64 (*map)(fdt32_t *addr, const fdt32_t *range,
|
u64 (*map)(fdt32_t *addr, const fdt32_t *range,
|
||||||
|
@ -1022,8 +1033,70 @@ static int of_bus_default_translate(fdt32_t *addr, u64 offset, int na)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_OF_ISA_BUS
|
||||||
|
|
||||||
|
/* ISA bus translator */
|
||||||
|
static int of_bus_isa_match(void *blob, int parentoffset)
|
||||||
|
{
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
name = fdt_get_name(blob, parentoffset, NULL);
|
||||||
|
if (!name)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return !strcmp(name, "isa");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void of_bus_isa_count_cells(void *blob, int parentoffset,
|
||||||
|
int *addrc, int *sizec)
|
||||||
|
{
|
||||||
|
if (addrc)
|
||||||
|
*addrc = 2;
|
||||||
|
if (sizec)
|
||||||
|
*sizec = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u64 of_bus_isa_map(fdt32_t *addr, const fdt32_t *range,
|
||||||
|
int na, int ns, int pna)
|
||||||
|
{
|
||||||
|
u64 cp, s, da;
|
||||||
|
|
||||||
|
/* Check address type match */
|
||||||
|
if ((addr[0] ^ range[0]) & cpu_to_be32(1))
|
||||||
|
return OF_BAD_ADDR;
|
||||||
|
|
||||||
|
cp = of_read_number(range + 1, na - 1);
|
||||||
|
s = of_read_number(range + na + pna, ns);
|
||||||
|
da = of_read_number(addr + 1, na - 1);
|
||||||
|
|
||||||
|
debug("OF: ISA map, cp=%" PRIu64 ", s=%" PRIu64
|
||||||
|
", da=%" PRIu64 "\n", cp, s, da);
|
||||||
|
|
||||||
|
if (da < cp || da >= (cp + s))
|
||||||
|
return OF_BAD_ADDR;
|
||||||
|
return da - cp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int of_bus_isa_translate(fdt32_t *addr, u64 offset, int na)
|
||||||
|
{
|
||||||
|
return of_bus_default_translate(addr + 1, offset, na - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_OF_ISA_BUS */
|
||||||
|
|
||||||
/* Array of bus specific translators */
|
/* Array of bus specific translators */
|
||||||
static struct of_bus of_busses[] = {
|
static struct of_bus of_busses[] = {
|
||||||
|
#ifdef CONFIG_OF_ISA_BUS
|
||||||
|
/* ISA */
|
||||||
|
{
|
||||||
|
.name = "isa",
|
||||||
|
.addresses = "reg",
|
||||||
|
.match = of_bus_isa_match,
|
||||||
|
.count_cells = of_bus_isa_count_cells,
|
||||||
|
.map = of_bus_isa_map,
|
||||||
|
.translate = of_bus_isa_translate,
|
||||||
|
},
|
||||||
|
#endif /* CONFIG_OF_ISA_BUS */
|
||||||
/* Default */
|
/* Default */
|
||||||
{
|
{
|
||||||
.name = "default",
|
.name = "default",
|
||||||
|
@ -1034,6 +1107,28 @@ static struct of_bus of_busses[] = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct of_bus *of_match_bus(void *blob, int parentoffset)
|
||||||
|
{
|
||||||
|
struct of_bus *bus;
|
||||||
|
|
||||||
|
if (ARRAY_SIZE(of_busses) == 1)
|
||||||
|
return of_busses;
|
||||||
|
|
||||||
|
for (bus = of_busses; bus; bus++) {
|
||||||
|
if (!bus->match || bus->match(blob, parentoffset))
|
||||||
|
return bus;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We should always have matched the default bus at least, since
|
||||||
|
* it has a NULL match field. If we didn't then it somehow isn't
|
||||||
|
* in the of_busses array or something equally catastrophic has
|
||||||
|
* gone wrong.
|
||||||
|
*/
|
||||||
|
assert(0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static int of_translate_one(void * blob, int parent, struct of_bus *bus,
|
static int of_translate_one(void * blob, int parent, struct of_bus *bus,
|
||||||
struct of_bus *pbus, fdt32_t *addr,
|
struct of_bus *pbus, fdt32_t *addr,
|
||||||
int na, int ns, int pna, const char *rprop)
|
int na, int ns, int pna, const char *rprop)
|
||||||
|
@ -1113,7 +1208,7 @@ static u64 __of_translate_address(void *blob, int node_offset, const fdt32_t *in
|
||||||
parent = fdt_parent_offset(blob, node_offset);
|
parent = fdt_parent_offset(blob, node_offset);
|
||||||
if (parent < 0)
|
if (parent < 0)
|
||||||
goto bail;
|
goto bail;
|
||||||
bus = &of_busses[0];
|
bus = of_match_bus(blob, parent);
|
||||||
|
|
||||||
/* Cound address cells & copy address locally */
|
/* Cound address cells & copy address locally */
|
||||||
bus->count_cells(blob, parent, &na, &ns);
|
bus->count_cells(blob, parent, &na, &ns);
|
||||||
|
@ -1142,7 +1237,7 @@ static u64 __of_translate_address(void *blob, int node_offset, const fdt32_t *in
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get new parent bus and counts */
|
/* Get new parent bus and counts */
|
||||||
pbus = &of_busses[0];
|
pbus = of_match_bus(blob, parent);
|
||||||
pbus->count_cells(blob, parent, &pna, &pns);
|
pbus->count_cells(blob, parent, &pna, &pns);
|
||||||
if (!OF_CHECK_COUNTS(pna, pns)) {
|
if (!OF_CHECK_COUNTS(pna, pns)) {
|
||||||
printf("%s: Bad cell count for %s\n", __FUNCTION__,
|
printf("%s: Bad cell count for %s\n", __FUNCTION__,
|
||||||
|
|
|
@ -178,4 +178,27 @@ config SPL_OF_TRANSLATE
|
||||||
used for the address translation. This function is faster and
|
used for the address translation. This function is faster and
|
||||||
smaller in size than fdt_translate_address().
|
smaller in size than fdt_translate_address().
|
||||||
|
|
||||||
|
config OF_ISA_BUS
|
||||||
|
bool
|
||||||
|
depends on OF_TRANSLATE
|
||||||
|
help
|
||||||
|
Is this option is enabled then support for the ISA bus will
|
||||||
|
be included for addresses read from DT. This is something that
|
||||||
|
should be known to be required or not based upon the board
|
||||||
|
being targetted, and whether or not it makes use of an ISA bus.
|
||||||
|
|
||||||
|
The bus is matched based upon its node name equalling "isa". The
|
||||||
|
busses #address-cells should equal 2, with the first cell being
|
||||||
|
used to hold flags & flag 0x1 indicating that the address range
|
||||||
|
should be accessed using I/O port in/out accessors. The second
|
||||||
|
cell holds the offset into ISA bus address space. The #size-cells
|
||||||
|
property should equal 1, and of course holds the size of the
|
||||||
|
address range used by a device.
|
||||||
|
|
||||||
|
If this option is not enabled then support for the ISA bus is
|
||||||
|
not included and any such busses used in DT will be treated as
|
||||||
|
typical simple-bus compatible busses. This will lead to
|
||||||
|
mistranslation of device addresses, so ensure that this is
|
||||||
|
enabled if your board does include an ISA bus.
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|
Loading…
Reference in New Issue