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