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