xref: /linux/drivers/clk/clk-max77686.c (revision 3966c947f45911e093114371462687134d5e8d40)
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 
3373118e61SJonghwa Lee enum {
3473118e61SJonghwa Lee 	MAX77686_CLK_AP = 0,
3573118e61SJonghwa Lee 	MAX77686_CLK_CP,
3673118e61SJonghwa Lee 	MAX77686_CLK_PMIC,
3773118e61SJonghwa Lee 	MAX77686_CLKS_NUM,
3873118e61SJonghwa Lee };
3973118e61SJonghwa Lee 
4073118e61SJonghwa Lee struct max77686_clk {
4173118e61SJonghwa Lee 	struct max77686_dev *iodev;
4273118e61SJonghwa Lee 	u32 mask;
4373118e61SJonghwa Lee 	struct clk_hw hw;
4473118e61SJonghwa Lee 	struct clk_lookup *lookup;
4573118e61SJonghwa Lee };
4673118e61SJonghwa Lee 
473fe296cfSAxel Lin static struct max77686_clk *to_max77686_clk(struct clk_hw *hw)
4873118e61SJonghwa Lee {
4973118e61SJonghwa Lee 	return container_of(hw, struct max77686_clk, hw);
5073118e61SJonghwa Lee }
5173118e61SJonghwa Lee 
5273118e61SJonghwa Lee static int max77686_clk_prepare(struct clk_hw *hw)
5373118e61SJonghwa Lee {
543fe296cfSAxel Lin 	struct max77686_clk *max77686 = to_max77686_clk(hw);
5573118e61SJonghwa Lee 
563fe296cfSAxel Lin 	return regmap_update_bits(max77686->iodev->regmap,
573fe296cfSAxel Lin 				  MAX77686_REG_32KHZ, max77686->mask,
583fe296cfSAxel Lin 				  max77686->mask);
5973118e61SJonghwa Lee }
6073118e61SJonghwa Lee 
6173118e61SJonghwa Lee static void max77686_clk_unprepare(struct clk_hw *hw)
6273118e61SJonghwa Lee {
633fe296cfSAxel Lin 	struct max77686_clk *max77686 = to_max77686_clk(hw);
6473118e61SJonghwa Lee 
6573118e61SJonghwa Lee 	regmap_update_bits(max77686->iodev->regmap,
6673118e61SJonghwa Lee 		MAX77686_REG_32KHZ, max77686->mask, ~max77686->mask);
6773118e61SJonghwa Lee }
6873118e61SJonghwa Lee 
6921c8ed2dSTomasz Figa static int max77686_clk_is_prepared(struct clk_hw *hw)
7073118e61SJonghwa Lee {
713fe296cfSAxel Lin 	struct max77686_clk *max77686 = to_max77686_clk(hw);
7273118e61SJonghwa Lee 	int ret;
7373118e61SJonghwa Lee 	u32 val;
7473118e61SJonghwa Lee 
7573118e61SJonghwa Lee 	ret = regmap_read(max77686->iodev->regmap,
7673118e61SJonghwa Lee 				MAX77686_REG_32KHZ, &val);
7773118e61SJonghwa Lee 
7873118e61SJonghwa Lee 	if (ret < 0)
7973118e61SJonghwa Lee 		return -EINVAL;
8073118e61SJonghwa Lee 
8173118e61SJonghwa Lee 	return val & max77686->mask;
8273118e61SJonghwa Lee }
8373118e61SJonghwa Lee 
84cf7d4a6fSTomasz Figa static unsigned long max77686_recalc_rate(struct clk_hw *hw,
85cf7d4a6fSTomasz Figa 					  unsigned long parent_rate)
86cf7d4a6fSTomasz Figa {
87cf7d4a6fSTomasz Figa 	return 32768;
88cf7d4a6fSTomasz Figa }
89cf7d4a6fSTomasz Figa 
9073118e61SJonghwa Lee static struct clk_ops max77686_clk_ops = {
9173118e61SJonghwa Lee 	.prepare	= max77686_clk_prepare,
9273118e61SJonghwa Lee 	.unprepare	= max77686_clk_unprepare,
9321c8ed2dSTomasz Figa 	.is_prepared	= max77686_clk_is_prepared,
94cf7d4a6fSTomasz Figa 	.recalc_rate	= max77686_recalc_rate,
9573118e61SJonghwa Lee };
9673118e61SJonghwa Lee 
9773118e61SJonghwa Lee static struct clk_init_data max77686_clks_init[MAX77686_CLKS_NUM] = {
9873118e61SJonghwa Lee 	[MAX77686_CLK_AP] = {
9973118e61SJonghwa Lee 		.name = "32khz_ap",
10073118e61SJonghwa Lee 		.ops = &max77686_clk_ops,
10173118e61SJonghwa Lee 		.flags = CLK_IS_ROOT,
10273118e61SJonghwa Lee 	},
10373118e61SJonghwa Lee 	[MAX77686_CLK_CP] = {
10473118e61SJonghwa Lee 		.name = "32khz_cp",
10573118e61SJonghwa Lee 		.ops = &max77686_clk_ops,
10673118e61SJonghwa Lee 		.flags = CLK_IS_ROOT,
10773118e61SJonghwa Lee 	},
10873118e61SJonghwa Lee 	[MAX77686_CLK_PMIC] = {
10973118e61SJonghwa Lee 		.name = "32khz_pmic",
11073118e61SJonghwa Lee 		.ops = &max77686_clk_ops,
11173118e61SJonghwa Lee 		.flags = CLK_IS_ROOT,
11273118e61SJonghwa Lee 	},
11373118e61SJonghwa Lee };
11473118e61SJonghwa Lee 
115badbc542STomasz Figa static struct clk *max77686_clk_register(struct device *dev,
11673118e61SJonghwa Lee 				struct max77686_clk *max77686)
11773118e61SJonghwa Lee {
11873118e61SJonghwa Lee 	struct clk *clk;
11973118e61SJonghwa Lee 	struct clk_hw *hw = &max77686->hw;
12073118e61SJonghwa Lee 
12173118e61SJonghwa Lee 	clk = clk_register(dev, hw);
12273118e61SJonghwa Lee 	if (IS_ERR(clk))
123badbc542STomasz Figa 		return clk;
12473118e61SJonghwa Lee 
125f1ba28a1SLaurent Pinchart 	max77686->lookup = kzalloc(sizeof(struct clk_lookup), GFP_KERNEL);
1269f58b9b9SAxel Lin 	if (!max77686->lookup)
127badbc542STomasz Figa 		return ERR_PTR(-ENOMEM);
12873118e61SJonghwa Lee 
12973118e61SJonghwa Lee 	max77686->lookup->con_id = hw->init->name;
13073118e61SJonghwa Lee 	max77686->lookup->clk = clk;
13173118e61SJonghwa Lee 
13273118e61SJonghwa Lee 	clkdev_add(max77686->lookup);
13373118e61SJonghwa Lee 
134badbc542STomasz Figa 	return clk;
13573118e61SJonghwa Lee }
13673118e61SJonghwa Lee 
137018ae93fSBill Pemberton static int max77686_clk_probe(struct platform_device *pdev)
13873118e61SJonghwa Lee {
13973118e61SJonghwa Lee 	struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
140*3966c947STomasz Figa 	struct max77686_clk *max77686_clks[MAX77686_CLKS_NUM];
141*3966c947STomasz Figa 	struct clk **clocks;
14273118e61SJonghwa Lee 	int i, ret;
14373118e61SJonghwa Lee 
144*3966c947STomasz Figa 	clocks = devm_kzalloc(&pdev->dev, sizeof(struct clk *)
14573118e61SJonghwa Lee 					* MAX77686_CLKS_NUM, GFP_KERNEL);
146*3966c947STomasz Figa 	if (!clocks)
14773118e61SJonghwa Lee 		return -ENOMEM;
14873118e61SJonghwa Lee 
14973118e61SJonghwa Lee 	for (i = 0; i < MAX77686_CLKS_NUM; i++) {
15073118e61SJonghwa Lee 		max77686_clks[i] = devm_kzalloc(&pdev->dev,
15173118e61SJonghwa Lee 					sizeof(struct max77686_clk), GFP_KERNEL);
1529f58b9b9SAxel Lin 		if (!max77686_clks[i])
15373118e61SJonghwa Lee 			return -ENOMEM;
15473118e61SJonghwa Lee 	}
15573118e61SJonghwa Lee 
15673118e61SJonghwa Lee 	for (i = 0; i < MAX77686_CLKS_NUM; i++) {
15773118e61SJonghwa Lee 		max77686_clks[i]->iodev = iodev;
15873118e61SJonghwa Lee 		max77686_clks[i]->mask = 1 << i;
15973118e61SJonghwa Lee 		max77686_clks[i]->hw.init = &max77686_clks_init[i];
16073118e61SJonghwa Lee 
161*3966c947STomasz Figa 		clocks[i] = max77686_clk_register(&pdev->dev, max77686_clks[i]);
162*3966c947STomasz Figa 		if (IS_ERR(clocks[i])) {
163*3966c947STomasz Figa 			ret = PTR_ERR(clocks[i]);
164d73ac4caSTomasz Figa 			dev_err(&pdev->dev, "failed to register %s\n",
165d73ac4caSTomasz Figa 				max77686_clks[i]->hw.init->name);
166d73ac4caSTomasz Figa 			goto err_clocks;
16773118e61SJonghwa Lee 		}
16873118e61SJonghwa Lee 	}
16973118e61SJonghwa Lee 
170*3966c947STomasz Figa 	platform_set_drvdata(pdev, clocks);
17173118e61SJonghwa Lee 
172b0f85177STomasz Figa 	return 0;
17373118e61SJonghwa Lee 
174d73ac4caSTomasz Figa err_clocks:
175d73ac4caSTomasz Figa 	for (--i; i >= 0; --i) {
176d73ac4caSTomasz Figa 		clkdev_drop(max77686_clks[i]->lookup);
177d73ac4caSTomasz Figa 		clk_unregister(max77686_clks[i]->hw.clk);
178d73ac4caSTomasz Figa 	}
179d73ac4caSTomasz Figa 
18073118e61SJonghwa Lee 	return ret;
18173118e61SJonghwa Lee }
18273118e61SJonghwa Lee 
1831fc7ad5dSBill Pemberton static int max77686_clk_remove(struct platform_device *pdev)
18473118e61SJonghwa Lee {
185*3966c947STomasz Figa 	struct clk **clocks = platform_get_drvdata(pdev);
18673118e61SJonghwa Lee 	int i;
18773118e61SJonghwa Lee 
18873118e61SJonghwa Lee 	for (i = 0; i < MAX77686_CLKS_NUM; i++) {
189*3966c947STomasz Figa 		struct clk_hw *hw = __clk_get_hw(clocks[i]);
190*3966c947STomasz Figa 		struct max77686_clk *max77686 = to_max77686_clk(hw);
191*3966c947STomasz Figa 
192*3966c947STomasz Figa 		clkdev_drop(max77686->lookup);
193*3966c947STomasz Figa 		clk_unregister(clocks[i]);
19473118e61SJonghwa Lee 	}
19573118e61SJonghwa Lee 	return 0;
19673118e61SJonghwa Lee }
19773118e61SJonghwa Lee 
19873118e61SJonghwa Lee static const struct platform_device_id max77686_clk_id[] = {
19973118e61SJonghwa Lee 	{ "max77686-clk", 0},
20073118e61SJonghwa Lee 	{ },
20173118e61SJonghwa Lee };
20273118e61SJonghwa Lee MODULE_DEVICE_TABLE(platform, max77686_clk_id);
20373118e61SJonghwa Lee 
20473118e61SJonghwa Lee static struct platform_driver max77686_clk_driver = {
20573118e61SJonghwa Lee 	.driver = {
20673118e61SJonghwa Lee 		.name  = "max77686-clk",
20773118e61SJonghwa Lee 		.owner = THIS_MODULE,
20873118e61SJonghwa Lee 	},
20973118e61SJonghwa Lee 	.probe = max77686_clk_probe,
210f9cfa630SBill Pemberton 	.remove = max77686_clk_remove,
21173118e61SJonghwa Lee 	.id_table = max77686_clk_id,
21273118e61SJonghwa Lee };
21373118e61SJonghwa Lee 
21473118e61SJonghwa Lee static int __init max77686_clk_init(void)
21573118e61SJonghwa Lee {
21673118e61SJonghwa Lee 	return platform_driver_register(&max77686_clk_driver);
21773118e61SJonghwa Lee }
21873118e61SJonghwa Lee subsys_initcall(max77686_clk_init);
21973118e61SJonghwa Lee 
22073118e61SJonghwa Lee static void __init max77686_clk_cleanup(void)
22173118e61SJonghwa Lee {
22273118e61SJonghwa Lee 	platform_driver_unregister(&max77686_clk_driver);
22373118e61SJonghwa Lee }
22473118e61SJonghwa Lee module_exit(max77686_clk_cleanup);
22573118e61SJonghwa Lee 
22673118e61SJonghwa Lee MODULE_DESCRIPTION("MAXIM 77686 Clock Driver");
22773118e61SJonghwa Lee MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>");
22873118e61SJonghwa Lee MODULE_LICENSE("GPL");
229