| Johannes Stezenbach | 2add87a | 2005-05-16 21:54:10 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III | 
 | 3 |  * | 
 | 4 |  * flexcop-i2c.c - flexcop internal 2Wire bus (I2C) and dvb i2c initialization | 
 | 5 |  * | 
 | 6 |  * see flexcop.c for copyright information. | 
 | 7 |  */ | 
 | 8 | #include "flexcop.h" | 
 | 9 |  | 
 | 10 | #define FC_MAX_I2C_RETRIES 100000 | 
 | 11 |  | 
| Patrick Boettcher | 6394cf5 | 2008-03-29 20:49:57 -0300 | [diff] [blame] | 12 | /* #define DUMP_I2C_MESSAGES */ | 
 | 13 |  | 
| Johannes Stezenbach | bdc7800 | 2005-05-16 21:54:18 -0700 | [diff] [blame] | 14 | static int flexcop_i2c_operation(struct flexcop_device *fc, flexcop_ibi_value *r100) | 
| Johannes Stezenbach | 2add87a | 2005-05-16 21:54:10 -0700 | [diff] [blame] | 15 | { | 
| Johannes Stezenbach | bdc7800 | 2005-05-16 21:54:18 -0700 | [diff] [blame] | 16 | 	int i; | 
| Johannes Stezenbach | 2add87a | 2005-05-16 21:54:10 -0700 | [diff] [blame] | 17 | 	flexcop_ibi_value r; | 
 | 18 |  | 
 | 19 | 	r100->tw_sm_c_100.working_start = 1; | 
 | 20 | 	deb_i2c("r100 before: %08x\n",r100->raw); | 
 | 21 |  | 
 | 22 | 	fc->write_ibi_reg(fc, tw_sm_c_100, ibi_zero); | 
 | 23 | 	fc->write_ibi_reg(fc, tw_sm_c_100, *r100); /* initiating i2c operation */ | 
 | 24 |  | 
 | 25 | 	for (i = 0; i < FC_MAX_I2C_RETRIES; i++) { | 
 | 26 | 		r = fc->read_ibi_reg(fc, tw_sm_c_100); | 
 | 27 |  | 
 | 28 | 		if (!r.tw_sm_c_100.no_base_addr_ack_error) { | 
 | 29 | 			if (r.tw_sm_c_100.st_done) {  /* && !r.tw_sm_c_100.working_start */ | 
 | 30 | 				*r100 = r; | 
 | 31 | 				deb_i2c("i2c success\n"); | 
 | 32 | 				return 0; | 
 | 33 | 			} | 
 | 34 | 		} else { | 
 | 35 | 			deb_i2c("suffering from an i2c ack_error\n"); | 
| Johannes Stezenbach | bdc7800 | 2005-05-16 21:54:18 -0700 | [diff] [blame] | 36 | 			return -EREMOTEIO; | 
| Johannes Stezenbach | 2add87a | 2005-05-16 21:54:10 -0700 | [diff] [blame] | 37 | 		} | 
 | 38 | 	} | 
 | 39 | 	deb_i2c("tried %d times i2c operation, never finished or too many ack errors.\n",i); | 
 | 40 | 	return -EREMOTEIO; | 
 | 41 | } | 
 | 42 |  | 
| Patrick Boettcher | 6394cf5 | 2008-03-29 20:49:57 -0300 | [diff] [blame] | 43 | static int flexcop_i2c_read4(struct flexcop_i2c_adapter *i2c, | 
 | 44 | 	flexcop_ibi_value r100, u8 *buf) | 
