/* * 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 #include "sunxi-mmc.h" #include "sunxi-mmc-export.h" #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; static int init_cnt; static sector_t blkdev_start_sect; static sector_t blkdev_sects; #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; char *ccmu_reg = gccmu_base_reg; u32 rval = 0; /*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); rval = readl(ccmu_reg + SUNXI_MMC_MODR); rval &= ~((1<<31)); writel(rval, ccmu_reg + SUNXI_MMC_MODR); mmc_mwritel(host, REG_DRV_DL, bak_regs->drv_dl); rval = readl(ccmu_reg + SUNXI_MMC_MODR); rval |= (1<<31); writel(rval, ccmu_reg + SUNXI_MMC_MODR); 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; } /* static ssize_t sunxi_mmc_panic_read_ps(const char *buf, sector_t start_sect, sector_t sects) { int ret; ret = sunxi_mmc_panic_read(part->start_sect + sec_off, sec_cnt, buf); if (ret) return ret; return sec_cnt; } */ static int sunxi_mmc_panic_write_ps(const char *buf, sector_t sect_off, sector_t sects) { int ret; if (sect_off + sects >= blkdev_sects) return -EOVERFLOW; ret = sunxi_mmc_panic_write(blkdev_start_sect + sect_off, sects, buf); if (ret) return ret; return 0; } int sunxi_mmc_panic_init_ps(void *data) { int ret = 0; struct pstore_blk_info info = {}; 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; info.major = MMC_BLOCK_MAJOR; info.flags = 0; info.panic_write = sunxi_mmc_panic_write_ps; ret = register_pstore_blk(&info); if (ret == -ENODEV) { info.major = BLOCK_EXT_MAJOR; ret = register_pstore_blk(&info); } if (ret) goto exit_panic; blkdev_sects = info.nr_sects; blkdev_start_sect = info.start_sect; init_cnt = 1; return 0; exit_panic: sunxi_mmc_panic_exit(); return ret; }