mirror of
https://github.com/torvalds/linux.git
synced 2026-05-31 02:24:24 +02:00
i2c-for-6.13-rc5
i2c-host-fixes (Andi) - IMX: fixed stop condition in single master mode and added compatible string for errata adherence. - Microchip: Added support for proper repeated sends and fixed unnecessary NAKs on empty messages, which caused false bus detection. -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEOZGx6rniZ1Gk92RdFA3kzBSgKbYFAmdwchgACgkQFA3kzBSg Kbb9CBAAqD028+v6e6MeQEzu3YuHKY80hJUi19VvCXh1BE3+Dw0dbzs+iKa2FOIm dxCJfivUbijHv6mzKxDrePW3DH023eHOG/qgHdYy4J2XkCnGFbdre0hf1GF3FDPw Xa5TvAXXdujyPADY+5Dm0qfAYxKJvSt+WI8kNcVR3IUI9FdA6/6YvON7q261+j+k cRzQpkeeTXbvZfe/X9ZpnREtUhpAPhB43+BMo2HAddr/GUW2JFxOEfTDdNhxpycz wsYQz/J/C2BLZczk+Phl3iT9n4bSxiucDD+vWDY1oXQJ3NzYC2PbKHWn0KTg9koC N1/gNUqCwLN+LL3h+Ke4duxWPunCR6+BOag9pZPPG6gkiEu5140PEovDF+QuKiPZ VySMxMVIadgC4TZfcLjxjLI0Rhus91Xq+RiKnUADKNzPRrqMyFtW9pjMjpJxBrnD MxaWRw2RcpZnnsGlEmnVpiwKFHAprzfygbGAnVXSL8QBeksx6MGlyvyxnI5Ykv5v h5dfrYvygj823+Jayu56Sk6bJXP3lyBgruzTMaxMKPZLdoTXu4c5CKin2qoUvBD4 GH50XZjJzwR4oqnn0YAolzUlBdSKejIOPe20PafXvILqpSGCxnZA2aQMI1AcXbKJ VyRvr1MkJ7TbXBalerXaXOA0WQ6H3oVFCrHj+H6xEex7y2ywcdo= =GIRt -----END PGP SIGNATURE----- Merge tag 'i2c-for-6.13-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux Pull i2c fixes from Wolfram Sang: - IMX: fix stop condition in single master mode and add compatible string for errata adherence - Microchip: Add support for proper repeated sends and fix unnecessary NAKs on empty messages, which caused false bus detection * tag 'i2c-for-6.13-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: i2c: microchip-core: fix "ghost" detections i2c: microchip-core: actually use repeated sends i2c: imx: add imx7d compatible string for applying erratum ERR007805 i2c: imx: fix missing stop condition in single-master mode
This commit is contained in:
commit
e1d9326608
|
|
@ -335,6 +335,7 @@ static const struct of_device_id i2c_imx_dt_ids[] = {
|
|||
{ .compatible = "fsl,imx6sll-i2c", .data = &imx6_i2c_hwdata, },
|
||||
{ .compatible = "fsl,imx6sx-i2c", .data = &imx6_i2c_hwdata, },
|
||||
{ .compatible = "fsl,imx6ul-i2c", .data = &imx6_i2c_hwdata, },
|
||||
{ .compatible = "fsl,imx7d-i2c", .data = &imx6_i2c_hwdata, },
|
||||
{ .compatible = "fsl,imx7s-i2c", .data = &imx6_i2c_hwdata, },
|
||||
{ .compatible = "fsl,imx8mm-i2c", .data = &imx6_i2c_hwdata, },
|
||||
{ .compatible = "fsl,imx8mn-i2c", .data = &imx6_i2c_hwdata, },
|
||||
|
|
@ -532,22 +533,20 @@ static void i2c_imx_dma_free(struct imx_i2c_struct *i2c_imx)
|
|||
|
||||
static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy, bool atomic)
|
||||
{
|
||||
bool multi_master = i2c_imx->multi_master;
|
||||
unsigned long orig_jiffies = jiffies;
|
||||
unsigned int temp;
|
||||
|
||||
if (!i2c_imx->multi_master)
|
||||
return 0;
|
||||
|
||||
while (1) {
|
||||
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
|
||||
|
||||
/* check for arbitration lost */
|
||||
if (temp & I2SR_IAL) {
|
||||
if (multi_master && (temp & I2SR_IAL)) {
|
||||
i2c_imx_clear_irq(i2c_imx, I2SR_IAL);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (for_busy && (temp & I2SR_IBB)) {
|
||||
if (for_busy && (!multi_master || (temp & I2SR_IBB))) {
|
||||
i2c_imx->stopped = 0;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,27 +93,35 @@
|
|||
* @base: pointer to register struct
|
||||
* @dev: device reference
|
||||
* @i2c_clk: clock reference for i2c input clock
|
||||
* @msg_queue: pointer to the messages requiring sending
|
||||
* @buf: pointer to msg buffer for easier use
|
||||
* @msg_complete: xfer completion object
|
||||
* @adapter: core i2c abstraction
|
||||
* @msg_err: error code for completed message
|
||||
* @bus_clk_rate: current i2c bus clock rate
|
||||
* @isr_status: cached copy of local ISR status
|
||||
* @total_num: total number of messages to be sent/received
|
||||
* @current_num: index of the current message being sent/received
|
||||
* @msg_len: number of bytes transferred in msg
|
||||
* @addr: address of the current slave
|
||||
* @restart_needed: whether or not a repeated start is required after current message
|
||||
*/
|
||||
struct mchp_corei2c_dev {
|
||||
void __iomem *base;
|
||||
struct device *dev;
|
||||
struct clk *i2c_clk;
|
||||
struct i2c_msg *msg_queue;
|
||||
u8 *buf;
|
||||
struct completion msg_complete;
|
||||
struct i2c_adapter adapter;
|
||||
int msg_err;
|
||||
int total_num;
|
||||
int current_num;
|
||||
u32 bus_clk_rate;
|
||||
u32 isr_status;
|
||||
u16 msg_len;
|
||||
u8 addr;
|
||||
bool restart_needed;
|
||||
};
|
||||
|
||||
static void mchp_corei2c_core_disable(struct mchp_corei2c_dev *idev)
|
||||
|
|
@ -222,6 +230,47 @@ static int mchp_corei2c_fill_tx(struct mchp_corei2c_dev *idev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void mchp_corei2c_next_msg(struct mchp_corei2c_dev *idev)
|
||||
{
|
||||
struct i2c_msg *this_msg;
|
||||
u8 ctrl;
|
||||
|
||||
if (idev->current_num >= idev->total_num) {
|
||||
complete(&idev->msg_complete);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there's been an error, the isr needs to return control
|
||||
* to the "main" part of the driver, so as not to keep sending
|
||||
* messages once it completes and clears the SI bit.
|
||||
*/
|
||||
if (idev->msg_err) {
|
||||
complete(&idev->msg_complete);
|
||||
return;
|
||||
}
|
||||
|
||||
this_msg = idev->msg_queue++;
|
||||
|
||||
if (idev->current_num < (idev->total_num - 1)) {
|
||||
struct i2c_msg *next_msg = idev->msg_queue;
|
||||
|
||||
idev->restart_needed = next_msg->flags & I2C_M_RD;
|
||||
} else {
|
||||
idev->restart_needed = false;
|
||||
}
|
||||
|
||||
idev->addr = i2c_8bit_addr_from_msg(this_msg);
|
||||
idev->msg_len = this_msg->len;
|
||||
idev->buf = this_msg->buf;
|
||||
|
||||
ctrl = readb(idev->base + CORE_I2C_CTRL);
|
||||
ctrl |= CTRL_STA;
|
||||
writeb(ctrl, idev->base + CORE_I2C_CTRL);
|
||||
|
||||
idev->current_num++;
|
||||
}
|
||||
|
||||
static irqreturn_t mchp_corei2c_handle_isr(struct mchp_corei2c_dev *idev)
|
||||
{
|
||||
u32 status = idev->isr_status;
|
||||
|
|
@ -238,8 +287,6 @@ static irqreturn_t mchp_corei2c_handle_isr(struct mchp_corei2c_dev *idev)
|
|||
ctrl &= ~CTRL_STA;
|
||||
writeb(idev->addr, idev->base + CORE_I2C_DATA);
|
||||
writeb(ctrl, idev->base + CORE_I2C_CTRL);
|
||||
if (idev->msg_len == 0)
|
||||
finished = true;
|
||||
break;
|
||||
case STATUS_M_ARB_LOST:
|
||||
idev->msg_err = -EAGAIN;
|
||||
|
|
@ -247,10 +294,14 @@ static irqreturn_t mchp_corei2c_handle_isr(struct mchp_corei2c_dev *idev)
|
|||
break;
|
||||
case STATUS_M_SLAW_ACK:
|
||||
case STATUS_M_TX_DATA_ACK:
|
||||
if (idev->msg_len > 0)
|
||||
if (idev->msg_len > 0) {
|
||||
mchp_corei2c_fill_tx(idev);
|
||||
else
|
||||
last_byte = true;
|
||||
} else {
|
||||
if (idev->restart_needed)
|
||||
finished = true;
|
||||
else
|
||||
last_byte = true;
|
||||
}
|
||||
break;
|
||||
case STATUS_M_TX_DATA_NACK:
|
||||
case STATUS_M_SLAR_NACK:
|
||||
|
|
@ -287,7 +338,7 @@ static irqreturn_t mchp_corei2c_handle_isr(struct mchp_corei2c_dev *idev)
|
|||
mchp_corei2c_stop(idev);
|
||||
|
||||
if (last_byte || finished)
|
||||
complete(&idev->msg_complete);
|
||||
mchp_corei2c_next_msg(idev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
|
@ -311,21 +362,48 @@ static irqreturn_t mchp_corei2c_isr(int irq, void *_dev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int mchp_corei2c_xfer_msg(struct mchp_corei2c_dev *idev,
|
||||
struct i2c_msg *msg)
|
||||
static int mchp_corei2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||
int num)
|
||||
{
|
||||
u8 ctrl;
|
||||
struct mchp_corei2c_dev *idev = i2c_get_adapdata(adap);
|
||||
struct i2c_msg *this_msg = msgs;
|
||||
unsigned long time_left;
|
||||
|
||||
idev->addr = i2c_8bit_addr_from_msg(msg);
|
||||
idev->msg_len = msg->len;
|
||||
idev->buf = msg->buf;
|
||||
idev->msg_err = 0;
|
||||
|
||||
reinit_completion(&idev->msg_complete);
|
||||
u8 ctrl;
|
||||
|
||||
mchp_corei2c_core_enable(idev);
|
||||
|
||||
/*
|
||||
* The isr controls the flow of a transfer, this info needs to be saved
|
||||
* to a location that it can access the queue information from.
|
||||
*/
|
||||
idev->restart_needed = false;
|
||||
idev->msg_queue = msgs;
|
||||
idev->total_num = num;
|
||||
idev->current_num = 0;
|
||||
|
||||
/*
|
||||
* But the first entry to the isr is triggered by the start in this
|
||||
* function, so the first message needs to be "dequeued".
|
||||
*/
|
||||
idev->addr = i2c_8bit_addr_from_msg(this_msg);
|
||||
idev->msg_len = this_msg->len;
|
||||
idev->buf = this_msg->buf;
|
||||
idev->msg_err = 0;
|
||||
|
||||
if (idev->total_num > 1) {
|
||||
struct i2c_msg *next_msg = msgs + 1;
|
||||
|
||||
idev->restart_needed = next_msg->flags & I2C_M_RD;
|
||||
}
|
||||
|
||||
idev->current_num++;
|
||||
idev->msg_queue++;
|
||||
|
||||
reinit_completion(&idev->msg_complete);
|
||||
|
||||
/*
|
||||
* Send the first start to pass control to the isr
|
||||
*/
|
||||
ctrl = readb(idev->base + CORE_I2C_CTRL);
|
||||
ctrl |= CTRL_STA;
|
||||
writeb(ctrl, idev->base + CORE_I2C_CTRL);
|
||||
|
|
@ -335,20 +413,8 @@ static int mchp_corei2c_xfer_msg(struct mchp_corei2c_dev *idev,
|
|||
if (!time_left)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return idev->msg_err;
|
||||
}
|
||||
|
||||
static int mchp_corei2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||
int num)
|
||||
{
|
||||
struct mchp_corei2c_dev *idev = i2c_get_adapdata(adap);
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
ret = mchp_corei2c_xfer_msg(idev, msgs++);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
if (idev->msg_err)
|
||||
return idev->msg_err;
|
||||
|
||||
return num;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user