| Johannes Stezenbach | 2add87a | 2005-05-16 21:54:10 -0700 | [diff] [blame] | 45 | { | 
 | 46 | 	flexcop_ibi_value r104; | 
 | 47 | 	int len = r100.tw_sm_c_100.total_bytes, /* remember total_bytes is buflen-1 */ | 
 | 48 | 		ret; | 
 | 49 |  | 
| Patrick Boettcher | 6394cf5 | 2008-03-29 20:49:57 -0300 | [diff] [blame] | 50 | 	ret = flexcop_i2c_operation(i2c->fc, &r100); | 
 | 51 | 	if (ret != 0) { | 
| Antti Seppälä | 11c6c7f | 2008-12-01 06:59:37 -0300 | [diff] [blame] | 52 | 		deb_i2c("Retrying operation\n"); | 
 | 53 | 		r100.tw_sm_c_100.no_base_addr_ack_error = i2c->no_base_addr; | 
 | 54 | 		ret = flexcop_i2c_operation(i2c->fc, &r100); | 
 | 55 | 	} | 
 | 56 | 	if (ret != 0) { | 
| Patrick Boettcher | 6394cf5 | 2008-03-29 20:49:57 -0300 | [diff] [blame] | 57 | 		deb_i2c("read failed. %d\n", ret); | 
 | 58 | 		return ret; | 
| Johannes Stezenbach | bdc7800 | 2005-05-16 21:54:18 -0700 | [diff] [blame] | 59 | 	} | 
| Johannes Stezenbach | 2add87a | 2005-05-16 21:54:10 -0700 | [diff] [blame] | 60 |  | 
| Johannes Stezenbach | 2add87a | 2005-05-16 21:54:10 -0700 | [diff] [blame] | 61 | 	buf[0] = r100.tw_sm_c_100.data1_reg; | 
 | 62 |  | 
| Johannes Stezenbach | bdc7800 | 2005-05-16 21:54:18 -0700 | [diff] [blame] | 63 | 	if (len > 0) { | 
| Patrick Boettcher | 6394cf5 | 2008-03-29 20:49:57 -0300 | [diff] [blame] | 64 | 		r104 = i2c->fc->read_ibi_reg(i2c->fc, tw_sm_c_104); | 
 | 65 | 		deb_i2c("read: r100: %08x, r104: %08x\n", r100.raw, r104.raw); | 
| Johannes Stezenbach | bdc7800 | 2005-05-16 21:54:18 -0700 | [diff] [blame] | 66 |  | 
 | 67 | 		/* there is at least one more byte, otherwise we wouldn't be here */ | 
 | 68 | 		buf[1] = r104.tw_sm_c_104.data2_reg; | 
 | 69 | 		if (len > 1) buf[2] = r104.tw_sm_c_104.data3_reg; | 
 | 70 | 		if (len > 2) buf[3] = r104.tw_sm_c_104.data4_reg; | 
 | 71 | 	} | 
| Johannes Stezenbach | 2add87a | 2005-05-16 21:54:10 -0700 | [diff] [blame] | 72 |  | 
 | 73 | 	return 0; | 
 | 74 | } | 
 | 75 |  | 
 | 76 | static int flexcop_i2c_write4(struct flexcop_device *fc, flexcop_ibi_value r100, u8 *buf) | 
 | 77 | { | 
 | 78 | 	flexcop_ibi_value r104; | 
 | 79 | 	int len = r100.tw_sm_c_100.total_bytes; /* remember total_bytes is buflen-1 */ | 
 | 80 | 	r104.raw = 0; | 
 | 81 |  | 
 | 82 | 	/* there is at least one byte, otherwise we wouldn't be here */ | 
 | 83 | 	r100.tw_sm_c_100.data1_reg = buf[0]; | 
 | 84 |  | 
 | 85 | 	r104.tw_sm_c_104.data2_reg = len > 0 ? buf[1] : 0; | 
 | 86 | 	r104.tw_sm_c_104.data3_reg = len > 1 ? buf[2] : 0; | 
 | 87 | 	r104.tw_sm_c_104.data4_reg = len > 2 ? buf[3] : 0; | 
 | 88 |  | 
| Patrick Boettcher | 6394cf5 | 2008-03-29 20:49:57 -0300 | [diff] [blame] | 89 | 	deb_i2c("write: r100: %08x, r104: %08x\n", r100.raw, r104.raw); | 
| Johannes Stezenbach | 2add87a | 2005-05-16 21:54:10 -0700 | [diff] [blame] | 90 |  | 
 | 91 | 	/* write the additional i2c data before doing the actual i2c operation */ | 
| Patrick Boettcher | 6394cf5 | 2008-03-29 20:49:57 -0300 | [diff] [blame] | 92 | 	fc->write_ibi_reg(fc, tw_sm_c_104, r104); | 
 | 93 | 	return flexcop_i2c_operation(fc, &r100); | 
| Johannes Stezenbach | 2add87a | 2005-05-16 21:54:10 -0700 | [diff] [blame] | 94 | } | 
 | 95 |  | 
