Home | History | Annotate | Line # | Download | only in fdt
      1 /* $NetBSD: fdt_i2c.c,v 1.16 2025/09/23 00:52:14 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 /*-
     30  * Copyright (c) 2015 Jared D. McNeill <jmcneill (at) invisible.ca>
     31  * All rights reserved.
     32  *
     33  * Redistribution and use in source and binary forms, with or without
     34  * modification, are permitted provided that the following conditions
     35  * are met:
     36  * 1. Redistributions of source code must retain the above copyright
     37  *    notice, this list of conditions and the following disclaimer.
     38  * 2. Redistributions in binary form must reproduce the above copyright
     39  *    notice, this list of conditions and the following disclaimer in the
     40  *    documentation and/or other materials provided with the distribution.
     41  *
     42  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     43  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     44  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     45  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     46  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     47  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     48  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     49  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     50  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     51  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     52  * SUCH DAMAGE.
     53  */
     54 
     55 #include <sys/cdefs.h>
     56 __KERNEL_RCSID(0, "$NetBSD: fdt_i2c.c,v 1.16 2025/09/23 00:52:14 thorpej Exp $");
     57 
     58 #include <sys/param.h>
     59 #include <sys/bus.h>
     60 #include <sys/kmem.h>
     61 #include <sys/queue.h>
     62 #include <sys/systm.h>
     63 
     64 #include <libfdt.h>
     65 #include <dev/fdt/fdtvar.h>
     66 
     67 #include <dev/i2c/i2cvar.h>
     68 #include <dev/i2c/i2c_calls.h>
     69 #include <dev/i2c/i2c_enum.h>
     70 
     71 struct fdtbus_i2c_controller {
     72 	i2c_tag_t i2c_tag;
     73 	int i2c_phandle;
     74 
     75 	LIST_ENTRY(fdtbus_i2c_controller) i2c_next;
     76 };
     77 
     78 static LIST_HEAD(, fdtbus_i2c_controller) fdtbus_i2c_controllers =
     79     LIST_HEAD_INITIALIZER(fdtbus_i2c_controllers);
     80 
     81 void
     82 fdtbus_register_i2c_controller(device_t dev, i2c_tag_t tag)
     83 {
     84 	int phandle = devhandle_to_of(device_handle(dev));
     85 	struct fdtbus_i2c_controller *i2c;
     86 
     87 	i2c = kmem_alloc(sizeof(*i2c), KM_SLEEP);
     88 	i2c->i2c_tag = tag;
     89 	i2c->i2c_phandle = phandle;
     90 
     91 	LIST_INSERT_HEAD(&fdtbus_i2c_controllers, i2c, i2c_next);
     92 }
     93 
     94 static struct fdtbus_i2c_controller *
     95 fdtbus_get_i2c_controller(int phandle)
     96 {
     97 	struct fdtbus_i2c_controller *i2c;
     98 
     99 	LIST_FOREACH(i2c, &fdtbus_i2c_controllers, i2c_next) {
    100 		if (i2c->i2c_phandle == phandle)
    101 			return i2c;
    102 	}
    103 
    104 	return NULL;
    105 }
    106 
    107 i2c_tag_t
    108 fdtbus_i2c_get_tag(int phandle)
    109 {
    110 	struct fdtbus_i2c_controller *i2c;
    111 
    112 	i2c = fdtbus_get_i2c_controller(phandle);
    113 	if (i2c == NULL)
    114 		return NULL;
    115 
    116 	return i2c->i2c_tag;
    117 }
    118 
    119 i2c_tag_t
    120 fdtbus_i2c_acquire(int phandle, const char *prop)
    121 {
    122 	int i2c_phandle;
    123 
    124 	i2c_phandle = fdtbus_get_phandle(phandle, prop);
    125 	if (i2c_phandle == -1)
    126 		return NULL;
    127 
    128 	return fdtbus_i2c_get_tag(i2c_phandle);
    129 }
    130 
    131 static int
    132 fdtbus_i2c_enumerate_devices(device_t dev, devhandle_t call_handle, void *v)
    133 {
    134 	struct i2c_enumerate_devices_args *args = v;
    135 	int i2c_node, node;
    136 	char name[32], compat_buf[32];
    137 	bus_addr_t addr;
    138 	char *clist;
    139 	int clist_size;
    140 	bool cbrv;
    141 
    142 	i2c_node = devhandle_to_of(call_handle);
    143 
    144 	/*
    145 	 * The Device Tree bindings state that if a controller has a
    146 	 * child node named "i2c-bus", then that is the node beneath
    147 	 * which the child devices are populated.
    148 	 */
    149 	for (node = OF_child(i2c_node); node != 0; node = OF_peer(node)) {
    150 		if (OF_getprop(node, "name", name, sizeof(name)) <= 0) {
    151 			continue;
    152 		}
    153 		if (strcmp(name, "i2c-bus") == 0) {
    154 			i2c_node = node;
    155 			break;
    156 		}
    157 	}
    158 
    159 	for (node = OF_child(i2c_node); node != 0; node = OF_peer(node)) {
    160 		if (OF_getprop(node, "name", name, sizeof(name)) <= 0) {
    161 			continue;
    162 		}
    163 		if (fdtbus_get_reg(node, 0, &addr, NULL) != 0) {
    164 			continue;
    165 		}
    166 
    167 		clist_size = OF_getproplen(node, "compatible");
    168 		if (clist_size <= 0) {
    169 			continue;
    170 		}
    171 		clist = kmem_tmpbuf_alloc(clist_size,
    172 		    compat_buf, sizeof(compat_buf), KM_SLEEP);
    173 		if (OF_getprop(node, "compatible", clist, clist_size)
    174 			       < clist_size) {
    175 			kmem_tmpbuf_free(clist, clist_size, compat_buf);
    176 			continue;
    177 		}
    178 
    179 		cbrv = i2c_enumerate_device(dev, args, name, clist,
    180 		    clist_size, (i2c_addr_t)addr,
    181 		    devhandle_from_of(call_handle, node));
    182 
    183 		kmem_tmpbuf_free(clist, clist_size, compat_buf);
    184 
    185 		if (!cbrv) {
    186 			break;
    187 		}
    188 	}
    189 
    190 	return 0;
    191 }
    192 OF_DEVICE_CALL_REGISTER(I2C_ENUMERATE_DEVICES_STR,
    193 			fdtbus_i2c_enumerate_devices);
    194