1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Clock driver for the ARM Integrator/IM-PD1 board 4 * Copyright (C) 2012-2013 Linus Walleij 5 */ 6 #include <linux/clk-provider.h> 7 #include <linux/clkdev.h> 8 #include <linux/err.h> 9 #include <linux/io.h> 10 #include <linux/platform_device.h> 11 #include <linux/module.h> 12 #include <linux/mfd/syscon.h> 13 #include <linux/regmap.h> 14 15 #include "icst.h" 16 #include "clk-icst.h" 17 18 #define IMPD1_OSC1 0x00 19 #define IMPD1_OSC2 0x04 20 #define IMPD1_LOCK 0x08 21 22 /* 23 * There are two VCO's on the IM-PD1 24 */ 25 26 static const struct icst_params impd1_vco1_params = { 27 .ref = 24000000, /* 24 MHz */ 28 .vco_max = ICST525_VCO_MAX_3V, 29 .vco_min = ICST525_VCO_MIN, 30 .vd_min = 12, 31 .vd_max = 519, 32 .rd_min = 3, 33 .rd_max = 120, 34 .s2div = icst525_s2div, 35 .idx2s = icst525_idx2s, 36 }; 37 38 static const struct clk_icst_desc impd1_icst1_desc = { 39 .params = &impd1_vco1_params, 40 .vco_offset = IMPD1_OSC1, 41 .lock_offset = IMPD1_LOCK, 42 }; 43 44 static const struct icst_params impd1_vco2_params = { 45 .ref = 24000000, /* 24 MHz */ 46 .vco_max = ICST525_VCO_MAX_3V, 47 .vco_min = ICST525_VCO_MIN, 48 .vd_min = 12, 49 .vd_max = 519, 50 .rd_min = 3, 51 .rd_max = 120, 52 .s2div = icst525_s2div, 53 .idx2s = icst525_idx2s, 54 }; 55 56 static const struct clk_icst_desc impd1_icst2_desc = { 57 .params = &impd1_vco2_params, 58 .vco_offset = IMPD1_OSC2, 59 .lock_offset = IMPD1_LOCK, 60 }; 61 62 static int integrator_impd1_clk_spawn(struct device *dev, 63 struct device_node *parent, 64 struct device_node *np) 65 { 66 struct regmap *map; 67 struct clk *clk = ERR_PTR(-EINVAL); 68 const char *name = np->name; 69 const char *parent_name; 70 const struct clk_icst_desc *desc; 71 int ret; 72 73 map = syscon_node_to_regmap(parent); 74 if (IS_ERR(map)) { 75 pr_err("no regmap for syscon IM-PD1 ICST clock parent\n"); 76 return PTR_ERR(map); 77 } 78 79 if (of_device_is_compatible(np, "arm,impd1-vco1")) { 80 desc = &impd1_icst1_desc; 81 } else if (of_device_is_compatible(np, "arm,impd1-vco2")) { 82 desc = &impd1_icst2_desc; 83 } else { 84 dev_err(dev, "not a clock node %s\n", name); 85 return -ENODEV; 86 } 87 88 of_property_read_string(np, "clock-output-names", &name); 89 parent_name = of_clk_get_parent_name(np, 0); 90 clk = icst_clk_setup(NULL, desc, name, parent_name, map, 91 ICST_INTEGRATOR_IM_PD1); 92 if (!IS_ERR(clk)) { 93 of_clk_add_provider(np, of_clk_src_simple_get, clk); 94 ret = 0; 95 } else { 96 dev_err(dev, "error setting up IM-PD1 ICST clock\n"); 97 ret = PTR_ERR(clk); 98 } 99 100 return ret; 101 } 102 103 static int integrator_impd1_clk_probe(struct platform_device *pdev) 104 { 105 struct device *dev = &pdev->dev; 106 struct device_node *np = dev->of_node; 107 struct device_node *child; 108 int ret = 0; 109 110 for_each_available_child_of_node(np, child) { 111 ret = integrator_impd1_clk_spawn(dev, np, child); 112 if (ret) 113 break; 114 } 115 116 return ret; 117 } 118 119 static const struct of_device_id impd1_syscon_match[] = { 120 { .compatible = "arm,im-pd1-syscon", }, 121 {} 122 }; 123 MODULE_DEVICE_TABLE(of, impd1_syscon_match); 124 125 static struct platform_driver impd1_clk_driver = { 126 .driver = { 127 .name = "impd1-clk", 128 .of_match_table = impd1_syscon_match, 129 }, 130 .probe = integrator_impd1_clk_probe, 131 }; 132 builtin_platform_driver(impd1_clk_driver); 133 134 MODULE_AUTHOR("Linus Walleij <linusw@kernel.org>"); 135 MODULE_DESCRIPTION("Arm IM-PD1 module clock driver"); 136 MODULE_LICENSE("GPL v2"); 137