Skip to content

Commit 949f647

Browse files
NXP-CarlosSongAndi Shyti
authored andcommitted
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
1 parent 7021f6c commit 949f647

1 file changed

Lines changed: 71 additions & 13 deletions

File tree

drivers/i2c/busses/i2c-imx-lpi2c.c

Lines changed: 71 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@
131131
#define CHUNK_DATA 256
132132

133133
#define I2C_PM_TIMEOUT 10 /* ms */
134+
#define I2C_PM_LONG_TIMEOUT_MS 1000 /* Avoid dead lock caused by big clock prepare lock */
134135
#define I2C_DMA_THRESHOLD 8 /* bytes */
135136

136137
enum lpi2c_imx_mode {
@@ -148,6 +149,11 @@ enum lpi2c_imx_pincfg {
148149
FOUR_PIN_PP,
149150
};
150151

152+
struct imx_lpi2c_hwdata {
153+
bool need_request_free_irq; /* Needed by irqsteer */
154+
bool need_prepare_unprepare_clk; /* Needed by LPCG */
155+
};
156+
151157
struct lpi2c_imx_dma {
152158
bool using_pio_mode;
153159
u8 rx_cmd_buf_len;
@@ -186,6 +192,21 @@ struct lpi2c_imx_struct {
186192
bool can_use_dma;
187193
struct lpi2c_imx_dma *dma;
188194
struct i2c_client *target;
195+
int irq;
196+
const struct imx_lpi2c_hwdata *hwdata;
197+
};
198+
199+
static const struct imx_lpi2c_hwdata imx7ulp_lpi2c_hwdata = {
200+
};
201+
202+
static const struct imx_lpi2c_hwdata imx8qxp_lpi2c_hwdata = {
203+
.need_request_free_irq = true,
204+
.need_prepare_unprepare_clk = true,
205+
};
206+
207+
static const struct imx_lpi2c_hwdata imx8qm_lpi2c_hwdata = {
208+
.need_request_free_irq = true,
209+
.need_prepare_unprepare_clk = true,
189210
};
190211

191212
#define lpi2c_imx_read_msr_poll_timeout(atomic, val, cond) \
@@ -1363,7 +1384,9 @@ static const struct i2c_algorithm lpi2c_imx_algo = {
13631384
};
13641385

13651386
static const struct of_device_id lpi2c_imx_of_match[] = {
1366-
{ .compatible = "fsl,imx7ulp-lpi2c" },
1387+
{ .compatible = "fsl,imx7ulp-lpi2c", .data = &imx7ulp_lpi2c_hwdata,},
1388+
{ .compatible = "fsl,imx8qxp-lpi2c", .data = &imx8qxp_lpi2c_hwdata,},
1389+
{ .compatible = "fsl,imx8qm-lpi2c", .data = &imx8qm_lpi2c_hwdata,},
13671390
{ }
13681391
};
13691392
MODULE_DEVICE_TABLE(of, lpi2c_imx_of_match);
@@ -1374,19 +1397,23 @@ static int lpi2c_imx_probe(struct platform_device *pdev)
13741397
struct resource *res;
13751398
dma_addr_t phy_addr;
13761399
unsigned int temp;
1377-
int irq, ret;
1400+
int ret;
13781401

13791402
lpi2c_imx = devm_kzalloc(&pdev->dev, sizeof(*lpi2c_imx), GFP_KERNEL);
13801403
if (!lpi2c_imx)
13811404
return -ENOMEM;
13821405

1406+
lpi2c_imx->hwdata = of_device_get_match_data(&pdev->dev);
1407+
if (!lpi2c_imx->hwdata)
1408+
return -ENODEV;
1409+
13831410
lpi2c_imx->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
13841411
if (IS_ERR(lpi2c_imx->base))
13851412
return PTR_ERR(lpi2c_imx->base);
13861413

1387-
irq = platform_get_irq(pdev, 0);
1388-
if (irq < 0)
1389-
return irq;
1414+
lpi2c_imx->irq = platform_get_irq(pdev, 0);
1415+
if (lpi2c_imx->irq < 0)
1416+
return lpi2c_imx->irq;
13901417

13911418
lpi2c_imx->adapter.owner = THIS_MODULE;
13921419
lpi2c_imx->adapter.algo = &lpi2c_imx_algo;
@@ -1406,10 +1433,10 @@ static int lpi2c_imx_probe(struct platform_device *pdev)
14061433
if (ret)
14071434
lpi2c_imx->bitrate = I2C_MAX_STANDARD_MODE_FREQ;
14081435

1409-
ret = devm_request_irq(&pdev->dev, irq, lpi2c_imx_isr, IRQF_NO_SUSPEND,
1436+
ret = devm_request_irq(&pdev->dev, lpi2c_imx->irq, lpi2c_imx_isr, IRQF_NO_SUSPEND,
14101437
pdev->name, lpi2c_imx);
14111438
if (ret)
1412-
return dev_err_probe(&pdev->dev, ret, "can't claim irq %d\n", irq);
1439+
return dev_err_probe(&pdev->dev, ret, "can't claim irq %d\n", lpi2c_imx->irq);
14131440

14141441
i2c_set_adapdata(&lpi2c_imx->adapter, lpi2c_imx);
14151442
platform_set_drvdata(pdev, lpi2c_imx);
@@ -1432,7 +1459,11 @@ static int lpi2c_imx_probe(struct platform_device *pdev)
14321459
return dev_err_probe(&pdev->dev, -EINVAL,
14331460
"can't get I2C peripheral clock rate\n");
14341461

1435-
pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_TIMEOUT);
1462+
if (lpi2c_imx->hwdata->need_prepare_unprepare_clk)
1463+
pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_LONG_TIMEOUT_MS);
1464+
else
1465+
pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_TIMEOUT);
1466+
14361467
pm_runtime_use_autosuspend(&pdev->dev);
14371468
pm_runtime_get_noresume(&pdev->dev);
14381469
pm_runtime_set_active(&pdev->dev);
@@ -1487,8 +1518,16 @@ static void lpi2c_imx_remove(struct platform_device *pdev)
14871518
static int __maybe_unused lpi2c_runtime_suspend(struct device *dev)
14881519
{
14891520
struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev);
1521+
bool need_prepare_unprepare_clk = lpi2c_imx->hwdata->need_prepare_unprepare_clk;
1522+
bool need_request_free_irq = lpi2c_imx->hwdata->need_request_free_irq;
1523+
1524+
if (need_request_free_irq)
1525+
devm_free_irq(dev, lpi2c_imx->irq, lpi2c_imx);
14901526

