linux/drivers/clk/nxp/clk-lpc18xx-creg.c
<<
>>
Prefs
   1/*
   2 * Clk driver for NXP LPC18xx/43xx Configuration Registers (CREG)
   3 *
   4 * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
   5 *
   6 * This file is licensed under the terms of the GNU General Public
   7 * License version 2. This program is licensed "as is" without any
   8 * warranty of any kind, whether express or implied.
   9 */
  10
  11#include <linux/clk-provider.h>
  12#include <linux/delay.h>
  13#include <linux/kernel.h>
  14#include <linux/mfd/syscon.h>
  15#include <linux/of.h>
  16#include <linux/platform_device.h>
  17#include <linux/regmap.h>
  18
  19#define LPC18XX_CREG_CREG0                      0x004
  20#define  LPC18XX_CREG_CREG0_EN1KHZ              BIT(0)
  21#define  LPC18XX_CREG_CREG0_EN32KHZ             BIT(1)
  22#define  LPC18XX_CREG_CREG0_RESET32KHZ          BIT(2)
  23#define  LPC18XX_CREG_CREG0_PD32KHZ             BIT(3)
  24
  25#define to_clk_creg(_hw) container_of(_hw, struct clk_creg_data, hw)
  26
  27enum {
  28        CREG_CLK_1KHZ,
  29        CREG_CLK_32KHZ,
  30        CREG_CLK_MAX,
  31};
  32
  33struct clk_creg_data {
  34        struct clk_hw hw;
  35        const char *name;
  36        struct regmap *reg;
  37        unsigned int en_mask;
  38        const struct clk_ops *ops;
  39};
  40
  41#define CREG_CLK(_name, _emask, _ops)           \
  42{                                               \
  43        .name = _name,                          \
  44        .en_mask = LPC18XX_CREG_CREG0_##_emask, \
  45        .ops = &_ops,                           \
  46}
  47
  48static int clk_creg_32k_prepare(struct clk_hw *hw)
  49{
  50        struct clk_creg_data *creg = to_clk_creg(hw);
  51        int ret;
  52
  53        ret = regmap_update_bits(creg->reg, LPC18XX_CREG_CREG0,
  54                                 LPC18XX_CREG_CREG0_PD32KHZ |
  55                                 LPC18XX_CREG_CREG0_RESET32KHZ, 0);
  56
  57        /*
  58         * Powering up the 32k oscillator takes a long while
  59         * and sadly there aren't any status bit to poll.
  60         */
  61        msleep(2500);
  62
  63        return ret;
  64}
  65
  66static void clk_creg_32k_unprepare(struct clk_hw *hw)
  67{
  68        struct clk_creg_data *creg = to_clk_creg(hw);
  69
  70        regmap_update_bits(creg->reg, LPC18XX_CREG_CREG0,
  71                           LPC18XX_CREG_CREG0_PD32KHZ,
  72                           LPC18XX_CREG_CREG0_PD32KHZ);
  73}
  74
  75static int clk_creg_32k_is_prepared(struct clk_hw *hw)
  76{
  77        struct clk_creg_data *creg = to_clk_creg(hw);
  78        u32 reg;
  79
  80        regmap_read(creg->reg, LPC18XX_CREG_CREG0, &reg);
  81
  82        return !(reg & LPC18XX_CREG_CREG0_PD32KHZ) &&
  83               !(reg & LPC18XX_CREG_CREG0_RESET32KHZ);
  84}
  85
  86static unsigned long clk_creg_1k_recalc_rate(struct clk_hw *hw,
  87                                             unsigned long parent_rate)
  88{
  89        return parent_rate / 32;
  90}
  91
  92static int clk_creg_enable(struct clk_hw *hw)
  93{
  94        struct clk_creg_data *creg = to_clk_creg(hw);
  95
  96        return regmap_update_bits(creg->reg, LPC18XX_CREG_CREG0,
  97                                  creg->en_mask, creg->en_mask);
  98}
  99
 100static void clk_creg_disable(struct clk_hw *hw)
 101{
 102        struct clk_creg_data *creg = to_clk_creg(hw);
 103
 104        regmap_update_bits(creg->reg, LPC18XX_CREG_CREG0,
 105                           creg->en_mask, 0);
 106}
 107
 108static int clk_creg_is_enabled(struct clk_hw *hw)
 109{
 110        struct clk_creg_data *creg = to_clk_creg(hw);
 111        u32 reg;
 112
 113        regmap_read(creg->reg, LPC18XX_CREG_CREG0, &reg);
 114
 115        return !!(reg & creg->en_mask);
 116}
 117
 118static const struct clk_ops clk_creg_32k = {
 119        .enable         = clk_creg_enable,
 120        .disable        = clk_creg_disable,
 121        .is_enabled     = clk_creg_is_enabled,
 122        .prepare        = clk_creg_32k_prepare,
 123        .unprepare      = clk_creg_32k_unprepare,
 124        .is_prepared    = clk_creg_32k_is_prepared,
 125};
 126
 127static const struct clk_ops clk_creg_1k = {
 128        .enable         = clk_creg_enable,
 129        .disable        = clk_creg_disable,
 130        .is_enabled     = clk_creg_is_enabled,
 131        .recalc_rate    = clk_creg_1k_recalc_rate,
 132};
 133
 134static struct clk_creg_data clk_creg_clocks[] = {
 135        [CREG_CLK_1KHZ]  = CREG_CLK("1khz_clk",  EN1KHZ,  clk_creg_1k),
 136        [CREG_CLK_32KHZ] = CREG_CLK("32khz_clk", EN32KHZ, clk_creg_32k),
 137};
 138
 139static struct clk *clk_register_creg_clk(struct device *dev,
 140                                         struct clk_creg_data *creg_clk,
 141                                         const char **parent_name,
 142                                         struct regmap *syscon)
 143{
 144        struct clk_init_data init;
 145
 146        init.ops = creg_clk->ops;
 147        init.name = creg_clk->name;
 148        init.parent_names = parent_name;
 149        init.num_parents = 1;
 150        init.flags = 0;
 151
 152        creg_clk->reg = syscon;
 153        creg_clk->hw.init = &init;
 154
 155        if (dev)
 156                return devm_clk_register(dev, &creg_clk->hw);
 157
 158        return clk_register(NULL, &creg_clk->hw);
 159}
 160
 161static struct clk *clk_creg_early[CREG_CLK_MAX];
 162static struct clk_onecell_data clk_creg_early_data = {
 163        .clks = clk_creg_early,
 164        .clk_num = CREG_CLK_MAX,
 165};
 166
 167static void __init lpc18xx_creg_clk_init(struct device_node *np)
 168{
 169        const char *clk_32khz_parent;
 170        struct regmap *syscon;
 171
 172        syscon = syscon_node_to_regmap(np->parent);
 173        if (IS_ERR(syscon)) {
 174                pr_err("%s: syscon lookup failed\n", __func__);
 175                return;
 176        }
 177
 178        clk_32khz_parent = of_clk_get_parent_name(np, 0);
 179
 180        clk_creg_early[CREG_CLK_32KHZ] =
 181                clk_register_creg_clk(NULL, &clk_creg_clocks[CREG_CLK_32KHZ],
 182                                      &clk_32khz_parent, syscon);
 183        clk_creg_early[CREG_CLK_1KHZ] = ERR_PTR(-EPROBE_DEFER);
 184
 185        of_clk_add_provider(np, of_clk_src_onecell_get, &clk_creg_early_data);
 186}
 187CLK_OF_DECLARE_DRIVER(lpc18xx_creg_clk, "nxp,lpc1850-creg-clk",
 188                      lpc18xx_creg_clk_init);
 189
 190static struct clk *clk_creg[CREG_CLK_MAX];
 191static struct clk_onecell_data clk_creg_data = {
 192        .clks = clk_creg,
 193        .clk_num = CREG_CLK_MAX,
 194};
 195
 196static int lpc18xx_creg_clk_probe(struct platform_device *pdev)
 197{
 198        struct device_node *np = pdev->dev.of_node;
 199        struct regmap *syscon;
 200
 201        syscon = syscon_node_to_regmap(np->parent);
 202        if (IS_ERR(syscon)) {
 203                dev_err(&pdev->dev, "syscon lookup failed\n");
 204                return PTR_ERR(syscon);
 205        }
 206
 207        clk_creg[CREG_CLK_32KHZ] = clk_creg_early[CREG_CLK_32KHZ];
 208        clk_creg[CREG_CLK_1KHZ] =
 209                clk_register_creg_clk(NULL, &clk_creg_clocks[CREG_CLK_1KHZ],
 210                                      &clk_creg_clocks[CREG_CLK_32KHZ].name,
 211                                      syscon);
 212
 213        return of_clk_add_provider(np, of_clk_src_onecell_get, &clk_creg_data);
 214}
 215
 216static const struct of_device_id lpc18xx_creg_clk_of_match[] = {
 217        { .compatible = "nxp,lpc1850-creg-clk" },
 218        {},
 219};
 220
 221static struct platform_driver lpc18xx_creg_clk_driver = {
 222        .probe = lpc18xx_creg_clk_probe,
 223        .driver = {
 224                .name = "lpc18xx-creg-clk",
 225                .of_match_table = lpc18xx_creg_clk_of_match,
 226        },
 227};
 228builtin_platform_driver(lpc18xx_creg_clk_driver);
 229