Commit 7e7b8ca66191c5dde9f6521ff8a0180834efa628
Committed by
Kishon Vijay Abraham I
1 parent
71e2f5c5c2
phy: ti: am654-serdes: Support all clksel values
Add support to select all 16 CLKSEL combinations that are shown in "SerDes Reference Clock Distribution" in AM65 TRM. Signed-off-by: Roger Quadros <rogerq@ti.com> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
Showing 1 changed file with 83 additions and 49 deletions Side-by-side Diff
drivers/phy/ti/phy-am654-serdes.c
... | ... | @@ -58,13 +58,14 @@ |
58 | 58 | |
59 | 59 | #define SERDES_NUM_CLOCKS 3 |
60 | 60 | |
61 | +#define AM654_SERDES_CTRL_CLKSEL_MASK GENMASK(7, 4) | |
62 | +#define AM654_SERDES_CTRL_CLKSEL_SHIFT 4 | |
63 | + | |
61 | 64 | struct serdes_am654_clk_mux { |
62 | 65 | struct clk_hw hw; |
63 | 66 | struct regmap *regmap; |
64 | 67 | unsigned int reg; |
65 | - int *table; | |
66 | - u32 mask; | |
67 | - u8 shift; | |
68 | + int clk_id; | |
68 | 69 | struct clk_init_data clk_data; |
69 | 70 | }; |
70 | 71 | |
71 | 72 | |
72 | 73 | |
73 | 74 | |
74 | 75 | |
... | ... | @@ -282,31 +283,52 @@ |
282 | 283 | .owner = THIS_MODULE, |
283 | 284 | }; |
284 | 285 | |
286 | +#define SERDES_NUM_MUX_COMBINATIONS 16 | |
287 | + | |
288 | +#define LICLK 0 | |
289 | +#define EXT_REFCLK 1 | |
290 | +#define RICLK 2 | |
291 | + | |
292 | +static const int | |
293 | +serdes_am654_mux_table[SERDES_NUM_MUX_COMBINATIONS][SERDES_NUM_CLOCKS] = { | |
294 | + /* | |
295 | + * Each combination maps to one of | |
296 | + * "Figure 12-1986. SerDes Reference Clock Distribution" | |
297 | + * in TRM. | |
298 | + */ | |
299 | + /* Parent of CMU refclk, Left output, Right output | |
300 | + * either of EXT_REFCLK, LICLK, RICLK | |
301 | + */ | |
302 | + { EXT_REFCLK, EXT_REFCLK, EXT_REFCLK }, /* 0000 */ | |
303 | + { RICLK, EXT_REFCLK, EXT_REFCLK }, /* 0001 */ | |
304 | + { EXT_REFCLK, RICLK, LICLK }, /* 0010 */ | |
305 | + { RICLK, RICLK, EXT_REFCLK }, /* 0011 */ | |
306 | + { LICLK, EXT_REFCLK, EXT_REFCLK }, /* 0100 */ | |
307 | + { EXT_REFCLK, EXT_REFCLK, EXT_REFCLK }, /* 0101 */ | |
308 | + { LICLK, RICLK, LICLK }, /* 0110 */ | |
309 | + { EXT_REFCLK, RICLK, LICLK }, /* 0111 */ | |
310 | + { EXT_REFCLK, EXT_REFCLK, LICLK }, /* 1000 */ | |
311 | + { RICLK, EXT_REFCLK, LICLK }, /* 1001 */ | |
312 | + { EXT_REFCLK, RICLK, EXT_REFCLK }, /* 1010 */ | |
313 | + { RICLK, RICLK, EXT_REFCLK }, /* 1011 */ | |
314 | + { LICLK, EXT_REFCLK, LICLK }, /* 1100 */ | |
315 | + { EXT_REFCLK, EXT_REFCLK, LICLK }, /* 1101 */ | |
316 | + { LICLK, RICLK, EXT_REFCLK }, /* 1110 */ | |
317 | + { EXT_REFCLK, RICLK, EXT_REFCLK }, /* 1111 */ | |
318 | +}; | |
319 | + | |
285 | 320 | static u8 serdes_am654_clk_mux_get_parent(struct clk_hw *hw) |
286 | 321 | { |
287 | 322 | struct serdes_am654_clk_mux *mux = to_serdes_am654_clk_mux(hw); |
288 | - unsigned int num_parents = clk_hw_get_num_parents(hw); | |
289 | 323 | struct regmap *regmap = mux->regmap; |
290 | 324 | unsigned int reg = mux->reg; |
291 | 325 | unsigned int val; |
292 | - int i; | |
293 | 326 | |
294 | 327 | regmap_read(regmap, reg, &val); |
295 | - val >>= mux->shift; | |
296 | - val &= mux->mask; | |
328 | + val &= AM654_SERDES_CTRL_CLKSEL_MASK; | |
329 | + val >>= AM654_SERDES_CTRL_CLKSEL_SHIFT; | |
297 | 330 | |
298 | - for (i = 0; i < num_parents; i++) | |
299 | - if (mux->table[i] == val) | |
300 | - return i; | |
301 | - | |
302 | - /* | |
303 | - * No parent? This should never happen! | |
304 | - * Verify if we set a valid parent in serdes_am654_clk_register() | |
305 | - */ | |
306 | - WARN(1, "Failed to find the parent of %s clock\n", hw->init->name); | |
307 | - | |
308 | - /* Make the parent lookup to fail */ | |
309 | - return num_parents; | |
331 | + return serdes_am654_mux_table[val][mux->clk_id]; | |
310 | 332 | } |
311 | 333 | |
312 | 334 | static int serdes_am654_clk_mux_set_parent(struct clk_hw *hw, u8 index) |
313 | 335 | |
314 | 336 | |
315 | 337 | |
316 | 338 | |
... | ... | @@ -314,16 +336,52 @@ |
314 | 336 | struct serdes_am654_clk_mux *mux = to_serdes_am654_clk_mux(hw); |
315 | 337 | struct regmap *regmap = mux->regmap; |
316 | 338 | unsigned int reg = mux->reg; |
317 | - int val; | |
339 | + int clk_id = mux->clk_id; | |
340 | + int parents[SERDES_NUM_CLOCKS]; | |
341 | + const int *p; | |
342 | + u32 val; | |
343 | + int found, i; | |
318 | 344 | int ret; |
319 | 345 | |
320 | - val = mux->table[index]; | |
346 | + /* get existing setting */ | |
347 | + regmap_read(regmap, reg, &val); | |
348 | + val &= AM654_SERDES_CTRL_CLKSEL_MASK; | |
349 | + val >>= AM654_SERDES_CTRL_CLKSEL_SHIFT; | |
321 | 350 | |
322 | - if (val == -1) | |
351 | + for (i = 0; i < SERDES_NUM_CLOCKS; i++) | |
352 | + parents[i] = serdes_am654_mux_table[val][i]; | |
353 | + | |
354 | + /* change parent of this clock. others left intact */ | |
355 | + parents[clk_id] = index; | |
356 | + | |
357 | + /* Find the match */ | |
358 | + for (val = 0; val < SERDES_NUM_MUX_COMBINATIONS; val++) { | |
359 | + p = serdes_am654_mux_table[val]; | |
360 | + found = 1; | |
361 | + for (i = 0; i < SERDES_NUM_CLOCKS; i++) { | |
362 | + if (parents[i] != p[i]) { | |
363 | + found = 0; | |
364 | + break; | |
365 | + } | |
366 | + } | |
367 | + | |
368 | + if (found) | |
369 | + break; | |
370 | + } | |
371 | + | |
372 | + if (!found) { | |
373 | + /* | |
374 | + * This can never happen, unless we missed | |
375 | + * a valid combination in serdes_am654_mux_table. | |
376 | + */ | |
377 | + WARN(1, "Failed to find the parent of %s clock\n", | |
378 | + hw->init->name); | |
323 | 379 | return -EINVAL; |
380 | + } | |
324 | 381 | |
325 | - val <<= mux->shift; | |
326 | - ret = regmap_update_bits(regmap, reg, mux->mask << mux->shift, val); | |
382 | + val <<= AM654_SERDES_CTRL_CLKSEL_SHIFT; | |
383 | + ret = regmap_update_bits(regmap, reg, AM654_SERDES_CTRL_CLKSEL_MASK, | |
384 | + val); | |
327 | 385 | |
328 | 386 | return ret; |
329 | 387 | } |
... | ... | @@ -333,21 +391,6 @@ |
333 | 391 | .get_parent = serdes_am654_clk_mux_get_parent, |
334 | 392 | }; |
335 | 393 | |
336 | -static int mux_table[SERDES_NUM_CLOCKS][3] = { | |
337 | - /* | |
338 | - * The entries represent values for selecting between | |
339 | - * {left input, external reference clock, right input} | |
340 | - * Only one of Left Output or Right Output should be used since | |
341 | - * both left and right output clock uses the same bits and modifying | |
342 | - * one clock will impact the other. | |
343 | - */ | |
344 | - { BIT(2), 0, BIT(0) }, /* Mux of CMU refclk */ | |
345 | - { -1, BIT(3), BIT(1) }, /* Mux of Left Output */ | |
346 | - { BIT(1), BIT(3) | BIT(1), -1 }, /* Mux of Right Output */ | |
347 | -}; | |
348 | - | |
349 | -static int mux_mask[SERDES_NUM_CLOCKS] = { 0x5, 0xa, 0xa }; | |
350 | - | |
351 | 394 | static int serdes_am654_clk_register(struct serdes_am654 *am654_phy, |
352 | 395 | const char *clock_name, int clock_num) |
353 | 396 | { |
354 | 397 | |
355 | 398 | |
... | ... | @@ -407,20 +450,11 @@ |
407 | 450 | init->num_parents = num_parents; |
408 | 451 | init->name = clock_name; |
409 | 452 | |
410 | - mux->table = mux_table[clock_num]; | |
411 | 453 | mux->regmap = regmap; |
412 | 454 | mux->reg = reg; |
413 | - mux->shift = 4; | |
414 | - mux->mask = mux_mask[clock_num]; | |
455 | + mux->clk_id = clock_num; | |
415 | 456 | mux->hw.init = init; |
416 | 457 | |
417 | - /* | |
418 | - * setup a sane default so get_parent() call evaluates | |
419 | - * to a valid parent. Index 1 is the safest choice as | |
420 | - * the default as it is valid value for all of serdes's | |
421 | - * output clocks. | |
422 | - */ | |
423 | - serdes_am654_clk_mux_set_parent(&mux->hw, 1); | |
424 | 458 | clk = devm_clk_register(dev, &mux->hw); |
425 | 459 | if (IS_ERR(clk)) |
426 | 460 | return PTR_ERR(clk); |