diff -drupN a/drivers/mmc/host/sunxi-mmc-panic.c b/drivers/mmc/host/sunxi-mmc-panic.c --- a/drivers/mmc/host/sunxi-mmc-panic.c 1970-01-01 03:00:00.000000000 +0300 +++ b/drivers/mmc/host/sunxi-mmc-panic.c 2022-06-12 05:28:14.000000000 +0300 @@ -0,0 +1,1078 @@ +/* +* Sunxi SD/MMC panic host driver +* +* Copyright (C) 2019 AllWinnertech Ltd. +* Author: lixiang +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* This program is distributed "as is" WITHOUT ANY WARRANTY of any +* kind, whether express or implied; without even the implied warranty +* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "sunxi-mmc.h" +#include "sunxi-mmc-export.h" + + +#ifdef CONFIG_SUNXI_PANICPART +#include +#endif + +#define NCAT +//#define MMC_DEBUG +#define SUNXI_TEST_SIZE (512*4) + +#define MWR_TO_NS (250*1000*1000) +#define MWR_RFAIL -1 +#define MWR_ROK 0 + +#if 0 +#define mmc_mreadl(reg_base, reg) \ + ({\ + int val = readl(reg_base + SDXC_##reg);\ + printk("%x\n", val);\ + val;\ + }) +#define mmc_mwritel(reg_base, reg, value) \ +({\ + int val = 0;\ + writel((value), reg_base + SDXC_##reg);\ + val = readl(reg_base + SDXC_##reg);\ + printk("%x\n", val);\ + val;\ +}) + +#else + +#define mmc_mreadl(reg_base, reg) \ + ({\ + int val = readl(reg_base + SDXC_##reg);\ + /*printk("%x\n", val);*/\ + val;\ + }) +#define mmc_mwritel(reg_base, reg, value) \ +({\ + int val = 0;\ + writel((value), reg_base + SDXC_##reg);\ + /*val = readl(reg_base + SDXC_##reg);*/\ + /*printk("%x\n", val);*/\ + val;\ +}) +#endif + + + +#ifndef NCAT +#define SUNXI_SMHC_BASE 0x1c11000 +#define SUNXI_CCMU_BASE 0x1c20000 +#else +#define SUNXI_SMHC_BASE 0x4022000 +#define SUNXI_CCMU_BASE 0x3001000 +#endif + + +#define SDXC_REG_EDSD (0x010C) +#define SDXC_REG_CSDC (0x0054) +#define SDXC_REG_SD_NTSR (0x005C) +#define SDXC_REG_THLD (0x0100) +#define SDXC_REG_DRV_DL (0x0140) +#define SDXC_REG_SAMP_DL (0x0144) +#define SDXC_REG_DS_DL (0x0148) + + +#define SDXC_DAT_STARV_ERR SDXC_VOLTAGE_CHANGE_DONE + + +#ifndef NCAT +#define SUNXI_MMC_GATR (0x60) +#define SUNXI_MMC_MODR (0x90) +#define SUNXI_MMC_RST (0x2C0) +#else +#define SUNXI_MMC_GATR (0x84c) +#define SUNXI_MMC_MODR (0x838) +#define SUNXI_MMC_RST (0x84c) +#endif + + +#ifdef MMC_DEBUG +#define mmcinfo(fmt...) printk(KERN_INFO "[mmc]: "fmt) +#define mmcdbg(fmt...) printk(KERN_DEBUG "[mmc]: "fmt) +#define mmcerr(fmt...) printk(KERN_ERR "[mmc]: "fmt) +#else +#define mmcinfo(fmt...) printk(KERN_INFO "[mmc]: "fmt) +#define mmcdbg(fmt...) +#define mmcerr(fmt...) printk(KERN_ERR "[mmc]: "fmt) +#endif + + + +struct sunxi_mmc_mbak_regs { + u32 gctrl; + u32 clkc; + u32 timeout; + u32 buswid; + u32 waterlvl; + u32 funcsel; + u32 debugc; + u32 idmacc; + u32 dlba; + u32 imask; + u32 drv_dl; + u32 samp_dl; + u32 ds_dl; + u32 edsd; + u32 csdc; + u32 sd_ntsr; +}; + +static struct sunxi_mmc_mbak_regs gmbak_regs; +static char *gccmu_base_reg; +static char *ghost_base_reg; + +#ifdef CONFIG_SUNXI_PANICPART +static int init_cnt; +#endif + +#ifdef NCAT +static void sunxi_mmc_mbusrst_host(char *host) +{ + char *ccmu_reg = gccmu_base_reg; + + u32 rval = 0; + rval = readl(ccmu_reg + SUNXI_MMC_GATR); + rval &= ~((1u<<2)|(1u<<18)); + writel(rval, ccmu_reg + SUNXI_MMC_GATR); + + rval = readl(ccmu_reg + SUNXI_MMC_MODR); + rval &= ~((1<<31)); + writel(rval, ccmu_reg + SUNXI_MMC_MODR); + + rval = readl(ccmu_reg + SUNXI_MMC_MODR); + rval |= (1<<31); + writel(rval, ccmu_reg + SUNXI_MMC_MODR); + + rval = readl(ccmu_reg + SUNXI_MMC_GATR); + rval |= ((1u<<2)|(1u<<18)); + writel(rval, ccmu_reg + SUNXI_MMC_GATR); +} +#else + +static void sunxi_mmc_mbusrst_host(char *host) +{ + char *ccmu_reg = gccmu_base_reg; + u32 rval = 0; + + rval = readl(ccmu_reg + SUNXI_MMC_GATR); + rval &= ~(1u<<10); + writel(rval, ccmu_reg + SUNXI_MMC_GATR); + + rval = readl(ccmu_reg + SUNXI_MMC_RST); + rval &= ~(1u<<10); + writel(rval, ccmu_reg + SUNXI_MMC_RST); + + rval = readl(ccmu_reg + SUNXI_MMC_MODR); + rval &= ~((1<<31)); + writel(rval, ccmu_reg + SUNXI_MMC_MODR); + + rval = readl(ccmu_reg + SUNXI_MMC_MODR); + rval |= (1<<31); + writel(rval, ccmu_reg + SUNXI_MMC_MODR); + + rval = readl(ccmu_reg + SUNXI_MMC_RST); + rval |= (1u<<10); + writel(rval, ccmu_reg + SUNXI_MMC_RST); + + rval = readl(ccmu_reg + SUNXI_MMC_GATR); + rval |= (1u<<10); + writel(rval, ccmu_reg + SUNXI_MMC_GATR); +} +#endif + + +static const char mtsdat[512] = { + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, + 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, + 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, + 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, + 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, + 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, + 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, + 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, + 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, + 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, + 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, + 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, + 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, + + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, + 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, + 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, + 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, + 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, + 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, + 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, + 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, + 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, + 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, + 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, + 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, + 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, + + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, + 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, + 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, + 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, + 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, + 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, + 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, + 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, + 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, + 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, + 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, + 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, + 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, + + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, + 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, + 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, + 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, + 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, + 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, + 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, + 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, + 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, + 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, + 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, + 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, + 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, +}; + +static void buf_dumphex32(char *name, const char *base, int len) +{ +#ifdef MMC_DEBUG + u32 i; + + pr_cont("dump %s\n", name); + + for (i = 0; i < len; i += 4) { + if (!(i&0xf)) + pr_cont("\n0x%p : ", base + i); + pr_cont("0x%08x ", *((u32 *)&base[i])); + } + pr_cont("\n"); +#endif +} + +static void sunxi_mmc_regs_save(char *host) +{ + struct sunxi_mmc_mbak_regs *bak_regs = &gmbak_regs; + + /*save public register */ + bak_regs->gctrl = mmc_mreadl(host, REG_GCTRL); + bak_regs->clkc = mmc_mreadl(host, REG_CLKCR); + bak_regs->timeout = mmc_mreadl(host, REG_TMOUT); + bak_regs->buswid = mmc_mreadl(host, REG_WIDTH); + bak_regs->debugc = 0xdeb; + + bak_regs->drv_dl = mmc_mreadl(host, REG_DRV_DL); + bak_regs->samp_dl = mmc_mreadl(host, REG_SAMP_DL); + bak_regs->ds_dl = mmc_mreadl(host, REG_DS_DL); + bak_regs->sd_ntsr = mmc_mreadl(host, REG_SD_NTSR); + bak_regs->edsd = mmc_mreadl(host, REG_EDSD); + bak_regs->csdc = mmc_mreadl(host, REG_CSDC); +} + +static void sunxi_mmc_regs_restore(char *host) +{ + struct sunxi_mmc_mbak_regs *bak_regs = &gmbak_regs; + + /*restore public register */ + mmc_mwritel(host, REG_GCTRL, bak_regs->gctrl); + mmc_mwritel(host, REG_CLKCR, bak_regs->clkc); + mmc_mwritel(host, REG_TMOUT, bak_regs->timeout); + mmc_mwritel(host, REG_WIDTH, bak_regs->buswid); + mmc_mwritel(host, REG_DBGC, bak_regs->debugc); + + mmc_mwritel(host, REG_DRV_DL, bak_regs->drv_dl); + mmc_mwritel(host, REG_SAMP_DL, bak_regs->samp_dl); + mmc_mwritel(host, REG_DS_DL, bak_regs->ds_dl); + mmc_mwritel(host, REG_SD_NTSR, bak_regs->sd_ntsr); + mmc_mwritel(host, REG_EDSD, bak_regs->edsd); + mmc_mwritel(host, REG_CSDC, bak_regs->csdc); +} + +static int sunxi_mmc_mupdate_clk(char *host) +{ + u32 rval; + int i = 0; + + rval = mmc_mreadl(host, REG_CLKCR); + rval &= ~(SDXC_CARD_CLOCK_ON | SDXC_LOW_POWER_ON | SDXC_MASK_DATA0); + + rval |= SDXC_CARD_CLOCK_ON; + rval |= SDXC_LOW_POWER_ON; + rval |= SDXC_MASK_DATA0; + + mmc_mwritel(host, REG_CLKCR, rval); + + mmcdbg("%s REG_CLKCR:%x\n", __func__, + mmc_mreadl(host, REG_CLKCR)); + + rval = SDXC_START | SDXC_UPCLK_ONLY | SDXC_WAIT_PRE_OVER; + mmc_mwritel(host, REG_CMDR, rval); + + do { + rval = mmc_mreadl(host, REG_CMDR); + ndelay(1); + } while (((i++) < MWR_TO_NS) && (rval & SDXC_START)); + + /* clear irq status bits set by the command */ + mmc_mwritel(host, REG_RINTR, + mmc_mreadl(host, REG_RINTR) & ~SDXC_SDIO_INTERRUPT); + + if (rval & SDXC_START) { + mmcerr("fatal err update clk timeout\n"); + return -EIO; + } + + mmc_mwritel(host, REG_CLKCR, + mmc_mreadl(host, REG_CLKCR) & ~SDXC_MASK_DATA0); + + return 0; +} + + + +static void sunxi_mmc_rcover_host(char *host) +{ + sunxi_mmc_regs_save(host); + sunxi_mmc_mbusrst_host(host); + sunxi_mmc_regs_restore(host); + sunxi_mmc_mupdate_clk(host); +} + +static int sunxi_mmc_mchk_r1_rdy(char *reg, int to_ns) +{ + int i = 0; + /*wait busy over*/ + for (i = 0; i < to_ns; i++) { + if (!(mmc_mreadl(reg, REG_STAS) & SDXC_CARD_DATA_BUSY)) + break; + ndelay(1); + } + + if ((mmc_mreadl(reg, REG_STAS) & SDXC_CARD_DATA_BUSY)) { + printk("dead Wait r1 rdy failed\n"); + return MWR_RFAIL; + } + return MWR_ROK; +} + + +static int sunxi_mmc_raw_write(char *reg, u32 sec_addr, + u32 sec_cnt, const char *inbuf) +{ + u32 cmd_val, ri; + u32 rval; + int to = 0; + int wcnt = (sec_cnt<<9)>>2; + int i = 0; + u32 *buf = (u32 *)inbuf; + + + rval = mmc_mreadl(reg, REG_GCTRL); + rval |= SDXC_ACCESS_BY_AHB | SDXC_FIFO_RESET; + rval &= ~SDXC_DMA_ENABLE_BIT; + mmc_mwritel(reg, REG_GCTRL, rval); + for (to = 0; to < MWR_TO_NS; to++) { + rval = mmc_mreadl(reg, REG_GCTRL); + if (!(rval & SDXC_FIFO_RESET)) + break; + ndelay(1); + } + if (to == MWR_TO_NS) { + mmcerr("wait fifo rest timout\n"); + goto eout; + } + + /**cmd seting**/ + cmd_val = SDXC_START | SDXC_RESP_EXPECT \ + | SDXC_CHECK_RESPONSE_CRC | SDXC_DATA_EXPECT\ + | SDXC_WRITE | SDXC_SEND_AUTO_STOP + | SDXC_WAIT_PRE_OVER | MMC_WRITE_MULTIPLE_BLOCK; + mmc_mwritel(reg, REG_CARG, sec_addr); + mmc_mwritel(reg, REG_A12A, 0); + + /**data setting*/ + mmc_mwritel(reg, REG_BLKSZ, 512); + mmc_mwritel(reg, REG_BCNTR, sec_cnt<<9); + + mmc_mwritel(reg, REG_THLD, (512<<16)|(1<<2)|(1<<0)); + mmcdbg("thld %x\n", readl(reg + 0x100)); + + /**int seting*/ + rval = mmc_mreadl(reg, REG_GCTRL); + rval &= ~SDXC_INTERRUPT_ENABLE_BIT; + mmc_mwritel(reg, REG_GCTRL, rval); + mmc_mwritel(reg, REG_MISTA, 0); + mmc_mwritel(reg, REG_RINTR, 0xffffffff); + + /**exe cmd**/ + mmc_mwritel(reg, REG_CMDR, cmd_val); + + /*write data*/ + for (i = 0; i < wcnt; i++) { + /*wait data not full*/ + for (to = 0; to < MWR_TO_NS; to++) { + if (!(mmc_mreadl(reg, REG_STAS) & SDXC_FIFO_FULL)) + break; + ri = mmc_mreadl(reg, REG_RINTR); + if (ri & (SDXC_INTERRUPT_ERROR_BIT)) { + mmcerr("trans err %x\n", ri); + goto eout; + } + ndelay(1); + } + if (to == MWR_TO_NS) { + mmcerr("wait fifo not full timeout\n"); + goto eout; + } + + mmc_mwritel(reg, REG_FIFO, buf[i]); + } + + /*wait busy over*/ + for (i = 0; i < MWR_TO_NS; i++) { + if (!(mmc_mreadl(reg, REG_STAS) & SDXC_CARD_DATA_BUSY)) + break; + ndelay(1); + } + + if ((mmc_mreadl(reg, REG_STAS) & SDXC_CARD_DATA_BUSY)) { + mmcerr("dead Wait r1 rdy failed\n"); + goto eout; + } + + for (to = 0; to < MWR_TO_NS; to++) { + ri = mmc_mreadl(reg, REG_RINTR); + if (ri & (SDXC_AUTO_COMMAND_DONE)) + break; + ndelay(1); + } + if (to == MWR_TO_NS) { + mmcerr("wait auto cmd done timout\n"); + goto eout; + } + + mmc_mwritel(reg, REG_RINTR, 0xffff); + mmcdbg("manul write ok\n"); + return MWR_ROK; + +eout: + mmcerr("mau write failed\n"); + return MWR_RFAIL; +} + + +static int sunxi_mmc_raw_half_write(char *reg, u32 sec_addr, + u32 sec_cnt, u32 stp_wd, const char *inbuf) +{ + u32 cmd_val, ri; + u32 rval; + int to = 0; + int wcnt = (sec_cnt<<9)>>2; + int i = 0; + u32 *buf = (u32 *)inbuf; + + /**cmd seting**/ + cmd_val = SDXC_START | SDXC_RESP_EXPECT \ + | SDXC_CHECK_RESPONSE_CRC | SDXC_DATA_EXPECT\ + | SDXC_WRITE | SDXC_SEND_AUTO_STOP + | SDXC_WAIT_PRE_OVER | MMC_WRITE_MULTIPLE_BLOCK; + mmc_mwritel(reg, REG_CARG, sec_addr); + mmc_mwritel(reg, REG_A12A, 0); + + /**data setting*/ + mmc_mwritel(reg, REG_BLKSZ, 512); + mmc_mwritel(reg, REG_BCNTR, sec_cnt<<9); + rval = mmc_mreadl(reg, REG_GCTRL); + rval |= SDXC_ACCESS_BY_AHB | SDXC_FIFO_RESET; + rval &= ~SDXC_DMA_ENABLE_BIT; + mmc_mwritel(reg, REG_GCTRL, rval); + for (to = 0; to < MWR_TO_NS; to++) { + rval = mmc_mreadl(reg, REG_GCTRL); + if (!(rval & SDXC_FIFO_RESET)) + break; + ndelay(1); + } + if (to == MWR_TO_NS) { + mmcerr("wait fifo rest timout\n"); + goto eout; + } + + /**int seting*/ + rval &= ~SDXC_INTERRUPT_ENABLE_BIT; + mmc_mwritel(reg, REG_GCTRL, rval); + mmc_mwritel(reg, REG_MISTA, 0); + mmc_mwritel(reg, REG_RINTR, 0xffff); + + /**exe cmd**/ + mmc_mwritel(reg, REG_CMDR, cmd_val); + + /*write data*/ + for (i = 0; (i < wcnt) && (i < stp_wd); i++) { + /*wait data not full*/ + for (to = 0; to < MWR_TO_NS; to++) { + if (!(mmc_mreadl(reg, REG_STAS) & SDXC_FIFO_FULL)) + break; + ri = mmc_mreadl(reg, REG_RINTR); + if (ri & (SDXC_INTERRUPT_ERROR_BIT)) { + mmcerr("trans err %x\n", ri); + goto eout; + } + ndelay(1); + } + if (to == MWR_TO_NS) { + mmcerr("wait fifo not full timeout\n"); + goto eout; + } + + mmc_mwritel(reg, REG_FIFO, buf[i]); + } + + mmc_mwritel(reg, REG_RINTR, 0xffff); + mmcdbg("manul half write ok\n"); + return MWR_ROK; + +eout: + mmcerr("mau write failed\n"); + return MWR_RFAIL; +} + +static int sunxi_mmc_raw_wcmd_clr(char *reg, int *out_cmd_val) +{ + int i = 0; + u32 cmd_val = 0; + + do { + cmd_val = mmc_mreadl(reg, REG_CMDR); + if (!(cmd_val & SDXC_START)) { + *out_cmd_val = cmd_val; + return MWR_ROK; + } + ndelay(1); + } while ((i++) < MWR_TO_NS); + + mmcerr("Wait cmd over timout\n"); + return MWR_RFAIL; +} + +static void sunxi_mmc_raw_stop(char *reg) +{ + u32 arg, cmd_val, ri; + int i = 0; + int rval = 0; + + cmd_val = SDXC_START | SDXC_RESP_EXPECT + |SDXC_STOP_ABORT_CMD | SDXC_CHECK_RESPONSE_CRC + |MMC_STOP_TRANSMISSION; + arg = 0; + + /**int seting*/ + rval = mmc_mreadl(reg, REG_GCTRL); + rval &= ~SDXC_INTERRUPT_ENABLE_BIT; + mmc_mwritel(reg, REG_GCTRL, rval); + mmc_mwritel(reg, REG_MISTA, 0); + mmc_mwritel(reg, REG_RINTR, 0xffff); + + mmc_mwritel(reg, REG_CARG, arg); + mmc_mwritel(reg, REG_CMDR, cmd_val); + + do { + ri = mmc_mreadl(reg, REG_RINTR); + if (ri & (SDXC_COMMAND_DONE | + (SDXC_INTERRUPT_ERROR_BIT|SDXC_DAT_STARV_ERR))) + break; + ndelay(1); + } while ((i++) < MWR_TO_NS); + + if (!(ri & SDXC_COMMAND_DONE) || + (ri & (SDXC_INTERRUPT_ERROR_BIT|SDXC_DAT_STARV_ERR))) { + ri = mmc_mreadl(reg, REG_RINTR); + if (!(ri & SDXC_COMMAND_DONE) || + (ri & (SDXC_INTERRUPT_ERROR_BIT|SDXC_DAT_STARV_ERR))) { + mmcdbg("send manual stop command failed, %x\n", ri); + } else { + mmcdbg("send manual stop command ok\n"); + } + } else + mmcdbg("send manual stop command ok\n"); + + + mmc_mwritel(reg, REG_RINTR, 0xffff); +} + +static int sunxi_mmc_raw_read(char *reg, u32 sec_addr, + u32 sec_cnt, char *outbuf) +{ + u32 cmd_val, ri; + u32 rval; + int to = 0; + int wcnt = (sec_cnt<<9)>>2; + int i = 0; + u32 *buf = (u32 *)outbuf; + int fifo_level = 0; + + /**int seting*/ + rval = mmc_mreadl(reg, REG_GCTRL); + rval &= ~SDXC_INTERRUPT_ENABLE_BIT; + mmc_mwritel(reg, REG_GCTRL, rval); + mmc_mwritel(reg, REG_MISTA, 0); + mmc_mwritel(reg, REG_RINTR, 0xffff); + + /**cmd seting**/ + cmd_val = SDXC_START | SDXC_RESP_EXPECT \ + | SDXC_CHECK_RESPONSE_CRC | SDXC_DATA_EXPECT\ + | SDXC_SEND_AUTO_STOP + | SDXC_WAIT_PRE_OVER | MMC_READ_MULTIPLE_BLOCK; + mmc_mwritel(reg, REG_CARG, sec_addr); + mmc_mwritel(reg, REG_A12A, 0); + + /**data setting*/ + mmc_mwritel(reg, REG_BLKSZ, 512); + mmc_mwritel(reg, REG_BCNTR, sec_cnt<<9); + mmc_mwritel(reg, REG_THLD, (512<<16)|(1<<2)|(1<<0)); + mmcdbg("thld %x\n", readl(reg + 0x100)); + + rval = mmc_mreadl(reg, REG_GCTRL); + rval |= SDXC_ACCESS_BY_AHB | SDXC_FIFO_RESET; + rval &= ~SDXC_DMA_ENABLE_BIT; + mmc_mwritel(reg, REG_GCTRL, rval); + for (to = 0; to < MWR_TO_NS; to++) { + rval = mmc_mreadl(reg, REG_GCTRL); + if (!(rval & SDXC_FIFO_RESET)) + break; + ndelay(1); + } + if (to == MWR_TO_NS) { + mmcerr("wait fifo rest timout\n"); + goto eout; + } + + + /**exe cmd**/ + mmc_mwritel(reg, REG_CMDR, cmd_val); + + /*read data*/ + do { + /*wait data not full*/ + for (to = 0; to < MWR_TO_NS; to++) { + if (!(mmc_mreadl(reg, REG_STAS) + & SDXC_FIFO_EMPTY)) + break; + ri = mmc_mreadl(reg, REG_RINTR); + if (ri & (SDXC_INTERRUPT_ERROR_BIT)) { + mmcerr("trans err %x\n", ri); + goto eout; + } + ndelay(1); + } + if (to == MWR_TO_NS) { + mmcerr("wait fifo no empty timeout %x\n", ri); + goto eout; + } + + fifo_level = (mmc_mreadl(reg, REG_STAS) >> 17) & 0x1f; + if (fifo_level && (fifo_level <= 16)) + while (fifo_level--) + buf[i++] = mmc_mreadl(reg, REG_FIFO); + else + buf[i++] = mmc_mreadl(reg, REG_FIFO); + } while (i < wcnt); + + + + for (to = 0; to < MWR_TO_NS; to++) { + ri = mmc_mreadl(reg, REG_RINTR); + if (ri & (SDXC_AUTO_COMMAND_DONE)) + break; + ndelay(1); + } + if (to == MWR_TO_NS) { + mmcerr("wait auto cmd done timout\n"); + goto eout; + } + + + mmc_mwritel(reg, REG_RINTR, 0xffff); + return MWR_ROK; + +eout: + mmcerr("mau read failed\n"); + return MWR_RFAIL; +} + + +static int sunxi_mmc_raw_half_read(char *reg, u32 sec_addr, u32 sec_cnt, u32 stp_wd, char *outbuf) +{ + u32 cmd_val, ri; + u32 rval; + int to = 0; + int wcnt = (sec_cnt<<9)>>2; + int i = 0; + u32 *buf = (u32 *)outbuf; + int fifo_level = 0; + + + /**int seting*/ + rval = mmc_mreadl(reg, REG_GCTRL); + rval &= ~SDXC_INTERRUPT_ENABLE_BIT; + mmc_mwritel(reg, REG_GCTRL, rval); + mmc_mwritel(reg, REG_MISTA, 0); + mmc_mwritel(reg, REG_RINTR, 0xffff); + + /**cmd seting**/ + cmd_val = SDXC_START | SDXC_RESP_EXPECT \ + | SDXC_CHECK_RESPONSE_CRC | SDXC_DATA_EXPECT\ + | SDXC_SEND_AUTO_STOP + | SDXC_WAIT_PRE_OVER | MMC_READ_MULTIPLE_BLOCK; + mmc_mwritel(reg, REG_CARG, sec_addr); + mmc_mwritel(reg, REG_A12A, 0); + + /**data setting*/ + mmc_mwritel(reg, REG_BLKSZ, 512); + mmc_mwritel(reg, REG_BCNTR, sec_cnt<<9); + rval = mmc_mreadl(reg, REG_GCTRL); + rval |= SDXC_ACCESS_BY_AHB | SDXC_FIFO_RESET; + rval &= ~SDXC_DMA_ENABLE_BIT; + mmc_mwritel(reg, REG_GCTRL, rval); + for (to = 0; to < MWR_TO_NS; to++) { + rval = mmc_mreadl(reg, REG_GCTRL); + if (!(rval & SDXC_FIFO_RESET)) + break; + ndelay(1); + } + if (to == MWR_TO_NS) { + mmcerr("wait fifo rest timout\n"); + goto eout; + } + + /**exe cmd**/ + mmc_mwritel(reg, REG_CMDR, cmd_val); + + /*read data*/ + do { + /*wait data not full*/ + for (to = 0; to < MWR_TO_NS; to++) { + if (!(mmc_mreadl(reg, REG_STAS) & SDXC_FIFO_EMPTY)) + break; + ri = mmc_mreadl(reg, REG_RINTR); + if (ri & (SDXC_INTERRUPT_ERROR_BIT)) { + mmcerr("trans err %x\n", ri); + goto eout; + } + ndelay(1); + } + if (to == MWR_TO_NS) + goto eout; + + fifo_level = (mmc_mreadl(reg, REG_STAS) >> 17) & 0x1f; + if (fifo_level && (fifo_level <= 16)) + while (fifo_level--) + buf[i++] = mmc_mreadl(reg, REG_FIFO); + else + buf[i++] = mmc_mreadl(reg, REG_FIFO); + } while ((i < wcnt) && (i < stp_wd)); + + mmc_mwritel(reg, REG_RINTR, 0xffff); + return MWR_ROK; + +eout: + mmcerr("mau read failed\n"); + return MWR_RFAIL; +} + + + +/**use for panic situation,no irq,no lock,no dma**/ +int sunxi_mmc_panic_read(u32 sec_addr, u32 sec_cnt, char *outbuf) +{ + char *reg = ghost_base_reg; + int ret = 0; + u32 cmd_val = 0; + + BUG_ON(outbuf == NULL); + if (!ghost_base_reg || !gccmu_base_reg) { + mmcerr("host,ccmu reg has not init\n"); + return MWR_RFAIL; + } + + cmd_val = mmc_mreadl(reg, REG_CMDR); + + ret = sunxi_mmc_raw_wcmd_clr(reg, &cmd_val); + if (ret) + return ret; + + if (cmd_val & SDXC_DATA_EXPECT) + sunxi_mmc_raw_stop(reg); + + sunxi_mmc_rcover_host(reg); + + if (cmd_val & SDXC_DATA_EXPECT) + sunxi_mmc_raw_stop(reg); + + if (cmd_val & SDXC_WRITE) + ret = sunxi_mmc_mchk_r1_rdy(reg, MWR_TO_NS); + if (ret) + return ret; + + return sunxi_mmc_raw_read(reg, sec_addr, sec_cnt, outbuf); +} + +/**use for panic situation,no irq,no lock,no dma**/ +int sunxi_mmc_panic_write(u32 sec_addr, u32 sec_cnt, const char *inbuf) +{ + int ret = 0; + u32 cmd_val = 0; + char *reg = ghost_base_reg; + + cmd_val = mmc_mreadl(reg, REG_CMDR); + BUG_ON(inbuf == NULL); + if (!ghost_base_reg || !gccmu_base_reg) { + mmcerr("host,ccmu reg has not init\n"); + return MWR_RFAIL; + } + + ret = sunxi_mmc_raw_wcmd_clr(reg, &cmd_val); + if (ret) + return ret; + + if (cmd_val & SDXC_DATA_EXPECT) + sunxi_mmc_raw_stop(reg); + + sunxi_mmc_rcover_host(reg); + + if (cmd_val & SDXC_DATA_EXPECT) + sunxi_mmc_raw_stop(reg); + + if (cmd_val & SDXC_WRITE) + ret = sunxi_mmc_mchk_r1_rdy(reg, MWR_TO_NS); + if (ret) + return ret; + + return sunxi_mmc_raw_write(reg, sec_addr, sec_cnt, inbuf); +} + +int sunxi_mmc_panic_init(void) +{ + gccmu_base_reg = ioremap(SUNXI_CCMU_BASE, 0x900); + if (!gccmu_base_reg) { + mmcerr("*iormap ccmu failed*\n"); + return MWR_RFAIL; + } + + ghost_base_reg = ioremap(SUNXI_SMHC_BASE, 0x300); + if (!ghost_base_reg) { + mmcerr("*iormap host failed*\n"); + return MWR_RFAIL; + } + return MWR_ROK; +} + + + +void sunxi_mmc_panic_exit(void) +{ + iounmap(gccmu_base_reg); + iounmap(ghost_base_reg); +} + +ssize_t +sunxi_mmc_panic_rtest(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mmc_host *mmc = platform_get_drvdata(pdev); + char *rxbuf = kzalloc(SUNXI_TEST_SIZE, GFP_KERNEL); + int ret = 0; + + printk("Start panic read test\n"); + + mmc_claim_host(mmc); + sunxi_mmc_panic_init(); + ret = sunxi_mmc_panic_read(16, SUNXI_TEST_SIZE/512, rxbuf); + if (ret) + goto out; + buf_dumphex32("rxbuf", rxbuf, SUNXI_TEST_SIZE); + printk(KERN_INFO "panic read ok\n"); + mmc_release_host(mmc); + +out: + kfree(rxbuf); + return SUNXI_TEST_SIZE; +} + +ssize_t +sunxi_mmc_pancic_wrtest(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mmc_host *mmc = platform_get_drvdata(pdev); + char *rxbuf = kzalloc(SUNXI_TEST_SIZE, GFP_KERNEL); + char *mwr_tsdat = kzalloc(SUNXI_TEST_SIZE, GFP_KERNEL); + int sc = 0; + int i = 0; + char *reg = NULL; + if (kstrtoint(buf, 0, &sc)) + goto out; + + mmcinfo(KERN_INFO "Start test sec %d\n", sc); + for (i = 0; i < (SUNXI_TEST_SIZE/512); i++) + memcpy(mwr_tsdat+i*512, mtsdat, 512); + + + mmc_claim_host(mmc); + sunxi_mmc_panic_init(); + reg = ghost_base_reg; + mmcinfo("***Test normal w/r***\n"); + buf_dumphex32("test data", mwr_tsdat, SUNXI_TEST_SIZE); + mmcdbg("Write test data\n"); + sunxi_mmc_panic_write(sc, SUNXI_TEST_SIZE/512, mwr_tsdat); + mmcdbg("Read test data\n"); + sunxi_mmc_panic_read(sc, SUNXI_TEST_SIZE/512, rxbuf); + buf_dumphex32("read data from mmc after write", rxbuf, SUNXI_TEST_SIZE); + if (memcmp(mwr_tsdat, rxbuf, SUNXI_TEST_SIZE) != 0) { + mmcinfo("write read failed\n"); + goto out; + } + mmcinfo(KERN_INFO "***write read compare ok,test ok***\n"); + +#if 1 + mmcinfo("\n***test half read***\n"); + memset(rxbuf, 0, SUNXI_TEST_SIZE); + buf_dumphex32("0 test data", rxbuf, SUNXI_TEST_SIZE);; + sunxi_mmc_raw_half_read(reg, sc, SUNXI_TEST_SIZE/512, 160, rxbuf); + buf_dumphex32("half read data", rxbuf, SUNXI_TEST_SIZE); + sunxi_mmc_panic_read(sc, SUNXI_TEST_SIZE/512, rxbuf); + buf_dumphex32("read test data", rxbuf, SUNXI_TEST_SIZE); + if (memcmp(mwr_tsdat, rxbuf, SUNXI_TEST_SIZE) != 0) { + mmcinfo("half read compare failed\n"); + goto out; + } + mmcinfo("***test half read test ok***\n"); + + + mmcinfo("\n***test half write***\n"); + memset(rxbuf, 0, SUNXI_TEST_SIZE); + sunxi_mmc_raw_half_write(reg, sc, SUNXI_TEST_SIZE/512, 160, mwr_tsdat); + sunxi_mmc_panic_read(sc, SUNXI_TEST_SIZE/512, rxbuf); + buf_dumphex32("read half test data", rxbuf, SUNXI_TEST_SIZE); + if (memcmp(mwr_tsdat, rxbuf, SUNXI_TEST_SIZE) != 0) { + mmcinfo("half write compare failed\n"); + return count; + } + mmcinfo("***test half write test ok***\n\n"); +#endif +out: + sunxi_mmc_panic_exit(); + mmc_release_host(mmc); + + + kfree(rxbuf); + kfree(mwr_tsdat); + return count; +} + +#ifdef CONFIG_SUNXI_PANICPART +static ssize_t sunxi_mmc_panic_read_ps(struct panic_part *part, loff_t sec_off, + size_t sec_cnt, char *buf) +{ + int ret; + + ret = sunxi_mmc_panic_read(part->start_sect + sec_off, sec_cnt, buf); + if (ret) + return ret; + return sec_cnt; +} + +static ssize_t sunxi_mmc_panic_write_ps(struct panic_part *part, loff_t sec_off, + size_t sec_cnt, const char *buf) +{ + int ret; + + ret = sunxi_mmc_panic_write(part->start_sect + sec_off, sec_cnt, buf); + if (ret) + return ret; + return sec_cnt; +} + + +static struct panic_part sunxi_mmc_panic_ps = { + .type = SUNXI_FLASH_MMC, + .panic_read = sunxi_mmc_panic_read_ps, + .panic_write = sunxi_mmc_panic_write_ps, +}; +#endif + +int sunxi_mmc_panic_init_ps(void *data) +{ + int ret = 0; + +#ifdef CONFIG_SUNXI_PANICPART + if (init_cnt) { + mmcdbg("error Has init sunxi mmc panic\n"); + return MWR_RFAIL; + } + + ret = sunxi_mmc_panic_init(); + if (ret <= MWR_RFAIL) + return ret; + if (!ret) + init_cnt = 1; + + ret = sunxi_panicpart_init(&sunxi_mmc_panic_ps); +#endif + return ret; +} +