📄 dpm.c
字号:
#define PLL_NF_MAX 0x1ff#define PLL_NR_MAX (0x1f+2)int dpm_jz_init_opt(struct dpm_opt *opt){ int clkdiv_decode[] = {-1, 0, 1, 2, 3, -1, 4, -1, 5, -1, -1, -1, 6, -1, -1, -1, 7, -1, -1, -1, -1, -1, -1, -1, 8, -1, -1, -1, -1, -1, -1, -1, 9};// int v = opt->pp[DPM_MD_V]; int pll_nf = opt->pp[DPM_MD_PLL_NF]; int pll_nr = opt->pp[DPM_MD_PLL_NR]; int pll_no = opt->pp[DPM_MD_PLL_NO]; int iclk_div = opt->pp[DPM_MD_ICLK_DIV]; int sclk_div = opt->pp[DPM_MD_SCLK_DIV]; int mclk_div = opt->pp[DPM_MD_MCLK_DIV]; int pclk_div = opt->pp[DPM_MD_PCLK_DIV]; struct dpm_md_opt *md_opt = &opt->md_opt; /* Let's do some error checking. If we fail any of these, then the * whole operating point is suspect and therefore invalid. */ if (iclk_div && /* iclk_div=0 means sleep */ ((pll_nf > PLL_NF_MAX) || (pll_nf < 0) || (pll_nr > PLL_NR_MAX) || (pll_nr < 2) || ((pll_no != 1) && (pll_no != 2) && (pll_no != 4)))) { printk(KERN_WARNING "dpm_md_init_opt: " "PLL_NF/PLL_NR/PLL_NO specification " "(%u/%u/%u) out of range for opt named %s\n", pll_nf, pll_nr, pll_no, opt->name ); return -EINVAL; } if (check_divider(iclk_div) < 0) { printk(KERN_WARNING "dpm_md_init_opt: " "ICLK_DIV specification " "(%u) out of range for opt named %s\n", iclk_div, opt->name ); return -EINVAL; } if (check_divider(sclk_div) < 0) { printk(KERN_WARNING "dpm_md_init_opt: " "SCLK_DIV specification " "(%u) out of range for opt named %s\n", sclk_div, opt->name ); return -EINVAL; } if (check_divider(mclk_div) < 0) { printk(KERN_WARNING "dpm_md_init_opt: " "MCLK_DIV specification " "(%u) out of range for opt named %s\n", mclk_div, opt->name ); return -EINVAL; } if (check_divider(pclk_div) < 0) { printk(KERN_WARNING "dpm_md_init_opt: " "PCLK_DIV specification " "(%u) out of range for opt named %s\n", pclk_div, opt->name ); return -EINVAL; } /* make sure the operating point follows the syncronous scalable mode rules */ if (iclk_div == 0) { sclk_div = mclk_div = pclk_div = 0; /* go to sleep */ } else { if ((sclk_div == 0) || (mclk_div == 0) || (pclk_div == 0)) { printk(KERN_WARNING "dpm_md_init_opt: " "SCLK, MCLK and PCLK specification " "(%u %u %u) cannot be zero for opt named %s\n", sclk_div, mclk_div, pclk_div, opt->name ); return -EINVAL; } /* ICLK must be integral multiple of SCLK */ { int d = sclk_div / iclk_div; if (d * iclk_div != sclk_div) { printk(KERN_WARNING "dpm_md_init_opt: " "ICLK and SCLK specification " "ICLK(%u) must be integral multiple of SCLK(%u) for opt named %s\n", iclk_div, sclk_div, opt->name ); return -EINVAL; } } /* SCLK must be equal to MCLK or twice of MCLK */ { if ((sclk_div != mclk_div) && (sclk_div * 2 != mclk_div)) { printk(KERN_WARNING "dpm_md_init_opt: " "SCLK and MCLK specification " "SCLK(%u) must be equal to MCLK or twice of MCLK(%u) for opt named %s\n", sclk_div, mclk_div, opt->name ); return -EINVAL; } } /* SCLK must be integral multiple of PCLK */ { int d = pclk_div / sclk_div; if (d * sclk_div != pclk_div) { printk(KERN_WARNING "dpm_md_init_opt: " "SCLK and PCLK specification " "SCLK(%u) must be integral multiple of PCLK(%u) for opt named %s\n", sclk_div, pclk_div, opt->name ); return -EINVAL; } } /* MCLK must be integral multiple of PCLK */ { int d = pclk_div / mclk_div; if (d * mclk_div != pclk_div) { printk(KERN_WARNING "dpm_md_init_opt: " "MCLK and PCLK specification " "MCLK(%u) must be integral multiple of PCLK(%u) for opt named %s\n", mclk_div, pclk_div, opt->name ); return -EINVAL; } } } /* PLL frequency */ if (iclk_div == 0) { md_opt->pll = 0; } else { md_opt->pll = JZ_EXTAL * pll_nf / (pll_nr * pll_no); md_opt->regs.plcr1 = ((pll_nf-2) << 23) | ((pll_nr-2) << 18) | ((pll_no - 1) << 16); md_opt->regs.plcr1_mask = CPM_PLCR1_PLL1FD_MASK | CPM_PLCR1_PLL1RD_MASK | CPM_PLCR1_PLL1OD_MASK; } /* CPU clock */ if (iclk_div == 0) /* zero means sleep for cpu */ md_opt->cpu = 0; else { md_opt->cpu = md_opt->pll / iclk_div; md_opt->regs.cfcr |= (clkdiv_decode[iclk_div] << CPM_CFCR_IFR_BIT); md_opt->regs.cfcr_mask |= CPM_CFCR_IFR_MASK; } /* System bus clock */ if (sclk_div > 0) { md_opt->sys = md_opt->pll / sclk_div; md_opt->regs.cfcr |= (clkdiv_decode[sclk_div] << CPM_CFCR_SFR_BIT); md_opt->regs.cfcr_mask |= CPM_CFCR_SFR_MASK; } /* Memory clock */ if (mclk_div > 0) { md_opt->mem = md_opt->pll / mclk_div; md_opt->regs.cfcr |= (clkdiv_decode[mclk_div] << CPM_CFCR_MFR_BIT); md_opt->regs.cfcr_mask |= CPM_CFCR_MFR_MASK; } /* Peripheral bus clock */ if (pclk_div > 0) { md_opt->per = md_opt->pll / pclk_div; md_opt->regs.cfcr |= (clkdiv_decode[pclk_div] << CPM_CFCR_PFR_BIT); md_opt->regs.cfcr_mask |= CPM_CFCR_PFR_MASK; } /* Voltage */ md_opt->v = 1800; /* not chageable by software */ md_opt->lpj = compute_lpj(loops_per_jiffy, __cpm_get_iclk(), md_opt->cpu); return 0;}/* Fully determine the current machine-dependent operating point, and fill in a structure presented by the caller. This should only be called when the dpm_sem is held. This call can return an error if the system is currently at an operating point that could not be constructed by dpm_md_init_opt(). */int dpm_jz_get_opt(struct dpm_opt *opt){ struct dpm_md_opt *md_opt = &opt->md_opt; md_opt->v = 1800; /* not chageable by software */ md_opt->pll = __cpm_get_pllout(); md_opt->cpu = __cpm_get_iclk(); md_opt->sys = __cpm_get_sclk(); md_opt->mem = __cpm_get_mclk(); md_opt->per = __cpm_get_pclk(); md_opt->lpj = loops_per_jiffy; md_opt->regs.plcr1 = REG_CPM_PLCR1; md_opt->regs.plcr1_mask = (CPM_PLCR1_PLL1FD_MASK | CPM_PLCR1_PLL1RD_MASK | CPM_PLCR1_PLL1OD_MASK); md_opt->regs.cfcr = REG_CPM_CFCR; md_opt->regs.cfcr_mask = (CPM_CFCR_MFR_MASK | CPM_CFCR_LFR_MASK | CPM_CFCR_PFR_MASK | CPM_CFCR_SFR_MASK | CPM_CFCR_IFR_MASK); md_opt->regs.cfcr2 = REG_CPM_CFCR2; md_opt->regs.cfcr2_mask = CPM_CFCR2_PXFR_MASK; return 0;}/**************************************************************************** * DPM Idle Handler ****************************************************************************//* Check for pending external interrupts. If so, the entry to a low-power idle is preempted. */int return_from_idle_immediate(void){ return 1;}/**************************************************************************** * Machine-dependent /proc/driver/dpm/md entries ****************************************************************************/static inline int p5d(char *buf, unsigned hz){ return sprintf(buf, "%d.%02d\t", (hz/1000000), (hz%1000000)/10000);}static int dpm_proc_print_opt(char *buf, struct dpm_opt *opt){ int len = 0; struct dpm_md_opt *md_opt = &opt->md_opt; len += sprintf(buf + len, "%12s ", opt->name); len += p5d(buf + len, md_opt->pll); len += p5d(buf + len, md_opt->cpu); len += p5d(buf + len, md_opt->sys); len += p5d(buf + len, md_opt->mem); len += p5d(buf + len, md_opt->per); len += sprintf(buf + len, "\n"); return len;}int read_proc_dpm_md_opts(char *page, char **start, off_t offset, int count, int *eof, void *data){ int len = 0; int limit = offset + count; struct dpm_opt *opt; struct list_head *opt_list; /* FIXME: For now we assume that the complete table, * formatted, fits within one page */ if (offset >= PAGE_SIZE) return 0; if (dpm_lock_interruptible()) return -ERESTARTSYS; if (!dpm_initialized) len += sprintf(page + len, "DPM is not initialized\n"); else if (!dpm_enabled) len += sprintf(page + len, "DPM is disabled\n"); else { len += sprintf(page + len, "The active DPM policy is \"%s\"\n", dpm_active_policy->name); len += sprintf(page + len, "The current operating point is \"%s\"\n", dpm_active_opt->name); } if (dpm_initialized) { len += sprintf(page + len, "Table of all defined operating points, " "frequencies in MHz:\n"); len += sprintf(page + len, " Name PLL\tCPU\tSYS\tMEM\tPER\n"); list_for_each(opt_list, &dpm_opts) { opt = list_entry(opt_list, struct dpm_opt, list); if (len >= PAGE_SIZE) BUG(); if (len >= limit) break; len += dpm_proc_print_opt(page + len, opt); }#define TO_MHZ(x) (x/1000000),(x%1000000)/10000 len += sprintf(page +len,"\nCurrent values PLL\tCPU\tSYS\tMEM\tPER\n"); len += sprintf(page +len,"\n %d.%02d\t%d.%02d\t%d.%02d\t%d.%02d\t%d.%02d\n", TO_MHZ(__cpm_get_pllout()), TO_MHZ(__cpm_get_iclk()), TO_MHZ(__cpm_get_sclk()), TO_MHZ(__cpm_get_mclk()), TO_MHZ(__cpm_get_pclk())); //calibrate_delay(); } dpm_unlock(); *eof = 1; if (offset >= len) return 0; *start = page + offset; return min(count, len - (int)offset);}/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * * /proc/driver/dpm/md/cmd (Write-only) * * This is a catch-all, simple command processor for the Innovator DPM * implementation. These commands are for experimentation and development * _only_, and may leave the system in an unstable state. * * No commands defined now. * ****************************************************************************/int write_proc_dpm_md_cmd (struct file *file, const char *buffer, unsigned long count, void *data){ char *buf, *tok, *s; char *whitespace = " \t\r\n"; int ret = 0; if (current->uid != 0) return -EACCES; if (count == 0) return 0; if (!(buf = kmalloc(count + 1, GFP_KERNEL))) return -ENOMEM; if (copy_from_user(buf, buffer, count)) { kfree(buf); return -EFAULT; } buf[count] = '\0'; s = buf + strspn(buf, whitespace); tok = strsep(&s, whitespace); if (strcmp(tok, "define-me") == 0) { ; } else { ret = -EINVAL; } kfree(buf); if (ret == 0) return count; else return ret;}/* * Support an Jz board */static int dpm_jz_bd_init(void){ return 0;}static void dpm_jz_bd_exit(void){ return;}void dpm_jz_board_setup(void){ dpm_bd.init = dpm_jz_bd_init; dpm_bd.exit = dpm_jz_bd_exit; dpm_bd.check_v = NULL; dpm_bd.set_v_pre = NULL; dpm_bd.set_v_post = NULL;}/**************************************************************************** * Initialization/Exit ****************************************************************************/void dpm_jz_cleanup(void){ dpm_bd.exit();}extern void (*pm_idle)(void); int __init dpm_jz_init(void){ dpm_md.init = NULL; dpm_md.init_opt = dpm_jz_init_opt; dpm_md.set_opt = dpm_default_set_opt; dpm_md.get_opt = dpm_jz_get_opt; dpm_md.idle_set_parms = NULL; dpm_md.cleanup = dpm_jz_cleanup; dpm_jz_board_setup(); dpm_bd.init();#ifdef CONFIG_DPM_UTIMER init_utimer(&set_opt_utimer);#endif#ifdef CONFIG_DPM_IDLE pm_idle = dpm_idle;#endif printk("Jz Dynamic Power Management\n"); return 0;}__initcall(dpm_jz_init);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -