Index: dev/fdt/fdt_i2c.c =================================================================== RCS file: /cvsroot/src/sys/dev/fdt/fdt_i2c.c,v retrieving revision 1.11 retrieving revision 1.11.2.1 diff -u -p -r1.11 -r1.11.2.1 --- dev/fdt/fdt_i2c.c 7 Aug 2021 16:19:10 -0000 1.11 +++ dev/fdt/fdt_i2c.c 9 Aug 2021 00:30:09 -0000 1.11.2.1 @@ -1,4 +1,4 @@ -/* $NetBSD: fdt_i2c.c,v 1.11 2021/08/07 16:19:10 thorpej Exp $ */ +/* $NetBSD: fdt_i2c.c,v 1.11.2.1 2021/08/09 00:30:09 thorpej Exp $ */ /*- * Copyright (c) 2015 Jared D. McNeill @@ -27,7 +27,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: fdt_i2c.c,v 1.11 2021/08/07 16:19:10 thorpej Exp $"); +__KERNEL_RCSID(0, "$NetBSD: fdt_i2c.c,v 1.11.2.1 2021/08/09 00:30:09 thorpej Exp $"); #include #include @@ -97,35 +97,3 @@ fdtbus_i2c_acquire(int phandle, const ch return fdtbus_get_i2c_tag(i2c_phandle); } - -device_t -fdtbus_attach_i2cbus(device_t dev, int phandle, i2c_tag_t tag, cfprint_t print) -{ - struct i2cbus_attach_args iba; - prop_dictionary_t devs, props; - device_t ret; - u_int address_cells; - - devs = prop_dictionary_create(); - if (of_getprop_uint32(phandle, "#address-cells", &address_cells)) - address_cells = 1; - - of_enter_i2c_devs(devs, phandle, address_cells * 4, 0); - - memset(&iba, 0, sizeof(iba)); - iba.iba_tag = tag; - iba.iba_child_devices = prop_dictionary_get(devs, "i2c-child-devices"); - if (iba.iba_child_devices) - prop_object_retain(iba.iba_child_devices); - prop_object_release(devs); - - props = device_properties(dev); - prop_dictionary_set_bool(props, "i2c-no-indirect-config", true); - - ret = config_found(dev, &iba, print, - CFARGS(.iattr = "i2cbus")); - if (iba.iba_child_devices) - prop_object_release(iba.iba_child_devices); - - return ret; -} Index: dev/ofw/ofw_i2c_subr.c =================================================================== RCS file: /cvsroot/src/sys/dev/ofw/ofw_i2c_subr.c,v retrieving revision 1.1 retrieving revision 1.1.16.4 diff -u -p -r1.1 -r1.1.16.4 --- dev/ofw/ofw_i2c_subr.c 4 Feb 2021 20:19:09 -0000 1.1 +++ dev/ofw/ofw_i2c_subr.c 11 Sep 2021 12:58:48 -0000 1.1.16.4 @@ -1,109 +1,204 @@ -/* $NetBSD: ofw_i2c_subr.c,v 1.1 2021/02/04 20:19:09 thorpej Exp $ */ +/* $NetBSD: ofw_i2c_subr.c,v 1.1.16.4 2021/09/11 12:58:48 thorpej Exp $ */ /* - * Copyright 1998 - * Digital Equipment Corporation. All rights reserved. + * Copyright (c) 2021 The NetBSD Foundation, Inc. + * All rights reserved. * - * This software is furnished under license and may be used and - * copied only in accordance with the following terms and conditions. - * Subject to these conditions, you may download, copy, install, - * use, modify and distribute this software in source and/or binary - * form. No title or ownership is transferred hereby. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 1) Any source code used, modified or distributed must reproduce - * and retain this copyright notice and list of conditions as - * they appear in the source file. - * - * 2) No right is granted to use any trade name, trademark, or logo of - * Digital Equipment Corporation. Neither the "Digital Equipment - * Corporation" name nor any trademark or logo of Digital Equipment - * Corporation may be used to endorse or promote products derived - * from this software without the prior written permission of - * Digital Equipment Corporation. - * - * 3) This software is provided "AS-IS" and any express or implied - * warranties, including but not limited to, any implied warranties - * of merchantability, fitness for a particular purpose, or - * non-infringement are disclaimed. In no event shall DIGITAL be - * liable for any damages whatsoever, and in particular, DIGITAL - * shall not be liable for special, indirect, consequential, or - * incidental damages or damages for lost profits, loss of - * revenue or loss of use, whether such damages arise in contract, - * negligence, tort, under statute, in equity, at law or otherwise, - * even if advised of the possibility of such damage. + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ #include -__KERNEL_RCSID(0, "$NetBSD: ofw_i2c_subr.c,v 1.1 2021/02/04 20:19:09 thorpej Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ofw_i2c_subr.c,v 1.1.16.4 2021/09/11 12:58:48 thorpej Exp $"); #include #include +#include #include #include #include #include +#ifdef __HAVE_OPENFIRMWARE_VARIANT_AAPL +/* + * Apple OpenFirmware implementations have the i2c device + * address shifted left 1 bit to account for the r/w bit + * on the wire. We also want to look at only the least- + * significant 8 bits of the address cell. + */ +#define OFW_I2C_ADDRESS_MASK __BITS(0,7) +#define OFW_I2C_ADDRESS_SHIFT 1 + /* - * Iterate over the subtree of a i2c controller node. - * Add all sub-devices into an array as part of the controller's - * device properties. - * This is used by the i2c bus attach code to do direct configuration. + * Some of Apple's older OpenFirmware implementations are rife with + * nodes lacking "compatible" properties. */ -void -of_enter_i2c_devs(prop_dictionary_t props, int ofnode, size_t cell_size, - int addr_shift) +#define OFW_I2C_ALLOW_MISSING_COMPATIBLE_PROPERTY +#endif /* __HAVE_OPENFIRMWARE_VARIANT_AAPL */ + +#ifdef __HAVE_OPENFIRMWARE_VARIANT_SUNW +/* + * Sun OpenFirmware implementations use 2 cells for the + * i2c device "reg" property, the first containing the + * channel number, the second containing the i2c device + * address shifted left 1 bit to account for the r/w bit + * on the wire. + */ +#define OFW_I2C_REG_NCELLS 2 +#define OFW_I2C_REG_CHANNEL 0 +#define OFW_I2C_REG_ADDRESS 1 +#define OFW_I2C_ADDRESS_SHIFT 1 +#endif /* __HAVE_OPENFIRMWARE_VARIANT_SUNW */ + +#ifndef OFW_I2C_REG_NCELLS +#define OFW_I2C_REG_NCELLS 1 +#endif + +#ifndef OFW_I2C_REG_ADDRESS +#define OFW_I2C_REG_ADDRESS 0 +#endif + +/* No default for OFW_I2C_REG_CHANNEL. */ + +#ifndef OFW_I2C_ADDRESS_MASK +#define OFW_I2C_ADDRESS_MASK __BITS(0,31) +#endif + +#ifndef OFW_I2C_ADDRESS_SHIFT +#define OFW_I2C_ADDRESS_SHIFT 0 +#endif + +static bool +of_i2c_get_address(i2c_tag_t tag, int node, uint32_t *addrp) { - int node, len; - char name[32]; - uint64_t reg64; - uint32_t reg32; - uint64_t addr; - prop_array_t array = NULL; - prop_dictionary_t dev; + uint32_t reg[OFW_I2C_REG_NCELLS]; + uint32_t addr; +#ifdef OFW_I2C_REG_CHANNEL + uint32_t channel; +#endif + + if (OF_getprop(node, "reg", reg, sizeof(reg)) != sizeof(reg)) { + /* + * "reg" property is malformed; reject the device. + */ + return false; + } + + addr = be32toh(reg[OFW_I2C_REG_ADDRESS]); + addr = (addr & OFW_I2C_ADDRESS_MASK) >> OFW_I2C_ADDRESS_SHIFT; + +#ifdef OFW_I2C_REG_CHANNEL + /* + * If the channel in the "reg" property does not match, + * reject the device. + */ + channel = be32toh(reg[OFW_I2C_REG_CHANNEL]); + if (channel != tag->ic_channel) { + return false; + } +#endif - for (node = OF_child(ofnode); node; node = OF_peer(node)) { - if (OF_getprop(node, "name", name, sizeof(name)) <= 0) + *addrp = addr; + return true; +} + +/* + * This follows the Device Tree bindings for i2c, which for the most part + * work for the classical OpenFirmware implementations, as well. There are + * some quirks do deal with for different OpenFirmware implementations, which + * are mainly in how the "reg" property is interpreted. + */ +static int +of_i2c_enumerate_devices(device_t dev, devhandle_t call_handle, void *v) +{ + struct i2c_enumerate_devices_args *args = v; + int i2c_node, node; + char name[32], compat_buf[32]; + uint32_t addr; + char *clist; + int clist_size; + bool cbrv; + + i2c_node = devhandle_to_of(call_handle); + + /* + * The Device Tree bindings state that if a controller has a + * child node named "i2c-bus", then that is the node beneath + * which the child devices are populated. + * + * Some OpenFirmware variants use a slightly different name. + */ + for (node = OF_child(i2c_node); node != 0; node = OF_peer(node)) { + if (OF_getprop(node, "name", name, sizeof(name)) <= 0) { + continue; + } + if (strcmp(name, "i2c-bus") == 0 +#if defined(__HAVE_OPENFIRMWARE_VARIANT_SUNW) + || strcmp(name, "i2c") == 0 +#endif /* __HAVE_OPENFIRMWARE_VARIANT_SUNW */ + ) { + i2c_node = node; + break; + } + } + + for (node = OF_child(i2c_node); node != 0; node = OF_peer(node)) { + if (OF_getprop(node, "name", name, sizeof(name)) <= 0) { + continue; + } + if (!of_i2c_get_address(args->ia->ia_tag, node, &addr)) { continue; - len = OF_getproplen(node, "reg"); - addr = 0; - if (cell_size == 8 && len >= sizeof(reg64)) { - if (OF_getprop(node, "reg", ®64, sizeof(reg64)) - < sizeof(reg64)) - continue; - addr = be64toh(reg64); - /* - * The i2c bus number (0 or 1) is encoded in bit 33 - * of the register, but we encode it in bit 8 of - * i2c_addr_t. - */ - if (addr & 0x100000000) - addr = (addr & 0xff) | 0x100; - } else if (cell_size == 4 && len >= sizeof(reg32)) { - if (OF_getprop(node, "reg", ®32, sizeof(reg32)) - < sizeof(reg32)) - continue; - addr = be32toh(reg32); - } else { + } + + clist_size = OF_getproplen(node, "compatible"); + if (clist_size <= 0) { +#ifndef OFW_I2C_ALLOW_MISSING_COMPATIBLE_PROPERTY + continue; +#else + clist_size = 0; +#endif + } + clist = kmem_tmpbuf_alloc(clist_size, + compat_buf, sizeof(compat_buf), KM_SLEEP); + if (OF_getprop(node, "compatible", clist, clist_size) < + clist_size) { + kmem_tmpbuf_free(clist, clist_size, compat_buf); continue; } - addr >>= addr_shift; - if (addr == 0) continue; - if (array == NULL) - array = prop_array_create(); + args->ia->ia_addr = (i2c_addr_t)addr; + args->ia->ia_name = name; + args->ia->ia_clist = clist; + args->ia->ia_clist_size = clist_size; + args->ia->ia_devhandle = devhandle_from_of(node); - dev = prop_dictionary_create(); - prop_dictionary_set_string(dev, "name", name); - prop_dictionary_set_uint32(dev, "addr", addr); - prop_dictionary_set_uint64(dev, "cookie", node); - prop_dictionary_set_uint32(dev, "cookietype", I2C_COOKIE_OF); - of_to_dataprop(dev, node, "compatible", "compatible"); - prop_array_add(array, dev); - prop_object_release(dev); - } + cbrv = args->callback(dev, args); + + kmem_tmpbuf_free(clist, clist_size, compat_buf); - if (array != NULL) { - prop_dictionary_set(props, "i2c-child-devices", array); - prop_object_release(array); + if (!cbrv) { + break; + } } + + return 0; } +OF_DEVICE_CALL_REGISTER("i2c-enumerate-devices", of_i2c_enumerate_devices);