1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 272ea4861SBintian Wang /* 372ea4861SBintian Wang * Hisilicon hi6220 SoC divider clock driver 472ea4861SBintian Wang * 572ea4861SBintian Wang * Copyright (c) 2015 Hisilicon Limited. 672ea4861SBintian Wang * 772ea4861SBintian Wang * Author: Bintian Wang <bintian.wang@huawei.com> 872ea4861SBintian Wang */ 972ea4861SBintian Wang 1072ea4861SBintian Wang #include <linux/kernel.h> 1172ea4861SBintian Wang #include <linux/clk-provider.h> 1272ea4861SBintian Wang #include <linux/slab.h> 1372ea4861SBintian Wang #include <linux/io.h> 1472ea4861SBintian Wang #include <linux/err.h> 1572ea4861SBintian Wang #include <linux/spinlock.h> 1672ea4861SBintian Wang 17b62c190fSBen Dooks #include "clk.h" 18b62c190fSBen Dooks 1972ea4861SBintian Wang #define div_mask(width) ((1 << (width)) - 1) 2072ea4861SBintian Wang 2172ea4861SBintian Wang /** 2272ea4861SBintian Wang * struct hi6220_clk_divider - divider clock for hi6220 2372ea4861SBintian Wang * 2472ea4861SBintian Wang * @hw: handle between common and hardware-specific interfaces 2572ea4861SBintian Wang * @reg: register containing divider 2672ea4861SBintian Wang * @shift: shift to the divider bit field 2772ea4861SBintian Wang * @width: width of the divider bit field 2872ea4861SBintian Wang * @mask: mask for setting divider rate 2972ea4861SBintian Wang * @table: the div table that the divider supports 3072ea4861SBintian Wang * @lock: register lock 3172ea4861SBintian Wang */ 3272ea4861SBintian Wang struct hi6220_clk_divider { 3372ea4861SBintian Wang struct clk_hw hw; 3472ea4861SBintian Wang void __iomem *reg; 3572ea4861SBintian Wang u8 shift; 3672ea4861SBintian Wang u8 width; 3772ea4861SBintian Wang u32 mask; 3872ea4861SBintian Wang const struct clk_div_table *table; 3972ea4861SBintian Wang spinlock_t *lock; 4072ea4861SBintian Wang }; 4172ea4861SBintian Wang 4272ea4861SBintian Wang #define to_hi6220_clk_divider(_hw) \ 4372ea4861SBintian Wang container_of(_hw, struct hi6220_clk_divider, hw) 4472ea4861SBintian Wang 4572ea4861SBintian Wang static unsigned long hi6220_clkdiv_recalc_rate(struct clk_hw *hw, 4672ea4861SBintian Wang unsigned long parent_rate) 4772ea4861SBintian Wang { 4872ea4861SBintian Wang unsigned int val; 4972ea4861SBintian Wang struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw); 5072ea4861SBintian Wang 5172ea4861SBintian Wang val = readl_relaxed(dclk->reg) >> dclk->shift; 5272ea4861SBintian Wang val &= div_mask(dclk->width); 5372ea4861SBintian Wang 5472ea4861SBintian Wang return divider_recalc_rate(hw, parent_rate, val, dclk->table, 5512a26c29SJerome Brunet CLK_DIVIDER_ROUND_CLOSEST, dclk->width); 5672ea4861SBintian Wang } 5772ea4861SBintian Wang 5872ea4861SBintian Wang static long hi6220_clkdiv_round_rate(struct clk_hw *hw, unsigned long rate, 5972ea4861SBintian Wang unsigned long *prate) 6072ea4861SBintian Wang { 6172ea4861SBintian Wang struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw); 6272ea4861SBintian Wang 6372ea4861SBintian Wang return divider_round_rate(hw, rate, prate, dclk->table, 6472ea4861SBintian Wang dclk->width, CLK_DIVIDER_ROUND_CLOSEST); 6572ea4861SBintian Wang } 6672ea4861SBintian Wang 6772ea4861SBintian Wang static int hi6220_clkdiv_set_rate(struct clk_hw *hw, unsigned long rate, 6872ea4861SBintian Wang unsigned long parent_rate) 6972ea4861SBintian Wang { 7072ea4861SBintian Wang int value; 7172ea4861SBintian Wang unsigned long flags = 0; 7272ea4861SBintian Wang u32 data; 7372ea4861SBintian Wang struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw); 7472ea4861SBintian Wang 7572ea4861SBintian Wang value = divider_get_val(rate, parent_rate, dclk->table, 7672ea4861SBintian Wang dclk->width, CLK_DIVIDER_ROUND_CLOSEST); 7772ea4861SBintian Wang 7872ea4861SBintian Wang if (dclk->lock) 7972ea4861SBintian Wang spin_lock_irqsave(dclk->lock, flags); 8072ea4861SBintian Wang 8172ea4861SBintian Wang data = readl_relaxed(dclk->reg); 8272ea4861SBintian Wang data &= ~(div_mask(dclk->width) << dclk->shift); 8372ea4861SBintian Wang data |= value << dclk->shift; 8472ea4861SBintian Wang data |= dclk->mask; 8572ea4861SBintian Wang 8672ea4861SBintian Wang writel_relaxed(data, dclk->reg); 8772ea4861SBintian Wang 8872ea4861SBintian Wang if (dclk->lock) 8972ea4861SBintian Wang spin_unlock_irqrestore(dclk->lock, flags); 9072ea4861SBintian Wang 9172ea4861SBintian Wang return 0; 9272ea4861SBintian Wang } 9372ea4861SBintian Wang 9472ea4861SBintian Wang static const struct clk_ops hi6220_clkdiv_ops = { 9572ea4861SBintian Wang .recalc_rate = hi6220_clkdiv_recalc_rate, 9672ea4861SBintian Wang .round_rate = hi6220_clkdiv_round_rate, 9772ea4861SBintian Wang .set_rate = hi6220_clkdiv_set_rate, 9872ea4861SBintian Wang }; 9972ea4861SBintian Wang 10072ea4861SBintian Wang struct clk *hi6220_register_clkdiv(struct device *dev, const char *name, 10172ea4861SBintian Wang const char *parent_name, unsigned long flags, void __iomem *reg, 10272ea4861SBintian Wang u8 shift, u8 width, u32 mask_bit, spinlock_t *lock) 10372ea4861SBintian Wang { 10472ea4861SBintian Wang struct hi6220_clk_divider *div; 10572ea4861SBintian Wang struct clk *clk; 10672ea4861SBintian Wang struct clk_init_data init; 10772ea4861SBintian Wang struct clk_div_table *table; 10872ea4861SBintian Wang u32 max_div, min_div; 10972ea4861SBintian Wang int i; 11072ea4861SBintian Wang 11172ea4861SBintian Wang /* allocate the divider */ 11272ea4861SBintian Wang div = kzalloc(sizeof(*div), GFP_KERNEL); 11372ea4861SBintian Wang if (!div) 11472ea4861SBintian Wang return ERR_PTR(-ENOMEM); 11572ea4861SBintian Wang 11672ea4861SBintian Wang /* Init the divider table */ 11772ea4861SBintian Wang max_div = div_mask(width) + 1; 11872ea4861SBintian Wang min_div = 1; 11972ea4861SBintian Wang 12072ea4861SBintian Wang table = kcalloc(max_div + 1, sizeof(*table), GFP_KERNEL); 12172ea4861SBintian Wang if (!table) { 12272ea4861SBintian Wang kfree(div); 12372ea4861SBintian Wang return ERR_PTR(-ENOMEM); 12472ea4861SBintian Wang } 12572ea4861SBintian Wang 12672ea4861SBintian Wang for (i = 0; i < max_div; i++) { 12772ea4861SBintian Wang table[i].div = min_div + i; 12872ea4861SBintian Wang table[i].val = table[i].div - 1; 12972ea4861SBintian Wang } 13072ea4861SBintian Wang 13172ea4861SBintian Wang init.name = name; 13272ea4861SBintian Wang init.ops = &hi6220_clkdiv_ops; 13372ea4861SBintian Wang init.flags = flags; 13472ea4861SBintian Wang init.parent_names = parent_name ? &parent_name : NULL; 13572ea4861SBintian Wang init.num_parents = parent_name ? 1 : 0; 13672ea4861SBintian Wang 13772ea4861SBintian Wang /* struct hi6220_clk_divider assignments */ 13872ea4861SBintian Wang div->reg = reg; 13972ea4861SBintian Wang div->shift = shift; 14072ea4861SBintian Wang div->width = width; 14172ea4861SBintian Wang div->mask = mask_bit ? BIT(mask_bit) : 0; 14272ea4861SBintian Wang div->lock = lock; 14372ea4861SBintian Wang div->hw.init = &init; 14472ea4861SBintian Wang div->table = table; 14572ea4861SBintian Wang 14672ea4861SBintian Wang /* register the clock */ 14772ea4861SBintian Wang clk = clk_register(dev, &div->hw); 14872ea4861SBintian Wang if (IS_ERR(clk)) { 14972ea4861SBintian Wang kfree(table); 15072ea4861SBintian Wang kfree(div); 15172ea4861SBintian Wang } 15272ea4861SBintian Wang 15372ea4861SBintian Wang return clk; 15472ea4861SBintian Wang } 155