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