Author: loos
Date: Mon Mar 24 20:06:27 2014
New Revision: 263693
URL: http://svnweb.freebsd.org/changeset/base/263693

Log:
  Adds the ADC driver for TI AM3xxx SoC family.
  
  The ADC has a 12bit resolution and its raw output can be read via sysctl(8)
  interface.
  
  The driver allows the setup of ADC clock, samples average and open delay
  (the number of clock cycles to wait before start the conversion).
  
  The TSC_ADC module is set in the general purpose mode (no touchscreen
  support).
  
  Tested on Beaglebone-black.
  
  Written based on AM335x TRM.
  
  Reviewed by:  rpaulo
  Approved by:  adrian (mentor)
  Tested by:    me, Brian J. McGovern, Sulev-Madis Silber (ketas)

Added:
  head/share/man/man4/man4.arm/ti_adc.4   (contents, props changed)
  head/sys/arm/ti/ti_adc.c   (contents, props changed)
  head/sys/arm/ti/ti_adcreg.h   (contents, props changed)
  head/sys/arm/ti/ti_adcvar.h   (contents, props changed)
Modified:
  head/share/man/man4/man4.arm/Makefile
  head/sys/arm/conf/BEAGLEBONE
  head/sys/arm/ti/am335x/am335x_prcm.c
  head/sys/arm/ti/files.ti
  head/sys/arm/ti/ti_prcm.h
  head/sys/boot/fdt/dts/arm/am335x.dtsi

Modified: head/share/man/man4/man4.arm/Makefile
==============================================================================
--- head/share/man/man4/man4.arm/Makefile       Mon Mar 24 19:21:14 2014        
(r263692)
+++ head/share/man/man4/man4.arm/Makefile       Mon Mar 24 20:06:27 2014        
(r263693)
@@ -1,6 +1,9 @@
 # $FreeBSD$
 
-MAN=   mge.4 npe.4 devcfg.4
+MAN=   devcfg.4 \
+       mge.4 \
+       npe.4 \
+       ti_adc.4
 
 MLINKS= mge.4 if_mge.4
 MLINKS+=npe.4 if_npe.4

Added: head/share/man/man4/man4.arm/ti_adc.4
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/share/man/man4/man4.arm/ti_adc.4       Mon Mar 24 20:06:27 2014        
(r263693)
@@ -0,0 +1,119 @@
+.\"
+.\" Copyright (c) 2014 Luiz Otavio O Souza <l...@freebsd.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 21, 2014
+.Dt TI_ADC 4
+.Os
+.Sh NAME
+.Nm ti_adc
+.Nd TI AM3XXX analog to digital converter driver
+.Sh SYNOPSIS
+.Cd "device ti_adc"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides access to the AIN (analog inputs) on am3xxx SoCs.
+.Pp
+It provides raw readings of the converted values for each analog inputs.
+.Pp
+The access to
+.Nm
+data is made via the
+.Xr sysctl 8
+interface:
+.Bd -literal
+dev.ti_adc.0.%desc: TI ADC controller
+dev.ti_adc.0.%driver: ti_adc
+dev.ti_adc.0.%pnpinfo: name=adc@44E0D000 compat=ti,adc
+dev.ti_adc.0.%parent: simplebus0
+dev.ti_adc.0.clockdiv: 2400
+dev.ti_adc.0.ain.0.enable: 0
+dev.ti_adc.0.ain.0.open_delay: 0
+dev.ti_adc.0.ain.0.samples_avg: 0
+dev.ti_adc.0.ain.0.input: 0
+dev.ti_adc.0.ain.1.enable: 0
+dev.ti_adc.0.ain.1.open_delay: 0
+dev.ti_adc.0.ain.1.samples_avg: 0
+dev.ti_adc.0.ain.1.input: 0
+dev.ti_adc.0.ain.2.enable: 0
+dev.ti_adc.0.ain.2.open_delay: 0
+dev.ti_adc.0.ain.2.samples_avg: 0
+dev.ti_adc.0.ain.2.input: 0
+dev.ti_adc.0.ain.3.enable: 0
+dev.ti_adc.0.ain.3.open_delay: 0
+dev.ti_adc.0.ain.3.samples_avg: 0
+dev.ti_adc.0.ain.3.input: 0
+dev.ti_adc.0.ain.4.enable: 0
+dev.ti_adc.0.ain.4.open_delay: 0
+dev.ti_adc.0.ain.4.samples_avg: 0
+dev.ti_adc.0.ain.4.input: 0
+dev.ti_adc.0.ain.5.enable: 0
+dev.ti_adc.0.ain.5.open_delay: 0
+dev.ti_adc.0.ain.5.samples_avg: 0
+dev.ti_adc.0.ain.5.input: 0
+dev.ti_adc.0.ain.6.enable: 1
+dev.ti_adc.0.ain.6.open_delay: 0
+dev.ti_adc.0.ain.6.samples_avg: 4
+dev.ti_adc.0.ain.6.input: 2308
+.Ed
+.Pp
+Global settings:
+.Bl -tag -width ".Va dev.ti_adc.0.clockdiv"
+.It Va dev.ti_adc.0.clockdiv
+Sets the ADC clock prescaler.
+The minimum value is 10 and the maximum is 65535.
+The ADC clock is based on CLK_M_OSC (24Mhz) / clockdiv.
+This gives a maximum of ~2.4Mhz for the ADC clock and ~10Khz for the default
+setting (clockdiv = 2400).
+.El
+.Pp
+Settings per input:
+.Bl -tag -width ".Va dev.ti_adc.0.ain.%d.samples_avg"
+.It Va dev.ti_adc.0.ain.%d.enable
+Enable the conversion for the input.
+Each input should be individually enabled before it can be used.
+When all the inputs are disabled, the ADC is turned off.
+.It Va dev.ti_adc.0.ain.%d.open_delay
+Sets the number of ADC clock cycles to wait after applying the input
+configuration and before start the ADC conversion.
+.It Va dev.ti_adc.0.ain.%d.samples_avg
+Sets the number of samples average used on each input, it can be set to 0
+(no samples average), 2, 4, 8, or 16.
+.It Va dev.ti_adc.0.ain.%d.input
+Is the converted raw value of the voltage applied on the analog input.
+It is made of a 12 bit value (0 ~ 4095).
+.El
+.Sh SEE ALSO
+.Xr sysctl 8
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Fx 11.0 .
+.Sh AUTHORS
+.An -nosplit
+The driver and this manual page was written by
+.An Luiz Otavio O Souza Aq l...@freebsd.org

