Home | History | Annotate | Line # | Download | only in ofw
ofw_subr.c revision 1.38
      1 /*	$NetBSD: ofw_subr.c,v 1.38 2020/06/25 22:50:56 rin Exp $	*/
      2 
      3 /*
      4  * Copyright 1998
      5  * Digital Equipment Corporation. All rights reserved.
      6  *
      7  * This software is furnished under license and may be used and
      8  * copied only in accordance with the following terms and conditions.
      9  * Subject to these conditions, you may download, copy, install,
     10  * use, modify and distribute this software in source and/or binary
     11  * form. No title or ownership is transferred hereby.
     12  *
     13  * 1) Any source code used, modified or distributed must reproduce
     14  *    and retain this copyright notice and list of conditions as
     15  *    they appear in the source file.
     16  *
     17  * 2) No right is granted to use any trade name, trademark, or logo of
     18  *    Digital Equipment Corporation. Neither the "Digital Equipment
     19  *    Corporation" name nor any trademark or logo of Digital Equipment
     20  *    Corporation may be used to endorse or promote products derived
     21  *    from this software without the prior written permission of
     22  *    Digital Equipment Corporation.
     23  *
     24  * 3) This software is provided "AS-IS" and any express or implied
     25  *    warranties, including but not limited to, any implied warranties
     26  *    of merchantability, fitness for a particular purpose, or
     27  *    non-infringement are disclaimed. In no event shall DIGITAL be
     28  *    liable for any damages whatsoever, and in particular, DIGITAL
     29  *    shall not be liable for special, indirect, consequential, or
     30  *    incidental damages or damages for lost profits, loss of
     31  *    revenue or loss of use, whether such damages arise in contract,
     32  *    negligence, tort, under statute, in equity, at law or otherwise,
     33  *    even if advised of the possibility of such damage.
     34  */
     35 
     36 #include <sys/cdefs.h>
     37 __KERNEL_RCSID(0, "$NetBSD: ofw_subr.c,v 1.38 2020/06/25 22:50:56 rin Exp $");
     38 
     39 #include <sys/param.h>
     40 #include <sys/systm.h>
     41 #include <sys/malloc.h>
     42 #include <dev/ofw/openfirm.h>
     43 
     44 #define	OFW_MAX_STACK_BUF_SIZE	256
     45 #define	OFW_PATH_BUF_SIZE	512
     46 
     47 /*
     48  * int of_decode_int(p)
     49  *
     50  * This routine converts OFW encoded-int datums
     51  * into the integer format of the host machine.
     52  *
     53  * It is primarily used to convert integer properties
     54  * returned by the OF_getprop routine.
     55  *
     56  * Arguments:
     57  *	p		pointer to unsigned char array which is an
     58  *			OFW-encoded integer.
     59  *
     60  * Return Value:
     61  *	Decoded integer value of argument p.
     62  *
     63  * Side Effects:
     64  *	None.
     65  */
     66 int
     67 of_decode_int(const unsigned char *p)
     68 {
     69 	unsigned int i = *p++ << 8;
     70 	i = (i + *p++) << 8;
     71 	i = (i + *p++) << 8;
     72 	return (i + *p);
     73 }
     74 
     75 /*
     76  * int of_compatible(phandle, strings)
     77  *
     78  * This routine checks an OFW node's "compatible" entry to see if
     79  * it matches any of the provided strings.
     80  *
     81  * It should be used when determining whether a driver can drive
     82  * a particular device.
     83  *
     84  * Arguments:
     85  *	phandle		OFW phandle of device to be checked for
     86  *			compatibility.
     87  *	strings		Array of containing expected "compatibility"
     88  *			property values, presence of any of which
     89  *			indicates compatibility.
     90  *
     91  * Return Value:
     92  *	-1 if none of the strings are found in phandle's "compatibility"
     93  *	property, or the reverse index of the matching string in the
     94  *	phandle's "compatibility" property.
     95  *
     96  * Side Effects:
     97  *	None.
     98  */
     99 int
    100 of_compatible(int phandle, const char * const *strings)
    101 {
    102 
    103 	int len, olen, allocated, nstr, cstr, rv;
    104 	char *buf, sbuf[OFW_MAX_STACK_BUF_SIZE];
    105 	const char *sp, *nsp;
    106 
    107 	len = OF_getproplen(phandle, "compatible");
    108 	if (len <= 0)
    109 		return (-1);
    110 
    111 	if (len > sizeof(sbuf)) {
    112 		KASSERT(!cold);
    113 		buf = malloc(len, M_TEMP, M_WAITOK);
    114 		allocated = 1;
    115 	} else {
    116 		buf = sbuf;
    117 		allocated = 0;
    118 	}
    119 
    120 	/* 'compatible' size should not change. */
    121 	if (OF_getprop(phandle, "compatible", buf, len) != len) {
    122 		rv = -1;
    123 		goto out;
    124 	}
    125 
    126 	/* count 'compatible' strings */
    127 	sp = buf;
    128 	nstr = 0;
    129 	olen = len;
    130 	while (len && (nsp = memchr(sp, 0, len)) != NULL) {
    131 		nsp++;			/* skip over NUL char */
    132 		len -= (nsp - sp);
    133 		sp = nsp;
    134 		nstr++;
    135 	}
    136 	len = olen;
    137 
    138 	sp = buf;
    139 	rv = nstr;
    140 	while (len && (nsp = memchr(sp, 0, len)) != NULL) {
    141 		rv--;
    142 		/* look for a match among the strings provided */
    143 		for (cstr = 0; strings[cstr] != NULL; cstr++)
    144 			if (strcmp(sp, strings[cstr]) == 0)
    145 				goto out;
    146 
    147 		nsp++;			/* skip over NUL char */
    148 		len -= (nsp - sp);
    149 		sp = nsp;
    150 	}
    151 	rv = -1;
    152 
    153 out:
    154 	if (allocated)
    155 		free(buf, M_TEMP);
    156 	return (rv);
    157 }
    158 
    159 /*
    160  * int of_match_compatible(phandle, strings)
    161  *
    162  * This routine checks an OFW node's "compatible" entry to see if
    163  * it matches any of the provided strings.
    164  *
    165  * It should be used when determining whether a driver can drive
    166  * a particular device.
    167  *
    168  * Arguments:
    169  *	phandle		OFW phandle of device to be checked for
    170  *			compatibility.
    171  *	strings		Array of containing expected "compatibility"
    172  *			property values, presence of any of which
    173  *			indicates compatibility.
    174  *
    175  * Return Value:
    176  *	0 if none of the strings are found in phandle's "compatibility"
    177  *	property, or a positive number based on the reverse index of the
    178  *	matching string in the phandle's "compatibility" property, plus 1.
    179  *
    180  * Side Effects:
    181  *	None.
    182  */
    183 int
    184 of_match_compatible(int phandle, const char * const *strings)
    185 {
    186 	return of_compatible(phandle, strings) + 1;
    187 }
    188 
    189 /*
    190  * int of_match_compat_data(phandle, compat_data)
    191  *
    192  * This routine searches an array of compat_data structures for a
    193  * matching "compatible" entry matching the supplied OFW node.
    194  *
    195  * It should be used when determining whether a driver can drive
    196  * a particular device.
    197  *
    198  * Arguments:
    199  *	phandle		OFW phandle of device to be checked for
    200  *			compatibility.
    201  *	compat_data	Array of possible compat entry strings and
    202  *			associated metadata. The last entry in the
    203  *			list should have a "compat" of NULL to terminate
    204  *			the list.
    205  *
    206  * Return Value:
    207  *	0 if none of the strings are found in phandle's "compatibility"
    208  *	property, or a positive number based on the reverse index of the
    209  *	matching string in the phandle's "compatibility" property, plus 1.
    210  *
    211  * Side Effects:
    212  *	None.
    213  */
    214 int
    215 of_match_compat_data(int phandle, const struct of_compat_data *compat_data)
    216 {
    217 	for (; compat_data->compat != NULL; compat_data++) {
    218 		const char *compat[] = { compat_data->compat, NULL };
    219 		const int match = of_match_compatible(phandle, compat);
    220 		if (match)
    221 			return match;
    222 	}
    223 	return 0;
    224 }
    225 
    226 /*
    227  * const struct of_compat_data *of_search_compatible(phandle, compat_data)
    228  *
    229  * This routine searches an array of compat_data structures for a
    230  * matching "compatible" entry matching the supplied OFW node.
    231  *
    232  * Arguments:
    233  *	phandle		OFW phandle of device to be checked for
    234  *			compatibility.
    235  *	compat_data	Array of possible compat entry strings and
    236  *			associated metadata. The last entry in the
    237  *			list should have a "compat" of NULL to terminate
    238  *			the list.
    239  *
    240  * Return Value:
    241  *	The first matching compat_data entry in the array. If no matches
    242  *	are found, the terminating ("compat" of NULL) record is returned.
    243  *
    244  * Side Effects:
    245  *	None.
    246  */
    247 const struct of_compat_data *
    248 of_search_compatible(int phandle, const struct of_compat_data *compat_data)
    249 {
    250 	for (; compat_data->compat != NULL; compat_data++) {
    251 		const char *compat[] = { compat_data->compat, NULL };
    252 		if (of_match_compatible(phandle, compat))
    253 			break;
    254 	}
    255 	return compat_data;
    256 }
    257 
    258 /*
    259  * int of_packagename(phandle, buf, bufsize)
    260  *
    261  * This routine places the last component of an OFW node's name
    262  * into a user-provided buffer.
    263  *
    264  * It can be used during autoconfiguration to make printing of
    265  * device names more informative.
    266  *
    267  * Arguments:
    268  *	phandle		OFW phandle of device whose name name is
    269  *			desired.
    270  *	buf		Buffer to contain device name, provided by
    271  *			caller.  (For now, must be at least 4
    272  *			bytes long.)
    273  *	bufsize		Length of buffer referenced by 'buf', in
    274  *			bytes.
    275  *
    276  * Return Value:
    277  *	-1 if the device path name could not be obtained or would
    278  *	not fit in the allocated temporary buffer, or zero otherwise
    279  *	(meaning that the leaf node name was successfully extracted).
    280  *
    281  * Side Effects:
    282  *	If the leaf node name was successfully extracted, 'buf' is
    283  *	filled in with at most 'bufsize' bytes of the leaf node
    284  *	name.  If the leaf node was not successfully extracted, a
    285  *	somewhat meaningful string is placed in the buffer.  In
    286  *	either case, the contents of 'buf' will be NUL-terminated.
    287  */
    288 int
    289 of_packagename(int phandle, char *buf, int bufsize)
    290 {
    291 	char *pbuf;
    292 	const char *lastslash;
    293 	int l, rv;
    294 
    295 	pbuf = malloc(OFW_PATH_BUF_SIZE, M_TEMP, M_WAITOK);
    296 	l = OF_package_to_path(phandle, pbuf, OFW_PATH_BUF_SIZE);
    297 
    298 	/* check that we could get the name, and that it's not too long. */
    299 	if (l < 0 ||
    300 	    (l == OFW_PATH_BUF_SIZE && pbuf[OFW_PATH_BUF_SIZE - 1] != '\0')) {
    301 		if (bufsize >= 25)
    302 			snprintf(buf, bufsize, "??? (phandle 0x%x)", phandle);
    303 		else if (bufsize >= 4)
    304 			strlcpy(buf, "???", bufsize);
    305 		else
    306 			panic("of_packagename: bufsize = %d is silly",
    307 			    bufsize);
    308 		rv = -1;
    309 	} else {
    310 		pbuf[l] = '\0';
    311 		lastslash = strrchr(pbuf, '/');
    312 		strlcpy(buf, (lastslash == NULL) ? pbuf : (lastslash + 1),
    313 		    bufsize);
    314 		rv = 0;
    315 	}
    316 
    317 	free(pbuf, M_TEMP);
    318 	return (rv);
    319 }
    320 
    321 /*
    322  * Find the first child of a given node that matches name. Does not recurse.
    323  */
    324 int
    325 of_find_firstchild_byname(int node, const char *name)
    326 {
    327 	char namex[32];
    328 	int nn;
    329 
    330 	for (nn = OF_child(node); nn; nn = OF_peer(nn)) {
    331 		memset(namex, 0, sizeof(namex));
    332 		if (OF_getprop(nn, "name", namex, sizeof(namex)) == -1)
    333 			continue;
    334 		if (strcmp(name, namex) == 0)
    335 			return nn;
    336 	}
    337 	return -1;
    338 }
    339 
    340 /*
    341  * Find a give node by name.  Recurses, and seems to walk upwards too.
    342  */
    343 
    344 int
    345 of_getnode_byname(int start, const char *target)
    346 {
    347 	int node, next;
    348 	char name[64];
    349 
    350 	if (start == 0)
    351 		start = OF_peer(0);
    352 
    353 	for (node = start; node; node = next) {
    354 		memset(name, 0, sizeof name);
    355 		OF_getprop(node, "name", name, sizeof name - 1);
    356 		if (strcmp(name, target) == 0)
    357 			break;
    358 
    359 		if ((next = OF_child(node)) != 0)
    360 			continue;
    361 
    362 		while (node) {
    363 			if ((next = OF_peer(node)) != 0)
    364 				break;
    365 			node = OF_parent(node);
    366 		}
    367 	}
    368 
    369 	/* XXX is this correct? */
    370 	return node;
    371 }
    372 
    373 /*
    374  * Create a uint32_t integer property from an OFW node property.
    375  */
    376 
    377 boolean_t
    378 of_to_uint32_prop(prop_dictionary_t dict, int node, const char *ofname,
    379     const char *propname)
    380 {
    381 	uint32_t prop;
    382 
    383 	if (OF_getprop(node, ofname, &prop, sizeof(prop)) != sizeof(prop))
    384 		return FALSE;
    385 
    386 	return(prop_dictionary_set_uint32(dict, propname, prop));
    387 }
    388 
    389 /*
    390  * Create a data property from an OFW node property.  Max size of 256bytes.
    391  */
    392 
    393 boolean_t
    394 of_to_dataprop(prop_dictionary_t dict, int node, const char *ofname,
    395     const char *propname)
    396 {
    397 	int len;
    398 	uint8_t prop[256];
    399 
    400 	len = OF_getprop(node, ofname, prop, 256);
    401 	if (len < 1)
    402 		return FALSE;
    403 
    404 	return prop_dictionary_set_data(dict, propname, prop, len);
    405 }
    406 
    407 /*
    408  * look at output-device, see if there's a Sun-typical video mode specifier as
    409  * in screen:r1024x768x60 attached. If found copy it into *buffer, otherwise
    410  * return NULL
    411  */
    412 
    413 char *
    414 of_get_mode_string(char *buffer, int len)
    415 {
    416 	int options;
    417 	char *pos, output_device[256];
    418 
    419 	/*
    420 	 * finally, let's see if there's a video mode specified in
    421 	 * output-device and pass it on so there's at least some way
    422 	 * to program video modes
    423 	 */
    424 	options = OF_finddevice("/options");
    425 	if ((options == 0) || (options == -1))
    426 		return NULL;
    427 	if (OF_getprop(options, "output-device", output_device, 256) == 0)
    428 		return NULL;
    429 
    430 	/* find the mode string if there is one */
    431 	pos = strstr(output_device, ":r");
    432 	if (pos == NULL)
    433 		return NULL;
    434 	strncpy(buffer, pos + 2, len);
    435 	return buffer;
    436 }
    437 
    438 /*
    439  * Iterate over the subtree of a i2c controller node.
    440  * Add all sub-devices into an array as part of the controller's
    441  * device properties.
    442  * This is used by the i2c bus attach code to do direct configuration.
    443  */
    444 void
    445 of_enter_i2c_devs(prop_dictionary_t props, int ofnode, size_t cell_size,
    446     int addr_shift)
    447 {
    448 	int node, len;
    449 	char name[32];
    450 	uint64_t reg64;
    451 	uint32_t reg32;
    452 	uint64_t addr;
    453 	prop_array_t array = NULL;
    454 	prop_dictionary_t dev;
    455 
    456 	for (node = OF_child(ofnode); node; node = OF_peer(node)) {
    457 		if (OF_getprop(node, "name", name, sizeof(name)) <= 0)
    458 			continue;
    459 		len = OF_getproplen(node, "reg");
    460 		addr = 0;
    461 		if (cell_size == 8 && len >= sizeof(reg64)) {
    462 			if (OF_getprop(node, "reg", &reg64, sizeof(reg64))
    463 			    < sizeof(reg64))
    464 				continue;
    465 			addr = be64toh(reg64);
    466 			/*
    467 			 * The i2c bus number (0 or 1) is encoded in bit 33
    468 			 * of the register, but we encode it in bit 8 of
    469 			 * i2c_addr_t.
    470 			 */
    471 			if (addr & 0x100000000)
    472 				addr = (addr & 0xff) | 0x100;
    473 		} else if (cell_size == 4 && len >= sizeof(reg32)) {
    474 			if (OF_getprop(node, "reg", &reg32, sizeof(reg32))
    475 			    < sizeof(reg32))
    476 				continue;
    477 			addr = be32toh(reg32);
    478 		} else {
    479 			continue;
    480 		}
    481 		addr >>= addr_shift;
    482 		if (addr == 0) continue;
    483 
    484 		if (array == NULL)
    485 			array = prop_array_create();
    486 
    487 		dev = prop_dictionary_create();
    488 		prop_dictionary_set_string(dev, "name", name);
    489 		prop_dictionary_set_uint32(dev, "addr", addr);
    490 		prop_dictionary_set_uint64(dev, "cookie", node);
    491 		of_to_dataprop(dev, node, "compatible", "compatible");
    492 		prop_array_add(array, dev);
    493 		prop_object_release(dev);
    494 	}
    495 
    496 	if (array != NULL) {
    497 		prop_dictionary_set(props, "i2c-child-devices", array);
    498 		prop_object_release(array);
    499 	}
    500 }
    501 
    502 void
    503 of_enter_spi_devs(prop_dictionary_t props, int ofnode, size_t cell_size)
    504 {
    505 	int node, len;
    506 	char name[32];
    507 	uint64_t reg64;
    508 	uint32_t reg32;
    509 	uint32_t slave;
    510 	u_int32_t maxfreq;
    511 	prop_array_t array = NULL;
    512 	prop_dictionary_t dev;
    513 	int mode;
    514 
    515 	for (node = OF_child(ofnode); node; node = OF_peer(node)) {
    516 		if (OF_getprop(node, "name", name, sizeof(name)) <= 0)
    517 			continue;
    518 		len = OF_getproplen(node, "reg");
    519 		slave = 0;
    520 		if (cell_size == 8 && len >= sizeof(reg64)) {
    521 			if (OF_getprop(node, "reg", &reg64, sizeof(reg64))
    522 			    < sizeof(reg64))
    523 				continue;
    524 			slave = be64toh(reg64);
    525 		} else if (cell_size == 4 && len >= sizeof(reg32)) {
    526 			if (OF_getprop(node, "reg", &reg32, sizeof(reg32))
    527 			    < sizeof(reg32))
    528 				continue;
    529 			slave = be32toh(reg32);
    530 		} else {
    531 			continue;
    532 		}
    533 		if (of_getprop_uint32(node, "spi-max-frequency", &maxfreq)) {
    534 			maxfreq = 0;
    535 		}
    536 		mode = ((int)of_hasprop(node, "cpol") << 1) | (int)of_hasprop(node, "cpha");
    537 
    538 		if (array == NULL)
    539 			array = prop_array_create();
    540 
    541 		dev = prop_dictionary_create();
    542 		prop_dictionary_set_string(dev, "name", name);
    543 		prop_dictionary_set_uint32(dev, "slave", slave);
    544 		prop_dictionary_set_uint32(dev, "mode", mode);
    545 		if (maxfreq > 0)
    546 			prop_dictionary_set_uint32(dev, "spi-max-frequency", maxfreq);
    547 		prop_dictionary_set_uint64(dev, "cookie", node);
    548 		of_to_dataprop(dev, node, "compatible", "compatible");
    549 		prop_array_add(array, dev);
    550 		prop_object_release(dev);
    551 	}
    552 
    553 	if (array != NULL) {
    554 		prop_dictionary_set(props, "spi-child-devices", array);
    555 		prop_object_release(array);
    556 	}
    557 }
    558 
    559 
    560 /*
    561  * Returns true if the specified property is present.
    562  */
    563 bool
    564 of_hasprop(int node, const char *prop)
    565 {
    566 	return OF_getproplen(node, prop) >= 0;
    567 }
    568 
    569 /*
    570  * Get the value of a uint32 property, compensating for host byte order.
    571  * Returns 0 on success, non-zero on failure.
    572  */
    573 int
    574 of_getprop_uint32(int node, const char *prop, uint32_t *val)
    575 {
    576 	uint32_t v;
    577 	int len;
    578 
    579 	len = OF_getprop(node, prop, &v, sizeof(v));
    580 	if (len != sizeof(v))
    581 		return -1;
    582 
    583 	*val = be32toh(v);
    584 	return 0;
    585 }
    586 
    587 /*
    588  * Get the value of a uint64 property, compensating for host byte order.
    589  * Returns 0 on success, non-zero on failure.
    590  */
    591 int
    592 of_getprop_uint64(int node, const char *prop, uint64_t *val)
    593 {
    594 	uint64_t v;
    595 	int len;
    596 
    597 	len = OF_getprop(node, prop, &v, sizeof(v));
    598 	if (len != sizeof(v))
    599 		return -1;
    600 
    601 	*val = be64toh(v);
    602 	return 0;
    603 }
    604