0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/clk.h>
0009 #include <linux/clk-provider.h>
0010 #include <linux/device.h>
0011 #include <linux/platform_device.h>
0012
0013 #include "meson-mx-sdhc.h"
0014
0015 struct meson_mx_sdhc_clkc {
0016 struct clk_mux src_sel;
0017 struct clk_divider div;
0018 struct clk_gate mod_clk_en;
0019 struct clk_gate tx_clk_en;
0020 struct clk_gate rx_clk_en;
0021 struct clk_gate sd_clk_en;
0022 };
0023
0024 static const struct clk_parent_data meson_mx_sdhc_src_sel_parents[4] = {
0025 { .fw_name = "clkin0" },
0026 { .fw_name = "clkin1" },
0027 { .fw_name = "clkin2" },
0028 { .fw_name = "clkin3" },
0029 };
0030
0031 static const struct clk_div_table meson_mx_sdhc_div_table[] = {
0032 { .div = 6, .val = 5, },
0033 { .div = 8, .val = 7, },
0034 { .div = 9, .val = 8, },
0035 { .div = 10, .val = 9, },
0036 { .div = 12, .val = 11, },
0037 { .div = 16, .val = 15, },
0038 { .div = 18, .val = 17, },
0039 { .div = 34, .val = 33, },
0040 { .div = 142, .val = 141, },
0041 { .div = 850, .val = 849, },
0042 { .div = 2126, .val = 2125, },
0043 { .div = 4096, .val = 4095, },
0044 { }
0045 };
0046
0047 static int meson_mx_sdhc_clk_hw_register(struct device *dev,
0048 const char *name_suffix,
0049 const struct clk_parent_data *parents,
0050 unsigned int num_parents,
0051 const struct clk_ops *ops,
0052 struct clk_hw *hw)
0053 {
0054 struct clk_init_data init = { };
0055 char clk_name[32];
0056
0057 snprintf(clk_name, sizeof(clk_name), "%s#%s", dev_name(dev),
0058 name_suffix);
0059
0060 init.name = clk_name;
0061 init.ops = ops;
0062 init.flags = CLK_SET_RATE_PARENT;
0063 init.parent_data = parents;
0064 init.num_parents = num_parents;
0065
0066 hw->init = &init;
0067
0068 return devm_clk_hw_register(dev, hw);
0069 }
0070
0071 static int meson_mx_sdhc_gate_clk_hw_register(struct device *dev,
0072 const char *name_suffix,
0073 struct clk_hw *parent,
0074 struct clk_hw *hw)
0075 {
0076 struct clk_parent_data parent_data = { .hw = parent };
0077
0078 return meson_mx_sdhc_clk_hw_register(dev, name_suffix, &parent_data, 1,
0079 &clk_gate_ops, hw);
0080 }
0081
0082 int meson_mx_sdhc_register_clkc(struct device *dev, void __iomem *base,
0083 struct clk_bulk_data *clk_bulk_data)
0084 {
0085 struct clk_parent_data div_parent = { };
0086 struct meson_mx_sdhc_clkc *clkc_data;
0087 int ret;
0088
0089 clkc_data = devm_kzalloc(dev, sizeof(*clkc_data), GFP_KERNEL);
0090 if (!clkc_data)
0091 return -ENOMEM;
0092
0093 clkc_data->src_sel.reg = base + MESON_SDHC_CLKC;
0094 clkc_data->src_sel.mask = 0x3;
0095 clkc_data->src_sel.shift = 16;
0096 ret = meson_mx_sdhc_clk_hw_register(dev, "src_sel",
0097 meson_mx_sdhc_src_sel_parents, 4,
0098 &clk_mux_ops,
0099 &clkc_data->src_sel.hw);
0100 if (ret)
0101 return ret;
0102
0103 clkc_data->div.reg = base + MESON_SDHC_CLKC;
0104 clkc_data->div.shift = 0;
0105 clkc_data->div.width = 12;
0106 clkc_data->div.table = meson_mx_sdhc_div_table;
0107 div_parent.hw = &clkc_data->src_sel.hw;
0108 ret = meson_mx_sdhc_clk_hw_register(dev, "div", &div_parent, 1,
0109 &clk_divider_ops,
0110 &clkc_data->div.hw);
0111 if (ret)
0112 return ret;
0113
0114 clkc_data->mod_clk_en.reg = base + MESON_SDHC_CLKC;
0115 clkc_data->mod_clk_en.bit_idx = 15;
0116 ret = meson_mx_sdhc_gate_clk_hw_register(dev, "mod_clk_on",
0117 &clkc_data->div.hw,
0118 &clkc_data->mod_clk_en.hw);
0119 if (ret)
0120 return ret;
0121
0122 clkc_data->tx_clk_en.reg = base + MESON_SDHC_CLKC;
0123 clkc_data->tx_clk_en.bit_idx = 14;
0124 ret = meson_mx_sdhc_gate_clk_hw_register(dev, "tx_clk_on",
0125 &clkc_data->div.hw,
0126 &clkc_data->tx_clk_en.hw);
0127 if (ret)
0128 return ret;
0129
0130 clkc_data->rx_clk_en.reg = base + MESON_SDHC_CLKC;
0131 clkc_data->rx_clk_en.bit_idx = 13;
0132 ret = meson_mx_sdhc_gate_clk_hw_register(dev, "rx_clk_on",
0133 &clkc_data->div.hw,
0134 &clkc_data->rx_clk_en.hw);
0135 if (ret)
0136 return ret;
0137
0138 clkc_data->sd_clk_en.reg = base + MESON_SDHC_CLKC;
0139 clkc_data->sd_clk_en.bit_idx = 12;
0140 ret = meson_mx_sdhc_gate_clk_hw_register(dev, "sd_clk_on",
0141 &clkc_data->div.hw,
0142 &clkc_data->sd_clk_en.hw);
0143 if (ret)
0144 return ret;
0145
0146
0147
0148
0149
0150 clk_bulk_data[0].clk = clkc_data->mod_clk_en.hw.clk;
0151 clk_bulk_data[1].clk = clkc_data->sd_clk_en.hw.clk;
0152 clk_bulk_data[2].clk = clkc_data->tx_clk_en.hw.clk;
0153 clk_bulk_data[3].clk = clkc_data->rx_clk_en.hw.clk;
0154
0155 return 0;
0156 }