1cfe238e4SDavid Virag // SPDX-License-Identifier: GPL-2.0-only 2cfe238e4SDavid Virag /* 3cfe238e4SDavid Virag * Copyright (C) 2021 Linaro Ltd. 4cfe238e4SDavid Virag * Copyright (C) 2021 Dávid Virág <virag.david003@gmail.com> 5cfe238e4SDavid Virag * Author: Sam Protsenko <semen.protsenko@linaro.org> 6cfe238e4SDavid Virag * Author: Dávid Virág <virag.david003@gmail.com> 7cfe238e4SDavid Virag * 8cfe238e4SDavid Virag * This file contains shared functions used by some arm64 Exynos SoCs, 9cfe238e4SDavid Virag * such as Exynos7885 or Exynos850 to register and init CMUs. 10cfe238e4SDavid Virag */ 11cfe238e4SDavid Virag #include <linux/clk.h> 12cfe238e4SDavid Virag #include <linux/of_address.h> 13*f05dc202SSam Protsenko #include <linux/of_device.h> 14*f05dc202SSam Protsenko #include <linux/pm_runtime.h> 15*f05dc202SSam Protsenko #include <linux/slab.h> 16cfe238e4SDavid Virag 17cfe238e4SDavid Virag #include "clk-exynos-arm64.h" 18cfe238e4SDavid Virag 19cfe238e4SDavid Virag /* Gate register bits */ 20cfe238e4SDavid Virag #define GATE_MANUAL BIT(20) 21cfe238e4SDavid Virag #define GATE_ENABLE_HWACG BIT(28) 22cfe238e4SDavid Virag 23cfe238e4SDavid Virag /* Gate register offsets range */ 24cfe238e4SDavid Virag #define GATE_OFF_START 0x2000 25cfe238e4SDavid Virag #define GATE_OFF_END 0x2fff 26cfe238e4SDavid Virag 27*f05dc202SSam Protsenko struct exynos_arm64_cmu_data { 28*f05dc202SSam Protsenko struct samsung_clk_reg_dump *clk_save; 29*f05dc202SSam Protsenko unsigned int nr_clk_save; 30*f05dc202SSam Protsenko const struct samsung_clk_reg_dump *clk_suspend; 31*f05dc202SSam Protsenko unsigned int nr_clk_suspend; 32*f05dc202SSam Protsenko 33*f05dc202SSam Protsenko struct clk *clk; 34*f05dc202SSam Protsenko struct clk **pclks; 35*f05dc202SSam Protsenko int nr_pclks; 36*f05dc202SSam Protsenko 37*f05dc202SSam Protsenko struct samsung_clk_provider *ctx; 38*f05dc202SSam Protsenko }; 39*f05dc202SSam Protsenko 40cfe238e4SDavid Virag /** 41cfe238e4SDavid Virag * exynos_arm64_init_clocks - Set clocks initial configuration 42cfe238e4SDavid Virag * @np: CMU device tree node with "reg" property (CMU addr) 43cfe238e4SDavid Virag * @reg_offs: Register offsets array for clocks to init 44cfe238e4SDavid Virag * @reg_offs_len: Number of register offsets in reg_offs array 45cfe238e4SDavid Virag * 46cfe238e4SDavid Virag * Set manual control mode for all gate clocks. 47cfe238e4SDavid Virag */ 48cfe238e4SDavid Virag static void __init exynos_arm64_init_clocks(struct device_node *np, 49cfe238e4SDavid Virag const unsigned long *reg_offs, size_t reg_offs_len) 50cfe238e4SDavid Virag { 51cfe238e4SDavid Virag void __iomem *reg_base; 52cfe238e4SDavid Virag size_t i; 53cfe238e4SDavid Virag 54cfe238e4SDavid Virag reg_base = of_iomap(np, 0); 55cfe238e4SDavid Virag if (!reg_base) 56cfe238e4SDavid Virag panic("%s: failed to map registers\n", __func__); 57cfe238e4SDavid Virag 58cfe238e4SDavid Virag for (i = 0; i < reg_offs_len; ++i) { 59cfe238e4SDavid Virag void __iomem *reg = reg_base + reg_offs[i]; 60cfe238e4SDavid Virag u32 val; 61cfe238e4SDavid Virag 62cfe238e4SDavid Virag /* Modify only gate clock registers */ 63cfe238e4SDavid Virag if (reg_offs[i] < GATE_OFF_START || reg_offs[i] > GATE_OFF_END) 64cfe238e4SDavid Virag continue; 65cfe238e4SDavid Virag 66cfe238e4SDavid Virag val = readl(reg); 67cfe238e4SDavid Virag val |= GATE_MANUAL; 68cfe238e4SDavid Virag val &= ~GATE_ENABLE_HWACG; 69cfe238e4SDavid Virag writel(val, reg); 70cfe238e4SDavid Virag } 71cfe238e4SDavid Virag 72cfe238e4SDavid Virag iounmap(reg_base); 73cfe238e4SDavid Virag } 74cfe238e4SDavid Virag 75cfe238e4SDavid Virag /** 76454e8d29SSam Protsenko * exynos_arm64_enable_bus_clk - Enable parent clock of specified CMU 77454e8d29SSam Protsenko * 78454e8d29SSam Protsenko * @dev: Device object; may be NULL if this function is not being 79454e8d29SSam Protsenko * called from platform driver probe function 80454e8d29SSam Protsenko * @np: CMU device tree node 81454e8d29SSam Protsenko * @cmu: CMU data 82454e8d29SSam Protsenko * 83454e8d29SSam Protsenko * Keep CMU parent clock running (needed for CMU registers access). 84454e8d29SSam Protsenko * 85454e8d29SSam Protsenko * Return: 0 on success or a negative error code on failure. 86454e8d29SSam Protsenko */ 87454e8d29SSam Protsenko static int __init exynos_arm64_enable_bus_clk(struct device *dev, 88454e8d29SSam Protsenko struct device_node *np, const struct samsung_cmu_info *cmu) 89454e8d29SSam Protsenko { 90454e8d29SSam Protsenko struct clk *parent_clk; 91454e8d29SSam Protsenko 92454e8d29SSam Protsenko if (!cmu->clk_name) 93454e8d29SSam Protsenko return 0; 94454e8d29SSam Protsenko 95*f05dc202SSam Protsenko if (dev) { 96*f05dc202SSam Protsenko struct exynos_arm64_cmu_data *data; 97*f05dc202SSam Protsenko 98454e8d29SSam Protsenko parent_clk = clk_get(dev, cmu->clk_name); 99*f05dc202SSam Protsenko data = dev_get_drvdata(dev); 100*f05dc202SSam Protsenko if (data) 101*f05dc202SSam Protsenko data->clk = parent_clk; 102*f05dc202SSam Protsenko } else { 103454e8d29SSam Protsenko parent_clk = of_clk_get_by_name(np, cmu->clk_name); 104*f05dc202SSam Protsenko } 105454e8d29SSam Protsenko 106454e8d29SSam Protsenko if (IS_ERR(parent_clk)) 107454e8d29SSam Protsenko return PTR_ERR(parent_clk); 108454e8d29SSam Protsenko 109454e8d29SSam Protsenko return clk_prepare_enable(parent_clk); 110454e8d29SSam Protsenko } 111454e8d29SSam Protsenko 112*f05dc202SSam Protsenko static int __init exynos_arm64_cmu_prepare_pm(struct device *dev, 113*f05dc202SSam Protsenko const struct samsung_cmu_info *cmu) 114*f05dc202SSam Protsenko { 115*f05dc202SSam Protsenko struct exynos_arm64_cmu_data *data = dev_get_drvdata(dev); 116*f05dc202SSam Protsenko int i; 117*f05dc202SSam Protsenko 118*f05dc202SSam Protsenko data->clk_save = samsung_clk_alloc_reg_dump(cmu->clk_regs, 119*f05dc202SSam Protsenko cmu->nr_clk_regs); 120*f05dc202SSam Protsenko if (!data->clk_save) 121*f05dc202SSam Protsenko return -ENOMEM; 122*f05dc202SSam Protsenko 123*f05dc202SSam Protsenko data->nr_clk_save = cmu->nr_clk_regs; 124*f05dc202SSam Protsenko data->clk_suspend = cmu->suspend_regs; 125*f05dc202SSam Protsenko data->nr_clk_suspend = cmu->nr_suspend_regs; 126*f05dc202SSam Protsenko data->nr_pclks = of_clk_get_parent_count(dev->of_node); 127*f05dc202SSam Protsenko if (!data->nr_pclks) 128*f05dc202SSam Protsenko return 0; 129*f05dc202SSam Protsenko 130*f05dc202SSam Protsenko data->pclks = devm_kcalloc(dev, sizeof(struct clk *), data->nr_pclks, 131*f05dc202SSam Protsenko GFP_KERNEL); 132*f05dc202SSam Protsenko if (!data->pclks) { 133*f05dc202SSam Protsenko kfree(data->clk_save); 134*f05dc202SSam Protsenko return -ENOMEM; 135*f05dc202SSam Protsenko } 136*f05dc202SSam Protsenko 137*f05dc202SSam Protsenko for (i = 0; i < data->nr_pclks; i++) { 138*f05dc202SSam Protsenko struct clk *clk = of_clk_get(dev->of_node, i); 139*f05dc202SSam Protsenko 140*f05dc202SSam Protsenko if (IS_ERR(clk)) { 141*f05dc202SSam Protsenko kfree(data->clk_save); 142*f05dc202SSam Protsenko while (--i >= 0) 143*f05dc202SSam Protsenko clk_put(data->pclks[i]); 144*f05dc202SSam Protsenko return PTR_ERR(clk); 145*f05dc202SSam Protsenko } 146*f05dc202SSam Protsenko data->pclks[i] = clk; 147*f05dc202SSam Protsenko } 148*f05dc202SSam Protsenko 149*f05dc202SSam Protsenko return 0; 150*f05dc202SSam Protsenko } 151*f05dc202SSam Protsenko 152454e8d29SSam Protsenko /** 153cfe238e4SDavid Virag * exynos_arm64_register_cmu - Register specified Exynos CMU domain 154cfe238e4SDavid Virag * @dev: Device object; may be NULL if this function is not being 155cfe238e4SDavid Virag * called from platform driver probe function 156cfe238e4SDavid Virag * @np: CMU device tree node 157cfe238e4SDavid Virag * @cmu: CMU data 158cfe238e4SDavid Virag * 159cfe238e4SDavid Virag * Register specified CMU domain, which includes next steps: 160cfe238e4SDavid Virag * 161cfe238e4SDavid Virag * 1. Enable parent clock of @cmu CMU 162cfe238e4SDavid Virag * 2. Set initial registers configuration for @cmu CMU clocks 163cfe238e4SDavid Virag * 3. Register @cmu CMU clocks using Samsung clock framework API 164cfe238e4SDavid Virag */ 165cfe238e4SDavid Virag void __init exynos_arm64_register_cmu(struct device *dev, 166cfe238e4SDavid Virag struct device_node *np, const struct samsung_cmu_info *cmu) 167cfe238e4SDavid Virag { 168454e8d29SSam Protsenko int err; 169cfe238e4SDavid Virag 170454e8d29SSam Protsenko /* 171454e8d29SSam Protsenko * Try to boot even if the parent clock enablement fails, as it might be 172454e8d29SSam Protsenko * already enabled by bootloader. 173454e8d29SSam Protsenko */ 174454e8d29SSam Protsenko err = exynos_arm64_enable_bus_clk(dev, np, cmu); 175454e8d29SSam Protsenko if (err) 176454e8d29SSam Protsenko pr_err("%s: could not enable bus clock %s; err = %d\n", 177454e8d29SSam Protsenko __func__, cmu->clk_name, err); 178cfe238e4SDavid Virag 179cfe238e4SDavid Virag exynos_arm64_init_clocks(np, cmu->clk_regs, cmu->nr_clk_regs); 180cfe238e4SDavid Virag samsung_cmu_register_one(np, cmu); 181cfe238e4SDavid Virag } 182*f05dc202SSam Protsenko 183*f05dc202SSam Protsenko /** 184*f05dc202SSam Protsenko * exynos_arm64_register_cmu_pm - Register Exynos CMU domain with PM support 185*f05dc202SSam Protsenko * 186*f05dc202SSam Protsenko * @pdev: Platform device object 187*f05dc202SSam Protsenko * @set_manual: If true, set gate clocks to manual mode 188*f05dc202SSam Protsenko * 189*f05dc202SSam Protsenko * It's a version of exynos_arm64_register_cmu() with PM support. Should be 190*f05dc202SSam Protsenko * called from probe function of platform driver. 191*f05dc202SSam Protsenko * 192*f05dc202SSam Protsenko * Return: 0 on success, or negative error code on error. 193*f05dc202SSam Protsenko */ 194*f05dc202SSam Protsenko int __init exynos_arm64_register_cmu_pm(struct platform_device *pdev, 195*f05dc202SSam Protsenko bool set_manual) 196*f05dc202SSam Protsenko { 197*f05dc202SSam Protsenko const struct samsung_cmu_info *cmu; 198*f05dc202SSam Protsenko struct device *dev = &pdev->dev; 199*f05dc202SSam Protsenko struct device_node *np = dev->of_node; 200*f05dc202SSam Protsenko struct exynos_arm64_cmu_data *data; 201*f05dc202SSam Protsenko void __iomem *reg_base; 202*f05dc202SSam Protsenko int ret; 203*f05dc202SSam Protsenko 204*f05dc202SSam Protsenko cmu = of_device_get_match_data(dev); 205*f05dc202SSam Protsenko 206*f05dc202SSam Protsenko data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 207*f05dc202SSam Protsenko if (!data) 208*f05dc202SSam Protsenko return -ENOMEM; 209*f05dc202SSam Protsenko 210*f05dc202SSam Protsenko platform_set_drvdata(pdev, data); 211*f05dc202SSam Protsenko 212*f05dc202SSam Protsenko ret = exynos_arm64_cmu_prepare_pm(dev, cmu); 213*f05dc202SSam Protsenko if (ret) 214*f05dc202SSam Protsenko return ret; 215*f05dc202SSam Protsenko 216*f05dc202SSam Protsenko /* 217*f05dc202SSam Protsenko * Try to boot even if the parent clock enablement fails, as it might be 218*f05dc202SSam Protsenko * already enabled by bootloader. 219*f05dc202SSam Protsenko */ 220*f05dc202SSam Protsenko ret = exynos_arm64_enable_bus_clk(dev, NULL, cmu); 221*f05dc202SSam Protsenko if (ret) 222*f05dc202SSam Protsenko dev_err(dev, "%s: could not enable bus clock %s; err = %d\n", 223*f05dc202SSam Protsenko __func__, cmu->clk_name, ret); 224*f05dc202SSam Protsenko 225*f05dc202SSam Protsenko if (set_manual) 226*f05dc202SSam Protsenko exynos_arm64_init_clocks(np, cmu->clk_regs, cmu->nr_clk_regs); 227*f05dc202SSam Protsenko 228*f05dc202SSam Protsenko reg_base = devm_platform_ioremap_resource(pdev, 0); 229*f05dc202SSam Protsenko if (IS_ERR(reg_base)) 230*f05dc202SSam Protsenko return PTR_ERR(reg_base); 231*f05dc202SSam Protsenko 232*f05dc202SSam Protsenko data->ctx = samsung_clk_init(dev, reg_base, cmu->nr_clk_ids); 233*f05dc202SSam Protsenko 234*f05dc202SSam Protsenko /* 235*f05dc202SSam Protsenko * Enable runtime PM here to allow the clock core using runtime PM 236*f05dc202SSam Protsenko * for the registered clocks. Additionally, we increase the runtime 237*f05dc202SSam Protsenko * PM usage count before registering the clocks, to prevent the 238*f05dc202SSam Protsenko * clock core from runtime suspending the device. 239*f05dc202SSam Protsenko */ 240*f05dc202SSam Protsenko pm_runtime_get_noresume(dev); 241*f05dc202SSam Protsenko pm_runtime_set_active(dev); 242*f05dc202SSam Protsenko pm_runtime_enable(dev); 243*f05dc202SSam Protsenko 244*f05dc202SSam Protsenko samsung_cmu_register_clocks(data->ctx, cmu); 245*f05dc202SSam Protsenko samsung_clk_of_add_provider(dev->of_node, data->ctx); 246*f05dc202SSam Protsenko pm_runtime_put_sync(dev); 247*f05dc202SSam Protsenko 248*f05dc202SSam Protsenko return 0; 249*f05dc202SSam Protsenko } 250*f05dc202SSam Protsenko 251*f05dc202SSam Protsenko int exynos_arm64_cmu_suspend(struct device *dev) 252*f05dc202SSam Protsenko { 253*f05dc202SSam Protsenko struct exynos_arm64_cmu_data *data = dev_get_drvdata(dev); 254*f05dc202SSam Protsenko int i; 255*f05dc202SSam Protsenko 256*f05dc202SSam Protsenko samsung_clk_save(data->ctx->reg_base, data->clk_save, 257*f05dc202SSam Protsenko data->nr_clk_save); 258*f05dc202SSam Protsenko 259*f05dc202SSam Protsenko for (i = 0; i < data->nr_pclks; i++) 260*f05dc202SSam Protsenko clk_prepare_enable(data->pclks[i]); 261*f05dc202SSam Protsenko 262*f05dc202SSam Protsenko /* For suspend some registers have to be set to certain values */ 263*f05dc202SSam Protsenko samsung_clk_restore(data->ctx->reg_base, data->clk_suspend, 264*f05dc202SSam Protsenko data->nr_clk_suspend); 265*f05dc202SSam Protsenko 266*f05dc202SSam Protsenko for (i = 0; i < data->nr_pclks; i++) 267*f05dc202SSam Protsenko clk_disable_unprepare(data->pclks[i]); 268*f05dc202SSam Protsenko 269*f05dc202SSam Protsenko clk_disable_unprepare(data->clk); 270*f05dc202SSam Protsenko 271*f05dc202SSam Protsenko return 0; 272*f05dc202SSam Protsenko } 273*f05dc202SSam Protsenko 274*f05dc202SSam Protsenko int exynos_arm64_cmu_resume(struct device *dev) 275*f05dc202SSam Protsenko { 276*f05dc202SSam Protsenko struct exynos_arm64_cmu_data *data = dev_get_drvdata(dev); 277*f05dc202SSam Protsenko int i; 278*f05dc202SSam Protsenko 279*f05dc202SSam Protsenko clk_prepare_enable(data->clk); 280*f05dc202SSam Protsenko 281*f05dc202SSam Protsenko for (i = 0; i < data->nr_pclks; i++) 282*f05dc202SSam Protsenko clk_prepare_enable(data->pclks[i]); 283*f05dc202SSam Protsenko 284*f05dc202SSam Protsenko samsung_clk_restore(data->ctx->reg_base, data->clk_save, 285*f05dc202SSam Protsenko data->nr_clk_save); 286*f05dc202SSam Protsenko 287*f05dc202SSam Protsenko for (i = 0; i < data->nr_pclks; i++) 288*f05dc202SSam Protsenko clk_disable_unprepare(data->pclks[i]); 289*f05dc202SSam Protsenko 290*f05dc202SSam Protsenko return 0; 291*f05dc202SSam Protsenko } 292