From patchwork Thu Oct 22 00:00:12 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Hilliard X-Patchwork-Id: 3686 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10]) by mxe881.netcup.net (Postfix) with ESMTPS id 2EC8F1C249C for ; Thu, 22 Oct 2026 02:15:50 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=gmail.com; spf=pass (sender IP is 172.234.253.10) smtp.mailfrom=linux-sunxi+bounces-26785-noreply=patchwork.local@lists.linux.dev smtp.helo=sea.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.234.253.10 as permitted sender) client-ip=172.234.253.10; envelope-from=linux-sunxi+bounces-26785-noreply=patchwork.local@lists.linux.dev; helo=sea.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.77.114.92]) by sea.lore.kernel.org (Postfix) with ESMTP id AC4B43F394 for ; Thu, 22 Oct 2026 00:00:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass header.oldest-pass=0 smtp.remote-ip=127.0.0.1 ARC-Seal: i=2; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=pass; t=1792627217; b=OKx7ySSk5bEe8eG34SbZzdqGBa/0hNFfXHeaviss5STBFisz0E4ERZITNzkbvBEWUdgZ RcLxFXSgvKffdzLvLXudOFN2p6UhCPaNEDjXnp1svfq5G0NCdM5NszlupCjBMVUAZj2SK PSKbkL4T5YH00MSOAjIOjPTBEEZCyEpA35OaoeFwT4958/HOnt2rf4JaiEjKAn3P3O2H5 Ya4h1hCe8pbZR5irSaEFfUNqs1mwzrhgorCyTYaju6g0ExYSJBmWQCUeJ/os+2kH0un1S U0iZ2tlQLWc/j3oucX2+PiMz53b6z3gLlyoCVa3IjnRS742H3ecFwwOwdl+nk4aJ73Q== ARC-Message-Signature: i=2; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1792627217; h=X-Original-To:Received:DMARC-Filter:Received:DKIM-Signature: X-Google-DKIM-Signature:X-Forwarded-Encrypted:X-Gm-Message-State: X-Gm-Gg:X-Received:Received:From:Date:Subject:X-Mailing-List:List-Id: List-Post:List-Help:List-Subscribe:List-Unsubscribe:List-Owner: Precedence:MIME-Version:Content-Type:Content-Transfer-Encoding: Message-Id:X-B4-Tracking:X-Change-ID:To:Cc:X-Mailer; bh=YPuKNwql1HIbNGqoX33bMzSynPjwm2jO6JjQ4c2NRG0=; b=H2z8KfUgB9q1hhvcb+DZZyxYrZ1pfXG7Mlp/H5OFoin/XothB2DJh8wD47bP0CI/EW+L 7gOqi229Lv3sGsCqoAQmko3ilKSL9lM9vPREayaXR4DwDZ3O9TMS24b/PLZthjVvZupyg 9AASCowN0VhBXxr7SP1qQ+2XR+Tvj+kDzU4lSZ6W0OqCd8GxYOplOsVLAunlegpzeICcg obbISyRn7CoslZ9ZEupvt7Mzhpo3IWoH0Vdzxj2DmmTPEzXLor3jcDdl1lOs39raUpG6j 6d8uVcDypABMuZEUeL3qXRuNSlV3Rg0sK/priYjARYKSMSE0Q93JRUNa1hf63JWdbeA== ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dkim=pass header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=pphv+rFk; dmarc=pass header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; arc=pass header.oldest-pass=0 smtp.remote-ip=127.0.0.1 Received: from localhost.localdomain (localhost [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 184F7606A33C; Thu, 22 Oct 2026 00:00:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=pphv+rFk X-Original-To: linux-sunxi@lists.linux.dev Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=209.85.161.46 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1792627217; b=CtHh+NySdIEuESJXI4EMC7Pb9eKUSo1J7mjo2EDfQQYu4HAjD4vUwtbYAlziWHEKrJ7d 6Y53gL1BamTGOpQjPBx+voLXCKC6+BEggkvhZo9nP5TypEPQI1pFUJnJ+TOSTwORmWkk7 a8HRM4DXK4HmbJEY8UpxPE3WwMcq+87BZuoJESmp9FETeEBFEED5bAOCdHObBipcPCADP /UvHM5qjThwP4U8fnKS/BugyrXGImZnjC2kbyku8ZrCNd/rPZqlzCuAMeMI2exTfZ100h o4MmVciPfUgXpfHpf2v3g4I39//3otY0zxPgUEKtu2YGzYGHnzSV3saYydQNE9r+5eQ== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1792627217; h=DMARC-Filter:Received:DKIM-Signature:X-Google-DKIM-Signature: X-Forwarded-Encrypted:X-Gm-Message-State:X-Gm-Gg:X-Received:Received: From:Date:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding: Message-Id:X-B4-Tracking:X-Change-ID:To:Cc:X-Mailer; bh=YPuKNwql1HIbNGqoX33bMzSynPjwm2jO6JjQ4c2NRG0=; b=0h0dcJSarJaWqY3IU/Lpd7uBfToIt1qIHba9xPmUlKuGR+64JsuDIhGLvDm+qtdXCLTM 2IGQjubDdprtWqj2kDuqgFch1rza2uYjCfjwuyj5VBc4dUIzDgcBlhJhUdCW7IHUUUG3T AdGHJsfW8lM5AVeCleLmSHIL3DDjjR5JwJFDKb13xJfCwolAPEA7ZkirTIAkdTtmeqB4C /tjp4BT9kcFL6ClsOrTbcEpZwoDf/HYzDR2iCwi8cLvN5XFNOzzo9EK8HMKNxqcSLN3+t D6IDI0jTwDoxEKhfyiOAav0YLMIEEhnsOQa7OX1Mn4VOmw1EL6LB9sc1R8sC80qrrPg== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=pphv+rFk; arc=none smtp.remote-ip=209.85.161.46 Received: from mail-oo1-f46.google.com (mail-oo1-f46.google.com [209.85.161.46]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2F74E606A330 for ; Thu, 22 Oct 2026 00:00:16 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 2F74E606A330 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-oo1-f46.google.com with SMTP id 006d021491bc7-6dde81a6820so910661eaf.2 for ; Wed, 21 Oct 2026 17:00:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792627215; x=1793232015; darn=lists.linux.dev; h=cc:to:message-id:content-transfer-encoding:content-type :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to:content-type; bh=YPuKNwql1HIbNGqoX33bMzSynPjwm2jO6JjQ4c2NRG0=; b=pphv+rFkR8GQjyrXLPWuQWeH+6YhnYON1wippkzKwfUfKk7cbSPN2UeNOBoTITmeV/ 5EEj7LaLe/xLqwAPwvDNvCcxKuAmE0bTewnxe90xPkSLDd8RK2kqX8GZcCRbgLr7/Fdh bM7Dkvko0mAQN30kc6ynUbBsTFw79MKaBEvFnLTmiVfqjMFZ4XRovgooiDGIMejCL9Jm uZHQKBA1j0seXlIQkYG2JIcDsxZLaTHlZst5ySh5CrcVke2CgDohyupREiN7oil3CoT5 I5QhOLwb0HrFM81XGmFn3YK/HFjHHWgiceCwu1HWX25v/fYTlREUVQh9Q0pjCwqVYX91 3Eqw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792627215; x=1793232015; h=cc:to:message-id:content-transfer-encoding:content-type :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to:content-type; bh=YPuKNwql1HIbNGqoX33bMzSynPjwm2jO6JjQ4c2NRG0=; b=DuDqh28OIQNXODwJECQH23dDB8/5V8OmIu9izisdqDQgMia1MElaqqlDGGV/hweV9p nfX4dWeDoxnguwdYw4oqyE8i0MCa06b4zmqC24U0/2ets8FMGAq1+1tc68x4L2qdIfTC AdWB/D1KlDI1YAWKhwMjAkbj2TwFVrEd9Di/W0FY5Ps6J9NiOR8OaAYMNye6JHm+mWfI Dbqj8zxki8sBe1jOJ39sWJiX0c2YyZf364JZKsY19D7mnlNxNhgC6lBG/YfueLFnvXBc cyN6d4oglZ3Rquk14CpP3TbBi2HBHuFRP9EBm5B2+uRVPHncXnCo7dX4DXZdqu1iPWUd 3KmA== X-Forwarded-Encrypted: i=1; AKwUvBztUz41t9+yVxZ70cAt0I2rSWxba7poDrY479dkni1pduAQUw/DYSs/ydUaGUvcXWUpr/WZtOo+oXxLJQ==@lists.linux.dev X-Gm-Message-State: AFq9FYL4vEExrH1yeuetLNfj5iVYwFiJVPMytVi/MoBW2IPX4gOQJKyF QGfKh3gavVF01ZpM8Orc64P5h1WTvX1zjDKRUhhCnObSFRxdy7usngvJ X-Gm-Gg: AYBFou3F5O/LK4LMez/8B6iluzCzi3AGwwiGPJIhhQT7m0Zem9a/Lw3KE06tpZMZFjj 2fqofxy9ZGMOuQiOQNc7vTxjRHyxuJXyGFBSAdD3LgPuTwtN71J3BQMZXzQM6ZUvkEWAjr6BkGu 7EpFoEKrFvanymy3eCT5BEA6E77+cYmohQR0cTHsqemIZGenfSEBkj14Jmq68F5HOpVSAzotZA4 P703gDdN2/pSSpspGqwlG/HyLD6jVV4N5ZG0caby9hpyh7TLqEynSmrLaiENcPffrzcB4NjviVQ 7+Se0A35oUEvQgqClnxlfoFJXYAbxii8c6K1k///igvYzHmyCUwZDXboTYP9/XZ+gL+Ay5YkPDm QuCvWCOEeNacGytTpfKlf3nCupZpLw6KuhlBbcAFIaJiKik3n2+8PiUO86RGHMcr3GTU1mzKv2z uijDQvVUS+qh9Kdr3OPV0G9vMtJ5B0TUwFLxMf3dlp7lngJifm0ql6X9/0AMshKwrgqnJi9vfjq qTV6p77Opzgr4uBTHf59evwrlcKEfYy2fhdkNxoiaEademFny4mWEEpndO9U3a4DuH+ybWp9Q/+ nn9SkHASoSGPD95NJWHfERCwtjLedQRKp55E/gTVTM8dzhVUsHao4L55glRWMylOiZg8c4YMrd5 kU3mcxRRoiLNI/oX4HsorjN0/9avaNHQPG4QqvxjDPxxAKsEQCjFNO+kLbMM022Ephf5mxQfXrF GxJw7k3RkLbSRQchmSOScfKRcX X-Received: by 2002:a05:6820:c87:b0:6d2:a9c5:ec7a with SMTP id 006d021491bc7-7093fdbccf6mr2829070eaf.34.1792627214681; Wed, 21 Oct 2026 17:00:14 -0700 (PDT) Received: from [127.0.1.1] (174-29-1-49.hlrn.qwest.net. [174.29.1.49]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-84cbddc7100sm3710064a34.24.2026.10.21.17.00.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 21 Oct 2026 17:00:14 -0700 (PDT) From: James Hilliard Date: Wed, 21 Oct 2026 18:00:12 -0600 Subject: [PATCH] i2c: mv64xxx: protect transfers against input clock changes X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Message-Id: <20261021-submit-i2c-mv64xxx-clock-v1-v1-1-959ea2aa3f90@gmail.com> X-B4-Tracking: v=1; b=H4sIAAAAAAAC/22PwW6EMAxEfwXl3KiJISHlV6o9OI4paQtsCYuQV vvvNUuPvVgae+ZpfFeFl8xFddVdLbzlkudJhH2pFA04fbDOSbQCA94asLrc4phXnYH0uPlm33d N3zN96c1qhyaFFiCC9UoI14X7vD/p75dTS/yTaT2QhyNiYR0XnGg4VmJY+Of1P/gfT843Kbme0 DNP8yiVuqphgto7MiFxbXzvwLujUO2Rexc8ROQUQ3qicKVBfusq7qltWpnovKmRjYuI9i0EkCy nNjkSDU5dHo9fnKmpbC8BAAA= X-Change-ID: 20261021-submit-i2c-mv64xxx-clock-v1-5a0d8722b216 To: Gregory CLEMENT , Andi Shyti Cc: linux-i2c@vger.kernel.org, linux-clk@vger.kernel.org, linux-sunxi@lists.linux.dev, linux-kernel@vger.kernel.org, James Hilliard X-Mailer: b4 0.15.2 X-Rspamd-Server: rspamd-worker-8404 X-Spamd-Result: default: False [4.31 / 15.00]; RBL_SENDERSCORE(2.00)[172.234.253.10:from]; SUSPICIOUS_RECIPS(1.50)[]; DMARC_POLICY_SOFTFAIL(1.00)[gmail.com : SPF not aligned (relaxed), No valid DKIM,none]; MAILLIST(-0.18)[generic]; BAD_REP_POLICIES(0.10)[]; MIME_GOOD(-0.10)[text/plain]; HAS_LIST_UNSUB(-0.01)[]; FROM_HAS_DN(0.00)[]; RCVD_COUNT_FIVE(0.00)[6]; DBL_BLOCKED_OPENRESOLVER(0.00)[sea.lore.kernel.org:helo,sea.lore.kernel.org:rdns]; ARC_ALLOW(0.00)[subspace.kernel.org:s=arc20261015:i=2]; FREEMAIL_CC(0.00)[vger.kernel.org,lists.linux.dev,gmail.com]; TAGGED_RCPT(0.00)[]; MID_RHS_MATCH_FROM(0.00)[]; FORGED_SENDER_MAILLIST(0.00)[]; ASN(0.00)[asn:63949, ipnet:172.234.224.0/19, country:SG]; FROM_NEQ_ENVFROM(0.00)[jameshilliard1@gmail.com,linux-sunxi@lists.linux.dev]; TO_DN_SOME(0.00)[]; FREEMAIL_FROM(0.00)[gmail.com]; RECEIVED_SPAMHAUS_BLOCKED_OPENRESOLVER(0.00)[100.77.114.92:received,209.85.161.46:received]; RBL_SPAMHAUS_BLOCKED_OPENRESOLVER(0.00)[172.234.253.10:from]; TAGGED_FROM(0.00)[bounces-26785-noreply=patchwork.local]; R_SPF_ALLOW(0.00)[+ip4:172.234.253.10]; FORGED_RECIPIENTS_MAILLIST(0.00)[]; RCVD_TLS_LAST(0.00)[]; RCPT_COUNT_SEVEN(0.00)[7]; MIME_TRACE(0.00)[0:+]; RECEIVED_SPAMHAUS_PBL(0.00)[174.29.1.49:received]; RCVD_VIA_SMTP_AUTH(0.00)[] X-Rspamd-Queue-Id: 2EC8F1C249C X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= The controller's timing divisor is calculated once at probe. On Allwinner H616 the input is APB2, which also supplies the UARTs. Changing that shared clock leaves I2C using its old divisor and can change SCL during an active transfer. Register an input clock notifier for DT-configured controllers. Calculate the prospective timing without modifying the current divisor, then try to acquire the adapter bus lock. A running transfer vetoes the change; an idle adapter keeps the lock through POST or ABORT so a new transfer cannot start between validation and the actual rate change. Do not wait for the bus lock under the clock framework lock: a transfer may be waiting there in runtime resume. Also reject a change while START or STOP is still pending in an active controller, since software completion can precede the physical STOP. Publish the new packed divisor on POST and install it before the next transfer. Runtime resume reads the same cached word, so a suspended controller needs no register access from the notifier. Preserve the old timing on ABORT. Drain notifications before removing the adapter, whose device storage is cleared by i2c_del_adapter(). Keep platform-data controllers on their supplied fixed divisors because they do not provide the requested bus frequency needed for recalculation. Signed-off-by: James Hilliard --- Based on i2c/i2c-next with the posted clock-abort fix [0]. The notifier holds the adapter lock between PRE_RATE_CHANGE and POST_RATE_CHANGE or ABORT_RATE_CHANGE, so an aborted parent change must deliver its matching ABORT notification. [0] https://lore.kernel.org/r/20261021-submit-clk-parent-abort-v1-v1-1-2936181822fc@gmail.com --- drivers/i2c/busses/i2c-mv64xxx.c | 137 +++++++++++++++++++++++++++++++++------ 1 file changed, 117 insertions(+), 20 deletions(-) --- base-commit: 4ec2365c08de306f52650d8736aef5862baedb8d change-id: 20261021-submit-i2c-mv64xxx-clock-v1-5a0d8722b216 prerequisite-patch-id: efc747efca5603ae05baa198820d8ed7d5caa125 Best regards, -- James Hilliard diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index 1acba628e16c..e34998b74fdd 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -132,8 +133,12 @@ struct mv64xxx_i2c_data { u32 send_stop; u32 block; int rc; - u32 freq_m; - u32 freq_n; + u32 baud_div; + u32 bus_freq; + u32 new_baud_div; + struct notifier_block clk_notifier; + bool clk_change_ready; + bool clk_change_locked; struct clk *clk; struct clk *reg_clk; wait_queue_head_t waitq; @@ -212,7 +217,7 @@ mv64xxx_i2c_hw_init(struct mv64xxx_i2c_data *drv_data) } writel(0, drv_data->reg_base + drv_data->reg_offsets.soft_reset); - writel(MV64XXX_I2C_BAUD_DIV_M(drv_data->freq_m) | MV64XXX_I2C_BAUD_DIV_N(drv_data->freq_n), + writel(READ_ONCE(drv_data->baud_div), drv_data->reg_base + drv_data->reg_offsets.clock); writel(0, drv_data->reg_base + drv_data->reg_offsets.addr); writel(0, drv_data->reg_base + drv_data->reg_offsets.ext_addr); @@ -751,6 +756,10 @@ mv64xxx_i2c_xfer_core(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) if (rc) return rc; + /* The adapter lock excludes rate changes until the transfer finishes. */ + writel(READ_ONCE(drv_data->baud_div), + drv_data->reg_base + drv_data->reg_offsets.clock); + BUG_ON(drv_data->msgs != NULL); drv_data->msgs = msgs; drv_data->num_msgs = num; @@ -813,9 +822,9 @@ static const struct of_device_id mv64xxx_i2c_of_match_table[] = { MODULE_DEVICE_TABLE(of, mv64xxx_i2c_of_match_table); #ifdef CONFIG_OF -static int +static unsigned long mv64xxx_calc_freq(struct mv64xxx_i2c_data *drv_data, - const int tclk, const int n, const int m) + unsigned long tclk, const int n, const int m) { if (drv_data->clk_n_base_0) return tclk / (10 * (m + 1) * (1 << n)); @@ -825,26 +834,96 @@ mv64xxx_calc_freq(struct mv64xxx_i2c_data *drv_data, static bool mv64xxx_find_baud_factors(struct mv64xxx_i2c_data *drv_data, - const u32 req_freq, const u32 tclk) + u32 req_freq, unsigned long tclk, u32 *baud_div) { - int freq, delta, best_delta = INT_MAX; + unsigned long freq, best_freq = 0; int m, n; for (n = 0; n <= 7; n++) for (m = 0; m <= 15; m++) { freq = mv64xxx_calc_freq(drv_data, tclk, n, m); - delta = req_freq - freq; - if (delta >= 0 && delta < best_delta) { - drv_data->freq_m = m; - drv_data->freq_n = n; - best_delta = delta; + if (freq <= req_freq && freq > best_freq) { + *baud_div = MV64XXX_I2C_BAUD_DIV_M(m) | + MV64XXX_I2C_BAUD_DIV_N(n); + best_freq = freq; } - if (best_delta == 0) + if (best_freq && best_freq == req_freq) return true; } - if (best_delta == INT_MAX) - return false; - return true; + return best_freq != 0; +} + +static int mv64xxx_i2c_clk_notifier(struct notifier_block *nb, + unsigned long event, void *arg) +{ + struct mv64xxx_i2c_data *drv_data = + container_of(nb, struct mv64xxx_i2c_data, clk_notifier); + struct clk_notifier_data *ndata = arg; + struct i2c_adapter *adap = &drv_data->adapter; + int active; + u32 control; + + switch (event) { + case PRE_RATE_CHANGE: + /* The adapter's bus lock is not available during registration. */ + if (!smp_load_acquire(&drv_data->clk_change_ready)) + return NOTIFY_BAD; + if (!mv64xxx_find_baud_factors(drv_data, drv_data->bus_freq, + ndata->new_rate, &drv_data->new_baud_div)) + return NOTIFY_BAD; + /* A transfer can be waiting for the clock framework in runtime PM. */ + if (!i2c_trylock_bus(adap, I2C_LOCK_ROOT_ADAPTER)) + return NOTIFY_BAD; + + /* Software completion can precede the STOP reaching the wire. */ + active = pm_runtime_get_if_active(adap->dev.parent); + if (active > 0 || !IS_ENABLED(CONFIG_PM)) { + control = readl(drv_data->reg_base + drv_data->reg_offsets.control); + if (active > 0) + pm_runtime_put(adap->dev.parent); + if (control & (MV64XXX_I2C_REG_CONTROL_START | + MV64XXX_I2C_REG_CONTROL_STOP)) { + i2c_unlock_bus(adap, I2C_LOCK_ROOT_ADAPTER); + return NOTIFY_BAD; + } + } + drv_data->clk_change_locked = true; + return NOTIFY_OK; + case POST_RATE_CHANGE: + /* Runtime resume can read this without holding the adapter lock. */ + if (drv_data->clk_change_locked) + WRITE_ONCE(drv_data->baud_div, drv_data->new_baud_div); + fallthrough; + case ABORT_RATE_CHANGE: + if (drv_data->clk_change_locked) { + drv_data->clk_change_locked = false; + i2c_unlock_bus(adap, I2C_LOCK_ROOT_ADAPTER); + } + return NOTIFY_OK; + default: + return NOTIFY_DONE; + } +} + +static int mv64xxx_i2c_init_clk_notifier(struct mv64xxx_i2c_data *drv_data, + struct device *dev) +{ + int ret; + + if (!IS_ENABLED(CONFIG_COMMON_CLK) || !drv_data->bus_freq) + return 0; + + drv_data->clk_notifier.notifier_call = mv64xxx_i2c_clk_notifier; + ret = devm_clk_notifier_register(dev, drv_data->clk, &drv_data->clk_notifier); + if (ret) + return ret; + + /* A rate change is now vetoed until the adapter is registered. */ + if (!mv64xxx_find_baud_factors(drv_data, drv_data->bus_freq, + clk_get_rate(drv_data->clk), &drv_data->baud_div)) + return -EINVAL; + + return 0; } static int @@ -853,7 +932,8 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data, { const struct mv64xxx_i2c_regs *data; struct device_node *np = dev->of_node; - u32 bus_freq, tclk; + unsigned long tclk; + u32 bus_freq; int rc = 0; /* CLK is mandatory when using DT to describe the i2c bus. We @@ -873,7 +953,8 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data, of_device_is_compatible(np, "allwinner,sun6i-a31-i2c")) drv_data->clk_n_base_0 = true; - if (!mv64xxx_find_baud_factors(drv_data, bus_freq, tclk)) { + drv_data->bus_freq = bus_freq; + if (!mv64xxx_find_baud_factors(drv_data, bus_freq, tclk, &drv_data->baud_div)) { rc = -EINVAL; goto out; } @@ -920,6 +1001,12 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data, return rc; } #else /* CONFIG_OF */ +static int mv64xxx_i2c_init_clk_notifier(struct mv64xxx_i2c_data *drv_data, + struct device *dev) +{ + return 0; +} + static int mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data, struct device *dev) @@ -1018,8 +1105,8 @@ mv64xxx_i2c_probe(struct platform_device *pd) return drv_data->irq; if (pdata) { - drv_data->freq_m = pdata->freq_m; - drv_data->freq_n = pdata->freq_n; + drv_data->baud_div = MV64XXX_I2C_BAUD_DIV_M(pdata->freq_m) | + MV64XXX_I2C_BAUD_DIV_N(pdata->freq_n); drv_data->adapter.timeout = msecs_to_jiffies(pdata->timeout); drv_data->offload_enabled = false; memcpy(&drv_data->reg_offsets, &mv64xxx_i2c_regs_mv64xxx, sizeof(drv_data->reg_offsets)); @@ -1042,6 +1129,10 @@ mv64xxx_i2c_probe(struct platform_device *pd) platform_set_drvdata(pd, drv_data); i2c_set_adapdata(&drv_data->adapter, drv_data); + rc = mv64xxx_i2c_init_clk_notifier(drv_data, &pd->dev); + if (rc) + return dev_err_probe(&pd->dev, rc, "Cannot monitor input clock changes\n"); + pm_runtime_set_autosuspend_delay(&pd->dev, MSEC_PER_SEC); pm_runtime_use_autosuspend(&pd->dev); pm_runtime_enable(&pd->dev); @@ -1064,6 +1155,8 @@ mv64xxx_i2c_probe(struct platform_device *pd) goto exit_free_irq; } + /* Publish the initialized adapter, including its bus lock, to PRE. */ + smp_store_release(&drv_data->clk_change_ready, true); return 0; exit_free_irq: @@ -1081,11 +1174,15 @@ mv64xxx_i2c_remove(struct platform_device *pd) { struct mv64xxx_i2c_data *drv_data = platform_get_drvdata(pd); + /* Drain notifications before i2c_del_adapter() clears adap->dev. */ + clk_rate_exclusive_get(drv_data->clk); + WRITE_ONCE(drv_data->clk_change_ready, false); i2c_del_adapter(&drv_data->adapter); free_irq(drv_data->irq, drv_data); pm_runtime_disable(&pd->dev); if (!pm_runtime_status_suspended(&pd->dev)) mv64xxx_i2c_runtime_suspend(&pd->dev); + clk_rate_exclusive_put(drv_data->clk); } static const struct dev_pm_ops mv64xxx_i2c_pm_ops = {