cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

fc0011.c (12268B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Fitipower FC0011 tuner driver
      4 *
      5 * Copyright (C) 2012 Michael Buesch <m@bues.ch>
      6 *
      7 * Derived from FC0012 tuner driver:
      8 * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>
      9 */
     10
     11#include "fc0011.h"
     12
     13
     14/* Tuner registers */
     15enum {
     16	FC11_REG_0,
     17	FC11_REG_FA,		/* FA */
     18	FC11_REG_FP,		/* FP */
     19	FC11_REG_XINHI,		/* XIN high 8 bit */
     20	FC11_REG_XINLO,		/* XIN low 8 bit */
     21	FC11_REG_VCO,		/* VCO */
     22	FC11_REG_VCOSEL,	/* VCO select */
     23	FC11_REG_7,		/* Unknown tuner reg 7 */
     24	FC11_REG_8,		/* Unknown tuner reg 8 */
     25	FC11_REG_9,
     26	FC11_REG_10,		/* Unknown tuner reg 10 */
     27	FC11_REG_11,		/* Unknown tuner reg 11 */
     28	FC11_REG_12,
     29	FC11_REG_RCCAL,		/* RC calibrate */
     30	FC11_REG_VCOCAL,	/* VCO calibrate */
     31	FC11_REG_15,
     32	FC11_REG_16,		/* Unknown tuner reg 16 */
     33	FC11_REG_17,
     34
     35	FC11_NR_REGS,		/* Number of registers */
     36};
     37
     38enum FC11_REG_VCOSEL_bits {
     39	FC11_VCOSEL_2		= 0x08, /* VCO select 2 */
     40	FC11_VCOSEL_1		= 0x10, /* VCO select 1 */
     41	FC11_VCOSEL_CLKOUT	= 0x20, /* Fix clock out */
     42	FC11_VCOSEL_BW7M	= 0x40, /* 7MHz bw */
     43	FC11_VCOSEL_BW6M	= 0x80, /* 6MHz bw */
     44};
     45
     46enum FC11_REG_RCCAL_bits {
     47	FC11_RCCAL_FORCE	= 0x10, /* force */
     48};
     49
     50enum FC11_REG_VCOCAL_bits {
     51	FC11_VCOCAL_RUN		= 0,	/* VCO calibration run */
     52	FC11_VCOCAL_VALUEMASK	= 0x3F,	/* VCO calibration value mask */
     53	FC11_VCOCAL_OK		= 0x40,	/* VCO calibration Ok */
     54	FC11_VCOCAL_RESET	= 0x80, /* VCO calibration reset */
     55};
     56
     57
     58struct fc0011_priv {
     59	struct i2c_adapter *i2c;
     60	u8 addr;
     61
     62	u32 frequency;
     63	u32 bandwidth;
     64};
     65
     66
     67static int fc0011_writereg(struct fc0011_priv *priv, u8 reg, u8 val)
     68{
     69	u8 buf[2] = { reg, val };
     70	struct i2c_msg msg = { .addr = priv->addr,
     71		.flags = 0, .buf = buf, .len = 2 };
     72
     73	if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
     74		dev_err(&priv->i2c->dev,
     75			"I2C write reg failed, reg: %02x, val: %02x\n",
     76			reg, val);
     77		return -EIO;
     78	}
     79
     80	return 0;
     81}
     82
     83static int fc0011_readreg(struct fc0011_priv *priv, u8 reg, u8 *val)
     84{
     85	u8 dummy;
     86	struct i2c_msg msg[2] = {
     87		{ .addr = priv->addr,
     88		  .flags = 0, .buf = &reg, .len = 1 },
     89		{ .addr = priv->addr,
     90		  .flags = I2C_M_RD, .buf = val ? : &dummy, .len = 1 },
     91	};
     92
     93	if (i2c_transfer(priv->i2c, msg, 2) != 2) {
     94		dev_err(&priv->i2c->dev,
     95			"I2C read failed, reg: %02x\n", reg);
     96		return -EIO;
     97	}
     98
     99	return 0;
    100}
    101
    102static void fc0011_release(struct dvb_frontend *fe)
    103{
    104	kfree(fe->tuner_priv);
    105	fe->tuner_priv = NULL;
    106}
    107
    108static int fc0011_init(struct dvb_frontend *fe)
    109{
    110	struct fc0011_priv *priv = fe->tuner_priv;
    111	int err;
    112
    113	if (WARN_ON(!fe->callback))
    114		return -EINVAL;
    115
    116	err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
    117			   FC0011_FE_CALLBACK_POWER, priv->addr);
    118	if (err) {
    119		dev_err(&priv->i2c->dev, "Power-on callback failed\n");
    120		return err;
    121	}
    122	err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
    123			   FC0011_FE_CALLBACK_RESET, priv->addr);
    124	if (err) {
    125		dev_err(&priv->i2c->dev, "Reset callback failed\n");
    126		return err;
    127	}
    128
    129	return 0;
    130}
    131
    132/* Initiate VCO calibration */
    133static int fc0011_vcocal_trigger(struct fc0011_priv *priv)
    134{
    135	int err;
    136
    137	err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RESET);
    138	if (err)
    139		return err;
    140	err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN);
    141	if (err)
    142		return err;
    143
    144	return 0;
    145}
    146
    147/* Read VCO calibration value */
    148static int fc0011_vcocal_read(struct fc0011_priv *priv, u8 *value)
    149{
    150	int err;
    151
    152	err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN);
    153	if (err)
    154		return err;
    155	usleep_range(10000, 20000);
    156	err = fc0011_readreg(priv, FC11_REG_VCOCAL, value);
    157	if (err)
    158		return err;
    159
    160	return 0;
    161}
    162
    163static int fc0011_set_params(struct dvb_frontend *fe)
    164{
    165	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
    166	struct fc0011_priv *priv = fe->tuner_priv;
    167	int err;
    168	unsigned int i, vco_retries;
    169	u32 freq = p->frequency / 1000;
    170	u32 bandwidth = p->bandwidth_hz / 1000;
    171	u32 fvco, xin, frac, xdiv, xdivr;
    172	u8 fa, fp, vco_sel, vco_cal;
    173	u8 regs[FC11_NR_REGS] = { };
    174
    175	regs[FC11_REG_7] = 0x0F;
    176	regs[FC11_REG_8] = 0x3E;
    177	regs[FC11_REG_10] = 0xB8;
    178	regs[FC11_REG_11] = 0x80;
    179	regs[FC11_REG_RCCAL] = 0x04;
    180	err = fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]);
    181	err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]);
    182	err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]);
    183	err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]);
    184	err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]);
    185	if (err)
    186		return -EIO;
    187
    188	/* Set VCO freq and VCO div */
    189	if (freq < 54000) {
    190		fvco = freq * 64;
    191		regs[FC11_REG_VCO] = 0x82;
    192	} else if (freq < 108000) {
    193		fvco = freq * 32;
    194		regs[FC11_REG_VCO] = 0x42;
    195	} else if (freq < 216000) {
    196		fvco = freq * 16;
    197		regs[FC11_REG_VCO] = 0x22;
    198	} else if (freq < 432000) {
    199		fvco = freq * 8;
    200		regs[FC11_REG_VCO] = 0x12;
    201	} else {
    202		fvco = freq * 4;
    203		regs[FC11_REG_VCO] = 0x0A;
    204	}
    205
    206	/* Calc XIN. The PLL reference frequency is 18 MHz. */
    207	xdiv = fvco / 18000;
    208	WARN_ON(xdiv > 0xFF);
    209	frac = fvco - xdiv * 18000;
    210	frac = (frac << 15) / 18000;
    211	if (frac >= 16384)
    212		frac += 32786;
    213	if (!frac)
    214		xin = 0;
    215	else
    216		xin = clamp_t(u32, frac, 512, 65024);
    217	regs[FC11_REG_XINHI] = xin >> 8;
    218	regs[FC11_REG_XINLO] = xin;
    219
    220	/* Calc FP and FA */
    221	xdivr = xdiv;
    222	if (fvco - xdiv * 18000 >= 9000)
    223		xdivr += 1; /* round */
    224	fp = xdivr / 8;
    225	fa = xdivr - fp * 8;
    226	if (fa < 2) {
    227		fp -= 1;
    228		fa += 8;
    229	}
    230	if (fp > 0x1F) {
    231		fp = 0x1F;
    232		fa = 0xF;
    233	}
    234	if (fa >= fp) {
    235		dev_warn(&priv->i2c->dev,
    236			 "fa %02X >= fp %02X, but trying to continue\n",
    237			 (unsigned int)(u8)fa, (unsigned int)(u8)fp);
    238	}
    239	regs[FC11_REG_FA] = fa;
    240	regs[FC11_REG_FP] = fp;
    241
    242	/* Select bandwidth */
    243	switch (bandwidth) {
    244	case 8000:
    245		break;
    246	case 7000:
    247		regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW7M;
    248		break;
    249	default:
    250		dev_warn(&priv->i2c->dev, "Unsupported bandwidth %u kHz. Using 6000 kHz.\n",
    251			 bandwidth);
    252		bandwidth = 6000;
    253		fallthrough;
    254	case 6000:
    255		regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW6M;
    256		break;
    257	}
    258
    259	/* Pre VCO select */
    260	if (fvco < 2320000) {
    261		vco_sel = 0;
    262		regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
    263	} else if (fvco < 3080000) {
    264		vco_sel = 1;
    265		regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
    266		regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
    267	} else {
    268		vco_sel = 2;
    269		regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
    270		regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2;
    271	}
    272
    273	/* Fix for low freqs */
    274	if (freq < 45000) {
    275		regs[FC11_REG_FA] = 0x6;
    276		regs[FC11_REG_FP] = 0x11;
    277	}
    278
    279	/* Clock out fix */
    280	regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_CLKOUT;
    281
    282	/* Write the cached registers */
    283	for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++) {
    284		err = fc0011_writereg(priv, i, regs[i]);
    285		if (err)
    286			return err;
    287	}
    288
    289	/* VCO calibration */
    290	err = fc0011_vcocal_trigger(priv);
    291	if (err)
    292		return err;
    293	err = fc0011_vcocal_read(priv, &vco_cal);
    294	if (err)
    295		return err;
    296	vco_retries = 0;
    297	while (!(vco_cal & FC11_VCOCAL_OK) && vco_retries < 3) {
    298		/* Reset the tuner and try again */
    299		err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
    300				   FC0011_FE_CALLBACK_RESET, priv->addr);
    301		if (err) {
    302			dev_err(&priv->i2c->dev, "Failed to reset tuner\n");
    303			return err;
    304		}
    305		/* Reinit tuner config */
    306		err = 0;
    307		for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++)
    308			err |= fc0011_writereg(priv, i, regs[i]);
    309		err |= fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]);
    310		err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]);
    311		err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]);
    312		err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]);
    313		err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]);
    314		if (err)
    315			return -EIO;
    316		/* VCO calibration */
    317		err = fc0011_vcocal_trigger(priv);
    318		if (err)
    319			return err;
    320		err = fc0011_vcocal_read(priv, &vco_cal);
    321		if (err)
    322			return err;
    323		vco_retries++;
    324	}
    325	if (!(vco_cal & FC11_VCOCAL_OK)) {
    326		dev_err(&priv->i2c->dev,
    327			"Failed to read VCO calibration value (got %02X)\n",
    328			(unsigned int)vco_cal);
    329		return -EIO;
    330	}
    331	vco_cal &= FC11_VCOCAL_VALUEMASK;
    332
    333	switch (vco_sel) {
    334	default:
    335		WARN_ON(1);
    336		return -EINVAL;
    337	case 0:
    338		if (vco_cal < 8) {
    339			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
    340			regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
    341			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
    342					      regs[FC11_REG_VCOSEL]);
    343			if (err)
    344				return err;
    345			err = fc0011_vcocal_trigger(priv);
    346			if (err)
    347				return err;
    348		} else {
    349			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
    350			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
    351					      regs[FC11_REG_VCOSEL]);
    352			if (err)
    353				return err;
    354		}
    355		break;
    356	case 1:
    357		if (vco_cal < 5) {
    358			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
    359			regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2;
    360			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
    361					      regs[FC11_REG_VCOSEL]);
    362			if (err)
    363				return err;
    364			err = fc0011_vcocal_trigger(priv);
    365			if (err)
    366				return err;
    367		} else if (vco_cal <= 48) {
    368			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
    369			regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
    370			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
    371					      regs[FC11_REG_VCOSEL]);
    372			if (err)
    373				return err;
    374		} else {
    375			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
    376			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
    377					      regs[FC11_REG_VCOSEL]);
    378			if (err)
    379				return err;
    380			err = fc0011_vcocal_trigger(priv);
    381			if (err)
    382				return err;
    383		}
    384		break;
    385	case 2:
    386		if (vco_cal > 53) {
    387			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
    388			regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
    389			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
    390					      regs[FC11_REG_VCOSEL]);
    391			if (err)
    392				return err;
    393			err = fc0011_vcocal_trigger(priv);
    394			if (err)
    395				return err;
    396		} else {
    397			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
    398			regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2;
    399			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
    400					      regs[FC11_REG_VCOSEL]);
    401			if (err)
    402				return err;
    403		}
    404		break;
    405	}
    406	err = fc0011_vcocal_read(priv, NULL);
    407	if (err)
    408		return err;
    409	usleep_range(10000, 50000);
    410
    411	err = fc0011_readreg(priv, FC11_REG_RCCAL, &regs[FC11_REG_RCCAL]);
    412	if (err)
    413		return err;
    414	regs[FC11_REG_RCCAL] |= FC11_RCCAL_FORCE;
    415	err = fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]);
    416	if (err)
    417		return err;
    418	regs[FC11_REG_16] = 0xB;
    419	err = fc0011_writereg(priv, FC11_REG_16, regs[FC11_REG_16]);
    420	if (err)
    421		return err;
    422
    423	dev_dbg(&priv->i2c->dev, "Tuned to fa=%02X fp=%02X xin=%02X%02X vco=%02X vcosel=%02X vcocal=%02X(%u) bw=%u\n",
    424		(unsigned int)regs[FC11_REG_FA],
    425		(unsigned int)regs[FC11_REG_FP],
    426		(unsigned int)regs[FC11_REG_XINHI],
    427		(unsigned int)regs[FC11_REG_XINLO],
    428		(unsigned int)regs[FC11_REG_VCO],
    429		(unsigned int)regs[FC11_REG_VCOSEL],
    430		(unsigned int)vco_cal, vco_retries,
    431		(unsigned int)bandwidth);
    432
    433	priv->frequency = p->frequency;
    434	priv->bandwidth = p->bandwidth_hz;
    435
    436	return 0;
    437}
    438
    439static int fc0011_get_frequency(struct dvb_frontend *fe, u32 *frequency)
    440{
    441	struct fc0011_priv *priv = fe->tuner_priv;
    442
    443	*frequency = priv->frequency;
    444
    445	return 0;
    446}
    447
    448static int fc0011_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
    449{
    450	*frequency = 0;
    451
    452	return 0;
    453}
    454
    455static int fc0011_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
    456{
    457	struct fc0011_priv *priv = fe->tuner_priv;
    458
    459	*bandwidth = priv->bandwidth;
    460
    461	return 0;
    462}
    463
    464static const struct dvb_tuner_ops fc0011_tuner_ops = {
    465	.info = {
    466		.name		  = "Fitipower FC0011",
    467
    468		.frequency_min_hz =   45 * MHz,
    469		.frequency_max_hz = 1000 * MHz,
    470	},
    471
    472	.release		= fc0011_release,
    473	.init			= fc0011_init,
    474
    475	.set_params		= fc0011_set_params,
    476
    477	.get_frequency		= fc0011_get_frequency,
    478	.get_if_frequency	= fc0011_get_if_frequency,
    479	.get_bandwidth		= fc0011_get_bandwidth,
    480};
    481
    482struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe,
    483				   struct i2c_adapter *i2c,
    484				   const struct fc0011_config *config)
    485{
    486	struct fc0011_priv *priv;
    487
    488	priv = kzalloc(sizeof(struct fc0011_priv), GFP_KERNEL);
    489	if (!priv)
    490		return NULL;
    491
    492	priv->i2c = i2c;
    493	priv->addr = config->i2c_address;
    494
    495	fe->tuner_priv = priv;
    496	fe->ops.tuner_ops = fc0011_tuner_ops;
    497
    498	dev_info(&priv->i2c->dev, "Fitipower FC0011 tuner attached\n");
    499
    500	return fe;
    501}
    502EXPORT_SYMBOL(fc0011_attach);
    503
    504MODULE_DESCRIPTION("Fitipower FC0011 silicon tuner driver");
    505MODULE_AUTHOR("Michael Buesch <m@bues.ch>");
    506MODULE_LICENSE("GPL");