^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Copyright (C) 2013 Freescale Semiconductor, Inc.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) #include <linux/clk-provider.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) #include <linux/err.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include "clk.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #define div_mask(d) ((1 << (d->width)) - 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) * struct clk_fixup_div - imx integer fixup divider clock
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) * @divider: the parent class
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) * @ops: pointer to clk_ops of parent class
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) * @fixup: a hook to fixup the write value
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) * The imx fixup divider clock is a subclass of basic clk_divider
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) * with an addtional fixup hook.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) struct clk_fixup_div {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) struct clk_divider divider;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) const struct clk_ops *ops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) void (*fixup)(u32 *val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) static inline struct clk_fixup_div *to_clk_fixup_div(struct clk_hw *hw)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) struct clk_divider *divider = to_clk_divider(hw);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) return container_of(divider, struct clk_fixup_div, divider);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) static unsigned long clk_fixup_div_recalc_rate(struct clk_hw *hw,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) unsigned long parent_rate)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) struct clk_fixup_div *fixup_div = to_clk_fixup_div(hw);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) return fixup_div->ops->recalc_rate(&fixup_div->divider.hw, parent_rate);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) static long clk_fixup_div_round_rate(struct clk_hw *hw, unsigned long rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) unsigned long *prate)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) struct clk_fixup_div *fixup_div = to_clk_fixup_div(hw);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) return fixup_div->ops->round_rate(&fixup_div->divider.hw, rate, prate);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) static int clk_fixup_div_set_rate(struct clk_hw *hw, unsigned long rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) unsigned long parent_rate)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) struct clk_fixup_div *fixup_div = to_clk_fixup_div(hw);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) struct clk_divider *div = to_clk_divider(hw);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) unsigned int divider, value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) unsigned long flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) u32 val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) divider = parent_rate / rate;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) /* Zero based divider */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) value = divider - 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) if (value > div_mask(div))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) value = div_mask(div);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) spin_lock_irqsave(div->lock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) val = readl(div->reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) val &= ~(div_mask(div) << div->shift);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) val |= value << div->shift;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) fixup_div->fixup(&val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) writel(val, div->reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) spin_unlock_irqrestore(div->lock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) static const struct clk_ops clk_fixup_div_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) .recalc_rate = clk_fixup_div_recalc_rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) .round_rate = clk_fixup_div_round_rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) .set_rate = clk_fixup_div_set_rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) struct clk_hw *imx_clk_hw_fixup_divider(const char *name, const char *parent,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) void __iomem *reg, u8 shift, u8 width,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) void (*fixup)(u32 *val))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) struct clk_fixup_div *fixup_div;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) struct clk_hw *hw;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) struct clk_init_data init;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) if (!fixup)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) return ERR_PTR(-EINVAL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) fixup_div = kzalloc(sizeof(*fixup_div), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) if (!fixup_div)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) return ERR_PTR(-ENOMEM);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) init.name = name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) init.ops = &clk_fixup_div_ops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) init.flags = CLK_SET_RATE_PARENT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) init.parent_names = parent ? &parent : NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) init.num_parents = parent ? 1 : 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) fixup_div->divider.reg = reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) fixup_div->divider.shift = shift;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) fixup_div->divider.width = width;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) fixup_div->divider.lock = &imx_ccm_lock;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) fixup_div->divider.hw.init = &init;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) fixup_div->ops = &clk_divider_ops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) fixup_div->fixup = fixup;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) hw = &fixup_div->divider.hw;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) ret = clk_hw_register(NULL, hw);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) kfree(fixup_div);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) return ERR_PTR(ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) return hw;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) }