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