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