This driver will add a MMC clock controller driver support.
The original idea about adding a clock controller is during the
discussion in the NAND driver mainline effort[1].
This driver is tested in the S400 board (AXG platform) with NAND driver.
Changes since v7 [8]
- move meson_clk_get_phase_delay_data() from header to driver
- CONFIG sclk-div with COMMON_CLK_AMLOGIC instead of COMMON_CLK_AMLOGIC_AUDIO
- remove onecell date and ID for internal MUX clk
- use helper for functions for ONE_BASED in sclk-div
- add ONE_BASED support for duty cycle
Changes since v6 [7]:
- add one based support for sclk divier
- alloc sclk in probe for multiple instance
- fix coding styles
Changes since v5 [6]:
- remove divider ops with .init and use sclk_div instead
- drop CLK_DIVIDER_ROUND_CLOSEST in mux and div
- drop the useless type cast
Changes since v4 [5]:
- use struct parm in phase delay driver
- remove 0 delay releted part in phase delay driver
- don't rebuild the parent name once again
- add divider ops with .init
Changes since v3 [4]:
- separate clk-phase-delay driver
- replace clk_get_rate() with clk_hw_get_rate()
- collect Rob's R-Y
- drop 'meson-' prefix from compatible string
Changes since v2 [3]:
- squash dt-binding clock-id patch
- update license
- fix alignment
- construct a clk register helper() function
Changes since v1 [2]:
- implement phase clock
- update compatible name
- adjust file name
- divider probe() into small functions, and re-use them
[1] https://lkml.kernel.org/r/20180628090034.0637a062@xps13
[2] https://lkml.kernel.org/r/[email protected]
[3] https://lkml.kernel.org/r/[email protected]
[4] https://lkml.kernel.org/r/[email protected]
[5] https://lkml.kernel.org/r/[email protected]
[6] https://lkml.kernel.org/r/[email protected]
[7] https://lkml.kernel.org/r/[email protected]
[8] https://lkml.kernel.org/r/[email protected]
Jianxin Pan (1):
clk: meson: add one based divider support for sclk divider
Yixun Lan (3):
clk: meson: add emmc sub clock phase delay driver
clk: meson: add DT documentation for emmc clock controller
clk: meson: add sub MMC clock controller driver
.../devicetree/bindings/clock/amlogic,mmc-clkc.txt | 39 +++
drivers/clk/meson/Kconfig | 9 +
drivers/clk/meson/Makefile | 5 +-
drivers/clk/meson/clk-phase-delay.c | 70 +++++
drivers/clk/meson/clkc-audio.h | 8 -
drivers/clk/meson/clkc.h | 17 +-
drivers/clk/meson/mmc-clkc.c | 304 +++++++++++++++++++++
drivers/clk/meson/sclk-div.c | 59 ++--
include/dt-bindings/clock/amlogic,mmc-clkc.h | 17 ++
9 files changed, 498 insertions(+), 30 deletions(-)
create mode 100644 Documentation/devicetree/bindings/clock/amlogic,mmc-clkc.txt
create mode 100644 drivers/clk/meson/clk-phase-delay.c
create mode 100644 drivers/clk/meson/mmc-clkc.c
create mode 100644 include/dt-bindings/clock/amlogic,mmc-clkc.h
--
1.9.1
From: Yixun Lan <[email protected]>
Document the MMC sub clock controller driver, the potential consumer
of this driver is MMC or NAND. Also add four clock bindings IDs which
provided by this driver.
Reviewed-by: Rob Herring <[email protected]>
Signed-off-by: Yixun Lan <[email protected]>
Signed-off-by: Jianxin Pan <[email protected]>
---
.../devicetree/bindings/clock/amlogic,mmc-clkc.txt | 39 ++++++++++++++++++++++
include/dt-bindings/clock/amlogic,mmc-clkc.h | 17 ++++++++++
2 files changed, 56 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/amlogic,mmc-clkc.txt
create mode 100644 include/dt-bindings/clock/amlogic,mmc-clkc.h
diff --git a/Documentation/devicetree/bindings/clock/amlogic,mmc-clkc.txt b/Documentation/devicetree/bindings/clock/amlogic,mmc-clkc.txt
new file mode 100644
index 0000000..0f518e6
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/amlogic,mmc-clkc.txt
@@ -0,0 +1,39 @@
+* Amlogic MMC Sub Clock Controller Driver
+
+The Amlogic MMC clock controller generates and supplies clock to support
+MMC and NAND controller
+
+Required Properties:
+
+- compatible: should be:
+ "amlogic,gx-mmc-clkc"
+ "amlogic,axg-mmc-clkc"
+
+- #clock-cells: should be 1.
+- clocks: phandles to clocks corresponding to the clock-names property
+- clock-names: list of parent clock names
+ - "clkin0", "clkin1"
+
+- reg: address of emmc sub clock register
+
+Example: Clock controller node:
+
+sd_mmc_c_clkc: clock-controller@7000 {
+ compatible = "amlogic,axg-mmc-clkc", "syscon";
+ reg = <0x0 0x7000 0x0 0x4>;
+ #clock-cells = <1>;
+
+ clock-names = "clkin0", "clkin1";
+ clocks = <&clkc CLKID_SD_MMC_C_CLK0>,
+ <&clkc CLKID_FCLK_DIV2>;
+};
+
+sd_emmc_b_clkc: clock-controller@5000 {
+ compatible = "amlogic,axg-mmc-clkc", "syscon";
+ reg = <0x0 0x5000 0x0 0x4>;
+
+ #clock-cells = <1>;
+ clock-names = "clkin0", "clkin1";
+ clocks = <&clkc CLKID_SD_EMMC_B_CLK0>,
+ <&clkc CLKID_FCLK_DIV2>;
+};
diff --git a/include/dt-bindings/clock/amlogic,mmc-clkc.h b/include/dt-bindings/clock/amlogic,mmc-clkc.h
new file mode 100644
index 0000000..34a3c56
--- /dev/null
+++ b/include/dt-bindings/clock/amlogic,mmc-clkc.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
+/*
+ * Meson MMC sub clock tree IDs
+ *
+ * Copyright (c) 2018 Amlogic, Inc. All rights reserved.
+ * Author: Yixun Lan <[email protected]>
+ */
+
+#ifndef __MMC_CLKC_H
+#define __MMC_CLKC_H
+
+#define CLKID_MMC_DIV 0
+#define CLKID_MMC_PHASE_CORE 1
+#define CLKID_MMC_PHASE_TX 2
+#define CLKID_MMC_PHASE_RX 3
+
+#endif
--
1.9.1
From: Yixun Lan <[email protected]>
Export the emmc sub clock phase delay ops which will be used
by the emmc sub clock driver itself.
Signed-off-by: Yixun Lan <[email protected]>
Signed-off-by: Jianxin Pan <[email protected]>
---
drivers/clk/meson/Makefile | 1 +
drivers/clk/meson/clk-phase-delay.c | 70 +++++++++++++++++++++++++++++++++++++
drivers/clk/meson/clkc.h | 7 ++++
3 files changed, 78 insertions(+)
create mode 100644 drivers/clk/meson/clk-phase-delay.c
diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
index acd8694..d59620d 100644
--- a/drivers/clk/meson/Makefile
+++ b/drivers/clk/meson/Makefile
@@ -3,6 +3,7 @@
#
obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-phase.o vid-pll-div.o
+obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-phase-delay.o
obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-input.o
obj-$(CONFIG_COMMON_CLK_AMLOGIC) += sclk-div.o
obj-$(CONFIG_COMMON_CLK_AMLOGIC_AUDIO) += clk-triphase.o
diff --git a/drivers/clk/meson/clk-phase-delay.c b/drivers/clk/meson/clk-phase-delay.c
new file mode 100644
index 0000000..88004d2
--- /dev/null
+++ b/drivers/clk/meson/clk-phase-delay.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Amlogic Meson MMC Sub Clock Controller Driver
+ *
+ * Copyright (c) 2017 Baylibre SAS.
+ * Author: Jerome Brunet <[email protected]>
+ *
+ * Copyright (c) 2018 Amlogic, inc.
+ * Author: Yixun Lan <[email protected]>
+ * Author: Jianxin Pan <[email protected]>
+ */
+
+#include <linux/clk-provider.h>
+#include "clkc.h"
+
+static inline struct meson_clk_phase_delay_data *
+meson_clk_get_phase_delay_data(struct clk_regmap *clk)
+{
+ return clk->data;
+}
+
+static int meson_clk_phase_delay_get_phase(struct clk_hw *hw)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_phase_delay_data *ph;
+ unsigned long period_ps, p, d;
+ int degrees;
+
+ ph = meson_clk_get_phase_delay_data(clk);
+ p = meson_parm_read(clk->map, &ph->phase);
+ degrees = p * 360 / (1 << (ph->phase.width));
+
+ period_ps = DIV_ROUND_UP(NSEC_PER_SEC * 1000,
+ clk_hw_get_rate(hw));
+
+ d = meson_parm_read(clk->map, &ph->delay);
+ degrees += d * ph->delay_step_ps * 360 / period_ps;
+ degrees %= 360;
+
+ return degrees;
+}
+
+static int meson_clk_phase_delay_set_phase(struct clk_hw *hw, int degrees)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_phase_delay_data *ph;
+ unsigned long period_ps, d = 0, r;
+
+ ph = meson_clk_get_phase_delay_data(clk);
+ period_ps = DIV_ROUND_UP(NSEC_PER_SEC * 1000, clk_hw_get_rate(hw));
+
+ /*
+ * First compute the phase index (p), the remainder (r) is the
+ * part we'll try to acheive using the delays (d).
+ */
+ r = do_div(degrees, 360 / 1 << (ph->phase.width));
+ d = DIV_ROUND_CLOSEST(r * period_ps,
+ 360 * ph->delay_step_ps);
+ d = min(d, PMASK(ph->delay.width));
+
+ meson_parm_write(clk->map, &ph->phase, degrees);
+ meson_parm_write(clk->map, &ph->delay, d);
+ return 0;
+}
+
+const struct clk_ops meson_clk_phase_delay_ops = {
+ .get_phase = meson_clk_phase_delay_get_phase,
+ .set_phase = meson_clk_phase_delay_set_phase,
+};
+EXPORT_SYMBOL_GPL(meson_clk_phase_delay_ops);
diff --git a/drivers/clk/meson/clkc.h b/drivers/clk/meson/clkc.h
index 00b3320..e6f0905 100644
--- a/drivers/clk/meson/clkc.h
+++ b/drivers/clk/meson/clkc.h
@@ -118,6 +118,12 @@ struct clk_regmap _name = { \
}, \
};
+struct meson_clk_phase_delay_data {
+ struct parm phase;
+ struct parm delay;
+ unsigned int delay_step_ps;
+};
+
/* clk_ops */
extern const struct clk_ops meson_clk_pll_ro_ops;
extern const struct clk_ops meson_clk_pll_ops;
@@ -127,6 +133,7 @@ struct clk_regmap _name = { \
extern const struct clk_ops meson_clk_phase_ops;
extern const struct clk_ops meson_vid_pll_div_ro_ops;
extern const struct clk_ops meson_sclk_div_ops;
+extern const struct clk_ops meson_clk_phase_delay_ops;
struct clk_hw *meson_clk_hw_register_input(struct device *dev,
const char *of_name,
--
1.9.1
From: Yixun Lan <[email protected]>
The patch will add a MMC clock controller driver which used by MMC or NAND,
It provide a mux and divider clock, and three phase clocks - core, tx, tx.
Two clocks are provided as the parent of MMC clock controller from
upper layer clock controller - eg "amlogic,axg-clkc" in AXG platform.
To specify which clock the MMC or NAND driver may consume,
the preprocessor macros in the dt-bindings/clock/amlogic,mmc-clkc.h header
can be used in the device tree sources.
Signed-off-by: Yixun Lan <[email protected]>
Signed-off-by: Jianxin Pan <[email protected]>
---
drivers/clk/meson/Kconfig | 9 ++
drivers/clk/meson/Makefile | 1 +
drivers/clk/meson/mmc-clkc.c | 304 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 314 insertions(+)
create mode 100644 drivers/clk/meson/mmc-clkc.c
diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
index efaa70f..c84b0f7 100644
--- a/drivers/clk/meson/Kconfig
+++ b/drivers/clk/meson/Kconfig
@@ -15,6 +15,15 @@ config COMMON_CLK_MESON_AO
select COMMON_CLK_REGMAP_MESON
select RESET_CONTROLLER
+config COMMON_CLK_MMC_MESON
+ tristate "Meson MMC Sub Clock Controller Driver"
+ select MFD_SYSCON
+ select COMMON_CLK_AMLOGIC
+ help
+ Support for the MMC sub clock controller on Amlogic Meson Platform,
+ which include S905 (GXBB, GXL), A113D/X (AXG) devices.
+ Say Y if you want this clock enabled.
+
config COMMON_CLK_REGMAP_MESON
bool
select REGMAP
diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
index d59620d..54416a2 100644
--- a/drivers/clk/meson/Makefile
+++ b/drivers/clk/meson/Makefile
@@ -12,4 +12,5 @@ obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o
obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-32k.o
obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o
obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o
+obj-$(CONFIG_COMMON_CLK_MMC_MESON) += mmc-clkc.o
obj-$(CONFIG_COMMON_CLK_REGMAP_MESON) += clk-regmap.o
diff --git a/drivers/clk/meson/mmc-clkc.c b/drivers/clk/meson/mmc-clkc.c
new file mode 100644
index 0000000..2582a98
--- /dev/null
+++ b/drivers/clk/meson/mmc-clkc.c
@@ -0,0 +1,304 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Amlogic Meson MMC Sub Clock Controller Driver
+ *
+ * Copyright (c) 2017 Baylibre SAS.
+ * Author: Jerome Brunet <[email protected]>
+ *
+ * Copyright (c) 2018 Amlogic, inc.
+ * Author: Yixun Lan <[email protected]>
+ * Author: Jianxin Pan <[email protected]>
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/platform_device.h>
+#include <dt-bindings/clock/amlogic,mmc-clkc.h>
+
+#include "clkc.h"
+
+/* clock ID used by internal driver */
+
+#define SD_EMMC_CLOCK 0
+#define CLK_DELAY_STEP_PS 200
+#define MUX_CLK_NUM_PARENTS 2
+#define MMC_MAX_CLKS 4
+
+struct mmc_clkc_data {
+ struct meson_clk_phase_delay_data tx;
+ struct meson_clk_phase_delay_data rx;
+};
+
+static struct clk_regmap_mux_data mmc_clkc_mux_data = {
+ .offset = SD_EMMC_CLOCK,
+ .mask = 0x3,
+ .shift = 6,
+};
+
+static const struct meson_sclk_div_data mmc_clkc_div_data = {
+ .div = {
+ .reg_off = SD_EMMC_CLOCK,
+ .width = 6,
+ },
+ .flags = CLK_DIVIDER_ONE_BASED,
+};
+
+static struct meson_clk_phase_data mmc_clkc_core_phase = {
+ .ph = {
+ .reg_off = SD_EMMC_CLOCK,
+ .shift = 8,
+ .width = 2,
+ }
+};
+
+static const struct mmc_clkc_data mmc_clkc_gx_data = {
+ .tx = {
+ .phase = {
+ .reg_off = SD_EMMC_CLOCK,
+ .shift = 10,
+ .width = 2,
+ },
+ .delay = {
+ .reg_off = SD_EMMC_CLOCK,
+ .shift = 16,
+ .width = 4,
+ },
+ .delay_step_ps = CLK_DELAY_STEP_PS,
+ },
+ .rx = {
+ .phase = {
+ .reg_off = SD_EMMC_CLOCK,
+ .shift = 12,
+ .width = 2,
+ },
+ .delay = {
+ .reg_off = SD_EMMC_CLOCK,
+ .shift = 20,
+ .width = 4,
+ },
+ .delay_step_ps = CLK_DELAY_STEP_PS,
+ },
+};
+
+static const struct mmc_clkc_data mmc_clkc_axg_data = {
+ .tx = {
+ .phase = {
+ .reg_off = SD_EMMC_CLOCK,
+ .shift = 10,
+ .width = 2,
+ },
+ .delay = {
+ .reg_off = SD_EMMC_CLOCK,
+ .shift = 16,
+ .width = 6,
+ },
+ .delay_step_ps = CLK_DELAY_STEP_PS,
+ },
+ .rx = {
+ .phase = {
+ .reg_off = SD_EMMC_CLOCK,
+ .shift = 12,
+ .width = 2,
+ },
+ .delay = {
+ .reg_off = SD_EMMC_CLOCK,
+ .shift = 22,
+ .width = 6,
+ },
+ .delay_step_ps = CLK_DELAY_STEP_PS,
+ },
+};
+
+static const struct of_device_id mmc_clkc_match_table[] = {
+ {
+ .compatible = "amlogic,gx-mmc-clkc",
+ .data = &mmc_clkc_gx_data
+ },
+ {
+ .compatible = "amlogic,axg-mmc-clkc",
+ .data = &mmc_clkc_axg_data
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, mmc_clkc_match_table);
+
+static struct clk_regmap *
+mmc_clkc_register_clk(struct device *dev, struct regmap *map,
+ struct clk_init_data *init,
+ const char *suffix, void *data)
+{
+ struct clk_regmap *clk;
+ char *name;
+ int ret;
+
+ clk = devm_kzalloc(dev, sizeof(*clk), GFP_KERNEL);
+ if (!clk)
+ return ERR_PTR(-ENOMEM);
+
+ name = kasprintf(GFP_KERNEL, "%s#%s", dev_name(dev), suffix);
+ if (!name)
+ return ERR_PTR(-ENOMEM);
+
+ init->name = name;
+ clk->map = map;
+ clk->data = data;
+ clk->hw.init = init;
+ ret = devm_clk_hw_register(dev, &clk->hw);
+ if (ret)
+ clk = ERR_PTR(ret);
+
+ kfree(name);
+ return clk;
+}
+
+static struct clk_regmap *mmc_clkc_register_mux(struct device *dev,
+ struct regmap *map)
+{
+ const char *parent_names[MUX_CLK_NUM_PARENTS];
+ struct clk_init_data init;
+ struct clk_regmap *mux;
+ struct clk *clk;
+ int i;
+
+ for (i = 0; i < MUX_CLK_NUM_PARENTS; i++) {
+ char name[8];
+
+ snprintf(name, sizeof(name), "clkin%d", i);
+ clk = devm_clk_get(dev, name);
+ if (IS_ERR(clk)) {
+ if (clk != ERR_PTR(-EPROBE_DEFER))
+ dev_err(dev, "Missing clock %s\n", name);
+ return ERR_CAST(clk);
+ }
+
+ parent_names[i] = __clk_get_name(clk);
+ }
+
+ init.ops = &clk_regmap_mux_ops;
+ init.flags = CLK_SET_RATE_PARENT;
+ init.parent_names = parent_names;
+ init.num_parents = MUX_CLK_NUM_PARENTS;
+
+ mux = mmc_clkc_register_clk(dev, map, &init, "mux", &mmc_clkc_mux_data);
+ if (IS_ERR(mux))
+ dev_err(dev, "Mux clock registration failed\n");
+
+ return mux;
+}
+
+static struct clk_regmap *
+mmc_clkc_register_clk_with_parent(struct device *dev, struct regmap *map,
+ char *suffix, const struct clk_hw *hw,
+ unsigned long flags,
+ const struct clk_ops *ops, void *data)
+{
+ struct clk_init_data init;
+ struct clk_regmap *clk;
+ const char *parent_name = clk_hw_get_name(hw);
+
+ init.ops = ops;
+ init.flags = flags;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ clk = mmc_clkc_register_clk(dev, map, &init, suffix, data);
+ if (IS_ERR(clk))
+ dev_err(dev, "%s clock registration failed\n", suffix);
+
+ return clk;
+}
+
+static int mmc_clkc_probe(struct platform_device *pdev)
+{
+ struct clk_hw_onecell_data *onecell_data;
+ struct device *dev = &pdev->dev;
+ struct mmc_clkc_data *data;
+ struct regmap *map;
+ struct clk_regmap *clk, *core;
+ struct meson_sclk_div_data *div_data;
+
+ /*cast to drop the const in match->data*/
+ data = (struct mmc_clkc_data *)of_device_get_match_data(dev);
+ if (!data)
+ return -ENODEV;
+
+ map = syscon_node_to_regmap(dev->of_node);
+ if (IS_ERR(map)) {
+ dev_err(dev, "could not find mmc clock controller\n");
+ return PTR_ERR(map);
+ }
+
+ onecell_data = devm_kzalloc(dev, sizeof(*onecell_data) +
+ sizeof(*onecell_data->hws) * MMC_MAX_CLKS,
+ GFP_KERNEL);
+ if (!onecell_data)
+ return -ENOMEM;
+
+ clk = mmc_clkc_register_mux(dev, map);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ div_data = devm_kzalloc(dev, sizeof(*div_data), GFP_KERNEL);
+ if (!div_data)
+ return -ENOMEM;
+
+ memcpy(div_data, &mmc_clkc_div_data, sizeof(*div_data));
+ clk = mmc_clkc_register_clk_with_parent(dev, map, "div",
+ &clk->hw,
+ CLK_SET_RATE_PARENT,
+ &meson_sclk_div_ops,
+ div_data);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ onecell_data->hws[CLKID_MMC_DIV] = &clk->hw,
+
+ core = mmc_clkc_register_clk_with_parent(dev, map, "core",
+ &clk->hw,
+ CLK_SET_RATE_PARENT,
+ &meson_clk_phase_ops,
+ &mmc_clkc_core_phase);
+ if (IS_ERR(core))
+ return PTR_ERR(core);
+
+ onecell_data->hws[CLKID_MMC_PHASE_CORE] = &core->hw,
+
+ clk = mmc_clkc_register_clk_with_parent(dev, map, "rx",
+ &core->hw, 0,
+ &meson_clk_phase_delay_ops,
+ &data->rx);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ onecell_data->hws[CLKID_MMC_PHASE_RX] = &clk->hw,
+ clk = mmc_clkc_register_clk_with_parent(dev, map, "tx",
+ &core->hw, 0,
+ &meson_clk_phase_delay_ops,
+ &data->tx);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ onecell_data->hws[CLKID_MMC_PHASE_TX] = &clk->hw,
+ onecell_data->num = MMC_MAX_CLKS;
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
+ onecell_data);
+}
+
+static struct platform_driver mmc_clkc_driver = {
+ .probe = mmc_clkc_probe,
+ .driver = {
+ .name = "meson-mmc-clkc",
+ .of_match_table = of_match_ptr(mmc_clkc_match_table),
+ },
+};
+
+module_platform_driver(mmc_clkc_driver);
+
+MODULE_DESCRIPTION("Amlogic AXG MMC clock driver");
+MODULE_AUTHOR("Jianxin Pan <[email protected]>");
+MODULE_LICENSE("GPL v2");
--
1.9.1
When CLK_DIVIDER_ONE_BASED flag is set, the sclk divider will be:
one based divider (div = val), and zero value gates the clock
Signed-off-by: Jianxin Pan <[email protected]>
---
drivers/clk/meson/Makefile | 3 ++-
drivers/clk/meson/clkc-audio.h | 8 ------
drivers/clk/meson/clkc.h | 10 ++++++-
drivers/clk/meson/sclk-div.c | 59 ++++++++++++++++++++++++++++--------------
4 files changed, 50 insertions(+), 30 deletions(-)
diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
index a849aa8..acd8694 100644
--- a/drivers/clk/meson/Makefile
+++ b/drivers/clk/meson/Makefile
@@ -4,7 +4,8 @@
obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-phase.o vid-pll-div.o
obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-input.o
-obj-$(CONFIG_COMMON_CLK_AMLOGIC_AUDIO) += clk-triphase.o sclk-div.o
+obj-$(CONFIG_COMMON_CLK_AMLOGIC) += sclk-div.o
+obj-$(CONFIG_COMMON_CLK_AMLOGIC_AUDIO) += clk-triphase.o
obj-$(CONFIG_COMMON_CLK_MESON_AO) += meson-aoclk.o
obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o
obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-32k.o
diff --git a/drivers/clk/meson/clkc-audio.h b/drivers/clk/meson/clkc-audio.h
index 0a7c157..286ff12 100644
--- a/drivers/clk/meson/clkc-audio.h
+++ b/drivers/clk/meson/clkc-audio.h
@@ -15,14 +15,6 @@ struct meson_clk_triphase_data {
struct parm ph2;
};
-struct meson_sclk_div_data {
- struct parm div;
- struct parm hi;
- unsigned int cached_div;
- struct clk_duty cached_duty;
-};
-
extern const struct clk_ops meson_clk_triphase_ops;
-extern const struct clk_ops meson_sclk_div_ops;
#endif /* __MESON_CLKC_AUDIO_H */
diff --git a/drivers/clk/meson/clkc.h b/drivers/clk/meson/clkc.h
index 6183b22..00b3320 100644
--- a/drivers/clk/meson/clkc.h
+++ b/drivers/clk/meson/clkc.h
@@ -27,6 +27,14 @@ struct parm {
u8 width;
};
+struct meson_sclk_div_data {
+ struct parm div;
+ struct parm hi;
+ unsigned int cached_div;
+ struct clk_duty cached_duty;
+ u8 flags;
+};
+
static inline unsigned int meson_parm_read(struct regmap *map, struct parm *p)
{
unsigned int val;
@@ -118,10 +126,10 @@ struct clk_regmap _name = { \
extern const struct clk_ops meson_clk_mpll_ops;
extern const struct clk_ops meson_clk_phase_ops;
extern const struct clk_ops meson_vid_pll_div_ro_ops;
+extern const struct clk_ops meson_sclk_div_ops;
struct clk_hw *meson_clk_hw_register_input(struct device *dev,
const char *of_name,
const char *clk_name,
unsigned long flags);
-
#endif /* __CLKC_H */
diff --git a/drivers/clk/meson/sclk-div.c b/drivers/clk/meson/sclk-div.c
index bc64019..a6c425b 100644
--- a/drivers/clk/meson/sclk-div.c
+++ b/drivers/clk/meson/sclk-div.c
@@ -4,42 +4,60 @@
* Author: Jerome Brunet <[email protected]>
*
* Sample clock generator divider:
- * This HW divider gates with value 0 but is otherwise a zero based divider:
+ * This HW divider gates with value 0
*
* val >= 1
- * divider = val + 1
+ * divider = val + 1 if ONE_BASED is not set, otherwise divider = val.
*
* The duty cycle may also be set for the LR clock variant. The duty cycle
* ratio is:
*
* hi = [0 - val]
- * duty_cycle = (1 + hi) / (1 + val)
+ * duty_cycle = (1 + hi) / (1 + val) if ONE_BASED is not set, otherwise:
+ * duty_cycle = hi / (1 + val)
*/
-#include "clkc-audio.h"
+#include "clkc.h"
-static inline struct meson_sclk_div_data *
-meson_sclk_div_data(struct clk_regmap *clk)
+static inline int get_reg(int val, unsigned char flag)
{
- return (struct meson_sclk_div_data *)clk->data;
+ WARN_ON(val < 1);
+ if ((flag & CLK_DIVIDER_ONE_BASED) || !val)
+ return val;
+ else
+ return val - 1;
+}
+
+static inline int get_value(int reg, unsigned char flag)
+{
+ if (flag & CLK_DIVIDER_ONE_BASED)
+ return reg;
+ else
+ return reg + 1;
}
-static int sclk_div_maxval(struct meson_sclk_div_data *sclk)
+static inline struct meson_sclk_div_data *
+meson_sclk_div_data(struct clk_regmap *clk)
{
- return (1 << sclk->div.width) - 1;
+ return (struct meson_sclk_div_data *)clk->data;
}
static int sclk_div_maxdiv(struct meson_sclk_div_data *sclk)
{
- return sclk_div_maxval(sclk) + 1;
+ unsigned int reg = clk_div_mask(sclk->div.width);
+
+ return get_value(reg, sclk->flags);
}
static int sclk_div_getdiv(struct clk_hw *hw, unsigned long rate,
unsigned long prate, int maxdiv)
{
int div = DIV_ROUND_CLOSEST_ULL((u64)prate, rate);
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
+ int mindiv = get_value(1, sclk->flags);
- return clamp(div, 2, maxdiv);
+ return clamp(div, mindiv, maxdiv);
}
static int sclk_div_bestdiv(struct clk_hw *hw, unsigned long rate,
@@ -47,7 +65,7 @@ static int sclk_div_bestdiv(struct clk_hw *hw, unsigned long rate,
struct meson_sclk_div_data *sclk)
{
struct clk_hw *parent = clk_hw_get_parent(hw);
- int bestdiv = 0, i;
+ int bestdiv = 0, i, mindiv;
unsigned long maxdiv, now, parent_now;
unsigned long best = 0, best_parent = 0;
@@ -64,8 +82,9 @@ static int sclk_div_bestdiv(struct clk_hw *hw, unsigned long rate,
* unsigned long in rate * i below
*/
maxdiv = min(ULONG_MAX / rate, maxdiv);
+ mindiv = get_value(1, sclk->flags);
- for (i = 2; i <= maxdiv; i++) {
+ for (i = mindiv; i <= maxdiv; i++) {
/*
* It's the most ideal case if the requested rate can be
* divided from parent clock without needing to change
@@ -111,10 +130,7 @@ static void sclk_apply_ratio(struct clk_regmap *clk,
sclk->cached_duty.num,
sclk->cached_duty.den);
- if (hi)
- hi -= 1;
-
- meson_parm_write(clk->map, &sclk->hi, hi);
+ meson_parm_write(clk->map, &sclk->hi, get_reg(hi, sclk->flags));
}
static int sclk_div_set_duty_cycle(struct clk_hw *hw,
@@ -145,7 +161,7 @@ static int sclk_div_get_duty_cycle(struct clk_hw *hw,
}
hi = meson_parm_read(clk->map, &sclk->hi);
- duty->num = hi + 1;
+ duty->num = get_value(hi, sclk->flags);
duty->den = sclk->cached_div;
return 0;
}
@@ -153,10 +169,13 @@ static int sclk_div_get_duty_cycle(struct clk_hw *hw,
static void sclk_apply_divider(struct clk_regmap *clk,
struct meson_sclk_div_data *sclk)
{
+ unsigned int div;
+
if (MESON_PARM_APPLICABLE(&sclk->hi))
sclk_apply_ratio(clk, sclk);
- meson_parm_write(clk->map, &sclk->div, sclk->cached_div - 1);
+ div = get_reg(sclk->cached_div, sclk->flags);
+ meson_parm_write(clk->map, &sclk->div, div);
}
static int sclk_div_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -224,7 +243,7 @@ static void sclk_div_init(struct clk_hw *hw)
if (!val)
sclk->cached_div = sclk_div_maxdiv(sclk);
else
- sclk->cached_div = val + 1;
+ sclk->cached_div = get_value(val, sclk->flags);
sclk_div_get_duty_cycle(hw, &sclk->cached_duty);
}
--
1.9.1
Hi Yixun,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on clk/clk-next]
[also build test ERROR on next-20181217]
[cannot apply to v4.20-rc7]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Jianxin-Pan/clk-meson-add-one-based-divider-support-for-sclk-divider/20181218-021139
base: https://git.kernel.org/pub/scm/linux/kernel/git/clk/linux.git clk-next
config: sh-allmodconfig (attached as .config)
compiler: sh4-linux-gnu-gcc (Debian 7.2.0-11) 7.2.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
GCC_VERSION=7.2.0 make.cross ARCH=sh
All error/warnings (new ones prefixed by >>):
In file included from include/linux/cache.h:5:0,
from include/linux/printk.h:9,
from include/linux/kernel.h:14,
from include/asm-generic/bug.h:18,
from arch/sh/include/asm/bug.h:112,
from include/linux/bug.h:5,
from include/linux/io.h:23,
from include/linux/clk-provider.h:9,
from drivers/clk//meson/clk-phase-delay.c:13:
drivers/clk//meson/clk-phase-delay.c: In function 'meson_clk_phase_delay_get_phase':
>> drivers/clk//meson/clk-phase-delay.c:33:40: warning: integer overflow in expression [-Woverflow]
period_ps = DIV_ROUND_UP(NSEC_PER_SEC * 1000,
^
include/uapi/linux/kernel.h:13:40: note: in definition of macro '__KERNEL_DIV_ROUND_UP'
#define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
^
>> drivers/clk//meson/clk-phase-delay.c:33:14: note: in expansion of macro 'DIV_ROUND_UP'
period_ps = DIV_ROUND_UP(NSEC_PER_SEC * 1000,
^~~~~~~~~~~~
drivers/clk//meson/clk-phase-delay.c: In function 'meson_clk_phase_delay_set_phase':
drivers/clk//meson/clk-phase-delay.c:50:40: warning: integer overflow in expression [-Woverflow]
period_ps = DIV_ROUND_UP(NSEC_PER_SEC * 1000, clk_hw_get_rate(hw));
^
include/uapi/linux/kernel.h:13:40: note: in definition of macro '__KERNEL_DIV_ROUND_UP'
#define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
^
drivers/clk//meson/clk-phase-delay.c:50:14: note: in expansion of macro 'DIV_ROUND_UP'
period_ps = DIV_ROUND_UP(NSEC_PER_SEC * 1000, clk_hw_get_rate(hw));
^~~~~~~~~~~~
In file included from ./arch/sh/include/generated/asm/div64.h:1:0,
from include/linux/kernel.h:207,
from include/asm-generic/bug.h:18,
from arch/sh/include/asm/bug.h:112,
from include/linux/bug.h:5,
from include/linux/io.h:23,
from include/linux/clk-provider.h:9,
from drivers/clk//meson/clk-phase-delay.c:13:
include/asm-generic/div64.h:222:28: warning: comparison of distinct pointer types lacks a cast
(void)(((typeof((n)) *)0) == ((uint64_t *)0)); \
^
>> drivers/clk//meson/clk-phase-delay.c:56:6: note: in expansion of macro 'do_div'
r = do_div(degrees, 360 / 1 << (ph->phase.width));
^~~~~~
In file included from include/linux/init.h:5:0,
from include/linux/io.h:22,
from include/linux/clk-provider.h:9,
from drivers/clk//meson/clk-phase-delay.c:13:
include/asm-generic/div64.h:235:25: warning: right shift count >= width of type [-Wshift-count-overflow]
} else if (likely(((n) >> 32) == 0)) { \
^
include/linux/compiler.h:76:40: note: in definition of macro 'likely'
# define likely(x) __builtin_expect(!!(x), 1)
^
>> drivers/clk//meson/clk-phase-delay.c:56:6: note: in expansion of macro 'do_div'
r = do_div(degrees, 360 / 1 << (ph->phase.width));
^~~~~~
In file included from ./arch/sh/include/generated/asm/div64.h:1:0,
from include/linux/kernel.h:207,
from include/asm-generic/bug.h:18,
from arch/sh/include/asm/bug.h:112,
from include/linux/bug.h:5,
from include/linux/io.h:23,
from include/linux/clk-provider.h:9,
from drivers/clk//meson/clk-phase-delay.c:13:
>> include/asm-generic/div64.h:239:22: error: passing argument 1 of '__div64_32' from incompatible pointer type [-Werror=incompatible-pointer-types]
__rem = __div64_32(&(n), __base); \
^
>> drivers/clk//meson/clk-phase-delay.c:56:6: note: in expansion of macro 'do_div'
r = do_div(degrees, 360 / 1 << (ph->phase.width));
^~~~~~
include/asm-generic/div64.h:213:17: note: expected 'uint64_t * {aka long long unsigned int *}' but argument is of type 'int *'
extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor);
^~~~~~~~~~
cc1: some warnings being treated as errors
--
In file included from include/linux/cache.h:5:0,
from include/linux/printk.h:9,
from include/linux/kernel.h:14,
from include/asm-generic/bug.h:18,
from arch/sh/include/asm/bug.h:112,
from include/linux/bug.h:5,
from include/linux/io.h:23,
from include/linux/clk-provider.h:9,
from drivers/clk/meson/clk-phase-delay.c:13:
drivers/clk/meson/clk-phase-delay.c: In function 'meson_clk_phase_delay_get_phase':
drivers/clk/meson/clk-phase-delay.c:33:40: warning: integer overflow in expression [-Woverflow]
period_ps = DIV_ROUND_UP(NSEC_PER_SEC * 1000,
^
include/uapi/linux/kernel.h:13:40: note: in definition of macro '__KERNEL_DIV_ROUND_UP'
#define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
^
drivers/clk/meson/clk-phase-delay.c:33:14: note: in expansion of macro 'DIV_ROUND_UP'
period_ps = DIV_ROUND_UP(NSEC_PER_SEC * 1000,
^~~~~~~~~~~~
drivers/clk/meson/clk-phase-delay.c: In function 'meson_clk_phase_delay_set_phase':
drivers/clk/meson/clk-phase-delay.c:50:40: warning: integer overflow in expression [-Woverflow]
period_ps = DIV_ROUND_UP(NSEC_PER_SEC * 1000, clk_hw_get_rate(hw));
^
include/uapi/linux/kernel.h:13:40: note: in definition of macro '__KERNEL_DIV_ROUND_UP'
#define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
^
drivers/clk/meson/clk-phase-delay.c:50:14: note: in expansion of macro 'DIV_ROUND_UP'
period_ps = DIV_ROUND_UP(NSEC_PER_SEC * 1000, clk_hw_get_rate(hw));
^~~~~~~~~~~~
In file included from ./arch/sh/include/generated/asm/div64.h:1:0,
from include/linux/kernel.h:207,
from include/asm-generic/bug.h:18,
from arch/sh/include/asm/bug.h:112,
from include/linux/bug.h:5,
from include/linux/io.h:23,
from include/linux/clk-provider.h:9,
from drivers/clk/meson/clk-phase-delay.c:13:
include/asm-generic/div64.h:222:28: warning: comparison of distinct pointer types lacks a cast
(void)(((typeof((n)) *)0) == ((uint64_t *)0)); \
^
drivers/clk/meson/clk-phase-delay.c:56:6: note: in expansion of macro 'do_div'
r = do_div(degrees, 360 / 1 << (ph->phase.width));
^~~~~~
In file included from include/linux/init.h:5:0,
from include/linux/io.h:22,
from include/linux/clk-provider.h:9,
from drivers/clk/meson/clk-phase-delay.c:13:
include/asm-generic/div64.h:235:25: warning: right shift count >= width of type [-Wshift-count-overflow]
} else if (likely(((n) >> 32) == 0)) { \
^
include/linux/compiler.h:76:40: note: in definition of macro 'likely'
# define likely(x) __builtin_expect(!!(x), 1)
^
drivers/clk/meson/clk-phase-delay.c:56:6: note: in expansion of macro 'do_div'
r = do_div(degrees, 360 / 1 << (ph->phase.width));
^~~~~~
In file included from ./arch/sh/include/generated/asm/div64.h:1:0,
from include/linux/kernel.h:207,
from include/asm-generic/bug.h:18,
from arch/sh/include/asm/bug.h:112,
from include/linux/bug.h:5,
from include/linux/io.h:23,
from include/linux/clk-provider.h:9,
from drivers/clk/meson/clk-phase-delay.c:13:
>> include/asm-generic/div64.h:239:22: error: passing argument 1 of '__div64_32' from incompatible pointer type [-Werror=incompatible-pointer-types]
__rem = __div64_32(&(n), __base); \
^
drivers/clk/meson/clk-phase-delay.c:56:6: note: in expansion of macro 'do_div'
r = do_div(degrees, 360 / 1 << (ph->phase.width));
^~~~~~
include/asm-generic/div64.h:213:17: note: expected 'uint64_t * {aka long long unsigned int *}' but argument is of type 'int *'
extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor);
^~~~~~~~~~
cc1: some warnings being treated as errors
vim +/__div64_32 +239 include/asm-generic/div64.h
^1da177e Linus Torvalds 2005-04-16 215
^1da177e Linus Torvalds 2005-04-16 216 /* The unnecessary pointer compare is there
^1da177e Linus Torvalds 2005-04-16 217 * to check for type safety (n must be 64bit)
^1da177e Linus Torvalds 2005-04-16 218 */
^1da177e Linus Torvalds 2005-04-16 219 # define do_div(n,base) ({ \
^1da177e Linus Torvalds 2005-04-16 220 uint32_t __base = (base); \
^1da177e Linus Torvalds 2005-04-16 221 uint32_t __rem; \
^1da177e Linus Torvalds 2005-04-16 222 (void)(((typeof((n)) *)0) == ((uint64_t *)0)); \
911918aa Nicolas Pitre 2015-11-02 223 if (__builtin_constant_p(__base) && \
911918aa Nicolas Pitre 2015-11-02 224 is_power_of_2(__base)) { \
911918aa Nicolas Pitre 2015-11-02 225 __rem = (n) & (__base - 1); \
911918aa Nicolas Pitre 2015-11-02 226 (n) >>= ilog2(__base); \
461a5e51 Nicolas Pitre 2015-10-30 227 } else if (__div64_const32_is_OK && \
461a5e51 Nicolas Pitre 2015-10-30 228 __builtin_constant_p(__base) && \
461a5e51 Nicolas Pitre 2015-10-30 229 __base != 0) { \
461a5e51 Nicolas Pitre 2015-10-30 230 uint32_t __res_lo, __n_lo = (n); \
461a5e51 Nicolas Pitre 2015-10-30 231 (n) = __div64_const32(n, __base); \
461a5e51 Nicolas Pitre 2015-10-30 232 /* the remainder can be computed with 32-bit regs */ \
461a5e51 Nicolas Pitre 2015-10-30 233 __res_lo = (n); \
461a5e51 Nicolas Pitre 2015-10-30 234 __rem = __n_lo - __res_lo * __base; \
911918aa Nicolas Pitre 2015-11-02 235 } else if (likely(((n) >> 32) == 0)) { \
^1da177e Linus Torvalds 2005-04-16 236 __rem = (uint32_t)(n) % __base; \
^1da177e Linus Torvalds 2005-04-16 237 (n) = (uint32_t)(n) / __base; \
^1da177e Linus Torvalds 2005-04-16 238 } else \
^1da177e Linus Torvalds 2005-04-16 @239 __rem = __div64_32(&(n), __base); \
^1da177e Linus Torvalds 2005-04-16 240 __rem; \
^1da177e Linus Torvalds 2005-04-16 241 })
^1da177e Linus Torvalds 2005-04-16 242
:::::: The code at line 239 was first introduced by commit
:::::: 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 Linux-2.6.12-rc2
:::::: TO: Linus Torvalds <[email protected]>
:::::: CC: Linus Torvalds <[email protected]>
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
Quoting Jianxin Pan (2018-12-17 08:24:10)
> diff --git a/drivers/clk/meson/mmc-clkc.c b/drivers/clk/meson/mmc-clkc.c
> new file mode 100644
> index 0000000..2582a98
> --- /dev/null
> +++ b/drivers/clk/meson/mmc-clkc.c
> @@ -0,0 +1,304 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Amlogic Meson MMC Sub Clock Controller Driver
> + *
> + * Copyright (c) 2017 Baylibre SAS.
> + * Author: Jerome Brunet <[email protected]>
> + *
> + * Copyright (c) 2018 Amlogic, inc.
> + * Author: Yixun Lan <[email protected]>
> + * Author: Jianxin Pan <[email protected]>
> + */
> +
> +#include <linux/clk.h>
Do you need this include?
> +#include <linux/clk-provider.h>
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +#include <linux/of_device.h>
> +#include <linux/mfd/syscon.h>
Is this used?
> +#include <linux/platform_device.h>
[...]
> + init.num_parents = 1;
> +
> + clk = mmc_clkc_register_clk(dev, map, &init, suffix, data);
> + if (IS_ERR(clk))
> + dev_err(dev, "%s clock registration failed\n", suffix);
> +
> + return clk;
> +}
> +
> +static int mmc_clkc_probe(struct platform_device *pdev)
> +{
> + struct clk_hw_onecell_data *onecell_data;
> + struct device *dev = &pdev->dev;
> + struct mmc_clkc_data *data;
> + struct regmap *map;
> + struct clk_regmap *clk, *core;
> + struct meson_sclk_div_data *div_data;
> +
> + /*cast to drop the const in match->data*/
> + data = (struct mmc_clkc_data *)of_device_get_match_data(dev);
Is that necessary? Maybe the users of this should take a const argument
and copy things?
> + if (!data)
> + return -ENODEV;
> +
> + map = syscon_node_to_regmap(dev->of_node);
> + if (IS_ERR(map)) {
> + dev_err(dev, "could not find mmc clock controller\n");
> + return PTR_ERR(map);
> + }
> +
> + onecell_data = devm_kzalloc(dev, sizeof(*onecell_data) +
> + sizeof(*onecell_data->hws) * MMC_MAX_CLKS,
struct_size()?
Hi Stephen,
Thank you for your time.
Please see my comments below.
On 2019/1/10 4:48, Stephen Boyd wrote:
> Quoting Jianxin Pan (2018-12-17 08:24:10)
>> diff --git a/drivers/clk/meson/mmc-clkc.c b/drivers/clk/meson/mmc-clkc.c
>> new file mode 100644
>> index 0000000..2582a98
>> --- /dev/null
>> +++ b/drivers/clk/meson/mmc-clkc.c
>> @@ -0,0 +1,304 @@
>> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
>> +/*
>> + * Amlogic Meson MMC Sub Clock Controller Driver
>> + *
>> + * Copyright (c) 2017 Baylibre SAS.
>> + * Author: Jerome Brunet <[email protected]>
>> + *
>> + * Copyright (c) 2018 Amlogic, inc.
>> + * Author: Yixun Lan <[email protected]>
>> + * Author: Jianxin Pan <[email protected]>
>> + */
>> +
>> +#include <linux/clk.h>
>
> Do you need this include?
>
Yes, It's also a clock-consumer.
devm_clk_get() gets clock from dts as the parent of mux.
Thank you for your review.
>> +#include <linux/clk-provider.h>
>> +#include <linux/module.h>
>> +#include <linux/regmap.h>
>> +#include <linux/slab.h>
>> +#include <linux/of_device.h>
>> +#include <linux/mfd/syscon.h>
>
> Is this used?
Yes, syscon_node_to_regmap() is used in mmc_clkc_probe().
>
>> +#include <linux/platform_device.h>
> [...]
>> + init.num_parents = 1;
>> +
>> + clk = mmc_clkc_register_clk(dev, map, &init, suffix, data);
>> + if (IS_ERR(clk))
>> + dev_err(dev, "%s clock registration failed\n", suffix);
>> +
>> + return clk;
>> +}
>> +
>> +static int mmc_clkc_probe(struct platform_device *pdev)
>> +{
>> + struct clk_hw_onecell_data *onecell_data;
>> + struct device *dev = &pdev->dev;
>> + struct mmc_clkc_data *data;
>> + struct regmap *map;
>> + struct clk_regmap *clk, *core;
>> + struct meson_sclk_div_data *div_data;
>> +
>> + /*cast to drop the const in match->data*/
>> + data = (struct mmc_clkc_data *)of_device_get_match_data(dev);
>
> Is that necessary? Maybe the users of this should take a const argument
> and copy things?
OK, I can alloc two extra memory for PHASE_RX and PHASE_TX data, and then copy from const data to them.
>
>> + if (!data)
>> + return -ENODEV;
>> +
>> + map = syscon_node_to_regmap(dev->of_node);
>> + if (IS_ERR(map)) {
>> + dev_err(dev, "could not find mmc clock controller\n");
>> + return PTR_ERR(map);
>> + }
>> +
>> + onecell_data = devm_kzalloc(dev, sizeof(*onecell_data) +
>> + sizeof(*onecell_data->hws) * MMC_MAX_CLKS,
>
> struct_size()?
>
OK, I will change it in the next version.
Thank you for your review.
> .
>