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