162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Clock driver for the ARM Integrator/IM-PD1 board 462306a36Sopenharmony_ci * Copyright (C) 2012-2013 Linus Walleij 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include <linux/clk-provider.h> 762306a36Sopenharmony_ci#include <linux/clkdev.h> 862306a36Sopenharmony_ci#include <linux/err.h> 962306a36Sopenharmony_ci#include <linux/io.h> 1062306a36Sopenharmony_ci#include <linux/platform_device.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1362306a36Sopenharmony_ci#include <linux/regmap.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "icst.h" 1662306a36Sopenharmony_ci#include "clk-icst.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define IMPD1_OSC1 0x00 1962306a36Sopenharmony_ci#define IMPD1_OSC2 0x04 2062306a36Sopenharmony_ci#define IMPD1_LOCK 0x08 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* 2362306a36Sopenharmony_ci * There are two VCO's on the IM-PD1 2462306a36Sopenharmony_ci */ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic const struct icst_params impd1_vco1_params = { 2762306a36Sopenharmony_ci .ref = 24000000, /* 24 MHz */ 2862306a36Sopenharmony_ci .vco_max = ICST525_VCO_MAX_3V, 2962306a36Sopenharmony_ci .vco_min = ICST525_VCO_MIN, 3062306a36Sopenharmony_ci .vd_min = 12, 3162306a36Sopenharmony_ci .vd_max = 519, 3262306a36Sopenharmony_ci .rd_min = 3, 3362306a36Sopenharmony_ci .rd_max = 120, 3462306a36Sopenharmony_ci .s2div = icst525_s2div, 3562306a36Sopenharmony_ci .idx2s = icst525_idx2s, 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic const struct clk_icst_desc impd1_icst1_desc = { 3962306a36Sopenharmony_ci .params = &impd1_vco1_params, 4062306a36Sopenharmony_ci .vco_offset = IMPD1_OSC1, 4162306a36Sopenharmony_ci .lock_offset = IMPD1_LOCK, 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic const struct icst_params impd1_vco2_params = { 4562306a36Sopenharmony_ci .ref = 24000000, /* 24 MHz */ 4662306a36Sopenharmony_ci .vco_max = ICST525_VCO_MAX_3V, 4762306a36Sopenharmony_ci .vco_min = ICST525_VCO_MIN, 4862306a36Sopenharmony_ci .vd_min = 12, 4962306a36Sopenharmony_ci .vd_max = 519, 5062306a36Sopenharmony_ci .rd_min = 3, 5162306a36Sopenharmony_ci .rd_max = 120, 5262306a36Sopenharmony_ci .s2div = icst525_s2div, 5362306a36Sopenharmony_ci .idx2s = icst525_idx2s, 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic const struct clk_icst_desc impd1_icst2_desc = { 5762306a36Sopenharmony_ci .params = &impd1_vco2_params, 5862306a36Sopenharmony_ci .vco_offset = IMPD1_OSC2, 5962306a36Sopenharmony_ci .lock_offset = IMPD1_LOCK, 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic int integrator_impd1_clk_spawn(struct device *dev, 6362306a36Sopenharmony_ci struct device_node *parent, 6462306a36Sopenharmony_ci struct device_node *np) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci struct regmap *map; 6762306a36Sopenharmony_ci struct clk *clk = ERR_PTR(-EINVAL); 6862306a36Sopenharmony_ci const char *name = np->name; 6962306a36Sopenharmony_ci const char *parent_name; 7062306a36Sopenharmony_ci const struct clk_icst_desc *desc; 7162306a36Sopenharmony_ci int ret; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci map = syscon_node_to_regmap(parent); 7462306a36Sopenharmony_ci if (IS_ERR(map)) { 7562306a36Sopenharmony_ci pr_err("no regmap for syscon IM-PD1 ICST clock parent\n"); 7662306a36Sopenharmony_ci return PTR_ERR(map); 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (of_device_is_compatible(np, "arm,impd1-vco1")) { 8062306a36Sopenharmony_ci desc = &impd1_icst1_desc; 8162306a36Sopenharmony_ci } else if (of_device_is_compatible(np, "arm,impd1-vco2")) { 8262306a36Sopenharmony_ci desc = &impd1_icst2_desc; 8362306a36Sopenharmony_ci } else { 8462306a36Sopenharmony_ci dev_err(dev, "not a clock node %s\n", name); 8562306a36Sopenharmony_ci return -ENODEV; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci of_property_read_string(np, "clock-output-names", &name); 8962306a36Sopenharmony_ci parent_name = of_clk_get_parent_name(np, 0); 9062306a36Sopenharmony_ci clk = icst_clk_setup(NULL, desc, name, parent_name, map, 9162306a36Sopenharmony_ci ICST_INTEGRATOR_IM_PD1); 9262306a36Sopenharmony_ci if (!IS_ERR(clk)) { 9362306a36Sopenharmony_ci of_clk_add_provider(np, of_clk_src_simple_get, clk); 9462306a36Sopenharmony_ci ret = 0; 9562306a36Sopenharmony_ci } else { 9662306a36Sopenharmony_ci dev_err(dev, "error setting up IM-PD1 ICST clock\n"); 9762306a36Sopenharmony_ci ret = PTR_ERR(clk); 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return ret; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic int integrator_impd1_clk_probe(struct platform_device *pdev) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct device *dev = &pdev->dev; 10662306a36Sopenharmony_ci struct device_node *np = dev->of_node; 10762306a36Sopenharmony_ci struct device_node *child; 10862306a36Sopenharmony_ci int ret = 0; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci for_each_available_child_of_node(np, child) { 11162306a36Sopenharmony_ci ret = integrator_impd1_clk_spawn(dev, np, child); 11262306a36Sopenharmony_ci if (ret) { 11362306a36Sopenharmony_ci of_node_put(child); 11462306a36Sopenharmony_ci break; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return ret; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic const struct of_device_id impd1_syscon_match[] = { 12262306a36Sopenharmony_ci { .compatible = "arm,im-pd1-syscon", }, 12362306a36Sopenharmony_ci {} 12462306a36Sopenharmony_ci}; 12562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, impd1_syscon_match); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic struct platform_driver impd1_clk_driver = { 12862306a36Sopenharmony_ci .driver = { 12962306a36Sopenharmony_ci .name = "impd1-clk", 13062306a36Sopenharmony_ci .of_match_table = impd1_syscon_match, 13162306a36Sopenharmony_ci }, 13262306a36Sopenharmony_ci .probe = integrator_impd1_clk_probe, 13362306a36Sopenharmony_ci}; 13462306a36Sopenharmony_cibuiltin_platform_driver(impd1_clk_driver); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ciMODULE_AUTHOR("Linus Walleij <linusw@kernel.org>"); 13762306a36Sopenharmony_ciMODULE_DESCRIPTION("Arm IM-PD1 module clock driver"); 13862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 139