Modified: head/sys/arm/conf/BEAGLEBONE
==============================================================================
--- head/sys/arm/conf/BEAGLEBONE        Mon Mar 24 19:21:14 2014        
(r263692)
+++ head/sys/arm/conf/BEAGLEBONE        Mon Mar 24 20:06:27 2014        
(r263693)
@@ -105,6 +105,9 @@ device              am335x_pmic             # AM335x Power 
Mana
 device         gpio
 device         gpioled
 
+# ADC support
+device         ti_adc
+
 # USB support
 device         usb
 options        USB_HOST_ALIGN=64       # Cacheline size is 64 on AM335x.

Modified: head/sys/arm/ti/am335x/am335x_prcm.c
==============================================================================
--- head/sys/arm/ti/am335x/am335x_prcm.c        Mon Mar 24 19:21:14 2014        
(r263692)
+++ head/sys/arm/ti/am335x/am335x_prcm.c        Mon Mar 24 20:06:27 2014        
(r263693)
@@ -107,6 +107,7 @@ __FBSDID("$FreeBSD$");
 #define CM_WKUP_CM_CLKDCOLDO_DPLL_PER  (CM_WKUP + 0x07C)
 #define CM_WKUP_CM_CLKMODE_DPLL_DISP   (CM_WKUP + 0x098)
 #define CM_WKUP_I2C0_CLKCTRL           (CM_WKUP + 0x0B8)
+#define CM_WKUP_ADC_TSC_CLKCTRL                (CM_WKUP + 0x0BC)
 
 #define CM_DPLL                                0x500
 #define CLKSEL_TIMER7_CLK              (CM_DPLL + 0x004)
