1*2874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2f05259a6SMark Brown /* 3f05259a6SMark Brown * WM831x clock control 4f05259a6SMark Brown * 5f05259a6SMark Brown * Copyright 2011-2 Wolfson Microelectronics PLC. 6f05259a6SMark Brown * 7f05259a6SMark Brown * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 8f05259a6SMark Brown */ 9f05259a6SMark Brown 10f05259a6SMark Brown #include <linux/clk-provider.h> 11f05259a6SMark Brown #include <linux/delay.h> 12f05259a6SMark Brown #include <linux/module.h> 13f05259a6SMark Brown #include <linux/slab.h> 14f05259a6SMark Brown #include <linux/platform_device.h> 15f05259a6SMark Brown #include <linux/mfd/wm831x/core.h> 16f05259a6SMark Brown 17f05259a6SMark Brown struct wm831x_clk { 18f05259a6SMark Brown struct wm831x *wm831x; 19f05259a6SMark Brown struct clk_hw xtal_hw; 20f05259a6SMark Brown struct clk_hw fll_hw; 21f05259a6SMark Brown struct clk_hw clkout_hw; 22f05259a6SMark Brown bool xtal_ena; 23f05259a6SMark Brown }; 24f05259a6SMark Brown 25a5828a6cSMark Brown static int wm831x_xtal_is_prepared(struct clk_hw *hw) 26f05259a6SMark Brown { 27f05259a6SMark Brown struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, 28f05259a6SMark Brown xtal_hw); 29f05259a6SMark Brown 30f05259a6SMark Brown return clkdata->xtal_ena; 31f05259a6SMark Brown } 32f05259a6SMark Brown 33f05259a6SMark Brown static unsigned long wm831x_xtal_recalc_rate(struct clk_hw *hw, 34f05259a6SMark Brown unsigned long parent_rate) 35f05259a6SMark Brown { 36f05259a6SMark Brown struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, 37f05259a6SMark Brown xtal_hw); 38f05259a6SMark Brown 39f05259a6SMark Brown if (clkdata->xtal_ena) 40f05259a6SMark Brown return 32768; 41f05259a6SMark Brown else 42f05259a6SMark Brown return 0; 43f05259a6SMark Brown } 44f05259a6SMark Brown 45f05259a6SMark Brown static const struct clk_ops wm831x_xtal_ops = { 46a5828a6cSMark Brown .is_prepared = wm831x_xtal_is_prepared, 47f05259a6SMark Brown .recalc_rate = wm831x_xtal_recalc_rate, 48f05259a6SMark Brown }; 49f05259a6SMark Brown 500777066dSBhumika Goyal static const struct clk_init_data wm831x_xtal_init = { 51f05259a6SMark Brown .name = "xtal", 52f05259a6SMark Brown .ops = &wm831x_xtal_ops, 53f05259a6SMark Brown }; 54f05259a6SMark Brown 55f05259a6SMark Brown static const unsigned long wm831x_fll_auto_rates[] = { 56f05259a6SMark Brown 2048000, 57f05259a6SMark Brown 11289600, 58f05259a6SMark Brown 12000000, 59f05259a6SMark Brown 12288000, 60f05259a6SMark Brown 19200000, 61f05259a6SMark Brown 22579600, 62f05259a6SMark Brown 24000000, 63f05259a6SMark Brown 24576000, 64f05259a6SMark Brown }; 65f05259a6SMark Brown 66a5828a6cSMark Brown static int wm831x_fll_is_prepared(struct clk_hw *hw) 67f05259a6SMark Brown { 68f05259a6SMark Brown struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, 69f05259a6SMark Brown fll_hw); 70f05259a6SMark Brown struct wm831x *wm831x = clkdata->wm831x; 71f05259a6SMark Brown int ret; 72f05259a6SMark Brown 73f05259a6SMark Brown ret = wm831x_reg_read(wm831x, WM831X_FLL_CONTROL_1); 74f05259a6SMark Brown if (ret < 0) { 75f05259a6SMark Brown dev_err(wm831x->dev, "Unable to read FLL_CONTROL_1: %d\n", 76f05259a6SMark Brown ret); 77f05259a6SMark Brown return true; 78f05259a6SMark Brown } 79f05259a6SMark Brown 80f05259a6SMark Brown return (ret & WM831X_FLL_ENA) != 0; 81f05259a6SMark Brown } 82f05259a6SMark Brown 83f05259a6SMark Brown static int wm831x_fll_prepare(struct clk_hw *hw) 84f05259a6SMark Brown { 85f05259a6SMark Brown struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, 86f05259a6SMark Brown fll_hw); 87f05259a6SMark Brown struct wm831x *wm831x = clkdata->wm831x; 88f05259a6SMark Brown int ret; 89f05259a6SMark Brown 906f8b3145SAxel Lin ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_1, 91f05259a6SMark Brown WM831X_FLL_ENA, WM831X_FLL_ENA); 92f05259a6SMark Brown if (ret != 0) 93f05259a6SMark Brown dev_crit(wm831x->dev, "Failed to enable FLL: %d\n", ret); 94f05259a6SMark Brown 95ed784c53SNicholas Mc Guire /* wait 2-3 ms for new frequency taking effect */ 96ed784c53SNicholas Mc Guire usleep_range(2000, 3000); 97f05259a6SMark Brown 98f05259a6SMark Brown return ret; 99f05259a6SMark Brown } 100f05259a6SMark Brown 101f05259a6SMark Brown static void wm831x_fll_unprepare(struct clk_hw *hw) 102f05259a6SMark Brown { 103f05259a6SMark Brown struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, 104f05259a6SMark Brown fll_hw); 105f05259a6SMark Brown struct wm831x *wm831x = clkdata->wm831x; 106f05259a6SMark Brown int ret; 107f05259a6SMark Brown 1086f8b3145SAxel Lin ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_1, WM831X_FLL_ENA, 0); 109f05259a6SMark Brown if (ret != 0) 1106f8b3145SAxel Lin dev_crit(wm831x->dev, "Failed to disable FLL: %d\n", ret); 111f05259a6SMark Brown } 112f05259a6SMark Brown 113f05259a6SMark Brown static unsigned long wm831x_fll_recalc_rate(struct clk_hw *hw, 114f05259a6SMark Brown unsigned long parent_rate) 115f05259a6SMark Brown { 116f05259a6SMark Brown struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, 117f05259a6SMark Brown fll_hw); 118f05259a6SMark Brown struct wm831x *wm831x = clkdata->wm831x; 119f05259a6SMark Brown int ret; 120f05259a6SMark Brown 121f05259a6SMark Brown ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2); 122f05259a6SMark Brown if (ret < 0) { 123f05259a6SMark Brown dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_2: %d\n", 124f05259a6SMark Brown ret); 125f05259a6SMark Brown return 0; 126f05259a6SMark Brown } 127f05259a6SMark Brown 128f05259a6SMark Brown if (ret & WM831X_FLL_AUTO) 129f05259a6SMark Brown return wm831x_fll_auto_rates[ret & WM831X_FLL_AUTO_FREQ_MASK]; 130f05259a6SMark Brown 131f05259a6SMark Brown dev_err(wm831x->dev, "FLL only supported in AUTO mode\n"); 132f05259a6SMark Brown 133f05259a6SMark Brown return 0; 134f05259a6SMark Brown } 135f05259a6SMark Brown 136f05259a6SMark Brown static long wm831x_fll_round_rate(struct clk_hw *hw, unsigned long rate, 137f05259a6SMark Brown unsigned long *unused) 138f05259a6SMark Brown { 139f05259a6SMark Brown int best = 0; 140f05259a6SMark Brown int i; 141f05259a6SMark Brown 142f05259a6SMark Brown for (i = 0; i < ARRAY_SIZE(wm831x_fll_auto_rates); i++) 143f05259a6SMark Brown if (abs(wm831x_fll_auto_rates[i] - rate) < 144f05259a6SMark Brown abs(wm831x_fll_auto_rates[best] - rate)) 145f05259a6SMark Brown best = i; 146f05259a6SMark Brown 147f05259a6SMark Brown return wm831x_fll_auto_rates[best]; 148f05259a6SMark Brown } 149f05259a6SMark Brown 150f05259a6SMark Brown static int wm831x_fll_set_rate(struct clk_hw *hw, unsigned long rate, 151f05259a6SMark Brown unsigned long parent_rate) 152f05259a6SMark Brown { 153f05259a6SMark Brown struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, 154f05259a6SMark Brown fll_hw); 155f05259a6SMark Brown struct wm831x *wm831x = clkdata->wm831x; 156f05259a6SMark Brown int i; 157f05259a6SMark Brown 158f05259a6SMark Brown for (i = 0; i < ARRAY_SIZE(wm831x_fll_auto_rates); i++) 159f05259a6SMark Brown if (wm831x_fll_auto_rates[i] == rate) 160f05259a6SMark Brown break; 161f05259a6SMark Brown if (i == ARRAY_SIZE(wm831x_fll_auto_rates)) 162f05259a6SMark Brown return -EINVAL; 163f05259a6SMark Brown 164a5828a6cSMark Brown if (wm831x_fll_is_prepared(hw)) 165f05259a6SMark Brown return -EPERM; 166f05259a6SMark Brown 167f05259a6SMark Brown return wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_2, 168f05259a6SMark Brown WM831X_FLL_AUTO_FREQ_MASK, i); 169f05259a6SMark Brown } 170f05259a6SMark Brown 171f05259a6SMark Brown static const char *wm831x_fll_parents[] = { 172f05259a6SMark Brown "xtal", 173f05259a6SMark Brown "clkin", 174f05259a6SMark Brown }; 175f05259a6SMark Brown 176f05259a6SMark Brown static u8 wm831x_fll_get_parent(struct clk_hw *hw) 177f05259a6SMark Brown { 178f05259a6SMark Brown struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, 179f05259a6SMark Brown fll_hw); 180f05259a6SMark Brown struct wm831x *wm831x = clkdata->wm831x; 181f05259a6SMark Brown int ret; 182f05259a6SMark Brown 183f05259a6SMark Brown /* AUTO mode is always clocked from the crystal */ 184f05259a6SMark Brown ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2); 185f05259a6SMark Brown if (ret < 0) { 186f05259a6SMark Brown dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_2: %d\n", 187f05259a6SMark Brown ret); 188f05259a6SMark Brown return 0; 189f05259a6SMark Brown } 190f05259a6SMark Brown 191f05259a6SMark Brown if (ret & WM831X_FLL_AUTO) 192f05259a6SMark Brown return 0; 193f05259a6SMark Brown 194f05259a6SMark Brown ret = wm831x_reg_read(wm831x, WM831X_FLL_CONTROL_5); 195f05259a6SMark Brown if (ret < 0) { 196f05259a6SMark Brown dev_err(wm831x->dev, "Unable to read FLL_CONTROL_5: %d\n", 197f05259a6SMark Brown ret); 198f05259a6SMark Brown return 0; 199f05259a6SMark Brown } 200f05259a6SMark Brown 201f05259a6SMark Brown switch (ret & WM831X_FLL_CLK_SRC_MASK) { 202f05259a6SMark Brown case 0: 203f05259a6SMark Brown return 0; 204f05259a6SMark Brown case 1: 205f05259a6SMark Brown return 1; 206f05259a6SMark Brown default: 207f05259a6SMark Brown dev_err(wm831x->dev, "Unsupported FLL clock source %d\n", 208f05259a6SMark Brown ret & WM831X_FLL_CLK_SRC_MASK); 209f05259a6SMark Brown return 0; 210f05259a6SMark Brown } 211f05259a6SMark Brown } 212f05259a6SMark Brown 213f05259a6SMark Brown static const struct clk_ops wm831x_fll_ops = { 214a5828a6cSMark Brown .is_prepared = wm831x_fll_is_prepared, 215f05259a6SMark Brown .prepare = wm831x_fll_prepare, 216f05259a6SMark Brown .unprepare = wm831x_fll_unprepare, 217f05259a6SMark Brown .round_rate = wm831x_fll_round_rate, 218f05259a6SMark Brown .recalc_rate = wm831x_fll_recalc_rate, 219f05259a6SMark Brown .set_rate = wm831x_fll_set_rate, 220f05259a6SMark Brown .get_parent = wm831x_fll_get_parent, 221f05259a6SMark Brown }; 222f05259a6SMark Brown 2230777066dSBhumika Goyal static const struct clk_init_data wm831x_fll_init = { 224f05259a6SMark Brown .name = "fll", 225f05259a6SMark Brown .ops = &wm831x_fll_ops, 226f05259a6SMark Brown .parent_names = wm831x_fll_parents, 227f05259a6SMark Brown .num_parents = ARRAY_SIZE(wm831x_fll_parents), 228f05259a6SMark Brown .flags = CLK_SET_RATE_GATE, 229f05259a6SMark Brown }; 230f05259a6SMark Brown 231a5828a6cSMark Brown static int wm831x_clkout_is_prepared(struct clk_hw *hw) 232f05259a6SMark Brown { 233f05259a6SMark Brown struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, 234f05259a6SMark Brown clkout_hw); 235f05259a6SMark Brown struct wm831x *wm831x = clkdata->wm831x; 236f05259a6SMark Brown int ret; 237f05259a6SMark Brown 238f05259a6SMark Brown ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_1); 239f05259a6SMark Brown if (ret < 0) { 240f05259a6SMark Brown dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_1: %d\n", 241f05259a6SMark Brown ret); 24220979202SPan Bian return false; 243f05259a6SMark Brown } 244f05259a6SMark Brown 245f05259a6SMark Brown return (ret & WM831X_CLKOUT_ENA) != 0; 246f05259a6SMark Brown } 247f05259a6SMark Brown 248f05259a6SMark Brown static int wm831x_clkout_prepare(struct clk_hw *hw) 249f05259a6SMark Brown { 250f05259a6SMark Brown struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, 251f05259a6SMark Brown clkout_hw); 252f05259a6SMark Brown struct wm831x *wm831x = clkdata->wm831x; 253f05259a6SMark Brown int ret; 254f05259a6SMark Brown 255f05259a6SMark Brown ret = wm831x_reg_unlock(wm831x); 256f05259a6SMark Brown if (ret != 0) { 257f05259a6SMark Brown dev_crit(wm831x->dev, "Failed to lock registers: %d\n", ret); 258f05259a6SMark Brown return ret; 259f05259a6SMark Brown } 260f05259a6SMark Brown 261f05259a6SMark Brown ret = wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_1, 262f05259a6SMark Brown WM831X_CLKOUT_ENA, WM831X_CLKOUT_ENA); 263f05259a6SMark Brown if (ret != 0) 264f05259a6SMark Brown dev_crit(wm831x->dev, "Failed to enable CLKOUT: %d\n", ret); 265f05259a6SMark Brown 266f05259a6SMark Brown wm831x_reg_lock(wm831x); 267f05259a6SMark Brown 268f05259a6SMark Brown return ret; 269f05259a6SMark Brown } 270f05259a6SMark Brown 271f05259a6SMark Brown static void wm831x_clkout_unprepare(struct clk_hw *hw) 272f05259a6SMark Brown { 273f05259a6SMark Brown struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, 274f05259a6SMark Brown clkout_hw); 275f05259a6SMark Brown struct wm831x *wm831x = clkdata->wm831x; 276f05259a6SMark Brown int ret; 277f05259a6SMark Brown 278f05259a6SMark Brown ret = wm831x_reg_unlock(wm831x); 279f05259a6SMark Brown if (ret != 0) { 280f05259a6SMark Brown dev_crit(wm831x->dev, "Failed to lock registers: %d\n", ret); 281f05259a6SMark Brown return; 282f05259a6SMark Brown } 283f05259a6SMark Brown 284f05259a6SMark Brown ret = wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_1, 285f05259a6SMark Brown WM831X_CLKOUT_ENA, 0); 286f05259a6SMark Brown if (ret != 0) 287f05259a6SMark Brown dev_crit(wm831x->dev, "Failed to disable CLKOUT: %d\n", ret); 288f05259a6SMark Brown 289f05259a6SMark Brown wm831x_reg_lock(wm831x); 290f05259a6SMark Brown } 291f05259a6SMark Brown 292f05259a6SMark Brown static const char *wm831x_clkout_parents[] = { 293f05259a6SMark Brown "fll", 294bcc7fd20SAxel Lin "xtal", 295f05259a6SMark Brown }; 296f05259a6SMark Brown 297f05259a6SMark Brown static u8 wm831x_clkout_get_parent(struct clk_hw *hw) 298f05259a6SMark Brown { 299f05259a6SMark Brown struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, 300f05259a6SMark Brown clkout_hw); 301f05259a6SMark Brown struct wm831x *wm831x = clkdata->wm831x; 302f05259a6SMark Brown int ret; 303f05259a6SMark Brown 304f05259a6SMark Brown ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_1); 305f05259a6SMark Brown if (ret < 0) { 306f05259a6SMark Brown dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_1: %d\n", 307f05259a6SMark Brown ret); 308f05259a6SMark Brown return 0; 309f05259a6SMark Brown } 310f05259a6SMark Brown 311f05259a6SMark Brown if (ret & WM831X_CLKOUT_SRC) 312f05259a6SMark Brown return 1; 313bcc7fd20SAxel Lin else 314bcc7fd20SAxel Lin return 0; 315f05259a6SMark Brown } 316f05259a6SMark Brown 317f05259a6SMark Brown static int wm831x_clkout_set_parent(struct clk_hw *hw, u8 parent) 318f05259a6SMark Brown { 319f05259a6SMark Brown struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, 320f05259a6SMark Brown clkout_hw); 321f05259a6SMark Brown struct wm831x *wm831x = clkdata->wm831x; 322f05259a6SMark Brown 323f05259a6SMark Brown return wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_1, 324f05259a6SMark Brown WM831X_CLKOUT_SRC, 325f05259a6SMark Brown parent << WM831X_CLKOUT_SRC_SHIFT); 326f05259a6SMark Brown } 327f05259a6SMark Brown 328f05259a6SMark Brown static const struct clk_ops wm831x_clkout_ops = { 329a5828a6cSMark Brown .is_prepared = wm831x_clkout_is_prepared, 330f05259a6SMark Brown .prepare = wm831x_clkout_prepare, 331f05259a6SMark Brown .unprepare = wm831x_clkout_unprepare, 332f05259a6SMark Brown .get_parent = wm831x_clkout_get_parent, 333f05259a6SMark Brown .set_parent = wm831x_clkout_set_parent, 334f05259a6SMark Brown }; 335f05259a6SMark Brown 3360777066dSBhumika Goyal static const struct clk_init_data wm831x_clkout_init = { 337f05259a6SMark Brown .name = "clkout", 338f05259a6SMark Brown .ops = &wm831x_clkout_ops, 339f05259a6SMark Brown .parent_names = wm831x_clkout_parents, 340f05259a6SMark Brown .num_parents = ARRAY_SIZE(wm831x_clkout_parents), 341f05259a6SMark Brown .flags = CLK_SET_RATE_PARENT, 342f05259a6SMark Brown }; 343f05259a6SMark Brown 344018ae93fSBill Pemberton static int wm831x_clk_probe(struct platform_device *pdev) 345f05259a6SMark Brown { 346f05259a6SMark Brown struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); 347f05259a6SMark Brown struct wm831x_clk *clkdata; 348f05259a6SMark Brown int ret; 349f05259a6SMark Brown 350f05259a6SMark Brown clkdata = devm_kzalloc(&pdev->dev, sizeof(*clkdata), GFP_KERNEL); 351f05259a6SMark Brown if (!clkdata) 352f05259a6SMark Brown return -ENOMEM; 353f05259a6SMark Brown 35408442ce9SMark Brown clkdata->wm831x = wm831x; 35508442ce9SMark Brown 356f05259a6SMark Brown /* XTAL_ENA can only be set via OTP/InstantConfig so just read once */ 357f05259a6SMark Brown ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2); 358f05259a6SMark Brown if (ret < 0) { 359f05259a6SMark Brown dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_2: %d\n", 360f05259a6SMark Brown ret); 361f05259a6SMark Brown return ret; 362f05259a6SMark Brown } 363f05259a6SMark Brown clkdata->xtal_ena = ret & WM831X_XTAL_ENA; 364f05259a6SMark Brown 365f05259a6SMark Brown clkdata->xtal_hw.init = &wm831x_xtal_init; 366cc671d13SStephen Boyd ret = devm_clk_hw_register(&pdev->dev, &clkdata->xtal_hw); 367cc671d13SStephen Boyd if (ret) 368cc671d13SStephen Boyd return ret; 369f05259a6SMark Brown 370f05259a6SMark Brown clkdata->fll_hw.init = &wm831x_fll_init; 371cc671d13SStephen Boyd ret = devm_clk_hw_register(&pdev->dev, &clkdata->fll_hw); 372cc671d13SStephen Boyd if (ret) 373cc671d13SStephen Boyd return ret; 374f05259a6SMark Brown 375f05259a6SMark Brown clkdata->clkout_hw.init = &wm831x_clkout_init; 376cc671d13SStephen Boyd ret = devm_clk_hw_register(&pdev->dev, &clkdata->clkout_hw); 377cc671d13SStephen Boyd if (ret) 378cc671d13SStephen Boyd return ret; 379f05259a6SMark Brown 380c0431037SJingoo Han platform_set_drvdata(pdev, clkdata); 381f05259a6SMark Brown 382f05259a6SMark Brown return 0; 383f05259a6SMark Brown } 384f05259a6SMark Brown 385f05259a6SMark Brown static struct platform_driver wm831x_clk_driver = { 386f05259a6SMark Brown .probe = wm831x_clk_probe, 387f05259a6SMark Brown .driver = { 388f05259a6SMark Brown .name = "wm831x-clk", 389f05259a6SMark Brown }, 390f05259a6SMark Brown }; 391f05259a6SMark Brown 392f05259a6SMark Brown module_platform_driver(wm831x_clk_driver); 393f05259a6SMark Brown 394f05259a6SMark Brown /* Module information */ 395f05259a6SMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 396f05259a6SMark Brown MODULE_DESCRIPTION("WM831x clock driver"); 397f05259a6SMark Brown MODULE_LICENSE("GPL"); 398f05259a6SMark Brown MODULE_ALIAS("platform:wm831x-clk"); 399