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