kernel-5.15/0602-Baikal-M-clock-driver.patch
Mikhail Novosyolov 839b6a86b6 Add support of Baikal-M SoCs
Information about config values was taken from:

From 804820df7bcb3d53a33ecd074b1eac277e938f24 Mon Sep 17 00:00:00 2001
From: Alexey Sheplyakov <asheplyakov@altlinux.org>
Date: Thu, 4 Feb 2021 19:35:14 +0400
Subject: [PATCH] config-aarch64: adjusted for Baikal-M (MBM1.0 board)

* DW_APB_TIMER=y, DW_APB_TIMER_OF=y: SoC clocks

* SERIAL_8250_DW=y: serial console

* I2C_DESIGNWARE_CORE=y, I2C_DESIGNWARE_PLATFORM=y: BMC (board
  management controller) and RTC (Real Time Clock) are connected
  via I2C.

* GPIO_DWAPB=y: device (PCIe, PHY, etc) reset/configuration

* RTC_DRV_PCF2127=y: RTC compiled in so the kernel automatically
  sets the system time from the hardware clock

* TP_BMC=y: amongst other things handles the power button

* DRM_BAIKAL_VDU=m, DRM_BAIKAL_HDMI=m: video unit and HDMI transmitter

* CMA_SIZE_MBYTES=256: video display unit and GPU use system RAM, hence
  CMA should reserve enough (contiguous) memory.
  Note: CMA reserves memory during very early init, hence the size
  has to be hard-coded into CONFIG

* MALI_MIDGARD=m: GPU driver, kernel side of proprietary mali blob.
  Note: kernel mode code is GPLv2, so it's fine to distribute it.

* SENSORS_BT1_PVT=m: hardware temperature/voltage sensors

* PCI_BAIKAL=m: PCIe root complex. Compiled as a module since takes
  ages (60 seconds or so) to probe the hardware. If compiled in
  substantially increases the boot time, and machine is completely
  unresponsive during probing PCIe. When built as a module probing
  executes concurrently with other boot activities (unless booting
  from a PCIe device)

* STMMAC_ETH=m, STMMAC_PLATFORM=m, DWMAC_BAIKAL=m: Ethernet driver
2021-06-22 16:35:50 +03:00

502 lines
13 KiB
Diff

