msm: clock-8960: Support QDSS clocks

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c
index e875b0a..178f140 100644
--- a/arch/arm/mach-msm/clock-8960.c
+++ b/arch/arm/mach-msm/clock-8960.c
@@ -50,6 +50,7 @@
 #define CLK_HALT_DFAB_STATE_REG			REG(0x2FC8)
 #define CLK_HALT_MSS_SMPSS_MISC_STATE_REG	REG(0x2FDC)
 #define CLK_HALT_SFPB_MISC_STATE_REG		REG(0x2FD8)
+#define CLK_HALT_AFAB_SFAB_STATEB_REG		REG(0x2FC4)
 #define CLK_TEST_REG				REG(0x2FA0)
 #define GSBIn_HCLK_CTL_REG(n)			REG(0x29C0+(0x20*((n)-1)))
 #define GSBIn_QUP_APPS_MD_REG(n)		REG(0x29C8+(0x20*((n)-1)))
@@ -75,6 +76,22 @@
 #define BB_PLL14_STATUS_REG			REG(0x31D8)
 #define PLLTEST_PAD_CFG_REG			REG(0x2FA4)
 #define PMEM_ACLK_CTL_REG			REG(0x25A0)
+#define QDSS_AT_CLK_SRC0_NS_REG			REG(0x2180)
+#define QDSS_AT_CLK_SRC1_NS_REG			REG(0x2184)
+#define QDSS_AT_CLK_SRC_CTL_REG			REG(0x2188)
+#define QDSS_AT_CLK_NS_REG			REG(0x218C)
+#define QDSS_HCLK_CTL_REG			REG(0x22A0)
+#define QDSS_RESETS_REG				REG(0x2260)
+#define QDSS_STM_CLK_CTL_REG			REG(0x2060)
+#define QDSS_TRACECLKIN_CLK_SRC0_NS_REG		REG(0x21A0)
+#define QDSS_TRACECLKIN_CLK_SRC1_NS_REG		REG(0x21A4)
+#define QDSS_TRACECLKIN_CLK_SRC_CTL_REG		REG(0x21A8)
+#define QDSS_TRACECLKIN_CTL_REG			REG(0x21AC)
+#define QDSS_TSCTR_CLK_SRC0_NS_REG		REG(0x21C0)
+#define QDSS_TSCTR_CLK_SRC1_NS_REG		REG(0x21C4)
+#define QDSS_TSCTR_CLK_SRC_CTL_REG		REG(0x21C8)
+#define QDSS_TSCTR_CLK_SRC_CTL_REG		REG(0x21C8)
+#define QDSS_TSCTR_CTL_REG			REG(0x21CC)
 #define RINGOSC_NS_REG				REG(0x2DC0)
 #define RINGOSC_STATUS_REG			REG(0x2DCC)
 #define RINGOSC_TCXO_CTL_REG			REG(0x2DC4)
@@ -1143,6 +1160,261 @@
 static CLK_GSBI_QUP(gsbi11_qup, 11, CLK_HALT_CFPB_STATEC_REG, 15);
 static CLK_GSBI_QUP(gsbi12_qup, 12, CLK_HALT_CFPB_STATEC_REG, 11);
 
