cregit-Linux how code gets into the kernel

Release 4.7 drivers/clk/clk-wm831x.c

Directory: drivers/clk
/*
 * WM831x clock control
 *
 * Copyright 2011-2 Wolfson Microelectronics PLC.
 *
 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
 *
 *  This program is free software; you can redistribute  it and/or modify it
 *  under  the terms of  the GNU General  Public License as published by the
 *  Free Software Foundation;  either version 2 of the  License, or (at your
 *  option) any later version.
 *
 */

#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/mfd/wm831x/core.h>


struct wm831x_clk {
	
struct wm831x *wm831x;
	
struct clk_hw xtal_hw;
	
struct clk_hw fll_hw;
	
struct clk_hw clkout_hw;
	
struct clk *xtal;
	
struct clk *fll;
	
struct clk *clkout;
	
bool xtal_ena;
};


static int wm831x_xtal_is_prepared(struct clk_hw *hw) { struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, xtal_hw); return clkdata->xtal_ena; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown31100.00%2100.00%
Total31100.00%2100.00%


static unsigned long wm831x_xtal_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, xtal_hw); if (clkdata->xtal_ena) return 32768; else return 0; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown44100.00%1100.00%
Total44100.00%1100.00%

static const struct clk_ops wm831x_xtal_ops = { .is_prepared = wm831x_xtal_is_prepared, .recalc_rate = wm831x_xtal_recalc_rate, }; static struct clk_init_data wm831x_xtal_init = { .name = "xtal", .ops = &wm831x_xtal_ops, }; static const unsigned long wm831x_fll_auto_rates[] = { 2048000, 11289600, 12000000, 12288000, 19200000, 22579600, 24000000, 24576000, };
static int wm831x_fll_is_prepared(struct clk_hw *hw) { struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, fll_hw); struct wm831x *wm831x = clkdata->wm831x; int ret; ret = wm831x_reg_read(wm831x, WM831X_FLL_CONTROL_1); if (ret < 0) { dev_err(wm831x->dev, "Unable to read FLL_CONTROL_1: %d\n", ret); return true; } return (ret & WM831X_FLL_ENA) != 0; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown78100.00%2100.00%
Total78100.00%2100.00%


static int wm831x_fll_prepare(struct clk_hw *hw) { struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, fll_hw); struct wm831x *wm831x = clkdata->wm831x; int ret; ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_1, WM831X_FLL_ENA, WM831X_FLL_ENA); if (ret != 0) dev_crit(wm831x->dev, "Failed to enable FLL: %d\n", ret); usleep_range(2000, 2000); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown7798.72%150.00%
axel linaxel lin11.28%150.00%
Total78100.00%2100.00%


static void wm831x_fll_unprepare(struct clk_hw *hw) { struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, fll_hw); struct wm831x *wm831x = clkdata->wm831x; int ret; ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_1, WM831X_FLL_ENA, 0); if (ret != 0) dev_crit(wm831x->dev, "Failed to disable FLL: %d\n", ret); }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown6697.06%150.00%
axel linaxel lin22.94%150.00%
Total68100.00%2100.00%


static unsigned long wm831x_fll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, fll_hw); struct wm831x *wm831x = clkdata->wm831x; int ret; ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2); if (ret < 0) { dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_2: %d\n", ret); return 0; } if (ret & WM831X_FLL_AUTO) return wm831x_fll_auto_rates[ret & WM831X_FLL_AUTO_FREQ_MASK]; dev_err(wm831x->dev, "FLL only supported in AUTO mode\n"); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown100100.00%1100.00%
Total100100.00%1100.00%


static long wm831x_fll_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *unused) { int best = 0; int i; for (i = 0; i < ARRAY_SIZE(wm831x_fll_auto_rates); i++) if (abs(wm831x_fll_auto_rates[i] - rate) < abs(wm831x_fll_auto_rates[best] - rate)) best = i; return wm831x_fll_auto_rates[best]; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown76100.00%1100.00%
Total76100.00%1100.00%


static int wm831x_fll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, fll_hw); struct wm831x *wm831x = clkdata->wm831x; int i; for (i = 0; i < ARRAY_SIZE(wm831x_fll_auto_rates); i++) if (wm831x_fll_auto_rates[i] == rate) break; if (i == ARRAY_SIZE(wm831x_fll_auto_rates)) return -EINVAL; if (wm831x_fll_is_prepared(hw)) return -EPERM; return wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_2, WM831X_FLL_AUTO_FREQ_MASK, i); }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown108100.00%2100.00%
Total108100.00%2100.00%

