mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-14 15:54:15 +08:00
4c5e3efae6
Now that devm_clk_hw_get_clk() has been available for a while we can resolve an older TODO where this API did not exist yet. No functional changes intended. Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Link: https://lore.kernel.org/r/20240217100200.1494980-2-martin.blumenstingl@googlemail.com Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
160 lines
4.2 KiB
C
160 lines
4.2 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Amlogic Meson SDHC clock controller
|
|
*
|
|
* Copyright (C) 2020 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
|
|
*/
|
|
|
|
#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_bulk_data *clk_bulk_data,
|
|
u8 bulk_index)
|
|
{
|
|
struct clk_parent_data parent_data = { .hw = parent };
|
|
int ret;
|
|
|
|
ret = meson_mx_sdhc_clk_hw_register(dev, name_suffix, &parent_data, 1,
|
|
&clk_gate_ops, hw);
|
|
if (ret)
|
|
return ret;
|
|
|
|
clk_bulk_data[bulk_index].clk = devm_clk_hw_get_clk(dev, hw, name_suffix);
|
|
if (IS_ERR(clk_bulk_data[bulk_index].clk))
|
|
return PTR_ERR(clk_bulk_data[bulk_index].clk);
|
|
|
|
return 0;
|
|
}
|
|
|
|
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,
|
|
clk_bulk_data, 0);
|
|
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,
|
|
clk_bulk_data, 1);
|
|
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,
|
|
clk_bulk_data, 2);
|
|
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,
|
|
clk_bulk_data, 3);
|
|
return ret;
|
|
}
|