xref: /linux/drivers/clk/clk-max77686.c (revision a8a76f563cfa1a4e47b6d8b2bef9c0d44a3fd7b0)
173118e61SJonghwa Lee /*
273118e61SJonghwa Lee  * clk-max77686.c - Clock driver for Maxim 77686
373118e61SJonghwa Lee  *
473118e61SJonghwa Lee  * Copyright (C) 2012 Samsung Electornics
573118e61SJonghwa Lee  * Jonghwa Lee <jonghwa3.lee@samsung.com>
673118e61SJonghwa Lee  *
773118e61SJonghwa Lee  * This program is free software; you can redistribute  it and/or modify it
873118e61SJonghwa Lee  * under  the terms of  the GNU General  Public License as published by the
973118e61SJonghwa Lee  * Free Software Foundation;  either version 2 of the  License, or (at your
1073118e61SJonghwa Lee  * option) any later version.
1173118e61SJonghwa Lee  *
1273118e61SJonghwa Lee  * This program is distributed in the hope that it will be useful,
1373118e61SJonghwa Lee  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1473118e61SJonghwa Lee  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1573118e61SJonghwa Lee  * GNU General Public License for more details.
1673118e61SJonghwa Lee  *
1773118e61SJonghwa Lee  * You should have received a copy of the GNU General Public License
1873118e61SJonghwa Lee  * along with this program; if not, write to the Free Software
1973118e61SJonghwa Lee  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
2073118e61SJonghwa Lee  *
2173118e61SJonghwa Lee  */
2273118e61SJonghwa Lee 
2373118e61SJonghwa Lee #include <linux/kernel.h>
2473118e61SJonghwa Lee #include <linux/slab.h>
2573118e61SJonghwa Lee #include <linux/err.h>
2673118e61SJonghwa Lee #include <linux/platform_device.h>
2773118e61SJonghwa Lee #include <linux/mfd/max77686.h>
2873118e61SJonghwa Lee #include <linux/mfd/max77686-private.h>
2973118e61SJonghwa Lee #include <linux/clk-provider.h>
3073118e61SJonghwa Lee #include <linux/mutex.h>
3173118e61SJonghwa Lee #include <linux/clkdev.h>
3273118e61SJonghwa Lee 
33*a8a76f56SJavier Martinez Canillas #include <dt-bindings/clock/maxim,max77686.h>
3473118e61SJonghwa Lee 
3573118e61SJonghwa Lee struct max77686_clk {
3673118e61SJonghwa Lee 	struct max77686_dev *iodev;
3773118e61SJonghwa Lee 	u32 mask;
3873118e61SJonghwa Lee 	struct clk_hw hw;
3973118e61SJonghwa Lee 	struct clk_lookup *lookup;
4073118e61SJonghwa Lee };
4173118e61SJonghwa Lee 
423fe296cfSAxel Lin static struct max77686_clk *to_max77686_clk(struct clk_hw *hw)
4373118e61SJonghwa Lee {
4473118e61SJonghwa Lee 	return container_of(hw, struct max77686_clk, hw);
4573118e61SJonghwa Lee }
4673118e61SJonghwa Lee 
4773118e61SJonghwa Lee static int max77686_clk_prepare(struct clk_hw *hw)
4873118e61SJonghwa Lee {
493fe296cfSAxel Lin 	struct max77686_clk *max77686 = to_max77686_clk(hw);
5073118e61SJonghwa Lee 
513fe296cfSAxel Lin 	return regmap_update_bits(max77686->iodev->regmap,
523fe296cfSAxel Lin 				  MAX77686_REG_32KHZ, max77686->mask,
533fe296cfSAxel Lin 				  max77686->mask);
5473118e61SJonghwa Lee }
5573118e61SJonghwa Lee 
5673118e61SJonghwa Lee static void max77686_clk_unprepare(struct clk_hw *hw)
5773118e61SJonghwa Lee {
583fe296cfSAxel Lin 	struct max77686_clk *max77686 = to_max77686_clk(hw);
5973118e61SJonghwa Lee 
6073118e61SJonghwa Lee 	regmap_update_bits(max77686->iodev->regmap,
6173118e61SJonghwa Lee 		MAX77686_REG_32KHZ, max77686->mask, ~max77686->mask);
6273118e61SJonghwa Lee }
6373118e61SJonghwa Lee 
6421c8ed2dSTomasz Figa static int max77686_clk_is_prepared(struct clk_hw *hw)
6573118e61SJonghwa Lee {
663fe296cfSAxel Lin 	struct max77686_clk *max77686 = to_max77686_clk(hw);
6773118e61SJonghwa Lee 	int ret;
6873118e61SJonghwa Lee 	u32 val;
6973118e61SJonghwa Lee 
7073118e61SJonghwa Lee 	ret = regmap_read(max77686->iodev->regmap,
7173118e61SJonghwa Lee 				MAX77686_REG_32KHZ, &val);
7273118e61SJonghwa Lee 
7373118e61SJonghwa Lee 	if (ret < 0)
7473118e61SJonghwa Lee 		return -EINVAL;
7573118e61SJonghwa Lee 
7673118e61SJonghwa Lee 	return val & max77686->mask;
7773118e61SJonghwa Lee }
7873118e61SJonghwa Lee 
79cf7d4a6fSTomasz Figa static unsigned long max77686_recalc_rate(struct clk_hw *hw,
80cf7d4a6fSTomasz Figa 					  unsigned long parent_rate)
81cf7d4a6fSTomasz Figa {
82cf7d4a6fSTomasz Figa 	return 32768;
83cf7d4a6fSTomasz Figa }
84cf7d4a6fSTomasz Figa 
8573118e61SJonghwa Lee static struct clk_ops max77686_clk_ops = {
8673118e61SJonghwa Lee 	.prepare	= max77686_clk_prepare,
8773118e61SJonghwa Lee 	.unprepare	= max77686_clk_unprepare,
8821c8ed2dSTomasz Figa 	.is_prepared	= max77686_clk_is_prepared,
89cf7d4a6fSTomasz Figa 	.recalc_rate	= max77686_recalc_rate,
9073118e61SJonghwa Lee };
9173118e61SJonghwa Lee 
9273118e61SJonghwa Lee static struct clk_init_data max77686_clks_init[MAX77686_CLKS_NUM] = {
9373118e61SJonghwa Lee 	[MAX77686_CLK_AP] = {
9473118e61SJonghwa Lee 		.name = "32khz_ap",
9573118e61SJonghwa Lee 		.ops = &max77686_clk_ops,
9673118e61SJonghwa Lee 		.flags = CLK_IS_ROOT,
9773118e61SJonghwa Lee 	},
9873118e61SJonghwa Lee 	[MAX77686_CLK_CP] = {
9973118e61SJonghwa Lee 		.name = "32khz_cp",
10073118e61SJonghwa Lee 		.ops = &max77686_clk_ops,
10173118e61SJonghwa Lee 		.flags = CLK_IS_ROOT,
10273118e61SJonghwa Lee 	},
10373118e61SJonghwa Lee 	[MAX77686_CLK_PMIC] = {
10473118e61SJonghwa Lee 		.name = "32khz_pmic",
10573118e61SJonghwa Lee 		.ops = &max77686_clk_ops,
10673118e61SJonghwa Lee 		.flags = CLK_IS_ROOT,
10773118e61SJonghwa Lee 	},
10873118e61SJonghwa Lee };
10973118e61SJonghwa Lee 
110badbc542STomasz Figa static struct clk *max77686_clk_register(struct device *dev,
11173118e61SJonghwa Lee 				struct max77686_clk *max77686)
11273118e61SJonghwa Lee {
11373118e61SJonghwa Lee 	struct clk *clk;
11473118e61SJonghwa Lee 	struct clk_hw *hw = &max77686->hw;
11573118e61SJonghwa Lee 
11673118e61SJonghwa Lee 	clk = clk_register(dev, hw);
11773118e61SJonghwa Lee 	if (IS_ERR(clk))
118badbc542STomasz Figa 		return clk;
11973118e61SJonghwa Lee 
120f1ba28a1SLaurent Pinchart 	max77686->lookup = kzalloc(sizeof(struct clk_lookup), GFP_KERNEL);
1219f58b9b9SAxel Lin 	if (!max77686->lookup)
122badbc542STomasz Figa 		return ERR_PTR(-ENOMEM);
12373118e61SJonghwa Lee 
12473118e61SJonghwa Lee 	max77686->lookup->con_id = hw->init->name;
12573118e61SJonghwa Lee 	max77686->lookup->clk = clk;
12673118e61SJonghwa Lee 
12773118e61SJonghwa Lee 	clkdev_add(max77686->lookup);
12873118e61SJonghwa Lee 
129badbc542STomasz Figa 	return clk;
13073118e61SJonghwa Lee }
13173118e61SJonghwa Lee 
132018ae93fSBill Pemberton static int max77686_clk_probe(struct platform_device *pdev)
13373118e61SJonghwa Lee {
13473118e61SJonghwa Lee 	struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
1353966c947STomasz Figa 	struct max77686_clk *max77686_clks[MAX77686_CLKS_NUM];
1363966c947STomasz Figa 	struct clk **clocks;
13773118e61SJonghwa Lee 	int i, ret;
13873118e61SJonghwa Lee 
1393966c947STomasz Figa 	clocks = devm_kzalloc(&pdev->dev, sizeof(struct clk *)
14073118e61SJonghwa Lee 					* MAX77686_CLKS_NUM, GFP_KERNEL);
1413966c947STomasz Figa 	if (!clocks)
14273118e61SJonghwa Lee 		return -ENOMEM;
14373118e61SJonghwa Lee 
14473118e61SJonghwa Lee 	for (i = 0; i < MAX77686_CLKS_NUM; i++) {
14573118e61SJonghwa Lee 		max77686_clks[i] = devm_kzalloc(&pdev->dev,
14673118e61SJonghwa Lee 					sizeof(struct max77686_clk), GFP_KERNEL);
1479f58b9b9SAxel Lin 		if (!max77686_clks[i])
14873118e61SJonghwa Lee 			return -ENOMEM;
14973118e61SJonghwa Lee 	}
15073118e61SJonghwa Lee 
15173118e61SJonghwa Lee 	for (i = 0; i < MAX77686_CLKS_NUM; i++) {
15273118e61SJonghwa Lee 		max77686_clks[i]->iodev = iodev;
15373118e61SJonghwa Lee 		max77686_clks[i]->mask = 1 << i;
15473118e61SJonghwa Lee 		max77686_clks[i]->hw.init = &max77686_clks_init[i];
15573118e61SJonghwa Lee 
1563966c947STomasz Figa 		clocks[i] = max77686_clk_register(&pdev->dev, max77686_clks[i]);
1573966c947STomasz Figa 		if (IS_ERR(clocks[i])) {
1583966c947STomasz Figa 			ret = PTR_ERR(clocks[i]);
159d73ac4caSTomasz Figa 			dev_err(&pdev->dev, "failed to register %s\n",
160d73ac4caSTomasz Figa 				max77686_clks[i]->hw.init->name);
161d73ac4caSTomasz Figa 			goto err_clocks;
16273118e61SJonghwa Lee 		}
16373118e61SJonghwa Lee 	}
16473118e61SJonghwa Lee 
1653966c947STomasz Figa 	platform_set_drvdata(pdev, clocks);
16673118e61SJonghwa Lee 
167b06c6987STomasz Figa 	if (iodev->dev->of_node) {
168b06c6987STomasz Figa 		struct clk_onecell_data *of_data;
169b06c6987STomasz Figa 
170b06c6987STomasz Figa 		of_data = devm_kzalloc(&pdev->dev,
171b06c6987STomasz Figa 					sizeof(*of_data), GFP_KERNEL);
172b06c6987STomasz Figa 		if (!of_data) {
173b06c6987STomasz Figa 			ret = -ENOMEM;
174b06c6987STomasz Figa 			goto err_clocks;
175b06c6987STomasz Figa 		}
176b06c6987STomasz Figa 
177b06c6987STomasz Figa 		of_data->clks = clocks;
178b06c6987STomasz Figa 		of_data->clk_num = MAX77686_CLKS_NUM;
179b06c6987STomasz Figa 		ret = of_clk_add_provider(iodev->dev->of_node,
180b06c6987STomasz Figa 					of_clk_src_onecell_get, of_data);
181b06c6987STomasz Figa 		if (ret) {
182b06c6987STomasz Figa 			dev_err(&pdev->dev, "failed to register OF clock provider\n");
183b06c6987STomasz Figa 			goto err_clocks;
184b06c6987STomasz Figa 		}
185b06c6987STomasz Figa 	}
186b06c6987STomasz Figa 
187b0f85177STomasz Figa 	return 0;
18873118e61SJonghwa Lee 
189d73ac4caSTomasz Figa err_clocks:
190d73ac4caSTomasz Figa 	for (--i; i >= 0; --i) {
191d73ac4caSTomasz Figa 		clkdev_drop(max77686_clks[i]->lookup);
192d73ac4caSTomasz Figa 		clk_unregister(max77686_clks[i]->hw.clk);
193d73ac4caSTomasz Figa 	}
194d73ac4caSTomasz Figa 
19573118e61SJonghwa Lee 	return ret;
19673118e61SJonghwa Lee }
19773118e61SJonghwa Lee 
1981fc7ad5dSBill Pemberton static int max77686_clk_remove(struct platform_device *pdev)
19973118e61SJonghwa Lee {
200b06c6987STomasz Figa 	struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
2013966c947STomasz Figa 	struct clk **clocks = platform_get_drvdata(pdev);
20273118e61SJonghwa Lee 	int i;
20373118e61SJonghwa Lee 
204b06c6987STomasz Figa 	if (iodev->dev->of_node)
205b06c6987STomasz Figa 		of_clk_del_provider(iodev->dev->of_node);
206b06c6987STomasz Figa 
20773118e61SJonghwa Lee 	for (i = 0; i < MAX77686_CLKS_NUM; i++) {
2083966c947STomasz Figa 		struct clk_hw *hw = __clk_get_hw(clocks[i]);
2093966c947STomasz Figa 		struct max77686_clk *max77686 = to_max77686_clk(hw);
2103966c947STomasz Figa 
2113966c947STomasz Figa 		clkdev_drop(max77686->lookup);
2123966c947STomasz Figa 		clk_unregister(clocks[i]);
21373118e61SJonghwa Lee 	}
21473118e61SJonghwa Lee 	return 0;
21573118e61SJonghwa Lee }
21673118e61SJonghwa Lee 
21773118e61SJonghwa Lee static const struct platform_device_id max77686_clk_id[] = {
21873118e61SJonghwa Lee 	{ "max77686-clk", 0},
21973118e61SJonghwa Lee 	{ },
22073118e61SJonghwa Lee };
22173118e61SJonghwa Lee MODULE_DEVICE_TABLE(platform, max77686_clk_id);
22273118e61SJonghwa Lee 
22373118e61SJonghwa Lee static struct platform_driver max77686_clk_driver = {
22473118e61SJonghwa Lee 	.driver = {
22573118e61SJonghwa Lee 		.name  = "max77686-clk",
22673118e61SJonghwa Lee 		.owner = THIS_MODULE,
22773118e61SJonghwa Lee 	},
22873118e61SJonghwa Lee 	.probe = max77686_clk_probe,
229f9cfa630SBill Pemberton 	.remove = max77686_clk_remove,
23073118e61SJonghwa Lee 	.id_table = max77686_clk_id,
23173118e61SJonghwa Lee };
23273118e61SJonghwa Lee 
23373118e61SJonghwa Lee static int __init max77686_clk_init(void)
23473118e61SJonghwa Lee {
23573118e61SJonghwa Lee 	return platform_driver_register(&max77686_clk_driver);
23673118e61SJonghwa Lee }
23773118e61SJonghwa Lee subsys_initcall(max77686_clk_init);
23873118e61SJonghwa Lee 
23973118e61SJonghwa Lee static void __init max77686_clk_cleanup(void)
24073118e61SJonghwa Lee {
24173118e61SJonghwa Lee 	platform_driver_unregister(&max77686_clk_driver);
24273118e61SJonghwa Lee }
24373118e61SJonghwa Lee module_exit(max77686_clk_cleanup);
24473118e61SJonghwa Lee 
24573118e61SJonghwa Lee MODULE_DESCRIPTION("MAXIM 77686 Clock Driver");
24673118e61SJonghwa Lee MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>");
24773118e61SJonghwa Lee MODULE_LICENSE("GPL");
248