/* arch/arm/mach-msm/clock.c * * Copyright (C) 2007 Google, Inc. * Copyright (c) 2007 QUALCOMM Incorporated * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * 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. * */ #include #include #include #include #include #include #include #include #include #include #include #include "clock.h" #if defined(CONFIG_MSM_AMSS_VERSION_WINCE) //#include "dex_comm.h" #endif #include "proc_comm.h" //#define ENABLE_CLOCK_INFO 1 static DEFINE_MUTEX(clocks_mutex); static DEFINE_SPINLOCK(clocks_lock); static LIST_HEAD(clocks); enum { DEBUG_UNKNOWN_ID = 1<<0, DEBUG_UNKNOWN_FREQ = 1<<1, DEBUG_MDNS = 1<<2, DEBUG_UNKNOWN_CMD = 1<<3, }; static int debug_mask=DEBUG_MDNS; module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); #if 1 #define D(x...) printk(KERN_DEBUG "clock-wince: " x) #else #define D(x...) do {} while (0) #endif struct mdns_clock_params { unsigned long freq; uint32_t calc_freq; uint32_t md; uint32_t ns; uint32_t pll_freq; uint32_t clk_id; }; struct msm_clock_params { unsigned clk_id; uint32_t glbl; // Whitch config reg GLBL_CLK_ENA or GLBL_CLK_ENA_2 unsigned idx; unsigned offset; // Offset points to .ns register unsigned ns_only; // value to fill in ns register, rather than using mdns_clock_params look-up table char *name; }; static int max_clk_rate[NR_CLKS], min_clk_rate[NR_CLKS]; #define GLBL_CLK_ENA ((uint32_t)MSM_CLK_CTL_BASE) #define GLBL_CLK_ENA_2 ((uint32_t)MSM_CLK_CTL_BASE + 0x220) #if defined(CONFIG_ARCH_QSD8X50) #define PLLn_BASE(n) (MSM_CLK_CTL_BASE + 0x300 + 32 * (n)) #else #define PLLn_BASE(n) (MSM_CLK_CTL_BASE + 0x300 + 28 * (n)) #endif #define TCX0 19200000 // Hz #define PLL_FREQ(l, m, n) (TCX0 * (l) + TCX0 * (m) / (n)) static unsigned int pll_get_rate(int n) { unsigned int mode, L, M, N, freq; if (n == -1) return TCX0; #if defined(CONFIG_ARCH_QSD8X50) if (n > 1) #else if (n > 3) #endif return 0; else { mode = readl(PLLn_BASE(n) + 0x0); L = readl(PLLn_BASE(n) + 0x4); M = readl(PLLn_BASE(n) + 0x8); N = readl(PLLn_BASE(n) + 0xc); freq = PLL_FREQ(L, M, N); printk(KERN_INFO "PLL%d: MODE=%08x L=%08x M=%08x N=%08x freq=%u Hz (%u MHz)\n", n, mode, L, M, N, freq, freq / 1000000); \ } return freq; } static unsigned int idx2pll(uint32_t idx) { int ret; switch(idx) { case 0: /* TCX0 */ ret=-1; break; case 1: /* PLL1 */ ret=1; break; case 4: /* PLL0 */ ret=0; break; default: ret=4; /* invalid */ } return ret; } static struct msm_clock_params msm_clock_parameters[] = { // Full ena/md/ns clock { .clk_id = SDC1_CLK, .glbl = GLBL_CLK_ENA, .idx = 7, .offset = 0xa4, .name="SDC1_CLK",}, { .clk_id = SDC2_CLK, .glbl = GLBL_CLK_ENA, .idx = 8, .offset = 0xac, .name="SDC2_CLK",}, #if defined(CONFIG_ARCH_QSD8X50) { .clk_id = SDC3_CLK, .glbl = GLBL_CLK_ENA, .idx = 27, .offset = 0x3d8, .name="SDC3_CLK",}, { .clk_id = SDC4_CLK, .glbl = GLBL_CLK_ENA, .idx = 28, .offset = 0x3e0, .name="SDC4_CLK",}, #else { .clk_id = SDC3_CLK, .glbl = GLBL_CLK_ENA, .idx = 27, .offset = 0xb4, .name="SDC3_CLK",}, { .clk_id = SDC4_CLK, .glbl = GLBL_CLK_ENA, .idx = 28, .offset = 0xbc, .name="SDC4_CLK",}, #endif #if defined(CONFIG_ARCH_QSD8X50) { .clk_id = UART1DM_CLK, .glbl = GLBL_CLK_ENA, .idx = 17, .offset = 0x124, .name="UART1DM_CLK",}, { .clk_id = UART2DM_CLK, .glbl = GLBL_CLK_ENA, .idx = 26, .offset = 0x12c, .name="UART2DM_CLK",}, { .clk_id = USB_HS_CLK, .glbl = GLBL_CLK_ENA_2, .idx = 7, .offset = 0x3e8, .ns_only = 0xb41, .name="USB_HS_CLK",}, #else { .clk_id = UART1DM_CLK, .glbl = GLBL_CLK_ENA, .idx = 17, .offset = 0xd4, .name="UART1DM_CLK",}, { .clk_id = UART2DM_CLK, .glbl = GLBL_CLK_ENA, .idx = 26, .offset = 0xdc, .name="UART2DM_CLK",}, { .clk_id = USB_HS_CLK, .glbl = GLBL_CLK_ENA, .idx = 25, .offset = 0x2c0, .ns_only = 0xb00, .name="USB_HS_CLK",}, #endif // these both enable the GRP and IMEM clocks. { .clk_id = GRP_CLK, .glbl = GLBL_CLK_ENA, .idx = 3, .offset = 0x84, .ns_only = 0xa80, .name="GRP_CLK", }, { .clk_id = IMEM_CLK, .glbl = GLBL_CLK_ENA, .idx = 3, .offset = 0x84, .ns_only = 0xa80, .name="IMEM_CLK", }, // MD/NS only; offset = Ns reg #if defined(CONFIG_ARCH_QSD8X50) { .clk_id = VFE_CLK, .glbl = GLBL_CLK_ENA, .idx = 2, .offset = 0x40, .name="VFE_CLK", }, #else { .clk_id = VFE_CLK, .offset = 0x44, .name="VFE_CLK", }, #endif // Enable bit only; bit = 1U << idx { .clk_id = MDP_CLK, .glbl = GLBL_CLK_ENA, .idx = 9, .name="MDP_CLK",}, // NS-reg only; offset = Ns reg, ns_only = Ns value #if defined(CONFIG_ARCH_QSD8X50) { .clk_id = GP_CLK, .glbl = GLBL_CLK_ENA, .offset = 0x58, .ns_only = 0x800, .name="GP_CLK" }, #else { .clk_id = GP_CLK, .offset = 0x5c, .ns_only = 0xa06, .name="GP_CLK" }, #endif #if defined(CONFIG_ARCH_QSD8X50) { .clk_id = PMDH_CLK, .glbl = GLBL_CLK_ENA_2, .idx = 4, .offset = 0x8c, .ns_only = 0x00c, .name="PMDH_CLK"}, #else { .clk_id = PMDH_CLK, .offset = 0x8c, .ns_only = 0xa0c, .name="PMDH_CLK"}, #endif #if defined(CONFIG_ARCH_QSD8X50) { .clk_id = I2C_CLK, .offset = 0x64, .ns_only = 0xa00, .name="I2C_CLK"}, #else { .clk_id = I2C_CLK, .offset = 0x68, .ns_only = 0xa00, .name="I2C_CLK"}, #endif #if defined(CONFIG_ARCH_QSD8X50) { .clk_id = SPI_CLK, .glbl = GLBL_CLK_ENA_2, .idx = 13, .offset = 0x14c, .ns_only = 0xa08, .name="SPI_CLK"}, #endif #if defined(CONFIG_ARCH_QSD8X50) // { .clk_id = UART1_CLK, .offset = 0xc0, .ns_only = 0xa00, .name="UART1_CLK"}, #else // { .clk_id = UART1_CLK, .offset = 0xe0, .ns_only = 0xa00, .name="UART1_CLK"}, #endif }; // This formula is used to generate md and ns reg values #define MSM_CLOCK_REG(frequency,M,N,D,PRE,a5,SRC,MNE,pll_frequency) { \ .freq = (frequency), \ .md = ((0xffff & (M)) << 16) | (0xffff & ~((D) << 1)), \ .ns = ((0xffff & ~((N) - (M))) << 16) \ | ((0xff & (0xa | (MNE))) << 8) \ | ((0x7 & (a5)) << 5) \ | ((0x3 & (PRE)) << 3) \ | (0x7 & (SRC)), \ .pll_freq = (pll_frequency), \ .calc_freq = 1000*((pll_frequency/1000)*M/((PRE+1)*N)), \ } struct mdns_clock_params msm_clock_freq_parameters[] = { MSM_CLOCK_REG( 144000, 3, 0x64, 0x32, 3, 3, 0, 1, 19200000), /* SD, 144kHz */ MSM_CLOCK_REG( 400000, 1, 0x30, 0x15, 0, 3, 0, 1, 19200000), /* SD, 400kHz */ #if 0 /* wince uses this clock setting for UART2DM */ MSM_CLOCK_REG( 1843200, 3, 0x64, 0x32, 3, 2, 4, 1, 245760000), /* 115200*16=1843200 */ // MSM_CLOCK_REG( , 2, 0xc8, 0x64, 3, 2, 1, 1, 768888888), /* 1.92MHz for 120000 bps */ #else MSM_CLOCK_REG( 7372800, 3, 0x64, 0x32, 0, 2, 4, 1, 245760000), /* 460800*16, will be divided by 4 for 115200 */ #endif MSM_CLOCK_REG(12000000, 1, 0x20, 0x10, 1, 3, 1, 1, 768000000), /* SD, 12MHz */ MSM_CLOCK_REG(14745600, 3, 0x32, 0x19, 0, 2, 4, 1, 245760000), /* BT, 921600 (*16)*/ MSM_CLOCK_REG(19200000, 1, 0x0a, 0x05, 3, 3, 1, 1, 768000000), /* SD, 19.2MHz */ MSM_CLOCK_REG(24000000, 1, 0x10, 0x08, 1, 3, 1, 1, 768000000), /* SD, 24MHz */ MSM_CLOCK_REG(24576000, 1, 0x0a, 0x05, 0, 2, 4, 1, 245760000), /* SD, 24,576000MHz */ MSM_CLOCK_REG(25000000, 14, 0xd7, 0x6b, 1, 3, 1, 1, 768000000), /* SD, 25MHz */ MSM_CLOCK_REG(32000000, 1, 0x0c, 0x06, 1, 3, 1, 1, 768000000), /* SD, 32MHz */ MSM_CLOCK_REG(48000000, 1, 0x08, 0x04, 1, 3, 1, 1, 768000000), /* SD, 48MHz */ MSM_CLOCK_REG(50000000, 25, 0xc0, 0x60, 1, 3, 1, 1, 768000000), /* SD, 50MHz */ MSM_CLOCK_REG(58982400, 6, 0x19, 0x0c, 0, 2, 4, 1, 245760000), /* BT, 3686400 (*16) */ MSM_CLOCK_REG(64000000,0x19, 0x60, 0x30, 0, 2, 4, 1, 245760000), /* BT, 4000000 (*16) */ }; static void set_grp_clk( int on ) { if ( on != 0 ) { //axi_reset writel(readl(MSM_CLK_CTL_BASE+0x208) |0x20, MSM_CLK_CTL_BASE+0x208); //AXI_RESET //row_reset writel(readl(MSM_CLK_CTL_BASE+0x214) |0x20000, MSM_CLK_CTL_BASE+0x214); //ROW_RESET //vdd_grp gfs_ctl writel( 0x11f, MSM_CLK_CTL_BASE+0x284); //VDD_GRP_GFS_CTL // very rough delay mdelay(20); //grp NS writel(readl(MSM_CLK_CTL_BASE+0x84) |0x800, MSM_CLK_CTL_BASE+0x84); //GRP_NS_REG writel(readl(MSM_CLK_CTL_BASE+0x84) |0x80, MSM_CLK_CTL_BASE+0x84); //GRP_NS_REG writel(readl(MSM_CLK_CTL_BASE+0x84) |0x200, MSM_CLK_CTL_BASE+0x84); //GRP_NS_REG //grp idx writel(readl(MSM_CLK_CTL_BASE) |0x8, MSM_CLK_CTL_BASE); //grp clk ramp writel(readl(MSM_CLK_CTL_BASE+0x290) &(~(0x4)), MSM_CLK_CTL_BASE+0x290); //MSM_RAIL_CLAMP_IO //Suppress bit 0 of grp MD (?!?) writel(readl(MSM_CLK_CTL_BASE+0x80) &(~(0x1)), MSM_CLK_CTL_BASE+0x80); //PRPH_WEB_NS_REG //axi_reset writel(readl(MSM_CLK_CTL_BASE+0x208) &(~(0x20)), MSM_CLK_CTL_BASE+0x208); //AXI_RESET //row_reset #if defined(CONFIG_ARCH_QSD8X50) writel(readl(MSM_CLK_CTL_BASE+0x218) &(~(0x20000)), MSM_CLK_CTL_BASE+0x218); //ROW_RESET #else writel(readl(MSM_CLK_CTL_BASE+0x214) &(~(0x20000)), MSM_CLK_CTL_BASE+0x214); //ROW_RESET #endif } else { //grp NS writel(readl(MSM_CLK_CTL_BASE+0x84) |0x800, MSM_CLK_CTL_BASE+0x84); //GRP_NS_REG writel(readl(MSM_CLK_CTL_BASE+0x84) |0x80, MSM_CLK_CTL_BASE+0x84); //GRP_NS_REG writel(readl(MSM_CLK_CTL_BASE+0x84) |0x200, MSM_CLK_CTL_BASE+0x84); //GRP_NS_REG //grp idx writel(readl(MSM_CLK_CTL_BASE) |0x8, MSM_CLK_CTL_BASE); //grp MD writel(readl(MSM_CLK_CTL_BASE+0x80) |0x1, MSM_CLK_CTL_BASE+0x80); //PRPH_WEB_NS_REG int i = 0; int status = 0; while ( status == 0 && i < 100) { i++; status = readl(MSM_CLK_CTL_BASE+0x84) & 0x1; } //axi_reset writel(readl(MSM_CLK_CTL_BASE+0x208) |0x20, MSM_CLK_CTL_BASE+0x208); //AXI_RESET //row_reset #if defined(CONFIG_ARCH_QSD8X50) writel(readl(MSM_CLK_CTL_BASE+0x218) |0x20000, MSM_CLK_CTL_BASE+0x218); //ROW_RESET #else writel(readl(MSM_CLK_CTL_BASE+0x214) |0x20000, MSM_CLK_CTL_BASE+0x214); //ROW_RESET #endif //grp NS writel(readl(MSM_CLK_CTL_BASE+0x84) &(~(0x800)), MSM_CLK_CTL_BASE+0x84); //GRP_NS_REG writel(readl(MSM_CLK_CTL_BASE+0x84) &(~(0x80)), MSM_CLK_CTL_BASE+0x84); //GRP_NS_REG writel(readl(MSM_CLK_CTL_BASE+0x84) &(~(0x200)), MSM_CLK_CTL_BASE+0x84); //GRP_NS_REG //grp clk ramp writel(readl(MSM_CLK_CTL_BASE+0x290) |0x4, MSM_CLK_CTL_BASE+0x290); //MSM_RAIL_CLAMP_IO writel( 0x11f, MSM_CLK_CTL_BASE+0x284); //VDD_GRP_GFS_CTL int control = readl(MSM_CLK_CTL_BASE+0x288); //VDD_VDC_GFS_CTL if ( control & 0x100 ) writel(readl(MSM_CLK_CTL_BASE) &(~(0x8)), MSM_CLK_CTL_BASE); } } static inline struct msm_clock_params msm_clk_get_params(uint32_t id) { int i; struct msm_clock_params empty = { }; for (i = 0; i < ARRAY_SIZE(msm_clock_parameters); i++) { if (id == msm_clock_parameters[i].clk_id) { return msm_clock_parameters[i]; } } return empty; } static inline uint32_t msm_clk_enable_bit(uint32_t id) { struct msm_clock_params params; params = msm_clk_get_params(id); if (!params.idx) return 0; return 1U << params.idx; } static inline uint32_t msm_clk_get_glbl(uint32_t id) { struct msm_clock_params params; params = msm_clk_get_params(id); if (!params.glbl) return 0; return params.glbl; } static inline unsigned msm_clk_reg_offset(uint32_t id) { struct msm_clock_params params; params = msm_clk_get_params(id); return params.offset; } static int set_mdns_host_clock(uint32_t id, unsigned long freq) { int n; unsigned offset; int retval; bool found; struct msm_clock_params params; uint32_t nsreg; found = 0; retval = -EINVAL; params = msm_clk_get_params(id); offset = params.offset; if(debug_mask&DEBUG_MDNS) D("set mdns: %u, %lu; bitidx=%u, offset=%x, ns=%x\n", id, freq, params.idx, params.offset, params.ns_only); if (!params.offset) { printk(KERN_WARNING "%s: FIXME! Don't know how to set clock %u - no known Md/Ns reg\n", __func__, id); return -ENOTSUPP; } // Turn off clock-enable bit if supported if (params.idx > 0 && params.glbl > 0) writel(readl(params.glbl) & ~(1U << params.idx), params.glbl); if (params.ns_only > 0) { nsreg = readl(MSM_CLK_CTL_BASE + offset) & 0xfffff000; writel( nsreg | params.ns_only, MSM_CLK_CTL_BASE + offset); found = 1; retval = 0; } else { for (n = ARRAY_SIZE(msm_clock_freq_parameters)-1; n >= 0; n--) { if (freq >= msm_clock_freq_parameters[n].freq) { // This clock requires MD and NS regs to set frequency: writel(msm_clock_freq_parameters[n].md, MSM_CLK_CTL_BASE + offset - 4); writel(msm_clock_freq_parameters[n].ns, MSM_CLK_CTL_BASE + offset); // msleep(5); if(debug_mask&DEBUG_MDNS) D("%s: %u, freq=%lu calc_freq=%u pll%d=%u expected pll =%u\n", __func__, id, msm_clock_freq_parameters[n].freq, msm_clock_freq_parameters[n].calc_freq, msm_clock_freq_parameters[n].ns&7, pll_get_rate(idx2pll(msm_clock_freq_parameters[n].ns&7)), msm_clock_freq_parameters[n].pll_freq ); retval = 0; found = 1; break; } } } // Turn clock-enable bit back on, if supported if (params.idx > 0 && params.glbl > 0) writel(readl(params.glbl) | (1U << params.idx), params.glbl); if (!found && debug_mask&DEBUG_UNKNOWN_FREQ) { printk(KERN_WARNING "clock-wince: FIXME! set_sdcc_host_clock could not " "find suitable parameter for freq %lu\n", freq); } // return retval; return 0; } static unsigned long get_mdns_host_clock(uint32_t id) { int n; unsigned offset; uint32_t mdreg; uint32_t nsreg; unsigned long freq = 0; offset = msm_clk_reg_offset(id); if (offset == 0) return -EINVAL; mdreg = readl(MSM_CLK_CTL_BASE + offset - 4); nsreg = readl(MSM_CLK_CTL_BASE + offset); for (n = 0; n < ARRAY_SIZE(msm_clock_freq_parameters); n++) { if (msm_clock_freq_parameters[n].md == mdreg && msm_clock_freq_parameters[n].ns == nsreg) { freq = msm_clock_freq_parameters[n].freq; break; } } return freq; } static int new_clk_set_rate(uint32_t id, unsigned long rate) { unsigned clk = -1; unsigned speed = 0; switch (id) { case ICODEC_RX_CLK: if (rate > 11289600) speed = 9; else if (rate > 8192000) speed = 8; else if (rate > 6144000) speed = 7; else if (rate > 5644800) speed = 6; else if (rate > 4096000) speed = 5; else if (rate > 3072000) speed = 4; else if (rate > 2822400) speed = 3; else if (rate > 2048000) speed = 2; else speed = 1; clk = 50; break; case ICODEC_TX_CLK: if (rate > 11289600) speed = 9; else if (rate > 8192000) speed = 8; else if (rate > 6144000) speed = 7; else if (rate > 5644800) speed = 6; else if (rate > 4096000) speed = 5; else if (rate > 3072000) speed = 4; else if (rate > 2822400) speed = 3; else if (rate > 2048000) speed = 2; else speed = 1; clk = 52; break; case ECODEC_CLK: if (rate > 2048000) speed = 3; else if (rate > 128000) speed = 2; else speed = 1; clk = 42; break; case SDAC_MCLK: if (rate > 1411200) speed = 9; else if (rate > 1024000) speed = 8; else if (rate > 768000) speed = 7; else if (rate > 705600) speed = 6; else if (rate > 512000) speed = 5; else if (rate > 384000) speed = 4; else if (rate > 352800) speed = 3; else if (rate > 256000) speed = 2; else speed = 1; clk = 64; break; case UART1DM_CLK: if (rate > 61440000) speed = 15; else if (rate > 58982400) speed = 14; else if (rate > 56000000) speed = 13; else if (rate > 51200000) speed = 12; else if (rate > 48000000) speed = 11; else if (rate > 40000000) speed = 10; else if (rate > 32000000) speed = 9; else if (rate > 24000000) speed = 8; else if (rate > 16000000) speed = 7; else if (rate > 15360000) speed = 6; else if (rate > 14745600) speed = 5; else if (rate > 7680000) speed = 4; else if (rate > 7372800) speed = 3; else if (rate > 3840000) speed = 2; else speed = 1; clk = 78; break; case UART2DM_CLK: if (rate > 61440000) speed = 15; else if (rate > 58982400) speed = 14; else if (rate > 56000000) speed = 13; else if (rate > 51200000) speed = 12; else if (rate > 48000000) speed = 11; else if (rate > 40000000) speed = 10; else if (rate > 32000000) speed = 9; else if (rate > 24000000) speed = 8; else if (rate > 16000000) speed = 7; else if (rate > 15360000) speed = 6; else if (rate > 14745600) speed = 5; else if (rate > 7680000) speed = 4; else if (rate > 7372800) speed = 3; else if (rate > 3840000) speed = 2; else speed = 1; clk = 80; break; case VFE_MDC_CLK: if (rate == 96000000) speed = 37; else if (rate == 48000000) speed = 32; else if (rate == 24000000) speed = 22; else if (rate == 12000000) speed = 14; else if (rate == 6000000) speed = 6; else if (rate == 3000000) speed = 1; else { printk("wrong MDC clock %d\n", rate); return 0; } clk = 40; break; case VFE_CLK: if (rate == 36000000) speed = 1; else if (rate == 48000000) speed = 2; else if (rate == 64000000) speed = 3; else if (rate == 78000000) speed = 4; else if (rate == 96000000) speed = 5; else { printk("wrong clock %d\n", rate); return 0; } clk = 41; break; case SPI_CLK: if (rate > 15360000) speed = 5; else if (rate > 9600000) speed = 4; else if (rate > 4800000) speed = 3; else if (rate > 960000) speed = 2; else speed = 1; clk = 95; break; /* case SDC1_CLK: clk = 66; break; case SDC2_CLK: clk = 67; break; case SDC3_CLK: clk = 68; break; case SDC4_CLK: clk = 69; break; case MDP_CLK: clk = 9; break; */ default: return -1; } #ifdef ENABLE_CLOCK_INFO printk("clk_rate %d : %d\n", clk, speed); #endif msm_proc_comm(PCOM_CLK_REGIME_SEC_SEL_SPEED, &clk, &speed); return 0; } static int new_clk_enable(uint32_t id) { unsigned clk = -1; switch (id) { case ICODEC_RX_CLK: clk = 50; break; case ICODEC_TX_CLK: clk = 52; break; case ECODEC_CLK: clk = 42; break; case SDAC_MCLK: clk = 64; break; case IMEM_CLK: clk = 55; break; case GRP_CLK: clk = 56; break; case ADM_CLK: clk = 19; break; case UART1DM_CLK: clk = 78; break; case UART2DM_CLK: clk = 80; break; case VFE_AXI_CLK: clk = 24; break; case VFE_MDC_CLK: clk = 40; break; case VFE_CLK: clk = 41; break; case MDC_CLK: clk = 53; // ?? break; case SPI_CLK: clk = 95; break; case MDP_CLK: clk = 9; break; /* case SDC1_CLK: clk = 66; break; case SDC2_CLK: clk = 67; break; case SDC3_CLK: clk = 68; break; case SDC4_CLK: clk = 69; break; */ default: return -1; } #ifdef ENABLE_CLOCK_INFO printk("clk_on %d\n", clk); #endif msm_proc_comm(PCOM_CLK_REGIME_SEC_ENABLE, &clk, 0); return 0; } static int new_clk_disable(uint32_t id) { unsigned clk = -1; switch (id) { case ICODEC_RX_CLK: clk = 50; break; case ICODEC_TX_CLK: clk = 52; break; case ECODEC_CLK: clk = 42; break; case SDAC_MCLK: clk = 64; break; case IMEM_CLK: clk = 55; break; case GRP_CLK: clk = 56; break; case ADM_CLK: clk = 19; break; case UART1DM_CLK: clk = 78; break; case UART2DM_CLK: clk = 80; break; case VFE_AXI_CLK: clk = 24; break; case VFE_MDC_CLK: clk = 40; break; case VFE_CLK: clk = 41; break; case MDC_CLK: clk = 53; // WTF?? break; case SPI_CLK: clk = 95; break; case MDP_CLK: clk = 9; break; /* case SDC1_CLK: clk = 66; break; case SDC2_CLK: clk = 67; break; case SDC3_CLK: clk = 68; break; case SDC4_CLK: clk = 69; break; case MDP_CLK: clk = 9; break; */ default: return -1; } #ifdef ENABLE_CLOCK_INFO printk("clk_off %d\n", clk); #endif msm_proc_comm(PCOM_CLK_REGIME_SEC_DISABLE, &clk, 0); return 0; } static int new_clk_set_flags(uint32_t id, unsigned long flags) { if (id == VFE_CLK) { // INTERNAL 0x00000100 << 1 if (flags == (0x00000100 << 1)) { uint32_t f = 0; msm_proc_comm(PCOM_CLK_REGIME_SEC_SEL_VFE_SRC, &f, 0); #ifdef ENABLE_CLOCK_INFO printk("internal VFE source\n"); #endif return 0; } // EXTERNAL 0x00000100 else if (flags == 0x00000100) { uint32_t f = 1; msm_proc_comm(PCOM_CLK_REGIME_SEC_SEL_VFE_SRC, &f, 0); #ifdef ENABLE_CLOCK_INFO printk("external VFE source\n"); #endif return 0; } } return -1; } ////////////////////////////////////////////////////////// static int pc_clk_enable(uint32_t id) { struct msm_clock_params params; int r; r = new_clk_enable(id); if (r != -1) return r; params = msm_clk_get_params(id); //XXX: too spammy, extreme debugging only: D(KERN_DEBUG "%s: %d\n", __func__, id); if ( id == IMEM_CLK || id == GRP_CLK ) { set_grp_clk( 1 ); writel(readl(params.glbl) | (1U << params.idx), params.glbl); return 0; } if (params.idx > 0 && params.glbl > 0) { writel(readl(params.glbl) | (1U << params.idx), params.glbl); return 0; } else if (params.ns_only > 0 && params.offset) { writel((readl(MSM_CLK_CTL_BASE + params.offset) &0xfffff000) | params.ns_only, MSM_CLK_CTL_BASE + params.offset); return 0; } if(debug_mask&DEBUG_UNKNOWN_ID) printk(KERN_WARNING "%s: FIXME! enabling a clock that doesn't have an ena bit " "or ns-only offset: %u\n", __func__, id); return 0; } static void pc_clk_disable(uint32_t id) { struct msm_clock_params params; params = msm_clk_get_params(id); int r; r = new_clk_disable(id); if (r != -1) return; //XXX: D(KERN_DEBUG "%s: %d\n", __func__, id); if ( id == IMEM_CLK || id == GRP_CLK ) { set_grp_clk( 1 ); writel(readl(params.glbl) & ~(1U << params.idx), params.glbl); return; } if (params.idx > 0 && params.glbl > 0) { writel(readl(params.glbl) & ~(1U << params.idx), params.glbl); } else if (params.ns_only > 0 && params.offset) { writel(readl(MSM_CLK_CTL_BASE + params.offset) & 0xfffff000, MSM_CLK_CTL_BASE + params.offset); } else { if(debug_mask&DEBUG_UNKNOWN_ID) printk(KERN_WARNING "%s: FIXME! disabling a clock that doesn't have an " "ena bit: %u\n", __func__, id); } } static int pc_clk_set_rate(uint32_t id, unsigned long rate) { int retval; retval = 0; int r; r = new_clk_set_rate(id, rate); if (r != -1) return r; if(DEBUG_MDNS) D("%s: id=%u rate=%lu\n", __func__, id, rate); retval = set_mdns_host_clock(id, rate); return retval; } static int pc_clk_set_min_rate(uint32_t id, unsigned long rate) { if (id < NR_CLKS) min_clk_rate[id]=rate; else if(debug_mask&DEBUG_UNKNOWN_ID) printk(KERN_WARNING " FIXME! clk_set_min_rate not implemented; %u:%lu NR_CLKS=%d\n", id, rate, NR_CLKS); return 0; } static int pc_clk_set_max_rate(uint32_t id, unsigned long rate) { if (id < NR_CLKS) max_clk_rate[id]=rate; else if(debug_mask&DEBUG_UNKNOWN_ID) printk(KERN_WARNING " FIXME! clk_set_min_rate not implemented; %u:%lu NR_CLKS=%d\n", id, rate, NR_CLKS); return 0; } static unsigned long pc_clk_get_rate(uint32_t id) { unsigned long rate = 0; switch (id) { /* known MD/NS clocks, MSM_CLK dump and arm/mach-msm/clock-7x30.c */ case SDC1_CLK: case SDC2_CLK: case SDC3_CLK: case SDC4_CLK: case UART1DM_CLK: case UART2DM_CLK: case USB_HS_CLK: case SDAC_CLK: case TV_DAC_CLK: case TV_ENC_CLK: case USB_OTG_CLK: rate = get_mdns_host_clock(id); break; case SDC1_PCLK: case SDC2_PCLK: case SDC3_PCLK: case SDC4_PCLK: rate = 64000000; /* g1 value */ break; default: //TODO: support all clocks if(debug_mask&DEBUG_UNKNOWN_ID) printk("%s: unknown clock: id=%u\n", __func__, id); rate = 0; } return rate; } static int pc_clk_set_flags(uint32_t id, unsigned long flags) { int r; r = new_clk_set_flags(id, flags); if (r != -1) return r; if(debug_mask&DEBUG_UNKNOWN_CMD) printk(KERN_WARNING "%s not implemented for clock: id=%u, flags=%lu\n", __func__, id, flags); return 0; } static int pc_clk_is_enabled(uint32_t id) { int is_enabled = 0; unsigned bit; uint32_t glbl; glbl = msm_clk_get_glbl(id); bit = msm_clk_enable_bit(id); if (bit > 0 && glbl>0) { is_enabled = (readl(glbl) & bit) != 0; } //XXX: is this necessary? if (id==SDC1_PCLK || id==SDC2_PCLK || id==SDC3_PCLK || id==SDC4_PCLK) is_enabled = 1; return is_enabled; } static int pc_pll_request(unsigned id, unsigned on) { if(debug_mask&DEBUG_UNKNOWN_CMD) printk(KERN_WARNING "%s not implemented for PLL=%u\n", __func__, id); return 0; } /* * Standard clock functions defined in include/linux/clk.h */ struct clk *clk_get(struct device *dev, const char *id) { struct clk *clk; mutex_lock(&clocks_mutex); list_for_each_entry(clk, &clocks, list) if (!strcmp(id, clk->name) && clk->dev == dev) goto found_it; list_for_each_entry(clk, &clocks, list) if (!strcmp(id, clk->name) && clk->dev == NULL) goto found_it; clk = ERR_PTR(-ENOENT); found_it: mutex_unlock(&clocks_mutex); return clk; } EXPORT_SYMBOL(clk_get); void clk_put(struct clk *clk) { } EXPORT_SYMBOL(clk_put); int clk_enable(struct clk *clk) { unsigned long flags; if (clk->id == ACPU_CLK) { return -ENOTSUPP; } spin_lock_irqsave(&clocks_lock, flags); clk->count++; if (clk->count == 1) pc_clk_enable(clk->id); spin_unlock_irqrestore(&clocks_lock, flags); return 0; } EXPORT_SYMBOL(clk_enable); void clk_disable(struct clk *clk) { unsigned long flags; spin_lock_irqsave(&clocks_lock, flags); BUG_ON(clk->count == 0); clk->count--; if (clk->count == 0) pc_clk_disable(clk->id); spin_unlock_irqrestore(&clocks_lock, flags); } EXPORT_SYMBOL(clk_disable); unsigned long clk_get_rate(struct clk *clk) { return pc_clk_get_rate(clk->id); } EXPORT_SYMBOL(clk_get_rate); int clk_set_rate(struct clk *clk, unsigned long rate) { int ret; if (clk->flags & CLKFLAG_USE_MAX_TO_SET) { ret = pc_clk_set_max_rate(clk->id, rate); if (ret) return ret; } if (clk->flags & CLKFLAG_USE_MIN_TO_SET) { ret = pc_clk_set_min_rate(clk->id, rate); if (ret) return ret; } if (clk->flags & CLKFLAG_USE_MAX_TO_SET || clk->flags & CLKFLAG_USE_MIN_TO_SET) return ret; return pc_clk_set_rate(clk->id, rate); } EXPORT_SYMBOL(clk_set_rate); int clk_set_parent(struct clk *clk, struct clk *parent) { return -ENOSYS; } EXPORT_SYMBOL(clk_set_parent); struct clk *clk_get_parent(struct clk *clk) { return ERR_PTR(-ENOSYS); } EXPORT_SYMBOL(clk_get_parent); int clk_set_flags(struct clk *clk, unsigned long flags) { if (clk == NULL || IS_ERR(clk)) return -EINVAL; return pc_clk_set_flags(clk->id, flags); } EXPORT_SYMBOL(clk_set_flags); void __init msm_clock_init(void) { struct clk *clk; spin_lock_init(&clocks_lock); mutex_lock(&clocks_mutex); for (clk = msm_clocks; clk && clk->name; clk++) { list_add_tail(&clk->list, &clocks); } mutex_unlock(&clocks_mutex); } void clk_enter_sleep(int from_idle) { } void clk_exit_sleep(void) { } int clks_print_running(void) { struct clk *clk; int clk_on_count = 0; char buf[100]; char *pbuf = buf; int size = sizeof(buf); int wr; unsigned long flags; spin_lock_irqsave(&clocks_lock, flags); list_for_each_entry(clk, &clocks, list) { if (clk->count) { clk_on_count++; wr = snprintf(pbuf, size, " %s", clk->name); if (wr >= size) break; pbuf += wr; size -= wr; } } if (clk_on_count) pr_info("clocks on:%s\n", buf); spin_unlock_irqrestore(&clocks_lock, flags); return !clk_on_count; } EXPORT_SYMBOL(clks_print_running); int clks_allow_tcxo_locked(void) { struct clk *clk; struct hlist_node *pos; unsigned long flags; spin_lock_irqsave(&clocks_lock, flags); list_for_each_entry(clk, &clocks, list) { if (clk->count) return 0; } spin_unlock_irqrestore(&clocks_lock, flags); return 1; } EXPORT_SYMBOL(clks_allow_tcxo_locked); int clks_allow_tcxo_locked_debug(void) { struct clk *clk; int clk_on_count = 0; unsigned long flags; spin_lock_irqsave(&clocks_lock, flags); list_for_each_entry(clk, &clocks, list) { if (clk->count) { pr_info("%s: '%s' not off.\n", __func__, clk->name); clk_on_count++; } } pr_info("%s: %d clks are on.\n", __func__, clk_on_count); spin_unlock_irqrestore(&clocks_lock, flags); return !clk_on_count; } EXPORT_SYMBOL(clks_allow_tcxo_locked_debug); /* The bootloader and/or AMSS may have left various clocks enabled. * Disable any clocks that belong to us (CLKFLAG_AUTO_OFF) but have * not been explicitly enabled by a clk_enable() call. */ static int __init clock_late_init(void) { unsigned long flags; struct clk *clk; unsigned count = 0; mutex_lock(&clocks_mutex); list_for_each_entry(clk, &clocks, list) { if (clk->flags & CLKFLAG_AUTO_OFF) { spin_lock_irqsave(&clocks_lock, flags); if (!clk->count && clk->id != MDP_CLK) { count++; pc_clk_disable(clk->id); } spin_unlock_irqrestore(&clocks_lock, flags); } } mutex_unlock(&clocks_mutex); pr_info("clock_late_init() disabled %d unused clocks\n", count); // reset imem config, I guess all devices need this so somewhere here would be good. // it needs to be moved to somewhere else. //writel( 0, MSM_IMEM_BASE ); // IMEM addresses have to ve checked and enabled //pr_info("reset imem_config\n"); return 0; } late_initcall(clock_late_init);