mirror of
https://github.com/torvalds/linux.git
synced 2026-10-23 15:32:04 +02:00
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:
commit
72732cbee6
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user