static const char *wm831x_fll_parents[] = { "xtal", "clkin", };
static u8 wm831x_fll_get_parent(struct clk_hw *hw) { struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, fll_hw); struct wm831x *wm831x = clkdata->wm831x; int ret; /* AUTO mode is always clocked from the crystal */ ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2); if (ret < 0) { dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_2: %d\n", ret); return 0; } if (ret & WM831X_FLL_AUTO) return 0; ret = wm831x_reg_read(wm831x, WM831X_FLL_CONTROL_5); if (ret < 0) { dev_err(wm831x->dev, "Unable to read FLL_CONTROL_5: %d\n", ret); return 0; } switch (ret & WM831X_FLL_CLK_SRC_MASK) { case 0: return 0; case 1: return 1; default: dev_err(wm831x->dev, "Unsupported FLL clock source %d\n", ret & WM831X_FLL_CLK_SRC_MASK); return 0; } }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown147100.00%1100.00%
Total147100.00%1100.00%

static const struct clk_ops wm831x_fll_ops = { .is_prepared = wm831x_fll_is_prepared, .prepare = wm831x_fll_prepare, .unprepare = wm831x_fll_unprepare, .round_rate = wm831x_fll_round_rate, .recalc_rate = wm831x_fll_recalc_rate, .set_rate = wm831x_fll_set_rate, .get_parent = wm831x_fll_get_parent, }; static struct clk_init_data wm831x_fll_init = { .name = "fll", .ops = &wm831x_fll_ops, .parent_names = wm831x_fll_parents, .num_parents = ARRAY_SIZE(wm831x_fll_parents), .flags = CLK_SET_RATE_GATE, };
static int wm831x_clkout_is_prepared(struct clk_hw *hw) { struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, clkout_hw); struct wm831x *wm831x = clkdata->wm831x; int ret; ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_1); if (ret < 0) { dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_1: %d\n", ret); return true; } return (ret & WM831X_CLKOUT_ENA) != 0; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown78100.00%2100.00%
Total78100.00%2100.00%


static int wm831x_clkout_prepare(struct clk_hw *hw) { struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, clkout_hw); struct wm831x *wm831x = clkdata->wm831x; int ret; ret = wm831x_reg_unlock(wm831x); if (ret != 0) { dev_crit(wm831x->dev, "Failed to lock registers: %d\n", ret); return ret; } ret = wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_1, WM831X_CLKOUT_ENA, WM831X_CLKOUT_ENA); if (ret != 0) dev_crit(wm831x->dev, "Failed to enable CLKOUT: %d\n", ret); wm831x_reg_lock(wm831x); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown105100.00%1100.00%
Total105100.00%1100.00%


static void wm831x_clkout_unprepare(struct clk_hw *hw) { struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, clkout_hw); struct wm831x *wm831x = clkdata->wm831x; int ret; ret = wm831x_reg_unlock(wm831x); if (ret != 0) { dev_crit(wm831x->dev, "Failed to lock registers: %d\n", ret); return; } ret = wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_1, WM831X_CLKOUT_ENA, 0); if (ret != 0) dev_crit(wm831x->dev, "Failed to disable CLKOUT: %d\n", ret); wm831x_reg_lock(wm831x); }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown100100.00%1100.00%
Total100100.00%1100.00%

static const char *wm831x_clkout_parents[] = { "fll", "xtal", };
static u8 wm831x_clkout_get_parent(struct clk_hw *hw) { struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, clkout_hw); struct wm831x *wm831x = clkdata->wm831x; int ret; ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_1); if (ret < 0) { dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_1: %d\n", ret); return 0; } if (ret & WM831X_CLKOUT_SRC) return 1; else return 0; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown8097.56%150.00%
axel linaxel lin22.44%150.00%
Total82100.00%2100.00%


static int wm831x_clkout_set_parent(struct clk_hw *hw, u8 parent) { struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk, clkout_hw); struct wm831x *wm831x = clkdata->wm831x; return wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_1, WM831X_CLKOUT_SRC, parent << WM831X_CLKOUT_SRC_SHIFT); }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown52100.00%1100.00%
Total52100.00%1100.00%

static const struct clk_ops wm831x_clkout_ops = { .is_prepared = wm831x_clkout_is_prepared, .prepare = wm831x_clkout_prepare, .unprepare = wm831x_clkout_unprepare, .get_parent = wm831x_clkout_get_parent, .set_parent = wm831x_clkout_set_parent, }; static struct clk_init_data wm831x_clkout_init = { .name = "clkout", .ops = &wm831x_clkout_ops, .parent_names = wm831x_clkout_parents, .num_parents = ARRAY_SIZE(wm831x_clkout_parents), .flags = CLK_SET_RATE_PARENT, };
static int wm831x_clk_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); struct wm831x_clk *clkdata; int ret; clkdata = devm_kzalloc(&pdev->dev, sizeof(*clkdata), GFP_KERNEL); if (!clkdata) return -ENOMEM; clkdata->wm831x = wm831x; /* XTAL_ENA can only be set via OTP/InstantConfig so just read once */ ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2); if (ret < 0) { dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_2: %d\n", ret); return ret; } clkdata->xtal_ena = ret & WM831X_XTAL_ENA; clkdata->xtal_hw.init = &wm831x_xtal_init; clkdata->xtal = devm_clk_register(&pdev->dev, &clkdata->xtal_hw); if (IS_ERR(clkdata->xtal)) return PTR_ERR(clkdata->xtal); clkdata->fll_hw.init = &wm831x_fll_init; clkdata->fll = devm_clk_register(&pdev->dev, &clkdata->fll_hw); if (IS_ERR(clkdata->fll)) return PTR_ERR(clkdata->fll); clkdata->clkout_hw.init = &wm831x_clkout_init; clkdata->clkout = devm_clk_register(&pdev->dev, &clkdata->clkout_hw); if (IS_ERR(clkdata->clkout)) return PTR_ERR(clkdata->clkout); platform_set_drvdata(pdev, clkdata); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown21085.71%240.00%
stephen boydstephen boyd3413.88%240.00%
jingoo hanjingoo han10.41%120.00%
Total245100.00%5100.00%

static struct platform_driver wm831x_clk_driver = { .probe = wm831x_clk_probe, .driver = { .name = "wm831x-clk", }, }; module_platform_driver(wm831x_clk_driver); /* Module information */ MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); MODULE_DESCRIPTION("WM831x clock driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:wm831x-clk");

Overall Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown169297.58%337.50%
stephen boydstephen boyd341.96%225.00%
axel linaxel lin70.40%225.00%
jingoo hanjingoo han10.06%112.50%
Total1734100.00%8100.00%
Directory: drivers/clk
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}