Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

i2c: imx-lpi2c: Add runtime PM support for IRQ and clock management on i.MX8QXP/8QM

On i.MX8QXP/8QM SoCs, both the lvds/mipi and lvds/mipi-lpi2c power domains
must enter low-power mode during runtime suspend to achieve deep power
savings.

LPI2C resides in the lvds-lpi2c/mipi-lpi2c power domain, while its IRQ is
routed through an irqsteer located in the lvds/mipi power domain. The LPI2C
clock source comes from an LPCG within the lvds-lpi2c domain.

For example, the hierarchy for lvds0 and lvds0-lpi2c0 domains is:
┌───────────────────────┐
│ pm-domain : lvds0 │
│ │
│ ┌──────────────┐ │
│ │ irqsteer │ │
│ └───────▲──────┘ │
│ │irq │
│ │ │
└────────────┼──────────┘
┌────────────┼──────────┐
│ ┌───┼───┐ │
│ │lpi2c0 │ │
│ └───┬───┘clk │
│ ┌────────┼───────┐ │
│ │ LPCG │ │
│ └────────────────┘ │
│pm-domain:lvds0-lpi2c0 │
└───────────────────────┘

To allow these domains to power down in system runtime suspend:

- All irqsteer clients must release IRQs.
- All LPCG clients must disable and unprepare clocks.

Thus, LPI2C must:

- Free its IRQ during runtime suspend and re-request it on resume.
- Disable and unprepare all clocks during runtime suspend and prepare
and rne ble them on resume.

This enables the lvds/mipi domains to enter deep low-power mode,
significantly reducing power consumption compared to active mode.

Signed-off-by: Carlos Song <carlos.song@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
Link: https://lore.kernel.org/r/20251125084718.2156168-1-carlos.song@nxp.com

authored by

Carlos Song and committed by
Andi Shyti
949f647e 7021f6c0

