]>
Commit | Line | Data |
---|---|---|
d6ffb00a SG |
1 | /* |
2 | * Device addresses | |
3 | * | |
4 | * Copyright (c) 2017 Google, Inc | |
5 | * | |
6 | * (C) Copyright 2012 | |
7 | * Pavel Herrmann <[email protected]> | |
8 | * | |
9 | * SPDX-License-Identifier: GPL-2.0+ | |
10 | */ | |
11 | ||
12 | #include <common.h> | |
13 | #include <dm.h> | |
14 | #include <fdt_support.h> | |
15 | #include <asm/io.h> | |
16 | #include <dm/device-internal.h> | |
17 | ||
18 | DECLARE_GLOBAL_DATA_PTR; | |
19 | ||
a821c4af | 20 | fdt_addr_t devfdt_get_addr_index(struct udevice *dev, int index) |
d6ffb00a SG |
21 | { |
22 | #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) | |
23 | fdt_addr_t addr; | |
24 | ||
25 | if (CONFIG_IS_ENABLED(OF_TRANSLATE)) { | |
26 | const fdt32_t *reg; | |
27 | int len = 0; | |
28 | int na, ns; | |
29 | ||
30 | na = fdt_address_cells(gd->fdt_blob, | |
31 | dev_of_offset(dev->parent)); | |
32 | if (na < 1) { | |
33 | debug("bad #address-cells\n"); | |
34 | return FDT_ADDR_T_NONE; | |
35 | } | |
36 | ||
37 | ns = fdt_size_cells(gd->fdt_blob, dev_of_offset(dev->parent)); | |
38 | if (ns < 0) { | |
39 | debug("bad #size-cells\n"); | |
40 | return FDT_ADDR_T_NONE; | |
41 | } | |
42 | ||
43 | reg = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), "reg", | |
44 | &len); | |
45 | if (!reg || (len <= (index * sizeof(fdt32_t) * (na + ns)))) { | |
46 | debug("Req index out of range\n"); | |
47 | return FDT_ADDR_T_NONE; | |
48 | } | |
49 | ||
50 | reg += index * (na + ns); | |
51 | ||
52 | /* | |
53 | * Use the full-fledged translate function for complex | |
54 | * bus setups. | |
55 | */ | |
56 | addr = fdt_translate_address((void *)gd->fdt_blob, | |
57 | dev_of_offset(dev), reg); | |
58 | } else { | |
59 | /* | |
60 | * Use the "simple" translate function for less complex | |
61 | * bus setups. | |
62 | */ | |
63 | addr = fdtdec_get_addr_size_auto_parent(gd->fdt_blob, | |
64 | dev_of_offset(dev->parent), dev_of_offset(dev), | |
65 | "reg", index, NULL, false); | |
66 | if (CONFIG_IS_ENABLED(SIMPLE_BUS) && addr != FDT_ADDR_T_NONE) { | |
67 | if (device_get_uclass_id(dev->parent) == | |
68 | UCLASS_SIMPLE_BUS) | |
69 | addr = simple_bus_translate(dev->parent, addr); | |
70 | } | |
71 | } | |
72 | ||
73 | /* | |
74 | * Some platforms need a special address translation. Those | |
75 | * platforms (e.g. mvebu in SPL) can configure a translation | |
76 | * offset in the DM by calling dm_set_translation_offset() that | |
a821c4af | 77 | * will get added to all addresses returned by devfdt_get_addr(). |
d6ffb00a SG |
78 | */ |
79 | addr += dm_get_translation_offset(); | |
80 | ||
81 | return addr; | |
82 | #else | |
83 | return FDT_ADDR_T_NONE; | |
84 | #endif | |
85 | } | |
86 | ||
a821c4af | 87 | fdt_addr_t devfdt_get_addr_size_index(struct udevice *dev, int index, |
d6ffb00a SG |
88 | fdt_size_t *size) |
89 | { | |
90 | #if CONFIG_IS_ENABLED(OF_CONTROL) | |
91 | /* | |
92 | * Only get the size in this first call. We'll get the addr in the | |
93 | * next call to the exisiting dev_get_xxx function which handles | |
94 | * all config options. | |
95 | */ | |
96 | fdtdec_get_addr_size_auto_noparent(gd->fdt_blob, dev_of_offset(dev), | |
97 | "reg", index, size, false); | |
98 | ||
99 | /* | |
100 | * Get the base address via the existing function which handles | |
101 | * all Kconfig cases | |
102 | */ | |
a821c4af | 103 | return devfdt_get_addr_index(dev, index); |
d6ffb00a SG |
104 | #else |
105 | return FDT_ADDR_T_NONE; | |
106 | #endif | |
107 | } | |
108 | ||
a821c4af | 109 | fdt_addr_t devfdt_get_addr_name(struct udevice *dev, const char *name) |
d6ffb00a SG |
110 | { |
111 | #if CONFIG_IS_ENABLED(OF_CONTROL) | |
112 | int index; | |
113 | ||
114 | index = fdt_stringlist_search(gd->fdt_blob, dev_of_offset(dev), | |
115 | "reg-names", name); | |
116 | if (index < 0) | |
117 | return index; | |
118 | ||
a821c4af | 119 | return devfdt_get_addr_index(dev, index); |
d6ffb00a SG |
120 | #else |
121 | return FDT_ADDR_T_NONE; | |
122 | #endif | |
123 | } | |
124 | ||
a821c4af | 125 | fdt_addr_t devfdt_get_addr(struct udevice *dev) |
d6ffb00a | 126 | { |
a821c4af | 127 | return devfdt_get_addr_index(dev, 0); |
d6ffb00a SG |
128 | } |
129 | ||
a821c4af | 130 | void *devfdt_get_addr_ptr(struct udevice *dev) |
d6ffb00a | 131 | { |
a821c4af | 132 | return (void *)(uintptr_t)devfdt_get_addr_index(dev, 0); |
d6ffb00a SG |
133 | } |
134 | ||
a821c4af | 135 | void *devfdt_map_physmem(struct udevice *dev, unsigned long size) |
d6ffb00a | 136 | { |
a821c4af | 137 | fdt_addr_t addr = devfdt_get_addr(dev); |
d6ffb00a SG |
138 | |
139 | if (addr == FDT_ADDR_T_NONE) | |
140 | return NULL; | |
141 | ||
142 | return map_physmem(addr, size, MAP_NOCACHE); | |
143 | } |