Skip to content

Commit a232a8f

Browse files
masahir0ystorulf
authored andcommitted
mmc: sdhci-cadence: add suspend / resume support
Currently, the probe function initializes the PHY, but PHY settings are lost during the sleep state. Restore the PHY registers when resuming. To facilitate this, split sdhci_cdns_phy_init() into the DT parse part and PHY update part so that the latter can be invoked from the resume hook. Signed-off-by: Masahiro Yamada <[email protected]> Acked-by: Adrian Hunter <[email protected]> Signed-off-by: Ulf Hansson <[email protected]>
1 parent aab6e25 commit a232a8f

File tree

1 file changed

+97
-9
lines changed

1 file changed

+97
-9
lines changed

drivers/mmc/host/sdhci-cadence.c

Lines changed: 97 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,16 @@
6767
*/
6868
#define SDHCI_CDNS_MAX_TUNING_LOOP 40
6969

70+
struct sdhci_cdns_phy_param {
71+
u8 addr;
72+
u8 data;
73+
};
74+
7075
struct sdhci_cdns_priv {
7176
void __iomem *hrs_addr;
7277
bool enhanced_strobe;
78+
unsigned int nr_phy_params;
79+
struct sdhci_cdns_phy_param phy_params[0];
7380
};
7481

7582
struct sdhci_cdns_phy_cfg {
@@ -115,9 +122,22 @@ static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv,
115122
return 0;
116123
}
117124

118-
static int sdhci_cdns_phy_init(struct device_node *np,
119-
struct sdhci_cdns_priv *priv)
125+
static unsigned int sdhci_cdns_phy_param_count(struct device_node *np)
120126
{
127+
unsigned int count = 0;
128+
int i;
129+
130+
for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++)
131+
if (of_property_read_bool(np, sdhci_cdns_phy_cfgs[i].property))
132+
count++;
133+
134+
return count;
135+
}
136+
137+
static void sdhci_cdns_phy_param_parse(struct device_node *np,
138+
struct sdhci_cdns_priv *priv)
139+
{
140+
struct sdhci_cdns_phy_param *p = priv->phy_params;
121141
u32 val;
122142
int ret, i;
123143

@@ -127,9 +147,19 @@ static int sdhci_cdns_phy_init(struct device_node *np,
127147
if (ret)
128148
continue;
129149

130-
ret = sdhci_cdns_write_phy_reg(priv,
131-
sdhci_cdns_phy_cfgs[i].addr,
132-
val);
150+
p->addr = sdhci_cdns_phy_cfgs[i].addr;
151+
p->data = val;
152+
p++;
153+
}
154+
}
155+
156+
static int sdhci_cdns_phy_init(struct sdhci_cdns_priv *priv)
157+
{
158+
int ret, i;
159+
160+
for (i = 0; i < priv->nr_phy_params; i++) {
161+
ret = sdhci_cdns_write_phy_reg(priv, priv->phy_params[i].addr,
162+
priv->phy_params[i].data);
133163
if (ret)
134164
return ret;
135165
}
@@ -302,6 +332,8 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
302332
struct sdhci_pltfm_host *pltfm_host;
303333
struct sdhci_cdns_priv *priv;
304334
struct clk *clk;
335+
size_t priv_size;
336+
unsigned int nr_phy_params;
305337
int ret;
306338
struct device *dev = &pdev->dev;
307339

@@ -313,7 +345,9 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
313345
if (ret)
314346
return ret;
315347

316-
host = sdhci_pltfm_init(pdev, &sdhci_cdns_pltfm_data, sizeof(*priv));
348+
nr_phy_params = sdhci_cdns_phy_param_count(dev->of_node);
349+
priv_size = sizeof(*priv) + sizeof(priv->phy_params[0]) * nr_phy_params;
350+
host = sdhci_pltfm_init(pdev, &sdhci_cdns_pltfm_data, priv_size);
317351
if (IS_ERR(host)) {
318352
ret = PTR_ERR(host);
319353
goto disable_clk;
@@ -322,7 +356,8 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
322356
pltfm_host = sdhci_priv(host);
323357
pltfm_host->clk = clk;
324358

325-
priv = sdhci_cdns_priv(host);
359+
priv = sdhci_pltfm_priv(pltfm_host);
360+
priv->nr_phy_params = nr_phy_params;
326361
priv->hrs_addr = host->ioaddr;
327362
priv->enhanced_strobe = false;
328363
host->ioaddr += SDHCI_CDNS_SRS_BASE;
@@ -336,7 +371,9 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
336371
if (ret)
337372
goto free;
338373

339-
ret = sdhci_cdns_phy_init(dev->of_node, priv);
374+
sdhci_cdns_phy_param_parse(dev->of_node, priv);
375+
376+
ret = sdhci_cdns_phy_init(priv);
340377
if (ret)
341378
goto free;
342379

@@ -353,6 +390,57 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
353390
return ret;
354391
}
355392

393+
#ifdef CONFIG_PM_SLEEP
394+
static int sdhci_cdns_suspend(struct device *dev)
395+
{
396+
struct sdhci_host *host = dev_get_drvdata(dev);
397+
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
398+
int ret;
399+
400+
if (host->tuning_mode != SDHCI_TUNING_MODE_3)
401+
mmc_retune_needed(host->mmc);
402+
403+
ret = sdhci_suspend_host(host);
404+
if (ret)
405+
return ret;
406+
407+
clk_disable_unprepare(pltfm_host->clk);
408+
409+
return 0;
410+
}
411+
412+
static int sdhci_cdns_resume(struct device *dev)
413+
{
414+
struct sdhci_host *host = dev_get_drvdata(dev);
415+
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
416+
struct sdhci_cdns_priv *priv = sdhci_pltfm_priv(pltfm_host);
417+
int ret;
418+
419+
ret = clk_prepare_enable(pltfm_host->clk);
420+
if (ret)
421+
return ret;
422+
423+
ret = sdhci_cdns_phy_init(priv);
424+
if (ret)
425+
goto disable_clk;
426+
427+
ret = sdhci_resume_host(host);
428+
if (ret)
429+
goto disable_clk;
430+
431+
return 0;
432+
433+
disable_clk:
434+
clk_disable_unprepare(pltfm_host->clk);
435+
436+
return ret;
437+
}
438+
#endif
439+
440+
static const struct dev_pm_ops sdhci_cdns_pm_ops = {
441+
SET_SYSTEM_SLEEP_PM_OPS(sdhci_cdns_suspend, sdhci_cdns_resume)
442+
};
443+
356444
static const struct of_device_id sdhci_cdns_match[] = {
357445
{ .compatible = "socionext,uniphier-sd4hc" },
358446
{ .compatible = "cdns,sd4hc" },
@@ -363,7 +451,7 @@ MODULE_DEVICE_TABLE(of, sdhci_cdns_match);
363451
static struct platform_driver sdhci_cdns_driver = {
364452
.driver = {
365453
.name = "sdhci-cdns",
366-
.pm = &sdhci_pltfm_pmops,
454+
.pm = &sdhci_cdns_pm_ops,
367455
.of_match_table = sdhci_cdns_match,
368456
},
369457
.probe = sdhci_cdns_probe,

0 commit comments

Comments
 (0)