From owner-svn-src-stable-10@FreeBSD.ORG Wed Aug 20 17:57:23 2014 Return-Path: Delivered-To: svn-src-stable-10@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 93275B2A; Wed, 20 Aug 2014 17:57:23 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 7DAC43DCA; Wed, 20 Aug 2014 17:57:23 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.9/8.14.9) with ESMTP id s7KHvN1H033667; Wed, 20 Aug 2014 17:57:23 GMT (envelope-from loos@FreeBSD.org) Received: (from loos@localhost) by svn.freebsd.org (8.14.9/8.14.9/Submit) id s7KHvNDJ033666; Wed, 20 Aug 2014 17:57:23 GMT (envelope-from loos@FreeBSD.org) Message-Id: <201408201757.s7KHvNDJ033666@svn.freebsd.org> X-Authentication-Warning: svn.freebsd.org: loos set sender to loos@FreeBSD.org using -f From: Luiz Otavio O Souza Date: Wed, 20 Aug 2014 17:57:23 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org Subject: svn commit: r270237 - stable/10/sys/arm/ti/am335x X-SVN-Group: stable-10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-stable-10@freebsd.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: SVN commit messages for only the 10-stable src tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 20 Aug 2014 17:57:23 -0000 Author: loos Date: Wed Aug 20 17:57:23 2014 New Revision: 270237 URL: http://svnweb.freebsd.org/changeset/base/270237 Log: MFC r266937: Export two new settings for the AM335x PWM, the clock prescaler (clkdiv) and the actual PWM frequency. Enforce the maximum value for the period sysctl. The frequency systcl now allows the direct setting of the PWM frequency (it will try to find the better clkdiv and period for a given frequency, i.e. the ones that will give the better PWM resolution). This allows the use lower frequencies on the PWM. Without changing the clock prescaler the minimum PWM frequency was 1.52kHz. PWM frequencies checked with an osciloscope. PWM output tested with some R/C servos at 50Hz. Modified: stable/10/sys/arm/ti/am335x/am335x_pwm.c Directory Properties: stable/10/ (props changed) Modified: stable/10/sys/arm/ti/am335x/am335x_pwm.c ============================================================================== --- stable/10/sys/arm/ti/am335x/am335x_pwm.c Wed Aug 20 17:39:53 2014 (r270236) +++ stable/10/sys/arm/ti/am335x/am335x_pwm.c Wed Aug 20 17:57:23 2014 (r270237) @@ -29,10 +29,11 @@ __FBSDID("$FreeBSD$"); #include #include -#include -#include #include +#include +#include #include +#include #include #include #include @@ -53,12 +54,13 @@ __FBSDID("$FreeBSD$"); /* In ticks */ #define DEFAULT_PWM_PERIOD 1000 +#define PWM_CLOCK 100000000UL #define PWM_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define PWM_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define PWM_LOCK_INIT(_sc) mtx_init(&(_sc)->sc_mtx, \ device_get_nameunit(_sc->sc_dev), "am335x_pwm softc", MTX_DEF) -#define PWM_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx); +#define PWM_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx) static struct resource_spec am335x_pwm_mem_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* PWMSS */ @@ -170,6 +172,8 @@ static struct resource_spec am335x_pwm_m static device_probe_t am335x_pwm_probe; static device_attach_t am335x_pwm_attach; static device_detach_t am335x_pwm_detach; + +static int am335x_pwm_clkdiv[8] = { 1, 2, 4, 8, 16, 32, 64, 128 }; struct am335x_pwm_softc { device_t sc_dev; @@ -177,6 +181,10 @@ struct am335x_pwm_softc { struct resource *sc_mem_res[4]; int sc_id; /* sysctl for configuration */ + int sc_pwm_clkdiv; + int sc_pwm_freq; + struct sysctl_oid *sc_clkdiv_oid; + struct sysctl_oid *sc_freq_oid; struct sysctl_oid *sc_period_oid; struct sysctl_oid *sc_chanA_oid; struct sysctl_oid *sc_chanB_oid; @@ -241,6 +249,98 @@ am335x_pwm_config_ecas(int unit, int per return (0); } +static void +am335x_pwm_freq(struct am335x_pwm_softc *sc) +{ + int clkdiv; + + clkdiv = am335x_pwm_clkdiv[sc->sc_pwm_clkdiv]; + sc->sc_pwm_freq = PWM_CLOCK / (1 * clkdiv) / sc->sc_pwm_period; +} + +static int +am335x_pwm_sysctl_freq(SYSCTL_HANDLER_ARGS) +{ + int clkdiv, error, freq, i, period; + struct am335x_pwm_softc *sc; + uint32_t reg; + + sc = (struct am335x_pwm_softc *)arg1; + + PWM_LOCK(sc); + freq = sc->sc_pwm_freq; + PWM_UNLOCK(sc); + + error = sysctl_handle_int(oidp, &freq, sizeof(freq), req); + if (error != 0 || req->newptr == NULL) + return (error); + + if (freq > PWM_CLOCK) + freq = PWM_CLOCK; + + PWM_LOCK(sc); + if (freq != sc->sc_pwm_freq) { + for (i = nitems(am335x_pwm_clkdiv) - 1; i >= 0; i--) { + clkdiv = am335x_pwm_clkdiv[i]; + period = PWM_CLOCK / clkdiv / freq; + if (period > USHRT_MAX) + break; + sc->sc_pwm_clkdiv = i; + sc->sc_pwm_period = period; + } + /* Reset the duty cycle settings. */ + sc->sc_pwm_dutyA = 0; + sc->sc_pwm_dutyB = 0; + EPWM_WRITE2(sc, EPWM_CMPA, sc->sc_pwm_dutyA); + EPWM_WRITE2(sc, EPWM_CMPB, sc->sc_pwm_dutyB); + /* Update the clkdiv settings. */ + reg = EPWM_READ2(sc, EPWM_TBCTL); + reg &= ~TBCTL_CLKDIV_MASK; + reg |= TBCTL_CLKDIV(sc->sc_pwm_clkdiv); + EPWM_WRITE2(sc, EPWM_TBCTL, reg); + /* Update the period settings. */ + EPWM_WRITE2(sc, EPWM_TBPRD, sc->sc_pwm_period - 1); + am335x_pwm_freq(sc); + } + PWM_UNLOCK(sc); + + return (0); +} + +static int +am335x_pwm_sysctl_clkdiv(SYSCTL_HANDLER_ARGS) +{ + int error, i, clkdiv; + struct am335x_pwm_softc *sc; + uint32_t reg; + + sc = (struct am335x_pwm_softc *)arg1; + + PWM_LOCK(sc); + clkdiv = am335x_pwm_clkdiv[sc->sc_pwm_clkdiv]; + PWM_UNLOCK(sc); + + error = sysctl_handle_int(oidp, &clkdiv, sizeof(clkdiv), req); + if (error != 0 || req->newptr == NULL) + return (error); + + PWM_LOCK(sc); + if (clkdiv != am335x_pwm_clkdiv[sc->sc_pwm_clkdiv]) { + for (i = 0; i < nitems(am335x_pwm_clkdiv); i++) + if (clkdiv >= am335x_pwm_clkdiv[i]) + sc->sc_pwm_clkdiv = i; + + reg = EPWM_READ2(sc, EPWM_TBCTL); + reg &= ~TBCTL_CLKDIV_MASK; + reg |= TBCTL_CLKDIV(sc->sc_pwm_clkdiv); + EPWM_WRITE2(sc, EPWM_TBCTL, reg); + am335x_pwm_freq(sc); + } + PWM_UNLOCK(sc); + + return (0); +} + static int am335x_pwm_sysctl_duty(SYSCTL_HANDLER_ARGS) { @@ -292,15 +392,19 @@ am335x_pwm_sysctl_period(SYSCTL_HANDLER_ if (period < 1) return (EINVAL); - if ((period < sc->sc_pwm_dutyA) || (period < sc->sc_pwm_dutyB)) { - device_printf(sc->sc_dev, "Period can't be less then duty cycle\n"); - return (EINVAL); - } - + if (period > USHRT_MAX) + period = USHRT_MAX; PWM_LOCK(sc); + /* Reset the duty cycle settings. */ + sc->sc_pwm_dutyA = 0; + sc->sc_pwm_dutyB = 0; + EPWM_WRITE2(sc, EPWM_CMPA, sc->sc_pwm_dutyA); + EPWM_WRITE2(sc, EPWM_CMPB, sc->sc_pwm_dutyB); + /* Update the period settings. */ sc->sc_pwm_period = period; EPWM_WRITE2(sc, EPWM_TBPRD, period - 1); + am335x_pwm_freq(sc); PWM_UNLOCK(sc); return (error); @@ -360,6 +464,14 @@ am335x_pwm_attach(device_t dev) ctx = device_get_sysctl_ctx(sc->sc_dev); tree = device_get_sysctl_tree(sc->sc_dev); + sc->sc_clkdiv_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "clkdiv", CTLTYPE_INT | CTLFLAG_RW, sc, 0, + am335x_pwm_sysctl_clkdiv, "I", "PWM clock prescaler"); + + sc->sc_freq_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "freq", CTLTYPE_INT | CTLFLAG_RW, sc, 0, + am335x_pwm_sysctl_freq, "I", "PWM frequency"); + sc->sc_period_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "period", CTLTYPE_INT | CTLFLAG_RW, sc, 0, am335x_pwm_sysctl_period, "I", "PWM period"); @@ -372,7 +484,6 @@ am335x_pwm_attach(device_t dev) "dutyB", CTLTYPE_INT | CTLFLAG_RW, sc, 0, am335x_pwm_sysctl_duty, "I", "Channel B duty cycles"); - /* CONFIGURE EPWM1 */ reg = EPWM_READ2(sc, EPWM_TBCTL); reg &= ~(TBCTL_CLKDIV_MASK | TBCTL_HSPCLKDIV_MASK); @@ -381,6 +492,7 @@ am335x_pwm_attach(device_t dev) sc->sc_pwm_period = DEFAULT_PWM_PERIOD; sc->sc_pwm_dutyA = 0; sc->sc_pwm_dutyB = 0; + am335x_pwm_freq(sc); EPWM_WRITE2(sc, EPWM_TBPRD, sc->sc_pwm_period - 1); EPWM_WRITE2(sc, EPWM_CMPA, sc->sc_pwm_dutyA);