Commit de49bc6e authored by Devin Heitmueller's avatar Devin Heitmueller Committed by Mauro Carvalho Chehab

[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: default avatarDevin Heitmueller <dheitmueller@kernellabs.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent baede40c
...@@ -62,6 +62,7 @@ struct xc5000_priv { ...@@ -62,6 +62,7 @@ struct xc5000_priv {
u8 radio_input; u8 radio_input;
int chip_id; int chip_id;
u16 pll_register_no;
}; };
/* Misc Defines */ /* Misc Defines */
...@@ -209,18 +210,21 @@ static struct XC_TV_STANDARD XC5000_Standard[MAX_TV_STANDARD] = { ...@@ -209,18 +210,21 @@ static struct XC_TV_STANDARD XC5000_Standard[MAX_TV_STANDARD] = {
struct xc5000_fw_cfg { struct xc5000_fw_cfg {
char *name; char *name;
u16 size; u16 size;
u16 pll_reg;
}; };
#define XC5000A_FIRMWARE "dvb-fe-xc5000-1.6.114.fw" #define XC5000A_FIRMWARE "dvb-fe-xc5000-1.6.114.fw"
static const struct xc5000_fw_cfg xc5000a_1_6_114 = { static const struct xc5000_fw_cfg xc5000a_1_6_114 = {
.name = XC5000A_FIRMWARE, .name = XC5000A_FIRMWARE,
.size = 12401, .size = 12401,
.pll_reg = 0x806c,
}; };
#define XC5000C_FIRMWARE "dvb-fe-xc5000c-41.024.5.fw" #define XC5000C_FIRMWARE "dvb-fe-xc5000c-41.024.5.fw"
static const struct xc5000_fw_cfg xc5000c_41_024_5 = { static const struct xc5000_fw_cfg xc5000c_41_024_5 = {
.name = XC5000C_FIRMWARE, .name = XC5000C_FIRMWARE,
.size = 16497, .size = 16497,
.pll_reg = 0x13,
}; };
static inline const struct xc5000_fw_cfg *xc5000_assign_firmware(int chip_id) static inline const struct xc5000_fw_cfg *xc5000_assign_firmware(int chip_id)
...@@ -234,7 +238,7 @@ static inline const struct xc5000_fw_cfg *xc5000_assign_firmware(int chip_id) ...@@ -234,7 +238,7 @@ static inline const struct xc5000_fw_cfg *xc5000_assign_firmware(int chip_id)
} }
} }
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_is_firmware_loaded(struct dvb_frontend *fe);
static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val); static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val);
static int xc5000_TunerReset(struct dvb_frontend *fe); static int xc5000_TunerReset(struct dvb_frontend *fe);
...@@ -619,6 +623,7 @@ static int xc5000_fwupload(struct dvb_frontend *fe) ...@@ -619,6 +623,7 @@ static int xc5000_fwupload(struct dvb_frontend *fe)
int ret; int ret;
const struct xc5000_fw_cfg *desired_fw = const struct xc5000_fw_cfg *desired_fw =
xc5000_assign_firmware(priv->chip_id); xc5000_assign_firmware(priv->chip_id);
priv->pll_register_no = desired_fw->pll_reg;
/* request the firmware, this will block and timeout */ /* request the firmware, this will block and timeout */
printk(KERN_INFO "xc5000: waiting for firmware upload (%s)...\n", printk(KERN_INFO "xc5000: waiting for firmware upload (%s)...\n",
...@@ -668,6 +673,7 @@ static void xc_debug_dump(struct xc5000_priv *priv) ...@@ -668,6 +673,7 @@ static void xc_debug_dump(struct xc5000_priv *priv)
u8 hw_majorversion = 0, hw_minorversion = 0; u8 hw_majorversion = 0, hw_minorversion = 0;
u8 fw_majorversion = 0, fw_minorversion = 0; u8 fw_majorversion = 0, fw_minorversion = 0;
u16 fw_buildversion = 0; u16 fw_buildversion = 0;
u16 regval;
/* Wait for stats to stabilize. /* Wait for stats to stabilize.
* Frame Lines needs two frame times after initial lock * Frame Lines needs two frame times after initial lock
...@@ -707,6 +713,11 @@ static void xc_debug_dump(struct xc5000_priv *priv) ...@@ -707,6 +713,11 @@ static void xc_debug_dump(struct xc5000_priv *priv)
xc_get_totalgain(priv, &totalgain); xc_get_totalgain(priv, &totalgain);
dprintk(1, "*** Total gain = %d.%d dB\n", totalgain / 256, dprintk(1, "*** Total gain = %d.%d dB\n", totalgain / 256,
(totalgain % 256) * 100 / 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) static int xc5000_set_params(struct dvb_frontend *fe)
...@@ -717,7 +728,7 @@ static int xc5000_set_params(struct dvb_frontend *fe) ...@@ -717,7 +728,7 @@ static int xc5000_set_params(struct dvb_frontend *fe)
u32 freq = fe->dtv_property_cache.frequency; u32 freq = fe->dtv_property_cache.frequency;
u32 delsys = fe->dtv_property_cache.delivery_system; 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"); dprintk(1, "Unable to load firmware and init tuner\n");
return -EINVAL; return -EINVAL;
} }
...@@ -850,6 +861,7 @@ static int xc5000_set_tv_freq(struct dvb_frontend *fe, ...@@ -850,6 +861,7 @@ static int xc5000_set_tv_freq(struct dvb_frontend *fe,
struct analog_parameters *params) struct analog_parameters *params)
{ {
struct xc5000_priv *priv = fe->tuner_priv; struct xc5000_priv *priv = fe->tuner_priv;
u16 pll_lock_status;
int ret; int ret;
dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n", dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n",
...@@ -930,6 +942,21 @@ static int xc5000_set_tv_freq(struct dvb_frontend *fe, ...@@ -930,6 +942,21 @@ static int xc5000_set_tv_freq(struct dvb_frontend *fe,
if (debug) if (debug)
xc_debug_dump(priv); 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; return 0;
} }
...@@ -1000,7 +1027,7 @@ static int xc5000_set_analog_params(struct dvb_frontend *fe, ...@@ -1000,7 +1027,7 @@ static int xc5000_set_analog_params(struct dvb_frontend *fe,
if (priv->i2c_props.adap == NULL) if (priv->i2c_props.adap == NULL)
return -EINVAL; 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"); dprintk(1, "Unable to load firmware and init tuner\n");
return -EINVAL; return -EINVAL;
} }
...@@ -1058,19 +1085,28 @@ static int xc5000_get_status(struct dvb_frontend *fe, u32 *status) ...@@ -1058,19 +1085,28 @@ static int xc5000_get_status(struct dvb_frontend *fe, u32 *status)
return 0; 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; struct xc5000_priv *priv = fe->tuner_priv;
int ret = XC_RESULT_SUCCESS; int ret = XC_RESULT_SUCCESS;
u16 pll_lock_status;
if (force || xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) {
fw_retry:
if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) {
ret = xc5000_fwupload(fe); ret = xc5000_fwupload(fe);
if (ret != XC_RESULT_SUCCESS) if (ret != XC_RESULT_SUCCESS)
return ret; return ret;
msleep(20);
/* Start the tuner self-calibration process */ /* Start the tuner self-calibration process */
ret |= xc_initialize(priv); ret |= xc_initialize(priv);
if (ret != XC_RESULT_SUCCESS)
goto fw_retry;
/* Wait for calibration to complete. /* Wait for calibration to complete.
* We could continue but XC5000 will clock stretch subsequent * We could continue but XC5000 will clock stretch subsequent
* I2C transactions until calibration is complete. This way we * I2C transactions until calibration is complete. This way we
...@@ -1078,6 +1114,16 @@ static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe) ...@@ -1078,6 +1114,16 @@ static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe)
*/ */
xc_wait(100); 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 */ /* Default to "CABLE" mode */
ret |= xc_write_reg(priv, XREG_SIGNALSOURCE, XC_RF_MODE_CABLE); ret |= xc_write_reg(priv, XREG_SIGNALSOURCE, XC_RF_MODE_CABLE);
} }
...@@ -1113,7 +1159,7 @@ static int xc5000_init(struct dvb_frontend *fe) ...@@ -1113,7 +1159,7 @@ static int xc5000_init(struct dvb_frontend *fe)
struct xc5000_priv *priv = fe->tuner_priv; struct xc5000_priv *priv = fe->tuner_priv;
dprintk(1, "%s()\n", __func__); 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"); printk(KERN_ERR "xc5000: Unable to initialise tuner\n");
return -EREMOTEIO; return -EREMOTEIO;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment