1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Clock driver for Palmas device. 4 * 5 * Copyright (c) 2013, NVIDIA Corporation. 6 * Copyright (c) 2013-2014 Texas Instruments, Inc. 7 * 8 * Author: Laxman Dewangan <ldewangan@nvidia.com> 9 * Peter Ujfalusi <peter.ujfalusi@ti.com> 10 */ 11 12 #include <linux/clk.h> 13 #include <linux/clk-provider.h> 14 #include <linux/mfd/palmas.h> 15 #include <linux/module.h> 16 #include <linux/of.h> 17 #include <linux/platform_device.h> 18 #include <linux/slab.h> 19 20 #define PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE1 1 21 #define PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE2 2 22 #define PALMAS_CLOCK_DT_EXT_CONTROL_NSLEEP 3 23 24 struct palmas_clk32k_desc { 25 const char *clk_name; 26 unsigned int control_reg; 27 unsigned int enable_mask; 28 unsigned int sleep_mask; 29 unsigned int sleep_reqstr_id; 30 int delay; 31 }; 32 33 struct palmas_clock_info { 34 struct device *dev; 35 struct clk_hw hw; 36 struct palmas *palmas; 37 const struct palmas_clk32k_desc *clk_desc; 38 int ext_control_pin; 39 }; 40 41 static inline struct palmas_clock_info *to_palmas_clks_info(struct clk_hw *hw) 42 { 43 return container_of(hw, struct palmas_clock_info, hw); 44 } 45 46 static unsigned long palmas_clks_recalc_rate(struct clk_hw *hw, 47 unsigned long parent_rate) 48 { 49 return 32768; 50 } 51 52 static int palmas_clks_prepare(struct clk_hw *hw) 53 { 54 struct palmas_clock_info *cinfo = to_palmas_clks_info(hw); 55 int ret; 56 57 ret = palmas_update_bits(cinfo->palmas, PALMAS_RESOURCE_BASE, 58 cinfo->clk_desc->control_reg, 59 cinfo->clk_desc->enable_mask, 60 cinfo->clk_desc->enable_mask); 61 if (ret < 0) 62 dev_err(cinfo->dev, "Reg 0x%02x update failed, %d\n", 63 cinfo->clk_desc->control_reg, ret); 64 else if (cinfo->clk_desc->delay) 65 udelay(cinfo->clk_desc->delay); 66 67 return ret; 68 } 69 70 static void palmas_clks_unprepare(struct clk_hw *hw) 71 { 72 struct palmas_clock_info *cinfo = to_palmas_clks_info(hw); 73 int ret; 74 75 /* 76 * Clock can be disabled through external pin if it is externally 77 * controlled. 78 */ 79 if (cinfo->ext_control_pin) 80 return; 81 82 ret = palmas_update_bits(cinfo->palmas, PALMAS_RESOURCE_BASE, 83 cinfo->clk_desc->control_reg, 84 cinfo->clk_desc->enable_mask, 0); 85 if (ret < 0) 86 dev_err(cinfo->dev, "Reg 0x%02x update failed, %d\n", 87 cinfo->clk_desc->control_reg, ret); 88 } 89 90 static int palmas_clks_is_prepared(struct clk_hw *hw) 91 { 92 struct palmas_clock_info *cinfo = to_palmas_clks_info(hw); 93 int ret; 94 u32 val; 95 96 if (cinfo->ext_control_pin) 97 return 1; 98 99 ret = palmas_read(cinfo->palmas, PALMAS_RESOURCE_BASE, 100 cinfo->clk_desc->control_reg, &val); 101 if (ret < 0) { 102 dev_err(cinfo->dev, "Reg 0x%02x read failed, %d\n", 103 cinfo->clk_desc->control_reg, ret); 104 return ret; 105 } 106 return !!(val & cinfo->clk_desc->enable_mask); 107 } 108 109 static const struct clk_ops palmas_clks_ops = { 110 .prepare = palmas_clks_prepare, 111 .unprepare = palmas_clks_unprepare, 112 .is_prepared = palmas_clks_is_prepared, 113 .recalc_rate = palmas_clks_recalc_rate, 114 }; 115 116 struct palmas_clks_of_match_data { 117 struct clk_init_data init; 118 const struct palmas_clk32k_desc desc; 119 }; 120 121 static const struct palmas_clks_of_match_data palmas_of_clk32kg = { 122 .init = { 123 .name = "clk32kg", 124 .ops = &palmas_clks_ops, 125 .flags = CLK_IGNORE_UNUSED, 126 }, 127 .desc = { 128 .clk_name = "clk32kg", 129 .control_reg = PALMAS_CLK32KG_CTRL, 130 .enable_mask = PALMAS_CLK32KG_CTRL_MODE_ACTIVE, 131 .sleep_mask = PALMAS_CLK32KG_CTRL_MODE_SLEEP, 132 .sleep_reqstr_id = PALMAS_EXTERNAL_REQSTR_ID_CLK32KG, 133 .delay = 200, 134 }, 135 }; 136 137 static const struct palmas_clks_of_match_data palmas_of_clk32kgaudio = { 138 .init = { 139 .name = "clk32kgaudio", 140 .ops = &palmas_clks_ops, 141 .flags = CLK_IGNORE_UNUSED, 142 }, 143 .desc = { 144 .clk_name = "clk32kgaudio", 145 .control_reg = PALMAS_CLK32KGAUDIO_CTRL, 146 .enable_mask = PALMAS_CLK32KG_CTRL_MODE_ACTIVE, 147 .sleep_mask = PALMAS_CLK32KG_CTRL_MODE_SLEEP, 148 .sleep_reqstr_id = PALMAS_EXTERNAL_REQSTR_ID_CLK32KGAUDIO, 149 .delay = 200, 150 }, 151 }; 152 153 static const struct of_device_id palmas_clks_of_match[] = { 154 { 155 .compatible = "ti,palmas-clk32kg", 156 .data = &palmas_of_clk32kg, 157 }, 158 { 159 .compatible = "ti,palmas-clk32kgaudio", 160 .data = &palmas_of_clk32kgaudio, 161 }, 162 { }, 163 }; 164 MODULE_DEVICE_TABLE(of, palmas_clks_of_match); 165 166 static void palmas_clks_get_clk_data(struct platform_device *pdev, 167 struct palmas_clock_info *cinfo) 168 { 169 struct device_node *node = pdev->dev.of_node; 170 unsigned int prop; 171 int ret; 172 173 ret = of_property_read_u32(node, "ti,external-sleep-control", 174 &prop); 175 if (ret) 176 return; 177 178 switch (prop) { 179 case PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE1: 180 prop = PALMAS_EXT_CONTROL_ENABLE1; 181 break; 182 case PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE2: 183 prop = PALMAS_EXT_CONTROL_ENABLE2; 184 break; 185 case PALMAS_CLOCK_DT_EXT_CONTROL_NSLEEP: 186 prop = PALMAS_EXT_CONTROL_NSLEEP; 187 break; 188 default: 189 dev_warn(&pdev->dev, "%pOFn: Invalid ext control option: %u\n", 190 node, prop); 191 prop = 0; 192 break; 193 } 194 cinfo->ext_control_pin = prop; 195 } 196 197 static int palmas_clks_init_configure(struct palmas_clock_info *cinfo) 198 { 199 int ret; 200 201 ret = palmas_update_bits(cinfo->palmas, PALMAS_RESOURCE_BASE, 202 cinfo->clk_desc->control_reg, 203 cinfo->clk_desc->sleep_mask, 0); 204 if (ret < 0) { 205 dev_err(cinfo->dev, "Reg 0x%02x update failed, %d\n", 206 cinfo->clk_desc->control_reg, ret); 207 return ret; 208 } 209 210 if (cinfo->ext_control_pin) { 211 ret = clk_prepare(cinfo->hw.clk); 212 if (ret < 0) { 213 dev_err(cinfo->dev, "Clock prep failed, %d\n", ret); 214 return ret; 215 } 216 217 ret = palmas_ext_control_req_config(cinfo->palmas, 218 cinfo->clk_desc->sleep_reqstr_id, 219 cinfo->ext_control_pin, true); 220 if (ret < 0) { 221 dev_err(cinfo->dev, "Ext config for %s failed, %d\n", 222 cinfo->clk_desc->clk_name, ret); 223 clk_unprepare(cinfo->hw.clk); 224 return ret; 225 } 226 } 227 228 return ret; 229 } 230 static int palmas_clks_probe(struct platform_device *pdev) 231 { 232 struct palmas *palmas = dev_get_drvdata(pdev->dev.parent); 233 struct device_node *node = pdev->dev.of_node; 234 const struct palmas_clks_of_match_data *match_data; 235 struct palmas_clock_info *cinfo; 236 int ret; 237 238 match_data = of_device_get_match_data(&pdev->dev); 239 if (!match_data) 240 return 1; 241 242 cinfo = devm_kzalloc(&pdev->dev, sizeof(*cinfo), GFP_KERNEL); 243 if (!cinfo) 244 return -ENOMEM; 245 246 palmas_clks_get_clk_data(pdev, cinfo); 247 platform_set_drvdata(pdev, cinfo); 248 249 cinfo->dev = &pdev->dev; 250 cinfo->palmas = palmas; 251 252 cinfo->clk_desc = &match_data->desc; 253 cinfo->hw.init = &match_data->init; 254 ret = devm_clk_hw_register(&pdev->dev, &cinfo->hw); 255 if (ret) { 256 dev_err(&pdev->dev, "Fail to register clock %s, %d\n", 257 match_data->desc.clk_name, ret); 258 return ret; 259 } 260 261 ret = palmas_clks_init_configure(cinfo); 262 if (ret < 0) { 263 dev_err(&pdev->dev, "Clock config failed, %d\n", ret); 264 return ret; 265 } 266 267 ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, &cinfo->hw); 268 if (ret < 0) 269 dev_err(&pdev->dev, "Fail to add clock driver, %d\n", ret); 270 return ret; 271 } 272 273 static void palmas_clks_remove(struct platform_device *pdev) 274 { 275 of_clk_del_provider(pdev->dev.of_node); 276 } 277 278 static struct platform_driver palmas_clks_driver = { 279 .driver = { 280 .name = "palmas-clk", 281 .of_match_table = palmas_clks_of_match, 282 }, 283 .probe = palmas_clks_probe, 284 .remove = palmas_clks_remove, 285 }; 286 287 module_platform_driver(palmas_clks_driver); 288 289 MODULE_DESCRIPTION("Clock driver for Palmas Series Devices"); 290 MODULE_ALIAS("platform:palmas-clk"); 291 MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>"); 292 MODULE_LICENSE("GPL v2"); 293