1 /* $NetBSD: gpiopwm.c,v 1.4 2014/02/25 18:30:09 pooka 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 #define GPIOPWM_NPINS 1 40 41 struct gpiopwm_softc { 42 device_t sc_dev; 43 void *sc_gpio; 44 struct gpio_pinmap sc_map; 45 int _map[GPIOPWM_NPINS]; 46 47 callout_t sc_pulse; 48 int sc_ticks_on; 49 int sc_ticks_off; 50 51 struct sysctllog *sc_log; 52 int sc_dying; 53 }; 54 55 int gpiopwm_match(device_t, cfdata_t, void *); 56 void gpiopwm_attach(device_t, device_t, void *); 57 int gpiopwm_detach(device_t, int); 58 int gpiopwm_activate(device_t, enum devact); 59 static int gpiopwm_set_on(SYSCTLFN_ARGS); 60 static int gpiopwm_set_off(SYSCTLFN_ARGS); 61 static void gpiopwm_pulse(void *); 62 63 CFATTACH_DECL_NEW(gpiopwm, sizeof(struct gpiopwm_softc), 64 gpiopwm_match, gpiopwm_attach, gpiopwm_detach, gpiopwm_activate); 65 66 extern struct cfdriver gpiopwm_cd; 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 pmf_device_register(self, NULL, NULL); 108 109 callout_init(&sc->sc_pulse, CALLOUT_MPSAFE); 110 callout_setfunc(&sc->sc_pulse, gpiopwm_pulse, sc); 111 112 sysctl_createv(&sc->sc_log, 0, NULL, &node, 113 0, 114 CTLTYPE_NODE, device_xname(sc->sc_dev), 115 SYSCTL_DESCR("GPIO software PWM"), 116 NULL, 0, NULL, 0, 117 CTL_HW, CTL_CREATE, CTL_EOL); 118 119 if (node == NULL) { 120 printf(": can't create sysctl node\n"); 121 return; 122 } 123 124 sysctl_createv(&sc->sc_log, 0, &node, NULL, 125 CTLFLAG_READWRITE, 126 CTLTYPE_INT, "on", 127 SYSCTL_DESCR("PWM 'on' period in ticks"), 128 gpiopwm_set_on, 0, (void *)sc, 0, 129 CTL_CREATE, CTL_EOL); 130 sysctl_createv(&sc->sc_log, 0, &node, NULL, 131 CTLFLAG_READWRITE, 132 CTLTYPE_INT, "off", 133 SYSCTL_DESCR("PWM 'off' period in ticks"), 134 gpiopwm_set_off, 0, (void *)sc, 0, 135 CTL_CREATE, CTL_EOL); 136 137 aprint_normal("\n"); 138 return; 139 } 140 141 int 142 gpiopwm_detach(device_t self, int flags) 143 { 144 struct gpiopwm_softc *sc = device_private(self); 145 146 callout_halt(&sc->sc_pulse, NULL); 147 callout_destroy(&sc->sc_pulse); 148 gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_LOW); 149 150 pmf_device_deregister(self); 151 gpio_pin_unmap(sc->sc_gpio, &sc->sc_map); 152 153 if (sc->sc_log != NULL) { 154 sysctl_teardown(&sc->sc_log); 155 sc->sc_log = NULL; 156 } 157 return 0; 158 } 159 160 static int 161 gpiopwm_set_on(SYSCTLFN_ARGS) 162 { 163 struct sysctlnode node; 164 struct gpiopwm_softc *sc; 165 int val, error; 166 167 node = *rnode; 168 sc = node.sysctl_data; 169 170 callout_halt(&sc->sc_pulse, NULL); 171 gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_LOW); 172 node.sysctl_data = &val; 173 174 val = sc->sc_ticks_on; 175 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 176 if (error || newp == NULL) 177 return error; 178 179 sc->sc_ticks_on = val; 180 if (sc->sc_ticks_on > 0 && sc->sc_ticks_off > 0) { 181 gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_HIGH); 182 callout_schedule(&sc->sc_pulse, sc->sc_ticks_on); 183 } 184 return 0; 185 } 186 187 static int 188 gpiopwm_set_off(SYSCTLFN_ARGS) 189 { 190 struct sysctlnode node; 191 struct gpiopwm_softc *sc; 192 int val, error; 193 194 node = *rnode; 195 sc = node.sysctl_data; 196 197 callout_halt(&sc->sc_pulse, NULL); 198 gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_LOW); 199 node.sysctl_data = &val; 200 201 val = sc->sc_ticks_off; 202 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 203 if (error || newp == NULL) 204 return error; 205 206 sc->sc_ticks_off = val; 207 if (sc->sc_ticks_on > 0 && sc->sc_ticks_off > 0) { 208 gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_HIGH); 209 callout_schedule(&sc->sc_pulse, sc->sc_ticks_on); 210 } 211 return 0; 212 } 213 214 static void 215 gpiopwm_pulse(void *arg) 216 { 217 struct gpiopwm_softc *sc; 218 219 sc = arg; 220 if (gpio_pin_read(sc->sc_gpio, &sc->sc_map, 0) == GPIO_PIN_HIGH) { 221 gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_LOW); 222 callout_schedule(&sc->sc_pulse, sc->sc_ticks_off); 223 } else { 224 gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_HIGH); 225 callout_schedule(&sc->sc_pulse, sc->sc_ticks_on); 226 } 227 } 228 229 int 230 gpiopwm_activate(device_t self, enum devact act) 231 { 232 struct gpiopwm_softc *sc = device_private(self); 233 234 switch (act) { 235 case DVACT_DEACTIVATE: 236 sc->sc_dying = 1; 237 return 0; 238 default: 239 return EOPNOTSUPP; 240 } 241 242 } 243