[svn-commits] sruffell: branch linux/sruffell/wctdm24xxp-cmdlist r9865 - /linux/team/sruffe...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Wed Mar 16 16:41:13 CDT 2011


Author: sruffell
Date: Wed Mar 16 16:41:09 2011
New Revision: 9865

URL: http://svnview.digium.com/svn/dahdi?view=rev&rev=9865
Log:
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 at digium.com>

Modified:
    linux/team/sruffell/wctdm24xxp-cmdlist/drivers/dahdi/wctdm24xxp/base.c

Modified: linux/team/sruffell/wctdm24xxp-cmdlist/drivers/dahdi/wctdm24xxp/base.c
URL: http://svnview.digium.com/svn/dahdi/linux/team/sruffell/wctdm24xxp-cmdlist/drivers/dahdi/wctdm24xxp/base.c?view=diff&rev=9865&r1=9864&r2=9865
==============================================================================
--- linux/team/sruffell/wctdm24xxp-cmdlist/drivers/dahdi/wctdm24xxp/base.c (original)
+++ linux/team/sruffell/wctdm24xxp-cmdlist/drivers/dahdi/wctdm24xxp/base.c Wed Mar 16 16:41:09 2011
@@ -62,7 +62,7 @@
 /* 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 @@
 		sizeof(chanconfig->EcanParametersB));
 }
 
-static int config_vpmadt032(struct vpmadt032 *vpm, struct wctdm *wc)
-{
-	int res, i;
+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)
+{
+	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;
-	GpakChannelConfig_t chanconfig;
-	GPAK_ChannelConfigStat_t cstatus;
-	GPAK_AlgControlStat_t algstatus;
+	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 @@
 
 	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,44 +546,81 @@
 
 	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;
-		}
-
-		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;
-		}
-
-		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;
-		}
-	}
-
-	if ((res = gpakPingDsp(vpm->dspid, &vpm->version))) {
+			goto exit;
+		}
+
+		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;
+		}
+
+		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;
+		}
+	}
+
+	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;
 }
@@ -4223,14 +4282,14 @@
 	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);
@@ -4252,24 +4311,163 @@
 	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;
-}
-
-static void wctdm_identify_modules(struct wctdm *wc)
+	return 0;
+}
+
+static int __wctdm_identify_module_group(struct wctdm *wc, unsigned long base)
 {
 	int x;
 	unsigned long flags;
+
+	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;
+		bool altcs = false;
+
+		if (fatal_signal_pending(current))
+			break;
+retry:
+		ret = wctdm_init_proslic(wc, mod, 0, 0, UNKNOWN);
+		if (!ret) {
+			if (debug & DEBUG_CARD) {
+				readi = wctdm_getreg(wc, mod, LOOP_I_LIMIT);
+				dev_info(&wc->vb.pdev->dev,
+					 "Proslic module %d loop current "
+					 "is %dmA\n", x, ((readi*3) + 20));
+			}
+			continue;
+		}
+
+		if (ret != -2) {
+			/* Init with Manual Calibration */
+			if (!wctdm_init_proslic(wc, mod, 0, 1, SANE)) {
+
+				if (debug & DEBUG_CARD) {
+					readi = wctdm_getreg(wc, mod,
+							     LOOP_I_LIMIT);
+					dev_info(&wc->vb.pdev->dev,
+						 "Proslic module %d loop "
+						 "current is %dmA\n", x,
+						 ((readi*3)+20));
+				}
+			} else {
+				dev_notice(&wc->vb.pdev->dev,
+					   "Port %d: FAILED FXS (%s)\n",
+					   x + 1, fxshonormode ?
+					   fxo_modes[_opermode].name : "FCC");
+			}
+			continue;
+		}
+
+		ret = wctdm_init_voicedaa(wc, mod, 0, 0, UNKNOWN);
+		if (!ret)
+			continue;
+
+		if (!wctdm_init_qrvdri(wc, x))
+			continue;
+
+		if (is_hx8(wc) && !wctdm_init_b400m(wc, x))
+			continue;
+
+		if ((wc->desc->ports != 24) && ((x&0x3) == 1) && !altcs) {
+
+			spin_lock_irqsave(&wc->reglock, flags);
+			set_offsets(mod, 2);
+			altcs = true;
+
+			if (wc->desc->ports == 4) {
+				set_offsets(&wc->mods[x+1], 3);
+				set_offsets(&wc->mods[x+2], 3);
+			}
+
+			mod->type = FXSINIT;
+			spin_unlock_irqrestore(&wc->reglock, flags);
+
+			udelay(1000);
+			udelay(1000);
+
+			spin_lock_irqsave(&wc->reglock, flags);
+			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;
+		}
+
+		mod->type = NONE;
+	}
+	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;
 
 	/*
@@ -4307,7 +4505,7 @@
 
 	/* Wait just a bit; this makes sure that cmd_dequeue is emitting SPI
 	 * commands in the appropriate mode(s). */
