From patchwork Mon Feb 25 09:24:15 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanley Chu X-Patchwork-Id: 10828389 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 0605913B5 for ; Mon, 25 Feb 2019 09:25:49 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E29202AACA for ; Mon, 25 Feb 2019 09:25:48 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D68952AC2A; Mon, 25 Feb 2019 09:25:48 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED,UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 17B6E2AC27 for ; Mon, 25 Feb 2019 09:25:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=vIzQZ9aCbldWVVhFsVRvm3wUCmhkr7of+B50fI3LtJ4=; b=SuMLKeMbKB49+L Vg0IcfTLP+CTRYNGwWBjvg+HqtjxnKD37zef49AbpVKaLMDj+c04x9jwERgD97oE/Ib+1R0qqOAVS s6AY4J8Vyu/e7JwNLnaMGyi+4ggNzrv6gG/w4RdlccNLuuwpi+STvlguur2lb5VCaIgNhM1kACflm rKDcMa5kWH+ssDYSjo4xMsw+5NSisZ1fNWS2/BJ12w5ui1oMyEYurru8Is2k1rSogKv3aKN6BA7XY AmozWV2UtVkvPlRHVjW7Rrw4D8mL4ue15vhmT0FjEGPeE56gxgghKYgGe7MA2u6Z/oFsGTcQ8n8Um iKET+gkrDwhwqyyAvpIg==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gyCVg-0006WU-ER; Mon, 25 Feb 2019 09:25:16 +0000 Received: from mailgw01.mediatek.com ([216.200.240.184]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gyCVQ-000539-T5 for linux-mediatek@lists.infradead.org; Mon, 25 Feb 2019 09:25:10 +0000 X-UUID: 339898e2e6294ba1ae04f16b9fb7a259-20190225 X-UUID: 339898e2e6294ba1ae04f16b9fb7a259-20190225 Received: from mtkcas67.mediatek.inc [(172.29.193.45)] by mailgw01.mediatek.com (envelope-from ) (musrelay.mediatek.com ESMTP with TLS) with ESMTP id 95264264; Mon, 25 Feb 2019 01:24:40 -0800 Received: from mtkmbs08n1.mediatek.inc (172.21.101.55) by MTKMBS62N1.mediatek.inc (172.29.193.41) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Mon, 25 Feb 2019 01:24:39 -0800 Received: from mtkcas08.mediatek.inc (172.21.101.126) by mtkmbs08n1.mediatek.inc (172.21.101.55) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Mon, 25 Feb 2019 17:24:37 +0800 Received: from mtkswgap22.mediatek.inc (172.21.77.33) by mtkcas08.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1395.4 via Frontend Transport; Mon, 25 Feb 2019 17:24:37 +0800 From: To: , , , , , , , , , , Subject: [PATCH v2 6/6] scsi: ufs-mediatek: Add UFS support for Mediatek SoC chips Date: Mon, 25 Feb 2019 17:24:15 +0800 Message-ID: <1551086655-5029-8-git-send-email-stanley.chu@mediatek.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1551086655-5029-1-git-send-email-stanley.chu@mediatek.com> References: <1551086655-5029-1-git-send-email-stanley.chu@mediatek.com> MIME-Version: 1.0 X-MTK: N X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190225_012501_359660_DBD48C23 X-CRM114-Status: GOOD ( 21.42 ) X-BeenThere: linux-mediatek@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: chun-hung.wu@mediatek.com, kuohong.wang@mediatek.com, matthias.bgg@gmail.com, linux-mediatek@lists.infradead.org, liwei213@huawei.com, vivek.gautam@codeaurora.org, subhashj@codeaurora.org, Stanley Chu , peter.wang@mediatek.com Sender: "Linux-mediatek" Errors-To: linux-mediatek-bounces+patchwork-linux-mediatek=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP From: Stanley Chu This patch adds UFS support for Mediatek SoC chips. Signed-off-by: Stanley Chu Reviewed-by: Avri Altman --- drivers/scsi/ufs/Kconfig | 14 ++ drivers/scsi/ufs/Makefile | 1 + drivers/scsi/ufs/ufs-mediatek.c | 377 ++++++++++++++++++++++++++++++++ drivers/scsi/ufs/ufs-mediatek.h | 61 ++++++ 4 files changed, 453 insertions(+) create mode 100644 drivers/scsi/ufs/ufs-mediatek.c create mode 100644 drivers/scsi/ufs/ufs-mediatek.h diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig index 2ddbb26d9c26..58e5a9c8eb5d 100644 --- a/drivers/scsi/ufs/Kconfig +++ b/drivers/scsi/ufs/Kconfig @@ -109,6 +109,20 @@ config SCSI_UFS_QCOM Select this if you have UFS controller on QCOM chipset. If unsure, say N. +config SCSI_UFS_MEDIATEK + tristate "Mediatek specific hooks to UFS controller platform driver" + depends on SCSI_UFSHCD_PLATFORM && ARCH_MEDIATEK + select PHY_MTK_UFS + help + This selects the Mediatek specific additions to UFSHCD platform driver. + UFS host on Mediatek needs some vendor specific configuration before + accessing the hardware which includes PHY configuration and vendor + specific registers. + + Select this if you have UFS controller on Mediatek chipset. + + If unsure, say N. + config SCSI_UFS_HISI tristate "Hisilicon specific hooks to UFS controller platform driver" depends on (ARCH_HISI || COMPILE_TEST) && SCSI_UFSHCD_PLATFORM diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile index a3bd70c3652c..2a9097939bcb 100644 --- a/drivers/scsi/ufs/Makefile +++ b/drivers/scsi/ufs/Makefile @@ -10,3 +10,4 @@ ufshcd-core-$(CONFIG_SCSI_UFS_BSG) += ufs_bsg.o obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o obj-$(CONFIG_SCSI_UFS_HISI) += ufs-hisi.o +obj-$(CONFIG_SCSI_UFS_MEDIATEK) += ufs-mediatek.o diff --git a/drivers/scsi/ufs/ufs-mediatek.c b/drivers/scsi/ufs/ufs-mediatek.c new file mode 100644 index 000000000000..ce633dff5888 --- /dev/null +++ b/drivers/scsi/ufs/ufs-mediatek.c @@ -0,0 +1,377 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Authors: + * Stanley Chu + * Peter Wang + * + * Copyright (C) 2019 MediaTek Inc. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ + +#include +#include +#include +#include + +#include "ufshcd.h" +#include "ufshcd-pltfrm.h" +#include "unipro.h" +#include "ufs-mediatek.h" + +void ufs_mtk_cfg_unipro_cg(struct ufs_hba *hba, bool enable) +{ + u32 tmp; + + if (enable) { + ufshcd_dme_get(hba, + UIC_ARG_MIB(VS_SAVEPOWERCONTROL), &tmp); + tmp = tmp | + (1 << RX_SYMBOL_CLK_GATE_EN) | + (1 << SYS_CLK_GATE_EN) | + (1 << TX_CLK_GATE_EN); + ufshcd_dme_set(hba, + UIC_ARG_MIB(VS_SAVEPOWERCONTROL), tmp); + + ufshcd_dme_get(hba, + UIC_ARG_MIB(VS_DEBUGCLOCKENABLE), &tmp); + tmp = tmp & ~(1 << TX_SYMBOL_CLK_REQ_FORCE); + ufshcd_dme_set(hba, + UIC_ARG_MIB(VS_DEBUGCLOCKENABLE), tmp); + } else { + ufshcd_dme_get(hba, + UIC_ARG_MIB(VS_SAVEPOWERCONTROL), &tmp); + tmp = tmp & ~((1 << RX_SYMBOL_CLK_GATE_EN) | + (1 << SYS_CLK_GATE_EN) | + (1 << TX_CLK_GATE_EN)); + ufshcd_dme_set(hba, + UIC_ARG_MIB(VS_SAVEPOWERCONTROL), tmp); + + ufshcd_dme_get(hba, + UIC_ARG_MIB(VS_DEBUGCLOCKENABLE), &tmp); + tmp = tmp | (1 << TX_SYMBOL_CLK_REQ_FORCE); + ufshcd_dme_set(hba, + UIC_ARG_MIB(VS_DEBUGCLOCKENABLE), tmp); + } +} + +int ufs_mtk_bind_mphy(struct ufs_hba *hba) +{ + struct ufs_mtk_host *host = ufshcd_get_variant(hba); + struct device *dev = hba->dev; + int err = 0; + + host->mphy = devm_phy_get(dev, "ufsphy"); + + if (host->mphy == ERR_PTR(-EPROBE_DEFER)) { + /* + * UFS driver might be probed before the phy driver does. + * In that case we would like to return EPROBE_DEFER code. + */ + err = -EPROBE_DEFER; + dev_info(dev, + "%s: required phy hasn't probed yet. err = %d\n", + __func__, err); + } else if (IS_ERR(host->mphy)) { + err = PTR_ERR(host->mphy); + dev_info(dev, "%s: PHY get failed %d\n", __func__, err); + } + + if (err) + host->mphy = NULL; + + return err; +} + +/** + * ufs_mtk_setup_clocks - enables/disable clocks + * @hba: host controller instance + * @on: If true, enable clocks else disable them. + * @status: PRE_CHANGE or POST_CHANGE notify + * + * Returns 0 on success, non-zero on failure. + */ +static int ufs_mtk_setup_clocks(struct ufs_hba *hba, bool on, + enum ufs_notify_change_status status) +{ + struct ufs_mtk_host *host = ufshcd_get_variant(hba); + int ret; + + /* + * In case ufs_mtk_init() is not yet done, simply ignore. + * This ufs_mtk_setup_clocks() shall be called from + * ufs_mtk_init() after init is done. + */ + if (!host) + return 0; + + switch (status) { + case PRE_CHANGE: + if (!on) + ret = phy_power_off(host->mphy); + break; + case POST_CHANGE: + if (on) { + pr_info("%s: phy_power_on\n", __func__); + ret = phy_power_on(host->mphy); + } + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * ufs_mtk_init - find other essential mmio bases + * @hba: host controller instance + * + * Binds PHY with controller and powers up PHY enabling clocks + * and regulators. + * + * Returns -EPROBE_DEFER if binding fails, returns negative error + * on phy power up failure and returns zero on success. + */ +static int ufs_mtk_init(struct ufs_hba *hba) +{ + struct ufs_mtk_host *host; + struct device *dev = hba->dev; + int err = 0; + + host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); + if (!host) { + err = -ENOMEM; + dev_info(dev, "%s: no memory for mtk ufs host\n", __func__); + goto out; + } + + host->hba = hba; + ufshcd_set_variant(hba, host); + + err = ufs_mtk_bind_mphy(hba); + if (err) + goto out_variant_clear; + + /* + * ufshcd_vops_init() is invoked after + * ufshcd_setup_clock(true) in ufshcd_hba_init() thus + * phy clock setup is skipped. + * + * Enable phy clocks specifically here. + */ + ufs_mtk_setup_clocks(hba, true, POST_CHANGE); + + goto out; + +out_variant_clear: + ufshcd_set_variant(hba, NULL); +out: + return err; +} + +static int ufs_mtk_pre_pwr_change(struct ufs_hba *hba, + struct ufs_pa_layer_attr *dev_max_params, + struct ufs_pa_layer_attr *dev_req_params) +{ + struct ufs_dev_params host_cap; + int ret; + + host_cap.tx_lanes = UFS_MTK_LIMIT_NUM_LANES_TX; + host_cap.rx_lanes = UFS_MTK_LIMIT_NUM_LANES_RX; + host_cap.hs_rx_gear = UFS_MTK_LIMIT_HSGEAR_RX; + host_cap.hs_tx_gear = UFS_MTK_LIMIT_HSGEAR_TX; + host_cap.pwm_rx_gear = UFS_MTK_LIMIT_PWMGEAR_RX; + host_cap.pwm_tx_gear = UFS_MTK_LIMIT_PWMGEAR_TX; + host_cap.rx_pwr_pwm = UFS_MTK_LIMIT_RX_PWR_PWM; + host_cap.tx_pwr_pwm = UFS_MTK_LIMIT_TX_PWR_PWM; + host_cap.rx_pwr_hs = UFS_MTK_LIMIT_RX_PWR_HS; + host_cap.tx_pwr_hs = UFS_MTK_LIMIT_TX_PWR_HS; + host_cap.hs_rate = UFS_MTK_LIMIT_HS_RATE; + host_cap.desired_working_mode = + UFS_MTK_LIMIT_DESIRED_MODE; + + ret = ufshcd_get_pwr_dev_param(&host_cap, + dev_max_params, + dev_req_params); + if (ret) { + pr_info("%s: failed to determine capabilities\n", + __func__); + } + + return ret; +} + +static int ufs_mtk_pwr_change_notify(struct ufs_hba *hba, + enum ufs_notify_change_status stage, + struct ufs_pa_layer_attr *dev_max_params, + struct ufs_pa_layer_attr *dev_req_params) +{ + int ret = 0; + + switch (stage) { + case PRE_CHANGE: + ret = ufs_mtk_pre_pwr_change(hba, dev_max_params, + dev_req_params); + break; + case POST_CHANGE: + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int ufs_mtk_pre_link(struct ufs_hba *hba) +{ + int ret; + u32 tmp; + + /* disable deep stall */ + ret = ufshcd_dme_get(hba, UIC_ARG_MIB(VS_SAVEPOWERCONTROL), &tmp); + if (ret) + return ret; + + tmp &= ~(1 << 6); + + ret = ufshcd_dme_set(hba, UIC_ARG_MIB(VS_SAVEPOWERCONTROL), tmp); + + return ret; +} + +static int ufs_mtk_post_link(struct ufs_hba *hba) +{ + /* disable device LCC */ + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE), 0); + + /* enable unipro clock gating feature */ + ufs_mtk_cfg_unipro_cg(hba, true); + + return 0; +} + +static int ufs_mtk_link_startup_notify(struct ufs_hba *hba, + enum ufs_notify_change_status stage) +{ + int ret = 0; + + switch (stage) { + case PRE_CHANGE: + ret = ufs_mtk_pre_link(hba); + break; + case POST_CHANGE: + ret = ufs_mtk_post_link(hba); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int ufs_mtk_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) +{ + struct ufs_mtk_host *host = ufshcd_get_variant(hba); + + if (ufshcd_is_link_hibern8(hba)) + phy_power_off(host->mphy); + + return 0; +} + +static int ufs_mtk_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) +{ + struct ufs_mtk_host *host = ufshcd_get_variant(hba); + + if (ufshcd_is_link_hibern8(hba)) + phy_power_on(host->mphy); + + return 0; +} + +/** + * struct ufs_hba_mtk_vops - UFS MTK specific variant operations + * + * The variant operations configure the necessary controller and PHY + * handshake during initialization. + */ +static struct ufs_hba_variant_ops ufs_hba_mtk_vops = { + .name = "mediatek.ufshci", + .init = ufs_mtk_init, + .setup_clocks = ufs_mtk_setup_clocks, + .link_startup_notify = ufs_mtk_link_startup_notify, + .pwr_change_notify = ufs_mtk_pwr_change_notify, + .suspend = ufs_mtk_suspend, + .resume = ufs_mtk_resume, +}; + +/** + * ufs_mtk_probe - probe routine of the driver + * @pdev: pointer to Platform device handle + * + * Return zero for success and non-zero for failure + */ +static int ufs_mtk_probe(struct platform_device *pdev) +{ + int err; + struct device *dev = &pdev->dev; + + /* perform generic probe */ + err = ufshcd_pltfrm_init(pdev, &ufs_hba_mtk_vops); + if (err) + dev_info(dev, "probe failed %d\n", err); + + return err; +} + +/** + * ufs_mtk_remove - set driver_data of the device to NULL + * @pdev: pointer to platform device handle + * + * Always return 0 + */ +static int ufs_mtk_remove(struct platform_device *pdev) +{ + struct ufs_hba *hba = platform_get_drvdata(pdev); + + pm_runtime_get_sync(&(pdev)->dev); + ufshcd_remove(hba); + return 0; +} + +const struct of_device_id ufs_mtk_of_match[] = { + { .compatible = "mediatek,ufshci"}, + {}, +}; + +static const struct dev_pm_ops ufs_mtk_pm_ops = { + .suspend = ufshcd_pltfrm_suspend, + .resume = ufshcd_pltfrm_resume, + .runtime_suspend = ufshcd_pltfrm_runtime_suspend, + .runtime_resume = ufshcd_pltfrm_runtime_resume, + .runtime_idle = ufshcd_pltfrm_runtime_idle, +}; + +static struct platform_driver ufs_mtk_pltform = { + .probe = ufs_mtk_probe, + .remove = ufs_mtk_remove, + .shutdown = ufshcd_pltfrm_shutdown, + .driver = { + .name = "ufshcd-mtk", + .owner = THIS_MODULE, + .pm = &ufs_mtk_pm_ops, + .of_match_table = ufs_mtk_of_match, + }, +}; + +module_platform_driver(ufs_mtk_pltform); + diff --git a/drivers/scsi/ufs/ufs-mediatek.h b/drivers/scsi/ufs/ufs-mediatek.h new file mode 100644 index 000000000000..3c964e5d9077 --- /dev/null +++ b/drivers/scsi/ufs/ufs-mediatek.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2019 MediaTek Inc. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ + +#ifndef _UFS_MEDIATEK_H +#define _UFS_MEDIATEK_H + +/* + * Vendor specific pre-defined parameters + */ +#define UFS_MTK_LIMIT_NUM_LANES_RX 1 +#define UFS_MTK_LIMIT_NUM_LANES_TX 1 +#define UFS_MTK_LIMIT_HSGEAR_RX UFS_HS_G3 +#define UFS_MTK_LIMIT_HSGEAR_TX UFS_HS_G3 +#define UFS_MTK_LIMIT_PWMGEAR_RX UFS_PWM_G4 +#define UFS_MTK_LIMIT_PWMGEAR_TX UFS_PWM_G4 +#define UFS_MTK_LIMIT_RX_PWR_PWM SLOW_MODE +#define UFS_MTK_LIMIT_TX_PWR_PWM SLOW_MODE +#define UFS_MTK_LIMIT_RX_PWR_HS FAST_MODE +#define UFS_MTK_LIMIT_TX_PWR_HS FAST_MODE +#define UFS_MTK_LIMIT_HS_RATE PA_HS_MODE_B +#define UFS_MTK_LIMIT_DESIRED_MODE UFS_HS_MODE + +/* + * Other attributes + */ +#define VS_DEBUGCLOCKENABLE 0xD0A1 +#define VS_SAVEPOWERCONTROL 0xD0A6 +#define VS_UNIPROPOWERDOWNCONTROL 0xD0A8 + +/* + * VS_DEBUGCLOCKENABLE + */ +enum { + TX_SYMBOL_CLK_REQ_FORCE = 5, +}; + +/* + * VS_SAVEPOWERCONTROL + */ +enum { + RX_SYMBOL_CLK_GATE_EN = 0, + SYS_CLK_GATE_EN = 2, + TX_CLK_GATE_EN = 3, +}; + +struct ufs_mtk_host { + struct ufs_hba *hba; + struct phy *mphy; +}; + +#endif /* !_UFS_MEDIATEK_H */ +