1 /* $NetBSD: gpiopwm.c,v 1.2 2011/11/13 13:16:33 mbalmer 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(NULL, 0, NULL, NULL, 113 CTLFLAG_PERMANENT, 114 CTLTYPE_NODE, "hw", NULL, 115 NULL, 0, NULL, 0, 116 CTL_HW, CTL_EOL); 117 sysctl_createv(&sc->sc_log, 0, NULL, &node, 118 0, 119 CTLTYPE_NODE, device_xname(sc->sc_dev), 120 SYSCTL_DESCR("GPIO software PWM"), 121 NULL, 0, NULL, 0, 122 CTL_HW, CTL_CREATE, CTL_EOL); 123 124 if (node == NULL) { 125 printf(": can't create sysctl node\n"); 126 return; 127 } 128 129 sysctl_createv(&sc->sc_log, 0, &node, NULL, 130 CTLFLAG_READWRITE, 131 CTLTYPE_INT, "on", 132 SYSCTL_DESCR("PWM 'on' period in ticks"), 133 gpiopwm_set_on, 0, sc, 0, 134 CTL_CREATE, CTL_EOL); 135 sysctl_createv(&sc->sc_log, 0, &node, NULL, 136 CTLFLAG_READWRITE, 137 CTLTYPE_INT, "off", 138 SYSCTL_DESCR("PWM 'off' period in ticks"), 139 gpiopwm_set_off, 0, sc, 0, 140 CTL_CREATE, CTL_EOL); 141 142 aprint_normal("\n"); 143 return; 144 } 145 146 int 147 gpiopwm_detach(device_t self, int flags) 148 { 149 struct gpiopwm_softc *sc = device_private(self); 150 151 callout_halt(&sc->sc_pulse, NULL); 152 callout_destroy(&sc->sc_pulse); 153 gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_LOW); 154 155 pmf_device_deregister(self); 156 gpio_pin_unmap(sc->sc_gpio, &sc->sc_map); 157 158 if (sc->sc_log != NULL) { 159 sysctl_teardown(&sc->sc_log); 160 sc->sc_log = NULL; 161 } 162 return 0; 163 } 164 165 static int 166 gpiopwm_set_on(SYSCTLFN_ARGS) 167 { 168 struct sysctlnode node; 169 struct gpiopwm_softc *sc; 170 int val, error; 171 172 node = *rnode; 173 sc = node.sysctl_data; 174 175 callout_halt(&sc->sc_pulse, NULL); 176 gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_LOW); 177 node.sysctl_data = &val; 178 179 val = sc->sc_ticks_on; 180 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 181 if (error || newp == NULL) 182 return error; 183 184 sc->sc_ticks_on = val; 185 if (sc->sc_ticks_on > 0 && sc->sc_ticks_off > 0) { 186 gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_HIGH); 187 callout_schedule(&sc->sc_pulse, sc->sc_ticks_on); 188 } 189 return 0; 190 } 191 192 static int 193 gpiopwm_set_off(SYSCTLFN_ARGS) 194 { 195 struct sysctlnode node; 196 struct gpiopwm_softc *sc; 197 int val, error; 198 199 node = *rnode; 200 sc = node.sysctl_data; 201 202 callout_halt(&sc->sc_pulse, NULL); 203 gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_LOW); 204 node.sysctl_data = &val; 205 206 val = sc->sc_ticks_off; 207 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 208 if (error || newp == NULL) 209 return error; 210 211 sc->sc_ticks_off = val; 212 if (sc->sc_ticks_on > 0 && sc->sc_ticks_off > 0) { 213 gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_HIGH); 214 callout_schedule(&sc->sc_pulse, sc->sc_ticks_on); 215 } 216 return 0; 217 } 218 219 static void 220 gpiopwm_pulse(void *arg) 221 { 222 struct gpiopwm_softc *sc; 223 224 sc = arg; 225 if (gpio_pin_read(sc->sc_gpio, &sc->sc_map, 0) == GPIO_PIN_HIGH) { 226 gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_LOW); 227 callout_schedule(&sc->sc_pulse, sc->sc_ticks_off); 228 } else { 229 gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_HIGH); 230 callout_schedule(&sc->sc_pulse, sc->sc_ticks_on); 231 } 232 } 233 234 int 235 gpiopwm_activate(device_t self, enum devact act) 236 { 237 struct gpiopwm_softc *sc = device_private(self); 238 239 switch (act) { 240 case DVACT_DEACTIVATE: 241 sc->sc_dying = 1; 242 return 0; 243 default: 244 return EOPNOTSUPP; 245 } 246 247 } 248