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(error: -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(np: parent); |
74 | if (IS_ERR(ptr: map)) { |
75 | pr_err("no regmap for syscon IM-PD1 ICST clock parent\n" ); |
76 | return PTR_ERR(ptr: map); |
77 | } |
78 | |
79 | if (of_device_is_compatible(device: np, "arm,impd1-vco1" )) { |
80 | desc = &impd1_icst1_desc; |
81 | } else if (of_device_is_compatible(device: 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, propname: "clock-output-names" , out_string: &name); |
89 | parent_name = of_clk_get_parent_name(np, index: 0); |
90 | clk = icst_clk_setup(NULL, desc, name, parent_name, map, |
91 | ctype: ICST_INTEGRATOR_IM_PD1); |
92 | if (!IS_ERR(ptr: clk)) { |
93 | of_clk_add_provider(np, clk_src_get: of_clk_src_simple_get, data: clk); |
94 | ret = 0; |
95 | } else { |
96 | dev_err(dev, "error setting up IM-PD1 ICST clock\n" ); |
97 | ret = PTR_ERR(ptr: 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, parent: np, np: child); |
112 | if (ret) { |
113 | of_node_put(node: child); |
114 | break; |
115 | } |
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" ); |
139 | |