brintos

brintos / linux-shallow public Read only

0
0
Text · 5.5 KiB · 3d3982e Raw
226 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Clk driver for NXP LPC18xx/43xx Configuration Registers (CREG)4 *5 * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>6 */7 8#include <linux/clk-provider.h>9#include <linux/delay.h>10#include <linux/kernel.h>11#include <linux/mfd/syscon.h>12#include <linux/of.h>13#include <linux/platform_device.h>14#include <linux/regmap.h>15 16#define LPC18XX_CREG_CREG0			0x00417#define  LPC18XX_CREG_CREG0_EN1KHZ		BIT(0)18#define  LPC18XX_CREG_CREG0_EN32KHZ		BIT(1)19#define  LPC18XX_CREG_CREG0_RESET32KHZ		BIT(2)20#define  LPC18XX_CREG_CREG0_PD32KHZ		BIT(3)21 22#define to_clk_creg(_hw) container_of(_hw, struct clk_creg_data, hw)23 24enum {25	CREG_CLK_1KHZ,26	CREG_CLK_32KHZ,27	CREG_CLK_MAX,28};29 30struct clk_creg_data {31	struct clk_hw hw;32	const char *name;33	struct regmap *reg;34	unsigned int en_mask;35	const struct clk_ops *ops;36};37 38#define CREG_CLK(_name, _emask, _ops)		\39{						\40	.name = _name,				\41	.en_mask = LPC18XX_CREG_CREG0_##_emask,	\42	.ops = &_ops,				\43}44 45static int clk_creg_32k_prepare(struct clk_hw *hw)46{47	struct clk_creg_data *creg = to_clk_creg(hw);48	int ret;49 50	ret = regmap_update_bits(creg->reg, LPC18XX_CREG_CREG0,51				 LPC18XX_CREG_CREG0_PD32KHZ |52				 LPC18XX_CREG_CREG0_RESET32KHZ, 0);53 54	/*55	 * Powering up the 32k oscillator takes a long while56	 * and sadly there aren't any status bit to poll.57	 */58	msleep(2500);59 60	return ret;61}62 63static void clk_creg_32k_unprepare(struct clk_hw *hw)64{65	struct clk_creg_data *creg = to_clk_creg(hw);66 67	regmap_update_bits(creg->reg, LPC18XX_CREG_CREG0,68			   LPC18XX_CREG_CREG0_PD32KHZ,69			   LPC18XX_CREG_CREG0_PD32KHZ);70}71 72static int clk_creg_32k_is_prepared(struct clk_hw *hw)73{74	struct clk_creg_data *creg = to_clk_creg(hw);75	u32 reg;76 77	regmap_read(creg->reg, LPC18XX_CREG_CREG0, &reg);78 79	return !(reg & LPC18XX_CREG_CREG0_PD32KHZ) &&80	       !(reg & LPC18XX_CREG_CREG0_RESET32KHZ);81}82 83static unsigned long clk_creg_1k_recalc_rate(struct clk_hw *hw,84					     unsigned long parent_rate)85{86	return parent_rate / 32;87}88 89static int clk_creg_enable(struct clk_hw *hw)90{91	struct clk_creg_data *creg = to_clk_creg(hw);92 93	return regmap_update_bits(creg->reg, LPC18XX_CREG_CREG0,94				  creg->en_mask, creg->en_mask);95}96 97static void clk_creg_disable(struct clk_hw *hw)98{99	struct clk_creg_data *creg = to_clk_creg(hw);100 101	regmap_update_bits(creg->reg, LPC18XX_CREG_CREG0,102			   creg->en_mask, 0);103}104 105static int clk_creg_is_enabled(struct clk_hw *hw)106{107	struct clk_creg_data *creg = to_clk_creg(hw);108	u32 reg;109 110	regmap_read(creg->reg, LPC18XX_CREG_CREG0, &reg);111 112	return !!(reg & creg->en_mask);113}114 115static const struct clk_ops clk_creg_32k = {116	.enable		= clk_creg_enable,117	.disable	= clk_creg_disable,118	.is_enabled	= clk_creg_is_enabled,119	.prepare	= clk_creg_32k_prepare,120	.unprepare	= clk_creg_32k_unprepare,121	.is_prepared	= clk_creg_32k_is_prepared,122};123 124static const struct clk_ops clk_creg_1k = {125	.enable		= clk_creg_enable,126	.disable	= clk_creg_disable,127	.is_enabled	= clk_creg_is_enabled,128	.recalc_rate	= clk_creg_1k_recalc_rate,129};130 131static struct clk_creg_data clk_creg_clocks[] = {132	[CREG_CLK_1KHZ]  = CREG_CLK("1khz_clk",  EN1KHZ,  clk_creg_1k),133	[CREG_CLK_32KHZ] = CREG_CLK("32khz_clk", EN32KHZ, clk_creg_32k),134};135 136static struct clk *clk_register_creg_clk(struct device *dev,137					 struct clk_creg_data *creg_clk,138					 const char **parent_name,139					 struct regmap *syscon)140{141	struct clk_init_data init;142 143	init.ops = creg_clk->ops;144	init.name = creg_clk->name;145	init.parent_names = parent_name;146	init.num_parents = 1;147	init.flags = 0;148 149	creg_clk->reg = syscon;150	creg_clk->hw.init = &init;151 152	if (dev)153		return devm_clk_register(dev, &creg_clk->hw);154 155	return clk_register(NULL, &creg_clk->hw);156}157 158static struct clk *clk_creg_early[CREG_CLK_MAX];159static struct clk_onecell_data clk_creg_early_data = {160	.clks = clk_creg_early,161	.clk_num = CREG_CLK_MAX,162};163 164static void __init lpc18xx_creg_clk_init(struct device_node *np)165{166	const char *clk_32khz_parent;167	struct regmap *syscon;168 169	syscon = syscon_node_to_regmap(np->parent);170	if (IS_ERR(syscon)) {171		pr_err("%s: syscon lookup failed\n", __func__);172		return;173	}174 175	clk_32khz_parent = of_clk_get_parent_name(np, 0);176 177	clk_creg_early[CREG_CLK_32KHZ] =178		clk_register_creg_clk(NULL, &clk_creg_clocks[CREG_CLK_32KHZ],179				      &clk_32khz_parent, syscon);180	clk_creg_early[CREG_CLK_1KHZ] = ERR_PTR(-EPROBE_DEFER);181 182	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_creg_early_data);183}184CLK_OF_DECLARE_DRIVER(lpc18xx_creg_clk, "nxp,lpc1850-creg-clk",185		      lpc18xx_creg_clk_init);186 187static struct clk *clk_creg[CREG_CLK_MAX];188static struct clk_onecell_data clk_creg_data = {189	.clks = clk_creg,190	.clk_num = CREG_CLK_MAX,191};192 193static int lpc18xx_creg_clk_probe(struct platform_device *pdev)194{195	struct device_node *np = pdev->dev.of_node;196	struct regmap *syscon;197 198	syscon = syscon_node_to_regmap(np->parent);199	if (IS_ERR(syscon)) {200		dev_err(&pdev->dev, "syscon lookup failed\n");201		return PTR_ERR(syscon);202	}203 204	clk_creg[CREG_CLK_32KHZ] = clk_creg_early[CREG_CLK_32KHZ];205	clk_creg[CREG_CLK_1KHZ] =206		clk_register_creg_clk(NULL, &clk_creg_clocks[CREG_CLK_1KHZ],207				      &clk_creg_clocks[CREG_CLK_32KHZ].name,208				      syscon);209 210	return of_clk_add_provider(np, of_clk_src_onecell_get, &clk_creg_data);211}212 213static const struct of_device_id lpc18xx_creg_clk_of_match[] = {214	{ .compatible = "nxp,lpc1850-creg-clk" },215	{},216};217 218static struct platform_driver lpc18xx_creg_clk_driver = {219	.probe = lpc18xx_creg_clk_probe,220	.driver = {221		.name = "lpc18xx-creg-clk",222		.of_match_table = lpc18xx_creg_clk_of_match,223	},224};225builtin_platform_driver(lpc18xx_creg_clk_driver);226