ARC: HSDK: CGU: Update AXI, TUN, ARC clock options

Update default AXI, TUN, ARC clock set options:
instead of changing only IDIV divider settings adjust also domain PLL
settings.

Add support of TUN_ROM and TUN_PWM clocks (subclocks of TUNN_PLL)

Signed-off-by: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>
Signed-off-by: Alexey Brodkin <abrodkin@synopsys.com>
diff --git a/drivers/clk/clk-hsdk-cgu.c b/drivers/clk/clk-hsdk-cgu.c
index c80f90e..d9da477 100644
--- a/drivers/clk/clk-hsdk-cgu.c
+++ b/drivers/clk/clk-hsdk-cgu.c
@@ -42,7 +42,9 @@
  *            |-->| TUNNEL PLL |
  *            |   --------------
  *            |        |
- *            |        |-->|CGU_TUN_IDIV|----------->
+ *            |        |-->|CGU_TUN_IDIV_TUN|----------->
+ *            |        |-->|CGU_TUN_IDIV_ROM|----------->
+ *            |        |-->|CGU_TUN_IDIV_PWM|----------->
  *            |
  *            |   ------------
  *            |-->| HDMI PLL |
@@ -60,7 +62,9 @@
 DECLARE_GLOBAL_DATA_PTR;
 
 #define CGU_ARC_IDIV		0x080
-#define CGU_TUN_IDIV		0x380
+#define CGU_TUN_IDIV_TUN	0x380
+#define CGU_TUN_IDIV_ROM	0x390
+#define CGU_TUN_IDIV_PWM	0x3A0
 #define CGU_HDMI_IDIV_APB	0x480
 #define CGU_SYS_IDIV_APB	0x180
 #define CGU_SYS_IDIV_AXI	0x190
@@ -114,8 +118,68 @@
 #define CREG_CORE_IF_CLK_DIV_1		0x0
 #define CREG_CORE_IF_CLK_DIV_2		0x1
 
+#define MIN_PLL_RATE			100000000 /* 100 MHz */
 #define PARENT_RATE			33333333 /* fixed clock - xtal */
-#define CGU_MAX_CLOCKS			24
+#define CGU_MAX_CLOCKS			26
+
+#define CGU_SYS_CLOCKS			16
+#define MAX_AXI_CLOCKS			4
+
+#define CGU_TUN_CLOCKS			3
+#define MAX_TUN_CLOCKS			6
+
+struct hsdk_tun_idiv_cfg {
+	u32 oft;
+	u8  val[MAX_TUN_CLOCKS];
+};
+
+struct hsdk_tun_clk_cfg {
+	const u32 clk_rate[MAX_TUN_CLOCKS];
+	const u32 pll_rate[MAX_TUN_CLOCKS];
+	const struct hsdk_tun_idiv_cfg idiv[CGU_TUN_CLOCKS];
+};
+
+static const struct hsdk_tun_clk_cfg tun_clk_cfg = {
+	{ 25000000,  50000000,  75000000,  100000000, 125000000, 150000000 },
+	{ 600000000, 600000000, 600000000, 600000000, 700000000, 600000000 }, {
+	{ CGU_TUN_IDIV_TUN,	{ 24,	12,	8,	6,	6,	4 } },
+	{ CGU_TUN_IDIV_ROM,	{ 4,	4,	4,	4,	5,	4 } },
+	{ CGU_TUN_IDIV_PWM,	{ 8,	8,	8,	8,	10,	8 } }
+	}
+};
+
+struct hsdk_sys_idiv_cfg {
+	u32 oft;
+	u8  val[MAX_AXI_CLOCKS];
+};
+
+struct hsdk_axi_clk_cfg {
+	const u32 clk_rate[MAX_AXI_CLOCKS];
+	const u32 pll_rate[MAX_AXI_CLOCKS];
+	const struct hsdk_sys_idiv_cfg idiv[CGU_SYS_CLOCKS];
+};
+
+static const struct hsdk_axi_clk_cfg axi_clk_cfg = {
+	{ 200000000,	400000000,	600000000,	800000000 },
+	{ 800000000,	800000000,	600000000,	800000000 }, {
+	{ CGU_SYS_IDIV_APB,	 { 4,	4,	3,	4 } },	/* APB */
+	{ CGU_SYS_IDIV_AXI,	 { 4,	2,	1,	1 } },	/* AXI */
+	{ CGU_SYS_IDIV_ETH,	 { 2,	2,	2,	2 } },	/* ETH */
+	{ CGU_SYS_IDIV_USB,	 { 2,	2,	2,	2 } },	/* USB */
+	{ CGU_SYS_IDIV_SDIO,	 { 2,	2,	2,	2 } },	/* SDIO */
+	{ CGU_SYS_IDIV_HDMI,	 { 2,	2,	2,	2 } },	/* HDMI */
+	{ CGU_SYS_IDIV_GFX_CORE, { 1,	1,	1,	1 } },	/* GPU-CORE */
+	{ CGU_SYS_IDIV_GFX_DMA,	 { 2,	2,	2,	2 } },	/* GPU-DMA */
+	{ CGU_SYS_IDIV_GFX_CFG,	 { 4,	4,	3,	4 } },	/* GPU-CFG */
+	{ CGU_SYS_IDIV_DMAC_CORE,{ 2,	2,	2,	2 } },	/* DMAC-CORE */
+	{ CGU_SYS_IDIV_DMAC_CFG, { 4,	4,	3,	4 } },	/* DMAC-CFG */
+	{ CGU_SYS_IDIV_SDIO_REF, { 8,	8,	6,	8 } },	/* SDIO-REF */
+	{ CGU_SYS_IDIV_SPI_REF,	 { 24,	24,	18,	24 } },	/* SPI-REF */
+	{ CGU_SYS_IDIV_I2C_REF,	 { 4,	4,	3,	4 } },	/* I2C-REF */
+	{ CGU_SYS_IDIV_UART_REF, { 24,	24,	18,	24 } },	/* UART-REF */
+	{ CGU_SYS_IDIV_EBI_REF,	 { 16,	16,	12,	16 } }	/* EBI-REF */
+	}
+};
 
 struct hsdk_pll_cfg {
 	u32 rate;
@@ -201,6 +265,9 @@
 };
 
 static ulong idiv_set(struct clk *, ulong);
