1 /* 2 * Copyright (C) 2016 Maxime Ripard 3 * Maxime Ripard <maxime.ripard@free-electrons.com> 4 * 5 * This program is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU General Public License as 7 * published by the Free Software Foundation; either version 2 of 8 * the License, or (at your option) any later version. 9 */ 10 11 #include <linux/clk-provider.h> 12 13 #include "ccu_gate.h" 14 #include "ccu_mult.h" 15 16 static void ccu_mult_find_best(unsigned long parent, unsigned long rate, 17 unsigned int max_n, unsigned int *n) 18 { 19 *n = rate / parent; 20 } 21 22 static unsigned long ccu_mult_round_rate(struct ccu_mux_internal *mux, 23 unsigned long parent_rate, 24 unsigned long rate, 25 void *data) 26 { 27 struct ccu_mult *cm = data; 28 unsigned int n; 29 30 ccu_mult_find_best(parent_rate, rate, 1 << cm->mult.width, &n); 31 32 return parent_rate * n; 33 } 34 35 static void ccu_mult_disable(struct clk_hw *hw) 36 { 37 struct ccu_mult *cm = hw_to_ccu_mult(hw); 38 39 return ccu_gate_helper_disable(&cm->common, cm->enable); 40 } 41 42 static int ccu_mult_enable(struct clk_hw *hw) 43 { 44 struct ccu_mult *cm = hw_to_ccu_mult(hw); 45 46 return ccu_gate_helper_enable(&cm->common, cm->enable); 47 } 48 49 static int ccu_mult_is_enabled(struct clk_hw *hw) 50 { 51 struct ccu_mult *cm = hw_to_ccu_mult(hw); 52 53 return ccu_gate_helper_is_enabled(&cm->common, cm->enable); 54 } 55 56 static unsigned long ccu_mult_recalc_rate(struct clk_hw *hw, 57 unsigned long parent_rate) 58 { 59 struct ccu_mult *cm = hw_to_ccu_mult(hw); 60 unsigned long val; 61 u32 reg; 62 63 reg = readl(cm->common.base + cm->common.reg); 64 val = reg >> cm->mult.shift; 65 val &= (1 << cm->mult.width) - 1; 66 67 ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1, 68 &parent_rate); 69 70 return parent_rate * (val + 1); 71 } 72 73 static int ccu_mult_determine_rate(struct clk_hw *hw, 74 struct clk_rate_request *req) 75 { 76 struct ccu_mult *cm = hw_to_ccu_mult(hw); 77 78 return ccu_mux_helper_determine_rate(&cm->common, &cm->mux, 79 req, ccu_mult_round_rate, cm); 80 } 81 82 static int ccu_mult_set_rate(struct clk_hw *hw, unsigned long rate, 83 unsigned long parent_rate) 84 { 85 struct ccu_mult *cm = hw_to_ccu_mult(hw); 86 unsigned long flags; 87 unsigned int n; 88 u32 reg; 89 90 ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1, 91 &parent_rate); 92 93 ccu_mult_find_best(parent_rate, rate, 1 << cm->mult.width, &n); 94 95 spin_lock_irqsave(cm->common.lock, flags); 96 97 reg = readl(cm->common.base + cm->common.reg); 98 reg &= ~GENMASK(cm->mult.width + cm->mult.shift - 1, cm->mult.shift); 99 100 writel(reg | ((n - 1) << cm->mult.shift), 101 cm->common.base + cm->common.reg); 102 103 spin_unlock_irqrestore(cm->common.lock, flags); 104 105 return 0; 106 } 107 108 static u8 ccu_mult_get_parent(struct clk_hw *hw) 109 { 110 struct ccu_mult *cm = hw_to_ccu_mult(hw); 111 112 return ccu_mux_helper_get_parent(&cm->common, &cm->mux); 113 } 114 115 static int ccu_mult_set_parent(struct clk_hw *hw, u8 index) 116 { 117 struct ccu_mult *cm = hw_to_ccu_mult(hw); 118 119 return ccu_mux_helper_set_parent(&cm->common, &cm->mux, index); 120 } 121 122 const struct clk_ops ccu_mult_ops = { 123 .disable = ccu_mult_disable, 124 .enable = ccu_mult_enable, 125 .is_enabled = ccu_mult_is_enabled, 126 127 .get_parent = ccu_mult_get_parent, 128 .set_parent = ccu_mult_set_parent, 129 130 .determine_rate = ccu_mult_determine_rate, 131 .recalc_rate = ccu_mult_recalc_rate, 132 .set_rate = ccu_mult_set_rate, 133 }; 134