-	udelay(2000);
+	msleep(20);
 
 	/* Now that all the cards have been reset, we can stop checking them
 	 * all if there aren't as many */
@@ -4315,106 +4513,18 @@
 	wc->mods_per_board = wc->desc->ports;
 	spin_unlock_irqrestore(&wc->reglock, flags);
 
-	/* Reset modules */
-	for (x = 0; x < wc->mods_per_board; x++) {
-		struct wctdm_module *const mod = &wc->mods[x];
-		enum {SANE = 1, UNKNOWN = 0};
-		int ret = 0, readi = 0;
-		bool altcs = false;
-
-		if (fatal_signal_pending(current))
-			break;
-retry:
-		ret = wctdm_init_proslic(wc, mod, 0, 0, UNKNOWN);
-		if (!ret) {
-			if (debug & DEBUG_CARD) {
-				readi = wctdm_getreg(wc, mod, 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 -- AUTO FXS/DPO\n", x + 1);
-			continue;
-		}
-
-		if (ret != -2) {
-			/* Init with Manual Calibration */
-			if (!wctdm_init_proslic(wc, mod, 0, 1, SANE)) {
-
-				if (debug & DEBUG_CARD) {
-					readi = wctdm_getreg(wc, mod,
-							     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");
-			}
-			continue;
-		}
-
-		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);
-			continue;
-		}
-
-		if (!wctdm_init_qrvdri(wc, x)) {
-			dev_info(&wc->vb.pdev->dev,
-				 "Port %d: Installed -- QRV DRI card\n", x + 1);
-			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);
-			continue;
-		}
-
-		if ((wc->desc->ports != 24) && ((x&0x3) == 1) && !altcs) {
-
-			spin_lock_irqsave(&wc->reglock, flags);
-			set_offsets(mod, 2);
-			altcs = true;
-
-			if (wc->desc->ports == 4) {
-				set_offsets(&wc->mods[x+1], 3);
-				set_offsets(&wc->mods[x+2], 3);
-			}
-
-			mod->type = FXSINIT;
-			spin_unlock_irqrestore(&wc->reglock, flags);
-
-			udelay(1000);
-			udelay(1000);
-
-			spin_lock_irqsave(&wc->reglock, flags);
-			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;
-		}
-
-		mod->type = NONE;
-		dev_info(&wc->vb.pdev->dev, "Port %d: Not installed\n", x + 1);
-	} /* for (x...) */
+	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;
@@ -5004,7 +5114,7 @@
 	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;
@@ -5133,6 +5243,12 @@
 /* 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);
 
@@ -5140,8 +5256,14 @@
 
 	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.
@@ -5162,6 +5284,7 @@
 					"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,
@@ -5169,6 +5292,7 @@
 							     pos);
 			if (!wc->spans[curspan]) {
 				wctdm_back_out_gracefully(wc);
+				bg_join(vpm_work);
 				return -EIO;
 			}
 			b4 = mod->mod.bri;
@@ -5238,7 +5362,6 @@
 
 	wc->avchannels = curchan;
 
-	wctdm_initialize_vpm(wc);
 
 #ifdef USE_ASYNC_INIT
 		async_synchronize_cookie(cookie);
@@ -5338,8 +5461,9 @@
 		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 */




More information about the svn-commits mailing list