/*++
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 .
WonderMedia Technologies, Inc.
10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C.
--*/
#include
#include "wmt_clk.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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< 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< 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< 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< *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< 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< %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= 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);