mirror of https://github.com/OpenIPC/firmware.git
				
				
				
			
		
			
				
	
	
		
			276 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Diff
		
	
	
			
		
		
	
	
			276 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Diff
		
	
	
| --- linux-4.9.37/drivers/mmc/host/sdhci-proc.c	1970-01-01 03:00:00.000000000 +0300
 | |
| +++ linux-4.9.y/drivers/mmc/host/sdhci-proc.c	2021-06-07 13:01:33.000000000 +0300
 | |
| @@ -0,0 +1,272 @@
 | |
| +/*
 | |
| + * Copyright (c) Hunan Goke,Chengdu Goke,Shandong Goke. 2021. All rights reserved.
 | |
| + */
 | |
| +
 | |
| +#include <linux/proc_fs.h>
 | |
| +#include <linux/seq_file.h>
 | |
| +#include <linux/device.h>
 | |
| +#include <linux/io.h>
 | |
| +#include <linux/platform_device.h>
 | |
| +#include <linux/mmc/card.h>
 | |
| +#include <linux/mmc/host.h>
 | |
| +#include "sdhci.h"
 | |
| +#include "sdhci-proc.h"
 | |
| +
 | |
| +#define MCI_PARENT       "mci"
 | |
| +#define MCI_STATS_PROC   "mci_info"
 | |
| +#define MAX_CLOCK_SCALE  4
 | |
| +
 | |
| +unsigned int slot_index;
 | |
| +struct mmc_host *mci_host[MCI_SLOT_NUM] = {NULL};
 | |
| +static struct proc_dir_entry *proc_mci_dir;
 | |
| +
 | |
| +static char *card_type[MAX_CARD_TYPE + 1] = {
 | |
| +	"MMC card",
 | |
| +	"SD card",
 | |
| +	"SDIO card",
 | |
| +	"SD combo (IO+mem) card",
 | |
| +	"unknown"
 | |
| +};
 | |
| +static char *clock_unit[MAX_CLOCK_SCALE] = {
 | |
| +	"Hz",
 | |
| +	"KHz",
 | |
| +	"MHz",
 | |
| +	"GHz"
 | |
| +};
 | |
| +
 | |
| +static char *mci_get_card_type(unsigned int sd_type)
 | |
| +{
 | |
| +	if (sd_type >= MAX_CARD_TYPE)
 | |
| +		return card_type[MAX_CARD_TYPE];
 | |
| +	else
 | |
| +		return card_type[sd_type];
 | |
| +}
 | |
| +
 | |
| +static unsigned int analyze_clock_scale(unsigned int clock, unsigned int *clock_val)
 | |
| +{
 | |
| +	unsigned int scale = 0;
 | |
| +	unsigned int tmp = clock;
 | |
| +
 | |
| +	while (1) {
 | |
| +		tmp = tmp / 1000; /* 1000 for clk calculate */
 | |
| +		if (tmp > 0) {
 | |
| +			*clock_val = tmp;
 | |
| +			scale++;
 | |
| +		} else {
 | |
| +			break;
 | |
| +		}
 | |
| +	}
 | |
| +	return scale;
 | |
| +}
 | |
| +
 | |
| +static inline int is_card_uhs(unsigned char timing)
 | |
| +{
 | |
| +	return timing >= MMC_TIMING_UHS_SDR12 &&
 | |
| +		timing <= MMC_TIMING_UHS_DDR50;
 | |
| +};
 | |
| +
 | |
| +static inline int is_card_hs(unsigned char timing)
 | |
| +{
 | |
| +	return timing == MMC_TIMING_SD_HS || timing == MMC_TIMING_MMC_HS;
 | |
| +};
 | |
| +
 | |
| +static void mci_stats_seq_printout(struct seq_file *s)
 | |
