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