@@ -260,6 +261,9 @@ struct ti_clock_dev ti_clk_devmap[] = {
        AM335X_GENERIC_CLOCK_DEV(I2C1_CLK),
        AM335X_GENERIC_CLOCK_DEV(I2C2_CLK),
 
+       /* TSC_ADC */
+       AM335X_GENERIC_CLOCK_DEV(TSC_ADC_CLK),
+
        /* EDMA */
        AM335X_GENERIC_CLOCK_DEV(EDMA_TPCC_CLK),
        AM335X_GENERIC_CLOCK_DEV(EDMA_TPTC0_CLK),
@@ -337,6 +341,9 @@ static struct am335x_clk_details g_am335
        _CLK_DETAIL(I2C1_CLK, CM_PER_I2C1_CLKCTRL, 0),
        _CLK_DETAIL(I2C2_CLK, CM_PER_I2C2_CLKCTRL, 0),
 
+       /* TSC_ADC module */
+       _CLK_DETAIL(TSC_ADC_CLK, CM_WKUP_ADC_TSC_CLKCTRL, 0),
+
        /* EDMA modules */
        _CLK_DETAIL(EDMA_TPCC_CLK, CM_PER_TPCC_CLKCTRL, 0),
        _CLK_DETAIL(EDMA_TPTC0_CLK, CM_PER_TPTC0_CLKCTRL, 0),

Modified: head/sys/arm/ti/files.ti
==============================================================================
--- head/sys/arm/ti/files.ti    Mon Mar 24 19:21:14 2014        (r263692)
+++ head/sys/arm/ti/files.ti    Mon Mar 24 20:06:27 2014        (r263693)
@@ -19,6 +19,7 @@ dev/mbox/mbox_if.m                            standard
 arm/ti/ti_mbox.c                               standard
 arm/ti/ti_pruss.c                              standard
 
+arm/ti/ti_adc.c                                        optional        ti_adc
 arm/ti/ti_gpio.c                               optional        gpio
 arm/ti/ti_i2c.c                                        optional        ti_i2c
 

Added: head/sys/arm/ti/ti_adc.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/arm/ti/ti_adc.c    Mon Mar 24 20:06:27 2014        (r263693)
@@ -0,0 +1,593 @@
+/*-
+ * Copyright 2014 Luiz Otavio O Souza <l...@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.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>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/ti/ti_prcm.h>
+#include <arm/ti/ti_adcreg.h>
+#include <arm/ti/ti_adcvar.h>
+
+/* Define our 7 steps, one for each input channel. */
+static struct ti_adc_input ti_adc_inputs[TI_ADC_NPINS] = {
+       { .stepconfig = ADC_STEPCFG1, .stepdelay = ADC_STEPDLY1 },
+       { .stepconfig = ADC_STEPCFG2, .stepdelay = ADC_STEPDLY2 },
+       { .stepconfig = ADC_STEPCFG3, .stepdelay = ADC_STEPDLY3 },
+       { .stepconfig = ADC_STEPCFG4, .stepdelay = ADC_STEPDLY4 },
+       { .stepconfig = ADC_STEPCFG5, .stepdelay = ADC_STEPDLY5 },
+       { .stepconfig = ADC_STEPCFG6, .stepdelay = ADC_STEPDLY6 },
+       { .stepconfig = ADC_STEPCFG7, .stepdelay = ADC_STEPDLY7 },
+};
+
+static int ti_adc_samples[5] = { 0, 2, 4, 8, 16 };
+
+static void
+ti_adc_enable(struct ti_adc_softc *sc)
+{
+
+       TI_ADC_LOCK_ASSERT(sc);
+
+       if (sc->sc_last_state == 1)
+               return;
+
+       /* Enable the FIFO0 threshold and the end of sequence interrupt. */
+       ADC_WRITE4(sc, ADC_IRQENABLE_SET,
+           ADC_IRQ_FIFO0_THRES | ADC_IRQ_END_OF_SEQ);
+
+       /* Enable the ADC.  Run thru enabled steps, start the conversions. */
+       ADC_WRITE4(sc, ADC_CTRL, ADC_READ4(sc, ADC_CTRL) | ADC_CTRL_ENABLE);
+
+       sc->sc_last_state = 1;
+}
+
+static void
+ti_adc_disable(struct ti_adc_softc *sc)
+{
+       int count;
+       uint32_t data;
+
+       TI_ADC_LOCK_ASSERT(sc);
+
+       if (sc->sc_last_state == 0)
+               return;
+
+       /* Disable all the enabled steps. */
+       ADC_WRITE4(sc, ADC_STEPENABLE, 0);
+
+       /* Disable the ADC. */
+       ADC_WRITE4(sc, ADC_CTRL, ADC_READ4(sc, ADC_CTRL) & ~ADC_CTRL_ENABLE);
+
+       /* Disable the FIFO0 threshold and the end of sequence interrupt. */
+       ADC_WRITE4(sc, ADC_IRQENABLE_CLR,
+           ADC_IRQ_FIFO0_THRES | ADC_IRQ_END_OF_SEQ);
+
+       /* ACK any pending interrupt. */
+       ADC_WRITE4(sc, ADC_IRQSTATUS, ADC_READ4(sc, ADC_IRQSTATUS));
+
+       /* Drain the FIFO data. */
+       count = ADC_READ4(sc, ADC_FIFO0COUNT) & ADC_FIFO_COUNT_MSK;
+       while (count > 0) {
+               data = ADC_READ4(sc, ADC_FIFO0DATA);
+               count = ADC_READ4(sc, ADC_FIFO0COUNT) & ADC_FIFO_COUNT_MSK;
+       }
+
+       sc->sc_last_state = 0;
+}
+
+static int
+ti_adc_setup(struct ti_adc_softc *sc)
+{
+       int ain;
+       uint32_t enabled;
+
+       TI_ADC_LOCK_ASSERT(sc);
+
+       /* Check for enabled inputs. */
+       enabled = 0;
+       for (ain = 0; ain < TI_ADC_NPINS; ain++) {
+               if (ti_adc_inputs[ain].enable)
+                       enabled |= (1U << (ain + 1));
+       }
+
+       /* Set the ADC global status. */
+       if (enabled != 0) {
+               ti_adc_enable(sc);
+               /* Update the enabled steps. */
+               if (enabled != ADC_READ4(sc, ADC_STEPENABLE))
+                       ADC_WRITE4(sc, ADC_STEPENABLE, enabled);
+       } else
+               ti_adc_disable(sc);
+
+       return (0);
+}
+
+static void
+ti_adc_input_setup(struct ti_adc_softc *sc, int32_t ain)
+{
+       struct ti_adc_input *input;
+       uint32_t reg, val;
+
+       TI_ADC_LOCK_ASSERT(sc);
+
+       input = &ti_adc_inputs[ain];
+       reg = input->stepconfig;
+       val = ADC_READ4(sc, reg);
+
+       /* Set single ended operation. */
+       val &= ~ADC_STEP_DIFF_CNTRL;
+
+       /* Set the negative voltage reference. */
+       val &= ~ADC_STEP_RFM_MSK;
+       val |= ADC_STEP_RFM_VREFN << ADC_STEP_RFM_SHIFT;
+
+       /* Set the positive voltage reference. */
+       val &= ~ADC_STEP_RFP_MSK;
+       val |= ADC_STEP_RFP_VREFP << ADC_STEP_RFP_SHIFT;
+
+       /* Set the samples average. */
+       val &= ~ADC_STEP_AVG_MSK;
+       val |= input->samples << ADC_STEP_AVG_SHIFT;
+
+       /* Select the desired input. */
+       val &= ~ADC_STEP_INP_MSK;
+       val |= ain << ADC_STEP_INP_SHIFT;
+
+       /* Set the ADC to one-shot mode. */
+       val &= ~ADC_STEP_MODE_MSK;
+
+       ADC_WRITE4(sc, reg, val);
+}
+
+static void
+ti_adc_reset(struct ti_adc_softc *sc)
+{
+       int ain;
+
+       TI_ADC_LOCK_ASSERT(sc);
+
+       /* Disable all the inputs. */
+       for (ain = 0; ain < TI_ADC_NPINS; ain++)
+               ti_adc_inputs[ain].enable = 0;
+}
+
+static int
+ti_adc_clockdiv_proc(SYSCTL_HANDLER_ARGS)
+{
+       int error, reg;
+       struct ti_adc_softc *sc;
+
+       sc = (struct ti_adc_softc *)arg1;
+
+       TI_ADC_LOCK(sc);
+       reg = (int)ADC_READ4(sc, ADC_CLKDIV) + 1;
+       TI_ADC_UNLOCK(sc);
+
+       error = sysctl_handle_int(oidp, &reg, sizeof(reg), req);
+       if (error != 0 || req->newptr == NULL)
+               return (error);
+
+       /*
+        * The actual written value is the prescaler setting - 1.
+        * Enforce a minimum value of 10 (i.e. 9) which limits the maximum
+        * ADC clock to ~2.4Mhz (CLK_M_OSC / 10).
+        */
+       reg--;
+       if (reg < 9)
+               reg = 9;
+       if (reg > USHRT_MAX)
+               reg = USHRT_MAX;
+
+       TI_ADC_LOCK(sc);
+       /* Disable the ADC. */
+       ti_adc_disable(sc);
+       /* Update the ADC prescaler setting. */
+       ADC_WRITE4(sc, ADC_CLKDIV, reg);
+       /* Enable the ADC again. */
+       ti_adc_setup(sc);
+       TI_ADC_UNLOCK(sc);
+
+       return (0);
+}
+
+static int
+ti_adc_enable_proc(SYSCTL_HANDLER_ARGS)
+{
+       int error;
+       int32_t enable;
+       struct ti_adc_softc *sc;
+       struct ti_adc_input *input;
+
+       input = (struct ti_adc_input *)arg1;
+       sc = input->sc;
+
+       enable = input->enable;
+       error = sysctl_handle_int(oidp, &enable, sizeof(enable),
+           req);
+       if (error != 0 || req->newptr == NULL)
+               return (error);
+
+       if (enable)
+               enable = 1;
+
+       TI_ADC_LOCK(sc);
+       /* Setup the ADC as needed. */
+       if (input->enable != enable) {
+               input->enable = enable;
+               ti_adc_setup(sc);
+               if (input->enable == 0)
+                       input->value = 0;
+       }
+       TI_ADC_UNLOCK(sc);
+
+       return (0);
+}
+
+static int
+ti_adc_open_delay_proc(SYSCTL_HANDLER_ARGS)
+{
+       int error, reg;
+       struct ti_adc_softc *sc;
+       struct ti_adc_input *input;
+
+       input = (struct ti_adc_input *)arg1;
+       sc = input->sc;
+
+       TI_ADC_LOCK(sc);
+       reg = (int)ADC_READ4(sc, input->stepdelay) & ADC_STEP_OPEN_DELAY;
+       TI_ADC_UNLOCK(sc);
+
+       error = sysctl_handle_int(oidp, &reg, sizeof(reg), req);
+       if (error != 0 || req->newptr == NULL)
+               return (error);
+
+       if (reg < 0)
+               reg = 0;
+
+       TI_ADC_LOCK(sc);
+       ADC_WRITE4(sc, input->stepdelay, reg & ADC_STEP_OPEN_DELAY);
+       TI_ADC_UNLOCK(sc);
+
+       return (0);
+}
+
+static int
+ti_adc_samples_avg_proc(SYSCTL_HANDLER_ARGS)
+{
+       int error, samples, i;
+       struct ti_adc_softc *sc;
+       struct ti_adc_input *input;
+
+       input = (struct ti_adc_input *)arg1;
+       sc = input->sc;
+
+       if (input->samples > nitems(ti_adc_samples))
+               input->samples = nitems(ti_adc_samples);
+       samples = ti_adc_samples[input->samples];
+
+       error = sysctl_handle_int(oidp, &samples, 0, req);
+       if (error != 0 || req->newptr == NULL)
+               return (error);
+
+       TI_ADC_LOCK(sc);
+       if (samples != ti_adc_samples[input->samples]) {
+               input->samples = 0;
+               for (i = 0; i < nitems(ti_adc_samples); i++)
+                       if (samples >= ti_adc_samples[i])
+                               input->samples = i;
+               ti_adc_input_setup(sc, input->input);
+       }
+       TI_ADC_UNLOCK(sc);
+
+       return (error);
+}
+
+static void
+ti_adc_read_data(struct ti_adc_softc *sc)
+{
+       int count, ain;
+       struct ti_adc_input *input;
+       uint32_t data;
+
+       TI_ADC_LOCK_ASSERT(sc);
+
+       /* Read the available data. */
+       count = ADC_READ4(sc, ADC_FIFO0COUNT) & ADC_FIFO_COUNT_MSK;
+       while (count > 0) {
+               data = ADC_READ4(sc, ADC_FIFO0DATA);
+               ain = (data & ADC_FIFO_STEP_ID_MSK) >> ADC_FIFO_STEP_ID_SHIFT;
+               input = &ti_adc_inputs[ain];
+               if (input->enable == 0)
+                       input->value = 0;
+               else
+                       input->value = (int32_t)(data & ADC_FIFO_DATA_MSK);
+               count = ADC_READ4(sc, ADC_FIFO0COUNT) & ADC_FIFO_COUNT_MSK;
+       }
+}
+
+static void
+ti_adc_intr(void *arg)
+{
+       struct ti_adc_softc *sc;
+       uint32_t status;
+
+       sc = (struct ti_adc_softc *)arg;
+
+       status = ADC_READ4(sc, ADC_IRQSTATUS);
+       if (status == 0)
+               return;
+       if (status & ~(ADC_IRQ_FIFO0_THRES | ADC_IRQ_END_OF_SEQ))
+               device_printf(sc->sc_dev, "stray interrupt: %#x\n", status);
+
+       TI_ADC_LOCK(sc);
+       /* ACK the interrupt. */
+       ADC_WRITE4(sc, ADC_IRQSTATUS, status);
+
+       /* Read the available data. */
+       if (status & ADC_IRQ_FIFO0_THRES)
+               ti_adc_read_data(sc);
+
+       /* Start the next conversion ? */
+       if (status & ADC_IRQ_END_OF_SEQ)
+               ti_adc_setup(sc);
+       TI_ADC_UNLOCK(sc);
+}
+
+static void
+ti_adc_sysctl_init(struct ti_adc_softc *sc)
+{
+       char pinbuf[3];
+       struct sysctl_ctx_list *ctx;
+       struct sysctl_oid *tree_node, *inp_node, *inpN_node;
+       struct sysctl_oid_list *tree, *inp_tree, *inpN_tree;
+       int ain;
+
+       /*
+        * Add per-pin sysctl tree/handlers.
+        */
+       ctx = device_get_sysctl_ctx(sc->sc_dev);
+       tree_node = device_get_sysctl_tree(sc->sc_dev);
+       tree = SYSCTL_CHILDREN(tree_node);
+       SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "clockdiv",
+           CTLFLAG_RW | CTLTYPE_UINT,  sc, 0,
+           ti_adc_clockdiv_proc, "IU", "ADC clock prescaler");
+       inp_node = SYSCTL_ADD_NODE(ctx, tree, OID_AUTO, "ain",
+           CTLFLAG_RD, NULL, "ADC inputs");
+       inp_tree = SYSCTL_CHILDREN(inp_node);
+
+       for (ain = 0; ain < TI_ADC_NPINS; ain++) {
+
+               snprintf(pinbuf, sizeof(pinbuf), "%d", ain);
+               inpN_node = SYSCTL_ADD_NODE(ctx, inp_tree, OID_AUTO, pinbuf,
+                   CTLFLAG_RD, NULL, "ADC input");
+               inpN_tree = SYSCTL_CHILDREN(inpN_node);
+
+               SYSCTL_ADD_PROC(ctx, inpN_tree, OID_AUTO, "enable",
+                   CTLFLAG_RW | CTLTYPE_UINT, &ti_adc_inputs[ain], 0,
+                   ti_adc_enable_proc, "IU", "Enable ADC input");
+               SYSCTL_ADD_PROC(ctx, inpN_tree, OID_AUTO, "open_delay",
+                   CTLFLAG_RW | CTLTYPE_UINT,  &ti_adc_inputs[ain], 0,
+                   ti_adc_open_delay_proc, "IU", "ADC open delay");
+               SYSCTL_ADD_PROC(ctx, inpN_tree, OID_AUTO, "samples_avg",
+                   CTLFLAG_RW | CTLTYPE_UINT,  &ti_adc_inputs[ain], 0,
+                   ti_adc_samples_avg_proc, "IU", "ADC samples average");
+               SYSCTL_ADD_INT(ctx, inpN_tree, OID_AUTO, "input",
+                   CTLFLAG_RD, &ti_adc_inputs[ain].value, 0,
+                   "Converted raw value for the ADC input");
+       }
+}
+
+static void
+ti_adc_inputs_init(struct ti_adc_softc *sc)
+{
+       int ain;
+       struct ti_adc_input *input;
+
+       TI_ADC_LOCK(sc);
+       for (ain = 0; ain < TI_ADC_NPINS; ain++) {
+               input = &ti_adc_inputs[ain];
+               input->sc = sc;
+               input->input = ain;
+               input->value = 0;
+               input->enable = 0;
+               input->samples = 0;
+               ti_adc_input_setup(sc, ain);
+       }
+       TI_ADC_UNLOCK(sc);
+}
+
+static void
+ti_adc_idlestep_init(struct ti_adc_softc *sc)
+{
+       uint32_t val;
+
+       val = ADC_READ4(sc, ADC_IDLECONFIG);
+
+       /* Set single ended operation. */
+       val &= ~ADC_STEP_DIFF_CNTRL;
+
+       /* Set the negative voltage reference. */
+       val &= ~ADC_STEP_RFM_MSK;
+       val |= ADC_STEP_RFM_VREFN << ADC_STEP_RFM_SHIFT;
+
+       /* Set the positive voltage reference. */
+       val &= ~ADC_STEP_RFP_MSK;
+       val |= ADC_STEP_RFP_VREFP << ADC_STEP_RFP_SHIFT;
+
+       /* Connect the input to VREFN. */
+       val &= ~ADC_STEP_INP_MSK;
+       val |= ADC_STEP_IN_VREFN << ADC_STEP_INP_SHIFT;
+
+       ADC_WRITE4(sc, ADC_IDLECONFIG, val);
+}
+
+static int
+ti_adc_probe(device_t dev)
+{
+
+       if (!ofw_bus_is_compatible(dev, "ti,adc"))
+               return (ENXIO);
+       device_set_desc(dev, "TI ADC controller");
+
+       return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ti_adc_attach(device_t dev)
+{
+       int err, rid;
+       struct ti_adc_softc *sc;
+       uint32_t reg, rev;
+
+       sc = device_get_softc(dev);
+       sc->sc_dev = dev;
+
+       rid = 0;
+       sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+           RF_ACTIVE);
+       if (!sc->sc_mem_res) {
+               device_printf(dev, "cannot allocate memory window\n");
+               return (ENXIO);
+       }
+
+       rid = 0;
+       sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+           RF_ACTIVE);
+       if (!sc->sc_irq_res) {
+               bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+               device_printf(dev, "cannot allocate interrupt\n");
+               return (ENXIO);
+       }
+
+       if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+           NULL, ti_adc_intr, sc, &sc->sc_intrhand) != 0) {
+               bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+               bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+               device_printf(dev, "Unable to setup the irq handler.\n");
+               return (ENXIO);
+       }
+
+       /* Activate the ADC_TSC module. */
+       err = ti_prcm_clk_enable(TSC_ADC_CLK);
+       if (err)
+               return (err);
+
+       /* Check the ADC revision. */
+       rev = ADC_READ4(sc, ADC_REVISION);
+       device_printf(dev,
+           "scheme: %#x func: %#x rtl: %d rev: %d.%d custom rev: %d\n",
+           (rev & ADC_REV_SCHEME_MSK) >> ADC_REV_SCHEME_SHIFT,
+           (rev & ADC_REV_FUNC_MSK) >> ADC_REV_FUNC_SHIFT,
+           (rev & ADC_REV_RTL_MSK) >> ADC_REV_RTL_SHIFT,
+           (rev & ADC_REV_MAJOR_MSK) >> ADC_REV_MAJOR_SHIFT,
+           rev & ADC_REV_MINOR_MSK,
+           (rev & ADC_REV_CUSTOM_MSK) >> ADC_REV_CUSTOM_SHIFT);
+
+       /*
+        * Disable the step write protect and make it store the step ID for
+        * the captured data on FIFO.
+        */
+       reg = ADC_READ4(sc, ADC_CTRL);
+       ADC_WRITE4(sc, ADC_CTRL, reg | ADC_CTRL_STEP_WP | ADC_CTRL_STEP_ID);
+
+       /*
+        * Set the ADC prescaler to 2400 (yes, the actual value written here
+        * is 2400 - 1).
+        * This sets the ADC clock to ~10Khz (CLK_M_OSC / 2400).
+        */
+       ADC_WRITE4(sc, ADC_CLKDIV, 2399);
+
+       TI_ADC_LOCK_INIT(sc);
+
+       ti_adc_idlestep_init(sc);
+       ti_adc_inputs_init(sc);
+       ti_adc_sysctl_init(sc);
+
+       return (0);
+}
+
+static int
+ti_adc_detach(device_t dev)
+{
+       struct ti_adc_softc *sc;
+
+       sc = device_get_softc(dev);
+
+       /* Turn off the ADC. */
+       TI_ADC_LOCK(sc);
+       ti_adc_reset(sc);
+       ti_adc_setup(sc);
+       TI_ADC_UNLOCK(sc);
+
+       TI_ADC_LOCK_DESTROY(sc);
+
+       if (sc->sc_intrhand)
+               bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand);
+       if (sc->sc_irq_res)
+               bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+       if (sc->sc_mem_res)
+               bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+
+       return (bus_generic_detach(dev));
+}
+
+static device_method_t ti_adc_methods[] = {
+       DEVMETHOD(device_probe,         ti_adc_probe),
+       DEVMETHOD(device_attach,        ti_adc_attach),
+       DEVMETHOD(device_detach,        ti_adc_detach),
+
+       DEVMETHOD_END
+};
+
+static driver_t ti_adc_driver = {
+       "ti_adc",
+       ti_adc_methods,
+       sizeof(struct ti_adc_softc),
+};
+
+static devclass_t ti_adc_devclass;
+
+DRIVER_MODULE(ti_adc, simplebus, ti_adc_driver, ti_adc_devclass, 0, 0);
+MODULE_VERSION(ti_adc, 1);
+MODULE_DEPEND(ti_adc, simplebus, 1, 1, 1);

