Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Clock driver for Palmas device.
0004  *
0005  * Copyright (c) 2013, NVIDIA Corporation.
0006  * Copyright (c) 2013-2014 Texas Instruments, Inc.
0007  *
0008  * Author:  Laxman Dewangan <ldewangan@nvidia.com>
0009  *      Peter Ujfalusi <peter.ujfalusi@ti.com>
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      * Clock can be disabled through external pin if it is externally
0078      * controlled.
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");