1 /* $NetBSD: fdt_gpio.c,v 1.7 2020/12/23 04:07:34 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca> 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 AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: fdt_gpio.c,v 1.7 2020/12/23 04:07:34 thorpej Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/bus.h> 34 #include <sys/kmem.h> 35 #include <sys/queue.h> 36 37 #include <libfdt.h> 38 #include <dev/fdt/fdtvar.h> 39 40 struct fdtbus_gpio_controller { 41 device_t gc_dev; 42 int gc_phandle; 43 const struct fdtbus_gpio_controller_func *gc_funcs; 44 45 LIST_ENTRY(fdtbus_gpio_controller) gc_next; 46 }; 47 48 static LIST_HEAD(, fdtbus_gpio_controller) fdtbus_gpio_controllers = 49 LIST_HEAD_INITIALIZER(fdtbus_gpio_controllers); 50 51 int 52 fdtbus_register_gpio_controller(device_t dev, int phandle, 53 const struct fdtbus_gpio_controller_func *funcs) 54 { 55 struct fdtbus_gpio_controller *gc; 56 57 gc = kmem_alloc(sizeof(*gc), KM_SLEEP); 58 gc->gc_dev = dev; 59 gc->gc_phandle = phandle; 60 gc->gc_funcs = funcs; 61 62 LIST_INSERT_HEAD(&fdtbus_gpio_controllers, gc, gc_next); 63 64 return 0; 65 } 66 67 static struct fdtbus_gpio_controller * 68 fdtbus_get_gpio_controller(int phandle) 69 { 70 struct fdtbus_gpio_controller *gc; 71 72 LIST_FOREACH(gc, &fdtbus_gpio_controllers, gc_next) { 73 if (gc->gc_phandle == phandle) 74 return gc; 75 } 76 77 return NULL; 78 } 79 80 int 81 fdtbus_gpio_count(int phandle, const char *prop) 82 { 83 const uint32_t *gpios, *p; 84 u_int n, gpio_cells; 85 int len, resid; 86 87 gpios = fdtbus_get_prop(phandle, prop, &len); 88 if (gpios == NULL) 89 return 0; 90 91 p = gpios; 92 for (n = 0, resid = len; resid > 0; n++) { 93 const int gc_phandle = 94 fdtbus_get_phandle_from_native(be32toh(p[0])); 95 if (of_getprop_uint32(gc_phandle, "#gpio-cells", &gpio_cells)) 96 break; 97 resid -= (gpio_cells + 1) * 4; 98 p += gpio_cells + 1; 99 } 100 101 return n; 102 } 103 104 struct fdtbus_gpio_pin * 105 fdtbus_gpio_acquire(int phandle, const char *prop, int flags) 106 { 107 return fdtbus_gpio_acquire_index(phandle, prop, 0, flags); 108 } 109 110 struct fdtbus_gpio_pin * 111 fdtbus_gpio_acquire_index(int phandle, const char *prop, 112 int index, int flags) 113 { 114 struct fdtbus_gpio_controller *gc; 115 struct fdtbus_gpio_pin *gp = NULL; 116 const uint32_t *gpios, *p; 117 u_int n, gpio_cells; 118 int len, resid; 119 120 gpios = fdtbus_get_prop(phandle, prop, &len); 121 if (gpios == NULL) 122 return NULL; 123 124 p = gpios; 125 for (n = 0, resid = len; resid > 0; n++) { 126 const int gc_phandle = 127 fdtbus_get_phandle_from_native(be32toh(p[0])); 128 if (of_getprop_uint32(gc_phandle, "#gpio-cells", &gpio_cells)) 129 break; 130 if (n == index) { 131 gc = fdtbus_get_gpio_controller(gc_phandle); 132 if (gc == NULL) 133 return NULL; 134 gp = kmem_alloc(sizeof(*gp), KM_SLEEP); 135 gp->gp_gc = gc; 136 gp->gp_priv = gc->gc_funcs->acquire(gc->gc_dev, 137 &p[0], (gpio_cells + 1) * 4, flags); 138 if (gp->gp_priv == NULL) { 139 kmem_free(gp, sizeof(*gp)); 140 return NULL; 141 } 142 break; 143 } 144 resid -= (gpio_cells + 1) * 4; 145 p += gpio_cells + 1; 146 } 147 148 return gp; 149 } 150 151 void 152 fdtbus_gpio_release(struct fdtbus_gpio_pin *gp) 153 { 154 struct fdtbus_gpio_controller *gc = gp->gp_gc; 155 156 gc->gc_funcs->release(gc->gc_dev, gp->gp_priv); 157 kmem_free(gp, sizeof(*gp)); 158 } 159 160 int 161 fdtbus_gpio_read(struct fdtbus_gpio_pin *gp) 162 { 163 struct fdtbus_gpio_controller *gc = gp->gp_gc; 164 165 return gc->gc_funcs->read(gc->gc_dev, gp->gp_priv, false); 166 } 167 168 void 169 fdtbus_gpio_write(struct fdtbus_gpio_pin *gp, int val) 170 { 171 struct fdtbus_gpio_controller *gc = gp->gp_gc; 172 173 gc->gc_funcs->write(gc->gc_dev, gp->gp_priv, val, false); 174 } 175 176 int 177 fdtbus_gpio_read_raw(struct fdtbus_gpio_pin *gp) 178 { 179 struct fdtbus_gpio_controller *gc = gp->gp_gc; 180 181 return gc->gc_funcs->read(gc->gc_dev, gp->gp_priv, true); 182 } 183 184 void 185 fdtbus_gpio_write_raw(struct fdtbus_gpio_pin *gp, int val) 186 { 187 struct fdtbus_gpio_controller *gc = gp->gp_gc; 188 189 gc->gc_funcs->write(gc->gc_dev, gp->gp_priv, val, true); 190 } 191