From patchwork Fri Oct 13 08:32:54 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: liuchang_125125@163.com X-Patchwork-Id: 13420423 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 93AA4CDB47E for ; Fri, 13 Oct 2023 08:33:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230080AbjJMIdR (ORCPT ); Fri, 13 Oct 2023 04:33:17 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53108 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230048AbjJMIdQ (ORCPT ); Fri, 13 Oct 2023 04:33:16 -0400 Received: from m15.mail.163.com (m15.mail.163.com [45.254.50.219]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id D6738B8; Fri, 13 Oct 2023 01:33:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:Subject:Date:Message-Id:MIME-Version; bh=gLlXF jKTiiIs5lXq19j2GudYTxs8nyTJiUwjN1lbkPE=; b=QTicBGFrsAwwaMl9UXZ5M XaRi1FjW71Fp5fIuJJupS6LLbb3qZc4fGCm5VygD5SA0ff8onxyMWOLMP8l6c7kP 4ZMXDKFgqY7Muk3O25Anc2bcATKQOUBvvL4Viqw3HEGv8BbYRulUKTiRGKMcoPZs NN4wsOM1iSayqvUdSvVS+0= Received: from test-Z390-GAMING-X.bayhubtech.com (unknown [58.48.115.170]) by zwqz-smtp-mta-g0-1 (Coremail) with SMTP id _____wDX3425ACll2Aa7AQ--.10191S2; Fri, 13 Oct 2023 16:32:58 +0800 (CST) From: liuchang_125125@163.com To: jejb@linux.ibm.com, martin.petersen@oracle.com, linux-scsi@vger.kernel.org, linux-kernel@vger.kernel.org Cc: mark.tao@bayhubtech.com, shaper.liu@bayhubtech.com, thomas.hu@bayhubtech.com, chevron.li@bayhubtech.com, charl.liu@bayhubtech.com, Charl Liu Subject: [PATCH 1/9] scsi: Update Kconfig and Makefile for supporting Bayhub's SD/MMC Card interface driver Date: Fri, 13 Oct 2023 16:32:54 +0800 Message-Id: <20231013083254.10244-1-liuchang_125125@163.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 X-CM-TRANSID: _____wDX3425ACll2Aa7AQ--.10191S2 X-Coremail-Antispam: 1Uf129KBjvJXoW7Zr4xWr48Ww1fAFyxAFykAFb_yoW8GF15pF Z5J34DA3yDXayFkrZrG3yUWFy5ta97t34Y9ayUX3s8WFy8CFyYvrnrtFy7JFWkGr4kJry3 tFnxGa42ga4UJrUanT9S1TB71UUUUUUqnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x0p_-eOUUUUUU= X-Originating-IP: [58.48.115.170] X-CM-SenderInfo: polxux5dqjsiqsvrjki6rwjhhfrp/xtbB0BsIWWEssysXgQAAsZ Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org From: Charl Liu 1.update Kconfig for loading Bayhub's Kconfig 2.update Makefile for compiling Bayhub's module Signed-off-by: Charl Liu --- Change in V1: Update Kconfig and Makefile for supporting Bayhub's SD/MMC Card interface driver. --- drivers/scsi/Kconfig | 1 + drivers/scsi/Makefile | 2 ++ 2 files changed, 3 insertions(+) diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 695a57d894cd..15e94b5fdb25 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -490,6 +490,7 @@ source "drivers/scsi/megaraid/Kconfig.megaraid" source "drivers/scsi/mpt3sas/Kconfig" source "drivers/scsi/mpi3mr/Kconfig" source "drivers/scsi/smartpqi/Kconfig" +source "drivers/scsi/bht/Kconfig" config SCSI_HPTIOP tristate "HighPoint RocketRAID 3xxx/4xxx Controller support" diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index f055bfd54a68..80f1987a9770 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -152,6 +152,8 @@ obj-$(CONFIG_SCSI_ENCLOSURE) += ses.o obj-$(CONFIG_SCSI_HISI_SAS) += hisi_sas/ +obj-$(CONFIG_SCSI_BHT) += bht/ + # This goes last, so that "real" scsi devices probe earlier obj-$(CONFIG_SCSI_DEBUG) += scsi_debug.o scsi_mod-y += scsi.o hosts.o scsi_ioctl.o \ From patchwork Fri Oct 13 08:33:06 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: liuchang_125125@163.com X-Patchwork-Id: 13420424 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 14FA8CDB47E for ; Fri, 13 Oct 2023 08:33:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230113AbjJMIdl (ORCPT ); Fri, 13 Oct 2023 04:33:41 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52924 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230123AbjJMIdj (ORCPT ); Fri, 13 Oct 2023 04:33:39 -0400 Received: from m15.mail.163.com (m15.mail.163.com [45.254.50.219]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id E4A17DC; Fri, 13 Oct 2023 01:33:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:Subject:Date:Message-Id:MIME-Version; bh=LaDzO 7P/7Bxrgwx4S6QAlzMcetmHukoMgGfY234HCi0=; b=jP33fvREKFdmWo5NUpGKh 5OffJgzMmeE4DxNokLA8KxfwgoFyRWRSM0M8o+3uRercJi0r54ABXI1fVfhBwzBN YPCW7NSkZDlAeoo4xCZzWKeVNbptrmJRjTw5juKjpsPbGoVWlLRQoBFkCU4CypoR sz+VE38nlgCKf47qDJFUyQ= Received: from test-Z390-GAMING-X.bayhubtech.com (unknown [58.48.115.170]) by zwqz-smtp-mta-g1-1 (Coremail) with SMTP id _____wD3f2vGACll9T+0AQ--.19521S2; Fri, 13 Oct 2023 16:33:11 +0800 (CST) From: liuchang_125125@163.com To: jejb@linux.ibm.com, martin.petersen@oracle.com, linux-scsi@vger.kernel.org, linux-kernel@vger.kernel.org Cc: mark.tao@bayhubtech.com, shaper.liu@bayhubtech.com, thomas.hu@bayhubtech.com, chevron.li@bayhubtech.com, charl.liu@bayhubtech.com, Charl Liu Subject: [PATCH 2/9] scsi: bht: Add Bayhub module's Kconfig and Makefile for compiling Bayhub's SD/MMC Card interface driver Date: Fri, 13 Oct 2023 16:33:06 +0800 Message-Id: <20231013083306.10261-1-liuchang_125125@163.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 X-CM-TRANSID: _____wD3f2vGACll9T+0AQ--.19521S2 X-Coremail-Antispam: 1Uf129KBjvJXoW7KFyUZF4xtF4fWryUAF15twb_yoW8tF47pF W8Ar13Ca18ta1Sg3s7uF12vFy3Kas2vFyjkay2q34UXFykCFyagF9FqFy5CrykXF4kXFWD CF9xWFykK3Z8JaDanT9S1TB71UUUUUUqnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x0p_9NVUUUUUU= X-Originating-IP: [58.48.115.170] X-CM-SenderInfo: polxux5dqjsiqsvrjki6rwjhhfrp/1tbiNwcIWVWBqsRVmAAAsW Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org From: Charl Liu 1.Add Bayhub module's config "SCSI_BHT" 2.Add Bayhub module's Makefile Signed-off-by: Charl Liu --- Change in V1: Add Bayhub module's Kconfig and Makefile for compiling Bayhub's SD/MMC Card interface driver. --- drivers/scsi/bht/Kconfig | 10 ++++++++++ drivers/scsi/bht/Makefile | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 drivers/scsi/bht/Kconfig create mode 100644 drivers/scsi/bht/Makefile diff --git a/drivers/scsi/bht/Kconfig b/drivers/scsi/bht/Kconfig new file mode 100644 index 000000000000..f6d224799885 --- /dev/null +++ b/drivers/scsi/bht/Kconfig @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-only +config SCSI_BHT + tristate "Bayhub's SD/MMC Card Interface Driver" + depends on PCI && SCSI + help + This driver supports the Bayhub's SD/MMC host controllers. + + If you have the Bayhub's SD/MMC host controllers, say Y or M here. + + If unsure, say N. diff --git a/drivers/scsi/bht/Makefile b/drivers/scsi/bht/Makefile new file mode 100644 index 000000000000..65fa352c51b2 --- /dev/null +++ b/drivers/scsi/bht/Makefile @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only +INCLUDE = -I$(KERNEL_SOURCE)/include \ + -I$(KERNEL_SOURCE)/drivers/scsi \ + -I$(PWD) + +ccflags-y += -g $(INCLUDE) + +OBJ = linux_os/linux_base.o linux_os/linux_api.o linux_os/linux_scsi.o +CARD_OBJ = card/cardcommon.o card/cardinterface.o card/mmc.o card/sd.o card/thermal.o card/uhs2.o card/card_ddr200_support.o card/output_tuning.o +HOST_OBJ = host/host.o host/hostven.o host/cmdhandler.o host/irqhandler.o host/transhandler.o +MAIN_OBJ = main/cfgmng.o main/thread.o main/autotimerfunc.o main/geniofunc.o main/pmfunc.o main/reqmng.o main/testcase.o +TQ_OBJ = tagqueue/tagqueue.o tagqueue/tqadma2.o tagqueue/tqadma3.o tagqueue/tqadma_sdma_like.o tagqueue/tqpolicy.o tagqueue/tqsdma.o tagqueue/tq_merge.o +UTIL_OBJ = util/util.o util/debug.o + +obj-$(CONFIG_SCSI_BHT) := bht-sd.o +bht-sd-objs := $(OBJ) $(CARD_OBJ) $(HOST_OBJ) $(MAIN_OBJ) $(TQ_OBJ) $(UTIL_OBJ) + + From patchwork Fri Oct 13 08:33:20 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: liuchang_125125@163.com X-Patchwork-Id: 13420425 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D86A5CDB47E for ; Fri, 13 Oct 2023 08:33:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230128AbjJMId6 (ORCPT ); Fri, 13 Oct 2023 04:33:58 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42316 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230183AbjJMIdy (ORCPT ); Fri, 13 Oct 2023 04:33:54 -0400 Received: from m15.mail.163.com (m15.mail.163.com [45.254.50.219]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id C1F75E9; Fri, 13 Oct 2023 01:33:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:Subject:Date:Message-Id:MIME-Version: Content-Type; bh=B/gZ6CUbOy0gqeNEewZojJb7oy2xS5MEdNX36nA6BqE=; b=A9wOleUpjKhnpl/K4V64cwYdSmrzRb6lXadhxy9/YdGt/nJ8+34B9bGNVS1SM/ OpmFEZEIL0uhxO3tLAzfE8njYnQ645Tbj54/Xc2jHEAayCSQ887hfosoYcqRNA+Q ASIP8pp4lj6u/5L8Q1DmeeWkThGE72N4cWhNsyypz/yQw= Received: from test-Z390-GAMING-X.bayhubtech.com (unknown [58.48.115.170]) by zwqz-smtp-mta-g0-3 (Coremail) with SMTP id _____wD3P5bSACllusG7AQ--.23061S2; Fri, 13 Oct 2023 16:33:23 +0800 (CST) From: liuchang_125125@163.com To: jejb@linux.ibm.com, martin.petersen@oracle.com, linux-scsi@vger.kernel.org, linux-kernel@vger.kernel.org Cc: mark.tao@bayhubtech.com, shaper.liu@bayhubtech.com, thomas.hu@bayhubtech.com, chevron.li@bayhubtech.com, charl.liu@bayhubtech.com, Charl Liu Subject: [PATCH 3/9] scsi: bht: card: Add the source files related to card initialization Date: Fri, 13 Oct 2023 16:33:20 +0800 Message-Id: <20231013083320.10279-1-liuchang_125125@163.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 X-CM-TRANSID: _____wD3P5bSACllusG7AQ--.23061S2 X-Coremail-Antispam: 1Uf129KBjvtXoWkZr48Zr1DZw4fCF13Wr43trb_yoW5XFy7uF g_AFyaq3ZrXwn8CrWjkws0ga13Cr15KF4kZw1kKF48Za9rKrn7A3y3Kwn3AF4avry7Aryr tr9rCF1S93sFvw1DGjkaLaAFLSUrUUUUUb8apTn2vfkv8UJUUUU8Yxn0WfASr-VFAUDa7- sFnT9fnUUvcSsGvfC2KfnxnUUI43ZEXa7xRMfHUJUUUUU== X-Originating-IP: [58.48.115.170] X-CM-SenderInfo: polxux5dqjsiqsvrjki6rwjhhfrp/xtbBnwcIWVetlJKn4gABsx Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org From: Charl Liu 1.card_ddr200_support: check whether the card supports DDR200/DDR225 mode 2.cardcommon: define common functions related to card initialization 3.cardinterface: implement card initialization main flow and define the functions related to card operations 4.mmc: define the functions related to MMC/eMMC card initialization 5.output_tuning: implement card tuning flow and related functions 6.sd: implement SD legacy card initialization flow and related functions 7.thermal: define the functions related to thermal control 8.uhs2: implement SD UHS2 card initialization flow and related functions Signed-off-by: Charl Liu --- Change in V1: Add the source files related to card initialization. --- drivers/scsi/bht/card/card_ddr200_support.c | 195 ++ drivers/scsi/bht/card/card_ddr200_support.h | 38 + drivers/scsi/bht/card/cardcommon.c | 961 ++++++ drivers/scsi/bht/card/cardcommon.h | 123 + drivers/scsi/bht/card/cardinterface.c | 2448 +++++++++++++++ drivers/scsi/bht/card/mmc.c | 1666 ++++++++++ drivers/scsi/bht/card/output_tuning.c | 756 +++++ drivers/scsi/bht/card/sd.c | 3029 +++++++++++++++++++ drivers/scsi/bht/card/thermal.c | 348 +++ drivers/scsi/bht/card/uhs2.c | 1228 ++++++++ 10 files changed, 10792 insertions(+) create mode 100644 drivers/scsi/bht/card/card_ddr200_support.c create mode 100644 drivers/scsi/bht/card/card_ddr200_support.h create mode 100644 drivers/scsi/bht/card/cardcommon.c create mode 100644 drivers/scsi/bht/card/cardcommon.h create mode 100644 drivers/scsi/bht/card/cardinterface.c create mode 100644 drivers/scsi/bht/card/mmc.c create mode 100644 drivers/scsi/bht/card/output_tuning.c create mode 100644 drivers/scsi/bht/card/sd.c create mode 100644 drivers/scsi/bht/card/thermal.c create mode 100644 drivers/scsi/bht/card/uhs2.c diff --git a/drivers/scsi/bht/card/card_ddr200_support.c b/drivers/scsi/bht/card/card_ddr200_support.c new file mode 100644 index 000000000000..b7fb935d700c --- /dev/null +++ b/drivers/scsi/bht/card/card_ddr200_support.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: card_ddr200_support.c + * + * Abstract: check whether the card supports DDR200/DDR225 mode + * + * Version: 1.00 + * + * Author: Fred + * + * Environment: OS Independent + * + * History: + * + * 12/17/2021 Creation Fred + */ + +#include "../include/basic.h" +#include "../include/hostapi.h" +#include "../include/debug.h" +#include "card_ddr200_support.h" + +bool sandisk_ddr_support(sd_card_t *card, bool ddr_mode) +{ + bool ret = FALSE; + u32 prv_tmp; + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "oemid is 0x%04x,\n" + "prod_name[0] is %x,\n" + "prod_name[1] is %x,\n" + "prod_name[2] is %x,\n" + "prod_name[3] is %x,\n" + "prod_name[4] is %x,\n" + "prv is 0x%x\n", card->info.cid.oemid, + card->info.cid.prod_name[0], card->info.cid.prod_name[1], + card->info.cid.prod_name[2], card->info.cid.prod_name[3], + card->info.cid.prod_name[4], card->info.cid.prv); + + /* + * check whether support DDR200 or DDR225 + * support DDR200 mode if prv is 0x85 + * support DDR225 mode if prv is 0x86 + */ + if (ddr_mode) + prv_tmp = 0x85; + else + prv_tmp = 0x86; + + if (card->info.cid.oemid == 0x4453 + && card->info.cid.prod_name[0] == 0x53 + && (card->info.cid.prod_name[1] == 0x4E + || card->info.cid.prod_name[1] == 0x46 + || card->info.cid.prod_name[1] == 0x52)) + ret = TRUE; + else if ((card->info.cid.oemid == 0x4453 + || card->info.cid.oemid == 0x5744) + && card->info.cid.prv == prv_tmp) + ret = TRUE; + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +bool lexar_transend_ddr200_support(sd_card_t *card) +{ + bool ret = FALSE; + card_info_t *card_info = &(card->info); + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "reserved is 0x%x, Group 2 vendor spcific is 0x%x\n", + card->info.cid.reserved, + card_info->sw_func_cap.sd_command_system); + + if ((card->info.cid.reserved == 0xA) + && ((card_info->sw_func_cap.sd_command_system) & (1 << 6))) + ret = TRUE; + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +bool phison_kingston_ddr200_support(sd_card_t *card) +{ + bool ret = FALSE; + card_info_t *card_info = &(card->info); + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "sd_specx is 0x%04x, reserved_B0 is 0x%04x, reserved_B1 is 0x%04x\n", + card_info->scr.sd_specx, card_info->scr.reserved_B0, + card_info->scr.reserved_B1); + + if ((card_info->scr.sd_specx >= 2) + && (card_info->scr.reserved_B0 == 0x32) + && (card_info->scr.reserved_B1 == 0x64)) + ret = TRUE; + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +bool manuefecture_ddr200_support(sd_card_t *card, u32 check_methood) +{ + bool ret = FALSE; + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + card->ddr225_card_flag = FALSE; + + if (card->info.cid.prv == 0x86) { + ret = sandisk_ddr_support(card, FALSE); + if (ret) { + card->ddr225_card_flag = TRUE; + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "DDR225 Check Stag: host support DDR225 mode\n"); + } else { + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "DDR225 Check Stag: host not support DDR225 mode\n"); + } + + goto exit; + } + + switch (check_methood) { + case SANDISK: + ret = sandisk_ddr_support(card, TRUE); + break; + case LEXAR: + case TRANSEND: + ret = lexar_transend_ddr200_support(card); + break; + case PHISON: + case KINGSTON: + ret = phison_kingston_ddr200_support(card); + break; + default: + break; + } + +exit: + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +bool sd_ddr_support(sd_card_t *card) +{ + byte i = 0; + bool ret = FALSE; + sd_host_t *host = card->host; + card_info_t *card_info = &(card->info); + cfg_item_t *cfg = card->host->cfg; + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + if ((sdhci_readl(host, 0x110) & (1 << 16)) && + (sdhci_readl(host, 0x3e) & (1 << 3))) { + if ((card_info->sw_func_cap.sd_access_mode & (1 << 3)) && + (card_info->sw_func_cap.sd_command_system) & (1 << 6)) { + while (i <= MAX_DDR200_CHECK_METHOD) { + ret = manuefecture_ddr200_support(card, i); + if (ret) + break; + + i++; + } + } + } + + /* + * 1.card support DDR200 + * 2.driver registry control + */ + if (ret && (cfg->card_item.test_max_access_mode.value == 0x5)) + ret = TRUE; + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + + return ret; +} diff --git a/drivers/scsi/bht/card/card_ddr200_support.h b/drivers/scsi/bht/card/card_ddr200_support.h new file mode 100644 index 000000000000..a984a0dc01b4 --- /dev/null +++ b/drivers/scsi/bht/card/card_ddr200_support.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: card_ddr200_support.h + * + * Abstract: the functon declaration about checking whether the card supports DDR200/DDR225 mode + * + * Version: 1.00 + * + * Author: Fred + * + * Environment: OS Independent + * + * History: + * + * 12/17/2021 Creation Fred + */ + +#ifndef _CARD_DDR200_SUPPORT_H +#define _CARD_DDR200_SUPPORT_H + +#include "../include/card.h" + +#define MAX_DDR200_CHECK_METHOD 0x4 +#define SANDISK 0x0 +#define LEXAR 0x1 +#define TRANSEND 0x2 +#define PHISON 0x3 +#define KINGSTON 0x4 + +bool sandisk_ddr_support(sd_card_t *card, bool ddr_mode); +bool lexar_transend_ddr200_support(sd_card_t *card); +bool phison_kingston_ddr200_support(sd_card_t *card); +bool sd_ddr_support(sd_card_t *card); +bool manuefecture_ddr200_support(sd_card_t *card, u32 check_methood); + +#endif diff --git a/drivers/scsi/bht/card/cardcommon.c b/drivers/scsi/bht/card/cardcommon.c new file mode 100644 index 000000000000..7a5d4a444b0c --- /dev/null +++ b/drivers/scsi/bht/card/cardcommon.c @@ -0,0 +1,961 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: cardcommon.c + * + * Abstract: define card related common functions + * + * Version: 1.00 + * + * Author: Samuel + * + * Environment: OS Independent + * + * History: + * + * 9/2/2014 Creation Samuel + */ + +#include "../include/basic.h" +#include "../include/hostapi.h" +#include "../include/cmdhandler.h" +#include "../include/debug.h" +#include "../include/util.h" +#include "../include/tqapi.h" +#include "../include/transhapi.h" +#include "cardcommon.h" + +bool card_need_get_info(sd_card_t *card) +{ + if ((card->quick_init) && (card->initialized_once)) + return FALSE; + else + return TRUE; +} + +bool card_send_command12(sd_card_t *card, sd_command_t *sd_cmd) +{ + bool ret = FALSE; + u32 status = 0; + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_OPS, NOT_TO_RAM, "Enter %s\n", + __func__); + + ret = card_send_sdcmd(card, sd_cmd, SD_CMD12, 0, + CMD_FLG_R1B | CMD_FLG_RESCHK, DATA_DIR_NONE, NULL, + 0); + + if (ret == FALSE) { + if ((sd_cmd->err.resp_err & RESP_ERR_TYPE_OUT_OF_RANGE) == + RESP_ERR_TYPE_OUT_OF_RANGE) { + ret = card_get_card_status(card, sd_cmd, &status); + } + } + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_OPS, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +bool card_send_sdcmd_timeout(sd_card_t *card, + sd_command_t *sd_cmd, + byte cmd_index, + u32 argument, + u32 cmdflag, + e_data_dir dir, + byte *data, u32 datalen, u32 timeout) +{ + + sd_data_t sd_data; + bool ret; + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARDCMD_TRACE, NOT_TO_RAM, + "Enter %s cmd=0x%02X arg=0x%08X\n", __func__, cmd_index, + argument); + + /* Avoid recursion call */ + if (card->has_built_inf && cmd_index != SD_CMD12) { + ret = card_send_command12(card, sd_cmd); + if (ret == FALSE) + goto exit; + } + + os_memset(sd_cmd, 0, sizeof(sd_command_t)); + + sd_cmd->cmd_flag = cmdflag; + sd_cmd->cmd_index = cmd_index; + sd_cmd->argument = argument; + sd_cmd->sd_cmd = 1; + sd_cmd->timeout = timeout; + + if (dir == DATA_DIR_NONE) + sd_cmd->data = NULL; + else { + os_memset(&sd_data, 0, sizeof(sd_data_t)); + sd_cmd->data = &sd_data; + sd_data.dir = dir; + sd_data.data_mng.driver_buff = data; + sd_data.data_mng.total_bytess = datalen; + if (cmdflag & CMD_FLG_ADMA_SDMA) { + ret = + build_dma_ctx(card->host->pdx, &sd_data, cmdflag, + dir, data, datalen, 0, 0); + if (ret == FALSE) { + DbgErr("build adma io error\n"); + ret = FALSE; + goto exit; + } + } + + if (sd_cmd->cmd_flag & CMD_FLG_DDR200_WORK_AROUND) + sd_cmd->gg8_ddr200_workaround = 1; + } + + ret = cmd_generate_reg(card, sd_cmd); + if (ret == FALSE) + goto exit; + + ret = cmd_execute_sync(card, sd_cmd, NULL); + +exit: + DbgInfo(MODULE_ALL_CARD, FEATURE_CARDCMD_TRACE, NOT_TO_RAM, + "Exit(%d) %s\n", ret, __func__); + return ret; + +} + +bool card_send_sdcmd_dma_timeout(sd_card_t *card, + sd_command_t *sd_cmd, + sd_data_t *sd_data, + byte cmd_index, + u32 argument, + u32 cmdflag, + e_data_dir dir, + byte *data, u32 datalen, u32 timeout) +{ + bool ret = FALSE; + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARDCMD_TRACE, NOT_TO_RAM, + "Enter %s cmd=0x%02X arg=0x%08X\n", __func__, cmd_index, + argument); + + /* Avoid recursion call */ + if (card->has_built_inf && cmd_index != SD_CMD12) { + ret = card_send_command12(card, sd_cmd); + if (ret == FALSE) + goto exit; + } + + os_memset(sd_cmd, 0, sizeof(sd_command_t)); + + sd_cmd->cmd_flag = cmdflag; + sd_cmd->cmd_index = cmd_index; + sd_cmd->argument = argument; + sd_cmd->sd_cmd = 1; + sd_cmd->timeout = timeout; + + if (dir == DATA_DIR_NONE) + sd_cmd->data = NULL; + else { + sd_cmd->data = sd_data; + sd_data->dir = dir; + /* sd_data->data_mng.driver_buff = data; */ + sd_data->data_mng.total_bytess = datalen; + } + + ret = cmd_generate_reg(card, sd_cmd); + if (ret == FALSE) + goto exit; + + ret = cmd_execute_sync(card, sd_cmd, NULL); + +exit: + DbgInfo(MODULE_ALL_CARD, FEATURE_CARDCMD_TRACE, NOT_TO_RAM, + "Exit(%d) %s\n", ret, __func__); + return ret; + +} + +/* + * Function Name: card_send_sdcmd + * + * Abstract: Issue command + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to the sd command structure. the caller need to check it's status. + * byte cmd_index: Command Index + * u32 argument: Command argument + * u32 cmdflag: Command flags, like response tpye, DMA or PIO + * e_data_dir dir: data direction: NONE/IN/OUT + * byte *data: Pointer to the data buffer for data command + * u32 datalen: Data length for transfer. + * + * Output: None + * + * Return value: Return TRUE if command successfully, else return FALSE. + * + * Notes: + * + * Caller: + * + */ + +bool card_send_sdcmd(sd_card_t *card, + sd_command_t *sd_cmd, + byte cmd_index, + u32 argument, + u32 cmdflag, e_data_dir dir, byte *data, u32 datalen) +{ + return card_send_sdcmd_timeout(card, sd_cmd, cmd_index, argument, + cmdflag, dir, data, datalen, 0); +} + +bool card_wr_protect(sd_card_t *card) +{ + bool ret = FALSE; + card_info_t *card_info = &(card->info); + + if (card_info->csd.temp_protect || card_info->csd.parm_protect) + ret = TRUE; + + return ret; +} + +bool card_reset_card(sd_card_t *card, sd_command_t *sd_cmd) +{ + byte cmd_index = (byte) (SD_CMD0); + u32 argument = 0; + u32 cmdflag = 0; + e_data_dir dir = DATA_DIR_NONE; + byte *data = NULL; + u32 datalen = 0; + bool ret = FALSE; + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + ret = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, dir, + data, datalen); + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +bool card_all_send_cid(sd_card_t *card, sd_command_t *sd_cmd) +{ + + byte cmd_index = SD_CMD2; + u32 argument = 0; + u32 cmdflag = CMD_FLG_R2; + e_data_dir dir = DATA_DIR_NONE; + byte *data = NULL; + u32 datalen = 0; + card_info_t *card_info = &(card->info); + bool ret = FALSE; + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* Issue CMD2 */ + ret = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, dir, + data, datalen); + if (ret) { + os_memcpy(&(card_info->raw_cid[0]), &(sd_cmd->response[0]), 16); + card_info->cid.manfid = card_info->raw_cid[0]; + card_info->cid.oemid = + card_info->raw_cid[1] | (card_info->raw_cid[2] << 8); + os_memcpy(card_info->cid.prod_name, &(card_info->raw_cid[3]), 5); + card_info->cid.prv = card_info->raw_cid[8]; + card_info->cid.serial = + card_info->raw_cid[9] | + (card_info->raw_cid[10] << 8) | + (card_info->raw_cid[11] << 16) | + (card_info->raw_cid[12] << 24); + card_info->cid.reserved = card_info->raw_cid[13] >> 4; + } + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +/* + * Function Name: card_get_rca + * + * Abstract: Ask the card to publish a new relative address RCA (CMD3) + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * + * Output: None + * + * Return value: Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: sd_card_identify + * + */ + +bool card_get_rca(sd_card_t *card, sd_command_t *sd_cmd) +{ + + byte cmd_index = SD_CMD3; + u32 argument = 0; + u32 cmdflag = CMD_FLG_R6 | CMD_FLG_RESCHK; + e_data_dir dir = DATA_DIR_NONE; + byte *data = NULL; + u32 datalen = 0; + + bool ret = FALSE; + + card_info_t *card_info = &(card->info); + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* Issue CMD3 */ + ret = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, dir, + data, datalen); + if (ret) { + /* Update the card RCA */ + card_info->rca = (sd_cmd->response[0] & 0xFFFF0000) >> 16; + } + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +/* + * Function Name: card_select_card + * + * Abstract: Select card (CMD7) + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * + * Output: None + * + * Return value: Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: sd_card_select + * + */ + +bool card_select_card(sd_card_t *card, sd_command_t *sd_cmd) +{ + + byte cmd_index = SD_CMD7; + u32 argument = 0; + u32 cmdflag = 0; + e_data_dir dir = DATA_DIR_NONE; + byte *data = NULL; + u32 datalen = 0; + + bool ret = FALSE; + card_info_t *card_info = &(card->info); + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + if (card->card_type == CARD_UHS2) + cmdflag = CMD_FLG_R1 | CMD_FLG_RESCHK; + else + cmdflag = CMD_FLG_R1B | CMD_FLG_RESCHK; + argument = (card_info->rca) << 16; + + /* Issue CMD7 */ + ret = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, dir, + data, datalen); + if (ret == TRUE) { + /* Get Lock/Unlock status, CMD7 Response [25]. + * Check bit 25 of CMD7 response. + */ + if (sd_cmd->response[0] & BIT25) + card->locked = TRUE; + else + card->locked = FALSE; + } + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Exit(%d) %s locked=%d\n", ret, __func__, card->locked); + return ret; +} + +/* + * Function Name: card_deselect_card + * + * Abstract: De-Select card (CMD7) + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * + * Output: None + * + * Return value: Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: sd_read_csd + * + */ + +bool card_deselect_card(sd_card_t *card, sd_command_t *sd_cmd) +{ + + byte cmd_index = SD_CMD7; + u32 argument = 0; + u32 cmdflag = 0; + e_data_dir dir = DATA_DIR_NONE; + byte *data = NULL; + u32 datalen = 0; + + bool ret = FALSE; + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + cmdflag = 0; + argument = 0; + + /* Issue CMD7 */ + ret = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, dir, + data, datalen); + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Exit(%d) %s locked=%d\n", ret, __func__, card->locked); + return ret; +} + +/* + * Function Name: card_set_csd_info + * + * Abstract: Acquired CSD Data, to be stored into Struct of CSD and Card. + * Save some contents of CSD Register into Struct of CSD, + * and generate(calcurate) necessary Data to save into Struct of Card. + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * unsigned char *csdbuff: CSD Buffer Pointer + * csd_t *csd_info: CSD information Pointer + * + * Output: None + * + * Return value: None + * + * Notes: + * + * Caller: card_get_csd + * + */ + +static void card_set_csd_info(sd_card_t *card, unsigned char *csdbuff, +csd_t *csd_info) +{ + u32 blocknr, mult, block_len, dummy1, dummy2; + byte i; + u32 value, unit; + byte taac_value, taac_unit; + u64 tmpsize; + + card_info_t *card_info = &(card->info); + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + value = unit = taac_value = taac_unit = 0; + mult = block_len = 1; + blocknr = dummy1 = dummy2 = 0; + + /* Store the CSD information to Struct of CSD */ + + /* Get "CSD Structure" */ + csd_info->csd_structure = ((csdbuff[0] & 0xC0) >> 6); + + /* Get MMC "Spec_Vers" ( = System Specification version ) */ + csd_info->mmc_spec_vers = ((csdbuff[0] & 0x3C) >> 2); + + /* Get "TRAN SPEED" */ + csd_info->tran_speed = csdbuff[3]; + + /* Get "TAAC" */ + csd_info->taac = csdbuff[1]; + + /* Get "NSAC" */ + csd_info->nsac = csdbuff[2]; + + /* Get "read_bl_len" */ + csd_info->read_bl_len = (csdbuff[5] & 0x0F); + + /* Get "PERM_WRITE_PROTECT" */ + csd_info->parm_protect = (csdbuff[14] & 0x20) >> 5; + + /* Get "TMP_WRITE_PROTECT" */ + csd_info->temp_protect = (csdbuff[14] & 0x10) >> 4; + + /* Get "c_size" */ + csd_info->c_size = 0; + + if (card->card_type == CARD_SD || card->card_type == CARD_UHS2) { + if (csd_info->csd_structure == 0) { + /* CSD Version 1.0 (Standard Capacity) */ + dummy1 = (csdbuff[6] & 0x03); + dummy1 = (dummy1 << 10); + dummy2 = csdbuff[7]; + dummy2 = (dummy2 << 2); + dummy2 = (dummy1 | dummy2); + csd_info->c_size = + (dummy2 | ((csdbuff[8] & 0xC0) >> 6)); + } else { + /* CSD Version 2.0 (High Capacity and Extended Capacity) */ + dummy1 = (csdbuff[7] & 0x3F); + dummy1 = (dummy1 << 16); + dummy2 = csdbuff[8]; + dummy2 = (dummy2 << 8); + dummy2 = (dummy1 | dummy2); + csd_info->c_size = (dummy2 | (csdbuff[9] & 0xFF)); + } + } else if ((card->card_type == CARD_MMC) || + (card->card_type == CARD_EMMC) + ) { + if (card_info->card_ccs == 0) { + /* (Standard Capacity) */ + dummy1 = (csdbuff[6] & 0x03); + dummy1 = (dummy1 << 10); + dummy2 = csdbuff[7]; + dummy2 = (dummy2 << 2); + dummy2 = (dummy1 | dummy2); + csd_info->c_size = + (dummy2 | ((csdbuff[8] & 0xC0) >> 6)); + } else { + /* (High Capacity and Extended Capacity) */ + dummy1 = (csdbuff[7] & 0x3F); + dummy1 = (dummy1 << 16); + dummy2 = csdbuff[8]; + dummy2 = (dummy2 << 8); + dummy2 = (dummy1 | dummy2); + csd_info->c_size = (dummy2 | (csdbuff[9] & 0xFF)); + } + + } + + /* Get "sect_size" */ + if ((card->card_type == CARD_MMC) || (card->card_type == CARD_EMMC) + ) { + /* MMC */ + csd_info->sector_size = ((csdbuff[10] & 0x7C) >> 2); + } else { + /* SD Memory Card */ + csd_info->sector_size = (((csdbuff[10] & 0x3f) << 1) | + ((csdbuff[11] & 0x80) >> 7)); + } + + /* Get "c_size_mult" */ + if (card->card_type == CARD_SD || card->card_type == CARD_UHS2) { + if (csd_info->csd_structure == 0) { + /* CSD Version 1.0 (Standard Capacity) */ + csd_info->c_size_mult = (((csdbuff[9] & 0x03) << 1) | + ((csdbuff[10] & 0x80) >> 7)); + } else { + /* CSD Version 2.0 (High Capacity and Extended Capacity) */ + /* not exist */ + ; + } + } else if ((card->card_type == CARD_MMC) || + (card->card_type == CARD_EMMC) + ) { + + if (card_info->card_ccs == 0) { + /* CSD Version 1.0 (Standard Capacity) */ + csd_info->c_size_mult = (((csdbuff[9] & 0x03) << 1) | + ((csdbuff[10] & 0x80) >> 7)); + } else { + /* CSD Version 2.0 (High Capacity and Extended Capacity) */ + /* not exist */ + ; + } + } + + /* + * Acquired CSD Data, to be stored into Struct of CSD and Card + * Save some contents of CSD Register into Struct of CSD, and + * generate(calcurate) necessary Data to save into Struct of Card + */ + + /* Calcuration of Total Sector count & Card Size */ + if (card->card_type == CARD_SD || card->card_type == CARD_UHS2) { + if (csd_info->csd_structure == 0) { + /* CSD Version 1.0 (Standard Capacity) */ + for (i = 0; i < (csd_info->c_size_mult + 2); i++) + mult = mult * 2; + for (i = 0; i < csd_info->read_bl_len; i++) + block_len = block_len * 2; + blocknr = (csd_info->c_size + 1) * mult; + /* Card Size (Byte) */ + + card->sec_count = ((u64) (blocknr) * (u64) (block_len)); + } else { + /* CSD Version 2.0 (High Capacity and Extended Capacity) */ + + /* Card Size (Byte) */ + + /* (c_size + 1) * 512K */ + tmpsize = ((u64) csd_info->c_size) + 1; + card->sec_count = tmpsize * 524288; + } + } else if ((card->card_type == CARD_MMC) || + (card->card_type == CARD_EMMC) + ) { + if (card_info->card_ccs == 0) { + /* CSD Version 1.0 (Standard Capacity) */ + for (i = 0; i < (csd_info->c_size_mult + 2); i++) + mult = mult * 2; + for (i = 0; i < csd_info->read_bl_len; i++) + block_len = block_len * 2; + blocknr = (csd_info->c_size + 1) * mult; + + /* Card Size (Byte) */ + + card->sec_count = ((u64) (blocknr) * (u64) (block_len)); + } else { + /* sector size will calculate at MMC_Set_CSDEXT() */ + ; + } + } + + /* Total sector count of Card */ + card->sec_count = ((card->sec_count) / (SD_BLOCK_LEN)); + /* SD_INFO_PRINTF("Card_Info.sect_num = %x\n", Card_Info.sect_num); */ + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Exit %s CardSectors: %d, %dGB\n", __func__, + card->sec_count, card->sec_count / 2 / 1024 / 1024); + +} + +/* + * Function Name: card_get_csd + * + * Abstract: Addressed card sends its card-specific data (CSD) on the CMD line (CMD9) + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * + * Output: None + * + * Return value: Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: sd_read_csd + * + */ + +bool card_get_csd(sd_card_t *card, sd_command_t *sd_cmd) +{ + + byte cmd_index = SD_CMD9; + u32 argument = 0; + u32 cmdflag = CMD_FLG_R2; + e_data_dir dir = DATA_DIR_NONE; + byte *data = NULL; + u32 datalen = 0; + + bool ret = FALSE; + card_info_t *card_info = &(card->info); + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + argument = card_info->rca << 16; + + /* Issue CMD9 */ + ret = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, dir, + data, datalen); + if (ret) { + /* Set the card CSD info */ + os_memcpy(&(card_info->raw_csd[0]), &(sd_cmd->response[0]), 16); + /* Parse the CSD info */ + card_set_csd_info(card, card_info->raw_csd, &(card_info->csd)); + } + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +/* + * Function Name: card_get_card_status + * + * Abstract: Read the SD Status Register (SSR) (ACMD13) + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * + * Output: None + * + * Return value: Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: card_check_rw_ready + * + */ + +bool card_get_card_status(sd_card_t *card, + sd_command_t *sd_cmd, u32 *card_status) +{ + + byte cmd_index = SD_CMD13; + u32 argument = 0; + u32 cmdflag = CMD_FLG_R1; + e_data_dir dir = DATA_DIR_NONE; + byte *data = NULL; + u32 datalen = 0; + + bool ret = FALSE; + card_info_t *card_info = &(card->info); + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_OPS, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* Issue CMD13 */ + argument = (card_info->rca << 16); + ret = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, dir, + data, datalen); + if (ret) { + /* Send the card status */ + os_memcpy(card_status, &(sd_cmd->response[0]), 4); + } + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_OPS, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +bool card_check_rw_ready(sd_card_t *card, sd_command_t *sd_cmd, + int timeout_ms) +{ + bool result = FALSE; + u32 card_status = 0; + loop_wait_t wait; + u32 delay_us = 10; + + util_init_waitloop(card->host->pdx, timeout_ms, delay_us, &wait); + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_OPS, NOT_TO_RAM, + "Enter %s, timeout_ms=%d\n", __func__, timeout_ms); + + do { + result = card_get_card_status(card, sd_cmd, &card_status); + if (result == FALSE) + goto exit; + + os_udelay(delay_us); + } while (((card_status & 0x900) != 0x900) && (!util_is_timeout(&wait))); + + if ((card_status & 0x900) != 0x900) + result = FALSE; + +exit: + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_OPS, NOT_TO_RAM, "Exit(%d) %s\n", + result, __func__); + return result; +} + +/* + * Function Name: card_set_block_len + * + * Abstract: Set the block length for all following block commands (ACMD6, block length = 5126) + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * u32 arg: SD_BLOCK_LEN + * + * Output: None + * + * Return value: Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: sd_init_get_info + * + */ + +bool card_set_block_len(sd_card_t *card, sd_command_t *sd_cmd, u32 arg) +{ + byte cmd_index = SD_CMD16; + u32 argument = arg; + u32 cmdflag = CMD_FLG_R1 | CMD_FLG_RESCHK; + e_data_dir dir = DATA_DIR_NONE; + byte *data = NULL; + u32 datalen = 0; + + bool ret = FALSE; + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, arg=0x%x\n", __func__, arg); + + /* Issue CMD16 */ + ret = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, dir, + data, datalen); + if (!ret) + DbgErr("Set Block Length(CMD6) %d Error!!", argument); + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +/* + * Function Name: card_get_legacy_freq + * + * Abstract: + * 1. Set the specific clock frequency + * 2. Set DM/DN and Clock Divider + * + * Input: + * + * sd_card_t *card: Pointer to the card structure + * u32 clk_freq_khz: clock frequency to be set (KHz) + * bool ddr_mode: if it is DDR50 mode (100MHz same as SDR50), need to check max frequency for DDR50 + * + * Output: DMDN Values + * + * Return value: BIT[31:16]:dmdn BIT[14:0] basediv + * + * Notes: + * + * Caller: card_legacy_change_clock + * + */ + +static u32 card_get_legacy_freq(sd_card_t *card, u32 clk_freq_khz, + bool ddr_mode) +{ + u32 value = 0; + u16 index = 0; + sd_host_t *host = card->host; + u16 freq_level = card->degrade_freq_level; + /* cfg_max_freq_item_t * freq = &(host->cfg->host_item.max_freq_item); */ + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, Clock frequency %d KHz, ddr50_mode=%\n", + __func__, clk_freq_khz, ddr_mode); + if (host->cfg == NULL || host->cfg->dmdn_tbl == NULL) { + DbgErr("host cfg is null\n"); + return 0; + } + + /* Set DM/DN according to the clock frequency */ + switch (clk_freq_khz) { + case SD_CLK_ID_400K: + value = host->cfg->dmdn_tbl[FREQ_400K_START_INDEX]; + break; + case SD_CLK_50M: + if (ddr_mode) + value = host->cfg->dmdn_tbl[FREQ_DDR50M_START_INDEX]; + else + value = host->cfg->dmdn_tbl[FREQ_50M_START_INDEX]; + break; + + case SD_CLK_100M: + index = (u16) FREQ_100M_START_INDEX + freq_level; + if (index > (u16) FREQ_100M_DEGRE_INDEX) + index = (u16) FREQ_100M_DEGRE_INDEX; + value = host->cfg->dmdn_tbl[index]; + break; + + case SD_CLK_200M: + if (ddr_mode) { + index = (u16) FREQ_DDR200M_START_INDEX + freq_level; + if (index > (u16) FREQ_DDR200M_DEGRE_INDEX) + index = (u16) FREQ_DDR200M_DEGRE_INDEX; + value = host->cfg->dmdn_tbl[index]; + } else { + index = (u16) FREQ_200M_START_INDEX + freq_level; + if (index > (u16) FREQ_200M_DEGRE_INDEX) + index = (u16) FREQ_200M_DEGRE_INDEX; + value = host->cfg->dmdn_tbl[index]; + } + break; + case SD_CLK_225M: + index = (u16) FREQ_DDR225M_START_INDEX + freq_level; + if (index > (u16) FREQ_DDR225M_DEGRE_INDEX) + index = (u16) FREQ_DDR225M_DEGRE_INDEX; + value = host->cfg->dmdn_tbl[index]; + break; + + case SD_CLK_75M: + value = host->cfg->dmdn_tbl[FREQ_75M_START_INDEX]; + break; + + default: + value = host->cfg->dmdn_tbl[FREQ_25M_START_INDEX]; + break; + } + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, TO_RAM, + "%s Exit Clock=%d (KHz): value=0x%08X\n", __func__, + clk_freq_khz, value); + + return value; + +} + +/* + * Function Name: card_legacy_change_clock + * + * Abstract: + * 1. Stop clock + * 2. Set the clock frequency (DM/DN, clk divider) + * 3. Start the clock + * + * Input: + * + * sd_card_t *card: Pointer to the card structure + * u32 clk_freq_khz: clock frequency to be set (KHz) + * bool ddr_mode: if it is DDR200/DDR50 mode (100MHz same as SDR50), + * need to check max frequency for DDR200/DDR50 + * + * Output: None + * + * Return value: None + * + * Notes: + * + * Caller: sd_init_stage2 + * + */ + +void card_legacy_change_clock(sd_card_t *card, u32 clk_freq_khz, bool ddr_mode) +{ + u32 value; + sd_host_t *host = card->host; + + value = card_get_legacy_freq(card, clk_freq_khz, ddr_mode); + host_change_clock(host, value); + +} diff --git a/drivers/scsi/bht/card/cardcommon.h b/drivers/scsi/bht/card/cardcommon.h new file mode 100644 index 000000000000..985411bb0802 --- /dev/null +++ b/drivers/scsi/bht/card/cardcommon.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: cardcommon.h + * + * Abstract: Include card related common functions. + * + * Version: 1.00 + * + * Author: Samuel + * + * Environment: OS Independent + * + * History: + * + * 9/2/2014 Creation Samuel + */ + +#ifndef _CARDCOMMON_H +#define _CARDCOMMON_H + +#include "../include/card.h" + +/* SD Legacy (UHSI, HS, DS) card initialization */ +bool sd_legacy_init(sd_card_t *card); + +/* MMC or eMMC card initialization */ +bool emmc_init(sd_card_t *card, bool bemmc); +bool emmc_init_stage2(sd_card_t *card); +bool emmc_tuning(sd_card_t *card, sd_command_t *sd_cmd); + +bool sd_tuning(sd_card_t *card, sd_command_t *sd_cmd, u32 timeout); + +bool card_get_card_status(sd_card_t *card, + sd_command_t *sd_cmd, u32 *card_status); + +bool card_reset_card(sd_card_t *card, sd_command_t *sd_cmd); + +bool card_all_send_cid(sd_card_t *card, sd_command_t *sd_cmd); + +bool card_get_rca(sd_card_t *card, sd_command_t *sd_cmd); + +bool card_select_card(sd_card_t *card, sd_command_t *sd_cmd); + +bool card_get_csd(sd_card_t *card, sd_command_t *sd_cmd); + +bool card_send_command12(sd_card_t *card, sd_command_t *sd_cmd); + +bool card_set_block_len(sd_card_t *card, sd_command_t *sd_cmd, u32 arg); + +bool uhs2_card_init(sd_card_t *card); +void card_power_on(sd_card_t *card); + +/* + * (1) If uhs2 call uhs2 cmd handler + * (2) generate sd_cmd_t structure and sd_data structure(pio only) + * (3) call cmd_generate_reg(sd_cmd) + * (4) call cmd_execute + * (5) do error recover if necessary + * (6) return result + */ + +bool card_send_sdcmd(sd_card_t *card, + sd_command_t *sd_cmd, + byte cmd_index, + u32 argument, + u32 cmdflag, e_data_dir dir, byte *data, u32 datalen); + +bool uhs2_native_ccmd(sd_card_t *card, sd_command_t *sd_cmd, + u16 ioaddr, bool broadcast, bool rwcmd, byte payload_num, + u32 *payload); + +bool card_send_sdcmd_dma_timeout(sd_card_t *card, + sd_command_t *sd_cmd, + sd_data_t *sd_data, + byte cmd_index, + u32 argument, + u32 cmdflag, + e_data_dir dir, + byte *data, u32 datalen, u32 timeout); + +bool card_select_card(sd_card_t *card, sd_command_t *sd_cmd); + +bool sd_switch_function_check(sd_card_t *card, sd_command_t *sd_cmd); + +bool sd_switch_function_set_pl(sd_card_t *card, + sd_command_t *sd_cmd, byte power_limit); + +bool card_wr_protect(sd_card_t *card); + +bool sd_card_identify(sd_card_t *card); + +bool sd_init_get_info(sd_card_t *card); + +bool sd_init_stage2(sd_card_t *card); + +bool uhs2_enter_dmt(sd_card_t *card, sd_command_t *sd_cmd, sd_host_t *host, + bool hbr); +bool uhs2_resume_dmt(sd_card_t *card, sd_command_t *sd_cmd, sd_host_t *host, + bool hbr); + +bool sd_card_select(sd_card_t *card); +bool uhs2_init_stage2(sd_card_t *card); +bool uhs2_full_reset_card(sd_card_t *card); + +bool sd_switch_power_limit(sd_card_t *card, sd_command_t *sd_cmd, bool *bchg); + +bool card_check_rw_ready(sd_card_t *card, sd_command_t *sd_cmd, + int timeout_ms); + +void card_legacy_change_clock(sd_card_t *card, u32 clk_freq_khz, + bool ddr50_mode); + +bool card_need_get_info(sd_card_t *card); + +bool card_deselect_card(sd_card_t *card, sd_command_t *sd_cmd); + +bool sd_program_csd(sd_card_t *card, sd_command_t *sd_cmd, byte *data); + +bool sd_read_csd(sd_card_t *card, sd_command_t *sd_cmd, byte *data); + +#endif diff --git a/drivers/scsi/bht/card/cardinterface.c b/drivers/scsi/bht/card/cardinterface.c new file mode 100644 index 000000000000..6c8ee43f2e11 --- /dev/null +++ b/drivers/scsi/bht/card/cardinterface.c @@ -0,0 +1,2448 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: cardinterface.c + * + * Abstract: + * 1. Card initialization main entry + * 2. Interface for card operations + * + * Version: 1.00 + * + * Author: Samuel + * + * Environment: OS Independent + * + * History: + * + * 9/3/2014 Creation Samuel + */ + +#include "../include/basic.h" +#include "../include/card.h" +#include "../include/cardapi.h" +#include "../include/hostapi.h" +#include "../include/transhapi.h" +#include "../include/hostvenapi.h" +#include "../include/util.h" +#include "../include/debug.h" +#include "../include/cmdhandler.h" +#include "../host/hostven.h" +#include "../include/card.h" +#include "../host/hostreg.h" +#include "../include/funcapi.h" +#include "../tagqueue/tq_trans_api.h" +#include "../include/cmdhandler.h" +#include "cardcommon.h" + +/* Thomas add for direct remove 7.0 */ +extern void bht_sd_remove(struct pci_dev *pdev); + +bool sd_thermal_control(sd_card_t *card); +void uhs2_degrade_policy(sd_card_t *card, sd_command_t *sd_cmd); +bool uhs2_sd_error_recovery(sd_card_t *card, sd_command_t *sd_cmd); +void sd_degrade_policy(sd_card_t *card); +void mmc_degrade_policy(sd_card_t *card); +u32 card_get_uhs2_freq(sd_card_t *card); +u32 sdr104_sdr50_output_tuning(sd_card_t *card, u32 address); +u32 ddr200_output_tuning(sd_card_t *card, u32 address); + +bool sd_dll_divider(sd_card_t *card, sd_command_t *pcmd); + +byte tuning_address_content_buf[512] = { 0 }; + +bool store_tuning_address_content(sd_card_t *card, u64 tuning_address) +{ + bool ret = 0; + sd_command_t sd_cmd; + u32 cmdflag; + sd_host_t *host = card->host; + + card->read_signal_block_flag = TRUE; + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Enter %s\n", + __func__); + /* Save Current DMA mode */ + host_transfer_init(card->host, FALSE, TRUE); + cmdflag = CMD_FLG_RESCHK | CMD_FLG_R1 | CMD_FLG_ADMA_SDMA; + + ret = + card_send_sdcmd_timeout(card, &sd_cmd, SD_CMD17, + (u32) tuning_address, (cmdflag), + DATA_DIR_IN, tuning_address_content_buf, + 512, 500); + if (ret == FALSE) { + host_reset(host, SDHCI_RESET_CMD); + host_reset(host, SDHCI_RESET_DATA); + card->read_signal_block_flag = FALSE; + DbgErr("Read data FAILED when store tuning address content\n"); + } + + /* Resorte current DMA mode */ + host_transfer_init(card->host, card->inf_trans_enable, FALSE); + + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Exit %s\n", + __func__); + return ret; +} + +bool restore_tuning_address_content(sd_card_t *card, u64 tuning_address) +{ + bool ret = 0; + int i = 0; + sd_command_t sd_cmd; + u32 cmdflag; + byte tuning_temp_buf[512]; + bool gg8_ddr200 = 0; + + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Enter %s\n", + __func__); + + if (card->host->chip_type == CHIP_GG8 + && card->info.sw_cur_setting.sd_access_mode == SD_FNC_AM_DDR200) { + /* Save Current DMA mode */ + host_transfer_init(card->host, TRUE, FALSE); + gg8_ddr200 = 1; + cmdflag = + CMD_FLG_RESCHK | CMD_FLG_R1 | CMD_FLG_ADMA_SDMA | + CMD_FLG_DDR200_WORK_AROUND | CMD_FLG_INF_BUILD; + } else { + /* Save Current DMA mode */ + host_transfer_init(card->host, FALSE, TRUE); + cmdflag = CMD_FLG_RESCHK | CMD_FLG_R1 | CMD_FLG_ADMA_SDMA; + } + + ret = + card_send_sdcmd_timeout(card, &sd_cmd, + gg8_ddr200 ? SD_CMD25 : SD_CMD24, + (u32) tuning_address, (cmdflag), + DATA_DIR_OUT, tuning_address_content_buf, + 512, 500); + if (ret == FALSE) { + DbgErr + ("Write data FAILED when restore tuning address content\n"); + goto exit; + } + + ret = + card_send_sdcmd_timeout(card, &sd_cmd, + gg8_ddr200 ? SD_CMD18 : SD_CMD17, + (u32) tuning_address, + (cmdflag | CMD_FLG_ADMA_SDMA), DATA_DIR_IN, + tuning_temp_buf, 512, 500); + if (ret == FALSE) { + DbgErr("Read data FAILED when store tuning address content\n"); + goto exit; + } + + for (i = 0; i < 512; i++) { + if (tuning_temp_buf[i] != tuning_address_content_buf[i]) { + DbgErr("Tuning address compare err!!!Write data 0x%x, Read out data 0x%x, Offset %d\n", + tuning_address_content_buf[i], tuning_temp_buf[i], i); + ret = FALSE; + break; + } + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Write data 0x%x, Read out data 0x%x, Offset %d\n", + tuning_address_content_buf[i], tuning_temp_buf[i], i); + } + +exit: + /* Resorte current DMA mode */ + host_transfer_init(card->host, card->inf_trans_enable, FALSE); + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Exit %s\n", + __func__); + return ret; +} + +bool card_output_tuning(sd_card_t *card, u64 tuning_address) +{ + sd_host_t *host = card->host; + int ii, jj, pattern_i, first_0, dll_i_mod; + int dat_cmp, dll_result[16]; + byte test_patern[6] = { 0x55, 0xaa, 0x00, 0xff, 0xf0, 0x0f }; + u32 dll_i, window_pass_number[16], + window_start_adr[16], window_pass_number_max, dll_mod; + u32 ret = FALSE; + bool result = FALSE; + sd_command_t sd_cmd; + u32 cmdflag; + u8 phase_count = 11; + + byte *test_buf = kcalloc(512, sizeof(unsigned char), GFP_KERNEL); + byte *test_buf_read = kcalloc(512, sizeof(unsigned char), GFP_KERNEL); + + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Enter %s\n", + __func__); + + if (test_buf == NULL || test_buf_read == NULL) { + DbgErr("kcalloc buffer failed\n"); + if (test_buf != NULL) + kfree(test_buf); + if (test_buf_read != NULL) + kfree(test_buf_read); + return FALSE; + } + + host->output_tuning.start_block = (u32) tuning_address; + host->output_tuning.auto_phase_flag = FALSE; + + /* Save Current DMA mode */ + if (card->host->chip_type == CHIP_GG8 + && card->info.sw_cur_setting.sd_access_mode != SD_FNC_AM_DDR200) + host_transfer_init(host, FALSE, TRUE); + else + host_transfer_init(host, TRUE, FALSE); + + if (host->chip_type == CHIP_GG8 || host->chip_type == CHIP_ALBATROSS) { + if (card->info.sw_cur_setting.sd_access_mode == + SD_FNC_AM_DDR200) + ret = ddr200_output_tuning(card, (u32) tuning_address); + else + ret = + sdr104_sdr50_output_tuning(card, + (u32) tuning_address); + + if (ret == 0) + result = TRUE; + } else { + cmdflag = CMD_FLG_RESCHK | CMD_FLG_R1 | CMD_FLG_ADMA_SDMA; + window_pass_number_max = 0; + for (dll_i = 0; dll_i < phase_count; dll_i++) + dll_result[dll_i] = TRUE; + + for (dll_i = 0; dll_i < 512; dll_i++) + test_buf[dll_i] = test_patern[dll_i % 6]; + + host_cmddat_line_reset(host); + + if (host->chip_type != CHIP_GG8 + || host->chip_type == CHIP_ALBATROSS) { + if (card_check_rw_ready(card, &sd_cmd, 600) != TRUE) { + DbgErr + ("Error when output_tuning, card_check_rw_ready fail\n"); + goto exit; + } + } + + for (dll_i = 0; dll_i < phase_count; dll_i++) { + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, + NOT_TO_RAM, " - DLL Adjust Test %d\n", dll_i); + + if (card->card_present == FALSE) { + DbgErr + ("Error when output_tuning, card is removed\n"); + goto exit; + } + host_cmddat_line_reset(host); + host_set_output_tuning_phase(host, dll_i); + + if (card->info.sw_cur_setting.sd_access_mode == + SD_FNC_AM_SDR104 + || card->info.sw_cur_setting.sd_access_mode == + SD_FNC_AM_SDR50 + || card->info.sw_cur_setting.sd_access_mode == + SD_FNC_AM_DDR200) { + + ret = sd_tuning(card, &sd_cmd, 150); + if (ret == FALSE) { + DbgErr("Error when output_tuning, sd_tuning fail at phase %d\n", dll_i); + dll_result[dll_i] = FALSE; + continue; + } + } + + for (pattern_i = 0; pattern_i < 1; pattern_i++) { + + ret = card_send_sdcmd_timeout(card, &sd_cmd, + SD_CMD24, + host->output_tuning.start_block, + (cmdflag), + DATA_DIR_OUT, + test_buf, 512, 500); + if (ret == FALSE) { + DbgErr("Write data FAILED when output_tuning\n"); + dll_result[dll_i] = FALSE; + host_cmddat_line_reset(host); + card_send_command12(card, &sd_cmd); + if (card_check_rw_ready + (card, &sd_cmd, 600) != TRUE) { + DbgErr("Error when output_tuning write CMD, card_check_rw_ready fail\n"); + goto exit; + } + break; + } + + ret = card_send_sdcmd_timeout(card, &sd_cmd, + SD_CMD17, + host->output_tuning.start_block, + (cmdflag), + DATA_DIR_IN, + test_buf_read, 512, + 500); + if (ret == FALSE) { + DbgErr("Read data FAILED when output_tuning\n"); + dll_result[dll_i] = FALSE; + host_cmddat_line_reset(host); + card_send_command12(card, &sd_cmd); + if (card_check_rw_ready(card, &sd_cmd, 600) != TRUE) { + DbgErr("Error when output_tuning read CMD, card_check_rw_ready fail\n"); + goto exit; + } + break; + } + + dat_cmp = TRUE; + for (ii = 0; ii < (1 * 512); ii++) { + if (*(test_buf + ii) != + *(test_buf_read + ii)) { + dat_cmp = FALSE; + dll_result[dll_i] = FALSE; + break; + } + } + if (dat_cmp == FALSE) + DbgErr("Compare data FAILED at index %d!!!\n", ii); + + } + } + + for (ii = 0; ii < 16; ii++) { + window_pass_number[ii] = 0; + window_start_adr[ii] = 0; + } + + first_0 = 0; + for (dll_i = 0; dll_i < phase_count; dll_i++) { + if (dll_result[dll_i] != TRUE) { + first_0 = dll_i; + break; + } + } + + jj = 0; + for (dll_i = 0; dll_i < phase_count; dll_i++) { + dll_i_mod = (first_0 + dll_i) % phase_count; + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, + NOT_TO_RAM, "DLL phase [%x] result %d.\n", + dll_i_mod, dll_result[dll_i_mod]); + if (dll_result[dll_i_mod] == TRUE) + window_pass_number[jj]++; + else { + if (window_pass_number[jj] > 0) + jj++; + } + if (window_pass_number[jj] == 1) + window_start_adr[jj] = dll_i_mod; + } + + for (ii = 0; ii < phase_count; ii++) { + if (window_pass_number_max < window_pass_number[ii]) { + window_pass_number_max = window_pass_number[ii]; + jj = ii; + } + } + + if (window_pass_number_max == 0) + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, + NOT_TO_RAM, + "DLL test result: All DLL test FAIL\n"); + else { + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, + NOT_TO_RAM, + "DLL test result: Total %d DLL test PASS\n", + window_pass_number_max); + window_pass_number_max = window_pass_number_max >> 1; + dll_mod = window_start_adr[jj] + window_pass_number_max; + dll_mod = dll_mod % phase_count; + + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, + NOT_TO_RAM, "select DLL phase Number %d\n", + dll_mod); + host->output_tuning.auto_phase = dll_mod; + host->output_tuning.auto_phase_flag = TRUE; + result = TRUE; + host_set_output_tuning_phase(host, + host->output_tuning.auto_phase); + if (card->info.sw_cur_setting.sd_access_mode == + SD_FNC_AM_SDR104 + || card->info.sw_cur_setting.sd_access_mode == + SD_FNC_AM_SDR50 + || card->info.sw_cur_setting.sd_access_mode == + SD_FNC_AM_DDR200) { + ret = sd_tuning(card, &sd_cmd, 150); + if (ret == FALSE) { + DbgErr + ("Error when output_tuning, sd_tuning fail\n"); + result = FALSE; + goto exit; + } + } + + } + + } +exit: + + /* Resorte current DMA mode */ + host_transfer_init(host, card->inf_trans_enable, FALSE); + if (result == FALSE) + hostven_set_output_tuning_phase(host, 0, TRUE); + host_cmddat_line_reset(host); + host->output_tuning.auto_flag = FALSE; + + kfree(test_buf); + kfree(test_buf_read); + + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Exit %s result is %d\n", __func__, result); + + return result; +} + +static bool legacy_error_recovery(sd_card_t *card, sd_command_t *pcmd) +{ + bool ret; + sd_command_t sd_cmd; + card_info_t *card_info = &(card->info); + sd_host_t *host = card->host; + bht_dev_ext_t *pdx = host->pdx; + cfg_output_tuning_item_t *cfg = + &pdx->cfg->feature_item.output_tuning_item; + + DbgInfo(MODULE_LEGACY_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Enter %s\n", __func__); + + /* Follow SD Host Spec V4.10 Section 3.10.1 Error Interrupt Recovery flow (Page 179) */ + card_send_command12(card, &sd_cmd); + + /* Call host api to do host related recovery stage2 */ + ret = + host_error_int_recovery_stage2(card->host, + sd_cmd.err.legacy_err_reg); + + if (ret == FALSE) + goto exit; + + ret = card_check_rw_ready(card, &sd_cmd, 150); + + if (ret == FALSE) { + DbgErr("Card status is not ready after error recovery"); + goto exit; + } + + if (pcmd != NULL) { + /* Crc error */ + if (pcmd->err.legacy_err_reg & (BIT1 | BIT5)) { + if (host->cfg->feature_item.output_tuning_item.enable_dll + == 0) { + if (card->card_type == CARD_SD) + ret = sd_tuning(card, &sd_cmd, 0); + else if (card->card_type == CARD_EMMC) + ret = emmc_tuning(card, &sd_cmd); + } else { + + if (card->card_type == CARD_SD && + pcmd->data && + pcmd->data->dir == DATA_DIR_OUT && + ((cfg->enable_dll == 1) + && (cfg->enable_dll_divider == 1)) + && (card_info->sw_cur_setting.sd_access_mode == + SD_FNC_AM_DDR50)) { + ret = sd_dll_divider(card, pcmd); + if (ret) + goto exit; + } else if (pcmd->data + && pcmd->data->dir == DATA_DIR_OUT) { + if (card->card_type == CARD_SD + || card->card_type == CARD_EMMC) { + if (hostven_fix_output_tuning(card->host, + card_info->sw_cur_setting.sd_access_mode) + == FALSE) { + ret = card_output_tuning(card, + pcmd->argument); + if (ret) + goto exit; + } + } + } else if (pcmd->data + && pcmd->data->dir == DATA_DIR_IN) { + if (card->card_type == CARD_SD) { + ret = + sd_tuning(card, &sd_cmd, 0); + } else if (card->card_type == CARD_EMMC) { + ret = + emmc_tuning(card, &sd_cmd); + } + } + } + } + + } + +exit: + DbgInfo(MODULE_LEGACY_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Exit %s\n", __func__); + return ret; +} + +/* + * Function Name: card_degrade_policy + * + * Abstract: This Function is used set card degrade flag + * if blightway is set, this function can also do card operation which don't need reinit + * + * Input: + * + * sd_card_t *card : The Command will send to which Card + * sd_command_t *sd_cmd: if the init occurred at init stage this parameter will be null + * + * Output: None + * + * Return value: None + * + * Notes: + * + * Caller: card_init + * + */ + +void card_degrade_policy(sd_card_t *card, sd_command_t *sd_cmd) +{ + DbgInfo(MODULE_ALL_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Enter %s\n", __func__); + switch (card->card_type) { + case CARD_SD: + sd_degrade_policy(card); + break; + case CARD_UHS2: + uhs2_degrade_policy(card, sd_cmd); + break; + case CARD_EMMC: + case CARD_MMC: + mmc_degrade_policy(card); + break; + default: + break; + + } + DbgInfo(MODULE_ALL_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +/* + * Function Name: card_rw_recovery + * + * Abstract: This Function is used to do card rw error recovery + * + * Input: + * + * sd_card_t *card : The Command will send to which Card + * sd_command_t *sd_cmd: if the init occurred at init stage this parameter will be null + * + * Output: None + * + * Return value: If the routine succeeds, it must return TRUE, + * and fill trans_reg_t part. otherwize reutrn FALSE + * + * Notes: + * + * Caller: card_recovery_flow + * + */ + +static bool card_rw_recovery(sd_card_t *card, sd_command_t *sd_cmd) +{ + + if (sd_cmd == NULL) + return FALSE; + + switch (card->card_type) { + case CARD_EMMC: + case CARD_MMC: + case CARD_SD: + return legacy_error_recovery(card, sd_cmd); + case CARD_UHS2: + return uhs2_sd_error_recovery(card, sd_cmd); + default: + DbgErr("Error Card no RW error recovery\n"); + break; + + } + + return FALSE; +} + +/* + * Function Name: card_init_infinite + * + * Abstract: This Function is used to determine whehter use infinte or not according to card type + * + * Input: + * + * sd_card_t *card : The Command will send to which Card + * sd_host_t *host: Pointer to the host structure + * + * Output: None + * + * Return value: None + * + * Notes: + * + * Caller: card_init + * + */ + +static void card_init_transfer(sd_card_t *card, sd_host_t *host) +{ + bool autocmd23 = FALSE; + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + card->has_built_inf = FALSE; + card->last_dir = DATA_DIR_NONE; + card->last_sect = 0; + + if (host->cfg->host_item.test_infinite_transfer_mode.enable_inf == + FALSE) { + card->inf_trans_enable = FALSE; + goto next; + } + + switch (card->card_type) { + case CARD_UHS2: + card->inf_trans_enable = + (byte) host->cfg->host_item.test_infinite_transfer_mode.enable_sd40_inf; + break; + case CARD_SD: + card->inf_trans_enable = + (byte) host->cfg->host_item.test_infinite_transfer_mode.enable_legacy_inf; + break; + case CARD_MMC: + card->inf_trans_enable = + (byte) host->cfg->host_item.test_infinite_transfer_mode.enable_mmc_inf; + break; + case CARD_EMMC: + card->inf_trans_enable = + (byte) host->cfg->host_item.test_infinite_transfer_mode.enable_emmc_inf; + break; + default: + card->inf_trans_enable = FALSE; + break; + } + +next: + if ((card->card_type == CARD_SD) && (card->info.scr.cmd_support & 0x2)) + autocmd23 = TRUE; + + host_transfer_init(host, card->inf_trans_enable, FALSE); + host_enable_cmd23(host, autocmd23); + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +/* + * Function Name: card_switch2_adma + * + * Abstract: call this function to switch to adma2 mode, caller must restore. + * + * Input: + * + * sd_card_t *card : The Command will send to which Card + * sd_command_t *sd_cmd: if the init occurred at init stage this parameter will be null + * + * Output: None + * + * Return value: If the routine succeeds, it must return TRUE, otherwize reutrn FALSE + * + * Notes: + * + * Caller: + * + */ + +bool card_switch2_adma(sd_card_t *card, sd_command_t *sd_cmd) +{ + bool ret = FALSE; + + DbgInfo(MODULE_ALL_CARD, FEATURE_ERROR_RECOVER | FEATURE_IOCTL_TRACE, + NOT_TO_RAM, "Enter %s\n", __func__); + /* stop inf */ + if (card->has_built_inf) { + /* need stop first */ + ret = card_stop_infinite(card, FALSE, sd_cmd); + if (ret == FALSE) { + DbgErr("Stop Inf error for swithc2_adma\n"); + goto exit; + } + } + + host_transfer_init(card->host, FALSE, TRUE); + ret = TRUE; +exit: + DbgInfo(MODULE_ALL_CARD, FEATURE_ERROR_RECOVER | FEATURE_IOCTL_TRACE, + NOT_TO_RAM, "Enter %s ret=%d\n", __func__, ret); + return ret; +} + +/* + * Function Name: card_degrade_info_init + * + * Abstract: init card degrade info + * + * Input: + * + * sd_card_t *card : The Command will send to which Card + * sd_host_t *host: Pointer to the host structure + * + * Output: None + * + * Return value: None + * + * Notes: + * + * Caller: card_stuct_init + * + */ + +static void card_degrade_info_init(sd_card_t *card, sd_host_t *host) +{ + /* 1. Init the target access mode (Legacy mode) to the maximum access mode. */ + card->sw_target_setting.sd_access_mode = + (byte) host->cfg->card_item.test_max_access_mode.value; + + /* 2. Init the target drive strength */ + card->sw_target_setting.sd_drv_type = + (byte) host->cfg->card_item.test_driver_strength_sel.value; + + /* 3. Init the target power limit */ + card->sw_target_setting.sd_power_limit = + (byte) host->cfg->card_item.test_max_power_limit.value; + + card->degrade_uhs2_range = 0; + card->degrade_uhs2_half = 0; + card->degrade_uhs2_legacy = 0; + card->degrade_final = 0; + card->degrade_freq_level = 0; + + /* below item is used for thremal control */ + card->thermal_enable = 0; + card->thermal_uhs2_range = 0; + card->thermal_uhs2_half_dis = 0; + card->thermal_uhs2_lpm = 0; + card->thermal_access_mode = 0; + card->thermal_power_limit = 0; + + card->continue_init_fail_cnt = 0; + card->continue_rw_err_cnt = 0; + card->adma_err_cnt = 0; + +} + +/* + * Function Name: card_stuct_init + * + * Abstract: + * + * 1. init card control info, such as degrade info + * 2. bind card to host and memset function + * + * Input: + * + * bht_dev_ext_t* pdev_ext: Pointer to the device structure + * + * Output: None + * + * Return value: None + * + * Notes: + * + * Caller: req_global_init + * + */ + +void card_stuct_init(bht_dev_ext_t *pdev_ext) +{ + sd_card_t *card; + sd_host_t *host; + + /* Support 1 virtual card so far. */ + card = &(pdev_ext->card); + + /* 1. Zero the card structure */ + os_memset(card, 0, sizeof(sd_card_t)); + + /* 2. set the host point of card */ + card->host = &(pdev_ext->host); + host = card->host; + + /* 3. Error Count clear */ + card->adma_err_cnt = 0; + card->continue_init_fail_cnt = 0; + card->continue_rw_err_cnt = 0; + card->restore_tuning_content_fail = 0; + card->read_signal_block_flag = 0; + + card_degrade_info_init(card, host); + card->host->output_tuning.auto_phase_flag = FALSE; + card->retry_output_fail_phase = 0xFF; +} + +/* + * Function Name: card_stuct_uinit + * + * Abstract: + * + * 1. this function is called by card remvoe and enter pm + * 2. this function will only clear software flag + * + * Input: + * + * sd_card_t *card : Pointer to the card structure + * + * Output: None + * + * Return value: None + * + * Notes: + * + * Caller: remove_card_handle + * + */ + +void card_stuct_uinit(sd_card_t *card) +{ + card->initialized_once = FALSE; + card->card_type = CARD_NONE; + os_memset(&card->info, 0, sizeof(card->info)); + card->has_built_inf = FALSE; + card->inf_trans_enable = FALSE; + card->last_dir = DATA_DIR_NONE; + card->last_sect = 0; + os_memset(&card->mmc, 0, sizeof(card->mmc)); + os_memset(&card->uhs2_info, 0, sizeof(card->uhs2_info)); + card->quirk = 0; + card->quick_init = 0; + + card->adma_err_cnt = 0; + card->continue_init_fail_cnt = 0; + card->continue_rw_err_cnt = 0; + card->restore_tuning_content_fail = 0; + card->read_signal_block_flag = 0; + + card->state = CARD_STATE_POWEROFF; + card->write_protected = FALSE; + card_degrade_info_init(card, card->host); + card->host->output_tuning.auto_phase_flag = FALSE; + card->thread_init_card_flag = 0; + card->retry_output_fail_phase = 0xFF; +} + +static inline bool uhs2_support(sd_host_t *host) +{ + bool ret = TRUE; + /* 1. Host do not support UHSII */ + if (!host->uhs2_supp) + ret = FALSE; + + /* 1 TODO. correct the check condition */ + /* 2. Configuration settings to disable UHSII function */ + if (host->cfg->card_item.sd_card_mode_dis.dis_sd40_card) + ret = FALSE; + return ret; + +} + +static inline bool emmc_enabled(sd_host_t *host) +{ + /* 1. Configuration settings to disable eMMC function */ + bool ret = FALSE; + + if (host->cfg->card_item.emmc_mode.emmc_enable) + ret = TRUE; + + return ret; +} + +inline bool mmc_disabled(sd_host_t *host) +{ + /* 1. Configuration settings to disable eMMC function */ + bool ret = FALSE; + + if (host->cfg->card_item.mmc_mode_dis.dis_mmc_func) + ret = TRUE; + return ret; +} + +static void card_variable_init(sd_card_t *card) +{ + card->info.card_ccs = 0; + card->info.card_s18a = 0; + card->info.rca = 0; + card->info.ddr_flag = 0; + card->info.io_signal_vol = 0; + os_memset(&card->info.sw_cur_setting, 0, sizeof(sd_sw_func_t)); + + card->uhs2_info.dev_id = 0; + os_memset(&card->uhs2_info.uhs2_setting, 0, sizeof(uhs2_info_t)); + card->mmc.cur_buswidth = EMMC_1Bit_BUSWIDTH; + card->mmc.cur_hs_type = 0; +} + +/* + * Related register setting and Driver behavior description + * 1). SD7.0 Card capacibility detection register + * 0x1e0[29:28] = 2’b11: Enable hardware capability detection interrupt; + * 0x1e0[25:24] = 2’b11: enable hardware capability detection interrupt status. + * 0x1e0[17:16]: write 1 to this bit to clear interrupt status. + * + */ +static bool check_express_card_clkreqn_status(sd_card_t *card) +{ + u32 delay_us = 1; + u32 delay_ms; + loop_wait_t wait; + u32 regval; + bool ret = FALSE; + sd_host_t *host = card->host; + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + switch (host->cfg->card_item.sd7_sdmode_switch_control.switch_method_ctrl) { + case HW_DETEC_HW_SWITCH: + /* hardware interrupt control */ + DbgInfo(MODULE_SD_HOST, FEATURE_INTR_TRACE, NOT_TO_RAM, + "before clkreqn_status : %d\n", host->clkreqn_status); + while (1) { + if (os_atomic_read(&host->clkreqn_status) == 1) { + ret = TRUE; + DbgInfo(MODULE_SD_HOST, FEATURE_INTR_TRACE, + NOT_TO_RAM, + "Wait express clkreqn complete status ok\n"); + break; + } else if (os_atomic_read(&host->clkreqn_status) == 2) { + ret = FALSE; + DbgInfo(MODULE_SD_HOST, FEATURE_INTR_TRACE, + NOT_TO_RAM, + "Wait express clkreqn timeout\n"); + break; + } + + if (card->card_present == FALSE) { + ret = FALSE; + DbgErr("card is removed\n"); + break; + } + } + DbgInfo(MODULE_SD_HOST, FEATURE_INTR_TRACE, NOT_TO_RAM, + "after clkreqn_status : %d\n", host->clkreqn_status); + break; + + case SW_POLL_SW_SWITCH: + case SW_POLL_SWCTRL_SWITCH: + /* software control */ + + /* set polling tmie fix value 30ms */ + delay_ms = 30; + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "software control\n"); + + util_init_waitloop(host->pdx, delay_ms, delay_us, &wait); + while (!util_is_timeout(&wait)) { + if (host_check_lost(host)) { + ret = TRUE; + break; + } + + if (((sdhci_readl(host, 0x1e0) & (0x1)) == 0)) { + ret = TRUE; + break; + } + + if (card->card_present == FALSE) { + ret = FALSE; + DbgErr("card is removed\n"); + break; + } + } + + break; + + case SW_POLL_INTER_SW_SWITCH: + case SW_POLL_INTER_SWCRTL_SWITCH: + /* hardware polling control */ + DbgInfo(MODULE_SD_HOST, FEATURE_INTR_TRACE, NOT_TO_RAM, + "software polling control\n"); + while (1) { + if (host_check_lost(host)) { + ret = TRUE; + DbgErr("chip lost, already switch to sd7.0\n"); + break; + } + + regval = sdhci_readl(host, 0x1e0); + if (regval & (1 << 16)) { + + DbgInfo(MODULE_SD_HOST, FEATURE_INTR_TRACE, + NOT_TO_RAM, + "Wait express clkreqn complete status ok\n"); + ret = TRUE; + sdhci_or16(host, 0x1e2, 0x01); + break; + } else if (regval & (1 << 17)) { + DbgInfo(MODULE_SD_HOST, FEATURE_INTR_TRACE, + NOT_TO_RAM, + "Wait express clkreqn timeout\n"); + ret = FALSE; + sdhci_or16(host, 0x1e2, 0x02); + break; + } + + if (card->card_present == FALSE) { + ret = FALSE; + break; + DbgErr("card is removed\n"); + } + } + break; + + default: + DbgErr("no such value!\n"); + break; + } + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Exit %s (%d)\n", __func__, ret); + + return ret; +} + +static bool Turn_on_vdd2_or_vdd3(sd_card_t *card, bool flag) +{ + sd_host_t *host = card->host; + u32 regval; + bool ret = FALSE; + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + regval = sdhci_readl(host, 0x1e0); + switch (host->cfg->card_item.sd7_sdmode_switch_control.switch_method_ctrl) { + case HW_DETEC_HW_SWITCH: + /* set 0x1e0[29:28] = 2’b11, set 0x1e0[25:24] = 2’b11 */ + regval |= (0x33 << 24); + break; + + case SW_POLL_SW_SWITCH: + case SW_POLL_SWCTRL_SWITCH: + /* set 0x1e0[29:28] = 2’b00, set 0x1e0[25:24] = 2’b00 */ + regval &= 0xccffffff; + break; + + case SW_POLL_INTER_SW_SWITCH: + case SW_POLL_INTER_SWCRTL_SWITCH: + /* set 0x1e0[29:28] = 2’b00, set 0x1e0[25:24] = 2’b11 */ + regval |= (0x3 << 24); + break; + + default: + DbgErr("Error:no such value in registry sd7_sdmode_switch_control, use default value\n"); + regval |= (0x33 << 24); + break; + } + sdhci_writel(host, 0x1e0, regval); + + /* 1:VDD3 0:VDD2 */ + if ((flag) + && (host->cfg->card_item.sd7_sdmode_switch_control.vdd3_control)) { + + /* Turn on vdd3 */ + os_atomic_set(&host->clkreqn_status, 0); + host_set_vddx_power(host, VDD3, POWER_ON); + ret = check_express_card_clkreqn_status(card); + if (!ret) { + DbgErr("check clkreq failed fater turn on vdd3\n"); + /* Turn off vdd3 */ + host_set_vddx_power(host, VDD3, POWER_OFF); + + /* Turn on vdd2 */ + os_atomic_set(&host->clkreqn_status, 0); + host_set_vddx_power(host, VDD2, POWER_ON); + ret = check_express_card_clkreqn_status(card); + if (!ret) { + DbgErr("check clkreq failed fater turn on vdd2\n"); + host_set_vddx_power(host, VDD2, POWER_OFF); + } + } + } else { + /* Turn on vdd2 */ + os_atomic_set(&host->clkreqn_status, 0); + host_set_vddx_power(host, VDD2, POWER_ON); + + ret = check_express_card_clkreqn_status(card); + if (!ret) { + DbgErr("check clkreq failed fater turn on vdd2 derectily\n"); + host_set_vddx_power(host, VDD2, POWER_OFF); + } + } + return ret; +} + +bool pcie_mode_init(sd_card_t *card, bool code_flag) +{ + sd_host_t *host = card->host; + u32 regval; + bool ret; + bool host_support_vdd3; + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* set flag that driver in sd_express mode */ + host->sd_express_flag = TRUE; + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Set sd_express_flag\n"); + + regval = sdhci_readl(host, 0x44); + if (!(regval & (1 << 29))) + host_support_vdd3 = FALSE; + else + host_support_vdd3 = TRUE; + + /* flag 1:for sd cmd code */ + if (code_flag) { + + host_enable_clock(host, FALSE); + + /* stop clk */ + if (shift_bit_func_enable(host)) { + set_pattern_value(host, 0x34); + return TRUE; + } + + if (card->card_support_vdd3 && host_support_vdd3) + ret = Turn_on_vdd2_or_vdd3(card, TRUE); + else + ret = Turn_on_vdd2_or_vdd3(card, FALSE); + + } else { + /* flag 0 :for trail run code */ + if (shift_bit_func_enable(host)) { + set_pattern_value(host, 0x34); + return TRUE; + } + ret = Turn_on_vdd2_or_vdd3(card, host_support_vdd3); + } + + if (ret) { + /* + * Software: if pcr 0x444[9]=1, + * set sd host register 054h[8]=1 to assert express_card_mode + */ + regval = pci_readl(host, 0x444); + if (regval & (1 << 9)) { + regval = sdhci_readl(host, 0x54); + regval |= (1 << 8); + sdhci_writel(host, 0x54, regval); + } + return TRUE; + + } + + DbgErr("Exit pcie mode init with FALSE\n"); + host->sd_express_flag = FALSE; + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Clear sd_express_flag\n"); + return FALSE; +} + +bool gg8_get_card_capability_flag(sd_card_t *card, bool check_uhs2_flag) +{ + bool ret; + bool flag_f8 = FALSE; + sd_command_t sd_cmd; + sd_host_t *host = card->host; + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + if (check_uhs2_flag) { + host_init(host); + card_variable_init(card); + host_init_400k_clock(host); + host_internal_clk_setup(host, TRUE); + + /* 1. Power on card */ + if (host_get_vdd1_state(host) == FALSE) { + os_mdelay(10); + /* host_set_vdd1_power(host, TRUE, SDHCI_POWER_VDD1_330); */ + if (shift_bit_func_enable(host)) + set_pattern_value(host, 0x11); + + host_set_vddx_power(host, VDD1, POWER_ON); + } + } + /* 1 SD CLK Start */ + host_enable_clock(host, TRUE); + + /* 2 CMD0 */ + ret = card_reset_card(card, &sd_cmd); + if (!ret) { + /* Go Idle State command failed. exit directly. */ + DbgErr("Reset Card (CMD0) Failed.\n"); + return FALSE; + + } + /* 3. Issue send IF condition command (CMD8) */ + if (check_uhs2_flag) + ret = sd_send_if_cond(card, &sd_cmd, 0x000001AA); + else + ret = sd_send_if_cond(card, &sd_cmd, 0x000031AA); + + + if (!ret) { + /* 3.1 Error response */ + if (sd_cmd.err.error_code == ERR_CODE_RESP_ERR || + sd_cmd.err.error_code == ERR_CODE_NO_CARD) { + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "CMD8 Response Error or no card.\n"); + } else { + /* 5.2 No Response (Standard Capacity Card) */ + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "CMD8 No Responser.\n"); + } + + return FALSE; + + } else { + /* 5.3 Good Response (High Capacity card) */ + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "CMD8 Good Responser\n"); + flag_f8 = TRUE; + } + + /* 5.4.1 read R7 */ + + /* sd_send_if_cond argument = 0x000031AA check card support pcie */ + if (check_uhs2_flag == FALSE) { + /* host ask card's PCIe availability */ + if (!(sd_cmd.response[0] & 0x1000)) + card->card_support_pcie = FALSE; + else + card->card_support_pcie = TRUE; + + if (sd_cmd.response[0] & 0x2000) { + /* host ask whether card support VDD3 */ + card->card_support_vdd3 = TRUE; + } else + card->card_support_vdd3 = FALSE; + + } + + /* if check_uhs2_flag == true, send ACMD41 to check response bit 29 */ + if (check_uhs2_flag) { + ret = card_init_ready(card, &sd_cmd, flag_f8); + + if (!ret) { + DbgErr("Wait for card ready (ACMD41) Failed.\n"); + return FALSE; + } + } + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + return TRUE; +} + +bool gg8_sd70_card_init(sd_card_t *card) +{ + bool ret; + u32 regval; + sd_host_t *host = card->host; + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + card->pcie_init_flag = TRUE; + + /* update card status */ + card->card_present = hostven_chk_card_present(host); + + /* check card exist? */ + if (card->card_present == FALSE || card->card_chg) + return FALSE; + + + if (INIT_DELAY & INIT_DELAY_EN_MASK) { + os_mdelay(INIT_DELAY & INIT_DELAY_CFG_MASK); + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "%s init delay %d ms\n", __func__, + (INIT_DELAY & INIT_DELAY_CFG_MASK)); + } else { + os_mdelay(200); + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "%s init delay %d ms\n", __func__, 200); + } + + /* 0 host side init */ + host_init(host); + card_variable_init(card); + host_init_400k_clock(host); + host_internal_clk_setup(host, TRUE); + + /* 1. Power on card */ + if (shift_bit_func_enable(host) && + (host->cfg->card_item.sd7_sdmode_switch_control.card_init_flow_select)) { + os_mdelay(10); + + if (shift_bit_func_enable(host)) + set_pattern_value(host, 0x10); + + host_set_vddx_power(host, VDD1, POWER_ON); + } else { + if (host_get_vdd1_state(host) == FALSE) { + os_mdelay(10); + + if (shift_bit_func_enable(host)) + set_pattern_value(host, 0x10); + + host_set_vddx_power(host, VDD1, POWER_ON); + } + } + + regval = pci_readl(host, 0x444); + if (!(regval & (0x1 << 11))) { + /* CMD8 */ + + /* FALSE no need to send ACMD41 */ + ret = gg8_get_card_capability_flag(card, FALSE); + if (!ret) { + DbgErr + ("gg8_get_card_capability_flag exit with faile\n"); + } + + regval = pci_readl(host, 0x444); + if (!(regval & (0x7 << 8))) { + DbgErr("host not support to switch to sd7.0\n"); + card->pcie_init_flag = FALSE; + return FALSE; + } + + if (card->card_support_pcie) { + ret = pcie_mode_init(card, TRUE); + if (!ret) { + DbgErr("pci cmd mode init failed\n"); + card->pcie_init_flag = FALSE; + } else { + card->pcie_init_flag = TRUE; + } + } else { + DbgErr("card not support pcie\n"); + card->pcie_init_flag = FALSE; + return FALSE; + } + } else { + + /* trail run */ + ret = pcie_mode_init(card, FALSE); + if (!ret) { + DbgErr("pci trail run mode init failed\n"); + card->pcie_init_flag = FALSE; + } else { + card->pcie_init_flag = TRUE; + } + } + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + /* Thomas test: call remove here. */ + if (card->pcie_init_flag == TRUE) { + bht_sd_remove(host->pci_dev.pci_dev); + + return TRUE; + } + + if (ret) { + card_init_transfer(card, host); + card->initialized_once = TRUE; + card->state = CARD_STATE_WORKING; + card->continue_init_fail_cnt = 0; + if (host_wr_protect_pin(host) || card_wr_protect(card)) + card->write_protected = TRUE; + else + card->write_protected = FALSE; + + return TRUE; + } else { + return FALSE; + } +} + +/* + * Function Name: card_pcie_support + * + * Abstract: check whether the card supports SD7.0 mode + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * + * Output: None + * + * Return value: return TRUE if the card supports SD7.0 mode, otherwise return FALSE + * + * Notes: + * + * Caller: card_init + * + */ + +bool card_pcie_support(sd_card_t *card) +{ + bool ret = FALSE; + u32 regval = 0; + bool host_support_sd70 = FALSE; + bool sd_cmd_low = FALSE; + bool registry_support_sd70 = TRUE; + bool any_switch_case_enable = FALSE; + sd_host_t *host = card->host; + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + if ((sdhci_readl(host, 0x40) & (1 << 20))) + host_support_sd70 = TRUE; + + if ((pci_readl(host, 0x444) & 0x700)) + any_switch_case_enable = TRUE; + + /* polling PCR 0x448[31] */ + if ((pci_readl(host, 0x448) & (1 << 31))) { + regval = pci_readl(host, 0x448); + regval |= (1 << 31); + pci_writel(host, 0x448, regval); + card->cmd_low_reset_flag = TRUE; + } else { + sd_cmd_low = TRUE; + } + + if (host->cfg->card_item.sd_card_mode_dis.dis_sd70_card) + registry_support_sd70 = FALSE; + + if (host_support_sd70 && sd_cmd_low && registry_support_sd70 + && any_switch_case_enable && (card->cmd_low_reset_flag == FALSE)) { + if ((pci_readl(host, 0x444) & (1 << 10))) { + if ((pci_readl(host, 0x444) & (1 << 15))) { + if ((pci_readl(host, 0x50c) & (1 << 6))) + ret = FALSE; + else + ret = TRUE; + } else { + if ((pci_readl(host, 0x50c) & (1 << 6))) + ret = TRUE; + else + ret = FALSE; + } + } else { + ret = TRUE; + } + } else { + ret = FALSE; + } + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Exit %s with(%d)\n", __func__, ret); + return ret; +} + +/* + * Function Name: card_init + * + * Abstract: Main card initialize entry. + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * int retry_num [in]: Retry number if card init failed. + * bool bfullreset: full reset flag + * + * Output: None + * + * Return value: Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: thread_init_card + * + */ + +bool card_init(sd_card_t *card, int retry_num, bool bfullreset) +{ + bool ret = FALSE; + bool stbl = FALSE; + sd_host_t *host = card->host; + bool first_init = TRUE; + u32 regval; + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + if (host->pdx == NULL) { + DbgErr("host->pdx should not be NULL\n"); + return FALSE; + } + if (host_check_lost(host)) { + DbgErr("Host lost at card init start\n"); + return FALSE; + } + + if (shift_bit_func_enable(host) && + (host->cfg->card_item.sd7_sdmode_switch_control.card_init_flow_select)) + goto retry; + else + goto express_flow; + +express_flow: + /* SD7.0 card mode init flow */ + if (host->chip_type == CHIP_GG8) { + if (card_pcie_support(card)) { + ret = gg8_sd70_card_init(card); + + if (!ret) { + regval = pci_readl(host, 0x444); + regval &= (~(1 << 11)); + pci_writel(host, 0x444, regval); + } + + if (card->pcie_init_flag == FALSE) { + if (card->card_present == TRUE) { + if (shift_bit_func_enable(host) + && (host->cfg->card_item.sd7_sdmode_switch_control.card_init_flow_select)) + goto legacy; + else + goto retry; + } else + return FALSE; + } else { + card->card_type = CARD_SD70; + card->card_present = FALSE; + return ret; + } + } else if (card->card_present == TRUE) { + if (shift_bit_func_enable(host) + && (host->cfg->card_item.sd7_sdmode_switch_control.card_init_flow_select)) + goto legacy; + else + goto retry; + } else { + return FALSE; + } + } + +retry: + + /* update card status */ + card->card_present = hostven_chk_card_present(host); + if (first_init == TRUE) { + first_init = FALSE; + } else { + + /* check card exist? */ + if (card->card_present == FALSE || card->card_chg) { + ret = FALSE; + goto end; + } + + if (INIT_DELAY & INIT_DELAY_EN_MASK) { + os_mdelay(INIT_DELAY & INIT_DELAY_CFG_MASK); + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "%s init delay %d ms\n", __func__, + (INIT_DELAY & INIT_DELAY_CFG_MASK)); + } else { + os_mdelay(200); + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "%s init delay %d ms\n", __func__, 200); + } + + } + + /* Do some Host side initialization */ + if (bfullreset == FALSE) + host_init(host); + card_variable_init(card); + + /* Check eMMC function enabled or not */ + if (emmc_enabled(host)) { + hostven_set_pml0_requrest(host, FALSE); + ret = emmc_init(card, TRUE); + goto exit; + } + + if (card_need_get_info(card) == FALSE) { + switch (card->card_type) { + case CARD_EMMC: + case CARD_MMC: + goto mmc; + case CARD_SD: + goto legacy; + default: + break; + } + } + + /* Check host and configuration support UHSII or not */ + if ((uhs2_support(host)) + && (card->degrade_uhs2_legacy == 0) + && (card->card_type != CARD_MMC && card->card_type != CARD_SD)) { + u32 clk_value = card_get_uhs2_freq(card); + + DbgErr("host support uhs2\n"); + DbgErr("uhs2 trail run mode\n"); + + hostven_set_pml0_requrest(host, TRUE); + host_uhs2_init(host, clk_value, bfullreset); + ret = host_uhs2_phychk(host, FALSE, &stbl); + + /* phy init ok */ + if (ret) { + ret = uhs2_card_init(card); + if (ret) { + if (shift_bit_func_enable(host)) + set_pattern_value(host, 0x32); + + goto exit; + } else if (card->card_type == CARD_SDIO) { + ret = FALSE; + goto end; + } + } + /* stbl check failed */ + else if (stbl == FALSE) { + if (shift_bit_func_enable(host) + && (host->cfg->card_item.sd7_sdmode_switch_control.card_init_flow_select)) { + host_uhs2_clear(host, + (bool)host->cfg->card_item.test_uhs2_setting2.enable_power_off_vdd1); + regval = pci_readl(host, 0x444); + if (regval & (1 << 11)) { + regval &= (~(1 << 11)); + pci_writel(host, 0x444, regval); + } + card_variable_init(card); + hostven_set_pml0_requrest(host, FALSE); + goto express_flow; + } + + if (card->card_type == CARD_UHS2 + && card->degrade_uhs2_legacy) { + card->card_type = CARD_NONE; + card->quick_init = 0; + card->degrade_freq_level = 0; + + /* If card last stb.l is ok we continue try as UHS2 */ + goto exit; + } + + host_uhs2_clear(host, + (bool)host->cfg->card_item.test_uhs2_setting2.enable_power_off_vdd1); + goto legacy; + } + + DbgErr("UHS2 init failed\n"); + /* UHS2 init failed case, try again */ + goto exit; + } + +legacy: + + regval = pci_readl(host, 0x444); + if (regval & (1 << 11)) { + regval &= (~(1 << 11)); + pci_writel(host, 0x444, regval); + } + + card_variable_init(card); + /* Do SD Legacy card initialization */ + if (card->card_type != CARD_MMC) { + hostven_set_pml0_requrest(host, FALSE); + ret = sd_legacy_init(card); + if (card->card_type == CARD_SDIO) { + ret = FALSE; + goto end; + } + + if ((ret == FALSE) + && (card->sw_ctrl_swicth_to_express == FALSE)) { + DbgErr("Legacy SD Init failed\n"); + goto mmc; + } else + goto exit; + } + +mmc: + if ((card->card_type != CARD_SD) && (card->card_type != CARD_UHS2)) { + if (mmc_disabled(host)) { + DbgErr("Registry disable MMC card function!!\n"); + goto exit; + } + host_poweroff(host, card->card_type); + host_init(host); + card_variable_init(card); + hostven_set_pml0_requrest(host, FALSE); + ret = emmc_init(card, FALSE); + } + +exit: + if (ret == TRUE) { + card_init_transfer(card, host); + card->initialized_once = TRUE; + card->state = CARD_STATE_WORKING; + card->continue_init_fail_cnt = 0; + if (host_wr_protect_pin(host) || card_wr_protect(card)) + card->write_protected = TRUE; + else + card->write_protected = FALSE; + + } else { + if (card->sw_ctrl_swicth_to_express == TRUE) + goto end; + + card->continue_init_fail_cnt++; + retry_num--; + if ((retry_num == 0) || + (card->card_present == FALSE) || + (card->card_type == CARD_ERROR) || host_check_lost(host)) { + goto end; + } + + /* Call degarde policy if try_times >= 4 */ + if (card->continue_init_fail_cnt >= CARD_INIT_DEGARDE_TIME) + card_degrade_policy(card, NULL); + + /* Need power cycle for retry, etc. */ + if (card->card_type == CARD_UHS2) { + if (host->cfg->card_item.test_uhs2_setting2.enable_full_reset_reinit) { + /* If last time not use fullreset, then use fullreset */ + bfullreset = bfullreset ? FALSE : TRUE; + if (bfullreset) { + uhs2_full_reset_card(card); + DbgErr + ("Card Init failed do fullreset retry\n"); + goto retry; + } + } + } + + host_poweroff(host, CARD_NONE); + card->state = CARD_STATE_POWEROFF; + DbgErr("Card Init failed do poweroff retry\n"); + goto retry; + } + +end: + if (ret == FALSE) { + host_poweroff(host, CARD_NONE); + card->state = CARD_STATE_POWEROFF; + if ((card->degrade_final) || + (card->card_type == CARD_NONE + && card->continue_init_fail_cnt >= 5)) { + DbgErr("Card finally Init failed\n"); + card->card_type = CARD_ERROR; + } + card->quick_init = 0; + } + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Exit %s ret=%d\n", __func__, ret); + return ret; +} + +bool card_init_stage2(sd_card_t *card) +{ + bool ret = FALSE; + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + switch (card->card_type) { + case CARD_SD: + ret = sd_init_stage2(card); + break; + case CARD_UHS2: + ret = uhs2_init_stage2(card); + break; + case CARD_MMC: + case CARD_EMMC: + ret = emmc_init_stage2(card); + break; + default: + + break; + } + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Exit %s ret=%d\n", __func__, ret); + return ret; +} + +/* + * Function Name: card_power_off + * + * Abstract: This function is used to set card to power off status + * 1. Resume from Sleep mode if necessary + * 2. Stop Infintie transfer if necessary + * 3. Poweroff Card + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * bool directly: If true means do card poweroff directly(often use at error case) + * + * Output: None + * + * Return value: None + * + * Notes: + * + * Caller: card_enter_sleep + * + */ + +void card_power_off(sd_card_t *card, bool directly) +{ + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_OPS, NOT_TO_RAM, + "Enter %s directy=%d\n", __func__, directly); + + if (directly) + goto next; + + /* If wake up failed than goto poweroff directly */ + if (card_resume_sleep(card, FALSE) == FALSE) + goto next; + + if (card_stop_infinite(card, FALSE, NULL) == FALSE) + goto next; + else { + /* go dormant for UHSII D3-hot */ + card_enter_sleep(card, FALSE, TRUE); + } + +next: + if (card->state != CARD_STATE_POWEROFF) + host_poweroff(card->host, card->card_type); + card->state = CARD_STATE_POWEROFF; + card->thread_init_card_flag = 0; + card->has_built_inf = FALSE; + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_OPS, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +/* + * Function Name: card_thermal_control + * + * Abstract: This Function is used to do card thremal control, only for SD + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * + * Output: None + * + * Return value: TRUE means ok, others means error, caller need do error recovery + * + * Notes: run in thread context + * + * Caller: func_thermal_control + * + */ + +bool card_thermal_control(sd_card_t *card) +{ + bool ret = FALSE; + + DbgInfo(MODULE_ALL_CARD, FEATURE_FUNC_THERMAL, NOT_TO_RAM, "Enter %s\n", + __func__); + + if (card->card_present == FALSE) + goto exit; + + if (card->card_type == CARD_SD) + ret = sd_thermal_control(card); + +exit: + DbgInfo(MODULE_ALL_CARD, FEATURE_FUNC_THERMAL, NOT_TO_RAM, "Exit %s\n", + __func__); + return ret; +} + +bool card_stop_infinite(sd_card_t *card, bool recover, sd_command_t *pcmd) +{ + bool ret = TRUE; + sd_command_t sd_cmd; + sd_command_t *cmd = (pcmd == NULL) ? &sd_cmd : pcmd; + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_OPS, NOT_TO_RAM, "Enter %s\n", + __func__); + + if (card->card_present == FALSE || card->has_built_inf == FALSE) + goto exit; + + ret = card_send_command12(card, cmd); + if (ret == FALSE && recover) { + DbgErr("Stop Inf failed for cmd12\n"); + ret = card_rw_recovery(card, cmd); + if (ret == FALSE) + goto exit; + } + + if (ret == TRUE) + ret = card_check_rw_ready(card, cmd, 150); + +exit: + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_OPS, NOT_TO_RAM, + "Exit %s ret=%d\n", __func__, ret); + return ret; + +} + +bool card_enter_sleep(sd_card_t *card, bool recover, bool deepslp) +{ + bool ret = TRUE; + sd_command_t sd_cmd; + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_OPS, NOT_TO_RAM, "Enter %s\n", + __func__); + + if (card->card_type == CARD_UHS2) { + if (deepslp && card->uhs2_info.uhs2_cap.hibernate == 0) + deepslp = FALSE; + ret = card_stop_infinite(card, recover, &sd_cmd); + if (ret == FALSE) + goto exit; + + ret = uhs2_enter_dmt(card, &sd_cmd, card->host, deepslp); + + if (ret == TRUE) { + card->state = + deepslp ? CARD_STATE_DEEP_SLEEP : CARD_STATE_SLEEP; + } + } + +exit: + if (ret == FALSE) { + DbgErr("enter sleep failed\n"); + card_power_off(card, TRUE); + } + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_OPS, NOT_TO_RAM, + "Exit %s ret=%d\n", __func__, ret); + return ret; + +} + +bool card_resume_sleep(sd_card_t *card, bool recover) +{ + bool ret = TRUE; + bool deepslp = FALSE; + sd_command_t sd_cmd; + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_OPS, NOT_TO_RAM, "Enter %s\n", + __func__); + + if (card->state != CARD_STATE_DEEP_SLEEP + && card->state != CARD_STATE_SLEEP) + goto exit; + + if (card->card_type == CARD_UHS2) { + deepslp = (card->state == CARD_STATE_DEEP_SLEEP) ? TRUE : FALSE; + ret = uhs2_resume_dmt(card, &sd_cmd, card->host, deepslp); + + if (ret == TRUE) + card->state = CARD_STATE_WORKING; + } + +exit: + if (ret == FALSE) { + DbgErr("resume sleep failed\n"); + if (recover) + card_power_off(card, TRUE); + } + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_OPS, NOT_TO_RAM, + "Exit %s ret=%d\n", __func__, ret); + return ret; +} + +bool card_piorw_data(sd_card_t *card, u32 sec_addr, u32 sec_cnt, + e_data_dir dir, byte *data) +{ + bool ret = FALSE; + + sd_command_t sd_cmd; + sd_host_t *host = card->host; + u8 cmd_index = 0; + u32 cmd_flag = CMD_FLG_R1 | CMD_FLG_RESCHK; + cfg_item_t *cfg = NULL; + + DbgInfo(MODULE_ALL_CARD, FEATURE_RW_TRACE, NOT_TO_RAM, + "Enter %s dir=%d seccnt=0x%08X secaddr=0x%08X\n", __func__, + dir, sec_cnt, sec_addr); + + X_ASSERT(host != NULL); + + cfg = host->cfg; + X_ASSERT(cfg != NULL); + + if (data == NULL) + goto exit; + + if (sec_cnt > 1) + cmd_flag |= CMD_FLG_MULDATA; + + if (cmd_flag & CMD_FLG_MULDATA) { + if (dir == DATA_DIR_OUT) + cmd_index = SD_CMD25; + else + cmd_index = SD_CMD18; + } else { + if (dir == DATA_DIR_OUT) + cmd_index = SD_CMD24; + else + cmd_index = SD_CMD17; + } + + cmd_set_auto_cmd_flag(card, &cmd_flag); + ret = + card_send_sdcmd(card, &sd_cmd, cmd_index, sec_addr, cmd_flag, dir, + data, sec_cnt * 512); + /* todo error recovery and cmd13 */ + +exit: + if (ret == FALSE) + DbgErr("Card Pio dir=%d seccnt=0x%08X secaddr=0x%08X failed\n", + dir, sec_cnt, sec_addr); + DbgInfo(MODULE_ALL_CARD, FEATURE_RW_TRACE, NOT_TO_RAM, + "Exit %s ret=%d\n", __func__, ret); + return ret; +} + +static void card_cmd_copy(sd_command_t *dst, sd_command_t *src) +{ + sd_data_t *data = dst->data; + + os_memcpy(dst, src, sizeof(sd_command_t)); + dst->data = data; + + if (data && src->data) + os_memcpy(dst->data, src->data, sizeof(sd_data_t)); + else + src->data = NULL; +} + +bool card_dma_rw_data(sd_card_t *card, u32 dma_mode, u32 sec_addr, u32 sec_cnt, + e_data_dir dir, byte *data, sg_list_t *sglist, + u32 sg_len, sd_command_t *cmd_err) +{ + bool ret = FALSE; + + sd_command_t sd_cmd; + sd_data_t sd_data; + sd_host_t *host = card->host; + u8 cmd_index = 0; + u32 cmd_flag = CMD_FLG_R1 | CMD_FLG_RESCHK; + cfg_item_t *cfg = NULL; + + DbgInfo(MODULE_ALL_CARD, FEATURE_RW_TRACE, NOT_TO_RAM, + "Enter %s dir=%d seccnt=0x%08X secaddr=0x%08X\n", __func__, + dir, sec_cnt, sec_addr); + + cfg = host->cfg; + + if (data == NULL && dma_mode != CFG_TRANS_MODE_ADMA2) { + DbgErr("%s argument wrong\n", __func__); + goto end; + } + + if (sec_cnt > 1) + cmd_flag |= CMD_FLG_MULDATA; + + if (cmd_flag & CMD_FLG_MULDATA) { + if (dir == DATA_DIR_OUT) + cmd_index = SD_CMD25; + else + cmd_index = SD_CMD18; + } else { + if (dir == DATA_DIR_OUT) + cmd_index = SD_CMD24; + else + cmd_index = SD_CMD17; + } + + cmd_set_auto_cmd_flag(card, &cmd_flag); + /* set dma mode */ + if (dma_mode == CFG_TRANS_MODE_SDMA) { + /* host_dma_select(card->host, TRANS_SDMA); */ + cmd_flag |= CMD_FLG_SDMA; + if ((card->card_type != CARD_UHS2) && + (cmd_flag & CMD_FLG_AUTO23)) { + /* SDMA don't use auto CMD23 */ + cmd_flag &= ~CMD_FLG_AUTO23; + cmd_flag |= CMD_FLG_AUTO12; + } + } else if (dma_mode == CFG_TRANS_MODE_ADMA2) { + cmd_flag |= CMD_FLG_ADMA2; + } else { + /* host_dma_select(card->host, TRANS_ADMA2); */ + cmd_flag |= CMD_FLG_ADMA_SDMA; + } + + os_memset(&sd_data, 0, sizeof(sd_data_t)); + ret = + build_dma_ctx(card->host->pdx, &sd_data, cmd_flag, dir, data, + sec_cnt * 512, sglist, sg_len); + if (ret == FALSE) { + DbgErr("build dma ctx failed\n"); + goto end; + } + + ret = + card_send_sdcmd_dma_timeout(card, &sd_cmd, &sd_data, cmd_index, + sec_addr, cmd_flag, dir, data, + sec_cnt * 512, 0); + + if (ret == FALSE && cmd_err != NULL) + card_cmd_copy(cmd_err, &sd_cmd); + +end: + if (ret == FALSE) { + DbgErr("Card dma dir=%d seccnt=0x%08X secaddr=0x%08X failed\n", + dir, sec_cnt, sec_addr); + } + + DbgInfo(MODULE_ALL_CARD, FEATURE_RW_TRACE, NOT_TO_RAM, + "Exit %s ret=%d\n", __func__, ret); + return ret; +} + +/* + * Currently this function is used for dump mode only, + * todo to let it support normal case(Add node2 init code for normal case) + */ +bool card_adma2_rw_inf(sd_card_t *card, u32 sec_addr, u32 sec_cnt, + e_data_dir dir, sg_list_t *sglist, u32 sg_len, + sd_command_t *cmd_err) +{ + u32 flg = 0; + bool ret = FALSE; + sd_command_t sd_cmd; + dma_desc_buf_t *pdma = 0; + bht_dev_ext_t *pdx = card->host->pdx; + node_t *node = NULL; + sd_data_t sd_data; + bool data_26bit_len = + pdx->cfg->host_item.test_dma_mode_setting.enable_dma_26bit_len ? + TRUE : FALSE; + DbgInfo(MODULE_ALL_CARD, FEATURE_RW_TRACE, NOT_TO_RAM, + "Enter %s dir=%d seccnt=0x%08X secaddr=0x%08X\n", __func__, + dir, sec_cnt, sec_addr); + + /* Step1 Check can use Infinte or not */ + flg = cmd_can_use_inf(card, dir, sec_addr, sec_cnt); + + /* not continue case stop infinite first */ + if (card->has_built_inf && (flg != CMD_FLG_INF_CON)) { + ret = card_stop_infinite(card, FALSE, &sd_cmd); + if (ret == FALSE) { + DbgErr("%s stop infinite failed\n", __func__); + goto exit; + } + } + + /* Non Infinte Case */ + if (flg == 0) { + ret = + card_dma_rw_data(card, CFG_TRANS_MODE_ADMA2, sec_addr, + sec_cnt, dir, NULL, sglist, sg_len, + cmd_err); + goto end; + } + + /* Step2 Build Infinte sd_cmd */ + node = + (pdx->dma_api.cur_node != + &pdx->dma_api.dma_node) ? &pdx->dma_api.dma_node : + &pdx->dma_api.dma_node2; + os_memset(&sd_cmd, 0, sizeof(sd_command_t)); + pdx->dma_api.cur_node = node; + if (dir == DATA_DIR_IN) + sd_cmd.cmd_index = SD_CMD18; + else + sd_cmd.cmd_index = SD_CMD25; + + sd_cmd.argument = sec_addr; + sd_cmd.cmd_flag |= + CMD_FLG_R1 | CMD_FLG_RESCHK | CMD_FLG_MULDATA | CMD_FLG_ADMA2 | flg; + sd_cmd.sd_cmd = 1; + + /* Step3 alloc dma desc buf */ + pdma = node_get_desc_res(node, MAX_ADMA2_TABLE_LEN); + if (pdma == NULL) { + DbgErr("%s get desc res failed\n", __func__); + ret = FALSE; + goto exit; + } + + node->phy_node_buffer.head = *pdma; + node->phy_node_buffer.end = + build_adma2_desc(sglist, sg_len, (byte *) pdma->va, pdma->len, + card->host->bit64_enable, data_26bit_len); + + if (node->phy_node_buffer.end.va == NULL) { + DbgErr("%s prepare dma buffer failed\n", __func__); + ret = FALSE; + goto exit; + } + + if (flg & CMD_FLG_INF_CON) + update_adma2_inf_tb(node->phy_node_buffer.end.va, + &(pdx->dma_api.adma2_inf_link_addr), + &node->phy_node_buffer.head.pa, + card->host->bit64_enable); + else + update_adma2_inf_tb(node->phy_node_buffer.end.va, + &(pdx->dma_api.adma2_inf_link_addr), NULL, + card->host->bit64_enable); + + /* Step4 Send Command12 */ + sd_cmd.data = &sd_data; + sd_cmd.data->data_mng.driver_buff = NULL; + sd_cmd.data->data_mng.offset = sd_cmd.data->data_mng.srb_cnt = 0; + sd_cmd.data->dir = dir; + sd_cmd.data->data_mng.total_bytess = sec_cnt * SD_BLOCK_LEN; + sd_cmd.data->data_mng.sys_addr = node->general_desc_tbl.pa; + + cmd_generate_reg(card, &sd_cmd); + /* 4.issue cmd */ + ret = cmd_execute_sync(card, &sd_cmd, NULL); + +exit: + if (ret == FALSE && cmd_err != NULL) + card_cmd_copy(cmd_err, &sd_cmd); + +end: + DbgInfo(MODULE_ALL_CARD, FEATURE_RW_TRACE, NOT_TO_RAM, + "Exit %s ret=%d\n", __func__, ret); + return ret; + +} + +/* + * Function Name: card_recovery_flow + * + * Abstract: This Function is used to do card rw error recovery flow + * + * Input: + * + * sd_card_t *card: The Command will send to which Card + * sd_command_t *sd_cmd: if the init occurred at init stage this parameter will be null + * + * Output: None + * + * Return value: + * REQ_RESULT_NO_CARD: card not exist or not card + * REQ_RESULT_ACCESS_ERR: card rw recovery failed + * REQ_RESULT_OK: no error + * + * Notes: This function is called in thread context to do RW Error Recovery + * + * Caller: tag_queue_rw_data_issue_stage + * + */ + +e_req_result card_recovery_flow(sd_card_t *card, sd_command_t *sd_cmd) +{ + e_req_result result = REQ_RESULT_ACCESS_ERR; + + DbgInfo(MODULE_ALL_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Enter %s\n", __func__); + + os_mdelay(50); + if (card->card_present == FALSE || card->card_chg + || host_check_lost(card->host) || card->sw_ctrl_swicth_to_express) { + DbgErr("Error Recover for no card\n"); + result = REQ_RESULT_NO_CARD; + goto exit; + } + + card->continue_rw_err_cnt++; + + /* If Adma Error */ + if (cmd_is_adma_error(sd_cmd)) { + DbgInfo(MODULE_ALL_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Adma error\n", __func__); + card->adma_err_cnt++; + if (card->adma_err_cnt >= 3) { + DbgInfo(MODULE_ALL_CARD, FEATURE_ERROR_RECOVER, + NOT_TO_RAM, "continue adma err>=3\n", + __func__); + card_degrade_policy(card, sd_cmd); + card->continue_rw_err_cnt = 0; + card->adma_err_cnt = 0; + /* card->thread_init_card_flag = 0; */ + card_power_off(card, TRUE); + if (card_init(card, 1, FALSE) == FALSE) { + if (card->card_type == CARD_ERROR) { + DbgErr("Adma error recover fatal\n"); + result = REQ_RESULT_NO_CARD; + } else { + DbgErr("Adma error recover failed\n"); + result = REQ_RESULT_ACCESS_ERR; + } + } else + result = REQ_RESULT_OK; + goto exit; + } + } + + if (card_rw_recovery(card, sd_cmd) == FALSE) { + card->continue_rw_err_cnt++; + if (card->continue_rw_err_cnt >= 3) { + DbgInfo(MODULE_ALL_CARD, FEATURE_ERROR_RECOVER, + NOT_TO_RAM, "continue rw err>=3\n", + __func__); + card_degrade_policy(card, sd_cmd); + card->continue_rw_err_cnt = 0; + card->adma_err_cnt = 0; + } + + card_power_off(card, TRUE); + if (card_init(card, 1, FALSE) == FALSE) { + if (card->card_type == CARD_ERROR) { + DbgErr(" error recover fatal\n"); + result = REQ_RESULT_NO_CARD; + } else { + DbgErr("error recover failed\n"); + result = REQ_RESULT_ACCESS_ERR; + } + } else + result = REQ_RESULT_OK; + } else + result = REQ_RESULT_OK; +exit: + DbgInfo(MODULE_ALL_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "EXIT %s\n", + __func__); + return result; +} + +bool card_set_blkcnt(sd_card_t *card, sd_command_t *sd_cmd, u32 blkcnt) +{ + bool ret = FALSE; + + DbgInfo(MODULE_LEGACY_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Enter %s blkcnt=%d\n", __func__, blkcnt); + ret = + card_send_sdcmd(card, sd_cmd, SD_CMD23, blkcnt, + CMD_FLG_R1 | CMD_FLG_RESCHK, DATA_DIR_NONE, NULL, + 0); + if (ret == FALSE) + DbgErr("issue cmd23 failed\n"); + + DbgInfo(MODULE_LEGACY_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Exit %s ret=%d\n", __func__, ret); + + return ret; +} + +/* + * Function Name: card_is_poweroff + * + * Abstract: This Function is used to get card power state + * + * Input: + * + * sd_card_t *card : The target Card + * + * Output: None + * + * Return value: + * TRUE: card poweroff + * FALSE: card doesn't poweroff + * + * Notes: + * + * Caller: tag_queue_rw_data_issue_stage + * + */ + +bool card_is_poweroff(sd_card_t *card) +{ + if (card->state == CARD_STATE_POWEROFF) + return TRUE; + else + return FALSE; +} + +/* + * Function Name: card_read_csd + * + * Abstract: De-select the card and send CMD9, and then select the card. + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * + * Output: + * + * byte *data: used for storing CSD data + * + * Return value: + * TRUE: read CSD successfully + * FALSE: occur error when read CSD + * + * Notes: + * + * Caller: thread_gen_io + * + */ + +bool card_read_csd(sd_card_t *card, byte *data) +{ + + sd_command_t sd_cmd; + + os_memset(&sd_cmd, 0, sizeof(sd_command_t)); + + return sd_read_csd(card, &sd_cmd, data); + +} + +/* + * Function Name: card_program_csd + * + * Abstract: Program CSD by CMD27 + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * + * Output: + * + * byte *data: used for storing CSD data + * + * Return value: return TRUE if program CSD successfully, else return FALSE + * + * Notes: + * + * Caller: thread_gen_io + * + */ + +bool card_program_csd(sd_card_t *card, byte *data) +{ + + sd_command_t sd_cmd; + + os_memset(&sd_cmd, 0, sizeof(sd_command_t)); + return sd_program_csd(card, &sd_cmd, data); + +} diff --git a/drivers/scsi/bht/card/mmc.c b/drivers/scsi/bht/card/mmc.c new file mode 100644 index 000000000000..449307972400 --- /dev/null +++ b/drivers/scsi/bht/card/mmc.c @@ -0,0 +1,1666 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: mmc.c + * + * Abstract: mmc/emmc card initialization + * + * Version: 1.00 + * + * Author: Amma.Li + * + * Environment: OS Independent + * + * History: + * + * 9/23/2014 Creation Amma.Li + */ +#include "../include/basic.h" +#include "../include/cardapi.h" +#include "../include/hostapi.h" +#include "../host/hostven.h" +#include "../host/hostreg.h" +#include "cardcommon.h" +#include "../include/cmdhandler.h" +#include "../include/debug.h" +#include "../include/util.h" +#define MMC_SPEC_VERS 0x04U +/* ------------------emmc setting-------------------- */ +/* ext_csd[196]: card type */ +#define MMC_CARD_TYPE_H200 0x30U +#define MMC_CARD_TYPE_H400 0xC0U +#define MMC_CARD_TYPE_HS 0x0FU +#define MMC_CARD_DDR_SUPP 0x0CU +#define MMC_CARD_TYPE_HS_DDR_12 0x8 +#define MMC_CARD_TYPE_HS_DDR_18 0x4 +#define MMC_CARD_TYPE_HS_52M 0x2 +#define MMC_CARD_TYPE_HS_26M 0x1 +/* ext_csd[183]: Bus Width */ +#define MMC_EXTCSD_BUS_WIDTH (0x00B70000) +#define MMC_BUSW_1BIT 0 +#define MMC_BUSW_SDR_4BIT (1 << 8) +#define MMC_BUSW_SDR_8BIT (2 << 8) +#define MMC_BUSW_DDR_4BIT (5 << 8) +#define MMC_BUSW_DDR_8BIT (6 << 8) +/* ext_csd[185]: HS_TIMING */ +#define MMC_EXTCSD_HS_TIMING (0x00B90000) +#define MMC_TIMING_BACKWARDS 0 +#define MMC_TIMING_HIGH_SPEED (1 << 8) +#define MMC_TIMING_HS200 (2 << 8) +#define MMC_TIMING_HS400 (3 << 8) +#define MMC_DRIVER_TYPE 0 +/* emmc CMD6 setting */ +#define MMC_EXTCSD_WRITE (3 << 24) +#define MMC_EXTCSD_SET (1 << 24) +#define MMC_EXTCSD_CLEAN (2 < 24) +/* emmc/mmc RCA */ +#define MMC_RCA (1 << 16) +/* -------------emmc setting end------------------ */ +static void emmc_get_ext_csd_info(sd_card_t *card); +static bool emmc_switch_buswidth(sd_card_t *card, sd_command_t *sd_cmd); +static void emmc_set_freq(sd_card_t *card, u32 clock_freq, bool bddr50); + +/* -------------emmc / mmc card CMD setting------------- */ + +/* + * Function Name: emmc_card_init_ready + * + * Abstract: + * 1. Issue CMD1 to Get OCR + * 2. Set the card ocr variable + * 3. Wait for card ready + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * + * Output: None + * + * Return value: Return TRUE if card ready, else return FALSE + * + * Notes: + * + * Caller: emmc_init + * + */ + +static bool emmc_card_init_ready(sd_card_t *card, sd_command_t *sd_cmd) +{ + byte cmd_index = SD_CMD1; + u32 argument = 0; + u32 cmdflag = CMD_FLG_R3; + e_data_dir dir = DATA_DIR_NONE; + byte *data = NULL; + u32 datalen = 0; + bool ret = FALSE; + loop_wait_t wait; + u32 delay_us = 20; + + sd_host_t *host = card->host; + card_info_t *card_info = &(card->info); + cfg_emmc_mode_t *emmc_mode = &(host->cfg->card_item.emmc_mode); + + if (emmc_mode->enable_18_vcc) + argument |= EMMC_OCR_LOW; + else + argument |= EMMC_OCR_HI; + + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s arg 0x%08x\n", __func__, argument); + + /* Wait for card ready */ + util_init_waitloop(card->host->pdx, + host->cfg->timeout_item.test_card_init_timeout.value, + delay_us, &wait); + + do { + ret = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, + dir, data, datalen); + if (ret == FALSE) { + DbgErr("Issue CMD1 to get eMMC card OCR Fail.\n"); + break; + } + + /* Check Busy status. 0b: On initialization; 1b: Initialization Complete. */ + if ((sd_cmd->response[0] & 0x80000000) == 0) { + os_udelay(delay_us); + continue; + } else { + break; + } + } while (!util_is_timeout(&wait)); + + /* If card ready, set related software flags */ + if (ret) { + /* check card ready or not */ + if (sd_cmd->response[0] & 0x80000000) { + if (sd_cmd->response[0] & 0x40000000) + /* the capability > 2GB */ + card_info->card_ccs = 1; + else + /* the capability < 2GB */ + card_info->card_ccs = 0; + } else + ret = FALSE; + } + + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +/* + * Function Name: emmc_set_rca + * + * Abstract: Set a new relative address RCA for MMC/eMMC card(CMD3) + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * + * Output: None + * + * Return value: Return TRUE if issue CMD3 successfully, else return FALSE + * + * Notes: + * + * Caller: emmc_init + * + */ + +bool emmc_set_rca(sd_card_t *card, sd_command_t *sd_cmd) +{ + bool ret = FALSE; + byte cmd_index = SD_CMD3; + u32 argument = 0; + u32 cmdflag = CMD_FLG_R1; + e_data_dir dir = DATA_DIR_NONE; + byte *data = NULL; + u32 datalen = 0; + card_info_t *card_info = &(card->info); + + argument = MMC_RCA; + + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s arg 0x%08x\n", __func__, argument); + + /* Issue CMD3 */ + ret = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, dir, + data, datalen); + if (ret) { + /* Update the card RCA */ + card_info->rca = (argument & 0xffff0000) >> 16; + } + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +/* + * Function Name: emmc_get_ext_csd + * + * Abstract: Read the MMC/EMMC card Ext_Csd Data + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * + * Output: None + * + * Return value: Return TRUE if issue eMMC CMD8 successfully, else return FALSE + * + * Notes: + * + * Caller: emmc_init + * + */ + +static bool emmc_get_ext_csd(sd_card_t *card, sd_command_t *sd_cmd) +{ + byte cmd_index = SD_CMD8; + u32 argument = 0; + u32 cmdflag = CMD_FLG_R1; + e_data_dir dir = DATA_DIR_IN; + byte data[512]; + u32 datalen = 512; + + bool ret = FALSE; + mmc_card_info_t *mmc_info = &(card->mmc); + + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* Issue eMMC CMD8 to get Ext_Csd */ + ret = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, dir, + data, datalen); + if (ret) { + /* Get the Ext_Csd */ + os_memcpy(&(mmc_info->raw_extcsd[0]), data, 512); + emmc_get_ext_csd_info(card); + } + + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +/* + * Function Name: emmc_send_cmd6 + * + * Abstract: + * 1. Issue CMD6 to switch mode to modify the Ext_Csd register + * 2. Set the emmc card hs_timing & bus width + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * argument: [31:26] set to 0 + * [25:24]: Access 1: set 2: clean 3: write + * [23:16]: Index the ext_csd index + * [15:8]: value + * [7:3]: set to 0 + * [2:0]: cmd set + * + * Output: None + * + * Return value: Return TRUE if issue CMD6 successfully, else return FALSE + * + * Notes: + * + * Caller: emmc_switch_buswidth + * + */ + +static bool emmc_send_cmd6(sd_card_t *card, + sd_command_t *sd_cmd, u32 argument) +{ + bool result = FALSE; + byte cmd_index = SD_CMD6; + u32 cmdflag = CMD_FLG_R1B; + e_data_dir dir = DATA_DIR_NONE; + byte *data = NULL; + u32 datalen = 0; + + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s arg = 0x%08x\n", __func__, argument); + result = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, dir, + data, datalen); + + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + result, __func__); + + return result; +} + +/* + * + * Function Name: emmc_bustest_r + * + * Abstract: + * 1. MMC/eMMC card bus width test read (CMD14) + * 2. A host reads the reversed bus testing data pattern from a Device. + * 3. Used after CMD19 (bus width test write CMD), + * need to check the cmd and data transfer error + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * u8 *buf : read data pattern buffer + * u32 data_len : read data length + * + * Output: None + * + * Return value: Return TRUE if issue CMD14 successfully, else return FALSE + * + * Notes: + * + * Caller: emmc_bus_width_test + */ +static bool emmc_bustest_r(sd_card_t *card, + sd_command_t *sd_cmd, u8 *buf, u32 data_len) +{ + bool result = FALSE; + byte cmd_index = SD_CMD14; + u32 argument = 0; + u32 cmdflag = CMD_FLG_R1 | CMD_FLG_RESCHK; + e_data_dir dir = DATA_DIR_IN; + byte *data = buf; + u32 datalen = data_len; + + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + result = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, dir, + data, datalen); + + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + result, __func__); + return result; +} + +/* + * + * Function Name: emmc_bustest_w + * + * Abstract: + * + * 1. MMC/eMMC card bus width test write (CMD19) + * 2. A host send the reversed bus testing data pattern from a Device. + * 3. Do not need to check the cmd and data transfer error + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * u8 *buf : write data pattern buffer + * u32 data_len : write data length + * + * Output: + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: card_init + */ +static bool emmc_bustest_w(sd_card_t *card, + sd_command_t *sd_cmd, u8 *buf, u32 data_len) +{ + bool result = FALSE; + byte cmd_index = SD_CMD19; + u32 argument = 0; + u32 cmdflag = CMD_FLG_R1 | CMD_FLG_RESCHK | CMD_FLG_NO_TRANS; + e_data_dir dir = DATA_DIR_OUT; + byte *data = buf; + u32 datalen = data_len; + + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + result = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, dir, + data, datalen); + + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + result, __func__); + + return result; +} + +/* + * + * Function Name: emmc_tuning_hw + * + * Abstract: + * + * 1. Hardware Tuning Procedure (CMD21) + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * + * Output: + * + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: sd_tuning + */ + +static bool emmc_tuning_hw(sd_card_t *card, sd_command_t *sd_cmd) +{ + bool result = FALSE; + byte cmd_index = SD_CMD21; + u32 argument = 0; + u32 cmdflag = CMD_FLG_R1 | CMD_FLG_TUNE; + e_data_dir dir = DATA_DIR_IN; + byte data[64]; + sd_host_t *host = card->host; + u32 datalen = 0x40; + + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* set hardware tuning */ + host_set_tuning_mode(host, TRUE); + + if ((host->chip_type == CHIP_SDS0) || + (host->chip_type == CHIP_SDS1) || (host->chip_type == CHIP_FUJIN2) + ) { + /* add 200us delay before CMD19 to fix FJ2 ASIC issue 14# */ + os_udelay(200); + } + + /* send emmc tuning CMD21 */ + result = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, dir, + data, datalen); + if (result == FALSE) { + DbgErr("eMMC card send hardware tuning CMD failed!!\n"); + goto exit; + } + + /* check tuning success or not */ + result = host_chk_tuning_comp(host, TRUE); + if (!result) + DbgErr("Check eMMC tuning failed!\n"); + +exit: + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + result, __func__); + return result; +} + +/* + * + * Function Name: sd_tuning_sw + * + * Abstract: + * + * 1. Software Tuning Procedure (CMD21) + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * + * Output: + * + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: sd_tuning + */ +static bool emmc_tuning_sw(sd_card_t *card, sd_command_t *sd_cmd) +{ + bool result = FALSE; + byte cmd_index = SD_CMD21; + u32 argument = 0; + u32 cmdflag = CMD_FLG_R1 | CMD_FLG_TUNE; + e_data_dir dir = DATA_DIR_IN; + byte data[64]; + u16 i = 0; + sd_host_t *host = card->host; + u32 datalen = 0x40; + + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + for (i = 0; i < 100; i++) { + /* set software tuning */ + host_set_tuning_mode(host, FALSE); + + if ((host->chip_type == CHIP_SDS0) || + (host->chip_type == CHIP_SDS1) || + (host->chip_type == CHIP_FUJIN2) + ) { + /* add 200us delay before CMD19 to fix FJ2 ASIC issue 14# */ + os_udelay(200); + } + + /* send emmc tuning CMD21 */ + result = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, + dir, data, datalen); + if (result == FALSE) { + DbgErr("eMMC card hardware tuning failed!!\n"); + goto exit; + } + + /* check tuning success or not */ + result = host_chk_tuning_comp(host, FALSE); + if (result) + break; + } + +exit: + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + result, __func__); + return result; +} + +/* + * + * Function Name: emmc_tuning + * + * Abstract: + * + * 1. Send Hw tuning or Sw tuning by the registry setting + * 2. tuning mode: 1 = CFG_TUNING_MODE_HW 0 = CFG_TUNING_MODE_SW + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * + * Output: + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: card_init + */ +bool emmc_tuning(sd_card_t *card, sd_command_t *sd_cmd) +{ + bool result = FALSE; + + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + if (card->mmc.cur_hs_type != EMMC_MODE_HS200 + && card->mmc.cur_hs_type != EMMC_MODE_HS400) { + result = TRUE; + goto exit; + } + + if (TUNING_MODE) + result = emmc_tuning_hw(card, sd_cmd); + else + result = emmc_tuning_sw(card, sd_cmd); + +exit: + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + result, __func__); + return result; +} + +/* + * + * Function Name: emmc_bus_width_test + * + * Abstract: + * 1. MMC/eMMC card bus width test by CMD14 (read) and CMD19(write) + * 2. The data pattern decided by the bus width (8-bit\4-bit) + * 3. Do not need to check the CMD19 any cmd and data transfer error + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * u32 data_len : data pattern length + * + * Output: + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: card_init + */ +static bool emmc_bus_width_test(sd_card_t *card, + sd_command_t *sd_cmd, u32 data_len) +{ + bool result = FALSE; + u8 buf[8] = { 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa }; + u8 patten[8] = { 0 }; + + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s datlen %d\n", __func__, data_len); + + if (card->card_present == FALSE) + goto exit; + + if (data_len == 4) { + os_memset(buf, 0, 8); + os_memset(buf, 0x5a, 4); + + } + + /* send CMD19 (eMMC bus write) */ + result = emmc_bustest_w(card, sd_cmd, buf, data_len); + + /* delay NCR clock */ + os_udelay(20); + + /* send CMD14 (eMMC bus read) */ + result = emmc_bustest_r(card, sd_cmd, patten, data_len); + if (!result) { + DbgErr("eMMC card CMD14 Receive bus width data Failed.\n"); + } else { + result = FALSE; + + /* check patten */ + if (data_len == 8) { + if ((patten[0] == 0xaa) || (patten[1] == 0x55)) { + result = TRUE; + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, + NOT_TO_RAM, + "8-bit bus width test OK!!\n"); + } + } else if (data_len == 4) { + if (patten[0] == 0xa5) { + result = TRUE; + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, + NOT_TO_RAM, + "4-bit bus width test OK!!\n"); + } + } + } + +exit: + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + result, __func__); + return result; +} + +/* + * + * Function Name: emmc_get_ext_csd_info + * + * Abstract: + * + * 1. Get the Ext_Csd structure + * (card_type, power class for 52M 26M 1.8V 3.3V voltage) + * from the Ext_Csd raw + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * + * Output: + * None + * + * Return value: + * + * None + * + * Notes: + * + * Caller: emmc_get_ext_csd + */ +static void emmc_get_ext_csd_info(sd_card_t *card) +{ + mmc_card_info_t *mmc_info = &(card->mmc); + extcsd_t *ext_csd = &(mmc_info->ext_csd); + u8 *raw_ext_csd = &(mmc_info->raw_extcsd[0]); + + ext_csd->card_type = *(raw_ext_csd + 196); + ext_csd->driver_strength_type = *(raw_ext_csd + 197); + ext_csd->pwr_cl_52_195 = *(raw_ext_csd + 200); + ext_csd->pwr_cl_26_195 = *(raw_ext_csd + 201); + ext_csd->pwr_cl_52_360 = *(raw_ext_csd + 202); + ext_csd->pwr_cl_26_360 = *(raw_ext_csd + 203); + ext_csd->pwr_cl_ddr_52_195 = *(raw_ext_csd + 238); + ext_csd->pwr_cl_ddr_52_360 = *(raw_ext_csd + 239); + ext_csd->sec_cnt = + (*(raw_ext_csd + 215) << 24) + (*(raw_ext_csd + 214) << 16) + + (*(raw_ext_csd + 213) << 8) + *(raw_ext_csd + 212); +} + +/* + * + * Function Name: emmc_switch_hs400 + * + * Abstract: + * 1. eMMC card switch HS400 mode + * + * Input: + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * + * Output: + * None + * + * Return value: + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * Caller: emmc_init_stage2 + */ +static bool emmc_switch_hs400(sd_card_t *card, sd_command_t *sd_cmd) +{ + bool result = FALSE; + u32 argument = 0; + sd_host_t *host = card->host; + + mmc_card_info_t *mmc_info = &(card->mmc); + + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + +#if (0) + /* 1. Check card support hs400 */ + if ((mmc_info->ext_csd.card_type & MMC_CARD_TYPE_H400) == 0) { + DbgErr("eMMC card don't support HS400 mode!!\n"); + goto exit; + } + + /* 2. check card support 8-bit bus width */ + if ((mmc_info->cur_buswidth != EMMC_8Bit_BUSWIDTH) || + (host->bus_8bit_supp == FALSE) + ) { + DbgErr("The card or host don't support 8bit bus width!!\n"); + goto exit; + } +#endif + + /* 3. clear UHSI mode select */ + host_set_uhs_mode(host, 0); + + /* 4.set card mode to DDR50 mode (eMMC: CMD6) */ + argument = + (MMC_EXTCSD_WRITE | MMC_EXTCSD_HS_TIMING | MMC_TIMING_HIGH_SPEED | + (mmc_info->drv_strength << 12)); + result = emmc_send_cmd6(card, sd_cmd, argument); + if (!result) { + DbgErr("Switch DDR50 mode Failed.\n"); + goto exit; + } + + /* 5. change clock to 50M Hz for DDR50 mode */ + emmc_set_freq(card, SD_CLK_50M, TRUE); + + /* 6.change to 8-bit DDR mode (eMMC: CMD6) */ + argument = + (MMC_EXTCSD_WRITE | MMC_EXTCSD_BUS_WIDTH | MMC_BUSW_DDR_8BIT); + result = emmc_send_cmd6(card, sd_cmd, argument); + if (!result) { + DbgErr("Change to 8-bit DDR mode Failed.\n"); + goto exit; + } + + /* 7.switch to hs400 */ + argument = + (MMC_EXTCSD_WRITE | MMC_EXTCSD_HS_TIMING | MMC_TIMING_HS400 | + (mmc_info->drv_strength << 12)); + result = emmc_send_cmd6(card, sd_cmd, argument); + if (!result) { + DbgErr("Switch hs400 mode Failed.\n"); + goto exit; + } + + /* 8.if switch hs400 ok, set PCI Register */ + host_emmc_hs400_set(host, TRUE); + + /* 9.change SDCLK frequency to 200M Hz */ + emmc_set_freq(card, SD_CLK_BASE, FALSE); + +exit: + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + result, __func__); + return result; +} + +/* + * + * Function Name: emmc_switch_hs200 + * + * Abstract: + * 1. eMMC card switch HS200 mode + * + * Input: + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * + * Output: + * None + * + * Return value: + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * Caller: emmc_init_stage2 + */ +static bool emmc_switch_hs200(sd_card_t *card, sd_command_t *sd_cmd) +{ + bool result = FALSE; + u32 argument = 0; + u32 card_status = 0; + sd_host_t *host = card->host; + mmc_card_info_t *mmc_info = &(card->mmc); + cfg_emmc_mode_t *emmc_mode = &(host->cfg->card_item.emmc_mode); + + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* 1. switch signal Data Rate mode bus width (4-bit or 8-bit: eMMC CMD6) */ + mmc_info->cur_hs_type = EMMC_MODE_HS200; + result = emmc_switch_buswidth(card, sd_cmd); + if (!result) { + DbgErr("Set signal Data Rate 8/4-bit Bus Width Failed.\n"); + goto exit; + } + + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Card support driver type %x\n", + mmc_info->ext_csd.driver_strength_type); + if ((2 << (emmc_mode->drv_strength)) & + (mmc_info->ext_csd.driver_strength_type) + ) + mmc_info->drv_strength = (byte) emmc_mode->drv_strength; + else + mmc_info->drv_strength = 0; + /* 2.switch card support driver type & hs200 mode (eMMC: CMD6) */ + argument = + (MMC_EXTCSD_WRITE | MMC_EXTCSD_HS_TIMING | MMC_TIMING_HS200 | + (mmc_info->drv_strength << 12)); + result = emmc_send_cmd6(card, sd_cmd, argument); + if (!result) { + DbgErr("Switch HS200 mode Failed.\n"); + goto exit; + } + + /* 3.check card status (Issue CMD13) */ + result = card_get_card_status(card, sd_cmd, &card_status); + if ((result == FALSE) || (card_status & 0x80) + ) { + DbgErr("Card Status failed.\n"); + goto exit; + } + + /* 4.change clock to 200M Hz */ + emmc_set_freq(card, SD_CLK_BASE, FALSE); + + /* 5.switch mode (hs200 == SDR104) */ + host_set_uhs_mode(host, SDHCI_CTRL_UHS_HS200); + + if (((host->chip_type < CHIP_SEAEAGLE2) || (host->chip_type == CHIP_GG8) + || (host->chip_type == CHIP_ALBATROSS)) + && (mmc_info->cur_buswidth == EMMC_8Bit_BUSWIDTH)) { + host_set_buswidth(host, BUS_WIDTH4); + } + + result = emmc_tuning(card, sd_cmd); + if (!result) + goto exit; + + if (mmc_info->cur_buswidth == EMMC_8Bit_BUSWIDTH) + host_set_buswidth(host, BUS_WIDTH8); + + /* 7.if tuning ok, set PCI & Host Register */ + host_emmc_hs400_set(host, FALSE); + +exit: + if (result == FALSE) + mmc_info->cur_hs_type = 0; + else + emmc_mode->enable_ddr_mode = 0; + + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + result, __func__); + return result; +} + +/* + * + * Function Name: emmc_switch_hs + * + * Abstract: + * 1. eMMC card switch High Speed mode + * + * Input: + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * + * Output: + * None + * + * Return value: + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * Caller: emmc_init_stage2 + */ +static bool emmc_switch_hs(sd_card_t *card, sd_command_t *sd_cmd) +{ + bool result = FALSE; + u32 argument = 0; + u32 clk = 0; + bool bddr50 = FALSE; + sd_host_t *host = card->host; + u32 b_dis_hs = host->cfg->card_item.emmc_mode.dis_hs; + u8 device_type = card->mmc.ext_csd.card_type; + + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* + * if registry set disable hs mode or host don't support high speed, + * check card type, and set the clk + */ + if (b_dis_hs || (host->hs_supp == 0)) { + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "User disable high speed, select 25M timing!!\n"); + if (device_type & MMC_CARD_TYPE_HS_26M) { + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "select 25M timing!!\n"); + clk = SD_CLK_25M; + } + } else { + if (device_type & + (MMC_CARD_TYPE_HS_52M | MMC_CARD_TYPE_HS_DDR_12 | + MMC_CARD_TYPE_HS_DDR_18)) { + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "select 52M timing!!\n"); + clk = SD_CLK_50M; + if ((host->cfg->card_item.emmc_mode.enable_ddr_mode) && + (device_type & MMC_CARD_DDR_SUPP) + ) { + bddr50 = TRUE; + } + } else if (device_type & MMC_CARD_TYPE_HS_26M) { + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "select 25M timing!!\n"); + clk = SD_CLK_25M; + } + } + + /* dump power size */ + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "PMclass- 52M, DDR, 3.3v: 0x%xh 1.8v: 0x%xh\n", + card->mmc.ext_csd.pwr_cl_ddr_52_360, + card->mmc.ext_csd.pwr_cl_ddr_52_195); + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "PMclass- 26M, SDR, 3.3v: 0x%xh 1.8v: 0x%xh\n", + card->mmc.ext_csd.pwr_cl_26_360, + card->mmc.ext_csd.pwr_cl_26_195); + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "PMclass- 52M, SDR, 1.8v: 0x%xh 3.3v: 0x%xh\n", + card->mmc.ext_csd.pwr_cl_52_195, + card->mmc.ext_csd.pwr_cl_52_360); + + /* if host & card all support high speed, then set the hs_timing */ + if (clk) { + argument = + (MMC_EXTCSD_WRITE | MMC_EXTCSD_HS_TIMING | + MMC_TIMING_HIGH_SPEED); + result = emmc_send_cmd6(card, sd_cmd, argument); + if (!result) { + DbgErr("Switch High Speed mode Failed.\n"); + goto exit; + } + } + + /* change clock */ + if (clk) + emmc_set_freq(card, clk, bddr50); + + /* if clock > 50M hz, set 0x28[2]: high speed enable */ + if (clk == SD_CLK_50M) + host_set_highspeed(host, TRUE); + +exit: + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + result, __func__); + return result; +} + +/* + * + * Function Name: emmc_ddr_mode_set + * + * Abstract: + * 1. set eMMC card DDR50 mode + * + * Input: + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * + * Output: + * None + * + * Return value: + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * Caller: emmc_init_stage2 + */ +static void emmc_ddr_mode_set(sd_card_t *card) +{ + sd_host_t *host = card->host; + u32 b_dis_hs = host->cfg->card_item.emmc_mode.dis_hs; + u8 device_type = card->mmc.ext_csd.card_type; + u32 enable_ddr = host->cfg->card_item.emmc_mode.enable_ddr_mode; + + if ((b_dis_hs == FALSE) && + (enable_ddr) && (device_type & MMC_CARD_DDR_SUPP) + ) { + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Set host DDR50 mode!!\n"); + host_set_uhs_mode(host, SDHCI_CTRL_UHS_DDR50); + host_emmc_ddr_set(host, TRUE); + } else { + host->cfg->card_item.emmc_mode.enable_ddr_mode = 0; + host_emmc_ddr_set(host, FALSE); + } + +} + +/* + * + * Function Name: emmc_switch_buswidth + * + * Abstract: + * 1. set eMMC card bus width + * + * Input: + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * + * Output: + * None + * + * Return value: + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * Caller: emmc_init_stage2 + */ +static bool emmc_switch_buswidth(sd_card_t *card, sd_command_t *sd_cmd) +{ + bool result = FALSE; + u32 argument = 0; + u32 data_len = 0; + sd_host_t *host = card->host; + mmc_card_info_t *mmc_info = &(card->mmc); + u32 dis_8_bit = host->cfg->card_item.emmc_mode.dis_8bit_bus_width; + u32 dis_4_bit = host->cfg->card_item.emmc_mode.dis_4bit_bus_width; + u32 enable_ddr = host->cfg->card_item.emmc_mode.enable_ddr_mode; + + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + if (card->card_present == FALSE) + goto exit; + + /* check host & user support 8-bit bus width */ + if ((dis_8_bit == 0) && (host->bus_8bit_supp) + ) { + mmc_info->cur_buswidth = EMMC_8Bit_BUSWIDTH; + data_len = 8; + + /* host set 8-bit bus width */ + host_set_buswidth(host, BUS_WIDTH8); + + /* delay 100us */ + os_udelay(100); + + result = emmc_bus_width_test(card, sd_cmd, data_len); + if (result == TRUE) { + if (enable_ddr && + (card->mmc.ext_csd.card_type & MMC_CARD_DDR_SUPP) && + (mmc_info->cur_hs_type != EMMC_MODE_HS200) + ) { + argument = + (MMC_EXTCSD_WRITE | MMC_EXTCSD_BUS_WIDTH | + MMC_BUSW_DDR_8BIT); + } else { + argument = + (MMC_EXTCSD_WRITE | MMC_EXTCSD_BUS_WIDTH | + MMC_BUSW_SDR_8BIT); + } + result = emmc_send_cmd6(card, sd_cmd, argument); + if (!result) + DbgErr("Set 8-bit bus width Failed.\n"); + else + goto exit; + } + DbgErr("Set 8-bit bus width Failed.\n"); + + } + + /* check user support 4-bit bus width */ + if (dis_4_bit == FALSE) { + mmc_info->cur_buswidth = EMMC_4Bit_BUSWIDTH; + /* host set 4-bit bus width */ + host_set_buswidth(host, BUS_WIDTH4); + data_len = 4; + + /* Test 4-bit patten, set block size to 4 bytes */ + result = emmc_bus_width_test(card, sd_cmd, data_len); + if (!result) + goto exit; + + if (enable_ddr && + (card->mmc.ext_csd.card_type & MMC_CARD_DDR_SUPP) && + (mmc_info->cur_hs_type != EMMC_MODE_HS200) + ) { + argument = + (MMC_EXTCSD_WRITE | MMC_EXTCSD_BUS_WIDTH | + MMC_BUSW_DDR_4BIT); + } else { + argument = + (MMC_EXTCSD_WRITE | MMC_EXTCSD_BUS_WIDTH | + MMC_BUSW_SDR_4BIT); + } + + result = emmc_send_cmd6(card, sd_cmd, argument); + if (!result) + DbgErr("Set 4-bit bus width Failed.\n"); + else + goto exit; + + DbgErr("Set 4-bit bus width Failed.\n"); + + } + + /* default 1-bit bus width */ + mmc_info->cur_buswidth = EMMC_1Bit_BUSWIDTH; + host_set_buswidth(host, BUS_WIDTH1); + +exit: + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + result, __func__); + return result; +} + +/* + * + * Function Name: emmc_set_trans_clk + * + * Abstract: + * 1. set MMC/eMMC card(Not support HS) transfer clock + * + * Input: + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * + * Output: + * None + * + * Return value: + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * Caller: emmc_init_stage2 + */ +static bool emmc_set_trans_clk(sd_card_t *card) +{ + bool result = FALSE; + u32 freq_unit = 0; + u32 multip_factor = 0; + u32 trans_clk; + card_info_t *card_info = &(card->info); + + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + /* get the frequency unit 0: 1KHz 1: 10KHz 2: 100KHz 3: 1000KHZ */ + switch ((card_info->csd.tran_speed) & 0x7) { + case 0: + freq_unit = 1; + break; + case 1: + freq_unit = 10; + break; + case 2: + freq_unit = 100; + break; + case 3: + freq_unit = 1000; + break; + default: + return result; + } + + switch (((card_info->csd.tran_speed) & 0x78) >> 3) { + case 1: + multip_factor = 100; + break; + case 2: + multip_factor = 120; + break; + case 3: + multip_factor = 130; + break; + case 4: + multip_factor = 150; + break; + case 5: + multip_factor = 200; + break; + case 6: + multip_factor = 260; + break; + case 7: + multip_factor = 300; + break; + case 8: + multip_factor = 350; + break; + case 9: + multip_factor = 400; + break; + case 10: + multip_factor = 450; + break; + case 11: + multip_factor = 520; + break; + case 12: + multip_factor = 550; + break; + case 13: + multip_factor = 600; + break; + case 14: + multip_factor = 700; + break; + case 15: + multip_factor = 800; + break; + default: + goto exit; + } + + trans_clk = freq_unit * multip_factor; + if (trans_clk >= SD_CLK_BASE) { + /* 200M Hz */ + trans_clk = SD_CLK_BASE; + } + + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "MMC support trans clk=%d\n", trans_clk); + /* change transfer clock */ + emmc_set_freq(card, trans_clk, FALSE); + result = TRUE; + +exit: + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + result, __func__); + return result; +} + +static u64 get_emmc_sec_count(sd_card_t *card) +{ + u64 capability = 0; + + card_info_t *card_info = &(card->info); + mmc_card_info_t *mmc_info = &(card->mmc); + + /* <2G capability / SD_BLOCK_LEN */ + if (card_info->card_ccs == 0) + capability = card->sec_count; + else { + /* > 2G sector count */ + capability = mmc_info->ext_csd.sec_cnt; + } + + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "MMC * EMMC sector count = 0x%x!!\n", capability); + return capability; +} + +/* + * + * Function Name: set_emmc_block_len + * + * Abstract: + * 1. set MMC/eMMC card block length + * + * Input: + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * bool b_hs400: if DDR mode, can't set CMD16 to set block length + * + * Output: + * None + * + * Return value: + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * Caller: emmc_init_stage2 + */ +static bool set_emmc_block_len(sd_card_t *card, bool b_hs400) +{ + bool result = FALSE; + u32 block_len = 0; + card_info_t *card_info = &(card->info); + sd_host_t *host = card->host; + cfg_emmc_mode_t *cfg_emmc_mode = &(host->cfg->card_item.emmc_mode); + sd_command_t sd_cmd; + + os_memset(&sd_cmd, 0, sizeof(sd_command_t)); + + block_len = (2 << (card_info->csd.read_bl_len)); + + if (block_len > host->max_block_len) { + DbgWarn(MODULE_MMC_CARD, NOT_TO_RAM, + "Device block length > HW Init max block length!!\n"); + } + + if ((cfg_emmc_mode->enable_ddr_mode) || b_hs400) { + /* todo: if DDR mode, */ + result = TRUE; + } else { + result = card_set_block_len(card, &sd_cmd, SD_BLOCK_LEN); + } + + return result; +} + +/* + * + * Function Name: emmc_init_stage2 + * + * Abstract: + * 1. emmc card initialize main function. + * 2. Fill virtual card structure, like cid, csd, ext_csd etc. + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * + * Output: + * + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: card_init_stage2 + */ +bool emmc_init_stage2(sd_card_t *card) +{ + bool result = FALSE; + sd_host_t *host = card->host; + mmc_card_info_t *mmc_info = &card->mmc; + cfg_emmc_mode_t *cfg_emmc_mode = &(host->cfg->card_item.emmc_mode); + sd_command_t sd_cmd; + + os_memset(&sd_cmd, 0, sizeof(sd_command_t)); + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* set card work clock to 25M */ + if (mmc_info->ext_csd.card_type) { + emmc_set_freq(card, SD_CLK_25M, FALSE); + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Set eMMC Card Host Clock to 25MHz.\n"); + } + + /* check emmc card HS200 mode */ + if ((mmc_info->ext_csd.card_type & MMC_CARD_TYPE_H200) && + (cfg_emmc_mode->enable_force_hs != 1) + ) { + result = emmc_switch_hs200(card, &sd_cmd); + if (result == FALSE) { + DbgErr("Switch hs200 mode failed!!\n"); + goto exit; + } + + if ((cfg_emmc_mode->enable_force_hs200) || + (mmc_info->cur_buswidth != EMMC_8Bit_BUSWIDTH) || + (host->bus_8bit_supp == FALSE) + ) + goto exit; + + /* check emmc card HS400 mode */ + if ((host->chip_type == CHIP_SEAEAGLE2 + || cfg_emmc_mode->enable_force_hs400 + || host->chip_type == CHIP_GG8 + || host->chip_type == CHIP_ALBATROSS) + && (mmc_info->ext_csd.card_type & MMC_CARD_TYPE_H400) + ) { + /* choose the eMMC hs400 mode */ + result = emmc_switch_hs400(card, &sd_cmd); + if (result == FALSE) + DbgErr("Switch hs400 mode failed!!\n"); + else + mmc_info->cur_hs_type = EMMC_MODE_HS400; + } + + goto exit; + } + + /* check emmc card HS mode */ + if ((mmc_info->ext_csd.card_type) & MMC_CARD_TYPE_HS) { + result = emmc_switch_hs(card, &sd_cmd); + if (result == FALSE) + DbgErr("Switch hs mode failed!!\n"); + + } else { + /* set transfer clock */ + result = emmc_set_trans_clk(card); + if (!result) { + DbgErr("Set Basic transfer clock failed.\n"); + goto exit; + } + } + + /* set MMC/eMMC card bus width & DDR mode */ + if (emmc_switch_buswidth(card, &sd_cmd) == TRUE) { + /* DDR mode not support 1-bit */ + emmc_ddr_mode_set(card); + } + +exit: + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + result, __func__); + return result; +} + +/* + * + * Function Name: emmc_init + * + * Abstract: + * 1. emmc card initialize main function. + * 2. Fill virtual card structure, like cid, csd, ext_csd etc. + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * + * Output: + * + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: card_init + */ +bool emmc_init(sd_card_t *card, bool bemmc) +{ + bool result = FALSE; + sd_host_t *host = card->host; + card_info_t *card_info = &(card->info); + sd_command_t sd_cmd; + cfg_emmc_mode_t *cfg_emmc_mode = &(host->cfg->card_item.emmc_mode); + mmc_card_info_t *mmc_info = &(card->mmc); + + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s bemmc = %d\n", __func__, bemmc); + os_memset(&sd_cmd, 0, sizeof(sd_command_t)); + os_memset(mmc_info, 0, sizeof(mmc_card_info_t)); + + /* 1. emmc host init */ + result = host_emmc_init(host, cfg_emmc_mode); + if (result == FALSE) { + DbgErr("Emmc Host Init Failed.\n"); + goto exit; + } + + /* 2. Issue reset command (CMD0) */ + result = card_reset_card(card, &sd_cmd); + result = card_reset_card(card, &sd_cmd); + if (!result) { + /* Go Idle State command failed. exit directly. */ + DbgErr("Reset Card (CMD0) Failed.\n"); + goto exit; + } + + /* 2. Wait for card ready (CMD01) */ + result = emmc_card_init_ready(card, &sd_cmd); + if (!result) { + DbgErr("Wait for card ready CMD1 Failed.\n"); + goto exit; + } + + if (bemmc) + card->card_type = CARD_EMMC; + else + card->card_type = CARD_MMC; + + /* Get card CID(CMD2) */ + result = card_all_send_cid(card, &sd_cmd); + if (!result) { + DbgErr("Get card CID(CMD2) failed.\n"); + goto exit; + } + + /* 4. Set card relative address (CMD3) */ + result = emmc_set_rca(card, &sd_cmd); + if (!result) { + DbgErr + ("MMC/eMMC card set card relative address (CMD3) failed.\n"); + goto exit; + } + + /* 5. Get CSD (CMD9) */ + result = card_get_csd(card, &sd_cmd); + if (!result) { + DbgErr("Get CSD (CMD9) failed.\n"); + goto exit; + } + + /* 6. Select the card (CMD7) */ + result = card_select_card(card, &sd_cmd); + if (!result) { + DbgErr("Select card (CMD7) failed.\n"); + goto exit; + } + + /* 7. Check card lock */ + if (card->locked == TRUE) { + DbgErr("Card is locked!!\n"); + goto exit; + } + + /* 8. Check card SPEC version */ + if (card_info->csd.mmc_spec_vers < MMC_SPEC_VERS) { + DbgWarn(MODULE_MMC_CARD, NOT_TO_RAM, + "Spec version < 4: it's old MMC Device!!\n"); + /* set transfer clock */ + result = emmc_set_trans_clk(card); + if (!result) { + DbgErr("Set Basic transfer clock failed.\n"); + goto exit; + } + goto exit; + } + + /* 9. Get Ext_Csd */ + result = emmc_get_ext_csd(card, &sd_cmd); + if (!result) { + DbgErr("Get mmc Ext_Csd failed.\n"); + goto exit; + } + + /* 10. Switch Card mode */ + result = card_init_stage2(card); + +exit: + if (result) { + /* max LBA */ + card->sec_count = get_emmc_sec_count(card); + result = + set_emmc_block_len(card, + (mmc_info->cur_hs_type == + EMMC_MODE_HS400) ? TRUE : FALSE); + if (!result) + DbgErr("Set block length failed.\n"); + } + + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + result, __func__); + return result; +} + +/* + * Function Name: mmc_degrade_policy + * Abstract: This Function is used set sd degrade flag + * + * Input: + * sd_card_t *card : The Command will send to which Card + * + * Return value: + * + */ +void mmc_degrade_policy(sd_card_t *card) +{ + + /* check if at hs200 or hs400 then can degrade freq */ + /* else set degrade_all flag */ + + cfg_emmc_mode_t *cfg_emmc_mode = + &(card->host->cfg->card_item.emmc_mode); + + if (cfg_emmc_mode->enable_force_hs400 + || cfg_emmc_mode->enable_force_hs200 + || (card->mmc.ext_csd.card_type & MMC_CARD_TYPE_H400) + || (card->mmc.ext_csd.card_type & MMC_CARD_TYPE_H200)) { + if (card->degrade_freq_level < CARD_DEGRADE_FREQ_TIMES) + card->degrade_freq_level++; + else + card->degrade_final = 1; + } else + card->degrade_final = 1; + + DbgErr("EMMC degrade final=%d freq_level=%d\n", card->degrade_final, + card->degrade_freq_level); + +} + +static void emmc_set_freq(sd_card_t *card, u32 clock_freq, bool bddr50) +{ + sd_host_t *host = card->host; + u32 value = 0; + u16 index = card->degrade_freq_level; + + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, clk_freq_khz=%dkhz, ddr50_mode=%d\n", __func__, + clock_freq, bddr50); + switch (clock_freq) { + case SD_CLK_ID_400K: + value = host->cfg->dmdn_tbl[FREQ_EMMC_400K_START_INDEX]; + break; + case SD_CLK_50M: + if (bddr50) + value = + host->cfg->dmdn_tbl[FREQ_EMMC_DDR50M_START_INDEX]; + else + value = host->cfg->dmdn_tbl[FREQ_EMMC_50M_START_INDEX]; + break; + case SD_CLK_200M: + value = host->cfg->dmdn_tbl[FREQ_EMMC_200M_START_INDEX + index]; + break; + default: + value = host->cfg->dmdn_tbl[FREQ_EMMC_25M_START_INDEX]; + break; + } + host_change_clock(host, value); + DbgInfo(MODULE_MMC_CARD, FEATURE_CARD_INIT, TO_RAM, + "Enter %s, clk_freq_khz=%dkhz, ddr50_mode=%d\n", __func__, + clock_freq, bddr50); +} diff --git a/drivers/scsi/bht/card/output_tuning.c b/drivers/scsi/bht/card/output_tuning.c new file mode 100644 index 000000000000..de1ae5e4f388 --- /dev/null +++ b/drivers/scsi/bht/card/output_tuning.c @@ -0,0 +1,756 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022 BHT Inc. + * + * File Name: output_tuning.c + * + * Abstract: + * 1. Card tuning main entry + * 2. Interface for card tuning + * + * Version: 1.00 + * + * Author: Chevron + * + * Environment: OS Independent + * + * History: + * + * 3/8/2022 Creation Chevron + */ + +#include "../include/basic.h" +#include "../include/card.h" +#include "../include/cardapi.h" +#include "cardcommon.h" +#include "../include/hostapi.h" +#include "../include/transhapi.h" +#include "../include/hostvenapi.h" +#include "../include/util.h" +#include "../include/debug.h" +#include "../include/cmdhandler.h" +#include "../host/hostven.h" +#include "../include/card.h" +#include "../host/hostreg.h" + +/* None Device error */ +#define SD_SUCCESS 0x00000000 +/* Device error */ +#define SD_ERR_DEVICE 0x00000001 +/*mmio value set timeout */ +#define SD_ERR_MMIO_SET_TIMEOUT 0x00000002 +#define SD_ERR_ALL_PHASE_PASS 0x00000003 +#define SD_ERR_FATAL 0x00000004 +/* Device error need degrade */ +#define SD_ERR_DEVICE_DEGRADE 0x00000005 +/* Device error need retry */ +#define SD_ERR_DEVICE_RETRY 0x00000006 +/* Device error need increase drive strength */ +#define SD_ERR_DEVICE_DS_INS 0x00000007 +/* Retry Over */ +#define SD_ERR_RETRY_OVER 0x80000000 +/* CRC Error */ +#define SD_ERR_CRC_MISSMACH 0x40000000 +/* No Response */ +#define SD_ERR_NO_RESPONSE 0x20000000 +/* No Response */ +#define SD_ERR_NO_RESPONSE 0x20000000 + +u16 tuning_phase_result(sd_card_t *card) +{ + u16 result[4] = { 0 }; + u8 phase_count = 11; + u16 phase_mask = 0x7FF; + /* u32 device_status; */ + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Enter %s\n", + __func__); + /* Only GG8 support 14 phase tuning */ + if (card->host->chip_type == CHIP_GG8 + || card->host->chip_type == CHIP_ALBATROSS) { + phase_count = 14; + phase_mask = 0x3FFF; + } + + /* get tuning result of 3 cycle */ + result[0] = + sdhci_readl(card->host, SDHCI_SAMPLE_CLK_RESULT_LOW) & phase_mask; + result[1] = + (sdhci_readl(card->host, SDHCI_SAMPLE_CLK_RESULT_LOW) >> + phase_count) & phase_mask; + + /* Low bits result */ + result[2] = + ((sdhci_readl(card->host, SDHCI_SAMPLE_CLK_RESULT_LOW) >> + (phase_count << 1) & phase_mask)); + + /* Result of full bits */ + if (card->host->chip_type == CHIP_GG8 + || card->host->chip_type == CHIP_ALBATROSS) + result[2] |= + (sdhci_readl(card->host, SDHCI_SAMPLE_CLK_RESULT_UP) & + 0x3FF) << 4; + else + result[2] |= + (sdhci_readl(card->host, SDHCI_SAMPLE_CLK_RESULT_UP) & + 0x3FF) << 1; + + result[3] = result[0] & result[1] & result[2]; + + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Exit %s, result 0x%x, phase count %d\n", __func__, + result[3], phase_count); + return result[3]; +} + +u8 select_tuning_phase(u16 tuning_phase, u8 phase_cnt) +{ + u8 temp[14] = { 0 }; + u8 cnt[14] = { 0 }; + u8 sel_phase, val, pos, start_phase; + u8 i, j; + + i = j = val = pos = sel_phase = 0; + + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Enter %s\n", + __func__); + for (i = 0; i < phase_cnt; i++) + temp[i] = (tuning_phase >> i) & 0x01; + + for (i = 0; i < phase_cnt; i++) { + for (j = 0; j < phase_cnt; j++) { + if (temp[(i + j) % phase_cnt]) + cnt[i]++; + else + break; + } + } + + val = cnt[0]; + for (i = 0; i < phase_cnt - 1; i++) { + if (cnt[i + 1] > val) { + val = cnt[i + 1]; + pos = i + 1; + } + } + + start_phase = (phase_cnt == 14 ? 9 : 8); + sel_phase = ((start_phase + pos + cnt[pos] / 2) % phase_cnt); + + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Exit %s select phase %d\n", __func__, sel_phase); + return sel_phase; +} + +void set_input_tuning_phase(sd_card_t *card, u8 sel_phase) +{ + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Enter %s\n", + __func__); + + host_enable_clock(card->host, FALSE); + + /* Clear origin phase */ + sdhci_and32(card->host, SDHCI_DLL_PHASE_CFG, ~0x1F000000); + /* select the 1B0h[27:24] to config the phase selection */ + sdhci_or32(card->host, SDHCI_DLL_PHASE_CFG, BIT28); + /* set new phase */ + sdhci_or32(card->host, SDHCI_DLL_PHASE_CFG, sel_phase << 24); + + host_enable_clock(card->host, TRUE); + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Exit %s select phase %d\n", __func__, sel_phase); +} + +void generate_traverse_range(sd_card_t *card, u8 center_point, u8 offset, + u8 *start_point, u8 *end_point) +{ + u16 phase_mask_all_pass = 0; + u8 phase_cnt = 11; + + if (card->host->chip_type == CHIP_GG8 + || card->host->chip_type == CHIP_ALBATROSS) + phase_cnt = 14; + + if (phase_cnt == 14) + phase_mask_all_pass = 0x3FFF; + else + phase_mask_all_pass = 0x7FF; + + if (center_point < offset) { + *start_point = phase_cnt + center_point - offset; + *end_point = phase_cnt + center_point + offset; + } else { + *start_point = center_point - offset; + *end_point = center_point + offset; + } +} + +u8 get_output_fix_phase(sd_card_t *card) +{ + cfg_output_tuning_item_t *output_tuning = + &card->host->cfg->feature_item.output_tuning_item; + + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Enter %s\n", + __func__); + + if (card->info.sw_cur_setting.sd_access_mode == SD_FNC_AM_DDR200) { + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "output_tuning_item DDR200\n"); + return (u8) output_tuning->fixed_value_ddr200; + } else if (card->info.sw_cur_setting.sd_access_mode == SD_FNC_AM_SDR104) { + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "output_tuning_item SDR104\n"); + return (u8) output_tuning->fixed_value_sdr104; + } else if (card->info.sw_cur_setting.sd_access_mode == SD_FNC_AM_SDR50) { + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "output_tuning_item SDR50\n"); + return (u8) output_tuning->fixed_value_sdr50; + } else { + /* Not support this mode at Bayhub Driver */ + DbgErr("sd_access_mode %d isn't supported !!!", + card->info.sw_cur_setting.sd_access_mode); + return 0; + } + +} + +/* Use to find a output phase in which input tuning result is not all pass */ +u32 find_input_phase_fail_point(sd_card_t *card, u8 *output_phase, + u16 *input_tuning_result) +{ + u32 result = 0; + u8 start_phase, end_phase, index_phase; + u8 output_fix_phase = 0; + u8 i = 0; + u16 phase_mask_all_pass = 0; + int ret = 0; + sd_command_t sd_cmd; + u8 phase_cnt = 11; + + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Enter %s\n", + __func__); + if (card->host->chip_type == CHIP_GG8 + || card->host->chip_type == CHIP_ALBATROSS) + phase_cnt = 14; + + output_fix_phase = get_output_fix_phase(card); + + if (phase_cnt == 14) + phase_mask_all_pass = 0x3FFF; + else + phase_mask_all_pass = 0x7FF; + generate_traverse_range(card, output_fix_phase, 3, &start_phase, + &end_phase); + + for (i = start_phase; i <= end_phase; i++) { + index_phase = i % phase_cnt; + + host_set_output_tuning_phase(card->host, index_phase); + + ret = sd_tuning(card, &sd_cmd, 150); + if (!ret && sd_cmd.err.error_code) { + DbgErr("Uncorrect fix output phase!!!\n"); + result = SD_ERR_FATAL; + break; + } else if (!ret) { + result = SD_ERR_DEVICE_DEGRADE; + break; + } + + *input_tuning_result = tuning_phase_result(card); + + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "output phase is %d, input result is 0x%x\n", + index_phase, *input_tuning_result); + if (*input_tuning_result == phase_mask_all_pass) { + result = SD_ERR_ALL_PHASE_PASS; + continue; + } else { + *output_phase = index_phase; + result = SD_SUCCESS; + break; + } + } + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Exit %s\n", + __func__); + return result; +} + +u32 generate_output_input_phase_pair(sd_card_t *card, u8 input_fix_phase, + u8 ddr200) +{ + u32 result = 0; + u8 output_phase = 0; + u8 input_phase = 0; + u16 input_tuning_result = 0; + u8 output_fix_phase = 0; + u8 start_phase, end_phase, index_phase, offset; + u8 i = 0; + u8 phase_cnt = 11; + u16 phase_mask_all_pass = 0x7FF; + sd_command_t sd_cmd; + int ret = 0; + + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Enter %s\n", + __func__); + if (card->host->chip_type == CHIP_GG8 + || card->host->chip_type == CHIP_ALBATROSS) { + phase_cnt = 14; + phase_mask_all_pass = 0x3FFF; + } + if (ddr200) { + output_fix_phase = + (u8) card->host->cfg->feature_item.output_tuning_item.fixed_value_sdr104; + offset = 2; + } else { + output_fix_phase = get_output_fix_phase(card); + offset = 3; + } + generate_traverse_range(card, output_fix_phase, offset, &start_phase, + &end_phase); + + for (i = start_phase; i <= end_phase; i++) { + index_phase = i % phase_cnt; + + host_set_output_tuning_phase(card->host, index_phase); + + ret = sd_tuning(card, &sd_cmd, 150); + if (!ret && sd_cmd.err.error_code) { + DbgErr("Uncorrect fix output phase!!!\n"); + result = SD_ERR_FATAL; + break; + } else if (!ret) { + result = SD_ERR_DEVICE_DEGRADE; + break; + } + + input_tuning_result = tuning_phase_result(card); + + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "output phase is %d, input result is 0x%x\n", + index_phase, input_tuning_result); + if (ddr200) { + card->output_input_phase_pair[index_phase] = + select_tuning_phase(input_tuning_result, + phase_cnt); + continue; + } + + if (input_tuning_result == phase_mask_all_pass) { + result = SD_ERR_ALL_PHASE_PASS; + continue; + } else { + output_phase = index_phase; + result = SD_SUCCESS; + break; + } + } + + /* result = find_input_phase_fail_point(card, &output_phase, &input_tuning_result); */ + if (ret && ddr200) { + u8 temp_phase_pair[14] = { 0 }; + u8 j; + + /* add all input phase */ + for (i = start_phase; i <= end_phase; i++) { + + input_phase = + card->output_input_phase_pair[i % phase_cnt]; + /* caclute the phase pair */ + for (j = i; j < (i + phase_cnt); j++) { + index_phase = j % phase_cnt; + temp_phase_pair[index_phase] += input_phase; + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, + NOT_TO_RAM, + "caclute ddr output-input pair:0x%x-0x%x\n", + index_phase, input_phase); + input_phase = (input_phase + 1) % phase_cnt; + } + } + + /* caclute the avargae value for other phase pair */ + for (i = end_phase + 1; i < (start_phase + phase_cnt); i++) { + index_phase = i % phase_cnt; + card->output_input_phase_pair[index_phase] = + temp_phase_pair[index_phase] / (1 + (offset << 1)); + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, + NOT_TO_RAM, + "###caclute ddr output-input pair:0x%x-0x%x\n", + index_phase, + card->output_input_phase_pair[index_phase]); + } + + } else if ((result == SD_SUCCESS) || (result == SD_ERR_ALL_PHASE_PASS)) { + if (result == SD_SUCCESS) { + start_phase = output_phase; + end_phase = output_phase + phase_cnt; + input_phase = + select_tuning_phase(input_tuning_result, phase_cnt); + } else { + start_phase = output_fix_phase; + end_phase = output_fix_phase + phase_cnt; + input_phase = input_fix_phase; + card->input_phase_all_pass = 1; + result = SD_SUCCESS; + } + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "output-input result:0x%x-0x%x\n", output_phase, + input_tuning_result); + + for (i = start_phase; i < end_phase; i++) { + index_phase = i % phase_cnt; + card->output_input_phase_pair[index_phase] = + input_phase; + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, + NOT_TO_RAM, "output-input pair:0x%x-0x%x\n", + index_phase, input_phase); + input_phase = (input_phase + 1) % phase_cnt; + } + } + + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Exit %s\n", + __func__); + return result; +} + +int output_tuning(sd_card_t *card, u32 address, bool ddr200) +{ + int ret = 0; + u8 test_patern[6] = { 0x55, 0xaa, 0x00, 0xff, 0xf0, 0x0f }; + u8 input_tuning_pattern[64] = { + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xcc, + 0xcc, 0xcc, 0x33, 0xcc, 0xcc, + 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, 0xff, 0xee, + 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, + 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, 0xff, 0xff, + 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, + 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, 0x77, 0x77, + 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff + }; + int j, k, pattern_i; + u32 result = 0; + sd_command_t sd_cmd; + u32 cmdflag; + + u8 *test_buf = kcalloc(512, sizeof(unsigned char), GFP_KERNEL); + u8 *test_buf_read = kcalloc(512, sizeof(unsigned char), GFP_KERNEL); + + if (test_buf == NULL || test_buf_read == NULL) { + DbgErr("kcalloc buffer failed\n"); + if (test_buf != NULL) + kfree(test_buf); + if (test_buf_read != NULL) + kfree(test_buf_read); + return SD_ERR_FATAL; + } + + if (ddr200) + cmdflag = + CMD_FLG_RESCHK | CMD_FLG_R1 | CMD_FLG_ADMA_SDMA | + CMD_FLG_DDR200_WORK_AROUND | CMD_FLG_INF_BUILD; + else + cmdflag = CMD_FLG_RESCHK | CMD_FLG_R1 | CMD_FLG_ADMA_SDMA; + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* the output tuning pattern make up by four part */ + for (pattern_i = 0; pattern_i < 4; pattern_i++) { + if (pattern_i < 3) { + for (k = 0; k < 512; k++) { + test_buf[k] = + test_patern[(k % 2) + 2 * pattern_i]; + } + } else { + for (j = 0; j < 8; j++) { + for (k = 0; k < 64; k++) { + if (j % 2) { + test_buf[k + 64 * j] = + input_tuning_pattern[k]; + } else { + test_buf[k + 64 * j] = + input_tuning_pattern[(k + + 63) % + 64]; + } + } + } + } + + ret = + card_send_sdcmd_timeout(card, &sd_cmd, + ddr200 ? SD_CMD25 : SD_CMD24, + address, cmdflag, DATA_DIR_OUT, + test_buf, 512, 500); + if (ret == FALSE) { + if (card->input_phase_all_pass) { + if (sd_cmd.err.legacy_err_reg & 0x0E) { + DbgErr + ("Uncorrect fix input phase!!!\n"); + result = SD_ERR_FATAL; + goto end; + } + } + + if ((sd_cmd.err.legacy_err_reg & 0x80F) + && (card->info.sw_cur_setting.sd_access_mode == + SD_FNC_AM_DDR200)) { + DbgErr("Uncorrect fix output phase!!!\n"); + result = SD_ERR_FATAL; + goto end; + } + + if (card->info.sw_cur_setting.sd_access_mode != + SD_FNC_AM_DDR200) { + DbgErr + ("Recovery fail at tuning stage, need re-init!!!\n"); + result = SD_ERR_DEVICE_RETRY; + } else { + host_cmddat_line_reset(card->host); + card_send_command12(card, &sd_cmd); + if (card_check_rw_ready(card, &sd_cmd, 600) != + TRUE) { + DbgErr + ("Uncorrect fix output phase for DDR200!!!\n"); + result = SD_ERR_FATAL; + } else + result = SD_ERR_CRC_MISSMACH; + } + goto end; + } + + ret = + card_send_sdcmd_timeout(card, &sd_cmd, + ddr200 ? SD_CMD18 : SD_CMD17, + address, (cmdflag), DATA_DIR_IN, + test_buf_read, 512, 500); + if (ret == FALSE) { + if (card->input_phase_all_pass) { + if (sd_cmd.err.legacy_err_reg & 0x6E) { + DbgErr + ("Uncorrect fix input phase!!!\n"); + result = SD_ERR_FATAL; + goto end; + } + } + + if ((sd_cmd.err.legacy_err_reg & 0x80F) + && (card->info.sw_cur_setting.sd_access_mode == + SD_FNC_AM_DDR200)) { + DbgErr("Uncorrect fix output phase!!!\n"); + result = SD_ERR_FATAL; + goto end; + } + + if (card->info.sw_cur_setting.sd_access_mode != + SD_FNC_AM_DDR200) { + DbgErr + ("Recovery fail at tuning stage, need re-init!!!\n"); + result = SD_ERR_DEVICE_RETRY; + } else { + host_cmddat_line_reset(card->host); + card_send_command12(card, &sd_cmd); + if (card_check_rw_ready(card, &sd_cmd, 600) != + TRUE) { + DbgErr + ("Uncorrect fix output phase for DDR200!!!\n"); + result = SD_ERR_FATAL; + } else + result = SD_ERR_CRC_MISSMACH; + } + goto end; + } + + for (j = 0; j < (1 * 512); j++) { + if (*(test_buf + j) != *(test_buf_read + j)) { + result = SD_ERR_CRC_MISSMACH; + DbgErr("Compare failed!!!\n"); + goto end; + } + } + } + result = SD_SUCCESS; +end: + + kfree(test_buf); + kfree(test_buf_read); + + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Exit %s(%d)\n", __func__, result); + return result; +} + +u32 sdr104_sdr50_output_tuning(sd_card_t *card, u32 address) +{ + u8 start_phase, end_phase, index_phase; + u8 best_output_phase = 0, best_input_phase = 0; + u8 i = 0; + u8 output_fix_phase = 0; + u8 input_fix_phase = 0; + u8 output_fail_phase = 0x0; + u32 result = 0; + u8 phase_cnt = 11; + sd_command_t sd_cmd; + + output_fix_phase = get_output_fix_phase(card); + + if (card->info.sw_cur_setting.sd_access_mode == SD_FNC_AM_SDR104) + input_fix_phase = + (u8) card->host->cfg->feature_item.output_tuning_item.sdr104_input_fix_phase_value; + else + input_fix_phase = + (u8) card->host->cfg->feature_item.output_tuning_item.sdr50_input_fix_phase_value; + + if (card->host->chip_type == CHIP_GG8 + || card->host->chip_type == CHIP_ALBATROSS) + phase_cnt = 14; + + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Enter %s, retry_output_fail_phase is 0x%x\n", __func__, + card->retry_output_fail_phase); + + if (card->retry_output_fail_phase != 0xFF) { + best_output_phase = + (card->retry_output_fail_phase + + (phase_cnt >> 1)) % (phase_cnt); + best_input_phase = + card->output_input_phase_pair[best_output_phase]; + goto phase_set; + } + + result = generate_output_input_phase_pair(card, input_fix_phase, 0); + if (result == SD_ERR_DEVICE_DEGRADE || result == SD_ERR_FATAL) + goto exit; + else if (result == SD_SUCCESS) { + start_phase = output_fix_phase + 4; + end_phase = start_phase + phase_cnt; + for (i = start_phase; i < end_phase; i++) { + index_phase = i % phase_cnt; + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, + NOT_TO_RAM, + "output phase is %d, input phase is %d\n", + index_phase, + card->output_input_phase_pair[index_phase]); + + host_set_output_tuning_phase(card->host, index_phase); + set_input_tuning_phase(card, + card->output_input_phase_pair + [index_phase]); + result = output_tuning(card, address, 0); + if (result == SD_SUCCESS) + continue; + else if (result == SD_ERR_FATAL) + goto exit; + else { + card->retry_output_fail_phase = index_phase; + goto exit; + } + } + + if (i == end_phase) + best_output_phase = output_fix_phase; + else + best_output_phase = + (output_fail_phase + (phase_cnt >> 1)) % phase_cnt; + + best_input_phase = + card->output_input_phase_pair[best_output_phase]; + } + +phase_set: + host_set_output_tuning_phase(card->host, best_output_phase); + + if (sd_tuning(card, &sd_cmd, 150) == FALSE) + result = SD_ERR_FATAL; + else + result = SD_SUCCESS; + +exit: + + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Exit %s result %d,best_output_phase 0x%x, best_input_phase 0x%x\n", + __func__, result, best_output_phase, best_input_phase); + + return result; +} + +u32 ddr200_output_tuning(sd_card_t *card, u32 address) +{ + u32 result = 0; + u8 start_phase, end_phase, index_phase; + u8 best_output_phase = 0; + u8 output_fix_phase = 0; + u8 i = 0; + u8 cnt = 0; + int pos = -1; + u8 phase_cnt = 11; + sd_host_t *host = card->host; + u16 output_tuning_result = 0; + + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Enter %s\n", + __func__); + if (card->host->chip_type == CHIP_GG8 + || card->host->chip_type == CHIP_ALBATROSS) + phase_cnt = 14; + + /* get output-input pair at DDR200 mode for DDR200_workaround */ + result = + generate_output_input_phase_pair(card, + (u8) card->host->cfg->feature_item.output_tuning_item.sdr104_input_fix_phase_value, + 1); + if (result == SD_ERR_DEVICE_DEGRADE || result == SD_ERR_FATAL) + goto exit; + + output_fix_phase = get_output_fix_phase(card); + + generate_traverse_range(card, output_fix_phase, 4, &start_phase, + &end_phase); + + for (i = start_phase; i <= end_phase; i++) { + index_phase = i % phase_cnt; + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "output phase is %d\n", index_phase); + + /* Used to update input/output phase before write command */ + host->cur_output_phase = index_phase; + result = output_tuning(card, address, 1); + + if (result == SD_SUCCESS) { + output_tuning_result |= 1 << index_phase; + if (pos == -1) + pos = index_phase; + cnt++; + } else if (result == SD_ERR_FATAL) + goto exit; + else { + /* Find the bad phase after good phase */ + if (pos != -1) { + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, + NOT_TO_RAM, + "break output phase is %d\n", + index_phase); + break; + } + } + } + + if (!cnt) { + DbgErr("All phase failed when output_tuning!!!\n"); + result = SD_ERR_DEVICE_DEGRADE; + } else { + best_output_phase = (pos + (cnt >> 1)) % phase_cnt; + host->cur_output_phase = best_output_phase; + host_set_output_tuning_phase(card->host, best_output_phase); + set_input_tuning_phase(card, + card->output_input_phase_pair + [best_output_phase]); + + result = SD_SUCCESS; + } + +exit: + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "best_output_phase 0x%x, output_tuning_result 0x%x\n", + best_output_phase, output_tuning_result); + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Exit %s, result %d\n", __func__, result); + return result; +} diff --git a/drivers/scsi/bht/card/sd.c b/drivers/scsi/bht/card/sd.c new file mode 100644 index 000000000000..6a21d76945a7 --- /dev/null +++ b/drivers/scsi/bht/card/sd.c @@ -0,0 +1,3029 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: sd.c + * + * Abstract: SD Legacy card initialization + * + * Version: 1.00 + * + * Author: Samuel + * + * Environment: OS Independent + * + * History: + * + * 9/2/2014 Creation Samuel + */ +#include "../include/basic.h" +#include "../include/cardapi.h" +#include "../include/hostapi.h" +#include "cardcommon.h" +#include "../include/debug.h" +#include "../include/cmdhandler.h" +#include "../include/host.h" +#include "../include/util.h" +#include "../include/hostvenapi.h" +#include "../host/hostven.h" +#include "card_ddr200_support.h" +#include "../include/funcapi.h" +/* 0: card not support ddr200 mode, 1: support */ +u32 sd_card_ddr200_flag; +extern bool card_output_tuning(sd_card_t *card, u64 tuning_address); +extern bool store_tuning_address_content(sd_card_t *card, u64 tuning_address); +extern bool restore_tuning_address_content(sd_card_t *card, + u64 tuning_address); +extern u32 generate_output_input_phase_pair(sd_card_t *card, + u8 input_fix_phase, u8 ddr200); + +inline bool uhs1_support(sd_host_t *host) +{ + bool ret = TRUE; + + /* 2. Configuration settings to disable UHSI function */ + if (host->cfg->card_item.sd_card_mode_dis.dis_sd30_card) + ret = FALSE; + + return ret; + +} + +static inline bool need_switch_sig_voltage(sd_card_t *card) +{ + bool ret = FALSE; + card_info_t *card_info = &(card->info); + + if (card->card_type == CARD_UHS2) + goto exit; + + /* 1. Check configuration settings + * BH722SE2LN-A UHS1 issue#3 Sharkbay QS ULT #6 platform, BH driver 10024, + * set card mode to be SDR25 or SDR12 by registry, but the card is SD2.0 mode. + * Change to switch voltage when s18a is true. + */ + + if (card_info->card_s18a) + ret = TRUE; + +exit: + return ret; +} + +static inline bool card_support_cmd6(sd_card_t *card) +{ + bool ret = TRUE; + card_info_t *card_info = &(card->info); + /* 1. SD Memory Card - Spec. Version 1.0 and 1.01 do not support CMD6 */ + if (card_info->scr.sd_spec < SCR_SPEC_VER_1) + ret = FALSE; + + return ret; +} + +#define SDHCI_POWER_VDD1_330 0x0E00 +#define POWER_ON TRUE +#define POWER_OFF FALSE + +bool sd_send_if_cond(sd_card_t *card, sd_command_t *sd_cmd, u32 argument) +{ + byte cmd_index = SD_CMD8; + /* u32 argument = 0x000001AA; * VHS set. 2.7-3.6V Check Pattern : 0xAA */ + u32 cmdflag = CMD_FLG_R7 | CMD_FLG_RESCHK; + e_data_dir dir = DATA_DIR_NONE; + byte *data = NULL; + u32 datalen = 0; + bool ret = FALSE; + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + ret = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, dir, + data, datalen); + + if (ret) { + /* Pattern Check */ + if ((sd_cmd->response[0] & 0xFF) != 0xAA) { + sd_cmd->err.error_code = ERR_CODE_RESP_ERR; + ret = FALSE; + DbgErr("CMD8 response pattern check failed."); + } + } + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +/* + * static bool sdio_check(sd_card_t *card, sd_command_t *sd_cmd) + * { + * byte cmd_index = SD_CMD5; + * u32 argument = 0; + * u32 cmdflag = CMD_FLG_R4 | CMD_FLG_RESCHK; + * e_data_dir dir = DATA_DIR_NONE; + * byte *data = NULL; + * u32 datalen = 0; + * bool ret = FALSE; + * + * DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + * __func__); + * + * ret = + * card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, dir, + * data, datalen); + * + * DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + * ret, __func__); + * return ret; + * } + */ + +/* + * + * Function Name: card_init_ready + * + * Abstract: + * + * 1. Issue ACMD41 to Get OCR + * 2. Set the card ocr variable + * 3. Wait for card ready. + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * bool flag_f8: Command 8 is executed correctly. + * + * Output: + * + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: card_init + */ + +bool card_init_ready(sd_card_t *card, sd_command_t *sd_cmd, bool flag_f8) +{ + byte cmd_index = SD_CMD41 | SD_APPCMD; + u32 argument = 0; + u32 cmdflag = CMD_FLG_R3; + e_data_dir dir = DATA_DIR_NONE; + byte *data = NULL; + u32 datalen = 0; + bool ret = FALSE; + loop_wait_t wait; + u32 delay_us = 20; + + sd_host_t *host = card->host; + card_info_t *card_info = &(card->info); + + /* default is 0 */ + card->uhs2_card = FALSE; + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, flag_f8=%d\n", __func__, flag_f8); + + if (flag_f8) + os_udelay(20); + + /* 3. Start the card initialization */ + /* 3.1 Set argument according to the flag F8 (command 8 executed correctly) */ + argument = (1 << fls32(host->ocr_avail)); + + if (card->card_type != CARD_UHS2) { + if (flag_f8) { + if (uhs1_support(host)) { + /* + * BH722SE2LN-A UHS1 issue#3 Sharkbay QS ULT #6 platform, + * BH driver 10024, + * set card mode to be SDR25 or SDR12 by registry, + * but the card is SD2.0 mode. + * Change to send s18R for SDR12/SDR25/SDR50/SDR104. + */ + + /* Try to set the HCS/XPC/S18R */ + argument |= 0x51000000; + } else { + /* Try to set the HCS */ + argument |= 0x40000000; + + /* Set XPC */ + argument |= BIT28; + } + } + } else { + /* UHS2 case */ + /* Set HCS bit only */ + argument |= BIT30; + + /* Set XPC */ + argument |= BIT28; + } + + /* 3.2 Wait for card ready */ + util_init_waitloop(card->host->pdx, + host->cfg->timeout_item.test_card_init_timeout.value, + delay_us, &wait); + + do { + ret = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, + dir, data, datalen); + if (!ret) + break; + + if (flag_f8) + os_udelay(delay_us); + + /* Check Busy status. 0b: On initialization; 1b: Initialization Complete. */ + if ((sd_cmd->response[0] & 0x80000000) == 0) { + ret = FALSE; + continue; + } else { + /* card is ready */ + ret = TRUE; + /* check uhs2 or not */ + if ((sd_cmd->response[0] & (1 << 29)) != 0) + card->uhs2_card = TRUE; + break; + } + } while (!util_is_timeout(&wait)); + + /* 3.3 If card ready, set related software flags */ + if (ret) { + /* 3.3.1. Set sd_virt_card OCR */ + card_info->card_ccs = (sd_cmd->response[0] & BIT30) >> 30; + if (uhs1_support(host)) + card_info->card_s18a = + (sd_cmd->response[0] & BIT24) >> 24; + else + card_info->card_s18a = 0; + + } + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +/* + * + * Function Name: signal_voltage_switch + * + * Abstract: + * + * 1. Do Signal Voltage Switch Procedure (UHSI, CMD11) + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * + * Output: + * + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: card_init + */ + +static bool signal_voltage_switch(sd_card_t *card, sd_command_t *sd_cmd) +{ + byte cmd_index = SD_CMD11; + u32 argument = 0; + u32 cmdflag = CMD_FLG_R1 | CMD_FLG_RESCHK; + e_data_dir dir = DATA_DIR_NONE; + byte *data = NULL; + u32 datalen = 0; + bool ret = FALSE; + sd_host_t *host = card->host; + card_info_t *card_info = &(card->info); + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* + * 1. If S18A of ACMD41 is se to 0, do not need to switch signal voltage, + * Exit from this procedure + */ + if (card_info->card_s18a == 0) { + ret = TRUE; + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Do not need to switch signal voltage.\n"); + + goto EXIT; + } + + /* 2. Issue CMD11 */ + ret = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, dir, + data, datalen); + if (!ret) { + DbgErr("Issue CMD11 failed.\n"); + goto EXIT; + } + + ret = host_enable_sd_signal18v(host); + + /* 3.3V->1.8V OK */ + if (ret == FALSE) + goto ERROR; + goto EXIT; + +ERROR: + + /* return to previous status */ + + host_1_8v_sig_set(host, FALSE); + +EXIT: + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; + +} + +/* + * + * Function Name: sd_get_sdstatus + * + * Abstract: + * + * 1. Read the SD Status Register (SSR) (ACMD13) + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * + * Output: + * + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: card_init + */ + +static bool sd_get_sdstatus(sd_card_t *card, sd_command_t *sd_cmd) +{ + + byte cmd_index = SD_CMD13 | SD_APPCMD; + u32 argument = 0; + u32 cmdflag = CMD_FLG_R1 | CMD_FLG_RESCHK; + e_data_dir dir = DATA_DIR_IN; + u32 datalen = 64; + + bool ret = FALSE; + card_info_t *card_info = &(card->info); + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* Issue ACMD13 */ + ret = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, dir, + &(card_info->raw_ssr[0]), datalen); + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +/* + * + * Function Name: sd_send_cmd35 + * + * Abstract: + * + * 1. Read the SD Status Register (SSR) (CMD13) + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * + * Output: + * + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: card_init + */ + +static bool sd_send_cmd35(sd_card_t *card) +{ + + byte cmd_index = SD_CMD35; + u32 argument = 0; + u32 cmdflag = CMD_FLG_R1 | CMD_FLG_RESCHK; + e_data_dir dir = DATA_DIR_OUT; + sd_command_t sd_cmd; + byte buffer[512] = { + 0x10, 0x03, + 0x27, 0x86, 0x45, 0xA5, 0x19, 0x40, 0xF2, 0x25, + 0x20, 0x47, 0xDF, 0x94, 0xB8, 0x16, 0x13, 0x00, + 0x11, 0xF2, 0x1B, 0x4F, 0x23, 0x08, 0x2B, 0x33, + 0x21, 0x8C, 0x16, 0x52, 0x6A, 0x1D, 0x89, 0xE3, + 0x14, 0x09, 0x53, 0x84, 0x09, 0x47, 0x59, 0x50, + 0x57, 0xBB, 0x71, 0x3C, 0x47, 0xA7, 0x2A, 0x46, + 0xE2, 0xAF, 0x43, 0x10, 0x44, 0x05, 0x53, 0x7A, + 0x79, 0xC3, 0x29, 0xF3, 0x83, 0x45, 0x22, 0x1B, + }; + u32 datalen = 512; + bool ret = FALSE; + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* Issue CMD35 */ + ret = + card_send_sdcmd(card, &sd_cmd, cmd_index, argument, cmdflag, dir, + buffer, datalen); + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +/* + * + * Function Name: sd_send_cmd34 + * + * Abstract: + * + * 1. Read the SD Status Register (SSR) (CMD13) + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * + * Output: + * + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: card_init + */ + +static bool sd_send_cmd34(sd_card_t *card) +{ + + byte cmd_index = SD_CMD34; + u32 argument = 0; + u32 cmdflag = CMD_FLG_R1 | CMD_FLG_RESCHK; + e_data_dir dir = DATA_DIR_IN; + sd_command_t sd_cmd; + byte buffer[512] = { 0x00, 0x00, }; + u32 datalen = 512; + bool ret = FALSE; + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* Issue CMD34 */ + ret = + card_send_sdcmd(card, &sd_cmd, cmd_index, argument, cmdflag, dir, + buffer, datalen); + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +/* + * + * Function Name: sd_set_bus_width + * + * Abstract: + * + * 1. Set Bus Width to 4bit (ACMD6) + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * + * Output: + * + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: card_init + */ + +static bool sd_set_bus_width(sd_card_t *card, sd_command_t *sd_cmd) +{ + + byte cmd_index = SD_CMD6 | SD_APPCMD; + u32 argument = 0; + u32 cmdflag = CMD_FLG_R1 | CMD_FLG_RESCHK; + e_data_dir dir = DATA_DIR_NONE; + byte *data = NULL; + u32 datalen = 0; + bool ret = FALSE; + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + argument = BUS_WIDTH_4BIT; + + /* Issue ACMD6 */ + ret = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, dir, + data, datalen); + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +/* + * + * Function Name: sd_get_cid + * + * Abstract: + * + * 1. Addressed card sends its card identification data (CID) + * on the CMD line (CMD10) + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * + * Output: + * + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: card_init + */ + +static bool sd_get_cid(sd_card_t *card, sd_command_t *sd_cmd) +{ + + byte cmd_index = SD_CMD10; + u32 argument = 0; + u32 cmdflag = CMD_FLG_R2; + e_data_dir dir = DATA_DIR_NONE; + byte *data = NULL; + u32 datalen = 0; + bool ret = FALSE; + card_info_t *card_info = &(card->info); + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + argument = card_info->rca << 16; + + /* Issue CMD10 */ + ret = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, dir, + data, datalen); + if (ret) { + /* Set the card CID info */ + os_memcpy(&(card_info->raw_cid[0]), &(sd_cmd->response[0]), 16); + /* Parse the CID info */ + card_info->cid.manfid = card_info->raw_cid[0]; + card_info->cid.oemid = + card_info->raw_cid[1] | (card_info->raw_cid[2] << 8); + os_memcpy(card_info->cid.prod_name, &(card_info->raw_cid[3]), + 5); + card_info->cid.prv = card_info->raw_cid[8]; + card_info->cid.serial = + card_info->raw_cid[9] | (card_info->raw_cid[10] << 8) | + (card_info->raw_cid[11] << 16) | (card_info->raw_cid[12] << 24); + } + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +/* + * + * Function Name: sd_get_scr + * + * Abstract: + * + * 1. Read the SD Configuration Register (SCR) (ACMD51) + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * + * Output: + * + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: card_init + */ + +static bool sd_get_scr(sd_card_t *card, sd_command_t *sd_cmd) +{ + + byte cmd_index = SD_CMD51 | SD_APPCMD; + u32 argument = 0; + u32 cmdflag = CMD_FLG_R1 | CMD_FLG_RESCHK; + e_data_dir dir = DATA_DIR_IN; + u32 datalen = 8; + bool ret = FALSE; + card_info_t *card_info = &(card->info); + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* Issue ACMD51 */ + ret = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, dir, + &(card_info->raw_scr[0]), datalen); + if (ret) { + /* Parse the CSD info */ + card_info->scr.sd_spec = card_info->raw_scr[0] & 0xF; + card_info->scr.cmd_support = card_info->raw_scr[3] & 0xF; + card_info->scr.sd_spec3 = card_info->raw_scr[2] & 0x80; + card_info->scr.sd_specx = + (card_info->raw_scr[2] & 0x3) << 2 | + (card_info->raw_scr[3] & 0xc0) >> 6; + card_info->scr.reserved_B0 = card_info->raw_scr[7] & 0xFF; + card_info->scr.reserved_B1 = card_info->raw_scr[6] & 0xFF; + } + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +/* + * + * Function Name: sd_get_scr + * + * Abstract: + * + * 1. Read the SD Configuration Register (SCR) (ACMD51) + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * + * Output: + * + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: card_init + */ + +/* + * static bool sd_clear_card_detect(sd_card_t *card) + * { + * + * sd_command_t sd_cmd; + * byte cmd_index = SD_CMD42 | SD_APPCMD; + * u32 argument = 0; + * u32 cmdflag = CMD_FLG_R1 | CMD_FLG_RESCHK; + * e_data_dir dir = DATA_DIR_NONE; + * bool ret = FALSE; + * + * DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + * __func__); + * + * ret = + * card_send_sdcmd(card, &sd_cmd, cmd_index, argument, cmdflag, dir, + * NULL, 0); + * DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + * ret, __func__); + * return ret; + * } + */ + +/* + * + * Function Name: sd_switch_function_set_am + * + * Abstract: + * + * 1. Set SD switch function status (Access Mode) (CMD6) + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * byte access_mode: access mode which want be set. + * + * Output: + * + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: sd_switch_function_set + */ + +bool sd_switch_function_set_am(sd_card_t *card, + sd_command_t *sd_cmd, byte access_mode) +{ + + byte cmd_index = SD_CMD6; + u32 argument = SD_FNC_SW | SD_FNC_G1_INFL; + u32 cmdflag = CMD_FLG_R1 | CMD_FLG_RESCHK; + e_data_dir dir = DATA_DIR_IN; + byte data[64]; + u32 datalen = 64; + bool ret = FALSE; + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, access_mode=0x%x\n", __func__, access_mode); + + if (access_mode == SD_FNC_AM_DDR200) + argument = 0x80FFFFEF; + else + argument |= access_mode; + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Set AM to %x, argument=%08X\n", access_mode, argument); + + /* Issue CMD6 */ + ret = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, dir, + data, datalen); + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +/* + * + * Function Name: sd_switch_function_set_ds + * + * Abstract: + * + * 1. Set SD switch function status (Driver Strength) (CMD6) + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * byte driver_strength: driver strength which want be set. + * + * Output: + * + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: sd_switch_function_set + */ + +bool sd_switch_function_set_ds(sd_card_t *card, + sd_command_t *sd_cmd, byte driver_strength) +{ + + byte cmd_index = SD_CMD6; + u32 argument = SD_FNC_SW | SD_FNC_G3_INFL; + u32 cmdflag = CMD_FLG_R1 | CMD_FLG_RESCHK; + e_data_dir dir = DATA_DIR_IN; + byte data[64]; + u32 datalen = 64; + bool ret = FALSE; + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, driver_strength=0x%x\n", __func__, + driver_strength); + + argument |= driver_strength << 8; + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Set Driver Strength to %x, argument=%08X\n", driver_strength, + argument); + + /* Issue CMD6 */ + ret = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, dir, + data, datalen); + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +static byte card_get_max_am_cap(sd_card_t *card) +{ + byte max_am_cap = 0; + + if (sd_ddr_support(card)) + max_am_cap = SD_FNC_AM_DDR200; + else if (card->info.sw_func_cap.sd_access_mode & (1 << SD_FNC_AM_SDR104)) + max_am_cap = SD_FNC_AM_SDR104; + else if (card->info.sw_func_cap.sd_access_mode & (1 << SD_FNC_AM_SDR50)) + max_am_cap = SD_FNC_AM_SDR50; + else if (card->info.sw_func_cap.sd_access_mode & (1 << SD_FNC_AM_DDR50)) + max_am_cap = SD_FNC_AM_DDR50; + else if (card->info.sw_func_cap.sd_access_mode & (1 << SD_FNC_AM_HS)) + max_am_cap = SD_FNC_AM_HS; + else if (card->info.sw_func_cap.sd_access_mode & (1 << SD_FNC_AM_DS)) + max_am_cap = SD_FNC_AM_DS; + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "(%x) %s\n", + max_am_cap, __func__); + return max_am_cap; +} + +static byte card_get_min_am(byte am1, byte am2) +{ + byte am = 0; + + switch (am1) { + case SD_FNC_AM_DDR200: + am = am2; + break; + case SD_FNC_AM_SDR104: + if (am2 == SD_FNC_AM_DDR200) + am = am1; + else + am = am2; + break; + case SD_FNC_AM_SDR50: + if (am2 == SD_FNC_AM_SDR104 || am2 == SD_FNC_AM_DDR200) + am = am1; + else + am = am2; + break; + case SD_FNC_AM_DDR50: + if (am2 == SD_FNC_AM_SDR50 || am2 == SD_FNC_AM_SDR104 + || am2 == SD_FNC_AM_DDR200) { + am = am1; + } else { + am = am2; + } + break; + case SD_FNC_AM_HS: + if (am2 == SD_FNC_AM_DS) + am = am2; + else + am = am1; + break; + case SD_FNC_AM_DS: + am = am1; + break; + default: + break; + } + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "(%x) %s\n", am, + __func__); + return am; +} + +static byte card_power_limit_to_index(u8 pmlimit) +{ + byte i = 0; + byte power_limit[5] = { + SD_FNC_PL_072W, + SD_FNC_PL_144W, + SD_FNC_PL_180W, + SD_FNC_PL_216W, + SD_FNC_PL_288W + }; + + for (i = 0; i < 5; i++) + if (power_limit[i] == pmlimit) + return i; + + return 0; +} + +bool sd_switch_power_limit(sd_card_t *card, sd_command_t *sd_cmd, bool *bchg) +{ + bool ret = FALSE; + card_info_t *card_info = &(card->info); + bool high_to_low = TRUE; + byte start; + + byte power_limit[5] = { + SD_FNC_PL_072W, + SD_FNC_PL_144W, + SD_FNC_PL_180W, + SD_FNC_PL_216W, + SD_FNC_PL_288W + }; + int i; + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, *bchg= %d\n", __func__, *bchg); + + /* + * 3.1 Get Max Power Limit, default: 2.88W + * 3.2 check the Max power limit that card support + * Check order: 2.88W -> 2.16W -> 1.8W -> 1.44W -> 0,72W + */ + + start = + card_power_limit_to_index(card->sw_target_setting.sd_power_limit); + if (start > 4) { + DbgErr("Power Limit settings invalid!"); + start = 4; + } + + /* start = i; */ + if (card->thermal_enable) + if (card->thermal_heat == 0) + high_to_low = FALSE; + + if (high_to_low) { + for (i = start; i >= 0; i--) { + if (card_info->sw_cur_setting.sd_power_limit == + power_limit[i] && *bchg == FALSE) { + ret = TRUE; + *bchg = FALSE; + break; + } + /* Check card support or not */ + if (card_info->sw_func_cap.sd_power_limit & + (1 << (power_limit[i]))) { + ret = + sd_switch_function_set_pl(card, sd_cmd, + power_limit[i]); + if (!ret) { + DbgErr + ("Set Power Limit to %X Failed.\n", + power_limit[i]); + goto exit; + } + /* Update the current settings */ + card_info->sw_cur_setting.sd_power_limit = + power_limit[i]; + DbgInfo(MODULE_ALL_CARD, + FEATURE_FUNC_THERMAL | + FEATURE_CARD_INIT, NOT_TO_RAM, + "Powerlimit1 index=%d\n", i); + *bchg = TRUE; + break; + } + } + } else { + for (i = 0; i <= start; i++) { + if (card_info->sw_cur_setting.sd_power_limit == + power_limit[i] && *bchg == FALSE) { + ret = TRUE; + *bchg = FALSE; + break; + } + /* Check card support or not */ + if (card_info->sw_func_cap.sd_power_limit & + (1 << (power_limit[i]))) { + ret = + sd_switch_function_set_pl(card, sd_cmd, + power_limit[i]); + if (!ret) { + DbgErr + ("Set Power Limit to %X Failed.\n", + power_limit[i]); + goto exit; + } + /* Update the current settings */ + card_info->sw_cur_setting.sd_power_limit = + power_limit[i]; + DbgInfo(MODULE_ALL_CARD, + FEATURE_FUNC_THERMAL | + FEATURE_CARD_INIT, NOT_TO_RAM, + "Powerlimit2 index=%d\n", i); + *bchg = TRUE; + break; + } + } + } +exit: + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +bool sd_switch_access_mode(sd_card_t *card, sd_command_t *sd_cmd, bool *bchg) +{ + bool ret = FALSE; + card_info_t *card_info = &(card->info); + byte i; + u32 clock_freq; + u32 regval; + + sd_host_t *host = card->host; + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, *bchg= %d\n", __func__, *bchg); + + /* 4.1 Get Max Access Mode of UHSI */ + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "card->sw_target_setting.sd_access_mode %d\n", + card->sw_target_setting.sd_access_mode); + card->sw_target_setting.sd_access_mode = + card_get_min_am(card->sw_target_setting.sd_access_mode, + (byte) card_get_max_am_cap(card)); + i = card->sw_target_setting.sd_access_mode; + + while (i >= 0) { + /* Check card support or not */ + if (i == SD_FNC_AM_DDR200) { + ret = sd_switch_function_set_am(card, sd_cmd, i); + if (!ret) { + DbgErr("Set Access Mode to %X Failed.\n", i); + i = SD_FNC_AM_SDR104; + continue; + } + + /* Update the current settings */ + card_info->sw_cur_setting.sd_access_mode = i; + DbgInfo(MODULE_ALL_CARD, + FEATURE_FUNC_THERMAL | FEATURE_CARD_INIT, + NOT_TO_RAM, "Access Mode=%d\n", i); + *bchg = TRUE; + break; + } + + if (card_info->sw_func_cap.sd_access_mode & (1 << i)) { + if (card_info->sw_cur_setting.sd_access_mode == i + && *bchg == FALSE) { + ret = TRUE; + *bchg = FALSE; + goto exit; + } + ret = sd_switch_function_set_am(card, sd_cmd, i); + if (!ret) { + DbgErr("Set Access Mode to %X Failed.\n", i); + goto exit; + } + /* Update the current settings */ + card_info->sw_cur_setting.sd_access_mode = i; + DbgInfo(MODULE_ALL_CARD, + FEATURE_FUNC_THERMAL | FEATURE_CARD_INIT, + NOT_TO_RAM, "Access Mode=%d\n", i); + *bchg = TRUE; + break; + } + + /* If DDR50 is the target access mode, + * then the next access mode should be High Speed (1), + * instead of SDR104 (3) + */ + if (i == SD_FNC_AM_DDR50) { + i = SD_FNC_AM_HS; + continue; + } + + i--; + } + + /* 5 Set timing accrodingly */ + switch (card_info->sw_cur_setting.sd_access_mode) { + case SD_FNC_AM_DDR200: + { + if (card->ddr225_card_flag) + clock_freq = SD_CLK_225M; + else + clock_freq = SD_CLK_200M; + + break; + } + case SD_FNC_AM_SDR104: + { + clock_freq = SD_CLK_200M; + break; + } + case SD_FNC_AM_SDR50: + { + clock_freq = SD_CLK_100M; + break; + } + case SD_FNC_AM_SDR25: + { + clock_freq = SD_CLK_50M; + break; + } + case SD_FNC_AM_SDR12: + { + clock_freq = SD_CLK_25M; + break; + } + case SD_FNC_AM_DDR50: + { + clock_freq = SD_CLK_50M; + break; + } + default: + { + clock_freq = SD_CLK_25M; + break; + } + } + + if (card_info->sw_cur_setting.sd_access_mode == SD_FNC_AM_DDR50 + || card_info->sw_cur_setting.sd_access_mode == SD_FNC_AM_DDR200) { + card_legacy_change_clock(card, clock_freq, TRUE); + } else { + card_legacy_change_clock(card, clock_freq, FALSE); + } + + /* + * if the switch is successful,set host mode to DDR200 + * host 0x110[17]=1 + */ + if (card_info->sw_cur_setting.sd_access_mode == SD_FNC_AM_DDR200) { + regval = sdhci_readl(host, 0x110); + regval |= (1 << 17); + sdhci_writel(host, 0x110, regval); + } + + host_set_uhs_mode(host, card_info->sw_cur_setting.sd_access_mode); + +exit: + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; + +} + +/* + * + * Function Name: sd_switch_function_set + * + * Abstract: + * + * 1. Set SD switch function status + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * + * Output: + * + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: card_init + */ + +bool sd_switch_function_set(sd_card_t *card, sd_command_t *sd_cmd) +{ + + bool ret = FALSE; + sd_host_t *host = card->host; + card_info_t *card_info = &(card->info); + bool bchg = TRUE; + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* 1. Check the function card supported */ + + /* + * 2. Set Driver Strength + * As default, driver do not set driver strength + * Only set it when configure driver_strength enabled + */ + + if (host->cfg->card_item.test_driver_strength_sel.enable_set) { + byte driver_strength = card->sw_target_setting.sd_drv_type; + /* Do function switch when card support this function */ + if ((1 << driver_strength) & + (card_info->sw_func_cap.sd_drv_type)) { + ret = + sd_switch_function_set_ds(card, sd_cmd, + driver_strength); + if (!ret) { + DbgErr("Set driver strength to %X Failed.\n", + driver_strength); + goto exit; + } + /* Update the current settings */ + card_info->sw_cur_setting.sd_drv_type = driver_strength; + } + } else if ((card_info->cid.manfid == 0x1b) && sd_ddr_support(card)) { + /* Driver strength select 3h: Type D */ + byte driver_strength = 0x3; + + PrintMsg("Samsung DDR200 card need switch DS to type D\n"); + + ret = sd_switch_function_set_ds(card, sd_cmd, driver_strength); + if (!ret) { + DbgErr("Set driver strength to %X Failed.\n", + driver_strength); + goto exit; + } + /* Update the current settings */ + card_info->sw_cur_setting.sd_drv_type = driver_strength; + } else { + /* do nothing */ + } + + /* 3. Set Power Limit. */ + /* Init case we always do pm setting */ + bchg = TRUE; + ret = sd_switch_power_limit(card, sd_cmd, &bchg); + if (!ret) { + DbgErr("Set Power Limit Failed.\n"); + goto exit; + } + + /* 4. Set Access mode */ + /* Init case we always do am setting */ + bchg = TRUE; + ret = sd_switch_access_mode(card, sd_cmd, &bchg); + if (!ret) { + DbgErr("Set Access Mode Failed.\n"); + goto exit; + } +exit: + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +/* + * + * Function Name: sd_tuning_hw + * + * Abstract: + * + * 1. Hardware Tuning Procedure (CMD19) + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * + * Output: + * + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: sd_tuning + */ + +bool sd_tuning_hw(sd_card_t *card, sd_command_t *sd_cmd, u32 timeout) +{ + + byte cmd_index = SD_CMD19; + u32 argument = 0; + u32 cmdflag = CMD_FLG_R1 | CMD_FLG_TUNE; + e_data_dir dir = DATA_DIR_IN; + byte data[64]; + u32 datalen = 64; + + bool ret = FALSE; + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* 1. Set driver HW mode here */ + host_set_tuning_mode(card->host, TRUE); + /* add 200us delay before CMD19 to fix FJ2 ASIC issue 14 */ + if (card->host->chip_type == CHIP_FUJIN2) + os_udelay(200); + + /* 2. Tuning now */ + ret = + card_send_sdcmd_timeout(card, sd_cmd, cmd_index, argument, cmdflag, + dir, data, datalen, timeout); + if (!ret) + DbgErr(" - SendCommand19 failed during tuning!\n"); + else + ret = host_chk_tuning_comp(card->host, TRUE); + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +/* + * + * Function Name: sd_tuning_sw + * + * Abstract: + * + * 1. Software Tuning Procedure (CMD19) + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * + * Output: + * + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: sd_tuning + */ + +bool sd_tuning_sw(sd_card_t *card, sd_command_t *sd_cmd) +{ + byte cmd_index = SD_CMD19; + u32 argument = 0; + u32 cmdflag = CMD_FLG_R1 | CMD_FLG_TUNE; + e_data_dir dir = DATA_DIR_IN; + byte data[64]; + u32 datalen = 64; + u16 i; + + bool ret = FALSE; + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* Try 100 SW tuning */ + for (i = 0; i < 100; i++) { + /* 1. Set driver SW mode here */ + host_set_tuning_mode(card->host, FALSE); + /* add 200us delay before CMD19 to fix FJ2 ASIC issue 14 */ + if (card->host->chip_type == CHIP_FUJIN2) + os_udelay(200); + + /* 2. Tuning now */ + ret = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, + dir, data, datalen); + if (!ret) { + DbgErr("SendCommand19 failed during tuning!\n"); + break; + } + + ret = host_chk_tuning_comp(card->host, TRUE); + if (ret) + break; + + } + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +/* + * + * Function Name: sd_tuning + * + * Abstract: + * + * 1. Tuning Procedure (CMD19) + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * + * Output: + * + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: card_init + */ + +bool sd_tuning(sd_card_t *card, sd_command_t *sd_cmd, u32 timeout) +{ + bool ret = FALSE; + card_info_t *card_info = &(card->info); + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + if (card_info->sw_cur_setting.sd_access_mode < SD_FNC_AM_SDR50) { + /* Only do tuning procedure for DDR50, SDR104, SDR50,DDR200 */ + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "No need to do tuning for access mode %d\n", + card_info->sw_cur_setting.sd_access_mode); + ret = TRUE; + } else { + if (TUNING_MODE) { + /* HW tuning */ + ret = sd_tuning_hw(card, sd_cmd, timeout); + } else { + ret = sd_tuning_sw(card, sd_cmd); + } + + } + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +/* + * + * Function Name: sd_switch_function_check + * + * Abstract: + * + * 1. Get SD switch function status (CMD6) + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * + * Output: + * + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: card_init + */ + +bool sd_switch_function_check(sd_card_t *card, sd_command_t *sd_cmd) +{ + + byte cmd_index = SD_CMD6; + u32 argument = SD_FNC_NOINFL; + u32 cmdflag = CMD_FLG_R1 | CMD_FLG_RESCHK; + e_data_dir dir = DATA_DIR_IN; + byte data[64]; + u32 datalen = 64; + bool ret = FALSE; + card_info_t *card_info = &(card->info); + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* Issue CMD6 */ + ret = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, dir, + data, datalen); + if (ret) { + /* Set the card swich function status info */ + card_info->sw_func_cap.sd_access_mode = data[13]; + card_info->sw_func_cap.sd_command_system = data[10]; + card_info->sw_func_cap.sd_drv_type = data[9]; + card_info->sw_func_cap.sd_power_limit = data[7]; + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Card Sup AM:%X\n", + card_info->sw_func_cap.sd_access_mode); + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Card Sup CS:%X\n", + card_info->sw_func_cap.sd_command_system); + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Card Sup DS:%X\n", card_info->sw_func_cap.sd_drv_type); + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Card Sup PL:%X\n", + card_info->sw_func_cap.sd_power_limit); + } + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +/* + * + * Function Name: sd_switch_function_set_pl + * + * Abstract: + * + * 1. Set SD switch function status (Power Limit) (CMD6) + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * byte driver_strength: driver strength which want be set. + * + * Output: + * + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: sd_switch_function_set + */ + +bool sd_switch_function_set_pl(sd_card_t *card, + sd_command_t *sd_cmd, byte power_limit) +{ + + byte cmd_index = SD_CMD6; + u32 argument = SD_FNC_SW | SD_FNC_G4_INFL; + u32 cmdflag = CMD_FLG_R1 | CMD_FLG_RESCHK; + e_data_dir dir = DATA_DIR_IN; + byte data[64]; + u32 datalen = 64; + bool ret = FALSE; + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, power_limit=%d\n", __func__, power_limit); + + argument |= (power_limit) << 12; + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Set Power Limit to %x, argument=%08X\n", power_limit, + argument); + + /* Issue CMD6 */ + ret = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, dir, + data, datalen); + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +/* + * + * Function Name: sd_switch_function_set_pl + * + * Abstract: + * + * 1. Set SD switch function status (Power Limit) (CMD6) + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * byte driver_strength: driver strength which want be set. + * + * Output: + * + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: sd_switch_function_set + */ + +bool sd_lightning_mode_sw(sd_card_t *card, sd_command_t *sd_cmd) +{ + + byte cmd_index = SD_CMD6; + u32 argument = SD_FNC_SW | SD_FNC_G4_INFL; + u32 cmdflag = CMD_FLG_R1 | CMD_FLG_RESCHK; + e_data_dir dir = DATA_DIR_IN; + byte data[64]; + u32 datalen = 64; + u32 card_status = 0; + bool ret = FALSE; + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* 1. Check if lightning mode is enabled or not */ + /* Host was set to do not support lightning mode */ + goto EXIT; + + if (card->info.cid.manfid != MID_SANDISK) { + /* Card is not SanDisk Card */ + goto EXIT; + } + + /* 2. Set card to vendor specific mode */ + { + cmd_index = SD_CMD6; + argument = SD_FNC_SW | SD_FNC_G2_VEN; + cmdflag = CMD_FLG_R1 | CMD_FLG_RESCHK; + dir = DATA_DIR_IN; + datalen = 64; + /* Issue CMD6 */ + ret = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, + dir, data, datalen); + if (ret == FALSE) { + DbgErr(("Set Vendor Specific mode failed!\n")); + + goto EXIT; + } + } + + /* 3. Send CMD13 */ + ret = card_get_card_status(card, sd_cmd, &card_status); + if (ret == FALSE) { + DbgErr(("Send Status error(CMD13)\n")); + + goto EXIT; + } + + /* 4. Send CMD35 */ + ret = sd_send_cmd35(card); + if (ret == FALSE) { + DbgErr(("Send Status error(CMD13)\n")); + + goto EXIT; + } + + /* Send CMD34 */ + ret = sd_send_cmd34(card); + if (ret == FALSE) { + DbgErr(("Send Status error(CMD13)\n")); + + goto EXIT; + } + + /* Delay */ + os_mdelay(1); + +EXIT: + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +/* + * + * Function Name: sd_card_identify + * + * Abstract: + * + * 1. Issue reset command (CMD0) + * 2. Issue send IF condition command (CMD8) + * 3. SDIO swithch function supportted? + * 4. Wait for card ready (ACMD41) + * 5. Signal voltage switch prucedure (CMD11) + * 6. Get card CID(CMD2) + * 7. Get card relative address (CMD3) + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * + * Output: + * + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: sd_legacy_init, ush2_card_init + */ + +bool sd_card_identify(sd_card_t *card) +{ + bool result = FALSE; + bool flag_f8 = FALSE; + sd_command_t sd_cmd; + u32 argument = 0x000001AA; + + card->uhs2_card = FALSE; + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + os_memset(&sd_cmd, 0, sizeof(sd_command_t)); + + if (card->card_type != CARD_UHS2) + os_udelay(200); + + /* 1. Issue reset command (CMD0) */ + + result = card_reset_card(card, &sd_cmd); + if (!result) { + /* Go Idle State command failed. exit directly. */ + DbgErr("Reset Card (CMD0) Failed.\n"); + goto exit; + } + + /* 2. Issue send IF condition command (CMD8) */ + result = sd_send_if_cond(card, &sd_cmd, argument); + if (!result) { + + /* 2.1 Error response */ + if (sd_cmd.err.error_code == ERR_CODE_RESP_ERR || + sd_cmd.err.error_code == ERR_CODE_NO_CARD) { + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "CMD8 Response Error or no card.\n"); + goto exit; + } + + /* 2.2 No Response (Standard Capacity Card) */ + { + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "CMD8 No Responser.\n"); + + /* 2.2.1 Set flag F8 = 0 */ + flag_f8 = FALSE; +#if (0) + card->card_cap_type = CARD_SDSC_V1; +#endif + /* 2.2.2 Reset card (CMD0) again. */ + result = card_reset_card(card, &sd_cmd); + if (!result) { + /* Go Idle State command failed. exit directly. */ + DbgErr("Reset Card Again (CMD0) Failed.\n"); + goto exit; + } + } + } else { + /* 2.3 Good Response (High Capacity card) */ + /* 2.3.1 Set flag F8 = 1 */ + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "CMD8 Good Responser (High Capacity Card).\n"); + + flag_f8 = TRUE; + } + + /* 3. SDIO swithch function supportted? */ +#if (0) + /* RTU_OK */ + if ((card->card_type != CARD_SD) && (card->card_type != CARD_UHS2)) { + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "SDIO swithch function supportted.\n"); + + /* 3.1 issue CMD5 to check card is SDIO card or not */ + result = sdio_check(card, &sd_cmd); + if (result == TRUE) { + /* 3.1.1 set card type to SDIO_CARD */ + card->card_type = CARD_SDIO; + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "SDIO card.\n"); + /* 3.1.3 Return failed */ + result = FALSE; + goto exit; + } + } +#endif + + /* 4. Wait for card ready (ACMD41) */ + result = card_init_ready(card, &sd_cmd, flag_f8); + if (!result) { + /* + * 4.1 Return failed for following cases: + * - OCR check fail, + * - or command timeout, + * - or command55 fail, + * - or ACMD41 response error + */ + DbgErr("Wait for card ready (ACMD41) Failed.\n"); + goto exit; + } + + /* Try to init as SD Legacy card */ + if (card->card_type != CARD_UHS2) + card->card_type = CARD_SD; + + /* 5. Signal voltage switch prucedure (CMD11) */ + if (need_switch_sig_voltage(card)) { + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Need to do signal voltage switch.\n"); + + result = signal_voltage_switch(card, &sd_cmd); + if (!result) { + /* + * 5.1 If signal voltage switch failed, + * need return failed for power cycle. + */ + DbgErr("Signal voltage switch failed.\n"); + goto exit; + } + } + + /* 6. Get card CID(CMD2) */ + result = card_all_send_cid(card, &sd_cmd); + if (!result) { + /* 6.1 If failed, need return failed for power cycle. */ + DbgErr("Get card CID(CMD2) failed.\n"); + goto exit; + } + + /* 7. Get card relative address (CMD3) */ + result = card_get_rca(card, &sd_cmd); + if (!result) { + /* 7.1 If failed, need return failed for power cycle. */ + DbgErr("Get card relative address (CMD3) failed.\n"); + goto exit; + } + +exit: + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + result, __func__); + return result; +} + +/* + * + * Function Name: sd_card_select + * + * Abstract: + * + * 1. Get CID (CMD10) + * 2. Get CSD (CMD9) + * 3. Select the card (CMD7) + * 4. Get Lock/Unlock status, CMD7 Response [25]. + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * + * Output: + * + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: sd_legacy_init, ush2_card_init + */ + +bool sd_card_select(sd_card_t *card) +{ + bool result = FALSE; + sd_command_t sd_cmd; + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + os_memset(&sd_cmd, 0, sizeof(sd_command_t)); + + if (card_need_get_info(card)) { + /* 1.1 Get CID (CMD10) */ + result = sd_get_cid(card, &sd_cmd); + if (!result) { + DbgErr("Get CID (CMD10) failed.\n"); + goto exit; + } + + /* 1.2 Get CSD (CMD9) */ + result = card_get_csd(card, &sd_cmd); + if (!result) { + DbgErr("Get CSD (CMD9) failed.\n"); + goto exit; + } + } else { + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Already get card info, skip getting CID,CSD.\n"); + } + + /* 2. Select the card (CMD7) */ + result = card_select_card(card, &sd_cmd); + if (!result) { + /* 2.1 If failed, need return failed for power cycle. */ + DbgErr("Select card (CMD7) failed.\n"); + goto exit; + } + +exit: + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + result, __func__); + return result; + +} + +bool sd_init_get_info(sd_card_t *card) +{ + bool result = TRUE; + sd_command_t sd_cmd; + sd_host_t *host = card->host; + + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + os_memset(&sd_cmd, 0, sizeof(sd_command_t)); + + /* 11. Set bus width */ + /* uhs2 card don't need this flow */ + if (card->card_type == CARD_SD) { + /* 11.1 Set card bus width (ACMD6) */ + result = sd_set_bus_width(card, &sd_cmd); + if (!result) { + /* 11.1 If failed, need return failed for power cycle. */ + DbgErr("Set card bus width (ACMD6) failed.\n"); + goto exit; + } + + /* 11.2 Set Host bus width */ + host_set_buswidth(host, BUS_WIDTH4); + + /* 12. Set block length (CMD16) */ + result = card_set_block_len(card, &sd_cmd, SD_BLOCK_LEN); + if (!result) { + /* 12.1 If failed, need return failed for power cycle. */ + DbgErr("Set block length (CMD16) failed.\n"); + goto exit; + } + } + + /* 13. Get card related info, like CID,CSD, SCR, SD_Status */ + if (card_need_get_info(card)) { + /* 13.3 Get SCR (ACMD51) */ + result = sd_get_scr(card, &sd_cmd); + if (!result) { + DbgErr("Get SCR (ACMD51) failed.\n"); + goto exit; + } + /* 13.4 Get SD Status (ACMD13) */ + result = sd_get_sdstatus(card, &sd_cmd); + if (!result) { + DbgErr("Get SD Status (ACMD13) failed.\n"); + goto exit; + } + } else { + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Already get card info, skip getting SCR, SD_Status.\n"); + } + +exit: + DbgInfo(MODULE_ALL_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + result, __func__); + return result; +} + +bool sd_init_stage2(sd_card_t *card) +{ + bool result = FALSE; + sd_host_t *host = card->host; + cfg_item_t *cfg_item = card->host->cfg; + card_info_t *card_info = &(card->info); + sd_command_t sd_cmd; + u8 tuning_type = 0; + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + os_memset(&sd_cmd, 0, sizeof(sd_command_t)); + + if (host->chip_type != CHIP_GG8) { + /* 1. Set SD Host Clock to 25MHz */ + card_legacy_change_clock(card, SD_CLK_25M, FALSE); + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Set SD Host Clock to 25MHz.\n"); + } else + os_mdelay(15); + + result = sd_init_get_info(card); + if (!result) { + DbgErr("SD Card get info failed\n"); + goto exit; + } +#if (0) + /* Turns Off the Pull-up resistor of the SD Card */ + result = sd_clear_card_detect(card); + if (!result) { + DbgErr + ("Turns Off the Pull-up resistor of the SD Card failed\n"); + goto exit; + } +#endif + + if (card->restore_tuning_content_fail) { + result = + restore_tuning_address_content(card, + card->sec_count - + TUNING_ADDRESS_OFFSET); + if (!result) { + DbgErr("restore_tuning_address_content failed\n"); + card->restore_tuning_content_fail = 1; + goto exit; + } + } + + /* 2. Need to clear High Speed Enable */ + host_set_highspeed(host, FALSE); + + /* 3. Swich function check/set */ + if (card_info->scr.sd_spec < SCR_SPEC_VER_1) { + result = TRUE; + card->sw_target_setting.sd_access_mode = SD_FNC_AM_DS; + if (host->chip_type == CHIP_GG8) { + /* 1. Set SD Host Clock to 25MHz */ + card_legacy_change_clock(card, SD_CLK_25M, FALSE); + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Set SD Host Clock to 25MHz.\n"); + } + goto exit; + } else if (!(card_info->card_s18a)) { + card->sw_target_setting.sd_access_mode = + os_min(card->sw_target_setting.sd_access_mode, + SD_FNC_AM_HS); + if (card_need_get_info(card)) { + /* 3.1. Check if card support Hight Speed. */ + result = sd_switch_function_check(card, &sd_cmd); + if (!result) { + DbgErr("Swich function check (CMD6) failed.\n"); + goto exit; + } + } else { + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Card function check skipped.\n"); + } + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Card support High Speed.\n"); + + if ((card->info.sw_func_cap.sd_access_mode & (1 << SD_FNC_AM_HS)) + && (card->sw_target_setting.sd_access_mode >= SD_FNC_AM_HS)) { + result = + sd_switch_function_set_am(card, &sd_cmd, + SD_FNC_AM_HS); + if (!result) { + DbgErr + ("Set Access Mode to High Speed Failed.\n"); + goto exit; + } + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Switch to High Speed OK.\n"); + if (host->chip_type == CHIP_GG8) { + /* 1. Set SD Host Clock to 25MHz */ + card_legacy_change_clock(card, SD_CLK_25M, + FALSE); + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, + NOT_TO_RAM, + "Set SD Host Clock to 25MHz.\n"); + } + + /* 3.2. Update the current settings */ + card_info->sw_cur_setting.sd_access_mode = SD_FNC_AM_HS; + /* 3.3. Need to set High Speed Enable */ + host_set_highspeed(host, TRUE); + + /* 4. Check Lighting card support */ + result = sd_lightning_mode_sw(card, &sd_cmd); + if (result == TRUE) { + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, + NOT_TO_RAM, + "Card support Lighting mode, change clock to 75MHz.\n"); + /* 4.1. Change the clock to 75MHz */ + card_legacy_change_clock(card, SD_CLK_75M, + FALSE); + } else { + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, + NOT_TO_RAM, "Change clock to 50MHz.\n"); + /* 4.2. Change the clock to 50MHz */ + card_legacy_change_clock(card, SD_CLK_50M, + FALSE); + } + result = TRUE; + } else { + /* + * Degrade access mode to Default Speed case. + * Need to switch access mode to Default Speed + * as card default AM is High Speed. + */ + result = + sd_switch_function_set_am(card, &sd_cmd, + SD_FNC_AM_DS); + if (!result) { + DbgErr + ("Set Access Mode to Default Speed Failed.\n"); + goto exit; + } + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Switch to Default Speed OK.\n"); + + } + + } else { + + if (card_need_get_info(card)) { + + /* 5.1 Swich function check first to get card function capabilities */ + result = sd_switch_function_check(card, &sd_cmd); + if (!result) { + DbgErr("Swich function check (CMD6) failed.\n"); + goto exit; + } + } else { + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Card function check skipped.\n"); + } + + if ((card_get_max_am_cap(card) >= SD_FNC_AM_SDR50) + && (cfg_item->card_item.test_max_access_mode.value >= 0x2)) + result = + store_tuning_address_content(card, + card->sec_count - + TUNING_ADDRESS_OFFSET); + else + /* SD2.0 card shall not store tuning */ + result = FALSE; + + if (!result) { + DbgErr("store_tuning_address_contento failed\n"); + /* goto exit; */ + } + + /* + * 5.2 Swich function check set. + * - Driver Strength, + * - Access Mode, + * - Power Limit + * - Change clock freq + */ + + result = sd_switch_function_set(card, &sd_cmd); + if (!result) { + DbgErr("Swich function set (CMD6) failed.\n"); + goto exit; + } + + tuning_type = + hostven_tuning_type_selection(host, + card_info->sw_cur_setting.sd_access_mode); + + /* 5.3 Tuning Procedure (for DDR200, SDR104 and SDR50 Only) */ + if (card->info.sw_cur_setting.sd_access_mode == SD_FNC_AM_SDR104 + || card->info.sw_cur_setting.sd_access_mode == + SD_FNC_AM_SDR50 + || card->info.sw_cur_setting.sd_access_mode == + SD_FNC_AM_DDR200) { + switch (tuning_type) { + case 0: + hostven_fix_output_tuning(host, + card_info->sw_cur_setting.sd_access_mode); + break; + case 1: + hostven_fix_output_tuning(host, + card_info->sw_cur_setting.sd_access_mode); + result = sd_tuning(card, &sd_cmd, 0); + if (!result) { + DbgErr("Tuning (CMD19) failed.\n"); + goto exit; + } + break; + case 2: + result = + card_output_tuning(card, + card->sec_count - + TUNING_ADDRESS_OFFSET); + if (!result) { + DbgErr("card_output_tuning failed.\n"); + card->restore_tuning_content_fail = 1; + goto exit; + } + + if (card->read_signal_block_flag) { + result = + restore_tuning_address_content(card, + card->sec_count + - + TUNING_ADDRESS_OFFSET); + if (!result) { + DbgErr + ("restore_tuning_address_content failed\n"); + card->restore_tuning_content_fail + = 1; + goto exit; + } + } else { + erase_rw_blk_start_set(card, &sd_cmd, + (u32) + (card->sec_count) + - + TUNING_ADDRESS_OFFSET); + erase_rw_blk_end_set(card, &sd_cmd, + ((u32) + (card->sec_count) + - + TUNING_ADDRESS_OFFSET) + + 1); + func_erase(card, &sd_cmd); + } + + break; + default: + break; + } + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Tuning Procedure Done. Access Mode=%d.\n", + card_info->sw_cur_setting.sd_access_mode); + } + + } +exit: + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + result, __func__); + return result; +} + +/* + * + * Function Name: sd_legacy_init + * + * Abstract: + * + * 1. sd legacy card (uhs1, legacy) initialize main function. + * 2. Fill virtual card structure, like cid, csd, etc. + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * + * Output: + * + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: card_init + */ + +bool sd_legacy_init(sd_card_t *card) +{ + bool result = FALSE; + sd_host_t *host = card->host; + + card->uhs2_card = FALSE; + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + if (shift_bit_func_enable(host)) + set_pattern_value(host, 0x10); + + host_sd_init(host); + + /* SD Card Identification */ + result = sd_card_identify(card); + if (!result) { + DbgErr("SD Card Identification Stage failed.\n"); + goto error_exit; + } + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Card Identification Stage OK.\n"); + + /* SD Card Select and lock/unlock check */ + result = sd_card_select(card); + if (!result) { + DbgErr("SD Card Select failed.\n"); + goto error_exit; + } + + if (card->locked == TRUE) { + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Card is Locked.\n"); + goto next; + } + + result = card_init_stage2(card); + if (!result) { + DbgErr("SD init stage 2 failed.\n"); + goto error_exit; + } + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Card Init Stage 2 OK.\n"); + +next: + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(OK) %s\n", + __func__); + return result; + +error_exit: + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Exit(FAIL) %s\n", __func__); + return result; +} + +static byte sd_get_lower_am(sd_card_t *card, byte access_mode) +{ + cfg_item_t *cfg = card->host->cfg; + byte lower_am = 0; + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT | FEATURE_ERROR_RECOVER, + NOT_TO_RAM, "Enter %s, access_mode=%d\n", __func__, + access_mode); + + /* Degrade access mode according to current access mode. */ + switch (access_mode) { + case SD_FNC_AM_DDR200: + lower_am = + card_get_min_am((byte) cfg->card_item.test_max_access_mode.value, + (byte) card_get_max_am_cap(card)); + break; + case SD_FNC_AM_SDR104: + /* Degrade to SDR50 directly */ + lower_am = SD_FNC_AM_SDR50; + break; + case SD_FNC_AM_SDR50: + if ((cfg->card_item.test_max_access_mode.value == SD_FNC_AM_DDR50) + && (card->info.sw_func_cap.sd_access_mode & + (1 << SD_FNC_AM_DDR50))) { + /* + * Max Access mode is DDR50 and card support DD50, + * then can be degrade to DDR50 + */ + lower_am = SD_FNC_AM_DDR50; + } else { + /* Degrade to High Speed */ + lower_am = SD_FNC_AM_HS; + } + break; + case SD_FNC_AM_DDR50: + /* Degrade to High Speed directly */ + lower_am = SD_FNC_AM_HS; + break; + case SD_FNC_AM_HS: + /* Degrade to Default Speed directly */ + lower_am = SD_FNC_AM_DS; + break; + default: + lower_am = SD_FNC_AM_DS; + break; + } + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT | FEATURE_ERROR_RECOVER, + NOT_TO_RAM, "Exit(%d) %s\n", lower_am, __func__); + return lower_am; +} + +static byte sd_get_higher_am(sd_card_t *card, byte access_mode) +{ + cfg_item_t *cfg = card->host->cfg; + byte higher_am = 0; + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT | FEATURE_ERROR_RECOVER, + NOT_TO_RAM, "Enter %s, access_mode=%d\n", __func__, + access_mode); + + /* get higher access mode. */ + switch (access_mode) { + case SD_FNC_AM_DDR200: + higher_am = SD_FNC_AM_DDR200; + break; + case SD_FNC_AM_SDR104: + /* Keep SDR104 */ + if (sd_ddr_support(card) + && (cfg->card_item.test_max_access_mode.value == + SD_FNC_AM_SDR104)) + higher_am = SD_FNC_AM_DDR200; + else + higher_am = SD_FNC_AM_SDR104; + + break; + case SD_FNC_AM_SDR50: + if ((cfg->card_item.test_max_access_mode.value == + SD_FNC_AM_SDR104) + && card->info.sw_func_cap.sd_access_mode & + (1 << SD_FNC_AM_SDR104)) { + /* SDR104 */ + higher_am = SD_FNC_AM_SDR104; + } else { + /* SDR50 */ + higher_am = SD_FNC_AM_SDR50; + } + break; + case SD_FNC_AM_DDR50: + /* Degrade to High Speed directly */ + if ((cfg->card_item.test_max_access_mode.value == + SD_FNC_AM_SDR50) + && card->info.sw_func_cap.sd_access_mode & + (1 << SD_FNC_AM_SDR50)) { + /* SDR50 is higher level access mode of DDR50 */ + higher_am = SD_FNC_AM_SDR50; + } else { + /* No change */ + higher_am = SD_FNC_AM_DDR50; + } + break; + case SD_FNC_AM_HS: + /* Degrade to Default Speed directly */ + if ((cfg->card_item.test_max_access_mode.value == + SD_FNC_AM_DDR50) + && card->info.sw_func_cap.sd_access_mode & + (1 << SD_FNC_AM_DDR50)) { + /* DDR50 supported, then it is higher level access mode of High Speed. */ + higher_am = SD_FNC_AM_DDR50; + } else if ((cfg->card_item.test_max_access_mode.value == + SD_FNC_AM_SDR50) + && card->info.sw_func_cap.sd_access_mode & + (1 << SD_FNC_AM_SDR50)) { + /* + * DDR50 do not supported, + * then SDR50 is the higher level access mode of High Speed. + */ + higher_am = SD_FNC_AM_SDR50; + } else { + /* No change */ + higher_am = SD_FNC_AM_HS; + } + break; + case SD_FNC_AM_DS: + if ((cfg->card_item.test_max_access_mode.value == SD_FNC_AM_HS) + && card->info.sw_func_cap.sd_access_mode & + (1 << SD_FNC_AM_HS)) { + /* High Speed is the higher level access mode of Default Speed. */ + higher_am = SD_FNC_AM_HS; + } else { + /* No change */ + higher_am = SD_FNC_AM_DS; + } + break; + default: + break; + } + + DbgInfo(MODULE_SD_CARD, FEATURE_CARD_INIT | FEATURE_ERROR_RECOVER, + NOT_TO_RAM, "Exit(%d) %s\n", higher_am, __func__); + return higher_am; +} + +/* + * Function Name: sd_degrade_policy + * + * Abstract: This Function is used set sd degrade flag + * + * Input: + * sd_card_t *card : The Command will send to which Card + + * Return value: + */ +void sd_degrade_policy(sd_card_t *card) +{ + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Enter %s\n", + __func__); + + if (card->info.sw_cur_setting.sd_access_mode == SD_FNC_AM_SDR50 || + card->info.sw_cur_setting.sd_access_mode == SD_FNC_AM_SDR104 || + card->info.sw_cur_setting.sd_access_mode == SD_FNC_AM_DDR200) { + /* If Access Mode >= SDR50, then try to degrade freq first */ + if (card->degrade_freq_level < CARD_DEGRADE_FREQ_TIMES) { + /* Degrade freq less than 3 times, continue to degrade freq */ + card->degrade_freq_level++; + } else { + /* As degrade mode is needed, clear the degrade freq level. */ + card->degrade_freq_level = 0; + + /* Degrade freq larger than 3 times, then degrade accessmode */ + card->sw_target_setting.sd_access_mode = + sd_get_lower_am(card, + card->sw_target_setting.sd_access_mode); + /* If Degrade to Default Speed already. Mark as degrade final */ + if (card->sw_target_setting.sd_access_mode == + SD_FNC_AM_DS) { + card->degrade_final = 1; + } + } + } else { + /* As degrade mode is needed, clear the degrade freq level. */ + card->degrade_freq_level = 0; + + /* If Access Mode < SDR50, then degrade access mode directly */ + card->sw_target_setting.sd_access_mode = + sd_get_lower_am(card, + card->sw_target_setting.sd_access_mode); + /* If Degrade to Default Speed already. Mark as degrade final */ + if (card->sw_target_setting.sd_access_mode == SD_FNC_AM_DS) + card->degrade_final = 1; + + } + + DbgErr("Legacy SD degrade target=%d freq_level=%d final=%d\n", + card->sw_target_setting.sd_access_mode, card->degrade_freq_level, + card->degrade_final); + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +/* + * Function Name: uhs2_thermal_control + * Abstract: This Function is used to do card thremal control, only for SD and UHS2 card + * + * Input: + * sd_card_t *card + * + * Return value: + * TRUE: means ok + * others means error occur, caller need do error recovery + * + * Notes: + * run in thread context + */ + +bool sd_thermal_control(sd_card_t *card) +{ + bool bheat = (bool)card->thermal_heat; + sd_command_t sd_cmd; + bool result = TRUE; + bool change_am = FALSE; + byte am = 0; + bool bchg = FALSE; + + DbgInfo(MODULE_SD_CARD, FEATURE_FUNC_THERMAL, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* 2.0 Card don't do thermal control */ + if (card->info.card_s18a == 0 + || card->host->cfg->card_item.sd_card_mode_dis.dis_sd30_card) + goto exit; + + if (bheat) { + am = sd_get_higher_am(card, + card->info.sw_cur_setting.sd_access_mode); + } else { + am = sd_get_lower_am(card, + card->info.sw_cur_setting.sd_access_mode); + } + + /* If one access mode need tuning and another don't need we can't change */ + if (am == card->info.sw_cur_setting.sd_access_mode) { + change_am = FALSE; + } else if (am == SD_FNC_AM_SDR50 || am == SD_FNC_AM_SDR104 + || am == SD_FNC_AM_DDR200) { + if (card->info.sw_cur_setting.sd_access_mode == SD_FNC_AM_SDR50 + || card->info.sw_cur_setting.sd_access_mode == + SD_FNC_AM_DDR200 + || card->info.sw_cur_setting.sd_access_mode == + SD_FNC_AM_SDR104) + change_am = TRUE; + else if (am != SD_FNC_AM_SDR50 && am != SD_FNC_AM_SDR104 + && am != SD_FNC_AM_DDR200) + if (card->info.sw_cur_setting.sd_access_mode != + SD_FNC_AM_SDR50 + && card->info.sw_cur_setting.sd_access_mode != + SD_FNC_AM_SDR104 + && card->info.sw_cur_setting.sd_access_mode != + SD_FNC_AM_DDR200) + change_am = TRUE; + } +/* next: */ + if (change_am) { + card->thermal_access_mode = am; + DbgInfo(MODULE_SD_CARD, FEATURE_FUNC_THERMAL, NOT_TO_RAM, + "thermal switch am = %d\n", am); + result = card_stop_infinite(card, TRUE, NULL); + if (result == FALSE) { + DbgErr("uhs2 Thermal Stop Infinite failed1\n"); + goto exit; + } + + result = sd_switch_access_mode(card, &sd_cmd, &bchg); + if (result == FALSE) + goto exit; + + if (card->info.sw_cur_setting.sd_access_mode == SD_FNC_AM_SDR104 + || card->info.sw_cur_setting.sd_access_mode == + SD_FNC_AM_SDR50 + || card->info.sw_cur_setting.sd_access_mode == + SD_FNC_AM_DDR200) { + + if (bchg) + result = sd_tuning(card, &sd_cmd, 0); + if (!result) { + DbgErr("Tuning failed for thermal control.\n"); + goto exit; + } + } + } + + if (bchg == FALSE) { + DbgInfo(MODULE_SD_CARD, FEATURE_FUNC_THERMAL, NOT_TO_RAM, + "thermal switch pm heatup = %d\n", card->thermal_heat); + result = card_stop_infinite(card, TRUE, NULL); + if (result == FALSE) { + DbgErr("uhs2 Thermal Stop Infinite failed1\n"); + goto exit; + } + result = sd_switch_power_limit(card, &sd_cmd, &bchg); + } + + if (result == TRUE && bchg) + host_cmddat_line_reset(card->host); + +exit: + DbgInfo(MODULE_SD_CARD, FEATURE_FUNC_THERMAL, NOT_TO_RAM, "Exit %s\n", + __func__); + return result; + +} + +static u8 sd_adjust_tuning(sd_card_t *card, u32 input_n1, u32 output_n1) +{ + u8 result = TRUE; + sd_command_t sd_cmd; + sd_host_t *host = card->host; + + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Enter %s\n", + __func__); + + hostven_set_tuning_phase(host, input_n1, output_n1, FALSE); + + if (card->info.sw_cur_setting.sd_access_mode == SD_FNC_AM_SDR104 || + card->info.sw_cur_setting.sd_access_mode == SD_FNC_AM_SDR50 || + card->info.sw_cur_setting.sd_access_mode == SD_FNC_AM_DDR200) { + + result = sd_tuning(card, &sd_cmd, 150); + if (result == FALSE) { + DbgErr("sd adjust tuning: sd_tuning fail\n"); + result = FALSE; + goto exit; + } + } + +exit: + + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Exit %s\n", + __func__); + return result; +} + +static void sd_calc_max_passrange(u8 *pdata, u32 *ret, u32 *sum) +{ + u32 window_pass_number[22], window_start_adr[22], + window_pass_number_max; + int ii, jj, first_0, dll_i_mod, dll_i, dll_mod; + + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Enter %s\n", + __func__); + + for (ii = 0; ii < 22; ii++) { + window_pass_number[ii] = 0; + window_start_adr[ii] = 0; + } + + first_0 = 0; + window_pass_number_max = 0; + for (dll_i = 0; dll_i < 22; dll_i++) { + if (pdata[dll_i] == 0) { + first_0 = dll_i; + break; + } + } + jj = 0; + for (dll_i = 0; dll_i < 22; dll_i++) { + dll_i_mod = (first_0 + dll_i) % 22; + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "DLL phase [%x] result %d.\n", dll_i_mod, + pdata[dll_i_mod]); + if (pdata[dll_i_mod] != 0) + window_pass_number[jj]++; + else { + if (window_pass_number[jj] > 0) + jj++; + + } + if ((window_pass_number[jj] == 1) && (jj > 0)) { + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, + NOT_TO_RAM, "Error! there are %d DLL window\n", + (jj + 1)); + } + if (window_pass_number[jj] == 1) + window_start_adr[jj] = dll_i_mod; + } + + for (ii = 0; ii < 22; ii++) { + if (window_pass_number_max < window_pass_number[ii]) { + window_pass_number_max = window_pass_number[ii]; + jj = ii; + } + } + if (window_pass_number_max == 0) + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "DLL test result: All DLL test FAIL\n"); + else { + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "DLL test result: Total %d DLL test PASS\n", + window_pass_number_max); + window_pass_number_max = window_pass_number_max >> 1; + dll_mod = window_start_adr[jj] + window_pass_number_max; + dll_mod = dll_mod % 22; + + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "select DLL phase Number %d\n", dll_mod); + } + *ret = dll_mod; + *sum = window_pass_number_max; + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +/* The caller check */ +u8 testbuf_write[512]; +u8 testbuf_read[512]; + +bool sd_dll_divider(sd_card_t *card, sd_command_t *pcmd) +{ + + u32 ii, jj, pattern_i; + bool ret = FALSE, result = FALSE, datcmp; + + u32 window_pass_sum, dll_i, input_n1, output_n1, input_n, output_n, + DLL_input_Phase = 0, DLL_output_Phase = 0; + sd_command_t sd_cmd; + byte test_patern[6] = { 0x55, 0xaa, 0x00, 0xff, 0xf0, 0x0f }; + u32 cmdflag; + sd_host_t *host = card->host; + u8 phasecheck[22][22], phasepass[22]; + + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Enter %s\n", + __func__); + + host->output_tuning.start_block = pcmd->argument; + + /* If use read write, Save Current DMA mode */ + host_transfer_init(host, FALSE, TRUE); + cmdflag = CMD_FLG_RESCHK | CMD_FLG_R1 | CMD_FLG_ADMA_SDMA; + jj = 0; + host_cmddat_line_reset(host); + for (dll_i = 0; dll_i < 512; dll_i++) + testbuf_write[dll_i] = test_patern[dll_i % 6]; + if (card_check_rw_ready(card, &sd_cmd, 600) != TRUE) { + DbgErr + ("Error when sd dll divider, card_check_rw_ready fail\n"); + result = FALSE; + goto exit; + } + + if (hostven_dll_input_tuning_init(host) == FALSE) { + DbgErr + ("Error when sd dll divider, hostven_dll_input_tuning_init fail\n"); + result = FALSE; + goto exit; + } + + for (output_n1 = 0; output_n1 < 22; output_n1++) + for (input_n1 = 0; input_n1 < 22; input_n1++) { + + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, + NOT_TO_RAM, + " - DLL input tuning Test %d , %d\n", input_n1, + output_n1); + phasecheck[output_n1][input_n1] = 0; + phasepass[output_n1] = 0; + host_cmddat_line_reset(host); + + if (sd_adjust_tuning(card, input_n1, output_n1) == + FALSE) { + DbgErr + (" -adjust Output Tuning phase FAILED!!!\n"); + + continue; + } + for (pattern_i = 0; pattern_i < 1; pattern_i++) { + for (ii = 0; ii < (1 * 512); ii++) + (*(testbuf_read + ii)) = 0x96; + + ret = + card_send_sdcmd_timeout(card, &sd_cmd, + SD_CMD24, + host->output_tuning.start_block, + (cmdflag), + DATA_DIR_OUT, + testbuf_write, 512, + 50); + if (ret == FALSE) + break; + ret = + card_send_sdcmd_timeout(card, &sd_cmd, + SD_CMD17, + host->output_tuning.start_block, + (cmdflag), + DATA_DIR_IN, + testbuf_read, 512, + 50); + if (ret == FALSE) { + DbgErr + ("Read data FAILED when output_tuning\n"); + + break; + } + + datcmp = TRUE; + for (ii = 0; ii < (1 * 512); ii++) { + if (*(testbuf_write + ii) != + *(testbuf_read + ii)) { + datcmp = FALSE; + phasecheck[output_n1][input_n1] + = 0; + break; + } + } + if (datcmp == FALSE) { + DbgInfo(MODULE_SD_CARD, + FEATURE_ERROR_RECOVER, + NOT_TO_RAM, + "Compare data FAILED at index %d!!!\n", + ii); + } else { + DbgInfo(MODULE_SD_CARD, + FEATURE_ERROR_RECOVER, + NOT_TO_RAM, + "Compare data OK.\n"); + phasecheck[output_n1][input_n1] = 1; + } + } + } + + for (output_n = 0; output_n < 22; output_n++) { + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + " ## The output tuning %d: ", output_n); + for (input_n = 0; input_n < 22; input_n++) + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, + NOT_TO_RAM, " %d: %d ", input_n, + phasecheck[output_n][input_n]); + } + + for (output_n = 0; output_n < 22; output_n++) { + for (input_n = 0; input_n < 22; input_n++) + phasepass[output_n] += phasecheck[output_n][input_n]; + } + + /* check for the max pass range */ + sd_calc_max_passrange(phasepass, &DLL_output_Phase, &window_pass_sum); + sd_calc_max_passrange(phasecheck[DLL_output_Phase], &DLL_input_Phase, + &window_pass_sum); + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "The best input tuning phase is %d\n", DLL_input_Phase); + /* Get the optimized clock phase to do read/write test */ + hostven_set_tuning_phase(host, DLL_input_Phase, DLL_output_Phase, + FALSE); + result = TRUE; + +exit: + /* Resorte current DMA mode */ + host_transfer_init(host, card->inf_trans_enable, FALSE); + if (result == FALSE) + hostven_set_tuning_phase(host, 0, 0, TRUE); + host_cmddat_line_reset(host); + DbgInfo(MODULE_SD_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Exit %s\n", + __func__); + return result; +} + +/* + * + * Function Name: sd_read_csd + * + * Abstract: + * + * 1. De-select the card and send CMD9, and then select the card. + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * + * Output: + * + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: + */ +bool sd_read_csd(sd_card_t *card, sd_command_t *sd_cmd, byte *data) +{ + card_info_t *card_info = &(card->info); + bool ret = FALSE, ret1 = FALSE, ret2 = FALSE; + + ret = card_deselect_card(card, sd_cmd); + if (!ret) + goto exit_select_card; + + ret1 = card_get_csd(card, sd_cmd); + +exit_select_card: + ret2 = card_select_card(card, sd_cmd); + + if ((ret == FALSE) || (ret1 == FALSE) || (ret2 == FALSE)) + return FALSE; + else { + os_memcpy(data, &(card_info->raw_csd[0]), 0x10); + return TRUE; + } +} + +/* + * + * Function Name: sd_program_csd + * + * Abstract: + * + * 1. Program CSD by CMD27. + * + * Input: + * + * sd_card_t *card [in] [out]: Pointer to the virtual card structure + * sd_command_t *sd_cmd: Pointer to sd command structure + * + * Output: + * + * None + * + * Return value: + * + * Return TRUE if card init successfully, else return FALSE. + * + * Notes: + * + * Caller: + */ +bool sd_program_csd(sd_card_t *card, sd_command_t *sd_cmd, byte *data) +{ + + byte cmd_index = SD_CMD27; + u32 argument = 0; + u32 cmdflag = CMD_FLG_R1 | CMD_FLG_RESCHK; + e_data_dir dir = DATA_DIR_OUT; + u32 datalen = 0x10; + sd_host_t *host = card->host; + bool ret = FALSE; + + ret = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, dir, + data, datalen); + if (ret == FALSE) + DbgErr("CMD27 failed\n"); + + host_cmddat_line_reset(host); + + DbgInfo(MODULE_ALL_CARD, FEATURE_RW_TRACE, NOT_TO_RAM, + "Exit %s ret=%d\n", __func__, ret); + return ret; + +} diff --git a/drivers/scsi/bht/card/thermal.c b/drivers/scsi/bht/card/thermal.c new file mode 100644 index 000000000000..bda251476944 --- /dev/null +++ b/drivers/scsi/bht/card/thermal.c @@ -0,0 +1,348 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: thermal.c + * + * Abstract: This File is used to handle thread event + * + * Version: 1.00 + * + * Author: Peter.Guo + * + * Environment: OS Independent + * + * History: + * + * 11/05/2014 Creation Peter.Guo + */ + +#include "../include/basic.h" +#include "../include/card.h" +#include "../include/function.h" +#include "../include/cardapi.h" +#include "cardcommon.h" +#include "../include/hostapi.h" +#include "../include/util.h" +#include "../include/debug.h" + +/* + * Function Name: thermal_gpio_sensor + * Abstract: This Function is used to do get sensor result for thermal control + * + * Input: + * sd_host_t *host + * + * Return value: + * NORMAL + * COOL + * HOT + * + * Notes: + * run in thread context + */ + +static e_thermal_val thermal_gpio_sensor(sd_host_t *host) +{ + e_thermal_val result = THERMAL_NORMAL; + u32 value = 0; + + DbgInfo(MODULE_THERMAL, FEATURE_FUNC_THERMAL, NOT_TO_RAM, "Enter %s\n", + __func__); + + switch (host->chip_type) { + case CHIP_SEAEAGLE: + ven_and32(host, 0x22c, ~0x7); + ven_or32(host, 0x22c, 0x13); + value = ven_readl(host, 0x22c); + value = (value & 0x40) >> 6; + break; + case CHIP_SEAEAGLE2: + case CHIP_GG8: + case CHIP_ALBATROSS: + ven_and32(host, 0x50c, ~0x7); + ven_or32(host, 0x50c, 0x13); + value = ven_readl(host, 0x50c); + value = (value & 0x40) >> 6; + break; + default: + value = pci_readl(host, 0xD4); + value = (value & 0x80) >> 7; + break; + } + + result = (e_thermal_val) value; + + DbgInfo(MODULE_THERMAL, FEATURE_FUNC_THERMAL, NOT_TO_RAM, + "Exit %s ret=%d\n", __func__, value); + return result; + +} + +/* + * Function Name: thermal_i2c_sensor + * Abstract: This Function is used to do get sensor result for thermal control + * + * Input: + * sd_host_t *host + * + * Return value: + * NORMAL + * COOL + * HOT + * + * Notes: + * run in thread context + */ + +static e_thermal_val thermal_i2c_sensor(sd_host_t *host) +{ + u32 temp_val = 0, count = 0; + u32 upper_limit = 0, lower_limit = 0; + e_thermal_val result = THERMAL_NORMAL; + + DbgInfo(MODULE_THERMAL, FEATURE_FUNC_THERMAL, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* Enable I2C I/F */ + ven_and32(host, 0x228, ~0x7); + ven_or32(host, 0x228, 0x1); + ven_and32(host, 0x230, ~0x7); + ven_or32(host, 0x230, 0x1); + os_mdelay(1); + + /* Reset I2C function */ + ven_writel(host, 0x220, 0x80000000); + + os_mdelay(1); + + /* Set FLTR and One short bit */ + ven_writel(host, 0x220, 0x440000); + ven_writel(host, 0x220, 0x20009401); + + while ((ven_readl(host, 0x220) & 0x20000000)) { + /* Timeout 5ms */ + if (count == 5) { + DbgErr(" - Wait I2C write operation timeout!\n"); + break; + } + /* else if (device_status == DEVICE_STATUS_CHIPLOST) */ + else if (ven_readl(host, 0x220) == 0xffffffff) { + DbgErr("break loop because chip lost!\n"); + break; + } + + os_mdelay(1); + count += 1; + } + + /* Read the temperature value */ + ven_writel(host, 0x220, 0x50009400); + + while ((ven_readl(host, 0x220) & 0x10000000)) { + /* Timeout 5ms */ + if (count == 5) { + DbgErr(" - Wait I2C read operation timeout!\n"); + break; + } + /* else if (device_status == DEVICE_STATUS_CHIPLOST) */ + else if (ven_readl(host, 0x220) == 0xffffffff) { + DbgErr("break loop because chip lost!!\n"); + break; + } + os_mdelay(1); + count += 1; + } + + temp_val = (ven_readl(host, 0x224) & 0xffff) >> 6; + /* upper_limit = (tmp_high & 0xffff0000) >> 16; */ + /* lower_limit = tmp_low & 0xffff; */ + + if ((temp_val & 0x200) && (upper_limit & 0x8000)) { + if ((temp_val & 0x1ff) < ((upper_limit & 0x1ff) << 2)) { + result = THERMAL_HOT; + goto exit; + } + } + + if ((0 == (temp_val & 0x200)) && (upper_limit & 0x8000)) { + result = THERMAL_HOT; + goto exit; + } + + if ((0 == (temp_val & 0x200)) && (0 == (upper_limit & 0x8000))) { + if ((temp_val & 0x1ff) > ((upper_limit & 0x1ff) << 2)) { + result = THERMAL_HOT; + goto exit; + } + } + + if ((temp_val & 0x200) && (lower_limit & 0x8000)) { + if ((temp_val & 0x1ff) > ((lower_limit & 0x1ff) << 2)) { + result = THERMAL_COOL; + goto exit; + } + } + + if ((temp_val & 0x200) && (0 == (lower_limit & 0x8000))) { + result = THERMAL_COOL; + goto exit; + } + + if ((0 == (temp_val & 0x200)) && (0 == (lower_limit & 0x8000))) { + if ((temp_val & 0x1ff) < ((lower_limit & 0x1ff) << 2)) { + result = THERMAL_COOL; + goto exit; + } + } + +exit: + DbgInfo(MODULE_THERMAL, FEATURE_FUNC_THERMAL, NOT_TO_RAM, + "Exit %s result=%d\n", __func__, result); + return result; + +} + +/* + * Function Name: func_thermal_control + * Abstract: This Function is used to do thremal control + * This function should be called before send card Read Write + * + * Input: + * sd_card_t *card + * + * Return value: + * TRUE: means ok + * others means error occur, caller need do error recovery + * + * Notes: + * run in thread context + */ + +bool func_thermal_control(sd_card_t *card) +{ + sd_host_t *host = card->host; + bht_dev_ext_t *pdx = host->pdx; + bool result = TRUE; + e_thermal_val thermal = THERMAL_NORMAL; + + DbgInfo(MODULE_THERMAL, FEATURE_FUNC_THERMAL, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* If Thermal Control is disabled then do nothing */ + if (pdx->thermal.enable == 0) + goto exit; + + /* If Thermal timeout is not occur then do nothing */ + if (pdx->thermal.enable_timer_chk == 1 && pdx->thermal.timeout == 0) + goto exit; + + pdx->thermal.timeout = 0; + pdx->thermal.last_check_ms = os_get_cur_tick(); + + /* If card not working do nothing */ + if (card->state != CARD_STATE_WORKING || card->card_present == FALSE) + goto exit; + + if (card->initialized_once == FALSE) + goto exit; + + /* Currently only uhs2 and SD support thermal control */ + switch (card->card_type) { + case CARD_SD: + case CARD_UHS2: + break; + default: + goto exit; + } + + if (pdx->thermal.use_i2c) + thermal = thermal_i2c_sensor(host); + else + thermal = thermal_gpio_sensor(host); + + DbgInfo(MODULE_THERMAL, FEATURE_FUNC_THERMAL, NOT_TO_RAM, + "Start do Thermal control sensor=%d\n", thermal); + + if (thermal == THERMAL_NORMAL) { + /* nothing to do for no thermal change */ + goto exit; + } else if (thermal == THERMAL_COOL) { + /* NEED change to higher mode */ + pdx->card.thermal_enable = 1; + pdx->card.thermal_heat = 1; + result = card_thermal_control(card); + } else { + /* change to lower mode */ + pdx->card.thermal_enable = 1; + pdx->card.thermal_heat = 0; + result = card_thermal_control(card); + } + + pdx->card.thermal_enable = 0; + +exit: + DbgInfo(MODULE_THERMAL, FEATURE_FUNC_THERMAL, NOT_TO_RAM, + "Exit %s ret=%d\n", __func__, result); + return result; +} + +/* + * Function Name: func_thermal_control + * Abstract: This Function is used to update thermal control time + * This function should be called before send card Read Write + * + * Input: + * sd_card_t *card + * + * Return value: + * TRUE: means ok + * others means error occur, caller need do error recovery + * + * Notes: + */ + +void func_thermal_update_time(bht_dev_ext_t *pdx) +{ + /* If time check for thermal contorl is not enable do nothing */ + if (pdx->thermal.enable == 0 || pdx->thermal.enable_timer_chk == 0) + return; + + if (pdx->card.state != CARD_STATE_WORKING + || pdx->card.card_present == FALSE) + return; + + if (pdx->card.initialized_once == FALSE) + return; + + if (pdx->thermal.timeout == 0) { + pdx->thermal.timeout = + (os_get_cur_tick() > + (pdx->thermal.last_check_ms + + pdx->thermal.check_period_ms)) ? 1 : 0; + } + +} + +void thermal_init(bht_dev_ext_t *pdx) +{ + pdx->thermal.enable = 0; + if (pdx->thermal.enable == 0) + return; + pdx->thermal.use_i2c = 0; + pdx->thermal.last_check_ms = os_get_cur_tick(); + pdx->thermal.enable_timer_chk = 0; + pdx->thermal.check_period_ms = 0; + DbgInfo(MODULE_THERMAL, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "thermal enable=%d i2c=%d timechk=%dms chkperiod=%dms\n", + pdx->thermal.enable, pdx->thermal.use_i2c, + pdx->thermal.enable_timer_chk, pdx->thermal.check_period_ms); + +} + +void thermal_uninit(bht_dev_ext_t *pdx) +{ + if (pdx->thermal.enable == 0) + return; + pdx->thermal.last_check_ms = os_get_cur_tick(); +} diff --git a/drivers/scsi/bht/card/uhs2.c b/drivers/scsi/bht/card/uhs2.c new file mode 100644 index 000000000000..6f9511b5d33d --- /dev/null +++ b/drivers/scsi/bht/card/uhs2.c @@ -0,0 +1,1228 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: uhs2.c + * + * Abstract: SD UHS2 card initialization + * + * Version: 1.00 + * + * Author: peter.guo + * + * Environment: OS Independent + * + * History: + * + * 10/10/2014 Creation Peter.Guo + */ + +#include "../include/basic.h" +#include "../include/cmdhandler.h" +#include "../include/cardapi.h" +#include "../include/hostapi.h" +#include "cardcommon.h" +#include "../include/util.h" +#include "../include/debug.h" + +#define UHS2_DEVINIT_CF 0x00000800 +#define UHS2_DEVINIT_GAP 0x0000000F +#define UHS2_ENUM_PLD 0x00000000 +#define UHS2_GODRM_HBNEN 0x00000080 +#define UHS2_ENMR_IDF 0x000000F0 +#define UHS2_ENMR_IDL 0x0000000F + +#define UHS2_LANES_2L_HD 0x00 +#define UHS2_LANES_2D1UFD 0x02 +#define UHS2_LANES_1D2UFD 0x03 +#define UHS2_LANES_2D2UFD 0x04 + +#define UHS2_UNRECOVER_ERROR (BIT0 | BIT2 | BIT7) + +static inline bool uhs2_is_uncoverable(sd_command_t *sd_cmd) +{ + if ((sd_cmd->err.error_code == ERR_CODE_TIMEOUT) || + (sd_cmd->err.uhs2_err_reg & UHS2_UNRECOVER_ERROR)) + return TRUE; + else + return FALSE; +} + +/* + * Function Name: uhs2_access_reg + * + * Abstract: This Function is used to send uhs2 ccmd + * + * Input: + * sd_card_t *card : The Command will send to which Card + * sd_command_t *sd_cmd: This parameter will contail card command info + * byte ioaddr: ioaddr for uhs2 ccmd + * bool broadcast: use broadcast or not + * bool rwcmd: Set RW flag in uhs2 header or not + * byte payload_num: payload count + * + * Input & Output: + * u32 *payload: contain the register want to setting, + * and store return regs value + * + * Return value: + * If the routine succeeds, it must return TRUE, and fill trans_reg_t part. + * otherwize reutrn FALSE + * + * Notes: + * so giving the routine another name requires you to modify the build tools. + */ + +static bool uhs2_native_ccmd_internal(sd_card_t *card, sd_command_t *sd_cmd, + u16 ioaddr, bool broadcast, bool rwcmd, + byte payload_num, u32 *payload) +{ + bool result = FALSE; + u32 headarg = UHS2_CMD_HEADER_NP; + int i; + + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARDCMD_TRACE, NOT_TO_RAM, + "Enter %s ioaddr=0x%04X\n", __func__, ioaddr); + + /* step1 prepare header for uhs2 ccmd */ + headarg |= UHS2_NATIVE_CCMD_IOADDR(ioaddr); + + if (broadcast == FALSE) + headarg |= UHS2_HEADER_DID(card->uhs2_info.dev_id); + + if (rwcmd) + headarg |= UHS2_NATIVE_HEADER_RW; + + switch (payload_num) { + case 0: + break; + case 1: + headarg |= UHS2_NATIVE_CCMD_PLEN4; + break; + case 2: + headarg |= UHS2_NATIVE_CCMD_PLEN8; + break; + case 4: + headarg |= UHS2_NATIVE_CCMD_PLEN16; + break; + default: + DbgErr("uhs2 ccmd payload number is wrong\n"); + goto exit; + } + + sd_cmd->uhs2_header = headarg; + if (rwcmd) + sd_cmd->uhs2_set_pld = 1; + + /* step 2 set payload */ + sd_cmd->payload_cnt = payload_num; + for (i = 1; i <= payload_num; i++) + sd_cmd->trans_reg[0].payload[i] = payload[i - 1]; + + result = cmd_generate_reg(card, sd_cmd); + if (result == FALSE) + goto exit; + + result = cmd_execute_sync(card, sd_cmd, NULL); +exit: + if (result == FALSE) + DbgErr("UHS2 Native cmd failed ioaddr=0x%02X errcode=0x%08X\n", + UHS2_GET_NATIVE_IOADDR(sd_cmd->uhs2_header), + sd_cmd->err.error_code); + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARDCMD_TRACE, NOT_TO_RAM, + "Exit %s result=%d\n", __func__, result); + return result; +} + +bool uhs2_native_ccmd(sd_card_t *card, sd_command_t *sd_cmd, + u16 ioaddr, bool broadcast, bool rwcmd, byte payload_num, + u32 *payload) +{ + os_memset(sd_cmd, 0, sizeof(sd_command_t)); + return uhs2_native_ccmd_internal(card, sd_cmd, ioaddr, broadcast, rwcmd, + payload_num, payload); +} + +/* + * Function Name: uhs2_access_reg + * + * Abstract: This Function is used to read or inquiry or set uhs2 card registers + * + * Input: + * sd_card_t *card : The Command will send to which Card + * sd_command_t *sd_cmd: This parameter will contail card command info + * byte ioaddr: reg addr + * bool broadcast: use broadcast or not + * bool setcfg: set reg or read reg + * byte payload_num: reg count + * + * Input & Output: + * u32 *payload: contain the register want to setting, and store return regs value + * + * Return value: + * If the routine succeeds, it must return TRUE, and fill trans_reg_t part. + * otherwize reutrn FALSE + * + * Notes: + * so giving the routine another name requires you to modify the build tools. + */ +static bool uhs2_access_reg(sd_card_t *card, sd_command_t *sd_cmd, + u16 ioaddr, bool broadcast, bool setcfg, + byte payload_num, u32 *payload) +{ + u32 i; + bool result = FALSE; + + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARDCMD_TRACE, NOT_TO_RAM, + "Enter %s ioaddr=0x%04X\n", __func__, ioaddr); + os_memset(sd_cmd, 0, sizeof(sd_command_t)); + if (payload_num > 4) { + DbgErr("payload_num is large than 4\n"); + goto exit; + } + + /* set reg case and broadcast inquiry need input value */ + if (setcfg || broadcast) { + for (i = 0; i < payload_num; i++) + payload[i] = swapu32(payload[i]); + sd_cmd->uhs2_set_pld = 1; + } else { + for (i = 0; i < payload_num; i++) + payload[i] = 0; + } + + result = + uhs2_native_ccmd_internal(card, sd_cmd, ioaddr, broadcast, setcfg, + payload_num, payload); + if (result == FALSE) { + DbgErr + ("uhs2 access reg failed ioaddr=0x%02X broadcast=%d setcfg=%d\n", + ioaddr, broadcast, setcfg); + goto exit; + } + + /* set reg case don't need get register value */ + if (setcfg == 0) { + for (i = 0; i < payload_num; i++) + payload[i] = swapu32(sd_cmd->response[i]); + } + +exit: + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARDCMD_TRACE, NOT_TO_RAM, + "Exit %s result=%d\n", __func__, result); + return result; +} + +/* + * Function Name: uhs2_send_fullreset + * + * Abstract: This Function is used init Send UHS2 full reset ccmd + * + * Input: + * sd_card_t *card : The Command will send to which Card + * sd_command_t *sd_cmd, + * + * Return value: + * If the routine succeeds, it must return TRUE, and fill trans_reg_t part. + * otherwize reutrn FALSE + */ +bool uhs2_send_fullreset(sd_card_t *card, sd_command_t *sd_cmd) +{ + bool result = FALSE; + u32 payload = 0; + + DbgInfo(MODULE_UHS2_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Enter %s\n", __func__); + + result = + uhs2_native_ccmd(card, sd_cmd, UHS2_IOADDR_FULLRESET, TRUE, TRUE, 0, + &payload); + + if (result == FALSE) + DbgErr("uhs2 fullreset failed\n"); + + DbgInfo(MODULE_UHS2_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Exit %s\n", __func__); + return result; +} + +/* + * Function Name: uhs2_trans_abort + * + * Abstract: This Function is used init Send UHS2 transfer abort command + * + * Input: + * sd_card_t *card : The Command will send to which Card + * sd_command_t *sd_cmd, + * + * Return value: + * If the routine succeeds, it must return TRUE, and fill trans_reg_t part. + * otherwize reutrn FALSE + */ +bool uhs2_trans_abort(sd_card_t *card, sd_command_t *sd_cmd) +{ + bool result = FALSE; + u32 payload = 0; + + DbgInfo(MODULE_UHS2_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Enter %s\n", __func__); + + result = + uhs2_native_ccmd(card, sd_cmd, UHS2_IOADDR_ABORT, FALSE, TRUE, 0, + &payload); + + if (result == FALSE) + DbgErr("uhs2 trans_abort failed\n"); + + DbgInfo(MODULE_UHS2_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Exit %s ret=%d\n", __func__, result); + return result; +} + +/* + * Function Name: uhs2_full_reset_card + * + * Abstract: This Function is used to send full reset command, if failed do host reset for all + * + * Input: + * sd_card_t *card : The Command will send to which Card + * + * Return value: + * If the routine succeeds, it must return TRUE, and fill trans_reg_t part. + * otherwize reutrn FALSE + */ + +bool uhs2_full_reset_card(sd_card_t *card) +{ + sd_command_t sd_cmd; + bool result = FALSE; + + DbgInfo(MODULE_UHS2_CARD, FEATURE_ERROR_RECOVER | FEATURE_CARD_OPS, + NOT_TO_RAM, "Enter %s\n", __func__); + + result = uhs2_send_fullreset(card, &sd_cmd); + DbgInfo(MODULE_UHS2_CARD, FEATURE_ERROR_RECOVER | FEATURE_CARD_OPS, + NOT_TO_RAM, "uhs2 full rest ret=%d\n", result); + if (result) { + os_udelay(200); + host_uhs2_reset(card->host, TRUE); + } else { + /* failed do reset for all */ + host_uhs2_clear(card->host, TRUE); + } + DbgInfo(MODULE_UHS2_CARD, FEATURE_ERROR_RECOVER | FEATURE_CARD_OPS, + NOT_TO_RAM, "Exit %s ret=%d\n", __func__, result); + + return result; +} + +/* + * Function Name: uhs2_send_devinit + * + * Abstract: This Function is used init send uhs2 dev_enum ccmd and get card deviceid + * + * Input: + * sd_card_t *card : The Command will send to which Card + * sd_command_t *sd_cmd, + * + * Return value: + * If the routine succeeds, it must return TRUE, and fill trans_reg_t part. + * otherwize reutrn FALSE + */ + +bool uhs2_dev_enumeration(sd_card_t *card, sd_command_t *sd_cmd) +{ + bool result = FALSE; + u32 payload = 0; + u8 firstid, lastid; + u32 resp; + u8 devcnt = 0; + + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + firstid = lastid = 0; + payload = UHS2_ENUM_PLD; + + result = + uhs2_native_ccmd(card, sd_cmd, UHS2_IOADDR_ENUM, TRUE, TRUE, 1, + &payload); + if (result == FALSE) + goto exit; + + resp = sd_cmd->response[0]; + firstid = (u8) ((resp & UHS2_ENMR_IDF) >> 4); + lastid = (u8) (resp & UHS2_ENMR_IDL); + + if (firstid > lastid) + devcnt = (lastid + 0x10) - firstid; + else + devcnt = lastid - firstid + 1; + + card->uhs2_info.dev_id = firstid; + + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARD_INIT, TO_RAM, + "firstid=0x%02X lastid=0x%02X\n", firstid, lastid); + +exit: + if (result == FALSE) + DbgErr("uhs2 enumeration failed\n"); + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Exit %s ret=%d\n", __func__, result); + return result; + +} + +/* + * Function Name: uhs2_send_devinit + * + * Abstract: This Function is used init send uhs2 dev_init cmd + * + * Input: + * sd_card_t *card : The Command will send to which Card + * sd_command_t *sd_cmd, + * u8 gap, + * u8 dap, + * + * Iput & Output: + * u8 *gd, + * u8 *cf + * + * Return value: + * If the routine succeeds, it must return TRUE, and fill trans_reg_t part. + * otherwize reutrn FALSE + */ + +static bool uhs2_send_devinit(sd_card_t *card, sd_command_t *sd_cmd, u8 *gd, + u8 gap, u8 dap, u8 *cf) +{ + bool result = FALSE; + u32 payload; + u32 resp; + + payload = + UHS2_DEVINIT_CF | (((*gd) & 0xf) << 4) | (gap & 0xf) | ((dap & 0xf) + << 12); + result = + uhs2_native_ccmd(card, sd_cmd, UHS2_IOADDR_DEVINIT, TRUE, TRUE, 1, + &payload); + if (result == FALSE) + goto exit; + + resp = sd_cmd->response[0]; + if (resp & UHS2_DEVINIT_CF) + *cf = 1; + else + *cf = 0; + + if (gap == (resp & UHS2_DEVINIT_GAP)) + (*gd)++; + +exit: + return result; +} + +/* + * Function Name: uhs2_devinit_flow + * + * Abstract: This Function is used do device_init flow + * + * Input: + * sd_card_t *card : The Command will send to which Card + * sd_command_t *sd_cmd, + * sd_host_t *host + * + * Return value: + * If the routine succeeds, it must return TRUE, and fill trans_reg_t part. + * otherwize reutrn FALSE + */ +static bool uhs2_devinit_flow(sd_card_t *card, sd_command_t *sd_cmd, + sd_host_t *host) +{ + bool result = FALSE; + u8 gd, dap, gap, cf; + + /* max 1200ms delay */ + u32 timeout = 1200; + loop_wait_t wait; + u32 delay_us = 20; + + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + cf = 0; + gd = 0; + dap = host->uhs2_cap.dap; + gap = host->uhs2_cap.gap; + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "devinit dap=0x%02X gap=0x%02X\n", dap, gap); + + util_init_waitloop(card->host->pdx, timeout, delay_us, &wait); + + do { + result = uhs2_send_devinit(card, sd_cmd, &gd, gap, dap, &cf); + os_udelay(delay_us); + + if (result == FALSE) { + DbgErr("Device Init cmd error\n"); + goto exit; + } + } while ((util_is_timeout(&wait) == FALSE) && (cf == 0)); + + if (cf == 0) + result = FALSE; + +exit: + if (result == FALSE) + DbgErr("host:%p devinit failed cf=%d\n", host, cf); + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + return result; + +} + +/* + * Function Name: uhs2_send_devinit + * + * Abstract: This Function is used init send uhs2 dev_enum ccmd and get card deviceid + * + * Input: + * sd_card_t *card : The Command will send to which Card + * sd_command_t *sd_cmd, + * + * Return value: + * If the routine succeeds, it must return TRUE, and fill trans_reg_t part. + * otherwize reutrn FALSE + */ + +static bool uhs2_card_get_caps(sd_card_t *card, sd_command_t *sd_cmd, + sd_host_t *host) +{ + u32 payload[2]; + bool result = FALSE; + + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* + * Get phy capbality for both host and card support + * 1. Hibernate; 2. Lss_Dir; 3. Lss_Syn + */ + os_memset(payload, 0, sizeof(payload)); + result = + uhs2_access_reg(card, sd_cmd, UHS2_IOADDR_PHY_CAPL, FALSE, FALSE, 2, + payload); + if (result == FALSE) { + DbgErr("Inquiry card phy cap failed\n"); + goto exit; + } + + card->uhs2_info.uhs2_cap.hibernate = (payload[0] & BIT15) ? 1 : 0; + card->uhs2_info.uhs2_cap.n_lss_dir = ((payload[1] & 0xF0) >> 4); + card->uhs2_info.uhs2_cap.n_lss_syn = ((payload[1] & 0x0F)); + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "card hbr=%d lssdir=%d lsssyn=%d\n", + card->uhs2_info.uhs2_cap.hibernate, + card->uhs2_info.uhs2_cap.n_lss_dir, + card->uhs2_info.uhs2_cap.n_lss_syn); + + /* + * Get Link/Tran capbality for both host and card support + * 1. nfcu; 2. datagap; 3. max block length + */ + os_memset(payload, 0, sizeof(payload)); + result = + uhs2_access_reg(card, sd_cmd, UHS2_IOADDR_LINKT_CAPL, FALSE, FALSE, + 2, payload); + if (result == FALSE) { + DbgErr("Inquiry card link cap failed\n"); + goto exit; + } + + card->uhs2_info.uhs2_cap.n_fcu = ((payload[0] & 0xFF00) >> 8); + card->uhs2_info.uhs2_cap.n_data_gap = ((payload[1] & 0x00FF)); + card->uhs2_info.uhs2_cap.max_blk_len = + ((payload[0] & 0xFFF00000) >> 20); + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "card nfcu=%d datagap=%d blklen=%d\n", + card->uhs2_info.uhs2_cap.n_fcu, + card->uhs2_info.uhs2_cap.n_data_gap, + card->uhs2_info.uhs2_cap.max_blk_len); + + /* Below capabliies only decide host */ + card->uhs2_info.uhs2_cap.speed_range = host->uhs2_cap.speed_range; + /* default we use Fast Mode */ + + /* card support low power mode */ + card->uhs2_info.uhs2_cap.pwr_mode = 1; + card->uhs2_info.uhs2_cap.retry_cnt = host->uhs2_cap.retry_cnt; + + os_memset(payload, 0, sizeof(payload)); + result = + uhs2_access_reg(card, sd_cmd, UHS2_IOADDR_GEN_CAPL, FALSE, FALSE, 1, + payload); + if (result == FALSE) { + DbgErr("Inquiry card gen cap failed\n"); + goto exit; + } + + card->uhs2_info.uhs2_cap.half_supp = (payload[0] & BIT8) ? TRUE : FALSE; + card->uhs2_info.uhs2_cap.lanes = ((payload[0] & 0x0E00) >> 8); + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "card halfsupp=%d lanes=%d\n", + card->uhs2_info.uhs2_cap.half_supp, + card->uhs2_info.uhs2_cap.lanes); + +exit: + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Exit %s result=%d\n", __func__, result); + return result; + +} + +static byte uhs2_get_large_lss(u32 val1, u32 val2) +{ + u32 v1, v2; + u32 v = 0; + + v1 = val1; + v2 = val2; + + if (v1 == 0) + v1 = 16; + if (v2 == 0) + v2 = 16; + + v = os_max(v1, v2); + if (v == 16) + v = 0; + + return (byte) v; +} + +/* + * Function Name: uhs2_get_card_setting_host + * + * Abstract: This Function is used generate uhs2 card setting by card caps and host caps + * + * Input: + * sd_card_t *card : + * sd_host_t *host + * + */ +static void uhs2_get_card_setting_host(sd_card_t *card, sd_host_t *host) +{ + u16 nfcu1, nfcu2; + byte lanes; + + card->uhs2_info.uhs2_setting.hibernate = + card->uhs2_info.uhs2_cap.hibernate; + card->uhs2_info.uhs2_setting.n_lss_dir = + uhs2_get_large_lss(card->uhs2_info.uhs2_cap.n_lss_dir, + host->uhs2_cap.n_lss_dir); + card->uhs2_info.uhs2_setting.n_lss_syn = + uhs2_get_large_lss(card->uhs2_info.uhs2_cap.n_lss_syn, + host->uhs2_cap.n_lss_syn); + + card->uhs2_info.uhs2_setting.n_data_gap = + os_max(card->uhs2_info.uhs2_cap.n_data_gap, + host->uhs2_cap.n_data_gap); + card->uhs2_info.uhs2_setting.max_blk_len = 0x200; + + nfcu1 = card->uhs2_info.uhs2_cap.n_fcu; + nfcu2 = host->uhs2_cap.n_fcu; + if (nfcu1 == 0) + nfcu1 = 256; + if (nfcu2 == 0) + nfcu2 = 256; + card->uhs2_info.uhs2_setting.n_fcu = + (nfcu1 > + nfcu2) ? host->uhs2_cap.n_fcu : card->uhs2_info.uhs2_cap.n_fcu; + + card->uhs2_info.uhs2_setting.speed_range = + card->uhs2_info.uhs2_cap.speed_range; + card->uhs2_info.uhs2_setting.pwr_mode = 0; + card->uhs2_info.uhs2_setting.retry_cnt = host->uhs2_cap.retry_cnt; + + card->uhs2_info.uhs2_setting.half_supp = + os_min(card->uhs2_info.uhs2_cap.half_supp, + (host->uhs2_cap.num_of_lane & 0x1)); + card->uhs2_info.uhs2_setting.lanes = + card->uhs2_info.uhs2_cap.lanes & host->uhs2_cap.num_of_lane; + + lanes = card->uhs2_info.uhs2_setting.lanes; + + if (lanes & UHS2_LANES_2D2UFD) + lanes = UHS2_LANES_2D2UFD; + else if (lanes & UHS2_LANES_1D2UFD) + lanes = UHS2_LANES_1D2UFD; + else if (lanes & UHS2_LANES_2D1UFD) + lanes = UHS2_LANES_2D1UFD; + else + lanes = UHS2_LANES_2L_HD; + card->uhs2_info.uhs2_setting.lanes = lanes; +} + +/* + * Function Name: uhs2_get_card_setting_host + * + * Abstract: This Function is used generate uhs2 card setting by vendor setting + * + * Input: + * sd_card_t *card : + * sd_host_t *host + * + */ +static void uhs2_update_card_setting_vendor(sd_card_t *card, sd_host_t *host) +{ + u16 nfcu1, nfcu2; + cfg_uhs2_setting_t *cfg = &host->cfg->card_item.uhs2_setting; + + card->uhs2_info.uhs2_setting.n_lss_dir = + uhs2_get_large_lss(card->uhs2_info.uhs2_setting.n_lss_dir, + cfg->min_lss_dir); + card->uhs2_info.uhs2_setting.n_lss_syn = + uhs2_get_large_lss(card->uhs2_info.uhs2_setting.n_lss_syn, + cfg->min_lss_syn); + + card->uhs2_info.uhs2_setting.n_data_gap = + os_max(card->uhs2_info.uhs2_setting.n_data_gap, + cfg->min_data_gap_sel); + + nfcu1 = (u16) card->uhs2_info.uhs2_setting.n_fcu; + nfcu2 = (u16) cfg->max_nfcn_sel; + if (nfcu1 == 0) + nfcu1 = 256; + if (nfcu2 == 0) + nfcu2 = 256; + card->uhs2_info.uhs2_setting.n_fcu = + (nfcu1 > + nfcu2) ? cfg->max_nfcn_sel : card->uhs2_info.uhs2_setting.n_fcu; + + card->uhs2_info.uhs2_setting.speed_range = + os_min(card->uhs2_info.uhs2_setting.speed_range, + cfg->max_speed_range_sel); + card->uhs2_info.uhs2_setting.pwr_mode = (byte) cfg->fast_low_pwr_sel; + + card->uhs2_info.uhs2_setting.half_supp = + os_min(card->uhs2_info.uhs2_cap.half_supp, cfg->half_full_sel); + +} + +/* + * Function Name: uhs2_update_card_setting_degrade + * + * Abstract: This Function is used generate uhs2 card setting by degrade info + * + * Input: + * sd_card_t *card : + * sd_host_t *host + * + */ +static void uhs2_update_card_setting_degrade(sd_card_t *card) +{ + if (card->degrade_uhs2_range) + card->uhs2_info.uhs2_setting.speed_range = 0; +} + +/* + * Function Name: uhs2_update_card_setting_thermal + * + * Abstract: This Function is used generate uhs2 card setting by degrade info + * + * Input: + * sd_card_t *card : + * sd_host_t *host + * + */ +static void uhs2_update_card_setting_thermal(sd_card_t *card) +{ + + if (card->thermal_enable == 0) + return; +} + +/* + * Function Name: uhs2_cfg_set_card + * + * Abstract: This Function is used to set card's configuration + * + * Input: + * sd_card_t *card : The Command will send to which Card + * sd_command_t *sd_cmd, + * + * Return value: + * If the routine succeeds, it must return TRUE, and fill trans_reg_t part. + * otherwize reutrn FALSE + */ +static bool uhs2_cfg_set_card(sd_card_t *card, sd_command_t *sd_cmd, + sd_host_t *host) +{ + u32 payload[2]; + bool result = FALSE; + + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* Set device Phy Setting register */ + os_memset(payload, 0, sizeof(payload)); + payload[0] = (card->uhs2_info.uhs2_setting.speed_range << 6); + payload[1] = (card->uhs2_info.uhs2_setting.n_lss_syn) | + (card->uhs2_info.uhs2_setting.n_lss_dir << 4); + result = + uhs2_access_reg(card, sd_cmd, UHS2_IOADDR_PHY_SETL, FALSE, TRUE, 2, + payload); + if (result == FALSE) { + DbgErr("set uhs2 cfg phy setting failed ret\n"); + goto exit; + } + + /* Set device Link and trans registers */ + os_memset(payload, 0, sizeof(payload)); + payload[0] = (card->uhs2_info.uhs2_setting.n_fcu << 8) | + (card->uhs2_info.uhs2_setting.retry_cnt << 16) | + (card->uhs2_info.uhs2_setting.max_blk_len << 20); + payload[1] = (card->uhs2_info.uhs2_setting.n_data_gap); + result = + uhs2_access_reg(card, sd_cmd, UHS2_IOADDR_LINKT_SETL, FALSE, TRUE, + 2, payload); + if (result == FALSE) { + DbgErr("set uhs2 cfg linktran setting failed\n"); + goto exit; + } + + /* Set device general setting registers */ + os_memset(payload, 0, sizeof(payload)); + payload[0] = (card->uhs2_info.uhs2_setting.pwr_mode) | + (card->uhs2_info.uhs2_setting.lanes << 8); + result = + uhs2_access_reg(card, sd_cmd, UHS2_IOADDR_GEN_SETL, FALSE, TRUE, 1, + payload); + if (result == FALSE) { + DbgErr("set uhs2 cfg gen setting failed ret\n"); + goto exit; + } + + /* Set device to active status */ + os_memset(payload, 0, sizeof(payload)); + /* Set config complete */ + payload[0] = BIT31; + result = + uhs2_access_reg(card, sd_cmd, UHS2_IOADDR_GEN_SETH, FALSE, TRUE, 1, + payload); + if (result == FALSE) { + DbgErr("set uhs2 cfg set active failed\n"); + return result; + } + +exit: + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Exit %s ret=%d\n", __func__, result); + return result; +} + +/* + * Function Name: uhs2_enter_dmt + * + * Abstract: This Function is used to + * + * Input: + * sd_card_t *card : The Command will send to which Card + * + * Return value: + * If the routine succeeds, it must return TRUE, and fill trans_reg_t part. + * otherwize reutrn FALSE + */ +bool uhs2_enter_dmt(sd_card_t *card, sd_command_t *sd_cmd, sd_host_t *host, + bool hbr) +{ + bool result = FALSE; + u32 payload = hbr ? UHS2_GODRM_HBNEN : 0; + byte retry_cnt = 2; + + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARD_INIT | FEATURE_CARD_OPS, + NOT_TO_RAM, "Enter %s\n", __func__); +retry: + retry_cnt--; + result = + uhs2_native_ccmd(card, sd_cmd, UHS2_IOADDR_GODMT, FALSE, TRUE, 1, + &payload); + if (result == FALSE) { + if (retry_cnt > 0) { + if (uhs2_is_uncoverable(sd_cmd)) + goto exit; + + result = uhs2_trans_abort(card, sd_cmd); + if (result == FALSE) + goto exit; + goto retry; + } + goto exit; + } + result = host_uhs2_go_dmt(host, hbr); + +exit: + if (result == FALSE) + DbgErr("UHS2 go dmt failed hbr=%d\n", hbr); + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARD_INIT | FEATURE_CARD_OPS, + NOT_TO_RAM, "Exit %s ret=%d\n", __func__, result); + return result; +} + +/* + * Function Name: uhs2_resume_dmt + * Abstract: This Function is used to + * + * Input: + * sd_card_t *card : The Command will send to which Card + * + * Return value: + * If the routine succeeds, it must return TRUE, and fill trans_reg_t part. + * otherwize reutrn FALSE + */ +bool uhs2_resume_dmt(sd_card_t *card, sd_command_t *sd_cmd, sd_host_t *host, + bool hbr) +{ + return host_uhs2_resume_dmt(host, hbr); +} + +/* + * Function Name: uhs2_card_configuration + * + * Abstract: This Function is used to + * + * Input: + * sd_card_t *card : The Command will send to which Card + * + * Return value: + * If the routine succeeds, it must return TRUE, and fill trans_reg_t part. + * otherwize reutrn FALSE + */ +bool uhs2_card_configuration(sd_card_t *card, sd_command_t *sd_cmd, + sd_host_t *host) +{ + bool result = FALSE; + uhs2_info_t info; + + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + result = uhs2_cfg_set_card(card, sd_cmd, host); + if (result == FALSE) + goto exit; + + os_memcpy(&info, &card->uhs2_info.uhs2_setting, sizeof(uhs2_info_t)); + info.speed_range = 0; + info.lanes = 0; + + host_uhs2_cfg_set(host, &info, FALSE); + + if (card->uhs2_info.uhs2_setting.lanes == 0 + && card->uhs2_info.uhs2_setting.speed_range == 0) + goto exit; + + result = uhs2_enter_dmt(card, sd_cmd, host, FALSE); + if (result == FALSE) + goto exit; + + info.speed_range = card->uhs2_info.uhs2_setting.speed_range; + info.lanes = card->uhs2_info.uhs2_setting.lanes; + host_uhs2_cfg_set(host, &info, TRUE); + + result = uhs2_resume_dmt(card, sd_cmd, host, FALSE); + if (result == FALSE) + goto exit; + +exit: + if (result == FALSE) + DbgErr("UHS2 card configuration failed\n"); + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Exit %s ret=%d\n", __func__, result); + return result; +} + +bool uhs2_init_stage2(sd_card_t *card) +{ + + bool result = FALSE; + sd_command_t sd_cmd; + + /* Init stage always do pm setting */ + bool bchg = TRUE; + + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + os_memset(&sd_cmd, 0, sizeof(sd_command_t)); + + result = sd_init_get_info(card); + if (result == FALSE) { + DbgErr("SD Card get info failed\n"); + goto exit; + } + + if (card_need_get_info(card)) { + result = sd_switch_function_check(card, &sd_cmd); + if (!result) { + DbgErr("uhs2 swich function check failed.\n"); + goto exit; + } + } + + /* 14. Swich function check/set */ + + { + /* + * 14.2 Swich function check set. + * - Driver Strength, + * - Access Mode, + * - Power Limit + * - Change clock freq + */ + result = sd_switch_power_limit(card, &sd_cmd, &bchg); + if (result == FALSE) { + DbgErr("uhs2 switch power limit failed\n"); + goto exit; + } + + } +exit: + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Exit %s ret=%d\n", __func__, result); + return result; +} + +/* + * Function Name: uhs2_card_init + * + * Abstract: This Function is used init uhs2 card + * + * Input: + * sd_card_t *card : The Command will send to which Card + * + * Return value: + * If the routine succeeds, it must return TRUE, and fill trans_reg_t part. + * otherwize reutrn FALSE + */ +bool uhs2_card_init(sd_card_t *card) +{ + bool result = FALSE; + sd_command_t sd_cmd; + sd_host_t *host = card->host; + + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* Try to init as UHS2 card */ + card->card_type = CARD_UHS2; + result = uhs2_devinit_flow(card, &sd_cmd, host); + if (result == FALSE) + goto exit; + + /* do enumeration */ + result = uhs2_dev_enumeration(card, &sd_cmd); + if (result == FALSE) + goto exit; + + if (card_need_get_info(card)) { + /* Get card capabilities */ + result = uhs2_card_get_caps(card, &sd_cmd, host); + if (result == FALSE) + goto exit; + + } + + uhs2_get_card_setting_host(card, host); + uhs2_update_card_setting_vendor(card, host); + uhs2_update_card_setting_degrade(card); + uhs2_update_card_setting_thermal(card); + + result = uhs2_card_configuration(card, &sd_cmd, host); + if (result == FALSE) + goto exit; + + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARD_INIT, TO_RAM, + "uhs2 setting n_fcu=%d lss_dir=%d lss_syn=%d datagap=%d\n", + card->uhs2_info.uhs2_setting.n_fcu, + card->uhs2_info.uhs2_setting.n_lss_dir, + card->uhs2_info.uhs2_setting.n_lss_syn, + card->uhs2_info.uhs2_setting.n_data_gap); + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARD_INIT, TO_RAM, + "uhs2 Setting range=%d half=%d lpm=%d\n", + card->uhs2_info.uhs2_setting.speed_range, + card->uhs2_info.uhs2_setting.half_supp, + card->uhs2_info.uhs2_setting.pwr_mode); + + result = sd_card_identify(card); + if (result == FALSE) + goto exit; + + result = sd_card_select(card); + if (result == FALSE) + goto exit; + + if (card->locked == TRUE) { + DbgWarn(MODULE_UHS2_CARD, NOT_TO_RAM, "uhs2 card is locked\n"); + goto exit; + } + + result = card_init_stage2(card); + if (result == FALSE) { + DbgErr("SD init stage 2 failed.\n"); + goto exit; + } + +exit: + if (result == FALSE) + DbgErr("UHS2 card init failed\n"); + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Exit %s ret=%d\n", __func__, result); + return result; +} + +/* + * Function Name: uhs2_degrade_policy + * + * Abstract: This Function is used set uhs2 degrade flag + * + * Input: + * sd_card_t *card : The Command will send to which Card + * sd_command_t *sd_cmd + * + * Return value: + * If the routine succeeds, it must return TRUE, and fill trans_reg_t part. + * otherwize reutrn FALSE + */ +void uhs2_degrade_policy(sd_card_t *card, sd_command_t *sd_cmd) +{ + if (card->degrade_freq_level < CARD_DEGRADE_FREQ_TIMES) { + card->degrade_freq_level++; + goto exit; + } + + if (sd_cmd != NULL && card->uhs2_info.uhs2_setting.half_supp + && (card->degrade_uhs2_half == 0)) { + card->degrade_uhs2_half = 1; + goto exit; + } + + if (card->degrade_uhs2_range == 0 + && card->uhs2_info.uhs2_setting.speed_range) { + card->degrade_uhs2_range = 1; + goto exit; + } + + card->degrade_uhs2_legacy = 1; + card->quick_init = 0; + card->card_type = CARD_NONE; + card->degrade_freq_level = 0; + +exit: + DbgErr("UHS2 degrade range=%d freq_level=%d half=%d legacy=%d\n", + card->degrade_uhs2_range, card->degrade_freq_level, + card->degrade_uhs2_half, card->degrade_uhs2_legacy); +} + +static bool uhs2_read_status_reg(sd_card_t *card, sd_command_t *sd_cmd) +{ + bool result = FALSE; + u32 payload = 0; + + DbgInfo(MODULE_UHS2_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Enter %s\n", __func__); + + result = + uhs2_access_reg(card, sd_cmd, UHS2_IOADDR_ST_REG, FALSE, FALSE, 1, + &payload); + + if (result == FALSE) + DbgErr("uhs2 read status reg failed\n"); + + DbgInfo(MODULE_UHS2_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Exit %s ret=%d\n", __func__, result); + return result; +} + +/* + * Function Name: uhs2_sd_error_recovery + * + * Abstract: This Function is used do error recovery for uhs2 + * + * Input: + * sd_card_t *card : The Command will send to which Card + * sd_command_t *sd_cmd + * + * Return value: + * If the routine succeeds, it must return TRUE, and fill trans_reg_t part. + * otherwize reutrn FALSE + */ + +bool uhs2_sd_error_recovery(sd_card_t *card, sd_command_t *sd_cmd) +{ + bool result = FALSE; + sd_command_t recover_cmd; + + DbgInfo(MODULE_UHS2_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Enter %s\n", __func__); + + if (sd_cmd == NULL) + goto exit; + + /* If uncoverable do fullreset recover directly */ + if (uhs2_is_uncoverable(sd_cmd)) + goto full_reset; + + host_uhs2_reset(card->host, FALSE); + + /* do sd-tran */ + result = uhs2_trans_abort(card, &recover_cmd); + if (result == FALSE) + goto full_reset; + + result = uhs2_read_status_reg(card, &recover_cmd); + if (result == FALSE) + goto full_reset; + + /* send cmd12 and check whether */ + card_send_command12(card, &recover_cmd); + if (recover_cmd.uhs2_nack != 0) + goto full_reset; + + result = card_check_rw_ready(card, &recover_cmd, 150); + if (result == FALSE) + goto full_reset; + + goto exit; + +full_reset: + DbgErr("Do Full reset uhs2 recovery\n"); + host_uhs2_reset(card->host, FALSE); + result = uhs2_full_reset_card(card); + if (result) + result = card_init(card, 1, TRUE); + +exit: + DbgInfo(MODULE_UHS2_CARD, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Exit %s ret=%d\n", __func__, result); + return result; +} + +u32 card_get_uhs2_freq(sd_card_t *card) +{ + sd_host_t *host = card->host; + /* cfg_max_freq_item_t *freq = &(host->cfg->host_item.max_freq_item); */ + u16 index = 0; + u32 value; + + if (host->cfg == NULL || host->cfg->dmdn_tbl == NULL) { + DbgErr("host cfg is null\n"); + return 0; + } + + index = (u16) FREQ_UHS2M_START_INDEX + card->degrade_freq_level; + if (index > (u16) FREQ_UHS2M_DEGRE_INDEX) + index = (u16) FREQ_UHS2M_DEGRE_INDEX; + value = host->cfg->dmdn_tbl[index]; + DbgInfo(MODULE_UHS2_CARD, FEATURE_CARD_INIT, NOT_TO_RAM, + "Get Uhs2 Dmdn=0x%08X\n", value); + + return value; +} From patchwork Fri Oct 13 08:33:43 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: liuchang_125125@163.com X-Patchwork-Id: 13420426 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5FF69CDB47E for ; Fri, 13 Oct 2023 08:34:21 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230011AbjJMIeU (ORCPT ); Fri, 13 Oct 2023 04:34:20 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54126 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230123AbjJMIeS (ORCPT ); Fri, 13 Oct 2023 04:34:18 -0400 Received: from m15.mail.163.com (m15.mail.163.com [45.254.50.219]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id E9752A9; Fri, 13 Oct 2023 01:34:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:Subject:Date:Message-Id:MIME-Version: Content-Type; bh=pX+JgnUBFn1ssbGQ55Jcu7IclPIYsehX4jjd64+1eGM=; b=EIdwsBvcXS1SQ9Mm49kA9VagkAq1Eu2A3hOAQRbActoNPd5p0W4HOSPqKfA7ZI AV3k811wB9SFy86dD73TtNNFxFSR+Hahn4CcZYd6jc3pYxU8NQhQArOvzB1DFV7t s1g+UeyleRtbmihPqzXL5b/mT94LU/apcKbdQZI34GIFA= Received: from test-Z390-GAMING-X.bayhubtech.com (unknown [58.48.115.170]) by zwqz-smtp-mta-g2-3 (Coremail) with SMTP id _____wDXXyfqACllFbzAAQ--.32507S2; Fri, 13 Oct 2023 16:33:46 +0800 (CST) From: liuchang_125125@163.com To: jejb@linux.ibm.com, martin.petersen@oracle.com, linux-scsi@vger.kernel.org, linux-kernel@vger.kernel.org Cc: mark.tao@bayhubtech.com, shaper.liu@bayhubtech.com, thomas.hu@bayhubtech.com, chevron.li@bayhubtech.com, charl.liu@bayhubtech.com, Charl Liu Subject: [PATCH 4/9] scsi: bht: host: Add the source files related to host initialization, command handler, interrupt handler and transfer handler Date: Fri, 13 Oct 2023 16:33:43 +0800 Message-Id: <20231013083343.10296-1-liuchang_125125@163.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 X-CM-TRANSID: _____wDXXyfqACllFbzAAQ--.32507S2 X-Coremail-Antispam: 1Uf129KBjvtXoWktw45tr1rAw15Ar4kWw18uFg_yoW5Xw13WF c_XFWUZ3s0qrn8KrW3Ka1DKw13Ary8Krs7Xw4kWa18G39Fkw1kt3y5tws7ZFWYvr17Cw4r tr109rsIkrn3Xw1UKjkaLaAFLSUrUUUUUb8apTn2vfkv8UJUUUU8Yxn0WfASr-VFAUDa7- sFnT9fnUUvcSsGvfC2KfnxnUUI43ZEXa7xRRHGQDUUUUU== X-Originating-IP: [58.48.115.170] X-CM-SenderInfo: polxux5dqjsiqsvrjki6rwjhhfrp/xtbB0BgIWWEssysZwgABsW Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org From: Charl Liu 1.cmdhandler: define the functions related to command handler 2.host: implement host initialization flow and related functions 3.hostven: define the specific functions related to host vendor 4.irqhandler: define the functions related to interrupt handler 5.transhandler: define the functions related to transfer handler Signed-off-by: Charl Liu --- Change in V1: Add the source files related to host initialization, command handler, interrupt handler and transfer handler. --- drivers/scsi/bht/host/cmdhandler.c | 1686 +++++++++++++++ drivers/scsi/bht/host/handler.h | 36 + drivers/scsi/bht/host/host.c | 2947 ++++++++++++++++++++++++++ drivers/scsi/bht/host/hostreg.h | 478 +++++ drivers/scsi/bht/host/hostven.c | 2774 ++++++++++++++++++++++++ drivers/scsi/bht/host/hostven.h | 31 + drivers/scsi/bht/host/irqhandler.c | 742 +++++++ drivers/scsi/bht/host/transhandler.c | 1730 +++++++++++++++ 8 files changed, 10424 insertions(+) create mode 100644 drivers/scsi/bht/host/cmdhandler.c create mode 100644 drivers/scsi/bht/host/handler.h create mode 100644 drivers/scsi/bht/host/host.c create mode 100644 drivers/scsi/bht/host/hostreg.h create mode 100644 drivers/scsi/bht/host/hostven.c create mode 100644 drivers/scsi/bht/host/hostven.h create mode 100644 drivers/scsi/bht/host/irqhandler.c create mode 100644 drivers/scsi/bht/host/transhandler.c diff --git a/drivers/scsi/bht/host/cmdhandler.c b/drivers/scsi/bht/host/cmdhandler.c new file mode 100644 index 000000000000..bc7a47453daa --- /dev/null +++ b/drivers/scsi/bht/host/cmdhandler.c @@ -0,0 +1,1686 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: cmdhandler.c + * + * Abstract: Include card command handler + * + * Version: 1.00 + * + * Author: Peter.Guo + * + * Environment: OS Independent + * + * History: + * + * 9/2/2014 Creation Peter.guo + */ + +#include "../include/basic.h" +#include "../include/debug.h" +#include "../include/hostapi.h" +#include "../include/cmdhandler.h" +#include "hostven.h" +#include "../host/handler.h" +#include "../host/hostreg.h" +#include "../include/util.h" + +void irq_disable_sdcmd_int(sd_host_t *host); + +#if DBG || _DEBUG +void host_dump_reg(sd_host_t *host) +{ + u16 i = 0; + + if ((g_dbg_ctrl & DBG_CTRL_DUMP_HOST) == 0) + return; + + for (i = 0; i < 0x120; i += 4) { + DbgErr("reg offset=0x%04X value=0x%08X\n", i, + sdhci_readl(host, i)); + + if (sdhci_readl(host, i) == 0xffffffff) + return; + } +} +#else +#define host_dump_reg(x) +#endif + +void host_error_int_recovery_stage1(sd_host_t *host, u16 error_int_state, + bool check); + +bool card_is_low_capacity(sd_card_t *card) +{ + if (card->card_type == CARD_SD) { + if (card->info.csd.csd_structure == 0) + return TRUE; + } else if (card->card_type == CARD_EMMC || card->card_type == CARD_MMC) { + if (card->info.card_ccs == 0) + return TRUE; + } + + return FALSE; +} + +/* + * Function Name: cmd_test_fill_err + * + * Abstract: This Function is to test error interrupt handler + * + * Input: + * sd_card_t *card, + * u32 percent: percent of error occur rate + * u32 fix: 0 means random generate error; other means the error reg setting + * + * Output: None + * + * Return value: + * True: If no error generate + * + */ + +/* + * static bool cmd_test_fill_err(sd_card_t *card, u32 percent, u32 fix) + * { + * bool generate = FALSE; + * sd_host_t *host = card->host; + * u32 reg; + * + * generate = random_percent_check(percent); + * + * if (generate == FALSE) + * goto exit; + * + * if (host->uhs2_flag) { + * if (fix == 0) + * reg = (1 << os_random_get(32)); + * else + * reg = fix; + * + * sdhci_writel(host, host->uhs2_cap.tst_base, reg); + * + * } else { + * if (fix == 0) + * reg = (1 << os_random_get(16)); + * else + * reg = fix; + * + * sdhci_writew(host, SDHCI_ERROR_INTR_EVENT, (u16) reg); + * } + * + * exit: + * return generate; + * } + */ + +/* + * Function Name: cmd_check_card_exist + * + * Abstract: This Function is to check whether card is present or not + * + * Input: + * sd_card_t *card : The Command will send to which Card + * sd_command_t *sd_cmd: This parameter will contail card command info and reg info + * for adma3 case this reg don't need conatin reg info + * + * Output: None + * + * Return value: + * If the routine succeeds, it must return TRUE + * otherwize reutrn FALSE + */ +static bool cmd_check_card_exist(sd_card_t *card, sd_command_t *sd_cmd) +{ + bool ret = FALSE; + + if (card->card_present == FALSE || card->card_chg) { + sd_cmd->err.error_code = ERR_CODE_NO_CARD; + goto exit; + } + + ret = TRUE; + +exit: + return ret; +} + +/* + * Function Name: sdcmd_response_chk + * Abstract: This Function is used to verify the response for sd-tran command + * + * Input: + * e_card_type type: card type + * sd_command_t *sd_cmd: + * u32 response: response value + * + * + * Return value: + * TRUE: means ok + * others error + * + * Notes: + * + * so giving the routine another name requires you to modify the build tools. + */ + +static bool sdcmd_response_chk(e_card_type type, sd_command_t *sd_cmd, + u32 response) +{ + bool ret = TRUE; + u32 flag = (sd_cmd->cmd_flag & CMD_FLG_RESP_MASK); + + sd_cmd->err.resp_err = 0; + DbgInfo(MODULE_SD_HOST, FEATURE_CARDCMD_TRACE, 0, "Enter %s\n", + __func__); + + /* don't check resopnse case */ + if (!(sd_cmd->cmd_flag & CMD_FLG_RESCHK)) + goto exit; + + switch (flag) { + case CMD_FLG_R1: + case CMD_FLG_R1B: + + /* + * Card status Check + * OUT_OF_RANGE + * ADDRESS_ERROR + * BLOCK_LEN_ERROR + * ERASE_SEQ_ERROR + * ERASE_PARAM + * WP_VIOLATION + * LOCK_UNLOCK_FAILED + * COM_CRC_ERROR + * ILLEGAL_COMMAND + * CARD_ECC_FAILED + * CC_ERROR + * ERROR + * UNDERRUN + * OVERRUN + * CID/CSD_OVERWRITE + */ + + if (response & 0xFDF90000) { + sd_cmd->err.error_code = ERR_CODE_RESP_ERR; + sd_cmd->err.resp_err = (response & 0xFDF90000); + ret = FALSE; + } + break; + case CMD_FLG_R6: + if (response & 0x0000E000) { + sd_cmd->err.error_code = ERR_CODE_RESP_ERR; + + if (response & 0x00000800) + sd_cmd->err.resp_err |= RESP_ERR_TYPE_ERROR; + if (response & 0x00004000) + sd_cmd->err.resp_err |= + RESP_ERR_TYPE_ILLEGAL_CMD; + if (response & 0x00002000) + sd_cmd->err.resp_err |= + RESP_ERR_TYPE_COM_CRC_ERROR; + ret = FALSE; + } + break; + + /* Response = R5 */ + case CMD_FLG_R5: + if ((response & 0x0000CB00) && type == CARD_SDIO) { + sd_cmd->err.error_code = ERR_CODE_RESP_ERR; + if (response & 0x00000800) + sd_cmd->err.resp_err |= RESP_ERR_TYPE_ERROR; + if (response & 0x00000100) + sd_cmd->err.resp_err |= + RESP_ERR_TYPE_OUT_OF_RANGE; + if (response & 0x00000200) + sd_cmd->err.resp_err |= RESP_ERR_TYPE_FUNC_NUM; + if (response & 0x00004000) + sd_cmd->err.resp_err |= + RESP_ERR_TYPE_ILLEGAL_CMD; + if (response & 0x00008000) + sd_cmd->err.resp_err |= + RESP_ERR_TYPE_COM_CRC_ERROR; + ret = FALSE; + } + break; + default: + break; + } +exit: + if (ret == FALSE) + DbgErr("resp err=0x%08X,response=%x\n", sd_cmd->err.resp_err, + response); + DbgInfo(MODULE_SD_HOST, FEATURE_CARDCMD_TRACE, 0, "Exit %s ret=%d\n", + __func__, ret); + return ret; +} + +/* + * Function Name: cmd_legacy_response + * Abstract: This Function is used to get response of legacy command + * + * Input: + * void *card : pointer to card + * void *host_request poineter to host_cmd_req_t + * + * + * Return value: + * 0: means ok + * others error + * + * Notes: + * + * so giving the routine another name requires you to modify the build tools. + */ +u32 cmd_legacy_response(void *pcard, void *host_request) +{ + u32 ret = INTR_CB_OK; + sd_card_t *card = pcard; + sd_host_t *host = card->host; + host_cmd_req_t *req = host_request; + sd_command_t *sd_cmd = req->private; + byte *buff = NULL; + u32 val; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARDCMD_TRACE, 0, "Enter %s\n", + __func__); + + if (req->trans_type == TRANS_ADMA3 || req->inf_mode != INF_NONE) + goto exit; + + if (sd_cmd->cmd_flag & CMD_FLG_R2) { + val = sdhci_readl(host, SDHCI_RESPONSE); + buff = (byte *) sd_cmd->response; + buff[14] = (byte) (val & 0x000000ff); + buff[13] = (byte) ((val & 0x0000ff00) >> 8); + buff[12] = (byte) ((val & 0x00ff0000) >> 16); + buff[11] = (byte) ((val & 0xff000000) >> 24); + + val = sdhci_readl(host, SDHCI_RESPONSE + 4); + buff[10] = (byte) (val & 0x000000ff); + buff[9] = (byte) ((val & 0x0000ff00) >> 8); + buff[8] = (byte) ((val & 0x00ff0000) >> 16); + buff[7] = (byte) ((val & 0xff000000) >> 24); + + val = sdhci_readl(host, SDHCI_RESPONSE + 8); + buff[6] = (byte) (val & 0x000000ff); + buff[5] = (byte) ((val & 0x0000ff00) >> 8); + buff[4] = (byte) ((val & 0x00ff0000) >> 16); + buff[3] = (byte) ((val & 0xff000000) >> 24); + + val = sdhci_readl(host, SDHCI_RESPONSE + 12); + buff[2] = (byte) (val & 0x000000ff); + buff[1] = (byte) ((val & 0x0000ff00) >> 8); + buff[0] = (byte) ((val & 0x00ff0000) >> 16); + } else { + sd_cmd->response[0] = sdhci_readl(host, SDHCI_RESPONSE); + if (sdcmd_response_chk + (card->card_type, sd_cmd, sd_cmd->response[0]) == FALSE) + ret = INTR_CB_ERR; + } + + if (sd_cmd->cmd_flag & CMD_FLG_DDR200_WORK_AROUND + && sd_cmd->data->dir == DATA_DIR_OUT) { + DbgInfo(MODULE_TRANS, FEATURE_RW_TRACE, NOT_TO_RAM, + "update output phase for write case\n"); + /* Disable SD clock */ + sdhci_and32(host, SDHCI_CLOCK_CONTROL, ~(SDHCI_CLOCK_CARD_EN)); + + /* update output phase */ + pci_andl(host, 0x354, 0xFFFFFF0F); + pci_orl(host, 0x354, (host->cur_output_phase << 4)); + + /* update input phase */ + sdhci_and32(card->host, SDHCI_DLL_PHASE_CFG, ~0x1F000000); + sdhci_or32(card->host, SDHCI_DLL_PHASE_CFG, + (BIT28) | + (card->output_input_phase_pair + [host->cur_output_phase] + << 24)); + + /* Enable SD clock */ + sdhci_or32(host, SDHCI_CLOCK_CONTROL, (SDHCI_CLOCK_CARD_EN)); + + /* Continue transfer */ + sdhci_or32(host, SDHCI_DRIVER_CTRL_REG, + SDHCI_DRIVER_CTRL_ADMA2_START_INF); + /* sd_cmd->gg8_ddr200_workaround = 0; */ + } + +exit: + DbgInfo(MODULE_SD_HOST, FEATURE_CARDCMD_TRACE, 0, + "Exit %s ret=0x%08X\n", __func__, ret); + return ret; +} + +/* + * Function Name: cmd_uhs2_response + * Abstract: This Function is used to get response of UHS2 command + * + * Input: + * void *card : pointer to card + * void *host_request poineter to host_cmd_req_t + * + * + * Return value: + * 0: means ok + * others error + * + * Notes: + * + * so giving the routine another name requires you to modify the build tools. + */ +u32 cmd_uhs2_response(void *pcard, void *host_request) +{ + u32 ret = INTR_CB_OK; + sd_card_t *card = pcard; + sd_host_t *host = card->host; + host_cmd_req_t *req = host_request; + sd_command_t *sd_cmd = req->private; + u32 resp0 = 0; + int i; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARDCMD_TRACE, 0, "Enter %s\n", + __func__); + + if (req->trans_type == TRANS_ADMA3 || req->inf_mode != INF_NONE) + goto exit; + + if (sd_cmd->cmd_flag & CMD_FLG_RESCHK) { + resp0 = sdhci_readl(host, SDHCI_UHS2_RESPONSE); + if (resp0 & UHS2_RESP_NACK) + sd_cmd->uhs2_nack = 1; + } + + if (sd_cmd->sd_cmd) { + if (sd_cmd->cmd_flag & CMD_FLG_R2) { + for (i = 0; i < 4; i++) + sd_cmd->response[i] = + sdhci_readl(host, + SDHCI_UHS2_RESPONSE4 + i * 4); + + } else if (sd_cmd->cmd_index == SD_CMD12) { + sd_cmd->response[0] = + swapu32(sdhci_readl(host, SDHCI_UHS2_CMD12_RES)); + if (sdcmd_response_chk + (card->card_type, sd_cmd, + sd_cmd->response[0]) == FALSE) + ret = INTR_CB_ERR; + sdhci_writel(host, SDHCI_ADMA_ADDRESS, 0); + } else { + sd_cmd->response[0] = + swapu32(sdhci_readl(host, SDHCI_UHS2_RESPONSE4)); + if (sdcmd_response_chk + (card->card_type, sd_cmd, + sd_cmd->response[0]) == FALSE) + ret = INTR_CB_ERR; + } + } else { + if (UHS2_GET_NATIVE_IOADDR(sd_cmd->uhs2_header) == + UHS2_IOADDR_ABORT) + sd_cmd->response[0] = + sdhci_readl(host, SDHCI_RESPONSE + 4); + else { + for (i = 0; i < 4; i++) + sd_cmd->response[i] = + sdhci_readl(host, + SDHCI_UHS2_RESPONSE4 + i * 4); + } + } + +exit: + DbgInfo(MODULE_SD_HOST, FEATURE_CARDCMD_TRACE, 0, + "Exit %s return=0x%08X\n", __func__, ret); + return ret; + +} + +/* + * Function Name: cmd_piobuff_ready + * Abstract: This Function is used to handle pio data buffer ready + * + * Input: + * void *card : pointer to card + * void *host_request poineter to host_cmd_req_t + * + * + * Return value: + * 0: means ok + * others error + * + * Notes: + * + * so giving the routine another name requires you to modify the build tools. + */ +u32 cmd_piobuff_ready(void *pcard, void *host_request) +{ + u32 ret = INTR_CB_OK; + sd_card_t *card = pcard; + sd_host_t *host = card->host; + host_cmd_req_t *req = host_request; + sd_command_t *sd_cmd = req->private; + sd_data_t *data = sd_cmd->data; + u32 i; + u32 trans_len = 0; + u32 *buffer = NULL; + u32 left = 0; + + DbgInfo(MODULE_TRANS, FEATURE_RW_TRACE, 0, "Enter %s\n", __func__); + if (data == NULL) { + ret = INTR_CB_ERR; + goto exit; + } + + buffer = (u32 *) data->data_mng.driver_buff; + if (buffer == NULL) { + ret = INTR_CB_ERR; + goto exit; + } + + /* get transfer start position and */ + buffer += data->data_mng.offset / 4; + left = data->data_mng.total_bytess - data->data_mng.offset; + DbgInfo(MODULE_TRANS, FEATURE_RW_TRACE, 0, + "pio dir=%d left=%d offset=%d\n", data->dir, left, + data->data_mng.offset); + + /* Caculete the data transfer length for this time */ + trans_len = data->block_size; + if (sd_cmd->uhs2_cmd) { + u32 n_fcu = card->uhs2_info.uhs2_setting.n_fcu; + + if (n_fcu == 0) + n_fcu = 256; + trans_len = os_min(left, n_fcu * trans_len); + } else { + trans_len = os_min(left, trans_len); + } + + trans_len /= 4; + if (sd_cmd->cmd_index == SD_CMD17) { + ven_or16(host, 0x510, 0x2000); + os_udelay(1); + ven_and16(host, 0x510, ~0x2000); + } + + /* transfer data */ + for (i = 0; i < trans_len; i++) { + if (data->dir == DATA_DIR_IN) + buffer[i] = sdhci_readl(host, SDHCI_BUFFER); + else + sdhci_writel(host, SDHCI_BUFFER, buffer[i]); + + data->data_mng.offset += 4; + left -= 4; + } + + if (left > 0) + ret = INTR_CB_NOEND; +exit: + DbgInfo(MODULE_TRANS, FEATURE_RW_TRACE, 0, "Exit %s return=0x%08X\n", + __func__, ret); + return ret; +} + +/* + * Function Name: uhs2_sdcmd_generate + * Abstract: This Function is used to generate UHS2 SDCmd registers + * + * Input: + * sd_card_t *card : The Command will send to which Card + * sd_command_t *sd_cmd: This parameter will contail card command info + * Output: + * sd_command_t *sd_cmd to store register values + * + * Return value: + * + * If the routine succeeds, it must return TRUE, and fill trans_reg_t part. + * otherwize reutrn FALSE + * Notes: + * + * so giving the routine another name requires you to modify the build tools. + */ +static bool uhs2_sdcmd_generate(sd_card_t *pcard, sd_command_t *sd_cmd) +{ + sd_data_t *data = sd_cmd->data; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARDCMD_TRACE, 0, "Enter %s\n", + __func__); + + /* Step1: Generate header */ + sd_cmd->hw_resp_chk = 0; + sd_cmd->uhs2_header = 0; + sd_cmd->payload_cnt = 1; + sd_cmd->trans_reg_cnt = 1; + sd_cmd->uhs2_set_pld = 1; + sd_cmd->uhs2_header |= pcard->uhs2_info.dev_id; + if (data != NULL) + sd_cmd->uhs2_header |= UHS2_CMD_HEADER_DCMD; + + if (sd_cmd->app_cmd) + sd_cmd->uhs2_header |= UHS2_CMD_HEADER_APPCMD; + + sd_cmd->uhs2_header |= ((sd_cmd->cmd_index & 0x3f) << 24); + sd_cmd->trans_reg[0].payload[2] = 0; + if (sd_cmd->muldat_cmd) { + if (!(sd_cmd->cmd_flag & CMD_FLG_INF) && (data != NULL)) { + sd_cmd->uhs2_header |= UHS2_CMD_TMODE_LM; + sd_cmd->trans_reg[0].payload[2] = + swapu32(data->block_cnt); + /* for muldata command we need geneate paylaod2 */ + sd_cmd->payload_cnt++; + } + + if (pcard->uhs2_info.uhs2_setting.half_supp + && (pcard->degrade_uhs2_half == 0) + && (pcard->thermal_uhs2_half_dis == 0)) + sd_cmd->uhs2_header |= UHS2_CMD_TMODE_DM; + } + + /* step2: paylaod */ + sd_cmd->trans_reg[0].payload[0] = sd_cmd->uhs2_header; + sd_cmd->trans_reg[0].payload[1] = swapu32(sd_cmd->argument); + DbgInfo(MODULE_SD_HOST, FEATURE_CARDCMD_TRACE, 0, + "uhs2_sdcmd:header%08X Arg%08X\n", sd_cmd->uhs2_header, + sd_cmd->argument); + + /* step3: generate transfer mode reg */ + sd_cmd->trans_reg[0].trans_mode = 0; + + if (data) { + sd_cmd->trans_reg[0].block_size = data->block_size; + + if (sd_cmd->uhs2_header & UHS2_CMD_TMODE_DM) + sd_cmd->trans_reg[0].trans_mode |= + SDHCI_UHS2_TRAN_HALF_DUPLEX; + if (data->dir == DATA_DIR_OUT) + sd_cmd->trans_reg[0].trans_mode |= + SDHCI_UHS2_TRAN_WRITE; + + if (sd_cmd->cmd_flag & CMD_FLG_DMA) + sd_cmd->trans_reg[0].trans_mode |= + SDHCI_UHS2_TRAN_DMA_EN; + + if (!(sd_cmd->cmd_flag & CMD_FLG_INF)) { + sd_cmd->trans_reg[0].trans_mode |= + SDHCI_UHS2_TRAN_EBSY_WAIT | SDHCI_UHS2_TRAN_BLK_EN; + sd_cmd->trans_reg[0].block_cnt = data->block_cnt; + } else + sd_cmd->trans_reg[0].block_cnt = 0; + + sd_cmd->trans_reg[0].trans_mode |= SDHCI_UHS2_CMD_DATA_PRESENT; + + if (pcard->host->feature.hw_resp_chk + && (sd_cmd->cmd_flag & CMD_FLG_RESCHK) + && (sd_cmd->cmd_flag & (CMD_FLG_R5 | CMD_FLG_R1)) + && (!(sd_cmd->cmd_flag & CMD_FLG_INF_CON))) { + sd_cmd->hw_resp_chk = 1; + if (sd_cmd->cmd_flag & CMD_FLG_R5) + sd_cmd->trans_reg[0].trans_mode |= + SDHCI_UHS2_RESP_TYPE_R5; + sd_cmd->trans_reg[0].trans_mode |= + SDHCI_UHS2_RESP_CHK | SDHCI_UHS2_RESP_INTR_DIS; + } + } else { + if (sd_cmd->cmd_flag & CMD_FLG_R1B) + sd_cmd->trans_reg[0].trans_mode |= + SDHCI_UHS2_TRAN_EBSY_WAIT; + + if (sd_cmd->cmd_index == SD_CMD12) + sd_cmd->trans_reg[0].trans_mode |= + SDHCI_UHS2_CMD_TYPE_CMD12; + } + sd_cmd->trans_reg[0].trans_mode |= ((sd_cmd->payload_cnt + 1) << 26); + + DbgInfo(MODULE_SD_HOST, FEATURE_CARDCMD_TRACE, 0, + "uhs2_sdcmd:trans_mode=%08X\n", + sd_cmd->trans_reg[0].trans_mode); + DbgInfo(MODULE_SD_HOST, FEATURE_CARDCMD_TRACE, 0, "Exit %s\n", + __func__); + return TRUE; + +} + +/* + * Function Name: uhs2_native_generate + * Abstract: This Function is used to generate legacy SDCmd registers + * + * Input: + * sd_card_t *card : The Command will send to which Card + * sd_command_t *sd_cmd: This parameter will contail card command info with + * uhs2_head and payload_cnt set + * Output: + * sd_command_t *sd_cmd to store register values + * + * Return value: + * + * If the routine succeeds, it must return TRUE, and fill trans_reg_t part. + * otherwize reutrn FALSE + * Notes: + * + * so giving the routine another name requires you to modify the build tools. + */ + +static bool uhs2_native_generate(sd_card_t *pcard, sd_command_t *sd_cmd) +{ + DbgInfo(MODULE_SD_HOST, FEATURE_CARDCMD_TRACE, 0, "Enter %s\n", + __func__); + + sd_cmd->trans_reg_cnt = 1; + sd_cmd->hw_resp_chk = 0; + sd_cmd->trans_reg[0].block_cnt = 0; + sd_cmd->trans_reg[0].trans_mode = 0; + sd_cmd->trans_reg[0].payload[0] = sd_cmd->uhs2_header; + if (sd_cmd->uhs2_set_pld == 0) + sd_cmd->payload_cnt = 0; + sd_cmd->trans_reg[0].trans_mode |= ((sd_cmd->payload_cnt + 1) << 26); + if (UHS2_GET_NATIVE_IOADDR(sd_cmd->uhs2_header) == UHS2_IOADDR_ABORT) + sd_cmd->trans_reg[0].trans_mode |= SDHCI_UHS2_CMD_TYPE_ABORT; + else if (UHS2_GET_NATIVE_IOADDR(sd_cmd->uhs2_header) == + UHS2_IOADDR_GODMT) + sd_cmd->trans_reg[0].trans_mode |= + SDHCI_UHS2_CMD_TYPE_GODORMANT; + else if (UHS2_GET_NATIVE_IOADDR(sd_cmd->uhs2_header) == + UHS2_IOADDR_FULLRESET) + sd_cmd->trans_reg[0].trans_mode |= + SDHCI_UHS2_CMD_TYPE_GODORMANT; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARDCMD_TRACE, 0, + "uhs2_native:trans_mode=%08X\n", + sd_cmd->trans_reg[0].trans_mode); + DbgInfo(MODULE_SD_HOST, FEATURE_CARDCMD_TRACE, 0, "Exit %s\n", + __func__); + return TRUE; +} + +/* + * Function Name: uhs2_sdcmd_generate + * Abstract: This Function is used to generate legacy SDCmd registers + * + * Input: + * sd_card_t *pcard : The Command will send to which Card + * sd_command_t *sd_cmd: This parameter will contail card command info + * Output: + * sd_command_t *sd_cmd to store register values + * + * Return value: + * + * If the routine succeeds, it must return TRUE, and fill trans_reg_t part. + * otherwize reutrn FALSE + * Notes: + * + * so giving the routine another name requires you to modify the build tools. + */ +static bool legacy_sdcmd_generate(sd_card_t *pcard, sd_command_t *sd_cmd) +{ + int i = 0; + sd_host_t *host = pcard->host; + sd_data_t *data = sd_cmd->data; + u32 flgs = sd_cmd->cmd_flag; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARDCMD_TRACE, 0, "Enter %s\n", + __func__); + + sd_cmd->trans_reg_cnt = 1; + sd_cmd->hw_resp_chk = 0; + + /* step 1 generate cmd55 registers */ + if (sd_cmd->app_cmd) { + sd_cmd->trans_reg_cnt++; + + sd_cmd->trans_reg[i].block_cnt = 0; + sd_cmd->trans_reg[i].block_size = 0; + sd_cmd->trans_reg[i].payload[0] = (pcard->info.rca << 16); + sd_cmd->trans_reg[i].trans_mode = (SD_CMD55 << 24); + sd_cmd->trans_reg[i].trans_mode |= SDHCI_RSP_TYPE_R1; + i++; + } + + /* step2 generate transmode reg */ + sd_cmd->trans_reg[i].trans_mode = ((sd_cmd->cmd_index & 0x3f) << 24); + sd_cmd->trans_reg[i].payload[0] = sd_cmd->argument; + DbgInfo(MODULE_SD_HOST, FEATURE_CARDCMD_TRACE, 0, + "legacycmd:cmdidx%08X Arg%08X\n", sd_cmd->cmd_index, + sd_cmd->argument); + + if (data) { + /* generate block_size and block_cnt and argument register */ + sd_cmd->trans_reg[i].block_size = data->block_size; + if (!(flgs & CMD_FLG_INF)) + sd_cmd->trans_reg[i].block_cnt = data->block_cnt; + else + sd_cmd->trans_reg[i].block_cnt = 0; + + sd_cmd->trans_reg[i].trans_mode |= SDCHI_CMD_DATA_PRESENT; + + if (data->dir == DATA_DIR_IN) + sd_cmd->trans_reg[i].trans_mode |= SDHCI_TRNS_READ; + + if (sd_cmd->cmd_flag & CMD_FLG_DMA) + sd_cmd->trans_reg[i].trans_mode |= SDHCI_TRNS_DMA; + + if (sd_cmd->muldat_cmd) { + sd_cmd->trans_reg[i].trans_mode |= SDHCI_TRNS_MULTI; + + if (!(flgs & CMD_FLG_INF)) { + sd_cmd->trans_reg[i].trans_mode |= + SDHCI_TRNS_BLK_CNT_EN; + if (sd_cmd->cmd_flag & (CMD_FLG_AUTO12 | + CMD_FLG_AUTO23)) { + if (host->feature.hw_autocmd) + sd_cmd->trans_reg[i].trans_mode |= + SDHCI_TRNS_AUTO_CMD12 | + SDHCI_TRNS_AUTO_CMD23; + else if (sd_cmd->cmd_flag & + CMD_FLG_AUTO12) + sd_cmd->trans_reg[i].trans_mode |= + SDHCI_TRNS_AUTO_CMD12; + else + sd_cmd->trans_reg[i].trans_mode |= + SDHCI_TRNS_AUTO_CMD23; + } + } + } + + if (host->feature.hw_resp_chk + && (sd_cmd->cmd_flag & CMD_FLG_RESCHK) + && (0 == (sd_cmd->cmd_flag & CMD_FLG_DDR200_WORK_AROUND)) + && (sd_cmd->cmd_flag & (CMD_FLG_R5 | CMD_FLG_R1)) + && (!(flgs & CMD_FLG_INF_CON))) { + sd_cmd->hw_resp_chk = 1; + if (sd_cmd->cmd_flag & CMD_FLG_R5) + sd_cmd->trans_reg[i].trans_mode |= + SDHCI_TRNS_RESP_R5; + sd_cmd->trans_reg[i].trans_mode |= + SDHCI_TRNS_RESP_CHK | SDHCI_TRNS_RESP_INTR_DIS; + } + } else { + if (sd_cmd->cmd_index == SD_CMD12) + sd_cmd->trans_reg[i].trans_mode |= + SDHCI_CMD_TYPE_12_OR_52; + + } + + /* generate respone related register */ + if ((flgs & CMD_FLG_R1) || (flgs & CMD_FLG_R5) || (flgs & CMD_FLG_R6) + || (flgs & CMD_FLG_R7)) { + sd_cmd->trans_reg[i].trans_mode |= SDHCI_RSP_TYPE_R1; + } else if (flgs & CMD_FLG_R2) { + sd_cmd->trans_reg[i].trans_mode |= SDHCI_RSP_TYPE_R2; + } else if ((flgs & CMD_FLG_R3) || (flgs & CMD_FLG_R4)) { + sd_cmd->trans_reg[i].trans_mode |= SDHCI_RSP_TYPE_R3; + } else if (flgs & CMD_FLG_R1B) { + sd_cmd->trans_reg[i].trans_mode |= SDHCI_RSP_TYPE_R1B; + } else { + sd_cmd->trans_reg[i].trans_mode |= SDHCI_RSP_NONE; + } + DbgInfo(MODULE_SD_HOST, FEATURE_CARDCMD_TRACE, 0, + "legacy_sdcmd:trans_mode=%08X\n", + sd_cmd->trans_reg[0].trans_mode); + DbgInfo(MODULE_SD_HOST, FEATURE_CARDCMD_TRACE, 0, "Exit %s\n", + __func__); + + return TRUE; +} + +/* + * Function Name: cmd_generate_reg + * Abstract: This Function is used to generate host register according to Card command + * + * Input: + * sd_card_t *card : The Command will send to which Card + * sd_command_t *sd_cmd: This parameter will contail card command info + * Output: + * sd_command_t *sd_cmd to store register values + * + * Return value: + * + * If the routine succeeds, it must return TRUE, and fill trans_reg_t part. + * otherwize reutrn FALSE + * Notes: + * + * so giving the routine another name requires you to modify the build tools. + */ + +bool cmd_generate_reg(sd_card_t *card, sd_command_t *sd_cmd) +{ + bool result = FALSE; + sd_data_t *data = sd_cmd->data; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARDCMD_TRACE, 0, "Enter %s\n", + __func__); + X_ASSERT(card != NULL); + + if ((sd_cmd->cmd_flag & CMD_FLG_DMA) == 0) + host_chk_ocb_occur(card->host); + + if (cmd_check_card_exist(card, sd_cmd) == FALSE) { + DbgErr("Card not exist in %s\n", __func__); + goto exit; + } + + /* Step1 Generate sd_data according to cmd_flag */ + + if (data == NULL) + goto step2; + if (data->dir == DATA_DIR_NONE) + goto exit; + if (data->data_mng.total_bytess > SD_BLOCK_LEN) { + data->block_size = SD_BLOCK_LEN; + data->block_cnt = data->data_mng.total_bytess / SD_BLOCK_LEN; + } else { + data->block_size = data->data_mng.total_bytess; + data->block_cnt = 1; + } + DbgInfo(MODULE_SD_HOST, FEATURE_RW_TRACE, 0, + "block_size=%d block_cnt=%d\n", data->block_size, + data->block_cnt); + + /* Step 2, prepare handle sd_cmd to generate uhs2_cmd and cmd_index */ +step2: + if (card->host->uhs2_flag) + sd_cmd->uhs2_cmd = 1; + else + sd_cmd->uhs2_cmd = 0; + + if (sd_cmd->cmd_flag & CMD_FLG_MULDATA) + sd_cmd->muldat_cmd = 1; + + if (sd_cmd->cmd_index & SD_APPCMD) { + sd_cmd->app_cmd = 1; + /* Legacy AppCmd case */ + if (sd_cmd->uhs2_cmd == 0) + sd_cmd->trans_reg_cnt = 2; + } + + /* Infinite transfer case don't have register setting */ + if (sd_cmd->cmd_flag & CMD_FLG_INF_CON) { + sd_cmd->trans_reg_cnt = 0; + result = TRUE; + goto exit; + } + + /* + * Step3 Generate register for 3 case. + * (1) UHS2 SD cmd + * (2) UHS2 Native Cmd + * (3) Legacy SD cmd + */ + + if (sd_cmd->uhs2_cmd && sd_cmd->sd_cmd) + result = uhs2_sdcmd_generate(card, sd_cmd); + else if (sd_cmd->uhs2_cmd) + result = uhs2_native_generate(card, sd_cmd); + else + result = legacy_sdcmd_generate(card, sd_cmd); + +exit: + DbgInfo(MODULE_SD_HOST, FEATURE_CARDCMD_TRACE, 0, "Exit %s ret=%d\n", + __func__, result); + return result; +} + +/* + * Function Name: wait_fifo_empty + * Abstract: This Function is used to wait host fifo empty for write case + * + * Input: + * sd_host_t *host : the host + * Output: + * + * Return value: None + * + * Notes: + * this function only use for infinite mode + */ +static void wait_fifo_empty(sd_host_t *host) +{ + int i = 0; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARDCMD_TRACE, 0, "Enter %s\n", + __func__); + +#define MAX_FIFO_TIMEOUT 400000 + + for (i = 0; i < MAX_FIFO_TIMEOUT; i++) { + if (sdhci_readl(host, SDHCI_DRIVER_CTRL_REG) & + SDHCI_DRIVER_CTRL_FIFO_EMPTY) { + /* 5us */ + os_udelay(5); + } else { + break; + } + } + + if (i == MAX_FIFO_TIMEOUT) + DbgErr("Wait FiFo empty failed\n"); + + DbgInfo(MODULE_SD_HOST, FEATURE_CARDCMD_TRACE, 0, "Exit %s\n", + __func__); +} + +/* + * Function Name: cmd_final_execute + * Abstract: This Function is used to Send SD command to host and wait + * + * Input: + * sd_card_t *card : The Command will send to which Card + * host_cmd_req_t *req; Caller need to allocate mem for this + * host_trans_reg_t *reg register value to be set + * sd_command_t *sd_cmd: This parameter will + * contail card command info and reg info + * for adma3 case this reg don't need conatin reg info + * bool bsync: Last command execute sync or async + * Output: + * Whether + * + * Return value: + * + * If the routine succeeds, it must return TRUE + * otherwize reutrn FALSE + * + */ +static bool cmd_final_execute(sd_card_t *card, sd_command_t *sd_cmd, + host_cmd_req_t *req, host_trans_reg_t *reg) +{ + byte buhs2 = sd_cmd->uhs2_cmd; + int i = 0; + sd_host_t *host = card->host; + sd_data_t *data = sd_cmd->data; + cfg_item_t *cfg = host->cfg; + bool result = FALSE; + u32 timeout; + bool autocmd23 = FALSE; + + if ((reg->trans_mode & SDHCI_TRNS_AUTO_CMD23) + && (sd_cmd->cmd_flag & CMD_FLG_AUTO23)) + autocmd23 = TRUE; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARDCMD_TRACE, 0, "Enter %s\n", + __func__); + + /* step1 get timeout from configuration */ + if (data) { + if (data->dir == DATA_DIR_IN) { + if (sd_cmd->cmd_flag & CMD_FLG_TUNE) + timeout = (u32) (TUNING_TIMEOUT & 0x0000ffff); + else + timeout = + (u32) cfg->timeout_item.test_read_data_timeout.value; + } else + timeout = + (u32) cfg->timeout_item.test_write_data_timeout.value; + } else { + if (sd_cmd->sd_cmd == 0) + timeout = UHS2_NATIVE_DATA_TIMEOUT; + /* timeout = (u32)cfg->timeout_item.uhs2_native_data_timeout.value; */ + else if (sd_cmd->cmd_flag & CMD_FLG_R1B) + timeout = + (u32) cfg->timeout_item.test_r1b_data_timeout.value; + else if (sd_cmd->cmd_flag & CMD_FLG_TUNE) + timeout = (u32) (TUNING_TIMEOUT & 0x0000ffff); + else + timeout = + (u32) cfg->timeout_item.test_non_data_timeout.value; + } + + if (sd_cmd->timeout != 0) + timeout = sd_cmd->timeout; + + /* adma3 don't have argument */ + if (req->trans_type == TRANS_ADMA3) + goto next; + + /* step 2 Set argument register and block register, infinite transfer don't set */ + if (req->inf_mode != INF_CONTINUE) { + if (buhs2) { + for (i = 0; i <= sd_cmd->payload_cnt; i++) { + sdhci_writel(host, SDHCI_UHS2_CMD_PKG(i * 4), + reg->payload[i]); + } + } else { + sdhci_writel(host, SDHCI_ARGUMENT, reg->payload[0]); + } + + if (sd_cmd->data) { + if (buhs2) { + sdhci_writel(host, SDHCI_UHS2_BLOCK_SIZE, + reg->block_size | + (host->sdma_boundary_val << 12)); + sdhci_writel(host, SDHCI_UHS2_BLOCK_COUNT, + reg->block_cnt); + } else { + if (host->sd_host4_enable) { + sdhci_writew(host, SDHCI_BLOCK_SIZE, + (host->sdma_boundary_val << + 12) | reg->block_size); + sdhci_writel(host, SDHCI_ARGUMENT2, + reg->block_cnt); + } else { + sdhci_writel(host, SDHCI_BLOCK_SIZE, + (reg->block_cnt << 16) | + (host->sdma_boundary_val << + 12) + | reg->block_size); + } + } + + /* Host need to set dma mode at init stage */ + } + } + + /* step3 set software structure ready and enable signale intr */ +next: + host->cmd_req = req; + req->private = sd_cmd; + req->card = card; + host_led_ctl(host, TRUE); + os_init_completion(host->pdx, &req->done); + + /* Clear Status Register */ + sdhci_writel(host, SDHCI_INT_STATUS, + ~SDHCI_INT_INSERT_REMOVE_CARD_BITS); + if (sd_cmd->uhs2_cmd) + sdhci_writel(host, SDHCI_UHS2_ERRINT_STS, 0xFFFFFFFF); + + /* step4 update sys addr */ + if (data) { + phy_addr_t sys_addr = data->data_mng.sys_addr; + + switch (req->trans_type) { + case TRANS_SDMA: + if (buhs2 || host->sd_host4_enable) { + sdhci_writel(host, SDHCI_ADMA_ADDRESS, + os_get_phy_addr32l(sys_addr)); + if (host->bit64_enable) + sdhci_writel(host, SDHCI_ADMA_ADDRESSH, + os_get_phy_addr32h + (sys_addr)); + } else { + /* This case only support 32bit */ + if (autocmd23) { + DbgErr + ("SDMA without SD4 enable can't use AutoCmd23\n"); + sd_cmd->err.error_code = + ERR_CODE_SOFTARE_ARG; + goto exit; + } + sdhci_writel(host, SDHCI_DMA_ADDRESS, + os_get_phy_addr32l(sys_addr)); + } + + break; + case TRANS_ADMA2: + case TRANS_ADMA2_SDMA_LIKE: + if (req->inf_mode == INF_CONTINUE) { + sdhci_or32(host, SDHCI_DRIVER_CTRL_REG, + SDHCI_DRIVER_CTRL_ADMA2_START_INF); + } else { + sdhci_writel(host, SDHCI_ADMA_ADDRESS, + os_get_phy_addr32l(sys_addr)); + if (host->bit64_enable) + sdhci_writel(host, SDHCI_ADMA_ADDRESSH, + os_get_phy_addr32h + (sys_addr)); + } + break; + case TRANS_ADMA3: + sdhci_writel(host, SDHCI_ADMA3_ADDRESS, + os_get_phy_addr32l(sys_addr)); + if (host->bit64_enable) + sdhci_writel(host, SDHCI_ADMA3_ADDRESSH, + os_get_phy_addr32h(sys_addr)); + break; + default: + break; + } + + if (autocmd23 && req->trans_type != TRANS_ADMA3) + sdhci_writel(host, SDHCI_DMA_ADDRESS, data->block_cnt); + } + + /* cmd12 to stop infinite case */ + if (sd_cmd->cmd_index == SD_CMD12 && card->has_built_inf) { + if (card->last_dir == DATA_DIR_OUT) + wait_fifo_empty(host); + } + + /* step5 set transfer mode */ + if (req->inf_mode != INF_CONTINUE && req->trans_type != TRANS_ADMA3) { + if (buhs2) + sdhci_writel(host, SDHCI_UHS2_TRAN_MODE, + reg->trans_mode); + else { + /* GPIO2 Trigger for GG8 chip DDR200 write operation: timing issue */ + if (host->chip_type == CHIP_GG8 + && card->info.sw_cur_setting.sd_access_mode == + SD_FNC_AM_DDR200) { + if (card->state == CARD_STATE_WORKING + || sd_cmd->cmd_flag & + CMD_FLG_DDR200_WORK_AROUND + || sd_cmd->cmd_index == SD_CMD12) { + + if ((pci_readl(host, 0x354) & 0xF0) != + (host->cfg->feature_item.output_tuning_item.fixed_value_sdr104 + << 4)) { + /* Disable SD clock */ + sdhci_and32(host, + SDHCI_CLOCK_CONTROL, + ~ + (SDHCI_CLOCK_CARD_EN)); + /* update output phase */ + pci_andl(host, 0x354, + 0xFFFFFF0F); + pci_orl(host, 0x354, + (host->cfg->feature_item.output_tuning_item.fixed_value_sdr104 + << 4)); + + /* update input phase */ + sdhci_and32(host, + SDHCI_DLL_PHASE_CFG, + ~0x1F000000); + sdhci_or32(host, + SDHCI_DLL_PHASE_CFG, + (BIT28) | + (card->output_input_phase_pair + [host->cfg->feature_item.output_tuning_item.fixed_value_sdr104] + << 24)); + + /* Enable SD clock */ + sdhci_or32(host, + SDHCI_CLOCK_CONTROL, + (SDHCI_CLOCK_CARD_EN)); + } + } + + } + sdhci_writel(host, SDHCI_TRANSFER_MODE, + reg->trans_mode); + } + } + + /* step6 enable intr */ + host_int_sig_update(host, + SDHCI_INT_INSERT_REMOVE_CARD_BITS | + SDHCI_INT_ERROR_MASK | req->int_flag_wait); + if (buhs2) + host_uhs2_err_sig_update(host, req->int_flag_uhs2_err); + + /* step7 update card command related info */ + if (req->inf_mode == INF_BUILT) + card->has_built_inf = 1; + if (req->inf_mode != INF_NONE && data) { + card->last_dir = data->dir; + if (card_is_low_capacity(card)) + card->last_sect = + (sd_cmd->argument / SD_BLOCK_LEN) + data->block_cnt; + else + card->last_sect = sd_cmd->argument + data->block_cnt; + } else if (sd_cmd->cmd_index == SD_CMD12) + card->has_built_inf = 0; + + /* step8 wait transfer done */ + if (host->dump_mode == FALSE && host->poll_mode == FALSE) { +#if (GBL_ASYNC_PERFEATCH_IO) + if (req->issue_post_cb) + req->issue_post_cb(host->pdx); +#endif + result = os_wait_for_completion(host->pdx, &req->done, timeout); + } else { + result = irq_poll_cmd_done(host->pdx, &req->done, timeout); + } + + /* timeout */ + if (result == FALSE) { + DbgErr("wait cmd software timeout\n"); + host_dump_reg(host); + irq_disable_sdcmd_int(host); + sd_cmd->err.error_code = ERR_CODE_TIMEOUT; + host_error_int_recovery_stage1(host, SDHCI_INT_DATCMD_ERR_MASK, + TRUE); + goto exit; + } else { + if (sd_cmd->err.error_code != 0) { + result = FALSE; + host_dump_reg(host); + host_error_int_recovery_stage1(host, + sd_cmd->err.legacy_err_reg, + TRUE); + } else { + if (sd_cmd->cmd_index == SD_CMD12) { + if (sd_cmd->uhs2_cmd == 0) + host_cmddat_line_reset(host); + /* + * Add 120us delay after send CMD12 to stop infinite transfer to + * fix "FJ2 Customer platform issue #2 , + * Gloria VAUAO LA-9591P platform ,win8x64, + * SD driver O2FJ2w7 1.2.2.1011, + * emmc card NTFS file can't format". + */ + + os_udelay(120); + } + result = TRUE; + } + } + +exit: + host->cmd_req = NULL; + if (result == FALSE) { + if (buhs2) + DbgErr + ("UHS2cmd inf_info=%d cmd=0x%08X transmode=0x%08X err=%08X\n", + req->inf_mode, reg->payload[0], reg->trans_mode, + sd_cmd->err.error_code); + else + DbgErr + ("Legacycmd inf_info=%d transmode=0x%08X err=%08X, err_reg=%08X\n", + req->inf_mode, reg->trans_mode, + sd_cmd->err.error_code, + sd_cmd->err.legacy_err_reg); + } + + if (host->feature.hw_led_fix == 0) + host_led_ctl(host, FALSE); + DbgInfo(MODULE_SD_HOST, FEATURE_CARDCMD_TRACE, 0, "Exit %s result=%d\n", + __func__, result); + return result; +} + +/* + * Function Name: cmd_execute_sync_inner + * Abstract: This Function is used to generate host register according to Card command + * + * Input: + * sd_card_t *card : The Command will send to which Card + * host_cmd_req_t *req; Caller need to allocate mem for this + * sd_command_t *sd_cmd: This parameter will + * contail card command info and reg info. + * for adma3 case this reg don't need conatin reg info + * req_callback func_done : call back function to end Srb if necessary + * bool bsync: Last command execute sync or async + * Output: + * Whether + * + * Return value: + * + * If the routine succeeds, it must return TRUE + * otherwize reutrn FALSE + * + */ +static bool cmd_execute_sync_inner(sd_card_t *card, host_cmd_req_t *req, + sd_command_t *sd_cmd, + req_callback func_done, + issue_post_callback post_cb) +{ + int i = 0; + bool res = FALSE; + sd_data_t *data = sd_cmd->data; + u32 timeout = 1000; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARDCMD_TRACE, 0, "Enter %s\n", + __func__); + if (cmd_check_card_exist(card, sd_cmd) == FALSE) { + DbgErr("Card not exist in exec_inner\n"); + goto end; + } + + if (sd_cmd->uhs2_cmd == 0 && !(sd_cmd->cmd_flag & CMD_FLG_INF_CON)) { + while (timeout) { + if (cmd_dat_line_chk(card, sd_cmd)) + break; + else { + os_udelay(10); + timeout--; + } + } + if (timeout <= 0) { + DbgErr("Check CMD/DAT line inhabit failed\n"); + goto end; + } + + } + + /* Handle Legacy SD Cmd 55 first */ + if (sd_cmd->app_cmd && sd_cmd->uhs2_cmd == 0) { + sd_command_t cmd; + + os_memset(&cmd, 0, sizeof(sd_command_t)); + os_memset(req, 0, sizeof(host_cmd_req_t)); + + req->cb_response = cmd_legacy_response; + req->issue_post_cb = NULL; + req->inf_mode = INF_NONE; + req->card_type = card->card_type; + req->trans_type = TRANS_NONDATA; + req->int_flag_err = SDHCI_INT_ERR_NON_DATA; + req->int_flag_wait = SDHCI_INT_CMD_COMP; + cmd.cmd_index = SD_CMD55; + cmd.data = NULL; + cmd.cmd_flag = CMD_FLG_R1; + cmd.uhs2_cmd = sd_cmd->uhs2_cmd; + res = cmd_final_execute(card, &cmd, req, &sd_cmd->trans_reg[0]); + + if (res == FALSE) { + memcpy(&sd_cmd->err, &cmd.err, sizeof(cmd_err_t)); + sd_cmd->err.app_stage = 1; + goto end; + } + i++; + } + + /* generate host_cmd_req_t */ + os_memset(req, 0, sizeof(host_cmd_req_t)); + req->card_type = card->card_type; + req->cb_req_complete = func_done; + req->issue_post_cb = post_cb; + + if (sd_cmd->cmd_flag & CMD_FLG_INF_BUILD) + req->inf_mode = INF_BUILT; + else if (sd_cmd->cmd_flag & CMD_FLG_INF_CON) + req->inf_mode = INF_CONTINUE; + else + req->inf_mode = INF_NONE; + + if (sd_cmd->cmd_flag & CMD_FLG_ADMA2) + req->trans_type = TRANS_ADMA2; + else if (sd_cmd->cmd_flag & CMD_FLG_ADMA_SDMA) + req->trans_type = TRANS_ADMA2_SDMA_LIKE; + else if (sd_cmd->cmd_flag & CMD_FLG_ADMA3) + req->trans_type = TRANS_ADMA3; + else if (sd_cmd->cmd_flag & CMD_FLG_SDMA) + req->trans_type = TRANS_SDMA; + else if (sd_cmd->data) + req->trans_type = TRANS_PIO; + else + req->trans_type = TRANS_NONDATA; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARDCMD_TRACE, 0, + "cmd_exec_inner:cmdflag=%08X transtype=%d infmode=%d\n", + sd_cmd->cmd_flag, req->trans_type, req->inf_mode); + + req->cb_response = + sd_cmd->uhs2_cmd ? cmd_uhs2_response : cmd_legacy_response; + + /* generate intr flag according to transfer type */ + switch (req->trans_type) { + case TRANS_PIO: + if (sd_cmd->cmd_flag & CMD_FLG_TUNE) { + req->int_flag_wait = SDHCI_INT_DATA_AVAIL; + req->int_flag_err = SDHCI_INT_ERR_TUNING_CMD; + } else { + req->int_flag_err = SDHCI_INT_ERR_DATA_CMD; + req->int_flag_wait = + SDHCI_INT_CMD_COMP | SDHCI_INT_TRANSFER_COMP; + req->int_flag_wait |= + (data->dir == + DATA_DIR_IN) ? SDHCI_INT_DATA_AVAIL : + SDHCI_INT_SPACE_AVAIL; + req->cb_buffer_ready = cmd_piobuff_ready; + } + break; + case TRANS_SDMA: + req->int_flag_err = SDHCI_INT_ERR_DATA_CMD; + req->int_flag_wait = SDHCI_INT_SDMA_BITS; + req->cb_boundary = cmd_sdma_boundary; + req->cb_trans_complete = cmd_sdma_trans_done; + break; + case TRANS_ADMA2: + case TRANS_ADMA2_SDMA_LIKE: + req->int_flag_err = SDHCI_INT_ERR_ADMA_CMD; + req->int_flag_wait = SDHCI_INT_ADMA_BITS; + if (req->inf_mode != INF_NONE) { + req->int_flag_wait |= SDHCI_INT_DMA_END; + req->cb_boundary = cmd_adma2_inf_boundary; + } else { + if (req->trans_type == TRANS_ADMA2_SDMA_LIKE) + req->cb_trans_complete = + cmd_adma2_sdma_like_trans_done; + } + break; + case TRANS_ADMA3: + req->int_flag_wait = SDHCI_INT_TRANSFER_COMP; + req->int_flag_err = SDHCI_INT_ERR_ADMA_CMD; + req->cb_trans_complete = cmd_adma3_trans_done; + break; + + default: + { + req->int_flag_err = SDHCI_INT_ERR_NON_DATA; + req->int_flag_wait = SDHCI_INT_CMD_COMP; + if (sd_cmd->cmd_flag & CMD_FLG_R1B) + req->int_flag_wait |= SDHCI_INT_TRANSFER_COMP; + } + break; + } + + if (req->inf_mode != INF_NONE) { + /* Infinte case don't have transfer complete and command complete */ + req->int_flag_wait &= ~(SDHCI_INT_TRANSFER_COMP); + if (req->inf_mode == INF_CONTINUE) + req->int_flag_wait &= ~SDHCI_INT_CMD_COMP; + } + if (sd_cmd->cmd_flag & CMD_FLG_NO_TRANS) + req->int_flag_wait &= ~SDHCI_INT_TRANSFER_COMP; + + /* If hardware response check enabled */ + if (sd_cmd->hw_resp_chk) { + req->int_flag_wait &= ~(SDHCI_INT_CMD_COMP); + if (sd_cmd->uhs2_cmd == 0) + req->int_flag_err |= SDHCI_INT_RESP_ERROR; + } + + if (sd_cmd->uhs2_cmd) + req->int_flag_uhs2_err = SDHCI_UHS2_INT_ERR_ALL; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARDCMD_TRACE, 0, + "cmd_exec_inner:flag_wait=%08X err_wait=%08X uhs2_errwait=%08X\n", + req->int_flag_wait, req->int_flag_err, req->int_flag_uhs2_err); + + res = cmd_final_execute(card, sd_cmd, req, &sd_cmd->trans_reg[i]); + +end: + DbgInfo(MODULE_SD_HOST, FEATURE_CARDCMD_TRACE, 0, "Exit %s ret=%d\n", + __func__, res); + return res; + +} + +/* + * Function Name: cmd_execute_sync + * Abstract: This Function is used to issue sd command and wati sync + * + * Input: + * sd_card_t *card : The Command will send to which Card + * sd_command_t *sd_cmd: This parameter will + * contail card command info and reg info + * for adma3 case this reg don't need conatin reg info + * req_callback func_done : call back function to end Srb if necessary + * Output: + * Whether + * + * Return value: + * + * If the routine succeeds, it must return TRUE + * otherwize reutrn FALSE + * + */ +bool cmd_execute_sync(sd_card_t *card, sd_command_t *sd_cmd, + req_callback func_done) +{ + return cmd_execute_sync_inner(card, &card->cmd_req, sd_cmd, func_done, + NULL); +} + +/* + * Function Name: cmd_execute_sync2 + * Abstract: This Function is used to issue sd command and wait sync2 + * Input: + * sd_card_t *card : The Command will send to which Card + * sd_command_t *sd_cmd: This parameter will + * contail card command info and reg info + * for adma3 case this reg don't need conatin reg info + * host_cmd_req_t *req: Caller need to allocate mem for this pointer + * req_callback func_done : call back function to end Srb if necessary + * Output: + * Whether + * + * Return value: + * + * If the routine succeeds, it must return TRUE + * otherwize reutrn FALSE + * + */ +bool cmd_execute_sync2(sd_card_t *card, sd_command_t *sd_cmd, + host_cmd_req_t *req, req_callback func_done) +{ + return cmd_execute_sync_inner(card, req, sd_cmd, func_done, NULL); +} + +/* + * Function Name: cmd_execute_async3 + * Abstract: This Function is used to issue sd command and + * assign a callback immediately follow issue CMD + * Input: + * sd_card_t *card : The Command will send to which Card + * sd_command_t *sd_cmd: This parameter will + * contail card command info and reg info + * for adma3 case this reg don't need conatin reg info + * host_cmd_req_t *req: Caller need to allocate mem for this pointer + * req_callback func_done : call back function to end Srb if necessary + * issue_post_callback post_cb: call back function for async operator + * Output: + * Whether + * + * Return value: + * + * If the routine succeeds, it must return TRUE + * otherwize reutrn FALSE + * + */ +bool cmd_execute_sync3(sd_card_t *card, sd_command_t *sd_cmd, + host_cmd_req_t *req, req_callback func_done, + issue_post_callback post_cb) +{ +#if (GBL_ASYNC_PERFEATCH_IO) + return cmd_execute_sync_inner(card, req, sd_cmd, func_done, post_cb); +#else + return cmd_execute_sync_inner(card, req, sd_cmd, func_done, NULL); +#endif +} + +/* + * Function Name: cmd_dat_line_chk + * Abstract: This Function is to check whether card is present or not + * + * Input: + * sd_card_t *card: The Command will send to which Card + * sd_command_t *sd_cmd: This parameter will + * contail card command info and reg info + * for adma3 case this reg don't need conatin reg info + * Output: + * + * Return value: + * + * If the routine succeeds, it must return TRUE + * otherwize reutrn FALSE + * + */ +bool cmd_dat_line_chk(sd_card_t *card, sd_command_t *sd_cmd) +{ + u32 reg = sdhci_readl(card->host, SDHCI_PRESENT_STATE); + + if (reg & SDHCI_CMD_INHIBIT) + return FALSE; + if (sd_cmd->data && (reg & SDHCI_DATA_INHIBIT)) + return FALSE; + return TRUE; +} + +/* + * Function Name: cmd_can_use_inf + * Abstract: This Function is to check whether next transfer use infinite or not + * + * Input: + * sd_card_t *card, + * u32 sec_addr: the start address want to transfer + * u32 sec_cnt: the sector count want to transfer + * + * Output: + * + * Return value: + * + * If the routine succeeds, it must return TRUE + * otherwize reutrn FALSE + * + */ +u32 cmd_can_use_inf(sd_card_t *card, e_data_dir dir, u32 sec_addr, u32 sec_cnt) +{ + u32 n_fcu = 1; + u32 flg = 0; + bool buhs2 = card->card_type == CARD_UHS2 ? TRUE : FALSE; + + if (card->inf_trans_enable == 0) + goto exit; + + if (buhs2) { + n_fcu = card->uhs2_info.uhs2_setting.n_fcu; + if (n_fcu == 0) + n_fcu = 256; + if ((sec_cnt % n_fcu) != 0) + goto exit; + } + + if (sec_addr != card->last_sect || dir != card->last_dir) { + flg = CMD_FLG_INF_BUILD; + goto exit; + } + + if (card->has_built_inf) + flg = CMD_FLG_INF_CON; + else + flg = CMD_FLG_INF_BUILD; + +exit: + return flg; +} + +void cmd_set_auto_cmd_flag(sd_card_t *card, u32 *cmd_flag) +{ + if (card->card_type != CARD_UHS2 && (*cmd_flag & CMD_FLG_MULDATA)) { + /* sd card if support cmd23 */ + if ((card->card_type == CARD_SD + && card->info.scr.cmd_support & 0x2)) { + *cmd_flag |= CMD_FLG_AUTO23; + } else + *cmd_flag |= CMD_FLG_AUTO12; + } + +} + +/* + * Function Name: cmd_is_adma_error + * Abstract: This Function is to test error interrupt handler + * + * Input: + * sd_command_t *sd_cmd + * + * Output: + * + * Return value: + * + * True: Adma error occur + * + */ +bool cmd_is_adma_error(sd_command_t *sd_cmd) +{ + if (sd_cmd == NULL) + return FALSE; + + if ((sd_cmd->err.legacy_err_reg & SDHCI_INT_ADMA_ERROR) || + (sd_cmd->uhs2_cmd + && (sd_cmd->err.uhs2_err_reg & SDHCI_UHS2_INT_ADMA))) + return TRUE; + return FALSE; +} diff --git a/drivers/scsi/bht/host/handler.h b/drivers/scsi/bht/host/handler.h new file mode 100644 index 000000000000..743a4065564b --- /dev/null +++ b/drivers/scsi/bht/host/handler.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: handler.h + * + * Abstract: Handler for interrupt and dma buffer manager apis + * + * Version: 1.00 + * + * Author: Peter.Guo + * + * Environment: OS Independent + * + * History: + * + * 9/5/2014 Creation Peter.guo + */ + +u32 cmd_legacy_response(void *card, void *host_request); + +u32 cmd_uhs2_response(void *card, void *host_request); + +u32 cmd_piobuff_ready(void *card, void *host_request); + +u32 cmd_sdma_boundary(void *card, void *host_request); + +u32 cmd_adma2_inf_boundary(void *card, void *host_request); + +u32 cmd_adma2_sdma_like_trans_done(void *card, void *host_request); + +u32 cmd_adma3_trans_done(void *card, void *host_request); + +u32 cmd_sdma_trans_done(void *card, void *host_request); + +bool irq_poll_cmd_done(bht_dev_ext_t *pdx, completion_t *p, s32 timeout_ms); diff --git a/drivers/scsi/bht/host/host.c b/drivers/scsi/bht/host/host.c new file mode 100644 index 000000000000..aefb3680637d --- /dev/null +++ b/drivers/scsi/bht/host/host.c @@ -0,0 +1,2947 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: host.c + * + * Abstract: Include host related common functions. + * + * Version: 1.00 + * + * Author: Samuel + * + * Environment: OS Independent + * + * History: + * + * 9/2/2014 Creation Samuel + */ +#include "../include/basic.h" +#include "../include/host.h" +#include "hostreg.h" +#include "../include/hostapi.h" +#include "../include/debug.h" +#include "hostven.h" +#include "../include/util.h" +#include "../include/hostvenapi.h" +#define UHS2_VENCNT_OFFSET 0x10 +#define UHS2_DAT3MD_OFFSET 0x14 +#define UHS2_EXTCNT_OFFSET 0x18 +#define UHS2_VENGIO_OFFSET 0x20 +#define GPIO2 0 +#define GPIO3 1 +#define GPIO_HIGHT 1 +#define GPIO_LOW 0 +#define START_BIT 0 +#define END_BIT 1 +#define SPECIAL_PATTERN 2 +#define UHS1_BIT_EN (1 << 0) +#define UHS2_BIT_EN (1 << 1) +#define SD70_BIT_EN (1 << 2) +#define VDD1_BIT_EN (1 << 4) +#define VDD2_BIT_EN (1 << 5) +static void host_uhs2_init_capability(sd_host_t *host); +static void host_pll_enable(sd_host_t *host, bool enable); +static void host_uhs2_reg_clean(sd_host_t *host); +static byte host_get_datline_state(sd_host_t *host); +/* static byte host_get_cmdline_state(sd_host_t *host); */ +static u16 host_check_1_8v_signal(sd_host_t *host); +void host_enable_clock(sd_host_t *host, bool on); +static bool host_uhs2_wait_dmt(sd_host_t *host); + +static void host_update_clock(sd_host_t *host, u32 basediv) +{ + + /* 1. Clear Divider */ + u32 reg; + + sdhci_and32(host, SDHCI_CLOCK_CONTROL, SDHCI_DIVIDER_CLEAR); + reg = (((basediv << 8) & 0xff00) | ((basediv & 0x300) >> 2)); + sdhci_or32(host, SDHCI_CLOCK_CONTROL, reg); +} + +/* + * + * Function Name: host_set_highspeed + * + * Abstract: + * + * 1. Set Host to Highspeed or clear Highspeed + * + * Input: + * + * sd_host_t *host: Pointer to the host structure + * bool on: Highspeed on + * + * Output: + * + * None + * + * Return value: + * + * None. + * + * Notes: + * + * Caller: sd_switch_function_set, sd_legacy_init + */ +void host_set_highspeed(sd_host_t *host, bool on) +{ + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, on=%d\n", __func__, on); + + if (on) + sdhci_or32(host, SDHCI_HOST_CONTROL, SDHCI_CTRL_HISPD); + else + sdhci_and32(host, SDHCI_HOST_CONTROL, ~(SDHCI_CTRL_HISPD)); + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + +} + +void host_set_tuning_mode(sd_host_t *host, bool hw_mode) +{ + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, hw_mode=%d\n", __func__, hw_mode); + + /* 1. Set the HW/SW tuning mode */ + if (hw_mode) { + /* Set driver HW mode here, 0x110[4] set 1'b0 to enable HW mode */ + sdhci_and16(host, SDHCI_VEN_SPEC_CTRL, ~(SDHCI_HW_TUNING)); + } else { + /* Set driver SW mode here, 0x110[4] set 1'b1 to enable SW mode */ + sdhci_or16(host, SDHCI_VEN_SPEC_CTRL, (SDHCI_HW_TUNING)); + } + + /* 2. Trigger tuning phase */ + sdhci_or16(host, SDHCI_HOST_CONTROL2, SDHCI_CTRL_EXEC_TUNING); + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + +} + +bool host_chk_tuning_comp(sd_host_t *host, bool hwtuning) +{ + u16 regval; + u32 delay_ms = 1; + loop_wait_t wait; + bool ret = FALSE; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, hwtuning=%d\n", __func__, hwtuning); + + if (hwtuning) { + /* Check HW tuning complete */ + util_init_waitloop(host->pdx, 100, delay_ms * 1000, &wait); + do { + /* Check tuning complete */ + regval = sdhci_readw(host, SDHCI_HOST_CONTROL2); + if ((regval & SDHCI_CTRL_EXEC_TUNING) == 0) { + regval = sdhci_readw(host, SDHCI_HOST_CONTROL2); + if ((regval & SDHCI_CTRL_TUNED_CLK)) { + DbgInfo(MODULE_SD_HOST, + FEATURE_CARD_INIT, NOT_TO_RAM, + "Tuning function %d OK!\n"); + ret = TRUE; + } else { + DbgErr(" - Tuning failed.\n"); + } + break; + } + /* Delay 1ms */ + os_mdelay(delay_ms); + } while (!util_is_timeout(&wait)); + } else { + regval = sdhci_readw(host, SDHCI_HOST_CONTROL2); + if ((regval & SDHCI_CTRL_EXEC_TUNING) == 0) { + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Tuning function %d OK!\n"); + ret = TRUE; + } + } + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + return ret; +} + +void host_enable_pll_software_reset(sd_host_t *host, bool on) +{ + loop_wait_t wait; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s on=%d\n", __func__, on); + + /* Force L0 before PLL reset:0x3E4[23] = 1 */ + pci_orl(host, 0x3e4, BIT23); + if (on) { + sdhci_or32(host, SDHCI_DLL_WATCH_DOG, (SDHCI_PLL_RESET)); + util_init_waitloop(host->pdx, 5000, 10, &wait); + while (!util_is_timeout(&wait)) { + if ((sdhci_readl(host, SDHCI_DLL_WATCH_DOG) & + SDHCI_PLL_UNLOCKBIT) == 0) { + break; + } + } + } else { + sdhci_and32(host, SDHCI_DLL_WATCH_DOG, ~(SDHCI_PLL_RESET)); + util_init_waitloop(host->pdx, 5000, 10, &wait); + while (!util_is_timeout(&wait)) { + if (sdhci_readl(host, SDHCI_DLL_WATCH_DOG) & + SDHCI_PLL_UNLOCKBIT) { + break; + } + } + } + /* Cancel force L0 before PLL reset */ + pci_andl(host, 0x3e4, ~BIT23); + os_mdelay(10); + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + +} + +void host_change_clock(sd_host_t *host, u32 value) +{ + u32 dmdn, basediv; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, value=0x%x\n", __func__, value); + basediv = value & 0x7FFF; + dmdn = ((value & 0xFFFF0000) >> 16); + + host_enable_clock(host, FALSE); + host_enable_pll_software_reset(host, TRUE); + hostven_update_dmdn(host, dmdn); + host_update_clock(host, basediv); + host_enable_pll_software_reset(host, FALSE); + host_enable_clock(host, TRUE); + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +void host_init_clock(sd_host_t *host, u32 value) +{ + u32 dmdn, basediv; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, value=0x%x\n", __func__, value); + basediv = value & 0x7FFF; + dmdn = ((value & 0xFFFF0000) >> 16); + + hostven_update_dmdn(host, dmdn); + host_update_clock(host, basediv); + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +void host_init_400k_clock(sd_host_t *host) +{ + if (host->cfg == NULL || host->cfg->dmdn_tbl == NULL) { + DbgErr("Host cfg is null\n"); + return; + } + + host_init_clock(host, host->cfg->dmdn_tbl[FREQ_400K_START_INDEX]); +} + +static void host_enable_clock_nodelay(sd_host_t *host, bool on) +{ + loop_wait_t wait; + u32 delay_us = 1; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_OPS | FEATURE_CARD_INIT, + NOT_TO_RAM, "Enter %s on=%d\n", __func__, on); + if (on == FALSE) { + util_init_waitloop(host->pdx, 5000, delay_us, &wait); + while (!util_is_timeout(&wait)) { + if ((sdhci_readl(host, SDHCI_PRESENT_STATE) & + (SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT)) == 0) + break; + /* avoid long wait for 7.0 SD driver switch to NVMe */ + else if (host->sd_express_flag == TRUE) + break; + else if (sdhci_readl(host, SDHCI_PRESENT_STATE) == + 0xffffffff) + break; + + os_udelay(delay_us); + } + sdhci_and16(host, SDHCI_CLOCK_CONTROL, ~(SDHCI_CLOCK_CARD_EN)); + } else { + /* enable SD clock */ + sdhci_or32(host, SDHCI_CLOCK_CONTROL, (SDHCI_CLOCK_CARD_EN)); + } + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_OPS | FEATURE_CARD_INIT, + NOT_TO_RAM, "Exit %s\n", __func__); +} + +void host_enable_clock(sd_host_t *host, bool on) +{ + u16 reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s on=%d\n", __func__, on); + + if (on && (reg & SDHCI_CLOCK_CARD_EN)) + goto exit; + else if ((on == 0) && (!(reg & SDHCI_CLOCK_CARD_EN))) + goto exit; + + host_enable_clock_nodelay(host, on); + +exit: + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +/* + * + * Function Name: host_dma_select + * + * Abstract: + * + * 1. select HOST dma mode + * + * Input: + * + * sd_host_t *host: Pointer to the host structure + * e_trans_type mode + * + * Output: + * + * None + * + * Return value: + * + * None. + * + * Notes: + * + * Caller: sd_legacy_init + */ + +void host_dma_select(sd_host_t *host, e_trans_type mode) +{ + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + sdhci_and32(host, SDHCI_HOST_CONTROL, ~(SDHCI_CTRL_DMA_MASK)); + switch (mode) { + case TRANS_SDMA: + sdhci_or32(host, SDHCI_HOST_CONTROL, SDHCI_CTRL_SDMA); + break; + case TRANS_ADMA2: + sdhci_or32(host, SDHCI_HOST_CONTROL, SDHCI_CTRL_ADMA32); + break; + case TRANS_ADMA3: + sdhci_or32(host, SDHCI_HOST_CONTROL, SDHCI_CTRL_ADMA64); + break; + default: + sdhci_or32(host, SDHCI_HOST_CONTROL, SDHCI_CTRL_ADMA32); + break; + } + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + +} + +void host_set_uhs_mode(sd_host_t *host, byte uhs_mode) +{ + u16 reg = 0; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, access_mode=%d\n", __func__, uhs_mode); + + reg = sdhci_readw(host, SDHCI_HOST_CONTROL2); + reg &= ~SDHCI_CTRL_UHS_MASK; + reg |= uhs_mode; + sdhci_writew(host, SDHCI_HOST_CONTROL2, reg); + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + +} + +/* + * + * Function Name: host_set_buswidth + * + * Abstract: + * + * 1. Set BUS WIDTH(4bit or 1bit) Function + * + * Input: + * + * sd_host_t *host: Pointer to the host structure + * bool bus_width_4: 4Bit or not. + * + * Output: + * + * None + * + * Return value: + * + * None. + * + * Notes: + * + * Caller: sd_legacy_init + */ +void host_set_buswidth(sd_host_t *host, e_bus_width width) +{ + u16 reg; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, width=%d\n", __func__, width); + + reg = sdhci_readw(host, SDHCI_HOST_CONTROL); + reg &= ~(SDHCI_CTRL_4BITBUS | SDHCI_CTRL_8BITBUS); + switch (width) { + case BUS_WIDTH4: + reg |= SDHCI_CTRL_4BITBUS; + break; + case BUS_WIDTH8: + reg |= SDHCI_CTRL_8BITBUS; + break; + default: + break; + } + + sdhci_writew(host, SDHCI_HOST_CONTROL, reg); + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + +} + +static byte host_get_datline_state(sd_host_t *host) +{ + u32 data_line_state = 0; + + data_line_state = sdhci_readl(host, SDHCI_PRESENT_STATE); + data_line_state &= SDHCI_DATA_LVL_MASK; + data_line_state = data_line_state >> SDHCI_DATA_LVL_SHIFT; + + return (byte) data_line_state; +} + +/* + * static byte host_get_cmdline_state(sd_host_t *host) + * { + * u32 regval = 0; + * + * regval = sdhci_readl(host, SDHCI_PRESENT_STATE); + * regval &= SDHCI_CMD_LVL_MASK; + * regval = regval >> SDHCI_CMD_LVL_SHIFT; + * + * return (byte) regval; + * } + */ + +void host_1_8v_sig_set(sd_host_t *host, bool enable) +{ + u16 regval; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, enable=%d\n", __func__, enable); + + if (enable) { + regval = sdhci_readw(host, SDHCI_HOST_CONTROL2); + regval |= SDHCI_CTRL_VDD_180; + sdhci_writew(host, SDHCI_HOST_CONTROL2, regval); + } else { + regval = sdhci_readw(host, SDHCI_HOST_CONTROL2); + regval &= ~(SDHCI_CTRL_VDD_180); + sdhci_writew(host, SDHCI_HOST_CONTROL2, regval); + } + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +void host_sig_vol_set(sd_host_t *host, e_sig_vol sig_vol) +{ + u16 regval; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, sig_vol=%d\n", __func__, sig_vol); + + /* 0x3e[3], clear 1.8V Signal Voltage */ + regval = sdhci_readw(host, SDHCI_HOST_CONTROL2); + regval &= ~(SDHCI_CTRL_VDD_180); + sdhci_writew(host, SDHCI_HOST_CONTROL2, regval); + + /* 0x1cc[31], clear 1.2V Signal Voltage */ + sdhci_and32(host, SDHCI_DLL_WATCH_DOG, ~(SDHCI_CTRL_VDD2_120)); + + if (sig_vol == SIG_VOL_18) { + regval = sdhci_readw(host, SDHCI_HOST_CONTROL2); + regval |= SDHCI_CTRL_VDD_180; + sdhci_writew(host, SDHCI_HOST_CONTROL2, regval); + } else if (sig_vol == SIG_VOL_12) { + regval = sdhci_readw(host, SDHCI_HOST_CONTROL2); + regval |= SDHCI_CTRL_VDD_180; + sdhci_writew(host, SDHCI_HOST_CONTROL2, regval); + + sdhci_or32(host, SDHCI_DLL_WATCH_DOG, SDHCI_CTRL_VDD2_120); + } + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +static u16 host_check_1_8v_signal(sd_host_t *host) +{ + u16 regval; + + regval = sdhci_readw(host, SDHCI_HOST_CONTROL2); + regval &= SDHCI_CTRL_VDD_180; + return regval; +} + +/* + * static bool host_check_voltage_stable(sd_host_t *host) + * { + * bool ret = FALSE; + * u32 delay_ms = 1; + * loop_wait_t wait; + * + * if (host->feature.hw_41_supp == 0) { + * ret = TRUE; + * goto exit; + * } + * + * util_init_waitloop(host->pdx, 50, delay_ms * 1000, &wait); + * while (!util_is_timeout(&wait)) { + * if ((sdhci_readl(host, SDHCI_PRESENT_STATE) & (1 << 25)) != 0) { + * DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + * "check voltage stable!\n"); + * ret = TRUE; + * break; + * } + * os_mdelay(delay_ms); + * } + * exit: + * return ret; + * } + */ + +void host_led_ctl(sd_host_t *host, bool on) +{ + if (on) { + host->led_on = TRUE; + sdhci_or16(host, SDHCI_HOST_CONTROL, SDHCI_CTRL_LED); + if (host->chip_type == CHIP_SEABIRD) + pci_orl(host, 0xd4, 0x40); + } else { + host->led_on = FALSE; + sdhci_and16(host, SDHCI_HOST_CONTROL, ~SDHCI_CTRL_LED); + if (host->chip_type == CHIP_SEABIRD) + pci_andl(host, 0xd4, ~0x40); + } +} + +/* + * + * Function Name: host_set_vdd1_power + * + * Abstract: + * + * 1. Set SD Bus Voltage Select for VDD1 + * 2. VDD1 power on/off + * + * Input: + * + * sd_host_t *host: Pointer to the host structure + * bool on_off: power On or Off. + * u32 vol_sel: + * + * Output: + * + * None + * + * Return value: + * + * None. + * + * Notes: + * + * Caller: sd_legacy_init + */ +void host_set_vdd1_power_nodelay(sd_host_t *host, bool on, u32 vol_sel) +{ + u16 regval; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT | FEATURE_CARD_OPS, + NOT_TO_RAM, "Enter %s, on=%d, vol_sel=0x%x\n", __func__, on, + vol_sel); + + regval = sdhci_readw(host, SDHCI_HOST_CONTROL); + + if (on) { + /* Clear the SD Bus Voltage Select for VDD1 */ + regval &= ~(SDHCI_POWER_VDD1_MASK); + + /* Select VDD1 voltage */ + if (vol_sel == SDHCI_POWER_VDD1_180) { + regval |= SDHCI_POWER_VDD1_180; + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Set VDD1 Voltage Select to 1.8V\n"); + + } else if (vol_sel == SDHCI_POWER_VDD1_300) { + regval |= SDHCI_POWER_VDD1_300; + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Set VDD1 Voltage Select to 3.0V\n"); + } else if (vol_sel == SDHCI_POWER_VDD1_330) { + regval |= SDHCI_POWER_VDD1_330; + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Set VDD1 Voltage Select to 3.3V\n"); + } + /* Set SD Bus Power Select for VDD1 and Power ON VDD1. */ + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Set VDD1 Power ON\n"); + sdhci_writew(host, SDHCI_HOST_CONTROL, + regval | SDHCI_POWER_VDD1_ON); + + } else { + /* Power off the VDD1 */ + regval &= ~(SDHCI_POWER_VDD1_ON | SDHCI_POWER_VDD1_MASK); + sdhci_writew(host, SDHCI_HOST_CONTROL, regval); + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Set VDD1 Power OFF\n"); + + } + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT | FEATURE_CARD_OPS, + NOT_TO_RAM, "Exit %s\n", __func__); +} + +void host_set_vdd1_power(sd_host_t *host, bool on, u32 vol_sel) +{ + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, on=%d, vol_sel=0x%x\n", __func__, on, vol_sel); + + host_set_vdd1_power_nodelay(host, on, vol_sel); + if (on) { + os_mdelay(host->cfg->timeout_item.power_wait_time.power_on_wait_ms); + host_chk_ocb_occur(host); + } else { + os_mdelay(host->cfg->timeout_item.power_wait_time.power_off_wait_ms); + } + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +/* + * + * Function Name: host_set_vdd2_power + * + * Abstract: + * + * 1. Set SD Bus Voltage Select for VDD2 + * 2. VDD2 power on/off + * + * Input: + * + * sd_host_t *host: Pointer to the host structure + * bool on_off: power On or Off. + * u32 vol_sel: + * + * Output: + * + * None + * + * Return value: + * + * None. + * + * Notes: + * + * Caller: sd_legacy_init + */ + +void host_set_vdd2_power(sd_host_t *host, bool on, u32 vol_sel) +{ + u16 regval; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, on=%d, vol_sel=0x%x\n", __func__, on, vol_sel); + + regval = sdhci_readw(host, SDHCI_HOST_CONTROL); + if (on) { + /* Clear the SD Bus Voltage Select for VDD2 */ + regval &= ~(SDHCI_POWER_VDD2_MASK); + + /* Select VDD2 voltage */ + if (vol_sel == SDHCI_POWER_VDD2_180) { + regval |= SDHCI_POWER_VDD2_180; + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Set VDD2 Voltage Select to 1.8V\n"); + + } else if (vol_sel == SDHCI_POWER_VDD2_120) { + regval |= SDHCI_POWER_VDD2_120; + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Set VDD2 Voltage Select to 1.2V\n"); + + } + /* Set SD Bus Power Select for VDD1 and Power ON VDD1. */ + sdhci_writew(host, SDHCI_HOST_CONTROL, + regval | SDHCI_POWER_VDD2_ON); + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Set VDD2 Power ON\n"); + + host_chk_ocb_occur(host); + } else { + /* Power off the VDD2 */ + regval &= ~(SDHCI_POWER_VDD2_ON | SDHCI_POWER_VDD2_MASK); + sdhci_writew(host, SDHCI_HOST_CONTROL, regval); + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Set VDD2 Power OFF\n"); + } + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +void host_set_vddx_power(sd_host_t *host, u8 vddx, bool on) +{ + cfg_vdd_power_source_item_t *cfg = + &host->cfg->host_item.vdd_power_source_item; + u16 regval; + u32 regval32; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + if (host->cfg->driver_item.camera_mode_ctrl_vdd1_vdd2_cd == 1) + goto camera_mode; + else + goto pc_mode; + +pc_mode: + + switch (vddx) { + case VDD1: + { + /* + * power source internal/external and polarity refer to: + * hosteven.c: hostven_bios_cfg() + */ + + if (on) { + + /* set power voltage */ + if (cfg->vdd1_voltage == VDDX_PWR_VOLTAGE_3V3) { + /* set vdd1 3.3V and power on it */ + regval = + sdhci_readw(host, + SDHCI_POWER_CONTROL); + regval |= (0xF << 0); + sdhci_writew(host, SDHCI_POWER_CONTROL, + regval); + DbgInfo(MODULE_SD_HOST, + FEATURE_CARD_INIT, NOT_TO_RAM, + "Set VDD1 Power On\n"); + + } else { + DbgErr("VDD1 only support 3.3V!\n"); + } + + /* + * delay to avoid 3.3V power switch signal level issue. + * no need this delay for VDD2, VDD3 + */ + os_mdelay(host->cfg->timeout_item.power_wait_time.power_on_wait_ms); + } else { + /* set vdd1 power off */ + regval = sdhci_readw(host, SDHCI_POWER_CONTROL); + regval &= ~(0xF << 0); + sdhci_writew(host, SDHCI_POWER_CONTROL, regval); + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, + NOT_TO_RAM, "Set VDD1 Power Off\n"); + + os_mdelay(host->cfg->timeout_item.power_wait_time.power_off_wait_ms); + } + + } + break; + + case VDD2: + { + /* + * power source internal/external and polarity refer to: + * hosteven.c: hostven_bios_cfg() + */ + + if (on) { + /* set power voltage */ + regval = sdhci_readw(host, SDHCI_POWER_CONTROL); + if (cfg->vdd2_voltage == VDDX_PWR_VOLTAGE_1V8) { + /* VDD2 1.8V and power on. */ + regval &= ~(0xF << 4); + regval |= (0xB << 4); + DbgInfo(MODULE_SD_HOST, + FEATURE_CARD_INIT, NOT_TO_RAM, + "Set VDD2 Power On 1.8V\n"); + } else if (cfg->vdd2_voltage == + VDDX_PWR_VOLTAGE_1V2) { + /* VDD2 1.2V and power on. */ + regval &= ~(0xF << 4); + regval |= (0x9 << 4); + DbgInfo(MODULE_SD_HOST, + FEATURE_CARD_INIT, NOT_TO_RAM, + "Set VDD2 Power On 1.2V\n"); + } else { + DbgErr + ("VDD2 only support 1.8V or 1.2V!\n"); + } + sdhci_writew(host, SDHCI_POWER_CONTROL, regval); + + /* set on-off control by gpio */ + if (cfg->vdd2_use_gpio1) { + /* use gpio1 as on-off control */ + regval = + ven_readw(host, + SDBAR1_GPIO_FUNC_GPIOCTRL_510); + + /* active high */ + if (cfg->vdd2_onoff_polarity == + VDDX_POLARITY_ACTIVE_HIGH) { + /* enable gpio1 && output high. */ + regval |= (1 << 3) | (1 << 5); + } else { + /* enable gpio1 && output low. */ + regval |= (1 << 3); + regval &= ~(1 << 5); + } + ven_writew(host, + SDBAR1_GPIO_FUNC_GPIOCTRL_510, + regval); + + if (host->sd_express_flag == TRUE) { + /* + * VDD2 set GPIO power control inverter for + * SD7.0 switch to SD driver + * hardware auto power-off. + */ + regval = + ven_readw(host, + SDBAR1_GPIO_FUNC_GPIOCTRL_510); + regval |= (1 << 7); + ven_writew(host, + SDBAR1_GPIO_FUNC_GPIOCTRL_510, + regval); + } + + } + + } + /* power off */ + else { + + /* common part for all power source cases */ + regval = sdhci_readw(host, SDHCI_POWER_CONTROL); + /* set vdd2 power off */ + regval &= ~(0xF << 4); + sdhci_writew(host, SDHCI_POWER_CONTROL, regval); + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, + NOT_TO_RAM, "Set VDD2 Power Off\n"); + + /* use gpio1 as on-off control */ + if (cfg->vdd2_use_gpio1) { + regval = + ven_readw(host, + SDBAR1_GPIO_FUNC_GPIOCTRL_510); + /* active high */ + if (cfg->vdd2_onoff_polarity == + VDDX_POLARITY_ACTIVE_HIGH) { + /* gpio1 output low to set power off. */ + regval &= ~(1 << 5); + } else { + /* gpio1 output high to set power off. */ + regval |= (1 << 5); + } + ven_writew(host, + SDBAR1_GPIO_FUNC_GPIOCTRL_510, + regval); + + if (host->sd_express_flag == TRUE) { + /* VDD2 clear GPIO power control */ + regval = + ven_readw(host, + SDBAR1_GPIO_FUNC_GPIOCTRL_510); + regval &= ~(1 << 7); + ven_writew(host, + SDBAR1_GPIO_FUNC_GPIOCTRL_510, + regval); + } + + } + + } + + } + break; + + case VDD3: + { + /* + * power source internal/external and polarity refer to: + * hosteven.c: hostven_bios_cfg() + */ + if (on) { + + /* set power enable, Enable VDD3 */ + regval32 = + sdhci_readl(host, SDHCI_DRIVER_CTRL_REG); + regval32 |= (1 << 30); + sdhci_writel(host, SDHCI_DRIVER_CTRL_REG, + regval32); + + /* set on-off control by gpio */ + regval = + ven_readw(host, + SDBAR1_GPIO_FUNC_GPIOCTRL_510); + /* active high */ + if (cfg->vdd3_onoff_polarity == + VDDX_POLARITY_ACTIVE_HIGH) { + /* Set GPIO2 high. */ + regval |= (1 << 13); + } else { + /* Set GPIO2 low. */ + regval &= ~(1 << 13); + } + ven_writew(host, SDBAR1_GPIO_FUNC_GPIOCTRL_510, + regval); + + if (host->sd_express_flag == TRUE) { + /* VDD3 set GPIO power control inverter. */ + regval = + ven_readw(host, + SDBAR1_GPIO_FUNC_GPIOCTRL_510); + regval |= (1 << 15); + ven_writew(host, + SDBAR1_GPIO_FUNC_GPIOCTRL_510, + regval); + } + } + /* power off */ + else { + + /* set power disable */ + regval32 = + sdhci_readl(host, SDHCI_DRIVER_CTRL_REG); + /* disable VDD3 */ + regval32 &= ~(1 << 30); + sdhci_writel(host, SDHCI_DRIVER_CTRL_REG, + regval32); + + /* set on-off control by gpio */ + regval = + ven_readw(host, + SDBAR1_GPIO_FUNC_GPIOCTRL_510); + /* active high */ + if (cfg->vdd3_onoff_polarity == + VDDX_POLARITY_ACTIVE_HIGH) { + /* Set GPIO2 low. */ + regval &= ~(1 << 13); + } else { + /* Set GPIO2 high. */ + regval |= (1 << 13); + } + ven_writew(host, SDBAR1_GPIO_FUNC_GPIOCTRL_510, + regval); + + if (host->sd_express_flag == TRUE) { + /* VDD3 clear GPIO power control inverter. */ + regval = + ven_readw(host, + SDBAR1_GPIO_FUNC_GPIOCTRL_510); + regval &= ~(1 << 15); + ven_writew(host, + SDBAR1_GPIO_FUNC_GPIOCTRL_510, + regval); + } + } + + } + break; + + default: + break; + } + /* pc_mode exit */ + goto exit; + +camera_mode: + + switch (vddx) { + case VDD1: + { + + if (on) { + ven_or16(host, SDBAR1_GPIO_FUNC_GPIOCTRL_510, + (1 << 13)); + + /* set power voltage */ + if (cfg->vdd1_voltage == VDDX_PWR_VOLTAGE_3V3) { + regval = + sdhci_readw(host, + SDHCI_POWER_CONTROL); + /* set vdd1 3.3V and power on it */ + regval |= (0xF << 0); + sdhci_writew(host, SDHCI_POWER_CONTROL, + regval); + DbgInfo(MODULE_SD_HOST, + FEATURE_CARD_INIT, NOT_TO_RAM, + "Set VDD1 Power On\n"); + + } else { + DbgErr("VDD1 only support 3.3V!\n"); + } + /* + * delay to avoid 3.3V power switch signal level issue. + * no need this delay for VDD2, VDD3 + */ + os_mdelay(host->cfg->timeout_item.power_wait_time.power_on_wait_ms); + + } else { + ven_and16(host, SDBAR1_GPIO_FUNC_GPIOCTRL_510, + ~(1 << 13)); + + regval = sdhci_readw(host, SDHCI_POWER_CONTROL); + /* set vdd1 power off */ + regval &= ~(0xF << 0); + sdhci_writew(host, SDHCI_POWER_CONTROL, regval); + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, + NOT_TO_RAM, "Set VDD1 Power Off\n"); + os_mdelay(host->cfg->timeout_item.power_wait_time.power_off_wait_ms); + } + + } + break; + + case VDD2: + { + if (on) { + ven_or16(host, SDBAR1_GPIO_1_2_CTRL_REG_510, + (1 << 5)); + + /* set power voltage */ + regval = sdhci_readw(host, SDHCI_POWER_CONTROL); + if (cfg->vdd2_voltage == VDDX_PWR_VOLTAGE_1V8) { + /* VDD2 1.8V and power on. */ + regval &= ~(0xF << 4); + regval |= (0xB << 4); + DbgInfo(MODULE_SD_HOST, + FEATURE_CARD_INIT, NOT_TO_RAM, + "Set VDD2 Power On 1.8V\n"); + } else if (cfg->vdd2_voltage == + VDDX_PWR_VOLTAGE_1V2) { + /* VDD2 1.2V and power on. */ + regval &= ~(0xF << 4); + regval |= (0x9 << 4); + DbgInfo(MODULE_SD_HOST, + FEATURE_CARD_INIT, NOT_TO_RAM, + "Set VDD2 Power On 1.2V\n"); + } else { + DbgErr + ("VDD2 only support 1.8V or 1.2V!\n"); + } + sdhci_writew(host, SDHCI_POWER_CONTROL, regval); + + /* use gpio1 as on-off control */ + if (cfg->vdd2_use_gpio1) { + regval = + ven_readw(host, + SDBAR1_GPIO_FUNC_GPIOCTRL_510); + /* active high */ + if (cfg->vdd2_onoff_polarity == + VDDX_POLARITY_ACTIVE_HIGH) { + /* enable gpio1 && output high. */ + regval |= (1 << 3) | (1 << 5); + } else { + /* enable gpio1 && output low. */ + regval |= (1 << 3); + regval &= ~(1 << 5); + } + ven_writew(host, + SDBAR1_GPIO_FUNC_GPIOCTRL_510, + regval); + + if (host->sd_express_flag == TRUE) { + /* + * VDD2 set GPIO power control inverter for + * SD7.0 switch to SD driver + * hardware auto power-off. + */ + regval = + ven_readw(host, + SDBAR1_GPIO_FUNC_GPIOCTRL_510); + regval |= (1 << 7); + ven_writew(host, + SDBAR1_GPIO_FUNC_GPIOCTRL_510, + regval); + } + + } + + } else { + ven_and16(host, SDBAR1_GPIO_1_2_CTRL_REG_510, + ~(1 << 5)); + + regval = sdhci_readw(host, SDHCI_POWER_CONTROL); + /* set vdd2 power off */ + regval &= ~(0xF << 4); + sdhci_writew(host, SDHCI_POWER_CONTROL, regval); + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, + NOT_TO_RAM, "Set VDD2 Power Off\n"); + + /* use gpio1 as on-off control */ + if (cfg->vdd2_use_gpio1) { + regval = + ven_readw(host, + SDBAR1_GPIO_FUNC_GPIOCTRL_510); + /* active high */ + if (cfg->vdd2_onoff_polarity == + VDDX_POLARITY_ACTIVE_HIGH) { + /* gpio1 output low to set power off. */ + regval &= ~(1 << 5); + } else { + /* gpio1 output high to set power off. */ + regval |= (1 << 5); + } + ven_writew(host, + SDBAR1_GPIO_FUNC_GPIOCTRL_510, + regval); + + if (host->sd_express_flag == TRUE) { + /* VDD2 clear GPIO power control */ + regval = + ven_readw(host, + SDBAR1_GPIO_FUNC_GPIOCTRL_510); + regval &= ~(1 << 7); + ven_writew(host, + SDBAR1_GPIO_FUNC_GPIOCTRL_510, + regval); + } + } + } + } + break; + /* No VDD3 case for camera mode */ + default: + break; + } + /* camera_mode exit */ + goto exit; + +exit: + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +/* + * + * Function Name: host_get_vdd1_state + * + * Abstract: + * + * 1. Get VDD1 power on/off state + * + * Input: + * + * sd_host_t *host: Pointer to the host structure + * + * Output: + * + * TRUE: VDD1 power is ON. + * FALSE: VDD1 power is OFF. + * + * Return value: + * + * None. + * + * Notes: + * + * Caller: sd_legacy_init + */ + +bool host_get_vdd1_state(sd_host_t *host) +{ + u16 regval; + u16 vdd1_voltage_sel; + bool ret = FALSE; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + regval = sdhci_readw(host, SDHCI_HOST_CONTROL); + vdd1_voltage_sel = regval & 0xE; + + if (shift_bit_func_enable(host)) + goto camera_mode; + + /* pc_mode: */ + if (regval & SDHCI_POWER_VDD1_ON) + ret = TRUE; + else + ret = FALSE; + +camera_mode: + if ((regval & SDHCI_POWER_VDD1_ON) && (vdd1_voltage_sel != 0)) + ret = TRUE; + else + ret = FALSE; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "host get vdd1 state: Vdd1 is %s\n", ret ? "ON" : "OFF"); + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + return ret; +} + +void host_reset(sd_host_t *host, u32 resetmode) +{ + u32 delay_us = 1; + loop_wait_t wait; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, resetmode=0x%x\n", __func__, resetmode); + + sdhci_or32(host, SDHCI_CLOCK_CONTROL, resetmode); + util_init_waitloop(host->pdx, RESET_FOR_ALL_ABRT_TM, delay_us, &wait); + while (!util_is_timeout(&wait)) { + if ((sdhci_readl(host, SDHCI_CLOCK_CONTROL) & resetmode) == 0) + break; + else if (sdhci_readl(host, SDHCI_CLOCK_CONTROL) == 0xffffffff) + break; + + os_udelay(delay_us); + } + + /* SetSlowLTRRequire(host); */ + + /* + * host reset will clear host registers, include host power register 0x29 + * need to clear gpio registers if vdd is external power source. + */ + host_set_vddx_power(host, VDD2, POWER_OFF); + /* host_set_vddx_power(host, VDD3, POWER_OFF); */ + os_mdelay(50); + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +void host_cmddat_line_reset(sd_host_t *host) +{ + host_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); +} + +void host_int_sig_update(sd_host_t *host, u32 int_val) +{ + if (host->dump_mode == FALSE && host->poll_mode == FALSE) + sdhci_writel(host, SDHCI_SIGNAL_ENABLE, int_val); + else + sdhci_writel(host, SDHCI_SIGNAL_ENABLE, 0); +} + +void host_uhs2_err_sig_update(sd_host_t *host, u32 int_val) +{ + if (host->dump_mode == FALSE && host->poll_mode == FALSE) + sdhci_writel(host, SDHCI_UHS2_ERRINT_SIG_EN, int_val); + else + sdhci_writel(host, SDHCI_UHS2_ERRINT_SIG_EN, 0); +} + +static void host_int_sig_en(sd_host_t *host, u32 int_val) +{ + + u32 reg = sdhci_readl(host, SDHCI_SIGNAL_ENABLE); + + host_int_sig_update(host, reg | int_val); +} + +void host_int_sig_dis(sd_host_t *host, u32 int_val) +{ + u32 reg = sdhci_readl(host, SDHCI_SIGNAL_ENABLE); + + host_int_sig_update(host, reg & (~int_val)); +} + +void host_int_clr_status(sd_host_t *host) +{ + sdhci_writel(host, SDHCI_INT_STATUS, 0xffffffff); +} + +void host_int_dis_sig_all(sd_host_t *host, bool all) +{ + /* Disable all interrupts except card insert/remove */ + host_int_sig_dis(host, SDHCI_INT_ALL_MASK); + host_int_sig_en(host, SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE); + +} + +void host_internal_clk_setup(sd_host_t *host, bool on) +{ + u32 timeout = 10000; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, on=%d\n", __func__, on); + + if (on) { + /* 1. Set Internal Clock Enable */ + sdhci_or32(host, SDHCI_CLOCK_CONTROL, SDHCI_CLOCK_INT_EN); + while (timeout) { + if (sdhci_readl(host, SDHCI_CLOCK_CONTROL) & + SDHCI_CLOCK_INT_STABLE) + goto exit; + else if (sdhci_readl(host, SDHCI_CLOCK_CONTROL) == + 0xffffffff) + goto exit; + + os_udelay(1); + timeout--; + } + + } else { + sdhci_and32(host, SDHCI_CLOCK_CONTROL, ~(SDHCI_CLOCK_INT_EN)); + } + +exit: + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +/* this function called to stop host power */ +void host_uninit(sd_host_t *host, bool disable_all_int) +{ + if (host_check_lost(host)) + return; + + host_poweroff(host, CARD_NONE); + + host_int_dis_sig_all(host, disable_all_int); +} + +void host_poweroff(sd_host_t *host, e_card_type type) +{ + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, type=%d\n", __func__, type); + + hostven_set_tuning_phase(host, 0, 0, TRUE); + host_enable_clock(host, FALSE); + + if (shift_bit_func_enable(host)) + set_pattern_value(host, 0x00); + + host_set_vddx_power(host, VDD2, POWER_OFF); + + if (host_get_vdd1_state(host)) + host_set_vddx_power(host, VDD1, POWER_OFF); + + if (type == CARD_UHS2 || host->uhs2_flag) { + host_uhs2_reg_clean(host); + } else if (type == CARD_SD) { + host_set_highspeed(host, FALSE); + host_set_uhs_mode(host, 0); + } else if (type == CARD_NONE || type == CARD_ERROR) { + host_uhs2_reg_clean(host); + host_set_highspeed(host, FALSE); + host_set_uhs_mode(host, 0); + } else { + /* MMC and EMMC case */ + host_set_highspeed(host, FALSE); + host_set_uhs_mode(host, 0); + } + + host_pll_enable(host, FALSE); + host_internal_clk_setup(host, FALSE); + host_led_ctl(host, FALSE); + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +void host_init_capbility(sd_host_t *host) +{ + u32 regval; + + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + host->ocr_avail = 0; + + /* 1. Init ocr host supported */ + regval = sdhci_readl(host, SDHCI_CAPABILITIES); + if (regval & SDHCI_CAN_VDD_330) { + host->ocr_avail |= BIT19 | BIT20; + host->mmc_ocr_avail |= (0x1ff << 15); + } + if (regval & SDHCI_CAN_VDD_300) + host->ocr_avail |= BIT17 | BIT18; + if (regval & SDHCI_CAN_VDD_180) { + host->ocr_avail |= BIT7; + host->mmc_ocr_avail |= BIT7; + + } + + if (regval & SDHCI_CAN_DO_SDMA) + host->sdma_supp = 1; + if (regval & SDHCI_CAN_DO_ADMA2) + host->adma2_supp = 1; + if (regval & SDHCI_CAN_64BIT_V3) + host->bit64_v3_supp = 1; + if (regval & SDHCI_CAN_64BIT_V4) + host->bit64_v4_supp = 1; + if (regval & SDHCI_CAN_DO_HISPD) + host->hs_supp = 1; + if (regval & SDHCI_CAN_DO_8BIT) + host->bus_8bit_supp = 1; + + host->max_block_len = (u8) ((regval & SDHCI_MAX_BLOCK_MASK) >> 16); + host->max_block_len = (2 << (9 + host->max_block_len)); + + regval = sdhci_readl(host, SDHCI_CAPABILITIES_1); + if (regval & SDHCI_CAP1_ADMA3_SUPP) + host->adma3_supp = 1; + if (regval & SDHCI_CAN_VDD2_18V) + host->vdd2_18v_supp = 1; + if (regval & SDHCI_CAN_VDD2_12V) + host->vdd2_12v_supp = 1; + + regval = sdhci_readl(host, SDHCI_MAX_CURRENT); + host->max_18vdd1_current = + 4 * + ((regval & SDHCI_MAX_CURRENT_180_MASK) >> + SDHCI_MAX_CURRENT_180_SHIFT); + host->max_33vdd1_current = + 4 * + ((regval & SDHCI_MAX_CURRENT_330_MASK) >> + SDHCI_MAX_CURRENT_330_SHIFT); + host->max_30vdd1_current = + 4 * + ((regval & SDHCI_MAX_CURRENT_300_MASK) >> + SDHCI_MAX_CURRENT_300_SHIFT); + + regval = sdhci_readl(host, SDHCI_MAX_CURRENT_2); + host->max_vdd2_current = + 4 * + ((regval & SDHCI_MAX_CUR_VDD2_180_MASK) >> + SDHCI_MAX_CUR_VDD2_180_SHIFT); + + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "ocr_avail=%d\n", host->ocr_avail); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "mmc_ocr_avail=%d\n", host->mmc_ocr_avail); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "sdma_supp=%d\n", host->sdma_supp); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "adma2_supp=%d\n", host->adma2_supp); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "bit64_v3_supp=%d\n", host->bit64_v3_supp); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "bit64_v4_supp=%d\n", host->bit64_v4_supp); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, "hs_supp=%d\n", + host->hs_supp); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "bus_8bit_supp=%d\n", host->bus_8bit_supp); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "max_block_len=%d\n", host->max_block_len); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "adma3_supp=%d\n", host->adma3_supp); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "vdd2_18v_supp=%d\n", host->vdd2_18v_supp); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "vdd2_12v_supp=%d\n", host->vdd2_12v_supp); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "max_18vdd1_current=%d\n", host->max_18vdd1_current); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "max_33vdd1_current=%d\n", host->max_33vdd1_current); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "max_30vdd1_current=%d\n", host->max_30vdd1_current); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "max_vdd2_current=%d\n", host->max_vdd2_current); + + host_uhs2_init_capability(host); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + +} + +static u16 host_get_boundary_value(u32 nkb) +{ + switch (nkb) { + case 4: + return 0; + case 8: + return 1; + case 16: + return 2; + case 32: + return 3; + case 64: + return 4; + case 128: + return 5; + case 256: + return 6; + case 512: + return 7; + default: + DbgErr("Error Sdma boundary size\n"); + break; + } + + return 0; +} + +/* + * Function Name: host_init_internal + * + * Abstract: This Function is used to init host setting registers and variables + * + * Input: + * sd_host_t *host, + * + */ + +static void host_init_internal(sd_host_t *host) +{ + u16 w; + u32 dma_mode = 0; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + dma_mode = host->cfg->host_item.test_dma_mode_setting.dma_mode; + host->uhs2_flag = FALSE; + + /* 2. Interrupt enable */ + + host_int_clr_status(host); + host_int_dis_sig_all(host, FALSE); + sdhci_writel(host, SDHCI_INT_ENABLE, 0xffffffff); + + hostven_ocb_cfg(host); + hostven_set_output_tuning_phase(host, 0, TRUE); + hostven_set_tuning_phase(host, 0, 0, TRUE); + hostven_detect_refclk_count_range_init(host); + hostven_refclk_stable_detection_circuit(host); + hostven_pcie_phy_tx_amplitude_adjustment(host); + + if (host->cfg->host_item.test_dma_mode_setting.dma_mode != 0xF) { + /* 3. After host reset , reset related software variable */ + + /* default is 4kb, set according to cfg */ + host->sdma_boundary_val = 0x00; + host->bit64_enable = + (byte) host->cfg->host_item.test_dma_mode_setting.enable_dma_64bit_address; + if (host->bit64_v3_supp == 0 && host->bit64_v4_supp == 0) + host->bit64_enable = 0; + + w = sdhci_readw(host, SDHCI_HOST_CONTROL2); + + if (host->cfg->host_item.test_dma_mode_setting.enable_dma_26bit_len + || host->cfg->host_item.test_dma_mode_setting.enable_dma_32bit_blkcount + || (host->adma3_supp + && (2 == dma_mode || 4 == dma_mode || 5 == dma_mode + || 6 == dma_mode)) + || host->bit64_enable) + host->sd_host4_enable = 1; + + if (host->cfg->host_item.test_dma_mode_setting.enable_dma_26bit_len) + w |= SDHCI_CTRL_ADMA2_26BIT_EN; + + if (host->bit64_enable) + w |= SDHCI_CTRL_64BIT_EN; + + if (host->sd_host4_enable) { + w |= SDHCI_CTRL_VER4_EN; + sdhci_writew(host, SDHCI_HOST_CONTROL2, w); + } + } else { + host->sd_host4_enable = 0; + host->bit64_enable = 0; + } + + host->led_on = FALSE; + + host->sdma_boundary_kb = host->cfg->host_item.test_sdma_boundary.value; + host->sdma_boundary_val = + host_get_boundary_value(host->sdma_boundary_kb); + + /* 4. set timeout */ + w = sdhci_readw(host, SDHCI_TIMEOUT_CONTROL); + w &= SDHCI_DAT_TIMEOUT_MASK; + w |= SDHCI_DAT_TIMEOUT_VAL; + sdhci_writew(host, SDHCI_TIMEOUT_CONTROL, w); + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "uhs2_flag=%d\n", + host->uhs2_flag); + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "sdma_boundary_val=%d\n", host->sdma_boundary_val); + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "bit64_enable=%d\n", host->bit64_enable); + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "sd_host4_enable=%d\n", host->sd_host4_enable); + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "led_on=%d\n", + host->led_on); + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "sdma_boundary_kb=%d\n", host->sdma_boundary_kb); + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "sdma_boundary_val=%d\n", host->sdma_boundary_val); + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +void host_init(sd_host_t *host) +{ + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* Host soft reset for all registers! */ + if (shift_bit_func_enable(host)) + set_pattern_value(host, 0x00); + + host_reset(host, SDHCI_RESET_ALL); + + /* + * if (host->chip_type == CHIP_SEABIRD) + * host_pll_enable(host, TRUE); + */ + host_init_internal(host); + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +/* + * Function Name: host_uhs2_reset + * Abstract: This Function is used to do host uhs2 full reset + * + * Input: + * sd_host_t *host, + * bool fullreset: True means do UHS2 Host Full Reset; + * False means do host SD_Tran Reset + * + * FullReset will clear all host setting except card power + * SD-Tran Reset only clear transfer buffer and interrupt + * + */ +void host_uhs2_reset(sd_host_t *host, bool fullreset) +{ + + u16 reg; + u32 delay_us = 1; + loop_wait_t wait; + + u16 set = (fullreset) ? SDHCI_UHS2_FULL_RESET : SDHCI_UHS2_SDTRAN_RESET; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, fullreset=%d\n", __func__, fullreset); + + /* For Full Reset case is called by uhs2_full_reset only we need to wait for dmt bit set */ + if (set == SDHCI_UHS2_FULL_RESET) { + if (host_uhs2_wait_dmt(host) == FALSE) + DbgErr("Wait Host Dm set before fullreset failed\n"); + } + + sdhci_or16(host, SDHCI_UHS2_SOFT_RST, set); + util_init_waitloop(host->pdx, RESET_FOR_ALL_ABRT_TM, delay_us, &wait); + while (!util_is_timeout(&wait)) { + reg = sdhci_readw(host, SDHCI_UHS2_SOFT_RST); + if (!(reg & set)) + break; + else if (reg == 0xffffffff) + break; + + os_udelay(delay_us); + } + + if (fullreset) { + host_pll_enable(host, FALSE); + host_init_internal(host); + } else { + /* Enable Host interrupt again, Enable UHS2 Err Status */ + host_int_clr_status(host); + host_int_dis_sig_all(host, FALSE); + sdhci_writel(host, SDHCI_INT_ENABLE, 0xffffffff); + sdhci_writel(host, SDHCI_UHS2_ERRINT_STS_EN, 0xFFFFFFFF); + } + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +bool host_wr_protect_pin(sd_host_t *host) +{ + u32 regval; + bool ret = TRUE; + + regval = sdhci_readl(host, SDHCI_PRESENT_STATE); + if (regval & SDHCI_WRITE_PROTECT) + ret = FALSE; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s,regval:%x,ret:%x\n", __func__, regval, ret); + return ret; +} + +static void host_emmc_power_supply(sd_host_t *host, + cfg_emmc_mode_t *emmc_mode, u32 *power_vdd) +{ + u32 power_vdd1, power_vdd2; + + power_vdd1 = power_vdd2 = 0; + power_vdd1 |= SDHCI_POWER_VDD1_330; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* SE2 chip support two combination mode */ + if ((host->chip_type != CHIP_SEAEAGLE2) && (host->chip_type != CHIP_GG8) + && (host->chip_type != CHIP_ALBATROSS)) { + emmc_mode->enable_18_vcc = 0; + *power_vdd = power_vdd1; + *(power_vdd + 1) = power_vdd2; + goto exit; + } + + /* 3.3v Vcc + 3.3v Vccq mode: */ + if (((emmc_mode->enable_18_vccq) == (emmc_mode->enable_12_vccq)) || + ((host->vdd2_12v_supp == FALSE) && (host->vdd2_18v_supp == FALSE)) + ) { + emmc_mode->enable_18_vccq = emmc_mode->enable_12_vccq = 0; + *power_vdd = power_vdd1; + *(power_vdd + 1) = power_vdd2; + goto exit; + } + + /* 1.8v Vcc */ + if (emmc_mode->enable_18_vcc) + power_vdd1 = SDHCI_POWER_VDD1_180; + else + power_vdd1 = SDHCI_POWER_VDD1_330; + + /* 1.2v Vccq */ + if ((host->vdd2_12v_supp) && (emmc_mode->enable_12_vccq) + ) { + power_vdd2 = SDHCI_POWER_VDD2_120; + } + /* 1.8v Vccq */ + else if ((host->vdd2_18v_supp) && (emmc_mode->enable_18_vccq) + ) { + power_vdd2 = SDHCI_POWER_VDD2_180; + } + + *power_vdd = power_vdd1; + *(power_vdd + 1) = power_vdd2; + +exit: + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +bool host_emmc_init(sd_host_t *host, cfg_emmc_mode_t *emmc_mode) +{ + bool ret = FALSE; + u32 power_vdd[2] = { 0 }; + u32 enable_1_8v_sig; + u32 enable_1_2v_sig; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + host_emmc_power_supply(host, emmc_mode, power_vdd); + + host_internal_clk_setup(host, TRUE); + + /* 1. clear VDD1 */ + if (host_get_vdd1_state(host) == TRUE) + host_set_vddx_power(host, VDD1, POWER_OFF); + + /* 2. set bus power VDD1 + VDD2 */ + if (power_vdd[1]) + host_set_vddx_power(host, VDD2, POWER_ON); + + host_set_vddx_power(host, VDD1, POWER_ON); + + /* 4. set 1.8V/1.2V IO voltage */ + enable_1_8v_sig = emmc_mode->enable_18_vccq; + enable_1_2v_sig = emmc_mode->enable_12_vccq; + if (enable_1_8v_sig) + host_sig_vol_set(host, SIG_VOL_18); + else if (enable_1_2v_sig) + host_sig_vol_set(host, SIG_VOL_12); + else + host_sig_vol_set(host, SIG_VOL_33); + + /* start clock to 400KHz */ + host_enable_clock(host, TRUE); + /* 6. clear bus width (4bit & 8bit) */ + host_set_buswidth(host, BUS_WIDTH1); + /* 7. clear ddr mode */ + host_emmc_ddr_set(host, FALSE); + + ret = TRUE; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + ret, __func__); + return ret; +} + +void host_emmc_hs400_set(sd_host_t *host, bool b_hs400) +{ + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, b_hs400=%d\n", __func__, b_hs400); + + if (b_hs400) { + if (hostven_hs400_host_chk(host) == TRUE) { + /* 0x110[17] set to 1 */ + sdhci_or32(host, SDHCI_VEN_SPEC_CTRL, + SDHCI_ENABLE_HS400); + } else { + /* set host to HS400 mode */ + host_set_uhs_mode(host, SDHCI_CTRL_UHS_HS400); + } + } else { + /* hs200 set 0x110[17] to 0 */ + sdhci_and32(host, SDHCI_VEN_SPEC_CTRL, ~(SDHCI_ENABLE_HS400)); + } + + /* 0x28[2] set to 1 */ + host_set_highspeed(host, TRUE); + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +void host_emmc_ddr_set(sd_host_t *host, bool b_ddr) +{ + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, b_ddr=%d\n", __func__, b_ddr); + + if (b_ddr) { + if ((host->chip_type == CHIP_SDS0) + || (host->chip_type == CHIP_SDS1) + || (host->chip_type == CHIP_FUJIN2) + || (host->chip_type == CHIP_SEABIRD) + || (host->chip_type == CHIP_SEAEAGLE) + ) { + /* For SDS|SB|FJ2|SE chip:Set eMMC DDR mode 0x110[5] = 1'b1 */ + sdhci_or32(host, SDHCI_VEN_SPEC_CTRL, + SDHCI_EMMC_HS_DDR); + } + } else { + /* clear eMMC DDR mode: set 0x110[15] to 0 */ + sdhci_and32(host, SDHCI_VEN_SPEC_CTRL, ~(SDHCI_EMMC_HS_DDR)); + } + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +/* + * Function Name: host_pll_enable + * Abstract: This Function is used to do uhs2 phy init + * + * + * Input: + * sd_host_t *host, + */ +static void host_pll_enable(sd_host_t *host, bool enable) +{ + + if (!host->feature.hw_pll_enable) + return; + + if (host->sd_host4_enable == FALSE && host->uhs2_flag == FALSE) + return; + + if (enable) { + u32 timeout = 5000; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Host Pll Enable\n"); + sdhci_or16(host, SDHCI_CLOCK_CONTROL, SDHCI_HOST_PLL_EN); + while (timeout) { + if (sdhci_readl(host, SDHCI_CLOCK_CONTROL) & + SDHCI_CLOCK_INT_STABLE) + break; + else if (sdhci_readl(host, SDHCI_CLOCK_CONTROL) == + 0xffffffff) + break; + + os_udelay(1); + timeout--; + } + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Real Enable pll 0x%04X\n", sdhci_readw(host, + SDHCI_CLOCK_CONTROL)); + } else + sdhci_and16(host, SDHCI_CLOCK_CONTROL, ~SDHCI_HOST_PLL_EN); +} + +void host_uhs2_init(sd_host_t *host, u32 clk_value, bool bfullreset) +{ + u16 reg; + u32 reg32; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, clk_value=0x%x\n", __func__, clk_value); + + host->uhs2_flag = TRUE; + reg = sdhci_readw(host, SDHCI_HOST_CONTROL2); + + /* Enable UHS2 function */ + reg |= SDHCI_CTRL_UHS2IF_EN | SDHCI_CTRL_VER4_EN; + sdhci_writew(host, SDHCI_HOST_CONTROL2, reg); + reg = sdhci_readw(host, SDHCI_HOST_CONTROL2); + reg |= SDHCI_CTRL_UHS2; + sdhci_writew(host, SDHCI_HOST_CONTROL2, reg); + if (bfullreset == 0) { + host_init_clock(host, clk_value); + host_internal_clk_setup(host, TRUE); + } + + /* Set UHS2 Timeout */ + reg = sdhci_readw(host, SDHCI_UHS2_TIMER_CTRL); + reg |= 0xFF; + sdhci_writew(host, SDHCI_UHS2_TIMER_CTRL, reg); + + /* Enable UHS2 Err Status */ + sdhci_writel(host, SDHCI_UHS2_ERRINT_STS_EN, 0xFFFFFFFF); + + /* Set Scrambling according to vender cfg */ + reg32 = sdhci_readl(host, host->uhs2_cap.vnd_base + UHS2_EXTCNT_OFFSET); + if (host->cfg->card_item.test_uhs2_setting2.disable_scramb_mode == 0) + reg32 |= BIT0; + else + reg32 &= ~BIT0; + sdhci_writel(host, host->uhs2_cap.vnd_base + UHS2_EXTCNT_OFFSET, reg32); + + if (shift_bit_func_enable(host)) + set_pattern_value(host, 0x00); + + host_set_vddx_power(host, VDD1, POWER_OFF); + os_mdelay(36); + host_set_vddx_power(host, VDD1, POWER_ON); + + if (shift_bit_func_enable(host)) + set_pattern_value(host, 0x30); + + host_set_vddx_power(host, VDD1, POWER_ON); + host_set_vddx_power(host, VDD2, POWER_ON); + + if (host_get_vdd1_state(host) == FALSE) + host_set_vddx_power(host, VDD1, POWER_ON); + + os_mdelay(36); + + host_pll_enable(host, TRUE); + + host_enable_clock(host, TRUE); + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +static void host_uhs2_reg_clean(sd_host_t *host) +{ + u16 reg; + + reg = SDHCI_CTRL_UHS2IF_EN; + if (host->sd_host4_enable == 0) + reg |= SDHCI_CTRL_VER4_EN; + sdhci_and16(host, SDHCI_HOST_CONTROL2, ~reg); + sdhci_writel(host, SDHCI_UHS2_ERRINT_STS_EN, 0); + sdhci_and16(host, SDHCI_UHS2_TIMER_CTRL, ~0xFF); + host_set_uhs_mode(host, 0); + host->uhs2_flag = FALSE; +} + +/* + * Function Name: host_uhs2_clear + * Abstract: This Function is used to check clear uhs2 related register + * + * Input: + * sd_host_t *host, + * bool breset: do host softreset for all to clear uhs2 status or not + * + */ +void host_uhs2_clear(sd_host_t *host, bool breset) +{ + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, breset=%d\n", __func__, breset); + + /* clear uhs2 related reg */ + if (breset) { + host_enable_clock(host, FALSE); + if (shift_bit_func_enable(host)) + set_pattern_value(host, 0x10); + + host_set_vddx_power(host, VDD2, POWER_OFF); + if (host_get_vdd1_state(host)) + host_set_vddx_power(host, VDD1, POWER_OFF); + + host_init(host); + } else { + host_enable_clock(host, FALSE); + + if (shift_bit_func_enable(host)) + set_pattern_value(host, 0x10); + + host_set_vddx_power(host, VDD2, POWER_OFF); + host_uhs2_reg_clean(host); + host_pll_enable(host, FALSE); + host_internal_clk_setup(host, FALSE); + host_init_internal(host); + } + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +/* + * Function Name: host_uhs2_phychk + * Abstract: This Function is used to check uhs2 phy init ok or not + * + * Input: + * sd_host_t *host, + * bool fromslp: whether call this in uhs2 resume context + * Output: + * bool *stbl : STBL check is ok or not + * + * Return value: + * phy init ok or not + */ +bool host_uhs2_phychk(sd_host_t *host, bool fromslp, bool *stbl) +{ + u32 timeout = 0; + u32 reg; + bool result = FALSE; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, *stbl=%d\n", __func__, *stbl); + + *stbl = FALSE; + + if (fromslp) + timeout = 6000; + timeout = 250; + + do { + reg = sdhci_readl(host, SDHCI_PRESENT_STATE); + if (reg == 0xFFFFFFFF) { + DbgErr("Present All FF in STBL check\n"); + goto exit; + } + + if (!(reg & SDHCI_CARD_PRESENT)) + goto exit; + + if (reg & SDHCI_UHS2_IF_DETECT) + break; + + timeout -= 1; + os_udelay(1); + + } while (timeout > 0); + + if (!timeout) { + DbgWarn(MODULE_SD_HOST, NOT_TO_RAM, "uhs2 STBL timeout\n"); + goto exit; + } + + *stbl = TRUE; + timeout = 1500 * 100; + + do { + reg = sdhci_readl(host, SDHCI_PRESENT_STATE); + if (!(reg & SDHCI_CARD_PRESENT)) + goto exit; + + if (reg & SDHCI_UHS2_LANE_SYNC) + break; + + reg = sdhci_readl(host, SDHCI_UHS2_ERRINT_STS); + if (reg & SDHCI_UHS2_INT_TO_DEADLOCK) { + DbgErr("UHS2 Wait for Sync deadlock occur\n"); + goto exit; + } + + timeout -= 10; + os_udelay(10); + } while (timeout > 0); + + if (timeout <= 0) { + DbgErr("UHS2 Wait for Sync timeout\n"); + goto exit; + } + + result = TRUE; +exit: + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + return result; +} + +/* + * Function Name: host_uhs2_cfg_set + * Abstract: This Function is used to update uhs2 host setting registers + * + * Input: + * sd_host_t *host, + * uhs2_info_t *setting: The setting values + * bool stage2; State 2 is update for RangeB and lanes, + * it is called after resume from dmt in uhs2 cfg flow + * + */ +void host_uhs2_cfg_set(sd_host_t *host, uhs2_info_t *setting, bool stage2) +{ + u32 reg; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, stage2=%d\n", __func__, stage2); + + if (stage2) { + /* update lanes and function */ + reg = + sdhci_readl(host, + host->uhs2_cap.set_base + + SDHCI_UHS2_IDX_GENERAL); + reg &= ~(SDHCI_UHS2_LANE_FUNC_MASK); + reg |= (setting->lanes << SDHCI_UHS2_LANE_FUNC_SHIFT); + sdhci_writel(host, + host->uhs2_cap.set_base + SDHCI_UHS2_IDX_GENERAL, + reg); + + /* update speed range */ + reg = + sdhci_readl(host, + host->uhs2_cap.set_base + + SDHCI_UHS2_IDX_PHYSICAL); + reg &= ~(SDHCI_UHS2_SPEED_MASK); + reg |= (setting->speed_range << SDHCI_UHS2_SPEED_SHIFT); + sdhci_writel(host, + host->uhs2_cap.set_base + SDHCI_UHS2_IDX_PHYSICAL, + reg); + goto exit; + } + + /* General Setting reg */ + reg = + sdhci_readl(host, host->uhs2_cap.set_base + SDHCI_UHS2_IDX_GENERAL); + reg &= ~(SDHCI_UHS2_LANE_FUNC_MASK | SDHCI_UHS2_POWER_MODE_MASK); + reg |= setting->pwr_mode; + sdhci_writel(host, host->uhs2_cap.set_base + SDHCI_UHS2_IDX_GENERAL, + reg); + + /* Phy setting reg */ + reg = + sdhci_readl(host, + host->uhs2_cap.set_base + SDHCI_UHS2_IDX_PHYSICAL); + reg &= + ~(SDHCI_UHS2_SPEED_MASK | SDHCI_UHS2_HIBERNATE_MASK | + SDHCI_UHS2_LSS_DIR_MASK | SDHCI_UHS2_LSS_SYN_MASK); + reg |= (setting->n_lss_dir << SDHCI_UHS2_LSS_DIR_SHIFT); + reg |= (setting->n_lss_syn << SDHCI_UHS2_LSS_SYN_SHIFT); + reg |= (setting->hibernate << SDHCI_UHS2_HIBERNATE_SHIFT); + sdhci_writel(host, host->uhs2_cap.set_base + SDHCI_UHS2_IDX_PHYSICAL, + reg); + + /* Link and Tran regs */ + reg = + sdhci_readl(host, host->uhs2_cap.set_base + SDHCI_UHS2_IDX_LNKTRH); + reg &= ~(SDHCI_UHS2_DATE_GAP_MASK); + reg |= (setting->n_data_gap); + sdhci_writel(host, host->uhs2_cap.set_base + SDHCI_UHS2_IDX_LNKTRH, + reg); + + reg = + sdhci_readl(host, host->uhs2_cap.set_base + SDHCI_UHS2_IDX_LNKTRL); + reg &= ~(SDHCI_UHS2_RETRY_CNT_MASK | SDHCI_UHS2_NFCU_MASK); + reg |= (setting->retry_cnt << SDHCI_UHS2_RETRY_CNT_SHIFT); + reg |= (setting->n_fcu << SDHCI_UHS2_NFCU_SHIFT); + sdhci_writel(host, host->uhs2_cap.set_base + SDHCI_UHS2_IDX_LNKTRL, + reg); + +exit: + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + return; + +} + +/* + * Function Name: host_uhs2_init_capability + * Abstract: This Function is used to get host uhs2 related capbility + * + * Input: + * sd_host_t *host, + * + */ +static void host_uhs2_init_capability(sd_host_t *host) +{ + u32 reg; + + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* + * below code is used for chip lost detection test + * sdhci_readl_test(host, SDHCI_CAPABILITIES_1); + * sdhci_readw_test(host, SDHCI_CAPABILITIES_1); + */ + + reg = sdhci_readl(host, SDHCI_CAPABILITIES_1); + if (!(reg & SDHCI_CAP1_UHS2_SUPP)) { + DbgWarn(MODULE_SD_HOST, TO_RAM, "host don't support uhs2\n"); + goto no_uhs2; + } + + host->uhs2_cap.vdd2_ocr = ((reg & SDHCI_CAP1_UHS2_VDD2_MASK) >> + SDHCI_CAP1_UHS2_VDD2_SHIFT); + + reg = sdhci_readl(host, SDHCI_MAX_CURRENT_2); + host->uhs2_cap.vdd2_18_maxpower = ((reg & SDHCI_MAX_CUR_VDD2_180_MASK) + * SDHCI_MAX_CURRENT_MULTIPLIER); + + /* Get uhs2 host reallocate register base */ + host->uhs2_cap.set_base = (sdhci_readw(host, SDHCI_UHS2_SETTING_BASE) & + SDHCI_MAX_LOCATABLE_REG); + host->uhs2_cap.cap_base = + (sdhci_readw(host, SDHCI_UHS2_CAPABILITY_BASE) & + SDHCI_MAX_LOCATABLE_REG); + host->uhs2_cap.tst_base = + (sdhci_readw(host, SDHCI_UHS2_TEST_BASE) & SDHCI_MAX_LOCATABLE_REG); + host->uhs2_cap.vnd_base = + (sdhci_readw(host, SDCHI_UHS2_VENDOR_BASE) & + SDHCI_MAX_LOCATABLE_REG); + + /* Get Host caps from General Capability reg */ + reg = + sdhci_readl(host, host->uhs2_cap.cap_base + SDHCI_UHS2_IDX_GENERAL); + host->uhs2_cap.max_devices = + ((reg & SDHCI_UHS2_DEVICE_NUM_MASK) >> SDHCI_UHS2_DEVICE_NUM_SHIFT); + + if (!(reg & SDHCI_UHS2_BUS_TOP_MASK)) + host->uhs2_cap.max_devices = 1; + else if (host->uhs2_cap.max_devices == 0) { + DbgErr("Host support zero uhs2 devices\n"); + goto no_uhs2; + } + + host->uhs2_cap.dap = (reg & SDHCI_UHS2_DAP_MASK); + host->uhs2_cap.gap = ((reg & SDHCI_UHS2_GAP_MASK) >> + SDHCI_UHS2_GAP_SHIFT); + host->uhs2_cap.num_of_lane = ((reg & SDHCI_UHS2_LANE_MASK) >> + SDHCI_UHS2_LANE_SHIFT); + + /* Get Host caps from Phy Capability reg */ + reg = + sdhci_readl(host, + host->uhs2_cap.cap_base + SDHCI_UHS2_IDX_PHYSICAL); + host->uhs2_cap.n_lss_dir = + ((reg & SDHCI_UHS2_LSS_DIR_MASK) >> SDHCI_UHS2_LSS_DIR_SHIFT); + host->uhs2_cap.n_lss_syn = + ((reg & SDHCI_UHS2_LSS_SYN_MASK) >> SDHCI_UHS2_LSS_SYN_SHIFT); + host->uhs2_cap.speed_range = + ((reg & SDHCI_UHS2_SPEED_MASK) >> SDHCI_UHS2_SPEED_SHIFT); + + /* Get Host caps from LinkTran Capability reg */ + reg = + sdhci_readl(host, host->uhs2_cap.cap_base + SDHCI_UHS2_IDX_LNKTRL); + host->uhs2_cap.n_fcu = + ((reg & SDHCI_UHS2_NFCU_MASK) >> SDHCI_UHS2_NFCU_SHIFT); + host->uhs2_cap.max_blk_len = + ((reg & SDHCI_UHS2_MAX_BLK_MASK) >> SDHCI_UHS2_MAX_BLK_SHIFT); + + reg = + sdhci_readl(host, host->uhs2_cap.cap_base + SDHCI_UHS2_IDX_LNKTRH); + host->uhs2_cap.n_data_gap = (reg & SDHCI_UHS2_DATE_GAP_MASK); + host->uhs2_cap.retry_cnt = 3; + + /* Set host support uhs2 */ + host->uhs2_supp = 1; + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Host uhs2_flag=%d\n", host->uhs2_flag); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Host uhs2_flag=%d\n", host->uhs2_cap.vdd2_ocr); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Host vdd2_18_maxpower=%d\n", host->uhs2_cap.vdd2_18_maxpower); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Host set_base=%d\n", host->uhs2_cap.set_base); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Host cap_base=%d\n", host->uhs2_cap.cap_base); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Host tst_base=%d\n", host->uhs2_cap.tst_base); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Host vnd_base=%d\n", host->uhs2_cap.vnd_base); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Host max_devices=%d\n", host->uhs2_cap.max_devices); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Host dap=%d\n", host->uhs2_cap.dap); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Host gap=%d\n", host->uhs2_cap.gap); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Host num_of_lane=%d\n", host->uhs2_cap.num_of_lane); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Host n_lss_dir=%d\n", host->uhs2_cap.n_lss_dir); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Host n_lss_syn=%d\n", host->uhs2_cap.n_lss_syn); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Host speed_range=%d\n", host->uhs2_cap.speed_range); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Host n_fcu=%d\n", host->uhs2_cap.n_fcu); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Host max_blk_len=%d\n", host->uhs2_cap.max_blk_len); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Host n_data_gap=%d\n", host->uhs2_cap.n_data_gap); + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Host retry_cnt=%d\n", host->uhs2_cap.retry_cnt); + + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + return; +no_uhs2: + host->uhs2_supp = 0; + DbgInfo(MODULE_SD_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +static bool host_uhs2_wait_dmt(sd_host_t *host) +{ + u32 reg = 0; + u32 delay_us = 1; + loop_wait_t wait; + + util_init_waitloop(host->pdx, 1500, delay_us, &wait); + while (!util_is_timeout(&wait)) { + reg = sdhci_readl(host, SDHCI_PRESENT_STATE); + if (!(reg & SDHCI_CARD_PRESENT)) + return FALSE; + + if (reg == 0xffffffff) { + DbgErr("chip lost when go dmt\n"); + return FALSE; + } + + if (reg & SDHCI_UHS2_DMT_STATUS) + break; + + os_udelay(delay_us); + } + + if (!(reg & SDHCI_UHS2_DMT_STATUS)) { + DbgErr("wait host dmt timeout.\n"); + return FALSE; + } + + return TRUE; +} + +/* + * Function Name: host_uhs2_go_dmt + * Abstract: This Function is used to set uhs2 card to dormant status + * + * + * Input: + * sd_host_t *host, + * bool hbr: enter hibernate status or not + */ +bool host_uhs2_go_dmt(sd_host_t *host, bool hbr) +{ + bool result = TRUE; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s hbr=%d\n", __func__, hbr); + + result = host_uhs2_wait_dmt(host); + if (result == FALSE) { + DbgErr("Uhs2 Go Dormant wait for Host Dmt failed\n"); + goto exit; + } + + if (DMT_DELAY_BEF_STOP_CLK_US) + os_udelay(DMT_DELAY_BEF_STOP_CLK_US); + + host_enable_clock_nodelay(host, FALSE); + + if (DMT_DEALY_AFT_STOP_CLK_US) + os_udelay(DMT_DEALY_AFT_STOP_CLK_US); + + host_pll_enable(host, FALSE); + if (host->cfg->card_item.test_uhs2_setting2.enable_internal_clk_dormant + == 0) { + host_internal_clk_setup(host, FALSE); + if (DMT_DELAY_AFT_ST_REFCLK_US) + os_udelay(DMT_DELAY_AFT_ST_REFCLK_US); + } + + if (hbr) { + /* host_set_vdd1_power_nodelay(host, FALSE, 0); */ + host_set_vddx_power(host, VDD1, POWER_OFF); + if (DMT_DELAY_AFT_PWROFF_MS) + os_mdelay(DMT_DELAY_AFT_PWROFF_MS); + + } +exit: + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + return result; +} + +/* + * Function Name: host_uhs2_resume_dmt + * Abstract: This Function is used to resume from dormant + * + * + * Input: + * sd_host_t *host, + * bool hbr: resume hibernate status or not + */ +bool host_uhs2_resume_dmt(sd_host_t *host, bool hbr) +{ + bool stbl; + bool result = FALSE; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT | FEATURE_CARD_OPS, + NOT_TO_RAM, "Enter %s hbr=%d\n", __func__, hbr); + + if (host->cfg->card_item.test_uhs2_setting2.enable_internal_clk_dormant + == 0) { + host_internal_clk_setup(host, TRUE); + } + + if (hbr) { + host_set_vddx_power(host, VDD1, POWER_ON); + if (RESUME_POWER_ON_DELAY_MS) + os_mdelay(RESUME_POWER_ON_DELAY_MS); + host_chk_ocb_occur(host); + } + + host_pll_enable(host, TRUE); + host_enable_clock_nodelay(host, TRUE); + + result = host_uhs2_phychk(host, TRUE, &stbl); + if (stbl == FALSE) + DbgErr("STBL failed for wakeup"); + + if (RESUME_DALAY_US) + os_udelay(RESUME_DALAY_US); + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + return result; +} + +/* + * Function Name: host_uhs2_resume_dmt + * Abstract: This Function is used for sd legacy host init operation + * + * Input: + * sd_host_t *host, + */ +void host_sd_init(sd_host_t *host) +{ + + host_init_400k_clock(host); + host_internal_clk_setup(host, TRUE); + /* 1. Power on card */ + if (host_get_vdd1_state(host) == FALSE) { + os_mdelay(10); + host_set_vddx_power(host, VDD1, POWER_ON); + + } + + if (shift_bit_func_enable(host)) + set_pattern_value(host, 0x11); + + host_enable_clock(host, TRUE); + +} + +bool host_enable_sd_signal18v(sd_host_t *host) +{ + bool result = FALSE; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* 2.1. Stop SD CLK after SD transfer. */ + + /* If it is not stopping, it shall be stopped */ + host_enable_clock(host, FALSE); + /* + * 1'. Wait 1ms for clock off. Issue #12074: + * [UHS1] SD_CLK voltage is wrong during Signal voltage switch sequence. + * Samuel 2011-09-06 + */ + os_mdelay(1); + + /* 2. Check DAT[3:0]line signal =0000b or not in the Present State (0x24:D23-D20) */ + if (host_get_datline_state(host) != 0) { + DbgErr("Check DAT[3:0]line signal =0000b Failed.\n"); + goto exit; + } + + /* 3. Set Host Control2 (0x3e:D03) .8V Signaling Enable=1b */ + host_1_8v_sig_set(host, TRUE); + + /* 4. Minimum Wait 5ms */ + /* os_mdelay(10); */ + + /* 5. Check Host Control2 (0x3e:D03) 1.8V Signaling Enable=1b or not */ + if (host_check_1_8v_signal(host) == 0x0) { + DbgErr + ("Check Host Control2 (0x3e:D03) 1.8V Signaling Enable=1b Failed.\n"); + goto exit; + } +#if (0) + /* voltage_switch_method_choose 1:hardware control 0:sofaware control */ + if (host->cfg->host_item.test_voltage_switch_method_choose.voltage_switch_method) { + /* hardware control */ + if (host_check_voltage_stable(host) == FALSE) + DbgErr("Check voltage stable Failed.\n"); + } else { + /* software control */ + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Software Method to Switch Voltage, Delay 10ms\n"); + os_mdelay(10); + } + +#else + /* software control */ + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Software Method to Switch Voltage, Delay 10ms\n"); + os_mdelay(10); + +#endif + + /* + * Add voltage stable check for voltage switch failed issue + * BH722SE2LN-A UHS1 issue#6 GIGABYTE D3V #6 platform, BH driver 10024, + * the Host regulator voltage switch de-bounce time setting did not + * meets design, so some SD3.0 card would switch to 1.8V fail. + */ + + /* 6. Clock On */ + host_enable_clock(host, TRUE); + /* Signal Voltage Switch procedure need 1ms wait */ + os_mdelay(1); + + /* 9. Check DAT[3:0]line signal =1111b or not in the Present State (0x24:D23-D20) */ + if (host_get_datline_state(host) != 0xF) { + DbgErr("Check DAT[3:0]line signal =1111b Failed.\n"); + goto exit; + } + + result = TRUE; +exit: + if (result == FALSE) + DbgErr("host set sd 18v signal failed\n"); + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit(%d) %s\n", + result, __func__); + return result; +} + +void host_enable_cmd23(sd_host_t *host, bool enable) +{ + if (host->feature.hw_autocmd) { + if (enable == 0) + sdhci_and16(host, SDHCI_HOST_CONTROL2, + ~SDHCI_CTRL_CMD23_EN); + else + sdhci_or16(host, SDHCI_HOST_CONTROL2, + SDHCI_CTRL_CMD23_EN); + } +} + +void host_transfer_init(sd_host_t *host, bool enable_infinite, bool force_adma) +{ + u32 dma_mode = 0; + + DbgInfo(MODULE_SD_HOST, + FEATURE_IOCTL_TRACE | FEATURE_CARD_INIT | FEATURE_ERROR_RECOVER, + NOT_TO_RAM, "Enter %s, enable_infinite=%d, force_adma=%d\n", + __func__, enable_infinite, force_adma); + + if (force_adma == TRUE) { + /* todo according to registery */ + host_dma_select(host, TRANS_ADMA2); + hostven_transfer_init(host, FALSE); + } else { + dma_mode = host->cfg->host_item.test_dma_mode_setting.dma_mode; + switch (dma_mode) { + case CFG_TRANS_MODE_SDMA: + host_dma_select(host, TRANS_SDMA); + break; + case CFG_TRANS_MODE_ADMA2: + case CFG_TRANS_MODE_ADMA2_SDMA_LIKE: + host_dma_select(host, TRANS_ADMA2); + break; + case CFG_TRANS_MODE_ADMA3: + case CFG_TRANS_MODE_ADMA3_SDMA_LIKE: + host_dma_select(host, TRANS_ADMA3); + break; + case CFG_TRANS_MODE_ADMA_MIX: + case CFG_TRANS_MODE_ADMA_MIX_SDMA_LIKE: + if ((host->chip_type == CHIP_SEAEAGLE2) + || (host->chip_type == CHIP_GG8) + || (host->chip_type == CHIP_ALBATROSS)) + /* ADMA2 or ADMA3 if SD4.0 */ + host_dma_select(host, TRANS_ADMA3); + else + /* ADMA2 or ADMA3 if SD4.0 */ + host_dma_select(host, TRANS_ADMA2); + break; + default: + DbgErr("%s dma mode %d no define\n", __func__, + dma_mode); + host_dma_select(host, TRANS_ADMA2); + break; + } + hostven_transfer_init(host, enable_infinite); + } + DbgInfo(MODULE_SD_HOST, + FEATURE_IOCTL_TRACE | FEATURE_CARD_INIT | FEATURE_ERROR_RECOVER, + NOT_TO_RAM, "Exit %s\n", __func__); +} + +void host_error_int_recovery_stage1(sd_host_t *host, u16 error_int_state, + bool check) +{ + /* + * Follow SD Host Spec V4.10 Section 3.10.1 Error Interrupt Recovery flow (Page 178) + * + * (3). Set Software Reset for CMD Line to 1 in the Software + * Reset register for software reset the CMD line. + * (4). Check Software Reset For CMD Line in the Software + * Reset register. If Software Reset For CMD Line is 0, go to (5) + * If it is 1, go to step (4) + * (5) Check bits D06-04 in the Error Interrupt Status register. + * If one of these bits (D06-04) is set to 1, goto (6). + * If none are set to 1 (all are 0), goto step (8) + * (6) Set Software Reset for DAT Line in the Software Reset + * register for software reset for DAT line. + * (7) Check Software Reset For DAT Line in the Software Reset register. + * If Software Reset For DAT Line is 0, go to (8) + * If it is 1, goto (7) + */ + + DbgInfo(MODULE_SD_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Enter %s, error_int_state=%d, check=%d\n", __func__, + error_int_state, check); + + /* If chip lost, do nothing. */ + if (host_check_lost(host)) + return; + + host_chk_ocb_occur(host); + + /* uhs2 case don't need this flow */ + if (host->uhs2_flag) + return; + + if (check) { + if (error_int_state & SDHCI_INT_CMD_ERROR_MASK) + host_reset(host, SDHCI_RESET_CMD); + if (error_int_state & SDHCI_INT_DAT_ERROR_MASK) + host_reset(host, SDHCI_RESET_DATA); + } else { + if (error_int_state & SDHCI_INT_CMD_ERROR_MASK) + sdhci_or32(host, SDHCI_CLOCK_CONTROL, SDHCI_RESET_CMD); + if (error_int_state & SDHCI_INT_DAT_ERROR_MASK) + sdhci_or32(host, SDHCI_CLOCK_CONTROL, SDHCI_RESET_DATA); + } + + DbgInfo(MODULE_SD_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +bool host_error_int_recovery_stage2(sd_host_t *host, u16 error_int_state) +{ + bool ret = FALSE; + u32 delay_us = 1; + loop_wait_t wait; + + DbgInfo(MODULE_SD_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Enter %s, error_int_state=%d\n", __func__, + error_int_state); + /* + * 11 Check Command Inhibit (DAT) and Command Inhibit(CMD) in the Present State register. + * Repeat this step until both Command Inhibit (DAT) and Command Inhibit(CMD) are set to 0 + */ + + /* Command Inhibit (DAT) and (CMD) Check */ + sdhci_or32(host, SDHCI_CLOCK_CONTROL, SDHCI_RESET_CMD); + util_init_waitloop(host->pdx, RESET_FOR_ALL_ABRT_TM, delay_us, &wait); + while (!util_is_timeout(&wait)) { + if ((sdhci_readl(host, SDHCI_PRESENT_STATE) & + (SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT)) == 0) { + goto next; + } else if (sdhci_readl(host, SDHCI_PRESENT_STATE) == 0xffffffff) { + break; + } + os_udelay(delay_us); + } + /* Command Inhibit (DAT) and Command Inhibit(CMD) timeout, treat as Non-recoverable Error */ + goto exit; + +next: + /* + * 12 Check bits D03-00 in the Error Interrupt Status register for Aboot Command. + * If one of these bits is set to 1, goto 16. if none of these bits are set to 1, go to 13 + */ + if (error_int_state & SDHCI_INT_CMD_ERROR_MASK) + goto exit; + + /* + * 13 Check Data Timeout Error in the Error interrupt Status register. + * If this bit is set to 1, go to step 16, If it is 0, goto 14 + */ + if (error_int_state & SDHCI_INT_DATA_TIMEOUT) + goto exit; + + /* 14 Wait for more than 40us */ + os_udelay(40); + + /* + * 15 By monitoring the DAT[3:0] Line Signal Level in the Present State register, + * judge whether the level of DAT line is low or not. + */ + if (host_get_datline_state(host) != 0xF) + ret = FALSE; + else + ret = TRUE; + +exit: + DbgInfo(MODULE_SD_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Exit(%d) %s\n", ret, __func__); + return ret; +} + +bool host_check_lost(sd_host_t *host) +{ + u32 reg = sdhci_readl(host, SDHCI_PRESENT_STATE); + + if (reg == 0xffffffff) + return TRUE; + else + return FALSE; +} + +void host_set_output_tuning_phase(sd_host_t *host, u32 phase) +{ + + DbgInfo(MODULE_SD_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Enter %s, phase=0x%x\n", __func__, phase); + + host_enable_clock(host, FALSE); + + hostven_set_output_tuning_phase(host, phase, FALSE); + + host_enable_clock(host, TRUE); + + DbgInfo(MODULE_SD_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +/* + * only used for camera mode, polling in thread daemon. + * for PC mode, sdhci_irq do card in/dessert check by INTR + */ +extern void remove_card_handle(bht_dev_ext_t *pdx); +extern void insert_card_handle(bht_dev_ext_t *pdx); +void host_check_card_insert_desert(sd_host_t *host) +{ + u16 regval; + + if (host->cfg->driver_item.camera_mode_ctrl_vdd1_vdd2_cd == 1) { + regval = ven_readw(host, SDBAR1_WP_GPIO3_CTRL_REG_514); + if ((regval & (1 << 6)) + && (host->camera_mode_card_state == CARD_INSERTED)) { + DbgInfo(MODULE_SD_HOST, + FEATURE_CARD_INIT | FEATURE_INTR_TRACE, TO_RAM, + "CARD_DESERTED %s\n", __func__); + host->camera_mode_card_state = CARD_DESERTED; + remove_card_handle(host->pdx); + } else if (!(regval & (1 << 6)) + && (host->camera_mode_card_state == CARD_DESERTED)) { + DbgInfo(MODULE_SD_HOST, + FEATURE_CARD_INIT | FEATURE_INTR_TRACE, TO_RAM, + "CARD_DESERTED %s\n", __func__); + host->camera_mode_card_state = CARD_INSERTED; + insert_card_handle(host->pdx); + } else { + /* nothing */ + } + } +} + +void set_gpio_levels(sd_host_t *host, bool gpio_num, bool signal_level) +{ + u8 gpio_setting_case; + u32 regval; + + gpio_setting_case = ((gpio_num << 1) | signal_level); + + switch (gpio_setting_case) { + case 0: + /* GPIO2 Low */ + DbgInfo(MODULE_SD_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Set GPIO2 Low\n"); + regval = ven_readl(host, 0x510); + regval &= ~(1 << 13); + ven_writel(host, 0x510, regval); + + break; + + case 1: + /* GPIO2 High */ + DbgInfo(MODULE_SD_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Set GPIO2 High\n"); + regval = ven_readl(host, 0x510); + regval |= (1 << 13); + ven_writel(host, 0x510, regval); + + break; + + case 2: + /* GPIO3 Low */ + DbgInfo(MODULE_SD_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Set GPIO3 Low\n"); + regval = ven_readl(host, 0x514); + regval &= ~(1 << 5); + ven_writel(host, 0x514, regval); + + break; + + case 3: + /* GPIO3 High */ + DbgInfo(MODULE_SD_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Set GPIO3 High\n"); + regval = ven_readl(host, 0x514); + + if (regval == 0xffffffff) + DbgInfo(MODULE_SD_HOST, FEATURE_ERROR_RECOVER, + NOT_TO_RAM, "Chip lost when Set GPIO3 High\n"); + else { + regval |= (1 << 5); + ven_writel(host, 0x514, regval); + } + break; + } + +} + +void shif_byte_pattern_bit_set(sd_host_t *host, bool bit_en, u8 pattern_case) +{ + switch (pattern_case) { + case SPECIAL_PATTERN: + if (bit_en) { + set_gpio_levels(host, GPIO2, GPIO_LOW); + set_gpio_levels(host, GPIO3, GPIO_HIGHT); + set_gpio_levels(host, GPIO2, GPIO_HIGHT); + set_gpio_levels(host, GPIO2, GPIO_LOW); + } else { + set_gpio_levels(host, GPIO2, GPIO_LOW); + set_gpio_levels(host, GPIO3, GPIO_LOW); + set_gpio_levels(host, GPIO2, GPIO_HIGHT); + set_gpio_levels(host, GPIO2, GPIO_LOW); + } + break; + + case START_BIT: + set_gpio_levels(host, GPIO2, GPIO_HIGHT); + set_gpio_levels(host, GPIO3, GPIO_LOW); + set_gpio_levels(host, GPIO2, GPIO_LOW); + break; + + case END_BIT: + set_gpio_levels(host, GPIO2, GPIO_HIGHT); + set_gpio_levels(host, GPIO3, GPIO_LOW); + set_gpio_levels(host, GPIO3, GPIO_HIGHT); + break; + } +} + +void set_pattern_value(sd_host_t *host, u8 value) +{ + int i; + + DbgInfo(MODULE_SD_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Enter %s with 0x%x\n", __func__, value); + + shif_byte_pattern_bit_set(host, 0, START_BIT); + + for (i = 5; i >= 0; i--) { + PrintMsg("#i = %d\n", i); + if (value & (1 << i)) + shif_byte_pattern_bit_set(host, 1, SPECIAL_PATTERN); + else + shif_byte_pattern_bit_set(host, 0, SPECIAL_PATTERN); + } + + shif_byte_pattern_bit_set(host, 0, END_BIT); +} + +void power_control_with_card_type(sd_host_t *host, u8 vddx, bool power_en) +{ + u8 value = 0x0; + bht_dev_ext_t *pdx = host->pdx; + sd_card_t *card = &pdx->card; + + DbgInfo(MODULE_SD_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Enter %s Card type-%d, VDD-%d, Power-%d\n", __func__, + card->card_type, vddx, power_en); + + if (card->card_type == CARD_SD) { + if (power_en && vddx == VDD1) + value = (UHS1_BIT_EN | VDD1_BIT_EN); + else if (!power_en && vddx == VDD1) + value = (UHS1_BIT_EN); + } else if (card->card_type == CARD_UHS2) { + if (power_en && vddx == VDD1) + value = (UHS2_BIT_EN | VDD1_BIT_EN); + else if (power_en && vddx == VDD2) + value = (UHS2_BIT_EN | VDD2_BIT_EN); + else if (!power_en) + value = (UHS2_BIT_EN); + } else if (card->card_type == CARD_SD70) { + if (power_en && vddx == VDD1) + value = (SD70_BIT_EN | VDD1_BIT_EN); + else if (power_en && vddx == VDD2) + value = (SD70_BIT_EN | VDD2_BIT_EN); + else if (!power_en) + value = (SD70_BIT_EN); + } else { + if (power_en && vddx == VDD1) + value = (VDD1_BIT_EN); + if (power_en && vddx == VDD2) + value = (VDD2_BIT_EN); + else if (!power_en) + value = 0x00; + } + + set_pattern_value(host, value); + + DbgInfo(MODULE_SD_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +bool shift_bit_func_enable(sd_host_t *host) +{ + if (host->cfg != NULL) { + if ((host->cfg->card_item.sd7_sdmode_switch_control.shift_byte_en) + && (host->chip_type == CHIP_GG8)) + return TRUE; + else + return FALSE; + } else + return FALSE; +} diff --git a/drivers/scsi/bht/host/hostreg.h b/drivers/scsi/bht/host/hostreg.h new file mode 100644 index 000000000000..1629e18140e0 --- /dev/null +++ b/drivers/scsi/bht/host/hostreg.h @@ -0,0 +1,478 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: hostreg.h + * + * Environment: OS Independent + * + */ + +#define SDHCI_DMA_ADDRESS 0x00 +#define SDHCI_ARGUMENT2 SDHCI_DMA_ADDRESS + +#define SDHCI_BLOCK_SIZE 0x04 +#define SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF)) + +#define SDHCI_BLOCK_COUNT 0x06 + +#define SDHCI_ARGUMENT 0x08 + +#define SDHCI_TRANSFER_MODE 0x0C +#define SDHCI_TRNS_DMA 0x01 +#define SDHCI_TRNS_BLK_CNT_EN 0x02 +#define SDHCI_TRNS_AUTO_CMD12 0x04 +#define SDHCI_TRNS_AUTO_CMD23 0x08 +#define SDHCI_TRNS_READ 0x10 +#define SDHCI_TRNS_MULTI 0x20 +#define SDHCI_TRNS_RESP_R5 0x40 +#define SDHCI_TRNS_RESP_CHK 0x80 +#define SDHCI_TRNS_RESP_INTR_DIS 0x100 + +#define SDHCI_RSP_NONE 0x00000000 +#define SDHCI_RSP_TYPE_R1 0x001A0000 +#define SDHCI_RSP_TYPE_R2 0x00090000 +#define SDHCI_RSP_TYPE_R3 0x00020000 +#define SDHCI_RSP_TYPE_R1B 0x001B0000 +#define SDHCI_CMD_TYPE_12_OR_52 0x00C00000 +#define SDCHI_CMD_DATA_PRESENT 0x00200000 + +#define SDHCI_COMMAND 0x0E + +/* add for VDD2 GPIO */ +#define SDBAR1_GPIO_FUNC_SEL_508 0x508 +#define SDBAR1_GPIO_FUNC_GPIOCTRL_510 0x510 + +#define SDHCI_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff)) +#define SDHCI_GET_CMD(c) ((c>>8) & 0x3f) + +#define SDHCI_RESPONSE 0x10 + +#define SDHCI_UHS2_CMD12_RES 0x1C + +#define SDHCI_BUFFER 0x20 + +#define SDHCI_PRESENT_STATE 0x24 +#define SDHCI_CMD_INHIBIT 0x00000001 +#define SDHCI_DATA_INHIBIT 0x00000002 +#define SDHCI_DOING_WRITE 0x00000100 +#define SDHCI_DOING_READ 0x00000200 +#define SDHCI_SPACE_AVAILABLE 0x00000400 +#define SDHCI_DATA_AVAILABLE 0x00000800 +#define SDHCI_CARD_PRESENT 0x00010000 +#define SDHCI_CARD_PRESENT_QUIRK 0x00050000 +#define SDHCI_WRITE_PROTECT 0x00080000 +#define SDHCI_DATA_LVL_MASK 0x00F00000 +#define SDHCI_CMD_LVL_MASK 0x01000000 +#define SDHCI_UHS2_IF_DETECT 0x80000000 +#define SDHCI_UHS2_LANE_SYNC 0x40000000 +#define SDHCI_UHS2_DMT_STATUS 0x20000000 +#define SDHCI_DATA_LVL_SHIFT 20 +#define SDHCI_CMD_LVL_SHIFT 24 + +#define SDHCI_HOST_CONTROL 0x28 +#define SDHCI_CTRL_LED 0x01 +#define SDHCI_CTRL_4BITBUS 0x02 +#define SDHCI_CTRL_HISPD 0x04 +#define SDHCI_CTRL_DMA_MASK 0x18 +#define SDHCI_CTRL_SDMA 0x00 +#define SDHCI_CTRL_ADMA1 0x08 +#define SDHCI_CTRL_ADMA32 0x10 +#define SDHCI_CTRL_ADMA64 0x18 +#define SDHCI_CTRL_DMA_MASK 0x18 +#define SDHCI_CTRL_8BITBUS 0x20 + +#define SDHCI_POWER_CONTROL 0x29 +#define SDHCI_POWER_VDD1_ON 0x0100 +#define SDHCI_POWER_VDD1_180 0x0A00 +#define SDHCI_POWER_VDD1_300 0x0C00 +#define SDHCI_POWER_VDD1_330 0x0E00 +#define SDHCI_POWER_VDD1_MASK 0x0F00 +#define SDHCI_POWER_VDD2_180 0xA000 +#define SDHCI_POWER_VDD2_120 0x9000 +#define SDHCI_POWER_VDD2_ON 0x1000 +#define SDHCI_POWER_VDD2_MASK 0xF000 + +#define SDBAR1_GPIO_FUNC_SEL_508 0x508 +#define SDBAR1_GPIO_FUNC_GPIOCTRL_510 0x510 +#define VDDX_PWR_SOURCE_EXTERNAL 1 +#define VDDX_PWR_SOURCE_INTERNAL 0 +#define VDDX_POLARITY_ACTIVE_HIGH 1 +#define VDDX_POLARITY_ACTIVE_LOW 0 +#define VDDX_PWR_VOLTAGE_1V2 0 +#define VDDX_PWR_VOLTAGE_1V8 1 +#define VDDX_PWR_VOLTAGE_3V3 2 +#define VDD1 1 +#define VDD2 2 +#define VDD3 3 +#define POWER_ON 1 +#define POWER_OFF 0 + +#define SDBAR1_SW_CTL_LED_GPIO0_50C 0x50C +#define SDBAR1_GPIO_1_2_CTRL_REG_510 0x510 +#define SDBAR1_WP_GPIO3_CTRL_REG_514 0x514 +#define SDBAR1_Ext_INT_MASK_REG_518 0x518 +#define SDBAR1_Ext_INT_STATUS_REG_51C 0x51C +#define SDBAR1_Ext_INT_SIGNAL_MASK_REG_520 0x520 + +#define ADJUST_EXPEXTED_RANGE_TIMEOUT_COUNT 10 + +#define SDHCI_BLOCK_GAP_CONTROL 0x2A + +#define SDHCI_WAKE_UP_CONTROL 0x2B +#define SDHCI_WAKE_ON_INT 0x01 +#define SDHCI_WAKE_ON_INSERT 0x02 +#define SDHCI_WAKE_ON_REMOVE 0x04 + +#define SDHCI_CLOCK_CONTROL 0x2C +#define SDHCI_DIVIDER_SHIFT 8 +#define SDHCI_DIVIDER_HI_SHIFT 6 +#define SDHCI_DIV_MASK 0xFF +#define SDHCI_DIV_MASK_LEN 8 +#define SDHCI_DIV_HI_MASK 0x300 +#define SDHCI_PROG_CLOCK_MODE 0x0020 +#define SDHCI_HOST_PLL_EN 0x0008 +#define SDHCI_CLOCK_CARD_EN 0x0004 +#define SDHCI_CLOCK_INT_STABLE 0x0002 +#define SDHCI_CLOCK_INT_EN 0x0001 +#define SDHCI_DIVIDER_CLEAR 0xffff003f + +#define SDHCI_TIMEOUT_CONTROL 0x2E +#define SDHCI_DAT_TIMEOUT_MASK 0xF0 +#define SDHCI_DAT_TIMEOUT_VAL 0x0E + +#define SDHCI_RESET_ALL 0x01000000 +#define SDHCI_RESET_CMD 0x02000000 +#define SDHCI_RESET_DATA 0x04000000 + +#define SDHCI_INT_STATUS 0x30 +#define SDHCI_INT_ENABLE 0x34 +#define SDHCI_SIGNAL_ENABLE 0x38 +#define SDHCI_INT_CMD_COMP 0x00000001 +#define SDHCI_INT_TRANSFER_COMP 0x00000002 +#define SDHCI_INT_BLK_GAP 0x00000004 +#define SDHCI_INT_DMA_END 0x00000008 +#define SDHCI_INT_SPACE_AVAIL 0x00000010 +#define SDHCI_INT_DATA_AVAIL 0x00000020 +#define SDHCI_INT_CARD_INSERT 0x00000040 +#define SDHCI_INT_CARD_REMOVE 0x00000080 +#define SDHCI_INT_CARD_INT 0x00000100 +#define SDHCI_INT_ROC_BITS 0x00007F00 +#define SDHCI_INT_ERROR 0x00008000 + +#define SDHCI_INT_ERR_STATUS 0x32 +/* 16bit define for error */ +#define SDHCI_INT_TIMEOUT (1<<0) +#define SDHCI_INT_CRC (1<<1) +#define SDHCI_INT_END_BIT (1<<2) +#define SDHCI_INT_INDEX (1<<3) +#define SDHCI_INT_DATA_TIMEOUT (1<<4) +#define SDHCI_INT_DATA_CRC (1<<5) +#define SDHCI_INT_DATA_END_BIT (1<<6) +#define SDHCI_INT_BUS_POWER (1<<7) +#define SDHCI_INT_ACMD12ERR (1<<8) +#define SDHCI_INT_ADMA_ERROR (1<<9) +#define SDHCI_INT_TUNING_ERROR (1<<10) +#define SDHCI_INT_RESP_ERROR (1<<11) + +#define SDHCI_INT_NORMAL_MASK 0x00007FFF +#define SDHCI_INT_ERROR_MASK 0xFFFF8000 + +#define SDHCI_INT_CMD_ERROR_MASK 0x000F +#define SDHCI_INT_DAT_ERROR_MASK 0x0070 +#define SDHCI_INT_DATCMD_ERR_MASK 0x007F + +#define SDHCI_INT_ALL_MASK ((unsigned int)-1) + +#define SDHCI_INT_BUFFER_READY_BITS (SDHCI_INT_SPACE_AVAIL|SDHCI_INT_DATA_AVAIL) +#define SDHCI_INT_INSERT_REMOVE_CARD_BITS (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE) +#define SDHCI_INT_NORMAL_BITS (SDHCI_INT_CMD_COMP | SDHCI_INT_TRANSFER_COMP | \ + SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_AVAIL | SDHCI_INT_DMA_END) +#define SDHCI_INT_SDMA_BITS (SDHCI_INT_CMD_COMP | \ + SDHCI_INT_TRANSFER_COMP | SDHCI_INT_DMA_END) +#define SDHCI_INT_ADMA_BITS (SDHCI_INT_CMD_COMP | SDHCI_INT_TRANSFER_COMP) + +#define SDHCI_INT_ERR_NON_DATA 0x0F +#define SDHCI_INT_ERR_TUNING_CMD (SDHCI_INT_ERR_NON_DATA | SDHCI_INT_TUNING_ERROR) +#define SDHCI_INT_ERR_DATA_CMD (SDHCI_INT_ERR_NON_DATA | 0x70) +#define SDHCI_INT_ERR_ADMA_CMD (SDHCI_INT_ERR_NON_DATA | 0x70 | SDHCI_INT_ADMA_ERROR) + +#define SDHCI_ACMD12_ERR 0x3C + +#define SDHCI_HOST_CONTROL2 0x3E +#define SDHCI_CTRL_UHS_MASK 0x0007 +#define SDHCI_CTRL_UHS_SDR12 0x0000 +#define SDHCI_CTRL_UHS_HS400 0x0005 +#define SDHCI_CTRL_UHS_HS200 0x0003 +#define SDHCI_CTRL_UHS_SDR25 0x0001 +#define SDHCI_CTRL_UHS_SDR50 0x0002 +#define SDHCI_CTRL_UHS_SDR104 0x0003 +#define SDHCI_CTRL_UHS_DDR50 0x0004 + +/* reserved value in SDIO spec */ +#define SDHCI_CTRL_HS_SDR200 0x0005 +#define SDHCI_CTRL_UHS2 0x0007 + +#define SDHCI_CTRL_VDD_180 0x0008 +#define SDHCI_CTRL_DRV_TYPE_MASK 0x0030 +#define SDHCI_CTRL_DRV_TYPE_B 0x0000 +#define SDHCI_CTRL_DRV_TYPE_A 0x0010 +#define SDHCI_CTRL_DRV_TYPE_C 0x0020 +#define SDHCI_CTRL_DRV_TYPE_D 0x0030 +#define SDHCI_CTRL_EXEC_TUNING 0x0040 +#define SDHCI_CTRL_TUNED_CLK 0x0080 +#define SDHCI_CTRL_UHS2IF_EN 0x0100 +#define SDHCI_CTRL_ADMA2_26BIT_EN 0x0400 +#define SDHCI_CTRL_CMD23_EN 0x0800 +#define SDHCI_CTRL_VER4_EN 0x1000 +#define SDHCI_CTRL_64BIT_EN 0x2000 +#define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000 + +#define SDHCI_CAPABILITIES 0x40 +#define SDHCI_TIMEOUT_CLK_MASK 0x0000003F +#define SDHCI_TIMEOUT_CLK_SHIFT 0 +#define SDHCI_TIMEOUT_CLK_UNIT 0x00000080 +#define SDHCI_CLOCK_BASE_MASK 0x00003F00 +#define SDHCI_CLOCK_V3_BASE_MASK 0x0000FF00 +#define SDHCI_CLOCK_BASE_SHIFT 8 +#define SDHCI_MAX_BLOCK_MASK 0x00030000 +#define SDHCI_MAX_BLOCK_SHIFT 16 +#define SDHCI_CAN_DO_8BIT 0x00040000 +#define SDHCI_CAN_DO_ADMA2 0x00080000 +#define SDHCI_CAN_DO_ADMA1 0x00100000 +#define SDHCI_CAN_DO_HISPD 0x00200000 +#define SDHCI_CAN_DO_SDMA 0x00400000 +#define SDHCI_CAN_VDD_330 0x01000000 +#define SDHCI_CAN_VDD_300 0x02000000 +#define SDHCI_CAN_VDD_180 0x04000000 +#define SDHCI_CAN_64BIT_V4 0x08000000 +#define SDHCI_CAN_64BIT_V3 0x10000000 + +#define SDHCI_SUPPORT_SDR50 0x00000001 +#define SDHCI_SUPPORT_SDR104 0x00000002 +#define SDHCI_SUPPORT_DDR50 0x00000004 +#define SDHCI_DRIVER_TYPE_A 0x00000010 +#define SDHCI_DRIVER_TYPE_C 0x00000020 +#define SDHCI_DRIVER_TYPE_D 0x00000040 +#define SDHCI_RETUNING_TIMER_COUNT_MASK 0x00000F00 +#define SDHCI_RETUNING_TIMER_COUNT_SHIFT 8 +#define SDHCI_USE_SDR50_TUNING 0x00002000 +#define SDHCI_RETUNING_MODE_MASK 0x0000C000 +#define SDHCI_RETUNING_MODE_SHIFT 14 +#define SDHCI_CLOCK_MUL_MASK 0x00FF0000 + +#define SDHCI_CLOCK_MUL_SHIFT 16 + +#define SDHCI_CAPABILITIES_1 0x44 +#define SDHCI_CAP1_ADMA3_SUPP (BIT27) +#define SDHCI_CAP1_UHS2_SUPP 0x00000008 +#define SDHCI_CAP1_UHS2_VDD2_MASK 0x30000000 +#define SDHCI_CAN_VDD2_18V 0x10000000 +#define SDHCI_CAN_VDD2_12V 0x20000000 +#define SDHCI_CAP1_UHS2_VDD2_SHIFT 28 + +#define SDHCI_MAX_CURRENT 0x48 +#define SDHCI_MAX_CURRENT_LIMIT 0xFF +#define SDHCI_MAX_CURRENT_330_MASK 0x0000FF +#define SDHCI_MAX_CURRENT_330_SHIFT 0 +#define SDHCI_MAX_CURRENT_300_MASK 0x00FF00 +#define SDHCI_MAX_CURRENT_300_SHIFT 8 +#define SDHCI_MAX_CURRENT_180_MASK 0xFF0000 +#define SDHCI_MAX_CURRENT_180_SHIFT 16 + +#define SDHCI_MAX_CURRENT_2 0x4C +#define SDHCI_MAX_CUR_VDD2_180_MASK 0x000000FF +#define SDHCI_MAX_CUR_VDD2_180_SHIFT 0 + +#define SDHCI_ERROR_INTR_EVENT 0x52 + +#define SDHCI_MAX_CURRENT_MULTIPLIER 4 + +/* 4C-4F reserved for more max current */ + +#define SDHCI_SET_ACMD12_ERROR 0x50 +#define SDHCI_SET_INT_ERROR 0x52 + +#define SDHCI_ADMA_ERROR 0x54 + +/* 55-57 reserved */ + +#define SDHCI_ADMA_ADDRESS 0x58 +#define SDHCI_ADMA_ADDRESSH 0x5C + +/* 60-FB reserved */ +#define SDHCI_ADMA3_ADDRESS 0x78 +#define SDHCI_ADMA3_ADDRESSH 0x7C + +#define SDHCI_PRESET_FOR_SDR12 0x66 +#define SDHCI_PRESET_FOR_SDR25 0x68 +#define SDHCI_PRESET_FOR_SDR50 0x6A +#define SDHCI_PRESET_FOR_SDR104 0x6C +#define SDHCI_PRESET_FOR_DDR50 0x6E +#define SDHCI_PRESET_DRV_MASK 0xC000 +#define SDHCI_PRESET_DRV_SHIFT 14 +#define SDHCI_PRESET_CLKGEN_SEL_MASK 0x400 +#define SDHCI_PRESET_CLKGEN_SEL_SHIFT 10 +#define SDHCI_PRESET_SDCLK_FREQ_MASK 0x3FF +#define SDHCI_PRESET_SDCLK_FREQ_SHIFT 0 + +/* 80-FB UHS-II Regsiters */ +#define SDHCI_UHS2_BLOCK_SIZE 0x80 +#define SDHCI_UHS2_BLOCK_COUNT 0x84 +#define SDHCI_UHS2_CMD_PKG(offset) (0x88 + (offset)) +#define SDHCI_UHS2_PLD_PKG(offset) (0x8C + (offset)) + +#define SDHCI_UHS2_TRAN_MODE 0x9C +#define SDHCI_UHS2_TRAN_HALF_DUPLEX 0x8000 +#define SDHCI_UHS2_TRAN_EBSY_WAIT 0x4000 +#define SDHCI_UHS2_RESP_INTR_DIS 0x0100 +#define SDHCI_UHS2_RESP_CHK 0x0080 +#define SDHCI_UHS2_RESP_TYPE_R5 0x0040 + +#define SDHCI_UHS2_TRAN_WRITE 0x10 +#define SDHCI_UHS2_TRAN_BLK_EN 0x02 +#define SDHCI_UHS2_TRAN_DMA_EN 0x01 + +#define SDHCI_UHS2_CMD_DATA_PRESENT 0x200000 +#define SDHCI_UHS2_CMD_TYPE_NORMAL 0x000000 +#define SDHCI_UHS2_CMD_TYPE_ABORT 0x400000 +#define SDHCI_UHS2_CMD_TYPE_CMD12 0x800000 +#define SDHCI_UHS2_CMD_TYPE_GODORMANT 0xC00000 + +#define SDHCI_UHS2_RESPONSE 0xA0 +#define SDHCI_UHS2_RESPONSE4 0xA4 + +#define SDHCI_UHS2_SOFT_RST 0xC0 +#define SDHCI_UHS2_SDTRAN_RESET 0x02 +#define SDHCI_UHS2_FULL_RESET 0x01 + +#define SDHCI_UHS2_TIMER_CTRL 0xC2 +#define SDHCI_UHS2_ERRINT_STS 0xC4 +#define SDHCI_UHS2_ERR_RESP BIT1 +#define SDHCI_UHS2_ERRINT_STS_EN 0xC8 +#define SDHCI_UHS2_ERRINT_SIG_EN 0xCC + +#define SDHCI_UHS2_SETTING_BASE 0xE0 +#define SDHCI_UHS2_CAPABILITY_BASE 0xE2 +#define SDHCI_UHS2_TEST_BASE 0xE4 +#define SDCHI_UHS2_VENDOR_BASE 0xE8 + +#define SDHCI_UHS2_BUS_TOP_MASK 0x00C00000 +#define SDHCI_UHS2_BUS_TOP_SHIFT 22 + +#define SDHCI_UHS2_DEVICE_NUM_MASK 0x003C0000 +#define SDHCI_UHS2_DEVICE_NUM_SHIFT 18 + +#define SDHCI_UHS2_LANE_MASK 0x00003F00 +#define SDHCI_UHS2_LANE_SHIFT 8 + +#define SDHCI_UHS2_GAP_MASK 0x000000F0 +#define SDHCI_UHS2_GAP_SHIFT 4 + +#define SDHCI_UHS2_DAP_MASK 0x0000000F + +#define SDHCI_UHS2_LSS_DIR_MASK 0x00F00000 +#define SDHCI_UHS2_LSS_DIR_SHIFT 20 + +#define SDHCI_UHS2_LSS_SYN_MASK 0x000F0000 +#define SDHCI_UHS2_LSS_SYN_SHIFT 16 + +#define SDHCI_UHS2_SPEED_MASK 0x000000C0 +#define SDHCI_UHS2_SPEED_SHIFT 6 + +#define SDHCI_UHS2_NFCU_MASK 0x0000FF00 +#define SDHCI_UHS2_NFCU_SHIFT 8 + +#define SDHCI_UHS2_MAX_BLK_MASK 0xFFF00000 +#define SDHCI_UHS2_MAX_BLK_SHIFT 20 + +#define SDHCI_UHS2_DATE_GAP_MASK 0x000000FF + +#define SDHCI_UHS2_LANE_FUNC_MASK 0x00000F00 +#define SDHCI_UHS2_LANE_FUNC_SHIFT 8 + +#define SDHCI_UHS2_POWER_MODE_MASK 0x00000001 +#define SDHCI_UHS2_POWER_MODE_SHIFT 0 + +#define SDHCI_UHS2_HIBERNATE_MASK 0x00008000 +#define SDHCI_UHS2_HIBERNATE_SHIFT 15 + +#define SDHCI_UHS2_RETRY_CNT_MASK 0x00030000 +#define SDHCI_UHS2_RETRY_CNT_SHIFT 16 + +#define SDHCI_MAX_LOCATABLE_REG 0x1FF +#define SDHCI_UHS2_IDX_GENERAL 0 +#define SDHCI_UHS2_IDX_PHYSICAL 4 +#define SDHCI_UHS2_IDX_LNKTRL 8 +#define SDHCI_UHS2_IDX_LNKTRH 12 + +#define SDHCI_UHS2_INT_HEADER 0x00000001 +#define SDHCI_UHS2_INT_RES_PKT 0x00000002 +#define SDHCI_UHS2_INT_RETRY_EXP 0x00000004 +#define SDHCI_UHS2_INT_CRC 0x00000008 +#define SDHCI_UHS2_INT_FRAMING 0x00000010 +#define SDHCI_UHS2_INT_TID 0x00000020 +#define SDHCI_UHS2_INT_UNRECOVER 0x00000080 +#define SDHCI_UHS2_INT_EBSY 0x00000100 +#define SDHCI_UHS2_INT_ADMA 0x00008000 +#define SDHCI_UHS2_INT_TO_CMD_RES 0x00010000 +#define SDHCI_UHS2_INT_TO_DEADLOCK 0x00020000 +#define SDHCI_UHS2_INT_VEN 0xF8000000 +#define SDHCI_UHS2_INT_ERR_ALL 0xFFFFFFFF + +#define SDHCI_SLOT_INT_STATUS 0xFC + +#define SDHCI_HOST_VERSION 0xFE +#define SDHCI_VENDOR_VER_MASK 0xFF00 +#define SDHCI_VENDOR_VER_SHIFT 8 +#define SDHCI_SPEC_VER_MASK 0x00FF +#define SDHCI_SPEC_VER_SHIFT 0 +#define SDHCI_SPEC_100 0 +#define SDHCI_SPEC_200 1 +#define SDHCI_SPEC_300 2 +#define SDHCI_SPEC_400 3 + +/* + * below is the vendor regisrer + */ + +#define SDHCI_VEN_SPEC_CTRL 0x110 +#define SDHCI_HW_TUNING (BIT4) +#define SDHCI_EMMC_HS_DDR (BIT5) +#define SDHCI_ENABLE_HS400 (1 << 17) + +#define SDHCI_SAMPLE_CLK_RESULT_LOW 0x1A8 +#define SDHCI_SAMPLE_CLK_RESULT_UP 0x1AC +#define SDHCI_DLL_PHASE_CFG 0x1B0 + +#define SDHCI_DRIVER_CTRL_REG 0x1c0 +#define SDHCI_OCB_INT_MASK (1 << 5) +#define SDHCI_VENDOR_SW_INT_BIT (1<<8) +#define SDHCI_DRIVER_CTRL_ADMA2_ENABLE_INF (1<<18) +#define SDHCI_DRIVER_CTRL_ADMA2_START_INF (1<<19) +#define SDHCI_OCB_FET_INT_DENOUNCE (1 << 22) +#define SDHCI_OCB_FET_INT_ACTIVE (1 << 23) +#define SDHCI_DRIVER_CTRL_FIFO_EMPTY 0x800 + +#define SDHCI_DLL_WATCH_DOG 0x1cc +#define SDHCI_CTRL_VDD2_120 0x80000000 +#define SDHCI_PLL_RESET 0x00001000 +#define SDHCI_PLL_UNLOCKBIT 0x00004000 +#define SDHCI_UHS2_PHY_SETTING 0x1D0 + +/* + * O2/BHT add BAR1 for PCIR mapping registers + * These registers is defined by O2/BHT, but we may follow name definition rule. + */ + +/* PCI CFG Space Register Mapping Value Register */ +#define VEN_PCIRMappingVal (0x200) + +/* PCI CFG Space Register Mapping Control Register */ +#define VEN_PCIRMappingCtl (0x204) + +/* PCI CFG Space Register Mapping Enable Register */ +#define VEN_PCIRMappingEn (0x208) + +/* GPIO control register*/ +#define VEN_GPIOCTL (0x210) diff --git a/drivers/scsi/bht/host/hostven.c b/drivers/scsi/bht/host/hostven.c new file mode 100644 index 000000000000..2434439856ba --- /dev/null +++ b/drivers/scsi/bht/host/hostven.c @@ -0,0 +1,2774 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: hostven.c + * + * Abstract: Include host vendor defined operations + * + * Version: 1.00 + * + * Author: Samuel + * + * Environment: OS Independent + * + * History: + * + * 9/2/2014 Creation Samuel + */ + +#include "../include/basic.h" +#include "hostreg.h" +#include "../include/hostapi.h" +#include "../include/debug.h" +#include "../include/hostvenapi.h" +#include "../include/cfgmng.h" +#include "../include/reqapi.h" + +void host_enable_clock(sd_host_t *host, bool on); +void host_internal_clk_setup(sd_host_t *host, bool on); +void host_init_clock(sd_host_t *host, u32 value); +void host_init_400k_clock(sd_host_t *host); + +/* PCI 16bit access */ +u16 pci_readw(sd_host_t *host, u16 offset) +{ + u32 i = 0; + u32 tmp[3] = { 0 }; + u16 reg_val = 0; + + if ((host->chip_type == CHIP_SDS0) || + (host->chip_type == CHIP_SDS1) || + (host->chip_type == CHIP_FUJIN2) || + (host->chip_type == CHIP_SEABIRD) || + (host->chip_type == CHIP_SEAEAGLE)) { + /* + * For Sandstorm, HW implement a mapping method by + * memory space reg to access PCI reg. + */ + + /* Enable mapping */ + + /* Check function conflict */ + if ((host->chip_type == CHIP_SDS0) || + (host->chip_type == CHIP_FUJIN2) || + (host->chip_type == CHIP_SEABIRD) || + (host->chip_type == CHIP_SEAEAGLE)) { + i = 0; + ven_writel(host, VEN_PCIRMappingEn, 0x40000000); + while ((ven_readl(host, VEN_PCIRMappingEn) & 0x40000000) + == 0) { + if (i == 5) + goto RD_DIS_MAPPING; + + os_mdelay(1); + i++; + ven_writel(host, VEN_PCIRMappingEn, 0x40000000); + + } + } else if (host->chip_type == CHIP_SDS1) { + i = 0; + + ven_writel(host, VEN_PCIRMappingEn, 0x20000000); + while ((ven_readl(host, VEN_PCIRMappingEn) & 0x20000000) + == 0) { + if (i == 5) + goto RD_DIS_MAPPING; + + os_mdelay(1); + i++; + ven_writel(host, VEN_PCIRMappingEn, 0x20000000); + } + } + + /* Check last operation is complete */ + i = 0; + while (ven_readl(host, VEN_PCIRMappingCtl) & 0xc0000000) { + if ((i == 5) + || ven_readl(host, + VEN_PCIRMappingCtl) == 0xffffffff) { + goto RD_DIS_MAPPING; + } + + os_mdelay(1); + i += 1; + } + + /* Set register address due to hardware constraint */ + + tmp[0] |= 0x40000000; + tmp[0] |= offset & 0xfffc; + ven_writel(host, VEN_PCIRMappingCtl, tmp[0]); + + /* Check read is complete */ + i = 0; + while (ven_readl(host, VEN_PCIRMappingCtl) & 0x40000000) { + if ((i == 5) + || ven_readl(host, + VEN_PCIRMappingCtl) == 0xffffffff) { + goto RD_DIS_MAPPING; + } + + os_mdelay(1); + i += 1; + } + + /* Get PCIR value */ + tmp[1] = ven_readl(host, VEN_PCIRMappingVal); + + if (offset & 0x2) + tmp[1] >>= 16; + +RD_DIS_MAPPING: + /* Disable mapping */ + ven_writel(host, VEN_PCIRMappingEn, 0x80000000); + + DbgInfo(MODULE_VEN_HOST, FEATURE_PCIREG_TRACER, NOT_TO_RAM, + "%s offset=%x Value:%x\n", __func__, offset, + (u16) tmp[1]); + return (u16) tmp[1]; + } else if (host->chip_type == CHIP_SEAEAGLE2) { + reg_val = ven_readw(host, offset); + DbgInfo(MODULE_VEN_HOST, FEATURE_PCIREG_TRACER, NOT_TO_RAM, + "%s offset=%x Value:%x\n", __func__, offset, + reg_val); + return reg_val; + } else if (host->chip_type == CHIP_GG8 + || host->chip_type == CHIP_ALBATROSS) { + reg_val = ven_readw(host, offset); + DbgInfo(MODULE_VEN_HOST, FEATURE_PCIREG_TRACER, NOT_TO_RAM, + "%s offset=%x Value:%x\n", __func__, offset, + reg_val); + return reg_val; + } + + DbgInfo(MODULE_VEN_HOST, FEATURE_PCIREG_TRACER, NOT_TO_RAM, + "%s offset=%x Value:%x\n", __func__, offset, (u16) tmp[0]); + return (u16) tmp[0]; + +} + +void pci_writew(sd_host_t *host, u16 offset, u16 value) +{ + u32 tmp = 0; + u32 i = 0; + + DbgInfo(MODULE_VEN_HOST, FEATURE_PCIREG_TRACEW, NOT_TO_RAM, + "%s, Addr:%x, Value: %x\n", __func__, offset, value); + + if ((host->chip_type == CHIP_SDS0) || + (host->chip_type == CHIP_SDS1) || + (host->chip_type == CHIP_FUJIN2) || + (host->chip_type == CHIP_SEABIRD) || + (host->chip_type == CHIP_SEAEAGLE)) { + /* + * For Sandstorm, HW implement a mapping method by + * memory space reg to access PCI reg. + * Upper caller doesn't need to set 0xD0. + */ + + /* Enable mapping */ + + /* Check function conflict */ + if ((host->chip_type == CHIP_SDS0) || + (host->chip_type == CHIP_FUJIN2) || + (host->chip_type == CHIP_SEABIRD) || + (host->chip_type == CHIP_SEAEAGLE)) { + i = 0; + ven_writel(host, VEN_PCIRMappingEn, 0x40000000); + while ((ven_readl(host, VEN_PCIRMappingEn) & 0x40000000) + == 0) { + if (i == 5) + goto WR_DIS_MAPPING; + + os_mdelay(1); + i++; + ven_writel(host, VEN_PCIRMappingEn, 0x40000000); + } + } else if (host->chip_type == CHIP_SDS1) { + i = 0; + ven_writel(host, VEN_PCIRMappingEn, 0x20000000); + + while ((ven_readl(host, VEN_PCIRMappingEn) & 0x20000000) + == 0) { + if (i == 5) + goto WR_DIS_MAPPING; + + os_mdelay(1); + i++; + ven_writel(host, VEN_PCIRMappingEn, 0x20000000); + } + } + + /* Enable MEM access */ + ven_writel(host, VEN_PCIRMappingVal, 0x80000000); + ven_writel(host, VEN_PCIRMappingCtl, 0x800000D0); + + /* Check last operation is complete */ + i = 0; + while (ven_readl(host, VEN_PCIRMappingCtl) & 0xc0000000) { + if ((i == 5) + || ven_readl(host, + VEN_PCIRMappingCtl) == 0xffffffff) { + goto WR_DIS_MAPPING; + } + + os_mdelay(1); + i += 1; + } + + /* Set write value */ + if (offset & 0x2) { + u32 val32 = value; + + val32 <<= 16; + offset = offset & 0xfffc; + ven_writel(host, VEN_PCIRMappingVal, val32); + /* Set register address */ + tmp |= 0x80030000; + tmp |= offset; + } else { + ven_writel(host, VEN_PCIRMappingVal, value); + /* Set register address */ + tmp |= 0x800c0000; + tmp |= offset; + } + ven_writel(host, VEN_PCIRMappingCtl, tmp); + + /* Check write is complete */ + i = 0; + while (ven_readl(host, VEN_PCIRMappingCtl) & 0x80000000) { + if ((i == 5) + || ven_readl(host, + VEN_PCIRMappingCtl) == 0xffffffff) { + goto WR_DIS_MAPPING; + } + + os_mdelay(1); + i += 1; + } + +WR_DIS_MAPPING: + /* Disable MEM access */ + ven_writel(host, VEN_PCIRMappingVal, 0x80000001); + ven_writel(host, VEN_PCIRMappingCtl, 0x800000D0); + + /* Check last operation is complete */ + i = 0; + while (ven_readl(host, VEN_PCIRMappingCtl) & 0xc0000000) { + if ((i == 5) + || ven_readl(host, + VEN_PCIRMappingCtl) == 0xffffffff) { + break; + } + + os_mdelay(1); + i += 1; + } + + /* Disable function conflict */ + + /* Disable mapping */ + ven_writel(host, VEN_PCIRMappingEn, 0x80000000); + } else if (host->chip_type == CHIP_SEAEAGLE2) { + ven_writew(host, offset, value); + } else if (host->chip_type == CHIP_GG8 + || host->chip_type == CHIP_ALBATROSS) { + ven_writew(host, offset, value); + } +} + +void pci_orw(sd_host_t *host, u16 offset, u16 value) +{ + u16 reg_val = 0; + + reg_val = pci_readw(host, offset); + reg_val |= value; + pci_writew(host, offset, reg_val); + DbgInfo(MODULE_SD_HOST, FEATURE_VENREG_TRACER, NOT_TO_RAM, + "[PCI] pci orw(0x%08X): 0x%08X\n", offset, reg_val); +} + +void pci_andw(sd_host_t *host, u16 offset, u16 value) +{ + u16 reg_val = 0; + + reg_val = pci_readw(host, offset); + reg_val &= value; + pci_writew(host, offset, reg_val); + DbgInfo(MODULE_SD_HOST, FEATURE_VENREG_TRACER, NOT_TO_RAM, + "[PCI] pci andw(0x%08X): 0x%08X\n", offset, reg_val); +} + +/* PCI 32bit access */ +u32 pci_readl(sd_host_t *host, u16 offset) +{ + u32 result = 0; + u32 i = 0; + u32 tmp[3] = { 0 }; + u32 reg_val = 0; + + if ((host->chip_type == CHIP_SDS0) || + (host->chip_type == CHIP_SDS1) || + (host->chip_type == CHIP_FUJIN2) || + (host->chip_type == CHIP_SEABIRD) || + (host->chip_type == CHIP_SEAEAGLE)) { + /* + * For Sandstorm, HW implement a mapping method by + * memory space reg to access PCI reg. + */ + + /* Enable mapping */ + + /* Check function conflict */ + if ((host->chip_type == CHIP_SDS0) || + (host->chip_type == CHIP_FUJIN2) || + (host->chip_type == CHIP_SEABIRD) || + (host->chip_type == CHIP_SEAEAGLE)) { + i = 0; + ven_writel(host, VEN_PCIRMappingEn, 0x40000000); + while ((ven_readl(host, VEN_PCIRMappingEn) & 0x40000000) + == 0) { + if (i == 5) + goto RD_DIS_MAPPING; + + os_mdelay(1); + i++; + ven_writel(host, VEN_PCIRMappingEn, 0x40000000); + + } + } else if (host->chip_type == CHIP_SDS1) { + i = 0; + + ven_writel(host, VEN_PCIRMappingEn, 0x20000000); + while ((ven_readl(host, VEN_PCIRMappingEn) & 0x20000000) + == 0) { + if (i == 5) + goto RD_DIS_MAPPING; + + os_mdelay(1); + i++; + ven_writel(host, VEN_PCIRMappingEn, 0x20000000); + } + } + + /* Check last operation is complete */ + i = 0; + while (ven_readl(host, VEN_PCIRMappingCtl) & 0xc0000000) { + if ((i == 5) + || ven_readl(host, + VEN_PCIRMappingCtl) == 0xffffffff) { + goto RD_DIS_MAPPING; + } + + os_mdelay(1); + i += 1; + } + + /* Set register address */ + tmp[0] |= 0x40000000; + tmp[0] |= offset; + ven_writel(host, VEN_PCIRMappingCtl, tmp[0]); + + /* Check read is complete */ + i = 0; + while (ven_readl(host, VEN_PCIRMappingCtl) & 0x40000000) { + if ((i == 5) + || ven_readl(host, + VEN_PCIRMappingCtl) == 0xffffffff) { + goto RD_DIS_MAPPING; + } + + os_mdelay(1); + i += 1; + } + + /* Get PCIR value */ + tmp[1] = ven_readl(host, VEN_PCIRMappingVal); + +RD_DIS_MAPPING: + /* Disable mapping */ + ven_writel(host, VEN_PCIRMappingEn, 0x80000000); + + DbgInfo(MODULE_VEN_HOST, FEATURE_PCIREG_TRACER, NOT_TO_RAM, + "%s offset=%x Value:%x\n", __func__, offset, + tmp[1]); + result = tmp[1]; + } else if (host->chip_type == CHIP_SEAEAGLE2) { + reg_val = ven_readl(host, offset); + DbgInfo(MODULE_VEN_HOST, FEATURE_PCIREG_TRACER, NOT_TO_RAM, + "%s offset=%x Value:%x\n", __func__, offset, + reg_val); + result = reg_val; + } else if (host->chip_type == CHIP_GG8 + || host->chip_type == CHIP_ALBATROSS) { + reg_val = ven_readl(host, offset); + DbgInfo(MODULE_VEN_HOST, FEATURE_PCIREG_TRACER, NOT_TO_RAM, + "%s offset=%x Value:%x\n", __func__, offset, + reg_val); + result = reg_val; + } else { + + DbgInfo(MODULE_VEN_HOST, FEATURE_PCIREG_TRACER, NOT_TO_RAM, + "%s offset=%x Value:%x\n", __func__, offset, + tmp[0]); + result = tmp[0]; + } + + return result; +} + +void pci_writel(sd_host_t *host, u16 offset, u32 value) +{ + u32 tmp = 0; + u32 i = 0; + + DbgInfo(MODULE_VEN_HOST, FEATURE_PCIREG_TRACEW, NOT_TO_RAM, + "%s, Addr:%x, Value:%x\n", __func__, offset, value); + if ((host->chip_type == CHIP_SDS0) || (host->chip_type == CHIP_SDS1) + || (host->chip_type == CHIP_FUJIN2) + || (host->chip_type == CHIP_SEABIRD) + || (host->chip_type == CHIP_SEAEAGLE)) { + /* + * For Sandstorm, HW implement a mapping method by + * memory space reg to access PCI reg. + * Upper caller doesn't need to set 0xD0. + */ + + /* Enable mapping */ + + /* Check function conflict */ + if ((host->chip_type == CHIP_SDS0) || + (host->chip_type == CHIP_FUJIN2) || + (host->chip_type == CHIP_SEABIRD) || + (host->chip_type == CHIP_SEAEAGLE)) { + i = 0; + ven_writel(host, VEN_PCIRMappingEn, 0x40000000); + while ((ven_readl(host, VEN_PCIRMappingEn) & 0x40000000) + == 0) { + if (i == 5) + goto WR_DIS_MAPPING; + + os_mdelay(1); + i++; + ven_writel(host, VEN_PCIRMappingEn, 0x40000000); + } + } else if (host->chip_type == CHIP_SDS1) { + i = 0; + ven_writel(host, VEN_PCIRMappingEn, 0x20000000); + + while ((ven_readl(host, VEN_PCIRMappingEn) & 0x20000000) + == 0) { + if (i == 5) + goto WR_DIS_MAPPING; + + os_mdelay(1); + i++; + ven_writel(host, VEN_PCIRMappingEn, 0x20000000); + } + } + + /* Enable MEM access */ + ven_writel(host, VEN_PCIRMappingVal, 0x80000000); + ven_writel(host, VEN_PCIRMappingCtl, 0x800000D0); + + /* Check last operation is complete */ + i = 0; + while (ven_readl(host, VEN_PCIRMappingCtl) & 0xc0000000) { + if ((i == 5) + || ven_readl(host, + VEN_PCIRMappingCtl) == 0xffffffff) { + goto WR_DIS_MAPPING; + } + + os_mdelay(1); + i += 1; + } + + /* Set write value */ + ven_writel(host, VEN_PCIRMappingVal, value); + /* Set register address */ + tmp |= 0x80000000; + tmp |= offset; + ven_writel(host, VEN_PCIRMappingCtl, tmp); + + /* Check write is complete */ + i = 0; + while (ven_readl(host, VEN_PCIRMappingCtl) & 0x80000000) { + if ((i == 5) + || ven_readl(host, + VEN_PCIRMappingCtl) == 0xffffffff) { + goto WR_DIS_MAPPING; + } + + os_mdelay(1); + i += 1; + } + +WR_DIS_MAPPING: + /* Disable MEM access */ + ven_writel(host, VEN_PCIRMappingVal, 0x80000001); + ven_writel(host, VEN_PCIRMappingCtl, 0x800000D0); + + /* Check last operation is complete */ + i = 0; + while (ven_readl(host, VEN_PCIRMappingCtl) & 0xc0000000) { + if ((i == 5) + || ven_readl(host, + VEN_PCIRMappingCtl) == 0xffffffff) { + break; + } + + os_mdelay(1); + i += 1; + } + + /* Disable function conflict */ + + /* Disable mapping */ + ven_writel(host, VEN_PCIRMappingEn, 0x80000000); + } else if (host->chip_type == CHIP_SEAEAGLE2) { + ven_writel(host, offset, value); + } else if (host->chip_type == CHIP_GG8 + || host->chip_type == CHIP_ALBATROSS) { + ven_writel(host, offset, value); + } + +} + +void pci_orl(sd_host_t *host, u16 offset, u32 value) +{ + u32 reg_val = 0; + + reg_val = pci_readl(host, offset); + reg_val |= value; + pci_writel(host, offset, reg_val); +} + +void pci_andl(sd_host_t *host, u16 offset, u32 value) +{ + u32 reg_val = 0; + + reg_val = pci_readl(host, offset); + reg_val &= value; + pci_writel(host, offset, reg_val); + +} + +void hostven_update_dmdn(sd_host_t *host, u32 dmdn) +{ + u32 regval; + + DbgInfo(MODULE_VEN_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Enter %s, dmdn = %x\n", __func__, dmdn); + if ((host->chip_type == CHIP_SDS0) || (host->chip_type == CHIP_FUJIN2) + || (host->chip_type == CHIP_SEABIRD) + || (host->chip_type == CHIP_SEAEAGLE) + || (host->chip_type == CHIP_SEAEAGLE2) + || (host->chip_type == CHIP_GG8) + || (host->chip_type == CHIP_ALBATROSS)) { + /* 0x304[28:16] */ + regval = pci_readl(host, 0x304); + regval &= 0x0000ffff; + regval |= (dmdn << 16); + pci_writel(host, 0x304, regval); + } else if (host->chip_type == CHIP_SDS1) { + /* 0x384[28:16] */ + regval = pci_readl(host, 0x384); + regval &= 0x0000ffff; + regval |= (dmdn << 16); + pci_writel(host, 0x384, regval); + } + + DbgInfo(MODULE_VEN_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + +} + +static bool dma_need_host_infinite_support(u32 dma_mode) +{ + if ((dma_mode == CFG_TRANS_MODE_ADMA2) || + (dma_mode == CFG_TRANS_MODE_ADMA2_SDMA_LIKE) || + (dma_mode == CFG_TRANS_MODE_ADMA_MIX_SDMA_LIKE) || + (dma_mode == CFG_TRANS_MODE_ADMA_MIX)) + return TRUE; + else + return FALSE; +} + +void hostven_drive_strength_cfg(sd_host_t *host) +{ + u32 regval; + u32 i; + + DbgInfo(MODULE_VEN_HOST, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + + for (i = 0; i < MAX_PCR_SETTING_SIZE; i++) { + if ((host->cfg->pcr_item.pcr_tb[i].valid_flg == 0) + && (host->cfg->pcr_item.pcr_tb[i].addr == 0x304)) { + DbgInfo(MODULE_VEN_HOST, FEATURE_RW_TRACE, NOT_TO_RAM, + "PCR 0x304 set is invalid.\n"); + goto exit; + } + } + + regval = pci_readl(host, 0x304); + + if (regval & BIT7) { + DbgInfo(MODULE_VEN_HOST, FEATURE_RW_TRACE, NOT_TO_RAM, + "BIOS setting enable.\n"); + } else if (host->cfg->host_item.host_drive_strength.ds_selection_enable) { + DbgInfo(MODULE_VEN_HOST, FEATURE_RW_TRACE, NOT_TO_RAM, + "Host's SD IO drive setting enable.\n"); + + /* Unlock write protect */ + pci_andl(host, 0xd0, ~BIT31); + + /* + * If detect PCR 0x304[7] = 0, + * please set host_drive_strength[14:12, 10:8, 6:4, 3:1] value + * to PCR 0x304[14:12, 10, 8, 6 : 4, 3 : 1] when driver loading + */ + + regval &= ~0x0000777E; + regval |= + (host->cfg->host_item.host_drive_strength.clk_driver_strength_3_3v << 12 | + host->cfg->host_item.host_drive_strength.data_cmd_driver_strength_3_3v << 8 | + host->cfg->host_item.host_drive_strength.clk_driver_strength_1_8v << 4 | + host->cfg->host_item.host_drive_strength.cmd_driver_strength_1_8v << 1); + + pci_writel(host, 0x304, regval); + + /* Lock write protect */ + pci_orl(host, 0xd0, BIT31); + } else { + DbgInfo(MODULE_VEN_HOST, FEATURE_RW_TRACE, NOT_TO_RAM, + "Host's SD IO drive setting disable.\n"); + } + +exit: + DbgInfo(MODULE_VEN_HOST, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +void hostven_transfer_init(sd_host_t *host, bool enable) +{ + DbgInfo(MODULE_VEN_HOST, FEATURE_RW_TRACE, NOT_TO_RAM, + "Enter %s, enable = %x\n", __func__, enable); + + /* If not ADMA2 infinite transfer return */ + if (FALSE == + dma_need_host_infinite_support( + host->cfg->host_item.test_dma_mode_setting.dma_mode)) + return; + + /* when don't use inf while inifinte is enable */ + if (enable) { + sdhci_or32(host, SDHCI_DRIVER_CTRL_REG, + SDHCI_DRIVER_CTRL_ADMA2_ENABLE_INF); + } else { + sdhci_and32(host, SDHCI_DRIVER_CTRL_REG, + ~(SDHCI_DRIVER_CTRL_ADMA2_ENABLE_INF)); + } + + DbgInfo(MODULE_VEN_HOST, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); + +} + +static void hostven_bios_cfg(sd_host_t *host) +{ + bht_dev_ext_t *pdx = host->pdx; + u16 regval; + u32 pcr_item_index = 0; + + cfg_vdd_power_source_item_t *cfg = + &host->cfg->host_item.vdd_power_source_item; + + /* initial setting for hsmux_vcme */ + cfg_hsmux_vcme_enable_item_t *cfg_hsmux_vcme = + &host->cfg->feature_item.hsmux_vcme_enable; + + DbgInfo(MODULE_VEN_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* + * Stop the soft L0 request for d3silence sub mode 2 because + * the sub mode 2 will open the soft L0 request at the card removal handle + */ + if (pdx->pm_state.d3_silc_en && pdx->pm_state.d3_silc_submode2_en) + pci_andl(host, 0x3e4, ~(1 << 23)); + + if (host->cfg->driver_item.camera_mode_ctrl_vdd1_vdd2_cd == 1) + goto camera_mode; + else + goto pc_mode; + +pc_mode: + /* for GG8: thomas.hu add for VDD 1,2,3 power source default setting. */ + + DbgInfo(MODULE_VEN_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "vdd_power_source_item: 0x%08X\n", *(u32 *) cfg); + + /* set vdd1 power source select internal/external and polarity */ + regval = ven_readw(host, SDBAR1_GPIO_FUNC_SEL_508); + + /* clear field for vdd1 power source select internal/external and polarity */ + regval &= ~(7 << 2); + + /* bit4: external, bit3: internal */ + regval |= + (cfg->vdd1_power_source == + VDDX_PWR_SOURCE_EXTERNAL) ? (1 << 4) : (1 << 3); + /* bit2: polarity: 1 for active high, 0 for low */ + regval |= + (cfg->vdd1_onoff_polarity == + VDDX_POLARITY_ACTIVE_HIGH) ? (1 << 2) : (0 << 2); + ven_writew(host, SDBAR1_GPIO_FUNC_SEL_508, regval); + + DbgInfo(MODULE_VEN_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "cfg->vdd1_power_source: 0x%X\n", cfg->vdd1_power_source); + DbgInfo(MODULE_VEN_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "cfg->vdd1_onoff_polarity: 0x%X\n", cfg->vdd1_onoff_polarity); + + /* set Vdd2 power source */ + regval = ven_readw(host, SDBAR1_GPIO_FUNC_SEL_508); + /* use internal LDO */ + if (cfg->vdd2_power_source == VDDX_PWR_SOURCE_INTERNAL) { + /* enable vdd2 internal powersource */ + regval &= ~(1 << 9); + } + /* use external power source. */ + else { + /* enable vdd2 external powersource */ + regval |= (1 << 9); + } + ven_writew(host, SDBAR1_GPIO_FUNC_SEL_508, regval); + + DbgInfo(MODULE_VEN_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "cfg->vdd2_power_source: 0x%X\n", cfg->vdd2_power_source); + + /* set Vdd3 power source */ + regval = ven_readw(host, SDBAR1_GPIO_FUNC_SEL_508); + + /* use internal LDO */ + if (cfg->vdd3_power_source == VDDX_PWR_SOURCE_INTERNAL) { + /* enable vdd3 internal powersource */ + regval &= ~(1 << 13); + } + /* use external power source. */ + else { + /* enable vdd3 external powersource */ + regval |= (1 << 13); + } + ven_writew(host, SDBAR1_GPIO_FUNC_SEL_508, regval); + + DbgInfo(MODULE_VEN_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "cfg->vdd3_power_source: 0x%X\n", cfg->vdd3_power_source); + + /* set vdd2/3's control signal: GPIO 1/2 default value */ + if (!(shift_bit_func_enable(host))) { + regval = ven_readw(host, SDBAR1_GPIO_FUNC_GPIOCTRL_510); + regval &= (u16)(~(0xFFFF)); + regval |= (0x0808); + ven_writew(host, SDBAR1_GPIO_FUNC_GPIOCTRL_510, regval); + } + /* set vdd2/3's GPIO power control inverter */ + regval = ven_readw(host, SDBAR1_GPIO_FUNC_GPIOCTRL_510); + regval &= ~(1 << 7 | 1 << 15); + ven_writew(host, SDBAR1_GPIO_FUNC_GPIOCTRL_510, regval); + + DbgInfo(MODULE_VEN_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "cfg_hsmux_vcme: 0x%X\n", *cfg_hsmux_vcme); + if (cfg_hsmux_vcme->enable) { + regval = + (cfg_hsmux_vcme->rc_rx_vcme << 3) | + (cfg_hsmux_vcme->sd_tx_vcme << 2) | + (cfg_hsmux_vcme->sd_rx_vcme << 1) | + (cfg_hsmux_vcme->rc_tx_vcme << 0); + /* clear 440 bit[31:28] and set 440 bit[31:28] */ + pci_andw(host, 0x442, (u16)(~(0xF << 12))); + pci_orw(host, 0x442, (regval << 12)); + } + + /* + * set host IO drive strength, PCR 0x304 bit7 is control switch. + * 0: set host IO drive strength by driver; + * 1: set host IO drive strength by BIOS + */ + if ((pci_readw(host, 0x304) & BIT7)) { + for (pcr_item_index = 0; + pcr_item_index < host->cfg->pcr_item.cnt; + pcr_item_index++) { + if ((host->cfg->pcr_item.pcr_tb[pcr_item_index].type == 0) + && (host->cfg->pcr_item.pcr_tb[pcr_item_index].addr == 0x304)) { + host->cfg->pcr_item.pcr_tb[pcr_item_index].valid_flg = 0; + DbgInfo(MODULE_VEN_HOST, FEATURE_DRIVER_INIT, + NOT_TO_RAM, + "disabled PCR 0x304 pcr config valid_flg\n"); + } + } + } + + /* set Uhs2 L0 clock request mode control in non-dormant state */ + if (host->chip_type == CHIP_SEAEAGLE) { + pci_andl(host, 0x35C, 0xFFFFFFFC); + DbgInfo(MODULE_VEN_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "uhs2_setting.l1_requirement_source: 0x%X\n", + host->cfg->card_item.uhs2_setting.l1_requirement_source); + pci_orl(host, 0x35C, + host->cfg->card_item.uhs2_setting.l1_requirement_source); + } + + goto exit; + +camera_mode: + + /* enable GPIO3 input, polling solution */ + regval = ven_readw(host, SDBAR1_WP_GPIO3_CTRL_REG_514); + /* clear bit [4:0] */ + regval &= ~0x1f; + regval |= (1 << 1 | 1 << 4); + ven_writew(host, SDBAR1_WP_GPIO3_CTRL_REG_514, regval); + + /* set VDD1 controlled by GPIO 2, and default value */ + regval = ven_readw(host, SDBAR1_GPIO_FUNC_GPIOCTRL_510); + /* clear bit [13:8] */ + regval &= ~(0x3f << 8); + /* set bit [11], GPIO2 output, default vdd1 off */ + regval |= (1 << 11); + ven_writew(host, SDBAR1_GPIO_FUNC_GPIOCTRL_510, regval); + + /* set VDD2 controlled by GPIO 1, and default value */ + /* select vdd2_en_pol =1 */ + pci_andl(host, 0x444, ~(1 << 17)); + + /* nothing, SDPWR_EXT_SEL(CDN) is fixed to 0 */ + + regval = ven_readw(host, SDBAR1_GPIO_1_2_CTRL_REG_510); + /* clear bit [5:0] */ + regval &= ~(0x3f); + /* GPIO4 output & input, default vdd2 off */ + regval |= (1 << 3); + ven_writew(host, SDBAR1_GPIO_1_2_CTRL_REG_510, regval); + + goto exit; + +exit: + DbgInfo(MODULE_VEN_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + +} + +/* + * + * Function Name: hostven_ltr_issue + * + * Abstract: + * + * The issue fix for Seabird PM issue 15#. + * The issue is the hardware LTR state machine issue. + * + * Input: + * + * host [in]: A pointer to the host structure. + * + * + * Output: + * + * None + * + * Return value: + * + * NULL. + * + * Notes: + * + * + * + */ +static void hostven_ltr_issue(sd_host_t *host) +{ + u32 reg_val = 0; + + DbgInfo(MODULE_VEN_HOST, FEATURE_PM_TRACE | FEATURE_DRIVER_INIT, + NOT_TO_RAM, "Enter %s\n", __func__); + + if ((host->chip_type == CHIP_FUJIN2) || + (host->chip_type == CHIP_SEABIRD)) { + reg_val = pci_readl(host, 0xa8); + if (reg_val & (1 << 10)) { + pci_writel(host, 0xa8, (reg_val & (~(1 << 10)))); + pci_writel(host, 0xa8, (reg_val | (1 << 10))); + } + } + + DbgInfo(MODULE_VEN_HOST, FEATURE_PM_TRACE | FEATURE_DRIVER_INIT, + NOT_TO_RAM, "Exit %s\n", __func__); +} + +/* + * + * Function Name: hostven_dma_engine_issue + * + * Abstract: + * + * DMA engine is not reset and DMA registers is reset to default value + * (default value is MWr) when remove SD card. + * + * Input: + * + * host [in]: A pointer to the host structure. + * + * + * Output: + * + * None + * + * Return value: + * + * NULL. + * + * Notes: + * + * disable DMA reset + * + */ +static void hostven_dma_engine_issue(sd_host_t *host) +{ + u32 reg_val = 0; + + DbgInfo(MODULE_VEN_HOST, FEATURE_PM_TRACE | FEATURE_DRIVER_INIT, + NOT_TO_RAM, "Enter %s\n", __func__); + reg_val = pci_readl(host, 0x308); + DbgErr("old PCR Register 0x308: 0x%x\n", reg_val); + reg_val |= 0xC00000; + pci_writel(host, 0x308, reg_val); + DbgErr("new PCR Register 0x308: 0x%x\n", pci_readl(host, 0x308)); + DbgInfo(MODULE_VEN_HOST, FEATURE_PM_TRACE | FEATURE_DRIVER_INIT, + NOT_TO_RAM, "Exit %s\n", __func__); +} + +/* + * + * Function Name: hostven_ms_sd30_dis + * + * Abstract: + * + * Disable the SD 3.0 function for the Microsoft win7 driver + * + * Input: + * + * host [in]: A pointer to the host structure. + * + * + * Output: + * + * None + * + * Return value: + * + * NULL. + * + * Notes: + * + * Add SD 3.0 enable bit for Microsoft driver: + * pcr 0x3E4[22] : MS SD driver disable. + * 0-- enable; 1-- disable. Default 0. + * Pcr3e4[22] :MS SD driver disable. + * 0: enable; + * 1: disable. + * Default 0. + * + * Add sd3.0 enable for MS driver. + * Pcr f8[31]: + * 0: sd2.0; + * 1: sd3.0. + * Default 0. + * + * Set pcr {3e4[22] ,f8[31]}=2'b00 + * Use Microsoft dirver sd2.0 driver, + * base clock is 50M and base frequency capability is 8'h32; + * + * Set pcr {3e4[22] ,f8[31]}=2'b01 + * Use Microsoft dirver sd3.0 driver, + * base clock is 200M and base frequency capability is 8'hc8; + * + * Set pcr 3e4[22]=1'b1 + * Use O2 driver, base clock is set by pcr 304 + * and base frequency capability is set by pcr328[15:8]; + * + */ +static void hostven_ms_sd30_dis(sd_host_t *host) +{ + DbgInfo(MODULE_VEN_HOST, FEATURE_PM_TRACE | FEATURE_DRIVER_INIT, + NOT_TO_RAM, "Enter %s\n", __func__); + + if ((host->chip_type == CHIP_SEABIRD) || + (host->chip_type == CHIP_SEAEAGLE) || + (host->chip_type == CHIP_SEAEAGLE2) || + (host->chip_type == CHIP_GG8) || + (host->chip_type == CHIP_ALBATROSS)) { + pci_orl(host, 0x3e4, 1 << 22); + } + DbgInfo(MODULE_VEN_HOST, FEATURE_PM_TRACE | FEATURE_DRIVER_INIT, + NOT_TO_RAM, "Exit %s\n", __func__); +} + +void hostven_ocb_cfg(sd_host_t *host) +{ + cfg_ocb_ctrl_t *test_ocb_ctrl = &(host->cfg->host_item.test_ocb_ctrl); + + /* + * if software to set the power off and clear the OCB status, + * need to disable hw power off function + */ + if (test_ocb_ctrl->sw_pwroff_en) { + /* unlock write protect bit */ + pci_andl(host, 0xd0, 0x7fffffff); + pci_andl(host, 0xd4, ~0x10); + /* restore write protect bit */ + pci_orl(host, 0xd0, (1 << 31)); + } else { + pci_andl(host, 0xd0, 0x7fffffff); + pci_orl(host, 0xd4, 0x10); + pci_orl(host, 0xd0, (1 << 31)); + } + + if ((host->chip_type == CHIP_FUJIN2) || + (host->chip_type == CHIP_SEABIRD) + ) { + /* host 0x1c0 [22][5] */ + sdhci_or32(host, SDHCI_DRIVER_CTRL_REG, + (SDHCI_OCB_FET_INT_DENOUNCE | SDHCI_OCB_INT_MASK)); + } + + if (test_ocb_ctrl->int_check_en == 0) { + /* clear 0x1c0 [5] */ + sdhci_and32(host, SDHCI_DRIVER_CTRL_REG, ~SDHCI_OCB_INT_MASK); + } + +} + +void hostven_switch_flow_cfg(sd_host_t *host) +{ + u32 regval = 0; + + DbgInfo(MODULE_VEN_HOST, FEATURE_PM_TRACE | FEATURE_DRIVER_INIT, + NOT_TO_RAM, "Enter %s\n", __func__); + + pci_andw(host, 0x444, 0xF8FF); + switch (host->cfg->card_item.sd7_sdmode_switch_control.switch_method_ctrl) { + case HW_DETEC_HW_SWITCH: + pci_orw(host, 0x444, BIT8); + break; + + case SW_POLL_SW_SWITCH: + case SW_POLL_INTER_SW_SWITCH: + pci_orw(host, 0x444, BIT9); + break; + + case SW_POLL_SWCTRL_SWITCH: + case SW_POLL_INTER_SWCRTL_SWITCH: + pci_orw(host, 0x444, BIT10); + if (host->cfg->card_item.sd7_sdmode_switch_control.sw_ctrl_polarit) + pci_orw(host, 0x444, BIT15); + else + pci_andw(host, 0x444, (u16)(~BIT15)); + + break; + + default: + DbgErr + ("Error:no such value in registry sd7_sdmode_switch_control, use default value\n"); + pci_orw(host, 0x444, BIT8); + break; + } + + regval = pci_readl(host, 0x444); + + regval = pci_readl(host, 0x328); + if (host->cfg->card_item.sd7_sdmode_switch_control.vdd3_control) + regval |= (1 << 5); + else + regval &= ~(1 << 5); + + pci_writel(host, 0x328, regval); + + if (host->cfg->card_item.sd7_sdmode_switch_control.sd70_trail_run) { + /* sd7.0 trail run case */ + /* set pcr 0x444[11] = 1, default 0 */ + regval = pci_readl(host, 0x444); + regval |= (1 << 11); + pci_writel(host, 0x444, regval); + + } else { + /* sd7.0 cdm switch case */ + regval = pci_readl(host, 0x444); + regval &= (~(1 << 11)); + pci_writel(host, 0x444, regval); + } + + DbgInfo(MODULE_VEN_HOST, FEATURE_PM_TRACE | FEATURE_DRIVER_INIT, + NOT_TO_RAM, "Exit %s,0x444 = 0x%x\n", __func__, regval); +} + +void hostven_cmd_low_cfg(sd_host_t *host) +{ + + u32 regval = 0; + + DbgInfo(MODULE_VEN_HOST, FEATURE_PM_TRACE | FEATURE_DRIVER_INIT, + NOT_TO_RAM, "Enter %s\n", __func__); + + if (host->cfg->card_item.sd7_sdmode_switch_control.sd_cmd_low_function_en) { + /* enable source */ + regval = pci_readl(host, 0x444); + regval |= (1 << 13); + pci_writel(host, 0x444, regval); + + /* enable event status interrupt */ + regval = pci_readl(host, 0x448); + regval |= (0x3 << 29); + regval &= ~(1 << 31); + pci_writel(host, 0x448, regval); + } else { + /* disable source */ + regval = pci_readl(host, 0x444); + regval &= ~(1 << 13); + pci_writel(host, 0x444, regval); + + /* disable event status interrupt */ + regval = pci_readl(host, 0x448); + regval &= ~(0x3 << 29); + pci_writel(host, 0x448, regval); + } + + regval = pci_readl(host, 0x444); + + DbgInfo(MODULE_VEN_HOST, FEATURE_PM_TRACE | FEATURE_DRIVER_INIT, + NOT_TO_RAM, "Exit %s,0x444 = 0x%x\n", __func__, regval); +} + +void hostven_pinshare_cfg(sd_host_t *host) +{ + cfg_driver_item_t *driver_item = &(host->cfg->driver_item); + u32 temp_value; + u32 switch_method = + host->cfg->card_item.sd7_sdmode_switch_control.switch_method_ctrl; + /* clear GPIOs setting to 0 */ + ven_and16(host, 0x510, 0xF8F8); + + if (!shift_bit_func_enable(host)) + ven_and16(host, 0x514, 0xFFF8); + + /* + * SD7.0 card remove interrupt status bit can't be cleanned by BHT driver, + * Add below part to clean 0x51c bit 2 when driver loading + */ + + DbgInfo(MODULE_VEN_HOST, FEATURE_PM_TRACE | FEATURE_DRIVER_INIT, + NOT_TO_RAM, "Enter %s\n", __func__); + + if (switch_method == SW_POLL_SWCTRL_SWITCH + || switch_method == SW_POLL_INTER_SWCRTL_SWITCH) { + temp_value = pci_readl(host, 0x51C); + temp_value |= 0x2; + pci_writel(host, 0x51C, temp_value); + } + + if (host->device_id != 0x9862) { + if (host->cfg->card_item.sd7_sdmode_switch_control.camera_mode_enable) { + /* set PCR 0x444[14]=1 to enable camera mode */ + temp_value = pci_readl(host, 0x444); + temp_value |= (1 << 14); + pci_writel(host, 0x444, temp_value); + } else { + /* set PCR 0x444[14]=0 to disable camera mode */ + temp_value = pci_readl(host, 0x444); + temp_value &= ~(1 << 14); + pci_writel(host, 0x444, temp_value); + } + + ven_and16(host, 0x50C, 0xFF78); + + /* set led polarity */ + if (driver_item->led_polarity) + ven_or16(host, 0x50C, 0x80); + + if ((switch_method == SW_POLL_SWCTRL_SWITCH + || switch_method == SW_POLL_INTER_SWCRTL_SWITCH) + && driver_item->sw_ctl_led_gpio0 == 0) { + ven_and16(host, 0x518, 0xFFF3); + ven_and16(host, 0x520, 0xFFF3); + + /* set BAR1 0x50c[2:0] = 000. */ + ven_or16(host, 0x50C, 0x10); + + ven_or16(host, 0x518, 0x0c); + ven_or16(host, 0x520, 0x0c); + + } else { + switch (driver_item->sw_ctl_led_gpio0) { + case 1: + ven_or16(host, 0x50C, 0x01); + + break; + case 2: + ven_or16(host, 0x50C, 0x02); + + break; + + default: + ven_or16(host, 0x50C, 0x03); + break; + } + } + + } + + if (shift_bit_func_enable(host)) { + /* gpio1 */ + /* GPIO1 input enable for GPIO and external interrupt. */ + ven_or32(host, 0x510, 0x00000010); + /* 000h: Register-controlled GPIO and external interrupt. */ + ven_and32(host, 0x510, 0xFFFFFFF0); + /* GPIO1 negedge/posedge interrupt enable bit */ + ven_or32(host, 0x518, 0x00000030); + /* GPIO1 negedge/posedge interrupt signal enable bit */ + ven_or32(host, 0x520, 0x00000030); + + /* gpio2 */ + /* GPIO2 output enable for GPIO only. */ + ven_or32(host, 0x510, 0x00000800); + /* 000h: Register-controlled GPIO. */ + ven_and32(host, 0x510, 0xFFFFF8FF); + /* Default status: GPIO2 output low, */ + set_gpio_levels(host, 0, 0); + + /* gpio3 */ + /* GPIO3 output enable for GPIO only. */ + ven_or32(host, 0x514, 0x00000008); + /* 010h: Register-controlled GPIO */ + ven_or32(host, 0x514, 0x00000002); + /* Default status: GPIO3 output high. */ + set_gpio_levels(host, 1, 1); + + temp_value = pci_readl(host, 0x51C); + if (temp_value & 0x4) { + temp_value |= 0x4; + pci_writel(host, 0x51C, temp_value); + } + + } + + switch (driver_item->led_gpio1) { + /* use as GPIO */ + case 0: + break; + /* use as led */ + case 1: + ven_or16(host, 0x510, 0x30); + break; + default: + ven_or16(host, 0x510, 0x30); + break; + } + + switch (driver_item->led_gpio2) { + /* use as GPIO */ + case 0: + break; + /* use as led */ + case 1: + ven_or16(host, 0x510, 0x0100); + break; + case 2: + default: + ven_or16(host, 0x510, 0x0300); + break; + } + + switch (driver_item->wp_led_gpio3) { + /* use as sd_wp */ + case 0: + break; + /* use as led */ + case 1: + ven_or16(host, 0x514, 0x01); + break; + /* use as gpio */ + case 2: + break; + /* use as RTD3 function test */ + case 3: + ven_or16(host, 0x514, 0x2A); + break; + default: + ven_or16(host, 0x514, 0x03); + break; + } +} + +/* + * + * Function Name: hostven_dll_watchdog_timer + * + * Abstract: + * + * Set the DLL watch dog timer register value. + * + * Input: + * + * host [in]: A pointer to the host structure. + * + * + * Output: + * + * None + * + * Return value: + * + * NULL. + * + * Notes: + * + * + * + * + */ +static void hostven_dll_watchdog_timer(sd_host_t *host) +{ + DbgInfo(MODULE_VEN_HOST, FEATURE_PM_TRACE | FEATURE_DRIVER_INIT, + NOT_TO_RAM, "Enter %s\n", __func__); + if (host->chip_type == CHIP_FUJIN2) + sdhci_writew(host, 0x1c8, 0x1280); + DbgInfo(MODULE_VEN_HOST, FEATURE_PM_TRACE | FEATURE_DRIVER_INIT, + NOT_TO_RAM, "Exit %s\n", __func__); + +} + +static void hostven_socket_pow_en(sd_host_t *host) +{ + u32 reg_val = 0; + + DbgInfo(MODULE_VEN_HOST, FEATURE_PM_TRACE | FEATURE_DRIVER_INIT, + NOT_TO_RAM, "Enter %s\n", __func__); + if ((host->chip_type == CHIP_SDS0) || (host->chip_type == CHIP_SDS1) + || (host->chip_type == CHIP_FUJIN2)) { + reg_val = pci_readl(host, 0xec); + reg_val |= 0x3; + pci_writel(host, 0xec, reg_val); + } + + DbgInfo(MODULE_VEN_HOST, FEATURE_PM_TRACE | FEATURE_DRIVER_INIT, + NOT_TO_RAM, "Exit %s\n", __func__); +} + +static bool seabird_pcr_check(u16 addr) +{ + if (addr < 0x100) { + if (((addr >= 0x64) && (addr < 0x6b)) + || ((addr >= 0x74) && (addr < 0x7f)) || ((addr >= 0xd0) + && (addr < 0xff))) + return FALSE; + else + return TRUE; + } else if ((addr >= 0x300) && (addr < 0xfff)) { + return FALSE; + } else { + return TRUE; + } +} + +static bool seaeagle_pcr_check(u16 addr) +{ + if (addr < 0x100) { + if (((addr >= 0x64) && (addr < 0x6b)) + || ((addr >= 0x74) && (addr < 0x7f)) || ((addr >= 0xd0) + && (addr < 0xff))) + return FALSE; + else + return TRUE; + } else if ((addr >= 0x300) && (addr < 0xfff)) { + return FALSE; + } else { + return TRUE; + } +} + +#if (1) +static bool hostven_pcr_need_direct_access(sd_host_t *host, u16 addr) +{ + if (host->chip_type == CHIP_SEABIRD) + return seabird_pcr_check(addr); + else + return seaeagle_pcr_check(addr); +} +#else +static bool hostven_pcr_need_direct_access(u16 addr) +{ + + if (addr < 0x100) { + switch (addr & 0xfffc) { + case 0x64: + case 0x68: + case 0x74: + case 0x78: + case 0x7c: + case 0xd0: + case 0xd4: + case 0xd8: + case 0xdc: + case 0xe0: + case 0xe8: + case 0xec: + case 0xf0: + case 0xf4: + case 0xfc: + return FALSE; + default: + return TRUE; + } + } else if (addr <= 0xfff && addr >= 0x300) { + return FALSE; + } else + return TRUE; +} +#endif + +static void hostven_load_pcr_cfg(sd_host_t *host) +{ + + cfg_item_t *cfg = host->cfg; + u32 i = 0; + cfg_pcr_t *pcr = 0; + + for (i = 0; i < MAX_PCR_SETTING_SIZE; i++) { + u16 val = 0; + + pcr = &cfg->pcr_item.pcr_tb[i]; + if (pcr->valid_flg == 0) + continue; + switch (pcr->type) { + case 0: + val = pcr->mask; + PrintMsg + ("PCR Setting: Addr=0x%04X, Mask=0x%04X, Val==0x%04X\n", + pcr->addr, pcr->mask, pcr->val); + if (TRUE == + hostven_pcr_need_direct_access(host, pcr->addr)) { + u32 reg_val = 0; + u32 mask = pcr->mask; + u32 val32 = pcr->val; + + reg_val = pci_readl(host, pcr->addr & 0xfffc); + if (pcr->addr & 2) { + mask <<= 16; + val32 <<= 16; + } + reg_val &= ~mask; + reg_val |= (val32 & mask); + pci_cfgio_writel(host, pcr->addr & 0xfffc, + reg_val); + } else { + u16 reg_val16 = 0; + u32 reg_val = 0; + + reg_val16 = pci_readw(host, pcr->addr); + reg_val16 &= (~val); + reg_val16 |= (pcr->val & pcr->mask); + pci_writew(host, pcr->addr, reg_val16); + reg_val = pci_readw(host, pcr->addr); + + } + break; + case 1: + val = pcr->mask; + PrintMsg + ("MEM Setting: Addr=0x%04X, Mask=0x%04X, Val==0x%04X\n", + pcr->addr, pcr->mask, pcr->val); + sdhci_and16(host, pcr->addr, ~val); + val = (pcr->val & pcr->mask); + sdhci_or16(host, pcr->addr, val); + break; + default: + break; + } + } + +} + +void hostven_set_tuning_phase(sd_host_t *host, u32 input_n1, u32 output_n1, + bool off) +{ + u32 val32; + bht_dev_ext_t *pdx = host->pdx; + cfg_output_tuning_item_t *cfg = + &pdx->cfg->feature_item.output_tuning_item; + + if ((cfg->enable_dll == 0) || (cfg->enable_dll_divider == 0)) + return; + + DbgInfo(MODULE_VEN_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Enter %s\n", __func__); + + if (off == TRUE) { + pci_andl(host, 0x354, 0xFE0EFFFF); + val32 = 1; + pci_andl(host, 0x354, (~(val32 << 25))); + pci_andl(host, 0x354, (~(val32 << 26))); + sdhci_and32(host, 0x1b0, (~(val32 << 28))); + sdhci_and32(host, 0x1b0, 0xC0FFFFFF); + } else { + host_enable_clock(host, FALSE); + + pci_orl(host, 0x354, (1 << 25)); + + /* choose output tuning */ + pci_andl(host, 0x354, 0xFE0EFFFF); + pci_orl(host, 0x354, (output_n1 << 20)); + pci_orl(host, 0x354, (1 << 16)); + + /* choose input tuning */ + val32 = (sdhci_readl(host, 0x1b0) & 0xC0FFFFFF); + val32 |= (1 << 28); + val32 |= ((input_n1 % 16) << 24); + val32 |= (input_n1 >> 4) << 29; + sdhci_writel(host, 0x1b0, val32); + pci_orl(host, 0x354, (1 << 26)); + host_enable_clock(host, TRUE); + } + DbgInfo(MODULE_VEN_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +bool hostven_dll_input_tuning_init(sd_host_t *host) +{ + u8 i; + + DbgInfo(MODULE_VEN_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Enter %s\n", __func__); + + host_enable_clock(host, FALSE); + + host_internal_clk_setup(host, FALSE); + + host_init_clock(host, + host->cfg->dmdn_tbl[FREQ_DDR50_INPUT_TUNIN_START_INDEX]); + + host_internal_clk_setup(host, TRUE); + host_enable_clock(host, TRUE); + + i = 0; + while (!((sdhci_readl(host, 0x1cc) & 0x4000) + && (sdhci_readl(host, 0x1cc) & 0x800))) { + if (i > 50) + return FALSE; + os_mdelay(1); + i++; + } + + host_enable_clock(host, FALSE); + host_internal_clk_setup(host, FALSE); + pci_orl(host, 0x354, 0x6010000); + sdhci_or32(host, 0x1b0, 0x10000000); + host_internal_clk_setup(host, TRUE); + + i = 0; + while (!((sdhci_readl(host, 0x1cc) & 0x4000))) { + if (i > 50) + return FALSE; + os_mdelay(1); + i++; + } + + if ((host->chip_type == CHIP_SEAEAGLE2) || (host->chip_type == CHIP_GG8) + || (host->chip_type == CHIP_ALBATROSS)) + host_enable_clock(host, TRUE); + + sdhci_or32(host, 0x1cc, (1 << 17)); + sdhci_or32(host, 0x1cc, (1 << 16)); + + i = 0; + while (!((sdhci_readl(host, 0x1cc) & 0x800))) { + if (i > 50) + return FALSE; + os_mdelay(1); + i++; + } + + sdhci_and32(host, 0x1cc, 0xFFFCFFFF); + + i = 0; + while (!((sdhci_readl(host, 0x1cc) & 0x4000) + && (sdhci_readl(host, 0x1cc) & 0x800))) { + if (i > 50) + return FALSE; + os_mdelay(1); + i++; + } + + if (host->chip_type == CHIP_SEAEAGLE) + host_enable_clock(host, TRUE); + + DbgInfo(MODULE_VEN_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Exit %s\n", + __func__); + return TRUE; +} + +static void hostven_output_tuning_init(sd_host_t *host) +{ + + DbgInfo(MODULE_VEN_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + host->output_tuning.auto_flag = FALSE; + +} + +void hostven_detect_refclk_count_range_init(sd_host_t *host) +{ + u16 expected_range = 0; + cfg_item_t *cfg = host->cfg; + + DbgInfo(MODULE_VEN_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + if ((pci_readl(host, 0x460) & BIT31)) { + expected_range = pci_readw(host, 0x460); + DbgInfo(MODULE_VEN_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Hardware invoked auto-adjust refclk counter range done, range min %#x , max %#x\n", + (expected_range >> 8), (expected_range & 0xFF)); + } + + /* + * Enable PCIe reference clock detection timeout status and + * PCIe reference clock detection timeout interrupt signal + */ + sdhci_or32(host, 0x1E0, (BIT23 | BIT27)); + + if (cfg->feature_item.auto_detect_refclk_counter_range_ctl.enable) { + DbgInfo(MODULE_VEN_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "set refclk_range_detect_cnt: 0x%X\n", + cfg->feature_item.auto_detect_refclk_counter_range_ctl.refclk_range_detect_cnt); + + /* reference clock stable counter register set disable */ + pci_andw(host, 0x456, (u16)(~BIT15)); + + /* set cycles_of_detection_period */ + pci_andw(host, 0x462, 0xFF00); + pci_orw(host, 0x462, + (u16) cfg->feature_item.auto_detect_refclk_counter_range_ctl.refclk_range_detect_cnt); + + /* reference clock stable counter register set enable */ + pci_orw(host, 0x456, BIT15); + os_udelay(10); + + /* reference clock stable counter register set disable */ + pci_andw(host, 0x456, (u16)(~BIT15)); + + if (cfg->feature_item.auto_detect_refclk_counter_range_ctl.req_refclkcnt_minmax_source_sel) { + DbgInfo(MODULE_VEN_HOST, FEATURE_DRIVER_INIT, + NOT_TO_RAM, + "set required_refclk_count_value, min: 0x%X, max: 0x%X\n", + cfg->feature_item.auto_detect_refclk_counter_range_ctl.req_refclkcnt_min, + cfg->feature_item.auto_detect_refclk_counter_range_ctl.req_refclkcnt_max); + + /* select from register configure 0x460[15:0] */ + pci_orw(host, 0x462, BIT13); + + /* set required_refclk_count_value */ + pci_writew(host, 0x460, (((u16) + (cfg->feature_item.auto_detect_refclk_counter_range_ctl.req_refclkcnt_min) + << 8) | + cfg->feature_item.auto_detect_refclk_counter_range_ctl.req_refclkcnt_max)); + + DbgInfo(MODULE_VEN_HOST, FEATURE_DRIVER_INIT, + NOT_TO_RAM, "PCR 0x460: 0x%X\n", pci_readw(host, + 0x460)); + } + + if (cfg->feature_item.auto_detect_refclk_counter_range_ctl.refclkcnt_range_detect_softreset) { + /* set refclk_cnt_range_detect_soft_reset */ + pci_orw(host, 0x462, BIT14); + } + } + + DbgInfo(MODULE_VEN_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); +} + + /* + * Below function designe please refers to GG8 architecture + * chapter12.3: Refclk stable detection circuit + */ +void hostven_refclk_stable_detection_circuit(sd_host_t *host) +{ + cfg_item_t *cfg = host->cfg; + u32 regval; + + DbgInfo(MODULE_VEN_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Enter %s\n", __func__); + /* + * Step0: Set PCR 0xD0[31] = 0 – disable ‘write protect’ for + * BHT defined config space registers; + */ + pci_andl(host, 0xd0, ~BIT31); + + /* Step1: Set PCR 0x454[31] = 0 – disable the setting; */ + pci_andl(host, 0x454, ~BIT31); + + /* Step2: Set SD host register 1C0[1] = 1 to force L0 request; */ + sdhci_or16(host, 0x1c0, BIT1); + + /* Step3 : Set target registry with the expected parameter value; */ + + /* Step3.1 L1_ENTER_EXIT_LOGIC_CTL setting */ + if (cfg->feature_item.l1_enter_exit_logic_ctl.enable) { + /*PCR 0x444[26] */ + if (cfg->feature_item.l1_enter_exit_logic_ctl.disable_tx_command_mode) + /* disable */ + pci_orl(host, 0x444, BIT26); + else + /* enable (default) */ + pci_andl(host, 0x444, ~BIT26); + + /*PCR 0x444[28], disable “PCIe Phy Reference clock active detection logic */ + if (cfg->feature_item.l1_enter_exit_logic_ctl.disable_pcie_phy_clk) + /* disable */ + pci_orl(host, 0x444, BIT28); + else + /* enable (default) */ + pci_andl(host, 0x444, ~BIT28); + } + + /* Step3.2 REFCLK_STABLE_DETECTION_COUNTER1 */ + if (cfg->feature_item.refclk_stable_detection_counter1.enable) { + + /* PCR 0x454[15:0] */ + regval = pci_readl(host, 0x454); + regval &= 0xffff0000; + regval |= + cfg->feature_item.refclk_stable_detection_counter1.required_refclk_compare_count; + pci_writel(host, 0x454, regval); + + /* + * REFCLK_STABLE_DETECTION_COUNTER2 + * required_refclk_compare_timeout_d0l10, it is controlled by PCR 0x458[31:16] + * The detection timeout counter in d0 L1.0. + * The timeout time is required_refclk_compare_timeout_d0l10 * Tclk_2m. + * Default: ‘h001E (15us). + * + * REFCLK_STABLE_DETECTION_COUNTER2 + * required_refclk_compare_timeout_d0l11, it is controlled by PCR 0x458[15:0] + * The detection timeout counter in d0 L1.1. + * The timeout time is required_refclk_compare_timeout_d0l11 * Tclk_2m. + * Default: ‘h044C (550us). + */ + regval = pci_readl(host, 0x458); + regval &= 0x0; + regval |= + (cfg->feature_item.refclk_stable_detection_counter2.required_refclk_compare_timeout_d0l10 + << 16) | + (cfg->feature_item.refclk_stable_detection_counter2.required_refclk_compare_timeout_d0l11); + pci_writel(host, 0x458, regval); + + /* + * REFCLK_STABLE_DETECTION_COUNTER3 + * required_refclk_compare_timeout_d0l12, it is controlled by PCR 0x45C[31:16] + * The detection timeout counter in d0 L1.2. + * The timeout time is required_refclk_compare_timeout_d0l12 * Tclk_2m. + * Default: ‘h04B0 (600us). + * + * required_refclk_compare_timeout_d3l12, it is controlled by PCR 0x458C[15:0] + * The detection timeout counter in d3 L1.2 and + * “switch back from PCIe SD mode to Legacy SD mode”. + * The timeout time is required_refclk_compare_timeout_d3l12 * Tclk_2m. + * Default: ‘h24B0 (4.7ms). + */ + regval = pci_readl(host, 0x45c); + regval &= 0x0; + regval |= + (cfg->feature_item.refclk_stable_detection_counter3.required_refclk_compare_timeout_d0l12 + << 16) | + (cfg->feature_item.refclk_stable_detection_counter3.required_refclk_compare_timeout_d3l12); + pci_writel(host, 0x45c, regval); + + /* PCR 0x454[31] = 1 – enable the setting; */ + if (cfg->feature_item.refclk_stable_detection_counter1.chk_refclk_parameter_en) + pci_orl(host, 0x454, BIT31); + } + + /* Step4: Read back target registers to make sure previous accessing is OK; */ + DbgInfo(MODULE_VEN_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Check START------------------\n"); + pci_readl(host, 0x444); + pci_readl(host, 0x454); + pci_readl(host, 0x458); + pci_readl(host, 0x45c); + DbgInfo(MODULE_VEN_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Check END--------------------\n"); + + /* Step5: Set SD host register 1C0[1] = 0 clear force L0 request; */ + sdhci_and16(host, 0x1c0, ~BIT1); + + DbgInfo(MODULE_VEN_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Exit %s\n", + __func__); +} + + /* Below function designe please refers to GG8 architecture chapter12.4 */ +void hostven_pcie_phy_tx_amplitude_adjustment(sd_host_t *host) +{ + cfg_item_t *cfg = host->cfg; + u32 regval_1 = + cfg->feature_item.pcie_phy_amplitude_adjust.pcietx_amplitude_setting; + u32 regval_2; + u16 i; + u32 regval; + + struct amplitude_configuration amplitude_configuration_arr[5] = { + { 0x6a, "1.0V" }, + { 0x6f, "1.05V" }, + { 0x75, "1.1V" }, + { 0x7a, "1.15V" }, + { 0x7f, "1.2V" } + }; + + DbgInfo(MODULE_VEN_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Enter %s\n", __func__); + + if (cfg->feature_item.pcie_phy_amplitude_adjust.pcietx_amplitude_chg_en) { + /*Step of CR write */ + + /* + * Step0: Set PCR 0xD0[31]=0 – disable ‘write protect’ for + * BHT defined config space registers; + */ + pci_andl(host, 0xd0, ~BIT31); + + /* + * Step1: Write CR address 16’h1002 to reg 0x78 [15:0]; + * + * Step2: Write updated CR data to reg 0x78[31:16], + * default value is 7b’1101010; + */ + regval = pci_readl(host, 0x78); + regval &= 0x0; + regval |= (0x1002 | (regval_1 << 16)); + pci_writel(host, 0x78, regval); + + /* + * Step3: Write 1’b1 to reg 0x78[30], + * to enable override CR data that write to CR address 16’h1002; + */ + pci_orl(host, 0x78, BIT30); + + /* Step4: Config reg 0x7C[0] to 1’b1 (Config CR direction to “write”); */ + pci_orl(host, 0x7C, BIT0); + + /* + * Step5: Config reg 0x7C[16] to 1’b1 to Initiate the CR access, + * and the CR write will be operated automatically; + */ + pci_orl(host, 0x7C, BIT16); + + /*Step of CR read */ + + /* Step1: Write CR address 16’h1009 to reg 0x78[15:0]; */ + regval = pci_readl(host, 0x78); + regval &= 0xffff0000; + regval |= 0x1009; + pci_writel(host, 0x78, regval); + + /* Step2: Config reg 0x7C[0] to 1’b0(Config CR direction to “read”); */ + pci_andl(host, 0x7C, ~BIT0); + + /* + * Step3: Config reg 0x7C[16] to 1’b1 to Initiate the CR access, + * and the CR read will be operated automatically; + */ + pci_orl(host, 0x7C, BIT16); + + /* + * Step4: Read reg 0x78[31:16] to get the CR read data, + * to confirm pcs_tx_swing_full[6:0] value is updated successfully; + */ + regval_2 = ((pci_readl(host, 0x78) & (0x00FF0000)) >> 16); + + /* + * Step5: Set PCR 0xD0[31]=1 – enable ‘write protect’ for + * BHT defined config space registers. + */ + pci_orl(host, 0xD0, BIT31); + + /* + * Debug driver should compare the CR write and CR read data, + * to confirm the CR write data is correct, + * then driver print “PCIe PHY TX Amplitude is xxV.”. + */ + + if (regval_1 == regval_2) { + for (i = 0; i < 5; i++) { + if (regval_1 == + amplitude_configuration_arr + [i].pcietx_amplitude) + DbgInfo(MODULE_VEN_HOST, + FEATURE_ERROR_RECOVER, + NOT_TO_RAM, + "PCIe PHY TX Amplitude is %s\n", + amplitude_configuration_arr + [i].amplitude); + } + } else { + DbgErr + ("Warning: write CR data is 0x%x, read CR data is 0x%x!\n", + regval_1, regval_2); + } + } + + DbgInfo(MODULE_VEN_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +void hostven_set_output_tuning_phase(sd_host_t *host, u32 value, bool off) +{ + bht_dev_ext_t *pdx = host->pdx; + + DbgInfo(MODULE_VEN_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Enter %s, Value:%x, Off:%x\n", __func__, value, off); + if (off == TRUE) { + /* select opclk */ + pci_andl(host, 0x354, 0xFFFEFFFF); + } else { + switch (host->chip_type) { + /* dll clock is selected according to UHS work mode */ + case CHIP_GG8: + case CHIP_ALBATROSS: + if (pdx->card.info.sw_cur_setting.sd_access_mode == + SD_FNC_AM_DDR200) { + pci_andl(host, 0x354, 0xFFFFFF0F); + pci_orl(host, 0x354, (value << 4)); + } else if (pdx->card.info.sw_cur_setting.sd_access_mode == + SD_FNC_AM_DDR50) { + /* Not support this mode at Bayhub Driver */ + DbgErr("DDR50 mode isn't supported !!!"); + } else if (pdx->card.info.sw_cur_setting.sd_access_mode == + SD_FNC_AM_SDR104) { + pci_andl(host, 0x354, 0xFF0FFFFF); + pci_orl(host, 0x354, (value << 20)); + } else if (pdx->card.info.sw_cur_setting.sd_access_mode == + SD_FNC_AM_SDR50) { + pci_andl(host, 0x354, 0xFFFFFFF0); + pci_orl(host, 0x354, value); + } + break; + default: + /* select dll clock */ + pci_andl(host, 0x354, 0xFF0EFFFF); + pci_orl(host, 0x354, ((value << 20) | BIT16)); + break; + } + } + DbgInfo(MODULE_VEN_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +bool hostven_fix_output_tuning(sd_host_t *host, byte sd_access_mode) +{ + bht_dev_ext_t *pdx = host->pdx; + cfg_output_tuning_item_t *cfg = + &pdx->cfg->feature_item.output_tuning_item; + + DbgInfo(MODULE_VEN_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Enter %s, Access mode:%x\n", __func__, sd_access_mode); + + host->output_tuning.auto_flag = FALSE; + if (cfg->enable_dll == 0) + goto exit; + + if (host->output_tuning.auto_phase_flag == TRUE) { + host_set_output_tuning_phase(host, + host->output_tuning.auto_phase); + goto exit; + } + + /* + * check whether need do output tuning or not: + * featre.output_tuning enable or not + * only SD_CARD + */ + + if (cfg->enable_emmc_hs400 == 1) { + host->output_tuning.auto_flag = TRUE; + host_set_output_tuning_phase(host, cfg->fixed_value_emmc_hs400); + } + + switch (sd_access_mode) { + case SD_FNC_AM_DDR200: + if (cfg->enable_ddr200 == 0) { + host->output_tuning.auto_flag = TRUE; + host_set_output_tuning_phase(host, + cfg->fixed_value_ddr200); + } + break; + case SD_FNC_AM_SDR104: + if (cfg->enable_sdr104 == 0) { + host->output_tuning.auto_flag = TRUE; + host_set_output_tuning_phase(host, + cfg->fixed_value_sdr104); + } + break; + case SD_FNC_AM_SDR50: + if (cfg->enable_sdr50 == 0) { + host->output_tuning.auto_flag = TRUE; + host_set_output_tuning_phase(host, + cfg->fixed_value_sdr50); + } + break; + case SD_FNC_AM_DDR50: + DbgErr("DDR50 isn't supported\n"); + break; + default: + break; + } + +exit: + DbgInfo(MODULE_VEN_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, "Exit %s\n", + __func__); + return host->output_tuning.auto_flag; +} + +u8 hostven_tuning_type_selection(sd_host_t *host, byte sd_access_mode) +{ + /* this function only SD_CARD */ + bht_dev_ext_t *pdx = host->pdx; + cfg_output_tuning_item_t *cfg = + &pdx->cfg->feature_item.output_tuning_item; + u8 tuning_type = 0; + u8 output_clock_source = 0; + u8 input_clock_source = 0; + + DbgInfo(MODULE_VEN_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Enter %s, Access mode:%x\n", __func__, sd_access_mode); + + /* select output clock source by mode */ + switch (sd_access_mode) { + case SD_FNC_AM_DDR200: + /* DLL clock is used for output */ + if (cfg->enable_ddr200 == 1) + output_clock_source = 1; + break; + case SD_FNC_AM_SDR104: + /* DLL clock is used for output */ + if (cfg->enable_sdr104 == 1) + output_clock_source = 1; + break; + case SD_FNC_AM_SDR50: + /* DLL clock is used for output */ + if (cfg->enable_sdr50 == 1) + output_clock_source = 1; + break; + case SD_FNC_AM_DDR50: + /* if (cfg->enable_ddr50 == 1) // DLL clock is used for output */ + /* output_clock_source = 1; */ + DbgErr("DDR50 isn't supported\n"); + break; + default: + break; + } + +/* input_clock: */ + + input_clock_source = 1; + + /* opclk used for input clock, Only SDR50 use it */ + if (input_clock_source == 0) { + /* no need input tuning, and use fix output phase */ + tuning_type = 0; + } else { + if (output_clock_source == 0) + /* use fix output phase and do input tuning */ + tuning_type = 1; + else + /* do input tuning and auto output tuning */ + tuning_type = 2; + } + + DbgInfo(MODULE_VEN_HOST, FEATURE_ERROR_RECOVER, NOT_TO_RAM, + "Exit %s with tuning type %d\n", __func__, tuning_type); + return tuning_type; +} + +/* init host feature */ +void host_vendor_feature_init(sd_host_t *host) +{ + bht_dev_ext_t *pdx = host->pdx; + + DbgInfo(MODULE_VEN_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + os_memset(&host->feature, 0, sizeof(host->feature)); + + hostven_bios_cfg(host); + hostven_load_pcr_cfg(host); + hostven_ltr_issue(host); + hostven_dma_engine_issue(host); + hostven_ms_sd30_dis(host); + + hostven_dll_watchdog_timer(host); + hostven_socket_pow_en(host); + hostven_output_tuning_init(host); + hostven_drive_strength_cfg(host); + + switch (host->chip_type) { + case CHIP_SEABIRD: + pci_orl(host, 0xd4, BIT6); + /* can't use timer for dump mode */ + if (host->dump_mode == FALSE) + host->feature.hw_led_fix = 1; + break; + case CHIP_SEAEAGLE: + /* can't use timer for dump mode */ + if (host->dump_mode == FALSE) + host->feature.hw_led_fix = 1; + /* Failsafe disable */ + pci_andl(host, 0x3E0, ~BIT6); + /* add for fail safe delay */ + os_mdelay(3 + + pdx->cfg->driver_item.delay_for_failsafe_s3resume); + break; + case CHIP_SEAEAGLE2: + case CHIP_GG8: + case CHIP_ALBATROSS: + host->feature.hw_pll_enable = 1; + host->feature.hw_resp_chk = 1; + host->feature.hw_autocmd = 1; + host->feature.hw_41_supp = 1; + host->camera_mode_card_state = CARD_DESERTED; + pci_andl(host, 0x3E0, ~BIT6); + /* can't use timer for dump mode */ + if (host->dump_mode == FALSE) + host->feature.hw_led_fix = 1; + + hostven_cmd_low_cfg(host); + hostven_switch_flow_cfg(host); + hostven_pinshare_cfg(host); + pcie_weakup(host->pdx, 0, TRUE); + break; + case CHIP_FUJIN2: + /* Default Enable fujin2 led */ + pci_andl(host, 0xdc, ~BIT13); + pci_orl(host, 0xd4, BIT6); + break; + default: + break; + } + + /* fail safe issue fix */ + + DbgInfo(MODULE_VEN_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +void host_force_pll_enable(sd_host_t *host, bool force) +{ + if (force) + sdhci_or32(host, 0x1cc, BIT18); + else + sdhci_and32(host, 0x1cc, ~BIT18); +} + +bool hostven_chk_card_present(sd_host_t *host) +{ + + u32 reg_val; + + if (shift_bit_func_enable(host)) { + reg_val = ven_readl(host, 0x510); + if (!(reg_val & 0x00000040)) { + DbgInfo(MODULE_VEN_HOST, FEATURE_DRIVER_INIT, + NOT_TO_RAM, "gpio check card present\n"); + return TRUE; + } else { + DbgInfo(MODULE_VEN_HOST, FEATURE_DRIVER_INIT, + NOT_TO_RAM, "gpio check card not present\n"); + return FALSE; + } + } else { + reg_val = sdhci_readl(host, SDHCI_PRESENT_STATE); + if ((reg_val & SDHCI_CARD_PRESENT_QUIRK) + && (reg_val != REGL_INVALID_VAL)) + return TRUE; + else + return FALSE; + } + +#if (0) + bool result; + u32 reg_val = 0; + int debounceTimeMax = 50; + +#if GLOBAL_ENABLE_BOOT + return TRUE; +#else + + reg_val = sdhci_readl(host, SDHCI_PRESENT_STATE); + if (reg_val == REGL_INVALID_VAL) + return FALSE; + + if (host->chip_type == CHIP_SEABIRD || host->chip_type == CHIP_SEAEAGLE) { + /* check if PLL is locked or not, if not, setup pll and enable pll first */ + if (!(sdhci_readl(host, 0x1cc) & BIT14)) { + /* host_init_emmc_400k_clock(host); */ + host_internal_clk_setup(host, TRUE); + } + } + /* wait for CD# debounce finished */ + while (1) { + reg_val = sdhci_readl(host, SDHCI_PRESENT_STATE); + if (((reg_val >> 18) & 0x01) == ((reg_val >> 16) & 0x01)) + break; + os_mdelay(1); + --debounceTimeMax; + if (debounceTimeMax <= 0 && (debounceTimeMax % 1000) == 0) + DbgErr("%s timeout\n", __func__); + } + + result = ((reg_val & SDHCI_CARD_PRESENT) == 0) ? FALSE : TRUE; + /* DbgErr("Card present status is %d\n", result); */ +#endif + return result; +#endif +} + +/* + * + * Function Name: check_chip_type + * + * Abstract: + * + * Acquire the chip type according to the vendor ID and the device ID. + * + * Input: + * + * pdev_ext [in]: Points to the device extension. + * ConfigInfo [in]: configuration information for a host bus adapter (HBA). + * + * Output: + * + * None + * + * Return value: + * + * If the driver found the chip type, + * it will return TRUE. Or it will return FALSE. + * + * Notes: + * + */ +bool hostven_chip_type_check(sd_host_t *host) +{ + + bool b_find = FALSE; + + DbgInfo(MODULE_VEN_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + host->sub_version = (u16) ((pci_readl(host, 0xdc) >> 24) & 0xff); + + if ((host->vendor_id == 0x1217) && (host->device_id == 0x8420)) { + + host->chip_type = CHIP_SDS0; + b_find = TRUE; + goto AssignFlag; + } + if ((host->vendor_id == 0x1217) && (host->device_id == 0x8421)) { + host->chip_type = CHIP_SDS1; + b_find = TRUE; + goto AssignFlag; + } + + if ((host->vendor_id == 0x1217) && (host->device_id == 0x8520)) { + if (0x12 == host->sub_version || 0x11 == host->sub_version) { + host->chip_type = CHIP_SEAEAGLE; + b_find = TRUE; + goto AssignFlag; + } else { + host->chip_type = CHIP_FUJIN2; + b_find = TRUE; + goto AssignFlag; + } + } + + if ((host->vendor_id == 0x1217) && (host->device_id == 0x8621)) { + host->chip_type = CHIP_SEABIRD; + b_find = TRUE; + goto AssignFlag; + } + + if ((host->vendor_id == 0x1217) && (host->device_id == 0x8620)) { + if (host->sub_version == 0x1) { + host->chip_type = CHIP_FUJIN2; + b_find = TRUE; + goto AssignFlag; + } else { + host->chip_type = CHIP_SEABIRD; + b_find = TRUE; + goto AssignFlag; + } + } + + if (((host->vendor_id == 0x1217) && (host->device_id == 0x8720)) + || ((host->vendor_id == 0x1217) && (host->device_id == 0x8721)) + || ((host->vendor_id == 0x1217) && (host->device_id == 0x8722)) + || ((host->vendor_id == 0x1217) && (host->device_id == 0x8723))) { + host->chip_type = CHIP_SEAEAGLE2; + b_find = TRUE; + goto AssignFlag; + + } + + if (((host->vendor_id == 0x1217) && (host->device_id == 0x9860)) + || ((host->vendor_id == 0x1217) && (host->device_id == 0x9861)) + || ((host->vendor_id == 0x1217) && (host->device_id == 0x9862)) + || ((host->vendor_id == 0x1217) && (host->device_id == 0x9863))) { + host->chip_type = CHIP_GG8; + b_find = TRUE; + goto AssignFlag; + + } + + if (((host->vendor_id == 0x1217) && (host->device_id == 0x9960)) + || ((host->vendor_id == 0x1217) && (host->device_id == 0x9961)) + || ((host->vendor_id == 0x1217) && (host->device_id == 0x9962)) + || ((host->vendor_id == 0x1217) && (host->device_id == 0x9963))) { + host->chip_type = CHIP_ALBATROSS; + b_find = TRUE; + goto AssignFlag; + + } + + DbgErr("venid=0x%04X devid=0x%04X\n", host->vendor_id, host->device_id); + +AssignFlag: + + if (b_find == TRUE) { + + } else { + DbgErr(" Chip not found!ven_id = 0x%08x, device_id = 0x%08x\n", + host->vendor_id, host->device_id); + DbgInfo(MODULE_VEN_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Exit %s, Find:%x\n", __func__, FALSE); + return FALSE; + } + + DbgInfo(MODULE_VEN_HOST, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Exit %s, Find:%x\n", __func__, TRUE); + return TRUE; + +} + +/* + * + * Function Name: hostven_rtd3_check + * + * Abstract: + * + * Check the runtime D3 enable or not. + * + * Input: + * + * host [in]: Points to the host structure. + * + * Output: + * + * None + * + * Return value: + * + * If RTD3 is enabled the value is TRUE. Or it is FALSE. + * + * Notes: + * + */ +bool hostven_rtd3_check(sd_host_t *host) +{ + bool rtd3_en = FALSE; + cfg_psd_mode_t rtd3_setting = host->cfg->feature_item.psd_mode; + + DbgInfo(MODULE_VEN_HOST, FEATURE_PM_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + if ((host->chip_type == CHIP_FUJIN2) + || (host->chip_type == CHIP_SEABIRD) + || (host->chip_type == CHIP_SEAEAGLE) + || (host->chip_type == CHIP_SEAEAGLE2) + || (host->chip_type == CHIP_GG8) + || (host->chip_type == CHIP_ALBATROSS)) { + + if (rtd3_setting.enable_rtd3) { + rtd3_en = TRUE; + pci_andl(host, 0x3e0, ~(1 << 29)); + + if (host->chip_type == CHIP_FUJIN2) { + + if (!(pci_readl(host, 0x3e0) & (1 << 29))) { + /* external enable polarity control pin */ + pci_andl(host, 0xd8, ~(1 << 9)); + /* 1.2v main LDO power control source selection */ + pci_andl(host, 0x3e0, ~(1 << 30)); + /* AOSC off support */ + pci_orl(host, 0x3f0, 1 << 31); + } + } + } else { + rtd3_en = FALSE; + pci_orl(host, 0x3e0, 1 << 29); + if (host->chip_type == CHIP_FUJIN2) { + pci_andl(host, 0xd8, ~(1 << 9)); + /* 1.2v main LDO power control source selection */ + pci_orl(host, 0x3e0, (1 << 30)); + /* AOSC off support */ + pci_andl(host, 0x3f0, ~(1 << 31)); + + } + } + } + + DbgInfo(MODULE_VEN_HOST, FEATURE_PM_TRACE, NOT_TO_RAM, + "Exit %s, RTD3 enable:%x\n", __func__, rtd3_en); + return rtd3_en; + +} + +/* + * + * Function Name: hostven_d3_mode_sel + * + * Abstract: + * + * Select the D3 work mode. D3 work mode: start-up mode(2'b11), + * RTD3 cold with external FET (2'b00), + * RTD3 cold with internal FET (2'b01), D3 silence (2'b10). + * + * + * + * Input: + * + * host [in]: Points to the host structure. + * d3_submode [out]: the d3 silence sub mode. + * + * Output: + * + * None + * + * Return value: + * + * The D3 work mode value. + * + * Notes: + * + */ +u32 hostven_d3_mode_sel(sd_host_t *host, u32 *d3_submode) +{ + u32 d3_mode = 0; + cfg_psd_mode_t rtd3_setting = host->cfg->feature_item.psd_mode; + + DbgInfo(MODULE_VEN_HOST, FEATURE_PM_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + if ((host->chip_type == CHIP_SEAEAGLE2) || (host->chip_type == CHIP_GG8) + || (host->chip_type == CHIP_ALBATROSS)) { + if (rtd3_setting.rtd3_ctrl_mode) { + /* d3_mode = rtd3_setting.d3_work_mode_sel; */ + pci_andl(host, 0x3f0, ~(3 << 28)); + pci_orl(host, 0x3f0, (d3_mode & 3) << 28); + pci_andl(host, 0x3f0, ~(1 << 26)); + pci_orl(host, 0x3f0, + (rtd3_setting.d3silence_submode_sel << 26)); + *d3_submode = rtd3_setting.d3silence_submode_sel; + + } else { + d3_mode = (pci_readl(host, 0x3f0) >> 28) & 3; + *d3_submode = (pci_readl(host, 0x3f0) >> 26) & 1; + } + } + + DbgInfo(MODULE_VEN_HOST, FEATURE_PM_TRACE, NOT_TO_RAM, + "Exit %s, D3 mode:%x\n", __func__, d3_mode); + return d3_mode; +} + +/* + * + * Function Name: hostven_pm_mode_cfg + * + * Abstract: + * + * host vendor pm mode configure feature + * + * + * + * Input: + * + * host [in]: Points to the host structure. + * pm_state_t *pm [in/out]: update pm state config. + * + * Output: + * + * None + * + * Return value: + * + * null + * + * Notes: + * + */ + +void hostven_pm_mode_cfg(sd_host_t *host, pm_state_t *pm) +{ + + switch (host->chip_type) { + + case CHIP_SEAEAGLE2: + case CHIP_GG8: + case CHIP_ALBATROSS: + { +#define D3_SILENCE_WORK_MODE 2 +#define D3_SILENCE_SUB_MODE2_EN 1 + u32 d3_silc_sub = 0; + + pm->d3_silc_en = + hostven_d3_mode_sel(host, &d3_silc_sub) == D3_SILENCE_WORK_MODE + ? TRUE : FALSE; + pm->d3_silc_submode2_en = + D3_SILENCE_SUB_MODE2_EN == + d3_silc_sub ? TRUE : FALSE; + if (pm->d3_silc_en && pm->d3_silc_submode2_en) + pm->rtd3_en = FALSE; + } + break; + case CHIP_SEABIRD: + { + u32 sub_chip_id; + + sub_chip_id = + ((host->device_id & 0x1) << 2) + + ((pci_readl(host, 0x3e4) & 0xC0000000) >> 30); + + switch (sub_chip_id) { + case 0x5: + host->ven_cap.pm.rtd3_hot = 1; + host->ven_cap.pm.rtd3_cold = 0; + host->ven_cap.pm.d3_silence = 1; + host->ven_cap.pm.l1_substate = 1; + host->ven_cap.pm.ltr = 1; + break; + case 0x3: + host->ven_cap.pm.rtd3_hot = 0; + host->ven_cap.pm.rtd3_cold = 0; + host->ven_cap.pm.d3_silence = 1; + host->ven_cap.pm.l1_substate = 0; + host->ven_cap.pm.ltr = 0; + break; + case 0x2: + host->ven_cap.pm.rtd3_hot = 0; + host->ven_cap.pm.rtd3_cold = 0; + host->ven_cap.pm.d3_silence = 0; + host->ven_cap.pm.l1_substate = 0; + host->ven_cap.pm.ltr = 0; + break; + case 0x000: + case 0x001: + case 0x4: + case 0x6: + case 0x7: + default: + host->ven_cap.pm.rtd3_hot = 1; + host->ven_cap.pm.rtd3_cold = 1; + host->ven_cap.pm.d3_silence = 1; + host->ven_cap.pm.l1_substate = 1; + host->ven_cap.pm.ltr = 1; + } + DbgInfo(MODULE_VEN_HOST, FEATURE_PM_TRACE, NOT_TO_RAM, + "subchipid is %x pm(%xh)\n", sub_chip_id, + host->ven_cap.pm); + } + { + + } + break; + default: + break; + } + +} + +/* + * + * Function Name: hostven_main_power_ctrl + * + * Abstract: + * + * Set the main power control for D3 silence sub mode 2. + * + * D3Silence submode2 main power control source: + * 1'b0: Main power will be kept on. + * 1'b1: Main power control derives from D3Silence logic according to + * entry and exit conditions. + * Driver configures this bit when SeaEagle2 is idle and can be auto-power-off. + * Default value is 1'b0. + * + * + * + * Input: + * + * host [in]: Points to the host structure. + * is_keep_on [in]: The main power is kept on or not. + * + * Output: + * + * None + * + * Return value: + * + * The D3 work mode value. + * + * Notes: + * + */ +void hostven_main_power_ctrl(sd_host_t *host, bool is_keep_on) +{ + bht_dev_ext_t *pdev_ext = (bht_dev_ext_t *) host->pdx; + u16 regval; + + DbgInfo(MODULE_VEN_HOST, FEATURE_PM_TRACE, NOT_TO_RAM, + "Enter %s is_keep_on:%xh\n", __func__, is_keep_on); + + switch (host->chip_type) { + case CHIP_SEAEAGLE2: + case CHIP_GG8: + case CHIP_ALBATROSS: + { + if (pdev_ext->pm_state.d3_silc_en + && pdev_ext->pm_state.d3_silc_submode2_en) { + if (is_keep_on) + pci_andl(host, 0x3f0, ~(1 << 25)); + else { + /* open the soft L0 request */ + pci_orl(host, 0x3e4, 1 << 23); + /* Power off the chip */ + pci_andl(host, 0x3f0, ~(1 << 25)); + pci_orl(host, 0x3f0, 1 << 25); + + } + } + + /* clear VDD2/3 GPIO power control inverter setting */ + regval = ven_readw(host, SDBAR1_GPIO_FUNC_GPIOCTRL_510); + regval &= ~(1 << 7 | 1 << 15); + ven_writew(host, SDBAR1_GPIO_FUNC_GPIOCTRL_510, regval); + } + break; + case CHIP_SEABIRD: + { + if (pdev_ext->pm_state.d3_silc_en) { + if (is_keep_on) { + pci_orl(host, 0x3e4, 0x40000); + pci_orl(host, 0x3e0, 0x80000000); + pci_orl(host, 0x3e0, 0x40000000); + pci_orl(host, 0xD8, 0x240); + + } else { + /* power off chip */ + pci_orl(host, 0x3e4, 0x40000); + pci_orl(host, 0x3e0, 0x80000000); + pci_andl(host, 0xD8, (~0x240)); + pci_andl(host, 0x3e0, (~0x40000000)); + } + } + } + break; + default: + break; + } + DbgInfo(MODULE_VEN_HOST, FEATURE_PM_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +/* + * Function Name: hostven_d3_mode_sel_se2 + * + * Abstract: + * + * Select the D3 work mode. D3 work mode: start-up mode(2'b11), + * RTD3 cold with external FET (2'b00), + * RTD3 cold with internal FET (2'b01), D3 silence (2'b10). + * + * Input: + * + * host [in]: Points to the host structure. + * + * Output: + * + * None + * + * Return value: + * + * The D3 work mode value. + * + * Notes: + * + */ +bool hostven_hs400_host_chk(sd_host_t *host) +{ + u32 hs400_sel_mode = 0; + bool ted_ip_en = FALSE; + + DbgInfo(MODULE_VEN_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + hs400_sel_mode = pci_readl(host, 0x320); + if (hs400_sel_mode & (1 << 5)) + ted_ip_en = TRUE; + + DbgInfo(MODULE_VEN_HOST, FEATURE_CARD_INIT, NOT_TO_RAM, + "Exit %s,TED IP enable:%x\n", __func__, ted_ip_en); + return ted_ip_en; +} + +void hostven_hw_timer_start(sd_host_t *host, u32 time_ms) +{ + DbgInfo(MODULE_VEN_HOST, FEATURE_TIMER_TRACE, NOT_TO_RAM, + "Timer: %s time_ms=%d\n", __func__, time_ms); + time_ms *= 2000; + pci_orl(host, 0x3f0, (3 << 30)); + ven_or32(host, 0x518, BIT7); + + pci_andl(host, 0x414, 0xfc000000); + pci_orl(host, 0x414, time_ms); + pci_orl(host, 0x414, BIT26); + pci_orl(host, 0x520, BIT7); +} + +void hostven_hw_timer_stop(sd_host_t *host) +{ + DbgInfo(MODULE_VEN_HOST, FEATURE_TIMER_TRACE, NOT_TO_RAM, "Timer: %s\n", + __func__); + pci_andl(host, 0x414, ~(BIT26)); + ven_and32(host, 0x518, ~(BIT7)); + pci_andl(host, 0x3f0, ~(3 << 30)); + pci_andl(host, 0x520, ~(BIT7)); +} + +void hostven_set_pml0_requrest(sd_host_t *host, bool enable) +{ + DbgInfo(MODULE_VEN_HOST, FEATURE_TIMER_TRACE, NOT_TO_RAM, "%s %xh\n", + __func__, enable); + if (host->chip_type == CHIP_SEAEAGLE) { + if (enable == TRUE) + pci_andl(host, 0x3e0, ~(BIT19)); + else + pci_orl(host, 0x3e0, (BIT19)); + } +} diff --git a/drivers/scsi/bht/host/hostven.h b/drivers/scsi/bht/host/hostven.h new file mode 100644 index 000000000000..a0a72c27228c --- /dev/null +++ b/drivers/scsi/bht/host/hostven.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: hostven.h + * + * Abstract: Delcare for the host vendor APIs + * + * Version: 1.00 + * + * Author: Yuxiang + * + * Environment: OS Independent + * + * History: + * + * 9/16/2014 Creation Yuxiang + */ + +void hostven_transfer_init(sd_host_t *host, bool enable); +void hostven_update_dmdn(sd_host_t *host, u32 dmdn); + +bool hostven_hs400_host_chk(sd_host_t *host); + +void hostven_drive_strength_cfg(sd_host_t *host); + +bool host_chk_ocb_occur(sd_host_t *host); +void hostven_ocb_cfg(sd_host_t *host); +void hostven_pinshare_cfg(sd_host_t *host); +void hostven_cmd_low_cfg(sd_host_t *host); +void hostven_switch_flow_cfg(sd_host_t *host); diff --git a/drivers/scsi/bht/host/irqhandler.c b/drivers/scsi/bht/host/irqhandler.c new file mode 100644 index 000000000000..6a4aa0458b39 --- /dev/null +++ b/drivers/scsi/bht/host/irqhandler.c @@ -0,0 +1,742 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: irqhandler.c + * + * Abstract: handle IRQ + * + * Version: 1.00 + * + * Author: Chuanjin + * + * Environment: OS Independent + * + * History: + * + * 9/2/2014 Creation Chuanjin + */ + +#include "../include/basic.h" +#include "../include/hostapi.h" +#include "../include/funcapi.h" +#include "../include/cardapi.h" +#include "hostreg.h" +#include "../include/debug.h" +#include "../include/hostvenapi.h" +#include "../include/cmdhandler.h" + +void host_error_int_recovery_stage1(sd_host_t *host, u16 error_int_state, + bool check); + +#define SDHCI_MAX_INT_RETRY 16 + +/* + * static void sw_int_issue(sd_host_t *host) + * { + * u32 reg = sdhci_readl(host, SDHCI_DRIVER_CTRL_REG); + * + * reg = reg | (SDHCI_VENDOR_SW_INT_BIT); + * sdhci_writel(host, SDHCI_DRIVER_CTRL_REG, reg); + * } + */ + +bool thread_exec_high_prio_job(bht_dev_ext_t *pdx, cb_soft_intr_t func, + void *data) +{ + bool result = TRUE; +#ifndef CFG_SCSIPORT_DRIVER + if (func) + func(data); + return result; +#else + os_init_completion(pdx, &pdx->soft_irq.completion); + pdx->soft_irq.data = data; + pdx->soft_irq.cb_func = func; + pdx->soft_irq.enable = 1; + sw_int_issue(&pdx->host); + result = + os_wait_for_completion(pdx, &pdx->soft_irq.completion, + SOFT_INTR_TIMEOUT); + pdx->soft_irq.cb_func = NULL; + pdx->soft_irq.data = NULL; + pdx->soft_irq.enable = 0; + if (result == FALSE) + DbgErr("Software Intr is not ocurr\n"); + return result; +#endif + +} + +void irq_disable_sdcmd_int(sd_host_t *host) +{ + host_int_sig_update(host, SDHCI_INT_INSERT_REMOVE_CARD_BITS); + if (host->uhs2_flag) + host_uhs2_err_sig_update(host, 0); +} + +void send_req_complete_evt(sd_host_t *host, void *pdx) +{ + host_cmd_req_t *req = host->cmd_req; + sd_command_t *sd_cmd = NULL; + + DbgInfo(MODULE_SD_HOST, FEATURE_INTR_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + + if (req != NULL) + sd_cmd = req->private; + if (sd_cmd == NULL) { + DbgErr("sd_cmd is NULL at send req complete evt!\n"); + goto exit; + } + + if (sd_cmd->err.error_code == 0) { + if (req->cb_req_complete) + req->cb_req_complete(pdx, &sd_cmd->err); + } + irq_disable_sdcmd_int(host); + os_finish_completion(pdx, &req->done); +exit: + DbgInfo(MODULE_SD_HOST, FEATURE_INTR_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); + +} + +void insert_card_handle(bht_dev_ext_t *pdx) +{ + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT | FEATURE_INTR_TRACE, TO_RAM, + "Enter %s\n", __func__); + + pdx->card.card_present = TRUE; + pdx->card.card_chg = TRUE; + pdx->scsi.scsi_eject = FALSE; + + /* Keep chip power on. */ + hostven_main_power_ctrl(&pdx->host, TRUE); + +#if CFG_OS_LINUX + os_set_event(&pdx->os, EVENT_CARD_CHG); +#endif + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT | FEATURE_INTR_TRACE, TO_RAM, + "Exit %s\n", __func__); +} + +void remove_card_handle(bht_dev_ext_t *pdx) +{ + + u32 regval; + sd_host_t *host = &pdx->host; + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT | FEATURE_INTR_TRACE, TO_RAM, + "Enter %s\n", __func__); + + if (shift_bit_func_enable(&pdx->host)) + set_pattern_value(&pdx->host, 0x00); + if (host->cfg->card_item.sd7_sdmode_switch_control.sd70_trail_run) { + regval = pci_readl(host, 0x444); + regval |= (1 << 11); + pci_writel(host, 0x444, regval); + } else { + regval = pci_readl(host, 0x444); + regval &= (~(1 << 11)); + pci_writel(host, 0x444, regval); + } + + pdx->card.card_present = FALSE; + pdx->scsi.scsi_eject = FALSE; + card_stuct_uinit(&pdx->card); + +#if CFG_OS_LINUX + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT | FEATURE_INTR_TRACE, TO_RAM, + "set event\n"); + + os_set_event(&pdx->os, EVENT_CARD_CHG); +#else + os_set_event(pdx, &pdx->os, EVENT_TASK_OCCUR, TASK_CARD_CHG); +#endif + /* add for driver controlled UHS2 VDD2 power off when card remove */ + host_uhs2_clear(&pdx->host, TRUE); + hostven_main_power_ctrl(&pdx->host, FALSE); + + DbgInfo(MODULE_SD_HOST, FEATURE_CARD_INIT | FEATURE_INTR_TRACE, TO_RAM, + "Exit %s\n", __func__); +} + +void sw_int_clear(sd_host_t *host) +{ + u32 reg = sdhci_readl(host, SDHCI_DRIVER_CTRL_REG); + + reg = reg & (~SDHCI_VENDOR_SW_INT_BIT); + sdhci_writel(host, SDHCI_DRIVER_CTRL_REG, reg); +} + +bool sw_int_occur(sd_host_t *host) +{ + u32 reg = sdhci_readl(host, SDHCI_DRIVER_CTRL_REG); + + if (reg & SDHCI_VENDOR_SW_INT_BIT) + return TRUE; + else + return FALSE; +} + +void host_chk_ocb_occur(sd_host_t *host) +{ + u32 ocb_status = 0; + + bht_dev_ext_t *pdx = (bht_dev_ext_t *) (host->pdx); + + if (host->chip_type == CHIP_ALBATROSS) + return; + + ocb_status = sdhci_readl(host, SDHCI_DRIVER_CTRL_REG); + DbgInfo(MODULE_SD_HOST, FEATURE_FUNC_TRACE, NOT_TO_RAM, + "Enter %s, ocb status =0x%08x\n", __func__, ocb_status); + if (ocb_status & SDHCI_OCB_FET_INT_ACTIVE) { + DbgErr("OCB FET is active!\n"); + + host_cmddat_line_reset(host); + host_int_dis_sig_all(host, FALSE); + host_poweroff(host, CARD_ERROR); + + pdx->card.card_present = FALSE; + card_stuct_uinit(&pdx->card); + + /* host 0x1c0 [22] */ + if ((ocb_status & SDHCI_OCB_FET_INT_DENOUNCE) != + SDHCI_OCB_FET_INT_DENOUNCE) { + ocb_status &= ~SDHCI_OCB_FET_INT_ACTIVE; + ocb_status |= SDHCI_OCB_FET_INT_DENOUNCE; + sdhci_writel(host, SDHCI_DRIVER_CTRL_REG, ocb_status); + } + + /* clear 0x1c0 [5] */ + if (host->cfg->host_item.test_ocb_ctrl.int_check_en == 0) { + ocb_status &= ~SDHCI_OCB_FET_INT_ACTIVE; + ocb_status &= ~SDHCI_OCB_INT_MASK; + sdhci_writel(host, SDHCI_DRIVER_CTRL_REG, ocb_status); + } else { + ocb_status &= ~SDHCI_OCB_FET_INT_ACTIVE; + ocb_status |= SDHCI_OCB_INT_MASK; + sdhci_writel(host, SDHCI_DRIVER_CTRL_REG, ocb_status); + } + + } + + DbgInfo(MODULE_SD_HOST, FEATURE_FUNC_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +/* + * model: + * 1. support async mode or sync with binding different evt(last one for ADMA3). + * 2. list all need complete SRB. + */ + +void sw_int_handle(bht_dev_ext_t *pdx) +{ + cb_soft_intr_t func = pdx->soft_irq.cb_func; + void *data = pdx->soft_irq.data; + + if (pdx->soft_irq.enable) { + if (func) + func(data); + } +} + +void cb_handle(u16 *wait_flag, u16 clr_bit, s32 cb_ret, bool *hascmd, + bht_dev_ext_t *pdx) +{ + sd_host_t *host = &pdx->host; + + DbgInfo(MODULE_SD_HOST, FEATURE_INTR_TRACE, NOT_TO_RAM, + "Enter %s wait_flag=%xh,clr_bit=%xh,cb_ret=%xh,hascmd=%x\n", + __func__, wait_flag, clr_bit, cb_ret, *hascmd); + switch (cb_ret) { + case INTR_CB_ERR: + /* callback must set error condition flag */ + *wait_flag = 0; + break; + case INTR_CB_OK: + (*wait_flag) &= clr_bit; + break; + case INTR_CB_NOEND: + default: + break; + } + if (*wait_flag == 0) { + send_req_complete_evt(host, pdx); + *hascmd = FALSE; + } + DbgInfo(MODULE_SD_HOST, FEATURE_INTR_TRACE, NOT_TO_RAM, + "Exit %s hascmd=%x\n", __func__, *hascmd); +} + +bool device_power_enter_non_D0(void *param) +{ + bht_dev_ext_t *pdx = (bht_dev_ext_t *) param; + + if (pdx->pm_state.rtd3_entered || + pdx->pm_state.s3s4_entered || + pdx->pm_state.s5_entered || pdx->pm_state.warm_boot_entered) + return TRUE; + else + return FALSE; +} + +bool sdhci_irq(void *param) +{ + bht_dev_ext_t *pdx = (bht_dev_ext_t *) param; + u32 int_status = 0; + bool ret = TRUE; + sd_host_t *host = &pdx->host; + sd_card_t *card = &pdx->card; + host_cmd_req_t *req = host->cmd_req; + cfg_item_t *cfg = host->cfg; + sd_command_t *sd_cmd = NULL; + bool hascmd = TRUE; + s32 cb_ret = 0; + u32 out_of_range = 0; + u16 max_loops = SDHCI_MAX_INT_RETRY; + u32 hw_timer_intr = 0; + u32 regval; + u16 expected_range = 0; + u32 time_cnt_100us = 0; + bool card_insert_flag = 0; + bool gpio_interrupt = 0; + + DbgInfo(MODULE_SD_HOST, FEATURE_INTR_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + + if (host->chip_type == CHIP_GG8) { + if (host->cfg->card_item.sd7_sdmode_switch_control.switch_method_ctrl == + HW_DETEC_HW_SWITCH) { + DbgInfo(MODULE_SD_HOST, FEATURE_INTR_TRACE, NOT_TO_RAM, + "Hardware\n"); + + regval = sdhci_readl(host, 0x1e0); + if (regval & (1 << 16)) { + os_atomic_set(&host->clkreqn_status, 1); + sdhci_or16(host, 0x1e2, 0x01); + return TRUE; + } else if (regval & (1 << 17)) { + os_atomic_set(&host->clkreqn_status, 2); + sdhci_or16(host, 0x1e2, 0x02); + return TRUE; + } + } + + regval = pci_readl(host, 0x51C); + if (regval & 0x2) { + regval |= 0x2; + pci_writel(host, 0x51C, regval); + + regval = sdhci_readl(host, 0x3E); + if (regval & (1 << 8)) { + regval &= ~(1 << 8); + sdhci_writel(host, 0x3E, regval); + } + card->sw_ctrl_swicth_to_express = TRUE; + + /* turn off VDD2/VDD1 */ + host_set_vddx_power(host, VDD2, POWER_OFF); + host_set_vddx_power(host, VDD1, POWER_OFF); + + card_stuct_uinit(&pdx->card); + +#if CFG_OS_LINUX + os_set_event(&pdx->os, EVENT_CARD_CHG); +#endif + + card->state = CARD_STATE_POWEROFF; + return TRUE; + } + + /* check PCIe reference clock detection timeout status */ + if (sdhci_readl(host, 0x1E0) & BIT15) { + DbgInfo(MODULE_VEN_HOST, FEATURE_DRIVER_INIT, + NOT_TO_RAM, + "PCIe reference clock detection timeout\n"); + + /* Clear interrupt */ + sdhci_or16(host, 0x1E0, BIT15); + + if ((cfg->feature_item.auto_detect_refclk_counter_range_ctl.enable + == 0) || + ((cfg->feature_item.auto_detect_refclk_counter_range_ctl.enable + == 1) && + (cfg->feature_item.auto_detect_refclk_counter_range_ctl.req_refclkcnt_minmax_source_sel + == 0))) { + /* set refclk_cnt_range_detect_soft_reset */ + pci_orw(host, 0x462, BIT14); + DbgInfo(MODULE_VEN_HOST, FEATURE_DRIVER_INIT, + NOT_TO_RAM, + "Start auto detect refclk counter range\n"); + + /* Polling refclk_cnt_range_detect_done */ + time_cnt_100us = 0; + while (0 == (pci_readl(host, 0x460) & BIT31)) { + if (time_cnt_100us >= + ADJUST_EXPEXTED_RANGE_TIMEOUT_COUNT) { + DbgInfo(MODULE_VEN_HOST, + FEATURE_DRIVER_INIT, + NOT_TO_RAM, + "Auto detect expected range timeout\n"); + return ret; + } + os_udelay(100); + time_cnt_100us++; + } + + expected_range = pci_readw(host, 0x460); + DbgInfo(MODULE_VEN_HOST, FEATURE_DRIVER_INIT, + NOT_TO_RAM, + "Software invoked Auto detect refclk counter range done, range min %#x , max %#x\n", + (expected_range >> 8), + (expected_range & 0xFF)); + } + } + } + + if (HW_TIMER_CFG == TRUE) { + hw_timer_intr = pci_readl(host, 0x51c) & BIT7; + if (hw_timer_intr & BIT7) + ven_writel(host, 0x51c, BIT7); + } + + int_status = sdhci_readl(host, SDHCI_INT_STATUS); + + if (shift_bit_func_enable(host)) { + if (ven_readl(host, 0x51c) & (1 << 2)) + gpio_interrupt = TRUE; + } + +int_again: + if (((!int_status) || (int_status == 0xffffffff)) + && (hw_timer_intr == 0) && (gpio_interrupt == FALSE)) { + if (int_status == 0xFFFFFFFF) + DbgErr("%s !!!int %x\n", __func__, int_status); + ret = FALSE; + goto out; + } + req = host->cmd_req; + + if (req == NULL) + sd_cmd = NULL; + else + sd_cmd = (sd_command_t *) req->private; + + if (req == NULL || sd_cmd == NULL) + hascmd = FALSE; + + DbgInfo(MODULE_SD_HOST, FEATURE_INTR_TRACE, NOT_TO_RAM, + "%s int_status=0x%08X\n", __func__, int_status); + /* error int handle */ + if (int_status & SDHCI_INT_ERROR) { + u16 error_status = (int_status >> 16); + + /* clear error interrupt */ + sdhci_writel(host, SDHCI_INT_STATUS, (int_status & 0xFFFF8000)); + + DbgErr("error intr=0x%04X hascmd=%d\n", error_status, hascmd); + switch (hascmd) { + case TRUE: + if ((sd_cmd->cmd_index == SD_CMD17 || + sd_cmd->cmd_index == SD_CMD18 || + sd_cmd->cmd_index == SD_CMD24 || + sd_cmd->cmd_index == SD_CMD25) && + (((error_status & SDHCI_INT_ACMD12ERR) + && (RESP_ERR_TYPE_OUT_OF_RANGE & + sdhci_readl(host, SDHCI_RESPONSE + 12))) + || (!(error_status & SDHCI_INT_ACMD12ERR) + && (RESP_ERR_TYPE_OUT_OF_RANGE & + sdhci_readl(host, SDHCI_RESPONSE)))) + ) { + PrintMsg + ("ignore out of range when use SD_CMD18 read %d sector, int_status=0x%08x\n", + sd_cmd->argument, int_status); + int_status |= + (SDHCI_INT_TRANSFER_COMP | + SDHCI_INT_NORMAL_BITS); + int_status &= ~SDHCI_INT_ERROR; + int_status &= + ~((SDHCI_INT_ADMA_ERROR | + SDHCI_INT_ACMD12ERR | + SDHCI_INT_RESP_ERROR) >> 16); + out_of_range = 1; + break; + } + fallthrough; + default: + if (host->uhs2_flag == TRUE) { + u32 uhs2_err_int_status = 0; + + uhs2_err_int_status = + sdhci_readl(host, SDHCI_UHS2_ERRINT_STS); + sdhci_writel(host, SDHCI_UHS2_ERRINT_STS, + uhs2_err_int_status); + DbgErr + ("error=0x%08x,UHS2 Error intr occur=0x%08X hascmd=%d\n", + int_status, uhs2_err_int_status, hascmd); + if (hascmd) { + if (uhs2_err_int_status & + req->int_flag_uhs2_err) { + sd_cmd->err.uhs2_err_reg = + uhs2_err_int_status & + req->int_flag_uhs2_err; + sd_cmd->err.error_code |= + ERR_CODE_INTR_ERR; + if ((uhs2_err_int_status & + SDHCI_UHS2_ERR_RESP) + && sd_cmd->hw_resp_chk + && req->cb_response) { + req->cb_response(card, + req); + } + cb_handle(&(req->int_flag_wait), + 0, INTR_CB_ERR, + &hascmd, pdx); + } + } + } else { + u16 err_status = (u16) (int_status >> 16); + + DbgErr("error intr=0x%04X hascmd=%d\n", + err_status, hascmd); + if (hascmd) { + if (err_status & req->int_flag_err) { + sd_cmd->err.legacy_err_reg = + err_status & + req->int_flag_err; + sd_cmd->err.error_code |= + ERR_CODE_INTR_ERR; + if ((err_status & + SDHCI_INT_RESP_ERROR) + && req->cb_response + && sd_cmd->hw_resp_chk) { + req->cb_response(card, + req); + } + cb_handle(&(req->int_flag_wait), + 0, INTR_CB_ERR, + &hascmd, pdx); + } + } + host_error_int_recovery_stage1(host, err_status, + FALSE); + } + break; + } + } + + /* If not in a SD command execute context, don't call sd cmd call back */ + if (hascmd == FALSE) + goto next; + + /* normal interrupt handle */ + if (int_status & SDHCI_INT_NORMAL_BITS) { + + if (int_status & req->int_flag_wait) { + u16 irq_wait = (u16) (int_status & req->int_flag_wait); + + if (irq_wait & SDHCI_INT_CMD_COMP) { + /* clear interrupt status */ + sdhci_writel(host, SDHCI_INT_STATUS, + SDHCI_INT_CMD_COMP); + if (req->cb_response) + cb_ret = req->cb_response(card, req); + else + cb_ret = INTR_CB_OK; + sd_cmd->cmd_done = 1; + cb_handle(&(req->int_flag_wait), + ~SDHCI_INT_CMD_COMP, cb_ret, &hascmd, + pdx); + } + if (irq_wait & SDHCI_INT_TRANSFER_COMP) { + /* clear interrupt status */ + sdhci_writel(host, SDHCI_INT_STATUS, + SDHCI_INT_TRANSFER_COMP); + if (req->cb_trans_complete) + cb_ret = req->cb_trans_complete(card, req); + else + cb_ret = INTR_CB_OK; + cb_handle(&(req->int_flag_wait), + ~SDHCI_INT_TRANSFER_COMP, cb_ret, + &hascmd, pdx); + + } + if (irq_wait & SDHCI_INT_BUFFER_READY_BITS) { + /* clear interrupt status */ + sdhci_writel(host, SDHCI_INT_STATUS, + SDHCI_INT_BUFFER_READY_BITS); + if (req->cb_buffer_ready) + cb_ret = req->cb_buffer_ready(card, req); + else + cb_ret = INTR_CB_OK; + cb_handle(&(req->int_flag_wait), + ~SDHCI_INT_BUFFER_READY_BITS, cb_ret, + &hascmd, pdx); + } + if (irq_wait & SDHCI_INT_DMA_END) { + /* clear interrupt status */ + sdhci_writel(host, SDHCI_INT_STATUS, + SDHCI_INT_DMA_END); + if (req->cb_boundary) + cb_ret = req->cb_boundary(card, req); + else + cb_ret = INTR_CB_OK; + + /* avoid wrongly clear DMA int wait flag for ddr200 workaround */ + if (sd_cmd != NULL + && sd_cmd->gg8_ddr200_workaround == 0) + cb_handle(&(req->int_flag_wait), + ~SDHCI_INT_DMA_END, cb_ret, + &hascmd, pdx); + else if (sd_cmd != NULL) + sd_cmd->gg8_ddr200_workaround = 0; + } + + } + + } + +next: + /* insert or remove card handle */ + if (device_power_enter_non_D0(param) == FALSE) { + + if (shift_bit_func_enable(host)) { + /* if (ven_readl(host, 0x51c) & (1 << 2)) */ + if (gpio_interrupt == TRUE) { + /* clear interrupt status */ + ven_or16(host, 0x51c, (1 << 2)); + + regval = ven_readl(host, 0x510); + + if (!(regval & (1 << 6))) + card_insert_flag = TRUE; + else if (regval & (1 << 6)) + card_insert_flag = FALSE; + } + + } else { + if (int_status & SDHCI_INT_CARD_INSERT) { + card_insert_flag = TRUE; + /* clear interrupt status */ + sdhci_writel(host, SDHCI_INT_STATUS, + SDHCI_INT_CARD_INSERT); + } else if (int_status & SDHCI_INT_CARD_REMOVE) { + card_insert_flag = FALSE; + /* clear interrupt status */ + sdhci_writel(host, SDHCI_INT_STATUS, + SDHCI_INT_CARD_REMOVE); + } + + } + + if ((gpio_interrupt && shift_bit_func_enable(host)) + || (!shift_bit_func_enable(host) + && (int_status & SDHCI_INT_CARD_INSERT)) + || (!shift_bit_func_enable(host) + && (int_status & SDHCI_INT_CARD_REMOVE))) { + if (card_insert_flag) { + pdx->scsi_init_flag = 0; + insert_card_handle(pdx); + /* fixed uhs1 issue#120 */ + pdx->card.cmd_low_reset_flag = FALSE; + } else { + if (hascmd) { + if (req->int_flag_wait) { + sd_cmd->err.error_code |= + ERR_CODE_NO_CARD; + cb_handle(&(req->int_flag_wait), + 0, INTR_CB_ERR, + &hascmd, pdx); + } + } + remove_card_handle(pdx); + } + } + + } +#if (0) + /* ptest */ + if (hascmd && req) { + if (req->int_flag_wait == 0) + goto out; + } +#endif + int_status = sdhci_readl(host, SDHCI_INT_STATUS); + + if (device_power_enter_non_D0(param)) + int_status &= + ~(SDHCI_INT_ROC_BITS | SDHCI_INT_CARD_INSERT | + SDHCI_INT_CARD_REMOVE); + else + int_status &= ~(SDHCI_INT_ROC_BITS); + if (HW_TIMER_CFG == TRUE) { + if (hw_timer_intr & BIT7) + func_timer_callback(pdx); + + hw_timer_intr = pci_readl(host, 0x51c) & BIT7; + + if (hw_timer_intr & BIT7) + ven_writel(host, 0x51c, BIT7); + } + + if (host->dump_mode) + goto out; + + if ((int_status || hw_timer_intr) && --max_loops) + goto int_again; + +out: + + if (host->cfg->host_item.test_ocb_ctrl.int_check_en) + host_chk_ocb_occur(host); + DbgInfo(MODULE_SD_HOST, FEATURE_INTR_TRACE, NOT_TO_RAM, + "Exit %s ret=%x\n", __func__, ret); + return ret; +} + +bool irq_poll_cmd_done(bht_dev_ext_t *pdx, completion_t *p, s32 timeout_ms) +{ + bool ret = FALSE; + sd_host_t *host = &pdx->host; + sd_card_t *card = &pdx->card; + host_cmd_req_t *req = host->cmd_req; + sd_command_t *sd_cmd = NULL; + /* in order sdhci_irq need some time so just multiple 400 */ + u32 timeout = timeout_ms * 400; + + DbgInfo(MODULE_SD_HOST, FEATURE_INTR_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + + if (req == NULL) + sd_cmd = NULL; + else + sd_cmd = (sd_command_t *) req->private; + + if (req == NULL || sd_cmd == NULL) { + DbgErr("no command need do poll wait\n"); + goto exit; + } + + while (timeout > 0) { + ret = sdhci_irq(pdx); + if (ret == FALSE || card->card_present == FALSE) + goto exit; + + if (req->int_flag_wait == 0) { + ret = TRUE; + break; + } + timeout--; + os_udelay(1); + } + +exit: + DbgInfo(MODULE_SD_HOST, FEATURE_INTR_TRACE, NOT_TO_RAM, + "Exit %s ret=%x\n", __func__, ret); + return ret; +} diff --git a/drivers/scsi/bht/host/transhandler.c b/drivers/scsi/bht/host/transhandler.c new file mode 100644 index 000000000000..7f26b7607128 --- /dev/null +++ b/drivers/scsi/bht/host/transhandler.c @@ -0,0 +1,1730 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: transhandler.c + * + * Abstract: Handler for interrupt and dma buffer manager apis + * + * Version: 1.00 + * + * Author: Peter.Guo + * + * Environment: OS Independent + * + * History: + * + * 9/5/2014 Creation Peter.guo + */ + +#include "../include/basic.h" +#include "../include/debug.h" +#include "../include/hostapi.h" +#include "../include/cmdhandler.h" +#include "../host/hostreg.h" +#include "../include/util.h" + +/* + * + * Function Name: gen_adma2_desc_low_32bit + * + * Abstract: + * + * generate adma2 descriptor table low 32bit(data length & attribution) + * + * Input: + * + * u32 len [in]: data length value + * u32 attr[in]: descriptor attribution value + * + * Output: + * + * u32 [out]: the composite value for ADMA2 descriptor low 32bit value + * + * Return value: + * + * None + * Notes: + * + * Caller: + * + */ +#define ADMA2_16BIT_MASK 0x0ffff +#define ADMA2_10BIT_MASK 0x003ff + +#define gen_adma2_desc_low_32bit(len, attr) (((len & ADMA2_16BIT_MASK) << 16) | \ + (((len >> 16) & ADMA2_10BIT_MASK) << 6) | attr) + +bool dma_api_build_adma_io_add_nop(bht_dev_ext_t *pdx, sd_data_t *sd_data, + sg_list_t *sg, u32 sg_len); +bool dma_api_build_adma_sdma_io_add_nop(bht_dev_ext_t *pdx, + sd_data_t *sd_data); + +/* + * + * Function Name: end_adma2_desc_line + * + * Abstract: + * + * end 32bit DAM address adma2 descriptor table + * + * Input: + * + * u32 *pTable [in]: Pointer to the descriptor table + * + * Output: + * + * None. + * + * Return value: + * + * None + * Notes: + * + * Caller: + * + */ +static void adma2_end_desc_line(u8 *ptb, bool dma_64bit) +{ + if (dma_64bit == TRUE) + ptb = ptb - ADMA2_128BIT_ITEM_LEN; + else + ptb = ptb - ADMA2_ITEM_LEN; + + *ptb |= ADMA2_DESC_END_BIT; +} + +/* + * + * Function Name: adma2_clear_end_flag + * + * Abstract: + * + * clear DAM address adma2 descriptor end flag table for link ADMA2 table + * + * Input: + * + * u32 *pTable [in]: Pointer to the descriptor table + * + * Output: + * + * None. + * + * Return value: + * + * None + * Notes: + * + * Caller: + * + */ +static void adma2_clear_end_flag(u8 *ptb, bool dma_64bit) +{ + if (dma_64bit == TRUE) + ptb = ptb - ADMA2_128BIT_ITEM_LEN; + else + ptb = ptb - ADMA2_ITEM_LEN; + + *ptb &= ~(ADMA2_DESC_END_BIT); + +} + +bool link_adma2_desc(u8 *pdesc, phy_addr_t *pa, bool dma_64bit) +{ + + DbgInfo(MODULE_TRANS, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + adma2_clear_end_flag(pdesc, dma_64bit); + + *((u32 *) (pdesc)) = gen_adma2_desc_low_32bit(0, ADMA2_DESC_LINK_VALID); + pdesc += 4; + *((u32 *) (pdesc)) = os_get_phy_addr32l(*pa); + if (dma_64bit == TRUE) + *((u32 *) (pdesc + 4)) = os_get_phy_addr32h(*pa); + return TRUE; +} + +/* + * + * Function Name: build_adma2_desc_line + * + * Abstract: + * + * build 32bit address adma2 descriptor lines for one SGlist item. + * + * Input: + * + * u32 *pTable [in]: descripter table pointer + * u32 len_limit [in]: data length limitation + * u32 itemTotalLen [in]: one SGlist item total length + * u32 itemAddrHdr [in]: one SGlist item physical address + * + * Output: + * + * None + * + * Return value: + * + * the adma2 descriptor line number for one SGlist item. + * Notes: + * + * Caller: + * + */ +static int build_adma2_desc_line(u32 *pTable, u32 len_limit, u32 itemTotalLen, + u64 itemAddrHdr, bool dma_64bit) +{ + int adma2_line_number = 0; + u64 *p64 = 0; + + if (itemTotalLen == 0) { + DbgErr("%s sg len 0\n", __func__); + return 0; + } + + if (itemTotalLen > ADMA2_16BIT_LEN_SIZE) { + DbgInfo(MODULE_TRANS, FEATURE_RW_TRACE, NOT_TO_RAM, + "SGlist item pa over 64KB\n"); + } + + if (itemTotalLen < len_limit) { + *(pTable++) = + gen_adma2_desc_low_32bit(itemTotalLen, + ADMA2_DESC_TRAN_VALID); + if (dma_64bit == TRUE) { + p64 = (u64 *) pTable; + *p64 = itemAddrHdr; + } else + *(pTable++) = (u32) itemAddrHdr; + adma2_line_number++; + } else { + u32 j = 0; + + do { + itemTotalLen -= len_limit; + *(pTable++) = + gen_adma2_desc_low_32bit(0, ADMA2_DESC_TRAN_VALID); + if (dma_64bit == TRUE) { + p64 = (u64 *) pTable; + *p64 = + itemAddrHdr + ((u64) j * (u64) (len_limit)); + pTable += 3; + } else + *(pTable++) = + (u32) itemAddrHdr + (j) * (len_limit); + + adma2_line_number++; + j++; + } while (itemTotalLen >= len_limit); + + /* left small segment desc line */ + if (itemTotalLen) { + *(pTable++) = + gen_adma2_desc_low_32bit(itemTotalLen, + ADMA2_DESC_TRAN_VALID); + + if (dma_64bit == TRUE) { + p64 = (u64 *) pTable; + *p64 = + itemAddrHdr + ((u64) j * (u64) (len_limit)); + } else + *(pTable++) = + (u32) itemAddrHdr + (j) * (len_limit); + + adma2_line_number++; + } + } + + return adma2_line_number; +} + +dma_desc_buf_t build_adma2_desc_nop(sg_list_t *sg, u32 sg_len, byte *desc_buf, + u32 desc_len, bool dma_64bit, + bool data_26bit) +{ + dma_desc_buf_t dma = { 0 }; + sg_list_t *pAddList = 0; + u32 *pTable = 0; + u32 i = 0; + /* counter for ADMA2 line number */ + int adma2_line_number = 0; + u32 adma2_data_length = 0; + u32 max_adma2_tb_len = 0; + u32 adma2_item_len = 0; + u64 *p64 = 0; + + DbgInfo(MODULE_TRANS, FEATURE_RW_TRACE, NOT_TO_RAM, + "Enter %s sglen=%xh 64dma=%x, 26dat=%x\n", __func__, sg_len, + dma_64bit, data_26bit); + /* 1. Init variables */ + pAddList = sg; + pTable = (u32 *) (desc_buf); + + /* 2. check parameters */ + if (sg_len == 0 || desc_buf == NULL || sg == NULL) { + DbgErr("%s invalid %d %p %p\n", __func__, sg_len, desc_buf, + sg); + dma.va = 0; + return dma; + } + /* 3. config dma 64bit */ + if (dma_64bit == TRUE) { + adma2_item_len = ADMA2_128BIT_ITEM_LEN; + max_adma2_tb_len = MAX_ADMA2_128BIT_TABLE_LEN; + } else { + adma2_item_len = ADMA2_ITEM_LEN; + max_adma2_tb_len = MAX_ADMA2_TABLE_LEN; + } + + if (desc_len < max_adma2_tb_len) { + DbgErr("%s no enough desc_len(%d)%d\n", __func__, desc_len, + max_adma2_tb_len); + dma.va = 0; + return dma; + } + + /* 3. clear buffer */ + os_memset(pTable, 0, max_adma2_tb_len); + + /* 4. select data length limit */ + if (data_26bit == TRUE) + adma2_data_length = ADMA2_26BIT_LEN_SIZE; + else + adma2_data_length = ADMA2_16BIT_LEN_SIZE; + /* 5.1. generate adma2 descriptor NOP lines */ + { + *(pTable++) = gen_adma2_desc_low_32bit(0, ADMA2_DESC_INT_VALID); + if (dma_64bit == TRUE) { + p64 = (u64 *) pTable; + *p64 = 0; + pTable += 3; + } else + *(pTable++) = 0; + adma2_line_number++; + } + + /* 5.2. generate adma2 descriptor lines */ + for (i = 0; i < sg_len; i++) { + int line_cnt = 0; + + line_cnt = + build_adma2_desc_line(pTable, adma2_data_length, + pAddList[i].Length, + pAddList[i].Address, dma_64bit); + pTable += (u32) ((u64) (adma2_item_len / 4) * (u64) line_cnt); + adma2_line_number += line_cnt; + } + + /* 6. end table */ + adma2_end_desc_line((u8 *) pTable, dma_64bit); + + /* 7. update */ + dma.va = + desc_buf + (u32) ((u64) (adma2_line_number) * (u64) adma2_item_len); + + DbgInfo(MODULE_TRANS, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); + + return dma; +} + +/* + * + * Function Name: build_adma2_desc + * + * Abstract: + * + * build 32bit address adma2 descriptor table for SGlist. + * + * Input: + * + * sg_list_t *sg [in]: pointer the SGlist + * u32 sg_len [in]: the item number of the SGlist + * byte *desc_buf [in]: the buffer for ADMA2 descriptor generate + * u32 desc_len [in]: the buffer length + * + * Output: + * + * None + * + * Return value: + * + * the adma2 descriptor line number for one SGlist item. + * Notes: + * + * Caller: + * + */ +dma_desc_buf_t build_adma2_desc(sg_list_t *sg, u32 sg_len, byte *desc_buf, + u32 desc_len, bool dma_64bit, bool data_26bit) +{ + dma_desc_buf_t dma = { 0 }; + sg_list_t *pAddList = 0; + u32 *pTable = 0; + u32 i = 0; + /* counter for ADMA2 line number */ + int adma2_line_number = 0; + u32 adma2_data_length = 0; + u32 max_adma2_tb_len = 0; + u32 adma2_item_len = 0; + + DbgInfo(MODULE_TRANS, FEATURE_RW_TRACE, NOT_TO_RAM, + "Enter %s sglen=%xh 64dma=%x, 26dat=%x\n", __func__, sg_len, + dma_64bit, data_26bit); + /* 1. Init variables */ + pAddList = sg; + pTable = (u32 *) (desc_buf); + + /* 2. check parameters */ + if (sg_len == 0 || desc_buf == NULL || sg == NULL) { + DbgErr("%s invalid %d %p %p\n", __func__, sg_len, desc_buf, + sg); + dma.va = 0; + return dma; + } + /* 3. config dma 64bit */ + if (dma_64bit == TRUE) { + adma2_item_len = ADMA2_128BIT_ITEM_LEN; + max_adma2_tb_len = MAX_ADMA2_128BIT_TABLE_LEN; + } else { + adma2_item_len = ADMA2_ITEM_LEN; + max_adma2_tb_len = MAX_ADMA2_TABLE_LEN; + } + + if (desc_len < max_adma2_tb_len) { + DbgErr("%s no enough desc_len(%d)%d\n", __func__, desc_len, + max_adma2_tb_len); + dma.va = 0; + return dma; + } + + /* 3. clear buffer */ + os_memset(pTable, 0, max_adma2_tb_len); + /* 4. select data length limit */ + if (data_26bit == TRUE) + adma2_data_length = ADMA2_26BIT_LEN_SIZE; + else + adma2_data_length = ADMA2_16BIT_LEN_SIZE; + /* 5. generate adma2 descriptor lines */ + for (i = 0; i < sg_len; i++) { + int line_cnt = 0; + + line_cnt = + build_adma2_desc_line(pTable, adma2_data_length, + pAddList[i].Length, + pAddList[i].Address, dma_64bit); + pTable += (u32) ((u64) (adma2_item_len / 4) * (u64) line_cnt); + adma2_line_number += line_cnt; + } + + /* 6. end table */ + adma2_end_desc_line((u8 *) pTable, dma_64bit); + + /* 7. update */ + dma.va = + desc_buf + (u32) ((u64) (adma2_line_number) * (u64) adma2_item_len); + + DbgInfo(MODULE_TRANS, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); + + return dma; +} + +/* + * + * Function Name: update_adma2_inf_tb + * + * Abstract: + * + * update adma2 descriptor table for infinite mode + * + * Input: + * + * u8 *pdesc [in]: Pointer to the descriptor table + * u8 **link_addr [in]: the previous adma2 table link address + * phy_addr_t *pa [in]: the current adma2 table address + * + * Output: + * + * u8 **link_addr [out]: the new adma2 table link address for + * next infinite transfer. + * + * Return value: + * + * None + * Notes: + * + * Caller: + * + */ +bool update_adma2_inf_tb(u8 *pdesc, u8 **link_addr, phy_addr_t *pa, + bool dma_64bit) +{ + u32 *ptb = 0; + + /* 1.update link addr */ + if (pa != NULL) { + *((u32 *) (*link_addr)) = os_get_phy_addr32l(*pa); + if (dma_64bit == TRUE) + *((u32 *) (*link_addr + 4)) = os_get_phy_addr32h(*pa); + } + + adma2_clear_end_flag(pdesc, dma_64bit); + ptb = (u32 *) pdesc; + *(ptb++) = ADMA2_DESC_INT_VALID; + *(ptb++) = 0; + if (dma_64bit == TRUE) { + *(ptb++) = 0; + *(ptb++) = 0; + } + *(ptb++) = ADMA2_DESC_LINK_VALID; + /* 2. save new link addr */ + (*link_addr) = (u8 *) ptb; + *(ptb++) = 0; + if (dma_64bit == TRUE) { + *(ptb++) = 0; + *(ptb++) = 0; + } + return TRUE; +} + +/* + * + * Function Name: build_uhs1_cmd_desc + * + * Abstract: + * + * build uhs1 card command descriptor table for ADMA3 + * + * Input: + * + * u8 *pdesc [in]: Pointer to the descriptor table + * host_trans_reg_t *regs [in] : pointer to regs for build + * + * Output: + * + * + * + * Return value: + * + * size of usage for build + * Notes: + * + * Caller: + * + */ +static u32 build_uhs1_cmd_desc(u8 *pdesc, host_trans_reg_t *regs) +{ + u32 *ptb = (u32 *) pdesc; + + *(ptb) = ADMA3_DESC_SD_VALID; + *(ptb + 1) = regs->block_cnt; + *(ptb + 2) = ADMA3_DESC_SD_VALID; + *(ptb + 3) = regs->block_size; + + /* Set argument */ + *(ptb + 4) = ADMA3_DESC_SD_VALID; + /* uhs1 use playload[0] for argument */ + *(ptb + 5) = regs->payload[0]; + + /* data cmd */ + *(ptb + 6) = ADMA3_DESC_SD_VALID; + *(ptb + 7) = regs->trans_mode; + + return ADMA3_CMDDESC_ITEM_LENGTH * ADMA3_CMDDESC_ITEM_NUM_UHSI; +} + +/* + * + * Function Name: build_uhs2_cmd_desc + * + * Abstract: + * + * build uhs2 card command descriptor table for ADMA3 + * + * Input: + * + * u8 *pdesc [in]: Pointer to the descriptor table + * host_trans_reg_t *regs [in] : pointer to regs for build + * + * Output: + * + * + * + * Return value: + * + * size of usage for build + * Notes: + * + * Caller: + * + */ +static u32 build_uhs2_cmd_desc(u8 *pdesc, host_trans_reg_t *regs) +{ + u32 *ptb = (u32 *) pdesc; + + *ptb = ADMA3_DESC_UHS2_VALID; + *(ptb + 1) = regs->block_size; + *(ptb + 2) = ADMA3_DESC_UHS2_VALID; + *(ptb + 3) = regs->block_cnt; + + *(ptb + 4) = ADMA3_DESC_UHS2_VALID; + /* header */ + *(ptb + 5) = regs->payload[0]; + + *(ptb + 6) = ADMA3_DESC_UHS2_VALID; + /* argument */ + *(ptb + 7) = regs->payload[1]; + + *(ptb + 8) = ADMA3_DESC_UHS2_VALID; + /* block cnt */ + *(ptb + 9) = regs->payload[2]; + + *(ptb + 10) = ADMA3_DESC_UHS2_VALID; + *(ptb + 11) = regs->payload[3]; + *(ptb + 12) = ADMA3_DESC_UHS2_VALID; + *(ptb + 13) = regs->payload[4]; + *(ptb + 14) = ADMA3_DESC_UHS2_VALID; + *(ptb + 15) = regs->trans_mode; + + return ADMA3_CMDDESC_ITEM_LENGTH * ADMA3_CMDDESC_ITEM_NUM_UHSII; +} + +u32 build_card_cmd_desc(sd_card_t *card, u8 *desc, sd_command_t *cmd) +{ + u32 size = 0; + + if (card->card_type == CARD_UHS2) { + byte i = 0; + + for (i = 0; i < cmd->trans_reg_cnt; i++) + size += build_uhs2_cmd_desc(desc, &cmd->trans_reg[i]); + + return size; + } else { + byte i = 0; + + for (i = 0; i < cmd->trans_reg_cnt; i++) + size += build_uhs1_cmd_desc(desc, &cmd->trans_reg[i]); + + return size; + } + +} + +/* + * + * Function Name: build_integrated_desc + * + * Abstract: + * + * build integrated descriptor table for ADMA3 + * + * Input: + * + * u8 *desc [in]: Pointer to descriptor buffer + * phy_addr_t *pa [in] : the physical address + * bool dma_64bit [in] : 64bit dma address + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: build ok + * Notes: + * + * Caller: + * + */ +byte *build_integrated_desc(u8 *desc, phy_addr_t *pa, bool dma_64bit) +{ + u32 *ptb = (u32 *) desc; + + *ptb = ADMA3_INTEGRATE_DESC_VALID; + *(ptb + 1) = os_get_phy_addr32l(*pa); + if (dma_64bit) { + *(ptb + 2) = os_get_phy_addr32h(*pa); + *(ptb + 3) = 0; + return (desc + ADMA3_INTEGRATEDDESC_128BIT_ITEM_LEN); + } else + return (desc + ADMA3_INTEGRATEDDESC_ITEM_LEN); +} + +/* + * + * Function Name: adma3_end_integrated_tb + * + * Abstract: + * + * end integrated descriptor table for ADMA3 + * + * Input: + * + * u8 *desc [in]: Pointer to descriptor buffer + * bool dma_64bit [in] : 64bit dma address + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: build ok + * Notes: + * + * Caller: + * + */ +bool adma3_end_integrated_tb(u8 *desc, bool dma_64bit) +{ + u32 *ptb = 0; + + if (dma_64bit) + desc = desc - ADMA3_INTEGRATEDDESC_128BIT_ITEM_LEN; + else + desc = desc - ADMA3_INTEGRATEDDESC_ITEM_LEN; + ptb = (u32 *) desc; + *ptb = (*ptb) | GENERAL_DESC_END_BIT; + return TRUE; +} + +/* + * + * Function Name: get_sdma_boudary_size + * + * Abstract: + * + * get sdma boundary size from config + * + * Input: + * + * cfg_item_t *cfg [in]: Pointer to config + * + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: byte size + * Notes: + * + * Caller: + * + */ +u32 get_sdma_boudary_size(cfg_item_t *cfg) +{ + u32 len = 0; + + len = cfg->host_item.test_sdma_boundary.value; + len = len * 1024; + return len; +} + +/* + * + * Function Name: dma_align + * + * Abstract: + * + * set dma buffer alignment + * + * Input: + * + * dma_desc_buf_t *pdma [in]: Pointer to DMA buffer which for align + * u32 align_size [in]: align byte size + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: align ok + * FALSE: align failed due to buffer to small to align + * Notes: + * + * Caller: + * + */ +bool dma_align(dma_desc_buf_t *pdma, u32 align_size) +{ + u32 dmaAlignOffset = 0; + bool ret = FALSE; + /* align dma buffer */ + dmaAlignOffset = os_get_phy_addr32l(pdma->pa) % align_size; + if (dmaAlignOffset) { + dmaAlignOffset = align_size - dmaAlignOffset; + if (resize_dma_buf(pdma, dmaAlignOffset) == FALSE) { + DbgErr("align DMA buf resize failed\n"); + ret = FALSE; + goto exit; + } + } + ret = TRUE; +exit: + return ret; +} + +/* + * + * Function Name: cmd_sdma_boundary + * + * Abstract: + * + * This Function is used to handle sdma boundary interrupt callback + * + * Input: + * + * void *card : pointer to card + * void *host_request poineter to host_cmd_req_t + * + * Output: + * + * None. + * + * Return value: + * + * INTR_CB_OK: final DMA int for sdma + * INTR_CB_NOEND: will get new DMA int + * Notes: + * so giving the routine another name requires you to modify the build tools. + * Caller: + * + * test case: + * 1.user data 512B, DMA boundary size 4KB. + * [no DMA int occur, so need transfer complete cb do copy] + * 2.user data 4KB, DMA boundary size 4KB. + * [both DMA int & transfer complete occur] + * 3.user data 5KB, DMA boundary size 4KB. + * [first DMA int occur, then transfer complete occur secondly.] + * 4.user data 12KB, DMA boundary size 4KB. + * [first, second DMA int occur, then both DMA int &transfer cpl ocuur] + * 5.user data 13KB, DMA boundary size 4KB. + */ +u32 cmd_sdma_boundary(void *pcard, void *host_request) +{ + u32 ret = INTR_CB_OK; + sd_card_t *card = pcard; + sd_host_t *host = card->host; + host_cmd_req_t *req = host_request; + sd_command_t *sd_cmd = req->private; + sd_data_t *data = sd_cmd->data; + data_dma_mng_t *mgr = &data->data_mng; + u32 sdma_bd_len = get_sdma_boudary_size(host->cfg); + bool dma_64bit = host->bit64_enable ? TRUE : FALSE; + u32 min_size = 0; + u32 left = 0; + byte buhs2 = sd_cmd->uhs2_cmd; + /* u32 device_status; */ + + DbgInfo(MODULE_TRANS, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + if (mgr->offset >= mgr->total_bytess) + return INTR_CB_OK; + + /* copy data */ + left = mgr->total_bytess - mgr->offset; + min_size = os_min(sdma_bd_len, left); + + if (data->dir == DATA_DIR_IN) { + os_memcpy(mgr->srb_buffer[0].buff + mgr->offset, + mgr->driver_buff, min_size); + mgr->offset += min_size; + left = mgr->total_bytess - mgr->offset; + } else { + /* write case */ + os_memcpy(mgr->driver_buff, + mgr->srb_buffer[0].buff + mgr->offset, min_size); + left = mgr->total_bytess - mgr->offset; + mgr->offset += min_size; + } + + /* update return value */ + if (left >= sdma_bd_len) { + /* will get new DMA int */ + ret = INTR_CB_NOEND; + } else + /* it's the last one DMA int */ + ret = INTR_CB_OK; + /* update SDMA system address : for reuse same sdma buffer, so no need update sys_addr */ + if (left > 0) { + if (buhs2 || host->sd_host4_enable) { + sdhci_writel(host, SDHCI_ADMA_ADDRESS, + os_get_phy_addr32l(mgr->sys_addr)); + if (dma_64bit) + sdhci_writel(host, SDHCI_ADMA_ADDRESSH, + os_get_phy_addr32h(mgr->sys_addr)); + } else + sdhci_writel(host, SDHCI_DMA_ADDRESS, + os_get_phy_addr32l(mgr->sys_addr)); + } + DbgInfo(MODULE_TRANS, FEATURE_RW_TRACE, NOT_TO_RAM, + "Enter %s ret=%x ofs=%xh,tot=%xh\n", __func__, ret, + mgr->offset, mgr->total_bytess); + return ret; +} + +/* + * + * Function Name: cmd_sdma_trans_done + * + * Abstract: + * + * handle sdma transfer done. + * + * Input: + * + * void *card : pointer to card + * void *host_request poineter to host_cmd_req_t + * + * Output: + * + * None. + * + * Return value: + * + * INTR_CB_OK: align ok + * + * Notes: + * clear DMA if transfer complete & data reach the size + * Caller: + * + */ +u32 cmd_sdma_trans_done(void *pcard, void *host_request) +{ + sd_card_t *card = pcard; + host_cmd_req_t *req = (host_cmd_req_t *) host_request; + sd_command_t *cmd = (sd_command_t *) req->private; + sd_data_t *data = cmd->data; + data_dma_mng_t *mgr = &data->data_mng; + u32 sdma_bd_len = get_sdma_boudary_size(card->host->cfg); + u32 left = 0; + + DbgInfo(MODULE_TRANS, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + left = mgr->total_bytess - mgr->offset; + if (left > sdma_bd_len) + DbgErr("left data over boundary size\n"); + if (left) { + if (data->dir == DATA_DIR_IN) { + /* copy last data */ + os_memcpy(mgr->srb_buffer[0].buff + mgr->offset, + mgr->driver_buff, left); + } else { + /* write case */ + DbgErr("sdma trans done, but need copy\n"); + os_memcpy(mgr->driver_buff, + mgr->srb_buffer[0].buff + mgr->offset, left); + } + } + mgr->offset += left; + /* clear DMA if transfer complete & data reach size */ + if (mgr->total_bytess <= mgr->offset) { + if (req->int_flag_wait & SDHCI_INT_DMA_END) + req->int_flag_wait &= ~SDHCI_INT_DMA_END; + } + DbgInfo(MODULE_TRANS, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); + + return INTR_CB_OK; +} + +/* + * Function Name: cmd_adma2_inf_boundary + * Abstract: This Function is used to handle adma2 and adma2_sdma infinite boundary intr + * + * Input: + * void *card : pointer to pcard + * void *host_request poineter to host_cmd_req_t + * + * + * Return value: + * 0: means ok + * others error + * + * Notes: + * + * so giving the routine another name requires you to modify the build tools. + */ +u32 cmd_adma2_inf_boundary(void *pcard, void *host_request) +{ + sd_card_t *card = pcard; + sd_host_t *host = card->host; + host_cmd_req_t *req = host_request; + sd_command_t *sd_cmd = req->private; + sd_data_t *data = sd_cmd->data; + data_dma_mng_t *mgr = &data->data_mng; + u32 i = 0; + + /* adma2 inf case no need do any action here */ + DbgInfo(MODULE_TRANS, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + + if (sd_cmd->gg8_ddr200_workaround) { + + if (data->dir == DATA_DIR_OUT) { + DbgInfo(MODULE_TRANS, FEATURE_RW_TRACE, NOT_TO_RAM, + "update output phase for write case\n"); + /* Disable SD clock */ + sdhci_and32(host, SDHCI_CLOCK_CONTROL, + ~(SDHCI_CLOCK_CARD_EN)); + + /* update output phase */ + pci_andl(host, 0x354, 0xFFFFFF0F); + pci_orl(host, 0x354, (host->cur_output_phase << 4)); + + /* update input phase */ + sdhci_and32(card->host, SDHCI_DLL_PHASE_CFG, + ~0x1F000000); + sdhci_or32(card->host, SDHCI_DLL_PHASE_CFG, + (BIT28) | + (card->output_input_phase_pair + [host->cur_output_phase] + << 24)); + + /* Enable SD clock */ + sdhci_or32(host, SDHCI_CLOCK_CONTROL, + (SDHCI_CLOCK_CARD_EN)); + } + + /* Continue transfer */ + sdhci_or32(host, SDHCI_DRIVER_CTRL_REG, + SDHCI_DRIVER_CTRL_ADMA2_START_INF); + /* sd_cmd->gg8_ddr200_workaround = 0; */ + } + + /* adma2 sdma-like inf case */ + if (data->dir == DATA_DIR_IN) { + for (i = 0; i < mgr->srb_cnt; i++) { + os_memcpy(mgr->srb_buffer[i].buff, + mgr->driver_buff + mgr->srb_buffer[i].ofs, + mgr->srb_buffer[i].len); + } + } + DbgInfo(MODULE_TRANS, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); + return INTR_CB_OK; +} + +/* + * Function Name: cmd_adma2_sdma_like_trans_done + * Abstract: This Function is used to handle adma2_sdma non-inf transfer complete + * + * Input: + * void *card : pointer to vpcard + * void *host_request poineter to host_cmd_req_t + * + * + * Return value: + * 0: means ok + * others error + * + * Notes: + * + * so giving the routine another name requires you to modify the build tools. + */ +u32 cmd_adma2_sdma_like_trans_done(void *pcard, void *host_request) +{ + host_cmd_req_t *req = (host_cmd_req_t *) host_request; + sd_command_t *cmd = (sd_command_t *) req->private; + sd_data_t *data = cmd->data; + data_dma_mng_t *mgr = &data->data_mng; + u32 i = 0; + + DbgInfo(MODULE_TRANS, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + if (data->dir == DATA_DIR_IN) { + for (i = 0; i < mgr->srb_cnt; i++) { + os_memcpy(mgr->srb_buffer[i].buff, + mgr->driver_buff + mgr->srb_buffer[i].ofs, + mgr->srb_buffer[i].len); + } + } + DbgInfo(MODULE_TRANS, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); + return INTR_CB_OK; +} + +/* + * Function Name: cmd_adma3_trans_done + * Abstract: This Function is used to handle adma3 transfer complete + * + * Input: + * void *card : pointer to pcard + * void *host_request poineter to host_cmd_req_t + * + * + * Return value: + * 0: means ok + * others error + * + * Notes: + * + * so giving the routine another name requires you to modify the build tools. + */ +u32 cmd_adma3_trans_done(void *pcard, void *host_request) +{ + /* + * for adma3 no need do anything + * for adma3 sdma-like, do memory copy for SRB buffer. + */ + return INTR_CB_OK; +} + +bool dma_api_build_sdma_io(bht_dev_ext_t *pdx, sd_data_t *sd_data); +bool dma_api_build_adma_sdma_io(bht_dev_ext_t *pdx, sd_data_t *sd_data); +bool dma_api_build_adma_io(bht_dev_ext_t *pdx, sd_data_t *sd_data, + sg_list_t *sg, u32 sg_len); + +bool build_dma_ctx(void *pdx, sd_data_t *sd_data, + u32 cmdflag, + e_data_dir dir, + byte *data, u32 datalen, sg_list_t *sglist, u32 sg_len) +{ + + bool ret = TRUE; + + sd_data->dir = dir; + sd_data->data_mng.driver_buff = data; + sd_data->data_mng.total_bytess = datalen; + +#if (1) + if (cmdflag & CMD_FLG_ADMA_SDMA) { + if (cmdflag & CMD_FLG_DDR200_WORK_AROUND) + ret = dma_api_build_adma_sdma_io_add_nop(pdx, sd_data); + else + ret = dma_api_build_adma_sdma_io(pdx, sd_data); + + if (ret == FALSE) { + DbgErr("build adma io error\n"); + ret = FALSE; + goto exit; + } + } + if (cmdflag & CMD_FLG_ADMA2) { + if (cmdflag & CMD_FLG_DDR200_WORK_AROUND) + ret = + dma_api_build_adma_io_add_nop(pdx, sd_data, sglist, + sg_len); + else + ret = + dma_api_build_adma_io(pdx, sd_data, sglist, sg_len); + + if (ret == FALSE) { + DbgErr("build adma io error\n"); + ret = FALSE; + goto exit; + } + } +#endif + if (cmdflag & CMD_FLG_SDMA) { + ret = dma_api_build_sdma_io(pdx, sd_data); + if (ret == FALSE) { + DbgErr("build sdma io error\n"); + ret = FALSE; + goto exit; + } + } +exit: + return ret; + +} + +bool dma_api_io_init(bht_dev_ext_t *pdx, dma_desc_buf_t *desc_buf) +{ + node_t *node = &pdx->dma_api.dma_node; + node_t *node2 = &pdx->dma_api.dma_node2; + bool ret = FALSE; + /* 1. check size */ + if (pdx->dump_mode == FALSE) { + if (desc_buf->len < (MIN_DMA_API_BUF_SIZE)) { + ret = FALSE; + DbgErr("dma buf too small 0x%x <=(%x)\n", desc_buf->len, + (MIN_DMA_API_BUF_SIZE)); + goto exit; + } + } + /* 2. assign buf */ + node->general_desc_tbl = *desc_buf; + node->general_desc_tbl.len = MAX_GENERAL_DESC_TABLE_LEN; + node->general_desc_tbl_img = node->general_desc_tbl; + ret = resize_dma_buf(desc_buf, MAX_GENERAL_DESC_TABLE_LEN); + if (ret == FALSE) { + ret = FALSE; + goto exit; + } + + pdx->dma_api.cur_node = NULL; + /* for dump mode we only use adma2 mode */ + if (pdx->dump_mode == TRUE) { + node2->general_desc_tbl = *desc_buf; + node2->general_desc_tbl.len = MAX_GENERAL_DESC_TABLE_LEN; + node2->general_desc_tbl_img = node2->general_desc_tbl; + ret = resize_dma_buf(desc_buf, MAX_GENERAL_DESC_TABLE_LEN); + if (ret == FALSE) { + DbgErr("Allocate node2 for dump mode failed\n"); + ret = FALSE; + goto exit; + } + + ret = TRUE; + goto exit; + } + + /* 3. align dma buffer for adma2 API buffer */ + if (dma_align(desc_buf, DMA_BUF_ALIGN_SIZE) == FALSE) { + DbgErr("tq adma2 API buffer align failed\n"); + ret = FALSE; + goto exit; + } + /* 4. allocate adma2 API buffer resource */ + node->data_tbl = *desc_buf; + node->data_tbl.len = DMA_API_BUF_SIZE; + node->data_tbl_img = node->data_tbl; + DbgInfo(MODULE_TQ_DMA, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "adma2 api buf len %x pa(%x)\n", node->data_tbl.len, + os_get_phy_addr32l(node->data_tbl.pa)); + /* update for dma buf usage */ + if (resize_dma_buf(desc_buf, DMA_API_BUF_SIZE) == FALSE) { + ret = FALSE; + DbgErr("%s adm2 API buf resize failed\n", __func__); + goto exit; + } + ret = TRUE; + /* DbgErr("DMA API desc %x , data %x\n",node->general_desc_tbl.pa,node->data_tbl.pa); */ +exit: + + return ret; +} + +/* + * + * Function Name:node_get_desc_res + * + * Abstract: + * + * get node descriptor resource + * + * Input: + * + * node_t *node [in]: Pointer to node + * u32 max_use_size [in]: the max maybe use size for descriptor table + * + * Output: + * + * + * + * Return value: + * + * dma_desc_buf_t *: NULL means failed to get the descriptor resource. + * other means get ok. + * Notes: + * + * Caller: + * + */ + +dma_desc_buf_t *node_get_desc_res(node_t *node, u32 max_use_size) +{ + dma_desc_buf_t *p = &node->general_desc_tbl; + + if (max_use_size > p->len) { + DbgErr("%s no enough buf for desc\n", __func__); + return NULL; + } + return &node->general_desc_tbl; +} + +bool _adma_only_build_io(sg_list_t *sg, u32 sg_len, bool dma_64bit, + bool data_26bit_len, dma_desc_buf_t *end_dma, + data_dma_mng_t *mgr, dma_desc_buf_t *dma) +{ + bool ret = FALSE; + + *end_dma = + build_adma2_desc(sg, sg_len, (byte *) dma->va, dma->len, dma_64bit, + data_26bit_len); + if (end_dma->va == NULL) { + DbgErr("%s build adma2 desc failed\n", __func__); + ret = FALSE; + goto exit; + } + + mgr->sys_addr = dma->pa; + ret = TRUE; +exit: + return ret; + +} + +void dbg_dump_general_desc_tb(u8 *desc, u32 size) +{ + u32 i = 0; + u32 *pTable = (u32 *) desc; + + size = size / (sizeof(u32) * 2); + /* + * for some case, need dump more + * dump more for 128bit infinite int + link case + */ + size += 4; + +#define MAX_DUMP_DESC_SIZE (1024 * 16) + if (size > MAX_DUMP_DESC_SIZE) { + DbgInfo(MODULE_TQ_DMA, FEATURE_FUNC_DESC, NOT_TO_RAM, + "%s over limit %x\n", __func__, size); + size = MAX_DUMP_DESC_SIZE; + } + + for (i = 0; i < size; i++) { + DbgErr(" [0x%0.8Xh], [0x%0.8x]\n", + pTable[(i * 2) + 1], pTable[i * 2]); + } +} + +u32 pp_ofs(byte *ph, byte *pl) +{ + u64 ofs = 0; + + ofs = ph - pl; + if (ofs >= 0xffffffff) { + DbgErr("%s:(%x)maybe over 32bit size\n", __func__, ofs); + return 0; + } + return (u32) ofs; +} + +void dump_adma2_desc(u8 *desc, u8 *desc_end) +{ + u32 size = 0; + + size = pp_ofs(desc_end, desc); + dbg_dump_general_desc_tb(desc, size); +} + +/* + * + * Function Name:dump_node_adma2_desc + * + * Abstract: + * + * dump node adma2 desc + * + * Input: + * + * + * Output: + * + * + * + * Return value: + * + * Notes: + * + * Caller: + * + */ +bool dump_node_adma2_desc(node_t *node, void *ctx) +{ + phy_addr_t sys_addr; + u8 *desc = node->phy_node_buffer.head.va; + u8 *desc_end = node->phy_node_buffer.end.va; + + sys_addr = node->phy_node_buffer.head.pa; + DbgErr("sys addrl %x addrh %x\n", os_get_phy_addr32l(sys_addr), + os_get_phy_addr32h(sys_addr)); + dump_adma2_desc(desc, desc_end); + return TRUE; +} + +bool _dma_api_build_adma_io(node_t *node, sg_list_t *sg, u32 sg_len, + bool dma_64bit, bool data_26bit_len, + sd_data_t *sd_data) +{ + bool ret = FALSE; + dma_desc_buf_t *pdma = 0; + u32 adma2_size = 0; + + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* 1. get ADMA2 desc buffer */ + adma2_size = + (TRUE == + dma_64bit) ? MAX_ADMA2_128BIT_TABLE_LEN : MAX_ADMA2_TABLE_LEN; + pdma = node_get_desc_res(node, adma2_size); + if (pdma == NULL) { + DbgErr("%s node get desc failed\n", __func__); + ret = FALSE; + goto exit; + } + node->phy_node_buffer.head = *pdma; + /* 2. build ADMA2 Desc */ + ret = + _adma_only_build_io(sg, sg_len, dma_64bit, data_26bit_len, + &node->phy_node_buffer.end, &sd_data->data_mng, + pdma); +exit: + /* dump_node_adma2_desc(node,NULL); */ + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "Enter %s ret=%d\n", __func__, ret); + return ret; + +} + +bool _dma_api_build_adma_io_add_nop(node_t *node, sg_list_t *sg, u32 sg_len, + bool dma_64bit, bool data_26bit_len, + sd_data_t *sd_data) +{ + bool ret = FALSE; + dma_desc_buf_t *pdma = 0; + u32 adma2_size = 0; + + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* 1. get ADMA2 desc buffer */ + adma2_size = + (TRUE == + dma_64bit) ? MAX_ADMA2_128BIT_TABLE_LEN : MAX_ADMA2_TABLE_LEN; + pdma = node_get_desc_res(node, adma2_size); + if (pdma == NULL) { + DbgErr("%s node get desc failed\n", __func__); + ret = FALSE; + goto exit; + } + node->phy_node_buffer.head = *pdma; + + node->phy_node_buffer.end = + build_adma2_desc_nop(sg, sg_len, (byte *) pdma->va, pdma->len, + dma_64bit, data_26bit_len); + if (node->phy_node_buffer.end.va == NULL) { + DbgErr("%s build adma2 desc failed\n", __func__); + ret = FALSE; + goto exit; + } + + /* generate DMA INT at end */ + { + u32 *ptb = 0; + + adma2_clear_end_flag(node->phy_node_buffer.end.va, dma_64bit); + ptb = (u32 *) node->phy_node_buffer.end.va; + *(ptb++) = ADMA2_DESC_INT_VALID; + *(ptb++) = 0; + if (dma_64bit == TRUE) { + *(ptb++) = 0; + *(ptb++) = 0; + } + } + + sd_data->data_mng.sys_addr = pdma->pa; + ret = TRUE; + +exit: + dump_node_adma2_desc(node, NULL); + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "Enter %s ret=%d\n", __func__, ret); + return ret; + +} + +void adma_sdma_post_io(data_dma_mng_t *mgr, e_data_dir dir, byte *data_buf) +{ + if (dir == DATA_DIR_OUT) + os_memcpy(data_buf, mgr->driver_buff, mgr->total_bytess); + if (dir == DATA_DIR_IN) { + mgr->srb_cnt = 1; + mgr->srb_buffer[0].buff = mgr->driver_buff; + mgr->srb_buffer[0].len = mgr->total_bytess; + mgr->srb_buffer[0].ofs = 0; + mgr->driver_buff = data_buf; + } +} + +/* + * + * Function Name: gen_sdma_like_sgl + * + * Abstract: + * + * generate sdma-like SGlist table + * + * Input: + * + * request_t *req [in]: Pointer to the request for build + * dma_desc_buf_t *pdma [in]:pointer to sdma-like buffer + * + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: build ok + * Notes: + * + * Caller: + * + */ +bool gen_sdma_like_sgl(request_t *req, dma_desc_buf_t *pdma) +{ + sg_list_t *sg; + + sg = req->srb_sg_list; + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + if ((pdma->va == 0) && (pdma->len == 0)) { + DbgErr("%s null va\n", __func__); + return FALSE; + } + + /* for 64bit case */ + sg[0].Address = os_get_phy_addr64(pdma->pa); + sg[0].Length = req->tag_req_t.sec_cnt * SD_BLOCK_LEN; + req->srb_sg_len = 1; + + if (pdma->len < sg[0].Length) { + DbgErr("%s dma buf too small\n", __func__); + return FALSE; + } + + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); + return TRUE; +} + +bool adma_sdma_gen_sglist(node_t *node, data_dma_mng_t *mgr, request_t *req) +{ + bool ret = FALSE; + + dma_desc_buf_t *pdma = 0; + + if (mgr->total_bytess > DMA_API_BUF_SIZE) { + DbgErr("%s data total bytes too large(%x)>(%x)\n", __func__, + mgr->total_bytess, DMA_API_BUF_SIZE); + ret = FALSE; + goto exit; + } + + /* build srb_ext */ + req->tag_req_t.sec_cnt = mgr->total_bytess / SD_BLOCK_LEN; + + /* 1. get sdma like buf address */ + pdma = &node->data_tbl; + + /* 2. generate sdma like sglist table */ + if (gen_sdma_like_sgl(req, pdma) == FALSE) { + DbgErr("%s gen sdma-like sgl failed\n", __func__); + ret = FALSE; + goto exit; + } + + ret = TRUE; +exit: + + return ret; +} + +bool dma_api_build_adma_io(bht_dev_ext_t *pdx, sd_data_t *sd_data, + sg_list_t *sg, u32 sg_len) +{ + node_t *node = &pdx->dma_api.dma_node; + + bool ret = FALSE; + + bool dma_64bit = pdx->card.host->bit64_enable ? TRUE : FALSE; + bool data_26bit_len = + pdx->cfg->host_item.test_dma_mode_setting.enable_dma_26bit_len + ? TRUE : FALSE; + + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* 3. get ADMA2 desc buffer */ + ret = + _dma_api_build_adma_io(node, sg, sg_len, dma_64bit, data_26bit_len, + sd_data); + + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "Enter %s ret=%d\n", __func__, ret); + return ret; + +} + +bool dma_api_build_adma_io_add_nop(bht_dev_ext_t *pdx, sd_data_t *sd_data, + sg_list_t *sg, u32 sg_len) +{ + node_t *node = &pdx->dma_api.dma_node; + + bool ret = FALSE; + bool dma_64bit = pdx->card.host->bit64_enable ? TRUE : FALSE; + bool data_26bit_len = + pdx->cfg->host_item.test_dma_mode_setting.enable_dma_26bit_len + ? TRUE : FALSE; + + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + + ret = + _dma_api_build_adma_io_add_nop(node, sg, sg_len, dma_64bit, + data_26bit_len, sd_data); + + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "Enter %s ret=%d\n", __func__, ret); + return ret; + +} + +request_t req; + +bool dma_api_build_adma_sdma_io(bht_dev_ext_t *pdx, sd_data_t *sd_data) +{ + node_t *node = &pdx->dma_api.dma_node; + /* request_t req ; */ + bool ret = FALSE; + + bool dma_64bit = pdx->card.host->bit64_enable ? TRUE : FALSE; + bool data_26bit_len = + pdx->cfg->host_item.test_dma_mode_setting.enable_dma_26bit_len + ? TRUE : FALSE; + + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + /* check buf size */ + ret = adma_sdma_gen_sglist(node, &sd_data->data_mng, &req); + if (ret == FALSE) + goto exit; + /* dump */ + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "dump sg %llx,%x\n", req.srb_sg_list[0].Address, + req.srb_sg_len); + + /* 3. get ADMA2 desc buffer */ + ret = + _dma_api_build_adma_io(node, req.srb_sg_list, req.srb_sg_len, + dma_64bit, data_26bit_len, sd_data); + /* 4. */ + adma_sdma_post_io(&sd_data->data_mng, sd_data->dir, node->data_tbl.va); + +exit: + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "Enter %s ret=%d\n", __func__, ret); + return ret; + +} + +bool dma_api_build_adma_sdma_io_add_nop(bht_dev_ext_t *pdx, + sd_data_t *sd_data) +{ + node_t *node = &pdx->dma_api.dma_node; + /* request_t req ; */ + bool ret = FALSE; + + bool dma_64bit = pdx->card.host->bit64_enable ? TRUE : FALSE; + bool data_26bit_len = + pdx->cfg->host_item.test_dma_mode_setting.enable_dma_26bit_len + ? TRUE : FALSE; + + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + /* check buf size */ + ret = adma_sdma_gen_sglist(node, &sd_data->data_mng, &req); + if (ret == FALSE) + goto exit; + /* dump */ + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "dump sg %llx,%x\n", req.srb_sg_list[0].Address, + req.srb_sg_len); + + /* 3. get ADMA2 desc buffer */ + ret = + _dma_api_build_adma_io_add_nop(node, req.srb_sg_list, + req.srb_sg_len, dma_64bit, + data_26bit_len, sd_data); + /* 4. */ + adma_sdma_post_io(&sd_data->data_mng, sd_data->dir, node->data_tbl.va); + +exit: + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "Enter %s ret=%d\n", __func__, ret); + return ret; + +} + +/* + * only support one data buffer SDMA transfer, PIO like . + */ +bool _sdma_build_io(data_dma_mng_t *mgr, dma_desc_buf_t *dma, u32 sdma_bd_len, + e_data_dir dir, byte *data_buf) +{ + u32 min_size = 0; + + mgr->srb_buffer[0].buff = data_buf; + mgr->offset = 0; + /* fix to 1 */ + mgr->srb_cnt = 1; + + /* system addr */ + mgr->sys_addr = dma->pa; + mgr->driver_buff = (byte *) dma->va; + /* for write data to card,need fill data first before transfer */ + if (dir == DATA_DIR_OUT) { + min_size = os_min(sdma_bd_len, mgr->total_bytess); + os_memcpy(mgr->driver_buff, + mgr->srb_buffer[0].buff + mgr->offset, min_size); + + mgr->offset += min_size; + } + return TRUE; + +} + +bool dma_api_build_sdma_io(bht_dev_ext_t *pdx, sd_data_t *sd_data) +{ + node_t *node = &pdx->dma_api.dma_node; + bool ret = FALSE; + dma_desc_buf_t dma; + data_dma_mng_t *mgr = &sd_data->data_mng; + u32 sdma_bd_len = get_sdma_boudary_size(pdx->cfg); + + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + /* check buf size */ + if (sd_data->data_mng.total_bytess > DMA_API_BUF_SIZE) { + DbgErr("%s data total bytes too large(%x)>(%x)\n", __func__, + sd_data->data_mng.total_bytess, DMA_API_BUF_SIZE); + ret = FALSE; + goto exit; + } + + /* align dma buffer */ +#define SDMA_BOUNDARY_MAX_SIZE (512*1024) + if (sdma_bd_len > SDMA_BOUNDARY_MAX_SIZE) { + DbgErr("%s boundary over max %x\n", __func__, sdma_bd_len); + ret = FALSE; + goto exit; + } else { + dma = node->data_tbl; + if (dma_align(&dma, sdma_bd_len) == FALSE) { + DbgErr("%s align failed\n", __func__); + ret = FALSE; + goto exit; + } + } + + ret = + _sdma_build_io(mgr, &dma, sdma_bd_len, sd_data->dir, + sd_data->data_mng.driver_buff); + +exit: + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "Enter %s ret=%d\n", __func__, ret); + return ret; + +} From patchwork Fri Oct 13 08:33:58 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: liuchang_125125@163.com X-Patchwork-Id: 13420431 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D7E91CDB47E for ; Fri, 13 Oct 2023 08:38:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230081AbjJMIiM (ORCPT ); Fri, 13 Oct 2023 04:38:12 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33936 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229998AbjJMIiL (ORCPT ); Fri, 13 Oct 2023 04:38:11 -0400 Received: from m12.mail.163.com (m12.mail.163.com [220.181.12.215]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 6F3FD91; Fri, 13 Oct 2023 01:38:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:Subject:Date:Message-Id:MIME-Version; bh=fUXBe BkUUytacUqbZ70MwhPYaSLloNFqUREscDPxhHM=; b=jVk1utdfsiL3aSKSnXqaV FXfp0OPTKqlGHYDcHepsFQQW+kXIEnGp2x0NlzzMymYKgXCNRTD4ARcWvezoNGtD HDSbpAIzo57wyC3ywf85ISh9DBppnpL7rxfIlbwEK7+ilbKWJegk2H2246wM7ldI HtfT/XzVVJboal4NoFRmHw= Received: from test-Z390-GAMING-X.bayhubtech.com (unknown [58.48.115.170]) by zwqz-smtp-mta-g3-2 (Coremail) with SMTP id _____wD337z6ACllkPS+AQ--.40632S2; Fri, 13 Oct 2023 16:34:02 +0800 (CST) From: liuchang_125125@163.com To: jejb@linux.ibm.com, martin.petersen@oracle.com, linux-scsi@vger.kernel.org, linux-kernel@vger.kernel.org Cc: mark.tao@bayhubtech.com, shaper.liu@bayhubtech.com, thomas.hu@bayhubtech.com, chevron.li@bayhubtech.com, charl.liu@bayhubtech.com, Charl Liu Subject: [PATCH 5/9] scsi: bht: include: Add the header files related to Bayhub specific struct, enum and macro Date: Fri, 13 Oct 2023 16:33:58 +0800 Message-Id: <20231013083358.10313-1-liuchang_125125@163.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 X-CM-TRANSID: _____wD337z6ACllkPS+AQ--.40632S2 X-Coremail-Antispam: 1Uf129KBjvtXoWkGry7XFWUWr13AFW7GF4DJwb_yoW8Jr13Zw b_uryxZFyDWr1UCFs3KFs8Ary7JFWj9w47Xr18Gw4rKrZakr1ftrs8Aw4rurW3Zr45u3y7 XF98Aw1qyFnaq3W5KjkaLaAFLSUrUUUUUb8apTn2vfkv8UJUUUU8Yxn0WfASr-VFAUDa7- sFnT9fnUUvcSsGvfC2KfnxnUUI43ZEXa7xRMxhL3UUUUU== X-Originating-IP: [58.48.115.170] X-CM-SenderInfo: polxux5dqjsiqsvrjki6rwjhhfrp/xtbB0A0IWWEssysbWQABsa Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org From: Charl Liu 1.basic: define basic types 2.card: define card structure 3.cardapi: include card related API 4.cfgmng: define interfaces for Dynamic configuration 5.cmdhandler: include related macro and declaration about SD CMD 6.debug: define log type mapping for debugging 7.funcapi: include functional API 8.function: define time and power state struct 9.globalcfg: define global configuration macros 10.host: define host structure 11.hostapi: include standard host operation functions 12.hostvenapi: include Bayhub host operation functions 13.osapi: define os independent API 14.reqapi: include the functions for OS entry layer 15.tq: define tag queue related struct 16.tqapi: include tag queue related function 17.transh: define transfer related struct 18.transhapi: include related API about transfer 19.util: include platform independent API Signed-off-by: Charl Liu --- Change in V1: Add the header files related to Bayhub specific struct, enum and macro. --- drivers/scsi/bht/include/basic.h | 395 ++++++++ drivers/scsi/bht/include/card.h | 626 ++++++++++++ drivers/scsi/bht/include/cardapi.h | 85 ++ drivers/scsi/bht/include/cfgmng.h | 1303 +++++++++++++++++++++++++ drivers/scsi/bht/include/cmdhandler.h | 289 ++++++ drivers/scsi/bht/include/debug.h | 169 ++++ drivers/scsi/bht/include/funcapi.h | 80 ++ drivers/scsi/bht/include/function.h | 94 ++ drivers/scsi/bht/include/globalcfg.h | 78 ++ drivers/scsi/bht/include/host.h | 282 ++++++ drivers/scsi/bht/include/hostapi.h | 170 ++++ drivers/scsi/bht/include/hostvenapi.h | 47 + drivers/scsi/bht/include/osapi.h | 201 ++++ drivers/scsi/bht/include/reqapi.h | 80 ++ drivers/scsi/bht/include/tq.h | 159 +++ drivers/scsi/bht/include/tqapi.h | 36 + drivers/scsi/bht/include/transh.h | 132 +++ drivers/scsi/bht/include/transhapi.h | 25 + drivers/scsi/bht/include/util.h | 37 + 19 files changed, 4288 insertions(+) create mode 100644 drivers/scsi/bht/include/basic.h create mode 100644 drivers/scsi/bht/include/card.h create mode 100644 drivers/scsi/bht/include/cardapi.h create mode 100644 drivers/scsi/bht/include/cfgmng.h create mode 100644 drivers/scsi/bht/include/cmdhandler.h create mode 100644 drivers/scsi/bht/include/debug.h create mode 100644 drivers/scsi/bht/include/funcapi.h create mode 100644 drivers/scsi/bht/include/function.h create mode 100644 drivers/scsi/bht/include/globalcfg.h create mode 100644 drivers/scsi/bht/include/host.h create mode 100644 drivers/scsi/bht/include/hostapi.h create mode 100644 drivers/scsi/bht/include/hostvenapi.h create mode 100644 drivers/scsi/bht/include/osapi.h create mode 100644 drivers/scsi/bht/include/reqapi.h create mode 100644 drivers/scsi/bht/include/tq.h create mode 100644 drivers/scsi/bht/include/tqapi.h create mode 100644 drivers/scsi/bht/include/transh.h create mode 100644 drivers/scsi/bht/include/transhapi.h create mode 100644 drivers/scsi/bht/include/util.h diff --git a/drivers/scsi/bht/include/basic.h b/drivers/scsi/bht/include/basic.h new file mode 100644 index 000000000000..e0cdb1620206 --- /dev/null +++ b/drivers/scsi/bht/include/basic.h @@ -0,0 +1,395 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: basic.h + * + * Abstract: This Include file used to define basic types + * + * Version: 1.00 + * + * Author: Peter.Guo + * + * Environment: OS Independent + * + * History: + * + * 8/25/2014 Creation Peter.Guo + */ + +#ifndef _BASIC_H +#define _BASIC_H + +#include "globalcfg.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TRUE 1 +#define FALSE 0 + +#define os_min(x, y) ((x > y) ? y:x) +#define os_max(x, y) ((x > y) ? x:y) +typedef void *PVOID; +typedef u8 byte; +typedef struct scsi_cmnd scsi_srb; +typedef u64 phy_addr_t; + +typedef struct { + u64 Address; + u32 Length; +} ADDRESS_LIST; + +typedef ADDRESS_LIST sg_list_t; + +typedef enum { + /* Sandstorm #0 */ + CHIP_SDS0 = 0, + /* Sandstorm #1 */ + CHIP_SDS1, + /* Fujin2 */ + CHIP_FUJIN2, + /* Seabird */ + CHIP_SEABIRD, + /* SeaEagle */ + CHIP_SEAEAGLE, + /* GG8 */ + CHIP_GG8, + /* SeaEagle2 */ + CHIP_SEAEAGLE2, + CHIP_ALBATROSS, + CHIP_SDS2_SD0, + CHIP_SDS2_SD1, + /* Last generate chip like FJ/RJ, ect. */ + CHIP_OLD, + /* Chip type unknown */ + CHIP_UNKNOWN +} e_chip_type; + +typedef enum { + CARD_NONE = 0, + CARD_ERROR, + CARD_UHS2, + CARD_SD, + CARD_MMC, + CARD_EMMC, + CARD_SDIO, + CARD_SD70, + CARD_TEST +} e_card_type; + +typedef enum { + CARD_SDSC_V1 = 0, + CARD_SDSC_V2V3, + CARD_SDHC, + CARD_SDXC +} e_capacity_type; + +typedef enum { + INF_NONE = 0, + INF_BUILT, + INF_CONTINUE +} e_infinite_mode; + +typedef enum { + TRANS_NONDATA = 0, + TRANS_SDMA, + TRANS_ADMA2, + TRANS_ADMA3, + TRANS_PIO, + TRANS_ADMA2_SDMA_LIKE, +} e_trans_type; + +typedef enum { + DATA_DIR_NONE = 0, + /* Card -> host */ + DATA_DIR_IN, + /* Host -> card */ + DATA_DIR_OUT +} e_data_dir; + +/* + * init request_t structure and + * cmd_index = 0 means don't use cmd_index to generate command + * upper layer need to fill srb_ext_t->req + */ +typedef enum { + REQ_RESULT_PENDING = 0, + REQ_RESULT_OK, + REQ_RESULT_PROTECTED, + REQ_RESULT_OVERSIZE, + REQ_RESULT_QUEUE_BUSY, + REQ_RESULT_INVALID_CMD, + REQ_RESULT_NO_CARD, + REQ_RESULT_ACCESS_ERR, + REQ_RESULT_ABORT +} e_req_result; + +typedef enum { + /* to use Tag queue io */ + REQ_TYPE_TAG_IO = 0, + /* to use general io */ + REQ_TYPE_GEN_IO +} e_req_type; + +typedef struct { + byte *buff; + u32 len; + u32 ofs; +} virt_buffer_t; + +typedef struct { + u32 len; + /* phy address */ + phy_addr_t pa; + /* virtual address */ + void *va; +} dma_desc_buf_t; + +#include "osapi.h" +#include "cfgmng.h" +#include "card.h" +#include "function.h" +#include "tq.h" + +typedef void (*cb_srb_done_t)(void *pdx, void *srb_ext); + +typedef struct { + /* enum RWReq, ioctl */ + e_req_type type; + + /* srb buffer */ + byte *srb_buff; + /* srb buffer sg list */ + sg_list_t srb_sg_list[MAX_SGL_RANGE]; + u32 srb_sg_len; + + bool gg8_ddr200_workaround; + req_card_access_info_t last_req; + + e_data_dir data_dir; + cb_srb_done_t srb_done_cb; + e_req_result result; + + union { + struct { + u32 sec_addr; + u32 sec_cnt; + + /* this indicate whether use cmd_index to generate sd_cmd */ + byte use_cmd; + /* this is valid when use_cmd is 1 */ + byte cmd_index; + } tag_req_t; + + struct { + u32 code; + u32 data_len; + u32 arg1; + u32 arg2; + + } gen_req_t; + }; + +} request_t; + +typedef struct srb_ext_t { + /* t_dev_ext_t pdx; */ + scsi_srb *psrb; + + request_t req; + + /* below item is maintained by card layer, defined here just for apply memory */ + struct srb_ext_t *prev; + sd_command_t cmd; + struct srb_ext_t *next; +} srb_ext_t; + +typedef struct { + byte scsi_eject; + byte sense_key; + byte sense_code; + /* when this bit change from 1->0 or 0->1 then bus change occur */ + byte last_present; + byte own_id; + byte prevent_eject; +} scsi_mng_t; + +typedef void (*cb_soft_intr_t)(void *); +typedef struct { + void *data; + completion_t completion; + cb_soft_intr_t cb_func; + byte enable; +} soft_irq_t; + +typedef struct { + bool enable; + bool use_i2c; + bool timeout; + bool enable_timer_chk; + u32 check_period_ms; + u32 last_check_ms; +} thermal_t; + +typedef struct { + /* Below info is used to check 1MB data transfer */ + u64 tick_start; + u64 tick_thr_start; + u64 tick_io_end; + u64 last_dir; + + u64 avg_start_2_thr; + u64 avg_thr_2_iodone; + u64 avg_iodone_2_next; + u32 io_cnt; + + /* Below info is used to report performance */ + u64 start_io_tick; + u64 io_duration; +} tPerTick; + +typedef struct { + u32 address; + u32 size; +} cprm_t; + +typedef struct { + u32 address; + u32 size; +} nsm_t; + +typedef struct { + /* 0 no test, 1: with card test, 2 host only test */ + byte test_type; + s32 test_id; + u32 test_loop; + s32 test_param1; + s32 test_param2; +} testcase_t; + +typedef struct { + byte pre_cid[16]; + u64 pre_sec_count; + bool s3s4_resume_for_card_init; + bool card_current_present; +} pre_card_info_t; + +typedef struct bht_dev_ext { + nsm_t nsm; + cprm_t cprm; + /* store host related variables */ + sd_host_t host; + /* store card related variables */ + sd_card_t card; + pre_card_info_t pre_card; + /* store os related viraiable thread dpc event and so on */ + os_struct os; + /* store auot function related variables */ + time_struct auto_timer; + /* store dynamic configuratin info */ + cfg_item_t *cfg; + tag_queue_t tag_queue; + dma_trans_api_t dma_api; + pm_state_t pm_state; + dma_desc_buf_t dma_buff; + soft_irq_t soft_irq; + scsi_mng_t scsi; + thermal_t thermal; + /* temporay use variable */ + /* store current srb info for gen io case */ + srb_ext_t *p_srb_ext; + /* bakeup current gen io srb ext for tag/gen parallel */ + srb_ext_t *p_srb_ext_bak; + u32 signature; + /* default is zero, set to 1 to indicate dump mode driver */ + bool dump_mode; + struct device *dev; + tPerTick tick; + bool scsi_init_flag; + req_card_access_info_t last_req; + testcase_t testcase; +} bht_dev_ext_t; + +typedef struct { + bool dump_mode; + /* for dumpmode is the sub value us each time, for normal mode it is start tick */ + u32 tick; + /* for dump mode unit is us; normal mode unit is ms */ + u32 timeout; +} loop_wait_t; + +extern cfg_item_t g_cfg_item[SUPPORT_CHIP_COUNT * 2]; + +/* 1 Bit define */ + +#define BIT0 (0x01) +#define BIT1 (0x02) +#define BIT2 (0x04) +#define BIT3 (0x08) +#define BIT4 (0x10) +#define BIT5 (0x20) +#define BIT6 (0x40) +#define BIT7 (0x80) +#define BIT8 (0x0100) +#define BIT9 (0x0200) +#define BIT10 (0x0400) +#define BIT11 (0x0800) +#define BIT12 (0x1000) +#define BIT13 (0x2000) +#define BIT14 (0x4000) +#define BIT15 (0x8000) +#define BIT16 (0x010000) +#define BIT17 (0x020000) +#define BIT18 (0x040000) +#define BIT19 (0x080000) +#define BIT20 (0x100000) +#define BIT21 (0x200000) +#define BIT22 (0x400000) +#define BIT23 (0x800000) +#define BIT24 (0x01000000) +#define BIT25 (0x02000000) +#define BIT26 (0x04000000) +#define BIT27 (0x08000000) +#define BIT28 (0x10000000) +#define BIT29 (0x20000000) +#define BIT30 (0x40000000) +#define BIT31 (0x80000000) + +#define REGL_INVALID_VAL (0xffffffff) +#define REGW_INVALID_VAL (0xffff) +#define REGB_INVALID_VAL (0xff) + +bool sdhci_irq(void *param); + +#endif diff --git a/drivers/scsi/bht/include/card.h b/drivers/scsi/bht/include/card.h new file mode 100644 index 000000000000..237bec7a4c4e --- /dev/null +++ b/drivers/scsi/bht/include/card.h @@ -0,0 +1,626 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: card.h + * + * Abstract: This Include file define card structure + * + * Version: 1.00 + * + * Author: Peter.Guo + * + * Environment: OS Independent + * + * History: + * + * 8/25/2014 Creation Peter.Guo + */ + +#ifndef _CARD_H + +#define _CARD_H + +#include "host.h" + +typedef struct { + u32 manfid; + byte prod_name[8]; + byte prv; + u32 serial; + u16 oemid; + u16 reserved; + +} cid_t; + +typedef struct { + /* CSD structure */ + byte csd_structure; + /* MMC spec version */ + byte mmc_spec_vers; + /* read data block length */ + byte read_bl_len; + /* device size */ + u32 c_size; + /* c_size_mult */ + u32 c_size_mult; + /* erase sector size */ + u16 sector_size; + /* max.data transfer rate */ + byte tran_speed; + /* taac */ + byte taac; + /* nsac */ + byte nsac; + + /* permanent write protection */ + byte parm_protect; + /* temporary write protection */ + byte temp_protect; + +#if (0) + u16 cmdclass; + u16 tacc_clks; + u32 tacc_ns; + u32 c_size; + u32 r2w_factor; + u32 max_dtr; + /* In sectors */ + u32 erase_size; + u32 read_blkbits; + u32 write_blkbits; + u32 capacity; + u32 read_partial:1, read_misalign:1, write_partial:1, write_misalign:1; + +#endif +} csd_t; + +typedef struct { +#if (0) + byte rev; + byte erase_group_def; + byte sec_feature_support; + byte rel_sectors; + byte rel_param; + byte part_config; + byte cache_ctrl; + byte rst_n_function; + byte max_packed_writes; + byte max_packed_reads; + byte packed_event_en; + /* Units: ms */ + u32 part_time; + /* Units: 100ns */ + u32 sa_timeout; + /* Units: 10ms */ + u32 generic_cmd6_time; + /* Units: ms */ + u32 power_off_longtime; + /* state */ + byte power_off_notification; + u32 hs_max_dtr; +#define MMC_HIGH_26_MAX_DTR 26000000 +#define MMC_HIGH_52_MAX_DTR 52000000 +#define MMC_HIGH_DDR_MAX_DTR 52000000 +#define MMC_HS200_MAX_DTR 200000000 + u32 sectors; + u32 card_type; + /* In sectors */ + u32 hc_erase_size; + /* In milliseconds */ + u32 hc_erase_timeout; + /* Secure trim multiplier */ + u32 sec_trim_mult; + /* Secure erase multiplier */ + u32 sec_erase_mult; + /* In milliseconds */ + u32 trim_timeout; + /* enable bit */ + byte enhanced_area_en; + /* Units: Byte */ + unsigned long long enhanced_area_offset; + /* Units: KB */ + u32 enhanced_area_size; + /* Units: KB */ + u32 cache_size; + /* HPI enablebit */ + byte hpi_en; + /* HPI support bit */ + byte hpi; + /* cmd used as HPI */ + u32 hpi_cmd; + /* background support bit */ + byte bkops; + /* background enable bit */ + byte bkops_en; + /* 512 bytes or 4KB */ + u32 data_sector_size; + /* DATA TAG UNIT size */ + u32 data_tag_unit_size; + /* ro lock support */ + u32 boot_ro_lock; + bool boot_ro_lockable; + /* 54 */ + byte raw_exception_status; + /* 160 */ + byte raw_partition_support; + /* 168 */ + byte raw_rpmb_size_mult; + /* 181 */ + byte raw_erased_mem_count; + /* 194 */ + byte raw_ext_csd_structure; + /* 196 */ + byte raw_card_type; + /* 198 */ + byte out_of_int_time; + /* 200 */ + byte raw_pwr_cl_52_195; + /* 201 */ + byte raw_pwr_cl_26_195; + /* 202 */ + byte raw_pwr_cl_52_360; + /* 203 */ + byte raw_pwr_cl_26_360; + /* 217 */ + byte raw_s_a_timeout; + /* 221 */ + byte raw_hc_erase_gap_size; + /* 223 */ + byte raw_erase_timeout_mult; + /* 224 */ + byte raw_hc_erase_grp_size; + /* 229 */ + byte raw_sec_trim_mult; + /* 230 */ + byte raw_sec_erase_mult; + /* 231 */ + byte raw_sec_feature_support; + /* 232 */ + byte raw_trim_mult; + /* 236 */ + byte raw_pwr_cl_200_195; + /* 237 */ + byte raw_pwr_cl_200_360; + /* 238 */ + byte raw_pwr_cl_ddr_52_195; + /* 239 */ + byte raw_pwr_cl_ddr_52_360; + /* 246 */ + byte raw_bkops_status; + /* 212 - 4 bytes */ + byte raw_sectors[4]; + + u32 feature_support; +#define MMC_DISCARD_FEATURE 1 /* CMD38 feature */ +#endif + + /* 196 */ + u8 card_type; + /* 197 */ + u8 driver_strength_type; + /* 200 */ + u8 pwr_cl_52_195; + /* 201 */ + u8 pwr_cl_26_195; + /* 202 */ + u8 pwr_cl_52_360; + /* 203 */ + u8 pwr_cl_26_360; + /* 238 */ + u8 pwr_cl_ddr_52_195; + /* 239 */ + u8 pwr_cl_ddr_52_360; + /* 212 ~ 215 */ + u32 sec_cnt; +} extcsd_t; + +#define CID_LEN (16) + +#define SD_SCR_BUS_WIDTH_1 (1<<0) +#define SD_SCR_BUS_WIDTH_4 (1<<2) +#define SD_SCR_CMD20_SUPPORT (1<<0) +#define SD_SCR_CMD23_SUPPORT (1<<1) + +typedef struct { + /* SD Spec Version */ + byte sd_spec; + + /* SD Spec Version3 */ + unsigned char sd_spec3; + byte sd_specx; + byte reserved_B0; + byte reserved_B1; + /* CMD Support */ + byte cmd_support; + /* reserved */ + u16 reserved; + +} sd_scr_t; + +typedef struct { + /* Size of AU */ + u32 au_size; + /* Speed Class of the card */ + u32 speed_class; + /* In second */ + u32 erase_timeout; + /* In second */ + u32 erase_offset; +} sd_ssr_t; + +/* Access Mode */ +#define UHS_SDR12_BUS_SPEED (0) +#define HIGH_SPEED_BUS_SPEED (1) +#define UHS_SDR25_BUS_SPEED (1) +#define UHS_SDR50_BUS_SPEED (2) +#define UHS_SDR104_BUS_SPEED (3) +#define UHS_DDR50_BUS_SPEED (4) + +/* Drive Strength */ +#define SD_DRIVER_TYPE_B (1 << 0) +#define SD_DRIVER_TYPE_A (1 << 1) +#define SD_DRIVER_TYPE_C (1 << 2) +#define SD_DRIVER_TYPE_D (1 << 3) + +/* Power Limit */ +#define SD_POWER_LIMIT_200 (1 << 0) +#define SD_POWER_LIMIT_400 (1 << 1) +#define SD_POWER_LIMIT_600 (1 << 2) +#define SD_POWER_LIMIT_800 (1 << 3) +#define SD_POWER_LIMIT_180W (1 << 4) + +#define SD_FNC_CHK 0x00000000UL +#define SD_FNC_SW 0x80000000UL +#define SD_FNC_GET 0x0000000FUL +#define SD_FNC_NOINFL 0x00FFFFFFUL +#define SD_FNC_G1_INFL 0x00FFFFF0UL +#define SD_FNC_G2_INFL 0x00FFFF0FUL +#define SD_FNC_G3_INFL 0x00FFF0FFUL +#define SD_FNC_G4_INFL 0x00FF0FFFUL +#define SD_FNC_G5_INFL 0x00F0FFFFUL +#define SD_FNC_G6_INFL 0x000FFFFFUL +#define SD_FNC_G2_VEN 0x000FFFEFUL +#define SD_FNC_GRP1 1U +#define SD_FNC_GRP2 2U +#define SD_FNC_GRP3 3U +#define SD_FNC_GRP4 4U +#define SD_FNC_GRP5 5U +#define SD_FNC_GRP6 6U + +/* SD 2.0 default speed 25M */ +#define SD_FNC_NO_DS 0x100 +/* SD 2.0 default speed 50M */ +#define SD_FNC_NO_HS 0x101 +#define SD_FNC_NO_0 0x0 +#define SD_FNC_NO_1 0x1 +#define SD_FNC_NO_2 0x2 +#define SD_FNC_NO_3 0x3 +#define SD_FNC_NO_4 0x4 +#define SD_FNC_NO_5 0x5 +#define SD_FNC_NO_6 0x6 +#define SD_FNC_NO_7 0x7 +#define SD_FNC_NO_8 0x8 +#define SD_FNC_NO_9 0x9 +#define SD_FNC_NO_A 0xA +#define SD_FNC_NO_B 0xB +#define SD_FNC_NO_C 0xC +#define SD_FNC_NO_D 0xD +#define SD_FNC_NO_E 0xE +#define SD_FNC_NO_F 0xF + +#define SD_FNC_AM_SDR12 0x0 +#define SD_FNC_AM_SDR25 0x1 +#define SD_FNC_AM_HS 0x1 +#define SD_FNC_AM_DS 0x0 + +#define SD_FNC_AM_SDR50 0x2 +#define SD_FNC_AM_SDR104 0x3 +#define SD_FNC_AM_DDR50 0x4 +#define SD_FNC_AM_DDR200 0x5 + +#define GROUP_FN4_POWERLIMIT_288W_CAP (4) +#define GROUP_FN4_POWERLIMIT_216W_CAP (3) +#define GROUP_FN4_POWERLIMIT_180W_CAP (2) +#define GROUP_FN4_POWERLIMIT_144W_CAP (1) +#define GROUP_FN4_POWERLIMIT_072W_CAP (0) + +#define SD_FNC_PL_288W (3) +#define SD_FNC_PL_216W (2) +#define SD_FNC_PL_180W (4) +#define SD_FNC_PL_144W (1) +#define SD_FNC_PL_072W (0) + +typedef struct { + byte sd_access_mode; + byte sd_command_system; + byte sd_drv_type; + byte sd_power_limit; +} sd_sw_func_t; + +typedef struct { + /* device-specify number of DIR LSS */ + byte n_lss_dir:4, + /* device-specify number of DIR SYN */ + n_lss_syn:4; + /* max block number in a flow control unit */ + byte n_fcu; + /* number of DIDL between DATA packets */ + byte n_data_gap; + /* Device Range support */ + byte speed_range:2, + /* device support hibernate or not */ + hibernate:1, + /* number of lanes */ + lanes:4, + /* device support power mode */ + pwr_mode:1; + /* card max block length */ + u16 max_blk_len:12, + /* card retry cnt setting */ + retry_cnt:2, half_supp:1, reserved:1; +} uhs2_info_t; + +#define MID_SANDISK (0x3) + +typedef struct { + + u16 rca; + + byte raw_cid[16]; + byte raw_csd[16]; + csd_t csd; + cid_t cid; + + /* for SD only */ + byte raw_scr[8]; + /* for SD only */ + byte raw_ssr[64]; + /* sd_ssr_t ssr; */ + sd_scr_t scr; + + /* + * Card Capacity Status (ACMD41 response). + * 0: SDSC; 1: SDHC or SDXC. + * This flag also can be used for MMC + * (OCR Access mode is sector mode or not) + */ + byte card_ccs; + /* S18A (ACMD41 response) Switching to 1.8v Accepted */ + byte card_s18a; + + /* work at DDR mode */ + byte ddr_flag; + + /* Indicate which Io signal the card work ok */ + byte io_signal_vol; +#define CARD_IO_VDD_33V 0 +#define CARD_IO_VDD_18V 1 +#define CARD_IO_VDD_12V 2 + + sd_sw_func_t sw_func_cap; + /* + * current settings, + * especially access mode should not > target_access_mode and sw_func_cap. + */ + sd_sw_func_t sw_cur_setting; + +} card_info_t; + +/* + * SCR field definitions + */ +#define SCR_SPEC_VER_0 0 /* Version 1.0 and 1.01 */ +#define SCR_SPEC_VER_1 1 /* Version 1.10 */ +#define SCR_SPEC_VER_2 2 /* Version 2.00 or Version 3.00 */ + +typedef struct { + /* UHS2 Device Id; */ + byte dev_id; + + uhs2_info_t uhs2_cap; + uhs2_info_t uhs2_setting; +} uhs2_card_info_t; + +typedef struct { + u32 part_capacity; + u32 part_idx; + bool write_protected; +} mmc_part_info_t; + +#define EMMC_MODE_NONE 0 +#define EMMC_MODE_HS200 1 +#define EMMC_MODE_HS400 2 + +#define EMMC_1Bit_BUSWIDTH 0 +#define EMMC_4Bit_BUSWIDTH 1 +#define EMMC_8Bit_BUSWIDTH 2 + +typedef struct { + byte raw_extcsd[512]; + extcsd_t ext_csd; +#if (0) + u32 partnum; + mmc_part_info_t part_info[MAX_EMMC_PARTION]; +#endif + byte cur_hs_type; + /* 0: 1-bit 1: 4-bit 2: 8-bit */ + byte cur_buswidth; + byte drv_strength; +} mmc_card_info_t; + +typedef enum { + /* Card not present, or card initialize not start/finished, or card init failed */ + CARD_STATE_POWEROFF = 0, + CARD_STATE_SLEEP, + CARD_STATE_DEEP_SLEEP, + /* set to this state when card can read write */ + CARD_STATE_WORKING, +} card_pm_state_t; + +typedef struct { + card_info_t info; + mmc_card_info_t mmc; + uhs2_card_info_t uhs2_info; + + e_card_type card_type; + /* pointer to sd_host structure */ + sd_host_t *host; + + /* Infinite transfer built or not */ + byte has_built_inf; + e_data_dir last_dir; + u32 last_sect; + byte inf_trans_enable; + /* This is used for case which set accroding to card CID */ + u32 quirk; + + /* Indicate card was initialized successfully once. */ + bool initialized_once; + bool quick_init; + + bool thread_init_card_flag; + /* this flag indicate whether card exist or not */ + bool card_present; + bool sw_ctrl_swicth_to_express; + card_pm_state_t state; + /* this flag indicated whether card is locked or not */ + bool locked; + + /* Card specific information */ + /* Card Capacity */ + u64 sec_count; + bool write_protected; + + /* to indicate whether card is changed */ + bool card_chg; + /* + * This flag is used to store card last taraget setting according to + * register and capability(not include thermal) + */ + sd_sw_func_t sw_target_setting; + + /* below 2 field is used for degrade and error recovery it is set by degrade policy */ + /* 0 means not degrade */ + u8 degrade_uhs2_range:1, + /* 0 means not degrade */ + degrade_uhs2_half:1, + /* 1 means degrade to legacy */ + degrade_uhs2_legacy:1, + /* 1 means degrade to last mode, if init still failed set error flag of card */ + degrade_final:1; + /* the level is a index add to card start of base freqncy table */ + u16 degrade_freq_level; + + /* + * below item is used for thremal control, it is set by + * thermal control function and used by card init and apis + */ + + /* 1 means card_init need to set card last mode according to this info */ + u16 thermal_enable:1, + /* 1 means RangeB, 0 means RangeA */ + thermal_uhs2_range:1, + /* 0 means use Half, 1 means not use Half */ + thermal_uhs2_half_dis:1, + /* 1 means low power, 0 means normal */ + thermal_uhs2_lpm:1, + /* 1 means change to higher work mode, 0 means change to lower work mode */ + thermal_heat:1, thermal_access_mode:5, thermal_power_limit:5; + + /* gg8 */ + bool uhs2_card; + bool card_support_pcie; + bool card_support_vdd3; + bool pcie_init_flag; + bool uhs2_trail_run; + bool check_result; + bool cmd_check_uhs2_flag; + bool read_signal_block_flag; + bool restore_tuning_content_fail; + bool cmd_low_reset_flag; + bool ddr225_card_flag; + /* Error recover counter */ + u32 continue_init_fail_cnt; + u32 adma_err_cnt; + u32 continue_rw_err_cnt; + host_cmd_req_t cmd_req; + + /* output tuning */ + u8 input_phase_all_pass; + u8 retry_output_fail_phase; + u8 output_input_phase_pair[14]; +} sd_card_t; + +typedef struct { + byte *driver_buff; + /* this is used for dma case, for pio it is not used */ + virt_buffer_t srb_buffer[MAX_WORK_QUEUE_SIZE]; + u32 total_bytess; + u32 offset; + u32 srb_cnt; + phy_addr_t sys_addr; +/* this is used for sdma(like) & pio */ +} data_dma_mng_t; + +typedef struct { + e_data_dir dir; + u32 block_size; + u32 block_cnt; + data_dma_mng_t data_mng; +} sd_data_t; + +typedef struct { + u32 argument; + int payload_cnt; + u32 uhs2_header; + byte cmd_index; + /* set array to 2 for legacy acmd */ + host_trans_reg_t trans_reg[2]; + byte trans_reg_cnt; + + /* + * for R2 Resp the byte order is byte[0]:127:120 byte[1]:119:112 ... ; + * for other is byte[0] is 15:8 ,byte[1]: 23:16... + */ + u32 response[4]; + + sd_data_t *data; + + byte app_cmd:1, + uhs2_cmd:1, muldat_cmd:1, sd_cmd:1, uhs2_set_pld:1, hw_resp_chk:1; + u32 cmd_flag; + + /* this flag is used to return whether command complete is occur */ + byte cmd_done:1, + /* this flag is used to indicate whether uhs2 command is nack */ + uhs2_nack:1; + + cmd_err_t err; + u32 timeout; + bool gg8_ddr200_workaround; + +} sd_command_t; + +void host_init_400k_clock(sd_host_t *host); +void host_set_vdd2_power(sd_host_t *host, bool on, u32 vol_sel); +void host_internal_clk_setup(sd_host_t *host, bool on); +bool host_get_vdd1_state(sd_host_t *host); +void host_set_vdd1_power(sd_host_t *host, bool on, u32 vol_sel); +void host_enable_clock(sd_host_t *host, bool on); + +/* uniformed interface for vdd1,2,3 power set, controlled by registry */ +void host_set_vddx_power(sd_host_t *host, u8 vddx, bool on); + +bool sd_send_if_cond(sd_card_t *card, sd_command_t *sd_cmd, u32 argument); + +bool card_init_ready(sd_card_t *card, sd_command_t *sd_cmd, bool flag_f8); + +inline bool uhs1_support(sd_host_t *host); +bool send_acmd(sd_card_t *card, sd_command_t *sd_cmd); + +bool pcie_mode_init(sd_card_t *card, bool code_flag); +bool gg8_uhs1_init(sd_card_t *card); +bool gg8_uhs2_init(sd_card_t *card); +bool gg8_get_card_capability_flag(sd_card_t *card, bool flag); +#endif diff --git a/drivers/scsi/bht/include/cardapi.h b/drivers/scsi/bht/include/cardapi.h new file mode 100644 index 000000000000..5265ce621242 --- /dev/null +++ b/drivers/scsi/bht/include/cardapi.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: cardapi.h + * + * Abstract: Include card related api definitions + * + * Version: 1.00 + * + * Author: Samuel + * + * Environment: OS Independent + * + * History: + * + * 9/2/2014 Creation Samuel + */ + +#include "basic.h" + +bool card_send_sdcmd(sd_card_t *card, + sd_command_t *sd_cmd, + byte cmd_index, + u32 argument, + u32 cmdflag, e_data_dir dir, byte *data, u32 datalen); +bool card_send_sdcmd_timeout(sd_card_t *card, + sd_command_t *sd_cmd, + byte cmd_index, + u32 argument, + u32 cmdflag, + e_data_dir dir, + byte *data, u32 datalen, u32 timeout); +/* + * init card control info, such as degrade info + * bind card to host + * and memset function + */ +void card_stuct_init(bht_dev_ext_t *pdev_ext); + +/* + * this function is called by card remvoe and enter pm + * this function will only clear software flag + */ +void card_stuct_uinit(sd_card_t *card); + +bool gg8_sd70_card_init(sd_card_t *card); + +bool card_init(sd_card_t *card, int retry_num, bool bfullreset); + +bool card_init_stage2(sd_card_t *card); + +void card_power_off(sd_card_t *card, bool directly); + +bool card_is_poweroff(sd_card_t *card); + +bool card_thermal_control(sd_card_t *card); + +bool card_stop_infinite(sd_card_t *card, bool recover, sd_command_t *pcmd); +bool card_enter_sleep(sd_card_t *card, bool recover, bool deepslp); +bool card_resume_sleep(sd_card_t *card, bool recover); + +/* + * Error recover will return ok or not + * if err_recovery need to do power off retry, it will set init card event + */ + +bool card_piorw_data(sd_card_t *card, u32 sec_addr, u32 sec_cnt, + e_data_dir dir, byte *buff); + +e_req_result card_recovery_flow(sd_card_t *card, sd_command_t *sd_cmd); + +bool card_set_blkcnt(sd_card_t *card, sd_command_t *sd_cmd, u32 blkcnt); + +bool card_dma_rw_data(sd_card_t *card, u32 dma_mode, u32 sec_addr, u32 sec_cnt, + e_data_dir dir, byte *data, sg_list_t *sglist, + u32 sg_len, sd_command_t *cmd_err); + +bool card_adma2_rw_inf(sd_card_t *card, u32 sec_addr, u32 sec_cnt, + e_data_dir dir, sg_list_t *sglist, u32 sg_len, + sd_command_t *cmd_err); + +bool card_read_csd(sd_card_t *card, byte *data); + +bool card_program_csd(sd_card_t *card, byte *data); diff --git a/drivers/scsi/bht/include/cfgmng.h b/drivers/scsi/bht/include/cfgmng.h new file mode 100644 index 000000000000..97ee3472f927 --- /dev/null +++ b/drivers/scsi/bht/include/cfgmng.h @@ -0,0 +1,1303 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: cfgmng.h + * + * Abstract: This File is used to define interface for Dynamic configuration + * + * Version: 1.00 + * + * Author: Amma.Li + * + * Environment: OS Independent + * + * History: + * + * 8/25/2014 Creation Amma.Li + */ + +#ifndef _CFGMNG_H +#define _CFGMNG_H + +/* not contain the subitem "Parameters" */ +#define REGISTRY_KEY_NUM (100) + +#define MAX_FREQ_SUPP 34 + +#define CFG_TRANS_MODE_SDMA 0 +#define CFG_TRANS_MODE_ADMA2 1 +#define CFG_TRANS_MODE_ADMA3 2 +#define CFG_TRANS_MODE_ADMA2_SDMA_LIKE 3 +#define CFG_TRANS_MODE_ADMA3_SDMA_LIKE 4 +#define CFG_TRANS_MODE_ADMA_MIX 5 +#define CFG_TRANS_MODE_ADMA_MIX_SDMA_LIKE 6 +#define CFG_TRANS_MODE_ADMA2_ONLY 7 +#define CFG_TRANS_MODE_ADMA2_ONLY_SDMA_LIKE 8 + +#define CFG_TRANS_MODE_PIO 0xF + +typedef struct { + /* output tuning SDR104 mode fixed value. */ + u32 fixed_value_sdr104:4; + /* output tuning SDR50 mode fixed value. */ + u32 fixed_value_sdr50:4; + u32 sdr50_input_fix_phase_value:4; + /* output tuning DDR200 mode fixed value. */ + u32 fixed_value_ddr200:4; + /* output tuning SDR104 mode enable flag: 1 means enable, 0 means disable(default) . */ + u32 enable_sdr104:1; + /* output tuning SDR50 mode enable flag: 1 means enable, 0 means disable(default) */ + u32 enable_sdr50:1; + + u32 resrv:1; + /* output tuning DDR200 mode enable flag: 1 means enable, 0 means disable(default) . */ + u32 enable_ddr200:1; + /* + * select output tuning Auto mode or fixed mode: + * 1 means Auto mode, 0 means fixed mode(default). + */ + u32 auto_or_fixed:1; + /* + * select output tuning Auto mode or fixed mode: + * 1 means Auto mode, 0 means fixed mode(default). + */ + u32 enable_dll_divider:1; + /* + * output tuning eMMC HS400 mode enable flag: + * 1 means enable, 0 means disable(default). + */ + u32 enable_emmc_hs400:1; + /* output tuning eMMC HS400 mode fixed value. */ + u32 fixed_value_emmc_hs400:4; + u32 sdr104_input_fix_phase_value:4; + /* + * output tuning function Global enable flag: + * 1 means enable, 0 means disable(default) + */ + u32 enable_dll:1; +} cfg_output_tuning_item_t; + +typedef struct { + /* + * 0: SDMA, 1: ADMA2, 2: ADMA3, 3: ADMA2_SDMA_Like, + * 4: ADMA3_SDMA_Like, 0xF: PIO, other: Reserved + */ + u32 dma_mode:4; + + /* Enable dma 26bit data length, 0: Disable, 1: Enable */ + u32 enable_dma_26bit_len:1; + /* Enable dma 64bit address 0: Disable, 1: Enable */ + u32 enable_dma_64bit_address:1; + /* enable 32bit block count 0: Disable, 1: Enable */ + u32 enable_dma_32bit_blkcount:1; + u32 reserved_1:24; + /* dma transfer mode setting valid, 0: invalid 1: valid */ + u32 reserve:1; +} cfg_dma_mode_setting_t; + +typedef struct { + /* enable SD3.0 card infinite transfer mode 0: disable, 1: enable */ + u32 enable_legacy_inf:1; + /* enable SD4.0 card infinite transfer mode 0: disable, 1: enable */ + u32 enable_sd40_inf:1; + /* enable MMC card infinite transfer mode 0: disable, 1: enable */ + u32 enable_mmc_inf:1; + /* enable eMMC card infinite transfer mode 0: disable, 1: enable */ + u32 enable_emmc_inf:1; + u32 reserved_1:27; + /* infinite transfer mode main switch 0: disable, 1: enable */ + u32 enable_inf:1; +} cfg_infinite_transfer_mode_t; + +typedef struct { + /* 0: disable 1: enable */ + u32 enable_cmdio_packet_test:1; + /* ADMA descriptor item length test 0: Disable, 1: Enable */ + u32 enable_adma_descitem_test:1; + u32 reserved_1:29; + /* enable ADMA transfer test, 0: disable 1: enable */ + u32 enable_adma_test:1; +} cfg_adma_test_setting_t; + +typedef struct { + /* unit: Kb, 0x4 = 4KB, 0x400 = 1MB */ + u32 value:16; + u32 reserved_1:15; + /* + * nable SDMA boundary length Setting + * 0: use the default boundary length + * 1: enable SDMA boundary length Setting + */ + u32 reserve:1; +} cfg_sdma_boundary_t; + +typedef struct { + /* Enable SDMA 4-bytes test mode 0: disable, 1: enable */ + u32 enable_sdma_4bit_test:1; + /* disable boundary alignment buffer for SDMA 0: enable, 1: disable */ + u32 dis_sdma_buffer_alignment:1; + u32 reserved_1:29; + /* enable sdma transfer test, 0: disable 1: enable */ + u32 enable_sdma_test:1; +} cfg_sdma_test_setting_t; + +typedef struct { + /* DMA buffer size ,(unit: 64kb) */ + u32 dma_buffer_size:16; + /* (unit: KB) */ + u32 dma_buffer_align:4; + u32 reserved_1:11; + /* + * enable set DMA buffer size + * 0: use the default buffer size (1MB), 1: enable set new buffer size + */ + u32 reserve:1; +} cfg_dma_buffer_size_t; + +typedef struct { + /* max transfer length value (unit: 512byte) */ + u32 value:16; + u32 reserved_1:15; + /* + * enable set max transfer length + * 0: use the default max transfer length (1MB) 1: enable set + */ + u32 reserve:1; +} cfg_max_trans_len_t; + +typedef struct { + /* max number of SRB ADMA2: set 1 SRBs 0: means 16 SRBs(default) */ + u32 max_srb:16; + /* 0: disable 1: enable */ + u32 enable_srb_merge:1; + /* 0: disable 1: enable */ + u32 reserved_1:14; + /* tag queue settings valid 0: invalid 1: valid */ + u32 reserve:1; +} cfg_tag_queue_capability_t; + +typedef struct { + /* [0] reserved */ + u32 resevred:1; + /* [1] disable SD 4.0 card mode, 0: enable 1: disable */ + u32 dis_sd40_card:1; + /* [2] disable SD 3.0 card mode, 0: enable 1: disable */ + u32 dis_sd30_card:1; + /* [3] reserved */ + u32 reserve_3:1; + /* [4] disable SD 7.0 card mode, 0: enable 1: disable */ + u32 dis_sd70_card:1; + /* [6:5] reserved */ + u32 reserve_2:2; + /* [30:7] reserved */ + u32 reserve_1:24; + /* [31]SD card mode disable settings valid, 0: invalid 1: valid */ + u32 sd_mode_dis_enable:1; +} cfg_sd_card_mode_dis_t; + +#define CFG_ACCESS_MODE_SDR12 0 +#define CFG_ACCESS_MODE_SDR25 1 +#define CFG_ACCESS_MODE_SDR50 2 +#define CFG_ACCESS_MODE_SDR104 3 +#define CFG_ACCESS_MODE_DDR50 4 + +#define CFG_DRIVER_TYPE_A 1 +#define CFG_DRIVER_TYPE_B 0 +#define CFG_DRIVER_TYPE_C 2 +#define CFG_DRIVER_TYPE_D 3 +typedef struct { + /* Driver strength select, 0h: Type B, 1h: Type A, 2h: Type C, 3h: Type D. Default is 0h. */ + u32 value:8; + u32 reserved_1:23; + /* enable driver strength select, 0: disable 1: enable */ + u32 enable_set:1; +} cfg_driver_strength_sel_t; +#define CFG_POWER_LIMIT_72 0 +#define CFG_POWER_LIMIT_144 1 +#define CFG_POWER_LIMIT_180 2 +#define CFG_POWER_LIMIT_216 3 +#define CFG_POWER_LIMIT_288 4 + +#define CFG_TUNING_MODE_SW 0 +#define CFG_TUNING_MODE_HW 1 + +typedef struct { + u32 reserved_1:31; + /* Disable MMC function. 0: enable 1: disable */ + u32 dis_mmc_func:1; +} cfg_mmc_mode_dis_t; + +typedef struct { + /* [0] eMMC enable DDR mode, 0: disable, 1: enable (Default: 0) */ + u32 enable_ddr_mode:1; + /* [1]eMMC card use 1.8V Vccq 0: no use 1: use */ + u32 enable_18_vccq:1; + /* [2]eMMC disable high speed, 0: Don't disable, 1: Disable (Default: 0) */ + u32 dis_hs:1; + /* [3]eMMC disable 4-bit bus, 0: Don't disable, 1: Disable (Default: 0) */ + u32 dis_4bit_bus_width:1; + /* [4]eMMC disable 8-bit bus, 0: Don't disable, 1: Disable (Default: 0) */ + u32 dis_8bit_bus_width:1; + /* + * [5]switch eMMC to hs200 mode force or automatically + * 0: automatically switch 1: force switch + */ + u32 enable_force_hs200:1; + /* + * [6]switch eMMC to hs400 mode force or automatically + * 0: automatically switch 1: force switch + */ + u32 enable_force_hs400:1; + /* [7]eMMC card use 1.2V Vccq 0: no use 1: use */ + u32 enable_12_vccq:1; + /* [8]eMMC card use 1.8V Vcc 0: use 3.3v Vcc 1: use 1.8v Vcc */ + u32 enable_18_vcc:1; + /* + * [12:9]Set the driver strength for emmc hs400/hs200 mode + * (0: Type0,1: Type1,2: Type2,3: Type3,4: Type4) + */ + u32 drv_strength:4; + /* + * [13]force switch HS timing no matter whether + * the card support HS200/HS400 + * 0: disable 1: enable + */ + u32 enable_force_hs:1; + /* [14:30] */ + u32 reserved_1:17; + /* [31] force eMMC work flow 0: auto init work flow 1: force eMMC work flow */ + u32 emmc_enable:1; +} cfg_emmc_mode_t; + +typedef struct { + /* [2:0] */ + u32 switch_method_ctrl:3; + /* [3] SW_CTL signal polarity selection */ + u32 sw_ctrl_polarit:1; + /* [4] camera mode enable/disable control */ + u32 camera_mode_enable:1; + /* [5] */ + u32 sd_cmd_low_function_en:1; + /* [6] */ + u32 vdd3_control:1; + /* [7] 0:SD CMD8, 1:Trail run */ + u32 sd70_trail_run:1; + /* [8] 0:Shift byte disable, 1:Shift byte enable */ + u32 shift_byte_en:1; + /* [9] select card flow 0: sd7.0->uhs2->uhs1 1:uhs2->sd7.0->uhs1 */ + u32 card_init_flow_select:1; + /* [31:10] */ + u32 reserve:22; +} cfg_sd7_sdmode_switch_control_t; + +#define CFG_UHS2_FULL_MODE 0 +#define CFG_UHS2_HALF_MODE 1 +#define CFG_UHS2_FAST_MODE 0 +#define CFG_UHS2_LOW_POWER 1 +#define CFG_UHS2_RANGE_A 0 +#define CFG_UHS2_RANGE_B 1 + +typedef struct { + /* Min N_LSS_SYN select. 0h: 4*16 LSS, 1h...Fh: 4*(1...15) LSS */ + u32 min_lss_syn:4; + /* Min N_LSS_DIR select. 0h: 8*16 LSS, 1h...Fh: 8*(1...15) LSS */ + u32 min_lss_dir:4; + /* Min N_DATA_GAP select. 00h: Reserved 01h: 1 LSS, FFh: 255 LSS */ + u32 min_data_gap_sel:8; + /* Max N_FCU select. 00h: 256 blocks, FFh: 255 blocks */ + u32 max_nfcn_sel:8; + /* + * Use the registry value or use the driver to decide the host capability of the NFCU value. + * 0 means to use the driver, 1 means to use the registry value + */ + u32 reserve_nfcu:1; + /* N_LSS_SYN/N_LSS_DIR/N_DATA_GAP setting mode, 0: Spec mode, 1: Registry priority mode */ + u32 reserve_syn_dir_gap:1; + /* 0x9ch[15]:Half/Full Selection For DCMD: 0: Full/ 1:Half */ + u32 half_full_sel:1; + /* 0x140h[0]:Power Mode: 0: Fast Mode/ 1: Low Power Mode */ + u32 fast_low_pwr_sel:1; + /* 0x144h[7:6]: Max Speed Range: 0:Rang A/ 1:Rang B */ + u32 max_speed_range_sel:1; + u32 l1_requirement_source:2; + /* uhs2 setting valid, 0: invalid 1:valid */ + u32 reserve:1; +} cfg_uhs2_setting_t; + +typedef struct { + /* + * power off vdd1 from UHSII to UHSI switch. + * 1 means enable this function, 0 means disable this function. + */ + u32 enable_power_off_vdd1:1; + /* + * The driver will use fullreset reinit instead power cycle reinit when + * Init UHS2 first time failed. 1:enable, 0:disable + */ + u32 enable_full_reset_reinit:1; + /* Enable disable internal clock when go dormant 0:Disable 1:Enable */ + u32 enable_internal_clk_dormant:1; + /* 1: Disable, 0: Enable */ + u32 disable_scramb_mode:1; + u32 reserved_1:27; + /* UHS2 setting valid, 0: invalid 1:valid */ + u32 reserve:1; +} cfg_uhs2_setting2_t; + +#define CFG_UHS2_TEST_BLOCK 0 +#define CFG_UHS2_TEST_BYTE 1 +typedef struct { + /* PIO & DMA DCMD test option */ + u32 data_dcmd_test_option:1; + /* Byte or block mode, 0h: Block mode, 1h: Byte mode */ + u32 byte_block_sel:1; + /* UHS2 initialize loop test, 0h: Disable, 1h: Enable */ + u32 enable_uhs2_init_loop:1; + /* UHS2 mode auto test, 0h: Disable, 1h: Enable */ + u32 enable_uhs2_auto_test:1; + /* Dormant stress test, 0: Disable, 1: Enable */ + u32 enable_dormant_test:1; + /* enable GPIO test for UHS2 (PCR 0xd0 0xd4) 0: disable 1: enable */ + u32 enable_gpio_test:1; + u32 reserved_1:25; + /* enable UHS2 card test, 0: disable 1: enable */ + u32 enable_uhs2_card_test:1; +} cfg_uhs2_card_test_item_t; + +#define CFG_DCM_LOW_MODE 0 +#define CFG_DCM_HIGH_MODE 1 + +typedef struct { + /* DM/DN/Divider settings in registry 0: Low mode, >0: High mode */ + u32 value:1; + u32 reserved_1:30; + /* set DM/DN for FPGA chip valid, 0: invalid 1: valid */ + u32 reserve:1; +} cfg_fpga_dcm_mode_t; + +typedef struct { + u32 value:31; + u32 enable_set:1; +} cfg_clk_driver_strength_33v_t; + +typedef struct { + u32 value:31; + u32 enable_set:1; +} cfg_data_driver_strength_33v_t; + +typedef struct { + u32 value:31; + u32 enable_set:1; +} cfg_data_driver_strength_18v_t; + +typedef struct { + u32 value:31; + u32 enable_set:1; +} cfg_clk_driver_strength_18v_t; + +typedef struct { + /* auto power off time. unit second (Default: 10) */ + u32 time_s:31; + /* Enable function, 0: Disable, 1: Enable (Default: 1) */ + u32 reserve:1; +} cfg_auto_poweroff_time_t; + +typedef struct { + /* The time to enter sleep after the card power off */ + u32 time_ms:8; + u32 reserved_1:23; + /* 0: Disable 1: Enable */ + u32 enable_sleep_func:1; +} cfg_auto_sleep_t; + +typedef struct { + /* + * The time interval from read/write request complete + * to stop the infinite transfer. Unit: ms + */ + u32 time_ms:31; + /* enable/disable stop infinite transfer timer 0: disable 1: enable */ + u32 enable:1; +} cfg_stop_inf_timer_t; + +typedef struct { + /* + * The time to send the go dormant command after R/W SRB finished, + * The unit is millisecond. + */ + u32 time_ms:30; + /* 1: means to use hibernate command, 0 means to use the dormant command. */ + u32 enable_hbr:1; + /* 0: Disable 1: Enable */ + u32 enable_dmt_func:1; +} cfg_auto_uhs2dmt_timer_t; + +typedef struct { + /* + * the delay time before check data[0] line state after + * inserting card in the card initialization (unit: 100us) + */ + u32 delay_us:8; + u32 reserved_1:23; + /* + * enable check data[0] line state at the beginning of inserting card + * 0: disable 1: enable + */ + u32 enable_set:1; +} cfg_check_data0_line_t; + +typedef struct { + u32 reserved_1:31; + /* + * enable/disable the function to + * backup/restore PCI bug fix register values. + * 1: enable, 0: disable + */ + u32 enable_save_pci_register:1; +} cfg_pci_bus_driver_bug_fix_t; + +typedef struct { + /* + * part A registers values backup and restore enable bit at D3/resume. + * 1:enable the function, 0 : disable + */ + u32 enable_bak_parta:1; + /* + * part B registers values backup and restore enable bit at D3/resume.. + * 1:enable the function 0 : disable + */ + u32 enable_bak_partb:1; + /* + * Enable/disable the registry to + * backup/restore for PCI bug fix/PartA/PartB. + * 1: enable, 0: disable + */ + u32 enable_registry_bak_setting:1; + u32 reserved_1:28; + /* part a part b recover setting valid, 0: invalid 1: valid */ + u32 reserve:1; +} cfg_pci_register_backup_t; + +typedef struct { + /* max snoop latency value equals PCR 0x234[9:0] */ + u32 max_snoop_val:10; + /* max snoop latency scale value equals PCR 0x234[12:10] */ + u32 max_snoop_scale:3; + u32 reserved_1:3; + /* max no-snoop latency value equals PCR 0x234[25:16] */ + u32 max_no_snoop_val:10; + /* max no-snoop latency scale value equals PCR 0x234[28:26] */ + u32 max_no_snoop_scale:3; + u32 reserved_2:3; +} cfg_bios_snoop_t; + +#define CFG_ENABLE_PCI_L12 1 +#define CFG_ENABLE_PCI_L11 2 +#define CFG_ENABLE_ASPM_L12 4 +#define CFG_ENABLE_ASPM_L11 8 +typedef struct { + /* + * The value equals PCR 0x248[3:0] (ASPM L1.1:0x248[3] + * ASPM L1.2: 0x248[2] PCI-PM L1.1:0x248[1] PCI-PM L1.2: 0x248[0]) + */ + u32 pm_mode_sel:4; + /* The value equals the PCR 0x248[25:16]. The LTR L1.2 threshold value */ + u32 l12_threshold_val:10; + /* The value equals the PCR 0x248[31:29]. The LTR L1.2 threshold scale */ + u32 l12_threshold_scale:3; + /* + * value equals PCR 0x3E8[31]: nonsnoop latency + * requirement bit for slow LTR + * (1. requirement 0: no requirement) + */ + u32 enable_req_slow_ltr:1; + /* + * value equals PCR 0x3Ec[31]: nonsnoop latency + * requirement bit for fast LTR + * (1. requirement 0: no requirement) + */ + u32 enable_req_fast_ltr:1; + /* + * Enable/disable the function to set the pm_mode_sel value to PCR 0x248. + * (0 means disable, 1 means enable) + */ + u32 enable_pm_mode_sel:1; + u32 reversed_1:11; + /* value equals bit[28] of PCR 0x3E0,disable or enable L1 substate */ + u32 dis_l1_substate:1; +} cfg_bios_l1_substate_t; + +typedef struct { + u32 pcr_ctx_en:1; + u32 new_flow_en:1; + u32 reversed:28; + u32 sw_en:1; + u32 hw_sw_control_sel:1; +} cfg_d3silence_t; + +typedef struct { + /* The value equals the PCR 0x74[7:0]: EP_NFTS_Value, NFTS value, default: 8'h10 */ + u32 ep_nfts_value:8; + /* The value equals PCR 0xf4[21:19], the ASPM L0s Exit Latency. The default value is 3. */ + u32 aspm_l0s_exit_latency:3; + /* The value equals PCR A8h[10],enable or disable LTR mechanism. */ + u32 enable_ltr_mechanism:1; + /* + * The value equals PCR 0xdc[13], enable or disable LED output or not. + * 1: Turn off MMI LED 0: Turn on + */ + u32 dis_mmi_led_pwroff:1; + /* disable RTD3 function, the value equals PCR 0x3e0[29] (1'b1: disable 1'b0: enable) */ + u32 dis_rtd3:1; + /* + * value equals PCR 0xf0[0]. Insert card and power on, it is HPA mode, + * pull out card, it is LPB mode, default 1'b1 + */ + u32 aspml1_mode_hpab:1; + /* + * The value will be set to the PCR 0xf0[5] in FJ2/Seabird and + * PCR 0xf0[1] in SeaEagle A0/A1. + */ + u32 aspml1_mode_lpb:1; + /* value equals PCR 0x90[8], enable clock power management. */ + u32 enable_clock_power_mng:1; + /* The value equals PCR 0x90[1:0], enable or disable ASPM L0s&L1. */ + u32 enable_aspm_mode:2; + u32 reserved_1:12; + /* + * Enable Use the driver to set the class A of the BIOS setting registers. + * 1 means use the driver, 0 means don't use. + */ + u32 enable_bios_part_a:1; + cfg_bios_snoop_t bios_snoop; +} cfg_bios_part_a_t; + +typedef struct { + /* + * The value equals the PCR 0xe0[31:28]: PCI-PM L1 Entrance Timer + * (0: 2us 1: 4us 2: 8us) + */ + u32 enter_pcil1_delay_us:4; + /* + * The value equals the PCR 0xfc[19:16]: ASPM L1 Entrance Timer + * (0: 2us 1: 4us 2: 8us) + */ + u32 enter_aspml1_delay_us:4; + /* + * disable use the default max payload size + * 0: use default max payload size (128bytes) + * 1: use the function to get the max payload size + */ + u32 dis_default_payload:1; + u32 reserved_1:22; + /* + * Use the driver to set the class B of the BIOS setting registers. + * 1 means use the driver, 0 means don't use. + */ + u32 enable_set_part_b:1; +} cfg_bios_part_b_t; + +typedef struct { + /* equals PCR 0xf0[9:8] (2'b00: 1s; 2'b01: 5s; 2'b10: 10s; 2'b11: 60s) */ + u32 aspm_int_timer_s:2; + /* + * enable or disable the interrupt timer for the scsiport driver. (PCR 0xf0[13] + * 1:enable the function, 0: disable the function) + */ + u32 enable_int_active:1; + u32 reserved_1:28; + /* enable or disable to set interrupt active function */ + u32 enable_set:1; +} cfg_interrupt_timer_t; + +typedef struct { + u32 reserved_1:31; + /* Whether the device is FPGA. 1 means FPGA, 0 means ASIC. */ + u32 is_fpga_chip:1; +} cfg_device_type_ctrl_t; + +typedef struct { + /* + * check OCB status in the interrupt work flow + * 0: disable check ocb in the interrupt work flow + * 1: enable in the interrupt check + */ + u32 int_check_en:1; + u32 reserved_1:30; + /* + * Enable software power off FET/LDO for OCB + * 0: Hardware power off FET/LDO 1: Software power off FET/LDO + */ + u32 sw_pwroff_en:1; + +} cfg_ocb_ctrl_t; + +#define CFG_POLARITY_PIN_LOW 0 +#define CFG_POLARITY_PIN_HIGH 1 +typedef struct { + /* polarity control pin, 1: high 0: low */ + u32 polarity_ctrl_pin:1; + u32 reserved_1:30; + /* + * enable control polarity control pin, It is only valid with the FJ2 chip. + * 0: disable 1: enable + */ + u32 enable_ctrl_polarity_pin:1; +} cfg_polarity_ctrl_t; + +typedef struct { + u32 reversed_1:31; + /* + * 1: enable LTR patch. 0:disable LTR patch. + * if LTR patch enable, it only affect FJ2 & SeaBird chip. + */ + u32 enable_ltr_patch:1; +} cfg_ltr_c10_patch_t; + +typedef struct { + /* The delay for the power off. The unit is ms. */ + u32 power_off_wait_ms:16; + /* The delay for the power on. The unit is ms. */ + u32 power_on_wait_ms:16; +} cfg_power_wait_time_t; + +#define CFG_HW_CTRL_RTD3 0 +#define CFG_SFT_CTRL_RTD3 1 + +typedef struct { + /* system enter D3 time, the unit is 1 second. */ + u32 adapter_idle_time_s:8; + /* driver enter request D3 function time, the unit is second */ + u32 disk_idle_time_s:8; + /* D3 work mode select (PCR0x3f0[29:28]) */ + u32 reserve:2; + /* enable/disable RTD3 function (software). 0:disable, 1:Enable. */ + u32 enable_rtd3:1; + /* enable D3Silence function 0:disable 1: enable */ + u32 reserve_1:1; + /* select D3 silence sub mode 0:sub mode 1 1: sub mode 2 */ + u32 d3silence_submode_sel:1; + u32 reserved_1:10; + /* + * hardware/software select bit. + * 1: use the registry to control RTD3 disable/enable, PSD_MODE[18]. + * 0: use the hardware register to control RTD3 disable/enable, PCR 0x3e0[29] + */ + u32 rtd3_ctrl_mode:1; + +} cfg_psd_mode_t; +#define CFG_THERMAL_SENSOR_HOT 0 +#define CFG_THERMAL_SENSOR_COOL 1 +#define CFG_THERMAL_USE_I2C 1 +#define CFG_THERMAL_USE_GPIO 0 + +typedef struct { + /* + * [0]When Driver receives S3 entry message, + * Driver can disable Card insert/remove source bit (0x468 [20] = 0) + */ + u32 s3_disable_wakeup:1; + /* + * [1]When Driver receives S4 entry message, + * Driver can disable Card insert/remove source bit (0x468 [20] = 0) + */ + u32 s4_disable_wakeup:1; + /* + * [2]When Driver receives S5 entry message, + * Driver can disable Card insert/remove source bit (0x468 [20] = 0) + */ + u32 s5_disable_wakeup:1; + /* [31:3] reserved */ + u32 reserve:29; +} cfg_pcie_wake_setting_t; + +typedef struct { + /* + * 0: GPIO High means Thermal Sensor Hot state, GPIO Low means Thermal Sensor Cool state + * 1: GPIO High means Thermal Sensor Cool state, GPIO Low means Thermal Sensor Hot state + */ + u32 gpio_level:1; + /* + * The GPIO/I2C selection for thermal control function. + * 1 means to use I2C, 0 means to use GPIO. + */ + u32 gpio_i2c_sel:1; + u32 reserved_1:29; + /* Enable the thermal control function. 1 means enable, 0 means disable. */ + u32 enable_thermal_func:1; +} cfg_thermal_control_en_t; + +typedef struct { + u32 reserved_1:31; + /* enable/disable timer for thermal check 0: disable 1: enable */ + u32 enable_check_timer:1; +} cfg_thermal_timer_ctrl_t; + +typedef struct { + /* the timer value for the Thermal Check (GPIO check), unit: ms */ + u32 timer_ms; +} cfg_thermal_control_timer_t; + +typedef struct { + /* Enable UHS2 Range Mode when Do thermal control. 0:disable(default) 1:enable */ + u32 enable_uhs2_range_mode:1; + /* Enable UHS2 Duplex Mode switch when do thermal control. 0:disable(default) 1:enable */ + u32 enable_uhs2_duplex_mode:1; + /* Enable UHS2 Power Mode switch when do thermal control. 0:disable(default) 1:enable */ + u32 enable_uhs2_power_mode:1; + u32 reserved_1:28; + /* + * Enable Legacy SD card Access Mode switch when + * do thermal control 0:disable(default) 1:enable + */ + u32 enable_legacy_access_mode:1; +} cfg_thermal_control_ctrl_t; + +typedef struct { + /* the upper limitation temperature for I2C thermal control function */ + u32 value:16; + u32 reserved_1:15; + /* + * set the upper limitation temperature for I2C thermal control function valid + * 0: use the default mode 1: set the upper limitation temperature + */ + u32 reserve:1; +} cfg_thermal_temperature_t; + +typedef struct { + /* + * main ldo setting for uhs2, + * if enable then write [1:0] to 0x68[11:10] for uhs2 when power on + */ + u32 value:2; + u32 reserved_1:29; + /* enable bit */ + u32 enable:1; +} cfg_ldo_t; + +typedef struct { + /* + * if failsafe patch enable,it control Delay time before + * software-reset-all in failsafe patch function. Unit ms. + */ + u32 soft_reset_delay_ms:30; + /* enable delay time before software-reset-all in failsafe patch 0:disable 1:enable */ + u32 enable_soft_reset_delay:1; + /* + * 1: means enable software reset all & fail safe patch. + * it only affect SeaEagle chip.0:mean disable this patch + */ + u32 reserve:1; +} cfg_sw_reset_all_cfg_t; + +typedef struct { + /* + * if fails safe patch enable, + * it control delay time after do disable failsafe action(ms). + */ + u32 dis_failsafe_delay_ms:4; + u32 reserved_1:27; + /* + * if fail safe patch enable, enable delay time after disable failsafe action. + * 0: disable 1:enable + */ + u32 enable_aft_disable_failsafe_delay:1; +} cfg_disable_failsafe_delay_ctrl_t; + +typedef struct { + u32 value:16; + u32 reserved_1:15; + /* enable set the card initialize (ACMD41) ready timeout. Default: 1500ms */ + u32 reserve:1; + +} cfg_timeout_t; + +typedef struct { + /* The debounce count value, it will be set to the PCR 0x324[30:0]. */ + u32 value:31; + /* + * enable/disable the function to modify the debounce count. + * 0 means disable, 1 means enable. + */ + u32 enable_deb_count:1; +} cfg_cd_debounce_count_t; + +/* ----------------------- Registry classify -------------------- */ + +typedef struct { + /* + * Maximum Access mode select, 0h: SDR12, 1h: SDR25, + * 2h: SDR50, 3h: SDR104, 4h: DDR50 5h: DDR200 Default is 3h. + */ + u32 value:8; + u32 reserved_1:23; + /* Maximum access mode valid, 0: invalid 1: valid */ + u32 reserve:1; +} cfg_test_max_access_mode_t; + +typedef struct { + /* + * Maximum Access mode select, 0h: SDR12, 1h: SDR25, 2h: SDR50, + * 3h: SDR104, 4h: DDR50 5h: DDR200 Default is 3h. + */ + u32 value:8; + u32 reserved_1:23; + /* Maximum access mode valid, 0: invalid 1: valid */ + u32 reserve:1; +} cfg_max_power_limit_t; + +typedef struct { + cfg_uhs2_setting_t uhs2_setting; + cfg_uhs2_setting2_t test_uhs2_setting2; +} cfg_uhs2_card_item_t; + +typedef struct { + /* 0: internal, 1: external */ + u32 vdd1_power_source:1; + /* 0: 1.2V, 1: 1.8V, 2: 3.3V */ + u32 vdd1_voltage:2; + /* 0: active low, 1: active high */ + u32 vdd1_onoff_polarity:1; + u32 vdd1_reserved:4; + /* 0: internal, 1: external */ + u32 vdd2_power_source:1; + /* 0: 1.2V, 1: 1.8V, 2: 3.3V */ + u32 vdd2_voltage:2; + /* 0: active low, 1: active high */ + u32 vdd2_onoff_polarity:1; + u32 vdd2_use_gpio1:1; + u32 vdd2_reserved:3; + /* 0: internal, 1: external */ + u32 vdd3_power_source:1; + /* 0: 1.2V, 1: 1.8V, 2: 3.3V */ + u32 vdd3_voltage:2; + /* 0: active low, 1: active high */ + u32 vdd3_onoff_polarity:1; + u32 vdd3_reserved:4; + u32 reserved:8; +} cfg_vdd_power_source_item_t; +/* -------------------- vdd_power_source item end ----------------------// */ + +#define INIT_DELAY 0x00000000 +#define INIT_DELAY_EN_MASK 0x000000001 +#define INIT_DELAY_CFG_MASK 0xFFFFFFFE + +typedef struct { + cfg_sd_card_mode_dis_t sd_card_mode_dis; + cfg_test_max_access_mode_t test_max_access_mode; + cfg_driver_strength_sel_t test_driver_strength_sel; + cfg_max_power_limit_t test_max_power_limit; + + cfg_mmc_mode_dis_t mmc_mode_dis; + cfg_emmc_mode_t emmc_mode; + cfg_sd7_sdmode_switch_control_t sd7_sdmode_switch_control; + + cfg_uhs2_setting_t uhs2_setting; + cfg_uhs2_setting2_t test_uhs2_setting2; + +} cfg_card_item_t; +/* -------------------- card item end ---------------------- */ + +/* 1 hw tuning0 sw tuning */ +#define TUNING_MODE 0x1 + +typedef struct { + u32 start:16, end:16; +} cfg_freq_item_t; + +/* [31:16] degrade frequency index from the frequency table */ +#define FREQ_DEGRE_INDEX_MASK 0xFFFF0000 +/* [15:0] start index from the frequency table */ +#define FREQ_START_INDEX_MASK 0x0000FFFF +#define FREQ_UHS2M 0x00030000 +#define FREQ_200M 0x00070004 +#define FREQ_100M 0x000B0008 +#define FREQ_DDR50M 0x000C000C +#define FREQ_DDR50_INPUT_TUNIN 0x00110011 +#define FREQ_75M 0x000D000D +#define FREQ_50M 0x000E000E +#define FREQ_25M 0x000F000F +#define FREQ_400K 0x00100010 +#define FREQ_EMMC_200M 0x00150012 +#define FREQ_EMMC_DDR50M 0x00160016 +#define FREQ_EMMC_50M 0x00170017 +#define FREQ_EMMC_25M 0x00180018 +#define FREQ_EMMC_400K 0x00190019 +#define FREQ_DDR200M 0x001D001A +#define FREQ_DDR225M 0x0021001E + +#define FREQ_UHS2M_DEGRE_INDEX ((FREQ_UHS2M & FREQ_DEGRE_INDEX_MASK) >> 16) +#define FREQ_UHS2M_START_INDEX (FREQ_UHS2M & FREQ_START_INDEX_MASK) + +#define FREQ_200M_DEGRE_INDEX ((FREQ_200M & FREQ_DEGRE_INDEX_MASK) >> 16) +#define FREQ_200M_START_INDEX (FREQ_200M & FREQ_START_INDEX_MASK) + +#define FREQ_100M_DEGRE_INDEX ((FREQ_100M & FREQ_DEGRE_INDEX_MASK) >> 16) +#define FREQ_100M_START_INDEX (FREQ_100M & FREQ_START_INDEX_MASK) + +#define FREQ_DDR50M_DEGRE_INDEX ((FREQ_DDR50M & FREQ_DEGRE_INDEX_MASK) >> 16) +#define FREQ_DDR50M_START_INDEX (FREQ_DDR50M & FREQ_START_INDEX_MASK) + +#define FREQ_DDR50_INPUT_TUNING_DEGRE_INDEX \ + ((FREQ_DDR50_INPUT_TUNIN & FREQ_DEGRE_INDEX_MASK) >> 16) +#define FREQ_DDR50_INPUT_TUNIN_START_INDEX \ + (FREQ_DDR50_INPUT_TUNIN & FREQ_START_INDEX_MASK) + +#define FREQ_75M_DEGRE_INDEX ((FREQ_75M & FREQ_DEGRE_INDEX_MASK) >> 16) +#define FREQ_75M_START_INDEX (FREQ_75M & FREQ_START_INDEX_MASK) + +#define FREQ_50M_DEGRE_INDEX ((FREQ_50M & FREQ_DEGRE_INDEX_MASK) >> 16) +#define FREQ_50M_START_INDEX (FREQ_50M & FREQ_START_INDEX_MASK) + +#define FREQ_25M_DEGRE_INDEX ((FREQ_25M & FREQ_DEGRE_INDEX_MASK) >> 16) +#define FREQ_25M_START_INDEX (FREQ_25M & FREQ_START_INDEX_MASK) + +#define FREQ_400K_DEGRE_INDEX ((FREQ_400K & FREQ_DEGRE_INDEX_MASK) >> 16) +#define FREQ_400K_START_INDEX (FREQ_400K & FREQ_START_INDEX_MASK) + +#define FREQ_EMMC_200M_DEGRE_INDEX ((FREQ_EMMC_200M & FREQ_DEGRE_INDEX_MASK) >> 16) +#define FREQ_EMMC_200M_START_INDEX (FREQ_EMMC_200M & FREQ_START_INDEX_MASK) + +#define FREQ_EMMC_DDR50M_DEGRE_INDEX ((FREQ_EMMC_DDR50M & FREQ_DEGRE_INDEX_MASK) >> 16) +#define FREQ_EMMC_DDR50M_START_INDEX (FREQ_EMMC_DDR50M & FREQ_START_INDEX_MASK) + +#define FREQ_EMMC_50M_DEGRE_INDEX ((FREQ_EMMC_50M & FREQ_DEGRE_INDEX_MASK) >> 16) +#define FREQ_EMMC_50M_START_INDEX (FREQ_EMMC_50M & FREQ_START_INDEX_MASK) + +#define FREQ_EMMC_25M_DEGRE_INDEX ((FREQ_EMMC_25M & FREQ_DEGRE_INDEX_MASK) >> 16) +#define FREQ_EMMC_25M_START_INDEX (FREQ_EMMC_25M & FREQ_START_INDEX_MASK) + +#define FREQ_EMMC_400K_DEGRE_INDEX ((FREQ_EMMC_400K & FREQ_DEGRE_INDEX_MASK) >> 16) +#define FREQ_EMMC_400K_START_INDEX (FREQ_EMMC_400K & FREQ_START_INDEX_MASK) + +#define FREQ_DDR200M_DEGRE_INDEX ((FREQ_DDR200M & FREQ_DEGRE_INDEX_MASK) >> 16) +#define FREQ_DDR200M_START_INDEX (FREQ_DDR200M & FREQ_START_INDEX_MASK) + +#define FREQ_DDR225M_DEGRE_INDEX ((FREQ_DDR225M & FREQ_DEGRE_INDEX_MASK) >> 16) +#define FREQ_DDR225M_START_INDEX (FREQ_DDR225M & FREQ_START_INDEX_MASK) +typedef struct { + /* setting clkreqn timeout value, unit : ms, (default: 5ms which is useful when[31] = 0 */ + u32 clkreqn_timeout_value:16; + /* Reserved */ + u32 reserved:15; + /* + * enable set clkreqn timeout, + * 0: use clkreqn timeout default value(1ms) + * 1 : enable set the clkreqn timeout(default:1) + */ + u32 enable_clkreqn_timeout:1; +} cfg_sd7_clkreqn_status_detect_timeout_t; + +typedef struct { + /* [0] Reserved */ + u32 reserve_1:1; + /* + * [3:1] + * SD 1.8v data / cmd driving strength when select PCIe external control drive strength + */ + u32 cmd_driver_strength_1_8v:3; + /* + * [6:4] + * SD 1.8v clk driving strength when select PCIe external control drive strength + */ + u32 clk_driver_strength_1_8v:3; + /* [7] Reserved */ + u32 reserve_2:1; + /* + * [10:8] + * SD 3.3v data / cmd driving strength when select PCIe external control drive strength + */ + u32 data_cmd_driver_strength_3_3v:3; + /* [11] Reserved */ + u32 reserve_3:1; + /* [14:12] SD 3.3v clk driving strength when select PCIe external control drive strength */ + u32 clk_driver_strength_3_3v:3; + /* [30:15] Reserved */ + u32 reserve_4:16; + /* + * [31] ds_selection_enable(default:0) + * 1 : driver strength method select enable 0 : driver strength method select enable + */ + u32 ds_selection_enable:1; +} cfg_host_drive_strength_item_t; + +typedef struct { + cfg_dma_mode_setting_t test_dma_mode_setting; + cfg_infinite_transfer_mode_t test_infinite_transfer_mode; + cfg_sdma_boundary_t test_sdma_boundary; + cfg_tag_queue_capability_t test_tag_queue_capability; + cfg_ocb_ctrl_t test_ocb_ctrl; + cfg_bios_l1_substate_t bios_l1_substate; + cfg_vdd_power_source_item_t vdd_power_source_item; + + cfg_host_drive_strength_item_t host_drive_strength; +} cfg_host_item_t; +/* -------------------- host item end ---------------------- */ + +typedef struct { + /* SW cfg */ + u32 value:1; + u32 reserved_1:29; + /* 1: sw, 0: hw */ + u32 sw_hw_select:1; + /* SW enable bit, or vendor PCR control */ + u32 enable:1; +} cfg_sw_write_protect_item_t; + +typedef struct { + /* 1: enable, 0: disable */ + u32 rc_tx_vcme:1; + /* 1: enable, 0: disable */ + u32 sd_rx_vcme:1; + /* 1: enable, 0: disable */ + u32 sd_tx_vcme:1; + /* 1: enable, 0: disable */ + u32 rc_rx_vcme:1; + u32 reserved_1:27; + /* 0: disable, 1: enable */ + u32 enable:1; +} cfg_hsmux_vcme_enable_item_t; + +typedef struct { + /* [15:00] */ + u32 required_refclk_compare_count:16; + /* [29:16] */ + u32 reserve:14; + /* [30] 1: Synchronize parameter 0: Not synchronize parameter (default :0) */ + u32 chk_refclk_parameter_en:1; + /* [31] 1:enable 0:disable */ + u32 enable:1; +} cfg_refclk_stable_detection_counter1_item_t; + +typedef struct { + /* [15:00] */ + u32 required_refclk_compare_timeout_d0l11:16; + /* [31:16] */ + u32 required_refclk_compare_timeout_d0l10:16; +} cfg_refclk_stable_detection_counter2_item_t; + +typedef struct { + /* [15:00] */ + u32 required_refclk_compare_timeout_d3l12:16; + /* [31:16] */ + u32 required_refclk_compare_timeout_d0l12:16; +} cfg_refclk_stable_detection_counter3_item_t; + +typedef struct { + /* [07:00] required_refclk_count_max */ + u32 req_refclkcnt_max:8; + /* [15:08] required_refclk_count_min */ + u32 req_refclkcnt_min:8; + /* [23:16] Refclk_range_detect_cnt */ + u32 refclk_range_detect_cnt:8; + /* [28:24] Reserve */ + u32 reserve:5; + /* [29] required_refclk_count_min_max_source_selection */ + u32 req_refclkcnt_minmax_source_sel:1; + /* [30] refclk_cnt_range_detect_soft_reset */ + u32 refclkcnt_range_detect_softreset:1; + /* [31] enable bit 1: enable 0 : disable (default: 1) */ + u32 enable:1; +} cfg_auto_detect_refclk_counter_range_ctl_item_t; + +typedef struct { + /* + * [0] Disable PCIe Phy Reference clock active detection logic. + * 1: Disable 0 : Enable(Default) + */ + u32 disable_pcie_phy_clk:1; + /* + * [1] Disable tx commond mode can be off only when l1sub_state == S_L1_N + * 1: Disable, 0 : Enable(Default) + */ + u32 disable_tx_command_mode:1; + /* [30:2] Reserve */ + u32 reserve:29; + /* [31] Enable bit 1: enable 0 : disable (default: 0) */ + u32 enable:1; +} cfg_l1_enter_exit_logic_ctl_item_t; + +typedef struct { + /* [15:00] */ + u32 pcietx_amplitude_setting:16; + /* [30:16] Reserve */ + u32 reserve:15; + /* [31] Enable bit 1: enable 0 : disable (default: 0) */ + u32 pcietx_amplitude_chg_en:1; +} cfg_pcie_phy_amplitude_adjust_item_t; + +typedef struct { + cfg_psd_mode_t psd_mode; + cfg_pcie_wake_setting_t pcie_wake_setting; + cfg_ldo_t test_main_ldo_setting; + cfg_output_tuning_item_t output_tuning_item; + cfg_hsmux_vcme_enable_item_t hsmux_vcme_enable; + cfg_refclk_stable_detection_counter1_item_t + refclk_stable_detection_counter1; + cfg_refclk_stable_detection_counter2_item_t + refclk_stable_detection_counter2; + cfg_refclk_stable_detection_counter3_item_t + refclk_stable_detection_counter3; + cfg_auto_detect_refclk_counter_range_ctl_item_t + auto_detect_refclk_counter_range_ctl; + cfg_l1_enter_exit_logic_ctl_item_t l1_enter_exit_logic_ctl; + cfg_pcie_phy_amplitude_adjust_item_t pcie_phy_amplitude_adjust; +} cfg_feature_item_t; +/* -------------------- feature item end ---------------------- */ + +typedef struct { + cfg_auto_sleep_t auto_sleep_control; + cfg_auto_uhs2dmt_timer_t auto_dormant_timer; +} cfg_timer_item_t; +/* -------------------- timer item end ---------------------- */ + +#define RESUME_DALAY_US 0x00000000 +#define RESUME_POWER_ON_DELAY_MS 0x00000024 +#define DMT_DELAY_BEF_STOP_CLK_US 0x00000000 +#define DMT_DEALY_AFT_STOP_CLK_US 0x00000014 +#define DMT_DELAY_AFT_PWROFF_MS 0x00000005 +#define DMT_DELAY_AFT_ST_REFCLK_US 0x00000000 + +typedef struct { + cfg_power_wait_time_t power_wait_time; + cfg_timeout_t test_write_data_timeout; + cfg_timeout_t test_read_data_timeout; + cfg_timeout_t test_non_data_timeout; + cfg_timeout_t test_r1b_data_timeout; + cfg_timeout_t test_card_init_timeout; +} cfg_timeout_item_t; + +#define UHS2_NATIVE_DATA_TIMEOUT 2000 + +/* -------------------- time item end ---------------------- */ +#define TUNING_TIMEOUT 0x80000096 + +typedef struct { + cfg_device_type_ctrl_t fpga_ctrl; +} cfg_fpga_item_t; +/* -------------------- fpga item end ---------------------- */ + +typedef struct { + /* [0:5] */ + u32 reserv_2:6; + /* [6] */ + u32 dis_patch_ntfs_verify_rtd3:1; + /* [7] */ + u32 dis_patch_rtd3_idle_ref_cnt:1; + /* [8] */ + u32 reserve_3:1; + /* [9:12] fix SeaEagle s3 resume no card stress test issue */ + u32 delay_for_failsafe_s3resume:4; + /* [13] fix SeaEagle s3 resume no card stress test issue */ + u32 failsafe_en:1; + /* + * [14] control SDR50_OPCLK to select + * OPECLK clock as sampling lock (sdr50_input_tuning_en=0) or + * DLL CLKOUTA as sampling clock (sdr50_input_tuning_en=1, default setting). + */ + u32 reserve_1:1; + /* [15] 0: disable(default); 1: enable */ + u32 card_details_display_enable:1; + /* [16:17] */ + u32 sw_ctl_led_gpio0:2; + /* [18:19] */ + u32 led_gpio1:2; + /* [20:21] */ + u32 led_gpio2:2; + /* [22:23] */ + u32 wp_led_gpio3:2; + /* [24] */ + u32 led_polarity:1; + /* [27:25] */ + u32 reserve_4:3; + /* [28] */ + u32 camera_mode_ctrl_vdd1_vdd2_cd:1; + /* [31:29] */ + u32 reserve:3; +} cfg_driver_item_t; +/* -------------------- driver item end ---------------------- */ +#define CARD_READER FALSE +#define REMOVABLE TRUE +#define HW_TIMER_CFG FALSE +#define REMOVABLE_PNP TRUE + +/* -------------------- error recovery item end ---------------------- */ + +#define MAX_PCR_SETTING_SIZE 256 + +typedef struct { + /* PCR address */ + u16 addr; + /* PCR mask */ + u16 mask; + /* PCR value */ + u16 val; + u16 valid_flg; + /* 0 : PCR; 1: bar0; */ + u32 type; +} cfg_pcr_t; + +typedef struct { + cfg_pcr_t pcr_tb[MAX_PCR_SETTING_SIZE]; + /* valid counter for pcr settings */ + u32 cnt; +} cfg_pcr_item_t; + +typedef struct { + u32 test_id; + /* 0 means infinite */ + u32 test_loop; + u32 test_param1; + u32 test_param2; +} cfg_testcase_t; + +typedef struct { + cfg_card_item_t card_item; + cfg_host_item_t host_item; + + cfg_feature_item_t feature_item; + cfg_timer_item_t timer_item; + cfg_timeout_item_t timeout_item; + cfg_fpga_item_t fpga_item; + cfg_driver_item_t driver_item; + cfg_testcase_t test_item; + + /* mem variable */ + cfg_pcr_item_t pcr_item; + /* Directly values: */ + u32 dmdn_tbl[MAX_FREQ_SUPP]; + bool boot_flag; + +} cfg_item_t; + +struct amplitude_configuration { + u32 pcietx_amplitude; + char amplitude[5]; +}; + +#define HW_DETEC_HW_SWITCH 0x0 +#define SW_POLL_SW_SWITCH 0x4 +#define SW_POLL_SWCTRL_SWITCH 0x5 +#define SW_POLL_INTER_SW_SWITCH 0x6 +#define SW_POLL_INTER_SWCRTL_SWITCH 0x7 + +void cfgmng_init(void); +void cfg_print_debug(PVOID cfg_item); +cfg_item_t *cfgmng_get(void *pdx, e_chip_type chip_type, bool boot); + +void cfg_dma_mode_dec(cfg_item_t *cfg, u32 dec_dma_mode); +void cfg_dma_addr_range_dec(cfg_item_t *cfg, u32 dma_range); +/* 0: 1bit 1: 4bit 2: 8bit */ +void cfg_emmc_busw_supp(cfg_emmc_mode_t *emmc_mode, u8 bus_width); +bool cfg_dma_need_sdma_like_buffer(u32 dma_mode); + +void os_load_pcr_cb(void *cfgp, u32 type, u32 idx, u32 addr, u32 value); +void os_load_dmdn_cb(void *cfgp, u32 type, u32 idx, u32 addr, u32 value); + +void cfgmng_init_chipcfg(e_chip_type chip_type, cfg_item_t *cfg, bool reinit); +void cfgmng_update_dumpmode(cfg_item_t *cfg, e_chip_type chip_type); + +extern cfg_item_t g_cfg[SUPPORT_CHIP_COUNT][2]; + +#endif diff --git a/drivers/scsi/bht/include/cmdhandler.h b/drivers/scsi/bht/include/cmdhandler.h new file mode 100644 index 000000000000..6a7c1f72ade5 --- /dev/null +++ b/drivers/scsi/bht/include/cmdhandler.h @@ -0,0 +1,289 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: cmdhandler.h + * + * Abstract: include related macro and declaration about SD CMD + * + * Version: 1.00 + * + * Environment: OS Independent + */ + +/* Command Response */ +#define CMD_FLG_RESCHK (1<<0) +/* Tuning */ +#define CMD_FLG_TUNE (1<<1) + +#define CMD_FLG_INF_BUILD (1<<2) +#define CMD_FLG_INF_CON (1<<3) +#define CMD_FLG_INF (CMD_FLG_INF_BUILD | CMD_FLG_INF_CON) + +/* R1 */ +#define CMD_FLG_R1 (1<<4) +/* R1B */ +#define CMD_FLG_R1B (1<<5) +/* R2(CID,CSD) */ +#define CMD_FLG_R2 (1<<6) +/* R3(OCR) */ +#define CMD_FLG_R3 (1<<7) +/* R6( Publish RSA ) */ +#define CMD_FLG_R6 (1<<8) +/* R4(I/O OCR) */ +#define CMD_FLG_R4 (1<<9) +/* R5 */ +#define CMD_FLG_R5 (1<<10) +/* R7 */ +#define CMD_FLG_R7 (1<<11) +#define CMD_FLG_RESP_MASK (CMD_FLG_R1 | CMD_FLG_R1B | CMD_FLG_R2 | CMD_FLG_R3 | CMD_FLG_R4 | \ + CMD_FLG_R5 | CMD_FLG_R6 | CMD_FLG_R7) +#define CMD_FLG_ADMA_SDMA (1<<12) +/* DMA */ +#define CMD_FLG_SDMA (1<<13) +/* DMA */ +#define CMD_FLG_ADMA2 (1<<14) +/* DMA */ +#define CMD_FLG_ADMA3 (1<<15) +/* DMA */ +#define CMD_FLG_DMA (CMD_FLG_SDMA | CMD_FLG_ADMA2 | CMD_FLG_ADMA3 | CMD_FLG_ADMA_SDMA) + +#define CMD_FLG_PACKED (1<<16) +#define CMD_FLG_AUTO23 (1<<17) +#define CMD_FLG_AUTO12 (1<<18) + +/* Multiple data command */ +#define CMD_FLG_MULDATA (1<<19) +#define CMD_FLG_NO_TRANS (1<<20) +#define CMD_FLG_DDR200_WORK_AROUND (1<<21) + +/* ------------------------------------------------------------------------- */ +/* SD MEMORY CARD */ +/* ------------------------------------------------------------------------- */ +/* 1bit Bus Width */ +#define BUS_WIDTH_1BIT 0x0000 +/* 4bit Bus Width */ +#define BUS_WIDTH_4BIT 0x0002 + +/* + * UHS2 SD-TRAN CMD HEADER + */ +#define UHS2_CMD_HEADER_DCMD (1 << 4) +#define UHS2_CMD_HEADER_NP (1 << 7) +#define UHS2_HEADER_DID(did) (did) +#define UHS2_HEADER_SID(sid) (sid<<12) + +/* + * UHS2 Native command headers + */ +#define UHS2_NATIVE_HEADER_RW (1<<23) +#define UHS2_NATIVE_CCMD_PLEN0 (0<<20) +#define UHS2_NATIVE_CCMD_PLEN4 (1<<20) +#define UHS2_NATIVE_CCMD_PLEN8 (2<<20) +#define UHS2_NATIVE_CCMD_PLEN16 (3<<20) + +#define UHS2_CMD_HEADER_APPCMD (1 << 30) + +#define UHS2_HEADER_TMODE_DAM (1<<19) +#define UHS2_CMD_TMODE_TLUM (1<<20) +#define UHS2_CMD_TMODE_LM (1<<21) +#define UHS2_CMD_TMODE_DM (1<<22) + +#define UHS2_CMD_NATIVE_RW (1<<23) + +#define UHS2_NATIVE_CCMD_IOADDR(addr) (((addr & 0x0F00)<<8) | ((addr & 0x00FF)<<24)) +#define UHS2_GET_NATIVE_IOADDR(head) (((head >> 24) & 0xFF) | ((head & 0xF0000) >> 8)) + +#define UHS2_GET_NATIVE_IOADDR(head) (((head >> 24) & 0xFF) | ((head & 0xF0000) >> 8)) +#define UHS2_IS_NATIVE(head) (head & UHS2_CMD_HEADER_NP) + +#define UHS2_IOADDR_FULLRESET 0x200 +#define UHS2_IOADDR_GODMT 0x201 +#define UHS2_IOADDR_DEVINIT 0x202 +#define UHS2_IOADDR_ENUM 0x203 +#define UHS2_IOADDR_ABORT 0x204 + +#define UHS2_IOADDR_GEN_CAPL 0x000 +#define UHS2_IOADDR_GEN_CAPH 0x001 +#define UHS2_IOADDR_PHY_CAPL 0x002 +#define UHS2_IOADDR_PHY_CAPH 0x003 +#define UHS2_IOADDR_LINKT_CAPL 0x004 +#define UHS2_IOADDR_LINKT_CAPH 0x005 + +#define UHS2_IOADDR_GEN_SETL 0x008 +#define UHS2_IOADDR_GEN_SETH 0x009 +#define UHS2_IOADDR_PHY_SETL 0x00A +#define UHS2_IOADDR_PHY_SETH 0x00B +#define UHS2_IOADDR_LINKT_SETL 0x00C +#define UHS2_IOADDR_LINKT_SETH 0x00D + +#define UHS2_IOADDR_ST_REG 0x180 + +#define UHS2_RESP_NACK BIT23 + +#define EMMC_OCR_HI 0x40FF8000UL +#define EMMC_OCR_LOW 0x40000080UL +#define EMMC_OCR 0x40FF8080UL + +/* Command Define */ + +#define SD_CMD0 0x00 +#define SD_CMD1 0x01 +#define SD_CMD2 0x02 +#define SD_CMD3 0x03 +#define SD_CMD4 0x04 +#define SD_CMD5 0x05 +#define SD_CMD6 0x06 +#define SD_CMD7 0x07 +#define SD_CMD8 0x08 +#define SD_CMD9 0x09 +#define SD_CMD10 0x0A +#define SD_CMD11 0x0B +#define SD_CMD12 0x0C +#define SD_CMD13 0x0D +#define SD_CMD14 0x0E +#define SD_CMD15 0x0F +#define SD_CMD16 0x10 +#define SD_CMD17 0x11 +#define SD_CMD18 0x12 +#define SD_CMD19 0x13 +#define SD_CMD20 0x14 + +/* DPS Passwd Authentication */ +#define SD_CMD21 0x15 +#define SD_CMD22 0x16 +#define SD_CMD23 0x17 +#define SD_CMD24 0x18 +#define SD_CMD25 0x19 +#define SD_CMD26 0x1A +#define SD_CMD27 0x1B +#define SD_CMD28 0x1C +#define SD_CMD29 0x1D +#define SD_CMD30 0x1E +#define SD_CMD31 0x1F +#define SD_CMD32 0x20 +#define SD_CMD33 0x21 +#define SD_CMD34 0x22 +#define SD_CMD35 0x23 +#define SD_CMD36 0x24 +#define SD_CMD37 0x25 +#define SD_CMD38 0x26 +#define SD_CMD39 0x27 +#define SD_CMD40 0x28 +#define SD_CMD41 0x29 +#define SD_CMD42 0x2A +#define SD_CMD43 0x2B +#define SD_CMD44 0x2C +#define SD_CMD45 0x2D +#define SD_CMD46 0x2E +#define SD_CMD47 0x2F +#define SD_CMD48 0x30 +#define SD_CMD49 0x31 +#define SD_CMD50 0x32 +#define SD_CMD51 0x33 +#define SD_CMD52 0x34 +#define SD_CMD53 0x35 +#define SD_CMD54 0x36 +#define SD_CMD55 0x37 +#define SD_CMD56 0x38 +#define SD_CMD57 0x39 +#define SD_CMD58 0x3A +#define SD_CMD59 0x3B +#define SD_CMD60 0x3C +#define SD_CMD61 0x3D +#define SD_CMD62 0x3E +#define SD_CMD63 0x3F + +#define SD_APPCMD 0x80 + +#define SD_ACMD6 0x86 +#define SD_ACMD13 0x8D +/* DPS Passwd Management */ +#define SD_ACMD14 0x8E +/* DPS Reads and Decrypts */ +#define SD_ACMD15 0x8F +/* DPS Encrypts and writes */ +#define SD_ACMD16 0x90 +#define SD_ACMD22 0x96 +#define SD_ACMD23 0x97 +/* DPS Read the infor of DPS, DPS_Off */ +#define SD_ACMD28 0x9C +#define SD_ACMD41 0xA9 +#define SD_ACMD42 0xAA +#define SD_ACMD51 0xB3 + +bool thread_exec_high_prio_job(bht_dev_ext_t *pdx, cb_soft_intr_t func, + void *data); + +/* + * check cmdline and datline inhabit + * Fill host_trans_reg_t according to sd_cmd + */ +bool cmd_generate_reg(sd_card_t *card, sd_command_t *sd_cmd); + +/* + * Fill host_cmd_req_t according to sd_cmd + * For legacy appcommand , will first sync execute cmd55 + */ +bool cmd_execute_sync(sd_card_t *card, sd_command_t *sd_cmd, + req_callback func_done); + +/* + * for legacy acmd case, this fucn will do cmd55 + * For legacy appcommand , will first sync execute cmd55 + */ +bool cmd_execute_sync2(sd_card_t *card, sd_command_t *sd_cmd, + host_cmd_req_t *req, req_callback func_done); + +bool cmd_execute_sync3(sd_card_t *card, sd_command_t *sd_cmd, + host_cmd_req_t *req, req_callback func_done, + issue_post_callback post_cb); + +/* inline bool cmd_can_use_packed(sd_card_t *card); */ +u32 cmd_can_use_inf(sd_card_t *card, e_data_dir dir, u32 sec_addr, + u32 sec_cnt); + +bool cmd_dat_line_chk(sd_card_t *card, sd_command_t *sd_cmd); +/* + * Descriptor Apis + */ + +/* + * 1.need support (26bit/16bit data length; 64bit/128bit desc) + * 2.generat ADMA2 desc buf on pbuf. + * 3.return end desc buffer + * 4.upper layer handle size for merge link. + */ +#define AMDA2_26BIT_DATA_LEN (1<<0) +#define ADMA2_128BIT_DESC_LEN (1<<1) +dma_desc_buf_t build_adma2_desc_nop(sg_list_t *sg, u32 sg_len, byte *desc_buf, + u32 desc_len, bool dma_64bit, + bool data_26bit); +dma_desc_buf_t build_adma2_desc(sg_list_t *sg, u32 sg_len, byte *desc_buf, + u32 desc_len, bool dma_64bit, bool data_26bit); +void update_adma2_desc_inf(byte *desc_end, phy_addr_t phy_addr, u32 flag); +void amda2_desc_merage(byte *tbl1_end, phy_addr_t tbl2_header); + +/* + * build adma3 cmd item + * return end of cmd table addr + */ +byte *build_adma3_cmd_desc(byte *cmd_tbl, host_trans_reg_t *reg, + e_card_type type); + +/* + * 1. need support 64bit/128bit desc + * (1)fill integrate table. + * caller prepare integrate desc table for all cmd list. + */ +byte *build_integrated_desc(byte *tbl, phy_addr_t *phy_addr, bool dma_64bit); +void end_integrated_desc(byte *tbl, u32 flag); + +void cmd_set_auto_cmd_flag(sd_card_t *card, u32 *cmd_flag); + +bool card_is_low_capacity(sd_card_t *card); +bool cmd_is_adma_error(sd_command_t *sd_cmd); + +dma_desc_buf_t *node_get_desc_res(node_t *node, u32 max_use_size); +bool update_adma2_inf_tb(u8 *pdesc, u8 **link_addr, phy_addr_t *pa, + bool dma_64bit); diff --git a/drivers/scsi/bht/include/debug.h b/drivers/scsi/bht/include/debug.h new file mode 100644 index 000000000000..8c9a66db9a63 --- /dev/null +++ b/drivers/scsi/bht/include/debug.h @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: debug.h + * + * Abstract: define log type mapping for debugging + * + * Version: 1.00 + * + * Environment: OS Independent + * + */ + +#ifndef _BHT_DBG_H + +#define _BHT_DBG_H + +/* host.c */ +#define MODULE_SD_HOST 0x00000001 +/* hostven.c */ +#define MODULE_VEN_HOST 0x00000002 +/* transhander.c */ +#define MODULE_TRANS 0x00000004 + +/* uhs2.c */ +#define MODULE_UHS2_CARD 0x00000008 +/* mmc.c */ +#define MODULE_MMC_CARD 0x00000010 +/* sd.c */ +#define MODULE_SD_CARD 0x00000020 +/* sdio.c */ +#define MODULE_OTHER_CARD 0x00000040 + +/* cardcommon.c cardinterface.c */ +#define MODULE_ALL_CARD (MODULE_UHS2_CARD | MODULE_MMC_CARD | \ + MODULE_SD_CARD | MODULE_OTHER_CARD) +#define MODULE_LEGACY_CARD (MODULE_MMC_CARD | MODULE_SD_CARD | MODULE_OTHER_CARD) + +#define MODULE_TQ_FLOW 0x00000080 +#define MODULE_TQ_POLICY 0x00000100 +#define MODULE_TQ_DMA 0x00000200 + +/* thread.c */ +#define MODULE_MAIN_THR 0x00000400 +/* reqmng.c */ +#define MODULE_REQ_MNG 0x00000800 +/* pmfunc.c */ +#define MODULE_MAIN_PM 0x00001000 +/* geniofunc.c */ +#define MODULE_MAIN_GENIO 0x00002000 +/* autotimer.c */ +#define MODULE_AUTOTIMER 0x00004000 +/* thermal.c */ +#define MODULE_THERMAL 0x00008000 +/* cfgmng.c */ +#define MODULE_CFG_MNG 0x00010000 + +#define MODULE_OTHER 0x00020000 + +#define MODULE_OS_ENTRYAPI 0x00040000 +#define MODULE_OS_ENTRY 0x00080000 +#define MODULE_OS_API 0x00100000 + +#define MODULE_TEST 0x00200000 + +#define MODULE_ALL 0xFFFFFFFF + +/* + * This is used for Driver Entry and Driver Unload + * req_global_init and uninit and sub call + */ +#define FEATURE_DRIVER_INIT 0x00000001 + +/* For Card Init Flow */ +#define FEATURE_CARD_INIT 0x00000002 + +/* For Error Recovery Flow */ +#define FEATURE_ERROR_RECOVER 0x00000004 + +/* For Scsi RW, Card RW, TagQueure RW case */ +#define FEATURE_RW_TRACE 0x00000008 + +/* Other Card operation, such as cmd12, power off and etc */ +#define FEATURE_CARD_OPS 0x00000010 + +/* for Command handler and card_send_sdcmd only */ +#define FEATURE_CARDCMD_TRACE 0x00000020 + +/* For trace interrupt only */ +#define FEATURE_INTR_TRACE 0x00000040 + +/* For PCI and SD Bar0 and SD Bar1 */ +#define FEATURE_SDREG_TRACEW 0x00000080 +#define FEATURE_SDREG_TRACER 0x00000100 +#define FEATURE_PCIREG_TRACEW 0x00000200 +#define FEATURE_PCIREG_TRACER 0x00000400 +#define FEATURE_VENREG_TRACEW 0x00000800 +#define FEATURE_VENREG_TRACER 0x00001000 + +/* Pnp and PM trace */ +#define FEATURE_PNP_TRACE 0x00002000 +#define FEATURE_PM_TRACE 0x00004000 + +/* Trace each scsi command and result */ +#define FEATURE_SCSICMD_TRACE 0x00008000 + +/* Main Thread Event trace */ +#define FEATURE_THREAD_TRACE 0x00010000 + +/* for Register Cfg setting only */ +#define FEATURE_CFG_TRACE 0x00020000 + +/* For other function not specified */ +#define FEATURE_FUNC_TRACE 0x00040000 +#define FEATURE_FUNC_DESC 0x00080000 + +/* Functions */ +#define FEATURE_TIMER_TRACE 0x00100000 +#define FEATURE_FUNC_THERMAL 0x00200000 +#define FEATURE_IOCTL_TRACE 0x00400000 + +#define FEATURE_DBG_ALL 0xFFFFFFFF + +#define DBG_CTRL_DUMP_HOST BIT0 +#define DBG_CTRL_DUMP_DESC BIT1 + +#define DBG_CTRL_CONTROL 3 + +/* contorol which feature will be print for DbgInfo */ +#define DBG_FEATURE_CONTROL (0xFFFFFFFF) +/* control which modules will be print for DbgWarn and DbgInfo */ +#define DBG_MODULE_CONTROL (0xFFFFFFFF) + +#define TO_RAM 1 +#define NOT_TO_RAM 0 +void DbgRamInit(void); +void DbgRamInitNon(void); +void DbgRamFree(void); + +void DbgErr(byte *info, ...); +void PrintMsg(byte *info, ...); + +#ifdef DBG_PERFORMANCE +void calc_req_start(tPerTick *tick, u32 sec_cnt, bool bWrite); +void calc_thr_start(tPerTick *tick); +void calc_io_end(tPerTick *tick); +#else +#define calc_req_start(x, y, z) +#define calc_thr_start(x) +#define calc_io_end(x) +#endif + +extern u32 g_dbg_ctrl; + +#if DBG || _DEBUG +void x_assert(char *, unsigned int); +#define X_ASSERT(f) do { if (f) { } else x_assert(__FILE__, __LINE__); } while (0) + +void DbgWarn(u32 module, byte toram, byte *info, ...); +void DbgInfo(u32 module, u32 feature, byte toram, byte *info, ...); + +#else +#define DbgWarn(m, r, _x_, ...) +#define DbgInfo(m, f, r, _x_, ...) +#define X_ASSERT(f) +#endif + +#endif diff --git a/drivers/scsi/bht/include/funcapi.h b/drivers/scsi/bht/include/funcapi.h new file mode 100644 index 000000000000..67b971d94f64 --- /dev/null +++ b/drivers/scsi/bht/include/funcapi.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: funcapi.h + * + * Abstract: include functional API + * + * Version: 1.00 + * + * Environment: OS Independent + */ + +void func_autotimer_stop(bht_dev_ext_t *pdx); +void func_autotimer_start(bht_dev_ext_t *pdx); + +void func_timer_callback(bht_dev_ext_t *pdx); +void func_autotimer_cancel(bht_dev_ext_t *pdx); + +void thermal_init(bht_dev_ext_t *pdx); +void thermal_uninit(bht_dev_ext_t *pdx); +void func_thermal_update_time(bht_dev_ext_t *pdx); +bool func_thermal_control(sd_card_t *card); + +bool func_cprm(sd_card_t *card, request_t *req); +bool func_io_reg(sd_card_t *card, request_t *req); +bool func_nsm(sd_card_t *card, request_t *req, bht_dev_ext_t *pdx); +bool erase_rw_blk_end_set(sd_card_t *card, sd_command_t *sd_cmd, + u32 sec_addr); +bool erase_rw_blk_start_set(sd_card_t *card, sd_command_t *sd_cmd, + u32 sec_addr); +bool func_erase(sd_card_t *card, sd_command_t *sd_cmd); + +#define CPRM_IO_GETCSD 11 +#define CPRM_IO_GETMKB 12 +#define CPRM_IO_GETMID 13 +#define CPRM_IO_GETWP 14 + +#define CPRM_IO_SETCERRN 15 +#define CPRM_IO_GETCERRN 16 +#define CPRM_IO_GETCERRES 17 +#define CPRM_IO_SETCERRES 18 + +#define CPRM_IO_CHANGE_SA 19 + +#define CPRM_IO_READ 21 +#define CPRM_IO_WRITE 22 + +#define CPRM_IO_SECURE_READ 23 +#define CPRM_IO_SECURE_WRITE 24 +#define CPRM_IO_REMOVE_UNIT 25 +#define CPRM_IO_GETSDHC 26 +#define IO_READ_PCI_REG 60 +#define IO_WRITE_PCI_REG 61 +#define IO_READ_MEM_REG 62 +#define IO_WRITE_MEM_REG 63 + +#define IO_NSM_CMD48 70 +#define IO_NSM_CMD49 71 +#define IO_NSM_CMD58 72 +#define IO_NSM_CMD59 73 + +#define IO_NSM_CMD42 74 + +/* DDSendCSD */ +#define IO_NSM_CMD9 75 +/* DDSendCID */ +#define IO_NSM_CMD10 76 +/* DDSDStatus */ +#define IO_NSM_ACMD13 77 +/* DDSendSCR */ +#define IO_NSM_ACMD51 78 + +/* DDProgramCSD */ +#define IO_NSM_CMD27 79 +/* DDGenCmd */ +#define IO_NSM_CMD56 80 + +/* DDSwitchMode */ +#define IO_NSM_CMD6 81 diff --git a/drivers/scsi/bht/include/function.h b/drivers/scsi/bht/include/function.h new file mode 100644 index 000000000000..e795e783c8f6 --- /dev/null +++ b/drivers/scsi/bht/include/function.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: function.h + * + * Abstract: define time and power state struct + * + * Version: 1.00 + * + * Environment: OS Independent + */ + +#include "basic.h" +#ifndef _FUNCTION_H +#define _FUNCTION_H +typedef struct { + u32 auto_dmt_tick; + u32 auto_cmd12_tick; + u32 auto_poweroff_tick; + u32 auto_led_off_tick; + + u32 last_tick; + + /* + * Below 3 variable shows the value for auto dmt time; million second + */ + u32 auto_dmt_time; + u32 auto_cmd12_time; + u32 auto_poweroff_time; + u32 auto_led_off_time; + + bool auto_dmt_enable; + bool auto_cmd12_enable; + bool auto_poweroff_enable; + bool auto_led_off_enable; + + bool enable_hibernate; + + bool stop; + bool enable; + bool cancel; + + bool s3reusme_cardchg_issuefix_en; + u8 s3reusme_timer_expect_cnt; + u8 s3reusme_timer_actual_cnt; + bool s3s4_start_timer; +} time_struct; + +typedef struct { + bool s3s4_entered; + bool rtd3_entered; + bool s5_entered; + bool warm_boot_entered; + bool rtd3_en; + bool d3_silc_en; + bool d3_silc_submode2_en; + + u32 reg_0x304; + /* PartA */ + u32 reg_0xdc; + u32 reg_0x3e0; + u32 reg_0x3e8; + u32 reg_0x3ec; + u32 reg_0xf4; + u32 reg_0x74; + u32 reg_0xf0; + u32 reg_0x90; + u32 reg_0x3e4; + /* PartB */ + u32 reg_0x64; + u32 reg_0xec; + u32 reg_0xd4; + u32 reg_0x328; + u32 reg_0x300; + u32 reg_0x68; + u32 reg_0x350; + u32 reg_0x35c; + u32 reg_0x334; + u32 reg_0xd8; + u32 reg_0x3f0; + u32 reg_0x88; + u32 reg_0x33c; + u32 reg_0xe0; + u32 reg_0xfc; +} pm_state_t; + +typedef enum { + THERMAL_COOL = 0x00, + THERMAL_HOT = 0x01, + THERMAL_NORMAL = 0x02 +} e_thermal_val; + +#endif diff --git a/drivers/scsi/bht/include/globalcfg.h b/drivers/scsi/bht/include/globalcfg.h new file mode 100644 index 000000000000..1bb493c480ac --- /dev/null +++ b/drivers/scsi/bht/include/globalcfg.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: globalcfg.h + * + * Abstract: This Include file is used for Global Configuration Macros + * + * Version: 1.00 + * + * Author: Peter.Guo + * + * Environment: OS Independent + * + * History: + * + * 8/25/2014 Creation Peter.Guo + */ + +#ifndef _GLOBALCFG_H +#define _GLOBALCFG_H + +#ifdef __linux__ +#define CFG_OS_LINUX 1 +#endif + +/* #define MultiThread */ + +#define SD_BLOCK_LEN 512 +#define GLOBAL_ENABLE_BOOT 0 +/* Below Item is for EMMC boot Setting only when GLOBAL_ENABLE_BOOT is 1 */ +#define EMMC_BOOT_NONE 0x00000000 +#define EMMC_BOOT_HS200 0x80000022 +#define EMMC_BOOT_HS400 0x80000042 +#define EMMC_BOOT_DDR50_33V 0x80002001 +#define EMMC_BOOT_DDR50_18V 0x80002003 +#define EMMC_BOOT_HS_33V 0x80002000 +#define EMMC_BOOT_HS_18V 0x80002002 + +#define GLOBAL_EMMC_BOOT_CFG EMMC_BOOT_NONE +#define GET_TIMER_PRECISE 1 + +/* Max Transfer Size */ +#define CFG_MAX_TRANSFER_LENGTH (1024 * 1024) +/* 256 + 1 */ +#define MAX_SGL_RANGE (258) +#define DBG 1 +#define _DEBUG 0 +#define BHT_LINUX_ENABLE_RTD3 0 + +#define MAX_WORK_QUEUE_SIZE (32) +#define TQ_WORK_QUEUE_SIZE 2 +#define ADMA2_MAX_DESC_LINE_SIZE (256) +#define GBL_ASYNC_PERFEATCH_IO 1 + +#define MAX_EMMC_PARTION 3 + +#define SUPPORT_CHIP_COUNT 10 + +/* 10S */ +#define SOFT_INTR_TIMEOUT (10 * 1000) + +#define AUTO_TIMER_TICK 20 + +#define BHT_PDX_SIGNATURE 0xAA5555AA +#define TUNING_ADDRESS_OFFSET 0xFF + +#define CARD_FIRST_INIT_RETRY 5 +#define CARD_REINIT_RETRY 3 +#define CARD_INIT_DEGARDE_TIME 2 + +#define CARD_DEGRADE_FREQ_TIMES 3 + +#define DEVICE_STATUS_CHIPLOST 1 +#define DEVICE_STATUS_D0_MODE 2 +#define DEVICE_STATUS_D3_MODE 3 + +#endif diff --git a/drivers/scsi/bht/include/host.h b/drivers/scsi/bht/include/host.h new file mode 100644 index 000000000000..e0a8e2248f27 --- /dev/null +++ b/drivers/scsi/bht/include/host.h @@ -0,0 +1,282 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: host.h + * + * Abstract: This Include file define host structure + * + * Version: 1.00 + * + * Author: Peter.Guo + * + * Environment: OS Independent + * + * History: + * + * 8/25/2014 Creation Peter.Guo + */ + +#ifndef _HOST_H +#define _HOST_H +#include "../include/basic.h" + +/* 200MHz */ +#define SD_CLK_BASE 200000 +/* ID Clock 400KHz */ +#define SD_CLK_ID_400K 400 +/* 50MHz (HS,SDR25) */ +#define SD_CLK_50M 50000 +/* 25MHz (DS,SDR12) */ +#define SD_CLK_25M 25000 +/* 100MHz (SDR50) */ +#define SD_CLK_100M 100000 +/* 200MHz (SDR104) */ +#define SD_CLK_200M 200000 +/* 225MHz (DDR200) */ +#define SD_CLK_225M 225000 +/* 75MHz (Lightning Mode) */ +#define SD_CLK_75M 75000 + +/* Example is 250ms for Abort time */ +#define RESET_FOR_ALL_ABRT_TM 250 + +typedef struct { + /* host support max LSS DIR */ + u8 n_lss_dir:4, + /* host support max LSS SYN */ + n_lss_syn:4; + /* host support max N_FCU */ + u8 n_fcu; + /* host support max DIDL between data packets */ + u8 n_data_gap; + /* device alloc power host max support */ + u8 gap:4, + /* group alloc power host max support */ + dap:4; + /* max spedd range host support */ + u8 speed_range:2, + /* max number of lane host support */ + num_of_lane:6; + /* max devices host support */ + u8 max_devices:4, + /*host support max retry count */ + retry_cnt:2; + /* host support max block len */ + u16 max_blk_len; + /* host max support vdd2 power */ + u32 vdd2_18_maxpower:29, + /* host vdd2 support */ + vdd2_ocr:3; + + /* base offset for uhs2 capability regs */ + u16 cap_base; + /* base offset for uhs2 setting regs */ + u16 set_base; + /* base offset for uhs2 test regs */ + u16 tst_base; + /* base offset for uhs2 vendor regs */ + u16 vnd_base; +} host_uhs2_cap; + +typedef struct { + byte l1_substate:1; + byte ltr:1; + byte d3_silence:1; + byte rtd3_hot:1; + byte rtd3_cold:1; +} vendor_pm_feature; + +typedef struct { + vendor_pm_feature pm; + u32 reserved; + byte non_removalbe; + + /* + * Rule: The Capability which get our PCR or vendor mem area + */ +} host_vendor_cap; + +typedef struct { + /* Hardware can check card response */ + byte hw_resp_chk:1; + byte hw_autocmd:1; + byte hw_pll_enable:1; + byte hw_led_fix:1; + /* 4.1 host support */ + byte hw_41_supp:1; + + /* + * Rule: The Feature the chip support + */ +} host_feature; + +typedef struct { + u32 error_code; + u16 legacy_err_reg; + u32 uhs2_err_reg; + byte app_stage; + + u32 resp_err; +} cmd_err_t; + +#define INTR_CB_ERR -1 +#define INTR_CB_OK 0 +#define INTR_CB_NOEND 1 + +typedef u32(*intr_callback) (void *card, void *host_request); +typedef u32(*req_callback) (void *pdx, cmd_err_t *err); +typedef u32(*issue_post_callback) (void *pdx); + +/* + * Card Response Error Type + */ +#define RESP_OUT_OF_RANGE 0x8000 +#define RESP_ADDRESS_ERROR 0x4000 +#define RESP_BLOCK_LEN_ERROR 0x2000 +#define RESP_ERASE_SEQ_ERROR 0x1000 +#define RESP_ERASE_PARAM 0x0800 +#define RESP_WP_VIOLATION 0x0400 +#define RESP_LOCK_UNLOCK_FAILED 0x0100 +#define RESP_COM_CRC_ERROR 0x0080 +#define RESP_ILLEGAL_COMMAND 0x0040 +#define RESP_CARD_ECC_FAILED 0x0020 +#define RESP_CC_ERROR 0x0010 +#define RESP_ERROR 0x0008 +#define RESP_UNDERRUN 0x0004 +#define RESP_OVERRUN 0x0002 +#define RESP_CIDCSD_OVERWRITE 0x0001 + +/* + * Error Code definition(0 means ok) + */ +#define ERR_CODE_NO_CARD 1 +#define ERR_CMDLINE_INHABIT 2 +#define ERR_DATLINE_INHABIT 3 +#define ERR_CODE_INVALID_CMD 4 +#define ERR_CODE_RESP_ERR 5 +#define ERR_CODE_TIMEOUT 6 +#define ERR_CODE_INTR_ERR 7 +#define ERR_CODE_EXCEPT_STOP 8 +#define ERR_CODE_AUTORESP_ERR 9 +#define ERR_CODE_SOFTARE_ARG 10 + +#define RESP_ERR_TYPE_OUT_OF_RANGE (1<<31) +#define RESP_ERR_TYPE_ADDRESS_ERROR (1<<30) +#define RESP_ERR_TYPE_BLOCK_LEN_ERROR (1<<29) +#define RESP_ERR_TYPE_ERASE_SEQ_ERROR (1<<28) +#define RESP_ERR_TYPE_ERASE_PARAM (1<<27) +#define RESP_ERR_TYPE_WP_VIOLATION (1<<26) +#define RESP_ERR_TYPE_LOCK_UNLOCK (1<<24) +#define RESP_ERR_TYPE_COM_CRC_ERROR (1<<23) +#define RESP_ERR_TYPE_ILLEGAL_CMD (1<<22) +#define RESP_ERR_TYPE_CARD_ECC_FAILED (1<<21) +#define RESP_ERR_TYPE_CC_ERROR (1<<20) +#define RESP_ERR_TYPE_ERROR (1<<19) + +#define RESP_ERR_TYPE_CSD_OVERWRITE (1<<16) + +#define RESP_ERR_TYPE_FUNC_NUM (1<<1) + +typedef struct { + bool auto_flag; + u32 sdr104_auto_flag; + u32 sdr50_auto_flag; + u32 ddr50_auto_flag; + u32 sdhs_auto_flag; + u32 start_block; + u32 auto_phase; + bool auto_phase_flag; +} output_tuning_t; + +typedef struct host_cmd_req_s { + u16 int_flag_wait; + u16 int_flag_err; + u32 int_flag_uhs2_err; + + completion_t done; + + e_infinite_mode inf_mode; + e_card_type card_type; + e_trans_type trans_type; + + /* to sd_cmd */ + void *private; + /* pointer to sd_card_t */ + void *card; + + intr_callback cb_response; + intr_callback cb_buffer_ready; + intr_callback cb_trans_complete; + intr_callback cb_boundary; + + req_callback cb_req_complete; + issue_post_callback issue_post_cb; +} host_cmd_req_t; + +typedef struct { + u16 vendor_id; + u16 device_id; + u16 revision_id; + /* PCR 0xDC[31:24] */ + u16 sub_version; + + e_chip_type chip_type; + + t_pci_dev pci_dev; + cfg_item_t *cfg; + + u32 ocr_avail; + u32 mmc_ocr_avail; + + u32 vdd2_12v_supp:1, + vdd2_18v_supp:1, + bit64_v3_supp:1, + bit64_v4_supp:1, + adma3_supp:1, + uhs2_supp:1, adma2_supp:1, sdma_supp:1, hs_supp:1, bus_8bit_supp:1; + u16 max_block_len; + + u16 max_vdd2_current; + u16 max_18vdd1_current; + u16 max_30vdd1_current; + u16 max_33vdd1_current; + + host_uhs2_cap uhs2_cap; + host_vendor_cap ven_cap; + host_feature feature; + + host_cmd_req_t *cmd_req; + + u16 sdma_boundary_val; + u32 sdma_boundary_kb; + /* 64bit DMA enable */ + byte bit64_enable; + byte sd_host4_enable; + bool led_on; + bool uhs2_flag; + bool sd_express_flag; + bool dump_mode; + /* NON INTERRUPT */ + bool poll_mode; + atomic_t clkreqn_status; + + void *pdx; + output_tuning_t output_tuning; + u8 cur_output_phase; + + /* ONLY for camera mode: polling card state */ +#define CARD_INSERTED 1 +#define CARD_DESERTED 0 + bool camera_mode_card_state; + +} sd_host_t; + +typedef struct { + u32 trans_mode; + u32 payload[5]; + u32 block_cnt; + u32 block_size; +} host_trans_reg_t; + +#endif diff --git a/drivers/scsi/bht/include/hostapi.h b/drivers/scsi/bht/include/hostapi.h new file mode 100644 index 000000000000..93864b53c87a --- /dev/null +++ b/drivers/scsi/bht/include/hostapi.h @@ -0,0 +1,170 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: hostapi.h + * + * Abstract: This file is used to define interface Standard Host Operation + * + * Version: 1.00 + * + * Author: Peter.Guo + * + * Environment: OS Independent + * + * History: + * + * 8/25/2014 Creation Peter.Guo + */ + +#ifndef _HOSTAPI_H +#define _HOSTAPI_H + +#include "host.h" +/* + * Below 2 function called pm function to save and restore host setting + */ +void host_backup_register(sd_host_t *host); +void host_restore_register(sd_host_t *host); + +/* + * Init host capability + */ +void host_init_capbility(sd_host_t *host); + +/* + * Init emmc host clock to 400K + */ + +/* + * This function called by showdown os + */ +void host_shutdown(sd_host_t *host); + +/* + * called by req_global_init, host vendor init + */ +void host_init(sd_host_t *host); +void host_uninit(sd_host_t *host, bool disable_all_int); +void host_poweroff(sd_host_t *host, e_card_type type); +void host_force_pll_enable(sd_host_t *host, bool force); + +/* + * Api to all card + */ +void host_led_ctl(sd_host_t *host, bool on); +typedef enum { + BUS_WIDTH1 = 0, + BUS_WIDTH4, + BUS_WIDTH8 +} e_bus_width; + +typedef enum { + SIG_VOL_33 = 0, + SIG_VOL_18, + SIG_VOL_12 +} e_sig_vol; + +void host_set_buswidth(sd_host_t *host, e_bus_width e_width); +void host_1_8v_sig_set(sd_host_t *host, bool enable); +void host_sig_vol_set(sd_host_t *host, e_sig_vol sig_vol); + +/* + * emmc card host register setting + */ +bool host_emmc_init(sd_host_t *host, cfg_emmc_mode_t *emmc_mode); +void host_emmc_hs400_set(sd_host_t *host, bool b_hs400); +void host_emmc_ddr_set(sd_host_t *host, bool b_ddr); + +/* Set the host timing */ + +void host_set_highspeed(sd_host_t *host, bool on); +void host_change_clock(sd_host_t *host, u32 value); +void host_set_uhs_mode(sd_host_t *host, byte access_mode); + +bool host_enable_sd_signal18v(sd_host_t *host); +void host_sd_init(sd_host_t *host); + +void host_transfer_init(sd_host_t *host, bool enable_infinite, + bool force_adma); +void host_cmddat_line_reset(sd_host_t *host); + +void host_int_dis_sig_all(sd_host_t *host, bool all); + +bool host_error_int_recovery_stage2(sd_host_t *host, u16 error_int_state); + +/* + * UHS2 only apis + */ +bool host_uhs2_phychk(sd_host_t *host, bool fromslp, bool *stblfail); +void host_uhs2_init(sd_host_t *host, u32 clkvalue, bool bfullreset); +void host_uhs2_clear(sd_host_t *host, bool breset); +void host_uhs2_cfg_set(sd_host_t *host, uhs2_info_t *setting, bool stage2); +bool host_uhs2_resume_dmt(sd_host_t *host, bool hbr); +bool host_uhs2_go_dmt(sd_host_t *host, bool hbr); +void host_uhs2_reset(sd_host_t *host, bool fullreset); +void host_set_tuning_mode(sd_host_t *host, bool hw_mode); +bool host_chk_tuning_comp(sd_host_t *host, bool hwtuning); + +/* Interrupt operation */ + +void host_int_sig_dis(sd_host_t *host, u32 int_val); +void host_int_clr_status(sd_host_t *host); + +void host_int_en_cdc(sd_host_t *host); +bool host_wr_protect_pin(sd_host_t *host); + +void host_enable_cmd23(sd_host_t *host, bool enable); + +u32 sdhci_readl(sd_host_t *host, u16 offset); +void sdhci_writel(sd_host_t *host, u16 offset, u32 value); +void sdhci_or32(sd_host_t *host, u16 offset, u32 value); +void sdhci_and32(sd_host_t *host, u16 offset, u32 value); +void sdhci_writew(sd_host_t *host, u16 offset, u16 value); +u16 sdhci_readw(sd_host_t *host, u16 offset); +void sdhci_or16(sd_host_t *host, u16 offset, u16 value); +void sdhci_and16(sd_host_t *host, u16 offset, u16 value); + +/* PCI config register accessing */ + +void pci_port_writel(sd_host_t *host, u32 port, u32 data); + +u32 pci_port_readl(sd_host_t *host, u32 port); +u32 pci_get_bus_data(sd_host_t *host); + +u16 pci_readw(sd_host_t *host, u16 offset); +void pci_writew(sd_host_t *host, u16 offset, u16 value); +void pci_orw(sd_host_t *host, u16 offset, u16 value); +void pci_andw(sd_host_t *host, u16 offset, u16 value); + +void pci_writel(sd_host_t *host, u16 offset, u32 value); +u32 pci_readl(sd_host_t *host, u16 offset); +void pci_orl(sd_host_t *host, u16 offset, u32 value); +void pci_andl(sd_host_t *host, u16 offset, u32 value); + +u16 ven_readw(sd_host_t *host, u16 offset); +void ven_writew(sd_host_t *host, u16 offset, u16 value); +void ven_or16(sd_host_t *host, u16 offset, u16 value); +void ven_and16(sd_host_t *host, u16 offset, u16 value); + +u32 ven_readl(sd_host_t *host, u16 offset); +void ven_writel(sd_host_t *host, u16 offset, u32 value); + +void ven_or32(sd_host_t *host, u16 offset, u32 value); +void ven_and32(sd_host_t *host, u16 offset, u32 value); + +void pci_cfgio_writel(sd_host_t *host, u16 offset, u32 value); + +bool host_check_lost(sd_host_t *host); +void host_reset(sd_host_t *host, u32 resetmode); +void host_set_output_tuning_phase(sd_host_t *host, u32 phase); +void host_int_sig_update(sd_host_t *host, u32 int_val); +void host_uhs2_err_sig_update(sd_host_t *host, u32 int_val); +void host_check_card_insert_desert(sd_host_t *host); + +void set_pattern_value(sd_host_t *host, u8 value); +void shif_byte_pattern_bit_set(sd_host_t *host, bool bit_en, u8 pattern_case); +void set_gpio_levels(sd_host_t *host, bool gpio_num, bool signal_level); +bool shift_bit_func_enable(sd_host_t *host); +void power_control_with_card_type(sd_host_t *host, u8 vddx, bool power_en); +#endif diff --git a/drivers/scsi/bht/include/hostvenapi.h b/drivers/scsi/bht/include/hostvenapi.h new file mode 100644 index 000000000000..c0b184e0021b --- /dev/null +++ b/drivers/scsi/bht/include/hostvenapi.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: hostvenapi.h + * + * Abstract: This File is used to define interface for BHT host operations + * + * Version: 1.00 + * + * Author: Peter.Guo + * + * Environment: OS Independent + * + * History: + * + * 8/25/2014 Creation Peter.Guo + */ + +bool hostven_dll_input_tuning_init(sd_host_t *host); +bool hostven_fix_output_tuning(sd_host_t *host, byte access_mode); +u8 hostven_tuning_type_selection(sd_host_t *host, byte sd_access_mode); +void hostven_optphaserw(sd_host_t *host, u8 output_opt, u8 input_opt); +void hostven_set_tuning_phase(sd_host_t *host, u32 input_n1, u32 output_n1, + bool off); +void hostven_set_output_tuning_phase(sd_host_t *host, u32 value, bool off); +void hostven_detect_refclk_count_range_init(sd_host_t *host); +void hostven_refclk_stable_detection_circuit(sd_host_t *host); +void hostven_pcie_phy_tx_amplitude_adjustment(sd_host_t *host); + +bool hostven_chk_card_present(sd_host_t *host); + +/* + * init host type and feature + */ +void host_vendor_feature_init(sd_host_t *host); + +bool hostven_chip_type_check(sd_host_t *host); + +bool hostven_rtd3_check(sd_host_t *host); +void hostven_pm_mode_cfg(sd_host_t *host, pm_state_t *pm); +u32 hostven_d3_mode_sel(sd_host_t *host, u32 *d3_submode); +void hostven_main_power_ctrl(sd_host_t *host, bool is_keep_on); + +void hostven_hw_timer_stop(sd_host_t *host); +void hostven_hw_timer_start(sd_host_t *host, u32 time_ms); +void hostven_set_pml0_requrest(sd_host_t *host, bool enable); diff --git a/drivers/scsi/bht/include/osapi.h b/drivers/scsi/bht/include/osapi.h new file mode 100644 index 000000000000..0cf25be3f58e --- /dev/null +++ b/drivers/scsi/bht/include/osapi.h @@ -0,0 +1,201 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: osapi.h + * + * Abstract: This Include file used to define os independent apis + * + * Version: 1.00 + * + * Author: Peter.Guo + * + * Environment: OS Independent + * + * History: + * + * 8/25/2014 Creation Peter.Guo + */ + +#ifndef _OSAPI_H +#define _OSAPI_H + +#define MAX_TIMER_NUM 1 + +#if CFG_OS_LINUX + +typedef enum { + EVENT_CARD_CHG = 0, + EVENT_TAG_IO, + EVENT_GEN_IO, + EVENT_RUNTIME_D3, + EVENT_AUTO_TIMER, + EVENT_SDIO, + EVENT_TERMINATE, + EVENT_PENDING, + EVENT_NONE +} e_event_t; + +#else + +typedef enum { + /* one event with task id solution. */ + EVENT_TASK_OCCUR = 0, + EVENT_NONE +} e_event_t; + +#endif + +typedef enum { + TASK_CARD_CHG = 0, + TASK_TERMINATE, + TASK_PENDING, + TASK_TAG_IO, + TASK_GEN_IO, + TASK_RUNTIME_D3, + TASK_AUTO_TIMER, + TASK_SDIO, + TASK_CAMOD_POLL_CARD_CHG, + TASK_NONE +} e_task_t; + +typedef enum { + TIMER_AUTO = 0, + TIMER_SUBID = 1 +} e_timer_t; + +#include "../linux_os/linux_api.h" + +typedef struct { + /* for Bar0 access */ + void __iomem *membase; + /* for Bar1 mem access */ + void __iomem *membase2; + struct pci_dev *pci_dev; + byte irq; + bool use_msi; +} t_pci_dev; + +typedef t_linux_os_struct os_struct; +typedef struct list_head list_entry; +typedef linux_completion_t completion_t; +typedef linux_list_t list_t; + +#define os_atomic_add(p, i) atomic_add(i, p) +#define os_atomic_sub(p, i) atomic_sub(i, p) +#define os_atomic_read(p) atomic_read(p) +#define os_atomic_set(p, i) atomic_set(p, i) + +#define os_container_of(p, type, member) container_of(p, type, member) + +typedef void (*thread_cb_t)(void *param); + +#if CFG_OS_LINUX +void os_set_event(os_struct *os, e_event_t event); +void os_clear_event(os_struct *os, e_event_t event); +e_event_t os_wait_event(os_struct *os); +bool os_create_thread(thread_t *thr, void *param, thread_cb_t func); +void os_list_init(list_t *p); +void os_sleep(u32 time_ms); + +#else +void os_set_event(void *pdx, os_struct *os, e_event_t event, e_task_t taskid); +void os_clear_event(void *pdx, os_struct *os, e_event_t event); +e_event_t os_wait_event(void *pdx, os_struct *os); +bool os_create_thread(void *pdx, thread_t *thr, void *param, thread_cb_t func); +void os_list_init(void *pdx, list_t *p); +void os_sleep(void *pdx, u32 time_ms); +#endif + +bool os_thread_is_freeze(void *pdx); +bool os_stop_thread(os_struct *os, thread_t *thr); +void os_kill_thread(os_struct *os, thread_t *thr); +bool os_pending_thread(void *pdx, bool pending); +u64 os_get_performance_tick(u64 *cpu_freq); + +/* + * timeout 0 means wait infinite + * timeout is in milli second + */ +void os_init_completion(void *pdx, completion_t *p); +void os_finish_completion(void *pdx, completion_t *completion); +bool os_wait_for_completion(void *pdx, completion_t *completion, s32 timeout); + +/* os list ops */ + +list_entry *os_list_locked_remove_head(list_t *p); +void os_list_locked_insert_tail(list_t *p, list_entry *entry); +void os_list_locked_insert_head(list_t *p, list_entry *entry); + +void os_set_dev_busy(void *pdx); +void os_set_dev_idle(void *pdx); + +/* + * This is called by req_global_init and req_global_uinit + */ +bool os_layer_init(void *pdx, os_struct *os); +bool os_layer_uinit(void *pdx, os_struct *os); + +void os_start_timer(void *pdx, os_struct *os, e_timer_t t, u32 time_ms); +void os_cancel_timer(void *pdx, os_struct *os, e_timer_t t); +void os_stop_timer(void *pdx, os_struct *os, e_timer_t t); + +void os_start_timer_s3s4(void *p, os_struct *os, e_timer_t t, u32 time_ms); + +bool os_alloc_dma_buffer(void *pdx, void *ctx, u32 nbytes, + dma_desc_buf_t *dma_buff); + +bool os_free_dma_buffer(void *pdx, dma_desc_buf_t *dma_buff); + +u32 os_get_cur_tick(void); +bool os_is_timeout(u32 start_tck, u32 time_ms); + +void os_udelay(u32 time_us); +void os_mdelay(u32 time_us); + +void os_print(byte *s); + +void *os_alloc_vbuff(u32 length); +void os_free_vbuff(void *vbuff); + +u32 os_get_phy_addr32l(phy_addr_t phy_addr); +u32 os_get_phy_addr32h(phy_addr_t phy_addr); +u64 os_get_phy_addr64(phy_addr_t phy_addr); + +void os_set_phy_addr32l(phy_addr_t *phy_addr, u32 addr); +void os_set_phy_addr32h(phy_addr_t *phy_addr, u32 addr); + +void os_set_phy_add64(phy_addr_t *phy_addr, u64 addr); + +void os_memcpy(void *dbuf, void *sbuf, s32 len); +void os_memset(void *buffer, byte fill, s32 len); + +s32 os_memcpr(void *dbuf, void *sbuf, s32 len); + +u32 os_get_sg_list(void *pdx, scsi_srb *Srb, sg_list_t *srb_sg_list); + +void os_cfg_load(void *cfg_item, e_chip_type chip_type); + +void os_pm_init(void *dev_evt); + +void os_random_init(void); +u32 os_random_get(u32 max); + +/* + * Bus related api + */ +void os_bus_change(void *pdx); + +void os_set_sdio_val(void *p, u8 val, bool need_set_did); + +void os_rtd3_req_wait_wake(void *pdx); + +bool os_pcr_pesistent_restore(u16 *addr_tb, u32 *val_tb, u32 tb_len); + +bool os_pcr_pesistent_save(u16 *addr_tb, u32 *val_tb, u32 tb_len); + +typedef void (*cb_enum_reg_t)(void *cfg, u32 type, u32 idx, u32 addr, + u32 value); +void os_enum_reg_cfg(void *cfg, e_chip_type chip_type, const byte *ustr, + cb_enum_reg_t func); +#endif diff --git a/drivers/scsi/bht/include/reqapi.h b/drivers/scsi/bht/include/reqapi.h new file mode 100644 index 000000000000..746a2d68e055 --- /dev/null +++ b/drivers/scsi/bht/include/reqapi.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: reqapi.h + * + * Abstract: This File is used to declare interface for OSEntry layer + * + * Version: 1.00 + * + * Author: Peter.Guo + * + * Environment: OS Independent + * + * History: + * + * 8/25/2014 Creation Peter.Guo + */ + +/* + * uninit os related structure: thread, timer, buffer, tagqueue + */ +s32 req_global_uninit(bht_dev_ext_t *pdx); + +/* + * init thread, timer, buffer, tagqueue + * host cap init, and vendor reg setting + * host interrupt and init to workable status + * card and host software structure init + * caller init pci_dev_t and global cfg + */ +s32 req_global_init(bht_dev_ext_t *pdx); +void req_global_reinit(bht_dev_ext_t *pdx); + +/* + * pm related function + */ +void failsafe_fct(bht_dev_ext_t *pdx); +void req_enter_d3(bht_dev_ext_t *pdx); +void req_enter_d0(bht_dev_ext_t *pdx); +void req_enter_d0_sync(bht_dev_ext_t *pdx); +void req_pre_enter_d3(bht_dev_ext_t *pdx); + +void pcie_weakup(bht_dev_ext_t *pdx, u32 Sx_flag, bool enable); + +void thread_handle_card_event(bht_dev_ext_t *pdx); +e_req_result thread_wakeup_card(bht_dev_ext_t *pdx); +/* + * Handle + */ +s32 req_os_shutdown(bht_dev_ext_t *pdx); + +e_req_result req_tag_io_add(bht_dev_ext_t *pdx, srb_ext_t *srb_ext); +e_req_result req_gen_io_add(bht_dev_ext_t *pdx, srb_ext_t *srb_ext); + +void req_bus_reset(bht_dev_ext_t *pdx); + +bool req_card_ready(bht_dev_ext_t *pdx); + +e_req_result req_eject(bht_dev_ext_t *pdx, srb_ext_t *srb_ext); + +e_req_result req_chk_card_info(bht_dev_ext_t *pdx, srb_ext_t *srb_ext); + +void req_cancel_all_io(bht_dev_ext_t *pdx); + +void thread_main(void *param); +bool thread_is_lock(bht_dev_ext_t *pdx, e_event_t event); +#define GEN_IO_CODE_INIT_CARD 0 +#define GEN_IO_CODE_EJECT 1 +#define GEN_IO_CODE_PIORW 2 +#define GEN_IO_CODE_CPRM 3 +#define GEN_IO_CODE_IO 4 +#define GEN_IO_CODE_NSM 5 + +#define GEN_IO_CODE_RECFG 6 +#define GEN_IO_CODE_CSD 7 + +#define ENTRY_S3 3 +#define ENTRY_S4 4 +#define ENTRY_S5 5 diff --git a/drivers/scsi/bht/include/tq.h b/drivers/scsi/bht/include/tq.h new file mode 100644 index 000000000000..dd876966b3f1 --- /dev/null +++ b/drivers/scsi/bht/include/tq.h @@ -0,0 +1,159 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: tq.h + * + * Abstract: define tag queue related struct + * + * Version: 1.00 + * + * Environment: OS Independent + * + */ + +#ifndef _TQ_HEADER_ +#define _TQ_HEADER_ + +#include "transh.h" + +/* reserved one node for internal use(currently for adma2 infinite link issue need one). */ +#define TQ_RESERVED_NODE_SIZE 1 + +/* TagQueueStateType; */ +typedef enum { + QUEUE_STATE_IDLE = 0, + QUEUE_STATE_BUILD, + QUEUE_STATE_READY, + QUEUE_STATE_WAIT_CPL, +} e_tq_state; + +typedef enum { + TQ_BUILD_IO_OK = 0, + TQ_BUILD_IO_ERROR, + TQ_BUILD_IO_EMPTY, +} e_build_ctx_result; + +typedef enum { + QUEUE_EVT_GET = 0, + QUEUE_EVT_BUILD_OK, + QUEUE_EVT_BUILD_FAILED, + QUEUE_EVT_ISSUE, + QUEUE_EVT_DONE, + QUEUE_EVT_FORCE_FAILED, + QUEUE_EVT_ABORT, + QUEUE_EVT_INIT, +} e_tq_state_evt; + +typedef struct { + e_data_dir data_dir; + u32 sec_addr; + u32 sec_cnt; +} req_card_access_info_t; + +typedef struct { + /* request list */ + list_t list; + /* id name */ + u32 id; + /* below is work queue member */ + e_tq_state state; + /* adma2 */ + req_card_access_info_t adma2_last_req; + dma_desc_buf_t adma3_integrate_tbl; + dma_desc_buf_t adma3_integrate_tbl_cur; + /* data mgr */ + sd_data_t sd_data; + /* private ctx */ + void *priv; +} req_queue_t; + +typedef bool (*req_queue_node_ops_ctx_cb)(node_t *, void *ctx); +typedef bool (*storage_cmd_cb)(void *pdx, node_t *node); +typedef bool (*storage_cb)(void *pdx); + +typedef struct { + /* Init transfer info and policy */ + storage_cb init_io; + /* build adma2 desc table only */ + storage_cmd_cb prebuild_io; + /* build adma2 desc table only */ + storage_cmd_cb build_io; + /* merage adma 2 table */ + storage_cb merge_io; + /* build cmd reg, amda3 cmd table and adma3 integate table */ + storage_cb issue_transfer; + storage_cb unload; + storage_cb poweroff_need_rebuild; +} transfer_cb_t; + +#define TAG_QUEUE_INIT_MAGIC_NUMBER 0x55AA3344 + +#define MAX_DECISION_SCOPE_SIZE 8 + +typedef void (*host_dma_mode_selector_cb)(void *tq, bool flg); + +typedef struct { + bool slot[MAX_DECISION_SCOPE_SIZE]; + int scope; + int idx; + /* output */ + bool out; + bool up_flg; + int up_thd; + int low_thd; + req_card_access_info_t last_req; + + host_dma_mode_selector_cb dma_selector_cb; +} decision_mgr; + +typedef struct { + /* node memory pool */ + /* node memory resources management (for below node array) */ + list_t node_pool_list; + /* internal use */ + node_t node[MAX_WORK_QUEUE_SIZE]; + + /* IO transfer strategy controller */ + u32 max_wq_req_size; + u32 max_build_limit; + /* pointer to cmd_req.done */ + completion_t *tran_cpl; + /* IO transfer strategy controller end */ + + atomic_t req_cnt; + /* SRBs Request Queue */ + req_queue_t req_queue; + + /* work queue start */ + /* pointer to current queue */ + req_queue_t *wq_cur; + /* build queue */ + req_queue_t *wq_build; + req_queue_t work_queues[TQ_WORK_QUEUE_SIZE]; + /* work queue end */ + + u8 *adma2_inf_link_addr; + host_cmd_req_t cmd_req; + /* tq fake cmd, only use for fake cmd */ + sd_command_t priv_fake_cmd; + + /* for anti-uninit use */ + u32 init_magic_number; + bool hw_idle; + /* init 0 */ + u32 queue_id_seed; + + /* transfer ops */ + transfer_cb_t ops; + /* dma mode */ + u32 cur_dma_mode; + atomic_t target_dma_mode; + u32 cfg_dma_mode; + decision_mgr decision; + +} tag_queue_t; + +#define TQ_MAX_RECOVERY_ERROR_RETRY_TIMES 2 + +#endif diff --git a/drivers/scsi/bht/include/tqapi.h b/drivers/scsi/bht/include/tqapi.h new file mode 100644 index 000000000000..5c24100a6c69 --- /dev/null +++ b/drivers/scsi/bht/include/tqapi.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: tqapi.h + * + * Abstract: This File is used to declare interface for TagQueue + * + * Version: 1.00 + * + * Author: Peter.Guo + * + * Environment: OS Independent + * + * History: + * + * 8/25/2014 Creation Peter.Guo + */ + +#ifndef _TQ_API_H_ +#define _TQ_API_H_ + +/* Start IO */ +e_req_result tq_add_request(bht_dev_ext_t *pdx, srb_ext_t *srb_ext, + sd_card_t *card); +bool tq_is_empty(bht_dev_ext_t *pdx); + +/* + * This function is called req_global_init + */ +bool tag_queue_init(bht_dev_ext_t *pdx); + +e_req_result tag_queue_rw_data(bht_dev_ext_t *pdx); +void tag_queue_abort(bht_dev_ext_t *pdx, e_req_result result); + +#endif diff --git a/drivers/scsi/bht/include/transh.h b/drivers/scsi/bht/include/transh.h new file mode 100644 index 000000000000..97bafd5c68f1 --- /dev/null +++ b/drivers/scsi/bht/include/transh.h @@ -0,0 +1,132 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: transh.h + * + * Abstract: define related macro about transmission + * + * Version: 1.00 + * + * Environment: OS Independent + * + */ + +#ifndef _TRANS_HD_H +#define _TRANS_HD_H + +/* OS platform */ +#define DMA_BUF_ALIGN_SIZE (1<<12) + +/* HOST attr */ +#define SDHCI_CPAS_ADMA2_26BIT_LEN (1<<0) + +/* general desc */ +#define GENERAL_DESC_END_BIT 0x2 + +/* ADMA2 */ +/* 1. attr */ +#define ADMA2_DESC_LINK_VALID 0x31 +#define ADMA2_DESC_TRAN_VALID 0x21 +#define ADMA2_DESC_INT_VALID 0x5 + +#define ADMA2_DESC_END_BIT 0x2 +#define ADMA2_DESC_INT_BIT 0x4 + +/* 2. user ext */ +#define ADMA2_16BIT_LEN_SIZE (1<<16) +#define ADMA2_26BIT_LEN_SIZE (1<<26) + +#define ADMA2_ITEM_LEN 8 +#define ADMA2_128BIT_ITEM_LEN 16 +#define MAX_ADMA2_ITEM_LEN (ADMA2_128BIT_ITEM_LEN) + +/* ADMA3 */ +/* 1. attr */ +#define ADMA3_INTEGRATE_DESC_VALID 0x39 + +#define ADMA3_DESC_SD_VALID 0x9 +#define ADMA3_DESC_UHS2_VALID 0x19 +#define ADMA3_DESC_SD_END_VALID 0xB + +/* 2. ADMA3 */ +#define RESEVED_ADMA3_INTEGRATEDDESC_LINE_SIZE 2 + +#define ADMA3_INTEGRATEDDESC_ITEM_LEN 8 +#define ADMA3_INTEGRATEDDESC_128BIT_ITEM_LEN 16 +#define MAX_ADMA3_INTEGRATE_ITEM_LEN (ADMA3_INTEGRATEDDESC_128BIT_ITEM_LEN) + +#define ADMA3_CMDDESC_ITEM_LENGTH 8 +#define ADMA3_CMDDESC_ITEM_NUM_UHSI 4 +#define ADMA3_CMDDESC_ITEM_NUM_UHSII 8 + +#define MAX_ADMA3_INTERGATE_TABLE_LEN_PER_QUEUE_PER_NODE \ + (MAX_ADMA3_INTEGRATE_ITEM_LEN * (1+RESEVED_ADMA3_INTEGRATEDDESC_LINE_SIZE)) +#define MAX_ADMA3_INTERGATE_TABLE_LEN_PER_NODE \ + (MAX_ADMA3_INTERGATE_TABLE_LEN_PER_QUEUE_PER_NODE * TQ_WORK_QUEUE_SIZE) + +/* MAX */ + +/* 1. SDMA */ +#define MAX_SDMA_LIKE_DATA_SIZE (CFG_MAX_TRANSFER_LENGTH) + +/* 2. desc buffer */ +#define RESEVED_ADMA2_DESC_LINE_SIZE 128 + +#define MAX_ADMA2_TABLE_LEN ((ADMA2_MAX_DESC_LINE_SIZE)*(ADMA2_ITEM_LEN)) +#define MAX_ADMA2_128BIT_TABLE_LEN ((ADMA2_MAX_DESC_LINE_SIZE)*(ADMA2_128BIT_ITEM_LEN)) +#define MAX_GENERAL_DESC_TABLE_LEN ((ADMA2_MAX_DESC_LINE_SIZE+RESEVED_ADMA2_DESC_LINE_SIZE)*MAX_ADMA2_ITEM_LEN) + +#define MAX_DUMP_MODE_DESC_SIZE (16*1024) + +#define MAX_ADMA3_CMDDESC_TABLE_LEN (ADMA3_CMDDESC_ITEM_LENGTH*ADMA3_CMDDESC_ITEM_NUM_UHSII) + +/* 3.NODE */ + +#define MAX_NODE_BUF_SIZE (MAX_ADMA3_INTERGATE_TABLE_LEN_PER_NODE + MAX_ADMA3_CMDDESC_TABLE_LEN +\ + MAX_GENERAL_DESC_TABLE_LEN + DMA_BUF_ALIGN_SIZE) +#define MAX_SDMA_LIKE_MODE_NODE_BUF_SIZE (MAX_NODE_BUF_SIZE + MAX_SDMA_LIKE_DATA_SIZE) + +/* DMA API */ +#define DMA_API_BUF_SIZE (64*1024) +#define MIN_DMA_API_BUF_SIZE (MAX_GENERAL_DESC_TABLE_LEN+DMA_BUF_ALIGN_SIZE + DMA_API_BUF_SIZE) + +/* this is used for tag queue */ +typedef struct s_note_t { + void *data; + void *psrb_ext; + /* bind to card */ + sd_card_t *card; + list_entry list; + struct { + /* poiner to adma2 table */ + dma_desc_buf_t head; + /* pointer to adma2 table */ + dma_desc_buf_t end; + } phy_node_buffer; + dma_desc_buf_t general_desc_tbl; + /* dbg use */ + dma_desc_buf_t general_desc_tbl_img; + /* sdma like mode */ + dma_desc_buf_t data_tbl; + /* dbg use */ + dma_desc_buf_t data_tbl_img; + + /* virt buffer is not merged */ + virt_buffer_t v_buff; + + /* flag 0 means it is attahment to a cmd, 1 means it has been merged */ + byte flag; + u8 sdma_like:1; +} node_t, *pnode_t; + +typedef struct { + node_t dma_node; + /* Add this node for ADMA2 Infinte API */ + node_t dma_node2; + node_t *cur_node; + u8 *adma2_inf_link_addr; + +} dma_trans_api_t; + +#endif diff --git a/drivers/scsi/bht/include/transhapi.h b/drivers/scsi/bht/include/transhapi.h new file mode 100644 index 000000000000..2af824165e09 --- /dev/null +++ b/drivers/scsi/bht/include/transhapi.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: transhapi.h + * + * Abstract: declare related API about transmission + * + * Version: 1.00 + * + * Environment: OS Independent + * + */ + +#ifndef _TRANS_HD_API_H_ +#define _TRANS_HD_API_H_ + +bool dma_api_io_init(bht_dev_ext_t *pdx, dma_desc_buf_t *desc_buf); + +bool build_dma_ctx(void *pdx, sd_data_t *sd_data, + u32 cmdflag, + e_data_dir dir, + byte *data, u32 datalen, sg_list_t *sglist, u32 sg_len); + +#endif diff --git a/drivers/scsi/bht/include/util.h b/drivers/scsi/bht/include/util.h new file mode 100644 index 000000000000..ed2551c502e5 --- /dev/null +++ b/drivers/scsi/bht/include/util.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: util.h + * + * Abstract: This Include file used to define platform independent APIs + * + * Version: 1.00 + * + * Author: Peter.Guo + * + * Environment: OS Independent + * + * History: + * + * 8/25/2014 Creation Peter.Guo + */ + +#ifndef _UTIL_H +#define _UTIL_H + +u32 swapu32(u32 value); + +bool resize_dma_buf(dma_desc_buf_t *p, u32 sz); +void dump_dma_buf(char *str, const dma_desc_buf_t *pdma); +void pa_offset_pa(phy_addr_t *pa, u32 offset); + +u32 fls32(u32 val); + +bool random_percent_check(u32 percent); + +void util_init_waitloop(void *pdx, u32 timeout_ms, u32 per_us, + loop_wait_t *wait); +bool util_is_timeout(loop_wait_t *wait); + +#endif From patchwork Fri Oct 13 08:34:15 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: liuchang_125125@163.com X-Patchwork-Id: 13420432 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9E1C9CDB47E for ; Fri, 13 Oct 2023 08:39:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230230AbjJMIjI (ORCPT ); Fri, 13 Oct 2023 04:39:08 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36698 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230221AbjJMIjF (ORCPT ); Fri, 13 Oct 2023 04:39:05 -0400 Received: from m12.mail.163.com (m12.mail.163.com [220.181.12.199]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 75B0AA9; Fri, 13 Oct 2023 01:38:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:Subject:Date:Message-Id:MIME-Version; bh=MDBOJ KfUuh4/VHlviBX0aVYr7w0hhC5DNatuWUc75DQ=; b=gVjozwGojhuqXRl8JGHdq STxPZQk+ul7tKee+bO38GKqQ2hEQWaDir4OcopTT+ufmsHSNBsKwOwR5iFypwHSn RIM/vuiYySNoBNh/ceWkEAj25Wpk78ioNUH8+b50PoUXyZMCmHgCp/XR4GD+wwNq h6gRwKYxP4LiO3R3gy/etw= Received: from test-Z390-GAMING-X.bayhubtech.com (unknown [58.48.115.170]) by zwqz-smtp-mta-g3-1 (Coremail) with SMTP id _____wDnDxgMASllSyO8AQ--.5797S2; Fri, 13 Oct 2023 16:34:20 +0800 (CST) From: liuchang_125125@163.com To: jejb@linux.ibm.com, martin.petersen@oracle.com, linux-scsi@vger.kernel.org, linux-kernel@vger.kernel.org Cc: mark.tao@bayhubtech.com, shaper.liu@bayhubtech.com, thomas.hu@bayhubtech.com, chevron.li@bayhubtech.com, charl.liu@bayhubtech.com, Charl Liu Subject: [PATCH 6/9] scsi: bht: linux_os: Add the source files related to SCSI frame and driver entry Date: Fri, 13 Oct 2023 16:34:15 +0800 Message-Id: <20231013083415.10330-1-liuchang_125125@163.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 X-CM-TRANSID: _____wDnDxgMASllSyO8AQ--.5797S2 X-Coremail-Antispam: 1Uf129KBjvAXoWkJFWfZFyxXr15AFyDCrWruFg_yoWDuryfGo WfZFsxtwn5JryUuw1vkr1xtFy7XrZa9FyYyr4rAr4kua1kArZ0gr9Fkw43Ga43Jr40grZr X3s7Ca4xtFZ8trn3n29KB7ZKAUJUUUUU529EdanIXcx71UUUUU7v73VFW2AGmfu7bjvjm3 AaLaJ3UbIYCTnIWIevJa73UjIFyTuYvj4RiPEfDUUUU X-Originating-IP: [58.48.115.170] X-CM-SenderInfo: polxux5dqjsiqsvrjki6rwjhhfrp/xtbB0BIIWWEssyseLQAAs1 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org From: Charl Liu 1.linux_api: package system basic functions 2.linux_base: define device & pci initialization functions 3.linux_scsi: define related functions for adapting SCSI frame Signed-off-by: Charl Liu --- Change in V1: Add the source files related to SCSI frame and driver entry. --- drivers/scsi/bht/linux_os/linux_api.c | 2207 ++++++++++++++++++++++++ drivers/scsi/bht/linux_os/linux_api.h | 90 + drivers/scsi/bht/linux_os/linux_base.c | 985 +++++++++++ drivers/scsi/bht/linux_os/linux_scsi.c | 1076 ++++++++++++ drivers/scsi/bht/linux_os/linux_scsi.h | 239 +++ 5 files changed, 4597 insertions(+) create mode 100644 drivers/scsi/bht/linux_os/linux_api.c create mode 100644 drivers/scsi/bht/linux_os/linux_api.h create mode 100644 drivers/scsi/bht/linux_os/linux_base.c create mode 100644 drivers/scsi/bht/linux_os/linux_scsi.c create mode 100644 drivers/scsi/bht/linux_os/linux_scsi.h diff --git a/drivers/scsi/bht/linux_os/linux_api.c b/drivers/scsi/bht/linux_os/linux_api.c new file mode 100644 index 000000000000..be7b44b641a3 --- /dev/null +++ b/drivers/scsi/bht/linux_os/linux_api.c @@ -0,0 +1,2207 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: linux_api.c + * + * Abstract: Linux api definition + * + * Version: 1.00 + * + * Author: Peter + * + * Environment: Linux + * + * History: + * + * 5/20/2015 Creation Peter.Guo + */ + +#include +#include "../include/basic.h" +#include "../include/debug.h" +#include "../include/util.h" +#include "../include/funcapi.h" +#include "../include/hostapi.h" +#include "../include/hostvenapi.h" +#include +#include +#include +#include +#include "../linux_os/linux_scsi.h" +#if BHT_LINUX_ENABLE_RTD3 +#include +#endif + +/* ----------linux Module param management---------- */ + +/* This Flag is used to control emmc */ +static ulong m_emmc_mode; +module_param(m_emmc_mode, ulong, 0444); + +/* Use to select dma mode, adma2 adma3 */ +static ulong m_dma_mode; +module_param(m_dma_mode, ulong, 0444); + +/* use to enable or disable infinite transfer */ +static ulong m_infinite_ctrl = 0x8000000f; +module_param(m_infinite_ctrl, ulong, 0444); + +/* use for tag queue capability */ +static ulong m_tag_cap; +module_param(m_tag_cap, ulong, 0444); + +/* use for sdmode disable */ +static ulong m_sdmode_dis; +module_param(m_sdmode_dis, ulong, 0444); + +/* use to disable mmc support */ +static ulong m_mmc_dis; +module_param(m_mmc_dis, ulong, 0444); + +/* uhs2 range/half/low select */ +static ulong m_uhs2_ctrl = 0x97400012; +module_param(m_uhs2_ctrl, ulong, 0444); + +/* output tuning control */ +static ulong m_output_tuning = 0x98030036; +module_param(m_output_tuning, ulong, 0444); + +/* auto stop infinite control */ +static ulong m_auto_stopinf; +module_param(m_auto_stopinf, ulong, 0444); + +/* auto poweroff card function */ +static ulong m_auto_poweroff; +module_param(m_auto_poweroff, ulong, 0444); + +/* uhs2 auto go dormant/hbr function */ +static ulong m_auto_dmt; +module_param(m_auto_dmt, ulong, 0444); + +/* Enable need set to 0x8004_0100 */ +static ulong m_psd_mode = 0x80000000; +module_param(m_psd_mode, ulong, 0444); + +/* Enable need set to 0x8004_0100 */ +static ulong m_sw_sel_inject_sdr50 = 0x67f; +module_param(m_sw_sel_inject_sdr50, ulong, 0444); + +static unsigned int pcr_setting_count; +char *pcr_settings[MAX_PCR_SETTING_SIZE] = { 0 }; + +module_param_array(pcr_settings, charp, &pcr_setting_count, 0444); +MODULE_PARM_DESC(pcr_settings, + "pcr_settings is a reg=value like string list split by comma"); + +static unsigned int dmdn_setting_count; +char *dmdn_settings[MAX_FREQ_SUPP] = { 0 }; + +module_param_array(dmdn_settings, charp, &dmdn_setting_count, 0444); +MODULE_PARM_DESC(dmdn_settings, + "dmdn_settings is a reg=value like string list split by comma"); + +uint m_sd_3v3_clk_driver_strength = 2; +module_param(m_sd_3v3_clk_driver_strength, uint, 0444); + +uint m_sd_3v3_cmddata_driver_strength = 2; +module_param(m_sd_3v3_cmddata_driver_strength, uint, 0444); + +uint m_sd_1v8_clk_driver_strength = 2; +module_param(m_sd_1v8_clk_driver_strength, uint, 0444); + +uint m_sd_1v8_cmddata_driver_strength = 1; +module_param(m_sd_1v8_cmddata_driver_strength, uint, 0444); + +uint m_cnfg_drv; +module_param(m_cnfg_drv, uint, 0444); + +uint m_cnfg_trm_code_tx = 8; +module_param(m_cnfg_trm_code_tx, uint, 0444); + +uint m_cnfg_trm_code_rx = 7; +module_param(m_cnfg_trm_code_rx, uint, 0444); + +uint m_cnfg_rint_code = 9; +module_param(m_cnfg_rint_code, uint, 0444); + +uint m_ram_ema = 0x42492000; +module_param(m_ram_ema, uint, 0444); + +uint m_vdd1_vdd2_source; +module_param(m_vdd1_vdd2_source, uint, 0444); + +uint m_vdd18_debounce_time = 1; +module_param(m_vdd18_debounce_time, uint, 0444); + +uint m_ssc_enable; +module_param(m_ssc_enable, uint, 0444); + +static ulong m_driver_item = 0x8080000a; +module_param(m_driver_item, ulong, 0444); + +/* sd7_sd_mode_switch_control, 300: camera, 100: pc */ +static ulong m_sd7_sdmode_switch_control = 0x00000300; +module_param(m_sd7_sdmode_switch_control, ulong, 0444); + +/* mmc_mode_dis */ +static ulong m_mmc_mode_dis = 0x80000000; +module_param(m_mmc_mode_dis, ulong, 0444); + +/* pcie_wake_setting */ +static ulong m_pcie_wake_setting; +module_param(m_pcie_wake_setting, ulong, 0444); + +/* sd_card_mode_dis */ +static ulong m_sd_card_mode_dis = 0x80000021; +module_param(m_sd_card_mode_dis, ulong, 0444); + +/* vdd_power_source_item */ +static ulong m_vdd_power_source_item = 0x00090a0c; +module_param(m_vdd_power_source_item, ulong, 0444); + +/* hsmux_vcme_enable */ +static ulong m_hsmux_vcme_enable; +module_param(m_hsmux_vcme_enable, ulong, 0444); + +/* test_max_access_mode */ +static ulong m_test_max_access_mode = 0x80000005; +module_param(m_test_max_access_mode, ulong, 0444); + +/* host_drive_strength */ +static ulong m_host_drive_strength = 0x80001125; +module_param(m_host_drive_strength, ulong, 0444); + +/* auto_detect_refclk_counter_range_ctl */ +static ulong m_auto_detect_refclk_counter_range_ctl = 0x80640000; +module_param(m_auto_detect_refclk_counter_range_ctl, ulong, 0444); + +/* refclk_stable_detection_counter1 */ +static ulong m_refclk_stable_detection_counter1 = 0x00000003; +module_param(m_refclk_stable_detection_counter1, ulong, 0444); + +static ulong m_refclk_stable_detection_counter2 = 0x001e044c; +module_param(m_refclk_stable_detection_counter2, ulong, 0444); + +static ulong m_refclk_stable_detection_counter3 = 0x04b024b0; +module_param(m_refclk_stable_detection_counter3, ulong, 0444); + +static ulong m_l1_enter_exit_logic_ctl; +module_param(m_l1_enter_exit_logic_ctl, ulong, 0444); + +/* pcie_phy_amplitude_adjust */ +static ulong m_pcie_phy_amplitude_adjust = 0x0000006a; +module_param(m_pcie_phy_amplitude_adjust, ulong, 0444); + +static ulong m_output_tuning_item = 0xc01837bb; +module_param(m_output_tuning_item, ulong, 0444); + +static ulong m_test_driver_strength_sel; +module_param(m_test_driver_strength_sel, ulong, 0444); + +static ulong m_test_max_power_limit = 0x80000003; +module_param(m_test_max_power_limit, ulong, 0444); + +static ulong m_test_uhs2_setting2 = 0x80000006; +module_param(m_test_uhs2_setting2, ulong, 0444); + +static ulong m_test_sdma_boundary = 0x00000020; +module_param(m_test_sdma_boundary, ulong, 0444); + +static ulong m_test_dma_mode_setting = 0x80000071; +module_param(m_test_dma_mode_setting, ulong, 0444); + +static ulong m_test_ocb_ctrl; +module_param(m_test_ocb_ctrl, ulong, 0444); + +static ulong m_bios_l1_substate = 0x8000000f; +module_param(m_bios_l1_substate, ulong, 0444); + +static ulong m_test_main_ldo_setting; +module_param(m_test_main_ldo_setting, ulong, 0444); + +static ulong m_auto_sleep_control; +module_param(m_auto_sleep_control, ulong, 0444); + +static ulong m_power_wait_time = 0x0024000a; +module_param(m_power_wait_time, ulong, 0444); + +static ulong m_test_write_data_timeout = 0x80001770; +module_param(m_test_write_data_timeout, ulong, 0444); + +static ulong m_test_read_data_timeout = 0x80001770; +module_param(m_test_read_data_timeout, ulong, 0444); + +static ulong m_test_non_data_timeout = 0x800003e8; +module_param(m_test_non_data_timeout, ulong, 0444); + +static ulong m_test_r1b_data_timeout = 0x80001194; +module_param(m_test_r1b_data_timeout, ulong, 0444); + +static ulong m_test_card_init_timeout = 0x800005dc; +module_param(m_test_card_init_timeout, ulong, 0444); + +extern cfg_item_t g_cfg[SUPPORT_CHIP_COUNT][2]; + +struct kmem_cache *srb_ext_cachep; + +/* + * SD Spec Standard Memory register access + */ + +u32 sdhci_readl(sd_host_t *host, u16 offset) +{ + u32 regval = 0; + + regval = readl(host->pci_dev.membase + offset); + DbgInfo(MODULE_SD_HOST, FEATURE_SDREG_TRACER, NOT_TO_RAM, + "%s(0x%08X): 0x%08X\n", __func__, offset, regval); + + return regval; +} + +void sdhci_writel(sd_host_t *host, u16 offset, u32 value) +{ + DbgInfo(MODULE_SD_HOST, FEATURE_SDREG_TRACEW, NOT_TO_RAM, + "%s(0x%08X): 0x%08X\n", __func__, offset, value); + writel(value, host->pci_dev.membase + offset); +} + +void sdhci_or32(sd_host_t *host, u16 offset, u32 value) +{ + u32 regval = 0; + + regval = sdhci_readl(host, offset); + regval |= value; + sdhci_writel(host, offset, regval); +} + +void sdhci_and32(sd_host_t *host, u16 offset, u32 value) +{ + u32 regval = 0; + + regval = sdhci_readl(host, offset); + regval &= (value); + sdhci_writel(host, offset, regval); + +} + +void sdhci_writew(sd_host_t *host, u16 offset, u16 value) +{ + writew(value, host->pci_dev.membase + offset); + DbgInfo(MODULE_SD_HOST, FEATURE_SDREG_TRACEW, NOT_TO_RAM, + "%s(0x%04X): 0x%08X\n", __func__, offset, value); + +} + +u16 sdhci_readw(sd_host_t *host, u16 offset) +{ + u16 regval = 0; + + regval = readw(host->pci_dev.membase + offset); + DbgInfo(MODULE_SD_HOST, FEATURE_SDREG_TRACER, NOT_TO_RAM, + "%s(0x%04X): 0x%08X\n", __func__, offset, regval); + + return regval; +} + +void sdhci_or16(sd_host_t *host, u16 offset, u16 value) +{ + u16 regval = 0; + + regval = sdhci_readw(host, offset); + regval |= value; + sdhci_writew(host, offset, regval); +} + +void sdhci_and16(sd_host_t *host, u16 offset, u16 value) +{ + u16 regval = 0; + + regval = sdhci_readw(host, offset); + regval &= value; + sdhci_writew(host, offset, regval); +} + +/* + * Vendor Memory register access + */ + +/* 16bit access */ +u16 ven_readw(sd_host_t *host, u16 offset) +{ + u16 regval = 0; + + regval = readw(host->pci_dev.membase2 + offset); + + DbgInfo(MODULE_SD_HOST, FEATURE_VENREG_TRACER, NOT_TO_RAM, + "[Memory Base 2] Readw(0x%08X): 0x%08X\n", offset, regval); + return regval; +} + +void ven_writew(sd_host_t *host, u16 offset, u16 value) +{ + + writew(value, host->pci_dev.membase2 + offset); + DbgInfo(MODULE_SD_HOST, FEATURE_VENREG_TRACEW, NOT_TO_RAM, + "[Memory Base 2] Writew(0x%08X): 0x%08X\n", offset, value); + +} + +void ven_or16(sd_host_t *host, u16 offset, u16 value) +{ + u16 regval = 0; + + regval = ven_readw(host, offset); + regval |= value; + ven_writew(host, offset, regval); +} + +void ven_and16(sd_host_t *host, u16 offset, u16 value) +{ + u16 regval = 0; + + regval = ven_readw(host, offset); + regval &= (value); + ven_writew(host, offset, regval); +} + +/* 32bit access */ +u32 ven_readl(sd_host_t *host, u16 offset) +{ + u32 regval = 0; + + regval = readl(host->pci_dev.membase2 + offset); + + DbgInfo(MODULE_SD_HOST, FEATURE_VENREG_TRACER, NOT_TO_RAM, + "[Memory Base 2] Readl(0x%08X): 0x%08X\n", offset, regval); + return regval; +} + +void ven_writel(sd_host_t *host, u16 offset, u32 value) +{ + + writel(value, host->pci_dev.membase2 + offset); + + DbgInfo(MODULE_SD_HOST, FEATURE_VENREG_TRACEW, NOT_TO_RAM, + "[Memory Base 2] Writel(0x%08X): 0x%08X\n", offset, value); + +} + +void ven_or32(sd_host_t *host, u16 offset, u32 value) +{ + u32 regval = 0; + + regval = ven_readl(host, offset); + regval |= value; + ven_writel(host, offset, regval); + +} + +void ven_and32(sd_host_t *host, u16 offset, u32 value) +{ + u32 regval = 0; + + regval = ven_readl(host, offset); + regval &= (value); + ven_writel(host, offset, regval); + +} + +/* + * PCI config register accessing + */ +void pci_port_writel(sd_host_t *host, u32 port, u32 data) +{ + + pci_write_config_dword(host->pci_dev.pci_dev, port, data); + + DbgInfo(MODULE_SD_HOST, FEATURE_PCIREG_TRACEW, NOT_TO_RAM, + "%s(0x%08X): 0x%08X\n", __func__, port, data); + +} + +u32 pci_port_readl(sd_host_t *host, u32 port) +{ + u32 regval = 0; + + pci_read_config_dword(host->pci_dev.pci_dev, port, ®val); + + DbgInfo(MODULE_SD_HOST, FEATURE_PCIREG_TRACER, NOT_TO_RAM, + "%s(0x%08X): 0x%08X\n", __func__, port, regval); + + return regval; +} + +/* + * PCI config register accessing by configuration IO method + */ +void pci_cfgio_writel(sd_host_t *host, u16 offset, u32 value) +{ + pci_write_config_dword(host->pci_dev.pci_dev, offset, value); +} + +u32 pci_cfgio_readl(sd_host_t *host, u16 offset) +{ + u32 regval = 0; + + pci_read_config_dword(host->pci_dev.pci_dev, offset, ®val); + DbgInfo(MODULE_SD_HOST, FEATURE_PCIREG_TRACER, NOT_TO_RAM, + "%s(0x%08X): 0x%08X\n", __func__, offset, regval); + + return regval; +} + +/* + * + * Function Name: timer_auto_cb + * + * Abstract: + * + * for timer callback + * + * Input: + * + * PVOID pctx [in]: Pointer to pdx + * + * Output: + * + * None + * + * Return value: + * + * null + * + * Notes: + * + * Caller: OS + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) +void timer_auto_cb2(struct timer_list *t) +{ + os_struct *o = from_timer(o, t, timer); + bht_dev_ext_t *pdx = container_of(o, bht_dev_ext_t, os); + + func_timer_callback(pdx); +} +#else +void timer_auto_cb2(PVOID pdx) +{ + func_timer_callback(pdx); +} +#endif + +/* + * + * Function Name: timer_subid_cb + * + * Abstract: + * + * for timer callback + * + * Input: + * + * PVOID pctx [in]: Pointer to pdx + * + * Output: + * + * None + * + * Return value: + * + * null + * + * Notes: + * + * Caller: OS + */ + +void timer_subid_cb(PVOID ctx, PVOID pdx) +{ + /* todo */ +} + +/* + * Function Name: os_start_timer + * Abstract: This Function is used to start a timer + * + * Input: + * os_struct *os, + * e_timer_t t timer id + * u32 time_ms + * + * + * + * IRQL: any + * + * so giving the routine another name requires you to modify the build tools. + */ + +void os_start_timer(void *p, os_struct *os, e_timer_t t, u32 time_ms) +{ + if (t >= MAX_TIMER_NUM) + return; + + if ((u32) t != 0) { + DbgErr("Timer %d not support now\n", t); + return; + } + + mod_timer(&os->timer, jiffies + msecs_to_jiffies(time_ms)); +} + +/* + * Function Name: os_cancel_timer + * Abstract: This Function is used to start a timer + * + * Input: + * os_struct *os, + * e_timer_t t timer id + * u32 time_ms + * + * + * IRQL:any + * + * so giving the routine another name requires you to modify the build tools. + */ +void os_cancel_timer(void *p, os_struct *os, e_timer_t t) +{ + + if (t >= MAX_TIMER_NUM) + return; + + del_timer(&os->timer); + +} + +void os_stop_timer(void *p, os_struct *os, e_timer_t t) +{ + + if (t >= MAX_TIMER_NUM) + return; +} + +/* + * + * Function Name: os_set_event + * + * Abstract: + * + * 1. set event to thread + * + * Input: + * + * os_struct *os [in]: pointer to the OS structure + * e_event_t event [in]: the event need set + * + * Output: + * + * None + * + * Return value: + * + * None + * + * IRQL:any + * + * + */ + +void os_set_event(os_struct *os, e_event_t event) +{ + spin_lock(&os->event.lock); + + set_bit((int)event, &os->event.evt_flag); + + atomic_set(&os->event.evt_comming, 1); + + spin_unlock(&os->event.lock); + + wake_up_interruptible(&os->event.evt_control); +} + +/* + * + * Function Name: os_clear_event + * + * Abstract: + * + * 1. clear event to thread + * + * Input: + * + * os_struct *os [in]: pointer to the OS structure + * e_event_t event [in]: the event need send + * + * Output: + * + * None + * + * Return value: + * + * None + * + * + * + */ + +void os_clear_event(os_struct *os, e_event_t event) +{ + /* nothing to do as event is auto cleared by os_wait_event */ +} + +/* + * + * Function Name: os_wait_event + * + * Abstract: + * + * 1. wait any one signaled event of multiple events + * + * Input: + * + * os_struct *os [in]: pointer to the OS structure + * s32 timeout [in]: timeout value + * + * Output: + * + * None + * + * Return value: + * + * None + * + * + * + */ +e_event_t os_wait_event(os_struct *os) +{ + e_event_t event = EVENT_NONE; + int i; + + if (atomic_read(&os->event.evt_comming)) + goto next; + + wait_event_interruptible(os->event.evt_control, + ((atomic_read(&os->event.evt_comming) != 0) + || (kthread_should_stop()))); + +next: + if (kthread_should_stop()) { + event = EVENT_TERMINATE; + clear_bit(event, &os->event.evt_flag); + atomic_set(&os->event.evt_comming, 0); + goto exit; + } + + if (atomic_read(&os->event.evt_comming) == 0) + goto exit; + + spin_lock(&(os->event.lock)); + + for (i = 0; i < EVENT_NONE; i++) { + if (test_bit(i, &os->event.evt_flag)) { + event = i; + clear_bit(i, &os->event.evt_flag); + break; + } + } + + if (os->event.evt_flag == 0) + atomic_set(&os->event.evt_comming, 0); + + spin_unlock(&(os->event.lock)); + +exit: + return event; + +} + +/* + * + * Function Name: os_create_thread + * + * Abstract: + * + * 1. create thread + * + * Input: + * + * thread_t *[in]: pointer to the thread entry + * void *param, [in]: the parameter which pass to thread entity + * thread_cb_t func [in]: the entry of thread enity + * + * Output: + * + * None + * + * Return value: + * + * TRUE: create ok + * FALSE: create failed + * + * + * + */ +bool os_create_thread(thread_t *thr, void *param, thread_cb_t func) +{ + + thr->pthread = + kthread_run((void *)func, (PVOID) param, "bhtsd_scsi_thread"); + if (thr->pthread == NULL) + return FALSE; + return TRUE; +} + +/* + * + * Function Name: os_stop_thread + * + * Abstract: + * + * 1. stop the thread + * + * Input: + * + * thread_t *[in]: pointer to the thread entry + * + * Output: + * + * None + * + * Return value: + * + * TRUE: stop ok + * FALSE:stop failed + * + * + * + */ +bool os_stop_thread(os_struct *os, thread_t *thr) +{ + bool ret = FALSE; + int i = 0; + + DbgInfo(MODULE_OS_API, FEATURE_THREAD_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + + if (thr != NULL && thr->pthread != NULL && os != NULL) { + os->thread.freeze = TRUE; + os_set_event(os, EVENT_TERMINATE); + kthread_stop(os->thread.pthread); + while (test_bit(EVENT_TERMINATE, &os->event.evt_flag)) { + os_mdelay(20); + i++; + if (i > 150) + goto exit; + } + + ret = TRUE; + } +exit: + DbgInfo(MODULE_OS_API, FEATURE_THREAD_TRACE, NOT_TO_RAM, + "Exit %s ret=%xh\n", __func__, ret); + return ret; +} + +void os_kill_thread(os_struct *os, thread_t *thr) +{ +} + +bool os_thread_is_freeze(void *pdx) +{ + os_struct *os = &((bht_dev_ext_t *) pdx)->os; + + if (os->thread.freeze || os->thread.pthread == NULL) + return TRUE; + else + return FALSE; +} + +/* + * + * Function Name: os_pending_thread + * + * Abstract: + * + * pending thread or resume thread + * + * + * Input: + * + * thread_t *[in]: pointer to the thread entry + * bool pending [in]: TRUE means pending thread + * + * Output: + * + * None + * + * Return value: + * + * TRUE: operate ok + * FALSE:operate failed + * + * IRQL:ANY + * + * + */ +bool os_pending_thread(void *pdx, bool pending) +{ + os_struct *os = &((bht_dev_ext_t *) pdx)->os; + u32 i = 600; + bool ret = TRUE; + + DbgInfo(MODULE_OS_API, FEATURE_THREAD_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + if (os->thread.pthread == NULL) { + DbgErr("thread obj null\n"); + return FALSE; + } + + os->thread.pending_lock = FALSE; + if (pending == TRUE) { + ret = FALSE; + os_init_completion(pdx, &os->thread.break_pending); + os->thread.freeze = TRUE; + os_set_event(os, EVENT_PENDING); + while (i--) { + if (os->thread.pending_lock == TRUE) { + ret = TRUE; + break; + } + os_mdelay(20); + } + + } else { + os_finish_completion(pdx, &os->thread.break_pending); + os->thread.freeze = FALSE; + + } + + if (ret == FALSE) + DbgErr("%s timeout\n", __func__); + DbgInfo(MODULE_OS_API, FEATURE_THREAD_TRACE, NOT_TO_RAM, + "Exit %s ret=%xh\n", __func__, ret); + return ret; +} + +/* + * completion API + */ + +/* + * + * Function Name: os_init_completion + * + * Abstract: + * + * 1.init the completion. + * + * Input: + * + * completion_t *p[in]: pointer to completion + * + * Output: + * + * None + * + * Return value: + * + * + * IRQL:any + * + * + */ + +void os_init_completion(void *pdx, completion_t *p) +{ + init_completion(p); +} + +/* + * + * Function Name: os_finish_completion + * + * Abstract: + * + * 1.wake the thread which wait for this completion. + * + * Input: + * + * completion_t *p[in]: pointer to completion + * + * Output: + * + * None + * + * Return value: + * + * + * Notes: + * + * + */ +void os_finish_completion(void *pdx, completion_t *p) +{ + complete(p); +} + +/* + * + * Function Name: os_finish_completion + * + * Abstract: + * + * 1.wake the thread which wait for this completion. + * + * Input: + * + * completion_t *p[in]: pointer to completion + * s32 timeout [in]: unit ms .if timeout =0, + * mean wait for signal until it completed + * + * Output: + * + * None + * + * Return value: + * + * + * Notes: + * + * + */ + +bool os_wait_for_completion(void *pdx, completion_t *p, s32 timeout) +{ + s32 ret = wait_for_completion_timeout(p, msecs_to_jiffies(timeout)); + + if (ret == 0) + return FALSE; + else + return TRUE; +} + +/* + * + * Function Name:os_list_locked_remove_head + * + * Abstract: + * + * remove the list_entry to list head node + * + * Input: + * + * list_t *p [in]: pointer to list + * + * Output: + * + * None + * + * Return value: + * + * + * IRQL:ANY + * + * + */ +list_entry *os_list_locked_remove_head(list_t *p) +{ + unsigned long flags; + list_entry *entry = NULL; + + spin_lock_irqsave(&p->lock, flags); + + if (list_empty(&p->list_hd)) + goto exit; + + entry = p->list_hd.next; + list_del_init(entry); + +exit: + spin_unlock_irqrestore(&p->lock, flags); + /* need with verify with chuanjing */ + return entry; +} + +/* + * + * Function Name: os_list_locked_insert_tail + * + * Abstract: + * + * insert the list etry to list tail + * + * Input: + * + * list_t *p [in]: pointer to list + * + * Output: + * + * None + * + * Return value: + * + * + * Notes: + * + * + */ +void os_list_locked_insert_tail(list_t *p, list_entry *entry) +{ + unsigned long flags; + + spin_lock_irqsave(&p->lock, flags); + + list_add_tail(entry, &p->list_hd); + + spin_unlock_irqrestore(&p->lock, flags); +} + +/* + * + * Function Name: os_list_locked_insert_head + * + * Abstract: + * + * insert the node to list head + * + * Input: + * + * list_t *p [in]: pointer to list + * + * Output: + * + * None + * + * Return value: + * + * + * Notes: + * + * + */ +void os_list_locked_insert_head(list_t *p, list_entry *entry) +{ + unsigned long flags; + + spin_lock_irqsave(&p->lock, flags); + + list_add(entry, &p->list_hd); + + spin_unlock_irqrestore(&p->lock, flags); + +} + +/* + * + * Function Name: os_list_init + * + * Abstract: + * + * init the list with lock + * + * Input: + * + * list_t *p [in]: pointer to list + * + * Output: + * + * None + * + * Return value: + * + * + * Notes: + * + * + */ +void os_list_init(list_t *p) +{ + p->lock = __SPIN_LOCK_UNLOCKED(p->lock); + INIT_LIST_HEAD(&p->list_hd); + os_atomic_set(&p->cnt, 0); + +} + +/* + * + * Function Name: os_layer_init + * + * Abstract: + * + * 1.init the os layer. + * + * Input: + * + * os_struct *os [in]: pointer to os structure + * + * Output: + * + * None + * + * Return value: + * + * + * irql:any + * + * + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) +typedef void (*func_timer)(struct timer_list *); +#else +typedef void (*func_timer)(unsigned long); +#endif + +bool os_layer_init(void *p, os_struct *os) +{ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + timer_setup(&os->timer, timer_auto_cb2, 0); +#else + init_timer(&os->timer); + os->timer.data = (unsigned long)pdx; + os->timer.function = (func_timer) timer_auto_cb2; +#endif + os->lock = __SPIN_LOCK_UNLOCKED(os->lock); + + atomic_set(&os->event.evt_comming, 0); + init_waitqueue_head(&os->event.evt_control); + os->rt_pm_cnt = 0; + os->event.evt_flag = 0; + os->dma_mapped = 0; + os->virt_buff = vmalloc(CFG_MAX_TRANSFER_LENGTH); + + if (os->virt_buff == NULL) { + DbgErr("Vmalloc failed for %d length\n", + CFG_MAX_TRANSFER_LENGTH); + } + + DbgInfo(MODULE_OS_API, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + return TRUE; +} + +/* + * + * Function Name: os_layer_uinit + * + * Abstract: + * + * 1.uninit the os layer + * + * Input: + * + * os_struct *os [in]: pointer to os structure + * + * Output: + * + * None + * + * Return value: + * + * + * Notes: + * + * + */ +bool os_layer_uinit(void *pdx, os_struct *os) +{ + DbgInfo(MODULE_OS_API, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + os_cancel_timer(pdx, os, (e_timer_t) 0); + if (os->virt_buff != NULL) + vfree(os->virt_buff); + + return TRUE; +} + +/* + * + * Function Name: os_alloc_dma_buffer + * + * Abstract: + * + * 1. allocate DMA buffer resource + * + * Input: + * + * bht_dev_ext_t *pdx [in]: pointer to device extension + * PPORT_CONFIGURATION_INFORMATION ConfigInfo [in]: + * pointer to config information + * u32 nbytes [in]: the size allocate + * dma_desc_buf_t *pdma [in]: pointer to the DMA information, + * include physical address & virtual address + * + * Output: + * + * dma_desc_buf_t *pdma [out]: pointer to the DMA information, + * include physical address & virtual address + * + * Return value: + * + * + * Notes: + * + * + */ +bool os_alloc_dma_buffer(void *p, void *ctx, u32 nbytes, dma_desc_buf_t *pdma) +{ +#define DMA_BUF_ALIGN_SIZE (1<<12) + u32 retry = 0; + bht_dev_ext_t *pdx = p; + + if (pdma->len < DMA_BUF_ALIGN_SIZE) + pdma->len = DMA_BUF_ALIGN_SIZE; + + pdma->len = nbytes; + DbgInfo(MODULE_OS_API, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Enter %s size=%xh\n", __func__, nbytes); + + for (retry = 1; retry < 10; retry++) { + pdma->pa = 0; + pdma->va = + (void *)dmam_alloc_coherent(&pdx->host.pci_dev.pci_dev->dev, + pdma->len, + (dma_addr_t *) (&pdma->pa), + GFP_KERNEL); + + if (pdma->va == NULL) { + DbgInfo(MODULE_OS_API, FEATURE_DRIVER_INIT, 0, + "os alloc dma buffer failed!!!,retry %d\n", + retry); + } else { + pdx->os.dma_info = *pdma; + break; + } + } + if (pdma->va == NULL) { + memset(&pdx->os.dma_info, 0, sizeof(dma_desc_buf_t)); + DbgErr("dmam_alloc_coherent(%xh) failed!!!\n", pdma->len); + return FALSE; + } else { + DbgInfo(MODULE_OS_API, FEATURE_DRIVER_INIT, 0, + "GetUncachedExtension return OK with length 0x%x\n", + pdma->len); + DbgInfo(MODULE_OS_API, FEATURE_DRIVER_INIT, 0, + " - Before alignment, DMA buffer physical address =h[%x] l[%X]\n", + os_get_phy_addr32h(pdma->pa), + os_get_phy_addr32l(pdma->pa)); + DbgInfo(MODULE_OS_API, FEATURE_DRIVER_INIT, 0, + " - Before alignment, DMA buffer virtual address = 0x%Xh\n", + pdma->va); + } + DbgInfo(MODULE_OS_API, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + + return TRUE; +} + +/* + * + * Function Name: os_free_dma_buffer + * + * Abstract: + * + * 1. free DMA buffer resource + * + * Input: + * + * bht_dev_ext_t *pdx [in]: pointer to device extension + * dma_desc_buf_t *pdma [in]: pointer to the DMA information, + * include physical address & virtual address + * + * Output: + * + * + * + * Return value: + * + * + * Notes: + */ +bool os_free_dma_buffer(void *p, dma_desc_buf_t *dma) +{ + bht_dev_ext_t *pdx = p; + dma_desc_buf_t *pdma = &pdx->os.dma_info; + + DbgInfo(MODULE_OS_API, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + if (pdma->len == 0) + goto exit; + + dmam_free_coherent(&pdx->host.pci_dev.pci_dev->dev, pdma->len, pdma->va, + (dma_addr_t) pdma->pa); + pdma->pa = 0; + pdma->va = 0; + pdma->len = 0; +exit: + DbgInfo(MODULE_OS_API, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + return TRUE; +} + +/* + * + * Function Name: _os_sleep + * + * Abstract: + * + * 1. sleep current thread + * + * Input: + * + * u32 time_ms [in]: sleep times + * + * Output: + * + * + * + * Return value: + * FALSE : means can't sleep, reture immediately + * TRUE: sleep ok + * + * Notes: + * Delay in ms. It won't ties up the CPU. + * But it is not so accurate. And it must be called in PASSIVE_LEVEL + * + */ +void os_sleep(u32 time_ms) +{ + u32 tick = os_get_cur_tick(); + + while (os_is_timeout(tick, time_ms) == FALSE) + schedule(); +} + +/* + * + * Function Name: os_udelay + * + * Abstract: + * + * 1. CPU busy delay + * + * Input: + * + * u32 time_us [in]: busy delay times + * + * Output: + * + * + * + * Return value: + * + * Notes: + * + */ + +void os_udelay(u32 time_us) +{ + udelay(time_us); +} + +/* + * + * Function Name: os_mdelay + * + * Abstract: + * + * 1. CPU busy delay + * + * Input: + * + * u32 time_ms [in]: busy delay times, unit ms + * + * Output: + * + * + * + * Return value: + * + * Notes: + * + */ +void os_mdelay(u32 time_ms) +{ + mdelay(time_ms); +} + +/* + * + * Function Name: os_print + * + * Abstract: + * + * 1. print string to debugview + * + * Input: + * + * byte * s [in]: pointer to string + * + * Output: + * + * + * + * Return value: + * + * Notes: + * + */ + +void os_print(byte *s) +{ + printk(s); +} + +/* + * + * Function Name: os_alloc_vbuff + * + * Abstract: + * + * 1. allocate virtual buffer + * + * Input: + * + * u32 length [in]: buffer length to allocate + * + * Output: + * + * + * + * Return value: + * + * the buffer address which allocate + * + * Notes: + * + */ + +void *os_alloc_vbuff(u32 length) +{ + return kmalloc(length, GFP_KERNEL); +} + +/* + * + * Function Name: os_free_vbuff + * + * Abstract: + * + * 1. free the virtual buffer + * + * Input: + * + * u32 length [in]: buffer length to free + * + * Output: + * + * + * + * Return value: + * + * none + * + * Notes: + * + */ +void os_free_vbuff(void *vbuff) +{ + kfree(vbuff); +} + +/* + * + * Function Name: os_get_phy_addr32l + * + * Abstract: + * + * 1. get physical address low 32bit value + * + * Input: + * + * + * + * Output: + * + * + * + * Return value: + * + * none + * + * Notes: + * + */ +u32 os_get_phy_addr32l(phy_addr_t phy_addr) +{ + return (phy_addr & 0x000000FFFFFFFF); +} + +u64 os_get_phy_addr64(phy_addr_t phy_addr) +{ + return phy_addr; +} + +/* + * + * Function Name: os_get_phy_addr32h + * + * Abstract: + * + * 1. get physical address high 32bit value + * + * Input: + * + * + * + * Output: + * + * + * + * Return value: + * + * none + * + * Notes: + * + */ +u32 os_get_phy_addr32h(phy_addr_t phy_addr) +{ + return ((phy_addr & 0xFFFFFFFF00000000) >> 32); +} + +/* + * + * Function Name: os_set_phy_addr32l + * + * Abstract: + * + * 1. set physical address low 32bit value + * + * Input: + * + * + * + * Output: + * + * + * + * Return value: + * + * none + * + * Notes: + * + */ +void os_set_phy_addr32l(phy_addr_t *phy_addr, u32 addr) +{ + u32 *haddr = (u32 *) phy_addr; + *haddr = addr; + +} + +/* + * + * Function Name: os_set_phy_addr32h + * + * Abstract: + * + * 1. set physical address high 32bit value + * + * Input: + * + * + * + * Output: + * + * + * + * Return value: + * + * none + * + * Notes: + * + */ +void os_set_phy_addr32h(phy_addr_t *phy_addr, u32 addr) +{ + u32 *haddr = (u32 *) phy_addr; + + haddr++; + *haddr = addr; +} + +/* + * + * Function Name: os_set_phy_add64 + * + * Abstract: + * + * 1. set 64bit physical address + * + * Input: + * + * + * + * Output: + * + * + * + * Return value: + * + * none + * + * Notes: + * + */ +void os_set_phy_add64(phy_addr_t *phy_addr, u64 addr) +{ + + *phy_addr = addr; + +} + +/* + * + * Function Name: srb_parse_sgl + * + * Abstract: + * + * 1. get the SG list information for the SRB + * + * Input: + * + * + * + * Output: + * + * + * + * Return value: + * + * none + * + * Notes: + * + */ +u32 os_get_sg_list(void *p, scsi_srb *pSrb, sg_list_t *sg) +{ + bht_dev_ext_t *pdx = p; + u32 sg_count = 0; + u32 i = 0; + u32 k = 0; + struct scatterlist *pAddList = NULL; + u32 dma_64bit = + pdx->cfg->host_item.test_dma_mode_setting.enable_dma_64bit_address; + + sg_count = + dma_map_sg(&pdx->host.pci_dev.pci_dev->dev, scsi_sglist(pSrb), + scsi_sg_count(pSrb), pSrb->sc_data_direction); + if (sg_count > 0) + pdx->os.dma_mapped = TRUE; + + scsi_for_each_sg(pSrb, pAddList, sg_count, k) { + if (dma_64bit) + sg[i].Address = + os_get_phy_addr64(sg_dma_address(pAddList)); + else + sg[i].Address = + os_get_phy_addr32l(sg_dma_address(pAddList)); + + sg[i].Length = sg_dma_len(pAddList); + if (sg[i].Length % 4) { + DbgErr + ("PortGetPhysicalAddress() return unaligned length: 0x%x!\n", + sg[i].Length); + return 0; + } + i++; + } + + return i; +} + +void os_free_sg_list(void *p, scsi_srb *Srb) +{ + bht_dev_ext_t *pdx = p; + + if (pdx->os.dma_mapped) { + pdx->os.dma_mapped = FALSE; + dma_unmap_sg(&pdx->host.pci_dev.pci_dev->dev, scsi_sglist(Srb), + scsi_sg_count(Srb), Srb->sc_data_direction); + } +} + +/* + * + * Function Name: os_set_dev_busy + * + * Abstract: + * + * 1. notify Power manager device is busy state + * + * Input: + * + * void *p [in]: pointer to device extension + * + * Output: + * + * + * + * Return value: + * + * none + * + * Notes: + * + */ +#if BHT_LINUX_ENABLE_RTD3 +void os_set_dev_busy(void *p) +{ + bht_dev_ext_t *pdx = p; + + if (pdx->pm_state.rtd3_en == FALSE) + return; + pdx->os.rt_pm_cnt++; + if (pdx->os.rt_pm_cnt == 1) + pm_runtime_get_sync(&pdx->host.pci_dev.pci_dev->dev); +} + +void os_set_dev_idle(void *p) +{ + bht_dev_ext_t *pdx = p; + + if (pdx->pm_state.rtd3_en == FALSE) + return; + if (pdx->os.rt_pm_cnt == 0) { + DbgErr("rt pm set idle cnt not right\n"); + } else { + + pdx->os.rt_pm_cnt--; + if (pdx->os.rt_pm_cnt == 0) { + pm_runtime_mark_last_busy(&pdx->host.pci_dev.pci_dev->dev); + pm_runtime_put_autosuspend(&pdx->host.pci_dev.pci_dev->dev); + } + } +} +#else +void os_set_dev_busy(void *p) +{ +} + +void os_set_dev_idle(void *pdx) +{ +} + +#endif + +void os_bus_change(void *p) +{ + bht_dev_ext_t *pdx = p; + + DbgErr("Enter os bus change with (%d)\n", pdx->card.card_present); + + if (pdx->card.card_present) + bht_scsi_init(pdx, pdx->dev); + else + bht_scsi_uinit(pdx); + +} + +u32 os_get_cur_tick(void) +{ + return jiffies_to_msecs(jiffies); +} + +bool os_is_timeout(u32 start_tck, u32 time_ms) +{ + if ((start_tck + time_ms) <= os_get_cur_tick()) + return TRUE; + else + return FALSE; +} + +/* + * + * Function Name: os_memset + * + * Abstract: + * + * Fill a block of memory with zeros, + * given a pointer to the block and the length, + * in bytes, to be filled. + * + * Input: + * + * + * memory_len [in]: The number of bytes to fill with zeros. + * + * + * Output: + * + * buffer [out]: A pointer to the memory block to be filled with zeros. + * + * Return value: + * + * None + * + * Notes: + * + * IRQL Any level + * + */ +void os_memset(void *buffer, byte fill, s32 len) +{ + memset(buffer, fill, len); +} + +/* + * + * Function Name: os_memcpy + * + * Abstract: + * + * Copy the contents of a source memory block to a destination memory block. + * + * Input: + * + * sbuf [in]: A pointer to the source memory block to copy the bytes from. + * memory_len [in]: The number of bytes to copy from + * the source to the destination. + * + * Output: + * + * dbuf [out]: A pointer to the destination memory block to copy the bytes to. + * + * Return value: + * + * None + * + * Notes: + * + * IRQL Any level + * + */ +void os_memcpy(void *dbuf, void *sbuf, s32 len) +{ + memcpy(dbuf, sbuf, len); +} + +s32 os_memcpr(void *dbuf, void *sbuf, s32 len) +{ + return memcmp(dbuf, sbuf, len); +} + +void os_enum_reg_cfg(void *cfg_item, e_chip_type chip_type, const byte *ustr, + cb_enum_reg_t func) +{ + char *idx_string = NULL; + char *addr_string = NULL; + char *value_string = NULL; + char *separator = NULL; + u32 i = 0; + u32 idx = 0; + u32 type = (u32) -1; + u32 addr = 0; + u32 value = 0; + int err = 0; + char cfg_buf[512]; + + if (strcmp(ustr, "\\pcr") == 0) { + for (i = 0; i < pcr_setting_count; i++) { + PrintMsg("pcr_settings[%d] = %s\n", i, pcr_settings[i]); + + if (strlen(pcr_settings[i]) >= sizeof(cfg_buf)) { + DbgErr("pcr setting string %d is too long\n", + i); + continue; + } else if (strlen(pcr_settings[i]) < 14) { + DbgErr("pcr setting string %d is too short\n", + i); + continue; + } + strcpy(cfg_buf, pcr_settings[i]); + idx_string = cfg_buf; + if (idx_string[3] != '_') { + DbgErr + ("failed to find separator between idx and type\n"); + continue; + } + idx_string[3] = '\0'; + err = kstrtou32(idx_string, 16, &idx); + if (err) { + DbgErr + ("failed to parse idx from pcr setting: %s\n", + pcr_settings[i]); + continue; + } + if (idx_string[4] == 'P' || idx_string[4] == 'p') + type = 0; + else if (idx_string[4] == 'M' || idx_string[4] == 'm') + type = 1; + else if (idx_string[4] == 'V' || idx_string[4] == 'v') + type = 2; + else { + DbgErr + ("failed to parse type from pcr setting: %s\n", + pcr_settings[i]); + continue; + } + addr_string = cfg_buf + 5; + separator = strchr(addr_string, '='); + if (separator == NULL) { + DbgErr + ("failed to find separator between addr and value\n"); + continue; + } + *separator = '\0'; + value_string = ++separator; + err = kstrtou32(addr_string, 16, &addr); + if (err) { + DbgErr + ("failed to parse addr from pcr setting: %s\n", + pcr_settings[i]); + continue; + } + err = kstrtou32(value_string, 16, &value); + if (err) { + DbgErr + ("failed to parse value from pcr setting: %s\n", + pcr_settings[i]); + continue; + } + PrintMsg("pcr[0x%08x]=0x%08x, type = %d, idx=%d\n", + addr, value, type, idx); + if (func) + func(cfg_item, type, idx, addr, value); + } + } else if (strcmp(ustr, "\\dmdn") == 0) { + for (i = 0; i < dmdn_setting_count; i++) { + if (strlen(pcr_settings[i]) >= sizeof(cfg_buf)) { + DbgErr("dmdn setting string %d is too long\n", + i); + continue; + } + strcpy(cfg_buf, dmdn_settings[i]); + addr_string = cfg_buf; + separator = strchr(addr_string, '='); + if (separator == NULL) { + DbgErr + ("failed to parse value from dmdn setting: %s\n", + dmdn_settings[i]); + continue; + } + *separator = '\0'; + value_string = ++separator; + err = kstrtou32(addr_string, 16, &addr); + if (err) { + DbgErr + ("failed to parse addr from dmdn setting: %s\n", + dmdn_settings[i]); + continue; + } + err = kstrtou32(value_string, 16, &value); + if (err) { + DbgErr + ("failed to parse value from dmdn setting: %s\n", + dmdn_settings[i]); + continue; + } + PrintMsg("dmdn[0x%08x]=0x%08x\n", addr, value); + if (func) + func(cfg_item, 0, 0, addr, value); + } + } else + PrintMsg("current only support pcr or dmdnsettings\n", + __func__); +} + +void os_pm_init(void *dev_evt) +{ + DbgInfo(MODULE_OS_API, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + /* todo for PM part */ + + DbgInfo(MODULE_OS_API, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + +} + +bool os_pcr_pesistent_restore(u16 *addr_tb, u32 *val_tb, u32 tb_len) +{ + return FALSE; +} + +bool os_pcr_pesistent_save(u16 *addr_tb, u32 *val_tb, u32 tb_len) +{ + return FALSE; +} + +void os_random_init(void) +{ + /* no implement for linux */ + +} + +u32 os_random_get(u32 max_value) +{ +#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 9, 0) + return (get_random_long() % max_value); +#else + return random32(); +#endif +} + +void os_bak_reg_hibernate(void) +{ + /* no implement for linux */ + +} + +u64 os_get_performance_tick(u64 *cpu_freq) +{ + /* no implement for linux */ + return 0; +} + +/* + * + * Function Name: os_cfg_load + * + * Abstract: + * 1. Read total reigstry information + * + * Input: + * cfg_item_t *cfg: Pointer to the registry config structure + * e_chip_type chip_type; + * + * Output: + * None + * + * Return value: + * None + * + * Notes: + * Caller: cfgmng_init + */ +void os_cfg_load(void *cfg_item, e_chip_type chip_type) +{ + cfg_item_t *cfg = cfg_item; + + if (m_emmc_mode & BIT31) + os_memcpy(&cfg->card_item.emmc_mode, &m_emmc_mode, 4); + + if (m_dma_mode & BIT31) { + os_memcpy(&cfg->host_item.test_dma_mode_setting, &m_dma_mode, + 4); + } + + if (m_infinite_ctrl & BIT31) { + os_memcpy(&cfg->host_item.test_infinite_transfer_mode, + &m_infinite_ctrl, 4); + } + + if (m_tag_cap & BIT31) { + os_memcpy(&cfg->host_item.test_tag_queue_capability, &m_tag_cap, + 4); + } + + if (m_sdmode_dis & BIT31) + os_memcpy(&cfg->card_item.sd_card_mode_dis, &m_sdmode_dis, 4); + + if (m_mmc_dis & BIT31) + os_memcpy(&cfg->card_item.mmc_mode_dis, &m_mmc_dis, 4); + + if (m_uhs2_ctrl & BIT31) + os_memcpy(&cfg->card_item.uhs2_setting, &m_uhs2_ctrl, 4); + + if (m_output_tuning & BIT31) { + os_memcpy(&cfg->feature_item.output_tuning_item, + &m_output_tuning, 4); + } + + if (m_auto_dmt & BIT31) + os_memcpy(&cfg->timer_item.auto_dormant_timer, &m_auto_dmt, 4); + + os_memcpy(&cfg->driver_item, &m_driver_item, 4); + + os_memcpy(&cfg->card_item.sd7_sdmode_switch_control, + &m_sd7_sdmode_switch_control, 4); + os_memcpy(&cfg->card_item.sd_card_mode_dis, &m_sd_card_mode_dis, 4); + os_memcpy(&cfg->card_item.test_max_access_mode, &m_test_max_access_mode, + 4); + os_memcpy(&cfg->card_item.test_uhs2_setting2, &m_test_uhs2_setting2, 4); + os_memcpy(&cfg->card_item.test_driver_strength_sel, + &m_test_driver_strength_sel, 4); + os_memcpy(&cfg->card_item.test_max_power_limit, &m_test_max_power_limit, + 4); + os_memcpy(&cfg->card_item.mmc_mode_dis, &m_mmc_mode_dis, 4); + + os_memcpy(&cfg->feature_item.pcie_wake_setting, &m_pcie_wake_setting, + 4); + os_memcpy(&cfg->feature_item.hsmux_vcme_enable, &m_hsmux_vcme_enable, + 4); + + os_memcpy(&cfg->host_item.vdd_power_source_item, + &m_vdd_power_source_item, 4); + os_memcpy(&cfg->host_item.host_drive_strength, &m_host_drive_strength, + 4); + os_memcpy(&cfg->host_item.test_sdma_boundary, &m_test_sdma_boundary, 4); + os_memcpy(&cfg->host_item.test_dma_mode_setting, + &m_test_dma_mode_setting, 4); + os_memcpy(&cfg->host_item.test_ocb_ctrl, &m_test_ocb_ctrl, 4); + os_memcpy(&cfg->host_item.bios_l1_substate, &m_bios_l1_substate, 4); + + os_memcpy(&cfg->feature_item.auto_detect_refclk_counter_range_ctl, + &m_auto_detect_refclk_counter_range_ctl, 4); + os_memcpy(&cfg->feature_item.refclk_stable_detection_counter1, + &m_refclk_stable_detection_counter1, 4); + os_memcpy(&cfg->feature_item.refclk_stable_detection_counter2, + &m_refclk_stable_detection_counter2, 4); + os_memcpy(&cfg->feature_item.refclk_stable_detection_counter3, + &m_refclk_stable_detection_counter3, 4); + os_memcpy(&cfg->feature_item.l1_enter_exit_logic_ctl, + &m_l1_enter_exit_logic_ctl, 4); + os_memcpy(&cfg->feature_item.pcie_phy_amplitude_adjust, + &m_pcie_phy_amplitude_adjust, 4); + os_memcpy(&cfg->feature_item.output_tuning_item, &m_output_tuning_item, + 4); + os_memcpy(&cfg->feature_item.test_main_ldo_setting, + &m_test_main_ldo_setting, 4); + + os_memcpy(&cfg->timeout_item.power_wait_time, &m_power_wait_time, 4); + os_memcpy(&cfg->timeout_item.test_write_data_timeout, + &m_test_write_data_timeout, 4); + os_memcpy(&cfg->timeout_item.test_read_data_timeout, + &m_test_read_data_timeout, 4); + os_memcpy(&cfg->timeout_item.test_non_data_timeout, + &m_test_non_data_timeout, 4); + os_memcpy(&cfg->timeout_item.test_r1b_data_timeout, + &m_test_r1b_data_timeout, 4); + os_memcpy(&cfg->timeout_item.test_card_init_timeout, + &m_test_card_init_timeout, 4); + +#if BHT_LINUX_ENABLE_RTD3 + os_memcpy(&cfg->feature_item.psd_mode, &m_psd_mode, 4); +#else + cfg->feature_item.psd_mode.enable_rtd3 = 0; + + /* software control */ + cfg->feature_item.psd_mode.rtd3_ctrl_mode = 1; +#endif + +} + +/* currently don't implement */ +void os_set_sdio_val(void *p, u8 val, bool need_set_did) +{ +} + +void os_rtd3_req_wait_wake(void *pdx) +{ + +} diff --git a/drivers/scsi/bht/linux_os/linux_api.h b/drivers/scsi/bht/linux_os/linux_api.h new file mode 100644 index 000000000000..960f9cc4dd5d --- /dev/null +++ b/drivers/scsi/bht/linux_os/linux_api.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: linux_api.h + * + * Abstract: Include apis for Linux + * + * Version: 1.00 + * + * Author: Peter.Guo + * + * Environment: Linux + * + * History: + * + * 5/20/2015 Creation Peter.Guo + */ + +#include +#include +#include +#include + +#include "../include/basic.h" +#define TASK_STATUS_IDLE 0 +#define TASK_STATUS_OCCUR 1 + +typedef struct completion linux_completion_t; + +typedef struct { + spinlock_t lock; + /* add for list */ + struct list_head list_hd; + atomic_t cnt; +} linux_list_t; + +typedef struct { + struct task_struct *pthread; + bool pending_lock; + bool freeze; + linux_completion_t break_pending; +} thread_t; + +typedef void (*timer_cb_t)(PVOID, PVOID); + +typedef struct { + void *timer; + timer_cb_t timer_callback; +} linux_timer_t; + +typedef struct { + wait_queue_head_t evt_control; + /* Command control thread - command in flag */ + atomic_t evt_comming; + unsigned long evt_flag; + spinlock_t lock; +} t_linux_event; + +typedef struct { + u8 status; + u32 id; + u8 shared_task_cnt; +} win_task_t; + +typedef struct { + win_task_t tasks[10]; + u32 task_cnt; +} task_mgr_t; + +typedef struct { + struct timer_list timer; + thread_t thread; + + task_mgr_t task_mgr; + t_linux_event event; + spinlock_t lock; + dma_desc_buf_t dma_info; + struct Scsi_Host *scsi_host; + byte *virt_buff; + /* + * If os_get_sg_list called successful, set this flag to 1. + * and need call dma_unmap_sg when transfer done + */ + bool dma_mapped; + int rt_pm_cnt; + bool thread_terminate_stop; +} t_linux_os_struct; + +void os_free_sg_list(void *p, scsi_srb *Srb); diff --git a/drivers/scsi/bht/linux_os/linux_base.c b/drivers/scsi/bht/linux_os/linux_base.c new file mode 100644 index 000000000000..a651141b7608 --- /dev/null +++ b/drivers/scsi/bht/linux_os/linux_base.c @@ -0,0 +1,985 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: linux_base.c + * + * Abstract: Driver Entry for Linux + * + * Version: 1.00 + * + * Author: Peter.Guo + * + * Environment: Linux + * + * History: + * + * 5/20/2015 Creation Peter.Guo + */ + +#include +#include +#include "../include/basic.h" +#include "../include/debug.h" +#include "../include/reqapi.h" +#include "../include/hostapi.h" +#include "../include/hostvenapi.h" + +#include "linux_scsi.h" +#if BHT_LINUX_ENABLE_RTD3 +#include +#endif + +#define VENDOR_O2MICRO 0x1217 +#define BHT_SD_MAX_DEV 8 +#define BHT_USE_PCI_MSI 1 +#define SRB_MIN_REQ 64 + +#define INPUT_TUNING_PASS_WINDOW 0x44 + +extern u32 g_dbg_module; +extern u32 g_dbg_feature; +extern u32 g_dbg_ctrl; + +extern uint m_sd_3v3_clk_driver_strength; +extern uint m_sd_3v3_cmddata_driver_strength; +extern uint m_sd_1v8_clk_driver_strength; +extern uint m_sd_1v8_cmddata_driver_strength; +extern uint m_ram_ema; +extern uint m_vdd1_vdd2_source; +extern uint m_vdd18_debounce_time; +extern uint m_ssc_enable; +extern uint m_cnfg_drv; +extern uint m_cnfg_trm_code_tx; +extern uint m_cnfg_trm_code_rx; +extern uint m_cnfg_rint_code; + +struct kmem_cache *bht_srb_ext_cachep; +mempool_t *bht_sd_mem_pool; + +typedef struct { + int used; + bht_dev_ext_t data; +} bht_sd_slot_t; + +static bht_sd_slot_t bht_slot[BHT_SD_MAX_DEV]; + +static bht_sd_slot_t *bht_slot_getfree(void) +{ + int i = 0; + + for (i = 0; i < BHT_SD_MAX_DEV; i++) { + if (bht_slot[i].used == 0) { + os_memset(&bht_slot[i].data, 0, sizeof(bht_dev_ext_t)); + DbgInfo(MODULE_OS_ENTRY, FEATURE_DRIVER_INIT, + NOT_TO_RAM, "Get Slot=%p i=%d\n", &bht_slot[i], + i); + return &bht_slot[i]; + } + } + return NULL; +} + +static void bht_global_init(void) +{ + int i = 0; + + for (i = 0; i < BHT_SD_MAX_DEV; i++) { + os_memset(&bht_slot[i].data, 0, sizeof(bht_dev_ext_t)); + bht_slot[i].used = 0; + } + + DbgRamInit(); +#if DBG || _DEBUG + g_dbg_module = DBG_MODULE_CONTROL; + g_dbg_feature = DBG_FEATURE_CONTROL; + g_dbg_ctrl = DBG_CTRL_CONTROL; + + DbgErr("Dbg Module=0x%08X feature=0x%08X control=0x%08X\n", + g_dbg_module, g_dbg_feature, g_dbg_ctrl); +#else + g_dbg_module = 0; + g_dbg_feature = 0; + g_dbg_ctrl = 0; +#endif + cfgmng_init(); +} + +static void bht_global_uninit(void) +{ + int i; + + DbgInfo(MODULE_OS_ENTRY, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "bht sd free slots\n"); + for (i = 0; i < BHT_SD_MAX_DEV; i++) { + os_memset(&bht_slot[i].data, 0, sizeof(bht_dev_ext_t)); + bht_slot[i].used = 0; + } + + DbgRamFree(); +} + +struct pci_device_id bht_id_table[] = { + { + /* SDS0 */ + .vendor = VENDOR_O2MICRO, + .device = 0x8420, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + + { + /* SDS1 */ + .vendor = VENDOR_O2MICRO, + .device = 0x8421, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + + { + /* Fujin2A Fjin2B */ + .vendor = VENDOR_O2MICRO, + .device = 0x8520, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + + { + /* Searbird */ + .vendor = VENDOR_O2MICRO, + .device = 0x8620, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + + { + /* SeaBird */ + .vendor = VENDOR_O2MICRO, + .device = 0x8621, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { + .vendor = VENDOR_O2MICRO, + /* 0x8720 to 0x8723 is SeaEagle2 */ + .device = 0x8720, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + + { + .vendor = VENDOR_O2MICRO, + .device = 0x8721, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { + .vendor = VENDOR_O2MICRO, + .device = 0x8722, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { + .vendor = VENDOR_O2MICRO, + .device = 0x8723, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { + /* 0x8750,0x8751 and 0x8740 is SandStorm2 */ + .vendor = VENDOR_O2MICRO, + .device = 0x8750, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { + .vendor = VENDOR_O2MICRO, + .device = 0x8751, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { + .vendor = VENDOR_O2MICRO, + .device = 0x8740, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { + /* gg8 */ + .vendor = VENDOR_O2MICRO, + .device = 0x9860, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { + .vendor = VENDOR_O2MICRO, + .device = 0x9861, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { + .vendor = VENDOR_O2MICRO, + .device = 0x9862, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { + .vendor = VENDOR_O2MICRO, + .device = 0x9863, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { 0, 0 }, +}; + +MODULE_DEVICE_TABLE(pci, bht_id_table); + +/* ----------BIOS GUIDE setting part---------- */ + +void bht_bios_setting(bht_dev_ext_t *pdx) +{ + sd_host_t *host = &pdx->host; + cfg_item_t *cfg = pdx->cfg; + u32 regaddr; + u32 regval; + + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_RW_TRACE, NOT_TO_RAM, + "Enter %s chip type = 0x%x\n", __func__, host->chip_type); + + switch (host->chip_type) { + case CHIP_GG8: + regval = pci_readl(host, 0x300); + regval &= ~(0xff); + regval |= 0x33; + pci_writel(host, 0x300, regval); + break; + case CHIP_SEAEAGLE2: + /* we need set this to enable hs400 function */ + if (cfg->card_item.emmc_mode.enable_force_hs400) + pci_orl(host, 0x3f8, BIT13); + else + pci_andl(host, 0x3f8, ~BIT13); + break; + case CHIP_SDS2_SD0: + case CHIP_SDS2_SD1: + /* pcie & pm #2, should be the first operation after detect host type */ + pci_writel(host, 0x438, m_ram_ema); + + /* uhsi #1 */ + pci_orl(host, 0x3e4, 0x80000000); + /* uhsi #3 */ + regaddr = (host->chip_type == CHIP_SDS2_SD0) ? 0x300 : 0x380; + pci_writel(host, regaddr, INPUT_TUNING_PASS_WINDOW); + + /* ushi #4 */ + regval = pci_readl(host, 0x3f8); + regval &= ~(0x03u << 19); + regval |= (m_vdd18_debounce_time << 19); + pci_orl(host, 0x3f8, regval); + + /* uhsi #6 */ + regaddr = host->chip_type == CHIP_SDS2_SD0 ? 0x304 : 0x384; + regval = pci_readl(host, regaddr); + regval &= 0xffff8881; + regval |= ((m_sd_3v3_clk_driver_strength & 0x07) << 12 | + (m_sd_3v3_cmddata_driver_strength & 0x07) << 8 | + (m_sd_1v8_clk_driver_strength & 0x07) << 4 | + (m_sd_1v8_cmddata_driver_strength & 0x07) << 1 | 1); + pci_writel(host, regaddr, regval); + + /* ushii #1 */ + regval = pci_readl(host, 0x3fc); + regval &= ~0x7f000000; + regval |= ((m_cnfg_drv & 0x7f) << 24); + pci_writel(host, 0x3fc, regval); + + regval = pci_readl(host, 0x400); + regval &= ~(0xfff); + regval |= ((m_cnfg_trm_code_tx & 0x0f) << 8); + regval |= ((m_cnfg_trm_code_rx & 0x0f) << 4); + regval |= (m_cnfg_rint_code & 0x0f); + pci_writel(host, 0x400, regval); + + /* uhsii 2 */ + switch (m_vdd1_vdd2_source) { + case 0: + regval = pci_readl(host, 0x508); + /* bit[25:24] = 2'b00 */ + regval &= ~(0x3 << 24); + /* bit[9:8] = 2'b00 */ + regval &= ~(0x3 << 8); + regval &= ~(0xf << 2); + /* bit[5:2]=4'b0101 */ + regval |= 0x14; + break; + case 1: + regval = pci_readl(host, 0x508); + /* bit[25:24] = 2'b11 */ + regval |= (0x3 << 24); + /* bit[9:8] = 2'b11 */ + regval |= (0x3 << 8); + /* bit[5:2]=4'b1111 */ + regval |= (0xf << 2); + break; + case 2: + regval = pci_readl(host, 0x508); + /* bit[25:24] = 2'b00 */ + regval &= ~(0x3 << 24); + /* bit[25:24] = 2'b10 */ + regval |= (0x2 << 24); + /* bit[9:8] = 2'b00 */ + regval &= ~(0x3 << 8); + /* bit[9:8] = 2'b10 */ + regval |= (0x2 << 8); + regval &= ~(0xf << 2); + /* bit[5:2]=4'b1010 */ + regval |= 0x28; + break; + default: + break; + } + pci_writel(host, 0x508, regval); + + /* clock & generator #1 */ + if (m_ssc_enable != 0) { + pci_writel(host, 0x420, 0xa0005e1e); + pci_writel(host, 0x424, 0x61180000); + } else { + pci_writel(host, 0x420, 0xa000561e); + pci_writel(host, 0x424, 0x21180000); + } + /* clock & generator #2 */ + pci_writel(host, 0x428, 0x01f0f0fa); + pci_writel(host, 0x42c, 0x01f0f0fa); + break; + default: + break; + } + +} + +#if BHT_LINUX_ENABLE_RTD3 + +static void bht_sd_runtime_pm_allow(bht_dev_ext_t *pdx, struct device *dev) +{ + if (pdx->pm_state.rtd3_en == FALSE) + return; + + pm_runtime_put_noidle(dev); + pm_runtime_allow(dev); + pm_runtime_set_autosuspend_delay(dev, + pdx->cfg->feature_item.psd_mode.disk_idle_time_s * 1000); + pm_runtime_use_autosuspend(dev); + pm_suspend_ignore_children(dev, 1); +} + +static void bht_sd_runtime_pm_forbid(bht_dev_ext_t *pdx, struct device *dev) +{ + + if (pdx->pm_state.rtd3_en == FALSE) + return; + + pm_runtime_forbid(dev); + pm_runtime_get_noresume(dev); +} + +static int bht_sd_runtime_suspend(struct device *dev) +{ + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + int ret = 0; + bht_sd_slot_t *slot = pci_get_drvdata(pdev); + bht_dev_ext_t *pdx = 0; + + DbgInfo(MODULE_OS_ENTRY, FEATURE_PM_TRACE, NOT_TO_RAM, + "BHT sd rt supspend begin\n"); + + if (slot == NULL || slot->used == FALSE) + goto exit; + + pdx = &slot->data; + pdx->pm_state.rtd3_entered = TRUE; + req_enter_d3(pdx); + +exit: + DbgInfo(MODULE_OS_ENTRY, FEATURE_PM_TRACE, NOT_TO_RAM, + "BHT sd rt supspend exit\n"); + return ret; +} + +static int bht_sd_runtime_resume(struct device *dev) +{ + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + int ret = 0; + bht_sd_slot_t *slot = pci_get_drvdata(pdev); + bht_dev_ext_t *pdx = 0; + + DbgInfo(MODULE_OS_ENTRY, FEATURE_PM_TRACE, NOT_TO_RAM, + "BHT sd rt resume begin\n"); + + if (slot == NULL || slot->used == FALSE) + goto exit; + + pdx = &slot->data; + req_enter_d0_sync(pdx); + +exit: + DbgInfo(MODULE_OS_ENTRY, FEATURE_PM_TRACE, NOT_TO_RAM, + "BHT sd rt resume exit\n"); + return ret; +} + +static int bht_sd_runtime_idle(struct device *dev) +{ + return 0; +} + +#endif + +static irqreturn_t bht_sd_irq(int irq, void *dev_id) +{ + irqreturn_t irqret = IRQ_NONE; + bht_dev_ext_t *pdx = (bht_dev_ext_t *) dev_id; + bool ret = FALSE; + + ret = sdhci_irq(pdx); + + if (pdx->host.pci_dev.use_msi) + irqret |= IRQ_HANDLED; + else { + if (ret) + irqret |= IRQ_HANDLED; + } + + return irqret; +} + +#ifdef CONFIG_PM +static int bht_sd_suspend(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + bht_sd_slot_t *slot = pci_get_drvdata(pdev); + bht_dev_ext_t *pdx = &slot->data; + + DbgInfo(MODULE_OS_ENTRY, FEATURE_PM_TRACE, NOT_TO_RAM, + "BHT sd suspend begin\n"); + + if (slot->used == 0) + goto exit; + + pdx->pm_state.s3s4_entered = TRUE; + req_pre_enter_d3(pdx); + req_enter_d3(pdx); + +exit: + DbgInfo(MODULE_OS_ENTRY, FEATURE_PM_TRACE, NOT_TO_RAM, + "BHT sd suspend end\n"); + return 0; +} + +static int bht_sd_resume(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + bht_sd_slot_t *slot = pci_get_drvdata(pdev); + bht_dev_ext_t *pdx = &slot->data; + + DbgInfo(MODULE_OS_ENTRY, FEATURE_PM_TRACE, NOT_TO_RAM, + "BHT sd resume begin\n"); + + if (slot->used == 0) + goto exit; + + req_enter_d0(pdx); + +exit: + DbgInfo(MODULE_OS_ENTRY, FEATURE_PM_TRACE, NOT_TO_RAM, + "BHT sd resume end\n"); + return 0; +} + +#else +#define bht_sd_suspend NULL +#define bht_sd_resume NULL +#endif + +/* + * pci dev interrupt init + */ +static int bht_pci_init_irq(struct pci_dev *pdev, bht_dev_ext_t *pdx) +{ + int ret = -1; + + pdx->host.pci_dev.use_msi = 0; + + /* Firstly try MSI interrupt if enabled */ +#if defined(BHT_USE_PCI_MSI) && defined(CONFIG_PCI_MSI) + if (pci_find_capability(pdev, PCI_CAP_ID_MSI)) { + int err; + /* PCI device support MSI interrupt mode. */ + err = pci_enable_msi(pdev); + if (!err) { + pdx->host.pci_dev.irq = pdev->irq; + pdx->host.pci_dev.use_msi = 1; + DbgInfo(MODULE_OS_ENTRY, FEATURE_DRIVER_INIT, + NOT_TO_RAM, "Use Msi irq irq=%d\n", pdev->irq); + } else { + DbgErr("MSI init failed %d\n", err); + } + + if (request_irq(pdev->irq, bht_sd_irq, IRQF_SHARED, + (const char *)"bht-sd", (void *)pdx)) { + DbgErr("request msi irq %d for bht-sd failed", + pdev->irq); + pci_disable_msi(pdev); + pdx->host.pci_dev.use_msi = 0; + } + ret = 0; + goto exit; + } +#endif + + pdx->host.pci_dev.irq = pdev->irq; + pdx->host.pci_dev.use_msi = 0; + if (request_irq(pdev->irq, bht_sd_irq, IRQF_SHARED, + (const char *)"bht-sd", (void *)pdx)) { + DbgErr("request irq %d for bht-sd failed", pdev->irq); + ret = -1; + goto exit; + } + + ret = 0; + +exit: + return ret; + +} + +/* + * pci device basci info init + */ +static int bht_pci_init(struct pci_dev *pdev, bht_dev_ext_t *pdx) +{ + int ret = -1; + u32 bus_address; + + pdx->host.pci_dev.pci_dev = pdev; + ret = pci_enable_device(pdev); + if (ret) { + DbgErr("pci enable device failed\n"); + return ret; + } + + pci_set_master(pdev); + + bus_address = pci_resource_start(pdev, 0); + if (pci_request_regions(pdev, "bht-sd") < 0) { + DbgErr("o2sd request_region failed(%d)\n", 0); + ret = -1; + goto exit; + } + /* BAR 0 and BAR1 */ + bus_address = pci_resource_start(pdev, 0); + pdx->host.pci_dev.membase = + (void *)ioremap(bus_address, pci_resource_len(pdev, 0)); + bus_address = pci_resource_start(pdev, 1); + pdx->host.pci_dev.membase2 = + (void *)ioremap(bus_address, pci_resource_len(pdev, 1)); + + DbgInfo(MODULE_OS_ENTRY, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "membase1=%p membase2=%p\n", pdx->host.pci_dev.membase, + pdx->host.pci_dev.membase2); + pdx->host.vendor_id = pdev->vendor; + pdx->host.device_id = pdev->device; + pdx->host.revision_id = pdev->revision; + + ret = 0; +exit: + if (ret != 0) + pci_disable_device(pdev); + return ret; + +} + +/* + * pci dev dma resource init + */ +static bool bht_sd_dma_init(bht_dev_ext_t *pdx) +{ + bool ret = FALSE; + struct pci_dev *pdev = pdx->host.pci_dev.pci_dev; + +#if CONFIG_X86_64 + +#else + + /* Set to 32Bit DMA currently */ + cfg_dma_addr_range_dec(pdx->cfg, 0); +#endif + + if (pdx->cfg->host_item.test_dma_mode_setting.enable_dma_64bit_address) { + if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)) + || dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64))) + DbgErr("dma_set_mask 64bit failed\n"); + else + goto next; + } + + if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)) + || dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32))) + DbgErr("dma_set_mask 32bit failed\n"); +next: + + if (pdx->signature != BHT_PDX_SIGNATURE) { + u32 max_req_numb = 0; + u32 dma_mode = 0; + u32 buf_size = 0x10; + u32 node_size = 0; + + max_req_numb = + pdx->cfg->host_item.test_tag_queue_capability.max_srb; + dma_mode = pdx->cfg->host_item.test_dma_mode_setting.dma_mode; + + if (cfg_dma_need_sdma_like_buffer(dma_mode) == TRUE) + node_size = MAX_SDMA_LIKE_MODE_NODE_BUF_SIZE; + else + node_size = MAX_NODE_BUF_SIZE; + + buf_size = (max_req_numb + TQ_RESERVED_NODE_SIZE) * node_size; + + while (1) { + /* preseved for ADMA2 API DMA buffer */ + buf_size += MIN_DMA_API_BUF_SIZE; + + /* less than minimum buf size will failed. */ + if (buf_size < + (MIN_DMA_API_BUF_SIZE + MAX_NODE_BUF_SIZE)) { + DbgErr + ("dma buffer less than min(0x%x),so Failed\n", + (MIN_DMA_API_BUF_SIZE + + MAX_NODE_BUF_SIZE)); + break; + } + DbgInfo(MODULE_OS_ENTRY, FEATURE_DRIVER_INIT, + NOT_TO_RAM, "os alloc dma buf len %x\n", + buf_size); + ret = + os_alloc_dma_buffer(pdx, NULL, buf_size, + &pdx->dma_buff); + if (ret == TRUE) + break; + /* small requirement size */ + buf_size = buf_size / 2; + } + } + + return ret; + +} + +/* + * Release pci layer resource + */ +static void bht_sd_pci_release(bht_dev_ext_t *pdx) +{ + struct pci_dev *pdev = pdx->host.pci_dev.pci_dev; + + free_irq(pdx->host.pci_dev.irq, pdx); + if (pdx->host.pci_dev.use_msi) + pci_disable_msi(pdev); + os_free_dma_buffer(pdx, &pdx->dma_buff); + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +/* + * Update Linux Cfg Setting + */ +static void bht_linux_cfg_update(bht_dev_ext_t *pdx) +{ + u32 dma_mode = pdx->cfg->host_item.test_dma_mode_setting.dma_mode; + + switch (dma_mode) { + case CFG_TRANS_MODE_SDMA: + case CFG_TRANS_MODE_ADMA2_SDMA_LIKE: + case CFG_TRANS_MODE_PIO: + case CFG_TRANS_MODE_ADMA_MIX_SDMA_LIKE: + /* don't support tagqueue */ + pdx->cfg->host_item.test_tag_queue_capability.max_srb = 2; + pdx->cfg->host_item.test_tag_queue_capability.enable_srb_merge = + 0; + break; + case CFG_TRANS_MODE_ADMA2: + case CFG_TRANS_MODE_ADMA3: + break; + /* Linux don't support adma3_sdma like mode */ + case CFG_TRANS_MODE_ADMA3_SDMA_LIKE: + pdx->cfg->host_item.test_dma_mode_setting.dma_mode = + CFG_TRANS_MODE_ADMA3; + break; + default: + break; + } + /* This function is add for degrade frequency for HS400 */ + if ((*(u32 *) (&pdx->cfg->card_item.emmc_mode)) & BIT13) + pdx->cfg->dmdn_tbl[4] = 0x23040000; + + bht_bios_setting(pdx); +} + +/* + * pci device remove init + */ +static int bht_sd_probe(struct pci_dev *pdev, const struct pci_device_id *pid) +{ + bht_sd_slot_t *slot = bht_slot_getfree(); + bht_dev_ext_t *pdx = &slot->data; + int ret = -1; + + DbgInfo(MODULE_OS_ENTRY, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "BHT sd probe begin\n"); + if (slot == NULL) { + DbgErr("all slot is used\n"); + goto exit; + } + + ret = bht_pci_init(pdev, &slot->data); + if (ret) + goto exit; + + pdx->host.pdx = pdx; + hostven_chip_type_check(&pdx->host); + /* Get the configuration pointer for the specified chip */ + pdx->cfg = cfgmng_get((void *)pdx, pdx->host.chip_type, FALSE); + pdx->host.cfg = pdx->cfg; + /* + * Linux Adjust the input parameter + */ + bht_linux_cfg_update(pdx); + os_memset(&pdx->testcase, 0, sizeof(testcase_t)); + pdx->testcase.test_type = 0; + + if (bht_sd_dma_init(pdx) == FALSE) { + ret = -1; + DbgErr("DMA Init failed\n"); + pci_release_regions(pdev); + pci_disable_device(pdev); + goto exit; + } + + req_global_init(pdx); + ret = bht_pci_init_irq(pdev, pdx); + if (ret) { + ret = -1; + DbgErr("Irq Init failed\n"); + os_free_dma_buffer(pdx, &pdx->dma_buff); + pci_release_regions(pdev); + pci_disable_device(pdev); + goto exit; + } + + if (pdx->card.card_present) + os_set_event(&pdx->os, EVENT_CARD_CHG); + + ret = 0; + +exit: + if (ret == 0) { + slot->used = 1; + pci_set_drvdata(pdev, slot); +#if BHT_LINUX_ENABLE_RTD3 + bht_sd_runtime_pm_allow(pdx, &pdev->dev); +#endif + } + + DbgInfo(MODULE_OS_ENTRY, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "BHT sd probe end ret=%d\n", ret); + return ret; + +} + +/* + * pci device remove interface + * Thomas change for direct remove + */ +void bht_sd_remove(struct pci_dev *pdev) +{ + bht_sd_slot_t *slot = pci_get_drvdata(pdev); + bht_dev_ext_t *pdx = &slot->data; + + DbgInfo(MODULE_OS_ENTRY, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "BHT sd remove begin\n"); + + if (slot->used == FALSE) + goto exit; + +#if BHT_LINUX_ENABLE_RTD3 + bht_sd_runtime_pm_forbid(pdx, &pdev->dev); +#endif + bht_scsi_uinit(pdx); + + /* + * Thomas comment because it cause AER 0xe6 and block the remove. + * + * if(os_stop_thread(&pdx->os, &pdx->os.thread) == FALSE) + * { + * DbgErr("stop thread timeout\n"); + * os_kill_thread(&pdx->os, &pdx->os.thread); + * } + * + */ + + req_global_uninit(pdx); + bht_sd_pci_release(pdx); + os_memset(slot, 0, sizeof(bht_sd_slot_t)); + pci_set_drvdata(pdev, NULL); + +exit: + DbgInfo(MODULE_OS_ENTRY, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "BHT sd remove end\n"); + +} + +/* + * pci_dev shutdown interface + */ +static void bht_sd_shutdown(struct pci_dev *pdev) +{ + bht_sd_slot_t *slot = pci_get_drvdata(pdev); + bht_dev_ext_t *pdx = &slot->data; + + if (slot->used == FALSE) + return; + + if (os_stop_thread(&pdx->os, &pdx->os.thread) == FALSE) { + DbgErr("stop thread timeout\n"); + os_kill_thread(&pdx->os, &pdx->os.thread); + } + req_global_uninit(pdx); + bht_sd_pci_release(pdx); + pci_set_drvdata(pdev, NULL); + os_memset(slot, 0, sizeof(bht_sd_slot_t)); +} + +static const struct dev_pm_ops bht_sd_pm_ops = { + .suspend = bht_sd_suspend, + .resume = bht_sd_resume, + .freeze = bht_sd_suspend, + .thaw = bht_sd_resume, + .poweroff = bht_sd_suspend, + .restore = bht_sd_resume, +#if BHT_LINUX_ENABLE_RTD3 + SET_RUNTIME_PM_OPS(bht_sd_runtime_suspend, + bht_sd_runtime_resume, bht_sd_runtime_idle) +#endif +}; + +static struct pci_driver bht_sd_pci_driver = { + .name = "bht-sd", + .id_table = bht_id_table, + .probe = bht_sd_probe, + .remove = bht_sd_remove, + .shutdown = bht_sd_shutdown, + .driver = { + .pm = &bht_sd_pm_ops }, +}; + +/* + * Uninit linux global resource + */ +static void bht_linux_global_uninit(void) +{ + if (bht_sd_mem_pool) + mempool_destroy(bht_sd_mem_pool); + + if (bht_srb_ext_cachep) + kmem_cache_destroy(bht_srb_ext_cachep); + + bht_srb_ext_cachep = NULL; + bht_sd_mem_pool = NULL; +} + +/* + * init linux global resource + */ +static int bht_linux_global_init(void) +{ + int ret = 0; + + bht_srb_ext_cachep = NULL; + bht_sd_mem_pool = NULL; + + bht_srb_ext_cachep = + kmem_cache_create("bht-sd-srbext", sizeof(srb_ext_t), 0, + SLAB_HWCACHE_ALIGN, NULL); + + if (bht_srb_ext_cachep == NULL) { + ret = -1; + DbgErr("Allocate Memory cache failed\n"); + goto exit; + } + + bht_sd_mem_pool = + mempool_create(LINUX_SCSI_MAX_QUEUE_DPETH * 2, mempool_alloc_slab, + mempool_free_slab, bht_srb_ext_cachep); +exit: + if (bht_sd_mem_pool == NULL) { + DbgErr("Allocate Memory Pool failed\n"); + bht_linux_global_uninit(); + ret = -1; + } + + return ret; + +} + +/* + * bht_sd_init - First called function when driver load/insert + */ +static int __init bht_sd_init(void) +{ + int ret = 0; + + ret = bht_linux_global_init(); + if (ret) + goto exit; + + bht_global_init(); + + ret = pci_register_driver(&bht_sd_pci_driver); + +exit: + if (ret != 0) { + bht_linux_global_uninit(); + DbgErr("Register bht pci driver failed\n"); + bht_global_uninit(); + } + + return ret; +} + +/* + * bht_sd_exit - First called when dirver unload/remove + */ +static void __exit bht_sd_exit(void) +{ + pci_unregister_driver(&bht_sd_pci_driver); + bht_linux_global_uninit(); + bht_global_uninit(); +} + +/* + * Driver parameter and Information + */ + +/* set first call function name when driver insert/load. */ +module_init(bht_sd_init); +/* set first call function name when driver remove/unload. */ +module_exit(bht_sd_exit); +MODULE_DESCRIPTION("BayHub SD Card reader device driver"); +MODULE_AUTHOR("BayHub Inc."); +#if BHT_LINUX_ENABLE_RTD3 +MODULE_LICENSE("GPL"); +#else +MODULE_LICENSE("GPL"); +#endif + +MODULE_VERSION("v1008_00_00"); diff --git a/drivers/scsi/bht/linux_os/linux_scsi.c b/drivers/scsi/bht/linux_os/linux_scsi.c new file mode 100644 index 000000000000..64b34cc7ad56 --- /dev/null +++ b/drivers/scsi/bht/linux_os/linux_scsi.c @@ -0,0 +1,1076 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015 BHT Inc. + * + * File Name: linux_scsi.c + * + * Abstract: SCSI function + * + * Version: 1.00 + * + * Author: Peter.Guo + * + * Environment: Linux + * + * History: + * + * 5/20/2015 Creation Peter.Guo + */ + +#include +#include +#include +#include +#include + +#include "../include/basic.h" +#include "../include/reqapi.h" +#include "../include/funcapi.h" +#include "../include/tqapi.h" +#include "../include/cardapi.h" +#include "../include/hostapi.h" +#include "../include/debug.h" +#include "../include/util.h" +#include "linux_scsi.h" +#include "../tagqueue/tq_trans_api.h" +#include "../include/cmdhandler.h" + +extern struct scsi_host_template bht_scsi_template; + +static srb_ext_t *bht_scsi_alloc_srb_ext(bht_dev_ext_t *pdx, + struct scsi_cmnd *srb) +{ + /* todo use cache to improve performance */ + srb_ext_t *srb_ext = mempool_alloc(bht_sd_mem_pool, GFP_ATOMIC); + + if (srb_ext == NULL) { + DbgErr("alloc srbext is null\n"); + + goto exit; + } + memset(srb_ext, 0, sizeof(srb_ext_t)); + srb_ext->psrb = srb; +exit: + return srb_ext; +} + +static void bht_scsi_free_srb_ext(bht_dev_ext_t *pdx, srb_ext_t *srb_ext) +{ + if (srb_ext != NULL) + mempool_free(srb_ext, bht_sd_mem_pool); +} + +/* + * The function to register and Init scsi host + */ +bool bht_scsi_init(bht_dev_ext_t *pdx, struct device *dev) +{ + bool ret = FALSE; + int error = 0; + + DbgInfo(MODULE_OS_ENTRY, FEATURE_FUNC_TRACE, NOT_TO_RAM, + "BHT scsi init begin\n"); + + if (pdx->scsi_init_flag == 1) { + DbgInfo(MODULE_OS_ENTRY, FEATURE_FUNC_TRACE, NOT_TO_RAM, + "BHT scsi initialized, and return\n"); + return TRUE; + } + + bht_scsi_template.can_queue = bht_scsi_template.cmd_per_lun = + os_min(pdx->cfg->host_item.test_tag_queue_capability.max_srb, + LINUX_SCSI_MAX_QUEUE_DPETH); + pdx->os.scsi_host = + scsi_host_alloc(&bht_scsi_template, sizeof(unsigned long)); + if (!pdx->os.scsi_host) { + DbgErr("Unable to register controller with SCSI subsystem\n"); + goto exit; + } + + pdx->dev = dev; + pdx->os.scsi_host->hostdata[0] = (unsigned long)pdx; + pdx->os.scsi_host->irq = pdx->host.pci_dev.irq; + pdx->os.scsi_host->base = (unsigned long)pdx->host.pci_dev.membase; + pdx->os.scsi_host->max_id = 1; + pdx->os.scsi_host->max_lun = 1; +#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 13, 0) + pdx->os.scsi_host->no_write_same = 1; +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0) + pdx->os.scsi_host->use_clustering = 1; +#endif + pdx->os.scsi_host->max_sectors = bht_scsi_template.max_sectors; + + error = scsi_add_host(pdx->os.scsi_host, dev); + if (error) { + DbgErr("scsi add host failed\n"); + return FALSE; + } + scsi_scan_host(pdx->os.scsi_host); + pdx->scsi_init_flag = 1; + ret = TRUE; + +exit: + DbgInfo(MODULE_OS_ENTRY, FEATURE_FUNC_TRACE, NOT_TO_RAM, + "BHT scsi init end\n"); + return ret; +} + +/* + * The function to remove scsi host + */ +void bht_scsi_uinit(bht_dev_ext_t *pdx) +{ + DbgInfo(MODULE_OS_ENTRY, FEATURE_FUNC_TRACE, NOT_TO_RAM, + "BHT scsi uinit begin\n"); + if (pdx->scsi_init_flag == 0) { + DbgInfo(MODULE_OS_ENTRY, FEATURE_FUNC_TRACE, NOT_TO_RAM, + "BHT scsi uinit excuted,and return\n"); + return; + } + if ((pdx != NULL) && (&pdx->os != NULL) && (pdx->os.scsi_host != NULL)) { + scsi_remove_host(pdx->os.scsi_host); + scsi_host_put(pdx->os.scsi_host); + pdx->scsi_init_flag = 0; + } + DbgInfo(MODULE_OS_ENTRY, FEATURE_FUNC_TRACE, NOT_TO_RAM, + "BHT scsi uinit end\n"); +} + +/* + * This function is used to save the sense code and fill sense buffer + */ +static void bht_scsi_set_sensecode(bht_dev_ext_t *pdx, struct scsi_cmnd *srb, + byte sense_key, byte sense_code) +{ + PSENSE_DATA senseBuffer = (PSENSE_DATA) srb->sense_buffer; + + pdx->scsi.sense_key = sense_key; + pdx->scsi.sense_code = sense_code; + + if (senseBuffer != NULL && sense_key != 0) { + senseBuffer->ErrorCode = 0x70; + senseBuffer->AdditionalSenseLength = 0xb; + senseBuffer->SenseKey = pdx->scsi.sense_key; + senseBuffer->AdditionalSenseCode = pdx->scsi.sense_code; + senseBuffer->AdditionalSenseCodeQualifier = 0; + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM, + "%s sense=%d code=%d\n", __func__, sense_key, + sense_code); + } + +} + +/* + * Handle Scsi command Test unit ready + */ +static void bht_scsi_test_unit_ready(bht_dev_ext_t *pdx, struct scsi_cmnd *srb) +{ + if (req_card_ready(pdx)) { + srb->result = GOOD; + } else { + srb->result = CHECK_CONDITION; + bht_scsi_set_sensecode(pdx, srb, SCSI_SENSE_NOT_READY, + SCSI_ADSENSE_NO_MEDIA_IN_DEVICE); + } + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM, + " %s, SrbResult:%x\n", __func__, srb->result); +} + +/* + * Handle SCSI Command Request Sense + */ +static void bht_scsi_request_sense(bht_dev_ext_t *pdx, struct scsi_cmnd *srb) +{ + + PSENSE_DATA senseBuffer = (PSENSE_DATA) srb->sense_buffer; + + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM, + "Enter %s\n", __func__); + if (senseBuffer == NULL) { + srb->result = CHECK_CONDITION; + } else { + srb->result = GOOD; + senseBuffer->ErrorCode = 0x70; + senseBuffer->AdditionalSenseLength = 0xb; + senseBuffer->SenseKey = pdx->scsi.sense_key; + senseBuffer->AdditionalSenseCode = pdx->scsi.sense_code; + senseBuffer->AdditionalSenseCodeQualifier = 0; + bht_scsi_set_sensecode(pdx, srb, 0, 0); + } + + scsi_sg_copy_from_buffer(srb, (byte *) senseBuffer, + os_min(srb->sdb.length, sizeof(SENSE_DATA))); + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM, + "Exit %s, SrbResult=%d\n", __func__, srb->result); +} + +/* + * Handle Mode Sense scsi command + */ +static void bht_scsi_exec_modesense(bht_dev_ext_t *pdx, struct scsi_cmnd *srb) +{ + MODE_PAGE_HEADER hdr; + MODE_PAGE_8 pg_8; + MODEPAGE19 pg_9; + byte pmdata[512]; + byte mode_code; + sd_card_t *card = &pdx->card; + + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM, + "Enter %s\n", __func__); + memset(pmdata, 0, 512); + + hdr.DataLength = 3; + hdr.MediumType = 0x00; + hdr.Reserved = 0; + + if (card->write_protected) + hdr.Reserved |= 0x80; + + hdr.BlockDescLength = 0; + pg_8.PageCode = 0x08; + pg_8.PageSavable = 1; + pg_8.PageLength = 0x12; + + pg_8.WriteCacheEnable = 1; + pg_8.DisablePrefetchTransfer[0] = 0xFF; + pg_8.DisablePrefetchTransfer[1] = 0xFF; + pg_8.MaximumPrefetch[0] = 0xFF; + pg_8.MaximumPrefetch[1] = 0xFF; + pg_8.MaximumPrefetchCeiling[0] = 0xFF; + pg_8.MaximumPrefetchCeiling[1] = 0xFF; + pg_8.NumberofCacheSegments = 0x20; + + pg_9.PageCode = 0x19; + pg_9.PageSavable = 1; + pg_9.PageLength = 0x04; + pg_9.ProtocolIdentifier = 0x0A; + pg_9.SynchronousTransferTimeout[0] = 0x00; + pg_9.SynchronousTransferTimeout[1] = 0x01; + + mode_code = srb->cmnd[2]; + + switch (mode_code) { + case 0x08: + hdr.DataLength += sizeof(MODE_PAGE_8); + memcpy(pmdata, &hdr, sizeof(MODE_PAGE_HEADER)); + memcpy(pmdata + sizeof(MODE_PAGE_HEADER), &pg_8, + sizeof(MODE_PAGE_8)); + break; + case 0x19: + hdr.DataLength += sizeof(MODEPAGE19); + memcpy(pmdata, &hdr, sizeof(MODE_PAGE_HEADER)); + memcpy(pmdata + sizeof(MODE_PAGE_HEADER), &pg_9, + sizeof(MODEPAGE19)); + break; + case 0x3F: + hdr.DataLength += (sizeof(MODEPAGE19) + sizeof(MODE_PAGE_8)); + memcpy(pmdata, &hdr, sizeof(MODE_PAGE_HEADER)); + memcpy(pmdata + sizeof(MODE_PAGE_HEADER), &pg_8, + sizeof(MODE_PAGE_8)); + memcpy(pmdata + sizeof(MODE_PAGE_HEADER) + sizeof(MODE_PAGE_8), + &pg_9, sizeof(MODEPAGE19)); + break; + default: + break; + } + + srb->result = GOOD; + scsi_sg_copy_from_buffer(srb, pmdata, os_min(srb->sdb.length, 512)); + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM, + "Exit %s\n", __func__); +} + +/* + * Handle SCSI Command Allow Removalabe + */ +static void bht_scsi_allow_removal(bht_dev_ext_t *pdx, struct scsi_cmnd *srb) +{ + byte prevent = srb->cmnd[4] & 0x3; + + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM, + "Enter %s prevent=%d\n", __func__, prevent); + if (req_card_ready(pdx) == FALSE) { + srb->result = CHECK_CONDITION; + + bht_scsi_set_sensecode(pdx, srb, SCSI_SENSE_NOT_READY, + SCSI_ADSENSE_NO_MEDIA_IN_DEVICE); + } else { + srb->result = GOOD; + if (prevent == 0x01 || prevent == 0x11) + pdx->scsi.prevent_eject = 1; + else + pdx->scsi.prevent_eject = 0; + } + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM, + "Exit %s\n", __func__); +} + +static bool bht_scsi_error_handle(bht_dev_ext_t *pdx, struct scsi_cmnd *srb, + e_req_result result) +{ + bool ret = TRUE; + + if (result == REQ_RESULT_PENDING) { + } else if (result == REQ_RESULT_QUEUE_BUSY) { + } else if (result == REQ_RESULT_NO_CARD) { + srb->result = CHECK_CONDITION; + bht_scsi_set_sensecode(pdx, srb, SCSI_SENSE_NOT_READY, + SCSI_ADSENSE_NO_MEDIA_IN_DEVICE); + } else if (result == REQ_RESULT_PROTECTED) { + srb->result = CHECK_CONDITION; + bht_scsi_set_sensecode(pdx, srb, SCSI_SENSE_DATA_PROTECT, + SCSI_ADSENSE_WRITE_PROTECT); + } else { + srb->result = CHECK_CONDITION; + bht_scsi_set_sensecode(pdx, srb, SCSI_SENSE_ILLEGAL_REQUEST, + SCSI_ADSENSE_ILLEGAL_BLOCK); + } + + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM, + " %s, SrbResult:%x\n", __func__, srb->result); + return ret; +} + +/* + * Fill Capacity Data + */ +static void bht_scsi_get_capacity(bht_dev_ext_t *pdx, u32 secnt, + struct scsi_cmnd *srb) +{ + READ_CAPACITY_DATA capacity; + PREAD_CAPACITY_DATA pcapacity = &capacity; + + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM, + "Enter %s\n", __func__); + + memset(pcapacity, 0, sizeof(READ_CAPACITY_DATA)); + /* Big Endian for Capacity data */ + pcapacity->LogicalBlockAddress = swapu32((secnt > 0) ? (secnt - 1) : 0); + /* Big Endian for Capacity data */ + pcapacity->BytesPerBlock = swapu32(SD_BLOCK_LEN); + srb->result = GOOD; + scsi_sg_copy_from_buffer(srb, pcapacity, + os_min(srb->sdb.length, + sizeof(READ_CAPACITY_DATA))); + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM, + "Exit %s, capacity:%x\n", __func__, + pcapacity->LogicalBlockAddress); +} + +/* + * Fill Inquriy Data function + */ +static void bht_scsi_get_inquiry(bht_dev_ext_t *pdx, e_card_type card_type, + struct scsi_cmnd *srb) +{ + _INQUIRYDATA inqdata; + _INQUIRYDATA *pinqdata = &inqdata; + + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM, + "Enter %s\n", __func__); + + os_memset(pinqdata, 0, sizeof(_INQUIRYDATA)); + /* TYPE_DISK; */ + pinqdata->DeviceType = 0x00; + + pinqdata->Versions = 0x00; + pinqdata->ResponseDataFormat = 0x02; + pinqdata->AdditionalLength = sizeof(_INQUIRYDATA) - 4; + strncpy((char *)(pinqdata->VendorId), "BHT-SD ", 8); + switch (card_type) { + case CARD_SDIO: + strncpy((char *)(pinqdata->ProductId), "SDIO ", 5); + break; + case CARD_SD: + case CARD_UHS2: + strncpy((char *)(pinqdata->ProductId), "SD ", 3); + break; + case CARD_EMMC: + case CARD_MMC: + strncpy((char *)(pinqdata->ProductId), "MMC ", 4); + break; + case CARD_TEST: + strncpy((char *)(pinqdata->ProductId), "TestCR ", 6); + break; + default: + strncpy((char *)(pinqdata->ProductId), "CR ", 16); + break; + } + strncpy((char *)(pinqdata->ProductRevisionLevel), "104a", 4); + srb->result = GOOD; + scsi_sg_copy_from_buffer(srb, pinqdata, + os_min(srb->sdb.length, sizeof(_INQUIRYDATA))); + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM, + "Exit %s\n", __func__); +} + +/* + * Callback for basci scsi command + */ +static void bht_scsi_srb_basic_cmd_done(void *p, void *srb_ext) +{ + srb_ext_t *p_srb_ext = srb_ext; + bht_dev_ext_t *pdx = p; + struct scsi_cmnd *srb = p_srb_ext->psrb; + sd_card_t *card = &pdx->card; + + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM, + "Enter %s\n", __func__); + if (srb == NULL) { + pdx->p_srb_ext = NULL; + goto exit; + } + if (p_srb_ext->req.result != REQ_RESULT_OK) { + srb->result = CHECK_CONDITION; + bht_scsi_set_sensecode(pdx, srb, SCSI_SENSE_NOT_READY, + SCSI_ADSENSE_NO_MEDIA_IN_DEVICE); + goto end; + } + + switch (srb->cmnd[0]) { + case INQUIRY: + { + bht_scsi_get_inquiry(pdx, card->card_type, srb); + break; + } + case READ_CAPACITY: + { + bht_scsi_get_capacity(pdx, (u32) card->sec_count, srb); + break; + } + case START_STOP: + { + srb->result = GOOD; + break; + } + default: + break; + } + +end: + bht_scsi_free_srb_ext(pdx, srb_ext); + pdx->p_srb_ext = NULL; + + scsi_done(srb); +exit: + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM, + "Exit %s\n", __func__); +} + +/* + * handle start stop scsi command + * return true means done while return false means pending + */ +static bool bht_scsi_load_unload(bht_dev_ext_t *pdx, struct scsi_cmnd *srb, + bool *busy) +{ + bool ret = TRUE; + byte load = srb->cmnd[4] & 0x01; + byte loej = srb->cmnd[4] & 0x02; + e_req_result result = REQ_RESULT_OK; + srb_ext_t *srb_ext = NULL; + + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM, + "Enter %s load=%d loej=%d\n", __func__, load, loej); + + if (loej == 0) { + srb->result = GOOD; + goto exit; + } + + if (load) { + /* + * For Load command we don't need use thread + * because card access module can Init card + */ + + pdx->scsi.scsi_eject = 0; + srb->result = GOOD; + + if (req_card_ready(pdx) == FALSE) { + srb->result = CHECK_CONDITION; + DbgErr("Scsi load failed\n"); + bht_scsi_set_sensecode(pdx, srb, SCSI_SENSE_NOT_READY, + SCSI_ADSENSE_NO_MEDIA_IN_DEVICE); + } + goto exit; + } else { + /* already ejected */ + if (pdx->scsi.scsi_eject) { + srb->result = GOOD; + goto exit; + } + /* eject is not allowed */ + else if (pdx->scsi.prevent_eject) { + srb->result = CHECK_CONDITION; + bht_scsi_set_sensecode(pdx, srb, + SCSI_SENSE_ILLEGAL_REQUEST, + SCSI_ADSENSE_ILLEGAL_COMMAND); + goto exit; + } + } + + /* below code is for unload operation */ + srb_ext = bht_scsi_alloc_srb_ext(pdx, srb); + if (srb_ext == NULL) { + result = REQ_RESULT_ABORT; + goto exit; + } + srb_ext->req.data_dir = DATA_DIR_NONE; + srb_ext->req.srb_buff = NULL; + srb_ext->req.srb_sg_len = 0; + srb_ext->req.gen_req_t.code = GEN_IO_CODE_EJECT; + srb_ext->req.gen_req_t.arg1 = load; + srb_ext->req.srb_done_cb = bht_scsi_srb_basic_cmd_done; + result = req_eject(pdx, srb_ext); + + if (result == REQ_RESULT_OK) { + pdx->scsi.scsi_eject = 1; + srb->result = GOOD; + } else { + bht_scsi_error_handle(pdx, srb, result); + /* cmd handle by thread case */ + if (result == REQ_RESULT_PENDING) + pdx->scsi.scsi_eject = 1; + } + +exit: + if (result == REQ_RESULT_PENDING) { + *busy = TRUE; + ret = FALSE; + } else { + if (result == REQ_RESULT_QUEUE_BUSY) + *busy = TRUE; + bht_scsi_free_srb_ext(pdx, srb_ext); + } + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM, + "Enter %s, ret:%x\n", __func__, ret); + return ret; +} + +/* + * Handle Inquriy scsi command + * return true means done while return false means pending + */ +static bool bht_scsi_exec_inquiry(bht_dev_ext_t *pdx, struct scsi_cmnd *srb, + bool *busy) +{ + + e_req_result result = REQ_RESULT_NO_CARD; + sd_card_t *card = &pdx->card; + srb_ext_t *srb_ext = NULL; + bool ret = TRUE; + + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM, + "Enter %s\n", __func__); + + srb_ext = bht_scsi_alloc_srb_ext(pdx, srb); + if (srb_ext == NULL) { + result = REQ_RESULT_ABORT; + goto exit; + } + srb_ext->req.data_dir = DATA_DIR_IN; + /* the callback will handle the data itself */ + srb_ext->req.srb_buff = NULL; + srb_ext->req.gen_req_t.code = GEN_IO_CODE_INIT_CARD; + srb_ext->req.srb_done_cb = bht_scsi_srb_basic_cmd_done; + result = req_chk_card_info(pdx, srb_ext); + + if (result == REQ_RESULT_OK) { + bht_scsi_get_inquiry(pdx, card->card_type, srb); + } else { + if (result == REQ_RESULT_QUEUE_BUSY) + *busy = TRUE; + bht_scsi_error_handle(pdx, srb, result); + } +exit: + if (result == REQ_RESULT_PENDING) + ret = FALSE; + else + bht_scsi_free_srb_ext(pdx, srb_ext); + + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM, + "Exit %s, ret:%x\n", __func__, ret); + return ret; +} + +/* + * Handle GetCapacity scsi command + * return true means done while return false means pending + */ +static bool bht_scsi_exec_capacity(bht_dev_ext_t *pdx, struct scsi_cmnd *srb, + bool *busy) +{ + e_req_result result = REQ_RESULT_NO_CARD; + sd_card_t *card = &pdx->card; + srb_ext_t *srb_ext; + + bool ret = TRUE; + + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM, + "Enter %s\n", __func__); + + srb_ext = bht_scsi_alloc_srb_ext(pdx, srb); + if (srb_ext == NULL) { + result = REQ_RESULT_ABORT; + goto exit; + } + srb_ext->req.data_dir = DATA_DIR_IN; + /* the callback will handle the data itself */ + srb_ext->req.srb_buff = NULL; + srb_ext->req.srb_sg_len = 0; + srb_ext->req.gen_req_t.code = GEN_IO_CODE_INIT_CARD; + srb_ext->req.srb_done_cb = bht_scsi_srb_basic_cmd_done; + + result = req_chk_card_info(pdx, srb_ext); + + if (result == REQ_RESULT_OK) { + bht_scsi_get_capacity(pdx, (u32) (card->sec_count), srb); + } else { + if (result == REQ_RESULT_QUEUE_BUSY) + *busy = TRUE; + bht_scsi_error_handle(pdx, srb, result); + } +exit: + if (result == REQ_RESULT_PENDING) + ret = FALSE; + else + bht_scsi_free_srb_ext(pdx, srb_ext); + + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_SCSICMD_TRACE, NOT_TO_RAM, + "Exit %s, ret:%x\n", __func__, ret); + return ret; +} + +/* + * Helper function to get rw parameter + */ +static void bht_scsi_get_rw_parameter(byte *cdb, u32 *pStartBlock, + u32 *pBlockCount) +{ + byte array[4]; + + array[0] = cdb[5]; + array[1] = cdb[4]; + array[2] = cdb[3]; + array[3] = cdb[2]; + *pStartBlock = *((u32 *) &array); + + array[0] = cdb[8]; + array[1] = cdb[7]; + array[2] = 0; + array[3] = 0; + *pBlockCount = *((u32 *) &array); + + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_RW_TRACE, NOT_TO_RAM, + "%s, StartBlock:%x, BlockCount:%x\n", __func__, + *pStartBlock, *pBlockCount); +} + +/* + * Call back for Tagged io and scsi rw + */ +static void bht_scsi_srb_tagio_done(void *p, void *srb_ext) +{ + srb_ext_t *p_srb_ext = srb_ext; + srb_ext_t *p_srb_ext_1 = p_srb_ext; + srb_ext_t *p_srb_ext_2 = NULL; + bht_dev_ext_t *pdx = p; + struct scsi_cmnd *srb = p_srb_ext->psrb; + + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + if (srb == NULL) { + pdx->p_srb_ext = NULL; + DbgErr("tagio complete called with srb is null\n"); + goto exit; + } + + srb->result = CHECK_CONDITION; + + switch (p_srb_ext->req.result) { + case REQ_RESULT_OK: + { + if (p_srb_ext->prev != NULL) { + p_srb_ext_1 = p_srb_ext->prev; + p_srb_ext_2 = p_srb_ext; + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_RW_TRACE, + NOT_TO_RAM, + "p_srb_ext_2: dir=%d, sec_cnt=%d, sec_addr=0x%x, p_srb_ext_1=%p\n", + p_srb_ext_2->req.data_dir, + p_srb_ext_2->req.tag_req_t.sec_cnt, + p_srb_ext_2->req.tag_req_t.sec_addr, + p_srb_ext_2->prev); + } else if (p_srb_ext->req.data_dir == DATA_DIR_IN + && p_srb_ext->req.tag_req_t.sec_cnt > 1 + && (p_srb_ext->req.tag_req_t.sec_addr + + p_srb_ext->req.tag_req_t.sec_cnt == + pdx->card.sec_count)) { + p_srb_ext_1 = p_srb_ext; + p_srb_ext_2 = bht_scsi_alloc_srb_ext(pdx, srb); + if (p_srb_ext_2 == NULL) { + DbgErr + ("Failed when alloc srb_ext_2 for read latest block\n"); + } else { + p_srb_ext_2->prev = p_srb_ext_1; + p_srb_ext_1->next = p_srb_ext_2; + p_srb_ext_2->req.data_dir = DATA_DIR_IN; + p_srb_ext_2->req.tag_req_t.sec_cnt = 1; + p_srb_ext_2->req.tag_req_t.sec_addr = + pdx->card.sec_count - 1; + p_srb_ext_2->req.tag_req_t.use_cmd = 0; + p_srb_ext_2->req.srb_done_cb = + bht_scsi_srb_tagio_done; + p_srb_ext_2->req.srb_buff = + (PVOID) ((unsigned long long) + p_srb_ext->req.srb_buff + + (p_srb_ext->req.tag_req_t.sec_cnt - + 1) * SD_BLOCK_LEN); + } + } + if (FALSE == + cfg_dma_need_sdma_like_buffer(pdx->cfg->host_item.test_dma_mode_setting.dma_mode)) { + if (p_srb_ext_2 == NULL + || p_srb_ext_2 == p_srb_ext) + os_free_sg_list(pdx, srb); + else { + if (p_srb_ext_1->req.srb_sg_list[p_srb_ext_1->req.srb_sg_len - + 1].Length >= + SD_BLOCK_LEN) { + p_srb_ext_2->req.srb_sg_len = 1; + p_srb_ext_2->req.srb_sg_list[0].Address = + p_srb_ext_1->req.srb_sg_list + [p_srb_ext_1->req.srb_sg_len - 1].Address + + p_srb_ext_1->req.srb_sg_list + [p_srb_ext_1->req.srb_sg_len - 1].Length - + SD_BLOCK_LEN; + p_srb_ext_2->req.srb_sg_list[0].Length = + SD_BLOCK_LEN; + } else { + DbgErr + ("sg list latest record size is %d, less than SD block size\n", + p_srb_ext_1->req.srb_sg_list + [p_srb_ext_1->req.srb_sg_len + - 1].Length); + bht_scsi_free_srb_ext(pdx, + p_srb_ext_2); + p_srb_ext_1->next = NULL; + p_srb_ext_2 = NULL; + } + } + } else { + if (p_srb_ext->req.data_dir == DATA_DIR_IN) { + if (p_srb_ext_2 == NULL + || p_srb_ext_2 == p_srb_ext) + scsi_sg_copy_from_buffer(srb, + p_srb_ext->req.srb_buff, + srb->sdb.length); + } + } + srb->result = GOOD; + break; + } + case REQ_RESULT_NO_CARD: + case REQ_RESULT_ABORT: + { + bht_scsi_set_sensecode(pdx, srb, SCSI_SENSE_NOT_READY, + SCSI_ADSENSE_NO_MEDIA_IN_DEVICE); + break; + } + case REQ_RESULT_PROTECTED: + { + bht_scsi_set_sensecode(pdx, srb, + SCSI_SENSE_DATA_PROTECT, + SCSI_ADSENSE_WRITE_PROTECT); + break; + } + /* RW error case */ + default: + { + bht_scsi_set_sensecode(pdx, srb, + SCSI_SENSE_UNIT_ATTENTION, 0); + break; + } + + } + + /* tag queue is empty we need to set device to free status */ + if (tq_is_empty(pdx)) { + func_autotimer_start(pdx); + if (pdx->host.feature.hw_led_fix == 0) + host_led_ctl(&pdx->host, FALSE); + } + + if (p_srb_ext_2 == NULL || p_srb_ext_2 == p_srb_ext) { + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_RW_TRACE, NOT_TO_RAM, + "scsi io done for srb(%p)\n", srb); + bht_scsi_free_srb_ext(pdx, p_srb_ext_2); + bht_scsi_free_srb_ext(pdx, p_srb_ext_1); + pdx->p_srb_ext = NULL; + + scsi_done(srb); + } else { + int result = req_tag_io_add(pdx, p_srb_ext_2); + + if (result != REQ_RESULT_OK) + bht_scsi_error_handle(pdx, srb, result); + + if (result == REQ_RESULT_PENDING) { + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_RW_TRACE, + NOT_TO_RAM, + "add srb_ext_2(%p) of srb(%p) to tag io queue\n", + p_srb_ext_2, srb); + } else { + DbgErr("Failed to add srb_ext_2 %p to tag io queue\n", + p_srb_ext_2); + bht_scsi_free_srb_ext(pdx, p_srb_ext_1); + pdx->p_srb_ext = NULL; + + scsi_done(srb); + } + } + +#ifdef DBG_PERFORMANCE + calc_io_end(&pdx->tick); +#endif + +exit: + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +/* + * Handle Read10 and Write10 scsi command + * return true means done while return false means pending + */ +static bool bht_scsi_exec_rw(bht_dev_ext_t *pdx, struct scsi_cmnd *srb, + bool bWrite, bool *busy) +{ + e_req_result result = REQ_RESULT_NO_CARD; + srb_ext_t *srb_ext; + bool ret = TRUE; + + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + srb_ext = bht_scsi_alloc_srb_ext(pdx, srb); + if (srb_ext == NULL) { + result = REQ_RESULT_ABORT; + goto exit; + } + srb_ext->psrb = srb; + srb_ext->req.data_dir = bWrite ? DATA_DIR_OUT : DATA_DIR_IN; + srb_ext->req.srb_buff = pdx->os.virt_buff; + if (FALSE == + cfg_dma_need_sdma_like_buffer(pdx->cfg->host_item.test_dma_mode_setting.dma_mode)) + srb_ext->req.srb_sg_len = + os_get_sg_list(pdx, srb, srb_ext->req.srb_sg_list); + else { + if (bWrite) + scsi_sg_copy_to_buffer(srb, srb_ext->req.srb_buff, + srb->sdb.length); + srb_ext->req.srb_sg_len = 0; + } + srb_ext->req.srb_done_cb = bht_scsi_srb_tagio_done; + srb_ext->req.tag_req_t.use_cmd = 0; + + /* + * Get Scsi parameter for RW + */ + bht_scsi_get_rw_parameter(srb->cmnd, + &srb_ext->req.tag_req_t.sec_addr, + &srb_ext->req.tag_req_t.sec_cnt); + calc_req_start(&pdx->tick, srb_ext->req.tag_req_t.sec_cnt, bWrite); + + /* Workaround for GG8 chip DDR200 write operation: timing issue */ + if (bWrite && pdx->host.chip_type == CHIP_GG8 + && pdx->card.info.sw_cur_setting.sd_access_mode == + SD_FNC_AM_DDR200) { + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_RW_TRACE, NOT_TO_RAM, + "Enter GG8 DDR200 workaround patch\n"); + if (tq_judge_request_continuous + (card_is_low_capacity(&pdx->card), pdx->last_req.data_dir, + pdx->last_req.sec_addr, pdx->last_req.sec_cnt, + srb_ext->req.data_dir, + srb_ext->req.tag_req_t.sec_addr) == FALSE) { + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_RW_TRACE, + NOT_TO_RAM, + "Set flag to add NOP descriptor table\n"); + srb_ext->req.gg8_ddr200_workaround = 1; + } else + srb_ext->req.gg8_ddr200_workaround = 0; + + } else + srb_ext->req.gg8_ddr200_workaround = 0; + + pdx->last_req.data_dir = srb_ext->req.data_dir; + pdx->last_req.sec_addr = srb_ext->req.tag_req_t.sec_addr; + pdx->last_req.sec_cnt = srb_ext->req.tag_req_t.sec_cnt; + + result = req_tag_io_add(pdx, srb_ext); + +exit: + if (result != REQ_RESULT_OK) + bht_scsi_error_handle(pdx, srb, result); + + if (result == REQ_RESULT_PENDING) + ret = FALSE; + else { + if (result == REQ_RESULT_QUEUE_BUSY) + *busy = TRUE; + bht_scsi_free_srb_ext(pdx, srb_ext); + } + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_RW_TRACE, NOT_TO_RAM, + "Exit %s, ret:%x\n", __func__, ret); + return ret; +} + +/* + * The Entry to handle SCSI command + */ +static int bht_scsi_queuecommand_lck(struct scsi_cmnd *srb) +{ + bht_dev_ext_t *pdx = (bht_dev_ext_t *) (srb->device->host->hostdata[0]); + bool cmd_done = TRUE; + bool dev_busy = FALSE; + + DbgInfo(MODULE_OS_ENTRYAPI, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + + if (pdx->signature != BHT_PDX_SIGNATURE) { + DbgErr("bht scsi queuecommand pdx signature is wrong\n"); + return SCSI_MLQUEUE_HOST_BUSY; + } + + if (srb->cmnd == NULL) { + DbgErr + ("bhtscsi_queuecommand srb->cmnd is NULL, cmd_len is %d ,srb %p\n", + srb->cmd_len, srb); + return SCSI_RETURN_NOT_HANDLED; + } + + srb->result = 0; + func_thermal_update_time(pdx); + + switch (srb->cmnd[0]) { + case TEST_UNIT_READY: + { + bht_scsi_test_unit_ready(pdx, srb); + break; + } + case REQUEST_SENSE: + { + bht_scsi_request_sense(pdx, srb); + break; + } + case INQUIRY: + { + cmd_done = bht_scsi_exec_inquiry(pdx, srb, &dev_busy); + break; + } + case READ_CAPACITY: + { + cmd_done = bht_scsi_exec_capacity(pdx, srb, &dev_busy); + break; + } + case READ_10: + { + cmd_done = bht_scsi_exec_rw(pdx, srb, FALSE, &dev_busy); + break; + } + case WRITE_10: + { + cmd_done = bht_scsi_exec_rw(pdx, srb, TRUE, &dev_busy); + break; + } + case MODE_SENSE: + { + bht_scsi_exec_modesense(pdx, srb); + break; + } + case VERIFY: + case SEND_DIAGNOSTIC: + case SYNCHRONIZE_CACHE: + { + srb->result = GOOD; + + scsi_done(srb); + break; + } + case START_STOP: + { + cmd_done = bht_scsi_load_unload(pdx, srb, &dev_busy); + break; + } + + case ALLOW_MEDIUM_REMOVAL: + { + bht_scsi_allow_removal(pdx, srb); + break; + } + + default: + { + srb->result = CHECK_CONDITION; + bht_scsi_set_sensecode(pdx, srb, + SCSI_SENSE_ILLEGAL_REQUEST, + SCSI_ADSENSE_ILLEGAL_COMMAND); + break; + } + } + + if (cmd_done && dev_busy == FALSE) + scsi_done(srb); + else if (dev_busy) + return SCSI_MLQUEUE_DEVICE_BUSY; + return 0; + +} + +static enum scsi_timeout_action bht_scsi_eh_timeout(struct scsi_cmnd *srb) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) + enum scsi_timeout_action retval = SCSI_EH_DONE; +#else + enum blk_eh_timer_return retval = BLK_EH_NOT_HANDLED; +#endif + + bht_dev_ext_t *pdx = (bht_dev_ext_t *) (srb->device->host->hostdata[0]); + + /* todo It is may not safer for busreset is at high IRQL; and one event to slove this */ + DbgErr("SCSI eh_timeout Enter\n"); + if (os_pending_thread(pdx, TRUE) == FALSE) + DbgErr("bus rest pending thread failed\n"); + func_autotimer_stop(pdx); + card_power_off(&pdx->card, TRUE); + host_init(&pdx->host); + req_cancel_all_io(pdx); + os_pending_thread(pdx, FALSE); + DbgErr("SCSI eh_timeout Exit\n"); + + return retval; +} + +static DEF_SCSI_QCMD(bht_scsi_queuecommand) +/* + * this defines our 'SCSI host' + */ +struct scsi_host_template bht_scsi_template = { + .name = "BHT SD Card Reader", + .module = THIS_MODULE, + .proc_name = "bht_scsi_host", + .queuecommand = bht_scsi_queuecommand, + .eh_timed_out = bht_scsi_eh_timeout, + .can_queue = 1, + .this_id = -1, + .sg_tablesize = SG_ALL, + .max_sectors = CFG_MAX_TRANSFER_LENGTH / SD_BLOCK_LEN, + .cmd_per_lun = 1, + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0) + .use_clustering = TRUE, +#endif + + .emulated = FALSE, +}; diff --git a/drivers/scsi/bht/linux_os/linux_scsi.h b/drivers/scsi/bht/linux_os/linux_scsi.h new file mode 100644 index 000000000000..0b4d2a8bd5db --- /dev/null +++ b/drivers/scsi/bht/linux_os/linux_scsi.h @@ -0,0 +1,239 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2015 BHT Inc. + * + * File Name: linux_scsi.h + * + * Abstract: SCSI function + * + * Version: 1.00 + * + * Author: Peter.Guo + * + * Environment: Linux + * + * History: + * + * 5/20/2015 Creation Peter.Guo + */ + +#ifndef _BHT_SCSI_H +#define _BHT_SCSI_H + +#define LINUX_SCSI_MAX_QUEUE_DPETH 32 + +bool bht_scsi_init(bht_dev_ext_t *pdx, struct device *dev); +void bht_scsi_uinit(bht_dev_ext_t *pdx); + +typedef struct { + byte ErrorCode; + byte Reserved; + byte SenseKey; + byte Infomation[4]; + byte AdditionalSenseLength; + byte CommandSpecInfo[4]; + byte AdditionalSenseCode; + byte AdditionalSenseCodeQualifier; + byte FieldReplaceable; + byte SenseKeySpec[3]; +} SENSE_DATA, *PSENSE_DATA; + +typedef struct { + byte DeviceType:5; + byte DeviceTypeQualifier:3; + byte DeviceTypeModifier:7; + byte RemovableMedia:1; + byte Versions; + byte ResponseDataFormat:4; + byte HiSupport:1; + byte NormACA:1; + byte ReservedBit:1; + byte AERC:1; + byte AdditionalLength; + byte Reserved[2]; + byte SoftReset:1; + byte CommandQueue:1; + byte Reserved2:1; + byte LinkedCommands:1; + byte Synchronous:1; + byte Wide16Bit:1; + byte Wide32Bit:1; + byte RelativeAddressing:1; + byte VendorId[8]; + byte ProductId[16]; + byte ProductRevisionLevel[4]; + byte VendorSpecific[20]; + byte IUS:1; + byte QAS:1; + byte Clocking:2; + byte Reserved3_first_byte:4; + byte Reserved3[39]; +} _INQUIRYDATA; + +typedef struct { + u32 LogicalBlockAddress; + u32 BytesPerBlock; +} READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA; + +typedef struct { + byte DataLength; + byte MediumType; + byte Reserved; + byte BlockDescLength; +} MODE_PAGE_HEADER; + +typedef struct { + u8 PageCode; + u8 PageLength; + u16 TracksPerZone; + u16 AltSectorsPerZone; + u16 AltTracksPerZone; + u16 AltTracksPerVolume; + u16 SectorsPerTrack; + u16 BytesPerSector; + u16 Interleave; + u16 TrackSkew; + u16 CylinderSkew; + u8 flags; + u8 reserved[3]; +} MODE_PAGE3; +/* + * Sense Data Format - Page 4 + */ +typedef struct { + u8 PageCode; + u8 PageLength; + u16 CylindersHigh; + u8 CylindersLow; + u8 Heads; + u16 WritePrecompHigh; + u8 WritePrecompLow; + u16 ReducedWriteCurrentHigh; + u8 ReducedWriteCurrentLow; + u16 StepRate; + u16 LandingZoneHigh; + u8 LandingZoneLow; + u8 flags; + u8 RotationalOffset; + u8 Reserved; + u16 MediumRotationRate; + u8 Reserved2[2]; +} MODE_PAGE4; + +typedef struct { + u8 PageCode; + u8 PageLength; + u8 Flags; + u8 RetensionPriority; + u8 DisablePrefetchTransfer[2]; + u8 MinimumPrefetch[2]; + u8 MaximumPrefetch[2]; + u8 MaximumPrefetchCeiling[2]; + u8 temp[7]; +} MODE_PAGE8; + +typedef struct { + byte PageCode:6; + byte Reserved:1; + byte PageSavable:1; + byte PageLength; + + byte ReadDisableCache:1; + byte MultiplicationFactor:1; + byte WriteCacheEnable:1; + byte Reserved2:5; + byte WriteRetensionPriority:4; + byte ReadRetensionPriority:4; + byte DisablePrefetchTransfer[2]; + byte MinimumPrefetch[2]; + byte MaximumPrefetch[2]; + byte MaximumPrefetchCeiling[2]; + byte FSW_LBCSS_RDA; + byte NumberofCacheSegments; + byte CacheSegmentSize[2]; + byte Reserved16; + byte NonCacheSegmentSize[3]; +} MODE_PAGE_8; + +typedef struct { + byte PageCode:6; + byte Reserved:1; + byte PageSavable:1; + byte PageLength; + + byte Reserved2; + byte ProtocolIdentifier; + byte SynchronousTransferTimeout[2]; + byte Reserved6[2]; +} MODEPAGE19; + +typedef struct { + MODE_PAGE_HEADER hdr; + union { + MODEPAGE19 pg_9; + MODE_PAGE_8 pg_8; + MODE_PAGE8 pg8; + MODE_PAGE3 pg3; + MODE_PAGE4 pg4; + } pdata; +} MODE_PAGE_DATA; + +/* Sense codes */ +#define SCSI_SENSE_NO_SENSE 0x00 +#define SCSI_SENSE_RECOVERED_ERROR 0x01 +#define SCSI_SENSE_NOT_READY 0x02 +#define SCSI_SENSE_MEDIUM_ERROR 0x03 +#define SCSI_SENSE_HARDWARE_ERROR 0x04 +#define SCSI_SENSE_ILLEGAL_REQUEST 0x05 +#define SCSI_SENSE_UNIT_ATTENTION 0x06 +#define SCSI_SENSE_DATA_PROTECT 0x07 +#define SCSI_SENSE_BLANK_CHECK 0x08 +#define SCSI_SENSE_UNIQUE 0x09 +#define SCSI_SENSE_COPY_ABORTED 0x0A +#define SCSI_SENSE_ABORTED_COMMAND 0x0B +#define SCSI_SENSE_EQUAL 0x0C +#define SCSI_SENSE_VOL_OVERFLOW 0x0D +#define SCSI_SENSE_MISCOMPARE 0x0E +#define SCSI_SENSE_RESERVED 0x0F + +/* Additional Sense codes */ +#define SCSI_ADSENSE_NO_SENSE 0x00 +#define SCSI_ADSENSE_NO_SEEK_COMPLETE 0x02 +#define SCSI_ADSENSE_LUN_NOT_READY 0x04 +#define SCSI_ADSENSE_LUN_COMMUNICATION 0x08 +#define SCSI_ADSENSE_WRITE_ERROR 0x0C +#define SCSI_ADSENSE_TRACK_ERROR 0x14 +#define SCSI_ADSENSE_SEEK_ERROR 0x15 +#define SCSI_ADSENSE_REC_DATA_NOECC 0x17 +#define SCSI_ADSENSE_REC_DATA_ECC 0x18 +#define SCSI_ADSENSE_PARAMETER_LIST_LENGTH 0x1A +#define SCSI_ADSENSE_ILLEGAL_COMMAND 0x20 +#define SCSI_ADSENSE_ILLEGAL_BLOCK 0x21 +#define SCSI_ADSENSE_INVALID_CDB 0x24 +#define SCSI_ADSENSE_INVALID_LUN 0x25 +#define SCSI_ADSENSE_INVALID_FIELD_PARAMETER_LIST 0x26 +#define SCSI_ADSENSE_WRITE_PROTECT 0x27 +#define SCSI_ADSENSE_MEDIUM_CHANGED 0x28 +#define SCSI_ADSENSE_BUS_RESET 0x29 +#define SCSI_ADSENSE_PARAMETERS_CHANGED 0x2A +#define SCSI_ADSENSE_INSUFFICIENT_TIME_FOR_OPERATION 0x2E +#define SCSI_ADSENSE_INVALID_MEDIA 0x30 +#define SCSI_ADSENSE_NO_MEDIA_IN_DEVICE 0x3a +#define SCSI_ADSENSE_POSITION_ERROR 0x3b +#define SCSI_ADSENSE_OPERATING_CONDITIONS_CHANGED 0x3f +#define SCSI_ADSENSE_OPERATOR_REQUEST 0x5a +#define SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED 0x5d +#define SCSI_ADSENSE_ILLEGAL_MODE_FOR_THIS_TRACK 0x64 +#define SCSI_ADSENSE_COPY_PROTECTION_FAILURE 0x6f +#define SCSI_ADSENSE_POWER_CALIBRATION_ERROR 0x73 +#define SCSI_ADSENSE_VENDOR_UNIQUE 0x80 +#define SCSI_ADSENSE_MUSIC_AREA 0xA0 +#define SCSI_ADSENSE_DATA_AREA 0xA1 +#define SCSI_ADSENSE_VOLUME_OVERFLOW 0xA7 + +/* for legacy apps : */ + +extern struct kmem_cache *bht_srb_ext_cachep; +extern mempool_t *bht_sd_mem_pool; + +#endif From patchwork Fri Oct 13 08:34:29 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: liuchang_125125@163.com X-Patchwork-Id: 13420486 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id EDBF5CDB47E for ; Fri, 13 Oct 2023 08:50:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230160AbjJMIuP (ORCPT ); Fri, 13 Oct 2023 04:50:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53636 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230229AbjJMIuO (ORCPT ); Fri, 13 Oct 2023 04:50:14 -0400 Received: from m15.mail.163.com (m15.mail.163.com [45.254.50.219]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 51828C0; Fri, 13 Oct 2023 01:50:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:Subject:Date:Message-Id:MIME-Version; bh=wK9wv WGebYMLO3g5Eil7zWDyODGO8DUazwRlnevaIBs=; b=Ss0FcIDLhj1OZ3X3w7vSw gau9iUnwJT+lmBQfym4vsFqNHJbEXFV4N70j6o6FZ7PlYW0HiW5vvL4oB5P3lIZU R9njBSKHN7rrGEWiPa+Ffl9DZS9SVoTNjBZ/no7Roz6nzEhEbhPfc2Yblpx9OeWx enMLbZ5rCNYVr0WJ20njaI= Received: from test-Z390-GAMING-X.bayhubtech.com (unknown [58.48.115.170]) by zwqz-smtp-mta-g0-4 (Coremail) with SMTP id _____wD3v0IcASllVHG5AQ--.10617S2; Fri, 13 Oct 2023 16:34:37 +0800 (CST) From: liuchang_125125@163.com To: jejb@linux.ibm.com, martin.petersen@oracle.com, linux-scsi@vger.kernel.org, linux-kernel@vger.kernel.org Cc: mark.tao@bayhubtech.com, shaper.liu@bayhubtech.com, thomas.hu@bayhubtech.com, chevron.li@bayhubtech.com, charl.liu@bayhubtech.com, Charl Liu Subject: [PATCH 7/9] scsi: bht: main: Add the source files related to driver setting management Date: Fri, 13 Oct 2023 16:34:29 +0800 Message-Id: <20231013083429.10347-1-liuchang_125125@163.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 X-CM-TRANSID: _____wD3v0IcASllVHG5AQ--.10617S2 X-Coremail-Antispam: 1Uf129KBjvAXoWkXF17GF48KF4kJw13AF1UKFg_yoWDurWDWo WxZF43C34UJry8Ww1vkr1jyry7X3s7CF1Syr48CrsYga1xAF4Ygr9Fyw43Ga4fJF4ftrW5 X3Z3uFySqa98tr18n29KB7ZKAUJUUUUU529EdanIXcx71UUUUU7v73VFW2AGmfu7bjvjm3 AaLaJ3UbIYCTnIWIevJa73UjIFyTuYvj4RiL05DUUUU X-Originating-IP: [58.48.115.170] X-CM-SenderInfo: polxux5dqjsiqsvrjki6rwjhhfrp/xtbBnx8IWVetlJKurAAAsv Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org From: Charl Liu 1.autotimerfunc: implemnt auto timer function 2.cfgmng: implement dynamic configuration function 3.geniofunc: implement IO control request 4.pmfunc: implement power management function 5.reqmng: implement request management function 6.testcase: implement test case function 7.thread: handle thread event Signed-off-by: Charl Liu --- Change in V1: Add the source files related to timer management, configuration management, IO management, power management, request management, test case management and thread management. --- drivers/scsi/bht/main/autotimerfunc.c | 335 ++++++++ drivers/scsi/bht/main/cfgmng.c | 1132 +++++++++++++++++++++++++ drivers/scsi/bht/main/funcapi.h | 36 + drivers/scsi/bht/main/geniofunc.c | 618 ++++++++++++++ drivers/scsi/bht/main/pmfunc.c | 357 ++++++++ drivers/scsi/bht/main/reqmng.c | 546 ++++++++++++ drivers/scsi/bht/main/testcase.c | 213 +++++ drivers/scsi/bht/main/thread.c | 530 ++++++++++++ 8 files changed, 3767 insertions(+) create mode 100644 drivers/scsi/bht/main/autotimerfunc.c create mode 100644 drivers/scsi/bht/main/cfgmng.c create mode 100644 drivers/scsi/bht/main/funcapi.h create mode 100644 drivers/scsi/bht/main/geniofunc.c create mode 100644 drivers/scsi/bht/main/pmfunc.c create mode 100644 drivers/scsi/bht/main/reqmng.c create mode 100644 drivers/scsi/bht/main/testcase.c create mode 100644 drivers/scsi/bht/main/thread.c diff --git a/drivers/scsi/bht/main/autotimerfunc.c b/drivers/scsi/bht/main/autotimerfunc.c new file mode 100644 index 000000000000..8bf70f0a1fb7 --- /dev/null +++ b/drivers/scsi/bht/main/autotimerfunc.c @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: autotimerfunc.c + * + * Abstract: This source file used to implemnt auto timer functions + * + * Version: 1.00 + * + * Author: Peter.Guo + * + * Environment: OS Independent + * + * History: + * + * 8/25/2014 Creation Peter.Guo + */ + +#include "../include/basic.h" +#include "../include/cardapi.h" +#include "../include/host.h" +#include "../include/hostapi.h" +#include "../include/debug.h" +#include "../include/util.h" + +/* + * Function Name: autotimer_clear + * Abstract: This Function is used to clear timer tick + * + * Input: + * bht_dev_ext_t *pdx + * + */ +static void autotimer_clear(bht_dev_ext_t *pdx) +{ + pdx->auto_timer.auto_dmt_tick = 0; + pdx->auto_timer.auto_poweroff_tick = 0; + pdx->auto_timer.auto_cmd12_tick = 0; + pdx->auto_timer.last_tick = 0; + pdx->auto_timer.auto_led_off_tick = 0; +} + +/* + * Function Name: func_timer_callback + * Abstract: This Function is used to calculate timer tick + * + * Input: + * bht_dev_ext_t *pdx + * + * + * Notes: + * + * so giving the routine another name requires you to modify the build tools. + */ + +void func_timer_callback(bht_dev_ext_t *pdx) +{ + u32 cur_time, interval; + bool event = FALSE; + + DbgInfo(MODULE_AUTOTIMER, FEATURE_TIMER_TRACE, 0, "Enter %s\n", + __func__); + if (pdx->auto_timer.enable == FALSE || pdx->auto_timer.cancel) + goto exit; + + if (pdx->auto_timer.stop) { + autotimer_clear(pdx); + goto exit; + } + + /* If card not workable don't use timer */ + if (pdx->card.card_present == FALSE) + goto exit; + if (pdx->card.card_type == CARD_NONE + || pdx->card.card_type == CARD_ERROR) + goto exit; + if (pdx->card.state == CARD_STATE_POWEROFF) + goto exit; + /* If rtd3 entered not do below steps */ + DbgInfo(MODULE_AUTOTIMER, FEATURE_TIMER_TRACE, 0, + "auto timer really work\n"); + + /* calculate the real interval */ + cur_time = os_get_cur_tick(); + if (pdx->auto_timer.last_tick == 0) { + pdx->auto_timer.last_tick = cur_time; + interval = AUTO_TIMER_TICK; + } else { + interval = cur_time - pdx->auto_timer.last_tick; + pdx->auto_timer.last_tick = cur_time; + } + + /* update and check timeout for each tick */ + if (pdx->auto_timer.auto_cmd12_enable && pdx->card.has_built_inf) { + pdx->auto_timer.auto_cmd12_tick += interval; + if (pdx->auto_timer.auto_cmd12_tick >= + pdx->auto_timer.auto_cmd12_time) + event = TRUE; + } + + if (pdx->auto_timer.auto_dmt_enable && pdx->card.card_type == CARD_UHS2 + && pdx->card.state == CARD_STATE_WORKING) { + pdx->auto_timer.auto_dmt_tick += interval; + if (pdx->auto_timer.auto_dmt_tick >= + pdx->auto_timer.auto_dmt_time) + event = TRUE; + } + + if (pdx->auto_timer.auto_led_off_enable && (pdx->host.led_on)) { + pdx->auto_timer.auto_led_off_tick += interval; + if (pdx->auto_timer.auto_led_off_tick >= + pdx->auto_timer.auto_led_off_time) + event = TRUE; + } + + if (pdx->auto_timer.auto_poweroff_enable) { + pdx->auto_timer.auto_poweroff_tick += interval; + if (pdx->auto_timer.auto_poweroff_tick >= + pdx->auto_timer.auto_poweroff_time) + event = TRUE; + } + + if (pdx->auto_timer.cancel || pdx->auto_timer.stop) + goto exit; + + if (event) { + DbgInfo(MODULE_AUTOTIMER, FEATURE_TIMER_TRACE, 0, + "auto timer set event\n"); + +#if CFG_OS_LINUX + os_set_event(&pdx->os, EVENT_AUTO_TIMER); +#else + os_set_event(pdx, &pdx->os, EVENT_TASK_OCCUR, EVENT_AUTO_TIMER); +#endif + + } + os_start_timer(pdx, &pdx->os, TIMER_AUTO, AUTO_TIMER_TICK); + +exit: + DbgInfo(MODULE_AUTOTIMER, FEATURE_TIMER_TRACE, 0, "Exit %s\n", + __func__); +} + +/* + * Function Name: func_autotimer_init + * Abstract: This Function is used to init timer function variables + * + * Input: + * bht_dev_ext_t *pdx + * + * + * Notes: + * + * so giving the routine another name requires you to modify the build tools. + */ + +void func_autotimer_init(bht_dev_ext_t *pdx) +{ + cfg_item_t *cfg = pdx->cfg; + + os_memset(&pdx->auto_timer, 0, sizeof(pdx->auto_timer)); + + pdx->auto_timer.auto_dmt_time = + cfg->timer_item.auto_dormant_timer.time_ms; + pdx->auto_timer.auto_dmt_enable = + (bool)cfg->timer_item.auto_dormant_timer.enable_dmt_func; + pdx->auto_timer.enable_hibernate = + (bool)cfg->timer_item.auto_dormant_timer.enable_hbr; + + pdx->auto_timer.auto_poweroff_enable = FALSE; + pdx->auto_timer.auto_poweroff_time = 10 * 1000; + + pdx->auto_timer.auto_led_off_enable = pdx->host.feature.hw_led_fix; + /* led off set to 1s */ + pdx->auto_timer.auto_led_off_time = 1000; + + pdx->auto_timer.enable = pdx->auto_timer.auto_cmd12_enable | + pdx->auto_timer.auto_poweroff_enable | + pdx->auto_timer.auto_dmt_enable | + pdx->auto_timer.auto_led_off_enable; + + DbgInfo(MODULE_AUTOTIMER, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Autopower off enable=%d time=%dms\n", + pdx->auto_timer.auto_poweroff_enable, + pdx->auto_timer.auto_poweroff_time); + DbgInfo(MODULE_AUTOTIMER, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Autodmt=%d time=%dms bhrb=%d\n", + pdx->auto_timer.auto_dmt_enable, pdx->auto_timer.auto_dmt_time, + pdx->auto_timer.enable_hibernate); + DbgInfo(MODULE_AUTOTIMER, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "AutoStopInf enable=%d time=%dms\n", + pdx->auto_timer.auto_cmd12_enable, + pdx->auto_timer.auto_cmd12_time); +} + +/* + * Function Name: func_timer_thread + * Abstract: This Function is used to calculate timer tick + * + * Input: + * bht_dev_ext_t *pdx + * + * + * Notes: + * + * This function is called by thread to do real job + */ +void func_timer_thread(bht_dev_ext_t *pdx) +{ + int busy = 0; + + DbgInfo(MODULE_AUTOTIMER, FEATURE_TIMER_TRACE, 0, "Enter %s\n", + __func__); + + if (pdx->auto_timer.enable == FALSE || pdx->auto_timer.cancel + || pdx->auto_timer.stop) + goto clear; + + /* If card not workable don't use timer */ + if (pdx->card.card_present == FALSE) + goto clear; + if (pdx->card.card_type == CARD_NONE + || pdx->card.card_type == CARD_ERROR) + goto clear; + if (pdx->card.state == CARD_STATE_POWEROFF) + goto clear; + + /* If rtd3 entered not call below function */ + + if (pdx->auto_timer.auto_poweroff_enable) { + if (pdx->auto_timer.auto_poweroff_tick >= + pdx->auto_timer.auto_poweroff_time) { + DbgInfo(MODULE_AUTOTIMER, FEATURE_CARD_OPS, 0, + "auto poweroff\n"); + if (busy == 0) { + os_set_dev_busy(pdx); + busy = 1; + } + card_power_off(&pdx->card, FALSE); + autotimer_clear(pdx); + goto next; + } + } + + if (pdx->auto_timer.auto_cmd12_enable && pdx->card.has_built_inf) { + if (pdx->auto_timer.auto_cmd12_tick >= + pdx->auto_timer.auto_cmd12_time) { + if (busy == 0) { + os_set_dev_busy(pdx); + busy = 1; + } + pdx->auto_timer.auto_cmd12_tick = 0; + DbgInfo(MODULE_AUTOTIMER, FEATURE_CARD_OPS, 0, + "auto stop infinite\n"); + card_stop_infinite(&pdx->card, TRUE, NULL); + } + } + + if (pdx->auto_timer.auto_dmt_enable && pdx->card.card_type == CARD_UHS2 + && pdx->card.state == CARD_STATE_WORKING) { + if (pdx->auto_timer.auto_dmt_tick >= + pdx->auto_timer.auto_dmt_time) { + if (busy == 0) { + os_set_dev_busy(pdx); + busy = 1; + } + pdx->auto_timer.auto_dmt_tick = 0; + DbgInfo(MODULE_AUTOTIMER, FEATURE_CARD_OPS, 0, + "auto enter sleep\n"); + card_enter_sleep(&pdx->card, TRUE, + pdx->auto_timer.enable_hibernate); + } + } + + if (pdx->auto_timer.auto_led_off_enable && (pdx->host.led_on)) { + if (pdx->auto_timer.auto_led_off_tick >= + pdx->auto_timer.auto_led_off_time) { + if (busy == 0) { + os_set_dev_busy(pdx); + busy = 1; + } + DbgInfo(MODULE_AUTOTIMER, FEATURE_CARD_OPS, 0, + "auto led off\n"); + pdx->auto_timer.auto_led_off_tick = 0; + host_led_ctl(&pdx->host, FALSE); + } + } + +next: + if (busy) + os_set_dev_idle(pdx); + goto exit; + +clear: + autotimer_clear(pdx); + +exit: + DbgInfo(MODULE_AUTOTIMER, FEATURE_TIMER_TRACE, 0, "Exit %s\n", + __func__); +} + +void func_autotimer_stop(bht_dev_ext_t *pdx) +{ + if (pdx->auto_timer.enable == FALSE) + return; + DbgInfo(MODULE_AUTOTIMER, FEATURE_TIMER_TRACE, 0, "stop autotimer\n", + __func__); + pdx->auto_timer.stop = 1; + os_stop_timer(pdx, &pdx->os, TIMER_AUTO); + autotimer_clear(pdx); + +} + +void func_autotimer_start(bht_dev_ext_t *pdx) +{ + if (pdx->auto_timer.enable == FALSE || pdx->auto_timer.cancel) + return; + DbgInfo(MODULE_AUTOTIMER, FEATURE_TIMER_TRACE, 0, "start autotimer\n", + __func__); + autotimer_clear(pdx); + pdx->auto_timer.stop = 0; + os_start_timer(pdx, &pdx->os, TIMER_AUTO, AUTO_TIMER_TICK); +} + +void func_autotimer_cancel(bht_dev_ext_t *pdx) +{ + if (pdx->auto_timer.enable == FALSE || pdx->auto_timer.cancel) + return; + DbgInfo(MODULE_AUTOTIMER, FEATURE_TIMER_TRACE, 0, "cancel autotimer\n", + __func__); + pdx->auto_timer.cancel = 1; + os_cancel_timer(pdx, &pdx->os, TIMER_AUTO); + autotimer_clear(pdx); +} diff --git a/drivers/scsi/bht/main/cfgmng.c b/drivers/scsi/bht/main/cfgmng.c new file mode 100644 index 000000000000..de0f112102f4 --- /dev/null +++ b/drivers/scsi/bht/main/cfgmng.c @@ -0,0 +1,1132 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: cfgmng.c + * + * Abstract: This source file used to mangage dynamic configuration + * + * Version: 1.00 + * + * Author: Peter.Guo + * + * Environment: OS Independent + * + * History: + * + * 8/25/2014 Creation Peter.Guo + */ + +#include "../include/basic.h" +#include "../include/debug.h" +#include "../include/util.h" + +#define TRUE 1 +#define FALSE 0 + +#define DMDN_TYPE_CNT 4 + +/* + * 0 1 2 3 + * UHS2 UHS2M1 UHS2M2 UHS2M3 + * + * 4 5 6 7 + * SD200 SD200M1 SD200M2 SD200M3 + * + * 8 9 10 11 + * SD100M SD100M1 SD100M2 SD100M3 + * + * 12 13 14 15 16 + * SD75M DDR50 50MZ 25MZ 400K + * + * 17 + * DDR50InputTuning + * + */ + +/* global definition about configuration structure */ +cfg_item_t g_cfg[SUPPORT_CHIP_COUNT][2]; + +u32 g_dmdn_divider_tbl[DMDN_TYPE_CNT][MAX_FREQ_SUPP] = { + /* SDS, Fujin2 */ + { + /* UHSII */ + 0x1f340002, 0x18230002, 0x181f0002, 0x181b0002, + /* SDR104=208M */ + 0x1f340000, 0x18230000, 0x181f0000, 0x181b0000, + /* 100M */ + 0x18270001, 0x18230001, 0x181F0001, 0x181B0001, + 0x181d0001, 0x18270002, 0x18270002, 0x18270004, 0x182700FA, + 0xFFFF0001, + /* 200M ~ 140M */ + 0x18270000, 0x18230000, 0x181f0000, 0x181b0000, + /* 50M 25M 400K */ + 0x18270002, 0x18270002, 0x18270004, 0x182700fa }, + + /* Seabird, SeaEagle */ + { + /* UHSII */ + 0x2c280002, 0x27140002, 0x2B1C0002, 0x2C1A0002, + /* SDR104=200M */ + 0x2c280000, 0x27140000, 0x2B1C0000, 0x2c1A0000, + /* 100M */ + 0x25100001, 0x27140001, 0x2B1C0001, 0x2C1A0001, + 0x250C0001, 0x25100002, 0x25100002, 0x25100004, 0x251000FA, + 0x35100001, + /* 200M ~ 140M */ + 0x25100000, 0x27140000, 0x2b1c0000, 0x2c1a0000, + /* 50M 25M 400K */ + 0x25100002, 0x25100002, 0x25100004, 0x251000fa }, + + /* SeaEagle2 */ + { + /* UHSII */ + 0x2c280002, 0x27140002, 0x2B1C0002, 0x2c1a0002, + /* SDR104=208M */ + 0x2c280000, 0x27140000, 0x2B1C0000, 0x2c1a0000, + /* 100M */ + 0x25100001, 0x27140001, 0x2b1c0001, 0x2c1a0001, + 0x250c0001, 0x25100002, 0x25100002, 0x25100004, 0x251000fa, + 0x35100001, + /* 200M ~ 140M */ + 0x25100000, 0x27140000, 0x2b1c0000, 0x2c1a0000, + /* 50M 25M 400K */ + 0x25100002, 0x25100002, 0x25100004, 0x251000fa }, + /* GG8 */ + { + /* UHSII */ + 0x2c500002, 0x251D0002, 0x251A0002, 0x25160002, + /* SDR104=208M */ + 0x2c500000, 0x251D0000, 0x251A0000, 0x25160000, + /* 100M */ + 0x25200001, 0x251D0001, 0x251A0001, 0x25160001, + 0x25160001, 0x25200002, 0x25200002, 0x25200004, 0x252000fa, + 0x25200002, + /* 200M ~ 140M */ + 0x25200000, 0x251D0000, 0x251A0001, 0x25160000, + /* 50M 25M 400K */ + 0x25200002, 0x25200002, 0x25200004, 0x252000fa, + /* DDR200 */ + 0x25200000, 0x251D0000, 0x251A0000, 0x25160000, + /* DDR225, 225MHz, 200MHz */ + 0x25240000, 0x25200000, 0x251D0000, 0x251A0000 } +}; + +#if (0) +/* + * UHS2 UHS2M1 UHS2M2 UHS2M3 + * SD200 SD200M1 SD200M2 SD200M3 + * SD100M SD100M1 SD100M2 SD100M3 + * SD75M DDR50 50MZ 25MZ 400K + */ +u32 g_dmdn_divider_tbl_fpga[MAX_FREQ_SUPP] = { + /* 208/4 187.5/4 166.7/4 145.8/4 UHSII */ + 0x05030002, 0x06040002, 0x08060002, 0x07060002, + /* 200/2 180/2 160.7/2 140.6/2 SDR104 */ + 0x08050001, 0x0A070001, 0x09070001, 0x09080001, + /* 100 90 80.4 70.3 100M */ + 0x08050001, 0x0A070001, 0x09070001, 0x09080001, + /* 75 50 50 25 400K */ + 0x06050001, 0x08050002, 0x08050002, 0x08050004, 0x080500FA +}; +#endif + +/* + * UHS2 UHS2M1 UHS2M2 UHS2M3 + * SD200 SD200M1 SD200M2 SD200M3 + * SD100M SD100M1 SD100M2 SD100M3 + * SD75M DDR50 50MZ 25MZ 400K + */ +u32 g_dmdn_divider_tbl_fpga[MAX_FREQ_SUPP] = { + /* 208/4 187.5/4 166.7/4 145.8/4 UHSII */ + 0x05030002, 0x05030002, 0x05030002, 0x05030002, + /* 200/2 180/2 160.7/2 140.6/2 SDR104 */ + 0x08050001, 0x08050002, 0x08050003, 0x08050001, + /* 100 90 80.4 70.3 100M */ + 0x08050001, 0x08050001, 0x08050001, 0x08050001, + /* 75 50 50 25 400K */ + 0x08050002, 0x08050002, 0x08050002, 0x08050004, 0x080500FA, + /* DDR50 Inputtuning */ + 0x080a0001, + /* use for eMMC */ + 0x08050001, 0x08050001, 0x08050001, 0x08050001, 0x08050002, 0x08050002, + 0x08050004, 0x080500FA, + /* 200/2 180/2 160.7/2 140.6/2 DDR200 */ + 0x08050001, 0x08050001, 0x08050001, 0x08050001 +}; + +static void cfg_parse(cfg_item_t *cfg, e_chip_type chip_type); +static void cfg_set_default_val(cfg_item_t *cfg, e_chip_type type); + +void cfgmng_init_chipcfg(e_chip_type chip_type, cfg_item_t *cfg, bool reinit) +{ + cfg_driver_item_t driver_item; + cfg_psd_mode_t psd_mode; + u32 bit64 = 0; + + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, NOT_TO_RAM, + "========================================\n"); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, NOT_TO_RAM, + "ChipType=%d, boot_flag = %d\n", chip_type, cfg->boot_flag); + + if (reinit) { + driver_item = cfg->driver_item; + psd_mode = cfg->feature_item.psd_mode; + bit64 = + cfg->host_item.test_dma_mode_setting.enable_dma_64bit_address; + } + cfg_set_default_val(cfg, chip_type); + os_cfg_load(cfg, chip_type); + cfg_parse(cfg, chip_type); + if (reinit) { + cfg->driver_item = driver_item; + cfg->feature_item.psd_mode = psd_mode; + cfg->host_item.test_dma_mode_setting.enable_dma_64bit_address = + bit64 ? 1 : 0; + } +} + +/* + * + * Function Name: cfgmng_init + * + * Abstract: + * 1. Read different chip type registry information + * 2. parse reigstry information + * + * Input: + * None + * + * Output: + * None + * + * Return value: + * None + * + * Notes: + * Caller: DriverEntry + */ +void cfgmng_init(void) +{ + u8 i = 0; + e_chip_type chip_type; + + for (i = 0; i < SUPPORT_CHIP_COUNT; i++) { + /* for non-boot cfg */ + + cfg_item_t *cfg = &g_cfg[i][0]; + + chip_type = (e_chip_type) i; + cfg->boot_flag = FALSE; + cfgmng_init_chipcfg(chip_type, cfg, FALSE); + + /* for boot config */ + cfg = &g_cfg[i][1]; + chip_type = (e_chip_type) i; + cfg->boot_flag = TRUE; + + cfgmng_init_chipcfg(chip_type, cfg, FALSE); + + } +} + +/* + * + * Function Name: cfgmng_get + * + * Abstract: + * 1. transfer the (cfg_item_t *) structure + * + * Input: + * e_chip_type chip_type; + * + * Output: + * pointer to cfg_item_t + * + * Return value: + * None + * + * Notes: + * Caller: + */ +cfg_item_t *cfgmng_get(void *pdx, e_chip_type chip_type, bool boot) +{ + int i = boot ? 1 : 0; + cfg_item_t *cfg = &g_cfg[(u32) chip_type][i]; + u32 *tbl = NULL; + + switch ((u32) chip_type) { + case CHIP_SDS0: + case CHIP_SDS1: + case CHIP_FUJIN2: + tbl = &g_dmdn_divider_tbl[0][0]; + break; + + case CHIP_SEABIRD: + case CHIP_SEAEAGLE: + tbl = &g_dmdn_divider_tbl[1][0]; + break; + case CHIP_SEAEAGLE2: + tbl = &g_dmdn_divider_tbl[2][0]; + break; + case CHIP_GG8: + case CHIP_ALBATROSS: + tbl = &g_dmdn_divider_tbl[3][0]; + break; + } + + if (cfg->fpga_item.fpga_ctrl.is_fpga_chip) { + tbl = g_dmdn_divider_tbl_fpga; + DbgInfo(MODULE_CFG_MNG, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Use Dmdn table For FPGA\n"); + } + + if (tbl != NULL) + os_memcpy(cfg->dmdn_tbl, tbl, sizeof(g_dmdn_divider_tbl_fpga)); + + DbgInfo(MODULE_CFG_MNG, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "chip type =%d DMDN basetbl = %p realtbl=%p\n", chip_type, + &g_dmdn_divider_tbl[0][0], cfg->dmdn_tbl); + cfg_print_debug(cfg); + cfg->pcr_item.cnt = 0; + + /* clear pcr cfg */ + os_memset(&cfg->pcr_item, 0, sizeof(cfg_pcr_item_t)); + + os_enum_reg_cfg(cfg, chip_type, (byte *) "\\pcr", os_load_pcr_cb); + os_enum_reg_cfg(cfg, chip_type, (byte *) "\\dmdn", os_load_dmdn_cb); + + return cfg; +} + +/* + * + * Function Name: print_registry_value + * + * Abstract: + * 1. print the registry name and it's value + * + * Input: + * PVOID cfg_item: Pointer to the registry configuration structure + * + * Output: + * None + * + * Return value: + * None + * + * Notes: + * Caller: registry_load + */ +void cfg_print_debug(PVOID cfg_item) +{ +#if DBG || _DEBUG + cfg_item_t *cfg = (cfg_item_t *) cfg_item; + + cfg_card_item_t card_item = cfg->card_item; + cfg_host_item_t host_item = cfg->host_item; + + cfg_feature_item_t feature_item = cfg->feature_item; + cfg_timer_item_t timer_item = cfg->timer_item; + cfg_timeout_item_t timeout_item = cfg->timeout_item; + + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "-------------Registry item Check Start--------------------\n"); + + /* -------------------- card item name as below------------------ */ + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "sd_card_mode_dis: 0x%08x\n", + card_item.sd_card_mode_dis); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "test_max_access_mode: 0x%08x\n", + card_item.test_max_access_mode); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "test_driver_strength_sel: 0x%08x\n", + card_item.test_driver_strength_sel); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "test_max_power_limit: 0x%08x\n", + card_item.test_max_power_limit); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "mmc_mode_dis: 0x%08x\n", + card_item.mmc_mode_dis); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "emmc_mode: 0x%08x\n", + card_item.emmc_mode); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "sd7_sdmode_switch_control: 0x%08x\n", + card_item.sd7_sdmode_switch_control); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "test_uhs2_setting: 0x%08x\n", + card_item.uhs2_setting); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "test_uhs2_setting2: 0x%08x\n", + card_item.test_uhs2_setting2); + + /* -------------------- host item name as below ------------------ */ + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "test_dma_mode_setting: 0x%08x\n", + host_item.test_dma_mode_setting); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "test_infinite_transfer_mode: 0x%08x\n", + host_item.test_infinite_transfer_mode); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "test_sdma_boun_setting: 0x%08x\n", + host_item.test_sdma_boundary); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "test_tag_queue_capability: 0x%08x\n", + host_item.test_tag_queue_capability); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "test_ocb_ctrl: 0x%08x\n", + host_item.test_ocb_ctrl); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "bios_l1_substate: 0x%08x\n", + host_item.bios_l1_substate); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "vdd_power_source_item: 0x%08x\n", + host_item.vdd_power_source_item); + + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "host_drive_strength: 0x%08x\n", + host_item.host_drive_strength); + + /* -------------------- issue fix item name as below------------------ */ + + /* -------------------- feature item name as below-------------------- */ + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "psd_mode: 0x%08x\n", + feature_item.psd_mode); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "pcie_wake_setting: 0x%08x\n", + feature_item.pcie_wake_setting); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "test_main_ldo_setting: 0x%08x\n", + feature_item.test_main_ldo_setting); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "output_tuning_item: 0x%08x\n", + feature_item.output_tuning_item); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "hsmux_vcme_enable: 0x%08x\n", + feature_item.hsmux_vcme_enable); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "refclk_stable_detection_counter1: 0x%08x\n", + feature_item.refclk_stable_detection_counter1); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "refclk_stable_detection_counter2: 0x%08x\n", + feature_item.refclk_stable_detection_counter2); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "refclk_stable_detection_counter3: 0x%08x\n", + feature_item.refclk_stable_detection_counter3); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "auto_detect_refclk_counter_range_ctl:0x%08x\n", + feature_item.auto_detect_refclk_counter_range_ctl); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "l1_enter_exit_logic_ctl: 0x%08x\n", + feature_item.l1_enter_exit_logic_ctl); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "pcie_phy_amplitude_adjust: 0x%08x\n", + feature_item.pcie_phy_amplitude_adjust); + + /* -------------------- timer item name as below------------------ */ + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "auto_sleep_control: 0x%08x\n", + timer_item.auto_sleep_control); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "auto_dormant_timer: 0x%08x\n", + timer_item.auto_dormant_timer); + + /* -------------------- timeout item name as below------------------ */ + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "power_wait_time: 0x%08x\n", + timeout_item.power_wait_time); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "test_write_data_timeout: 0x%08x\n", + timeout_item.test_write_data_timeout); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "test_read_data_timeout: 0x%08x\n", + timeout_item.test_read_data_timeout); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "test_non_data_timeout: 0x%08x\n", + timeout_item.test_non_data_timeout); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "test_r1b_data_timeout: 0x%08x\n", + timeout_item.test_r1b_data_timeout); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "test_card_init_timeout: 0x%08x\n", + timeout_item.test_card_init_timeout); + + /* -------------------- fpga item name as below------------------ */ + + /* -------------------- driver item name as below------------------ */ + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "driver item: 0x%08x\n", + cfg->driver_item); + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, 0, + "-------------Registry item Check End--------------------\n"); +#endif +} + +/* + * + * Function Name: fill_registry_struct + * + * Abstract: + * 1. call memcpy function to fill the registry structure + * + * Input: + * PVOID cfg_item: Pointer to single registry key address + * u32 cfg_def_val: the value of the single registry key structure + * u32 data_len: memcpy data length + * Output: + * None + * + * Return value: + * None + * + * Notes: + * Caller: cfg_set_default_val + */ +static void fill_registry_struct(PVOID cfg_item, u32 cfg_def_val, u32 data_len) +{ + u32 fill_registry_val = cfg_def_val; + + os_memcpy(cfg_item, &fill_registry_val, data_len); +} + +/* + * + * Function Name: cfg_set_default_val + * + * Abstract: + * 1. set the default value for every registry key + * + * Input: + * PVOID cfg_item: Pointer to the registry configuration structure + * e_chip_type type: + * + * Output: + * None + * + * Return value: + * None + * + * Notes: + * Caller: cfgmng_init + */ +static void cfg_set_default_val(cfg_item_t *cfg, e_chip_type type) +{ + u32 dma_mode, uhs2_setting, tag_queue_capability; + + if (type == CHIP_SEAEAGLE2 || type == CHIP_GG8 + || type == CHIP_ALBATROSS) { + dma_mode = 0x80000071; + tag_queue_capability = 0x80010010; + } else { +#if GLOBAL_ENABLE_BOOT + tag_queue_capability = 0x80010020; +#else + tag_queue_capability = 0x80010010; +#endif +#ifdef CFG_OS_LINUX + dma_mode = 0x80000071; +#else + dma_mode = 0x80000006; +#endif + } + + if (type == CHIP_SEAEAGLE) + uhs2_setting = 0x97080112; + else if (type == CHIP_SEAEAGLE2) + uhs2_setting = 0x97400012; + else if (type == CHIP_GG8 || type == CHIP_ALBATROSS) + uhs2_setting = 0x97400012; + else + uhs2_setting = 0x97100112; + + /* -------------------- card item name ------------------ * */ + fill_registry_struct(&(cfg->card_item.sd_card_mode_dis), 0, 4); + fill_registry_struct(&cfg->card_item.test_max_access_mode, 0x80000003, + 4); + fill_registry_struct(&cfg->card_item.test_driver_strength_sel, 0, 4); + fill_registry_struct(&cfg->card_item.test_max_power_limit, 0x80000003, + 4); + fill_registry_struct(&cfg->card_item.mmc_mode_dis, 0, 4); +#if GLOBAL_ENABLE_BOOT + fill_registry_struct(&cfg->card_item.emmc_mode, GLOBAL_EMMC_BOOT_CFG, + 4); +#else + fill_registry_struct(&cfg->card_item.emmc_mode, 0, 4); +#endif + + fill_registry_struct(&cfg->card_item.sd7_sdmode_switch_control, + 0x00000030, 4); + + fill_registry_struct(&cfg->card_item.uhs2_setting, uhs2_setting, 4); + + fill_registry_struct(&cfg->card_item.test_uhs2_setting2, 0x80000006, 4); + /* ------------------- card item name end --------------------- */ + + /* -------------------- host item name ------------------ * */ + fill_registry_struct(&cfg->host_item.test_dma_mode_setting, dma_mode, + 4); + if (type == CHIP_SDS0 || type == CHIP_SDS1) + fill_registry_struct(&cfg->host_item.test_infinite_transfer_mode, + 0x00000000, 4); + else + fill_registry_struct(&cfg->host_item.test_infinite_transfer_mode, + 0x8000000F, 4); + fill_registry_struct(&cfg->host_item.test_sdma_boundary, 0x20, 4); + fill_registry_struct(&cfg->host_item.test_tag_queue_capability, + tag_queue_capability, 4); + fill_registry_struct(&cfg->host_item.test_ocb_ctrl, 0x0, 4); + fill_registry_struct(&cfg->host_item.bios_l1_substate, 0x8000000f, 4); + + fill_registry_struct(&cfg->host_item.vdd_power_source_item, 0x00091B05, + 4); + fill_registry_struct(&cfg->host_item.host_drive_strength, 0x00000000, + 4); + /* -------------------- host item name end-------------------- */ + +#ifdef CFG_OS_LINUX + fill_registry_struct(&cfg->feature_item.psd_mode, 0x80000000, 4); +#else + fill_registry_struct(&cfg->feature_item.psd_mode, 0x0a0a, 4); +#endif + + fill_registry_struct(&cfg->feature_item.psd_mode, 0x0, 4); + + fill_registry_struct(&cfg->feature_item.test_main_ldo_setting, 0, 4); + + fill_registry_struct(&cfg->feature_item.output_tuning_item, 0xC01F17DC, + 4); + + /* -------------------- feature item name end------------------ */ + fill_registry_struct(&cfg->feature_item.hsmux_vcme_enable, 0x00000000, + 4); + + fill_registry_struct(&cfg->feature_item.refclk_stable_detection_counter1, + 0x00000003, 4); + fill_registry_struct(&cfg->feature_item.refclk_stable_detection_counter2, + 0x001e044c, 4); + fill_registry_struct(&cfg->feature_item.refclk_stable_detection_counter3, + 0x04b024b0, 4); + fill_registry_struct(&cfg->feature_item.auto_detect_refclk_counter_range_ctl, + 0x00000000, 4); + fill_registry_struct(&cfg->feature_item.l1_enter_exit_logic_ctl, + 0x00000000, 4); + fill_registry_struct(&cfg->feature_item.pcie_phy_amplitude_adjust, + 0x0000006a, 4); + + fill_registry_struct(&cfg->timer_item.auto_sleep_control, 0, 4); + fill_registry_struct(&cfg->timer_item.auto_dormant_timer, 0x80000025, + 4); + /* -------------------- timer item name end------------------ * 44 */ + + /* -------------------- timeout item name------------------ */ + fill_registry_struct(&cfg->timeout_item.power_wait_time, 0x0024000a, 4); + + fill_registry_struct(&cfg->timeout_item.test_write_data_timeout, + 0x80001770, 4); + fill_registry_struct(&cfg->timeout_item.test_read_data_timeout, + 0x80001770, 4); + fill_registry_struct(&cfg->timeout_item.test_non_data_timeout, + 0x800003e8, 4); + fill_registry_struct(&cfg->timeout_item.test_r1b_data_timeout, + 0x80001194, 4); + fill_registry_struct(&cfg->timeout_item.test_card_init_timeout, + 0x800005dc, 4); + /* -------------------- timeout item name end------------------ * 58 */ + + /* -------------------- fpga item name----------------------- */ + fill_registry_struct(&cfg->fpga_item.fpga_ctrl, 0x00000000, 4); + /* -------------------- fpga item name end ------------------ */ + + /* -------------------- driver item name------------------ * */ + fill_registry_struct(&(cfg->driver_item), 0x8080000a, 4); + + os_memset(&cfg->test_item, 0, sizeof(cfg->test_item)); +} + +/* + * + * Function Name: cfg_parse_card_item + * + * Abstract: + * 1. parse the card related registry information + * - If the registry valid bit is invalid state, then set the default configuration + * + * Input: + * cfg_item_t *cfg: Pointer to the config structure + * e_chip_type chip_type: chip type index + * + * Output: + * None + * + * Return value: + * None + * + * Notes: + * Caller: cfg_parse + */ +static void cfg_parse_card_item(cfg_item_t *cfg, e_chip_type chip_type) +{ + cfg_card_item_t *card_item = &cfg->card_item; + + /* sd_card_mode_dis */ + if (card_item->sd_card_mode_dis.sd_mode_dis_enable == 0) { + card_item->sd_card_mode_dis.dis_sd30_card = 0; + card_item->sd_card_mode_dis.dis_sd40_card = 0; + } + + /* max_access_mode */ + if (card_item->test_max_access_mode.reserve == 0) + card_item->test_max_access_mode.value = 3; + + /* max_power_limit */ + if (card_item->test_max_power_limit.reserve == 0) + card_item->test_max_power_limit.value = 3; + + /* emmc_mode */ + if (card_item->emmc_mode.emmc_enable) { + if ((chip_type != CHIP_SEAEAGLE2) && (chip_type != CHIP_GG8) + && (chip_type != CHIP_ALBATROSS)) { + card_item->emmc_mode.enable_12_vccq = 0; + card_item->emmc_mode.enable_18_vcc = 0; + card_item->emmc_mode.enable_force_hs400 = 0; + } + + if ((card_item->emmc_mode.dis_4bit_bus_width == 1) && + (card_item->emmc_mode.dis_8bit_bus_width == 1) + ) { + card_item->emmc_mode.enable_ddr_mode = 0; + } + + } else { + card_item->emmc_mode.dis_hs = 0; + card_item->emmc_mode.dis_4bit_bus_width = 0; + card_item->emmc_mode.dis_8bit_bus_width = 0; + card_item->emmc_mode.enable_ddr_mode = 0; + card_item->emmc_mode.enable_18_vccq = 0; + card_item->emmc_mode.enable_force_hs200 = 0; + card_item->emmc_mode.enable_12_vccq = 0; + card_item->emmc_mode.enable_18_vcc = 0; + card_item->emmc_mode.enable_force_hs400 = 0; + } + + /* uhs2_setting */ + if (card_item->uhs2_setting.reserve == 0) { + if (card_item->uhs2_setting.reserve_syn_dir_gap == 0) { + card_item->uhs2_setting.min_lss_syn = 2; + card_item->uhs2_setting.min_lss_dir = 1; + if (chip_type == CHIP_SEAEAGLE2 || chip_type == CHIP_GG8 + || chip_type == CHIP_ALBATROSS) + card_item->uhs2_setting.min_data_gap_sel = 0; + else + card_item->uhs2_setting.min_data_gap_sel = 1; + } + + if (card_item->uhs2_setting.reserve_nfcu == 0) { + if (chip_type == CHIP_SEAEAGLE) + card_item->uhs2_setting.max_nfcn_sel = 0x8; + else if (chip_type == CHIP_SEAEAGLE2 + || chip_type == CHIP_GG8 + || chip_type == CHIP_ALBATROSS) + card_item->uhs2_setting.max_nfcn_sel = 0x40; + else + card_item->uhs2_setting.max_nfcn_sel = 0x10; + } + + /* half */ + card_item->uhs2_setting.half_full_sel = 1; + /* fast mode */ + card_item->uhs2_setting.fast_low_pwr_sel = 0; + /* range-B */ + card_item->uhs2_setting.max_speed_range_sel = 1; + } + + /* test_uhs2_setting2 */ + if (card_item->test_uhs2_setting2.reserve == 0) { + card_item->test_uhs2_setting2.enable_power_off_vdd1 = 0; + card_item->test_uhs2_setting2.enable_full_reset_reinit = 1; + card_item->test_uhs2_setting2.enable_internal_clk_dormant = 1; + card_item->test_uhs2_setting2.disable_scramb_mode = 0; + } +} + +/* + * + * Function Name: cfg_parse_host_item + * + * Abstract: + * 1. set the host cfg pointer + * 2. parse the host related registry information + * - If the registry valid bit is invalid state, then set the default configuration + * + * Input: + * cfg_item_t *cfg: Pointer to the config structure + * e_chip_type chip_type: chip type index + * + * Output: + * None + * + * Return value: + * None + * + * Notes: + * Caller: cfg_parse + */ +static void cfg_parse_host_item(cfg_item_t *cfg, e_chip_type chip_type) +{ + cfg_host_item_t *host_item = &cfg->host_item; + + /* dma_mode_setting */ + if (host_item->test_dma_mode_setting.reserve == 0) { + /* adma2 */ + host_item->test_dma_mode_setting.dma_mode = 1; + host_item->test_dma_mode_setting.enable_dma_26bit_len = 0; + host_item->test_dma_mode_setting.enable_dma_64bit_address = 0; + host_item->test_dma_mode_setting.enable_dma_32bit_blkcount = 0; + } + + if ((chip_type != CHIP_SEAEAGLE2) && (chip_type != CHIP_GG8) + && (chip_type != CHIP_ALBATROSS)) { + /* only SE2 support merge */ + host_item->test_tag_queue_capability.enable_srb_merge = 0; + } + + /* infinite_transfer_mode */ + if (host_item->test_infinite_transfer_mode.enable_inf == 0) { + host_item->test_infinite_transfer_mode.enable_legacy_inf = 0; + host_item->test_infinite_transfer_mode.enable_sd40_inf = 0; + host_item->test_infinite_transfer_mode.enable_mmc_inf = 0; + host_item->test_infinite_transfer_mode.enable_emmc_inf = 0; + } + + /* sdma_boundary_len_setting */ + if (host_item->test_sdma_boundary.reserve) { + /* SDMA */ + if (host_item->test_dma_mode_setting.dma_mode == 0) { + if (host_item->test_sdma_boundary.value < 4) + host_item->test_sdma_boundary.value = 4; + } + } else { + host_item->test_sdma_boundary.value = 32; + } + + /* tag_queue_capability */ + if (host_item->test_tag_queue_capability.reserve == 0) { + /* 16 SRBs */ + host_item->test_tag_queue_capability.max_srb = 0x10; + host_item->test_tag_queue_capability.enable_srb_merge = 0; + } else { + if (host_item->test_tag_queue_capability.max_srb == 0) + host_item->test_tag_queue_capability.max_srb = 0x10; + } + + if (host_item->test_ocb_ctrl.sw_pwroff_en) + host_item->test_ocb_ctrl.int_check_en = 1; +} + +/* + * + * Function Name: cfg_parse_issue_fix_item + * + * Abstract: + * 1. set the issue ifx item cfg pointer + * 2. parse the issue fix item related registry information + * - If the registry valid bit is invalid state, then set the default configuration + * + * Input: + * cfg_item_t *cfg: Pointer to the config structure + * + * Output: + * None + * + * Return value: + * None + * + * Notes: + * Caller: cfg_parse + */ +static void cfg_parse_issue_fix_item(cfg_item_t *cfg) +{ + +} + +/* + * + * Function Name: cfg_parse_feature_item + * + * Abstract: + * 1. set the feature item cfg pointer + * 2. parse the feature item related registry information + * - If the registry valid bit is invalid state, then set the default configuration + * + * Input: + * cfg_item_t *cfg: Pointer to the config structure + * + * Output: + * None + * + * Return value: + * None + * + * Notes: + * Caller: cfg_parse + */ +static void cfg_parse_feature_item(cfg_item_t *cfg, e_chip_type chip_type) +{ + if (chip_type != CHIP_SEAEAGLE && chip_type != CHIP_SEAEAGLE2 + && chip_type != CHIP_GG8 && chip_type != CHIP_ALBATROSS) { + cfg->feature_item.output_tuning_item.enable_dll_divider = 0; + } +} + +/* + * + * Function Name: cfg_parse_timeout_item + * + * Abstract: + * 1. set the timeout item cfg pointer + * 2. parse the timeout item related registry information + * - If the registry valid bit is invalid state, then set the default configuration + * + * Input: + * cfg_item_t *cfg: Pointer to the config structure + * + * Output: + * None + * + * Return value: + * None + * + * Notes: + * Caller: cfg_parse + */ +static void cfg_parse_timeout_item(cfg_item_t *cfg) +{ + cfg_timeout_item_t *timeout_item = &cfg->timeout_item; + + if (timeout_item->test_write_data_timeout.reserve == 0) + /* default:6s */ + timeout_item->test_write_data_timeout.value = 6000; + + if (timeout_item->test_read_data_timeout.reserve == 0) + /* default:6s */ + timeout_item->test_read_data_timeout.value = 6000; + + if (timeout_item->test_non_data_timeout.reserve == 0) + timeout_item->test_non_data_timeout.value = 1000; + + if (timeout_item->test_r1b_data_timeout.reserve == 0) + timeout_item->test_r1b_data_timeout.value = 4500; + + if (timeout_item->test_card_init_timeout.reserve == 0) + /* delay ACMD41/CMD1 return ready 1.5s (max) */ + timeout_item->test_card_init_timeout.value = 1500; +} + +/* + * + * Function Name: cfg_parse_fpga_item + * + * Abstract: + * 1. set the fpga item cfg pointer + * 2. parse the fpga item related registry information + * - If the registry valid bit is invalid state, then set the default configuration + * + * Input: + * cfg_item_t *cfg: Pointer to the config structure + * + * Output: + * None + * + * Return value: + * None + * + * Notes: + * Caller: cfg_parse + */ +static void cfg_parse_fpga_item(cfg_item_t *cfg) +{ + +} + +/* + * + * Function Name: cfg_parse_driver_item + * + * Abstract: + * 1. set the driver item cfg pointer + * 2. parse the driver item related registry information + * - If the registry valid bit is invalid state, then set the default configuration + * + * Input: + * cfg_item_t *cfg: Pointer to the config structure + * + * Output: + * None + * + * Return value: + * None + * + * Notes: + * Caller: cfg_parse + */ +static void cfg_parse_driver_item(cfg_item_t *cfg, e_chip_type chip_type) +{ + cfg_driver_item_t *driver_item = &cfg->driver_item; + /* driver item */ + if (driver_item->reserve == 0) { + + driver_item->dis_patch_ntfs_verify_rtd3 = FALSE; + driver_item->dis_patch_rtd3_idle_ref_cnt = FALSE; + driver_item->delay_for_failsafe_s3resume = 3; + driver_item->failsafe_en = 0; + + } +#if GLOBAL_ENABLE_BOOT + driver_item->removable = FALSE; + driver_item->removable_pnp = FALSE; +#endif + +} + +/* + * + * Function Name: cfg_parse + * + * Abstract: + * 1. parse the registry information + * - If the registry valid bit is invalid state, then set the default configuration + * + * Input: + * cfg_item_t *cfg: Pointer to the cfg_item structure + * e_chip_type chip_type: chip type index + * + * Output: + * None + * + * Return value: + * None + * + * Notes: + * Caller: cfgmng_init + */ +static void cfg_parse(cfg_item_t *cfg, e_chip_type chip_type) +{ + cfg_parse_card_item(cfg, chip_type); + cfg_parse_host_item(cfg, chip_type); + cfg_parse_issue_fix_item(cfg); + cfg_parse_feature_item(cfg, chip_type); + cfg_parse_timeout_item(cfg); + cfg_parse_fpga_item(cfg); + cfg_parse_driver_item(cfg, chip_type); + +} + +void cfg_dma_mode_dec(cfg_item_t *cfg, u32 dec_dma_mode) +{ + cfg->host_item.test_dma_mode_setting.dma_mode = dec_dma_mode; + DbgInfo(MODULE_CFG_MNG, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Change DMA mode to 0x%08x\n", dec_dma_mode); +} + +void cfg_dma_addr_range_dec(cfg_item_t *cfg, u32 dma_range) +{ + cfg->host_item.test_dma_mode_setting.enable_dma_64bit_address = + dma_range; + DbgInfo(MODULE_CFG_MNG, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Change DMA addr(64bit Address) to %d\n", dma_range); +} + +void cfgmng_update_dumpmode(cfg_item_t *cfg, e_chip_type chip_type) +{ + /* Use Non-Infinite adma2 mode for dump_mode */ + cfg->host_item.test_dma_mode_setting.dma_mode = CFG_TRANS_MODE_ADMA2; + + cfg->timer_item.auto_dormant_timer.enable_dmt_func = 0; + + /* disable rtd3 */ + cfg->feature_item.psd_mode.enable_rtd3 = FALSE; + + /* disable output tuning */ + cfg->feature_item.output_tuning_item.enable_dll = 0; + cfg->host_item.test_tag_queue_capability.max_srb = 1; + + cfg->boot_flag = TRUE; + + DbgInfo(MODULE_CFG_MNG, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "update cfg for dump_mode setting\n"); +} + +/* bus_width 0: 1bit 1: 4bit 2: 8bit */ +void cfg_emmc_busw_supp(cfg_emmc_mode_t *emmc_mode, u8 bus_width) +{ + emmc_mode->dis_8bit_bus_width = 1; + emmc_mode->dis_4bit_bus_width = 1; + + if (bus_width == 1) { + emmc_mode->dis_8bit_bus_width = 1; + emmc_mode->dis_4bit_bus_width = 0; + } else if (bus_width == 2) { + emmc_mode->dis_8bit_bus_width = 0; + emmc_mode->dis_4bit_bus_width = 0; + } + + DbgInfo(MODULE_CFG_MNG, FEATURE_CARD_INIT, NOT_TO_RAM, + "Change MMC bus width to %d\n", bus_width); +} + +void os_load_pcr_cb(void *cfgp, u32 type, u32 idx, u32 addr, u32 value) +{ + cfg_item_t *cfg = cfgp; + + if (idx >= MAX_PCR_SETTING_SIZE) { + DbgErr("%s idx(%d) ovf(%d)\n", __func__, idx, + MAX_PCR_SETTING_SIZE); + goto exit; + } + if (cfg->pcr_item.cnt < MAX_PCR_SETTING_SIZE) { + cfg_pcr_t *pcr = &cfg->pcr_item.pcr_tb[idx]; + + pcr->valid_flg = 1; + pcr->type = type; + pcr->addr = (u16) addr; + pcr->mask = (u16) (value >> 16); + pcr->val = (u16) (value & 0xffff); + cfg->pcr_item.cnt++; + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, NOT_TO_RAM, + "[%d] PCR Addr: 0x%04X vlaue=0x%8X\n", idx, addr, + value); + DbgErr("[%d] PCR Addr: 0x%04X vlaue=0x%8X\n", idx, addr, value); + } else { + DbgErr("%s cnt(%d) ovf(%d)\n", __func__, cfg->pcr_item.cnt, + MAX_PCR_SETTING_SIZE); + goto exit; + } +exit: + ; +} + +void os_load_dmdn_cb(void *cfgp, u32 type, u32 idx, u32 addr, u32 value) +{ + cfg_item_t *cfg = cfgp; + + if (addr < MAX_FREQ_SUPP) { + cfg->dmdn_tbl[addr] = value; + DbgInfo(MODULE_CFG_MNG, FEATURE_CFG_TRACE, NOT_TO_RAM, + "DMDN Table idx: 0x%x, Value: 0x%08x\n", addr, value); + } +} + +bool cfg_dma_need_sdma_like_buffer(u32 dma_mode) +{ + if ((dma_mode == CFG_TRANS_MODE_ADMA2_SDMA_LIKE) || + (dma_mode == CFG_TRANS_MODE_ADMA3_SDMA_LIKE) || + (dma_mode == CFG_TRANS_MODE_SDMA) || + (dma_mode == CFG_TRANS_MODE_ADMA_MIX_SDMA_LIKE)) + return TRUE; + else + return FALSE; + +} diff --git a/drivers/scsi/bht/main/funcapi.h b/drivers/scsi/bht/main/funcapi.h new file mode 100644 index 000000000000..be93b723f017 --- /dev/null +++ b/drivers/scsi/bht/main/funcapi.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: funcapi.h + * + * Abstract: This include file define interface for each function + * + * Version: 1.00 + * + * Author: Peter.Guo + * + * Environment: OS Independent + * + * History: + * + * 8/25/2014 Creation Peter.Guo + */ + +void func_autotimer_init(bht_dev_ext_t *pdx); + +void pm_init(bht_dev_ext_t *pdx); + +void func_timer_thread(bht_dev_ext_t *pdx); + +/* + * This function is called by thread + * (1) If auotpowered off wake up card + * (2) Thermal contorl if tagqueue is not running + */ +void tagio_event_handler(bht_dev_ext_t *pdx); +void genio_event_hanlder(bht_dev_ext_t *pdx); +void rtd3_event_hanlder(bht_dev_ext_t *pdx); + +void testcase_main(bht_dev_ext_t *pdx, byte type); +void testcase_init(bht_dev_ext_t *pdx); diff --git a/drivers/scsi/bht/main/geniofunc.c b/drivers/scsi/bht/main/geniofunc.c new file mode 100644 index 000000000000..d43653594f57 --- /dev/null +++ b/drivers/scsi/bht/main/geniofunc.c @@ -0,0 +1,618 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: geniofunc.c + * + * Abstract: This source file used to implement IOctrl request + * + * Version: 1.00 + * + * Author: Peter.Guo + * + * Environment: OS Independent + * + * History: + * + * 8/25/2014 Creation Peter.Guo + */ + +#include "../include/basic.h" +#include "../include/cardapi.h" +#include "../include/reqapi.h" +#include "../include/tqapi.h" +#include "../include/hostapi.h" +#include "../include/cmdhandler.h" +#include "../include/funcapi.h" +#include "funcapi.h" +#include "../include/debug.h" + +bool func_cprm(sd_card_t *card, request_t *req) +{ + bool result = FALSE; + bool ret = FALSE; + sd_host_t *host = card->host; + card_info_t *card_info = &(card->info); + byte cmd_index; + u32 argument = 0; + u32 cmd_flag = CMD_FLG_R1 | CMD_FLG_RESCHK; + sd_command_t sd_cmd; + u32 *pSrbData32; + + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "Enter %s\n", __func__); + + if ((card->card_type != CARD_SD) && (card->card_type != CARD_UHS2)) + goto exit; + + result = card_stop_infinite(card, TRUE, NULL); + if (result == FALSE) { + DbgErr("Stop Infinite failed1\n"); + goto exit; + } + + /* If use read write, Save Current DMA mode */ + host_transfer_init(host, FALSE, TRUE); + result = FALSE; + switch (req->gen_req_t.arg1) { + case CPRM_IO_GETCSD: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "CPRM_IO_GETCSD\n"); + os_memcpy(req->srb_buff, &(card_info->raw_csd[0]), 16); + result = TRUE; + break; + case CPRM_IO_GETMID: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "CPRM_IO_GETMID\n"); + cmd_index = SD_CMD44 | SD_APPCMD; + cmd_flag = CMD_FLG_R1 | CMD_FLG_RESCHK; + ret = + card_send_sdcmd(card, &sd_cmd, cmd_index, 0, cmd_flag, + DATA_DIR_IN, req->srb_buff, 8); + if (!ret) { + DbgErr("Issue ACMD44 Fail.\n"); + goto exit; + } + result = TRUE; + break; + case CPRM_IO_GETWP: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "CPRM_IO_GETWP\n"); + pSrbData32 = (u32 *) req->srb_buff; + if ((sdhci_readw(host, 0x26) & 0x08) != 0) + *pSrbData32 = 0; + else + *pSrbData32 = 1; + result = TRUE; + break; + case CPRM_IO_GETMKB: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "CPRM_IO_GETMKB\n"); + cmd_index = SD_CMD43 | SD_APPCMD; + cmd_flag = CMD_FLG_R1 | CMD_FLG_RESCHK; + argument = *((u32 *) req->srb_buff); + ret = + card_send_sdcmd(card, &sd_cmd, cmd_index, argument, + cmd_flag, DATA_DIR_IN, req->srb_buff, 512); + if (!ret) { + DbgErr("Issue ACMD43 Fail.\n"); + goto exit; + } + result = TRUE; + break; + case CPRM_IO_SETCERRN: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "CPRM_IO_SETCERRN\n"); + cmd_index = SD_CMD45 | SD_APPCMD; + cmd_flag = CMD_FLG_R1 | CMD_FLG_RESCHK; + ret = + card_send_sdcmd(card, &sd_cmd, cmd_index, 0, cmd_flag, + DATA_DIR_OUT, req->srb_buff, 8); + if (!ret) { + DbgErr("Issue ACMD45 Fail.\n"); + goto exit; + } + result = TRUE; + break; + case CPRM_IO_GETCERRN: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "CPRM_IO_GETCERRN\n"); + cmd_index = SD_CMD46 | SD_APPCMD; + cmd_flag = CMD_FLG_R1 | CMD_FLG_RESCHK; + ret = + card_send_sdcmd(card, &sd_cmd, cmd_index, 0, cmd_flag, + DATA_DIR_IN, req->srb_buff, 8); + if (!ret) { + DbgErr("Issue ACMD46 Fail.\n"); + goto exit; + } + result = TRUE; + break; + case CPRM_IO_SETCERRES: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "CPRM_IO_SETCERRES\n"); + cmd_index = SD_CMD47 | SD_APPCMD; + cmd_flag = CMD_FLG_R1 | CMD_FLG_RESCHK; + ret = + card_send_sdcmd(card, &sd_cmd, cmd_index, 0, cmd_flag, + DATA_DIR_OUT, req->srb_buff, 8); + if (!ret) { + DbgErr("Issue ACMD47 Fail.\n"); + goto exit; + } + result = TRUE; + break; + case CPRM_IO_GETCERRES: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "CPRM_IO_GETCERRES\n"); + cmd_index = SD_CMD48 | SD_APPCMD; + cmd_flag = CMD_FLG_R1 | CMD_FLG_RESCHK; + ret = + card_send_sdcmd(card, &sd_cmd, cmd_index, 0, cmd_flag, + DATA_DIR_IN, req->srb_buff, 8); + if (!ret) { + DbgErr("Issue ACMD48 Fail.\n"); + goto exit; + } + result = TRUE; + break; + case CPRM_IO_CHANGE_SA: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "CPRM_IO_CHANGE_SA\n"); + cmd_index = SD_CMD49 | SD_APPCMD; + cmd_flag = CMD_FLG_R1 | CMD_FLG_RESCHK; + ret = + card_send_sdcmd(card, &sd_cmd, cmd_index, 0, cmd_flag, + DATA_DIR_NONE, NULL, 0); + if (!ret) { + DbgErr("Issue ACMD49 Fail.\n"); + goto exit; + } + result = TRUE; + break; + case CPRM_IO_READ: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "CPRM_IO_READ\n"); + if (req->gen_req_t.data_len == 512) { + cmd_index = SD_CMD17; + cmd_flag = + CMD_FLG_ADMA_SDMA | CMD_FLG_R1 | CMD_FLG_RESCHK; + } else { + cmd_index = SD_CMD18; + cmd_flag = + CMD_FLG_MULDATA | CMD_FLG_ADMA_SDMA | CMD_FLG_R1 | + CMD_FLG_RESCHK; + cmd_set_auto_cmd_flag(card, &cmd_flag); + } + + ret = + card_send_sdcmd(card, &sd_cmd, cmd_index, + req->gen_req_t.arg2, cmd_flag, DATA_DIR_IN, + req->srb_buff, req->gen_req_t.data_len); + if (!ret) { + DbgErr("Issue CMD18 Fail.\n"); + goto exit; + } + result = TRUE; + break; + case CPRM_IO_WRITE: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "CPRM_IO_WRITE\n"); + if (req->gen_req_t.data_len == 512) { + cmd_index = SD_CMD24; + cmd_flag = + CMD_FLG_ADMA_SDMA | CMD_FLG_R1 | CMD_FLG_RESCHK; + } else { + cmd_index = SD_CMD25; + cmd_flag = + CMD_FLG_MULDATA | CMD_FLG_ADMA_SDMA | CMD_FLG_R1 | + CMD_FLG_RESCHK; + cmd_set_auto_cmd_flag(card, &cmd_flag); + } + + ret = + card_send_sdcmd(card, &sd_cmd, cmd_index, + req->gen_req_t.arg2, cmd_flag, DATA_DIR_OUT, + req->srb_buff, req->gen_req_t.data_len); + if (!ret) { + DbgErr("Issue CMD25 Fail.\n"); + goto exit; + } + result = TRUE; + break; + case CPRM_IO_SECURE_READ: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "CPRM_IO_SECURE_READ\n"); + cmd_index = SD_CMD18 | SD_APPCMD; + cmd_flag = CMD_FLG_ADMA_SDMA | CMD_FLG_R1 | CMD_FLG_RESCHK; + if (card->card_type == CARD_SD) + cmd_flag |= CMD_FLG_MULDATA; + argument = req->gen_req_t.data_len >> 9; + argument = ((argument << 24) | req->gen_req_t.arg2); + + ret = + card_send_sdcmd(card, &sd_cmd, cmd_index, argument, + cmd_flag, DATA_DIR_IN, req->srb_buff, + req->gen_req_t.data_len); + if (!ret) { + DbgErr("Issue ACMD18 Fail.\n"); + goto exit; + } + result = TRUE; + break; + case CPRM_IO_SECURE_WRITE: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "CPRM_IO_SECURE_WRITE\n"); + cmd_index = SD_CMD25 | SD_APPCMD; + cmd_flag = CMD_FLG_ADMA_SDMA | CMD_FLG_R1 | CMD_FLG_RESCHK; + if (card->card_type == CARD_SD) + cmd_flag |= CMD_FLG_MULDATA; + argument = req->gen_req_t.data_len >> 9; + argument = ((argument << 24) | req->gen_req_t.arg2); + + ret = + card_send_sdcmd(card, &sd_cmd, cmd_index, argument, + cmd_flag, DATA_DIR_OUT, req->srb_buff, + req->gen_req_t.data_len); + if (!ret) { + DbgErr("Issue CMD25 Fail.\n"); + goto exit; + } + result = TRUE; + break; + case CPRM_IO_GETSDHC: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "CPRM_IO_GETSDHC\n"); + pSrbData32 = (u32 *) req->srb_buff; + *pSrbData32 = card_info->card_ccs; + result = TRUE; + break; + default: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "IOCTL Unknown\n"); + break; + + } + /* Resorte current DMA mode */ + host_transfer_init(host, card->inf_trans_enable, FALSE); +exit: + + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); + return result; +} + +bool func_io_reg(sd_card_t *card, request_t *req) +{ + bool result = FALSE; + sd_host_t *host = card->host; + u32 *pSrbData32; + + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "Enter %s\n", __func__); + + switch (req->gen_req_t.arg1) { + case IO_READ_PCI_REG: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "IO_READ_PCI_REG\n"); + pSrbData32 = (u32 *) req->srb_buff; + *pSrbData32 = pci_readl(host, (u16) req->gen_req_t.arg2); + result = TRUE; + break; + case IO_WRITE_PCI_REG: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "IO_WRITE_PCI_REG\n"); + pSrbData32 = (u32 *) req->srb_buff; + pci_writel(host, (u16) req->gen_req_t.arg2, *(pSrbData32 + 1)); + result = TRUE; + break; + case IO_READ_MEM_REG: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "IO_READ_MEM_REG\n"); + pSrbData32 = (u32 *) req->srb_buff; + *pSrbData32 = sdhci_readl(host, (u16) req->gen_req_t.arg2); + result = TRUE; + break; + case IO_WRITE_MEM_REG: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "IO_WRITE_MEM_REG\n"); + pSrbData32 = (u32 *) req->srb_buff; + sdhci_writel(host, (u16) req->gen_req_t.arg2, + *(pSrbData32 + 1)); + result = TRUE; + break; + default: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "IOCTL Unknown\n"); + break; + + } + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); + return result; +} + +bool erase_rw_blk_end_set(sd_card_t *card, sd_command_t *sd_cmd, u32 sec_addr) +{ + u32 cmdflag = CMD_FLG_R1 | CMD_FLG_RESCHK; + e_data_dir dir = DATA_DIR_NONE; + byte *data = NULL; + u32 datalen = 0; + + bool ret = FALSE; + + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "Enter %s\n", __func__); + + ret = + card_send_sdcmd(card, sd_cmd, SD_CMD33, sec_addr, cmdflag, dir, + data, datalen); + if (!ret) + DbgErr("erase rw blk end set error\n"); + + return ret; +} + +bool erase_rw_blk_start_set(sd_card_t *card, sd_command_t *sd_cmd, + u32 sec_addr) +{ + u32 cmdflag = CMD_FLG_R1 | CMD_FLG_RESCHK; + e_data_dir dir = DATA_DIR_NONE; + byte *data = NULL; + u32 datalen = 0; + + bool ret = FALSE; + + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "Enter %s\n", __func__); + + ret = + card_send_sdcmd(card, sd_cmd, SD_CMD32, sec_addr, cmdflag, dir, + data, datalen); + if (!ret) + DbgErr("erase rw blk start set error\n"); + + return ret; +} + +bool func_erase(sd_card_t *card, sd_command_t *sd_cmd) +{ + u32 cmdflag = CMD_FLG_R1B | CMD_FLG_RESCHK; + e_data_dir dir = DATA_DIR_NONE; + byte *data = NULL; + u32 datalen = 0; + byte cmd_index = SD_CMD38; + bool ret = FALSE; + u32 argument = 0x0; + + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "Enter %s\n", __func__); + + ret = + card_send_sdcmd(card, sd_cmd, cmd_index, argument, cmdflag, dir, + data, datalen); + if (!ret) + DbgErr("erase error\n"); + + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); + return ret; +} + +bool func_nsm(sd_card_t *card, request_t *req, bht_dev_ext_t *pdx) +{ + bool result = FALSE; + bool ret = FALSE; + sd_host_t *host = card->host; + card_info_t *card_info = &(card->info); + byte cmd_index; + u32 cmd_flag = CMD_FLG_R1 | CMD_FLG_RESCHK; + sd_command_t sd_cmd; + u8 *p8; + + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "Enter %s\n", __func__); + + if ((card->card_type != CARD_SD) && (card->card_type != CARD_UHS2)) + goto exit; + + result = card_stop_infinite(card, TRUE, NULL); + if (result == FALSE) { + DbgErr("Stop Infinite failed1\n"); + goto exit; + } + + /* If use read write, Save Current DMA mode */ + host_transfer_init(host, FALSE, TRUE); + result = FALSE; + switch (req->gen_req_t.arg1) { + + case IO_NSM_CMD48: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "IO_NSM_CMD48\n"); + cmd_index = SD_CMD48; + cmd_flag = CMD_FLG_R1 | CMD_FLG_RESCHK; + ret = + card_send_sdcmd(card, &sd_cmd, cmd_index, + req->gen_req_t.arg2, cmd_flag, DATA_DIR_IN, + req->srb_buff, req->gen_req_t.data_len); + if (!ret) { + DbgErr("IO_NSM_CMD48 Fail.\n"); + goto exit; + } + result = TRUE; + break; + case IO_NSM_CMD49: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "IO_NSM_CMD49\n"); + cmd_index = SD_CMD49; + cmd_flag = CMD_FLG_R1 | CMD_FLG_RESCHK; + ret = + card_send_sdcmd(card, &sd_cmd, cmd_index, + req->gen_req_t.arg2, cmd_flag, DATA_DIR_OUT, + req->srb_buff, req->gen_req_t.data_len); + if (!ret) { + DbgErr("IO_NSM_CMD49 Fail.\n"); + goto exit; + } + result = TRUE; + break; + case IO_NSM_CMD58: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "IO_NSM_CMD58\n"); + cmd_index = SD_CMD58; + cmd_flag = + CMD_FLG_AUTO12 | CMD_FLG_MULDATA | CMD_FLG_R1 | + CMD_FLG_RESCHK; + ret = + card_send_sdcmd(card, &sd_cmd, cmd_index, + req->gen_req_t.arg2, cmd_flag, DATA_DIR_IN, + req->srb_buff, req->gen_req_t.data_len); + if (!ret) { + DbgErr("IO_NSM_CMD58 Fail.\n"); + goto exit; + } + result = TRUE; + break; + case IO_NSM_CMD59: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "IO_NSM_CMD59\n"); + cmd_index = SD_CMD59; + cmd_flag = + CMD_FLG_AUTO12 | CMD_FLG_MULDATA | CMD_FLG_R1 | + CMD_FLG_RESCHK; + ret = + card_send_sdcmd(card, &sd_cmd, cmd_index, + req->gen_req_t.arg2, cmd_flag, DATA_DIR_OUT, + req->srb_buff, req->gen_req_t.data_len); + if (!ret) { + DbgErr("IO_NSM_CMD59 Fail.\n"); + goto exit; + } + result = TRUE; + break; + case IO_NSM_CMD42: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "IO_NSM_CMD42\n"); + + result = TRUE; + cmd_index = SD_CMD42; + cmd_flag = CMD_FLG_R1 | CMD_FLG_RESCHK; + ret = + card_send_sdcmd(card, &sd_cmd, cmd_index, + req->gen_req_t.arg2, cmd_flag, DATA_DIR_OUT, + req->srb_buff, req->gen_req_t.data_len); + if (!ret) { + DbgErr("IO_NSM_CMD42 Fail.\n"); + goto exit; + } + p8 = (u8 *) (req->srb_buff); + /* If Unlock command */ + if (((*p8) & 0x4) == 0) { + card->locked = FALSE; + result = card_init_stage2(card); + + if (result == TRUE && pdx->scsi.last_present == 0) { + + DbgInfo(MODULE_MAIN_THR, FEATURE_THREAD_TRACE, + NOT_TO_RAM, + "Exec Bus Change for Unlock ok\n"); + /* callback execute successfully */ + if (thread_exec_high_prio_job + (pdx, os_bus_change, pdx)) + pdx->scsi.last_present = 1; + } + } + break; + case IO_NSM_CMD9: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "IO_NSM_CMD9\n"); + os_memcpy(req->srb_buff, &(card_info->raw_csd[0]), 16); + result = TRUE; +#if (0) + result = TRUE; + cmd_index = SD_CMD9; + cmd_flag = CMD_FLG_R2 | CMD_FLG_RESCHK; + ret = + card_send_sdcmd(card, &sd_cmd, cmd_index, + card_info->rca << 16, cmd_flag, + DATA_DIR_NONE, NULL, 0); + if (!ret) { + DbgErr("IO_NSM_CMD9 Fail.\n"); + goto exit; + } else { + /* Set the card CSD info */ + os_memcpy(req->srb_buff, &(sd_cmd->response[0]), 16); + } +#endif + break; + case IO_NSM_CMD10: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "IO_NSM_CMD10\n"); + os_memcpy(req->srb_buff, &(card_info->raw_cid[0]), 16); + + result = TRUE; +#if (0) + cmd_index = SD_CMD10; + cmd_flag = CMD_FLG_R2 | CMD_FLG_RESCHK; + ret = + card_send_sdcmd(card, &sd_cmd, cmd_index, + card_info->rca << 16, cmd_flag, + DATA_DIR_NONE, NULL, 0); + if (!ret) { + DbgErr("IO_NSM_CMD10 Fail.\n"); + goto exit; + } else { + /* Set the card CID info */ + os_memcpy(req->srb_buff, &(sd_cmd->response[0]), 16); + } +#endif + break; + + case IO_NSM_ACMD51: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "IO_NSM_ACMD51\n"); + + result = TRUE; + cmd_index = SD_CMD51 | SD_APPCMD; + cmd_flag = CMD_FLG_R1 | CMD_FLG_RESCHK; + ret = + card_send_sdcmd(card, &sd_cmd, cmd_index, + card_info->rca << 16, cmd_flag, DATA_DIR_IN, + req->srb_buff, 8); + if (!ret) { + DbgErr("IO_NSM_ACMD51 Fail.\n"); + goto exit; + } + break; + + case IO_NSM_ACMD13: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "IO_NSM_ACMD13\n"); + + result = TRUE; + cmd_index = SD_CMD13 | SD_APPCMD; + cmd_flag = CMD_FLG_R1 | CMD_FLG_RESCHK; + ret = + card_send_sdcmd(card, &sd_cmd, cmd_index, 0, cmd_flag, + DATA_DIR_IN, req->srb_buff, 64); + if (!ret) { + DbgErr("IO_NSM_ACMD13 Fail.\n"); + goto exit; + } + break; + + default: + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "IOCTL Unknown\n"); + break; + + } + /* Resorte current DMA mode */ + host_transfer_init(host, card->inf_trans_enable, FALSE); +exit: + + DbgInfo(MODULE_MAIN_GENIO, FEATURE_IOCTL_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); + return result; +} diff --git a/drivers/scsi/bht/main/pmfunc.c b/drivers/scsi/bht/main/pmfunc.c new file mode 100644 index 000000000000..6431f0c1ffd9 --- /dev/null +++ b/drivers/scsi/bht/main/pmfunc.c @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: pmfunc.c + * + * Abstract: This source file used to implement power management functions + * + * Version: 1.00 + * + * Author: Yuxiang + * + * Environment: OS Independent + * + * History: + * + * 9/2/2014 Creation Yuxiang + */ + +#include "../include/basic.h" +#include "../include/debug.h" +#include "../include/reqapi.h" +#include "../include/funcapi.h" +#include "../include/hostapi.h" +#include "../include/hostvenapi.h" +#include "../include/cardapi.h" +#include "../include/cmdhandler.h" + +void pm_init(bht_dev_ext_t *pdx) +{ + + DbgInfo(MODULE_MAIN_PM, FEATURE_DRIVER_INIT, 0, "Enter %s chip:%xh\n", + __func__, pdx->host.chip_type); + + /* TODO: RTD3 enable From the registry */ + pdx->pm_state.s3s4_entered = FALSE; + pdx->pm_state.rtd3_en = hostven_rtd3_check(&pdx->host); + pdx->pm_state.rtd3_entered = FALSE; + pdx->pm_state.s5_entered = FALSE; + pdx->pm_state.warm_boot_entered = FALSE; + hostven_pm_mode_cfg(&pdx->host, &(pdx->pm_state)); + os_pm_init(pdx); + + DbgInfo(MODULE_MAIN_PM, FEATURE_PM_TRACE, 0, "Exit %s\n", __func__); +} + +extern void host_internal_clk_setup(sd_host_t *host, bool on); + +static void req_enter_d0_internal(bht_dev_ext_t *pdx, bool reinit_async) +{ + DbgInfo(MODULE_MAIN_PM, FEATURE_PM_TRACE, 0, + "Enter %s rtd3=%d card_init status=%d\n", __func__, + pdx->pm_state.rtd3_en, pdx->card.thread_init_card_flag); + +#ifdef MultiThread + os->thread_auto_timer_runing = FALSE; + os->thread_card_init_runing = FALSE; + os->thread_card_remove_runing = FALSE; + os->thread_pending_runing = FALSE; + os->thread_gen_io_runing = FALSE; + os->thread_tag_io_runing = FALSE; + os->thread_rtd3_runing = FALSE; + os->thread_terminate_runing = FALSE; +#else + +#endif + + host_vendor_feature_init(&(pdx->host)); + host_init(&(pdx->host)); + + if (hostven_chk_card_present(&pdx->host)) { + pdx->card.card_present = TRUE; + /* clean card_init flag status to wait for initialization */ + pdx->card.thread_init_card_flag = 0; + + } else { + pdx->scsi.scsi_eject = FALSE; + + pdx->card.card_present = FALSE; + card_stuct_uinit(&pdx->card); + } + thermal_uninit(pdx); + if (reinit_async) { + if (pdx->scsi.scsi_eject == 0) { + +#if CFG_OS_LINUX + os_set_event(&pdx->os, EVENT_CARD_CHG); +#else + os_set_event(pdx, &pdx->os, EVENT_TASK_OCCUR, + TASK_CARD_CHG); +#endif + } else { + host_internal_clk_setup(&(pdx->host), TRUE); + } + } + + pdx->pm_state.s3s4_entered = FALSE; + pdx->pm_state.s5_entered = FALSE; + pdx->pm_state.warm_boot_entered = FALSE; + if (pdx->pm_state.rtd3_en) + pdx->pm_state.rtd3_entered = FALSE; + + DbgInfo(MODULE_MAIN_PM, FEATURE_PM_TRACE, 0, + "Exit %s card_init status=%d\n", __func__, + pdx->card.thread_init_card_flag); +} + +void req_enter_d0(bht_dev_ext_t *pdx) +{ + req_enter_d0_internal(pdx, TRUE); +} + +void req_enter_d0_sync(bht_dev_ext_t *pdx) +{ + req_enter_d0_internal(pdx, FALSE); +} + +void req_pre_enter_d3(bht_dev_ext_t *pdx) +{ + DbgInfo(MODULE_MAIN_PM, FEATURE_PM_TRACE, 0, "Enter %s\n", + __func__); + + /* for thread sync : such as autotimer stop infinite */ + if (os_pending_thread(pdx, TRUE) == FALSE) + DbgErr("%s pending thread failed\n", __func__); + + if (card_stop_infinite(&pdx->card, FALSE, NULL) == FALSE) + card_power_off(&(pdx->card), TRUE); + + os_pending_thread(pdx, FALSE); + + DbgInfo(MODULE_MAIN_PM, FEATURE_PM_TRACE, 0, "Exit %s\n", __func__); +} + +#if (0) +void pcr_part_a_backup(bht_dev_ext_t *pdx) +{ + if (pdx->cfg->driver_item.backup_part_a) { + /* enable or disable LED output or not. */ + pdx->pm_state.reg_0xdc = pci_readl(&pdx->host, 0xdc); + + /* disable or enable RTD3 */ + pdx->pm_state.reg_0x3e0 = pci_readl(&pdx->host, 0x3e0); + + pdx->pm_state.reg_0x3e8 = pci_readl(&pdx->host, 0x3e8); + + pdx->pm_state.reg_0x3ec = pci_readl(&pdx->host, 0x3ec); + + /* Change the ASPM L0s Exit Latency */ + pdx->pm_state.reg_0xf4 = pci_readl(&pdx->host, 0xf4); + + /* ep nfts value */ + pdx->pm_state.reg_0x74 = pci_readl(&pdx->host, 0x74); + + /* Power saving mode setting */ + pdx->pm_state.reg_0xf0 = pci_readl(&pdx->host, 0xf0); + + /* enable or disable ASPM L0s&L1 */ + pdx->pm_state.reg_0x90 = pci_readl(&pdx->host, 0x90); + + } + +} + +void pcr_part_b_backup(bht_dev_ext_t *pdx) +{ + if (pdx->cfg->driver_item.backup_part_b) { + pdx->pm_state.reg_0x64 = pci_readl(&pdx->host, 0x64); + /* skt power control output enable */ + pdx->pm_state.reg_0xec = pci_readl(&pdx->host, 0xec); + /* ocb cntl timer */ + /* ocb cntl enable */ + pdx->pm_state.reg_0xd4 = pci_readl(&pdx->host, 0xd4); + pdx->pm_state.reg_0x3e4 = pci_readl(&pdx->host, 0x3e4); + /* PLL DM */ + pdx->pm_state.reg_0x304 = pci_readl(&pdx->host, 0x304); + + /* Set Base Clock Frequency */ + pdx->pm_state.reg_0x328 = pci_readl(&pdx->host, 0x328); + + /* Set DLL tuning window */ + pdx->pm_state.reg_0x300 = pci_readl(&pdx->host, 0x300); + + /* aux power LDO */ + pdx->pm_state.reg_0x68 = pci_readl(&pdx->host, 0x68); + + /* Adjust the output delay for SD2.0 high speed mode */ + pdx->pm_state.reg_0x350 = pci_readl(&pdx->host, 0x350); + /* T_EIDL_ENTRY */ + pdx->pm_state.reg_0x35c = pci_readl(&pdx->host, 0x35c); + + /* disable bit for UHSII term_resistor_calibration */ + pdx->pm_state.reg_0x3e0 = pci_readl(&pdx->host, 0x3e0); + + /* Set the Max power supply capability of SD host */ + pdx->pm_state.reg_0x334 = pci_readl(&pdx->host, 0x334); + + /* external enable polarity control pin */ + pdx->pm_state.reg_0xd8 = pci_readl(&pdx->host, 0xd8); + + /* AOSC off support */ + pdx->pm_state.reg_0x3f0 = pci_readl(&pdx->host, 0x3f0); + + /* max read request size */ + pdx->pm_state.reg_0x88 = pci_readl(&pdx->host, 0x88); + + /* UHSII DLL watch dog */ + pdx->pm_state.reg_0x33c = pci_readl(&pdx->host, 0x33c); + + /* PCI-PM L1 entrance timer */ + pdx->pm_state.reg_0xe0 = pci_readl(&pdx->host, 0xe0); + + /* ASPM L1 entrance timer */ + pdx->pm_state.reg_0xfc = pci_readl(&pdx->host, 0xfc); + + } +} + +void pcr_part_a_restore(bht_dev_ext_t *pdx) +{ + if (pdx->cfg->driver_item.backup_part_a) { + pci_writel(&pdx->host, 0xdc, pdx->pm_state.reg_0xdc); + pci_writel(&pdx->host, 0x3e0, pdx->pm_state.reg_0x3e0); + pci_writel(&pdx->host, 0x3e8, pdx->pm_state.reg_0x3e8); + pci_writel(&pdx->host, 0x3ec, pdx->pm_state.reg_0x3ec); + pci_writel(&pdx->host, 0xf4, pdx->pm_state.reg_0xf4); + pci_writel(&pdx->host, 0x74, pdx->pm_state.reg_0x74); + pci_writel(&pdx->host, 0xf0, pdx->pm_state.reg_0xf0); + pci_cfgio_writel(&pdx->host, 0x90, pdx->pm_state.reg_0x90); + } +} + +void pcr_part_b_restore(bht_dev_ext_t *pdx) +{ + if (pdx->cfg->driver_item.backup_part_b) { + pci_writel(&pdx->host, 0x64, pdx->pm_state.reg_0x64); + pci_writel(&pdx->host, 0xec, pdx->pm_state.reg_0xec); + pci_writel(&pdx->host, 0xd4, pdx->pm_state.reg_0xd4); + pci_writel(&pdx->host, 0x304, pdx->pm_state.reg_0x304); + pci_writel(&pdx->host, 0x328, pdx->pm_state.reg_0x328); + pci_writel(&pdx->host, 0x300, pdx->pm_state.reg_0x300); + /* Don't need to restore as it is the stick register */ + pci_writel(&pdx->host, 0x3e4, pdx->pm_state.reg_0x3e4); + pci_writel(&pdx->host, 0x68, pdx->pm_state.reg_0x68); + + pci_writel(&pdx->host, 0x350, pdx->pm_state.reg_0x350); + pci_writel(&pdx->host, 0x35c, pdx->pm_state.reg_0x35c); + pci_writel(&pdx->host, 0x3e0, pdx->pm_state.reg_0x3e0); + pci_writel(&pdx->host, 0x334, pdx->pm_state.reg_0x334); + pci_writel(&pdx->host, 0xd8, pdx->pm_state.reg_0xd8); + pci_writel(&pdx->host, 0x3f0, pdx->pm_state.reg_0x3f0); + pci_cfgio_writel(&pdx->host, 0x88, pdx->pm_state.reg_0x88); + pci_writel(&pdx->host, 0x33c, pdx->pm_state.reg_0x33c); + pci_writel(&pdx->host, 0xe0, pdx->pm_state.reg_0xe0); + pci_writel(&pdx->host, 0xfc, pdx->pm_state.reg_0xfc); + } +} +#endif + +void failsafe_fct(bht_dev_ext_t *pdx) +{ + DbgInfo(MODULE_MAIN_PM, FEATURE_PM_TRACE, 0, + "Enter %s, fail-safe: %d, chip type: 0x%x\n", __func__, + pdx->cfg->driver_item.failsafe_en, pdx->host.chip_type); + + /* add s5_entered here to fix GG8-MP FPGA PM issue#2 */ + if ((pdx->pm_state.s3s4_entered) || (pdx->pm_state.s5_entered)) { + if (pdx->cfg->driver_item.failsafe_en == 1) { + if (pdx->host.chip_type == CHIP_SEAEAGLE) { + /* Failsafe enable */ + pci_orl(&(pdx->host), 0x3E0, BIT6); + } + + if (pdx->host.chip_type == CHIP_GG8 + || pdx->host.chip_type == CHIP_ALBATROSS) { + + pci_andl(&(pdx->host), 0x408, ~(0x84210842)); + pci_andl(&(pdx->host), 0x410, ~(0x02108410)); + + } + } + } + + DbgInfo(MODULE_MAIN_PM, FEATURE_PM_TRACE, 0, "Exit %s\n", __func__); +} + +void pcie_weakup(bht_dev_ext_t *pdx, u32 Sx_flag, bool enable) +{ + DbgInfo(MODULE_MAIN_PM, FEATURE_PM_TRACE, 0, "Enter %s(%d) with S%d\n", + __func__, enable, Sx_flag); + + if (pdx->host.chip_type != CHIP_GG8 + && pdx->host.chip_type != CHIP_ALBATROSS) { + DbgErr("Error: pcie weakup function only support GG8\n"); + return; + } + + if (enable) { + /* Driver load, Driver re-enable Card insert/remove source bit(0x468[20] = 1). */ + pci_orl(&(pdx->host), 0x468, (1 << 20)); + } else { + switch (Sx_flag) { + case ENTRY_S3: + if (pdx->cfg->feature_item.pcie_wake_setting.s3_disable_wakeup) + pci_andl(&(pdx->host), 0x468, ~(1 << 20)); + else + DbgErr + ("Warning:registry setting for s3 is not ready\n"); + + break; + + case ENTRY_S4: + if (pdx->cfg->feature_item.pcie_wake_setting.s4_disable_wakeup) + pci_andl(&(pdx->host), 0x468, ~(1 << 20)); + else + DbgErr + ("Warning:registry setting for s4 is not ready\n"); + + break; + + case ENTRY_S5: + if (pdx->cfg->feature_item.pcie_wake_setting.s5_disable_wakeup) + pci_andl(&(pdx->host), 0x468, ~(1 << 20)); + else + DbgErr + ("Warning:registry setting for s5 is not ready\n"); + + break; + + default: + DbgErr + ("Error: only support 3/4/5!!! current value is %d\n", + Sx_flag); + break; + + } + } + + DbgInfo(MODULE_MAIN_PM, FEATURE_PM_TRACE, 0, "Exit %s\n", __func__); +} + +void req_enter_d3(bht_dev_ext_t *pdx) +{ + DbgInfo(MODULE_MAIN_PM, FEATURE_PM_TRACE, 0, "Enter %s\n", + __func__); + + func_autotimer_stop(pdx); + card_power_off(&(pdx->card), FALSE); + thermal_uninit(pdx); + host_int_sig_dis(&(pdx->host), 0xffffffff); + + pdx->card.initialized_once = FALSE; + card_stuct_init(pdx); + + DbgInfo(MODULE_MAIN_PM, FEATURE_PM_TRACE, 0, "Exit %s\n", __func__); + +} diff --git a/drivers/scsi/bht/main/reqmng.c b/drivers/scsi/bht/main/reqmng.c new file mode 100644 index 000000000000..8afcaf670511 --- /dev/null +++ b/drivers/scsi/bht/main/reqmng.c @@ -0,0 +1,546 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: reqmng.c + * + * Abstract: This file is used to manage interface for OSEntry layer + * + * Version: 1.00 + * + * Author: Peter.Guo + * + * Environment: OS Independent + * + * History: + * + * 8/25/2014 Creation Peter.Guo + */ + +#include "../include/basic.h" +#include "../include/reqapi.h" +#include "../include/tqapi.h" +#include "../include/transhapi.h" +#include "../include/debug.h" +#include "../include/hostapi.h" +#include "../include/cardapi.h" +#include "../include/funcapi.h" +#include "funcapi.h" +#include "../include/cmdhandler.h" +#include "../include/hostvenapi.h" + +#ifdef MultiThread +extern void thread_card_chg(void *pdx); +extern void thread_card_init(void *pdx); +extern void thread_event_pending(void *pdx); +extern void thread_event_tag_io(void *pdx); +extern void thread_event_gen_io(void *pdx); +extern void thread_runtime_d3(void *pdx); +extern void thread_auto_timer(void *pdx); +extern void thread_event_terminate(void *pdx); +extern void thread_camod_poll_card_chg(void *pdx); +#endif + +/* + * Function Name: req_global_init + * + * Abstract: + * + * This routine init timer, tagqueue, host cap init, + * and vendor reg setting, host interrupt and init to workable status + * card and host software structure init (caller init pci_dev_t and global cfg). + * + * Input: + * + * pdx [in]: The device extension object. + * + * Output: + * + * None + * + * Return value: + * + * None + * + * Notes: + * + * IRQL <= APC_LEVEL + */ +s32 req_global_init(bht_dev_ext_t *pdx) +{ + DbgInfo(MODULE_REQ_MNG, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* Initialize the host pointer for the card structure */ + pdx->card.host = &(pdx->host); + pdx->scsi_init_flag = 0; + /* Host vendor settings */ + host_vendor_feature_init(&(pdx->host)); + + host_init_capbility(&(pdx->host)); + + /* 6 Initialize the OS layer */ + + /* DPC init has to be in HWinitpassive routine */ + os_layer_init(pdx, &pdx->os); + + /* card struct init */ + card_stuct_init(pdx); + + os_memset(&pdx->pre_card, 0, sizeof(pre_card_info_t)); + + pdx->pre_card.s3s4_resume_for_card_init = FALSE; + + pdx->auto_timer.s3reusme_cardchg_issuefix_en = FALSE; + pdx->auto_timer.s3s4_start_timer = FALSE; + pdx->auto_timer.s3reusme_timer_expect_cnt = 0; + pdx->auto_timer.s3reusme_timer_actual_cnt = 0; + + func_autotimer_init(pdx); + + /* dma api init */ + dma_api_io_init(pdx, &pdx->dma_buff); + + /* Tag queue sturct init */ + + /* dump mode don't use tag queue */ + if (pdx->dump_mode == FALSE) + tag_queue_init(pdx); + + os_memset(&pdx->scsi, 0, sizeof(scsi_mng_t)); + + pdx->soft_irq.enable = 0; + pdx->p_srb_ext = NULL; + + /* 7. Initialize the thread */ + + /* dump mode don't use thread */ + if (pdx->dump_mode == FALSE) + os_create_thread(&pdx->os.thread, pdx, thread_main); + + /* 8. Host controller initialize */ + host_init(&(pdx->host)); + thermal_init(pdx); + pdx->signature = BHT_PDX_SIGNATURE; + + /* 9. PM init */ + pm_init(pdx); + + if (hostven_chk_card_present(&pdx->host)) { + hostven_main_power_ctrl(&pdx->host, TRUE); + pdx->card.card_present = TRUE; + } else { + hostven_main_power_ctrl(&pdx->host, FALSE); + } + + /* Test Case Init */ + testcase_init(pdx); + + DbgInfo(MODULE_REQ_MNG, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + + return 0; +} + +/* + * This function is used context: + * (1) In Thread + * (2) (Card and Tagqueue is not working) + * (3) No other SRB will coming down + */ +void req_global_reinit(bht_dev_ext_t *pdx) +{ + + DbgInfo(MODULE_REQ_MNG, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* Host vendor settings */ + host_vendor_feature_init(&(pdx->host)); + + host_init_capbility(&(pdx->host)); + + card_stuct_uinit(&pdx->card); + + func_autotimer_init(pdx); + + tag_queue_init(pdx); + + thermal_init(pdx); + + if (hostven_chk_card_present(&pdx->host)) { + hostven_main_power_ctrl(&pdx->host, TRUE); + pdx->card.card_present = TRUE; + } + + DbgInfo(MODULE_REQ_MNG, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + +} + +/* + * Function Name: req_cancel_all_io + * Abstract: This Function is used to cancel all not handled io + * + * Input: + * bht_dev_ext_t *pdx + * + * Output: + * + */ +void req_cancel_all_io(bht_dev_ext_t *pdx) +{ + DbgInfo(MODULE_REQ_MNG, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + + if (pdx->dump_mode) + goto exit; + /* try to cancel gen io */ + if (pdx->p_srb_ext != NULL) { + if (pdx->p_srb_ext->req.srb_done_cb) + pdx->p_srb_ext->req.srb_done_cb(pdx, pdx->p_srb_ext); + } else { + /* try to cancel tag io */ + tag_queue_abort(pdx, REQ_RESULT_ABORT); + } +exit: + DbgInfo(MODULE_REQ_MNG, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); + +} + +/* + * + * Function Name: req_global_uninit + * + * Abstract: + * + * This routine uninit os related variable, reset host, finish pending Srb + * reset host + * + * Input: + * + * pdx [in]: The device extension object. + * + * Output: + * + * None + * + * Return value: + * + * None + * + * Notes: + * + * + * + * + * IRQL <= APC_LEVEL + */ + +s32 req_global_uninit(bht_dev_ext_t *pdx) +{ + DbgInfo(MODULE_REQ_MNG, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + func_autotimer_cancel(pdx); + thermal_uninit(pdx); + host_uninit(&pdx->host, TRUE); + + req_cancel_all_io(pdx); + + os_layer_uinit(pdx, &pdx->os); + DbgInfo(MODULE_REQ_MNG, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + return 0; +} + +/* + * Function Name: scsi_check_card_ready + * Abstract: This Function is used to by scsi cmd to check whether UNIT IS READY OR NOT + * + * Input: + * bht_dev_ext_t *pdx + * sd_card_t *card, + * + * Output: + * + * + * Return value: + * TRUE: Card is ready + * FALSE Card is not ready + */ + +static bool scsi_check_card_ready(bht_dev_ext_t *pdx, sd_card_t *card) +{ + bool result = FALSE; + + if (pdx->scsi.scsi_eject) + goto exit; + + /* if card not preset, return failed */ + if (card->card_type == CARD_ERROR || card->card_type == CARD_NONE) + goto exit; + + if (card->card_present == FALSE || card->initialized_once == FALSE + || card->card_chg) + goto exit; + + if (card->locked) + goto exit; + + result = TRUE; +exit: + return result; +} + +/* + * Function Name: req_gen_io_add + * Abstract: This Function is used to add srb request to General IO + * + * Input: + * bht_dev_ext_t *pdx + * srb_ext_t *srb_ext: caller need to fill request_t req; + * Output: + * + * + * Return value: + * + * e_req_result: the result of this command, + * scsi layer use this value to determine scsi cmd status + */ +static bool append_general_io_queue(srb_ext_t **head, srb_ext_t *node) +{ + if (head == NULL) + return FALSE; + + if (*head == NULL) + *head = node; + else { + srb_ext_t *tail = *head; + + while (tail->next != NULL) + tail = tail->next; + tail->next = node; + } + return TRUE; +} + +e_req_result req_gen_io_add(bht_dev_ext_t *pdx, srb_ext_t *srb_ext) +{ + e_req_result result = REQ_RESULT_QUEUE_BUSY; + + DbgInfo(MODULE_REQ_MNG, FEATURE_IOCTL_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* If currently working at general IO, we can't start tag queue io */ +#if (0) + if (pdx->p_srb_ext != NULL) + if (pdx->p_srb_ext->req.type == REQ_TYPE_GEN_IO) + goto exit; +#endif + + result = REQ_RESULT_PENDING; + srb_ext->req.type = REQ_TYPE_GEN_IO; + srb_ext->req.result = REQ_RESULT_PENDING; + srb_ext->next = NULL; + /* check whether the taq queue is empty */ + if (tq_is_empty(pdx) == FALSE) { + append_general_io_queue(&(pdx->p_srb_ext_bak), srb_ext); + } else { + append_general_io_queue(&(pdx->p_srb_ext), srb_ext); + func_autotimer_stop(pdx); +#if CFG_OS_LINUX + os_set_event(&pdx->os, EVENT_GEN_IO); +#else + os_set_event(pdx, &pdx->os, EVENT_TASK_OCCUR, EVENT_GEN_IO); +#endif + } + + DbgInfo(MODULE_REQ_MNG, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "Exit %s ret=%d\n", __func__, result); + return result; + +} + +/* + * Function Name: req_tag_io_add + * Abstract: This Function is used to add srb request to tag queue + * + * Input: + * bht_dev_ext_t *pdx + * srb_ext_t *srb_ext: caller need to fill request_t req; + * Output: + * + * + * Return value: + * + * e_req_result: the result of this command, + * scsi layer use this value to determine scsi cmd status + */ + +e_req_result req_tag_io_add(bht_dev_ext_t *pdx, srb_ext_t *srb_ext) +{ + e_req_result result = REQ_RESULT_QUEUE_BUSY; + sd_card_t *card = &pdx->card; + u32 sec_cnt; + u32 sec_addr; + + DbgInfo(MODULE_REQ_MNG, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* If currently working at general IO, we can't start tag queue io */ + if (pdx->p_srb_ext != NULL) + if (pdx->p_srb_ext->req.type == REQ_TYPE_GEN_IO) + goto exit; + + if (scsi_check_card_ready(pdx, card) == FALSE) { + result = REQ_RESULT_NO_CARD; + goto exit; + } + + /* check write protected */ + if (card->write_protected && srb_ext->req.data_dir == DATA_DIR_OUT) { + result = REQ_RESULT_PROTECTED; + goto exit; + } + + sec_addr = srb_ext->req.tag_req_t.sec_addr; + sec_cnt = srb_ext->req.tag_req_t.sec_cnt; + + /* + * check request oversize + * todo: emmc boot partition case + */ + if ((card->sec_count < sec_addr) + || (card->sec_count < sec_cnt) + || ((card->sec_count - sec_addr) < sec_cnt)) { + result = REQ_RESULT_OVERSIZE; + goto exit; + } + + /* recaculate 1.0 card sector addr */ + + if (card_is_low_capacity(card)) { + sec_addr *= SD_BLOCK_LEN; + srb_ext->req.tag_req_t.sec_addr = sec_addr; + } + + /* for PIO case , forware the request to gen io */ + if (pdx->cfg->host_item.test_dma_mode_setting.dma_mode == 0xF) { + srb_ext->req.gen_req_t.code = GEN_IO_CODE_PIORW; + srb_ext->req.gen_req_t.arg1 = sec_addr; + srb_ext->req.gen_req_t.arg2 = sec_cnt; + result = req_gen_io_add(pdx, srb_ext); + } else { + /* update the request accroing to card type */ + srb_ext->req.type = REQ_TYPE_TAG_IO; + srb_ext->req.result = REQ_RESULT_PENDING; + result = tq_add_request(pdx, srb_ext, card); + if (result == REQ_RESULT_PENDING) + func_autotimer_stop(pdx); + } +exit: + DbgInfo(MODULE_REQ_MNG, FEATURE_RW_TRACE, NOT_TO_RAM, + "Exit %s ret=%d\n", __func__, result); + return result; +} + +/* + * Function Name: req_card_ready + * Abstract: This Function is used to by scsi cmd to check whether UNIT IS READY OR NOT + * + * Input: + * bht_dev_ext_t *pdx + * Output: + * + * + * Return value: + * TRUE: Card is ready + * FALSE Card is not ready + */ +bool req_card_ready(bht_dev_ext_t *pdx) +{ + return scsi_check_card_ready(pdx, &pdx->card); +} + +/* + * Function Name: scsi_check_card_ready + * Abstract: This Function is used to by scsi cmd to check whether UNIT IS READY OR NOT + * + * Input: + * bht_dev_ext_t *pdx, + * + * Output: + * e_req_result *result + * + * Return value: + * reutrn card if card is initialized + */ +e_req_result req_chk_card_info(bht_dev_ext_t *pdx, srb_ext_t *srb_ext) +{ + sd_card_t *card = &pdx->card; + e_req_result result = REQ_RESULT_NO_CARD; + + if (pdx->scsi.scsi_eject) + goto exit; + + if (card->card_present == FALSE) + goto exit; + + /* check whether card has initailaze once */ + if (card->initialized_once == FALSE) { + if (pdx->dump_mode == FALSE) + result = req_gen_io_add(pdx, srb_ext); + else + goto exit; + + if (result != REQ_RESULT_PENDING) + DbgErr("req chk card info add gen io failed\n"); + } else { + result = REQ_RESULT_OK; + } + +exit: + return result; +} + +/* + * Function Name: req_eject + * Abstract: This Function is used by scsi cmd to eject(poweroff) sd card + * + * Input: + * bht_dev_ext_t *pdx, + * + * Output: + * e_req_result result + * + * Return value: + * reutrn card if card is initialized + */ + +e_req_result req_eject(bht_dev_ext_t *pdx, srb_ext_t *srb_ext) +{ + sd_card_t *card = &pdx->card; + e_req_result result = REQ_RESULT_NO_CARD; + + DbgInfo(MODULE_REQ_MNG, FEATURE_SCSICMD_TRACE, NOT_TO_RAM, + "Enter %s ret=%d\n", __func__, result); + + if (card->card_present == FALSE) + goto exit; + + /* If card not power off, send request to thread to do poweroff */ + if (card->state != CARD_STATE_POWEROFF) { + result = req_gen_io_add(pdx, srb_ext); + if (result != REQ_RESULT_PENDING) + DbgErr("eject add gen io failed\n"); + goto exit; + } + + result = REQ_RESULT_OK; + +exit: + DbgInfo(MODULE_REQ_MNG, FEATURE_SCSICMD_TRACE, NOT_TO_RAM, + "Exit %s ret=%d\n", __func__, result); + return result; +} diff --git a/drivers/scsi/bht/main/testcase.c b/drivers/scsi/bht/main/testcase.c new file mode 100644 index 000000000000..95babb83c1ce --- /dev/null +++ b/drivers/scsi/bht/main/testcase.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015 BHT Inc. + * + * File Name: testcase.c + * + * Abstract: This source file used to implement testcase interface + * + * Version: 1.00 + * + * Author: Peter.Guo + * + * Environment: OS Independent + * + * History: + * + * 1/13/2015 Creation Peter.Guo + */ + +#include "../include/basic.h" +#include "../include/cardapi.h" +#include "../include/reqapi.h" +#include "../include/tqapi.h" +#include "../include/hostapi.h" +#include "../include/cmdhandler.h" +#include "../include/funcapi.h" +#include "funcapi.h" +#include "../include/debug.h" +#include "../card/cardcommon.h" +#include "../include/hostvenapi.h" + +typedef struct { + bool (*test_prepare)(bht_dev_ext_t *pdx); + bool (*test_execute)(bht_dev_ext_t *pdx); + char name[24]; +} test_func_t; + +/* + * This function is used to init card + */ +static bool test_init_card(bht_dev_ext_t *pdx) +{ + bool result = FALSE; + sd_card_t *card = &pdx->card; + + DbgInfo(MODULE_TEST, FEATURE_CARD_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + card->card_chg = FALSE; +#if CFG_OS_LINUX + os_sleep(200); +#else + os_sleep((PVOID) pdx, 200); + +#endif + /* Init card here */ + result = card_init(card, 10, FALSE); + + DbgInfo(MODULE_TEST, FEATURE_CARD_INIT, NOT_TO_RAM, "Exit %s ret=%d\n", + __func__, result); + return result; +} + +/* ---------------UHS2 Test Case--------------- */ +static bool test_uhs2_dmt(bht_dev_ext_t *pdx) +{ + sd_card_t *card = &pdx->card; + sd_command_t sd_cmd; + bool hbr = (pdx->testcase.test_param1 == 0) ? 0 : 1; + + if (card->uhs2_info.uhs2_cap.hibernate == 0) + hbr = 0; + + if (card->card_type != CARD_UHS2 || card->card_present == FALSE) { + DbgErr("Test uhs2 dmt failed for not uhs2 card\n"); + return FALSE; + } + + if (uhs2_enter_dmt(card, &sd_cmd, card->host, hbr) == FALSE) + return FALSE; + + if (uhs2_resume_dmt(card, &sd_cmd, card->host, hbr) == FALSE) + return FALSE; + + return TRUE; +} + +static bool test_uhs2_fullreset(bht_dev_ext_t *pdx) +{ + sd_card_t *card = &pdx->card; + + if (card->card_type != CARD_UHS2 || card->card_present == FALSE) { + DbgErr("Test uhs2 fullreset failed for not uhs2 card\n"); + return FALSE; + } + + if (uhs2_full_reset_card(card) == FALSE) + return FALSE; + + return card_init(card, 1, TRUE); +} + +test_func_t test_with_card_array[2] = { + /* TEST CASE1 */ + { test_init_card, test_uhs2_dmt, "uhs2dmt" }, + /* TEST CASE2 */ + { test_init_card, test_uhs2_fullreset, "uhs2fullreset" }, + +}; + +test_func_t test_host_only_array[1] = { + { 0, 0, "nulltest" } + +}; + +void testcase_main(bht_dev_ext_t *pdx, byte type) +{ + u32 id = pdx->testcase.test_id; + u32 loop = 0; + u32 testloop = pdx->testcase.test_loop; + bool binfinite = (testloop) ? 0 : 1; + test_func_t *test = NULL; + + if (pdx->card.card_present == 0) { + host_init(&pdx->host); + card_stuct_uinit(&pdx->card); + PrintMsg("TEST remove card\n"); + return; + } + + if (type != pdx->testcase.test_type) + return; + + if (type == 1) + test = &test_with_card_array[id - 1]; + else + test = &test_host_only_array[id - 1]; + + if (test == NULL || test->test_prepare == NULL) { + DbgErr("Test(%d) prepare is null\n", id); + return; + } + + if (test->test_prepare(pdx) == FALSE) { + DbgErr("Test(%s) prepare failed\n", test->name); + return; + } + + PrintMsg("Test(%s) type=%d begin infinite=%d totalloop=%d\n", + test->name, type, binfinite, pdx->testcase.test_loop); + + while (os_thread_is_freeze(pdx) == FALSE) { + loop++; + PrintMsg("Test(%s) loop=%d\n", test->name, loop); + if (test->test_execute(pdx) == FALSE) { + DbgErr("Test(%s) excute failed loop=%d\n", test->name, + loop); + break; + } + + if (binfinite == 0) { + testloop--; + if (testloop == 0) + break; + } + } + + PrintMsg("Test(%s) loop=%d end\n", test->name, loop); + host_poweroff(&pdx->host, 0); +} + +void testcase_init(bht_dev_ext_t *pdx) +{ + if (pdx->testcase.test_type == 0) + return; + + pdx->testcase.test_id = pdx->cfg->test_item.test_id; + pdx->testcase.test_loop = pdx->cfg->test_item.test_loop; + pdx->testcase.test_param1 = pdx->cfg->test_item.test_param1; + pdx->testcase.test_param2 = pdx->cfg->test_item.test_param2; + PrintMsg("testid=%d param1=0x%08X param2=0x%08X\n", + pdx->testcase.test_id, pdx->testcase.test_param1, + pdx->testcase.test_param2); + + /* If test id is out of range */ + if (pdx->testcase.test_id == 0) + pdx->testcase.test_type = 0; + + /* card test */ + if (pdx->testcase.test_type == 1) { + if (pdx->testcase.test_id > + sizeof(test_with_card_array) / sizeof(test_func_t)) + pdx->testcase.test_type = 0; + + if (pdx->card.card_present) { +#if CFG_OS_LINUX + os_set_event(&pdx->os, EVENT_CARD_CHG); +#else + os_set_event(pdx, &pdx->os, EVENT_TASK_OCCUR, + TASK_CARD_CHG); +#endif + } + } + /* host only test */ + else if (pdx->testcase.test_type == 2) { + if (pdx->testcase.test_id > + sizeof(test_host_only_array) / sizeof(test_func_t)) + pdx->testcase.test_type = 0; + else { + /* todo insert test event */ + } + } +} diff --git a/drivers/scsi/bht/main/thread.c b/drivers/scsi/bht/main/thread.c new file mode 100644 index 000000000000..175a63bd02e9 --- /dev/null +++ b/drivers/scsi/bht/main/thread.c @@ -0,0 +1,530 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: thread.c + * + * Abstract: This file is used to handle thread event + * + * Version: 1.00 + * + * Author: Peter.Guo + * + * Environment: OS Independent + * + * History: + * + * 8/25/2014 Creation Peter.Guo + */ + +#include "../include/basic.h" +#include "../include/cardapi.h" +#include "../include/reqapi.h" +#include "../include/tqapi.h" +#include "../include/hostapi.h" +#include "../include/cmdhandler.h" +#include "../include/funcapi.h" +#include "funcapi.h" +#include "../include/debug.h" +#include "../include/hostvenapi.h" +#include "../linux_os/linux_scsi.h" + +/* + * Function Name: thread_init_card + * Abstract: This Function is used to by thread to init card + * + * Input: + * bht_dev_ext_t *pdx, + * bool init_err_card, for error card case need to do init or not + * bool send_bus_chg : need to send bus change or not + * int retry_num : init retry time + * + * Return value: + * TRUE: means ok + * others error + * + * Notes: + * run in thread context + */ +static bool thread_init_card(bht_dev_ext_t *pdx, bool init_err_card, + bool send_bus_chg, int retry_num) +{ + bool result = FALSE; + sd_card_t *card = &pdx->card; + + DbgInfo(MODULE_MAIN_THR, FEATURE_THREAD_TRACE, NOT_TO_RAM, + "Enter %s init_err=%d\n", __func__, init_err_card); + + /* If error card and don't reinit err_card */ + if (card->card_type == CARD_ERROR && init_err_card == FALSE) + goto exit; + + card->card_chg = FALSE; + + if (card->card_type != CARD_ERROR && card->card_type != CARD_NONE) { + if (card->state == CARD_STATE_WORKING) { + result = TRUE; + goto exit; + } + } + + /* Init card here */ + result = card_init(card, retry_num, FALSE); + + /* todo degrade mode control if init failed */ + + /* If Card Init ok and scsi last presetn is 0 and need to enable to send bus change */ + if (result == TRUE && pdx->scsi.last_present == 0 && send_bus_chg + && card->card_type != CARD_SD70) { + + DbgInfo(MODULE_MAIN_THR, FEATURE_THREAD_TRACE, NOT_TO_RAM, + "Exec Bus Change for Ins Card\n"); + + /* add for LCFC s3/s4 card change issue 20160830 */ + if ((pdx->pre_card.pre_sec_count != 0) + && ((pdx->pre_card.pre_sec_count != pdx->card.sec_count) + || + (os_memcpr + (&(pdx->pre_card.pre_cid[0]), + &(pdx->card.info.raw_cid[0]), CID_LEN) != CID_LEN)) + && (pdx->pre_card.s3s4_resume_for_card_init)) { + DbgInfo(MODULE_MAIN_THR, FEATURE_THREAD_TRACE, + NOT_TO_RAM, + "LCFC s3 s4 resume card change issue!\n"); + pdx->card.card_present = FALSE; + thread_exec_high_prio_job(pdx, os_bus_change, pdx); + /* Use 500ms timer to set the card present to TRUE and send bus change */ + pdx->auto_timer.s3reusme_cardchg_issuefix_en = TRUE; + pdx->auto_timer.s3reusme_timer_expect_cnt = 2; + pdx->auto_timer.s3reusme_timer_actual_cnt = 2; + } else { + /* callback execute successfully */ + if (thread_exec_high_prio_job(pdx, os_bus_change, pdx)) + pdx->scsi.last_present = 1; + } + pdx->pre_card.pre_sec_count = pdx->card.sec_count; + os_memcpy(&(pdx->pre_card.pre_cid[0]), + &(pdx->card.info.raw_cid[0]), CID_LEN); + + } + +exit: + + DbgInfo(MODULE_MAIN_THR, FEATURE_THREAD_TRACE, NOT_TO_RAM, + "Exit %s ret=%d\n", __func__, result); + return result; + +} + +/* + * Function Name: thread_wakeup_card + * Abstract: This Function is used to by thread to wake up card from low power state + * + * Input: + * bht_dev_ext_t *pdx, + * + * Return value: + * TRUE: means ok + * others error + * + * Notes: + * run in thread context + */ +e_req_result thread_wakeup_card(bht_dev_ext_t *pdx) +{ + e_req_result result = REQ_RESULT_OK; + sd_card_t *card = &pdx->card; + + DbgInfo(MODULE_MAIN_THR, FEATURE_RW_TRACE | FEATURE_IOCTL_TRACE, + NOT_TO_RAM, "Enter %s\n", __func__); + + if (card->card_present == FALSE) { + result = REQ_RESULT_NO_CARD; + goto exit; + } + + if (card->state == CARD_STATE_SLEEP || + card->state == CARD_STATE_DEEP_SLEEP) { + if (card_resume_sleep(card, TRUE) == FALSE) + result = REQ_RESULT_ACCESS_ERR; + } + /* If card is power off then reinit card */ + if (card->state == CARD_STATE_POWEROFF) { + if (thread_init_card(pdx, FALSE, FALSE, CARD_REINIT_RETRY) == + FALSE) { + result = REQ_RESULT_ACCESS_ERR; + goto exit; + } + } + +exit: + if (result == FALSE) + DbgErr("Wakeup card failed\n"); + + DbgInfo(MODULE_MAIN_THR, FEATURE_RW_TRACE | FEATURE_IOCTL_TRACE, + NOT_TO_RAM, "Exit %s result=%d\n", __func__, result); + return result; +} + +typedef struct { + bht_dev_ext_t *pdx; + srb_ext_t *srb_ext; +} req_io_item_t; + +static void thread_io_done(void *p_item) +{ + req_io_item_t *item = p_item; + + if (item->srb_ext->req.srb_done_cb) + item->srb_ext->req.srb_done_cb(item->pdx, item->srb_ext); + /* default handler */ + else { + if (item->pdx) + item->pdx->p_srb_ext = NULL; + } + +} + +/* + * Function Name: thread_tag_io + * Abstract: This Function is used to by thread to trigger tag io + * + * Input: + * bht_dev_ext_t *pdx, + * + * Return value: + * TRUE: means ok + * others error + * + * Notes: + * run in thread context + */ +static void thread_tag_io(bht_dev_ext_t *pdx) +{ + e_req_result result; + + DbgInfo(MODULE_MAIN_THR, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + + result = tag_queue_rw_data(pdx); + if (result == REQ_RESULT_NO_CARD || result == REQ_RESULT_ABORT) { + /* do nothing for low level api will cancel all io */ + } else if (result == REQ_RESULT_ACCESS_ERR) { + if (tq_is_empty(pdx) == FALSE) + os_set_event(&pdx->os, EVENT_TAG_IO); + } + + DbgInfo(MODULE_MAIN_THR, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); + +} + +/* + * Function Name: thread_remove_card + * Abstract: This Function is used to by thread to trigger tag io + * + * Input: + * bht_dev_ext_t *pdx, + * + * Return value: + * TRUE: means ok + * others error + * + * Notes: + * run in thread context + */ +static void thread_remove_card(bht_dev_ext_t *pdx, bool eject) +{ + + DbgInfo(MODULE_MAIN_THR, FEATURE_THREAD_TRACE, NOT_TO_RAM, + "Enter %s eject=%d\n", __func__, eject); + if (eject == FALSE) { + host_init(&pdx->host); + + card_stuct_uinit(&pdx->card); + /* host reset for all and reopen card init */ + if (pdx->scsi.last_present == 1) { + /* callback execute successfully */ + if (thread_exec_high_prio_job(pdx, os_bus_change, pdx)) + pdx->scsi.last_present = 0; + } + + /* When card removed or not present, let Hardware control the host main power. */ + hostven_main_power_ctrl(&pdx->host, FALSE); + /* todo call ltr featre */ + } + /* card is still present, and this remove is called by eject */ + + DbgInfo(MODULE_MAIN_THR, FEATURE_THREAD_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); + +} + +/* + * Function Name: thread_gen_io + * Abstract: This Function is used to by thread to trigger tag io + * + * Input: + * bht_dev_ext_t *pdx, + * + * Return value: + * TRUE: means ok + * others error + * + * Notes: + * run in thread context + */ +static void thread_gen_io(bht_dev_ext_t *pdx) +{ + e_req_result result = REQ_RESULT_OK; + srb_ext_t *srb_ext = pdx->p_srb_ext; + sd_card_t *card = &pdx->card; + req_io_item_t item; + bool ret = FALSE; + + DbgInfo(MODULE_MAIN_THR, FEATURE_IOCTL_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + if (srb_ext == NULL) + goto exit; + + if (srb_ext->req.type != REQ_TYPE_GEN_IO) { + DbgWarn(MODULE_MAIN_THR, NOT_TO_RAM, + "pdx srb_ext should be genio type\n"); + goto exit; + } + + item.pdx = pdx; + item.srb_ext = srb_ext; + os_set_dev_busy(pdx); + + switch (srb_ext->req.gen_req_t.code) { + case GEN_IO_CODE_INIT_CARD: + if (thread_init_card(pdx, FALSE, TRUE, 3) == FALSE) + result = REQ_RESULT_NO_CARD; + break; + case GEN_IO_CODE_EJECT: + /* TODO currently we can only do phy card poweroff */ + thread_remove_card(pdx, TRUE); + break; + case GEN_IO_CODE_PIORW: + result = thread_wakeup_card(pdx); + if (result == REQ_RESULT_OK) { + ret = card_piorw_data(card, srb_ext->req.gen_req_t.arg1, + srb_ext->req.gen_req_t.arg2, + srb_ext->req.data_dir, + srb_ext->req.srb_buff); + if (ret == TRUE) + break; + if (card->card_present == FALSE) + result = REQ_RESULT_NO_CARD; + else + result = REQ_RESULT_ACCESS_ERR; + } + break; + case GEN_IO_CODE_CPRM: + result = thread_wakeup_card(pdx); + if (result == REQ_RESULT_OK) { + ret = func_cprm(card, &(srb_ext->req)); + if (ret == TRUE) + break; + result = REQ_RESULT_ACCESS_ERR; + if (card->card_present == FALSE) + result = REQ_RESULT_NO_CARD; + else + result = REQ_RESULT_ACCESS_ERR; + } + break; + case GEN_IO_CODE_IO: + ret = func_io_reg(card, &(srb_ext->req)); + + break; + case GEN_IO_CODE_NSM: + result = thread_wakeup_card(pdx); + if (result == REQ_RESULT_OK) { + ret = func_nsm(card, &(srb_ext->req), pdx); + if (ret == TRUE) + break; + result = REQ_RESULT_ACCESS_ERR; + if (card->card_present == FALSE) + result = REQ_RESULT_NO_CARD; + else + result = REQ_RESULT_ACCESS_ERR; + } + break; + case GEN_IO_CODE_RECFG: + DbgInfo(MODULE_MAIN_THR, + FEATURE_DRIVER_INIT | FEATURE_IOCTL_TRACE, 0, + "Begin do Reload Cfg\n"); + func_autotimer_stop(pdx); + card_power_off(&pdx->card, FALSE); + + cfgmng_init_chipcfg(pdx->host.chip_type, pdx->cfg, TRUE); + pdx->cfg = + cfgmng_get(pdx, pdx->host.chip_type, pdx->cfg->boot_flag); + pdx->host.cfg = pdx->cfg; + + req_global_reinit(pdx); + + if (card->card_present) { + if (thread_init_card + (pdx, FALSE, FALSE, CARD_REINIT_RETRY) == FALSE) { + result = REQ_RESULT_ACCESS_ERR; + break; + } + } + + result = REQ_RESULT_OK; + break; + case GEN_IO_CODE_CSD: + + result = thread_wakeup_card(pdx); + if (result == REQ_RESULT_OK) { + + if (srb_ext->req.data_dir == DATA_DIR_OUT) { + + ret = + card_program_csd(&pdx->card, + (u8 *) srb_ext->req.srb_buff); + if (!ret) + result = REQ_RESULT_ACCESS_ERR; + + } else if (srb_ext->req.data_dir == DATA_DIR_IN) { + + ret = + card_read_csd(&pdx->card, + (u8 *) srb_ext->req.srb_buff); + if (!ret) + result = REQ_RESULT_ACCESS_ERR; + + } + + } + + break; + + default: + /* CPRM function is to do */ + break; + } + + srb_ext->req.result = result; + + /* As card is removed we dont' need to enable timer */ + if (srb_ext->req.gen_req_t.code != GEN_IO_CODE_EJECT) + thread_exec_high_prio_job(pdx, + (cb_soft_intr_t) func_autotimer_start, + pdx); + + os_set_dev_idle(pdx); + thread_exec_high_prio_job(pdx, thread_io_done, &item); + +exit: + DbgInfo(MODULE_MAIN_THR, FEATURE_IOCTL_TRACE, NOT_TO_RAM, + "Exit %s %d\n", __func__); +} + +void thread_handle_card_event(bht_dev_ext_t *pdx) +{ + if (pdx->card.card_present) { + if (thread_init_card(pdx, TRUE, TRUE, CARD_FIRST_INIT_RETRY) == + TRUE) + thread_exec_high_prio_job(pdx, (cb_soft_intr_t) + func_autotimer_start, pdx); + else if (pdx->card.card_type == CARD_SDIO) { + DbgInfo(MODULE_MAIN_THR, FEATURE_CARD_INIT, TO_RAM, + "Failed Init with SDIO\n"); + os_set_sdio_val(pdx, 1, FALSE); + } + + DbgInfo(MODULE_MAIN_THR, FEATURE_CARD_INIT, TO_RAM, + "THREAD init card\n"); + } else { + thread_remove_card(pdx, FALSE); + DbgInfo(MODULE_MAIN_THR, FEATURE_CARD_INIT, TO_RAM, + "THREAD remove card\n"); + } + pdx->pre_card.s3s4_resume_for_card_init = FALSE; + +} + +void thread_main(void *param) +{ + bht_dev_ext_t *pdx = (bht_dev_ext_t *) param; + e_event_t evt = EVENT_NONE; + os_struct *os = &pdx->os; + + for (;;) { + DbgInfo(MODULE_MAIN_THR, FEATURE_THREAD_TRACE, NOT_TO_RAM, + "thread wait evt\n"); + evt = os_wait_event(os); + os_clear_event(os, evt); + DbgInfo(MODULE_MAIN_THR, FEATURE_THREAD_TRACE, NOT_TO_RAM, + "Thr get Event=%d\n", evt); + switch (evt) { + case EVENT_CARD_CHG: + os_set_dev_busy(pdx); + if (pdx->pm_state.rtd3_en && pdx->pm_state.rtd3_entered) + break; + + if (pdx->testcase.test_type == 0 + || pdx->testcase.test_type == 3) + thread_handle_card_event(pdx); + else + /* with card test */ + testcase_main(pdx, 1); + + os_set_dev_idle(pdx); + break; + case EVENT_TERMINATE: + DbgInfo(MODULE_MAIN_THR, FEATURE_THREAD_TRACE, TO_RAM, + "terminal task\n"); + goto exit; + case EVENT_PENDING: + os->thread.pending_lock = TRUE; + DbgInfo(MODULE_MAIN_THR, FEATURE_THREAD_TRACE, TO_RAM, + "pending thread start freeze=%d\n", + os->thread.freeze); + if (os->thread.freeze) + os_wait_for_completion(pdx, + &os->thread.break_pending, 0); + DbgInfo(MODULE_MAIN_THR, FEATURE_THREAD_TRACE, TO_RAM, + "pending thread stop\n"); + break; + case EVENT_TAG_IO: + calc_thr_start(&pdx->tick); + thread_tag_io(pdx); + DbgInfo(MODULE_MAIN_THR, FEATURE_RW_TRACE, NOT_TO_RAM, + "rw evt task\n"); + break; + case EVENT_GEN_IO: + thread_gen_io(pdx); + DbgInfo(MODULE_MAIN_THR, FEATURE_IOCTL_TRACE, + NOT_TO_RAM, "io_ctl task\n"); + break; + case EVENT_RUNTIME_D3: + DbgInfo(MODULE_MAIN_THR, FEATURE_PM_TRACE, TO_RAM, + "rtd3 task\n"); + os_rtd3_req_wait_wake(pdx); + break; + case EVENT_AUTO_TIMER: + func_timer_thread(pdx); + DbgInfo(MODULE_MAIN_THR, FEATURE_THREAD_TRACE, + NOT_TO_RAM, "autoTimer task\n"); + break; + case EVENT_SDIO: + DbgInfo(MODULE_MAIN_THR, MODULE_OTHER_CARD, TO_RAM, + "sdio task\n"); + break; + + default: + break; + } + + DbgInfo(MODULE_MAIN_THR, FEATURE_THREAD_TRACE, NOT_TO_RAM, + "Thr done Event=%d\n", evt); + + } +exit: + DbgInfo(MODULE_MAIN_THR, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "exit thread\n"); +} From patchwork Fri Oct 13 08:34:49 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: liuchang_125125@163.com X-Patchwork-Id: 13420487 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id A1561CDB47E for ; Fri, 13 Oct 2023 08:50:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230263AbjJMIug (ORCPT ); Fri, 13 Oct 2023 04:50:36 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46750 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230240AbjJMIud (ORCPT ); Fri, 13 Oct 2023 04:50:33 -0400 X-Greylist: delayed 931 seconds by postgrey-1.37 at lindbergh.monkeyblade.net; Fri, 13 Oct 2023 01:50:24 PDT Received: from m15.mail.163.com (m15.mail.163.com [45.254.50.220]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id A05D5EA; Fri, 13 Oct 2023 01:50:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:Subject:Date:Message-Id:MIME-Version; bh=35qAr DAtwQvT/LvfPrT4KhzTP+NRYXhdCcMQVg0nIGU=; b=fa3XAic/lBobC9+bCt20M AAghm01yYOuf/fBUstdExNVa6og3k20WYx9u6XAA7JhxHm+HF2bSMs49o4qOXDUY 7OCO1S457qcjHyYSdEFihcmcaddc/LVlzRYHWxw147My2g0gQ0G6A9E/8lguxZO9 gqPQ8sldVT3Qxxb/uw0XHI= Received: from test-Z390-GAMING-X.bayhubtech.com (unknown [58.48.115.170]) by zwqz-smtp-mta-g0-4 (Coremail) with SMTP id _____wD3_0IsASllFHa5AQ--.10768S2; Fri, 13 Oct 2023 16:34:52 +0800 (CST) From: liuchang_125125@163.com To: jejb@linux.ibm.com, martin.petersen@oracle.com, linux-scsi@vger.kernel.org, linux-kernel@vger.kernel.org Cc: mark.tao@bayhubtech.com, shaper.liu@bayhubtech.com, thomas.hu@bayhubtech.com, chevron.li@bayhubtech.com, charl.liu@bayhubtech.com, Charl Liu Subject: [PATCH 8/9] scsi: bht: tagqueue: Add the source files related to tagqueue transfer function Date: Fri, 13 Oct 2023 16:34:49 +0800 Message-Id: <20231013083449.10364-1-liuchang_125125@163.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 X-CM-TRANSID: _____wD3_0IsASllFHa5AQ--.10768S2 X-Coremail-Antispam: 1Uf129KBjvAXoWDur1fuF1rGF1fWrW7XrW8JFb_yoWktrWxGo ZxZFsxGas8Kw18GFsYkw1UAryxXanrCFs3Ars5Cr4FvFnYy34Yga4jkayUG34fWr4rGrWD Ww4xCFnavay5tw1fn29KB7ZKAUJUUUUU529EdanIXcx71UUUUU7v73VFW2AGmfu7bjvjm3 AaLaJ3UbIYCTnIWIevJa73UjIFyTuYvj4RWrWwUUUUU X-Originating-IP: [58.48.115.170] X-CM-SenderInfo: polxux5dqjsiqsvrjki6rwjhhfrp/xtbBnx8IWVetlJKurwABst Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org From: Charl Liu 1.tagqueue: handle tagqueue work flow 2.tq_merge: implement tagqueue merge 3.tqadma2: implement ADMA2 transfer 4.tqadma3: implement ADMA3 transfer 5.tqadma_sdma_like: implement sdma_like mode 6.tqpolicy: handle tagqueue policy for transfer mode 7.tqsdma: implement SDMA transfer Signed-off-by: Charl Liu --- Change in V1: Add the source files related to tagqueue transfer function. --- drivers/scsi/bht/tagqueue/tagqueue.c | 2517 ++++++++++++++++++ drivers/scsi/bht/tagqueue/tq_merge.c | 433 +++ drivers/scsi/bht/tagqueue/tq_trans_api.h | 91 + drivers/scsi/bht/tagqueue/tq_util.h | 29 + drivers/scsi/bht/tagqueue/tqadma2.c | 821 ++++++ drivers/scsi/bht/tagqueue/tqadma3.c | 504 ++++ drivers/scsi/bht/tagqueue/tqadma_sdma_like.c | 373 +++ drivers/scsi/bht/tagqueue/tqpolicy.c | 210 ++ drivers/scsi/bht/tagqueue/tqsdma.c | 285 ++ 9 files changed, 5263 insertions(+) create mode 100644 drivers/scsi/bht/tagqueue/tagqueue.c create mode 100644 drivers/scsi/bht/tagqueue/tq_merge.c create mode 100644 drivers/scsi/bht/tagqueue/tq_trans_api.h create mode 100644 drivers/scsi/bht/tagqueue/tq_util.h create mode 100644 drivers/scsi/bht/tagqueue/tqadma2.c create mode 100644 drivers/scsi/bht/tagqueue/tqadma3.c create mode 100644 drivers/scsi/bht/tagqueue/tqadma_sdma_like.c create mode 100644 drivers/scsi/bht/tagqueue/tqpolicy.c create mode 100644 drivers/scsi/bht/tagqueue/tqsdma.c diff --git a/drivers/scsi/bht/tagqueue/tagqueue.c b/drivers/scsi/bht/tagqueue/tagqueue.c new file mode 100644 index 000000000000..24ff0d2367db --- /dev/null +++ b/drivers/scsi/bht/tagqueue/tagqueue.c @@ -0,0 +1,2517 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: tagqueue.c + * + * Abstract: handle tagqueue work flow + * + * Version: 1.00 + * + * Author: Chuanjin + * + * Environment: OS Independent + * + * History: + * + * 9/4/2014 Creation Chuanjin + */ + +#include "../include/basic.h" +#include "../include/host.h" +#include "../include/tqapi.h" +#include "../include/cardapi.h" +#include "../include/funcapi.h" +#include "../include/reqapi.h" +#include "../include/debug.h" +#include "tq_trans_api.h" +#include "../include/util.h" +#include "../include/cmdhandler.h" +#include "tq_util.h" +#include "../include/hostapi.h" + +/* + * + * Function Name: node_malloc + * + * Abstract: + * + * get one node from tag queue node pool + * + * Input: + * + * tag_queue_t *tq [in]: Pointer to the tag queue + * + * Output: + * + * None. + * + * Return value: NULL means can't get one successful + * + * Notes: must use node_mfree for release + * + * Caller: + * + */ +static node_t *node_malloc(tag_queue_t *tq) +{ + if (tq == NULL) { + DbgErr("%s tq NULL\n", __func__); + return NULL; + } + return node_list_get_one(&tq->node_pool_list); +} + +/* + * + * Function Name: node_mfree + * + * Abstract: + * + * put the node back to tag queue node pool + * + * Input: + * + * tag_queue_t *tq [in]: Pointer to the tag queue + * node_t *node [nin] : the node which need free + * + * Output: + * + * None. + * + * Return value: + * + * Notes: + * + * Caller: + * + */ +static void node_mfree(tag_queue_t *tq, node_t *node) +{ + if (node) + node_list_put_one(&tq->node_pool_list, node); + else + DbgErr("%s node NULL\n", __func__); +} + +/* + * + * Function Name: node_pool_init + * + * Abstract: + * + * init node pool + * + * Input: + * + * tag_queue_t *tq [in]: Pointer to the tag queue + * dma_desc_buf_t *dma_res [in]: system dma buffer resource + * + * Output: + * + * None. + * + * Return value: + * + * Notes: this call must call before tq_setup_dma_res() for use DMA resource + * + * Caller: + * + */ +static bool node_pool_init(PVOID pdx, tag_queue_t *tq, + dma_desc_buf_t *dma_res, bool sdma_like) +{ + u32 i = 0; + dma_desc_buf_t dma; + node_t *node = 0; + bool ret = FALSE; + + DbgInfo(MODULE_TQ_FLOW, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Enter %s sdma_like:%d\n", __func__, sdma_like); + /* check input */ + if (tq == NULL || dma_res == NULL) { + ret = FALSE; + DbgErr("%s tq or dma NULL\n", __func__); + goto exit; + } + + if ((dma_res->va == 0) || (dma_res->len == 0)) { + ret = FALSE; + DbgErr("%s dma va:%p len:%xh\n", __func__, dma_res->va, + dma_res->len); + goto exit; + } + + if (dma_res->len <= (tq->max_wq_req_size * MAX_GENERAL_DESC_TABLE_LEN)) { + ret = FALSE; + DbgErr("dma buf too small 0x%x <=(%x)\n", dma_res->len, + tq->max_wq_req_size * MAX_GENERAL_DESC_TABLE_LEN); + goto exit; + } + /* init each node general desc buf */ + dma = *dma_res; +#if CFG_OS_LINUX + os_list_init(&tq->node_pool_list); +#else + os_list_init(pdx, &tq->node_pool_list); +#endif + for (i = 0; i < tq->max_wq_req_size; i++) { + node = &tq->node[i]; + node->general_desc_tbl = dma; + node->general_desc_tbl.len = MAX_GENERAL_DESC_TABLE_LEN; + node->general_desc_tbl_img = node->general_desc_tbl; + resize_dma_buf(&dma, MAX_GENERAL_DESC_TABLE_LEN); + node_list_put_one(&tq->node_pool_list, node); + DbgInfo(MODULE_TQ_FLOW, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "general desc tbl len %x\n", + node->general_desc_tbl.len); + } + ret = TRUE; + /* update dma_res for usage */ + resize_dma_buf(dma_res, + tq->max_wq_req_size * MAX_GENERAL_DESC_TABLE_LEN); + + /* if sdma like mode enable, init each node SDMA like buf */ + if (sdma_like == TRUE) { + /* dma buf align */ + if (dma_align(dma_res, DMA_BUF_ALIGN_SIZE) == FALSE) { + DbgErr("%s sdma-like dma align failed\n", __func__); + ret = FALSE; + goto exit; + } + /* check buf enough */ + if (dma_res->len <= + tq->max_wq_req_size * MAX_SDMA_LIKE_DATA_SIZE) { + ret = FALSE; + DbgErr("dma resource too small 0x%x <(%x)\n", + dma_res->len, + tq->max_wq_req_size * MAX_SDMA_LIKE_DATA_SIZE); + goto exit; + } + /* init each node for sdma-like mode */ + dma = *dma_res; + for (i = 0; i < tq->max_wq_req_size; i++) { + node = &tq->node[i]; + node->data_tbl = dma; + node->data_tbl.len = MAX_SDMA_LIKE_DATA_SIZE; + node->data_tbl_img = node->data_tbl; + DbgInfo(MODULE_TQ_FLOW, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "data tbl len %x\n", node->data_tbl.len); + if (FALSE == + resize_dma_buf(&dma, MAX_SDMA_LIKE_DATA_SIZE)) { + DbgErr + ("node sdma-like data buf resize failed\n"); + ret = FALSE; + goto exit; + } + } + /* update dma_res for usage */ + resize_dma_buf(dma_res, + tq->max_wq_req_size * MAX_SDMA_LIKE_DATA_SIZE); + } +exit: + DbgInfo(MODULE_TQ_FLOW, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Exit %s ret:%d\n", __func__, ret); + return ret; +} + +/* + * + * Function Name: node_notify_complete + * + * Abstract: + * + * complete one node by call upper layer callback + * + * Input: + * + * bht_dev_ext_t *pdx [in]: Pointer to the bht adapter extension structure + * node_t *pnode [in]: the node which need complete + * + * Output: + * + * None. + * + * Return value: + * + * Notes: + * + * Caller: + * + */ +static int node_notify_complete(bht_dev_ext_t *pdx, node_t *pnode) +{ + srb_ext_t *psrb_ext = 0; + + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + + if (pnode && pdx) { + psrb_ext = (srb_ext_t *) pnode->psrb_ext; + if (psrb_ext && psrb_ext->req.srb_done_cb) { + if (psrb_ext->req.result == REQ_RESULT_OK) { + if (pnode->sdma_like) { + if (DATA_DIR_IN == + psrb_ext->req.data_dir) + os_memcpy(psrb_ext->req.srb_buff, + pnode->data_tbl.va, + psrb_ext->req.tag_req_t.sec_cnt + * SD_BLOCK_LEN); + } + } + + psrb_ext->req.srb_done_cb(pdx, psrb_ext); + } else { + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "%s ext%p cb%p\n", __func__, psrb_ext, + psrb_ext->req.srb_done_cb); + } + } else { + DbgErr("%s NULL %p %p\n", __func__, pnode, pdx); + } + + return 0; +} + +/* + * + * Function Name: node_mark_node_status + * + * Abstract: + * + * the group for make node request result value + * + * Input: + * + * + * node_t *pnode [in]: the node which need make request status + * + * Output: + * + * None. + * + * Return value: + * + * Notes: + * + * Caller: + * + */ +static bool node_mark_node_status(node_t *pnode, void *ctx) +{ + bool ret = TRUE; + srb_ext_t *psrb_ext = 0; + e_req_result result = REQ_RESULT_ACCESS_ERR; + + if (ctx == NULL) { + DbgErr("%s ctx null\n", __func__); + ret = FALSE; + goto exit; + } + + if (pnode) { + psrb_ext = (srb_ext_t *) pnode->psrb_ext; + result = *((e_req_result *) ctx); + if (psrb_ext) + psrb_ext->req.result = result; + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "%s mark sta:%x\n", __func__, result); + } +exit: + return ret; +} + +/* + * + * Function Name: node_list_get_one + * + * Abstract: + * + * get one node from the queue + * + * Input: + * + * list_t *p [in]: Pointer to the list + * + * + * Output: + * + * None. + * + * Return value: + * node_t *, NULL means empty queue + * + * Notes: + * + * Caller: + * + */ +node_t *node_list_get_one(list_t *p) +{ + node_t *node = 0; + list_entry *plist = 0; + + DbgInfo(MODULE_TQ_FLOW, FEATURE_FUNC_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + /* check input */ + if (p == NULL) { + DbgErr("%s:p NULL\n", __func__); + node = NULL; + goto exit; + } + /* get list entry */ + plist = os_list_locked_remove_head(p); + if (plist == NULL) { + node = NULL; + goto exit; + } + + /* sometime caller try to get empty queue, the counter no need sub if emtpy */ + if (os_atomic_read(&p->cnt)) + os_atomic_sub(&p->cnt, 1); + node = os_container_of(plist, node_t, list); +exit: + DbgInfo(MODULE_TQ_FLOW, FEATURE_FUNC_TRACE, NOT_TO_RAM, + "Exit %s ret:%x\n", __func__, node); + return node; +} + +/* + * + * Function Name: node_list_head_put_one + * + * Abstract: + * + * put the node to the queue head + * + * Input: + * + * list_t *p [in]: Pointer to the list + * node_t *pnode [in] : the node to put + * + * Output: + * + * None. + * + * Return value: + * + * Notes: + * + * Caller: + * + */ +void node_list_head_put_one(list_t *p, node_t *pnode) +{ + if (p == NULL) { + DbgErr("%s:p NULL\n", __func__); + return; + } + os_atomic_add(&p->cnt, 1); + os_list_locked_insert_head(p, &pnode->list); +} + +/* + * + * Function Name: node_list_put_one + * + * Abstract: + * + * put the node to the queue tail + * + * Input: + * + * list_t *p [in]: Pointer to the list + * node_t *pnode [in] : the node to put + * + * Output: + * + * None. + * + * Return value: + * + * Notes: + * + * Caller: + * + */ +void node_list_put_one(list_t *p, node_t *pnode) +{ + if (p == NULL) { + DbgErr("%s:p NULL\n", __func__); + return; + } + os_atomic_add(&p->cnt, 1); + os_list_locked_insert_tail(p, &pnode->list); +} + +/* + * + * Function Name: node_list_get_cnt + * + * Abstract: + * + * get the counter of the queue + * + * Input: + * + * list_t *p [in]: Pointer to the list + * + * Output: + * + * None. + * + * Return value: the counter value. + * + * Notes: + * + * Caller: + * + */ +u32 node_list_get_cnt(list_t *p) +{ + if (p == NULL) { + DbgErr("%s:p NULL\n", __func__); + return 0; + } + return os_atomic_read(&p->cnt); +} + +/* + * + * Function Name: req_queue_loop_ctx_ops + * + * Abstract: + * + * do operation for all noed of the queue. + * + * Input: + * + * tag_queue_t *tq [in]: Pointer to the tag queue + * req_queue_node_ops_cb cb [in]: the operation callback + * + * Output: + * + * None. + * + * Return value: + * + * Notes: + * + * Caller: + * + */ +bool req_queue_loop_ctx_ops(req_queue_t *p, req_queue_node_ops_ctx_cb cb, + void *ctx) +{ + bool ret = TRUE; + node_t *pnode = 0; + u32 i = 0, qsize = 0; + + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + /* check input */ + if ((cb == NULL) || (p == NULL)) { + DbgErr("% NULL\n", __func__); + ret = FALSE; + goto exit; + } + /* check empty queue */ + if (node_list_get_cnt(&p->list) == 0) { + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "%s nobody in queue\n", __func__); + goto exit; + } + + /* do loops ops */ + qsize = node_list_get_cnt(&p->list); + for (i = 0; i < qsize; i++) { + /* get one node */ + pnode = node_list_get_one(&p->list); + if (pnode == NULL) + break; + if ((*cb) (pnode, ctx) == FALSE) + ret = FALSE; + /* put back to queue */ + node_list_put_one(&p->list, pnode); + } + /* check cnt */ + if (i != qsize) + DbgErr("%s:size no equal(%d-%d)\n", __func__, i, qsize); +exit: + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s %d\n", + __func__, ret); + return ret; +} + +/* + * + * Function Name: req_queue_init + * + * Abstract: + * + * init the request queue + * + * Input: + * + * req_queue_t *p [in]: the queue need init + * + * + * Output: + * + * None. + * + * Return value: + * TRUE: means ok + * + * Notes: + * + * Caller: + * + */ +static bool req_queue_init(PVOID pdx, req_queue_t *p, u32 id) +{ + if (p == NULL) { + DbgErr("%s:p NULL\n", __func__); + return FALSE; + } +#if CFG_OS_LINUX + os_list_init(&p->list); +#else + os_list_init(pdx, &p->list); +#endif + p->id = id; + return TRUE; +} + +/* + * + * Function Name: req_queue_mark_node_status + * + * Abstract: + * + * setup tag queue dma resource. + * + * Input: + * + * req_queue_t *p [in]: Pointer to the request queue + * e_req_result [in]: the mark request status value for queue + * + * Output: + * + * None. + * + * Return value: + * + * Notes: + * + * Caller: + * + */ +static void req_queue_mark_node_status(req_queue_t *p, e_req_result result) +{ + req_queue_loop_ctx_ops(p, node_mark_node_status, &result); +} + +static bool req_queue_move_node(req_queue_t *dq, req_queue_t *sq) +{ + bool ret = FALSE; + node_t *pnode = 0; + + if ((dq == NULL) || (sq == NULL)) { + DbgErr("%s null\n", __func__); + ret = FALSE; + goto exit; + } + + /* put request queue */ + for (;;) { + pnode = node_list_get_one(&sq->list); + if (pnode == NULL) + break; + node_list_head_put_one(&dq->list, pnode); + } + +exit: + return ret; +} + +bool req_queue_reverse_queue(PVOID pdx, req_queue_t *q) +{ + list_t tlist; + bool ret = TRUE; + u32 qsize = 0; + node_t *pnode = 0; + + if (q == NULL) { + DbgErr("%s null\n", __func__); + ret = FALSE; + goto exit; + } + /* init temp list */ +#if CFG_OS_LINUX + os_list_init(&tlist); +#else + os_list_init(pdx, &tlist); +#endif + + qsize = node_list_get_cnt(&q->list); + + for (;;) { + pnode = node_list_get_one(&q->list); + if (pnode == NULL) + break; + node_list_head_put_one(&tlist, pnode); + } + + for (;;) { + pnode = node_list_get_one(&tlist); + if (pnode == NULL) + break; + node_list_put_one(&q->list, pnode); + } + + if (qsize != node_list_get_cnt(&q->list)) { + DbgErr("%s:size no equal(%d-%d)\n", __func__, qsize); + ret = FALSE; + } +exit: + return ret; + +} + +static bool req_queue_move_order_node_at_head(PVOID pdx, req_queue_t *dq, + req_queue_t *sq) +{ + bool ret = FALSE; + node_t *pnode = 0; + + if ((dq == NULL) || (sq == NULL)) { + DbgErr("%s null\n", __func__); + ret = FALSE; + goto exit; + } + + ret = req_queue_reverse_queue(pdx, sq); + if (ret == FALSE) + DbgErr("%s: reverse failed)\n", __func__); + + /* put request queue */ + for (;;) { + pnode = node_list_get_one(&sq->list); + if (pnode == NULL) + break; + node_list_head_put_one(&dq->list, pnode); + } + +exit: + return ret; +} + +static e_tq_state work_queue_get_state(req_queue_t *rq) +{ + return rq->state; +} + +/* only state machine API can change states, other only can get */ +static bool work_queue_state_machine(req_queue_t *rq, e_tq_state_evt evt) +{ + bool ret = TRUE; + + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "Enter %s (%d)%x %x\n", __func__, rq->id, rq->state, evt); + + switch (evt) { + case QUEUE_EVT_GET: + { + /* pre-condition */ + if (rq->state == QUEUE_STATE_IDLE) { + rq->state = QUEUE_STATE_BUILD; + } else { + DbgErr("queue state not idle\n"); + ret = FALSE; + goto exit; + } + } + break; + case QUEUE_EVT_BUILD_OK: + { + /* pre-condition */ + if (rq->state == QUEUE_STATE_BUILD) { + rq->state = QUEUE_STATE_READY; + } else { + DbgErr("queue state not build\n"); + ret = FALSE; + goto exit; + } + } + break; + case QUEUE_EVT_BUILD_FAILED: + { + /* pre-condition */ + if (rq->state == QUEUE_STATE_BUILD) { + rq->state = QUEUE_STATE_IDLE; + } else { + DbgErr("queue state not build\n"); + ret = FALSE; + goto exit; + } + } + break; + case QUEUE_EVT_ISSUE: + { + + /* pre-condition */ + if (rq->state == QUEUE_STATE_READY) { + rq->state = QUEUE_STATE_WAIT_CPL; + } else { + DbgErr("queue state not ready\n"); + ret = FALSE; + goto exit; + } + } + break; + case QUEUE_EVT_DONE: + { + + /* pre-condition */ + if (rq->state == QUEUE_STATE_WAIT_CPL) { + rq->state = QUEUE_STATE_IDLE; + } else { + DbgErr("queue state not wait complete\n"); + ret = FALSE; + goto exit; + } + } + break; + case QUEUE_EVT_FORCE_FAILED: + { + rq->state = QUEUE_STATE_WAIT_CPL; + } + break; + case QUEUE_EVT_INIT: + { + rq->state = QUEUE_STATE_IDLE; + } + break; + case QUEUE_EVT_ABORT: + { + /* pre-condition */ + if (rq->state == QUEUE_STATE_READY + || rq->state == QUEUE_STATE_WAIT_CPL) { + rq->state = QUEUE_STATE_IDLE; + } else { + DbgErr + ("queue state not ready or wait complete\n"); + ret = FALSE; + goto exit; + } + } + break; + default: + DbgErr("%s state error %d\n", __func__, rq->state); + } +exit: + + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "Exit %s (%d)%x %x\n", __func__, rq->id, rq->state, evt); + return ret; +} + +/* side effect: dma will change size by use length */ +static bool work_queue_init_adma3_dma_res(req_queue_t *rq, + dma_desc_buf_t *dma, u32 len) +{ + bool ret = FALSE; + /* check input */ + if (dma->len < len) { + DbgErr("%s dma res len too small %x\n", __func__, dma->len); + ret = FALSE; + goto exit; + } + /* assign */ + rq->adma3_integrate_tbl = *dma; + rq->adma3_integrate_tbl.len = len; + resize_dma_buf(dma, len); + DbgInfo(MODULE_TQ_DMA, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "adma3 integrate tb len %x pa(%x)\n", len, + os_get_phy_addr32l(rq->adma3_integrate_tbl.pa)); + +exit: + return ret; + +} + +typedef struct { + dma_desc_buf_t *desc_buf; + u32 len; +} work_queue_init_adma3_arg; + +static bool work_queue_init_adma3_res_cb(PVOID pdx, req_queue_t *rq, void *ctx) +{ + bool ret = FALSE; + work_queue_init_adma3_arg *arg = ctx; + dma_desc_buf_t *dma = arg->desc_buf; + + ret = work_queue_init_adma3_dma_res(rq, dma, arg->len); + if (ret == FALSE) + return FALSE; + else + return TRUE; +} + +static bool work_queue_init_cb(PVOID pdx, req_queue_t *rq, void *ctx) +{ + tag_queue_t *tq = ctx; + + req_queue_init(pdx, rq, tq->queue_id_seed++); + work_queue_state_machine(rq, QUEUE_EVT_INIT); + return FALSE; +} + +/* + * static bool work_queue_dump_state(PVOID pdx, req_queue_t *rq, void *ctx) + * { + * DbgErr("wq %d state(%d) cnt:%d\n", rq->id, rq->state, + * node_list_get_cnt(&rq->list)); + * return FALSE; + * } + */ + +static bool work_queue_find_spec_state_queue_cb(PVOID pdx, req_queue_t *rq, + void *ctx) +{ + e_tq_state *pstate = (e_tq_state *) ctx; + + if (ctx == NULL) { + DbgErr("%s null ctx\n", ctx); + return FALSE; + } + if (*pstate == work_queue_get_state(rq)) + return TRUE; + else + return FALSE; +} + +static bool work_queue_reset_state_cb(PVOID pdx, req_queue_t *rq, void *ctx) +{ + work_queue_state_machine(rq, QUEUE_EVT_INIT); + return FALSE; +} + +typedef bool (*work_queue_ops_cb)(PVOID pdx, req_queue_t *rq, void *ctx); + +static req_queue_t *tq_work_queue_find_ops(void *pdx, tag_queue_t *ptq, + work_queue_ops_cb pred, void *ctx) +{ + req_queue_t *rq = NULL, *retq = NULL; + u32 i = 0; + + for (i = 0; i < TQ_WORK_QUEUE_SIZE; i++) { + rq = &ptq->work_queues[i]; + if (pred((PVOID) pdx, rq, ctx) == TRUE) { + retq = rq; + break; + } + } + return retq; +} + +/* + * + * Function Name: tq_work_queue_init_adma3_res + * + * Abstract: + * + * setup tag queue dma resource. + * + * Input: + * + * bht_dev_ext_t *pdx [in]: Pointer to the bht adapter extension structure + * dma_desc_buf_t *desc_buf [in]: the system DMA buffer resource + * + * Output: + * + * None. + * + * Return value: + * + * Notes: the caller must set request status before call this function. + * + * Caller: + * + */ + +static bool tq_work_queue_init_adma3_res(PVOID pdx, tag_queue_t *ptq, + dma_desc_buf_t *desc_buf) +{ + bool ret = FALSE; + + work_queue_init_adma3_arg arg; + + DbgInfo(MODULE_TQ_FLOW, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + /* check input parameters */ + if ((ptq == NULL) || (desc_buf == NULL)) { + ret = FALSE; + DbgErr("%s tq or dma NULL\n", __func__); + goto exit; + } + if ((desc_buf->va == 0) || (desc_buf->len == 0)) { + ret = FALSE; + DbgErr("%s dma va:%p len:%xh\n", __func__, desc_buf->va, + desc_buf->len); + goto exit; + } + + /* adma3 integrate table resource init */ + arg.desc_buf = desc_buf; + arg.len = + MAX_ADMA3_INTERGATE_TABLE_LEN_PER_QUEUE_PER_NODE * + ptq->max_wq_req_size; + if (NULL != + tq_work_queue_find_ops(pdx, ptq, work_queue_init_adma3_res_cb, + &arg)) { + DbgErr("%s amd3 wq init res failed\n", __func__); + ret = FALSE; + goto exit; + } + + ret = TRUE; +exit: + DbgInfo(MODULE_TQ_FLOW, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Exit %s ret:%d\n", __func__, ret); + return ret; + +} + +static bool dma_need_integrate_desc_buffer(u32 dma_mode) +{ + if ((dma_mode == CFG_TRANS_MODE_ADMA3_SDMA_LIKE) || + (dma_mode == CFG_TRANS_MODE_ADMA3) || + (dma_mode == CFG_TRANS_MODE_ADMA_MIX_SDMA_LIKE) || + (dma_mode == CFG_TRANS_MODE_ADMA_MIX)) + return TRUE; + else + return FALSE; + +} + +static bool tq_work_queue_init(PVOID pdx, tag_queue_t *tq, + dma_desc_buf_t *desc_buf, u32 dma_mode) +{ + bool ret = TRUE; + /* init basic work queue */ + tq_work_queue_find_ops(pdx, tq, work_queue_init_cb, tq); + /* ADMA3 integrate descriptor table res need */ + if (dma_need_integrate_desc_buffer(dma_mode) == TRUE) { + ret = tq_work_queue_init_adma3_res(pdx, tq, desc_buf); + if (ret == FALSE) + DbgErr("%s dma setup failed\n", __func__); + } + + return ret; +} + +static bool tq_work_queue_reset_state(PVOID pdx, tag_queue_t *tq) +{ + bool ret = TRUE; + /* init basic work queue */ + tq_work_queue_find_ops(pdx, tq, work_queue_reset_state_cb, 0); + + return ret; +} + +/* + * + * Function Name: tq_work_queue_complete_cur_wq + * + * Abstract: + * + * complete all requsts which in cur work queue. + * + * Input: + * + * bht_dev_ext_t *pdx [in]: Pointer to the bht adapter extension structure + * + * + * Output: + * + * None. + * + * Return value: + * + * Notes: the caller must set request status before call this function. + * + * Caller: + * + */ +static void tq_work_queue_complete_cur_wq(bht_dev_ext_t *pdx) +{ + tag_queue_t *ptq = &pdx->tag_queue; + req_queue_t *pwq = ptq->wq_cur; + node_t *pnode = 0; + + if (pwq == NULL) { + DbgErr("%s wq cur is NULL\n", __func__); + goto exit; + } + + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "Enter %s tq have(%d)\n", __func__, + os_atomic_read(&ptq->req_cnt)); + + if (pwq->state != QUEUE_STATE_WAIT_CPL) { + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "try to complete non-wait-cpl queue\n"); + } + + if (os_atomic_read(&ptq->req_cnt) == 0) { + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "try to complete empty queue\n"); + } + + for (;;) { + pnode = node_list_get_one(&pwq->list); + + if (pnode == NULL) { + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "Exit %s tq have(%d)\n", __func__, + os_atomic_read(&ptq->req_cnt)); + goto exit; + } + + if (os_atomic_read(&ptq->req_cnt)) + os_atomic_sub(&(ptq->req_cnt), 1); + else + DbgErr("tq complete req but cnt 0\n"); + node_notify_complete(pdx, pnode); + /* free node memory to pool */ + node_mfree(ptq, pnode); + } +exit: + + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "Exit %s tq have(%d)\n", __func__, + os_atomic_read(&ptq->req_cnt)); +} + +/* + * + * Function Name: tq_work_queue_move_update_node_status + * + * Abstract: + * + * update request status in tag queue. + * + * Input: + * + * bht_dev_ext_t *pdx [in]: Pointer to the bht adapter extension structure + * e_req_result req_status [in]: the complete status + * bool queue_select_all [in]: select all request or just work queue + * + * Output: + * + * None. + * + * Return value: + * + * Notes: + * + * Caller: + * + */ + +static bool tq_work_queue_move_node_to_cur_wq_cb(PVOID pdx, req_queue_t *rq, + void *ctx) +{ + tag_queue_t *tq = (tag_queue_t *) ctx; + + if (tq->wq_cur == NULL) { + DbgErr("%s cur wq null\n", __func__); + goto exit; + } + if (tq->wq_cur != rq) { + if (work_queue_get_state(rq) == QUEUE_STATE_READY) { + req_queue_move_node(tq->wq_cur, rq); + /* update queue state to idle */ + work_queue_state_machine(rq, QUEUE_EVT_ABORT); + } + } + +exit: + + return FALSE; +} + +/* + * + * Function Name: tq_work_queue_thread_io_done + * + * Abstract: + * + * a wapper function for complete request. + * + * Input: + * + * bht_dev_ext_t *pdx [in]: Pointer to the bht adapter extension structure + * Output: + * + * None. + * + * Return value: + * + * Notes: + * + * Caller: + * + */ +static void tq_work_queue_thread_io_done(void *p) +{ + tq_work_queue_complete_cur_wq(p); +} + +/* + * static void tq_work_queue_dump_state(PVOID pdx, tag_queue_t *tq) + * { + * if (tq->wq_cur) + * DbgErr("wq cur (%d)\n", tq->wq_cur->id, tq->wq_cur->state); + * tq_work_queue_find_ops(pdx, tq, work_queue_dump_state, 0); + * } + */ + +static bool tq_work_queue_rollback_node_cb(PVOID pdx, req_queue_t *rq, + void *ctx) +{ + tag_queue_t *tq = (tag_queue_t *) ctx; + + req_queue_move_node(&tq->req_queue, rq); + /* loop all */ + return FALSE; +} + +static void tq_work_queue_rollback_all_node(PVOID pdx, tag_queue_t *tq) +{ + tq_work_queue_find_ops(pdx, tq, tq_work_queue_rollback_node_cb, tq); + +} + +static void tq_all_queue_move_node_to_cur_wq(bht_dev_ext_t *pdx) +{ + tag_queue_t *ptq = &pdx->tag_queue; + + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* check cur wq */ + if (ptq->wq_cur == NULL) { + DbgErr("%s wq cur is NULL\n", __func__); + goto exit; + } + + /* put request queue io to cur_wq */ + req_queue_move_node(ptq->wq_cur, &ptq->req_queue); + + /* put left queue io to cur wq */ + tq_work_queue_find_ops((PVOID) pdx, ptq, + tq_work_queue_move_node_to_cur_wq_cb, ptq); + +exit: + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +/* + * + * Function Name: tq_thread_failed_queue + * + * Abstract: + * + * tag queue use this function for complete request in thread context. + * + * Input: + * + * bht_dev_ext_t *pdx [in]: Pointer to the bht adapter extension structure + * e_req_result req_status [in]: the complete status + * bool queue_select_all [in]: select all request or just work queue + * Output: + * + * None. + * + * Return value: + * + * Notes: + * + * Caller: + * + */ + +static void tq_thread_failed_queue(bht_dev_ext_t *pdx, e_req_result req_status, + bool queue_select_all) +{ + tag_queue_t *ptq = &pdx->tag_queue; + + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "Enter %s sta:%d select:%d\n", __func__, req_status, + queue_select_all); + /* check cur wq */ + if (ptq->wq_cur == NULL) { + DbgErr("%s wq cur is NULL, so force assign a cur wq\n", + __func__); + ptq->wq_cur = &ptq->work_queues[0]; + } + if (queue_select_all == TRUE) + tq_all_queue_move_node_to_cur_wq(pdx); + + /* mark status */ + req_queue_mark_node_status(ptq->wq_cur, req_status); + + /* check status */ + work_queue_state_machine(ptq->wq_cur, QUEUE_EVT_FORCE_FAILED); + + /* check empty */ + if (node_list_get_cnt(&ptq->wq_cur->list) != 0) { + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "%s %d\n", + __func__, ptq->req_cnt); + /* complete node to OS */ + thread_exec_high_prio_job(pdx, tq_work_queue_thread_io_done, + pdx); + } else { + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "%s empty %d\n", __func__, ptq->req_cnt); + } + + /* update to idle */ + work_queue_state_machine(ptq->wq_cur, QUEUE_EVT_DONE); + + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +bool tq_get_hardware_idle_state(tag_queue_t *ptq) +{ + return (ptq->hw_idle == TRUE) ? TRUE : FALSE; +} + +void tq_set_hardware_idle_state(tag_queue_t *ptq, bool idle) +{ + ptq->hw_idle = idle; +} + +bool tq_wait_stratege(req_queue_t *q) +{ + return TRUE; +} + +/* get a idle trans ctx for next build */ +static req_queue_t *tq_trans_ctx_get_spec_one(PVOID pdx, tag_queue_t *ptq, + e_tq_state sta) +{ + req_queue_t *rq = NULL; + + rq = tq_work_queue_find_ops(pdx, ptq, + work_queue_find_spec_state_queue_cb, &sta); + + return rq; +} + +static req_queue_t *tq_trans_ctx_get_idle_one(PVOID pdx, tag_queue_t *ptq) +{ + req_queue_t *rq = NULL; + + rq = tq_trans_ctx_get_spec_one(pdx, ptq, QUEUE_STATE_IDLE); + if (rq != NULL) + work_queue_state_machine(rq, QUEUE_EVT_GET); + return rq; +} + +static req_queue_t *tq_trans_ctx_get_ready_one(PVOID pdx, tag_queue_t *ptq) +{ + req_queue_t *rq = NULL; + + rq = tq_trans_ctx_get_spec_one(pdx, ptq, QUEUE_STATE_READY); + return rq; +} + +static e_build_ctx_result tq_trans_ctx_build_only(bht_dev_ext_t *pdx, + tag_queue_t *ptq, + req_queue_t *build_queue) +{ + node_t *pnode = 0; + u32 i = 0; + e_build_ctx_result ret = TQ_BUILD_IO_ERROR; + /* when build ctx maybe host transfer data for current queue */ + ptq->wq_build = build_queue; + /* check */ + if (work_queue_get_state(build_queue) != QUEUE_STATE_BUILD) { + DbgErr("TQ queue state error\n"); + ret = TQ_BUILD_IO_ERROR; + goto exit; + } + + /* 1. init context one transfer. */ + if (ptq->ops.init_io) { + if (ptq->ops.init_io(pdx) == FALSE) { + DbgErr("Tag Queue InitIo failed\n"); + ret = TQ_BUILD_IO_ERROR; + goto exit; + } + } + /* 2. build IOs context stage */ + + for (i = 0; i < ptq->max_build_limit; i++) { + /* 2.1 get one node from request queue list */ + pnode = node_list_get_one(&ptq->req_queue.list); + if (pnode == NULL) { + if (i == 0) { + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, + NOT_TO_RAM, + "no request in request queue\n"); + /* can't got any node, so exit. */ + ret = TQ_BUILD_IO_EMPTY; + goto exit; + } + /* can't fetch anything more node, so goto issue stage. */ + break; + } + /* 2.2 build one IO context for the node */ + /* prebuild io */ + if (ptq->ops.prebuild_io) { + if (ptq->ops.prebuild_io(pdx, pnode) == FALSE) + goto build_error_handle; + } + + if (ptq->ops.build_io) { + + if (ptq->ops.build_io(pdx, pnode) == FALSE) { + /* + * put this node backto request queue. + * 1.for ADMA2 merge case:no the continuous SRB + * 2.for ADMA3 maybe no enough buf resource,so must put it back + */ +build_error_handle: + node_list_head_put_one(&ptq->req_queue.list, + pnode); + /* check empty condition */ + if (node_list_get_cnt(&build_queue->list) == 0) { + DbgErr + ("Tag Queue transfer queue empty\n"); + /* abort */ + ret = TQ_BUILD_IO_ERROR; + goto exit; + } + /* goto issue stage */ + break; + } + } + node_list_put_one(&build_queue->list, pnode); + + /* 2.3 TQ policy need break. */ + if (tag_queue_policy_break(pdx) == TRUE) { + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "%s TQ policy break\n", __func__); + break; + } + /* hardware idle */ + if (tq_get_hardware_idle_state(ptq) == TRUE) { + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "%s HW idle break\n", __func__); + break; + } + } + + ret = TQ_BUILD_IO_OK; +exit: + return ret; +} + +static e_build_ctx_result tq_trans_ctx_build(bht_dev_ext_t *pdx, + tag_queue_t *ptq, + req_queue_t *build_queue) +{ + e_build_ctx_result ret = TQ_BUILD_IO_ERROR; + /* build */ + ret = tq_trans_ctx_build_only(pdx, ptq, build_queue); + /* update state */ + if (ret == TQ_BUILD_IO_OK) + work_queue_state_machine(build_queue, QUEUE_EVT_BUILD_OK); + else + work_queue_state_machine(build_queue, QUEUE_EVT_BUILD_FAILED); + return ret; +} + +static bool tq_trans_ctx_rebuild(bht_dev_ext_t *pdx, tag_queue_t *tq) +{ + bool ret = FALSE; + + /* put all build ios back to request queue */ + req_queue_move_order_node_at_head((PVOID) pdx, &tq->req_queue, + tq->wq_cur); + work_queue_state_machine(tq->wq_cur, QUEUE_EVT_ABORT); + /* get idle ctx */ + tq->wq_cur = tq_trans_ctx_get_idle_one((PVOID) pdx, tq); + if (tq->wq_cur == NULL) { + ret = FALSE; + DbgErr("%s null wq_cur\n", __func__); + goto exit; + } + /* rebuild ctx */ + if (tq_trans_ctx_build(pdx, tq, tq->wq_cur) != TQ_BUILD_IO_OK) { + /* empty request queue or build failed */ + ret = FALSE; + DbgErr("%s build failed\n", __func__); + goto exit; + } + /* update */ + work_queue_state_machine(tq->wq_cur, QUEUE_EVT_ISSUE); + ret = TRUE; +exit: + return ret; +} + +static bool tq_trans_ctx_rebuild_node_cb(node_t *pnode, void *ctx) +{ + bool ret = TRUE; + bht_dev_ext_t *pdx = ctx; + tag_queue_t *ptq = &pdx->tag_queue; + + /* build one IO context for the node */ + + if (ptq->ops.prebuild_io) { + if (ptq->ops.prebuild_io(pdx, pnode) == FALSE) { + ret = FALSE; + goto exit; + } + } + + if (ptq->ops.build_io) { + + if (ptq->ops.build_io(pdx, pnode) == FALSE) { + ret = FALSE; + goto exit; + } + } +exit: + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s %d\n", + __func__, ret); + return ret; +} + +static bool tq_trans_ctx_rebuild_each_node(bht_dev_ext_t *pdx, + tag_queue_t *ptq, + req_queue_t *build_queue) +{ + bool ret = TRUE; + + ptq->wq_build = build_queue; + + /* 1. init context one transfer. */ + if (ptq->ops.init_io) { + if (ptq->ops.init_io(pdx) == FALSE) { + DbgErr("Tag Queue InitIo failed\n"); + ret = FALSE; + goto exit; + } + } + + ret = + req_queue_loop_ctx_ops(ptq->wq_cur, tq_trans_ctx_rebuild_node_cb, + pdx); + + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s %d\n", + __func__, ret); + +exit: + return ret; +} + +bool tq_dma_mode_switch(bht_dev_ext_t *pdx, tag_queue_t *tq) +{ + bool ret = FALSE; + u32 target_dma_mode = os_atomic_read(&tq->target_dma_mode); + + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + /* need switch */ + if (target_dma_mode == tq->cur_dma_mode) { + ret = TRUE; + goto exit; + } + + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + " %s switch(%d,%d)\n", __func__, target_dma_mode, + tq->cur_dma_mode); + /* call each mode clean ops */ + if (tq->ops.unload) { + ret = tq->ops.unload(pdx); + /* need error recovery,switch failed. */ + if (ret == FALSE) { + DbgErr("%s unload failed\n", __func__); + ret = FALSE; + goto exit; + } + } + /* config new mode */ + os_memset(&tq->ops, 0, sizeof(transfer_cb_t)); + switch (target_dma_mode) { + case CFG_TRANS_MODE_ADMA3: + tq_adma3_mode_init(&tq->ops); + break; + case CFG_TRANS_MODE_ADMA2: + tq_adma2_inf_mode_init(&tq->ops); + break; + case CFG_TRANS_MODE_ADMA2_SDMA_LIKE: + tq_adma2_inf_sdmalike_mode_init(&tq->ops); + break; + case CFG_TRANS_MODE_ADMA3_SDMA_LIKE: + tq_adma3_sdmalike_mode_init(&tq->ops); + break; + case CFG_TRANS_MODE_ADMA2_ONLY: + host_transfer_init(&pdx->host, FALSE, TRUE); + tq_adma2_mode_init(&tq->ops); + break; + case CFG_TRANS_MODE_ADMA2_ONLY_SDMA_LIKE: + tq_adma2_sdmalike_mode_init(&tq->ops); + break; + default: + DbgErr("error dma mode\n"); + } + tq->cur_dma_mode = target_dma_mode; + /* rebuild io */ + tq_trans_ctx_rebuild(pdx, tq); + + ret = TRUE; +exit: + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); + return ret; +} + +static bool tq_dma_support_mix_mode(u32 dma_mode) +{ + + if ((dma_mode == CFG_TRANS_MODE_ADMA_MIX) || + (dma_mode == CFG_TRANS_MODE_ADMA_MIX_SDMA_LIKE)) + return TRUE; + else + return FALSE; + +} + +u32 tq_issue_post_cb(void *p) +{ + bht_dev_ext_t *pdx = p; + tag_queue_t *ptq = &pdx->tag_queue; + req_queue_t *build_queue = NULL; + + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + + tq_wait_stratege(ptq->wq_cur); + + /* try build it now */ + if (tq_trans_ctx_get_ready_one((PVOID) pdx, ptq) == NULL) { + build_queue = tq_trans_ctx_get_idle_one((PVOID) pdx, ptq); + if (build_queue != NULL) { + if (ptq->wq_cur == build_queue) { + DbgErr("%s wq_cur == build_queue\n", + __func__); + goto exit; + + } + if (TQ_BUILD_IO_OK != + tq_trans_ctx_build(pdx, ptq, build_queue)) { + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, + NOT_TO_RAM, "not build trans ctx\n"); + } else { + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, + NOT_TO_RAM, "build trans ctx ok\n"); + } + } + } +exit: + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); + return 0; +} + +/* + * + * Function Name: tag_queue_rw_data_issue_stage + * + * Abstract: + * + * issue cmd for tag_queue_rw_data() + * + * Input: + * + * bht_dev_ext_t *pdx [in]: Pointer to the bht adapter extension structure + * Output: + * + * None. + * + * Return value: + * + * Notes: + * + * Caller: only for tag_queue_rw_data() + * + */ + +e_req_result tag_queue_rw_data_issue_stage(bht_dev_ext_t *pdx, + tag_queue_t *ptq, sd_card_t *card, + host_cmd_req_t *irq) +{ + /* cfg retry times */ + int retry = TQ_MAX_RECOVERY_ERROR_RETRY_TIMES; + sd_command_t *sd_cmd = 0; + e_req_result ret = REQ_RESULT_OK; + bool card_poweroff_flg = TRUE; + + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* check cur wq */ + if (ptq->wq_cur == NULL) { + ret = REQ_RESULT_ABORT; + DbgErr("%s wq cur is NULL, so exit\n", __func__); + goto exit; + } + + /* update */ + work_queue_state_machine(ptq->wq_cur, QUEUE_EVT_ISSUE); + + os_set_dev_busy(pdx); + card_poweroff_flg = card_is_poweroff(&pdx->card); + ret = thread_wakeup_card(pdx); + if (ret != REQ_RESULT_OK) { + DbgErr("Tag Queue wakeup card failed\n"); + tq_thread_failed_queue(pdx, REQ_RESULT_ACCESS_ERR, TRUE); + ret = REQ_RESULT_ACCESS_ERR; + goto exit; + } + + if (card_poweroff_flg == TRUE) { + /* + * for ADMA3 case, if card poweroff, adma3 dma ctx may invalid, + * a) card init may degrade from uhs2 to uhs1. + * b) card power, the cmd descriptor may also build wrong. + */ + if (ptq->ops.poweroff_need_rebuild) { + if (ptq->ops.poweroff_need_rebuild(pdx) == TRUE) { + /* maybe card mode change during error recovery */ + if (FALSE == + tq_trans_ctx_rebuild_each_node(pdx, ptq, + ptq->wq_cur)) { + DbgErr("Tag Queue rebuild failed\n"); + ret = REQ_RESULT_ACCESS_ERR; + tq_thread_failed_queue(pdx, + REQ_RESULT_ACCESS_ERR, + TRUE); + goto exit; + } + } + } + } + + if (func_thermal_control(card) == FALSE) { + if (card_recovery_flow(card, NULL) == FALSE) { + DbgErr("%s thremal control recover failed\n", + __func__); + /* can't handle anything */ + tq_thread_failed_queue(pdx, REQ_RESULT_ACCESS_ERR, + TRUE); + ret = REQ_RESULT_ACCESS_ERR; + goto exit; + } + } + + /* 3.1 issue transfer */ + + if (TRUE == + tq_dma_support_mix_mode(pdx->cfg->host_item.test_dma_mode_setting.dma_mode)) { + ret = tq_dma_mode_switch(pdx, ptq); + if (ret == FALSE) + goto error_handle; + } +tq_trans_again: + if (os_thread_is_freeze(pdx) || card->card_type == CARD_ERROR) { + tq_thread_failed_queue(pdx, REQ_RESULT_ABORT, TRUE); + ret = REQ_RESULT_ABORT; + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "%s card err or thread freeze\n", __func__); + goto final; + } + + if (card->info.scr.sd_spec <= SCR_SPEC_VER_1) { + /* + * For SD1.1/SD1.0 card, need add delay between two + * read/write command when using infinite transfer mode + */ + os_udelay(100); + } + + /* hardware idle not sync with queue status */ + tq_set_hardware_idle_state(&pdx->tag_queue, FALSE); + retry--; + if (ptq->ops.issue_transfer(pdx) == FALSE) { + /* error recovery flow */ +error_handle: + DbgErr("Error Recovery for ReadWrite\n"); + sd_cmd = irq->private; + ret = card_recovery_flow(card, sd_cmd); + if (ret == REQ_RESULT_OK) { + if (retry > 0) { + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, + NOT_TO_RAM, "%s TQ retry %d\n", + __func__, retry); + /* maybe card mode change during error recovery */ + if (FALSE == + tq_trans_ctx_rebuild_each_node(pdx, ptq, + ptq->wq_cur)) { + DbgErr("Tag Queue rebuild failed\n"); + ret = REQ_RESULT_ACCESS_ERR; + tq_thread_failed_queue(pdx, + REQ_RESULT_ACCESS_ERR, + TRUE); + goto exit; + } else + goto tq_trans_again; + } else { + DbgErr + ("Tag Queue Error Recovery retry exceed\n"); + + /* + * set card error after retry fail. + * to avoid process one more tag io that already in queue. + */ + pdx->card.card_type = CARD_ERROR; + + tq_thread_failed_queue(pdx, + REQ_RESULT_ACCESS_ERR, + FALSE); + ret = REQ_RESULT_ACCESS_ERR; + goto exit; + } + } else { + DbgErr("Tag Queue Error card Recovery failed\n"); + tq_thread_failed_queue(pdx, ret, TRUE); + goto exit; + } + } + tq_set_hardware_idle_state(&pdx->tag_queue, TRUE); + /* queue final idle */ + work_queue_state_machine(ptq->wq_cur, QUEUE_EVT_DONE); + +exit: + os_set_dev_idle(pdx); +final: + + if (ptq->wq_cur->state != QUEUE_STATE_IDLE) + DbgErr("%s cur_wq state is not idle\n", __func__); + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "Exit %s ret=%x\n", __func__, ret); + return ret; +} + +bool tq_work_queue_state_recovery(PVOID pdx, tag_queue_t *tq) +{ + bool ret = FALSE; + + /* roll back all IOs to request queue, then reset TQ wq */ + + /* 1. rollback all IOs to request queue */ + tq_work_queue_rollback_all_node(pdx, tq); + /* 2.reset TQ wq */ + tq_work_queue_reset_state(pdx, tq); + + return ret; +} + +/* + * + * Function Name: tag_queue_rw_data + * + * Abstract: + * + * it handle tag queue read/write request work flow. + * + * Input: + * + * bht_dev_ext_t *pdx [in]: Pointer to the bht adapter extension structure + * Output: + * + * None. + * + * Return value: + * + * Notes: + * + * Caller: thread for complete TQ request + * + */ +e_req_result tag_queue_rw_data(bht_dev_ext_t *pdx) +{ + tag_queue_t *ptq = 0; + host_cmd_req_t *irq = 0; + e_req_result ret = REQ_RESULT_OK; + sd_card_t *card = 0; + + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + /* check input */ + if (pdx == NULL) { + DbgErr("%s:pdx NULL\n", __func__); + /* can't handle anything */ + ret = REQ_RESULT_ABORT; + goto exit; + } + /* init variable */ + ptq = &pdx->tag_queue; + irq = &ptq->cmd_req; + card = &pdx->card; + /* check TQ status */ + if (ptq->init_magic_number != TAG_QUEUE_INIT_MAGIC_NUMBER) { + DbgErr("%s TQ no init ok\n", __func__); + /* can't handle anything */ + tq_thread_failed_queue(pdx, REQ_RESULT_ABORT, TRUE); + ret = REQ_RESULT_ABORT; + goto exit; + } + + if (ptq->ops.issue_transfer == NULL) { + DbgErr("%s TQ no issue cb\n", __func__); + tq_thread_failed_queue(pdx, REQ_RESULT_ABORT, TRUE); + ret = REQ_RESULT_ABORT; + goto exit; + } + + /* start jobs */ + while (1) { + if (card->card_chg || card->card_present == FALSE) { + DbgErr("Tag Queue card not ready failed\n"); + /* can't handle anything */ + tq_thread_failed_queue(pdx, REQ_RESULT_NO_CARD, TRUE); + ret = REQ_RESULT_NO_CARD; + goto exit; + } + + /* Thomas add based on UHS2 issue 11. */ + if (card->card_type == CARD_ERROR) { + DbgErr("Tag Queue aborted for card error\n"); + + ret = REQ_RESULT_ACCESS_ERR; + goto exit; + } + + ptq->wq_cur = tq_trans_ctx_get_ready_one((PVOID) pdx, ptq); + if (ptq->wq_cur == NULL) { + /* need build it now */ + ptq->wq_cur = + tq_trans_ctx_get_idle_one((PVOID) pdx, ptq); + if (ptq->wq_cur == NULL) { + /* impossible path(assert case),but need check */ + DbgErr + ("%s fatal error,failed to get free trans ctx\n", + __func__); + tq_work_queue_state_recovery((PVOID) pdx, ptq); + ptq->wq_cur = + tq_trans_ctx_get_idle_one((PVOID) pdx, ptq); + + if (ptq->wq_cur == NULL) { + DbgErr + ("%s recovery but still get null cur wq\n", + __func__); + tq_thread_failed_queue(pdx, + REQ_RESULT_ACCESS_ERR, + TRUE); + ret = REQ_RESULT_ACCESS_ERR; + goto exit; + } + + } + /* build ctx */ + { + e_build_ctx_result build_ret = + tq_trans_ctx_build(pdx, ptq, ptq->wq_cur); + switch (build_ret) { + case TQ_BUILD_IO_ERROR: + { + /* can't handle anything */ + tq_thread_failed_queue(pdx, + REQ_RESULT_ACCESS_ERR, + TRUE); + /* empty request queue or build failed */ + ret = REQ_RESULT_ACCESS_ERR; + goto exit; + } + break; + case TQ_BUILD_IO_OK: + break; + case TQ_BUILD_IO_EMPTY: + ret = REQ_RESULT_OK; + goto exit; + break; + default: + DbgErr("%s build state error\n"); + } + } + } + + /* ---------------issue stage--------------- */ + ret = tag_queue_rw_data_issue_stage(pdx, ptq, card, irq); + if (ret != REQ_RESULT_OK) { + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "Exit %s ret=%x\n", __func__, ret); + goto exit; + } + + } + ret = REQ_RESULT_OK; +exit: + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s %d\n", + __func__, ret); + return ret; +} + +/* + * + * Function Name: tq_add_request + * + * Abstract: + * + * add one request to tag queue, and do prebuild callback if have. + * + * Input: + * + * bht_dev_ext_t *pdx [in]: Pointer to the bht adapter extension structure + * srb_ext* srb_ext [in]: Pointer to the request data structure. + * Output: + * + * None. + * + * Return value: + * + * REQ_RESULT_OK: tag queue have put this request successful in queue. + * REQ_RESULT_QUEUE_BUSY: tag queue is full, please retry late. + * Notes: + * + * Caller: StartIO. async add request to tq + * + */ + +e_req_result tq_add_request(bht_dev_ext_t *pdx, srb_ext_t *srb_ext, + sd_card_t *card) +{ + tag_queue_t *tq = 0; + node_t *pnode = 0; + e_req_result ret = 0; + u32 dma_mode; + + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + /* check */ + if (pdx == NULL) { + ret = REQ_RESULT_ABORT; + DbgErr("%s null pdx\n", __func__); + goto exit; + } + + tq = &pdx->tag_queue; + /* init done check */ + if (tq->init_magic_number != TAG_QUEUE_INIT_MAGIC_NUMBER) { + DbgErr("%s TQ no init\n", __func__); + ret = REQ_RESULT_ABORT; + goto exit; + } + + /* over size check */ + if ((int)os_atomic_read(&tq->req_cnt) < ((int)tq->max_wq_req_size)) { + /* get one node_t */ + pnode = node_malloc(tq); + if (pnode == NULL) { + DbgErr("%s node malloc failed\n", __func__); + ret = REQ_RESULT_QUEUE_BUSY; + goto exit; + } + /* bind srb_ext to node_t */ + pnode->psrb_ext = srb_ext; + /* bind card to node */ + pnode->card = card; + + dma_mode = pdx->cfg->host_item.test_dma_mode_setting.dma_mode; +#if CFG_OS_LINUX + if (cfg_dma_need_sdma_like_buffer(dma_mode) == TRUE) + tq_adma2_sdmalike_copy(pdx, pnode); +#endif + + /* put node to request queue & add cnt */ + os_atomic_add(&(tq->req_cnt), 1); + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "%s,tq cnt:%x\n", __func__, + os_atomic_read(&tq->req_cnt)); + node_list_put_one(&(tq->req_queue.list), pnode); + +#if CFG_OS_LINUX + os_set_event(&pdx->os, EVENT_TAG_IO); +#else + os_set_event(pdx, &pdx->os, EVENT_TASK_OCCUR, EVENT_TAG_IO); +#endif + + if (TRUE == + tq_dma_support_mix_mode(pdx->cfg->host_item.test_dma_mode_setting.dma_mode)) { + tq_dma_decision_policy(tq, card, &srb_ext->req); + } + + ret = REQ_RESULT_PENDING; + goto exit; + } else { + /* TQ full */ + ret = REQ_RESULT_QUEUE_BUSY; + goto exit; + } +exit: + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "Exit %s,ret=%x\n", __func__, ret); + return ret; + +} + +/* + * + * Function Name: tq_is_empty + * + * Abstract: + * + * get tag queue running status. + * + * Input: + * + * bht_dev_ext_t *pdx [in]: Pointer to the bht adapter extension structure + * + * Output: + * + + * Return value: + * + * TRUE: empty + * FALSE: no empty + * Notes: + * + * IO_control case for corrently crash. + */ + +bool tq_is_empty(bht_dev_ext_t *pdx) +{ + tag_queue_t *tq = 0; + bool ret = TRUE; + + if (pdx == NULL) { + DbgErr("%s pdx null\n", __func__); + ret = FALSE; + goto exit; + } + + tq = &pdx->tag_queue; + + if (tq->init_magic_number != TAG_QUEUE_INIT_MAGIC_NUMBER) { + DbgErr("%s TQ no init\n", __func__); + ret = TRUE; + goto exit; + } + + if (os_atomic_read(&tq->req_cnt)) + ret = FALSE; +exit: + return ret; +} + +/* + * + * Function Name: tag_queue_dma_size_init + * + * Abstract: + * + * try to get TQ cfg for DMA buffer size + * + * Input: + * + * bht_dev_ext_t *pdx [in]: Pointer to the bht adapter extension structure + * + * Output: + * + + * Return value: + * + * + * Notes: + * + * IO_control case for corrently crash. + */ + +bool tag_queue_dma_size_init(bht_dev_ext_t *pdx, u32 buf_size) +{ + u32 max_req_numb = 0; + tag_queue_t *tq = &pdx->tag_queue; + u32 dma_mode = 0; + u32 cnt = 0; + u32 infinite_mode_enable = 0; + bool sdma_like_mode = FALSE; + u32 node_size = 0; + bool ret = FALSE; + + DbgInfo(MODULE_TQ_FLOW, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + + max_req_numb = pdx->cfg->host_item.test_tag_queue_capability.max_srb; + dma_mode = pdx->cfg->host_item.test_dma_mode_setting.dma_mode; + infinite_mode_enable = + pdx->cfg->host_item.test_infinite_transfer_mode.enable_inf; + + max_req_numb += TQ_RESERVED_NODE_SIZE; + + /* get cfg value */ + if (max_req_numb == 0) { + tq->max_wq_req_size = 1; + } else { + if (max_req_numb > MAX_WORK_QUEUE_SIZE) + max_req_numb = MAX_WORK_QUEUE_SIZE; + tq->max_wq_req_size = max_req_numb; + } + + /* check can use sdma-like mode or not */ + if (cfg_dma_need_sdma_like_buffer(dma_mode) == TRUE) { + /* + * try get max for sdma-like mode first, + * if can't support at least two for sdma-like mode, + * then degrade to non-sdma-like mode + */ + if (buf_size >= (2 * MAX_SDMA_LIKE_MODE_NODE_BUF_SIZE)) + sdma_like_mode = TRUE; + else { + sdma_like_mode = FALSE; + /* can't use sdma_like mode ,so disable this cfg to non-sdma-like mode */ + DbgErr + ("TQ degrade sdma-like mode to non-sdma-like mode\n"); + if (dma_mode == CFG_TRANS_MODE_ADMA2_SDMA_LIKE) + cfg_dma_mode_dec(pdx->cfg, + CFG_TRANS_MODE_ADMA2); + if (dma_mode == CFG_TRANS_MODE_ADMA3_SDMA_LIKE) + cfg_dma_mode_dec(pdx->cfg, + CFG_TRANS_MODE_ADMA3); + if (dma_mode == CFG_TRANS_MODE_ADMA_MIX_SDMA_LIKE) + cfg_dma_mode_dec(pdx->cfg, + CFG_TRANS_MODE_ADMA_MIX); + if (dma_mode == CFG_TRANS_MODE_SDMA) { + DbgErr("SDMA mode degrade adma2 mode\n"); + cfg_dma_mode_dec(pdx->cfg, + CFG_TRANS_MODE_ADMA2); + } + } + } + + /* get node size */ + if (sdma_like_mode == TRUE) + node_size = MAX_SDMA_LIKE_MODE_NODE_BUF_SIZE; + else + node_size = MAX_NODE_BUF_SIZE; + + /* make sure have DMA buffer align size */ + cnt = buf_size / (node_size); + /* small req size is over buffer resource */ + if (tq->max_wq_req_size >= cnt) + tq->max_wq_req_size = cnt; + + /* if infinite mode enable, however only cfg one node. it can't work for link table. */ + if (infinite_mode_enable) { + if (tq->max_wq_req_size <= 1) { + DbgErr + ("infinite mode can't work when only one node.force TQ stall\n"); + tq->max_wq_req_size = 0; + } + } + + /* check cnt value */ + if (tq->max_wq_req_size == 0) { + DbgErr("TQ failed to get any dma buffer for only one desc\n"); + ret = FALSE; + } else + ret = TRUE; + + DbgInfo(MODULE_TQ_FLOW, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Exit %s max_req:%d dma_mode:%d\n", __func__, + tq->max_wq_req_size, dma_mode); + return ret; + +} + +/* + * + * Function Name: tag_queue_isr + * + * Abstract: + * + * it's the cb_req_complete function for ISR handle. + * + * Input: + * + * bht_dev_ext_t *pdx [in]: Pointer to the bht adapter extension structure + * cmd_err_t *err [in] : the error information + * Output: + * + * None. + * + * Return value: + * + * Notes: + * + * Caller: ISR + * + */ +u32 tag_queue_isr(void *p, cmd_err_t *err) +{ + bht_dev_ext_t *pdx = p; + tag_queue_t *tq = &pdx->tag_queue; + + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s (%s)\n", + __func__, err->error_code ? "err" : "ok"); + + tq_set_hardware_idle_state(&pdx->tag_queue, TRUE); + if (err->error_code) { + /* + * just update transfer queue requests to ERROR, + * wait for thread handle later.(retry or other) + */ + + req_queue_mark_node_status(tq->wq_cur, REQ_RESULT_ACCESS_ERR); + } else { + /* complete these request now, mark ok first(for error recovery successful case) */ + + req_queue_mark_node_status(tq->wq_cur, REQ_RESULT_OK); + tq_work_queue_complete_cur_wq(pdx); + } + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); + return TRUE; +} + +/* + * + * Function Name: tag_queue_abort + * + * Abstract: + * + * flush all request with error status in TQ for abort + * + * Input: + * + * bht_dev_ext_t *pdx [in]: Pointer to the bht adapter extension structure + * e_req_result result [in] : the abort status for request + * Output: + * + * None. + * + * Return value: + * + * Notes: + * + * Caller: thread when wakeup card failed, need abort. + * + */ +void tag_queue_abort(bht_dev_ext_t *pdx, e_req_result result) +{ + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "Enter %s req_result:%x\n", __func__, result); + + if (pdx == NULL) { + DbgErr("%s pdx null\n", __func__); + goto exit; + } + + if (pdx->tag_queue.init_magic_number != TAG_QUEUE_INIT_MAGIC_NUMBER) { + DbgErr("TQ no init\n"); + goto exit; + } + + tq_thread_failed_queue(pdx, result, TRUE); +exit: + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +/* + * + * Function Name: tag_queue_init + * + * Abstract: + * + * initialize tag queue. + * + * Input: + * + * bht_dev_ext_t *pdx [in]: Pointer to the bht adapter extension structure + * + * Output: + * + * None + * + * Return value: + * + * None. + * + * Notes: + * + * Caller: + * 1. This function is called req_global_init + * 2. tag queue must use DMA buffer for generate DMA desc table. + */ + +bool tag_queue_init(bht_dev_ext_t *pdx) +{ + tag_queue_t *tq = 0; + dma_desc_buf_t dma_res = { 0 }; + bool ret = FALSE, sdma_mode = FALSE; + u32 dma_mode = 0; + u32 merge_enable = 0; + + DbgInfo(MODULE_TQ_FLOW, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + /* input check */ + if (pdx == NULL) { + DbgErr("%s pdx null\n", __func__); + goto exit; + } + + os_memset(&(pdx->tag_queue), 0, sizeof(tag_queue_t)); + + tag_queue_dma_size_init(pdx, pdx->dma_buff.len); + /* init var */ + dma_mode = pdx->cfg->host_item.test_dma_mode_setting.dma_mode; + merge_enable = + pdx->cfg->host_item.test_tag_queue_capability.enable_srb_merge; + tq = &pdx->tag_queue; + + dma_res = pdx->dma_buff; + if (dma_res.len == 0 || dma_res.va == NULL) { + DbgErr("%s dma_res null\n", __func__); + ret = FALSE; + goto exit; + } + + /* check req size */ + if (tq->max_wq_req_size == 0) { + DbgErr("%s req size =0\n", __func__); + ret = FALSE; + goto exit; + } + + /* reset TQ state */ + os_atomic_set(&tq->req_cnt, 0); + req_queue_init((PVOID) pdx, &tq->req_queue, tq->queue_id_seed++); + tq->wq_cur = NULL; + /* bind completion */ + tq->tran_cpl = &tq->cmd_req.done; + /* + * init node memory pool , this call must call + * before tq_setup_dma_res() for use DMA resource + */ + if (cfg_dma_need_sdma_like_buffer(dma_mode) == TRUE) + sdma_mode = TRUE; + else + sdma_mode = FALSE; + if (node_pool_init((PVOID) pdx, tq, &dma_res, sdma_mode) == FALSE) { + DbgErr("%s node pool failed\n", __func__); + ret = FALSE; + goto exit; + } + + /* work queue init */ + if (tq_work_queue_init((PVOID) pdx, tq, &dma_res, dma_mode) == FALSE) { + ret = FALSE; + goto exit; + } + + /* cfg tag queue */ + + /* transfer cbs */ + /* + * 0: SDMA, 1: ADMA2, 2: ADMA3, 3: ADMA2_SDMA_Like, + * 4: ADMA3_SDMA_Like, 0xF: PIO, other: Reserved + */ + switch (dma_mode) { + case CFG_TRANS_MODE_SDMA: + tq_sdma_mode_init(&tq->ops); + break; + case CFG_TRANS_MODE_ADMA2: + tq_adma2_inf_mode_init(&tq->ops); + break; + case CFG_TRANS_MODE_ADMA3: + tq_adma3_mode_init(&tq->ops); + break; + case CFG_TRANS_MODE_ADMA2_SDMA_LIKE: + tq_adma2_inf_sdmalike_mode_init(&tq->ops); + break; + case CFG_TRANS_MODE_ADMA3_SDMA_LIKE: + tq_adma3_sdmalike_mode_init(&tq->ops); + break; + case CFG_TRANS_MODE_ADMA_MIX: + tq_adma2_inf_mode_init(&tq->ops); + break; + case CFG_TRANS_MODE_ADMA_MIX_SDMA_LIKE: + tq_adma2_inf_sdmalike_mode_init(&tq->ops); + break; + case CFG_TRANS_MODE_ADMA2_ONLY: + tq_adma2_mode_init(&tq->ops); + break; + case CFG_TRANS_MODE_ADMA2_ONLY_SDMA_LIKE: + tq_adma2_sdmalike_mode_init(&tq->ops); + break; + default: + /* ADMA2,no support PIO mode in tag queue now. */ + DbgErr("%s dma_mode(%d) invliad,so use default mode\n", + __func__, dma_mode); + tq_adma2_mode_init(&tq->ops); + break; + } + tq->cur_dma_mode = dma_mode; + os_atomic_set(&(tq->target_dma_mode), dma_mode); + tq->cfg_dma_mode = dma_mode; + tq_dma_decision_init(&tq->decision, pdx->host.chip_type, 4, 2, 0); + /* signature code for init done (TQ internal use) */ + tq->init_magic_number = TAG_QUEUE_INIT_MAGIC_NUMBER; + + DbgInfo(MODULE_TQ_FLOW, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "%s max req total support:%d\n", __func__, + tq->max_wq_req_size); + if (tq->max_wq_req_size > 0) + /* + * for dma infinite transfer limit: need reserved one for + * keep link address in descriptor buf. + */ + tq->max_wq_req_size -= TQ_RESERVED_NODE_SIZE; + DbgInfo(MODULE_TQ_FLOW, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "%s max req use:%d\n", __func__, tq->max_wq_req_size); + +#if (TQ_WORK_QUEUE_SIZE >= 2) + +#define MIN_PINGPONG_BUILD_FUNCTION_SIZE 4 +#define MAX_PINGPONG_BUILD_FUNCTION_SIZE 16 + + if (tq->max_wq_req_size >= MIN_PINGPONG_BUILD_FUNCTION_SIZE) { + tq->max_build_limit = tq->max_wq_req_size; + tq->max_build_limit = tq->max_build_limit / 2; + if (tq->max_build_limit >= MAX_PINGPONG_BUILD_FUNCTION_SIZE) + tq->max_build_limit = MAX_PINGPONG_BUILD_FUNCTION_SIZE; + DbgInfo(MODULE_TQ_FLOW, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "max build limit=%d\n", tq->max_build_limit); + } else + tq->max_build_limit = tq->max_wq_req_size; +#else + tq->max_build_limit = tq->max_wq_req_size; +#endif + + ret = TRUE; +exit: + DbgInfo(MODULE_TQ_FLOW, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + return ret; +} diff --git a/drivers/scsi/bht/tagqueue/tq_merge.c b/drivers/scsi/bht/tagqueue/tq_merge.c new file mode 100644 index 000000000000..51ce2da257d7 --- /dev/null +++ b/drivers/scsi/bht/tagqueue/tq_merge.c @@ -0,0 +1,433 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: tq_merge.c + * + * Abstract: handle tagqueue sdma_like transfer cb ops. + * + * Version: 1.00 + * + * Author: Chuanjin + * + * Environment: OS Independent + * + * History: + * + * 11/1/2014 Creation Chuanjin + */ + +#include "../include/basic.h" +#include "../include/tqapi.h" +#include "../include/debug.h" +#include "../include/cmdhandler.h" +#include "../include/card.h" +#include "tq_util.h" +#include "tq_trans_api.h" +#include "../include/util.h" + +/* + * + * Function Name: queue_2_tb + * + * Abstract: + * + * convert tq list to array format. + * + * Input: + * + * req_queue_t *rq [in]: Pointer to queue + * pnode_t *tb [in] : array table + * + * Output: + * + * None. + * + * Return value: + * + * convert size + * Notes: + * + * Caller: + * + */ +static u32 queue_2_tb(req_queue_t *rq, pnode_t *tb) +{ + u32 len = 0; + u32 i = 0; + node_t *node = 0; + + /* check parameter */ + len = node_list_get_cnt(&rq->list); + + if (len > MAX_WORK_QUEUE_SIZE) { + len = MAX_WORK_QUEUE_SIZE; + DbgErr("%s overflow\n", __func__); + } + /* convert to array */ + for (i = 0; i < len; i++) { + /* get one from queue */ + node = node_list_get_one(&rq->list); + if (node == NULL) + break; + tb[i] = node; + /* put back to queue */ + node_list_put_one(&rq->list, node); + } + + return len; +} + +/* + * + * Function Name: mark_continuous_flag + * + * Abstract: + * + * mark continuos flag for transfer queue. + * if next node is continuous node, then mark current node flag means can merge + * + * Input: + * + * sd_card_t *card [in]: card which for merge + * pnode_t *tb [in] : Pointer to array + * + * Output: + * + * None. + * + * Return value: + * + * + * Notes: + * + * Caller: + * + */ +bool tq_judge_request_continuous(bool low_capacity_card, e_data_dir req_dir, + u32 req_sec_addr, u32 req_sec_cnt, + e_data_dir next_req_dir, u32 next_req_sec_addr) +{ + bool ret = FALSE; + + DbgInfo(MODULE_TQ_POLICY, FEATURE_RW_TRACE, NOT_TO_RAM, + "req dir%x next dir%x\n", req_dir, next_req_dir); + DbgInfo(MODULE_TQ_POLICY, FEATURE_RW_TRACE, NOT_TO_RAM, + "req sec%x cnt%x, next %x\n", req_sec_addr, req_sec_cnt, + next_req_sec_addr); + /* same dir */ + if (req_dir == next_req_dir) { + u32 factor = 1; + /* for scsd card need */ + if (low_capacity_card) + factor = SD_BLOCK_LEN; + else + factor = 1; + /* calculate continuos case */ + if ((req_sec_addr + req_sec_cnt * factor) == next_req_sec_addr) { + ret = TRUE; + DbgInfo(MODULE_TQ_POLICY, FEATURE_RW_TRACE, NOT_TO_RAM, + "can merge\n"); + } + + } + return ret; +} + +static bool mark_continuous_flag(sd_card_t *card, pnode_t *tb, u32 len) +{ + u32 i = 0; + srb_ext_t *pext = 0; + request_t *req = 0; + request_t *next_req = 0; + + DbgInfo(MODULE_TQ_POLICY, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + /* + * loop for mark + * len-1 :make sure no over flow for mark + */ + for (i = 0; i < (len - 1); i++) { + pext = node_2_srb_ext(tb[i]); + req = &pext->req; + /* get next req */ + pext = node_2_srb_ext(tb[i + 1]); + next_req = &pext->req; + + /* clear or will false set */ + tb[i]->flag = + tq_judge_request_continuous(card_is_low_capacity(card), + req->data_dir, + req->tag_req_t.sec_addr, + req->tag_req_t.sec_cnt, + next_req->data_dir, + next_req->tag_req_t.sec_addr); + } + DbgInfo(MODULE_TQ_POLICY, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); + return TRUE; +} + +/* + * + * Function Name: update_adma3_blk_cnt + * + * Abstract: + * + * update adma3 command descriptor for block counter + * + * Input: + * + * sd_card_t *card [in]: card which for merge + * dma_desc_buf_t *pdma [in] : Pointer to adma3 command descriptor buffer + * + * + * Output: + * + * None. + * + * Return value: + * + * + * Notes: + * + * Caller: + * + */ +int update_adma3_blk_cnt(sd_card_t *card, const dma_desc_buf_t *pdma, u32 cnt) +{ + u32 *ptb = (u32 *) pdma->va; + + if (card->card_type == CARD_UHS2) { + *(ptb + 3) = cnt; + *(ptb + 9) = swapu32(cnt); + } else { + *(ptb + 1) = cnt; + } + return 0; +} + +/* + * + * Function Name: merge_continous_io + * + * Abstract: + * + * merge one continuous IOs until first break flag occur + * + * Input: + * + * sd_card_t *card [in]: card which for merge + * + * + * Output: + * + * None. + * + * Return value: + * merge number io. + * + * Notes: + * + * Caller: + * + */ +static int merge_continous_io(req_queue_t *rq, sd_card_t *card, pnode_t *tb, + int len, bool dma_64bit) +{ + int i = 0; + dma_desc_buf_t *pdma = 0; + int merge_cnt = 0; + srb_ext_t *pext = 0; + request_t *req = 0; + request_t *next_req = 0; + byte *pdesc = 0; + u32 size = 0; + + DbgInfo(MODULE_TQ_POLICY, FEATURE_RW_TRACE, NOT_TO_RAM, + "Enter %s len:%d dma_64b:%d\n", __func__, len, dma_64bit); + merge_cnt = 0; + /* get one integrate desc items buffer */ + pdma = get_one_integrate_desc_res(rq); + + pext = node_2_srb_ext(tb[i]); + req = &pext->req; + + merge_cnt = req->tag_req_t.sec_cnt; + /* do merge if any */ + for (i = 0; i < len - 1; i++) { + pext = node_2_srb_ext(tb[i]); + req = &pext->req; + /* get next req */ + pext = node_2_srb_ext(tb[i + 1]); + next_req = &pext->req; + + if (tb[i]->flag == TRUE) { + phy_addr_t m_pa; + u32 cmd_desc_len = 0; + + merge_cnt += next_req->tag_req_t.sec_cnt; + /* get next adma2 table physical address */ + m_pa = tb[i + 1]->phy_node_buffer.head.pa; + if (card->card_type == CARD_UHS2) { + cmd_desc_len = + ADMA3_CMDDESC_ITEM_LENGTH * + ADMA3_CMDDESC_ITEM_NUM_UHSII; + } else { + cmd_desc_len = + ADMA3_CMDDESC_ITEM_LENGTH * + ADMA3_CMDDESC_ITEM_NUM_UHSI; + } + pa_offset_pa(&m_pa, cmd_desc_len); + /* update adma2 table */ + link_adma2_desc(tb[i]->phy_node_buffer.end.va, &m_pa, + dma_64bit); + } else { + DbgInfo(MODULE_TQ_POLICY, FEATURE_RW_TRACE, NOT_TO_RAM, + "no continues (%x)\n", i); + break; + } + } + + pdesc = + build_integrated_desc(pdma->va, &(tb[0]->phy_node_buffer.head.pa), + dma_64bit); + size = pp_ofs(pdesc, pdma->va); + put_one_integrate_desc(rq, size); + /* update cnt */ + update_adma3_blk_cnt(card, &tb[0]->phy_node_buffer.head, merge_cnt); + DbgInfo(MODULE_TQ_POLICY, FEATURE_RW_TRACE, NOT_TO_RAM, + "Exit%s ret:%d\n", __func__, i + 1); + return (i + 1); +} + +/* + * + * Function Name: _update_io_descriptor + * + * Abstract: + * + * only forcus on merge IOs descriptor for queue + * + * Input: + * + * sd_card_t *card [in]: card which for merge + * + * + * Output: + * + * None. + * + * Return value: + * + * + * Notes: + * + * Caller: + * + */ +static int _update_io_descriptor(req_queue_t *rq, sd_card_t *card, + pnode_t *tb, int len, bool dma_64bit) +{ + int sz = 0, left = 0; + int i = 0; + + left = len; + for (i = 0; i < len;) { + sz = merge_continous_io(rq, card, &tb[i], left, dma_64bit); + DbgInfo(MODULE_TQ_POLICY, FEATURE_RW_TRACE, NOT_TO_RAM, + "merge sz=%x,len=%x\n", sz, len); + left -= sz; + i += sz; + } + return 0; + +} + +/* + * + * Function Name: update_io_descriptor + * + * Abstract: + * + * update merge IOs descriptor for queue + * + * Input: + * + * sd_card_t *card [in]: card which for merge + * + * + * Output: + * + * None. + * + * Return value: + * + * + * Notes: + * + * Caller: + * + */ +static bool update_io_descriptor(sd_card_t *card, req_queue_t *rq, + pnode_t *tb, u32 len, bool dma_64bit) +{ + /* reset */ + tq_adma3_reset_integrate(rq); + /* update descriptor */ + _update_io_descriptor(rq, card, &tb[0], len, dma_64bit); + /* end table */ + adma3_end_integrated_tb(rq->adma3_integrate_tbl_cur.va, dma_64bit); + + return 0; +} + +/* + * + * Function Name: adma3_merge_io_descriptor + * + * Abstract: + * + * adma3 merge IOs descriptor for queue + * + * Input: + * + * sd_card_t *card [in]: card which for merge + * + * + * Output: + * + * None. + * + * Return value: + * + * + * Notes: + * + * Caller: + * + */ +bool adma3_merge_io_descriptor(req_queue_t *rq, sd_card_t *card, + bool dma_64bit) +{ + u32 len = 0; + node_t *tb[MAX_WORK_QUEUE_SIZE]; + bool ret = 0; + + len = node_list_get_cnt(&rq->list); + DbgInfo(MODULE_TQ_POLICY, FEATURE_RW_TRACE, NOT_TO_RAM, + "Enter %s len:%d dma_64b:%d\n", __func__, len, dma_64bit); + /* check */ + if (len < 2) { + /* no need merge for one SRB */ + return TRUE; + } + + /* convert to node array */ + len = queue_2_tb(rq, tb); + mark_continuous_flag(card, tb, len); + ret = update_io_descriptor(card, rq, tb, len, dma_64bit); + DbgInfo(MODULE_TQ_POLICY, FEATURE_RW_TRACE, NOT_TO_RAM, + "Exit %s ret:%d\n", __func__, ret); + return ret; +} diff --git a/drivers/scsi/bht/tagqueue/tq_trans_api.h b/drivers/scsi/bht/tagqueue/tq_trans_api.h new file mode 100644 index 000000000000..e57974fafc03 --- /dev/null +++ b/drivers/scsi/bht/tagqueue/tq_trans_api.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: tq_trans_api.h + * + * Abstract: This file is used to declare interface for TagQueue DMA mode callbacks + * + * Version: 1.00 + * + * Author: Chuanjin + * + * Environment: OS Independent + * + * History: + * + * 9/12/2014 Creation Chuanjin + */ + +#ifndef _TQ_TRANS_CBS_ +#define _TQ_TRANS_CBS_ + +u32 node_list_get_cnt(list_t *p); +node_t *node_list_get_one(list_t *p); +void node_list_put_one(list_t *p, node_t *pnode); +u32 tag_queue_isr(void *pdx, cmd_err_t *err); +u32 tq_issue_post_cb(void *p); +bool req_queue_loop_ctx_ops(req_queue_t *p, req_queue_node_ops_ctx_cb cb, + void *ctx); + +/* transfer handle */ +bool adma3_end_integrated_tb(u8 *desc, bool dma_64bit); +bool link_adma2_desc(u8 *pdesc, phy_addr_t *pa, bool dma_64bit); +bool gen_sdma_like_sgl(request_t *req, dma_desc_buf_t *pdma); +dma_desc_buf_t *node_get_desc_res(node_t *node, u32 max_use_size); +u32 build_card_cmd_desc(sd_card_t *card, u8 *desc, sd_command_t *cmd); + +u32 get_sdma_boudary_size(cfg_item_t *cfg); +bool dma_align(dma_desc_buf_t *pdma, u32 align_size); +/* sdma */ +bool tq_sdma_mode_init(transfer_cb_t *ops); + +/* adma2 */ +void dump_adma2_desc(u8 *desc, u8 *desc_end); +bool dump_node_adma2_desc(node_t *node, void *ctx); + +void dbg_dump_general_desc_tb(u8 *desc, u32 size); + +bool tq_adma2_mode_init(transfer_cb_t *ops); +bool tq_adma2_inf_mode_init(transfer_cb_t *ops); + +bool tq_adma2_inf_build_io(void *p, node_t *node); +bool tq_adma2_build_io(void *p, node_t *node); +bool tq_adma2_inf_send_command(void *p); +bool req_build_cmd(sd_card_t *card, sd_command_t *cmd, request_t *req); +bool tq_adma2_prebuild_io(void *p, node_t *node); +bool update_adma2_inf_tb(u8 *pdesc, u8 **link_addr, phy_addr_t *pa, + bool dma_64bit); +bool tq_adma2_inf_unload(void *p); +bool tq_adma2_sdmalike_copy(void *p, node_t *node); + +/* adma3 */ +dma_desc_buf_t *get_one_integrate_desc_res(req_queue_t *rq); +bool put_one_integrate_desc(req_queue_t *rq, u32 size); +bool tq_adma3_reset_integrate(req_queue_t *rq); +bool tq_adma3_mode_init(transfer_cb_t *ops); + +bool tq_adma3_build_io(void *p, node_t *node); +bool tq_adma3_prebuild_io(void *p, node_t *node); +/* adma3 merge */ + +bool adma3_merge_io_descriptor(req_queue_t *rq, sd_card_t *card, + bool dma_64bit); +bool tq_judge_request_continuous(bool low_capacity_card, e_data_dir req_dir, + u32 req_sec_addr, u32 req_sec_cnt, + e_data_dir next_req_dir, + u32 next_req_sec_addr); + +/* sdma-like */ +bool tq_adma2_inf_sdmalike_mode_init(transfer_cb_t *ops); +bool tq_adma2_sdmalike_mode_init(transfer_cb_t *ops); +bool tq_adma3_sdmalike_mode_init(transfer_cb_t *ops); +/* others */ + +bool tag_queue_policy_break(bht_dev_ext_t *pdx); +void tq_dma_decision_policy(tag_queue_t *tq, sd_card_t *card, + request_t *request); +void tq_dma_decision_init(decision_mgr *mgr, e_chip_type chip_id, + int scope_size, int up_threshold, int low_threshold); + +#endif diff --git a/drivers/scsi/bht/tagqueue/tq_util.h b/drivers/scsi/bht/tagqueue/tq_util.h new file mode 100644 index 000000000000..4996527d865e --- /dev/null +++ b/drivers/scsi/bht/tagqueue/tq_util.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: tq_util.h + * + * Abstract: This file is used to define util interface for tag queue + * + * Version: 1.00 + * + * Author: Chuanjin + * + * Environment: OS Independent + * + * History: + * + * 9/12/2014 Creation Chuanjin + */ + +#ifndef _TQ_UTIL_CBS_ +#define _TQ_UTIL_CBS_ + +u32 pp_ofs(byte *ph, byte *pl); + +srb_ext_t *node_2_srb_ext(node_t *node); +dma_desc_buf_t *get_one_desc_res(dma_desc_buf_t *cur, u32 max_use_size); +bool put_one_desc_res(dma_desc_buf_t *cur, u32 size); + +#endif diff --git a/drivers/scsi/bht/tagqueue/tqadma2.c b/drivers/scsi/bht/tagqueue/tqadma2.c new file mode 100644 index 000000000000..90b43978315d --- /dev/null +++ b/drivers/scsi/bht/tagqueue/tqadma2.c @@ -0,0 +1,821 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: tqadma2.c + * + * Abstract: handle tagqueue adma2 transfer cb ops. + * + * Version: 1.00 + * + * Author: Chuanjin + * + * Environment: OS Independent + * + * History: + * + * 9/5/2014 Creation Chuanjin + */ + +#include "../include/basic.h" +#include "../include/tqapi.h" +#include "../include/debug.h" +#include "../include/cmdhandler.h" +#include "../include/card.h" +#include "tq_util.h" +#include "tq_trans_api.h" +#include "../include/util.h" +#include "../include/cardapi.h" +#include "../include/hostapi.h" + +/* + * + * Function Name: node_2_srb_ext + * + * Abstract: + * + * convert node to srb_ext pointer + * + * Input: + * + * node_t *node [in]: Pointer to node + * + * + * Output: + * + * + * + * Return value: + * + * the srb_ext_t *pointer of the node + * Notes: + * + * Caller: + * + */ +srb_ext_t *node_2_srb_ext(node_t *node) +{ + srb_ext_t *psrb_ext = (srb_ext_t *) node->psrb_ext; + return psrb_ext; +} + +static bool tq_adma2_init_ctx(void *p) +{ + bool ret = TRUE; + bht_dev_ext_t *pdx = (bht_dev_ext_t *) p; + req_queue_t *rq = pdx->tag_queue.wq_build; + + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + rq->adma2_last_req.data_dir = DATA_DIR_NONE; + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s %d\n", + __func__, ret); + return ret; +} + +/* + * + * Function Name: req_build_cmd + * + * Abstract: + * + * build cmd index & cmd_flag + * + * Input: + * + * sd_command_t *cmd [in]: Pointer to sd_cmd_t + * request_t *req [in] : the req need build + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: build ok + * Notes: + * + * Caller: + * + */ +bool req_build_cmd(sd_card_t *card, sd_command_t *cmd, request_t *req) +{ + u32 merge_enable = 0; + + merge_enable = + card->host->cfg->host_item.test_tag_queue_capability.enable_srb_merge; + + /* set cmd index */ + + /* read */ + if (req->data_dir == DATA_DIR_IN) { + if ((req->tag_req_t.sec_cnt == 1) + && (card->inf_trans_enable == 0) && (merge_enable == 0)) + cmd->cmd_index = SD_CMD17; + else { + cmd->cmd_index = SD_CMD18; + cmd->cmd_flag |= CMD_FLG_MULDATA; + } + } else { + if ((req->tag_req_t.sec_cnt == 1) + && (card->inf_trans_enable == 0) && (merge_enable == 0)) + cmd->cmd_index = SD_CMD24; + else { + cmd->cmd_index = SD_CMD25; + cmd->cmd_flag |= CMD_FLG_MULDATA; + } + } + /* set arg */ + cmd->argument = req->tag_req_t.sec_addr; + /* set flg */ + cmd->cmd_flag |= CMD_FLG_R1 | CMD_FLG_RESCHK; + + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, + "Exit %s cmd_idx=%x flg=%x\n", __func__, cmd->cmd_index, + cmd->cmd_flag); + return TRUE; +} + +/* + * + * Function Name: tq_adma2_prebuild_io + * + * Abstract: + * + * build ADMA2 desc table, prepare cmd + * + * Input: + * + * void *p [in]: Pointer to the bht_dev_ext_t * + * node_t *node [in] : the node need prebuild + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: build ok + * Notes: + * + * Caller: + * + */ +bool tq_adma2_prebuild_io(void *p, node_t *node) +{ + bht_dev_ext_t *pdx = (bht_dev_ext_t *) p; + dma_desc_buf_t *pdma = 0; + srb_ext_t *pext = node_2_srb_ext(node); + request_t *req = &pext->req; + sd_command_t *cmd = &pext->cmd; + bool ret = FALSE; + bool dma_64bit = node->card->host->bit64_enable ? TRUE : FALSE; + bool data_26bit_len = + pdx->cfg->host_item.test_dma_mode_setting.enable_dma_26bit_len ? TRUE : FALSE; + u32 dbg_var = 0; + + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + /* check parameters */ + if (req->srb_sg_len == 0 || req->srb_sg_list == NULL) { + DbgErr("%s null sglist\n", __func__); + ret = FALSE; + goto exit; + } + + /* 1.build cmd arg */ + os_memset(cmd, 0, sizeof(sd_command_t)); + req_build_cmd(node->card, cmd, req); + cmd->cmd_flag |= CMD_FLG_ADMA2; + + if (req->gg8_ddr200_workaround) + cmd->gg8_ddr200_workaround = 1; + else + cmd->gg8_ddr200_workaround = 0; + + cmd_set_auto_cmd_flag(&pdx->card, &cmd->cmd_flag); + + /* 2.alloc dma desc buf */ + + /* TODO max_len for 64bit dma */ + pdma = node_get_desc_res(node, MAX_ADMA2_TABLE_LEN); + if (pdma == NULL) { + DbgErr("%s get desc res failed\n", __func__); + ret = FALSE; + goto exit; + } + node->phy_node_buffer.head = *pdma; + /* 3.build ADMA2 Desc */ + dbg_var = req->srb_sg_list[0].Length; + + if (req->gg8_ddr200_workaround) { + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, + "Add NOP desc\n"); + node->phy_node_buffer.end = + build_adma2_desc_nop(req->srb_sg_list, req->srb_sg_len, + (byte *) pdma->va, pdma->len, + dma_64bit, data_26bit_len); + } else { + node->phy_node_buffer.end = build_adma2_desc(req->srb_sg_list, + req->srb_sg_len, + (byte *) pdma->va, + pdma->len, + dma_64bit, + data_26bit_len); + } + + if (node->phy_node_buffer.end.va == NULL) + ret = FALSE; + else + ret = TRUE; + +exit: + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s ret=%d\n", + __func__, ret); + return ret; +} + +/* + * + * Function Name: tq_adma2_build_io + * + * Abstract: + * + * build io for adma2 + * + * Input: + * + * void *p [in]: Pointer to the bht_dev_ext_t * + * node_t *node [in] : the node need build + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: build ok + * Notes: + * + * Caller: + * + */ +bool tq_adma2_build_io(void *p, node_t *node) +{ + bht_dev_ext_t *pdx = (bht_dev_ext_t *) p; + req_queue_t *rq = pdx->tag_queue.wq_build; + srb_ext_t *pext = node_2_srb_ext(node); + request_t *req = &pext->req; + sd_command_t *cmd = &pext->cmd; + sd_data_t *data = &rq->sd_data; + bool ret = FALSE; + + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + /* 1.bind cmd to TQ current queue(can't bind when prebuild stage for sync) */ + rq->priv = cmd; + /* 2.bind data to cmd */ + cmd->data = &rq->sd_data; + data->dir = req->data_dir; + data->data_mng.total_bytess = req->tag_req_t.sec_cnt * SD_BLOCK_LEN; + /* 3.cfg system addr */ + data->data_mng.sys_addr = node->general_desc_tbl.pa; + + ret = TRUE; + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s ret=%d\n", + __func__, ret); + return ret; + +} + +/* + * + * Function Name: tq_adma2_send_command + * + * Abstract: + * + * adma2 issue cmd + * + * Input: + * + * void *p [in]: Pointer to the bht_dev_ext_t * + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: issue cmd ok + * Notes: + * + * Caller: + * + */ +static bool tq_adma2_send_command(void *p) +{ + bht_dev_ext_t *pdx = (bht_dev_ext_t *) p; + sd_card_t *card = &(pdx->card); + tag_queue_t *ptq = &pdx->tag_queue; + req_queue_t *rq = pdx->tag_queue.wq_cur; + host_cmd_req_t *cmd_irq_req = &ptq->cmd_req; + sd_command_t *pcmd = (sd_command_t *) rq->priv; + bool ret = FALSE; + + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + /* bind card */ + cmd_irq_req->card = card; + /* set cmd type */ + pcmd->sd_cmd = 1; + /* generate reg */ + cmd_generate_reg(card, pcmd); + /* issue cmd */ + ret = + cmd_execute_sync3(card, pcmd, cmd_irq_req, tag_queue_isr, + tq_issue_post_cb); +#if DBG || _DEBUG + if (ret == FALSE) { + if (g_dbg_ctrl & DBG_CTRL_DUMP_DESC) { + req_queue_loop_ctx_ops(ptq->wq_cur, + dump_node_adma2_desc, NULL); + } + } +#endif + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s ret=%d\n", + __func__, ret); + return ret; +} + +bool tq_adma2_unload(void *p) +{ + bht_dev_ext_t *pdx = (bht_dev_ext_t *) p; + sd_card_t *card = &(pdx->card); + bool ret = FALSE; + + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* Resorte current DMA mode */ + host_transfer_init(&pdx->host, card->inf_trans_enable, FALSE); + ret = TRUE; + + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s ret=%d\n", + __func__, ret); + return ret; +} + +/* + * + * Function Name: tq_adma2_mode_init + * + * Abstract: + * + * init TQ ADMA2 mode cbs + * + * Input: + * + * transfer_cb_t *ops [in]: Pointer to the transfer_cb_t + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: init ok + * Notes: + * + * Caller: + * + */ + +bool tq_adma2_mode_init(transfer_cb_t *ops) +{ + DbgInfo(MODULE_TQ_DMA, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + os_memset(ops, 0, sizeof(transfer_cb_t)); + ops->init_io = tq_adma2_init_ctx; + ops->prebuild_io = tq_adma2_prebuild_io; + ops->build_io = tq_adma2_build_io; + ops->issue_transfer = tq_adma2_send_command; + ops->unload = tq_adma2_unload; + DbgInfo(MODULE_TQ_DMA, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + return TRUE; +} + +#define ADMA2_DESC_LINK_SUPPORT 1 + +#if (ADMA2_DESC_LINK_SUPPORT) + +/* + * static bool clean_int_for_adma2_inf_tb(u8 *link_addr, bool dma_64bit) + * { + * u32 *ptb = 0; + * + * ptb = (u32 *) (link_addr); + * ptb--; + * if (dma_64bit == TRUE) { + * ptb--; + * ptb--; + * } + * ptb--; + * ptb--; + * *ptb &= ~(ADMA2_DESC_INT_BIT); + * return TRUE; + * } + */ + +static bool update_link_for_adma2_inf_tb(u8 **link_addr, bool dma_64bit) +{ + u32 *ptb = 0; + + ptb = (u32 *) (*link_addr); + if (ptb == NULL) { + DbgErr("%s invalid poniter\n", __func__); + return FALSE; + } + ptb--; + if (dma_64bit == TRUE) { + ptb--; + ptb--; + } + ptb--; + ptb--; + *ptb = ADMA2_DESC_LINK_VALID; + /* update pa */ + ptb++; + (*link_addr) = (u8 *) ptb; + + return TRUE; +} +#else +static bool merge_adma2_inf_tb(u8 *pdesc, u8 *pdesc_end, u8 **link_addr, + bool dma_64bit) +{ + u32 *ptb = 0; + u8 *pbuf = 0; + + ptb = (u32 *) (*link_addr); + ptb--; + if (dma_64bit == TRUE) { + ptb--; + ptb--; + } + ptb--; + ptb--; + /* merge adma2 tb */ + pbuf = (u8 *) ptb; + for (; pdesc < pdesc_end;) + *(pbuf++) = *(pdesc++); + /* update pa */ + (*link_addr) = pbuf; + + return TRUE; + +} + +#endif + +/* + * + * Function Name: tq_adma2_inf_build_io + * + * Abstract: + * + * build adma2 infinite io + * + * Input: + * + * void *p [in]: Pointer to the bht_dev_ext_t + * node_t *node [in]:pointer to node which build for + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: build ok + * Notes: + * + * Caller: + * + */ +bool tq_adma2_inf_build_io(void *p, node_t *node) +{ + bht_dev_ext_t *pdx = (bht_dev_ext_t *) p; + req_queue_t *rq = pdx->tag_queue.wq_build; + srb_ext_t *pext = node_2_srb_ext(node); + request_t *req = &pext->req; + sd_command_t *cmd = &pext->cmd; + sd_data_t *data = &rq->sd_data; + bool ret = FALSE; + u32 flg = 0; + bool dma_64bit = node->card->host->bit64_enable ? TRUE : FALSE; + u32 merge_enable = + pdx->cfg->host_item.test_tag_queue_capability.enable_srb_merge; + + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + + if (merge_enable == TRUE) { + if (rq->adma2_last_req.data_dir != DATA_DIR_NONE) { + if (FALSE == + tq_judge_request_continuous(card_is_low_capacity + (node->card), + rq->adma2_last_req.data_dir, + rq->adma2_last_req.sec_addr, + rq->adma2_last_req.sec_cnt, + req->data_dir, + req->tag_req_t.sec_addr)) { + /* not continue build case */ + ret = FALSE; + goto exit; + } else { + if (pdx->tag_queue.adma2_inf_link_addr == NULL) { + /* + * not link case, for seq but nfcu not match case + * fix adma2 merge transfer bug(LinuxStorDriver Issue #3: + * Insert USHII Card ,Lead the Os Hang) + */ + ret = FALSE; + goto exit; + } + /* non-first node */ + flg = + cmd_can_use_inf(node->card, data->dir, + cmd->argument, + req->tag_req_t.sec_cnt); + if (flg == 0) { + /* can't merge case */ + ret = FALSE; + goto exit; + } +#if (ADMA2_DESC_LINK_SUPPORT) + if (FALSE == + update_link_for_adma2_inf_tb(& + (pdx->tag_queue.adma2_inf_link_addr), + dma_64bit)) { + /* can't merge case */ + ret = FALSE; + goto exit; + } + update_adma2_inf_tb(node->phy_node_buffer.end.va, + &(pdx->tag_queue.adma2_inf_link_addr), + &node->phy_node_buffer.head.pa, + dma_64bit); +#else + merge_adma2_inf_tb(node->phy_node_buffer.head.va, + node->phy_node_buffer.end.va, + &(pdx->tag_queue.adma2_inf_link_addr), + dma_64bit); + update_adma2_inf_tb(pdx->tag_queue.adma2_inf_link_addr, + &(pdx->tag_queue.adma2_inf_link_addr), + NULL, dma_64bit); + +#endif + rq->sd_data.data_mng.total_bytess += + req->tag_req_t.sec_cnt * SD_BLOCK_LEN; + /* update */ + rq->adma2_last_req.data_dir = req->data_dir; + rq->adma2_last_req.sec_addr = + req->tag_req_t.sec_addr; + rq->adma2_last_req.sec_cnt = + req->tag_req_t.sec_cnt; + /* update cmd arg for cmd layer merge case update last_sec */ + if (card_is_low_capacity(node->card)) + cmd->argument = + (cmd->argument + + req->tag_req_t.sec_cnt * + SD_BLOCK_LEN) - + rq->sd_data.data_mng.total_bytess; + else + cmd->argument = + (cmd->argument + + req->tag_req_t.sec_cnt) - + (rq->sd_data.data_mng.total_bytess / + SD_BLOCK_LEN); + + ret = TRUE; + goto exit; + } + } + /* update */ + rq->adma2_last_req.data_dir = req->data_dir; + rq->adma2_last_req.sec_addr = req->tag_req_t.sec_addr; + rq->adma2_last_req.sec_cnt = req->tag_req_t.sec_cnt; + } + + /* update */ + + /* 1. build basic adma2 io */ + if (tq_adma2_build_io(p, node) == FALSE) { + DbgErr("buid adm2 io failed\n"); + goto exit; + } + + /* 3. build inifinte table if need */ + + flg = + cmd_can_use_inf(node->card, data->dir, cmd->argument, + req->tag_req_t.sec_cnt); + + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, + "%s card:%x flg:%x\n", __func__, node->card->has_built_inf, + flg); + + /* have build infinite */ + if (node->card->has_built_inf) { + + if (CMD_FLG_INF_CON & flg) { + /* continue case */ + update_adma2_inf_tb(node->phy_node_buffer.end.va, + &(pdx->tag_queue.adma2_inf_link_addr), + &node->phy_node_buffer.head.pa, + dma_64bit); + } else { + /* need stop infinite */ + + /* can build inf */ + if (CMD_FLG_INF_BUILD & flg) { + update_adma2_inf_tb(node->phy_node_buffer.end.va, + &(pdx->tag_queue.adma2_inf_link_addr), + NULL, dma_64bit); + } else { + pdx->tag_queue.adma2_inf_link_addr = NULL; + } + } + } else { + /* can build inf */ + if (CMD_FLG_INF_BUILD & flg) { + update_adma2_inf_tb(node->phy_node_buffer.end.va, + &(pdx->tag_queue.adma2_inf_link_addr), + NULL, dma_64bit); + } else { + pdx->tag_queue.adma2_inf_link_addr = NULL; + } + + } + /* updae cmd flag */ + cmd->cmd_flag |= flg; + + ret = TRUE; +exit: + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s ret=%d\n", + __func__, ret); + return ret; + +} + +/* + * + * Function Name: tq_adma2_inf_send_command + * + * Abstract: + * + * issue adma2 infinite command + * + * Input: + * + * void *p [in]: Pointer to the bht_dev_ext_t + * + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: issue ok + * Notes: + * + * Caller: + * + */ +bool tq_adma2_inf_send_command(void *p) +{ + bht_dev_ext_t *pdx = (bht_dev_ext_t *) p; + sd_card_t *card = &(pdx->card); + tag_queue_t *ptq = &pdx->tag_queue; + req_queue_t *rq = pdx->tag_queue.wq_cur; + host_cmd_req_t *cmd_irq_req = &ptq->cmd_req; + sd_command_t *pcmd = (sd_command_t *) rq->priv; + bool ret = FALSE; + + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + /* 1.bind card */ + cmd_irq_req->card = card; + /* 2.set cmd type */ + pcmd->sd_cmd = 1; + + if (card->has_built_inf) { + if (!(pcmd->cmd_flag & CMD_FLG_INF_CON)) { + sd_command_t sd_cmd; + + ret = card_stop_infinite(card, FALSE, &sd_cmd); + if (ret == FALSE) { + DbgErr("%s stop infinite failed\n", + __func__); + pcmd->err = sd_cmd.err; + goto exit; + } + + } + } else { + if (pcmd->cmd_flag & CMD_FLG_INF_CON) { + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, + "%s clear INF_CON for infinite no build\n", + __func__); + pcmd->cmd_flag &= ~(CMD_FLG_INF_CON); + pcmd->cmd_flag |= CMD_FLG_INF_BUILD; + } + } + /* 3.generate reg */ + + /* todo check return */ + cmd_generate_reg(card, pcmd); + + /* 4.issue cmd */ + ret = + cmd_execute_sync3(card, pcmd, cmd_irq_req, tag_queue_isr, + tq_issue_post_cb); +#if DBG || _DEBUG + if (ret == FALSE) { + if (g_dbg_ctrl & DBG_CTRL_DUMP_DESC) { + req_queue_loop_ctx_ops(ptq->wq_cur, + dump_node_adma2_desc, NULL); + } + } +#endif +exit: + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s ret=%d\n", + __func__, ret); + return ret; +} + +bool tq_adma2_inf_unload(void *p) +{ + bht_dev_ext_t *pdx = (bht_dev_ext_t *) p; + sd_card_t *card = &(pdx->card); + req_queue_t *rq = pdx->tag_queue.wq_cur; + sd_command_t *pcmd = (sd_command_t *) rq->priv; + bool ret = FALSE; + + if (card->has_built_inf) { + sd_command_t sd_cmd; + + ret = card_stop_infinite(card, FALSE, &sd_cmd); + if (ret == FALSE) { + DbgErr("%s stop infinite failed\n", __func__); + pcmd->err = sd_cmd.err; + goto exit; + } + } + ret = TRUE; +exit: + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s ret=%d\n", + __func__, ret); + return ret; +} + +/* + * + * Function Name: tq_adma2_inf_mode_init + * + * Abstract: + * + * init adma2 infinite mode + * + * Input: + * + * transfer_cb_t *ops [in]: Pointer to the callback + * + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: init ok + * Notes: + * + * Caller: + * + */ +bool tq_adma2_inf_mode_init(transfer_cb_t *ops) +{ + DbgInfo(MODULE_TQ_DMA, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + os_memset(ops, 0, sizeof(transfer_cb_t)); + tq_adma2_mode_init(ops); + ops->build_io = tq_adma2_inf_build_io; + ops->issue_transfer = tq_adma2_inf_send_command; + ops->unload = tq_adma2_inf_unload; + DbgInfo(MODULE_TQ_DMA, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + return TRUE; +} diff --git a/drivers/scsi/bht/tagqueue/tqadma3.c b/drivers/scsi/bht/tagqueue/tqadma3.c new file mode 100644 index 000000000000..4a3113e5aef5 --- /dev/null +++ b/drivers/scsi/bht/tagqueue/tqadma3.c @@ -0,0 +1,504 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: tqadma3.c + * + * Abstract: handle tagqueue adma3 transfer cb ops + * + * Version: 1.00 + * + * Author: Chuanjin + * + * Environment: OS Independent + * + * History: + * + * 9/11/2014 Creation Chuanjin + */ + +#include "../include/basic.h" +#include "../include/tqapi.h" +#include "../include/debug.h" +#include "../include/cmdhandler.h" +#include "../include/card.h" +#include "tq_util.h" +#include "../include/util.h" +#include "tq_trans_api.h" + +/* + * + * Function Name: get_one_desc_res + * + * Abstract: + * + * get one descriptor dma buffer resource + * + * Input: + * + * dma_desc_buf_t *cur [in]: Pointer to the dma buffer resource + * u32 max_use_size [in] : the max use size for overflow buffer check + * + * Output: + * + * None. + * + * Return value: + * + * NULL: failed to get dma resource + * other: get the dma resource + * Notes: + * + * Caller: + * + */ +dma_desc_buf_t *get_one_desc_res(dma_desc_buf_t *cur, u32 max_use_size) +{ + dma_desc_buf_t *p = cur; + + if (max_use_size > p->len) { + DbgErr("%s no enough buf for desc %x > (%x)\n", __func__, + max_use_size, p->len); + return NULL; + } + return cur; +} + +/* + * + * Function Name: put_one_desc_res + * + * Abstract: + * + * put one descriptor size for update dma buffer resource + * + * Input: + * + * dma_desc_buf_t *cur [in]: Pointer to the dma buffer resource + * size [in] : the use size + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: put successful + * FALSE: put failed + * Notes: + * + * Caller: + * + */ +bool put_one_desc_res(dma_desc_buf_t *cur, u32 size) +{ + return resize_dma_buf(cur, size); +} + +dma_desc_buf_t *get_one_integrate_desc_res(req_queue_t *rq) +{ + return &rq->adma3_integrate_tbl_cur; +} + +bool put_one_integrate_desc(req_queue_t *rq, u32 size) +{ + return resize_dma_buf(&rq->adma3_integrate_tbl_cur, size); +} + +bool tq_adma3_reset_integrate(req_queue_t *rq) +{ + bool ret = FALSE; + + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, + "Enter %s rq(%d)\n", __func__, rq->id); + rq->adma3_integrate_tbl_cur = rq->adma3_integrate_tbl; + if (rq->adma3_integrate_tbl_cur.va == NULL) { + DbgErr("%s null va\n", __func__); + goto exit; + ret = FALSE; + } + os_memset(rq->adma3_integrate_tbl_cur.va, 0, + rq->adma3_integrate_tbl.len); + ret = TRUE; +exit: + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); + return TRUE; +} + +/* + * + * Function Name: tq_adma3_init_ctx + * + * Abstract: + * + * init adma3 IO context + * + * Input: + * + * void *p [in]: Pointer to the bht_dev_ext_t + * + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: init successful + * FALSE: init failed + * Notes: + * + * Caller: + * + */ +static bool tq_adma3_init_ctx(void *p) +{ + bool ret = FALSE; + bht_dev_ext_t *pdx = (bht_dev_ext_t *) p; + req_queue_t *rq = pdx->tag_queue.wq_build; + + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + ret = tq_adma3_reset_integrate(rq); + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s %d\n", + __func__, ret); + return ret; +} + +/* + * + * Function Name: tq_adma3_prebuild_io + * + * Abstract: + * + * build ADMA3 cmd & adma2 desc table, however not build integrate table. + * + * Input: + * + * void *p [in]: Pointer to the bht_dev_ext_t * + * node_t *node [in] : the node need prebuild + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: build ok + * Notes: + * + * Caller: + * + */ +bool tq_adma3_prebuild_io(void *p, node_t *node) +{ + bht_dev_ext_t *pdx = (bht_dev_ext_t *) p; + dma_desc_buf_t *pdma = 0, dma_buf; + srb_ext_t *pext = node_2_srb_ext(node); + request_t *req = &pext->req; + sd_command_t *cmd = &pext->cmd; + sd_card_t *card = node->card; + bool ret = FALSE; + sd_data_t mdata; + u32 size = 0; + bool dma_64bit = card->host->bit64_enable ? TRUE : FALSE; + bool data_26bit_len = + pdx->cfg->host_item.test_dma_mode_setting.enable_dma_26bit_len ? TRUE : FALSE; + + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* check parameters */ + if (req->srb_sg_len == 0 || req->srb_sg_list == NULL) { + DbgErr("%s null sglist\n", __func__); + ret = FALSE; + goto exit; + } + + /* 1.build cmd arg */ + os_memset(cmd, 0, sizeof(sd_command_t)); + req_build_cmd(card, cmd, req); + cmd_set_auto_cmd_flag(&pdx->card, &cmd->cmd_flag); + cmd->cmd_flag |= CMD_FLG_ADMA3; + /* 2. generate regs */ + cmd->data = &mdata; + mdata.dir = req->data_dir; + mdata.data_mng.total_bytess = req->tag_req_t.sec_cnt * SD_BLOCK_LEN; + /* set cmd type */ + cmd->sd_cmd = 1; + cmd_generate_reg(card, cmd); + + /* 3.alloc dma desc buf */ + + /* TODO max size */ + pdma = node_get_desc_res(node, MAX_ADMA2_TABLE_LEN); + + if (pdma == NULL) { + DbgErr("Adma3 Get desc res failed\n"); + ret = FALSE; + goto exit; + } + dma_buf = *pdma; + /* no change node desc buffer, or cause len small */ + pdma = &dma_buf; + node->phy_node_buffer.head = *pdma; + + /* 4.build cmd desc */ + size = build_card_cmd_desc(card, pdma->va, cmd); + + resize_dma_buf(pdma, size); + /* 5.build ADMA2 Desc */ + node->phy_node_buffer.end = build_adma2_desc(req->srb_sg_list, + req->srb_sg_len, + (byte *) pdma->va, + pdma->len, dma_64bit, + data_26bit_len); + if (node->phy_node_buffer.end.va == NULL) { + DbgErr("%s build adm2 desc failed\n", __func__); + ret = FALSE; + goto exit; + } else + ret = TRUE; + /* integrate table must delay to build stage. or can't support multi */ + +exit: + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s ret=%d\n", + __func__, ret); + return ret; +} + +/* + * + * Function Name: tq_adma3_build_io + * + * Abstract: + * + * build adma3 context + * + * Input: + * + * void *p [in]: Pointer to the bht_dev_ext_t + * node_t *node [in]: pointer to node which need build + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: build successful + * FALSE: build failed + * Notes: + * + * Caller: + * + */ +bool tq_adma3_build_io(void *p, node_t *node) +{ + bht_dev_ext_t *pdx = (bht_dev_ext_t *) p; + dma_desc_buf_t *pdma = 0; + req_queue_t *rq = pdx->tag_queue.wq_build; + request_t *req = &node_2_srb_ext(node)->req; + sd_command_t *cmd = &node_2_srb_ext(node)->cmd; + sd_data_t *data = &rq->sd_data; + bool ret = FALSE; + byte *pdesc = 0; + u32 size = 0; + bool dma_64bit = node->card->host->bit64_enable ? TRUE : FALSE; + + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + + if (tq_adma3_prebuild_io(p, node) == FALSE) + goto exit; + /* cfg integrated desc */ + pdma = get_one_integrate_desc_res(rq); + pdesc = + build_integrated_desc(pdma->va, &(node->phy_node_buffer.head.pa), + dma_64bit); + size = pp_ofs(pdesc, pdma->va); + put_one_integrate_desc(rq, size); + /* 1.bind cmd to TQ current queue(can't bind when prebuild stage for sync) */ + rq->priv = cmd; + /* 2.bind data to cmd */ + cmd->data = &rq->sd_data; + data->dir = req->data_dir; + data->data_mng.total_bytess = req->tag_req_t.sec_cnt * SD_BLOCK_LEN; + /* 3.cfg system addr */ + data->data_mng.sys_addr = rq->adma3_integrate_tbl.pa; + ret = TRUE; +exit: + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s ret=%d\n", + __func__, ret); + return ret; +} + +/* + * + * Function Name: tq_adma3_send_command + * + * Abstract: + * + * send adma3 command + * + * Input: + * + * void *p [in]: Pointer to the bht_dev_ext_t + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: issue cmd successful + * FALSE: issue failed + * Notes: + * + * Caller: + * + */ + +void dump_adma3_integrate_desc(u8 *desc, bool dma_64bit, u32 cnt) +{ + u32 size = 0; + + if (dma_64bit == TRUE) + size = cnt * ADMA3_INTEGRATEDDESC_128BIT_ITEM_LEN; + else + size = cnt * ADMA3_INTEGRATEDDESC_ITEM_LEN; + DbgErr("%s integrate cnt=%d\n", __func__, cnt); + dbg_dump_general_desc_tb(desc, size); +} + +/* + * + * Function Name:dump_node_adma3_desc + * + * Abstract: + * + * dump node adma3 desc + * + * Input: + * + * + * Output: + * + * + * + * Return value: + * + * Notes: + * + * Caller: + * + */ +static bool dump_node_adma3_desc(node_t *node, void *ctx) +{ + phy_addr_t sys_addr; + u8 *desc = node->phy_node_buffer.head.va; + u8 *desc_end = node->phy_node_buffer.end.va; + + sys_addr = node->phy_node_buffer.head.pa; + DbgErr("sys addrl %x addrh %x\n", os_get_phy_addr32l(sys_addr), + os_get_phy_addr32h(sys_addr)); + dump_adma2_desc(desc, desc_end); + return TRUE; +} + +bool tq_adma3_send_command(void *p) +{ + bht_dev_ext_t *pdx = (bht_dev_ext_t *) p; + sd_card_t *card = &(pdx->card); + tag_queue_t *ptq = &pdx->tag_queue; + req_queue_t *rq = pdx->tag_queue.wq_cur; + host_cmd_req_t *cmd_irq_req = &ptq->cmd_req; + sd_command_t *pcmd = (sd_command_t *) rq->priv; + bool ret = FALSE; + bool dma_64bit = card->host->bit64_enable ? TRUE : FALSE; + u32 merge_enable = + pdx->cfg->host_item.test_tag_queue_capability.enable_srb_merge; + + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + /* 1. generate integare table */ + adma3_end_integrated_tb(rq->adma3_integrate_tbl_cur.va, dma_64bit); + + /* bind card */ + cmd_irq_req->card = card; + + /* merge test */ + if (merge_enable) + adma3_merge_io_descriptor(rq, card, dma_64bit); + + /* 3. issue command */ + ret = + cmd_execute_sync3(card, pcmd, cmd_irq_req, tag_queue_isr, + tq_issue_post_cb); + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s ret=%d\n", + __func__, ret); +#if DBG || _DEBUG + if (ret == FALSE) { + if (g_dbg_ctrl & DBG_CTRL_DUMP_DESC) { + u32 cnt = 0; + + cnt = node_list_get_cnt(&ptq->wq_cur->list); + DbgErr("ADMA3 sys addrl %x addrh %x\n", + os_get_phy_addr32l(rq->adma3_integrate_tbl.pa), + os_get_phy_addr32h(rq->adma3_integrate_tbl.pa)); + dump_adma3_integrate_desc(rq->adma3_integrate_tbl.va, + dma_64bit, cnt); + req_queue_loop_ctx_ops(ptq->wq_cur, + dump_node_adma3_desc, NULL); + } + } +#endif + return ret; +} + +bool tq_adma3_poweroff_need_rebuild(void *p) +{ + /* when card poweroff, adma3 need rebuild transfer ctx */ + return TRUE; +} + +/* + * + * Function Name: tq_adma3_mode_init + * + * Abstract: + * + * init TQ ADMA3 mode cbs + * + * Input: + * + * transfer_cb_t *ops [in]: Pointer to the transfer_cb_t + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: init ok + * Notes: + * + * Caller: + * + */ +bool tq_adma3_mode_init(transfer_cb_t *ops) +{ + DbgInfo(MODULE_TQ_DMA, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + os_memset(ops, 0, sizeof(transfer_cb_t)); + ops->init_io = tq_adma3_init_ctx; + /* for amda3 auto poweroff case, adma3 can't get card type(uhs2 or legacy) */ + ops->prebuild_io = NULL; + ops->build_io = tq_adma3_build_io; + ops->issue_transfer = tq_adma3_send_command; + ops->poweroff_need_rebuild = tq_adma3_poweroff_need_rebuild; + DbgInfo(MODULE_TQ_DMA, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + return TRUE; +} diff --git a/drivers/scsi/bht/tagqueue/tqadma_sdma_like.c b/drivers/scsi/bht/tagqueue/tqadma_sdma_like.c new file mode 100644 index 000000000000..35ba6feb078d --- /dev/null +++ b/drivers/scsi/bht/tagqueue/tqadma_sdma_like.c @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: tqadma_sdma_like.c + * + * Abstract: handle tagqueue sdma_like transfer cb ops + * + * Version: 1.00 + * + * Author: Chuanjin + * + * Environment: OS Independent + * + * History: + * + * 11/1/2014 Creation Chuanjin + */ + +#include "../include/basic.h" +#include "../include/tqapi.h" +#include "../include/debug.h" +#include "../include/cmdhandler.h" +#include "../include/card.h" +#include "tq_util.h" +#include "tq_trans_api.h" +#include "../include/util.h" +#include "../include/hostapi.h" + +/* + * + * Function Name: tq_adma2_sdmalike_prebuild_io + * + * Abstract: + * + * prebuild sdma-like mode adma2 io + * + * Input: + * + * void * p [in]: Pointer to the bht_dev_ext_t * + * node_t *node [in] : the node need prebuild + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: build ok + * Notes: + * + * Caller: + * + */ +static bool tq_adma2_sdmalike_prebuild_io(void *p, node_t *node) +{ + srb_ext_t *pext = node_2_srb_ext(node); + request_t *req = &pext->req; + dma_desc_buf_t *pdma = 0; + bool ret = TRUE; + + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + /* 1. get sdma like buf address */ + pdma = &node->data_tbl; + + /* 2. mark it's sdma-like node */ + node->sdma_like = 1; + + /* 3. generate sdma like sglist table */ + + if (gen_sdma_like_sgl(req, pdma) == FALSE) { + DbgErr("%s sdma like sgl failed\n", __func__); + ret = FALSE; + goto exit; + } + + /* 4. call adma2 build API */ + ret = tq_adma2_prebuild_io(p, node); +exit: + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); + return ret; +} + +/* + * + * Function Name: tq_adma2_sdmalike_build_io + * + * Abstract: + * + * build sdma-like io + * + * Input: + * + * void * p [in]: Pointer to the bht_dev_ext_t * + * node_t *node [in] : the node need build + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: build ok + * Notes: + * + * Caller: + * + */ +bool tq_adma2_sdmalike_copy(void *p, node_t *node) +{ + srb_ext_t *pext = node_2_srb_ext(node); + request_t *req = &pext->req; + + /* copy for write case */ + if (req->data_dir == DATA_DIR_OUT) { + os_memcpy(node->data_tbl.va, req->srb_buff, + req->tag_req_t.sec_cnt * SD_BLOCK_LEN); + } + + return TRUE; + +} + +static bool tq_adma2_sdmalike_build_io(void *p, node_t *node) +{ +#if (!CFG_OS_LINUX) + tq_adma2_sdmalike_copy(p, node); +#endif + return tq_adma2_build_io(p, node); + +} + +/* + * + * Function Name: tq_adma2_sdmalike_mode_init + * + * Abstract: + * + * init TQ ADMA2 sdma-like mode cbs + * + * Input: + * + * transfer_cb_t *ops [in]: Pointer to the transfer_cb_t + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: init ok + * Notes: + * + * Caller: + * + */ +bool tq_adma2_sdmalike_mode_init(transfer_cb_t *ops) +{ + DbgInfo(MODULE_TQ_DMA, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + os_memset(ops, 0, sizeof(transfer_cb_t)); + tq_adma2_mode_init(ops); + ops->build_io = tq_adma2_sdmalike_build_io; + ops->prebuild_io = tq_adma2_sdmalike_prebuild_io; + DbgInfo(MODULE_TQ_DMA, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + return TRUE; +} + +/* + * + * Function Name: tq_adma2_inf_sdmalike_build_io + * + * Abstract: + * + * build adma2 infinite sdma-like io + * + * Input: + * + * void * p [in]: Pointer to the bht_dev_ext_t + * node_t *node [in]:pointer to node which build for + * + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: build ok + * Notes: + * + * Caller: + * + */ +static bool tq_adma2_inf_sdmalike_build_io(void *p, node_t *node) +{ +#if (!CFG_OS_LINUX) + tq_adma2_sdmalike_copy(p, node); +#endif + return tq_adma2_inf_build_io(p, node); + +} + +/* + * + * Function Name: tq_adma2_inf_sdmalike_mode_init + * + * Abstract: + * + * init TQ ADMA2 infinite sdma-like mode cbs + * + * Input: + * + * transfer_cb_t *ops [in]: Pointer to the transfer_cb_t + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: init ok + * Notes: + * + * Caller: + * + */ +bool tq_adma2_inf_sdmalike_mode_init(transfer_cb_t *ops) +{ + DbgInfo(MODULE_TQ_DMA, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + os_memset(ops, 0, sizeof(transfer_cb_t)); + tq_adma2_sdmalike_mode_init(ops); + ops->build_io = tq_adma2_inf_sdmalike_build_io; + ops->issue_transfer = tq_adma2_inf_send_command; + ops->unload = tq_adma2_inf_unload; + DbgInfo(MODULE_TQ_DMA, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + return TRUE; +} + +/* + * + * Function Name: tq_adma2_sdmalike_prebuild_io + * + * Abstract: + * + * prebuild sdma-like mode adma2 io + * + * Input: + * + * void * p [in]: Pointer to the bht_dev_ext_t * + * node_t *node [in] : the node need prebuild + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: build ok + * Notes: + * + * Caller: + * + */ +static bool tq_adma3_sdmalike_prebuild_io(void *p, node_t *node) +{ + srb_ext_t *pext = node_2_srb_ext(node); + request_t *req = &pext->req; + dma_desc_buf_t *pdma = 0; + bool ret = TRUE; + + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* 1. get sdma like buf address */ + pdma = &node->data_tbl; + + /* 2. mark it's sdma-like node */ + node->sdma_like = 1; + + /* 3. generate sdma like sglist table */ + + if (gen_sdma_like_sgl(req, pdma) == FALSE) { + DbgErr("%s sdma like sgl failed\n", __func__); + ret = FALSE; + goto exit; + } + + /* 4. call adma3 build API */ + ret = tq_adma3_prebuild_io(p, node); +exit: + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s ret=%x\n", + __func__, ret); + return ret; +} + +/* + * + * Function Name: tq_adma3_sdmalike_build_io + * + * Abstract: + * + * build adma3 sdma-like io + * + * Input: + * + * void * p [in]: Pointer to the bht_dev_ext_t * + * node_t *node [in] : the node need build + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: build ok + * Notes: + * + * Caller: + * + */ +static bool tq_adma3_sdmalike_build_io(void *p, node_t *node) +{ + if (tq_adma3_sdmalike_prebuild_io(p, node) == FALSE) + return FALSE; +#if (!CFG_OS_LINUX) + tq_adma2_sdmalike_copy(p, node); +#endif + return tq_adma3_build_io(p, node); + +} + +/* + * + * Function Name: tq_adma3_sdmalike_mode_init + * + * Abstract: + * + * init TQ ADMA2 sdma-like mode cbs + * + * Input: + * + * transfer_cb_t *ops [in]: Pointer to the transfer_cb_t + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: init ok + * Notes: + * + * Caller: + * + */ +bool tq_adma3_sdmalike_mode_init(transfer_cb_t *ops) +{ + DbgInfo(MODULE_TQ_DMA, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + os_memset(ops, 0, sizeof(transfer_cb_t)); + tq_adma3_mode_init(ops); + ops->build_io = tq_adma3_sdmalike_build_io; + ops->prebuild_io = NULL; + DbgInfo(MODULE_TQ_DMA, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + return TRUE; +} diff --git a/drivers/scsi/bht/tagqueue/tqpolicy.c b/drivers/scsi/bht/tagqueue/tqpolicy.c new file mode 100644 index 000000000000..6946c57e21ce --- /dev/null +++ b/drivers/scsi/bht/tagqueue/tqpolicy.c @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: tqpolicy.c + * + * Abstract: handle tagqueue policy for transfer mode + * + * Version: 1.00 + * + * Author: Chuanjin + * + * Environment: OS Independent + * + * History: + * + * 9/4/2014 Creation Chuanjin + */ + +#include "../include/basic.h" +#include "../include/cardapi.h" +#include "../include/tqapi.h" +#include "../include/debug.h" +#include "tq_trans_api.h" +#include "../include/util.h" +#include "../include/cmdhandler.h" + +/* + * consider below condition: + * 1.merge enable + * 2.infinite transfer enable + * 3.DMA mode: ADMA3 support multi IO + */ + +/* + * if return TRUE, mean only build one IO. + * if return FALSE, maybe build more then one IO for one transfer. + */ +bool tag_queue_policy_break(bht_dev_ext_t *pdx) +{ + u32 dma_mode = pdx->tag_queue.cur_dma_mode; + u32 merge_enable = + pdx->cfg->host_item.test_tag_queue_capability.enable_srb_merge; + + switch (dma_mode) { + case CFG_TRANS_MODE_SDMA: + return TRUE; + case CFG_TRANS_MODE_ADMA2: + case CFG_TRANS_MODE_ADMA2_SDMA_LIKE: + if (merge_enable == TRUE) + return FALSE; + else + return TRUE; + case CFG_TRANS_MODE_ADMA3: + case CFG_TRANS_MODE_ADMA3_SDMA_LIKE: + return FALSE; + default: + return TRUE; + } +} + +static bool decision_policy_calculus(decision_mgr *mgr, bool bval) +{ + int i = 0; + int sum = 0; + /* add */ + mgr->slot[mgr->idx % mgr->scope] = bval; + mgr->idx++; + + for (i = 0; i < mgr->scope; i++) { + if (mgr->slot[i]) + sum++; + } + + if (mgr->up_flg == TRUE) { + if (sum <= mgr->low_thd) { + mgr->up_flg = FALSE; + mgr->out = FALSE; + goto exit; + } + } else { + if (sum >= mgr->up_thd) { + mgr->up_flg = TRUE; + mgr->out = TRUE; + goto exit; + + } + } +exit: + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, "sum %d %d\n", + sum, mgr->out); + return mgr->out; +} + +void se2_dma_mode_selector(void *p, bool flg) +{ + tag_queue_t *tq = (tag_queue_t *) p; + + if (flg == FALSE) { + if (tq->cfg_dma_mode == CFG_TRANS_MODE_ADMA_MIX_SDMA_LIKE) { + os_atomic_set(&tq->target_dma_mode, + CFG_TRANS_MODE_ADMA3_SDMA_LIKE); + } else { + os_atomic_set(&tq->target_dma_mode, + CFG_TRANS_MODE_ADMA3); + } + } else { + if (tq->cfg_dma_mode == CFG_TRANS_MODE_ADMA_MIX_SDMA_LIKE) { + os_atomic_set(&tq->target_dma_mode, + CFG_TRANS_MODE_ADMA2_SDMA_LIKE); + } else { + os_atomic_set(&tq->target_dma_mode, + CFG_TRANS_MODE_ADMA2); + } + } +} + +void legacy_infinite_dma_mode_selector(void *p, bool flg) +{ + tag_queue_t *tq = (tag_queue_t *) p; + + if (flg == FALSE) { + if (tq->cfg_dma_mode == CFG_TRANS_MODE_ADMA_MIX_SDMA_LIKE) { + os_atomic_set(&tq->target_dma_mode, + CFG_TRANS_MODE_ADMA2_ONLY_SDMA_LIKE); + } else { + os_atomic_set(&tq->target_dma_mode, + CFG_TRANS_MODE_ADMA2_ONLY); + } + } else { + if (tq->cfg_dma_mode == CFG_TRANS_MODE_ADMA_MIX_SDMA_LIKE) { + os_atomic_set(&tq->target_dma_mode, + CFG_TRANS_MODE_ADMA2_SDMA_LIKE); + } else { + os_atomic_set(&tq->target_dma_mode, + CFG_TRANS_MODE_ADMA2); + } + } +} + +void tq_dma_decision_init(decision_mgr *mgr, e_chip_type chip_id, + int scope_size, int up_threshold, int low_threshold) +{ + DbgInfo(MODULE_TQ_FLOW, FEATURE_DRIVER_INIT, NOT_TO_RAM, + "Enter %s chip_id %d\n", __func__, chip_id); + memset(mgr, 0, sizeof(decision_mgr)); + + if (scope_size > MAX_DECISION_SCOPE_SIZE) + scope_size = MAX_DECISION_SCOPE_SIZE; + + if (up_threshold > MAX_DECISION_SCOPE_SIZE) + up_threshold = MAX_DECISION_SCOPE_SIZE; + + if (low_threshold > up_threshold) + low_threshold = up_threshold; + + mgr->scope = scope_size; + mgr->up_thd = up_threshold; + mgr->low_thd = low_threshold; + mgr->up_flg = FALSE; + /* chip related */ + switch (chip_id) { + case CHIP_SEABIRD: + case CHIP_FUJIN2: + case CHIP_SEAEAGLE: + mgr->dma_selector_cb = legacy_infinite_dma_mode_selector; + break; + case CHIP_SEAEAGLE2: + case CHIP_GG8: + case CHIP_ALBATROSS: + mgr->dma_selector_cb = se2_dma_mode_selector; + break; + default: + mgr->dma_selector_cb = 0; + break; + } + DbgInfo(MODULE_TQ_FLOW, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); +} + +void tq_dma_decision_policy(tag_queue_t *tq, sd_card_t *card, + request_t *request) +{ + bool bcont = FALSE; + decision_mgr *mgr = &tq->decision; + + bcont = + tq_judge_request_continuous(card_is_low_capacity(card), + mgr->last_req.data_dir, + mgr->last_req.sec_addr, + mgr->last_req.sec_cnt, + request->data_dir, + request->tag_req_t.sec_addr); + /* update */ + mgr->last_req.data_dir = request->data_dir; + mgr->last_req.sec_addr = request->tag_req_t.sec_addr; + mgr->last_req.sec_cnt = request->tag_req_t.sec_cnt; + DbgInfo(MODULE_TQ_FLOW, FEATURE_RW_TRACE, NOT_TO_RAM, + "Enter %s IO:dir:%d addr:0x%x ,cnt:0x%x (next:0x%x)\n", + __func__, request->data_dir, request->tag_req_t.sec_addr, + request->tag_req_t.sec_cnt, + request->tag_req_t.sec_addr + request->tag_req_t.sec_cnt); + + if (mgr->dma_selector_cb) { + bool dflg = decision_policy_calculus(&tq->decision, bcont); + + mgr->dma_selector_cb(tq, dflg); + } + +} diff --git a/drivers/scsi/bht/tagqueue/tqsdma.c b/drivers/scsi/bht/tagqueue/tqsdma.c new file mode 100644 index 000000000000..dad28d23f714 --- /dev/null +++ b/drivers/scsi/bht/tagqueue/tqsdma.c @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: tqsdma.c + * + * Abstract: handle tagqueue sdma transfer cb ops + * + * Version: 1.00 + * + * Author: Chuanjin + * + * Environment: OS Independent + * + * History: + * + * 9/11/2014 Creation Chuanjin + */ + +#include "../include/basic.h" +#include "../include/tqapi.h" +#include "../include/debug.h" +#include "../include/cmdhandler.h" +#include "../include/card.h" +#include "tq_util.h" +#include "tq_trans_api.h" +#include "../include/util.h" +#include "../include/cardapi.h" + +/* + * + * Function Name: tq_sdma_prebuild_io + * + * Abstract: + * + * prebuild SDMA CMD argument + * + * Input: + * + * void * p [in]: Pointer to the bht_dev_ext_t * + * node_t *node [in] : the node need prebuild + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: build ok + * Notes: + * + * Caller: + * + */ +bool tq_sdma_prebuild_io(void *p, node_t *node) +{ + bht_dev_ext_t *pdx = (bht_dev_ext_t *) p; + srb_ext_t *pext = node_2_srb_ext(node); + request_t *req = &pext->req; + sd_command_t *cmd = &pext->cmd; + bool ret = FALSE; + + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + + /* check parameters */ + if (req->srb_buff == 0) { + DbgErr("%s null srb buf\n", __func__); + ret = FALSE; + goto exit; + } + + /* 1.build cmd arg */ + os_memset(cmd, 0, sizeof(sd_command_t)); + req_build_cmd(node->card, cmd, req); + cmd->cmd_flag |= CMD_FLG_SDMA; + cmd_set_auto_cmd_flag(&pdx->card, &cmd->cmd_flag); + + ret = TRUE; + +exit: + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s\n", + __func__); + return ret; +} + +/* + * + * Function Name: tq_sdma_build_io + * + * Abstract: + * + * build SDMA CMD ,such as dma resource + * + * Input: + * + * void * p [in]: Pointer to the bht_dev_ext_t * + * node_t *node [in] : the node need build + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: build ok + * Notes: + * + * Caller: + * + */ +bool tq_sdma_build_io(void *p, node_t *node) +{ + bht_dev_ext_t *pdx = (bht_dev_ext_t *) p; + req_queue_t *rq = pdx->tag_queue.wq_build; + srb_ext_t *pext = node_2_srb_ext(node); + request_t *req = &pext->req; + sd_command_t *cmd = &pext->cmd; + sd_data_t *data = &rq->sd_data; + bool ret = FALSE; + u32 sdma_bd_len = get_sdma_boudary_size(pdx->cfg); + u32 min_size = 0; + data_dma_mng_t *mgr = &data->data_mng; + dma_desc_buf_t dma; + + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + if (tq_sdma_prebuild_io(p, node) == FALSE) { + DbgErr("%s prebuild io failed\n", __func__); + ret = FALSE; + goto exit; + } + + /* 1.bind cmd to TQ current queue(can't bind when prebuild stage for sync) */ + rq->priv = cmd; + /* 2.bind data to cmd */ + cmd->data = &rq->sd_data; + data->dir = req->data_dir; + mgr->total_bytess = req->tag_req_t.sec_cnt * SD_BLOCK_LEN; + mgr->srb_buffer[0].buff = req->srb_buff; + mgr->offset = 0; + /* fix to 1 */ + mgr->srb_cnt = 1; + + /* 3.cfg system addr */ + + /* align dma buffer */ +#define SDMA_BOUNDARY_MAX_SIZE (512*1024) + if (sdma_bd_len > SDMA_BOUNDARY_MAX_SIZE) { + DbgErr("%s boundary over max %x\n", __func__, sdma_bd_len); + ret = FALSE; + goto exit; + } else { + dma = node->data_tbl; + if (dma_align(&dma, sdma_bd_len) == FALSE) { + DbgErr("%s align failed\n", __func__); + ret = FALSE; + goto exit; + } + } + data->data_mng.sys_addr = dma.pa; + /* set host driver buffer */ + data->data_mng.driver_buff = (byte *) dma.va; + /* for write data to card,need fill data first before transfer */ + if (cmd->data->dir == DATA_DIR_OUT) { + min_size = os_min(sdma_bd_len, mgr->total_bytess); + os_memcpy(mgr->driver_buff, + mgr->srb_buffer[0].buff + mgr->offset, min_size); + mgr->offset += min_size; + } + ret = TRUE; +exit: + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s ret=%d\n", + __func__, ret); + return ret; +} + +/* + * + * Function Name: tq_sdma_send_command + * + * Abstract: + * + * issue SDMA CMD + * Input: + * + * void * p [in]: Pointer to the bht_dev_ext_t * + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: issue ok + * Notes: + * for SMDA mode, now no support auto CMD23, + * driver issue CMD23 before issue read/write CMD. + * Caller: + * + */ +bool tq_sdma_send_command(void *p) +{ + + bht_dev_ext_t *pdx = (bht_dev_ext_t *) p; + sd_card_t *card = &(pdx->card); + tag_queue_t *ptq = &pdx->tag_queue; + req_queue_t *rq = pdx->tag_queue.wq_cur; + host_cmd_req_t *cmd_irq_req = &ptq->cmd_req; + sd_command_t *pcmd = (sd_command_t *) rq->priv; + bool ret = FALSE; + sd_command_t cmd23; + + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Enter %s\n", + __func__); + /* bind card */ + cmd_irq_req->card = card; + /* set cmd type */ + pcmd->sd_cmd = 1; + + /* generate reg */ + + /* SDMA don't use auto CMD23 */ + if ((card->card_type != CARD_UHS2) && (pcmd->cmd_flag & CMD_FLG_AUTO23)) { + /* clear auto23 flag */ + pcmd->cmd_flag &= ~CMD_FLG_AUTO23; + ret = + card_set_blkcnt(card, &cmd23, + pcmd->data->data_mng.total_bytess / + SD_BLOCK_LEN); + if (ret == FALSE) { + DbgErr("%s issue cmd23 failed\n", __func__); + goto exit; + } + } + + if (cmd_generate_reg(card, pcmd) == FALSE) { + DbgErr("%s cmd generate reg error\n", __func__); + ret = FALSE; + goto exit; + } + /* issue cmd */ + ret = cmd_execute_sync2(card, pcmd, cmd_irq_req, tag_queue_isr); +exit: + DbgInfo(MODULE_TQ_DMA, FEATURE_RW_TRACE, NOT_TO_RAM, "Exit %s ret=%d\n", + __func__, ret); + return ret; + +} + +/* + * + * Function Name: tq_sdma_mode_init + * + * Abstract: + * + * init sdma mode + * + * Input: + * + * transfer_cb_t *ops [in]: Pointer to the callback + * + * Output: + * + * None. + * + * Return value: + * + * TRUE: init + * Notes: + * now, SDMA no support infinite & merge feature. + * Caller: + * + */ +bool tq_sdma_mode_init(transfer_cb_t *ops) +{ + DbgInfo(MODULE_TQ_DMA, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Enter %s\n", + __func__); + os_memset(ops, 0, sizeof(transfer_cb_t)); + ops->build_io = tq_sdma_build_io; + ops->issue_transfer = tq_sdma_send_command; + DbgInfo(MODULE_TQ_DMA, FEATURE_DRIVER_INIT, NOT_TO_RAM, "Exit %s\n", + __func__); + return TRUE; +} From patchwork Fri Oct 13 08:34:59 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: liuchang_125125@163.com X-Patchwork-Id: 13420430 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 16569CDB47E for ; Fri, 13 Oct 2023 08:36:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230160AbjJMIgc (ORCPT ); Fri, 13 Oct 2023 04:36:32 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57022 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230123AbjJMIgb (ORCPT ); Fri, 13 Oct 2023 04:36:31 -0400 Received: from m12.mail.163.com (m12.mail.163.com [220.181.12.196]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 8DB53A9; Fri, 13 Oct 2023 01:36:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:Subject:Date:Message-Id:MIME-Version; bh=r86dB yXANDOJwFZxFZCrfeab7vcM6Wm6pQDuzSLDzC8=; b=mpPKktqPvVWtv7yYyhGwV 1aKu7xvOLzzbIseDfybBU2kh37befQ/DIiIO6NSaEi4EuESjCies50jo/AkUBGr+ 3o5wrS5dDNx4JXDCg+P6C90PgOLzBUAZJ/1zHDm/ucKKYlovEqftw2e/DvyK/rLb SYZbST9cgrwVy0AvnFx8Es= Received: from test-Z390-GAMING-X.bayhubtech.com (unknown [58.48.115.170]) by zwqz-smtp-mta-g4-4 (Coremail) with SMTP id _____wBnT+41ASllgCy9AQ--.13444S2; Fri, 13 Oct 2023 16:35:02 +0800 (CST) From: liuchang_125125@163.com To: jejb@linux.ibm.com, martin.petersen@oracle.com, linux-scsi@vger.kernel.org, linux-kernel@vger.kernel.org Cc: mark.tao@bayhubtech.com, shaper.liu@bayhubtech.com, thomas.hu@bayhubtech.com, chevron.li@bayhubtech.com, charl.liu@bayhubtech.com, Charl Liu Subject: [PATCH 9/9] scsi: bht: util: Add the source files related to utility Date: Fri, 13 Oct 2023 16:34:59 +0800 Message-Id: <20231013083459.10381-1-liuchang_125125@163.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 X-CM-TRANSID: _____wBnT+41ASllgCy9AQ--.13444S2 X-Coremail-Antispam: 1Uf129KBjvJXoW3Cr4xZFy8Jr1rJFWxAw47Arb_yoWkAFy5pa 1aga45AanrXws3Cw4xAw18tF45C397AF9IgrZxurWavwnxXrWvq3Z2kFyjyFyxJw17ur1U tFn5KFy2kr4Dtw7anT9S1TB71UUUUUUqnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x0zRnXo3UUUUU= X-Originating-IP: [58.48.115.170] X-CM-SenderInfo: polxux5dqjsiqsvrjki6rwjhhfrp/xtbBnxcIWVetlJKwaAAAs9 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org From: Charl Liu 1.debug: define related functions for debugging 2.util: define utility functions Signed-off-by: Charl Liu --- Change in V1: Add the source files related to utility. --- drivers/scsi/bht/util/debug.c | 413 ++++++++++++++++++++++++++++++++++ drivers/scsi/bht/util/util.c | 141 ++++++++++++ 2 files changed, 554 insertions(+) create mode 100644 drivers/scsi/bht/util/debug.c create mode 100644 drivers/scsi/bht/util/util.c diff --git a/drivers/scsi/bht/util/debug.c b/drivers/scsi/bht/util/debug.c new file mode 100644 index 000000000000..16e87908724f --- /dev/null +++ b/drivers/scsi/bht/util/debug.c @@ -0,0 +1,413 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: debug.c + * + * Abstract: define related functions for debugging + * + * Version: 1.00 + * + * Author: Peter.Guo + * + * Environment: OS Independent + * + * History: + * + * 8/25/2014 Creation Peter.Guo + */ + +#define _NO_CRT_STDIO_INLINE +#include "../include/basic.h" +#include "../include/debug.h" +#include "../include/util.h" + +#define DBG_CHR_IN_LINE 124 +#define DBG_PREFIX "BHT-" + +u32 g_dbg_module = DBG_MODULE_CONTROL; +u32 g_dbg_feature = DBG_FEATURE_CONTROL; +u32 g_dbg_ctrl = DBG_CTRL_CONTROL; + +#if DBG || _DEBUG +static char dbg_module[][14] = { + "BHT-SDHOST : ", + "BHT-VENHOST: ", + "BHT-TRANS : ", + "BHT-CARD : ", + "BHT-CARD : ", + "BHT-CARD : ", + "BHT-CARD : ", + "BHT-TQFLOW : ", + "BHT-TQPLC : ", + "BHT-TQDMA : ", + "BHT-THR : ", + "BHT-REQMNG : ", + "BHT-MAINPM : ", + "BHT-GENIO : ", + "BHT-TIMER : ", + "BHT-THERMAL: ", + "BHT-CFG : ", + "BHT-OTHER : ", + "BHT-OSETAPI: ", + "BHT-OSENTRY: ", + "BHT-OSAPI : ", + "BHT-UNKNOW : " +}; +#endif + +typedef struct { + u32 index; + byte Data[DBG_CHR_IN_LINE]; +} tO2DbgLineInfo; + +#define O2DBG_MAX_COUNT 0x4000 +#define O2DBG_MAX_RAM_SIZE (O2DBG_MAX_COUNT * sizeof(tO2DbgLineInfo)) + +/* Global Debug Virtual Buffer for RAM Debug */ +tO2DbgLineInfo *pO2DbgInfo; +/* Global Ram Buffer item count */ +atomic_t gO2DbgInfoCnt; + +void x_assert(char *str, unsigned int uline) +{ + DbgErr("Assert failed %s line:%u\n", str, uline); +} + +/* + * Function Name: DbgRamInit + * Abstract: This Function is called by driver init entry to allocate Global Memory for Debug + * + * Input: + * Output: + * + * Return value: void + * Notes: + * + */ + +void DbgRamInit(void) +{ + os_atomic_set(&gO2DbgInfoCnt, 0); + pO2DbgInfo = (tO2DbgLineInfo *) os_alloc_vbuff(O2DBG_MAX_RAM_SIZE); + if (pO2DbgInfo != NULL) + os_memset(pO2DbgInfo, 0, O2DBG_MAX_RAM_SIZE); +} + +void DbgRamInitNon(void) +{ + os_atomic_set(&gO2DbgInfoCnt, 0); + pO2DbgInfo = NULL; +} + +/* + * Function Name: DbgRamInit + * Abstract: This Function is called by driver remove entry to free Global Memory for Debug + * + * Input: + * Output: + * + * Return value: void + * Notes: + * + */ +void DbgRamFree(void) +{ + os_atomic_set(&gO2DbgInfoCnt, 0); + if (pO2DbgInfo != NULL) + os_free_vbuff(pO2DbgInfo); + pO2DbgInfo = NULL; + +} + +/* + * Function Name: DbgRamAdd + * Abstract: This Function is used to Add a Debug log to Debug Ram buffer + * + * Input: byte *dbgbuf: The Debug log want to add to ram buffer + * Output: + * + * Return value: void + * Notes: + * + */ +static void DbgRamAdd(byte *dbgbuf) +{ + u32 i; + int cnt = 0; + int index = 0; + u32 len = (u32) strlen(dbgbuf); + + if (pO2DbgInfo == NULL) + return; + cnt = (int)os_atomic_read(&gO2DbgInfoCnt); + os_atomic_add(&gO2DbgInfoCnt, 1); + index = cnt % O2DBG_MAX_COUNT; + + pO2DbgInfo[index].index = cnt; + for (i = 0; i < DBG_CHR_IN_LINE; i++) { + if (i < len) + pO2DbgInfo[index].Data[i] = dbgbuf[i]; + else + pO2DbgInfo[index].Data[i] = '\0'; + } +} + +/* + * Function Name: DbgErr + * Abstract: This Function is used to print errlog and add log to Ram buffer + * + * Input: byte *info: The err log + * Output: + * + * Return value: void + * Notes: + * + */ + +void PrintMsg(byte *info, ...) +{ +#define DBG_PREFIX_MSG "BHT-MSG : " + + byte dbgbuf[512]; + va_list ap; + + va_start(ap, info); + + { + byte *buffer; + + buffer = dbgbuf; + os_memcpy(buffer, DBG_PREFIX_MSG, 13); + buffer += 13; + + vsnprintf(buffer, sizeof(dbgbuf) - 13, info, ap); + } + + va_end(ap); + os_print(dbgbuf); +} + +/* + * Function Name: DbgErr + * Abstract: This Function is used to print errlog and add log to Ram buffer + * + * Input: byte *info: The err log + * Output: + * + * Return value: void + * Notes: + * + */ + +void DbgErr(byte *info, ...) +{ +#define DBG_PREFIX_ERR "BHT-ERROR : " + + byte dbgbuf[512]; + va_list ap; + + va_start(ap, info); + + { + byte *buffer; + + buffer = dbgbuf; + os_memcpy(buffer, DBG_PREFIX_ERR, 13); + buffer += 13; + + vsnprintf(buffer, sizeof(dbgbuf) - 13, info, ap); + } + + va_end(ap); + DbgRamAdd(dbgbuf); + os_print(dbgbuf); +} + +#if DBG || _DEBUG +/* + * Function Name: DbgWarn + * Abstract: This Function is used to print warn log and add log to Ram buffer + * + * Input: u32 module; The module id which to print warn log + * byte ram: whether add the log to ram or not + * byte *info: the string for log + * Output: + * + * Return value: void + * Notes: + * + */ + +void DbgWarn(u32 module, byte ram, byte *info, ...) +{ +#define DBG_PREFIX_WARN "BHT-WARNING: " + + byte dbgbuf[512]; + va_list ap; + + va_start(ap, info); + if (module & g_dbg_module) { + byte *buffer; + + buffer = dbgbuf; + os_memcpy(buffer, DBG_PREFIX_WARN, 13); + buffer += 13; + + vsnprintf(buffer, sizeof(dbgbuf) - 13, info, ap); + } + + va_end(ap); + if (module & g_dbg_module) { + os_print(dbgbuf); + if (ram) + DbgRamAdd(dbgbuf); + } +} + +/* + * Function Name: DbgWarn + * Abstract: This Function is used to print info log and add log to Ram buffer + * + * Input: u32 module; The module id which to print info log + * u32 feature: The feature the info is + * byte ram: whether add the log to ram or not + * byte *info: the string for log + * Output: + * + * Return value: void + * Notes: + * + */ +void DbgInfo(u32 module, u32 feature, byte ram, byte *info, ...) +{ + byte dbgbuf[512]; + va_list ap; + + va_start(ap, info); + if ((module & g_dbg_module) && (g_dbg_feature & feature)) { + byte *buffer; + u32 idx = fls32(module); + + if (idx > 20) + idx = 21; + buffer = dbgbuf; + os_memcpy(buffer, dbg_module[idx], 13); + buffer += 13; + + vsnprintf(buffer, sizeof(dbgbuf) - 13, info, ap); + } + + va_end(ap); + if ((module & g_dbg_module) && (g_dbg_feature & feature)) { + os_print(dbgbuf); + if (ram) + DbgRamAdd(dbgbuf); + } +} + +#ifdef DBG_PERFORMANCE +static u64 cputick2_100ns(u64 period, u64 freq) +{ + + u64 timeIn100ns = 0; + + if (freq > 0) { + /* difference between performance counters, needs to convert to 100ns. */ + u64 countersDiff = period; + + /* get seconds */ + timeIn100ns = countersDiff / freq; + + /* get milliseconds */ + countersDiff = (countersDiff % freq) * 1000; + timeIn100ns *= 1000; + timeIn100ns += countersDiff / freq; + + /* get 100 nanoseconds */ + countersDiff = (countersDiff % freq) * 10000; + timeIn100ns *= 10000; + timeIn100ns += countersDiff / freq; + } + + return timeIn100ns; + +} + +void calc_req_start(tPerTick *tick, u32 sec_cnt, bool bWrite) +{ + u64 period = 0; + u64 cpu; + + period = os_get_performance_tick(&cpu); + if (tick->tick_io_end == 0) { + os_memset(tick, 0, sizeof(tPerTick)); + /* not calculate */ + goto next; + } + + /* not continue and not 1MB print */ + if ((bWrite != tick->last_dir || sec_cnt != 0x800) + && (tick->io_cnt != 0)) { + DbgErr("Avg Write=%d iocnt=%d T1=%d T2=%d T3=%d\n", + tick->last_dir, tick->io_cnt, + cputick2_100ns(tick->avg_start_2_thr, + cpu) / tick->io_cnt, + cputick2_100ns(tick->avg_thr_2_iodone, + cpu) / tick->io_cnt, + (tick->io_cnt > + 1) ? cputick2_100ns(tick->avg_iodone_2_next, + cpu) / (tick->io_cnt - 1) : 0); + os_memset(tick, 0, sizeof(tPerTick)); + } + +next: + if (sec_cnt == 0x800) { + tick->last_dir = bWrite; + tick->start_io_tick = tick->tick_start = period; + tick->io_cnt++; + if (tick->tick_io_end) { + period = (tick->tick_start - tick->tick_io_end); + tick->avg_iodone_2_next += period; + } + } else { + /* We conly calculate 1MB case */ + os_memset(tick, 0, sizeof(tPerTick)); + tick->start_io_tick = period; + } + +} + +void calc_io_end(tPerTick *tick) +{ + u64 period = 0; + u64 cpu_freq = 0; + + period = os_get_performance_tick(&cpu_freq); + tick->io_duration = + cputick2_100ns(period - tick->start_io_tick, cpu_freq); + if (tick->tick_thr_start == 0) + return; + + tick->tick_io_end = period; + { + period = tick->tick_io_end - tick->tick_thr_start; + tick->avg_thr_2_iodone += period; + } +} + +void calc_thr_start(tPerTick *tick) +{ + u64 period = 0; + + if (tick->tick_start == 0) + return; + + tick->tick_thr_start = os_get_performance_tick(NULL); + { + period = tick->tick_thr_start - tick->tick_start; + tick->avg_start_2_thr += period; + } +} +#endif +#endif diff --git a/drivers/scsi/bht/util/util.c b/drivers/scsi/bht/util/util.c new file mode 100644 index 000000000000..b7afd9bee9a0 --- /dev/null +++ b/drivers/scsi/bht/util/util.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 BHT Inc. + * + * File Name: util.c + * + * Abstract: This Include file used to implement platform independent APIs + * + * Version: 1.00 + * + * Author: Peter.Guo + * + * Environment: OS Independent + * + * History: + * + * 8/25/2014 Creation Peter.Guo + */ + +#include "../include/basic.h" +#include "../include/debug.h" + +/* + * + * Function Name: swapu32 + * + * Abstract: + * + * swap the u32 type byte order + * + * Input: + * + * + * Output: + * + * None + * + * Return value: + * + * none + * + * Notes: + */ +u32 swapu32(u32 value) +{ + u32 ret = ((value & 0xFF000000) >> 24) | ((value & 0x00FF0000) >> 8) | + ((value & 0x0000FF00) << 8) | ((value & 0x000000FF) << 24); + return ret; +} + +static void *va_offset_va(void *va, u32 offset) +{ + va = (byte *) va + offset; + return va; +} + +void pa_offset_pa(phy_addr_t *pa, u32 offset) +{ + u32 pal = 0, pah = 0; + u64 pa64 = 0; + + pal = os_get_phy_addr32l(*pa); + pah = os_get_phy_addr32h(*pa); + pa64 = pah; + pa64 <<= 32; + pa64 += pal; + pa64 += offset; + os_set_phy_add64(pa, pa64); +} + +bool resize_dma_buf(dma_desc_buf_t *p, u32 sz) +{ + if (sz > p->len) { + DbgErr("try to put over size to buf\n"); + return FALSE; + } + p->va = va_offset_va(p->va, sz); + pa_offset_pa(&p->pa, sz); + p->len = p->len - sz; + return TRUE; +} + +void dump_dma_buf(char *str, const dma_desc_buf_t *pdma) +{ +} + +u32 fls32(u32 x) +{ + s32 i; + + for (i = 31; i >= 0; i--) { + if (x & (1 << i)) + return i; + } + + return 0; +} + +/* + * This function will generate percetage of specified value + */ +bool random_percent_check(u32 percent) +{ + u32 val = 0; + + val = os_random_get(100); + + if (val >= (100 - percent)) + return TRUE; + else + return FALSE; +} + +void util_init_waitloop(void *pdx, u32 timeout_ms, u32 per_us, + loop_wait_t *wait) +{ + if (((bht_dev_ext_t *) pdx)->dump_mode == FALSE) { + wait->dump_mode = FALSE; + wait->tick = os_get_cur_tick(); + wait->timeout = timeout_ms; + } else { + wait->dump_mode = TRUE; + wait->tick = per_us; + wait->timeout = timeout_ms * 1000; + } +} + +bool util_is_timeout(loop_wait_t *wait) +{ + bool ret = FALSE; + + if (wait->dump_mode == FALSE) { + ret = os_is_timeout(wait->tick, wait->timeout); + } else { + if (wait->timeout == 0) + ret = TRUE; + wait->timeout -= os_min(wait->timeout, wait->tick); + } + + return ret; +}