0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/clk.h>
0010 #include <linux/clk-provider.h>
0011 #include <linux/cpu.h>
0012 #include <linux/kernel.h>
0013 #include <linux/module.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/pm_domain.h>
0016 #include <linux/regmap.h>
0017 #include <linux/slab.h>
0018
0019 #include "clk-regmap.h"
0020 #include "clk-regmap-mux-div.h"
0021
0022 static const u32 apcs_mux_clk_parent_map[] = { 0, 1, 5 };
0023
0024 static const struct clk_parent_data pdata[] = {
0025 { .fw_name = "ref" },
0026 { .fw_name = "aux" },
0027 { .fw_name = "pll" },
0028 };
0029
0030
0031
0032
0033
0034 static int a7cc_notifier_cb(struct notifier_block *nb, unsigned long event,
0035 void *data)
0036 {
0037 int ret = 0;
0038 struct clk_regmap_mux_div *md = container_of(nb,
0039 struct clk_regmap_mux_div,
0040 clk_nb);
0041 if (event == PRE_RATE_CHANGE)
0042
0043 ret = mux_div_set_src_div(md, 1, 2);
0044
0045 return notifier_from_errno(ret);
0046 }
0047
0048 static int qcom_apcs_sdx55_clk_probe(struct platform_device *pdev)
0049 {
0050 struct device *dev = &pdev->dev;
0051 struct device *parent = dev->parent;
0052 struct device *cpu_dev;
0053 struct clk_regmap_mux_div *a7cc;
0054 struct regmap *regmap;
0055 struct clk_init_data init = { };
0056 int ret;
0057
0058 regmap = dev_get_regmap(parent, NULL);
0059 if (!regmap) {
0060 dev_err(dev, "Failed to get parent regmap\n");
0061 return -ENODEV;
0062 }
0063
0064 a7cc = devm_kzalloc(dev, sizeof(*a7cc), GFP_KERNEL);
0065 if (!a7cc)
0066 return -ENOMEM;
0067
0068 init.name = "a7mux";
0069 init.parent_data = pdata;
0070 init.num_parents = ARRAY_SIZE(pdata);
0071 init.ops = &clk_regmap_mux_div_ops;
0072
0073 a7cc->clkr.hw.init = &init;
0074 a7cc->clkr.regmap = regmap;
0075 a7cc->reg_offset = 0x8;
0076 a7cc->hid_width = 5;
0077 a7cc->hid_shift = 0;
0078 a7cc->src_width = 3;
0079 a7cc->src_shift = 8;
0080 a7cc->parent_map = apcs_mux_clk_parent_map;
0081
0082 a7cc->pclk = devm_clk_get(parent, "pll");
0083 if (IS_ERR(a7cc->pclk))
0084 return dev_err_probe(dev, PTR_ERR(a7cc->pclk),
0085 "Failed to get PLL clk\n");
0086
0087 a7cc->clk_nb.notifier_call = a7cc_notifier_cb;
0088 ret = clk_notifier_register(a7cc->pclk, &a7cc->clk_nb);
0089 if (ret)
0090 return dev_err_probe(dev, ret,
0091 "Failed to register clock notifier\n");
0092
0093 ret = devm_clk_register_regmap(dev, &a7cc->clkr);
0094 if (ret) {
0095 dev_err_probe(dev, ret, "Failed to register regmap clock\n");
0096 goto err;
0097 }
0098
0099 ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
0100 &a7cc->clkr.hw);
0101 if (ret) {
0102 dev_err_probe(dev, ret, "Failed to add clock provider\n");
0103 goto err;
0104 }
0105
0106 platform_set_drvdata(pdev, a7cc);
0107
0108
0109
0110
0111
0112
0113 cpu_dev = get_cpu_device(0);
0114 dev_pm_domain_attach(cpu_dev, true);
0115
0116 return 0;
0117
0118 err:
0119 clk_notifier_unregister(a7cc->pclk, &a7cc->clk_nb);
0120 return ret;
0121 }
0122
0123 static int qcom_apcs_sdx55_clk_remove(struct platform_device *pdev)
0124 {
0125 struct device *cpu_dev = get_cpu_device(0);
0126 struct clk_regmap_mux_div *a7cc = platform_get_drvdata(pdev);
0127
0128 clk_notifier_unregister(a7cc->pclk, &a7cc->clk_nb);
0129 dev_pm_domain_detach(cpu_dev, true);
0130
0131 return 0;
0132 }
0133
0134 static struct platform_driver qcom_apcs_sdx55_clk_driver = {
0135 .probe = qcom_apcs_sdx55_clk_probe,
0136 .remove = qcom_apcs_sdx55_clk_remove,
0137 .driver = {
0138 .name = "qcom-sdx55-acps-clk",
0139 },
0140 };
0141 module_platform_driver(qcom_apcs_sdx55_clk_driver);
0142
0143 MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
0144 MODULE_LICENSE("GPL v2");
0145 MODULE_DESCRIPTION("Qualcomm SDX55 APCS clock driver");