linux/drivers/clk/clk-s2mps11.c
<<
>>
Prefs
   1/*
   2 * clk-s2mps11.c - Clock driver for S2MPS11.
   3 *
   4 * Copyright (C) 2013,2014 Samsung Electornics
   5 *
   6 * This program is free software; you can redistribute  it and/or modify it
   7 * under  the terms of  the GNU General  Public License as published by the
   8 * Free Software Foundation;  either version 2 of the  License, or (at your
   9 * option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  16 */
  17
  18#include <linux/module.h>
  19#include <linux/err.h>
  20#include <linux/of.h>
  21#include <linux/clkdev.h>
  22#include <linux/regmap.h>
  23#include <linux/clk-provider.h>
  24#include <linux/platform_device.h>
  25#include <linux/mfd/samsung/s2mps11.h>
  26#include <linux/mfd/samsung/s2mps13.h>
  27#include <linux/mfd/samsung/s2mps14.h>
  28#include <linux/mfd/samsung/s5m8767.h>
  29#include <linux/mfd/samsung/core.h>
  30
  31enum {
  32        S2MPS11_CLK_AP = 0,
  33        S2MPS11_CLK_CP,
  34        S2MPS11_CLK_BT,
  35        S2MPS11_CLKS_NUM,
  36};
  37
  38struct s2mps11_clk {
  39        struct sec_pmic_dev *iodev;
  40        struct device_node *clk_np;
  41        struct clk_hw hw;
  42        struct clk *clk;
  43        struct clk_lookup *lookup;
  44        u32 mask;
  45        unsigned int reg;
  46};
  47
  48static struct s2mps11_clk *to_s2mps11_clk(struct clk_hw *hw)
  49{
  50        return container_of(hw, struct s2mps11_clk, hw);
  51}
  52
  53static int s2mps11_clk_prepare(struct clk_hw *hw)
  54{
  55        struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw);
  56
  57        return regmap_update_bits(s2mps11->iodev->regmap_pmic,
  58                                 s2mps11->reg,
  59                                 s2mps11->mask, s2mps11->mask);
  60}
  61
  62static void s2mps11_clk_unprepare(struct clk_hw *hw)
  63{
  64        struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw);
  65
  66        regmap_update_bits(s2mps11->iodev->regmap_pmic, s2mps11->reg,
  67                           s2mps11->mask, ~s2mps11->mask);
  68}
  69
  70static int s2mps11_clk_is_prepared(struct clk_hw *hw)
  71{
  72        int ret;
  73        u32 val;
  74        struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw);
  75
  76        ret = regmap_read(s2mps11->iodev->regmap_pmic,
  77                                s2mps11->reg, &val);
  78        if (ret < 0)
  79                return -EINVAL;
  80
  81        return val & s2mps11->mask;
  82}
  83
  84static unsigned long s2mps11_clk_recalc_rate(struct clk_hw *hw,
  85                                             unsigned long parent_rate)
  86{
  87        return 32768;
  88}
  89
  90static struct clk_ops s2mps11_clk_ops = {
  91        .prepare        = s2mps11_clk_prepare,
  92        .unprepare      = s2mps11_clk_unprepare,
  93        .is_prepared    = s2mps11_clk_is_prepared,
  94        .recalc_rate    = s2mps11_clk_recalc_rate,
  95};
  96
  97/* This s2mps11_clks_init tructure is common to s2mps11, s2mps13 and s2mps14 */
  98static struct clk_init_data s2mps11_clks_init[S2MPS11_CLKS_NUM] = {
  99        [S2MPS11_CLK_AP] = {
 100                .name = "s2mps11_ap",
 101                .ops = &s2mps11_clk_ops,
 102        },
 103        [S2MPS11_CLK_CP] = {
 104                .name = "s2mps11_cp",
 105                .ops = &s2mps11_clk_ops,
 106        },
 107        [S2MPS11_CLK_BT] = {
 108                .name = "s2mps11_bt",
 109                .ops = &s2mps11_clk_ops,
 110        },
 111};
 112
 113static struct device_node *s2mps11_clk_parse_dt(struct platform_device *pdev,
 114                struct clk_init_data *clks_init)
 115{
 116        struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
 117        struct device_node *clk_np;
 118        int i;
 119
 120        if (!iodev->dev->of_node)
 121                return ERR_PTR(-EINVAL);
 122
 123        clk_np = of_get_child_by_name(iodev->dev->of_node, "clocks");
 124        if (!clk_np) {
 125                dev_err(&pdev->dev, "could not find clock sub-node\n");
 126                return ERR_PTR(-EINVAL);
 127        }
 128
 129        for (i = 0; i < S2MPS11_CLKS_NUM; i++)
 130                of_property_read_string_index(clk_np, "clock-output-names", i,
 131                                &clks_init[i].name);
 132
 133        return clk_np;
 134}
 135
 136static int s2mps11_clk_probe(struct platform_device *pdev)
 137{
 138        struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
 139        struct s2mps11_clk *s2mps11_clks;
 140        struct clk_hw_onecell_data *clk_data;
 141        unsigned int s2mps11_reg;
 142        int i, ret = 0;
 143        enum sec_device_type hwid = platform_get_device_id(pdev)->driver_data;
 144
 145        s2mps11_clks = devm_kcalloc(&pdev->dev, S2MPS11_CLKS_NUM,
 146                                sizeof(*s2mps11_clks), GFP_KERNEL);
 147        if (!s2mps11_clks)
 148                return -ENOMEM;
 149
 150        clk_data = devm_kzalloc(&pdev->dev, sizeof(*clk_data) +
 151                                sizeof(*clk_data->hws) * S2MPS11_CLKS_NUM,
 152                                GFP_KERNEL);
 153        if (!clk_data)
 154                return -ENOMEM;
 155
 156        switch (hwid) {
 157        case S2MPS11X:
 158                s2mps11_reg = S2MPS11_REG_RTC_CTRL;
 159                break;
 160        case S2MPS13X:
 161                s2mps11_reg = S2MPS13_REG_RTCCTRL;
 162                break;
 163        case S2MPS14X:
 164                s2mps11_reg = S2MPS14_REG_RTCCTRL;
 165                break;
 166        case S5M8767X:
 167                s2mps11_reg = S5M8767_REG_CTRL1;
 168                break;
 169        default:
 170                dev_err(&pdev->dev, "Invalid device type\n");
 171                return -EINVAL;
 172        }
 173
 174        /* Store clocks of_node in first element of s2mps11_clks array */
 175        s2mps11_clks->clk_np = s2mps11_clk_parse_dt(pdev, s2mps11_clks_init);
 176        if (IS_ERR(s2mps11_clks->clk_np))
 177                return PTR_ERR(s2mps11_clks->clk_np);
 178
 179        for (i = 0; i < S2MPS11_CLKS_NUM; i++) {
 180                if (i == S2MPS11_CLK_CP && hwid == S2MPS14X)
 181                        continue; /* Skip clocks not present in some devices */
 182                s2mps11_clks[i].iodev = iodev;
 183                s2mps11_clks[i].hw.init = &s2mps11_clks_init[i];
 184                s2mps11_clks[i].mask = 1 << i;
 185                s2mps11_clks[i].reg = s2mps11_reg;
 186
 187                s2mps11_clks[i].clk = devm_clk_register(&pdev->dev,
 188                                                        &s2mps11_clks[i].hw);
 189                if (IS_ERR(s2mps11_clks[i].clk)) {
 190                        dev_err(&pdev->dev, "Fail to register : %s\n",
 191                                                s2mps11_clks_init[i].name);
 192                        ret = PTR_ERR(s2mps11_clks[i].clk);
 193                        goto err_reg;
 194                }
 195
 196                s2mps11_clks[i].lookup = clkdev_hw_create(&s2mps11_clks[i].hw,
 197                                        s2mps11_clks_init[i].name, NULL);
 198                if (!s2mps11_clks[i].lookup) {
 199                        ret = -ENOMEM;
 200                        goto err_reg;
 201                }
 202                clk_data->hws[i] = &s2mps11_clks[i].hw;
 203        }
 204
 205        clk_data->num = S2MPS11_CLKS_NUM;
 206        of_clk_add_hw_provider(s2mps11_clks->clk_np, of_clk_hw_onecell_get,
 207                               clk_data);
 208
 209        platform_set_drvdata(pdev, s2mps11_clks);
 210
 211        return ret;
 212
 213err_reg:
 214        while (--i >= 0)
 215                clkdev_drop(s2mps11_clks[i].lookup);
 216
 217        return ret;
 218}
 219
 220static int s2mps11_clk_remove(struct platform_device *pdev)
 221{
 222        struct s2mps11_clk *s2mps11_clks = platform_get_drvdata(pdev);
 223        int i;
 224
 225        of_clk_del_provider(s2mps11_clks[0].clk_np);
 226        /* Drop the reference obtained in s2mps11_clk_parse_dt */
 227        of_node_put(s2mps11_clks[0].clk_np);
 228
 229        for (i = 0; i < S2MPS11_CLKS_NUM; i++) {
 230                /* Skip clocks not present on S2MPS14 */
 231                if (!s2mps11_clks[i].lookup)
 232                        continue;
 233                clkdev_drop(s2mps11_clks[i].lookup);
 234        }
 235
 236        return 0;
 237}
 238
 239static const struct platform_device_id s2mps11_clk_id[] = {
 240        { "s2mps11-clk", S2MPS11X},
 241        { "s2mps13-clk", S2MPS13X},
 242        { "s2mps14-clk", S2MPS14X},
 243        { "s5m8767-clk", S5M8767X},
 244        { },
 245};
 246MODULE_DEVICE_TABLE(platform, s2mps11_clk_id);
 247
 248static struct platform_driver s2mps11_clk_driver = {
 249        .driver = {
 250                .name  = "s2mps11-clk",
 251        },
 252        .probe = s2mps11_clk_probe,
 253        .remove = s2mps11_clk_remove,
 254        .id_table = s2mps11_clk_id,
 255};
 256
 257static int __init s2mps11_clk_init(void)
 258{
 259        return platform_driver_register(&s2mps11_clk_driver);
 260}
 261subsys_initcall(s2mps11_clk_init);
 262
 263static void __exit s2mps11_clk_cleanup(void)
 264{
 265        platform_driver_unregister(&s2mps11_clk_driver);
 266}
 267module_exit(s2mps11_clk_cleanup);
 268
 269MODULE_DESCRIPTION("S2MPS11 Clock Driver");
 270MODULE_AUTHOR("Yadwinder Singh Brar <yadi.brar@samsung.com>");
 271MODULE_LICENSE("GPL");
 272