diff options
Diffstat (limited to 'drivers/dahdi/voicebus/GpakCust.c')
-rw-r--r-- | drivers/dahdi/voicebus/GpakCust.c | 682 |
1 files changed, 682 insertions, 0 deletions
diff --git a/drivers/dahdi/voicebus/GpakCust.c b/drivers/dahdi/voicebus/GpakCust.c new file mode 100644 index 0000000..386c916 --- /dev/null +++ b/drivers/dahdi/voicebus/GpakCust.c @@ -0,0 +1,682 @@ +/* + * Copyright (c) 2005, Adaptive Digital Technologies, Inc. + * Copyright (c) 2005-2009, Digium Incorporated + * + * File Name: GpakCust.c + * + * Description: + * This file contains host system dependent functions to support generic + * G.PAK API functions. The file is integrated into the host processor + * connected to C55x G.PAK DSPs via a Host Port Interface. + * + * Note: This file is supplied by Adaptive Digital Technologies and + * modified by Digium in order to support the VPMADT032 modules. + * + * This program has been released under the terms of the GPL version 2 by + * permission of Adaptive Digital Technologies, Inc. + * + */ + +/* + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2 as published by the + * Free Software Foundation. See the LICENSE file included with + * this program for more details. + */ + +#include <linux/version.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/pci.h> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +#include <linux/semaphore.h> +#else +#include <asm/semaphore.h> +#endif + +#include <dahdi/kernel.h> +#include <dahdi/user.h> + +#include "GpakCust.h" +#include "GpakApi.h" + +#include "adt_lec.h" +#include "voicebus.h" +#include "vpmadtreg.h" + +static rwlock_t ifacelock; +static struct vpmadt032 *ifaces[MAX_DSP_CORES]; + +static inline struct vpmadt032 *find_iface(const unsigned short dspid) +{ + struct vpmadt032 *ret; + + read_lock(&ifacelock); + if (ifaces[dspid]) { + ret = ifaces[dspid]; + } else { + ret = NULL; + } + read_unlock(&ifacelock); + return ret; +} + +static struct vpmadt032_cmd *vpmadt032_get_free_cmd(struct vpmadt032 *vpm) +{ + unsigned long flags; + struct vpmadt032_cmd *cmd; + might_sleep(); + spin_lock_irqsave(&vpm->list_lock, flags); + if (list_empty(&vpm->free_cmds)) { + spin_unlock_irqrestore(&vpm->list_lock, flags); + cmd = kmalloc(sizeof(struct vpmadt032_cmd), GFP_KERNEL); + if (unlikely(!cmd)) + return NULL; + memset(cmd, 0, sizeof(*cmd)); + } else { + cmd = list_entry(vpm->free_cmds.next, struct vpmadt032_cmd, node); + list_del_init(&cmd->node); + spin_unlock_irqrestore(&vpm->list_lock, flags); + } + init_completion(&cmd->complete); + return cmd; +} + +/* Wait for any outstanding commands to the VPMADT032 to complete */ +static inline int vpmadt032_io_wait(struct vpmadt032 *vpm) +{ + unsigned long flags; + int empty; + while (1) { + spin_lock_irqsave(&vpm->list_lock, flags); + empty = list_empty(&vpm->pending_cmds) && list_empty(&vpm->active_cmds); + spin_unlock_irqrestore(&vpm->list_lock, flags); + if (empty) { + break; + } else { + msleep(1); + } + } + return 0; +} + +/* Issue a read command to a register on the VPMADT032. We'll get the results + * later. */ +static struct vpmadt032_cmd *vpmadt032_getreg_full_async(struct vpmadt032 *vpm, int pagechange, + unsigned short addr) +{ + unsigned long flags; + struct vpmadt032_cmd *cmd; + cmd = vpmadt032_get_free_cmd(vpm); + if (!cmd) + return NULL; + cmd->desc = (pagechange) ? __VPM150M_RWPAGE | __VPM150M_RD : __VPM150M_RD; + cmd->address = addr; + cmd->data = 0; + spin_lock_irqsave(&vpm->list_lock, flags); + list_add_tail(&cmd->node, &vpm->pending_cmds); + spin_unlock_irqrestore(&vpm->list_lock, flags); + return cmd; +} + +/* Get the results from a previous call to vpmadt032_getreg_full_async. */ +int vpmadt032_getreg_full_return(struct vpmadt032 *vpm, int pagechange, + u16 addr, u16 *outbuf, struct vpmadt032_cmd *cmd) +{ + unsigned long flags; + int ret = -EIO; + BUG_ON(!cmd); + wait_for_completion(&cmd->complete); + if (cmd->desc & __VPM150M_FIN) { + *outbuf = cmd->data; + cmd->desc = 0; + ret = 0; + } + + /* Just throw this command back on the ready list. */ + spin_lock_irqsave(&vpm->list_lock, flags); + list_add_tail(&cmd->node, &vpm->free_cmds); + spin_unlock_irqrestore(&vpm->list_lock, flags); + return ret; +} + +/* Read one of the registers on the VPMADT032 */ +static int vpmadt032_getreg_full(struct vpmadt032 *vpm, int pagechange, u16 addr, u16 *outbuf) +{ + struct vpmadt032_cmd *cmd; + cmd = vpmadt032_getreg_full_async(vpm, pagechange, addr); + if (unlikely(!cmd)) { + return -ENOMEM; + } + return vpmadt032_getreg_full_return(vpm, pagechange, addr, outbuf, cmd); +} + +static int vpmadt032_setreg_full(struct vpmadt032 *vpm, int pagechange, unsigned int addr, + u16 data) +{ + unsigned long flags; + struct vpmadt032_cmd *cmd; + cmd = vpmadt032_get_free_cmd(vpm); + if (!cmd) + return -ENOMEM; + cmd->desc = cpu_to_le16((pagechange) ? (__VPM150M_WR|__VPM150M_RWPAGE) : __VPM150M_WR); + cmd->address = cpu_to_le16(addr); + cmd->data = cpu_to_le16(data); + spin_lock_irqsave(&vpm->list_lock, flags); + list_add_tail(&cmd->node, &vpm->pending_cmds); + spin_unlock_irqrestore(&vpm->list_lock, flags); + return 0; +} + + +static int vpmadt032_setpage(struct vpmadt032 *vpm, u16 addr) +{ + addr &= 0xf; + /* We do not need to set the page if we're already on the page we're + * interested in. */ + if (vpm->curpage == addr) + return 0; + else + vpm->curpage = addr; + + return vpmadt032_setreg_full(vpm, 1, 0, addr); +} + +static unsigned char vpmadt032_getpage(struct vpmadt032 *vpm) +{ + unsigned short res; + const int pagechange = 1; + vpmadt032_getreg_full(vpm, pagechange, 0, &res); + return res; +} + +static int vpmadt032_getreg(struct vpmadt032 *vpm, unsigned int addr, u16 *data) +{ + unsigned short res; + vpmadt032_setpage(vpm, addr >> 16); + res = vpmadt032_getreg_full(vpm, 0, addr & 0xffff, data); + return res; +} + +static int vpmadt032_setreg(struct vpmadt032 *vpm, unsigned int addr, u16 data) +{ + int res; + vpmadt032_setpage(vpm, addr >> 16); + res = vpmadt032_setreg_full(vpm, 0, addr & 0xffff, data); + return res; +} + +/** + * vpmadt032_bh - Changes the echocan parameters on the vpmadt032 module. + * + * This function is typically scheduled to run in the workqueue by the + * vpmadt032_echocan_with_params function. This is because communicating with + * the hardware can take some time while messages are sent to the VPMADT032 + * module and the driver waits for the responses. + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) +static void vpmadt032_bh(void *data) +{ + struct vpmadt032 *vpm = data; +#else +static void vpmadt032_bh(struct work_struct *data) +{ + struct vpmadt032 *vpm = container_of(data, struct vpmadt032, work); +#endif + struct adt_lec_params *curstate, *desiredstate; + int channel; + + /* Sweep through all the echo can channels on the VPMADT032 module, + * looking for ones where the desired state does not match the current + * state. + */ + for (channel = 0; channel < vpm->span->channels; channel++) { + GPAK_AlgControlStat_t pstatus; + int res = 1; + curstate = &vpm->curecstate[channel]; + desiredstate = &vpm->desiredecstate[channel]; + + if ((desiredstate->nlp_type != curstate->nlp_type) || + (desiredstate->nlp_threshold != curstate->nlp_threshold) || + (desiredstate->nlp_max_suppress != curstate->nlp_max_suppress)) { + + GPAK_ChannelConfigStat_t cstatus; + GPAK_TearDownChanStat_t tstatus; + GpakChannelConfig_t chanconfig; + + if (vpm->options.debug & DEBUG_ECHOCAN) + printk(KERN_DEBUG "Reconfiguring chan %d for nlp %d, nlp_thresh %d, and max_supp %d\n", channel + 1, vpm->desiredecstate[channel].nlp_type, + desiredstate->nlp_threshold, desiredstate->nlp_max_suppress); + + vpm->setchanconfig_from_state(vpm, channel, &chanconfig); + + res = gpakTearDownChannel(vpm->dspid, channel, &tstatus); + if (res) + goto vpm_bh_out; + + res = gpakConfigureChannel(vpm->dspid, channel, tdmToTdm, &chanconfig, &cstatus); + if (res) + goto vpm_bh_out; + + if (!desiredstate->tap_length) { + res = gpakAlgControl(vpm->dspid, channel, BypassSwCompanding, &pstatus); + if (res) + printk("Unable to disable sw companding on echo cancellation channel %d (reason %d)\n", channel, res); + res = gpakAlgControl(vpm->dspid, channel, BypassEcanA, &pstatus); + } + + } else if (desiredstate->tap_length != curstate->tap_length) { + if (desiredstate->tap_length) { + printk(KERN_DEBUG "Enabling ecan on channel: %d\n", channel); + res = gpakAlgControl(vpm->dspid, channel, EnableMuLawSwCompanding, &pstatus); + if (res) + printk("Unable to set SW Companding on channel %d (reason %d)\n", channel, res); + res = gpakAlgControl(vpm->dspid, channel, EnableEcanA, &pstatus); + } else { + printk(KERN_DEBUG "Disabling ecan on channel: %d\n", channel); + res = gpakAlgControl(vpm->dspid, channel, BypassSwCompanding, &pstatus); + if (res) + printk("Unable to disable sw companding on echo cancellation channel %d (reason %d)\n", channel, res); + res = gpakAlgControl(vpm->dspid, channel, BypassEcanA, &pstatus); + } + } +vpm_bh_out: + if (!res) + *curstate = *desiredstate; + } + return; +} +#include "adt_lec.c" +int vpmadt032_echocan_with_params(struct vpmadt032 *vpm, int channo, + struct dahdi_echocanparams *ecp, struct dahdi_echocanparam *p) +{ + int update; + unsigned int ret; + + ret = adt_lec_parse_params(&vpm->desiredecstate[channo], ecp, p); + if (ret) + return ret; + + /* The driver cannot control the number of taps on the VPMADT032 + * module. Instead, it uses tap_length to enable or disable the echo + * cancellation. */ + vpm->desiredecstate[channo].tap_length = (ecp->tap_length) ? 1 : 0; + + /* Only update the parameters if the new state of the echo canceller + * is different than the current state. */ + update = memcmp(&vpm->curecstate[channo], + &vpm->desiredecstate[channo], + sizeof(vpm->curecstate[channo])); + if (update && test_bit(VPM150M_ACTIVE, &vpm->control)) { + /* Since updating the parameters can take a bit of time while + * the driver sends messages to the VPMADT032 and waits for + * their responses, we'll push the work of updating the + * parameters to a work queue so the caller can continue to + * proceed with setting up the call. + */ + schedule_work(&vpm->work); + } + + return 0; +} +EXPORT_SYMBOL(vpmadt032_echocan_with_params); + +struct vpmadt032 *vpmadt032_alloc(struct vpmadt032_options *options) +{ + struct vpmadt032 *vpm; + int i; + might_sleep(); + + vpm = kzalloc(sizeof(*vpm), GFP_KERNEL); + if (!vpm) + return NULL; + + /* Init our vpmadt032 struct */ + memcpy(&vpm->options, options, sizeof(*options)); + spin_lock_init(&vpm->list_lock); + INIT_LIST_HEAD(&vpm->free_cmds); + INIT_LIST_HEAD(&vpm->pending_cmds); + INIT_LIST_HEAD(&vpm->active_cmds); + sema_init(&vpm->sem, 1); + vpm->curpage = 0x80; + vpm->dspid = -1; + + /* Place this structure in the ifaces array so that the DspId from the + * Gpak Library can be used to locate it. */ + write_lock(&ifacelock); + for (i=0; i<MAX_DSP_CORES; ++i) { + if (NULL == ifaces[i]) { + ifaces[i] = vpm; + vpm->dspid = i; + break; + } + } + write_unlock(&ifacelock); + + if (-1 == vpm->dspid) { + kfree(vpm); + printk(KERN_NOTICE "Unable to initialize another vpmadt032 modules\n"); + vpm = NULL; + } else if (vpm->options.debug & DEBUG_ECHOCAN) { + printk(KERN_DEBUG "Setting VPMADT032 DSP ID to %d\n", vpm->dspid); + } + + return vpm; +} +EXPORT_SYMBOL(vpmadt032_alloc); + +int +vpmadt032_init(struct vpmadt032 *vpm, struct voicebus *vb) +{ + int i; + u16 reg; + int res = -EFAULT; + gpakPingDspStat_t pingstatus; + + BUG_ON(!vpm->setchanconfig_from_state); + might_sleep(); + + if (vpm->options.debug & DEBUG_ECHOCAN) + printk(KERN_DEBUG "VPMADT032 Testing page access: "); + + for (i = 0; i < 0xf; i++) { + int x; + for (x = 0; x < 3; x++) { + vpmadt032_setpage(vpm, i); + reg = vpmadt032_getpage(vpm); + if (reg != i) { + if (vpm->options.debug & DEBUG_ECHOCAN) + printk(KERN_DEBUG "Failed: Sent %x != %x VPMADT032 Failed HI page test\n", i, reg); + res = -ENODEV; + goto failed_exit; + } + } + } + + if (vpm->options.debug & DEBUG_ECHOCAN) + printk(KERN_DEBUG "Passed\n"); + + set_bit(VPM150M_HPIRESET, &vpm->control); + msleep(2000); + while (test_bit(VPM150M_HPIRESET, &vpm->control)) + msleep(1); + + /* Set us up to page 0 */ + vpmadt032_setpage(vpm, 0); + if (vpm->options.debug & DEBUG_ECHOCAN) + printk(KERN_DEBUG "VPMADT032 now doing address test: "); + + for (i = 0; i < 16; i++) { + int x; + for (x = 0; x < 2; x++) { + vpmadt032_setreg(vpm, 0x1000, i); + vpmadt032_getreg(vpm, 0x1000, ®); + if (reg != i) { + printk("VPMADT032 Failed address test\n"); + goto failed_exit; + } + + } + } + + if (vpm->options.debug & DEBUG_ECHOCAN) + printk("Passed\n"); + + set_bit(VPM150M_HPIRESET, &vpm->control); + while (test_bit(VPM150M_HPIRESET, &vpm->control)) + msleep(1); + + res = vpmadtreg_loadfirmware(vb); + if (res) { + struct pci_dev *pdev = voicebus_get_pci_dev(vb); + dev_printk(KERN_INFO, &pdev->dev, "Failed to load the firmware.\n"); + return res; + } + vpm->curpage = -1; + set_bit(VPM150M_SWRESET, &vpm->control); + + while (test_bit(VPM150M_SWRESET, &vpm->control)) + msleep(1); + + pingstatus = gpakPingDsp(vpm->dspid, &vpm->version); + + if (!pingstatus) { + if (vpm->options.debug & DEBUG_ECHOCAN) + printk(KERN_DEBUG "Version of DSP is %x\n", vpm->version); + } else { + printk(KERN_NOTICE "VPMADT032 Failed! Unable to ping the DSP (%d)!\n", pingstatus); + res = -1; + goto failed_exit; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) + INIT_WORK(&vpm->work, vpmadt032_bh, vpm); +#else + INIT_WORK(&vpm->work, vpmadt032_bh); +#endif + + return 0; + +failed_exit: + return res; +} +EXPORT_SYMBOL(vpmadt032_init); + + +void vpmadt032_free(struct vpmadt032 *vpm) +{ + unsigned long flags; + struct vpmadt032_cmd *cmd; + LIST_HEAD(local_list); + + BUG_ON(!vpm); + + /* Move all the commands onto the local list protected by the locks */ + spin_lock_irqsave(&vpm->list_lock, flags); + list_splice(&vpm->pending_cmds, &local_list); + list_splice(&vpm->active_cmds, &local_list); + list_splice(&vpm->free_cmds, &local_list); + spin_unlock_irqrestore(&vpm->list_lock, flags); + + while (!list_empty(&local_list)) { + cmd = list_entry(local_list.next, struct vpmadt032_cmd, node); + list_del(&cmd->node); + kfree(cmd); + } + + BUG_ON(ifaces[vpm->dspid] != vpm); + write_lock(&ifacelock); + ifaces[vpm->dspid] = NULL; + write_unlock(&ifacelock); + kfree(vpm); +} +EXPORT_SYMBOL(vpmadt032_free); + +int vpmadt032_module_init(void) +{ + rwlock_init(&ifacelock); + memset(ifaces, 0, sizeof(ifaces)); + return 0; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakReadDspMemory - Read DSP memory. + * + * FUNCTION + * This function reads a contiguous block of words from DSP memory starting at + * the specified address. + * + * RETURNS + * nothing + * + */ +void gpakReadDspMemory( + unsigned short int DspId, /* DSP Identifier (0 to MAX_DSP_CORES-1) */ + DSP_ADDRESS DspAddress, /* DSP's memory address of first word */ + unsigned int NumWords, /* number of contiguous words to read */ + DSP_WORD *pWordValues /* pointer to array of word values variable */ + ) +{ + struct vpmadt032 *vpm = find_iface(DspId); + int i; + int ret; + + vpmadt032_io_wait(vpm); + if ( NumWords < VPM150M_MAX_COMMANDS ) { + struct vpmadt032_cmd *cmds[VPM150M_MAX_COMMANDS] = {0}; + vpmadt032_setpage(vpm, DspAddress >> 16); + DspAddress &= 0xffff; + for (i=0; i < NumWords; ++i) { + if (!(cmds[i] = vpmadt032_getreg_full_async(vpm,0,DspAddress+i))) { + return; + } + } + for (i=NumWords-1; i >=0; --i) { + ret = vpmadt032_getreg_full_return(vpm,0,DspAddress+i,&pWordValues[i], + cmds[i]); + if (0 != ret) { + return; + } + } + } + else { + for (i=0; i<NumWords; ++i) { + vpmadt032_getreg(vpm, DspAddress + i, &pWordValues[i]); + } + } + return; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakWriteDspMemory - Write DSP memory. + * + * FUNCTION + * This function writes a contiguous block of words to DSP memory starting at + * the specified address. + * + * RETURNS + * nothing + * + */ +void gpakWriteDspMemory( + unsigned short int DspId, /* DSP Identifier (0 to MAX_DSP_CORES-1) */ + DSP_ADDRESS DspAddress, /* DSP's memory address of first word */ + unsigned int NumWords, /* number of contiguous words to write */ + DSP_WORD *pWordValues /* pointer to array of word values to write */ + ) +{ + + struct vpmadt032 *vpm = find_iface(DspId); + int i; + + //printk(KERN_DEBUG "Writing %d words to memory\n", NumWords); + if (vpm) { + for (i = 0; i < NumWords; ++i) { + vpmadt032_setreg(vpm, DspAddress + i, pWordValues[i]); + } +#if 0 + for (i = 0; i < NumWords; i++) { + if (wctdm_vpmadt032_getreg(wc, DspAddress + i) != pWordValues[i]) { + printk(KERN_NOTICE "Error in write. Address %x is not %x\n", DspAddress + i, pWordValues[i]); + } + } +#endif + } + return; + +} + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakHostDelay - Delay for a fixed time interval. + * + * FUNCTION + * This function delays for a fixed time interval before returning. The time + * interval is the Host Port Interface sampling period when polling a DSP for + * replies to command messages. + * + * RETURNS + * nothing + * + */ +void gpakHostDelay(void) +{ +} + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakLockAccess - Lock access to the specified DSP. + * + * FUNCTION + * This function aquires exclusive access to the specified DSP. + * + * RETURNS + * nothing + * + */ +void gpakLockAccess(unsigned short DspId) +{ + struct vpmadt032 *vpm; + + vpm = find_iface(DspId); + + if (vpm) { + if (down_interruptible(&vpm->sem)) { + return; + } + } +} + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakUnlockAccess - Unlock access to the specified DSP. + * + * FUNCTION + * This function releases exclusive access to the specified DSP. + * + * RETURNS + * nothing + * + */ +void gpakUnlockAccess(unsigned short DspId) +{ + struct vpmadt032 *vpm; + + vpm = find_iface(DspId); + + if (vpm) { + up(&vpm->sem); + } +} + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * gpakReadFile - Read a block of bytes from a G.PAK Download file. + * + * FUNCTION + * This function reads a contiguous block of bytes from a G.PAK Download file + * starting at the current file position. + * + * RETURNS + * The number of bytes read from the file. + * -1 indicates an error occurred. + * 0 indicates all bytes have been read (end of file) + * + */ +int gpakReadFile( + GPAK_FILE_ID FileId, /* G.PAK Download File Identifier */ + unsigned char *pBuffer, /* pointer to buffer for storing bytes */ + unsigned int NumBytes /* number of bytes to read */ + ) +{ + /* The firmware is loaded into the part by a closed-source firmware + * loader, and therefore this function should never be called. */ + WARN_ON(1); + return -1; +} |