1d058fd9eSRahul Tanwar // SPDX-License-Identifier: GPL-2.0 2d058fd9eSRahul Tanwar /* 303617731SRahul Tanwar * Copyright (C) 2020-2022 MaxLinear, Inc. 4d058fd9eSRahul Tanwar * Copyright (C) 2020 Intel Corporation. 503617731SRahul Tanwar * Zhu Yixin <yzhu@maxlinear.com> 603617731SRahul Tanwar * Rahul Tanwar <rtanwar@maxlinear.com> 7d058fd9eSRahul Tanwar */ 8d058fd9eSRahul Tanwar #include <linux/clk-provider.h> 9d058fd9eSRahul Tanwar #include <linux/device.h> 10d058fd9eSRahul Tanwar #include <linux/of.h> 11d058fd9eSRahul Tanwar 12d058fd9eSRahul Tanwar #include "clk-cgu.h" 13d058fd9eSRahul Tanwar 14d058fd9eSRahul Tanwar #define GATE_HW_REG_STAT(reg) ((reg) + 0x0) 15d058fd9eSRahul Tanwar #define GATE_HW_REG_EN(reg) ((reg) + 0x4) 16d058fd9eSRahul Tanwar #define GATE_HW_REG_DIS(reg) ((reg) + 0x8) 17d058fd9eSRahul Tanwar #define MAX_DDIV_REG 8 18d058fd9eSRahul Tanwar #define MAX_DIVIDER_VAL 64 19d058fd9eSRahul Tanwar 20d058fd9eSRahul Tanwar #define to_lgm_clk_mux(_hw) container_of(_hw, struct lgm_clk_mux, hw) 21d058fd9eSRahul Tanwar #define to_lgm_clk_divider(_hw) container_of(_hw, struct lgm_clk_divider, hw) 22d058fd9eSRahul Tanwar #define to_lgm_clk_gate(_hw) container_of(_hw, struct lgm_clk_gate, hw) 23d058fd9eSRahul Tanwar #define to_lgm_clk_ddiv(_hw) container_of(_hw, struct lgm_clk_ddiv, hw) 24d058fd9eSRahul Tanwar 25d058fd9eSRahul Tanwar static struct clk_hw *lgm_clk_register_fixed(struct lgm_clk_provider *ctx, 26d058fd9eSRahul Tanwar const struct lgm_clk_branch *list) 27d058fd9eSRahul Tanwar { 28d058fd9eSRahul Tanwar 29eaabee88SRahul Tanwar if (list->div_flags & CLOCK_FLAG_VAL_INIT) 30d058fd9eSRahul Tanwar lgm_set_clk_val(ctx->membase, list->div_off, list->div_shift, 31d058fd9eSRahul Tanwar list->div_width, list->div_val); 32d058fd9eSRahul Tanwar 33d058fd9eSRahul Tanwar return clk_hw_register_fixed_rate(NULL, list->name, 34d058fd9eSRahul Tanwar list->parent_data[0].name, 35d058fd9eSRahul Tanwar list->flags, list->mux_flags); 36d058fd9eSRahul Tanwar } 37d058fd9eSRahul Tanwar 38d058fd9eSRahul Tanwar static u8 lgm_clk_mux_get_parent(struct clk_hw *hw) 39d058fd9eSRahul Tanwar { 40d058fd9eSRahul Tanwar struct lgm_clk_mux *mux = to_lgm_clk_mux(hw); 41d058fd9eSRahul Tanwar u32 val; 42d058fd9eSRahul Tanwar 43d058fd9eSRahul Tanwar if (mux->flags & MUX_CLK_SW) 44d058fd9eSRahul Tanwar val = mux->reg; 45d058fd9eSRahul Tanwar else 46d058fd9eSRahul Tanwar val = lgm_get_clk_val(mux->membase, mux->reg, mux->shift, 47d058fd9eSRahul Tanwar mux->width); 48d058fd9eSRahul Tanwar return clk_mux_val_to_index(hw, NULL, mux->flags, val); 49d058fd9eSRahul Tanwar } 50d058fd9eSRahul Tanwar 51d058fd9eSRahul Tanwar static int lgm_clk_mux_set_parent(struct clk_hw *hw, u8 index) 52d058fd9eSRahul Tanwar { 53d058fd9eSRahul Tanwar struct lgm_clk_mux *mux = to_lgm_clk_mux(hw); 54d058fd9eSRahul Tanwar u32 val; 55d058fd9eSRahul Tanwar 56d058fd9eSRahul Tanwar val = clk_mux_index_to_val(NULL, mux->flags, index); 57d058fd9eSRahul Tanwar if (mux->flags & MUX_CLK_SW) 58d058fd9eSRahul Tanwar mux->reg = val; 59d058fd9eSRahul Tanwar else 60d058fd9eSRahul Tanwar lgm_set_clk_val(mux->membase, mux->reg, mux->shift, 61d058fd9eSRahul Tanwar mux->width, val); 62d058fd9eSRahul Tanwar 63d058fd9eSRahul Tanwar return 0; 64d058fd9eSRahul Tanwar } 65d058fd9eSRahul Tanwar 66d058fd9eSRahul Tanwar static int lgm_clk_mux_determine_rate(struct clk_hw *hw, 67d058fd9eSRahul Tanwar struct clk_rate_request *req) 68d058fd9eSRahul Tanwar { 69d058fd9eSRahul Tanwar struct lgm_clk_mux *mux = to_lgm_clk_mux(hw); 70d058fd9eSRahul Tanwar 71d058fd9eSRahul Tanwar return clk_mux_determine_rate_flags(hw, req, mux->flags); 72d058fd9eSRahul Tanwar } 73d058fd9eSRahul Tanwar 74d058fd9eSRahul Tanwar static const struct clk_ops lgm_clk_mux_ops = { 75d058fd9eSRahul Tanwar .get_parent = lgm_clk_mux_get_parent, 76d058fd9eSRahul Tanwar .set_parent = lgm_clk_mux_set_parent, 77d058fd9eSRahul Tanwar .determine_rate = lgm_clk_mux_determine_rate, 78d058fd9eSRahul Tanwar }; 79d058fd9eSRahul Tanwar 80d058fd9eSRahul Tanwar static struct clk_hw * 81d058fd9eSRahul Tanwar lgm_clk_register_mux(struct lgm_clk_provider *ctx, 82d058fd9eSRahul Tanwar const struct lgm_clk_branch *list) 83d058fd9eSRahul Tanwar { 84eaabee88SRahul Tanwar unsigned long cflags = list->mux_flags; 85d058fd9eSRahul Tanwar struct device *dev = ctx->dev; 86d058fd9eSRahul Tanwar u8 shift = list->mux_shift; 87d058fd9eSRahul Tanwar u8 width = list->mux_width; 88d058fd9eSRahul Tanwar struct clk_init_data init = {}; 89d058fd9eSRahul Tanwar struct lgm_clk_mux *mux; 90d058fd9eSRahul Tanwar u32 reg = list->mux_off; 91d058fd9eSRahul Tanwar struct clk_hw *hw; 92d058fd9eSRahul Tanwar int ret; 93d058fd9eSRahul Tanwar 94d058fd9eSRahul Tanwar mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); 95d058fd9eSRahul Tanwar if (!mux) 96d058fd9eSRahul Tanwar return ERR_PTR(-ENOMEM); 97d058fd9eSRahul Tanwar 98d058fd9eSRahul Tanwar init.name = list->name; 99d058fd9eSRahul Tanwar init.ops = &lgm_clk_mux_ops; 100d058fd9eSRahul Tanwar init.flags = list->flags; 101d058fd9eSRahul Tanwar init.parent_data = list->parent_data; 102d058fd9eSRahul Tanwar init.num_parents = list->num_parents; 103d058fd9eSRahul Tanwar 104d058fd9eSRahul Tanwar mux->membase = ctx->membase; 105d058fd9eSRahul Tanwar mux->reg = reg; 106d058fd9eSRahul Tanwar mux->shift = shift; 107d058fd9eSRahul Tanwar mux->width = width; 108d058fd9eSRahul Tanwar mux->flags = cflags; 109d058fd9eSRahul Tanwar mux->hw.init = &init; 110d058fd9eSRahul Tanwar 111d058fd9eSRahul Tanwar hw = &mux->hw; 1128529fc0aSRahul Tanwar ret = devm_clk_hw_register(dev, hw); 113d058fd9eSRahul Tanwar if (ret) 114d058fd9eSRahul Tanwar return ERR_PTR(ret); 115d058fd9eSRahul Tanwar 116eaabee88SRahul Tanwar if (cflags & CLOCK_FLAG_VAL_INIT) 117d058fd9eSRahul Tanwar lgm_set_clk_val(mux->membase, reg, shift, width, list->mux_val); 118d058fd9eSRahul Tanwar 119d058fd9eSRahul Tanwar return hw; 120d058fd9eSRahul Tanwar } 121d058fd9eSRahul Tanwar 122d058fd9eSRahul Tanwar static unsigned long 123d058fd9eSRahul Tanwar lgm_clk_divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) 124d058fd9eSRahul Tanwar { 125d058fd9eSRahul Tanwar struct lgm_clk_divider *divider = to_lgm_clk_divider(hw); 126d058fd9eSRahul Tanwar unsigned int val; 127d058fd9eSRahul Tanwar 128d058fd9eSRahul Tanwar val = lgm_get_clk_val(divider->membase, divider->reg, 129d058fd9eSRahul Tanwar divider->shift, divider->width); 130d058fd9eSRahul Tanwar 131d058fd9eSRahul Tanwar return divider_recalc_rate(hw, parent_rate, val, divider->table, 132d058fd9eSRahul Tanwar divider->flags, divider->width); 133d058fd9eSRahul Tanwar } 134d058fd9eSRahul Tanwar 135d058fd9eSRahul Tanwar static long 136d058fd9eSRahul Tanwar lgm_clk_divider_round_rate(struct clk_hw *hw, unsigned long rate, 137d058fd9eSRahul Tanwar unsigned long *prate) 138d058fd9eSRahul Tanwar { 139d058fd9eSRahul Tanwar struct lgm_clk_divider *divider = to_lgm_clk_divider(hw); 140d058fd9eSRahul Tanwar 141d058fd9eSRahul Tanwar return divider_round_rate(hw, rate, prate, divider->table, 142d058fd9eSRahul Tanwar divider->width, divider->flags); 143d058fd9eSRahul Tanwar } 144d058fd9eSRahul Tanwar 145d058fd9eSRahul Tanwar static int 146d058fd9eSRahul Tanwar lgm_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, 147d058fd9eSRahul Tanwar unsigned long prate) 148d058fd9eSRahul Tanwar { 149d058fd9eSRahul Tanwar struct lgm_clk_divider *divider = to_lgm_clk_divider(hw); 150d058fd9eSRahul Tanwar int value; 151d058fd9eSRahul Tanwar 152d058fd9eSRahul Tanwar value = divider_get_val(rate, prate, divider->table, 153d058fd9eSRahul Tanwar divider->width, divider->flags); 154d058fd9eSRahul Tanwar if (value < 0) 155d058fd9eSRahul Tanwar return value; 156d058fd9eSRahul Tanwar 157d058fd9eSRahul Tanwar lgm_set_clk_val(divider->membase, divider->reg, 158d058fd9eSRahul Tanwar divider->shift, divider->width, value); 159d058fd9eSRahul Tanwar 160d058fd9eSRahul Tanwar return 0; 161d058fd9eSRahul Tanwar } 162d058fd9eSRahul Tanwar 163d058fd9eSRahul Tanwar static int lgm_clk_divider_enable_disable(struct clk_hw *hw, int enable) 164d058fd9eSRahul Tanwar { 165d058fd9eSRahul Tanwar struct lgm_clk_divider *div = to_lgm_clk_divider(hw); 166d058fd9eSRahul Tanwar 167d058fd9eSRahul Tanwar lgm_set_clk_val(div->membase, div->reg, div->shift_gate, 168d058fd9eSRahul Tanwar div->width_gate, enable); 169d058fd9eSRahul Tanwar return 0; 170d058fd9eSRahul Tanwar } 171d058fd9eSRahul Tanwar 172d058fd9eSRahul Tanwar static int lgm_clk_divider_enable(struct clk_hw *hw) 173d058fd9eSRahul Tanwar { 174d058fd9eSRahul Tanwar return lgm_clk_divider_enable_disable(hw, 1); 175d058fd9eSRahul Tanwar } 176d058fd9eSRahul Tanwar 177d058fd9eSRahul Tanwar static void lgm_clk_divider_disable(struct clk_hw *hw) 178d058fd9eSRahul Tanwar { 179d058fd9eSRahul Tanwar lgm_clk_divider_enable_disable(hw, 0); 180d058fd9eSRahul Tanwar } 181d058fd9eSRahul Tanwar 182d058fd9eSRahul Tanwar static const struct clk_ops lgm_clk_divider_ops = { 183d058fd9eSRahul Tanwar .recalc_rate = lgm_clk_divider_recalc_rate, 184d058fd9eSRahul Tanwar .round_rate = lgm_clk_divider_round_rate, 185d058fd9eSRahul Tanwar .set_rate = lgm_clk_divider_set_rate, 186d058fd9eSRahul Tanwar .enable = lgm_clk_divider_enable, 187d058fd9eSRahul Tanwar .disable = lgm_clk_divider_disable, 188d058fd9eSRahul Tanwar }; 189d058fd9eSRahul Tanwar 190d058fd9eSRahul Tanwar static struct clk_hw * 191d058fd9eSRahul Tanwar lgm_clk_register_divider(struct lgm_clk_provider *ctx, 192d058fd9eSRahul Tanwar const struct lgm_clk_branch *list) 193d058fd9eSRahul Tanwar { 194eaabee88SRahul Tanwar unsigned long cflags = list->div_flags; 195d058fd9eSRahul Tanwar struct device *dev = ctx->dev; 196d058fd9eSRahul Tanwar struct lgm_clk_divider *div; 197d058fd9eSRahul Tanwar struct clk_init_data init = {}; 198d058fd9eSRahul Tanwar u8 shift = list->div_shift; 199d058fd9eSRahul Tanwar u8 width = list->div_width; 200d058fd9eSRahul Tanwar u8 shift_gate = list->div_shift_gate; 201d058fd9eSRahul Tanwar u8 width_gate = list->div_width_gate; 202d058fd9eSRahul Tanwar u32 reg = list->div_off; 203d058fd9eSRahul Tanwar struct clk_hw *hw; 204d058fd9eSRahul Tanwar int ret; 205d058fd9eSRahul Tanwar 206d058fd9eSRahul Tanwar div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL); 207d058fd9eSRahul Tanwar if (!div) 208d058fd9eSRahul Tanwar return ERR_PTR(-ENOMEM); 209d058fd9eSRahul Tanwar 210d058fd9eSRahul Tanwar init.name = list->name; 211d058fd9eSRahul Tanwar init.ops = &lgm_clk_divider_ops; 212d058fd9eSRahul Tanwar init.flags = list->flags; 213d058fd9eSRahul Tanwar init.parent_data = list->parent_data; 214d058fd9eSRahul Tanwar init.num_parents = 1; 215d058fd9eSRahul Tanwar 216d058fd9eSRahul Tanwar div->membase = ctx->membase; 217d058fd9eSRahul Tanwar div->reg = reg; 218d058fd9eSRahul Tanwar div->shift = shift; 219d058fd9eSRahul Tanwar div->width = width; 220d058fd9eSRahul Tanwar div->shift_gate = shift_gate; 221d058fd9eSRahul Tanwar div->width_gate = width_gate; 222d058fd9eSRahul Tanwar div->flags = cflags; 223d058fd9eSRahul Tanwar div->table = list->div_table; 224d058fd9eSRahul Tanwar div->hw.init = &init; 225d058fd9eSRahul Tanwar 226d058fd9eSRahul Tanwar hw = &div->hw; 2278529fc0aSRahul Tanwar ret = devm_clk_hw_register(dev, hw); 228d058fd9eSRahul Tanwar if (ret) 229d058fd9eSRahul Tanwar return ERR_PTR(ret); 230d058fd9eSRahul Tanwar 231eaabee88SRahul Tanwar if (cflags & CLOCK_FLAG_VAL_INIT) 232d058fd9eSRahul Tanwar lgm_set_clk_val(div->membase, reg, shift, width, list->div_val); 233d058fd9eSRahul Tanwar 234d058fd9eSRahul Tanwar return hw; 235d058fd9eSRahul Tanwar } 236d058fd9eSRahul Tanwar 237d058fd9eSRahul Tanwar static struct clk_hw * 238d058fd9eSRahul Tanwar lgm_clk_register_fixed_factor(struct lgm_clk_provider *ctx, 239d058fd9eSRahul Tanwar const struct lgm_clk_branch *list) 240d058fd9eSRahul Tanwar { 241d058fd9eSRahul Tanwar struct clk_hw *hw; 242d058fd9eSRahul Tanwar 243d058fd9eSRahul Tanwar hw = clk_hw_register_fixed_factor(ctx->dev, list->name, 244d058fd9eSRahul Tanwar list->parent_data[0].name, list->flags, 245d058fd9eSRahul Tanwar list->mult, list->div); 246d058fd9eSRahul Tanwar if (IS_ERR(hw)) 247d058fd9eSRahul Tanwar return ERR_CAST(hw); 248d058fd9eSRahul Tanwar 249eaabee88SRahul Tanwar if (list->div_flags & CLOCK_FLAG_VAL_INIT) 250d058fd9eSRahul Tanwar lgm_set_clk_val(ctx->membase, list->div_off, list->div_shift, 251d058fd9eSRahul Tanwar list->div_width, list->div_val); 252d058fd9eSRahul Tanwar 253d058fd9eSRahul Tanwar return hw; 254d058fd9eSRahul Tanwar } 255d058fd9eSRahul Tanwar 256d058fd9eSRahul Tanwar static int lgm_clk_gate_enable(struct clk_hw *hw) 257d058fd9eSRahul Tanwar { 258d058fd9eSRahul Tanwar struct lgm_clk_gate *gate = to_lgm_clk_gate(hw); 259d058fd9eSRahul Tanwar unsigned int reg; 260d058fd9eSRahul Tanwar 261d058fd9eSRahul Tanwar reg = GATE_HW_REG_EN(gate->reg); 262d058fd9eSRahul Tanwar lgm_set_clk_val(gate->membase, reg, gate->shift, 1, 1); 263d058fd9eSRahul Tanwar 264d058fd9eSRahul Tanwar return 0; 265d058fd9eSRahul Tanwar } 266d058fd9eSRahul Tanwar 267d058fd9eSRahul Tanwar static void lgm_clk_gate_disable(struct clk_hw *hw) 268d058fd9eSRahul Tanwar { 269d058fd9eSRahul Tanwar struct lgm_clk_gate *gate = to_lgm_clk_gate(hw); 270d058fd9eSRahul Tanwar unsigned int reg; 271d058fd9eSRahul Tanwar 272d058fd9eSRahul Tanwar reg = GATE_HW_REG_DIS(gate->reg); 273d058fd9eSRahul Tanwar lgm_set_clk_val(gate->membase, reg, gate->shift, 1, 1); 274d058fd9eSRahul Tanwar } 275d058fd9eSRahul Tanwar 276d058fd9eSRahul Tanwar static int lgm_clk_gate_is_enabled(struct clk_hw *hw) 277d058fd9eSRahul Tanwar { 278d058fd9eSRahul Tanwar struct lgm_clk_gate *gate = to_lgm_clk_gate(hw); 279d058fd9eSRahul Tanwar unsigned int reg, ret; 280d058fd9eSRahul Tanwar 281d058fd9eSRahul Tanwar reg = GATE_HW_REG_STAT(gate->reg); 282d058fd9eSRahul Tanwar ret = lgm_get_clk_val(gate->membase, reg, gate->shift, 1); 283d058fd9eSRahul Tanwar 284d058fd9eSRahul Tanwar return ret; 285d058fd9eSRahul Tanwar } 286d058fd9eSRahul Tanwar 287d058fd9eSRahul Tanwar static const struct clk_ops lgm_clk_gate_ops = { 288d058fd9eSRahul Tanwar .enable = lgm_clk_gate_enable, 289d058fd9eSRahul Tanwar .disable = lgm_clk_gate_disable, 290d058fd9eSRahul Tanwar .is_enabled = lgm_clk_gate_is_enabled, 291d058fd9eSRahul Tanwar }; 292d058fd9eSRahul Tanwar 293d058fd9eSRahul Tanwar static struct clk_hw * 294d058fd9eSRahul Tanwar lgm_clk_register_gate(struct lgm_clk_provider *ctx, 295d058fd9eSRahul Tanwar const struct lgm_clk_branch *list) 296d058fd9eSRahul Tanwar { 297eaabee88SRahul Tanwar unsigned long cflags = list->gate_flags; 298d058fd9eSRahul Tanwar const char *pname = list->parent_data[0].name; 299d058fd9eSRahul Tanwar struct device *dev = ctx->dev; 300d058fd9eSRahul Tanwar u8 shift = list->gate_shift; 301d058fd9eSRahul Tanwar struct clk_init_data init = {}; 302d058fd9eSRahul Tanwar struct lgm_clk_gate *gate; 303d058fd9eSRahul Tanwar u32 reg = list->gate_off; 304d058fd9eSRahul Tanwar struct clk_hw *hw; 305d058fd9eSRahul Tanwar int ret; 306d058fd9eSRahul Tanwar 307d058fd9eSRahul Tanwar gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL); 308d058fd9eSRahul Tanwar if (!gate) 309d058fd9eSRahul Tanwar return ERR_PTR(-ENOMEM); 310d058fd9eSRahul Tanwar 311d058fd9eSRahul Tanwar init.name = list->name; 312d058fd9eSRahul Tanwar init.ops = &lgm_clk_gate_ops; 313d058fd9eSRahul Tanwar init.flags = list->flags; 314d058fd9eSRahul Tanwar init.parent_names = pname ? &pname : NULL; 315d058fd9eSRahul Tanwar init.num_parents = pname ? 1 : 0; 316d058fd9eSRahul Tanwar 317d058fd9eSRahul Tanwar gate->membase = ctx->membase; 318d058fd9eSRahul Tanwar gate->reg = reg; 319d058fd9eSRahul Tanwar gate->shift = shift; 320d058fd9eSRahul Tanwar gate->flags = cflags; 321d058fd9eSRahul Tanwar gate->hw.init = &init; 322d058fd9eSRahul Tanwar 323d058fd9eSRahul Tanwar hw = &gate->hw; 3248529fc0aSRahul Tanwar ret = devm_clk_hw_register(dev, hw); 325d058fd9eSRahul Tanwar if (ret) 326d058fd9eSRahul Tanwar return ERR_PTR(ret); 327d058fd9eSRahul Tanwar 328d058fd9eSRahul Tanwar if (cflags & CLOCK_FLAG_VAL_INIT) { 329d058fd9eSRahul Tanwar lgm_set_clk_val(gate->membase, reg, shift, 1, list->gate_val); 330d058fd9eSRahul Tanwar } 331d058fd9eSRahul Tanwar 332d058fd9eSRahul Tanwar return hw; 333d058fd9eSRahul Tanwar } 334d058fd9eSRahul Tanwar 335d058fd9eSRahul Tanwar int lgm_clk_register_branches(struct lgm_clk_provider *ctx, 336d058fd9eSRahul Tanwar const struct lgm_clk_branch *list, 337d058fd9eSRahul Tanwar unsigned int nr_clk) 338d058fd9eSRahul Tanwar { 339d058fd9eSRahul Tanwar struct clk_hw *hw; 340d058fd9eSRahul Tanwar unsigned int idx; 341d058fd9eSRahul Tanwar 342d058fd9eSRahul Tanwar for (idx = 0; idx < nr_clk; idx++, list++) { 343d058fd9eSRahul Tanwar switch (list->type) { 344d058fd9eSRahul Tanwar case CLK_TYPE_FIXED: 345d058fd9eSRahul Tanwar hw = lgm_clk_register_fixed(ctx, list); 346d058fd9eSRahul Tanwar break; 347d058fd9eSRahul Tanwar case CLK_TYPE_MUX: 348d058fd9eSRahul Tanwar hw = lgm_clk_register_mux(ctx, list); 349d058fd9eSRahul Tanwar break; 350d058fd9eSRahul Tanwar case CLK_TYPE_DIVIDER: 351d058fd9eSRahul Tanwar hw = lgm_clk_register_divider(ctx, list); 352d058fd9eSRahul Tanwar break; 353d058fd9eSRahul Tanwar case CLK_TYPE_FIXED_FACTOR: 354d058fd9eSRahul Tanwar hw = lgm_clk_register_fixed_factor(ctx, list); 355d058fd9eSRahul Tanwar break; 356d058fd9eSRahul Tanwar case CLK_TYPE_GATE: 357*a5d49bd3SRahul Tanwar if (list->gate_flags & GATE_CLK_HW) { 358d058fd9eSRahul Tanwar hw = lgm_clk_register_gate(ctx, list); 359*a5d49bd3SRahul Tanwar } else { 360*a5d49bd3SRahul Tanwar /* 361*a5d49bd3SRahul Tanwar * GATE_CLKs can be controlled either from 362*a5d49bd3SRahul Tanwar * CGU clk driver i.e. this driver or directly 363*a5d49bd3SRahul Tanwar * from power management driver/daemon. It is 364*a5d49bd3SRahul Tanwar * dependent on the power policy/profile requirements 365*a5d49bd3SRahul Tanwar * of the end product. To override control of gate 366*a5d49bd3SRahul Tanwar * clks from this driver, provide NULL for this index 367*a5d49bd3SRahul Tanwar * of gate clk provider. 368*a5d49bd3SRahul Tanwar */ 369*a5d49bd3SRahul Tanwar hw = NULL; 370*a5d49bd3SRahul Tanwar } 371d058fd9eSRahul Tanwar break; 372*a5d49bd3SRahul Tanwar 373d058fd9eSRahul Tanwar default: 374d058fd9eSRahul Tanwar dev_err(ctx->dev, "invalid clk type\n"); 375d058fd9eSRahul Tanwar return -EINVAL; 376d058fd9eSRahul Tanwar } 377d058fd9eSRahul Tanwar 378d058fd9eSRahul Tanwar if (IS_ERR(hw)) { 379d058fd9eSRahul Tanwar dev_err(ctx->dev, 380d058fd9eSRahul Tanwar "register clk: %s, type: %u failed!\n", 381d058fd9eSRahul Tanwar list->name, list->type); 382d058fd9eSRahul Tanwar return -EIO; 383d058fd9eSRahul Tanwar } 384d058fd9eSRahul Tanwar ctx->clk_data.hws[list->id] = hw; 385d058fd9eSRahul Tanwar } 386d058fd9eSRahul Tanwar 387d058fd9eSRahul Tanwar return 0; 388d058fd9eSRahul Tanwar } 389d058fd9eSRahul Tanwar 390d058fd9eSRahul Tanwar static unsigned long 391d058fd9eSRahul Tanwar lgm_clk_ddiv_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) 392d058fd9eSRahul Tanwar { 393d058fd9eSRahul Tanwar struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw); 394d058fd9eSRahul Tanwar unsigned int div0, div1, exdiv; 395d058fd9eSRahul Tanwar u64 prate; 396d058fd9eSRahul Tanwar 397d058fd9eSRahul Tanwar div0 = lgm_get_clk_val(ddiv->membase, ddiv->reg, 398d058fd9eSRahul Tanwar ddiv->shift0, ddiv->width0) + 1; 399d058fd9eSRahul Tanwar div1 = lgm_get_clk_val(ddiv->membase, ddiv->reg, 400d058fd9eSRahul Tanwar ddiv->shift1, ddiv->width1) + 1; 401d058fd9eSRahul Tanwar exdiv = lgm_get_clk_val(ddiv->membase, ddiv->reg, 402d058fd9eSRahul Tanwar ddiv->shift2, ddiv->width2); 403d058fd9eSRahul Tanwar prate = (u64)parent_rate; 404d058fd9eSRahul Tanwar do_div(prate, div0); 405d058fd9eSRahul Tanwar do_div(prate, div1); 406d058fd9eSRahul Tanwar 407d058fd9eSRahul Tanwar if (exdiv) { 408d058fd9eSRahul Tanwar do_div(prate, ddiv->div); 409d058fd9eSRahul Tanwar prate *= ddiv->mult; 410d058fd9eSRahul Tanwar } 411d058fd9eSRahul Tanwar 412d058fd9eSRahul Tanwar return prate; 413d058fd9eSRahul Tanwar } 414d058fd9eSRahul Tanwar 415d058fd9eSRahul Tanwar static int lgm_clk_ddiv_enable(struct clk_hw *hw) 416d058fd9eSRahul Tanwar { 417d058fd9eSRahul Tanwar struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw); 418d058fd9eSRahul Tanwar 419d058fd9eSRahul Tanwar lgm_set_clk_val(ddiv->membase, ddiv->reg, ddiv->shift_gate, 420d058fd9eSRahul Tanwar ddiv->width_gate, 1); 421d058fd9eSRahul Tanwar return 0; 422d058fd9eSRahul Tanwar } 423d058fd9eSRahul Tanwar 424d058fd9eSRahul Tanwar static void lgm_clk_ddiv_disable(struct clk_hw *hw) 425d058fd9eSRahul Tanwar { 426d058fd9eSRahul Tanwar struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw); 427d058fd9eSRahul Tanwar 428d058fd9eSRahul Tanwar lgm_set_clk_val(ddiv->membase, ddiv->reg, ddiv->shift_gate, 429d058fd9eSRahul Tanwar ddiv->width_gate, 0); 430d058fd9eSRahul Tanwar } 431d058fd9eSRahul Tanwar 432d058fd9eSRahul Tanwar static int 433d058fd9eSRahul Tanwar lgm_clk_get_ddiv_val(u32 div, u32 *ddiv1, u32 *ddiv2) 434d058fd9eSRahul Tanwar { 435d058fd9eSRahul Tanwar u32 idx, temp; 436d058fd9eSRahul Tanwar 437d058fd9eSRahul Tanwar *ddiv1 = 1; 438d058fd9eSRahul Tanwar *ddiv2 = 1; 439d058fd9eSRahul Tanwar 440d058fd9eSRahul Tanwar if (div > MAX_DIVIDER_VAL) 441d058fd9eSRahul Tanwar div = MAX_DIVIDER_VAL; 442d058fd9eSRahul Tanwar 443d058fd9eSRahul Tanwar if (div > 1) { 444d058fd9eSRahul Tanwar for (idx = 2; idx <= MAX_DDIV_REG; idx++) { 445d058fd9eSRahul Tanwar temp = DIV_ROUND_UP_ULL((u64)div, idx); 446d058fd9eSRahul Tanwar if (div % idx == 0 && temp <= MAX_DDIV_REG) 447d058fd9eSRahul Tanwar break; 448d058fd9eSRahul Tanwar } 449d058fd9eSRahul Tanwar 450d058fd9eSRahul Tanwar if (idx > MAX_DDIV_REG) 451d058fd9eSRahul Tanwar return -EINVAL; 452d058fd9eSRahul Tanwar 453d058fd9eSRahul Tanwar *ddiv1 = temp; 454d058fd9eSRahul Tanwar *ddiv2 = idx; 455d058fd9eSRahul Tanwar } 456d058fd9eSRahul Tanwar 457d058fd9eSRahul Tanwar return 0; 458d058fd9eSRahul Tanwar } 459d058fd9eSRahul Tanwar 460d058fd9eSRahul Tanwar static int 461d058fd9eSRahul Tanwar lgm_clk_ddiv_set_rate(struct clk_hw *hw, unsigned long rate, 462d058fd9eSRahul Tanwar unsigned long prate) 463d058fd9eSRahul Tanwar { 464d058fd9eSRahul Tanwar struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw); 465d058fd9eSRahul Tanwar u32 div, ddiv1, ddiv2; 466d058fd9eSRahul Tanwar 467d058fd9eSRahul Tanwar div = DIV_ROUND_CLOSEST_ULL((u64)prate, rate); 468d058fd9eSRahul Tanwar 469d058fd9eSRahul Tanwar if (lgm_get_clk_val(ddiv->membase, ddiv->reg, ddiv->shift2, 1)) { 470d058fd9eSRahul Tanwar div = DIV_ROUND_CLOSEST_ULL((u64)div, 5); 471d058fd9eSRahul Tanwar div = div * 2; 472d058fd9eSRahul Tanwar } 473d058fd9eSRahul Tanwar 474eaabee88SRahul Tanwar if (div <= 0) 475d058fd9eSRahul Tanwar return -EINVAL; 476d058fd9eSRahul Tanwar 477eaabee88SRahul Tanwar if (lgm_clk_get_ddiv_val(div, &ddiv1, &ddiv2)) 478d058fd9eSRahul Tanwar return -EINVAL; 479d058fd9eSRahul Tanwar 480d058fd9eSRahul Tanwar lgm_set_clk_val(ddiv->membase, ddiv->reg, ddiv->shift0, ddiv->width0, 481d058fd9eSRahul Tanwar ddiv1 - 1); 482d058fd9eSRahul Tanwar 483d058fd9eSRahul Tanwar lgm_set_clk_val(ddiv->membase, ddiv->reg, ddiv->shift1, ddiv->width1, 484d058fd9eSRahul Tanwar ddiv2 - 1); 485d058fd9eSRahul Tanwar 486d058fd9eSRahul Tanwar return 0; 487d058fd9eSRahul Tanwar } 488d058fd9eSRahul Tanwar 489d058fd9eSRahul Tanwar static long 490d058fd9eSRahul Tanwar lgm_clk_ddiv_round_rate(struct clk_hw *hw, unsigned long rate, 491d058fd9eSRahul Tanwar unsigned long *prate) 492d058fd9eSRahul Tanwar { 493d058fd9eSRahul Tanwar struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw); 494d058fd9eSRahul Tanwar u32 div, ddiv1, ddiv2; 495d0364663SColin Ian King u64 rate64; 496d058fd9eSRahul Tanwar 497d058fd9eSRahul Tanwar div = DIV_ROUND_CLOSEST_ULL((u64)*prate, rate); 498d058fd9eSRahul Tanwar 499d058fd9eSRahul Tanwar /* if predivide bit is enabled, modify div by factor of 2.5 */ 500d058fd9eSRahul Tanwar if (lgm_get_clk_val(ddiv->membase, ddiv->reg, ddiv->shift2, 1)) { 501d058fd9eSRahul Tanwar div = div * 2; 502d058fd9eSRahul Tanwar div = DIV_ROUND_CLOSEST_ULL((u64)div, 5); 503d058fd9eSRahul Tanwar } 504c9e28fe6SRahul Tanwar 505c9e28fe6SRahul Tanwar if (div <= 0) 506d058fd9eSRahul Tanwar return *prate; 507d058fd9eSRahul Tanwar 508c9e28fe6SRahul Tanwar if (lgm_clk_get_ddiv_val(div, &ddiv1, &ddiv2) != 0) 509c9e28fe6SRahul Tanwar if (lgm_clk_get_ddiv_val(div + 1, &ddiv1, &ddiv2) != 0) 510d058fd9eSRahul Tanwar return -EINVAL; 511d058fd9eSRahul Tanwar 512d058fd9eSRahul Tanwar rate64 = *prate; 513d058fd9eSRahul Tanwar do_div(rate64, ddiv1); 514d058fd9eSRahul Tanwar do_div(rate64, ddiv2); 515d058fd9eSRahul Tanwar 516d058fd9eSRahul Tanwar /* if predivide bit is enabled, modify rounded rate by factor of 2.5 */ 517d058fd9eSRahul Tanwar if (lgm_get_clk_val(ddiv->membase, ddiv->reg, ddiv->shift2, 1)) { 518d058fd9eSRahul Tanwar rate64 = rate64 * 2; 519d058fd9eSRahul Tanwar rate64 = DIV_ROUND_CLOSEST_ULL(rate64, 5); 520d058fd9eSRahul Tanwar } 521d058fd9eSRahul Tanwar 522d058fd9eSRahul Tanwar return rate64; 523d058fd9eSRahul Tanwar } 524d058fd9eSRahul Tanwar 525d058fd9eSRahul Tanwar static const struct clk_ops lgm_clk_ddiv_ops = { 526d058fd9eSRahul Tanwar .recalc_rate = lgm_clk_ddiv_recalc_rate, 527d058fd9eSRahul Tanwar .enable = lgm_clk_ddiv_enable, 528d058fd9eSRahul Tanwar .disable = lgm_clk_ddiv_disable, 529d058fd9eSRahul Tanwar .set_rate = lgm_clk_ddiv_set_rate, 530d058fd9eSRahul Tanwar .round_rate = lgm_clk_ddiv_round_rate, 531d058fd9eSRahul Tanwar }; 532d058fd9eSRahul Tanwar 533d058fd9eSRahul Tanwar int lgm_clk_register_ddiv(struct lgm_clk_provider *ctx, 534d058fd9eSRahul Tanwar const struct lgm_clk_ddiv_data *list, 535d058fd9eSRahul Tanwar unsigned int nr_clk) 536d058fd9eSRahul Tanwar { 537d058fd9eSRahul Tanwar struct device *dev = ctx->dev; 538d058fd9eSRahul Tanwar struct clk_hw *hw; 539d058fd9eSRahul Tanwar unsigned int idx; 540d058fd9eSRahul Tanwar int ret; 541d058fd9eSRahul Tanwar 542d058fd9eSRahul Tanwar for (idx = 0; idx < nr_clk; idx++, list++) { 543d310124cSRahul Tanwar struct clk_init_data init = {}; 544d310124cSRahul Tanwar struct lgm_clk_ddiv *ddiv; 545d310124cSRahul Tanwar 546d058fd9eSRahul Tanwar ddiv = devm_kzalloc(dev, sizeof(*ddiv), GFP_KERNEL); 547d058fd9eSRahul Tanwar if (!ddiv) 548d058fd9eSRahul Tanwar return -ENOMEM; 549d058fd9eSRahul Tanwar 550d058fd9eSRahul Tanwar init.name = list->name; 551d058fd9eSRahul Tanwar init.ops = &lgm_clk_ddiv_ops; 552d058fd9eSRahul Tanwar init.flags = list->flags; 553d058fd9eSRahul Tanwar init.parent_data = list->parent_data; 554d058fd9eSRahul Tanwar init.num_parents = 1; 555d058fd9eSRahul Tanwar 556d058fd9eSRahul Tanwar ddiv->membase = ctx->membase; 557d058fd9eSRahul Tanwar ddiv->reg = list->reg; 558d058fd9eSRahul Tanwar ddiv->shift0 = list->shift0; 559d058fd9eSRahul Tanwar ddiv->width0 = list->width0; 560d058fd9eSRahul Tanwar ddiv->shift1 = list->shift1; 561d058fd9eSRahul Tanwar ddiv->width1 = list->width1; 562d058fd9eSRahul Tanwar ddiv->shift_gate = list->shift_gate; 563d058fd9eSRahul Tanwar ddiv->width_gate = list->width_gate; 564d058fd9eSRahul Tanwar ddiv->shift2 = list->ex_shift; 565d058fd9eSRahul Tanwar ddiv->width2 = list->ex_width; 566d058fd9eSRahul Tanwar ddiv->flags = list->div_flags; 567d058fd9eSRahul Tanwar ddiv->mult = 2; 568d058fd9eSRahul Tanwar ddiv->div = 5; 569d058fd9eSRahul Tanwar ddiv->hw.init = &init; 570d058fd9eSRahul Tanwar 571d058fd9eSRahul Tanwar hw = &ddiv->hw; 5728529fc0aSRahul Tanwar ret = devm_clk_hw_register(dev, hw); 573d058fd9eSRahul Tanwar if (ret) { 574d058fd9eSRahul Tanwar dev_err(dev, "register clk: %s failed!\n", list->name); 575d058fd9eSRahul Tanwar return ret; 576d058fd9eSRahul Tanwar } 577d058fd9eSRahul Tanwar ctx->clk_data.hws[list->id] = hw; 578d058fd9eSRahul Tanwar } 579d058fd9eSRahul Tanwar 580d058fd9eSRahul Tanwar return 0; 581d058fd9eSRahul Tanwar } 582