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 if (PTR_ERR(regmap) == -EPROBE_DEFER) 107 return -EPROBE_DEFER; 108 dev_err(dev, "failed to find parent regmap\n"); 109 return PTR_ERR(regmap); 110 } 111 112 num_clks = 0; 113 for (p = data; p->name; p++) 114 num_clks++; 115 116 hw_data = devm_kzalloc(dev, struct_size(hw_data, hws, num_clks), 117 GFP_KERNEL); 118 if (!hw_data) 119 return -ENOMEM; 120 121 hw_data->num = num_clks; 122 123 for (i = 0; i < num_clks; i++) { 124 hw_data->hws[i] = ti_syscon_gate_clk_register(dev, regmap, 125 &data[i]); 126 if (IS_ERR(hw_data->hws[i])) 127 dev_warn(dev, "failed to register %s\n", 128 data[i].name); 129 } 130 131 return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, 132 hw_data); 133 } 134 135 #define TI_SYSCON_CLK_GATE(_name, _offset, _bit_idx) \ 136 { \ 137 .name = _name, \ 138 .offset = (_offset), \ 139 .bit_idx = (_bit_idx), \ 140 } 141 142 static const struct ti_syscon_gate_clk_data am654_clk_data[] = { 143 TI_SYSCON_CLK_GATE("ehrpwm_tbclk0", 0x0, 0), 144 TI_SYSCON_CLK_GATE("ehrpwm_tbclk1", 0x4, 0), 145 TI_SYSCON_CLK_GATE("ehrpwm_tbclk2", 0x8, 0), 146 TI_SYSCON_CLK_GATE("ehrpwm_tbclk3", 0xc, 0), 147 TI_SYSCON_CLK_GATE("ehrpwm_tbclk4", 0x10, 0), 148 TI_SYSCON_CLK_GATE("ehrpwm_tbclk5", 0x14, 0), 149 { /* Sentinel */ }, 150 }; 151 152 static const struct ti_syscon_gate_clk_data am64_clk_data[] = { 153 TI_SYSCON_CLK_GATE("epwm_tbclk0", 0x0, 0), 154 TI_SYSCON_CLK_GATE("epwm_tbclk1", 0x0, 1), 155 TI_SYSCON_CLK_GATE("epwm_tbclk2", 0x0, 2), 156 TI_SYSCON_CLK_GATE("epwm_tbclk3", 0x0, 3), 157 TI_SYSCON_CLK_GATE("epwm_tbclk4", 0x0, 4), 158 TI_SYSCON_CLK_GATE("epwm_tbclk5", 0x0, 5), 159 TI_SYSCON_CLK_GATE("epwm_tbclk6", 0x0, 6), 160 TI_SYSCON_CLK_GATE("epwm_tbclk7", 0x0, 7), 161 TI_SYSCON_CLK_GATE("epwm_tbclk8", 0x0, 8), 162 { /* Sentinel */ }, 163 }; 164 165 static const struct of_device_id ti_syscon_gate_clk_ids[] = { 166 { 167 .compatible = "ti,am654-ehrpwm-tbclk", 168 .data = &am654_clk_data, 169 }, 170 { 171 .compatible = "ti,am64-epwm-tbclk", 172 .data = &am64_clk_data, 173 }, 174 { } 175 }; 176 MODULE_DEVICE_TABLE(of, ti_syscon_gate_clk_ids); 177 178 static struct platform_driver ti_syscon_gate_clk_driver = { 179 .probe = ti_syscon_gate_clk_probe, 180 .driver = { 181 .name = "ti-syscon-gate-clk", 182 .of_match_table = ti_syscon_gate_clk_ids, 183 }, 184 }; 185 module_platform_driver(ti_syscon_gate_clk_driver); 186 187 MODULE_AUTHOR("Vignesh Raghavendra <vigneshr@ti.com>"); 188 MODULE_DESCRIPTION("Syscon backed gate-clock driver"); 189 MODULE_LICENSE("GPL"); 190