/* 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 #include #include #include #include #include #include /****************** 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 #include 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 = grlib_calloc(1, sizeof(*priv)); if ( !priv ) return DRVMGR_NOMEM; 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<wr_pol ) config |= GRADCDAC_CFG_WRPOL; config |= (cfg->dac_dw<adc_ws<rc_pol ) config |= GRADCDAC_CFG_RCPOL; config |= (cfg->cs_mode<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<adc_dw<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; }