android_kernel_cmhtcleo/arch/arm/mach-msm/clock-wince.c
2010-10-12 14:55:49 +02:00

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);