From 3c91589a14c9a39b0e0e440b5af4273b6147a8d2 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 1 Aug 2024 09:41:25 +0200 Subject: [PATCH 1/7] i2c: pca954x: Remove pointer to GD There is no reason to have any pointer to GD that's why remove it. Signed-off-by: Michal Simek Reviewed-by: Heiko Schocher --- drivers/i2c/muxes/pca954x.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/i2c/muxes/pca954x.c b/drivers/i2c/muxes/pca954x.c index b4e3e16a976..795288fe2e9 100644 --- a/drivers/i2c/muxes/pca954x.c +++ b/drivers/i2c/muxes/pca954x.c @@ -10,12 +10,9 @@ #include #include #include -#include #include -DECLARE_GLOBAL_DATA_PTR; - enum pca_type { PCA9543, PCA9544, From 6d06fdb93c13e3b989091918aaa60b04b347ab55 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 1 Aug 2024 10:01:30 +0200 Subject: [PATCH 2/7] i2c: mux: Fix error path in i2c-arb-gpio There is no reason to use goto and just call return. Better is to call return directly which is done for some if/else parts. Also make no sense to setup ret to -ETIMEDOUT and then to 0. Return timeout directly. Signed-off-by: Michal Simek Reviewed-by: Heiko Schocher --- drivers/i2c/muxes/i2c-arb-gpio-challenge.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/i2c/muxes/i2c-arb-gpio-challenge.c b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c index a83d7cb0829..3d2ce0ca705 100644 --- a/drivers/i2c/muxes/i2c-arb-gpio-challenge.c +++ b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c @@ -54,7 +54,7 @@ int i2c_arbitrator_select(struct udevice *mux, struct udevice *bus, /* Indicate that we want to claim the bus */ ret = dm_gpio_set_value(&priv->ap_claim, 1); if (ret) - goto err; + return ret; udelay(priv->slew_delay_us); /* Wait for the EC to release it */ @@ -62,7 +62,7 @@ int i2c_arbitrator_select(struct udevice *mux, struct udevice *bus, while (get_timer(start_retry) < priv->wait_retry_ms) { ret = dm_gpio_get_value(&priv->ec_claim); if (ret < 0) { - goto err; + return ret; } else if (!ret) { /* We got it, so return */ return 0; @@ -75,17 +75,14 @@ int i2c_arbitrator_select(struct udevice *mux, struct udevice *bus, /* It didn't release, so give up, wait, and try again */ ret = dm_gpio_set_value(&priv->ap_claim, 0); if (ret) - goto err; + return ret; mdelay(priv->wait_retry_ms); } while (get_timer(start) < priv->wait_free_ms); /* Give up, release our claim */ printf("I2C: Could not claim bus, timeout %lu\n", get_timer(start)); - ret = -ETIMEDOUT; - ret = 0; -err: - return ret; + return -ETIMEDOUT; } static int i2c_arbitrator_probe(struct udevice *dev) From 8fc21bb8b2c6efefd6f98b3b58f2cdfe29419348 Mon Sep 17 00:00:00 2001 From: David Virag Date: Fri, 2 Aug 2024 21:19:15 +0200 Subject: [PATCH 3/7] i2c: samsung: Drop s3c24x0 specific code. This has been dead code for many years now. Remove it. Signed-off-by: David Virag Reviewed-by: Heiko Schocher --- drivers/i2c/exynos_hs_i2c.c | 4 ---- drivers/i2c/s3c24x0_i2c.c | 8 -------- 2 files changed, 12 deletions(-) diff --git a/drivers/i2c/exynos_hs_i2c.c b/drivers/i2c/exynos_hs_i2c.c index 2ab0bae4499..189ce6d5096 100644 --- a/drivers/i2c/exynos_hs_i2c.c +++ b/drivers/i2c/exynos_hs_i2c.c @@ -145,11 +145,7 @@ static int hsi2c_get_clk_details(struct s3c24x0_i2c_bus *i2c_bus) unsigned int i = 0, utemp0 = 0, utemp1 = 0; unsigned int t_ftl_cycle; -#if defined(CONFIG_ARCH_EXYNOS4) || defined(CONFIG_ARCH_EXYNOS5) clkin = get_i2c_clk(); -#else - clkin = get_PCLK(); -#endif /* FPCLK / FI2C = * (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) + 8 + 2 * FLT_CYCLE * uTemp0 = (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) diff --git a/drivers/i2c/s3c24x0_i2c.c b/drivers/i2c/s3c24x0_i2c.c index 72d2ab0f73d..ae3a801cad1 100644 --- a/drivers/i2c/s3c24x0_i2c.c +++ b/drivers/i2c/s3c24x0_i2c.c @@ -8,14 +8,10 @@ #include #include #include -#if defined(CONFIG_ARCH_EXYNOS4) || defined(CONFIG_ARCH_EXYNOS5) #include #include #include #include -#else -#include -#endif #include #include #include @@ -53,11 +49,7 @@ static void read_write_byte(struct s3c24x0_i2c *i2c) static void i2c_ch_init(struct s3c24x0_i2c *i2c, int speed, int slaveadd) { ulong freq, pres = 16, div; -#if defined(CONFIG_ARCH_EXYNOS4) || defined(CONFIG_ARCH_EXYNOS5) freq = get_i2c_clk(); -#else - freq = get_PCLK(); -#endif /* calculate prescaler and divisor values */ if ((freq / pres / (16 + 1)) > speed) /* set prescaler to 512 */ From 101f4e669d1f976032ac5e2165e4b415ab61a41d Mon Sep 17 00:00:00 2001 From: David Virag Date: Fri, 2 Aug 2024 21:19:16 +0200 Subject: [PATCH 4/7] i2c: samsung: Support platforms other than EXYNOS4 and EXYNOS5 Newer Samsung SoCs (including newer Exynos, ExynosAuto, Google Tensor) still use these IPs, or slightly newer versions of it. Make these drivers available on these platforms by guarding EXYNOS4/EXYNOS5 specific code behind their configs, and using CCF for clocks on other platforms. Tested S3C I2C driver on Exynos7885. This along with extended clock driver should enable S3C I2C on Exynos850. Signed-off-by: David Virag Tested-by: Henrik Grimler Reviewed-by: Heiko Schocher --- drivers/i2c/Kconfig | 2 +- drivers/i2c/exynos_hs_i2c.c | 25 +++++++++++++++++++++++-- drivers/i2c/s3c24x0_i2c.c | 30 +++++++++++++++++++++++++++--- drivers/i2c/s3c24x0_i2c.h | 2 ++ 4 files changed, 53 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index cba7f848942..52067fa7c1f 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -650,7 +650,7 @@ config SYS_I2C_GENI config SYS_I2C_S3C24X0 bool "Samsung I2C driver" - depends on (ARCH_EXYNOS4 || ARCH_EXYNOS5) && DM_I2C + depends on DM_I2C help Support for Samsung I2C controller as Samsung SoCs. diff --git a/drivers/i2c/exynos_hs_i2c.c b/drivers/i2c/exynos_hs_i2c.c index 189ce6d5096..fa0d1c8f64a 100644 --- a/drivers/i2c/exynos_hs_i2c.c +++ b/drivers/i2c/exynos_hs_i2c.c @@ -9,11 +9,15 @@ #include #include #include +#if IS_ENABLED(CONFIG_ARCH_EXYNOS4) || IS_ENABLED(CONFIG_ARCH_EXYNOS5) #include #include #include +#endif #include +#include #include +#include #include "s3c24x0_i2c.h" DECLARE_GLOBAL_DATA_PTR; @@ -137,15 +141,26 @@ static int hsi2c_wait_for_trx(struct exynos5_hsi2c *i2c) return I2C_NOK_TOUT; } -static int hsi2c_get_clk_details(struct s3c24x0_i2c_bus *i2c_bus) +static int hsi2c_get_clk_details(struct udevice *dev) { + struct s3c24x0_i2c_bus *i2c_bus = dev_get_priv(dev); struct exynos5_hsi2c *hsregs = i2c_bus->hsregs; ulong clkin; unsigned int op_clk = i2c_bus->clock_frequency; unsigned int i = 0, utemp0 = 0, utemp1 = 0; unsigned int t_ftl_cycle; +#if IS_ENABLED(CONFIG_ARCH_EXYNOS4) || IS_ENABLED(CONFIG_ARCH_EXYNOS5) clkin = get_i2c_clk(); +#else + struct clk clk; + int ret; + + ret = clk_get_by_name(dev, "hsi2c", &clk); + if (ret < 0) + return ret; + clkin = clk_get_rate(&clk); +#endif /* FPCLK / FI2C = * (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) + 8 + 2 * FLT_CYCLE * uTemp0 = (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) @@ -487,7 +502,7 @@ static int s3c24x0_i2c_set_bus_speed(struct udevice *dev, unsigned int speed) i2c_bus->clock_frequency = speed; - if (hsi2c_get_clk_details(i2c_bus)) + if (hsi2c_get_clk_details(dev)) return -EFAULT; hsi2c_ch_init(i2c_bus); @@ -514,7 +529,9 @@ static int s3c24x0_i2c_probe(struct udevice *dev, uint chip, uint chip_flags) static int s3c_i2c_of_to_plat(struct udevice *dev) { +#if IS_ENABLED(CONFIG_ARCH_EXYNOS4) || IS_ENABLED(CONFIG_ARCH_EXYNOS5) const void *blob = gd->fdt_blob; +#endif struct s3c24x0_i2c_bus *i2c_bus = dev_get_priv(dev); int node; @@ -522,7 +539,9 @@ static int s3c_i2c_of_to_plat(struct udevice *dev) i2c_bus->hsregs = dev_read_addr_ptr(dev); +#if IS_ENABLED(CONFIG_ARCH_EXYNOS4) || IS_ENABLED(CONFIG_ARCH_EXYNOS5) i2c_bus->id = pinmux_decode_periph_id(blob, node); +#endif i2c_bus->clock_frequency = dev_read_u32_default(dev, "clock-frequency", @@ -530,7 +549,9 @@ static int s3c_i2c_of_to_plat(struct udevice *dev) i2c_bus->node = node; i2c_bus->bus_num = dev_seq(dev); +#if IS_ENABLED(CONFIG_ARCH_EXYNOS4) || IS_ENABLED(CONFIG_ARCH_EXYNOS5) exynos_pinmux_config(i2c_bus->id, PINMUX_FLAG_HS_MODE); +#endif i2c_bus->active = true; diff --git a/drivers/i2c/s3c24x0_i2c.c b/drivers/i2c/s3c24x0_i2c.c index ae3a801cad1..ade1ad6cef7 100644 --- a/drivers/i2c/s3c24x0_i2c.c +++ b/drivers/i2c/s3c24x0_i2c.c @@ -9,12 +9,15 @@ #include #include #include +#if IS_ENABLED(CONFIG_ARCH_EXYNOS4) || IS_ENABLED(CONFIG_ARCH_EXYNOS5) #include #include #include +#endif #include #include #include +#include #include "s3c24x0_i2c.h" DECLARE_GLOBAL_DATA_PTR; @@ -46,10 +49,23 @@ static void read_write_byte(struct s3c24x0_i2c *i2c) clrbits_le32(&i2c->iiccon, I2CCON_IRPND); } -static void i2c_ch_init(struct s3c24x0_i2c *i2c, int speed, int slaveadd) +static int i2c_ch_init(struct udevice *dev, int speed, int slaveadd) { + struct s3c24x0_i2c_bus *i2c_bus = dev_get_priv(dev); + struct s3c24x0_i2c *i2c = i2c_bus->regs; ulong freq, pres = 16, div; + +#if IS_ENABLED(CONFIG_ARCH_EXYNOS4) || defined(CONFIG_ARCH_EXYNOS5) freq = get_i2c_clk(); +#else + struct clk clk; + int ret; + + ret = clk_get_by_name(dev, "i2c", &clk); + if (ret < 0) + return ret; + freq = clk_get_rate(&clk); +#endif /* calculate prescaler and divisor values */ if ((freq / pres / (16 + 1)) > speed) /* set prescaler to 512 */ @@ -67,6 +83,7 @@ static void i2c_ch_init(struct s3c24x0_i2c *i2c, int speed, int slaveadd) writel(slaveadd, &i2c->iicadd); /* program Master Transmit (and implicit STOP) */ writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat); + return 0; } #define SYS_I2C_S3C24X0_SLAVE_ADDR 0 @@ -77,8 +94,9 @@ static int s3c24x0_i2c_set_bus_speed(struct udevice *dev, unsigned int speed) i2c_bus->clock_frequency = speed; - i2c_ch_init(i2c_bus->regs, i2c_bus->clock_frequency, - SYS_I2C_S3C24X0_SLAVE_ADDR); + if (i2c_ch_init(dev, i2c_bus->clock_frequency, + SYS_I2C_S3C24X0_SLAVE_ADDR)) + return -EFAULT; return 0; } @@ -293,7 +311,9 @@ static int s3c24x0_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, static int s3c_i2c_of_to_plat(struct udevice *dev) { +#if IS_ENABLED(CONFIG_ARCH_EXYNOS4) || IS_ENABLED(CONFIG_ARCH_EXYNOS5) const void *blob = gd->fdt_blob; +#endif struct s3c24x0_i2c_bus *i2c_bus = dev_get_priv(dev); int node; @@ -301,7 +321,9 @@ static int s3c_i2c_of_to_plat(struct udevice *dev) i2c_bus->regs = dev_read_addr_ptr(dev); +#if IS_ENABLED(CONFIG_ARCH_EXYNOS4) || IS_ENABLED(CONFIG_ARCH_EXYNOS5) i2c_bus->id = pinmux_decode_periph_id(blob, node); +#endif i2c_bus->clock_frequency = dev_read_u32_default(dev, "clock-frequency", @@ -309,7 +331,9 @@ static int s3c_i2c_of_to_plat(struct udevice *dev) i2c_bus->node = node; i2c_bus->bus_num = dev_seq(dev); +#if IS_ENABLED(CONFIG_ARCH_EXYNOS4) || IS_ENABLED(CONFIG_ARCH_EXYNOS5) exynos_pinmux_config(i2c_bus->id, 0); +#endif i2c_bus->active = true; diff --git a/drivers/i2c/s3c24x0_i2c.h b/drivers/i2c/s3c24x0_i2c.h index ec8f1acaef5..12249d5c141 100644 --- a/drivers/i2c/s3c24x0_i2c.h +++ b/drivers/i2c/s3c24x0_i2c.h @@ -54,7 +54,9 @@ struct s3c24x0_i2c_bus { struct exynos5_hsi2c *hsregs; int is_highspeed; /* High speed type, rather than I2C */ unsigned clock_frequency; +#if IS_ENABLED(CONFIG_ARCH_EXYNOS4) || IS_ENABLED(CONFIG_ARCH_EXYNOS5) int id; +#endif unsigned clk_cycle; unsigned clk_div; }; From ed1842623f6c8805fd75704a71c6fbf3af6b8327 Mon Sep 17 00:00:00 2001 From: Fedor Ross Date: Wed, 7 Aug 2024 16:07:59 +0200 Subject: [PATCH 5/7] i2c: imx_lpi2c: Fix a typo in bus_i2c_receive Fix a typo in a debug message. It should be 'for' not 'fot' . Signed-off-by: Fedor Ross --- drivers/i2c/imx_lpi2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/imx_lpi2c.c b/drivers/i2c/imx_lpi2c.c index a1be841b119..3c43f61d819 100644 --- a/drivers/i2c/imx_lpi2c.c +++ b/drivers/i2c/imx_lpi2c.c @@ -130,7 +130,7 @@ static int bus_i2c_receive(struct udevice *bus, u8 *rxbuf, int len) result = bus_i2c_wait_for_tx_ready(regs); if (result) { - debug("i2c: receive wait fot tx ready: %d\n", result); + debug("i2c: receive wait for tx ready: %d\n", result); return result; } From 34c6731abe4fa5efadbb7519a050d2fcbdba3cab Mon Sep 17 00:00:00 2001 From: Fedor Ross Date: Wed, 7 Aug 2024 16:08:00 +0200 Subject: [PATCH 6/7] i2c: imx_lpi2c: Replace hard-coded bus speed value with bus->speed_hz Instead of using the hard-coded bus speed value I2C_SPEED_STANDARD_RATE, use the actual configured bus speed. This way the bus speed doesn't change suddenly after calling the imx_lpi2c_probe_chip() function for example. Signed-off-by: Fedor Ross --- drivers/i2c/imx_lpi2c.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/imx_lpi2c.c b/drivers/i2c/imx_lpi2c.c index 3c43f61d819..54db6fa863f 100644 --- a/drivers/i2c/imx_lpi2c.c +++ b/drivers/i2c/imx_lpi2c.c @@ -19,7 +19,7 @@ #define LPI2C_NACK_TOUT_MS 1 #define LPI2C_TIMEOUT_MS 100 -static int bus_i2c_init(struct udevice *bus, int speed); +static int bus_i2c_init(struct udevice *bus); /* Weak linked function for overridden by some SoC power function */ int __weak init_i2c_power(unsigned i2c_num) @@ -172,7 +172,7 @@ static int bus_i2c_start(struct udevice *bus, u8 addr, u8 dir) debug("i2c: start check busy bus: 0x%x\n", result); /* Try to init the lpi2c then check the bus busy again */ - bus_i2c_init(bus, I2C_SPEED_STANDARD_RATE); + bus_i2c_init(bus); result = imx_lpci2c_check_busy_bus(regs); if (result) { printf("i2c: Error check busy bus: 0x%x\n", result); @@ -344,11 +344,14 @@ static int bus_i2c_set_bus_speed(struct udevice *bus, int speed) return 0; } -static int bus_i2c_init(struct udevice *bus, int speed) +static int bus_i2c_init(struct udevice *bus) { u32 val; int ret; + struct dm_i2c_bus *i2c = dev_get_uclass_priv(bus); + int speed = i2c->speed_hz; + struct imx_lpi2c_bus *i2c_bus = dev_get_priv(bus); struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)(i2c_bus->base); /* reset peripheral */ @@ -388,13 +391,13 @@ static int imx_lpi2c_probe_chip(struct udevice *bus, u32 chip, result = bus_i2c_start(bus, chip, 0); if (result) { bus_i2c_stop(bus); - bus_i2c_init(bus, I2C_SPEED_STANDARD_RATE); + bus_i2c_init(bus); return result; } result = bus_i2c_stop(bus); if (result) - bus_i2c_init(bus, I2C_SPEED_STANDARD_RATE); + bus_i2c_init(bus); return result; } @@ -489,7 +492,7 @@ static int imx_lpi2c_probe(struct udevice *bus) return ret; } - ret = bus_i2c_init(bus, I2C_SPEED_STANDARD_RATE); + ret = bus_i2c_init(bus); if (ret < 0) return ret; From ec07bcc8eafb8d201d15facc969fe96600660095 Mon Sep 17 00:00:00 2001 From: Fedor Ross Date: Wed, 7 Aug 2024 16:08:01 +0200 Subject: [PATCH 7/7] i2c: imx_lpi2c: Support read transfers longer than 256 bytes The TXFIFO register of LPI2C only has one byte length, and if the length of the data that needs to be read exceeds 256 bytes, it needs to be written to TXFIFO multiple times. Signed-off-by: Fedor Ross --- drivers/i2c/imx_lpi2c.c | 72 +++++++++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 25 deletions(-) diff --git a/drivers/i2c/imx_lpi2c.c b/drivers/i2c/imx_lpi2c.c index 54db6fa863f..4636da9f301 100644 --- a/drivers/i2c/imx_lpi2c.c +++ b/drivers/i2c/imx_lpi2c.c @@ -19,6 +19,9 @@ #define LPI2C_NACK_TOUT_MS 1 #define LPI2C_TIMEOUT_MS 100 +#define LPI2C_CHUNK_DATA 256U +#define LPI2C_CHUNK_LEN_MIN 1U + static int bus_i2c_init(struct udevice *bus); /* Weak linked function for overridden by some SoC power function */ @@ -118,8 +121,10 @@ static int bus_i2c_send(struct udevice *bus, u8 *txbuf, int len) static int bus_i2c_receive(struct udevice *bus, u8 *rxbuf, int len) { + struct dm_i2c_bus *i2c = dev_get_uclass_priv(bus); struct imx_lpi2c_bus *i2c_bus = dev_get_priv(bus); struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)(i2c_bus->base); + unsigned int chunk_len, rx_remain, timeout; lpi2c_status_t result = LPI2C_SUCESS; u32 val; ulong start_time = get_timer(0); @@ -128,33 +133,50 @@ static int bus_i2c_receive(struct udevice *bus, u8 *rxbuf, int len) if (!len) return result; - result = bus_i2c_wait_for_tx_ready(regs); - if (result) { - debug("i2c: receive wait for tx ready: %d\n", result); - return result; - } + /* + * Extend the timeout for a bulk read if needed. + * The calculated timeout is the result of multiplying the + * transfer length with 8 bit + ACK + one clock of extra time, + * considering the I2C bus frequency. + */ + timeout = max(len * 10 * 1000 / i2c->speed_hz, LPI2C_TIMEOUT_MS); - /* clear all status flags */ - writel(0x7f00, ®s->msr); - /* send receive command */ - val = LPI2C_MTDR_CMD(0x1) | LPI2C_MTDR_DATA(len - 1); - writel(val, ®s->mtdr); + rx_remain = len; + while (rx_remain > 0) { + chunk_len = clamp(rx_remain, LPI2C_CHUNK_LEN_MIN, LPI2C_CHUNK_DATA) - 1; - while (len--) { - do { - result = imx_lpci2c_check_clear_error(regs); - if (result) { - debug("i2c: receive check clear error: %d\n", - result); - return result; - } - if (get_timer(start_time) > LPI2C_TIMEOUT_MS) { - debug("i2c: receive mrdr: timeout\n"); - return -1; - } - val = readl(®s->mrdr); - } while (val & LPI2C_MRDR_RXEMPTY_MASK); - *rxbuf++ = LPI2C_MRDR_DATA(val); + result = bus_i2c_wait_for_tx_ready(regs); + if (result) { + debug("i2c: receive wait for tx ready: %d\n", result); + return result; + } + + /* clear all status flags */ + writel(0x7f00, ®s->msr); + /* send receive command */ + writel(LPI2C_MTDR_CMD(0x1) | LPI2C_MTDR_DATA(chunk_len), ®s->mtdr); + rx_remain = rx_remain - (chunk_len & 0xff) - 1; + + while (len--) { + do { + result = imx_lpci2c_check_clear_error(regs); + if (result) { + debug("i2c: receive check clear error: %d\n", + result); + return result; + } + if (get_timer(start_time) > timeout) { + debug("i2c: receive mrdr: timeout\n"); + return -1; + } + val = readl(®s->mrdr); + } while (val & LPI2C_MRDR_RXEMPTY_MASK); + *rxbuf++ = LPI2C_MRDR_DATA(val); + + /* send next receive command before controller NACKs last byte */ + if ((len - rx_remain) < 2 && rx_remain > 0) + break; + } } return result;