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

714 lines
20 KiB
Diff

diff -drupN a/drivers/mmc/host/sunxi-mmc-debug.c b/drivers/mmc/host/sunxi-mmc-debug.c
--- a/drivers/mmc/host/sunxi-mmc-debug.c 1970-01-01 03:00:00.000000000 +0300
+++ b/drivers/mmc/host/sunxi-mmc-debug.c 2022-06-12 05:28:14.000000000 +0300
@@ -0,0 +1,709 @@
+/*
+* Sunxi SD/MMC host driver
+*
+* Copyright (C) 2015 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/stat.h>
+
+#include <linux/mmc/host.h>
+#include "sunxi-mmc.h"
+#include "sunxi-mmc-debug.h"
+#include "sunxi-mmc-export.h"
+#include "sunxi-mmc-sun50iw1p1-2.h"
+#include "sunxi-mmc-panic.h"
+
+
+#define GPIO_BASE_ADDR 0x1c20800
+/*nc platform no use these value*/
+#define CCMU_BASE_ADDR_BEFORE_V2P1H 0x1c20000
+
+#define SUNXI_MMC_MAX_HOST_PRT_ADDR 0x150
+#define SUNXI_MMC_MAX_GPIO_PRT_ADDR 0x120
+#define SUNXI_GPIOIC_PRT_EADDR 0x380
+#define SUNXI_GPIOIC_PRT_SADDR 0x200
+
+/*mmc bus clock gating register*/
+#define SUNXI_BCLKG_SADDR 0x60
+#define SUNXI_BCLKG_EADDR 0x80
+
+/*mmc moudule clock register*/
+#define SUNXI_CLK_PRT_SADDR 0x80
+#define SUNXI_CLK_PRT_EADDR 0xa0
+
+/*mmc bus soft reset register*/
+#define SUNXI_BSRES_SADDR 0x2C0
+#define SUNXI_BSRES_EADDR 0x2DC
+
+
+/*NC mmc bus gating,reset,moudule clouck register*/
+#define SUNXI_NCCM_EADDR 0x850
+#define SUNXI_NCCM_SADDR 0x830
+
+/*NC mmc PLL PERI register*/
+#define SUNXI_PP_NCM_EADDR 0x2C
+#define SUNXI_PP_NCM_SADDR 0x20
+
+#define SUNXI_DEG_MAX_MAP_REG 0x900
+
+static struct device_attribute dump_register[3];
+
+void sunxi_mmc_dumphex32(struct sunxi_mmc_host *host, char *name, char *base,
+ int len)
+{
+ u32 i;
+
+ pr_cont("dump %s registers:", name);
+ for (i = 0; i < len; i += 4) {
+ if (!(i&0xf))
+ pr_cont("\n0x%p : ", base + i);
+ pr_cont("0x%08x ", __raw_readl(host->reg_base+i));
+ }
+ pr_cont("\n");
+}
+
+void sunxi_mmc_dump_des(struct sunxi_mmc_host *host, char *base, int len)
+{
+ u32 i;
+
+ pr_cont("dump des mem\n");
+ 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");
+}
+
+static unsigned int sunxi_mmc_get_rate(uint64_t bytes, uint64_t time_us)
+{
+ uint64_t ns;
+
+ ns = time_us * 1000;
+ bytes *= 1000000000;
+
+ while (ns > UINT_MAX) {
+ bytes >>= 1;
+ ns >>= 1;
+ }
+
+ if (!ns)
+ return 0;
+
+ do_div(bytes, (uint32_t)ns);
+
+ return bytes;
+}
+
+static void sunxi_mmc_filter_rate(struct sunxi_mmc_host *host, struct mmc_data *data, int64_t bytes, uint64_t time_us)
+{
+ unsigned int rate = 0;
+
+ if (!(host->filter_sector)
+ || !(host->filter_speed))
+ return;
+
+ if ((data->blocks) >= (host->filter_sector)) {
+ rate = sunxi_mmc_get_rate(bytes, time_us);
+ if (rate < (host->filter_speed))
+ printk("c=%u,a=0x%8x,""bs=%5u,""t=%9lluus,""sp=%7uKB/s\n",
+ data->mrq->cmd->opcode, data->mrq->cmd->arg,
+ data->blocks, time_us, rate/1024);
+ }
+}
+
+
+static ssize_t maual_insert_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+
+ ret =
+ snprintf(buf, PAGE_SIZE,
+ "Usage: \"echo 1 > insert\" to scan card\n");
+ return ret;
+}
+
+static ssize_t maual_insert_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+
+ unsigned long insert = 0;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+
+ ret = kstrtoul(buf, 0, &insert);
+ if (ret) {
+ ret = -EINVAL;
+ return ret;
+ }
+
+ dev_info(dev, "insert %ld\n", insert);
+ if (insert)
+ mmc_detect_change(mmc, 0);
+ else
+ dev_info(dev, "no detect change\n");
+
+ ret = count;
+ return ret;
+}
+
+int sunxi_mmc_res_start_addr(const char * const res_str,
+ resource_size_t *res_addr)
+{
+ struct device_node *np = NULL;
+ int ret = 0;
+ struct resource res;
+
+ if (res_str == NULL || res_addr == NULL) {
+ pr_err("input arg is error\n");
+ return -EINVAL;
+ }
+
+ np = of_find_node_by_type(NULL, res_str);
+ if (IS_ERR(np)) {
+ pr_err("Can not find device type\n");
+ return -EINVAL;
+ }
+
+ ret = of_address_to_resource(np, 0, &res);
+ if (ret || !res.start) {
+ pr_err("Can not find resouce\n");
+ return -EINVAL;
+ }
+ *res_addr = res.start;
+
+ return 0;
+}
+
+void sunxi_dump_reg(struct mmc_host *mmc)
+{
+ int i = 0;
+ struct sunxi_mmc_host *host = mmc_priv(mmc);
+ int ret = 0;
+ void __iomem *gpio_ptr = NULL;
+ void __iomem *ccmu_ptr = NULL;
+ resource_size_t res_saddr_ccmu;
+ resource_size_t res_saddr_gpio;
+
+ ret = sunxi_mmc_res_start_addr("clocks", &res_saddr_ccmu);
+ if (ret < 0)
+ return;
+ ccmu_ptr = ioremap(res_saddr_ccmu, SUNXI_DEG_MAX_MAP_REG);
+ if (ccmu_ptr == NULL) {
+ pr_err("Can not map ccmu resource\n");
+ return;
+ }
+
+ ret = sunxi_mmc_res_start_addr("pio", &res_saddr_gpio);
+ if (ret < 0)
+ return;
+ gpio_ptr = ioremap(res_saddr_gpio, SUNXI_DEG_MAX_MAP_REG);
+ if (gpio_ptr == NULL) {
+ pr_err("Can not map gpio resource\n");
+ return;
+ }
+
+ pr_cont("Dump %s (p%x) regs :\n", mmc_hostname(mmc), host->phy_index);
+ for (i = 0; i < SUNXI_MMC_MAX_HOST_PRT_ADDR; i += 4) {
+ if (!(i&0xf))
+ pr_cont("\n0x%p : ", (host->reg_base + i));
+ pr_cont("%08x ", readl(host->reg_base + i));
+ }
+ pr_cont("\n");
+
+
+ pr_cont("Dump gpio regs:\n");
+ for (i = 0; i < SUNXI_MMC_MAX_GPIO_PRT_ADDR; i += 4) {
+ if (!(i&0xf))
+ pr_cont("\n0x%p : ", (gpio_ptr + i));
+ pr_cont("%08x ", readl(gpio_ptr + i));
+ }
+ pr_cont("\n");
+
+ pr_cont("Dump gpio irqc regs:\n");
+ for (i = SUNXI_GPIOIC_PRT_SADDR; i < SUNXI_GPIOIC_PRT_EADDR; i += 4) {
+ if (!(i&0xf))
+ pr_cont("\n0x%p : ", (gpio_ptr + i));
+ pr_cont("%08x ", readl(gpio_ptr + i));
+ }
+ pr_cont("\n");
+
+ if (res_saddr_ccmu == CCMU_BASE_ADDR_BEFORE_V2P1H) {
+ pr_cont("Dump ccmu regs:gating\n");
+ for (i = SUNXI_BCLKG_SADDR; i < SUNXI_BCLKG_EADDR; i += 4) {
+ if (!(i&0xf))
+ pr_cont("\n0x%p : ", (ccmu_ptr + i));
+ pr_cont("%08x ", readl(ccmu_ptr + i));
+ }
+ pr_cont("\n");
+
+ pr_cont("Dump ccmu regs:module clk\n");
+ for (i = SUNXI_CLK_PRT_SADDR; i < SUNXI_CLK_PRT_EADDR; i += 4) {
+ if (!(i&0xf))
+ pr_cont("\n0x%p : ", (ccmu_ptr + i));
+ pr_cont("%08x ", readl(ccmu_ptr + i));
+ }
+ pr_cont("\n");
+
+ pr_cont("Dump ccmu regs:reset\n");
+ for (i = SUNXI_BSRES_SADDR; i < SUNXI_BSRES_EADDR; i += 4) {
+ if (!(i&0xf))
+ pr_cont("\n0x%p : ", (ccmu_ptr + i));
+ pr_cont("%08x ", readl(ccmu_ptr + i));
+ }
+ pr_cont("\n");
+ } else {
+ pr_cont("Dump ccmu regs:pll,gating,reset,module clk\n");
+
+ for (i = SUNXI_PP_NCM_SADDR; i < SUNXI_PP_NCM_EADDR; i += 4) {
+ if (!(i&0xf))
+ pr_cont("\n0x%p : ", (ccmu_ptr + i));
+ pr_cont("%08x ", readl(ccmu_ptr + i));
+ }
+ pr_cont("\n");
+
+ for (i = SUNXI_NCCM_SADDR; i < SUNXI_NCCM_EADDR; i += 4) {
+ if (!(i&0xf))
+ pr_cont("\n0x%p : ", (ccmu_ptr + i));
+ pr_cont("%08x ", readl(ccmu_ptr + i));
+ }
+ pr_cont("\n");
+ }
+
+ iounmap(gpio_ptr);
+ iounmap(ccmu_ptr);
+
+}
+
+static ssize_t dump_host_reg_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ char *p = buf;
+ int i = 0;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct sunxi_mmc_host *host = mmc_priv(mmc);
+
+ p += sprintf(p, "Dump sdmmc regs:\n");
+ for (i = 0; i < SUNXI_MMC_MAX_HOST_PRT_ADDR; i += 4) {
+ if (!(i&0xf))
+ p += sprintf(p, "\n0x%p : ", (host->reg_base + i));
+ p += sprintf(p, "%08x ", readl(host->reg_base + i));
+ }
+ p += sprintf(p, "\n");
+
+ return p - buf;
+
+}
+
+static ssize_t dump_gpio_reg_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ char *p = buf;
+ int i = 0;
+ void __iomem *gpio_ptr = NULL;
+ resource_size_t res_saddr_gpio;
+ int ret = 0;
+
+ ret = sunxi_mmc_res_start_addr("pio", &res_saddr_gpio);
+ if (ret < 0)
+ goto out;
+
+ gpio_ptr = ioremap(res_saddr_gpio, SUNXI_DEG_MAX_MAP_REG);
+ if (!gpio_ptr) {
+ pr_err("Can not map gpio resource\n");
+ goto out;
+ }
+
+ p += sprintf(p, "Dump gpio regs:\n");
+ for (i = 0; i < SUNXI_MMC_MAX_GPIO_PRT_ADDR; i += 4) {
+ if (!(i&0xf))
+ p += sprintf(p, "\n0x%p : ", (gpio_ptr + i));
+ p += sprintf(p, "%08x ", readl(gpio_ptr + i));
+ }
+ p += sprintf(p, "\n");
+
+ p += sprintf(p, "Dump gpio irqc regs:\n");
+ for (i = SUNXI_GPIOIC_PRT_SADDR; i < SUNXI_GPIOIC_PRT_EADDR; i += 4) {
+ if (!(i&0xf))
+ p += sprintf(p, "\n0x%p : ", (gpio_ptr + i));
+ p += sprintf(p, "%08x ", readl(gpio_ptr + i));
+ }
+ p += sprintf(p, "\n");
+
+ iounmap(gpio_ptr);
+out:
+ return p-buf;
+
+}
+
+static ssize_t dump_ccmu_reg_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ char *p = buf;
+ int i = 0;
+ void __iomem *ccmu_ptr = NULL;
+ int ret = 0;
+ resource_size_t res_saddr_ccmu;
+
+ ret = sunxi_mmc_res_start_addr("clocks", &res_saddr_ccmu);
+ if (ret < 0)
+ goto out;
+
+ ccmu_ptr = ioremap(res_saddr_ccmu, SUNXI_DEG_MAX_MAP_REG);
+ if (!ccmu_ptr) {
+ pr_err("Can not map ccmu resource\n");
+ goto out;
+ }
+
+ p += sprintf(p, "Dump ccmu\n");
+ if (res_saddr_ccmu == CCMU_BASE_ADDR_BEFORE_V2P1H) {
+
+ p += sprintf(p, "Dump ccmu regs:gating\n");
+ for (i = SUNXI_BCLKG_SADDR; i < SUNXI_BCLKG_EADDR; i += 4) {
+ if (!(i&0xf))
+ p += sprintf(p, "\n0x%p : ", (ccmu_ptr + i));
+ p += sprintf(p, "%08x ", readl(ccmu_ptr + i));
+ }
+ p += sprintf(p, "\n");
+
+ p += sprintf(p, "Dump ccmu regs:module clk\n");
+ for (i = SUNXI_CLK_PRT_SADDR; i < SUNXI_CLK_PRT_EADDR; i += 4) {
+ if (!(i&0xf))
+ p += sprintf(p, "\n0x%p : ", (ccmu_ptr + i));
+ p += sprintf(p, "%08x ", readl(ccmu_ptr + i));
+ }
+ p += sprintf(p, "\n");
+
+ p += sprintf(p, "Dump ccmu regs:reset\n");
+ for (i = SUNXI_BSRES_SADDR; i < SUNXI_BSRES_EADDR; i += 4) {
+ if (!(i&0xf))
+ p += sprintf(p, "\n0x%p : ", (ccmu_ptr + i));
+ p += sprintf(p, "%08x ", readl(ccmu_ptr + i));
+ }
+ p += sprintf(p, "\n");
+
+ } else {
+ p += sprintf(p, "Dump ccmu regs:pll,gating,reset,module clk\n");
+
+ for (i = SUNXI_PP_NCM_SADDR; i < SUNXI_PP_NCM_EADDR; i += 4) {
+ if (!(i&0xf))
+ p += sprintf(p, "\n0x%p : ", (ccmu_ptr + i));
+ p += sprintf(p, "%08x ", readl(ccmu_ptr + i));
+ }
+ p += sprintf(p, "\n");
+
+ for (i = SUNXI_NCCM_SADDR; i < SUNXI_NCCM_EADDR; i += 4) {
+ if (!(i&0xf))
+ p += sprintf(p, "\n0x%p : ", (ccmu_ptr + i));
+ p += sprintf(p, "%08x ", readl(ccmu_ptr + i));
+ }
+ p += sprintf(p, "\n");
+ }
+ p += sprintf(p, "\n");
+
+ iounmap(ccmu_ptr);
+
+out:
+ return p-buf;
+
+}
+
+static ssize_t dump_clk_dly_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ char *p = buf;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct sunxi_mmc_host *host = mmc_priv(mmc);
+
+ if (host->sunxi_mmc_dump_dly_table)
+ host->sunxi_mmc_dump_dly_table(host);
+ else
+ dev_warn(mmc_dev(mmc), "not found the dump dly table\n");
+
+ return p - buf;
+}
+
+int sunxi_mmc_uperf_stat(struct sunxi_mmc_host *host,
+ struct mmc_data *data,
+ struct mmc_request *mrq_busy,
+ bool bhalf)
+{
+ ktime_t diff;
+
+ if (!bhalf) {
+ if (host->perf_enable && data) {
+ diff = ktime_sub(ktime_get(), host->perf.start);
+ if (data->flags & MMC_DATA_READ) {
+ host->perf.rbytes += data->bytes_xfered;
+ host->perf.rtime =
+ ktime_add(host->perf.rtime, diff);
+ } else if (data->flags & MMC_DATA_WRITE) {
+ if (!mrq_busy) {
+ host->perf.wbytes +=
+ data->bytes_xfered;
+ host->perf.wtime =
+ ktime_add(host->perf.wtime, diff);
+ sunxi_mmc_filter_rate(host, data, data->bytes_xfered, ktime_to_us(diff));
+ }
+ host->perf.wbytestran += data->bytes_xfered;
+ host->perf.wtimetran =
+ ktime_add(host->perf.wtimetran, diff);
+ }
+ }
+ } else {
+ if (host->perf_enable
+ && mrq_busy->data
+ && (mrq_busy->data->flags & MMC_DATA_WRITE)) {
+ diff = ktime_sub(ktime_get(), host->perf.start);
+ host->perf.wbytes += mrq_busy->data->bytes_xfered;
+ host->perf.wtime = ktime_add(host->perf.wtime, diff);
+ sunxi_mmc_filter_rate(host, data, data->bytes_xfered, ktime_to_us(diff));
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sunxi_mmc_uperf_stat);
+
+static ssize_t
+sunxi_mmc_show_perf(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);
+ struct sunxi_mmc_host *host = mmc_priv(mmc);
+ int64_t rtime_drv, wtime_drv, wtime_drv_tran;
+ int64_t rbytes_drv, wbytes_drv, wbytes_drv_tran;
+
+
+ mmc_claim_host(mmc);
+
+ rbytes_drv = host->perf.rbytes;
+ wbytes_drv = host->perf.wbytes;
+ wbytes_drv_tran = host->perf.wbytestran;
+
+ rtime_drv = ktime_to_us(host->perf.rtime);
+ wtime_drv = ktime_to_us(host->perf.wtime);
+ wtime_drv_tran = ktime_to_us(host->perf.wtimetran);
+
+ mmc_release_host(mmc);
+
+ return snprintf(buf, PAGE_SIZE, "Write performance at host driver Level:"
+ "%lld bytes in %lld microseconds\n"
+ "Read performance at host driver Level:"
+ "%lld bytes in %lld microseconds\n"
+ "write performance at host driver Level(no wait busy):"
+ "%lld bytes in %lld microseconds\n",
+ wbytes_drv, wtime_drv,
+ rbytes_drv, rtime_drv,
+ wbytes_drv_tran, wtime_drv_tran);
+}
+
+static ssize_t
+sunxi_mmc_set_perf(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);
+ struct sunxi_mmc_host *host = mmc_priv(mmc);
+ int64_t value;
+
+ sscanf(buf, "%lld", &value);
+ printk("set perf value %lld\n", value);
+
+ mmc_claim_host(mmc);
+ if (!value) {
+ memset(&host->perf, 0, sizeof(host->perf));
+ host->perf_enable = false;
+ } else {
+ host->perf_enable = true;
+ }
+ mmc_release_host(mmc);
+
+ return count;
+}
+
+static ssize_t
+sunxi_mmc_show_filter_sector(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);
+ struct sunxi_mmc_host *host = mmc_priv(mmc);
+
+ return snprintf(buf, PAGE_SIZE, "filter speed %d\n", host->filter_sector);
+}
+
+static ssize_t
+sunxi_mmc_set_filter_sector(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);
+ struct sunxi_mmc_host *host = mmc_priv(mmc);
+ int64_t value;
+
+ sscanf(buf, "%lld", &value);
+ printk("get filter sector %lld\n", value);
+ host->filter_sector = value;
+
+ return count;
+}
+
+
+static ssize_t
+sunxi_mmc_show_filter_speed(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);
+ struct sunxi_mmc_host *host = mmc_priv(mmc);
+
+ return snprintf(buf, PAGE_SIZE, "filter speed %d\n", host->filter_speed);
+}
+
+static ssize_t
+sunxi_mmc_set_filter_speed(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);
+ struct sunxi_mmc_host *host = mmc_priv(mmc);
+ int64_t value;
+
+ sscanf(buf, "%lld", &value);
+ printk("get filter speed %lld\n", value);
+ host->filter_speed = value;
+
+ return count;
+}
+
+
+
+
+
+int mmc_create_sys_fs(struct sunxi_mmc_host *host, struct platform_device *pdev)
+{
+ int ret;
+
+ host->maual_insert.show = maual_insert_show;
+ host->maual_insert.store = maual_insert_store;
+ sysfs_attr_init(&(host->maual_insert.attr));
+ host->maual_insert.attr.name = "sunxi_insert";
+ host->maual_insert.attr.mode = S_IRUGO | S_IWUSR;
+ ret = device_create_file(&pdev->dev, &host->maual_insert);
+ if (ret)
+ return ret;
+
+ host->host_perf.show = sunxi_mmc_show_perf;
+ host->host_perf.store = sunxi_mmc_set_perf;
+ sysfs_attr_init(&(host->host_perf.attr));
+ host->host_perf.attr.mode = S_IRUGO | S_IWUSR;
+ host->host_perf.attr.name = "sunxi_host_perf";
+ ret = device_create_file(&pdev->dev, &host->host_perf);
+ if (ret)
+ return ret;
+
+ host->filter_sector_perf.show = sunxi_mmc_show_filter_sector;
+ host->filter_sector_perf.store = sunxi_mmc_set_filter_sector;
+ sysfs_attr_init(&(host->filter_sector_perf.attr));
+ host->filter_sector_perf.attr.name = "sunxi_host_filter_w_sector";
+ host->filter_sector_perf.attr.mode = S_IRUGO | S_IWUSR;
+ ret = device_create_file(&pdev->dev, &host->filter_sector_perf);
+ if (ret)
+ return ret;
+
+ host->filter_speed_perf.show = sunxi_mmc_show_filter_speed;;
+ host->filter_speed_perf.store = sunxi_mmc_set_filter_speed;;
+ sysfs_attr_init(&(host->filter_speed_perf.attr));
+ host->filter_speed_perf.attr.name = "sunxi_host_filter_w_speed";
+ host->filter_speed_perf.attr.mode = S_IRUGO | S_IWUSR;
+ ret = device_create_file(&pdev->dev, &host->filter_speed_perf);
+ if (ret)
+ return ret;
+
+
+ host->dump_register = dump_register;
+ host->dump_register[0].show = dump_host_reg_show;
+ sysfs_attr_init(&(host->dump_register[0].attr));
+ host->dump_register[0].attr.name = "sunxi_dump_host_register";
+ host->dump_register[0].attr.mode = S_IRUGO;
+ ret = device_create_file(&pdev->dev, &host->dump_register[0]);
+ if (ret)
+ return ret;
+
+ host->dump_register[1].show = dump_gpio_reg_show;
+ sysfs_attr_init(&(host->dump_register[1].attr));
+ host->dump_register[1].attr.name = "sunxi_dump_gpio_register";
+ host->dump_register[1].attr.mode = S_IRUGO;
+ ret = device_create_file(&pdev->dev, &host->dump_register[1]);
+ if (ret)
+ return ret;
+
+ host->dump_register[2].show = dump_ccmu_reg_show;
+ sysfs_attr_init(&(host->dump_register[2].attr));
+ host->dump_register[2].attr.name = "sunxi_dump_ccmu_register";
+ host->dump_register[2].attr.mode = S_IRUGO;
+ ret = device_create_file(&pdev->dev, &host->dump_register[2]);
+ if (ret)
+ return ret;
+
+ host->dump_clk_dly.show = dump_clk_dly_show;
+ sysfs_attr_init(&(host->dump_clk_dly.attr));
+ host->dump_clk_dly.attr.name = "sunxi_dump_clk_dly";
+ host->dump_clk_dly.attr.mode = S_IRUGO;
+ ret = device_create_file(&pdev->dev, &host->dump_clk_dly);
+ if (ret)
+ return ret;
+
+ host->host_mwr.show = sunxi_mmc_panic_rtest;
+ host->host_mwr.store = sunxi_mmc_pancic_wrtest;
+ sysfs_attr_init(host->host_mwr.attr);
+ host->host_mwr.attr.name = "sunxi_host_panic_wr";
+ host->host_mwr.attr.mode = S_IRUGO | S_IWUSR;
+ ret = device_create_file(&pdev->dev, &host->host_mwr);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mmc_create_sys_fs);
+
+void mmc_remove_sys_fs(struct sunxi_mmc_host *host,
+ struct platform_device *pdev)
+{
+ device_remove_file(&pdev->dev, &host->host_mwr);
+ device_remove_file(&pdev->dev, &host->host_perf);
+ device_remove_file(&pdev->dev, &host->maual_insert);
+ device_remove_file(&pdev->dev, &host->dump_register[0]);
+ device_remove_file(&pdev->dev, &host->dump_register[1]);
+ device_remove_file(&pdev->dev, &host->dump_register[2]);
+ device_remove_file(&pdev->dev, &host->dump_clk_dly);
+ device_remove_file(&pdev->dev, &host->filter_sector_perf);
+ device_remove_file(&pdev->dev, &host->filter_speed_perf);
+}
+EXPORT_SYMBOL_GPL(mmc_remove_sys_fs);