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