Merge tag 'for-5.15/io_uring-2021-09-04' of git://git.kernel.dk/linux-block
[linux-2.6-microblaze.git] / drivers / mmc / host / meson-mx-sdhc-clkc.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Amlogic Meson SDHC clock controller
4  *
5  * Copyright (C) 2020 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
6  */
7
8 #include <linux/clk.h>
9 #include <linux/clk-provider.h>
10 #include <linux/device.h>
11 #include <linux/platform_device.h>
12
13 #include "meson-mx-sdhc.h"
14
15 #define MESON_SDHC_NUM_BUILTIN_CLKS     6
16
17 struct meson_mx_sdhc_clkc {
18         struct clk_mux                  src_sel;
19         struct clk_divider              div;
20         struct clk_gate                 mod_clk_en;
21         struct clk_gate                 tx_clk_en;
22         struct clk_gate                 rx_clk_en;
23         struct clk_gate                 sd_clk_en;
24 };
25
26 static const struct clk_parent_data meson_mx_sdhc_src_sel_parents[4] = {
27         { .fw_name = "clkin0" },
28         { .fw_name = "clkin1" },
29         { .fw_name = "clkin2" },
30         { .fw_name = "clkin3" },
31 };
32
33 static const struct clk_div_table meson_mx_sdhc_div_table[] = {
34         { .div = 6, .val = 5, },
35         { .div = 8, .val = 7, },
36         { .div = 9, .val = 8, },
37         { .div = 10, .val = 9, },
38         { .div = 12, .val = 11, },
39         { .div = 16, .val = 15, },
40         { .div = 18, .val = 17, },
41         { .div = 34, .val = 33, },
42         { .div = 142, .val = 141, },
43         { .div = 850, .val = 849, },
44         { .div = 2126, .val = 2125, },
45         { .div = 4096, .val = 4095, },
46         { /* sentinel */ }
47 };
48
49 static int meson_mx_sdhc_clk_hw_register(struct device *dev,
50                                          const char *name_suffix,
51                                          const struct clk_parent_data *parents,
52                                          unsigned int num_parents,
53                                          const struct clk_ops *ops,
54                                          struct clk_hw *hw)
55 {
56         struct clk_init_data init = { };
57         char clk_name[32];
58
59         snprintf(clk_name, sizeof(clk_name), "%s#%s", dev_name(dev),
60                  name_suffix);
61
62         init.name = clk_name;
63         init.ops = ops;
64         init.flags = CLK_SET_RATE_PARENT;
65         init.parent_data = parents;
66         init.num_parents = num_parents;
67
68         hw->init = &init;
69
70         return devm_clk_hw_register(dev, hw);
71 }
72
73 static int meson_mx_sdhc_gate_clk_hw_register(struct device *dev,
74                                               const char *name_suffix,
75                                               struct clk_hw *parent,
76                                               struct clk_hw *hw)
77 {
78         struct clk_parent_data parent_data = { .hw = parent };
79
80         return meson_mx_sdhc_clk_hw_register(dev, name_suffix, &parent_data, 1,
81                                              &clk_gate_ops, hw);
82 }
83
84 int meson_mx_sdhc_register_clkc(struct device *dev, void __iomem *base,
85                                 struct clk_bulk_data *clk_bulk_data)
86 {
87         struct clk_parent_data div_parent = { };
88         struct meson_mx_sdhc_clkc *clkc_data;
89         int ret;
90
91         clkc_data = devm_kzalloc(dev, sizeof(*clkc_data), GFP_KERNEL);
92         if (!clkc_data)
93                 return -ENOMEM;
94
95         clkc_data->src_sel.reg = base + MESON_SDHC_CLKC;
96         clkc_data->src_sel.mask = 0x3;
97         clkc_data->src_sel.shift = 16;
98         ret = meson_mx_sdhc_clk_hw_register(dev, "src_sel",
99                                             meson_mx_sdhc_src_sel_parents, 4,
100                                             &clk_mux_ops,
101                                             &clkc_data->src_sel.hw);
102         if (ret)
103                 return ret;
104
105         clkc_data->div.reg = base + MESON_SDHC_CLKC;
106         clkc_data->div.shift = 0;
107         clkc_data->div.width = 12;
108         clkc_data->div.table = meson_mx_sdhc_div_table;
109         div_parent.hw = &clkc_data->src_sel.hw;
110         ret = meson_mx_sdhc_clk_hw_register(dev, "div", &div_parent, 1,
111                                             &clk_divider_ops,
112                                             &clkc_data->div.hw);
113         if (ret)
114                 return ret;
115
116         clkc_data->mod_clk_en.reg = base + MESON_SDHC_CLKC;
117         clkc_data->mod_clk_en.bit_idx = 15;
118         ret = meson_mx_sdhc_gate_clk_hw_register(dev, "mod_clk_on",
119                                                  &clkc_data->div.hw,
120                                                  &clkc_data->mod_clk_en.hw);
121         if (ret)
122                 return ret;
123
124         clkc_data->tx_clk_en.reg = base + MESON_SDHC_CLKC;
125         clkc_data->tx_clk_en.bit_idx = 14;
126         ret = meson_mx_sdhc_gate_clk_hw_register(dev, "tx_clk_on",
127                                                  &clkc_data->div.hw,
128                                                  &clkc_data->tx_clk_en.hw);
129         if (ret)
130                 return ret;
131
132         clkc_data->rx_clk_en.reg = base + MESON_SDHC_CLKC;
133         clkc_data->rx_clk_en.bit_idx = 13;
134         ret = meson_mx_sdhc_gate_clk_hw_register(dev, "rx_clk_on",
135                                                  &clkc_data->div.hw,
136                                                  &clkc_data->rx_clk_en.hw);
137         if (ret)
138                 return ret;
139
140         clkc_data->sd_clk_en.reg = base + MESON_SDHC_CLKC;
141         clkc_data->sd_clk_en.bit_idx = 12;
142         ret = meson_mx_sdhc_gate_clk_hw_register(dev, "sd_clk_on",
143                                                  &clkc_data->div.hw,
144                                                  &clkc_data->sd_clk_en.hw);
145         if (ret)
146                 return ret;
147
148         /*
149          * TODO: Replace clk_hw.clk with devm_clk_hw_get_clk() once that is
150          * available.
151          */
152         clk_bulk_data[0].clk = clkc_data->mod_clk_en.hw.clk;
153         clk_bulk_data[1].clk = clkc_data->sd_clk_en.hw.clk;
154         clk_bulk_data[2].clk = clkc_data->tx_clk_en.hw.clk;
155         clk_bulk_data[3].clk = clkc_data->rx_clk_en.hw.clk;
156
157         return 0;
158 }