+static ulong cpu_clk_set(struct clk *, ulong);
+static ulong axi_clk_set(struct clk *, ulong);
+static ulong tun_clk_set(struct clk *, ulong);
 static ulong idiv_get(struct clk *);
 static int idiv_off(struct clk *);
 static ulong pll_set(struct clk *, ulong);
@@ -218,11 +285,11 @@
 
 static const struct hsdk_cgu_clock_map clock_map[] = {
 	{ CGU_ARC_PLL, 0, 0, &core_pll_dat, pll_get, pll_set, NULL },
-	{ CGU_ARC_PLL, 0, CGU_ARC_IDIV, &core_pll_dat, idiv_get, idiv_set, idiv_off },
+	{ CGU_ARC_PLL, 0, CGU_ARC_IDIV, &core_pll_dat, idiv_get, cpu_clk_set, idiv_off },
 	{ CGU_DDR_PLL, 0, 0, &sdt_pll_dat, pll_get, pll_set, NULL },
 	{ CGU_SYS_PLL, 0, 0, &sdt_pll_dat, pll_get, pll_set, NULL },
 	{ CGU_SYS_PLL, 0, CGU_SYS_IDIV_APB, &sdt_pll_dat, idiv_get, idiv_set, idiv_off },
-	{ CGU_SYS_PLL, 0, CGU_SYS_IDIV_AXI, &sdt_pll_dat, idiv_get, idiv_set, idiv_off },
+	{ CGU_SYS_PLL, 0, CGU_SYS_IDIV_AXI, &sdt_pll_dat, idiv_get, axi_clk_set, idiv_off },
 	{ CGU_SYS_PLL, 0, CGU_SYS_IDIV_ETH, &sdt_pll_dat, idiv_get, idiv_set, idiv_off },
 	{ CGU_SYS_PLL, 0, CGU_SYS_IDIV_USB, &sdt_pll_dat, idiv_get, idiv_set, idiv_off },
 	{ CGU_SYS_PLL, 0, CGU_SYS_IDIV_SDIO, &sdt_pll_dat, idiv_get, idiv_set, idiv_off },
@@ -238,7 +305,9 @@
 	{ CGU_SYS_PLL, 0, CGU_SYS_IDIV_UART_REF, &sdt_pll_dat, idiv_get, idiv_set, idiv_off },
 	{ CGU_SYS_PLL, 0, CGU_SYS_IDIV_EBI_REF, &sdt_pll_dat, idiv_get, idiv_set, idiv_off },
 	{ CGU_TUN_PLL, 0, 0, &sdt_pll_dat, pll_get, pll_set, NULL },
-	{ CGU_TUN_PLL, 0, CGU_TUN_IDIV, &sdt_pll_dat, idiv_get, idiv_set, idiv_off },
+	{ CGU_TUN_PLL, 0, CGU_TUN_IDIV_TUN, &sdt_pll_dat, idiv_get, tun_clk_set, idiv_off },
+	{ CGU_TUN_PLL, 0, CGU_TUN_IDIV_ROM, &sdt_pll_dat, idiv_get, idiv_set, idiv_off },
+	{ CGU_TUN_PLL, 0, CGU_TUN_IDIV_PWM, &sdt_pll_dat, idiv_get, idiv_set, idiv_off },
 	{ CGU_HDMI_PLL, 0, 0, &hdmi_pll_dat, pll_get, pll_set, NULL },
 	{ CGU_HDMI_PLL, 0, CGU_HDMI_IDIV_APB, &hdmi_pll_dat, idiv_get, idiv_set, idiv_off }
 };
