1/* 2 * Copyright (C) 2014 Intel Corporation 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 * 8 * Adjustable fractional divider clock implementation. 9 * Output rate = (m / n) * parent_rate. 10 */ 11 12#include <linux/clk-provider.h> 13#include <linux/module.h> 14#include <linux/device.h> 15#include <linux/slab.h> 16#include <linux/gcd.h> 17 18#define to_clk_fd(_hw) container_of(_hw, struct clk_fractional_divider, hw) 19 20static unsigned long clk_fd_recalc_rate(struct clk_hw *hw, 21 unsigned long parent_rate) 22{ 23 struct clk_fractional_divider *fd = to_clk_fd(hw); 24 unsigned long flags = 0; 25 u32 val, m, n; 26 u64 ret; 27 28 if (fd->lock) 29 spin_lock_irqsave(fd->lock, flags); 30 31 val = clk_readl(fd->reg); 32 33 if (fd->lock) 34 spin_unlock_irqrestore(fd->lock, flags); 35 36 m = (val & fd->mmask) >> fd->mshift; 37 n = (val & fd->nmask) >> fd->nshift; 38 39 if (!n || !m) 40 return parent_rate; 41 42 ret = (u64)parent_rate * m; 43 do_div(ret, n); 44 45 return ret; 46} 47 48static long clk_fd_round_rate(struct clk_hw *hw, unsigned long rate, 49 unsigned long *prate) 50{ 51 struct clk_fractional_divider *fd = to_clk_fd(hw); 52 unsigned maxn = (fd->nmask >> fd->nshift) + 1; 53 unsigned div; 54 55 if (!rate || rate >= *prate) 56 return *prate; 57 58 div = gcd(*prate, rate); 59 60 while ((*prate / div) > maxn) { 61 div <<= 1; 62 rate <<= 1; 63 } 64 65 return rate; 66} 67 68static int clk_fd_set_rate(struct clk_hw *hw, unsigned long rate, 69 unsigned long parent_rate) 70{ 71 struct clk_fractional_divider *fd = to_clk_fd(hw); 72 unsigned long flags = 0; 73 unsigned long div; 74 unsigned n, m; 75 u32 val; 76 77 div = gcd(parent_rate, rate); 78 m = rate / div; 79 n = parent_rate / div; 80 81 if (fd->lock) 82 spin_lock_irqsave(fd->lock, flags); 83 84 val = clk_readl(fd->reg); 85 val &= ~(fd->mmask | fd->nmask); 86 val |= (m << fd->mshift) | (n << fd->nshift); 87 clk_writel(val, fd->reg); 88 89 if (fd->lock) 90 spin_unlock_irqrestore(fd->lock, flags); 91 92 return 0; 93} 94 95const struct clk_ops clk_fractional_divider_ops = { 96 .recalc_rate = clk_fd_recalc_rate, 97 .round_rate = clk_fd_round_rate, 98 .set_rate = clk_fd_set_rate, 99}; 100EXPORT_SYMBOL_GPL(clk_fractional_divider_ops); 101 102struct clk *clk_register_fractional_divider(struct device *dev, 103 const char *name, const char *parent_name, unsigned long flags, 104 void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth, 105 u8 clk_divider_flags, spinlock_t *lock) 106{ 107 struct clk_fractional_divider *fd; 108 struct clk_init_data init; 109 struct clk *clk; 110 111 fd = kzalloc(sizeof(*fd), GFP_KERNEL); 112 if (!fd) { 113 dev_err(dev, "could not allocate fractional divider clk\n"); 114 return ERR_PTR(-ENOMEM); 115 } 116 117 init.name = name; 118 init.ops = &clk_fractional_divider_ops; 119 init.flags = flags | CLK_IS_BASIC; 120 init.parent_names = parent_name ? &parent_name : NULL; 121 init.num_parents = parent_name ? 1 : 0; 122 123 fd->reg = reg; 124 fd->mshift = mshift; 125 fd->mmask = (BIT(mwidth) - 1) << mshift; 126 fd->nshift = nshift; 127 fd->nmask = (BIT(nwidth) - 1) << nshift; 128 fd->flags = clk_divider_flags; 129 fd->lock = lock; 130 fd->hw.init = &init; 131 132 clk = clk_register(dev, &fd->hw); 133 if (IS_ERR(clk)) 134 kfree(fd); 135 136 return clk; 137} 138EXPORT_SYMBOL_GPL(clk_register_fractional_divider); 139