Added: head/sys/arm/ti/ti_adcreg.h
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/arm/ti/ti_adcreg.h Mon Mar 24 20:06:27 2014        (r263693)
@@ -0,0 +1,118 @@
+/*-
+ * Copyright 2014 Luiz Otavio O Souza <l...@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _TI_ADCREG_H_
+#define _TI_ADCREG_H_
+
+#define        ADC_REVISION            0x000
+#define        ADC_REV_SCHEME_MSK              0xc0000000
+#define        ADC_REV_SCHEME_SHIFT            30
+#define        ADC_REV_FUNC_MSK                0x0fff0000
+#define        ADC_REV_FUNC_SHIFT              16
+#define        ADC_REV_RTL_MSK                 0x0000f800
+#define        ADC_REV_RTL_SHIFT               11
+#define        ADC_REV_MAJOR_MSK               0x00000700
+#define        ADC_REV_MAJOR_SHIFT             8
+#define        ADC_REV_CUSTOM_MSK              0x000000c0
+#define        ADC_REV_CUSTOM_SHIFT            6
+#define        ADC_REV_MINOR_MSK               0x0000003f
+#define        ADC_SYSCFG              0x010
+#define        ADC_SYSCFG_IDLE_MSK             0x000000c0
+#define        ADC_SYSCFG_IDLE_SHIFT           2
+#define        ADC_IRQSTATUS_RAW       0x024
+#define        ADC_IRQSTATUS           0x028
+#define        ADC_IRQENABLE_SET       0x02c
+#define        ADC_IRQENABLE_CLR       0x030
+#define        ADC_IRQ_HW_PEN_SYNC             (1 << 10)
+#define        ADC_IRQ_PEN_UP                  (1 << 9)
+#define        ADC_IRQ_OUT_RANGE               (1 << 8)
+#define        ADC_IRQ_FIFO1_UNDR              (1 << 7)
+#define        ADC_IRQ_FIFO1_OVERR             (1 << 6)
+#define        ADC_IRQ_FIFO1_THRES             (1 << 5)
+#define        ADC_IRQ_FIFO0_UNDR              (1 << 4)
+#define        ADC_IRQ_FIFO0_OVERR             (1 << 3)
+#define        ADC_IRQ_FIFO0_THRES             (1 << 2)
+#define        ADC_IRQ_END_OF_SEQ              (1 << 1)
+#define        ADC_IRQ_HW_PEN_ASYNC            (1 << 0)
+#define        ADC_CTRL                0x040
+#define        ADC_CTRL_STEP_WP                (1 << 2)
+#define        ADC_CTRL_STEP_ID                (1 << 1)
+#define        ADC_CTRL_ENABLE                 (1 << 0)
+#define        ADC_STAT                0x044
+#define        ADC_CLKDIV              0x04c
+#define        ADC_STEPENABLE          0x054
+#define        ADC_IDLECONFIG          0x058
+#define        ADC_STEPCFG1            0x064
+#define        ADC_STEPDLY1            0x068
+#define        ADC_STEPCFG2            0x06c
+#define        ADC_STEPDLY2            0x070
+#define        ADC_STEPCFG3            0x074
+#define        ADC_STEPDLY3            0x078
+#define        ADC_STEPCFG4            0x07c
+#define        ADC_STEPDLY4            0x080
+#define        ADC_STEPCFG5            0x084
+#define        ADC_STEPDLY5            0x088
+#define        ADC_STEPCFG6            0x08c
+#define        ADC_STEPDLY6            0x090
+#define        ADC_STEPCFG7            0x094
+#define        ADC_STEPDLY7            0x098
+#define        ADC_STEP_DIFF_CNTRL             (1 << 25)
+#define        ADC_STEP_RFM_MSK                0x01800000
+#define        ADC_STEP_RFM_SHIFT              23
+#define        ADC_STEP_RFM_VSSA               0
+#define        ADC_STEP_RFM_XNUR               1
+#define        ADC_STEP_RFM_YNLR               2
+#define        ADC_STEP_RFM_VREFN              3
+#define        ADC_STEP_INP_MSK                0x00780000
+#define        ADC_STEP_INP_SHIFT              19
+#define        ADC_STEP_INM_MSK                0x00078000
+#define        ADC_STEP_INM_SHIFT              15
+#define        ADC_STEP_IN_VREFN               8
+#define        ADC_STEP_RFP_MSK                0x00007000
+#define        ADC_STEP_RFP_SHIFT              12
+#define        ADC_STEP_RFP_VDDA               0
+#define        ADC_STEP_RFP_XPUL               1
+#define        ADC_STEP_RFP_YPLL               2
+#define        ADC_STEP_RFP_VREFP              3
+#define        ADC_STEP_RFP_INTREF             4
+#define        ADC_STEP_AVG_MSK                0x0000001c
+#define        ADC_STEP_AVG_SHIFT              2
+#define        ADC_STEP_MODE_MSK               0x00000003
+#define        ADC_STEP_MODE_ONESHOT           0x00000000
+#define        ADC_STEP_MODE_CONTINUOUS        0x00000001
+#define        ADC_STEP_SAMPLE_DELAY           0xff000000
+#define        ADC_STEP_OPEN_DELAY             0x0003ffff
+#define        ADC_FIFO0COUNT          0x0e4
+#define        ADC_FIFO0THRESHOLD      0x0e8
+#define        ADC_FIFO0DATA           0x100
+#define        ADC_FIFO_COUNT_MSK              0x0000007f
+#define        ADC_FIFO_STEP_ID_MSK            0x000f0000
+#define        ADC_FIFO_STEP_ID_SHIFT          16
+#define        ADC_FIFO_DATA_MSK               0x00000fff
+
+#endif /* _TI_ADCREG_H_ */

