18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2013, The Linux Foundation. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/kernel.h> 78c2ecf20Sopenharmony_ci#include <linux/bitops.h> 88c2ecf20Sopenharmony_ci#include <linux/err.h> 98c2ecf20Sopenharmony_ci#include <linux/delay.h> 108c2ecf20Sopenharmony_ci#include <linux/export.h> 118c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 128c2ecf20Sopenharmony_ci#include <linux/regmap.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "clk-branch.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic bool clk_branch_in_hwcg_mode(const struct clk_branch *br) 178c2ecf20Sopenharmony_ci{ 188c2ecf20Sopenharmony_ci u32 val; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci if (!br->hwcg_reg) 218c2ecf20Sopenharmony_ci return false; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci regmap_read(br->clkr.regmap, br->hwcg_reg, &val); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci return !!(val & BIT(br->hwcg_bit)); 268c2ecf20Sopenharmony_ci} 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic bool clk_branch_check_halt(const struct clk_branch *br, bool enabling) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci bool invert = (br->halt_check == BRANCH_HALT_ENABLE); 318c2ecf20Sopenharmony_ci u32 val; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci regmap_read(br->clkr.regmap, br->halt_reg, &val); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci val &= BIT(br->halt_bit); 368c2ecf20Sopenharmony_ci if (invert) 378c2ecf20Sopenharmony_ci val = !val; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci return !!val == !enabling; 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define BRANCH_CLK_OFF BIT(31) 438c2ecf20Sopenharmony_ci#define BRANCH_NOC_FSM_STATUS_SHIFT 28 448c2ecf20Sopenharmony_ci#define BRANCH_NOC_FSM_STATUS_MASK 0x7 458c2ecf20Sopenharmony_ci#define BRANCH_NOC_FSM_STATUS_ON (0x2 << BRANCH_NOC_FSM_STATUS_SHIFT) 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic bool clk_branch2_check_halt(const struct clk_branch *br, bool enabling) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci u32 val; 508c2ecf20Sopenharmony_ci u32 mask; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci mask = BRANCH_NOC_FSM_STATUS_MASK << BRANCH_NOC_FSM_STATUS_SHIFT; 538c2ecf20Sopenharmony_ci mask |= BRANCH_CLK_OFF; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci regmap_read(br->clkr.regmap, br->halt_reg, &val); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci if (enabling) { 588c2ecf20Sopenharmony_ci val &= mask; 598c2ecf20Sopenharmony_ci return (val & BRANCH_CLK_OFF) == 0 || 608c2ecf20Sopenharmony_ci val == BRANCH_NOC_FSM_STATUS_ON; 618c2ecf20Sopenharmony_ci } else { 628c2ecf20Sopenharmony_ci return val & BRANCH_CLK_OFF; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int clk_branch_wait(const struct clk_branch *br, bool enabling, 678c2ecf20Sopenharmony_ci bool (check_halt)(const struct clk_branch *, bool)) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci bool voted = br->halt_check & BRANCH_VOTED; 708c2ecf20Sopenharmony_ci const char *name = clk_hw_get_name(&br->clkr.hw); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* 738c2ecf20Sopenharmony_ci * Skip checking halt bit if we're explicitly ignoring the bit or the 748c2ecf20Sopenharmony_ci * clock is in hardware gated mode 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_ci if (br->halt_check == BRANCH_HALT_SKIP || clk_branch_in_hwcg_mode(br)) 778c2ecf20Sopenharmony_ci return 0; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (br->halt_check == BRANCH_HALT_DELAY || (!enabling && voted)) { 808c2ecf20Sopenharmony_ci udelay(10); 818c2ecf20Sopenharmony_ci } else if (br->halt_check == BRANCH_HALT_ENABLE || 828c2ecf20Sopenharmony_ci br->halt_check == BRANCH_HALT || 838c2ecf20Sopenharmony_ci (enabling && voted)) { 848c2ecf20Sopenharmony_ci int count = 200; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci while (count-- > 0) { 878c2ecf20Sopenharmony_ci if (check_halt(br, enabling)) 888c2ecf20Sopenharmony_ci return 0; 898c2ecf20Sopenharmony_ci udelay(1); 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci WARN(1, "%s status stuck at 'o%s'", name, 928c2ecf20Sopenharmony_ci enabling ? "ff" : "n"); 938c2ecf20Sopenharmony_ci return -EBUSY; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci return 0; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int clk_branch_toggle(struct clk_hw *hw, bool en, 998c2ecf20Sopenharmony_ci bool (check_halt)(const struct clk_branch *, bool)) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct clk_branch *br = to_clk_branch(hw); 1028c2ecf20Sopenharmony_ci int ret; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (en) { 1058c2ecf20Sopenharmony_ci ret = clk_enable_regmap(hw); 1068c2ecf20Sopenharmony_ci if (ret) 1078c2ecf20Sopenharmony_ci return ret; 1088c2ecf20Sopenharmony_ci } else { 1098c2ecf20Sopenharmony_ci clk_disable_regmap(hw); 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return clk_branch_wait(br, en, check_halt); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic int clk_branch_enable(struct clk_hw *hw) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci return clk_branch_toggle(hw, true, clk_branch_check_halt); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic void clk_branch_disable(struct clk_hw *hw) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci clk_branch_toggle(hw, false, clk_branch_check_halt); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ciconst struct clk_ops clk_branch_ops = { 1268c2ecf20Sopenharmony_ci .enable = clk_branch_enable, 1278c2ecf20Sopenharmony_ci .disable = clk_branch_disable, 1288c2ecf20Sopenharmony_ci .is_enabled = clk_is_enabled_regmap, 1298c2ecf20Sopenharmony_ci}; 1308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_branch_ops); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic int clk_branch2_enable(struct clk_hw *hw) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci return clk_branch_toggle(hw, true, clk_branch2_check_halt); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic void clk_branch2_disable(struct clk_hw *hw) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci clk_branch_toggle(hw, false, clk_branch2_check_halt); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ciconst struct clk_ops clk_branch2_ops = { 1438c2ecf20Sopenharmony_ci .enable = clk_branch2_enable, 1448c2ecf20Sopenharmony_ci .disable = clk_branch2_disable, 1458c2ecf20Sopenharmony_ci .is_enabled = clk_is_enabled_regmap, 1468c2ecf20Sopenharmony_ci}; 1478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_branch2_ops); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ciconst struct clk_ops clk_branch2_aon_ops = { 1508c2ecf20Sopenharmony_ci .enable = clk_branch2_enable, 1518c2ecf20Sopenharmony_ci .is_enabled = clk_is_enabled_regmap, 1528c2ecf20Sopenharmony_ci}; 1538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_branch2_aon_ops); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ciconst struct clk_ops clk_branch_simple_ops = { 1568c2ecf20Sopenharmony_ci .enable = clk_enable_regmap, 1578c2ecf20Sopenharmony_ci .disable = clk_disable_regmap, 1588c2ecf20Sopenharmony_ci .is_enabled = clk_is_enabled_regmap, 1598c2ecf20Sopenharmony_ci}; 1608c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_branch_simple_ops); 161