Linux Audio

Check our new training course

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