0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #include <linux/clk.h>
0013 #include <linux/clk-provider.h>
0014 #include <linux/mfd/palmas.h>
0015 #include <linux/module.h>
0016 #include <linux/of.h>
0017 #include <linux/of_device.h>
0018 #include <linux/platform_device.h>
0019 #include <linux/slab.h>
0020
0021 #define PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE1 1
0022 #define PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE2 2
0023 #define PALMAS_CLOCK_DT_EXT_CONTROL_NSLEEP 3
0024
0025 struct palmas_clk32k_desc {
0026 const char *clk_name;
0027 unsigned int control_reg;
0028 unsigned int enable_mask;
0029 unsigned int sleep_mask;
0030 unsigned int sleep_reqstr_id;
0031 int delay;
0032 };
0033
0034 struct palmas_clock_info {
0035 struct device *dev;
0036 struct clk_hw hw;
0037 struct palmas *palmas;
0038 const struct palmas_clk32k_desc *clk_desc;
0039 int ext_control_pin;
0040 };
0041
0042 static inline struct palmas_clock_info *to_palmas_clks_info(struct clk_hw *hw)
0043 {
0044 return container_of(hw, struct palmas_clock_info, hw);
0045 }
0046
0047 static unsigned long palmas_clks_recalc_rate(struct clk_hw *hw,
0048 unsigned long parent_rate)
0049 {
0050 return 32768;
0051 }
0052
0053 static int palmas_clks_prepare(struct clk_hw *hw)
0054 {
0055 struct palmas_clock_info *cinfo = to_palmas_clks_info(hw);
0056 int ret;
0057
0058 ret = palmas_update_bits(cinfo->palmas, PALMAS_RESOURCE_BASE,
0059 cinfo->clk_desc->control_reg,
0060 cinfo->clk_desc->enable_mask,
0061 cinfo->clk_desc->enable_mask);
0062 if (ret < 0)
0063 dev_err(cinfo->dev, "Reg 0x%02x update failed, %d\n",
0064 cinfo->clk_desc->control_reg, ret);
0065 else if (cinfo->clk_desc->delay)
0066 udelay(cinfo->clk_desc->delay);
0067
0068 return ret;
0069 }
0070
0071 static void palmas_clks_unprepare(struct clk_hw *hw)
0072 {
0073 struct palmas_clock_info *cinfo = to_palmas_clks_info(hw);
0074 int ret;
0075
0076
0077
0078
0079
0080 if (cinfo->ext_control_pin)
0081 return;
0082
0083 ret = palmas_update_bits(cinfo->palmas, PALMAS_RESOURCE_BASE,
0084 cinfo->clk_desc->control_reg,
0085 cinfo->clk_desc->enable_mask, 0);
0086 if (ret < 0)
0087 dev_err(cinfo->dev, "Reg 0x%02x update failed, %d\n",
0088 cinfo->clk_desc->control_reg, ret);
0089 }
0090
0091 static int palmas_clks_is_prepared(struct clk_hw *hw)
0092 {
0093 struct palmas_clock_info *cinfo = to_palmas_clks_info(hw);
0094 int ret;
0095 u32 val;
0096
0097 if (cinfo->ext_control_pin)
0098 return 1;
0099
0100 ret = palmas_read(cinfo->palmas, PALMAS_RESOURCE_BASE,
0101 cinfo->clk_desc->control_reg, &val);
0102 if (ret < 0) {
0103 dev_err(cinfo->dev, "Reg 0x%02x read failed, %d\n",
0104 cinfo->clk_desc->control_reg, ret);
0105 return ret;
0106 }
0107 return !!(val & cinfo->clk_desc->enable_mask);
0108 }
0109
0110 static const struct clk_ops palmas_clks_ops = {
0111 .prepare = palmas_clks_prepare,
0112 .unprepare = palmas_clks_unprepare,
0113 .is_prepared = palmas_clks_is_prepared,
0114 .recalc_rate = palmas_clks_recalc_rate,
0115 };
0116
0117 struct palmas_clks_of_match_data {
0118 struct clk_init_data init;
0119 const struct palmas_clk32k_desc desc;
0120 };
0121
0122 static const struct palmas_clks_of_match_data palmas_of_clk32kg = {
0123 .init = {
0124 .name = "clk32kg",
0125 .ops = &palmas_clks_ops,
0126 .flags = CLK_IGNORE_UNUSED,
0127 },
0128 .desc = {
0129 .clk_name = "clk32kg",
0130 .control_reg = PALMAS_CLK32KG_CTRL,
0131 .enable_mask = PALMAS_CLK32KG_CTRL_MODE_ACTIVE,
0132 .sleep_mask = PALMAS_CLK32KG_CTRL_MODE_SLEEP,
0133 .sleep_reqstr_id = PALMAS_EXTERNAL_REQSTR_ID_CLK32KG,
0134 .delay = 200,
0135 },
0136 };
0137
0138 static const struct palmas_clks_of_match_data palmas_of_clk32kgaudio = {
0139 .init = {
0140 .name = "clk32kgaudio",
0141 .ops = &palmas_clks_ops,
0142 .flags = CLK_IGNORE_UNUSED,
0143 },
0144 .desc = {
0145 .clk_name = "clk32kgaudio",
0146 .control_reg = PALMAS_CLK32KGAUDIO_CTRL,
0147 .enable_mask = PALMAS_CLK32KG_CTRL_MODE_ACTIVE,
0148 .sleep_mask = PALMAS_CLK32KG_CTRL_MODE_SLEEP,
0149 .sleep_reqstr_id = PALMAS_EXTERNAL_REQSTR_ID_CLK32KGAUDIO,
0150 .delay = 200,
0151 },
0152 };
0153
0154 static const struct of_device_id palmas_clks_of_match[] = {
0155 {
0156 .compatible = "ti,palmas-clk32kg",
0157 .data = &palmas_of_clk32kg,
0158 },
0159 {
0160 .compatible = "ti,palmas-clk32kgaudio",
0161 .data = &palmas_of_clk32kgaudio,
0162 },
0163 { },
0164 };
0165 MODULE_DEVICE_TABLE(of, palmas_clks_of_match);
0166
0167 static void palmas_clks_get_clk_data(struct platform_device *pdev,
0168 struct palmas_clock_info *cinfo)
0169 {
0170 struct device_node *node = pdev->dev.of_node;
0171 unsigned int prop;
0172 int ret;
0173
0174 ret = of_property_read_u32(node, "ti,external-sleep-control",
0175 &prop);
0176 if (ret)
0177 return;
0178
0179 switch (prop) {
0180 case PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE1:
0181 prop = PALMAS_EXT_CONTROL_ENABLE1;
0182 break;
0183 case PALMAS_CLOCK_DT_EXT_CONTROL_ENABLE2:
0184 prop = PALMAS_EXT_CONTROL_ENABLE2;
0185 break;
0186 case PALMAS_CLOCK_DT_EXT_CONTROL_NSLEEP:
0187 prop = PALMAS_EXT_CONTROL_NSLEEP;
0188 break;
0189 default:
0190 dev_warn(&pdev->dev, "%pOFn: Invalid ext control option: %u\n",
0191 node, prop);
0192 prop = 0;
0193 break;
0194 }
0195 cinfo->ext_control_pin = prop;
0196 }
0197
0198 static int palmas_clks_init_configure(struct palmas_clock_info *cinfo)
0199 {
0200 int ret;
0201
0202 ret = palmas_update_bits(cinfo->palmas, PALMAS_RESOURCE_BASE,
0203 cinfo->clk_desc->control_reg,
0204 cinfo->clk_desc->sleep_mask, 0);
0205 if (ret < 0) {
0206 dev_err(cinfo->dev, "Reg 0x%02x update failed, %d\n",
0207 cinfo->clk_desc->control_reg, ret);
0208 return ret;
0209 }
0210
0211 if (cinfo->ext_control_pin) {
0212 ret = clk_prepare(cinfo->hw.clk);
0213 if (ret < 0) {
0214 dev_err(cinfo->dev, "Clock prep failed, %d\n", ret);
0215 return ret;
0216 }
0217
0218 ret = palmas_ext_control_req_config(cinfo->palmas,
0219 cinfo->clk_desc->sleep_reqstr_id,
0220 cinfo->ext_control_pin, true);
0221 if (ret < 0) {
0222 dev_err(cinfo->dev, "Ext config for %s failed, %d\n",
0223 cinfo->clk_desc->clk_name, ret);
0224 clk_unprepare(cinfo->hw.clk);
0225 return ret;
0226 }
0227 }
0228
0229 return ret;
0230 }
0231 static int palmas_clks_probe(struct platform_device *pdev)
0232 {
0233 struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
0234 struct device_node *node = pdev->dev.of_node;
0235 const struct palmas_clks_of_match_data *match_data;
0236 struct palmas_clock_info *cinfo;
0237 int ret;
0238
0239 match_data = of_device_get_match_data(&pdev->dev);
0240 if (!match_data)
0241 return 1;
0242
0243 cinfo = devm_kzalloc(&pdev->dev, sizeof(*cinfo), GFP_KERNEL);
0244 if (!cinfo)
0245 return -ENOMEM;
0246
0247 palmas_clks_get_clk_data(pdev, cinfo);
0248 platform_set_drvdata(pdev, cinfo);
0249
0250 cinfo->dev = &pdev->dev;
0251 cinfo->palmas = palmas;
0252
0253 cinfo->clk_desc = &match_data->desc;
0254 cinfo->hw.init = &match_data->init;
0255 ret = devm_clk_hw_register(&pdev->dev, &cinfo->hw);
0256 if (ret) {
0257 dev_err(&pdev->dev, "Fail to register clock %s, %d\n",
0258 match_data->desc.clk_name, ret);
0259 return ret;
0260 }
0261
0262 ret = palmas_clks_init_configure(cinfo);
0263 if (ret < 0) {
0264 dev_err(&pdev->dev, "Clock config failed, %d\n", ret);
0265 return ret;
0266 }
0267
0268 ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, &cinfo->hw);
0269 if (ret < 0)
0270 dev_err(&pdev->dev, "Fail to add clock driver, %d\n", ret);
0271 return ret;
0272 }
0273
0274 static int palmas_clks_remove(struct platform_device *pdev)
0275 {
0276 of_clk_del_provider(pdev->dev.of_node);
0277 return 0;
0278 }
0279
0280 static struct platform_driver palmas_clks_driver = {
0281 .driver = {
0282 .name = "palmas-clk",
0283 .of_match_table = palmas_clks_of_match,
0284 },
0285 .probe = palmas_clks_probe,
0286 .remove = palmas_clks_remove,
0287 };
0288
0289 module_platform_driver(palmas_clks_driver);
0290
0291 MODULE_DESCRIPTION("Clock driver for Palmas Series Devices");
0292 MODULE_ALIAS("platform:palmas-clk");
0293 MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
0294 MODULE_LICENSE("GPL v2");