From: Matt Wagantall <mattw@xxxxxxxxxxxxxx> Use the SoC's ring oscillator hardware to measure the clock rate of locally-controlled clocks. This allows for the development of more comprehensive end-to-end clock tests. A 'measure' debugfs node is created for each clock to perform the measurement and retrieve the result. soc_clk_measure_rate() should *only* be used for debug purposes since it busy-loops while the measurement takes place (~15 ms). Clock rates are in units of Hz. Clocks that don't support rate measurement will return -1. Reviewed-by: Saravana Kannan <skannan@xxxxxxxxxxxxxx> Signed-off-by: Matt Wagantall <mattw@xxxxxxxxxxxxxx> Signed-off-by: Stephen Boyd <sboyd@xxxxxxxxxxxxxx> Signed-off-by: David Brown <davidb@xxxxxxxxxxxxxx> --- arch/arm/mach-msm/clock-7x30.c | 239 ++++++++++++++++++++++++++++ arch/arm/mach-msm/clock-8x60.c | 333 ++++++++++++++++++++++++++++++++++++-- arch/arm/mach-msm/clock-debug.c | 36 ++++- arch/arm/mach-msm/clock.h | 2 +- 4 files changed, 589 insertions(+), 21 deletions(-) diff --git a/arch/arm/mach-msm/clock-7x30.c b/arch/arm/mach-msm/clock-7x30.c index 5b796e8..f73e76f 100644 --- a/arch/arm/mach-msm/clock-7x30.c +++ b/arch/arm/mach-msm/clock-7x30.c @@ -2426,6 +2426,244 @@ out: return rc; } +#ifdef CONFIG_DEBUG_FS + +#define CLK_TEST_2(s) (s) +#define CLK_TEST_HS(s) (0x4000 | ((s) << 8)) +#define CLK_TEST_LS(s) (0x4D40 | (s)) + +struct measure_sel { + u32 test_vector; + struct clk *clk; +}; + +static struct measure_sel measure_mux[] = { + { CLK_TEST_2(0x03), &emdh_p_clk.c }, + { CLK_TEST_2(0x04), &pmdh_p_clk.c }, + { CLK_TEST_2(0x06), &mdp_p_clk.c }, + { CLK_TEST_2(0x07), &lpa_p_clk.c }, + { CLK_TEST_2(0x08), &usb_hs2_p_clk.c }, + { CLK_TEST_2(0x09), &spi_clk.c }, + { CLK_TEST_2(0x0A), &midi_clk.c }, + { CLK_TEST_2(0x0B), &i2c_2_clk.c }, + { CLK_TEST_2(0x0D), &mi2s_m_clk.c }, + { CLK_TEST_2(0x0E), &lpa_core_clk.c }, + { CLK_TEST_2(0x0F), &lpa_codec_clk.c }, + { CLK_TEST_2(0x10), &usb_hs3_p_clk.c }, + { CLK_TEST_2(0x11), &adm_p_clk.c }, + { CLK_TEST_2(0x13), &hdmi_clk.c }, + { CLK_TEST_2(0x14), &usb_hs_core_clk.c }, + { CLK_TEST_2(0x15), &usb_hs2_core_clk.c }, + { CLK_TEST_2(0x16), &usb_hs3_core_clk.c }, + { CLK_TEST_2(0x17), &mi2s_codec_tx_s_clk.c }, + { CLK_TEST_2(0x18), &spi_p_clk.c }, + { CLK_TEST_2(0x1A), &camif_pad_p_clk.c }, + { CLK_TEST_2(0x1C), &qup_i2c_clk.c }, + { CLK_TEST_2(0x1F), &mfc_div2_clk.c }, + { CLK_TEST_2(0x38), &mfc_clk.c }, + + { CLK_TEST_HS(0x00), &adm_clk.c }, + { CLK_TEST_HS(0x01), &mdp_lcdc_pad_pclk_clk.c }, + { CLK_TEST_HS(0x02), &mdp_lcdc_pclk_clk.c }, + { CLK_TEST_HS(0x03), &axi_rotator_clk.c }, + { CLK_TEST_HS(0x07), &axi_li_vg_clk.c }, + { CLK_TEST_HS(0x09), &axi_li_apps_clk.c }, + { CLK_TEST_HS(0x0E), &axi_li_jpeg_clk.c }, + { CLK_TEST_HS(0x0F), &emdh_clk.c }, + { CLK_TEST_HS(0x14), &mdp_clk.c }, + { CLK_TEST_HS(0x15), &pmdh_clk.c }, + { CLK_TEST_HS(0x19), &axi_grp_2d_clk.c }, + { CLK_TEST_HS(0x1A), &axi_li_grp_clk.c }, + { CLK_TEST_HS(0x1B), &axi_li_vfe_clk.c }, + { CLK_TEST_HS(0x1C), &grp_2d_clk.c }, + { CLK_TEST_HS(0x1E), &grp_3d_clk.c }, + { CLK_TEST_HS(0x1F), &imem_clk.c }, + { CLK_TEST_HS(0x20), &jpeg_clk.c }, + { CLK_TEST_HS(0x24), &axi_li_adsp_a_clk.c }, + { CLK_TEST_HS(0x26), &rotator_imem_clk.c }, + { CLK_TEST_HS(0x27), &axi_vpe_clk.c }, + { CLK_TEST_HS(0x2A), &axi_mfc_clk.c }, + { CLK_TEST_HS(0x2B), &axi_mdp_clk.c }, + { CLK_TEST_HS(0x2C), &vpe_clk.c }, + { CLK_TEST_HS(0x30), &vfe_camif_clk.c }, + { CLK_TEST_HS(0x31), &csi0_clk.c }, + { CLK_TEST_HS(0x32), &csi0_vfe_clk.c }, + { CLK_TEST_HS(0x33), &csi0_p_clk.c }, + + { CLK_TEST_LS(0x03), &ce_clk.c }, + { CLK_TEST_LS(0x04), &cam_m_clk.c }, + { CLK_TEST_LS(0x0C), &grp_2d_p_clk.c }, + { CLK_TEST_LS(0x0D), &i2c_clk.c }, + { CLK_TEST_LS(0x0E), &mi2s_codec_rx_m_clk.c }, + { CLK_TEST_LS(0x0F), &mi2s_codec_rx_s_clk.c }, + { CLK_TEST_LS(0x10), &mi2s_codec_tx_m_clk.c }, + { CLK_TEST_LS(0x13), &mdp_vsync_clk.c }, + { CLK_TEST_LS(0x15), &vfe_p_clk.c }, + { CLK_TEST_LS(0x16), &mdc_clk.c }, + { CLK_TEST_LS(0x17), &vfe_mdc_clk.c }, + { CLK_TEST_LS(0x18), &usb_hs_p_clk.c }, + { CLK_TEST_LS(0x1C), &uart1dm_p_clk.c }, + { CLK_TEST_LS(0x1E), &jpeg_p_clk.c }, + { CLK_TEST_LS(0x20), &sdac_clk.c }, + { CLK_TEST_LS(0x21), &sdc1_p_clk.c }, + { CLK_TEST_LS(0x22), &sdc1_clk.c }, + { CLK_TEST_LS(0x23), &sdc2_p_clk.c }, + { CLK_TEST_LS(0x24), &sdc2_clk.c }, + { CLK_TEST_LS(0x25), &tsif_p_clk.c }, + { CLK_TEST_LS(0x26), &sdac_m_clk.c }, + { CLK_TEST_LS(0x27), &grp_3d_p_clk.c }, + { CLK_TEST_LS(0x2A), &tsif_ref_clk.c }, + { CLK_TEST_LS(0x2B), &tv_enc_clk.c }, + { CLK_TEST_LS(0x2C), &tv_dac_clk.c }, + { CLK_TEST_LS(0x2D), &rotator_p_clk.c }, + { CLK_TEST_LS(0x2F), &uart1_clk.c }, + { CLK_TEST_LS(0x30), &uart1dm_clk.c }, + { CLK_TEST_LS(0x31), &uart2_clk.c }, + { CLK_TEST_LS(0x33), &usb_hs2_clk.c }, + { CLK_TEST_LS(0x34), &usb_hs3_clk.c }, + { CLK_TEST_LS(0x35), &mfc_p_clk.c }, + { CLK_TEST_LS(0x36), &vfe_clk.c }, + { CLK_TEST_LS(0x39), &sdc3_p_clk.c }, + { CLK_TEST_LS(0x3A), &sdc3_clk.c }, + { CLK_TEST_LS(0x3B), &sdc4_p_clk.c }, + { CLK_TEST_LS(0x3C), &sdc4_clk.c }, + { CLK_TEST_LS(0x3D), &uart2dm_clk.c }, + { CLK_TEST_LS(0x3E), &uart2dm_p_clk.c }, + { CLK_TEST_LS(0x3F), &usb_hs_clk.c }, +}; + +static struct measure_sel *find_measure_sel(struct clk *clk) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(measure_mux); i++) + if (measure_mux[i].clk == clk) + return &measure_mux[i]; + return NULL; +} + +static int measure_clk_set_parent(struct clk *clk, struct clk *parent) +{ + struct measure_sel *p; + unsigned long flags; + + if (!parent) + return -EINVAL; + + p = find_measure_sel(parent); + if (!p) + return -EINVAL; + + spin_lock_irqsave(&local_clock_reg_lock, flags); + + /* Program test vector. */ + if (p->test_vector <= 0xFF) { + /* Select CLK_TEST_2 */ + writel_relaxed(0x4D40, CLK_TEST_BASE_REG); + writel_relaxed(p->test_vector, CLK_TEST_2_BASE_REG); + } else + writel_relaxed(p->test_vector, CLK_TEST_BASE_REG); + + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + + return 0; +} + +/* Sample clock for 'tcxo4_ticks' reference clock ticks. */ +static u32 run_measurement(unsigned tcxo4_ticks) +{ + /* TCXO4_CNT_EN and RINGOSC_CNT_EN register values. */ + u32 reg_val_enable = readl_relaxed(MISC_CLK_CTL_BASE_REG) | 0x3; + u32 reg_val_disable = reg_val_enable & ~0x3; + + /* Stop counters and set the TCXO4 counter start value. */ + writel_relaxed(reg_val_disable, MISC_CLK_CTL_BASE_REG); + writel_relaxed(tcxo4_ticks, TCXO_CNT_BASE_REG); + + /* Run measurement and wait for completion. */ + writel_relaxed(reg_val_enable, MISC_CLK_CTL_BASE_REG); + while (readl_relaxed(TCXO_CNT_DONE_BASE_REG) == 0) + cpu_relax(); + + /* Stop counters. */ + writel_relaxed(reg_val_disable, MISC_CLK_CTL_BASE_REG); + + return readl_relaxed(RINGOSC_CNT_BASE_REG); +} + +/* Perform a hardware rate measurement for a given clock. + FOR DEBUG USE ONLY: Measurements take ~15 ms! */ +static unsigned measure_clk_get_rate(struct clk *clk) +{ + unsigned long flags; + u32 regval, prph_web_reg_old; + u64 raw_count_short, raw_count_full; + unsigned ret; + + clk_enable(&tcxo_clk.c); + + spin_lock_irqsave(&local_clock_reg_lock, flags); + + /* Enable TCXO4 clock branch and root. */ + prph_web_reg_old = readl_relaxed(PRPH_WEB_NS_BASE_REG); + regval = prph_web_reg_old | BIT(9) | BIT(11); + writel_relaxed(regval, PRPH_WEB_NS_BASE_REG); + + /* + * The ring oscillator counter will not reset if the measured clock + * is not running. To detect this, run a short measurement before + * the full measurement. If the raw results of the two are the same + * then the clock must be off. + */ + + /* Run a short measurement. (~1 ms) */ + raw_count_short = run_measurement(0x1000); + /* Run a full measurement. (~14 ms) */ + raw_count_full = run_measurement(0x10000); + + /* Disable TCXO4 clock branch and root. */ + writel_relaxed(prph_web_reg_old, PRPH_WEB_NS_BASE_REG); + + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + + /* Return 0 if the clock is off. */ + if (raw_count_full == raw_count_short) + ret = 0; + else { + /* Compute rate in Hz. */ + raw_count_full = ((raw_count_full * 10) + 15) * 4800000; + do_div(raw_count_full, ((0x10000 * 10) + 35)); + ret = raw_count_full; + } + + clk_disable(&tcxo_clk.c); + + return ret; +} +#else /* !CONFIG_DEBUG_FS */ +static int measure_clk_set_parent(struct clk *clk, struct clk *parent) +{ + return -EINVAL; +} + +static unsigned measure_clk_get_rate(struct clk *clk) +{ + return 0; +} +#endif /* CONFIG_DEBUG_FS */ + +static struct clk_ops measure_clk_ops = { + .set_parent = measure_clk_set_parent, + .get_rate = measure_clk_get_rate, + .is_local = local_clk_is_local, +}; + +static struct clk measure_clk = { + .dbg_name = "measure_clk", + .ops = &measure_clk_ops, + CLK_INIT(measure_clk), +}; + /* Implementation for clk_set_flags(). */ int soc_clk_set_flags(struct clk *clk, unsigned clk_flags) { @@ -2525,6 +2763,7 @@ static struct clk_local_ownership { { CLK_LOOKUP("pll1_clk", pll1_clk.c, "acpu") }, { CLK_LOOKUP("pll2_clk", pll2_clk.c, "acpu") }, { CLK_LOOKUP("pll3_clk", pll3_clk.c, "acpu") }, + { CLK_LOOKUP("measure", measure_clk, "debug") }, /* PCOM */ { CLK_LOOKUP("adsp_clk", adsp_clk.c, NULL) }, diff --git a/arch/arm/mach-msm/clock-8x60.c b/arch/arm/mach-msm/clock-8x60.c index 170797a..7dac975 100644 --- a/arch/arm/mach-msm/clock-8x60.c +++ b/arch/arm/mach-msm/clock-8x60.c @@ -809,7 +809,7 @@ static struct branch_clk ijpeg_p_clk = { .halt_bit = 9, }, .c = { - .dbg_name = "ijepg_p_clk", + .dbg_name = "ijpeg_p_clk", .ops = &clk_ops_branch, .flags = CLKFLAG_AUTO_OFF, CLK_INIT(ijpeg_p_clk.c), @@ -2991,7 +2991,7 @@ static struct clk_freq_tbl clk_tbl_aif_osr[] = { F_END }; -#define CLK_AIF_OSR(i, ns, md, h_r, tv) \ +#define CLK_AIF_OSR(i, ns, md, h_r) \ struct rcg_clk i##_clk = { \ .b = { \ .ctl_reg = ns, \ @@ -3031,7 +3031,7 @@ static struct clk_freq_tbl clk_tbl_aif_bit[] = { F_END }; -#define CLK_AIF_BIT(i, ns, h_r, tv) \ +#define CLK_AIF_BIT(i, ns, h_r) \ struct rcg_clk i##_clk = { \ .b = { \ .ctl_reg = ns, \ @@ -3053,33 +3053,28 @@ static struct clk_freq_tbl clk_tbl_aif_bit[] = { } static CLK_AIF_OSR(mi2s_osr, LCC_MI2S_NS_REG, LCC_MI2S_MD_REG, - LCC_MI2S_STATUS_REG, TEST_LPA(0x0A)); -static CLK_AIF_BIT(mi2s_bit, LCC_MI2S_NS_REG, LCC_MI2S_STATUS_REG, - TEST_LPA(0x0B)); + LCC_MI2S_STATUS_REG); +static CLK_AIF_BIT(mi2s_bit, LCC_MI2S_NS_REG, LCC_MI2S_STATUS_REG); static CLK_AIF_OSR(codec_i2s_mic_osr, LCC_CODEC_I2S_MIC_NS_REG, - LCC_CODEC_I2S_MIC_MD_REG, LCC_CODEC_I2S_MIC_STATUS_REG, - TEST_LPA(0x0C)); + LCC_CODEC_I2S_MIC_MD_REG, LCC_CODEC_I2S_MIC_STATUS_REG); static CLK_AIF_BIT(codec_i2s_mic_bit, LCC_CODEC_I2S_MIC_NS_REG, - LCC_CODEC_I2S_MIC_STATUS_REG, TEST_LPA(0x0D)); + LCC_CODEC_I2S_MIC_STATUS_REG); static CLK_AIF_OSR(spare_i2s_mic_osr, LCC_SPARE_I2S_MIC_NS_REG, - LCC_SPARE_I2S_MIC_MD_REG, LCC_SPARE_I2S_MIC_STATUS_REG, - TEST_LPA(0x10)); + LCC_SPARE_I2S_MIC_MD_REG, LCC_SPARE_I2S_MIC_STATUS_REG); static CLK_AIF_BIT(spare_i2s_mic_bit, LCC_SPARE_I2S_MIC_NS_REG, - LCC_SPARE_I2S_MIC_STATUS_REG, TEST_LPA(0x11)); + LCC_SPARE_I2S_MIC_STATUS_REG); static CLK_AIF_OSR(codec_i2s_spkr_osr, LCC_CODEC_I2S_SPKR_NS_REG, - LCC_CODEC_I2S_SPKR_MD_REG, LCC_CODEC_I2S_SPKR_STATUS_REG, - TEST_LPA(0x0E)); + LCC_CODEC_I2S_SPKR_MD_REG, LCC_CODEC_I2S_SPKR_STATUS_REG); static CLK_AIF_BIT(codec_i2s_spkr_bit, LCC_CODEC_I2S_SPKR_NS_REG, - LCC_CODEC_I2S_SPKR_STATUS_REG, TEST_LPA(0x0F)); + LCC_CODEC_I2S_SPKR_STATUS_REG); static CLK_AIF_OSR(spare_i2s_spkr_osr, LCC_SPARE_I2S_SPKR_NS_REG, - LCC_SPARE_I2S_SPKR_MD_REG, LCC_SPARE_I2S_SPKR_STATUS_REG, - TEST_LPA(0x12)); + LCC_SPARE_I2S_SPKR_MD_REG, LCC_SPARE_I2S_SPKR_STATUS_REG); static CLK_AIF_BIT(spare_i2s_spkr_bit, LCC_SPARE_I2S_SPKR_NS_REG, - LCC_SPARE_I2S_SPKR_STATUS_REG, TEST_LPA(0x13)); + LCC_SPARE_I2S_SPKR_STATUS_REG); #define F_PCM(f, s, d, m, n, v) \ { \ @@ -3131,10 +3126,312 @@ static struct rcg_clk pcm_clk = { }, }; +#ifdef CONFIG_DEBUG_FS +struct measure_sel { + u32 test_vector; + struct clk *clk; +}; + +static struct measure_sel measure_mux[] = { + { TEST_PER_LS(0x08), &modem_ahb1_p_clk.c }, + { TEST_PER_LS(0x09), &modem_ahb2_p_clk.c }, + { TEST_PER_LS(0x12), &sdc1_p_clk.c }, + { TEST_PER_LS(0x13), &sdc1_clk.c }, + { TEST_PER_LS(0x14), &sdc2_p_clk.c }, + { TEST_PER_LS(0x15), &sdc2_clk.c }, + { TEST_PER_LS(0x16), &sdc3_p_clk.c }, + { TEST_PER_LS(0x17), &sdc3_clk.c }, + { TEST_PER_LS(0x18), &sdc4_p_clk.c }, + { TEST_PER_LS(0x19), &sdc4_clk.c }, + { TEST_PER_LS(0x1A), &sdc5_p_clk.c }, + { TEST_PER_LS(0x1B), &sdc5_clk.c }, + { TEST_PER_LS(0x26), &pmem_clk.c }, + { TEST_PER_LS(0x2B), &ppss_p_clk.c }, + { TEST_PER_LS(0x3D), &gsbi1_p_clk.c }, + { TEST_PER_LS(0x3E), &gsbi1_uart_clk.c }, + { TEST_PER_LS(0x3F), &gsbi1_qup_clk.c }, + { TEST_PER_LS(0x41), &gsbi2_p_clk.c }, + { TEST_PER_LS(0x42), &gsbi2_uart_clk.c }, + { TEST_PER_LS(0x44), &gsbi2_qup_clk.c }, + { TEST_PER_LS(0x45), &gsbi3_p_clk.c }, + { TEST_PER_LS(0x46), &gsbi3_uart_clk.c }, + { TEST_PER_LS(0x48), &gsbi3_qup_clk.c }, + { TEST_PER_LS(0x49), &gsbi4_p_clk.c }, + { TEST_PER_LS(0x4A), &gsbi4_uart_clk.c }, + { TEST_PER_LS(0x4C), &gsbi4_qup_clk.c }, + { TEST_PER_LS(0x4D), &gsbi5_p_clk.c }, + { TEST_PER_LS(0x4E), &gsbi5_uart_clk.c }, + { TEST_PER_LS(0x50), &gsbi5_qup_clk.c }, + { TEST_PER_LS(0x51), &gsbi6_p_clk.c }, + { TEST_PER_LS(0x52), &gsbi6_uart_clk.c }, + { TEST_PER_LS(0x54), &gsbi6_qup_clk.c }, + { TEST_PER_LS(0x55), &gsbi7_p_clk.c }, + { TEST_PER_LS(0x56), &gsbi7_uart_clk.c }, + { TEST_PER_LS(0x58), &gsbi7_qup_clk.c }, + { TEST_PER_LS(0x59), &gsbi8_p_clk.c }, + { TEST_PER_LS(0x5A), &gsbi8_uart_clk.c }, + { TEST_PER_LS(0x5C), &gsbi8_qup_clk.c }, + { TEST_PER_LS(0x5D), &gsbi9_p_clk.c }, + { TEST_PER_LS(0x5E), &gsbi9_uart_clk.c }, + { TEST_PER_LS(0x60), &gsbi9_qup_clk.c }, + { TEST_PER_LS(0x61), &gsbi10_p_clk.c }, + { TEST_PER_LS(0x62), &gsbi10_uart_clk.c }, + { TEST_PER_LS(0x64), &gsbi10_qup_clk.c }, + { TEST_PER_LS(0x65), &gsbi11_p_clk.c }, + { TEST_PER_LS(0x66), &gsbi11_uart_clk.c }, + { TEST_PER_LS(0x68), &gsbi11_qup_clk.c }, + { TEST_PER_LS(0x69), &gsbi12_p_clk.c }, + { TEST_PER_LS(0x6A), &gsbi12_uart_clk.c }, + { TEST_PER_LS(0x6C), &gsbi12_qup_clk.c }, + { TEST_PER_LS(0x7A), &pmic_ssbi2_clk.c }, + { TEST_PER_LS(0x7B), &pmic_arb0_p_clk.c }, + { TEST_PER_LS(0x7C), &pmic_arb1_p_clk.c }, + { TEST_PER_LS(0x7D), &prng_clk.c }, + { TEST_PER_LS(0x7F), &rpm_msg_ram_p_clk.c }, + { TEST_PER_LS(0x80), &adm0_p_clk.c }, + { TEST_PER_LS(0x81), &adm1_p_clk.c }, + { TEST_PER_LS(0x84), &usb_hs1_p_clk.c }, + { TEST_PER_LS(0x85), &usb_hs1_xcvr_clk.c }, + { TEST_PER_LS(0x89), &usb_fs1_p_clk.c }, + { TEST_PER_LS(0x8A), &usb_fs1_sys_clk.c }, + { TEST_PER_LS(0x8B), &usb_fs1_xcvr_clk.c }, + { TEST_PER_LS(0x8C), &usb_fs2_p_clk.c }, + { TEST_PER_LS(0x8D), &usb_fs2_sys_clk.c }, + { TEST_PER_LS(0x8E), &usb_fs2_xcvr_clk.c }, + { TEST_PER_LS(0x8F), &tsif_p_clk.c }, + { TEST_PER_LS(0x91), &tsif_ref_clk.c }, + { TEST_PER_LS(0x93), &ce2_p_clk.c }, + { TEST_PER_LS(0x94), &tssc_clk.c }, + + { TEST_PER_HS(0x2A), &adm0_clk.c }, + { TEST_PER_HS(0x2B), &adm1_clk.c }, + + { TEST_MM_LS(0x00), &dsi_byte_clk.c }, + { TEST_MM_LS(0x01), &pixel_lcdc_clk.c }, + { TEST_MM_LS(0x04), &pixel_mdp_clk.c }, + { TEST_MM_LS(0x06), &_p_clk.c }, + { TEST_MM_LS(0x07), &csi0_p_clk.c }, + { TEST_MM_LS(0x08), &csi1_p_clk.c }, + { TEST_MM_LS(0x09), &dsi_m_p_clk.c }, + { TEST_MM_LS(0x0A), &dsi_s_p_clk.c }, + { TEST_MM_LS(0x0C), &gfx2d0_p_clk.c }, + { TEST_MM_LS(0x0D), &gfx2d1_p_clk.c }, + { TEST_MM_LS(0x0E), &gfx3d_p_clk.c }, + { TEST_MM_LS(0x0F), &hdmi_m_p_clk.c }, + { TEST_MM_LS(0x10), &hdmi_s_p_clk.c }, + { TEST_MM_LS(0x11), &ijpeg_p_clk.c }, + { TEST_MM_LS(0x12), &imem_p_clk.c }, + { TEST_MM_LS(0x13), &jpegd_p_clk.c }, + { TEST_MM_LS(0x14), &mdp_p_clk.c }, + { TEST_MM_LS(0x16), &rot_p_clk.c }, + { TEST_MM_LS(0x18), &smmu_p_clk.c }, + { TEST_MM_LS(0x19), &tv_enc_p_clk.c }, + { TEST_MM_LS(0x1A), &vcodec_p_clk.c }, + { TEST_MM_LS(0x1B), &vfe_p_clk.c }, + { TEST_MM_LS(0x1C), &vpe_p_clk.c }, + { TEST_MM_LS(0x1D), &cam_clk.c }, + { TEST_MM_LS(0x1F), &hdmi_app_clk.c }, + { TEST_MM_LS(0x20), &mdp_vsync_clk.c }, + { TEST_MM_LS(0x21), &tv_dac_clk.c }, + { TEST_MM_LS(0x22), &tv_enc_clk.c }, + { TEST_MM_LS(0x23), &dsi_esc_clk.c }, + + { TEST_MM_HS(0x00), &csi0_clk.c }, + { TEST_MM_HS(0x01), &csi1_clk.c }, + { TEST_MM_HS(0x03), &csi0_vfe_clk.c }, + { TEST_MM_HS(0x04), &csi1_vfe_clk.c }, + { TEST_MM_HS(0x05), &ijpeg_clk.c }, + { TEST_MM_HS(0x06), &vfe_clk.c }, + { TEST_MM_HS(0x07), &gfx2d0_clk.c }, + { TEST_MM_HS(0x08), &gfx2d1_clk.c }, + { TEST_MM_HS(0x09), &gfx3d_clk.c }, + { TEST_MM_HS(0x0A), &jpegd_clk.c }, + { TEST_MM_HS(0x0B), &vcodec_clk.c }, + { TEST_MM_HS(0x11), &gmem_axi_clk.c }, + { TEST_MM_HS(0x12), &ijpeg_axi_clk.c }, + { TEST_MM_HS(0x13), &imem_axi_clk.c }, + { TEST_MM_HS(0x14), &jpegd_axi_clk.c }, + { TEST_MM_HS(0x15), &mdp_axi_clk.c }, + { TEST_MM_HS(0x17), &vcodec_axi_clk.c }, + { TEST_MM_HS(0x18), &vfe_axi_clk.c }, + { TEST_MM_HS(0x1A), &mdp_clk.c }, + { TEST_MM_HS(0x1B), &rot_clk.c }, + { TEST_MM_HS(0x1C), &vpe_clk.c }, + { TEST_MM_HS(0x1E), &hdmi_tv_clk.c }, + { TEST_MM_HS(0x1F), &mdp_tv_clk.c }, + + { TEST_LPA(0x0A), &mi2s_osr_clk.c }, + { TEST_LPA(0x0B), &mi2s_bit_clk.c }, + { TEST_LPA(0x0C), &codec_i2s_mic_osr_clk.c }, + { TEST_LPA(0x0D), &codec_i2s_mic_bit_clk.c }, + { TEST_LPA(0x0E), &codec_i2s_spkr_osr_clk.c }, + { TEST_LPA(0x0F), &codec_i2s_spkr_bit_clk.c }, + { TEST_LPA(0x10), &spare_i2s_mic_osr_clk.c }, + { TEST_LPA(0x11), &spare_i2s_mic_bit_clk.c }, + { TEST_LPA(0x12), &spare_i2s_spkr_osr_clk.c }, + { TEST_LPA(0x13), &spare_i2s_spkr_bit_clk.c }, + { TEST_LPA(0x14), &pcm_clk.c }, +}; + +static struct measure_sel *find_measure_sel(struct clk *clk) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(measure_mux); i++) + if (measure_mux[i].clk == clk) + return &measure_mux[i]; + return NULL; +} + +static int measure_clk_set_parent(struct clk *clk, struct clk *parent) +{ + int ret = 0; + u32 clk_sel; + struct measure_sel *p; + unsigned long flags; + + if (!parent) + return -EINVAL; + + p = find_measure_sel(parent); + if (!p) + return -EINVAL; + + spin_lock_irqsave(&local_clock_reg_lock, flags); + + /* Program the test vector. */ + clk_sel = p->test_vector & TEST_CLK_SEL_MASK; + switch (p->test_vector >> TEST_TYPE_SHIFT) { + case TEST_TYPE_PER_LS: + writel_relaxed(0x4030D00|BVAL(7, 0, clk_sel), CLK_TEST_REG); + break; + case TEST_TYPE_PER_HS: + writel_relaxed(0x4020000|BVAL(16, 10, clk_sel), CLK_TEST_REG); + break; + case TEST_TYPE_MM_LS: + writel_relaxed(0x4030D97, CLK_TEST_REG); + writel_relaxed(BVAL(6, 1, clk_sel)|BIT(0), DBG_CFG_REG_LS_REG); + break; + case TEST_TYPE_MM_HS: + writel_relaxed(0x402B800, CLK_TEST_REG); + writel_relaxed(BVAL(6, 1, clk_sel)|BIT(0), DBG_CFG_REG_HS_REG); + break; + case TEST_TYPE_LPA: + writel_relaxed(0x4030D98, CLK_TEST_REG); + writel_relaxed(BVAL(6, 1, clk_sel)|BIT(0), + LCC_CLK_LS_DEBUG_CFG_REG); + break; + default: + ret = -EPERM; + } + /* Make sure test vector is set before starting measurements. */ + mb(); + + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + + return ret; +} + +/* Sample clock for 'ticks' reference clock ticks. */ +static u32 run_measurement(unsigned ticks) +{ + /* Stop counters and set the XO4 counter start value. */ + writel_relaxed(0x0, RINGOSC_TCXO_CTL_REG); + writel_relaxed(ticks, RINGOSC_TCXO_CTL_REG); + + /* Wait for timer to become ready. */ + while ((readl_relaxed(RINGOSC_STATUS_REG) & BIT(25)) != 0) + cpu_relax(); + + /* Run measurement and wait for completion. */ + writel_relaxed(BIT(20)|ticks, RINGOSC_TCXO_CTL_REG); + while ((readl_relaxed(RINGOSC_STATUS_REG) & BIT(25)) == 0) + cpu_relax(); + + /* Stop counters. */ + writel_relaxed(0x0, RINGOSC_TCXO_CTL_REG); + + /* Return measured ticks. */ + return readl_relaxed(RINGOSC_STATUS_REG) & BM(24, 0); +} + +/* Perform a hardware rate measurement for a given clock. + FOR DEBUG USE ONLY: Measurements take ~15 ms! */ +static unsigned measure_clk_get_rate(struct clk *clk) +{ + unsigned long flags; + u32 pdm_reg_backup, ringosc_reg_backup; + u64 raw_count_short, raw_count_full; + unsigned ret; + + spin_lock_irqsave(&local_clock_reg_lock, flags); + + /* Enable CXO/4 and RINGOSC branch and root. */ + pdm_reg_backup = readl_relaxed(PDM_CLK_NS_REG); + ringosc_reg_backup = readl_relaxed(RINGOSC_NS_REG); + writel_relaxed(0x2898, PDM_CLK_NS_REG); + writel_relaxed(0xA00, RINGOSC_NS_REG); + + /* + * The ring oscillator counter will not reset if the measured clock + * is not running. To detect this, run a short measurement before + * the full measurement. If the raw results of the two are the same + * then the clock must be off. + */ + + /* Run a short measurement. (~1 ms) */ + raw_count_short = run_measurement(0x1000); + /* Run a full measurement. (~14 ms) */ + raw_count_full = run_measurement(0x10000); + + writel_relaxed(ringosc_reg_backup, RINGOSC_NS_REG); + writel_relaxed(pdm_reg_backup, PDM_CLK_NS_REG); + + /* Return 0 if the clock is off. */ + if (raw_count_full == raw_count_short) + ret = 0; + else { + /* Compute rate in Hz. */ + raw_count_full = ((raw_count_full * 10) + 15) * 4800000; + do_div(raw_count_full, ((0x10000 * 10) + 35)); + ret = raw_count_full; + } + + /* Route dbg_hs_clk to PLLTEST. 300mV single-ended amplitude. */ + writel_relaxed(0x3CF8, PLLTEST_PAD_CFG_REG); + spin_unlock_irqrestore(&local_clock_reg_lock, flags); + + return ret; +} +#else /* !CONFIG_DEBUG_FS */ +static int measure_clk_set_parent(struct clk *clk, struct clk *parent) +{ + return -EINVAL; +} + +static unsigned measure_clk_get_rate(struct clk *clk) +{ + return 0; +} +#endif /* CONFIG_DEBUG_FS */ + +static struct clk_ops measure_clk_ops = { + .set_parent = measure_clk_set_parent, + .get_rate = measure_clk_get_rate, + .is_local = local_clk_is_local, +}; + +static struct clk measure_clk = { + .dbg_name = "measure_clk", + .ops = &measure_clk_ops, + CLK_INIT(measure_clk), +}; + struct clk_lookup msm_clocks_8x60[] = { CLK_LOOKUP("cxo", cxo_clk.c, NULL), CLK_LOOKUP("pll4", pll4_clk.c, NULL), CLK_LOOKUP("pll4", pll4_clk.c, "peripheral-reset"), + CLK_LOOKUP("measure", measure_clk, "debug"), CLK_LOOKUP("gsbi_uart_clk", gsbi1_uart_clk.c, NULL), CLK_LOOKUP("gsbi_uart_clk", gsbi2_uart_clk.c, NULL), diff --git a/arch/arm/mach-msm/clock-debug.c b/arch/arm/mach-msm/clock-debug.c index 78b7b6c..1a08a9a 100644 --- a/arch/arm/mach-msm/clock-debug.c +++ b/arch/arm/mach-msm/clock-debug.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2007-2010, Code Aurora Forum. All rights reserved. + * Copyright (c) 2007-2011, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -50,6 +50,23 @@ static int clock_debug_rate_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(clock_rate_fops, clock_debug_rate_get, clock_debug_rate_set, "%llu\n"); +static struct clk *measure; + +static int clock_debug_measure_get(void *data, u64 *val) +{ + int ret; + struct clk *clock = data; + + ret = clk_set_parent(measure, clock); + if (!ret) + *val = clk_get_rate(measure); + + return ret; +} + +DEFINE_SIMPLE_ATTRIBUTE(clock_measure_fops, clock_debug_measure_get, + NULL, "%lld\n"); + static int clock_debug_enable_set(void *data, u64 val) { struct clk *clock = data; @@ -96,10 +113,19 @@ static struct dentry *debugfs_base; int __init clock_debug_init(void) { + int ret = 0; + debugfs_base = debugfs_create_dir("clk", NULL); if (!debugfs_base) return -ENOMEM; - return 0; + + measure = clk_get_sys("debug", "measure"); + if (IS_ERR(measure)) { + ret = PTR_ERR(measure); + measure = NULL; + } + + return ret; } static int list_rates_show(struct seq_file *m, void *unused) @@ -153,6 +179,12 @@ int __init clock_debug_add(struct clk *clock) &clock_local_fops)) goto error; + if (measure && + !clk_set_parent(measure, clock) && + !debugfs_create_file("measure", S_IRUGO, clk_dir, clock, + &clock_measure_fops)) + goto error; + if (clock->ops->list_rate) if (!debugfs_create_file("list_rates", S_IRUGO, clk_dir, clock, &list_rates_fops)) diff --git a/arch/arm/mach-msm/clock.h b/arch/arm/mach-msm/clock.h index 26b52f2..32b164c 100644 --- a/arch/arm/mach-msm/clock.h +++ b/arch/arm/mach-msm/clock.h @@ -1,7 +1,7 @@ /* arch/arm/mach-msm/clock.h * * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2007-2010, Code Aurora Forum. All rights reserved. + * Copyright (c) 2007-2011, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and -- Sent by an employee of the Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum. -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html