| +{
 | |
| +	unsigned int index_mci;
 | |
| +	unsigned int clock;
 | |
| +	unsigned int clock_scale;
 | |
| +	unsigned int clock_value = 0;
 | |
| +	const char *type = NULL;
 | |
| +	static struct mmc_host *mmc = NULL;
 | |
| +	const char *uhs_bus_speed_mode = "";
 | |
| +	static const char *uhs_speeds[] = {
 | |
| +		[UHS_SDR12_BUS_SPEED] = "SDR12 ",
 | |
| +		[UHS_SDR25_BUS_SPEED] = "SDR25 ",
 | |
| +		[UHS_SDR50_BUS_SPEED] = "SDR50 ",
 | |
| +		[UHS_SDR104_BUS_SPEED] = "SDR104 ",
 | |
| +		[UHS_DDR50_BUS_SPEED] = "DDR50 ",
 | |
| +	};
 | |
| +	unsigned int speed_class, grade_speed_uhs;
 | |
| +	struct card_info *info = NULL;
 | |
| +	unsigned int present;
 | |
| +	struct sdhci_host *host = NULL;
 | |
| +
 | |
| +	for (index_mci = 0; index_mci < MCI_SLOT_NUM; index_mci++) {
 | |
| +		mmc = mci_host[index_mci];
 | |
| +		if (mmc == NULL) {
 | |
| +			seq_printf(s, "MCI%d: invalid\n", index_mci);
 | |
| +			continue;
 | |
| +		} else {
 | |
| +			seq_printf(s, "MCI%d", index_mci);
 | |
| +		}
 | |
| +		host = mmc_priv(mmc);
 | |
| +		info = &host->c_info;
 | |
| +
 | |
| +		present = host->mmc->ops->get_cd(host->mmc);
 | |
| +		if (present)
 | |
| +			seq_puts(s, ": pluged");
 | |
| +		else
 | |
| +			seq_puts(s, ": unplugged");
 | |
| +
 | |
| +		if (info->card_connect != CARD_CONNECT) {
 | |
| +			if (mmc->card_status == MMC_CARD_INIT_FAIL)
 | |
| +				seq_puts(s, "_init_failed\n");
 | |
| +			else
 | |
| +				seq_puts(s, "_disconnected\n");
 | |
| +		} else {
 | |
| +			seq_puts(s, "_connected\n");
 | |
| +
 | |
| +			seq_printf(s, "\tType: %s",
 | |
| +					mci_get_card_type(info->card_type));
 | |
| +
 | |
| +			if (info->card_state & MMC_STATE_BLOCKADDR) {
 | |
| +				if (info->card_state & MMC_CARD_SDXC)
 | |
| +					type = "SDXC";
 | |
| +				else
 | |
| +					type = "SDHC";
 | |
| +				seq_printf(s, "(%s)\n", type);
 | |
| +			}
 | |
| +
 | |
| +			if (is_card_uhs(info->timing) &&
 | |
| +					info->sd_bus_speed < ARRAY_SIZE(uhs_speeds))
 | |
| +				uhs_bus_speed_mode =
 | |
| +					uhs_speeds[info->sd_bus_speed];
 | |
| +
 | |
| +			seq_printf(s, "\tMode: %s%s%s%s\n",
 | |
| +					is_card_uhs(info->timing) ? "UHS " :
 | |
| +					(is_card_hs(info->timing) ? "HS " : ""),
 | |
| +					info->timing == MMC_TIMING_MMC_HS400 ? "HS400 " :
 | |
| +					(info->timing == MMC_TIMING_MMC_HS200 ? "HS200 " : ""),
 | |
| +					info->timing == MMC_TIMING_MMC_DDR52 ? "DDR " : "",
 | |
| +					uhs_bus_speed_mode);
 | |
| +
 | |
| +			speed_class = UNSTUFF_BITS(info->ssr, 56, 8); /* 56 equal 440 -384 */
 | |
| +			grade_speed_uhs = UNSTUFF_BITS(info->ssr, 12, 4); /* 12 equal 396 - 384 */
 | |
| +			seq_printf(s, "\tSpeed Class: Class %s\n",
 | |
| +					(speed_class == 0x00) ? "0" :
 | |
| +					(speed_class == 0x01) ? "2" :
 | |
| +					(speed_class == 0x02) ? "4" :
 | |
| +					(speed_class == 0x03) ? "6" :
 | |
| +					(speed_class == 0x04) ? "10" :
 | |
| +					"Reserved");
 | |
| +			seq_printf(s, "\tUhs Speed Grade: %s\n",
 | |
| +					(grade_speed_uhs == 0x00) ?
 | |
| +					"Less than 10MB/sec(0h)" :
 | |
| +					(grade_speed_uhs == 0x01) ?
 | |
| +					"10MB/sec and above(1h)" :
 | |
| +					"Reserved");
 | |
| +
 | |
| +			clock = info->card_support_clock;
 | |
| +			clock_scale = analyze_clock_scale(clock, &clock_value);
 | |
| +			seq_printf(s, "\tHost work clock: %d%s\n",
 | |
| +					clock_value, clock_unit[clock_scale]);
 | |
| +
 | |
| +			clock = info->card_support_clock;
 | |
| +			clock_scale = analyze_clock_scale(clock, &clock_value);
 | |
| +			seq_printf(s, "\tCard support clock: %d%s\n",
 | |
| +					clock_value, clock_unit[clock_scale]);
 | |
| +
 | |
| +			clock = mmc->actual_clock;
 | |
| +			clock_scale = analyze_clock_scale(clock, &clock_value);
 | |
| +			seq_printf(s, "\tCard work clock: %d%s\n",
 | |
| +					clock_value, clock_unit[clock_scale]);
 | |
| +
 | |
| +			/* add card read/write error count */
 | |
| +			seq_printf(s, "\tCard error count: %d\n",
 | |
| +					host->error_count);
 | |
| +		}
 | |
| +	}
 | |
| +}
 | |
