2018-05-20 12:46:35 -03:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2009-01-30 10:32:28 +02:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2002 Motorola GSG-China
|
|
|
|
*
|
|
|
|
* Author:
|
|
|
|
* Darius Augulis, Teltonika Inc.
|
|
|
|
*
|
|
|
|
* Desc.:
|
|
|
|
* Implementation of I2C Adapter/Algorithm Driver
|
|
|
|
* for I2C Bus integrated in Freescale i.MX/MXC processors
|
|
|
|
*
|
|
|
|
* Derived from Motorola GSG China I2C example driver
|
|
|
|
*
|
|
|
|
* Copyright (C) 2005 Torsten Koschorrek <koschorrek at synertronixx.de
|
|
|
|
* Copyright (C) 2005 Matthias Blaschke <blaschke at synertronixx.de
|
|
|
|
* Copyright (C) 2007 RightHand Technologies, Inc.
|
|
|
|
* Copyright (C) 2008 Darius Augulis <darius.augulis at teltonika.lt>
|
|
|
|
*
|
2013-08-07 17:05:36 +08:00
|
|
|
* Copyright 2013 Freescale Semiconductor, Inc.
|
2024-11-04 12:00:44 +02:00
|
|
|
* Copyright 2020, 2024 NXP
|
2013-08-07 17:05:36 +08:00
|
|
|
*
|
2009-01-30 10:32:28 +02:00
|
|
|
*/
|
|
|
|
|
2019-09-06 15:53:19 +08:00
|
|
|
#include <linux/acpi.h>
|
2014-11-18 18:31:05 +08:00
|
|
|
#include <linux/clk.h>
|
2025-05-31 14:57:26 +08:00
|
|
|
#include <linux/cleanup.h>
|
2014-11-18 18:31:06 +08:00
|
|
|
#include <linux/completion.h>
|
2009-01-30 10:32:28 +02:00
|
|
|
#include <linux/delay.h>
|
2014-11-18 18:31:06 +08:00
|
|
|
#include <linux/dma-mapping.h>
|
|
|
|
#include <linux/dmaengine.h>
|
|
|
|
#include <linux/dmapool.h>
|
2014-11-18 18:31:05 +08:00
|
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/errno.h>
|
2017-12-08 14:35:35 +01:00
|
|
|
#include <linux/gpio/consumer.h>
|
2009-01-30 10:32:28 +02:00
|
|
|
#include <linux/i2c.h>
|
2014-11-18 18:31:05 +08:00
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/interrupt.h>
|
2009-01-30 10:32:28 +02:00
|
|
|
#include <linux/io.h>
|
2020-01-20 10:36:50 +01:00
|
|
|
#include <linux/iopoll.h>
|
2014-11-18 18:31:05 +08:00
|
|
|
#include <linux/kernel.h>
|
2021-11-12 07:39:55 -06:00
|
|
|
#include <linux/spinlock.h>
|
|
|
|
#include <linux/hrtimer.h>
|
2014-11-18 18:31:05 +08:00
|
|
|
#include <linux/module.h>
|
2011-09-08 15:09:35 +08:00
|
|
|
#include <linux/of.h>
|
2014-11-18 18:31:06 +08:00
|
|
|
#include <linux/of_dma.h>
|
2015-11-17 17:53:18 +08:00
|
|
|
#include <linux/pinctrl/consumer.h>
|
2012-08-24 15:14:29 +02:00
|
|
|
#include <linux/platform_data/i2c-imx.h>
|
2014-11-18 18:31:05 +08:00
|
|
|
#include <linux/platform_device.h>
|
2015-12-11 10:24:09 +08:00
|
|
|
#include <linux/pm_runtime.h>
|
2014-11-18 18:31:05 +08:00
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/slab.h>
|
2009-01-30 10:32:28 +02:00
|
|
|
|
|
|
|
/* This will be the driver name the kernel reports */
|
|
|
|
#define DRIVER_NAME "imx-i2c"
|
|
|
|
|
2021-11-12 07:39:55 -06:00
|
|
|
#define I2C_IMX_CHECK_DELAY 30000 /* Time to check for bus idle, in NS */
|
|
|
|
|
2014-11-18 18:31:06 +08:00
|
|
|
/*
|
|
|
|
* Enable DMA if transfer byte size is bigger than this threshold.
|
|
|
|
* As the hardware request, it must bigger than 4 bytes.\
|
|
|
|
* I have set '16' here, maybe it's not the best but I think it's
|
|
|
|
* the appropriate.
|
|
|
|
*/
|
|
|
|
#define DMA_THRESHOLD 16
|
|
|
|
#define DMA_TIMEOUT 1000
|
|
|
|
|
2013-08-07 17:05:40 +08:00
|
|
|
/* IMX I2C registers:
|
|
|
|
* the I2C register offset is different between SoCs,
|
2022-07-15 15:42:18 +02:00
|
|
|
* to provide support for all these chips, split the
|
2013-08-07 17:05:40 +08:00
|
|
|
* register offset into a fixed base address and a
|
|
|
|
* variable shift value, then the full register offset
|
|
|
|
* will be calculated by
|
|
|
|
* reg_off = ( reg_base_addr << reg_shift)
|
|
|
|
*/
|
2009-01-30 10:32:28 +02:00
|
|
|
#define IMX_I2C_IADR 0x00 /* i2c slave address */
|
2013-08-07 17:05:40 +08:00
|
|
|
#define IMX_I2C_IFDR 0x01 /* i2c frequency divider */
|
|
|
|
#define IMX_I2C_I2CR 0x02 /* i2c control */
|
|
|
|
#define IMX_I2C_I2SR 0x03 /* i2c status */
|
|
|
|
#define IMX_I2C_I2DR 0x04 /* i2c transfer data */
|
|
|
|
|
2020-11-11 19:32:55 +08:00
|
|
|
/*
|
|
|
|
* All of the layerscape series SoCs support IBIC register.
|
|
|
|
*/
|
|
|
|
#define IMX_I2C_IBIC 0x05 /* i2c bus interrupt config */
|
|
|
|
|
2013-08-07 17:05:40 +08:00
|
|
|
#define IMX_I2C_REGSHIFT 2
|
2013-08-07 17:05:43 +08:00
|
|
|
#define VF610_I2C_REGSHIFT 0
|
2024-11-04 12:00:44 +02:00
|
|
|
#define S32G_I2C_REGSHIFT 0
|
2009-01-30 10:32:28 +02:00
|
|
|
|
|
|
|
/* Bits of IMX I2C registers */
|
|
|
|
#define I2SR_RXAK 0x01
|
|
|
|
#define I2SR_IIF 0x02
|
|
|
|
#define I2SR_SRW 0x04
|
|
|
|
#define I2SR_IAL 0x10
|
|
|
|
#define I2SR_IBB 0x20
|
|
|
|
#define I2SR_IAAS 0x40
|
|
|
|
#define I2SR_ICF 0x80
|
2014-11-18 18:31:06 +08:00
|
|
|
#define I2CR_DMAEN 0x02
|
2009-01-30 10:32:28 +02:00
|
|
|
#define I2CR_RSTA 0x04
|
|
|
|
#define I2CR_TXAK 0x08
|
|
|
|
#define I2CR_MTX 0x10
|
|
|
|
#define I2CR_MSTA 0x20
|
|
|
|
#define I2CR_IIEN 0x40
|
|
|
|
#define I2CR_IEN 0x80
|
2020-11-11 19:32:55 +08:00
|
|
|
#define IBIC_BIIE 0x80 /* Bus idle interrupt enable */
|
2009-01-30 10:32:28 +02:00
|
|
|
|
2013-08-07 17:05:41 +08:00
|
|
|
/* register bits different operating codes definition:
|
|
|
|
* 1) I2SR: Interrupt flags clear operation differ between SoCs:
|
|
|
|
* - write zero to clear(w0c) INT flag on i.MX,
|
|
|
|
* - but write one to clear(w1c) INT flag on Vybrid.
|
|
|
|
* 2) I2CR: I2C module enable operation also differ between SoCs:
|
|
|
|
* - set I2CR_IEN bit enable the module on i.MX,
|
|
|
|
* - but clear I2CR_IEN bit enable the module on Vybrid.
|
|
|
|
*/
|
|
|
|
#define I2SR_CLR_OPCODE_W0C 0x0
|
|
|
|
#define I2SR_CLR_OPCODE_W1C (I2SR_IAL | I2SR_IIF)
|
|
|
|
#define I2CR_IEN_OPCODE_0 0x0
|
|
|
|
#define I2CR_IEN_OPCODE_1 I2CR_IEN
|
|
|
|
|
2015-12-11 10:24:09 +08:00
|
|
|
#define I2C_PM_TIMEOUT 10 /* ms */
|
|
|
|
|
2009-01-30 10:32:28 +02:00
|
|
|
/*
|
|
|
|
* sorted list of clock divider, register value pairs
|
|
|
|
* taken from table 26-5, p.26-9, Freescale i.MX
|
|
|
|
* Integrated Portable System Processor Reference Manual
|
|
|
|
* Document Number: MC9328MXLRM, Rev. 5.1, 06/2007
|
|
|
|
*
|
|
|
|
* Duplicated divider values removed from list
|
|
|
|
*/
|
2013-08-07 17:05:36 +08:00
|
|
|
struct imx_i2c_clk_pair {
|
|
|
|
u16 div;
|
|
|
|
u16 val;
|
|
|
|
};
|
2009-01-30 10:32:28 +02:00
|
|
|
|
2013-08-07 17:05:42 +08:00
|
|
|
static struct imx_i2c_clk_pair imx_i2c_clk_div[] = {
|
2009-01-30 10:32:28 +02:00
|
|
|
{ 22, 0x20 }, { 24, 0x21 }, { 26, 0x22 }, { 28, 0x23 },
|
|
|
|
{ 30, 0x00 }, { 32, 0x24 }, { 36, 0x25 }, { 40, 0x26 },
|
|
|
|
{ 42, 0x03 }, { 44, 0x27 }, { 48, 0x28 }, { 52, 0x05 },
|
|
|
|
{ 56, 0x29 }, { 60, 0x06 }, { 64, 0x2A }, { 72, 0x2B },
|
|
|
|
{ 80, 0x2C }, { 88, 0x09 }, { 96, 0x2D }, { 104, 0x0A },
|
|
|
|
{ 112, 0x2E }, { 128, 0x2F }, { 144, 0x0C }, { 160, 0x30 },
|
|
|
|
{ 192, 0x31 }, { 224, 0x32 }, { 240, 0x0F }, { 256, 0x33 },
|
|
|
|
{ 288, 0x10 }, { 320, 0x34 }, { 384, 0x35 }, { 448, 0x36 },
|
|
|
|
{ 480, 0x13 }, { 512, 0x37 }, { 576, 0x14 }, { 640, 0x38 },
|
|
|
|
{ 768, 0x39 }, { 896, 0x3A }, { 960, 0x17 }, { 1024, 0x3B },
|
|
|
|
{ 1152, 0x18 }, { 1280, 0x3C }, { 1536, 0x3D }, { 1792, 0x3E },
|
|
|
|
{ 1920, 0x1B }, { 2048, 0x3F }, { 2304, 0x1C }, { 2560, 0x1D },
|
|
|
|
{ 3072, 0x1E }, { 3840, 0x1F }
|
|
|
|
};
|
|
|
|
|
2013-08-07 17:05:43 +08:00
|
|
|
/* Vybrid VF610 clock divider, register value pairs */
|
|
|
|
static struct imx_i2c_clk_pair vf610_i2c_clk_div[] = {
|
|
|
|
{ 20, 0x00 }, { 22, 0x01 }, { 24, 0x02 }, { 26, 0x03 },
|
|
|
|
{ 28, 0x04 }, { 30, 0x05 }, { 32, 0x09 }, { 34, 0x06 },
|
|
|
|
{ 36, 0x0A }, { 40, 0x07 }, { 44, 0x0C }, { 48, 0x0D },
|
|
|
|
{ 52, 0x43 }, { 56, 0x0E }, { 60, 0x45 }, { 64, 0x12 },
|
|
|
|
{ 68, 0x0F }, { 72, 0x13 }, { 80, 0x14 }, { 88, 0x15 },
|
|
|
|
{ 96, 0x19 }, { 104, 0x16 }, { 112, 0x1A }, { 128, 0x17 },
|
|
|
|
{ 136, 0x4F }, { 144, 0x1C }, { 160, 0x1D }, { 176, 0x55 },
|
|
|
|
{ 192, 0x1E }, { 208, 0x56 }, { 224, 0x22 }, { 228, 0x24 },
|
|
|
|
{ 240, 0x1F }, { 256, 0x23 }, { 288, 0x5C }, { 320, 0x25 },
|
|
|
|
{ 384, 0x26 }, { 448, 0x2A }, { 480, 0x27 }, { 512, 0x2B },
|
|
|
|
{ 576, 0x2C }, { 640, 0x2D }, { 768, 0x31 }, { 896, 0x32 },
|
|
|
|
{ 960, 0x2F }, { 1024, 0x33 }, { 1152, 0x34 }, { 1280, 0x35 },
|
|
|
|
{ 1536, 0x36 }, { 1792, 0x3A }, { 1920, 0x37 }, { 2048, 0x3B },
|
|
|
|
{ 2304, 0x3C }, { 2560, 0x3D }, { 3072, 0x3E }, { 3584, 0x7A },
|
|
|
|
{ 3840, 0x3F }, { 4096, 0x7B }, { 5120, 0x7D }, { 6144, 0x7E },
|
|
|
|
};
|
|
|
|
|
2024-11-04 12:00:44 +02:00
|
|
|
/* S32G2/S32G3 clock divider, register value pairs */
|
|
|
|
static struct imx_i2c_clk_pair s32g2_i2c_clk_div[] = {
|
|
|
|
{ 34, 0x00 }, { 36, 0x01 }, { 38, 0x02 }, { 40, 0x03 },
|
|
|
|
{ 42, 0x04 }, { 44, 0x05 }, { 46, 0x06 }, { 48, 0x09 },
|
|
|
|
{ 52, 0x0A }, { 54, 0x07 }, { 56, 0x0B }, { 60, 0x0C },
|
|
|
|
{ 64, 0x0D }, { 68, 0x40 }, { 72, 0x0E }, { 76, 0x42 },
|
|
|
|
{ 80, 0x12 }, { 84, 0x0F }, { 88, 0x13 }, { 96, 0x14 },
|
|
|
|
{ 104, 0x15 }, { 108, 0x47 }, { 112, 0x19 }, { 120, 0x16 },
|
|
|
|
{ 128, 0x1A }, { 136, 0x80 }, { 144, 0x17 }, { 152, 0x82 },
|
|
|
|
{ 160, 0x1C }, { 168, 0x84 }, { 176, 0x1D }, { 192, 0x21 },
|
|
|
|
{ 208, 0x1E }, { 216, 0x87 }, { 224, 0x22 }, { 240, 0x56 },
|
|
|
|
{ 256, 0x1F }, { 288, 0x24 }, { 320, 0x25 }, { 336, 0x8F },
|
|
|
|
{ 352, 0x93 }, { 356, 0x5D }, { 358, 0x98 }, { 384, 0x26 },
|
|
|
|
{ 416, 0x56 }, { 448, 0x2A }, { 480, 0x27 }, { 512, 0x2B },
|
|
|
|
{ 576, 0x2C }, { 640, 0x2D }, { 704, 0x9D }, { 768, 0x2E },
|
|
|
|
{ 832, 0x9D }, { 896, 0x32 }, { 960, 0x2F }, { 1024, 0x33 },
|
|
|
|
{ 1152, 0x34 }, { 1280, 0x35 }, { 1536, 0x36 }, { 1792, 0x3A },
|
|
|
|
{ 1920, 0x37 }, { 2048, 0x3B }, { 2304, 0x74 }, { 2560, 0x3D },
|
|
|
|
{ 3072, 0x3E }, { 3584, 0x7A }, { 3840, 0x3F }, { 4096, 0x7B },
|
|
|
|
{ 4608, 0x7C }, { 5120, 0x7D }, { 6144, 0x7E }, { 7168, 0xBA },
|
|
|
|
{ 7680, 0x7F }, { 8192, 0xBB }, { 9216, 0xBC }, { 10240, 0xBD },
|
|
|
|
{ 12288, 0xBE }, { 15360, 0xBF },
|
|
|
|
};
|
|
|
|
|
2012-09-14 15:19:00 +08:00
|
|
|
enum imx_i2c_type {
|
|
|
|
IMX1_I2C,
|
|
|
|
IMX21_I2C,
|
2024-11-04 12:00:44 +02:00
|
|
|
S32G_I2C,
|
2013-08-07 17:05:43 +08:00
|
|
|
VF610_I2C,
|
2012-09-14 15:19:00 +08:00
|
|
|
};
|
|
|
|
|
2013-08-07 17:05:42 +08:00
|
|
|
struct imx_i2c_hwdata {
|
|
|
|
enum imx_i2c_type devtype;
|
2021-06-23 17:36:43 +09:00
|
|
|
unsigned int regshift;
|
2013-08-07 17:05:42 +08:00
|
|
|
struct imx_i2c_clk_pair *clk_div;
|
2021-06-23 17:36:43 +09:00
|
|
|
unsigned int ndivs;
|
|
|
|
unsigned int i2sr_clr_opcode;
|
|
|
|
unsigned int i2cr_ien_opcode;
|
i2c: imx: Implement errata ERR007805 or e7805 bus frequency limit
The i.MX8MP Mask Set Errata for Mask 1P33A, Rev. 2.0 has description of
errata ERR007805 as below. This errata is found on all MX8M{M,N,P,Q},
MX7{S,D}, MX6{UL{,L,Z},S{,LL,X},S,D,DL,Q,DP,QP} . MX7ULP, MX8Q, MX8X
are not affected. MX53 and older status is unknown, as the errata
first appears in MX6 errata sheets from 2016 and the latest errata
sheet for MX53 is from 2015. Older SoC errata sheets predate the
MX53 errata sheet. MX8ULP and MX9 status is unknown as the errata
sheet is not available yet.
"
ERR007805 I2C: When the I2C clock speed is configured for 400 kHz,
the SCL low period violates the I2C spec of 1.3 uS min
Description: When the I2C module is programmed to operate at the
maximum clock speed of 400 kHz (as defined by the I2C spec), the SCL
clock low period violates the I2C spec of 1.3 uS min. The user must
reduce the clock speed to obtain the SCL low time to meet the 1.3us
I2C minimum required. This behavior means the SoC is not compliant
to the I2C spec at 400kHz.
Workaround: To meet the clock low period requirement in fast speed
mode, SCL must be configured to 384KHz or less.
"
Implement the workaround by matching on the affected SoC specific
compatible strings and by limiting the maximum bus frequency in case
the SoC is affected.
Signed-off-by: Marek Vasut <marex@denx.de>
To: linux-i2c@vger.kernel.org
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Wolfram Sang <wsa@kernel.org>
2022-04-08 19:15:24 +02:00
|
|
|
/*
|
|
|
|
* Errata ERR007805 or e7805:
|
|
|
|
* I2C: When the I2C clock speed is configured for 400 kHz,
|
|
|
|
* the SCL low period violates the I2C spec of 1.3 uS min.
|
|
|
|
*/
|
|
|
|
bool has_err007805;
|
2013-08-07 17:05:42 +08:00
|
|
|
};
|
|
|
|
|
2014-11-18 18:31:06 +08:00
|
|
|
struct imx_i2c_dma {
|
|
|
|
struct dma_chan *chan_tx;
|
|
|
|
struct dma_chan *chan_rx;
|
|
|
|
struct dma_chan *chan_using;
|
|
|
|
struct completion cmd_complete;
|
|
|
|
dma_addr_t dma_buf;
|
|
|
|
unsigned int dma_len;
|
|
|
|
enum dma_transfer_direction dma_transfer_dir;
|
|
|
|
enum dma_data_direction dma_data_dir;
|
|
|
|
};
|
|
|
|
|
i2c: imx: prevent rescheduling in non dma mode
We are experiencing a problem with the i.MX I2C controller when
communicating with SMBus devices. We are seeing devices time-out because
the time between sending/receiving two bytes is too long, and the SMBus
device returns to the idle state. This happens because the i.MX I2C
controller sends and receives byte by byte. When a byte is sent or
received, we get an interrupt and can send or receive the next byte.
The current implementation sends a byte and then waits for an event
generated by the interrupt subroutine. After the event is received, the
next byte is sent and we wait again. This waiting allows the scheduler
to reschedule other tasks, with the disadvantage that we may not send
the next byte for a long time because the send task is not immediately
scheduled. For example, if the rescheduling takes more than 25ms, this
can cause SMBus devices to timeout and communication to fail.
This patch changes the behavior so that we do not reschedule the
send/receive task, but instead send or receive the next byte in the
interrupt subroutine. This prevents rescheduling and drastically reduces
the time between sending/receiving bytes. The cost in the interrupt
subroutine is relatively small, we check what state we are in and then
send/receive the next byte. Before we had to call wake_up, which is even
less expensive. However, we also had to do some scheduling, which
increased the overall cost compared to the new solution. The wake_up
function to wake up the send/receive task is now only called when an
error occurs or when the transfer is complete.
Signed-off-by: Stefan Eichenberger <stefan.eichenberger@toradex.com>
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
2024-10-14 15:15:14 +02:00
|
|
|
enum imx_i2c_state {
|
|
|
|
IMX_I2C_STATE_DONE,
|
|
|
|
IMX_I2C_STATE_FAILED,
|
|
|
|
IMX_I2C_STATE_WRITE,
|
|
|
|
IMX_I2C_STATE_DMA,
|
|
|
|
IMX_I2C_STATE_READ,
|
|
|
|
IMX_I2C_STATE_READ_CONTINUE,
|
|
|
|
IMX_I2C_STATE_READ_BLOCK_DATA,
|
|
|
|
IMX_I2C_STATE_READ_BLOCK_DATA_LEN,
|
|
|
|
};
|
|
|
|
|
2009-01-30 10:32:28 +02:00
|
|
|
struct imx_i2c_struct {
|
|
|
|
struct i2c_adapter adapter;
|
|
|
|
struct clk *clk;
|
2018-03-08 14:25:17 +01:00
|
|
|
struct notifier_block clk_change_nb;
|
2009-01-30 10:32:28 +02:00
|
|
|
void __iomem *base;
|
|
|
|
wait_queue_head_t queue;
|
|
|
|
unsigned long i2csr;
|
2015-01-22 16:17:29 +01:00
|
|
|
unsigned int disable_delay;
|
2009-10-17 17:46:22 +08:00
|
|
|
int stopped;
|
2009-10-17 17:46:24 +08:00
|
|
|
unsigned int ifdr; /* IMX_I2C_IFDR */
|
2014-05-20 10:21:45 +08:00
|
|
|
unsigned int cur_clk;
|
|
|
|
unsigned int bitrate;
|
2013-08-07 17:05:42 +08:00
|
|
|
const struct imx_i2c_hwdata *hwdata;
|
2015-10-23 20:28:54 +08:00
|
|
|
struct i2c_bus_recovery_info rinfo;
|
|
|
|
|
2014-11-18 18:31:06 +08:00
|
|
|
struct imx_i2c_dma *dma;
|
2020-11-11 19:32:55 +08:00
|
|
|
struct i2c_client *slave;
|
2020-12-22 11:48:50 -08:00
|
|
|
enum i2c_slave_event last_slave_event;
|
2021-11-12 07:39:55 -06:00
|
|
|
|
i2c: imx: prevent rescheduling in non dma mode
We are experiencing a problem with the i.MX I2C controller when
communicating with SMBus devices. We are seeing devices time-out because
the time between sending/receiving two bytes is too long, and the SMBus
device returns to the idle state. This happens because the i.MX I2C
controller sends and receives byte by byte. When a byte is sent or
received, we get an interrupt and can send or receive the next byte.
The current implementation sends a byte and then waits for an event
generated by the interrupt subroutine. After the event is received, the
next byte is sent and we wait again. This waiting allows the scheduler
to reschedule other tasks, with the disadvantage that we may not send
the next byte for a long time because the send task is not immediately
scheduled. For example, if the rescheduling takes more than 25ms, this
can cause SMBus devices to timeout and communication to fail.
This patch changes the behavior so that we do not reschedule the
send/receive task, but instead send or receive the next byte in the
interrupt subroutine. This prevents rescheduling and drastically reduces
the time between sending/receiving bytes. The cost in the interrupt
subroutine is relatively small, we check what state we are in and then
send/receive the next byte. Before we had to call wake_up, which is even
less expensive. However, we also had to do some scheduling, which
increased the overall cost compared to the new solution. The wake_up
function to wake up the send/receive task is now only called when an
error occurs or when the transfer is complete.
Signed-off-by: Stefan Eichenberger <stefan.eichenberger@toradex.com>
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
2024-10-14 15:15:14 +02:00
|
|
|
struct i2c_msg *msg;
|
|
|
|
unsigned int msg_buf_idx;
|
|
|
|
int isr_result;
|
|
|
|
bool is_lastmsg;
|
|
|
|
enum imx_i2c_state state;
|
|
|
|
|
2024-10-14 15:15:12 +02:00
|
|
|
bool multi_master;
|
|
|
|
|
2021-11-12 07:39:55 -06:00
|
|
|
/* For checking slave events. */
|
|
|
|
spinlock_t slave_lock;
|
|
|
|
struct hrtimer slave_timer;
|
2013-08-07 17:05:42 +08:00
|
|
|
};
|
|
|
|
|
2016-01-25 15:48:32 +03:00
|
|
|
static const struct imx_i2c_hwdata imx1_i2c_hwdata = {
|
2013-08-07 17:05:42 +08:00
|
|
|
.devtype = IMX1_I2C,
|
|
|
|
.regshift = IMX_I2C_REGSHIFT,
|
|
|
|
.clk_div = imx_i2c_clk_div,
|
|
|
|
.ndivs = ARRAY_SIZE(imx_i2c_clk_div),
|
|
|
|
.i2sr_clr_opcode = I2SR_CLR_OPCODE_W0C,
|
|
|
|
.i2cr_ien_opcode = I2CR_IEN_OPCODE_1,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2016-01-25 15:48:32 +03:00
|
|
|
static const struct imx_i2c_hwdata imx21_i2c_hwdata = {
|
2013-08-07 17:05:42 +08:00
|
|
|
.devtype = IMX21_I2C,
|
|
|
|
.regshift = IMX_I2C_REGSHIFT,
|
|
|
|
.clk_div = imx_i2c_clk_div,
|
|
|
|
.ndivs = ARRAY_SIZE(imx_i2c_clk_div),
|
|
|
|
.i2sr_clr_opcode = I2SR_CLR_OPCODE_W0C,
|
|
|
|
.i2cr_ien_opcode = I2CR_IEN_OPCODE_1,
|
|
|
|
|
2012-09-14 15:19:00 +08:00
|
|
|
};
|
|
|
|
|
i2c: imx: Implement errata ERR007805 or e7805 bus frequency limit
The i.MX8MP Mask Set Errata for Mask 1P33A, Rev. 2.0 has description of
errata ERR007805 as below. This errata is found on all MX8M{M,N,P,Q},
MX7{S,D}, MX6{UL{,L,Z},S{,LL,X},S,D,DL,Q,DP,QP} . MX7ULP, MX8Q, MX8X
are not affected. MX53 and older status is unknown, as the errata
first appears in MX6 errata sheets from 2016 and the latest errata
sheet for MX53 is from 2015. Older SoC errata sheets predate the
MX53 errata sheet. MX8ULP and MX9 status is unknown as the errata
sheet is not available yet.
"
ERR007805 I2C: When the I2C clock speed is configured for 400 kHz,
the SCL low period violates the I2C spec of 1.3 uS min
Description: When the I2C module is programmed to operate at the
maximum clock speed of 400 kHz (as defined by the I2C spec), the SCL
clock low period violates the I2C spec of 1.3 uS min. The user must
reduce the clock speed to obtain the SCL low time to meet the 1.3us
I2C minimum required. This behavior means the SoC is not compliant
to the I2C spec at 400kHz.
Workaround: To meet the clock low period requirement in fast speed
mode, SCL must be configured to 384KHz or less.
"
Implement the workaround by matching on the affected SoC specific
compatible strings and by limiting the maximum bus frequency in case
the SoC is affected.
Signed-off-by: Marek Vasut <marex@denx.de>
To: linux-i2c@vger.kernel.org
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Wolfram Sang <wsa@kernel.org>
2022-04-08 19:15:24 +02:00
|
|
|
static const struct imx_i2c_hwdata imx6_i2c_hwdata = {
|
|
|
|
.devtype = IMX21_I2C,
|
|
|
|
.regshift = IMX_I2C_REGSHIFT,
|
|
|
|
.clk_div = imx_i2c_clk_div,
|
|
|
|
.ndivs = ARRAY_SIZE(imx_i2c_clk_div),
|
|
|
|
.i2sr_clr_opcode = I2SR_CLR_OPCODE_W0C,
|
|
|
|
.i2cr_ien_opcode = I2CR_IEN_OPCODE_1,
|
|
|
|
.has_err007805 = true,
|
|
|
|
};
|
|
|
|
|
2013-08-07 17:05:43 +08:00
|
|
|
static struct imx_i2c_hwdata vf610_i2c_hwdata = {
|
|
|
|
.devtype = VF610_I2C,
|
|
|
|
.regshift = VF610_I2C_REGSHIFT,
|
|
|
|
.clk_div = vf610_i2c_clk_div,
|
|
|
|
.ndivs = ARRAY_SIZE(vf610_i2c_clk_div),
|
|
|
|
.i2sr_clr_opcode = I2SR_CLR_OPCODE_W1C,
|
|
|
|
.i2cr_ien_opcode = I2CR_IEN_OPCODE_0,
|
2024-11-04 12:00:44 +02:00
|
|
|
};
|
2013-08-07 17:05:43 +08:00
|
|
|
|
2024-11-04 12:00:44 +02:00
|
|
|
static const struct imx_i2c_hwdata s32g2_i2c_hwdata = {
|
|
|
|
.devtype = S32G_I2C,
|
|
|
|
.regshift = S32G_I2C_REGSHIFT,
|
|
|
|
.clk_div = s32g2_i2c_clk_div,
|
|
|
|
.ndivs = ARRAY_SIZE(s32g2_i2c_clk_div),
|
|
|
|
.i2sr_clr_opcode = I2SR_CLR_OPCODE_W1C,
|
|
|
|
.i2cr_ien_opcode = I2CR_IEN_OPCODE_0,
|
2013-08-07 17:05:43 +08:00
|
|
|
};
|
|
|
|
|
2021-01-19 23:41:23 -03:00
|
|
|
static const struct platform_device_id imx_i2c_devtype[] = {
|
|
|
|
{
|
|
|
|
.name = "imx1-i2c",
|
|
|
|
.driver_data = (kernel_ulong_t)&imx1_i2c_hwdata,
|
|
|
|
}, {
|
|
|
|
.name = "imx21-i2c",
|
|
|
|
.driver_data = (kernel_ulong_t)&imx21_i2c_hwdata,
|
|
|
|
}, {
|
|
|
|
/* sentinel */
|
|
|
|
}
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(platform, imx_i2c_devtype);
|
|
|
|
|
2011-09-08 15:09:35 +08:00
|
|
|
static const struct of_device_id i2c_imx_dt_ids[] = {
|
2013-08-07 17:05:42 +08:00
|
|
|
{ .compatible = "fsl,imx1-i2c", .data = &imx1_i2c_hwdata, },
|
|
|
|
{ .compatible = "fsl,imx21-i2c", .data = &imx21_i2c_hwdata, },
|
i2c: imx: Implement errata ERR007805 or e7805 bus frequency limit
The i.MX8MP Mask Set Errata for Mask 1P33A, Rev. 2.0 has description of
errata ERR007805 as below. This errata is found on all MX8M{M,N,P,Q},
MX7{S,D}, MX6{UL{,L,Z},S{,LL,X},S,D,DL,Q,DP,QP} . MX7ULP, MX8Q, MX8X
are not affected. MX53 and older status is unknown, as the errata
first appears in MX6 errata sheets from 2016 and the latest errata
sheet for MX53 is from 2015. Older SoC errata sheets predate the
MX53 errata sheet. MX8ULP and MX9 status is unknown as the errata
sheet is not available yet.
"
ERR007805 I2C: When the I2C clock speed is configured for 400 kHz,
the SCL low period violates the I2C spec of 1.3 uS min
Description: When the I2C module is programmed to operate at the
maximum clock speed of 400 kHz (as defined by the I2C spec), the SCL
clock low period violates the I2C spec of 1.3 uS min. The user must
reduce the clock speed to obtain the SCL low time to meet the 1.3us
I2C minimum required. This behavior means the SoC is not compliant
to the I2C spec at 400kHz.
Workaround: To meet the clock low period requirement in fast speed
mode, SCL must be configured to 384KHz or less.
"
Implement the workaround by matching on the affected SoC specific
compatible strings and by limiting the maximum bus frequency in case
the SoC is affected.
Signed-off-by: Marek Vasut <marex@denx.de>
To: linux-i2c@vger.kernel.org
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Wolfram Sang <wsa@kernel.org>
2022-04-08 19:15:24 +02:00
|
|
|
{ .compatible = "fsl,imx6q-i2c", .data = &imx6_i2c_hwdata, },
|
|
|
|
{ .compatible = "fsl,imx6sl-i2c", .data = &imx6_i2c_hwdata, },
|
|
|
|
{ .compatible = "fsl,imx6sll-i2c", .data = &imx6_i2c_hwdata, },
|
|
|
|
{ .compatible = "fsl,imx6sx-i2c", .data = &imx6_i2c_hwdata, },
|
|
|
|
{ .compatible = "fsl,imx6ul-i2c", .data = &imx6_i2c_hwdata, },
|
i2c: imx: add imx7d compatible string for applying erratum ERR007805
Compatible string "fsl,imx7d-i2c" is not exited at i2c-imx driver
compatible string table, at the result, "fsl,imx21-i2c" will be
matched, but it will cause erratum ERR007805 not be applied in fact.
So Add "fsl,imx7d-i2c" compatible string in i2c-imx driver to apply
the erratum ERR007805(https://www.nxp.com/docs/en/errata/IMX7DS_3N09P.pdf).
"
ERR007805 I2C: When the I2C clock speed is configured for 400 kHz,
the SCL low period violates the I2C spec of 1.3 uS min
Description: When the I2C module is programmed to operate at the
maximum clock speed of 400 kHz (as defined by the I2C spec), the SCL
clock low period violates the I2C spec of 1.3 uS min. The user must
reduce the clock speed to obtain the SCL low time to meet the 1.3us
I2C minimum required. This behavior means the SoC is not compliant
to the I2C spec at 400kHz.
Workaround: To meet the clock low period requirement in fast speed
mode, SCL must be configured to 384KHz or less.
"
"fsl,imx7d-i2c" already is documented in binding doc. This erratum
fix has been included in imx6_i2c_hwdata and it is the same in all
I.MX6/7/8, so just reuse it.
Fixes: 39c025721d70 ("i2c: imx: Implement errata ERR007805 or e7805 bus frequency limit")
Cc: stable@vger.kernel.org # v5.18+
Signed-off-by: Carlos Song <carlos.song@nxp.com>
Signed-off-by: Haibo Chen <haibo.chen@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Fixes: 39c025721d70 ("i2c: imx: Implement errata ERR007805 or e7805 bus frequency limit")
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Link: https://lore.kernel.org/r/20241218044238.143414-1-carlos.song@nxp.com
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
2024-12-18 12:42:38 +08:00
|
|
|
{ .compatible = "fsl,imx7d-i2c", .data = &imx6_i2c_hwdata, },
|
i2c: imx: Implement errata ERR007805 or e7805 bus frequency limit
The i.MX8MP Mask Set Errata for Mask 1P33A, Rev. 2.0 has description of
errata ERR007805 as below. This errata is found on all MX8M{M,N,P,Q},
MX7{S,D}, MX6{UL{,L,Z},S{,LL,X},S,D,DL,Q,DP,QP} . MX7ULP, MX8Q, MX8X
are not affected. MX53 and older status is unknown, as the errata
first appears in MX6 errata sheets from 2016 and the latest errata
sheet for MX53 is from 2015. Older SoC errata sheets predate the
MX53 errata sheet. MX8ULP and MX9 status is unknown as the errata
sheet is not available yet.
"
ERR007805 I2C: When the I2C clock speed is configured for 400 kHz,
the SCL low period violates the I2C spec of 1.3 uS min
Description: When the I2C module is programmed to operate at the
maximum clock speed of 400 kHz (as defined by the I2C spec), the SCL
clock low period violates the I2C spec of 1.3 uS min. The user must
reduce the clock speed to obtain the SCL low time to meet the 1.3us
I2C minimum required. This behavior means the SoC is not compliant
to the I2C spec at 400kHz.
Workaround: To meet the clock low period requirement in fast speed
mode, SCL must be configured to 384KHz or less.
"
Implement the workaround by matching on the affected SoC specific
compatible strings and by limiting the maximum bus frequency in case
the SoC is affected.
Signed-off-by: Marek Vasut <marex@denx.de>
To: linux-i2c@vger.kernel.org
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Wolfram Sang <wsa@kernel.org>
2022-04-08 19:15:24 +02:00
|
|
|
{ .compatible = "fsl,imx7s-i2c", .data = &imx6_i2c_hwdata, },
|
|
|
|
{ .compatible = "fsl,imx8mm-i2c", .data = &imx6_i2c_hwdata, },
|
|
|
|
{ .compatible = "fsl,imx8mn-i2c", .data = &imx6_i2c_hwdata, },
|
|
|
|
{ .compatible = "fsl,imx8mp-i2c", .data = &imx6_i2c_hwdata, },
|
|
|
|
{ .compatible = "fsl,imx8mq-i2c", .data = &imx6_i2c_hwdata, },
|
2013-08-07 17:05:43 +08:00
|
|
|
{ .compatible = "fsl,vf610-i2c", .data = &vf610_i2c_hwdata, },
|
2024-11-04 12:00:44 +02:00
|
|
|
{ .compatible = "nxp,s32g2-i2c", .data = &s32g2_i2c_hwdata, },
|
2011-09-08 15:09:35 +08:00
|
|
|
{ /* sentinel */ }
|
|
|
|
};
|
2013-06-20 23:07:06 +02:00
|
|
|
MODULE_DEVICE_TABLE(of, i2c_imx_dt_ids);
|
2011-09-08 15:09:35 +08:00
|
|
|
|
2019-09-06 15:53:19 +08:00
|
|
|
static const struct acpi_device_id i2c_imx_acpi_ids[] = {
|
|
|
|
{"NXP0001", .driver_data = (kernel_ulong_t)&vf610_i2c_hwdata},
|
|
|
|
{ }
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(acpi, i2c_imx_acpi_ids);
|
|
|
|
|
2012-09-14 15:19:00 +08:00
|
|
|
static inline int is_imx1_i2c(struct imx_i2c_struct *i2c_imx)
|
|
|
|
{
|
2013-08-07 17:05:42 +08:00
|
|
|
return i2c_imx->hwdata->devtype == IMX1_I2C;
|
2012-09-14 15:19:00 +08:00
|
|
|
}
|
|
|
|
|
2020-11-11 19:32:55 +08:00
|
|
|
static inline int is_vf610_i2c(struct imx_i2c_struct *i2c_imx)
|
|
|
|
{
|
|
|
|
return i2c_imx->hwdata->devtype == VF610_I2C;
|
|
|
|
}
|
|
|
|
|
2013-08-07 17:05:39 +08:00
|
|
|
static inline void imx_i2c_write_reg(unsigned int val,
|
|
|
|
struct imx_i2c_struct *i2c_imx, unsigned int reg)
|
|
|
|
{
|
2013-08-07 17:05:42 +08:00
|
|
|
writeb(val, i2c_imx->base + (reg << i2c_imx->hwdata->regshift));
|
2013-08-07 17:05:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline unsigned char imx_i2c_read_reg(struct imx_i2c_struct *i2c_imx,
|
|
|
|
unsigned int reg)
|
|
|
|
{
|
2013-08-07 17:05:42 +08:00
|
|
|
return readb(i2c_imx->base + (reg << i2c_imx->hwdata->regshift));
|
2013-08-07 17:05:39 +08:00
|
|
|
}
|
|
|
|
|
2020-11-11 19:32:55 +08:00
|
|
|
static void i2c_imx_clear_irq(struct imx_i2c_struct *i2c_imx, unsigned int bits)
|
|
|
|
{
|
|
|
|
unsigned int temp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* i2sr_clr_opcode is the value to clear all interrupts. Here we want to
|
|
|
|
* clear only <bits>, so we write ~i2sr_clr_opcode with just <bits>
|
|
|
|
* toggled. This is required because i.MX needs W0C and Vybrid uses W1C.
|
|
|
|
*/
|
|
|
|
temp = ~i2c_imx->hwdata->i2sr_clr_opcode ^ bits;
|
|
|
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set up i2c controller register and i2c status register to default value. */
|
|
|
|
static void i2c_imx_reset_regs(struct imx_i2c_struct *i2c_imx)
|
|
|
|
{
|
|
|
|
imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
|
|
|
|
i2c_imx, IMX_I2C_I2CR);
|
|
|
|
i2c_imx_clear_irq(i2c_imx, I2SR_IIF | I2SR_IAL);
|
|
|
|
}
|
|
|
|
|
2014-11-18 18:31:06 +08:00
|
|
|
/* Functions for DMA support */
|
2024-12-26 14:25:21 +08:00
|
|
|
static int i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, dma_addr_t phy_addr)
|
2014-11-18 18:31:06 +08:00
|
|
|
{
|
|
|
|
struct imx_i2c_dma *dma;
|
|
|
|
struct dma_slave_config dma_sconfig;
|
2024-12-26 14:25:21 +08:00
|
|
|
struct device *dev = i2c_imx->adapter.dev.parent;
|
2014-11-18 18:31:06 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
|
|
|
|
if (!dma)
|
2024-12-26 14:25:21 +08:00
|
|
|
return -ENOMEM;
|
2014-11-18 18:31:06 +08:00
|
|
|
|
2019-01-09 06:56:49 +01:00
|
|
|
dma->chan_tx = dma_request_chan(dev, "tx");
|
|
|
|
if (IS_ERR(dma->chan_tx)) {
|
2019-01-17 03:14:54 +00:00
|
|
|
ret = PTR_ERR(dma->chan_tx);
|
2019-01-09 06:56:49 +01:00
|
|
|
if (ret != -ENODEV && ret != -EPROBE_DEFER)
|
|
|
|
dev_err(dev, "can't request DMA tx channel (%d)\n", ret);
|
2014-11-18 18:31:06 +08:00
|
|
|
goto fail_al;
|
|
|
|
}
|
|
|
|
|
|
|
|
dma_sconfig.dst_addr = phy_addr +
|
|
|
|
(IMX_I2C_I2DR << i2c_imx->hwdata->regshift);
|
|
|
|
dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
|
|
|
dma_sconfig.dst_maxburst = 1;
|
|
|
|
dma_sconfig.direction = DMA_MEM_TO_DEV;
|
|
|
|
ret = dmaengine_slave_config(dma->chan_tx, &dma_sconfig);
|
|
|
|
if (ret < 0) {
|
2019-01-09 06:56:49 +01:00
|
|
|
dev_err(dev, "can't configure tx channel (%d)\n", ret);
|
2014-11-18 18:31:06 +08:00
|
|
|
goto fail_tx;
|
|
|
|
}
|
|
|
|
|
2019-01-09 06:56:49 +01:00
|
|
|
dma->chan_rx = dma_request_chan(dev, "rx");
|
|
|
|
if (IS_ERR(dma->chan_rx)) {
|
|
|
|
ret = PTR_ERR(dma->chan_rx);
|
|
|
|
if (ret != -ENODEV && ret != -EPROBE_DEFER)
|
|
|
|
dev_err(dev, "can't request DMA rx channel (%d)\n", ret);
|
2014-11-18 18:31:06 +08:00
|
|
|
goto fail_tx;
|
|
|
|
}
|
|
|
|
|
|
|
|
dma_sconfig.src_addr = phy_addr +
|
|
|
|
(IMX_I2C_I2DR << i2c_imx->hwdata->regshift);
|
|
|
|
dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
|
|
|
dma_sconfig.src_maxburst = 1;
|
|
|
|
dma_sconfig.direction = DMA_DEV_TO_MEM;
|
|
|
|
ret = dmaengine_slave_config(dma->chan_rx, &dma_sconfig);
|
|
|
|
if (ret < 0) {
|
2019-01-09 06:56:49 +01:00
|
|
|
dev_err(dev, "can't configure rx channel (%d)\n", ret);
|
2014-11-18 18:31:06 +08:00
|
|
|
goto fail_rx;
|
|
|
|
}
|
|
|
|
|
|
|
|
i2c_imx->dma = dma;
|
|
|
|
init_completion(&dma->cmd_complete);
|
|
|
|
dev_info(dev, "using %s (tx) and %s (rx) for DMA transfers\n",
|
|
|
|
dma_chan_name(dma->chan_tx), dma_chan_name(dma->chan_rx));
|
|
|
|
|
2024-12-26 14:25:21 +08:00
|
|
|
return 0;
|
2014-11-18 18:31:06 +08:00
|
|
|
|
|
|
|
fail_rx:
|
|
|
|
dma_release_channel(dma->chan_rx);
|
|
|
|
fail_tx:
|
|
|
|
dma_release_channel(dma->chan_tx);
|
|
|
|
fail_al:
|
|
|
|
devm_kfree(dev, dma);
|
2024-12-26 14:25:21 +08:00
|
|
|
|
|
|
|
return ret;
|
2014-11-18 18:31:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void i2c_imx_dma_callback(void *arg)
|
|
|
|
{
|
|
|
|
struct imx_i2c_struct *i2c_imx = (struct imx_i2c_struct *)arg;
|
|
|
|
struct imx_i2c_dma *dma = i2c_imx->dma;
|
|
|
|
|
|
|
|
dma_unmap_single(dma->chan_using->device->dev, dma->dma_buf,
|
|
|
|
dma->dma_len, dma->dma_data_dir);
|
|
|
|
complete(&dma->cmd_complete);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int i2c_imx_dma_xfer(struct imx_i2c_struct *i2c_imx,
|
|
|
|
struct i2c_msg *msgs)
|
|
|
|
{
|
|
|
|
struct imx_i2c_dma *dma = i2c_imx->dma;
|
|
|
|
struct dma_async_tx_descriptor *txdesc;
|
|
|
|
struct device *dev = &i2c_imx->adapter.dev;
|
|
|
|
struct device *chan_dev = dma->chan_using->device->dev;
|
|
|
|
|
|
|
|
dma->dma_buf = dma_map_single(chan_dev, msgs->buf,
|
|
|
|
dma->dma_len, dma->dma_data_dir);
|
|
|
|
if (dma_mapping_error(chan_dev, dma->dma_buf)) {
|
|
|
|
dev_err(dev, "DMA mapping failed\n");
|
|
|
|
goto err_map;
|
|
|
|
}
|
|
|
|
|
|
|
|
txdesc = dmaengine_prep_slave_single(dma->chan_using, dma->dma_buf,
|
|
|
|
dma->dma_len, dma->dma_transfer_dir,
|
|
|
|
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
|
|
|
if (!txdesc) {
|
|
|
|
dev_err(dev, "Not able to get desc for DMA xfer\n");
|
|
|
|
goto err_desc;
|
|
|
|
}
|
|
|
|
|
2018-07-09 11:43:01 +02:00
|
|
|
reinit_completion(&dma->cmd_complete);
|
2014-11-18 18:31:06 +08:00
|
|
|
txdesc->callback = i2c_imx_dma_callback;
|
|
|
|
txdesc->callback_param = i2c_imx;
|
|
|
|
if (dma_submit_error(dmaengine_submit(txdesc))) {
|
|
|
|
dev_err(dev, "DMA submit failed\n");
|
|
|
|
goto err_submit;
|
|
|
|
}
|
|
|
|
|
|
|
|
dma_async_issue_pending(dma->chan_using);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_submit:
|
2021-06-23 11:59:36 +02:00
|
|
|
dmaengine_terminate_sync(dma->chan_using);
|
2014-11-18 18:31:06 +08:00
|
|
|
err_desc:
|
|
|
|
dma_unmap_single(chan_dev, dma->dma_buf,
|
|
|
|
dma->dma_len, dma->dma_data_dir);
|
|
|
|
err_map:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void i2c_imx_dma_free(struct imx_i2c_struct *i2c_imx)
|
|
|
|
{
|
|
|
|
struct imx_i2c_dma *dma = i2c_imx->dma;
|
|
|
|
|
|
|
|
dma->dma_buf = 0;
|
|
|
|
dma->dma_len = 0;
|
|
|
|
|
|
|
|
dma_release_channel(dma->chan_tx);
|
|
|
|
dma->chan_tx = NULL;
|
|
|
|
|
|
|
|
dma_release_channel(dma->chan_rx);
|
|
|
|
dma->chan_rx = NULL;
|
|
|
|
|
|
|
|
dma->chan_using = NULL;
|
|
|
|
}
|
|
|
|
|
2020-01-20 10:36:50 +01:00
|
|
|
static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy, bool atomic)
|
2009-01-30 10:32:28 +02:00
|
|
|
{
|
2024-12-16 16:16:40 +01:00
|
|
|
bool multi_master = i2c_imx->multi_master;
|
2009-01-30 10:32:28 +02:00
|
|
|
unsigned long orig_jiffies = jiffies;
|
2009-10-17 17:46:22 +08:00
|
|
|
unsigned int temp;
|
2009-01-30 10:32:28 +02:00
|
|
|
|
2009-10-17 17:46:22 +08:00
|
|
|
while (1) {
|
2013-08-07 17:05:39 +08:00
|
|
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
|
i2c: imx: Add arbitration lost check
According to the i.mx spec, for multimaster mode, if I2C is
enabled when the bus is busy and asserts start, hardware inhibits
the transmission, clears MSTA without signaling a stop, generate
an interrupt, and set I2C_I2SR[IAL] to indicate a failed attempt
to engage the bus, which means arbitration lost. In this case,
we should first test I2C_I2SR[IAL], and clear this bit if it is
set, and then I2C controller default to slave receive mode.
This patch check the IAL bit every time before an I2c transmission.
if IAL is set, clear it and make I2C controller to default mode.
Signed-off-by: Haibo Chen <haibo.chen@freescale.com>
Acked-by: Fugang Duan <B38611@freescale.com>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
2014-09-03 13:52:07 +08:00
|
|
|
|
|
|
|
/* check for arbitration lost */
|
2024-12-16 16:16:40 +01:00
|
|
|
if (multi_master && (temp & I2SR_IAL)) {
|
2020-10-09 13:03:18 +02:00
|
|
|
i2c_imx_clear_irq(i2c_imx, I2SR_IAL);
|
i2c: imx: Add arbitration lost check
According to the i.mx spec, for multimaster mode, if I2C is
enabled when the bus is busy and asserts start, hardware inhibits
the transmission, clears MSTA without signaling a stop, generate
an interrupt, and set I2C_I2SR[IAL] to indicate a failed attempt
to engage the bus, which means arbitration lost. In this case,
we should first test I2C_I2SR[IAL], and clear this bit if it is
set, and then I2C controller default to slave receive mode.
This patch check the IAL bit every time before an I2c transmission.
if IAL is set, clear it and make I2C controller to default mode.
Signed-off-by: Haibo Chen <haibo.chen@freescale.com>
Acked-by: Fugang Duan <B38611@freescale.com>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
2014-09-03 13:52:07 +08:00
|
|
|
return -EAGAIN;
|
|
|
|
}
|
|
|
|
|
2024-12-16 16:16:40 +01:00
|
|
|
if (for_busy && (!multi_master || (temp & I2SR_IBB))) {
|
2018-08-16 10:43:13 +02:00
|
|
|
i2c_imx->stopped = 0;
|
2009-10-17 17:46:22 +08:00
|
|
|
break;
|
2018-08-16 10:43:13 +02:00
|
|
|
}
|
|
|
|
if (!for_busy && !(temp & I2SR_IBB)) {
|
|
|
|
i2c_imx->stopped = 1;
|
2009-10-17 17:46:22 +08:00
|
|
|
break;
|
2018-08-16 10:43:13 +02:00
|
|
|
}
|
2010-03-23 17:28:28 +01:00
|
|
|
if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) {
|
2009-01-30 10:32:28 +02:00
|
|
|
dev_dbg(&i2c_imx->adapter.dev,
|
|
|
|
"<%s> I2C bus is busy\n", __func__);
|
2010-03-23 17:28:28 +01:00
|
|
|
return -ETIMEDOUT;
|
2009-01-30 10:32:28 +02:00
|
|
|
}
|
2020-01-20 10:36:50 +01:00
|
|
|
if (atomic)
|
|
|
|
udelay(100);
|
|
|
|
else
|
|
|
|
schedule();
|
2009-01-30 10:32:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-01-20 10:36:50 +01:00
|
|
|
static int i2c_imx_trx_complete(struct imx_i2c_struct *i2c_imx, bool atomic)
|
2009-01-30 10:32:28 +02:00
|
|
|
{
|
2020-01-20 10:36:50 +01:00
|
|
|
if (atomic) {
|
|
|
|
void __iomem *addr = i2c_imx->base + (IMX_I2C_I2SR << i2c_imx->hwdata->regshift);
|
|
|
|
unsigned int regval;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The formula for the poll timeout is documented in the RM
|
|
|
|
* Rev.5 on page 1878:
|
|
|
|
* T_min = 10/F_scl
|
|
|
|
* Set the value hard as it is done for the non-atomic use-case.
|
|
|
|
* Use 10 kHz for the calculation since this is the minimum
|
|
|
|
* allowed SMBus frequency. Also add an offset of 100us since it
|
|
|
|
* turned out that the I2SR_IIF bit isn't set correctly within
|
|
|
|
* the minimum timeout in polling mode.
|
|
|
|
*/
|
|
|
|
readb_poll_timeout_atomic(addr, regval, regval & I2SR_IIF, 5, 1000 + 100);
|
|
|
|
i2c_imx->i2csr = regval;
|
2020-10-09 13:03:18 +02:00
|
|
|
i2c_imx_clear_irq(i2c_imx, I2SR_IIF | I2SR_IAL);
|
2020-01-20 10:36:50 +01:00
|
|
|
} else {
|
|
|
|
wait_event_timeout(i2c_imx->queue, i2c_imx->i2csr & I2SR_IIF, HZ / 10);
|
|
|
|
}
|
2009-01-30 10:32:28 +02:00
|
|
|
|
2010-06-21 09:27:05 +02:00
|
|
|
if (unlikely(!(i2c_imx->i2csr & I2SR_IIF))) {
|
2009-01-30 10:32:28 +02:00
|
|
|
dev_dbg(&i2c_imx->adapter.dev, "<%s> Timeout\n", __func__);
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
2020-10-09 13:03:19 +02:00
|
|
|
|
2024-10-14 15:15:12 +02:00
|
|
|
/* In multi-master mode check for arbitration lost */
|
|
|
|
if (i2c_imx->multi_master && (i2c_imx->i2csr & I2SR_IAL)) {
|
2020-10-09 13:03:19 +02:00
|
|
|
dev_dbg(&i2c_imx->adapter.dev, "<%s> Arbitration lost\n", __func__);
|
|
|
|
i2c_imx_clear_irq(i2c_imx, I2SR_IAL);
|
|
|
|
|
|
|
|
i2c_imx->i2csr = 0;
|
|
|
|
return -EAGAIN;
|
|
|
|
}
|
|
|
|
|
2009-01-30 10:32:28 +02:00
|
|
|
dev_dbg(&i2c_imx->adapter.dev, "<%s> TRX complete\n", __func__);
|
|
|
|
i2c_imx->i2csr = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int i2c_imx_acked(struct imx_i2c_struct *i2c_imx)
|
|
|
|
{
|
2013-08-07 17:05:39 +08:00
|
|
|
if (imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR) & I2SR_RXAK) {
|
2009-01-30 10:32:28 +02:00
|
|
|
dev_dbg(&i2c_imx->adapter.dev, "<%s> No ACK\n", __func__);
|
2015-10-22 14:41:20 -02:00
|
|
|
return -ENXIO; /* No ACK */
|
2009-01-30 10:32:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
dev_dbg(&i2c_imx->adapter.dev, "<%s> ACK received\n", __func__);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-11-25 22:15:21 +08:00
|
|
|
static int i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx,
|
|
|
|
unsigned int i2c_clk_rate)
|
2014-05-20 10:21:45 +08:00
|
|
|
{
|
|
|
|
struct imx_i2c_clk_pair *i2c_clk_div = i2c_imx->hwdata->clk_div;
|
|
|
|
unsigned int div;
|
|
|
|
int i;
|
|
|
|
|
i2c: imx: Implement errata ERR007805 or e7805 bus frequency limit
The i.MX8MP Mask Set Errata for Mask 1P33A, Rev. 2.0 has description of
errata ERR007805 as below. This errata is found on all MX8M{M,N,P,Q},
MX7{S,D}, MX6{UL{,L,Z},S{,LL,X},S,D,DL,Q,DP,QP} . MX7ULP, MX8Q, MX8X
are not affected. MX53 and older status is unknown, as the errata
first appears in MX6 errata sheets from 2016 and the latest errata
sheet for MX53 is from 2015. Older SoC errata sheets predate the
MX53 errata sheet. MX8ULP and MX9 status is unknown as the errata
sheet is not available yet.
"
ERR007805 I2C: When the I2C clock speed is configured for 400 kHz,
the SCL low period violates the I2C spec of 1.3 uS min
Description: When the I2C module is programmed to operate at the
maximum clock speed of 400 kHz (as defined by the I2C spec), the SCL
clock low period violates the I2C spec of 1.3 uS min. The user must
reduce the clock speed to obtain the SCL low time to meet the 1.3us
I2C minimum required. This behavior means the SoC is not compliant
to the I2C spec at 400kHz.
Workaround: To meet the clock low period requirement in fast speed
mode, SCL must be configured to 384KHz or less.
"
Implement the workaround by matching on the affected SoC specific
compatible strings and by limiting the maximum bus frequency in case
the SoC is affected.
Signed-off-by: Marek Vasut <marex@denx.de>
To: linux-i2c@vger.kernel.org
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Wolfram Sang <wsa@kernel.org>
2022-04-08 19:15:24 +02:00
|
|
|
if (i2c_imx->hwdata->has_err007805 && i2c_imx->bitrate > 384000) {
|
|
|
|
dev_dbg(&i2c_imx->adapter.dev,
|
|
|
|
"SoC errata ERR007805 or e7805 applies, bus frequency limited from %d Hz to 384000 Hz.\n",
|
|
|
|
i2c_imx->bitrate);
|
|
|
|
i2c_imx->bitrate = 384000;
|
|
|
|
}
|
|
|
|
|
2014-05-20 10:21:45 +08:00
|
|
|
/* Divider value calculation */
|
|
|
|
if (i2c_imx->cur_clk == i2c_clk_rate)
|
2024-11-25 22:15:21 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Keep the denominator of the following program always NOT equal to 0. */
|
|
|
|
if (!(i2c_clk_rate / 2))
|
|
|
|
return -EINVAL;
|
2015-01-22 16:17:29 +01:00
|
|
|
|
|
|
|
i2c_imx->cur_clk = i2c_clk_rate;
|
2014-05-20 10:21:45 +08:00
|
|
|
|
2020-12-22 21:31:31 +08:00
|
|
|
div = DIV_ROUND_UP(i2c_clk_rate, i2c_imx->bitrate);
|
2014-05-20 10:21:45 +08:00
|
|
|
if (div < i2c_clk_div[0].div)
|
|
|
|
i = 0;
|
|
|
|
else if (div > i2c_clk_div[i2c_imx->hwdata->ndivs - 1].div)
|
|
|
|
i = i2c_imx->hwdata->ndivs - 1;
|
|
|
|
else
|
2015-01-22 16:17:29 +01:00
|
|
|
for (i = 0; i2c_clk_div[i].div < div; i++)
|
|
|
|
;
|
2014-05-20 10:21:45 +08:00
|
|
|
|
|
|
|
/* Store divider value */
|
|
|
|
i2c_imx->ifdr = i2c_clk_div[i].val;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* There dummy delay is calculated.
|
|
|
|
* It should be about one I2C clock period long.
|
|
|
|
* This delay is used in I2C bus disable function
|
|
|
|
* to fix chip hardware bug.
|
|
|
|
*/
|
2020-12-22 21:31:31 +08:00
|
|
|
i2c_imx->disable_delay = DIV_ROUND_UP(500000U * i2c_clk_div[i].div,
|
|
|
|
i2c_clk_rate / 2);
|
2014-05-20 10:21:45 +08:00
|
|
|
|
|
|
|
#ifdef CONFIG_I2C_DEBUG_BUS
|
|
|
|
dev_dbg(&i2c_imx->adapter.dev, "I2C_CLK=%d, REQ DIV=%d\n",
|
|
|
|
i2c_clk_rate, div);
|
|
|
|
dev_dbg(&i2c_imx->adapter.dev, "IFDR[IC]=0x%x, REAL DIV=%d\n",
|
|
|
|
i2c_clk_div[i].val, i2c_clk_div[i].div);
|
|
|
|
#endif
|
2024-11-25 22:15:21 +08:00
|
|
|
|
|
|
|
return 0;
|
2014-05-20 10:21:45 +08:00
|
|
|
}
|
|
|
|
|
2018-03-08 14:25:17 +01:00
|
|
|
static int i2c_imx_clk_notifier_call(struct notifier_block *nb,
|
|
|
|
unsigned long action, void *data)
|
|
|
|
{
|
|
|
|
struct clk_notifier_data *ndata = data;
|
2019-04-17 01:59:34 +00:00
|
|
|
struct imx_i2c_struct *i2c_imx = container_of(nb,
|
2018-03-08 14:25:17 +01:00
|
|
|
struct imx_i2c_struct,
|
2019-04-17 01:59:34 +00:00
|
|
|
clk_change_nb);
|
2024-11-25 22:15:21 +08:00
|
|
|
int ret = 0;
|
2018-03-08 14:25:17 +01:00
|
|
|
|
|
|
|
if (action & POST_RATE_CHANGE)
|
2024-11-25 22:15:21 +08:00
|
|
|
ret = i2c_imx_set_clk(i2c_imx, ndata->new_rate);
|
2018-03-08 14:25:17 +01:00
|
|
|
|
2024-11-25 22:15:21 +08:00
|
|
|
return notifier_from_errno(ret);
|
2018-03-08 14:25:17 +01:00
|
|
|
}
|
|
|
|
|
2020-01-20 10:36:50 +01:00
|
|
|
static int i2c_imx_start(struct imx_i2c_struct *i2c_imx, bool atomic)
|
2009-01-30 10:32:28 +02:00
|
|
|
{
|
|
|
|
unsigned int temp = 0;
|
2009-10-17 17:46:22 +08:00
|
|
|
int result;
|
2009-01-30 10:32:28 +02:00
|
|
|
|
2013-08-07 17:05:39 +08:00
|
|
|
imx_i2c_write_reg(i2c_imx->ifdr, i2c_imx, IMX_I2C_IFDR);
|
2009-01-30 10:32:28 +02:00
|
|
|
/* Enable I2C controller */
|
2013-08-07 17:05:42 +08:00
|
|
|
imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);
|
|
|
|
imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode, i2c_imx, IMX_I2C_I2CR);
|
2009-10-17 17:46:22 +08:00
|
|
|
|
|
|
|
/* Wait controller to be stable */
|
2020-01-20 10:36:50 +01:00
|
|
|
if (atomic)
|
|
|
|
udelay(50);
|
|
|
|
else
|
|
|
|
usleep_range(50, 150);
|
2009-10-17 17:46:22 +08:00
|
|
|
|
2009-01-30 10:32:28 +02:00
|
|
|
/* Start I2C transaction */
|
2013-08-07 17:05:39 +08:00
|
|
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
2009-01-30 10:32:28 +02:00
|
|
|
temp |= I2CR_MSTA;
|
2013-08-07 17:05:39 +08:00
|
|
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
2020-01-20 10:36:50 +01:00
|
|
|
result = i2c_imx_bus_busy(i2c_imx, 1, atomic);
|
2009-10-17 17:46:22 +08:00
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
|
2009-01-30 10:32:28 +02:00
|
|
|
temp |= I2CR_IIEN | I2CR_MTX | I2CR_TXAK;
|
2020-01-20 10:36:50 +01:00
|
|
|
if (atomic)
|
|
|
|
temp &= ~I2CR_IIEN; /* Disable interrupt */
|
|
|
|
|
2014-11-18 18:31:06 +08:00
|
|
|
temp &= ~I2CR_DMAEN;
|
2013-08-07 17:05:39 +08:00
|
|
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
2009-10-17 17:46:22 +08:00
|
|
|
return result;
|
2009-01-30 10:32:28 +02:00
|
|
|
}
|
|
|
|
|
2020-01-20 10:36:50 +01:00
|
|
|
static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx, bool atomic)
|
2009-01-30 10:32:28 +02:00
|
|
|
{
|
|
|
|
unsigned int temp = 0;
|
|
|
|
|
2009-10-17 17:46:22 +08:00
|
|
|
if (!i2c_imx->stopped) {
|
|
|
|
/* Stop I2C transaction */
|
2013-08-07 17:05:39 +08:00
|
|
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
2020-10-09 13:03:20 +02:00
|
|
|
if (!(temp & I2CR_MSTA))
|
|
|
|
i2c_imx->stopped = 1;
|
2009-10-17 17:46:22 +08:00
|
|
|
temp &= ~(I2CR_MSTA | I2CR_MTX);
|
2014-11-18 18:31:06 +08:00
|
|
|
if (i2c_imx->dma)
|
|
|
|
temp &= ~I2CR_DMAEN;
|
2013-08-07 17:05:39 +08:00
|
|
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
2009-10-17 17:46:22 +08:00
|
|
|
}
|
2012-09-14 15:19:00 +08:00
|
|
|
if (is_imx1_i2c(i2c_imx)) {
|
2009-10-17 17:46:23 +08:00
|
|
|
/*
|
|
|
|
* This delay caused by an i.MXL hardware bug.
|
|
|
|
* If no (or too short) delay, no "STOP" bit will be generated.
|
|
|
|
*/
|
|
|
|
udelay(i2c_imx->disable_delay);
|
|
|
|
}
|
2009-10-17 17:46:22 +08:00
|
|
|
|
2018-08-16 10:43:13 +02:00
|
|
|
if (!i2c_imx->stopped)
|
2020-01-20 10:36:50 +01:00
|
|
|
i2c_imx_bus_busy(i2c_imx, 0, atomic);
|
2009-10-17 17:46:22 +08:00
|
|
|
|
2009-01-30 10:32:28 +02:00
|
|
|
/* Disable I2C controller */
|
2024-09-09 15:52:57 +08:00
|
|
|
temp = i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN;
|
2013-08-07 17:05:42 +08:00
|
|
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
2009-01-30 10:32:28 +02:00
|
|
|
}
|
|
|
|
|
2020-11-11 19:32:55 +08:00
|
|
|
/*
|
|
|
|
* Enable bus idle interrupts
|
|
|
|
* Note: IBIC register will be cleared after disabled i2c module.
|
|
|
|
* All of layerscape series SoCs support IBIC register.
|
|
|
|
*/
|
|
|
|
static void i2c_imx_enable_bus_idle(struct imx_i2c_struct *i2c_imx)
|
|
|
|
{
|
|
|
|
if (is_vf610_i2c(i2c_imx)) {
|
|
|
|
unsigned int temp;
|
|
|
|
|
|
|
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_IBIC);
|
|
|
|
temp |= IBIC_BIIE;
|
|
|
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_IBIC);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-22 11:48:50 -08:00
|
|
|
static void i2c_imx_slave_event(struct imx_i2c_struct *i2c_imx,
|
|
|
|
enum i2c_slave_event event, u8 *val)
|
|
|
|
{
|
|
|
|
i2c_slave_event(i2c_imx->slave, event, val);
|
|
|
|
i2c_imx->last_slave_event = event;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void i2c_imx_slave_finish_op(struct imx_i2c_struct *i2c_imx)
|
|
|
|
{
|
2021-11-12 07:39:55 -06:00
|
|
|
u8 val = 0;
|
2020-12-22 11:48:50 -08:00
|
|
|
|
|
|
|
while (i2c_imx->last_slave_event != I2C_SLAVE_STOP) {
|
|
|
|
switch (i2c_imx->last_slave_event) {
|
|
|
|
case I2C_SLAVE_READ_REQUESTED:
|
|
|
|
i2c_imx_slave_event(i2c_imx, I2C_SLAVE_READ_PROCESSED,
|
|
|
|
&val);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case I2C_SLAVE_WRITE_REQUESTED:
|
|
|
|
case I2C_SLAVE_READ_PROCESSED:
|
|
|
|
case I2C_SLAVE_WRITE_RECEIVED:
|
|
|
|
i2c_imx_slave_event(i2c_imx, I2C_SLAVE_STOP, &val);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case I2C_SLAVE_STOP:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-12 07:39:55 -06:00
|
|
|
/* Returns true if the timer should be restarted, false if not. */
|
|
|
|
static irqreturn_t i2c_imx_slave_handle(struct imx_i2c_struct *i2c_imx,
|
|
|
|
unsigned int status, unsigned int ctl)
|
2020-11-11 19:32:55 +08:00
|
|
|
{
|
2021-11-12 07:39:55 -06:00
|
|
|
u8 value = 0;
|
2020-11-11 19:32:55 +08:00
|
|
|
|
|
|
|
if (status & I2SR_IAL) { /* Arbitration lost */
|
|
|
|
i2c_imx_clear_irq(i2c_imx, I2SR_IAL);
|
|
|
|
if (!(status & I2SR_IAAS))
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
2021-11-12 07:39:55 -06:00
|
|
|
if (!(status & I2SR_IBB)) {
|
|
|
|
/* No master on the bus, that could mean a stop condition. */
|
|
|
|
i2c_imx_slave_finish_op(i2c_imx);
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(status & I2SR_ICF))
|
|
|
|
/* Data transfer still in progress, ignore this. */
|
|
|
|
goto out;
|
|
|
|
|
2020-11-11 19:32:55 +08:00
|
|
|
if (status & I2SR_IAAS) { /* Addressed as a slave */
|
2020-12-22 11:48:50 -08:00
|
|
|
i2c_imx_slave_finish_op(i2c_imx);
|
2020-11-11 19:32:55 +08:00
|
|
|
if (status & I2SR_SRW) { /* Master wants to read from us*/
|
|
|
|
dev_dbg(&i2c_imx->adapter.dev, "read requested");
|
2020-12-22 11:48:50 -08:00
|
|
|
i2c_imx_slave_event(i2c_imx,
|
|
|
|
I2C_SLAVE_READ_REQUESTED, &value);
|
2020-11-11 19:32:55 +08:00
|
|
|
|
|
|
|
/* Slave transmit */
|
|
|
|
ctl |= I2CR_MTX;
|
|
|
|
imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
|
|
|
|
|
|
|
|
/* Send data */
|
|
|
|
imx_i2c_write_reg(value, i2c_imx, IMX_I2C_I2DR);
|
|
|
|
} else { /* Master wants to write to us */
|
|
|
|
dev_dbg(&i2c_imx->adapter.dev, "write requested");
|
2020-12-22 11:48:50 -08:00
|
|
|
i2c_imx_slave_event(i2c_imx,
|
|
|
|
I2C_SLAVE_WRITE_REQUESTED, &value);
|
2020-11-11 19:32:55 +08:00
|
|
|
|
|
|
|
/* Slave receive */
|
|
|
|
ctl &= ~I2CR_MTX;
|
|
|
|
imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
|
|
|
|
/* Dummy read */
|
|
|
|
imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
|
|
|
|
}
|
|
|
|
} else if (!(ctl & I2CR_MTX)) { /* Receive mode */
|
2021-11-12 07:39:55 -06:00
|
|
|
value = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
|
|
|
|
i2c_imx_slave_event(i2c_imx,
|
|
|
|
I2C_SLAVE_WRITE_RECEIVED, &value);
|
2020-11-11 19:32:55 +08:00
|
|
|
} else if (!(status & I2SR_RXAK)) { /* Transmit mode received ACK */
|
|
|
|
ctl |= I2CR_MTX;
|
|
|
|
imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
|
|
|
|
|
2020-12-22 11:48:50 -08:00
|
|
|
i2c_imx_slave_event(i2c_imx,
|
|
|
|
I2C_SLAVE_READ_PROCESSED, &value);
|
2020-11-11 19:32:55 +08:00
|
|
|
|
|
|
|
imx_i2c_write_reg(value, i2c_imx, IMX_I2C_I2DR);
|
2021-11-12 07:39:55 -06:00
|
|
|
} else { /* Transmit mode received NAK, operation is done */
|
2020-11-11 19:32:55 +08:00
|
|
|
ctl &= ~I2CR_MTX;
|
|
|
|
imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
|
|
|
|
imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
|
2024-02-21 20:27:13 +01:00
|
|
|
|
|
|
|
/* flag the last byte as processed */
|
|
|
|
i2c_imx_slave_event(i2c_imx,
|
|
|
|
I2C_SLAVE_READ_PROCESSED, &value);
|
|
|
|
|
2021-11-12 07:39:55 -06:00
|
|
|
i2c_imx_slave_finish_op(i2c_imx);
|
|
|
|
return IRQ_HANDLED;
|
2020-11-11 19:32:55 +08:00
|
|
|
}
|
|
|
|
|
2021-11-12 07:39:55 -06:00
|
|
|
out:
|
|
|
|
/*
|
|
|
|
* No need to check the return value here. If it returns 0 or
|
|
|
|
* 1, then everything is fine. If it returns -1, then the
|
|
|
|
* timer is running in the handler. This will still work,
|
|
|
|
* though it may be redone (or already have been done) by the
|
|
|
|
* timer function.
|
|
|
|
*/
|
|
|
|
hrtimer_try_to_cancel(&i2c_imx->slave_timer);
|
|
|
|
hrtimer_forward_now(&i2c_imx->slave_timer, I2C_IMX_CHECK_DELAY);
|
|
|
|
hrtimer_restart(&i2c_imx->slave_timer);
|
2020-11-11 19:32:55 +08:00
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
2021-11-12 07:39:55 -06:00
|
|
|
static enum hrtimer_restart i2c_imx_slave_timeout(struct hrtimer *t)
|
|
|
|
{
|
|
|
|
struct imx_i2c_struct *i2c_imx = container_of(t, struct imx_i2c_struct,
|
|
|
|
slave_timer);
|
|
|
|
unsigned int ctl, status;
|
|
|
|
|
2025-05-31 14:57:26 +08:00
|
|
|
guard(spinlock_irqsave)(&i2c_imx->slave_lock);
|
|
|
|
|
2021-11-12 07:39:55 -06:00
|
|
|
status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
|
|
|
|
ctl = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
|
|
|
i2c_imx_slave_handle(i2c_imx, status, ctl);
|
2025-05-31 14:57:26 +08:00
|
|
|
|
2021-11-12 07:39:55 -06:00
|
|
|
return HRTIMER_NORESTART;
|
|
|
|
}
|
|
|
|
|
2020-11-11 19:32:55 +08:00
|
|
|
static void i2c_imx_slave_init(struct imx_i2c_struct *i2c_imx)
|
|
|
|
{
|
|
|
|
int temp;
|
|
|
|
|
|
|
|
/* Set slave addr. */
|
|
|
|
imx_i2c_write_reg((i2c_imx->slave->addr << 1), i2c_imx, IMX_I2C_IADR);
|
|
|
|
|
|
|
|
i2c_imx_reset_regs(i2c_imx);
|
|
|
|
|
|
|
|
/* Enable module */
|
|
|
|
temp = i2c_imx->hwdata->i2cr_ien_opcode;
|
|
|
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
|
|
|
|
|
|
|
/* Enable interrupt from i2c module */
|
|
|
|
temp |= I2CR_IIEN;
|
|
|
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
|
|
|
|
|
|
|
i2c_imx_enable_bus_idle(i2c_imx);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int i2c_imx_reg_slave(struct i2c_client *client)
|
|
|
|
{
|
|
|
|
struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(client->adapter);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (i2c_imx->slave)
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
i2c_imx->slave = client;
|
2020-12-22 11:48:50 -08:00
|
|
|
i2c_imx->last_slave_event = I2C_SLAVE_STOP;
|
2020-11-11 19:32:55 +08:00
|
|
|
|
|
|
|
/* Resume */
|
2021-04-08 19:06:38 +08:00
|
|
|
ret = pm_runtime_resume_and_get(i2c_imx->adapter.dev.parent);
|
2020-11-11 19:32:55 +08:00
|
|
|
if (ret < 0) {
|
|
|
|
dev_err(&i2c_imx->adapter.dev, "failed to resume i2c controller");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
i2c_imx_slave_init(i2c_imx);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int i2c_imx_unreg_slave(struct i2c_client *client)
|
|
|
|
{
|
|
|
|
struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(client->adapter);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!i2c_imx->slave)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* Reset slave address. */
|
|
|
|
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IADR);
|
|
|
|
|
|
|
|
i2c_imx_reset_regs(i2c_imx);
|
|
|
|
|
|
|
|
i2c_imx->slave = NULL;
|
|
|
|
|
|
|
|
/* Suspend */
|
|
|
|
ret = pm_runtime_put_sync(i2c_imx->adapter.dev.parent);
|
|
|
|
if (ret < 0)
|
|
|
|
dev_err(&i2c_imx->adapter.dev, "failed to suspend i2c controller");
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
i2c: imx: prevent rescheduling in non dma mode
We are experiencing a problem with the i.MX I2C controller when
communicating with SMBus devices. We are seeing devices time-out because
the time between sending/receiving two bytes is too long, and the SMBus
device returns to the idle state. This happens because the i.MX I2C
controller sends and receives byte by byte. When a byte is sent or
received, we get an interrupt and can send or receive the next byte.
The current implementation sends a byte and then waits for an event
generated by the interrupt subroutine. After the event is received, the
next byte is sent and we wait again. This waiting allows the scheduler
to reschedule other tasks, with the disadvantage that we may not send
the next byte for a long time because the send task is not immediately
scheduled. For example, if the rescheduling takes more than 25ms, this
can cause SMBus devices to timeout and communication to fail.
This patch changes the behavior so that we do not reschedule the
send/receive task, but instead send or receive the next byte in the
interrupt subroutine. This prevents rescheduling and drastically reduces
the time between sending/receiving bytes. The cost in the interrupt
subroutine is relatively small, we check what state we are in and then
send/receive the next byte. Before we had to call wake_up, which is even
less expensive. However, we also had to do some scheduling, which
increased the overall cost compared to the new solution. The wake_up
function to wake up the send/receive task is now only called when an
error occurs or when the transfer is complete.
Signed-off-by: Stefan Eichenberger <stefan.eichenberger@toradex.com>
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
2024-10-14 15:15:14 +02:00
|
|
|
static inline int i2c_imx_isr_acked(struct imx_i2c_struct *i2c_imx)
|
|
|
|
{
|
|
|
|
i2c_imx->isr_result = 0;
|
|
|
|
|
|
|
|
if (imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR) & I2SR_RXAK) {
|
|
|
|
i2c_imx->state = IMX_I2C_STATE_FAILED;
|
|
|
|
i2c_imx->isr_result = -ENXIO;
|
|
|
|
wake_up(&i2c_imx->queue);
|
|
|
|
}
|
|
|
|
|
|
|
|
return i2c_imx->isr_result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int i2c_imx_isr_write(struct imx_i2c_struct *i2c_imx)
|
|
|
|
{
|
|
|
|
int result;
|
|
|
|
|
|
|
|
result = i2c_imx_isr_acked(i2c_imx);
|
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
if (i2c_imx->msg->len == i2c_imx->msg_buf_idx)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
imx_i2c_write_reg(i2c_imx->msg->buf[i2c_imx->msg_buf_idx++], i2c_imx, IMX_I2C_I2DR);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int i2c_imx_isr_read(struct imx_i2c_struct *i2c_imx)
|
|
|
|
{
|
|
|
|
int result;
|
|
|
|
unsigned int temp;
|
|
|
|
|
|
|
|
result = i2c_imx_isr_acked(i2c_imx);
|
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
/* setup bus to read data */
|
|
|
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
|
|
|
temp &= ~I2CR_MTX;
|
2025-05-20 14:22:52 +02:00
|
|
|
if ((i2c_imx->msg->len - 1) || (i2c_imx->msg->flags & I2C_M_RECV_LEN))
|
i2c: imx: prevent rescheduling in non dma mode
We are experiencing a problem with the i.MX I2C controller when
communicating with SMBus devices. We are seeing devices time-out because
the time between sending/receiving two bytes is too long, and the SMBus
device returns to the idle state. This happens because the i.MX I2C
controller sends and receives byte by byte. When a byte is sent or
received, we get an interrupt and can send or receive the next byte.
The current implementation sends a byte and then waits for an event
generated by the interrupt subroutine. After the event is received, the
next byte is sent and we wait again. This waiting allows the scheduler
to reschedule other tasks, with the disadvantage that we may not send
the next byte for a long time because the send task is not immediately
scheduled. For example, if the rescheduling takes more than 25ms, this
can cause SMBus devices to timeout and communication to fail.
This patch changes the behavior so that we do not reschedule the
send/receive task, but instead send or receive the next byte in the
interrupt subroutine. This prevents rescheduling and drastically reduces
the time between sending/receiving bytes. The cost in the interrupt
subroutine is relatively small, we check what state we are in and then
send/receive the next byte. Before we had to call wake_up, which is even
less expensive. However, we also had to do some scheduling, which
increased the overall cost compared to the new solution. The wake_up
function to wake up the send/receive task is now only called when an
error occurs or when the transfer is complete.
Signed-off-by: Stefan Eichenberger <stefan.eichenberger@toradex.com>
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
2024-10-14 15:15:14 +02:00
|
|
|
temp &= ~I2CR_TXAK;
|
|
|
|
|
|
|
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
|
|
|
imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); /* dummy read */
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void i2c_imx_isr_read_continue(struct imx_i2c_struct *i2c_imx)
|
|
|
|
{
|
|
|
|
unsigned int temp;
|
|
|
|
|
|
|
|
if ((i2c_imx->msg->len - 1) == i2c_imx->msg_buf_idx) {
|
|
|
|
if (i2c_imx->is_lastmsg) {
|
|
|
|
/*
|
|
|
|
* It must generate STOP before read I2DR to prevent
|
|
|
|
* controller from generating another clock cycle
|
|
|
|
*/
|
|
|
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
|
|
|
if (!(temp & I2CR_MSTA))
|
|
|
|
i2c_imx->stopped = 1;
|
|
|
|
temp &= ~(I2CR_MSTA | I2CR_MTX);
|
|
|
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* For i2c master receiver repeat restart operation like:
|
|
|
|
* read -> repeat MSTA -> read/write
|
|
|
|
* The controller must set MTX before read the last byte in
|
|
|
|
* the first read operation, otherwise the first read cost
|
|
|
|
* one extra clock cycle.
|
|
|
|
*/
|
|
|
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
|
|
|
temp |= I2CR_MTX;
|
|
|
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
|
|
|
}
|
|
|
|
} else if (i2c_imx->msg_buf_idx == (i2c_imx->msg->len - 2)) {
|
|
|
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
|
|
|
temp |= I2CR_TXAK;
|
|
|
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
|
|
|
}
|
|
|
|
|
|
|
|
i2c_imx->msg->buf[i2c_imx->msg_buf_idx++] = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void i2c_imx_isr_read_block_data_len(struct imx_i2c_struct *i2c_imx)
|
|
|
|
{
|
|
|
|
u8 len = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
|
|
|
|
|
|
|
|
if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) {
|
|
|
|
i2c_imx->isr_result = -EPROTO;
|
|
|
|
i2c_imx->state = IMX_I2C_STATE_FAILED;
|
|
|
|
wake_up(&i2c_imx->queue);
|
|
|
|
}
|
|
|
|
i2c_imx->msg->len += len;
|
2025-05-20 14:22:52 +02:00
|
|
|
i2c_imx->msg->buf[i2c_imx->msg_buf_idx++] = len;
|
i2c: imx: prevent rescheduling in non dma mode
We are experiencing a problem with the i.MX I2C controller when
communicating with SMBus devices. We are seeing devices time-out because
the time between sending/receiving two bytes is too long, and the SMBus
device returns to the idle state. This happens because the i.MX I2C
controller sends and receives byte by byte. When a byte is sent or
received, we get an interrupt and can send or receive the next byte.
The current implementation sends a byte and then waits for an event
generated by the interrupt subroutine. After the event is received, the
next byte is sent and we wait again. This waiting allows the scheduler
to reschedule other tasks, with the disadvantage that we may not send
the next byte for a long time because the send task is not immediately
scheduled. For example, if the rescheduling takes more than 25ms, this
can cause SMBus devices to timeout and communication to fail.
This patch changes the behavior so that we do not reschedule the
send/receive task, but instead send or receive the next byte in the
interrupt subroutine. This prevents rescheduling and drastically reduces
the time between sending/receiving bytes. The cost in the interrupt
subroutine is relatively small, we check what state we are in and then
send/receive the next byte. Before we had to call wake_up, which is even
less expensive. However, we also had to do some scheduling, which
increased the overall cost compared to the new solution. The wake_up
function to wake up the send/receive task is now only called when an
error occurs or when the transfer is complete.
Signed-off-by: Stefan Eichenberger <stefan.eichenberger@toradex.com>
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
2024-10-14 15:15:14 +02:00
|
|
|
}
|
|
|
|
|
2020-11-11 19:32:55 +08:00
|
|
|
static irqreturn_t i2c_imx_master_isr(struct imx_i2c_struct *i2c_imx, unsigned int status)
|
|
|
|
{
|
i2c: imx: prevent rescheduling in non dma mode
We are experiencing a problem with the i.MX I2C controller when
communicating with SMBus devices. We are seeing devices time-out because
the time between sending/receiving two bytes is too long, and the SMBus
device returns to the idle state. This happens because the i.MX I2C
controller sends and receives byte by byte. When a byte is sent or
received, we get an interrupt and can send or receive the next byte.
The current implementation sends a byte and then waits for an event
generated by the interrupt subroutine. After the event is received, the
next byte is sent and we wait again. This waiting allows the scheduler
to reschedule other tasks, with the disadvantage that we may not send
the next byte for a long time because the send task is not immediately
scheduled. For example, if the rescheduling takes more than 25ms, this
can cause SMBus devices to timeout and communication to fail.
This patch changes the behavior so that we do not reschedule the
send/receive task, but instead send or receive the next byte in the
interrupt subroutine. This prevents rescheduling and drastically reduces
the time between sending/receiving bytes. The cost in the interrupt
subroutine is relatively small, we check what state we are in and then
send/receive the next byte. Before we had to call wake_up, which is even
less expensive. However, we also had to do some scheduling, which
increased the overall cost compared to the new solution. The wake_up
function to wake up the send/receive task is now only called when an
error occurs or when the transfer is complete.
Signed-off-by: Stefan Eichenberger <stefan.eichenberger@toradex.com>
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
2024-10-14 15:15:14 +02:00
|
|
|
/*
|
|
|
|
* This state machine handles I2C reception and transmission in non-DMA
|
|
|
|
* mode. We must process all the data in the ISR to reduce the delay
|
|
|
|
* between two consecutive messages. If the data is not processed in
|
|
|
|
* the ISR, SMBus devices may timeout, leading to a bus error.
|
|
|
|
*/
|
|
|
|
switch (i2c_imx->state) {
|
|
|
|
case IMX_I2C_STATE_DMA:
|
|
|
|
i2c_imx->i2csr = status;
|
|
|
|
wake_up(&i2c_imx->queue);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IMX_I2C_STATE_READ:
|
|
|
|
if (i2c_imx_isr_read(i2c_imx))
|
|
|
|
break;
|
|
|
|
i2c_imx->state = IMX_I2C_STATE_READ_CONTINUE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IMX_I2C_STATE_READ_CONTINUE:
|
|
|
|
i2c_imx_isr_read_continue(i2c_imx);
|
|
|
|
if (i2c_imx->msg_buf_idx == i2c_imx->msg->len) {
|
|
|
|
i2c_imx->state = IMX_I2C_STATE_DONE;
|
|
|
|
wake_up(&i2c_imx->queue);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IMX_I2C_STATE_READ_BLOCK_DATA:
|
|
|
|
if (i2c_imx_isr_read(i2c_imx))
|
|
|
|
break;
|
|
|
|
i2c_imx->state = IMX_I2C_STATE_READ_BLOCK_DATA_LEN;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IMX_I2C_STATE_READ_BLOCK_DATA_LEN:
|
|
|
|
i2c_imx_isr_read_block_data_len(i2c_imx);
|
|
|
|
i2c_imx->state = IMX_I2C_STATE_READ_CONTINUE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IMX_I2C_STATE_WRITE:
|
|
|
|
if (i2c_imx_isr_write(i2c_imx))
|
|
|
|
break;
|
|
|
|
i2c_imx->state = IMX_I2C_STATE_DONE;
|
|
|
|
wake_up(&i2c_imx->queue);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
i2c_imx->i2csr = status;
|
|
|
|
i2c_imx->state = IMX_I2C_STATE_FAILED;
|
|
|
|
i2c_imx->isr_result = -EINVAL;
|
|
|
|
wake_up(&i2c_imx->queue);
|
|
|
|
}
|
2020-11-11 19:32:55 +08:00
|
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
2009-01-30 10:32:28 +02:00
|
|
|
static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
|
|
|
|
{
|
|
|
|
struct imx_i2c_struct *i2c_imx = dev_id;
|
2020-11-11 19:32:55 +08:00
|
|
|
unsigned int ctl, status;
|
2009-01-30 10:32:28 +02:00
|
|
|
|
2025-05-31 14:57:26 +08:00
|
|
|
scoped_guard(spinlock_irqsave, &i2c_imx->slave_lock) {
|
|
|
|
status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
|
|
|
|
ctl = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
|
|
|
|
|
|
|
if (!(status & I2SR_IIF))
|
|
|
|
return IRQ_NONE;
|
2020-12-22 11:48:50 -08:00
|
|
|
|
2020-10-09 13:03:18 +02:00
|
|
|
i2c_imx_clear_irq(i2c_imx, I2SR_IIF);
|
2025-05-31 14:57:26 +08:00
|
|
|
|
2020-12-22 11:48:50 -08:00
|
|
|
if (i2c_imx->slave) {
|
2025-05-31 14:57:26 +08:00
|
|
|
if (!(ctl & I2CR_MSTA))
|
|
|
|
return i2c_imx_slave_handle(i2c_imx,
|
|
|
|
status, ctl);
|
|
|
|
|
2021-11-12 07:39:55 -06:00
|
|
|
i2c_imx_slave_finish_op(i2c_imx);
|
2020-12-22 11:48:50 -08:00
|
|
|
}
|
2009-01-30 10:32:28 +02:00
|
|
|
}
|
|
|
|
|
2025-05-31 14:57:26 +08:00
|
|
|
return i2c_imx_master_isr(i2c_imx, status);
|
2009-01-30 10:32:28 +02:00
|
|
|
}
|
|
|
|
|
2014-11-18 18:31:06 +08:00
|
|
|
static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx,
|
|
|
|
struct i2c_msg *msgs)
|
|
|
|
{
|
|
|
|
int result;
|
2015-02-08 06:39:56 -05:00
|
|
|
unsigned long time_left;
|
2014-11-18 18:31:06 +08:00
|
|
|
unsigned int temp = 0;
|
|
|
|
unsigned long orig_jiffies = jiffies;
|
|
|
|
struct imx_i2c_dma *dma = i2c_imx->dma;
|
|
|
|
struct device *dev = &i2c_imx->adapter.dev;
|
|
|
|
|
i2c: imx: prevent rescheduling in non dma mode
We are experiencing a problem with the i.MX I2C controller when
communicating with SMBus devices. We are seeing devices time-out because
the time between sending/receiving two bytes is too long, and the SMBus
device returns to the idle state. This happens because the i.MX I2C
controller sends and receives byte by byte. When a byte is sent or
received, we get an interrupt and can send or receive the next byte.
The current implementation sends a byte and then waits for an event
generated by the interrupt subroutine. After the event is received, the
next byte is sent and we wait again. This waiting allows the scheduler
to reschedule other tasks, with the disadvantage that we may not send
the next byte for a long time because the send task is not immediately
scheduled. For example, if the rescheduling takes more than 25ms, this
can cause SMBus devices to timeout and communication to fail.
This patch changes the behavior so that we do not reschedule the
send/receive task, but instead send or receive the next byte in the
interrupt subroutine. This prevents rescheduling and drastically reduces
the time between sending/receiving bytes. The cost in the interrupt
subroutine is relatively small, we check what state we are in and then
send/receive the next byte. Before we had to call wake_up, which is even
less expensive. However, we also had to do some scheduling, which
increased the overall cost compared to the new solution. The wake_up
function to wake up the send/receive task is now only called when an
error occurs or when the transfer is complete.
Signed-off-by: Stefan Eichenberger <stefan.eichenberger@toradex.com>
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
2024-10-14 15:15:14 +02:00
|
|
|
i2c_imx->state = IMX_I2C_STATE_DMA;
|
|
|
|
|
2014-11-18 18:31:06 +08:00
|
|
|
dma->chan_using = dma->chan_tx;
|
|
|
|
dma->dma_transfer_dir = DMA_MEM_TO_DEV;
|
|
|
|
dma->dma_data_dir = DMA_TO_DEVICE;
|
|
|
|
dma->dma_len = msgs->len - 1;
|
|
|
|
result = i2c_imx_dma_xfer(i2c_imx, msgs);
|
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
|
|
|
temp |= I2CR_DMAEN;
|
|
|
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write slave address.
|
|
|
|
* The first byte must be transmitted by the CPU.
|
|
|
|
*/
|
2018-05-16 09:16:47 +02:00
|
|
|
imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR);
|
2015-02-08 06:39:56 -05:00
|
|
|
time_left = wait_for_completion_timeout(
|
2014-11-18 18:31:06 +08:00
|
|
|
&i2c_imx->dma->cmd_complete,
|
|
|
|
msecs_to_jiffies(DMA_TIMEOUT));
|
2015-02-08 06:39:56 -05:00
|
|
|
if (time_left == 0) {
|
2021-06-23 11:59:36 +02:00
|
|
|
dmaengine_terminate_sync(dma->chan_using);
|
2014-12-27 08:33:53 -05:00
|
|
|
return -ETIMEDOUT;
|
2014-11-18 18:31:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Waiting for transfer complete. */
|
|
|
|
while (1) {
|
|
|
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
|
|
|
|
if (temp & I2SR_ICF)
|
|
|
|
break;
|
|
|
|
if (time_after(jiffies, orig_jiffies +
|
|
|
|
msecs_to_jiffies(DMA_TIMEOUT))) {
|
|
|
|
dev_dbg(dev, "<%s> Timeout\n", __func__);
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
schedule();
|
|
|
|
}
|
|
|
|
|
|
|
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
|
|
|
temp &= ~I2CR_DMAEN;
|
|
|
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
|
|
|
|
|
|
|
/* The last data byte must be transferred by the CPU. */
|
|
|
|
imx_i2c_write_reg(msgs->buf[msgs->len-1],
|
|
|
|
i2c_imx, IMX_I2C_I2DR);
|
2020-01-20 10:36:50 +01:00
|
|
|
result = i2c_imx_trx_complete(i2c_imx, false);
|
2014-11-18 18:31:06 +08:00
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
|
2014-11-19 10:11:39 +01:00
|
|
|
return i2c_imx_acked(i2c_imx);
|
2014-11-18 18:31:06 +08:00
|
|
|
}
|
|
|
|
|
2024-10-14 15:15:13 +02:00
|
|
|
static int i2c_imx_prepare_read(struct imx_i2c_struct *i2c_imx,
|
i2c: imx: prevent rescheduling in non dma mode
We are experiencing a problem with the i.MX I2C controller when
communicating with SMBus devices. We are seeing devices time-out because
the time between sending/receiving two bytes is too long, and the SMBus
device returns to the idle state. This happens because the i.MX I2C
controller sends and receives byte by byte. When a byte is sent or
received, we get an interrupt and can send or receive the next byte.
The current implementation sends a byte and then waits for an event
generated by the interrupt subroutine. After the event is received, the
next byte is sent and we wait again. This waiting allows the scheduler
to reschedule other tasks, with the disadvantage that we may not send
the next byte for a long time because the send task is not immediately
scheduled. For example, if the rescheduling takes more than 25ms, this
can cause SMBus devices to timeout and communication to fail.
This patch changes the behavior so that we do not reschedule the
send/receive task, but instead send or receive the next byte in the
interrupt subroutine. This prevents rescheduling and drastically reduces
the time between sending/receiving bytes. The cost in the interrupt
subroutine is relatively small, we check what state we are in and then
send/receive the next byte. Before we had to call wake_up, which is even
less expensive. However, we also had to do some scheduling, which
increased the overall cost compared to the new solution. The wake_up
function to wake up the send/receive task is now only called when an
error occurs or when the transfer is complete.
Signed-off-by: Stefan Eichenberger <stefan.eichenberger@toradex.com>
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
2024-10-14 15:15:14 +02:00
|
|
|
struct i2c_msg *msgs, bool use_dma)
|
2024-10-14 15:15:13 +02:00
|
|
|
{
|
|
|
|
int result;
|
|
|
|
unsigned int temp = 0;
|
|
|
|
|
|
|
|
/* write slave address */
|
|
|
|
imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR);
|
i2c: imx: prevent rescheduling in non dma mode
We are experiencing a problem with the i.MX I2C controller when
communicating with SMBus devices. We are seeing devices time-out because
the time between sending/receiving two bytes is too long, and the SMBus
device returns to the idle state. This happens because the i.MX I2C
controller sends and receives byte by byte. When a byte is sent or
received, we get an interrupt and can send or receive the next byte.
The current implementation sends a byte and then waits for an event
generated by the interrupt subroutine. After the event is received, the
next byte is sent and we wait again. This waiting allows the scheduler
to reschedule other tasks, with the disadvantage that we may not send
the next byte for a long time because the send task is not immediately
scheduled. For example, if the rescheduling takes more than 25ms, this
can cause SMBus devices to timeout and communication to fail.
This patch changes the behavior so that we do not reschedule the
send/receive task, but instead send or receive the next byte in the
interrupt subroutine. This prevents rescheduling and drastically reduces
the time between sending/receiving bytes. The cost in the interrupt
subroutine is relatively small, we check what state we are in and then
send/receive the next byte. Before we had to call wake_up, which is even
less expensive. However, we also had to do some scheduling, which
increased the overall cost compared to the new solution. The wake_up
function to wake up the send/receive task is now only called when an
error occurs or when the transfer is complete.
Signed-off-by: Stefan Eichenberger <stefan.eichenberger@toradex.com>
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
2024-10-14 15:15:14 +02:00
|
|
|
result = i2c_imx_trx_complete(i2c_imx, !use_dma);
|
2024-10-14 15:15:13 +02:00
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
result = i2c_imx_acked(i2c_imx);
|
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
dev_dbg(&i2c_imx->adapter.dev, "<%s> setup bus\n", __func__);
|
|
|
|
|
|
|
|
/* setup bus to read data */
|
|
|
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
|
|
|
temp &= ~I2CR_MTX;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reset the I2CR_TXAK flag initially for SMBus block read since the
|
|
|
|
* length is unknown
|
|
|
|
*/
|
|
|
|
if (msgs->len - 1)
|
|
|
|
temp &= ~I2CR_TXAK;
|
|
|
|
if (use_dma)
|
|
|
|
temp |= I2CR_DMAEN;
|
|
|
|
|
|
|
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
|
|
|
imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); /* dummy read */
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-18 18:31:06 +08:00
|
|
|
static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx,
|
|
|
|
struct i2c_msg *msgs, bool is_lastmsg)
|
|
|
|
{
|
|
|
|
int result;
|
2015-02-08 06:39:56 -05:00
|
|
|
unsigned long time_left;
|
2014-11-18 18:31:06 +08:00
|
|
|
unsigned int temp;
|
|
|
|
unsigned long orig_jiffies = jiffies;
|
|
|
|
struct imx_i2c_dma *dma = i2c_imx->dma;
|
|
|
|
struct device *dev = &i2c_imx->adapter.dev;
|
|
|
|
|
i2c: imx: prevent rescheduling in non dma mode
We are experiencing a problem with the i.MX I2C controller when
communicating with SMBus devices. We are seeing devices time-out because
the time between sending/receiving two bytes is too long, and the SMBus
device returns to the idle state. This happens because the i.MX I2C
controller sends and receives byte by byte. When a byte is sent or
received, we get an interrupt and can send or receive the next byte.
The current implementation sends a byte and then waits for an event
generated by the interrupt subroutine. After the event is received, the
next byte is sent and we wait again. This waiting allows the scheduler
to reschedule other tasks, with the disadvantage that we may not send
the next byte for a long time because the send task is not immediately
scheduled. For example, if the rescheduling takes more than 25ms, this
can cause SMBus devices to timeout and communication to fail.
This patch changes the behavior so that we do not reschedule the
send/receive task, but instead send or receive the next byte in the
interrupt subroutine. This prevents rescheduling and drastically reduces
the time between sending/receiving bytes. The cost in the interrupt
subroutine is relatively small, we check what state we are in and then
send/receive the next byte. Before we had to call wake_up, which is even
less expensive. However, we also had to do some scheduling, which
increased the overall cost compared to the new solution. The wake_up
function to wake up the send/receive task is now only called when an
error occurs or when the transfer is complete.
Signed-off-by: Stefan Eichenberger <stefan.eichenberger@toradex.com>
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
2024-10-14 15:15:14 +02:00
|
|
|
i2c_imx->state = IMX_I2C_STATE_DMA;
|
|
|
|
|
|
|
|
result = i2c_imx_prepare_read(i2c_imx, msgs, true);
|
2024-10-14 15:15:13 +02:00
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
dev_dbg(&i2c_imx->adapter.dev, "<%s> read data\n", __func__);
|
2014-11-18 18:31:06 +08:00
|
|
|
|
|
|
|
dma->chan_using = dma->chan_rx;
|
|
|
|
dma->dma_transfer_dir = DMA_DEV_TO_MEM;
|
|
|
|
dma->dma_data_dir = DMA_FROM_DEVICE;
|
|
|
|
/* The last two data bytes must be transferred by the CPU. */
|
|
|
|
dma->dma_len = msgs->len - 2;
|
|
|
|
result = i2c_imx_dma_xfer(i2c_imx, msgs);
|
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
|
2015-02-08 06:39:56 -05:00
|
|
|
time_left = wait_for_completion_timeout(
|
2014-11-18 18:31:06 +08:00
|
|
|
&i2c_imx->dma->cmd_complete,
|
|
|
|
msecs_to_jiffies(DMA_TIMEOUT));
|
2015-02-08 06:39:56 -05:00
|
|
|
if (time_left == 0) {
|
2021-06-23 11:59:36 +02:00
|
|
|
dmaengine_terminate_sync(dma->chan_using);
|
2014-12-27 08:33:53 -05:00
|
|
|
return -ETIMEDOUT;
|
2014-11-18 18:31:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* waiting for transfer complete. */
|
|
|
|
while (1) {
|
|
|
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
|
|
|
|
if (temp & I2SR_ICF)
|
|
|
|
break;
|
|
|
|
if (time_after(jiffies, orig_jiffies +
|
|
|
|
msecs_to_jiffies(DMA_TIMEOUT))) {
|
|
|
|
dev_dbg(dev, "<%s> Timeout\n", __func__);
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
schedule();
|
|
|
|
}
|
|
|
|
|
|
|
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
|
|
|
temp &= ~I2CR_DMAEN;
|
|
|
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
|
|
|
|
|
|
|
/* read n-1 byte data */
|
|
|
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
|
|
|
temp |= I2CR_TXAK;
|
|
|
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
|
|
|
|
|
|
|
msgs->buf[msgs->len-2] = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
|
|
|
|
/* read n byte data */
|
2020-01-20 10:36:50 +01:00
|
|
|
result = i2c_imx_trx_complete(i2c_imx, false);
|
2014-11-18 18:31:06 +08:00
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
if (is_lastmsg) {
|
|
|
|
/*
|
|
|
|
* It must generate STOP before read I2DR to prevent
|
|
|
|
* controller from generating another clock cycle
|
|
|
|
*/
|
|
|
|
dev_dbg(dev, "<%s> clear MSTA\n", __func__);
|
|
|
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
2020-10-09 13:03:20 +02:00
|
|
|
if (!(temp & I2CR_MSTA))
|
|
|
|
i2c_imx->stopped = 1;
|
2014-11-18 18:31:06 +08:00
|
|
|
temp &= ~(I2CR_MSTA | I2CR_MTX);
|
|
|
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
2020-10-09 13:03:20 +02:00
|
|
|
if (!i2c_imx->stopped)
|
|
|
|
i2c_imx_bus_busy(i2c_imx, 0, false);
|
2014-11-18 18:31:06 +08:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* For i2c master receiver repeat restart operation like:
|
|
|
|
* read -> repeat MSTA -> read/write
|
|
|
|
* The controller must set MTX before read the last byte in
|
|
|
|
* the first read operation, otherwise the first read cost
|
|
|
|
* one extra clock cycle.
|
|
|
|
*/
|
2017-06-20 10:20:42 +02:00
|
|
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
2014-11-18 18:31:06 +08:00
|
|
|
temp |= I2CR_MTX;
|
2017-06-20 10:20:42 +02:00
|
|
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
2014-11-18 18:31:06 +08:00
|
|
|
}
|
|
|
|
msgs->buf[msgs->len-1] = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
i2c: imx: prevent rescheduling in non dma mode
We are experiencing a problem with the i.MX I2C controller when
communicating with SMBus devices. We are seeing devices time-out because
the time between sending/receiving two bytes is too long, and the SMBus
device returns to the idle state. This happens because the i.MX I2C
controller sends and receives byte by byte. When a byte is sent or
received, we get an interrupt and can send or receive the next byte.
The current implementation sends a byte and then waits for an event
generated by the interrupt subroutine. After the event is received, the
next byte is sent and we wait again. This waiting allows the scheduler
to reschedule other tasks, with the disadvantage that we may not send
the next byte for a long time because the send task is not immediately
scheduled. For example, if the rescheduling takes more than 25ms, this
can cause SMBus devices to timeout and communication to fail.
This patch changes the behavior so that we do not reschedule the
send/receive task, but instead send or receive the next byte in the
interrupt subroutine. This prevents rescheduling and drastically reduces
the time between sending/receiving bytes. The cost in the interrupt
subroutine is relatively small, we check what state we are in and then
send/receive the next byte. Before we had to call wake_up, which is even
less expensive. However, we also had to do some scheduling, which
increased the overall cost compared to the new solution. The wake_up
function to wake up the send/receive task is now only called when an
error occurs or when the transfer is complete.
Signed-off-by: Stefan Eichenberger <stefan.eichenberger@toradex.com>
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
2024-10-14 15:15:14 +02:00
|
|
|
static int i2c_imx_atomic_write(struct imx_i2c_struct *i2c_imx,
|
|
|
|
struct i2c_msg *msgs)
|
2009-01-30 10:32:28 +02:00
|
|
|
{
|
|
|
|
int i, result;
|
|
|
|
|
|
|
|
dev_dbg(&i2c_imx->adapter.dev, "<%s> write slave address: addr=0x%x\n",
|
2018-05-16 09:16:47 +02:00
|
|
|
__func__, i2c_8bit_addr_from_msg(msgs));
|
2009-01-30 10:32:28 +02:00
|
|
|
|
|
|
|
/* write slave address */
|
2018-05-16 09:16:47 +02:00
|
|
|
imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR);
|
i2c: imx: prevent rescheduling in non dma mode
We are experiencing a problem with the i.MX I2C controller when
communicating with SMBus devices. We are seeing devices time-out because
the time between sending/receiving two bytes is too long, and the SMBus
device returns to the idle state. This happens because the i.MX I2C
controller sends and receives byte by byte. When a byte is sent or
received, we get an interrupt and can send or receive the next byte.
The current implementation sends a byte and then waits for an event
generated by the interrupt subroutine. After the event is received, the
next byte is sent and we wait again. This waiting allows the scheduler
to reschedule other tasks, with the disadvantage that we may not send
the next byte for a long time because the send task is not immediately
scheduled. For example, if the rescheduling takes more than 25ms, this
can cause SMBus devices to timeout and communication to fail.
This patch changes the behavior so that we do not reschedule the
send/receive task, but instead send or receive the next byte in the
interrupt subroutine. This prevents rescheduling and drastically reduces
the time between sending/receiving bytes. The cost in the interrupt
subroutine is relatively small, we check what state we are in and then
send/receive the next byte. Before we had to call wake_up, which is even
less expensive. However, we also had to do some scheduling, which
increased the overall cost compared to the new solution. The wake_up
function to wake up the send/receive task is now only called when an
error occurs or when the transfer is complete.
Signed-off-by: Stefan Eichenberger <stefan.eichenberger@toradex.com>
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
2024-10-14 15:15:14 +02:00
|
|
|
result = i2c_imx_trx_complete(i2c_imx, true);
|
2009-01-30 10:32:28 +02:00
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
result = i2c_imx_acked(i2c_imx);
|
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
dev_dbg(&i2c_imx->adapter.dev, "<%s> write data\n", __func__);
|
|
|
|
|
|
|
|
/* write data */
|
|
|
|
for (i = 0; i < msgs->len; i++) {
|
|
|
|
dev_dbg(&i2c_imx->adapter.dev,
|
|
|
|
"<%s> write byte: B%d=0x%X\n",
|
|
|
|
__func__, i, msgs->buf[i]);
|
2013-08-07 17:05:39 +08:00
|
|
|
imx_i2c_write_reg(msgs->buf[i], i2c_imx, IMX_I2C_I2DR);
|
i2c: imx: prevent rescheduling in non dma mode
We are experiencing a problem with the i.MX I2C controller when
communicating with SMBus devices. We are seeing devices time-out because
the time between sending/receiving two bytes is too long, and the SMBus
device returns to the idle state. This happens because the i.MX I2C
controller sends and receives byte by byte. When a byte is sent or
received, we get an interrupt and can send or receive the next byte.
The current implementation sends a byte and then waits for an event
generated by the interrupt subroutine. After the event is received, the
next byte is sent and we wait again. This waiting allows the scheduler
to reschedule other tasks, with the disadvantage that we may not send
the next byte for a long time because the send task is not immediately
scheduled. For example, if the rescheduling takes more than 25ms, this
can cause SMBus devices to timeout and communication to fail.
This patch changes the behavior so that we do not reschedule the
send/receive task, but instead send or receive the next byte in the
interrupt subroutine. This prevents rescheduling and drastically reduces
the time between sending/receiving bytes. The cost in the interrupt
subroutine is relatively small, we check what state we are in and then
send/receive the next byte. Before we had to call wake_up, which is even
less expensive. However, we also had to do some scheduling, which
increased the overall cost compared to the new solution. The wake_up
function to wake up the send/receive task is now only called when an
error occurs or when the transfer is complete.
Signed-off-by: Stefan Eichenberger <stefan.eichenberger@toradex.com>
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
2024-10-14 15:15:14 +02:00
|
|
|
result = i2c_imx_trx_complete(i2c_imx, true);
|
2009-01-30 10:32:28 +02:00
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
result = i2c_imx_acked(i2c_imx);
|
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
i2c: imx: prevent rescheduling in non dma mode
We are experiencing a problem with the i.MX I2C controller when
communicating with SMBus devices. We are seeing devices time-out because
the time between sending/receiving two bytes is too long, and the SMBus
device returns to the idle state. This happens because the i.MX I2C
controller sends and receives byte by byte. When a byte is sent or
received, we get an interrupt and can send or receive the next byte.
The current implementation sends a byte and then waits for an event
generated by the interrupt subroutine. After the event is received, the
next byte is sent and we wait again. This waiting allows the scheduler
to reschedule other tasks, with the disadvantage that we may not send
the next byte for a long time because the send task is not immediately
scheduled. For example, if the rescheduling takes more than 25ms, this
can cause SMBus devices to timeout and communication to fail.
This patch changes the behavior so that we do not reschedule the
send/receive task, but instead send or receive the next byte in the
interrupt subroutine. This prevents rescheduling and drastically reduces
the time between sending/receiving bytes. The cost in the interrupt
subroutine is relatively small, we check what state we are in and then
send/receive the next byte. Before we had to call wake_up, which is even
less expensive. However, we also had to do some scheduling, which
increased the overall cost compared to the new solution. The wake_up
function to wake up the send/receive task is now only called when an
error occurs or when the transfer is complete.
Signed-off-by: Stefan Eichenberger <stefan.eichenberger@toradex.com>
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
2024-10-14 15:15:14 +02:00
|
|
|
static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs)
|
2024-10-14 15:15:13 +02:00
|
|
|
{
|
i2c: imx: prevent rescheduling in non dma mode
We are experiencing a problem with the i.MX I2C controller when
communicating with SMBus devices. We are seeing devices time-out because
the time between sending/receiving two bytes is too long, and the SMBus
device returns to the idle state. This happens because the i.MX I2C
controller sends and receives byte by byte. When a byte is sent or
received, we get an interrupt and can send or receive the next byte.
The current implementation sends a byte and then waits for an event
generated by the interrupt subroutine. After the event is received, the
next byte is sent and we wait again. This waiting allows the scheduler
to reschedule other tasks, with the disadvantage that we may not send
the next byte for a long time because the send task is not immediately
scheduled. For example, if the rescheduling takes more than 25ms, this
can cause SMBus devices to timeout and communication to fail.
This patch changes the behavior so that we do not reschedule the
send/receive task, but instead send or receive the next byte in the
interrupt subroutine. This prevents rescheduling and drastically reduces
the time between sending/receiving bytes. The cost in the interrupt
subroutine is relatively small, we check what state we are in and then
send/receive the next byte. Before we had to call wake_up, which is even
less expensive. However, we also had to do some scheduling, which
increased the overall cost compared to the new solution. The wake_up
function to wake up the send/receive task is now only called when an
error occurs or when the transfer is complete.
Signed-off-by: Stefan Eichenberger <stefan.eichenberger@toradex.com>
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
2024-10-14 15:15:14 +02:00
|
|
|
dev_dbg(&i2c_imx->adapter.dev, "<%s> write slave address: addr=0x%x\n",
|
|
|
|
__func__, i2c_8bit_addr_from_msg(msgs));
|
|
|
|
|
|
|
|
i2c_imx->state = IMX_I2C_STATE_WRITE;
|
|
|
|
i2c_imx->msg = msgs;
|
|
|
|
i2c_imx->msg_buf_idx = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* By writing the device address we start the state machine in the ISR.
|
|
|
|
* The ISR will report when it is done or when it fails.
|
|
|
|
*/
|
|
|
|
imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR);
|
|
|
|
wait_event_timeout(i2c_imx->queue,
|
|
|
|
i2c_imx->state == IMX_I2C_STATE_DONE ||
|
|
|
|
i2c_imx->state == IMX_I2C_STATE_FAILED,
|
|
|
|
(msgs->len + 1) * HZ / 10);
|
|
|
|
if (i2c_imx->state == IMX_I2C_STATE_FAILED) {
|
|
|
|
dev_dbg(&i2c_imx->adapter.dev, "<%s> write failed with %d\n",
|
|
|
|
__func__, i2c_imx->isr_result);
|
|
|
|
return i2c_imx->isr_result;
|
|
|
|
}
|
|
|
|
if (i2c_imx->state != IMX_I2C_STATE_DONE) {
|
|
|
|
dev_err(&i2c_imx->adapter.dev, "<%s> write timedout\n", __func__);
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
return 0;
|
2024-10-14 15:15:13 +02:00
|
|
|
}
|
|
|
|
|
i2c: imx: prevent rescheduling in non dma mode
We are experiencing a problem with the i.MX I2C controller when
communicating with SMBus devices. We are seeing devices time-out because
the time between sending/receiving two bytes is too long, and the SMBus
device returns to the idle state. This happens because the i.MX I2C
controller sends and receives byte by byte. When a byte is sent or
received, we get an interrupt and can send or receive the next byte.
The current implementation sends a byte and then waits for an event
generated by the interrupt subroutine. After the event is received, the
next byte is sent and we wait again. This waiting allows the scheduler
to reschedule other tasks, with the disadvantage that we may not send
the next byte for a long time because the send task is not immediately
scheduled. For example, if the rescheduling takes more than 25ms, this
can cause SMBus devices to timeout and communication to fail.
This patch changes the behavior so that we do not reschedule the
send/receive task, but instead send or receive the next byte in the
interrupt subroutine. This prevents rescheduling and drastically reduces
the time between sending/receiving bytes. The cost in the interrupt
subroutine is relatively small, we check what state we are in and then
send/receive the next byte. Before we had to call wake_up, which is even
less expensive. However, we also had to do some scheduling, which
increased the overall cost compared to the new solution. The wake_up
function to wake up the send/receive task is now only called when an
error occurs or when the transfer is complete.
Signed-off-by: Stefan Eichenberger <stefan.eichenberger@toradex.com>
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
2024-10-14 15:15:14 +02:00
|
|
|
static int i2c_imx_atomic_read(struct imx_i2c_struct *i2c_imx,
|
|
|
|
struct i2c_msg *msgs, bool is_lastmsg)
|
2009-01-30 10:32:28 +02:00
|
|
|
{
|
|
|
|
int i, result;
|
|
|
|
unsigned int temp;
|
2014-04-04 14:56:10 +02:00
|
|
|
int block_data = msgs->flags & I2C_M_RECV_LEN;
|
2009-01-30 10:32:28 +02:00
|
|
|
|
i2c: imx: prevent rescheduling in non dma mode
We are experiencing a problem with the i.MX I2C controller when
communicating with SMBus devices. We are seeing devices time-out because
the time between sending/receiving two bytes is too long, and the SMBus
device returns to the idle state. This happens because the i.MX I2C
controller sends and receives byte by byte. When a byte is sent or
received, we get an interrupt and can send or receive the next byte.
The current implementation sends a byte and then waits for an event
generated by the interrupt subroutine. After the event is received, the
next byte is sent and we wait again. This waiting allows the scheduler
to reschedule other tasks, with the disadvantage that we may not send
the next byte for a long time because the send task is not immediately
scheduled. For example, if the rescheduling takes more than 25ms, this
can cause SMBus devices to timeout and communication to fail.
This patch changes the behavior so that we do not reschedule the
send/receive task, but instead send or receive the next byte in the
interrupt subroutine. This prevents rescheduling and drastically reduces
the time between sending/receiving bytes. The cost in the interrupt
subroutine is relatively small, we check what state we are in and then
send/receive the next byte. Before we had to call wake_up, which is even
less expensive. However, we also had to do some scheduling, which
increased the overall cost compared to the new solution. The wake_up
function to wake up the send/receive task is now only called when an
error occurs or when the transfer is complete.
Signed-off-by: Stefan Eichenberger <stefan.eichenberger@toradex.com>
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
2024-10-14 15:15:14 +02:00
|
|
|
result = i2c_imx_prepare_read(i2c_imx, msgs, false);
|
2009-01-30 10:32:28 +02:00
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
dev_dbg(&i2c_imx->adapter.dev, "<%s> read data\n", __func__);
|
|
|
|
|
|
|
|
/* read data */
|
|
|
|
for (i = 0; i < msgs->len; i++) {
|
2014-04-04 14:56:10 +02:00
|
|
|
u8 len = 0;
|
2015-01-22 16:17:29 +01:00
|
|
|
|
i2c: imx: prevent rescheduling in non dma mode
We are experiencing a problem with the i.MX I2C controller when
communicating with SMBus devices. We are seeing devices time-out because
the time between sending/receiving two bytes is too long, and the SMBus
device returns to the idle state. This happens because the i.MX I2C
controller sends and receives byte by byte. When a byte is sent or
received, we get an interrupt and can send or receive the next byte.
The current implementation sends a byte and then waits for an event
generated by the interrupt subroutine. After the event is received, the
next byte is sent and we wait again. This waiting allows the scheduler
to reschedule other tasks, with the disadvantage that we may not send
the next byte for a long time because the send task is not immediately
scheduled. For example, if the rescheduling takes more than 25ms, this
can cause SMBus devices to timeout and communication to fail.
This patch changes the behavior so that we do not reschedule the
send/receive task, but instead send or receive the next byte in the
interrupt subroutine. This prevents rescheduling and drastically reduces
the time between sending/receiving bytes. The cost in the interrupt
subroutine is relatively small, we check what state we are in and then
send/receive the next byte. Before we had to call wake_up, which is even
less expensive. However, we also had to do some scheduling, which
increased the overall cost compared to the new solution. The wake_up
function to wake up the send/receive task is now only called when an
error occurs or when the transfer is complete.
Signed-off-by: Stefan Eichenberger <stefan.eichenberger@toradex.com>
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
2024-10-14 15:15:14 +02:00
|
|
|
result = i2c_imx_trx_complete(i2c_imx, true);
|
2009-01-30 10:32:28 +02:00
|
|
|
if (result)
|
|
|
|
return result;
|
2014-04-04 14:56:10 +02:00
|
|
|
/*
|
|
|
|
* First byte is the length of remaining packet
|
|
|
|
* in the SMBus block data read. Add it to
|
|
|
|
* msgs->len.
|
|
|
|
*/
|
|
|
|
if ((!i) && block_data) {
|
|
|
|
len = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
|
|
|
|
if ((len == 0) || (len > I2C_SMBUS_BLOCK_MAX))
|
|
|
|
return -EPROTO;
|
|
|
|
dev_dbg(&i2c_imx->adapter.dev,
|
|
|
|
"<%s> read length: 0x%X\n",
|
|
|
|
__func__, len);
|
|
|
|
msgs->len += len;
|
|
|
|
}
|
2009-01-30 10:32:28 +02:00
|
|
|
if (i == (msgs->len - 1)) {
|
2014-04-30 14:24:58 +08:00
|
|
|
if (is_lastmsg) {
|
|
|
|
/*
|
|
|
|
* It must generate STOP before read I2DR to prevent
|
|
|
|
* controller from generating another clock cycle
|
|
|
|
*/
|
|
|
|
dev_dbg(&i2c_imx->adapter.dev,
|
|
|
|
"<%s> clear MSTA\n", __func__);
|
|
|
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
2020-10-09 13:03:20 +02:00
|
|
|
if (!(temp & I2CR_MSTA))
|
|
|
|
i2c_imx->stopped = 1;
|
2014-04-30 14:24:58 +08:00
|
|
|
temp &= ~(I2CR_MSTA | I2CR_MTX);
|
|
|
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
2020-10-09 13:03:20 +02:00
|
|
|
if (!i2c_imx->stopped)
|
i2c: imx: prevent rescheduling in non dma mode
We are experiencing a problem with the i.MX I2C controller when
communicating with SMBus devices. We are seeing devices time-out because
the time between sending/receiving two bytes is too long, and the SMBus
device returns to the idle state. This happens because the i.MX I2C
controller sends and receives byte by byte. When a byte is sent or
received, we get an interrupt and can send or receive the next byte.
The current implementation sends a byte and then waits for an event
generated by the interrupt subroutine. After the event is received, the
next byte is sent and we wait again. This waiting allows the scheduler
to reschedule other tasks, with the disadvantage that we may not send
the next byte for a long time because the send task is not immediately
scheduled. For example, if the rescheduling takes more than 25ms, this
can cause SMBus devices to timeout and communication to fail.
This patch changes the behavior so that we do not reschedule the
send/receive task, but instead send or receive the next byte in the
interrupt subroutine. This prevents rescheduling and drastically reduces
the time between sending/receiving bytes. The cost in the interrupt
subroutine is relatively small, we check what state we are in and then
send/receive the next byte. Before we had to call wake_up, which is even
less expensive. However, we also had to do some scheduling, which
increased the overall cost compared to the new solution. The wake_up
function to wake up the send/receive task is now only called when an
error occurs or when the transfer is complete.
Signed-off-by: Stefan Eichenberger <stefan.eichenberger@toradex.com>
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
2024-10-14 15:15:14 +02:00
|
|
|
i2c_imx_bus_busy(i2c_imx, 0, true);
|
2014-04-30 14:24:58 +08:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* For i2c master receiver repeat restart operation like:
|
|
|
|
* read -> repeat MSTA -> read/write
|
|
|
|
* The controller must set MTX before read the last byte in
|
|
|
|
* the first read operation, otherwise the first read cost
|
|
|
|
* one extra clock cycle.
|
|
|
|
*/
|
2017-06-20 10:20:42 +02:00
|
|
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
2014-04-30 14:24:58 +08:00
|
|
|
temp |= I2CR_MTX;
|
2017-06-20 10:20:42 +02:00
|
|
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
2014-04-30 14:24:58 +08:00
|
|
|
}
|
2009-01-30 10:32:28 +02:00
|
|
|
} else if (i == (msgs->len - 2)) {
|
|
|
|
dev_dbg(&i2c_imx->adapter.dev,
|
|
|
|
"<%s> set TXAK\n", __func__);
|
2013-08-07 17:05:39 +08:00
|
|
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
2009-01-30 10:32:28 +02:00
|
|
|
temp |= I2CR_TXAK;
|
2013-08-07 17:05:39 +08:00
|
|
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
2009-01-30 10:32:28 +02:00
|
|
|
}
|
2014-04-04 14:56:10 +02:00
|
|
|
if ((!i) && block_data)
|
|
|
|
msgs->buf[0] = len;
|
|
|
|
else
|
2016-01-25 15:48:32 +03:00
|
|
|
msgs->buf[i] = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
|
2009-01-30 10:32:28 +02:00
|
|
|
dev_dbg(&i2c_imx->adapter.dev,
|
|
|
|
"<%s> read byte: B%d=0x%X\n",
|
|
|
|
__func__, i, msgs->buf[i]);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
i2c: imx: prevent rescheduling in non dma mode
We are experiencing a problem with the i.MX I2C controller when
communicating with SMBus devices. We are seeing devices time-out because
the time between sending/receiving two bytes is too long, and the SMBus
device returns to the idle state. This happens because the i.MX I2C
controller sends and receives byte by byte. When a byte is sent or
received, we get an interrupt and can send or receive the next byte.
The current implementation sends a byte and then waits for an event
generated by the interrupt subroutine. After the event is received, the
next byte is sent and we wait again. This waiting allows the scheduler
to reschedule other tasks, with the disadvantage that we may not send
the next byte for a long time because the send task is not immediately
scheduled. For example, if the rescheduling takes more than 25ms, this
can cause SMBus devices to timeout and communication to fail.
This patch changes the behavior so that we do not reschedule the
send/receive task, but instead send or receive the next byte in the
interrupt subroutine. This prevents rescheduling and drastically reduces
the time between sending/receiving bytes. The cost in the interrupt
subroutine is relatively small, we check what state we are in and then
send/receive the next byte. Before we had to call wake_up, which is even
less expensive. However, we also had to do some scheduling, which
increased the overall cost compared to the new solution. The wake_up
function to wake up the send/receive task is now only called when an
error occurs or when the transfer is complete.
Signed-off-by: Stefan Eichenberger <stefan.eichenberger@toradex.com>
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
2024-10-14 15:15:14 +02:00
|
|
|
static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs,
|
|
|
|
bool is_lastmsg)
|
2024-10-14 15:15:13 +02:00
|
|
|
{
|
i2c: imx: prevent rescheduling in non dma mode
We are experiencing a problem with the i.MX I2C controller when
communicating with SMBus devices. We are seeing devices time-out because
the time between sending/receiving two bytes is too long, and the SMBus
device returns to the idle state. This happens because the i.MX I2C
controller sends and receives byte by byte. When a byte is sent or
received, we get an interrupt and can send or receive the next byte.
The current implementation sends a byte and then waits for an event
generated by the interrupt subroutine. After the event is received, the
next byte is sent and we wait again. This waiting allows the scheduler
to reschedule other tasks, with the disadvantage that we may not send
the next byte for a long time because the send task is not immediately
scheduled. For example, if the rescheduling takes more than 25ms, this
can cause SMBus devices to timeout and communication to fail.
This patch changes the behavior so that we do not reschedule the
send/receive task, but instead send or receive the next byte in the
interrupt subroutine. This prevents rescheduling and drastically reduces
the time between sending/receiving bytes. The cost in the interrupt
subroutine is relatively small, we check what state we are in and then
send/receive the next byte. Before we had to call wake_up, which is even
less expensive. However, we also had to do some scheduling, which
increased the overall cost compared to the new solution. The wake_up
function to wake up the send/receive task is now only called when an
error occurs or when the transfer is complete.
Signed-off-by: Stefan Eichenberger <stefan.eichenberger@toradex.com>
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
2024-10-14 15:15:14 +02:00
|
|
|
int block_data = msgs->flags & I2C_M_RECV_LEN;
|
|
|
|
|
|
|
|
dev_dbg(&i2c_imx->adapter.dev,
|
|
|
|
"<%s> write slave address: addr=0x%x\n",
|
|
|
|
__func__, i2c_8bit_addr_from_msg(msgs));
|
|
|
|
|
|
|
|
i2c_imx->is_lastmsg = is_lastmsg;
|
|
|
|
|
|
|
|
if (block_data)
|
|
|
|
i2c_imx->state = IMX_I2C_STATE_READ_BLOCK_DATA;
|
|
|
|
else
|
|
|
|
i2c_imx->state = IMX_I2C_STATE_READ;
|
|
|
|
i2c_imx->msg = msgs;
|
|
|
|
i2c_imx->msg_buf_idx = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* By writing the device address we start the state machine in the ISR.
|
|
|
|
* The ISR will report when it is done or when it fails.
|
|
|
|
*/
|
|
|
|
imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR);
|
|
|
|
wait_event_timeout(i2c_imx->queue,
|
|
|
|
i2c_imx->state == IMX_I2C_STATE_DONE ||
|
|
|
|
i2c_imx->state == IMX_I2C_STATE_FAILED,
|
|
|
|
(msgs->len + 1) * HZ / 10);
|
|
|
|
if (i2c_imx->state == IMX_I2C_STATE_FAILED) {
|
|
|
|
dev_dbg(&i2c_imx->adapter.dev, "<%s> read failed with %d\n",
|
|
|
|
__func__, i2c_imx->isr_result);
|
|
|
|
return i2c_imx->isr_result;
|
|
|
|
}
|
|
|
|
if (i2c_imx->state != IMX_I2C_STATE_DONE) {
|
|
|
|
dev_err(&i2c_imx->adapter.dev, "<%s> read timedout\n", __func__);
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
if (!i2c_imx->stopped)
|
|
|
|
return i2c_imx_bus_busy(i2c_imx, 0, false);
|
|
|
|
|
|
|
|
return 0;
|
2024-10-14 15:15:13 +02:00
|
|
|
}
|
|
|
|
|
2020-01-20 10:36:50 +01:00
|
|
|
static int i2c_imx_xfer_common(struct i2c_adapter *adapter,
|
|
|
|
struct i2c_msg *msgs, int num, bool atomic)
|
2009-01-30 10:32:28 +02:00
|
|
|
{
|
|
|
|
unsigned int i, temp;
|
|
|
|
int result;
|
2014-04-30 14:24:58 +08:00
|
|
|
bool is_lastmsg = false;
|
2009-01-30 10:32:28 +02:00
|
|
|
struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);
|
2024-10-14 15:15:13 +02:00
|
|
|
int use_dma = 0;
|
2009-01-30 10:32:28 +02:00
|
|
|
|
2009-10-17 17:46:22 +08:00
|
|
|
/* Start I2C transfer */
|
2020-01-20 10:36:50 +01:00
|
|
|
result = i2c_imx_start(i2c_imx, atomic);
|
2015-10-23 20:28:54 +08:00
|
|
|
if (result) {
|
2020-01-20 10:36:50 +01:00
|
|
|
/*
|
|
|
|
* Bus recovery uses gpiod_get_value_cansleep() which is not
|
|
|
|
* allowed within atomic context.
|
|
|
|
*/
|
|
|
|
if (!atomic && i2c_imx->adapter.bus_recovery_info) {
|
2015-10-23 20:28:54 +08:00
|
|
|
i2c_recover_bus(&i2c_imx->adapter);
|
2020-01-20 10:36:50 +01:00
|
|
|
result = i2c_imx_start(i2c_imx, atomic);
|
2015-10-23 20:28:54 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-30 10:32:28 +02:00
|
|
|
if (result)
|
|
|
|
goto fail0;
|
|
|
|
|
|
|
|
/* read/write data */
|
|
|
|
for (i = 0; i < num; i++) {
|
2014-04-30 14:24:58 +08:00
|
|
|
if (i == num - 1)
|
|
|
|
is_lastmsg = true;
|
|
|
|
|
2009-01-30 10:32:28 +02:00
|
|
|
if (i) {
|
|
|
|
dev_dbg(&i2c_imx->adapter.dev,
|
|
|
|
"<%s> repeated start\n", __func__);
|
2013-08-07 17:05:39 +08:00
|
|
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
2009-01-30 10:32:28 +02:00
|
|
|
temp |= I2CR_RSTA;
|
2013-08-07 17:05:39 +08:00
|
|
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
2020-01-20 10:36:50 +01:00
|
|
|
result = i2c_imx_bus_busy(i2c_imx, 1, atomic);
|
2009-10-17 17:46:22 +08:00
|
|
|
if (result)
|
|
|
|
goto fail0;
|
2009-01-30 10:32:28 +02:00
|
|
|
}
|
|
|
|
dev_dbg(&i2c_imx->adapter.dev,
|
|
|
|
"<%s> transfer message: %d\n", __func__, i);
|
|
|
|
/* write/read data */
|
|
|
|
#ifdef CONFIG_I2C_DEBUG_BUS
|
2013-08-07 17:05:39 +08:00
|
|
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
2015-01-22 16:17:29 +01:00
|
|
|
dev_dbg(&i2c_imx->adapter.dev,
|
|
|
|
"<%s> CONTROL: IEN=%d, IIEN=%d, MSTA=%d, MTX=%d, TXAK=%d, RSTA=%d\n",
|
|
|
|
__func__,
|
2009-01-30 10:32:28 +02:00
|
|
|
(temp & I2CR_IEN ? 1 : 0), (temp & I2CR_IIEN ? 1 : 0),
|
|
|
|
(temp & I2CR_MSTA ? 1 : 0), (temp & I2CR_MTX ? 1 : 0),
|
|
|
|
(temp & I2CR_TXAK ? 1 : 0), (temp & I2CR_RSTA ? 1 : 0));
|
2013-08-07 17:05:39 +08:00
|
|
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
|
2009-01-30 10:32:28 +02:00
|
|
|
dev_dbg(&i2c_imx->adapter.dev,
|
2015-01-22 16:17:29 +01:00
|
|
|
"<%s> STATUS: ICF=%d, IAAS=%d, IBB=%d, IAL=%d, SRW=%d, IIF=%d, RXAK=%d\n",
|
|
|
|
__func__,
|
2009-01-30 10:32:28 +02:00
|
|
|
(temp & I2SR_ICF ? 1 : 0), (temp & I2SR_IAAS ? 1 : 0),
|
|
|
|
(temp & I2SR_IBB ? 1 : 0), (temp & I2SR_IAL ? 1 : 0),
|
|
|
|
(temp & I2SR_SRW ? 1 : 0), (temp & I2SR_IIF ? 1 : 0),
|
|
|
|
(temp & I2SR_RXAK ? 1 : 0));
|
|
|
|
#endif
|
2024-10-14 15:15:13 +02:00
|
|
|
|
|
|
|
use_dma = i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD &&
|
|
|
|
msgs[i].flags & I2C_M_DMA_SAFE;
|
2020-01-20 10:36:50 +01:00
|
|
|
if (msgs[i].flags & I2C_M_RD) {
|
2024-10-14 15:15:13 +02:00
|
|
|
int block_data = msgs->flags & I2C_M_RECV_LEN;
|
|
|
|
|
|
|
|
if (atomic)
|
|
|
|
result = i2c_imx_atomic_read(i2c_imx, &msgs[i], is_lastmsg);
|
|
|
|
else if (use_dma && !block_data)
|
|
|
|
result = i2c_imx_dma_read(i2c_imx, &msgs[i], is_lastmsg);
|
|
|
|
else
|
i2c: imx: prevent rescheduling in non dma mode
We are experiencing a problem with the i.MX I2C controller when
communicating with SMBus devices. We are seeing devices time-out because
the time between sending/receiving two bytes is too long, and the SMBus
device returns to the idle state. This happens because the i.MX I2C
controller sends and receives byte by byte. When a byte is sent or
received, we get an interrupt and can send or receive the next byte.
The current implementation sends a byte and then waits for an event
generated by the interrupt subroutine. After the event is received, the
next byte is sent and we wait again. This waiting allows the scheduler
to reschedule other tasks, with the disadvantage that we may not send
the next byte for a long time because the send task is not immediately
scheduled. For example, if the rescheduling takes more than 25ms, this
can cause SMBus devices to timeout and communication to fail.
This patch changes the behavior so that we do not reschedule the
send/receive task, but instead send or receive the next byte in the
interrupt subroutine. This prevents rescheduling and drastically reduces
the time between sending/receiving bytes. The cost in the interrupt
subroutine is relatively small, we check what state we are in and then
send/receive the next byte. Before we had to call wake_up, which is even
less expensive. However, we also had to do some scheduling, which
increased the overall cost compared to the new solution. The wake_up
function to wake up the send/receive task is now only called when an
error occurs or when the transfer is complete.
Signed-off-by: Stefan Eichenberger <stefan.eichenberger@toradex.com>
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
2024-10-14 15:15:14 +02:00
|
|
|
result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg);
|
2020-01-20 10:36:50 +01:00
|
|
|
} else {
|
2024-10-14 15:15:13 +02:00
|
|
|
if (atomic)
|
|
|
|
result = i2c_imx_atomic_write(i2c_imx, &msgs[i]);
|
|
|
|
else if (use_dma)
|
2014-11-18 18:31:06 +08:00
|
|
|
result = i2c_imx_dma_write(i2c_imx, &msgs[i]);
|
|
|
|
else
|
i2c: imx: prevent rescheduling in non dma mode
We are experiencing a problem with the i.MX I2C controller when
communicating with SMBus devices. We are seeing devices time-out because
the time between sending/receiving two bytes is too long, and the SMBus
device returns to the idle state. This happens because the i.MX I2C
controller sends and receives byte by byte. When a byte is sent or
received, we get an interrupt and can send or receive the next byte.
The current implementation sends a byte and then waits for an event
generated by the interrupt subroutine. After the event is received, the
next byte is sent and we wait again. This waiting allows the scheduler
to reschedule other tasks, with the disadvantage that we may not send
the next byte for a long time because the send task is not immediately
scheduled. For example, if the rescheduling takes more than 25ms, this
can cause SMBus devices to timeout and communication to fail.
This patch changes the behavior so that we do not reschedule the
send/receive task, but instead send or receive the next byte in the
interrupt subroutine. This prevents rescheduling and drastically reduces
the time between sending/receiving bytes. The cost in the interrupt
subroutine is relatively small, we check what state we are in and then
send/receive the next byte. Before we had to call wake_up, which is even
less expensive. However, we also had to do some scheduling, which
increased the overall cost compared to the new solution. The wake_up
function to wake up the send/receive task is now only called when an
error occurs or when the transfer is complete.
Signed-off-by: Stefan Eichenberger <stefan.eichenberger@toradex.com>
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
2024-10-14 15:15:14 +02:00
|
|
|
result = i2c_imx_write(i2c_imx, &msgs[i]);
|
2014-11-18 18:31:06 +08:00
|
|
|
}
|
2010-03-23 17:28:28 +01:00
|
|
|
if (result)
|
|
|
|
goto fail0;
|
2009-01-30 10:32:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fail0:
|
|
|
|
/* Stop I2C transfer */
|
2020-01-20 10:36:50 +01:00
|
|
|
i2c_imx_stop(i2c_imx, atomic);
|
2015-12-11 10:24:09 +08:00
|
|
|
|
2009-01-30 10:32:28 +02:00
|
|
|
dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,
|
|
|
|
(result < 0) ? "error" : "success msg",
|
|
|
|
(result < 0) ? result : num);
|
2020-11-11 19:32:55 +08:00
|
|
|
/* After data is transferred, switch to slave mode(as a receiver) */
|
|
|
|
if (i2c_imx->slave)
|
|
|
|
i2c_imx_slave_init(i2c_imx);
|
|
|
|
|
2009-01-30 10:32:28 +02:00
|
|
|
return (result < 0) ? result : num;
|
|
|
|
}
|
|
|
|
|
2020-01-20 10:36:50 +01:00
|
|
|
static int i2c_imx_xfer(struct i2c_adapter *adapter,
|
|
|
|
struct i2c_msg *msgs, int num)
|
|
|
|
{
|
|
|
|
struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);
|
|
|
|
int result;
|
|
|
|
|
2020-12-01 17:31:41 +08:00
|
|
|
result = pm_runtime_resume_and_get(i2c_imx->adapter.dev.parent);
|
2020-01-20 10:36:50 +01:00
|
|
|
if (result < 0)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
result = i2c_imx_xfer_common(adapter, msgs, num, false);
|
|
|
|
|
|
|
|
pm_runtime_mark_last_busy(i2c_imx->adapter.dev.parent);
|
|
|
|
pm_runtime_put_autosuspend(i2c_imx->adapter.dev.parent);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int i2c_imx_xfer_atomic(struct i2c_adapter *adapter,
|
|
|
|
struct i2c_msg *msgs, int num)
|
|
|
|
{
|
|
|
|
struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);
|
|
|
|
int result;
|
|
|
|
|
|
|
|
result = clk_enable(i2c_imx->clk);
|
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
result = i2c_imx_xfer_common(adapter, msgs, num, true);
|
|
|
|
|
|
|
|
clk_disable(i2c_imx->clk);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-09-12 17:22:30 -05:00
|
|
|
/*
|
|
|
|
* We switch SCL and SDA to their GPIO function and do some bitbanging
|
|
|
|
* for bus recovery. These alternative pinmux settings can be
|
|
|
|
* described in the device tree by a separate pinctrl state "gpio". If
|
|
|
|
* this is missing this is not a big problem, the only implication is
|
|
|
|
* that we can't do bus recovery.
|
|
|
|
*/
|
|
|
|
static int i2c_imx_init_recovery_info(struct imx_i2c_struct *i2c_imx,
|
2015-10-23 20:28:54 +08:00
|
|
|
struct platform_device *pdev)
|
|
|
|
{
|
2024-01-25 14:56:36 +01:00
|
|
|
struct i2c_bus_recovery_info *bri = &i2c_imx->rinfo;
|
2015-10-23 20:28:54 +08:00
|
|
|
|
2024-01-25 14:56:36 +01:00
|
|
|
bri->pinctrl = devm_pinctrl_get(&pdev->dev);
|
|
|
|
if (IS_ERR(bri->pinctrl))
|
|
|
|
return PTR_ERR(bri->pinctrl);
|
2015-10-23 20:28:54 +08:00
|
|
|
|
2024-01-25 14:56:36 +01:00
|
|
|
i2c_imx->adapter.bus_recovery_info = bri;
|
2016-09-12 17:22:30 -05:00
|
|
|
|
|
|
|
return 0;
|
2015-10-23 20:28:54 +08:00
|
|
|
}
|
|
|
|
|
2009-01-30 10:32:28 +02:00
|
|
|
static u32 i2c_imx_func(struct i2c_adapter *adapter)
|
|
|
|
{
|
2014-04-04 14:56:10 +02:00
|
|
|
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL
|
|
|
|
| I2C_FUNC_SMBUS_READ_BLOCK_DATA;
|
2009-01-30 10:32:28 +02:00
|
|
|
}
|
|
|
|
|
2017-01-27 23:36:17 +05:30
|
|
|
static const struct i2c_algorithm i2c_imx_algo = {
|
2025-06-12 09:27:22 +02:00
|
|
|
.xfer = i2c_imx_xfer,
|
|
|
|
.xfer_atomic = i2c_imx_xfer_atomic,
|
2020-01-20 10:36:50 +01:00
|
|
|
.functionality = i2c_imx_func,
|
2025-06-12 09:27:22 +02:00
|
|
|
.reg_slave = i2c_imx_reg_slave,
|
|
|
|
.unreg_slave = i2c_imx_unreg_slave,
|
2009-01-30 10:32:28 +02:00
|
|
|
};
|
|
|
|
|
2013-10-08 22:35:34 +02:00
|
|
|
static int i2c_imx_probe(struct platform_device *pdev)
|
2009-01-30 10:32:28 +02:00
|
|
|
{
|
|
|
|
struct imx_i2c_struct *i2c_imx;
|
|
|
|
struct resource *res;
|
2013-07-30 16:59:33 +09:00
|
|
|
struct imxi2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
2009-01-30 10:32:28 +02:00
|
|
|
void __iomem *base;
|
2012-07-08 13:11:43 +02:00
|
|
|
int irq, ret;
|
2014-11-18 18:31:06 +08:00
|
|
|
dma_addr_t phy_addr;
|
2019-09-06 15:53:19 +08:00
|
|
|
const struct imx_i2c_hwdata *match;
|
2009-01-30 10:32:28 +02:00
|
|
|
|
|
|
|
irq = platform_get_irq(pdev, 0);
|
2020-03-18 18:07:48 +08:00
|
|
|
if (irq < 0)
|
2025-04-08 12:02:59 +02:00
|
|
|
return dev_err_probe(&pdev->dev, irq, "can't get IRQ\n");
|
2009-01-30 10:32:28 +02:00
|
|
|
|
2022-11-10 17:23:42 +08:00
|
|
|
base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
2013-01-21 11:09:03 +01:00
|
|
|
if (IS_ERR(base))
|
2025-04-08 12:02:59 +02:00
|
|
|
return dev_err_probe(&pdev->dev, PTR_ERR(base), "can't get IO memory\n");
|
2009-01-30 10:32:28 +02:00
|
|
|
|
2014-11-18 18:31:06 +08:00
|
|
|
phy_addr = (dma_addr_t)res->start;
|
2014-11-07 00:44:34 -02:00
|
|
|
i2c_imx = devm_kzalloc(&pdev->dev, sizeof(*i2c_imx), GFP_KERNEL);
|
2014-05-13 10:51:58 +09:00
|
|
|
if (!i2c_imx)
|
2012-06-04 19:04:25 +08:00
|
|
|
return -ENOMEM;
|
2009-03-31 14:52:54 +03:00
|
|
|
|
2021-11-12 07:39:55 -06:00
|
|
|
spin_lock_init(&i2c_imx->slave_lock);
|
2025-02-05 11:46:16 +01:00
|
|
|
hrtimer_setup(&i2c_imx->slave_timer, i2c_imx_slave_timeout, CLOCK_MONOTONIC,
|
|
|
|
HRTIMER_MODE_ABS);
|
2021-11-12 07:39:55 -06:00
|
|
|
|
2019-09-06 15:53:19 +08:00
|
|
|
match = device_get_match_data(&pdev->dev);
|
2021-01-19 23:41:23 -03:00
|
|
|
if (match)
|
|
|
|
i2c_imx->hwdata = match;
|
|
|
|
else
|
|
|
|
i2c_imx->hwdata = (struct imx_i2c_hwdata *)
|
|
|
|
platform_get_device_id(pdev)->driver_data;
|
2012-09-14 15:19:00 +08:00
|
|
|
|
2009-01-30 10:32:28 +02:00
|
|
|
/* Setup i2c_imx driver structure */
|
2021-06-23 17:36:43 +09:00
|
|
|
strscpy(i2c_imx->adapter.name, pdev->name, sizeof(i2c_imx->adapter.name));
|
2009-01-30 10:32:28 +02:00
|
|
|
i2c_imx->adapter.owner = THIS_MODULE;
|
|
|
|
i2c_imx->adapter.algo = &i2c_imx_algo;
|
|
|
|
i2c_imx->adapter.dev.parent = &pdev->dev;
|
2015-01-22 16:17:29 +01:00
|
|
|
i2c_imx->adapter.nr = pdev->id;
|
2011-09-08 15:09:35 +08:00
|
|
|
i2c_imx->adapter.dev.of_node = pdev->dev.of_node;
|
2009-01-30 10:32:28 +02:00
|
|
|
i2c_imx->base = base;
|
2019-09-06 15:53:19 +08:00
|
|
|
ACPI_COMPANION_SET(&i2c_imx->adapter.dev, ACPI_COMPANION(&pdev->dev));
|
2009-01-30 10:32:28 +02:00
|
|
|
|
|
|
|
/* Get I2C clock */
|
2023-03-13 19:33:49 +01:00
|
|
|
i2c_imx->clk = devm_clk_get_enabled(&pdev->dev, NULL);
|
2020-08-12 11:45:54 +08:00
|
|
|
if (IS_ERR(i2c_imx->clk))
|
|
|
|
return dev_err_probe(&pdev->dev, PTR_ERR(i2c_imx->clk),
|
|
|
|
"can't get I2C clock\n");
|
2009-01-30 10:32:28 +02:00
|
|
|
|
|
|
|
/* Init queue */
|
|
|
|
init_waitqueue_head(&i2c_imx->queue);
|
|
|
|
|
|
|
|
/* Set up adapter data */
|
|
|
|
i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);
|
|
|
|
|
2015-12-11 10:24:09 +08:00
|
|
|
/* Set up platform driver data */
|
|
|
|
platform_set_drvdata(pdev, i2c_imx);
|
|
|
|
|
|
|
|
pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_TIMEOUT);
|
|
|
|
pm_runtime_use_autosuspend(&pdev->dev);
|
|
|
|
pm_runtime_set_active(&pdev->dev);
|
|
|
|
pm_runtime_enable(&pdev->dev);
|
|
|
|
|
|
|
|
ret = pm_runtime_get_sync(&pdev->dev);
|
|
|
|
if (ret < 0)
|
|
|
|
goto rpm_disable;
|
|
|
|
|
2020-09-20 23:12:38 +02:00
|
|
|
/* Request IRQ */
|
i2c: imx: make controller available until system suspend_noirq() and from resume_noirq()
Put runtime PM to resume state between suspend() and suspend_noirq(),
resume_noirq() and resume(), because some I2C devices need the controller
on to perform communication during this period.
The controller can't be woken up once runtime pm is disabled and in
runtime autosuspended state.
The problem can be easily reproduced on the I.MX8MQ platform:
the PMIC needs to be used to enable regulator when the system resumes.
When the PMIC uses the I2C controller, I2C runtime pm has not been enabled,
so in i2c xfer(), pm_runtime_resume_and_get() will return an error,
which causes data transfer to fail. Therefore, regulators cannot
be enabled and system resume hangs.
Here is resume error log:
[ 53.888902] galcore 38000000.gpu3d: PM: calling genpd_resume_noirq @ 529, parent: platform
[ 53.897203] i2c_imx_xfer, pm_runtime_resume_and_get is -13
[ 53.902713] imx-pgc imx-pgc-domain.5: failed to enable regulator: -EACCES
[ 53.909518] galcore 38000000.gpu3d: PM: genpd_resume_noirq returned 0 after 12331 usecs
[ 53.917545] mxc_hantro 38300000.vpu: PM: calling genpd_resume_noirq @ 529, parent: soc@0
[ 53.925659] i2c_imx_xfer, pm_runtime_resume_and_get is -13
[ 53.931157] imx-pgc imx-pgc-domain.6: failed to enable regulator: -EACCES
I.MX8MQ system resume normally after applying the fix. Here is resume log:
[ 71.068807] galcore 38000000.gpu3d: PM: calling genpd_resume_noirq @ 530, parent: platform
[ 71.077103] i2c_imx_xfer, pm_runtime_resume_and_get is 0
[ 71.083578] galcore 38000000.gpu3d: PM: genpd_resume_noirq returned 0 after 6490 usecs
[ 71.091526] mxc_hantro 38300000.vpu: PM: calling genpd_resume_noirq @ 530, parent: soc@0
[ 71.099638] i2c_imx_xfer, pm_runtime_resume_and_get is 0
[ 71.106091] mxc_hantro 38300000.vpu: PM: genpd_resume_noirq returned 0 after 6458 usecs
Signed-off-by: Carlos Song <carlos.song@nxp.com>
Signed-off-by: Haibo Chen <haibo.chen@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Link: https://lore.kernel.org/r/20241125142108.1613016-1-carlos.song@nxp.com
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
2024-11-25 22:21:08 +08:00
|
|
|
ret = request_irq(irq, i2c_imx_isr, IRQF_SHARED | IRQF_NO_SUSPEND,
|
|
|
|
pdev->name, i2c_imx);
|
2020-09-20 23:12:38 +02:00
|
|
|
if (ret) {
|
|
|
|
dev_err(&pdev->dev, "can't claim irq %d\n", irq);
|
|
|
|
goto rpm_disable;
|
|
|
|
}
|
|
|
|
|
2024-10-14 15:15:12 +02:00
|
|
|
/*
|
|
|
|
* We use the single-master property for backward compatibility.
|
|
|
|
* By default multi master mode is enabled.
|
|
|
|
*/
|
|
|
|
i2c_imx->multi_master = !of_property_read_bool(pdev->dev.of_node, "single-master");
|
|
|
|
|
2009-01-30 10:32:28 +02:00
|
|
|
/* Set up clock divider */
|
2020-03-24 14:32:16 +02:00
|
|
|
i2c_imx->bitrate = I2C_MAX_STANDARD_MODE_FREQ;
|
2011-09-08 15:09:35 +08:00
|
|
|
ret = of_property_read_u32(pdev->dev.of_node,
|
2014-05-20 10:21:45 +08:00
|
|
|
"clock-frequency", &i2c_imx->bitrate);
|
2011-09-08 15:09:35 +08:00
|
|
|
if (ret < 0 && pdata && pdata->bitrate)
|
2014-05-20 10:21:45 +08:00
|
|
|
i2c_imx->bitrate = pdata->bitrate;
|
2018-03-08 14:25:17 +01:00
|
|
|
i2c_imx->clk_change_nb.notifier_call = i2c_imx_clk_notifier_call;
|
|
|
|
clk_notifier_register(i2c_imx->clk, &i2c_imx->clk_change_nb);
|
2024-11-25 22:15:21 +08:00
|
|
|
ret = i2c_imx_set_clk(i2c_imx, clk_get_rate(i2c_imx->clk));
|
|
|
|
if (ret < 0) {
|
|
|
|
dev_err(&pdev->dev, "can't get I2C clock\n");
|
|
|
|
goto clk_notifier_unregister;
|
|
|
|
}
|
2009-01-30 10:32:28 +02:00
|
|
|
|
2020-11-11 19:32:55 +08:00
|
|
|
i2c_imx_reset_regs(i2c_imx);
|
2009-01-30 10:32:28 +02:00
|
|
|
|
2016-09-12 17:22:30 -05:00
|
|
|
/* Init optional bus recovery function */
|
|
|
|
ret = i2c_imx_init_recovery_info(i2c_imx, pdev);
|
|
|
|
/* Give it another chance if pinctrl used is not ready yet */
|
|
|
|
if (ret == -EPROBE_DEFER)
|
2018-03-08 14:25:17 +01:00
|
|
|
goto clk_notifier_unregister;
|
2015-12-09 11:08:22 +08:00
|
|
|
|
2024-12-26 14:25:21 +08:00
|
|
|
/*
|
|
|
|
* DMA mode should be optional for I2C, when encountering DMA errors,
|
|
|
|
* no need to exit I2C probe. Only print warning to show DMA error and
|
|
|
|
* use PIO mode directly to ensure I2C bus available as much as possible.
|
|
|
|
*/
|
|
|
|
ret = i2c_imx_dma_request(i2c_imx, phy_addr);
|
|
|
|
if (ret) {
|
2025-04-08 12:02:59 +02:00
|
|
|
if (ret == -EPROBE_DEFER) {
|
|
|
|
dev_err_probe(&pdev->dev, ret, "can't get DMA channels\n");
|
2024-12-26 14:25:21 +08:00
|
|
|
goto clk_notifier_unregister;
|
2025-04-08 12:02:59 +02:00
|
|
|
} else if (ret == -ENODEV) {
|
2024-12-26 14:25:21 +08:00
|
|
|
dev_dbg(&pdev->dev, "Only use PIO mode\n");
|
2025-04-08 12:02:59 +02:00
|
|
|
} else {
|
2024-12-26 14:25:21 +08:00
|
|
|
dev_warn(&pdev->dev, "Failed to setup DMA (%pe), only use PIO mode\n",
|
|
|
|
ERR_PTR(ret));
|
2025-04-08 12:02:59 +02:00
|
|
|
}
|
2024-12-26 14:25:21 +08:00
|
|
|
}
|
|
|
|
|
2009-01-30 10:32:28 +02:00
|
|
|
/* Add I2C adapter */
|
|
|
|
ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
|
2016-08-09 13:36:17 +02:00
|
|
|
if (ret < 0)
|
2018-03-08 14:25:17 +01:00
|
|
|
goto clk_notifier_unregister;
|
2009-01-30 10:32:28 +02:00
|
|
|
|
2015-12-11 10:24:09 +08:00
|
|
|
pm_runtime_mark_last_busy(&pdev->dev);
|
|
|
|
pm_runtime_put_autosuspend(&pdev->dev);
|
2009-01-30 10:32:28 +02:00
|
|
|
|
2012-06-04 19:04:25 +08:00
|
|
|
dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", irq);
|
2014-08-06 11:45:08 +08:00
|
|
|
dev_dbg(&i2c_imx->adapter.dev, "device resources: %pR\n", res);
|
2009-01-30 10:32:28 +02:00
|
|
|
dev_dbg(&i2c_imx->adapter.dev, "adapter name: \"%s\"\n",
|
|
|
|
i2c_imx->adapter.name);
|
2019-08-08 18:01:36 -03:00
|
|
|
dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n");
|
2009-01-30 10:32:28 +02:00
|
|
|
|
|
|
|
return 0; /* Return OK */
|
2014-10-04 09:17:27 -03:00
|
|
|
|
2018-03-08 14:25:17 +01:00
|
|
|
clk_notifier_unregister:
|
|
|
|
clk_notifier_unregister(i2c_imx->clk, &i2c_imx->clk_change_nb);
|
2020-09-20 23:12:38 +02:00
|
|
|
free_irq(irq, i2c_imx);
|
2015-12-11 10:24:09 +08:00
|
|
|
rpm_disable:
|
|
|
|
pm_runtime_put_noidle(&pdev->dev);
|
|
|
|
pm_runtime_disable(&pdev->dev);
|
|
|
|
pm_runtime_set_suspended(&pdev->dev);
|
|
|
|
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
2014-10-04 09:17:27 -03:00
|
|
|
return ret;
|
2009-01-30 10:32:28 +02:00
|
|
|
}
|
|
|
|
|
2023-05-08 22:51:38 +02:00
|
|
|
static void i2c_imx_remove(struct platform_device *pdev)
|
2009-01-30 10:32:28 +02:00
|
|
|
{
|
|
|
|
struct imx_i2c_struct *i2c_imx = platform_get_drvdata(pdev);
|
2020-09-20 23:12:38 +02:00
|
|
|
int irq, ret;
|
2015-12-11 10:24:09 +08:00
|
|
|
|
2022-07-20 17:09:33 +02:00
|
|
|
ret = pm_runtime_get_sync(&pdev->dev);
|
2009-01-30 10:32:28 +02:00
|
|
|
|
2021-11-12 07:39:55 -06:00
|
|
|
hrtimer_cancel(&i2c_imx->slave_timer);
|
|
|
|
|
2009-01-30 10:32:28 +02:00
|
|
|
/* remove adapter */
|
|
|
|
dev_dbg(&i2c_imx->adapter.dev, "adapter removed\n");
|
|
|
|
i2c_del_adapter(&i2c_imx->adapter);
|
|
|
|
|
2014-11-18 18:31:06 +08:00
|
|
|
if (i2c_imx->dma)
|
|
|
|
i2c_imx_dma_free(i2c_imx);
|
|
|
|
|
2022-09-12 15:20:40 +02:00
|
|
|
if (ret >= 0) {
|
2022-07-20 17:09:33 +02:00
|
|
|
/* setup chip registers to defaults */
|
|
|
|
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IADR);
|
|
|
|
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IFDR);
|
|
|
|
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2CR);
|
|
|
|
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2SR);
|
|
|
|
}
|
2009-01-30 10:32:28 +02:00
|
|
|
|
2018-03-08 14:25:17 +01:00
|
|
|
clk_notifier_unregister(i2c_imx->clk, &i2c_imx->clk_change_nb);
|
2020-09-20 23:12:38 +02:00
|
|
|
irq = platform_get_irq(pdev, 0);
|
|
|
|
if (irq >= 0)
|
|
|
|
free_irq(irq, i2c_imx);
|
2022-07-20 17:09:33 +02:00
|
|
|
|
2015-12-11 10:24:09 +08:00
|
|
|
pm_runtime_put_noidle(&pdev->dev);
|
|
|
|
pm_runtime_disable(&pdev->dev);
|
|
|
|
}
|
|
|
|
|
2024-08-07 15:14:56 -03:00
|
|
|
static int i2c_imx_runtime_suspend(struct device *dev)
|
2015-12-11 10:24:09 +08:00
|
|
|
{
|
2016-01-25 15:48:32 +03:00
|
|
|
struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
|
2015-12-11 10:24:09 +08:00
|
|
|
|
2018-03-08 14:25:18 +01:00
|
|
|
clk_disable(i2c_imx->clk);
|
2024-12-23 11:43:43 +08:00
|
|
|
return pinctrl_pm_select_sleep_state(dev);
|
2009-01-30 10:32:28 +02:00
|
|
|
}
|
|
|
|
|
2024-08-07 15:14:56 -03:00
|
|
|
static int i2c_imx_runtime_resume(struct device *dev)
|
2015-12-11 10:24:09 +08:00
|
|
|
{
|
2016-01-25 15:48:32 +03:00
|
|
|
struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
|
2015-12-11 10:24:09 +08:00
|
|
|
int ret;
|
|
|
|
|
2024-12-23 11:43:43 +08:00
|
|
|
ret = pinctrl_pm_select_default_state(dev);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2018-03-08 14:25:18 +01:00
|
|
|
ret = clk_enable(i2c_imx->clk);
|
2015-12-11 10:24:09 +08:00
|
|
|
if (ret)
|
|
|
|
dev_err(dev, "can't enable I2C clock, ret=%d\n", ret);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
i2c: imx: make controller available until system suspend_noirq() and from resume_noirq()
Put runtime PM to resume state between suspend() and suspend_noirq(),
resume_noirq() and resume(), because some I2C devices need the controller
on to perform communication during this period.
The controller can't be woken up once runtime pm is disabled and in
runtime autosuspended state.
The problem can be easily reproduced on the I.MX8MQ platform:
the PMIC needs to be used to enable regulator when the system resumes.
When the PMIC uses the I2C controller, I2C runtime pm has not been enabled,
so in i2c xfer(), pm_runtime_resume_and_get() will return an error,
which causes data transfer to fail. Therefore, regulators cannot
be enabled and system resume hangs.
Here is resume error log:
[ 53.888902] galcore 38000000.gpu3d: PM: calling genpd_resume_noirq @ 529, parent: platform
[ 53.897203] i2c_imx_xfer, pm_runtime_resume_and_get is -13
[ 53.902713] imx-pgc imx-pgc-domain.5: failed to enable regulator: -EACCES
[ 53.909518] galcore 38000000.gpu3d: PM: genpd_resume_noirq returned 0 after 12331 usecs
[ 53.917545] mxc_hantro 38300000.vpu: PM: calling genpd_resume_noirq @ 529, parent: soc@0
[ 53.925659] i2c_imx_xfer, pm_runtime_resume_and_get is -13
[ 53.931157] imx-pgc imx-pgc-domain.6: failed to enable regulator: -EACCES
I.MX8MQ system resume normally after applying the fix. Here is resume log:
[ 71.068807] galcore 38000000.gpu3d: PM: calling genpd_resume_noirq @ 530, parent: platform
[ 71.077103] i2c_imx_xfer, pm_runtime_resume_and_get is 0
[ 71.083578] galcore 38000000.gpu3d: PM: genpd_resume_noirq returned 0 after 6490 usecs
[ 71.091526] mxc_hantro 38300000.vpu: PM: calling genpd_resume_noirq @ 530, parent: soc@0
[ 71.099638] i2c_imx_xfer, pm_runtime_resume_and_get is 0
[ 71.106091] mxc_hantro 38300000.vpu: PM: genpd_resume_noirq returned 0 after 6458 usecs
Signed-off-by: Carlos Song <carlos.song@nxp.com>
Signed-off-by: Haibo Chen <haibo.chen@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Link: https://lore.kernel.org/r/20241125142108.1613016-1-carlos.song@nxp.com
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
2024-11-25 22:21:08 +08:00
|
|
|
static int i2c_imx_suspend(struct device *dev)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Some I2C devices may need the I2C controller to remain active
|
|
|
|
* during resume_noirq() or suspend_noirq(). If the controller is
|
|
|
|
* autosuspended, there is no way to wake it up once runtime PM is
|
|
|
|
* disabled (in suspend_late()).
|
|
|
|
*
|
|
|
|
* During system resume, the I2C controller will be available only
|
|
|
|
* after runtime PM is re-enabled (in resume_early()). However, this
|
|
|
|
* may be too late for some devices.
|
|
|
|
*
|
|
|
|
* Wake up the controller in the suspend() callback while runtime PM
|
|
|
|
* is still enabled. The I2C controller will remain available until
|
|
|
|
* the suspend_noirq() callback (pm_runtime_force_suspend()) is
|
|
|
|
* called. During resume, the I2C controller can be restored by the
|
|
|
|
* resume_noirq() callback (pm_runtime_force_resume()).
|
|
|
|
*
|
|
|
|
* Finally, the resume() callback re-enables autosuspend, ensuring
|
|
|
|
* the I2C controller remains available until the system enters
|
|
|
|
* suspend_noirq() and from resume_noirq().
|
|
|
|
*/
|
|
|
|
return pm_runtime_resume_and_get(dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int i2c_imx_resume(struct device *dev)
|
|
|
|
{
|
|
|
|
pm_runtime_mark_last_busy(dev);
|
|
|
|
pm_runtime_put_autosuspend(dev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-12-11 10:24:09 +08:00
|
|
|
static const struct dev_pm_ops i2c_imx_pm_ops = {
|
i2c: imx: make controller available until system suspend_noirq() and from resume_noirq()
Put runtime PM to resume state between suspend() and suspend_noirq(),
resume_noirq() and resume(), because some I2C devices need the controller
on to perform communication during this period.
The controller can't be woken up once runtime pm is disabled and in
runtime autosuspended state.
The problem can be easily reproduced on the I.MX8MQ platform:
the PMIC needs to be used to enable regulator when the system resumes.
When the PMIC uses the I2C controller, I2C runtime pm has not been enabled,
so in i2c xfer(), pm_runtime_resume_and_get() will return an error,
which causes data transfer to fail. Therefore, regulators cannot
be enabled and system resume hangs.
Here is resume error log:
[ 53.888902] galcore 38000000.gpu3d: PM: calling genpd_resume_noirq @ 529, parent: platform
[ 53.897203] i2c_imx_xfer, pm_runtime_resume_and_get is -13
[ 53.902713] imx-pgc imx-pgc-domain.5: failed to enable regulator: -EACCES
[ 53.909518] galcore 38000000.gpu3d: PM: genpd_resume_noirq returned 0 after 12331 usecs
[ 53.917545] mxc_hantro 38300000.vpu: PM: calling genpd_resume_noirq @ 529, parent: soc@0
[ 53.925659] i2c_imx_xfer, pm_runtime_resume_and_get is -13
[ 53.931157] imx-pgc imx-pgc-domain.6: failed to enable regulator: -EACCES
I.MX8MQ system resume normally after applying the fix. Here is resume log:
[ 71.068807] galcore 38000000.gpu3d: PM: calling genpd_resume_noirq @ 530, parent: platform
[ 71.077103] i2c_imx_xfer, pm_runtime_resume_and_get is 0
[ 71.083578] galcore 38000000.gpu3d: PM: genpd_resume_noirq returned 0 after 6490 usecs
[ 71.091526] mxc_hantro 38300000.vpu: PM: calling genpd_resume_noirq @ 530, parent: soc@0
[ 71.099638] i2c_imx_xfer, pm_runtime_resume_and_get is 0
[ 71.106091] mxc_hantro 38300000.vpu: PM: genpd_resume_noirq returned 0 after 6458 usecs
Signed-off-by: Carlos Song <carlos.song@nxp.com>
Signed-off-by: Haibo Chen <haibo.chen@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Link: https://lore.kernel.org/r/20241125142108.1613016-1-carlos.song@nxp.com
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
2024-11-25 22:21:08 +08:00
|
|
|
NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
|
|
|
pm_runtime_force_resume)
|
|
|
|
SYSTEM_SLEEP_PM_OPS(i2c_imx_suspend, i2c_imx_resume)
|
2024-08-07 15:14:56 -03:00
|
|
|
RUNTIME_PM_OPS(i2c_imx_runtime_suspend, i2c_imx_runtime_resume, NULL)
|
2015-12-11 10:24:09 +08:00
|
|
|
};
|
|
|
|
|
2009-01-30 10:32:28 +02:00
|
|
|
static struct platform_driver i2c_imx_driver = {
|
2013-10-08 22:35:34 +02:00
|
|
|
.probe = i2c_imx_probe,
|
2024-09-29 09:21:57 +02:00
|
|
|
.remove = i2c_imx_remove,
|
2015-12-11 10:24:09 +08:00
|
|
|
.driver = {
|
|
|
|
.name = DRIVER_NAME,
|
2024-08-07 15:14:56 -03:00
|
|
|
.pm = pm_ptr(&i2c_imx_pm_ops),
|
2011-09-08 15:09:35 +08:00
|
|
|
.of_match_table = i2c_imx_dt_ids,
|
2019-09-06 15:53:19 +08:00
|
|
|
.acpi_match_table = i2c_imx_acpi_ids,
|
2012-09-14 15:19:00 +08:00
|
|
|
},
|
2021-01-19 23:41:23 -03:00
|
|
|
.id_table = imx_i2c_devtype,
|
2009-01-30 10:32:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
static int __init i2c_adap_imx_init(void)
|
|
|
|
{
|
2013-10-08 22:35:34 +02:00
|
|
|
return platform_driver_register(&i2c_imx_driver);
|
2009-01-30 10:32:28 +02:00
|
|
|
}
|
2009-09-19 09:09:50 +02:00
|
|
|
subsys_initcall(i2c_adap_imx_init);
|
2009-01-30 10:32:28 +02:00
|
|
|
|
|
|
|
static void __exit i2c_adap_imx_exit(void)
|
|
|
|
{
|
|
|
|
platform_driver_unregister(&i2c_imx_driver);
|
|
|
|
}
|
|
|
|
module_exit(i2c_adap_imx_exit);
|
|
|
|
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_AUTHOR("Darius Augulis");
|
|
|
|
MODULE_DESCRIPTION("I2C adapter driver for IMX I2C bus");
|
|
|
|
MODULE_ALIAS("platform:" DRIVER_NAME);
|