-
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/mfd/core.h>
#include <linux/slab.h>
#include <linux/mfd/rk616.h>
-#include <linux/clk.h>
#include <mach/iomux.h>
#include <linux/err.h>
#include <linux/uaccess.h>
#include <linux/seq_file.h>
#endif
+#ifndef MHZ
+#define MHZ (1000*1000)
+#endif
static struct mfd_cell rk616_devs[] = {
{
ret = i2c_transfer(adap, &msg, 1);
kfree(tx_buf);
-
return (ret == 1) ? 4 : ret;
}
+static int rk616_i2c_write_bits(struct mfd_rk616 *rk616, u16 reg,u32 mask,u32 *pval)
+{
+
+ struct i2c_client *client = rk616->client;
+ struct i2c_adapter *adap = client->adapter;
+ struct i2c_msg msg;
+ int ret;
+ u32 reg_val;
+ char *tx_buf = NULL;
+
+ tx_buf = (char *)kmalloc(6, GFP_KERNEL);
+ if(!tx_buf)
+ return -ENOMEM;
+
+ mutex_lock(&rk616->reg_lock);
+ rk616->read_dev(rk616,reg,®_val);
+ reg_val &= ~mask;
+ *pval &= mask;
+ reg_val |= *pval;
+ *pval = reg_val;
+ memcpy(tx_buf, ®, 2);
+ memcpy(tx_buf+2, (char *)pval, 4);
+
+ msg.addr = client->addr;
+ msg.flags = client->flags;
+ msg.len = 6;
+ msg.buf = (char *)tx_buf;
+ msg.scl_rate = rk616->pdata->scl_rate;
+ msg.udelay = client->udelay;
+
+ ret = i2c_transfer(adap, &msg, 1);
+ kfree(tx_buf);
+ mutex_unlock(&rk616->reg_lock);
+
+ return (ret == 1) ? 4 : ret;
+}
#if defined(CONFIG_DEBUG_FS)
static int rk616_reg_show(struct seq_file *s, void *v)
{
for(i=0;i<= CRU_CFGMISC_CON;i+=4)
{
rk616->read_dev(rk616,i,&val);
- seq_printf(s,"0x%04x>>0x%08x\n",i,val);
+ if(i%16==0)
+ seq_printf(s,"\n0x%04x:",i);
+ seq_printf(s," %08x",val);
}
+ seq_printf(s,"\n");
return 0;
}
struct mfd_rk616 *rk616 = file->f_path.dentry->d_inode->i_private;
u32 reg;
u32 val;
+
char kbuf[25];
if (copy_from_user(kbuf, buf, count))
return -EFAULT;
sscanf(kbuf, "%x%x", ®,&val);
- dev_dbg(rk616->dev,"%s:reg:0x%04x val:0x%08x\n",__func__,reg,val);
rk616->write_dev(rk616,reg,&val);
return count;
}
};
#endif
+
+static u32 rk616_clk_gcd(u32 numerator, u32 denominator)
+{
+ u32 a, b;
+
+ if (!numerator || !denominator)
+ return 0;
+
+ if (numerator > denominator) {
+ a = numerator;
+ b = denominator;
+ } else {
+ a = denominator;
+ b = numerator;
+ }
+
+ while (b != 0) {
+ int r = b;
+ b = a % b;
+ a = r;
+ }
+
+ return a;
+}
+
+
+static int rk616_pll_par_calc(u32 fin_hz,u32 fout_hz,u32 *refdiv, u32 *fbdiv,
+ u32 *postdiv1, u32 *postdiv2, u32 *frac)
+{
+ // FIXME set postdiv1/2 always 1
+ u32 gcd;
+ u64 fin_64, frac_64;
+ u32 f_frac;
+ if(!fin_hz || !fout_hz)
+ return -EINVAL;
+
+ if(fin_hz / MHZ * MHZ == fin_hz && fout_hz /MHZ * MHZ == fout_hz)
+ {
+ fin_hz /= MHZ;
+ fout_hz /= MHZ;
+ gcd = rk616_clk_gcd(fin_hz, fout_hz);
+ *refdiv = fin_hz / gcd;
+ *fbdiv = fout_hz / gcd;
+ *postdiv1 = 1;
+ *postdiv2 = 1;
+
+ *frac = 0;
+
+ }
+ else
+ {
+
+ gcd = rk616_clk_gcd(fin_hz / MHZ, fout_hz / MHZ);
+ *refdiv = fin_hz / MHZ / gcd;
+ *fbdiv = fout_hz / MHZ / gcd;
+ *postdiv1 = 1;
+ *postdiv2 = 1;
+
+ *frac = 0;
+
+ f_frac = (fout_hz % MHZ);
+ fin_64 = fin_hz;
+ do_div(fin_64, (u64)*refdiv);
+ frac_64 = (u64)f_frac << 24;
+ do_div(frac_64, fin_64);
+ *frac = (u32) frac_64;
+ printk(KERN_INFO "frac_64=%llx, frac=%u\n", frac_64, *frac);
+ }
+ printk(KERN_INFO "fin=%u,fout=%u,gcd=%u,refdiv=%u,fbdiv=%u,postdiv1=%u,postdiv2=%u,frac=%u\n",
+ fin_hz, fout_hz, gcd, *refdiv, *fbdiv, *postdiv1, *postdiv2, *frac);
+ return 0;
+}
+
+
+static int rk616_pll_wait_lock(struct mfd_rk616 *rk616,int id)
+{
+ u32 delay = 10;
+ u32 val = 0;
+ int ret;
+ int offset;
+
+ if(id == 0) //PLL0
+ {
+ offset = 0;
+ }
+ else // PLL1
+ {
+ offset = 0x0c;
+ }
+ while (delay >= 1)
+ {
+ ret = rk616->read_dev(rk616,CRU_PLL0_CON1 + offset,&val);
+ if (val&PLL0_LOCK)
+ {
+ rk616_dbg(rk616->dev,"PLL%d locked\n",id);
+ break;
+ }
+ msleep(1);
+ delay--;
+ }
+ if (delay == 0)
+ {
+ dev_err(rk616->dev,"rk616 wait PLL%d lock time out!\n",id);
+ }
+
+ return 0;
+}
+
+
+
+int rk616_pll_pwr_down(struct mfd_rk616 *rk616,int id)
+{
+ u32 val = 0;
+ int ret;
+ int offset;
+ if(id == 0) //PLL0
+ {
+ offset = 0;
+ }
+ else // PLL1
+ {
+ offset = 0x0c;
+ }
+
+
+ val = PLL0_PWR_DN | (PLL0_PWR_DN << 16);
+ ret = rk616->write_dev(rk616,CRU_PLL0_CON1 + offset,&val);
+
+ return 0;
+
+}
+
+
+
+int rk616_pll_set_rate(struct mfd_rk616 *rk616,int id,u32 cfg_val,u32 frac)
+{
+ u32 val = 0;
+ int ret;
+ int offset;
+ u16 con0 = cfg_val & 0xffff;
+ u16 con1 = (cfg_val >> 16)&0xffff;
+ u32 fbdiv = con0 & 0xfff;
+ u32 postdiv1 = (con0 >> 12)&0x7;
+ u32 refdiv = con1 & 0x3f;
+ u32 postdiv2 = (con1 >> 6) & 0x7;
+ u8 mode = !frac;
+
+ if(id == 0) //PLL0
+ {
+ if(((rk616->pll0_rate >> 32) == cfg_val) &&
+ ((rk616->pll0_rate & 0xffffffff) == frac))
+ {
+ //return 0;
+ }
+ rk616->pll0_rate = ((u64)cfg_val << 32) | frac;
+ offset = 0;
+ }
+ else // PLL1
+ {
+ if(((rk616->pll1_rate >> 32) == cfg_val) &&
+ ((rk616->pll1_rate & 0xffffffff) == frac))
+ {
+ // return 0;
+ }
+ rk616->pll1_rate = ((u64)cfg_val << 32) | frac;
+ offset = 0x0c;
+ }
+
+
+ val = PLL0_PWR_DN | (PLL0_PWR_DN << 16);
+ ret = rk616->write_dev(rk616,CRU_PLL0_CON1 + offset,&val);
+
+
+ ret = rk616->read_dev(rk616,CRU_PLL0_CON2 + offset,&val);
+ val &= 0xff000000;
+ if(frac)
+ val |= PLL0_FRAC(frac);
+ else
+ val |= 0x800000; //default value
+ ret = rk616->write_dev(rk616,CRU_PLL0_CON2 + offset,&val);
+
+ val = PLL0_POSTDIV1(postdiv1) | PLL0_FBDIV(fbdiv) | PLL0_POSTDIV1_MASK |
+ PLL0_FBDIV_MASK | (PLL0_BYPASS << 16);
+ ret = rk616->write_dev(rk616,CRU_PLL0_CON0 + offset,&val);
+
+ val = PLL0_DIV_MODE(mode) | PLL0_POSTDIV2(postdiv2) | PLL0_REFDIV(refdiv) |
+ (PLL0_DIV_MODE_MASK) | PLL0_POSTDIV2_MASK | PLL0_REFDIV_MASK;
+ ret = rk616->write_dev(rk616,CRU_PLL0_CON1 + offset,&val);
+
+ val = (PLL0_PWR_DN << 16);
+ ret = rk616->write_dev(rk616,CRU_PLL0_CON1 + offset,&val);
+
+ rk616_pll_wait_lock(rk616,id);
+
+ msleep(5);
+
+ return 0;
+
+}
/***********************************
default clk patch settiing:
CLKIN-------->CODEC
u32 val = 0;
int ret;
- val = PLL1_CLK_SEL(1) | PLL0_CLK_SEL(0) | LCD1_CLK_DIV(0) | LCD0_CLK_DIV(0) |
- PLL1_CLK_SEL_MASK | PLL0_CLK_SEL_MASK | LCD1_CLK_DIV_MASK |
- LCD0_CLK_DIV_MASK; //pll1 clk from lcdc1_dclk,pll0 clk from lcdc0_dclk,mux_lcdx = lcdx_clk
+ val = PLL1_CLK_SEL(LCD1_DCLK) | PLL0_CLK_SEL(LCD0_DCLK) | LCD1_CLK_DIV(0) |
+ LCD0_CLK_DIV(0) | PLL1_CLK_SEL_MASK | PLL0_CLK_SEL_MASK |
+ LCD1_CLK_DIV_MASK | LCD0_CLK_DIV_MASK; //pll1 clk from lcdc1_dclk,pll0 clk from lcdc0_dclk,mux_lcdx = lcdx_clk
ret = rk616->write_dev(rk616,CRU_CLKSEL0_CON,&val);
- val = CODEC_MCLK_SEL(2) | CODEC_MCLK_SEL_MASK; //codec mclk from clkin
+ val = SCLK_SEL(SCLK_SEL_PLL1) | CODEC_MCLK_SEL(CODEC_MCLK_SEL_12M) |
+ CODEC_MCLK_SEL_MASK | SCLK_SEL_MASK; //codec mclk from clkin
ret = rk616->write_dev(rk616,CRU_CLKSEL1_CON,&val);
val = 0; //codec mck = clkin
val = (PLL0_BYPASS) | (PLL0_BYPASS << 16); //bypass pll0
ret = rk616->write_dev(rk616,CRU_PLL0_CON0,&val);
+ val = PLL0_PWR_DN | (PLL0_PWR_DN << 16);
+ ret = rk616->write_dev(rk616,CRU_PLL0_CON1,&val); //power down pll0
val = (PLL1_BYPASS) | (PLL1_BYPASS << 16);
ret = rk616->write_dev(rk616,CRU_PLL1_CON0,&val);
+
return 0;
}
+
+static int rk616_core_suspend(struct device *dev, pm_message_t state)
+{
+ return 0;
+}
+
+static int rk616_core_resume(struct device* dev)
+{
+ struct mfd_rk616 *rk616 = dev_get_drvdata(dev);
+ rk616_clk_common_init(rk616);
+ return 0;
+}
static int rk616_i2c_probe(struct i2c_client *client,const struct i2c_device_id *id)
{
int ret;
i2c_set_clientdata(client, rk616);
dev_set_drvdata(rk616->dev,rk616);
- if(rk616->pdata->power_init)
- rk616->pdata->power_init();
-
#if defined(CONFIG_SND_RK29_SOC_I2S_8CH)
iis_clk = clk_get_sys("rk29_i2s.0", "i2s");
#elif defined(CONFIG_SND_RK29_SOC_I2S_2CH)
}
else
{
+ rk616->mclk = iis_clk;
+
#if defined(CONFIG_ARCH_RK29)
rk29_mux_api_set(GPIO2D0_I2S0CLK_MIIRXCLKIN_NAME, GPIO2H_I2S0_CLK);
#else
#endif
clk_enable(iis_clk);
clk_set_rate(iis_clk, 11289600);
- clk_put(iis_clk);
+ //clk_put(iis_clk);
}
+
+ mutex_init(&rk616->reg_lock);
+
+ if(rk616->pdata->power_init)
+ rk616->pdata->power_init();
+
rk616->read_dev = rk616_i2c_read_reg;
rk616->write_dev = rk616_i2c_write_reg;
+ rk616->write_dev_bits = rk616_i2c_write_bits;
+
#if defined(CONFIG_DEBUG_FS)
rk616->debugfs_dir = debugfs_create_dir("rk616", NULL);
if (IS_ERR(rk616->debugfs_dir))
dev_err(rk616->dev,"failed to create debugfs dir for rk616!\n");
}
else
- debugfs_create_file("rk616-reg", S_IRUSR,rk616->debugfs_dir,rk616,&rk616_reg_fops);
+ debugfs_create_file("core", S_IRUSR,rk616->debugfs_dir,rk616,&rk616_reg_fops);
#endif
rk616_clk_common_init(rk616);
ret = mfd_add_devices(rk616->dev, -1,
rk616_devs, ARRAY_SIZE(rk616_devs),
NULL, rk616->irq_base);
-
dev_info(&client->dev,"rk616 core probe success!\n");
return 0;
}
return 0;
}
+static void rk616_core_shutdown(struct i2c_client *client)
+{
+ struct mfd_rk616 *rk616 = i2c_get_clientdata(client);
+ if(rk616->pdata->power_deinit)
+ rk616->pdata->power_deinit();
+}
+
+
static const struct i2c_device_id id_table[] = {
{"rk616", 0 },
{ }
.driver = {
.name = "rk616",
.owner = THIS_MODULE,
+ .suspend = &rk616_core_suspend,
+ .resume = &rk616_core_resume,
},
.probe = &rk616_i2c_probe,
.remove = &rk616_i2c_remove,
+ .shutdown = &rk616_core_shutdown,
.id_table = id_table,
};