Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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
 33enum {
 34	MAX77686_CLK_AP = 0,
 35	MAX77686_CLK_CP,
 36	MAX77686_CLK_PMIC,
 37	MAX77686_CLKS_NUM,
 38};
 39
 40struct max77686_clk {
 41	struct max77686_dev *iodev;
 42	u32 mask;
 43	struct clk_hw hw;
 44	struct clk_lookup *lookup;
 45};
 46
 47static struct max77686_clk *to_max77686_clk(struct clk_hw *hw)
 48{
 49	return container_of(hw, struct max77686_clk, hw);
 50}
 51
 52static int max77686_clk_prepare(struct clk_hw *hw)
 53{
 54	struct max77686_clk *max77686 = to_max77686_clk(hw);
 55
 56	return regmap_update_bits(max77686->iodev->regmap,
 57				  MAX77686_REG_32KHZ, max77686->mask,
 58				  max77686->mask);
 59}
 60
 61static void max77686_clk_unprepare(struct clk_hw *hw)
 62{
 63	struct max77686_clk *max77686 = to_max77686_clk(hw);
 64
 65	regmap_update_bits(max77686->iodev->regmap,
 66		MAX77686_REG_32KHZ, max77686->mask, ~max77686->mask);
 67}
 68
 69static int max77686_clk_is_prepared(struct clk_hw *hw)
 70{
 71	struct max77686_clk *max77686 = to_max77686_clk(hw);
 72	int ret;
 73	u32 val;
 74
 75	ret = regmap_read(max77686->iodev->regmap,
 76				MAX77686_REG_32KHZ, &val);
 77
 78	if (ret < 0)
 79		return -EINVAL;
 80
 81	return val & max77686->mask;
 82}
 83
 84static unsigned long max77686_recalc_rate(struct clk_hw *hw,
 85					  unsigned long parent_rate)
 86{
 87	return 32768;
 88}
 89
 90static struct clk_ops max77686_clk_ops = {
 91	.prepare	= max77686_clk_prepare,
 92	.unprepare	= max77686_clk_unprepare,
 93	.is_prepared	= max77686_clk_is_prepared,
 94	.recalc_rate	= max77686_recalc_rate,
 95};
 96
 97static struct clk_init_data max77686_clks_init[MAX77686_CLKS_NUM] = {
 98	[MAX77686_CLK_AP] = {
 99		.name = "32khz_ap",
100		.ops = &max77686_clk_ops,
101		.flags = CLK_IS_ROOT,
102	},
103	[MAX77686_CLK_CP] = {
104		.name = "32khz_cp",
105		.ops = &max77686_clk_ops,
106		.flags = CLK_IS_ROOT,
107	},
108	[MAX77686_CLK_PMIC] = {
109		.name = "32khz_pmic",
110		.ops = &max77686_clk_ops,
111		.flags = CLK_IS_ROOT,
112	},
113};
114
115static struct clk *max77686_clk_register(struct device *dev,
116				struct max77686_clk *max77686)
117{
118	struct clk *clk;
119	struct clk_hw *hw = &max77686->hw;
120
121	clk = clk_register(dev, hw);
122	if (IS_ERR(clk))
123		return clk;
124
125	max77686->lookup = kzalloc(sizeof(struct clk_lookup), GFP_KERNEL);
126	if (!max77686->lookup)
127		return ERR_PTR(-ENOMEM);
128
129	max77686->lookup->con_id = hw->init->name;
130	max77686->lookup->clk = clk;
131
132	clkdev_add(max77686->lookup);
133
134	return clk;
135}
136
137static int max77686_clk_probe(struct platform_device *pdev)
138{
139	struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
140	struct max77686_clk *max77686_clks[MAX77686_CLKS_NUM];
141	struct clk **clocks;
142	int i, ret;
143
144	clocks = devm_kzalloc(&pdev->dev, sizeof(struct clk *)
145					* MAX77686_CLKS_NUM, GFP_KERNEL);
146	if (!clocks)
147		return -ENOMEM;
148
149	for (i = 0; i < MAX77686_CLKS_NUM; i++) {
150		max77686_clks[i] = devm_kzalloc(&pdev->dev,
151					sizeof(struct max77686_clk), GFP_KERNEL);
152		if (!max77686_clks[i])
153			return -ENOMEM;
154	}
155
156	for (i = 0; i < MAX77686_CLKS_NUM; i++) {
157		max77686_clks[i]->iodev = iodev;
158		max77686_clks[i]->mask = 1 << i;
159		max77686_clks[i]->hw.init = &max77686_clks_init[i];
160
161		clocks[i] = max77686_clk_register(&pdev->dev, max77686_clks[i]);
162		if (IS_ERR(clocks[i])) {
163			ret = PTR_ERR(clocks[i]);
164			dev_err(&pdev->dev, "failed to register %s\n",
165				max77686_clks[i]->hw.init->name);
166			goto err_clocks;
167		}
168	}
169
170	platform_set_drvdata(pdev, clocks);
171
172	if (iodev->dev->of_node) {
173		struct clk_onecell_data *of_data;
174
175		of_data = devm_kzalloc(&pdev->dev,
176					sizeof(*of_data), GFP_KERNEL);
177		if (!of_data) {
178			ret = -ENOMEM;
179			goto err_clocks;
180		}
181
182		of_data->clks = clocks;
183		of_data->clk_num = MAX77686_CLKS_NUM;
184		ret = of_clk_add_provider(iodev->dev->of_node,
185					of_clk_src_onecell_get, of_data);
186		if (ret) {
187			dev_err(&pdev->dev, "failed to register OF clock provider\n");
188			goto err_clocks;
189		}
190	}
191
192	return 0;
193
194err_clocks:
195	for (--i; i >= 0; --i) {
196		clkdev_drop(max77686_clks[i]->lookup);
197		clk_unregister(max77686_clks[i]->hw.clk);
198	}
199
200	return ret;
201}
202
203static int max77686_clk_remove(struct platform_device *pdev)
204{
205	struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
206	struct clk **clocks = platform_get_drvdata(pdev);
207	int i;
208
209	if (iodev->dev->of_node)
210		of_clk_del_provider(iodev->dev->of_node);
211
212	for (i = 0; i < MAX77686_CLKS_NUM; i++) {
213		struct clk_hw *hw = __clk_get_hw(clocks[i]);
214		struct max77686_clk *max77686 = to_max77686_clk(hw);
215
216		clkdev_drop(max77686->lookup);
217		clk_unregister(clocks[i]);
218	}
219	return 0;
220}
221
222static const struct platform_device_id max77686_clk_id[] = {
223	{ "max77686-clk", 0},
224	{ },
225};
226MODULE_DEVICE_TABLE(platform, max77686_clk_id);
227
228static struct platform_driver max77686_clk_driver = {
229	.driver = {
230		.name  = "max77686-clk",
231		.owner = THIS_MODULE,
232	},
233	.probe = max77686_clk_probe,
234	.remove = max77686_clk_remove,
235	.id_table = max77686_clk_id,
236};
237
238static int __init max77686_clk_init(void)
239{
240	return platform_driver_register(&max77686_clk_driver);
241}
242subsys_initcall(max77686_clk_init);
243
244static void __init max77686_clk_cleanup(void)
245{
246	platform_driver_unregister(&max77686_clk_driver);
247}
248module_exit(max77686_clk_cleanup);
249
250MODULE_DESCRIPTION("MAXIM 77686 Clock Driver");
251MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>");
252MODULE_LICENSE("GPL");