1e2ad626fSUlf Hansson // SPDX-License-Identifier: GPL-2.0 2e2ad626fSUlf Hansson /* 3e2ad626fSUlf Hansson * Copyright 2022 NXP 4e2ad626fSUlf Hansson */ 5e2ad626fSUlf Hansson 6e2ad626fSUlf Hansson #include <linux/clk.h> 7e2ad626fSUlf Hansson #include <linux/delay.h> 8e2ad626fSUlf Hansson #include <linux/iopoll.h> 9e2ad626fSUlf Hansson #include <linux/mod_devicetable.h> 10e2ad626fSUlf Hansson #include <linux/module.h> 11e2ad626fSUlf Hansson #include <linux/platform_device.h> 12e2ad626fSUlf Hansson #include <linux/pm_domain.h> 13e2ad626fSUlf Hansson 14e2ad626fSUlf Hansson #define MIX_SLICE_SW_CTRL_OFF 0x20 15e2ad626fSUlf Hansson #define SLICE_SW_CTRL_PSW_CTRL_OFF_MASK BIT(4) 16e2ad626fSUlf Hansson #define SLICE_SW_CTRL_PDN_SOFT_MASK BIT(31) 17e2ad626fSUlf Hansson 18e2ad626fSUlf Hansson #define MIX_FUNC_STAT_OFF 0xB4 19e2ad626fSUlf Hansson 20e2ad626fSUlf Hansson #define FUNC_STAT_PSW_STAT_MASK BIT(0) 21e2ad626fSUlf Hansson #define FUNC_STAT_RST_STAT_MASK BIT(2) 22e2ad626fSUlf Hansson #define FUNC_STAT_ISO_STAT_MASK BIT(4) 23e2ad626fSUlf Hansson 24e2ad626fSUlf Hansson struct imx93_power_domain { 25e2ad626fSUlf Hansson struct generic_pm_domain genpd; 26e2ad626fSUlf Hansson struct device *dev; 27e2ad626fSUlf Hansson void __iomem *addr; 28e2ad626fSUlf Hansson struct clk_bulk_data *clks; 29e2ad626fSUlf Hansson int num_clks; 30e2ad626fSUlf Hansson bool init_off; 31e2ad626fSUlf Hansson }; 32e2ad626fSUlf Hansson 33e2ad626fSUlf Hansson #define to_imx93_pd(_genpd) container_of(_genpd, struct imx93_power_domain, genpd) 34e2ad626fSUlf Hansson 35e2ad626fSUlf Hansson static int imx93_pd_on(struct generic_pm_domain *genpd) 36e2ad626fSUlf Hansson { 37e2ad626fSUlf Hansson struct imx93_power_domain *domain = to_imx93_pd(genpd); 38e2ad626fSUlf Hansson void __iomem *addr = domain->addr; 39e2ad626fSUlf Hansson u32 val; 40e2ad626fSUlf Hansson int ret; 41e2ad626fSUlf Hansson 42e2ad626fSUlf Hansson ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks); 43e2ad626fSUlf Hansson if (ret) { 44e2ad626fSUlf Hansson dev_err(domain->dev, "failed to enable clocks for domain: %s\n", genpd->name); 45e2ad626fSUlf Hansson return ret; 46e2ad626fSUlf Hansson } 47e2ad626fSUlf Hansson 48e2ad626fSUlf Hansson val = readl(addr + MIX_SLICE_SW_CTRL_OFF); 49e2ad626fSUlf Hansson val &= ~SLICE_SW_CTRL_PDN_SOFT_MASK; 50e2ad626fSUlf Hansson writel(val, addr + MIX_SLICE_SW_CTRL_OFF); 51e2ad626fSUlf Hansson 52e2ad626fSUlf Hansson ret = readl_poll_timeout(addr + MIX_FUNC_STAT_OFF, val, 53e2ad626fSUlf Hansson !(val & FUNC_STAT_ISO_STAT_MASK), 1, 10000); 54e2ad626fSUlf Hansson if (ret) { 55e2ad626fSUlf Hansson dev_err(domain->dev, "pd_on timeout: name: %s, stat: %x\n", genpd->name, val); 56e2ad626fSUlf Hansson return ret; 57e2ad626fSUlf Hansson } 58e2ad626fSUlf Hansson 59e2ad626fSUlf Hansson return 0; 60e2ad626fSUlf Hansson } 61e2ad626fSUlf Hansson 62e2ad626fSUlf Hansson static int imx93_pd_off(struct generic_pm_domain *genpd) 63e2ad626fSUlf Hansson { 64e2ad626fSUlf Hansson struct imx93_power_domain *domain = to_imx93_pd(genpd); 65e2ad626fSUlf Hansson void __iomem *addr = domain->addr; 66e2ad626fSUlf Hansson int ret; 67e2ad626fSUlf Hansson u32 val; 68e2ad626fSUlf Hansson 69e2ad626fSUlf Hansson /* Power off MIX */ 70e2ad626fSUlf Hansson val = readl(addr + MIX_SLICE_SW_CTRL_OFF); 71e2ad626fSUlf Hansson val |= SLICE_SW_CTRL_PDN_SOFT_MASK; 72e2ad626fSUlf Hansson writel(val, addr + MIX_SLICE_SW_CTRL_OFF); 73e2ad626fSUlf Hansson 74e2ad626fSUlf Hansson ret = readl_poll_timeout(addr + MIX_FUNC_STAT_OFF, val, 75e2ad626fSUlf Hansson val & FUNC_STAT_PSW_STAT_MASK, 1, 1000); 76e2ad626fSUlf Hansson if (ret) { 77e2ad626fSUlf Hansson dev_err(domain->dev, "pd_off timeout: name: %s, stat: %x\n", genpd->name, val); 78e2ad626fSUlf Hansson return ret; 79e2ad626fSUlf Hansson } 80e2ad626fSUlf Hansson 81e2ad626fSUlf Hansson clk_bulk_disable_unprepare(domain->num_clks, domain->clks); 82e2ad626fSUlf Hansson 83e2ad626fSUlf Hansson return 0; 84e2ad626fSUlf Hansson }; 85e2ad626fSUlf Hansson 86*673c09bcSUwe Kleine-König static void imx93_pd_remove(struct platform_device *pdev) 87e2ad626fSUlf Hansson { 88e2ad626fSUlf Hansson struct imx93_power_domain *domain = platform_get_drvdata(pdev); 89e2ad626fSUlf Hansson struct device *dev = &pdev->dev; 90e2ad626fSUlf Hansson struct device_node *np = dev->of_node; 91e2ad626fSUlf Hansson 92e2ad626fSUlf Hansson if (!domain->init_off) 93e2ad626fSUlf Hansson clk_bulk_disable_unprepare(domain->num_clks, domain->clks); 94e2ad626fSUlf Hansson 95e2ad626fSUlf Hansson of_genpd_del_provider(np); 96e2ad626fSUlf Hansson pm_genpd_remove(&domain->genpd); 97e2ad626fSUlf Hansson } 98e2ad626fSUlf Hansson 99e2ad626fSUlf Hansson static int imx93_pd_probe(struct platform_device *pdev) 100e2ad626fSUlf Hansson { 101e2ad626fSUlf Hansson struct device *dev = &pdev->dev; 102e2ad626fSUlf Hansson struct device_node *np = dev->of_node; 103e2ad626fSUlf Hansson struct imx93_power_domain *domain; 104e2ad626fSUlf Hansson int ret; 105e2ad626fSUlf Hansson 106e2ad626fSUlf Hansson domain = devm_kzalloc(dev, sizeof(*domain), GFP_KERNEL); 107e2ad626fSUlf Hansson if (!domain) 108e2ad626fSUlf Hansson return -ENOMEM; 109e2ad626fSUlf Hansson 110e2ad626fSUlf Hansson domain->addr = devm_platform_ioremap_resource(pdev, 0); 111e2ad626fSUlf Hansson if (IS_ERR(domain->addr)) 112e2ad626fSUlf Hansson return PTR_ERR(domain->addr); 113e2ad626fSUlf Hansson 114e2ad626fSUlf Hansson domain->num_clks = devm_clk_bulk_get_all(dev, &domain->clks); 115e2ad626fSUlf Hansson if (domain->num_clks < 0) 116e2ad626fSUlf Hansson return dev_err_probe(dev, domain->num_clks, "Failed to get domain's clocks\n"); 117e2ad626fSUlf Hansson 118e2ad626fSUlf Hansson domain->genpd.name = dev_name(dev); 119e2ad626fSUlf Hansson domain->genpd.power_off = imx93_pd_off; 120e2ad626fSUlf Hansson domain->genpd.power_on = imx93_pd_on; 121e2ad626fSUlf Hansson domain->dev = dev; 122e2ad626fSUlf Hansson 123e2ad626fSUlf Hansson domain->init_off = readl(domain->addr + MIX_FUNC_STAT_OFF) & FUNC_STAT_ISO_STAT_MASK; 124e2ad626fSUlf Hansson /* Just to sync the status of hardware */ 125e2ad626fSUlf Hansson if (!domain->init_off) { 126e2ad626fSUlf Hansson ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks); 127e2ad626fSUlf Hansson if (ret) { 128e2ad626fSUlf Hansson dev_err(domain->dev, "failed to enable clocks for domain: %s\n", 129e2ad626fSUlf Hansson domain->genpd.name); 130e2ad626fSUlf Hansson return ret; 131e2ad626fSUlf Hansson } 132e2ad626fSUlf Hansson } 133e2ad626fSUlf Hansson 134e2ad626fSUlf Hansson ret = pm_genpd_init(&domain->genpd, NULL, domain->init_off); 135e2ad626fSUlf Hansson if (ret) 136e2ad626fSUlf Hansson goto err_clk_unprepare; 137e2ad626fSUlf Hansson 138e2ad626fSUlf Hansson platform_set_drvdata(pdev, domain); 139e2ad626fSUlf Hansson 140e2ad626fSUlf Hansson ret = of_genpd_add_provider_simple(np, &domain->genpd); 141e2ad626fSUlf Hansson if (ret) 142e2ad626fSUlf Hansson goto err_genpd_remove; 143e2ad626fSUlf Hansson 144e2ad626fSUlf Hansson return 0; 145e2ad626fSUlf Hansson 146e2ad626fSUlf Hansson err_genpd_remove: 147e2ad626fSUlf Hansson pm_genpd_remove(&domain->genpd); 148e2ad626fSUlf Hansson 149e2ad626fSUlf Hansson err_clk_unprepare: 150e2ad626fSUlf Hansson if (!domain->init_off) 151e2ad626fSUlf Hansson clk_bulk_disable_unprepare(domain->num_clks, domain->clks); 152e2ad626fSUlf Hansson 153e2ad626fSUlf Hansson return ret; 154e2ad626fSUlf Hansson } 155e2ad626fSUlf Hansson 156e2ad626fSUlf Hansson static const struct of_device_id imx93_pd_ids[] = { 157e2ad626fSUlf Hansson { .compatible = "fsl,imx93-src-slice" }, 158e2ad626fSUlf Hansson { } 159e2ad626fSUlf Hansson }; 160e2ad626fSUlf Hansson MODULE_DEVICE_TABLE(of, imx93_pd_ids); 161e2ad626fSUlf Hansson 162e2ad626fSUlf Hansson static struct platform_driver imx93_power_domain_driver = { 163e2ad626fSUlf Hansson .driver = { 164e2ad626fSUlf Hansson .name = "imx93_power_domain", 165e2ad626fSUlf Hansson .of_match_table = imx93_pd_ids, 166e2ad626fSUlf Hansson }, 167e2ad626fSUlf Hansson .probe = imx93_pd_probe, 168*673c09bcSUwe Kleine-König .remove_new = imx93_pd_remove, 169e2ad626fSUlf Hansson }; 170e2ad626fSUlf Hansson module_platform_driver(imx93_power_domain_driver); 171e2ad626fSUlf Hansson 172e2ad626fSUlf Hansson MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>"); 173e2ad626fSUlf Hansson MODULE_DESCRIPTION("NXP i.MX93 power domain driver"); 174e2ad626fSUlf Hansson MODULE_LICENSE("GPL"); 175