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 } 159