+#define F_QDSS(f, s, d, v) \
+	{ \
+		.freq_hz = f, \
+		.src_clk = &s##_clk.c, \
+		.ns_val = NS_DIVSRC(6, 3, d, 2, 0, s##_to_bb_mux), \
+		.sys_vdd = v, \
+	}
+static struct clk_freq_tbl clk_tbl_qdss[] = {
+	F_QDSS(128000000, pll8, 3, LOW),
+	F_QDSS(300000000, pll3, 4, NOMINAL),
+	F_END
+};
+
+struct qdss_bank {
+	const u32 bank_sel_mask;
+	void __iomem *const ns_reg;
+	const u32 ns_mask;
+};
+
+static void set_rate_qdss(struct rcg_clk *clk, struct clk_freq_tbl *nf)
+{
+	const struct qdss_bank *bank = clk->bank_info;
+	u32 reg, bank_sel_mask = bank->bank_sel_mask;
+
+	/* Switch to bank 0 (always sourced from PXO) */
+	reg = readl_relaxed(clk->ns_reg);
+	reg &= ~bank_sel_mask;
+	writel_relaxed(reg, clk->ns_reg);
+	/*
+	 * Wait at least 6 cycles of slowest bank's clock for the glitch-free
+	 * MUX to fully switch sources.
+	 */
+	mb();
+	udelay(1);
+
+	/* Set source and divider */
+	reg = readl_relaxed(bank->ns_reg);
+	reg &= ~bank->ns_mask;
+	reg |= nf->ns_val;
+	writel_relaxed(reg, bank->ns_reg);
+
+	/* Switch to reprogrammed bank */
+	reg = readl_relaxed(clk->ns_reg);
+	reg |= bank_sel_mask;
+	writel_relaxed(reg, clk->ns_reg);
+	/*
+	 * Wait at least 6 cycles of slowest bank's clock for the glitch-free
+	 * MUX to fully switch sources.
+	 */
+	mb();
+	udelay(1);
+}
+
+#define QDSS_CLK_ROOT_ENA BIT(1)
+
+static int qdss_clk_enable(struct clk *c)
+{
+	struct rcg_clk *clk = to_rcg_clk(c);
+	const struct qdss_bank *bank = clk->bank_info;
+	u32 reg, bank_sel_mask = bank->bank_sel_mask;
+	int ret;
+
+	/* Switch to bank 1 */
+	reg = readl_relaxed(clk->ns_reg);
+	reg |= bank_sel_mask;
+	writel_relaxed(reg, clk->ns_reg);
+	/* Enable root */
+	reg |= QDSS_CLK_ROOT_ENA;
+	writel_relaxed(reg, clk->ns_reg);
+
+	ret = rcg_clk_enable(c);
+	if (ret) {
+		/* Disable root */
+		reg = readl_relaxed(clk->ns_reg);
+		reg &= ~QDSS_CLK_ROOT_ENA;
+		writel_relaxed(reg, clk->ns_reg);
+		/* Switch to bank 0 */
+		reg &= ~bank_sel_mask;
+		writel_relaxed(reg, clk->ns_reg);
+	}
+	return ret;
+}
+
+static void qdss_clk_disable(struct clk *c)
+{
+	struct rcg_clk *clk = to_rcg_clk(c);
+	const struct qdss_bank *bank = clk->bank_info;
+	u32 reg, bank_sel_mask = bank->bank_sel_mask;
+
+	rcg_clk_disable(c);
+	/* Disable root */
+	reg = readl_relaxed(clk->ns_reg);
+	reg &= ~QDSS_CLK_ROOT_ENA;
+	writel_relaxed(reg, clk->ns_reg);
+	/* Switch to bank 0 */
+	reg &= ~bank_sel_mask;
+	writel_relaxed(reg, clk->ns_reg);
+}
+
+static void qdss_clk_auto_off(struct clk *c)
+{
+	struct rcg_clk *clk = to_rcg_clk(c);
+	const struct qdss_bank *bank = clk->bank_info;
+	u32 reg, bank_sel_mask = bank->bank_sel_mask;
+
+	rcg_clk_auto_off(c);
+	/* Disable root */
+	reg = readl_relaxed(clk->ns_reg);
+	reg &= ~QDSS_CLK_ROOT_ENA;
+	writel_relaxed(reg, clk->ns_reg);
+	/* Switch to bank 0 */
+	reg &= ~bank_sel_mask;
+	writel_relaxed(reg, clk->ns_reg);
+}
+
+static struct clk_ops clk_ops_qdss = {
+	.enable = qdss_clk_enable,
+	.disable = qdss_clk_disable,
+	.auto_off = qdss_clk_auto_off,
+	.set_rate = rcg_clk_set_rate,
+	.set_min_rate = rcg_clk_set_min_rate,
+	.get_rate = rcg_clk_get_rate,
+	.list_rate = rcg_clk_list_rate,
+	.is_enabled = rcg_clk_is_enabled,
+	.round_rate = rcg_clk_round_rate,
+	.reset = soc_clk_reset,
+	.is_local = local_clk_is_local,
+	.get_parent = rcg_clk_get_parent,
+};
+
+static struct qdss_bank bdiv_info_qdss = {
+	.bank_sel_mask = BIT(0),
+	.ns_reg = QDSS_AT_CLK_SRC1_NS_REG,
+	.ns_mask = BM(6, 0),
+};
+
+static struct rcg_clk qdss_at_clk = {
+	.b = {
+		.ctl_reg = QDSS_AT_CLK_NS_REG,
+		.en_mask = BIT(6),
+		.reset_reg = QDSS_RESETS_REG,
+		.reset_mask = BIT(0),
+		.halt_reg = CLK_HALT_MSS_SMPSS_MISC_STATE_REG,
+		.halt_bit = 10,
+		.halt_check = HALT_VOTED,
+	},
+	.ns_reg = QDSS_AT_CLK_SRC_CTL_REG,
+	.set_rate = set_rate_qdss,
+	.freq_tbl = clk_tbl_qdss,
+	.bank_info = &bdiv_info_qdss,
+	.current_freq = &rcg_dummy_freq,
+	.c = {
+		.dbg_name = "qdss_at_clk",
+		.ops = &clk_ops_qdss,
+		CLK_INIT(qdss_at_clk.c),
+	},
+};
+
+static struct branch_clk qdss_pclkdbg_clk = {
+	.b = {
+		.ctl_reg = QDSS_AT_CLK_NS_REG,
+		.en_mask = BIT(4),
+		.reset_reg = QDSS_RESETS_REG,
+		.reset_mask = BIT(0),
+		.halt_reg = CLK_HALT_MSS_SMPSS_MISC_STATE_REG,
+		.halt_bit = 9,
+		.halt_check = HALT_VOTED
+	},
+	.parent = &qdss_at_clk.c,
+	.c = {
+		.dbg_name = "qdss_pclkdbg_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(qdss_pclkdbg_clk.c),
+	},
+};
+
+static struct qdss_bank bdiv_info_qdss_trace = {
+	.bank_sel_mask = BIT(0),
+	.ns_reg = QDSS_TRACECLKIN_CLK_SRC1_NS_REG,
+	.ns_mask = BM(6, 0),
+};
+
+static struct rcg_clk qdss_traceclkin_clk = {
+	.b = {
+		.ctl_reg = QDSS_TRACECLKIN_CTL_REG,
+		.en_mask = BIT(4),
+		.reset_reg = QDSS_RESETS_REG,
+		.reset_mask = BIT(0),
+		.halt_reg = CLK_HALT_MSS_SMPSS_MISC_STATE_REG,
+		.halt_bit = 8,
+		.halt_check = HALT_VOTED,
+	},
+	.ns_reg = QDSS_TRACECLKIN_CLK_SRC_CTL_REG,
+	.set_rate = set_rate_qdss,
+	.freq_tbl = clk_tbl_qdss,
+	.bank_info = &bdiv_info_qdss_trace,
+	.current_freq = &rcg_dummy_freq,
+	.c = {
+		.dbg_name = "qdss_traceclkin_clk",
+		.ops = &clk_ops_qdss,
+		CLK_INIT(qdss_traceclkin_clk.c),
+	},
+};
+
+static struct clk_freq_tbl clk_tbl_qdss_tsctr[] = {
+	F_QDSS(200000000, pll3, 6, LOW),
+	F_QDSS(400000000, pll3, 3, NOMINAL),
+	F_END
+};
+
+static struct qdss_bank bdiv_info_qdss_tsctr = {
+	.bank_sel_mask = BIT(0),
+	.ns_reg = QDSS_TSCTR_CLK_SRC1_NS_REG,
+	.ns_mask = BM(6, 0),
+};
+
+static struct rcg_clk qdss_tsctr_clk = {
+	.b = {
+		.ctl_reg = QDSS_TSCTR_CTL_REG,
+		.en_mask = BIT(4),
+		.reset_reg = QDSS_RESETS_REG,
+		.reset_mask = BIT(3),
+		.halt_reg = CLK_HALT_MSS_SMPSS_MISC_STATE_REG,
+		.halt_bit = 7,
+		.halt_check = HALT_VOTED,
+	},
+	.ns_reg = QDSS_TSCTR_CLK_SRC_CTL_REG,
+	.set_rate = set_rate_qdss,
+	.freq_tbl = clk_tbl_qdss_tsctr,
+	.bank_info = &bdiv_info_qdss_tsctr,
+	.current_freq = &rcg_dummy_freq,
+	.c = {
+		.dbg_name = "qdss_tsctr_clk",
+		.ops = &clk_ops_qdss,
+		CLK_INIT(qdss_tsctr_clk.c),
+	},
+};
+
+static struct branch_clk qdss_stm_clk = {
+	.b = {
+		.ctl_reg = QDSS_STM_CLK_CTL_REG,
+		.en_mask = BIT(4),
+		.reset_reg = QDSS_RESETS_REG,
+		.reset_mask = BIT(1),
+		.halt_reg = CLK_HALT_AFAB_SFAB_STATEB_REG,
+		.halt_bit = 20,
+		.halt_check = HALT_VOTED,
+	},
+	.c = {
+		.dbg_name = "qdss_stm_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(qdss_stm_clk.c),
+	},
+};
+
 #define F_PDM(f, s, d, v) \
 	{ \
 		.freq_hz = f, \
@@ -1835,6 +2107,23 @@
 	},
 };
 
