mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2025-01-12 20:26:22 +07:00
25d4affb0c
Coverity complains about werid stuff at the debug logic: CID 113542 (#1 of 1): Out-of-bounds access (ARRAY_VS_SINGLETON)10. callee_ptr_arith: Passing buf to function flexcop_i2c_write4 which uses it as an array. This might corrupt or misinterpret adjacent memory locations. Instead of directly addressing the issue there, let's rework at the logic there. On newer kernels, KERN_CONT does nothing, as the previous message won't wait for a continuation. Also, both flexcop_i2c_read4() and flexcop_i2c_write4(), called by it, will print stuff if (debug &4). So, the way it is is too buggy. There are two kinds of debug stuff there: deb_i2c() and a code hidden under #ifdef DUMP_I2C_MESSAGES, with can't be selected without touching the source code. Also, if both debug & 0x4 and DUMP_I2C_MESSAGES, flexcop_i2c_request() will emit two debug messages per call with different data, with sounds messy. Simplify it by getting rid of DUMP_I2C_MESSAGES and adding a new flag to debug (0x40), and making the debug logic there more consistent. Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com> Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
281 lines
7.7 KiB
C
281 lines
7.7 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
|
|
* flexcop-i2c.c - flexcop internal 2Wire bus (I2C) and dvb i2c initialization
|
|
* see flexcop.c for copyright information
|
|
*/
|
|
#include "flexcop.h"
|
|
|
|
#define FC_MAX_I2C_RETRIES 100000
|
|
|
|
static int flexcop_i2c_operation(struct flexcop_device *fc,
|
|
flexcop_ibi_value *r100)
|
|
{
|
|
int i;
|
|
flexcop_ibi_value r;
|
|
|
|
r100->tw_sm_c_100.working_start = 1;
|
|
deb_i2c("r100 before: %08x\n",r100->raw);
|
|
|
|
fc->write_ibi_reg(fc, tw_sm_c_100, ibi_zero);
|
|
fc->write_ibi_reg(fc, tw_sm_c_100, *r100); /* initiating i2c operation */
|
|
|
|
for (i = 0; i < FC_MAX_I2C_RETRIES; i++) {
|
|
r = fc->read_ibi_reg(fc, tw_sm_c_100);
|
|
|
|
if (!r.tw_sm_c_100.no_base_addr_ack_error) {
|
|
if (r.tw_sm_c_100.st_done) {
|
|
*r100 = r;
|
|
deb_i2c("i2c success\n");
|
|
return 0;
|
|
}
|
|
} else {
|
|
deb_i2c("suffering from an i2c ack_error\n");
|
|
return -EREMOTEIO;
|
|
}
|
|
}
|
|
deb_i2c("tried %d times i2c operation, never finished or too many ack errors.\n",
|
|
i);
|
|
return -EREMOTEIO;
|
|
}
|
|
|
|
static int flexcop_i2c_read4(struct flexcop_i2c_adapter *i2c,
|
|
flexcop_ibi_value r100, u8 *buf)
|
|
{
|
|
flexcop_ibi_value r104;
|
|
int len = r100.tw_sm_c_100.total_bytes,
|
|
/* remember total_bytes is buflen-1 */
|
|
ret;
|
|
|
|
/* work-around to have CableStar2 and SkyStar2 rev 2.7 work
|
|
* correctly:
|
|
*
|
|
* the ITD1000 is behind an i2c-gate which closes automatically
|
|
* after an i2c-transaction the STV0297 needs 2 consecutive reads
|
|
* one with no_base_addr = 0 and one with 1
|
|
*
|
|
* those two work-arounds are conflictin: we check for the card
|
|
* type, it is set when probing the ITD1000 */
|
|
if (i2c->fc->dev_type == FC_SKY_REV27)
|
|
r100.tw_sm_c_100.no_base_addr_ack_error = i2c->no_base_addr;
|
|
|
|
ret = flexcop_i2c_operation(i2c->fc, &r100);
|
|
if (ret != 0) {
|
|
deb_i2c("Retrying operation\n");
|
|
r100.tw_sm_c_100.no_base_addr_ack_error = i2c->no_base_addr;
|
|
ret = flexcop_i2c_operation(i2c->fc, &r100);
|
|
}
|
|
if (ret != 0) {
|
|
deb_i2c("read failed. %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
buf[0] = r100.tw_sm_c_100.data1_reg;
|
|
|
|
if (len > 0) {
|
|
r104 = i2c->fc->read_ibi_reg(i2c->fc, tw_sm_c_104);
|
|
deb_i2c("read: r100: %08x, r104: %08x\n", r100.raw, r104.raw);
|
|
|
|
/* there is at least one more byte, otherwise we wouldn't be here */
|
|
buf[1] = r104.tw_sm_c_104.data2_reg;
|
|
if (len > 1) buf[2] = r104.tw_sm_c_104.data3_reg;
|
|
if (len > 2) buf[3] = r104.tw_sm_c_104.data4_reg;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int flexcop_i2c_write4(struct flexcop_device *fc,
|
|
flexcop_ibi_value r100, u8 *buf)
|
|
{
|
|
flexcop_ibi_value r104;
|
|
int len = r100.tw_sm_c_100.total_bytes; /* remember total_bytes is buflen-1 */
|
|
r104.raw = 0;
|
|
|
|
/* there is at least one byte, otherwise we wouldn't be here */
|
|
r100.tw_sm_c_100.data1_reg = buf[0];
|
|
r104.tw_sm_c_104.data2_reg = len > 0 ? buf[1] : 0;
|
|
r104.tw_sm_c_104.data3_reg = len > 1 ? buf[2] : 0;
|
|
r104.tw_sm_c_104.data4_reg = len > 2 ? buf[3] : 0;
|
|
|
|
deb_i2c("write: r100: %08x, r104: %08x\n", r100.raw, r104.raw);
|
|
|
|
/* write the additional i2c data before doing the actual i2c operation */
|
|
fc->write_ibi_reg(fc, tw_sm_c_104, r104);
|
|
return flexcop_i2c_operation(fc, &r100);
|
|
}
|
|
|
|
int flexcop_i2c_request(struct flexcop_i2c_adapter *i2c,
|
|
flexcop_access_op_t op, u8 chipaddr,
|
|
u8 start_addr, u8 *buf, u16 size)
|
|
{
|
|
int ret;
|
|
int len = size;
|
|
u8 *p;
|
|
u8 addr = start_addr;
|
|
|
|
u16 bytes_to_transfer;
|
|
flexcop_ibi_value r100;
|
|
|
|
deb_i2c("port %d %s(%02x): register %02x, size: %d\n",
|
|
i2c->port,
|
|
op == FC_READ ? "rd" : "wr",
|
|
chipaddr, start_addr, size);
|
|
r100.raw = 0;
|
|
r100.tw_sm_c_100.chipaddr = chipaddr;
|
|
r100.tw_sm_c_100.twoWS_rw = op;
|
|
r100.tw_sm_c_100.twoWS_port_reg = i2c->port;
|
|
|
|
/* in that case addr is the only value ->
|
|
* we write it twice as baseaddr and val0
|
|
* BBTI is doing it like that for ISL6421 at least */
|
|
if (i2c->no_base_addr && len == 0 && op == FC_WRITE) {
|
|
buf = &start_addr;
|
|
len = 1;
|
|
}
|
|
|
|
p = buf;
|
|
|
|
while (len != 0) {
|
|
bytes_to_transfer = len > 4 ? 4 : len;
|
|
|
|
r100.tw_sm_c_100.total_bytes = bytes_to_transfer - 1;
|
|
r100.tw_sm_c_100.baseaddr = addr;
|
|
|
|
if (op == FC_READ)
|
|
ret = flexcop_i2c_read4(i2c, r100, p);
|
|
else
|
|
ret = flexcop_i2c_write4(i2c->fc, r100, p);
|
|
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
p += bytes_to_transfer;
|
|
addr += bytes_to_transfer;
|
|
len -= bytes_to_transfer;
|
|
}
|
|
deb_i2c_dump("port %d %s(%02x): register %02x: %*ph\n",
|
|
i2c->port,
|
|
op == FC_READ ? "rd" : "wr",
|
|
chipaddr, start_addr, size, buf);
|
|
|
|
return 0;
|
|
}
|
|
/* exported for PCI i2c */
|
|
EXPORT_SYMBOL(flexcop_i2c_request);
|
|
|
|
/* master xfer callback for demodulator */
|
|
static int flexcop_master_xfer(struct i2c_adapter *i2c_adap,
|
|
struct i2c_msg msgs[], int num)
|
|
{
|
|
struct flexcop_i2c_adapter *i2c = i2c_get_adapdata(i2c_adap);
|
|
int i, ret = 0;
|
|
|
|
/* Some drivers use 1 byte or 0 byte reads as probes, which this
|
|
* driver doesn't support. These probes will always fail, so this
|
|
* hack makes them always succeed. If one knew how, it would of
|
|
* course be better to actually do the read. */
|
|
if (num == 1 && msgs[0].flags == I2C_M_RD && msgs[0].len <= 1)
|
|
return 1;
|
|
|
|
if (mutex_lock_interruptible(&i2c->fc->i2c_mutex))
|
|
return -ERESTARTSYS;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
/* reading */
|
|
if (i+1 < num && (msgs[i+1].flags == I2C_M_RD)) {
|
|
ret = i2c->fc->i2c_request(i2c, FC_READ, msgs[i].addr,
|
|
msgs[i].buf[0], msgs[i+1].buf,
|
|
msgs[i+1].len);
|
|
i++; /* skip the following message */
|
|
} else /* writing */
|
|
ret = i2c->fc->i2c_request(i2c, FC_WRITE, msgs[i].addr,
|
|
msgs[i].buf[0], &msgs[i].buf[1],
|
|
msgs[i].len - 1);
|
|
if (ret < 0) {
|
|
deb_i2c("i2c master_xfer failed");
|
|
break;
|
|
}
|
|
}
|
|
|
|
mutex_unlock(&i2c->fc->i2c_mutex);
|
|
|
|
if (ret == 0)
|
|
ret = num;
|
|
return ret;
|
|
}
|
|
|
|
static u32 flexcop_i2c_func(struct i2c_adapter *adapter)
|
|
{
|
|
return I2C_FUNC_I2C;
|
|
}
|
|
|
|
static struct i2c_algorithm flexcop_algo = {
|
|
.master_xfer = flexcop_master_xfer,
|
|
.functionality = flexcop_i2c_func,
|
|
};
|
|
|
|
int flexcop_i2c_init(struct flexcop_device *fc)
|
|
{
|
|
int ret;
|
|
mutex_init(&fc->i2c_mutex);
|
|
|
|
fc->fc_i2c_adap[0].fc = fc;
|
|
fc->fc_i2c_adap[1].fc = fc;
|
|
fc->fc_i2c_adap[2].fc = fc;
|
|
fc->fc_i2c_adap[0].port = FC_I2C_PORT_DEMOD;
|
|
fc->fc_i2c_adap[1].port = FC_I2C_PORT_EEPROM;
|
|
fc->fc_i2c_adap[2].port = FC_I2C_PORT_TUNER;
|
|
|
|
strlcpy(fc->fc_i2c_adap[0].i2c_adap.name, "B2C2 FlexCop I2C to demod",
|
|
sizeof(fc->fc_i2c_adap[0].i2c_adap.name));
|
|
strlcpy(fc->fc_i2c_adap[1].i2c_adap.name, "B2C2 FlexCop I2C to eeprom",
|
|
sizeof(fc->fc_i2c_adap[1].i2c_adap.name));
|
|
strlcpy(fc->fc_i2c_adap[2].i2c_adap.name, "B2C2 FlexCop I2C to tuner",
|
|
sizeof(fc->fc_i2c_adap[2].i2c_adap.name));
|
|
|
|
i2c_set_adapdata(&fc->fc_i2c_adap[0].i2c_adap, &fc->fc_i2c_adap[0]);
|
|
i2c_set_adapdata(&fc->fc_i2c_adap[1].i2c_adap, &fc->fc_i2c_adap[1]);
|
|
i2c_set_adapdata(&fc->fc_i2c_adap[2].i2c_adap, &fc->fc_i2c_adap[2]);
|
|
|
|
fc->fc_i2c_adap[0].i2c_adap.algo =
|
|
fc->fc_i2c_adap[1].i2c_adap.algo =
|
|
fc->fc_i2c_adap[2].i2c_adap.algo = &flexcop_algo;
|
|
fc->fc_i2c_adap[0].i2c_adap.algo_data =
|
|
fc->fc_i2c_adap[1].i2c_adap.algo_data =
|
|
fc->fc_i2c_adap[2].i2c_adap.algo_data = NULL;
|
|
fc->fc_i2c_adap[0].i2c_adap.dev.parent =
|
|
fc->fc_i2c_adap[1].i2c_adap.dev.parent =
|
|
fc->fc_i2c_adap[2].i2c_adap.dev.parent = fc->dev;
|
|
|
|
ret = i2c_add_adapter(&fc->fc_i2c_adap[0].i2c_adap);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = i2c_add_adapter(&fc->fc_i2c_adap[1].i2c_adap);
|
|
if (ret < 0)
|
|
goto adap_1_failed;
|
|
|
|
ret = i2c_add_adapter(&fc->fc_i2c_adap[2].i2c_adap);
|
|
if (ret < 0)
|
|
goto adap_2_failed;
|
|
|
|
fc->init_state |= FC_STATE_I2C_INIT;
|
|
return 0;
|
|
|
|
adap_2_failed:
|
|
i2c_del_adapter(&fc->fc_i2c_adap[1].i2c_adap);
|
|
adap_1_failed:
|
|
i2c_del_adapter(&fc->fc_i2c_adap[0].i2c_adap);
|
|
return ret;
|
|
}
|
|
|
|
void flexcop_i2c_exit(struct flexcop_device *fc)
|
|
{
|
|
if (fc->init_state & FC_STATE_I2C_INIT) {
|
|
i2c_del_adapter(&fc->fc_i2c_adap[2].i2c_adap);
|
|
i2c_del_adapter(&fc->fc_i2c_adap[1].i2c_adap);
|
|
i2c_del_adapter(&fc->fc_i2c_adap[0].i2c_adap);
|
|
}
|
|
fc->init_state &= ~FC_STATE_I2C_INIT;
|
|
}
|