^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Hisilicon hi6220 SoC divider clock driver
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (c) 2015 Hisilicon Limited.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Author: Bintian Wang <bintian.wang@huawei.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/clk-provider.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/err.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/spinlock.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include "clk.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #define div_mask(width) ((1 << (width)) - 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) * struct hi6220_clk_divider - divider clock for hi6220
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) * @hw: handle between common and hardware-specific interfaces
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) * @reg: register containing divider
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) * @shift: shift to the divider bit field
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) * @width: width of the divider bit field
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) * @mask: mask for setting divider rate
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) * @table: the div table that the divider supports
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) * @lock: register lock
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) struct hi6220_clk_divider {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) struct clk_hw hw;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) void __iomem *reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) u8 shift;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) u8 width;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) u32 mask;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) const struct clk_div_table *table;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) spinlock_t *lock;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) #define to_hi6220_clk_divider(_hw) \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) container_of(_hw, struct hi6220_clk_divider, hw)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) static unsigned long hi6220_clkdiv_recalc_rate(struct clk_hw *hw,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) unsigned long parent_rate)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) unsigned int val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) val = readl_relaxed(dclk->reg) >> dclk->shift;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) val &= div_mask(dclk->width);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) return divider_recalc_rate(hw, parent_rate, val, dclk->table,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) CLK_DIVIDER_ROUND_CLOSEST, dclk->width);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) static long hi6220_clkdiv_round_rate(struct clk_hw *hw, unsigned long rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) unsigned long *prate)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) return divider_round_rate(hw, rate, prate, dclk->table,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) dclk->width, CLK_DIVIDER_ROUND_CLOSEST);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) static int hi6220_clkdiv_set_rate(struct clk_hw *hw, unsigned long rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) unsigned long parent_rate)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) int value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) unsigned long flags = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) u32 data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) value = divider_get_val(rate, parent_rate, dclk->table,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) dclk->width, CLK_DIVIDER_ROUND_CLOSEST);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) if (dclk->lock)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) spin_lock_irqsave(dclk->lock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) data = readl_relaxed(dclk->reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) data &= ~(div_mask(dclk->width) << dclk->shift);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) data |= value << dclk->shift;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) data |= dclk->mask;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) writel_relaxed(data, dclk->reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) if (dclk->lock)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) spin_unlock_irqrestore(dclk->lock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) static const struct clk_ops hi6220_clkdiv_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) .recalc_rate = hi6220_clkdiv_recalc_rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) .round_rate = hi6220_clkdiv_round_rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) .set_rate = hi6220_clkdiv_set_rate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) struct clk *hi6220_register_clkdiv(struct device *dev, const char *name,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) const char *parent_name, unsigned long flags, void __iomem *reg,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) u8 shift, u8 width, u32 mask_bit, spinlock_t *lock)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) struct hi6220_clk_divider *div;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) struct clk *clk;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) struct clk_init_data init;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) struct clk_div_table *table;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) u32 max_div, min_div;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) /* allocate the divider */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) div = kzalloc(sizeof(*div), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) if (!div)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) return ERR_PTR(-ENOMEM);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) /* Init the divider table */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) max_div = div_mask(width) + 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) min_div = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) table = kcalloc(max_div + 1, sizeof(*table), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) if (!table) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) kfree(div);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) return ERR_PTR(-ENOMEM);
^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) for (i = 0; i < max_div; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) table[i].div = min_div + i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) table[i].val = table[i].div - 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) init.name = name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) init.ops = &hi6220_clkdiv_ops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) init.flags = flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) init.parent_names = parent_name ? &parent_name : NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) init.num_parents = parent_name ? 1 : 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) /* struct hi6220_clk_divider assignments */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) div->reg = reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) div->shift = shift;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) div->width = width;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) div->mask = mask_bit ? BIT(mask_bit) : 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) div->lock = lock;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) div->hw.init = &init;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) div->table = table;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) /* register the clock */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) clk = clk_register(dev, &div->hw);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) if (IS_ERR(clk)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) kfree(table);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) kfree(div);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) return clk;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) }