diff options
Diffstat (limited to 'board/wmt/wmt_clk.c')
-rwxr-xr-x | board/wmt/wmt_clk.c | 1367 |
1 files changed, 1367 insertions, 0 deletions
diff --git a/board/wmt/wmt_clk.c b/board/wmt/wmt_clk.c new file mode 100755 index 0000000..3bb41a0 --- /dev/null +++ b/board/wmt/wmt_clk.c @@ -0,0 +1,1367 @@ +/*++ +Copyright (c) 2010 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 <common.h> +#include "include/wmt_clk.h" + +#define PMC_BASE 0xD8130000 +#define PMC_PLL 0xD8130200 +#define PMC_CLK 0xD8130250 +#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*/ +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 + +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++) { + printf("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 +static int disable_dev_clk(enum dev_id dev) +{ + int en_count; + + if (dev >= 128) { + printf("device dev_id > 128\n"); + 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))); + } else if (en_count > 1) { + dev_en_count[dev] = (--en_count); + } + + #ifdef debug_clk + print_refer_count(); + #endif + + return en_count; +} + +static int enable_dev_clk(enum dev_id dev) +{ + int en_count, tmp; + + if (dev > 128) { + printf("device dev_id > 128\n"); + return -1; + } + + #ifdef debug_clk + printf("device dev_id = %d\n",dev); + print_refer_count(); + #endif + + en_count = dev_en_count[dev]; + 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)); + } else + dev_en_count[dev] = (++en_count); + + #ifdef debug_clk + print_refer_count(); + #endif + + 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))))) { + /*enable_dev_clk(dev);*/ + printf("dev[%d] clock disabled\n", dev); + return -1; + } + } + return 0; +} +/* +* get only PLL frequency not including divisor of each device +* PLL_NO : PLL number(0~6) means PLLA ~ PLLG +* return : return the frequence in MHz. +*/ +int get_pll_freq(int PLL_NO) +{ + int freq; + unsigned int tmp; + + if (PLL_NO < 0 || PLL_NO > 6) { + printf("PLL NO(%d) is out of range\n", PLL_NO); + return -1; + } + + tmp = *(volatile unsigned int *)(PMC_PLL + 4*PLL_NO); + freq = ((SRC_FREQ)*GET_DIVF(tmp)) / (GET_DIVR(tmp)*GET_DIVQ(tmp)); + + return freq; +} + +static int get_freq(enum dev_id dev, int *divisor) { + + unsigned int tmp, freq, div = 0, base = 1000000; + int PLL_NO, j = 0, div_addr_offs; + + PLL_NO = calc_pll_num(dev, &div_addr_offs); + if (PLL_NO == -1) { + printf("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); + //printf("div_addr_offs=0x%x PLL_NO=%d \n", div_addr_offs, PLL_NO); + 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 = (SRC_FREQ*GET_DIVF(tmp))*(base/(GET_DIVR(tmp)*GET_DIVQ(tmp)*div)); + + return freq; +} +static int get_freq_t(enum dev_id dev, int *divisor) { + + unsigned int div = 0, freq = 0, base = 1000000, tmp; + int PLL_NO, i, j = 0, div_addr_offs; + #ifdef DEBUG_CLK + unsigned int t1 = 0, t2 = 0; + #endif + + PLL_NO = calc_pll_num(dev, &div_addr_offs); + if (PLL_NO == -1) { + printf("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 + printf("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 + tmp = *(volatile unsigned int *)(PMC_PLL + 4*PLL_NO); + for (i = 0; i < ARRAYSIZE(pllmapAll); i++) { + if (pllmapAll[i].pll == tmp) { + freq = pllmapAll[i].freq; + #ifdef DEBUG_CLK + printf("********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) { + printf("gfreq : dev%d********************pll not match table**************\n", dev); + /*freq = (SRC_FREQ*GET_DIVF(tmp))*(base/(GET_DIVR(tmp)*GET_DIVQ(tmp)*div));*/ + freq = ((SRC_FREQ/GET_DIVQ(tmp)) * (GET_DIVF(tmp)/GET_DIVR(tmp)) * base) / div; + } else { + freq = (freq * 15625)<<6; + freq = freq/div; + } + + #ifdef DEBUG_CLK + t2 = wmt_read_oscr() - t1; + printf("************delay_time=%d\n", t2); + printf("get_freq cmd: freq=%d \n", freq); + #endif + + return freq; +} + +static int set_divisor(enum dev_id dev, int unit, int freq, int *divisor) { + + unsigned int tmp, PLL, div = 0; + int PLL_NO, i, j = 0, div_addr_offs, SD_MMC = 1, ret; + + 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) { + printf("device not found"); + return -1; + } + + if (PLL_NO == -2) { + printf("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))/(GET_DIVR(tmp)*GET_DIVQ(tmp)); + + ret = check_clk_enabled(dev, SET_DIV); + if (ret) + return -1; + + PLL *= 1000000; + if (unit == 1) + freq *= 1000; + else if (unit == 2) + freq *= 1000000; + + if (SD_MMC == 0) { + for (i = 1; i < 33; i++) { + if ((i > 1 && (i%2)) && (PLL_NO == 2)) + continue; + if ((PLL/i) <= ((unsigned int)freq)) { + *divisor = div = i; + break; + } + } + } else { + if ((PLL/64) >= ((unsigned int)freq)) + j = 1; + for (i = 1; i < 33; i++) + if ((PLL/(i*(j?64:1)*2)) <= ((unsigned int)freq)) { + *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(); + if (SD_MMC == 1) + return (PLL/(div*(j?64:1)*2)); + else + return (PLL/(div*(j?64:1))); + } + printf("no suitable divisor"); + return -1; +} +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) { + printf("device not found"); + return -1; + } else if (PLL_NO == -2) { + printf("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) { + printf("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) { + 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 + printf("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(); + return freq; + } + printf("no suitable divisor dev0x%x div=%d treq%d\n", dev, div, freq_target); + return -1; +} + +static int set_pll_speed(enum dev_id dev, int unit, int freq, int *divisor) { + + unsigned int PLL, DIVF=1, DIVR=1, DIVQ=1; + unsigned int last_freq, div=1, base, base_f=1/*, filt_range, filter*/; + unsigned long minor, min_minor = 0xFF000000; + int PLL_NO, div_addr_offs, DF, DR, VD, DQ; + + PLL_NO = calc_pll_num(dev, &div_addr_offs); + if (PLL_NO == -1) { + printf("device not belong to PLL A B C D E F G"); + return -1; + } + + if (PLL_NO == -2) { + printf("device not found"); + 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); + base = 1000000; + if (unit == 1) + base_f = 1000; + else if (unit == 2) + base_f = 1000000; + else if (unit != 0) { + printf("unit is out of range"); + return -1; + } + + 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; + if (((SRC_FREQ * 2 * (DF+1)) * (base/(VD * (DR+1)*(1<<DQ)))) == (freq * base_f)) { + DIVF = DF; + DIVR = DR; + DIVQ = DQ; + div = VD; + /*printf("find the equal value\n");*/ + goto find; + } else if (((SRC_FREQ * 2 * (DF+1)) * (base/(VD * (DR+1)*(1<<DQ)))) < (freq * base_f)) { + minor = (freq * base_f) - ((SRC_FREQ * 2 * (DF+1)) * (base/(VD * (DR+1)*(1<<DQ)))); + //printf("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 (((SRC_FREQ * 2 * (DF+1)) * (base/(VD * (DR+1)*(1<<DQ)))) > (freq * base_f)) { + if (PLL_NO == 2) { + minor = ((SRC_FREQ * 2 * (DF+1)) * (base/(VD * (DR+1)*(1<<DQ)))) - (freq * base_f); + if (minor < min_minor) { + DIVF = DF; + DIVR = DR; + DIVQ = DQ; + div = VD; + min_minor = minor; + } + } + break; + } + }//DF + }//DQ + }//DR +/*minimun:*/ +find: + + /*filter = SRC_FREQ/(DIVR+1); + filt_range = check_filter(filter);*/ + last_freq = (SRC_FREQ * 2 * (DIVF+1)) * (base / ((DIVR+1)*(1<<DIVQ)*(*divisor))); + /*printf("DIVF%d, DIVR%d, DIVQ%d, divisor%d freq=%dHz \n", + DIVF, DIVR, DIVQ, *divisor, last_freq);*/ + PLL = (DIVF<<16) + (DIVR<<8) + DIVQ/* + (filt_range<<24)*/; + + /* 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(); + /*printf("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(); + return last_freq; + + printf("no suitable pll"); + return -1; +} + +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) { + printf("device not belong to PLL A B C D E"); + return -1; + } + + if (PLL_NO == -2) { + printf("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) { + printf("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) + printf("minimum minor=0x%x, unit=%d \n", (unsigned int)min_minor, unit); + printf("SETPLL********dev%d PLL_%X PLL=0x%x cal_freq=%d target_freq%d\n", + dev, PLL_NO+10, tmp_PLL, set_freq, freq); + #endif + PLL = tmp_PLL; + + check_PLL_DIV_busy(); + *(volatile unsigned int *)(PMC_PLL + 4*PLL_NO) = PLL; + if (wait) + check_PLL_DIV_busy(); + return set_freq; +} + +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, base, base_f=1/*, filt_range, filter*/; + unsigned long minor, min_minor = 0xFF000000; + int PLL_NO, div_addr_offs, DF, DR, VD, DQ; + + PLL_NO = calc_pll_num(dev, &div_addr_offs); + if (PLL_NO == -1) { + printf("device not belong to PLL A B C D E"); + return -1; + } + + if (PLL_NO == -2) { + printf("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); + + base = 1000000; + if (unit == 1) + base_f = 1000; + else if (unit == 2) + base_f = 1000000; + else if (unit != 0) { + printf("unit is out of range"); + return -1; + } + + //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)) + 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; + if (((SRC_FREQ * 2 * (DF+1)) * (base/(VD * (DR+1)*(1<<DQ)))) == (freq * base_f)) { + DIVF = DF; + DIVR = DR; + DIVQ = DQ; + div = VD; + /*printf("find the equal value\n");*/ + goto find; + } else if (((SRC_FREQ * 2 * (DF+1)) * (base/(VD * (DR+1)*(1<<DQ)))) < (freq * base_f)) { + minor = (freq * base_f) - ((SRC_FREQ * 2 * (DF+1)) * (base/(VD * (DR+1)*(1<<DQ)))); + /*if (unit == 2) + minor = ((SRC_FREQ * DF) - (VD * (DR*(1<<DQ)))) * base; + else if (unit == 1) + minor = (1000 * SRC_FREQ * DF) - (freq * VD * (DR*(1<<DQ))); + else + minor = (1000 * SRC_FREQ * DF) - ((freq * VD * (DR*(1<<DQ)))/1000);*/ + //printf("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; + /*if (min_minor < 1000000 && unit == 2) + goto minimun; + else if (min_minor < 1000 && unit == 1) + goto minimun; + else if (min_minor < 20 && unit == 0) + goto minimun;*/ + } + } else if (((SRC_FREQ * 2 * (DF+1)) * (base/(VD * (DR+1)*(1<<DQ)))) > (freq * base_f)) { + if (PLL_NO == 2) { + minor = ((SRC_FREQ * 2 * (DF+1)) * (base/(VD * (DR+1)*(1<<DQ)))) - (freq * base_f); + if (minor < min_minor) { + DIVF = DF; + DIVR = DR; + DIVQ = DQ; + div = VD; + min_minor = minor; + } + } + break; + } + }//DF + }//VD + }//DQ + }//DR +/*minimun:*/ + //printf("minimum minor=0x%x, unit=%d \n", (unsigned int)min_minor, unit); +find: + + /*filter = SRC_FREQ/(DIVR+1); + filt_range = check_filter(filter);*/ + *divisor = div; + last_freq = (SRC_FREQ * 2 * (DIVF+1)) * (base / ((DIVR+1)*(1<<DIVQ)*(*divisor))); + /*printf("DIVF%d, DIVR%d, DIVQ%d, divisor%d freq=%dHz \n", + DIVF, DIVR, DIVQ, *divisor, last_freq);*/ + PLL = (DIVF<<16) + (DIVR<<8) + DIVQ/* + (filt_range<<24)*/; + + + /* 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);*/ + printf("device clock is disabled"); + return -1; + } + } + + check_PLL_DIV_busy(); + if (old_divisor < *divisor) { + *(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(); + } + //printf("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(); + /*printf("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) { + *(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(); + } + + return last_freq; + + printf("no suitable divisor"); + return -1; +} + + +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) { + printf("device not belong to PLL A B C D E"); + return -1; + } else if (PLL_NO == -2) { + printf("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) { + printf("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) + printf("minimum minor=0x%x, unit=%d \n", (unsigned int)min_minor, unit); + printf("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; + + 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(); + } + + return set_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; + + switch (cmd) { + case CLK_DISABLE: + if (dev < 128) { + en_count = disable_dev_clk(dev); + return en_count; + } else { + printf("device has not clock enable register"); + return -1; + } + case CLK_ENABLE: + if (dev < 128) { + en_count = enable_dev_clk(dev); + return en_count; + } else { + printf("device has not clock enable register"); + return -1; + } + case GET_FREQ: + #ifdef PMC_TABLE + last_freq = get_freq_t(dev, &divisor); + #else + last_freq = get_freq(dev, &divisor); + #endif + return last_freq; + case SET_DIV: + divisor = 0; + #ifdef PMC_TABLE + last_freq = set_divisor_t(dev, unit, freq, &divisor); + #else + last_freq = set_divisor(dev, unit, freq, &divisor); + #endif + return last_freq; + case SET_PLL: + divisor = 0; + #ifdef PMC_TABLE + last_freq = set_pll_speed_t(dev, unit, freq, &divisor, 1); + #else + last_freq = set_pll_speed(dev, unit, freq, &divisor); + #endif + return last_freq; + case SET_PLLDIV: + divisor = 0; + #ifdef PMC_TABLE + last_freq = set_pll_divisor_t(dev, unit, freq, &divisor); + #else + last_freq = set_pll_divisor(dev, unit, freq, &divisor); + #endif + return last_freq; + default: + printf("clock cmd unknow"); + return -1; + } +} + + +/** +* 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 (DIVF < 0 || DIVF > MAX_DF){ + printf("DIVF is out of range 0 ~ 127"); + return -1; + } + if (DIVR < 0 || DIVR > MAX_DR){ + printf("DIVR is out of range 0 ~ 1"); + return -1; + } + if (DIVQ < 0 || DIVQ > MAX_DQ){ + printf("DIVQ is out of range 0 ~ 3"); + return -1; + } + if ((800/SRC_FREQ) > ((2*(DIVF+1))/(DIVR+1))) { + printf("((2(DIVF+1))/(DIVR+1)) should great than (800/SRC_FREQ)"); + return -1; + } + if ((1600/SRC_FREQ) < ((2*(DIVF+1))/(DIVR+1))) { + printf("((2*(DIVF+1))/(DIVR+1)) should less than (1600/SRC_FREQ)"); + return -1; + } + + if (dev_div > ((SD_MMC == 1)?63:31)){ + printf("divisor is out of range 0 ~ 31"); + return -1; + } + + PLL_NO = calc_pll_num(dev, &div_addr_offs); + if (PLL_NO == -1) { + printf("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; + //printf("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); + } + + 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*/; + //printf("set divisor =0x%x, divider address=0x%x\n", PLL, (PMC_BASE + div_addr_offs)); + return freq; +} + + |