1 /* $NetBSD: bcm2835_pwm.c,v 1.2 2016/02/02 13:55:50 skrll Exp $ */ 2 3 /*- 4 * Copyright (c) 2015 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Michael van Elst 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Driver for BCM2835 Pulse Width Modulator 34 * 35 * Each channel can be allocated and used individually, but 36 * for FIFO usage, both channels must to be requested. 37 */ 38 39 #include <sys/cdefs.h> 40 __KERNEL_RCSID(0, "$NetBSD: bcm2835_pwm.c,v 1.2 2016/02/02 13:55:50 skrll Exp $"); 41 42 #include "bcmdmac.h" 43 44 #include <sys/param.h> 45 #include <sys/systm.h> 46 #include <sys/device.h> 47 #include <sys/kernel.h> 48 #include <sys/bus.h> 49 #include <sys/atomic.h> 50 #include <sys/intr.h> 51 52 #include <arm/broadcom/bcm2835reg.h> 53 #include <arm/broadcom/bcm_amba.h> 54 55 #include <arm/broadcom/bcm2835_pwm.h> 56 57 struct bcm_pwm_channel { 58 struct bcm2835pwm_softc *sc; 59 uint32_t ctlmask, stamask, gapomask; 60 int rng, dat; 61 bool inuse; 62 uint32_t ctlsave, rngsave, datsave; 63 }; 64 65 struct bcm2835pwm_softc { 66 device_t sc_dev; 67 68 bus_space_tag_t sc_iot; 69 bus_space_handle_t sc_ioh; 70 bus_addr_t sc_iob; 71 72 int sc_clockrate; 73 struct bcm_pwm_channel sc_channels[2]; 74 kmutex_t sc_lock; 75 }; 76 77 #define PWM_WRITE(sc, reg, val) \ 78 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 79 #define PWM_READ(sc, reg) \ 80 bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)) 81 82 static int bcmpwm_match(device_t, cfdata_t, void *); 83 static void bcmpwm_attach(device_t, device_t, void *); 84 static int bcmpwm_wait(struct bcm2835pwm_softc *); 85 86 CFATTACH_DECL_NEW(bcmpwm_amba, sizeof(struct bcm2835pwm_softc), 87 bcmpwm_match, bcmpwm_attach, NULL, NULL); 88 89 /* ARGSUSED */ 90 static int 91 bcmpwm_match(device_t parent, cfdata_t match, void *aux) 92 { 93 struct amba_attach_args *aaa = aux; 94 95 if (strcmp(aaa->aaa_name, "bcmpwm") != 0) 96 return 0; 97 98 if (aaa->aaa_addr != BCM2835_PWM_BASE) 99 return 0; 100 101 return 1; 102 } 103 104 static void 105 bcmpwm_attach(device_t parent, device_t self, void *aux) 106 { 107 struct bcm2835pwm_softc *sc = device_private(self); 108 struct amba_attach_args *aaa = aux; 109 const prop_dictionary_t cfg = device_properties(self); 110 111 aprint_naive("\n"); 112 aprint_normal(": PWM\n"); 113 114 sc->sc_dev = self; 115 sc->sc_iot = aaa->aaa_iot; 116 sc->sc_iob = aaa->aaa_addr; 117 118 if (bus_space_map(aaa->aaa_iot, aaa->aaa_addr, BCM2835_PWM_SIZE, 0, 119 &sc->sc_ioh)) { 120 aprint_error_dev(sc->sc_dev, "unable to map device\n"); 121 goto fail0; 122 } 123 124 prop_dictionary_get_uint32(cfg, "pwmclockrate", &sc->sc_clockrate); 125 126 sc->sc_channels[0].sc = sc; 127 sc->sc_channels[0].ctlmask = PWM_CTL_MSEN1 | PWM_CTL_USEF1 | 128 PWM_CTL_POLA1 | PWM_CTL_SBIT1 | 129 PWM_CTL_RPTL1 | PWM_CTL_MODE1 | 130 PWM_CTL_PWEN1; 131 sc->sc_channels[0].stamask = PWM_STA_STA1; 132 sc->sc_channels[0].gapomask = PWM_STA_GAPO1; 133 sc->sc_channels[0].rng = PWM_RNG1; 134 sc->sc_channels[0].dat = PWM_DAT1; 135 136 sc->sc_channels[1].sc = sc; 137 sc->sc_channels[1].ctlmask = PWM_CTL_MSEN2 | PWM_CTL_USEF2 | 138 PWM_CTL_POLA2 | PWM_CTL_SBIT2 | 139 PWM_CTL_RPTL2 | PWM_CTL_MODE2 | 140 PWM_CTL_PWEN2; 141 sc->sc_channels[1].stamask = PWM_STA_STA2; 142 sc->sc_channels[1].gapomask = PWM_STA_GAPO2; 143 sc->sc_channels[1].rng = PWM_RNG2; 144 sc->sc_channels[1].dat = PWM_DAT2; 145 146 /* The PWM hardware can be used by vcaudio if the 147 * analog output is selected 148 */ 149 sc->sc_channels[0].inuse = false; 150 sc->sc_channels[1].inuse = false; 151 152 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); 153 154 /* Success! */ 155 156 fail0: return; 157 } 158 159 struct bcm_pwm_channel * 160 bcm_pwm_alloc(int num) 161 { 162 struct bcm2835pwm_softc *sc; 163 device_t dev; 164 struct bcm_pwm_channel *pwm; 165 166 dev = device_find_by_driver_unit("bcmpwm", 0); 167 if (dev == NULL) 168 return NULL; 169 sc = device_private(dev); 170 171 if (num < 0 || num >= __arraycount(sc->sc_channels)) 172 return NULL; 173 174 pwm = &sc->sc_channels[num]; 175 176 mutex_enter(&sc->sc_lock); 177 if (pwm->inuse) 178 pwm = NULL; 179 else 180 pwm->inuse = true; 181 mutex_exit(&sc->sc_lock); 182 183 if (pwm) { 184 pwm->datsave = PWM_READ(pwm->sc, pwm->dat); 185 pwm->ctlsave = PWM_READ(pwm->sc, PWM_CTL); 186 pwm->rngsave = PWM_READ(pwm->sc, pwm->rng); 187 } 188 189 return pwm; 190 } 191 192 void 193 bcm_pwm_free(struct bcm_pwm_channel *pwm) 194 { 195 struct bcm2835pwm_softc *sc = pwm->sc; 196 197 KASSERT(pwm->inuse); 198 199 PWM_WRITE(pwm->sc, pwm->rng, pwm->rngsave); 200 PWM_WRITE(pwm->sc, PWM_CTL, pwm->ctlsave & ~PWM_CTL_WRITEZERO); 201 PWM_WRITE(pwm->sc, pwm->dat, pwm->datsave); 202 203 mutex_enter(&sc->sc_lock); 204 pwm->inuse = false; 205 mutex_exit(&sc->sc_lock); 206 } 207 208 void 209 bcm_pwm_control(struct bcm_pwm_channel *pwm, uint32_t ctl, uint32_t rng) 210 { 211 struct bcm2835pwm_softc *sc = pwm->sc; 212 uint32_t w; 213 214 KASSERT(pwm->inuse); 215 216 /* set control bits like for channel 0 217 * there are generic bit definitions that the caller can use. 218 */ 219 w = PWM_READ(pwm->sc, PWM_CTL); 220 ctl = (w & ~pwm->ctlmask) | __SHIFTIN(ctl, pwm->ctlmask); 221 222 /* when FIFO usage gets enabled but wasn't clear the FIFO */ 223 if ((w & (PWM_CTL_USEF1|PWM_CTL_USEF2)) == 0 && 224 (ctl & (PWM_CTL_USEF1|PWM_CTL_USEF2)) != 0) 225 ctl |= PWM_CTL_CLRF1; 226 227 PWM_WRITE(sc, pwm->rng, rng); 228 PWM_WRITE(sc, PWM_CTL, ctl & ~PWM_CTL_WRITEZERO); 229 } 230 231 uint32_t 232 bcm_pwm_status(struct bcm_pwm_channel *pwm) 233 { 234 uint32_t w; 235 uint32_t common = PWM_STA_BERR | PWM_STA_RERR1 | 236 PWM_STA_WERR1 | PWM_STA_EMPT1 | PWM_STA_FULL1; 237 238 /* return status bits like for channel 0 239 * there are generic bit definitions that the caller can use. 240 * 241 * The BERR bit is returned for both channels. 242 */ 243 w = PWM_READ(pwm->sc, PWM_STA); 244 PWM_WRITE(pwm->sc, PWM_STA, w & 245 (pwm->stamask | pwm->gapomask | common)); 246 247 w = __SHIFTIN(__SHIFTOUT(w, pwm->stamask), PWM_STA_STA) 248 | __SHIFTIN(__SHIFTOUT(w, pwm->gapomask), PWM_STA_GAPO) 249 | (w & common); 250 251 return w; 252 } 253 254 static int 255 bcmpwm_wait(struct bcm2835pwm_softc *sc) 256 { 257 int i; 258 uint32_t s; 259 260 for (i=0; i<1000; ++i) { 261 s = PWM_READ(sc, PWM_STA); 262 if ((s & PWM_STA_FULL1) == 0) 263 break; 264 delay(1); 265 } 266 if (i >= 1000) 267 return -1; 268 269 return 0; 270 } 271 272 int 273 bcm_pwm_write(struct bcm_pwm_channel *pwm, uint32_t *data1, uint32_t *data2, 274 int len) 275 { 276 struct bcm2835pwm_softc *sc = pwm->sc; 277 int n; 278 uint32_t r; 279 bool even = false; 280 281 KASSERT(pwm->inuse); 282 283 n = len; 284 while (n > 0) { 285 if (bcmpwm_wait(sc)) 286 break; 287 r = even ? *data2++ : *data1++; 288 PWM_WRITE(sc, PWM_FIFO, r); 289 if (data2 != NULL) 290 even = !even; 291 --n; 292 } 293 294 return len - n; 295 } 296 297 void 298 bcm_pwm_set(struct bcm_pwm_channel *pwm, uint32_t w) 299 { 300 struct bcm2835pwm_softc *sc = pwm->sc; 301 302 PWM_WRITE(sc, pwm->dat, w); 303 } 304 305 int 306 bcm_pwm_flush(struct bcm_pwm_channel *pwm) 307 { 308 struct bcm2835pwm_softc *sc = pwm->sc; 309 310 return bcmpwm_wait(sc) ? EIO : 0; 311 } 312 313 void 314 bcm_pwm_dma_enable(struct bcm_pwm_channel *pwm, bool enable) 315 { 316 struct bcm2835pwm_softc *sc = pwm->sc; 317 uint32_t w; 318 319 #if 0 320 w = PWM_READ(sc, PWM_DMAC); 321 if (enable) 322 w |= PWM_DMAC_ENAB; 323 else 324 w &= ~PWM_DMAC_ENAB; 325 #else 326 w = (enable ? PWM_DMAC_ENAB : 0) 327 | __SHIFTIN(7, PWM_DMAC_PANIC) 328 | __SHIFTIN(7, PWM_DMAC_DREQ); 329 #endif 330 PWM_WRITE(sc, PWM_DMAC, w & ~PWM_DMAC_WRITEZERO); 331 } 332 333 uint32_t 334 bcm_pwm_dma_address(struct bcm_pwm_channel *pwm) 335 { 336 struct bcm2835pwm_softc *sc = pwm->sc; 337 338 return sc->sc_iob + PWM_FIFO; 339 } 340 341