18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com/
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
78c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
108c2ecf20Sopenharmony_ci#include <linux/regmap.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_cistruct ti_syscon_gate_clk_priv {
138c2ecf20Sopenharmony_ci	struct clk_hw hw;
148c2ecf20Sopenharmony_ci	struct regmap *regmap;
158c2ecf20Sopenharmony_ci	u32 reg;
168c2ecf20Sopenharmony_ci	u32 idx;
178c2ecf20Sopenharmony_ci};
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistruct ti_syscon_gate_clk_data {
208c2ecf20Sopenharmony_ci	char *name;
218c2ecf20Sopenharmony_ci	u32 offset;
228c2ecf20Sopenharmony_ci	u32 bit_idx;
238c2ecf20Sopenharmony_ci};
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic struct
268c2ecf20Sopenharmony_citi_syscon_gate_clk_priv *to_ti_syscon_gate_clk_priv(struct clk_hw *hw)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	return container_of(hw, struct ti_syscon_gate_clk_priv, hw);
298c2ecf20Sopenharmony_ci}
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic int ti_syscon_gate_clk_enable(struct clk_hw *hw)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	struct ti_syscon_gate_clk_priv *priv = to_ti_syscon_gate_clk_priv(hw);
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	return regmap_write_bits(priv->regmap, priv->reg, priv->idx,
368c2ecf20Sopenharmony_ci				 priv->idx);
378c2ecf20Sopenharmony_ci}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic void ti_syscon_gate_clk_disable(struct clk_hw *hw)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	struct ti_syscon_gate_clk_priv *priv = to_ti_syscon_gate_clk_priv(hw);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	regmap_write_bits(priv->regmap, priv->reg, priv->idx, 0);
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic int ti_syscon_gate_clk_is_enabled(struct clk_hw *hw)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	unsigned int val;
498c2ecf20Sopenharmony_ci	struct ti_syscon_gate_clk_priv *priv = to_ti_syscon_gate_clk_priv(hw);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	regmap_read(priv->regmap, priv->reg, &val);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	return !!(val & priv->idx);
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic const struct clk_ops ti_syscon_gate_clk_ops = {
578c2ecf20Sopenharmony_ci	.enable		= ti_syscon_gate_clk_enable,
588c2ecf20Sopenharmony_ci	.disable	= ti_syscon_gate_clk_disable,
598c2ecf20Sopenharmony_ci	.is_enabled	= ti_syscon_gate_clk_is_enabled,
608c2ecf20Sopenharmony_ci};
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic struct clk_hw
638c2ecf20Sopenharmony_ci*ti_syscon_gate_clk_register(struct device *dev, struct regmap *regmap,
648c2ecf20Sopenharmony_ci			     const struct ti_syscon_gate_clk_data *data)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	struct ti_syscon_gate_clk_priv *priv;
678c2ecf20Sopenharmony_ci	struct clk_init_data init;
688c2ecf20Sopenharmony_ci	int ret;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
718c2ecf20Sopenharmony_ci	if (!priv)
728c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	init.name = data->name;
758c2ecf20Sopenharmony_ci	init.ops = &ti_syscon_gate_clk_ops;
768c2ecf20Sopenharmony_ci	init.parent_names = NULL;
778c2ecf20Sopenharmony_ci	init.num_parents = 0;
788c2ecf20Sopenharmony_ci	init.flags = 0;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	priv->regmap = regmap;
818c2ecf20Sopenharmony_ci	priv->reg = data->offset;
828c2ecf20Sopenharmony_ci	priv->idx = BIT(data->bit_idx);
838c2ecf20Sopenharmony_ci	priv->hw.init = &init;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	ret = devm_clk_hw_register(dev, &priv->hw);
868c2ecf20Sopenharmony_ci	if (ret)
878c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	return &priv->hw;
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic int ti_syscon_gate_clk_probe(struct platform_device *pdev)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	const struct ti_syscon_gate_clk_data *data, *p;
958c2ecf20Sopenharmony_ci	struct clk_hw_onecell_data *hw_data;
968c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
978c2ecf20Sopenharmony_ci	struct regmap *regmap;
988c2ecf20Sopenharmony_ci	int num_clks, i;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	data = device_get_match_data(dev);
1018c2ecf20Sopenharmony_ci	if (!data)
1028c2ecf20Sopenharmony_ci		return -EINVAL;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	regmap = syscon_node_to_regmap(dev->of_node);
1058c2ecf20Sopenharmony_ci	if (IS_ERR(regmap)) {
1068c2ecf20Sopenharmony_ci		if (PTR_ERR(regmap) == -EPROBE_DEFER)
1078c2ecf20Sopenharmony_ci			return -EPROBE_DEFER;
1088c2ecf20Sopenharmony_ci		dev_err(dev, "failed to find parent regmap\n");
1098c2ecf20Sopenharmony_ci		return PTR_ERR(regmap);
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	num_clks = 0;
1138c2ecf20Sopenharmony_ci	for (p = data; p->name; p++)
1148c2ecf20Sopenharmony_ci		num_clks++;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	hw_data = devm_kzalloc(dev, struct_size(hw_data, hws, num_clks),
1178c2ecf20Sopenharmony_ci			       GFP_KERNEL);
1188c2ecf20Sopenharmony_ci	if (!hw_data)
1198c2ecf20Sopenharmony_ci		return -ENOMEM;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	hw_data->num = num_clks;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	for (i = 0; i < num_clks; i++) {
1248c2ecf20Sopenharmony_ci		hw_data->hws[i] = ti_syscon_gate_clk_register(dev, regmap,
1258c2ecf20Sopenharmony_ci							      &data[i]);
1268c2ecf20Sopenharmony_ci		if (IS_ERR(hw_data->hws[i]))
1278c2ecf20Sopenharmony_ci			dev_warn(dev, "failed to register %s\n",
1288c2ecf20Sopenharmony_ci				 data[i].name);
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
1328c2ecf20Sopenharmony_ci					   hw_data);
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci#define TI_SYSCON_CLK_GATE(_name, _offset, _bit_idx)	\
1368c2ecf20Sopenharmony_ci	{						\
1378c2ecf20Sopenharmony_ci		.name = _name,				\
1388c2ecf20Sopenharmony_ci		.offset = (_offset),			\
1398c2ecf20Sopenharmony_ci		.bit_idx = (_bit_idx),			\
1408c2ecf20Sopenharmony_ci	}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic const struct ti_syscon_gate_clk_data am654_clk_data[] = {
1438c2ecf20Sopenharmony_ci	TI_SYSCON_CLK_GATE("ehrpwm_tbclk0", 0x0, 0),
1448c2ecf20Sopenharmony_ci	TI_SYSCON_CLK_GATE("ehrpwm_tbclk1", 0x4, 0),
1458c2ecf20Sopenharmony_ci	TI_SYSCON_CLK_GATE("ehrpwm_tbclk2", 0x8, 0),
1468c2ecf20Sopenharmony_ci	TI_SYSCON_CLK_GATE("ehrpwm_tbclk3", 0xc, 0),
1478c2ecf20Sopenharmony_ci	TI_SYSCON_CLK_GATE("ehrpwm_tbclk4", 0x10, 0),
1488c2ecf20Sopenharmony_ci	TI_SYSCON_CLK_GATE("ehrpwm_tbclk5", 0x14, 0),
1498c2ecf20Sopenharmony_ci	{ /* Sentinel */ },
1508c2ecf20Sopenharmony_ci};
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic const struct of_device_id ti_syscon_gate_clk_ids[] = {
1538c2ecf20Sopenharmony_ci	{
1548c2ecf20Sopenharmony_ci		.compatible = "ti,am654-ehrpwm-tbclk",
1558c2ecf20Sopenharmony_ci		.data = &am654_clk_data,
1568c2ecf20Sopenharmony_ci	},
1578c2ecf20Sopenharmony_ci	{ }
1588c2ecf20Sopenharmony_ci};
1598c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ti_syscon_gate_clk_ids);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic struct platform_driver ti_syscon_gate_clk_driver = {
1628c2ecf20Sopenharmony_ci	.probe = ti_syscon_gate_clk_probe,
1638c2ecf20Sopenharmony_ci	.driver = {
1648c2ecf20Sopenharmony_ci		.name = "ti-syscon-gate-clk",
1658c2ecf20Sopenharmony_ci		.of_match_table = ti_syscon_gate_clk_ids,
1668c2ecf20Sopenharmony_ci	},
1678c2ecf20Sopenharmony_ci};
1688c2ecf20Sopenharmony_cimodule_platform_driver(ti_syscon_gate_clk_driver);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vignesh Raghavendra <vigneshr@ti.com>");
1718c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Syscon backed gate-clock driver");
1728c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
173