12025cf9eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2038b892aSChris Zhong /*
3038b892aSChris Zhong * Clkout driver for Rockchip RK808
4038b892aSChris Zhong *
5038b892aSChris Zhong * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
6038b892aSChris Zhong *
7038b892aSChris Zhong * Author:Chris Zhong <zyw@rock-chips.com>
8038b892aSChris Zhong */
9038b892aSChris Zhong
10038b892aSChris Zhong #include <linux/clk-provider.h>
11038b892aSChris Zhong #include <linux/module.h>
12038b892aSChris Zhong #include <linux/slab.h>
13038b892aSChris Zhong #include <linux/platform_device.h>
14038b892aSChris Zhong #include <linux/mfd/rk808.h>
15038b892aSChris Zhong
16038b892aSChris Zhong struct rk808_clkout {
17*2dc51ca8SSebastian Reichel struct regmap *regmap;
18038b892aSChris Zhong struct clk_hw clkout1_hw;
19038b892aSChris Zhong struct clk_hw clkout2_hw;
20038b892aSChris Zhong };
21038b892aSChris Zhong
rk808_clkout_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)22038b892aSChris Zhong static unsigned long rk808_clkout_recalc_rate(struct clk_hw *hw,
23038b892aSChris Zhong unsigned long parent_rate)
24038b892aSChris Zhong {
25038b892aSChris Zhong return 32768;
26038b892aSChris Zhong }
27038b892aSChris Zhong
rk808_clkout2_enable(struct clk_hw * hw,bool enable)28038b892aSChris Zhong static int rk808_clkout2_enable(struct clk_hw *hw, bool enable)
29038b892aSChris Zhong {
30038b892aSChris Zhong struct rk808_clkout *rk808_clkout = container_of(hw,
31038b892aSChris Zhong struct rk808_clkout,
32038b892aSChris Zhong clkout2_hw);
33038b892aSChris Zhong
34*2dc51ca8SSebastian Reichel return regmap_update_bits(rk808_clkout->regmap, RK808_CLK32OUT_REG,
35038b892aSChris Zhong CLK32KOUT2_EN, enable ? CLK32KOUT2_EN : 0);
36038b892aSChris Zhong }
37038b892aSChris Zhong
rk808_clkout2_prepare(struct clk_hw * hw)38038b892aSChris Zhong static int rk808_clkout2_prepare(struct clk_hw *hw)
39038b892aSChris Zhong {
40038b892aSChris Zhong return rk808_clkout2_enable(hw, true);
41038b892aSChris Zhong }
42038b892aSChris Zhong
rk808_clkout2_unprepare(struct clk_hw * hw)43038b892aSChris Zhong static void rk808_clkout2_unprepare(struct clk_hw *hw)
44038b892aSChris Zhong {
45038b892aSChris Zhong rk808_clkout2_enable(hw, false);
46038b892aSChris Zhong }
47038b892aSChris Zhong
rk808_clkout2_is_prepared(struct clk_hw * hw)48038b892aSChris Zhong static int rk808_clkout2_is_prepared(struct clk_hw *hw)
49038b892aSChris Zhong {
50038b892aSChris Zhong struct rk808_clkout *rk808_clkout = container_of(hw,
51038b892aSChris Zhong struct rk808_clkout,
52038b892aSChris Zhong clkout2_hw);
53038b892aSChris Zhong uint32_t val;
54038b892aSChris Zhong
55*2dc51ca8SSebastian Reichel int ret = regmap_read(rk808_clkout->regmap, RK808_CLK32OUT_REG, &val);
56038b892aSChris Zhong
57038b892aSChris Zhong if (ret < 0)
58038b892aSChris Zhong return ret;
59038b892aSChris Zhong
60038b892aSChris Zhong return (val & CLK32KOUT2_EN) ? 1 : 0;
61038b892aSChris Zhong }
62038b892aSChris Zhong
63038b892aSChris Zhong static const struct clk_ops rk808_clkout1_ops = {
64038b892aSChris Zhong .recalc_rate = rk808_clkout_recalc_rate,
65038b892aSChris Zhong };
66038b892aSChris Zhong
67038b892aSChris Zhong static const struct clk_ops rk808_clkout2_ops = {
68038b892aSChris Zhong .prepare = rk808_clkout2_prepare,
69038b892aSChris Zhong .unprepare = rk808_clkout2_unprepare,
70038b892aSChris Zhong .is_prepared = rk808_clkout2_is_prepared,
71038b892aSChris Zhong .recalc_rate = rk808_clkout_recalc_rate,
72038b892aSChris Zhong };
73038b892aSChris Zhong
74a8b6e85dSStephen Boyd static struct clk_hw *
of_clk_rk808_get(struct of_phandle_args * clkspec,void * data)75a8b6e85dSStephen Boyd of_clk_rk808_get(struct of_phandle_args *clkspec, void *data)
76a8b6e85dSStephen Boyd {
77a8b6e85dSStephen Boyd struct rk808_clkout *rk808_clkout = data;
78a8b6e85dSStephen Boyd unsigned int idx = clkspec->args[0];
79a8b6e85dSStephen Boyd
80a8b6e85dSStephen Boyd if (idx >= 2) {
81a8b6e85dSStephen Boyd pr_err("%s: invalid index %u\n", __func__, idx);
82a8b6e85dSStephen Boyd return ERR_PTR(-EINVAL);
83a8b6e85dSStephen Boyd }
84a8b6e85dSStephen Boyd
85a8b6e85dSStephen Boyd return idx ? &rk808_clkout->clkout2_hw : &rk808_clkout->clkout1_hw;
86a8b6e85dSStephen Boyd }
87a8b6e85dSStephen Boyd
rk817_clkout2_enable(struct clk_hw * hw,bool enable)888ed14401STony Xie static int rk817_clkout2_enable(struct clk_hw *hw, bool enable)
898ed14401STony Xie {
908ed14401STony Xie struct rk808_clkout *rk808_clkout = container_of(hw,
918ed14401STony Xie struct rk808_clkout,
928ed14401STony Xie clkout2_hw);
938ed14401STony Xie
94*2dc51ca8SSebastian Reichel return regmap_update_bits(rk808_clkout->regmap, RK817_SYS_CFG(1),
958ed14401STony Xie RK817_CLK32KOUT2_EN,
968ed14401STony Xie enable ? RK817_CLK32KOUT2_EN : 0);
978ed14401STony Xie }
988ed14401STony Xie
rk817_clkout2_prepare(struct clk_hw * hw)998ed14401STony Xie static int rk817_clkout2_prepare(struct clk_hw *hw)
1008ed14401STony Xie {
1018ed14401STony Xie return rk817_clkout2_enable(hw, true);
1028ed14401STony Xie }
1038ed14401STony Xie
rk817_clkout2_unprepare(struct clk_hw * hw)1048ed14401STony Xie static void rk817_clkout2_unprepare(struct clk_hw *hw)
1058ed14401STony Xie {
1068ed14401STony Xie rk817_clkout2_enable(hw, false);
1078ed14401STony Xie }
1088ed14401STony Xie
rk817_clkout2_is_prepared(struct clk_hw * hw)1098ed14401STony Xie static int rk817_clkout2_is_prepared(struct clk_hw *hw)
1108ed14401STony Xie {
1118ed14401STony Xie struct rk808_clkout *rk808_clkout = container_of(hw,
1128ed14401STony Xie struct rk808_clkout,
1138ed14401STony Xie clkout2_hw);
1148ed14401STony Xie unsigned int val;
1158ed14401STony Xie
116*2dc51ca8SSebastian Reichel int ret = regmap_read(rk808_clkout->regmap, RK817_SYS_CFG(1), &val);
1178ed14401STony Xie
1188ed14401STony Xie if (ret < 0)
1198ed14401STony Xie return 0;
1208ed14401STony Xie
1218ed14401STony Xie return (val & RK817_CLK32KOUT2_EN) ? 1 : 0;
1228ed14401STony Xie }
1238ed14401STony Xie
1248ed14401STony Xie static const struct clk_ops rk817_clkout2_ops = {
1258ed14401STony Xie .prepare = rk817_clkout2_prepare,
1268ed14401STony Xie .unprepare = rk817_clkout2_unprepare,
1278ed14401STony Xie .is_prepared = rk817_clkout2_is_prepared,
1288ed14401STony Xie .recalc_rate = rk808_clkout_recalc_rate,
1298ed14401STony Xie };
1308ed14401STony Xie
rkpmic_get_ops(long variant)1318ed14401STony Xie static const struct clk_ops *rkpmic_get_ops(long variant)
1328ed14401STony Xie {
1338ed14401STony Xie switch (variant) {
1348ed14401STony Xie case RK809_ID:
1358ed14401STony Xie case RK817_ID:
1368ed14401STony Xie return &rk817_clkout2_ops;
1378ed14401STony Xie /*
1388ed14401STony Xie * For the default case, it match the following PMIC type.
1398ed14401STony Xie * RK805_ID
1408ed14401STony Xie * RK808_ID
1418ed14401STony Xie * RK818_ID
1428ed14401STony Xie */
1438ed14401STony Xie default:
1448ed14401STony Xie return &rk808_clkout2_ops;
1458ed14401STony Xie }
1468ed14401STony Xie }
1478ed14401STony Xie
rk808_clkout_probe(struct platform_device * pdev)148038b892aSChris Zhong static int rk808_clkout_probe(struct platform_device *pdev)
149038b892aSChris Zhong {
150038b892aSChris Zhong struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent);
151*2dc51ca8SSebastian Reichel struct device *dev = &pdev->dev;
152038b892aSChris Zhong struct clk_init_data init = {};
153038b892aSChris Zhong struct rk808_clkout *rk808_clkout;
154a8b6e85dSStephen Boyd int ret;
155038b892aSChris Zhong
156*2dc51ca8SSebastian Reichel dev->of_node = pdev->dev.parent->of_node;
157*2dc51ca8SSebastian Reichel
158*2dc51ca8SSebastian Reichel rk808_clkout = devm_kzalloc(dev,
159038b892aSChris Zhong sizeof(*rk808_clkout), GFP_KERNEL);
160038b892aSChris Zhong if (!rk808_clkout)
161038b892aSChris Zhong return -ENOMEM;
162038b892aSChris Zhong
163*2dc51ca8SSebastian Reichel rk808_clkout->regmap = dev_get_regmap(pdev->dev.parent, NULL);
164*2dc51ca8SSebastian Reichel if (!rk808_clkout->regmap)
165*2dc51ca8SSebastian Reichel return -ENODEV;
166038b892aSChris Zhong
167038b892aSChris Zhong init.parent_names = NULL;
168038b892aSChris Zhong init.num_parents = 0;
169038b892aSChris Zhong init.name = "rk808-clkout1";
170038b892aSChris Zhong init.ops = &rk808_clkout1_ops;
171038b892aSChris Zhong rk808_clkout->clkout1_hw.init = &init;
172038b892aSChris Zhong
173038b892aSChris Zhong /* optional override of the clockname */
174*2dc51ca8SSebastian Reichel of_property_read_string_index(dev->of_node, "clock-output-names",
175038b892aSChris Zhong 0, &init.name);
176038b892aSChris Zhong
177*2dc51ca8SSebastian Reichel ret = devm_clk_hw_register(dev, &rk808_clkout->clkout1_hw);
178a8b6e85dSStephen Boyd if (ret)
179a8b6e85dSStephen Boyd return ret;
180038b892aSChris Zhong
181038b892aSChris Zhong init.name = "rk808-clkout2";
1828ed14401STony Xie init.ops = rkpmic_get_ops(rk808->variant);
183038b892aSChris Zhong rk808_clkout->clkout2_hw.init = &init;
184038b892aSChris Zhong
185038b892aSChris Zhong /* optional override of the clockname */
186*2dc51ca8SSebastian Reichel of_property_read_string_index(dev->of_node, "clock-output-names",
187038b892aSChris Zhong 1, &init.name);
188038b892aSChris Zhong
189*2dc51ca8SSebastian Reichel ret = devm_clk_hw_register(dev, &rk808_clkout->clkout2_hw);
190a8b6e85dSStephen Boyd if (ret)
191a8b6e85dSStephen Boyd return ret;
192038b892aSChris Zhong
19325224667SMatti Vaittinen return devm_of_clk_add_hw_provider(&pdev->dev, of_clk_rk808_get,
19425224667SMatti Vaittinen rk808_clkout);
195038b892aSChris Zhong }
196038b892aSChris Zhong
197038b892aSChris Zhong static struct platform_driver rk808_clkout_driver = {
198038b892aSChris Zhong .probe = rk808_clkout_probe,
199038b892aSChris Zhong .driver = {
200038b892aSChris Zhong .name = "rk808-clkout",
201038b892aSChris Zhong },
202038b892aSChris Zhong };
203038b892aSChris Zhong
204038b892aSChris Zhong module_platform_driver(rk808_clkout_driver);
205038b892aSChris Zhong
206038b892aSChris Zhong MODULE_DESCRIPTION("Clkout driver for the rk808 series PMICs");
207038b892aSChris Zhong MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
208038b892aSChris Zhong MODULE_LICENSE("GPL");
209038b892aSChris Zhong MODULE_ALIAS("platform:rk808-clkout");
210