Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
10
drivers/char/s3c-dvfs/Kconfig
Normal file
10
drivers/char/s3c-dvfs/Kconfig
Normal file
@@ -0,0 +1,10 @@
|
||||
#
|
||||
# DVFS (Dynamic voltage/frequency Scaling)
|
||||
#
|
||||
|
||||
menu "DVFS support"
|
||||
|
||||
config DVFS
|
||||
tristate "Support for DVFS on SMDK Board"
|
||||
depends on CPU_S3C6400 || CPU_S3C6410 || CPU_S3C2416 || CPU_S3C2450 || CPU_S3C2443
|
||||
endmenu
|
||||
5
drivers/char/s3c-dvfs/Makefile
Normal file
5
drivers/char/s3c-dvfs/Makefile
Normal file
@@ -0,0 +1,5 @@
|
||||
#
|
||||
# Makefile for the DVS support driver
|
||||
#
|
||||
|
||||
obj-$(CONFIG_DVFS) = s3c-dvfs.o
|
||||
647
drivers/char/s3c-dvfs/s3c-dvfs.c
Normal file
647
drivers/char/s3c-dvfs/s3c-dvfs.c
Normal file
@@ -0,0 +1,647 @@
|
||||
/* drivers/char/s3c-dvfs/s3c-dvfs.c
|
||||
*
|
||||
* Copyright (c) 2008 Samsung Electronics
|
||||
* Kwanghyun.La <nala.la@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* S3C64xx DVFS interface with LTC3714 DCDC convertor power
|
||||
* 2008.02.28. basic scheme update to here
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/fcntl.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <linux/stat.h>
|
||||
#include <linux/proc_fs.h>
|
||||
|
||||
#include <linux/delay.h> // For loops_per_jiffy
|
||||
#include <asm/arch/regs-gpio.h>
|
||||
|
||||
#include "s3c-dvfs.h"
|
||||
|
||||
#define DEV_NAME "s3c-dvfs"
|
||||
#define DEV_MAJOR 240
|
||||
|
||||
|
||||
|
||||
#define XTAL 12*1000*1000 /* Clock source 12Mhz */
|
||||
#define MAX_APLL_RATIO 7 /* See S3C6400 user manual 3-7 */
|
||||
#define MAX_HCLK2_RATIO 7 /* See S3C6400 user manual 0-1 */
|
||||
#define Mhz 1000*1000
|
||||
|
||||
#define SUPPORT_PROC_FS
|
||||
|
||||
#ifdef SUPPORT_PROC_FS
|
||||
struct proc_dir_entry *proc_root_fp = NULL;
|
||||
struct proc_dir_entry *proc_voltage_fp = NULL;
|
||||
struct proc_dir_entry *proc_freq_fp = NULL;
|
||||
struct proc_dir_entry *proc_step_fp = NULL;
|
||||
|
||||
char proc_voltage_str[PAGE_SIZE-80] = { 0,};
|
||||
char proc_freq_str[PAGE_SIZE-80] = { 0,};
|
||||
char proc_step_str[PAGE_SIZE-80] = { 0,};
|
||||
#endif
|
||||
|
||||
|
||||
/* it's depend on DCDC regurator specification
|
||||
* if change parts, you must be change table and configuration*/
|
||||
static const unsigned int voltage_table[32] = {
|
||||
1750, 1700, 1650, 1600, 1550, 1500, 1450, 1400,
|
||||
1350, 1300, 1250, 1200, 1150, 1100, 1050, 1000,
|
||||
975, 950, 925, 900, 875, 850, 825, 800,
|
||||
775, 750, 725, 700, 675, 650, 625, 600,
|
||||
};
|
||||
|
||||
#define NUMBER_OF_STEP 4
|
||||
|
||||
/*Frequency step define section */
|
||||
#define MDIV 0
|
||||
#define PDIV 1
|
||||
#define SDIV 2
|
||||
#define ARM_RATIO 3
|
||||
#define HCLK_RATIO 4
|
||||
#define DVFS MDIV /* for S3C2443,2416 and S3C2450*/
|
||||
|
||||
#define ARM_VOLT_STEP 0
|
||||
#define INT_VOLT_STEP 1
|
||||
#define FREQ_STEP 2
|
||||
|
||||
#if defined(CONFIG_MACH_SMDK6400) || defined(CONFIG_MACH_SMDK6410)
|
||||
static const unsigned int pll_mps_table[][5] = {
|
||||
{533, 6, 1, 0, 1}, /* step 0 ARM:(533 / 1)=533 MHz ,HCLKx2:266 HCLK: 133*/
|
||||
{400, 6, 1, 0, 1}, /* step 1 ARM:(400 / 1)=400 MHz ,HCLKx2:266 HCLK: 133*/
|
||||
{400, 6, 1, 1, 1}, /* step 2 ARM:(400 / 2)=200 MHz ,HCLKx2:266 HCLK: 133*/
|
||||
{400, 6, 1, 3, 2}, /* step 3 ARM:(400 / 4)=100 MHz ,HCLKx2:133 HCLK: 66 ==> careful linked device*/
|
||||
/* {MDIV, PDIV, SDIV, ARM RATIO, HCLKx2 RATIO } */
|
||||
};
|
||||
|
||||
static const unsigned int dvfs_step[NUMBER_OF_STEP][3] = {
|
||||
{ 1200 , 1200, 0}, /* ARM 1.2Volt, INT 1.2V, pll mode 0 533-133*/
|
||||
{ 1000 , 1000, 1}, /* ARM 1.2Volt, INT 1.2V, pll mode 2 200-133*/
|
||||
{ 950 , 1000, 2}, /* ARM 1.2Volt, INT 1.2V, pll mode 2 200-133*/
|
||||
{ 900 , 1000, 2}, /* ARM 1.2Volt, INT 1.2V, pll mode 3 100- 66*/
|
||||
/* {Voltage, Frequency_STEP} */
|
||||
};
|
||||
#elif defined(CONFIG_MACH_SMDK2450) ||defined (CONFIG_MACH_SMDK2416) ||defined (CONFIG_MACH_SMDK2443)
|
||||
static const unsigned int pll_mps_table[][5] = {
|
||||
{0, 0, 0, 0, 1}, /* step 0 DVS OFF, ARM:534MHz ,HCLK:133MHz*/
|
||||
{0, 0, 0, 1, 1}, /* step 1 DVS OFF, ARM:266MHz ,HCLK:133MHz*/
|
||||
{0, 0, 0, 3, 1}, /* step 2 DVS OFF, ARM:133MHz ,HCLK:133MHz*/
|
||||
{1, 0, 0, 3, 1}, /* step 3 DVS ON , ARM:133MHz ,HCLK:133MHz careful the linked devices*/
|
||||
/* {DVS_ON, NA, NA, ARM RATIO, HCLK RATIO } */
|
||||
/* S3C24xx case , when HCLK is changed , UART signal is fragle, so must need to re setting the uart div
|
||||
and the ARM Clock must be over than HCLK or same, it'can not support under HCLK*/
|
||||
};
|
||||
|
||||
static const unsigned int pll_mps_table400[][5] = {
|
||||
{0, 0, 0, 1, 1}, /* step 0 DVS OFF, ARM:400MHz ,HCLK:133MHz*/
|
||||
{0, 0, 0, 2, 1}, /* step 1 DVS OFF, ARM:266MHz ,HCLK:133MHz*/
|
||||
{0, 0, 0, 4, 1}, /* step 2 DVS OFF, ARM:160Hz ,HCLK:133MHz*/
|
||||
{1, 0, 0, 4, 1}, /* step 3 DVS ON , ARM:160MHz ,HCLK:133MHz careful the linked devices*/
|
||||
/* {DVS_ON, NA, NA, ARM RATIO, HCLK RATIO } */
|
||||
/* S3C24xx case , when HCLK is changed , UART signal is fragle, so must need to re setting the uart div
|
||||
and the ARM Clock must be over than HCLK or same, it'can not support under HCLK*/
|
||||
};
|
||||
|
||||
static const unsigned int dvfs_step[NUMBER_OF_STEP][3] = {
|
||||
{ 1200 , 1200, 0}, /* ARM 1.2Volt, INT 1.2V, pll mode 0 533-133*/
|
||||
{ 1100 , 1100, 1}, /* ARM 1.0Volt, INT 1.0V, pll mode 2 200-133*/
|
||||
{ 975 , 1100, 2}, /* ARM 1.0Volt, INT 1.0V, pll mode 2 200-133*/
|
||||
{ 955 , 1000, 3}, /* ARM 1.0Volt, INT 1.0V, pll mode 3 100- 66*/
|
||||
/* {ARM Voltage,INT ARM Voltage, Frequency_STEP} */
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
static int previous_dvfs_step;
|
||||
|
||||
static s3c_dvfs_info dvfs_info;
|
||||
|
||||
static void set_dvfs_gpio(void)
|
||||
{
|
||||
/* We can control the voltage of ARM core and Internal block by setting GPIO */
|
||||
/* GPN(11~15). First of all, we should set these gpio to output mode. */
|
||||
/* GPIO configuration */
|
||||
GPIO_CFG(LTC3714_DATA1, LTC3714_OUTP1);
|
||||
GPIO_CFG(LTC3714_DATA2, LTC3714_OUTP2);
|
||||
GPIO_CFG(LTC3714_DATA3, LTC3714_OUTP3);
|
||||
GPIO_CFG(LTC3714_DATA4, LTC3714_OUTP4);
|
||||
GPIO_CFG(LTC3714_DATA5, LTC3714_OUTP5);
|
||||
|
||||
/* Pull-up/down disable */
|
||||
GPIO_PULLUP(LTC3714_DATA1, 0x0);
|
||||
GPIO_PULLUP(LTC3714_DATA2, 0x0);
|
||||
GPIO_PULLUP(LTC3714_DATA3, 0x0);
|
||||
GPIO_PULLUP(LTC3714_DATA4, 0x0);
|
||||
GPIO_PULLUP(LTC3714_DATA5, 0x0);
|
||||
|
||||
|
||||
/* Latch control signal*/
|
||||
/* CORE_REG_OE: GPL9, ARM_REG_OE: GPL8, INT_REG_LE: GPL10*/
|
||||
GPIO_CFG(ARM_LE , ARM_LE_OUTP);
|
||||
GPIO_CFG(DVS_OE , DVS_OE_OUTP);
|
||||
GPIO_CFG(INT_LE , INT_LE_OUTP);
|
||||
|
||||
GPIO_PULLUP(ARM_LE , 0x0);
|
||||
GPIO_PULLUP(DVS_OE , 0x0);
|
||||
GPIO_PULLUP(INT_LE , 0x0);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/* Set LTC3714 voltage regulator */
|
||||
/* Input : pwr : 1(ARM), 2(Internal), 3(Both) */
|
||||
/* voltage : 1mV step */
|
||||
static int set_ltc3714(unsigned int pwr, unsigned int voltage)
|
||||
{
|
||||
int position = 0;
|
||||
unsigned int val;
|
||||
|
||||
|
||||
if(voltage > voltage_table[0] || voltage < voltage_table[31]) {
|
||||
printk("[ERROR]: voltage value over limits!!!");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if(voltage > voltage_table[16]) { // 1750 ~ 1000 mV
|
||||
for(position = 15; position >= 0; position --) {
|
||||
if(voltage_table[position] == voltage) break;
|
||||
}
|
||||
|
||||
}
|
||||
else if(voltage >= voltage_table[31]) { //975 ~ 600 mV
|
||||
for(position = 31; position >= 16; position --) {
|
||||
if(voltage_table[position] == voltage) break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
printk("[error]: Can't find adquate voltage table list value\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
printk("Founded postion :[%d] \n",position);
|
||||
|
||||
position &=0x1f;
|
||||
|
||||
#if defined (CONFIG_MACH_SMDK6400) ||defined (CONFIG_MACH_SMDK6410)
|
||||
__raw_writel((__raw_readl(S3C_GPNDAT)&~(0x1f<<11))|(position<<11), S3C_GPNDAT);
|
||||
#elif defined (CONFIG_MACH_SMDK2450) ||defined (CONFIG_MACH_SMDK2416) ||defined (CONFIG_MACH_SMDK2443)
|
||||
/* It's depend on schematic and assigned GPIO pin */
|
||||
val = __raw_readl(S3C2410_GPCDAT) &~(0x1<<7|0x1<<6|0x1<<5|0x1<<0);
|
||||
val |= ((((position & 0xe)>>1)<<5)|(position & 0x1));
|
||||
__raw_writel(val, S3C2410_GPCDAT);
|
||||
|
||||
val = __raw_readl(S3C2410_GPBDAT) &~(0x1<<2);
|
||||
val |= (position & 0x10)<<2;
|
||||
__raw_writel(val,S3C2410_GPBDAT);
|
||||
#endif
|
||||
|
||||
if(pwr == ARM_VOLT_STEP) { //ARM Voltage Control => ARM_REG_LE => Output H => Data Changed
|
||||
|
||||
CONTROl_SET(ARM_LE, 0x1);
|
||||
udelay(10);
|
||||
CONTROl_SET(ARM_LE, 0x0);
|
||||
} else if(pwr == INT_VOLT_STEP) { // INT Voltage Control
|
||||
CONTROl_SET(INT_LE, 0x1);
|
||||
udelay(10);
|
||||
CONTROl_SET(INT_LE, 0x0);
|
||||
} else {
|
||||
printk("[error]: set_ltc3714, check mode [pwr] value\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/* Set CLK_DIV0 register with specific APLL divider value*/
|
||||
static int set_freq_divider(unsigned int clk_source , unsigned int val)
|
||||
{
|
||||
unsigned int tmp;
|
||||
|
||||
if (clk_source == ARM_RATIO){
|
||||
if(val > MAX_APLL_RATIO) {
|
||||
printk(KERN_ERR "Freq divider value(APLL_RATIO) is out of spec\n");
|
||||
printk(KERN_ERR "APLL_RATIO : 0~7\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
dvfs_info.freq_divider = (val + 1);
|
||||
tmp = __raw_readl(ARM_CLK_DIV)&~ARM_DIV_MASK;
|
||||
tmp |= val<<ARM_DIV_RATIO_BIT;
|
||||
__raw_writel(tmp, ARM_CLK_DIV);
|
||||
} else if (clk_source == HCLK_RATIO){
|
||||
if(val > MAX_HCLK2_RATIO) {
|
||||
printk(KERN_ERR "Freq divider value is out of spec\n");
|
||||
printk(KERN_ERR "HCLK_PostDivider RATIO : 0,1 \n");
|
||||
return -EINVAL;
|
||||
}
|
||||
/* it's only for setting the HCLK or system Clock */
|
||||
tmp = __raw_readl(ARM_CLK_DIV) & ~HCLK_DIV_MASK;
|
||||
tmp |= (val<<HCLK_DIV_RATIO_BIT);
|
||||
__raw_writel(tmp, ARM_CLK_DIV);
|
||||
} else {
|
||||
printk(KERN_ERR " It's wrong clock post divider path \n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set APLL P,M,S value to make specific CPU frequency*/
|
||||
/* Only support Async mode */
|
||||
static int set_pll(unsigned int freq_level)
|
||||
{
|
||||
unsigned int val,err;
|
||||
|
||||
if (freq_level < NUMBER_OF_STEP ){
|
||||
|
||||
#if defined (CONFIG_MACH_SMDK2450) ||defined (CONFIG_MACH_SMDK2416)
|
||||
|
||||
val = ((__raw_readl(MPLL_CON)>>14)&0x3ff);
|
||||
|
||||
if (val == 267){
|
||||
err = set_freq_divider(ARM_RATIO, pll_mps_table[freq_level][ARM_RATIO]);
|
||||
if(err)
|
||||
return err;
|
||||
|
||||
err = set_freq_divider(HCLK_RATIO, pll_mps_table[freq_level][HCLK_RATIO]);
|
||||
if(err)
|
||||
return err;
|
||||
}
|
||||
else{
|
||||
err = set_freq_divider(ARM_RATIO, pll_mps_table400[freq_level][ARM_RATIO]);
|
||||
if(err)
|
||||
return err;
|
||||
|
||||
err = set_freq_divider(HCLK_RATIO, pll_mps_table400[freq_level][HCLK_RATIO]);
|
||||
if(err)
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
err = set_freq_divider(ARM_RATIO, pll_mps_table[freq_level][ARM_RATIO]);
|
||||
if(err)
|
||||
return err;
|
||||
|
||||
err = set_freq_divider(HCLK_RATIO, pll_mps_table[freq_level][HCLK_RATIO]);
|
||||
if(err)
|
||||
return err;
|
||||
#endif
|
||||
|
||||
/*
|
||||
it's only guarantee for chaiging PLL on S3C64xx
|
||||
if you want to change dynamic freq. you must check stability of system
|
||||
*/
|
||||
#if defined (CONFIG_MACH_SMDK6400) ||defined (CONFIG_MACH_SMDK6410)
|
||||
val = PLL_CALC_VAL(pll_mps_table[freq_level][MDIV],\
|
||||
pll_mps_table[freq_level][PDIV],\
|
||||
pll_mps_table[freq_level][SDIV]);
|
||||
__raw_writel(val, ARM_PLL_CON);
|
||||
/*
|
||||
it does not guarantee for chaiging PLL on S3C2443,S3C2450 and S3C2416
|
||||
but we guide to set DVFS or divide arm clock output
|
||||
*/
|
||||
#elif defined (CONFIG_MACH_SMDK2450) ||defined (CONFIG_MACH_SMDK2416) ||defined (CONFIG_MACH_SMDK2443)
|
||||
/* set the DVS ON or OFF*/
|
||||
val = __raw_readl(ARM_CLK_DIV) & ~(DVFS_MASK);
|
||||
val |= (pll_mps_table[freq_level][DVFS] << 13) ;
|
||||
/* it's ony for conpensation PCLK of S3C2443,2416and 2450*/
|
||||
__raw_writel(val, ARM_CLK_DIV);
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
else{
|
||||
printk("It's wrong DFS %d range \n",freq_level);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Other modules can use this function to change power status */
|
||||
/* Especially, ARM Dynamic power interface can control DVFS by accessing this */
|
||||
int set_dvfs_step(unsigned int step)
|
||||
{
|
||||
int err;
|
||||
|
||||
if(step >= NUMBER_OF_STEP) {
|
||||
printk(KERN_ERR "DVFS step is out of range(0 ~ %2d)\n",NUMBER_OF_STEP-1);
|
||||
return -EINVAL;
|
||||
}
|
||||
//printk("set_dvfs_step[%d]\n",step);
|
||||
|
||||
if(previous_dvfs_step < step) { // decreasing CPU freq & Voltage
|
||||
/* CPU Frequency control */
|
||||
err = set_pll(dvfs_step[step][FREQ_STEP]);
|
||||
|
||||
/* CPU voltage control */
|
||||
dvfs_info.pwr_type = ARM_VOLT_STEP;
|
||||
dvfs_info.new_voltage = dvfs_step[step][ARM_VOLT_STEP];
|
||||
err = set_ltc3714(dvfs_info.pwr_type, dvfs_info.new_voltage);
|
||||
if(err == 0){
|
||||
dvfs_info.curr_voltage_arm = dvfs_info.new_voltage;
|
||||
}
|
||||
|
||||
|
||||
dvfs_info.pwr_type = INT_VOLT_STEP;
|
||||
dvfs_info.new_voltage = dvfs_step[step][INT_VOLT_STEP];
|
||||
err = set_ltc3714(dvfs_info.pwr_type, dvfs_info.new_voltage);
|
||||
if(err == 0){
|
||||
dvfs_info.curr_voltage_internal = dvfs_info.new_voltage;
|
||||
}
|
||||
|
||||
} else if(previous_dvfs_step > step) { // increasing CPU freq & Voltage
|
||||
|
||||
/* CPU voltage control */
|
||||
dvfs_info.pwr_type = ARM_VOLT_STEP;
|
||||
dvfs_info.new_voltage = dvfs_step[step][ARM_VOLT_STEP];
|
||||
err = set_ltc3714(dvfs_info.pwr_type, dvfs_info.new_voltage);
|
||||
if(err == 0){
|
||||
dvfs_info.curr_voltage_arm = dvfs_info.new_voltage;
|
||||
}
|
||||
|
||||
dvfs_info.pwr_type = INT_VOLT_STEP;
|
||||
dvfs_info.new_voltage = dvfs_step[step][INT_VOLT_STEP];
|
||||
err = set_ltc3714(dvfs_info.pwr_type, dvfs_info.new_voltage);
|
||||
if(err == 0){
|
||||
dvfs_info.curr_voltage_internal = dvfs_info.new_voltage;
|
||||
}
|
||||
|
||||
/* CPU Frequency control */
|
||||
err = set_pll(dvfs_step[step][FREQ_STEP]);
|
||||
|
||||
} else {
|
||||
}
|
||||
previous_dvfs_step = step;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(set_dvfs_step);
|
||||
|
||||
int s3c_dvfs_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
int num = MINOR(inode->i_rdev);
|
||||
|
||||
printk("s3c_dvfs_open -> minor : %d\n", num);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t s3c_dvfs_read(struct file *filp, char *buf, size_t count,
|
||||
loff_t *f_pos)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t s3c_dvfs_write(struct file *filp, const char *buf, size_t count,
|
||||
loff_t *f_pos)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int s3c_dvfs_ioctl(struct inode *inode, struct file *filp,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int err, size;
|
||||
// printk("s3c_dvfs_ioctl -> cmd 0x%x, arg : 0x%x\n", cmd, arg);
|
||||
|
||||
if(_IOC_TYPE(cmd) != DVFS_IOCTL_MAGIC) return -EINVAL;
|
||||
if(_IOC_NR(cmd) >= DVFS_MAXNR) return -EINVAL;
|
||||
|
||||
size = _IOC_SIZE(cmd);
|
||||
|
||||
switch(cmd) {
|
||||
case DVFS_ON:
|
||||
break;
|
||||
case DVFS_OFF:
|
||||
break;
|
||||
case DVFS_GET_STATUS:
|
||||
err=copy_to_user((void *) arg, (const void *) &dvfs_info, (unsigned long) size);
|
||||
break;
|
||||
case DVFS_SET_STATUS:
|
||||
err=copy_from_user((void *) &dvfs_info, (const void *) arg, size);
|
||||
err = set_ltc3714(dvfs_info.pwr_type, dvfs_info.new_voltage);
|
||||
|
||||
if(err != 0) return err;
|
||||
|
||||
if(dvfs_info.pwr_type == ARM_VOLT_STEP) {
|
||||
dvfs_info.curr_voltage_arm = dvfs_info.new_voltage;
|
||||
} else if(dvfs_info.pwr_type == INT_VOLT_STEP) {
|
||||
dvfs_info.curr_voltage_internal = dvfs_info.new_voltage;
|
||||
} else {
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int s3c_dvfs_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
printk("s3c_dvfs_release \n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct file_operations s3c_dvfs_fops =
|
||||
{
|
||||
.owner = THIS_MODULE,
|
||||
// .llseek = s3c_dvfs_llseek,
|
||||
.read = s3c_dvfs_read,
|
||||
.write = s3c_dvfs_write,
|
||||
.ioctl = s3c_dvfs_ioctl,
|
||||
.open = s3c_dvfs_open,
|
||||
.release = s3c_dvfs_release,
|
||||
};
|
||||
|
||||
#ifdef SUPPORT_PROC_FS
|
||||
int read_proc_voltage(char *page, char **start, off_t off,
|
||||
int count, int *eof, void *data_unused)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
buf = page;
|
||||
buf += sprintf(buf, "ARM = [%d]mV, Internal = [%d]mV\n", dvfs_info.curr_voltage_arm,dvfs_info.curr_voltage_internal);
|
||||
|
||||
return buf - page;
|
||||
}
|
||||
|
||||
int write_proc_voltage(struct file *file, const char __user *buffer,
|
||||
unsigned long count, void *data)
|
||||
{
|
||||
int len, err;
|
||||
char *realdata;
|
||||
|
||||
realdata = (char *) data;
|
||||
|
||||
if(copy_from_user(realdata, buffer, count))
|
||||
return -EFAULT;
|
||||
|
||||
realdata[count] = '\0';
|
||||
len = strlen(realdata);
|
||||
if(realdata[len - 1] == '\n')
|
||||
realdata[--len] = 0;
|
||||
|
||||
dvfs_info.new_voltage = simple_strtoul(realdata, NULL, 10);
|
||||
|
||||
err = set_ltc3714(dvfs_info.pwr_type, dvfs_info.new_voltage);
|
||||
|
||||
if(err == 0) {
|
||||
if(dvfs_info.pwr_type == ARM_VOLT_STEP) {
|
||||
dvfs_info.curr_voltage_arm = dvfs_info.new_voltage;
|
||||
} else if(dvfs_info.pwr_type == INT_VOLT_STEP) {
|
||||
dvfs_info.curr_voltage_internal = dvfs_info.new_voltage;
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int read_proc_freq(char *page, char **start, off_t off,
|
||||
int count, int *eof, void *data_unused)
|
||||
{
|
||||
char *buf;
|
||||
unsigned int arm_freq,tmp;
|
||||
|
||||
arm_freq = GET_ARM_CLOCK(XTAL);
|
||||
dvfs_info.freq_divider = READ_ARM_DIV;
|
||||
dvfs_info.curr_freq = arm_freq/dvfs_info.freq_divider;
|
||||
|
||||
/*it's only supporting DVFS of S3C2443/2450/2416 */
|
||||
#if defined (CONFIG_MACH_SMDK2450) ||defined (CONFIG_MACH_SMDK2416) ||defined (CONFIG_MACH_SMDK2443)
|
||||
tmp=__raw_readl(ARM_CLK_DIV) ;
|
||||
if((tmp&DVFS_MASK)){ /* if DVFS set, HCLK use for ARMCLK*/
|
||||
dvfs_info.curr_freq = arm_freq/(((tmp & HCLK_DIV_MASK)+1)<<((tmp&0x8)>>3));
|
||||
}
|
||||
#endif
|
||||
|
||||
buf = page;
|
||||
buf += sprintf(buf, "Freq = [%d]MHz\n", dvfs_info.curr_freq/1000000);
|
||||
|
||||
return buf - page;
|
||||
}
|
||||
|
||||
int write_proc_freq(struct file *file, const char __user *buffer,
|
||||
unsigned long count, void *data)
|
||||
{
|
||||
int len, tmp, err;
|
||||
char *realdata;
|
||||
|
||||
realdata = (char *) data;
|
||||
|
||||
if(copy_from_user(realdata, buffer, count))
|
||||
return -EFAULT;
|
||||
|
||||
realdata[count] = '\0';
|
||||
len = strlen(realdata);
|
||||
if(realdata[len - 1] == '\n')
|
||||
realdata[--len] = 0;
|
||||
tmp = simple_strtoul(realdata, NULL, 10);
|
||||
|
||||
printk("level %d \n",tmp);
|
||||
|
||||
err = set_freq_divider(ARM_RATIO, tmp);
|
||||
if(err){
|
||||
printk("level err %d \n",err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int write_proc_step(struct file *file, const char __user *buffer, unsigned long count, void *data)
|
||||
{
|
||||
int len, tmp, err;
|
||||
char *realdata;
|
||||
|
||||
realdata = (char *) data;
|
||||
|
||||
if(copy_from_user(realdata, buffer, count))
|
||||
return -EFAULT;
|
||||
realdata[count] = '\0';
|
||||
len = strlen(realdata);
|
||||
if(realdata[len - 1] == '\n')
|
||||
realdata[--len] = 0;
|
||||
tmp = simple_strtoul(realdata, NULL, 10);
|
||||
|
||||
err = set_dvfs_step(tmp);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int s3c_dvfs_init(void)
|
||||
{
|
||||
int result;
|
||||
|
||||
/* Initialize structure */
|
||||
previous_dvfs_step = -1; /* 1st initial operating*/
|
||||
set_dvfs_gpio();
|
||||
set_dvfs_step(0); // Set Maximum or default initial performance set
|
||||
CONTROl_SET(DVS_OE, 0x1); /* Just open target power voltage*/
|
||||
|
||||
result = register_chrdev(DEV_MAJOR, DEV_NAME, &s3c_dvfs_fops);
|
||||
|
||||
if(result < 0)
|
||||
return result;
|
||||
|
||||
|
||||
|
||||
#ifdef SUPPORT_PROC_FS
|
||||
proc_root_fp = proc_mkdir("dvfs", 0);
|
||||
|
||||
proc_voltage_fp = create_proc_entry("voltage", S_IFREG | S_IRWXU, proc_root_fp);
|
||||
if(proc_voltage_fp) {
|
||||
proc_voltage_fp->data = proc_voltage_str;
|
||||
proc_voltage_fp->read_proc = read_proc_voltage;
|
||||
proc_voltage_fp->write_proc = write_proc_voltage;
|
||||
}
|
||||
|
||||
proc_freq_fp = create_proc_entry("frequency", S_IFREG | S_IRWXU, proc_root_fp);
|
||||
if(proc_freq_fp) {
|
||||
proc_freq_fp->data = proc_freq_str;
|
||||
proc_freq_fp->read_proc = read_proc_freq;
|
||||
proc_freq_fp->write_proc = write_proc_freq;
|
||||
}
|
||||
|
||||
proc_step_fp = create_proc_entry("step", S_IFREG | S_IWUSR, proc_root_fp);
|
||||
if(proc_step_fp) {
|
||||
proc_step_fp->data = proc_step_str;
|
||||
proc_step_fp->read_proc = NULL;
|
||||
proc_step_fp->write_proc = write_proc_step;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void s3c_dvfs_exit(void)
|
||||
{
|
||||
unregister_chrdev(DEV_MAJOR, DEV_NAME);
|
||||
remove_proc_entry("voltage", proc_root_fp);
|
||||
remove_proc_entry("frequency", proc_root_fp);
|
||||
remove_proc_entry("step", proc_root_fp);
|
||||
remove_proc_entry("dvfs", 0);
|
||||
}
|
||||
|
||||
module_init(s3c_dvfs_init);
|
||||
module_exit(s3c_dvfs_exit);
|
||||
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_DESCRIPTION("S3C64xx DVFS Driver");
|
||||
MODULE_AUTHOR("KWANGHYUN.LA, <nala.la@samsung.com>");
|
||||
116
drivers/char/s3c-dvfs/s3c-dvfs.h
Normal file
116
drivers/char/s3c-dvfs/s3c-dvfs.h
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
2008.04.14. All of AP are merged single application and definitin
|
||||
in order to be matcing dvs and dfs concept
|
||||
These are confirm on SMDK2450, SMDK2416,SMDK6400,SMDK6410
|
||||
*/
|
||||
#ifndef __S3CDVFS_H_
|
||||
#define __S3CDVFS_H_
|
||||
|
||||
#define DVFS_IOCTL_MAGIC 'd'
|
||||
|
||||
typedef struct {
|
||||
unsigned int pwr_type; //ARM:ARMV_STEP, INT:INTV_STEP
|
||||
unsigned int curr_voltage_arm; //ARM core voltage
|
||||
unsigned int curr_voltage_internal; //Internal block voltage
|
||||
unsigned int new_voltage;
|
||||
unsigned int curr_freq;
|
||||
unsigned int freq_divider; // APLL_RATIO + 1
|
||||
unsigned int size;
|
||||
} s3c_dvfs_info;
|
||||
|
||||
#define DVFS_ON _IO(DVFS_IOCTL_MAGIC, 0)
|
||||
#define DVFS_OFF _IO(DVFS_IOCTL_MAGIC, 1)
|
||||
#define DVFS_GET_STATUS _IOR(DVFS_IOCTL_MAGIC, 2, s3c_dvfs_info)
|
||||
#define DVFS_SET_STATUS _IOW(DVFS_IOCTL_MAGIC, 3, s3c_dvfs_info)
|
||||
|
||||
#define DVFS_MAXNR 4
|
||||
|
||||
|
||||
/* Board dependency SIGNAL and GPIO configuration */
|
||||
#if defined (CONFIG_MACH_SMDK6400) ||defined (CONFIG_MACH_SMDK6410)
|
||||
#include <asm/arch/regs-s3c6400-clock.h>
|
||||
#include <asm/arch/regs-s3c6410-clock.h>
|
||||
|
||||
#define ARM_LE S3C_GPL8
|
||||
#define DVS_OE S3C_GPL9
|
||||
#define INT_LE S3C_GPL10
|
||||
|
||||
#define CONTROl_SET(pin,to) s3c_gpio_setpin(pin, to);
|
||||
#define ARM_LE_OUTP S3C_GPL8_OUTP
|
||||
#define DVS_OE_OUTP S3C_GPL9_OUTP
|
||||
#define INT_LE_OUTP S3C_GPL10_OUTP
|
||||
|
||||
#define LTC3714_DATA1 S3C_GPN11
|
||||
#define LTC3714_DATA2 S3C_GPN12
|
||||
#define LTC3714_DATA3 S3C_GPN13
|
||||
#define LTC3714_DATA4 S3C_GPN14
|
||||
#define LTC3714_DATA5 S3C_GPN15
|
||||
|
||||
#define LTC3714_OUTP1 S3C_GPN11_OUTP
|
||||
#define LTC3714_OUTP2 S3C_GPN12_OUTP
|
||||
#define LTC3714_OUTP3 S3C_GPN13_OUTP
|
||||
#define LTC3714_OUTP4 S3C_GPN14_OUTP
|
||||
#define LTC3714_OUTP5 S3C_GPN15_OUTP
|
||||
|
||||
#define GPIO_CFG(pin,to) s3c_gpio_cfgpin((pin),(to))
|
||||
#define GPIO_PULLUP(pin,to) s3c_gpio_pullup((pin),(to))
|
||||
|
||||
#define ARM_PLL_CON S3C_APLL_CON
|
||||
#define ARM_CLK_DIV S3C_CLK_DIV0
|
||||
#define ARM_DIV_RATIO_BIT 0
|
||||
#define ARM_DIV_MASK (0xf<<ARM_DIV_RATIO_BIT)
|
||||
#define HCLK_DIV_RATIO_BIT 9
|
||||
#define HCLK_DIV_MASK (0x7<<HCLK_DIV_RATIO_BIT)
|
||||
|
||||
#define READ_ARM_DIV ((__raw_readl(ARM_CLK_DIV)&ARM_DIV_MASK) + 1)
|
||||
#define PLL_CALC_VAL(MDIV,PDIV,SDIV) ((1<<31)|(MDIV)<<16 |(PDIV)<<8 |(SDIV))
|
||||
#define GET_ARM_CLOCK(baseclk) s3c6400_get_pll(__raw_readl(S3C_APLL_CON),baseclk)
|
||||
|
||||
|
||||
#elif defined (CONFIG_MACH_SMDK2450) ||defined (CONFIG_MACH_SMDK2416) ||defined (CONFIG_MACH_SMDK2443)
|
||||
#include <asm/arch/regs-s3c2450-clock.h>
|
||||
#include <asm/arch/regs-s3c2416-clock.h>
|
||||
#include <asm/arch/regs-s3c2443-clock.h>
|
||||
|
||||
#define ARM_LE S3C2410_GPF5
|
||||
#define DVS_OE S3C2410_GPF6
|
||||
#define INT_LE S3C2410_GPF7
|
||||
|
||||
#define CONTROl_SET(pin,to) s3c2410_gpio_setpin(pin, to);
|
||||
#define ARM_LE_OUTP S3C2410_GPF5_OUTP
|
||||
#define DVS_OE_OUTP S3C2410_GPF6_OUTP
|
||||
#define INT_LE_OUTP S3C2410_GPF7_OUTP
|
||||
|
||||
#define LTC3714_DATA1 S3C2410_GPC0
|
||||
#define LTC3714_DATA2 S3C2410_GPC5
|
||||
#define LTC3714_DATA3 S3C2410_GPC6
|
||||
#define LTC3714_DATA4 S3C2410_GPC7
|
||||
#define LTC3714_DATA5 S3C2410_GPB2
|
||||
|
||||
#define LTC3714_OUTP1 S3C2410_GPC0_OUTP
|
||||
#define LTC3714_OUTP2 S3C2410_GPC5_OUTP
|
||||
#define LTC3714_OUTP3 S3C2410_GPC6_OUTP
|
||||
#define LTC3714_OUTP4 S3C2410_GPC7_OUTP
|
||||
#define LTC3714_OUTP5 S3C2410_GPB2_OUTP
|
||||
|
||||
#define GPIO_CFG(pin, fnc) s3c2410_gpio_cfgpin((pin),(fnc))
|
||||
#define GPIO_PULLUP(pin, to) s3c2410_gpio_pullup((pin),(to))
|
||||
|
||||
#define ARM_PLL_CON S3C2443_MPLLCON
|
||||
#define ARM_CLK_DIV S3C2443_CLKDIV0
|
||||
#define MPLL_CON S3C2443_MPLLCON
|
||||
#define ARM_DIV_RATIO_BIT 9
|
||||
#define ARM_DIV_MASK (0xf<<ARM_DIV_RATIO_BIT)
|
||||
#define HCLK_DIV_RATIO_BIT 0
|
||||
#define HCLK_DIV_MASK (0x3<<HCLK_DIV_RATIO_BIT)
|
||||
#define DVFS_MASK (0x1<<13)
|
||||
|
||||
#define READ_ARM_DIV (((__raw_readl(ARM_CLK_DIV)&ARM_DIV_MASK)>>9) + 1)
|
||||
#define PLL_CALC_VAL(MDIV,PDIV,SDIV) ((MDIV)<<14 |(PDIV)<<5 |(SDIV))
|
||||
#define GET_ARM_CLOCK(baseclk) s3c2443_get_mpll(__raw_readl(S3C2443_MPLLCON),baseclk)
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif //__S3CDVS_H_
|
||||
Reference in New Issue
Block a user