| Patrick Boettcher | 6394cf5 | 2008-03-29 20:49:57 -0300 | [diff] [blame] | 96 | int flexcop_i2c_request(struct flexcop_i2c_adapter *i2c, | 
 | 97 | 	flexcop_access_op_t op, u8 chipaddr, u8 addr, u8 *buf, u16 len) | 
| Johannes Stezenbach | 2add87a | 2005-05-16 21:54:10 -0700 | [diff] [blame] | 98 | { | 
 | 99 | 	int ret; | 
| Patrick Boettcher | 6394cf5 | 2008-03-29 20:49:57 -0300 | [diff] [blame] | 100 |  | 
 | 101 | #ifdef DUMP_I2C_MESSAGES | 
 | 102 | 	int i; | 
 | 103 | #endif | 
 | 104 |  | 
| Johannes Stezenbach | 2add87a | 2005-05-16 21:54:10 -0700 | [diff] [blame] | 105 | 	u16 bytes_to_transfer; | 
 | 106 | 	flexcop_ibi_value r100; | 
 | 107 |  | 
 | 108 | 	deb_i2c("op = %d\n",op); | 
 | 109 | 	r100.raw = 0; | 
 | 110 | 	r100.tw_sm_c_100.chipaddr = chipaddr; | 
 | 111 | 	r100.tw_sm_c_100.twoWS_rw = op; | 
| Patrick Boettcher | 6394cf5 | 2008-03-29 20:49:57 -0300 | [diff] [blame] | 112 | 	r100.tw_sm_c_100.twoWS_port_reg = i2c->port; | 
 | 113 |  | 
 | 114 | #ifdef DUMP_I2C_MESSAGES | 
 | 115 | 	printk(KERN_DEBUG "%d ", i2c->port); | 
 | 116 | 	if (op == FC_READ) | 
 | 117 | 		printk("rd("); | 
 | 118 | 	else | 
 | 119 | 		printk("wr("); | 
 | 120 |  | 
 | 121 | 	printk("%02x): %02x ", chipaddr, addr); | 
 | 122 | #endif | 
 | 123 |  | 
 | 124 | 	/* in that case addr is the only value -> | 
 | 125 | 	 * we write it twice as baseaddr and val0 | 
 | 126 | 	 * BBTI is doing it like that for ISL6421 at least */ | 
 | 127 | 	if (i2c->no_base_addr && len == 0 && op == FC_WRITE) { | 
 | 128 | 		buf = &addr; | 
 | 129 | 		len = 1; | 
 | 130 | 	} | 
| Johannes Stezenbach | 2add87a | 2005-05-16 21:54:10 -0700 | [diff] [blame] | 131 |  | 
 | 132 | 	while (len != 0) { | 
 | 133 | 		bytes_to_transfer = len > 4 ? 4 : len; | 
 | 134 |  | 
 | 135 | 		r100.tw_sm_c_100.total_bytes = bytes_to_transfer - 1; | 
 | 136 | 		r100.tw_sm_c_100.baseaddr = addr; | 
 | 137 |  | 
 | 138 | 		if (op == FC_READ) | 
| Patrick Boettcher | 6394cf5 | 2008-03-29 20:49:57 -0300 | [diff] [blame] | 139 | 			ret = flexcop_i2c_read4(i2c, r100, buf); | 
| Johannes Stezenbach | 2add87a | 2005-05-16 21:54:10 -0700 | [diff] [blame] | 140 | 		else | 
| Patrick Boettcher | 6394cf5 | 2008-03-29 20:49:57 -0300 | [diff] [blame] | 141 | 			ret = flexcop_i2c_write4(i2c->fc, r100, buf); | 
 | 142 |  | 
 | 143 | #ifdef DUMP_I2C_MESSAGES | 
 | 144 | 		for (i = 0; i < bytes_to_transfer; i++) | 
 | 145 | 			printk("%02x ", buf[i]); | 
 | 146 | #endif | 
| Johannes Stezenbach | 2add87a | 2005-05-16 21:54:10 -0700 | [diff] [blame] | 147 |  | 
 | 148 | 		if (ret < 0) | 
 | 149 | 			return ret; | 
 | 150 |  | 
 | 151 | 		buf  += bytes_to_transfer; | 
 | 152 | 		addr += bytes_to_transfer; | 
 | 153 | 		len  -= bytes_to_transfer; | 
| Patrick Boettcher | 6394cf5 | 2008-03-29 20:49:57 -0300 | [diff] [blame] | 154 | 	} | 
 | 155 |  | 
 | 156 | #ifdef DUMP_I2C_MESSAGES | 
 | 157 | 	printk("\n"); | 
 | 158 | #endif | 
| Johannes Stezenbach | 2add87a | 2005-05-16 21:54:10 -0700 | [diff] [blame] | 159 |  | 
 | 160 | 	return 0; | 
 | 161 | } | 
 | 162 | /* exported for PCI i2c */ | 
 | 163 | EXPORT_SYMBOL(flexcop_i2c_request); | 
 | 164 |  | 
