1 // SPDX-License-Identifier: GPL-2.0+
3 * Amlogic Meson SDHC clock controller
5 * Copyright (C) 2020 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
9 #include <linux/clk-provider.h>
10 #include <linux/device.h>
11 #include <linux/platform_device.h>
13 #include "meson-mx-sdhc.h"
15 #define MESON_SDHC_NUM_BUILTIN_CLKS 6
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
;
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" },
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, },
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
,
56 struct clk_init_data init
= { };
59 snprintf(clk_name
, sizeof(clk_name
), "%s#%s", dev_name(dev
),
64 init
.flags
= CLK_SET_RATE_PARENT
;
65 init
.parent_data
= parents
;
66 init
.num_parents
= num_parents
;
70 return devm_clk_hw_register(dev
, hw
);
73 static int meson_mx_sdhc_gate_clk_hw_register(struct device
*dev
,
74 const char *name_suffix
,
75 struct clk_hw
*parent
,
78 struct clk_parent_data parent_data
= { .hw
= parent
};
80 return meson_mx_sdhc_clk_hw_register(dev
, name_suffix
, &parent_data
, 1,
84 int meson_mx_sdhc_register_clkc(struct device
*dev
, void __iomem
*base
,
85 struct clk_bulk_data
*clk_bulk_data
)
87 struct clk_parent_data div_parent
= { };
88 struct meson_mx_sdhc_clkc
*clkc_data
;
91 clkc_data
= devm_kzalloc(dev
, sizeof(*clkc_data
), GFP_KERNEL
);
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,
101 &clkc_data
->src_sel
.hw
);
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,
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",
120 &clkc_data
->mod_clk_en
.hw
);
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",
128 &clkc_data
->tx_clk_en
.hw
);
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",
136 &clkc_data
->rx_clk_en
.hw
);
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",
144 &clkc_data
->sd_clk_en
.hw
);
149 * TODO: Replace clk_hw.clk with devm_clk_hw_get_clk() once that is
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
;