void restart_dvfs_per() { u32 reg; if (clk_get_rate(main_bus_clk) == 24000000) cur_setpoint = 2; else cur_setpoint = 0; /* Remove weights. */ __raw_writel(0, MXC_DVFS_PER_LTR3); reg = __raw_readl(MXC_DVFS_PER_PMCR1); reg &= ~0xf; __raw_writel(reg, MXC_DVFS_PER_PMCR1); dvfs_per_load_config(); /*Unmask GPC1 IRQ */ reg = __raw_readl(MXC_GPC_CNTR); reg &= ~MXC_GPCCNTR_GPCIRQM; __raw_writel(reg, MXC_GPC_CNTR); reg = __raw_readl(MXC_DVFS_PER_PMCR0); /* Enable FVFS interrupt */ /* FSVAIM=0 */ reg &= ~MXC_DVFSPMCR0_FSVAI_MASK; reg |= FSVAI_FREQ_NOCHANGE; reg = (reg & ~MXC_DVFSPMCR0_FSVAIM); reg |= MXC_DVFSPMCR0_DVFEN; __raw_writel(reg, MXC_DVFS_PER_PMCR0); dvfs_per_is_active = 1; }
/*! * This function is called for module initialization. * It sets up the DVFS hardware. * It sets default values for DVFS thresholds and counters. The default * values was chosen from a set of different reasonable values. They was tested * and the default values in the driver gave the best results. * More work should be done to find optimal values. * * @return 0 if successful; non-zero otherwise. * */ static int init_dvfs_per_controller(void) { u32 reg; reg = __raw_readl(dvfsper_plt_data->membase + MXC_DVFS_PER_LTR0); /* DIV3CLK */ reg &= ~dvfsper_plt_data->div3_mask; reg |= (dvfsper_plt_data->div3_div << dvfsper_plt_data->div3_offset); __raw_writel(reg, dvfsper_plt_data->membase + MXC_DVFS_PER_LTR0); reg = __raw_readl(dvfsper_plt_data->membase + MXC_DVFS_PER_LTR1); /* Set load tracking buffer register source */ reg &= ~MXC_DVFSLTR1_LTBRSR; reg |= MXC_DVFSLTR1_LTBRSR; reg &= ~MXC_DVFSLTR1_LTBRSH; __raw_writel(reg, dvfsper_plt_data->membase + MXC_DVFS_PER_LTR1); /* Enable all the peripheral signals, but VPU and IPU panic*/ __raw_writel(0x30000, dvfsper_plt_data->membase + MXC_DVFS_PER_PMCR1); /* Disable weighted load tracking signals */ __raw_writel(0, dvfsper_plt_data->membase + MXC_DVFS_PER_LTR3); reg = __raw_readl(dvfsper_plt_data->membase + MXC_DVFS_PER_PMCR0); reg &= ~MXC_DVFSPMCR0_DVFEV; reg |= MXC_DVFSPMCR0_LBMI; __raw_writel(reg, dvfsper_plt_data->membase + MXC_DVFS_PER_PMCR0); /* DVFS loading config */ dvfs_per_load_config(); return 0; }
static void dvfs_per_handler(struct work_struct *work) { u32 fsvai; u32 reg; u32 ret; unsigned long flags; int retry = 20; /* Check DVFS frequency adjustment interrupt status */ reg = __raw_readl(dvfsper_plt_data->membase + MXC_DVFS_PER_PMCR0); fsvai = (reg & MXC_DVFSPMCR0_FSVAI_MASK) >> MXC_DVFSPMCR0_FSVAI_OFFSET; /* Check FSVAI, FSVAI=0 is error */ if (fsvai == FSVAI_FREQ_NOCHANGE) { /* Do nothing. Freq change is not required */ goto END; } #if DVFS_PER_DEBUG dump_dvfs_per_regs(); #endif /* If FSVAI indicate freq down. */ if (fsvai == FSVAI_FREQ_DECREASE) { if (cpu_is_mx51()) { /*Change the DDR freq to 133Mhz. */ clk_set_rate(ddr_hf_clk, clk_round_rate(ddr_hf_clk, 133000000)); } #ifndef DVFS_SW_WORKAROUND spin_lock_irqsave(&mxc_dvfs_per_lock, flags); reg = __raw_readl(dvfsper_plt_data->membase + MXC_DVFS_PER_PMCR0); reg &= ~MXC_DVFSPMCR0_UDCS; __raw_writel(reg, dvfsper_plt_data->membase + MXC_DVFS_PER_PMCR0); /* Set the peripheral divider */ reg = __raw_readl(dvfsper_plt_data->gpc_cntr_reg_addr); reg &= ~(MXC_GPCCNTR_ADU_MASK | MXC_GPCCNTR_FUPD_MASK); reg |= MXC_GPCCNTR_FUPD; __raw_writel(reg, dvfsper_plt_data->gpc_cntr_reg_addr); reg = __raw_readl(dvfsper_plt_data->gpc_vcr_reg_addr); reg &= ~(MXC_GPCVCR_VINC_MASK | MXC_GPCVCR_VCNTU_MASK | MXC_GPCVCR_VCNT_MASK); reg |= (1 << MXC_GPCVCR_VCNTU_OFFSET) | (1 << MXC_GPCVCR_VCNT_OFFSET); __raw_writel(reg, dvfsper_plt_data->gpc_vcr_reg_addr); reg = __raw_readl(dvfsper_plt_data->gpc_cntr_reg_addr); reg |= MXC_GPCCNTR_STRT; __raw_writel(reg, dvfsper_plt_data->gpc_cntr_reg_addr); retry = 10; while ((__raw_readl( dvfsper_plt_data->gpc_cntr_reg_addr) & 0x4000) && retry > 0) { udelay(10); retry--; } spin_unlock_irqrestore(&mxc_dvfs_per_lock, flags); #else /*Set the frequencies manually */ rate = clk_get_rate(axi_b_clk); clk_set_rate(axi_b_clk, clk_round_rate(axi_b_clk, rate/2)); rate = clk_get_rate(ahb_clk); clk_set_rate(ahb_clk, clk_round_rate(ahb_clk, rate/2)); #endif dvfs_per_low_freq = 1; if (clk_get_rate(main_bus_clk) == LOW_BUS_FREQ) { cur_setpoint = 2; } else { #if defined(CONFIG_ARCH_MX37) dptc_suspend(DPTC_LP_ID); #endif cur_setpoint = 1; #ifndef DVFS_SW_WORKAROUND clk_set_parent(main_bus_clk, clk_get(NULL, "pll2")); #endif } #ifndef DVFS_SW_WORKAROUND /* Drop the LP domain voltage */ ret = regulator_set_voltage(lp_regulator, dvfsper_plt_data->lp_low, dvfsper_plt_data->lp_low); if (ret < 0) { printk(KERN_DEBUG "COULD NOT SET LP VOLTAGE\n"); return; } udelay(100); #endif dvfs_per_load_config(); } else if ((fsvai == FSVAI_FREQ_INCREASE) || (fsvai == FSVAI_FREQ_EMERG)) { #ifndef DVFS_SW_WORKAROUND /* Increase the LP domain voltage first. */ ret = regulator_set_voltage(lp_regulator, dvfsper_plt_data->lp_high, dvfsper_plt_data->lp_high); if (ret < 0) { printk(KERN_DEBUG "COULD NOT SET LP VOLTAGE\n"); return; } udelay(100); #endif #ifndef DVFS_SW_WORKAROUND spin_lock_irqsave(&mxc_dvfs_per_lock, flags); reg = __raw_readl(dvfsper_plt_data->membase + MXC_DVFS_PER_PMCR0); reg |= MXC_DVFSPMCR0_UDCS; __raw_writel(reg, dvfsper_plt_data->membase + MXC_DVFS_PER_PMCR0); reg = __raw_readl(dvfsper_plt_data->gpc_cntr_reg_addr); reg &= ~(MXC_GPCCNTR_ADU_MASK | MXC_GPCCNTR_FUPD_MASK); reg |= MXC_GPCCNTR_FUPD; __raw_writel(reg, dvfsper_plt_data->gpc_cntr_reg_addr); reg = __raw_readl(dvfsper_plt_data->gpc_vcr_reg_addr); reg &= ~(MXC_GPCVCR_VINC_MASK | MXC_GPCVCR_VCNTU_MASK | MXC_GPCVCR_VCNT_MASK); reg |= (1 << MXC_GPCVCR_VINC_OFFSET | 1 << MXC_GPCVCR_VCNTU_OFFSET | 1 << MXC_GPCVCR_VCNT_OFFSET); __raw_writel(reg, dvfsper_plt_data->gpc_vcr_reg_addr); reg = __raw_readl(dvfsper_plt_data->gpc_cntr_reg_addr); reg &= ~MXC_GPCCNTR_ADU; reg |= MXC_GPCCNTR_STRT; __raw_writel(reg, dvfsper_plt_data->gpc_cntr_reg_addr); retry = 10; while ((__raw_readl( dvfsper_plt_data->gpc_cntr_reg_addr) & 0x4000) && retry > 0) { udelay(10); retry--; } spin_unlock_irqrestore(&mxc_dvfs_per_lock, flags); if (retry < 0) printk(KERN_ERR "****ERROR- DVFS\n"); #else /*Set the frequencies manually */ rate = clk_get_rate(axi_b_clk); clk_set_rate(axi_b_clk, clk_round_rate(axi_b_clk, 130000000)); rate = clk_get_rate(ahb_clk); clk_set_rate(ahb_clk, clk_round_rate(ahb_clk, 130000000)); #endif if (cpu_is_mx51()) { /*Change the DDR freq to 200Mhz. */ clk_set_rate(ddr_hf_clk, clk_round_rate(ddr_hf_clk, 200000000)); } dvfs_per_low_freq = 0; if (clk_get_rate(main_bus_clk) == LOW_BUS_FREQ) { cur_setpoint = 2; } else { cur_setpoint = 0; #if defined(CONFIG_ARCH_MX37) dptc_resume(DPTC_LP_ID); #endif #ifndef DVFS_SW_WORKAROUND clk_set_parent(main_bus_clk, clk_get(NULL, "pll2")); #endif } dvfs_per_load_config(); freq_increased = 1; } END: #if DVFS_PER_DEBUG dump_dvfs_per_regs(void)(); #endif if (dvfs_per_is_active) { reg = __raw_readl(dvfsper_plt_data->membase + MXC_DVFS_PER_PMCR0); /* Enable dVFS interrupt */ /* FSVAIM=0 */ reg &= ~MXC_DVFSPMCR0_FSVAI_MASK; reg |= FSVAI_FREQ_NOCHANGE; reg = (reg & ~MXC_DVFSPMCR0_FSVAIM); __raw_writel(reg, dvfsper_plt_data->membase + MXC_DVFS_PER_PMCR0); /*Unmask GPC1 IRQ */ reg = __raw_readl(dvfsper_plt_data->gpc_cntr_reg_addr); reg &= ~MXC_GPCCNTR_GPCIRQM; __raw_writel(reg, dvfsper_plt_data->gpc_cntr_reg_addr); } }
static void dvfs_per_workqueue_handler(struct work_struct *work) { u32 fsvai; u32 reg; u32 ret; u32 rate; /* Check DVFS frequency adjustment interrupt status */ reg = __raw_readl(MXC_DVFS_PER_PMCR0); fsvai = (reg & MXC_DVFSPMCR0_FSVAI_MASK) >> MXC_DVFSPMCR0_FSVAI_OFFSET; /* Check FSVAI, FSVAI=0 is error */ if (fsvai == FSVAI_FREQ_NOCHANGE) { /* Do nothing. Freq change is not required */ goto END; } /* If FSVAI indicate freq down. */ if (fsvai == FSVAI_FREQ_DECREASE) { reg = __raw_readl(MXC_DVFS_PER_PMCR0); reg &= ~MXC_DVFSPMCR0_UDCS; __raw_writel(reg, MXC_DVFS_PER_PMCR0); if (cpu_is_mx51()) { /*Change the DDR freq to 133Mhz. */ clk_set_rate(ddr_hf_clk, clk_round_rate(ddr_hf_clk, 133000000)); } #ifndef DVFS_SW_WORKAROUND /* Set the peripheral divider */ reg = __raw_readl(MXC_GPC_CNTR); reg |= MXC_GPCCNTR_FUPD; __raw_writel(reg, MXC_GPC_CNTR); reg = __raw_readl(MXC_GPC_VCR); reg &= ~(MXC_GPCVCR_VINC_MASK | MXC_GPCVCR_VCNTU_MASK | MXC_GPCVCR_VCNT_MASK); reg |= (1 << MXC_GPCVCR_VCNTU_OFFSET) | (20 << MXC_GPCVCR_VCNT_OFFSET); __raw_writel(reg, MXC_GPC_VCR); reg = __raw_readl(MXC_GPC_CNTR); reg |= MXC_GPCCNTR_STRT; __raw_writel(reg, MXC_GPC_CNTR); while (__raw_readl(MXC_GPC_CNTR) & 0x4000) { udelay(10); } #else //Set the frequencies manually rate = clk_get_rate(axi_b_clk); clk_set_rate(axi_b_clk, clk_round_rate(axi_b_clk, rate/2)); rate = clk_get_rate(ahb_clk); clk_set_rate(ahb_clk, clk_round_rate(ahb_clk, rate/2)); #endif if (clk_get_rate(main_bus_clk) == LOW_BUS_FREQ) { cur_setpoint = 2; } else { if (cpu_is_mx37()) ;//dptc_suspend(DPTC_LP_ID); cur_setpoint = 1; #ifndef DVFS_SW_WORKAROUND clk_set_parent(main_bus_clk, pll2); #endif } #ifndef DVFS_SW_WORKAROUND /* Drop the LP domain voltage */ mx37_set_lp_voltage(LP_LOWFREQ_VOLTAGE); udelay(100); #endif dvfs_per_load_config(); } else if ((fsvai == FSVAI_FREQ_INCREASE) || (fsvai == FSVAI_FREQ_EMERG)) { reg = __raw_readl(MXC_DVFS_PER_PMCR0); reg |= MXC_DVFSPMCR0_UDCS; __raw_writel(reg, MXC_DVFS_PER_PMCR0); #ifndef DVFS_SW_WORKAROUND /* Increase the LP domain voltage first. */ mx37_set_lp_voltage(LP_NORMAL_VOLTAGE); udelay(100); #endif if (cpu_is_mx51()) { /*Change the DDR freq to 133Mhz. */ clk_set_rate(ddr_hf_clk, clk_round_rate(ddr_hf_clk, 200000000)); } #ifndef DVFS_SW_WORKAROUND reg = __raw_readl(MXC_GPC_CNTR); reg |= MXC_GPCCNTR_FUPD; __raw_writel(reg, MXC_GPC_CNTR); reg = __raw_readl(MXC_GPC_VCR); reg &= ~(MXC_GPCVCR_VINC_MASK | MXC_GPCVCR_VCNTU_MASK | MXC_GPCVCR_VCNT_MASK); reg |= (1 << MXC_GPCVCR_VINC_OFFSET | 1 << MXC_GPCVCR_VCNTU_OFFSET | 20 << MXC_GPCVCR_VCNT_OFFSET); __raw_writel(reg, MXC_GPC_VCR); reg = __raw_readl(MXC_GPC_CNTR); reg &= ~MXC_GPCCNTR_ADU; reg |= MXC_GPCCNTR_STRT; __raw_writel(reg, MXC_GPC_CNTR); while (__raw_readl(MXC_GPC_CNTR) & 0x4000) { udelay(10); } #else //Set the frequencies manually rate = clk_get_rate(axi_b_clk); clk_set_rate(axi_b_clk, clk_round_rate(axi_b_clk, 130000000)); rate = clk_get_rate(ahb_clk); clk_set_rate(ahb_clk, clk_round_rate(ahb_clk, 130000000)); #endif if (clk_get_rate(main_bus_clk) == LOW_BUS_FREQ) { cur_setpoint = 2; } else { cur_setpoint = 0; if (cpu_is_mx37()) ;//dptc_resume(DPTC_LP_ID); #ifndef DVFS_SW_WORKAROUND clk_set_parent(main_bus_clk, clk_get(NULL, "pll2")); #endif } dvfs_per_load_config(); freq_increased = 1; } END: // dump_dvfs_per_regs(); if (dvfs_per_is_active) { reg = __raw_readl(MXC_DVFS_PER_PMCR0); /* Enable dVFS interrupt */ /* FSVAIM=0 */ reg &= ~MXC_DVFSPMCR0_FSVAI_MASK; reg |= FSVAI_FREQ_NOCHANGE; reg = (reg & ~MXC_DVFSPMCR0_FSVAIM); __raw_writel(reg, MXC_DVFS_PER_PMCR0); /*Unmask GPC1 IRQ */ reg = __raw_readl(MXC_GPC_CNTR); reg &= ~MXC_GPCCNTR_GPCIRQM; __raw_writel(reg, MXC_GPC_CNTR); } }