1491-
clk_bulk_disable(lpi2c_imx->num_clks, lpi2c_imx->clks);
1527+
if (need_prepare_unprepare_clk)
1528+
clk_bulk_disable_unprepare(lpi2c_imx->num_clks, lpi2c_imx->clks);
1529+
else
1530+
clk_bulk_disable(lpi2c_imx->num_clks, lpi2c_imx->clks);
14921531
pinctrl_pm_select_sleep_state(dev);
14931532

14941533
return 0;
@@ -1497,13 +1536,32 @@ static int __maybe_unused lpi2c_runtime_suspend(struct device *dev)
14971536
static int __maybe_unused lpi2c_runtime_resume(struct device *dev)
14981537
{
14991538
struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev);
1539+
bool need_prepare_unprepare_clk = lpi2c_imx->hwdata->need_prepare_unprepare_clk;
1540+
bool need_request_free_irq = lpi2c_imx->hwdata->need_request_free_irq;
15001541
int ret;
15011542

15021543
pinctrl_pm_select_default_state(dev);
1503-
ret = clk_bulk_enable(lpi2c_imx->num_clks, lpi2c_imx->clks);
1504-
if (ret) {
1505-
dev_err(dev, "failed to enable I2C clock, ret=%d\n", ret);
1506-
return ret;
1544+
if (need_prepare_unprepare_clk) {
1545+
ret = clk_bulk_prepare_enable(lpi2c_imx->num_clks, lpi2c_imx->clks);
1546+
if (ret) {
1547+
dev_err(dev, "failed to enable I2C clock, ret=%d\n", ret);
1548+
return ret;
1549+
}
1550+
} else {
1551+
ret = clk_bulk_enable(lpi2c_imx->num_clks, lpi2c_imx->clks);
1552+
if (ret) {
1553+
dev_err(dev, "failed to enable clock %d\n", ret);
1554+
return ret;
1555+
}
1556+
}
1557+
1558+
if (need_request_free_irq) {
1559+
ret = devm_request_irq(dev, lpi2c_imx->irq, lpi2c_imx_isr, IRQF_NO_SUSPEND,
1560+
dev_name(dev), lpi2c_imx);
1561+
if (ret) {
1562+
dev_err(dev, "can't claim irq %d\n", lpi2c_imx->irq);
1563+
return ret;
1564+
}
15071565
}
15081566

15091567
return 0;

0 commit comments

Comments
 (0)