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 29*eaabee88SRahul 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 { 84*eaabee88SRahul 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 116*eaabee88SRahul 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 { 194*eaabee88SRahul 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 231*eaabee88SRahul 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 249*eaabee88SRahul 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 { 297*eaabee88SRahul 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: 357d058fd9eSRahul Tanwar hw = lgm_clk_register_gate(ctx, list); 358d058fd9eSRahul Tanwar break; 359d058fd9eSRahul Tanwar default: 360d058fd9eSRahul Tanwar dev_err(ctx->dev, "invalid clk type\n"); 361d058fd9eSRahul Tanwar return -EINVAL; 362d058fd9eSRahul Tanwar } 363d058fd9eSRahul Tanwar 364d058fd9eSRahul Tanwar if (IS_ERR(hw)) { 365d058fd9eSRahul Tanwar dev_err(ctx->dev, 366d058fd9eSRahul Tanwar "register clk: %s, type: %u failed!\n", 367d058fd9eSRahul Tanwar list->name, list->type); 368d058fd9eSRahul Tanwar return -EIO; 369d058fd9eSRahul Tanwar } 370d058fd9eSRahul Tanwar ctx->clk_data.hws[list->id] = hw; 371d058fd9eSRahul Tanwar } 372d058fd9eSRahul Tanwar 373d058fd9eSRahul Tanwar return 0; 374d058fd9eSRahul Tanwar } 375d058fd9eSRahul Tanwar 376d058fd9eSRahul Tanwar static unsigned long 377d058fd9eSRahul Tanwar lgm_clk_ddiv_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) 378d058fd9eSRahul Tanwar { 379d058fd9eSRahul Tanwar struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw); 380d058fd9eSRahul Tanwar unsigned int div0, div1, exdiv; 381d058fd9eSRahul Tanwar u64 prate; 382d058fd9eSRahul Tanwar 383d058fd9eSRahul Tanwar div0 = lgm_get_clk_val(ddiv->membase, ddiv->reg, 384d058fd9eSRahul Tanwar ddiv->shift0, ddiv->width0) + 1; 385d058fd9eSRahul Tanwar div1 = lgm_get_clk_val(ddiv->membase, ddiv->reg, 386d058fd9eSRahul Tanwar ddiv->shift1, ddiv->width1) + 1; 387d058fd9eSRahul Tanwar exdiv = lgm_get_clk_val(ddiv->membase, ddiv->reg, 388d058fd9eSRahul Tanwar ddiv->shift2, ddiv->width2); 389d058fd9eSRahul Tanwar prate = (u64)parent_rate; 390d058fd9eSRahul Tanwar do_div(prate, div0); 391d058fd9eSRahul Tanwar do_div(prate, div1); 392d058fd9eSRahul Tanwar 393d058fd9eSRahul Tanwar if (exdiv) { 394d058fd9eSRahul Tanwar do_div(prate, ddiv->div); 395d058fd9eSRahul Tanwar prate *= ddiv->mult; 396d058fd9eSRahul Tanwar } 397d058fd9eSRahul Tanwar 398d058fd9eSRahul Tanwar return prate; 399d058fd9eSRahul Tanwar } 400d058fd9eSRahul Tanwar 401d058fd9eSRahul Tanwar static int lgm_clk_ddiv_enable(struct clk_hw *hw) 402d058fd9eSRahul Tanwar { 403d058fd9eSRahul Tanwar struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw); 404d058fd9eSRahul Tanwar 405d058fd9eSRahul Tanwar lgm_set_clk_val(ddiv->membase, ddiv->reg, ddiv->shift_gate, 406d058fd9eSRahul Tanwar ddiv->width_gate, 1); 407d058fd9eSRahul Tanwar return 0; 408d058fd9eSRahul Tanwar } 409d058fd9eSRahul Tanwar 410d058fd9eSRahul Tanwar static void lgm_clk_ddiv_disable(struct clk_hw *hw) 411d058fd9eSRahul Tanwar { 412d058fd9eSRahul Tanwar struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw); 413d058fd9eSRahul Tanwar 414d058fd9eSRahul Tanwar lgm_set_clk_val(ddiv->membase, ddiv->reg, ddiv->shift_gate, 415d058fd9eSRahul Tanwar ddiv->width_gate, 0); 416d058fd9eSRahul Tanwar } 417d058fd9eSRahul Tanwar 418d058fd9eSRahul Tanwar static int 419d058fd9eSRahul Tanwar lgm_clk_get_ddiv_val(u32 div, u32 *ddiv1, u32 *ddiv2) 420d058fd9eSRahul Tanwar { 421d058fd9eSRahul Tanwar u32 idx, temp; 422d058fd9eSRahul Tanwar 423d058fd9eSRahul Tanwar *ddiv1 = 1; 424d058fd9eSRahul Tanwar *ddiv2 = 1; 425d058fd9eSRahul Tanwar 426d058fd9eSRahul Tanwar if (div > MAX_DIVIDER_VAL) 427d058fd9eSRahul Tanwar div = MAX_DIVIDER_VAL; 428d058fd9eSRahul Tanwar 429d058fd9eSRahul Tanwar if (div > 1) { 430d058fd9eSRahul Tanwar for (idx = 2; idx <= MAX_DDIV_REG; idx++) { 431d058fd9eSRahul Tanwar temp = DIV_ROUND_UP_ULL((u64)div, idx); 432d058fd9eSRahul Tanwar if (div % idx == 0 && temp <= MAX_DDIV_REG) 433d058fd9eSRahul Tanwar break; 434d058fd9eSRahul Tanwar } 435d058fd9eSRahul Tanwar 436d058fd9eSRahul Tanwar if (idx > MAX_DDIV_REG) 437d058fd9eSRahul Tanwar return -EINVAL; 438d058fd9eSRahul Tanwar 439d058fd9eSRahul Tanwar *ddiv1 = temp; 440d058fd9eSRahul Tanwar *ddiv2 = idx; 441d058fd9eSRahul Tanwar } 442d058fd9eSRahul Tanwar 443d058fd9eSRahul Tanwar return 0; 444d058fd9eSRahul Tanwar } 445d058fd9eSRahul Tanwar 446d058fd9eSRahul Tanwar static int 447d058fd9eSRahul Tanwar lgm_clk_ddiv_set_rate(struct clk_hw *hw, unsigned long rate, 448d058fd9eSRahul Tanwar unsigned long prate) 449d058fd9eSRahul Tanwar { 450d058fd9eSRahul Tanwar struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw); 451d058fd9eSRahul Tanwar u32 div, ddiv1, ddiv2; 452d058fd9eSRahul Tanwar 453d058fd9eSRahul Tanwar div = DIV_ROUND_CLOSEST_ULL((u64)prate, rate); 454d058fd9eSRahul Tanwar 455d058fd9eSRahul Tanwar if (lgm_get_clk_val(ddiv->membase, ddiv->reg, ddiv->shift2, 1)) { 456d058fd9eSRahul Tanwar div = DIV_ROUND_CLOSEST_ULL((u64)div, 5); 457d058fd9eSRahul Tanwar div = div * 2; 458d058fd9eSRahul Tanwar } 459d058fd9eSRahul Tanwar 460*eaabee88SRahul Tanwar if (div <= 0) 461d058fd9eSRahul Tanwar return -EINVAL; 462d058fd9eSRahul Tanwar 463*eaabee88SRahul Tanwar if (lgm_clk_get_ddiv_val(div, &ddiv1, &ddiv2)) 464d058fd9eSRahul Tanwar return -EINVAL; 465d058fd9eSRahul Tanwar 466d058fd9eSRahul Tanwar lgm_set_clk_val(ddiv->membase, ddiv->reg, ddiv->shift0, ddiv->width0, 467d058fd9eSRahul Tanwar ddiv1 - 1); 468d058fd9eSRahul Tanwar 469d058fd9eSRahul Tanwar lgm_set_clk_val(ddiv->membase, ddiv->reg, ddiv->shift1, ddiv->width1, 470d058fd9eSRahul Tanwar ddiv2 - 1); 471d058fd9eSRahul Tanwar 472d058fd9eSRahul Tanwar return 0; 473d058fd9eSRahul Tanwar } 474d058fd9eSRahul Tanwar 475d058fd9eSRahul Tanwar static long 476d058fd9eSRahul Tanwar lgm_clk_ddiv_round_rate(struct clk_hw *hw, unsigned long rate, 477d058fd9eSRahul Tanwar unsigned long *prate) 478d058fd9eSRahul Tanwar { 479d058fd9eSRahul Tanwar struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw); 480d058fd9eSRahul Tanwar u32 div, ddiv1, ddiv2; 481d0364663SColin Ian King u64 rate64; 482d058fd9eSRahul Tanwar 483d058fd9eSRahul Tanwar div = DIV_ROUND_CLOSEST_ULL((u64)*prate, rate); 484d058fd9eSRahul Tanwar 485d058fd9eSRahul Tanwar /* if predivide bit is enabled, modify div by factor of 2.5 */ 486d058fd9eSRahul Tanwar if (lgm_get_clk_val(ddiv->membase, ddiv->reg, ddiv->shift2, 1)) { 487d058fd9eSRahul Tanwar div = div * 2; 488d058fd9eSRahul Tanwar div = DIV_ROUND_CLOSEST_ULL((u64)div, 5); 489d058fd9eSRahul Tanwar } 490c9e28fe6SRahul Tanwar 491c9e28fe6SRahul Tanwar if (div <= 0) 492d058fd9eSRahul Tanwar return *prate; 493d058fd9eSRahul Tanwar 494c9e28fe6SRahul Tanwar if (lgm_clk_get_ddiv_val(div, &ddiv1, &ddiv2) != 0) 495c9e28fe6SRahul Tanwar if (lgm_clk_get_ddiv_val(div + 1, &ddiv1, &ddiv2) != 0) 496d058fd9eSRahul Tanwar return -EINVAL; 497d058fd9eSRahul Tanwar 498d058fd9eSRahul Tanwar rate64 = *prate; 499d058fd9eSRahul Tanwar do_div(rate64, ddiv1); 500d058fd9eSRahul Tanwar do_div(rate64, ddiv2); 501d058fd9eSRahul Tanwar 502d058fd9eSRahul Tanwar /* if predivide bit is enabled, modify rounded rate by factor of 2.5 */ 503d058fd9eSRahul Tanwar if (lgm_get_clk_val(ddiv->membase, ddiv->reg, ddiv->shift2, 1)) { 504d058fd9eSRahul Tanwar rate64 = rate64 * 2; 505d058fd9eSRahul Tanwar rate64 = DIV_ROUND_CLOSEST_ULL(rate64, 5); 506d058fd9eSRahul Tanwar } 507d058fd9eSRahul Tanwar 508d058fd9eSRahul Tanwar return rate64; 509d058fd9eSRahul Tanwar } 510d058fd9eSRahul Tanwar 511d058fd9eSRahul Tanwar static const struct clk_ops lgm_clk_ddiv_ops = { 512d058fd9eSRahul Tanwar .recalc_rate = lgm_clk_ddiv_recalc_rate, 513d058fd9eSRahul Tanwar .enable = lgm_clk_ddiv_enable, 514d058fd9eSRahul Tanwar .disable = lgm_clk_ddiv_disable, 515d058fd9eSRahul Tanwar .set_rate = lgm_clk_ddiv_set_rate, 516d058fd9eSRahul Tanwar .round_rate = lgm_clk_ddiv_round_rate, 517d058fd9eSRahul Tanwar }; 518d058fd9eSRahul Tanwar 519d058fd9eSRahul Tanwar int lgm_clk_register_ddiv(struct lgm_clk_provider *ctx, 520d058fd9eSRahul Tanwar const struct lgm_clk_ddiv_data *list, 521d058fd9eSRahul Tanwar unsigned int nr_clk) 522d058fd9eSRahul Tanwar { 523d058fd9eSRahul Tanwar struct device *dev = ctx->dev; 524d058fd9eSRahul Tanwar struct clk_hw *hw; 525d058fd9eSRahul Tanwar unsigned int idx; 526d058fd9eSRahul Tanwar int ret; 527d058fd9eSRahul Tanwar 528d058fd9eSRahul Tanwar for (idx = 0; idx < nr_clk; idx++, list++) { 529d310124cSRahul Tanwar struct clk_init_data init = {}; 530d310124cSRahul Tanwar struct lgm_clk_ddiv *ddiv; 531d310124cSRahul Tanwar 532d058fd9eSRahul Tanwar ddiv = devm_kzalloc(dev, sizeof(*ddiv), GFP_KERNEL); 533d058fd9eSRahul Tanwar if (!ddiv) 534d058fd9eSRahul Tanwar return -ENOMEM; 535d058fd9eSRahul Tanwar 536d058fd9eSRahul Tanwar init.name = list->name; 537d058fd9eSRahul Tanwar init.ops = &lgm_clk_ddiv_ops; 538d058fd9eSRahul Tanwar init.flags = list->flags; 539d058fd9eSRahul Tanwar init.parent_data = list->parent_data; 540d058fd9eSRahul Tanwar init.num_parents = 1; 541d058fd9eSRahul Tanwar 542d058fd9eSRahul Tanwar ddiv->membase = ctx->membase; 543d058fd9eSRahul Tanwar ddiv->reg = list->reg; 544d058fd9eSRahul Tanwar ddiv->shift0 = list->shift0; 545d058fd9eSRahul Tanwar ddiv->width0 = list->width0; 546d058fd9eSRahul Tanwar ddiv->shift1 = list->shift1; 547d058fd9eSRahul Tanwar ddiv->width1 = list->width1; 548d058fd9eSRahul Tanwar ddiv->shift_gate = list->shift_gate; 549d058fd9eSRahul Tanwar ddiv->width_gate = list->width_gate; 550d058fd9eSRahul Tanwar ddiv->shift2 = list->ex_shift; 551d058fd9eSRahul Tanwar ddiv->width2 = list->ex_width; 552d058fd9eSRahul Tanwar ddiv->flags = list->div_flags; 553d058fd9eSRahul Tanwar ddiv->mult = 2; 554d058fd9eSRahul Tanwar ddiv->div = 5; 555d058fd9eSRahul Tanwar ddiv->hw.init = &init; 556d058fd9eSRahul Tanwar 557d058fd9eSRahul Tanwar hw = &ddiv->hw; 5588529fc0aSRahul Tanwar ret = devm_clk_hw_register(dev, hw); 559d058fd9eSRahul Tanwar if (ret) { 560d058fd9eSRahul Tanwar dev_err(dev, "register clk: %s failed!\n", list->name); 561d058fd9eSRahul Tanwar return ret; 562d058fd9eSRahul Tanwar } 563d058fd9eSRahul Tanwar ctx->clk_data.hws[list->id] = hw; 564d058fd9eSRahul Tanwar } 565d058fd9eSRahul Tanwar 566d058fd9eSRahul Tanwar return 0; 567d058fd9eSRahul Tanwar } 568