diff options
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/mach-wmt/wmt_clk.c | 2378 | ||||
-rw-r--r-- | arch/arm/mach-wmt/wmt_cpufreq.c | 752 | ||||
-rw-r--r-- | arch/arm/mach-wmt/wmt_time.c | 460 |
3 files changed, 3590 insertions, 0 deletions
diff --git a/arch/arm/mach-wmt/wmt_clk.c b/arch/arm/mach-wmt/wmt_clk.c new file mode 100644 index 00000000..c7d5a322 --- /dev/null +++ b/arch/arm/mach-wmt/wmt_clk.c @@ -0,0 +1,2378 @@ +/*++ +linux/arch/arm/mach-wmt/wmt_clk.c + +Copyright (c) 2008 WonderMedia Technologies, Inc. + +This program is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software Foundation, +either version 2 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. +You should have received a copy of the GNU General Public License along with +this program. If not, see <http://www.gnu.org/licenses/>. + +WonderMedia Technologies, Inc. +10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C. +--*/ + +#include <linux/mtd/mtd.h> +#include "wmt_clk.h" +#include <mach/hardware.h> + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/cpufreq.h> +#include <linux/ioport.h> +#include <linux/platform_device.h> + +#include <asm/system.h> +#include <asm/pgtable.h> +#include <asm/mach/map.h> +#include <asm/irq.h> +#include <asm/sizes.h> +#include <linux/i2c.h> +#include <asm/div64.h> +#include <linux/mutex.h> +#include <linux/regulator/consumer.h> +#include <linux/interrupt.h> + +#include "generic.h" + +#define MMFREQ_VD DEV_CNMVDU // DEV_MSVD +int mmfreq_num = ~0; +int mmfreq_cur_num = ~0; +int mmfreq_type_mask; +int mmfreq_debug; +struct regulator *re; + +#define PMC_BASE PM_CTRL_BASE_ADDR +#define PMC_PLL (PM_CTRL_BASE_ADDR + 0x200) +#define PMC_CLK (PM_CTRL_BASE_ADDR + 0x250) +#define WM8980_SCC_BASE 0xD8110110 +#define CPINFO (WM8980_SCC_BASE + WMT_MMAP_OFFSET) + +#define PLL_BUSY 0x7F0038 +#define MAX_DF ((1<<7) - 1) +#define MAX_DR 1 //((1<<5) - 1) +#define MAX_DQ ((1<<2) - 1) +#define GET_DIVF(d) ((((d>>16)&MAX_DF)+1)*2) +#define GET_DIVR(d) (((d>>8)&MAX_DR)+1) +#define GET_DIVQ(d) (1<<(d&MAX_DQ)) + +#define SRC_FREQ25 25 +#define SRC_FREQ24 24 +#define SRC_FREQ SRC_FREQ24 +/*#define debug_clk*/ +extern int wmt_getsyspara(char *varname, char *varval, int *varlen); +#define DEBUG_PLLA +/* +#define DEBUG +*/ +#ifdef DEBUG +static int dbg_mask = 1; +module_param(dbg_mask, int, S_IRUGO | S_IWUSR); +#define fq_dbg(fmt, args...) \ + do {\ + if (dbg_mask) \ + printk(KERN_ERR "[%s]_%d_%d: " fmt, __func__ , __LINE__, smp_processor_id(), ## args);\ + } while (0) +#define fq_trace() \ + do {\ + if (dbg_mask) \ + printk(KERN_ERR "trace in %s %d\n", __func__, __LINE__);\ + } while (0) +#else +#define fq_dbg(fmt, args...) +#define fq_trace() +#endif +extern int wmt_getsyspara(char *varname, char *varval, int *varlen); +static int dev_en_count[128] = {0}; +struct pll_map pllmap[] = {//total 168 +{126, 0x00290103},{129, 0x002A0103},{132, 0x002B0103},{135, 0x002C0103},{138, 0x002D0103}, +{141, 0x002E0103},{144, 0x002F0103},{147, 0x00300103},{150, 0x00310103},{153, 0x00320103}, +{156, 0x00330103},{159, 0x00340103},{162, 0x00350103},{165, 0x00360103},{168, 0x00370103}, +{171, 0x00380103},{174, 0x00390103},{177, 0x003A0103},{180, 0x003B0103},{183, 0x003C0103}, +{186, 0x003D0103},{189, 0x003E0103},{192, 0x003F0103},{195, 0x00400103},{198, 0x00410103}, +{201, 0x00420103},{204, 0x00430103},{207, 0x00440103},{210, 0x00450103},{213, 0x00460103}, +{216, 0x00470103},{219, 0x00480103},{222, 0x00490103},{225, 0x004A0103},{228, 0x004B0103}, +{231, 0x004C0103},{234, 0x004D0103},{237, 0x004E0103},{240, 0x004F0103},{243, 0x00500103}, +{246, 0x00510103},{249, 0x00520103},{252, 0x00290102},{258, 0x002A0102},{264, 0x002B0102}, +{270, 0x002C0102},{276, 0x002D0102},{282, 0x002E0102},{288, 0x002F0102},{294, 0x00300102}, +{300, 0x00310102},{306, 0x00320102},{312, 0x00330102},{318, 0x00340102},{324, 0x00350102}, +{330, 0x00360102},{336, 0x00370102},{342, 0x00380102},{348, 0x00390102},{354, 0x003A0102}, +{360, 0x003B0102},{366, 0x003C0102},{372, 0x003D0102},{378, 0x003E0102},{384, 0x003F0102}, +{390, 0x00400102},{396, 0x00410102},{402, 0x00420102},{408, 0x00430102},{414, 0x00440102}, +{420, 0x00450102},{426, 0x00460102},{432, 0x00470102},{438, 0x00480102},{444, 0x00490102}, +{450, 0x004A0102},{456, 0x004B0102},{462, 0x004C0102},{468, 0x004D0102},{474, 0x004E0102}, +{480, 0x004F0102},{486, 0x00500102},{492, 0x00510102},{498, 0x00520102},{504, 0x00290101}, +{516, 0x002A0101},{528, 0x002B0101},{540, 0x002C0101},{552, 0x002D0101},{564, 0x002E0101}, +{576, 0x002F0101},{588, 0x00300101},{600, 0x00310101},{612, 0x00320101},{624, 0x00330101}, +{636, 0x00340101},{648, 0x00350101},{660, 0x00360101},{672, 0x00370101},{684, 0x00380101}, +{696, 0x00390101},{708, 0x003A0101},{720, 0x003B0101},{732, 0x003C0101},{744, 0x003D0101}, +{756, 0x003E0101},{768, 0x003F0101},{780, 0x00400101},{792, 0x00410101},{804, 0x00420101}, +{816, 0x00430101},{828, 0x00440101},{840, 0x00450101},{852, 0x00460101},{864, 0x00470101}, +{876, 0x00480101},{888, 0x00490101},{900, 0x004A0101},{912, 0x004B0101},{924, 0x004C0101}, +{936, 0x004D0101},{948, 0x004E0101},{960, 0x004F0101},{972, 0x00500101},{984, 0x00510101}, +{996, 0x00520101},{1008, 0x00290100},{1032, 0x002A0100},{1056, 0x002B0100},{1080, 0x002C0100}, +{1104, 0x002D0100},{1128, 0x002E0100},{1152, 0x002F0100},{1176, 0x00300100},{1200, 0x00310100}, +{1224, 0x00320100},{1248, 0x00330100},{1272, 0x00340100},{1296, 0x00350100},{1320, 0x00360100}, +{1344, 0x00370100},{1368, 0x00380100},{1392, 0x00390100},{1416, 0x003A0100},{1440, 0x003B0100}, +{1464, 0x003C0100},{1488, 0x003D0100},{1512, 0x003E0100},{1536, 0x003F0100},{1560, 0x00400100}, +{1584, 0x00410100},{1608, 0x00420100},{1632, 0x00430100},{1656, 0x00440100},{1680, 0x00450100}, +{1704, 0x00460100},{1728, 0x00470100},{1752, 0x00480100},{1776, 0x00490100},{1800, 0x004A0100}, +{1824, 0x004B0100},{1848, 0x004C0100},{1872, 0x004D0100},{1896, 0x004E0100},{1920, 0x004F0100}, +{1944, 0x00500100},{1968, 0x00510100},{1992, 0x00520100}//,2016, 0x00530100 +}; + +//total 43+88+88+172 = 391 +struct pll_map pllmapAll[] = { +{1008, 0x00290100},{504, 0x00290101},{252, 0x00290102},{126, 0x00290103}, +{1032, 0x002A0100},{516, 0x002A0101},{258, 0x002A0102},{129, 0x002A0103}, +{1056, 0x002B0100},{528, 0x002B0101},{264, 0x002B0102},{132, 0x002B0103}, +{1080, 0x002C0100},{540, 0x002C0101},{270, 0x002C0102},{135, 0x002C0103}, +{1104, 0x002D0100},{552, 0x002D0101},{276, 0x002D0102},{138, 0x002D0103}, +{1128, 0x002E0100},{564, 0x002E0101},{282, 0x002E0102},{141, 0x002E0103}, +{1152, 0x002F0100},{576, 0x002F0101},{288, 0x002F0102},{144, 0x002F0103}, +{1176, 0x00300100},{588, 0x00300101},{294, 0x00300102},{147, 0x00300103}, +{1200, 0x00310100},{600, 0x00310101},{300, 0x00310102},{150, 0x00310103}, +{1224, 0x00320100},{612, 0x00320101},{306, 0x00320102},{153, 0x00320103}, +{1248, 0x00330100},{624, 0x00330101},{312, 0x00330102},{156, 0x00330103}, +{1272, 0x00340100},{636, 0x00340101},{318, 0x00340102},{159, 0x00340103}, +{1296, 0x00350100},{648, 0x00350101},{324, 0x00350102},{162, 0x00350103}, +{1320, 0x00360100},{660, 0x00360101},{330, 0x00360102},{165, 0x00360103}, +{1344, 0x00370100},{672, 0x00370101},{336, 0x00370102},{168, 0x00370103}, +{1368, 0x00380100},{684, 0x00380101},{342, 0x00380102},{171, 0x00380103}, +{1392, 0x00390100},{696, 0x00390101},{348, 0x00390102},{174, 0x00390103}, +{1416, 0x003A0100},{708, 0x003A0101},{354, 0x003A0102},{177, 0x003A0103}, +{1440, 0x003B0100},{720, 0x003B0101},{360, 0x003B0102},{180, 0x003B0103}, +{1464, 0x003C0100},{732, 0x003C0101},{366, 0x003C0102},{183, 0x003C0103}, +{1488, 0x003D0100},{744, 0x003D0101},{372, 0x003D0102},{186, 0x003D0103}, +{1512, 0x003E0100},{756, 0x003E0101},{378, 0x003E0102},{189, 0x003E0103}, +{1536, 0x003F0100},{768, 0x003F0101},{384, 0x003F0102},{192, 0x003F0103}, +{1560, 0x00400100},{780, 0x00400101},{390, 0x00400102},{195, 0x00400103}, +{1584, 0x00410100},{792, 0x00410101},{396, 0x00410102},{198, 0x00410103}, +{1608, 0x00420100},{804, 0x00420101},{402, 0x00420102},{201, 0x00420103}, +{1632, 0x00430100},{816, 0x00430101},{408, 0x00430102},{204, 0x00430103}, +{1656, 0x00440100},{828, 0x00440101},{414, 0x00440102},{207, 0x00440103}, +{1680, 0x00450100},{840, 0x00450101},{420, 0x00450102},{210, 0x00450103}, +{1704, 0x00460100},{852, 0x00460101},{426, 0x00460102},{213, 0x00460103}, +{1728, 0x00470100},{864, 0x00470101},{432, 0x00470102},{216, 0x00470103}, +{1752, 0x00480100},{876, 0x00480101},{438, 0x00480102},{219, 0x00480103}, +{1776, 0x00490100},{888, 0x00490101},{444, 0x00490102},{222, 0x00490103}, +{1800, 0x004A0100},{900, 0x004A0101},{450, 0x004A0102},{225, 0x004A0103}, +{1824, 0x004B0100},{912, 0x004B0101},{456, 0x004B0102},{228, 0x004B0103}, +{1848, 0x004C0100},{924, 0x004C0101},{462, 0x004C0102},{231, 0x004C0103}, +{1872, 0x004D0100},{936, 0x004D0101},{468, 0x004D0102},{234, 0x004D0103}, +{1896, 0x004E0100},{948, 0x004E0101},{474, 0x004E0102},{237, 0x004E0103}, +{1920, 0x004F0100},{960, 0x004F0101},{480, 0x004F0102},{240, 0x004F0103}, +{1944, 0x00500100},{972, 0x00500101},{486, 0x00500102},{243, 0x00500103}, +{1968, 0x00510100},{984, 0x00510101},{492, 0x00510102},{246, 0x00510103}, +{1992, 0x00520100},{996, 0x00520101},{498, 0x00520102},{249, 0x00520103}, +{2016, 0x00530100},{1008, 0x00530101},{504, 0x00530102},{252, 0x00530103}, + + +{1008, 0x00140000}, {504, 0x00140001}, {252, 0x00140002}, {126, 0x00140003}, +{1056, 0x00150000}, {528, 0x00150001}, {264, 0x00150002}, {132, 0x00150003}, +{1104, 0x00160000}, {552, 0x00160001}, {276, 0x00160002}, {138, 0x00160003}, +{1152, 0x00170000}, {576, 0x00170001}, {288, 0x00170002}, {144, 0x00170003}, +{1200, 0x00180000}, {600, 0x00180001}, {300, 0x00180002}, {150, 0x00180003}, +{1248, 0x00190000}, {624, 0x00190001}, {312, 0x00190002}, {156, 0x00190003}, +{1296, 0x001A0000}, {648, 0x001A0001}, {324, 0x001A0002}, {162, 0x001A0003}, +{1344, 0x001B0000}, {672, 0x001B0001}, {336, 0x001B0002}, {168, 0x001B0003}, +{1392, 0x001C0000}, {696, 0x001C0001}, {348, 0x001C0002}, {174, 0x001C0003}, +{1440, 0x001D0000}, {720, 0x001D0001}, {360, 0x001D0002}, {180, 0x001D0003}, +{1488, 0x001E0000}, {744, 0x001E0001}, {372, 0x001E0002}, {186, 0x001E0003}, +{1536, 0x001F0000}, {768, 0x001F0001}, {384, 0x001F0002}, {192, 0x001F0003}, +{1584, 0x00200000}, {792, 0x00200001}, {396, 0x00200002}, {198, 0x00200003}, +{1632, 0x00210000}, {816, 0x00210001}, {408, 0x00210002}, {204, 0x00210003}, +{1680, 0x00220000}, {840, 0x00220001}, {420, 0x00220002}, {210, 0x00220003}, +{1728, 0x00230000}, {864, 0x00230001}, {432, 0x00230002}, {216, 0x00230003}, +{1776, 0x00240000}, {888, 0x00240001}, {444, 0x00240002}, {222, 0x00240003}, +{1824, 0x00250000}, {912, 0x00250001}, {456, 0x00250002}, {228, 0x00250003}, +{1872, 0x00260000}, {936, 0x00260001}, {468, 0x00260002}, {234, 0x00260003}, +{1920, 0x00270000}, {960, 0x00270001}, {480, 0x00270002}, {240, 0x00270003}, +{1968, 0x00280000}, {984, 0x00280001}, {492, 0x00280002}, {246, 0x00280003}, +{2016, 0x00290000}, {1008, 0x00290001}, {504, 0x00290002}, {252, 0x00290003}, + +{144, 0x00020000}, +{192, 0x00030000}, +{240, 0x00040000}, +{288, 0x00050000}, {144, 0x00050001}, +{336, 0x00060000}, {168, 0x00060001}, +{384, 0x00070000}, {192, 0x00070001}, +{432, 0x00080000}, {216, 0x00080001}, +{480, 0x00090000}, {240, 0x00090001}, +{528, 0x000A0000}, {264, 0x000A0001}, {132, 0x000A0002}, +{576, 0x000B0000}, {288, 0x000B0001}, {144, 0x000B0002}, +{624, 0x000C0000}, {312, 0x000C0001}, {156, 0x000C0002}, +{672, 0x000D0000}, {336, 0x000D0001}, {168, 0x000D0002}, +{720, 0x000E0000}, {360, 0x000E0001}, {180, 0x000E0002}, +{768, 0x000F0000}, {384, 0x000F0001}, {192, 0x000F0002}, +{816, 0x00100000}, {408, 0x00100001}, {204, 0x00100002}, +{864, 0x00110000}, {432, 0x00110001}, {216, 0x00110002}, +{912, 0x00120000}, {456, 0x00120001}, {228, 0x00120002}, +{960, 0x00130000}, {480, 0x00130001}, {240, 0x00130002}, + +{144, 0x00050100}, +{168, 0x00060100}, +{192, 0x00070100}, +{216, 0x00080100}, +{240, 0x00090100}, +{264, 0x000A0100}, {132, 0x000A0101}, +{288, 0x000B0100}, {144, 0x000B0101}, +{312, 0x000C0100}, {156, 0x000C0101}, +{336, 0x000D0100}, {168, 0x000D0101}, +{360, 0x000E0100}, {180, 0x000E0101}, +{384, 0x000F0100}, {192, 0x000F0101}, +{408, 0x00100100}, {204, 0x00100101}, +{432, 0x00110100}, {216, 0x00110101}, +{456, 0x00120100}, {228, 0x00120101}, +{480, 0x00130100}, {240, 0x00130101}, +{504, 0x00140100}, {252, 0x00140101}, {126, 0x00140102}, +{528, 0x00150100}, {264, 0x00150101}, {132, 0x00150102}, +{552, 0x00160100}, {276, 0x00160101}, {138, 0x00160102}, +{576, 0x00170100}, {288, 0x00170101}, {144, 0x00170102}, +{600, 0x00180100}, {300, 0x00180101}, {150, 0x00180102}, +{624, 0x00190100}, {312, 0x00190101}, {156, 0x00190102}, +{648, 0x001A0100}, {324, 0x001A0101}, {162, 0x001A0102}, +{672, 0x001B0100}, {336, 0x001B0101}, {168, 0x001B0102}, +{696, 0x001C0100}, {348, 0x001C0101}, {174, 0x001C0102}, +{720, 0x001D0100}, {360, 0x001D0101}, {180, 0x001D0102}, +{744, 0x001E0100}, {372, 0x001E0101}, {186, 0x001E0102}, +{768, 0x001F0100}, {384, 0x001F0101}, {192, 0x001F0102}, +{792, 0x00200100}, {396, 0x00200101}, {198, 0x00200102}, +{816, 0x00210100}, {408, 0x00210101}, {204, 0x00210102}, +{840, 0x00220100}, {420, 0x00220101}, {210, 0x00220102}, +{864, 0x00230100}, {432, 0x00230101}, {216, 0x00230102}, +{888, 0x00240100}, {444, 0x00240101}, {222, 0x00240102}, +{912, 0x00250100}, {456, 0x00250101}, {228, 0x00250102}, +{936, 0x00260100}, {468, 0x00260101}, {234, 0x00260102}, +{960, 0x00270100}, {480, 0x00270101}, {240, 0x00270102}, +{984, 0x00280100}, {492, 0x00280101}, {246, 0x00280102} +}; + +#ifndef ARRAYSIZE +#define ARRAYSIZE(a) (sizeof(a) / sizeof(a[0])) +#endif +#define MAXPLL 3515625*(1<<8) +unsigned int tblpll = MAXPLL/1000000; +unsigned int fr_rdn = 0; +static DEFINE_SPINLOCK(wmt_clk_irqlock); +unsigned long wmt_clk_irqlock_flags; + +void wmt_mc5_autotune(void)//gri +{ + unsigned int reg_val, reg_tmp; + + MC_CLOCK_CTRL0_VAL = 0x0; + wmb(); + MC_CLOCK_CTRL0_VAL = 0x80000000; + while(MC_CLOCK_CTRL0_VAL != 0xC0000000); + if ((MC_CONF_VAL & 0x3) == 0x2) { + //ddr3 + reg_val = (MC_CLOCK_CTRL1_VAL & 0xffff8080); + reg_tmp = (0x7f0000 & reg_val); + reg_tmp |= ((0x7f0000 & reg_val) >> 8); + reg_tmp |= ((0x7f0000 & reg_val) >> 16); + reg_val |= reg_tmp; + MC_CLOCK_CTRL1_VAL = reg_val; + } else { + //lpddr2 + reg_val = (MC_CLOCK_CTRL1_VAL & 0xffff0000); + reg_tmp = (0x7f000000 & reg_val); + reg_tmp |= ((0x7f000000 & reg_val) >> 16); + reg_tmp |= ((0x7f000000 & reg_val) >> 24); + reg_val |= reg_tmp; + MC_CLOCK_CTRL1_VAL = reg_val; + } +} + +void wmt_clk_mutex_lock(int lock) +{ + if (lock) { + spin_lock_irqsave(&wmt_clk_irqlock, wmt_clk_irqlock_flags); + } else { + spin_unlock_irqrestore(&wmt_clk_irqlock, wmt_clk_irqlock_flags); + } +} + +static void check_PLL_DIV_busy(void) +{ + while ((*(volatile unsigned int *)(PMC_BASE+0x18))&PLL_BUSY) + ; +} + +#ifdef debug_clk +static void print_refer_count(void) +{ + int i; + for (i = 0; i < 4; i++) { + printk("clk cnt %d ~ %d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n",i*16,(i*16)+15, + dev_en_count[i*16],dev_en_count[i*16+1],dev_en_count[i*16+2],dev_en_count[i*16+3],dev_en_count[i*16+4],dev_en_count[i*16+5],dev_en_count[i*16+6],dev_en_count[i*16+7], + dev_en_count[i*16+8],dev_en_count[i*16+9],dev_en_count[i*16+10],dev_en_count[i*16+11],dev_en_count[i*16+12],dev_en_count[i*16+13],dev_en_count[i*16+14],dev_en_count[i*16+15]); + } +} +#endif +#if 0 +static int disable_dev_clk(enum dev_id dev) +{ + int en_count; + + if (dev >= 128) { + printk(KERN_INFO"device dev_id = %d > 128\n",dev); + return -1; + } + + #ifdef debug_clk + print_refer_count(); + #endif + + en_count = dev_en_count[dev]; + + if (en_count <= 1) { + dev_en_count[dev] = en_count = 0; + *(volatile unsigned int *)(PMC_CLK + 4*(dev/32)) + &= ~(1 << (dev - 32*(dev/32))); + *(volatile unsigned char *)(PMC_BASE + 0x604) = 0; + while((REG32_VAL((PMC_BASE + 0x604)) & 0xf5) != 0x00){}; + *(volatile unsigned char *)(PMC_BASE + 0x62C) = 0; + while((REG32_VAL((PMC_BASE + 0x62C)) & 0xf5) != 0x00){}; + /*printk(KERN_INFO" really disable clock\n");*/ + } else if (en_count > 1) { + dev_en_count[dev] = (--en_count); + } + + #ifdef debug_clk + print_refer_count(); + #endif + + return en_count; +} +#endif +/* +* ON : clock enable : 1, clock disable : 0 +* +*/ +static int enable_dev_clk(enum dev_id dev, int ON) +{ + int en_count, tmp; + + if (dev > 128) { + printk(KERN_INFO"device dev_id = %d > 128\n",dev); + return -1; + } + + #ifdef debug_clk + printk(KERN_INFO"device dev_id = %d\n",dev); + print_refer_count(); + #endif + + //mutex_lock(&clk_cnt_lock); + wmt_clk_mutex_lock(1); + en_count = dev_en_count[dev]; + + if (ON) { + tmp = *(volatile unsigned int *)(PMC_CLK + 4*(dev/32)); + if (en_count <= 0 || !(tmp&(1 << (dev - 32*(dev/32))))) { + dev_en_count[dev] = en_count = 1; + *(volatile unsigned int *)(PMC_CLK + 4*(dev/32)) + |= 1 << (dev - 32*(dev/32)); + /*if (dev == DEV_MSVD) { + *(volatile unsigned char *)(PMC_BASE + 0x604) = 1; + while((REG32_VAL((PMC_BASE + 0x604)) & 0xf5) != 0xf1){}; + *(volatile unsigned char *)(PMC_BASE + 0x62C) = 1; + while((REG32_VAL((PMC_BASE + 0x62C)) & 0xf5) != 0xf1){}; + }*/ + /*printk(KERN_INFO" really enable clock\n");*/ + } else + dev_en_count[dev] = (++en_count); + } else { + if (en_count <= 1) { + dev_en_count[dev] = en_count = 0; + *(volatile unsigned int *)(PMC_CLK + 4*(dev/32)) + &= ~(1 << (dev - 32*(dev/32))); + /**(volatile unsigned char *)(PMC_BASE + 0x604) = 0; + while((REG32_VAL((PMC_BASE + 0x604)) & 0xf5) != 0x00){}; + *(volatile unsigned char *)(PMC_BASE + 0x62C) = 0; + while((REG32_VAL((PMC_BASE + 0x62C)) & 0xf5) != 0x00){};*/ + /*printk(KERN_INFO" really disable clock\n");*/ + } else if (en_count > 1) { + dev_en_count[dev] = (--en_count); + } + } + //mutex_unlock(&clk_cnt_lock); + wmt_clk_mutex_lock(0); + + #ifdef debug_clk + print_refer_count(); + #endif + + /* mmfreq */ + if ((dev == MMFREQ_VD) && (mmfreq_num != ~0)) { + int cnt; + wmt_clk_mutex_lock(1); + cnt = dev_en_count[MMFREQ_VD]; + wmt_clk_mutex_lock(0); + if (cnt == 0) + wmt_set_mmfreq(mmfreq_num); + } + + return en_count; +} +/* +* PLLA return 0, PLLB return 1, +* PLLC return 2, PLLD return 3, +* PLLE return 4, PLLF return 5, +* PLLG return 6, +* device not found return -1. +* device has no divisor but has clock enable return -2. +*/ +static int calc_pll_num(enum dev_id dev, int *div_offset) +{ + switch (dev) { + case DEV_UART0: + case DEV_UART1: + case DEV_UART2: + case DEV_UART3: /*case DEV_UART4: case DEV_UART5:*/ + case DEV_AHBB: + case DEV_CIR: + case DEV_SYS: /* no use */ + case DEV_RTC: + case DEV_UHDC: + case DEV_PERM: /* no use */ + case DEV_PDMA: /* no use */ + case DEV_VID: + *div_offset = 0; + return -2; + case DEV_SAE: + *div_offset = 0x348; + return 1; + case DEV_C24MOUT: + *div_offset = 0x3E4; + return 1; + case DEV_I2C0: + *div_offset = 0x3A0; + return 1; + case DEV_I2C1: + *div_offset = 0x3A4; + return 1; + case DEV_I2C2: + *div_offset = 0x3A8; + return 1; + case DEV_I2C3: + *div_offset = 0x3AC; + return 1; + case DEV_I2C4: + *div_offset = 0x39C; + return 1; + case DEV_PWM: + *div_offset = 0x350; + return 1; + case DEV_SPI0: + *div_offset = 0x340; + return 1; + case DEV_SPI1: + *div_offset = 0x344; + return 1; + case DEV_HDMILVDS: + case DEV_SDTV: + case DEV_HDMI: + *div_offset = 0x36C; + return 2; + case DEV_DVO: + case DEV_LVDS: + *div_offset = 0x370; + return 6; + case DEV_CSI0: + *div_offset = 0x380; + return 1; + case DEV_CSI1: + *div_offset = 0x384; + return 1; + case DEV_MALI: + *div_offset = 0x388; + return 1; + case DEV_DDRMC: + *div_offset = 0x310; + return 3; + case DEV_WMTNA: + *div_offset = 0x358; + return 3; + case DEV_CNMNA: + *div_offset = 0x360; + return 3; + case DEV_WMTVDU: + case DEV_JENC: + case DEV_HDCE: + case DEV_H264: + case DEV_JDEC: + case DEV_MSVD: + case DEV_AMP: + case DEV_VPU: + case DEV_MBOX: + *div_offset = 0x368; + return 3; + case DEV_CNMVDU: + case DEV_VP8DEC: + *div_offset = 0x38C; + return 3; + case DEV_NA12: + case DEV_VPP: + case DEV_GE: + case DEV_DISP: + case DEV_GOVRHD: + case DEV_SCL444U: + case DEV_GOVW: + *div_offset = 0x35C; + return 1; + case DEV_PAXI: + case DEV_DMA: + *div_offset = 0x354; + return 1; + case DEV_L2C: + *div_offset = 0x30C; + return 0; + case DEV_L2CAXI: + *div_offset = 0x3B0; + return 0; + case DEV_AT: + *div_offset = 0x3B4; + return 0; + case DEV_PERI: + *div_offset = 0x3B8; + return 0; + case DEV_TRACE: + *div_offset = 0x3BC; + return 0; + case DEV_L2CPAXI: + *div_offset = 0x3C0; + return 0; + case DEV_DBG: + *div_offset = 0x3D0; + return 0; + case DEV_NAND: + *div_offset = 0x318; + return 1; + /*case DEV_NOR: + *div_offset = 0x31C; + return 1;*/ + case DEV_SDMMC0: + *div_offset = 0x330; + return 1; + case DEV_SDMMC1: + *div_offset = 0x334; + return 1; + case DEV_SDMMC2: + *div_offset = 0x338; + return 1; + /*case DEV_SDMMC3: + *div_offset = 0x33C; + return 1;*/ + case DEV_ADC: + *div_offset = 0x394; + return 1; + case DEV_HDMII2C: /* div 1~63 */ + *div_offset = 0x390; + return 4; + case DEV_SF: + *div_offset = 0x314; + return 1; + case DEV_PCM0: + *div_offset = 0x324; + return 1; + case DEV_PCM1: + *div_offset = 0x328; + return 1; + case DEV_I2S: + case DEV_ARF: + case DEV_ARFP: + *div_offset = 0x374; + return 4; + case DEV_ARM: + *div_offset = 0x300; + return 0; + case DEV_AHB: + *div_offset = 0x304; + return 1; + case DEV_APB: + case DEV_SCC: + case DEV_GPIO: + *div_offset = 0x320; + return 1; + /*case DEV_ETHPHY: + *div_offset = 25; + return -2;*/ + default: + return -1; + } +} + + +/* if the clk of the dev is not enable, then enable it or fail */ +int check_clk_enabled(enum dev_id dev, enum clk_cmd cmd) { + unsigned int pmc_clk_en; + if (dev < 128) { + pmc_clk_en = *(volatile unsigned int *)(PMC_CLK + 4*(dev/32)); + if (!(pmc_clk_en & (1 << (dev - 32*(dev/32))))) { + if (dev == DEV_ARM) + return 0; + /*enable_dev_clk(dev);*/ + printk(KERN_INFO"dev[%d] clock disabled\n", dev); + return -1; + } + } + return 0; +} +#if 0 +static int get_freq(enum dev_id dev, int *divisor) { + + unsigned int div = 0, freq; + int PLL_NO, j = 0, div_addr_offs; + unsigned long long freq_llu, base = 1000000, tmp, mod; + + PLL_NO = calc_pll_num(dev, &div_addr_offs); + if (PLL_NO == -1) { + printk(KERN_INFO"device not found"); + return -1; + } + + if (PLL_NO == -2) + return div_addr_offs*1000000; + + *(volatile unsigned char *)(PMC_BASE + div_addr_offs + 2) = (dev == DEV_SDTV) ? 1 : 0; + + check_PLL_DIV_busy(); + + div = *divisor = *(volatile unsigned char *)(PMC_BASE + div_addr_offs); + //printk(KERN_INFO"div_addr_offs=0x%x PLL_NO=%d PMC_PLL=0x%x\n", div_addr_offs, PLL_NO, PMC_PLL); + tmp = *(volatile unsigned int *)(PMC_PLL + 4*PLL_NO); + + if ((dev != DEV_SDMMC0) && (dev != DEV_SDMMC1) && (dev != DEV_SDMMC2)) { + div = div&0x1F; + } else { + if (div & (1<<5)) + j = 1; + div &= 0x1F; + div = div*(j?64:1)*2; + } + + freq_llu = (SRC_FREQ*GET_DIVF(tmp)) * base; + //printk(KERN_INFO" freq_llu =%llu\n", freq_llu); + tmp = GET_DIVR(tmp) * GET_DIVQ(tmp) * div; + mod = do_div(freq_llu, tmp); + freq = (unsigned int)freq_llu; + //printk("get_freq cmd: freq=%d freq(unsigned long long)=%llu div=%llu mod=%llu\n", freq, freq_llu, tmp, mod); + + return freq; +} +#endif +extern unsigned int wmt_read_oscr(void); +static int get_freq_t(enum dev_id dev, int *divisor) { + + unsigned int div = 0, freq = 0, div_ahb = 1; + int PLL_NO, i, j = 0, div_addr_offs; + unsigned long long freq_llu = 0, base = 1000000, tmp, mod; + #ifdef DEBUG_CLK + unsigned int t1 = 0, t2 = 0; + #endif + + if (dev == DEV_APB) { + PLL_NO = calc_pll_num(DEV_AHB, &div_addr_offs); + if (PLL_NO < 0) { + printk(KERN_INFO"device not found"); + return -1; + } + div_ahb = *(volatile unsigned char *)(PMC_BASE + div_addr_offs); + } + + PLL_NO = calc_pll_num(dev, &div_addr_offs); + if (PLL_NO == -1) { + printk(KERN_INFO"device not found"); + return -1; + } + + if (PLL_NO == -2) + return div_addr_offs*1000000; + + *(volatile unsigned char *)(PMC_BASE + div_addr_offs + 2) = (dev == DEV_SDTV) ? 1 : 0; + + check_PLL_DIV_busy(); + + div = *divisor = *(volatile unsigned char *)(PMC_BASE + div_addr_offs); + + if ((dev != DEV_SDMMC0) && (dev != DEV_SDMMC1) && (dev != DEV_SDMMC2)) { + div = div&0x1F; + } else { + if (div & (1<<5)) + j = 1; + div &= 0x1F; + div = div*(j?64:1)*2; + } + + #ifdef DEBUG_CLK + printk(KERN_INFO"div_addr_offs=0x%x PLL_NO=%d PMC_PLL=0x%x\n", div_addr_offs, PLL_NO, PMC_PLL); + t1 = wmt_read_oscr(); + #endif + //mutex_lock(&clk_cnt_lock); + tmp = *(volatile unsigned int *)(PMC_PLL + 4*PLL_NO); + //mutex_unlock(&clk_cnt_lock); + for (i = 0; i < ARRAYSIZE(pllmapAll); i++) { + if (pllmapAll[i].pll == tmp) { + freq = pllmapAll[i].freq; + #ifdef DEBUG_CLK + printk("********dev%d---PLL_NO=%X---get freq=%d set0x%x\n", dev, PLL_NO+10, freq, pllmapAll[i].pll); + #endif + break; + } + } + if (i >= ARRAYSIZE(pllmapAll) || freq == 0) { + printk(KERN_INFO"gfreq : dev%d********************pll not match table**************\n", dev); + freq_llu = (SRC_FREQ*GET_DIVF(tmp)) * base; + //printk(KERN_INFO" freq_llu =%llu\n", freq_llu); + tmp = GET_DIVR(tmp) * GET_DIVQ(tmp) * div * div_ahb; + mod = do_div(freq_llu, tmp); + freq = (unsigned int)freq_llu; + } else { + freq = (freq * 15625)<<6; + freq = freq/(div*div_ahb); + } + + #ifdef DEBUG_CLK + t2 = wmt_read_oscr() - t1; + printk("************delay_time=%d\n", t2); + printk("get_freq cmd: freq=%d freq(unsigned long long)=%llu div=%llu mod=%llu\n", + freq, freq_llu, tmp, mod); + #endif + + #ifdef DEBUG_PLLA + if (dev == DEV_ARM && fr_rdn > (tblpll*1000000)) + return fr_rdn; + #endif + + return freq; +} +#if 0 +static int set_divisor(enum dev_id dev, int unit, int freq, int *divisor) { + + unsigned int div = 0, tmp; + int PLL_NO, i, j = 0, div_addr_offs, SD_MMC = 1, ret; + unsigned long long base = 1000000, PLL, PLL_tmp, mod, div_llu, freq_target = freq, mini,tmp1; + if ((dev != DEV_SDMMC0) && (dev != DEV_SDMMC1) && (dev != DEV_SDMMC2)) + SD_MMC = 0; + + PLL_NO = calc_pll_num(dev, &div_addr_offs); + if (PLL_NO == -1) { + printk(KERN_INFO"device not found"); + return -1; + } + + if (PLL_NO == -2) { + printk(KERN_INFO"device has no divisor"); + return -1; + } + + *(volatile unsigned char *)(PMC_BASE + div_addr_offs + 2) = (dev == DEV_SDTV) ? 1 : 0; + + check_PLL_DIV_busy(); + + tmp = *(volatile unsigned int *)(PMC_PLL + 4*PLL_NO); + PLL = SRC_FREQ*GET_DIVF(tmp)*base; + //printk(KERN_INFO" PLL =%llu, dev_id=%d, PLL_NO=%d, PMC_PLL=0x%x, tmp = 0x%x\n", PLL, dev, PLL_NO,PMC_PLL,(unsigned int)&tmp); + div_llu = GET_DIVR(tmp)*GET_DIVQ(tmp); + + ret = check_clk_enabled(dev, SET_DIV); + if (ret) + return -1; + + if (unit == 1) + freq_target *= 1000; + else if (unit == 2) + freq_target *= 1000000; + + if (SD_MMC == 0) { + for (i = 1; i < 33; i++) { + if ((i > 1 && (i%2)) && (PLL_NO == 2)) + continue; + PLL_tmp = PLL; + mod = do_div(PLL_tmp, div_llu*i); + if (PLL_tmp <= freq_target) { + *divisor = div = i; + break; + } + } + } else { + mini = freq_target; + tmp1 = PLL; + do_div(tmp1, div_llu); + do_div(tmp1, 64); + if ((tmp1) >= freq_target) + j = 1; + for (i = 1; i < 33; i++) { + PLL_tmp = PLL; + mod = do_div(PLL_tmp, div_llu*(i*(j?64:1)*2)); + if (PLL_tmp <= freq_target) { + *divisor = div = i; + break; + } + } + } + if (div != 0 && div < 33) { + check_PLL_DIV_busy(); + if (dev == DEV_WMTNA) + *(volatile unsigned int *)(PMC_BASE + div_addr_offs) = (0x200 | ((div == 32) ? 0 : div)); + else + *(volatile unsigned int *)(PMC_BASE + div_addr_offs) + = ((dev == DEV_SDTV)?0x10000:0) + (j?32:0) + ((div == 32) ? 0 : div); + check_PLL_DIV_busy(); + freq = (unsigned int)PLL_tmp; + /*printk("set_divisor: freq=%d freq(unsigned long long)=%llu div=%llu mod=%llu divisor%d pmc_pll=0x%x\n", + freq, PLL_tmp, div_llu, mod, *divisor, tmp);*/ + return freq; + } + printk(KERN_INFO"no suitable divisor"); + return -1; +} +#endif +static int set_divisor_t(enum dev_id dev, int unit, int freq, int *divisor) { + + unsigned int div = 0, tmp; + int /*ret,*/ PLL_NO, i, j = 1, div_addr_offs, SD_MMC = 1; + unsigned int PLL = 0, freq_target = freq; + if ((dev != DEV_SDMMC0) && (dev != DEV_SDMMC1) && (dev != DEV_SDMMC2)) + SD_MMC = 0; + + PLL_NO = calc_pll_num(dev, &div_addr_offs); + if (PLL_NO == -1) { + printk(KERN_INFO"device not found"); + return -1; + } else if (PLL_NO == -2) { + printk(KERN_INFO"device has no divisor"); + return -1; + } + + *(volatile unsigned char *)(PMC_BASE + div_addr_offs + 2) = (dev == DEV_SDTV) ? 1 : 0; + + check_PLL_DIV_busy(); + tmp = *(volatile unsigned int *)(PMC_PLL + 4*PLL_NO); + + /*ret = check_clk_enabled(dev, SET_DIV); + if (ret) + return -1;*/ + + if (unit == 1) + freq_target *= 1000; + else if (unit == 2) + freq_target *= 1000000; + + for (i = 0; i < ARRAYSIZE(pllmapAll); i++) { + if (pllmapAll[i].pll == tmp) { + PLL = pllmapAll[i].freq; + break; + } + } + if (i >= ARRAYSIZE(pllmapAll) || PLL == 0) { + printk(KERN_INFO"set div : dev%d********************pll not match table**************\n", dev); + PLL = (SRC_FREQ/GET_DIVQ(tmp)) * (GET_DIVF(tmp)/GET_DIVR(tmp)); + } + PLL = (PLL * 15625)<<6; + + if (SD_MMC == 0) { + for (i = 1; i < 64; i++) { + if ((i > 1 && (i%2)) && (PLL_NO == 2)) + continue; + freq = PLL/i; + if (freq <= freq_target) { + *divisor = div = i; + break; + } + } + } else { + if ((PLL>>6) >= freq_target) + j = 1<<6; + for (i = 1; i < 64; i++) { + freq = PLL/(i*j); + if (freq <= freq_target) { + *divisor = div = i; + break; + } + } + } + if (div > 32) + div = 32; + + if (div != 0 && div < 33) { + //mutex_lock(&clk_cnt_lock); + check_PLL_DIV_busy(); + if (dev == DEV_WMTNA) + *(volatile unsigned int *)(PMC_BASE + div_addr_offs) = (0x200 | ((div == 32) ? 0 : div)); + else + *(volatile unsigned int *)(PMC_BASE + div_addr_offs) + = ((dev == DEV_SDTV)?0x10000:0) + (j==64?32:0) + ((div == 32) ? 0 : div); + #ifdef DEBUG_CLK + printk("SETDIV********dev%d PLL_%X PLL=0x%x div%d cal_freq=%d target_freq%d\n", + dev, PLL_NO+10, PLL, div, freq, freq_target); + #endif + check_PLL_DIV_busy(); + //mutex_unlock(&clk_cnt_lock); + return freq; + } + printk(KERN_INFO"no suitable divisor\n"); + return -1; +} +#if 0 +static int set_pll_speed(enum dev_id dev, int unit, int freq, int *divisor, int wait) { + + unsigned int PLL, DIVF=1, DIVR=1, DIVQ=1; + unsigned int last_freq, div=1; + unsigned long long minor, min_minor = 0xFF000000; + int PLL_NO, div_addr_offs, DF, DR, VD, DQ; + unsigned long long base = 1000000, base_f = 1, PLL_llu, div_llu, freq_llu, mod; + PLL_NO = calc_pll_num(dev, &div_addr_offs); + if (PLL_NO == -1) { + printk(KERN_INFO"device not belong to PLL A B C D E"); + return -1; + } + + if (PLL_NO == -2) { + printk(KERN_INFO"device has no divisor"); + return -1; + } + + *(volatile unsigned char *)(PMC_BASE + div_addr_offs + 2) = (dev == DEV_SDTV) ? 1 : 0; + + check_PLL_DIV_busy(); + VD = *divisor = *(volatile unsigned char *)(PMC_BASE + div_addr_offs); + + if (unit == 1) + base_f = 1000; + else if (unit == 2) + base_f = 1000000; + else if (unit != 0) { + printk(KERN_INFO"unit is out of range"); + return -1; + } + freq_llu = freq * base_f; + for (DR = MAX_DR; DR >= 0; DR--) { + for (DQ = MAX_DQ; DQ >= 0; DQ--) { + for (DF = 0; DF <= MAX_DF; DF++) { + if (SRC_FREQ/(DR+1) < 10) + break; + if ((1000/SRC_FREQ) > ((2*(DF+1))/(DR+1))) + continue; + if ((2000/SRC_FREQ) < ((2*(DF+1))/(DR+1))) + break; + PLL_llu = (unsigned long long)(SRC_FREQ * 2 * (DF+1) * base); + div_llu = (unsigned long long)(VD * (DR+1)*(1<<DQ)); + mod = do_div(PLL_llu, div_llu); + if (PLL_llu == freq_llu) { + DIVF = DF; + DIVR = DR; + DIVQ = DQ; + div = VD; + /*printk(KERN_INFO"find the equal value");*/ + goto find; + } else if (PLL_llu < freq_llu) { + minor = freq_llu - PLL_llu; + //printk(KERN_INFO"minor=0x%x, min_minor=0x%x", minor, min_minor); + if (minor < min_minor) { + DIVF = DF; + DIVR = DR; + DIVQ = DQ; + div = VD; + min_minor = minor; + } + } else if (PLL_llu > freq_llu) { + if (PLL_NO == 2) { + minor = PLL_llu - freq_llu; + if (minor < min_minor) { + DIVF = DF; + DIVR = DR; + DIVQ = DQ; + div = VD; + min_minor = minor; + } + } + break; + } + }//DF + }//DQ + }//DR +/*minimun:*/ + //printk(KERN_INFO"minimum minor=0x%x, unit=%d \n", (unsigned int)min_minor, unit); +find: + + PLL_llu = (unsigned long long)(SRC_FREQ * 2 * (DIVF+1) * base); + div_llu = (unsigned long long)((DIVR+1)*(1<<DIVQ)*(*divisor)); + mod = do_div(PLL_llu, div_llu); + last_freq = (unsigned int)PLL_llu; + /*printk("set_pll cmd: freq=%d freq(unsigned long long)=%llu div=%llu mod=%llu\n", last_freq, PLL_llu, div_llu, mod); + printk(KERN_INFO"DIVF%d, DIVR%d, DIVQ%d, divisor%d freq=%dHz \n", + DIVF, DIVR, DIVQ, *divisor, last_freq);*/ + PLL = (DIVF<<16) + (DIVR<<8) + DIVQ; + + /* if the clk of the device is not enable, then enable it */ + /*if (dev < 128) { + pmc_clk_en = *(volatile unsigned int *)(PMC_CLK + 4*(dev/32)); + if (!(pmc_clk_en & (1 << (dev - 32*(dev/32))))) + enable_dev_clk(dev); + }*/ + + check_PLL_DIV_busy(); + /*printk(KERN_INFO"PLL0x%x, pll addr =0x%x\n", PLL, PMC_PLL + 4*PLL_NO);*/ + *(volatile unsigned int *)(PMC_PLL + 4*PLL_NO) = PLL; + if (wait) + check_PLL_DIV_busy(); + return last_freq; + + printk(KERN_INFO"no suitable pll"); + return -1; +} +#endif +static int set_pll_speed_t(enum dev_id dev, int unit, int freq, int *divisor, int wait) { + + unsigned int PLL, VD; + unsigned int minor, min_minor = 0x7F000000, set_freq = 0; + int PLL_NO, div_addr_offs, i; + unsigned int tmp_freq, tmp_PLL = 0; + PLL_NO = calc_pll_num(dev, &div_addr_offs); + if (PLL_NO == -1) { + printk(KERN_INFO"device not belong to PLL A B C D E"); + return -1; + } + + if (PLL_NO == -2) { + printk(KERN_INFO"device has no divisor"); + return -1; + } + + *(volatile unsigned char *)(PMC_BASE + div_addr_offs + 2) = (dev == DEV_SDTV) ? 1 : 0; + + check_PLL_DIV_busy(); + VD = *divisor = *(volatile unsigned char *)(PMC_BASE + div_addr_offs); + + if (unit == 1) + freq *= 1000; + else if (unit == 2) + freq *= 1000000; + else if (unit != 0) { + printk(KERN_INFO"unit is out of range"); + return -1; + } + + for (i = 0; i < ARRAYSIZE(pllmap); i++) { + if (pllmap[i].freq > 1150 && PLL_NO == 4) + continue; + if (pllmap[i].freq > 1250 && ((PLL_NO == 2) || (PLL_NO == 6))) + continue; + tmp_freq = ((pllmap[i].freq*15625)<<6) / VD; + if (tmp_freq == freq) { + tmp_PLL = pllmap[i].pll; + set_freq = tmp_freq; + min_minor = 0; + goto find; + } else if (tmp_freq < freq) { + minor = freq - tmp_freq; + if (minor < min_minor) { + tmp_PLL = pllmap[i].pll; + set_freq = tmp_freq; + min_minor = minor; + } + } else if (tmp_freq > freq) { + if (PLL_NO == 2) { + minor = tmp_freq - freq; + if (minor < min_minor) { + tmp_PLL = pllmap[i].pll; + set_freq = tmp_freq; + min_minor = minor; + } + } + break; + } + } +find: + #ifdef DEBUG_CLK + if (min_minor) + printk(KERN_INFO"minimum minor=0x%x, unit=%d \n", (unsigned int)min_minor, unit); + printk("SETPLL********dev%d PLL_%X PLL=0x%x div%d cal_freq=%d target_freq%d\n", + dev, PLL_NO+10, tmp_PLL, div, set_freq, freq); + #endif + PLL = tmp_PLL; + + //mutex_lock(&clk_cnt_lock); + check_PLL_DIV_busy(); + *(volatile unsigned int *)(PMC_PLL + 4*PLL_NO) = PLL; + if (wait) + check_PLL_DIV_busy(); + //mutex_unlock(&clk_cnt_lock); + + return set_freq; +} +#if 0 +static int set_pll_divisor(enum dev_id dev, int unit, int freq, int *divisor) { + + unsigned int PLL, DIVF=1, DIVR=1, DIVQ=1, pmc_clk_en, old_divisor; + unsigned int last_freq, div=1; + unsigned long long minor, min_minor = 0xFF000000; + int PLL_NO, div_addr_offs, DF, DR, VD, DQ; + unsigned long long base = 1000000, base_f = 1, PLL_llu, div_llu, freq_llu, mod; + PLL_NO = calc_pll_num(dev, &div_addr_offs); + if (PLL_NO == -1) { + printk(KERN_INFO"device not belong to PLL A B C D E"); + return -1; + } + + if (PLL_NO == -2) { + printk(KERN_INFO"device has no divisor"); + return -1; + } + + *(volatile unsigned char *)(PMC_BASE + div_addr_offs + 2) = (dev == DEV_SDTV) ? 1 : 0; + + check_PLL_DIV_busy(); + + old_divisor = *(volatile unsigned char *)(PMC_BASE + div_addr_offs); + + if (unit == 1) + base_f = 1000; + else if (unit == 2) + base_f = 1000000; + else if (unit != 0) { + printk(KERN_INFO"unit is out of range"); + return -1; + } +//printk(KERN_INFO"target freq=%d unit=%d PLL_NO=%d\n", freq, unit, PLL_NO); + freq_llu = freq * base_f; + //tmp = div * freq; + for (DR = MAX_DR; DR >= 0; DR--) { + for (DQ = MAX_DQ; DQ >= 0; DQ--) { + for (VD = 32; VD >= 1; VD--) { + if ((VD > 1 && (VD%2)) && ((PLL_NO == 2) || (PLL_NO == 6))) + continue; + for (DF = 0; DF <= MAX_DF; DF++) { + if (SRC_FREQ/(DR+1) < 10) + break; + if ((1000/SRC_FREQ) > ((2*(DF+1))/(DR+1))) + continue; + if ((2000/SRC_FREQ) < ((2*(DF+1))/(DR+1))) + break; + PLL_llu = (unsigned long long)(SRC_FREQ * 2 * (DF+1) * base); + div_llu = (unsigned long long)(VD * (DR+1)*(1<<DQ)); + mod = do_div(PLL_llu, div_llu); + if (PLL_llu == freq_llu) { + DIVF = DF; + DIVR = DR; + DIVQ = DQ; + div = VD; + /*printk(KERN_INFO"find the equal value");*/ + goto find; + } else if ((PLL_llu < freq_llu) /*&& (dev != DEV_I2S)*/) { + minor = freq_llu - PLL_llu; + //printk(KERN_INFO"minor=0x%x, min_minor=0x%x", minor, min_minor); + if (minor < min_minor) { + DIVF = DF; + DIVR = DR; + DIVQ = DQ; + div = VD; + min_minor = minor; + /*printk(KERN_INFO"< target :DIVF%d, DIVR%d, DIVQ%d, divisor%d min_minor=%lluHz \n", + DIVF, DIVR, DIVQ, div, min_minor);*/ + } + } else if (PLL_llu > freq_llu) { + if ((PLL_NO == 2) || (PLL_NO == 6)) { + minor = PLL_llu - freq_llu; + if (minor < min_minor) { + DIVF = DF; + DIVR = DR; + DIVQ = DQ; + div = VD; + min_minor = minor; + /*printk(KERN_INFO" > target DIVF%d, DIVR%d, DIVQ%d, divisor%d min_minor=%lluHz \n", + DIVF, DIVR, DIVQ, div, min_minor);*/ + } + } + break; + } + }//DF + }//VD + }//DQ + }//DR +/*minimun:*/ + //printk(KERN_INFO"minimum minor=%llu, unit=%d \n", min_minor, unit); +find: + + *divisor = div; + + PLL_llu = (unsigned long long)(SRC_FREQ * 2 * (DIVF+1) * base); + div_llu = (unsigned long long)((DIVR+1)*(1<<DIVQ)*(*divisor)); + mod = do_div(PLL_llu, div_llu); + last_freq = (unsigned int)PLL_llu; + /*printk("set_pll_divisor cmd: freq=%d freq(unsigned long long)=%llu div=%llu mod=%llu\n", last_freq, PLL_llu, div_llu, mod); + printk(KERN_INFO"DIVF%d, DIVR%d, DIVQ%d, divisor%d freq=%dHz \n", + DIVF, DIVR, DIVQ, *divisor, last_freq);*/ + PLL = (DIVF<<16) + (DIVR<<8) + DIVQ; + + /* if the clk of the device is not enable, then enable it */ + if (dev < 128) { + pmc_clk_en = *(volatile unsigned int *)(PMC_CLK + 4*(dev/32)); + if (!(pmc_clk_en & (1 << (dev - 32*(dev/32)))) && dev != DEV_ARM) { + /*enable_dev_clk(dev);*/ + printk(KERN_INFO"device clock is disabled"); + return -1; + } + } + + check_PLL_DIV_busy(); + if (old_divisor < *divisor) { + if (dev == DEV_WMTNA) + *(volatile unsigned int *)(PMC_BASE + div_addr_offs) = (0x200 | ((div == 32) ? 0 : div)); + else + *(volatile unsigned int *)(PMC_BASE + div_addr_offs) + = ((dev == DEV_SDTV)?0x10000:0) +/*(j?32:0) + */((div == 32) ? 0 : div)/* + (div&1) ? (1<<8): 0*/; + check_PLL_DIV_busy(); + } + //printk(KERN_INFO"PLL0x%x, pll addr =0x%x\n", PLL, PMC_PLL + 4*PLL_NO); + *(volatile unsigned int *)(PMC_PLL + 4*PLL_NO) = PLL; + check_PLL_DIV_busy(); + /*printk(KERN_INFO"DIVF%d, DIVR%d, DIVQ%d, div%d div_addr_offs=0x%x\n", + DIVF, DIVR, DIVQ, div, div_addr_offs);*/ + + if (old_divisor > *divisor) { + if (dev == DEV_WMTNA) + *(volatile unsigned int *)(PMC_BASE + div_addr_offs) = (0x200 | ((div == 32) ? 0 : div)); + else + *(volatile unsigned int *)(PMC_BASE + div_addr_offs) + = ((dev == DEV_SDTV)?0x10000:0) +/*(j?32:0) + */((div == 32) ? 0 : div)/* + (div&1) ? (1<<8): 0*/; + check_PLL_DIV_busy(); + } + + + /*ddv = *(volatile unsigned int *)(PMC_BASE + div_addr_offs); + check_PLL_DIV_busy(); + pllx = *(volatile unsigned int *)(PMC_PLL + 4*PLL_NO); + printk(KERN_INFO"read divisor=%d, pll=0x%x from register\n", 0x1F&ddv, pllx);*/ + return last_freq; + + /*printk(KERN_INFO"no suitable divisor"); + return -1;*/ +} +#endif + +static int set_pll_divisor_t(enum dev_id dev, int unit, int freq, int *divisor) { + + unsigned int PLL, old_divisor, div=1, set_freq = 0; + unsigned int minor, min_minor = 0x7F000000, tmp_freq = 0, tmp_PLL = 0, tfreq = (unsigned int)freq; + int i, PLL_NO, div_addr_offs, VD/*, ret*/; + + PLL_NO = calc_pll_num(dev, &div_addr_offs); + if (PLL_NO == -1) { + printk(KERN_INFO"device not belong to PLL A B C D E"); + return -1; + } else if (PLL_NO == -2) { + printk(KERN_INFO"device has no divisor"); + return -1; + } + + *(volatile unsigned char *)(PMC_BASE + div_addr_offs + 2) = (dev == DEV_SDTV) ? 1 : 0; + + check_PLL_DIV_busy(); + old_divisor = *(volatile unsigned char *)(PMC_BASE + div_addr_offs); + + if (unit == 1) + tfreq *= 1000; + else if (unit == 2) + tfreq *= 1000000; + else if (unit != 0) { + printk(KERN_INFO"unit is out of range"); + return -1; + } + + for (i = 0; i < ARRAYSIZE(pllmap); i++) { + if (pllmap[i].freq > 1150 && PLL_NO == 4) + continue; + if (pllmap[i].freq > 1250 && ((PLL_NO == 2) || (PLL_NO == 6))) + continue; + for (VD = 32; VD >= 1; VD--) { + if ((VD > 1 && (VD == 3)) && ((PLL_NO == 2) || (PLL_NO == 6))) + continue; + tmp_freq = ((pllmap[i].freq*15625)<<6) / VD; + if (tmp_freq == tfreq) { + tmp_PLL = pllmap[i].pll; + set_freq = tmp_freq; + div = VD; + min_minor = 0; + goto find; + } else if (tmp_freq < tfreq) { + minor = tfreq - tmp_freq; + if (minor < min_minor) { + tmp_PLL = pllmap[i].pll; + set_freq = tmp_freq; + div = VD; + min_minor = minor; + } + } else if (tmp_freq > tfreq) { + if ((PLL_NO == 2) || (PLL_NO == 6)) { + minor = tmp_freq - tfreq; + if (minor < min_minor) { + tmp_PLL = pllmap[i].pll; + set_freq = tmp_freq; + div = VD; + min_minor = minor; + } + } + break; + } + } + } +find: + #ifdef DEBUG_CLK + if (min_minor) + printk(KERN_INFO"minimum minor=0x%x, unit=%d \n", (unsigned int)min_minor, unit); + printk("SETPLLDIV********dev%d PLL_%X PLL=0x%x div%d cal_freq=%d target_freq%d\n", + dev, PLL_NO+10, tmp_PLL, div, set_freq, tfreq); + #endif + + *divisor = div; + PLL = tmp_PLL; + + /*ret = check_clk_enabled(dev, SET_PLLDIV); + if (ret) + return -1;*/ + + //mutex_lock(&clk_cnt_lock); + wmt_clk_mutex_lock(1); + check_PLL_DIV_busy(); + if (old_divisor < *divisor) { + if (dev == DEV_WMTNA) + *(volatile unsigned int *)(PMC_BASE + div_addr_offs) = (0x200 | ((div == 32) ? 0 : div)); + else + *(volatile unsigned int *)(PMC_BASE + div_addr_offs) + = ((dev == DEV_SDTV)?0x10000:0) +/*(j?32:0) + */((div == 32) ? 0 : div)/* + (div&1) ? (1<<8): 0*/; + check_PLL_DIV_busy(); + } + *(volatile unsigned int *)(PMC_PLL + 4*PLL_NO) = PLL; + check_PLL_DIV_busy(); + + if (old_divisor > *divisor) { + if (dev == DEV_WMTNA) + *(volatile unsigned int *)(PMC_BASE + div_addr_offs) = (0x200 | ((div == 32) ? 0 : div)); + else + *(volatile unsigned int *)(PMC_BASE + div_addr_offs) + = ((dev == DEV_SDTV)?0x10000:0) +/*(j?32:0) + */((div == 32) ? 0 : div)/* + (div&1) ? (1<<8): 0*/; + check_PLL_DIV_busy(); + } + wmt_clk_mutex_lock(0); + //mutex_unlock(&clk_cnt_lock); + + return set_freq; +} + +static int get_freq_timer(enum dev_id dev, int *divisor) { + + unsigned int div = 0, freq = 0, div_ahb = 1; + int PLL_NO, i, j = 0, div_addr_offs; + unsigned long long freq_llu = 0, base = 1000000, tmp, mod; + #ifdef DEBUG_CLK + unsigned int t1 = 0, t2 = 0; + #endif + + if (dev == DEV_APB) { + PLL_NO = calc_pll_num(DEV_AHB, &div_addr_offs); + if (PLL_NO < 0) { + printk(KERN_INFO"device not found"); + return -1; + } + div_ahb = *(volatile unsigned char *)(PMC_BASE + div_addr_offs); + } + + PLL_NO = calc_pll_num(dev, &div_addr_offs); + if (PLL_NO == -1) { + printk(KERN_INFO"device not found"); + return -1; + } + + if (PLL_NO == -2) + return div_addr_offs*1000000; + + *(volatile unsigned char *)(PMC_BASE + div_addr_offs + 2) = (dev == DEV_SDTV) ? 1 : 0; + + check_PLL_DIV_busy(); + + div = *divisor = *(volatile unsigned char *)(PMC_BASE + div_addr_offs); + + if ((dev != DEV_SDMMC0) && (dev != DEV_SDMMC1) && (dev != DEV_SDMMC2)) { + div = div&0x1F; + } else { + if (div & (1<<5)) + j = 1; + div &= 0x1F; + div = div*(j?64:1)*2; + } + + #ifdef DEBUG_CLK + printk(KERN_INFO"div_addr_offs=0x%x PLL_NO=%d PMC_PLL=0x%x\n", div_addr_offs, PLL_NO, PMC_PLL); + t1 = wmt_read_oscr(); + #endif + //mutex_lock(&clk_cnt_lock); + tmp = *(volatile unsigned int *)(PMC_PLL + 4*PLL_NO); + //mutex_unlock(&clk_cnt_lock); + for (i = 0; i < ARRAYSIZE(pllmapAll); i++) { + if (pllmapAll[i].pll == tmp) { + freq = pllmapAll[i].freq; + #ifdef DEBUG_CLK + printk("********dev%d---PLL_NO=%X---get freq=%d set0x%x\n", dev, PLL_NO+10, freq, pllmapAll[i].pll); + #endif + break; + } + } + if (i >= ARRAYSIZE(pllmapAll) || freq == 0) { + printk(KERN_INFO"gfreq : dev%d********************pll not match table**************\n", dev); + freq_llu = (SRC_FREQ*GET_DIVF(tmp)) * base; + //printk(KERN_INFO" freq_llu =%llu\n", freq_llu); + tmp = GET_DIVR(tmp) * GET_DIVQ(tmp) * div * div_ahb; + mod = do_div(freq_llu, tmp); + freq = (unsigned int)freq_llu; + } else { + freq = (freq * 15625)<<6; + freq = freq/(div*div_ahb); + } + + #ifdef DEBUG_CLK + t2 = wmt_read_oscr() - t1; + printk("************delay_time=%d\n", t2); + printk("get_freq cmd: freq=%d freq(unsigned long long)=%llu div=%llu mod=%llu\n", + freq, freq_llu, tmp, mod); + #endif + + return freq; +} + +/* +* cmd : CLK_DISABLE : disable clock, +* CLK_ENABLE : enable clock, +* GET_FREQ : get device frequency, it doesn't enable or disable clock, +* SET_DIV : set clock by setting divisor only(clock must be enabled by CLK_ENABLE command), +* SET_PLL : set clock by setting PLL only, no matter clock is enabled or not, +* this cmd can be used before CLK_ENABLE cmd to avoid a extreme speed +* to be enabled when clock enable. +* SET_PLLDIV : set clock by setting PLL and divisor(clock must be enabled by CLK_ENABLE command). +* dev : Target device ID to be set the clock. +* unit : the unit of parameter "freq", 0 = Hz, 1 = KHz, 2 = MHz. +* freq : frequency of the target to be set when cmd is "SET_XXX". +* +* return : return value is different depend on cmd type, +* CLK_DISABLE : return internal count which means how many drivers still enable this clock, +* retrun -1 if this device has no clock enable register. +* +* CLK_ENABLE : return internal count which means how many drivers enable this clock, +* retrun -1 if this device has no clock enable register. +* +* GET_FREQ : return device frequency in Hz when clock is enabled, +* return -1 when clock is disabled. +* +* SET_DIV : return the finally calculated frequency when clock is enabled, +* return -1 when clock is disabled. +* +* SET_PLL : return the finally calculated frequency no matter clock is enabled or not. +* +* SET_PLLDIV : return the finally calculated frequency when clock is enabled, +* return -1 when clock is disabled. +* Caution : +* 1. The final clock freq maybe an approximative value, +* equal to or less than the setting freq. +* 2. SET_DIV and SET_PLLDIV commands which would set divisor register must use CLK_ENABLE command +* first to enable device clock. +* 3. Due to default frequency may be extremely fast when clock is enabled. use SET_PLL command can +* set the frequency into a reasonable value, but don't need to enable clock first. +*/ +#define PMC_TABLE 1 +int auto_pll_divisor(enum dev_id dev, enum clk_cmd cmd, int unit, int freq) +{ + int last_freq, divisor, en_count; + /*unsigned int t1 = 0, t2 = 0, t3;*/ + /*if (mutexInit == 23456789) { + mutexInit = 0; + mutex_init(&clk_cnt_lock); + }*/ + + switch (cmd) { + case CLK_DISABLE: + if (dev < 128) { + //en_count = disable_dev_clk(dev); + en_count = enable_dev_clk(dev, 0); + return en_count; + } else { + printk(KERN_INFO"device has not clock enable register"); + return -1; + } + case CLK_ENABLE: + if (dev < 128) { + en_count = enable_dev_clk(dev, 1); + return en_count; + } else { + printk(KERN_INFO"device has not clock enable register"); + return -1; + } + case GET_FREQ: + #ifdef PMC_TABLE + /*t1 = wmt_read_oscr();*/ + last_freq = get_freq_t(dev, &divisor); + /*t2 = wmt_read_oscr() - t1; + t1 = wmt_read_oscr();*/ + #else + last_freq = get_freq(dev, &divisor); + #endif + /*t3 = wmt_read_oscr() - t1; + printk("*******************GF t3=%d tt2=%d min=%d\n", t3, t2, last_freq - last_freq1);*/ + return last_freq; + case SET_DIV: + divisor = 0; + #ifdef PMC_TABLE + /*t1 = wmt_read_oscr();*/ + last_freq = set_divisor_t(dev, unit, freq, &divisor); + /*t2 = wmt_read_oscr() - t1; + t1 = wmt_read_oscr();*/ + #else + last_freq = set_divisor(dev, unit, freq, &divisor); + #endif + /*t3 = wmt_read_oscr() - t1; + printk("*******************SD t3=%d tt2=%d min=%d\n", t3, t2, last_freq - last_freq1);*/ + return last_freq; + case SET_PLL: + divisor = 0; + #ifdef PMC_TABLE + /*t1 = wmt_read_oscr();*/ + last_freq = set_pll_speed_t(dev, unit, freq, &divisor, 1); + /*t2 = wmt_read_oscr() - t1; + t1 = wmt_read_oscr();*/ + #else + last_freq = set_pll_speed(dev, unit, freq, &divisor, 1); + #endif + /*t3 = wmt_read_oscr() - t1; + printk("*******************SP t3=%d tt2=%d min=%d\n", t3, t2, last_freq - last_freq1);*/ + return last_freq; + case SET_PLLDIV: + divisor = 0; + #ifdef PMC_TABLE + /*t1 = wmt_read_oscr();*/ + last_freq = set_pll_divisor_t(dev, unit, freq, &divisor); + /*t2 = wmt_read_oscr() - t1; + t1 = wmt_read_oscr();*/ + #else + last_freq = set_pll_divisor(dev, unit, freq, &divisor); + #endif + /*t3 = wmt_read_oscr() - t1; + printk("******************SPD t3=%d tt2=%d min=%d\n", t3, t2, last_freq - last_freq1);*/ + return last_freq; + case GET_CPUTIMER: + last_freq = get_freq_timer(dev, &divisor); + return last_freq; + default: + printk(KERN_INFO"clock cmd unknow"); + /*last_freq = get_freq(dev, &divisor); + last_freq = set_divisor(dev, unit, freq, &divisor); + last_freq = set_pll_speed(dev, unit, freq, &divisor, 1); + last_freq = set_pll_divisor(dev, unit, freq, &divisor);*/ + return -1; + } +} +EXPORT_SYMBOL(auto_pll_divisor); + +/** +* freq = src_freq * 2 * (DIVF+1)/((DIVR+1)*(2^DIVQ)) +* +* dev : Target device ID to be set the clock. +* DIVF : Feedback divider value. +* DIVR : Reference divider value. +* DIVQ : Output divider value. +* dev_div : Divisor belongs to each device, 0 means not changed. +* return : The final clock freq, in Hz, will be returned when success, +* -1 means fail (waiting busy timeout). +* +* caution : +* 1. src_freq/(DIVR+1) should great than or equal to 10, +*/ +int manu_pll_divisor(enum dev_id dev, int DIVF, int DIVR, int DIVQ, int dev_div) +{ + + unsigned int PLL, freq, pmc_clk_en, old_divisor, div; + int PLL_NO, div_addr_offs, j = 0, SD_MMC = 0; + + if ((dev == DEV_SDMMC0)||(dev == DEV_SDMMC1)||(dev == DEV_SDMMC2)) + SD_MMC = 1; + + if (DIVF < 0 || DIVF > MAX_DF){ + printk(KERN_INFO"DIVF is out of range 0 ~ %d", MAX_DF); + return -1; + } + if (DIVR < 0 || DIVR > MAX_DR){ + printk(KERN_INFO"DIVR is out of range 0 ~ %d", MAX_DR); + return -1; + } + if (DIVQ < 0 || DIVQ > MAX_DQ){ + printk(KERN_INFO"DIVQ is out of range 0 ~ %d", MAX_DQ); + return -1; + } + if ((1000/SRC_FREQ) > ((2*(DIVF+1))/(DIVR+1))) { + printk(KERN_INFO"((2*(DIVF+1))/(DIVR+1)) should great than (1000/SRC_FREQ)"); + return -1; + } + if ((2000/SRC_FREQ) < ((2*(DIVF+1))/(DIVR+1))) { + printk(KERN_INFO"((2*(DIVF+1))/(DIVR+1)) should less than (2000/SRC_FREQ)"); + return -1; + } + + if (dev_div > ((SD_MMC == 1)?63:31)){ + printk(KERN_INFO"divisor is out of range 0 ~ 31"); + return -1; + } + + PLL_NO = calc_pll_num(dev, &div_addr_offs); + if (PLL_NO == -1) { + printk(KERN_INFO"device not found"); + return -1; + } + old_divisor = *(volatile unsigned char *)(PMC_BASE + div_addr_offs); + + if (SD_MMC == 1 && (dev_div&32)) + j = 1; /* sdmmc has a another divider = 64 */ + div = dev_div&0x1F; + freq = (1000 * SRC_FREQ * 2 * (DIVF+1))/((DIVR+1)*(1<<DIVQ)*div*(j?128:1)); + freq *= 1000; + //printk(KERN_INFO"DIVF%d, DIVR%d, DIVQ%d, dev_div%d, freq=%dkHz\n", DIVF, DIVR, DIVQ, dev_div, freq); + + PLL = (DIVF<<16) + (DIVR<<8) + DIVQ; + + /* if the clk of the device is not enable, then enable it */ + if (dev < 128) { + pmc_clk_en = *(volatile unsigned int *)(PMC_CLK + 4*(dev/32)); + if (!(pmc_clk_en & (1 << (dev - 32*(dev/32))))) + enable_dev_clk(dev, 1); + } + + check_PLL_DIV_busy(); + if (old_divisor < dev_div) { + *(volatile unsigned int *)(PMC_BASE + div_addr_offs) + = (j?32:0) + ((div == 32) ? 0 : div)/* + (div&1) ? (1<<8): 0*/; + check_PLL_DIV_busy(); + } + *(volatile unsigned int *)(PMC_PLL + 4*PLL_NO) = PLL; + check_PLL_DIV_busy(); + if (old_divisor > dev_div) { + *(volatile unsigned int *)(PMC_BASE + div_addr_offs) + = (j?32:0) + ((div == 32) ? 0 : div)/* + (div&1) ? (1<<8): 0*/; + check_PLL_DIV_busy(); + } + + + //PLL = (j?32:0) + ((div == 32) ? 0 : div) /*+ (div&1) ? (1<<8): 0*/; + //printk(KERN_INFO"set divisor =0x%x, divider address=0x%x\n", PLL, (PMC_BASE + div_addr_offs)); + return freq; +} +EXPORT_SYMBOL(manu_pll_divisor); + + +unsigned int ftb[20] = {0xffffffff}; +int set_plla_divisor(struct plla_param *plla_env) +{ + int divisor = 0, ret = 0, i; + unsigned int plla_clk, arm_div, l2c_div, l2c_tag, l2c_data, axi_div; + unsigned int cpu_clk, index, /*old_arm_div,*/ nand_clk, tfreq, tb_index; + unsigned int drv_en, tbl_num, info, tmp; + int varlen = 512; + unsigned char buf[512] = {0}; + char *ptr = NULL; + + plla_clk = plla_env->plla_clk; + arm_div = plla_env->arm_div; + l2c_div = plla_env->l2c_div; + l2c_tag = plla_env->l2c_tag_div; + l2c_data = plla_env->l2c_data_div; + axi_div = plla_env->axi_div; + tb_index = plla_env->tb_index; + + cpu_clk = get_freq_t(DEV_ARM, &divisor); + cpu_clk >>= 20; + //cpu_clk /= 1000000; + tfreq = plla_clk/arm_div; + + info = *(volatile unsigned int *)(CPINFO); + if (ftb[0] == 0xffffffff) { + if ((info&0x8) == 0) + ret = wmt_getsyspara("wmt.cfadj.param", buf, &varlen); + else + ret = wmt_getsyspara("wmt.cflpadj.param", buf, &varlen); + if (ret) { + printk(KERN_INFO "Can not find uboot env wmt.cfadj.param\n"); + ret = -ENODATA; + ftb[0] = 0; + goto no_adj; + } + sscanf(buf, "%x:%d:", &drv_en, &tbl_num); + if ((drv_en&1) == 0 || tbl_num == 0) { + ret = -ENODATA; + ftb[0] = 0; + goto no_adj; + } + + ptr = buf; + if (tbl_num > 20) + tbl_num = 20; + fq_dbg("dvfs_adj_table:"); + for (i = 0; i < tbl_num; i++) { + strsep(&ptr, "["); + sscanf(ptr, "%d][", &tmp); + ftb[i] = tmp; + fq_dbg("ftb[%d]=%d \n", i, ftb[i]); + } + fq_dbg("\n"); + } + #ifdef DEBUG_PLLA + if (ftb[0] != 0) + tblpll = ftb[tb_index]; + else + ret = 1; + //printk("tblpll = %d\n", tblpll); + #endif +no_adj: + //printk("ret = %d\n", ret); + if (!ret) { + if (tfreq != tblpll) { + fr_rdn = (tfreq*15625)<<6; + tfreq = tblpll; + plla_clk = tfreq; + arm_div = 1; + } else + fr_rdn = 0; + } else { + #ifdef DEBUG_PLLA_FX + if (tfreq > (MAXPLL/1000000)) { + fr_rdn = tfreq*1000000; + tfreq = (MAXPLL/1000000); + plla_clk = tfreq; + arm_div = 1; + } else + #endif + fr_rdn = 0; + } + + //printk("tfreq = %d\n", tfreq); + #if 0 + if (cpu_clk > tfreq) { + //change cpu from faster to slower + old_arm_div = *(volatile unsigned int *)(PMC_BASE + 0x300); + index = set_pll_speed_t(DEV_ARM, 1, (plla_clk*1000)/old_arm_div, &divisor, 1); + if (index < 0) + return -1; + + if (*(volatile unsigned char *)(PMC_BASE + 0x300) != arm_div) { + *(volatile unsigned int *)(PMC_BASE + 0x300) = arm_div; + check_PLL_DIV_busy(); + } + if (*(volatile unsigned int *)(PMC_BASE + 0x30C) != l2c_div) { + *(volatile unsigned int *)(PMC_BASE + 0x30C) = l2c_div; + check_PLL_DIV_busy(); + } + if (*(volatile unsigned int *)(PMC_BASE + 0x3F0) != l2c_tag) { + *(volatile unsigned int *)(PMC_BASE + 0x3F0) = l2c_tag; + check_PLL_DIV_busy(); + } + if (*(volatile unsigned int *)(PMC_BASE + 0x3F4) != l2c_data) { + *(volatile unsigned int *)(PMC_BASE + 0x3F4) = l2c_data; + check_PLL_DIV_busy(); + } + if (*(volatile unsigned int *)(PMC_BASE + 0x3B0) != axi_div) { + *(volatile unsigned int *)(PMC_BASE + 0x3B0) = axi_div; + check_PLL_DIV_busy(); + } + } else + #endif + { + //change cpu from slower to faster + nand_clk = get_freq_t(DEV_NAND, &divisor); + //if ((nand_clk*divisor) > plla_clk) + *(volatile unsigned int *)(PMC_BASE + 0x300) = 2; + /*else + *(volatile unsigned int *)(PMC_BASE + 0x300) = arm_div;*/ + check_PLL_DIV_busy(); + if (*(volatile unsigned int *)(PMC_BASE + 0x30C) != l2c_div) { + *(volatile unsigned int *)(PMC_BASE + 0x30C) = l2c_div; + check_PLL_DIV_busy(); + } + if (*(volatile unsigned int *)(PMC_BASE + 0x3F0) != l2c_tag) { + *(volatile unsigned int *)(PMC_BASE + 0x3F0) = l2c_tag; + check_PLL_DIV_busy(); + } + if (*(volatile unsigned int *)(PMC_BASE + 0x3F4) != l2c_data) { + *(volatile unsigned int *)(PMC_BASE + 0x3F4) = l2c_data; + check_PLL_DIV_busy(); + } + if (*(volatile unsigned int *)(PMC_BASE + 0x3B0) != axi_div) { + *(volatile unsigned int *)(PMC_BASE + 0x3B0) = axi_div; + check_PLL_DIV_busy(); + } + //if ((nand_clk*divisor) > plla_clk) { + index = set_pll_speed_t(DEV_ARM, 1, (plla_clk*1000)/2, &divisor, 1); + if (index < 0) + return -1; + index = index*2; + *(volatile unsigned int *)(PMC_BASE + 0x300) = arm_div; + /*} else { + index = set_pll_speed_t(DEV_ARM, 1, (plla_clk*1000)/arm_div, &divisor, 0); + if (index < 0) + return -1; + }*/ + } + + return index; +} +EXPORT_SYMBOL(set_plla_divisor); + +int wm_pmc_set(enum dev_id dev, enum power_cmd cmd) +{ + int retval = -1; + unsigned int temp = 0, base = 0, mali_val = 0; + unsigned int mali_off, mali_l2c_off = 0x00130620/*, mali_gp_off = 0x00130624*/; + mali_off = mali_l2c_off; + + base = 0xfe000000; + temp = REG32_VAL(base + 0x00110110); + temp = ((temp >> 10)&0xC0)|(temp&0x3F); + + if ((cmd == DEV_PWRON)) { + if ((temp & 0xC0) == 0x80) {//8700/8720 can't be power on + retval = -1; + } else { + while ((REG32_VAL(base + mali_off)&0xF0)!= 0 + && (REG32_VAL(base + mali_off)&0xF0)!= 0xF0) + ; + + mali_val = REG32_VAL(base + mali_off); + if (!((mali_val&0xF0)==0xF0)) + REG32_VAL(base + mali_off) |= 1; + + while ((REG32_VAL(base + mali_off)&0xF0)!= 0 + && (REG32_VAL(base + mali_off)&0xF0)!= 0xF0) + ; + retval = 0; + } + } else if (cmd == DEV_PWROFF) { + if ((temp & 0xC3) == 0xC0) {//8710B0 can't power off after power on + retval = -1; + } else { + while ((REG32_VAL(base + mali_off)&0xF0)!= 0 + && (REG32_VAL(base + mali_off)&0xF0)!= 0xF0) + ; + mali_val = REG32_VAL(base + mali_off); + if ((mali_val&0xF0)) + REG32_VAL(base + mali_off) &= ~1; + while ((REG32_VAL(base + mali_off)&0xF0)!= 0 + && (REG32_VAL(base + mali_off)&0xF0)!= 0xF0) + ; + retval = 0; + } + } else if (cmd == DEV_PWRSTS) { + while ((REG32_VAL(base + mali_off)&0xF0)!= 0 + && (REG32_VAL(base + mali_off)&0xF0)!= 0xF0) + ; + temp = REG32_VAL(base + mali_off); + if (temp & 0xF0) + retval = 1; + else + retval = 0; + } + return retval; +} + + + +#define LOADER_ADDR 0xffff0000 +#define HIBERNATION_ENTER_EXIT_CODE_BASE_ADDR 0xFFFFFFC0 +#define DO_POWER_SET (HIBERNATION_ENTER_EXIT_CODE_BASE_ADDR + 0x2C) +#define CP15_C1_XPbit 23 +/* + * Function: + * int wmt_power_dev(enum dev_id dev, enum power_cmd cmd) + * This function is used to control/get WMT SoC specific device power state. + * + * Parameter: + * The available dev_id is DEV_MALI cmd used to control/get device power state. + * The available power_cmd are + * - DEV_PWRON + * - DEV_PWROFF + * - DEV_PWRSTS + * + * Return: + * - As power_cmd are DEV_PWRON, DEV_PWROFF 0 indicates success. + * Negative value indicates failure as error code. + * + * - As power_cmd is DEV_PWRSTS + * 0 indicates the current device is power off. + * 1 indicates the current device is power on. + * Negative value indicates failure as error code. + */ +extern int spi_read_status(int chip); +int wmt_power_dev(enum dev_id dev, enum power_cmd cmd) +{ +#if 0 + static unsigned int base = 0; + unsigned int exec_at = (unsigned int)-1, temp = 0, bootdev; + int (*theKernel_power)(int from, enum dev_id dev, enum power_cmd cmd); + int en_count, retval = 0, rc = 0; + unsigned long flags; + bootdev = GPIO_STRAP_STATUS_VAL; + + /*printk(KERN_INFO"entry dev_id=%d cmd=%d, boot_strap=%x,\n",dev, cmd, GPIO_STRAP_STATUS_VAL);*/ + /*enble SF clock*/ + if((bootdev & 0x4040) == 0) + en_count = enable_dev_clk(DEV_SF); //SF boot + else if((bootdev & 0x4040) == 4) + en_count = enable_dev_clk(DEV_NAND);//NAND boot + else + en_count = enable_dev_clk(DEV_SDMMC0);//MMC boot + udelay(1); +#ifdef CONFIG_MTD_WMT_SF + rc = spi_read_status(0); + if (rc) + printk("wr c0 wait status ret=%d\n", rc); +#endif + /*rc = spi_read_status(1); + if (rc) + printk("wr c1 wait status ret=%d\n", rc);*/ + + rc = 0; + /*jump to loader api to do something*/ + if (base == 0) + base = (unsigned int)ioremap/*_nocache*/(LOADER_ADDR, 0x10000); + exec_at = base + (DO_POWER_SET - LOADER_ADDR); + theKernel_power = (int (*)(int from, enum dev_id dev, enum power_cmd cmd))exec_at; + /*temp = *(volatile unsigned int *)(0xFE110110); + printk(KERN_INFO"entry exec_at=0x%x chip_id=0x%x, clock enable=0x%x\n", exec_at, + ((temp >> 10)&0xC0)|(temp&0x3F), *(volatile unsigned int *)(0xFE130250));*/ + /*backup flags and disable irq*/ + local_irq_save(flags); + /*enable subpage AP bits*/ + asm volatile ("mrc p15, 0, %0, c1, c0, 0" : "=r" (temp)); + //if (!(temp & (1<<CP15_C1_XPbit))) +// printk(KERN_INFO"1 xp(23) bits =0x%x cp15c0=0x%x\n", temp&(1<<CP15_C1_XPbit), temp); + if (temp & (1<<CP15_C1_XPbit)) { + rc = 1; + temp &= ~(1<<CP15_C1_XPbit); + /*printk(KERN_INFO"2 xp(23) bits =0x%x \n", temp&(1<<CP15_C1_XPbit));*/ + asm volatile ("mcr p15, 0, %0, c1, c0, 0" : : "r" (temp)); + } + //retval = theKernel_power(4, DEV_MALI, cmd); + retval = wm_pmc_set(DEV_MALI, cmd); + + if (rc == 1) { + /*disable subpage AP bits*/ + asm volatile ("mrc p15, 0, %0, c1, c0, 0" : "=r" (temp)); + /*printk(KERN_INFO"3 xp(23) bits =0x%x \n", temp&(1<<CP15_C1_XPbit));*/ + temp |= (1<<CP15_C1_XPbit); + /*printk(KERN_INFO"4 xp(23) bits =0x%x \n", temp&(1<<CP15_C1_XPbit));*/ + asm volatile ("mcr p15, 0, %0, c1, c0, 0" : : "r" (temp)); + } + /*restore irq flags*/ + local_irq_restore(flags); + + /*iounmap((void *)base);*/ + /*printk(KERN_INFO"entry base=0x%x exec_at=0x%x clock enable =0x%x\n", + base, exec_at, *(volatile unsigned int *)(0xFE130250));*/ + + /*disable SF clock*/ + if((bootdev & 0x4040) == 0) + en_count = disable_dev_clk(DEV_SF); //SF boot + else if((bootdev & 0x4040) == 4) + en_count = disable_dev_clk(DEV_NAND);//NAND boot + else + en_count = disable_dev_clk(DEV_SDMMC0);//MMC boot + + /*printk(KERN_INFO"exit!!ret = (%d)\n",retval);*/ +#endif + return 0;//retval; +} +EXPORT_SYMBOL(wmt_power_dev); + +/*struct pll_map pllmapAll[] = { +{126, 0x00140003}, {126, 0x00290103}, {126, 0x00140102}, +{129, 0x002A0103}, +{132, 0x00150003}, {132, 0x002B0103}, {132, 0x00150102}, {132, 0x000A0101}, {132, 0x000A0002}, +{135, 0x002C0103}, +{138, 0x00160003}, {138, 0x002D0103}, {138, 0x00160102}, +{141, 0x002E0103}, +{144, 0x00170003}, {144, 0x002F0103}, {144, 0x00020000}, {144, 0x00170102}, {144, 0x000B0101}, {144, 0x00050100}, {144, 0x000B0002}, {144, 0x00050001}, +{147, 0x00300103}, +{150, 0x00180003}, {150, 0x00310103}, {150, 0x00180102}, +{153, 0x00320103}, +{156, 0x00190003}, {156, 0x00330103}, {156, 0x000C0101}, {156, 0x000C0002}, +{159, 0x00340103}, +{162, 0x001A0003}, {162, 0x00350103}, {162, 0x001A0102}, +{165, 0x00360103}, {156, 0x00190102}, +{168, 0x001B0003}, {168, 0x00370103}, {168, 0x001B0102}, {168, 0x000D0101}, {168, 0x00060100}, {168, 0x000D0002}, {168, 0x00060001}, +{171, 0x00380103}, +{174, 0x001C0003}, {174, 0x00390103}, {174, 0x001C0102}, +{177, 0x003A0103}, +{180, 0x001D0003}, {180, 0x003B0103}, {180, 0x001D0102}, {180, 0x000E0101}, {180, 0x000E0002}, +{183, 0x003C0103}, +{186, 0x001E0003}, {186, 0x001E0102}, {186, 0x003D0103}, +{189, 0x003E0103}, +{192, 0x001F0003}, {192, 0x00030000}, {192, 0x003F0103}, {192, 0x001F0102}, {192, 0x000F0101}, {192, 0x00070100}, {192, 0x000F0002}, {192, 0x00070001}, +{195, 0x00400103}, +{198, 0x00200003}, {198, 0x00200102}, +{198, 0x00410103}, +{201, 0x00420103}, +{204, 0x00210003}, {204, 0x00430103}, {204, 0x00210102}, {204, 0x00100101}, {204, 0x00100002}, +{207, 0x00440103}, +{210, 0x00220003}, {210, 0x00450103}, {210, 0x00220102}, +{213, 0x00460103}, +{216, 0x00230003}, {216, 0x00470103}, {216, 0x00230102}, {216, 0x00110101}, {216, 0x00080001}, {216, 0x00080100}, {216, 0x00110002}, +{219, 0x00480103}, +{222, 0x00240003}, {222, 0x00240102}, +{222, 0x00490103}, +{225, 0x004A0103}, +{228, 0x004B0103}, {228, 0x00250102}, {228, 0x00120101}, {228, 0x00250003}, {228, 0x00120002}, +{231, 0x004C0103}, +{234, 0x00260003}, {234, 0x00260102}, +{234, 0x004D0103}, +{237, 0x004E0103}, +{240, 0x00270003}, {240, 0x00270102}, {240, 0x004F0103}, {240, 0x00040000}, {240, 0x00130101}, {240, 0x00090001}, {240, 0x00090100}, {240, 0x00130002}, +{243, 0x00500103}, +{246, 0x00280003}, {246, 0x00280102}, +{246, 0x00510103}, +{249, 0x00520103}, +{252, 0x00290003}, {252, 0x00530103}, {252, 0x00290102}, {252, 0x00140002}, {252, 0x00140101}, +{258, 0x002A0102}, +{264, 0x00150002}, {264, 0x002B0102}, {264, 0x00150101}, {264, 0x000A0001}, {264, 0x000A0100}, +{270, 0x002C0102}, +{276, 0x00160002}, {276, 0x002D0102}, {276, 0x00160101}, +{282, 0x002E0102}, +{288, 0x00170002}, {288, 0x002F0102}, {288, 0x00050000}, {288, 0x000B0001}, {288, 0x00170101}, {288, 0x000B0100}, +{294, 0x00300102}, +{300, 0x00180002}, {300, 0x00310102}, {300, 0x00180101}, +{306, 0x00320102}, +{312, 0x00190002}, {312, 0x00330102}, {312, 0x000C0001}, {312, 0x00190101}, {312, 0x000C0100}, +{318, 0x00340102}, +{324, 0x001A0002}, {324, 0x00350102}, {324, 0x001A0101}, +{330, 0x00360102}, +{336, 0x001B0002}, {336, 0x00370102}, {336, 0x00060000}, {336, 0x000D0001}, {336, 0x001B0101}, {336, 0x000D0100}, +{342, 0x00380102}, +{348, 0x001C0002}, {348, 0x00390102}, {384, 0x00070000}, {348, 0x001C0101}, {384, 0x000F0100}, {384, 0x000F0001}, +{354, 0x003A0102}, +{360, 0x001D0002}, {360, 0x003B0102}, {360, 0x000E0001}, {360, 0x001D0101}, {360, 0x000E0100}, +{366, 0x003C0102}, +{372, 0x001E0002}, {372, 0x003D0102}, {372, 0x001E0101}, +{378, 0x003E0102}, +{384, 0x001F0002}, {384, 0x003F0102}, {384, 0x001F0101}, +{390, 0x00400102}, +{396, 0x00200002}, {396, 0x00410102}, {396, 0x00200101}, +{402, 0x00420102}, +{408, 0x00210002}, {408, 0x00430102}, {408, 0x00210101}, {408, 0x00100100}, {408, 0x00100001}, +{414, 0x00440102}, +{420, 0x00220002}, {420, 0x00450102}, {420, 0x00220101}, +{426, 0x00460102}, +{432, 0x00230002}, {432, 0x00470102}, {432, 0x00230101}, {432, 0x00080000}, {432, 0x00110001}, {432, 0x00110100}, +{438, 0x00480102}, {480, 0x00130001}, {480, 0x00130100}, {480, 0x00090000}, +{444, 0x00240002}, {444, 0x00490102}, {444, 0x00240101}, +{450, 0x004A0102}, +{456, 0x00250002}, {456, 0x004B0102}, {456, 0x00250101}, {456, 0x00120100}, {456, 0x00120001}, +{462, 0x004C0102}, +{468, 0x00260002}, {468, 0x004D0102}, {468, 0x00260101}, +{474, 0x004E0102}, +{480, 0x00270002}, {480, 0x004F0102}, {480, 0x00270101}, +{486, 0x00500102}, +{492, 0x00280002}, {492, 0x00510102}, {492, 0x00280101}, +{498, 0x00520102}, +{504, 0x00140001}, {504, 0x00290002}, {504, 0x00530102}, {504, 0x00290101}, {504, 0x00140100}, +{516, 0x002A0101}, +{528, 0x00150001}, {528, 0x002B0101}, {528, 0x00150100}, {528, 0x000A0000}, +{540, 0x002C0101}, +{552, 0x00160001}, {552, 0x002D0101}, {552, 0x00160100}, +{564, 0x002E0101}, +{576, 0x00170001}, {576, 0x002F0101}, {576, 0x00170100}, {576, 0x000B0000}, +{588, 0x00300101}, +{600, 0x00180001}, {600, 0x00310101}, {600, 0x00180100}, +{612, 0x00320101}, +{624, 0x00190001}, {624, 0x00330101}, {624, 0x000C0000}, {624, 0x00190100}, +{636, 0x00340101}, +{648, 0x001A0001}, {648, 0x00350101}, {648, 0x001A0100}, +{660, 0x00360101}, +{672, 0x001B0001}, {672, 0x00370101}, {672, 0x000D0000}, {672, 0x001B0100}, +{684, 0x00380101}, +{696, 0x001C0001}, {696, 0x00390101}, {696, 0x001C0100}, +{708, 0x003A0101}, +{720, 0x001D0001}, {720, 0x003B0101}, {720, 0x000E0000}, {720, 0x001D0100}, +{732, 0x003C0101}, +{744, 0x001E0001}, {744, 0x003D0101}, {744, 0x001E0100}, +{756, 0x003E0101}, +{768, 0x001F0001}, {768, 0x003F0101}, {768, 0x000F0000}, {768, 0x001F0100}, +{780, 0x00400101}, +{792, 0x00200001}, {792, 0x00410101}, {792, 0x00200100}, +{804, 0x00420101}, +{816, 0x00210001}, {816, 0x00430101}, {816, 0x00100000}, {816, 0x00210100}, +{828, 0x00440101}, +{840, 0x00220001}, {840, 0x00450101}, {840, 0x00220100}, +{852, 0x00460101}, +{864, 0x00230001}, {864, 0x00470101}, {864, 0x00230100}, {864, 0x00110000}, +{876, 0x00480101}, +{888, 0x00240001}, {888, 0x00490101}, {888, 0x00240100}, +{900, 0x004A0101}, +{912, 0x00250001}, {912, 0x004B0101}, {912, 0x00120000}, {912, 0x00250100}, +{924, 0x004C0101}, +{936, 0x004D0101}, {936, 0x00260100}, +{948, 0x004E0101}, +{960, 0x00270001}, {960, 0x004F0101}, {960, 0x00270100}, {960, 0x00130000}, +{972, 0x00500101}, +{984, 0x00280001}, {984, 0x00510101}, {984, 0x00280100}, +{936, 0x00260001}, {996, 0x00520101}, +{1008, 0x00290001}, {1008, 0x00530101}, {1008, 0x00290100}, {1008, 0x00140000}, +{1032, 0x002A0100}, +{1056, 0x00150000}, {1056, 0x002B0100}, +{1080, 0x002C0100}, +{1104, 0x00160000}, {1104, 0x002D0100}, +{1128, 0x002E0100}, +{1152, 0x00170000}, {1152, 0x002F0100}, +{1176, 0x00300100}, +{1200, 0x00180000}, {1200, 0x00310100}, +{1224, 0x00320100}, +{1248, 0x00190000}, {1248, 0x00330100}, +{1272, 0x00340100}, +{1296, 0x001A0000}, {1296, 0x00350100}, +{1320, 0x00360100}, +{1344, 0x001B0000}, {1344, 0x00370100}, +{1368, 0x00380100}, +{1392, 0x001C0000}, {1392, 0x00390100}, +{1416, 0x003A0100}, +{1440, 0x001D0000}, {1440, 0x003B0100}, +{1464, 0x003C0100}, +{1488, 0x001E0000}, {1488, 0x003D0100}, +{1512, 0x003E0100}, +{1536, 0x001F0000}, {1536, 0x003F0100}, +{1560, 0x00400100}, +{1584, 0x00200000}, {1584, 0x00410100}, +{1608, 0x00420100}, +{1632, 0x00210000}, {1632, 0x00430100}, +{1656, 0x00440100}, +{1680, 0x00220000}, {1680, 0x00450100}, +{1704, 0x00460100}, +{1728, 0x00230000}, {1728, 0x00470100}, +{1752, 0x00480100}, +{1776, 0x00240000}, {1776, 0x00490100}, +{1800, 0x004A0100}, +{1824, 0x004B0100}, {1824, 0x00250000}, +{1848, 0x004C0100}, +{1872, 0x00260000}, {1872, 0x004D0100}, +{1896, 0x004E0100}, +{1920, 0x00270000}, {1920, 0x004F0100}, +{1944, 0x00500100}, +{1968, 0x00280000}, {1968, 0x00510100}, +{1992, 0x00520100}, +{2016, 0x00290000}, {2016, 0x00530100} +};*/ +void wmt_set_DIV(enum dev_id dev,int div) +{ + int PLL_NO, div_addr_offs; + + PLL_NO = calc_pll_num(dev, &div_addr_offs); + check_PLL_DIV_busy(); + if (dev == DEV_WMTNA) + *(volatile unsigned int *)(PMC_BASE + div_addr_offs) = (0x200 | ((div == 32) ? 0 : div)); + else + *(volatile unsigned int *)(PMC_BASE + div_addr_offs) + = ((dev == DEV_SDTV)?0x10000:0) + ((div == 32) ? 0 : div); + check_PLL_DIV_busy(); +} + +extern int vpp_parse_param(char *buf, unsigned int *param, + int cnt, unsigned int hex_mask); + +#define WMT_VDD_CONFIG +struct workqueue_struct *mmfreq_workqueue = 0; +struct work_struct mmfreq_work; +static int div_table[3][7]; +static int table_num; +unsigned int mc5_08_default; +unsigned int mc5_18_default; +unsigned int mc5_20_default; + +void wmt_resume_mmfreq(void) +{ + int current_voltage = 0; + + if (re) { + current_voltage = regulator_get_voltage(re); + regulator_set_voltage(re, current_voltage, current_voltage); + } + mmfreq_cur_num = ~0; +} + +void wmt_suspend_mmfreq(void) +{ + if (!mmfreq_workqueue) + return; + + flush_workqueue(mmfreq_workqueue); +} + +void wmt_do_mmfreq(struct work_struct *ptr) +{ + static int init; + + int num = mmfreq_num; + int dev_cnt; + + wmt_clk_mutex_lock(1); + dev_cnt = dev_en_count[MMFREQ_VD]; + wmt_clk_mutex_lock(0); + + +// printk("%s %d --> %d,%d\n", __FUNCTION__, mmfreq_cur_num, num,dev_cnt); + + if (init == 0) { +#ifdef WMT_VDD_CONFIG + re = regulator_get(NULL, "wmt_vdd"); +#endif + init = 1; + } + + if (dev_cnt) + return; + + if (num > table_num) + return; + + if (num == mmfreq_cur_num) + return; + + if (mmfreq_debug) { +#ifdef WMT_VDD_CONFIG + printk("mmfreq %d --> %d,re 0x%x\n",mmfreq_cur_num,num,(int)re); +#endif + printk("vol %d,mali %d,vpp %d,vdu %d,vduna %d,cmn %d,cmnna %d\n", + div_table[num][0], div_table[num][1], + div_table[num][2], div_table[num][3], + div_table[num][4], div_table[num][5], + div_table[num][6]); + } +#ifdef WMT_VDD_CONFIG + if (mmfreq_cur_num < num) { /* lo to hi */ + regulator_set_voltage(re, div_table[num][0]*1000, div_table[num][0]*1000); + } +#endif + wmt_set_DIV(DEV_MALI,div_table[num][1]); + wmt_set_DIV(DEV_VPP,div_table[num][2]); + wmt_set_DIV(DEV_WMTVDU,div_table[num][3]); + wmt_set_DIV(DEV_WMTNA,div_table[num][4]); + wmt_set_DIV(DEV_CNMVDU,div_table[num][5]); + wmt_set_DIV(DEV_CNMNA,div_table[num][6]); +#ifdef WMT_VDD_CONFIG + if (mmfreq_cur_num > num) { /* hi to lo */ + regulator_set_voltage(re, div_table[num][0]*1000, div_table[num][0]*1000); + } +#endif + mmfreq_cur_num = num; + printk("vol %d,mali %d,vpp %d,vdu %d,vduna %d,cmn %d,cmnna %d,end wmt_do_mmfreq\n", + div_table[num][0], div_table[num][1], + div_table[num][2], div_table[num][3], + div_table[num][4], div_table[num][5], + div_table[num][6]); +} + +void wmt_set_mmfreq(int num) +{ + (*(volatile unsigned int *)(MEMORY_CTRL_V4_CFG_BASE_ADDR + 0x08)) = + (num) ? 0x00f80000 : mc5_08_default; + (*(volatile unsigned int *)(MEMORY_CTRL_V4_CFG_BASE_ADDR + 0x18)) = + (num) ? 0x00fa0000 : mc5_18_default; + (*(volatile unsigned int *)(MEMORY_CTRL_V4_CFG_BASE_ADDR + 0x20)) = + (num) ? 0x00020505 : mc5_20_default; + + if (!mmfreq_workqueue) + return; + +// printk("%s %d --> %d\n", __FUNCTION__, mmfreq_cur_num, num); + mmfreq_num = num; + if (mmfreq_cur_num == num) + return; + + flush_workqueue(mmfreq_workqueue); + queue_work(mmfreq_workqueue, &mmfreq_work); +} + +void wmt_enable_mmfreq(enum wmt_mmfreq_type type, int enable) +{ + wmt_clk_mutex_lock(1); + mmfreq_type_mask = (enable) ? + (mmfreq_type_mask | type) : (mmfreq_type_mask & ~type); + wmt_clk_mutex_lock(0); + wmt_set_mmfreq((mmfreq_type_mask) ? 1 : 0); +} + +int wmt_mmfreq_init(void) +{ + char buf[100]; + int varlen = 100; + int parm[21]; + int i; + unsigned int info; + + mc5_08_default = + (*(volatile unsigned int *)(MEMORY_CTRL_V4_CFG_BASE_ADDR + 0x08)); + mc5_18_default = + (*(volatile unsigned int *)(MEMORY_CTRL_V4_CFG_BASE_ADDR + 0x18)); + mc5_20_default = + (*(volatile unsigned int *)(MEMORY_CTRL_V4_CFG_BASE_ADDR + 0x20)); + + info = *(volatile unsigned int *)(CPINFO); + if (info & 0x8) + i = wmt_getsyspara("wmt.mmfreqlp.param", buf, &varlen); + else + i = wmt_getsyspara("wmt.mmfreq.param", buf, &varlen); + + if (i) + return 0; + + table_num = vpp_parse_param(buf, (unsigned int *)parm, 21, 0x1); + if (parm[0] & 0x1) { + mmfreq_debug = (parm[0] & 0x10) ? 1 : 0; +#if 0 + for (i = 0; i < table_num; i++ ) { + printk("%d:%d,",i,parm[i]); + } + printk("\n"); +#endif + table_num = (table_num - 3 + 1) / 9; + printk("mmfreq.parm en 0x%x,sr %d,num %d,%d\n",parm[0],parm[1],parm[2],table_num); + for (i=0; i<table_num; i++) { + div_table[i][0] = parm[9 * i + 4]; + div_table[i][1] = parm[9 * i + 5]; + div_table[i][2] = parm[9 * i + 6]; + div_table[i][3] = parm[9 * i + 7]; + div_table[i][4] = parm[9 * i + 8]; + div_table[i][5] = parm[9 * i + 9]; + div_table[i][6] = parm[9 * i + 10]; + printk("vol %d,mali %d,vpp %d,vdu %d,vduna %d,cmn %d,cmnna %d\n", + div_table[i][0], div_table[i][1], + div_table[i][2], div_table[i][3], + div_table[i][4], div_table[i][5], + div_table[i][6]); + } + } + else { + table_num = 0; + printk("mmfreq disable\n"); + } + + if (table_num >= 2) { + mmfreq_workqueue = create_singlethread_workqueue("mmfreq_wq"); + if (!mmfreq_workqueue) + return -1; + INIT_WORK(&mmfreq_work,wmt_do_mmfreq); + } + return 0; +} +module_init(wmt_mmfreq_init); + diff --git a/arch/arm/mach-wmt/wmt_cpufreq.c b/arch/arm/mach-wmt/wmt_cpufreq.c new file mode 100644 index 00000000..f5046f40 --- /dev/null +++ b/arch/arm/mach-wmt/wmt_cpufreq.c @@ -0,0 +1,752 @@ +/*++ + linux/arch/arm/mach-wmt/wmt_cpufreq.c + + Copyright (c) 2013 WonderMedia Technologies, Inc. + + This program is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software Foundation, + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with + this program. If not, see <http://www.gnu.org/licenses/>. + + WonderMedia Technologies, Inc. + 10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C. +--*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/cpufreq.h> +#include <linux/delay.h> +#include <linux/reboot.h> +#include <linux/syscalls.h> +#include <linux/suspend.h> +#include <asm/smp_plat.h> +#include <asm/cpu.h> +#include <linux/regulator/consumer.h> + +#include <mach/wmt_env.h> +#include <mach/hardware.h> + +/* +#define DEBUG +*/ +#ifdef DEBUG +static int dbg_mask = 1; +module_param(dbg_mask, int, S_IRUGO | S_IWUSR); +#define fq_dbg(fmt, args...) \ + do {\ + if (dbg_mask) \ + printk(KERN_ERR "[%s]_%d_%d: " fmt, __func__ , __LINE__, smp_processor_id(), ## args);\ + } while (0) +#define fq_trace() \ + do {\ + if (dbg_mask) \ + printk(KERN_ERR "trace in %s %d\n", __func__, __LINE__);\ + } while (0) +#else +#define fq_dbg(fmt, args...) +#define fq_trace() +#endif + +#define DVFS_TABLE_NUM_MAX 20 +#define DVFS_TABLE_NUM_MIN 2 +#define PMC_BASE PM_CTRL_BASE_ADDR +#define PLLA_FREQ_KHZ (24 * 1000) +#define PMC_PLLA (PM_CTRL_BASE_ADDR + 0x200) +#define ARM_DIV_OFFSET (PM_CTRL_BASE_ADDR + 0x300) +#define MILLISEC_TO_MICROSEC 1000 + +struct wmt_dvfs_table { + unsigned int freq; + unsigned int vol; + unsigned int l2c_div; + unsigned int l2c_tag; + unsigned int l2c_data; + unsigned int axi; + int index; + struct list_head node; +}; + +struct wmt_dvfs_driver_data { + unsigned int tbl_num; + unsigned int sample_rate; + struct list_head wmt_dvfs_list; + struct wmt_dvfs_table *dvfs_table; + struct cpufreq_frequency_table *freq_table; +}; +static struct wmt_dvfs_driver_data wmt_dvfs_drvdata; +static struct regulator *re; +static unsigned int use_regulator = 0; + +char use_dvfs;/*flag for read env varalbe*/ +static char use_dvfs_debug; +static int wmt_dvfs_running;/*flag for reboot disable dvfs*/ +static DEFINE_SEMAPHORE(wmt_cpufreq_sem); + +/* register a reboot notifier, invoked in kernel_restart_prepare() + * or kernel_shutdown_prepare() */ +static int wmt_dvfs_reboot(struct notifier_block *, unsigned long, void *); +static struct notifier_block wmt_reboot_notifier = { + .notifier_call = wmt_dvfs_reboot, +}; + +static int wmt_suspend_target(unsigned target, unsigned relation, unsigned is_suspend); +static int wmt_dvfs_reboot(struct notifier_block *nb, unsigned long event, void *unused) +{ + unsigned target = 0xFFFFFFFF; + struct cpufreq_policy *policy = cpufreq_cpu_get(0); /* boot CPU */ + + /* find the highest freq in table */ + if (policy) + target = policy->max; + + /* change freq to highest to make sure reboot use the max volatage */ + wmt_suspend_target(target, CPUFREQ_RELATION_H, 0); + + return NOTIFY_OK; +} + +static int wmt_init_cpufreq_table(struct wmt_dvfs_driver_data *drv_data) +{ + int i = 0; + struct wmt_dvfs_table *dvfs_tbl = NULL; + struct cpufreq_frequency_table *freq_tbl = NULL; + + freq_tbl = kzalloc(sizeof(struct cpufreq_frequency_table) * (drv_data->tbl_num + 1), + GFP_KERNEL); + if (freq_tbl == NULL) { + printk(KERN_ERR "%s: failed to allocate frequency table\n", __func__); + return -ENOMEM; + } + + drv_data->freq_table = freq_tbl; + list_for_each_entry(dvfs_tbl, &drv_data->wmt_dvfs_list, node) { + fq_dbg("freq_table[%d]:freq->%dKhz", i, dvfs_tbl->freq); + freq_tbl[i].index = i; + freq_tbl[i].frequency = dvfs_tbl->freq; + i++; + } + /* the last element must be initialized to CPUFREQ_TABLE_END */ + freq_tbl[i].index = i; + freq_tbl[i].frequency = CPUFREQ_TABLE_END; + + return 0; +} + +/* wmt_getspeed return unit is Khz */ +static unsigned int wmt_getspeed(unsigned int cpu) +{ + int freq = 0; + + if (cpu >= NR_CPUS) + return 0; + + freq = auto_pll_divisor(DEV_ARM, GET_FREQ, 0, 0) / 1000; + + if (freq < 0) + freq = 0; + + return freq; +} + +static struct wmt_dvfs_table *find_freq_ceil(unsigned int *target) +{ + struct wmt_dvfs_table *dvfs_tbl = NULL; + unsigned int freq = *target; + + list_for_each_entry(dvfs_tbl, &wmt_dvfs_drvdata.wmt_dvfs_list, node) { + if (dvfs_tbl->freq >= freq) { + *target = dvfs_tbl->freq; + return dvfs_tbl; + } + } + + return NULL; +} + +static struct wmt_dvfs_table *find_freq_floor(unsigned int *target) +{ + struct wmt_dvfs_table *dvfs_tbl = NULL; + unsigned int freq = *target; + + list_for_each_entry_reverse(dvfs_tbl, &wmt_dvfs_drvdata.wmt_dvfs_list, node) { + if (dvfs_tbl->freq <= freq) { + *target = dvfs_tbl->freq; + return dvfs_tbl; + } + } + + return NULL; +} + +static struct wmt_dvfs_table * +wmt_recalc_target_freq(unsigned *target_freq, unsigned relation) +{ + struct wmt_dvfs_table *dvfs_tbl = NULL; + + if (!target_freq) + return NULL; + + switch (relation) { + case CPUFREQ_RELATION_L: + /* Try to select a new_freq higher than or equal target_freq */ + dvfs_tbl = find_freq_ceil(target_freq); + break; + case CPUFREQ_RELATION_H: + /* Try to select a new_freq lower than or equal target_freq */ + dvfs_tbl = find_freq_floor(target_freq); + break; + default: + dvfs_tbl = NULL; + break; + } + + return dvfs_tbl; +} + +/* this is a debug func, for checking pmc divf, divr, divq value */ +static int get_arm_plla_param(void) +{ +#ifdef DEBUG + unsigned ft, df, dr, dq, div, tmp; + + tmp = *(volatile unsigned int *)PMC_PLLA; + fq_dbg("PMC PLLA REG IS 0x%08x\n", tmp); + + ft = (tmp >> 24) & 0x03; /*bit24 ~ bit26*/ + df = (tmp >> 16) & 0xFF; /*bit16 ~ bit23*/ + dr = (tmp >> 8) & 0x1F; /*bit8 ~ bit12*/ + dq = tmp & 0x03; + + tmp = *(volatile unsigned int *)ARM_DIV_OFFSET; + div = tmp & 0x1F; + + fq_dbg("ft:%d, df:%d, dr:%d, dq:%d, div:%d\n", ft, df, dr, dq, div); +#endif + return 0; +} + +static int wmt_change_plla_table(struct wmt_dvfs_table *plla_table, unsigned relation) +{ + int ret = 0; + int ret_vol = 0; + struct plla_param plla_env; + + plla_env.plla_clk = (plla_table->freq / 1000); + plla_env.arm_div = 1; + plla_env.l2c_div = plla_table->l2c_div; + plla_env.l2c_tag_div = plla_table->l2c_tag; + plla_env.l2c_data_div = plla_table->l2c_data; + plla_env.axi_div = plla_table->axi; + plla_env.tb_index = plla_table->index; + + switch (relation) { + case CPUFREQ_RELATION_L: + if (use_dvfs_debug) + printk(KERN_INFO "change to L %dKhz\n", plla_table->freq); + ret = set_plla_divisor(&plla_env); + if (plla_table->vol) { + if (use_dvfs_debug) + printk(KERN_INFO "pllal_table = %d\n", plla_table->vol*1000); + if (use_regulator) { + ret_vol = regulator_set_voltage(re, plla_table->vol*1000, plla_table->vol*1000); + if (ret_vol < 0) + printk(KERN_INFO "xxx set vol fail %d\n", ret_vol); + } + } + break; + case CPUFREQ_RELATION_H: + if ((use_regulator == 1) && (regulator_is_enabled(re) < 0)) { + if (use_dvfs_debug) + printk(KERN_INFO "xxx regulator is disabled\n"); + ret = -EINVAL; + } else { + if (plla_table->vol) { + if (use_dvfs_debug) + printk(KERN_INFO "pllah_table = %d\n", plla_table->vol*1000); + if (use_regulator) + ret = regulator_set_voltage(re, plla_table->vol*1000, plla_table->vol*1000); + } + if (ret >= 0) { + if (use_dvfs_debug) + printk(KERN_INFO "change to H %dKhz\n", plla_table->freq); + ret = set_plla_divisor(&plla_env); + } else + printk(KERN_INFO "xxx set vol fail %d\n", ret); + } + break; + default: + break; + } + + return ret; +} + +/* target is calculated by cpufreq governor, unit Khz */ +static int freq_save = 0; // unit Khz +static int wmt_target(struct cpufreq_policy *, unsigned, unsigned); +static int wmt_suspend_target(unsigned target, unsigned relation, unsigned is_suspend) +{ + unsigned int cur_freq = wmt_getspeed(0); + int voltage = 0; + + down(&wmt_cpufreq_sem); + + /* + * Set to the highest voltage before suspend/reboot + */ + if (use_regulator) { + if (use_dvfs_debug) + printk("Set to Max. Voltage: %dmV\n", wmt_dvfs_drvdata.dvfs_table[(wmt_dvfs_drvdata.tbl_num - 1)].vol); + regulator_set_voltage(re, wmt_dvfs_drvdata.dvfs_table[(wmt_dvfs_drvdata.tbl_num - 1)].vol * 1000, + wmt_dvfs_drvdata.dvfs_table[(wmt_dvfs_drvdata.tbl_num - 1)].vol * 1000); + voltage = regulator_get_voltage(re); + printk("Current voltage = %d\n", voltage); + } + /* for some governor, like userspace, performance, powersaving, + * need change frequency to pre-suspend when resume */ + wmt_dvfs_running = 0; + freq_save = cur_freq; + up(&wmt_cpufreq_sem); + + return 0; +} + +/* + * Note that loops_per_jiffy is not updated on SMP systems in + * cpufreq driver. So, update the per-CPU loops_per_jiffy value + * on frequency transition. We need to update all dependent CPUs. + */ +#ifdef CONFIG_SMP +struct lpj_info { + unsigned long ref; + unsigned int freq; +}; +static DEFINE_PER_CPU(struct lpj_info, lpj_ref); +static struct lpj_info global_lpj_ref; + +static void +wmt_update_lpj(struct cpufreq_policy *policy, struct cpufreq_freqs freqs) +{ + unsigned int i = 0; + + for_each_cpu(i, policy->cpus) { + struct lpj_info *lpj = &per_cpu(lpj_ref, i); + if (!lpj->freq) { + lpj->ref = per_cpu(cpu_data, i).loops_per_jiffy; + lpj->freq = freqs.old; + } + per_cpu(cpu_data, i).loops_per_jiffy = cpufreq_scale(lpj->ref, + lpj->freq, freqs.new); + } + /* And don't forget to adjust the global one */ + if (!global_lpj_ref.freq) { + global_lpj_ref.ref = loops_per_jiffy; + global_lpj_ref.freq = freqs.old; + } + loops_per_jiffy = cpufreq_scale(global_lpj_ref.ref, + global_lpj_ref.freq, freqs.new); +} +#else +static void wmt_update_lpj(struct cpufreq_policy *policy, struct cpufreq_freqs freqs) +{ + return ; +} +#endif + +/* target is calculated by cpufreq governor, unit Khz */ +static int +wmt_target(struct cpufreq_policy *policy, unsigned target, unsigned relation) +{ + int ret = 0; + unsigned int i = 0; + struct cpufreq_freqs freqs; + struct wmt_dvfs_table *dvfs_tbl = NULL; + + if (policy->cpu > NR_CPUS) + return -EINVAL; + + fq_dbg("cpu freq:%dMhz now, need %s to %dMhz\n", wmt_getspeed(policy->cpu) / 1000, + (relation == CPUFREQ_RELATION_L) ? "DOWN" : "UP", target / 1000); + if (use_regulator) { + if (regulator_is_enabled(re) < 0) { + ret = -EBUSY; + fq_dbg("regulator is disabled\n\n"); + goto out; + } + } + + /* Ensure desired rate is within allowed range. Some govenors + * (ondemand) will just pass target_freq=0 to get the minimum. */ + + if ((strncmp(policy->governor->name, "performance", CPUFREQ_NAME_LEN)) && (wmt_dvfs_drvdata.tbl_num > 2)) + { + if (policy->max >= wmt_dvfs_drvdata.dvfs_table[(wmt_dvfs_drvdata.tbl_num - 1)].freq) + policy->max = wmt_dvfs_drvdata.dvfs_table[(wmt_dvfs_drvdata.tbl_num - 2)].freq; + if (policy->min >= wmt_dvfs_drvdata.dvfs_table[(wmt_dvfs_drvdata.tbl_num - 1)].freq) + policy->min = wmt_dvfs_drvdata.dvfs_table[(wmt_dvfs_drvdata.tbl_num - 2)].freq; + } + + if (target < policy->min) + target = policy->min; + if (target > policy->max) + target = policy->max; + + /* find out (freq, voltage) pair to do dvfs */ + dvfs_tbl = wmt_recalc_target_freq(&target, relation); + if (dvfs_tbl == NULL) { + fq_dbg("Can not change to target_freq:%dKhz", target); + ret = -EINVAL; + goto out; + } + fq_dbg("recalculated target freq is %dMhz\n", target / 1000); + + freqs.cpu = policy->cpu; + freqs.new = target; + freqs.old = wmt_getspeed(policy->cpu); + + if (freqs.new == freqs.old) { + ret = 0; + goto out; + } else if (freqs.new > freqs.old) + relation = CPUFREQ_RELATION_H; + else + relation = CPUFREQ_RELATION_L; + + /* notifier for all cpus */ + for_each_cpu(i, policy->cpus) { + freqs.cpu = i; + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + } + + /* actually we just scaling CPU frequency here */ + get_arm_plla_param(); + ret = wmt_change_plla_table(dvfs_tbl, relation); + get_arm_plla_param(); + fq_dbg("change to %dKhz\n", ret / 1000); + + if (ret < 0) { + ret = -EFAULT; + fq_dbg("wmt_cpufreq: auto_pll_divisor failed\n"); + } else { + wmt_update_lpj(policy, freqs); + + for_each_cpu(i, policy->cpus) { + freqs.cpu = i; + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + } + } + +out: + fq_dbg("cpu freq scaled to %dMhz now\n\n", wmt_getspeed(policy->cpu) / 1000); + return ret; +} + +static int +wmt_dvfs_target(struct cpufreq_policy *policy, unsigned target, unsigned relation) +{ + int ret = 0; + + down(&wmt_cpufreq_sem); + + if (!wmt_dvfs_running) { + ret = -ENODEV; + fq_dbg("dvfs is not running now!\n"); + goto out; + } + + ret = wmt_target(policy, target, relation); + +out: + up(&wmt_cpufreq_sem); + fq_dbg("cpu freq scaled to %dMhz now\n\n", wmt_getspeed(policy->cpu) / 1000); + return ret; +} + + +static int +wmt_cpufreq_pm_notify(struct notifier_block *nb, unsigned long event, void *dummy) +{ + struct cpufreq_policy *policy = cpufreq_cpu_get(0); + int default_voltage = 0; + struct plla_param plla_env; + struct wmt_dvfs_table *plla_table = NULL; + + plla_table = &wmt_dvfs_drvdata.dvfs_table[0]; + + plla_env.plla_clk = (plla_table->freq / 1000); + plla_env.arm_div = 1; + plla_env.l2c_div = plla_table->l2c_div; + plla_env.l2c_tag_div = plla_table->l2c_tag; + plla_env.l2c_data_div = plla_table->l2c_data; + plla_env.axi_div = plla_table->axi; + plla_env.tb_index = plla_table->index; + + if (event == PM_SUSPEND_PREPARE) + wmt_suspend_target(0, CPUFREQ_RELATION_L, 1); + else if (event == PM_POST_SUSPEND) { + if (use_regulator) { + default_voltage = regulator_get_voltage(re); + if (use_dvfs_debug) + printk("default_voltage = %d\n", default_voltage); + regulator_set_voltage(re, default_voltage, default_voltage); + if (use_dvfs_debug) + printk("Set Min. Freq = %d\n", plla_env.plla_clk); + set_plla_divisor(&plla_env); + } + down(&wmt_cpufreq_sem); + wmt_dvfs_running = 1; + wmt_target(policy, freq_save, CPUFREQ_RELATION_H); + up(&wmt_cpufreq_sem); + /* when resume back, changed to suspended freq */ + } + + return NOTIFY_OK; +} + +static struct notifier_block wmt_cpu_pm_notifier = { + .notifier_call = wmt_cpufreq_pm_notify, +}; + +static int wmt_verify_speed(struct cpufreq_policy *policy) +{ + int ret = 0; + struct cpufreq_frequency_table *freq_tbl = wmt_dvfs_drvdata.freq_table; + + if (policy->cpu >= NR_CPUS) + return -EINVAL; + + if (NULL == freq_tbl) + ret = -EINVAL; + else + ret = cpufreq_frequency_table_verify(policy, freq_tbl); + + return ret; +} + +static int wmt_cpu_init(struct cpufreq_policy *policy) +{ + int ret = 0; + struct cpufreq_frequency_table *wmt_freq_tbl = NULL; + + fq_dbg("CPU%d cpufreq init\n", policy->cpu); + if (policy->cpu >= NR_CPUS) + return -EINVAL; + + wmt_freq_tbl = wmt_dvfs_drvdata.freq_table; + if (!wmt_freq_tbl) + return -EINVAL; + + policy->cur = wmt_getspeed(policy->cpu); + policy->min = wmt_getspeed(policy->cpu); + policy->max = wmt_getspeed(policy->cpu); + + /* + * check each frequency and find max_freq + * min_freq in the table, then set: + * policy->min = policy->cpuinfo.min_freq = min_freq; + * policy->max = policy->cpuinfo.max_freq = max_freq; + */ + ret = cpufreq_frequency_table_cpuinfo(policy, wmt_freq_tbl); + if (0 == ret) + cpufreq_frequency_table_get_attr(wmt_freq_tbl, policy->cpu); + + /* + * On ARM-CortaxA9 configuartion, both processors share the voltage + * and clock. So both CPUs needs to be scaled together and hence + * needs software co-ordination. Use cpufreq affected_cpus + * interface to handle this scenario. Additional is_smp() check + * is to keep SMP_ON_UP build working. + */ + if (is_smp()) { + policy->shared_type = CPUFREQ_SHARED_TYPE_ANY; + cpumask_setall(policy->cpus); + } + /* only CPU0 can enable dvfs when cpufreq init. + * when cpu hotplug enable, cpu1 will re-init when plugin */ + if (!policy->cpu) { + down(&wmt_cpufreq_sem); + wmt_dvfs_running = 1; + wmt_target(policy, policy->max, CPUFREQ_RELATION_H); + up(&wmt_cpufreq_sem); + } + /* + * 1. make sure current frequency be covered in cpufreq_table + * 2. change cpu frequency to policy-max for fast booting + */ + + policy->cur = wmt_getspeed(policy->cpu); + policy->cpuinfo.transition_latency = wmt_dvfs_drvdata.sample_rate; + + fq_dbg("CPU%d,p->max:%d, p->min:%d, c->max=%d, c->min=%d, current:%d\n", + policy->cpu, policy->max, policy->min, policy->cpuinfo.max_freq, + policy->cpuinfo.min_freq, wmt_getspeed(policy->cpu)); + + return ret; +} + +static struct freq_attr *wmt_cpufreq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static struct cpufreq_driver wmt_cpufreq_driver = { + .name = "wmt_cpufreq", + .owner = THIS_MODULE, + .flags = CPUFREQ_STICKY, + .init = wmt_cpu_init, + .verify = wmt_verify_speed, + .target = wmt_dvfs_target, + .get = wmt_getspeed, + .attr = wmt_cpufreq_attr, +}; + +static int __init wmt_cpufreq_check_env(void) +{ + int i = 0; + int ret = 0; + int varlen = 512; + char *ptr = NULL; + unsigned int tbl_num = 0; + unsigned int drv_en = 0; + unsigned int sample_rate = 0; + unsigned int freq = 0; + unsigned int voltage = 0; + unsigned int l2c_div = 0; + unsigned int l2c_tag = 0; + unsigned int l2c_data = 0; + unsigned int axi = 0; + struct wmt_dvfs_table *wmt_dvfs_tbl = NULL; + unsigned char buf[512] = {0}; + char f_is_bonding = 0; + + /* uboot env name is: wmt.cpufreq.param, format is: + <enable>:<sample_rate>:<table_number>:<[freq,voltage,l2c_div,l2c_tag,l2c_data,axi] + */ + + use_dvfs = 0; + use_dvfs_debug = 0; + + if (BONDING_OPTION_4BYTE_VAL & 0x8) + f_is_bonding = 1; + + if (f_is_bonding) + ret = wmt_getsyspara("wmt.cpufreqlp.param", buf, &varlen); + else + ret = wmt_getsyspara("wmt.cpufreq.param", buf, &varlen); + + if (ret) { + printk(KERN_INFO "Can not find uboot env wmt.cpufreq.param\n"); + ret = -ENODATA; + goto out; + } + fq_dbg("wmt.cpufreq.param:%s\n", buf); + + sscanf(buf, "%x:%d:%d", &drv_en, &sample_rate, &tbl_num); + if (!drv_en) { + printk(KERN_INFO "wmt cpufreq disaled\n"); + ret = -ENODEV; + goto out; + } + + /* 2 dvfs table at least */ + if (tbl_num < DVFS_TABLE_NUM_MIN) { + printk(KERN_INFO "No frequency information found\n"); + ret = -ENODATA; + goto out; + } + if (tbl_num > DVFS_TABLE_NUM_MAX) + tbl_num = DVFS_TABLE_NUM_MAX; + + wmt_dvfs_tbl = kzalloc(sizeof(struct wmt_dvfs_table) * tbl_num, GFP_KERNEL); + if (!wmt_dvfs_tbl) { + ret = -ENOMEM; + goto out; + } + wmt_dvfs_drvdata.tbl_num = tbl_num; + wmt_dvfs_drvdata.sample_rate = sample_rate * MILLISEC_TO_MICROSEC; + wmt_dvfs_drvdata.dvfs_table = wmt_dvfs_tbl; + INIT_LIST_HEAD(&wmt_dvfs_drvdata.wmt_dvfs_list); + + /* copy freq&vol info from uboot env to wmt_dvfs_table */ + ptr = buf; + for (i = 0; i < tbl_num; i++) { + strsep(&ptr, "["); + sscanf(ptr, "%d,%d,%d,%d,%d,%d]:[", &freq, &voltage, &l2c_div, &l2c_tag, &l2c_data, &axi); + wmt_dvfs_tbl[i].freq = freq*1000; + wmt_dvfs_tbl[i].vol = voltage; + wmt_dvfs_tbl[i].l2c_div = l2c_div; + wmt_dvfs_tbl[i].l2c_tag = l2c_tag; + wmt_dvfs_tbl[i].l2c_data = l2c_data; + wmt_dvfs_tbl[i].axi = axi; + wmt_dvfs_tbl[i].index = i; + INIT_LIST_HEAD(&wmt_dvfs_tbl[i].node); + list_add_tail(&wmt_dvfs_tbl[i].node, &wmt_dvfs_drvdata.wmt_dvfs_list); + fq_dbg("dvfs_table[%d]: freq %dMhz, voltage %dmV l2c_div %d l2c_tag %d l2c_data %d axi %d\n", + i, freq, voltage, l2c_div, l2c_tag, l2c_data, axi); +// printk("dvfs_table[%d]: freq %dMhz, voltage %dmV l2c_div %d l2c_tag %d l2c_data %d axi %d \n", +// i, freq, voltage, l2c_div, l2c_tag, l2c_data, axi);//gri + } + use_dvfs = 1; + + if (drv_en & 0x10) + use_dvfs_debug = 1; + +out: + return ret; +} + +static int __init wmt_cpufreq_driver_init(void) +{ + int ret = 0; + unsigned int chip_id = 0; + unsigned int bondingid = 0; + struct cpufreq_frequency_table *wmt_freq_tbl = NULL; + + wmt_dvfs_running = 0; + sema_init(&wmt_cpufreq_sem, 1); + wmt_getsocinfo(&chip_id, &bondingid); + + /* if cpufreq disabled, cpu will always run at current frequency + * which defined in wmt.plla.param */ + ret = wmt_cpufreq_check_env(); + if (ret) { + printk(KERN_WARNING "wmt_cpufreq check env failed, current cpu " + "frequency is %dKhz\n", wmt_getspeed(0)); + goto out; + } + /* copy dvfs info from uboot to cpufreq table, + generate cpu frequency table here */ + wmt_init_cpufreq_table(&wmt_dvfs_drvdata); + wmt_freq_tbl = wmt_dvfs_drvdata.freq_table; + if (NULL == wmt_freq_tbl) { + printk(KERN_ERR "wmt_cpufreq create wmt_freq_tbl failed\n"); + ret = -EINVAL; + goto out; + } + + register_reboot_notifier(&wmt_reboot_notifier); + register_pm_notifier(&wmt_cpu_pm_notifier); + + use_regulator = 1; + re = regulator_get(NULL, "wmt_corepower"); + if (IS_ERR(re)) + use_regulator = 0; + printk("[CPU-FREQ]use_regulator = %d\n", use_regulator); + ret = cpufreq_register_driver(&wmt_cpufreq_driver); + +out: + return ret; +} +late_initcall(wmt_cpufreq_driver_init); + +MODULE_AUTHOR("WonderMedia Technologies, Inc"); +MODULE_DESCRIPTION("WMT CPU frequency driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/arch/arm/mach-wmt/wmt_time.c b/arch/arm/mach-wmt/wmt_time.c new file mode 100644 index 00000000..e4f6093a --- /dev/null +++ b/arch/arm/mach-wmt/wmt_time.c @@ -0,0 +1,460 @@ +/*++ +linux/arch/arm/mach-wmt/wmt_time.c + +Copyright (c) 2008 WonderMedia Technologies, Inc. + +This program is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software Foundation, +either version 2 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. +You should have received a copy of the GNU General Public License along with +this program. If not, see <http://www.gnu.org/licenses/>. + +WonderMedia Technologies, Inc. +10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C. +--*/ + +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/clocksource.h> +#include <linux/clockchips.h> +#include <linux/delay.h> +#include <linux/export.h> + +#include <asm/mach/time.h> +#include <asm/sched_clock.h> +#include <mach/hardware.h> +#include <mach/wmt_mmap.h> +#include <mach/wmt_env.h> +#include <asm/smp_twd.h> + +//#define DEBUG +#ifdef DEBUG +#define fq_dbg(fmt, args...) printk(KERN_ERR "[%s]_%d: " fmt, __func__ , __LINE__, ## args) +#define fq_trace() printk(KERN_ERR "trace in %s %d\n", __func__, __LINE__) +#else +#define fq_dbg(fmt, args...) +#define fq_trace() +#endif + +#define MIN_OSCR_DELTA 16 +#define MIN_HRTMR_CYC_DELTA 64 +#define WMT_CLOCK_TICK_RATE 3000000 +#define WMT_CLOCK_TICK_RATE1 5000000 +#define WMT_CLOCK_TICK_RATE2 6000000 +#define WMT_CLOCK_TICK_RATE3 6000000 + +/* Clear OS Timer1 irq */ +static inline void wmt_os_timer_clear_irq(void) +{ + OSTS_VAL = OSTS_M1; +} + +/* disable OS Timer1 irq */ +static inline void wmt_os_timer_disable_irq(void) +{ + OSTI_VAL &= ~OSTI_E1; +} + +/* Enable OS timer1 irq */ +static inline void wmt_os_timer_enable_irq(void) +{ + OSTI_VAL |= OSTI_E1; +} + +/* Stop ostimer, counter stop */ +static inline void wmt_os_timer_freeze_counter(void) +{ + OSTC_VAL = 0; +} + +/* Let OS Timer free run, counter increase now */ +static inline void wmt_os_timer_restart_counter(void) +{ + OSTC_VAL = OSTC_ENABLE; +} + +static inline void wmt_os_timer_set_counter(u32 new_cnt) +{ + OSCR_VAL = new_cnt; +} + +static inline u32 wmt_os_timer_read_counter(void) +{ + OSTC_VAL |= OSTC_RDREQ; + while (OSTA_VAL & OSTA_RCA) + ; + + return (u32)OSCR_VAL; +} + +static inline void wmt_os_timer_set_match(u32 new_match) +{ + /* check if can write OS Timer1 match register nows */ + while (OSTA_VAL & OSTA_MWA1) + ; + + OSM1_VAL = new_match; +} + +/* OS timer hardware initializing routine */ +/* TODO: Here we let os timer run, but disable interrupt, + when clcokevent registed, then enable interrupt +*/ +static void __init wmt_os_timer_init(void) +{ + wmt_os_timer_disable_irq(); + wmt_os_timer_clear_irq(); + wmt_os_timer_freeze_counter(); + wmt_os_timer_set_match(0); + wmt_os_timer_restart_counter(); +} + +/* for clocksource */ +static cycle_t wmt_timer_read_cycles(struct clocksource *cs) +{ + return (cycle_t)wmt_os_timer_read_counter(); +} + +struct clocksource wmt_clocksource = { + .name = "wmt_clocksource", + .rating = 200, + .read = wmt_timer_read_cycles, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static void __init wmt_clocksource_init(struct clocksource *cs) +{ + clocksource_register_hz(cs, WMT_CLOCK_TICK_RATE); + fq_dbg("%s mult:%u, shift:%u, max_idle_ns:%llu\n\n", cs->name, + cs->mult, cs->shift, cs->max_idle_ns); +} + +struct clocksource wmt_clocksource1 = { + .name = "wmt_clocksource1", + .rating = 150, + .read = wmt_timer_read_cycles, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static void __init wmt_clocksource_init1(struct clocksource *cs) +{ + clocksource_register_hz(cs, WMT_CLOCK_TICK_RATE1); + fq_dbg("%s mult:%u, shift:%u, max_idle_ns:%llu\n\n", cs->name, + cs->mult, cs->shift, cs->max_idle_ns); +} + + +struct clocksource wmt_clocksource2 = { + .name = "wmt_clocksource2", + .rating = 150, + .read = wmt_timer_read_cycles, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static void __init wmt_clocksource_init2(struct clocksource *cs) +{ + clocksource_register_hz(cs, WMT_CLOCK_TICK_RATE2); + fq_dbg("%s mult:%u, shift:%u, max_idle_ns:%llu\n\n", cs->name, + cs->mult, cs->shift, cs->max_idle_ns); +} + +struct clocksource wmt_clocksource3 = { + .name = "wmt_clocksource3", + .rating = 150, + .read = wmt_timer_read_cycles, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static void __init wmt_clocksource_init3(struct clocksource *cs) +{ + clocksource_register_hz(cs, WMT_CLOCK_TICK_RATE3); + fq_dbg("%s mult:%u, shift:%u, max_idle_ns:%llu\n\n", cs->name, + cs->mult, cs->shift, cs->max_idle_ns); +} + + +static int +wmt_timer_set_next_event(unsigned long cycles, struct clock_event_device *evt) +{ + unsigned long next = 0; + unsigned long oscr = 0; + + oscr = wmt_os_timer_read_counter(); + next = oscr + cycles; + /* set new value to os time1 match register */ + wmt_os_timer_set_match(next); + /* Enable match on timer 1 to cause interrupts. */ + wmt_os_timer_enable_irq(); + /* check if overflow */ + if ((signed long)(next - wmt_os_timer_read_counter()) <= MIN_OSCR_DELTA) { + fq_dbg("set os timer overflow!, set_cyc:%lu, now_cyc:%lu," + " next_cyc:%lu\n\n\n", cycles, oscr, next); + return -ETIME; + } + + return 0; +} + +static void +wmt_timer_set_mode(enum clock_event_mode mode, struct clock_event_device *evt) +{ + switch (mode) { + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_ONESHOT: + /* disable OS Timer irq here */ + wmt_os_timer_disable_irq(); + /* Clear match on OS Timer 1 */ + wmt_os_timer_clear_irq(); + break; + case CLOCK_EVT_MODE_RESUME: + case CLOCK_EVT_MODE_PERIODIC: + default: + break; + } + + return ; +} + +struct clock_event_device wmt_clockevent = { + .name = "wmt_clockevent", + .features = CLOCK_EVT_FEAT_ONESHOT, + .rating = 200, + .set_next_event = wmt_timer_set_next_event, + .set_mode = wmt_timer_set_mode, + .shift = 32, +}; + +static irqreturn_t wmt_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + + /* Clear match on OS Timer 1 irq */ + wmt_os_timer_clear_irq(); + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +struct irqaction wmt_timer_irq = { + .name = "wmt_timer", + .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, + .handler = wmt_timer_interrupt, + .dev_id = &wmt_clockevent, +}; + +static void __init wmt_clockevent_init(struct clock_event_device *evt, + unsigned int irq, struct irqaction *act) +{ + /* reset some elements for clockevent */ + evt->cpumask = cpumask_of(smp_processor_id()); + evt->mult = div_sc(WMT_CLOCK_TICK_RATE, NSEC_PER_SEC, evt->shift); + evt->max_delta_ns = clockevent_delta2ns(0x7fffffff, evt); + evt->min_delta_ns = clockevent_delta2ns(MIN_HRTMR_CYC_DELTA, evt); + fq_dbg("%s, mult:%u, shift:%d, max_delta:%llu, min_delta:%llu\n\n\n", + evt->name, evt->mult, evt->shift, evt->max_delta_ns, evt->min_delta_ns); + + /* setup os timer1 irq */ + if (setup_irq(irq, act)) + printk(KERN_ERR "setup clockevent %s irq %u, failed!\n\n", evt->name, irq); + + /* register clockevent */ + clockevents_register_device(evt); + + return ; +} + +/* for sched_clock */ +static u32 notrace wmt_read_sched_clock(void) +{ + return wmt_os_timer_read_counter(); +} + +/* for MPcore local timer */ +#ifdef CONFIG_LOCAL_TIMERS +extern void wmt3498_init_clocks(void); +#define WMT_LOCAL_TIMER_BASE (0xD8018000 + 0x600) +#define WMT_LOCAL_TIMER_PPI_NUM 29 +static DEFINE_TWD_LOCAL_TIMER(wmt_local_timer, + WMT_LOCAL_TIMER_BASE, WMT_LOCAL_TIMER_PPI_NUM); + +/* check if uboot env wmt.twd.disable = 1, if twd disable (=1), return 1, + * if twd enable, return 0 + */ +static int wmt_twd_is_disabled(void) +{ + int ret = 0; + int varlen = 128; + unsigned int drv_en = 0; + unsigned char buf[128] = {0}; + + /* uboot env name is: wmt.twd.disable */ + ret = wmt_getsyspara("wmt.twd.disable", buf, &varlen); + if (ret) { + ret = 0; + goto out; + } + + sscanf(buf, "%d", &drv_en); + if (drv_en) { + ret = -ENODEV; + goto out; + } + +out: + return ret; +} +#endif + +static void __init wmt_timer_init(void) +{ + /* prepare OS timer hardware, irq disabled */ + wmt_os_timer_init(); + /* os timer1 as clocksourece */ + wmt_clocksource_init(&wmt_clocksource); + wmt_clocksource_init1(&wmt_clocksource1); + wmt_clocksource_init2(&wmt_clocksource2); + wmt_clocksource_init3(&wmt_clocksource3); + /* sched_clock for timestamp, as printk.... */ + setup_sched_clock(wmt_read_sched_clock, 32, WMT_CLOCK_TICK_RATE); + /* os timer1 as clockevent device */ + wmt_clockevent_init(&wmt_clockevent, IRQ_OST1, &wmt_timer_irq); + +#ifdef CONFIG_LOCAL_TIMERS + wmt3498_init_clocks(); + if (wmt_twd_is_disabled()) { + printk(KERN_INFO "WMT local timer disabled\n"); + goto out; + } + if (twd_local_timer_register(&wmt_local_timer)) + printk(KERN_ERR "Register ARM local timer failed! Use broadcast mode!\n"); +#endif +out: + /* this is a MUST operation */ + wmt_os_timer_enable_irq(); + + return ; +} + +struct sys_timer wmt_timer = { + .init = wmt_timer_init +}; + + +/* below code is for old design compatibility */ +inline unsigned int wmt_read_oscr(void) +{ + return wmt_os_timer_read_counter(); +} +EXPORT_SYMBOL(wmt_read_oscr); + +int wmt_rtc_on = 1; +static int __init no_rtc(char *str) +{ + wmt_rtc_on = 0; + return 1; +} +__setup("nortc", no_rtc); + + +static void wmt_rtc_init(void) +{ + fq_dbg("Enter\n"); + + RTCC_VAL = (RTCC_ENA|RTCC_INTTYPE); + if (!(RTSR_VAL&RTSR_VAILD)) + while (!(RTSR_VAL&RTSR_VAILD)) + ; + /* Reset RTC alarm settings */ + //RTAS_VAL = 0; + /* Reset RTC test mode. */ + RTTM_VAL = 0; + + /* Patch 1, RTCD default value isn't 0x41 and it will not sync with RTDS. */ + if (RTCD_VAL == 0) { + while (RTWS_VAL & RTWS_DATESET) + ; + RTDS_VAL = RTDS_VAL; + } + + /* + * Disable all RTC control functions. + * Set to 24-hr format and update type to each second. + * Disable sec/min update interrupt. + * Let RTC free run without interrupts. + */ + /* RTCC_VAL &= ~(RTCC_12HR | RTCC_INTENA); */ + /* RTCC_VAL |= RTCC_ENA | RTCC_INTTYPE; */ + RTCC_VAL = (RTCC_ENA|RTCC_INTTYPE); + + if (RTWS_VAL & RTWS_CONTROL) { + while (RTWS_VAL & RTWS_CONTROL) + ; + } + + fq_dbg("Exit\n\n\n"); + return ; +} + +void wmt_read_rtc(unsigned int *date, unsigned int *time) +{ + /* before read rtc, we should check if rtc already be initialized */ + if (((RTCC_VAL & RTCC_ENA) == 0) || ((RTSR_VAL & RTSR_VAILD) == 0)) { + wmt_rtc_init(); + } + + if (!(RTSR_VAL & RTSR_VAILD)) + while (!(RTSR_VAL & RTSR_VAILD)) + ; + + if (RTWS_VAL & RTWS_DATESET) + while (RTWS_VAL & RTWS_DATESET) + ; + + *date = RTCD_VAL; + + if (RTWS_VAL & RTWS_TIMESET) + while (RTWS_VAL & RTWS_TIMESET) + ; + + *time = RTCT_VAL; +} +EXPORT_SYMBOL(wmt_read_rtc); + +/* + * get current Gregorian date from RTC, + * coverts to seconds since 1970-01-01 00:00:00 + */ +static unsigned long wmt_get_rtc_time(void) +{ + unsigned int date = 0; + unsigned int time = 0; + + /* if no rtc or rtc disabled, return 0, + * means timekeeping is 1970-01-01 00:00:00 when system booting */ + if (!wmt_rtc_on) + return 0; + + wmt_read_rtc(&date, &time); + return mktime(RTCD_CENT(date)? 2100+RTCD_YEAR(date) : 2000+RTCD_YEAR(date), + RTCD_MON(date), RTCD_MDAY(date),RTCT_HOUR(time), + RTCT_MIN(time), RTCT_SEC(time)); +} + +void read_persistent_clock(struct timespec *ts) +{ + ts->tv_nsec = 0; + ts->tv_sec = (__kernel_time_t)wmt_get_rtc_time(); + + fq_dbg("seconds read from RTC is %lu\n\n\n", ts->tv_sec); + return ; +} |