brintos

brintos / linux-shallow public Read only

0
0
Text · 8.7 KiB · 75adf2a Raw
429 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * CIMaX SP2/SP2HF (Atmel T90FJR) CI driver4 *5 * Copyright (C) 2014 Olli Salonen <olli.salonen@iki.fi>6 *7 * Heavily based on CIMax2(R) SP2 driver in conjunction with NetUp Dual8 * DVB-S2 CI card (cimax2) with following copyrights:9 *10 *  Copyright (C) 2009 NetUP Inc.11 *  Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>12 *  Copyright (C) 2009 Abylay Ospan <aospan@netup.ru>13 */14 15#include "sp2_priv.h"16 17static int sp2_read_i2c(struct sp2 *s, u8 reg, u8 *buf, int len)18{19	int ret;20	struct i2c_client *client = s->client;21	struct i2c_adapter *adap = client->adapter;22	struct i2c_msg msg[] = {23		{24			.addr = client->addr,25			.flags = 0,26			.buf = &reg,27			.len = 128		}, {29			.addr = client->addr,30			.flags	= I2C_M_RD,31			.buf = buf,32			.len = len33		}34	};35 36	ret = i2c_transfer(adap, msg, 2);37 38	if (ret != 2) {39		dev_err(&client->dev, "i2c read error, reg = 0x%02x, status = %d\n",40				reg, ret);41		if (ret < 0)42			return ret;43		else44			return -EIO;45	}46 47	dev_dbg(&s->client->dev, "addr=0x%04x, reg = 0x%02x, data = %02x\n",48				client->addr, reg, buf[0]);49 50	return 0;51}52 53static int sp2_write_i2c(struct sp2 *s, u8 reg, u8 *buf, int len)54{55	int ret;56	u8 buffer[35];57	struct i2c_client *client = s->client;58	struct i2c_adapter *adap = client->adapter;59	struct i2c_msg msg = {60		.addr = client->addr,61		.flags = 0,62		.buf = &buffer[0],63		.len = len + 164	};65 66	if ((len + 1) > sizeof(buffer)) {67		dev_err(&client->dev, "i2c wr reg=%02x: len=%d is too big!\n",68				reg, len);69		return -EINVAL;70	}71 72	buffer[0] = reg;73	memcpy(&buffer[1], buf, len);74 75	ret = i2c_transfer(adap, &msg, 1);76 77	if (ret != 1) {78		dev_err(&client->dev, "i2c write error, reg = 0x%02x, status = %d\n",79				reg, ret);80		if (ret < 0)81			return ret;82		else83			return -EIO;84	}85 86	dev_dbg(&s->client->dev, "addr=0x%04x, reg = 0x%02x, data = %*ph\n",87				client->addr, reg, len, buf);88 89	return 0;90}91 92static int sp2_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot, u8 acs,93			u8 read, int addr, u8 data)94{95	struct sp2 *s = en50221->data;96	u8 store;97	int mem, ret;98	int (*ci_op_cam)(void*, u8, int, u8, int*) = s->ci_control;99 100	if (slot != 0)101		return -EINVAL;102 103	/*104	 * change module access type between IO space and attribute memory105	 * when needed106	 */107	if (s->module_access_type != acs) {108		ret = sp2_read_i2c(s, 0x00, &store, 1);109 110		if (ret)111			return ret;112 113		store &= ~(SP2_MOD_CTL_ACS1 | SP2_MOD_CTL_ACS0);114		store |= acs;115 116		ret = sp2_write_i2c(s, 0x00, &store, 1);117		if (ret)118			return ret;119	}120 121	s->module_access_type = acs;122 123	/* implementation of ci_op_cam is device specific */124	if (ci_op_cam) {125		ret = ci_op_cam(s->priv, read, addr, data, &mem);126	} else {127		dev_err(&s->client->dev, "callback not defined");128		return -EINVAL;129	}130 131	if (ret)132		return ret;133 134	dev_dbg(&s->client->dev, "%s: slot=%d, addr=0x%04x, %s, data=%x",135			(read) ? "read" : "write", slot, addr,136			(acs == SP2_CI_ATTR_ACS) ? "attr" : "io",137			(read) ? mem : data);138 139	if (read)140		return mem;141	else142		return 0;143 144}145 146int sp2_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221,147				int slot, int addr)148{149	return sp2_ci_op_cam(en50221, slot, SP2_CI_ATTR_ACS,150			SP2_CI_RD, addr, 0);151}152 153int sp2_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221,154				int slot, int addr, u8 data)155{156	return sp2_ci_op_cam(en50221, slot, SP2_CI_ATTR_ACS,157			SP2_CI_WR, addr, data);158}159 160int sp2_ci_read_cam_control(struct dvb_ca_en50221 *en50221,161				int slot, u8 addr)162{163	return sp2_ci_op_cam(en50221, slot, SP2_CI_IO_ACS,164			SP2_CI_RD, addr, 0);165}166 167int sp2_ci_write_cam_control(struct dvb_ca_en50221 *en50221,168				int slot, u8 addr, u8 data)169{170	return sp2_ci_op_cam(en50221, slot, SP2_CI_IO_ACS,171			SP2_CI_WR, addr, data);172}173 174int sp2_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot)175{176	struct sp2 *s = en50221->data;177	u8 buf;178	int ret;179 180	dev_dbg(&s->client->dev, "slot: %d\n", slot);181 182	if (slot != 0)183		return -EINVAL;184 185	/* RST on */186	buf = SP2_MOD_CTL_RST;187	ret = sp2_write_i2c(s, 0x00, &buf, 1);188 189	if (ret)190		return ret;191 192	usleep_range(500, 600);193 194	/* RST off */195	buf = 0x00;196	ret = sp2_write_i2c(s, 0x00, &buf, 1);197 198	if (ret)199		return ret;200 201	msleep(1000);202 203	return 0;204}205 206int sp2_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot)207{208	struct sp2 *s = en50221->data;209 210	dev_dbg(&s->client->dev, "slot:%d\n", slot);211 212	/* not implemented */213	return 0;214}215 216int sp2_ci_slot_ts_enable(struct dvb_ca_en50221 *en50221, int slot)217{218	struct sp2 *s = en50221->data;219	u8 buf;220 221	dev_dbg(&s->client->dev, "slot:%d\n", slot);222 223	if (slot != 0)224		return -EINVAL;225 226	sp2_read_i2c(s, 0x00, &buf, 1);227 228	/* disable bypass and enable TS */229	buf |= (SP2_MOD_CTL_TSOEN | SP2_MOD_CTL_TSIEN);230	return sp2_write_i2c(s, 0, &buf, 1);231}232 233int sp2_ci_poll_slot_status(struct dvb_ca_en50221 *en50221,234				int slot, int open)235{236	struct sp2 *s = en50221->data;237	u8 buf[2];238	int ret;239 240	dev_dbg(&s->client->dev, "slot:%d open:%d\n", slot, open);241 242	/*243	 * CAM module INSERT/REMOVE processing. Slow operation because of i2c244	 * transfers. Throttle read to one per sec.245	 */246	if (time_after(jiffies, s->next_status_checked_time)) {247		ret = sp2_read_i2c(s, 0x00, buf, 1);248		s->next_status_checked_time = jiffies +	msecs_to_jiffies(1000);249 250		if (ret)251			return 0;252 253		if (buf[0] & SP2_MOD_CTL_DET)254			s->status = DVB_CA_EN50221_POLL_CAM_PRESENT |255					DVB_CA_EN50221_POLL_CAM_READY;256		else257			s->status = 0;258	}259 260	return s->status;261}262 263static int sp2_init(struct sp2 *s)264{265	int ret = 0;266	u8 buf;267	u8 cimax_init[34] = {268		0x00, /* module A control*/269		0x00, /* auto select mask high A */270		0x00, /* auto select mask low A */271		0x00, /* auto select pattern high A */272		0x00, /* auto select pattern low A */273		0x44, /* memory access time A, 600 ns */274		0x00, /* invert input A */275		0x00, /* RFU */276		0x00, /* RFU */277		0x00, /* module B control*/278		0x00, /* auto select mask high B */279		0x00, /* auto select mask low B */280		0x00, /* auto select pattern high B */281		0x00, /* auto select pattern low B */282		0x44, /* memory access time B, 600 ns */283		0x00, /* invert input B */284		0x00, /* RFU */285		0x00, /* RFU */286		0x00, /* auto select mask high Ext */287		0x00, /* auto select mask low Ext */288		0x00, /* auto select pattern high Ext */289		0x00, /* auto select pattern low Ext */290		0x00, /* RFU */291		0x02, /* destination - module A */292		0x01, /* power control reg, VCC power on */293		0x00, /* RFU */294		0x00, /* int status read only */295		0x00, /* Interrupt Mask Register */296		0x05, /* EXTINT=active-high, INT=push-pull */297		0x00, /* USCG1 */298		0x04, /* ack active low */299		0x00, /* LOCK = 0 */300		0x22, /* unknown */301		0x00, /* synchronization? */302	};303 304	dev_dbg(&s->client->dev, "\n");305 306	s->ca.owner = THIS_MODULE;307	s->ca.read_attribute_mem = sp2_ci_read_attribute_mem;308	s->ca.write_attribute_mem = sp2_ci_write_attribute_mem;309	s->ca.read_cam_control = sp2_ci_read_cam_control;310	s->ca.write_cam_control = sp2_ci_write_cam_control;311	s->ca.slot_reset = sp2_ci_slot_reset;312	s->ca.slot_shutdown = sp2_ci_slot_shutdown;313	s->ca.slot_ts_enable = sp2_ci_slot_ts_enable;314	s->ca.poll_slot_status = sp2_ci_poll_slot_status;315	s->ca.data = s;316	s->module_access_type = 0;317 318	/* initialize all regs */319	ret = sp2_write_i2c(s, 0x00, &cimax_init[0], 34);320	if (ret)321		goto err;322 323	/* lock registers */324	buf = 1;325	ret = sp2_write_i2c(s, 0x1f, &buf, 1);326	if (ret)327		goto err;328 329	/* power on slots */330	ret = sp2_write_i2c(s, 0x18, &buf, 1);331	if (ret)332		goto err;333 334	ret = dvb_ca_en50221_init(s->dvb_adap, &s->ca, 0, 1);335	if (ret)336		goto err;337 338	return 0;339 340err:341	dev_dbg(&s->client->dev, "init failed=%d\n", ret);342	return ret;343}344 345static int sp2_exit(struct i2c_client *client)346{347	struct sp2 *s;348 349	dev_dbg(&client->dev, "\n");350 351	if (!client)352		return 0;353 354	s = i2c_get_clientdata(client);355	if (!s)356		return 0;357 358	if (!s->ca.data)359		return 0;360 361	dvb_ca_en50221_release(&s->ca);362 363	return 0;364}365 366static int sp2_probe(struct i2c_client *client)367{368	struct sp2_config *cfg = client->dev.platform_data;369	struct sp2 *s;370	int ret;371 372	dev_dbg(&client->dev, "\n");373 374	s = kzalloc(sizeof(*s), GFP_KERNEL);375	if (!s) {376		ret = -ENOMEM;377		goto err;378	}379 380	s->client = client;381	s->dvb_adap = cfg->dvb_adap;382	s->priv = cfg->priv;383	s->ci_control = cfg->ci_control;384 385	i2c_set_clientdata(client, s);386 387	ret = sp2_init(s);388	if (ret)389		goto err;390 391	dev_info(&s->client->dev, "CIMaX SP2 successfully attached\n");392	return 0;393err:394	dev_dbg(&client->dev, "init failed=%d\n", ret);395	kfree(s);396 397	return ret;398}399 400static void sp2_remove(struct i2c_client *client)401{402	struct sp2 *s = i2c_get_clientdata(client);403 404	dev_dbg(&client->dev, "\n");405	sp2_exit(client);406	kfree(s);407}408 409static const struct i2c_device_id sp2_id[] = {410	{ "sp2" },411	{}412};413MODULE_DEVICE_TABLE(i2c, sp2_id);414 415static struct i2c_driver sp2_driver = {416	.driver = {417		.name	= "sp2",418	},419	.probe		= sp2_probe,420	.remove		= sp2_remove,421	.id_table	= sp2_id,422};423 424module_i2c_driver(sp2_driver);425 426MODULE_DESCRIPTION("CIMaX SP2/HF CI driver");427MODULE_AUTHOR("Olli Salonen <olli.salonen@iki.fi>");428MODULE_LICENSE("GPL");429