xref: /linux/drivers/clk/clk-max77686.c (revision 73118e6188c23719eeec3560b7fd1ca76f1a0919)
1*73118e61SJonghwa Lee /*
2*73118e61SJonghwa Lee  * clk-max77686.c - Clock driver for Maxim 77686
3*73118e61SJonghwa Lee  *
4*73118e61SJonghwa Lee  * Copyright (C) 2012 Samsung Electornics
5*73118e61SJonghwa Lee  * Jonghwa Lee <jonghwa3.lee@samsung.com>
6*73118e61SJonghwa Lee  *
7*73118e61SJonghwa Lee  * This program is free software; you can redistribute  it and/or modify it
8*73118e61SJonghwa Lee  * under  the terms of  the GNU General  Public License as published by the
9*73118e61SJonghwa Lee  * Free Software Foundation;  either version 2 of the  License, or (at your
10*73118e61SJonghwa Lee  * option) any later version.
11*73118e61SJonghwa Lee  *
12*73118e61SJonghwa Lee  * This program is distributed in the hope that it will be useful,
13*73118e61SJonghwa Lee  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14*73118e61SJonghwa Lee  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15*73118e61SJonghwa Lee  * GNU General Public License for more details.
16*73118e61SJonghwa Lee  *
17*73118e61SJonghwa Lee  * You should have received a copy of the GNU General Public License
18*73118e61SJonghwa Lee  * along with this program; if not, write to the Free Software
19*73118e61SJonghwa Lee  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20*73118e61SJonghwa Lee  *
21*73118e61SJonghwa Lee  */
22*73118e61SJonghwa Lee 
23*73118e61SJonghwa Lee #include <linux/kernel.h>
24*73118e61SJonghwa Lee #include <linux/slab.h>
25*73118e61SJonghwa Lee #include <linux/err.h>
26*73118e61SJonghwa Lee #include <linux/platform_device.h>
27*73118e61SJonghwa Lee #include <linux/mfd/max77686.h>
28*73118e61SJonghwa Lee #include <linux/mfd/max77686-private.h>
29*73118e61SJonghwa Lee #include <linux/clk-provider.h>
30*73118e61SJonghwa Lee #include <linux/mutex.h>
31*73118e61SJonghwa Lee #include <linux/clkdev.h>
32*73118e61SJonghwa Lee 
33*73118e61SJonghwa Lee enum {
34*73118e61SJonghwa Lee 	MAX77686_CLK_AP = 0,
35*73118e61SJonghwa Lee 	MAX77686_CLK_CP,
36*73118e61SJonghwa Lee 	MAX77686_CLK_PMIC,
37*73118e61SJonghwa Lee 	MAX77686_CLKS_NUM,
38*73118e61SJonghwa Lee };
39*73118e61SJonghwa Lee 
40*73118e61SJonghwa Lee struct max77686_clk {
41*73118e61SJonghwa Lee 	struct max77686_dev *iodev;
42*73118e61SJonghwa Lee 	u32 mask;
43*73118e61SJonghwa Lee 	struct clk_hw hw;
44*73118e61SJonghwa Lee 	struct clk_lookup *lookup;
45*73118e61SJonghwa Lee };
46*73118e61SJonghwa Lee 
47*73118e61SJonghwa Lee static struct max77686_clk *get_max77686_clk(struct clk_hw *hw)
48*73118e61SJonghwa Lee {
49*73118e61SJonghwa Lee 	return container_of(hw, struct max77686_clk, hw);
50*73118e61SJonghwa Lee }
51*73118e61SJonghwa Lee 
52*73118e61SJonghwa Lee static int max77686_clk_prepare(struct clk_hw *hw)
53*73118e61SJonghwa Lee {
54*73118e61SJonghwa Lee 	struct max77686_clk *max77686;
55*73118e61SJonghwa Lee 	int ret;
56*73118e61SJonghwa Lee 
57*73118e61SJonghwa Lee 	max77686 = get_max77686_clk(hw);
58*73118e61SJonghwa Lee 	if (!max77686)
59*73118e61SJonghwa Lee 		return -ENOMEM;
60*73118e61SJonghwa Lee 
61*73118e61SJonghwa Lee 	ret = regmap_update_bits(max77686->iodev->regmap,
62*73118e61SJonghwa Lee 		MAX77686_REG_32KHZ, max77686->mask, max77686->mask);
63*73118e61SJonghwa Lee 
64*73118e61SJonghwa Lee 	return ret;
65*73118e61SJonghwa Lee }
66*73118e61SJonghwa Lee 
67*73118e61SJonghwa Lee static void max77686_clk_unprepare(struct clk_hw *hw)
68*73118e61SJonghwa Lee {
69*73118e61SJonghwa Lee 	struct max77686_clk *max77686;
70*73118e61SJonghwa Lee 
71*73118e61SJonghwa Lee 	max77686 = get_max77686_clk(hw);
72*73118e61SJonghwa Lee 	if (!max77686)
73*73118e61SJonghwa Lee 		return;
74*73118e61SJonghwa Lee 
75*73118e61SJonghwa Lee 	regmap_update_bits(max77686->iodev->regmap,
76*73118e61SJonghwa Lee 		MAX77686_REG_32KHZ, max77686->mask, ~max77686->mask);
77*73118e61SJonghwa Lee }
78*73118e61SJonghwa Lee 
79*73118e61SJonghwa Lee static int max77686_clk_is_enabled(struct clk_hw *hw)
80*73118e61SJonghwa Lee {
81*73118e61SJonghwa Lee 	struct max77686_clk *max77686;
82*73118e61SJonghwa Lee 	int ret;
83*73118e61SJonghwa Lee 	u32 val;
84*73118e61SJonghwa Lee 
85*73118e61SJonghwa Lee 	max77686 = get_max77686_clk(hw);
86*73118e61SJonghwa Lee 	if (!max77686)
87*73118e61SJonghwa Lee 		return -ENOMEM;
88*73118e61SJonghwa Lee 
89*73118e61SJonghwa Lee 	ret = regmap_read(max77686->iodev->regmap,
90*73118e61SJonghwa Lee 				MAX77686_REG_32KHZ, &val);
91*73118e61SJonghwa Lee 
92*73118e61SJonghwa Lee 	if (ret < 0)
93*73118e61SJonghwa Lee 		return -EINVAL;
94*73118e61SJonghwa Lee 
95*73118e61SJonghwa Lee 	return val & max77686->mask;
96*73118e61SJonghwa Lee }
97*73118e61SJonghwa Lee 
98*73118e61SJonghwa Lee static struct clk_ops max77686_clk_ops = {
99*73118e61SJonghwa Lee 	.prepare	= max77686_clk_prepare,
100*73118e61SJonghwa Lee 	.unprepare	= max77686_clk_unprepare,
101*73118e61SJonghwa Lee 	.is_enabled	= max77686_clk_is_enabled,
102*73118e61SJonghwa Lee };
103*73118e61SJonghwa Lee 
104*73118e61SJonghwa Lee static struct clk_init_data max77686_clks_init[MAX77686_CLKS_NUM] = {
105*73118e61SJonghwa Lee 	[MAX77686_CLK_AP] = {
106*73118e61SJonghwa Lee 		.name = "32khz_ap",
107*73118e61SJonghwa Lee 		.ops = &max77686_clk_ops,
108*73118e61SJonghwa Lee 		.flags = CLK_IS_ROOT,
109*73118e61SJonghwa Lee 	},
110*73118e61SJonghwa Lee 	[MAX77686_CLK_CP] = {
111*73118e61SJonghwa Lee 		.name = "32khz_cp",
112*73118e61SJonghwa Lee 		.ops = &max77686_clk_ops,
113*73118e61SJonghwa Lee 		.flags = CLK_IS_ROOT,
114*73118e61SJonghwa Lee 	},
115*73118e61SJonghwa Lee 	[MAX77686_CLK_PMIC] = {
116*73118e61SJonghwa Lee 		.name = "32khz_pmic",
117*73118e61SJonghwa Lee 		.ops = &max77686_clk_ops,
118*73118e61SJonghwa Lee 		.flags = CLK_IS_ROOT,
119*73118e61SJonghwa Lee 	},
120*73118e61SJonghwa Lee };
121*73118e61SJonghwa Lee 
122*73118e61SJonghwa Lee static int max77686_clk_register(struct device *dev,
123*73118e61SJonghwa Lee 				struct max77686_clk *max77686)
124*73118e61SJonghwa Lee {
125*73118e61SJonghwa Lee 	struct clk *clk;
126*73118e61SJonghwa Lee 	struct clk_hw *hw = &max77686->hw;
127*73118e61SJonghwa Lee 
128*73118e61SJonghwa Lee 	clk = clk_register(dev, hw);
129*73118e61SJonghwa Lee 
130*73118e61SJonghwa Lee 	if (IS_ERR(clk))
131*73118e61SJonghwa Lee 		return -ENOMEM;
132*73118e61SJonghwa Lee 
133*73118e61SJonghwa Lee 	max77686->lookup = devm_kzalloc(dev, sizeof(struct clk_lookup),
134*73118e61SJonghwa Lee 					GFP_KERNEL);
135*73118e61SJonghwa Lee 	if (IS_ERR(max77686->lookup))
136*73118e61SJonghwa Lee 		return -ENOMEM;
137*73118e61SJonghwa Lee 
138*73118e61SJonghwa Lee 	max77686->lookup->con_id = hw->init->name;
139*73118e61SJonghwa Lee 	max77686->lookup->clk = clk;
140*73118e61SJonghwa Lee 
141*73118e61SJonghwa Lee 	clkdev_add(max77686->lookup);
142*73118e61SJonghwa Lee 
143*73118e61SJonghwa Lee 	return 0;
144*73118e61SJonghwa Lee }
145*73118e61SJonghwa Lee 
146*73118e61SJonghwa Lee static __devinit int max77686_clk_probe(struct platform_device *pdev)
147*73118e61SJonghwa Lee {
148*73118e61SJonghwa Lee 	struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
149*73118e61SJonghwa Lee 	struct max77686_clk **max77686_clks;
150*73118e61SJonghwa Lee 	int i, ret;
151*73118e61SJonghwa Lee 
152*73118e61SJonghwa Lee 	max77686_clks = devm_kzalloc(&pdev->dev, sizeof(struct max77686_clk *)
153*73118e61SJonghwa Lee 					* MAX77686_CLKS_NUM, GFP_KERNEL);
154*73118e61SJonghwa Lee 	if (IS_ERR(max77686_clks))
155*73118e61SJonghwa Lee 		return -ENOMEM;
156*73118e61SJonghwa Lee 
157*73118e61SJonghwa Lee 	for (i = 0; i < MAX77686_CLKS_NUM; i++) {
158*73118e61SJonghwa Lee 		max77686_clks[i] = devm_kzalloc(&pdev->dev,
159*73118e61SJonghwa Lee 					sizeof(struct max77686_clk), GFP_KERNEL);
160*73118e61SJonghwa Lee 		if (IS_ERR(max77686_clks[i]))
161*73118e61SJonghwa Lee 			return -ENOMEM;
162*73118e61SJonghwa Lee 	}
163*73118e61SJonghwa Lee 
164*73118e61SJonghwa Lee 	for (i = 0; i < MAX77686_CLKS_NUM; i++) {
165*73118e61SJonghwa Lee 		max77686_clks[i]->iodev = iodev;
166*73118e61SJonghwa Lee 		max77686_clks[i]->mask = 1 << i;
167*73118e61SJonghwa Lee 		max77686_clks[i]->hw.init = &max77686_clks_init[i];
168*73118e61SJonghwa Lee 
169*73118e61SJonghwa Lee 		ret = max77686_clk_register(&pdev->dev, max77686_clks[i]);
170*73118e61SJonghwa Lee 		if (ret) {
171*73118e61SJonghwa Lee 			switch (i) {
172*73118e61SJonghwa Lee 			case MAX77686_CLK_AP:
173*73118e61SJonghwa Lee 				dev_err(&pdev->dev, "Fail to register CLK_AP\n");
174*73118e61SJonghwa Lee 				goto err_clk_ap;
175*73118e61SJonghwa Lee 				break;
176*73118e61SJonghwa Lee 			case MAX77686_CLK_CP:
177*73118e61SJonghwa Lee 				dev_err(&pdev->dev, "Fail to register CLK_CP\n");
178*73118e61SJonghwa Lee 				goto err_clk_cp;
179*73118e61SJonghwa Lee 				break;
180*73118e61SJonghwa Lee 			case MAX77686_CLK_PMIC:
181*73118e61SJonghwa Lee 				dev_err(&pdev->dev, "Fail to register CLK_PMIC\n");
182*73118e61SJonghwa Lee 				goto err_clk_pmic;
183*73118e61SJonghwa Lee 			}
184*73118e61SJonghwa Lee 		}
185*73118e61SJonghwa Lee 	}
186*73118e61SJonghwa Lee 
187*73118e61SJonghwa Lee 	platform_set_drvdata(pdev, max77686_clks);
188*73118e61SJonghwa Lee 
189*73118e61SJonghwa Lee 	goto out;
190*73118e61SJonghwa Lee 
191*73118e61SJonghwa Lee err_clk_pmic:
192*73118e61SJonghwa Lee 	clkdev_drop(max77686_clks[MAX77686_CLK_CP]->lookup);
193*73118e61SJonghwa Lee 	kfree(max77686_clks[MAX77686_CLK_CP]->hw.clk);
194*73118e61SJonghwa Lee err_clk_cp:
195*73118e61SJonghwa Lee 	clkdev_drop(max77686_clks[MAX77686_CLK_AP]->lookup);
196*73118e61SJonghwa Lee 	kfree(max77686_clks[MAX77686_CLK_AP]->hw.clk);
197*73118e61SJonghwa Lee err_clk_ap:
198*73118e61SJonghwa Lee out:
199*73118e61SJonghwa Lee 	return ret;
200*73118e61SJonghwa Lee }
201*73118e61SJonghwa Lee 
202*73118e61SJonghwa Lee static int __devexit max77686_clk_remove(struct platform_device *pdev)
203*73118e61SJonghwa Lee {
204*73118e61SJonghwa Lee 	struct max77686_clk **max77686_clks = platform_get_drvdata(pdev);
205*73118e61SJonghwa Lee 	int i;
206*73118e61SJonghwa Lee 
207*73118e61SJonghwa Lee 	for (i = 0; i < MAX77686_CLKS_NUM; i++) {
208*73118e61SJonghwa Lee 		clkdev_drop(max77686_clks[i]->lookup);
209*73118e61SJonghwa Lee 		kfree(max77686_clks[i]->hw.clk);
210*73118e61SJonghwa Lee 	}
211*73118e61SJonghwa Lee 	return 0;
212*73118e61SJonghwa Lee }
213*73118e61SJonghwa Lee 
214*73118e61SJonghwa Lee static const struct platform_device_id max77686_clk_id[] = {
215*73118e61SJonghwa Lee 	{ "max77686-clk", 0},
216*73118e61SJonghwa Lee 	{ },
217*73118e61SJonghwa Lee };
218*73118e61SJonghwa Lee MODULE_DEVICE_TABLE(platform, max77686_clk_id);
219*73118e61SJonghwa Lee 
220*73118e61SJonghwa Lee static struct platform_driver max77686_clk_driver = {
221*73118e61SJonghwa Lee 	.driver = {
222*73118e61SJonghwa Lee 		.name  = "max77686-clk",
223*73118e61SJonghwa Lee 		.owner = THIS_MODULE,
224*73118e61SJonghwa Lee 	},
225*73118e61SJonghwa Lee 	.probe = max77686_clk_probe,
226*73118e61SJonghwa Lee 	.remove = __devexit_p(max77686_clk_remove),
227*73118e61SJonghwa Lee 	.id_table = max77686_clk_id,
228*73118e61SJonghwa Lee };
229*73118e61SJonghwa Lee 
230*73118e61SJonghwa Lee static int __init max77686_clk_init(void)
231*73118e61SJonghwa Lee {
232*73118e61SJonghwa Lee 	return platform_driver_register(&max77686_clk_driver);
233*73118e61SJonghwa Lee }
234*73118e61SJonghwa Lee subsys_initcall(max77686_clk_init);
235*73118e61SJonghwa Lee 
236*73118e61SJonghwa Lee static void __init max77686_clk_cleanup(void)
237*73118e61SJonghwa Lee {
238*73118e61SJonghwa Lee 	platform_driver_unregister(&max77686_clk_driver);
239*73118e61SJonghwa Lee }
240*73118e61SJonghwa Lee module_exit(max77686_clk_cleanup);
241*73118e61SJonghwa Lee 
242*73118e61SJonghwa Lee MODULE_DESCRIPTION("MAXIM 77686 Clock Driver");
243*73118e61SJonghwa Lee MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>");
244*73118e61SJonghwa Lee MODULE_LICENSE("GPL");
245