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 88 return PTR_ERR_OR_ZERO(clk_bulk_data[bulk_index].clk); 89 } 90 91 int meson_mx_sdhc_register_clkc(struct device *dev, void __iomem *base, 92 struct clk_bulk_data *clk_bulk_data) 93 { 94 struct clk_parent_data div_parent = { }; 95 struct meson_mx_sdhc_clkc *clkc_data; 96 int ret; 97 98 clkc_data = devm_kzalloc(dev, sizeof(*clkc_data), GFP_KERNEL); 99 if (!clkc_data) 100 return -ENOMEM; 101 102 clkc_data->src_sel.reg = base + MESON_SDHC_CLKC; 103 clkc_data->src_sel.mask = 0x3; 104 clkc_data->src_sel.shift = 16; 105 ret = meson_mx_sdhc_clk_hw_register(dev, "src_sel", 106 meson_mx_sdhc_src_sel_parents, 4, 107 &clk_mux_ops, 108 &clkc_data->src_sel.hw); 109 if (ret) 110 return ret; 111 112 clkc_data->div.reg = base + MESON_SDHC_CLKC; 113 clkc_data->div.shift = 0; 114 clkc_data->div.width = 12; 115 clkc_data->div.table = meson_mx_sdhc_div_table; 116 div_parent.hw = &clkc_data->src_sel.hw; 117 ret = meson_mx_sdhc_clk_hw_register(dev, "div", &div_parent, 1, 118 &clk_divider_ops, 119 &clkc_data->div.hw); 120 if (ret) 121 return ret; 122 123 clkc_data->mod_clk_en.reg = base + MESON_SDHC_CLKC; 124 clkc_data->mod_clk_en.bit_idx = 15; 125 ret = meson_mx_sdhc_gate_clk_hw_register(dev, "mod_clk_on", 126 &clkc_data->div.hw, 127 &clkc_data->mod_clk_en.hw, 128 clk_bulk_data, 0); 129 if (ret) 130 return ret; 131 132 clkc_data->tx_clk_en.reg = base + MESON_SDHC_CLKC; 133 clkc_data->tx_clk_en.bit_idx = 14; 134 ret = meson_mx_sdhc_gate_clk_hw_register(dev, "tx_clk_on", 135 &clkc_data->div.hw, 136 &clkc_data->tx_clk_en.hw, 137 clk_bulk_data, 1); 138 if (ret) 139 return ret; 140 141 clkc_data->rx_clk_en.reg = base + MESON_SDHC_CLKC; 142 clkc_data->rx_clk_en.bit_idx = 13; 143 ret = meson_mx_sdhc_gate_clk_hw_register(dev, "rx_clk_on", 144 &clkc_data->div.hw, 145 &clkc_data->rx_clk_en.hw, 146 clk_bulk_data, 2); 147 if (ret) 148 return ret; 149 150 clkc_data->sd_clk_en.reg = base + MESON_SDHC_CLKC; 151 clkc_data->sd_clk_en.bit_idx = 12; 152 ret = meson_mx_sdhc_gate_clk_hw_register(dev, "sd_clk_on", 153 &clkc_data->div.hw, 154 &clkc_data->sd_clk_en.hw, 155 clk_bulk_data, 3); 156 return ret; 157 } 158