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 <linux/io.h>
 #include <linux/of.h>
 #include <linux/clk.h>
+#include <linux/notifier.h>
 #include <linux/err.h>
 #include <linux/delay.h>
 
@@ -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 = {
