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