[PATCH 5/7] clk: ralink: add clock gate driver for mt7621 SoC

From: Sergio Paracuellos
Date: Wed Nov 11 2020 - 11:30:55 EST


In mt7621 SoC register 'SYSC_REG_CPLL_CLKCFG1' allows to
handle a bunch of gates to enable/disable clocks for
all or some ip cores. Add a driver to properly handle them.
Parent clocks for this gates are not documented at all in
the SoC documentation so all of them have been assumed looking
into the clock frequencies used in its related driver code.
There are three main clocks which are "cpu", "ahb" and "apb"
from the 'mt7621-pll'.

The following parents are set to each GATE:
* "hsdma": "ahb"
* "fe": "ahb"
* "sp_divtx": "ahb"
* "timer": "cpu"
* "int": "cpu"
* "mc": "ahb"
* "pcm": "ahb"
* "pio": "ahb"
* "gdma": "ahb"
* "nand": "ahb"
* "i2c": "ahb"
* "i2s": "ahb"
* "spi": "ahb"
* "uart1": "apb"
* "uart2": "apb"
* "uart3": "apb"
* "eth": "ahb"
* "pcie0": "ahb"
* "pcie1": "ahb"
* "pcie2": "ahb"
* "crypto": "ahb"
* "shxc": "ahb"

With this information the clk driver will provide gate functionality
from a a set of hardcoded clocks allowing to define a nice device
tree without fixed clocks.

