162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Clk driver for NXP LPC18xx/43xx Configuration Registers (CREG)
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/clk-provider.h>
962306a36Sopenharmony_ci#include <linux/delay.h>
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
1262306a36Sopenharmony_ci#include <linux/of.h>
1362306a36Sopenharmony_ci#include <linux/platform_device.h>
1462306a36Sopenharmony_ci#include <linux/regmap.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define LPC18XX_CREG_CREG0			0x004
1762306a36Sopenharmony_ci#define  LPC18XX_CREG_CREG0_EN1KHZ		BIT(0)
1862306a36Sopenharmony_ci#define  LPC18XX_CREG_CREG0_EN32KHZ		BIT(1)
1962306a36Sopenharmony_ci#define  LPC18XX_CREG_CREG0_RESET32KHZ		BIT(2)
2062306a36Sopenharmony_ci#define  LPC18XX_CREG_CREG0_PD32KHZ		BIT(3)
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define to_clk_creg(_hw) container_of(_hw, struct clk_creg_data, hw)
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cienum {
2562306a36Sopenharmony_ci	CREG_CLK_1KHZ,
2662306a36Sopenharmony_ci	CREG_CLK_32KHZ,
2762306a36Sopenharmony_ci	CREG_CLK_MAX,
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistruct clk_creg_data {
3162306a36Sopenharmony_ci	struct clk_hw hw;
3262306a36Sopenharmony_ci	const char *name;
3362306a36Sopenharmony_ci	struct regmap *reg;
3462306a36Sopenharmony_ci	unsigned int en_mask;
3562306a36Sopenharmony_ci	const struct clk_ops *ops;
3662306a36Sopenharmony_ci};
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define CREG_CLK(_name, _emask, _ops)		\
3962306a36Sopenharmony_ci{						\
4062306a36Sopenharmony_ci	.name = _name,				\
4162306a36Sopenharmony_ci	.en_mask = LPC18XX_CREG_CREG0_##_emask,	\
4262306a36Sopenharmony_ci	.ops = &_ops,				\
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic int clk_creg_32k_prepare(struct clk_hw *hw)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	struct clk_creg_data *creg = to_clk_creg(hw);
4862306a36Sopenharmony_ci	int ret;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	ret = regmap_update_bits(creg->reg, LPC18XX_CREG_CREG0,
5162306a36Sopenharmony_ci				 LPC18XX_CREG_CREG0_PD32KHZ |
5262306a36Sopenharmony_ci				 LPC18XX_CREG_CREG0_RESET32KHZ, 0);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	/*
5562306a36Sopenharmony_ci	 * Powering up the 32k oscillator takes a long while
5662306a36Sopenharmony_ci	 * and sadly there aren't any status bit to poll.
5762306a36Sopenharmony_ci	 */
5862306a36Sopenharmony_ci	msleep(2500);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	return ret;
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic void clk_creg_32k_unprepare(struct clk_hw *hw)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	struct clk_creg_data *creg = to_clk_creg(hw);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	regmap_update_bits(creg->reg, LPC18XX_CREG_CREG0,
6862306a36Sopenharmony_ci			   LPC18XX_CREG_CREG0_PD32KHZ,
6962306a36Sopenharmony_ci			   LPC18XX_CREG_CREG0_PD32KHZ);
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic int clk_creg_32k_is_prepared(struct clk_hw *hw)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	struct clk_creg_data *creg = to_clk_creg(hw);
7562306a36Sopenharmony_ci	u32 reg;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	regmap_read(creg->reg, LPC18XX_CREG_CREG0, &reg);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	return !(reg & LPC18XX_CREG_CREG0_PD32KHZ) &&
8062306a36Sopenharmony_ci	       !(reg & LPC18XX_CREG_CREG0_RESET32KHZ);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic unsigned long clk_creg_1k_recalc_rate(struct clk_hw *hw,
8462306a36Sopenharmony_ci					     unsigned long parent_rate)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	return parent_rate / 32;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic int clk_creg_enable(struct clk_hw *hw)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	struct clk_creg_data *creg = to_clk_creg(hw);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	return regmap_update_bits(creg->reg, LPC18XX_CREG_CREG0,
9462306a36Sopenharmony_ci				  creg->en_mask, creg->en_mask);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic void clk_creg_disable(struct clk_hw *hw)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	struct clk_creg_data *creg = to_clk_creg(hw);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	regmap_update_bits(creg->reg, LPC18XX_CREG_CREG0,
10262306a36Sopenharmony_ci			   creg->en_mask, 0);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic int clk_creg_is_enabled(struct clk_hw *hw)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	struct clk_creg_data *creg = to_clk_creg(hw);
10862306a36Sopenharmony_ci	u32 reg;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	regmap_read(creg->reg, LPC18XX_CREG_CREG0, &reg);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	return !!(reg & creg->en_mask);
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic const struct clk_ops clk_creg_32k = {
11662306a36Sopenharmony_ci	.enable		= clk_creg_enable,
11762306a36Sopenharmony_ci	.disable	= clk_creg_disable,
11862306a36Sopenharmony_ci	.is_enabled	= clk_creg_is_enabled,
11962306a36Sopenharmony_ci	.prepare	= clk_creg_32k_prepare,
12062306a36Sopenharmony_ci	.unprepare	= clk_creg_32k_unprepare,
12162306a36Sopenharmony_ci	.is_prepared	= clk_creg_32k_is_prepared,
12262306a36Sopenharmony_ci};
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic const struct clk_ops clk_creg_1k = {
12562306a36Sopenharmony_ci	.enable		= clk_creg_enable,
12662306a36Sopenharmony_ci	.disable	= clk_creg_disable,
12762306a36Sopenharmony_ci	.is_enabled	= clk_creg_is_enabled,
12862306a36Sopenharmony_ci	.recalc_rate	= clk_creg_1k_recalc_rate,
12962306a36Sopenharmony_ci};
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic struct clk_creg_data clk_creg_clocks[] = {
13262306a36Sopenharmony_ci	[CREG_CLK_1KHZ]  = CREG_CLK("1khz_clk",  EN1KHZ,  clk_creg_1k),
13362306a36Sopenharmony_ci	[CREG_CLK_32KHZ] = CREG_CLK("32khz_clk", EN32KHZ, clk_creg_32k),
13462306a36Sopenharmony_ci};
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic struct clk *clk_register_creg_clk(struct device *dev,
13762306a36Sopenharmony_ci					 struct clk_creg_data *creg_clk,
13862306a36Sopenharmony_ci					 const char **parent_name,
13962306a36Sopenharmony_ci					 struct regmap *syscon)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct clk_init_data init;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	init.ops = creg_clk->ops;
14462306a36Sopenharmony_ci	init.name = creg_clk->name;
14562306a36Sopenharmony_ci	init.parent_names = parent_name;
14662306a36Sopenharmony_ci	init.num_parents = 1;
14762306a36Sopenharmony_ci	init.flags = 0;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	creg_clk->reg = syscon;
15062306a36Sopenharmony_ci	creg_clk->hw.init = &init;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (dev)
15362306a36Sopenharmony_ci		return devm_clk_register(dev, &creg_clk->hw);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	return clk_register(NULL, &creg_clk->hw);
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic struct clk *clk_creg_early[CREG_CLK_MAX];
15962306a36Sopenharmony_cistatic struct clk_onecell_data clk_creg_early_data = {
16062306a36Sopenharmony_ci	.clks = clk_creg_early,
16162306a36Sopenharmony_ci	.clk_num = CREG_CLK_MAX,
16262306a36Sopenharmony_ci};
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic void __init lpc18xx_creg_clk_init(struct device_node *np)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	const char *clk_32khz_parent;
16762306a36Sopenharmony_ci	struct regmap *syscon;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	syscon = syscon_node_to_regmap(np->parent);
17062306a36Sopenharmony_ci	if (IS_ERR(syscon)) {
17162306a36Sopenharmony_ci		pr_err("%s: syscon lookup failed\n", __func__);
17262306a36Sopenharmony_ci		return;
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	clk_32khz_parent = of_clk_get_parent_name(np, 0);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	clk_creg_early[CREG_CLK_32KHZ] =
17862306a36Sopenharmony_ci		clk_register_creg_clk(NULL, &clk_creg_clocks[CREG_CLK_32KHZ],
17962306a36Sopenharmony_ci				      &clk_32khz_parent, syscon);
18062306a36Sopenharmony_ci	clk_creg_early[CREG_CLK_1KHZ] = ERR_PTR(-EPROBE_DEFER);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_creg_early_data);
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ciCLK_OF_DECLARE_DRIVER(lpc18xx_creg_clk, "nxp,lpc1850-creg-clk",
18562306a36Sopenharmony_ci		      lpc18xx_creg_clk_init);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic struct clk *clk_creg[CREG_CLK_MAX];
18862306a36Sopenharmony_cistatic struct clk_onecell_data clk_creg_data = {
18962306a36Sopenharmony_ci	.clks = clk_creg,
19062306a36Sopenharmony_ci	.clk_num = CREG_CLK_MAX,
19162306a36Sopenharmony_ci};
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic int lpc18xx_creg_clk_probe(struct platform_device *pdev)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
19662306a36Sopenharmony_ci	struct regmap *syscon;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	syscon = syscon_node_to_regmap(np->parent);
19962306a36Sopenharmony_ci	if (IS_ERR(syscon)) {
20062306a36Sopenharmony_ci		dev_err(&pdev->dev, "syscon lookup failed\n");
20162306a36Sopenharmony_ci		return PTR_ERR(syscon);
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	clk_creg[CREG_CLK_32KHZ] = clk_creg_early[CREG_CLK_32KHZ];
20562306a36Sopenharmony_ci	clk_creg[CREG_CLK_1KHZ] =
20662306a36Sopenharmony_ci		clk_register_creg_clk(NULL, &clk_creg_clocks[CREG_CLK_1KHZ],
20762306a36Sopenharmony_ci				      &clk_creg_clocks[CREG_CLK_32KHZ].name,
20862306a36Sopenharmony_ci				      syscon);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	return of_clk_add_provider(np, of_clk_src_onecell_get, &clk_creg_data);
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic const struct of_device_id lpc18xx_creg_clk_of_match[] = {
21462306a36Sopenharmony_ci	{ .compatible = "nxp,lpc1850-creg-clk" },
21562306a36Sopenharmony_ci	{},
21662306a36Sopenharmony_ci};
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic struct platform_driver lpc18xx_creg_clk_driver = {
21962306a36Sopenharmony_ci	.probe = lpc18xx_creg_clk_probe,
22062306a36Sopenharmony_ci	.driver = {
22162306a36Sopenharmony_ci		.name = "lpc18xx-creg-clk",
22262306a36Sopenharmony_ci		.of_match_table = lpc18xx_creg_clk_of_match,
22362306a36Sopenharmony_ci	},
22462306a36Sopenharmony_ci};
22562306a36Sopenharmony_cibuiltin_platform_driver(lpc18xx_creg_clk_driver);
226