From 6eae311e4ced7446d7c3fe2dd2511cc5e1d04a07 Mon Sep 17 00:00:00 2001 From: Shaun Ruffell Date: Fri, 18 Feb 2011 16:36:01 -0600 Subject: wctdm24xxp: Load the onboard modules in parallel as much as possible. This provides a dramatic improvement in driver load time. Signed-off-by: Shaun Ruffell --- drivers/dahdi/wctdm24xxp/base.c | 324 +++++++++++++++++++++++++++------------- 1 file changed, 224 insertions(+), 100 deletions(-) diff --git a/drivers/dahdi/wctdm24xxp/base.c b/drivers/dahdi/wctdm24xxp/base.c index 134358c..02aab40 100644 --- a/drivers/dahdi/wctdm24xxp/base.c +++ b/drivers/dahdi/wctdm24xxp/base.c @@ -62,7 +62,7 @@ Tx Gain - W/Pre-Emphasis: -23.99 to 0.00 db /* Define this if you would like to load the modules in parallel. While this * can speed up loads when multiple cards handled by this driver are installed, * it also makes it impossible to abort module loads with ctrl-c */ -#undef USE_ASYNC_INIT +#define USE_ASYNC_INIT #include #else #undef USE_ASYNC_INIT @@ -438,15 +438,37 @@ setchanconfig_from_state(struct vpmadt032 *vpm, int channel, sizeof(chanconfig->EcanParametersB)); } -static int config_vpmadt032(struct vpmadt032 *vpm, struct wctdm *wc) +struct vpmadt032_channel_setup { + struct work_struct work; + struct wctdm *wc; +}; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) +static void vpm_setup_work_func(void *data) { - int res, i; - GpakPortConfig_t portconfig = {0}; - gpakConfigPortStatus_t configportstatus; - GPAK_PortConfigStat_t pstatus; + struct vpmadt032_channel_setup *setup = data; +#else +static void vpm_setup_work_func(struct work_struct *work) +{ + struct vpmadt032_channel_setup *setup = + container_of(work, struct vpmadt032_channel_setup, work); +#endif + int i; + int res; GpakChannelConfig_t chanconfig; GPAK_ChannelConfigStat_t cstatus; GPAK_AlgControlStat_t algstatus; + GpakPortConfig_t portconfig = {0}; + gpakConfigPortStatus_t configportstatus; + GPAK_PortConfigStat_t pstatus; + struct vpmadt032 *vpm; + struct wctdm *const wc = setup->wc; + + WARN_ON(!wc); + WARN_ON(!wc->vpmadt032); + if (unlikely(!wc || !wc->vpmadt032)) + return; + vpm = wc->vpmadt032; /* First Serial Port config */ portconfig.SlotsSelect1 = SlotCfgNone; @@ -516,7 +538,7 @@ static int config_vpmadt032(struct vpmadt032 *vpm, struct wctdm *wc) if ((configportstatus = gpakConfigurePorts(vpm->dspid, &portconfig, &pstatus))) { dev_notice(&wc->vb.pdev->dev, "Configuration of ports failed (%d)!\n", configportstatus); - return -1; + return; } else { if (vpm->options.debug & DEBUG_ECHOCAN) dev_info(&wc->vb.pdev->dev, "Configured McBSP ports successfully\n"); @@ -524,45 +546,82 @@ static int config_vpmadt032(struct vpmadt032 *vpm, struct wctdm *wc) if ((res = gpakPingDsp(vpm->dspid, &vpm->version))) { dev_notice(&wc->vb.pdev->dev, "Error pinging DSP (%d)\n", res); - return -1; + return; } for (i = 0; i < vpm->options.channels; ++i) { - struct dahdi_chan *const chan = &wc->mods[i].chan->chan; vpm->curecstate[i].tap_length = 0; 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 = (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->curecstate[i].nlp_max_suppress = + vpm->options.vpmnlpmaxsupp; + vpm->curecstate[i].companding = ADT_COMP_ULAW; vpm->setchanconfig_from_state(vpm, i, &chanconfig); - if ((res = gpakConfigureChannel(vpm->dspid, i, tdmToTdm, &chanconfig, &cstatus))) { - dev_notice(&wc->vb.pdev->dev, "Unable to configure channel #%d (%d)", i, res); - if (res == 1) { + + res = gpakConfigureChannel(vpm->dspid, i, tdmToTdm, + &chanconfig, &cstatus); + if (res) { + dev_notice(&wc->vb.pdev->dev, + "Unable to configure channel #%d (%d)", + i, res); + if (res == 1) printk(KERN_CONT ", reason %d", cstatus); - } printk(KERN_CONT "\n"); - return -1; + goto exit; } - if ((res = gpakAlgControl(vpm->dspid, i, BypassEcanA, &algstatus))) { - dev_notice(&wc->vb.pdev->dev, "Unable to disable echo can on channel %d (reason %d:%d)\n", i + 1, res, algstatus); - return -1; + res = gpakAlgControl(vpm->dspid, i, BypassEcanA, &algstatus); + if (res) { + dev_notice(&wc->vb.pdev->dev, + "Unable to disable echo can on channel %d " + "(reason %d:%d)\n", i + 1, res, algstatus); + goto exit; } - if ((res = gpakAlgControl(vpm->dspid, i, BypassSwCompanding, &algstatus))) { - dev_notice(&wc->vb.pdev->dev, "Unable to disable echo can on channel %d (reason %d:%d)\n", i + 1, res, algstatus); - return -1; + res = gpakAlgControl(vpm->dspid, i, + BypassSwCompanding, &algstatus); + if (res) { + dev_notice(&wc->vb.pdev->dev, + "Unable to disable echo can on channel %d " + "(reason %d:%d)\n", i + 1, res, algstatus); + goto exit; } } - if ((res = gpakPingDsp(vpm->dspid, &vpm->version))) { + res = gpakPingDsp(vpm->dspid, &vpm->version); + if (res) { dev_notice(&wc->vb.pdev->dev, "Error pinging DSP (%d)\n", res); - return -1; + goto exit; } set_bit(VPM150M_ACTIVE, &vpm->control); +exit: + kfree(setup); +} + +static int config_vpmadt032(struct vpmadt032 *vpm, struct wctdm *wc) +{ + struct vpmadt032_channel_setup *setup; + + /* Because the channel configuration can take such a long time, let's + * move this out onto the VPM workqueue so the system can proceeded + * with startup. */ + + setup = kzalloc(sizeof(*setup), GFP_KERNEL); + if (!setup) + return -ENOMEM; + + setup->wc = wc; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + INIT_WORK(&setup->work, vpm_setup_work_func, setup); +#else + INIT_WORK(&setup->work, vpm_setup_work_func); +#endif + + queue_work(vpm->wq, &setup->work); + return 0; } @@ -4230,14 +4289,14 @@ static int wctdm_initialize_vpmadt032(struct wctdm *wc) options.vpmnlptype = vpmnlptype; options.vpmnlpthresh = vpmnlpthresh; options.vpmnlpmaxsupp = vpmnlpmaxsupp; - options.channels = wc->avchannels; + options.channels = wc->desc->ports; 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. */ res = vpmadt032_init(wc->vpmadt032, &wc->vb); @@ -4259,71 +4318,27 @@ static int wctdm_initialize_vpmadt032(struct wctdm *wc) return 0; } -static void wctdm_initialize_vpm(struct wctdm *wc) +static int wctdm_initialize_vpm(struct wctdm *wc, unsigned long unused) { int res = 0; if (!vpmsupport) { dev_notice(&wc->vb.pdev->dev, "VPM: Support Disabled\n"); - return; + return 0; } res = wctdm_initialize_vpmadt032(wc); if (!res) wc->ctlreg |= 0x10; + return 0; } -static void wctdm_identify_modules(struct wctdm *wc) +static int __wctdm_identify_module_group(struct wctdm *wc, unsigned long base) { int x; unsigned long flags; - wc->ctlreg = 0x00; - /* - * 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 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 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. - */ - - /* Make sure all units go into daisy chain mode */ - spin_lock_irqsave(&wc->reglock, flags); - for (x = 0; x < ARRAY_SIZE(wc->mods); x++) - wc->mods[x].type = FXSINIT; - spin_unlock_irqrestore(&wc->reglock, flags); - - /* Wait just a bit; this makes sure that cmd_dequeue is emitting SPI - * commands in the appropriate mode(s). */ - udelay(2000); - - /* 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->mods_per_board = wc->desc->ports; - spin_unlock_irqrestore(&wc->reglock, flags); - - /* Reset modules */ - for (x = 0; x < wc->mods_per_board; x++) { + for (x = base; x < base + 4; ++x) { struct wctdm_module *const mod = &wc->mods[x]; enum {SANE = 1, UNKNOWN = 0}; int ret = 0, readi = 0; @@ -4340,8 +4355,6 @@ retry: "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); continue; } @@ -4357,10 +4370,6 @@ retry: "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", @@ -4371,24 +4380,14 @@ retry: } ret = wctdm_init_voicedaa(wc, mod, 0, 0, UNKNOWN); - if (!ret) { - dev_info(&wc->vb.pdev->dev, "Port %d: Installed -- " - "AUTO FXO (%s mode)\n", x + 1, - fxo_modes[_opermode].name); + if (!ret) continue; - } - if (!wctdm_init_qrvdri(wc, x)) { - dev_info(&wc->vb.pdev->dev, - "Port %d: Installed -- QRV DRI card\n", x + 1); + if (!wctdm_init_qrvdri(wc, x)) continue; - } - if (is_hx8(wc) && !wctdm_init_b400m(wc, x)) { - dev_info(&wc->vb.pdev->dev, "Port %d: Installed -- BRI " - "quad-span module\n", x + 1); + if (is_hx8(wc) && !wctdm_init_b400m(wc, x)) continue; - } if ((wc->desc->ports != 24) && ((x&0x3) == 1) && !altcs) { @@ -4420,8 +4419,119 @@ retry: } mod->type = NONE; - dev_info(&wc->vb.pdev->dev, "Port %d: Not installed\n", x + 1); - } /* for (x...) */ + } + return 0; +} + +/** + * wctdm_print_moule_configuration - Print the configuration to the kernel log + * @wc: The card we're interested in. + * + * This is to ensure that the module configuration from each card shows up + * sequentially in the kernel log, as opposed to interleaved with one another. + * + */ +static void wctdm_print_module_configuration(const struct wctdm *const wc) +{ + int i; + static DEFINE_MUTEX(print); + + mutex_lock(&print); + for (i = 0; i < wc->mods_per_board; ++i) { + const struct wctdm_module *const mod = &wc->mods[i]; + + switch (mod->type) { + case FXO: + dev_info(&wc->vb.pdev->dev, "Port %d: Installed -- " + "AUTO FXO (%s mode)\n", i + 1, + fxo_modes[_opermode].name); + break; + case FXS: + dev_info(&wc->vb.pdev->dev, + "Port %d: Installed -- AUTO FXS/DPO\n", i + 1); + break; + case BRI: + dev_info(&wc->vb.pdev->dev, "Port %d: Installed -- BRI " + "quad-span module\n", i + 1); + break; + case QRV: + dev_info(&wc->vb.pdev->dev, + "Port %d: Installed -- QRV DRI card\n", i + 1); + break; + case NONE: + dev_info(&wc->vb.pdev->dev, + "Port %d: Not installed\n", i + 1); + break; + case FXSINIT: + break; + } + } + mutex_unlock(&print); +} + +static void wctdm_identify_modules(struct wctdm *wc) +{ + int x; + unsigned long flags; + struct bg *bg_work[ARRAY_SIZE(wc->mods)/4 + 1] = {NULL, }; + + wc->ctlreg = 0x00; + + /* + * 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 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 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. + */ + + /* Make sure all units go into daisy chain mode */ + spin_lock_irqsave(&wc->reglock, flags); + for (x = 0; x < ARRAY_SIZE(wc->mods); x++) + wc->mods[x].type = FXSINIT; + spin_unlock_irqrestore(&wc->reglock, flags); + + /* Wait just a bit; this makes sure that cmd_dequeue is emitting SPI + * commands in the appropriate mode(s). */ + msleep(20); + + /* 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->mods_per_board = wc->desc->ports; + spin_unlock_irqrestore(&wc->reglock, flags); + + BUG_ON(wc->desc->ports % 4); + + /* Detecting and configuring the modules over voicebus takes a + * significant amount of time. We can speed things up by performing + * this in parallel for each group of four modules. */ + for (x = 0; x < wc->desc->ports/4; x++) + bg_work[x] = bg_create(wc, __wctdm_identify_module_group, x*4); + + for (x = 0; bg_work[x]; ++x) + bg_join(bg_work[x]); + + wctdm_print_module_configuration(wc); } static struct pci_driver wctdm_driver; @@ -5011,7 +5121,7 @@ __wctdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) struct wctdm *wc; unsigned int pos; int i, ret; - + struct bg *vpm_work; int anamods, digimods, curchan, curspan; neonmwi_offlimit_cycles = neonmwi_offlimit / MS_PER_HOOKCHECK; @@ -5140,6 +5250,12 @@ __wctdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) /* 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; + vpm_work = bg_create(wc, wctdm_initialize_vpm, 0); + if (!vpm_work) { + wctdm_back_out_gracefully(wc); + return -ENOMEM; + } + /* Now track down what modules are installed */ wctdm_identify_modules(wc); @@ -5147,8 +5263,14 @@ __wctdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (fatal_signal_pending(current)) { wctdm_back_out_gracefully(wc); + bg_join(vpm_work); return -EINTR; } + + /* We need to wait for the vpm thread to finish before we setup the + * spans in order to ensure they are named properly. */ + bg_join(vpm_work); + /* * 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. @@ -5169,6 +5291,7 @@ __wctdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) "detected on a non-hybrid card. " "This is unsupported.\n"); wctdm_back_out_gracefully(wc); + bg_join(vpm_work); return -EIO; } wc->spans[curspan] = wctdm_init_span(wc, curspan, @@ -5176,6 +5299,7 @@ __wctdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) pos); if (!wc->spans[curspan]) { wctdm_back_out_gracefully(wc); + bg_join(vpm_work); return -EIO; } b4 = mod->mod.bri; @@ -5245,7 +5369,6 @@ __wctdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) wc->avchannels = curchan; - wctdm_initialize_vpm(wc); #ifdef USE_ASYNC_INIT async_synchronize_cookie(cookie); @@ -5345,8 +5468,9 @@ static void __devexit wctdm_remove_one(struct pci_dev *pdev) remove_sysfs_files(wc); if (vpm) { + flush_workqueue(vpm->wq); clear_bit(VPM150M_ACTIVE, &vpm->control); - flush_scheduled_work(); + flush_workqueue(vpm->wq); } /* shut down any BRI modules */ -- cgit v1.2.3