// SPDX-License-Identifier: GPL-2.0 /* * Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd.. * All rights reserved. * * Authors: * Yifeng Huang * Xuyang Dong */ #include #include #include #include #include #include #include "common.h" #define PLL_EN_MASK GENMASK(1, 0) #define PLL_REFDIV_MASK GENMASK(17, 12) #define PLL_FBDIV_MASK GENMASK(31, 20) #define PLL_FRAC_MASK GENMASK(27, 4) #define PLL_POSTDIV1_MASK GENMASK(10, 8) #define PLL_POSTDIV2_MASK GENMASK(18, 16) struct eswin_clock_data *eswin_clk_init(struct platform_device *pdev, size_t nr_clks) { struct eswin_clock_data *eclk_data; eclk_data = devm_kzalloc(&pdev->dev, struct_size(eclk_data, clk_data.hws, nr_clks), GFP_KERNEL); if (!eclk_data) return ERR_PTR(-ENOMEM); eclk_data->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(eclk_data->base)) return ERR_PTR(-EINVAL); eclk_data->clk_data.num = nr_clks; spin_lock_init(&eclk_data->lock); return eclk_data; } EXPORT_SYMBOL_GPL(eswin_clk_init); /** * eswin_calc_pll - calculate PLL values * @frac_val: fractional divider * @fbdiv_val: feedback divider * @rate: reference rate * @parent_rate: parent rate * * Calculate PLL values for frac and fbdiv: * fbdiv = rate * 4 / parent_rate * frac = (rate * 4 % parent_rate * (2 ^ 24)) / parent_rate */ static void eswin_calc_pll(u32 *frac_val, u32 *fbdiv_val, unsigned long rate, unsigned long parent_rate) { u32 rem; u64 tmp; /* step 1: rate * 4 */ tmp = rate * 4; /* step 2: use do_div() to get the quotient(tmp) and remainder(rem) */ rem = do_div(tmp, (u32)parent_rate); /* fbdiv = rate * 4 / parent_rate */ *fbdiv_val = (u32)tmp; /* * step 3: rem << 24 * 24: 24-bit fractional accuracy */ tmp = (u64)rem << 24; /* step 4: use do_div() to get the quotient(tmp) */ do_div(tmp, (u32)parent_rate); /* frac = (rate * 4 % parent_rate * (2 ^ 24)) / parent_rate */ *frac_val = (u32)tmp; } static inline struct eswin_clk_pll *to_pll_clk(struct clk_hw *hw) { return container_of(hw, struct eswin_clk_pll, hw); } static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct eswin_clk_pll *clk = to_pll_clk(hw); u32 frac_val, fbdiv_val, val, mask; int ret; eswin_calc_pll(&frac_val, &fbdiv_val, rate, parent_rate); /* First, disable pll */ val = readl_relaxed(clk->ctrl_reg0); val &= ~PLL_EN_MASK; val |= FIELD_PREP(PLL_EN_MASK, 0); writel_relaxed(val, clk->ctrl_reg0); val = readl_relaxed(clk->ctrl_reg0); val &= ~(PLL_REFDIV_MASK | PLL_FBDIV_MASK); val |= FIELD_PREP(PLL_FBDIV_MASK, fbdiv_val); val |= FIELD_PREP(PLL_REFDIV_MASK, 1); writel_relaxed(val, clk->ctrl_reg0); val = readl_relaxed(clk->ctrl_reg1); val &= ~PLL_FRAC_MASK; val |= FIELD_PREP(PLL_FRAC_MASK, frac_val); writel_relaxed(val, clk->ctrl_reg1); val = readl_relaxed(clk->ctrl_reg2); val &= ~(PLL_POSTDIV1_MASK | PLL_POSTDIV2_MASK); val |= FIELD_PREP(PLL_POSTDIV1_MASK, 1); val |= FIELD_PREP(PLL_POSTDIV2_MASK, 1); writel_relaxed(val, clk->ctrl_reg2); /* Last, enable pll */ val = readl_relaxed(clk->ctrl_reg0); val &= ~PLL_EN_MASK; val |= FIELD_PREP(PLL_EN_MASK, 1); writel_relaxed(val, clk->ctrl_reg0); /* Usually the pll will lock in 50us */ mask = GENMASK(clk->lock_shift + clk->lock_width - 1, clk->lock_shift); ret = readl_poll_timeout(clk->status_reg, val, val & mask, 1, 50 * 2); if (ret) pr_err("failed to lock the pll!\n"); return ret; } static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct eswin_clk_pll *clk = to_pll_clk(hw); u64 fbdiv_val, frac_val, tmp; u32 rem, val; val = readl_relaxed(clk->ctrl_reg0); val &= PLL_FBDIV_MASK; fbdiv_val = (val >> clk->fbdiv_shift); val = readl_relaxed(clk->ctrl_reg1); val &= PLL_FRAC_MASK; frac_val = (val >> clk->frac_shift); /* rate = 24000000 * (fbdiv + frac / (2 ^ 24)) / 4 */ tmp = parent_rate * frac_val; rem = do_div(tmp, BIT(24)); if (rem) tmp = parent_rate * fbdiv_val + tmp + 1; else tmp = parent_rate * fbdiv_val + tmp; do_div(tmp, 4); return tmp; } static int clk_pll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { struct eswin_clk_pll *clk = to_pll_clk(hw); req->rate = clamp(req->rate, clk->min_rate, clk->max_rate); req->min_rate = clk->min_rate; req->max_rate = clk->max_rate; return 0; } int eswin_clk_register_fixed_rate(struct device *dev, struct eswin_fixed_rate_clock *clks, int nums, struct eswin_clock_data *data) { struct clk_hw *clk_hw; int i; for (i = 0; i < nums; i++) { clk_hw = devm_clk_hw_register_fixed_rate(dev, clks[i].name, NULL, clks[i].flags, clks[i].rate); if (IS_ERR(clk_hw)) return PTR_ERR(clk_hw); clks[i].hw = *clk_hw; data->clk_data.hws[clks[i].id] = clk_hw; } return 0; } EXPORT_SYMBOL_GPL(eswin_clk_register_fixed_rate); static const struct clk_ops eswin_clk_pll_ops = { .set_rate = clk_pll_set_rate, .recalc_rate = clk_pll_recalc_rate, .determine_rate = clk_pll_determine_rate, }; int eswin_clk_register_pll(struct device *dev, struct eswin_pll_clock *clks, int nums, struct eswin_clock_data *data) { struct eswin_clk_pll *p_clk = NULL; struct clk_init_data init; struct clk_hw *clk_hw; int i, ret; p_clk = devm_kzalloc(dev, sizeof(*p_clk) * nums, GFP_KERNEL); if (!p_clk) return -ENOMEM; for (i = 0; i < nums; i++) { p_clk->id = clks[i].id; p_clk->ctrl_reg0 = data->base + clks[i].ctrl_reg0; p_clk->fbdiv_shift = clks[i].fbdiv_shift; p_clk->ctrl_reg1 = data->base + clks[i].ctrl_reg1; p_clk->frac_shift = clks[i].frac_shift; p_clk->ctrl_reg2 = data->base + clks[i].ctrl_reg2; p_clk->status_reg = data->base + clks[i].status_reg; p_clk->lock_shift = clks[i].lock_shift; p_clk->lock_width = clks[i].lock_width; p_clk->max_rate = clks[i].max_rate; p_clk->min_rate = clks[i].min_rate; init.name = clks[i].name; init.flags = 0; init.parent_data = clks[i].parent_data; init.num_parents = 1; init.ops = &eswin_clk_pll_ops; p_clk->hw.init = &init; clk_hw = &p_clk->hw; ret = devm_clk_hw_register(dev, clk_hw); if (ret) return ret; clks[i].hw = *clk_hw; data->clk_data.hws[clks[i].id] = clk_hw; p_clk++; } return 0; } EXPORT_SYMBOL_GPL(eswin_clk_register_pll); int eswin_clk_register_fixed_factor(struct device *dev, struct eswin_fixed_factor_clock *clks, int nums, struct eswin_clock_data *data) { struct clk_hw *clk_hw; int i; for (i = 0; i < nums; i++) { clk_hw = devm_clk_hw_register_fixed_factor_index(dev, clks[i].name, clks[i].parent_data->index, clks[i].flags, clks[i].mult, clks[i].div); if (IS_ERR(clk_hw)) return PTR_ERR(clk_hw); clks[i].hw = *clk_hw; data->clk_data.hws[clks[i].id] = clk_hw; } return 0; } EXPORT_SYMBOL_GPL(eswin_clk_register_fixed_factor); int eswin_clk_register_mux(struct device *dev, struct eswin_mux_clock *clks, int nums, struct eswin_clock_data *data) { struct clk_hw *clk_hw; int i; for (i = 0; i < nums; i++) { clk_hw = devm_clk_hw_register_mux_parent_data_table(dev, clks[i].name, clks[i].parent_data, clks[i].num_parents, clks[i].flags, data->base + clks[i].reg, clks[i].shift, clks[i].width, clks[i].mux_flags, clks[i].table, &data->lock); if (IS_ERR(clk_hw)) return PTR_ERR(clk_hw); clks[i].hw = *clk_hw; data->clk_data.hws[clks[i].id] = clk_hw; } return 0; } EXPORT_SYMBOL_GPL(eswin_clk_register_mux); static unsigned int _eswin_get_val(unsigned int div, unsigned long flags, u8 width) { unsigned int maxdiv; maxdiv = clk_div_mask(width); div = div > maxdiv ? maxdiv : div; if (flags & ESWIN_PRIV_DIV_MIN_2) return (div < 2) ? 2 : div; return div; } static unsigned int eswin_div_get_val(unsigned long rate, unsigned long parent_rate, u8 width, unsigned long flags) { unsigned int div; div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); return _eswin_get_val(div, flags, width); } static inline struct eswin_divider_clock *to_div_clk(struct clk_hw *hw) { return container_of(hw, struct eswin_divider_clock, hw); } static int clk_div_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct eswin_divider_clock *dclk = to_div_clk(hw); unsigned long flags; unsigned int value; u32 val; value = eswin_div_get_val(rate, parent_rate, dclk->width, dclk->priv_flag); spin_lock_irqsave(dclk->lock, flags); val = readl_relaxed(dclk->ctrl_reg); val &= ~(clk_div_mask(dclk->width) << dclk->shift); val |= (u32)value << dclk->shift; writel_relaxed(val, dclk->ctrl_reg); spin_unlock_irqrestore(dclk->lock, flags); return 0; } static unsigned long clk_div_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct eswin_divider_clock *dclk = to_div_clk(hw); unsigned int div, val; val = readl_relaxed(dclk->ctrl_reg) >> dclk->shift; val &= clk_div_mask(dclk->width); div = _eswin_get_val(val, dclk->priv_flag, dclk->width); return DIV_ROUND_UP_ULL((u64)parent_rate, div); } static int eswin_clk_bestdiv(unsigned long rate, unsigned long best_parent_rate, u8 width, unsigned long flags) { unsigned long bestdiv, up_rate, down_rate; int up, down; if (!rate) rate = 1; /* closest round */ up = DIV_ROUND_UP_ULL((u64)best_parent_rate, rate); down = best_parent_rate / rate; up_rate = DIV_ROUND_UP_ULL((u64)best_parent_rate, up); down_rate = DIV_ROUND_UP_ULL((u64)best_parent_rate, down); bestdiv = (rate - up_rate) <= (down_rate - rate) ? up : down; return bestdiv; } static int clk_div_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { struct eswin_divider_clock *dclk = to_div_clk(hw); int div; div = eswin_clk_bestdiv(req->rate, req->best_parent_rate, dclk->width, dclk->priv_flag); div = _eswin_get_val(div, dclk->priv_flag, dclk->width); req->rate = DIV_ROUND_UP_ULL((u64)req->best_parent_rate, div); return 0; } static const struct clk_ops eswin_clk_div_ops = { .set_rate = clk_div_set_rate, .recalc_rate = clk_div_recalc_rate, .determine_rate = clk_div_determine_rate, }; struct clk_hw *eswin_register_clkdiv(struct device *dev, unsigned int id, const char *name, const struct clk_hw *parent_hw, unsigned long flags, void __iomem *reg, u8 shift, u8 width, unsigned long clk_divider_flags, unsigned long priv_flag, spinlock_t *lock) { struct eswin_divider_clock *dclk; struct clk_init_data init; struct clk_hw *clk_hw; int ret; dclk = devm_kzalloc(dev, sizeof(*dclk), GFP_KERNEL); if (!dclk) return ERR_PTR(-ENOMEM); init.name = name; init.ops = &eswin_clk_div_ops; init.flags = flags; init.parent_hws = &parent_hw; init.num_parents = 1; /* struct clk_divider assignments */ dclk->id = id; dclk->ctrl_reg = reg; dclk->shift = shift; dclk->width = width; dclk->div_flags = clk_divider_flags; dclk->priv_flag = priv_flag; dclk->lock = lock; dclk->hw.init = &init; /* register the clock */ clk_hw = &dclk->hw; ret = devm_clk_hw_register(dev, clk_hw); if (ret) { dev_err(dev, "failed to register divider clock!\n"); return ERR_PTR(ret); } return clk_hw; } EXPORT_SYMBOL_GPL(eswin_register_clkdiv); int eswin_clk_register_divider(struct device *dev, struct eswin_divider_clock *clks, int nums, struct eswin_clock_data *data) { struct clk_hw *clk_hw; int i; for (i = 0; i < nums; i++) { clk_hw = devm_clk_hw_register_divider_parent_data(dev, clks[i].name, clks[i].parent_data, clks[i].flags, data->base + clks[i].reg, clks[i].shift, clks[i].width, clks[i].div_flags, &data->lock); if (IS_ERR(clk_hw)) return PTR_ERR(clk_hw); clks[i].hw = *clk_hw; data->clk_data.hws[clks[i].id] = clk_hw; } return 0; } EXPORT_SYMBOL_GPL(eswin_clk_register_divider); int eswin_clk_register_gate(struct device *dev, struct eswin_gate_clock *clks, int nums, struct eswin_clock_data *data) { struct clk_hw *clk_hw; int i; for (i = 0; i < nums; i++) { clk_hw = devm_clk_hw_register_gate_parent_data(dev, clks[i].name, clks[i].parent_data, clks[i].flags, data->base + clks[i].reg, clks[i].bit_idx, clks[i].gate_flags, &data->lock); if (IS_ERR(clk_hw)) return PTR_ERR(clk_hw); clks[i].hw = *clk_hw; data->clk_data.hws[clks[i].id] = clk_hw; } return 0; } EXPORT_SYMBOL_GPL(eswin_clk_register_gate); int eswin_clk_register_clks(struct device *dev, struct eswin_clk_info *clks, int nums, struct eswin_clock_data *data) { struct eswin_clk_info *info; const struct clk_hw *phw = NULL; struct clk_hw *hw; int i; for (i = 0; i < nums; i++) { info = &clks[i]; switch (info->type) { case CLK_FIXED_FACTOR: { const struct eswin_fixed_factor_clock *factor; factor = &info->data.factor; phw = data->clk_data.hws[info->pid]; hw = devm_clk_hw_register_fixed_factor_parent_hw(dev, factor->name, phw, factor->flags, factor->mult, factor->div); break; } case CLK_MUX: { const struct eswin_mux_clock *mux = &info->data.mux; hw = devm_clk_hw_register_mux_parent_data_table(dev, mux->name, mux->parent_data, mux->num_parents, mux->flags, data->base + mux->reg, mux->shift, mux->width, mux->mux_flags, mux->table, &data->lock); break; } case CLK_DIVIDER: { const struct eswin_divider_clock *div = &info->data.div; phw = data->clk_data.hws[info->pid]; if (div->priv_flag) hw = eswin_register_clkdiv(dev, div->id, div->name, phw, div->flags, data->base + div->reg, div->shift, div->width, div->div_flags, div->priv_flag, &data->lock); else hw = devm_clk_hw_register_divider_parent_hw(dev, div->name, phw, div->flags, data->base + div->reg, div->shift, div->width, div->div_flags, &data->lock); break; } case CLK_GATE: { const struct eswin_gate_clock *gate = &info->data.gate; phw = data->clk_data.hws[info->pid]; hw = devm_clk_hw_register_gate_parent_hw(dev, gate->name, phw, gate->flags, data->base + gate->reg, gate->bit_idx, gate->gate_flags, &data->lock); break; } default: dev_err(dev, "Unidentifiable clock type!\n"); return -EINVAL; } if (IS_ERR(hw)) return PTR_ERR(hw); info->hw = *hw; data->clk_data.hws[info->id] = hw; } return 0; } EXPORT_SYMBOL_GPL(eswin_clk_register_clks);