Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**************************************************************************
- * linux/arch/arm/mach-dm320/cm.c
- *
- * DM320 Clock Management
- *
- * Copyright (c) 2006 Kent Ryhorchuk <kent@zing.net>
- *
- **************************************************************************/
- #include <linux/init.h>
- #include <linux/kernel.h>
- #include <linux/delay.h>
- #include <linux/module.h>
- #include <linux/spinlock.h>
- #include <asm/arch/reg_clkc.h>
- #include <asm/io.h>
- #include <asm/irq.h>
- #include <asm/arch/gio.h>
- #include <linux/proc_fs.h>
- #include <asm/uaccess.h>
- #include <asm/arch/cm.h>
- #include <linux/devfs_fs_kernel.h>
- #include <linux/device.h>
- #include <asm/arch/memconfig.h>
- static int idle_service_level[CM_N_CLOCKS] = {0, 0, 0, 0};
- static int running_service_level[CM_N_CLOCKS] = {0xf, 0xf, 0, 0};
- /* Defaults for the /proc interface. These ensure the board will run. */
- static int proc_idle_service_level[CM_N_CLOCKS] = {0, 0, 0, 0};
- static int proc_running_service_level[CM_N_CLOCKS] = {0, 0, 0, 0};
- /* Dividers initialized in init. */
- static uint16_t idle_dividers[CM_N_CLOCKS];
- static uint16_t running_dividers[CM_N_CLOCKS];
- static uint16_t idle_refctl = 0x140;
- static uint16_t running_refctl = 0x140;
- static uint16_t min_cpu_divider = 0;
- /* This is the base value for the SDRAM refresh cycle. You divide this by
- the SDRAM clock divider and add 1 to get the SDRAM refresh cycle base. */
- #define REF_CYC_BASE 190 // 64MB, 8K cycle, 64ms.
- #define REF_CYC_FOR_DIV(D) ( REF_CYC_BASE / D ) // 0,1 invalid (illegal freqs)
- /* Returns the desired setting of DIV0, including AHB. */
- static uint16_t cpu_divider_for_sl(int sl)
- {
- uint16_t ret;
- if( sl > 0xf )
- sl = 0xf;
- /* ARM clock divider is 5 bits, service levels are 4. So shift by 1. */
- /* Add 1 so the minimum divider is 2. TODO - determine the minimum level
- some other way. */
- ret = ( ( 0xf - sl ) << 1 ) + 1;
- if( ret < min_cpu_divider )
- ret = min_cpu_divider;
- /* If the ARM divider is 2 or less, set the AHB divider to 2. */
- if( ret <= 1 )
- ret |= 0x0100;
- return ret;
- }
- /* Determined in init. */
- static uint16_t min_ram_divider = 0;
- static uint16_t ram_divider_for_sl(int sl)
- {
- uint16_t ret;
- if( sl > 0xf )
- sl = 0xf;
- ret = ( 0xf - sl ) << 1; /* Allow for 0. */
- /* On some boards the minimum divider is 1, on others it is 3. We use what
- was found at startup as the minimum. */
- if( ret < min_ram_divider )
- ret = min_ram_divider;
- return ret;
- }
- /* Determined in init. */
- static uint16_t min_axl_divider = 0;
- static uint16_t axl_divider_for_sl(int sl, uint16_t ram_div)
- {
- uint16_t ret;
- /* DM320 Errata #10 - AXL clock has to be some amount faster than SDRAM.
- The inequality is 1/AXL + 0.0028 < 1/SDR .
- If you do the math for the PLLA only case, this means that AXL divider
- must be at least one less than the SDRAM divider. The inequality also
- holds for the case where SDRAM is clocked from PLLB and PLLB is running
- slower than PLLA. */
- if( sl > 0xf )
- sl = 0xf;
- ret = ( ( 0xf - sl ) << 1 ) + 1;
- /* Complicated by the fact that on some boards the RAM divider is 1. */
- if( ram_div == 0 ){
- ret = min_axl_divider;
- }else if( ret >= ram_div ){
- ret = ram_div - 1;
- }
- if( ret < min_axl_divider )
- ret = min_axl_divider;
- return ret;
- }
- static uint16_t min_dsp_divider = 0;
- static uint16_t dsp_divider_for_sl(int sl, uint16_t cpu_div)
- {
- uint16_t ret;
- if( sl > 0xf )
- sl = 0xf;
- ret = ( ( 0xf - sl ) << 1 ) + 1;
- if( ret < min_dsp_divider )
- ret = min_dsp_divider;
- if( cpu_div & 0x0100 ){
- cpu_div &= 0xff;
- cpu_div += 1;
- }
- if( cpu_div < ret )
- ret = cpu_div;
- return ret;
- }
- static void set_idle_divider(cm_clock_t clock)
- {
- switch( clock ){
- case CM_CPU :
- idle_dividers[CM_CPU] = cpu_divider_for_sl(idle_service_level[CM_CPU]);
- break;
- case CM_RAM :
- case CM_AXL:
- idle_dividers[CM_RAM] = ram_divider_for_sl(idle_service_level[CM_RAM]);
- idle_dividers[CM_AXL] = axl_divider_for_sl(idle_service_level[CM_CPU],
- idle_dividers[CM_RAM]);
- idle_refctl = 0x100 | REF_CYC_FOR_DIV(idle_dividers[CM_RAM]);
- break;
- case CM_DSP :
- idle_dividers[CM_DSP] = dsp_divider_for_sl(idle_service_level[CM_DSP],
- idle_dividers[CM_CPU]);
- break;
- default:
- break;
- }
- }
- static void set_running_divider(cm_clock_t clock)
- {
- switch( clock ){
- case CM_CPU :
- running_dividers[CM_CPU] =
- cpu_divider_for_sl(running_service_level[CM_CPU]);
- break;
- case CM_RAM :
- case CM_AXL :
- running_dividers[CM_RAM] =
- ram_divider_for_sl(running_service_level[CM_RAM]);
- running_dividers[CM_AXL] =
- axl_divider_for_sl(running_service_level[CM_CPU],
- running_dividers[CM_RAM]);
- running_refctl = 0x100 | REF_CYC_FOR_DIV(running_dividers[CM_RAM]);
- break;
- case CM_DSP :
- running_dividers[CM_DSP] =
- dsp_divider_for_sl(running_service_level[CM_DSP],
- running_dividers[CM_CPU]);
- break;
- default:
- break;
- }
- }
- static void set_dividers(void)
- {
- idle_dividers[CM_CPU] = cpu_divider_for_sl(idle_service_level[CM_CPU]);
- idle_dividers[CM_RAM] = ram_divider_for_sl(idle_service_level[CM_RAM]);
- idle_dividers[CM_AXL] = axl_divider_for_sl(idle_service_level[CM_CPU],
- idle_dividers[CM_RAM]);
- idle_dividers[CM_DSP] = dsp_divider_for_sl(idle_service_level[CM_DSP],
- idle_dividers[CM_CPU]);
- running_dividers[CM_CPU] =
- cpu_divider_for_sl(running_service_level[CM_CPU]);
- running_dividers[CM_RAM] =
- ram_divider_for_sl(running_service_level[CM_RAM]);
- running_dividers[CM_AXL] =
- axl_divider_for_sl(running_service_level[CM_CPU],
- running_dividers[CM_RAM]);
- running_dividers[CM_DSP] =
- dsp_divider_for_sl(running_service_level[CM_DSP],
- running_dividers[CM_CPU]);
- running_refctl = 0x100 | REF_CYC_FOR_DIV(running_dividers[CM_RAM]);
- idle_refctl = 0x100 | REF_CYC_FOR_DIV(idle_dividers[CM_RAM]);
- }
- int disable_throttle(cm_clock_t clock, int level)
- {
- unsigned long flags;
- local_irq_save(flags);
- idle_service_level[clock] += level;
- set_idle_divider(clock);
- local_irq_restore(flags);
- return level;
- }
- EXPORT_SYMBOL(disable_throttle);
- void enable_throttle(cm_clock_t clock, int token)
- {
- unsigned long flags;
- local_irq_save(flags);
- idle_service_level[clock] -= token;
- if( idle_service_level[clock] < 0 )
- idle_service_level[clock] = 0;
- set_idle_divider(clock);
- local_irq_restore(flags);
- }
- EXPORT_SYMBOL(enable_throttle);
- int enable_boost(cm_clock_t clock, int level)
- {
- unsigned long flags;
- local_irq_save(flags);
- running_service_level[clock] += level;
- set_running_divider(clock);
- dm320_exit_idle();
- local_irq_restore(flags);
- return level;
- }
- EXPORT_SYMBOL(enable_boost);
- void disable_boost(cm_clock_t clock, int token)
- {
- unsigned long flags;
- local_irq_save(flags);
- running_service_level[clock] -= token;
- if( idle_service_level[clock] < 0 )
- idle_service_level[clock] = 0;
- set_running_divider(clock);
- dm320_exit_idle();
- local_irq_restore(flags);
- }
- EXPORT_SYMBOL(disable_boost);
- void throttle_all(cm_setsl_in_t *a, cm_user_t *cmu)
- {
- int i;
- unsigned long flags;
- local_irq_save(flags);
- for( i = 0 ; i < CM_N_CLOCKS ; i++ ){
- idle_service_level[i] -= cmu->idle_tokens[i];
- idle_service_level[i] += cmu->idle_tokens[i] = a[0].sl[i];
- running_service_level[i] -= cmu->running_tokens[i];
- running_service_level[i] += cmu->running_tokens[i] = a[1].sl[i];
- }
- set_dividers();
- dm320_exit_idle();
- local_irq_restore(flags);
- }
- void dm320_enter_idle(void)
- {
- uint16_t div0_new, div1_new, div2_new;
- div0_new = idle_dividers[CM_CPU]; /* Includes AHB. */
- div1_new = ( idle_dividers[CM_AXL] << 8 ) | idle_dividers[CM_RAM];
- div2_new = idle_dividers[CM_DSP] << 8;
- outw(div0_new, DM320_CLKC_DIV0);
- outw(div1_new, DM320_CLKC_DIV1);
- outw(div2_new, DM320_CLKC_DIV2);
- outw(idle_refctl, DM320_SDRAMC_REFCTL);
- }
- void dm320_exit_idle(void)
- {
- uint16_t div0_new, div1_new, div2_new;
- div0_new = running_dividers[CM_CPU];
- div1_new = ( running_dividers[CM_AXL] << 8 ) | running_dividers[CM_RAM];
- div2_new = running_dividers[CM_DSP] << 8;
- outw(div2_new, DM320_CLKC_DIV2);
- outw(div1_new, DM320_CLKC_DIV1);
- outw(div0_new, DM320_CLKC_DIV0);
- outw(running_refctl, DM320_SDRAMC_REFCTL);
- }
- /* This is the number of bits of precision for the loops_per_jiffy. Each
- bit takes on average 1.5/HZ seconds. This (like the original) is a little
- better than 1% */
- #define LPS_PREC 8
- void recalibrate_delay(void)
- {
- #if !defined(CONFIG_BOGOMIPS) || !CONFIG_BOGOMIPS
- unsigned long ticks, loopbit;
- int lps_precision = LPS_PREC;
- loops_per_jiffy = (1<<12);
- printk("Recalibrating delay loop... ");
- while (loops_per_jiffy <<= 1) {
- /* wait for "start of" clock tick */
- ticks = jiffies;
- while (ticks == jiffies)
- /* nothing */;
- /* Go .. */
- ticks = jiffies;
- __delay(loops_per_jiffy);
- ticks = jiffies - ticks;
- if (ticks)
- break;
- }
- /* Do a binary approximation to get loops_per_jiffy set to equal one clock
- (up to lps_precision bits) */
- loops_per_jiffy >>= 1;
- loopbit = loops_per_jiffy;
- while ( lps_precision-- && (loopbit >>= 1) ) {
- loops_per_jiffy |= loopbit;
- ticks = jiffies;
- while (ticks == jiffies);
- ticks = jiffies;
- __delay(loops_per_jiffy);
- if (jiffies != ticks) /* longer than 1 tick */
- loops_per_jiffy &= ~loopbit;
- }
- #else
- printk("Using pre-calculated value: ");
- #endif
- /* Round the value and print it */
- printk("%lu.%02lu BogoMIPS\n",
- loops_per_jiffy/(500000/HZ),
- (loops_per_jiffy/(5000/HZ)) % 100);
- }
- static int throttle_read_proc(char *page, char **start, off_t off,
- int count, int *eof, void *data)
- {
- int len;
- len = sprintf(page,
- "\
- idle: cpu %02d, ram %02d, axl %02d, dsp %02d\n\
- idle_div: cpu %03x, ram %02x, axl %02x, dsp %02x\n\
- running: cpu %02d, ram %02d, axl %02d, dsp %02d\n\
- running_div: cpu %03x, ram %02x, axl %02x, dsp %02x\n\
- ",
- idle_service_level[CM_CPU], idle_service_level[CM_RAM],
- idle_service_level[CM_AXL], idle_service_level[CM_DSP],
- idle_dividers[CM_CPU], idle_dividers[CM_RAM],
- idle_dividers[CM_AXL], idle_dividers[CM_DSP],
- running_service_level[CM_CPU], running_service_level[CM_RAM],
- running_service_level[CM_AXL], running_service_level[CM_DSP],
- running_dividers[CM_CPU], running_dividers[CM_RAM],
- running_dividers[CM_AXL], running_dividers[CM_DSP]);
- len -= off;
- *start = page + off;
- if (len > count)
- len = count;
- else
- *eof = 1;
- if (len < 0)
- len = 0;
- return len;
- }
- static cm_user_t proc_cmu;
- static int throttle_write_proc(struct file *file, const char __user *buffer,
- unsigned long count, void *data)
- {
- int len, i;
- unsigned long num;
- int sl_in[CM_N_CLOCKS];
- char str[20];
- len = sizeof(str);
- if (count < len)
- len = count;
- if (copy_from_user(str, buffer, len) > 0)
- return -EFAULT;
- num = sscanf(str, " %d %d %d %d ", &sl_in[0], &sl_in[1], &sl_in[2],
- &sl_in[3]);
- if( num != 4 ){
- printk("Invalid string written to throttle.\n");
- return -EINVAL;
- }
- for( i = 0 ; i < CM_N_CLOCKS ; i++ ){
- enable_throttle(i, proc_cmu.idle_tokens[i]);
- proc_cmu.idle_tokens[i] = disable_throttle(i, sl_in[i]);
- }
- return count;
- }
- #define THROTTLE_CHAR_MAJOR 154
- #define THROTTLE_CPU_MINOR 0
- #define THROTTLE_RAM_MINOR 1
- static cm_user_t *users[CM_MAX_USERS];
- void throttle_add(int index)
- {
- devfs_mk_cdev(MKDEV(THROTTLE_CHAR_MAJOR, index),
- S_IFCHR | S_IRUGO | S_IWUGO, "throttle/%d", index);
- }
- void throttle_remove(int index)
- {
- devfs_remove("throttle/%d", index);
- }
- #define TO_CM_USER(file) (cm_user_t *)(file->private_data)
- static int throttle_open(struct inode *inode, struct file *file)
- {
- int i;
- cm_user_t *cmu;
- /* Check for space in the list */
- for( i = 0 ; i < CM_MAX_USERS ; i++ )
- if( users[i] == NULL )
- break;
- if( i == CM_MAX_USERS )
- return -EMFILE;
- cmu = users[i] = kmalloc(sizeof(cm_user_t), GFP_KERNEL);
- if( users[i] == NULL )
- return -ENOMEM;
- cmu->index = i;
- for( i = 0 ; i < CM_N_CLOCKS ; i++ ){
- cmu->idle_tokens[i] = 0;
- cmu->running_tokens[i] = 0;
- }
- file->private_data = cmu;
- return 0;
- }
- static int throttle_close(struct inode *inode, struct file *file)
- {
- cm_user_t *cmu = TO_CM_USER(file);
- int i;
- unsigned long flags;
- users[cmu->index] = NULL;
- /* Remove users influence on the dividers and reset them. */
- local_irq_save(flags);
- for( i = 0 ; i < CM_N_CLOCKS ; i++ ){
- enable_throttle(i, cmu->idle_tokens[i]);
- disable_boost(i, cmu->running_tokens[i]);
- }
- set_dividers();
- dm320_exit_idle(); /* Apply running clocks now. */
- local_irq_restore(flags);
- kfree(cmu);
- //printk("Close clock %d idle service level is now %d\n", clock,
- // idle_service_level[clock]);
- return 0;
- }
- static int throttle_ioctl(struct inode *inode, struct file *file,
- u_int cmd, u_long arg)
- {
- cm_user_t *cmu = TO_CM_USER(file);
- void __user *argp = (void __user *)arg;
- cm_setsl_in_t setsl_arg;
- cm_setsl_in_t setallsl_arg[2];
- u_long size;
- int i;
- size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
- if (cmd & IOC_IN) {
- if (!access_ok(VERIFY_READ, argp, size))
- return -EFAULT;
- }
- if (cmd & IOC_OUT) {
- if (!access_ok(VERIFY_WRITE, argp, size))
- return -EFAULT;
- }
- if( cmd == CM_SET_ALL_SL ){
- if( copy_from_user(&setallsl_arg, argp, sizeof(setallsl_arg)) )
- return -EFAULT;
- }else{
- if( copy_from_user(&setsl_arg, argp, sizeof(setsl_arg)) )
- return -EFAULT;
- }
- switch( cmd ){
- case CM_SET_IDLE_SL :
- for( i = 0 ; i < CM_N_CLOCKS ; i++ ){
- enable_throttle(i, cmu->idle_tokens[i]);
- cmu->idle_tokens[i] =
- disable_throttle(i, setsl_arg.sl[i]);
- }
- break;
- case CM_SET_RUNNING_SL :
- for( i = 0 ; i < CM_N_CLOCKS ; i++ ){
- disable_boost(i, cmu->running_tokens[i]);
- cmu->running_tokens[i] =
- enable_boost(i, setsl_arg.sl[i]);
- }
- break;
- case CM_SET_ALL_SL :
- throttle_all(setallsl_arg, cmu);
- break;
- default:
- return -EINVAL;
- }
- return 0;
- }
- static ssize_t throttle_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)
- {
- return -EINVAL;
- }
- static ssize_t throttle_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos)
- {
- return -EINVAL;
- }
- static struct file_operations cm_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .read = throttle_read,
- .write = throttle_write,
- .open = throttle_open,
- .release = throttle_close,
- .ioctl = throttle_ioctl,
- };
- static int __init cm_init(void)
- {
- struct proc_dir_entry *de;
- int i;
- printk("DM320 clock management\n");
- if( register_chrdev(THROTTLE_CHAR_MAJOR, "throttle", &cm_fops) ){
- printk("Can't allocate major number %d for throttle.\n",
- THROTTLE_CHAR_MAJOR);
- return -EAGAIN;
- }
- devfs_mk_dir("throttle");
- throttle_add(0);
- de = create_proc_entry("throttle", 0644, NULL);
- if (!de)
- return -1;
- de->read_proc = (read_proc_t *) throttle_read_proc;
- de->write_proc = (write_proc_t *) throttle_write_proc;
- de->owner = THIS_MODULE;
- /* Minumum dividers. */
- min_cpu_divider = inw(DM320_CLKC_DIV0) & 0x001f;
- min_ram_divider = inw(DM320_CLKC_DIV1) & 0x001f;
- min_axl_divider = ( inw(DM320_CLKC_DIV1) & 0x1f00 ) >> 8;
- min_dsp_divider = ( inw(DM320_CLKC_DIV2) & 0x1f00 ) >> 8;
- set_dividers();
- for( i = 0 ; i < CM_N_CLOCKS ; i++ ){
- proc_cmu.idle_tokens[i] =
- disable_throttle(i, proc_idle_service_level[i]);
- proc_cmu.running_tokens[i] =
- enable_boost(i, proc_running_service_level[i]);
- }
- return 0;
- }
- static void __exit cm_exit(void)
- {
- throttle_remove(0);
- unregister_chrdev(THROTTLE_CHAR_MAJOR, "throttle");
- remove_proc_entry("throttle", NULL);
- }
- module_init(cm_init);
- module_exit(cm_exit);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement