xref: /linux/drivers/clk/clk-rk808.c (revision cdd5b5a9761fd66d17586e4f4ba6588c70e640ea)
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