linux/drivers/clk/clk-max77686.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2//
   3// clk-max77686.c - Clock driver for Maxim 77686/MAX77802
   4//
   5// Copyright (C) 2012 Samsung Electornics
   6// Jonghwa Lee <jonghwa3.lee@samsung.com>
   7
   8#include <linux/kernel.h>
   9#include <linux/slab.h>
  10#include <linux/err.h>
  11#include <linux/module.h>
  12#include <linux/platform_device.h>
  13#include <linux/mfd/max77620.h>
  14#include <linux/mfd/max77686.h>
  15#include <linux/mfd/max77686-private.h>
  16#include <linux/clk-provider.h>
  17#include <linux/mutex.h>
  18#include <linux/clkdev.h>
  19#include <linux/of.h>
  20#include <linux/regmap.h>
  21
  22#include <dt-bindings/clock/maxim,max77686.h>
  23#include <dt-bindings/clock/maxim,max77802.h>
  24#include <dt-bindings/clock/maxim,max77620.h>
  25
  26#define MAX77802_CLOCK_LOW_JITTER_SHIFT 0x3
  27
  28enum max77686_chip_name {
  29        CHIP_MAX77686,
  30        CHIP_MAX77802,
  31        CHIP_MAX77620,
  32};
  33
  34struct max77686_hw_clk_info {
  35        const char *name;
  36        u32 clk_reg;
  37        u32 clk_enable_mask;
  38        u32 flags;
  39};
  40
  41struct max77686_clk_init_data {
  42        struct regmap *regmap;
  43        struct clk_hw hw;
  44        struct clk_init_data clk_idata;
  45        const struct max77686_hw_clk_info *clk_info;
  46};
  47
  48struct max77686_clk_driver_data {
  49        enum max77686_chip_name chip;
  50        struct max77686_clk_init_data *max_clk_data;
  51        size_t num_clks;
  52};
  53
  54static const struct
  55max77686_hw_clk_info max77686_hw_clks_info[MAX77686_CLKS_NUM] = {
  56        [MAX77686_CLK_AP] = {
  57                .name = "32khz_ap",
  58                .clk_reg = MAX77686_REG_32KHZ,
  59                .clk_enable_mask = BIT(MAX77686_CLK_AP),
  60        },
  61        [MAX77686_CLK_CP] = {
  62                .name = "32khz_cp",
  63                .clk_reg = MAX77686_REG_32KHZ,
  64                .clk_enable_mask = BIT(MAX77686_CLK_CP),
  65        },
  66        [MAX77686_CLK_PMIC] = {
  67                .name = "32khz_pmic",
  68                .clk_reg = MAX77686_REG_32KHZ,
  69                .clk_enable_mask = BIT(MAX77686_CLK_PMIC),
  70        },
  71};
  72
  73static const struct
  74max77686_hw_clk_info max77802_hw_clks_info[MAX77802_CLKS_NUM] = {
  75        [MAX77802_CLK_32K_AP] = {
  76                .name = "32khz_ap",
  77                .clk_reg = MAX77802_REG_32KHZ,
  78                .clk_enable_mask = BIT(MAX77802_CLK_32K_AP),
  79        },
  80        [MAX77802_CLK_32K_CP] = {
  81                .name = "32khz_cp",
  82                .clk_reg = MAX77802_REG_32KHZ,
  83                .clk_enable_mask = BIT(MAX77802_CLK_32K_CP),
  84        },
  85};
  86
  87static const struct
  88max77686_hw_clk_info max77620_hw_clks_info[MAX77620_CLKS_NUM] = {
  89        [MAX77620_CLK_32K_OUT0] = {
  90                .name = "32khz_out0",
  91                .clk_reg = MAX77620_REG_CNFG1_32K,
  92                .clk_enable_mask = MAX77620_CNFG1_32K_OUT0_EN,
  93        },
  94};
  95
  96static struct max77686_clk_init_data *to_max77686_clk_init_data(
  97                                struct clk_hw *hw)
  98{
  99        return container_of(hw, struct max77686_clk_init_data, hw);
 100}
 101
 102static int max77686_clk_prepare(struct clk_hw *hw)
 103{
 104        struct max77686_clk_init_data *max77686 = to_max77686_clk_init_data(hw);
 105
 106        return regmap_update_bits(max77686->regmap, max77686->clk_info->clk_reg,
 107                                  max77686->clk_info->clk_enable_mask,
 108                                  max77686->clk_info->clk_enable_mask);
 109}
 110
 111static void max77686_clk_unprepare(struct clk_hw *hw)
 112{
 113        struct max77686_clk_init_data *max77686 = to_max77686_clk_init_data(hw);
 114
 115        regmap_update_bits(max77686->regmap, max77686->clk_info->clk_reg,
 116                           max77686->clk_info->clk_enable_mask,
 117                           ~max77686->clk_info->clk_enable_mask);
 118}
 119
 120static int max77686_clk_is_prepared(struct clk_hw *hw)
 121{
 122        struct max77686_clk_init_data *max77686 = to_max77686_clk_init_data(hw);
 123        int ret;
 124        u32 val;
 125
 126        ret = regmap_read(max77686->regmap, max77686->clk_info->clk_reg, &val);
 127
 128        if (ret < 0)
 129                return -EINVAL;
 130
 131        return val & max77686->clk_info->clk_enable_mask;
 132}
 133
 134static unsigned long max77686_recalc_rate(struct clk_hw *hw,
 135                                          unsigned long parent_rate)
 136{
 137        return 32768;
 138}
 139
 140static const struct clk_ops max77686_clk_ops = {
 141        .prepare        = max77686_clk_prepare,
 142        .unprepare      = max77686_clk_unprepare,
 143        .is_prepared    = max77686_clk_is_prepared,
 144        .recalc_rate    = max77686_recalc_rate,
 145};
 146
 147static struct clk_hw *
 148of_clk_max77686_get(struct of_phandle_args *clkspec, void *data)
 149{
 150        struct max77686_clk_driver_data *drv_data = data;
 151        unsigned int idx = clkspec->args[0];
 152
 153        if (idx >= drv_data->num_clks) {
 154                pr_err("%s: invalid index %u\n", __func__, idx);
 155                return ERR_PTR(-EINVAL);
 156        }
 157
 158        return &drv_data->max_clk_data[idx].hw;
 159}
 160
 161static int max77686_clk_probe(struct platform_device *pdev)
 162{
 163        struct device *dev = &pdev->dev;
 164        struct device *parent = dev->parent;
 165        const struct platform_device_id *id = platform_get_device_id(pdev);
 166        struct max77686_clk_driver_data *drv_data;
 167        const struct max77686_hw_clk_info *hw_clks;
 168        struct regmap *regmap;
 169        int i, ret, num_clks;
 170
 171        drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL);
 172        if (!drv_data)
 173                return -ENOMEM;
 174
 175        regmap = dev_get_regmap(parent, NULL);
 176        if (!regmap) {
 177                dev_err(dev, "Failed to get rtc regmap\n");
 178                return -ENODEV;
 179        }
 180
 181        drv_data->chip = id->driver_data;
 182
 183        switch (drv_data->chip) {
 184        case CHIP_MAX77686:
 185                num_clks = MAX77686_CLKS_NUM;
 186                hw_clks = max77686_hw_clks_info;
 187                break;
 188
 189        case CHIP_MAX77802:
 190                num_clks = MAX77802_CLKS_NUM;
 191                hw_clks = max77802_hw_clks_info;
 192                break;
 193
 194        case CHIP_MAX77620:
 195                num_clks = MAX77620_CLKS_NUM;
 196                hw_clks = max77620_hw_clks_info;
 197                break;
 198
 199        default:
 200                dev_err(dev, "Unknown Chip ID\n");
 201                return -EINVAL;
 202        }
 203
 204        drv_data->num_clks = num_clks;
 205        drv_data->max_clk_data = devm_kcalloc(dev, num_clks,
 206                                              sizeof(*drv_data->max_clk_data),
 207                                              GFP_KERNEL);
 208        if (!drv_data->max_clk_data)
 209                return -ENOMEM;
 210
 211        for (i = 0; i < num_clks; i++) {
 212                struct max77686_clk_init_data *max_clk_data;
 213                const char *clk_name;
 214
 215                max_clk_data = &drv_data->max_clk_data[i];
 216
 217                max_clk_data->regmap = regmap;
 218                max_clk_data->clk_info = &hw_clks[i];
 219                max_clk_data->clk_idata.flags = hw_clks[i].flags;
 220                max_clk_data->clk_idata.ops = &max77686_clk_ops;
 221
 222                if (parent->of_node &&
 223                    !of_property_read_string_index(parent->of_node,
 224                                                   "clock-output-names",
 225                                                   i, &clk_name))
 226                        max_clk_data->clk_idata.name = clk_name;
 227                else
 228                        max_clk_data->clk_idata.name = hw_clks[i].name;
 229
 230                max_clk_data->hw.init = &max_clk_data->clk_idata;
 231
 232                ret = devm_clk_hw_register(dev, &max_clk_data->hw);
 233                if (ret) {
 234                        dev_err(dev, "Failed to clock register: %d\n", ret);
 235                        return ret;
 236                }
 237
 238                ret = devm_clk_hw_register_clkdev(dev, &max_clk_data->hw,
 239                                                  max_clk_data->clk_idata.name,
 240                                                  NULL);
 241                if (ret < 0) {
 242                        dev_err(dev, "Failed to clkdev register: %d\n", ret);
 243                        return ret;
 244                }
 245        }
 246
 247        if (parent->of_node) {
 248                ret = devm_of_clk_add_hw_provider(dev, of_clk_max77686_get,
 249                                                  drv_data);
 250
 251                if (ret < 0) {
 252                        dev_err(dev, "Failed to register OF clock provider: %d\n",
 253                                ret);
 254                        return ret;
 255                }
 256        }
 257
 258        /* MAX77802: Enable low-jitter mode on the 32khz clocks. */
 259        if (drv_data->chip == CHIP_MAX77802) {
 260                ret = regmap_update_bits(regmap, MAX77802_REG_32KHZ,
 261                                         1 << MAX77802_CLOCK_LOW_JITTER_SHIFT,
 262                                         1 << MAX77802_CLOCK_LOW_JITTER_SHIFT);
 263                if (ret < 0) {
 264                        dev_err(dev, "Failed to config low-jitter: %d\n", ret);
 265                        return ret;
 266                }
 267        }
 268
 269        return 0;
 270}
 271
 272static const struct platform_device_id max77686_clk_id[] = {
 273        { "max77686-clk", .driver_data = CHIP_MAX77686, },
 274        { "max77802-clk", .driver_data = CHIP_MAX77802, },
 275        { "max77620-clock", .driver_data = CHIP_MAX77620, },
 276        {},
 277};
 278MODULE_DEVICE_TABLE(platform, max77686_clk_id);
 279
 280static struct platform_driver max77686_clk_driver = {
 281        .driver = {
 282                .name  = "max77686-clk",
 283        },
 284        .probe = max77686_clk_probe,
 285        .id_table = max77686_clk_id,
 286};
 287
 288module_platform_driver(max77686_clk_driver);
 289
 290MODULE_DESCRIPTION("MAXIM 77686 Clock Driver");
 291MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>");
 292MODULE_LICENSE("GPL");
 293