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