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 167*106ef3bdSRahul Tanwar if (div->flags != DIV_CLK_NO_MASK) 168d058fd9eSRahul Tanwar lgm_set_clk_val(div->membase, div->reg, div->shift_gate, 169d058fd9eSRahul Tanwar div->width_gate, enable); 170d058fd9eSRahul Tanwar return 0; 171d058fd9eSRahul Tanwar } 172d058fd9eSRahul Tanwar 173d058fd9eSRahul Tanwar static int lgm_clk_divider_enable(struct clk_hw *hw) 174d058fd9eSRahul Tanwar { 175d058fd9eSRahul Tanwar return lgm_clk_divider_enable_disable(hw, 1); 176d058fd9eSRahul Tanwar } 177d058fd9eSRahul Tanwar 178d058fd9eSRahul Tanwar static void lgm_clk_divider_disable(struct clk_hw *hw) 179d058fd9eSRahul Tanwar { 180d058fd9eSRahul Tanwar lgm_clk_divider_enable_disable(hw, 0); 181d058fd9eSRahul Tanwar } 182d058fd9eSRahul Tanwar 183d058fd9eSRahul Tanwar static const struct clk_ops lgm_clk_divider_ops = { 184d058fd9eSRahul Tanwar .recalc_rate = lgm_clk_divider_recalc_rate, 185d058fd9eSRahul Tanwar .round_rate = lgm_clk_divider_round_rate, 186d058fd9eSRahul Tanwar .set_rate = lgm_clk_divider_set_rate, 187d058fd9eSRahul Tanwar .enable = lgm_clk_divider_enable, 188d058fd9eSRahul Tanwar .disable = lgm_clk_divider_disable, 189d058fd9eSRahul Tanwar }; 190d058fd9eSRahul Tanwar 191d058fd9eSRahul Tanwar static struct clk_hw * 192d058fd9eSRahul Tanwar lgm_clk_register_divider(struct lgm_clk_provider *ctx, 193d058fd9eSRahul Tanwar const struct lgm_clk_branch *list) 194d058fd9eSRahul Tanwar { 195eaabee88SRahul Tanwar unsigned long cflags = list->div_flags; 196d058fd9eSRahul Tanwar struct device *dev = ctx->dev; 197d058fd9eSRahul Tanwar struct lgm_clk_divider *div; 198d058fd9eSRahul Tanwar struct clk_init_data init = {}; 199d058fd9eSRahul Tanwar u8 shift = list->div_shift; 200d058fd9eSRahul Tanwar u8 width = list->div_width; 201d058fd9eSRahul Tanwar u8 shift_gate = list->div_shift_gate; 202d058fd9eSRahul Tanwar u8 width_gate = list->div_width_gate; 203d058fd9eSRahul Tanwar u32 reg = list->div_off; 204d058fd9eSRahul Tanwar struct clk_hw *hw; 205d058fd9eSRahul Tanwar int ret; 206d058fd9eSRahul Tanwar 207d058fd9eSRahul Tanwar div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL); 208d058fd9eSRahul Tanwar if (!div) 209d058fd9eSRahul Tanwar return ERR_PTR(-ENOMEM); 210d058fd9eSRahul Tanwar 211d058fd9eSRahul Tanwar init.name = list->name; 212d058fd9eSRahul Tanwar init.ops = &lgm_clk_divider_ops; 213d058fd9eSRahul Tanwar init.flags = list->flags; 214d058fd9eSRahul Tanwar init.parent_data = list->parent_data; 215d058fd9eSRahul Tanwar init.num_parents = 1; 216d058fd9eSRahul Tanwar 217d058fd9eSRahul Tanwar div->membase = ctx->membase; 218d058fd9eSRahul Tanwar div->reg = reg; 219d058fd9eSRahul Tanwar div->shift = shift; 220d058fd9eSRahul Tanwar div->width = width; 221d058fd9eSRahul Tanwar div->shift_gate = shift_gate; 222d058fd9eSRahul Tanwar div->width_gate = width_gate; 223d058fd9eSRahul Tanwar div->flags = cflags; 224d058fd9eSRahul Tanwar div->table = list->div_table; 225d058fd9eSRahul Tanwar div->hw.init = &init; 226d058fd9eSRahul Tanwar 227d058fd9eSRahul Tanwar hw = &div->hw; 2288529fc0aSRahul Tanwar ret = devm_clk_hw_register(dev, hw); 229d058fd9eSRahul Tanwar if (ret) 230d058fd9eSRahul Tanwar return ERR_PTR(ret); 231d058fd9eSRahul Tanwar 232eaabee88SRahul Tanwar if (cflags & CLOCK_FLAG_VAL_INIT) 233d058fd9eSRahul Tanwar lgm_set_clk_val(div->membase, reg, shift, width, list->div_val); 234d058fd9eSRahul Tanwar 235d058fd9eSRahul Tanwar return hw; 236d058fd9eSRahul Tanwar } 237d058fd9eSRahul Tanwar 238d058fd9eSRahul Tanwar static struct clk_hw * 239d058fd9eSRahul Tanwar lgm_clk_register_fixed_factor(struct lgm_clk_provider *ctx, 240d058fd9eSRahul Tanwar const struct lgm_clk_branch *list) 241d058fd9eSRahul Tanwar { 242d058fd9eSRahul Tanwar struct clk_hw *hw; 243d058fd9eSRahul Tanwar 244d058fd9eSRahul Tanwar hw = clk_hw_register_fixed_factor(ctx->dev, list->name, 245d058fd9eSRahul Tanwar list->parent_data[0].name, list->flags, 246d058fd9eSRahul Tanwar list->mult, list->div); 247d058fd9eSRahul Tanwar if (IS_ERR(hw)) 248d058fd9eSRahul Tanwar return ERR_CAST(hw); 249d058fd9eSRahul Tanwar 250eaabee88SRahul Tanwar if (list->div_flags & CLOCK_FLAG_VAL_INIT) 251d058fd9eSRahul Tanwar lgm_set_clk_val(ctx->membase, list->div_off, list->div_shift, 252d058fd9eSRahul Tanwar list->div_width, list->div_val); 253d058fd9eSRahul Tanwar 254d058fd9eSRahul Tanwar return hw; 255d058fd9eSRahul Tanwar } 256d058fd9eSRahul Tanwar 257d058fd9eSRahul Tanwar static int lgm_clk_gate_enable(struct clk_hw *hw) 258d058fd9eSRahul Tanwar { 259d058fd9eSRahul Tanwar struct lgm_clk_gate *gate = to_lgm_clk_gate(hw); 260d058fd9eSRahul Tanwar unsigned int reg; 261d058fd9eSRahul Tanwar 262d058fd9eSRahul Tanwar reg = GATE_HW_REG_EN(gate->reg); 263d058fd9eSRahul Tanwar lgm_set_clk_val(gate->membase, reg, gate->shift, 1, 1); 264d058fd9eSRahul Tanwar 265d058fd9eSRahul Tanwar return 0; 266d058fd9eSRahul Tanwar } 267d058fd9eSRahul Tanwar 268d058fd9eSRahul Tanwar static void lgm_clk_gate_disable(struct clk_hw *hw) 269d058fd9eSRahul Tanwar { 270d058fd9eSRahul Tanwar struct lgm_clk_gate *gate = to_lgm_clk_gate(hw); 271d058fd9eSRahul Tanwar unsigned int reg; 272d058fd9eSRahul Tanwar 273d058fd9eSRahul Tanwar reg = GATE_HW_REG_DIS(gate->reg); 274d058fd9eSRahul Tanwar lgm_set_clk_val(gate->membase, reg, gate->shift, 1, 1); 275d058fd9eSRahul Tanwar } 276d058fd9eSRahul Tanwar 277d058fd9eSRahul Tanwar static int lgm_clk_gate_is_enabled(struct clk_hw *hw) 278d058fd9eSRahul Tanwar { 279d058fd9eSRahul Tanwar struct lgm_clk_gate *gate = to_lgm_clk_gate(hw); 280d058fd9eSRahul Tanwar unsigned int reg, ret; 281d058fd9eSRahul Tanwar 282d058fd9eSRahul Tanwar reg = GATE_HW_REG_STAT(gate->reg); 283d058fd9eSRahul Tanwar ret = lgm_get_clk_val(gate->membase, reg, gate->shift, 1); 284d058fd9eSRahul Tanwar 285d058fd9eSRahul Tanwar return ret; 286d058fd9eSRahul Tanwar } 287d058fd9eSRahul Tanwar 288d058fd9eSRahul Tanwar static const struct clk_ops lgm_clk_gate_ops = { 289d058fd9eSRahul Tanwar .enable = lgm_clk_gate_enable, 290d058fd9eSRahul Tanwar .disable = lgm_clk_gate_disable, 291d058fd9eSRahul Tanwar .is_enabled = lgm_clk_gate_is_enabled, 292d058fd9eSRahul Tanwar }; 293d058fd9eSRahul Tanwar 294d058fd9eSRahul Tanwar static struct clk_hw * 295d058fd9eSRahul Tanwar lgm_clk_register_gate(struct lgm_clk_provider *ctx, 296d058fd9eSRahul Tanwar const struct lgm_clk_branch *list) 297d058fd9eSRahul Tanwar { 298eaabee88SRahul Tanwar unsigned long cflags = list->gate_flags; 299d058fd9eSRahul Tanwar const char *pname = list->parent_data[0].name; 300d058fd9eSRahul Tanwar struct device *dev = ctx->dev; 301d058fd9eSRahul Tanwar u8 shift = list->gate_shift; 302d058fd9eSRahul Tanwar struct clk_init_data init = {}; 303d058fd9eSRahul Tanwar struct lgm_clk_gate *gate; 304d058fd9eSRahul Tanwar u32 reg = list->gate_off; 305d058fd9eSRahul Tanwar struct clk_hw *hw; 306d058fd9eSRahul Tanwar int ret; 307d058fd9eSRahul Tanwar 308d058fd9eSRahul Tanwar gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL); 309d058fd9eSRahul Tanwar if (!gate) 310d058fd9eSRahul Tanwar return ERR_PTR(-ENOMEM); 311d058fd9eSRahul Tanwar 312d058fd9eSRahul Tanwar init.name = list->name; 313d058fd9eSRahul Tanwar init.ops = &lgm_clk_gate_ops; 314d058fd9eSRahul Tanwar init.flags = list->flags; 315d058fd9eSRahul Tanwar init.parent_names = pname ? &pname : NULL; 316d058fd9eSRahul Tanwar init.num_parents = pname ? 1 : 0; 317d058fd9eSRahul Tanwar 318d058fd9eSRahul Tanwar gate->membase = ctx->membase; 319d058fd9eSRahul Tanwar gate->reg = reg; 320d058fd9eSRahul Tanwar gate->shift = shift; 321d058fd9eSRahul Tanwar gate->flags = cflags; 322d058fd9eSRahul Tanwar gate->hw.init = &init; 323d058fd9eSRahul Tanwar 324d058fd9eSRahul Tanwar hw = &gate->hw; 3258529fc0aSRahul Tanwar ret = devm_clk_hw_register(dev, hw); 326d058fd9eSRahul Tanwar if (ret) 327d058fd9eSRahul Tanwar return ERR_PTR(ret); 328d058fd9eSRahul Tanwar 329d058fd9eSRahul Tanwar if (cflags & CLOCK_FLAG_VAL_INIT) { 330d058fd9eSRahul Tanwar lgm_set_clk_val(gate->membase, reg, shift, 1, list->gate_val); 331d058fd9eSRahul Tanwar } 332d058fd9eSRahul Tanwar 333d058fd9eSRahul Tanwar return hw; 334d058fd9eSRahul Tanwar } 335d058fd9eSRahul Tanwar 336d058fd9eSRahul Tanwar int lgm_clk_register_branches(struct lgm_clk_provider *ctx, 337d058fd9eSRahul Tanwar const struct lgm_clk_branch *list, 338d058fd9eSRahul Tanwar unsigned int nr_clk) 339d058fd9eSRahul Tanwar { 340d058fd9eSRahul Tanwar struct clk_hw *hw; 341d058fd9eSRahul Tanwar unsigned int idx; 342d058fd9eSRahul Tanwar 343d058fd9eSRahul Tanwar for (idx = 0; idx < nr_clk; idx++, list++) { 344d058fd9eSRahul Tanwar switch (list->type) { 345d058fd9eSRahul Tanwar case CLK_TYPE_FIXED: 346d058fd9eSRahul Tanwar hw = lgm_clk_register_fixed(ctx, list); 347d058fd9eSRahul Tanwar break; 348d058fd9eSRahul Tanwar case CLK_TYPE_MUX: 349d058fd9eSRahul Tanwar hw = lgm_clk_register_mux(ctx, list); 350d058fd9eSRahul Tanwar break; 351d058fd9eSRahul Tanwar case CLK_TYPE_DIVIDER: 352d058fd9eSRahul Tanwar hw = lgm_clk_register_divider(ctx, list); 353d058fd9eSRahul Tanwar break; 354d058fd9eSRahul Tanwar case CLK_TYPE_FIXED_FACTOR: 355d058fd9eSRahul Tanwar hw = lgm_clk_register_fixed_factor(ctx, list); 356d058fd9eSRahul Tanwar break; 357d058fd9eSRahul Tanwar case CLK_TYPE_GATE: 358a5d49bd3SRahul Tanwar if (list->gate_flags & GATE_CLK_HW) { 359d058fd9eSRahul Tanwar hw = lgm_clk_register_gate(ctx, list); 360a5d49bd3SRahul Tanwar } else { 361a5d49bd3SRahul Tanwar /* 362a5d49bd3SRahul Tanwar * GATE_CLKs can be controlled either from 363a5d49bd3SRahul Tanwar * CGU clk driver i.e. this driver or directly 364a5d49bd3SRahul Tanwar * from power management driver/daemon. It is 365a5d49bd3SRahul Tanwar * dependent on the power policy/profile requirements 366a5d49bd3SRahul Tanwar * of the end product. To override control of gate 367a5d49bd3SRahul Tanwar * clks from this driver, provide NULL for this index 368a5d49bd3SRahul Tanwar * of gate clk provider. 369a5d49bd3SRahul Tanwar */ 370a5d49bd3SRahul Tanwar hw = NULL; 371a5d49bd3SRahul Tanwar } 372d058fd9eSRahul Tanwar break; 373a5d49bd3SRahul Tanwar 374d058fd9eSRahul Tanwar default: 375d058fd9eSRahul Tanwar dev_err(ctx->dev, "invalid clk type\n"); 376d058fd9eSRahul Tanwar return -EINVAL; 377d058fd9eSRahul Tanwar } 378d058fd9eSRahul Tanwar 379d058fd9eSRahul Tanwar if (IS_ERR(hw)) { 380d058fd9eSRahul Tanwar dev_err(ctx->dev, 381d058fd9eSRahul Tanwar "register clk: %s, type: %u failed!\n", 382d058fd9eSRahul Tanwar list->name, list->type); 383d058fd9eSRahul Tanwar return -EIO; 384d058fd9eSRahul Tanwar } 385d058fd9eSRahul Tanwar ctx->clk_data.hws[list->id] = hw; 386d058fd9eSRahul Tanwar } 387d058fd9eSRahul Tanwar 388d058fd9eSRahul Tanwar return 0; 389d058fd9eSRahul Tanwar } 390d058fd9eSRahul Tanwar 391d058fd9eSRahul Tanwar static unsigned long 392d058fd9eSRahul Tanwar lgm_clk_ddiv_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) 393d058fd9eSRahul Tanwar { 394d058fd9eSRahul Tanwar struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw); 395d058fd9eSRahul Tanwar unsigned int div0, div1, exdiv; 396d058fd9eSRahul Tanwar u64 prate; 397d058fd9eSRahul Tanwar 398d058fd9eSRahul Tanwar div0 = lgm_get_clk_val(ddiv->membase, ddiv->reg, 399d058fd9eSRahul Tanwar ddiv->shift0, ddiv->width0) + 1; 400d058fd9eSRahul Tanwar div1 = lgm_get_clk_val(ddiv->membase, ddiv->reg, 401d058fd9eSRahul Tanwar ddiv->shift1, ddiv->width1) + 1; 402d058fd9eSRahul Tanwar exdiv = lgm_get_clk_val(ddiv->membase, ddiv->reg, 403d058fd9eSRahul Tanwar ddiv->shift2, ddiv->width2); 404d058fd9eSRahul Tanwar prate = (u64)parent_rate; 405d058fd9eSRahul Tanwar do_div(prate, div0); 406d058fd9eSRahul Tanwar do_div(prate, div1); 407d058fd9eSRahul Tanwar 408d058fd9eSRahul Tanwar if (exdiv) { 409d058fd9eSRahul Tanwar do_div(prate, ddiv->div); 410d058fd9eSRahul Tanwar prate *= ddiv->mult; 411d058fd9eSRahul Tanwar } 412d058fd9eSRahul Tanwar 413d058fd9eSRahul Tanwar return prate; 414d058fd9eSRahul Tanwar } 415d058fd9eSRahul Tanwar 416d058fd9eSRahul Tanwar static int lgm_clk_ddiv_enable(struct clk_hw *hw) 417d058fd9eSRahul Tanwar { 418d058fd9eSRahul Tanwar struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw); 419d058fd9eSRahul Tanwar 420d058fd9eSRahul Tanwar lgm_set_clk_val(ddiv->membase, ddiv->reg, ddiv->shift_gate, 421d058fd9eSRahul Tanwar ddiv->width_gate, 1); 422d058fd9eSRahul Tanwar return 0; 423d058fd9eSRahul Tanwar } 424d058fd9eSRahul Tanwar 425d058fd9eSRahul Tanwar static void lgm_clk_ddiv_disable(struct clk_hw *hw) 426d058fd9eSRahul Tanwar { 427d058fd9eSRahul Tanwar struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw); 428d058fd9eSRahul Tanwar 429d058fd9eSRahul Tanwar lgm_set_clk_val(ddiv->membase, ddiv->reg, ddiv->shift_gate, 430d058fd9eSRahul Tanwar ddiv->width_gate, 0); 431d058fd9eSRahul Tanwar } 432d058fd9eSRahul Tanwar 433d058fd9eSRahul Tanwar static int 434d058fd9eSRahul Tanwar lgm_clk_get_ddiv_val(u32 div, u32 *ddiv1, u32 *ddiv2) 435d058fd9eSRahul Tanwar { 436d058fd9eSRahul Tanwar u32 idx, temp; 437d058fd9eSRahul Tanwar 438d058fd9eSRahul Tanwar *ddiv1 = 1; 439d058fd9eSRahul Tanwar *ddiv2 = 1; 440d058fd9eSRahul Tanwar 441d058fd9eSRahul Tanwar if (div > MAX_DIVIDER_VAL) 442d058fd9eSRahul Tanwar div = MAX_DIVIDER_VAL; 443d058fd9eSRahul Tanwar 444d058fd9eSRahul Tanwar if (div > 1) { 445d058fd9eSRahul Tanwar for (idx = 2; idx <= MAX_DDIV_REG; idx++) { 446d058fd9eSRahul Tanwar temp = DIV_ROUND_UP_ULL((u64)div, idx); 447d058fd9eSRahul Tanwar if (div % idx == 0 && temp <= MAX_DDIV_REG) 448d058fd9eSRahul Tanwar break; 449d058fd9eSRahul Tanwar } 450d058fd9eSRahul Tanwar 451d058fd9eSRahul Tanwar if (idx > MAX_DDIV_REG) 452d058fd9eSRahul Tanwar return -EINVAL; 453d058fd9eSRahul Tanwar 454d058fd9eSRahul Tanwar *ddiv1 = temp; 455d058fd9eSRahul Tanwar *ddiv2 = idx; 456d058fd9eSRahul Tanwar } 457d058fd9eSRahul Tanwar 458d058fd9eSRahul Tanwar return 0; 459d058fd9eSRahul Tanwar } 460d058fd9eSRahul Tanwar 461d058fd9eSRahul Tanwar static int 462d058fd9eSRahul Tanwar lgm_clk_ddiv_set_rate(struct clk_hw *hw, unsigned long rate, 463d058fd9eSRahul Tanwar unsigned long prate) 464d058fd9eSRahul Tanwar { 465d058fd9eSRahul Tanwar struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw); 466d058fd9eSRahul Tanwar u32 div, ddiv1, ddiv2; 467d058fd9eSRahul Tanwar 468d058fd9eSRahul Tanwar div = DIV_ROUND_CLOSEST_ULL((u64)prate, rate); 469d058fd9eSRahul Tanwar 470d058fd9eSRahul Tanwar if (lgm_get_clk_val(ddiv->membase, ddiv->reg, ddiv->shift2, 1)) { 471d058fd9eSRahul Tanwar div = DIV_ROUND_CLOSEST_ULL((u64)div, 5); 472d058fd9eSRahul Tanwar div = div * 2; 473d058fd9eSRahul Tanwar } 474d058fd9eSRahul Tanwar 475eaabee88SRahul Tanwar if (div <= 0) 476d058fd9eSRahul Tanwar return -EINVAL; 477d058fd9eSRahul Tanwar 478eaabee88SRahul Tanwar if (lgm_clk_get_ddiv_val(div, &ddiv1, &ddiv2)) 479d058fd9eSRahul Tanwar return -EINVAL; 480d058fd9eSRahul Tanwar 481d058fd9eSRahul Tanwar lgm_set_clk_val(ddiv->membase, ddiv->reg, ddiv->shift0, ddiv->width0, 482d058fd9eSRahul Tanwar ddiv1 - 1); 483d058fd9eSRahul Tanwar 484d058fd9eSRahul Tanwar lgm_set_clk_val(ddiv->membase, ddiv->reg, ddiv->shift1, ddiv->width1, 485d058fd9eSRahul Tanwar ddiv2 - 1); 486d058fd9eSRahul Tanwar 487d058fd9eSRahul Tanwar return 0; 488d058fd9eSRahul Tanwar } 489d058fd9eSRahul Tanwar 490d058fd9eSRahul Tanwar static long 491d058fd9eSRahul Tanwar lgm_clk_ddiv_round_rate(struct clk_hw *hw, unsigned long rate, 492d058fd9eSRahul Tanwar unsigned long *prate) 493d058fd9eSRahul Tanwar { 494d058fd9eSRahul Tanwar struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw); 495d058fd9eSRahul Tanwar u32 div, ddiv1, ddiv2; 496d0364663SColin Ian King u64 rate64; 497d058fd9eSRahul Tanwar 498d058fd9eSRahul Tanwar div = DIV_ROUND_CLOSEST_ULL((u64)*prate, rate); 499d058fd9eSRahul Tanwar 500d058fd9eSRahul Tanwar /* if predivide bit is enabled, modify div by factor of 2.5 */ 501d058fd9eSRahul Tanwar if (lgm_get_clk_val(ddiv->membase, ddiv->reg, ddiv->shift2, 1)) { 502d058fd9eSRahul Tanwar div = div * 2; 503d058fd9eSRahul Tanwar div = DIV_ROUND_CLOSEST_ULL((u64)div, 5); 504d058fd9eSRahul Tanwar } 505c9e28fe6SRahul Tanwar 506c9e28fe6SRahul Tanwar if (div <= 0) 507d058fd9eSRahul Tanwar return *prate; 508d058fd9eSRahul Tanwar 509c9e28fe6SRahul Tanwar if (lgm_clk_get_ddiv_val(div, &ddiv1, &ddiv2) != 0) 510c9e28fe6SRahul Tanwar if (lgm_clk_get_ddiv_val(div + 1, &ddiv1, &ddiv2) != 0) 511d058fd9eSRahul Tanwar return -EINVAL; 512d058fd9eSRahul Tanwar 513d058fd9eSRahul Tanwar rate64 = *prate; 514d058fd9eSRahul Tanwar do_div(rate64, ddiv1); 515d058fd9eSRahul Tanwar do_div(rate64, ddiv2); 516d058fd9eSRahul Tanwar 517d058fd9eSRahul Tanwar /* if predivide bit is enabled, modify rounded rate by factor of 2.5 */ 518d058fd9eSRahul Tanwar if (lgm_get_clk_val(ddiv->membase, ddiv->reg, ddiv->shift2, 1)) { 519d058fd9eSRahul Tanwar rate64 = rate64 * 2; 520d058fd9eSRahul Tanwar rate64 = DIV_ROUND_CLOSEST_ULL(rate64, 5); 521d058fd9eSRahul Tanwar } 522d058fd9eSRahul Tanwar 523d058fd9eSRahul Tanwar return rate64; 524d058fd9eSRahul Tanwar } 525d058fd9eSRahul Tanwar 526d058fd9eSRahul Tanwar static const struct clk_ops lgm_clk_ddiv_ops = { 527d058fd9eSRahul Tanwar .recalc_rate = lgm_clk_ddiv_recalc_rate, 528d058fd9eSRahul Tanwar .enable = lgm_clk_ddiv_enable, 529d058fd9eSRahul Tanwar .disable = lgm_clk_ddiv_disable, 530d058fd9eSRahul Tanwar .set_rate = lgm_clk_ddiv_set_rate, 531d058fd9eSRahul Tanwar .round_rate = lgm_clk_ddiv_round_rate, 532d058fd9eSRahul Tanwar }; 533d058fd9eSRahul Tanwar 534d058fd9eSRahul Tanwar int lgm_clk_register_ddiv(struct lgm_clk_provider *ctx, 535d058fd9eSRahul Tanwar const struct lgm_clk_ddiv_data *list, 536d058fd9eSRahul Tanwar unsigned int nr_clk) 537d058fd9eSRahul Tanwar { 538d058fd9eSRahul Tanwar struct device *dev = ctx->dev; 539d058fd9eSRahul Tanwar struct clk_hw *hw; 540d058fd9eSRahul Tanwar unsigned int idx; 541d058fd9eSRahul Tanwar int ret; 542d058fd9eSRahul Tanwar 543d058fd9eSRahul Tanwar for (idx = 0; idx < nr_clk; idx++, list++) { 544d310124cSRahul Tanwar struct clk_init_data init = {}; 545d310124cSRahul Tanwar struct lgm_clk_ddiv *ddiv; 546d310124cSRahul Tanwar 547d058fd9eSRahul Tanwar ddiv = devm_kzalloc(dev, sizeof(*ddiv), GFP_KERNEL); 548d058fd9eSRahul Tanwar if (!ddiv) 549d058fd9eSRahul Tanwar return -ENOMEM; 550d058fd9eSRahul Tanwar 551d058fd9eSRahul Tanwar init.name = list->name; 552d058fd9eSRahul Tanwar init.ops = &lgm_clk_ddiv_ops; 553d058fd9eSRahul Tanwar init.flags = list->flags; 554d058fd9eSRahul Tanwar init.parent_data = list->parent_data; 555d058fd9eSRahul Tanwar init.num_parents = 1; 556d058fd9eSRahul Tanwar 557d058fd9eSRahul Tanwar ddiv->membase = ctx->membase; 558d058fd9eSRahul Tanwar ddiv->reg = list->reg; 559d058fd9eSRahul Tanwar ddiv->shift0 = list->shift0; 560d058fd9eSRahul Tanwar ddiv->width0 = list->width0; 561d058fd9eSRahul Tanwar ddiv->shift1 = list->shift1; 562d058fd9eSRahul Tanwar ddiv->width1 = list->width1; 563d058fd9eSRahul Tanwar ddiv->shift_gate = list->shift_gate; 564d058fd9eSRahul Tanwar ddiv->width_gate = list->width_gate; 565d058fd9eSRahul Tanwar ddiv->shift2 = list->ex_shift; 566d058fd9eSRahul Tanwar ddiv->width2 = list->ex_width; 567d058fd9eSRahul Tanwar ddiv->flags = list->div_flags; 568d058fd9eSRahul Tanwar ddiv->mult = 2; 569d058fd9eSRahul Tanwar ddiv->div = 5; 570d058fd9eSRahul Tanwar ddiv->hw.init = &init; 571d058fd9eSRahul Tanwar 572d058fd9eSRahul Tanwar hw = &ddiv->hw; 5738529fc0aSRahul Tanwar ret = devm_clk_hw_register(dev, hw); 574d058fd9eSRahul Tanwar if (ret) { 575d058fd9eSRahul Tanwar dev_err(dev, "register clk: %s failed!\n", list->name); 576d058fd9eSRahul Tanwar return ret; 577d058fd9eSRahul Tanwar } 578d058fd9eSRahul Tanwar ctx->clk_data.hws[list->id] = hw; 579d058fd9eSRahul Tanwar } 580d058fd9eSRahul Tanwar 581d058fd9eSRahul Tanwar return 0; 582d058fd9eSRahul Tanwar } 583