18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// Copyright (c) 2018, The Linux Foundation. All rights reserved. 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/kernel.h> 58c2ecf20Sopenharmony_ci#include <linux/init.h> 68c2ecf20Sopenharmony_ci#include <linux/module.h> 78c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 88c2ecf20Sopenharmony_ci#include <linux/of.h> 98c2ecf20Sopenharmony_ci#include <linux/clk.h> 108c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 118c2ecf20Sopenharmony_ci#include <linux/regmap.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "clk-regmap.h" 148c2ecf20Sopenharmony_ci#include "clk-hfpll.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic const struct hfpll_data hdata = { 178c2ecf20Sopenharmony_ci .mode_reg = 0x00, 188c2ecf20Sopenharmony_ci .l_reg = 0x04, 198c2ecf20Sopenharmony_ci .m_reg = 0x08, 208c2ecf20Sopenharmony_ci .n_reg = 0x0c, 218c2ecf20Sopenharmony_ci .user_reg = 0x10, 228c2ecf20Sopenharmony_ci .config_reg = 0x14, 238c2ecf20Sopenharmony_ci .config_val = 0x430405d, 248c2ecf20Sopenharmony_ci .status_reg = 0x1c, 258c2ecf20Sopenharmony_ci .lock_bit = 16, 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci .user_val = 0x8, 288c2ecf20Sopenharmony_ci .user_vco_mask = 0x100000, 298c2ecf20Sopenharmony_ci .low_vco_max_rate = 1248000000, 308c2ecf20Sopenharmony_ci .min_rate = 537600000UL, 318c2ecf20Sopenharmony_ci .max_rate = 2900000000UL, 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic const struct of_device_id qcom_hfpll_match_table[] = { 358c2ecf20Sopenharmony_ci { .compatible = "qcom,hfpll" }, 368c2ecf20Sopenharmony_ci { } 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, qcom_hfpll_match_table); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic const struct regmap_config hfpll_regmap_config = { 418c2ecf20Sopenharmony_ci .reg_bits = 32, 428c2ecf20Sopenharmony_ci .reg_stride = 4, 438c2ecf20Sopenharmony_ci .val_bits = 32, 448c2ecf20Sopenharmony_ci .max_register = 0x30, 458c2ecf20Sopenharmony_ci .fast_io = true, 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int qcom_hfpll_probe(struct platform_device *pdev) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct resource *res; 518c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 528c2ecf20Sopenharmony_ci void __iomem *base; 538c2ecf20Sopenharmony_ci struct regmap *regmap; 548c2ecf20Sopenharmony_ci struct clk_hfpll *h; 558c2ecf20Sopenharmony_ci struct clk_init_data init = { 568c2ecf20Sopenharmony_ci .num_parents = 1, 578c2ecf20Sopenharmony_ci .ops = &clk_ops_hfpll, 588c2ecf20Sopenharmony_ci /* 598c2ecf20Sopenharmony_ci * rather than marking the clock critical and forcing the clock 608c2ecf20Sopenharmony_ci * to be always enabled, we make sure that the clock is not 618c2ecf20Sopenharmony_ci * disabled: the firmware remains responsible of enabling this 628c2ecf20Sopenharmony_ci * clock (for more info check the commit log) 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_ci .flags = CLK_IGNORE_UNUSED, 658c2ecf20Sopenharmony_ci }; 668c2ecf20Sopenharmony_ci int ret; 678c2ecf20Sopenharmony_ci struct clk_parent_data pdata = { .index = 0 }; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci h = devm_kzalloc(dev, sizeof(*h), GFP_KERNEL); 708c2ecf20Sopenharmony_ci if (!h) 718c2ecf20Sopenharmony_ci return -ENOMEM; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 748c2ecf20Sopenharmony_ci base = devm_ioremap_resource(dev, res); 758c2ecf20Sopenharmony_ci if (IS_ERR(base)) 768c2ecf20Sopenharmony_ci return PTR_ERR(base); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci regmap = devm_regmap_init_mmio(&pdev->dev, base, &hfpll_regmap_config); 798c2ecf20Sopenharmony_ci if (IS_ERR(regmap)) 808c2ecf20Sopenharmony_ci return PTR_ERR(regmap); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (of_property_read_string_index(dev->of_node, "clock-output-names", 838c2ecf20Sopenharmony_ci 0, &init.name)) 848c2ecf20Sopenharmony_ci return -ENODEV; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci init.parent_data = &pdata; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci h->d = &hdata; 898c2ecf20Sopenharmony_ci h->clkr.hw.init = &init; 908c2ecf20Sopenharmony_ci spin_lock_init(&h->lock); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci ret = devm_clk_register_regmap(dev, &h->clkr); 938c2ecf20Sopenharmony_ci if (ret) { 948c2ecf20Sopenharmony_ci dev_err(dev, "failed to register regmap clock: %d\n", ret); 958c2ecf20Sopenharmony_ci return ret; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, 998c2ecf20Sopenharmony_ci &h->clkr.hw); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic struct platform_driver qcom_hfpll_driver = { 1038c2ecf20Sopenharmony_ci .probe = qcom_hfpll_probe, 1048c2ecf20Sopenharmony_ci .driver = { 1058c2ecf20Sopenharmony_ci .name = "qcom-hfpll", 1068c2ecf20Sopenharmony_ci .of_match_table = qcom_hfpll_match_table, 1078c2ecf20Sopenharmony_ci }, 1088c2ecf20Sopenharmony_ci}; 1098c2ecf20Sopenharmony_cimodule_platform_driver(qcom_hfpll_driver); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("QCOM HFPLL Clock Driver"); 1128c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1138c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:qcom-hfpll"); 114