TERES/SOFTWARE/A64-TERES/linux-a64/drivers/mmc/host/sunxi-mmc-sun8iw10p1-1.c
Dimitar Gamishev f9b0e7a283 linux
2017-10-13 14:07:04 +03:00

600 lines
15 KiB
C

#ifdef CONFIG_ARCH_SUN8IW10P1
#include <linux/clk.h>
#include <linux/clk-private.h>
#include <linux/clk/sunxi.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/scatterlist.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/reset.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/mmc/host.h>
#include <linux/mmc/sd.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/core.h>
#include <linux/mmc/card.h>
#include <linux/mmc/slot-gpio.h>
#include "sunxi-mmc.h"
#include "sunxi-mmc-sun8iw10p1-1.h"
//reg
#define SDXC_REG_EDSD (0x010C) /*SMHC eMMC4.5 DDR Start Bit Detection Control Register*/
#define SDXC_REG_CSDC (0x0054) /*SMHC CRC Status Detect Control Register*/
#define SDXC_REG_THLD (0x0100) /*SMHC Card Threshold Control Register*/
#define SDXC_REG_DRV_DL (0x0140) /*SMHC Drive Delay Control Register*/
#define SDXC_REG_SAMP_DL (0x0144) /*SMHC Sample Delay Control Register*/
#define SDXC_REG_DS_DL (0x0148) /*SMHC Data Strobe Delay Control Register*/
#define SDXC_REG_SD_NTSR (0x005C) /*SMHC NewTiming Set Register*/
//bit
#define SDXC_HS400_MD_EN (1U<<31)
#define SDXC_CARD_WR_THLD_ENB (1U<<2)
#define SDXC_CARD_RD_THLD_ENB (1U)
#define SDXC_DAT_DRV_PH_SEL (1U<<17)
#define SDXC_CMD_DRV_PH_SEL (1U<<16)
#define SDXC_SAMP_DL_SW_EN (1u<<7)
#define SDXC_DS_DL_SW_EN (1u<<7)
#define SDXC_2X_TIMING_MODE (1U<<31)
//mask
#define SDXC_CRC_DET_PARA_MASK (0xf)
#define SDXC_CARD_RD_THLD_MASK (0x0FFF0000)
#define SDXC_TX_TL_MASK (0xff)
#define SDXC_RX_TL_MASK (0x00FF0000)
#define SDXC_SAMP_DL_SW_MASK (0x0000003F)
#define SDXC_DS_DL_SW_MASK (0x0000003F)
#define SDXC_SAM_TIMING_PH_MASK (0x00000030)
//value
#define SDXC_CRC_DET_PARA_HS400 (6)
#define SDXC_CRC_DET_PARA_OTHER (3)
#define SDXC_FIFO_DETH (1024>>2)
//size
#define SDXC_CARD_RD_THLD_SIZE (0x00000FFF)
//shit
#define SDXC_CARD_RD_THLD_SIZE_SHIFT (16)
#define SDXC_SAM_TIMING_PH_SHIFT (4)
enum sunxi_mmc_clk_mode
{
mmc_clk_400k = 0,
mmc_clk_26M,
mmc_clk_52M,
mmc_clk_52M_DDR4,
mmc_clk_52M_DDR8,
mmc_clk_104M,
mmc_clk_208M,
mmc_clk_104M_DDR,
mmc_clk_208M_DDR,
mmc_clk_mod_num,
};
struct sunxi_mmc_clk_dly {
enum sunxi_mmc_clk_mode cmod;
char *mod_str;
u32 cmd_drv_ph;
u32 dat_drv_ph;
u32 sam_dly;
u32 ds_dly;
u32 sam_ph;
};
/*sample delay and output deley setting*/
static struct sunxi_mmc_clk_dly mmc_clk_dly[mmc_clk_mod_num] = {
[mmc_clk_400k] = {
.cmod = mmc_clk_400k,
.mod_str = "sunxi-dly-400k",
.cmd_drv_ph = 1,
.dat_drv_ph = 0,
.sam_dly = 0,
.ds_dly = 0,
.sam_ph = 0,
},
[mmc_clk_26M] = {
.cmod = mmc_clk_26M,
.mod_str = "sunxi-dly-26M",
.cmd_drv_ph = 1,
.dat_drv_ph = 0,
.sam_dly = 0,
.ds_dly = 0,
.sam_ph = 0,
},
[mmc_clk_52M] = {
.cmod = mmc_clk_52M,
.mod_str = "sunxi-dly-52M",
.cmd_drv_ph = 1,
.dat_drv_ph = 0,
.sam_dly = 0,
.ds_dly = 0,
.sam_ph = 0,
},
[mmc_clk_52M_DDR4] = {
.cmod = mmc_clk_52M_DDR4,
.mod_str = "sunxi-dly-52M-ddr4",
.cmd_drv_ph = 1,
.dat_drv_ph = 0,
.sam_dly = 0,
.ds_dly = 0,
.sam_ph = 0,
},
[mmc_clk_52M_DDR8] = {
.cmod = mmc_clk_52M_DDR8,
.mod_str = "sunxi-dly-52M-ddr8",
.cmd_drv_ph = 1,
.dat_drv_ph = 0,
.sam_dly = 0,
.ds_dly = 0,
.sam_ph = 0,
},
[mmc_clk_104M] = {
.cmod = mmc_clk_104M,
.mod_str = "sunxi-dly-104M",
.cmd_drv_ph = 1,
.dat_drv_ph = 0,
.sam_dly = 0,
.ds_dly = 0,
.sam_ph = 0,
},
[mmc_clk_208M] = {
.cmod = mmc_clk_208M,
.mod_str = "sunxi-dly-208M",
.cmd_drv_ph = 1,
.dat_drv_ph = 0,
.sam_dly = 0,
.ds_dly = 0,
.sam_ph = 0,
},
[mmc_clk_104M_DDR] = {
.cmod = mmc_clk_104M_DDR,
.mod_str = "sunxi-dly-104M-ddr",
.cmd_drv_ph = 1,
.dat_drv_ph = 0,
.sam_dly = 0,
.ds_dly = 0,
.sam_ph = 0,
},
[mmc_clk_208M_DDR] = {
.cmod = mmc_clk_208M_DDR,
.mod_str = "sunxi-dly-208M-ddr",
.cmd_drv_ph = 1,
.dat_drv_ph = 0,
.sam_dly = 0,
.ds_dly = 0,
.sam_ph = 0,
},
};
struct sunxi_mmc_spec_regs {
u32 drv_dl;//REG_DRV_DL
u32 samp_dl;//REG_SAMP_DL
u32 ds_dl;//REG_DS_DL
u32 sd_ntsr;//REG_SD_NTSR
};
static struct sunxi_mmc_spec_regs bak_spec_regs;
/*
static int sunxi_of_parse_clk_dly(struct sunxi_mmc_host *host)
{
struct device_node *np;
struct mmc_host *mhost = host->mmc;
int i = 0;
u32 in_clk_dly[4] = {0};
int ret = 0;
if (!mhost->parent || !mhost->parent->of_node){
dev_err(mmc_dev(host->mmc), "no dts to parse clk dly\n");
return -EINVAL;
}
np = mhost->parent->of_node;
for(i=0;i<mmc_clk_mod_num;i++){
ret = of_property_read_u32_array(np,mmc_clk_dly[i].mod_str,\
in_clk_dly,ARRAY_SIZE(in_clk_dly));
if(ret){
dev_info(mmc_dev(host->mmc),"faild to get %s\n",mmc_clk_dly[i].mod_str);
}else{
mmc_clk_dly[i].cmd_drv_ph = in_clk_dly[0];
mmc_clk_dly[i].dat_drv_ph = in_clk_dly[1];
mmc_clk_dly[i].sam_dly = in_clk_dly[2];
mmc_clk_dly[i].ds_dly = in_clk_dly[3];
dev_info(mmc_dev(host->mmc),"Get %s clk dly ok\n",mmc_clk_dly[i].mod_str);
dev_info(mmc_dev(host->mmc),"cmd_drv_ph %d\n",mmc_clk_dly[i].cmd_drv_ph);
dev_info(mmc_dev(host->mmc),"dat_drv_ph %d\n",mmc_clk_dly[i].dat_drv_ph);
dev_info(mmc_dev(host->mmc),"sam_dly %d\n",mmc_clk_dly[i].sam_dly);
dev_info(mmc_dev(host->mmc),"ds_dly %d\n",mmc_clk_dly[i].ds_dly);
}
}
return 0;
}
*/
static void sunxi_mmc_set_clk_dly(struct sunxi_mmc_host *host,int clk,int bus_width,int timing)
{
struct mmc_host *mhost = host->mmc;
u32 rval = 0;
enum sunxi_mmc_clk_mode cmod = mmc_clk_400k;
u32 in_clk_dly[5] = {0};
int ret = 0;
struct device_node *np = NULL;
if (!mhost->parent || !mhost->parent->of_node){
dev_err(mmc_dev(host->mmc), "no dts to parse clk dly,use default\n");
return;
}
np = mhost->parent->of_node;
if(clk <= 400*1000 ){
cmod = mmc_clk_400k;
}else if(clk <= 26*1000*1000){
cmod = mmc_clk_26M;
}else if(clk <= 52*1000*1000){
if((bus_width == MMC_BUS_WIDTH_4)&&(timing == MMC_TIMING_UHS_DDR50)){
cmod = mmc_clk_52M_DDR4;
}else if((bus_width == MMC_BUS_WIDTH_8)&&(timing == MMC_TIMING_UHS_DDR50)){
cmod = mmc_clk_52M_DDR8;
}else{
cmod = mmc_clk_52M;
}
}else if(clk <= 104*1000*1000){
if((bus_width == MMC_BUS_WIDTH_8)&&(timing == MMC_TIMING_MMC_HS400)){
cmod = mmc_clk_104M_DDR;
}else{
cmod = mmc_clk_104M;
}
}else if(clk <= 208*1000*1000){
if((bus_width == MMC_BUS_WIDTH_8)&&(timing == MMC_TIMING_MMC_HS400)){
cmod = mmc_clk_208M_DDR;
}else{
cmod = mmc_clk_208M;
}
}else{
dev_err(mmc_dev(mhost),"clk %d is out of range\n",clk);
return;
}
ret = of_property_read_u32_array(np,mmc_clk_dly[cmod].mod_str,\
in_clk_dly,ARRAY_SIZE(in_clk_dly));
if(ret){
dev_dbg(mmc_dev(host->mmc),"faild to get %s used default\n",mmc_clk_dly[cmod].mod_str);
}else{
mmc_clk_dly[cmod].cmd_drv_ph = in_clk_dly[0];
mmc_clk_dly[cmod].dat_drv_ph = in_clk_dly[1];
//mmc_clk_dly[cmod].sam_dly = in_clk_dly[2];
//mmc_clk_dly[cmod].ds_dly = in_clk_dly[3];
mmc_clk_dly[cmod].sam_ph = in_clk_dly[4];
dev_dbg(mmc_dev(host->mmc),"Get %s clk dly ok\n",mmc_clk_dly[cmod].mod_str);
}
dev_dbg(mmc_dev(host->mmc),"Try set %s clk dly ok\n",mmc_clk_dly[cmod].mod_str);
dev_dbg(mmc_dev(host->mmc),"cmd_drv_ph %d\n",mmc_clk_dly[cmod].cmd_drv_ph);
dev_dbg(mmc_dev(host->mmc),"dat_drv_ph %d\n",mmc_clk_dly[cmod].dat_drv_ph);
//dev_dbg(mmc_dev(host->mmc),"sam_dly %d\n",mmc_clk_dly[cmod].sam_dly);
//dev_dbg(mmc_dev(host->mmc),"ds_dly %d\n",mmc_clk_dly[cmod].ds_dly);
dev_dbg(mmc_dev(host->mmc),"sam_ph %d\n",mmc_clk_dly[cmod].sam_ph);
rval = mmc_readl(host,REG_DRV_DL);
if(mmc_clk_dly[cmod].cmd_drv_ph){
rval |= SDXC_CMD_DRV_PH_SEL;//180 phase
}else{
rval &= ~SDXC_CMD_DRV_PH_SEL;//90 phase
}
if(mmc_clk_dly[cmod].dat_drv_ph){
rval |= SDXC_DAT_DRV_PH_SEL;//180 phase
}else{
rval &= ~SDXC_DAT_DRV_PH_SEL;//90 phase
}
mmc_writel(host,REG_DRV_DL,rval);
// rval = mmc_readl(host,REG_SAMP_DL);
// rval &= ~SDXC_SAMP_DL_SW_MASK;
// rval |= mmc_clk_dly[cmod].sam_dly & SDXC_SAMP_DL_SW_MASK;
// rval |= SDXC_SAMP_DL_SW_EN;
// mmc_writel(host,REG_SAMP_DL,rval);
// rval = mmc_readl(host,REG_DS_DL);
// rval &= ~SDXC_DS_DL_SW_MASK;
// rval |= mmc_clk_dly[cmod].ds_dly & SDXC_DS_DL_SW_MASK;
// rval |= SDXC_DS_DL_SW_EN;
// mmc_writel(host,REG_DS_DL,rval);
rval = mmc_readl(host,REG_SD_NTSR);
rval &= ~SDXC_SAM_TIMING_PH_MASK;
rval |= (mmc_clk_dly[cmod].sam_ph << SDXC_SAM_TIMING_PH_SHIFT) & SDXC_SAM_TIMING_PH_MASK;
mmc_writel(host,REG_SD_NTSR,rval);
dev_dbg(mmc_dev(host->mmc)," REG_DRV_DL %08x\n",mmc_readl(host,REG_DRV_DL));
dev_dbg(mmc_dev(host->mmc)," REG_SAMP_DL %08x\n",mmc_readl(host,REG_SAMP_DL));
dev_dbg(mmc_dev(host->mmc)," REG_DS_DL %08x\n",mmc_readl(host,REG_DS_DL));
}
static int __sunxi_mmc_do_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en,u32 pwr_save,u32 ignore_dat0)
{
unsigned long expire = jiffies + msecs_to_jiffies(250);
u32 rval;
rval = mmc_readl(host, REG_CLKCR);
rval &= ~(SDXC_CARD_CLOCK_ON | SDXC_LOW_POWER_ON | SDXC_MASK_DATA0);
if (oclk_en)
rval |= SDXC_CARD_CLOCK_ON;
if(pwr_save)
rval |= SDXC_LOW_POWER_ON;
if(ignore_dat0)
rval |= SDXC_MASK_DATA0;
mmc_writel(host, REG_CLKCR, rval);
dev_dbg(mmc_dev(host->mmc), "%s REG_CLKCR:%x\n",__FUNCTION__,mmc_readl(host,REG_CLKCR));
rval = SDXC_START | SDXC_UPCLK_ONLY | SDXC_WAIT_PRE_OVER;
mmc_writel(host, REG_CMDR, rval);
do {
rval = mmc_readl(host, REG_CMDR);
} while (time_before(jiffies, expire) && (rval & SDXC_START));
/* clear irq status bits set by the command */
mmc_writel(host, REG_RINTR,
mmc_readl(host, REG_RINTR) & ~SDXC_SDIO_INTERRUPT);//????
if (rval & SDXC_START) {
dev_err(mmc_dev(host->mmc), "fatal err update clk timeout\n");
return -EIO;
}
/*only use mask data0 when update clk,clear it when not update clk*/
if(ignore_dat0)
mmc_writel(host, REG_CLKCR,
mmc_readl(host, REG_CLKCR)&~SDXC_MASK_DATA0);
return 0;
}
static int sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en)
{
struct device_node *np = NULL;
struct mmc_host *mmc = host->mmc;
int pwr_save = 0;
int len = 0;
if (!mmc->parent || !mmc->parent->of_node){
dev_err(mmc_dev(host->mmc), "no dts to parse power save mode\n");
return -EIO; ;
}
np = mmc->parent->of_node;
if (of_find_property(np, "sunxi-power-save-mode", &len))
pwr_save = 1;
return __sunxi_mmc_do_oclk_onoff(host,oclk_en,pwr_save,1);
}
static void sunxi_mmc_2xmod_onoff(struct sunxi_mmc_host* host, u32 newmode_en)
{
u32 rval = mmc_readl(host, REG_SD_NTSR);
if(newmode_en){
rval |= SDXC_2X_TIMING_MODE;
}else{
rval &= ~SDXC_2X_TIMING_MODE;
}
mmc_writel(host, REG_SD_NTSR, rval);
dev_dbg(mmc_dev(host->mmc), "REG_SD_NTSR: 0x%08x ,val %x\n", mmc_readl(host, REG_SD_NTSR),rval);
}
int sunxi_mmc_clk_set_rate_for_sdmmc1(struct sunxi_mmc_host *host,
struct mmc_ios *ios)
{
u32 mod_clk = 0;
u32 src_clk = 0;
u32 rval = 0;
s32 err = 0;
u32 rate = 0;
char *sclk_name = NULL;
struct clk *mclk = host->clk_mmc;
struct clk *sclk = NULL;
struct device *dev = mmc_dev(host->mmc);
int div = 0;
if(ios->clock == 0){
__sunxi_mmc_do_oclk_onoff(host, 0,0,1);
return 0;
}
if(ios->timing == MMC_TIMING_UHS_DDR50){
mod_clk = ios->clock<<2;
div = 1;
}else{
mod_clk = ios->clock<<1;
div = 0;
}
if (ios->clock<= 400000) {
//sclk = of_clk_get(np, 0);
sclk = clk_get(dev,"osc24m");
sclk_name = "osc24m";
} else {
//sclk = clk_get(np, 1);
sclk = clk_get(dev,"pll_periph");
sclk_name = "pll_periph";
}
if (IS_ERR(sclk)) {
dev_err(mmc_dev(host->mmc), "Error to get source clock %s\n",sclk_name );
return -1;
}
sunxi_mmc_oclk_onoff(host, 0);
err = clk_set_parent(mclk, sclk);
if(err){
dev_err(mmc_dev(host->mmc), "set parent failed\n");
clk_put(sclk);
return -1;
}
rate = clk_round_rate(mclk, mod_clk);
dev_dbg(mmc_dev(host->mmc),"get round rate %d\n", rate);
clk_disable_unprepare(host->clk_mmc);
err = clk_set_rate(mclk, rate);
if (err) {
dev_err(mmc_dev(host->mmc),"set mclk rate error, rate %dHz\n",rate);
clk_put(sclk);
return -1;
}
rval = clk_prepare_enable(host->clk_mmc);
if (rval) {
dev_err(mmc_dev(host->mmc), "Enable mmc clk err %d\n", rval);
return -1;
}
src_clk = clk_get_rate(sclk);
clk_put(sclk);
dev_dbg(mmc_dev(host->mmc),"set round clock %d, soure clk is %d\n", rate, src_clk);
#ifdef MMC_FPGA
if(ios->timing == MMC_TIMING_UHS_DDR50){
/* clear internal divider */
rval = mmc_readl(host, REG_CLKCR);
rval &= ~0xff;
rval |= 1;
}else{
/* support internal divide clock under fpga environment */
rval = mmc_readl(host, REG_CLKCR);
rval &= ~0xff;
rval |= 24000000 / mod_clk / 2; // =24M/400K/2=0x1E
}
mmc_writel(host, REG_CLKCR, rval);
dev_info(mmc_dev(host->mmc), "--FPGA REG_CLKCR: 0x%08x \n", mmc_readl(host, REG_CLKCR));
#else
/* clear internal divider */
rval = mmc_readl(host, REG_CLKCR);
rval &= ~0xff;
rval |= div;
mmc_writel(host, REG_CLKCR, rval);
#endif
//sunxi_of_parse_clk_dly(host);
sunxi_mmc_2xmod_onoff(host,1);
if(ios->timing == MMC_TIMING_UHS_DDR50){
ios->clock = rate>>2;
}else{
ios->clock = rate>>1;
}
sunxi_mmc_set_clk_dly(host,ios->clock,ios->bus_width,ios->timing);
return sunxi_mmc_oclk_onoff(host, 1);
}
void sunxi_mmc_thld_ctl_for_sdmmc1(struct sunxi_mmc_host *host,
struct mmc_ios *ios, struct mmc_data *data)
{
u32 bsz = data->blksz;
//u32 tdtl = (host->dma_tl & SDXC_TX_TL_MASK)<<2; //unit:byte
u32 rdtl = ((host->dma_tl & SDXC_RX_TL_MASK)>>16)<<2;//unit:byte
u32 rval = 0;
if( (data->flags & MMC_DATA_READ)
&& (bsz <= SDXC_CARD_RD_THLD_SIZE)
&& ((SDXC_FIFO_DETH<<2) >= (rdtl+bsz)) //((SDXC_FIFO_DETH<<2)-bsz) >= (rdtl)
&& ((ios->timing == MMC_TIMING_MMC_HS200)\
||(ios->timing == MMC_TIMING_UHS_SDR50)\
||(ios->timing == MMC_TIMING_UHS_SDR104)) ){
rval = mmc_readl(host,REG_THLD);
rval &= ~SDXC_CARD_RD_THLD_MASK;
rval |= data->blksz<<SDXC_CARD_RD_THLD_SIZE_SHIFT;
rval |= SDXC_CARD_RD_THLD_ENB;
mmc_writel(host,REG_THLD,rval);
}else{
rval = mmc_readl(host,REG_THLD);
rval &= ~SDXC_CARD_RD_THLD_ENB;
mmc_writel(host,REG_THLD,rval);
}
dev_dbg(mmc_dev(host->mmc), "--SDXC_REG_THLD: 0x%08x \n", mmc_readl(host, REG_THLD));
}
void sunxi_mmc_save_spec_reg1(struct sunxi_mmc_host *host)
{
bak_spec_regs.drv_dl = mmc_readl(host,REG_DRV_DL);
bak_spec_regs.samp_dl = mmc_readl(host,REG_SAMP_DL);
bak_spec_regs.ds_dl = mmc_readl(host,REG_DS_DL);
bak_spec_regs.sd_ntsr = mmc_readl(host,REG_SD_NTSR);
}
void sunxi_mmc_restore_spec_reg1(struct sunxi_mmc_host *host)
{
mmc_writel(host,REG_DRV_DL,bak_spec_regs.drv_dl);
mmc_writel(host,REG_SAMP_DL,bak_spec_regs.samp_dl);
mmc_writel(host,REG_DS_DL,bak_spec_regs.ds_dl);
mmc_writel(host,REG_SD_NTSR,bak_spec_regs.sd_ntsr);
}
#endif