Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
70ee6577 LW |
2 | /* |
3 | * Clock driver for the ARM Integrator/IM-PD1 board | |
8e048b99 | 4 | * Copyright (C) 2012-2013 Linus Walleij |
70ee6577 LW |
5 | */ |
6 | #include <linux/clk-provider.h> | |
70ee6577 LW |
7 | #include <linux/clkdev.h> |
8 | #include <linux/err.h> | |
9 | #include <linux/io.h> | |
84655b76 | 10 | #include <linux/platform_device.h> |
84655b76 LW |
11 | #include <linux/module.h> |
12 | #include <linux/mfd/syscon.h> | |
13 | #include <linux/regmap.h> | |
70ee6577 | 14 | |
ba3fae06 | 15 | #include "icst.h" |
70ee6577 LW |
16 | #include "clk-icst.h" |
17 | ||
cc0cc4ca LW |
18 | #define IMPD1_OSC1 0x00 |
19 | #define IMPD1_OSC2 0x04 | |
20 | #define IMPD1_LOCK 0x08 | |
21 | ||
70ee6577 | 22 | /* |
8e048b99 | 23 | * There are two VCO's on the IM-PD1 |
70ee6577 LW |
24 | */ |
25 | ||
8e048b99 | 26 | static const struct icst_params impd1_vco1_params = { |
70ee6577 LW |
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 = { | |
8e048b99 | 39 | .params = &impd1_vco1_params, |
70ee6577 LW |
40 | .vco_offset = IMPD1_OSC1, |
41 | .lock_offset = IMPD1_LOCK, | |
42 | }; | |
43 | ||
8e048b99 LW |
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 | ||
84655b76 LW |
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 | ||
24661081 | 88 | of_property_read_string(np, "clock-output-names", &name); |
84655b76 LW |
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); | |
da9c43dc SP |
112 | if (ret) { |
113 | of_node_put(child); | |
84655b76 | 114 | break; |
da9c43dc | 115 | } |
84655b76 LW |
116 | } |
117 | ||
118 | return ret; | |
119 | } | |
120 | ||
121 | static const struct of_device_id impd1_syscon_match[] = { | |
122 | { .compatible = "arm,im-pd1-syscon", }, | |
123 | {} | |
124 | }; | |
125 | MODULE_DEVICE_TABLE(of, impd1_syscon_match); | |
126 | ||
127 | static struct platform_driver impd1_clk_driver = { | |
128 | .driver = { | |
129 | .name = "impd1-clk", | |
130 | .of_match_table = impd1_syscon_match, | |
131 | }, | |
132 | .probe = integrator_impd1_clk_probe, | |
133 | }; | |
134 | builtin_platform_driver(impd1_clk_driver); | |
135 | ||
136 | MODULE_AUTHOR("Linus Walleij <linusw@kernel.org>"); | |
137 | MODULE_DESCRIPTION("Arm IM-PD1 module clock driver"); | |
138 | MODULE_LICENSE("GPL v2"); |