+static struct branch_clk qdss_p_clk = {
+	.b = {
+		.ctl_reg = QDSS_HCLK_CTL_REG,
+		.en_mask = BIT(4),
+		.halt_reg = CLK_HALT_SFPB_MISC_STATE_REG,
+		.halt_bit = 11,
+		.halt_check = HALT_VOTED,
+		.reset_reg = QDSS_RESETS_REG,
+		.reset_mask = BIT(2),
+	},
+	.c = {
+		.dbg_name = "qdss_p_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(qdss_p_clk.c),
+	},
+};
+
 static struct branch_clk tsif_p_clk = {
 	.b = {
 		.ctl_reg = TSIF_HCLK_CTL_REG,
@@ -3819,6 +4108,7 @@
 static DEFINE_CLK_MEASURE(krait1_m_clk);
 
 static struct measure_sel measure_mux[] = {
+	{ TEST_PER_LS(0x05), &qdss_p_clk.c },
 	{ TEST_PER_LS(0x08), &slimbus_xo_src_clk.c },
 	{ TEST_PER_LS(0x12), &sdc1_p_clk.c },
 	{ TEST_PER_LS(0x13), &sdc1_clk.c },
@@ -3905,6 +4195,11 @@
 	{ TEST_PER_HS(0x2A), &adm0_clk.c },
 	{ TEST_PER_HS(0x34), &ebi1_clk.c },
 	{ TEST_PER_HS(0x34), &ebi1_a_clk.c },
+	{ TEST_PER_HS(0x48), &qdss_at_clk.c },
+	{ TEST_PER_HS(0x49), &qdss_pclkdbg_clk.c },
+	{ TEST_PER_HS(0x4A), &qdss_traceclkin_clk.c },
+	{ TEST_PER_HS(0x4B), &qdss_tsctr_clk.c },
+	{ TEST_PER_HS(0x4F), &qdss_stm_clk.c },
 	{ TEST_PER_HS(0x50), &usb_hsic_hsic_clk.c },
 
 	{ TEST_MM_LS(0x00), &dsi1_byte_clk.c },
@@ -4305,6 +4600,12 @@
 	CLK_LOOKUP("mdp_clk",		mdp_clk.c,		NULL),
 	CLK_LOOKUP("mdp_vsync_clk",	mdp_vsync_clk.c,	NULL),
 	CLK_LOOKUP("lut_mdp",		lut_mdp_clk.c,		NULL),
+	CLK_LOOKUP("qdss_pclk",		qdss_p_clk.c,		NULL),
+	CLK_LOOKUP("qdss_at_clk",	qdss_at_clk.c,		NULL),
+	CLK_LOOKUP("qdss_pclkdbg_clk",	qdss_pclkdbg_clk.c,	NULL),
+	CLK_LOOKUP("qdss_traceclkin_clk", qdss_traceclkin_clk.c, NULL),
+	CLK_LOOKUP("qdss_tsctr_clk",	qdss_tsctr_clk.c,	NULL),
+	CLK_LOOKUP("qdss_stm_clk",	qdss_stm_clk.c,		NULL),
 	CLK_LOOKUP("rot_clk",		rot_clk.c,		NULL),
 	CLK_LOOKUP("tv_src_clk",	tv_src_clk.c,		NULL),
 	CLK_LOOKUP("tv_enc_clk",	tv_enc_clk.c,		NULL),