From patchwork Tue Aug 31 11:01:20 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lai Jason X-Patchwork-Id: 504654 Delivered-To: patch@linaro.org Received: by 2002:a02:8629:0:0:0:0:0 with SMTP id e38csp2662540jai; Tue, 31 Aug 2021 04:01:32 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyan3d6II+81ABv+1che+l2KDbodkIA62wb+54GCqHyA5dSOosmh4hM9kx4OL5KKc7ZiSgt X-Received: by 2002:a5e:d80a:: with SMTP id l10mr7764490iok.36.1630407692514; Tue, 31 Aug 2021 04:01:32 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1630407692; cv=none; d=google.com; s=arc-20160816; b=vWf3kCT7vH2etLiWaUUsr0d9oUmWkAAcNAakIPUHTCp0RHidpbT+BHTVkjkYMQPS0m wjPoxLMWo7C1cYmS6XKPgaTnMlB048RNYBOoJT+IoSqVvW452hoiFWbIS5xF1hWwEZ/G IiGw0j8V6vyMUrlKxqUl5D53pT2MAoPbi7xjm2N5WaIFb8UQSGHSAdtF7PmAmvHxPSbf sgJr63tXwxYGNmFhjt179kIi1hlUxGK6cUDVMOoTlRu1LcwcltfHaDWg4HaSrm26CcBm d8rhyFl3QDZRFM1j6UjbBz/giEpgSAsKZXUZgyWmzIH5HmTw84EBALrQFM6HiKP2Bu5l jmxw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :message-id:date:subject:cc:to:from:dkim-signature; bh=3Y7ivpWUU7sN0mLtTRtSTnTmujt6c6Iud30UaDT46dk=; b=sR6DlmhyildBYQzEIQjVR+3xy6V9SJJkF8kqrHjrYAFJawgpxg0ZLXYYDq7qGRRxJY hT3WvRaoWAFW4FmuinAx7aS5UOwE2JA1m/qo+3ICseozDcZsJ+nnuE+Tl49qZzowoTvw S3weCOXg8cBBQj5XIbE03IAlATSbl4AbhMrwUQOqIGjrug4IDxJm3GHe86ExkaB5Me0g UGz0cVKQYsH75mnI8BlOlHsaC2UB3INYbZa5OQV7u7HF6+e+mDe9o2FYdo6EMNl5tCRi kbCko/zzvQG7N8Q/YkSXGvmxFtf271fjC+mKyQMI/HI7jY2EPolyzo6USsH4eFN/EQrE 4SFg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=S2VWvqRO; spf=pass (google.com: domain of linux-mmc-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-mmc-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id e15si16792834ilu.153.2021.08.31.04.01.32; Tue, 31 Aug 2021 04:01:32 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-mmc-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=S2VWvqRO; spf=pass (google.com: domain of linux-mmc-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-mmc-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238589AbhHaLC0 (ORCPT + 3 others); Tue, 31 Aug 2021 07:02:26 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44350 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231304AbhHaLCV (ORCPT ); Tue, 31 Aug 2021 07:02:21 -0400 Received: from mail-pj1-x102a.google.com (mail-pj1-x102a.google.com [IPv6:2607:f8b0:4864:20::102a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id F1764C061575 for ; Tue, 31 Aug 2021 04:01:25 -0700 (PDT) Received: by mail-pj1-x102a.google.com with SMTP id d5so5741155pjx.2 for ; Tue, 31 Aug 2021 04:01:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=3Y7ivpWUU7sN0mLtTRtSTnTmujt6c6Iud30UaDT46dk=; b=S2VWvqRO+VQY/YW7qA8NJG9JZLKCmHQ0inBWSPMIOsv5ZTAGWTB/FiFYOD5c7AAYMN VeOjpq/Z5NoEXvgfmsMe7zufNfst0xVQJ3DvyPoY8liOZ8UP9TFmFIMYHiTfLXO5+xvF BUKvQPTwLy8h0J8CsBZrZaQrS2WwiFWWBe2mTKDxftUq0IfqcqjnTmIYgBwtKry62iel E0OvUY6re9fX1fY4nwF8pS7Rga5strWCeIXU0WfP5fxOxpKNTtDK1gOj/EMi2ixGH9jw stkr5vUY4NEUOs7+asK/Fob4CgZ1Ngw8at98u8jFKbxxfZk4pfYFpJdb6Tq+iUaOkqWQ Ckkg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=3Y7ivpWUU7sN0mLtTRtSTnTmujt6c6Iud30UaDT46dk=; b=nHxu0Ng1wbpmTaZUxOscC6163L9XEy8F6prQjnS2qI0QZ9drL/MrfPJmrcc+3etf4W vfw+jMCUCsLGDjmd18UWm8ER/0VTQrMp5+8XutdgpfGrdsgtYmGTRTe21Ryo1tjrcuTr ZfNC+PSia9VDp69baykwu7sV5ukgLDIWDizYZ1YnvhJ9AaUbrp8nsrlTfkJemo7q4UTP qY5rnaUrmnQkHnHvrq/p2JrThj3nIeeIlthzhVYWo5Qbxn73PcvcFN6OioXTcD1KZQda +3vm/YlwKFx56YZIKBY4c8pCtbaQgmvXg3zzA6rJp3KRpoTOWwapiCn5JteSvFMv2syz fX1Q== X-Gm-Message-State: AOAM530IO4zrRUaGYpwH9Eak1Z8bOu4ujZ1YLvn8dbKWRw23kMzCAHn9 r9iDXDHqt70+MoG0VoJghmKrOtAxVlujOw== X-Received: by 2002:a17:902:bf46:b029:12c:75f6:f643 with SMTP id u6-20020a170902bf46b029012c75f6f643mr4245466pls.6.1630407685561; Tue, 31 Aug 2021 04:01:25 -0700 (PDT) Received: from localhost.localdomain (60-251-58-169.HINET-IP.hinet.net. [60.251.58.169]) by smtp.gmail.com with ESMTPSA id 6sm2760210pjz.8.2021.08.31.04.01.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 31 Aug 2021 04:01:25 -0700 (PDT) From: Jason Lai To: ulf.hansson@linaro.org, takahiro.akashi@linaro.org, adrian.hunter@intel.com Cc: Jason Lai , linux-mmc@vger.kernel.org, ben.chuang@genesyslogic.com.tw, greg.tu@genesyslogic.com.tw Subject: [PATCH 1/7] mmc: core: Cleanup printing of speed mode at card Date: Tue, 31 Aug 2021 19:01:20 +0800 Message-Id: <20210831110120.50508-1-jasonlai.genesyslogic@gmail.com> X-Mailer: git-send-email 2.32.0 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org From: Jason Lai From: Ulf Hansson The current print of the bus speed mode in mmc_add_card() has grown over the years and is now difficult to parse. Let's clean up the code and also take the opportunity to properly announce "DDR" for eMMCs as "high speed DDR", which is according to the eMMC spec. Signed-off-by: Ulf Hansson --- drivers/mmc/core/bus.c | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) -- 2.32.0 diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 4383c262b..27ba3c749 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -311,6 +311,7 @@ int mmc_add_card(struct mmc_card *card) { int ret; const char *type; + const char *speed_mode = ""; const char *uhs_bus_speed_mode = ""; static const char *const uhs_speeds[] = { [UHS_SDR12_BUS_SPEED] = "SDR12 ", @@ -349,27 +350,30 @@ int mmc_add_card(struct mmc_card *card) break; } + if (mmc_card_hs(card)) + speed_mode = "high speed "; + else if (mmc_card_uhs(card)) + speed_mode = "ultra high speed "; + else if (mmc_card_ddr52(card)) + speed_mode = "high speed DDR "; + else if (mmc_card_hs200(card)) + speed_mode = "HS200 "; + else if (mmc_card_hs400es(card)) + speed_mode = "HS400 Enhanced strobe "; + else if (mmc_card_hs400(card)) + speed_mode = "HS400 "; + if (mmc_card_uhs(card) && (card->sd_bus_speed < ARRAY_SIZE(uhs_speeds))) uhs_bus_speed_mode = uhs_speeds[card->sd_bus_speed]; - if (mmc_host_is_spi(card->host)) { - pr_info("%s: new %s%s%s card on SPI\n", - mmc_hostname(card->host), - mmc_card_hs(card) ? "high speed " : "", - mmc_card_ddr52(card) ? "DDR " : "", - type); - } else { - pr_info("%s: new %s%s%s%s%s%s card at address %04x\n", - mmc_hostname(card->host), - mmc_card_uhs(card) ? "ultra high speed " : - (mmc_card_hs(card) ? "high speed " : ""), - mmc_card_hs400(card) ? "HS400 " : - (mmc_card_hs200(card) ? "HS200 " : ""), - mmc_card_hs400es(card) ? "Enhanced strobe " : "", - mmc_card_ddr52(card) ? "DDR " : "", + if (mmc_host_is_spi(card->host)) + pr_info("%s: new %s%s card on SPI\n", + mmc_hostname(card->host), speed_mode, type); + else + pr_info("%s: new %s%s%s card at address %04x\n", + mmc_hostname(card->host), speed_mode, uhs_bus_speed_mode, type, card->rca); - } #ifdef CONFIG_DEBUG_FS mmc_add_card_debugfs(card); From patchwork Tue Aug 31 11:01:35 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lai Jason X-Patchwork-Id: 504655 Delivered-To: patch@linaro.org Received: by 2002:a02:8629:0:0:0:0:0 with SMTP id e38csp2662673jai; Tue, 31 Aug 2021 04:01:44 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzX4k2Km7Ih/0bu6A846kjx1tEy5cARWfnfdJv6fwqtwVGZ/PGOiK+44D3tCw7ZeOJzg+vw X-Received: by 2002:a92:cb4b:: with SMTP id f11mr19955864ilq.189.1630407704105; Tue, 31 Aug 2021 04:01:44 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1630407704; cv=none; d=google.com; s=arc-20160816; b=wifcG8uE/t46Ui95VtCOaUSygim59D2z96+eaGeSL787yxiV3w1ZnQnx1hj/yclhal myYR+E7HWnb9oRwI/aa6fmKmSonEPmSxL14tGuaIdPWqDeHCNaJ+edkKoN6hjnfLpCbu 4oYsJn2fu4zA7+S2XervuZ3Si9MKaK2TzQgepx1plM3IDLPabQGnQNrTKlZw1tOJD6UL SeaSSgN7ciCwdWIJgGWwgl9ty4uXpXvb3qUoDOzeLTmrE2bG+Yly6e9M17sAntLPeSut fbLBWe1I3tBCOVlzCyRxxObX+IduWWa7lFgWv8O5wr2lsFON1/gVzt52kK54dQdBUn2l KDTw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :message-id:date:subject:cc:to:from:dkim-signature; bh=1TLVbhlGm90fCyHf7eSqWj1hJNbqsjWi6QF7kRuy8tI=; b=d4j68BnO8PfGYKn/uGtCezmeEDi90BTdEcz4dmPG5Hd+Tv34UzynCzIPT9+U54GGzc DC5KWudIS3B1tIW1VC9+YU6EQfUJQNCWSlre9C/ckSkG6A6RWSXiXdMPBq/L9XKNezk7 Rjdue8WhUwkNLuhOa2/C96Kymqt5kQcVcO2/iLkSiGGaxXh6fg1tHOdaRshla88AAwHg cbrlC89V3XZRIdoHETgvP7g63WPWemaSsc9YlJKExsS4QHBNwQ5h/8/biFC4KAK46Mni 0zjPG5rtNScSvxF41VDpfRjQ82kvHBrTMmQFfo38s38KYAhe877RBjdp5lBhPas3kk5P p46A== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=VYMVc6Yt; spf=pass (google.com: domain of linux-mmc-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-mmc-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id e15si16792834ilu.153.2021.08.31.04.01.43; Tue, 31 Aug 2021 04:01:44 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-mmc-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=VYMVc6Yt; spf=pass (google.com: domain of linux-mmc-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-mmc-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239451AbhHaLCh (ORCPT + 3 others); Tue, 31 Aug 2021 07:02:37 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44408 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231304AbhHaLCg (ORCPT ); Tue, 31 Aug 2021 07:02:36 -0400 Received: from mail-pl1-x635.google.com (mail-pl1-x635.google.com [IPv6:2607:f8b0:4864:20::635]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 93F31C061575 for ; Tue, 31 Aug 2021 04:01:41 -0700 (PDT) Received: by mail-pl1-x635.google.com with SMTP id q21so10385251plq.3 for ; Tue, 31 Aug 2021 04:01:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=1TLVbhlGm90fCyHf7eSqWj1hJNbqsjWi6QF7kRuy8tI=; b=VYMVc6Ytya+rR1+apiCQVIywxverPmcr/tQ92q3NGiLTXu9DtOCzCL8uw8Xx3BDMOl mgZ/bjHaNqM0NQG3AD2zuE01Mq/YdSJJk73yUem9PTzl29G+wTBxe8uJVj7XJGOwER4h 7nkn8CRk1w0ZnbLxJVrlXE15G3+7ymWAX99Bagh+n++oa6f5F/YYwfVh9oTDuL+DSaPn hiewHHRUksQdykzHDjB1l1oXZzJDKJ+RJntx9o/g1igv5tQC4RTKNm4zyZxQqHvTEh8M 9mkyGl2l4BjgcRCnwtXIThd++MwZZojoGAoCGPHQ98JJjSHMzzYLVA5H9YRdcBKuiONo Q7XQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=1TLVbhlGm90fCyHf7eSqWj1hJNbqsjWi6QF7kRuy8tI=; b=gySBxacMFKcnGSjtPDX6H8JKiwwnVGwvpPIlzXfhc/BfSW2v/LvCiq/Vp1qSNt7lh7 2lrRB9bIyR53cNKq06AfPfYOkpVEZj6ONbdhUi14mF6+IWMwRAK+KWZi/zZBksVRA2Ut ysSdlJ75S31vXUlQjv7HVGIz+Fye+23r6YThXlfKALqHs+filO82DYFLzNdzEXKWidDT aZB+Z10tdVY0jbuk/We/fFKQ06x7k6m7iGb9pPHwjNJZ3wDAdrMRUS5LzfdQ2Kf+SMbQ pgZq7shEuO53snEsmi89iusf3LBrHyHaVg3dq7nhkVfUxh4LUVeZztqju318U/GKEaDI AH0A== X-Gm-Message-State: AOAM533SKquWmBV9goBa8C0hP9j5/j9W7Q+KwlOMgmXceLj9QEGImFYM ts8+j9gdtET1q5em1OSH95c= X-Received: by 2002:a17:902:edd0:b0:135:b351:bd5a with SMTP id q16-20020a170902edd000b00135b351bd5amr4231298plk.52.1630407700805; Tue, 31 Aug 2021 04:01:40 -0700 (PDT) Received: from localhost.localdomain (60-251-58-169.HINET-IP.hinet.net. [60.251.58.169]) by smtp.gmail.com with ESMTPSA id z33sm19667684pga.20.2021.08.31.04.01.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 31 Aug 2021 04:01:40 -0700 (PDT) From: Jason Lai To: ulf.hansson@linaro.org, takahiro.akashi@linaro.org, adrian.hunter@intel.com Cc: Jason Lai , linux-mmc@vger.kernel.org, ben.chuang@genesyslogic.com.tw, greg.tu@genesyslogic.com.tw Subject: [PATCH 2/7] mmc: core: Prepare to support SD UHS-II cards Date: Tue, 31 Aug 2021 19:01:35 +0800 Message-Id: <20210831110135.50562-1-jasonlai.genesyslogic@gmail.com> X-Mailer: git-send-email 2.32.0 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org From: Jason Lai From: Ulf Hansson The SD UHS-II interface was introduced to the SD spec v4.00 several years ago. The interface is fundamentally different from an electrical and a protocol point of view, comparing to the legacy SD interface. However, the legacy SD protocol is supported through a specific transport layer (SD-TRAN) defined in the UHS-II addendum of the spec. This allows the SD card to be managed in a very similar way as a legacy SD card, hence a lot of code can be re-used to support these new types of cards through the mmc subsystem. Moreover, an SD card that supports the UHS-II interface shall also be backwards compatible with the legacy SD interface, which allows a UHS-II card to be inserted into a legacy slot. As a matter of fact, this is already supported by mmc subsystem as of today. To prepare to add support for UHS-II, this change puts the basic foundation in the mmc core in place, allowing it to be more easily reviewed before subsequent changes implements the actual support. Basically, the approach here adds a new UHS-II bus_ops type and adds a separate initialization path for the UHS-II card. The intent is to avoid us from sprinkling the legacy initialization path, but also to simplify implementation of the UHS-II specific bits. At this point, there is only one new host ops added to manage the various ios settings needed for UHS-II. Additional host ops that are needed, are being added from subsequent changes. Signed-off-by: Ulf Hansson --- drivers/mmc/core/Makefile | 2 +- drivers/mmc/core/core.c | 17 ++- drivers/mmc/core/core.h | 1 + drivers/mmc/core/sd_uhs2.c | 292 +++++++++++++++++++++++++++++++++++++ include/linux/mmc/card.h | 7 + include/linux/mmc/host.h | 23 +++ 6 files changed, 340 insertions(+), 2 deletions(-) create mode 100644 drivers/mmc/core/sd_uhs2.c -- 2.32.0 diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index 6a907736c..15b067e8b 100644 --- a/drivers/mmc/core/Makefile +++ b/drivers/mmc/core/Makefile @@ -7,7 +7,7 @@ obj-$(CONFIG_MMC) += mmc_core.o mmc_core-y := core.o bus.o host.o \ mmc.o mmc_ops.o sd.o sd_ops.o \ sdio.o sdio_ops.o sdio_bus.o \ - sdio_cis.o sdio_io.o sdio_irq.o \ + sdio_cis.o sdio_io.o sdio_irq.o sd_uhs2.o\ slot-gpio.o regulator.o mmc_core-$(CONFIG_OF) += pwrseq.o obj-$(CONFIG_PWRSEQ_SIMPLE) += pwrseq_simple.o diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 95fedcf56..19b409179 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2194,6 +2194,18 @@ void mmc_rescan(struct work_struct *work) goto out; } + /* + * Ideally we should favor initialization of legacy SD cards and defer + * UHS-II enumeration. However, it seems like cards doesn't reliably + * announce their support for UHS-II in the response to the ACMD41, + * while initializing the legacy SD interface. Therefore, let's start + * with UHS-II for now. + */ + if (!mmc_attach_sd_uhs2(host)) { + mmc_release_host(host); + goto out; + } + for (i = 0; i < ARRAY_SIZE(freqs); i++) { unsigned int freq = freqs[i]; if (freq > host->f_max) { @@ -2215,10 +2227,13 @@ void mmc_rescan(struct work_struct *work) void mmc_start_host(struct mmc_host *host) { + bool power_up = !(host->caps2 & + (MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_SD_UHS2)); + host->f_init = max(min(freqs[0], host->f_max), host->f_min); host->rescan_disable = 0; - if (!(host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)) { + if (power_up) { mmc_claim_host(host); mmc_power_up(host, host->ocr_avail); mmc_release_host(host); diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 0c4de2030..5fb89afff 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -80,6 +80,7 @@ int mmc_detect_card_removed(struct mmc_host *host); int mmc_attach_mmc(struct mmc_host *host); int mmc_attach_sd(struct mmc_host *host); int mmc_attach_sdio(struct mmc_host *host); +int mmc_attach_sd_uhs2(struct mmc_host *host); /* Module parameters */ extern bool use_spi_crc; diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c new file mode 100644 index 000000000..24aa51a6d --- /dev/null +++ b/drivers/mmc/core/sd_uhs2.c @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021 Linaro Ltd + * + * Author: Ulf Hansson + * + * Support for SD UHS-II cards + */ +#include + +#include +#include + +#include "core.h" +#include "bus.h" +#include "sd.h" +#include "mmc_ops.h" + +static const unsigned int sd_uhs2_freqs[] = { 52000000, 26000000 }; + +static int sd_uhs2_set_ios(struct mmc_host *host) +{ + struct mmc_ios *ios = &host->ios; + + return host->ops->uhs2_set_ios(host, ios); +} + +static int sd_uhs2_power_up(struct mmc_host *host) +{ + host->ios.vdd = fls(host->ocr_avail) - 1; + host->ios.clock = host->f_init; + host->ios.timing = MMC_TIMING_SD_UHS2; + host->ios.power_mode = MMC_POWER_UP; + + return sd_uhs2_set_ios(host); +} + +static void sd_uhs2_power_off(struct mmc_host *host) +{ + host->ios.vdd = 0; + host->ios.clock = 0; + host->ios.timing = MMC_TIMING_LEGACY; + host->ios.power_mode = MMC_POWER_OFF; + + sd_uhs2_set_ios(host); +} + +/* + * Run the phy initialization sequence, which mainly relies on the UHS-II host + * to check that we reach the expected electrical state, between the host and + * the card. + */ +static int sd_uhs2_phy_init(struct mmc_host *host) +{ + return 0; +} + +/* + * Do the early initialization of the card, by sending the device init +roadcast + * command and wait for the process to be completed. + */ +static int sd_uhs2_dev_init(struct mmc_host *host) +{ + return 0; +} + +/* + * Run the enumeration process by sending the enumerate command to the card. + * Note that, we currently support only the point to point connection, which + * means only one card can be attached per host/slot. + */ +static int sd_uhs2_enum(struct mmc_host *host, u32 *node_id) +{ + return 0; +} + +/* + * Read the UHS-II configuration registers (CFG_REG) of the card, by sending it + * commands and by parsing the responses. Store a copy of the relevant data in + * card->uhs2_config. + */ +static int sd_uhs2_config_read(struct mmc_host *host, struct mmc_card *card) +{ + return 0; +} + +/* + * Based on the card's and host's UHS-II capabilities, let's update the + * configuration of the card and the host. This may also include to move to a + * greater speed range/mode. Depending on the updated configuration, we may +eed + * to do a soft reset of the card via sending it a GO_DORMANT_STATE command. + * + * In the final step, let's check if the card signals "config completion", +hich + * indicates that the card has moved from config state into active state. + */ +static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card) +{ + return 0; +} + +/* + * Initialize the UHS-II card through the SD-TRAN transport layer. This enables + * commands/requests to be backwards compatible through the legacy SD protocol. + * UHS-II cards has a specific power limit specified for VDD1/VDD2, that should + * be set through a legacy CMD6. Note that, the power limit that becomes set, + * survives a soft reset through the GO_DORMANT_STATE command. + */ +static int sd_uhs2_legacy_init(struct mmc_host *host, struct mmc_card *card) +{ + return 0; +} + +/* + * Allocate the data structure for the mmc_card and run the UHS-II specific + * initialization sequence. + */ +static int sd_uhs2_init_card(struct mmc_host *host) +{ + struct mmc_card *card; + u32 node_id; + int err; + + err = sd_uhs2_dev_init(host); + if (err) + return err; + + err = sd_uhs2_enum(host, &node_id); + if (err) + return err; + + card = mmc_alloc_card(host, &sd_type); + if (IS_ERR(card)) + return PTR_ERR(card); + + card->uhs2_config.node_id = node_id; + card->type = MMC_TYPE_SD; + + err = sd_uhs2_config_read(host, card); + if (err) + goto err; + + err = sd_uhs2_config_write(host, card); + if (err) + goto err; + + err = sd_uhs2_legacy_init(host, card); + if (err) + goto err; + + host->card = card; + return 0; + +err: + mmc_remove_card(card); + return err; +} + +static void sd_uhs2_remove(struct mmc_host *host) +{ + mmc_remove_card(host->card); + host->card = NULL; +} + +static int sd_uhs2_alive(struct mmc_host *host) +{ + return mmc_send_status(host->card, NULL); +} + +static void sd_uhs2_detect(struct mmc_host *host) +{ + int err; + + mmc_get_card(host->card, NULL); + err = _mmc_detect_card_removed(host); + mmc_put_card(host->card, NULL); + + if (err) { + sd_uhs2_remove(host); + + mmc_claim_host(host); + mmc_detach_bus(host); + sd_uhs2_power_off(host); + mmc_release_host(host); + } +} + +static int sd_uhs2_suspend(struct mmc_host *host) +{ + return 0; +} + +static int sd_uhs2_resume(struct mmc_host *host) +{ + return 0; +} + +static int sd_uhs2_runtime_suspend(struct mmc_host *host) +{ + return 0; +} + +static int sd_uhs2_runtime_resume(struct mmc_host *host) +{ + return 0; +} + +static int sd_uhs2_shutdown(struct mmc_host *host) +{ + return 0; +} + +static int sd_uhs2_hw_reset(struct mmc_host *host) +{ + return 0; +} + +static const struct mmc_bus_ops sd_uhs2_ops = { + .remove = sd_uhs2_remove, + .alive = sd_uhs2_alive, + .detect = sd_uhs2_detect, + .suspend = sd_uhs2_suspend, + .resume = sd_uhs2_resume, + .runtime_suspend = sd_uhs2_runtime_suspend, + .runtime_resume = sd_uhs2_runtime_resume, + .shutdown = sd_uhs2_shutdown, + .hw_reset = sd_uhs2_hw_reset, +}; + +static int sd_uhs2_attach(struct mmc_host *host) +{ + int err; + + err = sd_uhs2_power_up(host); + if (err) + goto err; + + err = sd_uhs2_phy_init(host); + if (err) + goto err; + + err = sd_uhs2_init_card(host); + if (err) + goto err; + + mmc_attach_bus(host, &sd_uhs2_ops); + + mmc_release_host(host); + + err = mmc_add_card(host->card); + if (err) + goto remove_card; + + mmc_claim_host(host); + return 0; + +remove_card: + mmc_remove_card(host->card); + host->card = NULL; + mmc_claim_host(host); + mmc_detach_bus(host); +err: + sd_uhs2_power_off(host); + return err; +} + +int mmc_attach_sd_uhs2(struct mmc_host *host) +{ + int i, err = 0; + + if (!(host->caps2 & MMC_CAP2_SD_UHS2)) + return -EOPNOTSUPP; + + /* Turn off the legacy SD interface before trying with UHS-II. */ + mmc_power_off(host); + + /* + * Start UHS-II initialization at 52MHz and possibly make a retry at + * 26MHz according to the spec. It's required that the host driver + * validates ios->clock, to set a rate within the correct range. + */ + for (i = 0; i < ARRAY_SIZE(sd_uhs2_freqs); i++) { + host->f_init = sd_uhs2_freqs[i]; + err = sd_uhs2_attach(host); + if (!err) + break; + } + + return err; +} diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 74e6c0624..82b07eac1 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -210,6 +210,11 @@ struct sd_ext_reg { #define SD_EXT_PERF_CMD_QUEUE (1<<4) }; +struct sd_uhs2_config { + u32 node_id; + /* TODO: Extend with more register configs. */ +}; + struct sdio_cccr { unsigned int sdio_vsn; unsigned int sd_vsn; @@ -314,6 +319,8 @@ struct mmc_card { struct sd_ext_reg ext_power; /* SD extension reg for PM */ struct sd_ext_reg ext_perf; /* SD extension reg for PERF */ + struct sd_uhs2_config uhs2_config; /* SD UHS-II config */ + unsigned int sdio_funcs; /* number of SDIO functions */ atomic_t sdio_funcs_probed; /* number of probed SDIO funcs */ struct sdio_cccr cccr; /* common card info */ diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 0abd47e9e..91065ab7f 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -63,6 +63,10 @@ struct mmc_ios { #define MMC_TIMING_MMC_HS400 10 #define MMC_TIMING_SD_EXP 11 #define MMC_TIMING_SD_EXP_1_2V 12 +#define MMC_TIMING_SD_UHS2 13 + + unsigned char vqmmc2_voltage; +#define MMC_VQMMC2_VOLTAGE_180 0 unsigned char signal_voltage; /* signalling voltage (1.8V or 3.3V) */ @@ -91,6 +95,10 @@ struct mmc_clk_phase_map { struct mmc_clk_phase phase[MMC_NUM_CLK_PHASES]; }; +struct sd_uhs2_caps { + /* TODO: Add UHS-II capabilities for the host. */ +}; + struct mmc_host; struct mmc_host_ops { @@ -126,6 +134,18 @@ struct mmc_host_ops { */ void (*set_ios)(struct mmc_host *host, struct mmc_ios *ios); + /* + * The uhs2_set_ios callback is mandatory to implement for hosts that + * supports the SD UHS-II interface (MMC_CAP2_SD_UHS2), while the + * callback is unused for the other cases. Note that, the struct + * mmc_ios is being re-used for this as well. + * + * Expected return values for the uhs2_set_ios callback are a negative + * errno in case of a failure or zero for success. + */ + int (*uhs2_set_ios)(struct mmc_host *host, struct mmc_ios *ios); + + /* * Return values for the get_ro callback should be: * 0 for a read/write card @@ -374,6 +394,7 @@ struct mmc_host { MMC_CAP2_HS200_1_2V_SDR) #define MMC_CAP2_SD_EXP (1 << 7) /* SD express via PCIe */ #define MMC_CAP2_SD_EXP_1_2V (1 << 8) /* SD express 1.2V */ +#define MMC_CAP2_SD_UHS2 (1 << 9) /* SD UHS-II support */ #define MMC_CAP2_CD_ACTIVE_HIGH (1 << 10) /* Card-detect signal active high */ #define MMC_CAP2_RO_ACTIVE_HIGH (1 << 11) /* Write-protect signal active high */ #define MMC_CAP2_NO_PRESCAN_POWERUP (1 << 14) /* Don't power up before scan */ @@ -399,6 +420,8 @@ struct mmc_host { #define MMC_CAP2_CRYPTO 0 #endif + struct sd_uhs2_caps uhs2_caps; /* SD UHS-II capabilities */ + int fixed_drv_type; /* fixed driver type for non-removable media */ mmc_pm_flag_t pm_caps; /* supported pm features */ From patchwork Tue Aug 31 11:01:55 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lai Jason X-Patchwork-Id: 504656 Delivered-To: patch@linaro.org Received: by 2002:a02:8629:0:0:0:0:0 with SMTP id e38csp2662914jai; Tue, 31 Aug 2021 04:02:02 -0700 (PDT) X-Google-Smtp-Source: ABdhPJySoqOJYlnZxRGv4coTtxgxIwtxexKjzqPGlpHam+FxdFQX/lJlY30SGbtER/g1wGFvEfqu X-Received: by 2002:a5e:a601:: with SMTP id q1mr22100390ioi.51.1630407722483; Tue, 31 Aug 2021 04:02:02 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1630407722; cv=none; d=google.com; s=arc-20160816; b=htBzVcyBcBGBtDknLmS9dOkz1Oh0LKZRCDhyjoMn6MOWKfEOMlPXtxLkFE0zAPQCbK K1JU7QChLAr0JSmQMd3w4C7KREVgFf9stVZHxX6aQniLWcYAz1vCCMkEMiHnfo2P/wkP C812sfeF1WWJ1l5K3AT+IrwhjDOxKWZt3HV9uLAA6F6Vef9orylVJMGpXGvyOxeI4RHP DvgYCMc48ByfJffUeaQramETYgzeOrF2KPl2zgiHiYw5pUpAzm1UoqfO7rTcMXrTy8dc d6Ns5/AmFplpMkIfdDJYjXGfptsAzPJtEPSMe3yzuqKHmDlw1+iugdhuXHSuodiHRepM 38Zw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :message-id:date:subject:cc:to:from:dkim-signature; bh=J7ZX5cWq/OCrI2PmtgCFKZxObJ0fq4sVzKxoaxZ4qkM=; b=xn3jsCJ2XJvPyiWOjXbCffD7IpnVkk6uefonEkjow3wRYS+ObnyZ++0ZUDe9eCHSgi fOxVLLPnc3j/BXjQDlmH7Up0UUXPZrtOYjHNoOs5aB+Nj0g5P9/xOyKyUCDMWNAMvpnU C2eFWH6WXgbe7U4KbOLq+r/anpD0VjOcpmAcYyApboKeDe6/0hHExkqcd0iiyA95AdBR nQMAavbwjfZ2CCxs9jpPA4RzbAY/o1pM2Osfy0xCkbPe2ZNl/XDVkgHLx7Br/UGl4JQg UVFvBxB87Mx1gr1xM/w6eCt9iwidX1aso+wSOX7Jg9lExne7xF41MCp1eI7YnKzhfpv9 bCHg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=fm+zE4KE; spf=pass (google.com: domain of linux-mmc-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-mmc-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id 132si11043040jaf.20.2021.08.31.04.02.02; Tue, 31 Aug 2021 04:02:02 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-mmc-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=fm+zE4KE; spf=pass (google.com: domain of linux-mmc-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-mmc-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S241148AbhHaLC4 (ORCPT + 3 others); Tue, 31 Aug 2021 07:02:56 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44480 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231304AbhHaLCz (ORCPT ); Tue, 31 Aug 2021 07:02:55 -0400 Received: from mail-pg1-x52e.google.com (mail-pg1-x52e.google.com [IPv6:2607:f8b0:4864:20::52e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 49AE0C061575 for ; Tue, 31 Aug 2021 04:02:00 -0700 (PDT) Received: by mail-pg1-x52e.google.com with SMTP id w7so15127324pgk.13 for ; Tue, 31 Aug 2021 04:02:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=J7ZX5cWq/OCrI2PmtgCFKZxObJ0fq4sVzKxoaxZ4qkM=; b=fm+zE4KE1BdrQqK4f7m5nRZ6LSOroWDywcddzD650TFEJr0mA1x4lZsD2R7vfkbK/J LQVQyKixt8ZWBbyWuHeJ9knnSDam3kiubwDPWYozIG8+MLJJ2LVEwGpQ70jUHzxoMcIe d4Q/pYIRg30OBdTxZjzIhx2L7kggkjEgq67DdZotj48bJB+P6VfeVFWES49RkKd1Bbkp OZp4Ov6H3mEQ14ogrcwHjtTyy0hjP7YywSJr2/rN83WI9ORbKufbA6cKXEsLb/MYWwd1 adz587T5tQwuqHKwnapePuLM8x322i0IYPtWmy0n2qJ/WG6shR7xuf9bSXp8LBBz7HEn zoLA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=J7ZX5cWq/OCrI2PmtgCFKZxObJ0fq4sVzKxoaxZ4qkM=; b=qJahVxy/aOUr48dytLSsO2KusNWbGa3boxXHBNE3HENZtu1WyXiI5nYCo3HkkyIKXw +vzXZFMbpmRcV+gsknsb8Phbtg1V04VajRPmnMQ3OE2qA8ncQyKDqnYaWa5ISsFXPJgR +DeOrWlMgILVh7beSWmLOJQWxs2P5YjHls2NqBG0vdsVXW9U5kEA6EJwYa7hHNm1t3iV wQEtGAjoH2aPIHGst8v9LR7MiT+e2v9yG/CMFZoGdUuqueErDVgUHd9ntO6fK1jS/Rce rTMY8fvRSqhHMlNOZMM9I2X2D2AVd/xVwweWdsBv2B49sNNY1BdQDIkWEvJEBOvd9drc GL/Q== X-Gm-Message-State: AOAM533Td31Hza6Fj0R0SnBBQeMYIZWU7/u4tyuTPzYUf2CaLeVsKea5 HIxrOoFq+DWwWGRvNnbH0ubrqHc1Qp/Lhw== X-Received: by 2002:a63:f94c:: with SMTP id q12mr25892581pgk.171.1630407719853; Tue, 31 Aug 2021 04:01:59 -0700 (PDT) Received: from localhost.localdomain (60-251-58-169.HINET-IP.hinet.net. [60.251.58.169]) by smtp.gmail.com with ESMTPSA id i20sm5952653pfa.185.2021.08.31.04.01.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 31 Aug 2021 04:01:59 -0700 (PDT) From: Jason Lai To: ulf.hansson@linaro.org, takahiro.akashi@linaro.org, adrian.hunter@intel.com Cc: Jason Lai , linux-mmc@vger.kernel.org, ben.chuang@genesyslogic.com.tw, greg.tu@genesyslogic.com.tw Subject: [PATCH 3/7] mmc: core: Announce successful insertion of an SD Date: Tue, 31 Aug 2021 19:01:55 +0800 Message-Id: <20210831110155.50616-1-jasonlai.genesyslogic@gmail.com> X-Mailer: git-send-email 2.32.0 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org From: Jason Lai From: Ulf Hansson To inform the users about SD UHS-II cards, let's extend the print at card insertion with a "UHS-II" substring. Within this change, it seems reasonable to convert from using "ultra high speed" into "UHS-I speed", for the UHS-I type, as it should makes it more clear. Note that, the new print for UHS-II cards doesn't include the actual selected speed mode. Instead, this is going to be added from subsequent change. Signed-off-by: Ulf Hansson --- drivers/mmc/core/bus.c | 4 +++- drivers/mmc/core/host.h | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) -- 2.32.0 diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 27ba3c749..8fd46b5f5 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -353,7 +353,9 @@ int mmc_add_card(struct mmc_card *card) if (mmc_card_hs(card)) speed_mode = "high speed "; else if (mmc_card_uhs(card)) - speed_mode = "ultra high speed "; + speed_mode = "UHS-I speed "; + else if (mmc_card_uhs2(card)) + speed_mode = "UHS-II speed "; else if (mmc_card_ddr52(card)) speed_mode = "high speed DDR "; else if (mmc_card_hs200(card)) diff --git a/drivers/mmc/core/host.h b/drivers/mmc/core/host.h index ba407617e..c243d3096 100644 --- a/drivers/mmc/core/host.h +++ b/drivers/mmc/core/host.h @@ -83,5 +83,9 @@ static inline bool mmc_card_sd_express(struct mmc_host *host) host->ios.timing == MMC_TIMING_SD_EXP_1_2V; } +static inline bool mmc_card_uhs2(struct mmc_card *card) +{ + return card->host->ios.timing == MMC_TIMING_SD_UHS2; +} #endif From patchwork Tue Aug 31 11:02:03 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lai Jason X-Patchwork-Id: 504657 Delivered-To: patch@linaro.org Received: by 2002:a02:8629:0:0:0:0:0 with SMTP id e38csp2663014jai; Tue, 31 Aug 2021 04:02:09 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzc3DmJ4jQ1JMd7PpM3TXdzMiDt41LTHJIdG4FAgy+2ZczFmpzERSF5VrAoenpWxlu2QeoG X-Received: by 2002:a05:6638:521:: with SMTP id j1mr2259923jar.122.1630407729537; Tue, 31 Aug 2021 04:02:09 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1630407729; cv=none; d=google.com; s=arc-20160816; b=PEvlz0LXOFVk8XbUgi2ERhQ+rnM14PrYWOZC7eeggoKfAcAOW33crYok6aZE47cPrZ KZk2JUbViwFOJWhlpFTxUQisjKS4wvTVS2OQhqpFzAuqn+tffJ+apI7Shy6MN8rh+kP7 vDaDodoBDF7sVXyDiaSSUT7r122MkqY6pz/O74FRWDICJLlRDlu7KSv6N/ooCcCZSbE+ mRdJM5w0mGb+rqXZuUTQTgJ+AKJU3Wm4LCBXzaaGTcfNCa6E94O41BfrrvF6mRATLUZd 9OVNjteHxLobRaVEMyGvg2pruU5o7+NwepH9YhT7HDmH3hHDYJfaXWa5pJs3sptGgZI+ qQ7Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :message-id:date:subject:cc:to:from:dkim-signature; bh=+xjZYk6uNy4ZGkIZN40uDZFqPEfdCLB0M2J0sE1v5D8=; b=jIf6kCAX1vu/dS56egkxxfXWUYkH46LsfLVTQiyTVoSKsWrFrsdpl5rCiw2UPstZfS xpj1F61UMbkxn6sXdc6eFOQPB3VR3vlZQqWqeR+/Bhk2CWjV9dx1IzvMA++dUq0b5JrM z8VbD9yqnY7Q3198uqU/J/m1gRCRSWXczKWh84TpU6lFelDghj516ex0MmE7spmbJipd HNo7kfi7eAGtmECKfX6vT5oYsCnlYKgoE3fahmJq+WFl8LvP4QPCGQp32DzTeQDJn/9v aesOTKPA3mrBAgSkIOCJ3EoLoBCt2y6ehyXI2BRbHPKpy7dtaf7YnFzS+oLTTmTdrLYM 4LvA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=DQIGWZo1; spf=pass (google.com: domain of linux-mmc-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-mmc-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id 132si11043040jaf.20.2021.08.31.04.02.09; Tue, 31 Aug 2021 04:02:09 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-mmc-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=DQIGWZo1; spf=pass (google.com: domain of linux-mmc-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-mmc-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S241124AbhHaLDD (ORCPT + 3 others); Tue, 31 Aug 2021 07:03:03 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44512 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231304AbhHaLDD (ORCPT ); Tue, 31 Aug 2021 07:03:03 -0400 Received: from mail-pj1-x102d.google.com (mail-pj1-x102d.google.com [IPv6:2607:f8b0:4864:20::102d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3E63BC061575 for ; Tue, 31 Aug 2021 04:02:08 -0700 (PDT) Received: by mail-pj1-x102d.google.com with SMTP id w19-20020a17090aaf9300b00191e6d10a19so2099106pjq.1 for ; Tue, 31 Aug 2021 04:02:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=+xjZYk6uNy4ZGkIZN40uDZFqPEfdCLB0M2J0sE1v5D8=; b=DQIGWZo1EJekke7YkCqnCmJgFYrQNxZdr2/VJPB9onVNUpoqPffHqM4CE7vxAPjb7C WIGpFvemdaE8C8LS+/qoQDn0bviPUKO72VYxmJ6kNpnacnH8GYg3f1/L8y5bCW1PWXQZ P9YOLBttomkctSGAStWoV84q8vmctXwu9YN7K5V+2aKVgJbI2KdjvnIjLfBHh1gT5SnO n5+BYVnlHXgKzf+kXWf2OoIHf11pKTLwiuyguwsU8w9fyBGWecamiznzQu+LB6wpqED0 VaikWcsqQ6XXGQFfRgxZjdCCggVxtoE4Rkg6kjmT6Awhf4mLYlE6ByiRkS083Z/+Ksyc yDXA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=+xjZYk6uNy4ZGkIZN40uDZFqPEfdCLB0M2J0sE1v5D8=; b=pfbqXe8v+CaDKL/1+mJt0iaaavy3RoYi/1EeUAy0FKvPBe+x2Qa9T9Yz7i3o8zswPA VSnN5snP5p45opdmKbdNQUqlW/zcWpEHQm2qQLj33S3WIAGTQZNk/zAuDPKgPfSsBHP1 8HyaLtEu/4sfNw4/YNBL8UOWSfxKUWIhB9zS6xDvrzLD3opTk+naXxdPMpWjt7iZVS6r CJUjFkuDtL0iM8B6sRAAzSYeq+1a/38ZLJXtQiRSyfBxfo9YPWhmkxoRIMmRGaAlbtw8 zdB3al45cK72bqeAmJDmOsd+SKeEsOh7u8n2JKqe8LK0yyWbAr9uHSFdcNv7jY/8hOll irqQ== X-Gm-Message-State: AOAM531UsXL3k+8qRYYlBDAE7CZsfvwjyeThNdv+l6oduJA/ec/mvlYD WP3h+FXAX2sz7OTfH7hB1jg= X-Received: by 2002:a17:90a:4285:: with SMTP id p5mr4833238pjg.162.1630407727800; Tue, 31 Aug 2021 04:02:07 -0700 (PDT) Received: from localhost.localdomain (60-251-58-169.HINET-IP.hinet.net. [60.251.58.169]) by smtp.gmail.com with ESMTPSA id q4sm2429546pjd.52.2021.08.31.04.02.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 31 Aug 2021 04:02:07 -0700 (PDT) From: Jason Lai To: ulf.hansson@linaro.org, takahiro.akashi@linaro.org, adrian.hunter@intel.com Cc: Jason Lai , linux-mmc@vger.kernel.org, ben.chuang@genesyslogic.com.tw, greg.tu@genesyslogic.com.tw Subject: [PATCH 4/7] mmc: core: Extend support for mmc regulators with a Date: Tue, 31 Aug 2021 19:02:03 +0800 Message-Id: <20210831110203.50669-1-jasonlai.genesyslogic@gmail.com> X-Mailer: git-send-email 2.32.0 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org From: Jason Lai From: Ulf Hansson To allow an additional external regulator to be controlled by an mmc host driver, let's add support for a vqmmc2 regulator to the mmc core. For an SD UHS-II interface the vqmmc2 regulator may correspond to the so called vdd2 supply, as described by the SD spec. Initially, only 1.8V is needed, hence limit the new helper function, mmc_regulator_set_vqmmc2() to this too. Note that, to allow for flexibility mmc host drivers need to manage the enable/disable of the vqmmc2 regulator themselves, while the regulator is looked up through the common mmc_regulator_get_supply(). Signed-off-by: Ulf Hansson --- drivers/mmc/core/regulator.c | 33 +++++++++++++++++++++++++++++++++ include/linux/mmc/host.h | 8 ++++++++ 2 files changed, 41 insertions(+) -- 2.32.0 diff --git a/drivers/mmc/core/regulator.c b/drivers/mmc/core/regulator.c index 609201a46..640eb46e8 100644 --- a/drivers/mmc/core/regulator.c +++ b/drivers/mmc/core/regulator.c @@ -223,6 +223,32 @@ int mmc_regulator_set_vqmmc(struct mmc_host *mmc, struct mmc_ios *ios) } EXPORT_SYMBOL_GPL(mmc_regulator_set_vqmmc); +/** + * mmc_regulator_set_vqmmc2 - Set vqmmc2 as per the ios->vqmmc2_voltage + * @mmc: The mmc host to regulate + * @ios: The io bus settings + * + * Sets a new voltage level for the vqmmc2 regulator, which may correspond to + * the vdd2 regulator for an SD UHS-II interface. This function is expected to + * be called by mmc host drivers. + * + * Returns a negative error code on failure, zero if the voltage level was + * changed successfully or a positive value if the level didn't need to change. + */ +int mmc_regulator_set_vqmmc2(struct mmc_host *mmc, struct mmc_ios *ios) +{ + if (IS_ERR(mmc->supply.vqmmc2)) + return -EINVAL; + + switch (ios->vqmmc2_voltage) { + case MMC_VQMMC2_VOLTAGE_180: + return mmc_regulator_set_voltage_if_supported( + mmc->supply.vqmmc2, 1700000, 1800000, 1950000); + default: + return -EINVAL; + } +} +EXPORT_SYMBOL_GPL(mmc_regulator_set_vqmmc2); + #else static inline int mmc_regulator_get_ocrmask(struct regulator *supply) @@ -249,6 +275,7 @@ int mmc_regulator_get_supply(struct mmc_host *mmc) mmc->supply.vmmc = devm_regulator_get_optional(dev, "vmmc"); mmc->supply.vqmmc = devm_regulator_get_optional(dev, "vqmmc"); + mmc->supply.vqmmc2 = devm_regulator_get_optional(dev, "vqmmc2"); if (IS_ERR(mmc->supply.vmmc)) { if (PTR_ERR(mmc->supply.vmmc) == -EPROBE_DEFER) @@ -268,6 +295,12 @@ int mmc_regulator_get_supply(struct mmc_host *mmc) dev_dbg(dev, "No vqmmc regulator found\n"); } + if (IS_ERR(mmc->supply.vqmmc2)) { + if (PTR_ERR(mmc->supply.vqmmc2) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_dbg(dev, "No vqmmc2 regulator found\n"); + } + return 0; } EXPORT_SYMBOL_GPL(mmc_regulator_get_supply); diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 91065ab7f..69f8c8a8f 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -303,6 +303,7 @@ struct mmc_pwrseq; struct mmc_supply { struct regulator *vmmc; /* Card power supply */ struct regulator *vqmmc; /* Optional Vccq supply */ + struct regulator *vqmmc2; /* Optional supply for phy */ }; struct mmc_ctx { @@ -580,6 +581,7 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc, struct regulator *supply, unsigned short vdd_bit); int mmc_regulator_set_vqmmc(struct mmc_host *mmc, struct mmc_ios *ios); +int mmc_regulator_set_vqmmc2(struct mmc_host *mmc, struct mmc_ios *ios); #else static inline int mmc_regulator_set_ocr(struct mmc_host *mmc, struct regulator *supply, @@ -593,6 +595,12 @@ static inline int mmc_regulator_set_vqmmc(struct mmc_host *mmc, { return -EINVAL; } + +static inline int mmc_regulator_set_vqmmc2(struct mmc_host *mmc, + struct mmc_ios *ios) +{ + return -EINVAL; +} #endif int mmc_regulator_get_supply(struct mmc_host *mmc); From patchwork Tue Aug 31 11:02:27 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lai Jason X-Patchwork-Id: 504830 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C1335C432BE for ; Tue, 31 Aug 2021 11:02:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A5D9560FE8 for ; Tue, 31 Aug 2021 11:02:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S241228AbhHaLDd (ORCPT ); Tue, 31 Aug 2021 07:03:33 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44618 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S241180AbhHaLD1 (ORCPT ); Tue, 31 Aug 2021 07:03:27 -0400 Received: from mail-pf1-x431.google.com (mail-pf1-x431.google.com [IPv6:2607:f8b0:4864:20::431]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DFA43C06175F for ; Tue, 31 Aug 2021 04:02:31 -0700 (PDT) Received: by mail-pf1-x431.google.com with SMTP id y17so1279279pfl.13 for ; Tue, 31 Aug 2021 04:02:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=DaC8+5pzOWL+51MSRQ2wq9pB0Y+ekNkEjbWtSjXPhVc=; b=vepsR+JRgcGTwQ4p1e/BqlQNgU2P/5cU3nVu8rZOBfwvql6RzmKRQ9fgGNkfsq6QPm oUvl4cQ8UipYisuxOTIdbjw3yXCb3kAa+Y3Dz/u1N1QBMGhzwUmuNZm0xKtydbmnGlLo tw3bbz/jFvdxgrjCpPeBZLumrpbbUzLaFaxwhMU9X3w3lfF/Bn1KXJhMj3j5AILA7ZhP 8Gp7sKWRCumQPkA4ydCJTm+zNSbzxCtfM+R8uaWZjOpi/LFWv5Hfh1rObkGI7gZVvF2P iHeV4EU1h+y9PXgndyV5V1CdA5muhfx4HBvFnGyI7Jzq4Bo7IYvI3HP3LII0c+XOOIxJ B/BA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=DaC8+5pzOWL+51MSRQ2wq9pB0Y+ekNkEjbWtSjXPhVc=; b=LsQV9sWL4G7j/2+goaGbLA/ujrUYP3MWIFMveOeiDRGwWAYWcnvNy8p25WSZrKToUN ZDLIyTCaN4qcDqGVkMYyH4YMdBsJibasDaXv20dfk9RoTAG2Vpw02Btyk/jRiR/p92/q 7/u5o/jqOIlY6qMhdrrqJ4R4TkRqvI/fDehtrTkgLwTJLoajkJ5edyuGSnaN3/AsbLmd bAX58s3wIyB2gQ4OP6xEvEusC1vpZqYeOcx4YFKXjaGuyylJU6IAbrYqWvsr9wZPMvhU c/A4ShcA05Irj/AIsGQ1SGjCAKsuQRsfGjSqizwuUTOYtYV9vEuHi/ooScBhY9dFKEeH S2eA== X-Gm-Message-State: AOAM533QKAPu7+uBk7g1i09/czwDBDhpgUVbXZrNk5Hdk3Yocw7sfqog 76I5lTcjNULslgIv3jGJf2w= X-Google-Smtp-Source: ABdhPJxLoxmv0kZq5U2OXM2lRfQa30TXASi1fY0uaIkXkI6ECno5uXIvY+WTjMVW9V9OzDMsJCF8QA== X-Received: by 2002:a05:6a00:16d5:b0:3e0:f6ce:f2f7 with SMTP id l21-20020a056a0016d500b003e0f6cef2f7mr27420204pfc.78.1630407751060; Tue, 31 Aug 2021 04:02:31 -0700 (PDT) Received: from localhost.localdomain (60-251-58-169.HINET-IP.hinet.net. [60.251.58.169]) by smtp.gmail.com with ESMTPSA id a11sm19723247pgj.75.2021.08.31.04.02.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 31 Aug 2021 04:02:30 -0700 (PDT) From: Jason Lai To: ulf.hansson@linaro.org, takahiro.akashi@linaro.org, adrian.hunter@intel.com Cc: Jason Lai , linux-mmc@vger.kernel.org, ben.chuang@genesyslogic.com.tw, greg.tu@genesyslogic.com.tw Subject: [PATCH 6/7] mmc: core: Implement content of UHS-II card initialization functions Date: Tue, 31 Aug 2021 19:02:27 +0800 Message-Id: <20210831110227.50780-1-jasonlai.genesyslogic@gmail.com> X-Mailer: git-send-email 2.32.0 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org From: Jason Lai From: Jason Lai UHS-II card initialization flow is divided into 2 categories: PHY & Card. Part 1 - PHY Initialization: Every host controller may need their own avtivation operation to establish LINK between controller and card. So we add a new member function(uhs2_detect_init) in struct mmc_host_ops for host controller use. Part 2 - Card Initialization: This part can be divided into 6 substeps. 1. Send UHS-II CCMD DEVICE_INIT to card. 2. Send UHS-II CCMD ENUMERATE to card. 3. Send UHS-II Native Read CCMD to obtain capabilities in CFG_REG of card. 4. Host compares capabilities of host controller and card, then write the negotiated values to Setting field in CFG_REG of card through UHS-II Native Write CCMD. 5. Switch host controller's clock to Range B if it is supported by both host controller and card. 6. Execute legacy SD initialization flow. Signed-off-by: Jason Lai --- drivers/mmc/core/sd_uhs2.c | 918 +++++++++++++++++++++++++++++++++++-- 1 file changed, 868 insertions(+), 50 deletions(-) diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c index 24aa51a6d..d9ceee20d 100644 --- a/drivers/mmc/core/sd_uhs2.c +++ b/drivers/mmc/core/sd_uhs2.c @@ -3,6 +3,7 @@ * Copyright (C) 2021 Linaro Ltd * * Author: Ulf Hansson + * Author: Jason Lai * * Support for SD UHS-II cards */ @@ -10,29 +11,37 @@ #include #include +#include #include "core.h" #include "bus.h" #include "sd.h" #include "mmc_ops.h" +#include "sd_uhs2.h" static const unsigned int sd_uhs2_freqs[] = { 52000000, 26000000 }; -static int sd_uhs2_set_ios(struct mmc_host *host) +static void sd_uhs2_set_ios(struct mmc_host *host) { struct mmc_ios *ios = &host->ios; - return host->ops->uhs2_set_ios(host, ios); + pr_debug("%s: clock=%uHz, bus_mode=%u, power_mode=%u, ", + mmc_hostname(host), ios->clock, ios->bus_mode, + ios->power_mode); + pr_debug("cs=%u, Vdd=%u, width=%u, timing=%u\n", + ios->chip_select, ios->vdd, 1 << ios->bus_width, + ios->timing); + + host->ops->uhs2_set_ios(host, ios); } -static int sd_uhs2_power_up(struct mmc_host *host) +static void sd_uhs2_power_up(struct mmc_host *host) { host->ios.vdd = fls(host->ocr_avail) - 1; host->ios.clock = host->f_init; host->ios.timing = MMC_TIMING_SD_UHS2; host->ios.power_mode = MMC_POWER_UP; - return sd_uhs2_set_ios(host); + sd_uhs2_set_ios(host); } static void sd_uhs2_power_off(struct mmc_host *host) @@ -45,6 +54,43 @@ static void sd_uhs2_power_off(struct mmc_host *host) sd_uhs2_set_ios(host); } +/* + * uhs2_cmd_assemble - assemble and build up uhs2 command + * @cmd: MMC command + * @uhs2_cmd: UHS2 command + * @header: Value of packet header + * @arg: Argument of packet + * @payload: Payload of packet + * @plen: Payload length + * @resp: Buffer for response + * @resp_len: Response buffer length + * + * resp is inputted outside which should be a variable created by caller + * so caller should handle it. For SD command, there is no uhs2_resp and + * response should be stored in resp of mmc_command. + */ +static void uhs2_cmd_assemble(struct mmc_command *cmd, + struct uhs2_command *uhs2_cmd, + u16 header, u16 arg, + u32 *payload, u8 plen, u8 *resp, u8 resp_len) +{ + uhs2_cmd->header = header; + uhs2_cmd->arg = arg; + uhs2_cmd->payload = payload; + uhs2_cmd->payload_len = plen * sizeof(u32); + uhs2_cmd->packet_len = uhs2_cmd->payload_len + 4; + + cmd->uhs2_cmd = uhs2_cmd; + cmd->uhs2_resp = resp; + cmd->uhs2_resp_len = resp_len; + + pr_debug("%s: uhs2_cmd: header=0x%x arg=0x%x\n", + __func__, uhs2_cmd->header, uhs2_cmd->arg); + pr_debug("%s: payload_len=%d packet_len=%d resp_len=%d\n", + __func__, uhs2_cmd->payload_len, uhs2_cmd->packet_len, + cmd->uhs2_resp_len); +} + /* * Run the phy initialization sequence, which mainly relies on the UHS-II host * to check that we reach the expected electrical state, between the host and @@ -52,16 +98,104 @@ static void sd_uhs2_power_off(struct mmc_host *host) */ static int sd_uhs2_phy_init(struct mmc_host *host) { + int err = 0; + + /* + * Every host controller can assign its own function which is used + * to initialize PHY. + */ + if (host->ops->uhs2_detect_init) { + err = host->ops->uhs2_detect_init(host) + if (err) { + pr_err("%s: fail to detect UHS2!\n", + mmc_hostname(host)); + err = UHS2_PHY_INIT_ERR; + } + } + return 0; } /* * Do the early initialization of the card, by sending the device init -roadcast - * command and wait for the process to be completed. + * broadcast command and wait for the process to be completed. */ static int sd_uhs2_dev_init(struct mmc_host *host) { + struct mmc_command cmd = {0}; + struct uhs2_command uhs2_cmd = {}; + u32 cnt; + u32 dap, gap, gap1; + u16 header = 0, arg = 0; + u32 payload[1]; + u8 plen = 1; + u8 gd = 0, cf = 1; + u8 resp[6] = {0}; + u8 resp_len = 6; + int err; + + dap = host->uhs2_caps.dap; + gap = host->uhs2_caps.gap; + + header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD; + arg = ((UHS2_DEV_CMD_DEVICE_INIT & 0xFF) << 8) | + UHS2_NATIVE_CMD_WRITE | + UHS2_NATIVE_CMD_PLEN_4B | + (UHS2_DEV_CMD_DEVICE_INIT >> 8); + + /* need this for some cards */ + cmd.busy_timeout = 1000; + + for (cnt = 0; cnt < 30; cnt++) { + payload[0] = ((dap & 0xF) << 12) | + (cf << 11) | + ((gd & 0xF) << 4) | + (gap & 0xF); + + uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, + payload, plen, resp, resp_len); + + DBG("Begin DEVICE_INIT, header=0x%x, arg=0x%x, payload=0x%x.\n", + header, arg, payload[0]); + + DBG("Sending DEVICE_INIT. Count = %d\n", cnt); + err = mmc_wait_for_cmd(host, &cmd, 0); + + if (err) { + pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n", + mmc_hostname(host), __func__, err); + return -EIO; + } + + if (IS_ENABLED(CONFIG_MMC_DEBUG)) { + int i; + + pr_warn("%s: DEVICE_INIT response is: ", + mmc_hostname(host)); + for (i = 0; i < resp_len; i++) + pr_warn("0x%x ", resp[i]); + pr_warn("\n"); + } + + if (resp[3] != (UHS2_DEV_CMD_DEVICE_INIT & 0xFF)) { + pr_err("%s: DEVICE_INIT response is wrong!\n", + mmc_hostname(host)); + return -EIO; + } + + if (resp[5] & 0x8) { + DBG("CF is set, device is initialized!\n"); + host->group_desc = gd; + break; + } + gap1 = resp[4] & 0x0F; + if (gap == gap1) + gd++; + } + if (cnt == 30) { + pr_err("%s: DEVICE_INIT fail, already 30 times!\n", + mmc_hostname(host)); + return -EIO; + } + return 0; } @@ -70,8 +204,60 @@ static int sd_uhs2_dev_init(struct mmc_host *host) * Note that, we currently support only the point to point connection, which * means only one card can be attached per host/slot. */ -static int sd_uhs2_enum(struct mmc_host *host, u32 *node_id) +static int sd_uhs2_enum(struct mmc_host *host) { + struct mmc_command cmd = {0}; + struct uhs2_command uhs2_cmd = {}; + u16 header = 0, arg = 0; + u32 payload[1]; + u8 plen = 1; + u8 id_f = 0xF, id_l = 0x0; + u8 resp[8] = {0}; + u8 resp_len = 8; + int err; + + header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD; + arg = ((UHS2_DEV_CMD_ENUMERATE & 0xFF) << 8) | + UHS2_NATIVE_CMD_WRITE | + UHS2_NATIVE_CMD_PLEN_4B | + (UHS2_DEV_CMD_ENUMERATE >> 8); + + payload[0] = (id_f << 4) | id_l; + + DBG("Begin ENUMERATE, header=0x%x, arg=0x%x, payload=0x%x.\n", + header, arg, payload[0]); + uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, + resp, resp_len); + + err = mmc_wait_for_cmd(host, &cmd, 0); + if (err) { + pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n", + mmc_hostname(host), __func__, err); + return -EIO; + } + + if (IS_ENABLED(CONFIG_MMC_DEBUG)) { + int i; + + pr_warn("%s: ENUMERATE response is: ", mmc_hostname(host)); + for (i = 0; i < resp_len; i++) + pr_warn("0x%x ", resp[i]); + pr_warn("\n"); + } + + if (resp[3] != (UHS2_DEV_CMD_ENUMERATE & 0xFF)) { + pr_err("%s: ENUMERATE response is wrong!\n", + mmc_hostname(host)); + return -EIO; + } + + id_f = (resp[4] >> 4) & 0xF; + id_l = resp[4] & 0xF; + DBG("id_f = %d, id_l = %d.\n", id_f, id_l); + DBG("Enumerate Cmd Completed. No. of Devices connected = %d\n", + id_l - id_f + 1); + host->uhs2_dev_prop.node_id = id_f; + return 0; } @@ -80,8 +266,158 @@ static int sd_uhs2_enum(struct mmc_host *host, u32 *node_id) * commands and by parsing the responses. Store a copy of the relevant data in * card->uhs2_config. */ -static int sd_uhs2_config_read(struct mmc_host *host, struct mmc_card *card) +static int sd_uhs2_config_read(struct mmc_host *host) { + struct mmc_command cmd = {0}; + struct uhs2_command uhs2_cmd = {}; + u16 header = 0, arg = 0; + u32 cap; + int err; + + DBG("INQUIRY_CFG: read Generic Caps.\n"); + header = UHS2_NATIVE_PACKET | + UHS2_PACKET_TYPE_CCMD | + host->uhs2_dev_prop.node_id; + arg = ((UHS2_DEV_CONFIG_GEN_CAPS & 0xFF) << 8) | + UHS2_NATIVE_CMD_READ | + UHS2_NATIVE_CMD_PLEN_4B | + (UHS2_DEV_CONFIG_GEN_CAPS >> 8); + + pr_debug("Begin INQUIRY_CFG, header=0x%x, arg=0x%x.\n", header, arg); + /* There is no payload because per spec, there should be + * no payload field for read CCMD. + * Plen is set in arg. Per spec, plen for read CCMD + * represents the len of read data which is assigned in payload + * of following RES (p136). + */ + uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0); + + err = mmc_wait_for_cmd(host, &cmd, 0); + if (err) { + pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n", + mmc_hostname(host), __func__, err); + return -EIO; + } + + if (IS_ENABLED(CONFIG_MMC_DEBUG)) { + int i; + + pr_warn("%s: INQUIRY_CFG generic response is: ", + mmc_hostname(host)); + for (i = 0; i < 2; i++) + pr_warn("0x%x ", cmd.resp[i]); + pr_warn("\n"); + } + + cap = cmd.resp[0]; + pr_debug("Device Generic Caps (0-31) is: 0x%x.\n", cap); + host->uhs2_dev_prop.n_lanes = (cap >> UHS2_DEV_CONFIG_N_LANES_POS) & + UHS2_DEV_CONFIG_N_LANES_MASK; + host->uhs2_dev_prop.dadr_len = (cap >> UHS2_DEV_CONFIG_DADR_POS) & + UHS2_DEV_CONFIG_DADR_MASK; + host->uhs2_dev_prop.app_type = (cap >> UHS2_DEV_CONFIG_APP_POS) & + UHS2_DEV_CONFIG_APP_MASK; + + pr_debug("INQUIRY_CFG: read PHY Caps.\n"); + arg = ((UHS2_DEV_CONFIG_PHY_CAPS & 0xFF) << 8) | + UHS2_NATIVE_CMD_READ | + UHS2_NATIVE_CMD_PLEN_8B | + (UHS2_DEV_CONFIG_PHY_CAPS >> 8); + + pr_debug("Begin INQUIRY_CFG, header=0x%x, arg=0x%x.\n", header, arg); + uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0); + + err = mmc_wait_for_cmd(host, &cmd, 0); + if (err) { + pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n", + mmc_hostname(host), __func__, err); + return -EIO; + } + + if (IS_ENABLED(CONFIG_MMC_DEBUG)) { + int i; + + pr_warn("%s: INQUIRY_CFG PHY response is: ", + mmc_hostname(host)); + for (i = 0; i < 2; i++) + pr_warn("0x%x ", cmd.resp[i]); + pr_warn("\n"); + } + + cap = cmd.resp[0]; + pr_debug("Device PHY Caps (0-31) is: 0x%x.\n", cap); + host->uhs2_dev_prop.phy_minor_rev = cap & + UHS2_DEV_CONFIG_PHY_MINOR_MASK; + host->uhs2_dev_prop.phy_major_rev = (cap >> + UHS2_DEV_CONFIG_PHY_MAJOR_POS) & + UHS2_DEV_CONFIG_PHY_MAJOR_MASK; + host->uhs2_dev_prop.can_hibernate = (cap >> + UHS2_DEV_CONFIG_CAN_HIBER_POS) & + UHS2_DEV_CONFIG_CAN_HIBER_MASK; + + cap = cmd.resp[1]; + pr_debug("Device PHY Caps (32-63) is: 0x%x.\n", cap); + host->uhs2_dev_prop.n_lss_sync = cap & UHS2_DEV_CONFIG_N_LSS_SYN_MASK; + host->uhs2_dev_prop.n_lss_dir = (cap >> + UHS2_DEV_CONFIG_N_LSS_DIR_POS) & + UHS2_DEV_CONFIG_N_LSS_DIR_MASK; + if (host->uhs2_dev_prop.n_lss_sync == 0) + host->uhs2_dev_prop.n_lss_sync = 16 << 2; + else + host->uhs2_dev_prop.n_lss_sync <<= 2; + + if (host->uhs2_dev_prop.n_lss_dir == 0) + host->uhs2_dev_prop.n_lss_dir = 16 << 3; + else + host->uhs2_dev_prop.n_lss_dir <<= 3; + + pr_debug("INQUIRY_CFG: read LINK-TRAN Caps.\n"); + arg = ((UHS2_DEV_CONFIG_LINK_TRAN_CAPS & 0xFF) << 8) | + UHS2_NATIVE_CMD_READ | + UHS2_NATIVE_CMD_PLEN_8B | + (UHS2_DEV_CONFIG_LINK_TRAN_CAPS >> 8); + + pr_debug("Begin INQUIRY_CFG, header=0x%x, arg=0x%x.\n", header, arg); + uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, NULL, 0, NULL, 0); + + err = mmc_wait_for_cmd(host, &cmd, 0); + if (err) { + pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n", + mmc_hostname(host), __func__, err); + return -EIO; + } + + if (IS_ENABLED(CONFIG_MMC_DEBUG)) { + int i; + + pr_warn("%s: INQUIRY_CFG Link-Tran response is: ", + mmc_hostname(host)); + for (i = 0; i < 2; i++) + pr_warn("0x%x ", cmd.resp[i]); + pr_warn("\n"); + } + + cap = cmd.resp[0]; + DBG("Device LINK-TRAN Caps (0-31) is: 0x%x.\n", cap); + host->uhs2_dev_prop.link_minor_rev = cap & + UHS2_DEV_CONFIG_LT_MINOR_MASK; + host->uhs2_dev_prop.link_major_rev = (cap >> + UHS2_DEV_CONFIG_LT_MAJOR_POS) & + UHS2_DEV_CONFIG_LT_MAJOR_MASK; + host->uhs2_dev_prop.n_fcu = (cap >> UHS2_DEV_CONFIG_N_FCU_POS) & + UHS2_DEV_CONFIG_N_FCU_MASK; + host->uhs2_dev_prop.dev_type = (cap >> UHS2_DEV_CONFIG_DEV_TYPE_POS) & + UHS2_DEV_CONFIG_DEV_TYPE_MASK; + host->uhs2_dev_prop.maxblk_len = (cap >> + UHS2_DEV_CONFIG_MAX_BLK_LEN_POS) & + UHS2_DEV_CONFIG_MAX_BLK_LEN_MASK; + + cap = cmd.resp[1]; + DBG("Device LINK-TRAN Caps (32-63) is: 0x%x.\n", cap); + host->uhs2_dev_prop.n_data_gap = cap & UHS2_DEV_CONFIG_N_DATA_GAP_MASK; + if (host->uhs2_dev_prop.n_fcu == 0) + host->uhs2_dev_prop.n_fcu = 256; + return 0; } @@ -89,15 +425,393 @@ static int sd_uhs2_config_read(struct mmc_host *host, struct mmc_card *card) * Based on the card's and host's UHS-II capabilities, let's update the * configuration of the card and the host. This may also include to move to a * greater speed range/mode. Depending on the updated configuration, we may -eed - * to do a soft reset of the card via sending it a GO_DORMANT_STATE command. + * need to do a soft reset of the card via sending it a GO_DORMANT_STATE command. * - * In the final step, let's check if the card signals "config completion", -hich - * indicates that the card has moved from config state into active state. + * In the final step, let's check if the card signals "config completion", + * which indicates that the card has moved from config state into active state. */ -static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card) +static int sd_uhs2_config_write(struct mmc_host *host) +{ + struct mmc_command cmd = {0}; + struct uhs2_command uhs2_cmd = {}; + u16 header = 0, arg = 0; + u32 payload[2]; + u8 nMinDataGap; + u8 plen; + u8 try; + int err; + u8 resp[5] = {0}; + u8 resp_len = 5; + + pr_debug("SET_COMMON_CFG: write Generic Settings.\n"); + header = UHS2_NATIVE_PACKET | + UHS2_PACKET_TYPE_CCMD | host->uhs2_dev_prop.node_id; + arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) | + UHS2_NATIVE_CMD_WRITE | + UHS2_NATIVE_CMD_PLEN_8B | + (UHS2_DEV_CONFIG_GEN_SET >> 8); + + if (host->uhs2_dev_prop.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD && + host->uhs2_caps.n_lanes == UHS2_DEV_CONFIG_2L_HD_FD) { + /* Support HD */ + pr_debug("Both Host and device support 2L-HD.\n"); + host->flags |= MMC_UHS2_2L_HD; + host->uhs2_caps.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD; + host->uhs2_dev_prop.n_lanes_set = + UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD; + nMinDataGap = 1; + } else { + /* Only support 2L-FD so far */ + host->flags &= ~MMC_UHS2_2L_HD; + host->uhs2_caps.n_lanes_set = UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD; + host->uhs2_dev_prop.n_lanes_set = + UHS2_DEV_CONFIG_GEN_SET_2L_FD_HD; + nMinDataGap = 3; + } + + plen = 2; + payload[0] = host->uhs2_dev_prop.n_lanes_set << + UHS2_DEV_CONFIG_N_LANES_POS; + payload[1] = 0; + payload[0] = cpu_to_be32(payload[0]); + payload[1] = cpu_to_be32(payload[1]); + + pr_debug("Begin SET_COMMON_CFG, header=0x%x, arg=0x%x\n", header, arg); + pr_debug("UHS2 write Generic Settings %08x %08x\n", + payload[0], payload[1]); + pr_debug("flags=%08x, ", host->flags); + pr_debug("host_caps.n_lanes_set%x, ", host->uhs2_caps.n_lanes_set); + pr_debug("dev_prop.n_lanes_set=%x\n", host->uhs2_dev_prop.n_lanes_set); + + /* + * There is no payload because per spec, there should be + * no payload field for read CCMD. + * Plen is set in arg. Per spec, plen for read CCMD + * represents the len of read data which is assigned in payload + * of following RES (p136). + */ + uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, NULL, 0); + + err = mmc_wait_for_cmd(host, &cmd, 0); + if (err) { + pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n", + mmc_hostname(host), __func__, err); + return -EIO; + } + + pr_debug("SET_COMMON_CFG: PHY Settings.\n"); + arg = ((UHS2_DEV_CONFIG_PHY_SET & 0xFF) << 8) | + UHS2_NATIVE_CMD_WRITE | + UHS2_NATIVE_CMD_PLEN_8B | + (UHS2_DEV_CONFIG_PHY_SET >> 8); + + for (try = 0; try < 2; try++) { + plen = 2; + + if (host->uhs2_caps.speed_range == + UHS2_DEV_CONFIG_PHY_SET_SPEED_B) { + host->flags |= MMC_UHS2_SPEED_B; + host->uhs2_dev_prop.speed_range_set = + UHS2_DEV_CONFIG_PHY_SET_SPEED_B; + pr_debug("set dev_prop.speed_range_set to SPEED_B\n"); + } else { + host->uhs2_dev_prop.speed_range_set = + UHS2_DEV_CONFIG_PHY_SET_SPEED_A; + host->flags &= ~MMC_UHS2_SPEED_B; + pr_debug("set dev_prop.speed_range_set to SPEED_A\n"); + } + + payload[0] = host->uhs2_dev_prop.speed_range_set << + UHS2_DEV_CONFIG_PHY_SET_SPEED_POS; + + host->uhs2_dev_prop.n_lss_sync_set = + (min(host->uhs2_dev_prop.n_lss_sync, + host->uhs2_caps.n_lss_sync) >> 2) & + UHS2_DEV_CONFIG_N_LSS_SYN_MASK; + host->uhs2_caps.n_lss_sync_set = + host->uhs2_dev_prop.n_lss_sync_set; + + if (try == 0) { + host->uhs2_caps.n_lss_dir_set = + (host->uhs2_dev_prop.n_lss_dir >> 3) & + UHS2_DEV_CONFIG_N_LSS_DIR_MASK; + host->uhs2_dev_prop.n_lss_dir_set = + ((host->uhs2_caps.n_lss_dir >> 3) + 1) & + UHS2_DEV_CONFIG_N_LSS_DIR_MASK; + } else { + host->uhs2_dev_prop.n_lss_dir_set = + (max(host->uhs2_dev_prop.n_lss_dir, + host->uhs2_caps.n_lss_dir) >> 3) & + UHS2_DEV_CONFIG_N_LSS_DIR_MASK; + host->uhs2_caps.n_lss_dir_set = + host->uhs2_dev_prop.n_lss_dir_set; + payload[1] = (host->uhs2_dev_prop.n_lss_dir_set << + UHS2_DEV_CONFIG_N_LSS_DIR_POS) | + host->uhs2_dev_prop.n_lss_sync_set; + } + + payload[1] = (host->uhs2_dev_prop.n_lss_dir_set << + UHS2_DEV_CONFIG_N_LSS_DIR_POS) | + host->uhs2_dev_prop.n_lss_sync_set; + payload[0] = cpu_to_be32(payload[0]); + payload[1] = cpu_to_be32(payload[1]); + + pr_debug("UHS2 SET PHY Settings %08x %08x\n", + payload[0], payload[1]); + pr_debug("host->flags=%08x dev_prop.speed_range_set=%x\n", + host->flags, + host->uhs2_dev_prop.speed_range_set); + pr_debug("dev_prop.n_lss_sync_set=%x host_caps.n_lss_sync_set=%x\n", + host->uhs2_dev_prop.n_lss_sync_set, + host->uhs2_caps.n_lss_sync_set); + pr_debug("dev_prop.n_lss_dir_set=%x host_caps.n_lss_dir_set=%x\n", + host->uhs2_dev_prop.n_lss_dir_set, + host->uhs2_caps.n_lss_dir_set); + + pr_debug("Begin SET_COMMON_CFG header=0x%x arg=0x%x\n", + header, arg); + pr_debug("\t\tpayload[0]=0x%x payload[1]=0x%x\n", + payload[0], payload[1]); + + resp_len = 4; + memset(resp, 0, sizeof(resp)); + + uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, + payload, plen, resp, resp_len); + + err = mmc_wait_for_cmd(host, &cmd, 0); + if (err) { + pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n", + mmc_hostname(host), __func__, err); + return -EIO; + } + + if (!(resp[2] & 0x80)) + break; + + pr_debug("%s: %s: UHS2 SET PHY Settings fail, res= 0x%x!\n", + mmc_hostname(host), __func__, resp[2]); + } + + pr_debug("SET_COMMON_CFG: LINK-TRAN Settings.\n"); + arg = ((UHS2_DEV_CONFIG_LINK_TRAN_SET & 0xFF) << 8) | + UHS2_NATIVE_CMD_WRITE | + UHS2_NATIVE_CMD_PLEN_8B | + (UHS2_DEV_CONFIG_LINK_TRAN_SET >> 8); + + plen = 2; + + if (host->uhs2_dev_prop.app_type == UHS2_DEV_CONFIG_APP_SD_MEM) + host->uhs2_dev_prop.maxblk_len_set = + UHS2_DEV_CONFIG_LT_SET_MAX_BLK_LEN; + else + host->uhs2_dev_prop.maxblk_len_set = + min(host->uhs2_dev_prop.maxblk_len, + host->uhs2_caps.maxblk_len); + host->uhs2_caps.maxblk_len_set = host->uhs2_dev_prop.maxblk_len_set; + + host->uhs2_dev_prop.n_fcu_set = + min(host->uhs2_dev_prop.n_fcu, + host->uhs2_caps.n_fcu); + host->uhs2_caps.n_fcu_set = host->uhs2_dev_prop.n_fcu_set; + + host->uhs2_dev_prop.n_data_gap_set = + max(nMinDataGap, host->uhs2_dev_prop.n_data_gap); + + host->uhs2_caps.n_data_gap_set = host->uhs2_dev_prop.n_data_gap_set; + + host->uhs2_caps.max_retry_set = 3; + host->uhs2_dev_prop.max_retry_set = host->uhs2_caps.max_retry_set; + + payload[0] = (host->uhs2_dev_prop.maxblk_len_set << + UHS2_DEV_CONFIG_MAX_BLK_LEN_POS) | + (host->uhs2_dev_prop.max_retry_set << + UHS2_DEV_CONFIG_LT_SET_MAX_RETRY_POS) | + (host->uhs2_dev_prop.n_fcu_set << + UHS2_DEV_CONFIG_N_FCU_POS); + payload[1] = host->uhs2_dev_prop.n_data_gap_set; + payload[0] = cpu_to_be32(payload[0]); + payload[1] = cpu_to_be32(payload[1]); + + pr_debug("Begin SET_COMMON_CFG header=0x%x arg=0x%x\n", header, arg); + pr_debug("\t\tpayload[0]=0x%x payload[1]=0x%x\n", + payload[0], payload[1]); + + uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, NULL, 0); + + err = mmc_wait_for_cmd(host, &cmd, 0); + if (err) { + pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n", + mmc_hostname(host), __func__, err); + return -EIO; + } + + pr_debug("SET_COMMON_CFG: Set Config Completion.\n"); + arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) | + UHS2_NATIVE_CMD_WRITE | + UHS2_NATIVE_CMD_PLEN_8B | + (UHS2_DEV_CONFIG_GEN_SET >> 8); + + plen = 2; + payload[0] = 0; + payload[1] = UHS2_DEV_CONFIG_GEN_SET_CFG_COMPLETE; + payload[0] = cpu_to_be32(payload[0]); + payload[1] = cpu_to_be32(payload[1]); + + pr_debug("Begin SET_COMMON_CFG, "); + pr_debug("header=0x%x, arg=0x%x, payload[0] = 0x%x.\n", + header, arg, payload[0]); + resp_len = 5; + memset(resp, 0, sizeof(resp)); + uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, + resp, resp_len); + + err = mmc_wait_for_cmd(host, &cmd, 0); + if (err) { + pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n", + mmc_hostname(host), __func__, err); + return -EIO; + } + + /* Set host Config Setting registers */ + if (!host->ops->uhs2_set_reg || + host->ops->uhs2_set_reg(host, SET_CONFIG)) { + pr_err("%s: %s: UHS2 SET_CONFIG fail!\n", + mmc_hostname(host), __func__); + return -EIO; + } + + return 0; +} + +static int uhs2_go_dormant(struct mmc_host *host, bool hibernate) { + struct mmc_command cmd = {0}; + struct uhs2_command uhs2_cmd = {}; + u16 header = 0, arg = 0; + u32 payload[1]; + u8 plen = 1; + int err; + + /* Disable Normal INT */ + if (!host->ops->uhs2_set_reg || + host->ops->uhs2_set_reg(host, DISABLE_INT)) { + pr_err("%s: %s: UHS2 DISABLE_INT fail!\n", + mmc_hostname(host), __func__); + return -EIO; + } + + header = UHS2_NATIVE_PACKET | UHS2_PACKET_TYPE_CCMD | + host->uhs2_dev_prop.node_id; + + arg = ((UHS2_DEV_CMD_GO_DORMANT_STATE & 0xFF) << 8) | + UHS2_NATIVE_CMD_WRITE | + UHS2_NATIVE_CMD_PLEN_4B | + (UHS2_DEV_CMD_GO_DORMANT_STATE >> 8); + + if (hibernate) + payload[0] = UHS2_DEV_CMD_DORMANT_HIBER; + + pr_debug("Begin GO_DORMANT_STATE, "); + pr_debug("header=0x%x, arg=0x%x, payload=0x%x.\n", + header, arg, payload[0]); + uhs2_cmd_assemble(&cmd, &uhs2_cmd, header, arg, payload, plen, NULL, 0); + + err = mmc_wait_for_cmd(host, &cmd, 0); + if (err) { + pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n", + mmc_hostname(host), __func__, err); + return -EIO; + } + + /* Check Dormant State in Present */ + if (!host->ops->uhs2_set_reg || + host->ops->uhs2_set_reg(host, CHECK_DORMANT)) { + pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail!\n", + mmc_hostname(host), __func__); + return -EIO; + } + + if (host->ops->uhs2_disable_clk) + host->ops->uhs2_disable_clk(host); + + return 0; +} + +static int uhs2_change_speed(struct mmc_host *host) +{ + int err; + int timeout = 100; + + /* Change Speed Range at controller side. */ + if (!host->ops->uhs2_set_reg || + host->ops->uhs2_set_reg(host, SET_SPEED_B)) { + pr_err("%s: %s: UHS2 SET_SPEED fail!\n", + mmc_hostname(host), __func__); + return -EIO; + } + + err = uhs2_go_dormant(host, false); + if (err) { + pr_err("%s: %s: UHS2 GO_DORMANT_STATE fail, err= 0x%x!\n", + mmc_hostname(host), __func__, err); + return -EIO; + } + + /* restore sd clock */ + mmc_delay(5); + if (host->ops->uhs2_enable_clk) + host->ops->uhs2_enable_clk(host); + + /* Enable Normal INT */ + if (!host->ops->uhs2_set_reg || + host->ops->uhs2_set_reg(host, ENABLE_INT)) { + pr_err("%s: %s: UHS2 ENABLE_INT fail!\n", + mmc_hostname(host), __func__); + return -EIO; + } + + /* + * According to UHS-II Addendum Version 1.01, chapter 6.2.3, + * wait card switch to Active State + */ + header = UHS2_NATIVE_PACKET | + UHS2_PACKET_TYPE_CCMD | host->uhs2_dev_prop.node_id; + arg = ((UHS2_DEV_CONFIG_GEN_SET & 0xFF) << 8) | + UHS2_NATIVE_CMD_READ | + UHS2_NATIVE_CMD_PLEN_8B | + (UHS2_DEV_CONFIG_GEN_SET >> 8); + do { + uhs2_cmd_assemble(&cmd, &uhs2_cmd, + header, arg, + NULL, 0, + NULL, 0); + err = mmc_wait_for_cmd(host, &cmd, 0); + if (err) { + pr_err("%s: %s: UHS2 CMD send fail, err= 0x%x!\n", + mmc_hostname(host), __func__, err); + return -EIO; + } + + pr_debug("%s: Device Generic Settings Register=0x%x-0x%x\n", + mmc_hostname(host), cmd.resp[0], cmd.resp[1]) + if (cmd.resp[1] & UHS2_DEV_CONFIG_GEN_SET_CFG_COMPLETE) { + pr_debug("%s: %s: UHS2 switch to range B OK\n", + mmc_hostname(host), __func__); + break; + } + + timeout--; + if (timeout == 0) { + pr_err("%s: %s: UHS2 not switch to Active in 100ms.\n", + mmc_hostname(host->mmc), __func__); + return -EIO; + } + + mmc_delay(1); + + } while (1); + return 0; } @@ -108,8 +822,67 @@ static int sd_uhs2_config_write(struct mmc_host *host, struct mmc_card *card) * be set through a legacy CMD6. Note that, the power limit that becomes set, * survives a soft reset through the GO_DORMANT_STATE command. */ -static int sd_uhs2_legacy_init(struct mmc_host *host, struct mmc_card *card) +static int sd_uhs2_legacy_init(struct mmc_host *host) { + int err; + u32 ocr, rocr; + + WARN_ON(!host->claimed); + + /* Send CMD0 to reset SD card */ + mmc_go_idle(host); + + /* Send CMD8 to communicate SD interface operation condition */ + err = mmc_send_if_cond(host, host->ocr_avail); + if (err) { + pr_err("%s: %s: SEND_IF_COND fail!\n", + mmc_hostname(host), __func__); + return err; + } + + /* + * Get CID, send relative address, get CSD are the same as legacy + * sd, call mmc_sd_init_card() in sd.c directly + */ + err = mmc_send_app_op_cond(host, 0, &ocr); + if (err) { + pr_err("%s: %s: SD_SEND_OP_COND fail!\n", + mmc_hostname(host), __func__); + return err; + } + + if (host->ocr_avail_sd) + host->ocr_avail = host->ocr_avail_sd; + + /* + * Some SD cards claims an out of spec VDD voltage range. Let's treat + * these bits as being in-valid and especially also bit7. + */ + ocr &= ~0x7FFF; + rocr = mmc_select_voltage(host, ocr); + + /* + * Some cards have zero value of rocr in UHS-II mode. Assign host's ocr + * value to rocr. + */ + if (!rocr) { + if (host->ocr_avail) { + rocr = host->ocr_avail; + } else { + pr_err("%s: %s: there is no valid OCR.\n", + mmc_hostname(host), __func__); + return -EINVAL; + } + } + + /* + * Get CID, send relative address, get CSD are the same as legacy + * sd, call mmc_sd_init_card() in sd.c directly + */ + err = mmc_sd_init_card(host, rocr, NULL); + if (err) { + pr_err("%s: %s: mmc_sd_init_card() fail!\n", + mmc_hostname(host), __func__); + return err; + } + return 0; } @@ -120,42 +893,49 @@ static int sd_uhs2_legacy_init(struct mmc_host *host, struct mmc_card *card) static int sd_uhs2_init_card(struct mmc_host *host) { struct mmc_card *card; - u32 node_id; int err; err = sd_uhs2_dev_init(host); - if (err) + if (err) { + pr_err("%s: UHS2 DEVICE_INIT fail!\n", mmc_hostname(host)); return err; + } - err = sd_uhs2_enum(host, &node_id); + err = sd_uhs2_enum(host); + if (err) { + pr_err("%s: UHS2 ENUMERATE fail!\n", mmc_hostname(host)); + return err; + } + + err = sd_uhs2_config_read(host); if (err) return err; - card = mmc_alloc_card(host, &sd_type); - if (IS_ERR(card)) - return PTR_ERR(card); + err = sd_uhs2_config_write(host); + if (err) + return err; - card->uhs2_config.node_id = node_id; - card->type = MMC_TYPE_SD; + /* Delay some time to let card apply new configuration */ + mmc_delay(10); - err = sd_uhs2_config_read(host, card); - if (err) - goto err; + /* Change to Speed Range B if it is supported */ + if (host->flags & MMC_UHS2_SPEED_B) { + err = uhs2_change_speed(host); + if (err) { + pr_err("%s: %s: UHS2 uhs2_change_speed() fail!\n", + mmc_hostname(host), __func__); + return err; + } + } - err = sd_uhs2_config_write(host, card); - if (err) - goto err; + host->flags |= MMC_UHS2_INITIALIZED; - err = sd_uhs2_legacy_init(host, card); + err = sd_uhs2_legacy_init(host); if (err) - goto err; + return err; host->card = card; return 0; - -err: - mmc_remove_card(card); - return err; } static void sd_uhs2_remove(struct mmc_host *host) @@ -234,38 +1014,66 @@ static int sd_uhs2_attach(struct mmc_host *host) int err; err = sd_uhs2_power_up(host); - if (err) + if (err) { + pr_debug("%s: %s: sd_uhs2_power_up() fail!\n", + mmc_hostname(host), __func__); goto err; + } err = sd_uhs2_phy_init(host); - if (err) + if (err) { + pr_debug("%s: %s: sd_uhs2_phy_init() fail!\n", + mmc_hostname(host), __func__); goto err; + } err = sd_uhs2_init_card(host); - if (err) + if (err) { + pr_debug("%s: %s: sd_uhs2_init_card() fail!\n", + mmc_hostname(host), __func__); goto err; + } mmc_attach_bus(host, &sd_uhs2_ops); mmc_release_host(host); err = mmc_add_card(host->card); - if (err) + if (err) { + pr_debug("%s: %s: mmc_add_card() fail!\n", + mmc_hostname(host), __func__); goto remove_card; + } mmc_claim_host(host); + + if (host->ops->uhs2_post_attach_sd) + host->ops->uhs2_post_attach_sd(host); + return 0; remove_card: - mmc_remove_card(host->card); - host->card = NULL; + sd_uhs2_remove(host); mmc_claim_host(host); - mmc_detach_bus(host); + err: + mmc_detach_bus(host); sd_uhs2_power_off(host); + if (host->flags & MMC_UHS2_INITIALIZED) + host->flags &= ~MMC_UHS2_INITIALIZED; + host->flags &= ~MMC_UHS2_SUPPORT; return err; } +/** + * mmc_attach_sd_uhs2 - select UHS2 interface + * @host: MMC host + * + * Try to select UHS2 interface and initialize the bus for a given + * frequency, @freq. + * + * Return: 0 on success, non-zero error on failure + */ int mmc_attach_sd_uhs2(struct mmc_host *host) { int i, err = 0; @@ -273,8 +1081,12 @@ int mmc_attach_sd_uhs2(struct mmc_host *host) if (!(host->caps2 & MMC_CAP2_SD_UHS2)) return -EOPNOTSUPP; - /* Turn off the legacy SD interface before trying with UHS-II. */ - mmc_power_off(host); + host->flags |= MMC_UHS2_SUPPORT; + + if (!(host->caps2 & MMC_CAP2_NO_SDIO)) + pr_info("mmc_card_sd_uhs2: sdio\n"); + if (!(host->caps2 & MMC_CAP2_NO_SD)) + pr_info("mmc_card_sd_uhs2: sd\n"); /* * Start UHS-II initialization at 52MHz and possibly make a retry at @@ -282,11 +1094,17 @@ int mmc_attach_sd_uhs2(struct mmc_host *host) * validates ios->clock, to set a rate within the correct range. */ for (i = 0; i < ARRAY_SIZE(sd_uhs2_freqs); i++) { - host->f_init = sd_uhs2_freqs[i]; - err = sd_uhs2_attach(host); - if (!err) - break; - } + host->f_init = sd_uhs2_freqs[i]; + pr_debug("%s: %s: trying to init card at %u Hz\n", + mmc_hostname(host), __func__, host->f_init); - return err; + err = sd_uhs2_attach(host); + if (!err) + break; + else + pr_debug("%s: %s: init card at %u Hz failed\n", + mmc_hostname(host), __func__, + host->f_init); + } + + return err; }