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