pru: Add support for ADC input

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor
2017-05-11 14:53:00 -04:00
parent b85755c0ff
commit c9b6662138
7 changed files with 165 additions and 5 deletions

View File

@@ -2,6 +2,11 @@
if MACH_PRU
config PRU_SELECT
bool
default y
select HAVE_GPIO_ADC
config BOARD_DIRECTORY
string
default "pru"

View File

@@ -4,9 +4,11 @@
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "board/io.h" // readl
#include "command.h" // shutdown
#include "compiler.h" // ARRAY_SIZE
#include "gpio.h" // gpio_out_setup
#include "internal.h" // ADC
#include "sched.h" // sched_shutdown
@@ -140,3 +142,68 @@ gpio_in_read(struct gpio_in g)
{
return !!(*g.reg & g.bit);
}
/****************************************************************
* Analog to Digital Converter (ADC) pins
****************************************************************/
DECL_CONSTANT(ADC_MAX, 4095);
struct gpio_adc
gpio_adc_setup(uint8_t pin)
{
uint8_t chan = pin - ARRAY_SIZE(digital_regs) * 32;
if (chan >= 8)
shutdown("Not an adc channel");
if (!readl(&ADC->ctrl))
shutdown("ADC module not enabled");
return (struct gpio_adc){ .chan = chan };
}
enum { ADC_DUMMY=0xff };
static uint8_t last_analog_read = ADC_DUMMY;
static uint16_t last_analog_sample;
// Try to sample a value. Returns zero if sample ready, otherwise
// returns the number of clock ticks the caller should wait before
// retrying this function.
uint32_t
gpio_adc_sample(struct gpio_adc g)
{
uint8_t last = last_analog_read;
if (last == ADC_DUMMY) {
// Start sample
last_analog_read = g.chan;
writel(&ADC->stepenable, 1 << (g.chan + 1));
goto need_delay;
}
if (last == g.chan) {
// Check if sample ready
while (readl(&ADC->fifo0count)) {
uint32_t sample = readl(&ADC->fifo0data);
if (sample >> 16 == g.chan) {
last_analog_read = ADC_DUMMY;
last_analog_sample = sample;
return 0;
}
}
}
need_delay:
return 160;
}
// Read a value; use only after gpio_adc_sample() returns zero
uint16_t
gpio_adc_read(struct gpio_adc g)
{
return last_analog_sample;
}
// Cancel a sample that may have been started with gpio_adc_sample()
void
gpio_adc_cancel_sample(struct gpio_adc g)
{
if (last_analog_read == g.chan)
last_analog_read = ADC_DUMMY;
}

View File

@@ -19,4 +19,12 @@ struct gpio_in {
struct gpio_in gpio_in_setup(uint8_t pin, int8_t pull_up);
uint8_t gpio_in_read(struct gpio_in g);
struct gpio_adc {
uint8_t chan;
};
struct gpio_adc gpio_adc_setup(uint8_t pin);
uint32_t gpio_adc_sample(struct gpio_adc g);
uint16_t gpio_adc_read(struct gpio_adc g);
void gpio_adc_cancel_sample(struct gpio_adc g);
#endif // gpio.h

View File

@@ -34,6 +34,37 @@ struct shared_mem {
#define SIGNAL_PRU0_WAITING 0xefefefef
#define SIGNAL_PRU1_READY 0xabababab
static struct shared_mem *SHARED_MEM = (void*)0x10000;
#define SHARED_MEM ((struct shared_mem *)0x10000)
// Hardware ADC registers
struct beaglebone_adc {
uint32_t pad_00[10];
uint32_t irqstatus;
uint32_t irqenable_set;
uint32_t irqenable_clr;
uint32_t irqwakeup;
uint32_t dmaenable_set;
uint32_t dmaenable_clr;
uint32_t ctrl;
uint32_t adcstat;
uint32_t adcrange;
uint32_t adc_clkdiv;
uint32_t adc_misc;
uint32_t stepenable;
uint32_t idleconfig;
uint32_t ts_charge_stepconfig;
uint32_t ts_charge_delay;
struct {
uint32_t config;
uint32_t delay;
} step[16];
uint32_t fifo0count;
uint32_t pad_e8[6];
uint32_t fifo0data;
uint32_t pad_104[63];
uint32_t fifo1data;
};
#define ADC ((struct beaglebone_adc *)0x44e0d000)
#endif // internal.h

View File

@@ -90,8 +90,6 @@ static void
timer_init(void)
{
CT_IEP.TMR_CNT = 0;
CT_IEP.TMR_CMP_CFG = 0x01 << 1;
CT_IEP.TMR_GLB_CFG = 0x11;
timer_shutdown();
}

View File

@@ -8,6 +8,7 @@
#include <string.h> // memset
#include <pru/io.h> // write_r31
#include <pru_cfg.h> // CT_CFG
#include <pru_iep.h> // CT_IEP
#include <pru_intc.h> // CT_INTC
#include <pru_rpmsg.h> // pru_rpmsg_send
#include <pru_virtio_ids.h> // VIRTIO_ID_RPMSG
@@ -76,6 +77,44 @@ process_io(struct pru_rpmsg_transport *transport)
}
/****************************************************************
* Peripheral reset
****************************************************************/
static void
timer_reset(void)
{
CT_IEP.TMR_CMP_CFG = 0x01 << 1;
CT_IEP.TMR_GLB_CFG = 0x11;
}
static void
adc_reset(void)
{
// Disable ADC
ADC->ctrl = (1<<2);
barrier();
// Clear registers
ADC->irqstatus = 0xffffffff;
ADC->irqenable_clr = 0xffffffff;
ADC->dmaenable_clr = 0xffffffff;
ADC->adc_clkdiv = 0;
ADC->stepenable = 0;
ADC->idleconfig = 0;
int i;
for (i=0; i<8; i++) {
ADC->step[i].config = i<<19;
ADC->step[i].delay = 0;
}
// Enable ADC
barrier();
writel(&ADC->ctrl, 0x07);
// Drain fifo
while (readl(&ADC->fifo0count))
readl(&ADC->fifo0data);
}
/****************************************************************
* Resource table
****************************************************************/
@@ -218,8 +257,12 @@ main(void)
, CHAN_DESC, CHAN_PORT) != PRU_RPMSG_SUCCESS)
;
// Wait for PRU1 to be ready
// Reset peripherals
memset(SHARED_MEM, 0, sizeof(*SHARED_MEM));
timer_reset();
adc_reset();
// Wait for PRU1 to be ready
writel(&SHARED_MEM->signal, SIGNAL_PRU0_WAITING);
while (readl(&SHARED_MEM->signal) != SIGNAL_PRU1_READY)
;