summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeil Armstrong <narmstrong@baylibre.com>2018-11-06 09:57:34 -0500
committerNeil Armstrong <narmstrong@baylibre.com>2018-11-23 09:11:56 -0500
commit72dbb8c94d0d8a44d1a006fd487e755e2de48dec (patch)
tree7e3bb995ee0532c09ac00feb9527b7890cad0f02
parent8e1dd17c8b0e3f8c66ed2a3f88a440d36135e589 (diff)
clk: meson: Add vid_pll divider driver
Add support the VID_PLL fully programmable divider used right after the HDMI PLL clock source. It is used to achieve complex fractional division with a programmble bitfield. Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> Acked-by: Jerome Brunet <jbrunet@baylibre.com> Link: http://lkml.kernel.org/r/1541516257-16157-2-git-send-email-narmstrong@baylibre.com
-rw-r--r--drivers/clk/meson/Makefile2
-rw-r--r--drivers/clk/meson/clkc.h6
-rw-r--r--drivers/clk/meson/vid-pll-div.c91
3 files changed, 98 insertions, 1 deletions
diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
index 72ec8c40d848..0234767f6cfc 100644
--- a/drivers/clk/meson/Makefile
+++ b/drivers/clk/meson/Makefile
@@ -2,7 +2,7 @@
2# Makefile for Meson specific clk 2# Makefile for Meson specific clk
3# 3#
4 4
5obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-phase.o 5obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-phase.o vid-pll-div.o
6obj-$(CONFIG_COMMON_CLK_AMLOGIC_AUDIO) += clk-triphase.o sclk-div.o 6obj-$(CONFIG_COMMON_CLK_AMLOGIC_AUDIO) += clk-triphase.o sclk-div.o
7obj-$(CONFIG_COMMON_CLK_MESON_AO) += meson-aoclk.o 7obj-$(CONFIG_COMMON_CLK_MESON_AO) += meson-aoclk.o
8obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o 8obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o
diff --git a/drivers/clk/meson/clkc.h b/drivers/clk/meson/clkc.h
index 6b96d55c047d..91666055c75a 100644
--- a/drivers/clk/meson/clkc.h
+++ b/drivers/clk/meson/clkc.h
@@ -90,6 +90,11 @@ struct meson_clk_phase_data {
90int meson_clk_degrees_from_val(unsigned int val, unsigned int width); 90int meson_clk_degrees_from_val(unsigned int val, unsigned int width);
91unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width); 91unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width);
92 92
93struct meson_vid_pll_div_data {
94 struct parm val;
95 struct parm sel;
96};
97
93#define MESON_GATE(_name, _reg, _bit) \ 98#define MESON_GATE(_name, _reg, _bit) \
94struct clk_regmap _name = { \ 99struct clk_regmap _name = { \
95 .data = &(struct clk_regmap_gate_data){ \ 100 .data = &(struct clk_regmap_gate_data){ \
@@ -112,5 +117,6 @@ extern const struct clk_ops meson_clk_cpu_ops;
112extern const struct clk_ops meson_clk_mpll_ro_ops; 117extern const struct clk_ops meson_clk_mpll_ro_ops;
113extern const struct clk_ops meson_clk_mpll_ops; 118extern const struct clk_ops meson_clk_mpll_ops;
114extern const struct clk_ops meson_clk_phase_ops; 119extern const struct clk_ops meson_clk_phase_ops;
120extern const struct clk_ops meson_vid_pll_div_ro_ops;
115 121
116#endif /* __CLKC_H */ 122#endif /* __CLKC_H */
diff --git a/drivers/clk/meson/vid-pll-div.c b/drivers/clk/meson/vid-pll-div.c
new file mode 100644
index 000000000000..b3370ea7beac
--- /dev/null
+++ b/drivers/clk/meson/vid-pll-div.c
@@ -0,0 +1,91 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2018 BayLibre, SAS.
4 * Author: Neil Armstrong <narmstrong@baylibre.com>
5 */
6
7#include <linux/clk-provider.h>
8#include "clkc.h"
9
10static inline struct meson_vid_pll_div_data *
11meson_vid_pll_div_data(struct clk_regmap *clk)
12{
13 return (struct meson_vid_pll_div_data *)clk->data;
14}
15
16/*
17 * This vid_pll divided is a fully programmable fractionnal divider to
18 * achieve complex video clock rates.
19 *
20 * Here are provided the commonly used fraction values provided by Amlogic.
21 */
22
23struct vid_pll_div {
24 unsigned int shift_val;
25 unsigned int shift_sel;
26 unsigned int divider;
27 unsigned int multiplier;
28};
29
30#define VID_PLL_DIV(_val, _sel, _ft, _fb) \
31 { \
32 .shift_val = (_val), \
33 .shift_sel = (_sel), \
34 .divider = (_ft), \
35 .multiplier = (_fb), \
36 }
37
38static const struct vid_pll_div vid_pll_div_table[] = {
39 VID_PLL_DIV(0x0aaa, 0, 2, 1), /* 2/1 => /2 */
40 VID_PLL_DIV(0x5294, 2, 5, 2), /* 5/2 => /2.5 */
41 VID_PLL_DIV(0x0db6, 0, 3, 1), /* 3/1 => /3 */
42 VID_PLL_DIV(0x36cc, 1, 7, 2), /* 7/2 => /3.5 */
43 VID_PLL_DIV(0x6666, 2, 15, 4), /* 15/4 => /3.75 */
44 VID_PLL_DIV(0x0ccc, 0, 4, 1), /* 4/1 => /4 */
45 VID_PLL_DIV(0x739c, 2, 5, 1), /* 5/1 => /5 */
46 VID_PLL_DIV(0x0e38, 0, 6, 1), /* 6/1 => /6 */
47 VID_PLL_DIV(0x0000, 3, 25, 4), /* 25/4 => /6.25 */
48 VID_PLL_DIV(0x3c78, 1, 7, 1), /* 7/1 => /7 */
49 VID_PLL_DIV(0x78f0, 2, 15, 2), /* 15/2 => /7.5 */
50 VID_PLL_DIV(0x0fc0, 0, 12, 1), /* 12/1 => /12 */
51 VID_PLL_DIV(0x3f80, 1, 14, 1), /* 14/1 => /14 */
52 VID_PLL_DIV(0x7f80, 2, 15, 1), /* 15/1 => /15 */
53};
54
55#define to_meson_vid_pll_div(_hw) \
56 container_of(_hw, struct meson_vid_pll_div, hw)
57
58const struct vid_pll_div *_get_table_val(unsigned int shift_val,
59 unsigned int shift_sel)
60{
61 int i;
62
63 for (i = 0 ; i < ARRAY_SIZE(vid_pll_div_table) ; ++i) {
64 if (vid_pll_div_table[i].shift_val == shift_val &&
65 vid_pll_div_table[i].shift_sel == shift_sel)
66 return &vid_pll_div_table[i];
67 }
68
69 return NULL;
70}
71
72static unsigned long meson_vid_pll_div_recalc_rate(struct clk_hw *hw,
73 unsigned long parent_rate)
74{
75 struct clk_regmap *clk = to_clk_regmap(hw);
76 struct meson_vid_pll_div_data *pll_div = meson_vid_pll_div_data(clk);
77 const struct vid_pll_div *div;
78
79 div = _get_table_val(meson_parm_read(clk->map, &pll_div->val),
80 meson_parm_read(clk->map, &pll_div->sel));
81 if (!div || !div->divider) {
82 pr_info("%s: Invalid config value for vid_pll_div\n", __func__);
83 return parent_rate;
84 }
85
86 return DIV_ROUND_UP_ULL(parent_rate * div->multiplier, div->divider);
87}
88
89const struct clk_ops meson_vid_pll_div_ro_ops = {
90 .recalc_rate = meson_vid_pll_div_recalc_rate,
91};