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