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

pmdomain: imx8mp-blk-ctrl: Keep gpc power domain on for system wakeup

Current design will power off all dependent GPC power domains in
imx8mp_blk_ctrl_suspend(), even though the user device has enabled
wakeup capability. The result is that wakeup function never works
for such device.

An example will be USB wakeup on i.MX8MP. PHY device '382f0040.usb-phy'
is attached to power domain 'hsioblk-usb-phy2' which is spawned by hsio
block control. A virtual power domain device 'genpd:3:32f10000.blk-ctrl'
is created to build connection with 'hsioblk-usb-phy2' and it depends on
GPC power domain 'usb-otg2'. If device '382f0040.usb-phy' enable wakeup,
only power domain 'hsioblk-usb-phy2' keeps on during system suspend,
power domain 'usb-otg2' is off all the time. So the wakeup event can't
happen.

In order to further establish a connection between the power domains
related to GPC and block control during system suspend, register a genpd
power on/off notifier for the power_dev. This allows us to prevent the GPC
power domain from being powered off, in case the block control power
domain is kept on to serve system wakeup.

Suggested-by: Ulf Hansson <ulf.hansson@linaro.org>
Fixes: 556f5cf9568a ("soc: imx: add i.MX8MP HSIO blk-ctrl")
Cc: stable@vger.kernel.org
Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>

authored by

Xu Yang and committed by
Ulf Hansson
e9ab2b83 6bd8b4a9

+26
+26
drivers/pmdomain/imx/imx8mp-blk-ctrl.c
··· 65 65 struct icc_bulk_data paths[DOMAIN_MAX_PATHS]; 66 66 struct device *power_dev; 67 67 struct imx8mp_blk_ctrl *bc; 68 + struct notifier_block power_nb; 68 69 int num_paths; 69 70 int id; 70 71 }; ··· 595 594 return 0; 596 595 } 597 596 597 + static int imx8mp_blk_ctrl_gpc_notifier(struct notifier_block *nb, 598 + unsigned long action, void *data) 599 + { 600 + struct imx8mp_blk_ctrl_domain *domain = 601 + container_of(nb, struct imx8mp_blk_ctrl_domain, power_nb); 602 + 603 + if (action == GENPD_NOTIFY_PRE_OFF) { 604 + if (domain->genpd.status == GENPD_STATE_ON) 605 + return NOTIFY_BAD; 606 + } 607 + 608 + return NOTIFY_OK; 609 + } 610 + 598 611 static struct lock_class_key blk_ctrl_genpd_lock_class; 599 612 600 613 static int imx8mp_blk_ctrl_probe(struct platform_device *pdev) ··· 713 698 goto cleanup_pds; 714 699 } 715 700 701 + domain->power_nb.notifier_call = imx8mp_blk_ctrl_gpc_notifier; 702 + ret = dev_pm_genpd_add_notifier(domain->power_dev, &domain->power_nb); 703 + if (ret) { 704 + dev_err_probe(dev, ret, "failed to add power notifier\n"); 705 + dev_pm_domain_detach(domain->power_dev, true); 706 + goto cleanup_pds; 707 + } 708 + 716 709 domain->genpd.name = data->name; 717 710 domain->genpd.power_on = imx8mp_blk_ctrl_power_on; 718 711 domain->genpd.power_off = imx8mp_blk_ctrl_power_off; ··· 730 707 ret = pm_genpd_init(&domain->genpd, NULL, true); 731 708 if (ret) { 732 709 dev_err_probe(dev, ret, "failed to init power domain\n"); 710 + dev_pm_genpd_remove_notifier(domain->power_dev); 733 711 dev_pm_domain_detach(domain->power_dev, true); 734 712 goto cleanup_pds; 735 713 } ··· 779 755 cleanup_pds: 780 756 for (i--; i >= 0; i--) { 781 757 pm_genpd_remove(&bc->domains[i].genpd); 758 + dev_pm_genpd_remove_notifier(bc->domains[i].power_dev); 782 759 dev_pm_domain_detach(bc->domains[i].power_dev, true); 783 760 } 784 761 ··· 799 774 struct imx8mp_blk_ctrl_domain *domain = &bc->domains[i]; 800 775 801 776 pm_genpd_remove(&domain->genpd); 777 + dev_pm_genpd_remove_notifier(domain->power_dev); 802 778 dev_pm_domain_detach(domain->power_dev, true); 803 779 } 804 780