+71 -13
+71 -13
drivers/i2c/busses/i2c-imx-lpi2c.c
··· 131 131 #define CHUNK_DATA 256 132 132 133 133 #define I2C_PM_TIMEOUT 10 /* ms */ 134 + #define I2C_PM_LONG_TIMEOUT_MS 1000 /* Avoid dead lock caused by big clock prepare lock */ 134 135 #define I2C_DMA_THRESHOLD 8 /* bytes */ 135 136 136 137 enum lpi2c_imx_mode { ··· 147 146 TWO_PIN_OO, 148 147 TWO_PIN_PP, 149 148 FOUR_PIN_PP, 149 + }; 150 + 151 + struct imx_lpi2c_hwdata { 152 + bool need_request_free_irq; /* Needed by irqsteer */ 153 + bool need_prepare_unprepare_clk; /* Needed by LPCG */ 150 154 }; 151 155 152 156 struct lpi2c_imx_dma { ··· 192 186 bool can_use_dma; 193 187 struct lpi2c_imx_dma *dma; 194 188 struct i2c_client *target; 189 + int irq; 190 + const struct imx_lpi2c_hwdata *hwdata; 191 + }; 192 + 193 + static const struct imx_lpi2c_hwdata imx7ulp_lpi2c_hwdata = { 194 + }; 195 + 196 + static const struct imx_lpi2c_hwdata imx8qxp_lpi2c_hwdata = { 197 + .need_request_free_irq = true, 198 + .need_prepare_unprepare_clk = true, 199 + }; 200 + 201 + static const struct imx_lpi2c_hwdata imx8qm_lpi2c_hwdata = { 202 + .need_request_free_irq = true, 203 + .need_prepare_unprepare_clk = true, 195 204 }; 196 205 197 206 #define lpi2c_imx_read_msr_poll_timeout(atomic, val, cond) \ ··· 1384 1363 }; 1385 1364 1386 1365 static const struct of_device_id lpi2c_imx_of_match[] = { 1387 - { .compatible = "fsl,imx7ulp-lpi2c" }, 1366 + { .compatible = "fsl,imx7ulp-lpi2c", .data = &imx7ulp_lpi2c_hwdata,}, 1367 + { .compatible = "fsl,imx8qxp-lpi2c", .data = &imx8qxp_lpi2c_hwdata,}, 1368 + { .compatible = "fsl,imx8qm-lpi2c", .data = &imx8qm_lpi2c_hwdata,}, 1388 1369 { } 1389 1370 }; 1390 1371 MODULE_DEVICE_TABLE(of, lpi2c_imx_of_match); ··· 1397 1374 struct resource *res; 1398 1375 dma_addr_t phy_addr; 1399 1376 unsigned int temp; 1400 - int irq, ret; 1377 + int ret; 1401 1378 1402 1379 lpi2c_imx = devm_kzalloc(&pdev->dev, sizeof(*lpi2c_imx), GFP_KERNEL); 1403 1380 if (!lpi2c_imx) 1404 1381 return -ENOMEM; 1405 1382 1383 + lpi2c_imx->hwdata = of_device_get_match_data(&pdev->dev); 1384 + if (!lpi2c_imx->hwdata) 1385 + return -ENODEV; 1386 + 1406 1387 lpi2c_imx->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 1407 1388 if (IS_ERR(lpi2c_imx->base)) 1408 1389 return PTR_ERR(lpi2c_imx->base); 1409 1390 1410 - irq = platform_get_irq(pdev, 0); 1411 - if (irq < 0) 1412 - return irq; 1391 + lpi2c_imx->irq = platform_get_irq(pdev, 0); 1392 + if (lpi2c_imx->irq < 0) 1393 + return lpi2c_imx->irq; 1413 1394 1414 1395 lpi2c_imx->adapter.owner = THIS_MODULE; 1415 1396 lpi2c_imx->adapter.algo = &lpi2c_imx_algo; ··· 1433 1406 if (ret) 1434 1407 lpi2c_imx->bitrate = I2C_MAX_STANDARD_MODE_FREQ; 1435 1408 1436 - ret = devm_request_irq(&pdev->dev, irq, lpi2c_imx_isr, IRQF_NO_SUSPEND, 1409 + ret = devm_request_irq(&pdev->dev, lpi2c_imx->irq, lpi2c_imx_isr, IRQF_NO_SUSPEND, 1437 1410 pdev->name, lpi2c_imx); 1438 1411 if (ret) 1439 - return dev_err_probe(&pdev->dev, ret, "can't claim irq %d\n", irq); 1412 + return dev_err_probe(&pdev->dev, ret, "can't claim irq %d\n", lpi2c_imx->irq); 1440 1413 1441 1414 i2c_set_adapdata(&lpi2c_imx->adapter, lpi2c_imx); 1442 1415 platform_set_drvdata(pdev, lpi2c_imx); ··· 1459 1432 return dev_err_probe(&pdev->dev, -EINVAL, 1460 1433 "can't get I2C peripheral clock rate\n"); 1461 1434 1462 - pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_TIMEOUT); 1435 + if (lpi2c_imx->hwdata->need_prepare_unprepare_clk) 1436 + pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_LONG_TIMEOUT_MS); 1437 + else 1438 + pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_TIMEOUT); 1439 + 1463 1440 pm_runtime_use_autosuspend(&pdev->dev); 1464 1441 pm_runtime_get_noresume(&pdev->dev); 1465 1442 pm_runtime_set_active(&pdev->dev); ··· 1518 1487 static int __maybe_unused lpi2c_runtime_suspend(struct device *dev) 1519 1488 { 1520 1489 struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev); 1490 + bool need_prepare_unprepare_clk = lpi2c_imx->hwdata->need_prepare_unprepare_clk; 1491 + bool need_request_free_irq = lpi2c_imx->hwdata->need_request_free_irq; 1521 1492 1522 - clk_bulk_disable(lpi2c_imx->num_clks, lpi2c_imx->clks); 1493 + if (need_request_free_irq) 1494 + devm_free_irq(dev, lpi2c_imx->irq, lpi2c_imx); 1495 + 1496 + if (need_prepare_unprepare_clk) 1497 + clk_bulk_disable_unprepare(lpi2c_imx->num_clks, lpi2c_imx->clks); 1498 + else 1499 + clk_bulk_disable(lpi2c_imx->num_clks, lpi2c_imx->clks); 1523 1500 pinctrl_pm_select_sleep_state(dev); 1524 1501 1525 1502 return 0; ··· 1536 1497 static int __maybe_unused lpi2c_runtime_resume(struct device *dev) 1537 1498 { 1538 1499 struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev); 1500 + bool need_prepare_unprepare_clk = lpi2c_imx->hwdata->need_prepare_unprepare_clk; 1501 + bool need_request_free_irq = lpi2c_imx->hwdata->need_request_free_irq; 1539 1502 int ret; 1540 1503 1541 1504 pinctrl_pm_select_default_state(dev); 1542 - ret = clk_bulk_enable(lpi2c_imx->num_clks, lpi2c_imx->clks); 1543 - if (ret) { 1544 - dev_err(dev, "failed to enable I2C clock, ret=%d\n", ret); 1545 - return ret; 1505 + if (need_prepare_unprepare_clk) { 1506 + ret = clk_bulk_prepare_enable(lpi2c_imx->num_clks, lpi2c_imx->clks); 1507 + if (ret) { 1508 + dev_err(dev, "failed to enable I2C clock, ret=%d\n", ret); 1509 + return ret; 1510 + } 1511 + } else { 1512 + ret = clk_bulk_enable(lpi2c_imx->num_clks, lpi2c_imx->clks); 1513 + if (ret) { 1514 + dev_err(dev, "failed to enable clock %d\n", ret); 1515 + return ret; 1516 + } 1517 + } 1518 + 1519 + if (need_request_free_irq) { 1520 + ret = devm_request_irq(dev, lpi2c_imx->irq, lpi2c_imx_isr, IRQF_NO_SUSPEND, 1521 + dev_name(dev), lpi2c_imx); 1522 + if (ret) { 1523 + dev_err(dev, "can't claim irq %d\n", lpi2c_imx->irq); 1524 + return ret; 1525 + } 1546 1526 } 1547 1527 1548 1528 return 0;