From 4244c7073ad8a83d58d0aa3c4a87f4f36153ff3a Mon Sep 17 00:00:00 2001 From: Shaun Ruffell Date: Fri, 26 Feb 2010 16:40:44 +0000 Subject: wctdm24xxp: Add support for Hx8 series cards. The Hx8 series cards support BRI modules in addition to analog modules. git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/trunk@8123 a0bf4364-ded3-4de4-8d8a-66a801d63aff --- drivers/dahdi/voicebus/voicebus.c | 238 +- drivers/dahdi/voicebus/voicebus.h | 7 +- .../vpmadt032_loader/dahdi_vpmadt032_loader.c | 11 +- drivers/dahdi/wctdm24xxp/Kbuild | 2 +- drivers/dahdi/wctdm24xxp/base.c | 1817 +++++++++---- drivers/dahdi/wctdm24xxp/wctdm24xxp.h | 135 +- drivers/dahdi/wctdm24xxp/xhfc.c | 2756 ++++++++++++++++++++ drivers/dahdi/wctdm24xxp/xhfc.h | 49 + drivers/dahdi/wcte12xp/base.c | 8 +- 9 files changed, 4389 insertions(+), 634 deletions(-) create mode 100644 drivers/dahdi/wctdm24xxp/xhfc.c create mode 100644 drivers/dahdi/wctdm24xxp/xhfc.h diff --git a/drivers/dahdi/voicebus/voicebus.c b/drivers/dahdi/voicebus/voicebus.c index eacc0f7..fe23856 100644 --- a/drivers/dahdi/voicebus/voicebus.c +++ b/drivers/dahdi/voicebus/voicebus.c @@ -95,10 +95,11 @@ #define OWN_BIT (1 << 31) #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) -static kmem_cache_t *buffer_cache; +kmem_cache_t *voicebus_vbb_cache; #else -static struct kmem_cache *buffer_cache; +struct kmem_cache *voicebus_vbb_cache; #endif +EXPORT_SYMBOL(voicebus_vbb_cache); /* In memory structure shared by the host and the adapter. */ struct voicebus_descriptor { @@ -365,8 +366,6 @@ vb_cleanup_tx_descriptors(struct voicebus *vb) struct voicebus_descriptor *d; unsigned long flags; - WARN_ON(!vb_is_stopped(vb)); - spin_lock_irqsave(&vb->lock, flags); for (i = 0; i < DRING_SIZE; ++i) { d = vb_descriptor(dl, i); @@ -374,7 +373,7 @@ vb_cleanup_tx_descriptors(struct voicebus *vb) WARN_ON(!dl->pending[i]); dma_unmap_single(&vb->pdev->dev, d->buffer1, VOICEBUS_SFRAME_SIZE, DMA_TO_DEVICE); - voicebus_free(vb, dl->pending[i]); + kmem_cache_free(voicebus_vbb_cache, dl->pending[i]); } if (!test_bit(VOICEBUS_NORMAL_MODE, &vb->flags)) { d->buffer1 = 0; @@ -388,8 +387,6 @@ vb_cleanup_tx_descriptors(struct voicebus *vb) } } - /* Send out two idle buffers to start because sometimes the first buffer - * doesn't make it back to us. */ dl->head = dl->tail = 0; spin_unlock_irqrestore(&vb->lock, flags); atomic_set(&dl->count, 0); @@ -403,8 +400,6 @@ vb_cleanup_rx_descriptors(struct voicebus *vb) struct voicebus_descriptor *d; unsigned long flags; - BUG_ON(!vb_is_stopped(vb)); - spin_lock_irqsave(&vb->lock, flags); for (i = 0; i < DRING_SIZE; ++i) { d = vb_descriptor(dl, i); @@ -413,7 +408,7 @@ vb_cleanup_rx_descriptors(struct voicebus *vb) VOICEBUS_SFRAME_SIZE, DMA_FROM_DEVICE); d->buffer1 = 0; BUG_ON(!dl->pending[i]); - kmem_cache_free(buffer_cache, dl->pending[i]); + kmem_cache_free(voicebus_vbb_cache, dl->pending[i]); dl->pending[i] = NULL; } d->des0 &= ~OWN_BIT; @@ -533,7 +528,7 @@ vb_reset_interface(struct voicebus *vb) unsigned long timeout; u32 reg; u32 pci_access; - const u32 DEFAULT_PCI_ACCESS = 0xfff80002; + const u32 DEFAULT_PCI_ACCESS = 0xfffc0002; u8 cache_line_size; BUG_ON(in_interrupt()); @@ -601,7 +596,7 @@ vb_submit_rxb(struct voicebus *vb, struct vbb *vbb) if (unlikely(d->buffer1)) { /* Do not overwrite a buffer that is still in progress. */ WARN_ON(1); - voicebus_free(vb, vbb); + kmem_cache_free(voicebus_vbb_cache, vbb); return -EBUSY; } @@ -614,6 +609,49 @@ vb_submit_rxb(struct voicebus *vb, struct vbb *vbb) return 0; } +/** + * voicebus_transmit - Queue a buffer on the hardware descriptor ring. + * + */ +int voicebus_transmit(struct voicebus *vb, struct vbb *vbb) +{ + struct voicebus_descriptor *d; + struct voicebus_descriptor_list *dl = &vb->txd; + + d = vb_descriptor(dl, dl->tail); + + if (unlikely((d->buffer1 != vb->idle_vbb_dma_addr) && d->buffer1)) { + if (printk_ratelimit()) + dev_warn(&vb->pdev->dev, "Dropping tx buffer buffer\n"); + kmem_cache_free(voicebus_vbb_cache, vbb); + /* Schedule the underrun handler to run here, since we'll need + * to cleanup as best we can. */ + schedule_work(&vb->underrun_work); + return -EFAULT; + } + + dl->pending[dl->tail] = vbb; + d->buffer1 = dma_map_single(&vb->pdev->dev, vbb->data, + sizeof(vbb->data), DMA_TO_DEVICE); + dl->tail = (++(dl->tail)) & DRING_MASK; + SET_OWNED(d); /* That's it until the hardware is done with it. */ + atomic_inc(&dl->count); + return 0; +} +EXPORT_SYMBOL(voicebus_transmit); + + +/*! + * \brief Instruct the hardware to check for a new tx descriptor. + */ +static inline void +__vb_tx_demand_poll(struct voicebus *vb) +{ + u32 status = __vb_getctl(vb, 0x0028); + if ((status & 0x00700000) == 0x00600000) + __vb_setctl(vb, 0x0008, 0x00000000); +} + static void setup_descriptors(struct voicebus *vb) { int i; @@ -631,7 +669,7 @@ static void setup_descriptors(struct voicebus *vb) vb_setctl(vb, 0x0018, (u32)vb->rxd.desc_dma); for (i = 0; i < DRING_SIZE; ++i) { - vbb = kmem_cache_alloc(buffer_cache, GFP_KERNEL); + vbb = kmem_cache_alloc(voicebus_vbb_cache, GFP_KERNEL); if (unlikely(NULL == vbb)) BUG_ON(1); list_add_tail(&vbb->entry, &buffers); @@ -646,13 +684,13 @@ static void setup_descriptors(struct voicebus *vb) if (test_bit(VOICEBUS_NORMAL_MODE, &vb->flags)) { for (i = 0; i < vb->min_tx_buffer_count; ++i) { - vbb = kmem_cache_alloc(buffer_cache, GFP_KERNEL); + vbb = kmem_cache_alloc(voicebus_vbb_cache, GFP_KERNEL); if (unlikely(NULL == vbb)) BUG_ON(1); else list_add_tail(&vbb->entry, &buffers); } - + tasklet_disable(&vb->tasklet); handle_transmit(vb, &buffers); tasklet_enable(&vb->tasklet); @@ -740,55 +778,6 @@ show_buffer(struct voicebus *vb, struct vbb *vbb) } #endif -/** - * voicebus_transmit - Queue a buffer on the hardware descriptor ring. - * - */ -static int __voicebus_transmit(struct voicebus *vb, struct vbb *vbb) -{ - struct voicebus_descriptor *d; - struct voicebus_descriptor_list *dl = &vb->txd; - - d = vb_descriptor(dl, dl->tail); - - if (unlikely((d->buffer1 != vb->idle_vbb_dma_addr) && d->buffer1)) { - if (printk_ratelimit()) - dev_warn(&vb->pdev->dev, "Dropping tx buffer buffer\n"); - voicebus_free(vb, vbb); - /* Schedule the underrun handler to run here, since we'll need - * to cleanup as best we can. */ - schedule_work(&vb->underrun_work); - return -EFAULT; - } - - dl->pending[dl->tail] = vbb; - d->buffer1 = dma_map_single(&vb->pdev->dev, vbb->data, - sizeof(vbb->data), DMA_TO_DEVICE); - dl->tail = (++(dl->tail)) & DRING_MASK; - SET_OWNED(d); /* That's it until the hardware is done with it. */ - atomic_inc(&dl->count); - return 0; -} - -/*! - * \brief Instruct the hardware to check for a new tx descriptor. - */ -static inline void -__vb_tx_demand_poll(struct voicebus *vb) -{ - u32 status = __vb_getctl(vb, 0x0028); - if ((status & 0x00700000) == 0x00600000) - __vb_setctl(vb, 0x0008, 0x00000000); -} - -int voicebus_transmit(struct voicebus *vb, struct vbb *vbb) -{ - __voicebus_transmit(vb, vbb); - __vb_tx_demand_poll(vb); - return 0; -} -EXPORT_SYMBOL(voicebus_transmit); - /*! * \brief Remove the next completed transmit buffer (txb) from the tx * descriptor ring. @@ -859,18 +848,6 @@ vb_get_completed_rxb(struct voicebus *vb, u32 *des0) return vbb; } -/*! - * \brief Free a buffer for reuse. - * - */ -void -voicebus_free(struct voicebus *vb, struct vbb *vbb) -{ - kmem_cache_free(buffer_cache, vbb); -} -EXPORT_SYMBOL(voicebus_free); - - /*! * \brief Command the hardware to check if it owns the next receive * descriptor. @@ -925,12 +902,16 @@ static void start_packet_processing(struct voicebus *vb) /* Start the transmit and receive processors. */ reg = __vb_getctl(vb, 0x0030); __vb_setctl(vb, 0x0030, reg|0x00002002); - /* Tell the interface to poll the tx and rx descriptors. */ + __vb_getctl(vb, 0x0030); __vb_rx_demand_poll(vb); __vb_tx_demand_poll(vb); + __vb_getctl(vb, 0x0030); VBUNLOCK(vb); } +static void vb_tasklet_relaxed(unsigned long data); +static void vb_tasklet(unsigned long data); + /*! * \brief Starts the VoiceBus interface. * @@ -951,6 +932,11 @@ voicebus_start(struct voicebus *vb) if (!vb_is_stopped(vb)) return -EBUSY; + if (test_bit(VOICEBUS_NORMAL_MODE, &vb->flags)) + tasklet_init(&vb->tasklet, vb_tasklet, (unsigned long)vb); + else + tasklet_init(&vb->tasklet, vb_tasklet_relaxed, (unsigned long)vb); + ret = vb_reset_interface(vb); if (ret) return ret; @@ -1108,7 +1094,7 @@ vb_increase_latency(struct voicebus *vb, unsigned int increase, /* Set the minimum latency in case we're restarted...we don't want to * wait for the buffer to grow to this depth again in that case. */ for (i = 0; i < increase; ++i) { - vbb = kmem_cache_alloc(buffer_cache, GFP_ATOMIC); + vbb = kmem_cache_alloc(voicebus_vbb_cache, GFP_ATOMIC); WARN_ON(NULL == vbb); if (likely(NULL != vbb)) list_add_tail(&vbb->entry, &local); @@ -1127,29 +1113,64 @@ vb_increase_latency(struct voicebus *vb, unsigned int increase, vb->min_tx_buffer_count += increase; } -static void vb_set_all_owned(struct voicebus *vb, - struct voicebus_descriptor_list *dl) +/** + * vb_tasklet_relaxed() - Service the rings without strict timing requierments. + * + */ +static void vb_tasklet_relaxed(unsigned long data) { - int i; - struct voicebus_descriptor *d; + struct voicebus *vb = (struct voicebus *)data; + LIST_HEAD(buffers); + struct vbb *vbb; + const int DEFAULT_COUNT = 5; + int count = DEFAULT_COUNT; + u32 des0 = 0; - for (i = 0; i < DRING_SIZE; ++i) { - d = vb_descriptor(dl, i); - SET_OWNED(d); + /* First, temporarily store any non-idle buffers that the hardware has + * indicated it's finished transmitting. Non idle buffers are those + * buffers that contain actual data and was filled out by the client + * driver (as of this writing, the wcte12xp or wctdm24xxp drivers) when + * passed up through the handle_transmit callback. + * + * On the other hand, idle buffers are "dummy" buffers that solely exist + * to in order to prevent the transmit descriptor ring from ever + * completely draining. */ + while ((vbb = vb_get_completed_txb(vb))) + list_add_tail(&vbb->entry, &vb->tx_complete); + + while (--count && !list_empty(&vb->tx_complete)) + list_move_tail(vb->tx_complete.next, &buffers); + + /* Prep all the new buffers for transmit before actually sending any + * of them. */ + handle_transmit(vb, &buffers); + + list_for_each_entry(vbb, &buffers, entry) + voicebus_transmit(vb, vbb); + INIT_LIST_HEAD(&buffers); + + /* If there may still be buffers in the descriptor rings, reschedule + * ourself to run again. We essentially yield here to allow any other + * cards a chance to run. */ + if (unlikely(!count && !test_bit(VOICEBUS_STOP, &vb->flags))) + tasklet_hi_schedule(&vb->tasklet); + + /* And finally, pass up any receive buffers. */ + count = DEFAULT_COUNT; + while (--count && (vbb = vb_get_completed_rxb(vb, &des0))) { + if (((des0 >> 16) & 0x7fff) == VOICEBUS_SFRAME_SIZE) + list_add_tail(&vbb->entry, &buffers); } -} -static inline void vb_set_all_tx_owned(struct voicebus *vb) -{ - vb_set_all_owned(vb, &vb->txd); + handle_receive(vb, &buffers); + list_for_each_entry(vbb, &buffers, entry) + vb_submit_rxb(vb, vbb); + return; } -/** - * vb_deferred() - Manage the transmit and receive descriptor rings. - * - */ -static void vb_deferred(struct voicebus *vb) +static void vb_tasklet(unsigned long data) { + struct voicebus *vb = (struct voicebus *)data; int softunderrun; LIST_HEAD(buffers); struct vbb *vbb; @@ -1277,13 +1298,8 @@ static void vb_deferred(struct voicebus *vb) /* And finally, pass up any receive buffers. */ count = DEFAULT_COUNT; while (--count && (vbb = vb_get_completed_rxb(vb, &des0))) { - if (((des0 >> 16) & 0x7fff) != VOICEBUS_SFRAME_SIZE) { - dev_dbg(&vb->pdev->dev, - "ERROR: Dropping packet of wrong size. (%d)\n", - (des0>>16)&0x7fff); - } else { + if (((des0 >> 16) & 0x7fff) == VOICEBUS_SFRAME_SIZE) list_add_tail(&vbb->entry, &buffers); - } } handle_receive(vb, &buffers); @@ -1297,7 +1313,7 @@ tx_error_exit: while (!list_empty(&buffers)) { vbb = list_entry(buffers.next, struct vbb, entry); list_del(&vbb->entry); - kmem_cache_free(buffer_cache, vbb); + kmem_cache_free(voicebus_vbb_cache, vbb); } return; } @@ -1365,7 +1381,8 @@ vb_isr(int irq, void *dev_id) if (unlikely((int_status & (TX_UNAVAILABLE_INTERRUPT|RX_UNAVAILABLE_INTERRUPT)) && - !test_bit(VOICEBUS_STOP, &vb->flags))) { + !test_bit(VOICEBUS_STOP, &vb->flags) && + test_bit(VOICEBUS_NORMAL_MODE, &vb->flags))) { schedule_work(&vb->underrun_work); __vb_setctl(vb, SR_CSR5, int_status); } else if (likely(int_status & @@ -1422,12 +1439,6 @@ vb_timer(unsigned long data) } #endif -static void vb_tasklet(unsigned long data) -{ - struct voicebus *vb = (struct voicebus *)data; - vb_deferred(vb); -} - /*! * \brief Initalize the voicebus interface. * @@ -1462,7 +1473,6 @@ __voicebus_init(struct voicebus *vb, const char *board_name, int normal_mode) vb->min_tx_buffer_count = VOICEBUS_DEFAULT_LATENCY; INIT_LIST_HEAD(&vb->tx_complete); - tasklet_init(&vb->tasklet, vb_tasklet, (unsigned long)vb); #if defined(CONFIG_VOICEBUS_TIMER) init_timer(&vb->timer); @@ -1672,7 +1682,7 @@ static int __init voicebus_module_init(void) int res; #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23) - buffer_cache = kmem_cache_create(THIS_MODULE->name, + voicebus_vbb_cache = kmem_cache_create(THIS_MODULE->name, sizeof(struct vbb), 0, #if defined(CONFIG_SLUB) && (LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 22)) SLAB_HWCACHE_ALIGN | @@ -1684,18 +1694,18 @@ static int __init voicebus_module_init(void) #endif #else #if (defined(DEBUG) && defined(CONFIG_SLAB_DEBUG)) - buffer_cache = kmem_cache_create(THIS_MODULE->name, + voicebus_vbb_cache = kmem_cache_create(THIS_MODULE->name, sizeof(struct vbb), 0, SLAB_HWCACHE_ALIGN | SLAB_STORE_USER | SLAB_POISON | SLAB_DEBUG_FREE, NULL); #else - buffer_cache = kmem_cache_create(THIS_MODULE->name, + voicebus_vbb_cache = kmem_cache_create(THIS_MODULE->name, sizeof(struct vbb), 0, SLAB_HWCACHE_ALIGN, NULL); #endif #endif - if (NULL == buffer_cache) { + if (NULL == voicebus_vbb_cache) { printk(KERN_ERR "%s: Failed to allocate buffer cache.\n", THIS_MODULE->name); return -ENOMEM; @@ -1715,7 +1725,7 @@ static int __init voicebus_module_init(void) static void __exit voicebus_module_cleanup(void) { - kmem_cache_destroy(buffer_cache); + kmem_cache_destroy(voicebus_vbb_cache); WARN_ON(!list_empty(&binary_loader_list)); } diff --git a/drivers/dahdi/voicebus/voicebus.h b/drivers/dahdi/voicebus/voicebus.h index 149b4d0..35f0e29 100644 --- a/drivers/dahdi/voicebus/voicebus.h +++ b/drivers/dahdi/voicebus/voicebus.h @@ -132,12 +132,17 @@ struct voicebus { #endif }; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) +extern kmem_cache_t *voicebus_vbb_cache; +#else +extern struct kmem_cache *voicebus_vbb_cache; +#endif + int __voicebus_init(struct voicebus *vb, const char *board_name, int normal_mode); void voicebus_release(struct voicebus *vb); int voicebus_start(struct voicebus *vb); int voicebus_stop(struct voicebus *vb); -void voicebus_free(struct voicebus *vb, struct vbb *vbb); int voicebus_transmit(struct voicebus *vb, struct vbb *vbb); int voicebus_set_minlatency(struct voicebus *vb, unsigned int milliseconds); int voicebus_current_latency(struct voicebus *vb); diff --git a/drivers/dahdi/vpmadt032_loader/dahdi_vpmadt032_loader.c b/drivers/dahdi/vpmadt032_loader/dahdi_vpmadt032_loader.c index be43780..cdba790 100644 --- a/drivers/dahdi/vpmadt032_loader/dahdi_vpmadt032_loader.c +++ b/drivers/dahdi/vpmadt032_loader/dahdi_vpmadt032_loader.c @@ -105,15 +105,20 @@ static int vpmadt032_load_firmware(struct voicebus *vb) struct private_context *ctx; const struct voicebus_operations *old; void *old_drvdata; + int id; might_sleep(); ctx = kzalloc(sizeof(struct private_context), GFP_KERNEL); if (!ctx) return -ENOMEM; init_private_context(ctx); ctx->vb = vb; - ret = __vpmadt032_start_load( - 0, vb->pdev->vendor << 16 | vb->pdev->device, - &ctx->pvt); + + if (0x8007 == vb->pdev->device || 0x8008 == vb->pdev->device) + id = vb->pdev->vendor << 16 | 0x2400; + else + id = vb->pdev->vendor << 16 | vb->pdev->device; + + ret = __vpmadt032_start_load(0, id, &ctx->pvt); if (ret) goto error_exit; old_drvdata = pci_get_drvdata(vb->pdev); diff --git a/drivers/dahdi/wctdm24xxp/Kbuild b/drivers/dahdi/wctdm24xxp/Kbuild index 6b1d0e7..22cc71a 100644 --- a/drivers/dahdi/wctdm24xxp/Kbuild +++ b/drivers/dahdi/wctdm24xxp/Kbuild @@ -2,4 +2,4 @@ obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTDM24XXP) += wctdm24xxp.o EXTRA_CFLAGS += -I$(src)/.. -Wno-undef -wctdm24xxp-objs := base.o +wctdm24xxp-objs := base.o xhfc.o diff --git a/drivers/dahdi/wctdm24xxp/base.c b/drivers/dahdi/wctdm24xxp/base.c index ce6799e..5cf9e85 100644 --- a/drivers/dahdi/wctdm24xxp/base.c +++ b/drivers/dahdi/wctdm24xxp/base.c @@ -4,6 +4,9 @@ * Written by Mark Spencer * Support for TDM800P and VPM150M by Matthew Fredrickson * + * Support for Hx8 by Andrew Kohlsmith and Matthew + * Fredrickson + * * Copyright (C) 2005 - 2010 Digium, Inc. * All rights reserved. * @@ -34,6 +37,8 @@ Tx Gain - No Pre-Emphasis: -35.99 to 12.00 db Tx Gain - W/Pre-Emphasis: -23.99 to 0.00 db */ +#define DEBUG + #include #include #include @@ -46,11 +51,13 @@ Tx Gain - W/Pre-Emphasis: -23.99 to 0.00 db #include #include #include +#include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) #include #else #include #endif +#include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) /* Define this if you would like to load the modules in parallel. While this @@ -68,6 +75,7 @@ Tx Gain - W/Pre-Emphasis: -23.99 to 0.00 db #include "proslic.h" #include "wctdm24xxp.h" +#include "xhfc.h" #include "adt_lec.h" @@ -187,10 +195,24 @@ static const struct wctdm_desc wctdm410 = { "Wildcard TDM410P", 0, 4 }; static const struct wctdm_desc wcaex2400 = { "Wildcard AEX2400", FLAG_EXPRESS, 24 }; static const struct wctdm_desc wcaex800 = { "Wildcard AEX800", FLAG_EXPRESS, 8 }; static const struct wctdm_desc wcaex410 = { "Wildcard AEX410", FLAG_EXPRESS, 4 }; +static const struct wctdm_desc wcha80000 = { "HA8-0000", 0, 8 }; +static const struct wctdm_desc wchb80000 = { "HB8-0000", FLAG_EXPRESS, 8 }; + +/** + * Returns true if the card is one of the Hybrid Digital Analog Cards. + */ +static inline bool is_hx8(const struct wctdm *wc) +{ + return (&wcha80000 == wc->desc) || (&wchb80000 == wc->desc); +} +static inline bool is_digital_span(const struct wctdm_span *wspan) +{ + return (wspan->span.linecompat > 0); +} struct wctdm *ifaces[WC_MAX_IFACES]; -spinlock_t ifacelock = SPIN_LOCK_UNLOCKED; +DECLARE_MUTEX(ifacelock); static void wctdm_release(struct wctdm *wc); @@ -215,6 +237,7 @@ static int nativebridge = 0; static int ringdebounce = DEFAULT_RING_DEBOUNCE; static int fwringdetect = 0; static int latency = VOICEBUS_DEFAULT_LATENCY; +static int forceload; #define MS_PER_HOOKCHECK (1) #define NEONMWI_ON_DEBOUNCE (100/MS_PER_HOOKCHECK) @@ -291,7 +314,7 @@ int schluffen(wait_queue_head_t *q) static inline int empty_slot(struct wctdm *wc, int card) { int x; - for (x=0;xcmdq[card].cmds[x]) return x; } @@ -438,8 +461,7 @@ static int config_vpmadt032(struct vpmadt032 *vpm, struct wctdm *wc) vpm->curecstate[i].nlp_type = vpm->options.vpmnlptype; vpm->curecstate[i].nlp_threshold = vpm->options.vpmnlpthresh; vpm->curecstate[i].nlp_max_suppress = vpm->options.vpmnlpmaxsupp; - vpm->curecstate[i].companding = (wc->span.deflaw == DAHDI_LAW_MULAW) ? ADT_COMP_ULAW : ADT_COMP_ALAW; - + vpm->curecstate[i].companding = (wc->chans[i]->chan.span->deflaw == DAHDI_LAW_ALAW) ? ADT_COMP_ALAW : ADT_COMP_ULAW; /* set_vpmadt032_chanconfig_from_state(&vpm->curecstate[i], &vpm->options, i, &chanconfig); !!! */ vpm->setchanconfig_from_state(vpm, i, &chanconfig); if ((res = gpakConfigureChannel(vpm->dspid, i, tdmToTdm, &chanconfig, &cstatus))) { @@ -485,6 +507,7 @@ static inline bool is_good_frame(const u8 *sframe) static inline void cmd_dequeue_vpmadt032(struct wctdm *wc, u8 *writechunk, int whichframe) { + unsigned long flags; struct vpmadt032_cmd *curcmd = NULL; struct vpmadt032 *vpmadt032 = wc->vpmadt032; int x; @@ -496,6 +519,7 @@ static inline void cmd_dequeue_vpmadt032(struct wctdm *wc, u8 *writechunk, int w if (test_bit(VPM150M_SPIRESET, &vpmadt032->control) || test_bit(VPM150M_HPIRESET, &vpmadt032->control)) { if (debug & DEBUG_ECHOCAN) dev_info(&wc->vb.pdev->dev, "HW Resetting VPMADT032...\n"); + spin_lock_irqsave(&wc->reglock, flags); for (x = 24; x < 28; x++) { if (x == 24) { if (test_and_clear_bit(VPM150M_SPIRESET, &vpmadt032->control)) @@ -507,6 +531,7 @@ static inline void cmd_dequeue_vpmadt032(struct wctdm *wc, u8 *writechunk, int w writechunk[CMD_BYTE(x, 1, 0)] = 0; writechunk[CMD_BYTE(x, 2, 0)] = 0x00; } + spin_unlock_irqrestore(&wc->reglock, flags); return; } @@ -603,39 +628,37 @@ static inline void cmd_dequeue(struct wctdm *wc, unsigned char *writechunk, int ecval = ecval % EC_SIZE; #endif - /* if a QRV card, map it to its first channel */ - if ((wc->modtype[card] == MOD_TYPE_QRV) && (card & 3)) - { - return; - } - if (wc->altcs[card]) - subaddr = 0; - + /* QRV and BRI modules only use commands relating to the first channel */ + if ((card & 0x03) && (wc->modtype[card] == MOD_TYPE_QRV)) { + return; + } + + if (wc->altcs[card]) + subaddr = 0; - /* Skip audio */ writechunk += 24; spin_lock_irqsave(&wc->reglock, flags); /* Search for something waiting to transmit */ if (pos) { - for (x=0;xcmdq[card].cmds[x] & (__CMD_RD | __CMD_WR)) && !(wc->cmdq[card].cmds[x] & (__CMD_TX | __CMD_FIN))) { curcmd = wc->cmdq[card].cmds[x]; -#if 0 - dev_info(&wc->vb.pdev->dev, "Transmitting command '%08x' in slot %d\n", wc->cmdq[card].cmds[x], wc->txident); -#endif wc->cmdq[card].cmds[x] |= (wc->txident << 24) | __CMD_TX; break; } } } + if (!curcmd) { /* If nothing else, use filler */ if (wc->modtype[card] == MOD_TYPE_FXS) curcmd = CMD_RD(LINE_STATE); else if (wc->modtype[card] == MOD_TYPE_FXO) curcmd = CMD_RD(12); + else if (wc->modtype[card] == MOD_TYPE_BRI) + curcmd = 0x101010; else if (wc->modtype[card] == MOD_TYPE_QRV) curcmd = CMD_RD(3); else if (wc->modtype[card] == MOD_TYPE_VPM) { @@ -649,13 +672,15 @@ static inline void cmd_dequeue(struct wctdm *wc, unsigned char *writechunk, int curcmd = CMD_RD(0x1a0); } } + if (wc->modtype[card] == MOD_TYPE_FXS) { - writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = (1 << (subaddr)); + writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = (1 << (subaddr)); if (curcmd & __CMD_WR) - writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = (curcmd >> 8) & 0x7f; + writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = (curcmd >> 8) & 0x7f; else - writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x80 | ((curcmd >> 8) & 0x7f); - writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff; + writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x80 | ((curcmd >> 8) & 0x7f); + writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff; + } else if (wc->modtype[card] == MOD_TYPE_FXO) { static const int FXO_ADDRS[4] = { 0x00, 0x08, 0x04, 0x0c }; int idx = CMD_BYTE(card, 0, wc->altcs[card]); @@ -663,48 +688,53 @@ static inline void cmd_dequeue(struct wctdm *wc, unsigned char *writechunk, int writechunk[idx] = 0x20 | FXO_ADDRS[subaddr]; else writechunk[idx] = 0x60 | FXO_ADDRS[subaddr]; - writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = (curcmd >> 8) & 0xff; - writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff; + writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = (curcmd >> 8) & 0xff; + writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff; + } else if (wc->modtype[card] == MOD_TYPE_FXSINIT) { /* Special case, we initialize the FXS's into the three-byte command mode then switch to the regular mode. To send it into thee byte mode, treat the path as 6 two-byte commands and in the last one we initialize register 0 to 0x80. All modules read this as the command to switch to daisy chain mode and we're done. */ - writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x00; - writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x00; + writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x00; + writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x00; if ((card & 0x1) == 0x1) - writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x80; + writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x80; else - writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x00; + writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x00; + + } else if (wc->modtype[card] == MOD_TYPE_BRI) { + + if (unlikely((curcmd != 0x101010) && (curcmd & 0x1010) == 0x1010)) /* b400m CPLD */ + writechunk[CMD_BYTE(card, 0, 0)] = 0x55; + else /* xhfc */ + writechunk[CMD_BYTE(card, 0, 0)] = 0x10; + writechunk[CMD_BYTE(card, 1, 0)] = (curcmd >> 8) & 0xff; + writechunk[CMD_BYTE(card, 2, 0)] = curcmd & 0xff; } else if (wc->modtype[card] == MOD_TYPE_VPM) { if (curcmd & __CMD_WR) - writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = ((card & 0x3) << 4) | 0xc | ((curcmd >> 16) & 0x1); + writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = ((card & 0x3) << 4) | 0xc | ((curcmd >> 16) & 0x1); else - writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = ((card & 0x3) << 4) | 0xa | ((curcmd >> 16) & 0x1); - writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = (curcmd >> 8) & 0xff; - writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff; - } else if (wc->modtype[card] == MOD_TYPE_VPM150M) { - ; - } else if (wc->modtype[card] == MOD_TYPE_QRV) { + writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = ((card & 0x3) << 4) | 0xa | ((curcmd >> 16) & 0x1); + writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = (curcmd >> 8) & 0xff; + writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff; + } else if (wc->modtype[card] == MOD_TYPE_QRV) { - writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x00; - if (!curcmd) - { - writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x00; - writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x00; - } - else - { - if (curcmd & __CMD_WR) - writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x40 | ((curcmd >> 8) & 0x3f); - else - writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0xc0 | ((curcmd >> 8) & 0x3f); - writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff; - } + writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x00; + if (!curcmd) { + writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x00; + writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x00; + } else { + if (curcmd & __CMD_WR) + writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x40 | ((curcmd >> 8) & 0x3f); + else + writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0xc0 | ((curcmd >> 8) & 0x3f); + writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff; + } } else if (wc->modtype[card] == MOD_TYPE_NONE) { - writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x00; - writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x00; - writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x00; + writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x10; + writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x10; + writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x10; } #if 0 /* XXX */ @@ -715,10 +745,10 @@ static inline void cmd_dequeue(struct wctdm *wc, unsigned char *writechunk, int #if 0 /* XXX */ cmddesc++; -#endif +#endif } -static inline void cmd_decipher_vpmadt032(struct wctdm *wc, unsigned char *readchunk) +static inline void cmd_decipher_vpmadt032(struct wctdm *wc, const u8 *readchunk) { unsigned long flags; struct vpmadt032 *vpm = wc->vpmadt032; @@ -747,6 +777,7 @@ static inline void cmd_decipher_vpmadt032(struct wctdm *wc, unsigned char *readc /* Skip audio */ readchunk += 24; + /* Store result */ cmd->data = (0xff & readchunk[CMD_BYTE(25, 1, 0)]) << 8; cmd->data |= readchunk[CMD_BYTE(25, 2, 0)]; @@ -757,26 +788,23 @@ static inline void cmd_decipher_vpmadt032(struct wctdm *wc, unsigned char *readc cmd->desc |= __VPM150M_FIN; complete(&cmd->complete); } -#if 0 - // if (printk_ratelimit()) - dev_info(&wc->vb.pdev->dev, "Received txident = %d, desc = 0x%x, addr = 0x%x, data = 0x%x\n", cmd->txident, cmd->desc, cmd->address, cmd->data); -#endif } -static inline void cmd_decipher(struct wctdm *wc, u8 *readchunk, int card) +static inline void cmd_decipher(struct wctdm *wc, const u8 *readchunk, int card) { unsigned long flags; unsigned char ident; int x; - /* if a QRV card, map it to its first channel */ - if ((wc->modtype[card] == MOD_TYPE_QRV) && (card & 3)) - { + /* QRV and BRI modules only use commands relating to the first channel */ + if ((card & 0x03) && (wc->modtype[card] == MOD_TYPE_QRV)) { /* || (wc->modtype[card] == MOD_TYPE_BRI))) { */ return; } + /* Skip audio */ readchunk += 24; spin_lock_irqsave(&wc->reglock, flags); + /* Search for any pending results */ for (x=0;xcmdq[card].cmds[x] & (__CMD_RD | __CMD_WR)) && @@ -787,6 +815,11 @@ static inline void cmd_decipher(struct wctdm *wc, u8 *readchunk, int card) /* Store result */ wc->cmdq[card].cmds[x] |= readchunk[CMD_BYTE(card, 2, wc->altcs[card])]; wc->cmdq[card].cmds[x] |= __CMD_FIN; +/* + if (card == 0 && wc->cmdq[card].cmds[x] & __CMD_RD) { + dev_info(&wc->vb.pdev->dev, "decifer: got response %02x\n", wc->cmdq[card].cmds[x] & 0xff); + } +*/ if (wc->cmdq[card].cmds[x] & __CMD_WR) { /* Go ahead and clear out writes since they need no acknowledgement */ wc->cmdq[card].cmds[x] = 0x00000000; @@ -819,6 +852,8 @@ static inline void cmd_checkisr(struct wctdm *wc, int card) wc->cmdq[card].cmds[USER_COMMANDS + 0] = CMD_RD(5); /* Hook/Ring state */ } else if (wc->modtype[card] == MOD_TYPE_QRV) { wc->cmdq[card & 0xfc].cmds[USER_COMMANDS + 0] = CMD_RD(3); /* COR/CTCSS state */ + } else if (wc->modtype[card] == MOD_TYPE_BRI) { + wc->cmdq[card].cmds[USER_COMMANDS + 0] = wctdm_bri_checkisr(wc, card, 0); #ifdef VPM_SUPPORT } else if (wc->modtype[card] == MOD_TYPE_VPM) { wc->cmdq[card].cmds[USER_COMMANDS + 0] = CMD_RD(0xb9); /* DTMF interrupt */ @@ -836,6 +871,8 @@ static inline void cmd_checkisr(struct wctdm *wc, int card) wc->cmdq[card].cmds[USER_COMMANDS + 1] = CMD_RD(29); /* Battery */ } else if (wc->modtype[card] == MOD_TYPE_QRV) { wc->cmdq[card & 0xfc].cmds[USER_COMMANDS + 1] = CMD_RD(3); /* Battery */ + } else if (wc->modtype[card] == MOD_TYPE_BRI) { + wc->cmdq[card].cmds[USER_COMMANDS + 1] = wctdm_bri_checkisr(wc, card, 1); #ifdef VPM_SUPPORT } else if (wc->modtype[card] == MOD_TYPE_VPM) { wc->cmdq[card].cmds[USER_COMMANDS + 1] = CMD_RD(0xbd); /* DTMF interrupt */ @@ -853,8 +890,8 @@ static void insert_tdm_data(const struct wctdm *wc, u8 *sframe) int i; register u8 *chanchunk; - for (i = 0; i < wc->cards; i += 4) { - chanchunk = &wc->chans[0 + i]->writechunk[0]; + for (i = 0; i < wc->avchannels; i += 4) { + chanchunk = &wc->chans[0 + i]->chan.writechunk[0]; sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*0] = chanchunk[0]; sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*1] = chanchunk[1]; sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*2] = chanchunk[2]; @@ -864,7 +901,7 @@ static void insert_tdm_data(const struct wctdm *wc, u8 *sframe) sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*6] = chanchunk[6]; sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*7] = chanchunk[7]; - chanchunk = &wc->chans[1 + i]->writechunk[0]; + chanchunk = &wc->chans[1 + i]->chan.writechunk[0]; sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*0] = chanchunk[0]; sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*1] = chanchunk[1]; sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*2] = chanchunk[2]; @@ -874,7 +911,7 @@ static void insert_tdm_data(const struct wctdm *wc, u8 *sframe) sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*6] = chanchunk[6]; sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*7] = chanchunk[7]; - chanchunk = &wc->chans[2 + i]->writechunk[0]; + chanchunk = &wc->chans[2 + i]->chan.writechunk[0]; sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*0] = chanchunk[0]; sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*1] = chanchunk[1]; sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*2] = chanchunk[2]; @@ -884,7 +921,7 @@ static void insert_tdm_data(const struct wctdm *wc, u8 *sframe) sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*6] = chanchunk[6]; sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*7] = chanchunk[7]; - chanchunk = &wc->chans[3 + i]->writechunk[0]; + chanchunk = &wc->chans[3 + i]->chan.writechunk[0]; sframe[3 + i + (EFRAME_SIZE + EFRAME_GAP)*0] = chanchunk[0]; sframe[3 + i + (EFRAME_SIZE + EFRAME_GAP)*1] = chanchunk[1]; sframe[3 + i + (EFRAME_SIZE + EFRAME_GAP)*2] = chanchunk[2]; @@ -899,30 +936,40 @@ static void insert_tdm_data(const struct wctdm *wc, u8 *sframe) static inline void wctdm_transmitprep(struct wctdm *wc, unsigned char *writechunk) { int x,y; + struct dahdi_span *s; /* Calculate Transmission */ if (likely(wc->initialized)) { - dahdi_transmit(&wc->span); + for (x = 0; x < MAX_SPANS; x++) { + if (wc->spans[x]) { + s = &wc->spans[x]->span; + dahdi_transmit(s); + } + } insert_tdm_data(wc, writechunk); } - for (x=0;xcards;y++) { - if (!x) { + + /* TODO: ABK: hmm, this was originally mods_per_board, but we + * need to worry about all the active "voice" timeslots, since + * BRI modules have a different number of TDM channels than + * installed modules. */ + for (y = 0; y < wc->avchannels; y++) { + if (!x && y < wc->mods_per_board) { cmd_checkisr(wc, y); } - if (x < 3) + if ((x < 3) && (y < wc->mods_per_board)) cmd_dequeue(wc, writechunk, y, x); } if (!x) wc->blinktimer++; if (wc->vpm100) { - for (y=24;y<28;y++) { - if (!x) { + for (y = NUM_MODULES; y < NUM_MODULES + NUM_EC; y++) { + if (!x) cmd_checkisr(wc, y); - } cmd_dequeue(wc, writechunk, y, x); } #ifdef FANCY_ECHOCAN @@ -939,7 +986,7 @@ static inline void wctdm_transmitprep(struct wctdm *wc, unsigned char *writechun writechunk[EFRAME_SIZE] = wc->ctlreg; writechunk[EFRAME_SIZE + 1] = wc->txident++; - if ((wc->desc->ports == 4) && ((wc->ctlreg & 0x10) || (wc->modtype[NUM_CARDS] == MOD_TYPE_NONE))) { + if ((wc->desc->ports == 4) && ((wc->ctlreg & 0x10) || (wc->modtype[NUM_MODULES] == MOD_TYPE_NONE))) { writechunk[EFRAME_SIZE + 2] = 0; for (y = 0; y < 4; y++) { if (wc->modtype[y] == MOD_TYPE_NONE) @@ -958,11 +1005,11 @@ static inline int wctdm_setreg_full(struct wctdm *wc, int card, int addr, int va int hit=0; int ret; - /* if a QRV card, use only its first channel */ - if (wc->modtype[card] == MOD_TYPE_QRV) - { - if (card & 3) return(0); - } + /* QRV and BRI cards are only addressed at their first "port" */ + if ((card & 0x03) && ((wc->modtype[card] == MOD_TYPE_QRV) || + (wc->modtype[card] == MOD_TYPE_BRI))) + return 0; + do { spin_lock_irqsave(&wc->reglock, flags); hit = empty_slot(wc, card); @@ -984,12 +1031,12 @@ static inline int wctdm_setreg_intr(struct wctdm *wc, int card, int addr, int va { return wctdm_setreg_full(wc, card, addr, val, 1); } -static inline int wctdm_setreg(struct wctdm *wc, int card, int addr, int val) +inline int wctdm_setreg(struct wctdm *wc, int card, int addr, int val) { return wctdm_setreg_full(wc, card, addr, val, 0); } -static inline int wctdm_getreg(struct wctdm *wc, int card, int addr) +inline int wctdm_getreg(struct wctdm *wc, int card, int addr) { unsigned long flags; int hit; @@ -1028,14 +1075,15 @@ static inline int wctdm_getreg(struct wctdm *wc, int card, int addr) return ret; } + static inline unsigned char wctdm_vpm_in(struct wctdm *wc, int unit, const unsigned int addr) { - return wctdm_getreg(wc, unit + NUM_CARDS, addr); + return wctdm_getreg(wc, unit + NUM_MODULES, addr); } static inline void wctdm_vpm_out(struct wctdm *wc, int unit, const unsigned int addr, const unsigned char val) { - wctdm_setreg(wc, unit + NUM_CARDS, addr, val); + wctdm_setreg(wc, unit + NUM_MODULES, addr, val); } /* TODO: this should go in the dahdi_voicebus module... */ @@ -1055,6 +1103,7 @@ static inline void cmd_vpmadt032_retransmit(struct wctdm *wc) list_move_tail(&cmd->node, &vpmadt032->pending_cmds); } spin_unlock_irqrestore(&vpmadt032->list_lock, flags); + } static inline void cmd_retransmit(struct wctdm *wc) @@ -1064,9 +1113,11 @@ static inline void cmd_retransmit(struct wctdm *wc) /* Force retransmissions */ spin_lock_irqsave(&wc->reglock, flags); for (x=0;xcards;y++) { - if (!(wc->cmdq[y].cmds[x] & __CMD_FIN)) - wc->cmdq[y].cmds[x] &= ~(__CMD_TX | (0xff << 24)); + for (y = 0; y < wc->mods_per_board; y++) { + if (wc->modtype[y] != MOD_TYPE_BRI) { + if (!(wc->cmdq[y].cmds[x] & __CMD_FIN)) + wc->cmdq[y].cmds[x] &= ~(__CMD_TX | (0xff << 24)); + } } } spin_unlock_irqrestore(&wc->reglock, flags); @@ -1085,8 +1136,8 @@ static void extract_tdm_data(struct wctdm *wc, const u8 *sframe) int i; register u8 *chanchunk; - for (i = 0; i < wc->cards; i += 4) { - chanchunk = &wc->chans[0 + i]->readchunk[0]; + for (i = 0; i < wc->avchannels; i += 4) { + chanchunk = &wc->chans[0 + i]->chan.readchunk[0]; chanchunk[0] = sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*0]; chanchunk[1] = sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*1]; chanchunk[2] = sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*2]; @@ -1096,7 +1147,7 @@ static void extract_tdm_data(struct wctdm *wc, const u8 *sframe) chanchunk[6] = sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*6]; chanchunk[7] = sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*7]; - chanchunk = &wc->chans[1 + i]->readchunk[0]; + chanchunk = &wc->chans[1 + i]->chan.readchunk[0]; chanchunk[0] = sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*0]; chanchunk[1] = sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*1]; chanchunk[2] = sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*2]; @@ -1106,7 +1157,7 @@ static void extract_tdm_data(struct wctdm *wc, const u8 *sframe) chanchunk[6] = sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*6]; chanchunk[7] = sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*7]; - chanchunk = &wc->chans[2 + i]->readchunk[0]; + chanchunk = &wc->chans[2 + i]->chan.readchunk[0]; chanchunk[0] = sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*0]; chanchunk[1] = sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*1]; chanchunk[2] = sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*2]; @@ -1116,7 +1167,7 @@ static void extract_tdm_data(struct wctdm *wc, const u8 *sframe) chanchunk[6] = sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*6]; chanchunk[7] = sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*7]; - chanchunk = &wc->chans[3 + i]->readchunk[0]; + chanchunk = &wc->chans[3 + i]->chan.readchunk[0]; chanchunk[0] = sframe[3 + i + (EFRAME_SIZE + EFRAME_GAP)*0]; chanchunk[1] = sframe[3 + i + (EFRAME_SIZE + EFRAME_GAP)*1]; chanchunk[2] = sframe[3 + i + (EFRAME_SIZE + EFRAME_GAP)*2]; @@ -1128,9 +1179,10 @@ static void extract_tdm_data(struct wctdm *wc, const u8 *sframe) } } -static inline void wctdm_receiveprep(struct wctdm *wc, unsigned char *readchunk) +static inline void wctdm_receiveprep(struct wctdm *wc, const u8 *readchunk) { int x,y; + bool irqmiss = 0; unsigned char expected; if (unlikely(!is_good_frame(readchunk))) @@ -1139,38 +1191,56 @@ static inline void wctdm_receiveprep(struct wctdm *wc, unsigned char *readchunk) if (likely(wc->initialized)) extract_tdm_data(wc, readchunk); - for (x=0;xrxident+1; wc->rxident = readchunk[EFRAME_SIZE + 1]; if (wc->rxident != expected) { - wc->span.irqmisses++; + irqmiss = 1; cmd_retransmit(wc); } } - for (y=0;y < wc->cards;y++) { + for (y = 0; y < wc->avchannels; y++) { if (x < 3) cmd_decipher(wc, readchunk, y); } if (wc->vpm100) { - for (y=NUM_CARDS;y < NUM_CARDS + NUM_EC; y++) + for (y = NUM_MODULES; y < NUM_MODULES + NUM_EC; y++) cmd_decipher(wc, readchunk, y); - } else if (wc->vpmadt032) { + } else if (wc->vpmadt032) cmd_decipher_vpmadt032(wc, readchunk); - } readchunk += (EFRAME_SIZE + EFRAME_GAP); } + /* XXX We're wasting 8 taps. We should get closer :( */ if (likely(wc->initialized)) { - for (x = 0; x < wc->desc->ports; x++) { - if (wc->cardflag & (1 << x)) - dahdi_ec_chunk(wc->chans[x], wc->chans[x]->readchunk, wc->chans[x]->writechunk); + for (x = 0; x < wc->avchannels; x++) { + struct dahdi_chan *c = &wc->chans[x]->chan; + dahdi_ec_chunk(c, c->readchunk, c->writechunk); + } + + for (x = 0; x < MAX_SPANS; x++) { + if (wc->spans[x]) { + struct dahdi_span *s = &wc->spans[x]->span; +#if 1 + /* Check for digital spans */ + if (s->chanconfig == b400m_chanconfig) { + BUG_ON(!is_hx8(wc)); + if (s->flags & DAHDI_FLAG_RUNNING) + b400m_dchan(s); + + } +#endif + dahdi_receive(s); + if (unlikely(irqmiss)) + ++s->irqmisses; + } } - dahdi_receive(&wc->span); } + /* Wake up anyone sleeping to read/write a new register */ - wake_up_interruptible(&wc->regq); + wake_up_interruptible_all(&wc->regq); } static int wait_access(struct wctdm *wc, int card) @@ -1193,7 +1263,8 @@ static int wait_access(struct wctdm *wc, int card) } - if(count > (MAX-1)) dev_notice(&wc->vb.pdev->dev, " ##### Loop error (%02x) #####\n", data); + if (count > (MAX-1)) + dev_notice(&wc->vb.pdev->dev, " ##### Loop error (%02x) #####\n", data); return 0; } @@ -1219,7 +1290,7 @@ static int wctdm_proslic_setreg_indirect(struct wctdm *wc, int card, unsigned ch if (address == 255) return 0; } - if(!wait_access(wc, card)) { + if (!wait_access(wc, card)) { wctdm_setreg(wc, card, IDA_LO,(unsigned char)(data & 0xFF)); wctdm_setreg(wc, card, IDA_HI,(unsigned char)((data & 0xFF00)>>8)); wctdm_setreg(wc, card, IAA,address); @@ -1258,9 +1329,10 @@ static int wctdm_proslic_init_indirect_regs(struct wctdm *wc, int card) { unsigned char i; - for (i = 0; i < ARRAY_SIZE(indirect_regs); i++) - { - if(wctdm_proslic_setreg_indirect(wc, card, indirect_regs[i].address,indirect_regs[i].initial)) + for (i = 0; i < ARRAY_SIZE(indirect_regs); i++) { + if (wctdm_proslic_setreg_indirect(wc, card, + indirect_regs[i].address, + indirect_regs[i].initial)) return -1; } @@ -1275,7 +1347,8 @@ static int wctdm_proslic_verify_indirect_regs(struct wctdm *wc, int card) for (i = 0; i < ARRAY_SIZE(indirect_regs); i++) { - if((j = wctdm_proslic_getreg_indirect(wc, card, (unsigned char) indirect_regs[i].address)) < 0) { + j = wctdm_proslic_getreg_indirect(wc, card, (unsigned char) indirect_regs[i].address); + if (j < 0) { dev_notice(&wc->vb.pdev->dev, "Failed to read indirect register %d\n", i); return -1; } @@ -1389,7 +1462,7 @@ static inline void wctdm_qrvdri_check_hook(struct wctdm *wc, int card) { b1 = wc->qrvhook[qrvcard + 2]; if (debug) dev_info(&wc->vb.pdev->dev, "QRV channel %d rx state changed to %d\n",qrvcard,wc->qrvhook[qrvcard + 2]); - dahdi_hooksig(wc->chans[qrvcard], + dahdi_hooksig(wc->aspan->span.chans[qrvcard], (b1) ? DAHDI_RXSIG_OFFHOOK : DAHDI_RXSIG_ONHOOK); wc->qrvdebtime[card] = 0; } @@ -1403,7 +1476,7 @@ if (debug) dev_info(&wc->vb.pdev->dev, "QRV channel %d rx state changed to %d\n" { b1 = wc->qrvhook[qrvcard + 3]; if (debug) dev_info(&wc->vb.pdev->dev, "QRV channel %d rx state changed to %d\n",qrvcard + 1,wc->qrvhook[qrvcard + 3]); - dahdi_hooksig(wc->chans[qrvcard + 1], + dahdi_hooksig(wc->aspan->span.chans[qrvcard + 1], (b1) ? DAHDI_RXSIG_OFFHOOK : DAHDI_RXSIG_ONHOOK); wc->qrvdebtime[card] = 0; } @@ -1430,7 +1503,7 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) wctdm_setreg_intr(wc, card, 5, 0x8); } if (!fxo->offhook) { - if(fwringdetect || neonmwi_monitor) { + if (fwringdetect || neonmwi_monitor) { /* Look for ring status bits (Ring Detect Signal Negative and * Ring Detect Signal Positive) to transition back and forth * some number of times to indicate that a ring is occurring. @@ -1453,11 +1526,11 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) /* If ring detect signal has transversed */ if (res && res != fxo->lastrdtx) { /* if there are at least 3 ring polarity transversals */ - if(++fxo->lastrdtx_count >= 2) { + if (++fxo->lastrdtx_count >= 2) { fxo->wasringing = 1; if (debug) - dev_info(&wc->vb.pdev->dev, "FW RING on %d/%d!\n", wc->span.spanno, card + 1); - dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING); + dev_info(&wc->vb.pdev->dev, "FW RING on %d/%d!\n", wc->aspan->span.spanno, card + 1); + dahdi_hooksig(wc->aspan->span.chans[card], DAHDI_RXSIG_RING); fxo->ringdebounce = ringdebounce / 16; } else { fxo->lastrdtx = res; @@ -1473,8 +1546,8 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) } else if (--fxo->ringdebounce == 0) { fxo->wasringing = 0; if (debug) - dev_info(&wc->vb.pdev->dev, "FW NO RING on %d/%d!\n", wc->span.spanno, card + 1); - dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); + dev_info(&wc->vb.pdev->dev, "FW NO RING on %d/%d!\n", wc->aspan->span.spanno, card + 1); + dahdi_hooksig(wc->aspan->span.chans[card], DAHDI_RXSIG_OFFHOOK); } } } else { @@ -1484,9 +1557,9 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) if (fxo->ringdebounce >= DAHDI_CHUNKSIZE * ringdebounce) { if (!fxo->wasringing) { fxo->wasringing = 1; - dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING); + dahdi_hooksig(wc->aspan->span.chans[card], DAHDI_RXSIG_RING); if (debug) - dev_info(&wc->vb.pdev->dev, "RING on %d/%d!\n", wc->span.spanno, card + 1); + dev_info(&wc->vb.pdev->dev, "RING on %d/%d!\n", wc->aspan->span.spanno, card + 1); } fxo->ringdebounce = DAHDI_CHUNKSIZE * ringdebounce; } @@ -1495,9 +1568,9 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) if (fxo->ringdebounce <= 0) { if (fxo->wasringing) { fxo->wasringing = 0; - dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); + dahdi_hooksig(wc->aspan->span.chans[card], DAHDI_RXSIG_OFFHOOK); if (debug) - dev_info(&wc->vb.pdev->dev, "NO RING on %d/%d!\n", wc->span.spanno, card + 1); + dev_info(&wc->vb.pdev->dev, "NO RING on %d/%d!\n", wc->aspan->span.spanno, card + 1); } fxo->ringdebounce = 0; } @@ -1511,12 +1584,11 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) if (fxovoltage) { if (!(wc->intcount % 100)) { - dev_info(&wc->vb.pdev->dev, "Port %d: Voltage: %d Debounce %d\n", card + 1, - b, fxo->battdebounce); + dev_info(&wc->vb.pdev->dev, "Port %d: Voltage: %d Debounce %d\n", card + 1, b, fxo->battdebounce); } } - if (unlikely(DAHDI_RXSIG_INITIAL == wc->chans[card]->rxhooksig)) { + if (unlikely(DAHDI_RXSIG_INITIAL == wc->aspan->span.chans[card]->rxhooksig)) { /* * dahdi-base will set DAHDI_RXSIG_INITIAL after a * DAHDI_STARTUP or DAHDI_CHANCONFIG ioctl so that new events @@ -1551,10 +1623,10 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) if (--fxo->battdebounce == 0) { fxo->battery = BATTERY_LOST; if (debug) - dev_info(&wc->vb.pdev->dev, "NO BATTERY on %d/%d!\n", wc->span.spanno, card + 1); + dev_info(&wc->vb.pdev->dev, "NO BATTERY on %d/%d!\n", wc->aspan->span.spanno, card + 1); #ifdef JAPAN if (!wc->ohdebounce && wc->offhook) { - dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_ONHOOK); + dahdi_hooksig(wc->aspan->chans[card], DAHDI_RXSIG_ONHOOK); if (debug) dev_info(&wc->vb.pdev->dev, "Signalled On Hook\n"); #ifdef ZERO_BATT_RING @@ -1562,7 +1634,7 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) #endif } #else - dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_ONHOOK); + dahdi_hooksig(wc->aspan->span.chans[card], DAHDI_RXSIG_ONHOOK); /* set the alarm timer, taking into account that part of its time period has already passed while debouncing occurred */ fxo->battalarm = (battalarm - battdebounce) / MS_PER_CHECK_HOOK; @@ -1592,18 +1664,22 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) /* going to BATTERY_PRESENT, see if we are there yet */ if (--fxo->battdebounce == 0) { fxo->battery = BATTERY_PRESENT; - if (debug) - dev_info(&wc->vb.pdev->dev, "BATTERY on %d/%d (%s)!\n", wc->span.spanno, card + 1, - (b < 0) ? "-" : "+"); + if (debug) { + dev_info(&wc->vb.pdev->dev, + "BATTERY on %d/%d (%s)!\n", + wc->aspan->span.spanno, + card + 1, + (b < 0) ? "-" : "+"); + } #ifdef ZERO_BATT_RING if (wc->onhook) { wc->onhook = 0; - dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); + dahdi_hooksig(wc->aspan->chans[card], DAHDI_RXSIG_OFFHOOK); if (debug) dev_info(&wc->vb.pdev->dev, "Signalled Off Hook\n"); } #else - dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); + dahdi_hooksig(wc->aspan->span.chans[card], DAHDI_RXSIG_OFFHOOK); #endif /* set the alarm timer, taking into account that part of its time period has already passed while debouncing occurred */ @@ -1633,7 +1709,7 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) if (--fxo->battalarm == 0) { /* the alarm timer has expired, so update the battery alarm state for this channel */ - dahdi_alarm_channel(wc->chans[card], fxo->battery == BATTERY_LOST ? DAHDI_ALARM_RED : DAHDI_ALARM_NONE); + dahdi_alarm_channel(wc->aspan->span.chans[card], fxo->battery == BATTERY_LOST ? DAHDI_ALARM_RED : DAHDI_ALARM_NONE); } } @@ -1646,7 +1722,7 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) fxo->polarity, fxo->lastpol); if (fxo->polarity) - dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_POLARITY); + dahdi_qevent_lock(wc->aspan->span.chans[card], DAHDI_EVENT_POLARITY); fxo->polarity = fxo->lastpol; } } @@ -1665,8 +1741,8 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) fxo->neonmwi_last_voltage = b; if (NEONMWI_ON_DEBOUNCE == fxo->neonmwi_debounce) { fxo->neonmwi_offcounter = neonmwi_offlimit_cycles; - if(0 == fxo->neonmwi_state) { - dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_NEONMWI_ACTIVE); + if (0 == fxo->neonmwi_state) { + dahdi_qevent_lock(wc->aspan->span.chans[card], DAHDI_EVENT_NEONMWI_ACTIVE); fxo->neonmwi_state = 1; if (debug) dev_info(&wc->vb.pdev->dev, "NEON MWI active for card %d\n", card+1); @@ -1685,7 +1761,7 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) if (fxo->neonmwi_state && 0 < fxo->neonmwi_offcounter ) { fxo->neonmwi_offcounter--; if (0 == fxo->neonmwi_offcounter) { - dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_NEONMWI_INACTIVE); + dahdi_qevent_lock(wc->aspan->span.chans[card], DAHDI_EVENT_NEONMWI_INACTIVE); fxo->neonmwi_state = 0; if (debug) dev_info(&wc->vb.pdev->dev, "NEON MWI cleared for card %d\n", card+1); @@ -1711,7 +1787,7 @@ static void wctdm_fxs_off_hook(struct wctdm *wc, const int card) SLIC_LF_ACTIVE_FWD; break; } - dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); + dahdi_hooksig(wc->aspan->span.chans[card], DAHDI_RXSIG_OFFHOOK); if (robust) wctdm_init_proslic(wc, card, 1, 0, 1); fxs->oldrxhook = 1; @@ -1722,7 +1798,7 @@ static void wctdm_fxs_on_hook(struct wctdm *wc, const int card) struct fxs *const fxs = &wc->mods[card].fxs; if (debug & DEBUG_CARD) dev_info(&wc->vb.pdev->dev, "wctdm: Card %d Going on hook\n", card); - dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_ONHOOK); + dahdi_hooksig(wc->aspan->span.chans[card], DAHDI_RXSIG_ONHOOK); fxs->oldrxhook = 0; } @@ -1772,14 +1848,14 @@ static inline void wctdm_vpm_check(struct wctdm *wc, int x) { if (wc->cmdq[x].isrshadow[0]) { if (debug & DEBUG_ECHOCAN) - dev_info(&wc->vb.pdev->dev, "VPM: Detected dtmf ON channel %02x on chip %d!\n", wc->cmdq[x].isrshadow[0], x - NUM_CARDS); + dev_info(&wc->vb.pdev->dev, "VPM: Detected dtmf ON channel %02x on chip %d!\n", wc->cmdq[x].isrshadow[0], x - NUM_MODULES); wc->sethook[x] = CMD_WR(0xb9, wc->cmdq[x].isrshadow[0]); wc->cmdq[x].isrshadow[0] = 0; /* Cancel most recent lookup, if there is one */ wc->cmdq[x].cmds[USER_COMMANDS+0] = 0x00000000; } else if (wc->cmdq[x].isrshadow[1]) { if (debug & DEBUG_ECHOCAN) - dev_info(&wc->vb.pdev->dev, "VPM: Detected dtmf OFF channel %02x on chip %d!\n", wc->cmdq[x].isrshadow[1], x - NUM_CARDS); + dev_info(&wc->vb.pdev->dev, "VPM: Detected dtmf OFF channel %02x on chip %d!\n", wc->cmdq[x].isrshadow[1], x - NUM_MODULES); wc->sethook[x] = CMD_WR(0xbd, wc->cmdq[x].isrshadow[1]); wc->cmdq[x].isrshadow[1] = 0; /* Cancel most recent lookup, if there is one */ @@ -1791,6 +1867,7 @@ static int echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *e struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec) { struct wctdm *wc = chan->pvt; + struct wctdm_chan *wchan = container_of(chan, struct wctdm_chan, chan); const struct dahdi_echocan_ops *ops; const struct dahdi_echocan_features *features; @@ -1810,7 +1887,7 @@ static int echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *e return -EINVAL; } - *ec = wc->ec[chan->chanpos - 1]; + *ec = &wchan->ec; (*ec)->ops = ops; (*ec)->features = *features; @@ -1818,8 +1895,8 @@ static int echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *e int channel; int unit; - channel = (chan->chanpos - 1); - unit = (chan->chanpos - 1) & 0x3; + channel = wchan->timeslot; + unit = wchan->timeslot & 0x3; if (wc->vpm100 < 2) channel >>= 2; @@ -1835,7 +1912,7 @@ static int echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *e ADT_COMP_ALAW : ADT_COMP_ULAW; return vpmadt032_echocan_create(wc->vpmadt032, - chan->chanpos-1, comp, ecp, p); + wchan->timeslot, comp, ecp, p); } else { return -ENODEV; } @@ -1844,14 +1921,15 @@ static int echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *e static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec) { struct wctdm *wc = chan->pvt; + struct wctdm_chan *wchan = container_of(chan, struct wctdm_chan, chan); memset(ec, 0, sizeof(*ec)); if (wc->vpm100) { int channel; int unit; - channel = (chan->chanpos - 1); - unit = (chan->chanpos - 1) & 0x3; + channel = wchan->timeslot; + unit = wchan->timeslot & 0x3; if (wc->vpm100 < 2) channel >>= 2; @@ -1860,7 +1938,7 @@ static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec unit, channel); wctdm_vpm_out(wc, unit, channel, 0x01); } else if (wc->vpmadt032) { - vpmadt032_echocan_free(wc->vpmadt032, chan->chanpos - 1, ec); + vpmadt032_echocan_free(wc->vpmadt032, wchan->timeslot, ec); } } @@ -1915,8 +1993,8 @@ static inline void wctdm_isr_misc(struct wctdm *wc) return; } - for (x=0;xcards;x++) { - if (wc->cardflag & (1 << x)) { + for (x = 0; x < wc->mods_per_board; x++) { + if (wc->modmap & (1 << x)) { if (wc->modtype[x] == MOD_TYPE_FXS) { wctdm_isr_misc_fxs(wc, x); } else if (wc->modtype[x] == MOD_TYPE_FXO) { @@ -1927,9 +2005,8 @@ static inline void wctdm_isr_misc(struct wctdm *wc) } } if (wc->vpm100 > 0) { - for (x=NUM_CARDS;xsframe, vbb, sizeof(frame->sframe)); + spin_lock(&wc->frame_list_lock); + list_add_tail(&frame->node, &wc->frame_list); + spin_unlock(&wc->frame_list_lock); + + /* Wake up anyone waiting for a new packet. */ + wake_up(&wc->regq); + return; +} + +static void handle_hx8_receive(struct voicebus *vb, struct list_head *buffers) +{ + struct wctdm *wc = container_of(vb, struct wctdm, vb); + struct vbb *vbb; + list_for_each_entry(vbb, buffers, entry) + handle_hx8_bootmode_receive(wc, vbb->data); +} + +static void handle_hx8_transmit(struct voicebus *vb, struct list_head *buffers) +{ + struct vbb *vbb, *n; + + list_for_each_entry_safe(vbb, n, buffers, entry) { + list_del(&vbb->entry); + kmem_cache_free(voicebus_vbb_cache, vbb); + } +} + static int wctdm_voicedaa_insane(struct wctdm *wc, int card) { int blah; @@ -1972,7 +2098,7 @@ static int wctdm_proslic_insane(struct wctdm *wc, int card) insane_report=0; blah = wctdm_getreg(wc, card, 0); - if (debug & DEBUG_CARD) + if (blah != 0xff && (debug & DEBUG_CARD)) dev_info(&wc->vb.pdev->dev, "ProSLIC on module %d, product %d, version %d\n", card, (blah & 0x30) >> 4, (blah & 0xf)); #if 0 @@ -2045,7 +2171,7 @@ static int wctdm_proslic_powerleak_test(struct wctdm *wc, int card) /* Wait for one second */ origjiffies = jiffies; - while((vbat = wctdm_getreg(wc, card, 82)) > 0x6) { + while ((vbat = wctdm_getreg(wc, card, 82)) > 0x6) { if ((jiffies - origjiffies) >= (HZ/2)) break;; } @@ -2079,7 +2205,7 @@ static int wctdm_powerup_proslic(struct wctdm *wc, int card, int fast) if (fast) return 0; - while((vbat = wctdm_getreg(wc, card, 82)) < 0xc0) { + while ((vbat = wctdm_getreg(wc, card, 82)) < 0xc0) { /* Wait no more than 500ms */ if ((jiffies - origjiffies) > HZ/2) { break; @@ -2110,25 +2236,6 @@ static int wctdm_powerup_proslic(struct wctdm *wc, int card, int fast) /* Engage DC-DC converter */ wctdm_setreg(wc, card, 93, 0x19 /* was 0x19 */); -#if 0 - origjiffies = jiffies; - while(0x80 & wctdm_getreg(wc, card, 93)) { - if ((jiffies - origjiffies) > 2 * HZ) { - dev_info(&wc->vb.pdev->dev, "Timeout waiting for DC-DC calibration on module %d\n", card); - return -1; - } - } - -#if 0 - /* Wait a full two seconds */ - while((jiffies - origjiffies) < 2 * HZ); - - /* Just check to be sure */ - vbat = wctdm_getreg(wc, card, 82); - dev_info(&wc->vb.pdev->dev, "ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n", - card, vbat * 376 / 1000, vbat, (int)(((jiffies - origjiffies) * 1000 / HZ))); -#endif -#endif return 0; } @@ -2147,8 +2254,8 @@ static int wctdm_proslic_manual_calibrate(struct wctdm *wc, int card) wctdm_setreg(wc, card, 96, 0x47); //(0x47) Calibrate common mode and differential DAC mode DAC + ILIM origjiffies=jiffies; - while( wctdm_getreg(wc,card,96)!=0 ){ - if((jiffies-origjiffies)>80) + while (wctdm_getreg(wc, card, 96) != 0) { + if ((jiffies-origjiffies) > 80) return -1; } //Initialized DR 98 and 99 to get consistant results. @@ -2158,7 +2265,7 @@ static int wctdm_proslic_manual_calibrate(struct wctdm *wc, int card) /*******************************This is also available as a function *******************************************/ // Delay 10ms origjiffies=jiffies; - while((jiffies-origjiffies)<1); + while ((jiffies-origjiffies) < 1); wctdm_proslic_setreg_indirect(wc, card, 88,0); wctdm_proslic_setreg_indirect(wc,card,89,0); wctdm_proslic_setreg_indirect(wc,card,90,0); @@ -2173,8 +2280,8 @@ static int wctdm_proslic_manual_calibrate(struct wctdm *wc, int card) { wctdm_setreg(wc, card, 98,i); origjiffies=jiffies; - while((jiffies-origjiffies)<4); - if((wctdm_getreg(wc,card,88)) == 0) + while ((jiffies-origjiffies) < 4); + if ((wctdm_getreg(wc, card, 88)) == 0) break; } // for @@ -2182,15 +2289,16 @@ static int wctdm_proslic_manual_calibrate(struct wctdm *wc, int card) { wctdm_setreg(wc, card, 99,i); origjiffies=jiffies; - while((jiffies-origjiffies)<4); - if((wctdm_getreg(wc,card,89)) == 0) + while ((jiffies-origjiffies) < 4); + if ((wctdm_getreg(wc, card, 89)) == 0) break; }//for /*******************************The preceding is the manual gain mismatch calibration****************************/ /**********************************The following is the longitudinal Balance Cal***********************************/ wctdm_setreg(wc,card,64,1); - while((jiffies-origjiffies)<10); // Sleep 100? + while ((jiffies-origjiffies) < 10) /* Sleep 100? */ + ; wctdm_setreg(wc, card, 64, 0); wctdm_setreg(wc, card, 23, 0x4); // enable interrupt for the balance Cal @@ -2220,7 +2328,7 @@ static int wctdm_proslic_calibrate(struct wctdm *wc, int card) /* Wait for it to finish */ origjiffies = jiffies; - while(wctdm_getreg(wc, card, 96)) { + while (wctdm_getreg(wc, card, 96)) { if ((jiffies - origjiffies) > 2 * HZ) { dev_notice(&wc->vb.pdev->dev, "Timeout waiting for calibration of module %d\n", card); return -1; @@ -2237,11 +2345,12 @@ static int wctdm_proslic_calibrate(struct wctdm *wc, int card) return 0; } -static void wait_just_a_bit(int foo) +void wait_just_a_bit(int foo) { long newjiffies; newjiffies = jiffies + foo; - while(jiffies < newjiffies); + while (jiffies < newjiffies) + ; } /********************************************************************* @@ -2355,18 +2464,36 @@ static int set_vmwi(struct wctdm *wc, int chan_idx) return 0; } +static void wctdm_voicedaa_set_ts(struct wctdm *wc, int card, int ts) +{ + wctdm_setreg(wc, card, 34, (ts * 8) & 0xff); + wctdm_setreg(wc, card, 35, (ts * 8) >> 8); + wctdm_setreg(wc, card, 36, (ts * 8) & 0xff); + wctdm_setreg(wc, card, 37, (ts * 8) >> 8); + + if (debug) + dev_info(&wc->vb.pdev->dev, "voicedaa: card %d new timeslot: %d\n", card + 1, ts); +} + static int wctdm_init_voicedaa(struct wctdm *wc, int card, int fast, int manual, int sane) { unsigned char reg16=0, reg26=0, reg30=0, reg31=0; + unsigned long flags; long newjiffies; - if (wc->modtype[card & 0xfc] == MOD_TYPE_QRV) return -2; + if ((wc->modtype[card & 0xfc] == MOD_TYPE_QRV) || + (wc->modtype[card & 0xfc] == MOD_TYPE_BRI)) + return -2; + spin_lock_irqsave(&wc->reglock, flags); wc->modtype[card] = MOD_TYPE_NONE; + spin_unlock_irqrestore(&wc->reglock, flags); /* Wait just a bit */ wait_just_a_bit(HZ/10); + spin_lock_irqsave(&wc->reglock, flags); wc->modtype[card] = MOD_TYPE_FXO; + spin_unlock_irqrestore(&wc->reglock, flags); wait_just_a_bit(HZ/10); if (!sane && wctdm_voicedaa_insane(wc, card)) @@ -2391,7 +2518,7 @@ static int wctdm_init_voicedaa(struct wctdm *wc, int card, int fast, int manual, reg16 |= (fxo_modes[_opermode].rt); wctdm_setreg(wc, card, 16, reg16); - if(fwringdetect || neonmwi_monitor) { + if (fwringdetect || neonmwi_monitor) { /* Enable ring detector full-wave rectifier mode */ wctdm_setreg(wc, card, 18, 2); wctdm_setreg(wc, card, 24, 0); @@ -2421,11 +2548,7 @@ static int wctdm_init_voicedaa(struct wctdm *wc, int card, int fast, int manual, reg31 |= (fxo_modes[_opermode].ohs2 << 3); wctdm_setreg(wc, card, 31, reg31); - /* Set Transmit/Receive timeslot */ - wctdm_setreg(wc, card, 34, (card * 8) & 0xff); - wctdm_setreg(wc, card, 35, (card * 8) >> 8); - wctdm_setreg(wc, card, 36, (card * 8) & 0xff); - wctdm_setreg(wc, card, 37, (card * 8) >> 8); + wctdm_voicedaa_set_ts(wc, card, card); /* Enable ISO-Cap */ wctdm_setreg(wc, card, 6, 0x00); @@ -2433,7 +2556,7 @@ static int wctdm_init_voicedaa(struct wctdm *wc, int card, int fast, int manual, /* Wait 1000ms for ISO-cap to come up */ newjiffies = jiffies; newjiffies += 2 * HZ; - while((jiffies < newjiffies) && !(wctdm_getreg(wc, card, 11) & 0xf0)) + while ((jiffies < newjiffies) && !(wctdm_getreg(wc, card, 11) & 0xf0)) wait_just_a_bit(HZ/10); if (!(wctdm_getreg(wc, card, 11) & 0xf0)) { @@ -2451,23 +2574,41 @@ static int wctdm_init_voicedaa(struct wctdm *wc, int card, int fast, int manual, wctdm_set_hwgain(wc, card, fxotxgain, 1); wctdm_set_hwgain(wc, card, fxorxgain, 0); - if(debug) + if (debug) dev_info(&wc->vb.pdev->dev, "DEBUG fxotxgain:%i.%i fxorxgain:%i.%i\n", (wctdm_getreg(wc, card, 38)/16) ? -(wctdm_getreg(wc, card, 38) - 16) : wctdm_getreg(wc, card, 38), (wctdm_getreg(wc, card, 40)/16) ? -(wctdm_getreg(wc, card, 40) - 16) : wctdm_getreg(wc, card, 40), (wctdm_getreg(wc, card, 39)/16) ? -(wctdm_getreg(wc, card, 39) - 16): wctdm_getreg(wc, card, 39), (wctdm_getreg(wc, card, 41)/16)?-(wctdm_getreg(wc, card, 41) - 16) : wctdm_getreg(wc, card, 41)); return 0; } +static void wctdm_proslic_set_ts(struct wctdm *wc, int card, int ts) +{ + wctdm_setreg(wc, card, 2, (ts * 8) & 0xff); /* Tx Start count low byte 0 */ + wctdm_setreg(wc, card, 3, (ts * 8) >> 8); /* Tx Start count high byte 0 */ + wctdm_setreg(wc, card, 4, (ts * 8) & 0xff); /* Rx Start count low byte 0 */ + wctdm_setreg(wc, card, 5, (ts * 8) >> 8); /* Rx Start count high byte 0 */ + + if (debug) + dev_info(&wc->vb.pdev->dev, "proslic: card %d new timeslot: %d\n", card + 1, ts); +} + static int wctdm_init_proslic(struct wctdm *wc, int card, int fast, int manual, int sane) { unsigned short tmp[5]; + unsigned long flags; unsigned char r19,r9; int x; int fxsmode=0; if (wc->modtype[card & 0xfc] == MOD_TYPE_QRV) return -2; + spin_lock_irqsave(&wc->reglock, flags); + wc->modtype[card] = MOD_TYPE_FXS; + spin_unlock_irqrestore(&wc->reglock, flags); + + wait_just_a_bit(HZ/10); + /* Sanity check the ProSLIC */ if (!sane && wctdm_proslic_insane(wc, card)) return -2; @@ -2537,7 +2678,7 @@ static int wctdm_init_proslic(struct wctdm *wc, int card, int fast, int manual, } #ifndef NO_CALIBRATION /* Perform calibration */ - if(manual) { + if (manual) { if (wctdm_proslic_manual_calibrate(wc, card)) { //dev_notice(&wc->vb.pdev->dev, "Proslic failed on Manual Calibration\n"); if (wctdm_proslic_manual_calibrate(wc, card)) { @@ -2548,7 +2689,7 @@ static int wctdm_init_proslic(struct wctdm *wc, int card, int fast, int manual, } } else { - if(wctdm_proslic_calibrate(wc, card)) { + if (wctdm_proslic_calibrate(wc, card)) { //dev_notice(&wc->vb.pdev->dev, "ProSlic died on Auto Calibration.\n"); if (wctdm_proslic_calibrate(wc, card)) { dev_notice(&wc->vb.pdev->dev, "Proslic Failed on Second Attempt to Auto Calibrate\n"); @@ -2603,11 +2744,10 @@ static int wctdm_init_proslic(struct wctdm *wc, int card, int fast, int manual, wctdm_setreg(wc, card, 1, 0x20); else wctdm_setreg(wc, card, 1, 0x28); - // U-Law 8-bit interface - wctdm_setreg(wc, card, 2, (card * 8) & 0xff); // Tx Start count low byte 0 - wctdm_setreg(wc, card, 3, (card * 8) >> 8); // Tx Start count high byte 0 - wctdm_setreg(wc, card, 4, (card * 8) & 0xff); // Rx Start count low byte 0 - wctdm_setreg(wc, card, 5, (card * 8) >> 8); // Rx Start count high byte 0 + + /* U-Law 8-bit interface */ + wctdm_proslic_set_ts(wc, card, card); + wctdm_setreg(wc, card, 18, 0xff); // clear all interrupt wctdm_setreg(wc, card, 19, 0xff); wctdm_setreg(wc, card, 20, 0xff); @@ -2707,10 +2847,23 @@ static int wctdm_init_proslic(struct wctdm *wc, int card, int fast, int manual, return 0; } +static void wctdm_qrvdri_set_ts(struct wctdm *wc, int card, int ts) +{ + wctdm_setreg(wc, card, 0x13, ts + 0x80); /* codec 2 tx, ts0 */ + wctdm_setreg(wc, card, 0x17, ts + 0x80); /* codec 0 rx, ts0 */ + wctdm_setreg(wc, card, 0x14, ts + 0x81); /* codec 1 tx, ts1 */ + wctdm_setreg(wc, card, 0x18, ts + 0x81); /* codec 1 rx, ts1 */ + + if (debug) + dev_info(&wc->vb.pdev->dev, "qrvdri: card %d new timeslot: %d\n", card + 1, ts); +} + static int wctdm_init_qrvdri(struct wctdm *wc, int card) { unsigned char x,y; - unsigned long endjif; + + if (MOD_TYPE_BRI == wc->modtype[card & 0xfc]) + return -2; /* have to set this, at least for now */ wc->modtype[card] = MOD_TYPE_QRV; @@ -2743,18 +2896,16 @@ static int wctdm_init_qrvdri(struct wctdm *wc, int card) wc->modtype[card] = MOD_TYPE_NONE; return 1; } - for(x = 0; x < 0x30; x++) + for (x = 0; x < 0x30; x++) { if ((x >= 0x1c) && (x <= 0x1e)) wctdm_setreg(wc,card,x,0xff); else wctdm_setreg(wc,card,x,0); } wctdm_setreg(wc,card,0,0x80); - endjif = jiffies + (HZ/10); - while(endjif > jiffies); + msleep(100); wctdm_setreg(wc,card,0,0x10); wctdm_setreg(wc,card,0,0x10); - endjif = jiffies + (HZ/10); - while(endjif > jiffies); + msleep(100); /* set up modes */ wctdm_setreg(wc,card,0,0x1c); /* set up I/O directions */ @@ -2765,11 +2916,9 @@ static int wctdm_init_qrvdri(struct wctdm *wc, int card) wctdm_setreg(wc,card,3,0x11); /* D0-7 */ wctdm_setreg(wc,card,4,0xa); /* D8-11 */ wctdm_setreg(wc,card,7,0); /* CS outputs */ - /* set up timeslots */ - wctdm_setreg(wc,card,0x13,card + 0x80); /* codec 2 tx, ts0 */ - wctdm_setreg(wc,card,0x17,card + 0x80); /* codec 0 rx, ts0 */ - wctdm_setreg(wc,card,0x14,card + 0x81); /* codec 1 tx, ts1 */ - wctdm_setreg(wc,card,0x18,card + 0x81); /* codec 1 rx, ts1 */ + + wctdm_qrvdri_set_ts(wc, card, card); + /* set up for max gains */ wctdm_setreg(wc,card,0x26,0x24); wctdm_setreg(wc,card,0x27,0x24); @@ -2782,9 +2931,9 @@ static int wctdm_init_qrvdri(struct wctdm *wc, int card) static void qrv_dosetup(struct dahdi_chan *chan,struct wctdm *wc) { -int qrvcard; -unsigned char r; -long l; + int qrvcard; + unsigned char r; + long l; /* actually do something with the values */ qrvcard = (chan->chanpos - 1) & 0xfc; @@ -2839,6 +2988,7 @@ long l; return; } + static int wctdm_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data) { struct wctdm_stats stats; @@ -3159,19 +3309,29 @@ static int wctdm_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long static int wctdm_open(struct dahdi_chan *chan) { - struct wctdm *wc = chan->pvt; - int channo = chan->chanpos - 1; + struct wctdm *wc; + int channo; unsigned long flags; - if (!(wc->cardflag & (1 << (chan->chanpos - 1)))) + wc = chan->pvt; + channo = chan->chanpos - 1; + +#if 0 + if (!(wc->modmap & (1 << (chan->chanpos - 1)))) + return -ENODEV; + if (wc->dead) return -ENODEV; +#endif + wc->usecount++; - /* Reset the mwi indicators */ - spin_lock_irqsave(&wc->reglock, flags); - wc->mods[channo].fxo.neonmwi_debounce = 0; - wc->mods[channo].fxo.neonmwi_offcounter = 0; - wc->mods[channo].fxo.neonmwi_state = 0; - spin_unlock_irqrestore(&wc->reglock, flags); + if (wc->modtype[channo] == MOD_TYPE_FXO) { + /* Reset the mwi indicators */ + spin_lock_irqsave(&wc->reglock, flags); + wc->mods[channo].fxo.neonmwi_debounce = 0; + wc->mods[channo].fxo.neonmwi_offcounter = 0; + wc->mods[channo].fxo.neonmwi_state = 0; + spin_unlock_irqrestore(&wc->reglock, flags); + } return 0; } @@ -3185,17 +3345,17 @@ static int wctdm_watchdog(struct dahdi_span *span, int event) static int wctdm_close(struct dahdi_chan *chan) { - struct wctdm *wc = chan->pvt; + struct wctdm *wc; int x; signed char reg; - for (x=0;xcards;x++) { - if (wc->modtype[x] == MOD_TYPE_FXS) { + + wc = chan->pvt; + for (x = 0; x < wc->mods_per_board; x++) { + if (MOD_TYPE_FXS == wc->modtype[x]) { wc->mods[x].fxs.idletxhookstate = POLARITY_XOR(x) ? SLIC_LF_ACTIVE_REV : SLIC_LF_ACTIVE_FWD; - } - if (wc->modtype[x] == MOD_TYPE_QRV) - { + } else if (MOD_TYPE_QRV == wc->modtype[x]) { int qrvcard = x & 0xfc; wc->qrvhook[x] = 0; @@ -3206,8 +3366,10 @@ static int wctdm_close(struct dahdi_chan *chan) wc->txgain[x] = 3599; wc->rxgain[x] = 1199; reg = 0; - if (!wc->qrvhook[qrvcard]) reg |= 1; - if (!wc->qrvhook[qrvcard + 1]) reg |= 0x10; + if (!wc->qrvhook[qrvcard]) + reg |= 1; + if (!wc->qrvhook[qrvcard + 1]) + reg |= 0x10; wc->sethook[qrvcard] = CMD_WR(3, reg); qrv_dosetup(chan,wc); } @@ -3219,8 +3381,8 @@ static int wctdm_close(struct dahdi_chan *chan) static int wctdm_hooksig(struct dahdi_chan *chan, enum dahdi_txsig txsig) { struct wctdm *wc = chan->pvt; + int reg = 0, qrvcard; - int reg=0,qrvcard; if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_QRV) { qrvcard = (chan->chanpos - 1) & 0xfc; switch(txsig) { @@ -3255,7 +3417,7 @@ static int wctdm_hooksig(struct dahdi_chan *chan, enum dahdi_txsig txsig) default: dev_notice(&wc->vb.pdev->dev, "wctdm24xxp: Can't set tx state to %d\n", txsig); } - } else { /* Else this is an fxs port */ + } else if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) { unsigned long flags; struct fxs *const fxs = &wc->mods[chan->chanpos - 1].fxs; spin_lock_irqsave(&fxs->lasttxhooklock, flags); @@ -3309,6 +3471,7 @@ static int wctdm_hooksig(struct dahdi_chan *chan, enum dahdi_txsig txsig) spin_unlock_irqrestore(&fxs->lasttxhooklock, flags); if (debug & DEBUG_CARD) dev_info(&wc->vb.pdev->dev, "Setting FXS hook state to %d (%02x)\n", txsig, reg); + } else { } return 0; } @@ -3337,7 +3500,7 @@ static void wctdm_dacs_connect(struct wctdm *wc, int srccard, int dstcard) /* proslic */ wctdm_setreg(wc, srccard, PCM_XMIT_START_COUNT_LSB, ((srccard+24) * 8) & 0xff); wctdm_setreg(wc, srccard, PCM_XMIT_START_COUNT_MSB, ((srccard+24) * 8) >> 8); - } else if(wc->modtype[srccard] == MOD_TYPE_FXO) { + } else if (wc->modtype[srccard] == MOD_TYPE_FXO) { /* daa */ wctdm_setreg(wc, srccard, 34, ((srccard+24) * 8) & 0xff); /* TX */ wctdm_setreg(wc, srccard, 35, ((srccard+24) * 8) >> 8); /* TX */ @@ -3348,7 +3511,7 @@ static void wctdm_dacs_connect(struct wctdm *wc, int srccard, int dstcard) /* proslic */ wctdm_setreg(wc, dstcard, PCM_RCV_START_COUNT_LSB, ((srccard+24) * 8) & 0xff); wctdm_setreg(wc, dstcard, PCM_RCV_START_COUNT_MSB, ((srccard+24) * 8) >> 8); - } else if(wc->modtype[dstcard] == MOD_TYPE_FXO) { + } else if (wc->modtype[dstcard] == MOD_TYPE_FXO) { /* daa */ wctdm_setreg(wc, dstcard, 36, ((srccard+24) * 8) & 0xff); /* RX */ wctdm_setreg(wc, dstcard, 37, ((srccard+24) * 8) >> 8); /* RX */ @@ -3363,10 +3526,10 @@ static void wctdm_dacs_disconnect(struct wctdm *wc, int card) dev_info(&wc->vb.pdev->dev, "wctdm_dacs_disconnect: restoring TX for %d and RX for %d\n",wc->dacssrc[card], card); /* restore TX (source card) */ - if(wc->modtype[wc->dacssrc[card]] == MOD_TYPE_FXS){ + if (wc->modtype[wc->dacssrc[card]] == MOD_TYPE_FXS) { wctdm_setreg(wc, wc->dacssrc[card], PCM_XMIT_START_COUNT_LSB, (wc->dacssrc[card] * 8) & 0xff); wctdm_setreg(wc, wc->dacssrc[card], PCM_XMIT_START_COUNT_MSB, (wc->dacssrc[card] * 8) >> 8); - } else if(wc->modtype[wc->dacssrc[card]] == MOD_TYPE_FXO){ + } else if (wc->modtype[wc->dacssrc[card]] == MOD_TYPE_FXO) { wctdm_setreg(wc, card, 34, (card * 8) & 0xff); wctdm_setreg(wc, card, 35, (card * 8) >> 8); } else { @@ -3374,10 +3537,10 @@ static void wctdm_dacs_disconnect(struct wctdm *wc, int card) } /* restore RX (this card) */ - if(wc->modtype[card] == MOD_TYPE_FXS){ - wctdm_setreg(wc, card, PCM_RCV_START_COUNT_LSB, (card * 8) & 0xff); - wctdm_setreg(wc, card, PCM_RCV_START_COUNT_MSB, (card * 8) >> 8); - } else if(wc->modtype[card] == MOD_TYPE_FXO){ + if (MOD_TYPE_FXS == wc->modtype[card]) { + wctdm_setreg(wc, card, PCM_RCV_START_COUNT_LSB, (card * 8) & 0xff); + wctdm_setreg(wc, card, PCM_RCV_START_COUNT_MSB, (card * 8) >> 8); + } else if (MOD_TYPE_FXO == wc->modtype[card]) { wctdm_setreg(wc, card, 36, (card * 8) & 0xff); wctdm_setreg(wc, card, 37, (card * 8) >> 8); } else { @@ -3392,12 +3555,12 @@ static int wctdm_dacs(struct dahdi_chan *dst, struct dahdi_chan *src) { struct wctdm *wc; - if(!nativebridge) + if (!nativebridge) return 0; /* should this return -1 since unsuccessful? */ wc = dst->pvt; - if(src) { + if (src) { wctdm_dacs_connect(wc, src->chanpos - 1, dst->chanpos - 1); if (debug) dev_info(&wc->vb.pdev->dev, "dacs connecct: %d -> %d!\n\n", src->chanpos, dst->chanpos); @@ -3409,77 +3572,177 @@ static int wctdm_dacs(struct dahdi_chan *dst, struct dahdi_chan *src) return 0; } -static int wctdm_initialize(struct wctdm *wc) +static struct wctdm_chan *wctdm_init_chan(struct wctdm *wc, struct wctdm_span *s, int chanoffset, int channo) +{ + struct wctdm_chan *c; + + c = kzalloc(sizeof(*c), GFP_KERNEL); + if (!c) + return NULL; + + /* Do not change the procfs representation for non-hx8 cards. */ + if (is_digital_span(s)) { + sprintf(c->chan.name, "WCBRI/%d/%d/%d", wc->pos, s->spanno, + channo); + } else { + sprintf(c->chan.name, "WCTDM/%d/%d", wc->pos, channo); + } + + c->chan.chanpos = channo+1; + c->chan.span = &s->span; + c->chan.pvt = wc; + c->timeslot = chanoffset + channo; + return c; +} + +#if 0 +/** + * wctdm_span_count() - Return the number of spans exported by this board. + * + * This is only called during initialization so let's just count the spans each + * time we need this information as opposed to storing another variable in the + * wctdm structure. + */ +static int wctdm_span_count(const struct wctdm *wc) +{ + int i; + int count = 0; + for (i = 0; i < MAX_SPANS; ++i) { + if (wc->spans[i]) + ++count; + } + return count; +} +#endif + +static struct wctdm_span *wctdm_init_span(struct wctdm *wc, int spanno, int chanoffset, int chancount, int digital_span) { int x; + static int first = 1; struct pci_dev *pdev = wc->vb.pdev; + struct wctdm_chan *c; + struct wctdm_span *s; + static int spancount; + + s = kzalloc(sizeof(*s), GFP_KERNEL); + if (!s) + return NULL; /* DAHDI stuff */ - sprintf(wc->span.name, "WCTDM/%d", wc->pos); - snprintf(wc->span.desc, sizeof(wc->span.desc) - 1, - "%s Board %d", wc->desc->name, wc->pos + 1); - snprintf(wc->span.location, sizeof(wc->span.location) - 1, + s->span.owner = THIS_MODULE; + s->span.offset = spanno; + + s->spanno = spancount++; + + /* Do not change the procfs representation for non-hx8 cards. */ + if (digital_span) + sprintf(s->span.name, "WCBRI/%d/%d", wc->pos, s->spanno); + else + sprintf(s->span.name, "WCTDM/%d", wc->pos); + + snprintf(s->span.desc, sizeof(s->span.desc) - 1, "%s Board %d", wc->desc->name, wc->pos + 1); + snprintf(s->span.location, sizeof(s->span.location) - 1, "PCI%s Bus %02d Slot %02d", (wc->flags[0] & FLAG_EXPRESS) ? " Express" : "", pdev->bus->number, PCI_SLOT(pdev->devfn) + 1); - wc->span.manufacturer = "Digium"; - strncpy(wc->span.devicetype, wc->desc->name, - sizeof(wc->span.devicetype) - 1); + s->span.manufacturer = "Digium"; + strncpy(s->span.devicetype, wc->desc->name, sizeof(s->span.devicetype) - 1); + if (alawoverride) { - dev_info(&wc->vb.pdev->dev, "ALAW override parameter detected. Device will be operating in ALAW\n"); - wc->span.deflaw = DAHDI_LAW_ALAW; + s->span.deflaw = DAHDI_LAW_ALAW; + if (first) { + dev_info(&wc->vb.pdev->dev, "ALAW override parameter detected. Device will be operating in ALAW\n"); + first = 0; + } + } else if (digital_span) { + /* BRIs are in A-law */ + s->span.deflaw = DAHDI_LAW_ALAW; + } else { + /* Analog mods are ulaw unless alawoverride is used */ + s->span.deflaw = DAHDI_LAW_MULAW; + } + + if (digital_span) { + s->span.hdlc_hard_xmit = wctdm_hdlc_hard_xmit; + s->span.spanconfig = b400m_spanconfig; + s->span.chanconfig = b400m_chanconfig; + s->span.linecompat = DAHDI_CONFIG_AMI | DAHDI_CONFIG_B8ZS | DAHDI_CONFIG_D4; + s->span.linecompat |= DAHDI_CONFIG_ESF | DAHDI_CONFIG_HDB3 | DAHDI_CONFIG_CCS | DAHDI_CONFIG_CRC4; + s->span.linecompat |= DAHDI_CONFIG_NTTE | DAHDI_CONFIG_TERM; } else { - wc->span.deflaw = DAHDI_LAW_MULAW; - } - for (x=0;xcards;x++) { - sprintf(wc->chans[x]->name, "WCTDM/%d/%d", wc->pos, x); - wc->chans[x]->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR; - wc->chans[x]->sigcap |= DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS | DAHDI_SIG_SF | DAHDI_SIG_CLEAR; - wc->chans[x]->chanpos = x+1; - wc->chans[x]->pvt = wc; - } - wc->span.owner = THIS_MODULE; - wc->span.chans = wc->chans; - wc->span.channels = wc->desc->ports; - wc->span.irq = pdev->irq; - wc->span.hooksig = wctdm_hooksig; - wc->span.open = wctdm_open; - wc->span.close = wctdm_close; - wc->span.flags = DAHDI_FLAG_RBS; - wc->span.ioctl = wctdm_ioctl; - wc->span.watchdog = wctdm_watchdog; - wc->span.dacs= wctdm_dacs; + s->span.hooksig = wctdm_hooksig; + s->span.flags = DAHDI_FLAG_RBS; + /* analog sigcap handled in fixup_analog_span() */ + } + + s->span.chans = kmalloc(sizeof(struct dahdi_chan *) * chancount, GFP_KERNEL); + if (!s->span.chans) + return NULL; + + /* allocate channels for the span */ + for (x = 0; x < chancount; x++) { + c = wctdm_init_chan(wc, s, chanoffset, x); + if (!c) + return NULL; + wc->chans[chanoffset + x] = c; + s->span.chans[x] = &c->chan; + } + + s->span.channels = chancount; + s->span.irq = pdev->irq; + s->span.open = wctdm_open; + s->span.close = wctdm_close; + s->span.ioctl = wctdm_ioctl; + s->span.watchdog = wctdm_watchdog; + s->span.dacs = wctdm_dacs; + + if (digital_span) { + wc->chans[chanoffset + 0]->chan.sigcap = DAHDI_SIG_CLEAR; + wc->chans[chanoffset + 1]->chan.sigcap = DAHDI_SIG_CLEAR; + wc->chans[chanoffset + 2]->chan.sigcap = DAHDI_SIG_HARDHDLC; + } #ifdef VPM_SUPPORT if (vpmsupport) - wc->span.echocan_create = echocan_create; + s->span.echocan_create = echocan_create; #endif - init_waitqueue_head(&wc->span.maintq); - wc->span.pvt = wc; - return 0; + init_waitqueue_head(&s->span.maintq); + wc->spans[spanno] = s; + return s; } -static void wctdm_post_initialize(struct wctdm *wc) +static void wctdm_fixup_analog_span(struct wctdm *wc, int spanno) { - int x; + struct dahdi_span *s; + int x, y; /* Finalize signalling */ - for (x = 0; x cards; x++) { - if (wc->cardflag & (1 << x)) { - if (wc->modtype[x] == MOD_TYPE_FXO) - wc->chans[x]->sigcap = DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS | DAHDI_SIG_SF | DAHDI_SIG_CLEAR; - else if (wc->modtype[x] == MOD_TYPE_FXS) - wc->chans[x]->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR; - else if (wc->modtype[x] == MOD_TYPE_QRV) - wc->chans[x]->sigcap = DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR; - } else if (!(wc->chans[x]->sigcap & DAHDI_SIG_BROKEN)) { - wc->chans[x]->sigcap = 0; + y = 0; + s = &wc->spans[spanno]->span; + + for (x = 0; x < wc->desc->ports; x++) { + if (debug) { + dev_info(&wc->vb.pdev->dev, + "fixup_analog: x=%d, y=%d modtype=%d, " + "s->chans[%d]=%p\n", x, y, wc->modtype[x], + y, s->chans[y]); } + if (wc->modtype[x] == MOD_TYPE_FXO) + s->chans[y++]->sigcap = DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS | DAHDI_SIG_SF | DAHDI_SIG_CLEAR; + else if (wc->modtype[x] == MOD_TYPE_FXS) + s->chans[y++]->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR; + else if (wc->modtype[x] == MOD_TYPE_QRV) + s->chans[y++]->sigcap = DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR; + else + s->chans[y++]->sigcap = 0; } - if (wc->vpm100) { - strncat(wc->span.devicetype, " (VPM100M)", sizeof(wc->span.devicetype) - 1); - } else if (wc->vpmadt032) { - strncat(wc->span.devicetype, " (VPMADT032)", sizeof(wc->span.devicetype) - 1); + for (x = 0; x < MAX_SPANS; x++) { + if (!wc->spans[x]) + continue; + if (wc->vpm100) + strncat(wc->spans[x]->span.devicetype, " (VPM100M)", sizeof(wc->spans[x]->span.devicetype) - 1); + else if (wc->vpmadt032) + strncat(wc->spans[x]->span.devicetype, " (VPMADT032)", sizeof(wc->spans[x]->span.devicetype) - 1); } } @@ -3684,173 +3947,659 @@ static void get_default_portconfig(GpakPortConfig_t *portconfig) portconfig->EightSlotMask3 = 0x0000; } -static int wctdm_locate_modules(struct wctdm *wc) +static int wctdm_initialize_vpmadt032(struct wctdm *wc) +{ + int x; + int res; + unsigned long flags; + struct vpmadt032_options options; + + GpakPortConfig_t portconfig; + + spin_lock_irqsave(&wc->reglock, flags); + for (x = NUM_MODULES; x < NUM_MODULES + NUM_EC; x++) + wc->modtype[x] = MOD_TYPE_NONE; + spin_unlock_irqrestore(&wc->reglock, flags); + + options.debug = debug; + options.vpmnlptype = vpmnlptype; + options.vpmnlpthresh = vpmnlpthresh; + options.vpmnlpmaxsupp = vpmnlpmaxsupp; + options.channels = wc->avchannels; + + wc->vpmadt032 = vpmadt032_alloc(&options, wc->board_name); + if (!wc->vpmadt032) + return -ENOMEM; + + wc->vpmadt032->setchanconfig_from_state = setchanconfig_from_state; + /* wc->vpmadt032->context = wc; */ + /* Pull the configuration information from the span holding + * the analog channels. */ + get_default_portconfig(&portconfig); + res = vpmadt032_init(wc->vpmadt032, &wc->vb); + if (res) { + vpmadt032_free(wc->vpmadt032); + wc->vpmadt032 = NULL; + return res; + } + + /* Now we need to configure the VPMADT032 module for this + * particular board. */ + res = config_vpmadt032(wc->vpmadt032, wc); + if (res) { + vpmadt032_free(wc->vpmadt032); + wc->vpmadt032 = NULL; + return res; + } + + return 0; +} + +static int wctdm_initialize_vpm(struct wctdm *wc) +{ + int res = 0; + + if (!vpmsupport) { + dev_notice(&wc->vb.pdev->dev, "VPM: Support Disabled\n"); + } else if (!wctdm_vpm_init(wc)) { + dev_info(&wc->vb.pdev->dev, "VPM: Present and operational (Rev %c)\n", + 'A' + wc->vpm100 - 1); + wc->ctlreg |= 0x10; + } else { + res = wctdm_initialize_vpmadt032(wc); + if (!res) + wc->ctlreg |= 0x10; + } + return res; +} + +static int wctdm_identify_modules(struct wctdm *wc) { int x; unsigned long flags; wc->ctlreg = 0x00; - + /* Make sure all units go into daisy chain mode */ spin_lock_irqsave(&wc->reglock, flags); - wc->span.irqmisses = 0; - for (x=0;xcards;x++) + +/* + * This looks a little weird. + * + * There are only 8 physical ports on the TDM/AEX800, but the code immediately + * below sets 24 modules up. This has to do with the altcs magic that allows us + * to have single-port and quad-port modules on these products. + * The variable "mods_per_board" is set to the appropriate value just below the + * next code block. + * + * Now why this is important: + * The FXS modules come out of reset in a two-byte, non-chainable SPI mode. + * This is currently incompatible with how we do things, so we need to set + * them to a chained, 3-byte command mode. This is done by setting the module + * type to MOD_TYPE_FXSINIT for a little while so that cmd_dequeue will + * initialize the SLIC into the appropriate mode. + * + * This "go to 3-byte chained mode" command, however, wreaks havoc with HybridBRI. + * + * The solution: + * Since HybridBRI is only designed to work in an 8-port card, and since the single-port + * modules "show up" in SPI slots >= 8 in these cards, we only set SPI slots 8-23 to + * MOD_TYPE_FXSINIT. The HybridBRI will never see the command that causes it to freak + * out and the single-port FXS cards get what they need so that when we probe with altcs + * we see them. + */ + + for (x = 0; x < wc->mods_per_board; x++) wc->modtype[x] = MOD_TYPE_FXSINIT; + wc->vpm100 = -1; - for (x = wc->cards; x < wc->cards+NUM_EC; x++) + for (x = wc->mods_per_board; x < wc->mods_per_board+NUM_EC; x++) wc->modtype[x] = MOD_TYPE_VPM; + spin_unlock_irqrestore(&wc->reglock, flags); - /* Wait just a bit */ - for (x=0;x<10;x++) + +/* Wait just a bit; this makes sure that cmd_dequeue is emitting SPI commands in the appropriate mode(s). */ + for (x = 0; x < 10; x++) schluffen(&wc->regq); - spin_lock_irqsave(&wc->reglock, flags); - for (x=0;xcards;x++) - wc->modtype[x] = MOD_TYPE_FXS; - spin_unlock_irqrestore(&wc->reglock, flags); -#if 0 - /* XXX */ - cmddesc = 0; -#endif - /* Now that all the cards have been reset, we can stop checking them all if there aren't as many */ +/* Now that all the cards have been reset, we can stop checking them all if there aren't as many */ spin_lock_irqsave(&wc->reglock, flags); - wc->cards = wc->desc->ports; + wc->mods_per_board = wc->desc->ports; spin_unlock_irqrestore(&wc->reglock, flags); /* Reset modules */ - for (x=0;xcards;x++) { - int sane=0,ret=0,readi=0; + for (x = 0; x < wc->mods_per_board; x++) { + int sane = 0, ret = 0, readi = 0; if (fatal_signal_pending(current)) break; retry: - /* Init with Auto Calibration */ if (!(ret = wctdm_init_proslic(wc, x, 0, 0, sane))) { - wc->cardflag |= (1 << x); + wc->modmap |= (1 << x); if (debug & DEBUG_CARD) { readi = wctdm_getreg(wc,x,LOOP_I_LIMIT); - dev_info(&wc->vb.pdev->dev, "Proslic module %d loop current is %dmA\n",x, - ((readi*3)+20)); + dev_info(&wc->vb.pdev->dev, "Proslic module %d loop current is %dmA\n", x, ((readi*3)+20)); } dev_info(&wc->vb.pdev->dev, "Port %d: Installed -- AUTO FXS/DPO\n", x + 1); } else { - if(ret!=-2) { - sane=1; + if (ret != -2) { + sane = 1; /* Init with Manual Calibration */ if (!wctdm_init_proslic(wc, x, 0, 1, sane)) { - wc->cardflag |= (1 << x); - if (debug & DEBUG_CARD) { - readi = wctdm_getreg(wc,x,LOOP_I_LIMIT); - dev_info(&wc->vb.pdev->dev, "Proslic module %d loop current is %dmA\n",x, - ((readi*3)+20)); - } + wc->modmap |= (1 << x); + + if (debug & DEBUG_CARD) { + readi = wctdm_getreg(wc, x, LOOP_I_LIMIT); + dev_info(&wc->vb.pdev->dev, "Proslic module %d loop current is %dmA\n", x, ((readi*3)+20)); + } + dev_info(&wc->vb.pdev->dev, "Port %d: Installed -- MANUAL FXS\n",x + 1); } else { dev_notice(&wc->vb.pdev->dev, "Port %d: FAILED FXS (%s)\n", x + 1, fxshonormode ? fxo_modes[_opermode].name : "FCC"); - wc->chans[x]->sigcap = DAHDI_SIG_BROKEN | __DAHDI_SIG_FXO; - } + } + } else if (!(ret = wctdm_init_voicedaa(wc, x, 0, 0, sane))) { - wc->cardflag |= (1 << x); - dev_info(&wc->vb.pdev->dev, "Port %d: Installed -- AUTO FXO (%s mode)\n",x + 1, fxo_modes[_opermode].name); - } else if (!wctdm_init_qrvdri(wc,x)) { - wc->cardflag |= 1 << x; - dev_info(&wc->vb.pdev->dev, "Port %d: Installed -- QRV DRI card\n",x + 1); + wc->modmap |= (1 << x); + dev_info(&wc->vb.pdev->dev, + "Port %d: Installed -- AUTO FXO " + "(%s mode)\n", x + 1, + fxo_modes[_opermode].name); + } else if (!wctdm_init_qrvdri(wc, x)) { + wc->modmap |= 1 << x; + dev_info(&wc->vb.pdev->dev, + "Port %d: Installed -- QRV DRI card\n", x + 1); + } else if (is_hx8(wc) && !wctdm_init_b400m(wc, x)) { + wc->modmap |= (1 << x); + dev_info(&wc->vb.pdev->dev, + "Port %d: Installed -- XHFC " + "quad-span module\n", x + 1); } else { - if ((wc->desc->ports != 24) && - ((x & 0x3) == 1) && !wc->altcs[x]) { + if ((wc->desc->ports != 24) && ((x & 0x3) == 1) && !wc->altcs[x]) { spin_lock_irqsave(&wc->reglock, flags); wc->altcs[x] = 2; + if (wc->desc->ports == 4) { wc->altcs[x+1] = 3; wc->altcs[x+2] = 3; } - wc->modtype[x] = MOD_TYPE_FXSINIT; - spin_unlock_irqrestore(&wc->reglock, flags); - - schluffen(&wc->regq); - schluffen(&wc->regq); - spin_lock_irqsave(&wc->reglock, flags); - wc->modtype[x] = MOD_TYPE_FXS; - spin_unlock_irqrestore(&wc->reglock, flags); - if (debug & DEBUG_CARD) - dev_info(&wc->vb.pdev->dev, "Trying port %d with alternate chip select\n", x + 1); - goto retry; + + wc->modtype[x] = MOD_TYPE_FXSINIT; + spin_unlock_irqrestore(&wc->reglock, flags); + + schluffen(&wc->regq); + schluffen(&wc->regq); + + spin_lock_irqsave(&wc->reglock, flags); + wc->modtype[x] = MOD_TYPE_FXS; + spin_unlock_irqrestore(&wc->reglock, flags); + + if (debug & DEBUG_CARD) + dev_info(&wc->vb.pdev->dev, "Trying port %d with alternate chip select\n", x + 1); + goto retry; + } else { - dev_notice(&wc->vb.pdev->dev, "Port %d: Not installed\n", x + 1); - wc->modtype[x] = MOD_TYPE_NONE; - wc->cardflag |= (1 << x); - } + dev_notice(&wc->vb.pdev->dev, "Port %d: Not installed\n", x + 1); + wc->modtype[x] = MOD_TYPE_NONE; + } } } + } /* for (x...) */ + + return 0; +} + +static struct pci_driver wctdm_driver; + +static void wctdm_back_out_gracefully(struct wctdm *wc) +{ + int i; + unsigned long flags; + struct sframe_packet *frame; + LIST_HEAD(local_list); + + for (i = 0; i < ARRAY_SIZE(wc->spans); ++i) { + if (wc->spans[i] && wc->spans[i]->span.chans) + kfree(wc->spans[i]->span.chans); + + kfree(wc->spans[i]); + wc->spans[i] = NULL; } - if (fatal_signal_pending(current)) - return -EINTR; + for (i = 0; i < ARRAY_SIZE(wc->chans); ++i) { + kfree(wc->chans[i]); + wc->chans[i] = NULL; + } - if (!vpmsupport) { - dev_notice(&wc->vb.pdev->dev, "VPM: Support Disabled\n"); - } else if (!wctdm_vpm_init(wc)) { - dev_info(&wc->vb.pdev->dev, "VPM: Present and operational (Rev %c)\n", 'A' + wc->vpm100 - 1); - wc->ctlreg |= 0x10; - } else { - int res; - struct vpmadt032_options options; - GpakPortConfig_t portconfig; + voicebus_release(&wc->vb); + + spin_lock_irqsave(&wc->frame_list_lock, flags); + list_splice(&wc->frame_list, &local_list); + spin_unlock_irqrestore(&wc->frame_list_lock, flags); + + while (!list_empty(&local_list)) { + frame = list_entry(local_list.next, + struct sframe_packet, node); + list_del(&frame->node); + kfree(frame); + } + + kfree(wc); +} + +static const struct voicebus_operations voicebus_operations = { + .handle_receive = handle_receive, + .handle_transmit = handle_transmit, +}; + +static const struct voicebus_operations hx8_voicebus_operations = { + .handle_receive = handle_hx8_receive, + .handle_transmit = handle_hx8_transmit, +}; + +struct cmd_results { + u8 results[8]; +}; + +static int hx8_send_command(struct wctdm *wc, const u8 *command, + size_t count, int checksum, + int application, int bootloader, + struct cmd_results *results) +{ + int ret = 0; + struct vbb *vbb; + struct sframe_packet *frame; + const int MAX_COMMAND_LENGTH = 264 + 4; + unsigned long flags; + + might_sleep(); + + /* can't boot both into the application and the bootloader at once. */ + WARN_ON((application > 0) && (bootloader > 0)); + if ((application > 0) && (bootloader > 0)) + return -EINVAL; + + WARN_ON(count > MAX_COMMAND_LENGTH); + if (count > MAX_COMMAND_LENGTH) + return -EINVAL; + + vbb = kmem_cache_alloc(voicebus_vbb_cache, GFP_KERNEL); + WARN_ON(!vbb); + if (!vbb) + return -ENOMEM; - spin_lock_irqsave(&wc->reglock, flags); - for (x = NUM_CARDS; x < NUM_CARDS + NUM_EC; x++) - wc->modtype[x] = MOD_TYPE_NONE; - spin_unlock_irqrestore(&wc->reglock, flags); + memset(vbb->data, 0, SFRAME_SIZE); + memcpy(&vbb->data[EFRAME_SIZE + EFRAME_GAP], command, count); - options.debug = debug; - options.vpmnlptype = vpmnlptype; - options.vpmnlpthresh = vpmnlpthresh; - options.vpmnlpmaxsupp = vpmnlpmaxsupp; + vbb->data[EFRAME_SIZE] = 0x80 | ((application) ? 0 : 0x40) | + ((checksum) ? 0x20 : 0) | ((count & 0x100) >> 4); + vbb->data[EFRAME_SIZE + 1] = count & 0xff; - wc->vpmadt032 = vpmadt032_alloc(&options, wc->board_name); - if (!wc->vpmadt032) - return -ENOMEM; + if (bootloader) + vbb->data[EFRAME_SIZE + 3] = 0xAA; - wc->vpmadt032->setchanconfig_from_state = setchanconfig_from_state; - wc->vpmadt032->options.channels = wc->span.channels; - get_default_portconfig(&portconfig); - res = vpmadt032_init(wc->vpmadt032, &wc->vb); - if (res) { - vpmadt032_free(wc->vpmadt032); - wc->vpmadt032 = NULL; - return res; - } + spin_lock_bh(&wc->vb.lock); + voicebus_transmit(&wc->vb, vbb); + spin_unlock_bh(&wc->vb.lock); - /* Now we need to configure the VPMADT032 module for this - * particular board. */ - res = config_vpmadt032(wc->vpmadt032, wc); - if (res) { - vpmadt032_free(wc->vpmadt032); - wc->vpmadt032 = NULL; - return res; + /* Do not wait for the response if the caller doesn't care about the + * results. */ + if (NULL == results) + return 0; + + if (!wait_event_timeout(wc->regq, !list_empty(&wc->frame_list), 2*HZ)) { + dev_err(&wc->vb.pdev->dev, "Timeout waiting " + "for receive frame.\n"); + ret = -EIO; + } + + /* We only want the last packet received. Throw away anything else on + * the list */ + frame = NULL; + spin_lock_irqsave(&wc->frame_list_lock, flags); + while (!list_empty(&wc->frame_list)) { + frame = list_entry(wc->frame_list.next, + struct sframe_packet, node); + list_del(&frame->node); + if (!list_empty(&wc->frame_list)) { + kfree(frame); + frame = NULL; } + } + spin_unlock_irqrestore(&wc->frame_list_lock, flags); - dev_info(&wc->vb.pdev->dev, "VPMADT032: Present and operational (Firmware version %x)\n", wc->vpmadt032->version); - /* TODO what is 0x10 in this context? */ - wc->ctlreg |= 0x10; + if (frame) { + memcpy(results->results, &frame->sframe[EFRAME_SIZE], + sizeof(results->results)); + } else { + ret = -EIO; + } + + return ret; + +} + +static int hx8_get_fpga_version(struct wctdm *wc, u8 *major, u8 *minor) +{ + int ret; + struct cmd_results results; + u8 command[] = {0xD7, 0x00}; + + ret = hx8_send_command(wc, command, ARRAY_SIZE(command), + 0, 0, 0, &results); + if (ret) + return ret; + + *major = results.results[0]; + *minor = results.results[2]; + return 0; +} + +static void hx8_cleanup_frame_list(struct wctdm *wc) +{ + unsigned long flags; + LIST_HEAD(local_list); + struct sframe_packet *frame; + + spin_lock_irqsave(&wc->frame_list_lock, flags); + list_splice_init(&wc->frame_list, &local_list); + spin_unlock_irqrestore(&wc->frame_list_lock, flags); + + while (!list_empty(&local_list)) { + frame = list_entry(local_list.next, struct sframe_packet, node); + list_del(&frame->node); + kfree(frame); } +} + +static int hx8_switch_to_application(struct wctdm *wc) +{ + int ret; + u8 command[] = {0xD7, 0x00}; + + ret = hx8_send_command(wc, command, ARRAY_SIZE(command), + 0, 1, 0, NULL); + if (ret) + return ret; + + msleep(1000); + hx8_cleanup_frame_list(wc); return 0; } -static struct pci_driver wctdm_driver; +/** + * hx8_switch_to_bootloader() - Send packet to switch hx8 into bootloader + * + */ +static int hx8_switch_to_bootloader(struct wctdm *wc) +{ + int ret; + u8 command[] = {0xD7, 0x00}; -static void free_wc(struct wctdm *wc) + ret = hx8_send_command(wc, command, ARRAY_SIZE(command), + 0, 0, 1, NULL); + if (ret) + return ret; + + /* It takes some time for the FPGA to reload and switch it's + * configuration. */ + msleep(300); + hx8_cleanup_frame_list(wc); + + return 0; +} + +struct ha80000_firmware { + u8 header[6]; + u8 major_ver; + u8 minor_ver; + u8 data[54648]; + u32 chksum; +} __attribute__((packed)); + +static void hx8_send_dummy(struct wctdm *wc) { - unsigned int x; + u8 command[] = {0xD7, 0x00}; + + hx8_send_command(wc, command, ARRAY_SIZE(command), + 0, 0, 0, NULL); +} + +static int hx8_read_status_register(struct wctdm *wc, u8 *status) +{ + int ret; + struct cmd_results results; + u8 command[] = {0xD7, 0x00}; + + ret = hx8_send_command(wc, command, ARRAY_SIZE(command), + 0, 0, 0, &results); + if (ret) + return ret; + + *status = results.results[3]; + return 0; +} + +static const unsigned int HYBRID_PAGE_SIZE = 264; + +static int hx8_write_buffer(struct wctdm *wc, const u8 *buffer, size_t size) +{ + int ret = 0; + struct cmd_results results; + int padding_bytes = 0; + u8 *local_data; + u8 command[] = {0x84, 0, 0, 0}; + + if (size > HYBRID_PAGE_SIZE) + return -EINVAL; + + if (size < HYBRID_PAGE_SIZE) + padding_bytes = HYBRID_PAGE_SIZE - size; + + local_data = kmalloc(sizeof(command) + size + padding_bytes, GFP_KERNEL); + if (!local_data) + return -ENOMEM; + + memcpy(local_data, command, sizeof(command)); + memcpy(&local_data[sizeof(command)], buffer, size); + memset(&local_data[sizeof(command) + size], 0xff, padding_bytes); + + ret = hx8_send_command(wc, local_data, + sizeof(command) + size + padding_bytes, + 1, 0, 0, &results); + if (ret) + goto cleanup; + +cleanup: + kfree(local_data); + return ret; +} + +static int hx8_buffer_to_page(struct wctdm *wc, const unsigned int page) +{ + int ret; + struct cmd_results results; + u8 command[] = {0x83, (page & 0x180) >> 7, (page & 0x7f) << 1, 0x00}; + + ret = hx8_send_command(wc, command, sizeof(command), + 1, 0, 0, &results); + if (ret) + return ret; + + return 0; +} + +static int hx8_wait_for_ready(struct wctdm *wc, const int timeout) +{ + int ret; + u8 status; + unsigned long local_timeout = jiffies + timeout; + + do { + ret = hx8_read_status_register(wc, &status); + if (ret) + return ret; + if ((status & 0x80) > 0) + break; + } while (time_after(local_timeout, jiffies)); - for (x = 0; x < ARRAY_SIZE(wc->chans); x++) { - if (wc->chans[x]) { - kfree(wc->chans[x]); + if (time_after(jiffies, local_timeout)) + return -EIO; + + return 0; +} + +/** + * hx8_reload_application - reload the application firmware + * + * NOTE: The caller should ensure that the board is in bootloader mode before + * calling this function. + */ +static int hx8_reload_application(struct wctdm *wc, const struct ha80000_firmware *ha8_fw) +{ + unsigned int cur_page; + const u8 *data; + u8 status; + int ret = 0; + const int HYBRID_PAGE_COUNT = (sizeof(ha8_fw->data)) / HYBRID_PAGE_SIZE; + + BUG_ON(!ha8_fw); + might_sleep(); + + data = &ha8_fw->data[0]; + ret = hx8_read_status_register(wc, &status); + if (ret) + return ret; + + for (cur_page = 0; cur_page < HYBRID_PAGE_COUNT; ++cur_page) { + /* dev_dbg(&wc->vb.pdev->dev, "PAGE: %d\n", cur_page); */ + ret = hx8_write_buffer(wc, data, HYBRID_PAGE_SIZE); + if (ret) + return ret; + /* The application starts out at page 0x100 */ + ret = hx8_buffer_to_page(wc, 0x100 + cur_page); + if (ret) + return ret; + + /* wait no more than a second for the write to the page to + * finish */ + ret = hx8_wait_for_ready(wc, HZ); + if (ret) + return ret; + + data += HYBRID_PAGE_SIZE; + } + + return ret; +} + +static void print_hx8_recovery_message(struct device *dev) +{ + dev_warn(dev, "The firmware may be corrupted. Please completely " + "power off your system, power on, and then reload the driver " + "with the 'forceload' module parameter set to 1 to attempt " + "recovery.\n"); +} + +/** + * hx8_check_firmware - Check the firmware version and load a new one possibly. + * + */ +static int hx8_check_firmware(struct wctdm *wc) +{ + int ret; + u8 major; + u8 minor; + const struct firmware *fw; + const struct ha80000_firmware *ha8_fw; + struct device *dev = &wc->vb.pdev->dev; + int retries = 10; + + BUG_ON(!is_hx8(wc)); + + might_sleep(); + + do { + hx8_send_dummy(wc); + ret = hx8_get_fpga_version(wc, &major, &minor); + if (!ret) + break; + if (fatal_signal_pending(current)) + return -EINTR; + } while (--retries); + + if (ret) { + print_hx8_recovery_message(dev); + return ret; + } + + /* If we're in the bootloader, try to jump into the application. */ + if ((1 == major) && (0x80 == minor) && !forceload) { + dev_dbg(dev, "Switching to application.\n"); + hx8_switch_to_application(wc); + ret = hx8_get_fpga_version(wc, &major, &minor); + if (ret) { + print_hx8_recovery_message(dev); + return ret; } - if (wc->ec[x]) - kfree(wc->ec[x]); } - kfree(wc); + + dev_dbg(dev, "FPGA VERSION: %02x.%02x\n", major, minor); + + ret = request_firmware(&fw, "dahdi-fw-ha80000.bin", dev); + if (ret) { + dev_warn(dev, "Failed to load firmware from userspace, skipping " + "check. (%d)\n", ret); + return 0; + } + ha8_fw = (const struct ha80000_firmware *)fw->data; + + if ((fw->size != sizeof(*ha8_fw)) || + (0 != memcmp("DIGIUM", ha8_fw->header, sizeof(ha8_fw->header))) || + ((crc32(~0, (void *)ha8_fw, sizeof(*ha8_fw) - sizeof(u32)) ^ ~0) != + ha8_fw->chksum)) { + dev_warn(dev, "Firmware file is invalid. Skipping load.\n"); + ret = 0; + goto cleanup; + } + + dev_dbg(dev, "FIRMWARE: %02x.%02x\n", ha8_fw->major_ver, ha8_fw->minor_ver); + + if (ha8_fw->major_ver == major && + ha8_fw->minor_ver == minor) { + dev_dbg(dev, "Firmware versions match, skipping load.\n"); + ret = 0; + goto cleanup; + } + + if (2 == major) { + hx8_switch_to_bootloader(wc); + ret = hx8_get_fpga_version(wc, &major, &minor); + if (ret) + goto cleanup; + } + + /* so now we're in boot loader mode, ready to load the new firmware. */ + ret = hx8_reload_application(wc, ha8_fw); + if (ret) + goto cleanup; + + dev_dbg(dev, "Firmware reloaded. Booting into application.\n"); + + hx8_switch_to_application(wc); + ret = hx8_get_fpga_version(wc, &major, &minor); + if (ret) + goto cleanup; + + dev_dbg(dev, "FPGA VERSION AFTER LOAD: %02x.%02x\n", major, minor); + + if (forceload) { + dev_warn(dev, "Please unset forceload if your card is able to " + "detect the installed modules.\n"); + } + +cleanup: + release_firmware(fw); + return ret; } #ifdef CONFIG_VOICEBUS_SYSFS @@ -3872,11 +4621,6 @@ static DEVICE_ATTR(voicebus_current_latency, 0400, #endif -static const struct voicebus_operations voicebus_operations = { - .handle_receive = handle_receive, - .handle_transmit = handle_transmit, -}; - #ifdef USE_ASYNC_INIT struct async_data { struct pci_dev *pdev; @@ -3891,19 +4635,17 @@ __wctdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) #endif { struct wctdm *wc; - int i; - int y; - int ret; + int i, ret; + + int anamods, digimods, curchan, curspan; - neonmwi_offlimit_cycles = neonmwi_offlimit /MS_PER_HOOKCHECK; + neonmwi_offlimit_cycles = neonmwi_offlimit / MS_PER_HOOKCHECK; - if (!(wc = kmalloc(sizeof(*wc), GFP_KERNEL))) { + wc = kzalloc(sizeof(*wc), GFP_KERNEL); + if (!wc) return -ENOMEM; - } - memset(wc, 0, sizeof(*wc)); - wc->desc = (struct wctdm_desc *)ent->driver_data; - spin_lock(&ifacelock); + down(&ifacelock); /* \todo this is a candidate for removal... */ for (i = 0; i < WC_MAX_IFACES; ++i) { if (!ifaces[i]) { @@ -3911,23 +4653,38 @@ __wctdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) break; } } - spin_unlock(&ifacelock); + up(&ifacelock); + + wc->desc = (struct wctdm_desc *)ent->driver_data; + + /* This is to insure that the analog span is given lowest priority */ + wc->oldsync = -1; + init_MUTEX(&wc->syncsem); + INIT_LIST_HEAD(&wc->frame_list); + spin_lock_init(&wc->frame_list_lock); - snprintf(wc->board_name, sizeof(wc->board_name)-1, "%s%d", - wctdm_driver.name, i); + snprintf(wc->board_name, sizeof(wc->board_name)-1, "%s%d", wctdm_driver.name, i); pci_set_drvdata(pdev, wc); wc->vb.ops = &voicebus_operations; wc->vb.pdev = pdev; wc->vb.debug = &debug; - ret = voicebus_init(&wc->vb, wc->board_name); + + if (is_hx8(wc)) { + wc->vb.ops = &hx8_voicebus_operations; + ret = voicebus_no_idle_init(&wc->vb, wc->board_name); + } else { + wc->vb.ops = &voicebus_operations; + ret = voicebus_init(&wc->vb, wc->board_name); + voicebus_set_minlatency(&wc->vb, latency); + } + if (ret) { kfree(wc); return ret; } #ifdef CONFIG_VOICEBUS_SYSFS - dev_dbg(&wc->vb.pdev->dev, "Creating sysfs attributes.\n"); ret = device_create_file(&wc->vb.pdev->dev, &dev_attr_voicebus_current_latency); if (ret) { @@ -3935,70 +4692,160 @@ __wctdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) "Failed to create device attributes.\n"); } #endif - if (VOICEBUS_DEFAULT_LATENCY != latency) { - voicebus_set_minlatency(&wc->vb, latency); - } + voicebus_lock_latency(&wc->vb); + + init_waitqueue_head(&wc->regq); spin_lock_init(&wc->reglock); - wc->cards = NUM_CARDS; + wc->mods_per_board = NUM_MODULES; wc->pos = i; wc->txident = 1; - for (y=0;yflags[y] = wc->desc->flags; - wc->dacssrc[y] = -1; - } - init_waitqueue_head(&wc->regq); + for (i = 0; i < NUM_MODULES; i++) { + wc->flags[i] = wc->desc->flags; + wc->dacssrc[i] = -1; + } - for (i = 0; i < wc->cards; i++) { - if (!(wc->chans[i] = kmalloc(sizeof(*wc->chans[i]), GFP_KERNEL))) { - free_wc(wc); - return -ENOMEM; - } - memset(wc->chans[i], 0, sizeof(*wc->chans[i])); - if (!(wc->ec[i] = kmalloc(sizeof(*wc->ec[i]), GFP_KERNEL))) { - free_wc(wc); - return -ENOMEM; - } - memset(wc->ec[i], 0, sizeof(*wc->ec[i])); + /* Start the hardware processing. */ + if (voicebus_start(&wc->vb)) { + BUG_ON(1); } + if (is_hx8(wc)) { + ret = hx8_check_firmware(wc); + if (ret) { + voicebus_release(&wc->vb); + kfree(wc); + return -EIO; + } - if (wctdm_initialize(wc)) { - voicebus_release(&wc->vb); - kfree(wc); - return -EIO; + /* Switch back to the normal mode of operation */ + voicebus_stop(&wc->vb); + wc->vb.ops = &voicebus_operations; + voicebus_set_minlatency(&wc->vb, latency); + voicebus_set_normal_mode(&wc->vb); + if (voicebus_start(&wc->vb)) + BUG_ON(1); } - voicebus_lock_latency(&wc->vb); +/* first we have to make sure that we process all module data, we'll fine-tune it later in this routine. */ + wc->avchannels = NUM_MODULES; - if (voicebus_start(&wc->vb)) - BUG_ON(1); - /* Now track down what modules are installed */ - wctdm_locate_modules(wc); - - /* Final initialization */ - wctdm_post_initialize(wc); + wctdm_identify_modules(wc); + + if (fatal_signal_pending(current)) { + wctdm_back_out_gracefully(wc); + return -EINTR; + } +/* + * Walk the module list and create a 3-channel span for every BRI module found. + * Empty and analog modules get a common span which is allocated outside of this loop. + */ + anamods = digimods = 0; + curchan = curspan = 0; + for (i = 0; i < wc->mods_per_board; i++) { + struct b400m *b4; + + if (wc->modtype[i] == MOD_TYPE_NONE) { + ++curspan; + continue; + } else if (wc->modtype[i] == MOD_TYPE_BRI) { + wc->spans[curspan] = wctdm_init_span(wc, curspan, curchan, 3, 1); + if (!wc->spans[curspan]) { + wctdm_back_out_gracefully(wc); + return -EIO; + } + b4 = wc->mods[i].bri; + b400m_set_dahdi_span(b4, i & 0x03, &wc->spans[curspan]->span); + + ++curspan; + curchan += 3; + if (!(i & 0x03)) { + b400m_post_init(b4); + ++digimods; + } + } else { +/* + * FIXME: ABK: + * create a wctdm_chan for every analog module and link them into a span of their own down below. + * then evaluate all of the callbacks and hard-code whether they are receiving a dahdi_chan or wctdm_chan *. + * Finally, move the union from the wctdm structure to the dahdi_chan structure, and we should have something + * resembling a clean dynamic # of channels/dynamic # of spans driver. + */ + ++curspan; + ++anamods; + } + + if (digimods > 2) { + dev_info(&wc->vb.pdev->dev, "More than two digital modules detected. This is unsupported.\n"); + wctdm_back_out_gracefully(wc); + return -EIO; + } + } + +/* create an analog span if there are analog modules, or if there are no digital ones. */ + if (anamods || !digimods) { + if (!digimods) { + curspan = 0; + } + wctdm_init_span(wc, curspan, curchan, wc->desc->ports, 0); + wctdm_fixup_analog_span(wc, curspan); + wc->aspan = wc->spans[curspan]; + wc->aspan->span.pvt = wc; + curchan += wc->desc->ports; + ++curspan; + } + + /* Now fix up the timeslots for the analog modules, since the digital + * modules are always first */ + for (i = 0; i < wc->mods_per_board; i++) { + if (wc->modtype[i] == MOD_TYPE_FXS) { + wctdm_proslic_set_ts(wc, i, (digimods * 12) + i); + } else if (wc->modtype[i] == MOD_TYPE_FXO) { + wctdm_voicedaa_set_ts(wc, i, (digimods * 12) + i); + } else if (wc->modtype[i] == MOD_TYPE_QRV) { + wctdm_qrvdri_set_ts(wc, i, (digimods * 12) + i); + } + } + + wc->digi_mods = digimods; + + /* This shouldn't ever occur, but if we don't try to trap it, the driver + * will be scribbling into memory it doesn't own. */ + BUG_ON(curchan > 24); + + wc->avchannels = curchan; + + wctdm_initialize_vpm(wc); + #ifdef USE_ASYNC_INIT - async_synchronize_cookie(cookie); + async_synchronize_cookie(cookie); #endif - /* We should be ready for DAHDI to come in now. */ - if (dahdi_register(&wc->span, 0)) { - dev_info(&wc->vb.pdev->dev, - "Unable to register span with DAHDI\n"); - voicebus_release(&wc->vb); - kfree(wc); - return -1; + for (i = 0; i < MAX_SPANS; ++i) { + if (!wc->spans[i]) + continue; + + if (dahdi_register(&wc->spans[i]->span, 0)) { + dev_notice(&wc->vb.pdev->dev, "Unable to register span %d with DAHDI\n", i); + while (i) + dahdi_unregister(&wc->spans[i--]->span); + wctdm_back_out_gracefully(wc); + return -1; + } } wc->initialized = 1; - dev_info(&wc->vb.pdev->dev, "Found a Wildcard TDM: %s (%d modules)\n", - wc->desc->name, wc->desc->ports); - + dev_info(&wc->vb.pdev->dev, + "Found a Wildcard TDM: %s (%d digital %s, %d analog %s)\n", + wc->desc->name, digimods, + (digimods == 1) ? "module" : "modules", anamods, + (anamods == 1) ? "module" : "modules"); + ret = 0; + voicebus_unlock_latency(&wc->vb); return 0; } @@ -4043,25 +4890,27 @@ static void wctdm_release(struct wctdm *wc) int i; if (wc->initialized) { - dahdi_unregister(&wc->span); + for (i = 0; i < MAX_SPANS; i++) { + if (wc->spans[i]) + dahdi_unregister(&wc->spans[i]->span); + } } - voicebus_release(&wc->vb); - - spin_lock(&ifacelock); + down(&ifacelock); for (i = 0; i < WC_MAX_IFACES; i++) if (ifaces[i] == wc) break; ifaces[i] = NULL; - spin_unlock(&ifacelock); + up(&ifacelock); - free_wc(wc); + wctdm_back_out_gracefully(wc); } static void __devexit wctdm_remove_one(struct pci_dev *pdev) { struct wctdm *wc = pci_get_drvdata(pdev); struct vpmadt032 *vpm = wc->vpmadt032; + int i; #ifdef CONFIG_VOICEBUS_SYSFS device_remove_file(&wc->vb.pdev->dev, @@ -4075,6 +4924,12 @@ static void __devexit wctdm_remove_one(struct pci_dev *pdev) flush_scheduled_work(); } + /* shut down any BRI modules */ + for (i = 0; i < wc->mods_per_board; i += 4) { + if (wc->modtype[i] == MOD_TYPE_BRI) + wctdm_unload_b400m(wc, i); + } + voicebus_stop(&wc->vb); if (vpm) { @@ -4095,6 +4950,8 @@ static struct pci_device_id wctdm_pci_tbl[] = { { 0xd161, 0x8003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wcaex2400 }, { 0xd161, 0x8005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wctdm410 }, { 0xd161, 0x8006, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wcaex410 }, + { 0xd161, 0x8007, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wcha80000 }, + { 0xd161, 0x8008, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wchb80000 }, { 0 } }; @@ -4146,6 +5003,8 @@ static int __init wctdm_init(void) battthresh = fxo_modes[_opermode].battthresh; } + b400m_module_init(); + res = dahdi_pci_module(&wctdm_driver); if (res) return -ENODEV; @@ -4195,6 +5054,28 @@ module_param(vpmnlpthresh, int, 0600); module_param(vpmnlpmaxsupp, int, 0600); #endif +/* Module parameters backed by code in xhfc.c */ +module_param(bri_debug, int, 0600); +MODULE_PARM_DESC(bri_debug, "bitmap: 1=general 2=dtmf 4=regops 8=fops 16=ec 32=st state 64=hdlc 128=alarm"); + +module_param(bri_spanfilter, int, 0600); +MODULE_PARM_DESC(bri_spanfilter, "debug filter for spans. bitmap: 1=port 1, 2=port 2, 4=port 3, 8=port 4"); + +module_param(bri_alarmdebounce, int, 0600); +MODULE_PARM_DESC(bri_alarmdebounce, "msec to wait before set/clear alarm condition"); + +module_param(bri_teignorered, int, 0600); +MODULE_PARM_DESC(bri_teignorered, "1=ignore (do not inform DAHDI) if a red alarm exists in TE mode"); + +module_param(bri_persistentlayer1, int, 0600); +MODULE_PARM_DESC(bri_persistentlayer1, "Set to 0 for disabling automatic layer 1 reactivation (when other end deactivates it)"); + +module_param(timingcable, int, 0600); +MODULE_PARM_DESC(timingcable, "Set to 1 for enabling timing cable. This means that *all* cards in the system are linked together with a single timing cable"); + +module_param(forceload, int, 0600); +MODULE_PARM_DESC(forceload, "Set to 1 in order to force an FPGA reload after power on (currently only for HA8/HB8 cards)."); + MODULE_DESCRIPTION("Wildcard VoiceBus Analog Card Driver"); MODULE_AUTHOR("Digium Incorporated "); MODULE_ALIAS("wctdm8xxp"); diff --git a/drivers/dahdi/wctdm24xxp/wctdm24xxp.h b/drivers/dahdi/wctdm24xxp/wctdm24xxp.h index fe96966..58b1907 100644 --- a/drivers/dahdi/wctdm24xxp/wctdm24xxp.h +++ b/drivers/dahdi/wctdm24xxp/wctdm24xxp.h @@ -62,10 +62,11 @@ #define MOD_TYPE_NONE 0 #define MOD_TYPE_FXS 1 #define MOD_TYPE_FXO 2 -#define MOD_TYPE_FXSINIT 3 +#define MOD_TYPE_FXSINIT 3 #define MOD_TYPE_VPM 4 #define MOD_TYPE_QRV 5 #define MOD_TYPE_VPM150M 6 +#define MOD_TYPE_BRI 7 #define MINPEGTIME 10 * 8 /* 30 ms peak to peak gets us no more than 100 Hz */ #define PEGTIME 50 * 8 /* 50ms peak to peak gets us rings of 10 Hz or more */ @@ -88,24 +89,23 @@ #define CMD_BYTE(card,bit,altcs) (((((card) & 0x3) * 3 + (bit)) * 7) \ + ((card) >> 2) + (altcs) + ((altcs) ? -21 : 0)) #endif -#define NUM_CARDS 24 -#define NUM_EC 4 -#define NUM_SLOTS 6 -#define MAX_TDM_CHAN 31 +#define NUM_MODULES 24 +#define NUM_EC 4 +#define NUM_SLOTS 6 +#define MAX_TDM_CHAN 31 +#define MAX_SPANS 9 -#define NUM_CAL_REGS 12 +#define NUM_CAL_REGS 12 -#define USER_COMMANDS 8 -#define ISR_COMMANDS 2 -#define QRV_DEBOUNCETIME 20 +#define USER_COMMANDS 8 +#define ISR_COMMANDS 2 +#define QRV_DEBOUNCETIME 20 -#define MAX_COMMANDS (USER_COMMANDS + ISR_COMMANDS) - - -#define VPM150M_HPI_CONTROL 0x00 -#define VPM150M_HPI_ADDRESS 0x02 -#define VPM150M_HPI_DATA 0x03 +#define MAX_COMMANDS (USER_COMMANDS + ISR_COMMANDS) +#define VPM150M_HPI_CONTROL 0x00 +#define VPM150M_HPI_ADDRESS 0x02 +#define VPM150M_HPI_DATA 0x03 #define VPM_SUPPORT @@ -139,22 +139,57 @@ enum battery_state { BATTERY_LOST, }; +/** + * struct wctdm_span - + * @span: dahdi_span to register. + * @timing_priority: What the priority of this span is relative to the other + * spans. + * @spanno: Which span on the card this is. + * + * NOTE: spanno would normally be taken care of by dahdi_span.offset, but + * appears to have meaning in xhfc.c, and that needs to be audited before + * changing. !!!TODO!!! + * + */ +struct wctdm_span { + struct dahdi_span span; + int timing_priority; + int spanno; +}; + +struct wctdm_chan { + struct dahdi_chan chan; + struct dahdi_echocan_state ec; + int timeslot; +}; + struct wctdm { const struct wctdm_desc *desc; char board_name[80]; - struct dahdi_span span; + int usecount; + int pos; /* card number in system */ + + spinlock_t frame_list_lock; + struct list_head frame_list; + unsigned int intcount; unsigned char txident; unsigned char rxident; - int pos; - int flags[NUM_CARDS]; - unsigned char ctlreg; - int cards; - int cardflag; /* Bit-map of present cards */ - int altcs[NUM_CARDS + NUM_EC]; - char qrvhook[NUM_CARDS]; - unsigned short qrvdebtime[NUM_CARDS]; - int radmode[NUM_CARDS]; + + int flags[NUM_MODULES]; /* bitmap of board-specific + module-specific flags */ + unsigned char ctlreg; /* FIXME: something to do with VPM? */ + + int mods_per_board; /* maximum number of modules for this board */ + int digi_mods; /* number of digital modules present */ + int avchannels; /* active "voice" (voice, B and D) channels */ + int modmap; /* Bit-map of present cards (1=present) */ + + int altcs[NUM_MODULES + NUM_EC]; + +/* FIXME: why are all of these QRV-only members part of the main card structure? */ + char qrvhook[NUM_MODULES]; + unsigned short qrvdebtime[NUM_MODULES]; + int radmode[NUM_MODULES]; #define RADMODE_INVERTCOR 1 #define RADMODE_IGNORECOR 2 #define RADMODE_EXTTONE 4 @@ -162,12 +197,13 @@ struct wctdm { #define RADMODE_IGNORECT 16 #define RADMODE_PREEMP 32 #define RADMODE_DEEMP 64 - unsigned short debouncetime[NUM_CARDS]; - signed short rxgain[NUM_CARDS]; - signed short txgain[NUM_CARDS]; - spinlock_t reglock; - wait_queue_head_t regq; - /* FXO Stuff */ + unsigned short debouncetime[NUM_MODULES]; + signed short rxgain[NUM_MODULES]; + signed short txgain[NUM_MODULES]; + + spinlock_t reglock; /* held when accessing anything affecting the module array */ + wait_queue_head_t regq; /* for schluffen() */ + union { struct fxo { int wasringing; @@ -211,13 +247,13 @@ struct wctdm { int reversepolarity; /* polarity reversal */ struct calregs calregs; } fxs; - } mods[NUM_CARDS]; - struct cmdq cmdq[NUM_CARDS + NUM_EC]; - /* Receive hook state and debouncing */ - int modtype[NUM_CARDS + NUM_EC]; - /* Set hook */ - int sethook[NUM_CARDS + NUM_EC]; - int dacssrc[NUM_CARDS]; + struct b400m *bri; + } mods[NUM_MODULES]; + + struct cmdq cmdq[NUM_MODULES + NUM_EC]; + int modtype[NUM_MODULES + NUM_EC]; /* type of module (FXO/FXS/QRV/VPM/etc.) in this position */ + int sethook[NUM_MODULES + NUM_EC]; /* pending hook state command for each module */ + int dacssrc[NUM_MODULES]; int vpm100; @@ -225,17 +261,30 @@ struct wctdm { #ifdef FANCY_ECHOCAN int echocanpos; int blinktimer; -#endif +#endif struct voicebus vb; - struct dahdi_chan *chans[NUM_CARDS]; - struct dahdi_echocan_state *ec[NUM_CARDS]; - int initialized; + struct wctdm_span *aspan; /* pointer to the spans[] holding the analog span */ + struct wctdm_span *spans[MAX_SPANS]; + struct wctdm_chan *chans[NUM_MODULES]; + + /* Only care about digital spans here */ + /* int span_timing_prio[MAX_SPANS - 1]; */ + struct semaphore syncsem; + int oldsync; + + int initialized; /* =1 when the entire card is ready to go */ + unsigned long checkflag; /* Internal state flags and task bits */ }; +/* Atomic flag bits for checkflag field */ +#define WCTDM_CHECK_TIMING 0 int schluffen(wait_queue_head_t *q); +void wait_just_a_bit(int foo); +int wctdm_getreg(struct wctdm *wc, int card, int addr); +int wctdm_setreg(struct wctdm *wc, int card, int addr, int val); -extern spinlock_t ifacelock; +extern struct semaphore ifacelock; extern struct wctdm *ifaces[WC_MAX_IFACES]; #endif diff --git a/drivers/dahdi/wctdm24xxp/xhfc.c b/drivers/dahdi/wctdm24xxp/xhfc.c new file mode 100644 index 0000000..500cdf7 --- /dev/null +++ b/drivers/dahdi/wctdm24xxp/xhfc.c @@ -0,0 +1,2756 @@ +/* + * B400M Quad-BRI module Driver + * Written by Andrew Kohlsmith + * + * Copyright (C) 2010 Digium, Inc. + * All rights reserved. + * + */ + +/* + * 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 +#include +#include +#include + +#define FAST_HDLC_NEED_TABLES +#include +#include + +#include "wctdm24xxp.h" +#include "xhfc.h" + +#define HFC_NR_FIFOS 16 +#define HFC_ZMIN 0x00 /* from datasheet */ +#define HFC_ZMAX 0x7f +#define HFC_FMIN 0x00 +#define HFC_FMAX 0x07 + +/* + * yuck. Any reg which is not mandated read/write or read-only is write-only. + * Also, there are dozens of registers with the same address. Additionally, + * there are array registers (A_) which have an index register These A_ + * registers require an index register to be written to indicate WHICH in the + * array you want. + */ + +#define R_CIRM 0x00 /* WO */ +#define R_CTRL 0x01 /* WO */ +#define R_CLK_CFG 0x02 /* WO */ +#define A_Z1 0x04 /* RO */ +#define A_Z2 0x06 /* RO */ +#define R_RAM_ADDR 0x08 /* WO */ +#define R_RAM_CTRL 0x09 /* WO */ +#define R_FIRST_FIFO 0x0b /* WO */ +#define R_FIFO_THRES 0x0c /* WO */ +#define A_F1 0x0c /* RO */ +#define R_FIFO_MD 0x0d /* WO */ +#define A_F2 0x0d /* RO */ +#define A_INC_RES_FIFO 0x0e /* WO */ +#define A_FIFO_STA 0x0e /* RO */ +#define R_FSM_IDX 0x0f /* WO */ +#define R_FIFO 0x0f /* WO */ + +#define R_SLOT 0x10 /* WO */ +#define R_IRQ_OVIEW 0x10 /* RO */ +#define R_MISC_IRQMSK 0x11 /* WO */ +#define R_MISC_IRQ 0x11 /* RO */ +#define R_SU_IRQMSK 0x12 /* WO */ +#define R_SU_IRQ 0x12 /* RO */ +#define R_IRQ_CTRL 0x13 /* WO */ +#define R_AF0_OVIEW 0x13 /* RO */ +#define R_PCM_MD0 0x14 /* WO */ +#define A_USAGE 0x14 /* RO */ +#define R_MSS0 0x15 /* WO */ +#define R_MSS1 0x15 /* WO */ +#define R_PCM_MD1 0x15 /* WO */ +#define R_PCM_MD2 0x15 /* WO */ +#define R_SH0H 0x15 /* WO */ +#define R_SH1H 0x15 /* WO */ +#define R_SH0L 0x15 /* WO */ +#define R_SH1L 0x15 /* WO */ +#define R_SL_SEL0 0x15 /* WO */ +#define R_SL_SEL1 0x15 /* WO */ +#define R_SL_SEL7 0x15 /* WO */ +#define R_RAM_USE 0x15 /* RO */ +#define R_SU_SEL 0x16 /* WO */ +#define R_CHIP_ID 0x16 /* RO */ +#define R_SU_SYNC 0x17 /* WO */ +#define R_BERT_STA 0x17 /* RO */ +#define R_F0_CNTL 0x18 /* RO */ +#define R_F0_CNTH 0x19 /* RO */ +#define R_TI_WD 0x1a /* WO */ +#define R_BERT_ECL 0x1a /* RO */ +#define R_BERT_WD_MD 0x1b /* WO */ +#define R_BERT_ECH 0x1b /* RO */ +#define R_STATUS 0x1c /* RO */ +#define R_SL_MAX 0x1d /* RO */ +#define R_PWM_CFG 0x1e /* WO */ +#define R_CHIP_RV 0x1f /* RO */ + +#define R_FIFO_BL0_IRQ 0x20 /* RO */ +#define R_FIFO_BL1_IRQ 0x21 /* RO */ +#define R_FIFO_BL2_IRQ 0x22 /* RO */ +#define R_FIFO_BL3_IRQ 0x23 /* RO */ +#define R_FILL_BL0 0x24 /* RO */ +#define R_FILL_BL1 0x25 /* RO */ +#define R_FILL_BL2 0x26 /* RO */ +#define R_FILL_BL3 0x27 /* RO */ +#define R_CI_TX 0x28 /* WO */ +#define R_CI_RX 0x28 /* RO */ +#define R_CGI_CFG0 0x29 /* WO */ +#define R_CGI_STA 0x29 /* RO */ +#define R_CGI_CFG1 0x2a /* WO */ +#define R_MON_RX 0x2a /* RO */ +#define R_MON_TX 0x2b /* WO */ + +#define A_SU_WR_STA 0x30 /* WO */ +#define A_SU_RD_STA 0x30 /* RO */ +#define A_SU_CTRL0 0x31 /* WO */ +#define A_SU_DLYL 0x31 /* RO */ +#define A_SU_CTRL1 0x32 /* WO */ +#define A_SU_DLYH 0x32 /* RO */ +#define A_SU_CTRL2 0x33 /* WO */ +#define A_MS_TX 0x34 /* WO */ +#define A_MS_RX 0x34 /* RO */ +#define A_ST_CTRL3 0x35 /* WO */ +#define A_UP_CTRL3 0x35 /* WO */ +#define A_SU_STA 0x35 /* RO */ +#define A_MS_DF 0x36 /* WO */ +#define A_SU_CLK_DLY 0x37 /* WO */ +#define R_PWM0 0x38 /* WO */ +#define R_PWM1 0x39 /* WO */ +#define A_B1_TX 0x3c /* WO */ +#define A_B1_RX 0x3c /* RO */ +#define A_B2_TX 0x3d /* WO */ +#define A_B2_RX 0x3d /* RO */ +#define A_D_TX 0x3e /* WO */ +#define A_D_RX 0x3e /* RO */ +#define A_BAC_S_TX 0x3f /* WO */ +#define A_E_RX 0x3f /* RO */ + +#define R_GPIO_OUT1 0x40 /* WO */ +#define R_GPIO_IN1 0x40 /* RO */ +#define R_GPIO_OUT3 0x41 /* WO */ +#define R_GPIO_IN3 0x41 /* RO */ +#define R_GPIO_EN1 0x42 /* WO */ +#define R_GPIO_EN3 0x43 /* WO */ +#define R_GPIO_SEL_BL 0x44 /* WO */ +#define R_GPIO_OUT2 0x45 /* WO */ +#define R_GPIO_IN2 0x45 /* RO */ +#define R_PWM_MD 0x46 /* WO */ +#define R_GPIO_EN2 0x47 /* WO */ +#define R_GPIO_OUT0 0x48 /* WO */ +#define R_GPIO_IN0 0x48 /* RO */ +#define R_GPIO_EN0 0x4a /* WO */ +#define R_GPIO_SEL 0x4c /* WO */ + +#define R_PLL_CTRL 0x50 /* WO */ +#define R_PLL_STA 0x50 /* RO */ +#define R_PLL_P 0x51 /* RW */ +#define R_PLL_N 0x52 /* RW */ +#define R_PLL_S 0x53 /* RW */ + +#define A_FIFO_DATA 0x80 /* RW */ +#define A_FIFO_DATA_NOINC 0x84 /* RW */ + +#define R_INT_DATA 0x88 /* RO */ + +#define R_RAM_DATA 0xc0 /* RW */ + +#define A_SL_CFG 0xd0 /* RW */ +#define A_CH_MSK 0xf4 /* RW */ +#define A_CON_HDLC 0xfa /* RW */ +#define A_SUBCH_CFG 0xfb /* RW */ +#define A_CHANNEL 0xfc /* RW */ +#define A_FIFO_SEQ 0xfd /* RW */ +#define A_FIFO_CTRL 0xff /* RW */ + +/* R_CIRM bits */ +#define V_CLK_OFF (1 << 0) /* 1=internal clocks disabled */ +#define V_WAIT_PROC (1 << 1) /* 1=additional /WAIT after write access */ +#define V_WAIT_REG (1 << 2) /* 1=additional /WAIT for internal BUSY phase */ +#define V_SRES (1 << 3) /* soft reset (group 0) */ +#define V_HFC_RES (1 << 4) /* HFC reset (group 1) */ +#define V_PCM_RES (1 << 5) /* PCM reset (group 2) */ +#define V_SU_RES (1 << 6) /* S/T reset (group 3) */ +#define XHFC_FULL_RESET (V_SRES | V_HFC_RES | V_PCM_RES | V_SU_RES) + +/* R_STATUS bits */ +#define V_BUSY (1 << 0) /* 1=HFC busy, limited register access */ +#define V_PROC (1 << 1) /* 1=HFC in processing phase */ +#define V_LOST_STA (1 << 3) /* 1=frames have been lost */ +#define V_PCM_INIT (1 << 4) /* 1=PCM init in progress */ +#define V_WAK_STA (1 << 5) /* state of WAKEUP pin wien V_WAK_EN=1 */ +#define V_MISC_IRQSTA (1 << 6) /* 1=misc interrupt has occurred */ +#define V_FR_IRQSTA (1 << 7) /* 1=fifo interrupt has occured */ +#define XHFC_INTS (V_MISC_IRQSTA | V_FR_IRQSTA) + +/* R_FIFO_BLx_IRQ bits */ +#define V_FIFOx_TX_IRQ (1 << 0) /* FIFO TX interrupt occurred */ +#define V_FIFOx_RX_IRQ (1 << 1) /* FIFO RX interrupt occurred */ +#define FIFOx_TXRX_IRQ (V_FIFOx_TX_IRQ | V_FIFOx_RX_IRQ) + +/* R_FILL_BLx bits */ +#define V_FILL_FIFOx_TX (1 << 0) /* TX FIFO reached V_THRES_TX level */ +#define V_FILL_FIFOx_RX (1 << 1) /* RX FIFO reached V_THRES_RX level */ +#define FILL_FIFOx_TXRX (V_FILL_FIFOx_TX | V_FILL_FIFOx_RX) + +/* R_MISC_IRQ / R_MISC_IRQMSK bits */ +#define V_SLP_IRQ (1 << 0) /* frame sync pulse flips */ +#define V_TI_IRQ (1 << 1) /* timer elapsed */ +#define V_PROC_IRQ (1 << 2) /* processing/non-processing transition */ +#define V_CI_IRQ (1 << 4) /* indication bits changed */ +#define V_WAK_IRQ (1 << 5) /* WAKEUP pin */ +#define V_MON_TX_IRQ (1 << 6) /* monitor byte can be written */ +#define V_MON_RX_IRQ (1 << 7) /* monitor byte received */ + +/* R_SU_IRQ/R_SU_IRQMSK bits */ +#define V_SU0_IRQ (1 << 0) /* interrupt/mask port 1 */ +#define V_SU1_IRQ (1 << 1) /* interrupt/mask port 2 */ +#define V_SU2_IRQ (1 << 2) /* interrupt/mask port 3 */ +#define V_SU3_IRQ (1 << 3) /* interrupt/mask port 4 */ + +/* R_IRQ_CTRL bits */ +#define V_FIFO_IRQ_EN (1 << 0) /* enable any unmasked FIFO IRQs */ +#define V_GLOB_IRQ_EN (1 << 3) /* enable any unmasked IRQs */ +#define V_IRQ_POL (1 << 4) /* 1=IRQ active high */ + +/* R_BERT_WD_MD bits */ +#define V_BERT_ERR (1 << 3) /* 1=generate an error bit in BERT stream */ +#define V_AUTO_WD_RES (1 << 5) /* 1=automatically kick the watchdog */ +#define V_WD_RES (1 << 7) /* 1=kick the watchdog (bit auto clears) */ + +/* R_TI_WD bits */ +#define V_EV_TS_SHIFT (0) +#define V_EV_TS_MASK (0x0f) +#define V_WD_TS_SHIFT (4) +#define V_WD_TS_MASK (0xf0) + +/* A_FIFO_CTRL bits */ +#define V_FIFO_IRQMSK (1 << 0) /* 1=FIFO can generate interrupts */ +#define V_BERT_EN (1 << 1) /* 1=BERT data replaces FIFO data */ +#define V_MIX_IRQ (1 << 2) /* IRQ when 0=end of frame only, 1=also when Z1==Z2 */ +#define V_FR_ABO (1 << 3) /* 1=generate frame abort/frame abort detected */ +#define V_NO_CRC (1 << 4) /* 1=do not send CRC at end of frame */ +#define V_NO_REP (1 << 5) /* 1=frame deleted after d-chan contention */ + +/* R_CLK_CFG bits */ +#define V_CLK_PLL (1 << 0) /* Sysclk select 0=OSC_IN, 1=PLL output */ +#define V_CLKO_HI (1 << 1) /* CLKOUT selection 0=PLL/8, 1=PLL */ +#define V_CLKO_PLL (1 << 2) /* CLKOUT source 0=divider or PLL input, 1=PLL output */ +#define V_PCM_CLK (1 << 5) /* 1=PCM clk = OSC, 0 = PCM clk is 2x OSC */ +#define V_CLKO_OFF (1 << 6) /* CLKOUT enable 0=enabled */ +#define V_CLK_F1 (1 << 7) /* PLL input pin 0=OSC_IN, 1=F1_1 */ + +/* R_PCM_MD0 bits */ +#define V_PCM_MD (1 << 0) /* 1=PCM master */ +#define V_C4_POL (1 << 1) /* 1=F0IO sampled on rising edge of C4IO */ +#define V_F0_NEG (1 << 2) /* 1=negative polarity of F0IO */ +#define V_F0_LEN (1 << 3) /* 1=F0IO active for 2 C4IO clocks */ +#define V_PCM_IDX_SEL0 (0x0 << 4) /* reg15 = R_SL_SEL0 */ +#define V_PCM_IDX_SEL1 (0x1 << 4) /* reg15 = R_SL_SEL1 */ +#define V_PCM_IDX_SEL7 (0x7 << 4) /* reg15 = R_SL_SEL7 */ +#define V_PCM_IDX_MSS0 (0x8 << 4) /* reg15 = R_MSS0 */ +#define V_PCM_IDX_MD1 (0x9 << 4) /* reg15 = R_PCM_MD1 */ +#define V_PCM_IDX_MD2 (0xa << 4) /* reg15 = R_PCM_MD2 */ +#define V_PCM_IDX_MSS1 (0xb << 4) /* reg15 = R_MSS1 */ +#define V_PCM_IDX_SH0L (0xc << 4) /* reg15 = R_SH0L */ +#define V_PCM_IDX_SH0H (0xd << 4) /* reg15 = R_SH0H */ +#define V_PCM_IDX_SH1L (0xe << 4) /* reg15 = R_SH1L */ +#define V_PCM_IDX_SH1H (0xf << 4) /* reg15 = R_SH1H */ +#define V_PCM_IDX_MASK (0xf0) + +/* R_PCM_MD1 bits */ +#define V_PLL_ADJ_00 (0x0 << 2) /* adj 4 times by 0.5 system clk cycles */ +#define V_PLL_ADJ_01 (0x1 << 2) /* adj 3 times by 0.5 system clk cycles */ +#define V_PLL_ADJ_10 (0x2 << 2) /* adj 2 times by 0.5 system clk cycles */ +#define V_PLL_ADJ_11 (0x3 << 2) /* adj 1 time by 0.5 system clk cycles */ +#define V_PCM_DR_2048 (0x0 << 4) /* 2.048Mbps, 32 timeslots */ +#define V_PCM_DR_4096 (0x1 << 4) /* 4.096Mbps, 64 timeslots */ +#define V_PCM_DR_8192 (0x2 << 4) /* 8.192Mbps, 128 timeslots */ +#define V_PCM_DR_075 (0x3 << 4) /* 0.75Mbps, 12 timeslots */ +#define V_PCM_LOOP (1 << 6) /* 1=internal loopback */ +#define V_PCM_SMPL (1 << 7) /* 0=sample at middle of bit cell, 1=sample at 3/4 point */ +#define V_PLL_ADJ_MASK (0x3 << 2) +#define V_PCM_DR_MASK (0x3 << 4) + +/* R_PCM_MD2 bits */ +#define V_SYNC_OUT1 (1 << 1) /* SYNC_O source 0=SYNC_I or FSX_RX, 1=512kHz from PLL or multiframe */ +#define V_SYNC_SRC (1 << 2) /* 0=line interface, 1=SYNC_I */ +#define V_SYNC_OUT2 (1 << 3) /* SYNC_O source 0=rx sync or FSC_RX 1=SYNC_I or received superframe */ +#define V_C2O_EN (1 << 4) /* C2IO output enable (when V_C2I_EN=0) */ +#define V_C2I_EN (1 << 5) /* PCM controller clock source 0=C4IO, 1=C2IO */ +#define V_PLL_ICR (1 << 6) /* 0=reduce PCM frame time, 1=increase */ +#define V_PLL_MAN (1 << 7) /* 0=auto, 1=manual */ + +/* A_SL_CFG bits */ +#define V_CH_SDIR (1 << 0) /* 1=HFC channel receives data from PCM TS */ +#define V_ROUT_TX_DIS (0x0 << 6) /* disabled, output disabled */ +#define V_ROUT_TX_LOOP (0x1 << 6) /* internally looped, output disabled */ +#define V_ROUT_TX_STIO1 (0x2 << 6) /* output data to STIO1 */ +#define V_ROUT_TX_STIO2 (0x3 << 6) /* output data to STIO2 */ +#define V_ROUT_RX_DIS (0x0 << 6) /* disabled, input data ignored */ +#define V_ROUT_RX_LOOP (0x1 << 6) /* internally looped, input data ignored */ +#define V_ROUT_RX_STIO2 (0x2 << 6) /* channel data comes from STIO1 */ +#define V_ROUT_RX_STIO1 (0x3 << 6) /* channel data comes from STIO2 */ +#define V_CH_SNUM_SHIFT (1) +#define V_CH_SNUM_MASK (31 << 1) + +/* A_CON_HDLC bits */ +#define V_IFF (1 << 0) /* Inter-Frame Fill: 0=0x7e, 1=0xff */ +#define V_HDLC_TRP (1 << 1) /* 0=HDLC mode, 1=transparent */ +#define V_TRP_DISABLED (0x0 << 2) /* FIFO disabled, no interrupt */ +#define V_TRP_IRQ_64 (0x1 << 2) /* FIFO enabled, int @ 8 bytes */ +#define V_TRP_IRQ_128 (0x2 << 2) /* FIFO enabled, int @ 16 bytes */ +#define V_TRP_IRQ_256 (0x3 << 2) /* FIFO enabled, int @ 32 bytes */ +#define V_TRP_IRQ_512 (0x4 << 2) /* FIFO enabled, int @ 64 bytes */ +#define V_TRP_IRQ_1024 (0x5 << 2) /* FIFO enabled, int @ 128 bytes */ +#define V_TRP_NO_IRQ (0x7 << 2) /* FIFO enabled, no interrupt */ +#define V_HDLC_IRQ (0x3 << 2) /* HDLC: FIFO enabled, interrupt at end of frame or when FIFO > 16 byte boundary (Mixed IRQ) */ +#define V_DATA_FLOW_000 (0x0 << 5) /* see A_CON_HDLC reg description in datasheet */ +#define V_DATA_FLOW_001 (0x1 << 5) /* see A_CON_HDLC reg description in datasheet */ +#define V_DATA_FLOW_010 (0x2 << 5) /* see A_CON_HDLC reg description in datasheet */ +#define V_DATA_FLOW_011 (0x3 << 5) /* see A_CON_HDLC reg description in datasheet */ +#define V_DATA_FLOW_100 (0x4 << 5) /* see A_CON_HDLC reg description in datasheet */ +#define V_DATA_FLOW_101 (0x5 << 5) /* see A_CON_HDLC reg description in datasheet */ +#define V_DATA_FLOW_110 (0x6 << 5) /* see A_CON_HDLC reg description in datasheet */ +#define V_DATA_FLOW_111 (0x7 << 5) /* see A_CON_HDLC reg description in datasheet */ + +/* R_FIFO bits */ +#define V_FIFO_DIR (1 << 0) /* 1=RX FIFO data */ +#define V_REV (1 << 7) /* 1=MSB first */ +#define V_FIFO_NUM_SHIFT (1) +#define V_FIFO_NUM_MASK (0x3e) + +/* A_CHANNEL bits */ +#define V_CH_FDIR (1 << 0) /* 1=HFC chan for RX data */ +#define V_CH_FNUM_SHIFT (1) +#define V_CH_FNUM_MASK (0x3e) + +/* R_SLOT bits */ +#define V_SL_DIR (1 << 0) /* 1=timeslot will RX PCM data from bus */ +#define V_SL_NUM_SHIFT (1) +#define V_SL_NUM_MASK (0xfe) + +/* A_INC_RES_FIFO bits */ +#define V_INC_F (1 << 0) /* 1=increment FIFO F-counter (bit auto-clears) */ +#define V_RES_FIFO (1 << 1) /* 1=reset FIFO (bit auto-clears) */ +#define V_RES_LOST (1 << 2) /* 1=reset LOST error (bit auto-clears) */ +#define V_RES_FIFO_ERR (1 << 3) /* 1=reset FIFO error (bit auto-clears), check V_ABO_DONE before setting */ + +/* R_FIFO_MD bits */ +#define V_FIFO_MD_00 (0x0 << 0) /* 16 FIFOs, 64 bytes TX/RX, 128 TX or RX if V_UNIDIR_RX */ +#define V_FIFO_MD_01 (0x1 << 0) /* 8 FIFOs, 128 bytes TX/RX, 256 TX or RX if V_UNIDIR_RX */ +#define V_FIFO_MD_10 (0x2 << 0) /* 4 FIFOs, 256 bytes TX/RX, invalid mode with V_UNIDIR_RX */ +#define V_DF_MD_SM (0x0 << 2) /* simple data flow mode */ +#define V_DF_MD_CSM (0x1 << 2) /* channel select mode */ +#define V_DF_MD_FSM (0x3 << 2) /* FIFO sequence mode */ +#define V_UNIDIR_MD (1 << 4) /* 1=unidirectional FIFO mode */ +#define V_UNIDIR_RX (1 << 5) /* 1=unidirection FIFO is RX */ + +/* A_SUBCH_CFG bits */ +#define V_BIT_CNT_8BIT (0) /* process 8 bits */ +#define V_BIT_CNT_1BIT (1) /* process 1 bit */ +#define V_BIT_CNT_2BIT (2) /* process 2 bits */ +#define V_BIT_CNT_3BIT (3) /* process 3 bits */ +#define V_BIT_CNT_4BIT (4) /* process 4 bits */ +#define V_BIT_CNT_5BIT (5) /* process 5 bits */ +#define V_BIT_CNT_6BIT (6) /* process 6 bits */ +#define V_BIT_CNT_7BIT (7) /* process 7 bits */ +#define V_LOOP_FIFO (1 << 6) /* loop FIFO data */ +#define V_INV_DATA (1 << 7) /* invert FIFO data */ +#define V_START_BIT_SHIFT (3) +#define V_START_BIT_MASK (0x38) + +/* R_SU_SYNC bits */ +#define V_SYNC_SEL_PORT0 (0x0 << 0) /* sync to TE port 0 */ +#define V_SYNC_SEL_PORT1 (0x1 << 0) /* sync to TE port 1 */ +#define V_SYNC_SEL_PORT2 (0x2 << 0) /* sync to TE port 2 */ +#define V_SYNC_SEL_PORT3 (0x3 << 0) /* sync to TE port 3 */ +#define V_SYNC_SEL_SYNCI (0x4 << 0) /* sync to SYNC_I */ +#define V_MAN_SYNC (1 << 3) /* 1=manual sync mode */ +#define V_AUTO_SYNCI (1 << 4) /* 1=SYNC_I used if FSC_RX not found */ +#define V_D_MERGE_TX (1 << 5) /* 1=all 4 dchan taken from one byte in TX */ +#define V_E_MERGE_RX (1 << 6) /* 1=all 4 echan combined in RX direction */ +#define V_D_MERGE_RX (1 << 7) /* 1=all 4 dchan combined in RX direction */ +#define V_SYNC_SEL_MASK (0x03) + +/* A_SU_WR_STA bits */ +#define V_SU_SET_STA_MASK (0x0f) +#define V_SU_LD_STA (1 << 4) /* 1=force SU_SET_STA mode, must be manually cleared 6us later */ +#define V_SU_ACT_NOP (0x0 << 5) /* NOP */ +#define V_SU_ACT_DEACTIVATE (0x2 << 5) /* start deactivation. auto-clears */ +#define V_SU_ACT_ACTIVATE (0x3 << 5) /* start activation. auto-clears. */ +#define V_SET_G2_G3 (1 << 7) /* 1=auto G2->G3 in NT mode. auto-clears after transition. */ + +/* A_SU_RD_STA */ +#define V_SU_STA_MASK (0x0f) +#define V_SU_FR_SYNC (1 << 4) /* 1=synchronized */ +#define V_SU_T2_EXP (1 << 5) /* 1=T2 expired (NT only) */ +#define V_SU_INFO0 (1 << 6) /* 1=INFO0 */ +#define V_G2_G3 (1 << 7) /* 1=allows G2->G3 (NT only, auto-clears) */ + +/* A_SU_CLK_DLY bits */ +#define V_SU_DLY_MASK (0x0f) +#define V_SU_SMPL_MASK (0xf0) +#define V_SU_SMPL_SHIFT (4) + +/* A_SU_CTRL0 bits */ +#define V_B1_TX_EN (1 << 0) /* 1=B1-channel transmit */ +#define V_B2_TX_EN (1 << 1) /* 1=B2-channel transmit */ +#define V_SU_MD (1 << 2) /* 0=TE, 1=NT */ +#define V_ST_D_LPRIO (1 << 3) /* D-Chan priority 0=high, 1=low */ +#define V_ST_SQ_EN (1 << 4) /* S/Q bits transmit (1=enabled) */ +#define V_SU_TST_SIG (1 << 5) /* 1=transmit test signal */ +#define V_ST_PU_CTRL (1 << 6) /* 1=enable end of pulse control */ +#define V_SU_STOP (1 << 7) /* 1=power down */ + +/* A_SU_CTRL1 bits */ +#define V_G2_G3_EN (1 << 0) /* 1=G2->G3 allowed without V_SET_G2_G3 */ +#define V_D_RES (1 << 2) /* 1=D-chan reset */ +#define V_ST_E_IGNO (1 << 3) /* TE:1=ignore Echan, NT:should always be 1. */ +#define V_ST_E_LO (1 << 4) /* NT only: 1=force Echan low */ +#define V_BAC_D (1 << 6) /* 1=BAC bit controls Dchan TX */ +#define V_B12_SWAP (1 << 7) /* 1=swap B1/B2 */ + +/* A_SU_CTRL2 bits */ +#define V_B1_RX_EN (1 << 0) /* 1=enable B1 RX */ +#define V_B2_RX_EN (1 << 1) /* 1=enable B2 RX */ +#define V_MS_SSYNC2 (1 << 2) /* 0 normally, see datasheet */ +#define V_BAC_S_SEL (1 << 3) /* see datasheet */ +#define V_SU_SYNC_NT (1 << 4) /* 0=sync pulses generated only in TE, 1=in TE and NT */ +#define V_SU_2KHZ (1 << 5) /* 0=96kHz test tone, 1=2kHz */ +#define V_SU_TRI (1 << 6) /* 1=tristate output buffer */ +#define V_SU_EXCHG (1 << 7) /* 1=invert output drivers */ + +/* R_IRQ_OVIEW bits */ +#define V_FIFO_BL0_IRQ (1 << 0) /* FIFO 0-3 IRQ */ +#define V_FIFO_BL1_IRQ (1 << 1) /* FIFO 4-7 IRQ */ +#define V_FIFO_BL2_IRQ (1 << 2) /* FIFO 8-11 IRQ */ +#define V_FIFO_BL3_IRQ (1 << 3) /* FIFO 12-15 IRQ */ +#define V_MISC_IRQ (1 << 4) /* R_MISC_IRQ changed */ +#define V_STUP_IRQ (1 << 5) /* R_SU_IRQ changed */ +#define V_FIFO_BLx_IRQ (V_FIFO_BL0_IRQ | V_FIFO_BL1_IRQ | V_FIFO_BL2_IRQ | V_FIFO_BL3_IRQ) + +/* R_FIRST_FIFO bits */ +#define V_FIRST_FIFO_NUM_SHIFT (1) + +/* A_FIFO_SEQ bits */ +#define V_NEXT_FIFO_NUM_SHIFT (1) +#define V_SEQ_END (1 << 6) + +#if (DAHDI_CHUNKSIZE != 8) +#error Sorry, the b400m does not support chunksize != 8 +#endif + +/* general debug messages */ +#define DEBUG_GENERAL (1 << 0) +/* emit DTMF detector messages */ +#define DEBUG_DTMF (1 << 1) +/* emit register read/write, but only if the kernel's DEBUG is defined */ +#define DEBUG_REGS (1 << 2) +/* emit file operation messages */ +#define DEBUG_FOPS (1 << 3) +#define DEBUG_ECHOCAN (1 << 4) +/* S/T state machine */ +#define DEBUG_ST_STATE (1 << 5) +/* HDLC controller */ +#define DEBUG_HDLC (1 << 6) +/* alarm changes */ +#define DEBUG_ALARM (1 << 7) +/* Timing related changes */ +#define DEBUG_TIMING (1 << 8) + +#define DBG (bri_debug & DEBUG_GENERAL) +#define DBG_DTMF (bri_debug & DEBUG_DTMF) +#define DBG_REGS (bri_debug & DEBUG_REGS) +#define DBG_FOPS (bri_debug & DEBUG_FOPS) +#define DBG_EC (bri_debug & DEBUG_ECHOCAN) +#define DBG_ST (bri_debug & DEBUG_ST_STATE) +#define DBG_HDLC (bri_debug & DEBUG_HDLC) +#define DBG_ALARM (bri_debug & DEBUG_ALARM) +#define DBG_TIMING (bri_debug & DEBUG_TIMING) + +#define DBG_SPANFILTER ((1 << bspan->port) & bri_spanfilter) + +/* #define HARDHDLC_RX */ + +/* Any static variables not initialized by default should be set + * to 0 automatically */ +int bri_debug; +int bri_spanfilter = 9; +int bri_teignorered = 1; +int bri_alarmdebounce; +int bri_persistentlayer1; +int timingcable; + +static int synccard = -1; +static int syncspan = -1; + +static const int TIMER_3_MS = 30000; + +#define b4_info(b4, format, arg...) \ + dev_info(&(b4)->wc->vb.pdev->dev , format , ## arg) + +/* if defined, swaps ports 2 and 3 on the B400M module */ +#define SWAP_PORTS + +#define XHFC_T1 0 +#define XHFC_T2 1 +#define XHFC_T3 2 +/* T4 - Special timer, used for debug purposes for monitoring of L1 state during activation attempt. */ +#define XHFC_T4 3 + +#define B400M_CHANNELS_PER_SPAN 3 /* 2 B-channels and 1 D-Channel for each BRI span */ +#define B400M_HDLC_BUF_LEN 128 /* arbitrary, just the max # of byts we will send to DAHDI per call */ + +#define get_F(f1, f2, flen) { \ + f1 = hfc_readcounter8(b4, A_F1); \ + f2 = hfc_readcounter8(b4, A_F2); \ + flen = f1 - f2; \ + \ + if (flen < 0) \ + flen += (HFC_FMAX - HFC_FMIN) + 1; \ + } + +#define get_Z(z1, z2, zlen) { \ + z1 = hfc_readcounter8(b4, A_Z1); \ + z2 = hfc_readcounter8(b4, A_Z2); \ + zlen = z1 - z2; \ + \ + if (zlen < 0) \ + zlen += (HFC_ZMAX - HFC_ZMIN) + 1; \ + } + +struct b400m_span { + struct b400m *parent; + unsigned int port; /* which S/T port this span belongs to */ + + int oldstate; /* old state machine state */ + int newalarm; /* alarm to send to DAHDI once alarm timer expires */ + unsigned long alarmtimer; + + unsigned int te_mode:1; /* 1=TE, 0=NT */ + unsigned int term_on:1; /* 1= 390 ohm termination enable, 0 = disabled */ + unsigned long hfc_timers[B400M_CHANNELS_PER_SPAN+1]; /* T1, T2, T3 */ + int hfc_timer_on[B400M_CHANNELS_PER_SPAN+1]; /* 1=timer active */ + int fifos[B400M_CHANNELS_PER_SPAN]; /* B1, B2, D <--> host fifo numbers */ + + /* HDLC controller fields */ + struct dahdi_span *span; /* pointer to the actual dahdi_span */ + struct dahdi_chan *sigchan; /* pointer to the signalling channel for this span */ + int sigactive; /* nonzero means we're in the middle of sending an HDLC frame */ + atomic_t hdlc_pending; /* hdlc_hard_xmit() increments, hdlc_tx_frame() decrements */ + unsigned int frames_out; + unsigned int frames_in; + + struct fasthdlc_state rxhdlc; + int infcs; + int f_sz; +}; + +/* This structure exists one per module */ +struct b400m { + char name[10]; + int position; /* module position in carrier board */ + int b400m_no; /* 0-based B400M number in system */ + + struct wctdm *wc; /* parent structure */ + + spinlock_t reglock; /* lock for all register accesses */ + + unsigned long ticks; + + unsigned long fifo_en_rxint; /* each bit is the RX int enable for that FIFO */ + unsigned long fifo_en_txint; /* each bit is the TX int enable for that FIFO */ + unsigned char fifo_irqstatus; /* top-half ORs in new interrupts, bottom-half ANDs them out */ + + int setsyncspan; /* Span reported from HFC for sync on this card */ + int reportedsyncspan; /* Span reported from HFC for sync on this card */ + + unsigned int running:1; /* interrupts are enabled */ + unsigned int shutdown:1; /* 1=bottom half doesn't process anything, just returns */ + unsigned int inited:1; /* FIXME: temporary */ + unsigned int misc_irq_mask:1; /* 1= interrupt is valid */ + + struct b400m_span spans[4]; /* Individual spans */ + + struct workqueue_struct *xhfc_ws; + struct work_struct xhfc_wq; + + unsigned char irq_oview; /* copy of r_irq_oview */ + unsigned char fifo_fill; /* copy of R_FILL_BL0 */ + + struct semaphore regsem; /* lock for low-level register accesses */ + struct semaphore fifosem; /* lock for fifo accesses */ + + unsigned char lastreg; /* last XHFC register accessed (used to speed up multiple address "hits" */ +}; + +static void hfc_start_st(struct b400m_span *s); +static void hfc_stop_st(struct b400m_span *s); + +void b400m_set_dahdi_span(struct b400m *b4, int spanno, + struct dahdi_span *span) +{ + span->pvt = &b4->spans[spanno]; + b4->spans[spanno].span = span; +} + +static inline void flush_hw(void) +{ +} + +static int xhfc_getreg(struct wctdm *wc, int card, + int addr, u8 *lastreg) +{ + int x; + + if (*lastreg != (unsigned char)addr) { + wctdm_setreg(wc, card, 0x60, addr); + *lastreg = (unsigned char)addr; + } + x = wctdm_getreg(wc, card, 0x80); + return x; +} + +static int xhfc_setreg(struct wctdm *wc, int card, int addr, + int val, u8 *lastreg) +{ + if (*lastreg != (unsigned char)addr) { + wctdm_setreg(wc, card, 0x60, addr); + *lastreg = (unsigned char)addr; + } + return wctdm_setreg(wc, card, 0x00, val); +} + +static int b400m_getreg(struct b400m *b4, int addr) +{ + int x; + + if (down_trylock(&b4->regsem)) { + if (down_interruptible(&b4->regsem)) { + b4_info(b4, "b400m_getreg(0x%02x) interrupted\n", + addr); + return -1; + } + } + + x = xhfc_getreg(b4->wc, b4->position, addr, &b4->lastreg); + up(&b4->regsem); + + return x; +} + +static int b400m_setreg(struct b400m *b4, const int addr, const int val) +{ + int x; + + if (down_trylock(&b4->regsem)) { + if (down_interruptible(&b4->regsem)) { + b4_info(b4, "b400m_setreg(0x%02x -> 0x%02x) " + "interrupted\n", val, addr); + return -1; + } + } + + x = xhfc_setreg(b4->wc, b4->position, addr, val, &b4->lastreg); + up(&b4->regsem); + + return x; +} + + +/* + * A lot of the registers in the XHFC are indexed. + * this function sets the index, and then writes to the indexed register. + */ +static void b400m_setreg_ra(struct b400m *b4, u8 r, u8 rd, u8 a, u8 ad) +{ + if (down_trylock(&b4->regsem)) { + if (down_interruptible(&b4->regsem)) { + b4_info(b4, "b400m_setreg_ra(0x%02x -> 0x%02x) " + "interrupted\n", a, ad); + return; + } + } + + xhfc_setreg(b4->wc, b4->position, r, rd, &b4->lastreg); + xhfc_setreg(b4->wc, b4->position, a, ad, &b4->lastreg); + up(&b4->regsem); +} + +static u8 b400m_getreg_ra(struct b400m *b4, u8 r, u8 rd, u8 a) +{ + unsigned char res; + if (down_trylock(&b4->regsem)) { + if (down_interruptible(&b4->regsem)) { + b4_info(b4, "b400m_getreg_ra(0x%02x) interrupted\n", + a); + return -1; + } + } + + xhfc_setreg(b4->wc, b4->position, r, rd, &b4->lastreg); + res = xhfc_getreg(b4->wc, b4->position, a, &b4->lastreg); + up(&b4->regsem); + return res; +} + + +/* + * XHFC-4S GPIO routines + * + * the xhfc doesn't use its gpio for anything. :-) + */ + +/* + * initialize XHFC GPIO. + * GPIO 0-7 are output, low (unconnected, or used for their primary function). + */ +static void hfc_gpio_init(struct b400m *b4) +{ + /* GPIO0..3,7 are GPIO, 4,5,6 primary function */ + b400m_setreg(b4, R_GPIO_SEL, 0x8f); + /* GPIO0..7 drivers set low */ + b400m_setreg(b4, R_GPIO_OUT0, 0x00); + /* GPIO0..7 drivers enabled */ + b400m_setreg(b4, R_GPIO_EN0, 0xff); + /* all other GPIO set to primary function */ + b400m_setreg(b4, R_GPIO_SEL_BL, 0x00); + +} + + +/* performs a register write and then waits for the HFC "busy" bit to clear + * NOTE: doesn't actually read status, since busy bit is 1us typically, and + * we're much, much slower than that. */ +static void hfc_setreg_waitbusy(struct b400m *b4, const unsigned int reg, + const unsigned int val) +{ + b400m_setreg(b4, reg, val); +} + +/* + * reads an 8-bit register over over and over until the same value is read + * twice, then returns that value. + */ +static unsigned char hfc_readcounter8(struct b400m *b4, const unsigned int reg) +{ + unsigned char r1, r2; + unsigned long maxwait = 1048576; + + do { + r1 = b400m_getreg(b4, reg); + r2 = b400m_getreg(b4, reg); + } while ((r1 != r2) && maxwait--); + + if (!maxwait) { + if (printk_ratelimit()) { + dev_warn(&b4->wc->vb.pdev->dev, + "hfc_readcounter8(reg 0x%02x) timed out " \ + "waiting for data to settle!\n", reg); + } + } + + return r1; +} + +/* performs a soft-reset of the HFC-4S. */ +static void hfc_reset(struct b400m *b4) +{ + unsigned long start; + int TIMEOUT = HZ; /* 1s */ + + /* Set the FIFOs to 8 128 bytes FIFOs, bidirectional, and set up the + * flow controller for channel select mode. */ + /* Note, this reg has to be set *before* the SW reset */ + b400m_setreg(b4, R_FIFO_MD, V_FIFO_MD_01 | V_DF_MD_FSM); + + msleep(1); /* wait a bit for clock to settle */ +/* reset everything, wait 100ms, then allow the XHFC to come out of reset */ + b400m_setreg(b4, R_CIRM, V_SRES); + flush_hw(); + + wait_just_a_bit(HZ/10); + + b400m_setreg(b4, R_CIRM, 0x00); + flush_hw(); + + /* wait for XHFC to come out of reset. */ + start = jiffies; + while (b400m_getreg(b4, R_STATUS) & (V_BUSY | V_PCM_INIT)) { + if (time_after(jiffies, start + TIMEOUT)) { + b4_info(b4, "hfc_reset() Module won't come out of " + "reset... continuing.\n"); + break; + } + }; + + /* Disable the output clock pin, and also the PLL (it's not needed) */ + b400m_setreg(b4, R_CTRL, 0x00); +} + +static void hfc_enable_fifo_irqs(struct b400m *b4) +{ + b400m_setreg(b4, R_IRQ_CTRL, V_FIFO_IRQ_EN | V_GLOB_IRQ_EN); + flush_hw(); +} + +static void hfc_enable_interrupts(struct b400m *b4) +{ + b4->running = 1; + + /* mask all misc interrupts */ + b4->misc_irq_mask = 0x01; + b400m_setreg(b4, R_MISC_IRQMSK, b4->misc_irq_mask); + +/* clear any pending interrupts */ + b400m_getreg(b4, R_STATUS); + b400m_getreg(b4, R_MISC_IRQ); + b400m_getreg(b4, R_FIFO_BL0_IRQ); + b400m_getreg(b4, R_FIFO_BL1_IRQ); + b400m_getreg(b4, R_FIFO_BL2_IRQ); + b400m_getreg(b4, R_FIFO_BL3_IRQ); + + hfc_enable_fifo_irqs(b4); +} + +static inline void hfc_reset_fifo(struct b400m *b4) +{ + hfc_setreg_waitbusy(b4, A_INC_RES_FIFO, + V_RES_FIFO | V_RES_LOST | V_RES_FIFO_ERR); +} + +static void hfc_setup_fifo(struct b400m *b4, int fifo) +{ + if (fifo < 4) { + /* TX */ + hfc_setreg_waitbusy(b4, R_FIFO, (fifo << V_FIFO_NUM_SHIFT)); + b400m_setreg(b4, A_CON_HDLC, + V_HDLC_IRQ | V_DATA_FLOW_000 | V_IFF); + hfc_reset_fifo(b4); + + /* RX */ + hfc_setreg_waitbusy(b4, R_FIFO, + (fifo << V_FIFO_NUM_SHIFT) | V_FIFO_DIR); + b400m_setreg(b4, A_CON_HDLC, + V_HDLC_IRQ | V_DATA_FLOW_000 | V_IFF); + hfc_reset_fifo(b4); + } else { + /* TX */ + hfc_setreg_waitbusy(b4, R_FIFO, (fifo << V_FIFO_NUM_SHIFT)); + b400m_setreg(b4, A_CON_HDLC, + V_HDLC_TRP | V_TRP_NO_IRQ | V_DATA_FLOW_110); + hfc_reset_fifo(b4); + + /* RX */ + hfc_setreg_waitbusy(b4, R_FIFO, + (fifo << V_FIFO_NUM_SHIFT) | V_FIFO_DIR); + b400m_setreg(b4, A_CON_HDLC, + V_HDLC_TRP | V_TRP_NO_IRQ | V_DATA_FLOW_110); + hfc_reset_fifo(b4); + } +} + +static void hfc_setup_pcm(struct b400m *b4, int port) +{ + int physport; + int offset; + int hfc_chan; + int ts; +#ifdef HARDHDLC_RX + const int MAX_OFFSET = 2; +#else + const int MAX_OFFSET = 3; +#endif + +#ifdef SWAP_PORTS + /* swap the middle ports */ + physport = (1 == port) ? 2 : (2 == port) ? 1 : port; +#else + physport = port; +#endif + + for (offset = 0; offset < MAX_OFFSET; offset++) { + hfc_chan = (port * 4) + offset; + ts = (physport * 3) + offset; + ts += (b4->b400m_no * 12); + b400m_setreg(b4, R_SLOT, (ts << V_SL_NUM_SHIFT)); + b400m_setreg(b4, A_SL_CFG, + (hfc_chan << V_CH_SNUM_SHIFT) | + V_ROUT_TX_STIO2); + + if (offset < 2) { + b400m_setreg(b4, R_SLOT, + (ts << V_SL_NUM_SHIFT) | + V_SL_DIR); + b400m_setreg(b4, A_SL_CFG, + (hfc_chan << V_CH_SNUM_SHIFT) | + V_ROUT_RX_STIO1 | V_CH_SDIR); + } + } +} + +#ifdef SWAP_PORTS +#ifdef HARDHDLC_RX +static const int fifos[24] = {0, 0, 2, 2, 1, 1, 3, 3, 4, 4, 4, 4, 6, 6, 6, 6, + 5, 5, 5, 5, 7, 7, 7, 7 }; +#else +static const int fifos[24] = {0, 4, 2, 6, 1, 5, 3, 7, 4, 4, 4, 4, 6, 6, 6, 6, + 5, 5, 5, 5, 7, 7, 7, 7 }; +#endif +static const int hfc_chans[12] = {2, 10, 6, 14, 0, 1, 8, 9, 4, 5, 12, 13 }; +#else +#ifdef HARDHDLC_RX +static const int fifos[24] = {0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, + 6, 6, 6, 6, 7, 7, 7, 7 }; +#else +static const int fifos[24] = {0, 4, 1, 5, 2, 6, 3, 7, 4, 4, 4, 4, 5, 5, 5, 5, + 6, 6, 6, 6, 7, 7, 7, 7 }; +#endif +static const int hfc_chans[12] = { 2, 6, 10, 14, 0, 1, 4, 5, 8, 9, 12, 13 }; +#endif + +static void hfc_setup_fifo_arrays(struct b400m *b4, int fifo) +{ + int val; + if (!fifo) { + val = (fifos[fifo] << V_FIRST_FIFO_NUM_SHIFT) | (fifo & 1); + b400m_setreg(b4, R_FIRST_FIFO, val); + } else { +#ifdef HARDHDLC_RX + val = (fifos[fifo] << V_NEXT_FIFO_NUM_SHIFT) | (fifo & 1); +#else + val = (fifo < 8) ? (fifos[fifo] << V_NEXT_FIFO_NUM_SHIFT) : + (fifos[fifo] << V_NEXT_FIFO_NUM_SHIFT) | + (fifo&1); +#endif + b400m_setreg(b4, A_FIFO_SEQ, val); + } + + b400m_setreg(b4, R_FSM_IDX, fifo); + val = (fifo < 8) ? (hfc_chans[fifo>>1] << V_CH_FNUM_SHIFT) : + (hfc_chans[fifo>>1] << V_CH_FNUM_SHIFT) | + (fifo & 1); + + b400m_setreg(b4, A_CHANNEL, val); + b400m_setreg(b4, A_SUBCH_CFG, 0x02); +} + +static void hfc_setup_fsm(struct b400m *b4) +{ + int chan, fifo, port, offset; +#ifdef SWAP_PORTS + const int chan_to_fifo[12] = { 4, 4, 0, 6, 6, 2, 5, 5, 1, 7, 7, 3 }; +#else + const int chan_to_fifo[12] = { 4, 4, 0, 5, 5, 1, 6, 6, 2, 7, 7, 3 }; +#endif + + for (port = 0; port < 4; port++) { + for (offset = 0; offset < 3; offset++) { + b4->spans[port].fifos[offset] = + chan_to_fifo[(port * 3) + offset]; + } + } + + for (chan = 0; chan < ARRAY_SIZE(fifos); chan++) + hfc_setup_fifo_arrays(b4, chan); + + b400m_setreg(b4, A_FIFO_SEQ, V_SEQ_END); + + for (fifo = 0; fifo < 8; fifo++) + hfc_setup_fifo(b4, fifo); + + for (port = 0; port < 4; port++) + hfc_setup_pcm(b4, port); +} + +/* takes a read/write fifo pair and optionally resets it, optionally enabling + * the rx/tx interrupt */ +static void hfc_reset_fifo_pair(struct b400m *b4, int fifo, + int reset, int force_no_irq) +{ + unsigned char b; + + if (down_interruptible(&b4->fifosem)) { + b4_info(b4, "Unable to retrieve fifo sem\n"); + return; + } + b = (!force_no_irq && b4->fifo_en_txint & (1 << fifo)) ? + V_FIFO_IRQMSK : 0; + hfc_setreg_waitbusy(b4, R_FIFO, (fifo << V_FIFO_NUM_SHIFT)); + + if (fifo < 4) + b |= V_MIX_IRQ; + + b400m_setreg(b4, A_FIFO_CTRL, b); + + if (reset) + hfc_reset_fifo(b4); + + b = (!force_no_irq && b4->fifo_en_rxint & (1 << fifo)) ? + V_FIFO_IRQMSK : 0; + hfc_setreg_waitbusy(b4, R_FIFO, + (fifo << V_FIFO_NUM_SHIFT) | V_FIFO_DIR); + + if (fifo < 4) + b |= V_MIX_IRQ; + + b400m_setreg(b4, A_FIFO_CTRL, b); + + if (reset) + hfc_reset_fifo(b4); + + up(&b4->fifosem); +} + +static void xhfc_set_sync_src(struct b400m *b4, int port) +{ + int b; + + /* -2 means we need to go back and try again later */ + if (port == -2) + return; + + if (port == b4->setsyncspan) + return; + else + b4->setsyncspan = port; + + b4_info(b4, "xhfc_set_sync_src - modpos %d: setting sync to " + "be port %d\n", b4->position, port); + + if (port == -1) /* automatic */ + b = 0; + else { +#ifdef SWAP_PORTS + port = (1 == port) ? 2 : (2 == port) ? 1 : port; +#endif + b = (port & V_SYNC_SEL_MASK) | V_MAN_SYNC; + } + + b400m_setreg(b4, R_SU_SYNC, b); +} + +static void wctdm_change_card_sync_src(struct wctdm *wc, int newsrc, int master) +{ + int newctlreg; + + newctlreg = wc->ctlreg; + + if (master) + newctlreg |= (1 << 5); + else + newctlreg &= ~(1 << 5); + + newctlreg &= 0xfc; + + newctlreg |= newsrc; + + if (DBG_TIMING) { + dev_info(&wc->vb.pdev->dev, + "Final ctlreg before swap: %02x\n", newctlreg); + } + + wc->ctlreg = newctlreg; + wc->oldsync = newsrc; + + msleep(10); +} + +static void wctdm_change_system_sync_src(int oldsync, int oldspan, + int newsync, int newspan) +{ + struct wctdm *wc; + struct wctdm *oldsyncwc = NULL, *newsyncwc = NULL; + int newspot; + int i; + int max_latency = 0; + + if (oldsync > -1) + oldsyncwc = ifaces[oldsync]; + + if (newsync > -1) + newsyncwc = ifaces[newsync]; + + if (newsync == -1) { + BUG_ON(!ifaces[0]); + + newsyncwc = ifaces[0]; + newsync = 0; + } + + newspot = (-1 == newspan) ? 0 : 2 | (newspan >> 2); + + if ((oldsync == newsync) && (oldspan == newspan)) { + dev_info(&newsyncwc->vb.pdev->dev, + "No need for timing change. All is same\n"); + return; + } + + /* First we set all sources to local timing */ + for (i = 0; i < WC_MAX_IFACES; i++) { + wc = ifaces[i]; + if ((wc != oldsyncwc) && wc) { + wctdm_change_card_sync_src(wc, 0, 0); + if (voicebus_current_latency(&wc->vb) > max_latency) + max_latency = voicebus_current_latency(&wc->vb); + } + } + msleep(max_latency << 1); + + /* Set the old sync source to local timing, not driving timing */ + if (oldsyncwc) { + wctdm_change_card_sync_src(oldsyncwc, 0, 0); + msleep(voicebus_current_latency(&oldsyncwc->vb) << 1); + } + + dev_info(&newsyncwc->vb.pdev->dev, + "Setting new card %d now to be timing master\n", newsync); + /* Finally, set the new sync source to broadcast master timing */ + wctdm_change_card_sync_src(newsyncwc, newspot, 1); + msleep(voicebus_current_latency(&newsyncwc->vb) << 1); + + /* Last we double verify and set all the remaining cards to be timing + * slaves */ + for (i = 0; (i < WC_MAX_IFACES) && ifaces[i]; i++) { + wc = ifaces[i]; + if (i == newsync) + continue; + + dev_info(&wc->vb.pdev->dev, + "Setting card %d to be timing slave\n", i); + wctdm_change_card_sync_src(wc, 1, 0); + } + msleep(max_latency << 1); + synccard = newsync; + syncspan = newspan; +} + +static int xhfc_find_sync_with_timingcable(struct b400m *b4) +{ + struct wctdm *wc = b4->wc; + int i, j, osrc, src = -1; + int lowestprio = 10000; + int lowestcard = -1; + + if (down_trylock(&ifacelock)) { + set_bit(WCTDM_CHECK_TIMING, &wc->checkflag); + return -2; + } + + for (j = 0; j < WC_MAX_IFACES && ifaces[j]; j++) { + if (!ifaces[j]->initialized) { + set_bit(WCTDM_CHECK_TIMING, &wc->checkflag); + osrc = -2; + goto out; + } else { + for (i = 0; i < (MAX_SPANS - 1); i++) { + struct wctdm_span *wspan = ifaces[j]->spans[i]; + if (wspan && + wspan->timing_priority && + !wspan->span.alarms && + (wspan->timing_priority < + lowestprio)) { + src = i; + lowestprio = wspan->timing_priority; + lowestcard = j; + } + } + } + } + + if (lowestcard != synccard) { + b4_info(b4, "Found new timing master, card " + "%d. Old is card %d\n", lowestcard, synccard); + } else if (src != syncspan) { + b4_info(b4, "Timing change, but only from %d to %d on " + "card %d\n", syncspan, src, lowestcard); + } + + wctdm_change_system_sync_src(synccard, syncspan, + lowestcard, src); + + osrc = -1; + + if (wc == ifaces[lowestcard]) { + if (src < (b4->position + 4) && (src >= b4->position)) + osrc = src - b4->position; + } + +out: + up(&ifacelock); + + return osrc; +} + +static int xhfc_find_sync_without_timingcable(struct b400m *b4) +{ + struct wctdm *wc = b4->wc; + int i, osrc, src = -1; + int lowestprio = 10000; + int newctlregmux; + + if (down_trylock(&wc->syncsem)) { + set_bit(WCTDM_CHECK_TIMING, &wc->checkflag); + return -2; + } + + /* Find lowest slave timing priority on digital spans */ + for (i = 0; i < (MAX_SPANS - 1); i++) { + struct wctdm_span *const wspan = wc->spans[i]; + if (wspan && wspan->timing_priority && + !wspan->span.alarms && + (wspan->timing_priority < lowestprio)) { + src = i; + lowestprio = wspan->timing_priority; + } + } + + if (src < 0) { + if (DBG_TIMING) + b4_info(b4, "Picked analog span\n"); + osrc = src; + goto check_card_timing; + } else { + if (DBG_TIMING) { + b4_info(b4, "Picked span offset %d to be timing " + "source\n", src); + } + } + + osrc = ((src < (b4->position + 4)) && (src >= b4->position)) ? + src - b4->position : -1; + + if (DBG_TIMING) { + b4_info(b4, "For b4->position %d timing is %d\n", + b4->position, osrc); + } + +check_card_timing: + + if (src != -1) + newctlregmux = 2 | (src >> 2); + else + newctlregmux = 0; + + if ((newctlregmux & 3) != (wc->ctlreg & 3)) { + if (DBG_TIMING) { + b4_info(b4, "!!!Need to change timing " + "on baseboard to spot %d!!!\n", + src >> 2); + } + + wctdm_change_card_sync_src(wc, newctlregmux, 0); + } else { + if (DBG_TIMING) { + dev_info(&b4->wc->vb.pdev->dev, "!!!No need to change timing " \ + "on baseboard to spot %d, already there!!!\n", + src >> 2); + + } + } + + + up(&wc->syncsem); + + return osrc; +} + +/* + * Finds the highest-priority sync span that is not in alarm and returns it. + * Note: the span #s in b4->spans[].sync are 1-based, and this returns a + * 0-based span, or -1 if no spans are found. + */ +static inline int xhfc_find_sync(struct b400m *b4) +{ + if (timingcable) + return xhfc_find_sync_with_timingcable(b4); + else + return xhfc_find_sync_without_timingcable(b4); +} + +/* + * allocates memory and pretty-prints a given S/T state engine state to it. + * calling routine is responsible for freeing the pointer returned! Performs + * no hardware access whatsoever, but does use GFP_KERNEL so do not call from + * IRQ context. if full == 1, prints a "full" dump; otherwise just prints + * current state. + */ +static char *hfc_decode_st_state(struct b400m *b4, struct b400m_span *span, + unsigned char state, int full) +{ + int nt, sta; + char s[128], *str; + const char *ststr[2][16] = { /* TE, NT */ + { "RESET", "?", "SENSING", "DEACT.", + "AWAIT.SIG", "IDENT.INPUT", "SYNCD", "ACTIVATED", + "LOSTFRAMING", "?", "?", "?", + "?", "?", "?", "?" }, + { "RESET", "DEACT.", "PEND.ACT", "ACTIVE", + "PEND.DEACT", "?", "?", "?", + "?", "?", "?", "?", + "?", "?", "?", "?" } + }; + + str = kmalloc(256, GFP_KERNEL); + if (!str) { + dev_warn(&b4->wc->vb.pdev->dev, + "could not allocate mem for ST state decode " \ + "string!\n"); + return NULL; + } + + nt = (span->te_mode == 0); + sta = (state & V_SU_STA_MASK); + + sprintf(str, "P%d: %s state %c%d (%s)", span->port + 1, + (nt ? "NT" : "TE"), (nt ? 'G' : 'F'), sta, + ststr[nt][sta]); + + if (full) { + sprintf(s, " SYNC: %s, RX INFO0: %s", + ((state & V_SU_FR_SYNC) ? "yes" : "no"), + ((state & V_SU_INFO0) ? "yes" : "no")); + strcat(str, s); + + if (nt) { + sprintf(s, ", T2 %s, auto G2->G3: %s", + ((state & V_SU_T2_EXP) ? "expired" : "OK"), + ((state & V_G2_G3) ? "yes" : "no")); + strcat(str, s); + } + } + + return str; +} + +/* + * sets an S/T port state machine to a given state. if 'auto' is nonzero, + * will put the state machine back in auto mode after setting the state. + */ +static void hfc_handle_state(struct b400m_span *s); +static void hfc_force_st_state(struct b400m *b4, struct b400m_span *s, + int state, int resume_auto) +{ + b400m_setreg_ra(b4, R_SU_SEL, s->port, A_SU_WR_STA, + state | V_SU_LD_STA); + + if (resume_auto) + b400m_setreg_ra(b4, R_SU_SEL, s->port, A_SU_WR_STA, state); + + if (DBG_ST && ((1 << s->port) & bri_spanfilter)) { + char *x; + + x = hfc_decode_st_state(b4, s, state, 1); + b4_info(b4, "forced port %d to state %d (auto: %d), " + "new decode: %s\n", s->port + 1, state, + resume_auto, x); + kfree(x); + } + + /* make sure that we activate any timers/etc needed by this state + * change */ + hfc_handle_state(s); +} + +/* figures out what to do when an S/T port's timer expires. */ +static void hfc_timer_expire(struct b400m_span *s, int t_no) +{ + struct b400m *b4 = s->parent; + + if (DBG_ST && ((1 << s->port) & bri_spanfilter)) { + b4_info(b4, "%lu: hfc_timer_expire, Port %d T%d " + "expired (value=%lu ena=%d)\n", b4->ticks, + s->port + 1, t_no + 1, s->hfc_timers[t_no], + s->hfc_timer_on[t_no]); + } + /* + * there are three timers associated with every HFC S/T port. + * + * T1 is used by the NT state machine, and is the maximum time the NT + * side should wait for G3 (active) state. + * + * T2 is not actually used in the driver, it is handled by the HFC-4S + * internally. + * + * T3 is used by the TE state machine; it is the maximum time the TE + * side should wait for the INFO4 (activated) signal. + */ + + /* First, disable the expired timer; hfc_force_st_state() may activate + * it again. */ + s->hfc_timer_on[t_no] = 0; + + switch (t_no) { + case XHFC_T1: /* switch to G4 (pending deact.), resume auto mode */ + hfc_force_st_state(b4, s, 4, 1); + break; + case XHFC_T2: /* switch to G1 (deactivated), resume auto mode */ + hfc_force_st_state(b4, s, 1, 1); + break; + case XHFC_T3: /* switch to F3 (deactivated), resume auto mode */ + hfc_stop_st(s); + if (bri_persistentlayer1) + hfc_start_st(s); + break; + case XHFC_T4: /* switch to F3 (deactivated), resume auto mode */ + hfc_handle_state(s); + s->hfc_timers[XHFC_T4] = b4->ticks + 1000; + s->hfc_timer_on[XHFC_T4] = 1; + break; + default: + if (printk_ratelimit()) { + dev_warn(&b4->wc->vb.pdev->dev, + "hfc_timer_expire found an unknown expired " + "timer (%d)??\n", t_no); + } + } +} + +/* + * Run through the active timers on a card and deal with any expiries. + * Also see if the alarm debounce time has expired and if it has, tell DAHDI. + */ +static void hfc_update_st_timers(struct b400m *b4) +{ + int i, j; + struct b400m_span *s; + + for (i = 0; i < 4; i++) { + s = &b4->spans[i]; + + for (j = XHFC_T1; j <= XHFC_T4; j++) { + + /* we don't really do timer2, it is expired by the + * state change handler */ + if (j == XHFC_T2) + continue; + + if (s->hfc_timer_on[j] && + time_after_eq(b4->ticks, s->hfc_timers[j])) + hfc_timer_expire(s, j); + } + + if (s->span && s->newalarm != s->span->alarms && + time_after_eq(b4->ticks, s->alarmtimer)) { + s->span->alarms = s->newalarm; + if ((!s->newalarm && bri_teignorered) || (!bri_teignorered)) + dahdi_alarm_notify(s->span); + + if (DBG_ALARM) { + dev_info(&b4->wc->vb.pdev->dev, "span %d: alarm " \ + "%d debounced\n", i + 1, + s->newalarm); + } + set_bit(WCTDM_CHECK_TIMING, &b4->wc->checkflag); + } + } + + if (test_and_clear_bit(WCTDM_CHECK_TIMING, &b4->wc->checkflag)) + xhfc_set_sync_src(b4, xhfc_find_sync(b4)); +} + +/* this is the driver-level state machine for an S/T port */ +static void hfc_handle_state(struct b400m_span *s) +{ + struct b400m *b4; + unsigned char state, sta; + int nt, newsync, oldalarm; + unsigned long oldtimer; + + b4 = s->parent; + nt = !s->te_mode; + + state = b400m_getreg_ra(b4, R_SU_SEL, s->port, A_SU_RD_STA); + sta = (state & V_SU_STA_MASK); + + if (DBG_ST && ((1 << s->port) & bri_spanfilter)) { + char *x; + + x = hfc_decode_st_state(b4, s, state, 1); + b4_info(b4, "port %d A_SU_RD_STA old=0x%02x " + "now=0x%02x, decoded: %s\n", s->port + 1, + s->oldstate, state, x); + kfree(x); + } + + oldalarm = s->newalarm; + oldtimer = s->alarmtimer; + + if (nt) { + switch (sta) { + default: /* Invalid NT state */ + case 0x0: /* NT state G0: Reset */ + case 0x1: /* NT state G1: Deactivated */ + case 0x4: /* NT state G4: Pending Deactivation */ + s->newalarm = DAHDI_ALARM_RED; + break; + case 0x2: /* NT state G2: Pending Activation */ + s->newalarm = DAHDI_ALARM_YELLOW; + break; + case 0x3: /* NT state G3: Active */ + s->hfc_timer_on[XHFC_T1] = 0; + s->newalarm = 0; + break; + } + } else { + switch (sta) { + default: /* Invalid TE state */ + case 0x0: /* TE state F0: Reset */ + case 0x2: /* TE state F2: Sensing */ + case 0x3: /* TE state F3: Deactivated */ + case 0x4: /* TE state F4: Awaiting Signal */ + case 0x8: /* TE state F8: Lost Framing */ + s->newalarm = DAHDI_ALARM_RED; + break; + case 0x5: /* TE state F5: Identifying Input */ + case 0x6: /* TE state F6: Synchronized */ + s->newalarm = DAHDI_ALARM_YELLOW; + break; + case 0x7: /* TE state F7: Activated */ + s->hfc_timer_on[XHFC_T3] = 0; + s->hfc_timer_on[XHFC_T4] = 0; + s->newalarm = 0; + break; + } + } + + s->alarmtimer = b4->ticks + bri_alarmdebounce; + s->oldstate = state; + + if (DBG_ALARM) { + b4_info(b4, "span %d: old alarm %d expires %ld, " + "new alarm %d expires %ld\n", s->port + 1, oldalarm, + oldtimer, s->newalarm, s->alarmtimer); + } + + /* we only care about T2 expiry in G4. */ + if (nt && (sta == 4) && (state & V_SU_T2_EXP)) { + if (s->hfc_timer_on[XHFC_T2]) + hfc_timer_expire(s, XHFC_T2); /* handle T2 expiry */ + } + + /* If we're in F3 and receiving INFO0, start T3 and jump to F4 */ + if (!nt && (sta == 3) && (state & V_SU_INFO0)) { + if (bri_persistentlayer1) { + s->hfc_timers[XHFC_T3] = b4->ticks + TIMER_3_MS; + s->hfc_timer_on[XHFC_T3] = 1; + if (DBG_ST) { + b4_info(b4, "port %d: receiving " + "INFO0 in state 3, setting T3 and " + "jumping to F4\n", s->port + 1); + } + hfc_start_st(s); + } + } + + /* read in R_BERT_STA to determine where our current sync source is */ + newsync = b400m_getreg(b4, R_BERT_STA) & 0x07; + if (newsync != b4->reportedsyncspan) { + if (DBG_TIMING) { + if (newsync == 5) { + b4_info(b4, "new card sync source: SYNC_I\n"); + } else { + b4_info(b4, "Card position %d: new " + "sync source: port %d\n", + b4->position, newsync); + } + } + + b4->reportedsyncspan = newsync; + } +} + +static void hfc_stop_all_timers(struct b400m_span *s) +{ + s->hfc_timer_on[XHFC_T4] = 0; + s->hfc_timer_on[XHFC_T3] = 0; + s->hfc_timer_on[XHFC_T2] = 0; + s->hfc_timer_on[XHFC_T1] = 0; +} + +static void hfc_stop_st(struct b400m_span *s) +{ + struct b400m *b4 = s->parent; + + hfc_stop_all_timers(s); + + b400m_setreg_ra(b4, R_SU_SEL, s->port, A_SU_WR_STA, V_SU_ACT_DEACTIVATE); +} + +/* + * resets an S/T interface to a given NT/TE mode + */ +static void hfc_reset_st(struct b400m_span *s) +{ + int b; + struct b400m *b4; + + b4 = s->parent; + + hfc_stop_st(s); + + /* force state G0/F0 (reset), then force state 1/2 + * (deactivated/sensing) */ + b400m_setreg_ra(b4, R_SU_SEL, s->port, A_SU_WR_STA, V_SU_LD_STA); + flush_hw(); /* make sure write hit hardware */ + + s->span->alarms = DAHDI_ALARM_RED; + s->newalarm = DAHDI_ALARM_RED; + dahdi_alarm_notify(s->span); + + /* set up the clock control register. Must be done before we activate + * the interface. */ + if (s->te_mode) + b = 0x0e; + else + b = 0x0c | (6 << V_SU_SMPL_SHIFT); + + b400m_setreg(b4, A_SU_CLK_DLY, b); + + /* set TE/NT mode, enable B and D channels. */ + b400m_setreg(b4, A_SU_CTRL0, V_B1_TX_EN | V_B2_TX_EN | + (s->te_mode ? 0 : V_SU_MD) | V_ST_PU_CTRL); + b400m_setreg(b4, A_SU_CTRL1, V_G2_G3_EN); + b400m_setreg(b4, A_SU_CTRL2, V_B1_RX_EN | V_B2_RX_EN); + b400m_setreg(b4, A_ST_CTRL3, (0x7c << 1)); + + /* enable the state machine. */ + b400m_setreg(b4, A_SU_WR_STA, 0x00); + flush_hw(); +} + +static void hfc_start_st(struct b400m_span *s) +{ + struct b400m *b4 = s->parent; + + b400m_setreg_ra(b4, R_SU_SEL, s->port, A_SU_WR_STA, V_SU_ACT_ACTIVATE); + + /* start T1 if in NT mode, T3 if in TE mode */ + if (s->te_mode) { + /* 500ms wait first time, TIMER_3_MS afterward. */ + s->hfc_timers[XHFC_T3] = b4->ticks + TIMER_3_MS; + s->hfc_timer_on[XHFC_T3] = 1; + s->hfc_timer_on[XHFC_T1] = 0; + + s->hfc_timers[XHFC_T4] = b4->ticks + 1000; + s->hfc_timer_on[XHFC_T4] = 1; + + if (DBG_ST) { + b4_info(b4, "setting port %d t3 timer to %lu\n", + s->port + 1, s->hfc_timers[XHFC_T3]); + } + } else { + static const int TIMER_1_MS = 2000; + s->hfc_timers[XHFC_T1] = b4->ticks + TIMER_1_MS; + s->hfc_timer_on[XHFC_T1] = 1; + s->hfc_timer_on[XHFC_T3] = 0; + if (DBG_ST) { + b4_info(b4, "setting port %d t1 timer to %lu\n", + s->port + 1, s->hfc_timers[XHFC_T1]); + } + } +} + +/* + * read in the HFC GPIO to determine each port's mode (TE or NT). + * Then, reset and start the port. + * the flow controller should be set up before this is called. + */ +static int hdlc_start(struct b400m *b4, int fifo); +static void hfc_init_all_st(struct b400m *b4) +{ + int i, gpio; + struct b400m_span *s; + + gpio = 0; + + for (i = 0; i < 4; i++) { + s = &b4->spans[i]; + s->parent = b4; + +#ifdef SWAP_PORTS + s->port = (1 == i) ? 2 : (2 == i) ? 1 : i; +#else + s->port = i; +#endif + s->te_mode = 1; + + hdlc_start(b4, s->fifos[2]); + } + +} + +/* NOTE: assumes fifo lock is held */ +#define debug_fz(b4, fifo, prefix, buf) \ +do { \ + sprintf(buf, "%s: (fifo %d): f1/f2/flen=%d/%d/%d, " \ + "z1/z2/zlen=%d/%d/%d\n", prefix, fifo, f1, f2, flen, z1, \ + z2, zlen); \ +} while (0) + +/* enable FIFO RX int and reset the FIFO */ +static int hdlc_start(struct b400m *b4, int fifo) +{ + b4->fifo_en_txint |= (1 << fifo); + b4->fifo_en_rxint |= (1 << fifo); + + hfc_reset_fifo_pair(b4, fifo, 1, 0); + return 0; +} + +#ifdef HARDHDLC_RX + +/** + * hdlc_signal_complete() - Signal dahdi that we have a complete frame. + * + * @bpan: The span which received the frame. + * @stat: The frame status from the XHFC controller. + * + */ +static void hdlc_signal_complete(struct b400m_span *bspan, u8 stat) +{ + struct b400m *b4 = bspan->parent; + + /* if STAT != 0, indicates bad frame */ + if (stat != 0x00) { + if (DBG_HDLC && DBG_SPANFILTER) { + b4_info(b4, "(span %d) STAT=0x%02x indicates " \ + "frame problem: %s\n", bspan->port + 1, stat, + (0xff == stat) ? "HDLC Abort" : "Bad FCS"); + } + + dahdi_hdlc_abort(bspan->sigchan, (0xff == stat) ? + DAHDI_EVENT_ABORT : DAHDI_EVENT_BADFCS); + /* STAT == 0, means frame was OK */ + } else { + if (DBG_HDLC && DBG_SPANFILTER) { + b4_info(b4, "(span %d) Frame %d is good!\n", + bspan->port + 1, bspan->frames_in); + } + dahdi_hdlc_finish(bspan->sigchan); + } +} + +/* + * Inner loop for D-channel receive function. Retrieves HDLC data from the + * hardware. If the hardware indicates that the frame is complete, we check + * the HDLC engine's STAT byte and update DAHDI as needed. + * + * Returns the number of HDLC frames left in the FIFO, or -1 if we couldn't + * get the lock. + */ +static int hdlc_rx_frame(struct b400m_span *bspan) +{ + int fifo, i, j, x, zleft; + int z1, z2, zlen, f1, f2, flen, new_flen; + unsigned char buf[B400M_HDLC_BUF_LEN]; + char debugbuf[256]; + struct b400m *b4 = bspan->parent; + + fifo = bspan->fifos[2]; + + if (DBG_HDLC && DBG_SPANFILTER) + b4_info(b4, "hdlc_rx_frame fifo %d: start\n", fifo); + + if (down_trylock(&b4->fifosem) && DBG_HDLC && DBG_SPANFILTER) { + b4_info(b4, "rx_frame: fifo %d 1: couldn't get lock\n", + fifo); + return -1; + } + + hfc_setreg_waitbusy(b4, R_FIFO, + (fifo << V_FIFO_NUM_SHIFT) | V_FIFO_DIR); + get_F(f1, f2, flen); + get_Z(z1, z2, zlen); + debug_fz(b4, fifo, "hdlc_rx_frame", debugbuf); + up(&b4->fifosem); + + if (DBG_HDLC && DBG_SPANFILTER) + pr_info("%s", debugbuf); + + /* if we have at least one complete frame, increment zleft to include + * status byte */ + zleft = zlen; + if (flen) + zleft++; + + do { + if (zleft > B400M_HDLC_BUF_LEN) + j = B400M_HDLC_BUF_LEN; + else + j = zleft; + + if (down_trylock(&b4->fifosem) && DBG_HDLC && DBG_SPANFILTER) { + b4_info(b4, + "rx_frame fifo %d 2: couldn't get lock\n", + fifo); + return -1; + } + hfc_setreg_waitbusy(b4, R_FIFO, + (fifo << V_FIFO_NUM_SHIFT) | V_FIFO_DIR); + for (i = 0; i < j; i++) + buf[i] = b400m_getreg(b4, A_FIFO_DATA); + up(&b4->fifosem); + + /* don't send STAT byte to DAHDI */ + x = j; + if (bspan->sigchan) { + if ((j != B400M_HDLC_BUF_LEN) && flen) + x--; + if (x) + dahdi_hdlc_putbuf(bspan->sigchan, buf, x); + } + + zleft -= j; + + if (DBG_HDLC && DBG_SPANFILTER) { + b4_info(b4, "transmitted %d bytes to dahdi, " \ + "zleft=%d\n", x, zleft); + } + + if (DBG_HDLC && DBG_SPANFILTER) { + /* !!! */ + b4_info(b4, "hdlc_rx_frame(span %d): " \ + "z1/z2/zlen=%d/%d/%d, zleft=%d\n", + bspan->port + 1, z1, z2, zlen, zleft); + for (i = 0; i < j; i++) { + b4_info(b4, "%02x%c", buf[i], + (i < (j - 1)) ? ' ' : '\n'); + } + } + } while (zleft > 0); + + /* Frame received, increment F2 and get an updated count of frames + * left */ + if (down_trylock(&b4->fifosem) && DBG_HDLC && DBG_SPANFILTER) { + b4_info(b4, "rx_frame fifo %d 3: couldn't get lock\n", + fifo); + return 0; + } + + /* go get the F count again, just in case another frame snuck in while + * we weren't looking. */ + if (flen) { + hfc_setreg_waitbusy(b4, A_INC_RES_FIFO, V_INC_F); + ++bspan->frames_in; + get_F(f1, f2, new_flen); + } else + new_flen = flen; + + up(&b4->fifosem); + + /* If this channel is not configured with a signalling span we don't + * need to notify the rest of dahdi about this frame. */ + if (!bspan->sigchan) { + if (DBG_HDLC && DBG_SPANFILTER) { + b4_info(b4, "hdlc_rx_frame fifo %d: " \ + "new_flen %d, early end.\n", fifo, new_flen); + } + return new_flen; + } + + if (flen) { + /* disable < 3 check for now */ + if (0 && zlen < 3) { + if (DBG_HDLC && DBG_SPANFILTER) + b4_info(b4, "odd, zlen less then 3?\n"); + dahdi_hdlc_abort(bspan->sigchan, DAHDI_EVENT_ABORT); + } else { + hdlc_signal_complete(bspan, buf[i - 1]); + } + } + + if (DBG_HDLC && DBG_SPANFILTER) { + b4_info(b4, "hdlc_rx_frame fifo %d: new_flen=%d end.\n", + fifo, new_flen); + } + + return new_flen; +} + +#endif /* HARDHDLC_RX */ + + +/* + * Takes one blob of data from DAHDI and shoots it out to the hardware. The + * blob may or may not be a complete HDLC frame. If it isn't, the D-channel + * FIFO interrupt handler will take care of pulling the rest. Returns nonzero + * if there is still data to send in the current HDLC frame. + */ +static int hdlc_tx_frame(struct b400m_span *bspan) +{ + struct b400m *b4 = bspan->parent; + int res, i, fifo; + int z1, z2, zlen; + int f1 = -1, f2 = -1, flen = -1; + unsigned char buf[B400M_HDLC_BUF_LEN]; + unsigned int size = ARRAY_SIZE(buf); + char debugbuf[256]; + + /* if we're ignoring TE red alarms and we are in alarm, restart the + * S/T state machine */ + if (bspan->te_mode && (bspan->newalarm != 0)) { + hfc_start_st(bspan); + } + + fifo = bspan->fifos[2]; + res = dahdi_hdlc_getbuf(bspan->sigchan, buf, &size); + + if (down_interruptible(&b4->fifosem)) { + static int arg; + b4_info(b4, "b400m: arg (%d), grabbed data from DAHDI " \ + "but couldn't grab the lock!\n", ++arg); + /* TODO: Inform DAHDI that we have grabbed data and can't use + * it */ + dahdi_hdlc_abort(bspan->sigchan, DAHDI_EVENT_OVERRUN); + return 1; /* return 1 so we keep trying */ + } + hfc_setreg_waitbusy(b4, R_FIFO, (fifo << V_FIFO_NUM_SHIFT)); + + get_Z(z1, z2, zlen); + debug_fz(b4, fifo, __func__, debugbuf); + + /* TODO: check zlen, etc. */ + if ((HFC_ZMAX-zlen) < size) { + static int arg; + b4_info(b4, "b400m: arg (%d), zlen (%d) < what we " \ + "grabbed from DAHDI (%d)!\n", ++arg, zlen, size); + size = zlen; + dahdi_hdlc_abort(bspan->sigchan, DAHDI_EVENT_OVERRUN); + } + + if (size > 0) { + bspan->sigactive = 1; + + for (i = 0; i < size; i++) + b400m_setreg(b4, A_FIFO_DATA, buf[i]); + /* + * If we got a full frame from DAHDI, increment F and + * decrement our HDLC pending counter. Otherwise, select the + * FIFO again (to start transmission) and make sure the TX IRQ + * is enabled so we will get called again to finish off the + * data + */ + if (res != 0) { + ++bspan->frames_out; + bspan->sigactive = 0; + hfc_setreg_waitbusy(b4, A_INC_RES_FIFO, V_INC_F); + atomic_dec(&bspan->hdlc_pending); + } else { + hfc_setreg_waitbusy(b4, R_FIFO, + (fifo << V_FIFO_NUM_SHIFT)); + } + } + + up(&b4->fifosem); + + if (0 && DBG_HDLC && DBG_SPANFILTER) { + b4_info(b4, "%s", debugbuf); + + b4_info(b4, "hdlc_tx_frame(span %d): DAHDI gave %d " \ + "bytes for FIFO %d (res = %d)\n", + bspan->port + 1, size, fifo, res); + + for (i = 0; i < size; i++) + b4_info(b4, + "%02x%c\n", buf[i], + (i < (size - 1)) ? ' ' : '\n'); + + if (size && res != 0) { + pr_info("Transmitted frame %d on span %d\n", + bspan->frames_out - 1, bspan->port); + } + } + + return (res == 0); +} + +/* + * b400m lowlevel functions These are functions which impact more than just + * the HFC controller. (those are named hfc_xxx()) + */ + +/* + * Performs a total reset of the card, reinitializes GPIO. The card is + * initialized enough to have LEDs running, and that's about it. Anything to + * do with audio and enabling any kind of processing is done in stage2. + */ +static void xhfc_init_stage1(struct b400m *b4) +{ + int i; + + hfc_reset(b4); + hfc_gpio_init(b4); + + /* make sure interrupts are disabled */ + b400m_setreg(b4, R_IRQ_CTRL, 0x00); + + /* make sure write hits hardware */ + flush_hw(); + + /* disable all FIFO interrupts */ + for (i = 0; i < HFC_NR_FIFOS; i++) { + hfc_setreg_waitbusy(b4, R_FIFO, (i << V_FIFO_NUM_SHIFT)); + /* disable the interrupt */ + b400m_setreg(b4, A_FIFO_CTRL, 0x00); + hfc_setreg_waitbusy(b4, R_FIFO, + (i << V_FIFO_NUM_SHIFT) | V_FIFO_DIR); + /* disable the interrupt */ + b400m_setreg(b4, A_FIFO_CTRL, 0x00); + flush_hw(); + } + + /* set fill threshhold to 16 bytes */ + b400m_setreg(b4, R_FIFO_THRES, 0x11); + + /* clear any pending FIFO interrupts */ + b400m_getreg(b4, R_FIFO_BL2_IRQ); + b400m_getreg(b4, R_FIFO_BL3_IRQ); + + b4->misc_irq_mask = 0x00; + b400m_setreg(b4, R_MISC_IRQMSK, b4->misc_irq_mask); + b400m_setreg(b4, R_IRQ_CTRL, 0); +} + +/* + * Stage 2 hardware init. Sets up the flow controller, PCM and FIFOs. + * Initializes the echo cancellers. S/T interfaces are not initialized here, + * that is done later, in hfc_init_all_st(). Interrupts are enabled and once + * the s/t interfaces are configured, chip should be pretty much operational. + */ +static void xhfc_init_stage2(struct b400m *b4) +{ + /* + * set up PCM bus. XHFC is PCM slave C2IO is the clock, auto sync, + * SYNC_O follows SYNC_I. 128 timeslots, long frame sync positive + * polarity, sample on falling clock edge. STIO2 is transmit-only, + * STIO1 is receive-only. + */ + b400m_setreg(b4, R_PCM_MD0, V_PCM_IDX_MD1); + b400m_setreg(b4, R_PCM_MD1, V_PCM_DR_8192 | (0x3 << 2)); + b400m_setreg(b4, R_PCM_MD0, V_PCM_IDX_MD2); + b400m_setreg(b4, R_PCM_MD2, V_C2I_EN | V_SYNC_OUT1); + b400m_setreg(b4, R_SU_SYNC, V_SYNC_SEL_PORT0); + + /* Now set up the flow controller. */ + hfc_setup_fsm(b4); + + /* + * At this point, everything's set up and ready to go. Don't actually + * enable the global interrupt pin. DAHDI still needs to start up the + * spans, and we don't know exactly when. + */ +} + +static int xhfc_startup(struct dahdi_span *span) +{ + struct b400m_span *bspan = span->pvt; + struct b400m *b4 = bspan->parent; + if (!b4->running) + hfc_enable_interrupts(bspan->parent); + + return 0; +} + +/* resets all the FIFOs for a given span. Disables IRQs for the span FIFOs */ +static void xhfc_reset_span(struct b400m_span *bspan) +{ + int i; + struct b400m *b4 = bspan->parent; + + /* b4_info(b4, "xhfc_reset_span()\n"); */ + for (i = 0; i < 3; i++) + hfc_reset_fifo_pair(b4, bspan->fifos[i], (i == 2) ? 1 : 0, 1); +} + +static void b400m_enable_workqueues(struct wctdm *wc) +{ + struct b400m *b4s[2]; + int i, numb4s = 0; + unsigned long flags; + + spin_lock_irqsave(&wc->reglock, flags); + for (i = 0; i < wc->mods_per_board; i += 4) { + if (wc->modtype[i] == MOD_TYPE_BRI) + b4s[numb4s++] = wc->mods[i].bri; + } + spin_unlock_irqrestore(&wc->reglock, flags); + + for (i = 0; i < numb4s; i++) { + if (b4s[i]) + b4s[i]->shutdown = 0; + } + +} + +static void b400m_disable_workqueues(struct wctdm *wc) +{ + struct b400m *b4s[2]; + int i, numb4s = 0; + unsigned long flags; + + spin_lock_irqsave(&wc->reglock, flags); + for (i = 0; i < wc->mods_per_board; i += 4) { + if (wc->modtype[i] == MOD_TYPE_BRI) + b4s[numb4s++] = wc->mods[i].bri; + } + spin_unlock_irqrestore(&wc->reglock, flags); + + for (i = 0; i < numb4s; i++) { + if (b4s[i]) { + down(&wc->syncsem); + b4s[i]->shutdown = 1; + up(&wc->syncsem); + flush_workqueue(b4s[i]->xhfc_ws); + } + } +} +/* + * Software selectable NT and TE mode settings on the B400M. + * + * mode - bitwise selection of NT vs TE mode + * 1 = NT; 0 = TE; + * bit 0 is port 0 + * bit 1 is port 1 + * ... + * term - termination resistance + * 0 = no termination resistance + * 1 = 390 ohm termination resistance switched on + */ +static int b400m_set_ntte(struct b400m_span *bspan, int te_mode, int term_on) +{ + struct b400m *b4 = bspan->parent; + unsigned char data; + unsigned char addr; + int all_modes = 0, all_terms = 0; + int i; + + bspan->te_mode = te_mode; + bspan->term_on = term_on; + + for (i = 0; i < 4; i++) { + if (!b4->spans[i].te_mode) + all_modes |= (1 << i); + if (b4->spans[i].term_on) + all_terms |= (1 << i); + } + + data = 0x10 | ((all_terms << 4) & 0xc0) | ((all_terms << 2) & 0x0c); + addr = 0x10 | all_modes; + + msleep(voicebus_current_latency(&b4->wc->vb) + 2); + wctdm_setreg(b4->wc, b4->position, addr, data); + + b4->lastreg = 0xff; + msleep(voicebus_current_latency(&b4->wc->vb) + 2); + + hfc_reset_st(bspan); + + if (bri_persistentlayer1) + hfc_start_st(bspan); + + return 0; +} + +/* spanconfig for us means ...? */ +int b400m_spanconfig(struct dahdi_span *span, struct dahdi_lineconfig *lc) +{ + struct b400m_span *bspan; + struct b400m *b4; + struct wctdm *wc; + int te_mode, term; + int pos; + + bspan = span->pvt; + b4 = bspan->parent; + wc = b4->wc; + + b400m_disable_workqueues(b4->wc); + + te_mode = (lc->lineconfig & DAHDI_CONFIG_NTTE) ? 0 : 1; + + term = (lc->lineconfig & DAHDI_CONFIG_TERM) ? 1 : 0; + + b4_info(b4, "xhfc: Configuring port %d span %d in %s " \ + "mode with termination resistance %s\n", bspan->port, + span->spanno, (te_mode) ? "TE" : "NT", + (term) ? "ENABLED" : "DISABLED"); + + b400m_set_ntte(bspan, te_mode, term); + if (lc->sync < 0) { + b4_info(b4, "Span %d has invalid sync priority (%d), " \ + "removing from sync source list\n", span->spanno, + lc->sync); + lc->sync = 0; + } + + if (span->offset >= 4) { + pos = span->offset; + } else { + /* This is tricky. Have to figure out if we're slot 1 or slot + * 2 */ + pos = span->offset + b4->position; + } + + if (!te_mode && lc->sync) { + b4_info(b4, "NT Spans cannot be timing sources. " \ + "Span %d requested to be timing source of " \ + "priority %d. Changing priority to 0\n", pos, + lc->sync); + lc->sync = 0; + } + + wc->spans[pos]->timing_priority = lc->sync; + + bspan->span = span; + xhfc_reset_span(bspan); + + /* call startup() manually here, because DAHDI won't call the startup + * function unless it receives an IOCTL to do so, and dahdi_cfg + * doesn't. */ + xhfc_startup(span); + + span->flags |= DAHDI_FLAG_RUNNING; + + set_bit(WCTDM_CHECK_TIMING, &wc->checkflag); + + b400m_enable_workqueues(b4->wc); + + return 0; +} + +/* chanconfig for us means to configure the HDLC controller, if appropriate + * + * NOTE: apparently the DAHDI ioctl function calls us with a interrupts + * disabled. This means we cannot actually touch the hardware, because all + * register accesses are wrapped up in a mutex that can sleep. + * + * The solution to that is to simply increment the span's "restart" flag, and + * the driver's workqueue will do the dirty work on our behalf. + */ +int b400m_chanconfig(struct dahdi_chan *chan, int sigtype) +{ + int alreadyrunning; + struct b400m_span *bspan = chan->span->pvt; + struct b400m *b4 = bspan->parent; + int res; + + alreadyrunning = bspan->span->flags & DAHDI_FLAG_RUNNING; + + if (DBG_FOPS) { + b4_info(b4, "%s channel %d (%s) sigtype %08x\n", + alreadyrunning ? "Reconfigured" : "Configured", + chan->channo, chan->name, sigtype); + } + + switch (sigtype) { + case DAHDI_SIG_HARDHDLC: + if (DBG_FOPS) { + b4_info(b4, "%sonfiguring hardware HDLC on %s\n", + ((sigtype == DAHDI_SIG_HARDHDLC) ? "C" : + "Unc"), chan->name); + } + bspan->sigchan = chan; + bspan->sigactive = 0; + atomic_set(&bspan->hdlc_pending, 0); + res = 0; + break; + case DAHDI_SIG_HDLCFCS: + case DAHDI_SIG_HDLCNET: + case DAHDI_SIG_HDLCRAW: + /* Only HARDHDLC is supported for the signalling channel on BRI + * spans. */ + res = -EINVAL; + break; + default: + res = 0; + break; + }; + + return res; +} + +int b400m_dchan(struct dahdi_span *span) +{ + struct b400m_span *bspan; + struct b400m *b4; + unsigned char *rxb; + int res; + int i; + + bspan = span->pvt; + b4 = bspan->parent; +#ifdef HARDHDLC_RX + return 0; +#else +#endif + + if (!bspan->sigchan) + return 0; + + rxb = bspan->sigchan->readchunk; + + if (!rxb) { + b4_info(b4, "No RXB!\n"); + return 0; + } + + for (i = 0; i < DAHDI_CHUNKSIZE; i++) { + fasthdlc_rx_load_nocheck(&bspan->rxhdlc, *(rxb++)); + res = fasthdlc_rx_run(&bspan->rxhdlc); + /* If there is nothing there, continue */ + if (res & RETURN_EMPTY_FLAG) + continue; + else if (res & RETURN_COMPLETE_FLAG) { + + if (!bspan->f_sz) + continue; + + /* Only count this if it's a non-empty frame */ + if (bspan->infcs != PPP_GOODFCS) { + dahdi_hdlc_abort(bspan->sigchan, + DAHDI_EVENT_BADFCS); + } else { + dahdi_hdlc_finish(bspan->sigchan); + } + bspan->infcs = PPP_INITFCS; + bspan->f_sz = 0; + continue; + } else if (res & RETURN_DISCARD_FLAG) { + + if (!bspan->f_sz) + continue; + + dahdi_hdlc_abort(bspan->sigchan, DAHDI_EVENT_ABORT); + bspan->infcs = PPP_INITFCS; + bspan->f_sz = 0; + break; + } else { + unsigned char rxc = res; + bspan->infcs = PPP_FCS(bspan->infcs, rxc); + bspan->f_sz++; + dahdi_hdlc_putbuf(bspan->sigchan, &rxc, 1); + } + } + + return 0; +} + +/* internal functions, not specific to the hardware or DAHDI */ + +/* + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) +static void xhfc_work(void *data) +{ + struct b400m *b4 = data; +#else +static void xhfc_work(struct work_struct *work) +{ + struct b400m *b4 = container_of(work, struct b400m, xhfc_wq); +#endif + int i, j, k, fifo; + unsigned char b, b2; + + if (b4->shutdown || !b4->wc->initialized) + return; + + b4->irq_oview = b400m_getreg(b4, R_IRQ_OVIEW); + b4->fifo_fill = b400m_getreg(b4, R_FILL_BL0); + + if (b4->irq_oview & V_FIFO_BL0_IRQ) { + b4->fifo_irqstatus |= b400m_getreg(b4, R_FIFO_BL0_IRQ); + b4->irq_oview &= ~V_FIFO_BL0_IRQ; + } + + /* only look at BL0, we put all D channel FIFOs in the first block. */ + b = b2 = b4->fifo_irqstatus; + + for (j = 0; j < 4; j++) { +#ifdef SWAP_PORTS + fifo = (1 == j) ? 2 : (2 == j) ? 1 : j; +#else + fifo = j; +#endif + +#ifdef HARDHDLC_RX + if (b & V_FIFOx_RX_IRQ) { + if (fifo < 4) { /* d-channel FIFO */ + + /* + * I have to loop here until hdlc_rx_frame + * says there are no more frames waiting. for + * whatever reason, the HFC will not generate + * another interrupt if there are still HDLC + * frames waiting to be received. i.e. I get + * an int when F1 changes, not when F1 != F2. + * + */ + do { + k = hdlc_rx_frame(&b4->spans[fifo]); + } while (k); + } + } + +#endif + b >>= 2; + } + + /* zero the bits we just processed */ + b4->fifo_irqstatus &= ~b2; + b4->fifo_fill &= ~b2; + + +#if 1 + /* All four D channel FIFOs are in BL0. */ + b = b2 = b4->fifo_fill; + + for (j = 0; j < 4; j++) { +#ifdef SWAP_PORTS + fifo = (1 == j) ? 2 : (2 == j) ? 1 : j; +#else + fifo = j; +#endif + if (b4->spans[fifo].sigactive && (b & V_FIFOx_TX_IRQ)) + hdlc_tx_frame(&b4->spans[fifo]); + +#ifdef HARDHDLC_RX + if (b & V_FIFOx_RX_IRQ) + hdlc_rx_frame(&b4->spans[fifo]); +#endif + + b >>= 2; + } +#endif + + /* Check for outgoing HDLC frame requests The HFC does not generate TX + * interrupts when there is room to send, so I use an atomic counter + * that is incremented every time DAHDI wants to send a frame, and + * decremented every time I send a frame. It'd be better if I could + * just use the interrupt handler, but the HFC seems to trigger a FIFO + * TX IRQ only when it has finished sending a frame, not when one can + * be sent. + */ + for (i = 0; i < ARRAY_SIZE(b4->spans); i++) { + struct b400m_span *bspan = &b4->spans[i]; + + if (atomic_read(&bspan->hdlc_pending)) { + do { + k = hdlc_tx_frame(bspan); + } while (k); + } + } + + b = b400m_getreg(b4, R_SU_IRQ); + + if (b) { + for (i = 0; i < ARRAY_SIZE(b4->spans); i++) { + int physport; + +#ifdef SWAP_PORTS + if (i == 1) + physport = 2; + else if (i == 2) + physport = 1; + else + physport = i; +#else + physport = i; +#endif + if (b & (1 << i)) + hfc_handle_state(&b4->spans[physport]); + } + } + + hfc_update_st_timers(b4); +} + +int wctdm_bri_checkisr(struct wctdm *wc, int modpos, int offset) +{ + struct b400m *b4; + int ret = 0; + + /* don't do anything for non-base card slots */ + if (modpos & 0x03) + return 0; + + /* DEFINITELY don't do anything if our structures aren't ready! */ + if (!wc || !wc->initialized || !(wc->mods[modpos].bri) || + !((struct b400m *)wc->mods[modpos].bri)->inited) { + return 0; + } + + b4 = (struct b400m *)wc->mods[modpos].bri; + if (offset == 0) { + if (!b4->shutdown) { + /* if (!(wc->intcount % 50)) */ + queue_work(b4->xhfc_ws, &b4->xhfc_wq); + } + b4->ticks++; + } + + return ret; +} + +/* DAHDI calls this when it has data it wants to send to the HDLC controller */ +void wctdm_hdlc_hard_xmit(struct dahdi_chan *chan) +{ + struct wctdm *wc; + struct b400m *b4; + struct b400m_span *bspan; + struct dahdi_span *dspan; + int span; + + dspan = chan->span; + bspan = dspan->pvt; + b4 = bspan->parent; + wc = b4->wc; + span = bspan->port; + + if ((DBG_FOPS || DBG_HDLC) && DBG_SPANFILTER) { + b4_info(b4, "hdlc_hard_xmit on chan %s (%i/%i), " \ + "span=%i (sigchan=%p, chan=%p)\n", chan->name, + chan->channo, chan->chanpos, span + 1, + bspan->sigchan, chan); + } + + /* Increment the hdlc_pending counter and trigger the bottom-half so + * it will be picked up and sent. */ + if (bspan->sigchan == chan) + atomic_inc(&bspan->hdlc_pending); +} + +static int b400m_probe(struct wctdm *wc, int modpos) +{ + unsigned char id, x; + struct b400m *b4; + unsigned long flags; + int chiprev; + + wctdm_setreg(wc, modpos, 0x10, 0x10); + id = xhfc_getreg(wc, modpos, R_CHIP_ID, &x); + + /* chip ID high 7 bits must be 0x62, see datasheet */ + if ((id & 0xfe) != 0x62) + return -2; + + b4 = kzalloc(sizeof(struct b400m), GFP_KERNEL); + if (!b4) { + dev_err(&wc->vb.pdev->dev, + "Couldn't allocate memory for b400m structure!\n"); + return -ENOMEM; + } + + /* card found, enabled and main struct allocated. Fill it out. */ + b4->wc = wc; + b4->position = modpos; + + /* which B400M in the system is this one? count all of them found so + * far */ + for (x = 0; x < modpos; x += 4) { + if (wc->modtype[x] == MOD_TYPE_BRI) + ++b4->b400m_no; + } + + spin_lock_init(&b4->reglock); + init_MUTEX(&b4->regsem); + init_MUTEX(&b4->fifosem); + + for (x = 0; x < 4; x++) { + fasthdlc_init(&b4->spans[x].rxhdlc, FASTHDLC_MODE_16); + b4->spans[x].infcs = PPP_INITFCS; + } + + b4->lastreg = 0xff; /* a register we won't hit right off the bat */ + + chiprev = b400m_getreg(b4, R_CHIP_RV); + + b4->setsyncspan = -1; /* sync span is unknown */ + b4->reportedsyncspan = -1; /* sync span is unknown */ + + if (DBG) { + b4_info(b4, "Identified controller rev %d in module %d.\n", + chiprev, b4->position); + } + + xhfc_init_stage1(b4); + + xhfc_init_stage2(b4); + hfc_init_all_st(b4); + + hfc_enable_interrupts(b4); + + spin_lock_irqsave(&wc->reglock, flags); + wc->mods[modpos].bri = (void *)b4; + spin_unlock_irqrestore(&wc->reglock, flags); + + return 0; +} + +void b400m_post_init(struct b400m *b4) +{ + snprintf(b4->name, sizeof(b4->name) - 1, "b400m-%d", + b4->b400m_no); + b4->xhfc_ws = create_singlethread_workqueue(b4->name); +# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + INIT_WORK(&b4->xhfc_wq, xhfc_work, b4); +# else + INIT_WORK(&b4->xhfc_wq, xhfc_work); +# endif + b4->inited = 1; +} + +/* functions called from the wctdm code */ +int wctdm_init_b400m(struct wctdm *wc, int card) +{ + int i, ret = 0; + unsigned long flags; + + if (wc->modtype[card & 0xfc] == MOD_TYPE_QRV) + return -2; + + if (!(card & 0x03)) { /* only init if at lowest port in module */ + spin_lock_irqsave(&wc->reglock, flags); + wc->modtype[card + 0] = MOD_TYPE_BRI; + wc->modtype[card + 1] = MOD_TYPE_BRI; + wc->modtype[card + 2] = MOD_TYPE_BRI; + wc->modtype[card + 3] = MOD_TYPE_BRI; + spin_unlock_irqrestore(&wc->reglock, flags); + + for (i = 0; i < 10; i++) + schluffen(&wc->regq); + + if (b400m_probe(wc, card) != 0) { + spin_lock_irqsave(&wc->reglock, flags); + wc->modtype[card + 0] = MOD_TYPE_NONE; + wc->modtype[card + 1] = MOD_TYPE_NONE; + wc->modtype[card + 2] = MOD_TYPE_NONE; + wc->modtype[card + 3] = MOD_TYPE_NONE; + spin_unlock_irqrestore(&wc->reglock, flags); + ret = -2; + } + } else { /* for the "sub-cards" */ + if (wc->modtype[card & 0xfc] == MOD_TYPE_BRI) { + spin_lock_irqsave(&wc->reglock, flags); + wc->modtype[card] = MOD_TYPE_BRI; + wc->mods[card].bri = wc->mods[card & 0xfc].bri; + spin_unlock_irqrestore(&wc->reglock, flags); + } else { + ret = -2; + } + } + + return ret; +} + +void wctdm_unload_b400m(struct wctdm *wc, int card) +{ + struct b400m *b4 = wc->mods[card].bri; + int i; + + /* TODO: shutdown once won't work if just a single card is hotswapped + * out. But since most of the time this is called because the entire + * driver is in the process of unloading, I'll leave it here. */ + static int shutdown_once; + + + /* only really unload with the 'base' card number. base+1/2/3 aren't + * real. */ + if (card & 0x03) + return; + + if (timingcable && !shutdown_once) { + b4_info(b4, "Disabling all workqueues for B400Ms\n"); + /* Gotta shut down timing change potential during this */ + for (i = 0; i < WC_MAX_IFACES; i++) { + if (ifaces[i]) + b400m_disable_workqueues(ifaces[i]); + } + b4_info(b4, "Forcing sync to card 0\n"); + /* Put the timing configuration in a known state: card 0 is + * master */ + wctdm_change_system_sync_src(synccard, syncspan, -1, -1); + /* Change all other cards in the system to self time before + * card 0 is removed */ + b4_info(b4, "Setting all cards to return to self sync\n"); + for (i = 1; i < WC_MAX_IFACES; i++) { + if (ifaces[i]) + wctdm_change_card_sync_src(ifaces[i], 0, 0); + } + + b4_info(b4, + "Finished preparing timing linked cards for " + "shutdown\n"); + + shutdown_once = 1; + } + + if (b4) { + b4->inited = 0; + + wait_just_a_bit(HZ/10); + + /* TODO: wait for tdm24xx driver to unregister the spans */ + /* do { ... } while(not_unregistered); */ + + /* Change sync source back to base board so we don't freeze up + * when we reset the XHFC */ + b400m_disable_workqueues(wc); + + for (i = 0; i < (MAX_SPANS - 1); i++) { + if (wc->spans[i]) + wc->spans[i]->timing_priority = 0; + } + + for (i = 0; i < 4; i++) + b4->spans[i].span->flags &= ~DAHDI_FLAG_RUNNING; + + wctdm_change_card_sync_src(b4->wc, 0, 0); + + xhfc_init_stage1(b4); + + destroy_workqueue(b4->xhfc_ws); + + /* Set these to MOD_TYPE_NONE to ensure that our checkisr + * routines are not entered */ + wc->modtype[card] = MOD_TYPE_NONE; + wc->modtype[card + 1] = MOD_TYPE_NONE; + wc->modtype[card + 2] = MOD_TYPE_NONE; + wc->modtype[card + 3] = MOD_TYPE_NONE; + + wc->mods[card].bri = NULL; + wc->mods[card + 1].bri = NULL; + wc->mods[card + 2].bri = NULL; + wc->mods[card + 3].bri = NULL; + + msleep(voicebus_current_latency(&wc->vb) << 1); + b4_info(b4, "Driver unloaded.\n"); + kfree(b4); + } + +} + +void b400m_module_init(void) +{ + fasthdlc_precalc(); +} + +void b400m_module_cleanup(void) +{ +} diff --git a/drivers/dahdi/wctdm24xxp/xhfc.h b/drivers/dahdi/wctdm24xxp/xhfc.h new file mode 100644 index 0000000..2699175 --- /dev/null +++ b/drivers/dahdi/wctdm24xxp/xhfc.h @@ -0,0 +1,49 @@ +/* + * B400M Quad-BRI module Driver + * Written by Andrew Kohlsmith + * + * Copyright (C) 2010 Digium, Inc. + * All rights reserved. + * + */ + +/* + * 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. + */ + + +#ifndef _B4XXM_H_ +#define _B4XXM_H_ + +extern int bri_debug; +extern int bri_spanfilter; +extern int bri_teignorered; +extern int bri_alarmdebounce; +extern int bri_persistentlayer1; +extern int timingcable; + +struct b400m; + +/* probes the given card to see if it's a B400M */ +int wctdm_init_b400m(struct wctdm *wc, int card); +int wctdm_bri_checkisr(struct wctdm *wc, int card, int offset); +void wctdm_unload_b400m(struct wctdm *wc, int card); +void wctdm_hdlc_hard_xmit(struct dahdi_chan *chan); +int b400m_spanconfig(struct dahdi_span *span, struct dahdi_lineconfig *lc); +int b400m_dchan(struct dahdi_span *span); +int b400m_chanconfig(struct dahdi_chan *chan, int sigtype); +void b400m_post_init(struct b400m *b4); +void b400m_set_dahdi_span(struct b400m *b4, int spanno, struct dahdi_span *span); +void b400m_module_init(void); +void b400m_module_cleanup(void); + +#endif /* _B4XX_H_ */ diff --git a/drivers/dahdi/wcte12xp/base.c b/drivers/dahdi/wcte12xp/base.c index 38efdfa..f88b042 100644 --- a/drivers/dahdi/wcte12xp/base.c +++ b/drivers/dahdi/wcte12xp/base.c @@ -193,7 +193,7 @@ static void cmd_dequeue(struct t1 *wc, unsigned char *writechunk, int eframe, in } -static inline void cmd_decipher(struct t1 *wc, unsigned char *readchunk) +static inline void cmd_decipher(struct t1 *wc, const u8 *readchunk) { struct command *cmd = NULL; unsigned long flags; @@ -221,7 +221,7 @@ static inline void cmd_decipher(struct t1 *wc, unsigned char *readchunk) spin_unlock_irqrestore(&wc->cmd_list_lock, flags); } -inline void cmd_decipher_vpmadt032(struct t1 *wc, unsigned char *readchunk) +inline void cmd_decipher_vpmadt032(struct t1 *wc, const u8 *readchunk) { unsigned long flags; struct vpmadt032 *vpm = wc->vpmadt032; @@ -1660,7 +1660,7 @@ static void t1_do_counters(struct t1 *wc) } } -static inline void t1_transmitprep(struct t1 *wc, unsigned char* writechunk) +static inline void t1_transmitprep(struct t1 *wc, u8 *writechunk) { int x; int y; @@ -1697,7 +1697,7 @@ static inline void t1_transmitprep(struct t1 *wc, unsigned char* writechunk) } } -static inline void t1_receiveprep(struct t1 *wc, unsigned char* readchunk) +static inline void t1_receiveprep(struct t1 *wc, const u8* readchunk) { int x,chan; unsigned char expected; -- cgit v1.2.3