18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Copyright (c) 2018 Samsung Electronics Co., Ltd. 48c2ecf20Sopenharmony_ci// Author: Marek Szyprowski <m.szyprowski@samsung.com> 58c2ecf20Sopenharmony_ci// Common Clock Framework support for Exynos5 power-domain dependent clocks 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/io.h> 88c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 98c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 108c2ecf20Sopenharmony_ci#include <linux/pm_domain.h> 118c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "clk.h" 148c2ecf20Sopenharmony_ci#include "clk-exynos5-subcmu.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic struct samsung_clk_provider *ctx; 178c2ecf20Sopenharmony_cistatic const struct exynos5_subcmu_info **cmu; 188c2ecf20Sopenharmony_cistatic int nr_cmus; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic void exynos5_subcmu_clk_save(void __iomem *base, 218c2ecf20Sopenharmony_ci struct exynos5_subcmu_reg_dump *rd, 228c2ecf20Sopenharmony_ci unsigned int num_regs) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci for (; num_regs > 0; --num_regs, ++rd) { 258c2ecf20Sopenharmony_ci rd->save = readl(base + rd->offset); 268c2ecf20Sopenharmony_ci writel((rd->save & ~rd->mask) | rd->value, base + rd->offset); 278c2ecf20Sopenharmony_ci rd->save &= rd->mask; 288c2ecf20Sopenharmony_ci } 298c2ecf20Sopenharmony_ci}; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic void exynos5_subcmu_clk_restore(void __iomem *base, 328c2ecf20Sopenharmony_ci struct exynos5_subcmu_reg_dump *rd, 338c2ecf20Sopenharmony_ci unsigned int num_regs) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci for (; num_regs > 0; --num_regs, ++rd) 368c2ecf20Sopenharmony_ci writel((readl(base + rd->offset) & ~rd->mask) | rd->save, 378c2ecf20Sopenharmony_ci base + rd->offset); 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic void exynos5_subcmu_defer_gate(struct samsung_clk_provider *ctx, 418c2ecf20Sopenharmony_ci const struct samsung_gate_clock *list, int nr_clk) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci while (nr_clk--) 448c2ecf20Sopenharmony_ci samsung_clk_add_lookup(ctx, ERR_PTR(-EPROBE_DEFER), list++->id); 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* 488c2ecf20Sopenharmony_ci * Pass the needed clock provider context and register sub-CMU clocks 498c2ecf20Sopenharmony_ci * 508c2ecf20Sopenharmony_ci * NOTE: This function has to be called from the main, OF_CLK_DECLARE- 518c2ecf20Sopenharmony_ci * initialized clock provider driver. This happens very early during boot 528c2ecf20Sopenharmony_ci * process. Then this driver, during core_initcall registers two platform 538c2ecf20Sopenharmony_ci * drivers: one which binds to the same device-tree node as OF_CLK_DECLARE 548c2ecf20Sopenharmony_ci * driver and second, for handling its per-domain child-devices. Those 558c2ecf20Sopenharmony_ci * platform drivers are bound to their devices a bit later in arch_initcall, 568c2ecf20Sopenharmony_ci * when OF-core populates all device-tree nodes. 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_civoid exynos5_subcmus_init(struct samsung_clk_provider *_ctx, int _nr_cmus, 598c2ecf20Sopenharmony_ci const struct exynos5_subcmu_info **_cmu) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci ctx = _ctx; 628c2ecf20Sopenharmony_ci cmu = _cmu; 638c2ecf20Sopenharmony_ci nr_cmus = _nr_cmus; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci for (; _nr_cmus--; _cmu++) { 668c2ecf20Sopenharmony_ci exynos5_subcmu_defer_gate(ctx, (*_cmu)->gate_clks, 678c2ecf20Sopenharmony_ci (*_cmu)->nr_gate_clks); 688c2ecf20Sopenharmony_ci exynos5_subcmu_clk_save(ctx->reg_base, (*_cmu)->suspend_regs, 698c2ecf20Sopenharmony_ci (*_cmu)->nr_suspend_regs); 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int __maybe_unused exynos5_subcmu_suspend(struct device *dev) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct exynos5_subcmu_info *info = dev_get_drvdata(dev); 768c2ecf20Sopenharmony_ci unsigned long flags; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctx->lock, flags); 798c2ecf20Sopenharmony_ci exynos5_subcmu_clk_save(ctx->reg_base, info->suspend_regs, 808c2ecf20Sopenharmony_ci info->nr_suspend_regs); 818c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctx->lock, flags); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci return 0; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic int __maybe_unused exynos5_subcmu_resume(struct device *dev) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci struct exynos5_subcmu_info *info = dev_get_drvdata(dev); 898c2ecf20Sopenharmony_ci unsigned long flags; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctx->lock, flags); 928c2ecf20Sopenharmony_ci exynos5_subcmu_clk_restore(ctx->reg_base, info->suspend_regs, 938c2ecf20Sopenharmony_ci info->nr_suspend_regs); 948c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctx->lock, flags); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return 0; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic int __init exynos5_subcmu_probe(struct platform_device *pdev) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1028c2ecf20Sopenharmony_ci struct exynos5_subcmu_info *info = dev_get_drvdata(dev); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci pm_runtime_set_suspended(dev); 1058c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 1068c2ecf20Sopenharmony_ci pm_runtime_get(dev); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci ctx->dev = dev; 1098c2ecf20Sopenharmony_ci samsung_clk_register_div(ctx, info->div_clks, info->nr_div_clks); 1108c2ecf20Sopenharmony_ci samsung_clk_register_gate(ctx, info->gate_clks, info->nr_gate_clks); 1118c2ecf20Sopenharmony_ci ctx->dev = NULL; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return 0; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic const struct dev_pm_ops exynos5_subcmu_pm_ops = { 1198c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(exynos5_subcmu_suspend, 1208c2ecf20Sopenharmony_ci exynos5_subcmu_resume, NULL) 1218c2ecf20Sopenharmony_ci SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 1228c2ecf20Sopenharmony_ci pm_runtime_force_resume) 1238c2ecf20Sopenharmony_ci}; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic struct platform_driver exynos5_subcmu_driver __refdata = { 1268c2ecf20Sopenharmony_ci .driver = { 1278c2ecf20Sopenharmony_ci .name = "exynos5-subcmu", 1288c2ecf20Sopenharmony_ci .suppress_bind_attrs = true, 1298c2ecf20Sopenharmony_ci .pm = &exynos5_subcmu_pm_ops, 1308c2ecf20Sopenharmony_ci }, 1318c2ecf20Sopenharmony_ci .probe = exynos5_subcmu_probe, 1328c2ecf20Sopenharmony_ci}; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int __init exynos5_clk_register_subcmu(struct device *parent, 1358c2ecf20Sopenharmony_ci const struct exynos5_subcmu_info *info, 1368c2ecf20Sopenharmony_ci struct device_node *pd_node) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct of_phandle_args genpdspec = { .np = pd_node }; 1398c2ecf20Sopenharmony_ci struct platform_device *pdev; 1408c2ecf20Sopenharmony_ci int ret; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci pdev = platform_device_alloc("exynos5-subcmu", PLATFORM_DEVID_AUTO); 1438c2ecf20Sopenharmony_ci if (!pdev) 1448c2ecf20Sopenharmony_ci return -ENOMEM; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci pdev->dev.parent = parent; 1478c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, (void *)info); 1488c2ecf20Sopenharmony_ci of_genpd_add_device(&genpdspec, &pdev->dev); 1498c2ecf20Sopenharmony_ci ret = platform_device_add(pdev); 1508c2ecf20Sopenharmony_ci if (ret) 1518c2ecf20Sopenharmony_ci platform_device_put(pdev); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return ret; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic int __init exynos5_clk_probe(struct platform_device *pdev) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct device_node *np; 1598c2ecf20Sopenharmony_ci const char *name; 1608c2ecf20Sopenharmony_ci int i; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") { 1638c2ecf20Sopenharmony_ci if (of_property_read_string(np, "label", &name) < 0) 1648c2ecf20Sopenharmony_ci continue; 1658c2ecf20Sopenharmony_ci for (i = 0; i < nr_cmus; i++) 1668c2ecf20Sopenharmony_ci if (strcmp(cmu[i]->pd_name, name) == 0) 1678c2ecf20Sopenharmony_ci exynos5_clk_register_subcmu(&pdev->dev, 1688c2ecf20Sopenharmony_ci cmu[i], np); 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic const struct of_device_id exynos5_clk_of_match[] = { 1748c2ecf20Sopenharmony_ci { .compatible = "samsung,exynos5250-clock", }, 1758c2ecf20Sopenharmony_ci { .compatible = "samsung,exynos5420-clock", }, 1768c2ecf20Sopenharmony_ci { .compatible = "samsung,exynos5800-clock", }, 1778c2ecf20Sopenharmony_ci { }, 1788c2ecf20Sopenharmony_ci}; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic struct platform_driver exynos5_clk_driver __refdata = { 1818c2ecf20Sopenharmony_ci .driver = { 1828c2ecf20Sopenharmony_ci .name = "exynos5-clock", 1838c2ecf20Sopenharmony_ci .of_match_table = exynos5_clk_of_match, 1848c2ecf20Sopenharmony_ci .suppress_bind_attrs = true, 1858c2ecf20Sopenharmony_ci }, 1868c2ecf20Sopenharmony_ci .probe = exynos5_clk_probe, 1878c2ecf20Sopenharmony_ci}; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic int __init exynos5_clk_drv_init(void) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci platform_driver_register(&exynos5_clk_driver); 1928c2ecf20Sopenharmony_ci platform_driver_register(&exynos5_subcmu_driver); 1938c2ecf20Sopenharmony_ci return 0; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_cicore_initcall(exynos5_clk_drv_init); 196