From 2e8241aba5add6f768a0876bc11c989681e28817 Mon Sep 17 00:00:00 2001
From: Alexey Sheplyakov <asheplyakov@altlinux.org>
Date: Fri, 20 Mar 2020 13:55:42 +0400
Subject: [PATCH 602/625] Baikal-M: clock driver
(cherry picked from commit acf15020f93d3be658922bb46ee9b4eb84497494)
---
drivers/clk/Makefile | 1 +
drivers/clk/baikal/Makefile | 1 +
drivers/clk/baikal/clk-baikal.c | 459 ++++++++++++++++++++++++++++++++
3 files changed, 461 insertions(+)
create mode 100644 drivers/clk/baikal/Makefile
create mode 100644 drivers/clk/baikal/clk-baikal.c
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index da8fcf147eb1..ab785aca5c8e 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -77,6 +77,7 @@ obj-$(CONFIG_COMMON_CLK_AT91) += at91/
obj-$(CONFIG_ARCH_ARTPEC) += axis/
obj-$(CONFIG_ARC_PLAT_AXS10X) += axs10x/
obj-$(CONFIG_CLK_BAIKAL_T1) += baikal-t1/
+obj-$(CONFIG_ARCH_BAIKAL) += baikal/
obj-y += bcm/
obj-$(CONFIG_ARCH_BERLIN) += berlin/
obj-$(CONFIG_ARCH_DAVINCI) += davinci/
diff --git a/drivers/clk/baikal/Makefile b/drivers/clk/baikal/Makefile
new file mode 100644
index 000000000000..56aa4de4081c
--- /dev/null
+++ b/drivers/clk/baikal/Makefile
@@ -0,0 +1 @@
+obj-y += clk-baikal.o
\ No newline at end of file
diff --git a/drivers/clk/baikal/clk-baikal.c b/drivers/clk/baikal/clk-baikal.c
new file mode 100644
index 000000000000..ddf1d328eeaf
--- /dev/null
+++ b/drivers/clk/baikal/clk-baikal.c
@@ -0,0 +1,459 @@
+/*
+ * clk-baikal.c - Baikal Electronics clock driver.
+ *
+ * Copyright (C) 2015,2016 Baikal Electronics JSC
+ *
+ * Author:
+ * Ekaterina Skachko <Ekaterina.Skachko@baikalelectronics.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <asm/setup.h>
+#include <linux/arm-smccc.h>
+
+#define CMU_PLL_SET_RATE 0
+#define CMU_PLL_GET_RATE 1
+#define CMU_PLL_ENABLE 2
+#define CMU_PLL_DISABLE 3
+#define CMU_PLL_ROUND_RATE 4
+#define CMU_PLL_IS_ENABLED 5
+#define CMU_CLK_CH_SET_RATE 6
+#define CMU_CLK_CH_GET_RATE 7
+#define CMU_CLK_CH_ENABLE 8
+#define CMU_CLK_CH_DISABLE 9
+#define CMU_CLK_CH_ROUND_RATE 10
+#define CMU_CLK_CH_IS_ENABLED 11
+
+
+struct baikal_clk_cmu {
+ struct clk_hw hw;
+ uint32_t cmu_id;
+ unsigned int parent;
+ const char *name;
+ spinlock_t *lock;
+ void __iomem *reg;
+ unsigned int latency; /* ns */
+ unsigned int min, max, step;
+ unsigned int clk_ch_num;
+ uint32_t is_clk_ch;
+};
+
+#define to_baikal_cmu(_hw) container_of(_hw, struct baikal_clk_cmu, hw)
+
+/* Pointer to the place on handling SMC CMU calls in monitor */
+#define BAIKAL_SMC_LCRU_ID 0x82000000
+
+static int baikal_clk_enable(struct clk_hw *hw)
+{
+ struct arm_smccc_res res;
+ struct baikal_clk_cmu *pclk = to_baikal_cmu(hw);
+ uint32_t cmd;
+
+ if (pclk->is_clk_ch){
+ cmd = CMU_CLK_CH_ENABLE;
+ }
+ else {
+ cmd = CMU_PLL_ENABLE;
+ }
+
+ pr_debug("[%s, %x:%d:%s] %s\n",
+ pclk->name,
+ pclk->parent,
+ pclk->cmu_id,
+ pclk->is_clk_ch?"ch":"pll",
+ "enable");
+
+ /* If clock valid */
+ arm_smccc_smc(BAIKAL_SMC_LCRU_ID, pclk->cmu_id, cmd, 0,
+ pclk->parent, 0, 0, 0, &res);
+
+ return res.a0;
+}
+
+static void baikal_clk_disable(struct clk_hw *hw)
+{
+
+ struct arm_smccc_res res;
+ struct baikal_clk_cmu *pclk = to_baikal_cmu(hw);
+ uint32_t cmd;
+
+ if (pclk->is_clk_ch)
+ cmd = CMU_CLK_CH_DISABLE;
+ else
+ cmd = CMU_PLL_DISABLE;
+
+ pr_debug("[%s, %x:%d:%s] %s\n",
+ pclk->name,
+ pclk->parent,
+ pclk->cmu_id,
+ pclk->is_clk_ch?"ch":"pll",
+ "disable");
+
+ /* If clock valid */
+ arm_smccc_smc(BAIKAL_SMC_LCRU_ID, pclk->cmu_id, cmd, 0,
+ pclk->parent, 0, 0, 0, &res);
+}
+
+static int baikal_clk_is_enabled(struct clk_hw *hw)
+{
+ struct arm_smccc_res res;
+ struct baikal_clk_cmu *pclk = to_baikal_cmu(hw);
+ uint32_t cmd;
+
+ if (pclk->is_clk_ch)
+ cmd = CMU_CLK_CH_IS_ENABLED;
+ else
+ cmd = CMU_PLL_IS_ENABLED;
+
+ /* If clock valid */
+ arm_smccc_smc(BAIKAL_SMC_LCRU_ID, pclk->cmu_id, cmd, 0,
+ pclk->parent, 0, 0, 0, &res);
+
+ pr_debug("[%s, %x:%d:%s] %s, %ld\n",
+ pclk->name,
+ pclk->parent,
+ pclk->cmu_id,
+ pclk->is_clk_ch?"ch":"pll",
+ "is enable",
+ res.a0);
+
+ return res.a0;
+}
+
+static unsigned long baikal_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct arm_smccc_res res;
+ struct baikal_clk_cmu *pclk = to_baikal_cmu(hw);
+ uint32_t cmd;
+ unsigned long parent;
+
+ if (pclk->is_clk_ch) {
+ cmd = CMU_CLK_CH_GET_RATE;
+ parent = pclk->parent;
+ } else {
+ cmd = CMU_PLL_GET_RATE;
+ parent= parent_rate;
+ }
+
+ /* If clock valid */
+ arm_smccc_smc(BAIKAL_SMC_LCRU_ID, pclk->cmu_id, cmd, 0,
+ parent, 0, 0, 0, &res);
+
+ pr_debug("[%s, %x:%d:%s] %s, %ld\n",
+ pclk->name,
+ parent,
+ pclk->cmu_id,
+ pclk->is_clk_ch?"ch":"pll",
+ "get rate",
+ res.a0);
+
+ /* Return actual freq */
+ return res.a0;
+
+}
+
+static int baikal_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct arm_smccc_res res;
+ struct baikal_clk_cmu *pclk = to_baikal_cmu(hw);
+ uint32_t cmd;
+
+ if (pclk->is_clk_ch)
+ cmd = CMU_CLK_CH_SET_RATE;
+ else
+ cmd = CMU_PLL_SET_RATE;
+
+ pr_debug("[%s, %x:%d:%s] %s, %ld\n",
+ pclk->name,
+ pclk->parent,
+ pclk->cmu_id,
+ pclk->is_clk_ch?"ch":"pll",
+ "set rate",
+ rate);
+
+ arm_smccc_smc(BAIKAL_SMC_LCRU_ID, pclk->cmu_id, cmd, rate,
+ pclk->parent, 0, 0, 0, &res);
+
+ return res.a0;
+}
+
+static long baikal_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct arm_smccc_res res;
+ struct baikal_clk_cmu *pclk = to_baikal_cmu(hw);
+ unsigned long parent;
+ uint32_t cmd;
+
+ if (pclk->is_clk_ch) {
+ cmd = CMU_CLK_CH_ROUND_RATE;
+ parent = pclk->parent;
+ } else {
+ cmd = CMU_PLL_ROUND_RATE;
+ parent = *prate;
+ }
+
+
+ /* If clock valid */
+ arm_smccc_smc(BAIKAL_SMC_LCRU_ID, pclk->cmu_id, cmd, rate,
+ parent, 0, 0, 0, &res);
+
+ pr_debug("[%s, %x:%d:%s] %s, %ld\n",
+ pclk->name,
+ pclk->parent,
+ pclk->cmu_id,
+ pclk->is_clk_ch?"ch":"pll",
+ "round rate",
+ res.a0);
+
+ /* Return actual freq */
+ return res.a0;
+}
+
+const struct clk_ops be_clk_pll_ops = {
+ .enable = baikal_clk_enable,
+ .disable = baikal_clk_disable,
+ .is_enabled = baikal_clk_is_enabled,
+ .recalc_rate = baikal_clk_recalc_rate,
+ .set_rate = baikal_clk_set_rate,
+ .round_rate = baikal_clk_round_rate,
+};
+
+
+
+static int baikal_clk_probe(struct platform_device *pdev)
+{
+ struct clk_init_data init;
+ struct clk_init_data *init_ch;
+ struct baikal_clk_cmu *cmu;
+ struct baikal_clk_cmu **cmu_ch;
+ struct device_node *node = pdev->dev.of_node;
+
+ struct clk *clk;
+ struct clk_onecell_data *clk_ch;
+
+ int number, i = 0;
+ u32 rc, index;
+ struct property *prop;
+ const __be32 *p;
+ const char *clk_ch_name;
+ const char *parent_name;
+
+ cmu = kmalloc(sizeof(struct baikal_clk_cmu *), GFP_KERNEL);
+ if (!cmu) {
+ /* Error */
+ pr_err("%s: could not allocate CMU clk\n", __func__);
+ kfree(cmu);
+ return -ENOMEM;
+ }
+
+ of_property_read_string(node, "clock-output-names", &cmu->name);
+ parent_name = of_clk_get_parent_name(node, 0);
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ of_property_read_u32(node, "clock-frequency", &cmu->parent);
+ of_property_read_u32(node, "cmu-id", &cmu->cmu_id);
+
+ /* Setup clock init structure */
+ init.name = cmu->name;
+ init.ops = &be_clk_pll_ops;
+ init.flags = CLK_IGNORE_UNUSED;
+
+ cmu->hw.init = &init;
+ cmu->is_clk_ch = 0;
+
+ /* Register the clock */
+ pr_debug("Add %s clock\n", cmu->name);
+ clk = clk_register(NULL, &cmu->hw);
+
+ if (IS_ERR(clk)) {
+ /* Error */
+ pr_err("%s: could not register clk %s\n", __func__, cmu->name);
+ return -ENOMEM;
+ }
+
+ /* Register the clock for lookup */
+ rc = clk_register_clkdev(clk, cmu->name, NULL);
+ if (rc != 0) {
+ /* Error */
+ pr_err("%s: could not register lookup clk %s\n",
+ __func__, cmu->name);
+ }
+
+ /* FIXME We probably SHOULDN'T enable it here */
+ clk_prepare_enable(clk);
+
+ number = of_property_count_u32_elems(node, "clock-indices");
+
+ if (number > 0) {
+ clk_ch = kmalloc(sizeof(struct clk_onecell_data), GFP_KERNEL);
+ if (!clk_ch) {
+ /* Error */
+ pr_err("%s: could not allocate CMU clk channel\n", __func__);
+ kfree(clk_ch);
+ return -ENOMEM;
+ }
+ /* Get the last index to find out max number of children*/
+ of_property_for_each_u32(node, "clock-indices", prop, p, index) {
+ ;
+ }
+ clk_ch->clks = kcalloc(index + 1, sizeof(struct clk *), GFP_KERNEL);
+ clk_ch->clk_num = index + 1;
+ cmu_ch = kcalloc((index + 1 ), sizeof(struct baikal_clk_cmu *), GFP_KERNEL);
+ if (!cmu_ch) {
+ kfree(cmu_ch);
+ kfree(clk_ch);
+ return -ENOMEM;
+ }
+ init_ch = kcalloc((number + 1 ), sizeof(struct clk_init_data), GFP_KERNEL);
+ if (!init_ch){
+ /* Error */
+ pr_err("%s: could not allocate CMU init structure \n", __func__);
+ kfree(init_ch);
+ kfree(cmu_ch);
+ kfree(clk_ch);
+ return -ENOMEM;
+ }
+
+ of_property_for_each_u32(node, "clock-indices", prop, p, index) {
+ of_property_read_string_index(node, "clock-names",
+ i, &clk_ch_name);
+ pr_err("%s index %x name <%s> i %x \n", __func__,index, clk_ch_name, i);
+ cmu_ch[index] = kmalloc(sizeof(struct baikal_clk_cmu), GFP_KERNEL);
+
+ cmu_ch[index]->name = clk_ch_name;
+ cmu_ch[index]->cmu_id = index;
+ cmu_ch[index]->parent = cmu->cmu_id;
+ cmu_ch[index]->is_clk_ch = 1;
+ init_ch[i].parent_names = &cmu->name;
+ init_ch[i].num_parents = 1;
+
+ init_ch[i].name = clk_ch_name;
+ init_ch[i].ops = &be_clk_pll_ops;
+ init_ch[i].flags = CLK_IGNORE_UNUSED;
+
+ cmu_ch[index]->hw.init = &init_ch[i];
+ clk_ch->clks[index] = clk_register(NULL, &cmu_ch[index]->hw);
+
+ if (IS_ERR(clk_ch->clks[index])) {
+ /* Error */
+ pr_err("%s: could not register clk %s\n", __func__, clk_ch_name);
+ }
+ /* Register the clock for lookup */
+ rc = clk_register_clkdev(clk_ch->clks[index], clk_ch_name, NULL);
+ if (rc != 0) {
+ /* Error */
+ pr_err("%s: could not register lookup clk %s\n",
+ __func__, clk_ch_name);
+ }
+ /* FIXME We probably SHOULDN'T enable it here */
+ clk_prepare_enable(clk_ch->clks[index]);
+ i++;
+ }
+ return of_clk_add_provider(pdev->dev.of_node, of_clk_src_onecell_get, clk_ch);
+ } else
+
+ return of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get, clk);
+}
+
+static int baikal_clk_remove(struct platform_device *pdev)
+{
+ of_clk_del_provider(pdev->dev.of_node);
+
+ return 0;
+}
+
+static const struct of_device_id baikal_clk_of_match[] = {
+ {.compatible = "baikal,cmu"},
+ { /* sentinel value */ }
+};
+
+static struct platform_driver clk_avlsp_cmu0_driver = {
+ .probe = baikal_clk_probe,
+ .remove = baikal_clk_remove,
+ .driver = {
+ .name = "baikal-avlsp-cmu0",
+ .of_match_table = baikal_clk_of_match,
+ },
+};
+
+static struct platform_driver clk_avlsp_cmu1_driver = {
+ .probe = baikal_clk_probe,
+ .remove = baikal_clk_remove,
+ .driver = {
+ .name = "baikal-avlsp-cmu1",
+ .of_match_table = baikal_clk_of_match,
+ },
+};
+
+static struct platform_driver clk_xgbe_cmu0_driver = {
+ .probe = baikal_clk_probe,
+ .remove = baikal_clk_remove,
+ .driver = {
+ .name = "baikal-xgbe-cmu0",
+ .of_match_table = baikal_clk_of_match,
+ },
+};
+
+static struct platform_driver clk_xgbe_cmu1_driver = {
+ .probe = baikal_clk_probe,
+ .remove = baikal_clk_remove,
+ .driver = {
+ .name = "baikal-xgbe-cmu1",
+ .of_match_table = baikal_clk_of_match,
+ },
+};
+
+static struct platform_driver clk_ca57_cmu_driver = {
+ .probe = baikal_clk_probe,
+ .remove = baikal_clk_remove,
+ .driver = {
+ .name = "baikal-ca57_cmu",
+ .of_match_table = baikal_clk_of_match,
+ },
+};
+
+static struct platform_driver clk_mali_cmu_driver = {
+ .probe = baikal_clk_probe,
+ .remove = baikal_clk_remove,
+ .driver = {
+ .name = "baikal-mali-cmu",
+ .of_match_table = baikal_clk_of_match,
+ },
+};
+
+module_platform_driver(clk_avlsp_cmu0_driver);
+module_platform_driver(clk_avlsp_cmu1_driver);
+module_platform_driver(clk_xgbe_cmu0_driver);
+module_platform_driver(clk_xgbe_cmu1_driver);
+module_platform_driver(clk_mali_cmu_driver);
+module_platform_driver(clk_ca57_cmu_driver);
+
+MODULE_DESCRIPTION("Clkout driver for the Baikal-M");
+MODULE_AUTHOR("Ekaterina Skachko <Ekaterina.Skachko@baikalelectronics.ru>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:baikal-cmu");
--
2.31.1