i2c-fixes for v7.3-rc8

Three independent clock management fixes, plus one for a simple
 reset mask blunder in the qcom-cci interrupt handler.
 
 - qcom-cci: prevent lost reset requests during interrupt handling
 
 - qcom-geni: preserve the selected source clock
 
 - viai2c-wmt: fix clock reference leak
 
 - xiic: fix clock disable warnings during removal
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQScDfrjQa34uOld1VLaeAVmJtMtbgUCatTkqQAKCRDaeAVmJtMt
 bmntAQCohnAvbkYQh6VfaPl5TzQPytHWTMg3AiqyLeDWs7MuAAD/RQS6vFQMyzvj
 kD1fK/ocHIs/D59T21JiOzhhiNSSNwo=
 =SdHD
 -----END PGP SIGNATURE-----

Merge tag 'i2c-fixes-7.3-rc8' of git://git.kernel.org/pub/scm/linux/kernel/git/andi.shyti/linux

Pull i2c fixes from Andi Shyti:
 "Three independent clock management fixes, plus one for a simple
  reset mask blunder in the qcom-cci interrupt handler"

* tag 'i2c-fixes-7.3-rc8' of git://git.kernel.org/pub/scm/linux/kernel/git/andi.shyti/linux:
  i2c: xiic: restore non-managed runtime PM to fix clk WARN flood
  i2c: qcom-cci: Fix HALT_ACK reset request being dropped
  i2c: qcom-geni: Restore the source-clock selection policy
  i2c: viai2c-wmt: Fix clock leak in wmt_i2c_probe()
This commit is contained in:
Linus Torvalds 2026-10-18 08:16:56 -10:00
commit 72732cbee6
4 changed files with 63 additions and 29 deletions

View File

@ -164,12 +164,12 @@ static irqreturn_t cci_isr(int irq, void *dev)
}
if (unlikely(val & CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK)) {
reset = CCI_RESET_CMD_M0_MASK;
reset |= CCI_RESET_CMD_M0_MASK;
ret = IRQ_HANDLED;
}
if (unlikely(val & CCI_IRQ_STATUS_0_I2C_M1_Q0Q1_HALT_ACK)) {
reset = CCI_RESET_CMD_M1_MASK;
reset |= CCI_RESET_CMD_M1_MASK;
ret = IRQ_HANDLED;
}

View File