Added: head/sys/arm/ti/ti_adcvar.h
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/arm/ti/ti_adcvar.h Mon Mar 24 20:06:27 2014        (r263693)
@@ -0,0 +1,69 @@
+/*-
+ * Copyright 2014 Luiz Otavio O Souza <l...@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _TI_ADCVAR_H_
+#define _TI_ADCVAR_H_
+
+#define        TI_ADC_NPINS    7
+
+#define        ADC_READ4(_sc, reg)     bus_read_4((_sc)->sc_mem_res, reg)
+#define        ADC_WRITE4(_sc, reg, value)     \
+       bus_write_4((_sc)->sc_mem_res, reg, value)
+
+struct ti_adc_softc {
+       device_t                sc_dev;
+       int                     sc_last_state;
+       struct mtx              sc_mtx;
+       struct resource         *sc_mem_res;
+       struct resource         *sc_irq_res;
+       void                    *sc_intrhand;
+};
+
+struct ti_adc_input {
+       int32_t                 enable;         /* input enabled */
+       int32_t                 samples;        /* samples average */
+       int32_t                 input;          /* input number */
+       int32_t                 value;          /* raw converted value */
+       uint32_t                stepconfig;     /* step config register */
+       uint32_t                stepdelay;      /* step delay register */
+       struct ti_adc_softc     *sc;            /* pointer to adc softc */
+};
+
+#define        TI_ADC_LOCK(_sc)                \
+       mtx_lock(&(_sc)->sc_mtx)
+#define        TI_ADC_UNLOCK(_sc)              \
+       mtx_unlock(&(_sc)->sc_mtx)
+#define        TI_ADC_LOCK_INIT(_sc)   \
+       mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \
+           "ti_adc", MTX_DEF)
+#define        TI_ADC_LOCK_DESTROY(_sc)        \
+       mtx_destroy(&_sc->sc_mtx);
+#define        TI_ADC_LOCK_ASSERT(_sc) \
+       mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
+
+#endif /* _TI_ADCVAR_H_ */

Modified: head/sys/arm/ti/ti_prcm.h
==============================================================================
--- head/sys/arm/ti/ti_prcm.h   Mon Mar 24 19:21:14 2014        (r263692)
+++ head/sys/arm/ti/ti_prcm.h   Mon Mar 24 20:06:27 2014        (r263693)
@@ -162,6 +162,8 @@ typedef enum {
 
        PRUSS_CLK = 1700,
 
+       TSC_ADC_CLK = 1800,
+
        INVALID_CLK_IDENT
 
 } clk_ident_t;

Modified: head/sys/boot/fdt/dts/arm/am335x.dtsi
==============================================================================
--- head/sys/boot/fdt/dts/arm/am335x.dtsi       Mon Mar 24 19:21:14 2014        
(r263692)
+++ head/sys/boot/fdt/dts/arm/am335x.dtsi       Mon Mar 24 20:06:27 2014        
(r263693)
@@ -75,6 +75,13 @@
                        interrupt-parent = <&AINTC>;
                };
 
+               adc0: adc@44E0D000 {
+                       compatible = "ti,adc";
+                       reg = <0x44E0D000 0x2000>;
+                       interrupts = < 16 >;
+                       interrupt-parent = <&AINTC>;
+               };
+               

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to