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

cx24113.c (14042B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  Driver for Conexant CX24113/CX24128 Tuner (Satellite)
      4 *
      5 *  Copyright (C) 2007-8 Patrick Boettcher <pb@linuxtv.org>
      6 *
      7 *  Developed for BBTI / Technisat
      8 */
      9
     10#include <linux/slab.h>
     11#include <linux/kernel.h>
     12#include <linux/module.h>
     13#include <linux/init.h>
     14
     15#include <media/dvb_frontend.h>
     16#include "cx24113.h"
     17
     18static int debug;
     19
     20#define cx_info(args...) do { printk(KERN_INFO "CX24113: " args); } while (0)
     21#define cx_err(args...)  do { printk(KERN_ERR  "CX24113: " args); } while (0)
     22
     23#define dprintk(args...) \
     24	do { \
     25		if (debug) { \
     26			printk(KERN_DEBUG "CX24113: %s: ", __func__); \
     27			printk(args); \
     28		} \
     29	} while (0)
     30
     31struct cx24113_state {
     32	struct i2c_adapter *i2c;
     33	const struct cx24113_config *config;
     34
     35#define REV_CX24113 0x23
     36	u8 rev;
     37	u8 ver;
     38
     39	u8 icp_mode:1;
     40
     41#define ICP_LEVEL1 0
     42#define ICP_LEVEL2 1
     43#define ICP_LEVEL3 2
     44#define ICP_LEVEL4 3
     45	u8 icp_man:2;
     46	u8 icp_auto_low:2;
     47	u8 icp_auto_mlow:2;
     48	u8 icp_auto_mhi:2;
     49	u8 icp_auto_hi:2;
     50	u8 icp_dig;
     51
     52#define LNA_MIN_GAIN 0
     53#define LNA_MID_GAIN 1
     54#define LNA_MAX_GAIN 2
     55	u8 lna_gain:2;
     56
     57	u8 acp_on:1;
     58
     59	u8 vco_mode:2;
     60	u8 vco_shift:1;
     61#define VCOBANDSEL_6 0x80
     62#define VCOBANDSEL_5 0x01
     63#define VCOBANDSEL_4 0x02
     64#define VCOBANDSEL_3 0x04
     65#define VCOBANDSEL_2 0x08
     66#define VCOBANDSEL_1 0x10
     67	u8 vco_band;
     68
     69#define VCODIV4 4
     70#define VCODIV2 2
     71	u8 vcodiv;
     72
     73	u8 bs_delay:4;
     74	u16 bs_freqcnt:13;
     75	u16 bs_rdiv;
     76	u8 prescaler_mode:1;
     77
     78	u8 rfvga_bias_ctrl;
     79
     80	s16 tuner_gain_thres;
     81	u8  gain_level;
     82
     83	u32 frequency;
     84
     85	u8 refdiv;
     86
     87	u8 Fwindow_enabled;
     88};
     89
     90static int cx24113_writereg(struct cx24113_state *state, int reg, int data)
     91{
     92	u8 buf[] = { reg, data };
     93	struct i2c_msg msg = { .addr = state->config->i2c_addr,
     94		.flags = 0, .buf = buf, .len = 2 };
     95	int err = i2c_transfer(state->i2c, &msg, 1);
     96	if (err != 1) {
     97		printk(KERN_DEBUG "%s: writereg error(err == %i, reg == 0x%02x, data == 0x%02x)\n",
     98		       __func__, err, reg, data);
     99		return err;
    100	}
    101
    102	return 0;
    103}
    104
    105static int cx24113_readreg(struct cx24113_state *state, u8 reg)
    106{
    107	int ret;
    108	u8 b;
    109	struct i2c_msg msg[] = {
    110		{ .addr = state->config->i2c_addr,
    111			.flags = 0, .buf = &reg, .len = 1 },
    112		{ .addr = state->config->i2c_addr,
    113			.flags = I2C_M_RD, .buf = &b, .len = 1 }
    114	};
    115
    116	ret = i2c_transfer(state->i2c, msg, 2);
    117
    118	if (ret != 2) {
    119		printk(KERN_DEBUG "%s: reg=0x%x (error=%d)\n",
    120			__func__, reg, ret);
    121		return ret;
    122	}
    123
    124	return b;
    125}
    126
    127static void cx24113_set_parameters(struct cx24113_state *state)
    128{
    129	u8 r;
    130
    131	r = cx24113_readreg(state, 0x10) & 0x82;
    132	r |= state->icp_mode;
    133	r |= state->icp_man << 4;
    134	r |= state->icp_dig << 2;
    135	r |= state->prescaler_mode << 5;
    136	cx24113_writereg(state, 0x10, r);
    137
    138	r = (state->icp_auto_low  << 0) | (state->icp_auto_mlow << 2)
    139		| (state->icp_auto_mhi << 4) | (state->icp_auto_hi << 6);
    140	cx24113_writereg(state, 0x11, r);
    141
    142	if (state->rev == REV_CX24113) {
    143		r = cx24113_readreg(state, 0x20) & 0xec;
    144		r |= state->lna_gain;
    145		r |= state->rfvga_bias_ctrl << 4;
    146		cx24113_writereg(state, 0x20, r);
    147	}
    148
    149	r = cx24113_readreg(state, 0x12) & 0x03;
    150	r |= state->acp_on << 2;
    151	r |= state->bs_delay << 4;
    152	cx24113_writereg(state, 0x12, r);
    153
    154	r = cx24113_readreg(state, 0x18) & 0x40;
    155	r |= state->vco_shift;
    156	if (state->vco_band == VCOBANDSEL_6)
    157		r |= (1 << 7);
    158	else
    159		r |= (state->vco_band << 1);
    160	cx24113_writereg(state, 0x18, r);
    161
    162	r  = cx24113_readreg(state, 0x14) & 0x20;
    163	r |= (state->vco_mode << 6) | ((state->bs_freqcnt >> 8) & 0x1f);
    164	cx24113_writereg(state, 0x14, r);
    165	cx24113_writereg(state, 0x15, (state->bs_freqcnt        & 0xff));
    166
    167	cx24113_writereg(state, 0x16, (state->bs_rdiv >> 4) & 0xff);
    168	r = (cx24113_readreg(state, 0x17) & 0x0f) |
    169		((state->bs_rdiv & 0x0f) << 4);
    170	cx24113_writereg(state, 0x17, r);
    171}
    172
    173#define VGA_0 0x00
    174#define VGA_1 0x04
    175#define VGA_2 0x02
    176#define VGA_3 0x06
    177#define VGA_4 0x01
    178#define VGA_5 0x05
    179#define VGA_6 0x03
    180#define VGA_7 0x07
    181
    182#define RFVGA_0 0x00
    183#define RFVGA_1 0x01
    184#define RFVGA_2 0x02
    185#define RFVGA_3 0x03
    186
    187static int cx24113_set_gain_settings(struct cx24113_state *state,
    188		s16 power_estimation)
    189{
    190	u8 ampout = cx24113_readreg(state, 0x1d) & 0xf0,
    191	   vga    = cx24113_readreg(state, 0x1f) & 0x3f,
    192	   rfvga  = cx24113_readreg(state, 0x20) & 0xf3;
    193	u8 gain_level = power_estimation >= state->tuner_gain_thres;
    194
    195	dprintk("power estimation: %d, thres: %d, gain_level: %d/%d\n",
    196			power_estimation, state->tuner_gain_thres,
    197			state->gain_level, gain_level);
    198
    199	if (gain_level == state->gain_level)
    200		return 0; /* nothing to be done */
    201
    202	ampout |= 0xf;
    203
    204	if (gain_level) {
    205		rfvga |= RFVGA_0 << 2;
    206		vga   |= (VGA_7 << 3) | VGA_7;
    207	} else {
    208		rfvga |= RFVGA_2 << 2;
    209		vga  |= (VGA_6 << 3) | VGA_2;
    210	}
    211	state->gain_level = gain_level;
    212
    213	cx24113_writereg(state, 0x1d, ampout);
    214	cx24113_writereg(state, 0x1f, vga);
    215	cx24113_writereg(state, 0x20, rfvga);
    216
    217	return 1; /* did something */
    218}
    219
    220static int cx24113_set_Fref(struct cx24113_state *state, u8 high)
    221{
    222	u8 xtal = cx24113_readreg(state, 0x02);
    223	if (state->rev == 0x43 && state->vcodiv == VCODIV4)
    224		high = 1;
    225
    226	xtal &= ~0x2;
    227	if (high)
    228		xtal |= high << 1;
    229	return cx24113_writereg(state, 0x02, xtal);
    230}
    231
    232static int cx24113_enable(struct cx24113_state *state, u8 enable)
    233{
    234	u8 r21 = (cx24113_readreg(state, 0x21) & 0xc0) | enable;
    235	if (state->rev == REV_CX24113)
    236		r21 |= (1 << 1);
    237	return cx24113_writereg(state, 0x21, r21);
    238}
    239
    240static int cx24113_set_bandwidth(struct cx24113_state *state, u32 bandwidth_khz)
    241{
    242	u8 r;
    243
    244	if (bandwidth_khz <= 19000)
    245		r = 0x03 << 6;
    246	else if (bandwidth_khz <= 25000)
    247		r = 0x02 << 6;
    248	else
    249		r = 0x01 << 6;
    250
    251	dprintk("bandwidth to be set: %d\n", bandwidth_khz);
    252	bandwidth_khz *= 10;
    253	bandwidth_khz -= 10000;
    254	bandwidth_khz /= 1000;
    255	bandwidth_khz += 5;
    256	bandwidth_khz /= 10;
    257
    258	dprintk("bandwidth: %d %d\n", r >> 6, bandwidth_khz);
    259
    260	r |= bandwidth_khz & 0x3f;
    261
    262	return cx24113_writereg(state, 0x1e, r);
    263}
    264
    265static int cx24113_set_clk_inversion(struct cx24113_state *state, u8 on)
    266{
    267	u8 r = (cx24113_readreg(state, 0x10) & 0x7f) | ((on & 0x1) << 7);
    268	return cx24113_writereg(state, 0x10, r);
    269}
    270
    271static int cx24113_get_status(struct dvb_frontend *fe, u32 *status)
    272{
    273	struct cx24113_state *state = fe->tuner_priv;
    274	u8 r = (cx24113_readreg(state, 0x10) & 0x02) >> 1;
    275	if (r)
    276		*status |= TUNER_STATUS_LOCKED;
    277	dprintk("PLL locked: %d\n", r);
    278	return 0;
    279}
    280
    281static u8 cx24113_set_ref_div(struct cx24113_state *state, u8 refdiv)
    282{
    283	if (state->rev == 0x43 && state->vcodiv == VCODIV4)
    284		refdiv = 2;
    285	return state->refdiv = refdiv;
    286}
    287
    288static void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f)
    289{
    290	s32 N;
    291	s64 F;
    292	u64 dividend;
    293	u8 R, r;
    294	u8 vcodiv;
    295	u8 factor;
    296	s32 freq_hz = state->frequency * 1000;
    297
    298	if (state->config->xtal_khz < 20000)
    299		factor = 1;
    300	else
    301		factor = 2;
    302
    303	if (state->rev == REV_CX24113) {
    304		if (state->frequency >= 1100000)
    305			vcodiv = VCODIV2;
    306		else
    307			vcodiv = VCODIV4;
    308	} else {
    309		if (state->frequency >= 1165000)
    310			vcodiv = VCODIV2;
    311		else
    312			vcodiv = VCODIV4;
    313	}
    314	state->vcodiv = vcodiv;
    315
    316	dprintk("calculating N/F for %dHz with vcodiv %d\n", freq_hz, vcodiv);
    317	R = 0;
    318	do {
    319		R = cx24113_set_ref_div(state, R + 1);
    320
    321		/* calculate tuner PLL settings: */
    322		N =  (freq_hz / 100 * vcodiv) * R;
    323		N /= (state->config->xtal_khz) * factor * 2;
    324		N += 5;     /* For round up. */
    325		N /= 10;
    326		N -= 32;
    327	} while (N < 6 && R < 3);
    328
    329	if (N < 6) {
    330		cx_err("strange frequency: N < 6\n");
    331		return;
    332	}
    333	F = freq_hz;
    334	F *= (u64) (R * vcodiv * 262144);
    335	dprintk("1 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
    336	/* do_div needs an u64 as first argument */
    337	dividend = F;
    338	do_div(dividend, state->config->xtal_khz * 1000 * factor * 2);
    339	F = dividend;
    340	dprintk("2 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
    341	F -= (N + 32) * 262144;
    342
    343	dprintk("3 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
    344
    345	if (state->Fwindow_enabled) {
    346		if (F > (262144 / 2 - 1638))
    347			F = 262144 / 2 - 1638;
    348		if (F < (-262144 / 2 + 1638))
    349			F = -262144 / 2 + 1638;
    350		if ((F < 3277 && F > 0) || (F > -3277 && F < 0)) {
    351			F = 0;
    352			r = cx24113_readreg(state, 0x10);
    353			cx24113_writereg(state, 0x10, r | (1 << 6));
    354		}
    355	}
    356	dprintk("4 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
    357
    358	*n = (u16) N;
    359	*f = (s32) F;
    360}
    361
    362
    363static void cx24113_set_nfr(struct cx24113_state *state, u16 n, s32 f, u8 r)
    364{
    365	u8 reg;
    366	cx24113_writereg(state, 0x19, (n >> 1) & 0xff);
    367
    368	reg = ((n & 0x1) << 7) | ((f >> 11) & 0x7f);
    369	cx24113_writereg(state, 0x1a, reg);
    370
    371	cx24113_writereg(state, 0x1b, (f >> 3) & 0xff);
    372
    373	reg = cx24113_readreg(state, 0x1c) & 0x1f;
    374	cx24113_writereg(state, 0x1c, reg | ((f & 0x7) << 5));
    375
    376	cx24113_set_Fref(state, r - 1);
    377}
    378
    379static int cx24113_set_frequency(struct cx24113_state *state, u32 frequency)
    380{
    381	u8 r;
    382	u16 n = 6;
    383	s32 f = 0;
    384
    385	r = cx24113_readreg(state, 0x14);
    386	cx24113_writereg(state, 0x14, r & 0x3f);
    387
    388	r = cx24113_readreg(state, 0x10);
    389	cx24113_writereg(state, 0x10, r & 0xbf);
    390
    391	state->frequency = frequency;
    392
    393	dprintk("tuning to frequency: %d\n", frequency);
    394
    395	cx24113_calc_pll_nf(state, &n, &f);
    396	cx24113_set_nfr(state, n, f, state->refdiv);
    397
    398	r = cx24113_readreg(state, 0x18) & 0xbf;
    399	if (state->vcodiv != VCODIV2)
    400		r |= 1 << 6;
    401	cx24113_writereg(state, 0x18, r);
    402
    403	/* The need for this sleep is not clear. But helps in some cases */
    404	msleep(5);
    405
    406	r = cx24113_readreg(state, 0x1c) & 0xef;
    407	cx24113_writereg(state, 0x1c, r | (1 << 4));
    408	return 0;
    409}
    410
    411static int cx24113_init(struct dvb_frontend *fe)
    412{
    413	struct cx24113_state *state = fe->tuner_priv;
    414	int ret;
    415
    416	state->tuner_gain_thres = -50;
    417	state->gain_level = 255; /* to force a gain-setting initialization */
    418	state->icp_mode = 0;
    419
    420	if (state->config->xtal_khz < 11000) {
    421		state->icp_auto_hi  = ICP_LEVEL4;
    422		state->icp_auto_mhi  = ICP_LEVEL4;
    423		state->icp_auto_mlow = ICP_LEVEL3;
    424		state->icp_auto_low = ICP_LEVEL3;
    425	} else {
    426		state->icp_auto_hi  = ICP_LEVEL4;
    427		state->icp_auto_mhi  = ICP_LEVEL4;
    428		state->icp_auto_mlow = ICP_LEVEL3;
    429		state->icp_auto_low = ICP_LEVEL2;
    430	}
    431
    432	state->icp_dig = ICP_LEVEL3;
    433	state->icp_man = ICP_LEVEL1;
    434	state->acp_on  = 1;
    435	state->vco_mode = 0;
    436	state->vco_shift = 0;
    437	state->vco_band = VCOBANDSEL_1;
    438	state->bs_delay = 8;
    439	state->bs_freqcnt = 0x0fff;
    440	state->bs_rdiv = 0x0fff;
    441	state->prescaler_mode = 0;
    442	state->lna_gain = LNA_MAX_GAIN;
    443	state->rfvga_bias_ctrl = 1;
    444	state->Fwindow_enabled = 1;
    445
    446	cx24113_set_Fref(state, 0);
    447	cx24113_enable(state, 0x3d);
    448	cx24113_set_parameters(state);
    449
    450	cx24113_set_gain_settings(state, -30);
    451
    452	cx24113_set_bandwidth(state, 18025);
    453	cx24113_set_clk_inversion(state, 1);
    454
    455	if (state->config->xtal_khz >= 40000)
    456		ret = cx24113_writereg(state, 0x02,
    457			(cx24113_readreg(state, 0x02) & 0xfb) | (1 << 2));
    458	else
    459		ret = cx24113_writereg(state, 0x02,
    460			(cx24113_readreg(state, 0x02) & 0xfb) | (0 << 2));
    461
    462	return ret;
    463}
    464
    465static int cx24113_set_params(struct dvb_frontend *fe)
    466{
    467	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
    468	struct cx24113_state *state = fe->tuner_priv;
    469	/* for a ROLL-OFF factor of 0.35, 0.2: 600, 0.25: 625 */
    470	u32 roll_off = 675;
    471	u32 bw;
    472
    473	bw  = ((c->symbol_rate/100) * roll_off) / 1000;
    474	bw += (10000000/100) + 5;
    475	bw /= 10;
    476	bw += 1000;
    477	cx24113_set_bandwidth(state, bw);
    478
    479	cx24113_set_frequency(state, c->frequency);
    480	msleep(5);
    481	return cx24113_get_status(fe, &bw);
    482}
    483
    484static s8 cx24113_agc_table[2][10] = {
    485	{-54, -41, -35, -30, -25, -21, -16, -10,  -6,  -2},
    486	{-39, -35, -30, -25, -19, -15, -11,  -5,   1,   9},
    487};
    488
    489void cx24113_agc_callback(struct dvb_frontend *fe)
    490{
    491	struct cx24113_state *state = fe->tuner_priv;
    492	s16 s, i;
    493	if (!fe->ops.read_signal_strength)
    494		return;
    495
    496	do {
    497		/* this only works with the current CX24123 implementation */
    498		fe->ops.read_signal_strength(fe, (u16 *) &s);
    499		s >>= 8;
    500		dprintk("signal strength: %d\n", s);
    501		for (i = 0; i < sizeof(cx24113_agc_table[0]); i++)
    502			if (cx24113_agc_table[state->gain_level][i] > s)
    503				break;
    504		s = -25 - i*5;
    505	} while (cx24113_set_gain_settings(state, s));
    506}
    507EXPORT_SYMBOL(cx24113_agc_callback);
    508
    509static int cx24113_get_frequency(struct dvb_frontend *fe, u32 *frequency)
    510{
    511	struct cx24113_state *state = fe->tuner_priv;
    512	*frequency = state->frequency;
    513	return 0;
    514}
    515
    516static void cx24113_release(struct dvb_frontend *fe)
    517{
    518	struct cx24113_state *state = fe->tuner_priv;
    519	dprintk("\n");
    520	fe->tuner_priv = NULL;
    521	kfree(state);
    522}
    523
    524static const struct dvb_tuner_ops cx24113_tuner_ops = {
    525	.info = {
    526		.name              = "Conexant CX24113",
    527		.frequency_min_hz  =  950 * MHz,
    528		.frequency_max_hz  = 2150 * MHz,
    529		.frequency_step_hz =  125 * kHz,
    530	},
    531
    532	.release       = cx24113_release,
    533
    534	.init          = cx24113_init,
    535
    536	.set_params    = cx24113_set_params,
    537	.get_frequency = cx24113_get_frequency,
    538	.get_status    = cx24113_get_status,
    539};
    540
    541struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe,
    542		const struct cx24113_config *config, struct i2c_adapter *i2c)
    543{
    544	/* allocate memory for the internal state */
    545	struct cx24113_state *state = kzalloc(sizeof(*state), GFP_KERNEL);
    546	int rc;
    547
    548	if (!state)
    549		return NULL;
    550
    551	/* setup the state */
    552	state->config = config;
    553	state->i2c = i2c;
    554
    555	cx_info("trying to detect myself\n");
    556
    557	/* making a dummy read, because of some expected troubles
    558	 * after power on */
    559	cx24113_readreg(state, 0x00);
    560
    561	rc = cx24113_readreg(state, 0x00);
    562	if (rc < 0) {
    563		cx_info("CX24113 not found.\n");
    564		goto error;
    565	}
    566	state->rev = rc;
    567
    568	switch (rc) {
    569	case 0x43:
    570		cx_info("detected CX24113 variant\n");
    571		break;
    572	case REV_CX24113:
    573		cx_info("successfully detected\n");
    574		break;
    575	default:
    576		cx_err("unsupported device id: %x\n", state->rev);
    577		goto error;
    578	}
    579	state->ver = cx24113_readreg(state, 0x01);
    580	cx_info("version: %x\n", state->ver);
    581
    582	/* create dvb_frontend */
    583	memcpy(&fe->ops.tuner_ops, &cx24113_tuner_ops,
    584			sizeof(struct dvb_tuner_ops));
    585	fe->tuner_priv = state;
    586	return fe;
    587
    588error:
    589	kfree(state);
    590
    591	return NULL;
    592}
    593EXPORT_SYMBOL(cx24113_attach);
    594
    595module_param(debug, int, 0644);
    596MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
    597
    598MODULE_AUTHOR("Patrick Boettcher <pb@linuxtv.org>");
    599MODULE_DESCRIPTION("DVB Frontend module for Conexant CX24113/CX24128hardware");
    600MODULE_LICENSE("GPL");
    601