1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2017 Poul-Henning Kamp <phk@FreeBSD.org> 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 AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, 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 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include <sys/param.h> 34 #include <sys/systm.h> 35 #include <sys/bus.h> 36 37 #include <sys/kernel.h> 38 #include <sys/module.h> 39 #include <sys/rman.h> 40 #include <sys/lock.h> 41 #include <sys/sysctl.h> 42 43 #include <machine/bus.h> 44 #include <machine/resource.h> 45 46 #include <dev/ofw/ofw_bus.h> 47 #include <dev/ofw/ofw_bus_subr.h> 48 49 #include <arm/broadcom/bcm2835/bcm2835_gpio.h> 50 #include <arm/broadcom/bcm2835/bcm2835_clkman.h> 51 52 static struct ofw_compat_data compat_data[] = { 53 {"broadcom,bcm2835-pwm", 1}, 54 {"brcm,bcm2835-pwm", 1}, 55 {NULL, 0} 56 }; 57 58 struct bcm_pwm_softc { 59 device_t sc_dev; 60 61 struct resource * sc_mem_res; 62 bus_space_tag_t sc_m_bst; 63 bus_space_handle_t sc_m_bsh; 64 65 device_t clkman; 66 67 uint32_t freq; 68 uint32_t period; 69 uint32_t ratio; 70 uint32_t mode; 71 72 }; 73 74 #define BCM_PWM_MEM_WRITE(_sc, _off, _val) \ 75 bus_space_write_4(_sc->sc_m_bst, _sc->sc_m_bsh, _off, _val) 76 #define BCM_PWM_MEM_READ(_sc, _off) \ 77 bus_space_read_4(_sc->sc_m_bst, _sc->sc_m_bsh, _off) 78 #define BCM_PWM_CLK_WRITE(_sc, _off, _val) \ 79 bus_space_write_4(_sc->sc_c_bst, _sc->sc_c_bsh, _off, _val) 80 #define BCM_PWM_CLK_READ(_sc, _off) \ 81 bus_space_read_4(_sc->sc_c_bst, _sc->sc_c_bsh, _off) 82 83 #define W_CTL(_sc, _val) BCM_PWM_MEM_WRITE(_sc, 0x00, _val) 84 #define R_CTL(_sc) BCM_PWM_MEM_READ(_sc, 0x00) 85 #define W_STA(_sc, _val) BCM_PWM_MEM_WRITE(_sc, 0x04, _val) 86 #define R_STA(_sc) BCM_PWM_MEM_READ(_sc, 0x04) 87 #define W_RNG(_sc, _val) BCM_PWM_MEM_WRITE(_sc, 0x10, _val) 88 #define R_RNG(_sc) BCM_PWM_MEM_READ(_sc, 0x10) 89 #define W_DAT(_sc, _val) BCM_PWM_MEM_WRITE(_sc, 0x14, _val) 90 #define R_DAT(_sc) BCM_PWM_MEM_READ(_sc, 0x14) 91 92 static int 93 bcm_pwm_reconf(struct bcm_pwm_softc *sc) 94 { 95 uint32_t u; 96 device_t gpio; 97 98 /* Disable PWM */ 99 W_CTL(sc, 0); 100 101 /* Stop PWM clock */ 102 (void)bcm2835_clkman_set_frequency(sc->clkman, BCM_PWM_CLKSRC, 0); 103 104 if (sc->mode == 0) 105 return (0); 106 107 /* Ask GPIO0 to set ALT0 for pin 12 */ 108 gpio = devclass_get_device(devclass_find("gpio"), 0); 109 if (!gpio) { 110 device_printf(sc->sc_dev, "cannot find gpio0\n"); 111 return (ENXIO); 112 } 113 bcm_gpio_set_alternate(gpio, 12, BCM_GPIO_ALT0); 114 115 u = bcm2835_clkman_set_frequency(sc->clkman, BCM_PWM_CLKSRC, sc->freq); 116 if (u == 0) 117 return (EINVAL); 118 sc->freq = u; 119 120 /* Config PWM */ 121 W_RNG(sc, sc->period); 122 if (sc->ratio > sc->period) 123 sc->ratio = sc->period; 124 W_DAT(sc, sc->ratio); 125 126 /* Start PWM */ 127 if (sc->mode == 1) 128 W_CTL(sc, 0x81); 129 else 130 W_CTL(sc, 0x1); 131 132 return (0); 133 } 134 135 static int 136 bcm_pwm_pwm_freq_proc(SYSCTL_HANDLER_ARGS) 137 { 138 struct bcm_pwm_softc *sc; 139 uint32_t r; 140 int error; 141 142 sc = (struct bcm_pwm_softc *)arg1; 143 if (sc->mode == 1) 144 r = sc->freq / sc->period; 145 else 146 r = 0; 147 error = sysctl_handle_int(oidp, &r, sizeof(r), req); 148 return (error); 149 } 150 151 152 static int 153 bcm_pwm_mode_proc(SYSCTL_HANDLER_ARGS) 154 { 155 struct bcm_pwm_softc *sc; 156 uint32_t r; 157 int error; 158 159 sc = (struct bcm_pwm_softc *)arg1; 160 r = sc->mode; 161 error = sysctl_handle_int(oidp, &r, sizeof(r), req); 162 if (error != 0 || req->newptr == NULL) 163 return (error); 164 if (r > 2) 165 return (EINVAL); 166 sc->mode = r; 167 return (bcm_pwm_reconf(sc)); 168 } 169 170 static int 171 bcm_pwm_freq_proc(SYSCTL_HANDLER_ARGS) 172 { 173 struct bcm_pwm_softc *sc; 174 uint32_t r; 175 int error; 176 177 sc = (struct bcm_pwm_softc *)arg1; 178 r = sc->freq; 179 error = sysctl_handle_int(oidp, &r, sizeof(r), req); 180 if (error != 0 || req->newptr == NULL) 181 return (error); 182 if (r > 125000000) 183 return (EINVAL); 184 sc->freq = r; 185 return (bcm_pwm_reconf(sc)); 186 } 187 188 static int 189 bcm_pwm_period_proc(SYSCTL_HANDLER_ARGS) 190 { 191 struct bcm_pwm_softc *sc; 192 int error; 193 194 sc = (struct bcm_pwm_softc *)arg1; 195 error = sysctl_handle_int(oidp, &sc->period, sizeof(sc->period), req); 196 if (error != 0 || req->newptr == NULL) 197 return (error); 198 return (bcm_pwm_reconf(sc)); 199 } 200 201 static int 202 bcm_pwm_ratio_proc(SYSCTL_HANDLER_ARGS) 203 { 204 struct bcm_pwm_softc *sc; 205 uint32_t r; 206 int error; 207 208 sc = (struct bcm_pwm_softc *)arg1; 209 r = sc->ratio; 210 error = sysctl_handle_int(oidp, &r, sizeof(r), req); 211 if (error != 0 || req->newptr == NULL) 212 return (error); 213 if (r > sc->period) // XXX >= ? 214 return (EINVAL); 215 sc->ratio = r; 216 BCM_PWM_MEM_WRITE(sc, 0x14, sc->ratio); 217 return (0); 218 } 219 220 static int 221 bcm_pwm_reg_proc(SYSCTL_HANDLER_ARGS) 222 { 223 struct bcm_pwm_softc *sc; 224 uint32_t reg; 225 int error; 226 227 sc = (struct bcm_pwm_softc *)arg1; 228 reg = BCM_PWM_MEM_READ(sc, arg2 & 0xff); 229 230 error = sysctl_handle_int(oidp, ®, sizeof(reg), req); 231 if (error != 0 || req->newptr == NULL) 232 return (error); 233 234 BCM_PWM_MEM_WRITE(sc, arg2, reg); 235 return (0); 236 } 237 238 static void 239 bcm_pwm_sysctl_init(struct bcm_pwm_softc *sc) 240 { 241 struct sysctl_ctx_list *ctx; 242 struct sysctl_oid *tree_node; 243 struct sysctl_oid_list *tree; 244 245 /* 246 * Add system sysctl tree/handlers. 247 */ 248 ctx = device_get_sysctl_ctx(sc->sc_dev); 249 tree_node = device_get_sysctl_tree(sc->sc_dev); 250 tree = SYSCTL_CHILDREN(tree_node); 251 if (bootverbose) { 252 #define RR(x,y) \ 253 SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, y, \ 254 CTLFLAG_RW | CTLTYPE_UINT, sc, 0x##x, \ 255 bcm_pwm_reg_proc, "IU", "Register 0x" #x " " y); 256 257 RR(24, "DAT2") 258 RR(20, "RNG2") 259 RR(18, "FIF1") 260 RR(14, "DAT1") 261 RR(10, "RNG1") 262 RR(08, "DMAC") 263 RR(04, "STA") 264 RR(00, "CTL") 265 #undef RR 266 } 267 268 SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "pwm_freq", 269 CTLFLAG_RD | CTLTYPE_UINT, sc, 0, 270 bcm_pwm_pwm_freq_proc, "IU", "PWM frequency (Hz)"); 271 SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "period", 272 CTLFLAG_RW | CTLTYPE_UINT, sc, 0, 273 bcm_pwm_period_proc, "IU", "PWM period (#clocks)"); 274 SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "ratio", 275 CTLFLAG_RW | CTLTYPE_UINT, sc, 0, 276 bcm_pwm_ratio_proc, "IU", "PWM ratio (0...period)"); 277 SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "freq", 278 CTLFLAG_RW | CTLTYPE_UINT, sc, 0, 279 bcm_pwm_freq_proc, "IU", "PWM clock (Hz)"); 280 SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "mode", 281 CTLFLAG_RW | CTLTYPE_UINT, sc, 0, 282 bcm_pwm_mode_proc, "IU", "PWM mode (0=off, 1=pwm, 2=dither)"); 283 } 284 285 static int 286 bcm_pwm_probe(device_t dev) 287 { 288 289 #if 0 290 // XXX: default state is disabled in RPI3 DTB, assume for now 291 // XXX: that people want the PWM to work if the KLD this module. 292 if (!ofw_bus_status_okay(dev)) 293 return (ENXIO); 294 #endif 295 296 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 297 return (ENXIO); 298 299 device_set_desc(dev, "BCM2708/2835 PWM controller"); 300 301 return (BUS_PROBE_DEFAULT); 302 } 303 304 static int 305 bcm_pwm_attach(device_t dev) 306 { 307 struct bcm_pwm_softc *sc; 308 int rid; 309 310 if (device_get_unit(dev) != 0) { 311 device_printf(dev, "only one PWM controller supported\n"); 312 return (ENXIO); 313 } 314 315 sc = device_get_softc(dev); 316 sc->sc_dev = dev; 317 318 sc->clkman = devclass_get_device(devclass_find("bcm2835_clkman"), 0); 319 if (sc->clkman == NULL) { 320 device_printf(dev, "cannot find Clock Manager\n"); 321 return (ENXIO); 322 } 323 324 rid = 0; 325 sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 326 RF_ACTIVE); 327 if (!sc->sc_mem_res) { 328 device_printf(dev, "cannot allocate memory window\n"); 329 return (ENXIO); 330 } 331 332 sc->sc_m_bst = rman_get_bustag(sc->sc_mem_res); 333 sc->sc_m_bsh = rman_get_bushandle(sc->sc_mem_res); 334 335 /* Add sysctl nodes. */ 336 bcm_pwm_sysctl_init(sc); 337 338 sc->freq = 125000000; 339 sc->period = 10000; 340 sc->ratio = 2500; 341 342 343 return (bus_generic_attach(dev)); 344 } 345 346 static int 347 bcm_pwm_detach(device_t dev) 348 { 349 struct bcm_pwm_softc *sc; 350 351 bus_generic_detach(dev); 352 353 sc = device_get_softc(dev); 354 sc->mode = 0; 355 (void)bcm_pwm_reconf(sc); 356 if (sc->sc_mem_res) 357 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); 358 359 return (0); 360 } 361 362 static phandle_t 363 bcm_pwm_get_node(device_t bus, device_t dev) 364 { 365 366 return (ofw_bus_get_node(bus)); 367 } 368 369 370 static device_method_t bcm_pwm_methods[] = { 371 /* Device interface */ 372 DEVMETHOD(device_probe, bcm_pwm_probe), 373 DEVMETHOD(device_attach, bcm_pwm_attach), 374 DEVMETHOD(device_detach, bcm_pwm_detach), 375 DEVMETHOD(ofw_bus_get_node, bcm_pwm_get_node), 376 377 DEVMETHOD_END 378 }; 379 380 static devclass_t bcm_pwm_devclass; 381 382 static driver_t bcm_pwm_driver = { 383 "pwm", 384 bcm_pwm_methods, 385 sizeof(struct bcm_pwm_softc), 386 }; 387 388 DRIVER_MODULE(bcm2835_pwm, simplebus, bcm_pwm_driver, bcm_pwm_devclass, 0, 0); 389 MODULE_DEPEND(bcm2835_pwm, bcm2835_clkman, 1, 1, 1); 390