18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Clock driver for the ARM Integrator/IM-PD1 board 48c2ecf20Sopenharmony_ci * Copyright (C) 2012-2013 Linus Walleij 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 78c2ecf20Sopenharmony_ci#include <linux/clkdev.h> 88c2ecf20Sopenharmony_ci#include <linux/err.h> 98c2ecf20Sopenharmony_ci#include <linux/io.h> 108c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 138c2ecf20Sopenharmony_ci#include <linux/regmap.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "icst.h" 168c2ecf20Sopenharmony_ci#include "clk-icst.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define IMPD1_OSC1 0x00 198c2ecf20Sopenharmony_ci#define IMPD1_OSC2 0x04 208c2ecf20Sopenharmony_ci#define IMPD1_LOCK 0x08 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* 238c2ecf20Sopenharmony_ci * There are two VCO's on the IM-PD1 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic const struct icst_params impd1_vco1_params = { 278c2ecf20Sopenharmony_ci .ref = 24000000, /* 24 MHz */ 288c2ecf20Sopenharmony_ci .vco_max = ICST525_VCO_MAX_3V, 298c2ecf20Sopenharmony_ci .vco_min = ICST525_VCO_MIN, 308c2ecf20Sopenharmony_ci .vd_min = 12, 318c2ecf20Sopenharmony_ci .vd_max = 519, 328c2ecf20Sopenharmony_ci .rd_min = 3, 338c2ecf20Sopenharmony_ci .rd_max = 120, 348c2ecf20Sopenharmony_ci .s2div = icst525_s2div, 358c2ecf20Sopenharmony_ci .idx2s = icst525_idx2s, 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic const struct clk_icst_desc impd1_icst1_desc = { 398c2ecf20Sopenharmony_ci .params = &impd1_vco1_params, 408c2ecf20Sopenharmony_ci .vco_offset = IMPD1_OSC1, 418c2ecf20Sopenharmony_ci .lock_offset = IMPD1_LOCK, 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic const struct icst_params impd1_vco2_params = { 458c2ecf20Sopenharmony_ci .ref = 24000000, /* 24 MHz */ 468c2ecf20Sopenharmony_ci .vco_max = ICST525_VCO_MAX_3V, 478c2ecf20Sopenharmony_ci .vco_min = ICST525_VCO_MIN, 488c2ecf20Sopenharmony_ci .vd_min = 12, 498c2ecf20Sopenharmony_ci .vd_max = 519, 508c2ecf20Sopenharmony_ci .rd_min = 3, 518c2ecf20Sopenharmony_ci .rd_max = 120, 528c2ecf20Sopenharmony_ci .s2div = icst525_s2div, 538c2ecf20Sopenharmony_ci .idx2s = icst525_idx2s, 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic const struct clk_icst_desc impd1_icst2_desc = { 578c2ecf20Sopenharmony_ci .params = &impd1_vco2_params, 588c2ecf20Sopenharmony_ci .vco_offset = IMPD1_OSC2, 598c2ecf20Sopenharmony_ci .lock_offset = IMPD1_LOCK, 608c2ecf20Sopenharmony_ci}; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int integrator_impd1_clk_spawn(struct device *dev, 638c2ecf20Sopenharmony_ci struct device_node *parent, 648c2ecf20Sopenharmony_ci struct device_node *np) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct regmap *map; 678c2ecf20Sopenharmony_ci struct clk *clk = ERR_PTR(-EINVAL); 688c2ecf20Sopenharmony_ci const char *name = np->name; 698c2ecf20Sopenharmony_ci const char *parent_name; 708c2ecf20Sopenharmony_ci const struct clk_icst_desc *desc; 718c2ecf20Sopenharmony_ci int ret; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci map = syscon_node_to_regmap(parent); 748c2ecf20Sopenharmony_ci if (IS_ERR(map)) { 758c2ecf20Sopenharmony_ci pr_err("no regmap for syscon IM-PD1 ICST clock parent\n"); 768c2ecf20Sopenharmony_ci return PTR_ERR(map); 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (of_device_is_compatible(np, "arm,impd1-vco1")) { 808c2ecf20Sopenharmony_ci desc = &impd1_icst1_desc; 818c2ecf20Sopenharmony_ci } else if (of_device_is_compatible(np, "arm,impd1-vco2")) { 828c2ecf20Sopenharmony_ci desc = &impd1_icst2_desc; 838c2ecf20Sopenharmony_ci } else { 848c2ecf20Sopenharmony_ci dev_err(dev, "not a clock node %s\n", name); 858c2ecf20Sopenharmony_ci return -ENODEV; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci of_property_read_string(np, "clock-output-names", &name); 898c2ecf20Sopenharmony_ci parent_name = of_clk_get_parent_name(np, 0); 908c2ecf20Sopenharmony_ci clk = icst_clk_setup(NULL, desc, name, parent_name, map, 918c2ecf20Sopenharmony_ci ICST_INTEGRATOR_IM_PD1); 928c2ecf20Sopenharmony_ci if (!IS_ERR(clk)) { 938c2ecf20Sopenharmony_ci of_clk_add_provider(np, of_clk_src_simple_get, clk); 948c2ecf20Sopenharmony_ci ret = 0; 958c2ecf20Sopenharmony_ci } else { 968c2ecf20Sopenharmony_ci dev_err(dev, "error setting up IM-PD1 ICST clock\n"); 978c2ecf20Sopenharmony_ci ret = PTR_ERR(clk); 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci return ret; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic int integrator_impd1_clk_probe(struct platform_device *pdev) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1068c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 1078c2ecf20Sopenharmony_ci struct device_node *child; 1088c2ecf20Sopenharmony_ci int ret = 0; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci for_each_available_child_of_node(np, child) { 1118c2ecf20Sopenharmony_ci ret = integrator_impd1_clk_spawn(dev, np, child); 1128c2ecf20Sopenharmony_ci if (ret) { 1138c2ecf20Sopenharmony_ci of_node_put(child); 1148c2ecf20Sopenharmony_ci break; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci return ret; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic const struct of_device_id impd1_syscon_match[] = { 1228c2ecf20Sopenharmony_ci { .compatible = "arm,im-pd1-syscon", }, 1238c2ecf20Sopenharmony_ci {} 1248c2ecf20Sopenharmony_ci}; 1258c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, impd1_syscon_match); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic struct platform_driver impd1_clk_driver = { 1288c2ecf20Sopenharmony_ci .driver = { 1298c2ecf20Sopenharmony_ci .name = "impd1-clk", 1308c2ecf20Sopenharmony_ci .of_match_table = impd1_syscon_match, 1318c2ecf20Sopenharmony_ci }, 1328c2ecf20Sopenharmony_ci .probe = integrator_impd1_clk_probe, 1338c2ecf20Sopenharmony_ci}; 1348c2ecf20Sopenharmony_cibuiltin_platform_driver(impd1_clk_driver); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ciMODULE_AUTHOR("Linus Walleij <linusw@kernel.org>"); 1378c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Arm IM-PD1 module clock driver"); 1388c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 139