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