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;
|
|
+}
|