summaryrefslogtreecommitdiff
path: root/drivers/dahdi/wctdm24xxp
diff options
context:
space:
mode:
authorShaun Ruffell <sruffell@digium.com>2011-08-30 16:37:57 +0000
committerShaun Ruffell <sruffell@digium.com>2011-08-30 16:37:57 +0000
commit505318cd5dd7fbfaf6cb92986497ea83d9a5a1e1 (patch)
tree46af260f0d7842cac39c61d1e7a9cdb62b1f190d /drivers/dahdi/wctdm24xxp
parentb4c7afd9bac6f0dd0cc5155274234c24bbc80fdc (diff)
wctdm24xxp: Probe for and configure modules in parallel.
Use the newly create bg_create and bg_join to actually probe / configure groups of 4 modules in parallel. This currently has to be done in groups of four due to the way 4-port modules are identified relative to single port modules. This provides a dramatic improvement in driver load time. When loading a single TDM2400 with 24 FXS ports before this change: # time modprobe wctdm24xxp vpmsupport=0 real 0m46.674s user 0m0.000s sys 0m0.520s And after this change: # time modprobe wctdm24xxp vpmsupport=0 real 0m7.900s user 0m0.000s sys 0m0.070s Note that the boards themselves are still configured serially. Board configuration can be parallelized once the assignment of board position is moved out of the function that is run in parallel. Otherwise it could be possible for board numbers to switch on repeated loads. Signed-off-by: Shaun Ruffell <sruffell@digium.com> Acked-by: Russ Meyerriecks <rmeyerriecks@digium.com> git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/trunk@10160 a0bf4364-ded3-4de4-8d8a-66a801d63aff
Diffstat (limited to 'drivers/dahdi/wctdm24xxp')
-rw-r--r--drivers/dahdi/wctdm24xxp/base.c331
1 files changed, 228 insertions, 103 deletions
diff --git a/drivers/dahdi/wctdm24xxp/base.c b/drivers/dahdi/wctdm24xxp/base.c
index fadce28..69cd139 100644
--- a/drivers/dahdi/wctdm24xxp/base.c
+++ b/drivers/dahdi/wctdm24xxp/base.c
@@ -448,15 +448,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;
@@ -526,7 +548,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");
@@ -535,45 +557,82 @@ static int config_vpmadt032(struct vpmadt032 *vpm, struct wctdm *wc)
res = gpakPingDsp(vpm->dspid, &vpm->version);
if (res) {
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->chans[i]->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;
}
@@ -4461,14 +4520,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);
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_test(wc->vpmadt032, &wc->vb);
@@ -4517,17 +4576,19 @@ static void wctdm_vpm_load_complete(struct device *dev, bool operational)
vpmoct_free(vpm);
}
-static void wctdm_initialize_vpm(struct wctdm *wc)
+static int wctdm_initialize_vpm(struct wctdm *wc, unsigned long unused)
{
int res = 0;
- if (!vpmsupport)
- return;
+ if (!vpmsupport) {
+ dev_notice(&wc->vb.pdev->dev, "VPM: Support Disabled\n");
+ return 0;
+ }
res = wctdm_initialize_vpmadt032(wc);
if (!res) {
wc->ctlreg |= 0x10;
- return;
+ return 0;
} else {
struct vpmoct *vpm;
unsigned long flags;
@@ -4536,7 +4597,7 @@ static void wctdm_initialize_vpm(struct wctdm *wc)
if (!vpm) {
dev_info(&wc->vb.pdev->dev,
"Unable to allocate memory for struct vpmoct\n");
- return;
+ return -ENOMEM;
}
vpm->dev = &wc->vb.pdev->dev;
@@ -4555,60 +4616,15 @@ static void wctdm_initialize_vpm(struct wctdm *wc)
vpmoct_free(vpm);
}
}
- return;
+ 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;
@@ -4625,8 +4641,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;
}
@@ -4642,10 +4656,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",
@@ -4656,24 +4666,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) {
@@ -4705,8 +4705,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;
@@ -5379,7 +5490,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;
@@ -5511,6 +5622,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);
@@ -5518,8 +5635,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.
@@ -5540,6 +5663,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,
@@ -5547,6 +5671,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;
@@ -5616,7 +5741,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);
@@ -5722,8 +5846,9 @@ static void __devexit wctdm_remove_one(struct pci_dev *pdev)
remove_sysfs_files(wc);
if (vpmadt032) {
+ flush_workqueue(vpmadt032->wq);
clear_bit(VPM150M_ACTIVE, &vpmadt032->control);
- flush_scheduled_work();
+ flush_workqueue(vpmadt032->wq);
} else if (vpmoct) {
while (wctdm_wait_for_ready(wc))
schedule();