From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-ot1-f51.google.com (mail-ot1-f51.google.com [209.85.210.51]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8D4DB3264E5 for ; Fri, 5 Jun 2026 13:15:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.51 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780665354; cv=none; b=e6KYGvo0TH9r7+wawIT1ZMjWgOnMooyPtMgoqZGOrcFOXX9UGPoOxe9Mg0l/3gv1M8LQhjYNj+iU5VfdDv7+6X/XcZPFttS8eizMAQgYWYYgamIhL4TGXSV5XCZ44gy/6cD5UbR+DWJotCqrH3twAyIllXS18V+RQf/RPH10SU0= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780665354; c=relaxed/simple; bh=8GzRpG09VCGlsJ7Ud+t3q/W6QVKe4lC7YNpFfCZi0Wo=; h=Message-ID:Date:MIME-Version:Subject:To:Cc:References:From: In-Reply-To:Content-Type; b=upgVUDGYHS1xVoLWDy8knQiq/DhJXgJw0sKfO/7tvKbbIBQJ43FbYo0dPgV8EUveF9ui6rbNFjVIncBCiCS8DxEcADoAnjd6v99Oyd/8T4USbur+NI63CaB9qNu0q9AkIba30G6852AzLmgspl/YRt/IUkWBh2aMhkpqFij4if8= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=riscstar.com; spf=pass smtp.mailfrom=riscstar.com; dkim=pass (2048-bit key) header.d=riscstar-com.20251104.gappssmtp.com header.i=@riscstar-com.20251104.gappssmtp.com header.b=pi20fCi3; arc=none smtp.client-ip=209.85.210.51 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=riscstar.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=riscstar.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=riscstar-com.20251104.gappssmtp.com header.i=@riscstar-com.20251104.gappssmtp.com header.b="pi20fCi3" Received: by mail-ot1-f51.google.com with SMTP id 46e09a7af769-7e6f586a0d5so822385a34.0 for ; Fri, 05 Jun 2026 06:15:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=riscstar-com.20251104.gappssmtp.com; s=20251104; t=1780665349; x=1781270149; darn=vger.kernel.org; h=content-transfer-encoding:in-reply-to:from:content-language :references:cc:to:subject:user-agent:mime-version:date:message-id :from:to:cc:subject:date:message-id:reply-to; bh=80H/6gzN2Fx+w9MgBdvx+Y3iWbR4+/4hOV49CAjxSdQ=; b=pi20fCi3lFkCFXyoeWKSOoeenQnlBR+136Mna1SVx8ppb6PQfZSaAKtg6FuZbCCaPI A+DVA4LZVaUIAG2kkaGK0E0kyNpywyC5ouH0B2h/aj6lD6Gy3lQY3aoZLWQpGH3gpw4H VrcfuTNx35snc51s7EzIYpVlEJke4Dp1uH9pgURNHFK3WU41T3yswHpp1G+QTJUUFpIR dlI2SEAzOsOvE89hGcVe6E84Qt8fXqrAfU59/sxL4Zod6GDjooIKapdeXaMqj1aOD/90 8uN2UWrpNi2njAyFQfilcrftDcaRtIZZunFv0je+u7rX8uoQJ8YcfDINFcAvz4vPCfJP gV+w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780665349; x=1781270149; h=content-transfer-encoding:in-reply-to:from:content-language :references:cc:to:subject:user-agent:mime-version:date:message-id :x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=80H/6gzN2Fx+w9MgBdvx+Y3iWbR4+/4hOV49CAjxSdQ=; b=jtwx1+cycSx+nY9mS0ya0Gbrk3JvAlrpnQSpB0rj4FgEANe0JV2mA0cjXdYBOJxnH/ uql0je0poggv/zBzgjMy5UTGgDOOzYWh+M537+Ubc3aPT0oaL/oC7QDp7L662j9rw6ui VT2oDRXkic13AbLOwoJUOGkg8qnTPL7TfAuzVbKbPE6QJGtDv6pMizrzRoZBYN9q1Ysm aIEIIn4SM4kvJMOL1lG0AaLb7fGogOfye+A6ykDtxV5wN6UNGlQXHCksyfh2WntPH9Br 52Qk6p7hu3vy3sNkn6IANB2Rstx9Lb3x4c4Ab47UvQSZZ/65AtGuiqBEmH+YssQZISAQ 6hRw== X-Forwarded-Encrypted: i=1; AFNElJ9Fguc9fB3QdxghS8+4qmgEoa+1AZVoriYr8TsMwVGDUOn/KqnfSAfqjo1wCq+yLrF6NgU5vBmDO4zcTS4=@vger.kernel.org X-Gm-Message-State: AOJu0YzOq/RLusw+uy7XX2i0ZolvZUIeiTaosJq8rEZcLsUTzDfjm0lN X/i/8/fxCoRXtgEGxDV+C37TiRemjiFjWTjLvEPbk8ND/ERLronfZPizeCnlDpQxG3A= X-Gm-Gg: Acq92OEAPNopiV75dT637o6rLWsTef7NA/UO8OknDGjomZMkEZLQyLikdnUSQILCloI J/aIY0WkP2DdsLL8+mctchqO+V0hW90NXC7h0ojF48kv42iVw8yr/tu9M0ynvDA1C2PT4JUZVz/ TrNZXqlyagRHdSMCI84dx/F1Pcdqcu4TpmEVlhMyVBtg5BB3ul/tGCFZRxQhoHoGqOvyF0AxYvD tnTPF2hCjp8JUSd3xrKY7hx9uzRTZWQVQjWduz6Z94EyWNAUdTj6cXPVzYb1y/yfcakvD3XAbWX 8dXfvPvh/pLpq4aZE1EhKhW3Dd5A7V5sWn93RuJqkzjRnOTMKMDa43IUvFdDRIIxKBYHqqEFO/w t83OD1qwSJ99uARw/+knhKWioCj4AXsSfOav+L8ASSA5AjCBN7U7++kXwCtokfWu5Sb+OTNtrzS N/hrOmiuEc/9oy8xbMwmxV7uyFv+3XLZzKQbo= X-Received: by 2002:a05:6830:2714:b0:7e6:f709:a2bb with SMTP id 46e09a7af769-7e70ca505d5mr1629026a34.20.1780665349316; Fri, 05 Jun 2026 06:15:49 -0700 (PDT) Received: from [172.22.22.234] ([73.62.185.64]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-7e6e746965asm5812794a34.4.2026.06.05.06.15.47 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Fri, 05 Jun 2026 06:15:48 -0700 (PDT) Message-ID: Date: Fri, 5 Jun 2026 08:15:46 -0500 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH v7 1/2] i2c: spacemit: configure ILCR/IWCR for accurate SCL frequency To: Troy Mitchell , Andi Shyti , Yixun Lan Cc: linux-i2c@vger.kernel.org, linux-kernel@vger.kernel.org, linux-riscv@lists.infradead.org, spacemit@lists.linux.dev References: <20260508-k1-i2c-ilcr-v7-0-8c2dde5c3ed5@linux.spacemit.com> <20260508-k1-i2c-ilcr-v7-1-8c2dde5c3ed5@linux.spacemit.com> Content-Language: en-US From: Alex Elder In-Reply-To: <20260508-k1-i2c-ilcr-v7-1-8c2dde5c3ed5@linux.spacemit.com> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit On 5/8/26 2:25 AM, Troy Mitchell wrote: > The SpacemiT I2C controller's SCL (Serial Clock Line) frequency for > master mode operations is determined by the ILCR (I2C Load Count Register). > Previously, the driver relied on the hardware's reset default > values for this register. > > The hardware's default ILCR values (SLV=0x156, FLV=0x5d) yield SCL > frequencies lower than intended. For example, with the default > 31.5 MHz input clock, these default settings result in an SCL > frequency of approximately 93 kHz (standard mode) when targeting 100 kHz, > and approximately 338 kHz (fast mode) when targeting 400 kHz. > These frequencies are below the 100 kHz/400 kHz nominal speeds. > > This patch integrates the SCL frequency management into > the Common Clock Framework (CCF). Specifically, the ILCR register, > which acts as a frequency divider for the SCL clock, is now registered > as a managed clock (scl_clk) within the CCF. > > The actual hardware timing formulas are: > - standard mode: SCL = FCLK / (2 * SLV + 8) > - fast mode: SCL = FCLK / (2 * FLV + 10) > > These formulas are only valid when the IWCR (Wait Count Register) is > programmed to 0x142A, a value specified by the I2C IP designer. The > driver now initializes IWCR to this value during controller init. > > Reviewed-by: Yixun Lan > Signed-off-by: Troy Mitchell I have several comments below, but none of them are really critical, they're just suggestions. Consider incorporating each of them, but that's up to you. Either way: Reviewed-by: Alex Elder > --- > Changelog in v7: > - break SPACEMIT_IWCR_INIT_VALUE into FIELD_PREP fields with GENMASK definitions > - extract spacemit_i2c_calc_lv() as shared helper for set_rate and determine_rate > - switch from devm_clk_register() to devm_clk_hw_register() (deprecated API) > - use devm_clk_hw_get_clk() to obtain struct clk* from clk_hw > - Link to v6: https://lore.kernel.org/all/20260429-k1-i2c-ilcr-v6-1-1c7a5a5a8b24@linux.spacemit.com/ > > Changelog in v6: > - fix SCL frequency calculation to match hardware timing formulas > (SCL = FCLK / (2*SLV+8) for standard, FCLK / (2*FLV+10) for fast) > - initialize IWCR to 0x142A during init(required by I2C IP for > correct SCL timing) > - use DIV_ROUND_CLOSEST() instead of DIV_ROUND_UP() for more accurate frequency > - use field_prep() instead of manual bit shift for ILCR register programming > - replace .round_rate with .determine_rate (round_rate was removed from clk_ops) > - remove _MAX_VALUE macros (no longer needed after removing max_lv validation) > - remove redundant max_lv validation in set_rate (determine_rate guarantees valid values) > - simplify scl_clk_disable_unprepare callback to take clk pointer directly > - remove unused parent parameter from spacemit_i2c_register_scl_clk() > - remove stale description about whitespace cleanup from commit message > - Link to v5: https://lore.kernel.org/r/20251226-k1-i2c-ilcr-v5-0-b5807b7dd0e6@linux.spacemit.com > > Changelog in v5: > - use __ffs() instead of *_SHIFT > - remove useless *_SHIFT > - check return value when scl clk name array is truncated > - rebase to v6.19-rc1 > - Link to v3: https://lore.kernel.org/all/20251017-k1-i2c-ilcr-v4-1-eed4903ecdb9@linux.spacemit.com/ > > Changelog in v4: > - initialize clk_init_data with {} so that init.flags is implicitly set to 0 > - minor cleanup and style fixes for better readability > - remove unused spacemit_i2c_scl_clk_exclusive_put() cleanup callback > - replace clk_set_rate_exclusive()/clk_rate_exclusive_put() pair with clk_set_rate() > - simplify LCR LV field macros by using FIELD_GET/FIELD_MAX helpers > - Link to v3: https://lore.kernel.org/all/20250814-k1-i2c-ilcr-v3-1-317723e74bcd@linux.spacemit.com/ > > Changelog in v3: > - use MASK macro in `recalc_rate` function > - rename clock name > - Link to v2: https://lore.kernel.org/r/20250718-k1-i2c-ilcr-v2-1-b4c68f13dcb1@linux.spacemit.com > > Changelog in v2: > - Align line breaks. > - Check `lv` in `clk_set_rate` function. > - Force fast mode when SCL frequency is illegal or unavailable. > - Change "linux/bits.h" to > - Kconfig: Add dependency on CCF. > - Link to v1: https://lore.kernel.org/all/20250710-k1-i2c-ilcr-v1-1-188d1f460c7d@linux.spacemit.com/ > --- > drivers/i2c/busses/Kconfig | 2 +- > drivers/i2c/busses/i2c-k1.c | 174 ++++++++++++++++++++++++++++++++++++++++++-- > 2 files changed, 167 insertions(+), 9 deletions(-) > > diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig > index 8c935f867a37..89898fff1967 100644 > --- a/drivers/i2c/busses/Kconfig > +++ b/drivers/i2c/busses/Kconfig > @@ -793,7 +793,7 @@ config I2C_JZ4780 > config I2C_K1 > tristate "SpacemiT K1 I2C adapter" > depends on ARCH_SPACEMIT || COMPILE_TEST > - depends on OF > + depends on OF && COMMON_CLK > help > This option enables support for the I2C interface on the SpacemiT K1 > platform. > diff --git a/drivers/i2c/busses/i2c-k1.c b/drivers/i2c/busses/i2c-k1.c > index 9152cf436bea..c6fe2052e479 100644 > --- a/drivers/i2c/busses/i2c-k1.c > +++ b/drivers/i2c/busses/i2c-k1.c > @@ -4,7 +4,9 @@ > */ > > #include > +#include > #include > +#include > #include > #include > #include > @@ -17,6 +19,8 @@ > #define SPACEMIT_ISR 0x4 /* Status register */ > #define SPACEMIT_IDBR 0xc /* Data buffer register */ > #define SPACEMIT_IRCR 0x18 /* Reset cycle counter */ > +#define SPACEMIT_ILCR 0x10 /* Load Count Register */ > +#define SPACEMIT_IWCR 0x14 /* Wait Count Register */ > #define SPACEMIT_IBMR 0x1c /* Bus monitor register */ > > /* SPACEMIT_ICR register fields */ > @@ -88,6 +92,19 @@ > #define SPACEMIT_BMR_SDA BIT(0) /* SDA line level */ > #define SPACEMIT_BMR_SCL BIT(1) /* SCL line level */ > /* SPACEMIT_ILCR register fields */ > +#define SPACEMIT_LCR_LV_STANDARD_MASK GENMASK(8, 0) > +#define SPACEMIT_LCR_LV_FAST_MASK GENMASK(17, 9) > + > +/* SPACEMIT_IWCR register fields */ > +#define SPACEMIT_WCR_COUNT GENMASK(4, 0) > +#define SPACEMIT_WCR_HS_COUNT1 GENMASK(9, 5) > +#define SPACEMIT_WCR_HS_COUNT2 GENMASK(14, 10) > + > +/* Required by I2C IP for correct SCL timing */ > +#define SPACEMIT_IWCR_INIT_VALUE (FIELD_PREP(SPACEMIT_WCR_COUNT, 10) | \ > + FIELD_PREP(SPACEMIT_WCR_HS_COUNT1, 1) | \ > + FIELD_PREP(SPACEMIT_WCR_HS_COUNT2, 5)) > + > /* i2c bus recover timeout: us */ > #define SPACEMIT_I2C_BUS_BUSY_TIMEOUT 100000 > > @@ -109,11 +126,20 @@ enum spacemit_i2c_state { > SPACEMIT_STATE_WRITE, > }; > > +enum spacemit_i2c_mode { > + SPACEMIT_MODE_STANDARD, > + SPACEMIT_MODE_FAST > +}; > + > /* i2c-spacemit driver's main struct */ > struct spacemit_i2c_dev { > struct device *dev; > struct i2c_adapter adapt; > > + struct clk_hw scl_clk_hw; > + struct clk *scl_clk; > + enum spacemit_i2c_mode mode; > + > /* hardware resources */ > void __iomem *base; > int irq; > @@ -135,6 +161,85 @@ struct spacemit_i2c_dev { > u32 status; > }; > > +static void spacemit_i2c_scl_clk_disable_unprepare(void *data) > +{ I know a cast from void pointer is not needed, but I normally would expect to see this here: struct clk *clk = data; > + clk_disable_unprepare(data); > +} > + > +/* > + * Calculate the ILCR divider value (lv) from the target SCL rate. > + * > + * Hardware timing formulas: > + * - standard mode: SCL = FCLK / (2 * SLV + 8) > + * - fast mode: SCL = FCLK / (2 * FLV + 10) This is a very clear description of what you're doing here. > + */ > +static u32 spacemit_i2c_calc_lv(struct spacemit_i2c_dev *i2c, > + unsigned long parent_rate, > + unsigned long target_rate) > +{ > + u32 offset, denom; > + > + offset = (i2c->mode == SPACEMIT_MODE_STANDARD) ? 8 : 10; No need for the parentheses above. > + denom = DIV_ROUND_CLOSEST(parent_rate, target_rate); > + > + return (denom <= offset) ? 0 : DIV_ROUND_CLOSEST(denom - offset, 2); Parentheses are not needed here around the condition here either (and in general for simple ternary conditional operators). > +} > + > +static int spacemit_i2c_clk_set_rate(struct clk_hw *hw, unsigned long rate, > + unsigned long parent_rate) > +{ > + struct spacemit_i2c_dev *i2c = container_of(hw, struct spacemit_i2c_dev, scl_clk_hw); > + u32 lv, lcr, mask; > + > + lv = spacemit_i2c_calc_lv(i2c, parent_rate, rate); > + > + mask = (i2c->mode == SPACEMIT_MODE_STANDARD) ? > + SPACEMIT_LCR_LV_STANDARD_MASK : SPACEMIT_LCR_LV_FAST_MASK; > + > + lcr = readl(i2c->base + SPACEMIT_ILCR); > + lcr &= ~mask; > + lcr |= field_prep(mask, lv); > + writel(lcr, i2c->base + SPACEMIT_ILCR); > + > + return 0; > +} > + > +static int spacemit_i2c_clk_determine_rate(struct clk_hw *hw, > + struct clk_rate_request *req) > +{ > + struct spacemit_i2c_dev *i2c = container_of(hw, struct spacemit_i2c_dev, scl_clk_hw); > + u32 lv, offset; > + > + lv = spacemit_i2c_calc_lv(i2c, req->best_parent_rate, req->rate); > + offset = (i2c->mode == SPACEMIT_MODE_STANDARD) ? 8 : 10; > + req->rate = DIV_ROUND_CLOSEST(req->best_parent_rate, lv * 2 + offset); > + > + return 0; > +} > + > +static unsigned long spacemit_i2c_clk_recalc_rate(struct clk_hw *hw, > + unsigned long parent_rate) > +{ > + struct spacemit_i2c_dev *i2c = container_of(hw, struct spacemit_i2c_dev, scl_clk_hw); > + u32 lcr, lv = 0; > + > + lcr = readl(i2c->base + SPACEMIT_ILCR); > + You could structure this function a little more like spacemit_i2c_clk_determine_rate(). Or you could create a tiny helper that computes the rate, given the parent rate, lv, and mode. The main reason for doing that is that the "complex" math is isolated rather than duplicated. > + if (i2c->mode == SPACEMIT_MODE_STANDARD) { > + lv = FIELD_GET(SPACEMIT_LCR_LV_STANDARD_MASK, lcr); > + return DIV_ROUND_CLOSEST(parent_rate, lv * 2 + 8); > + } > + > + lv = FIELD_GET(SPACEMIT_LCR_LV_FAST_MASK, lcr); > + return DIV_ROUND_CLOSEST(parent_rate, lv * 2 + 10); > +} > + > +static const struct clk_ops spacemit_i2c_clk_ops = { > + .set_rate = spacemit_i2c_clk_set_rate, > + .determine_rate = spacemit_i2c_clk_determine_rate, > + .recalc_rate = spacemit_i2c_clk_recalc_rate, > +}; > + > static void spacemit_i2c_enable(struct spacemit_i2c_dev *i2c) > { > u32 val; > @@ -153,6 +258,28 @@ static void spacemit_i2c_disable(struct spacemit_i2c_dev *i2c) > writel(val, i2c->base + SPACEMIT_ICR); > } > > +static int spacemit_i2c_register_scl_clk(struct spacemit_i2c_dev *i2c) > +{ > + struct clk_init_data init = {}; > + char name[64]; > + int ret; > + > + ret = snprintf(name, sizeof(name), "%s_scl_clk", dev_name(i2c->dev)); > + if (ret >= ARRAY_SIZE(name)) > + dev_warn(i2c->dev, "scl clock name truncated"); > + > + init.name = name; > + init.ops = &spacemit_i2c_clk_ops; > + init.parent_data = (struct clk_parent_data[]) { > + { .fw_name = "func" }, > + }; > + init.num_parents = 1; > + > + i2c->scl_clk_hw.init = &init; > + > + return devm_clk_hw_register(i2c->dev, &i2c->scl_clk_hw); > +} > + > static void spacemit_i2c_reset(struct spacemit_i2c_dev *i2c) > { > writel(SPACEMIT_CR_UR, i2c->base + SPACEMIT_ICR); > @@ -286,7 +413,7 @@ static void spacemit_i2c_init(struct spacemit_i2c_dev *i2c) > val |= SPACEMIT_CR_MSDIE; > } > > - if (i2c->clock_freq == SPACEMIT_I2C_MAX_FAST_MODE_FREQ) > + if (i2c->mode == SPACEMIT_MODE_FAST) > val |= SPACEMIT_CR_MODE_FAST; > > /* disable response to general call */ > @@ -309,6 +436,14 @@ static void spacemit_i2c_init(struct spacemit_i2c_dev *i2c) > writel(val, i2c->base + SPACEMIT_IRCR); > > spacemit_i2c_clear_int_status(i2c, SPACEMIT_I2C_INT_STATUS_MASK); > + > + /* > + * Initialize IWCR to the value specified by the I2C IP designer. > + * The SCL frequency formulas (SCL = FCLK / (2*SLV+8) for standard > + * mode, SCL = FCLK / (2*FLV+10) for fast mode) are only valid when > + * IWCR contains this specific value. > + */ Nice explanation for writing the magic number. -Alex > + writel(SPACEMIT_IWCR_INIT_VALUE, i2c->base + SPACEMIT_IWCR); > } > > static void spacemit_i2c_start(struct spacemit_i2c_dev *i2c) > @@ -703,14 +838,15 @@ static int spacemit_i2c_probe(struct platform_device *pdev) > dev_warn(dev, "failed to read clock-frequency property: %d\n", ret); > > /* For now, this driver doesn't support high-speed. */ > - if (!i2c->clock_freq || i2c->clock_freq > SPACEMIT_I2C_MAX_FAST_MODE_FREQ) { > - dev_warn(dev, "unsupported clock frequency %u; using %u\n", > - i2c->clock_freq, SPACEMIT_I2C_MAX_FAST_MODE_FREQ); > + if (i2c->clock_freq > SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ && > + i2c->clock_freq <= SPACEMIT_I2C_MAX_FAST_MODE_FREQ) { > + i2c->mode = SPACEMIT_MODE_FAST; > + } else if (i2c->clock_freq && i2c->clock_freq <= SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ) { > + i2c->mode = SPACEMIT_MODE_STANDARD; > + } else { > + dev_warn(i2c->dev, "invalid clock-frequency, fallback to fast mode"); > + i2c->mode = SPACEMIT_MODE_FAST; > i2c->clock_freq = SPACEMIT_I2C_MAX_FAST_MODE_FREQ; > - } else if (i2c->clock_freq < SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ) { > - dev_warn(dev, "unsupported clock frequency %u; using %u\n", > - i2c->clock_freq, SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ); > - i2c->clock_freq = SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ; > } > > i2c->dev = &pdev->dev; > @@ -732,6 +868,15 @@ static int spacemit_i2c_probe(struct platform_device *pdev) > if (IS_ERR(clk)) > return dev_err_probe(dev, PTR_ERR(clk), "failed to enable func clock"); > > + ret = spacemit_i2c_register_scl_clk(i2c); > + if (ret) > + return dev_err_probe(dev, ret, "failed to register scl clock\n"); > + > + i2c->scl_clk = devm_clk_hw_get_clk(dev, &i2c->scl_clk_hw, "scl"); > + if (IS_ERR(i2c->scl_clk)) > + return dev_err_probe(dev, PTR_ERR(i2c->scl_clk), > + "failed to get scl clock\n"); > + > clk = devm_clk_get_enabled(dev, "bus"); > if (IS_ERR(clk)) > return dev_err_probe(dev, PTR_ERR(clk), "failed to enable bus clock"); > @@ -741,6 +886,19 @@ static int spacemit_i2c_probe(struct platform_device *pdev) > return dev_err_probe(dev, PTR_ERR(rst), > "failed to acquire deasserted reset\n"); > > + ret = clk_set_rate(i2c->scl_clk, i2c->clock_freq); > + if (ret) > + return dev_err_probe(dev, ret, "failed to set rate for SCL clock"); > + > + ret = clk_prepare_enable(i2c->scl_clk); > + if (ret) > + return dev_err_probe(dev, ret, "failed to prepare and enable clock"); > + > + ret = devm_add_action_or_reset(dev, spacemit_i2c_scl_clk_disable_unprepare, > + i2c->scl_clk); > + if (ret) > + return ret; > + > spacemit_i2c_reset(i2c); > > i2c_set_adapdata(&i2c->adapt, i2c); >