sdk-hwV1.3/lichee/linux-4.9/drivers/mmc/host/sunxi-mmc-debug.c

972 lines
26 KiB
C
Raw Normal View History

2024-05-07 10:09:20 +00:00
/*
* 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"
#include <linux/mmc/card.h>
#include <linux/delay.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*/
#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);