1 /* $NetBSD: gpiopwm.c,v 1.7 2017/10/28 04:53:56 riastradh Exp $ */ 2 3 /* 4 * Copyright (c) 2011 Marc Balmer <marc@msys.ch> 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, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 /* 29 * Driver for pulsing GPIO pins in software 30 */ 31 32 #include <sys/param.h> 33 #include <sys/device.h> 34 #include <sys/gpio.h> 35 #include <sys/sysctl.h> 36 37 #include <dev/gpio/gpiovar.h> 38 39 #include "ioconf.h" 40 41 #define GPIOPWM_NPINS 1 42 43 struct gpiopwm_softc { 44 device_t sc_dev; 45 void *sc_gpio; 46 struct gpio_pinmap sc_map; 47 int _map[GPIOPWM_NPINS]; 48 49 callout_t sc_pulse; 50 int sc_ticks_on; 51 int sc_ticks_off; 52 53 struct sysctllog *sc_log; 54 int sc_dying; 55 }; 56 57 int gpiopwm_match(device_t, cfdata_t, void *); 58 void gpiopwm_attach(device_t, device_t, void *); 59 int gpiopwm_detach(device_t, int); 60 int gpiopwm_activate(device_t, enum devact); 61 static int gpiopwm_set_on(SYSCTLFN_ARGS); 62 static int gpiopwm_set_off(SYSCTLFN_ARGS); 63 static void gpiopwm_pulse(void *); 64 65 CFATTACH_DECL_NEW(gpiopwm, sizeof(struct gpiopwm_softc), 66 gpiopwm_match, gpiopwm_attach, gpiopwm_detach, gpiopwm_activate); 67 68 int 69 gpiopwm_match(device_t parent, cfdata_t cf, 70 void *aux) 71 { 72 struct gpio_attach_args *ga = aux; 73 74 if (strcmp(ga->ga_dvname, cf->cf_name)) 75 return 0; 76 77 if (ga->ga_offset == -1) 78 return 0; 79 80 /* Check number of pins, must be 1 */ 81 if (gpio_npins(ga->ga_mask) != GPIOPWM_NPINS) { 82 aprint_debug("%s: invalid pin mask 0x%02x\n", cf->cf_name, 83 ga->ga_mask); 84 return 0; 85 } 86 return 1; 87 } 88 89 void 90 gpiopwm_attach(device_t parent, device_t self, void *aux) 91 { 92 struct gpiopwm_softc *sc = device_private(self); 93 struct gpio_attach_args *ga = aux; 94 const struct sysctlnode *node; 95 96 sc->sc_dev = self; 97 98 /* Map pin */ 99 sc->sc_gpio = ga->ga_gpio; 100 sc->sc_map.pm_map = sc->_map; 101 if (gpio_pin_map(sc->sc_gpio, ga->ga_offset, ga->ga_mask, 102 &sc->sc_map)) { 103 aprint_error(": can't map pin\n"); 104 return; 105 } 106 aprint_normal(" [%d]", sc->sc_map.pm_map[0]); 107 if (!pmf_device_register(self, NULL, NULL)) 108 aprint_error_dev(self, "couldn't establish power handler\n"); 109 110 callout_init(&sc->sc_pulse, CALLOUT_MPSAFE); 111 callout_setfunc(&sc->sc_pulse, gpiopwm_pulse, sc); 112 113 sysctl_createv(&sc->sc_log, 0, NULL, &node, 114 0, 115 CTLTYPE_NODE, device_xname(sc->sc_dev), 116 SYSCTL_DESCR("GPIO software PWM"), 117 NULL, 0, NULL, 0, 118 CTL_HW, CTL_CREATE, CTL_EOL); 119 120 if (node == NULL) { 121 aprint_error(": can't create sysctl node\n"); 122 return; 123 } 124 125 sysctl_createv(&sc->sc_log, 0, &node, NULL, 126 CTLFLAG_READWRITE, 127 CTLTYPE_INT, "on", 128 SYSCTL_DESCR("PWM 'on' period in ticks"), 129 gpiopwm_set_on, 0, (void *)sc, 0, 130 CTL_CREATE, CTL_EOL); 131 sysctl_createv(&sc->sc_log, 0, &node, NULL, 132 CTLFLAG_READWRITE, 133 CTLTYPE_INT, "off", 134 SYSCTL_DESCR("PWM 'off' period in ticks"), 135 gpiopwm_set_off, 0, (void *)sc, 0, 136 CTL_CREATE, CTL_EOL); 137 138 aprint_normal("\n"); 139 return; 140 } 141 142 int 143 gpiopwm_detach(device_t self, int flags) 144 { 145 struct gpiopwm_softc *sc = device_private(self); 146 147 callout_halt(&sc->sc_pulse, NULL); 148 callout_destroy(&sc->sc_pulse); 149 gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_LOW); 150 151 pmf_device_deregister(self); 152 gpio_pin_unmap(sc->sc_gpio, &sc->sc_map); 153 154 if (sc->sc_log != NULL) { 155 sysctl_teardown(&sc->sc_log); 156 sc->sc_log = NULL; 157 } 158 return 0; 159 } 160 161 static int 162 gpiopwm_set_on(SYSCTLFN_ARGS) 163 { 164 struct sysctlnode node; 165 struct gpiopwm_softc *sc; 166 int val, error; 167 168 node = *rnode; 169 sc = node.sysctl_data; 170 171 callout_halt(&sc->sc_pulse, NULL); 172 gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_LOW); 173 node.sysctl_data = &val; 174 175 val = sc->sc_ticks_on; 176 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 177 if (error || newp == NULL) 178 return error; 179 180 sc->sc_ticks_on = val; 181 if (sc->sc_ticks_on > 0 && sc->sc_ticks_off > 0) { 182 gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_HIGH); 183 callout_schedule(&sc->sc_pulse, sc->sc_ticks_on); 184 } 185 return 0; 186 } 187 188 static int 189 gpiopwm_set_off(SYSCTLFN_ARGS) 190 { 191 struct sysctlnode node; 192 struct gpiopwm_softc *sc; 193 int val, error; 194 195 node = *rnode; 196 sc = node.sysctl_data; 197 198 callout_halt(&sc->sc_pulse, NULL); 199 gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_LOW); 200 node.sysctl_data = &val; 201 202 val = sc->sc_ticks_off; 203 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 204 if (error || newp == NULL) 205 return error; 206 207 sc->sc_ticks_off = val; 208 if (sc->sc_ticks_on > 0 && sc->sc_ticks_off > 0) { 209 gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_HIGH); 210 callout_schedule(&sc->sc_pulse, sc->sc_ticks_on); 211 } 212 return 0; 213 } 214 215 static void 216 gpiopwm_pulse(void *arg) 217 { 218 struct gpiopwm_softc *sc; 219 220 sc = arg; 221 if (gpio_pin_read(sc->sc_gpio, &sc->sc_map, 0) == GPIO_PIN_HIGH) { 222 gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_LOW); 223 callout_schedule(&sc->sc_pulse, sc->sc_ticks_off); 224 } else { 225 gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_HIGH); 226 callout_schedule(&sc->sc_pulse, sc->sc_ticks_on); 227 } 228 } 229 230 int 231 gpiopwm_activate(device_t self, enum devact act) 232 { 233 struct gpiopwm_softc *sc = device_private(self); 234 235 switch (act) { 236 case DVACT_DEACTIVATE: 237 sc->sc_dying = 1; 238 return 0; 239 default: 240 return EOPNOTSUPP; 241 } 242 } 243