162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2022 NXP 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/clk.h> 762306a36Sopenharmony_ci#include <linux/delay.h> 862306a36Sopenharmony_ci#include <linux/iopoll.h> 962306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/platform_device.h> 1262306a36Sopenharmony_ci#include <linux/pm_domain.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define MIX_SLICE_SW_CTRL_OFF 0x20 1562306a36Sopenharmony_ci#define SLICE_SW_CTRL_PSW_CTRL_OFF_MASK BIT(4) 1662306a36Sopenharmony_ci#define SLICE_SW_CTRL_PDN_SOFT_MASK BIT(31) 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define MIX_FUNC_STAT_OFF 0xB4 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define FUNC_STAT_PSW_STAT_MASK BIT(0) 2162306a36Sopenharmony_ci#define FUNC_STAT_RST_STAT_MASK BIT(2) 2262306a36Sopenharmony_ci#define FUNC_STAT_ISO_STAT_MASK BIT(4) 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistruct imx93_power_domain { 2562306a36Sopenharmony_ci struct generic_pm_domain genpd; 2662306a36Sopenharmony_ci struct device *dev; 2762306a36Sopenharmony_ci void __iomem *addr; 2862306a36Sopenharmony_ci struct clk_bulk_data *clks; 2962306a36Sopenharmony_ci int num_clks; 3062306a36Sopenharmony_ci bool init_off; 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define to_imx93_pd(_genpd) container_of(_genpd, struct imx93_power_domain, genpd) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic int imx93_pd_on(struct generic_pm_domain *genpd) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci struct imx93_power_domain *domain = to_imx93_pd(genpd); 3862306a36Sopenharmony_ci void __iomem *addr = domain->addr; 3962306a36Sopenharmony_ci u32 val; 4062306a36Sopenharmony_ci int ret; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks); 4362306a36Sopenharmony_ci if (ret) { 4462306a36Sopenharmony_ci dev_err(domain->dev, "failed to enable clocks for domain: %s\n", genpd->name); 4562306a36Sopenharmony_ci return ret; 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci val = readl(addr + MIX_SLICE_SW_CTRL_OFF); 4962306a36Sopenharmony_ci val &= ~SLICE_SW_CTRL_PDN_SOFT_MASK; 5062306a36Sopenharmony_ci writel(val, addr + MIX_SLICE_SW_CTRL_OFF); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci ret = readl_poll_timeout(addr + MIX_FUNC_STAT_OFF, val, 5362306a36Sopenharmony_ci !(val & FUNC_STAT_ISO_STAT_MASK), 1, 10000); 5462306a36Sopenharmony_ci if (ret) { 5562306a36Sopenharmony_ci dev_err(domain->dev, "pd_on timeout: name: %s, stat: %x\n", genpd->name, val); 5662306a36Sopenharmony_ci return ret; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci return 0; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic int imx93_pd_off(struct generic_pm_domain *genpd) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci struct imx93_power_domain *domain = to_imx93_pd(genpd); 6562306a36Sopenharmony_ci void __iomem *addr = domain->addr; 6662306a36Sopenharmony_ci int ret; 6762306a36Sopenharmony_ci u32 val; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* Power off MIX */ 7062306a36Sopenharmony_ci val = readl(addr + MIX_SLICE_SW_CTRL_OFF); 7162306a36Sopenharmony_ci val |= SLICE_SW_CTRL_PDN_SOFT_MASK; 7262306a36Sopenharmony_ci writel(val, addr + MIX_SLICE_SW_CTRL_OFF); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci ret = readl_poll_timeout(addr + MIX_FUNC_STAT_OFF, val, 7562306a36Sopenharmony_ci val & FUNC_STAT_PSW_STAT_MASK, 1, 1000); 7662306a36Sopenharmony_ci if (ret) { 7762306a36Sopenharmony_ci dev_err(domain->dev, "pd_off timeout: name: %s, stat: %x\n", genpd->name, val); 7862306a36Sopenharmony_ci return ret; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci clk_bulk_disable_unprepare(domain->num_clks, domain->clks); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return 0; 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int imx93_pd_remove(struct platform_device *pdev) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct imx93_power_domain *domain = platform_get_drvdata(pdev); 8962306a36Sopenharmony_ci struct device *dev = &pdev->dev; 9062306a36Sopenharmony_ci struct device_node *np = dev->of_node; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (!domain->init_off) 9362306a36Sopenharmony_ci clk_bulk_disable_unprepare(domain->num_clks, domain->clks); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci of_genpd_del_provider(np); 9662306a36Sopenharmony_ci pm_genpd_remove(&domain->genpd); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return 0; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic int imx93_pd_probe(struct platform_device *pdev) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct device *dev = &pdev->dev; 10462306a36Sopenharmony_ci struct device_node *np = dev->of_node; 10562306a36Sopenharmony_ci struct imx93_power_domain *domain; 10662306a36Sopenharmony_ci int ret; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci domain = devm_kzalloc(dev, sizeof(*domain), GFP_KERNEL); 10962306a36Sopenharmony_ci if (!domain) 11062306a36Sopenharmony_ci return -ENOMEM; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci domain->addr = devm_platform_ioremap_resource(pdev, 0); 11362306a36Sopenharmony_ci if (IS_ERR(domain->addr)) 11462306a36Sopenharmony_ci return PTR_ERR(domain->addr); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci domain->num_clks = devm_clk_bulk_get_all(dev, &domain->clks); 11762306a36Sopenharmony_ci if (domain->num_clks < 0) 11862306a36Sopenharmony_ci return dev_err_probe(dev, domain->num_clks, "Failed to get domain's clocks\n"); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci domain->genpd.name = dev_name(dev); 12162306a36Sopenharmony_ci domain->genpd.power_off = imx93_pd_off; 12262306a36Sopenharmony_ci domain->genpd.power_on = imx93_pd_on; 12362306a36Sopenharmony_ci domain->dev = dev; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci domain->init_off = readl(domain->addr + MIX_FUNC_STAT_OFF) & FUNC_STAT_ISO_STAT_MASK; 12662306a36Sopenharmony_ci /* Just to sync the status of hardware */ 12762306a36Sopenharmony_ci if (!domain->init_off) { 12862306a36Sopenharmony_ci ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks); 12962306a36Sopenharmony_ci if (ret) { 13062306a36Sopenharmony_ci dev_err(domain->dev, "failed to enable clocks for domain: %s\n", 13162306a36Sopenharmony_ci domain->genpd.name); 13262306a36Sopenharmony_ci return ret; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci ret = pm_genpd_init(&domain->genpd, NULL, domain->init_off); 13762306a36Sopenharmony_ci if (ret) 13862306a36Sopenharmony_ci goto err_clk_unprepare; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci platform_set_drvdata(pdev, domain); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci ret = of_genpd_add_provider_simple(np, &domain->genpd); 14362306a36Sopenharmony_ci if (ret) 14462306a36Sopenharmony_ci goto err_genpd_remove; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci return 0; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cierr_genpd_remove: 14962306a36Sopenharmony_ci pm_genpd_remove(&domain->genpd); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cierr_clk_unprepare: 15262306a36Sopenharmony_ci if (!domain->init_off) 15362306a36Sopenharmony_ci clk_bulk_disable_unprepare(domain->num_clks, domain->clks); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci return ret; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic const struct of_device_id imx93_pd_ids[] = { 15962306a36Sopenharmony_ci { .compatible = "fsl,imx93-src-slice" }, 16062306a36Sopenharmony_ci { } 16162306a36Sopenharmony_ci}; 16262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, imx93_pd_ids); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic struct platform_driver imx93_power_domain_driver = { 16562306a36Sopenharmony_ci .driver = { 16662306a36Sopenharmony_ci .name = "imx93_power_domain", 16762306a36Sopenharmony_ci .of_match_table = imx93_pd_ids, 16862306a36Sopenharmony_ci }, 16962306a36Sopenharmony_ci .probe = imx93_pd_probe, 17062306a36Sopenharmony_ci .remove = imx93_pd_remove, 17162306a36Sopenharmony_ci}; 17262306a36Sopenharmony_cimodule_platform_driver(imx93_power_domain_driver); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ciMODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>"); 17562306a36Sopenharmony_ciMODULE_DESCRIPTION("NXP i.MX93 power domain driver"); 17662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 177