Skip to content

Commit 7dc0d13

Browse files
Merge patch series "ufs: host: mediatek: Power Management and stability enhancements"
Peter Wang <peter.wang@mediatek.com> says: These patches collectively enhance the UFS host driver's reliability, power management efficiency, and error recovery mechanisms on MediaTek platforms. They address critical issues and introduce optimizations that improve system stability and performance. Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2 parents 508e754 + 2936049 commit 7dc0d13

4 files changed

Lines changed: 145 additions & 31 deletions

File tree

drivers/ufs/core/ufshcd.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6454,13 +6454,14 @@ void ufshcd_schedule_eh_work(struct ufs_hba *hba)
64546454
}
64556455
}
64566456

6457-
static void ufshcd_force_error_recovery(struct ufs_hba *hba)
6457+
void ufshcd_force_error_recovery(struct ufs_hba *hba)
64586458
{
64596459
spin_lock_irq(hba->host->host_lock);
64606460
hba->force_reset = true;
64616461
ufshcd_schedule_eh_work(hba);
64626462
spin_unlock_irq(hba->host->host_lock);
64636463
}
6464+
EXPORT_SYMBOL_GPL(ufshcd_force_error_recovery);
64646465

64656466
static void ufshcd_clk_scaling_allow(struct ufs_hba *hba, bool allow)
64666467
{

drivers/ufs/host/ufs-mediatek.c

Lines changed: 141 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ static void ufs_mtk_dbg_sel(struct ufs_hba *hba)
416416
}
417417
}
418418

