1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com/ 4 */ 5 6 #include <linux/clk-provider.h> 7 #include <linux/mfd/syscon.h> 8 #include <linux/module.h> 9 #include <linux/platform_device.h> 10 #include <linux/regmap.h> 11 12 struct ti_syscon_gate_clk_priv { 13 struct clk_hw hw; 14 struct regmap *regmap; 15 u32 reg; 16 u32 idx; 17 }; 18 19 struct ti_syscon_gate_clk_data { 20 char *name; 21 u32 offset; 22 u32 bit_idx; 23 }; 24 25 static struct 26 ti_syscon_gate_clk_priv *to_ti_syscon_gate_clk_priv(struct clk_hw *hw) 27 { 28 return container_of(hw, struct ti_syscon_gate_clk_priv, hw); 29 } 30 31 static int ti_syscon_gate_clk_enable(struct clk_hw *hw) 32 { 33 struct ti_syscon_gate_clk_priv *priv = to_ti_syscon_gate_clk_priv(hw); 34 35 return regmap_write_bits(priv->regmap, priv->reg, priv->idx, 36 priv->idx); 37 } 38 39 static void ti_syscon_gate_clk_disable(struct clk_hw *hw) 40 { 41 struct ti_syscon_gate_clk_priv *priv = to_ti_syscon_gate_clk_priv(hw); 42 43 regmap_write_bits(priv->regmap, priv->reg, priv->idx, 0); 44 } 45 46 static int ti_syscon_gate_clk_is_enabled(struct clk_hw *hw) 47 { 48 unsigned int val; 49 struct ti_syscon_gate_clk_priv *priv = to_ti_syscon_gate_clk_priv(hw); 50 51 regmap_read(priv->regmap, priv->reg, &val); 52 53 return !!(val & priv->idx); 54 } 55 56 static const struct clk_ops ti_syscon_gate_clk_ops = { 57 .enable = ti_syscon_gate_clk_enable, 58 .disable = ti_syscon_gate_clk_disable, 59 .is_enabled = ti_syscon_gate_clk_is_enabled, 60 }; 61 62 static struct clk_hw 63 *ti_syscon_gate_clk_register(struct device *dev, struct regmap *regmap, 64 const struct ti_syscon_gate_clk_data *data) 65 { 66 struct ti_syscon_gate_clk_priv *priv; 67 struct clk_init_data init; 68 int ret; 69 70 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 71 if (!priv) 72 return ERR_PTR(-ENOMEM); 73 74 init.name = data->name; 75 init.ops = &ti_syscon_gate_clk_ops; 76 init.parent_names = NULL; 77 init.num_parents = 0; 78 init.flags = 0; 79 80 priv->regmap = regmap; 81 priv->reg = data->offset; 82 priv->idx = BIT(data->bit_idx); 83 priv->hw.init = &init; 84 85 ret = devm_clk_hw_register(dev, &priv->hw); 86 if (ret) 87 return ERR_PTR(ret); 88 89 return &priv->hw; 90 } 91 92 static int ti_syscon_gate_clk_probe(struct platform_device *pdev) 93 { 94 const struct ti_syscon_gate_clk_data *data, *p; 95 struct clk_hw_onecell_data *hw_data; 96 struct device *dev = &pdev->dev; 97 struct regmap *regmap; 98 int num_clks, i; 99 100 data = device_get_match_data(dev); 101 if (!data) 102 return -EINVAL; 103 104 regmap = syscon_node_to_regmap(dev->of_node); 105 if (IS_ERR(regmap)) 106 return dev_err_probe(dev, PTR_ERR(regmap), 107 "failed to find parent regmap\n"); 108 109 num_clks = 0; 110 for (p = data; p->name; p++) 111 num_clks++; 112 113 hw_data = devm_kzalloc(dev, struct_size(hw_data, hws, num_clks), 114 GFP_KERNEL); 115 if (!hw_data) 116 return -ENOMEM; 117 118 hw_data->num = num_clks; 119 120 for (i = 0; i < num_clks; i++) { 121 hw_data->hws[i] = ti_syscon_gate_clk_register(dev, regmap, 122 &data[i]); 123 if (IS_ERR(hw_data->hws[i])) 124 dev_warn(dev, "failed to register %s\n", 125 data[i].name); 126 } 127 128 return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, 129 hw_data); 130 } 131 132 #define TI_SYSCON_CLK_GATE(_name, _offset, _bit_idx) \ 133 { \ 134 .name = _name, \ 135 .offset = (_offset), \ 136 .bit_idx = (_bit_idx), \ 137 } 138 139 static const struct ti_syscon_gate_clk_data am654_clk_data[] = { 140 TI_SYSCON_CLK_GATE("ehrpwm_tbclk0", 0x0, 0), 141 TI_SYSCON_CLK_GATE("ehrpwm_tbclk1", 0x4, 0), 142 TI_SYSCON_CLK_GATE("ehrpwm_tbclk2", 0x8, 0), 143 TI_SYSCON_CLK_GATE("ehrpwm_tbclk3", 0xc, 0), 144 TI_SYSCON_CLK_GATE("ehrpwm_tbclk4", 0x10, 0), 145 TI_SYSCON_CLK_GATE("ehrpwm_tbclk5", 0x14, 0), 146 { /* Sentinel */ }, 147 }; 148 149 static const struct ti_syscon_gate_clk_data am64_clk_data[] = { 150 TI_SYSCON_CLK_GATE("epwm_tbclk0", 0x0, 0), 151 TI_SYSCON_CLK_GATE("epwm_tbclk1", 0x0, 1), 152 TI_SYSCON_CLK_GATE("epwm_tbclk2", 0x0, 2), 153 TI_SYSCON_CLK_GATE("epwm_tbclk3", 0x0, 3), 154 TI_SYSCON_CLK_GATE("epwm_tbclk4", 0x0, 4), 155 TI_SYSCON_CLK_GATE("epwm_tbclk5", 0x0, 5), 156 TI_SYSCON_CLK_GATE("epwm_tbclk6", 0x0, 6), 157 TI_SYSCON_CLK_GATE("epwm_tbclk7", 0x0, 7), 158 TI_SYSCON_CLK_GATE("epwm_tbclk8", 0x0, 8), 159 { /* Sentinel */ }, 160 }; 161 162 static const struct ti_syscon_gate_clk_data am62_clk_data[] = { 163 TI_SYSCON_CLK_GATE("epwm_tbclk0", 0x0, 0), 164 TI_SYSCON_CLK_GATE("epwm_tbclk1", 0x0, 1), 165 TI_SYSCON_CLK_GATE("epwm_tbclk2", 0x0, 2), 166 { /* Sentinel */ }, 167 }; 168 169 static const struct of_device_id ti_syscon_gate_clk_ids[] = { 170 { 171 .compatible = "ti,am654-ehrpwm-tbclk", 172 .data = &am654_clk_data, 173 }, 174 { 175 .compatible = "ti,am64-epwm-tbclk", 176 .data = &am64_clk_data, 177 }, 178 { 179 .compatible = "ti,am62-epwm-tbclk", 180 .data = &am62_clk_data, 181 }, 182 { } 183 }; 184 MODULE_DEVICE_TABLE(of, ti_syscon_gate_clk_ids); 185 186 static struct platform_driver ti_syscon_gate_clk_driver = { 187 .probe = ti_syscon_gate_clk_probe, 188 .driver = { 189 .name = "ti-syscon-gate-clk", 190 .of_match_table = ti_syscon_gate_clk_ids, 191 }, 192 }; 193 module_platform_driver(ti_syscon_gate_clk_driver); 194 195 MODULE_AUTHOR("Vignesh Raghavendra <vigneshr@ti.com>"); 196 MODULE_DESCRIPTION("Syscon backed gate-clock driver"); 197 MODULE_LICENSE("GPL"); 198