| +
 | |
| +/* proc interface setup */
 | |
| +static void *mci_seq_start(struct seq_file *s, loff_t *pos)
 | |
| +{
 | |
| +	/*   counter is used to tracking multi proc interfaces
 | |
| +	 *  We have only one interface so return zero
 | |
| +	 *  pointer to start the sequence.
 | |
| +	 */
 | |
| +	static unsigned long counter;
 | |
| +
 | |
| +	if (*pos == 0)
 | |
| +		return &counter;
 | |
| +
 | |
| +	*pos = 0;
 | |
| +	return NULL;
 | |
| +}
 | |
| +
 | |
| +/* proc interface next */
 | |
| +static void *mci_seq_next(struct seq_file *s, void *v, loff_t *pos)
 | |
| +{
 | |
| +	(*pos)++;
 | |
| +	if (*pos >= MCI_SLOT_NUM)
 | |
| +		return NULL;
 | |
| +
 | |
| +	return NULL;
 | |
| +}
 | |
| +
 | |
| +/* define parameters where showed in proc file */
 | |
| +static int mci_stats_seq_show(struct seq_file *s, void *v)
 | |
| +{
 | |
| +	mci_stats_seq_printout(s);
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +/* proc interface stop */
 | |
| +static void mci_seq_stop(struct seq_file *s, void *v)
 | |
| +{
 | |
| +}
 | |
| +
 | |
| +/* proc interface operation */
 | |
| +static const struct seq_operations mci_stats_seq_ops = {
 | |
| +	.start = mci_seq_start,
 | |
| +	.next = mci_seq_next,
 | |
| +	.stop = mci_seq_stop,
 | |
| +	.show = mci_stats_seq_show
 | |
| +};
 | |
| +
 | |
| +/* proc file open */
 | |
| +static int mci_stats_proc_open(struct inode *inode, struct file *file)
 | |
| +{
 | |
| +	return seq_open(file, &mci_stats_seq_ops);
 | |
| +};
 | |
| +
 | |
| +/* proc file operation */
 | |
| +static const struct file_operations mci_stats_proc_ops = {
 | |
| +	.owner = THIS_MODULE,
 | |
| +	.open = mci_stats_proc_open,
 | |
| +	.read = seq_read,
 | |
| +	.release = seq_release
 | |
| +};
 | |
| +
 | |
| +int mci_proc_init(void)
 | |
| +{
 | |
| +	struct proc_dir_entry *proc_stats_entry = NULL;
 | |
| +
 | |
| +	proc_mci_dir = proc_mkdir(MCI_PARENT, NULL);
 | |
| +	if (!proc_mci_dir) {
 | |
| +		pr_err("%s: failed to create proc file %s\n",
 | |
| +				__func__, MCI_PARENT);
 | |
| +		return 1;
 | |
| +	}
 | |
| +
 | |
| +	proc_stats_entry = proc_create(MCI_STATS_PROC,
 | |
| +			0, proc_mci_dir, &mci_stats_proc_ops);
 | |
| +	if (!proc_stats_entry) {
 | |
| +		pr_err("%s: failed to create proc file %s\n",
 | |
| +				__func__, MCI_STATS_PROC);
 | |
| +		return 1;
 | |
| +	}
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +int mci_proc_shutdown(void)
 | |
| +{
 | |
| +	if (proc_mci_dir) {
 | |
| +		remove_proc_entry(MCI_STATS_PROC, proc_mci_dir);
 | |
| +		remove_proc_entry(MCI_PARENT, NULL);
 | |
| +		proc_mci_dir = NULL;
 | |
| +	}
 | |
| +
 | |
| +	return 0;
 | |
| +}
 |