a5626c33d1e3e67b5f792adb36173c6ab49913ed
[oweals/u-boot.git] / drivers / clk / clk-composite.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2013 NVIDIA CORPORATION.  All rights reserved.
4  * Copyright 2019 NXP
5  */
6
7 #include <common.h>
8 #include <asm/io.h>
9 #include <malloc.h>
10 #include <clk-uclass.h>
11 #include <dm/device.h>
12 #include <linux/clk-provider.h>
13 #include <clk.h>
14
15 #include "clk.h"
16
17 #define UBOOT_DM_CLK_COMPOSITE "clk_composite"
18
19 static u8 clk_composite_get_parent(struct clk *clk)
20 {
21         struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ?
22                 (struct clk *)dev_get_clk_ptr(clk->dev) : clk);
23         struct clk *mux = composite->mux;
24
25         return clk_mux_get_parent(mux);
26 }
27
28 static int clk_composite_set_parent(struct clk *clk, struct clk *parent)
29 {
30         struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ?
31                 (struct clk *)dev_get_clk_ptr(clk->dev) : clk);
32         const struct clk_ops *mux_ops = composite->mux_ops;
33         struct clk *mux = composite->mux;
34
35         return mux_ops->set_parent(mux, parent);
36 }
37
38 static unsigned long clk_composite_recalc_rate(struct clk *clk)
39 {
40         struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ?
41                 (struct clk *)dev_get_clk_ptr(clk->dev) : clk);
42         const struct clk_ops *rate_ops = composite->rate_ops;
43         struct clk *rate = composite->rate;
44
45         return rate_ops->get_rate(rate);
46 }
47
48 static ulong clk_composite_set_rate(struct clk *clk, unsigned long rate)
49 {
50         struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ?
51                 (struct clk *)dev_get_clk_ptr(clk->dev) : clk);
52         const struct clk_ops *rate_ops = composite->rate_ops;
53         struct clk *clk_rate = composite->rate;
54
55         return rate_ops->set_rate(clk_rate, rate);
56 }
57
58 static int clk_composite_enable(struct clk *clk)
59 {
60         struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ?
61                 (struct clk *)dev_get_clk_ptr(clk->dev) : clk);
62         const struct clk_ops *gate_ops = composite->gate_ops;
63         struct clk *gate = composite->gate;
64
65         return gate_ops->enable(gate);
66 }
67
68 static int clk_composite_disable(struct clk *clk)
69 {
70         struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ?
71                 (struct clk *)dev_get_clk_ptr(clk->dev) : clk);
72         const struct clk_ops *gate_ops = composite->gate_ops;
73         struct clk *gate = composite->gate;
74
75         gate_ops->disable(gate);
76
77         return 0;
78 }
79
80 struct clk_ops clk_composite_ops = {
81         /* This will be set according to clk_register_composite */
82 };
83
84 struct clk *clk_register_composite(struct device *dev, const char *name,
85                                    const char * const *parent_names,
86                                    int num_parents, struct clk *mux,
87                                    const struct clk_ops *mux_ops,
88                                    struct clk *rate,
89                                    const struct clk_ops *rate_ops,
90                                    struct clk *gate,
91                                    const struct clk_ops *gate_ops,
92                                    unsigned long flags)
93 {
94         struct clk *clk;
95         struct clk_composite *composite;
96         int ret;
97         struct clk_ops *composite_ops = &clk_composite_ops;
98
99         composite = kzalloc(sizeof(*composite), GFP_KERNEL);
100         if (!composite)
101                 return ERR_PTR(-ENOMEM);
102
103         if (mux && mux_ops) {
104                 composite->mux = mux;
105                 composite->mux_ops = mux_ops;
106                 if (mux_ops->set_parent)
107                         composite_ops->set_parent = clk_composite_set_parent;
108                 mux->data = (ulong)composite;
109         }
110
111         if (rate && rate_ops) {
112                 if (!rate_ops->get_rate) {
113                         clk = ERR_PTR(-EINVAL);
114                         goto err;
115                 }
116                 composite_ops->get_rate = clk_composite_recalc_rate;
117
118                 /* .set_rate requires either .round_rate or .determine_rate */
119                 if (rate_ops->set_rate)
120                         composite_ops->set_rate = clk_composite_set_rate;
121
122                 composite->rate = rate;
123                 composite->rate_ops = rate_ops;
124                 rate->data = (ulong)composite;
125         }
126
127         if (gate && gate_ops) {
128                 if (!gate_ops->enable || !gate_ops->disable) {
129                         clk = ERR_PTR(-EINVAL);
130                         goto err;
131                 }
132
133                 composite->gate = gate;
134                 composite->gate_ops = gate_ops;
135                 composite_ops->enable = clk_composite_enable;
136                 composite_ops->disable = clk_composite_disable;
137                 gate->data = (ulong)composite;
138         }
139
140         clk = &composite->clk;
141         ret = clk_register(clk, UBOOT_DM_CLK_COMPOSITE, name,
142                            parent_names[clk_composite_get_parent(clk)]);
143         if (ret) {
144                 clk = ERR_PTR(ret);
145                 goto err;
146         }
147
148         return clk;
149
150 err:
151         kfree(composite);
152         return clk;
153 }
154
155 U_BOOT_DRIVER(clk_composite) = {
156         .name   = UBOOT_DM_CLK_COMPOSITE,
157         .id     = UCLASS_CLK,
158         .ops    = &clk_composite_ops,
159         .flags = DM_FLAG_PRE_RELOC,
160 };