[media] xc5000: reset device if encountering PLL lock failure

It's possible for the xc5000 to enter an unknown state such that all
subsequent tuning requests fail.  The only way to recover is to reset the
tuner and reload the firmware.  This problem was detected after several days
straight of issuing tuning requests every five seconds.

Reset the firmware in the event that the PLL is in an unlocked state.  This
solution was provided by the engineer at CrestaTech (the company that acquired
Xceive).

Signed-off-by: Devin Heitmueller <dheitmueller@kernellabs.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c
index 06f66fe..cd92b9e 100644
--- a/drivers/media/common/tuners/xc5000.c
+++ b/drivers/media/common/tuners/xc5000.c
@@ -62,6 +62,7 @@
 	u8  radio_input;
 
 	int chip_id;
+	u16 pll_register_no;
 };
 
 /* Misc Defines */
@@ -209,18 +210,21 @@
 struct xc5000_fw_cfg {
 	char *name;
 	u16 size;
+	u16 pll_reg;
 };
 
 #define XC5000A_FIRMWARE "dvb-fe-xc5000-1.6.114.fw"
 static const struct xc5000_fw_cfg xc5000a_1_6_114 = {
 	.name = XC5000A_FIRMWARE,
 	.size = 12401,
+	.pll_reg = 0x806c,
 };
 
 #define XC5000C_FIRMWARE "dvb-fe-xc5000c-41.024.5.fw"
 static const struct xc5000_fw_cfg xc5000c_41_024_5 = {
 	.name = XC5000C_FIRMWARE,
 	.size = 16497,
+	.pll_reg = 0x13,
 };
 
 static inline const struct xc5000_fw_cfg *xc5000_assign_firmware(int chip_id)
@@ -234,7 +238,7 @@
 	}
 }
 
-static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe);
+static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force);
 static int xc5000_is_firmware_loaded(struct dvb_frontend *fe);
 static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val);
 static int xc5000_TunerReset(struct dvb_frontend *fe);
@@ -619,6 +623,7 @@
 	int ret;
 	const struct xc5000_fw_cfg *desired_fw =
 		xc5000_assign_firmware(priv->chip_id);
+	priv->pll_register_no = desired_fw->pll_reg;
 
 	/* request the firmware, this will block and timeout */
 	printk(KERN_INFO "xc5000: waiting for firmware upload (%s)...\n",
@@ -668,6 +673,7 @@
 	u8 hw_majorversion = 0, hw_minorversion = 0;
 	u8 fw_majorversion = 0, fw_minorversion = 0;
 	u16 fw_buildversion = 0;
+	u16 regval;
 
 	/* Wait for stats to stabilize.
 	 * Frame Lines needs two frame times after initial lock
@@ -707,6 +713,11 @@
 	xc_get_totalgain(priv,  &totalgain);
 	dprintk(1, "*** Total gain = %d.%d dB\n", totalgain / 256,
 		(totalgain % 256) * 100 / 256);
+
+	if (priv->pll_register_no) {
+		xc5000_readreg(priv, priv->pll_register_no, &regval);
+		dprintk(1, "*** PLL lock status = 0x%04x\n", regval);
+	}
 }
 
 static int xc5000_set_params(struct dvb_frontend *fe)
@@ -717,7 +728,7 @@
 	u32 freq = fe->dtv_property_cache.frequency;
 	u32 delsys  = fe->dtv_property_cache.delivery_system;
 
-	if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) {
+	if (xc_load_fw_and_init_tuner(fe, 0) != XC_RESULT_SUCCESS) {
 		dprintk(1, "Unable to load firmware and init tuner\n");
 		return -EINVAL;
 	}
@@ -850,6 +861,7 @@
 	struct analog_parameters *params)
 {
 	struct xc5000_priv *priv = fe->tuner_priv;
+	u16 pll_lock_status;
 	int ret;
 
 	dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n",
@@ -930,6 +942,21 @@
 	if (debug)
 		xc_debug_dump(priv);
 
+	if (priv->pll_register_no != 0) {
+		msleep(20);
+		xc5000_readreg(priv, priv->pll_register_no, &pll_lock_status);
+		if (pll_lock_status > 63) {
+			/* PLL is unlocked, force reload of the firmware */
+			dprintk(1, "xc5000: PLL not locked (0x%x).  Reloading...\n",
+				pll_lock_status);
+			if (xc_load_fw_and_init_tuner(fe, 1) != XC_RESULT_SUCCESS) {
+				printk(KERN_ERR "xc5000: Unable to reload fw\n");
+				return -EREMOTEIO;
+			}
+			goto tune_channel;
+		}
+	}
+
 	return 0;
 }
 
@@ -1000,7 +1027,7 @@
 	if (priv->i2c_props.adap == NULL)
 		return -EINVAL;
 
-	if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) {
+	if (xc_load_fw_and_init_tuner(fe, 0) != XC_RESULT_SUCCESS) {
 		dprintk(1, "Unable to load firmware and init tuner\n");
 		return -EINVAL;
 	}
@@ -1058,19 +1085,28 @@
 	return 0;
 }
 
-static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe)
+static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force)
 {
 	struct xc5000_priv *priv = fe->tuner_priv;
 	int ret = XC_RESULT_SUCCESS;
+	u16 pll_lock_status;
 
-	if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) {
+	if (force || xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) {
+
+fw_retry:
+
 		ret = xc5000_fwupload(fe);
 		if (ret != XC_RESULT_SUCCESS)
 			return ret;
 
+		msleep(20);
+
 		/* Start the tuner self-calibration process */
 		ret |= xc_initialize(priv);
 
+		if (ret != XC_RESULT_SUCCESS)
+			goto fw_retry;
+
 		/* Wait for calibration to complete.
 		 * We could continue but XC5000 will clock stretch subsequent
 		 * I2C transactions until calibration is complete.  This way we
@@ -1078,6 +1114,16 @@
 		 */
 		xc_wait(100);
 
+		if (priv->pll_register_no) {
+			xc5000_readreg(priv, priv->pll_register_no,
+				       &pll_lock_status);
+			if (pll_lock_status > 63) {
+				/* PLL is unlocked, force reload of the firmware */
+				printk(KERN_ERR "xc5000: PLL not running after fwload.\n");
+				goto fw_retry;
+			}
+		}
+
 		/* Default to "CABLE" mode */
 		ret |= xc_write_reg(priv, XREG_SIGNALSOURCE, XC_RF_MODE_CABLE);
 	}
@@ -1113,7 +1159,7 @@
 	struct xc5000_priv *priv = fe->tuner_priv;
 	dprintk(1, "%s()\n", __func__);
 
-	if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) {
+	if (xc_load_fw_and_init_tuner(fe, 0) != XC_RESULT_SUCCESS) {
 		printk(KERN_ERR "xc5000: Unable to initialise tuner\n");
 		return -EREMOTEIO;
 	}