| Johannes Stezenbach | bdc7800 | 2005-05-16 21:54:18 -0700 | [diff] [blame] | 165 | /* master xfer callback for demodulator */ | 
 | 166 | static int flexcop_master_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) | 
 | 167 | { | 
| Patrick Boettcher | 6394cf5 | 2008-03-29 20:49:57 -0300 | [diff] [blame] | 168 | 	struct flexcop_i2c_adapter *i2c = i2c_get_adapdata(i2c_adap); | 
| Johannes Stezenbach | bdc7800 | 2005-05-16 21:54:18 -0700 | [diff] [blame] | 169 | 	int i, ret = 0; | 
 | 170 |  | 
| Trent Piepho | 6175e48 | 2007-08-19 05:05:54 -0300 | [diff] [blame] | 171 | 	/* Some drivers use 1 byte or 0 byte reads as probes, which this | 
 | 172 | 	 * driver doesn't support.  These probes will always fail, so this | 
 | 173 | 	 * hack makes them always succeed.  If one knew how, it would of | 
 | 174 | 	 * course be better to actually do the read.  */ | 
 | 175 | 	if (num == 1 && msgs[0].flags == I2C_M_RD && msgs[0].len <= 1) | 
 | 176 | 		return 1; | 
 | 177 |  | 
| Patrick Boettcher | 6394cf5 | 2008-03-29 20:49:57 -0300 | [diff] [blame] | 178 | 	if (mutex_lock_interruptible(&i2c->fc->i2c_mutex)) | 
| Johannes Stezenbach | bdc7800 | 2005-05-16 21:54:18 -0700 | [diff] [blame] | 179 | 		return -ERESTARTSYS; | 
 | 180 |  | 
| Patrick Boettcher | 6394cf5 | 2008-03-29 20:49:57 -0300 | [diff] [blame] | 181 | 	for (i = 0; i < num; i++) { | 
 | 182 | 		/* reading */ | 
 | 183 | 		if (i+1 < num && (msgs[i+1].flags == I2C_M_RD)) { | 
 | 184 | 			ret = i2c->fc->i2c_request(i2c, FC_READ, msgs[i].addr, | 
 | 185 | 				msgs[i].buf[0], msgs[i+1].buf, msgs[i+1].len); | 
 | 186 | 			i++; /* skip the following message */ | 
 | 187 | 		} else /* writing */ | 
 | 188 | 			ret = i2c->fc->i2c_request(i2c, FC_WRITE, msgs[i].addr, | 
 | 189 | 				msgs[i].buf[0], &msgs[i].buf[1], | 
 | 190 | 				msgs[i].len - 1); | 
 | 191 | 		if (ret < 0) { | 
 | 192 | 			err("i2c master_xfer failed"); | 
| Johannes Stezenbach | bdc7800 | 2005-05-16 21:54:18 -0700 | [diff] [blame] | 193 | 			break; | 
 | 194 | 		} | 
| Johannes Stezenbach | bdc7800 | 2005-05-16 21:54:18 -0700 | [diff] [blame] | 195 | 	} | 
 | 196 |  | 
| Patrick Boettcher | 6394cf5 | 2008-03-29 20:49:57 -0300 | [diff] [blame] | 197 | 	mutex_unlock(&i2c->fc->i2c_mutex); | 
 | 198 |  | 
 | 199 | 	if (ret == 0) | 
| Johannes Stezenbach | bdc7800 | 2005-05-16 21:54:18 -0700 | [diff] [blame] | 200 | 		ret = num; | 
| Johannes Stezenbach | bdc7800 | 2005-05-16 21:54:18 -0700 | [diff] [blame] | 201 | 	return ret; | 
 | 202 | } | 
 | 203 |  | 