@ -201,28 +201,31 @@ static const struct geni_i2c_clk_fld geni_i2c_clk_map_32mhz[] = {
static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c)
{
const struct geni_i2c_clk_fld *itr;
unsigned long res_freq;
unsigned long req_freq, res_freq;
int ret;
/*
* Frequency counter tables are calibrated for a specific source
* clock frequency and are not valid for any multiple of it
* (e.g. 64 MHz, 128 MHz).
* Use exact=true and verify res_freq matches req_freq literally
* to reject harmonics: a 64 MHz clock that divides evenly to
* 32 MHz would pass exact matching but produce double the intended
* I2C frequency with these counter values.
* Use 32 MHz only if already selected, otherwise use 19.2 MHz.
* Preferring 32 MHz merely because DFS supports it breaks I2C
* transfers on X1E80100 CRD.
*/
if (!geni_se_clk_freq_match(&gi2c->se, GENI_SE_CLK_32MHZ,
&gi2c->clk_idx, &res_freq, true) &&
res_freq == GENI_SE_CLK_32MHZ) {
if (clk_get_rate(gi2c->se.clk) == GENI_SE_CLK_32MHZ) {
req_freq = GENI_SE_CLK_32MHZ;
itr = geni_i2c_clk_map_32mhz;
} else if (!geni_se_clk_freq_match(&gi2c->se, GENI_SE_CLK_19P2MHZ,
&gi2c->clk_idx, &res_freq, true) &&
res_freq == GENI_SE_CLK_19P2MHZ) {
itr = geni_i2c_clk_map_19p2mhz;
} else {
dev_err(gi2c->se.dev,
"Unsupported SE source clock: must be exactly 32 MHz or 19.2 MHz\n");
req_freq = GENI_SE_CLK_19P2MHZ;
itr = geni_i2c_clk_map_19p2mhz;
}
ret = geni_se_clk_freq_match(&gi2c->se, req_freq, &gi2c->clk_idx,
&res_freq, true);
if (ret)
return dev_err_probe(gi2c->se.dev, ret,
"Failed to find SE source clock %lu Hz\n",
req_freq);
if (res_freq != req_freq) {
dev_err(gi2c->se.dev, "Unsupported SE source clock %lu Hz\n",
res_freq);
return -EINVAL;
}

View File

@ -116,7 +116,7 @@ static int wmt_i2c_probe(struct platform_device *pdev)
if (err)
return err;
i2c->clk = of_clk_get(np, 0);
i2c->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(i2c->clk))
return dev_err_probe(&pdev->dev, PTR_ERR(i2c->clk),
"unable to request clock\n");

View File

@ -67,6 +67,7 @@ enum i2c_scl_freq {
* @rx_pos: Position within current RX message
* @endianness: big/little-endian byte order
* @clk: Pointer to AXI4-lite input clock
* @irq: Interrupt number
* @state: See STATE_
* @singlemaster: Indicates bus is single master
* @dynamic: Mode of controller
@ -95,6 +96,7 @@ struct xiic_i2c {
int rx_pos;
enum xiic_endian endianness;
struct clk *clk;
int irq;
enum xilinx_i2c_state state;
bool singlemaster;
bool dynamic;
@ -1506,9 +1508,17 @@ static int xiic_i2c_probe(struct platform_device *pdev)
pm_runtime_set_autosuspend_delay(dev, XIIC_PM_TIMEOUT);
pm_runtime_use_autosuspend(dev);
ret = devm_pm_runtime_set_active_enabled(dev);
if (ret)
/*
* Enable runtime PM by hand: devm_pm_runtime_set_active_enabled()
* tears down in an order that races the devm-enabled clock release and
* makes clk_core_disable() WARN (see xiic_i2c_remove()).
*/
ret = pm_runtime_set_active(dev);
if (ret) {
pm_runtime_dont_use_autosuspend(dev);
return ret;
}
pm_runtime_enable(dev);
/* SCL frequency configuration */
i2c->input_clk = clk_get_rate(i2c->clk);
@ -1517,10 +1527,15 @@ static int xiic_i2c_probe(struct platform_device *pdev)
if (ret || i2c->i2c_clk > I2C_MAX_FAST_MODE_PLUS_FREQ)
i2c->i2c_clk = 0;
ret = devm_request_threaded_irq(dev, irq, NULL, xiic_process,
IRQF_ONESHOT, pdev->name, i2c);
/*
* Request the IRQ non-managed: later probe steps unwind manually via
* goto, so a devm handler could still be live after that teardown runs.
*/
i2c->irq = irq;
ret = request_threaded_irq(irq, NULL, xiic_process, IRQF_ONESHOT,
pdev->name, i2c);
if (ret)
return ret;
goto err_pm_disable;
i2c->singlemaster = device_property_read_bool(dev, "single-master");
@ -1537,14 +1552,16 @@ static int xiic_i2c_probe(struct platform_device *pdev)
i2c->endianness = BIG;
ret = xiic_reinit(i2c);
if (ret)
return dev_err_probe(dev, ret, "Cannot xiic_reinit\n");
if (ret) {
dev_err_probe(dev, ret, "Cannot xiic_reinit\n");
goto err_free_irq;
}
/* add i2c adapter to i2c tree */
ret = i2c_add_numbered_adapter(&i2c->adap);
if (ret) {
xiic_deinit(i2c);
return ret;
goto err_free_irq;
}
if (pdata) {
@ -1557,6 +1574,15 @@ static int xiic_i2c_probe(struct platform_device *pdev)
res, irq, i2c->i2c_clk);
return 0;
err_free_irq:
free_irq(irq, i2c);
err_pm_disable:
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
pm_runtime_dont_use_autosuspend(dev);
return ret;
}
static void xiic_i2c_remove(struct platform_device *pdev)
@ -1568,6 +1594,8 @@ static void xiic_i2c_remove(struct platform_device *pdev)
/* remove adapter & data */
i2c_del_adapter(&i2c->adap);
free_irq(i2c->irq, i2c);
ret = pm_runtime_get_sync(dev);
if (ret < 0)
dev_warn(dev, "Failed to activate device for removal (%pe)\n",
@ -1575,7 +1603,10 @@ static void xiic_i2c_remove(struct platform_device *pdev)
else
xiic_deinit(i2c);
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
pm_runtime_put_noidle(dev);
pm_runtime_set_suspended(dev);
pm_runtime_dont_use_autosuspend(dev);
}
static const struct dev_pm_ops xiic_dev_pm_ops = {