diff options
author | Daniel Hellstrom <daniel@gaisler.com> | 2015-02-23 13:02:39 +0100 |
---|---|---|
committer | Daniel Hellstrom <daniel@gaisler.com> | 2015-04-17 01:10:17 +0200 |
commit | 3bb41226e0941b86d58ecb97f7d292677de573c8 (patch) | |
tree | 907aa270343f7c6d1bc08bf73288fb9b10da6197 /c/src/lib/libbsp/sparc/shared/pwm | |
parent | LEON: added network device configuration helper function (diff) | |
download | rtems-3bb41226e0941b86d58ecb97f7d292677de573c8.tar.bz2 |
LEON: added new drivers to the LEON2/LEON3 BSPs
Most drivers use the Driver Manager for device probing, they
work on AMBA-over-PCI systems if PCI is big-endian.
New APIs:
* GPIO Library, interfaced to GRGPIO
* GENIRQ, Generic interrupt service implementation helper
New GRLIB Drivers:
* ACTEL 1553 RT, user interface is similar to 1553 BRM driver
* GR1553 (1553 BC, RT and BM core)
* AHBSTAT (AHB error status core)
* GRADCDAC (Core interfacing to ADC/DAC hardware)
* GRGPIO (GPIO port accessed from GPIO Library)
* MCTRL (Memory controller settings configuration)
* GRETH (10/100/1000 Ethernet driver using Driver manager)
* GRPWM (Pulse Width Modulation core)
* SPICTRL (SPI master interface)
* GRSPW_ROUTER (SpaceWire Router AMBA configuration interface)
* GRCTM (SpaceCraft on-board Time Management core)
* SPWCUC (Time distribution over SpaceWire)
* GRTC (SpaceCraft up-link Tele core)
* GRTM (SpaceCraft down-link Tele Metry core)
GR712RC ASIC specific interfaces:
* GRASCS
* CANMUX (select between OCCAN and SATCAN)
* SATCAN
* SLINK
Diffstat (limited to 'c/src/lib/libbsp/sparc/shared/pwm')
-rw-r--r-- | c/src/lib/libbsp/sparc/shared/pwm/grpwm.c | 847 |
1 files changed, 847 insertions, 0 deletions
diff --git a/c/src/lib/libbsp/sparc/shared/pwm/grpwm.c b/c/src/lib/libbsp/sparc/shared/pwm/grpwm.c new file mode 100644 index 0000000000..753a6807f3 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/pwm/grpwm.c @@ -0,0 +1,847 @@ +/* + * GRPWM PWM Driver interface. + * + * 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.com/license/LICENSE. + */ + +#include <bsp.h> +#include <rtems/libio.h> +#include <stdlib.h> +#include <assert.h> +#include <rtems/bspIo.h> +#include <string.h> +#include <stdio.h> + +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> +#include <grpwm.h> +#include <ambapp.h> + +/* #define DEBUG 1 */ + +#ifdef DEBUG +#define DBG(x...) printk(x) +#define STATIC +#else +#define DBG(x...) +#define STATIC static +#endif + +/*** REGISTER LAYOUT ***/ + +/* PWM Channel specific registers */ +struct grpwm_pwm_regs { + volatile unsigned int period; /* 0x00 */ + volatile unsigned int comp; /* 0x04 */ + volatile unsigned int dbcomp; /* 0x08 */ + volatile unsigned int ctrl; /* 0x0C */ +}; + +/* Core common registers */ +struct grpwm_regs { + volatile unsigned int ctrl; /* 0x00 */ + volatile unsigned int scaler; /* 0x04 */ + volatile unsigned int ipend; /* 0x08 */ + volatile unsigned int cap1; /* 0x0C */ + volatile unsigned int cap2; /* 0x10 */ + volatile unsigned int wctrl; /* 0x14 */ + int reserved0[2]; + struct grpwm_pwm_regs pwms[8]; /* 0x20 */ + int reserved1[(0x8000-0xA0)/4]; /* 0xA0-0x7FFC */ + volatile unsigned int wram[0x8000/4]; /* 0x8000-0xFFFC */ +}; + +/*** REGISTER BIT LAYOUT ***/ + +/* CTRL REGISTER - 0x0 */ +#define GRPWM_CTRL_EN_BIT 0 +#define GRPWM_CTRL_SCSEL_BIT 8 +#define GRPWM_CTRL_NOUP_BIT 12 +#define GRPWM_CTRL_EN (1<<GRPWM_CTRL_EN_BIT) +#define GRPWM_CTRL_SCSEL (0x7<<GRPWM_CTRL_SCSEL_BIT) +#define GRPWM_CTRL_NOUP (0xff<<GRPWM_CTRL_NOUP_BIT) + + +/* CAPABILITY1 REGISTER - 0x0C */ +#define GRPWM_CAP_NPWM_BIT 0 +#define GRPWM_CAP_PBITS_BIT 3 +#define GRPWM_CAP_SBITS_BIT 8 +#define GRPWM_CAP_NSC_BIT 13 +#define GRPWM_CAP_DBB_BIT 16 +#define GRPWM_CAP_DBSC_BIT 21 +#define GRPWM_CAP_ASY_BIT 22 +#define GRPWM_CAP_SYM_BIT 23 +#define GRPWM_CAP_SEP_BIT 25 +#define GRPWM_CAP_DCM_BIT 27 + +#define GRPWM_CAP_NPWM (0x7<<GRPWM_CAP_NPWM_BIT) +#define GRPWM_CAP_PBITS (0x1f<<GRPWM_CAP_PBITS_BIT) +#define GRPWM_CAP_SBITS (0x1f<<GRPWM_CAP_SBITS_BIT) +#define GRPWM_CAP_NSC (0x7<<GRPWM_CAP_NSC_BIT) +#define GRPWM_CAP_DBB (0x1f<<GRPWM_CAP_DBB_BIT) +#define GRPWM_CAP_DBSC (1<<GRPWM_CAP_DBSC_BIT) +#define GRPWM_CAP_ASY (1<<GRPWM_CAP_ASY_BIT) +#define GRPWM_CAP_SYM (1<<GRPWM_CAP_SYM_BIT) +#define GRPWM_CAP_SEP (0x3<<GRPWM_CAP_SEP_BIT) +#define GRPWM_CAP_DCM (1<<GRPWM_CAP_DCM_BIT) + +/* CAPABILITY2 REGISTER - 0x10 */ +#define GRPWM_CAP2_WPWM_BIT 0 +#define GRPWM_CAP2_WDBITS_BIT 1 +#define GRPWM_CAP2_WABITS_BIT 6 +#define GRPWM_CAP2_WSYNC_BIT 10 + +#define GRPWM_CAP2_WPWM (0x1<<GRPWM_CAP2_WPWM_BIT) +#define GRPWM_CAP2_WDBITS (0x1f<<GRPWM_CAP2_WDBITS_BIT) +#define GRPWM_CAP2_WABITS (0xf<<GRPWM_CAP2_WABITS_BIT) +#define GRPWM_CAP2_WSYNC (1<<GRPWM_CAP2_WSYNC_BIT) + +/* WAVE FORM CONFIG REGISTER - 0x14 */ +#define GRPWM_WCTRL_STOP_BIT 0 +#define GRPWM_WCTRL_WSYNC_BIT 16 +#define GRPWM_WCTRL_WSEN_BIT 29 +#define GRPWM_WCTRL_WSYNCCFG_BIT 30 + +#define GRPWM_WCTRL_STOP (0x1fff<<GRPWM_WCTRL_STOP_BIT) +#define GRPWM_WCTRL_WSYNC (0x1fff<<GRPWM_WCTRL_WSYNC_BIT) +#define GRPWM_WCTRL_WSEN (0x1<<GRPWM_WCTRL_WSEN_BIT) +#define GRPWM_WCTRL_WSYNCCFG (0x3<<GRPWM_WCTRL_WSYNCCFG_BIT) + + +/* PWM CONTROL REGISTER - 0x2C, 0x3C... */ +#define GRPWM_PCTRL_EN_BIT 0 +#define GRPWM_PCTRL_POL_BIT 1 +#define GRPWM_PCTRL_PAIR_BIT 2 +#define GRPWM_PCTRL_FIX_BIT 3 +#define GRPWM_PCTRL_METH_BIT 6 +#define GRPWM_PCTRL_DCEN_BIT 8 +#define GRPWM_PCTRL_WEN_BIT 9 +#define GRPWM_PCTRL_SCSEL_BIT 10 +#define GRPWM_PCTRL_IEN_BIT 13 +#define GRPWM_PCTRL_IT_BIT 14 +#define GRPWM_PCTRL_ISC_BIT 15 +#define GRPWM_PCTRL_DBEN_BIT 21 +#define GRPWM_PCTRL_DBSC_BIT 22 +#define GRPWM_PCTRL_FLIP_BIT 26 + +#define GRPWM_PCTRL_EN (0x1<<GRPWM_PCTRL_EN_BIT) +#define GRPWM_PCTRL_POL (0x1<<GRPWM_PCTRL_POL_BIT) +#define GRPWM_PCTRL_PAIR (0x1<<GRPWM_PCTRL_PAIR_BIT) +#define GRPWM_PCTRL_FIX (0x7<<GRPWM_PCTRL_FIX_BIT) +#define GRPWM_PCTRL_METH (0x1<<GRPWM_PCTRL_METH_BIT) +#define GRPWM_PCTRL_DCEN (0x1<<GRPWM_PCTRL_DCEN_BIT) +#define GRPWM_PCTRL_WEN (0x1<<GRPWM_PCTRL_WEN_BIT) +#define GRPWM_PCTRL_SCSEL (0x7<<GRPWM_PCTRL_SCSEL_BIT) +#define GRPWM_PCTRL_IEN (0x1<<GRPWM_PCTRL_IEN_BIT) +#define GRPWM_PCTRL_IT (0x1<<GRPWM_PCTRL_IT_BIT) +#define GRPWM_PCTRL_ISC (0x3f<<GRPWM_PCTRL_ISC_BIT) +#define GRPWM_PCTRL_DBEN (0x1<<GRPWM_PCTRL_DBEN_BIT) +#define GRPWM_PCTRL_DBSC (0xf<<GRPWM_PCTRL_DBSC_BIT) +#define GRPWM_PCTRL_FLIP (0xf<<GRPWM_PCTRL_FLIP_BIT) + +/*** DRIVER PRIVATE STRUCTURE ***/ +struct grpwm_priv { + struct drvmgr_dev *dev; + struct grpwm_regs *regs; + char devName[32]; + int irq; + int open; + + /* Driver implementation */ + char nscalers; /* Number of scalers */ + char wave; /* If Wave form is available */ + int wlength; /* Wave Form RAM Length */ + int channel_cnt; + struct grpwm_chan_priv *channels[8]; + rtems_id dev_sem; +}; + +struct grpwm_chan_priv { + struct grpwm_priv *common; + struct grpwm_pwm_regs *pwmregs; + /* IRQ */ + int irqindex; + void (*isr)(int channel, void *arg); + void *isr_arg; +}; + +/******************* Driver Manager Part ***********************/ + +int grpwm_device_init(struct grpwm_priv *priv); +int grpwm_register_io(rtems_device_major_number *m); +static int grpwm_driver_io_registered = 0; +static rtems_device_major_number grpwm_driver_io_major = 0; + +int grpwm_init2(struct drvmgr_dev *dev); +int grpwm_init3(struct drvmgr_dev *dev); + +struct drvmgr_drv_ops grpwm_ops = +{ + .init = {NULL, grpwm_init2, grpwm_init3, NULL}, + .remove = NULL, + .info = NULL +}; + +struct amba_dev_id grpwm_ids[] = +{ + {VENDOR_GAISLER, GAISLER_GRPWM}, + {0, 0} /* Mark end of table */ +}; + +struct amba_drv_info grpwm_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_GRPWM_ID, /* Driver ID */ + "GRPWM_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &grpwm_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &grpwm_ids[0] +}; + +void grpwm_register_drv (void) +{ + DBG("Registering GRPWM driver\n"); + drvmgr_drv_register(&grpwm_drv_info.general); +} + +int grpwm_init2(struct drvmgr_dev *dev) +{ + struct grpwm_priv *priv; + + DBG("GRPWM[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); + + priv = dev->priv = malloc(sizeof(struct grpwm_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 grpwm_init3(struct drvmgr_dev *dev) +{ + struct grpwm_priv *priv = dev->priv; + char prefix[32]; + rtems_status_code status; + + if ( !priv ) + return DRVMGR_FAIL; + + if ( grpwm_driver_io_registered == 0) { + /* Register the I/O driver only once for all cores */ + if ( grpwm_register_io(&grpwm_driver_io_major) ) { + /* Failed to register I/O driver */ + dev->priv = NULL; + return DRVMGR_FAIL; + } + + grpwm_driver_io_registered = 1; + } + + /* I/O system registered and initialized + * Now we take care of device initialization. + */ + if ( grpwm_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/grpwm%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/%sgrpwm%d", prefix, dev->minor_bus); + } + + /* Register Device */ + status = rtems_io_register_name(priv->devName, grpwm_driver_io_major, + dev->minor_drv); + if (status != RTEMS_SUCCESSFUL) { + return DRVMGR_FAIL; + } + + return DRVMGR_OK; +} + +/******************* Driver Implementation ***********************/ + +static rtems_device_driver grpwm_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver grpwm_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver grpwm_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver grpwm_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver grpwm_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver grpwm_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); + +#define GRPWM_DRIVER_TABLE_ENTRY { grpwm_initialize, grpwm_open, grpwm_close, grpwm_read, grpwm_write, grpwm_ioctl } + +static rtems_driver_address_table grpwm_driver = GRPWM_DRIVER_TABLE_ENTRY; + +int grpwm_register_io(rtems_device_major_number *m) +{ + rtems_status_code r; + + if ((r = rtems_io_register_driver(0, &grpwm_driver, m)) == RTEMS_SUCCESSFUL) { + DBG("GRPWM driver successfully registered, major: %d\n", *m); + } else { + switch(r) { + case RTEMS_TOO_MANY: + DBG("GRPWM rtems_io_register_driver failed: RTEMS_TOO_MANY\n"); + return -1; + case RTEMS_INVALID_NUMBER: + DBG("GRPWM rtems_io_register_driver failed: RTEMS_INVALID_NUMBER\n"); + return -1; + case RTEMS_RESOURCE_IN_USE: + DBG("GRPWM rtems_io_register_driver failed: RTEMS_RESOURCE_IN_USE\n"); + return -1; + default: + DBG("GRPWM rtems_io_register_driver failed\n"); + return -1; + } + } + return 0; +} + +void grpwm_scaler_set(struct grpwm_regs *regs, int scaler, unsigned int value) +{ + /* Select scaler */ + regs->ctrl = (regs->ctrl & ~GRPWM_CTRL_SCSEL) | (scaler << GRPWM_CTRL_SCSEL_BIT); + /* Write scaler */ + regs->scaler = value; +} + +/* Write Wave form RAM */ +void grpwm_write_wram(struct grpwm_regs *regs, unsigned int *data, int length) +{ + unsigned int *end; + volatile unsigned int *pos; + + pos = ®s->wram[0]; + + /* Write RAM */ + if ( data ) { + end = data + length; + while ( data < end ) { + *pos++ = *data++; + } + } else { + while( length > 0 ) { + *pos++ = 0; + length -= 4; + } + } +} + +void grpwm_hw_reset(struct grpwm_priv *priv) +{ + int i; + struct grpwm_chan_priv *pwm; + struct grpwm_regs *regs = priv->regs; + + /* Disable Core */ + regs->ctrl = 0; + + /* Clear all registers */ + regs->ipend = 0xffffffff; + regs->wctrl = 0; + + /* Init all PWM channels */ + for (i=0; i<priv->channel_cnt; i++) { + pwm = priv->channels[i]; + pwm->pwmregs->ctrl = 0; + pwm->pwmregs->period = 0; + pwm->pwmregs->comp = 0; + pwm->pwmregs->dbcomp = 0; + pwm->pwmregs->ctrl = 0; /* Twice because METH and POL requires EN=0 */ + } + + /* Clear RAM */ + if ( priv->wave ) { + grpwm_write_wram(regs, NULL, priv->wlength); + } + + /* Set max scaler */ + for (i=0; i<priv->nscalers; i++) { + grpwm_scaler_set(regs, i, 0xffffffff); + } +} + +/* Update one Channel but leaves the "Hold update" bit set + * + * A bit mask of updated bits are returned. + */ +unsigned int grpwm_update_prepare_channel( + struct grpwm_priv *priv, + int channel, + struct grpwm_ioctl_update_chan *up + ) +{ + struct grpwm_chan_priv *pwm; + struct grpwm_pwm_regs *pwmregs; + unsigned int ctrl; + unsigned int ret; + + pwm = priv->channels[channel]; + pwmregs = pwm->pwmregs; + + /* Read channel control register */ + ctrl = pwmregs->ctrl; + ret = 0; + + if ( up->options & GRPWM_UPDATE_OPTION_DISABLE ) { + ctrl &= ~GRPWM_PCTRL_EN; + pwmregs->ctrl = ctrl; + ret |= GRPWM_PCTRL_EN; + } + + /* Hold the updates */ + if ( up->options & (GRPWM_UPDATE_OPTION_PERIOD| + GRPWM_UPDATE_OPTION_COMP|GRPWM_UPDATE_OPTION_DBCOMP) ) { + + if ( up->options & (GRPWM_UPDATE_OPTION_PERIOD) ) { + DBG("GRPWM: UPDATING 0x%x: 0x%x\n", &pwmregs->period, up->period); + pwmregs->period = up->period; + } + if ( up->options & (GRPWM_UPDATE_OPTION_COMP) ) { + DBG("GRPWM: UPDATING 0x%x: 0x%x\n", &pwmregs->comp, up->compare); + pwmregs->comp = up->compare; + } + if ( up->options & (GRPWM_UPDATE_OPTION_DBCOMP) ) { + DBG("GRPWM: UPDATING 0x%x: 0x%x\n", &pwmregs->dbcomp, up->dbcomp); + pwmregs->dbcomp = up->dbcomp; + } + } + + if ( up->options & GRPWM_UPDATE_OPTION_ENABLE ) { + ret |= GRPWM_PCTRL_EN; + pwmregs->ctrl = ctrl | GRPWM_PCTRL_EN; + } + return ret; +} + +void grpwm_update_active(struct grpwm_priv *priv, int enable) +{ + unsigned int ctrl; + int i; + + ctrl = priv->regs->ctrl; + + /* Make all "Update Hold" bits be cleared */ + ctrl &= ~GRPWM_CTRL_NOUP; + + /* A change in any of the Channel enable/disable bits? */ + if ( enable ) { + ctrl &= ~GRPWM_CTRL_EN; + for(i=0; i<priv->channel_cnt; i++) { + ctrl |= priv->regs->pwms[i].ctrl & GRPWM_CTRL_EN; + } + } + priv->regs->ctrl = ctrl; +} + +/* Configure the hardware of a channel according to this */ +rtems_status_code grpwm_config_channel( + struct grpwm_priv *priv, + int channel, + struct grpwm_ioctl_config *cfg + ) +{ + struct grpwm_chan_priv *pwm; + unsigned int pctrl, wctrl=0; + + pwm = priv->channels[channel]; + if ( pwm->pwmregs->ctrl & GRPWM_PCTRL_EN_BIT ) { + return RTEMS_RESOURCE_IN_USE; + } + if ( cfg->options & ~GRPWM_CONFIG_OPTION_MASK ) { + return RTEMS_INVALID_NAME; + } + if ( (cfg->options & GRPWM_CONFIG_OPTION_DUAL) && + ((priv->regs->cap1 & GRPWM_CAP_DCM) == 0) ) { + return RTEMS_INVALID_NAME; + } + /* IRQ set up */ + pwm->isr_arg = cfg->isr_arg; + pwm->isr = cfg->isr; + + pctrl = cfg->options | + (cfg->dbscaler << GRPWM_PCTRL_DBSC_BIT) | + (cfg->irqscaler << GRPWM_PCTRL_ISC_BIT) | + (cfg->scaler_index << GRPWM_PCTRL_SCSEL_BIT); + + /* Set Wave form gerneration if available */ + if ( !priv->wave || (priv->channel_cnt != (channel+1)) ) { + /* Wave Form not available for this channel (or core) */ + if ( cfg->wave_activate || cfg->wave_data || cfg->wave_data_length ) { + return RTEMS_INVALID_NAME; + } + } else if ( cfg->wave_activate ) { + /* Enable Wave form generation */ + DBG("GRPWM: ENABLING WAVE FORM GENERATION 0x%x\n", cfg->wave_data_length); + + if ( cfg->wave_data ) { + grpwm_write_wram(priv->regs, cfg->wave_data, cfg->wave_data_length); + } + + /* Write length register, and let user control Wave-Sync functionality */ + wctrl = (((cfg->wave_data_length-1) << GRPWM_WCTRL_STOP_BIT) & GRPWM_WCTRL_STOP); + wctrl |= cfg->wave_synccfg & (GRPWM_WCTRL_WSYNCCFG|GRPWM_WCTRL_WSEN); + wctrl |= (cfg->wave_sync << 16) & 0x1fff0000; + priv->regs->wctrl = wctrl; + + /* Enable Wave form */ + pctrl |= GRPWM_PCTRL_WEN; + } + + DBG("GRPWM: CONFIG: 0x%x, WAVE CONFIG: 0x%x\n", pctrl, wctrl); + + pwm->pwmregs->ctrl = pctrl; + + return RTEMS_SUCCESSFUL; +} + +static void grpwm_isr(void *arg) +{ + unsigned int ipend; + struct grpwm_chan_priv *pwm = arg; + struct grpwm_priv *priv = pwm->common; + int i; + + /* Get current pending interrupts */ + ipend = priv->regs->ipend; + + for (i=0; i<priv->channel_cnt; i++) { + if ( ipend & (1<<i) ) { + pwm = priv->channels[i]; + if ( pwm->isr ) { + pwm->isr(i, pwm->isr_arg); + } + } + } + priv->regs->ipend = ipend; +} + +static rtems_device_driver grpwm_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver grpwm_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + struct grpwm_priv *priv; + rtems_device_driver ret; + struct drvmgr_dev *dev; + + if ( drvmgr_get_dev(&grpwm_drv_info.general, minor, &dev) ) { + DBG("Wrong minor %d\n", minor); + return RTEMS_INVALID_NAME; + } + priv = (struct grpwm_priv *)dev->priv; + + /* Wait until we get semaphore */ + if ( rtems_semaphore_obtain(priv->dev_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) != + RTEMS_SUCCESSFUL ){ + return RTEMS_INTERNAL_ERROR; + } + + /* is device busy/taken? */ + if ( priv->open ) { + ret=RTEMS_RESOURCE_IN_USE; + goto out; + } + + /* Mark device taken */ + priv->open = 1; + + ret = RTEMS_SUCCESSFUL; +out: + rtems_semaphore_release(priv->dev_sem); + return ret; +} + +static rtems_device_driver grpwm_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + struct grpwm_priv *priv; + struct drvmgr_dev *dev; + + if ( drvmgr_get_dev(&grpwm_drv_info.general, minor, &dev) ) { + return RTEMS_INVALID_NAME; + } + priv = (struct grpwm_priv *)dev->priv; + + /* Reset Hardware */ + grpwm_hw_reset(priv); + + /* Mark Device closed */ + priv->open = 0; + + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver grpwm_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + return RTEMS_UNSATISFIED; +} + +static rtems_device_driver grpwm_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + return RTEMS_UNSATISFIED; +} + +static rtems_device_driver grpwm_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + + struct grpwm_priv *priv; + struct drvmgr_dev *dev; + rtems_libio_ioctl_args_t *ioarg = (rtems_libio_ioctl_args_t *)arg; + + if ( drvmgr_get_dev(&grpwm_drv_info.general, minor, &dev) ) { + return RTEMS_INVALID_NAME; + } + priv = (struct grpwm_priv *)dev->priv; + + if (!ioarg) + return RTEMS_INVALID_NAME; + + ioarg->ioctl_return = 0; + switch(ioarg->command) { + default: /* Not a valid command */ + return RTEMS_NOT_DEFINED; + + case GRPWM_IOCTL_GET_CAP: + { + struct grpwm_ioctl_cap *cap = (void *)ioarg->buffer; + if ( cap == NULL ) + return RTEMS_INVALID_NAME; + + /* Copy Capability registers to user */ + cap->channel_cnt = priv->channel_cnt; + cap->pwm = priv->regs->cap1; + cap->wave = priv->regs->cap2; + break; + } + case GRPWM_IOCTL_SET_CONFIG: + { + struct grpwm_ioctl_config *cfg = (void *)ioarg->buffer; + if ( cfg == NULL ) + return RTEMS_INVALID_NAME; + if ( cfg->channel >= priv->channel_cnt ) + return RTEMS_INVALID_NAME; + + return grpwm_config_channel(priv, cfg->channel, cfg); + } + case GRPWM_IOCTL_SET_SCALER: + { + unsigned int invalid_mask; + int i; + struct grpwm_ioctl_scaler *sc = ioarg->buffer; + + if ( sc == NULL ) + return RTEMS_INVALID_NAME; + + /* Test if caller reqest to set a scaler not existing */ + invalid_mask = ~((1 << priv->nscalers) - 1); + if ( invalid_mask & sc->index_mask ) { + return RTEMS_INVALID_NAME; + } + + /* Set scalers requested */ + for (i=0; i<priv->nscalers; i++) { + if ( sc->index_mask & (1<<i) ) { + /* Update Scaler 'i' */ + grpwm_scaler_set(priv->regs, i, sc->values[i]); + } + } + break; + } + case GRPWM_IOCTL_UPDATE: + { + struct grpwm_ioctl_update *up = ioarg->buffer; + unsigned int invalid_mask, pctrl = 0; + int i; + + if ( up == NULL ) + return RTEMS_INVALID_NAME; + + /* Test if caller reqest to set a scaler not existing */ + invalid_mask = ~((1 << priv->channel_cnt) - 1); + if ( invalid_mask & up->chanmask ) { + return RTEMS_INVALID_NAME; + } + + /* In order for the changes to take effect at the same time, the "Hold update" + * bits is set for all PWM channels that will be updated. The hold update bits + * will be cleared at the same time for all channels. + */ + priv->regs->ctrl = (priv->regs->ctrl & ~GRPWM_CTRL_NOUP) | + (up->chanmask << GRPWM_CTRL_NOUP_BIT); + + for (i=0; i<priv->channel_cnt; i++) { + if ( up->chanmask & (1<<i) ) { + /* Prepare update channel 'i' */ + pctrl |= grpwm_update_prepare_channel(priv, i, &up->channels[i]); + } + } + + /* 1. Update all channels requested, + * 2. Enable the core if at least one channel is enabled + * 3. Disable the core if all channels are disabled + */ + grpwm_update_active(priv, (pctrl & GRPWM_PCTRL_EN)); + + break; + } + case GRPWM_IOCTL_IRQ: + { + unsigned int data = (unsigned int)ioarg->buffer; + int channel = (data >> 8) & 0x7; + struct grpwm_chan_priv *pwm; + unsigned int pctrl; + + pwm = priv->channels[channel]; + + if ( data & GRPWM_IRQ_CLEAR ) { + priv->regs->ipend |= (1<<channel); + drvmgr_interrupt_clear(priv->dev, pwm->irqindex); + } + if ( (data & 0x3) && !pwm->isr ) { + /* Enable IRQ but no ISR */ + return RTEMS_INVALID_NAME; + } + pctrl = pwm->pwmregs->ctrl & ~(GRPWM_PCTRL_IEN|GRPWM_PCTRL_IT); + pctrl |= ((data & 0x3) << GRPWM_PCTRL_IEN_BIT); + pwm->pwmregs->ctrl = pctrl; + break; + } + } + + return RTEMS_SUCCESSFUL; +} + +#define MAX_CHANNEL 8 +char grpwm_irqindex_lookup[8][MAX_CHANNEL] = +{ +/* Channel 1 2 3 4 5 6 7 8 */ +/* npwm 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* npwm 2 */ {0, 1, 0, 0, 0, 0, 0, 0}, +/* npwm 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* npwm 4 */ {0, 0, 0, 1, 0, 0, 0, 0}, +/* npwm 5 */ {0, 0, 0, 1, 2, 0, 0, 0}, +/* npwm 6 */ {0, 0, 0, 1, 1, 1, 0, 0}, +/* npwm 7 */ {0, 0, 0, 1, 1, 1, 2, 0}, +/* npwm 8 */ {0, 0, 0, 1, 1, 1, 2, 3} +}; + +int grpwm_device_init(struct grpwm_priv *priv) +{ + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + int mask, i, sepirq; + unsigned int wabits; + struct grpwm_chan_priv *pwm; + struct grpwm_regs *regs; + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)priv->dev->businfo; + if ( ambadev == NULL ) { + return -1; + } + pnpinfo = &ambadev->info; + priv->irq = pnpinfo->irq; + regs = priv->regs = (struct grpwm_regs *)pnpinfo->apb_slv->start; + + DBG("GRPWM: 0x%08x irq %d\n", (unsigned int)regs, priv->irq); + + /* Disable Core */ + regs->ctrl = 0; + + /* Clear all registers */ + regs->ipend = 0xffffffff; + regs->wctrl = 0; + + /* Find the number of PWM channels */ + priv->channel_cnt = 1 + ((regs->cap1 & GRPWM_CAP_NPWM) >> GRPWM_CAP_NPWM_BIT); + pwm = malloc(sizeof(*pwm)*priv->channel_cnt); + if ( !pwm ) + return -1; + memset(pwm, 0, sizeof(*pwm)*priv->channel_cnt); + + /* Init all PWM channels */ + sepirq = ((regs->cap1 & GRPWM_CAP_SEP) >> GRPWM_CAP_SEP_BIT); + for (i=0; i<priv->channel_cnt; i++, pwm++) { + priv->channels[i] = pwm; + pwm->common = priv; + pwm->pwmregs = ®s->pwms[i]; + if ( sepirq == 0 ) { + pwm->irqindex = 0; + } else if ( sepirq == 1 ) { + pwm->irqindex = i; + } else { + pwm->irqindex = grpwm_irqindex_lookup[priv->channel_cnt][i]; + } + } + + /* Detect if Wave Form capability is availble for last PWM channel */ + if ( regs->cap2 & GRPWM_CAP2_WPWM ) { + priv->wave = 1; + + /* Clear RAM */ + wabits = (regs->cap2 & GRPWM_CAP2_WABITS) >> GRPWM_CAP2_WABITS_BIT; + priv->wlength = 1 << wabits; + } + priv->nscalers = 1 + ((regs->cap1 & GRPWM_CAP_NSC) >> GRPWM_CAP_NSC_BIT); + + grpwm_hw_reset(priv); + + /* Device Semaphore created with count = 1 */ + if ( rtems_semaphore_create(rtems_build_name('G', 'P', 'W', 'M'), + 1, + RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|\ + RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, + 0, + &priv->dev_sem) != RTEMS_SUCCESSFUL ) { + return -1; + } + + /* Register interrupt handler for all PWM channels */ + mask = 0; + for (i=0; i<priv->channel_cnt; i++) { + pwm = priv->channels[i]; + if ( (mask & (1 << pwm->irqindex)) == 0 ) { + /* Not registered interrupt handler for this IRQ index before, + * we do it now. + */ + mask |= (1 << pwm->irqindex); + drvmgr_interrupt_register( + priv->dev, + pwm->irqindex, + "grpwm", + grpwm_isr, + pwm); + } + } + + return 0; +} |