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