diff options
Diffstat (limited to 'bsps/sparc/shared/analog/gradcdac.c')
-rw-r--r-- | bsps/sparc/shared/analog/gradcdac.c | 579 |
1 files changed, 579 insertions, 0 deletions
diff --git a/bsps/sparc/shared/analog/gradcdac.c b/bsps/sparc/shared/analog/gradcdac.c new file mode 100644 index 0000000000..07fb2ee9dd --- /dev/null +++ b/bsps/sparc/shared/analog/gradcdac.c @@ -0,0 +1,579 @@ +/* ADC / DAC (GRADCDAC) interface implementation + * + * COPYRIGHT (c) 2009. + * Cobham Gaisler AB. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <rtems.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> +#include <bsp/gradcdac.h> + +/****************** DEBUG Definitions ********************/ +#define DBG_IOCTRL 1 +#define DBG_TX 2 +#define DBG_RX 4 + +#define DEBUG_FLAGS (DBG_IOCTRL | DBG_RX | DBG_TX ) +/* Uncomment for debug output */ +/* +#define DEBUG +#define DEBUGFUNCS +*/ +#include <bsp/debug_defs.h> + +struct gradcdac_priv { + struct gradcdac_regs *regs; /* Must be first */ + struct drvmgr_dev *dev; + char devName[48]; + + unsigned int freq; + int irqno; + int minor; + + void (*isr_adc)(void *cookie, void *arg); + void (*isr_dac)(void *cookie, void *arg); + void *isr_adc_arg; + void *isr_dac_arg; + + int open; +}; + +/* Global variables */ + +/* Print Info routines */ +void gradcdac_print(void *cookie); + +int gradcdac_init2(struct drvmgr_dev *dev); +int gradcdac_init3(struct drvmgr_dev *dev); +int gradcadc_device_init(struct gradcdac_priv *pDev); +void gradcdac_adc_interrupt(void *arg); +void gradcdac_dac_interrupt(void *arg); + +struct drvmgr_drv_ops gradcdac_ops = +{ + .init = {NULL, gradcdac_init2, gradcdac_init3, NULL}, + .remove = NULL, + .info = NULL +}; + +struct amba_dev_id gradcdac_ids[] = +{ + {VENDOR_GAISLER, GAISLER_GRADCDAC}, + {0, 0} /* Mark end of table */ +}; + +struct amba_drv_info gradcdac_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_GRADCDAC_ID, /* Driver ID */ + "GRADCDAC_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &gradcdac_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &gradcdac_ids[0] +}; + +void gradcdac_register_drv (void) +{ + DBG("Registering GRADCDAC driver\n"); + drvmgr_drv_register(&gradcdac_drv_info.general); +} + +int gradcdac_init2(struct drvmgr_dev *dev) +{ + struct gradcdac_priv *priv; + + DBG("GRADCDAC[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); + + priv = dev->priv = malloc(sizeof(struct gradcdac_priv)); + if ( !priv ) + return DRVMGR_NOMEM; + memset(priv, 0, sizeof(*priv)); + priv->dev = dev; + + /* This core will not find other cores, so we wait for init2() */ + + return DRVMGR_OK; +} + + +int gradcdac_init3(struct drvmgr_dev *dev) +{ + struct gradcdac_priv *priv = dev->priv; + char prefix[32]; + + if ( !priv ) + return DRVMGR_FAIL; + + if ( gradcadc_device_init(priv) ) { + free(dev->priv); + dev->priv = NULL; + return DRVMGR_FAIL; + } + + /* Get Filesystem name prefix */ + prefix[0] = '\0'; + if ( drvmgr_get_dev_prefix(dev, prefix) ) { + /* Failed to get prefix, make sure of a unique FS name + * by using the driver minor. + */ + sprintf(priv->devName, "/dev/gradcdac%d", dev->minor_drv); + } else { + /* Got special prefix, this means we have a bus prefix + * And we should use our "bus minor" + */ + sprintf(priv->devName, "/dev/%sgradcdac%d", prefix, dev->minor_bus); + } + + return DRVMGR_OK; +} + +static void gradcdac_print_dev(struct gradcdac_priv *pDev) +{ + printf("======= GRADCDAC %p =======\n", pDev->regs); + printf(" Minor: %d\n", pDev->minor); + printf(" Dev Name: %s\n", pDev->devName); + printf(" RegBase: %p\n", pDev->regs); + printf(" IRQ: %d and %d\n", pDev->irqno, pDev->irqno+1); + printf(" Core Freq: %d kHz\n", pDev->freq / 1000); + printf(" Opened: %s\n", pDev->open ? "YES" : "NO"); + + printf(" CONFIG: 0x%x\n", pDev->regs->config); + printf(" STATUS: 0x%x\n", pDev->regs->status); +} + +void gradcdac_print(void *cookie) +{ + struct drvmgr_dev *dev; + struct gradcdac_priv *pDev; + + if ( cookie ) { + gradcdac_print_dev(cookie); + return; + } + + /* Show all */ + dev = gradcdac_drv_info.general.dev; + while (dev) { + pDev = (struct gradcdac_priv *)dev->priv; + gradcdac_print_dev(pDev); + dev = dev->next_in_drv; + } +} + +static void gradcdac_hw_reset(struct gradcdac_regs *regs) +{ + /* Reset core */ + regs->config = 0; + regs->adrdir = 0; + regs->adrout = 0; + regs->data_dir = 0; + regs->data_out = 0; +} + +/* Device initialization called once on startup */ +int gradcadc_device_init(struct gradcdac_priv *pDev) +{ + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)pDev->dev->businfo; + if ( ambadev == NULL ) { + return -1; + } + pnpinfo = &ambadev->info; + pDev->irqno = pnpinfo->irq; + pDev->regs = (struct gradcdac_regs *)pnpinfo->apb_slv->start; + pDev->minor = pDev->dev->minor_drv; + + /* Reset Hardware before attaching IRQ handler */ + gradcdac_hw_reset(pDev->regs); + + pDev->open = 0; + + /* Get frequency in Hz */ + if ( drvmgr_freq_get(pDev->dev, DEV_APB_SLV, &pDev->freq) ) { + return -1; + } + + DBG("GRADCDAC frequency: %d Hz\n", pDev->freq); + + return 0; +} + +void gradcdac_dac_interrupt(void *arg) +{ + struct gradcdac_priv *pDev = arg; + if ( pDev->isr_dac ) + pDev->isr_dac(pDev, pDev->isr_dac_arg); +} + +void gradcdac_adc_interrupt(void *arg) +{ + struct gradcdac_priv *pDev = arg; + if ( pDev->isr_adc ) + pDev->isr_adc(pDev, pDev->isr_adc_arg); +} + +void *gradcdac_open(char *devname) +{ + struct gradcdac_priv *pDev; + struct drvmgr_dev *dev; + + /* Find device by name */ + dev = gradcdac_drv_info.general.dev; + while ( dev ) { + pDev = (struct gradcdac_priv *)dev->priv; + if ( pDev ) { + if ( strncmp(pDev->devName, devname, sizeof(pDev->devName)) == 0 ) { + /* Found matching device name */ + break; + } + } + dev = dev->next_in_drv; + } + + if ( !dev ) + return NULL; + + /* is device busy/taken? */ + if ( pDev->open ) + return NULL; + + /* Mark device taken */ + pDev->open = 1; + + return pDev; +} + +void gradcdac_set_config(void *cookie, struct gradcdac_config *cfg) +{ + struct gradcdac_priv *pDev = cookie; + unsigned int config=0; + + config = (cfg->dac_ws<<GRADCDAC_CFG_DACWS_BIT)&GRADCDAC_CFG_DACWS; + + if ( cfg->wr_pol ) + config |= GRADCDAC_CFG_WRPOL; + + config |= (cfg->dac_dw<<GRADCDAC_CFG_DACDW_BIT)&GRADCDAC_CFG_DACDW; + + config |= (cfg->adc_ws<<GRADCDAC_CFG_ADCWS_BIT)&GRADCDAC_CFG_ADCWS; + + if ( cfg->rc_pol ) + config |= GRADCDAC_CFG_RCPOL; + + config |= (cfg->cs_mode<<GRADCDAC_CFG_CSMODE_BIT)&GRADCDAC_CFG_CSMODE; + + if ( cfg->cs_pol ) + config |= GRADCDAC_CFG_CSPOL; + + if ( cfg->ready_mode ) + config |= GRADCDAC_CFG_RDYMODE; + + if ( cfg->ready_pol ) + config |= GRADCDAC_CFG_RDYPOL; + + if ( cfg->trigg_pol ) + config |= GRADCDAC_CFG_TRIGPOL; + + config |= (cfg->trigg_mode<<GRADCDAC_CFG_TRIGMODE_BIT)&GRADCDAC_CFG_TRIGMODE; + + config |= (cfg->adc_dw<<GRADCDAC_CFG_ADCDW_BIT)&GRADCDAC_CFG_ADCDW; + + /* Write config */ + pDev->regs->config = config; +} + +void gradcdac_get_config(void *cookie, struct gradcdac_config *cfg) +{ + struct gradcdac_priv *pDev = cookie; + unsigned int config; + + if ( !cfg ) + return; + + /* Get config */ + config = pDev->regs->config; + + cfg->dac_ws = (config&GRADCDAC_CFG_DACWS)>>GRADCDAC_CFG_DACWS_BIT; + + cfg->wr_pol = (config&GRADCDAC_CFG_WRPOL)>>GRADCDAC_CFG_WRPOL_BIT; + + cfg->dac_dw = (config&GRADCDAC_CFG_DACDW)>>GRADCDAC_CFG_DACDW_BIT; + + cfg->adc_ws = (config&GRADCDAC_CFG_ADCWS)>>GRADCDAC_CFG_ADCWS_BIT; + + cfg->rc_pol = (config&GRADCDAC_CFG_RCPOL)>>GRADCDAC_CFG_RCPOL_BIT; + + cfg->cs_mode = (config&GRADCDAC_CFG_CSMODE)>>GRADCDAC_CFG_CSMODE_BIT; + + cfg->cs_pol = (config&GRADCDAC_CFG_CSPOL)>>GRADCDAC_CFG_CSPOL_BIT; + + cfg->ready_mode = (config&GRADCDAC_CFG_RDYMODE)>>GRADCDAC_CFG_RDYMODE_BIT; + + cfg->ready_pol = (config&GRADCDAC_CFG_RDYPOL)>>GRADCDAC_CFG_RDYPOL_BIT; + + cfg->trigg_pol = (config&GRADCDAC_CFG_TRIGPOL)>>GRADCDAC_CFG_TRIGPOL_BIT; + + cfg->trigg_mode = (config&GRADCDAC_CFG_TRIGMODE)>>GRADCDAC_CFG_TRIGMODE_BIT; + + cfg->adc_dw = (config&GRADCDAC_CFG_ADCDW)>>GRADCDAC_CFG_ADCDW_BIT; +} + +void gradcdac_set_cfg(void *cookie, unsigned int config) +{ + struct gradcdac_priv *pDev = cookie; + pDev->regs->config = config; +} + +unsigned int gradcdac_get_cfg(void *cookie) +{ + struct gradcdac_priv *pDev = cookie; + return pDev->regs->config; +} + +unsigned int gradcdac_get_status(void *cookie) +{ + struct gradcdac_priv *pDev = cookie; + return pDev->regs->status; +} + +/* Install IRQ handler for ADC and/or DAC interrupt. + * The installed IRQ handler(ISR) must read the status + * register to clear the pending interrupt avoiding multiple + * entries to the ISR caused by the same IRQ. + * + * \param adc 1=ADC interrupt, 2=ADC interrupt, 3=ADC and DAC interrupt + * \param isr Interrupt service routine called when IRQ is fired + * \param arg custom argument passed to ISR when called. + */ +int gradcdac_install_irq_handler(void *cookie, int adc, void (*isr)(void *cookie, void *arg), void *arg) +{ + struct gradcdac_priv *pDev = cookie; + + if ( (adc > 3) || !adc ) + return -1; + + if ( adc & GRADCDAC_ISR_ADC ){ + pDev->isr_adc_arg = arg; + pDev->isr_adc = isr; + drvmgr_interrupt_register(pDev->dev, GRADCDAC_IRQ_ADC, "gradcdac_adc", gradcdac_adc_interrupt, pDev); + } + + if ( adc & GRADCDAC_ISR_DAC ){ + pDev->isr_dac_arg = arg; + pDev->isr_dac = isr; + drvmgr_interrupt_register(pDev->dev, GRADCDAC_IRQ_DAC, "gradcdac_dac", gradcdac_dac_interrupt, pDev); + } + + return 0; +} + +void gradcdac_uninstall_irq_handler(void *cookie, int adc) +{ + struct gradcdac_priv *pDev = cookie; + + if ( (adc > 3) || !adc ) + return; + + if ( adc & GRADCDAC_ISR_ADC ){ + drvmgr_interrupt_unregister(pDev->dev, GRADCDAC_IRQ_ADC, gradcdac_adc_interrupt, pDev); + pDev->isr_adc = NULL; + pDev->isr_adc_arg = NULL; + } + + if ( adc & GRADCDAC_ISR_DAC ){ + drvmgr_interrupt_unregister(pDev->dev, GRADCDAC_IRQ_DAC, gradcdac_dac_interrupt, pDev); + pDev->isr_dac = NULL; + pDev->isr_dac_arg = NULL; + } +} + +/* Make the ADC circuitry initialize a analogue to digital + * conversion. The result can be read out by gradcdac_adc_convert_try + * or gradcdac_adc_convert. + */ +void gradcdac_adc_convert_start(void *cookie) +{ + struct gradcdac_priv *pDev = cookie; + + /* Write to ADC Data Input register to start a conversion */ + pDev->regs->adc_din = 0; +} + +/* Tries to read the conversion result. If the circuitry is busy + * converting the function return a non-zero value, if the conversion + * has successfully finished the function return zero. + * + * \param digital_value the resulting converted value is placed here + * \return zero = ADC conversion complete, digital_value contain current conversion result + * positive = ADC busy, digital_value contain previous conversion result + * negative = Conversion request failed. + */ +int gradcdac_adc_convert_try(void *cookie, unsigned short *digital_value) +{ + struct gradcdac_priv *pDev = cookie; + unsigned int status; + + status = pDev->regs->status; + + if ( digital_value ){ + *digital_value = pDev->regs->adc_din; + } + + if ( gradcdac_ADC_isOngoing(status) ) + return 1; + + if ( gradcdac_ADC_isCompleted(status) ) + return 0; + + /* Failure */ + return -1; +} + +/* Waits until the ADC circuity has finished a digital to analogue + * conversion. The Waiting is implemented as a busy loop utilizing + * 100% CPU load. + */ +int gradcdac_adc_convert(void *cookie, unsigned short *digital_value) +{ + struct gradcdac_priv *pDev = cookie; + unsigned int status; + + do { + status=gradcdac_get_status(pDev); + }while ( gradcdac_ADC_isOngoing(status) ); + + if ( digital_value ) + *digital_value = pDev->regs->adc_din; + + if ( gradcdac_ADC_isCompleted(status) ) + return 0; + + return -1; +} + +/* Try to make the DAC circuitry initialize a digital to analogue + * conversion. If the circuitry is busy by a previous conversion + * the function return a non-zero value, if the conversion is + * successfully initialized the function return zero. + */ +int gradcdac_dac_convert_try(void *cookie, unsigned short digital_value) +{ + struct gradcdac_priv *pDev = cookie; + unsigned int status = pDev->regs->status; + + if ( gradcdac_DAC_isOngoing(status) ) + return -1; + + /* Force a new conversion */ + pDev->regs->dac_dout = digital_value; + + /* Return success */ + return 0; +} + +/* Initializes a digital to analogue conversion by waiting until + * previous conversions is finished before proceeding with the + * conversion. The Waiting is implemented as a busy loop utilizing + * 100% CPU load. + */ +void gradcdac_dac_convert(void *cookie, unsigned short digital_value) +{ + struct gradcdac_priv *pDev = cookie; + unsigned int status; + + do { + status = gradcdac_get_status(pDev); + }while( gradcdac_DAC_isOngoing(status) ); + + pDev->regs->dac_dout = digital_value; +} + +unsigned int gradcdac_get_adrinput(void *cookie) +{ + struct gradcdac_priv *pDev = cookie; + return pDev->regs->adrin; +} + +void gradcdac_set_adrinput(void *cookie, unsigned int input) +{ + struct gradcdac_priv *pDev = cookie; + pDev->regs->adrin = input; +} + +unsigned int gradcdac_get_adroutput(void *cookie) +{ + struct gradcdac_priv *pDev = cookie; + return pDev->regs->adrout; +} + +void gradcdac_set_adroutput(void *cookie, unsigned int output) +{ + struct gradcdac_priv *pDev = cookie; + pDev->regs->adrout = output; +} + +unsigned int gradcdac_get_adrdir(void *cookie) +{ + struct gradcdac_priv *pDev = cookie; + return pDev->regs->adrdir; +} + +void gradcdac_set_adrdir(void *cookie, unsigned int dir) +{ + struct gradcdac_priv *pDev = cookie; + pDev->regs->adrdir = dir; +} + +unsigned int gradcdac_get_datainput(void *cookie) +{ + struct gradcdac_priv *pDev = cookie; + return pDev->regs->data_in; +} + +void gradcdac_set_datainput(void *cookie, unsigned int input) +{ + struct gradcdac_priv *pDev = cookie; + pDev->regs->data_in = input; +} + +unsigned int gradcdac_get_dataoutput(void *cookie) +{ + struct gradcdac_priv *pDev = cookie; + return pDev->regs->data_out; +} + +void gradcdac_set_dataoutput(void *cookie, unsigned int output) +{ + struct gradcdac_priv *pDev = cookie; + pDev->regs->data_out = output; +} + +unsigned int gradcdac_get_datadir(void *cookie) +{ + struct gradcdac_priv *pDev = cookie; + return pDev->regs->data_dir; +} + +void gradcdac_set_datadir(void *cookie, unsigned int dir) +{ + struct gradcdac_priv *pDev = cookie; + pDev->regs->data_dir = dir; +} |