diff options
| -rw-r--r-- | drivers/clk/at91/clk-programmable.c | 57 | ||||
| -rw-r--r-- | drivers/clk/at91/pmc.h | 2 | ||||
| -rw-r--r-- | drivers/clk/at91/sama5d2.c | 10 |
3 files changed, 54 insertions, 15 deletions
diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c index 89d6f3736dbf..f8edbb65eda3 100644 --- a/drivers/clk/at91/clk-programmable.c +++ b/drivers/clk/at91/clk-programmable.c | |||
| @@ -20,8 +20,7 @@ | |||
| 20 | #define PROG_ID_MAX 7 | 20 | #define PROG_ID_MAX 7 |
| 21 | 21 | ||
| 22 | #define PROG_STATUS_MASK(id) (1 << ((id) + 8)) | 22 | #define PROG_STATUS_MASK(id) (1 << ((id) + 8)) |
| 23 | #define PROG_PRES_MASK 0x7 | 23 | #define PROG_PRES(layout, pckr) ((pckr >> layout->pres_shift) & layout->pres_mask) |
| 24 | #define PROG_PRES(layout, pckr) ((pckr >> layout->pres_shift) & PROG_PRES_MASK) | ||
| 25 | #define PROG_MAX_RM9200_CSS 3 | 24 | #define PROG_MAX_RM9200_CSS 3 |
| 26 | 25 | ||
| 27 | struct clk_programmable { | 26 | struct clk_programmable { |
| @@ -37,20 +36,29 @@ static unsigned long clk_programmable_recalc_rate(struct clk_hw *hw, | |||
| 37 | unsigned long parent_rate) | 36 | unsigned long parent_rate) |
| 38 | { | 37 | { |
| 39 | struct clk_programmable *prog = to_clk_programmable(hw); | 38 | struct clk_programmable *prog = to_clk_programmable(hw); |
| 39 | const struct clk_programmable_layout *layout = prog->layout; | ||
| 40 | unsigned int pckr; | 40 | unsigned int pckr; |
| 41 | unsigned long rate; | ||
| 41 | 42 | ||
| 42 | regmap_read(prog->regmap, AT91_PMC_PCKR(prog->id), &pckr); | 43 | regmap_read(prog->regmap, AT91_PMC_PCKR(prog->id), &pckr); |
| 43 | 44 | ||
| 44 | return parent_rate >> PROG_PRES(prog->layout, pckr); | 45 | if (layout->is_pres_direct) |
| 46 | rate = parent_rate / (PROG_PRES(layout, pckr) + 1); | ||
| 47 | else | ||
| 48 | rate = parent_rate >> PROG_PRES(layout, pckr); | ||
| 49 | |||
| 50 | return rate; | ||
| 45 | } | 51 | } |
| 46 | 52 | ||
| 47 | static int clk_programmable_determine_rate(struct clk_hw *hw, | 53 | static int clk_programmable_determine_rate(struct clk_hw *hw, |
| 48 | struct clk_rate_request *req) | 54 | struct clk_rate_request *req) |
| 49 | { | 55 | { |
| 56 | struct clk_programmable *prog = to_clk_programmable(hw); | ||
| 57 | const struct clk_programmable_layout *layout = prog->layout; | ||
| 50 | struct clk_hw *parent; | 58 | struct clk_hw *parent; |
| 51 | long best_rate = -EINVAL; | 59 | long best_rate = -EINVAL; |
| 52 | unsigned long parent_rate; | 60 | unsigned long parent_rate; |
| 53 | unsigned long tmp_rate; | 61 | unsigned long tmp_rate = 0; |
| 54 | int shift; | 62 | int shift; |
| 55 | int i; | 63 | int i; |
| 56 | 64 | ||
| @@ -60,10 +68,18 @@ static int clk_programmable_determine_rate(struct clk_hw *hw, | |||
| 60 | continue; | 68 | continue; |
| 61 | 69 | ||
| 62 | parent_rate = clk_hw_get_rate(parent); | 70 | parent_rate = clk_hw_get_rate(parent); |
| 63 | for (shift = 0; shift < PROG_PRES_MASK; shift++) { | 71 | if (layout->is_pres_direct) { |
| 64 | tmp_rate = parent_rate >> shift; | 72 | for (shift = 0; shift <= layout->pres_mask; shift++) { |
| 65 | if (tmp_rate <= req->rate) | 73 | tmp_rate = parent_rate / (shift + 1); |
| 66 | break; | 74 | if (tmp_rate <= req->rate) |
| 75 | break; | ||
| 76 | } | ||
| 77 | } else { | ||
| 78 | for (shift = 0; shift < layout->pres_mask; shift++) { | ||
| 79 | tmp_rate = parent_rate >> shift; | ||
| 80 | if (tmp_rate <= req->rate) | ||
| 81 | break; | ||
| 82 | } | ||
| 67 | } | 83 | } |
| 68 | 84 | ||
| 69 | if (tmp_rate > req->rate) | 85 | if (tmp_rate > req->rate) |
| @@ -137,16 +153,23 @@ static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate, | |||
| 137 | if (!div) | 153 | if (!div) |
| 138 | return -EINVAL; | 154 | return -EINVAL; |
| 139 | 155 | ||
| 140 | shift = fls(div) - 1; | 156 | if (layout->is_pres_direct) { |
| 157 | shift = div - 1; | ||
| 141 | 158 | ||
| 142 | if (div != (1 << shift)) | 159 | if (shift > layout->pres_mask) |
| 143 | return -EINVAL; | 160 | return -EINVAL; |
| 161 | } else { | ||
| 162 | shift = fls(div) - 1; | ||
| 144 | 163 | ||
| 145 | if (shift >= PROG_PRES_MASK) | 164 | if (div != (1 << shift)) |
| 146 | return -EINVAL; | 165 | return -EINVAL; |
| 166 | |||
| 167 | if (shift >= layout->pres_mask) | ||
| 168 | return -EINVAL; | ||
| 169 | } | ||
| 147 | 170 | ||
| 148 | regmap_update_bits(prog->regmap, AT91_PMC_PCKR(prog->id), | 171 | regmap_update_bits(prog->regmap, AT91_PMC_PCKR(prog->id), |
| 149 | PROG_PRES_MASK << layout->pres_shift, | 172 | layout->pres_mask << layout->pres_shift, |
| 150 | shift << layout->pres_shift); | 173 | shift << layout->pres_shift); |
| 151 | 174 | ||
| 152 | return 0; | 175 | return 0; |
| @@ -202,19 +225,25 @@ at91_clk_register_programmable(struct regmap *regmap, | |||
| 202 | } | 225 | } |
| 203 | 226 | ||
| 204 | const struct clk_programmable_layout at91rm9200_programmable_layout = { | 227 | const struct clk_programmable_layout at91rm9200_programmable_layout = { |
| 228 | .pres_mask = 0x7, | ||
| 205 | .pres_shift = 2, | 229 | .pres_shift = 2, |
| 206 | .css_mask = 0x3, | 230 | .css_mask = 0x3, |
| 207 | .have_slck_mck = 0, | 231 | .have_slck_mck = 0, |
| 232 | .is_pres_direct = 0, | ||
| 208 | }; | 233 | }; |
| 209 | 234 | ||
| 210 | const struct clk_programmable_layout at91sam9g45_programmable_layout = { | 235 | const struct clk_programmable_layout at91sam9g45_programmable_layout = { |
| 236 | .pres_mask = 0x7, | ||
| 211 | .pres_shift = 2, | 237 | .pres_shift = 2, |
| 212 | .css_mask = 0x3, | 238 | .css_mask = 0x3, |
| 213 | .have_slck_mck = 1, | 239 | .have_slck_mck = 1, |
| 240 | .is_pres_direct = 0, | ||
| 214 | }; | 241 | }; |
| 215 | 242 | ||
| 216 | const struct clk_programmable_layout at91sam9x5_programmable_layout = { | 243 | const struct clk_programmable_layout at91sam9x5_programmable_layout = { |
| 244 | .pres_mask = 0x7, | ||
| 217 | .pres_shift = 4, | 245 | .pres_shift = 4, |
| 218 | .css_mask = 0x7, | 246 | .css_mask = 0x7, |
| 219 | .have_slck_mck = 0, | 247 | .have_slck_mck = 0, |
| 248 | .is_pres_direct = 0, | ||
| 220 | }; | 249 | }; |
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index 672a79bda88c..a0e5ce9c9b9e 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h | |||
| @@ -71,9 +71,11 @@ struct clk_pll_characteristics { | |||
| 71 | }; | 71 | }; |
| 72 | 72 | ||
| 73 | struct clk_programmable_layout { | 73 | struct clk_programmable_layout { |
| 74 | u8 pres_mask; | ||
| 74 | u8 pres_shift; | 75 | u8 pres_shift; |
| 75 | u8 css_mask; | 76 | u8 css_mask; |
| 76 | u8 have_slck_mck; | 77 | u8 have_slck_mck; |
| 78 | u8 is_pres_direct; | ||
| 77 | }; | 79 | }; |
| 78 | 80 | ||
| 79 | extern const struct clk_programmable_layout at91rm9200_programmable_layout; | 81 | extern const struct clk_programmable_layout at91rm9200_programmable_layout; |
diff --git a/drivers/clk/at91/sama5d2.c b/drivers/clk/at91/sama5d2.c index 1f70cb164b06..81943fac4537 100644 --- a/drivers/clk/at91/sama5d2.c +++ b/drivers/clk/at91/sama5d2.c | |||
| @@ -125,6 +125,14 @@ static const struct { | |||
| 125 | .pll = true }, | 125 | .pll = true }, |
| 126 | }; | 126 | }; |
| 127 | 127 | ||
| 128 | static const struct clk_programmable_layout sama5d2_programmable_layout = { | ||
| 129 | .pres_mask = 0xff, | ||
| 130 | .pres_shift = 4, | ||
| 131 | .css_mask = 0x7, | ||
| 132 | .have_slck_mck = 0, | ||
| 133 | .is_pres_direct = 1, | ||
| 134 | }; | ||
| 135 | |||
| 128 | static void __init sama5d2_pmc_setup(struct device_node *np) | 136 | static void __init sama5d2_pmc_setup(struct device_node *np) |
| 129 | { | 137 | { |
| 130 | struct clk_range range = CLK_RANGE(0, 0); | 138 | struct clk_range range = CLK_RANGE(0, 0); |
| @@ -249,7 +257,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np) | |||
| 249 | 257 | ||
| 250 | hw = at91_clk_register_programmable(regmap, name, | 258 | hw = at91_clk_register_programmable(regmap, name, |
| 251 | parent_names, 6, i, | 259 | parent_names, 6, i, |
| 252 | &at91sam9x5_programmable_layout); | 260 | &sama5d2_programmable_layout); |
| 253 | if (IS_ERR(hw)) | 261 | if (IS_ERR(hw)) |
| 254 | goto err_free; | 262 | goto err_free; |
| 255 | } | 263 | } |
