1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * |
4 | * Copyright (C) 2012 ARM Limited |
5 | */ |
6 | |
7 | #include <linux/clkdev.h> |
8 | #include <linux/clk-provider.h> |
9 | #include <linux/err.h> |
10 | #include <linux/module.h> |
11 | #include <linux/of.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/vexpress.h> |
15 | |
16 | struct vexpress_osc { |
17 | struct regmap *reg; |
18 | struct clk_hw hw; |
19 | unsigned long rate_min; |
20 | unsigned long rate_max; |
21 | }; |
22 | |
23 | #define to_vexpress_osc(osc) container_of(osc, struct vexpress_osc, hw) |
24 | |
25 | static unsigned long vexpress_osc_recalc_rate(struct clk_hw *hw, |
26 | unsigned long parent_rate) |
27 | { |
28 | struct vexpress_osc *osc = to_vexpress_osc(hw); |
29 | u32 rate; |
30 | |
31 | regmap_read(map: osc->reg, reg: 0, val: &rate); |
32 | |
33 | return rate; |
34 | } |
35 | |
36 | static long vexpress_osc_round_rate(struct clk_hw *hw, unsigned long rate, |
37 | unsigned long *parent_rate) |
38 | { |
39 | struct vexpress_osc *osc = to_vexpress_osc(hw); |
40 | |
41 | if (osc->rate_min && rate < osc->rate_min) |
42 | rate = osc->rate_min; |
43 | |
44 | if (osc->rate_max && rate > osc->rate_max) |
45 | rate = osc->rate_max; |
46 | |
47 | return rate; |
48 | } |
49 | |
50 | static int vexpress_osc_set_rate(struct clk_hw *hw, unsigned long rate, |
51 | unsigned long parent_rate) |
52 | { |
53 | struct vexpress_osc *osc = to_vexpress_osc(hw); |
54 | |
55 | return regmap_write(map: osc->reg, reg: 0, val: rate); |
56 | } |
57 | |
58 | static const struct clk_ops vexpress_osc_ops = { |
59 | .recalc_rate = vexpress_osc_recalc_rate, |
60 | .round_rate = vexpress_osc_round_rate, |
61 | .set_rate = vexpress_osc_set_rate, |
62 | }; |
63 | |
64 | |
65 | static int vexpress_osc_probe(struct platform_device *pdev) |
66 | { |
67 | struct clk_init_data init; |
68 | struct vexpress_osc *osc; |
69 | u32 range[2]; |
70 | int ret; |
71 | |
72 | osc = devm_kzalloc(dev: &pdev->dev, size: sizeof(*osc), GFP_KERNEL); |
73 | if (!osc) |
74 | return -ENOMEM; |
75 | |
76 | osc->reg = devm_regmap_init_vexpress_config(dev: &pdev->dev); |
77 | if (IS_ERR(ptr: osc->reg)) |
78 | return PTR_ERR(ptr: osc->reg); |
79 | |
80 | if (of_property_read_u32_array(np: pdev->dev.of_node, propname: "freq-range" , out_values: range, |
81 | ARRAY_SIZE(range)) == 0) { |
82 | osc->rate_min = range[0]; |
83 | osc->rate_max = range[1]; |
84 | } |
85 | |
86 | if (of_property_read_string(np: pdev->dev.of_node, propname: "clock-output-names" , |
87 | out_string: &init.name) != 0) |
88 | init.name = dev_name(dev: &pdev->dev); |
89 | |
90 | init.ops = &vexpress_osc_ops; |
91 | init.flags = 0; |
92 | init.num_parents = 0; |
93 | |
94 | osc->hw.init = &init; |
95 | |
96 | ret = devm_clk_hw_register(dev: &pdev->dev, hw: &osc->hw); |
97 | if (ret < 0) |
98 | return ret; |
99 | |
100 | devm_of_clk_add_hw_provider(dev: &pdev->dev, get: of_clk_hw_simple_get, data: &osc->hw); |
101 | clk_hw_set_rate_range(hw: &osc->hw, min_rate: osc->rate_min, max_rate: osc->rate_max); |
102 | |
103 | dev_dbg(&pdev->dev, "Registered clock '%s'\n" , init.name); |
104 | |
105 | return 0; |
106 | } |
107 | |
108 | static const struct of_device_id vexpress_osc_of_match[] = { |
109 | { .compatible = "arm,vexpress-osc" , }, |
110 | {} |
111 | }; |
112 | MODULE_DEVICE_TABLE(of, vexpress_osc_of_match); |
113 | |
114 | static struct platform_driver vexpress_osc_driver = { |
115 | .driver = { |
116 | .name = "vexpress-osc" , |
117 | .of_match_table = vexpress_osc_of_match, |
118 | }, |
119 | .probe = vexpress_osc_probe, |
120 | }; |
121 | module_platform_driver(vexpress_osc_driver); |
122 | MODULE_LICENSE("GPL v2" ); |
123 | |