Signed-off-by: Sergio Paracuellos <sergio.paracuellos@xxxxxxxxx>
---
drivers/clk/Kconfig | 1 +
drivers/clk/Makefile | 1 +
drivers/clk/ralink/Kconfig | 14 ++
drivers/clk/ralink/Makefile | 2 +
drivers/clk/ralink/clk-mt7621.c | 258 ++++++++++++++++++++++++++++++++
5 files changed, 276 insertions(+)
create mode 100644 drivers/clk/ralink/Kconfig
create mode 100644 drivers/clk/ralink/Makefile
create mode 100644 drivers/clk/ralink/clk-mt7621.c

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index c715d4681a0b..5f94c4329033 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -372,6 +372,7 @@ source "drivers/clk/mediatek/Kconfig"
source "drivers/clk/meson/Kconfig"
source "drivers/clk/mvebu/Kconfig"
source "drivers/clk/qcom/Kconfig"
+source "drivers/clk/ralink/Kconfig"
source "drivers/clk/renesas/Kconfig"
source "drivers/clk/rockchip/Kconfig"
source "drivers/clk/samsung/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index da8fcf147eb1..6578e167b047 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -100,6 +100,7 @@ obj-$(CONFIG_COMMON_CLK_NXP) += nxp/
obj-$(CONFIG_MACH_PISTACHIO) += pistachio/
obj-$(CONFIG_COMMON_CLK_PXA) += pxa/
obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/
+obj-y += ralink/
obj-y += renesas/
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/
diff --git a/drivers/clk/ralink/Kconfig b/drivers/clk/ralink/Kconfig
new file mode 100644
index 000000000000..7e8697327e0c
--- /dev/null
+++ b/drivers/clk/ralink/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# MediaTek Mt7621 Clock Driver
+#
+menu "Clock driver for mediatek mt7621 SoC"
+ depends on SOC_MT7621 || COMPILE_TEST
+
+config CLK_MT7621
+ bool "Clock driver for MediaTek MT7621"
+ depends on SOC_MT7621 || COMPILE_TEST
+ default SOC_MT7621
+ help
+ This driver supports MediaTek MT7621 basic clocks.
+endmenu
diff --git a/drivers/clk/ralink/Makefile b/drivers/clk/ralink/Makefile
new file mode 100644
index 000000000000..cf6f9216379d
--- /dev/null
+++ b/drivers/clk/ralink/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_CLK_MT7621) += clk-mt7621.o
diff --git a/drivers/clk/ralink/clk-mt7621.c b/drivers/clk/ralink/clk-mt7621.c
new file mode 100644
index 000000000000..f7279d784a36
--- /dev/null
+++ b/drivers/clk/ralink/clk-mt7621.c
@@ -0,0 +1,258 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Mediatek MT7621 Clock gate Driver
+ * Author: Sergio Paracuellos <sergio.paracuellos@xxxxxxxxx>
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk-provider.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <dt-bindings/clock/mt7621-clk.h>
+
+/* clock gate configuration register */
+#define SYSC_REG_CLKCFG1 0x30
+
+/* Gate register enable bits */
+#define MT7621_HSDMA_CLK_EN BIT(5)
+#define MT7621_FE_CLK_EN BIT(6)
+#define MT7621_SP_DIVTX_CLK_EN BIT(7)
+#define MT7621_TIMER_CLK_EN BIT(8)
+#define MT7621_INT_CLK_EN BIT(9)
+#define MT7621_MC_CLK_EN BIT(10)
+#define MT7621_PCM_CLK_EN BIT(11)
+#define MT7621_PIO_CLK_EN BIT(13)
+#define MT7621_GDMA_CLK_EN BIT(14)
+#define MT7621_NAND_CLK_EN BIT(15)
+#define MT7621_I2C_CLK_EN BIT(16)
+#define MT7621_I2S_CLK_EN BIT(17)
+#define MT7621_SPI_CLK_EN BIT(18)
+#define MT7621_UART1_CLK_EN BIT(19)
+#define MT7621_UART2_CLK_EN BIT(20)
+#define MT7621_UART3_CLK_EN BIT(21)
+#define MT7621_ETH_CLK_EN BIT(23)
+#define MT7621_PCIE0_CLK_EN BIT(24)
+#define MT7621_PCIE1_CLK_EN BIT(25)
+#define MT7621_PCIE2_CLK_EN BIT(26)
+#define MT7621_CRYPTO_CLK_EN BIT(29)
+#define MT7621_SHXC_CLK_EN BIT(30)
+
+struct mt7621_clk_provider {
+ struct device_node *node;
+ struct device *dev;
+ struct regmap *syscon_regmap;
+ struct clk_hw_onecell_data *clk_data;
+};
+
+struct mt7621_gate {
+ u8 idx;
+ const char *name;
+ const char *parent_name;
+ struct mt7621_clk_provider *clk_prov;
+ struct clk_hw hw;
+ u32 bit_idx;
+};
+
+struct mt7621_gate_data {
+ u8 idx;
+ const char *name;
+ const char *parent_name;
+ u32 bit_idx;
+};
+
+#define GATE(_id, _name, _pname, _shift) \
+ { \
+ .idx = _id, \
+ .name = _name, \
+ .parent_name = _pname, \
+ .bit_idx = _shift \
+ }
+
+static const struct mt7621_gate mt7621_gates[] = {
+ GATE(MT7621_CLK_HSDMA, "hsdma", "ahb", MT7621_HSDMA_CLK_EN),
+ GATE(MT7621_CLK_FE, "fe", "ahb", MT7621_FE_CLK_EN),
+ GATE(MT7621_CLK_SP_DIVTX, "sp_divtx", "ahb", MT7621_SP_DIVTX_CLK_EN),
+ GATE(MT7621_CLK_TIMER, "timer", "cpu", MT7621_TIMER_CLK_EN),
+ GATE(MT7621_CLK_INT, "int", "cpu", MT7621_INT_CLK_EN),
+ GATE(MT7621_CLK_MC, "mc", "ahb", MT7621_MC_CLK_EN),
+ GATE(MT7621_CLK_PCM, "pcm", "ahb", MT7621_PCM_CLK_EN),
+ GATE(MT7621_CLK_PIO, "pio", "ahb", MT7621_PIO_CLK_EN),
+ GATE(MT7621_CLK_GDMA, "gdma", "ahb", MT7621_GDMA_CLK_EN),
+ GATE(MT7621_CLK_NAND, "nand", "ahb", MT7621_NAND_CLK_EN),
+ GATE(MT7621_CLK_I2C, "i2c", "ahb", MT7621_I2C_CLK_EN),
+ GATE(MT7621_CLK_I2S, "i2s", "ahb", MT7621_I2S_CLK_EN),
+ GATE(MT7621_CLK_SPI, "spi", "ahb", MT7621_SPI_CLK_EN),
+ GATE(MT7621_CLK_UART1, "uart1", "apb", MT7621_UART1_CLK_EN),
+ GATE(MT7621_CLK_UART2, "uart2", "apb", MT7621_UART2_CLK_EN),
+ GATE(MT7621_CLK_UART3, "uart3", "apb", MT7621_UART3_CLK_EN),
+ GATE(MT7621_CLK_ETH, "eth", "ahb", MT7621_ETH_CLK_EN),
+ GATE(MT7621_CLK_PCIE0, "pcie0", "ahb", MT7621_PCIE0_CLK_EN),
+ GATE(MT7621_CLK_PCIE1, "pcie1", "ahb", MT7621_PCIE1_CLK_EN),
+ GATE(MT7621_CLK_PCIE2, "pcie2", "ahb", MT7621_PCIE2_CLK_EN),
+ GATE(MT7621_CLK_CRYPTO, "crypto", "ahb", MT7621_CRYPTO_CLK_EN),
+ GATE(MT7621_CLK_SHXC, "shxc", "ahb", MT7621_SHXC_CLK_EN)
+};
+
+static inline struct mt7621_gate *to_mt7621_gate(struct clk_hw *hw)
+{
+ return container_of(hw, struct mt7621_gate, hw);
+}
+
+static int mt7621_gate_enable(struct clk_hw *hw)
+{
+ struct mt7621_gate *clk_gate = to_mt7621_gate(hw);
+ struct regmap *scon = clk_gate->clk_prov->syscon_regmap;
+
+ return regmap_update_bits(scon, SYSC_REG_CLKCFG1,
+ clk_gate->bit_idx, clk_gate->bit_idx);
+}
+
+static void mt7621_gate_disable(struct clk_hw *hw)
+{
+ struct mt7621_gate *clk_gate = to_mt7621_gate(hw);
+ struct regmap *scon = clk_gate->clk_prov->syscon_regmap;
+
+ regmap_update_bits(scon, SYSC_REG_CLKCFG1, clk_gate->bit_idx, 0);
+}
+
+static int mt7621_gate_is_enabled(struct clk_hw *hw)
+{
+ struct mt7621_gate *clk_gate = to_mt7621_gate(hw);
+ struct regmap *scon = clk_gate->clk_prov->syscon_regmap;
+ unsigned int val;
+
+ if (regmap_read(scon, SYSC_REG_CLKCFG1, &val))
+ return 0;
+
+ return val & clk_gate->bit_idx;
+}
+
+static const struct clk_ops mt7621_gate_ops = {
+ .enable = mt7621_gate_enable,
+ .disable = mt7621_gate_disable,
+ .is_enabled = mt7621_gate_is_enabled,
+};
+
+static int mt7621_gate_ops_init(struct device *dev, struct mt7621_gate *sclk)
+{
+ struct clk_init_data init = {
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ .num_parents = 1,
+ .parent_names = &sclk->parent_name,
+ .ops = &mt7621_gate_ops,
+ .name = sclk->name,
+ };
+
+ sclk->hw.init = &init;
+ return devm_clk_hw_register(dev, &sclk->hw);
+}
+
+static int mt7621_register_gates(struct mt7621_clk_provider *clk_prov)
+{
+ struct clk_hw_onecell_data **clk_data = &clk_prov->clk_data;
+ struct device *dev = clk_prov->dev;
+ int idx, err, count;
+ struct clk_hw **hws;
+
+ count = ARRAY_SIZE(mt7621_gates);
+ *clk_data = devm_kzalloc(dev, struct_size(*clk_data, hws, count),
+ GFP_KERNEL);
+ if (!*clk_data)
+ return -ENOMEM;
+
+ (*clk_data)->num = count;
+ hws = (*clk_data)->hws;
+
+ for (idx = 0; idx < ARRAY_SIZE(mt7621_gates); idx++) {
+ struct mt7621_gate *sclk;
+
+ sclk = devm_kzalloc(dev, sizeof(*sclk), GFP_KERNEL);
+ if (!sclk)
+ return -ENOMEM;
+
+ sclk->idx = mt7621_gates[idx].idx;
+ sclk->name = mt7621_gates[idx].name;
+ sclk->parent_name = mt7621_gates[idx].parent_name;
+ sclk->bit_idx = mt7621_gates[idx].bit_idx;
+ sclk->clk_prov = clk_prov;
+
+ err = mt7621_gate_ops_init(dev, sclk);
+ if (err) {
+ dev_err(dev, "failed to register clock %d\n", idx);
+ devm_kfree(dev, sclk);
+ hws[idx] = NULL;
+ } else {
+ dev_info(dev, "Registered clock gate: %s\n",
+ mt7621_gates[idx].name);
+ hws[idx] = &sclk->hw;
+ }
+ }
+
+ return 0;
+}
+
+static int mt7621_clk_init(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = pdev->dev.of_node;
+ struct mt7621_clk_provider *clk_provider;
+ int ret;
+
+ clk_provider = devm_kzalloc(dev, sizeof(*clk_provider), GFP_KERNEL);
+ if (!clk_provider)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, clk_provider);
+ clk_provider->syscon_regmap = syscon_regmap_lookup_by_phandle(node, "ralink,sysctl");
+ if (IS_ERR(clk_provider->syscon_regmap)) {
+ dev_err(dev, "Could not get syscon regmap\n");
+ return -EINVAL;
+ }
+
+ clk_provider->node = node;
+ clk_provider->dev = dev;
+
+ ret = mt7621_register_gates(clk_provider);
+ if (ret) {
+ dev_err(dev, "Error registering gates\n");
+ return ret;
+ }
+
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
+ clk_provider->clk_data);
+}
+
+static int clk_mt7621_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ ret = mt7621_clk_init(pdev);
+ if (ret) {
+ dev_err(dev, "Could not register clock provider: %s: %d\n",
+ pdev->name, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id of_match_clk_mt7621[] = {
+ { .compatible = "mediatek,mt7621-clk", .data = mt7621_clk_init },
+ {}
+};
+
+static struct platform_driver clk_mt7621_drv = {
+ .probe = clk_mt7621_probe,
+ .driver = {
+ .name = "clk-mt7621",
+ .of_match_table = of_match_clk_mt7621,
+ },
+};
+builtin_platform_driver(clk_mt7621_drv);
+
+MODULE_AUTHOR("Sergio Paracuellos <sergio.paracuellos@xxxxxxxxx>");
+MODULE_DESCRIPTION("Mediatek Mt7621 clock gate driver");
+MODULE_LICENSE("GPL v2");
--
2.25.1