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