summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShaun Ruffell <sruffell@digium.com>2011-02-18 16:36:01 -0600
committerShaun Ruffell <sruffell@digium.com>2011-04-15 14:19:01 -0500
commit6eae311e4ced7446d7c3fe2dd2511cc5e1d04a07 (patch)
tree8a31cae4bad8434b2cddda5e4c37dc884924bc7d
parent206b5d94576722df68efa61f295fbd91b9d974a4 (diff)
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 <sruffell@digium.com>
-rw-r--r--drivers/dahdi/wctdm24xxp/base.c324
1 files 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 <linux/async.h>
#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 */