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 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; 22 }; 23 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" }, 29 }; 30 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, }, 44 { /* sentinel */ } 45 }; 46 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, 52 struct clk_hw *hw) 53 { 54 struct clk_init_data init = { }; 55 char clk_name[32]; 56 57 snprintf(clk_name, sizeof(clk_name), "%s#%s", dev_name(dev), 58 name_suffix); 59 60 init.name = clk_name; 61 init.ops = ops; 62 init.flags = CLK_SET_RATE_PARENT; 63 init.parent_data = parents; 64 init.num_parents = num_parents; 65 66 hw->init = &init; 67 68 return devm_clk_hw_register(dev, hw); 69 } 70 71 static int meson_mx_sdhc_gate_clk_hw_register(struct device *dev, 72 const char *name_suffix, 73 struct clk_hw *parent, 74 struct clk_hw *hw, 75 struct clk_bulk_data *clk_bulk_data, 76 u8 bulk_index) 77 { 78 struct clk_parent_data parent_data = { .hw = parent }; 79 int ret; 80 81 ret = meson_mx_sdhc_clk_hw_register(dev, name_suffix, &parent_data, 1, 82 &clk_gate_ops, hw); 83 if (ret) 84 return ret; 85 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); 89 90 return 0; 91 } 92 93 int meson_mx_sdhc_register_clkc(struct device *dev, void __iomem *base, 94 struct clk_bulk_data *clk_bulk_data) 95 { 96 struct clk_parent_data div_parent = { }; 97 struct meson_mx_sdhc_clkc *clkc_data; 98 int ret; 99 100 clkc_data = devm_kzalloc(dev, sizeof(*clkc_data), GFP_KERNEL); 101 if (!clkc_data) 102 return -ENOMEM; 103 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, 109 &clk_mux_ops, 110 &clkc_data->src_sel.hw); 111 if (ret) 112 return ret; 113 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, 120 &clk_divider_ops, 121 &clkc_data->div.hw); 122 if (ret) 123 return ret; 124 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", 128 &clkc_data->div.hw, 129 &clkc_data->mod_clk_en.hw, 130 clk_bulk_data, 0); 131 if (ret) 132 return ret; 133 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", 137 &clkc_data->div.hw, 138 &clkc_data->tx_clk_en.hw, 139 clk_bulk_data, 1); 140 if (ret) 141 return ret; 142 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", 146 &clkc_data->div.hw, 147 &clkc_data->rx_clk_en.hw, 148 clk_bulk_data, 2); 149 if (ret) 150 return ret; 151 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", 155 &clkc_data->div.hw, 156 &clkc_data->sd_clk_en.hw, 157 clk_bulk_data, 3); 158 return ret; 159 } 160