firmware/br-ext-chip-allwinner/board/v83x/kernel/patches/00000-drivers_mmc_host_sunx...

1083 lines
27 KiB
Diff

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 <lixiang@allwinnertech>
+*
+* 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 <linux/clk.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/regulator/consumer.h>
+#include <linux/delay.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-export.h"
+
+
+#ifdef CONFIG_SUNXI_PANICPART
+#include <linux/sunxi-panicpart.h>
+#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;
+}
+