419-
static void ufs_mtk_wait_idle_state(struct ufs_hba *hba,
419+
static int ufs_mtk_wait_idle_state(struct ufs_hba *hba,
420420
unsigned long retry_ms)
421421
{
422422
u64 timeout, time_checked;
@@ -452,8 +452,12 @@ static void ufs_mtk_wait_idle_state(struct ufs_hba *hba,
452452
break;
453453
} while (time_checked < timeout);
454454

455-
if (wait_idle && sm != VS_HCE_BASE)
455+
if (wait_idle && sm != VS_HCE_BASE) {
456456
dev_info(hba->dev, "wait idle tmo: 0x%x\n", val);
457+
return -ETIMEDOUT;
458+
}
459+
460+
return 0;
457461
}
458462

459463
static int ufs_mtk_wait_link_state(struct ufs_hba *hba, u32 state,
@@ -1429,19 +1433,53 @@ static int ufs_mtk_pre_pwr_change(struct ufs_hba *hba,
14291433
return ret;
14301434
}
14311435

1436+
static int ufs_mtk_auto_hibern8_disable(struct ufs_hba *hba)
1437+
{
1438+
int ret;
1439+
1440+
/* disable auto-hibern8 */
1441+
ufshcd_writel(hba, 0, REG_AUTO_HIBERNATE_IDLE_TIMER);
1442+
1443+
/* wait host return to idle state when auto-hibern8 off */
1444+
ret = ufs_mtk_wait_idle_state(hba, 5);
1445+
if (ret)
1446+
goto out;
1447+
1448+
ret = ufs_mtk_wait_link_state(hba, VS_LINK_UP, 100);
1449+
1450+
out:
1451+
if (ret) {
1452+
dev_warn(hba->dev, "exit h8 state fail, ret=%d\n", ret);
1453+
1454+
ufshcd_force_error_recovery(hba);
1455+
1456+
/* trigger error handler and break suspend */
1457+
ret = -EBUSY;
1458+
}
1459+
1460+
return ret;
1461+
}
1462+
14321463
static int ufs_mtk_pwr_change_notify(struct ufs_hba *hba,
14331464
enum ufs_notify_change_status stage,
14341465
const struct ufs_pa_layer_attr *dev_max_params,
14351466
struct ufs_pa_layer_attr *dev_req_params)
14361467
{
14371468
int ret = 0;
1469+
static u32 reg;
14381470

14391471
switch (stage) {
14401472
case PRE_CHANGE:
1473+
if (ufshcd_is_auto_hibern8_supported(hba)) {
1474+
reg = ufshcd_readl(hba, REG_AUTO_HIBERNATE_IDLE_TIMER);
1475+
ufs_mtk_auto_hibern8_disable(hba);
1476+
}
14411477
ret = ufs_mtk_pre_pwr_change(hba, dev_max_params,
14421478
dev_req_params);
14431479
break;
14441480
case POST_CHANGE:
1481+
if (ufshcd_is_auto_hibern8_supported(hba))
1482+
ufshcd_writel(hba, reg, REG_AUTO_HIBERNATE_IDLE_TIMER);
14451483
break;
14461484
default:
14471485
ret = -EINVAL;
@@ -1513,8 +1551,19 @@ static int ufs_mtk_pre_link(struct ufs_hba *hba)
15131551

15141552
return ret;
15151553
}
1554+
15161555
static void ufs_mtk_post_link(struct ufs_hba *hba)
15171556
{
1557+
struct ufs_mtk_host *host = ufshcd_get_variant(hba);
1558+
u32 tmp;
1559+
1560+
/* fix device PA_INIT no adapt */
1561+
if (host->ip_ver >= IP_VER_MT6899) {
1562+
ufshcd_dme_get(hba, UIC_ARG_MIB(VS_DEBUGOMC), &tmp);
1563+
tmp |= 0x100;
1564+
ufshcd_dme_set(hba, UIC_ARG_MIB(VS_DEBUGOMC), tmp);
1565+
}
1566+
15181567
/* enable unipro clock gating feature */
15191568
ufs_mtk_cfg_unipro_cg(hba, true);
15201569
}
@@ -1584,7 +1633,11 @@ static int ufs_mtk_link_set_hpm(struct ufs_hba *hba)
15841633
return err;
15851634

15861635
/* Check link state to make sure exit h8 success */
1587-
ufs_mtk_wait_idle_state(hba, 5);
1636+
err = ufs_mtk_wait_idle_state(hba, 5);
1637+
if (err) {
1638+
dev_warn(hba->dev, "wait idle fail, err=%d\n", err);
1639+
return err;
1640+
}
15881641
err = ufs_mtk_wait_link_state(hba, VS_LINK_UP, 100);
15891642
if (err) {
15901643
dev_warn(hba->dev, "exit h8 state fail, err=%d\n", err);
@@ -1686,21 +1739,6 @@ static void ufs_mtk_dev_vreg_set_lpm(struct ufs_hba *hba, bool lpm)
16861739
}
16871740
}
16881741

1689-
static void ufs_mtk_auto_hibern8_disable(struct ufs_hba *hba)
1690-
{
1691-
int ret;
1692-
1693-
/* disable auto-hibern8 */
1694-
ufshcd_writel(hba, 0, REG_AUTO_HIBERNATE_IDLE_TIMER);
1695-
1696-
/* wait host return to idle state when auto-hibern8 off */
1697-
ufs_mtk_wait_idle_state(hba, 5);
1698-
1699-
ret = ufs_mtk_wait_link_state(hba, VS_LINK_UP, 100);
1700-
if (ret)
1701-
dev_warn(hba->dev, "exit h8 state fail, ret=%d\n", ret);
1702-
}
1703-
17041742
static int ufs_mtk_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op,
17051743
enum ufs_notify_change_status status)
17061744
{
@@ -1709,7 +1747,7 @@ static int ufs_mtk_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op,
17091747

17101748
if (status == PRE_CHANGE) {
17111749
if (ufshcd_is_auto_hibern8_supported(hba))
1712-
ufs_mtk_auto_hibern8_disable(hba);
1750+
return ufs_mtk_auto_hibern8_disable(hba);
17131751
return 0;
17141752
}
17151753

@@ -1767,8 +1805,21 @@ static int ufs_mtk_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
17671805
}
17681806

17691807
return 0;
1808+
17701809
fail:
1771-
return ufshcd_link_recovery(hba);
1810+
/*
1811+
* Check if the platform (parent) device has resumed, and ensure that
1812+
* power, clock, and MTCMOS are all turned on.
1813+
*/
1814+
err = ufshcd_link_recovery(hba);
1815+
if (err) {
1816+
dev_err(hba->dev, "Device PM: req=%d, status:%d, err:%d\n",
1817+
hba->dev->power.request,
1818+
hba->dev->power.runtime_status,
1819+
hba->dev->power.runtime_error);
1820+
}
1821+
1822+
return 0; /* Cannot return a failure, otherwise, the I/O will hang. */
17721823
}
17731824

