162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2021 Linaro Ltd. 462306a36Sopenharmony_ci * Copyright (C) 2021 Dávid Virág <virag.david003@gmail.com> 562306a36Sopenharmony_ci * Author: Sam Protsenko <semen.protsenko@linaro.org> 662306a36Sopenharmony_ci * Author: Dávid Virág <virag.david003@gmail.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This file contains shared functions used by some arm64 Exynos SoCs, 962306a36Sopenharmony_ci * such as Exynos7885 or Exynos850 to register and init CMUs. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci#include <linux/clk.h> 1262306a36Sopenharmony_ci#include <linux/of_address.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci#include <linux/platform_device.h> 1562306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "clk-exynos-arm64.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* Gate register bits */ 2162306a36Sopenharmony_ci#define GATE_MANUAL BIT(20) 2262306a36Sopenharmony_ci#define GATE_ENABLE_HWACG BIT(28) 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* Gate register offsets range */ 2562306a36Sopenharmony_ci#define GATE_OFF_START 0x2000 2662306a36Sopenharmony_ci#define GATE_OFF_END 0x2fff 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistruct exynos_arm64_cmu_data { 2962306a36Sopenharmony_ci struct samsung_clk_reg_dump *clk_save; 3062306a36Sopenharmony_ci unsigned int nr_clk_save; 3162306a36Sopenharmony_ci const struct samsung_clk_reg_dump *clk_suspend; 3262306a36Sopenharmony_ci unsigned int nr_clk_suspend; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci struct clk *clk; 3562306a36Sopenharmony_ci struct clk **pclks; 3662306a36Sopenharmony_ci int nr_pclks; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci struct samsung_clk_provider *ctx; 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/** 4262306a36Sopenharmony_ci * exynos_arm64_init_clocks - Set clocks initial configuration 4362306a36Sopenharmony_ci * @np: CMU device tree node with "reg" property (CMU addr) 4462306a36Sopenharmony_ci * @reg_offs: Register offsets array for clocks to init 4562306a36Sopenharmony_ci * @reg_offs_len: Number of register offsets in reg_offs array 4662306a36Sopenharmony_ci * 4762306a36Sopenharmony_ci * Set manual control mode for all gate clocks. 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_cistatic void __init exynos_arm64_init_clocks(struct device_node *np, 5062306a36Sopenharmony_ci const unsigned long *reg_offs, size_t reg_offs_len) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci void __iomem *reg_base; 5362306a36Sopenharmony_ci size_t i; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci reg_base = of_iomap(np, 0); 5662306a36Sopenharmony_ci if (!reg_base) 5762306a36Sopenharmony_ci panic("%s: failed to map registers\n", __func__); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci for (i = 0; i < reg_offs_len; ++i) { 6062306a36Sopenharmony_ci void __iomem *reg = reg_base + reg_offs[i]; 6162306a36Sopenharmony_ci u32 val; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci /* Modify only gate clock registers */ 6462306a36Sopenharmony_ci if (reg_offs[i] < GATE_OFF_START || reg_offs[i] > GATE_OFF_END) 6562306a36Sopenharmony_ci continue; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci val = readl(reg); 6862306a36Sopenharmony_ci val |= GATE_MANUAL; 6962306a36Sopenharmony_ci val &= ~GATE_ENABLE_HWACG; 7062306a36Sopenharmony_ci writel(val, reg); 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci iounmap(reg_base); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/** 7762306a36Sopenharmony_ci * exynos_arm64_enable_bus_clk - Enable parent clock of specified CMU 7862306a36Sopenharmony_ci * 7962306a36Sopenharmony_ci * @dev: Device object; may be NULL if this function is not being 8062306a36Sopenharmony_ci * called from platform driver probe function 8162306a36Sopenharmony_ci * @np: CMU device tree node 8262306a36Sopenharmony_ci * @cmu: CMU data 8362306a36Sopenharmony_ci * 8462306a36Sopenharmony_ci * Keep CMU parent clock running (needed for CMU registers access). 8562306a36Sopenharmony_ci * 8662306a36Sopenharmony_ci * Return: 0 on success or a negative error code on failure. 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_cistatic int __init exynos_arm64_enable_bus_clk(struct device *dev, 8962306a36Sopenharmony_ci struct device_node *np, const struct samsung_cmu_info *cmu) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct clk *parent_clk; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (!cmu->clk_name) 9462306a36Sopenharmony_ci return 0; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (dev) { 9762306a36Sopenharmony_ci struct exynos_arm64_cmu_data *data; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci parent_clk = clk_get(dev, cmu->clk_name); 10062306a36Sopenharmony_ci data = dev_get_drvdata(dev); 10162306a36Sopenharmony_ci if (data) 10262306a36Sopenharmony_ci data->clk = parent_clk; 10362306a36Sopenharmony_ci } else { 10462306a36Sopenharmony_ci parent_clk = of_clk_get_by_name(np, cmu->clk_name); 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (IS_ERR(parent_clk)) 10862306a36Sopenharmony_ci return PTR_ERR(parent_clk); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci return clk_prepare_enable(parent_clk); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic int __init exynos_arm64_cmu_prepare_pm(struct device *dev, 11462306a36Sopenharmony_ci const struct samsung_cmu_info *cmu) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci struct exynos_arm64_cmu_data *data = dev_get_drvdata(dev); 11762306a36Sopenharmony_ci int i; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci data->clk_save = samsung_clk_alloc_reg_dump(cmu->clk_regs, 12062306a36Sopenharmony_ci cmu->nr_clk_regs); 12162306a36Sopenharmony_ci if (!data->clk_save) 12262306a36Sopenharmony_ci return -ENOMEM; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci data->nr_clk_save = cmu->nr_clk_regs; 12562306a36Sopenharmony_ci data->clk_suspend = cmu->suspend_regs; 12662306a36Sopenharmony_ci data->nr_clk_suspend = cmu->nr_suspend_regs; 12762306a36Sopenharmony_ci data->nr_pclks = of_clk_get_parent_count(dev->of_node); 12862306a36Sopenharmony_ci if (!data->nr_pclks) 12962306a36Sopenharmony_ci return 0; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci data->pclks = devm_kcalloc(dev, sizeof(struct clk *), data->nr_pclks, 13262306a36Sopenharmony_ci GFP_KERNEL); 13362306a36Sopenharmony_ci if (!data->pclks) { 13462306a36Sopenharmony_ci kfree(data->clk_save); 13562306a36Sopenharmony_ci return -ENOMEM; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci for (i = 0; i < data->nr_pclks; i++) { 13962306a36Sopenharmony_ci struct clk *clk = of_clk_get(dev->of_node, i); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (IS_ERR(clk)) { 14262306a36Sopenharmony_ci kfree(data->clk_save); 14362306a36Sopenharmony_ci while (--i >= 0) 14462306a36Sopenharmony_ci clk_put(data->pclks[i]); 14562306a36Sopenharmony_ci return PTR_ERR(clk); 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci data->pclks[i] = clk; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/** 15462306a36Sopenharmony_ci * exynos_arm64_register_cmu - Register specified Exynos CMU domain 15562306a36Sopenharmony_ci * @dev: Device object; may be NULL if this function is not being 15662306a36Sopenharmony_ci * called from platform driver probe function 15762306a36Sopenharmony_ci * @np: CMU device tree node 15862306a36Sopenharmony_ci * @cmu: CMU data 15962306a36Sopenharmony_ci * 16062306a36Sopenharmony_ci * Register specified CMU domain, which includes next steps: 16162306a36Sopenharmony_ci * 16262306a36Sopenharmony_ci * 1. Enable parent clock of @cmu CMU 16362306a36Sopenharmony_ci * 2. Set initial registers configuration for @cmu CMU clocks 16462306a36Sopenharmony_ci * 3. Register @cmu CMU clocks using Samsung clock framework API 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_civoid __init exynos_arm64_register_cmu(struct device *dev, 16762306a36Sopenharmony_ci struct device_node *np, const struct samsung_cmu_info *cmu) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci int err; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* 17262306a36Sopenharmony_ci * Try to boot even if the parent clock enablement fails, as it might be 17362306a36Sopenharmony_ci * already enabled by bootloader. 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_ci err = exynos_arm64_enable_bus_clk(dev, np, cmu); 17662306a36Sopenharmony_ci if (err) 17762306a36Sopenharmony_ci pr_err("%s: could not enable bus clock %s; err = %d\n", 17862306a36Sopenharmony_ci __func__, cmu->clk_name, err); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci exynos_arm64_init_clocks(np, cmu->clk_regs, cmu->nr_clk_regs); 18162306a36Sopenharmony_ci samsung_cmu_register_one(np, cmu); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci/** 18562306a36Sopenharmony_ci * exynos_arm64_register_cmu_pm - Register Exynos CMU domain with PM support 18662306a36Sopenharmony_ci * 18762306a36Sopenharmony_ci * @pdev: Platform device object 18862306a36Sopenharmony_ci * @set_manual: If true, set gate clocks to manual mode 18962306a36Sopenharmony_ci * 19062306a36Sopenharmony_ci * It's a version of exynos_arm64_register_cmu() with PM support. Should be 19162306a36Sopenharmony_ci * called from probe function of platform driver. 19262306a36Sopenharmony_ci * 19362306a36Sopenharmony_ci * Return: 0 on success, or negative error code on error. 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_ciint __init exynos_arm64_register_cmu_pm(struct platform_device *pdev, 19662306a36Sopenharmony_ci bool set_manual) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci const struct samsung_cmu_info *cmu; 19962306a36Sopenharmony_ci struct device *dev = &pdev->dev; 20062306a36Sopenharmony_ci struct device_node *np = dev->of_node; 20162306a36Sopenharmony_ci struct exynos_arm64_cmu_data *data; 20262306a36Sopenharmony_ci void __iomem *reg_base; 20362306a36Sopenharmony_ci int ret; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci cmu = of_device_get_match_data(dev); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 20862306a36Sopenharmony_ci if (!data) 20962306a36Sopenharmony_ci return -ENOMEM; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci platform_set_drvdata(pdev, data); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci ret = exynos_arm64_cmu_prepare_pm(dev, cmu); 21462306a36Sopenharmony_ci if (ret) 21562306a36Sopenharmony_ci return ret; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* 21862306a36Sopenharmony_ci * Try to boot even if the parent clock enablement fails, as it might be 21962306a36Sopenharmony_ci * already enabled by bootloader. 22062306a36Sopenharmony_ci */ 22162306a36Sopenharmony_ci ret = exynos_arm64_enable_bus_clk(dev, NULL, cmu); 22262306a36Sopenharmony_ci if (ret) 22362306a36Sopenharmony_ci dev_err(dev, "%s: could not enable bus clock %s; err = %d\n", 22462306a36Sopenharmony_ci __func__, cmu->clk_name, ret); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (set_manual) 22762306a36Sopenharmony_ci exynos_arm64_init_clocks(np, cmu->clk_regs, cmu->nr_clk_regs); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci reg_base = devm_platform_ioremap_resource(pdev, 0); 23062306a36Sopenharmony_ci if (IS_ERR(reg_base)) 23162306a36Sopenharmony_ci return PTR_ERR(reg_base); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci data->ctx = samsung_clk_init(dev, reg_base, cmu->nr_clk_ids); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* 23662306a36Sopenharmony_ci * Enable runtime PM here to allow the clock core using runtime PM 23762306a36Sopenharmony_ci * for the registered clocks. Additionally, we increase the runtime 23862306a36Sopenharmony_ci * PM usage count before registering the clocks, to prevent the 23962306a36Sopenharmony_ci * clock core from runtime suspending the device. 24062306a36Sopenharmony_ci */ 24162306a36Sopenharmony_ci pm_runtime_get_noresume(dev); 24262306a36Sopenharmony_ci pm_runtime_set_active(dev); 24362306a36Sopenharmony_ci pm_runtime_enable(dev); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci samsung_cmu_register_clocks(data->ctx, cmu); 24662306a36Sopenharmony_ci samsung_clk_of_add_provider(dev->of_node, data->ctx); 24762306a36Sopenharmony_ci pm_runtime_put_sync(dev); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci return 0; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ciint exynos_arm64_cmu_suspend(struct device *dev) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct exynos_arm64_cmu_data *data = dev_get_drvdata(dev); 25562306a36Sopenharmony_ci int i; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci samsung_clk_save(data->ctx->reg_base, data->clk_save, 25862306a36Sopenharmony_ci data->nr_clk_save); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci for (i = 0; i < data->nr_pclks; i++) 26162306a36Sopenharmony_ci clk_prepare_enable(data->pclks[i]); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* For suspend some registers have to be set to certain values */ 26462306a36Sopenharmony_ci samsung_clk_restore(data->ctx->reg_base, data->clk_suspend, 26562306a36Sopenharmony_ci data->nr_clk_suspend); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci for (i = 0; i < data->nr_pclks; i++) 26862306a36Sopenharmony_ci clk_disable_unprepare(data->pclks[i]); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci clk_disable_unprepare(data->clk); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci return 0; 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ciint exynos_arm64_cmu_resume(struct device *dev) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci struct exynos_arm64_cmu_data *data = dev_get_drvdata(dev); 27862306a36Sopenharmony_ci int i; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci clk_prepare_enable(data->clk); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci for (i = 0; i < data->nr_pclks; i++) 28362306a36Sopenharmony_ci clk_prepare_enable(data->pclks[i]); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci samsung_clk_restore(data->ctx->reg_base, data->clk_save, 28662306a36Sopenharmony_ci data->nr_clk_save); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci for (i = 0; i < data->nr_pclks; i++) 28962306a36Sopenharmony_ci clk_disable_unprepare(data->pclks[i]); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci return 0; 29262306a36Sopenharmony_ci} 293