@@ -453,6 +522,94 @@
 	return parent_rate / div_factor;
 }
 
+/* Special behavior: wen we set this clock we set both idiv and pll */
+static ulong cpu_clk_set(struct clk *sclk, ulong rate)
+{
+	ulong ret;
+
+	ret = pll_set(sclk, rate);
+	idiv_set(sclk, rate);
+
+	return ret;
+}
+
+/* Special behavior: wen we set this clock we set both idiv and pll and all pll dividers */
+static ulong axi_clk_set(struct clk *sclk, ulong rate)
+{
+	struct hsdk_cgu_clk *clk = dev_get_priv(sclk->dev);
+	ulong pll_rate;
+	int i, freq_idx = -1;
+	ulong ret = 0;
+
+	pll_rate = pll_get(sclk);
+
+	for (i = 0; i < MAX_AXI_CLOCKS; i++) {
+		if (axi_clk_cfg.clk_rate[i] == rate) {
+			freq_idx = i;
+			break;
+		}
+	}
+
+	if (freq_idx < 0) {
+		pr_err("axi clk: invalid rate=%ld Hz\n", rate);
+		return -EINVAL;
+	}
+
+	/* configure PLL before dividers */
+	if (axi_clk_cfg.pll_rate[freq_idx] < pll_rate)
+		ret = pll_set(sclk, axi_clk_cfg.pll_rate[freq_idx]);
+
+	/* configure SYS dividers */
+	for (i = 0; i < CGU_SYS_CLOCKS; i++) {
+		clk->idiv_regs = clk->cgu_regs + axi_clk_cfg.idiv[i].oft;
+		hsdk_idiv_write(clk, axi_clk_cfg.idiv[i].val[freq_idx]);
+	}
+
+	/* configure PLL after dividers */
+	if (axi_clk_cfg.pll_rate[freq_idx] >= pll_rate)
+		ret = pll_set(sclk, axi_clk_cfg.pll_rate[freq_idx]);
+
+	return ret;
+}
+
+static ulong tun_clk_set(struct clk *sclk, ulong rate)
+{
+	struct hsdk_cgu_clk *clk = dev_get_priv(sclk->dev);
+	ulong pll_rate;
+	int i, freq_idx = -1;
+	ulong ret = 0;
+
+	pll_rate = pll_get(sclk);
+
+	for (i = 0; i < MAX_TUN_CLOCKS; i++) {
+		if (tun_clk_cfg.clk_rate[i] == rate) {
+			freq_idx = i;
+			break;
+		}
+	}
+
+	if (freq_idx < 0) {
+		pr_err("tun clk: invalid rate=%ld Hz\n", rate);
+		return -EINVAL;
+	}
+
+	/* configure PLL before dividers */
+	if (tun_clk_cfg.pll_rate[freq_idx] < pll_rate)
+		ret = pll_set(sclk, tun_clk_cfg.pll_rate[freq_idx]);
+
+	/* configure SYS dividers */
+	for (i = 0; i < CGU_TUN_CLOCKS; i++) {
+		clk->idiv_regs = clk->cgu_regs + tun_clk_cfg.idiv[i].oft;
+		hsdk_idiv_write(clk, tun_clk_cfg.idiv[i].val[freq_idx]);
+	}
+
+	/* configure PLL after dividers */
+	if (tun_clk_cfg.pll_rate[freq_idx] >= pll_rate)
+		ret = pll_set(sclk, tun_clk_cfg.pll_rate[freq_idx]);
+
+	return ret;
+}
+
 static ulong idiv_set(struct clk *sclk, ulong rate)
 {
 	struct hsdk_cgu_clk *clk = dev_get_priv(sclk->dev);