1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Clock driver for Hi655x |
4 | * |
5 | * Copyright (c) 2017, Linaro Ltd. |
6 | * |
7 | * Author: Daniel Lezcano <daniel.lezcano@linaro.org> |
8 | */ |
9 | #include <linux/clk-provider.h> |
10 | #include <linux/module.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/regmap.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/mfd/core.h> |
15 | #include <linux/mfd/hi655x-pmic.h> |
16 | |
17 | #define HI655X_CLK_BASE HI655X_BUS_ADDR(0x1c) |
18 | #define HI655X_CLK_SET BIT(6) |
19 | |
20 | struct hi655x_clk { |
21 | struct hi655x_pmic *hi655x; |
22 | struct clk_hw clk_hw; |
23 | }; |
24 | |
25 | static unsigned long hi655x_clk_recalc_rate(struct clk_hw *hw, |
26 | unsigned long parent_rate) |
27 | { |
28 | return 32768; |
29 | } |
30 | |
31 | static int hi655x_clk_enable(struct clk_hw *hw, bool enable) |
32 | { |
33 | struct hi655x_clk *hi655x_clk = |
34 | container_of(hw, struct hi655x_clk, clk_hw); |
35 | |
36 | struct hi655x_pmic *hi655x = hi655x_clk->hi655x; |
37 | |
38 | return regmap_update_bits(map: hi655x->regmap, HI655X_CLK_BASE, |
39 | HI655X_CLK_SET, val: enable ? HI655X_CLK_SET : 0); |
40 | } |
41 | |
42 | static int hi655x_clk_prepare(struct clk_hw *hw) |
43 | { |
44 | return hi655x_clk_enable(hw, enable: true); |
45 | } |
46 | |
47 | static void hi655x_clk_unprepare(struct clk_hw *hw) |
48 | { |
49 | hi655x_clk_enable(hw, enable: false); |
50 | } |
51 | |
52 | static int hi655x_clk_is_prepared(struct clk_hw *hw) |
53 | { |
54 | struct hi655x_clk *hi655x_clk = |
55 | container_of(hw, struct hi655x_clk, clk_hw); |
56 | struct hi655x_pmic *hi655x = hi655x_clk->hi655x; |
57 | int ret; |
58 | uint32_t val; |
59 | |
60 | ret = regmap_read(map: hi655x->regmap, HI655X_CLK_BASE, val: &val); |
61 | if (ret < 0) |
62 | return ret; |
63 | |
64 | return val & HI655X_CLK_BASE; |
65 | } |
66 | |
67 | static const struct clk_ops hi655x_clk_ops = { |
68 | .prepare = hi655x_clk_prepare, |
69 | .unprepare = hi655x_clk_unprepare, |
70 | .is_prepared = hi655x_clk_is_prepared, |
71 | .recalc_rate = hi655x_clk_recalc_rate, |
72 | }; |
73 | |
74 | static int hi655x_clk_probe(struct platform_device *pdev) |
75 | { |
76 | struct device *parent = pdev->dev.parent; |
77 | struct hi655x_pmic *hi655x = dev_get_drvdata(dev: parent); |
78 | struct hi655x_clk *hi655x_clk; |
79 | const char *clk_name = "hi655x-clk" ; |
80 | struct clk_init_data init = { |
81 | .name = clk_name, |
82 | .ops = &hi655x_clk_ops |
83 | }; |
84 | int ret; |
85 | |
86 | hi655x_clk = devm_kzalloc(dev: &pdev->dev, size: sizeof(*hi655x_clk), GFP_KERNEL); |
87 | if (!hi655x_clk) |
88 | return -ENOMEM; |
89 | |
90 | of_property_read_string_index(np: parent->of_node, propname: "clock-output-names" , |
91 | index: 0, output: &clk_name); |
92 | |
93 | hi655x_clk->clk_hw.init = &init; |
94 | hi655x_clk->hi655x = hi655x; |
95 | |
96 | platform_set_drvdata(pdev, data: hi655x_clk); |
97 | |
98 | ret = devm_clk_hw_register(dev: &pdev->dev, hw: &hi655x_clk->clk_hw); |
99 | if (ret) |
100 | return ret; |
101 | |
102 | return devm_of_clk_add_hw_provider(dev: &pdev->dev, get: of_clk_hw_simple_get, |
103 | data: &hi655x_clk->clk_hw); |
104 | } |
105 | |
106 | static struct platform_driver hi655x_clk_driver = { |
107 | .probe = hi655x_clk_probe, |
108 | .driver = { |
109 | .name = "hi655x-clk" , |
110 | }, |
111 | }; |
112 | |
113 | module_platform_driver(hi655x_clk_driver); |
114 | |
115 | MODULE_DESCRIPTION("Clk driver for the hi655x series PMICs" ); |
116 | MODULE_AUTHOR("Daniel Lezcano <daniel.lezcano@linaro.org>" ); |
117 | MODULE_LICENSE("GPL" ); |
118 | MODULE_ALIAS("platform:hi655x-clk" ); |
119 | |