[RFC] AM335x (Beaglebone-black) ADC driver

Luiz Otavio O Souza loos.br at gmail.com
Mon Mar 17 13:31:57 UTC 2014


Hi,

I've written a driver for the am335x ADC module (AIN inputs on BBB).

It works from the sysctl(8) interface, each input can be individually
enabled/disabled. When all the inputs are disabled, the ADC is turned
off.

The sysctl(8) interface also allows the selection of the number of the
samples average for each input (none, 2, 4, 8, 16) and the setting of
the ADC prescaler value (low clock speeds gives far stable readings
with the cost of loading - sometimes too much - the input circuit as
conversions can take a long time).

The converted values are exported as raw values (12 bits - 0 ~ 4095).

The ADC module can also work as a touchscreen sensor, but i have no
idea how to program the module as TSC sensor neither have the hardware
to test it, so this driver only works for reading the AIN converted
values.

Here is the output of the sysctl(8) for this driver:

# sysctl dev.am335x_adc.0
dev.am335x_adc.0.%desc: AM335x ADC controller
dev.am335x_adc.0.%driver: am335x_adc
dev.am335x_adc.0.%pnpinfo: name=adc at 44E0D000 compat=ti,am335x-adc
dev.am335x_adc.0.%parent: simplebus0
dev.am335x_adc.0.clockdiv: 30000
dev.am335x_adc.0.ain.0.enable: 0
dev.am335x_adc.0.ain.0.samples_avg: 0
dev.am335x_adc.0.ain.0.input: 0
dev.am335x_adc.0.ain.1.enable: 0
dev.am335x_adc.0.ain.1.samples_avg: 0
dev.am335x_adc.0.ain.1.input: 0
dev.am335x_adc.0.ain.2.enable: 0
dev.am335x_adc.0.ain.2.samples_avg: 0
dev.am335x_adc.0.ain.2.input: 0
dev.am335x_adc.0.ain.3.enable: 0
dev.am335x_adc.0.ain.3.samples_avg: 0
dev.am335x_adc.0.ain.3.input: 0
dev.am335x_adc.0.ain.4.enable: 0
dev.am335x_adc.0.ain.4.samples_avg: 0
dev.am335x_adc.0.ain.4.input: 0
dev.am335x_adc.0.ain.5.enable: 0
dev.am335x_adc.0.ain.5.samples_avg: 0
dev.am335x_adc.0.ain.5.input: 0
dev.am335x_adc.0.ain.6.enable: 1
dev.am335x_adc.0.ain.6.samples_avg: 0
dev.am335x_adc.0.ain.6.input: 2176

I've checked the 7 AINs with a potentiometer connected to them (and
using VDD_ADC and GNDA_ADC).

I am now writing the man page so i can ask for a approval to commit it.

Comments ?

Regards,
Luiz
-------------- next part --------------
Index: sys/arm/ti/am335x/am335x_prcm.c
===================================================================
--- sys/arm/ti/am335x/am335x_prcm.c	(revision 262131)
+++ sys/arm/ti/am335x/am335x_prcm.c	(working copy)
@@ -107,6 +107,7 @@
 #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 @@
 	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 @@
 	_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),
Index: sys/arm/ti/am335x/files.beaglebone
===================================================================
--- sys/arm/ti/am335x/files.beaglebone	(revision 262131)
+++ sys/arm/ti/am335x/files.beaglebone	(working copy)
@@ -1,3 +1,4 @@
 #$FreeBSD$
 
+arm/ti/am335x/am335x_adc.c			optional	am335x_adc
 arm/ti/am335x/am335x_pmic.c			optional	am335x_pmic
Index: sys/arm/ti/ti_prcm.h
===================================================================
--- sys/arm/ti/ti_prcm.h	(revision 262131)
+++ sys/arm/ti/ti_prcm.h	(working copy)
@@ -162,6 +162,8 @@
 
 	PRUSS_CLK = 1700,
 
+	TSC_ADC_CLK = 1800,
+
 	INVALID_CLK_IDENT
 
 } clk_ident_t;
