1 /* $NetBSD: ofw_i2c_machdep.c,v 1.2 2025/09/21 19:12:45 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 2021, 2025 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: ofw_i2c_machdep.c,v 1.2 2025/09/21 19:12:45 thorpej Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/device.h> 34 #include <sys/endian.h> 35 #include <sys/kmem.h> 36 #include <sys/systm.h> 37 38 #include <dev/ofw/openfirm.h> 39 40 #include <dev/i2c/i2cvar.h> 41 #include <dev/i2c/i2c_calls.h> 42 #include <dev/i2c/i2c_enum.h> 43 44 /* 45 * Apple OpenFirmware implementations have the i2c device 46 * address shifted left 1 bit to account for the r/w bit 47 * on the wire. Some implementations also encode the channel 48 * number (for multi-channel controllers) in bit 8 of the 49 * address. 50 */ 51 #define OFW_I2C_ADDRESS_MASK __BITS(1,7) 52 #define OFW_I2C_ADDRESS_CHMASK __BIT(8) 53 54 static bool 55 of_i2c_get_address(device_t dev, int i2c_node, i2c_tag_t tag, int node, 56 uint32_t *addrp) 57 { 58 uint32_t reg; 59 uint32_t addr; 60 int channel; 61 62 /* 63 * dev is the iic bus instance. We need the controller parent 64 * so we can compare their OFW nodes. 65 */ 66 int ctlr_node = devhandle_to_of(device_handle(device_parent(dev))); 67 68 if (OF_getprop(node, "reg", ®, sizeof(reg)) != sizeof(reg)) { 69 /* 70 * Some Apple OpenFirmware implementations use "i2c-address" 71 * instead of "reg". 72 */ 73 if (OF_getprop(node, "i2c-address", ®, 74 sizeof(reg)) != sizeof(reg)) { 75 /* 76 * No address property; reject the device. 77 */ 78 return false; 79 } 80 } 81 82 addr = __SHIFTOUT(reg, OFW_I2C_ADDRESS_MASK); 83 channel = __SHIFTOUT(reg, OFW_I2C_ADDRESS_CHMASK); 84 85 aprint_debug_dev(dev, "%s: addr=0x%x channel=%u\n", __func__, 86 addr, channel); 87 88 /* 89 * If the controller supports multiple channels and the controller 90 * node and the i2c bus node are the same, then the devices for 91 * multiple channels are all mixed together in the device tree. 92 * We need to filter them by channel in this case. 93 */ 94 aprint_debug_dev(dev, "%s: ic_channel=%d i2c_node=%d ctlr_node=%d\n", 95 __func__, tag->ic_channel, i2c_node, ctlr_node); 96 if (tag->ic_channel != I2C_CHANNEL_DEFAULT && i2c_node == ctlr_node && 97 tag->ic_channel != channel) { 98 return false; 99 } 100 101 *addrp = addr; 102 return true; 103 } 104 105 static int 106 of_i2c_enumerate_devices(device_t dev, devhandle_t call_handle, void *v) 107 { 108 struct i2c_enumerate_devices_args *args = v; 109 int i2c_node, node; 110 char name[32], compat_buf[32]; 111 uint32_t addr; 112 char *clist; 113 int clist_size; 114 bool cbrv; 115 116 i2c_node = devhandle_to_of(call_handle); 117 118 for (node = OF_child(i2c_node); node != 0; node = OF_peer(node)) { 119 if (OF_getprop(node, "name", name, sizeof(name)) <= 0) { 120 continue; 121 } 122 if (!of_i2c_get_address(dev, i2c_node, args->ia->ia_tag, 123 node, &addr)) { 124 continue; 125 } 126 127 /* 128 * Some of Apple's older OpenFirmware implementations are 129 * rife with nodes lacking "compatible" properties. 130 */ 131 clist_size = OF_getproplen(node, "compatible"); 132 if (clist_size <= 0) { 133 clist = name; 134 clist_size = 0; 135 } else { 136 clist = kmem_tmpbuf_alloc(clist_size, 137 compat_buf, sizeof(compat_buf), KM_SLEEP); 138 if (OF_getprop(node, "compatible", clist, clist_size) 139 < clist_size) { 140 kmem_tmpbuf_free(clist, clist_size, compat_buf); 141 continue; 142 } 143 } 144 145 cbrv = i2c_enumerate_device(dev, args, name, 146 clist, clist_size, addr, 147 devhandle_from_of(call_handle, node)); 148 149 if (clist != name) { 150 kmem_tmpbuf_free(clist, clist_size, compat_buf); 151 } 152 153 if (!cbrv) { 154 break; 155 } 156 } 157 158 return 0; 159 } 160 OF_DEVICE_CALL_REGISTER(I2C_ENUMERATE_DEVICES_STR, 161 of_i2c_enumerate_devices); 162