| Johannes Stezenbach | 2add87a | 2005-05-16 21:54:10 -0700 | [diff] [blame] | 204 | static u32 flexcop_i2c_func(struct i2c_adapter *adapter) | 
 | 205 | { | 
 | 206 | 	return I2C_FUNC_I2C; | 
 | 207 | } | 
 | 208 |  | 
 | 209 | static struct i2c_algorithm flexcop_algo = { | 
| Johannes Stezenbach | 2add87a | 2005-05-16 21:54:10 -0700 | [diff] [blame] | 210 | 	.master_xfer	= flexcop_master_xfer, | 
 | 211 | 	.functionality	= flexcop_i2c_func, | 
 | 212 | }; | 
 | 213 |  | 
 | 214 | int flexcop_i2c_init(struct flexcop_device *fc) | 
 | 215 | { | 
 | 216 | 	int ret; | 
 | 217 |  | 
| Ingo Molnar | 3593cab | 2006-02-07 06:49:14 -0200 | [diff] [blame] | 218 | 	mutex_init(&fc->i2c_mutex); | 
| Johannes Stezenbach | 2add87a | 2005-05-16 21:54:10 -0700 | [diff] [blame] | 219 |  | 
| Patrick Boettcher | 6394cf5 | 2008-03-29 20:49:57 -0300 | [diff] [blame] | 220 | 	fc->fc_i2c_adap[0].fc = fc; | 
 | 221 | 	fc->fc_i2c_adap[1].fc = fc; | 
 | 222 | 	fc->fc_i2c_adap[2].fc = fc; | 
| Johannes Stezenbach | 2add87a | 2005-05-16 21:54:10 -0700 | [diff] [blame] | 223 |  | 
| Patrick Boettcher | 6394cf5 | 2008-03-29 20:49:57 -0300 | [diff] [blame] | 224 | 	fc->fc_i2c_adap[0].port = FC_I2C_PORT_DEMOD; | 
 | 225 | 	fc->fc_i2c_adap[1].port = FC_I2C_PORT_EEPROM; | 
 | 226 | 	fc->fc_i2c_adap[2].port = FC_I2C_PORT_TUNER; | 
| Johannes Stezenbach | 2add87a | 2005-05-16 21:54:10 -0700 | [diff] [blame] | 227 |  | 
| Jean Delvare | 1d43401 | 2008-09-03 17:12:23 -0300 | [diff] [blame] | 228 | 	strlcpy(fc->fc_i2c_adap[0].i2c_adap.name, "B2C2 FlexCop I2C to demod", | 
 | 229 | 		sizeof(fc->fc_i2c_adap[0].i2c_adap.name)); | 
 | 230 | 	strlcpy(fc->fc_i2c_adap[1].i2c_adap.name, "B2C2 FlexCop I2C to eeprom", | 
 | 231 | 		sizeof(fc->fc_i2c_adap[1].i2c_adap.name)); | 
 | 232 | 	strlcpy(fc->fc_i2c_adap[2].i2c_adap.name, "B2C2 FlexCop I2C to tuner", | 
 | 233 | 		sizeof(fc->fc_i2c_adap[2].i2c_adap.name)); | 
| Johannes Stezenbach | 2add87a | 2005-05-16 21:54:10 -0700 | [diff] [blame] | 234 |  | 
| Patrick Boettcher | 6394cf5 | 2008-03-29 20:49:57 -0300 | [diff] [blame] | 235 | 	i2c_set_adapdata(&fc->fc_i2c_adap[0].i2c_adap, &fc->fc_i2c_adap[0]); | 
 | 236 | 	i2c_set_adapdata(&fc->fc_i2c_adap[1].i2c_adap, &fc->fc_i2c_adap[1]); | 
 | 237 | 	i2c_set_adapdata(&fc->fc_i2c_adap[2].i2c_adap, &fc->fc_i2c_adap[2]); | 
 | 238 |  | 
 | 239 | 	fc->fc_i2c_adap[0].i2c_adap.class = | 
 | 240 | 		fc->fc_i2c_adap[1].i2c_adap.class = | 
 | 241 | 		fc->fc_i2c_adap[2].i2c_adap.class = I2C_CLASS_TV_DIGITAL; | 
 | 242 | 	fc->fc_i2c_adap[0].i2c_adap.algo = | 
 | 243 | 		fc->fc_i2c_adap[1].i2c_adap.algo = | 
 | 244 | 		fc->fc_i2c_adap[2].i2c_adap.algo = &flexcop_algo; | 
 | 245 | 	fc->fc_i2c_adap[0].i2c_adap.algo_data = | 
 | 246 | 		fc->fc_i2c_adap[1].i2c_adap.algo_data = | 
 | 247 | 		fc->fc_i2c_adap[2].i2c_adap.algo_data = NULL; | 
 | 248 | 	fc->fc_i2c_adap[0].i2c_adap.dev.parent = | 
 | 249 | 		fc->fc_i2c_adap[1].i2c_adap.dev.parent = | 
 | 250 | 		fc->fc_i2c_adap[2].i2c_adap.dev.parent = fc->dev; | 
 | 251 |  | 
 | 252 | 	ret = i2c_add_adapter(&fc->fc_i2c_adap[0].i2c_adap); | 
 | 253 | 	if (ret < 0) | 
| Johannes Stezenbach | 2add87a | 2005-05-16 21:54:10 -0700 | [diff] [blame] | 254 | 		return ret; | 
 | 255 |  | 
| Patrick Boettcher | 6394cf5 | 2008-03-29 20:49:57 -0300 | [diff] [blame] | 256 | 	ret = i2c_add_adapter(&fc->fc_i2c_adap[1].i2c_adap); | 
 | 257 | 	if (ret < 0) | 
 | 258 | 		goto adap_1_failed; | 
 | 259 |  | 
 | 260 | 	ret = i2c_add_adapter(&fc->fc_i2c_adap[2].i2c_adap); | 
 | 261 | 	if (ret < 0) | 
 | 262 | 		goto adap_2_failed; | 
 | 263 |  | 
| Johannes Stezenbach | 2add87a | 2005-05-16 21:54:10 -0700 | [diff] [blame] | 264 | 	fc->init_state |= FC_STATE_I2C_INIT; | 
 | 265 | 	return 0; | 
| Patrick Boettcher | 6394cf5 | 2008-03-29 20:49:57 -0300 | [diff] [blame] | 266 |  | 
 | 267 | adap_2_failed: | 
 | 268 | 	i2c_del_adapter(&fc->fc_i2c_adap[1].i2c_adap); | 
 | 269 | adap_1_failed: | 
 | 270 | 	i2c_del_adapter(&fc->fc_i2c_adap[0].i2c_adap); | 
 | 271 |  | 
 | 272 | 	return ret; | 
| Johannes Stezenbach | 2add87a | 2005-05-16 21:54:10 -0700 | [diff] [blame] | 273 | } | 
 | 274 |  | 
 | 275 | void flexcop_i2c_exit(struct flexcop_device *fc) | 
 | 276 | { | 
| Patrick Boettcher | 6394cf5 | 2008-03-29 20:49:57 -0300 | [diff] [blame] | 277 | 	if (fc->init_state & FC_STATE_I2C_INIT) { | 
 | 278 | 		i2c_del_adapter(&fc->fc_i2c_adap[2].i2c_adap); | 
 | 279 | 		i2c_del_adapter(&fc->fc_i2c_adap[1].i2c_adap); | 
 | 280 | 		i2c_del_adapter(&fc->fc_i2c_adap[0].i2c_adap); | 
 | 281 | 	} | 
| Johannes Stezenbach | 2add87a | 2005-05-16 21:54:10 -0700 | [diff] [blame] | 282 |  | 
 | 283 | 	fc->init_state &= ~FC_STATE_I2C_INIT; | 
 | 284 | } |