17741825
static void ufs_mtk_dbg_register_dump(struct ufs_hba *hba)
@@ -2139,6 +2190,7 @@ static int ufs_mtk_config_mcq_irq(struct ufs_hba *hba)
21392190
return ret;
21402191
}
21412192
}
2193+
host->is_mcq_intr_enabled = true;
21422194

21432195
return 0;
21442196
}
@@ -2222,10 +2274,12 @@ static const struct ufs_hba_variant_ops ufs_hba_mtk_vops = {
22222274
static int ufs_mtk_probe(struct platform_device *pdev)
22232275
{
22242276
int err;
2225-
struct device *dev = &pdev->dev;
2226-
struct device_node *reset_node;
2227-
struct platform_device *reset_pdev;
2277+
struct device *dev = &pdev->dev, *phy_dev = NULL;
2278+
struct device_node *reset_node, *phy_node = NULL;
2279+
struct platform_device *reset_pdev, *phy_pdev = NULL;
22282280
struct device_link *link;
2281+
struct ufs_hba *hba;
2282+
struct ufs_mtk_host *host;
22292283

22302284
reset_node = of_find_compatible_node(NULL, NULL,
22312285
"ti,syscon-reset");
@@ -2252,13 +2306,51 @@ static int ufs_mtk_probe(struct platform_device *pdev)
22522306
}
22532307

22542308
skip_reset:
2309+
/* find phy node */
2310+
phy_node = of_parse_phandle(dev->of_node, "phys", 0);
2311+
2312+
if (phy_node) {
2313+
phy_pdev = of_find_device_by_node(phy_node);
2314+
if (!phy_pdev)
2315+
goto skip_phy;
2316+
phy_dev = &phy_pdev->dev;
2317+
2318+
pm_runtime_set_active(phy_dev);
2319+
pm_runtime_enable(phy_dev);
2320+
pm_runtime_get_sync(phy_dev);
2321+
2322+
put_device(phy_dev);
2323+
dev_info(dev, "phys node found\n");
2324+
} else {
2325+
dev_notice(dev, "phys node not found\n");
2326+
}
2327+
2328+
skip_phy:
22552329
/* perform generic probe */
22562330
err = ufshcd_pltfrm_init(pdev, &ufs_hba_mtk_vops);
2257-
2258-
out:
2259-
if (err)
2331+
if (err) {
22602332
dev_err(dev, "probe failed %d\n", err);
2333+
goto out;
2334+
}
2335+
2336+
hba = platform_get_drvdata(pdev);
2337+
if (!hba)
2338+
goto out;
2339+
2340+
if (phy_node && phy_dev) {
2341+
host = ufshcd_get_variant(hba);
2342+
host->phy_dev = phy_dev;
2343+
}
22612344

2345+
/*
2346+
* Because the default power setting of VSx (the upper layer of
2347+
* VCCQ/VCCQ2) is HWLP, we need to prevent VCCQ/VCCQ2 from
2348+
* entering LPM.
2349+
*/
2350+
ufs_mtk_dev_vreg_set_lpm(hba, false);
2351+
2352+
out:
2353+
of_node_put(phy_node);
22622354
of_node_put(reset_node);
22632355
return err;
22642356
}
@@ -2283,34 +2375,46 @@ static int ufs_mtk_system_suspend(struct device *dev)
22832375

