Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

clk: add lpc18xx creg clk driver

The CREG block on lpc18xx contains configuration register
for two low power clocks. Support enabling of these two
clocks with a clk driver that access CREG trough the
syscon regmap interface.

These clocks are needed to support peripherals like the
internal RTC on lpc18xx.

Signed-off-by: Joachim Eastwood <manabian@gmail.com>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>

authored by

Joachim Eastwood and committed by
Stephen Boyd
378523d1 37655fae

+227
+1
drivers/clk/nxp/Makefile
··· 1 1 obj-$(CONFIG_ARCH_LPC18XX) += clk-lpc18xx-cgu.o 2 2 obj-$(CONFIG_ARCH_LPC18XX) += clk-lpc18xx-ccu.o 3 + obj-$(CONFIG_ARCH_LPC18XX) += clk-lpc18xx-creg.o 3 4 obj-$(CONFIG_ARCH_LPC32XX) += clk-lpc32xx.o
+226
drivers/clk/nxp/clk-lpc18xx-creg.c
··· 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 + 27 + enum { 28 + CREG_CLK_1KHZ, 29 + CREG_CLK_32KHZ, 30 + CREG_CLK_MAX, 31 + }; 32 + 33 + struct 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 + 48 + static 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 + 66 + static 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 + 75 + static 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 + 86 + static unsigned long clk_creg_1k_recalc_rate(struct clk_hw *hw, 87 + unsigned long parent_rate) 88 + { 89 + return parent_rate / 32; 90 + } 91 + 92 + static 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 + 100 + static 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 + 108 + static 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 + 118 + static 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 + 127 + static 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 + 134 + static 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 + 139 + static 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 + 151 + creg_clk->reg = syscon; 152 + creg_clk->hw.init = &init; 153 + 154 + if (dev) 155 + return devm_clk_register(dev, &creg_clk->hw); 156 + 157 + return clk_register(NULL, &creg_clk->hw); 158 + } 159 + 160 + static struct clk *clk_creg_early[CREG_CLK_MAX]; 161 + static struct clk_onecell_data clk_creg_early_data = { 162 + .clks = clk_creg_early, 163 + .clk_num = CREG_CLK_MAX, 164 + }; 165 + 166 + static void __init lpc18xx_creg_clk_init(struct device_node *np) 167 + { 168 + const char *clk_32khz_parent; 169 + struct regmap *syscon; 170 + 171 + syscon = syscon_node_to_regmap(np->parent); 172 + if (IS_ERR(syscon)) { 173 + pr_err("%s: syscon lookup failed\n", __func__); 174 + return; 175 + } 176 + 177 + clk_32khz_parent = of_clk_get_parent_name(np, 0); 178 + 179 + clk_creg_early[CREG_CLK_32KHZ] = 180 + clk_register_creg_clk(NULL, &clk_creg_clocks[CREG_CLK_32KHZ], 181 + &clk_32khz_parent, syscon); 182 + clk_creg_early[CREG_CLK_1KHZ] = ERR_PTR(-EPROBE_DEFER); 183 + 184 + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_creg_early_data); 185 + } 186 + CLK_OF_DECLARE(lpc18xx_creg_clk, "nxp,lpc1850-creg-clk", lpc18xx_creg_clk_init); 187 + 188 + static struct clk *clk_creg[CREG_CLK_MAX]; 189 + static struct clk_onecell_data clk_creg_data = { 190 + .clks = clk_creg, 191 + .clk_num = CREG_CLK_MAX, 192 + }; 193 + 194 + static int lpc18xx_creg_clk_probe(struct platform_device *pdev) 195 + { 196 + struct device_node *np = pdev->dev.of_node; 197 + struct regmap *syscon; 198 + 199 + syscon = syscon_node_to_regmap(np->parent); 200 + if (IS_ERR(syscon)) { 201 + dev_err(&pdev->dev, "syscon lookup failed\n"); 202 + return PTR_ERR(syscon); 203 + } 204 + 205 + clk_creg[CREG_CLK_32KHZ] = clk_creg_early[CREG_CLK_32KHZ]; 206 + clk_creg[CREG_CLK_1KHZ] = 207 + clk_register_creg_clk(NULL, &clk_creg_clocks[CREG_CLK_1KHZ], 208 + &clk_creg_clocks[CREG_CLK_32KHZ].name, 209 + syscon); 210 + 211 + return of_clk_add_provider(np, of_clk_src_onecell_get, &clk_creg_data); 212 + } 213 + 214 + static const struct of_device_id lpc18xx_creg_clk_of_match[] = { 215 + { .compatible = "nxp,lpc1850-creg-clk" }, 216 + {}, 217 + }; 218 + 219 + static struct platform_driver lpc18xx_creg_clk_driver = { 220 + .probe = lpc18xx_creg_clk_probe, 221 + .driver = { 222 + .name = "lpc18xx-creg-clk", 223 + .of_match_table = lpc18xx_creg_clk_of_match, 224 + }, 225 + }; 226 + builtin_platform_driver(lpc18xx_creg_clk_driver);