Path: blob/main/sys/arm/broadcom/bcm2835/bcm2835_pwm.c
39566 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2017 Poul-Henning Kamp <[email protected]>4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND16* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE19* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS21* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF25* SUCH DAMAGE.26*27*/2829#include <sys/param.h>30#include <sys/systm.h>31#include <sys/bus.h>3233#include <sys/kernel.h>34#include <sys/module.h>35#include <sys/rman.h>36#include <sys/lock.h>37#include <sys/sysctl.h>3839#include <machine/bus.h>40#include <machine/resource.h>4142#include <dev/ofw/ofw_bus.h>43#include <dev/ofw/ofw_bus_subr.h>4445#include <arm/broadcom/bcm2835/bcm2835_clkman.h>4647static struct ofw_compat_data compat_data[] = {48{"broadcom,bcm2835-pwm", 1},49{"brcm,bcm2835-pwm", 1},50{NULL, 0}51};5253struct bcm_pwm_softc {54device_t sc_dev;5556struct resource * sc_mem_res;57bus_space_tag_t sc_m_bst;58bus_space_handle_t sc_m_bsh;5960device_t clkman;6162uint32_t freq; /* shared between channels 1 and 2 */63uint32_t period; /* channel 1 */64uint32_t ratio;65uint32_t mode;66uint32_t period2; /* channel 2 */67uint32_t ratio2;68uint32_t mode2;69};7071#define BCM_PWM_MEM_WRITE(_sc, _off, _val) \72bus_space_write_4(_sc->sc_m_bst, _sc->sc_m_bsh, _off, _val)73#define BCM_PWM_MEM_READ(_sc, _off) \74bus_space_read_4(_sc->sc_m_bst, _sc->sc_m_bsh, _off)75#define BCM_PWM_CLK_WRITE(_sc, _off, _val) \76bus_space_write_4(_sc->sc_c_bst, _sc->sc_c_bsh, _off, _val)77#define BCM_PWM_CLK_READ(_sc, _off) \78bus_space_read_4(_sc->sc_c_bst, _sc->sc_c_bsh, _off)7980#define W_CTL(_sc, _val) BCM_PWM_MEM_WRITE(_sc, 0x00, _val)81#define R_CTL(_sc) BCM_PWM_MEM_READ(_sc, 0x00)82#define W_STA(_sc, _val) BCM_PWM_MEM_WRITE(_sc, 0x04, _val)83#define R_STA(_sc) BCM_PWM_MEM_READ(_sc, 0x04)84#define W_RNG(_sc, _val) BCM_PWM_MEM_WRITE(_sc, 0x10, _val)85#define R_RNG(_sc) BCM_PWM_MEM_READ(_sc, 0x10)86#define W_DAT(_sc, _val) BCM_PWM_MEM_WRITE(_sc, 0x14, _val)87#define R_DAT(_sc) BCM_PWM_MEM_READ(_sc, 0x14)88#define W_RNG2(_sc, _val) BCM_PWM_MEM_WRITE(_sc, 0x20, _val)89#define R_RNG2(_sc) BCM_PWM_MEM_READ(_sc, 0x20)90#define W_DAT2(_sc, _val) BCM_PWM_MEM_WRITE(_sc, 0x24, _val)91#define R_DAT2(_sc) BCM_PWM_MEM_READ(_sc, 0x24)9293static int94bcm_pwm_reconf(struct bcm_pwm_softc *sc)95{96uint32_t u, ctlr;9798/* Disable PWM */99W_CTL(sc, 0);100101/* Stop PWM clock */102(void)bcm2835_clkman_set_frequency(sc->clkman, BCM_PWM_CLKSRC, 0);103104ctlr = 0; /* pre-assign zero, enable bits, write to CTL at end */105106if (sc->mode == 0 && sc->mode2 == 0) /* both modes are zero */107return 0; /* device is now off - return */108109/* set the PWM clock frequency */110/* TODO: should I only do this if it changes and not stop it first? */111u = bcm2835_clkman_set_frequency(sc->clkman, BCM_PWM_CLKSRC, sc->freq);112if (u == 0)113return (EINVAL);114sc->freq = u;115116/* control register CTL bits:117* (from BCM2835 ARM Peripherals manual, section 9.6)118*119* 15 MSEN2 chan 2 M/S enable; 0 for PWM algo, 1 for M/S transmission120* 14 unused; always reads as 0121* 13 USEF2 chan 2 use FIFO (0 uses data; 1 uses FIFO)122* 12 POLA2 chan 2 invert polarity (0 normal, 1 inverted polarity)123* 11 SBIT2 chan 2 'Silence' bit (when not transmitting data)124* 10 RPTL2 chan 2 FIFO repeat last data (1 repeats, 0 interrupts)125* 9 MODE2 chan 2 PWM/Serializer mode (0 PWM, 1 Serializer)126* 8 PWEN2 chan 2 enable (0 disable, 1 enable)127* 7 MSEN1 chan 1 M/S enable; 0 for PWM algo, 1 for M/S transmission128* 6 CLRF1 chan 1 clear FIFO (set 1 to clear; always reads as 0)129* 5 USEF1 chan 1 use FIFO (0 uses data; 1 uses FIFO)130* 4 POLA1 chan 1 invert polarity (0 normal, 1 inverted polarity)131* 3 SBIT1 chan 1 'Silence' bit (when not transmitting data)132* 2 RTPL1 chan 1 FIFO repeat last data (1 repeats, 0 interrupts)133* 1 MODE1 chan 1 PWM/Serializer mode (0 PWM, 1 Serializer)134* 0 PWMEN1 chan 1 enable (0 disable, 1 enable)135*136* Notes on M/S enable: when this bit is '1', a simple M/S ratio is used. In short,137* the value of 'ratio' is the number of 'on' bits, and the total length of the data is138* defined by 'period'. So if 'ratio' is 2500 and 'period' is 10000, then the output139* remains 'on' for 2500 clocks, and goes 'off' for the remaining 7500 clocks.140* When the M/S enable is '0', a more complicated algorithm effectively 'dithers' the141* pulses in order to obtain the desired ratio. For details, see section 9.3 of the142* BCM2835 ARM Peripherals manual.143*/144145if (sc->mode != 0) {146/* Config PWM Channel 1 */147W_RNG(sc, sc->period);148if (sc->ratio > sc->period)149sc->ratio = sc->period;150W_DAT(sc, sc->ratio);151152/* Start PWM Channel 1 */153if (sc->mode == 1)154ctlr |= 0x81; /* chan 1 enable + chan 1 M/S enable */155else156ctlr |= 0x1; /* chan 1 enable */157}158159if (sc->mode2 != 0) {160/* Config PWM Channel 2 */161W_RNG2(sc, sc->period2);162if (sc->ratio2 > sc->period2)163sc->ratio2 = sc->period2;164W_DAT2(sc, sc->ratio2);165166/* Start PWM Channel 2 */167if (sc->mode2 == 1)168ctlr |= 0x8100; /* chan 2 enable + chan 2 M/S enable */169else170ctlr |= 0x100; /* chan 2 enable */171}172173/* write CTL register with updated value */174W_CTL(sc, ctlr);175176return (0);177}178179static int180bcm_pwm_pwm_freq_proc(SYSCTL_HANDLER_ARGS)181{182struct bcm_pwm_softc *sc;183uint32_t r;184int error;185186sc = (struct bcm_pwm_softc *)arg1;187if (sc->mode == 1)188r = sc->freq / sc->period;189else190r = 0;191error = sysctl_handle_int(oidp, &r, sizeof(r), req);192return (error);193}194195static int196bcm_pwm_mode_proc(SYSCTL_HANDLER_ARGS)197{198struct bcm_pwm_softc *sc;199uint32_t r;200int error;201202sc = (struct bcm_pwm_softc *)arg1;203r = sc->mode;204error = sysctl_handle_int(oidp, &r, sizeof(r), req);205if (error != 0 || req->newptr == NULL)206return (error);207if (r > 2)208return (EINVAL);209sc->mode = r;210return (bcm_pwm_reconf(sc));211}212213static int214bcm_pwm_freq_proc(SYSCTL_HANDLER_ARGS)215{216struct bcm_pwm_softc *sc;217uint32_t r;218int error;219220sc = (struct bcm_pwm_softc *)arg1;221r = sc->freq;222error = sysctl_handle_int(oidp, &r, sizeof(r), req);223if (error != 0 || req->newptr == NULL)224return (error);225if (r > 125000000)226return (EINVAL);227sc->freq = r;228return (bcm_pwm_reconf(sc));229}230231static int232bcm_pwm_period_proc(SYSCTL_HANDLER_ARGS)233{234struct bcm_pwm_softc *sc;235int error;236237sc = (struct bcm_pwm_softc *)arg1;238error = sysctl_handle_int(oidp, &sc->period, sizeof(sc->period), req);239if (error != 0 || req->newptr == NULL)240return (error);241return (bcm_pwm_reconf(sc));242}243244static int245bcm_pwm_ratio_proc(SYSCTL_HANDLER_ARGS)246{247struct bcm_pwm_softc *sc;248uint32_t r;249int error;250251sc = (struct bcm_pwm_softc *)arg1;252r = sc->ratio;253error = sysctl_handle_int(oidp, &r, sizeof(r), req);254if (error != 0 || req->newptr == NULL)255return (error);256if (r > sc->period) // XXX >= ?257return (EINVAL);258sc->ratio = r;259W_DAT(sc, sc->ratio);260return (0);261}262263static int264bcm_pwm_pwm_freq2_proc(SYSCTL_HANDLER_ARGS)265{266struct bcm_pwm_softc *sc;267uint32_t r;268int error;269270sc = (struct bcm_pwm_softc *)arg1;271if (sc->mode2 == 1)272r = sc->freq / sc->period2;273else274r = 0;275error = sysctl_handle_int(oidp, &r, sizeof(r), req);276return (error);277}278279static int280bcm_pwm_mode2_proc(SYSCTL_HANDLER_ARGS)281{282struct bcm_pwm_softc *sc;283uint32_t r;284int error;285286sc = (struct bcm_pwm_softc *)arg1;287r = sc->mode2;288error = sysctl_handle_int(oidp, &r, sizeof(r), req);289if (error != 0 || req->newptr == NULL)290return (error);291if (r > 2)292return (EINVAL);293sc->mode2 = r;294return (bcm_pwm_reconf(sc));295}296297static int298bcm_pwm_period2_proc(SYSCTL_HANDLER_ARGS)299{300struct bcm_pwm_softc *sc;301int error;302303sc = (struct bcm_pwm_softc *)arg1;304error = sysctl_handle_int(oidp, &sc->period2, sizeof(sc->period2), req);305if (error != 0 || req->newptr == NULL)306return (error);307return (bcm_pwm_reconf(sc));308}309310static int311bcm_pwm_ratio2_proc(SYSCTL_HANDLER_ARGS)312{313struct bcm_pwm_softc *sc;314uint32_t r;315int error;316317sc = (struct bcm_pwm_softc *)arg1;318r = sc->ratio2;319error = sysctl_handle_int(oidp, &r, sizeof(r), req);320if (error != 0 || req->newptr == NULL)321return (error);322if (r > sc->period2) // XXX >= ?323return (EINVAL);324sc->ratio2 = r;325W_DAT(sc, sc->ratio2);326return (0);327}328329static int330bcm_pwm_reg_proc(SYSCTL_HANDLER_ARGS)331{332struct bcm_pwm_softc *sc;333uint32_t reg;334int error;335336sc = (struct bcm_pwm_softc *)arg1;337reg = BCM_PWM_MEM_READ(sc, arg2 & 0xff);338339error = sysctl_handle_int(oidp, ®, sizeof(reg), req);340if (error != 0 || req->newptr == NULL)341return (error);342343BCM_PWM_MEM_WRITE(sc, arg2, reg);344return (0);345}346347static void348bcm_pwm_sysctl_init(struct bcm_pwm_softc *sc)349{350struct sysctl_ctx_list *ctx;351struct sysctl_oid *tree_node;352struct sysctl_oid_list *tree;353354/*355* Add system sysctl tree/handlers.356*/357ctx = device_get_sysctl_ctx(sc->sc_dev);358tree_node = device_get_sysctl_tree(sc->sc_dev);359tree = SYSCTL_CHILDREN(tree_node);360if (bootverbose) {361#define RR(x,y) \362SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, y, \363CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, \364sc, 0x##x, \365bcm_pwm_reg_proc, "IU", "Register 0x" #x " " y);366367RR(24, "DAT2")368RR(20, "RNG2")369RR(18, "FIF1")370RR(14, "DAT1")371RR(10, "RNG1")372RR(08, "DMAC")373RR(04, "STA")374RR(00, "CTL")375#undef RR376}377378SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "pwm_freq",379CTLFLAG_RD | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, 0,380bcm_pwm_pwm_freq_proc, "IU", "PWM frequency ch 1 (Hz)");381SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "period",382CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, 0,383bcm_pwm_period_proc, "IU", "PWM period ch 1 (#clocks)");384SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "ratio",385CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, 0,386bcm_pwm_ratio_proc, "IU", "PWM ratio ch 1 (0...period)");387SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "freq",388CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, 0,389bcm_pwm_freq_proc, "IU", "PWM clock (Hz)");390SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "mode",391CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, 0,392bcm_pwm_mode_proc, "IU", "PWM mode ch 1 (0=off, 1=pwm, 2=dither)");393SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "pwm_freq2",394CTLFLAG_RD | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, 0,395bcm_pwm_pwm_freq2_proc, "IU", "PWM frequency ch 2 (Hz)");396SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "period2",397CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, 0,398bcm_pwm_period2_proc, "IU", "PWM period ch 2 (#clocks)");399SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "ratio2",400CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, 0,401bcm_pwm_ratio2_proc, "IU", "PWM ratio ch 2 (0...period)");402SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "mode2",403CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, 0,404bcm_pwm_mode2_proc, "IU", "PWM mode ch 2 (0=off, 1=pwm, 2=dither)");405}406407static int408bcm_pwm_probe(device_t dev)409{410411if (!ofw_bus_status_okay(dev))412return (ENXIO);413414if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)415return (ENXIO);416417device_set_desc(dev, "BCM2708/2835 PWM controller");418419return (BUS_PROBE_DEFAULT);420}421422static int423bcm_pwm_attach(device_t dev)424{425struct bcm_pwm_softc *sc;426int rid;427428if (device_get_unit(dev) != 0) {429device_printf(dev, "only one PWM controller supported\n");430return (ENXIO);431}432433sc = device_get_softc(dev);434sc->sc_dev = dev;435436sc->clkman = devclass_get_device(devclass_find("bcm2835_clkman"), 0);437if (sc->clkman == NULL) {438device_printf(dev, "cannot find Clock Manager\n");439return (ENXIO);440}441442rid = 0;443sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,444RF_ACTIVE);445if (!sc->sc_mem_res) {446device_printf(dev, "cannot allocate memory window\n");447return (ENXIO);448}449450sc->sc_m_bst = rman_get_bustag(sc->sc_mem_res);451sc->sc_m_bsh = rman_get_bushandle(sc->sc_mem_res);452453/* Add sysctl nodes. */454bcm_pwm_sysctl_init(sc);455456sc->freq = 125000000; /* 125 Mhz */457sc->period = 10000; /* 12.5 khz */458sc->ratio = 2500; /* 25% */459sc->period2 = 10000; /* 12.5 khz */460sc->ratio2 = 2500; /* 25% */461462bus_attach_children(dev);463return (0);464}465466static int467bcm_pwm_detach(device_t dev)468{469struct bcm_pwm_softc *sc;470471bus_generic_detach(dev);472473sc = device_get_softc(dev);474sc->mode = 0;475sc->mode2 = 0;476(void)bcm_pwm_reconf(sc);477if (sc->sc_mem_res)478bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);479480return (0);481}482483static phandle_t484bcm_pwm_get_node(device_t bus, device_t dev)485{486487return (ofw_bus_get_node(bus));488}489490static device_method_t bcm_pwm_methods[] = {491/* Device interface */492DEVMETHOD(device_probe, bcm_pwm_probe),493DEVMETHOD(device_attach, bcm_pwm_attach),494DEVMETHOD(device_detach, bcm_pwm_detach),495DEVMETHOD(ofw_bus_get_node, bcm_pwm_get_node),496497DEVMETHOD_END498};499500static driver_t bcm_pwm_driver = {501"pwm",502bcm_pwm_methods,503sizeof(struct bcm_pwm_softc),504};505506DRIVER_MODULE(bcm2835_pwm, simplebus, bcm_pwm_driver, 0, 0);507MODULE_DEPEND(bcm2835_pwm, bcm2835_clkman, 1, 1, 1);508509510