1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Device addresses
4 *
5 * Copyright (c) 2017 Google, Inc
6 *
7 * (C) Copyright 2012
8 * Pavel Herrmann <morpheus.ibis@gmail.com>
9 */
10
11 #include <common.h>
12 #include <dm.h>
13 #include <fdt_support.h>
14 #include <log.h>
15 #include <mapmem.h>
16 #include <asm/global_data.h>
17 #include <asm/io.h>
18 #include <dm/device-internal.h>
19
20 DECLARE_GLOBAL_DATA_PTR;
21
devfdt_get_addr_index(const struct udevice * dev,int index)22 fdt_addr_t devfdt_get_addr_index(const struct udevice *dev, int index)
23 {
24 #if CONFIG_IS_ENABLED(OF_REAL)
25 int offset = dev_of_offset(dev);
26 int parent = dev_of_offset(dev->parent);
27 fdt_addr_t addr;
28
29 if (CONFIG_IS_ENABLED(OF_TRANSLATE)) {
30 const fdt32_t *reg;
31 int len = 0;
32 int na, ns;
33
34 na = fdt_address_cells(gd->fdt_blob, parent);
35 if (na < 1) {
36 debug("bad #address-cells\n");
37 return FDT_ADDR_T_NONE;
38 }
39
40 ns = fdt_size_cells(gd->fdt_blob, parent);
41 if (ns < 0) {
42 debug("bad #size-cells\n");
43 return FDT_ADDR_T_NONE;
44 }
45
46 reg = fdt_getprop(gd->fdt_blob, offset, "reg", &len);
47 if (!reg || (len <= (index * sizeof(fdt32_t) * (na + ns)))) {
48 debug("Req index out of range\n");
49 return FDT_ADDR_T_NONE;
50 }
51
52 reg += index * (na + ns);
53
54 if (ns) {
55 /*
56 * Use the full-fledged translate function for complex
57 * bus setups.
58 */
59 addr = fdt_translate_address((void *)gd->fdt_blob,
60 offset, reg);
61 } else {
62 /* Non translatable if #size-cells == 0 */
63 addr = fdt_read_number(reg, na);
64 }
65 } else {
66 /*
67 * Use the "simple" translate function for less complex
68 * bus setups.
69 */
70 addr = fdtdec_get_addr_size_auto_parent(gd->fdt_blob, parent,
71 offset, "reg", index,
72 NULL, false);
73 if (CONFIG_IS_ENABLED(SIMPLE_BUS) && addr != FDT_ADDR_T_NONE) {
74 if (device_get_uclass_id(dev->parent) ==
75 UCLASS_SIMPLE_BUS)
76 addr = simple_bus_translate(dev->parent, addr);
77 }
78 }
79
80 #if defined(CONFIG_TRANSLATION_OFFSET)
81 /*
82 * Some platforms need a special address translation. Those
83 * platforms (e.g. mvebu in SPL) can configure a translation
84 * offset by setting this value in the GD and enaling this
85 * feature via CONFIG_TRANSLATION_OFFSET. This value will
86 * get added to all addresses returned by devfdt_get_addr().
87 */
88 addr += gd->translation_offset;
89 #endif
90
91 return addr;
92 #else
93 return FDT_ADDR_T_NONE;
94 #endif
95 }
96
devfdt_get_addr_index_ptr(const struct udevice * dev,int index)97 void *devfdt_get_addr_index_ptr(const struct udevice *dev, int index)
98 {
99 fdt_addr_t addr = devfdt_get_addr_index(dev, index);
100
101 if (addr == FDT_ADDR_T_NONE)
102 return NULL;
103
104 return map_sysmem(addr, 0);
105 }
106
devfdt_get_addr_size_index(const struct udevice * dev,int index,fdt_size_t * size)107 fdt_addr_t devfdt_get_addr_size_index(const struct udevice *dev, int index,
108 fdt_size_t *size)
109 {
110 #if CONFIG_IS_ENABLED(OF_CONTROL)
111 /*
112 * Only get the size in this first call. We'll get the addr in the
113 * next call to the exisiting dev_get_xxx function which handles
114 * all config options.
115 */
116 fdtdec_get_addr_size_auto_noparent(gd->fdt_blob, dev_of_offset(dev),
117 "reg", index, size, false);
118
119 /*
120 * Get the base address via the existing function which handles
121 * all Kconfig cases
122 */
123 return devfdt_get_addr_index(dev, index);
124 #else
125 return FDT_ADDR_T_NONE;
126 #endif
127 }
128
devfdt_get_addr_size_index_ptr(const struct udevice * dev,int index,fdt_size_t * size)129 void *devfdt_get_addr_size_index_ptr(const struct udevice *dev, int index,
130 fdt_size_t *size)
131 {
132 fdt_addr_t addr = devfdt_get_addr_size_index(dev, index, size);
133
134 if (addr == FDT_ADDR_T_NONE)
135 return NULL;
136
137 return map_sysmem(addr, 0);
138 }
139
devfdt_get_addr_name(const struct udevice * dev,const char * name)140 fdt_addr_t devfdt_get_addr_name(const struct udevice *dev, const char *name)
141 {
142 #if CONFIG_IS_ENABLED(OF_CONTROL)
143 int index;
144
145 index = fdt_stringlist_search(gd->fdt_blob, dev_of_offset(dev),
146 "reg-names", name);
147 if (index < 0)
148 return index;
149
150 return devfdt_get_addr_index(dev, index);
151 #else
152 return FDT_ADDR_T_NONE;
153 #endif
154 }
155
devfdt_get_addr_size_name(const struct udevice * dev,const char * name,fdt_size_t * size)156 fdt_addr_t devfdt_get_addr_size_name(const struct udevice *dev,
157 const char *name, fdt_size_t *size)
158 {
159 #if CONFIG_IS_ENABLED(OF_CONTROL)
160 int index;
161
162 index = fdt_stringlist_search(gd->fdt_blob, dev_of_offset(dev),
163 "reg-names", name);
164 if (index < 0)
165 return index;
166
167 return devfdt_get_addr_size_index(dev, index, size);
168 #else
169 return FDT_ADDR_T_NONE;
170 #endif
171 }
172
devfdt_get_addr(const struct udevice * dev)173 fdt_addr_t devfdt_get_addr(const struct udevice *dev)
174 {
175 return devfdt_get_addr_index(dev, 0);
176 }
177
devfdt_get_addr_ptr(const struct udevice * dev)178 void *devfdt_get_addr_ptr(const struct udevice *dev)
179 {
180 return devfdt_get_addr_index_ptr(dev, 0);
181 }
182
devfdt_remap_addr_index(const struct udevice * dev,int index)183 void *devfdt_remap_addr_index(const struct udevice *dev, int index)
184 {
185 fdt_addr_t addr = devfdt_get_addr_index(dev, index);
186
187 if (addr == FDT_ADDR_T_NONE)
188 return NULL;
189
190 return map_physmem(addr, 0, MAP_NOCACHE);
191 }
192
devfdt_remap_addr_name(const struct udevice * dev,const char * name)193 void *devfdt_remap_addr_name(const struct udevice *dev, const char *name)
194 {
195 fdt_addr_t addr = devfdt_get_addr_name(dev, name);
196
197 if (addr == FDT_ADDR_T_NONE)
198 return NULL;
199
200 return map_physmem(addr, 0, MAP_NOCACHE);
201 }
202
devfdt_remap_addr(const struct udevice * dev)203 void *devfdt_remap_addr(const struct udevice *dev)
204 {
205 return devfdt_remap_addr_index(dev, 0);
206 }
207
devfdt_map_physmem(const struct udevice * dev,unsigned long size)208 void *devfdt_map_physmem(const struct udevice *dev, unsigned long size)
209 {
210 fdt_addr_t addr = devfdt_get_addr(dev);
211
212 if (addr == FDT_ADDR_T_NONE)
213 return NULL;
214
215 return map_physmem(addr, size, MAP_NOCACHE);
216 }
217
devfdt_get_addr_pci(const struct udevice * dev)218 fdt_addr_t devfdt_get_addr_pci(const struct udevice *dev)
219 {
220 ulong addr;
221
222 addr = devfdt_get_addr(dev);
223 if (CONFIG_IS_ENABLED(PCI) && addr == FDT_ADDR_T_NONE) {
224 struct fdt_pci_addr pci_addr;
225 u32 bar;
226 int ret;
227
228 ret = ofnode_read_pci_addr(dev_ofnode(dev), FDT_PCI_SPACE_MEM32,
229 "reg", &pci_addr);
230 if (ret) {
231 /* try if there is any i/o-mapped register */
232 ret = ofnode_read_pci_addr(dev_ofnode(dev),
233 FDT_PCI_SPACE_IO, "reg",
234 &pci_addr);
235 if (ret)
236 return FDT_ADDR_T_NONE;
237 }
238 ret = fdtdec_get_pci_bar32(dev, &pci_addr, &bar);
239 if (ret)
240 return FDT_ADDR_T_NONE;
241 addr = bar;
242 }
243
244 return addr;
245 }
246