Commit | Line | Data |
---|---|---|
2025cf9e | 1 | // SPDX-License-Identifier: GPL-2.0-only |
038b892a CZ |
2 | /* |
3 | * Clkout driver for Rockchip RK808 | |
4 | * | |
5 | * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd | |
6 | * | |
7 | * Author:Chris Zhong <zyw@rock-chips.com> | |
038b892a CZ |
8 | */ |
9 | ||
038b892a CZ |
10 | #include <linux/clk-provider.h> |
11 | #include <linux/module.h> | |
12 | #include <linux/slab.h> | |
13 | #include <linux/platform_device.h> | |
14 | #include <linux/mfd/rk808.h> | |
038b892a | 15 | |
038b892a | 16 | struct rk808_clkout { |
2dc51ca8 | 17 | struct regmap *regmap; |
038b892a CZ |
18 | struct clk_hw clkout1_hw; |
19 | struct clk_hw clkout2_hw; | |
20 | }; | |
21 | ||
22 | static unsigned long rk808_clkout_recalc_rate(struct clk_hw *hw, | |
23 | unsigned long parent_rate) | |
24 | { | |
25 | return 32768; | |
26 | } | |
27 | ||
28 | static int rk808_clkout2_enable(struct clk_hw *hw, bool enable) | |
29 | { | |
30 | struct rk808_clkout *rk808_clkout = container_of(hw, | |
31 | struct rk808_clkout, | |
32 | clkout2_hw); | |
038b892a | 33 | |
2dc51ca8 | 34 | return regmap_update_bits(rk808_clkout->regmap, RK808_CLK32OUT_REG, |
038b892a CZ |
35 | CLK32KOUT2_EN, enable ? CLK32KOUT2_EN : 0); |
36 | } | |
37 | ||
38 | static int rk808_clkout2_prepare(struct clk_hw *hw) | |
39 | { | |
40 | return rk808_clkout2_enable(hw, true); | |
41 | } | |
42 | ||
43 | static void rk808_clkout2_unprepare(struct clk_hw *hw) | |
44 | { | |
45 | rk808_clkout2_enable(hw, false); | |
46 | } | |
47 | ||
48 | static int rk808_clkout2_is_prepared(struct clk_hw *hw) | |
49 | { | |
50 | struct rk808_clkout *rk808_clkout = container_of(hw, | |
51 | struct rk808_clkout, | |
52 | clkout2_hw); | |
038b892a CZ |
53 | uint32_t val; |
54 | ||
2dc51ca8 | 55 | int ret = regmap_read(rk808_clkout->regmap, RK808_CLK32OUT_REG, &val); |
038b892a CZ |
56 | |
57 | if (ret < 0) | |
58 | return ret; | |
59 | ||
60 | return (val & CLK32KOUT2_EN) ? 1 : 0; | |
61 | } | |
62 | ||
63 | static const struct clk_ops rk808_clkout1_ops = { | |
64 | .recalc_rate = rk808_clkout_recalc_rate, | |
65 | }; | |
66 | ||
67 | static const struct clk_ops rk808_clkout2_ops = { | |
68 | .prepare = rk808_clkout2_prepare, | |
69 | .unprepare = rk808_clkout2_unprepare, | |
70 | .is_prepared = rk808_clkout2_is_prepared, | |
71 | .recalc_rate = rk808_clkout_recalc_rate, | |
72 | }; | |
73 | ||
a8b6e85d SB |
74 | static struct clk_hw * |
75 | of_clk_rk808_get(struct of_phandle_args *clkspec, void *data) | |
76 | { | |
77 | struct rk808_clkout *rk808_clkout = data; | |
78 | unsigned int idx = clkspec->args[0]; | |
79 | ||
80 | if (idx >= 2) { | |
81 | pr_err("%s: invalid index %u\n", __func__, idx); | |
82 | return ERR_PTR(-EINVAL); | |
83 | } | |
84 | ||
85 | return idx ? &rk808_clkout->clkout2_hw : &rk808_clkout->clkout1_hw; | |
86 | } | |
87 | ||
8ed14401 TX |
88 | static int rk817_clkout2_enable(struct clk_hw *hw, bool enable) |
89 | { | |
90 | struct rk808_clkout *rk808_clkout = container_of(hw, | |
91 | struct rk808_clkout, | |
92 | clkout2_hw); | |
8ed14401 | 93 | |
2dc51ca8 | 94 | return regmap_update_bits(rk808_clkout->regmap, RK817_SYS_CFG(1), |
8ed14401 TX |
95 | RK817_CLK32KOUT2_EN, |
96 | enable ? RK817_CLK32KOUT2_EN : 0); | |
97 | } | |
98 | ||
99 | static int rk817_clkout2_prepare(struct clk_hw *hw) | |
100 | { | |
101 | return rk817_clkout2_enable(hw, true); | |
102 | } | |
103 | ||
104 | static void rk817_clkout2_unprepare(struct clk_hw *hw) | |
105 | { | |
106 | rk817_clkout2_enable(hw, false); | |
107 | } | |
108 | ||
109 | static int rk817_clkout2_is_prepared(struct clk_hw *hw) | |
110 | { | |
111 | struct rk808_clkout *rk808_clkout = container_of(hw, | |
112 | struct rk808_clkout, | |
113 | clkout2_hw); | |
8ed14401 TX |
114 | unsigned int val; |
115 | ||
2dc51ca8 | 116 | int ret = regmap_read(rk808_clkout->regmap, RK817_SYS_CFG(1), &val); |
8ed14401 TX |
117 | |
118 | if (ret < 0) | |
119 | return 0; | |
120 | ||
121 | return (val & RK817_CLK32KOUT2_EN) ? 1 : 0; | |
122 | } | |
123 | ||
124 | static const struct clk_ops rk817_clkout2_ops = { | |
125 | .prepare = rk817_clkout2_prepare, | |
126 | .unprepare = rk817_clkout2_unprepare, | |
127 | .is_prepared = rk817_clkout2_is_prepared, | |
128 | .recalc_rate = rk808_clkout_recalc_rate, | |
129 | }; | |
130 | ||
131 | static const struct clk_ops *rkpmic_get_ops(long variant) | |
132 | { | |
133 | switch (variant) { | |
134 | case RK809_ID: | |
135 | case RK817_ID: | |
136 | return &rk817_clkout2_ops; | |
137 | /* | |
138 | * For the default case, it match the following PMIC type. | |
139 | * RK805_ID | |
140 | * RK808_ID | |
141 | * RK818_ID | |
142 | */ | |
143 | default: | |
144 | return &rk808_clkout2_ops; | |
145 | } | |
146 | } | |
147 | ||
038b892a CZ |
148 | static int rk808_clkout_probe(struct platform_device *pdev) |
149 | { | |
150 | struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent); | |
2dc51ca8 | 151 | struct device *dev = &pdev->dev; |
038b892a | 152 | struct clk_init_data init = {}; |
038b892a | 153 | struct rk808_clkout *rk808_clkout; |
a8b6e85d | 154 | int ret; |
038b892a | 155 | |
2dc51ca8 SR |
156 | dev->of_node = pdev->dev.parent->of_node; |
157 | ||
158 | rk808_clkout = devm_kzalloc(dev, | |
038b892a CZ |
159 | sizeof(*rk808_clkout), GFP_KERNEL); |
160 | if (!rk808_clkout) | |
161 | return -ENOMEM; | |
162 | ||
2dc51ca8 SR |
163 | rk808_clkout->regmap = dev_get_regmap(pdev->dev.parent, NULL); |
164 | if (!rk808_clkout->regmap) | |
165 | return -ENODEV; | |
038b892a | 166 | |
038b892a CZ |
167 | init.parent_names = NULL; |
168 | init.num_parents = 0; | |
169 | init.name = "rk808-clkout1"; | |
170 | init.ops = &rk808_clkout1_ops; | |
171 | rk808_clkout->clkout1_hw.init = &init; | |
172 | ||
173 | /* optional override of the clockname */ | |
2dc51ca8 | 174 | of_property_read_string_index(dev->of_node, "clock-output-names", |
038b892a CZ |
175 | 0, &init.name); |
176 | ||
2dc51ca8 | 177 | ret = devm_clk_hw_register(dev, &rk808_clkout->clkout1_hw); |
a8b6e85d SB |
178 | if (ret) |
179 | return ret; | |
038b892a CZ |
180 | |
181 | init.name = "rk808-clkout2"; | |
8ed14401 | 182 | init.ops = rkpmic_get_ops(rk808->variant); |
038b892a CZ |
183 | rk808_clkout->clkout2_hw.init = &init; |
184 | ||
185 | /* optional override of the clockname */ | |
2dc51ca8 | 186 | of_property_read_string_index(dev->of_node, "clock-output-names", |
038b892a CZ |
187 | 1, &init.name); |
188 | ||
2dc51ca8 | 189 | ret = devm_clk_hw_register(dev, &rk808_clkout->clkout2_hw); |
a8b6e85d SB |
190 | if (ret) |
191 | return ret; | |
038b892a | 192 | |
25224667 MV |
193 | return devm_of_clk_add_hw_provider(&pdev->dev, of_clk_rk808_get, |
194 | rk808_clkout); | |
038b892a CZ |
195 | } |
196 | ||
197 | static struct platform_driver rk808_clkout_driver = { | |
198 | .probe = rk808_clkout_probe, | |
038b892a CZ |
199 | .driver = { |
200 | .name = "rk808-clkout", | |
201 | }, | |
202 | }; | |
203 | ||
204 | module_platform_driver(rk808_clkout_driver); | |
205 | ||
206 | MODULE_DESCRIPTION("Clkout driver for the rk808 series PMICs"); | |
207 | MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>"); | |
208 | MODULE_LICENSE("GPL"); | |
209 | MODULE_ALIAS("platform:rk808-clkout"); |