[Feature][T8TSK-48][TCAM_T800_SW_261] add emmc health interface

Change-Id: I23caddce7d76c067a3661d7cfc29c027668fc2fa
diff --git a/src/kernel/linux/v4.19/arch/arm64/configs/auto2735evb_defconfig b/src/kernel/linux/v4.19/arch/arm64/configs/auto2735evb_defconfig
index cb84b4f..335e5da 100644
--- a/src/kernel/linux/v4.19/arch/arm64/configs/auto2735evb_defconfig
+++ b/src/kernel/linux/v4.19/arch/arm64/configs/auto2735evb_defconfig
@@ -807,3 +807,7 @@
 #dongyu@2022.7.12 modify for RTC/PCF85063 start
 CONFIG_RTC_DRV_PCF85063=y
 #dongyu@2022.7.12 modify for RTC/PCF85063 end
+
+#you.chen@2022-07-16 for emmc health 
+CONFIG_MMC_HEALTH=y
+
diff --git a/src/kernel/linux/v4.19/drivers/mmc/core/block.c b/src/kernel/linux/v4.19/drivers/mmc/core/block.c
index 87cfd13..e5566bd 100644
--- a/src/kernel/linux/v4.19/drivers/mmc/core/block.c
+++ b/src/kernel/linux/v4.19/drivers/mmc/core/block.c
@@ -139,6 +139,11 @@
 	/* debugfs files (only in main mmc_blk_data) */
 	struct dentry *status_dentry;
 	struct dentry *ext_csd_dentry;
+//you.chen@2022-07-16 for emmc health begin
+#ifdef CONFIG_MMC_HEALTH
+	struct dentry *health_dentry;
+#endif
+//you.chen@2022-07-16 for emmc health end
 };
 
 /* Device type for RPMB character devices */
@@ -1079,6 +1084,14 @@
 		ext_csd = mq_rq->drv_op_data;
 		ret = mmc_get_ext_csd(card, ext_csd);
 		break;
+//you.chen@2022-07-16 for emmc health begin
+#ifdef CONFIG_MMC_HEALTH
+	case MMC_DRV_OP_GET_HEALTH:
+		ext_csd = mq_rq->drv_op_data;
+		ret = mmc_get_health(card, ext_csd);
+		break;
+#endif
+//you.chen@2022-07-16 for emmc health end 
 	default:
 		pr_err("%s: unknown driver specific operation\n",
 		       md->disk->disk_name);
@@ -3074,6 +3087,96 @@
 	.llseek		= default_llseek,
 };
 
+//you.chen@2022-07-16 for emmc health begin
+#ifdef CONFIG_MMC_HEALTH
+#define HEALTH_STR_LEN 14
+static int mmc_health_open(struct inode *inode, struct file *filp)
+{
+	struct mmc_card *card = inode->i_private;
+	struct mmc_blk_data *md = dev_get_drvdata(&card->dev);
+	struct mmc_queue *mq = &md->queue;
+	struct request *req;
+	char *buf;
+	u8 *ext_csd;
+	int err;
+	ssize_t n;
+	unsigned int amount, health;
+
+	//printk("cy-health open\n");
+	buf = kmalloc(EXT_CSD_STR_LEN + 1, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+	
+	/* Ask the block layer for the EXT CSD */
+	req = blk_get_request(mq->queue, REQ_OP_DRV_IN, 0);
+	if (IS_ERR(req)) {
+		err = PTR_ERR(req);
+		goto out_free;
+	}
+	req_to_mmc_queue_req(req)->drv_op = MMC_DRV_OP_GET_HEALTH;
+	req_to_mmc_queue_req(req)->drv_op_data = &ext_csd;
+	//printk("cy- blk_execute_rq begin\n");
+	blk_execute_rq(mq->queue, NULL, req, 0);
+	//printk("cy- blk_execute_rq end\n");
+	err = req_to_mmc_queue_req(req)->drv_op_result;
+	//printk("cy-blk_put_request begin\n");
+	blk_put_request(req);
+	//printk("cy-blk_put_request end\n");
+	if (err) {
+		pr_err("FAILED %d\n", err);
+		goto out_free;
+	}
+
+	memcpy(&amount, ext_csd + 64, 4);
+	memcpy(&health, ext_csd + 184, 4);
+	if (health > 100) {
+		pr_err("Health [%d] large than 100\n", health);
+		health = 100;
+	}
+
+	n = sprintf(buf, "%010u-%03u", amount*100, health);
+
+	if (n != HEALTH_STR_LEN) {
+		printk("cy-bad count %d , %d\n", (int)n , HEALTH_STR_LEN);
+		err = -EINVAL;
+		kfree(ext_csd);
+		goto out_free;
+	}
+
+	filp->private_data = buf;
+	kfree(ext_csd);
+	return 0;
+
+out_free:
+	kfree(buf);
+	return err;
+}
+
+static ssize_t mmc_health_read(struct file *filp, char __user *ubuf,
+                                size_t cnt, loff_t *ppos)
+{
+	char *buf = filp->private_data;
+
+	//printk("cy-mmc_ext_csd_read\n");
+	return simple_read_from_buffer(ubuf, cnt, ppos,
+                                       buf, HEALTH_STR_LEN);
+}
+
+static int mmc_health_release(struct inode *inode, struct file *file)
+{
+	kfree(file->private_data);
+	return 0;
+}
+
+static const struct file_operations mmc_dbg_health_fops = {
+	.open		= mmc_health_open,
+	.read		= mmc_health_read,
+	.release	= mmc_health_release,
+	.llseek		= default_llseek,
+};
+#endif
+//you.chen@2022-07-16 for emmc health end
+
 static int mmc_blk_add_debugfs(struct mmc_card *card, struct mmc_blk_data *md)
 {
 	struct dentry *root;
@@ -3099,6 +3202,15 @@
 			return -EIO;
 	}
 
+//you.chen@2022-07-16 for emmc health begin
+#ifdef CONFIG_MMC_HEALTH
+	if (mmc_card_mmc(card)) {
+		md->health_dentry=
+			debugfs_create_file("health", S_IRUSR, root, card,
+					    &mmc_dbg_health_fops);
+	}
+#endif
+//you.chen@2022-07-16 for emmc health end
 	return 0;
 }
 
