File Index
Files and directories
Sign in
FILE

olpc_dt.c

File preview with syntax highlighting.

linux-6.19.14/arch/x86/platform/olpc/olpc_dt.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * OLPC-specific OFW device tree support code.
  4 *
  5 * Paul Mackerras	August 1996.
  6 * Copyright (C) 1996-2005 Paul Mackerras.
  7 *
  8 *  Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner.
  9 *    {engebret|bergner}@us.ibm.com
 10 *
 11 *  Adapted for sparc by David S. Miller davem@davemloft.net
 12 *  Adapted for x86/OLPC by Andres Salomon <dilinger@queued.net>
 13 */
 14
 15#include <linux/kernel.h>
 16#include <linux/memblock.h>
 17#include <linux/of.h>
 18#include <linux/of_pdt.h>
 19#include <asm/olpc.h>
 20#include <asm/olpc_ofw.h>
 21
 22static phandle __init olpc_dt_getsibling(phandle node)
 23{
 24	const void *args[] = { (void *)node };
 25	void *res[] = { &node };
 26
 27	if ((s32)node == -1)
 28		return 0;
 29
 30	if (olpc_ofw("peer", args, res) || (s32)node == -1)
 31		return 0;
 32
 33	return node;
 34}
 35
 36static phandle __init olpc_dt_getchild(phandle node)
 37{
 38	const void *args[] = { (void *)node };
 39	void *res[] = { &node };
 40
 41	if ((s32)node == -1)
 42		return 0;
 43
 44	if (olpc_ofw("child", args, res) || (s32)node == -1) {
 45		pr_err("PROM: %s: fetching child failed!\n", __func__);
 46		return 0;
 47	}
 48
 49	return node;
 50}
 51
 52static int __init olpc_dt_getproplen(phandle node, const char *prop)
 53{
 54	const void *args[] = { (void *)node, prop };
 55	int len;
 56	void *res[] = { &len };
 57
 58	if ((s32)node == -1)
 59		return -1;
 60
 61	if (olpc_ofw("getproplen", args, res)) {
 62		pr_err("PROM: %s: getproplen failed!\n", __func__);
 63		return -1;
 64	}
 65
 66	return len;
 67}
 68
 69static int __init olpc_dt_getproperty(phandle node, const char *prop,
 70		char *buf, int bufsize)
 71{
 72	int plen;
 73
 74	plen = olpc_dt_getproplen(node, prop);
 75	if (plen > bufsize || plen < 1) {
 76		return -1;
 77	} else {
 78		const void *args[] = { (void *)node, prop, buf, (void *)plen };
 79		void *res[] = { &plen };
 80
 81		if (olpc_ofw("getprop", args, res)) {
 82			pr_err("PROM: %s: getprop failed!\n", __func__);
 83			return -1;
 84		}
 85	}
 86
 87	return plen;
 88}
 89
 90static int __init olpc_dt_nextprop(phandle node, char *prev, char *buf)
 91{
 92	const void *args[] = { (void *)node, prev, buf };
 93	int success;
 94	void *res[] = { &success };
 95
 96	buf[0] = '\0';
 97
 98	if ((s32)node == -1)
 99		return -1;
100
101	if (olpc_ofw("nextprop", args, res) || success != 1)
102		return -1;
103
104	return 0;
105}
106
107static int __init olpc_dt_pkg2path(phandle node, char *buf,
108		const int buflen, int *len)
109{
110	const void *args[] = { (void *)node, buf, (void *)buflen };
111	void *res[] = { len };
112
113	if ((s32)node == -1)
114		return -1;
115
116	if (olpc_ofw("package-to-path", args, res) || *len < 1)
117		return -1;
118
119	return 0;
120}
121
122static unsigned int prom_early_allocated __initdata;
123
124void * __init prom_early_alloc(unsigned long size)
125{
126	static u8 *mem;
127	static size_t free_mem;
128	void *res;
129
130	if (free_mem < size) {
131		const size_t chunk_size = max(PAGE_SIZE, size);
132
133		/*
134		 * To minimize the number of allocations, grab at least
135		 * PAGE_SIZE of memory (that's an arbitrary choice that's
136		 * fast enough on the platforms we care about while minimizing
137		 * wasted bootmem) and hand off chunks of it to callers.
138		 */
139		res = memblock_alloc_or_panic(chunk_size, SMP_CACHE_BYTES);
140		prom_early_allocated += chunk_size;
141		memset(res, 0, chunk_size);
142		free_mem = chunk_size;
143		mem = res;
144	}
145
146	/* allocate from the local cache */
147	free_mem -= size;
148	res = mem;
149	mem += size;
150	return res;
151}
152
153static struct of_pdt_ops prom_olpc_ops __initdata = {
154	.nextprop = olpc_dt_nextprop,
155	.getproplen = olpc_dt_getproplen,
156	.getproperty = olpc_dt_getproperty,
157	.getchild = olpc_dt_getchild,
158	.getsibling = olpc_dt_getsibling,
159	.pkg2path = olpc_dt_pkg2path,
160};
161
162static phandle __init olpc_dt_finddevice(const char *path)
163{
164	phandle node;
165	const void *args[] = { path };
166	void *res[] = { &node };
167
168	if (olpc_ofw("finddevice", args, res)) {
169		pr_err("olpc_dt: finddevice failed!\n");
170		return 0;
171	}
172
173	if ((s32) node == -1)
174		return 0;
175
176	return node;
177}
178
179static int __init olpc_dt_interpret(const char *words)
180{
181	int result;
182	const void *args[] = { words };
183	void *res[] = { &result };
184
185	if (olpc_ofw("interpret", args, res)) {
186		pr_err("olpc_dt: interpret failed!\n");
187		return -1;
188	}
189
190	return result;
191}
192
193/*
194 * Extract board revision directly from OFW device tree.
195 * We can't use olpc_platform_info because that hasn't been set up yet.
196 */
197static u32 __init olpc_dt_get_board_revision(void)
198{
199	phandle node;
200	__be32 rev;
201	int r;
202
203	node = olpc_dt_finddevice("/");
204	if (!node)
205		return 0;
206
207	r = olpc_dt_getproperty(node, "board-revision-int",
208				(char *) &rev, sizeof(rev));
209	if (r < 0)
210		return 0;
211
212	return be32_to_cpu(rev);
213}
214
215static int __init olpc_dt_compatible_match(phandle node, const char *compat)
216{
217	char buf[64], *p;
218	int plen;
219
220	plen = olpc_dt_getproperty(node, "compatible", buf, sizeof(buf));
221	if (plen <= 0)
222		return 0;
223
224	for (p = buf; p < buf + plen; p += strlen(p) + 1) {
225		if (strcmp(p, compat) == 0)
226			return 1;
227	}
228
229	return 0;
230}
231
232static void __init olpc_dt_fixup(void)
233{
234	phandle node;
235	u32 board_rev;
236
237	node = olpc_dt_finddevice("/battery@0");
238	if (!node)
239		return;
240
241	board_rev = olpc_dt_get_board_revision();
242	if (!board_rev)
243		return;
244
245	if (board_rev >= olpc_board_pre(0xd0)) {
246		/* XO-1.5 */
247
248		if (olpc_dt_compatible_match(node, "olpc,xo1.5-battery"))
249			return;
250
251		/* Add olpc,xo1.5-battery compatible marker to battery node */
252		olpc_dt_interpret("\" /battery@0\" find-device");
253		olpc_dt_interpret("  \" olpc,xo1.5-battery\" +compatible");
254		olpc_dt_interpret("device-end");
255
256		if (olpc_dt_compatible_match(node, "olpc,xo1-battery")) {
257			/*
258			 * If we have a olpc,xo1-battery compatible, then we're
259			 * running a new enough firmware that already has
260			 * the dcon node.
261			 */
262			return;
263		}
264
265		/* Add dcon device */
266		olpc_dt_interpret("\" /pci/display@1\" find-device");
267		olpc_dt_interpret("  new-device");
268		olpc_dt_interpret("    \" dcon\" device-name");
269		olpc_dt_interpret("    \" olpc,xo1-dcon\" +compatible");
270		olpc_dt_interpret("  finish-device");
271		olpc_dt_interpret("device-end");
272	} else {
273		/* XO-1 */
274
275		if (olpc_dt_compatible_match(node, "olpc,xo1-battery")) {
276			/*
277			 * If we have a olpc,xo1-battery compatible, then we're
278			 * running a new enough firmware that already has
279			 * the dcon and RTC nodes.
280			 */
281			return;
282		}
283
284		/* Add dcon device, mark RTC as olpc,xo1-rtc */
285		olpc_dt_interpret("\" /pci/display@1,1\" find-device");
286		olpc_dt_interpret("  new-device");
287		olpc_dt_interpret("    \" dcon\" device-name");
288		olpc_dt_interpret("    \" olpc,xo1-dcon\" +compatible");
289		olpc_dt_interpret("  finish-device");
290		olpc_dt_interpret("device-end");
291
292		olpc_dt_interpret("\" /rtc\" find-device");
293		olpc_dt_interpret(" \" olpc,xo1-rtc\" +compatible");
294		olpc_dt_interpret("device-end");
295	}
296
297	/* Add olpc,xo1-battery compatible marker to battery node */
298	olpc_dt_interpret("\" /battery@0\" find-device");
299	olpc_dt_interpret("  \" olpc,xo1-battery\" +compatible");
300	olpc_dt_interpret("device-end");
301}
302
303void __init olpc_dt_build_devicetree(void)
304{
305	phandle root;
306
307	if (!olpc_ofw_is_installed())
308		return;
309
310	olpc_dt_fixup();
311
312	root = olpc_dt_getsibling(0);
313	if (!root) {
314		pr_err("PROM: unable to get root node from OFW!\n");
315		return;
316	}
317	of_pdt_build_devicetree(root, &prom_olpc_ops);
318
319	pr_info("PROM DT: Built device tree with %u bytes of memory.\n",
320			prom_early_allocated);
321}
~/ Read-only file preview.