cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

clk-wm831x.c (9503B)


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