brintos

brintos / linux-shallow public Read only

0
0
Text · 9.3 KiB · 34e9d4d Raw
400 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * WM831x clock control4 *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	else42		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	else314		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	.determine_rate = clk_hw_determine_rate_no_reparent,333	.get_parent = wm831x_clkout_get_parent,334	.set_parent = wm831x_clkout_set_parent,335};336 337static const struct clk_init_data wm831x_clkout_init = {338	.name = "clkout",339	.ops = &wm831x_clkout_ops,340	.parent_names = wm831x_clkout_parents,341	.num_parents = ARRAY_SIZE(wm831x_clkout_parents),342	.flags = CLK_SET_RATE_PARENT,343};344 345static int wm831x_clk_probe(struct platform_device *pdev)346{347	struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);348	struct wm831x_clk *clkdata;349	int ret;350 351	clkdata = devm_kzalloc(&pdev->dev, sizeof(*clkdata), GFP_KERNEL);352	if (!clkdata)353		return -ENOMEM;354 355	clkdata->wm831x = wm831x;356 357	/* XTAL_ENA can only be set via OTP/InstantConfig so just read once */358	ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2);359	if (ret < 0) {360		dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_2: %d\n",361			ret);362		return ret;363	}364	clkdata->xtal_ena = ret & WM831X_XTAL_ENA;365 366	clkdata->xtal_hw.init = &wm831x_xtal_init;367	ret = devm_clk_hw_register(&pdev->dev, &clkdata->xtal_hw);368	if (ret)369		return ret;370 371	clkdata->fll_hw.init = &wm831x_fll_init;372	ret = devm_clk_hw_register(&pdev->dev, &clkdata->fll_hw);373	if (ret)374		return ret;375 376	clkdata->clkout_hw.init = &wm831x_clkout_init;377	ret = devm_clk_hw_register(&pdev->dev, &clkdata->clkout_hw);378	if (ret)379		return ret;380 381	platform_set_drvdata(pdev, clkdata);382 383	return 0;384}385 386static struct platform_driver wm831x_clk_driver = {387	.probe = wm831x_clk_probe,388	.driver		= {389		.name	= "wm831x-clk",390	},391};392 393module_platform_driver(wm831x_clk_driver);394 395/* Module information */396MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");397MODULE_DESCRIPTION("WM831x clock driver");398MODULE_LICENSE("GPL");399MODULE_ALIAS("platform:wm831x-clk");400