Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 1 Jun 2014 03:57:58 +0000 (UTC)
From:      Luiz Otavio O Souza <loos@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r266937 - head/sys/arm/ti/am335x
Message-ID:  <201406010357.s513vwts010414@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: loos
Date: Sun Jun  1 03:57:57 2014
New Revision: 266937
URL: http://svnweb.freebsd.org/changeset/base/266937

Log:
  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:
  head/sys/arm/ti/am335x/am335x_pwm.c

Modified: head/sys/arm/ti/am335x/am335x_pwm.c
==============================================================================
--- head/sys/arm/ti/am335x/am335x_pwm.c	Sun Jun  1 03:14:03 2014	(r266936)
+++ head/sys/arm/ti/am335x/am335x_pwm.c	Sun Jun  1 03:57:57 2014	(r266937)
@@ -29,10 +29,11 @@ __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
 #include <sys/systm.h>
-#include <sys/kernel.h>
-#include <sys/module.h>
 #include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
 #include <sys/lock.h>
+#include <sys/module.h>
 #include <sys/mutex.h>
 #include <sys/resource.h>
 #include <sys/rman.h>
@@ -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);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201406010357.s513vwts010414>