@@ -3117,6 +3229,15 @@
 		debugfs_remove(md->ext_csd_dentry);
 		md->ext_csd_dentry = NULL;
 	}
+
+//you.chen@2022-07-16 for emmc health begin
+#ifdef CONFIG_MMC_HEALTH
+	if (!IS_ERR_OR_NULL(md->health_dentry)) {
+		debugfs_remove(md->health_dentry);
+		md->health_dentry= NULL;
+	}
+#endif
+//you.chen@2022-07-16 for emmc health end
 }
 
 #else
diff --git a/src/kernel/linux/v4.19/drivers/mmc/core/mmc_ops.c b/src/kernel/linux/v4.19/drivers/mmc/core/mmc_ops.c
index 873b2aa..fa04e2f 100644
--- a/src/kernel/linux/v4.19/drivers/mmc/core/mmc_ops.c
+++ b/src/kernel/linux/v4.19/drivers/mmc/core/mmc_ops.c
@@ -381,6 +381,60 @@
 }
 EXPORT_SYMBOL_GPL(mmc_get_ext_csd);
 
+//you.chen@2022-07-16 for emmc health begin
+#ifdef CONFIG_MMC_HEALTH
+int mmc_get_health(struct mmc_card *card, u8 **new_health)
+{
+	int err;
+	u8 *ext_csd;
+
+	struct mmc_host *host = card->host;
+	struct mmc_command cmd = {};
+
+
+	if (!card || !new_health)
+	return -EINVAL;
+
+
+	/*
+	 * As the ext_csd is so large and mostly unused, we don't store the
+	 * raw block in mmc_card.
+	 */
+	ext_csd = kzalloc(512, GFP_KERNEL);
+	if (!ext_csd)
+		return -ENOMEM;
+
+	mmc_retune_hold(host);
+
+	cmd.opcode = 62;
+	cmd.arg = 0x96C9D71C;
+	cmd.flags = MMC_CMD_AC;
+	cmd.flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B;
+	cmd.busy_timeout = 1000;
+
+	err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
+	if (err)
+	{
+		//printk("cy2-return error 62\n");
+		mmc_retune_release(host);
+		return err;
+	}
+
+	err = mmc_send_cxd_data(card, card->host, 63, ext_csd,
+                                512);
+	if (err)
+		kfree(ext_csd);
+	else
+		*new_health = ext_csd;
+
+	mmc_retune_release(host);
+	//printk("cy2-return\n");
+	return err;
+}
+EXPORT_SYMBOL_GPL(mmc_get_health);
+#endif
+//you.chen@2022-07-16 for emmc health end
+
 int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp)
 {
 	struct mmc_command cmd = {};
diff --git a/src/kernel/linux/v4.19/drivers/mmc/core/mmc_ops.h b/src/kernel/linux/v4.19/drivers/mmc/core/mmc_ops.h
index a1390d4..0bd4025 100644
--- a/src/kernel/linux/v4.19/drivers/mmc/core/mmc_ops.h
+++ b/src/kernel/linux/v4.19/drivers/mmc/core/mmc_ops.h
@@ -33,6 +33,11 @@
 int mmc_interrupt_hpi(struct mmc_card *card);
 int mmc_can_ext_csd(struct mmc_card *card);
 int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd);
+//you.chen@2022-07-16 for emmc health begin
+#ifdef CONFIG_MMC_HEALTH
+int mmc_get_health(struct mmc_card *card, u8 **new_heath);
+#endif
+//you.chen@2022-07-16 for emmc health end
 int mmc_switch_status(struct mmc_card *card);
 int __mmc_switch_status(struct mmc_card *card, bool crc_err_fatal);
 int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
diff --git a/src/kernel/linux/v4.19/drivers/mmc/core/queue.h b/src/kernel/linux/v4.19/drivers/mmc/core/queue.h
index 9bf3c92..32ab77f 100644
--- a/src/kernel/linux/v4.19/drivers/mmc/core/queue.h
+++ b/src/kernel/linux/v4.19/drivers/mmc/core/queue.h
@@ -59,6 +59,11 @@
 	MMC_DRV_OP_BOOT_WP,
 	MMC_DRV_OP_GET_CARD_STATUS,
 	MMC_DRV_OP_GET_EXT_CSD,
+//you.chen@2022-07-16 for emmc health begin
+#ifdef CONFIG_MMC_HEALTH
+	MMC_DRV_OP_GET_HEALTH,
+#endif
+//you.chen@2022-07-16 for emmc health end
 };
 
 struct mmc_queue_req {
diff --git a/src/kernel/linux/v4.19/drivers/mmc/host/Kconfig b/src/kernel/linux/v4.19/drivers/mmc/host/Kconfig
index cae1c5a..43ea78c 100644
--- a/src/kernel/linux/v4.19/drivers/mmc/host/Kconfig
+++ b/src/kernel/linux/v4.19/drivers/mmc/host/Kconfig
@@ -952,3 +952,10 @@
 	  If you have a controller with this interface, say Y or M here.
 
 	  If unsure, say N.
+
+config MMC_HEALTH
+    tristate "SD/MMC Card Health Interface support"
+	depends on MMC_MTK
+    help
+      This selects the card health Interface.
+