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 struct meson_mx_sdhc_clkc
{
16 struct clk_mux src_sel
;
17 struct clk_divider div
;
18 struct clk_gate mod_clk_en
;
19 struct clk_gate tx_clk_en
;
20 struct clk_gate rx_clk_en
;
21 struct clk_gate sd_clk_en
;
24 static const struct clk_parent_data meson_mx_sdhc_src_sel_parents
[4] = {
25 { .fw_name
= "clkin0" },
26 { .fw_name
= "clkin1" },
27 { .fw_name
= "clkin2" },
28 { .fw_name
= "clkin3" },
31 static const struct clk_div_table meson_mx_sdhc_div_table
[] = {
32 { .div
= 6, .val
= 5, },
33 { .div
= 8, .val
= 7, },
34 { .div
= 9, .val
= 8, },
35 { .div
= 10, .val
= 9, },
36 { .div
= 12, .val
= 11, },
37 { .div
= 16, .val
= 15, },
38 { .div
= 18, .val
= 17, },
39 { .div
= 34, .val
= 33, },
40 { .div
= 142, .val
= 141, },
41 { .div
= 850, .val
= 849, },
42 { .div
= 2126, .val
= 2125, },
43 { .div
= 4096, .val
= 4095, },
47 static int meson_mx_sdhc_clk_hw_register(struct device
*dev
,
48 const char *name_suffix
,
49 const struct clk_parent_data
*parents
,
50 unsigned int num_parents
,
51 const struct clk_ops
*ops
,
54 struct clk_init_data init
= { };
57 snprintf(clk_name
, sizeof(clk_name
), "%s#%s", dev_name(dev
),
62 init
.flags
= CLK_SET_RATE_PARENT
;
63 init
.parent_data
= parents
;
64 init
.num_parents
= num_parents
;
68 return devm_clk_hw_register(dev
, hw
);
71 static int meson_mx_sdhc_gate_clk_hw_register(struct device
*dev
,
72 const char *name_suffix
,
73 struct clk_hw
*parent
,
75 struct clk_bulk_data
*clk_bulk_data
,
78 struct clk_parent_data parent_data
= { .hw
= parent
};
81 ret
= meson_mx_sdhc_clk_hw_register(dev
, name_suffix
, &parent_data
, 1,
86 clk_bulk_data
[bulk_index
].clk
= devm_clk_hw_get_clk(dev
, hw
, name_suffix
);
87 if (IS_ERR(clk_bulk_data
[bulk_index
].clk
))
88 return PTR_ERR(clk_bulk_data
[bulk_index
].clk
);
93 int meson_mx_sdhc_register_clkc(struct device
*dev
, void __iomem
*base
,
94 struct clk_bulk_data
*clk_bulk_data
)
96 struct clk_parent_data div_parent
= { };
97 struct meson_mx_sdhc_clkc
*clkc_data
;
100 clkc_data
= devm_kzalloc(dev
, sizeof(*clkc_data
), GFP_KERNEL
);
104 clkc_data
->src_sel
.reg
= base
+ MESON_SDHC_CLKC
;
105 clkc_data
->src_sel
.mask
= 0x3;
106 clkc_data
->src_sel
.shift
= 16;
107 ret
= meson_mx_sdhc_clk_hw_register(dev
, "src_sel",
108 meson_mx_sdhc_src_sel_parents
, 4,
110 &clkc_data
->src_sel
.hw
);
114 clkc_data
->div
.reg
= base
+ MESON_SDHC_CLKC
;
115 clkc_data
->div
.shift
= 0;
116 clkc_data
->div
.width
= 12;
117 clkc_data
->div
.table
= meson_mx_sdhc_div_table
;
118 div_parent
.hw
= &clkc_data
->src_sel
.hw
;
119 ret
= meson_mx_sdhc_clk_hw_register(dev
, "div", &div_parent
, 1,
125 clkc_data
->mod_clk_en
.reg
= base
+ MESON_SDHC_CLKC
;
126 clkc_data
->mod_clk_en
.bit_idx
= 15;
127 ret
= meson_mx_sdhc_gate_clk_hw_register(dev
, "mod_clk_on",
129 &clkc_data
->mod_clk_en
.hw
,
134 clkc_data
->tx_clk_en
.reg
= base
+ MESON_SDHC_CLKC
;
135 clkc_data
->tx_clk_en
.bit_idx
= 14;
136 ret
= meson_mx_sdhc_gate_clk_hw_register(dev
, "tx_clk_on",
138 &clkc_data
->tx_clk_en
.hw
,
143 clkc_data
->rx_clk_en
.reg
= base
+ MESON_SDHC_CLKC
;
144 clkc_data
->rx_clk_en
.bit_idx
= 13;
145 ret
= meson_mx_sdhc_gate_clk_hw_register(dev
, "rx_clk_on",
147 &clkc_data
->rx_clk_en
.hw
,
152 clkc_data
->sd_clk_en
.reg
= base
+ MESON_SDHC_CLKC
;
153 clkc_data
->sd_clk_en
.bit_idx
= 12;
154 ret
= meson_mx_sdhc_gate_clk_hw_register(dev
, "sd_clk_on",
156 &clkc_data
->sd_clk_en
.hw
,