123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Amlogic Meson SDHC clock controller
- *
- * Copyright (C) 2020 Martin Blumenstingl <[email protected]>
- */
- #include <linux/clk.h>
- #include <linux/clk-provider.h>
- #include <linux/device.h>
- #include <linux/platform_device.h>
- #include "meson-mx-sdhc.h"
- struct meson_mx_sdhc_clkc {
- struct clk_mux src_sel;
- struct clk_divider div;
- struct clk_gate mod_clk_en;
- struct clk_gate tx_clk_en;
- struct clk_gate rx_clk_en;
- struct clk_gate sd_clk_en;
- };
- static const struct clk_parent_data meson_mx_sdhc_src_sel_parents[4] = {
- { .fw_name = "clkin0" },
- { .fw_name = "clkin1" },
- { .fw_name = "clkin2" },
- { .fw_name = "clkin3" },
- };
- static const struct clk_div_table meson_mx_sdhc_div_table[] = {
- { .div = 6, .val = 5, },
- { .div = 8, .val = 7, },
- { .div = 9, .val = 8, },
- { .div = 10, .val = 9, },
- { .div = 12, .val = 11, },
- { .div = 16, .val = 15, },
- { .div = 18, .val = 17, },
- { .div = 34, .val = 33, },
- { .div = 142, .val = 141, },
- { .div = 850, .val = 849, },
- { .div = 2126, .val = 2125, },
- { .div = 4096, .val = 4095, },
- { /* sentinel */ }
- };
- static int meson_mx_sdhc_clk_hw_register(struct device *dev,
- const char *name_suffix,
- const struct clk_parent_data *parents,
- unsigned int num_parents,
- const struct clk_ops *ops,
- struct clk_hw *hw)
- {
- struct clk_init_data init = { };
- char clk_name[32];
- snprintf(clk_name, sizeof(clk_name), "%s#%s", dev_name(dev),
- name_suffix);
- init.name = clk_name;
- init.ops = ops;
- init.flags = CLK_SET_RATE_PARENT;
- init.parent_data = parents;
- init.num_parents = num_parents;
- hw->init = &init;
- return devm_clk_hw_register(dev, hw);
- }
- static int meson_mx_sdhc_gate_clk_hw_register(struct device *dev,
- const char *name_suffix,
- struct clk_hw *parent,
- struct clk_hw *hw)
- {
- struct clk_parent_data parent_data = { .hw = parent };
- return meson_mx_sdhc_clk_hw_register(dev, name_suffix, &parent_data, 1,
- &clk_gate_ops, hw);
- }
- int meson_mx_sdhc_register_clkc(struct device *dev, void __iomem *base,
- struct clk_bulk_data *clk_bulk_data)
- {
- struct clk_parent_data div_parent = { };
- struct meson_mx_sdhc_clkc *clkc_data;
- int ret;
- clkc_data = devm_kzalloc(dev, sizeof(*clkc_data), GFP_KERNEL);
- if (!clkc_data)
- return -ENOMEM;
- clkc_data->src_sel.reg = base + MESON_SDHC_CLKC;
- clkc_data->src_sel.mask = 0x3;
- clkc_data->src_sel.shift = 16;
- ret = meson_mx_sdhc_clk_hw_register(dev, "src_sel",
- meson_mx_sdhc_src_sel_parents, 4,
- &clk_mux_ops,
- &clkc_data->src_sel.hw);
- if (ret)
- return ret;
- clkc_data->div.reg = base + MESON_SDHC_CLKC;
- clkc_data->div.shift = 0;
- clkc_data->div.width = 12;
- clkc_data->div.table = meson_mx_sdhc_div_table;
- div_parent.hw = &clkc_data->src_sel.hw;
- ret = meson_mx_sdhc_clk_hw_register(dev, "div", &div_parent, 1,
- &clk_divider_ops,
- &clkc_data->div.hw);
- if (ret)
- return ret;
- clkc_data->mod_clk_en.reg = base + MESON_SDHC_CLKC;
- clkc_data->mod_clk_en.bit_idx = 15;
- ret = meson_mx_sdhc_gate_clk_hw_register(dev, "mod_clk_on",
- &clkc_data->div.hw,
- &clkc_data->mod_clk_en.hw);
- if (ret)
- return ret;
- clkc_data->tx_clk_en.reg = base + MESON_SDHC_CLKC;
- clkc_data->tx_clk_en.bit_idx = 14;
- ret = meson_mx_sdhc_gate_clk_hw_register(dev, "tx_clk_on",
- &clkc_data->div.hw,
- &clkc_data->tx_clk_en.hw);
- if (ret)
- return ret;
- clkc_data->rx_clk_en.reg = base + MESON_SDHC_CLKC;
- clkc_data->rx_clk_en.bit_idx = 13;
- ret = meson_mx_sdhc_gate_clk_hw_register(dev, "rx_clk_on",
- &clkc_data->div.hw,
- &clkc_data->rx_clk_en.hw);
- if (ret)
- return ret;
- clkc_data->sd_clk_en.reg = base + MESON_SDHC_CLKC;
- clkc_data->sd_clk_en.bit_idx = 12;
- ret = meson_mx_sdhc_gate_clk_hw_register(dev, "sd_clk_on",
- &clkc_data->div.hw,
- &clkc_data->sd_clk_en.hw);
- if (ret)
- return ret;
- /*
- * TODO: Replace clk_hw.clk with devm_clk_hw_get_clk() once that is
- * available.
- */
- clk_bulk_data[0].clk = clkc_data->mod_clk_en.hw.clk;
- clk_bulk_data[1].clk = clkc_data->sd_clk_en.hw.clk;
- clk_bulk_data[2].clk = clkc_data->tx_clk_en.hw.clk;
- clk_bulk_data[3].clk = clkc_data->rx_clk_en.hw.clk;
- return 0;
- }
|