1296 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1296 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* 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 <linux/version.h>
 | |
| #include <linux/kernel.h>
 | |
| #include <linux/init.h>
 | |
| #include <linux/module.h>
 | |
| #include <linux/list.h>
 | |
| #include <linux/err.h>
 | |
| #include <linux/clk.h>
 | |
| #include <linux/delay.h>
 | |
| #include <linux/spinlock.h>
 | |
| 
 | |
| #include <mach/msm_iomap.h>
 | |
| #include <asm/io.h>
 | |
| 
 | |
| #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;
 | |
| }
 | |
| 
 | |
| // Cotullaz "new" clock functions
 | |
| 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;
 | |
| 
 | |
| // Cotulla: I am too lazy...
 | |
| #define MHZ(x)  ((x) * 1000 * 1000)
 | |
| #define KHZ(x)  ((x) * 1000)
 | |
| 
 | |
|     case SDC1_CLK:
 | |
| 		if (rate > MHZ(50)) speed = 14;
 | |
|         else if (rate > KHZ(49152)) speed = 13;
 | |
|         else if (rate > MHZ(45)) speed = 12;
 | |
|         else if (rate > MHZ(40)) speed = 11;
 | |
|         else if (rate > MHZ(35)) speed = 10;
 | |
|         else if (rate > MHZ(30)) speed = 9;
 | |
|         else if (rate > MHZ(25)) speed = 8;
 | |
|         else if (rate > MHZ(20)) speed = 7;
 | |
|         else if (rate > MHZ(15)) speed = 6;
 | |
|         else if (rate > MHZ(10)) speed = 5;
 | |
|         else if (rate > MHZ(5))  speed = 4;
 | |
|         else if (rate > KHZ(400))speed = 3;
 | |
|         else if (rate > KHZ(144))speed = 2;
 | |
|         else speed = 1;
 | |
|         clk = 66;
 | |
|         break;
 | |
|     case SDC2_CLK:
 | |
| 		if (rate > MHZ(50)) speed = 14;
 | |
|         else if (rate > KHZ(49152)) speed = 13;
 | |
|         else if (rate > MHZ(45)) speed = 12;
 | |
|         else if (rate > MHZ(40)) speed = 11;
 | |
|         else if (rate > MHZ(35)) speed = 10;
 | |
|         else if (rate > MHZ(30)) speed = 9;
 | |
|         else if (rate > MHZ(25)) speed = 8;
 | |
|         else if (rate > MHZ(20)) speed = 7;
 | |
|         else if (rate > MHZ(15)) speed = 6;
 | |
|         else if (rate > MHZ(10)) speed = 5;
 | |
|         else if (rate > MHZ(5))  speed = 4;
 | |
|         else if (rate > KHZ(400))speed = 3;
 | |
|         else if (rate > KHZ(144))speed = 2;
 | |
|         else speed = 1;
 | |
|         clk = 67;
 | |
|         break;
 | |
| 
 | |
| #undef MHZ
 | |
| #undef KHZ
 | |
| 
 | |
|         // both none
 | |
|     case SDC1_PCLK:
 | |
|     case SDC2_PCLK:
 | |
|         return 0;
 | |
|         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 SDC1_PCLK:
 | |
|         clk = 17;
 | |
|         break;
 | |
|     case SDC2_PCLK:
 | |
|         clk = 16;
 | |
|         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 SDC1_PCLK:
 | |
|         clk = 17;
 | |
|         break;
 | |
|     case SDC2_PCLK:
 | |
|         clk = 16;
 | |
|         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 long new_clk_get_rate(uint32_t id)
 | |
| {
 | |
|     unsigned clk = -1;
 | |
|     unsigned rate;
 | |
|     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 SDC1_PCLK:
 | |
|         clk = 17;
 | |
|         break;
 | |
|     case SDC2_PCLK:
 | |
|         clk = 16;
 | |
|         break;
 | |
| 
 | |
|     default:
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     msm_proc_comm(PCOM_CLK_REGIME_SEC_MSM_GET_CLK_FREQ_KHZ, &clk, &rate);
 | |
|     return clk*1000;
 | |
| }
 | |
| 
 | |
| 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;
 | |
| 
 | |
| 	rate = new_clk_get_rate(id);
 | |
| 
 | |
| 	if(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);
 |