22842376
ret = ufshcd_system_suspend(dev);
22852377
if (ret)
2286-
return ret;
2378+
goto out;
2379+
2380+
if (pm_runtime_suspended(hba->dev))
2381+
goto out;
22872382

22882383
ufs_mtk_dev_vreg_set_lpm(hba, true);
22892384

22902385
if (ufs_mtk_is_rtff_mtcmos(hba))
22912386
ufs_mtk_mtcmos_ctrl(false, res);
22922387

2293-
return 0;
2388+
out:
2389+
return ret;
22942390
}
22952391

22962392
static int ufs_mtk_system_resume(struct device *dev)
22972393
{
2394+
int ret = 0;
22982395
struct ufs_hba *hba = dev_get_drvdata(dev);
22992396
struct arm_smccc_res res;
23002397

2301-
ufs_mtk_dev_vreg_set_lpm(hba, false);
2398+
if (pm_runtime_suspended(hba->dev))
2399+
goto out;
23022400

23032401
if (ufs_mtk_is_rtff_mtcmos(hba))
23042402
ufs_mtk_mtcmos_ctrl(true, res);
23052403

2306-
return ufshcd_system_resume(dev);
2404+
ufs_mtk_dev_vreg_set_lpm(hba, false);
2405+
2406+
out:
2407+
ret = ufshcd_system_resume(dev);
2408+
2409+
return ret;
23072410
}
23082411
#endif
23092412

23102413
#ifdef CONFIG_PM
23112414
static int ufs_mtk_runtime_suspend(struct device *dev)
23122415
{
23132416
struct ufs_hba *hba = dev_get_drvdata(dev);
2417+
struct ufs_mtk_host *host = ufshcd_get_variant(hba);
23142418
struct arm_smccc_res res;
23152419
int ret = 0;
23162420

@@ -2323,17 +2427,24 @@ static int ufs_mtk_runtime_suspend(struct device *dev)
23232427
if (ufs_mtk_is_rtff_mtcmos(hba))
23242428
ufs_mtk_mtcmos_ctrl(false, res);
23252429

2430+
if (host->phy_dev)
2431+
pm_runtime_put_sync(host->phy_dev);
2432+
23262433
return 0;
23272434
}
23282435

23292436
static int ufs_mtk_runtime_resume(struct device *dev)
23302437
{
23312438
struct ufs_hba *hba = dev_get_drvdata(dev);
2439+
struct ufs_mtk_host *host = ufshcd_get_variant(hba);
23322440
struct arm_smccc_res res;
23332441

23342442
if (ufs_mtk_is_rtff_mtcmos(hba))
23352443
ufs_mtk_mtcmos_ctrl(true, res);
23362444

2445+
if (host->phy_dev)
2446+
pm_runtime_get_sync(host->phy_dev);
2447+
23372448
ufs_mtk_dev_vreg_set_lpm(hba, false);
23382449

23392450
return ufshcd_runtime_resume(dev);

drivers/ufs/host/ufs-mediatek.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ struct ufs_mtk_host {
193193
bool is_mcq_intr_enabled;
194194
int mcq_nr_intr;
195195
struct ufs_mtk_mcq_intr_info mcq_intr_info[UFSHCD_MAX_Q_NR];
196+
struct device *phy_dev;
196197
};
197198

198199
/* MTK delay of autosuspend: 500 ms */

include/ufs/ufshcd.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1507,5 +1507,6 @@ int __ufshcd_write_ee_control(struct ufs_hba *hba, u32 ee_ctrl_mask);
15071507
int ufshcd_write_ee_control(struct ufs_hba *hba);
15081508
int ufshcd_update_ee_control(struct ufs_hba *hba, u16 *mask,
15091509
const u16 *other_mask, u16 set, u16 clr);
1510+
void ufshcd_force_error_recovery(struct ufs_hba *hba);
15101511

15111512
#endif /* End of Header */

0 commit comments

Comments
 (0)