From owner-svn-src-head@FreeBSD.ORG Mon Mar 24 20:06:29 2014 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 9DF85470; Mon, 24 Mar 2014 20:06:29 +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)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id 8637C2EE; Mon, 24 Mar 2014 20:06:29 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.8/8.14.8) with ESMTP id s2OK6TuH070573; Mon, 24 Mar 2014 20:06:29 GMT (envelope-from loos@svn.freebsd.org) Received: (from loos@localhost) by svn.freebsd.org (8.14.8/8.14.8/Submit) id s2OK6RVA070560; Mon, 24 Mar 2014 20:06:27 GMT (envelope-from loos@svn.freebsd.org) Message-Id: <201403242006.s2OK6RVA070560@svn.freebsd.org> From: Luiz Otavio O Souza Date: Mon, 24 Mar 2014 20:06:27 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r263693 - in head: share/man/man4/man4.arm sys/arm/conf sys/arm/ti sys/arm/ti/am335x sys/boot/fdt/dts/arm X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.17 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 24 Mar 2014 20:06:29 -0000 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 +.\" 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 loos@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 + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +/* 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, ®, 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, ®, 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 + * 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 + * 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 ***