/* * Sunxi SD/MMC host driver * * Copyright (C) 2015 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 "sunxi-mmc.h" #include "sunxi-mmc-debug.h" #include "sunxi-mmc-export.h" #include "sunxi-mmc-sun50iw1p1-2.h" #include "sunxi-mmc-panic.h" #include #include #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*/ #ifdef CONFIG_ARCH_SUN50IW11 #define SUNXI_PP_NCM_SADDR 0x1010 #define SUNXI_PP_NCM_EADDR 0x101C #else #define SUNXI_PP_NCM_EADDR 0x2C #define SUNXI_PP_NCM_SADDR 0x20 #endif #define SUNXI_DEG_MAX_MAP_REG 0x900 #define SUNXI_DEG_PRCM_MAP_REG 0x1100 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(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; u32 caps2_old = 0; 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; } mmc_claim_host(mmc); dev_info(dev, "insert %ld\n", insert); if (insert) mmc_detect_change(mmc, 0); else { caps2_old = mmc->caps2; if (mmc->card == NULL) { dev_info(dev, "the device has been removed\n"); mmc_release_host(mmc); return 0; } mmc_card_set_removed(mmc->card); mmc->caps2 |= MMC_CAP2_NO_SDIO | MMC_CAP2_NO_SD | MMC_CAP2_NO_MMC; mmc_detect_change(mmc, 0); mdelay(300); mmc->caps2 = caps2_old; dev_info(dev, "remove card\n"); } mmc_release_host(mmc); ret = count; return ret; } int sunxi_mmc_res_start_addr(const char * const res_str, resource_size_t *res_addr, int index) { 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, index, &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; #ifdef CONFIG_ARCH_SUN50IW11 void __iomem *pll_ptr = NULL; resource_size_t res_saddr_pll; #endif resource_size_t res_saddr_ccmu; resource_size_t res_saddr_gpio; ret = sunxi_mmc_res_start_addr("clocks", &res_saddr_ccmu, 0); 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; } #ifdef CONFIG_ARCH_SUN50IW11 ret = sunxi_mmc_res_start_addr("clocks", &res_saddr_pll, 1); if (ret < 0) return; pll_ptr = ioremap(res_saddr_pll, SUNXI_DEG_MAX_MAP_REG); if (pll_ptr == NULL) { pr_err("Can not map pll resource\n"); return; } #endif ret = sunxi_mmc_res_start_addr("pio", &res_saddr_gpio, 0); if (ret < 0) return; gpio_ptr = ioremap(res_saddr_gpio, SUNXI_DEG_PRCM_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"); #ifdef CONFIG_ARCH_SUN50IW11 for (i = SUNXI_PP_NCM_SADDR; i < SUNXI_PP_NCM_EADDR; i += 4) { if (!(i&0xf)) pr_cont("\n0x%p : ", (pll_ptr + i)); pr_cont("%08x ", readl(pll_ptr + i)); } pr_cont("\n"); #else 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"); #endif 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); #ifdef CONFIG_ARCH_SUN50IW11 iounmap(pll_ptr); #endif } 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, 0); 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; #ifdef CONFIG_ARCH_SUN50IW11 void __iomem *pll_ptr = NULL; resource_size_t res_saddr_pll; #endif ret = sunxi_mmc_res_start_addr("clocks", &res_saddr_ccmu, 0); 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; } #ifdef CONFIG_ARCH_SUN50IW11 ret = sunxi_mmc_res_start_addr("clocks", &res_saddr_pll, 1); if (ret < 0) goto out; pll_ptr = ioremap(res_saddr_pll, SUNXI_DEG_PRCM_MAP_REG); if (!pll_ptr) { pr_err("Can not map pll resource\n"); goto out; } #endif 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"); #ifdef CONFIG_ARCH_SUN50IW11 for (i = SUNXI_PP_NCM_SADDR; i < SUNXI_PP_NCM_EADDR; i += 4) { if (!(i&0xf)) p += sprintf(p, "\n0x%p : ", (pll_ptr + i)); p += sprintf(p, "%08x ", readl(pll_ptr + i)); } p += sprintf(p, "\n"); #else 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"); #endif 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); #ifdef CONFIG_ARCH_SUN50IW11 iounmap(pll_ptr); #endif 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; } static ssize_t sunxi_detect_pin_status(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); int value; value = mmc_gpio_get_cd(mmc); if (value < 0) { dev_err(dev, "gpio get value fail\n"); } else { dev_info(dev, "%d\n", value); } return sprintf(buf, "%d\n", value); } static ssize_t sunxi_card_power_gpio_get_value(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); int value = -1; if (gpio_is_valid(host->card_pwr_gpio)) { value = gpio_get_value(host->card_pwr_gpio); if (value < 0) { dev_err(dev, "gpio get value fail\n"); } else { if (value == 0) { if (host->ctl_spec_cap & CARD_PWR_GPIO_HIGH_ACTIVE) { dev_info(dev, "power off\n"); return sprintf(buf, "gpio value = %d, power off\n", value); } else { dev_info(dev, "power on\n"); return sprintf(buf, "gpio value = %d, power on\n", value); } } else if (value == 1) { if (host->ctl_spec_cap & CARD_PWR_GPIO_HIGH_ACTIVE) { dev_info(dev, "power on\n"); return sprintf(buf, "gpio value = %d, power on\n", value); } else { dev_info(dev, "power off\n"); return sprintf(buf, "gpio value = %d, power off\n", value); } } else { dev_err(dev, "gpio get value is illegal, value = %d\n", value); } } } else { dev_err(dev, "card_pwr_gpio get fail\n"); } return sprintf(buf, "fail\n"); } static ssize_t sunxi_card_power_gpio_set_value(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; if (!gpio_is_valid(host->card_pwr_gpio)) { dev_err(dev, "error: card_pwr_gpio is invalid\n"); return -1; } if (host->card_pwr_mode != CARD_PWR_GPIO_MANUAL) { dev_err(dev, "error: need manual mode\n"); return -1; } mmc_claim_host(mmc); sscanf(buf, "%lld", &value); dev_info(dev, "get gpio value %lld\n", value); if (value == 0) { gpio_set_value(host->card_pwr_gpio, (host-> ctl_spec_cap & CARD_PWR_GPIO_HIGH_ACTIVE) ? 0 : 1); msleep(host->time_pwroff_ms); } else if (value == 1) { gpio_set_value(host->card_pwr_gpio, (host-> ctl_spec_cap & CARD_PWR_GPIO_HIGH_ACTIVE) ? 0 : 1); msleep(host->time_pwroff_ms); gpio_set_value(host->card_pwr_gpio, (host-> ctl_spec_cap & CARD_PWR_GPIO_HIGH_ACTIVE) ? 1 : 0); /* delay to ensure voltage stability */ msleep(1); } else { dev_err(dev, "invalid input\n"); } mmc_release_host(mmc); return count; } static ssize_t sunxi_card_power_gpio_get_mode(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); if (host->card_pwr_mode == CARD_PWR_GPIO_AUTO) { dev_info(dev, "auto mode\n"); } else if (host->card_pwr_mode == CARD_PWR_GPIO_MANUAL) { dev_info(dev, "manual mode\n"); } else { dev_err(dev, "unknown mode\n"); } return sprintf(buf, "%d\n", host->card_pwr_mode); } static ssize_t sunxi_card_power_gpio_set_mode(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; if (!gpio_is_valid(host->card_pwr_gpio)) { dev_err(dev, "error: card_pwr_gpio is invalid\n"); return -1; } sscanf(buf, "%lld", &value); dev_info(dev, "get mode value %lld\n", value); mmc_claim_host(mmc); if (value == CARD_PWR_GPIO_AUTO) { host->card_pwr_mode = CARD_PWR_GPIO_AUTO; } else if (value == CARD_PWR_GPIO_MANUAL) { host->card_pwr_mode = CARD_PWR_GPIO_MANUAL; } else { dev_err(dev, "set mode err: unknown mode\n"); } mmc_claim_host(mmc); return count; } 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; #if IS_ENABLED(CONFIG_PSTORE_BLK) 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); #endif host->sd_detect_pin_status.show = sunxi_detect_pin_status; sysfs_attr_init(&(host->sd_detect_pin_status.attr)); host->sd_detect_pin_status.attr.name = "sunxi_sd_detect_pin_status"; host->sd_detect_pin_status.attr.mode = S_IRUGO; ret = device_create_file(&pdev->dev, &host->sd_detect_pin_status); if (ret) return ret; host->card_pwr_gpio_cfg.show = sunxi_card_power_gpio_get_value; host->card_pwr_gpio_cfg.store = sunxi_card_power_gpio_set_value; sysfs_attr_init(&(host->card_pwr_gpio_cfg.attr)); host->card_pwr_gpio_cfg.attr.name = "sunxi_card_power_gpio_value"; host->card_pwr_gpio_cfg.attr.mode = S_IRUGO | S_IWUSR; ret = device_create_file(&pdev->dev, &host->card_pwr_gpio_cfg); if (ret) return ret; host->card_pwr_gpio_mode.show = sunxi_card_power_gpio_get_mode; host->card_pwr_gpio_mode.store = sunxi_card_power_gpio_set_mode; sysfs_attr_init(&(host->card_pwr_gpio_mode.attr)); host->card_pwr_gpio_mode.attr.name = "sunxi_card_power_gpio_mode"; host->card_pwr_gpio_mode.attr.mode = S_IRUGO | S_IWUSR; ret = device_create_file(&pdev->dev, &host->card_pwr_gpio_mode); if (ret) return ret; return ret; } EXPORT_SYMBOL_GPL(mmc_create_sys_fs); void mmc_remove_sys_fs(struct sunxi_mmc_host *host, struct platform_device *pdev) { #if IS_ENABLED(CONFIG_PSTORE_BLK) device_remove_file(&pdev->dev, &host->host_mwr); #endif 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); device_remove_file(&pdev->dev, &host->sd_detect_pin_status); device_remove_file(&pdev->dev, &host->card_pwr_gpio_cfg); device_remove_file(&pdev->dev, &host->card_pwr_gpio_mode); } EXPORT_SYMBOL_GPL(mmc_remove_sys_fs);