cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

meson-mx-sdhc-clkc.c (4219B)


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