1 /* $NetBSD: fdt_gpio.c,v 1.6 2018/06/30 20:34:43 jmcneill 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.6 2018/06/30 20:34:43 jmcneill 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 struct fdtbus_gpio_pin * 81 fdtbus_gpio_acquire(int phandle, const char *prop, int flags) 82 { 83 return fdtbus_gpio_acquire_index(phandle, prop, 0, flags); 84 } 85 86 struct fdtbus_gpio_pin * 87 fdtbus_gpio_acquire_index(int phandle, const char *prop, 88 int index, int flags) 89 { 90 struct fdtbus_gpio_controller *gc; 91 struct fdtbus_gpio_pin *gp = NULL; 92 const uint32_t *gpios, *p; 93 u_int n, gpio_cells; 94 int len, resid; 95 96 gpios = fdtbus_get_prop(phandle, prop, &len); 97 if (gpios == NULL) 98 return NULL; 99 100 p = gpios; 101 for (n = 0, resid = len; resid > 0; n++) { 102 const int gc_phandle = 103 fdtbus_get_phandle_from_native(be32toh(p[0])); 104 if (of_getprop_uint32(gc_phandle, "#gpio-cells", &gpio_cells)) 105 break; 106 if (n == index) { 107 gc = fdtbus_get_gpio_controller(gc_phandle); 108 if (gc == NULL) 109 return NULL; 110 gp = kmem_alloc(sizeof(*gp), KM_SLEEP); 111 gp->gp_gc = gc; 112 gp->gp_priv = gc->gc_funcs->acquire(gc->gc_dev, 113 &p[0], (gpio_cells + 1) * 4, flags); 114 if (gp->gp_priv == NULL) { 115 kmem_free(gp, sizeof(*gp)); 116 return NULL; 117 } 118 break; 119 } 120 resid -= (gpio_cells + 1) * 4; 121 p += gpio_cells + 1; 122 } 123 124 return gp; 125 } 126 127 void 128 fdtbus_gpio_release(struct fdtbus_gpio_pin *gp) 129 { 130 struct fdtbus_gpio_controller *gc = gp->gp_gc; 131 132 gc->gc_funcs->release(gc->gc_dev, gp->gp_priv); 133 kmem_free(gp, sizeof(*gp)); 134 } 135 136 int 137 fdtbus_gpio_read(struct fdtbus_gpio_pin *gp) 138 { 139 struct fdtbus_gpio_controller *gc = gp->gp_gc; 140 141 return gc->gc_funcs->read(gc->gc_dev, gp->gp_priv, false); 142 } 143 144 void 145 fdtbus_gpio_write(struct fdtbus_gpio_pin *gp, int val) 146 { 147 struct fdtbus_gpio_controller *gc = gp->gp_gc; 148 149 gc->gc_funcs->write(gc->gc_dev, gp->gp_priv, val, false); 150 } 151 152 int 153 fdtbus_gpio_read_raw(struct fdtbus_gpio_pin *gp) 154 { 155 struct fdtbus_gpio_controller *gc = gp->gp_gc; 156 157 return gc->gc_funcs->read(gc->gc_dev, gp->gp_priv, true); 158 } 159 160 void 161 fdtbus_gpio_write_raw(struct fdtbus_gpio_pin *gp, int val) 162 { 163 struct fdtbus_gpio_controller *gc = gp->gp_gc; 164 165 gc->gc_funcs->write(gc->gc_dev, gp->gp_priv, val, true); 166 } 167