--- /dev/null	2014-03-16 11:00:00.000000000 -0300
+++ sys/arm/ti/am335x/am335x_adc.c	2014-03-15 23:48:37.099205803 -0300
@@ -0,0 +1,497 @@
+/*-
+ * Copyright 2014 Luiz Otavio O Souza <loos at 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/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/am335x/am335x_adcreg.h>
+#include <arm/ti/am335x/am335x_adcvar.h>
+
+/* Define our 7 steps, one for each input channel. */
+static struct am335x_adc_input am335x_adc_inputs[AM335X_ADC_NPINS] = {
+	{ .stepreg = ADC_STEPCFG1, },
+	{ .stepreg = ADC_STEPCFG2, },
+	{ .stepreg = ADC_STEPCFG3, },
+	{ .stepreg = ADC_STEPCFG4, },
+	{ .stepreg = ADC_STEPCFG5, },
+	{ .stepreg = ADC_STEPCFG6, },
+	{ .stepreg = ADC_STEPCFG7, },
+};
+
+static int am335x_adc_samples[5] = { 0, 2, 4, 8, 16 };
+
+static void
+am335x_adc_enable(struct am335x_adc_softc *sc)
+{
+	uint32_t val;
+
+	AM335X_ADC_LOCK_ASSERT(sc);
+
+	/* Enable the FIFO0 threshold interrupt. */
+	ADC_WRITE4(sc, ADC_IRQENABLE_SET, ADC_IRQ_FIFO0_THRES);
+
+	/* Enable the ADC.  Run thru enabled steps, start the convertions. */
+	val = ADC_READ4(sc, ADC_CTRL);
+	ADC_WRITE4(sc, ADC_CTRL, val | ADC_CTRL_ENABLE);
+}
+
+static void
+am335x_adc_disable(struct am335x_adc_softc *sc)
+{
+	uint32_t val;
+
+	AM335X_ADC_LOCK_ASSERT(sc);
+
+	/* Disable the FIFO0 threshold interrupt. */
+	ADC_WRITE4(sc, ADC_IRQENABLE_CLR, ADC_IRQ_FIFO0_THRES);
+
+	/* Disable the ADC. */
+	val = ADC_READ4(sc, ADC_CTRL);
+	val &= ~ADC_CTRL_ENABLE;
+	ADC_WRITE4(sc, ADC_CTRL, val);
+}
+
+static void
+am335x_adc_enable_input(struct am335x_adc_softc *sc, int32_t input)
+{
+	uint32_t reg, val;
+
+	AM335X_ADC_LOCK_ASSERT(sc);
+
+	reg = am335x_adc_inputs[input].stepreg;
+	val = ADC_READ4(sc, reg);
+
+	/* 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 |= am335x_adc_inputs[input].samples << ADC_STEP_AVG_SHIFT;
+
+	/* Select the desired input. */
+	val &= ~ADC_STEP_INP_MSK;
+	val |= input << ADC_STEP_INP_SHIFT;
+
+	/* Set the ADC to continuous mode. */
+	val &= ~ADC_STEP_MODE_MSK;
+	val |= ADC_STEP_MODE_CONTINUOUS;
+
+	ADC_WRITE4(sc, reg, val);
+}
+
+static void
+am335x_adc_disable_input(struct am335x_adc_softc *sc, int32_t input)
+{
+	uint32_t reg, val;
+
+	AM335X_ADC_LOCK_ASSERT(sc);
+
+	/* Reset the input read data. */
+	am335x_adc_inputs[input].value = 0;
+
+	reg = am335x_adc_inputs[input].stepreg;
+	val = ADC_READ4(sc, reg);
+
+	/* Reset the ADC operation mode. */
+	val &= ~ADC_STEP_MODE_MSK;
+
+	/* Reset the input pin selection. */
+	val &= ~ADC_STEP_INP_MSK;
+
+	/* Reset the samples average. */
+	val &= ~ADC_STEP_AVG_MSK;
+
+	/* Reset the positive voltage reference. */
+	val &= ~ADC_STEP_RFP_MSK;
+
+	/* Reset the negative voltage reference. */
+	val &= ~ADC_STEP_RFM_MSK;
+
+	ADC_WRITE4(sc, reg, val);
+}
+
+static void
+am335x_adc_reset(struct am335x_adc_softc *sc)
+{
+	int i;
+
+	AM335X_ADC_LOCK_ASSERT(sc);
+
+	/* Disable all the inputs. */
+	for (i = 0; i < AM335X_ADC_NPINS; i++)
+		am335x_adc_inputs[i].enable = 0;
+}
+
+static int
+am335x_adc_setup(struct am335x_adc_softc *sc)
+{
+	int enabled, i;
+
+	AM335X_ADC_LOCK_ASSERT(sc);
+
+	/* Check if we have any input enabled. */
+	enabled = 0;
+	for (i = 0; i < AM335X_ADC_NPINS; i++) {
+		if (am335x_adc_inputs[i].enable) {
+			enabled |= (1U << (i + 1));
+			am335x_adc_enable_input(sc, i);
+		} else
+			am335x_adc_disable_input(sc, i);
+	}
+
+	/* Enable the selected steps. */
+	ADC_WRITE4(sc, ADC_STEPENABLE, enabled);
+
+	/* Set the global ADC status. */
+	if (enabled != 0)
+		am335x_adc_enable(sc);
+	else
+		am335x_adc_disable(sc);
+
+	return (0);
+}
+
+static int
+am335x_adc_clockdiv_proc(SYSCTL_HANDLER_ARGS)
+{
+	int error, reg;
+	struct am335x_adc_softc *sc;
+
+	sc = (struct am335x_adc_softc *)arg1;
+
+	AM335X_ADC_LOCK(sc);
+	reg = (int)ADC_READ4(sc, ADC_CLKDIV) + 1;
+	AM335X_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. */
+	reg--;
+	if (reg < 0)
+		reg = 0;
+	if (reg > 65535)
+		reg = 65535;
+
+	AM335X_ADC_LOCK(sc);
+	/* Disable the ADC. */
+	am335x_adc_disable(sc);
+	/* Update the ADC prescaler setting. */
+	ADC_WRITE4(sc, ADC_CLKDIV, reg);
+	/* Enable the ADC again. */
+	am335x_adc_setup(sc);
+	AM335X_ADC_UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+am335x_adc_enable_proc(SYSCTL_HANDLER_ARGS)
+{
+	int error;
+	int32_t enable;
+	struct am335x_adc_softc *sc;
+	struct am335x_adc_input *input;
+
+	input = (struct am335x_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;
+
+	AM335X_ADC_LOCK(sc);
+	/* Setup the ADC as needed. */
+	if (input->enable != enable) {
+		input->enable = enable;
+		am335x_adc_setup(sc);
+	}
+	AM335X_ADC_UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+am335x_adc_samples_avg_proc(SYSCTL_HANDLER_ARGS)
+{
+	int error, samples, i;
+	struct am335x_adc_softc *sc;
+	struct am335x_adc_input *input;
+
+	input = (struct am335x_adc_input *)arg1;
+	sc = input->sc;
+
+	if (input->samples > nitems(am335x_adc_samples))
+		input->samples = nitems(am335x_adc_samples);
+	samples = am335x_adc_samples[input->samples];
+
+	error = sysctl_handle_int(oidp, &samples, 0, req);
+	if (error != 0 || req->newptr == NULL)
+		return (error);
+
+	if (samples != am335x_adc_samples[input->samples]) {
+		AM335X_ADC_LOCK(sc);
+		am335x_adc_disable(sc);
+		input->samples = 0;
+		for (i = 0; i < nitems(am335x_adc_samples); i++)
+			if (samples >= am335x_adc_samples[i])
+				input->samples = i;
+
+		/* Update the ADC setup as needed. */
+		am335x_adc_setup(sc);
+		AM335X_ADC_UNLOCK(sc);
+	}
+
+	return (error);
+}
+
+static void
+am335x_adc_intr(void *arg)
+{
+	int count, input;
+	struct am335x_adc_softc *sc;
+	uint32_t data, status;
+
+	sc = (struct am335x_adc_softc *)arg;
+
+	status = ADC_READ4(sc, ADC_IRQSTATUS);
+	if (status & ~ADC_IRQ_FIFO0_THRES)
+		device_printf(sc->sc_dev, "stray interrupt: %#x\n", status);
+
+	/* Clear the interrupt. */
+	ADC_WRITE4(sc, ADC_IRQSTATUS, ADC_IRQ_FIFO0_THRES);
+
+	/* Read the available data. */
+	count = ADC_READ4(sc, ADC_FIFO0COUNT) & ADC_FIFO_COUNT_MSK;
+	while (count-- > 0) {
+		data = ADC_READ4(sc, ADC_FIFO0DATA);
+		input = (data & ADC_FIFO_STEP_ID_MSK) >> ADC_FIFO_STEP_ID_SHIFT;
+		am335x_adc_inputs[input].value = data & ADC_FIFO_DATA_MSK;
+	}
+}
+
+static void
+am335x_adc_sysctl_init(struct am335x_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 i;
+
+	/*
+	 * 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,
+	    am335x_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 (i = 0; i < AM335X_ADC_NPINS; i++) {
+
+		snprintf(pinbuf, sizeof(pinbuf), "%d", i);
+		inpN_node = SYSCTL_ADD_NODE(ctx, inp_tree, OID_AUTO, pinbuf,
+		    CTLFLAG_RD, NULL, "ADC input");
+		inpN_tree = SYSCTL_CHILDREN(inpN_node);
+
+		am335x_adc_inputs[i].sc = sc;
+		am335x_adc_inputs[i].input = i;
+		am335x_adc_inputs[i].value = 0;
+		am335x_adc_inputs[i].enable = 0;
+		am335x_adc_inputs[i].samples = 0;
+		SYSCTL_ADD_PROC(ctx, inpN_tree, OID_AUTO, "enable",
+		    CTLFLAG_RW | CTLTYPE_UINT, &am335x_adc_inputs[i], 0,
+		    am335x_adc_enable_proc, "IU", "Enable ADC input");
+		SYSCTL_ADD_PROC(ctx, inpN_tree, OID_AUTO, "samples_avg",
+		    CTLFLAG_RW | CTLTYPE_UINT,  &am335x_adc_inputs[i], 0,
+		    am335x_adc_samples_avg_proc, "IU", "ADC samples average");
+		SYSCTL_ADD_INT(ctx, inpN_tree, OID_AUTO, "input",
+		    CTLFLAG_RD, &am335x_adc_inputs[i].value, 0,
+		    "Converted value for the ADC input");
+	}
+}
+
+static int
+am335x_adc_probe(device_t dev)
+{
+
+	if (!ofw_bus_is_compatible(dev, "ti,am335x-adc"))
+		return (ENXIO);
+	device_set_desc(dev, "AM335x ADC controller");
+
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+am335x_adc_attach(device_t dev)
+{
+	int err, rid;
+	struct am335x_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, am335x_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 2000 (yes, the actual value written here
+	 * is 2000 - 1).
+	 */
+	ADC_WRITE4(sc, ADC_CLKDIV, 1999);
+
+	AM335X_ADC_LOCK_INIT(sc);
+
+	am335x_adc_sysctl_init(sc);
+
+	return (0);
+}
+
+static int
+am335x_adc_detach(device_t dev)
+{
+	struct am335x_adc_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	/* Turn off the ADC. */
+	AM335X_ADC_LOCK(sc);
+	am335x_adc_reset(sc);
+	am335x_adc_setup(sc);
+	AM335X_ADC_UNLOCK(sc);
+
+	AM335X_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 am335x_adc_methods[] = {
+	DEVMETHOD(device_probe,		am335x_adc_probe),
+	DEVMETHOD(device_attach,	am335x_adc_attach),
+	DEVMETHOD(device_detach,	am335x_adc_detach),
+
+	DEVMETHOD_END
+};
+
+static driver_t am335x_adc_driver = {
+	"am335x_adc",
+	am335x_adc_methods,
+	sizeof(struct am335x_adc_softc),
+};
+
+static devclass_t am335x_adc_devclass;
+
+DRIVER_MODULE(am335x_adc, simplebus, am335x_adc_driver, am335x_adc_devclass, 0, 0);
+MODULE_VERSION(am335x_adc, 1);
+MODULE_DEPEND(am335x_adc, simplebus, 1, 1, 1);
--- /dev/null	2014-03-16 11:00:00.000000000 -0300
+++ sys/arm/ti/am335x/am335x_adcreg.h	2014-03-16 11:07:47.204396970 -0300
@@ -0,0 +1,105 @@
+/*-
+ * Copyright 2014 Luiz Otavio O Souza <loos at 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 _AM335X_ADCREG_H_
+#define _AM335X_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		0x00000004
+#define	ADC_CTRL_STEP_ID		0x00000002
+#define	ADC_CTRL_ENABLE			0x00000001
+#define	ADC_CLKDIV		0x04c
+#define	ADC_STEPENABLE		0x054
+#define	ADC_STEPCFG1		0x064
+#define	ADC_STEPCFG2		0x06c
+#define	ADC_STEPCFG3		0x074
+#define	ADC_STEPCFG4		0x07c
+#define	ADC_STEPCFG5		0x084
+#define	ADC_STEPCFG6		0x08c
+#define	ADC_STEPCFG7		0x094
+#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_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_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 /* _AM335X_ADCREG_H_ */
--- /dev/null	2014-03-16 11:00:00.000000000 -0300
+++ sys/arm/ti/am335x/am335x_adcvar.h	2014-03-15 22:26:47.313542959 -0300
@@ -0,0 +1,67 @@
+/*-
+ * Copyright 2014 Luiz Otavio O Souza <loos at 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 _AM335X_ADCVAR_H_
+#define _AM335X_ADCVAR_H_
+
+#define	AM335X_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 am335x_adc_softc {
+	device_t		sc_dev;
+	struct mtx		sc_mtx;
+	struct resource		*sc_mem_res;
+	struct resource		*sc_irq_res;
+	void			*sc_intrhand;
+};
+
+struct am335x_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		stepreg;	/* step register */
+	struct am335x_adc_softc	*sc;		/* pointer to adc softc */
+};
+
+#define	AM335X_ADC_LOCK(_sc)		\
+	mtx_lock(&(_sc)->sc_mtx)
+#define	AM335X_ADC_UNLOCK(_sc)		\
+	mtx_unlock(&(_sc)->sc_mtx)
+#define	AM335X_ADC_LOCK_INIT(_sc)	\
+	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \
+	    "am335x_adc", MTX_DEF)
+#define	AM335X_ADC_LOCK_DESTROY(_sc)	\
+	mtx_destroy(&_sc->sc_mtx);
+#define	AM335X_ADC_LOCK_ASSERT(_sc)	\
+	mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
+
+#endif /* _AM335X_ADCVAR_H_ */
Index: sys/boot/fdt/dts/am335x.dtsi
===================================================================
--- sys/boot/fdt/dts/am335x.dtsi	(revision 262131)
+++ sys/boot/fdt/dts/am335x.dtsi	(working copy)
@@ -75,6 +75,13 @@
 			interrupt-parent = <&AINTC>;
 		};
 
+		adc0: adc at 44E0D000 {
+			compatible = "ti,am335x-adc";
+			reg = <0x44E0D000 0x2000>;
+			interrupts = < 16 >;
+			interrupt-parent = <&AINTC>;
+ 		};
+ 		
 		GPIO: gpio {
 			#gpio-cells = <3>;
 			compatible = "ti,gpio";
Index: sys/arm/conf/BEAGLEBONE
===================================================================
--- sys/arm/conf/BEAGLEBONE	(revision 262131)
+++ sys/arm/conf/BEAGLEBONE	(working copy)
@@ -103,6 +103,9 @@
 device		gpio
 device		gpioled
 
+# ADC support
+device		am335x_adc
+
 # USB support
 device		usb
 options 	USB_HOST_ALIGN=64	# Cacheline size is 64 on AM335x.


More information about the freebsd-arm mailing list