* [PATCH 2/2] scsi: ufs: add Exynos-specific driver
[not found] <CGME20171128053631epcas1p389908b55511552b79b4083109dddc515@epcas1p3.samsung.com>
@ 2017-11-28 5:36 ` 김기웅
2017-11-28 8:19 ` Jaehoon Chung
2017-11-28 14:24 ` Christoph Hellwig
0 siblings, 2 replies; 8+ messages in thread
From: 김기웅 @ 2017-11-28 5:36 UTC (permalink / raw)
To: linux-scsi, Martin K. Petersen
Cc: cpgs, HeonGwang Chu, 김부진, YOUNGEUN PARK
This driver is to use UFS devices on Exynos SoC and
has been already used for many years for commercial products.
Signed-off-by: Kiwoong Kim <kwmad.kim@samsung.com>
---
drivers/scsi/ufs/Kconfig | 10 +
drivers/scsi/ufs/Makefile | 1 +
drivers/scsi/ufs/ufs-exynos.c | 962 ++++++++++++++++++++++++++++++++++++++++++
drivers/scsi/ufs/ufs-exynos.h | 351 +++++++++++++++
drivers/scsi/ufs/ufshcd.h | 1 +
5 files changed, 1325 insertions(+)
create mode 100644 drivers/scsi/ufs/ufs-exynos.c
create mode 100644 drivers/scsi/ufs/ufs-exynos.h
diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig
index e27b4d4e6ae2..7d71ad8768c3 100644
--- a/drivers/scsi/ufs/Kconfig
+++ b/drivers/scsi/ufs/Kconfig
@@ -100,3 +100,13 @@ config SCSI_UFS_QCOM
Select this if you have UFS controller on QCOM chipset.
If unsure, say N.
+
+config SCSI_UFS_EXYNOS
+ tristate "EXYNOS UFS Host Controller Driver"
+ depends on SCSI_UFSHCD && SCSI_UFSHCD_PLATFORM
+ ---help---
+ This selects the EXYNOS UFS host controller driver.
+
+ If you have a controller with this interface, say Y or M here.
+
+ If unsure, say N.
diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile
index 9310c6c83041..3312b052dcff 100644
--- a/drivers/scsi/ufs/Makefile
+++ b/drivers/scsi/ufs/Makefile
@@ -3,6 +3,7 @@
obj-$(CONFIG_SCSI_UFS_DWC_TC_PCI) += tc-dwc-g210-pci.o ufshcd-dwc.o tc-dwc-g210.o
obj-$(CONFIG_SCSI_UFS_DWC_TC_PLATFORM) += tc-dwc-g210-pltfrm.o ufshcd-dwc.o tc-dwc-g210.o
obj-$(CONFIG_SCSI_UFS_QCOM) += ufs-qcom.o
+obj-$(CONFIG_SCSI_UFS_EXYNOS) += ufs-exynos.o
obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o
obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o
obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o
diff --git a/drivers/scsi/ufs/ufs-exynos.c b/drivers/scsi/ufs/ufs-exynos.c
new file mode 100644
index 000000000000..98e5aeb80b06
--- /dev/null
+++ b/drivers/scsi/ufs/ufs-exynos.c
@@ -0,0 +1,962 @@
+/*
+ * UFS Host Controller driver for Exynos specific extensions
+ *
+ * Copyright (C) 2013-2014 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/clk.h>
+#include "ufshcd.h"
+#include "ufshcd-pltfrm.h"
+#include "ufs-exynos.h"
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/spinlock.h>
+
+/*
+ * Debugging information, SFR/attributes/misc
+ */
+static struct exynos_ufs *ufs_host_backup[1];
+static int ufs_host_index = 0;
+
+static struct exynos_ufs_sfr_log ufs_log_std_sfr[] = {
+ {"CAPABILITIES" , REG_CONTROLLER_CAPABILITIES, 0},
+ {"UFS VERSION" , REG_UFS_VERSION, 0},
+ {"PRODUCT ID" , REG_CONTROLLER_DEV_ID, 0},
+ {"MANUFACTURE ID" , REG_CONTROLLER_PROD_ID, 0},
+ {"INTERRUPT STATUS" , REG_INTERRUPT_STATUS, 0},
+ {"INTERRUPT ENABLE" , REG_INTERRUPT_ENABLE, 0},
+ {"CONTROLLER STATUS" , REG_CONTROLLER_STATUS, 0},
+ {"CONTROLLER ENABLE" , REG_CONTROLLER_ENABLE, 0},
+ {"UIC ERR PHY ADAPTER LAYER" , REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER, 0},
+ {"UIC ERR DATA LINK LAYER" , REG_UIC_ERROR_CODE_DATA_LINK_LAYER, 0},
+ {"UIC ERR NETWORK LATER" , REG_UIC_ERROR_CODE_NETWORK_LAYER, 0},
+ {"UIC ERR TRANSPORT LAYER" , REG_UIC_ERROR_CODE_TRANSPORT_LAYER, 0},
+ {"UIC ERR DME" , REG_UIC_ERROR_CODE_DME, 0},
+ {"UTP TRANSF REQ INT AGG CNTRL" , REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL, 0},
+ {"UTP TRANSF REQ LIST BASE L" , REG_UTP_TRANSFER_REQ_LIST_BASE_L, 0},
+ {"UTP TRANSF REQ LIST BASE H" , REG_UTP_TRANSFER_REQ_LIST_BASE_H, 0},
+ {"UTP TRANSF REQ DOOR BELL" , REG_UTP_TRANSFER_REQ_DOOR_BELL, 0},
+ {"UTP TRANSF REQ LIST CLEAR" , REG_UTP_TRANSFER_REQ_LIST_CLEAR, 0},
+ {"UTP TRANSF REQ LIST RUN STOP" , REG_UTP_TRANSFER_REQ_LIST_RUN_STOP, 0},
+ {"UTP TASK REQ LIST BASE L" , REG_UTP_TASK_REQ_LIST_BASE_L, 0},
+ {"UTP TASK REQ LIST BASE H" , REG_UTP_TASK_REQ_LIST_BASE_H, 0},
+ {"UTP TASK REQ DOOR BELL" , REG_UTP_TASK_REQ_DOOR_BELL, 0},
+ {"UTP TASK REQ LIST CLEAR" , REG_UTP_TASK_REQ_LIST_CLEAR, 0},
+ {"UTP TASK REQ LIST RUN STOP" , REG_UTP_TASK_REQ_LIST_RUN_STOP, 0},
+ {"UIC COMMAND" , REG_UIC_COMMAND, 0},
+ {"UIC COMMAND ARG1" , REG_UIC_COMMAND_ARG_1, 0},
+ {"UIC COMMAND ARG2" , REG_UIC_COMMAND_ARG_2, 0},
+ {"UIC COMMAND ARG3" , REG_UIC_COMMAND_ARG_3, 0},
+
+ {},
+};
+
+/* Helper for UFS CAL interface */
+static inline int ufs_init_cal(struct exynos_ufs *ufs, int idx,
+ struct platform_device *pdev)
+{
+ return 0;
+}
+
+static inline int ufs_pre_link(struct exynos_ufs *ufs)
+{
+ return 0;
+}
+
+static inline int ufs_post_link(struct exynos_ufs *ufs)
+{
+ return 0;
+}
+
+static inline int ufs_pre_gear_change(struct exynos_ufs *ufs,
+ struct uic_pwr_mode *pmd)
+{
+ return 0;
+}
+
+static inline int ufs_post_gear_change(struct exynos_ufs *ufs)
+{
+ return 0;
+}
+
+static inline int ufs_post_h8_enter(struct exynos_ufs *ufs)
+{
+ return 0;
+}
+
+static inline int ufs_pre_h8_exit(struct exynos_ufs *ufs)
+{
+ return 0;
+}
+
+static inline void exynos_ufs_ctrl_phy_pwr(struct exynos_ufs *ufs, bool en)
+{
+ int ret = 0;
+
+ if (en)
+ ret = regmap_update_bits(ufs->pmureg, ufs->cxt_iso.offset,
+ ufs->cxt_iso.mask, ufs->cxt_iso.val);
+ else
+ ret = regmap_update_bits(ufs->pmureg, ufs->cxt_iso.offset,
+ ufs->cxt_iso.mask, 0);
+
+ if (ret)
+ dev_err(ufs->dev, "Unable to update PHY ISO control\n");
+}
+
+#ifndef __EXYNOS_UFS_VS_DEBUG__
+static void exynos_ufs_dump_std_sfr(struct ufs_hba *hba)
+{
+ struct exynos_ufs *ufs = to_exynos_ufs(hba);
+ struct exynos_ufs_sfr_log* cfg = ufs->debug.std_sfr;
+
+ dev_err(hba->dev, ": --------------------------------------------------- \n");
+ dev_err(hba->dev, ": \t\tREGISTER DUMP\n");
+ dev_err(hba->dev, ": --------------------------------------------------- \n");
+
+ while(cfg) {
+ if (!cfg->name)
+ break;
+ cfg->val = ufshcd_readl(hba, cfg->offset);
+
+ /* Dump */
+ dev_err(hba->dev, ": %s(0x%04x):\t\t\t\t0x%08x\n",
+ cfg->name, cfg->offset, cfg->val);
+
+ /* Next SFR */
+ cfg++;
+ }
+}
+#endif
+
+/*
+ * Exynos debugging main function
+ */
+static void exynos_ufs_dump_debug_info(struct ufs_hba *hba)
+{
+#ifdef __EXYNOS_UFS_VS_DEBUG__
+#else
+ exynos_ufs_dump_std_sfr(hba);
+#endif
+}
+
+static inline void exynos_ufs_set_hwacg_control(struct exynos_ufs *ufs, bool en)
+{
+ u32 reg;
+ if ((ufs->hw_rev != UFS_VER_0004) && (ufs->hw_rev != UFS_VER_0005))
+ return;
+
+ /*
+ * default value 1->0 at KC. so,
+ * need to set "1(disable HWACG)" during UFS init
+ */
+ reg = hci_readl(ufs, HCI_UFS_ACG_DISABLE);
+ if (en)
+ hci_writel(ufs, reg & (~HCI_UFS_ACG_DISABLE_EN), HCI_UFS_ACG_DISABLE);
+ else
+ hci_writel(ufs, reg | HCI_UFS_ACG_DISABLE_EN, HCI_UFS_ACG_DISABLE);
+
+}
+
+static inline void exynos_ufs_ctrl_auto_hci_clk(struct exynos_ufs *ufs, bool en)
+{
+ u32 reg = hci_readl(ufs, HCI_FORCE_HCS);
+
+ if (en)
+ hci_writel(ufs, reg | HCI_CORECLK_STOP_EN, HCI_FORCE_HCS);
+ else
+ hci_writel(ufs, reg & ~HCI_CORECLK_STOP_EN, HCI_FORCE_HCS);
+}
+
+static inline void exynos_ufs_ctrl_clk(struct exynos_ufs *ufs, bool en)
+{
+ u32 reg = hci_readl(ufs, HCI_FORCE_HCS);
+
+ if (en)
+ hci_writel(ufs, reg | CLK_STOP_CTRL_EN_ALL, HCI_FORCE_HCS);
+ else
+ hci_writel(ufs, reg & ~CLK_STOP_CTRL_EN_ALL, HCI_FORCE_HCS);
+}
+
+static inline void exynos_ufs_gate_clk(struct exynos_ufs *ufs, bool en)
+{
+
+ u32 reg = hci_readl(ufs, HCI_CLKSTOP_CTRL);
+
+ if (en)
+ hci_writel(ufs, reg | CLK_STOP_ALL, HCI_CLKSTOP_CTRL);
+ else
+ hci_writel(ufs, reg & ~CLK_STOP_ALL, HCI_CLKSTOP_CTRL);
+}
+
+static inline void exynos_ufs_set_unipro_mclk(struct exynos_ufs *ufs)
+{
+ ufs->mclk_rate = clk_get_rate(ufs->clk_unipro);
+}
+
+static inline void exynos_ufs_fit_aggr_timeout(struct exynos_ufs *ufs)
+{
+ u32 cnt_val;
+ u32 nVal;
+
+ /* IA_TICK_SEL : 1(1us_TO_CNT_VAL) */
+ nVal = hci_readl(ufs, HCI_UFSHCI_V2P1_CTRL);
+ nVal |= IA_TICK_SEL;
+ hci_writel(ufs, nVal, HCI_UFSHCI_V2P1_CTRL);
+
+ cnt_val = ufs->mclk_rate / 1000000 ;
+ hci_writel(ufs, cnt_val & CNT_VAL_1US_MASK, HCI_1US_TO_CNT_VAL);
+}
+
+static void exynos_ufs_init_pmc_req(struct ufs_hba *hba,
+ struct ufs_pa_layer_attr *pwr_max,
+ struct ufs_pa_layer_attr *pwr_req)
+{
+
+ struct exynos_ufs *ufs = to_exynos_ufs(hba);
+ struct uic_pwr_mode *req_pmd = &ufs->req_pmd_parm;
+ struct uic_pwr_mode *act_pmd = &ufs->act_pmd_parm;
+
+ /* update lane variable after link */
+ ufs->num_rx_lanes = pwr_max->lane_rx;
+ ufs->num_tx_lanes = pwr_max->lane_tx;
+
+ pwr_req->gear_rx
+ = act_pmd->gear= min_t(u8, pwr_max->gear_rx, req_pmd->gear);
+ pwr_req->gear_tx
+ = act_pmd->gear = min_t(u8, pwr_max->gear_tx, req_pmd->gear);
+ pwr_req->lane_rx
+ = act_pmd->lane = min_t(u8, pwr_max->lane_rx, req_pmd->lane);
+ pwr_req->lane_tx
+ = act_pmd->lane = min_t(u8, pwr_max->lane_tx, req_pmd->lane);
+ pwr_req->pwr_rx = act_pmd->mode = req_pmd->mode;
+ pwr_req->pwr_tx = act_pmd->mode = req_pmd->mode;
+ pwr_req->hs_rate = act_pmd->hs_series = req_pmd->hs_series;
+}
+
+static void exynos_ufs_config_intr(struct exynos_ufs *ufs, u32 errs, u8 index)
+{
+ switch(index) {
+ case UNIP_PA_LYR:
+ hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERROR_EN_PA_LAYER);
+ break;
+ case UNIP_DL_LYR:
+ hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERROR_EN_DL_LAYER);
+ break;
+ case UNIP_N_LYR:
+ hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERROR_EN_N_LAYER);
+ break;
+ case UNIP_T_LYR:
+ hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERROR_EN_T_LAYER);
+ break;
+ case UNIP_DME_LYR:
+ hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERROR_EN_DME_LAYER);
+ break;
+ }
+}
+
+static void exynos_ufs_dev_hw_reset(struct ufs_hba *hba)
+{
+ struct exynos_ufs *ufs = to_exynos_ufs(hba);
+
+ /* bit[1] for resetn */
+ hci_writel(ufs, 0 << 0, HCI_GPIO_OUT);
+ udelay(5);
+ hci_writel(ufs, 1 << 0, HCI_GPIO_OUT);
+}
+
+static void exynos_ufs_init_host(struct exynos_ufs *ufs)
+{
+ u32 reg;
+
+ /* internal clock control */
+ exynos_ufs_ctrl_auto_hci_clk(ufs, false);
+ exynos_ufs_set_unipro_mclk(ufs);
+
+ /* period for interrupt aggregation */
+ exynos_ufs_fit_aggr_timeout(ufs);
+
+ /* misc HCI configurations */
+ hci_writel(ufs, 0xA, HCI_DATA_REORDER);
+ hci_writel(ufs, PRDT_PREFECT_EN | PRDT_SET_SIZE(12),
+ HCI_TXPRDT_ENTRY_SIZE);
+ hci_writel(ufs, PRDT_SET_SIZE(12), HCI_RXPRDT_ENTRY_SIZE);
+ hci_writel(ufs, 0xFFFFFFFF, HCI_UTRL_NEXUS_TYPE);
+ hci_writel(ufs, 0xFFFFFFFF, HCI_UTMRL_NEXUS_TYPE);
+
+ reg = hci_readl(ufs, HCI_AXIDMA_RWDATA_BURST_LEN) &
+ ~BURST_LEN(0);
+ hci_writel(ufs, WLU_EN | BURST_LEN(3),
+ HCI_AXIDMA_RWDATA_BURST_LEN);
+
+ /*
+ * Enable HWAGC control by IOP
+ *
+ * default value 1->0 at KC.
+ * always "0"(controlled by UFS_ACG_DISABLE)
+ */
+ reg = hci_readl(ufs, HCI_IOP_ACG_DISABLE);
+ hci_writel(ufs, reg & (~HCI_IOP_ACG_DISABLE_EN), HCI_IOP_ACG_DISABLE);
+}
+
+static int exynos_ufs_init_system(struct exynos_ufs *ufs)
+{
+ struct device *dev = ufs->dev;
+ int ret = 0;
+ bool is_io_coherency;
+ bool is_dma_coherent;
+
+ /* PHY isolation bypass */
+ exynos_ufs_ctrl_phy_pwr(ufs, true);
+
+ /* IO cohernecy */
+ is_io_coherency = !IS_ERR(ufs->sysreg);
+ is_dma_coherent = !!of_find_property(dev->of_node,
+ "dma-coherent", NULL);
+
+ if (is_io_coherency != is_dma_coherent)
+ BUG();
+
+ if (!is_io_coherency)
+ dev_err(dev, "Not configured to use IO coherency\n");
+ else
+ ret = regmap_update_bits(ufs->sysreg, ufs->cxt_coherency.offset,
+ ufs->cxt_coherency.mask, ufs->cxt_coherency.val);
+
+ return ret;
+}
+
+static int exynos_ufs_get_clks(struct ufs_hba *hba)
+{
+ struct exynos_ufs *ufs = to_exynos_ufs(hba);
+ struct list_head *head = &hba->clk_list_head;
+ struct ufs_clk_info *clki;
+ int i = 0;
+
+ ufs_host_backup[ufs_host_index++] = ufs;
+ ufs->debug.std_sfr = ufs_log_std_sfr;
+
+ if (!head || list_empty(head))
+ goto out;
+
+ list_for_each_entry(clki, head, list) {
+ /*
+ * get clock with an order listed in device tree
+ */
+ if (i == 0)
+ ufs->clk_hci = clki->clk;
+ else if (i == 1)
+ ufs->clk_unipro = clki->clk;
+
+ i++;
+ }
+out:
+ if (!ufs->clk_hci || !ufs->clk_unipro)
+ return -EINVAL;
+
+ return 0;
+}
+
+static void exynos_ufs_set_features(struct ufs_hba *hba, u32 hw_rev)
+{
+ /* caps */
+ hba->caps = UFSHCD_CAP_CLK_GATING |
+ UFSHCD_CAP_HIBERN8_WITH_CLK_GATING |
+ UFSHCD_CAP_INTR_AGGR;
+
+ /* quirks of common driver */
+ hba->quirks = UFSHCD_QUIRK_PRDT_BYTE_GRAN;
+
+ /* quirks of exynos-specific driver */
+}
+
+/*
+ * Exynos-specific callback functions
+ *
+ * init | Pure SW init & system-related init
+ * host_reset | Host SW reset & init
+ * ...
+ *
+ * Initializations for software, host controller and system
+ * should be contained only in ->host_reset() as possible.
+ */
+
+static int exynos_ufs_init(struct ufs_hba *hba)
+{
+ struct exynos_ufs *ufs = to_exynos_ufs(hba);
+ int ret;
+
+ /* set features, such as caps or quirks */
+ exynos_ufs_set_features(hba, ufs->hw_rev);
+
+ /* get some clock sources and debug infomation structures */
+ ret = exynos_ufs_get_clks(hba);
+ if (ret)
+ return ret;
+
+ /* system init */
+ ret = exynos_ufs_init_system(ufs);
+ if (ret)
+ return ret;
+
+ ufs->misc_flags = EXYNOS_UFS_MISC_TOGGLE_LOG;
+
+ return 0;
+}
+
+static void exynos_ufs_host_reset(struct ufs_hba *hba)
+{
+ struct exynos_ufs *ufs = to_exynos_ufs(hba);
+ unsigned long timeout = jiffies + msecs_to_jiffies(1);
+
+ exynos_ufs_ctrl_auto_hci_clk(ufs, false);
+
+ hci_writel(ufs, UFS_SW_RST_MASK, HCI_SW_RST);
+
+ do {
+ if (!(hci_readl(ufs, HCI_SW_RST) & UFS_SW_RST_MASK))
+ goto success;
+ } while (time_before(jiffies, timeout));
+
+ dev_err(ufs->dev, "timeout host sw-reset\n");
+
+ goto out;
+
+success:
+ /* host init */
+ exynos_ufs_init_host(ufs);
+
+ /* device reset */
+ exynos_ufs_dev_hw_reset(hba);
+out:
+ return;
+}
+
+static inline void exynos_ufs_dev_reset_ctrl(struct exynos_ufs *ufs, bool en)
+{
+
+ if (en)
+ hci_writel(ufs, 1 << 0, HCI_GPIO_OUT);
+ else
+ hci_writel(ufs, 0 << 0, HCI_GPIO_OUT);
+}
+
+static int exynos_ufs_setup_clocks(struct ufs_hba *hba, bool on,
+ enum ufs_notify_change_status status)
+{
+ struct exynos_ufs *ufs = to_exynos_ufs(hba);
+ int ret = 0;
+
+ if (status == PRE_CHANGE) {
+ if (on) {
+ /*
+ * Now all used blocks would not be turned off in a host.
+ */
+ exynos_ufs_ctrl_auto_hci_clk(ufs, false);
+ exynos_ufs_gate_clk(ufs, false);
+
+ /* HWAGC disable */
+ exynos_ufs_set_hwacg_control(ufs, false);
+ } else {
+ ret = ufs_post_h8_enter(ufs);
+ }
+ } else {
+ if (on) {
+ ret = ufs_pre_h8_exit(ufs);
+ } else {
+ /*
+ * Now all used blocks would be turned off in a host.
+ */
+ exynos_ufs_ctrl_auto_hci_clk(ufs, true);
+
+ /* HWAGC enable */
+ exynos_ufs_set_hwacg_control(ufs, true);
+ }
+ }
+
+ return ret;
+}
+
+static int exynos_ufs_link_startup_notify(struct ufs_hba *hba,
+ enum ufs_notify_change_status status)
+{
+ struct exynos_ufs *ufs = to_exynos_ufs(hba);
+ int ret = 0;
+
+ switch (status) {
+ case PRE_CHANGE:
+ /* refer to hba */
+ ufs->hba = hba;
+
+ /* hci */
+ exynos_ufs_config_intr(ufs, DFES_DEF_DL_ERRS, UNIP_DL_LYR);
+ exynos_ufs_config_intr(ufs, DFES_DEF_N_ERRS, UNIP_N_LYR);
+ exynos_ufs_config_intr(ufs, DFES_DEF_T_ERRS, UNIP_T_LYR);
+
+ exynos_ufs_ctrl_clk(ufs, true);
+ exynos_ufs_gate_clk(ufs, false);
+ exynos_ufs_set_hwacg_control(ufs, false);
+
+ if (ufs->num_rx_lanes == 0 || ufs->num_tx_lanes == 0) {
+ ufshcd_dme_get(hba, UIC_ARG_MIB(PA_AVAILRXDATALANES),
+ &ufs->num_rx_lanes);
+ ufshcd_dme_get(hba, UIC_ARG_MIB(PA_AVAILTXDATALANES),
+ &ufs->num_tx_lanes);
+ WARN(ufs->num_rx_lanes != ufs->num_tx_lanes,
+ "available data lane is not equal(rx:%d, tx:%d)\n",
+ ufs->num_rx_lanes, ufs->num_tx_lanes);
+ }
+
+ ufs->mclk_rate = clk_get_rate(ufs->clk_unipro);
+
+ ret = ufs_pre_link(ufs);
+ break;
+ case POST_CHANGE:
+ /* UIC configuration table after link startup */
+ ret = ufs_post_link(ufs);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int exynos_ufs_pwr_change_notify(struct ufs_hba *hba,
+ enum ufs_notify_change_status status,
+ struct ufs_pa_layer_attr *pwr_max,
+ struct ufs_pa_layer_attr *pwr_req)
+{
+ struct exynos_ufs *ufs = to_exynos_ufs(hba);
+ struct uic_pwr_mode *act_pmd = &ufs->act_pmd_parm;
+ int ret = 0;
+
+ switch (status) {
+ case PRE_CHANGE:
+
+ /* Set PMC parameters to be requested */
+ exynos_ufs_init_pmc_req(hba, pwr_max, pwr_req);
+
+ /* UIC configuration table before power mode change */
+ ret = ufs_pre_gear_change(ufs, act_pmd);
+
+ break;
+ case POST_CHANGE:
+ /* UIC configuration table after power mode change */
+ ret = ufs_post_gear_change(ufs);
+
+ dev_info(ufs->dev,
+ "Power mode change(%d): M(%d)G(%d)L(%d)HS-series(%d)\n",
+ ret, act_pmd->mode, act_pmd->gear,
+ act_pmd->lane, act_pmd->hs_series);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static void exynos_ufs_set_nexus_t_xfer_req(struct ufs_hba *hba,
+ int tag, bool op)
+{
+ struct exynos_ufs *ufs = to_exynos_ufs(hba);
+ u32 type;
+
+ type = hci_readl(ufs, HCI_UTRL_NEXUS_TYPE);
+
+ if (op)
+ type |= (1 << tag);
+ else
+ type &= ~(1 << tag);
+
+ hci_writel(ufs, type, HCI_UTRL_NEXUS_TYPE);
+}
+
+static void exynos_ufs_set_nexus_t_task_mgmt(struct ufs_hba *hba, int tag, u8 tm_func)
+{
+ struct exynos_ufs *ufs = to_exynos_ufs(hba);
+ u32 type;
+
+ type = hci_readl(ufs, HCI_UTMRL_NEXUS_TYPE);
+
+ switch (tm_func) {
+ case UFS_ABORT_TASK:
+ case UFS_QUERY_TASK:
+ type |= (1 << tag);
+ break;
+ case UFS_ABORT_TASK_SET:
+ case UFS_CLEAR_TASK_SET:
+ case UFS_LOGICAL_RESET:
+ case UFS_QUERY_TASK_SET:
+ type &= ~(1 << tag);
+ break;
+ }
+
+ hci_writel(ufs, type, HCI_UTMRL_NEXUS_TYPE);
+}
+
+static int __exynos_ufs_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
+{
+ struct exynos_ufs *ufs = to_exynos_ufs(hba);
+
+ exynos_ufs_dev_reset_ctrl(ufs, false);
+
+ exynos_ufs_ctrl_phy_pwr(ufs, false);
+
+ return 0;
+}
+
+static int __exynos_ufs_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
+{
+ struct exynos_ufs *ufs = to_exynos_ufs(hba);
+ int ret = 0;
+
+ exynos_ufs_ctrl_phy_pwr(ufs, true);
+
+ /* system init */
+ ret = exynos_ufs_init_system(ufs);
+ if (ret)
+ return ret;
+
+ if (ufshcd_is_clkgating_allowed(hba))
+ clk_prepare_enable(ufs->clk_hci);
+ exynos_ufs_ctrl_auto_hci_clk(ufs, false);
+
+ if (ufshcd_is_clkgating_allowed(hba))
+ clk_disable_unprepare(ufs->clk_hci);
+
+ return 0;
+}
+
+static struct ufs_hba_variant_ops exynos_ufs_ops = {
+ .init = exynos_ufs_init,
+ .host_reset = exynos_ufs_host_reset,
+ .setup_clocks = exynos_ufs_setup_clocks,
+ .link_startup_notify = exynos_ufs_link_startup_notify,
+ .pwr_change_notify = exynos_ufs_pwr_change_notify,
+ .setup_xfer_req = exynos_ufs_set_nexus_t_xfer_req,
+ .setup_task_mgmt = exynos_ufs_set_nexus_t_task_mgmt,
+ .dbg_register_dump = exynos_ufs_dump_debug_info,
+ .suspend = __exynos_ufs_suspend,
+ .resume = __exynos_ufs_resume,
+};
+
+static int exynos_ufs_populate_dt_phy(struct device *dev, struct exynos_ufs *ufs)
+{
+ struct device_node *ufs_phy;
+ struct exynos_ufs_phy *phy = &ufs->phy;
+ struct resource io_res;
+ int ret;
+
+ ufs_phy = of_get_child_by_name(dev->of_node, "ufs-phy");
+ if (!ufs_phy) {
+ dev_err(dev, "failed to get ufs-phy node\n");
+ return -ENODEV;
+ }
+
+ ret = of_address_to_resource(ufs_phy, 0, &io_res);
+ if (ret) {
+ dev_err(dev, "failed to get i/o address phy pma\n");
+ goto err_0;
+ }
+
+ phy->reg_pma = devm_ioremap_resource(dev, &io_res);
+ if (!phy->reg_pma) {
+ dev_err(dev, "failed to ioremap for phy pma\n");
+ ret = -ENOMEM;
+ goto err_0;
+ }
+
+err_0:
+ of_node_put(ufs_phy);
+
+ return ret;
+}
+
+/*
+ * This function is to define offset, mask and shift to access somewhere.
+ */
+static int exynos_ufs_set_context_for_access(struct device *dev,
+ const char *name, struct exynos_access_cxt *cxt)
+{
+ struct device_node *np;
+ int ret;
+
+ np = of_get_child_by_name(dev->of_node, name);
+ if (!np) {
+ dev_err(dev, "failed to get node(%s)\n", name);
+ return 1;
+ }
+
+ ret = of_property_read_u32(np, "offset", &cxt->offset);
+ if (IS_ERR(&cxt->offset)) {
+ dev_err(dev, "failed to set cxt(%s) offset\n", name);
+ return cxt->offset;
+ }
+
+ ret = of_property_read_u32(np, "mask", &cxt->mask);
+ if (IS_ERR(&cxt->mask)) {
+ dev_err(dev, "failed to set cxt(%s) mask\n", name);
+ return cxt->mask;
+ }
+
+ ret = of_property_read_u32(np, "val", &cxt->val);
+ if (IS_ERR(&cxt->val)) {
+ dev_err(dev, "failed to set cxt(%s) val\n", name);
+ return cxt->val;
+ }
+
+ return 0;
+}
+
+static int exynos_ufs_populate_dt_system(struct device *dev, struct exynos_ufs *ufs)
+{
+ int ret;
+
+ /* regmap pmureg */
+ ufs->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "samsung,pmu-phandle");
+ if (IS_ERR(ufs->pmureg)) {
+ /*
+ * phy isolation should be available.
+ * so this case need to be failed.
+ */
+ dev_err(dev, "pmu regmap lookup failed.\n");
+ return PTR_ERR(ufs->pmureg);
+ }
+
+ /* Set access context for phy isolation bypass */
+ ret = exynos_ufs_set_context_for_access(dev, "ufs-phy-iso",
+ &ufs->cxt_iso);
+ if (ret == 1) {
+ /* no device node, default */
+ ufs->cxt_iso.offset = 0x0724;
+ ufs->cxt_iso.mask = 0x1;
+ ufs->cxt_iso.val = 0x1;
+ ret = 0;
+ }
+
+ /* regmap sysreg */
+ ufs->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "samsung,sysreg-fsys-phandle");
+ if (IS_ERR(ufs->sysreg)) {
+ /*
+ * Currently, ufs driver gets sysreg for io coherency.
+ * Some architecture might not support this feature.
+ * So the device node might not exist.
+ */
+ dev_err(dev, "sysreg regmap lookup failed.\n");
+ return 0;
+ }
+
+ /* Set access context for io coherency */
+ ret = exynos_ufs_set_context_for_access(dev, "ufs-dma-coherency",
+ &ufs->cxt_coherency);
+ if (ret == 1) {
+ /* no device node, default */
+ ufs->cxt_coherency.offset = 0x0700;
+ ufs->cxt_coherency.mask = 0x300; /* bit 8,9 */
+ ufs->cxt_coherency.val = 0x3;
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int exynos_ufs_get_pwr_mode(struct device_node *np,
+ struct exynos_ufs *ufs)
+{
+ struct uic_pwr_mode *pmd = &ufs->req_pmd_parm;
+
+ pmd->mode = FAST_MODE;
+
+ if (of_property_read_u8(np, "ufs,pmd-attr-lane", &pmd->lane))
+ pmd->lane = 1;
+
+ if (of_property_read_u8(np, "ufs,pmd-attr-gear", &pmd->gear))
+ pmd->gear = 1;
+
+ pmd->hs_series = PA_HS_MODE_B;
+
+ return 0;
+}
+
+static int exynos_ufs_populate_dt(struct device *dev, struct exynos_ufs *ufs)
+{
+ struct device_node *np = dev->of_node;
+ int ret;
+
+ /* Get exynos-specific version for featuring */
+ if (of_property_read_u32(np, "hw-rev", &ufs->hw_rev))
+ ufs->hw_rev = UFS_VER_0004;
+
+ ret = exynos_ufs_populate_dt_phy(dev, ufs);
+ if (ret) {
+ dev_err(dev, "failed to populate dt-phy\n");
+ goto out;
+ }
+
+ ret = exynos_ufs_populate_dt_system(dev, ufs);
+ if (ret) {
+ dev_err(dev, "failed to populate dt-pmu\n");
+ goto out;
+ }
+
+ exynos_ufs_get_pwr_mode(np, ufs);
+
+out:
+ return ret;
+}
+
+static u64 exynos_ufs_dma_mask = DMA_BIT_MASK(32);
+
+static int exynos_ufs_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct exynos_ufs *ufs;
+ struct resource *res;
+ int ret;
+
+ ufs = devm_kzalloc(dev, sizeof(*ufs), GFP_KERNEL);
+ if (!ufs) {
+ dev_err(dev, "cannot allocate mem for exynos-ufs\n");
+ return -ENOMEM;
+ }
+
+ /* exynos-specific hci */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ ufs->reg_hci = devm_ioremap_resource(dev, res);
+ if (!ufs->reg_hci) {
+ dev_err(dev, "cannot ioremap for hci vendor register\n");
+ return -ENOMEM;
+ }
+
+ /* unipro */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ ufs->reg_unipro = devm_ioremap_resource(dev, res);
+ if (!ufs->reg_unipro) {
+ dev_err(dev, "cannot ioremap for unipro register\n");
+ return -ENOMEM;
+ }
+
+ /* This must be before calling exynos_ufs_populate_dt */
+ ret = ufs_init_cal(ufs, ufs_host_index, pdev);
+ if (ret)
+ return ret;
+
+ ret = exynos_ufs_populate_dt(dev, ufs);
+ if (ret) {
+ dev_err(dev, "failed to get dt info.\n");
+ return ret;
+ }
+
+ ufs->dev = dev;
+ dev->platform_data = ufs;
+ dev->dma_mask = &exynos_ufs_dma_mask;
+
+ ret = ufshcd_pltfrm_init(pdev, &exynos_ufs_ops);
+
+ return ret;
+}
+
+static int exynos_ufs_remove(struct platform_device *pdev)
+{
+ struct ufs_hba *hba = platform_get_drvdata(pdev);
+ struct exynos_ufs *ufs = dev_get_platdata(&pdev->dev);
+
+ ufshcd_remove(hba);
+
+ ufs->misc_flags = EXYNOS_UFS_MISC_TOGGLE_LOG;
+
+ exynos_ufs_ctrl_phy_pwr(ufs, false);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int exynos_ufs_suspend(struct device *dev)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+
+ return ufshcd_system_suspend(hba);
+}
+
+static int exynos_ufs_resume(struct device *dev)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+
+ return ufshcd_system_resume(hba);
+}
+#else
+#define exynos_ufs_suspend NULL
+#define exynos_ufs_resume NULL
+#endif /* CONFIG_PM_SLEEP */
+
+#ifdef CONFIG_PM_RUNTIME
+static int exynos_ufs_runtime_suspend(struct device *dev)
+{
+ return ufshcd_system_suspend(dev_get_drvdata(dev));
+}
+
+static int exynos_ufs_runtime_resume(struct device *dev)
+{
+ return ufshcd_system_resume(dev_get_drvdata(dev));
+}
+
+static int exynos_ufs_runtime_idle(struct device *dev)
+{
+ return ufshcd_runtime_idle(dev_get_drvdata(dev));
+}
+
+#else
+#define exynos_ufs_runtime_suspend NULL
+#define exynos_ufs_runtime_resume NULL
+#define exynos_ufs_runtime_idle NULL
+#endif /* CONFIG_PM_RUNTIME */
+
+static void exynos_ufs_shutdown(struct platform_device *pdev)
+{
+ ufshcd_shutdown((struct ufs_hba *)platform_get_drvdata(pdev));
+}
+
+static const struct dev_pm_ops exynos_ufs_dev_pm_ops = {
+ .suspend = exynos_ufs_suspend,
+ .resume = exynos_ufs_resume,
+ .runtime_suspend = exynos_ufs_runtime_suspend,
+ .runtime_resume = exynos_ufs_runtime_resume,
+ .runtime_idle = exynos_ufs_runtime_idle,
+};
+
+static const struct of_device_id exynos_ufs_match[] = {
+ { .compatible = "samsung,exynos-ufs", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, exynos_ufs_match);
+
+static struct platform_driver exynos_ufs_driver = {
+ .driver = {
+ .name = "exynos-ufs",
+ .owner = THIS_MODULE,
+ .pm = &exynos_ufs_dev_pm_ops,
+ .of_match_table = exynos_ufs_match,
+ .suppress_bind_attrs = true,
+ },
+ .probe = exynos_ufs_probe,
+ .remove = exynos_ufs_remove,
+ .shutdown = exynos_ufs_shutdown,
+};
+
+module_platform_driver(exynos_ufs_driver);
+MODULE_DESCRIPTION("Exynos Specific UFSHCI driver");
+MODULE_AUTHOR("Seungwon Jeon <tgih.jun@samsung.com>");
+MODULE_AUTHOR("Kiwoong Kim <kwmad.kim@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/ufs/ufs-exynos.h b/drivers/scsi/ufs/ufs-exynos.h
new file mode 100644
index 000000000000..0480fc4a8931
--- /dev/null
+++ b/drivers/scsi/ufs/ufs-exynos.h
@@ -0,0 +1,351 @@
+/*
+ * UFS Host Controller driver for Exynos specific extensions
+ *
+ * Copyright (C) 2013-2014 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _UFS_EXYNOS_H_
+#define _UFS_EXYNOS_H_
+
+#define UFS_VER_0004 4
+#define UFS_VER_0005 5
+
+/*
+ * Exynos's Vendor specific registers for UFSHCI
+ */
+#define HCI_TXPRDT_ENTRY_SIZE 0x00
+#define HCI_RXPRDT_ENTRY_SIZE 0x04
+#define HCI_TO_CNT_DIV_VAL 0x08
+#define HCI_1US_TO_CNT_VAL 0x0C
+ #define CNT_VAL_1US_MASK 0x3ff
+#define HCI_INVALID_UPIU_CTRL 0x10
+#define HCI_INVALID_UPIU_BADDR 0x14
+#define HCI_INVALID_UPIU_UBADDR 0x18
+#define HCI_INVALID_UTMR_OFFSET_ADDR 0x1C
+#define HCI_INVALID_UTR_OFFSET_ADDR 0x20
+#define HCI_INVALID_DIN_OFFSET_ADDR 0x24
+#define HCI_VENDOR_SPECIFIC_IS 0x38
+#define HCI_VENDOR_SPECIFIC_IE 0x3C
+#define HCI_UTRL_NEXUS_TYPE 0x40
+#define HCI_UTMRL_NEXUS_TYPE 0x44
+#define HCI_E2EFC_CTRL 0x48
+#define HCI_SW_RST 0x50
+ #define UFS_LINK_SW_RST (1 << 0)
+ #define UFS_UNIPRO_SW_RST (1 << 1)
+ #define UFS_SW_RST_MASK (UFS_UNIPRO_SW_RST | UFS_LINK_SW_RST)
+#define HCI_LINK_VERSION 0x54
+#define HCI_IDLE_TIMER_CONFIG 0x58
+#define HCI_RX_UPIU_MATCH_ERROR_CODE 0x5C
+#define HCI_DATA_REORDER 0x60
+#define HCI_MAX_DOUT_DATA_SIZE 0x64
+#define HCI_UNIPRO_APB_CLK_CTRL 0x68
+#define HCI_AXIDMA_RWDATA_BURST_LEN 0x6C
+ #define BURST_LEN(x) ((x) << 27 | (x))
+ #define WLU_EN (1 << 31)
+ #define AXIDMA_RWDATA_BURST_LEN (0xF)
+#define HCI_GPIO_OUT 0x70
+#define HCI_WRITE_DMA_CTRL 0x74
+#define HCI_ERROR_EN_PA_LAYER 0x78
+#define HCI_ERROR_EN_DL_LAYER 0x7C
+#define HCI_ERROR_EN_N_LAYER 0x80
+#define HCI_ERROR_EN_T_LAYER 0x84
+#define HCI_ERROR_EN_DME_LAYER 0x88
+#define HCI_UFSHCI_V2P1_CTRL 0X8C
+#define IA_TICK_SEL BIT(16)
+#define HCI_REQ_HOLD_EN 0xAC
+
+#define HCI_CLKSTOP_CTRL 0xB0
+ #define REFCLKOUT_STOP BIT(4)
+ #define MPHY_APBCLK_STOP BIT(3)
+ #define REFCLK_STOP BIT(2)
+ #define UNIPRO_MCLK_STOP BIT(1)
+ #define UNIPRO_PCLK_STOP BIT(0)
+ #define CLK_STOP_ALL (REFCLKOUT_STOP |\
+ REFCLK_STOP |\
+ UNIPRO_MCLK_STOP |\
+ UNIPRO_PCLK_STOP)
+
+#define HCI_FORCE_HCS 0xB4
+ #define REFCLKOUT_STOP_EN BIT(11)
+ #define MPHY_APBCLK_STOP_EN BIT(10)
+ #define UFSP_DRCG_EN BIT(8)
+ #define REFCLK_STOP_EN BIT(7)
+ #define UNIPRO_PCLK_STOP_EN BIT(6)
+ #define UNIPRO_MCLK_STOP_EN BIT(5)
+ #define HCI_CORECLK_STOP_EN BIT(4)
+ #define CLK_STOP_CTRL_EN_ALL (UFSP_DRCG_EN |\
+ MPHY_APBCLK_STOP_EN |\
+ REFCLKOUT_STOP_EN |\
+ REFCLK_STOP_EN |\
+ UNIPRO_PCLK_STOP_EN |\
+ UNIPRO_MCLK_STOP_EN)
+
+#define HCI_FSM_MONITOR 0xC0
+#define HCI_PRDT_HIT_RATIO 0xC4
+#define HCI_DMA0_MONITOR_STATE 0xC8
+#define HCI_DMA0_MONITOR_CNT 0xCC
+#define HCI_DMA1_MONITOR_STATE 0xD0
+#define HCI_DMA1_MONITOR_CNT 0xD4
+
+#define HCI_UFS_AXI_DMA_IF_CTRL 0xF8
+#define HCI_UFS_ACG_DISABLE 0xFC
+ #define HCI_UFS_ACG_DISABLE_EN BIT(0)
+#define HCI_IOP_ACG_DISABLE 0x100
+ #define HCI_IOP_ACG_DISABLE_EN BIT(0)
+#define HCI_MPHY_REFCLK_SEL 0x108
+ #define MPHY_REFCLK_SEL BIT(0)
+
+/* Device fatal error */
+#define DFES_ERR_EN BIT(31)
+#define DFES_DEF_DL_ERRS (UIC_DATA_LINK_LAYER_ERROR_RX_BUF_OF |\
+ UIC_DATA_LINK_LAYER_ERROR_PA_INIT)
+#define DFES_DEF_N_ERRS (UIC_NETWORK_UNSUPPORTED_HEADER_TYPE |\
+ UIC_NETWORK_BAD_DEVICEID_ENC |\
+ UIC_NETWORK_LHDR_TRAP_PACKET_DROPPING)
+#define DFES_DEF_T_ERRS (UIC_TRANSPORT_UNSUPPORTED_HEADER_TYPE |\
+ UIC_TRANSPORT_UNKNOWN_CPORTID |\
+ UIC_TRANSPORT_NO_CONNECTION_RX |\
+ UIC_TRANSPORT_BAD_TC)
+
+/* TXPRDT defines */
+#define PRDT_PREFECT_EN BIT(31)
+#define PRDT_SET_SIZE(x) ((x) & 0x1F)
+
+enum {
+ UNIP_PA_LYR = 0,
+ UNIP_DL_LYR,
+ UNIP_N_LYR,
+ UNIP_T_LYR,
+ UNIP_DME_LYR,
+};
+
+/*
+ * UNIPRO registers
+ */
+#define UNIP_COMP_VERSION 0x000
+#define UNIP_COMP_INFO 0x004
+#define UNIP_COMP_RESET 0x010
+
+#define UNIP_DME_POWERON_REQ 0x7800
+#define UNIP_DME_POWERON_CNF_RESULT 0x7804
+#define UNIP_DME_POWEROFF_REQ 0x7810
+#define UNIP_DME_POWEROFF_CNF_RESULT 0x7814
+#define UNIP_DME_RESET_REQ 0x7820
+#define UNIP_DME_RESET_REQ_LEVEL 0x7824
+#define UNIP_DME_ENABLE_REQ 0x7830
+#define UNIP_DME_ENABLE_CNF_RESULT 0x7834
+#define UNIP_DME_ENDPOINTRESET_REQ 0x7840
+#define UNIP_DME_ENDPOINTRESET_CNF_RESULT 0x7844
+#define UNIP_DME_LINKSTARTUP_REQ 0x7850
+#define UNIP_DME_LINKSTARTUP_CNF_RESULT 0x7854
+#define UNIP_DME_HIBERN8_ENTER_REQ 0x7860
+#define UNIP_DME_HIBERN8_ENTER_CNF_RESULT 0x7864
+#define UNIP_DME_HIBERN8_ENTER_IND_RESULT 0x7868
+#define UNIP_DME_HIBERN8_EXIT_REQ 0x7870
+#define UNIP_DME_HIBERN8_EXIT_CNF_RESULT 0x7874
+#define UNIP_DME_HIBERN8_EXIT_IND_RESULT 0x7878
+#define UNIP_DME_PWR_REQ 0x7880
+#define UNIP_DME_PWR_REQ_POWERMODE 0x7884
+#define UNIP_DME_PWR_REQ_LOCALL2TIMER0 0x7888
+#define UNIP_DME_PWR_REQ_LOCALL2TIMER1 0x788C
+#define UNIP_DME_PWR_REQ_LOCALL2TIMER2 0x7890
+#define UNIP_DME_PWR_REQ_REMOTEL2TIMER0 0x78B8
+#define UNIP_DME_PWR_REQ_REMOTEL2TIMER1 0x78BC
+#define UNIP_DME_PWR_REQ_REMOTEL2TIMER2 0x78C0
+#define UNIP_DME_PWR_CNF_RESULT 0x78E8
+#define UNIP_DME_PWR_IND_RESULT 0x78EC
+#define UNIP_DME_TEST_MODE_REQ 0x7900
+#define UNIP_DME_TEST_MODE_CNF_RESULT 0x7904
+
+#define UNIP_DME_ERROR_IND_LAYER 0x0C0
+#define UNIP_DME_ERROR_IND_ERRCODE 0x0C4
+#define UNIP_DME_PACP_CNFBIT 0x0C8
+#define UNIP_DME_DL_FRAME_IND 0x0D0
+#define UNIP_DME_INTR_STATUS 0x0E0
+#define UNIP_DME_INTR_ENABLE 0x0E4
+
+#define UNIP_DME_GETSET_CONTROL 0x7A00
+#define UNIP_DME_GETSET_ADDR 0x7A04
+#define UNIP_DME_GETSET_WDATA 0x7A08
+#define UNIP_DME_GETSET_RDATA 0x7A0C
+#define UNIP_DME_GETSET_RESULT 0x7A10
+#define UNIP_DME_PEER_GETSET_CONTROL 0x7A20
+#define UNIP_DME_PEER_GETSET_ADDR 0x7A24
+#define UNIP_DME_PEER_GETSET_WDATA 0x7A28
+#define UNIP_DME_PEER_GETSET_RDATA 0x7A2C
+#define UNIP_DME_PEER_GETSET_RESULT 0x7A30
+
+#define UNIP_DME_INTR_STATUS_LSB 0x7B00
+#define UNIP_DME_INTR_STATUS_MSB 0x7B04
+#define UNIP_DME_INTR_ERROR_CODE 0x7B20
+#define UNIP_DME_DISCARD_PORT_ID 0x7B24
+#define UNIP_DME_DBG_OPTION_SUITE 0x7C00
+#define UNIP_DME_DBG_CTRL_FSM 0x7D00
+#define UNIP_DME_DBG_FLAG_STATUS 0x7D14
+#define UNIP_DME_DBG_LINKCFG_FSM 0x7D18
+
+#define UNIP_DME_INTR_ERROR_CODE 0x7B20
+#define UNIP_DME_DEEPSTALL_ENTER_REQ 0x7910
+#define UNIP_DME_DISCARD_CPORT_ID 0x7B24
+
+#define UNIP_DBG_FORCE_DME_CTRL_STATE 0x150
+#define UNIP_DBG_AUTO_DME_LINKSTARTUP 0x158
+#define UNIP_DBG_PA_CTRLSTATE 0x15C
+#define UNIP_DBG_PA_TX_STATE 0x160
+#define UNIP_DBG_BREAK_DME_CTRL_STATE 0x164
+#define UNIP_DBG_STEP_DME_CTRL_STATE 0x168
+#define UNIP_DBG_NEXT_DME_CTRL_STATE 0x16C
+
+/*
+ * Driver specific definitions
+ */
+struct exynos_ufs_phy {
+ void __iomem *reg_pma;
+};
+
+struct exynos_ufs_clk_info {
+ struct list_head list;
+ struct clk *clk;
+ const char *name;
+ u32 freq;
+};
+
+struct exynos_ufs_misc_log {
+ struct list_head clk_list_head;
+ bool isolation;
+};
+
+struct exynos_ufs_sfr_log {
+ const char* name;
+ const u32 offset;
+#define LOG_STD_HCI_SFR 0xFFFFFFF0
+#define LOG_VS_HCI_SFR 0xFFFFFFF1
+#define LOG_FMP_SFR 0xFFFFFFF2
+#define LOG_UNIPRO_SFR 0xFFFFFFF3
+#define LOG_PMA_SFR 0xFFFFFFF4
+ u32 val;
+};
+
+struct exynos_ufs_attr_log {
+ const u32 offset;
+ u32 res;
+ u32 val;
+};
+
+struct exynos_ufs_perf {
+ u32 opcode; /* 0: read, 1: write */
+ u32 chunk_size;
+ ktime_t time;
+ u64 total_time;
+ u32 count;
+ u32 total_count;
+};
+
+/* Main structure for debug and performance */
+struct exynos_ufs_debug {
+ struct exynos_ufs_sfr_log* std_sfr;
+ struct exynos_ufs_sfr_log* sfr;
+ struct exynos_ufs_attr_log* attr;
+ struct exynos_ufs_misc_log misc;
+ struct exynos_ufs_perf perf;
+};
+
+struct exynos_access_cxt {
+ u32 offset;
+ u32 mask;
+ u32 val;
+};
+
+struct uic_pwr_mode {
+ u8 lane;
+ u8 gear;
+ u8 mode;
+ u8 hs_series;
+};
+
+struct exynos_ufs {
+ struct device *dev;
+ struct ufs_hba *hba;
+
+ void __iomem *reg_hci;
+ void __iomem *reg_unipro;
+
+ struct regmap *pmureg;
+ struct regmap *sysreg;
+
+ struct clk *clk_hci;
+ struct clk *pclk;
+ struct clk *clk_unipro;
+ u32 mclk_rate;
+
+ int num_rx_lanes;
+ int num_tx_lanes;
+
+ struct exynos_ufs_phy phy;
+ struct uic_pwr_mode req_pmd_parm;
+ struct uic_pwr_mode act_pmd_parm;
+
+ u32 rx_min_actv_time_cap;
+ u32 rx_hibern8_time_cap;
+ u32 tx_hibern8_time_cap;
+
+ /* for miscellaneous control */
+ u32 misc_flags;
+#define EXYNOS_UFS_MISC_TOGGLE_LOG BIT(0)
+
+ struct exynos_ufs_debug debug;
+
+ u32 hw_rev;
+
+ struct exynos_access_cxt cxt_iso; /* phy isolation */
+ struct exynos_access_cxt cxt_coherency; /* io coherency */
+};
+
+static inline struct exynos_ufs *to_exynos_ufs(struct ufs_hba *hba)
+{
+ return dev_get_platdata(hba->dev);
+}
+
+#ifndef __EXYNOS_UFS_MMIO_FUNC__
+#define __EXYNOS_UFS_MMIO_FUNC__
+#define EXYNOS_UFS_MMIO_FUNC(name) \
+static inline void name##_writel(struct exynos_ufs *ufs, u32 val, u32 reg) \
+{ \
+ writel(val, ufs->reg_##name + reg); \
+} \
+ \
+static inline u32 name##_readl(struct exynos_ufs *ufs, u32 reg) \
+{ \
+ return readl(ufs->reg_##name + reg); \
+}
+
+EXYNOS_UFS_MMIO_FUNC(hci);
+EXYNOS_UFS_MMIO_FUNC(unipro);
+
+static inline void phy_pma_writel(struct exynos_ufs *ufs, u32 val, u32 reg)
+{
+ u32 reg1 = hci_readl(ufs, HCI_CLKSTOP_CTRL);
+
+ hci_writel(ufs, reg1 & ~MPHY_APBCLK_STOP, HCI_CLKSTOP_CTRL);
+ writel(val, ufs->phy.reg_pma + reg);
+ hci_writel(ufs, reg1 | MPHY_APBCLK_STOP, HCI_CLKSTOP_CTRL);
+}
+
+static inline u32 phy_pma_readl(struct exynos_ufs *ufs, u32 reg)
+{
+ u32 reg1 = hci_readl(ufs, HCI_CLKSTOP_CTRL);
+
+ hci_writel(ufs, reg1 & ~MPHY_APBCLK_STOP, HCI_CLKSTOP_CTRL);
+ reg = readl(ufs->phy.reg_pma + reg);
+ hci_writel(ufs, reg1 | MPHY_APBCLK_STOP, HCI_CLKSTOP_CTRL);
+
+ return reg;
+}
+#endif
+
+#endif /* _UFS_EXYNOS_H_ */
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 1332e544da92..1afd5ac9707c 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -308,6 +308,7 @@ struct ufs_hba_variant_ops {
int (*setup_clocks)(struct ufs_hba *, bool,
enum ufs_notify_change_status);
int (*setup_regulators)(struct ufs_hba *, bool);
+ void (*host_reset)(struct ufs_hba *);
int (*hce_enable_notify)(struct ufs_hba *,
enum ufs_notify_change_status);
int (*link_startup_notify)(struct ufs_hba *,
--
2.11.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] scsi: ufs: add Exynos-specific driver
2017-11-28 5:36 ` [PATCH 2/2] scsi: ufs: add Exynos-specific driver 김기웅
@ 2017-11-28 8:19 ` Jaehoon Chung
2017-11-28 8:27 ` 김기웅
2017-11-28 14:24 ` Christoph Hellwig
1 sibling, 1 reply; 8+ messages in thread
From: Jaehoon Chung @ 2017-11-28 8:19 UTC (permalink / raw)
To: 김기웅, linux-scsi, Martin K. Petersen
Cc: cpgs, HeonGwang Chu, 김부진, YOUNGEUN PARK
Hi,
On 11/28/2017 02:36 PM, 김기웅 wrote:
> This driver is to use UFS devices on Exynos SoC and
> has been already used for many years for commercial products.
Well, i'm not sure but i remembered there are the similar patches before..Seungwon and Alim's patches.
Is it relevant to them?
Anyway.. i think i can't test with only these patches..
how did you test this patches?
>
> Signed-off-by: Kiwoong Kim <kwmad.kim@samsung.com>
> ---
> drivers/scsi/ufs/Kconfig | 10 +
> drivers/scsi/ufs/Makefile | 1 +
> drivers/scsi/ufs/ufs-exynos.c | 962 ++++++++++++++++++++++++++++++++++++++++++
> drivers/scsi/ufs/ufs-exynos.h | 351 +++++++++++++++
> drivers/scsi/ufs/ufshcd.h | 1 +
> 5 files changed, 1325 insertions(+)
> create mode 100644 drivers/scsi/ufs/ufs-exynos.c
> create mode 100644 drivers/scsi/ufs/ufs-exynos.h
There is no binding file.
>
> diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig
> index e27b4d4e6ae2..7d71ad8768c3 100644
> --- a/drivers/scsi/ufs/Kconfig
> +++ b/drivers/scsi/ufs/Kconfig
> @@ -100,3 +100,13 @@ config SCSI_UFS_QCOM
>
> Select this if you have UFS controller on QCOM chipset.
> If unsure, say N.
> +
> +config SCSI_UFS_EXYNOS
> + tristate "EXYNOS UFS Host Controller Driver"
> + depends on SCSI_UFSHCD && SCSI_UFSHCD_PLATFORM
> + ---help---
> + This selects the EXYNOS UFS host controller driver.
> +
> + If you have a controller with this interface, say Y or M here.
> +
> + If unsure, say N.
> diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile
> index 9310c6c83041..3312b052dcff 100644
> --- a/drivers/scsi/ufs/Makefile
> +++ b/drivers/scsi/ufs/Makefile
> @@ -3,6 +3,7 @@
> obj-$(CONFIG_SCSI_UFS_DWC_TC_PCI) += tc-dwc-g210-pci.o ufshcd-dwc.o tc-dwc-g210.o
> obj-$(CONFIG_SCSI_UFS_DWC_TC_PLATFORM) += tc-dwc-g210-pltfrm.o ufshcd-dwc.o tc-dwc-g210.o
> obj-$(CONFIG_SCSI_UFS_QCOM) += ufs-qcom.o
> +obj-$(CONFIG_SCSI_UFS_EXYNOS) += ufs-exynos.o
> obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o
> obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o
> obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o
> diff --git a/drivers/scsi/ufs/ufs-exynos.c b/drivers/scsi/ufs/ufs-exynos.c
> new file mode 100644
> index 000000000000..98e5aeb80b06
> --- /dev/null
> +++ b/drivers/scsi/ufs/ufs-exynos.c
> @@ -0,0 +1,962 @@
> +/*
> + * UFS Host Controller driver for Exynos specific extensions
> + *
> + * Copyright (C) 2013-2014 Samsung Electronics Co., Ltd.
2013-2014? is it right?
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/clk.h>
> +#include "ufshcd.h"
> +#include "ufshcd-pltfrm.h"
> +#include "ufs-exynos.h"
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +#include <linux/spinlock.h>
ordering about header file.
> +
> +/*
> + * Debugging information, SFR/attributes/misc
> + */
> +static struct exynos_ufs *ufs_host_backup[1];> +static int ufs_host_index = 0;
It has to use the global? Is there any other solution?
> +
> +static struct exynos_ufs_sfr_log ufs_log_std_sfr[] = {
> + {"CAPABILITIES" , REG_CONTROLLER_CAPABILITIES, 0},
> + {"UFS VERSION" , REG_UFS_VERSION, 0},
> + {"PRODUCT ID" , REG_CONTROLLER_DEV_ID, 0},
> + {"MANUFACTURE ID" , REG_CONTROLLER_PROD_ID, 0},
> + {"INTERRUPT STATUS" , REG_INTERRUPT_STATUS, 0},
> + {"INTERRUPT ENABLE" , REG_INTERRUPT_ENABLE, 0},
> + {"CONTROLLER STATUS" , REG_CONTROLLER_STATUS, 0},
> + {"CONTROLLER ENABLE" , REG_CONTROLLER_ENABLE, 0},
> + {"UIC ERR PHY ADAPTER LAYER" , REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER, 0},
> + {"UIC ERR DATA LINK LAYER" , REG_UIC_ERROR_CODE_DATA_LINK_LAYER, 0},
> + {"UIC ERR NETWORK LATER" , REG_UIC_ERROR_CODE_NETWORK_LAYER, 0},
> + {"UIC ERR TRANSPORT LAYER" , REG_UIC_ERROR_CODE_TRANSPORT_LAYER, 0},
> + {"UIC ERR DME" , REG_UIC_ERROR_CODE_DME, 0},
> + {"UTP TRANSF REQ INT AGG CNTRL" , REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL, 0},
> + {"UTP TRANSF REQ LIST BASE L" , REG_UTP_TRANSFER_REQ_LIST_BASE_L, 0},
> + {"UTP TRANSF REQ LIST BASE H" , REG_UTP_TRANSFER_REQ_LIST_BASE_H, 0},
> + {"UTP TRANSF REQ DOOR BELL" , REG_UTP_TRANSFER_REQ_DOOR_BELL, 0},
> + {"UTP TRANSF REQ LIST CLEAR" , REG_UTP_TRANSFER_REQ_LIST_CLEAR, 0},
> + {"UTP TRANSF REQ LIST RUN STOP" , REG_UTP_TRANSFER_REQ_LIST_RUN_STOP, 0},
> + {"UTP TASK REQ LIST BASE L" , REG_UTP_TASK_REQ_LIST_BASE_L, 0},
> + {"UTP TASK REQ LIST BASE H" , REG_UTP_TASK_REQ_LIST_BASE_H, 0},
> + {"UTP TASK REQ DOOR BELL" , REG_UTP_TASK_REQ_DOOR_BELL, 0},
> + {"UTP TASK REQ LIST CLEAR" , REG_UTP_TASK_REQ_LIST_CLEAR, 0},
> + {"UTP TASK REQ LIST RUN STOP" , REG_UTP_TASK_REQ_LIST_RUN_STOP, 0},
> + {"UIC COMMAND" , REG_UIC_COMMAND, 0},
> + {"UIC COMMAND ARG1" , REG_UIC_COMMAND_ARG_1, 0},
> + {"UIC COMMAND ARG2" , REG_UIC_COMMAND_ARG_2, 0},
> + {"UIC COMMAND ARG3" , REG_UIC_COMMAND_ARG_3, 0},
> +
> + {},
> +};
> +
> +/* Helper for UFS CAL interface */
> +static inline int ufs_init_cal(struct exynos_ufs *ufs, int idx,
> + struct platform_device *pdev)
> +{
> + return 0;
> +}
> +
> +static inline int ufs_pre_link(struct exynos_ufs *ufs)
> +{
> + return 0;
> +}
> +
> +static inline int ufs_post_link(struct exynos_ufs *ufs)
> +{
> + return 0;
> +}
> +
> +static inline int ufs_pre_gear_change(struct exynos_ufs *ufs,
> + struct uic_pwr_mode *pmd)
> +{
> + return 0;
> +}
> +
> +static inline int ufs_post_gear_change(struct exynos_ufs *ufs)
> +{
> + return 0;
> +}
> +
> +static inline int ufs_post_h8_enter(struct exynos_ufs *ufs)
> +{
> + return 0;
> +}
> +
> +static inline int ufs_pre_h8_exit(struct exynos_ufs *ufs)
> +{
> + return 0;
> +}
What purpose has these helper fuctions?
> +
> +static inline void exynos_ufs_ctrl_phy_pwr(struct exynos_ufs *ufs, bool en)
> +{
> + int ret = 0;
> +
> + if (en)
> + ret = regmap_update_bits(ufs->pmureg, ufs->cxt_iso.offset,
> + ufs->cxt_iso.mask, ufs->cxt_iso.val);
> + else
> + ret = regmap_update_bits(ufs->pmureg, ufs->cxt_iso.offset,
> + ufs->cxt_iso.mask, 0);
> +
> + if (ret)
> + dev_err(ufs->dev, "Unable to update PHY ISO control\n");
> +}
> +
> +#ifndef __EXYNOS_UFS_VS_DEBUG__
There is no defined "__EXYNOS_UFS_VS_DEBUG__". Remove it or include it into Kconfig.
> +static void exynos_ufs_dump_std_sfr(struct ufs_hba *hba)
> +{
> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> + struct exynos_ufs_sfr_log* cfg = ufs->debug.std_sfr;
> +
> + dev_err(hba->dev, ": --------------------------------------------------- \n");
> + dev_err(hba->dev, ": \t\tREGISTER DUMP\n");
> + dev_err(hba->dev, ": --------------------------------------------------- \n");
> +
> + while(cfg) {
> + if (!cfg->name)
> + break;
> + cfg->val = ufshcd_readl(hba, cfg->offset);
> +
> + /* Dump */
> + dev_err(hba->dev, ": %s(0x%04x):\t\t\t\t0x%08x\n",
> + cfg->name, cfg->offset, cfg->val);
> +
> + /* Next SFR */
> + cfg++;
> + }
> +}
> +#endif
> +
> +/*
> + * Exynos debugging main function
> + */
> +static void exynos_ufs_dump_debug_info(struct ufs_hba *hba)
> +{
> +#ifdef __EXYNOS_UFS_VS_DEBUG__
What is this? Can be remove this.
> +#else
> + exynos_ufs_dump_std_sfr(hba);
> +#endif
> +}
> +
> +static inline void exynos_ufs_set_hwacg_control(struct exynos_ufs *ufs, bool en)
> +{
> + u32 reg;
> + if ((ufs->hw_rev != UFS_VER_0004) && (ufs->hw_rev != UFS_VER_0005))
> + return;
> +
> + /*
> + * default value 1->0 at KC. so,
> + * need to set "1(disable HWACG)" during UFS init
> + */
> + reg = hci_readl(ufs, HCI_UFS_ACG_DISABLE);
> + if (en)
> + hci_writel(ufs, reg & (~HCI_UFS_ACG_DISABLE_EN), HCI_UFS_ACG_DISABLE);
> + else
> + hci_writel(ufs, reg | HCI_UFS_ACG_DISABLE_EN, HCI_UFS_ACG_DISABLE);
> +
> +}
> +
> +static inline void exynos_ufs_ctrl_auto_hci_clk(struct exynos_ufs *ufs, bool en)
> +{
> + u32 reg = hci_readl(ufs, HCI_FORCE_HCS);
> +
> + if (en)
> + hci_writel(ufs, reg | HCI_CORECLK_STOP_EN, HCI_FORCE_HCS);
> + else
> + hci_writel(ufs, reg & ~HCI_CORECLK_STOP_EN, HCI_FORCE_HCS);
> +}
> +
> +static inline void exynos_ufs_ctrl_clk(struct exynos_ufs *ufs, bool en)
> +{
> + u32 reg = hci_readl(ufs, HCI_FORCE_HCS);
> +
> + if (en)
> + hci_writel(ufs, reg | CLK_STOP_CTRL_EN_ALL, HCI_FORCE_HCS);
> + else
> + hci_writel(ufs, reg & ~CLK_STOP_CTRL_EN_ALL, HCI_FORCE_HCS);
> +}
> +
> +static inline void exynos_ufs_gate_clk(struct exynos_ufs *ufs, bool en)
> +{
> +
> + u32 reg = hci_readl(ufs, HCI_CLKSTOP_CTRL);
> +
> + if (en)
> + hci_writel(ufs, reg | CLK_STOP_ALL, HCI_CLKSTOP_CTRL);
> + else
> + hci_writel(ufs, reg & ~CLK_STOP_ALL, HCI_CLKSTOP_CTRL);
> +}
> +
> +static inline void exynos_ufs_set_unipro_mclk(struct exynos_ufs *ufs)
> +{
> + ufs->mclk_rate = clk_get_rate(ufs->clk_unipro);
> +}
> +
> +static inline void exynos_ufs_fit_aggr_timeout(struct exynos_ufs *ufs)
> +{
> + u32 cnt_val;
> + u32 nVal;
Don't use the upper character.
> +
> + /* IA_TICK_SEL : 1(1us_TO_CNT_VAL) */
> + nVal = hci_readl(ufs, HCI_UFSHCI_V2P1_CTRL);
> + nVal |= IA_TICK_SEL;
> + hci_writel(ufs, nVal, HCI_UFSHCI_V2P1_CTRL);
> +
> + cnt_val = ufs->mclk_rate / 1000000 ;
> + hci_writel(ufs, cnt_val & CNT_VAL_1US_MASK, HCI_1US_TO_CNT_VAL);
> +}
> +
> +static void exynos_ufs_init_pmc_req(struct ufs_hba *hba,
> + struct ufs_pa_layer_attr *pwr_max,
> + struct ufs_pa_layer_attr *pwr_req)
> +{
> +
> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> + struct uic_pwr_mode *req_pmd = &ufs->req_pmd_parm;
> + struct uic_pwr_mode *act_pmd = &ufs->act_pmd_parm;
> +
> + /* update lane variable after link */
> + ufs->num_rx_lanes = pwr_max->lane_rx;
> + ufs->num_tx_lanes = pwr_max->lane_tx;
> +
> + pwr_req->gear_rx
> + = act_pmd->gear= min_t(u8, pwr_max->gear_rx, req_pmd->gear);
> + pwr_req->gear_tx
> + = act_pmd->gear = min_t(u8, pwr_max->gear_tx, req_pmd->gear);
> + pwr_req->lane_rx
> + = act_pmd->lane = min_t(u8, pwr_max->lane_rx, req_pmd->lane);
> + pwr_req->lane_tx
> + = act_pmd->lane = min_t(u8, pwr_max->lane_tx, req_pmd->lane);
> + pwr_req->pwr_rx = act_pmd->mode = req_pmd->mode;
> + pwr_req->pwr_tx = act_pmd->mode = req_pmd->mode;
> + pwr_req->hs_rate = act_pmd->hs_series = req_pmd->hs_series;
> +}
> +
> +static void exynos_ufs_config_intr(struct exynos_ufs *ufs, u32 errs, u8 index)
> +{
> + switch(index) {
> + case UNIP_PA_LYR:
> + hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERROR_EN_PA_LAYER);
> + break;
> + case UNIP_DL_LYR:
> + hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERROR_EN_DL_LAYER);
> + break;
> + case UNIP_N_LYR:
> + hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERROR_EN_N_LAYER);
> + break;
> + case UNIP_T_LYR:
> + hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERROR_EN_T_LAYER);
> + break;
> + case UNIP_DME_LYR:
> + hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERROR_EN_DME_LAYER);
> + break;
> + }
> +}
> +
> +static void exynos_ufs_dev_hw_reset(struct ufs_hba *hba)
> +{
> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> +
> + /* bit[1] for resetn */
f> + hci_writel(ufs, 0 << 0, HCI_GPIO_OUT);
your comment is "bit[1] for resetn" ..but this bit conotrol is for bit[0].
I don't want to use "0 << 0" and " 1 << 0".
#define RESETN_HIGH/LOW whatever...
> + udelay(5);
> + hci_writel(ufs, 1 << 0, HCI_GPIO_OUT);
> +}
> +
> +static void exynos_ufs_init_host(struct exynos_ufs *ufs)
> +{
> + u32 reg;
> +
> + /* internal clock control */
> + exynos_ufs_ctrl_auto_hci_clk(ufs, false);
> + exynos_ufs_set_unipro_mclk(ufs);
> +
> + /* period for interrupt aggregation */
> + exynos_ufs_fit_aggr_timeout(ufs);
> +
> + /* misc HCI configurations */
> + hci_writel(ufs, 0xA, HCI_DATA_REORDER);
Don't use magin number. what is 0xA?
> + hci_writel(ufs, PRDT_PREFECT_EN | PRDT_SET_SIZE(12),
> + HCI_TXPRDT_ENTRY_SIZE);
> + hci_writel(ufs, PRDT_SET_SIZE(12), HCI_RXPRDT_ENTRY_SIZE);
> + hci_writel(ufs, 0xFFFFFFFF, HCI_UTRL_NEXUS_TYPE);
> + hci_writel(ufs, 0xFFFFFFFF, HCI_UTMRL_NEXUS_TYPE);
Ditto.
> +
> + reg = hci_readl(ufs, HCI_AXIDMA_RWDATA_BURST_LEN) &
> + ~BURST_LEN(0);
> + hci_writel(ufs, WLU_EN | BURST_LEN(3),
> + HCI_AXIDMA_RWDATA_BURST_LEN);
> +
> + /*
> + * Enable HWAGC control by IOP
> + *
> + * default value 1->0 at KC.
> + * always "0"(controlled by UFS_ACG_DISABLE)
> + */
> + reg = hci_readl(ufs, HCI_IOP_ACG_DISABLE);
> + hci_writel(ufs, reg & (~HCI_IOP_ACG_DISABLE_EN), HCI_IOP_ACG_DISABLE);
> +}
> +
> +static int exynos_ufs_init_system(struct exynos_ufs *ufs)
> +{
> + struct device *dev = ufs->dev;
> + int ret = 0;
> + bool is_io_coherency;
> + bool is_dma_coherent;
> +
> + /* PHY isolation bypass */
> + exynos_ufs_ctrl_phy_pwr(ufs, true);
> +
> + /* IO cohernecy */
> + is_io_coherency = !IS_ERR(ufs->sysreg);
> + is_dma_coherent = !!of_find_property(dev->of_node,
> + "dma-coherent", NULL);
> +
> + if (is_io_coherency != is_dma_coherent)
> + BUG();
BUG()?
> +
> + if (!is_io_coherency)
> + dev_err(dev, "Not configured to use IO coherency\n");
> + else
> + ret = regmap_update_bits(ufs->sysreg, ufs->cxt_coherency.offset,
> + ufs->cxt_coherency.mask, ufs->cxt_coherency.val);
> +
> + return ret;
> +}
> +
> +static int exynos_ufs_get_clks(struct ufs_hba *hba)
> +{
> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> + struct list_head *head = &hba->clk_list_head;
> + struct ufs_clk_info *clki;
> + int i = 0;
> +
> + ufs_host_backup[ufs_host_index++] = ufs;
> + ufs->debug.std_sfr = ufs_log_std_sfr;
> +
> + if (!head || list_empty(head))
> + goto out;
> +
> + list_for_each_entry(clki, head, list) {
> + /*
> + * get clock with an order listed in device tree
> + */
> + if (i == 0)
> + ufs->clk_hci = clki->clk;
> + else if (i == 1)
> + ufs->clk_unipro = clki->clk;
> +
> + i++;
> + }
> +out:
> + if (!ufs->clk_hci || !ufs->clk_unipro)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static void exynos_ufs_set_features(struct ufs_hba *hba, u32 hw_rev)
> +{
> + /* caps */
> + hba->caps = UFSHCD_CAP_CLK_GATING |
> + UFSHCD_CAP_HIBERN8_WITH_CLK_GATING |
> + UFSHCD_CAP_INTR_AGGR;
> +
> + /* quirks of common driver */
> + hba->quirks = UFSHCD_QUIRK_PRDT_BYTE_GRAN;
> +
> + /* quirks of exynos-specific driver */
Remove unused comment.
> +}
> +
> +/*
> + * Exynos-specific callback functions
> + *
> + * init | Pure SW init & system-related init
> + * host_reset | Host SW reset & init
> + * ...
> + *
> + * Initializations for software, host controller and system
> + * should be contained only in ->host_reset() as possible.
> + */
> +
> +static int exynos_ufs_init(struct ufs_hba *hba)
> +{
> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> + int ret;
> +
> + /* set features, such as caps or quirks */
> + exynos_ufs_set_features(hba, ufs->hw_rev);
> +
> + /* get some clock sources and debug infomation structures */
> + ret = exynos_ufs_get_clks(hba);
> + if (ret)
> + return ret;
> +
> + /* system init */
> + ret = exynos_ufs_init_system(ufs);
> + if (ret)
> + return ret;
> +
> + ufs->misc_flags = EXYNOS_UFS_MISC_TOGGLE_LOG;
> +
> + return 0;
> +}
> +
> +static void exynos_ufs_host_reset(struct ufs_hba *hba)
> +{
> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> + unsigned long timeout = jiffies + msecs_to_jiffies(1);
> +
> + exynos_ufs_ctrl_auto_hci_clk(ufs, false);
> +
> + hci_writel(ufs, UFS_SW_RST_MASK, HCI_SW_RST);
> +
> + do {
> + if (!(hci_readl(ufs, HCI_SW_RST) & UFS_SW_RST_MASK))
> + goto success;
Need to "goto" statement? Just can be located the below code.
if () {
exynos_ufs_init_host();
exynos_ufs_dev_hw_reset();
return;
}
> + } while (time_before(jiffies, timeout));
> +
> + dev_err(ufs->dev, "timeout host sw-reset\n");
> +
> + goto out;
Not need "goto out", instead "return" at here.
> +
> +success:
> + /* host init */
> + exynos_ufs_init_host(ufs);
> +
> + /* device reset */
> + exynos_ufs_dev_hw_reset(hba);
> +out:
> + return;
> +}
> +
> +static inline void exynos_ufs_dev_reset_ctrl(struct exynos_ufs *ufs, bool en)
> +{
> +
> + if (en)
> + hci_writel(ufs, 1 << 0, HCI_GPIO_OUT);
> + else
> + hci_writel(ufs, 0 << 0, HCI_GPIO_OUT);
I don't know why this function is need. There is no call anywhere with "true".
> +}
> +
> +static int exynos_ufs_setup_clocks(struct ufs_hba *hba, bool on,
> + enum ufs_notify_change_status status)
> +{
> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> + int ret = 0;
> +
> + if (status == PRE_CHANGE) {
> + if (on) {
> + /*
> + * Now all used blocks would not be turned off in a host.
> + */
> + exynos_ufs_ctrl_auto_hci_clk(ufs, false);
> + exynos_ufs_gate_clk(ufs, false);
> +
> + /* HWAGC disable */
> + exynos_ufs_set_hwacg_control(ufs, false);
> + } else {
> + ret = ufs_post_h8_enter(ufs);
> + }
> + } else {
> + if (on) {
> + ret = ufs_pre_h8_exit(ufs);
> + } else {
> + /*
> + * Now all used blocks would be turned off in a host.
> + */
> + exynos_ufs_ctrl_auto_hci_clk(ufs, true);
> +
> + /* HWAGC enable */
> + exynos_ufs_set_hwacg_control(ufs, true);
> + }
> + }
> +
> + return ret;
> +}
> +
> +static int exynos_ufs_link_startup_notify(struct ufs_hba *hba,
> + enum ufs_notify_change_status status)
> +{
> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> + int ret = 0;
> +
> + switch (status) {
> + case PRE_CHANGE:
> + /* refer to hba */
> + ufs->hba = hba;
> +
> + /* hci */
> + exynos_ufs_config_intr(ufs, DFES_DEF_DL_ERRS, UNIP_DL_LYR);
> + exynos_ufs_config_intr(ufs, DFES_DEF_N_ERRS, UNIP_N_LYR);
> + exynos_ufs_config_intr(ufs, DFES_DEF_T_ERRS, UNIP_T_LYR);
> +
> + exynos_ufs_ctrl_clk(ufs, true);
> + exynos_ufs_gate_clk(ufs, false);
> + exynos_ufs_set_hwacg_control(ufs, false);
> +
> + if (ufs->num_rx_lanes == 0 || ufs->num_tx_lanes == 0) {
> + ufshcd_dme_get(hba, UIC_ARG_MIB(PA_AVAILRXDATALANES),
> + &ufs->num_rx_lanes);
> + ufshcd_dme_get(hba, UIC_ARG_MIB(PA_AVAILTXDATALANES),
> + &ufs->num_tx_lanes);
> + WARN(ufs->num_rx_lanes != ufs->num_tx_lanes,
> + "available data lane is not equal(rx:%d, tx:%d)\n",
> + ufs->num_rx_lanes, ufs->num_tx_lanes);
> + }
> +
> + ufs->mclk_rate = clk_get_rate(ufs->clk_unipro);
> +
> + ret = ufs_pre_link(ufs);
> + break;
> + case POST_CHANGE:
> + /* UIC configuration table after link startup */
> + ret = ufs_post_link(ufs);
> + break;
> + default:
> + break;
> + }
> +
> + return ret;
> +}
> +
> +static int exynos_ufs_pwr_change_notify(struct ufs_hba *hba,
> + enum ufs_notify_change_status status,
> + struct ufs_pa_layer_attr *pwr_max,
> + struct ufs_pa_layer_attr *pwr_req)
> +{
> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> + struct uic_pwr_mode *act_pmd = &ufs->act_pmd_parm;
> + int ret = 0;
> +
> + switch (status) {
> + case PRE_CHANGE:
> +
> + /* Set PMC parameters to be requested */
> + exynos_ufs_init_pmc_req(hba, pwr_max, pwr_req);
> +
> + /* UIC configuration table before power mode change */
> + ret = ufs_pre_gear_change(ufs, act_pmd);
> +
> + break;
> + case POST_CHANGE:
> + /* UIC configuration table after power mode change */
> + ret = ufs_post_gear_change(ufs);
> +
> + dev_info(ufs->dev,
> + "Power mode change(%d): M(%d)G(%d)L(%d)HS-series(%d)\n",
> + ret, act_pmd->mode, act_pmd->gear,
> + act_pmd->lane, act_pmd->hs_series);
Fix the indent
> + break;
> + default:
> + break;
> + }
> +
> + return ret;
> +}
> +
> +static void exynos_ufs_set_nexus_t_xfer_req(struct ufs_hba *hba,
> + int tag, bool op)
> +{
> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> + u32 type;
> +
> + type = hci_readl(ufs, HCI_UTRL_NEXUS_TYPE);
> +
> + if (op)
> + type |= (1 << tag);
> + else
> + type &= ~(1 << tag);
> +
> + hci_writel(ufs, type, HCI_UTRL_NEXUS_TYPE);
> +}
> +
> +static void exynos_ufs_set_nexus_t_task_mgmt(struct ufs_hba *hba, int tag, u8 tm_func)
> +{
> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> + u32 type;
> +
> + type = hci_readl(ufs, HCI_UTMRL_NEXUS_TYPE);
> +
> + switch (tm_func) {
> + case UFS_ABORT_TASK:
> + case UFS_QUERY_TASK:
> + type |= (1 << tag);
> + break;
> + case UFS_ABORT_TASK_SET:
> + case UFS_CLEAR_TASK_SET:
> + case UFS_LOGICAL_RESET:
> + case UFS_QUERY_TASK_SET:
> + type &= ~(1 << tag);
> + break;
> + }
> +
> + hci_writel(ufs, type, HCI_UTMRL_NEXUS_TYPE);
> +}
> +
> +static int __exynos_ufs_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
> +{
> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> +
> + exynos_ufs_dev_reset_ctrl(ufs, false);
> +
> + exynos_ufs_ctrl_phy_pwr(ufs, false);
> +
> + return 0;
> +}
> +
> +static int __exynos_ufs_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
> +{
> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> + int ret = 0;
> +
> + exynos_ufs_ctrl_phy_pwr(ufs, true);
> +
> + /* system init */
> + ret = exynos_ufs_init_system(ufs);
> + if (ret)
> + return ret;
> +
> + if (ufshcd_is_clkgating_allowed(hba))
> + clk_prepare_enable(ufs->clk_hci);
> + exynos_ufs_ctrl_auto_hci_clk(ufs, false);
> +
> + if (ufshcd_is_clkgating_allowed(hba))
> + clk_disable_unprepare(ufs->clk_hci);
> +
> + return 0;
> +}
> +
> +static struct ufs_hba_variant_ops exynos_ufs_ops = {
> + .init = exynos_ufs_init,
> + .host_reset = exynos_ufs_host_reset,
> + .setup_clocks = exynos_ufs_setup_clocks,
> + .link_startup_notify = exynos_ufs_link_startup_notify,
> + .pwr_change_notify = exynos_ufs_pwr_change_notify,
> + .setup_xfer_req = exynos_ufs_set_nexus_t_xfer_req,
> + .setup_task_mgmt = exynos_ufs_set_nexus_t_task_mgmt,
> + .dbg_register_dump = exynos_ufs_dump_debug_info,
> + .suspend = __exynos_ufs_suspend,
> + .resume = __exynos_ufs_resume,
> +};
> +
> +static int exynos_ufs_populate_dt_phy(struct device *dev, struct exynos_ufs *ufs)
> +{
> + struct device_node *ufs_phy;
> + struct exynos_ufs_phy *phy = &ufs->phy;
> + struct resource io_res;
> + int ret;
> +
> + ufs_phy = of_get_child_by_name(dev->of_node, "ufs-phy");
> + if (!ufs_phy) {
> + dev_err(dev, "failed to get ufs-phy node\n");
> + return -ENODEV;
> + }
> +
> + ret = of_address_to_resource(ufs_phy, 0, &io_res);
> + if (ret) {
> + dev_err(dev, "failed to get i/o address phy pma\n");
> + goto err_0;
> + }
> +
> + phy->reg_pma = devm_ioremap_resource(dev, &io_res);
> + if (!phy->reg_pma) {
> + dev_err(dev, "failed to ioremap for phy pma\n");
> + ret = -ENOMEM;
> + goto err_0;
> + }
> +
> +err_0:
> + of_node_put(ufs_phy);
> +
> + return ret;
> +}
> +
> +/*
> + * This function is to define offset, mask and shift to access somewhere.
> + */
> +static int exynos_ufs_set_context_for_access(struct device *dev,
> + const char *name, struct exynos_access_cxt *cxt)
> +{
> + struct device_node *np;
> + int ret;
> +
> + np = of_get_child_by_name(dev->of_node, name);
> + if (!np) {
> + dev_err(dev, "failed to get node(%s)\n", name);
> + return 1;
Don't use the meaningless value.
> + }
> +
> + ret = of_property_read_u32(np, "offset", &cxt->offset);
> + if (IS_ERR(&cxt->offset)) {
> + dev_err(dev, "failed to set cxt(%s) offset\n", name);
> + return cxt->offset;
> + }
> +
> + ret = of_property_read_u32(np, "mask", &cxt->mask);
> + if (IS_ERR(&cxt->mask)) {
> + dev_err(dev, "failed to set cxt(%s) mask\n", name);
> + return cxt->mask;
> + }
> +
> + ret = of_property_read_u32(np, "val", &cxt->val);
> + if (IS_ERR(&cxt->val)) {
> + dev_err(dev, "failed to set cxt(%s) val\n", name);
> + return cxt->val;
> + }
> +
> + return 0;
> +}
> +
> +static int exynos_ufs_populate_dt_system(struct device *dev, struct exynos_ufs *ufs)
> +{
> + int ret;
> +
> + /* regmap pmureg */
> + ufs->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
> + "samsung,pmu-phandle");
> + if (IS_ERR(ufs->pmureg)) {
> + /*
> + * phy isolation should be available.
> + * so this case need to be failed.
> + */
> + dev_err(dev, "pmu regmap lookup failed.\n");
> + return PTR_ERR(ufs->pmureg);
> + }
> +
> + /* Set access context for phy isolation bypass */
> + ret = exynos_ufs_set_context_for_access(dev, "ufs-phy-iso",
> + &ufs->cxt_iso);
> + if (ret == 1) {
> + /* no device node, default */
> + ufs->cxt_iso.offset = 0x0724;
> + ufs->cxt_iso.mask = 0x1;
> + ufs->cxt_iso.val = 0x1;
> + ret = 0;
> + }
> +
> + /* regmap sysreg */
> + ufs->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
> + "samsung,sysreg-fsys-phandle");
> + if (IS_ERR(ufs->sysreg)) {
> + /*
> + * Currently, ufs driver gets sysreg for io coherency.
> + * Some architecture might not support this feature.
> + * So the device node might not exist.
> + */
> + dev_err(dev, "sysreg regmap lookup failed.\n");
> + return 0;
return 0?
> + }
> +
> + /* Set access context for io coherency */
> + ret = exynos_ufs_set_context_for_access(dev, "ufs-dma-coherency",
> + &ufs->cxt_coherency);
> + if (ret == 1) {
> + /* no device node, default */
> + ufs->cxt_coherency.offset = 0x0700;
> + ufs->cxt_coherency.mask = 0x300; /* bit 8,9 */
> + ufs->cxt_coherency.val = 0x3;
> + ret = 0;
> + }
> +
> + return ret;
> +}
> +
> +static int exynos_ufs_get_pwr_mode(struct device_node *np,
> + struct exynos_ufs *ufs)
> +{
> + struct uic_pwr_mode *pmd = &ufs->req_pmd_parm;
> +
> + pmd->mode = FAST_MODE;
> +
> + if (of_property_read_u8(np, "ufs,pmd-attr-lane", &pmd->lane))
> + pmd->lane = 1;
> +
> + if (of_property_read_u8(np, "ufs,pmd-attr-gear", &pmd->gear))
> + pmd->gear = 1;
> +
> + pmd->hs_series = PA_HS_MODE_B;
> +
> + return 0;
> +}
> +
> +static int exynos_ufs_populate_dt(struct device *dev, struct exynos_ufs *ufs)
> +{
> + struct device_node *np = dev->of_node;
> + int ret;
> +
> + /* Get exynos-specific version for featuring */
> + if (of_property_read_u32(np, "hw-rev", &ufs->hw_rev))
> + ufs->hw_rev = UFS_VER_0004;
> +
> + ret = exynos_ufs_populate_dt_phy(dev, ufs);
> + if (ret) {
> + dev_err(dev, "failed to populate dt-phy\n");
> + goto out;
> + }
> +
> + ret = exynos_ufs_populate_dt_system(dev, ufs);
> + if (ret) {
> + dev_err(dev, "failed to populate dt-pmu\n");
> + goto out;
> + }
> +
> + exynos_ufs_get_pwr_mode(np, ufs);
> +
> +out:
> + return ret;
> +}
> +
> +static u64 exynos_ufs_dma_mask = DMA_BIT_MASK(32);
> +
> +static int exynos_ufs_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct exynos_ufs *ufs;
> + struct resource *res;
> + int ret;
> +
> + ufs = devm_kzalloc(dev, sizeof(*ufs), GFP_KERNEL);
> + if (!ufs) {
> + dev_err(dev, "cannot allocate mem for exynos-ufs\n");
> + return -ENOMEM;
> + }
> +
> + /* exynos-specific hci */
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> + ufs->reg_hci = devm_ioremap_resource(dev, res);
> + if (!ufs->reg_hci) {
> + dev_err(dev, "cannot ioremap for hci vendor register\n");
> + return -ENOMEM;
> + }
> +
> + /* unipro */
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> + ufs->reg_unipro = devm_ioremap_resource(dev, res);
> + if (!ufs->reg_unipro) {
> + dev_err(dev, "cannot ioremap for unipro register\n");
> + return -ENOMEM;
> + }
> +
> + /* This must be before calling exynos_ufs_populate_dt */
> + ret = ufs_init_cal(ufs, ufs_host_index, pdev);
> + if (ret)
> + return ret;
> +
> + ret = exynos_ufs_populate_dt(dev, ufs);
> + if (ret) {
> + dev_err(dev, "failed to get dt info.\n");
> + return ret;
> + }
> +
> + ufs->dev = dev;
> + dev->platform_data = ufs;
> + dev->dma_mask = &exynos_ufs_dma_mask;
> +
> + ret = ufshcd_pltfrm_init(pdev, &exynos_ufs_ops);
> +
> + return ret;
> +}
> +
> +static int exynos_ufs_remove(struct platform_device *pdev)
> +{
> + struct ufs_hba *hba = platform_get_drvdata(pdev);
> + struct exynos_ufs *ufs = dev_get_platdata(&pdev->dev);
> +
> + ufshcd_remove(hba);
> +
> + ufs->misc_flags = EXYNOS_UFS_MISC_TOGGLE_LOG;
> +
> + exynos_ufs_ctrl_phy_pwr(ufs, false);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int exynos_ufs_suspend(struct device *dev)
> +{
> + struct ufs_hba *hba = dev_get_drvdata(dev);
> +
> + return ufshcd_system_suspend(hba);
> +}
> +
> +static int exynos_ufs_resume(struct device *dev)
> +{
> + struct ufs_hba *hba = dev_get_drvdata(dev);
> +
> + return ufshcd_system_resume(hba);
> +}
> +#else
> +#define exynos_ufs_suspend NULL
> +#define exynos_ufs_resume NULL
> +#endif /* CONFIG_PM_SLEEP */
> +
> +#ifdef CONFIG_PM_RUNTIME
> +static int exynos_ufs_runtime_suspend(struct device *dev)
> +{
> + return ufshcd_system_suspend(dev_get_drvdata(dev));
> +}
> +
> +static int exynos_ufs_runtime_resume(struct device *dev)
> +{
> + return ufshcd_system_resume(dev_get_drvdata(dev));
> +}
> +
> +static int exynos_ufs_runtime_idle(struct device *dev)
> +{
> + return ufshcd_runtime_idle(dev_get_drvdata(dev));
> +}
> +
> +#else
> +#define exynos_ufs_runtime_suspend NULL
> +#define exynos_ufs_runtime_resume NULL
> +#define exynos_ufs_runtime_idle NULL
> +#endif /* CONFIG_PM_RUNTIME */
> +
> +static void exynos_ufs_shutdown(struct platform_device *pdev)
> +{
> + ufshcd_shutdown((struct ufs_hba *)platform_get_drvdata(pdev));
> +}
> +
> +static const struct dev_pm_ops exynos_ufs_dev_pm_ops = {
> + .suspend = exynos_ufs_suspend,
> + .resume = exynos_ufs_resume,
> + .runtime_suspend = exynos_ufs_runtime_suspend,
> + .runtime_resume = exynos_ufs_runtime_resume,
> + .runtime_idle = exynos_ufs_runtime_idle,
> +};
> +
> +static const struct of_device_id exynos_ufs_match[] = {
> + { .compatible = "samsung,exynos-ufs", },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, exynos_ufs_match);
> +
> +static struct platform_driver exynos_ufs_driver = {
> + .driver = {
> + .name = "exynos-ufs",
> + .owner = THIS_MODULE,
> + .pm = &exynos_ufs_dev_pm_ops,
> + .of_match_table = exynos_ufs_match,
> + .suppress_bind_attrs = true,
> + },
> + .probe = exynos_ufs_probe,
> + .remove = exynos_ufs_remove,
> + .shutdown = exynos_ufs_shutdown,
> +};
> +
> +module_platform_driver(exynos_ufs_driver);
> +MODULE_DESCRIPTION("Exynos Specific UFSHCI driver");
> +MODULE_AUTHOR("Seungwon Jeon <tgih.jun@samsung.com>");
> +MODULE_AUTHOR("Kiwoong Kim <kwmad.kim@samsung.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/scsi/ufs/ufs-exynos.h b/drivers/scsi/ufs/ufs-exynos.h
> new file mode 100644
> index 000000000000..0480fc4a8931
> --- /dev/null
> +++ b/drivers/scsi/ufs/ufs-exynos.h
> @@ -0,0 +1,351 @@
> +/*
> + * UFS Host Controller driver for Exynos specific extensions
> + *
> + * Copyright (C) 2013-2014 Samsung Electronics Co., Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#ifndef _UFS_EXYNOS_H_
> +#define _UFS_EXYNOS_H_
> +
> +#define UFS_VER_0004 4
> +#define UFS_VER_0005 5
> +
> +/*
> + * Exynos's Vendor specific registers for UFSHCI
> + */
> +#define HCI_TXPRDT_ENTRY_SIZE 0x00
> +#define HCI_RXPRDT_ENTRY_SIZE 0x04
> +#define HCI_TO_CNT_DIV_VAL 0x08
> +#define HCI_1US_TO_CNT_VAL 0x0C
> + #define CNT_VAL_1US_MASK 0x3ff
> +#define HCI_INVALID_UPIU_CTRL 0x10
> +#define HCI_INVALID_UPIU_BADDR 0x14
> +#define HCI_INVALID_UPIU_UBADDR 0x18
> +#define HCI_INVALID_UTMR_OFFSET_ADDR 0x1C
> +#define HCI_INVALID_UTR_OFFSET_ADDR 0x20
> +#define HCI_INVALID_DIN_OFFSET_ADDR 0x24
> +#define HCI_VENDOR_SPECIFIC_IS 0x38
> +#define HCI_VENDOR_SPECIFIC_IE 0x3C
> +#define HCI_UTRL_NEXUS_TYPE 0x40
> +#define HCI_UTMRL_NEXUS_TYPE 0x44
> +#define HCI_E2EFC_CTRL 0x48
> +#define HCI_SW_RST 0x50
> + #define UFS_LINK_SW_RST (1 << 0)
> + #define UFS_UNIPRO_SW_RST (1 << 1)
> + #define UFS_SW_RST_MASK (UFS_UNIPRO_SW_RST | UFS_LINK_SW_RST)
> +#define HCI_LINK_VERSION 0x54
> +#define HCI_IDLE_TIMER_CONFIG 0x58
> +#define HCI_RX_UPIU_MATCH_ERROR_CODE 0x5C
> +#define HCI_DATA_REORDER 0x60
> +#define HCI_MAX_DOUT_DATA_SIZE 0x64
> +#define HCI_UNIPRO_APB_CLK_CTRL 0x68
> +#define HCI_AXIDMA_RWDATA_BURST_LEN 0x6C
> + #define BURST_LEN(x) ((x) << 27 | (x))
> + #define WLU_EN (1 << 31)
Use BIT(31)
> + #define AXIDMA_RWDATA_BURST_LEN (0xF)
> +#define HCI_GPIO_OUT 0x70
> +#define HCI_WRITE_DMA_CTRL 0x74
> +#define HCI_ERROR_EN_PA_LAYER 0x78
> +#define HCI_ERROR_EN_DL_LAYER 0x7C
> +#define HCI_ERROR_EN_N_LAYER 0x80
> +#define HCI_ERROR_EN_T_LAYER 0x84
> +#define HCI_ERROR_EN_DME_LAYER 0x88
> +#define HCI_UFSHCI_V2P1_CTRL 0X8C
> +#define IA_TICK_SEL BIT(16)
> +#define HCI_REQ_HOLD_EN 0xAC
> +
> +#define HCI_CLKSTOP_CTRL 0xB0
> + #define REFCLKOUT_STOP BIT(4)
> + #define MPHY_APBCLK_STOP BIT(3)
> + #define REFCLK_STOP BIT(2)
> + #define UNIPRO_MCLK_STOP BIT(1)
> + #define UNIPRO_PCLK_STOP BIT(0)
> + #define CLK_STOP_ALL (REFCLKOUT_STOP |\
> + REFCLK_STOP |\
> + UNIPRO_MCLK_STOP |\
> + UNIPRO_PCLK_STOP)
> +
> +#define HCI_FORCE_HCS 0xB4
> + #define REFCLKOUT_STOP_EN BIT(11)
> + #define MPHY_APBCLK_STOP_EN BIT(10)
> + #define UFSP_DRCG_EN BIT(8)
> + #define REFCLK_STOP_EN BIT(7)
> + #define UNIPRO_PCLK_STOP_EN BIT(6)
> + #define UNIPRO_MCLK_STOP_EN BIT(5)
> + #define HCI_CORECLK_STOP_EN BIT(4)
> + #define CLK_STOP_CTRL_EN_ALL (UFSP_DRCG_EN |\
> + MPHY_APBCLK_STOP_EN |\
> + REFCLKOUT_STOP_EN |\
> + REFCLK_STOP_EN |\
> + UNIPRO_PCLK_STOP_EN |\
> + UNIPRO_MCLK_STOP_EN)
> +
> +#define HCI_FSM_MONITOR 0xC0
> +#define HCI_PRDT_HIT_RATIO 0xC4
> +#define HCI_DMA0_MONITOR_STATE 0xC8
> +#define HCI_DMA0_MONITOR_CNT 0xCC
> +#define HCI_DMA1_MONITOR_STATE 0xD0
> +#define HCI_DMA1_MONITOR_CNT 0xD4
> +
> +#define HCI_UFS_AXI_DMA_IF_CTRL 0xF8
> +#define HCI_UFS_ACG_DISABLE 0xFC
> + #define HCI_UFS_ACG_DISABLE_EN BIT(0)
> +#define HCI_IOP_ACG_DISABLE 0x100
> + #define HCI_IOP_ACG_DISABLE_EN BIT(0)
> +#define HCI_MPHY_REFCLK_SEL 0x108
> + #define MPHY_REFCLK_SEL BIT(0)
> +
> +/* Device fatal error */
> +#define DFES_ERR_EN BIT(31)
> +#define DFES_DEF_DL_ERRS (UIC_DATA_LINK_LAYER_ERROR_RX_BUF_OF |\
> + UIC_DATA_LINK_LAYER_ERROR_PA_INIT)
> +#define DFES_DEF_N_ERRS (UIC_NETWORK_UNSUPPORTED_HEADER_TYPE |\
> + UIC_NETWORK_BAD_DEVICEID_ENC |\
> + UIC_NETWORK_LHDR_TRAP_PACKET_DROPPING)
> +#define DFES_DEF_T_ERRS (UIC_TRANSPORT_UNSUPPORTED_HEADER_TYPE |\
> + UIC_TRANSPORT_UNKNOWN_CPORTID |\
> + UIC_TRANSPORT_NO_CONNECTION_RX |\
> + UIC_TRANSPORT_BAD_TC)
> +
> +/* TXPRDT defines */
> +#define PRDT_PREFECT_EN BIT(31)
> +#define PRDT_SET_SIZE(x) ((x) & 0x1F)
> +
> +enum {
> + UNIP_PA_LYR = 0,
> + UNIP_DL_LYR,
> + UNIP_N_LYR,
> + UNIP_T_LYR,
> + UNIP_DME_LYR,
> +};
> +
> +/*
> + * UNIPRO registers
> + */
> +#define UNIP_COMP_VERSION 0x000
> +#define UNIP_COMP_INFO 0x004
> +#define UNIP_COMP_RESET 0x010
> +
> +#define UNIP_DME_POWERON_REQ 0x7800
> +#define UNIP_DME_POWERON_CNF_RESULT 0x7804
> +#define UNIP_DME_POWEROFF_REQ 0x7810
> +#define UNIP_DME_POWEROFF_CNF_RESULT 0x7814
> +#define UNIP_DME_RESET_REQ 0x7820
> +#define UNIP_DME_RESET_REQ_LEVEL 0x7824
> +#define UNIP_DME_ENABLE_REQ 0x7830
> +#define UNIP_DME_ENABLE_CNF_RESULT 0x7834
> +#define UNIP_DME_ENDPOINTRESET_REQ 0x7840
> +#define UNIP_DME_ENDPOINTRESET_CNF_RESULT 0x7844
> +#define UNIP_DME_LINKSTARTUP_REQ 0x7850
> +#define UNIP_DME_LINKSTARTUP_CNF_RESULT 0x7854
> +#define UNIP_DME_HIBERN8_ENTER_REQ 0x7860
> +#define UNIP_DME_HIBERN8_ENTER_CNF_RESULT 0x7864
> +#define UNIP_DME_HIBERN8_ENTER_IND_RESULT 0x7868
> +#define UNIP_DME_HIBERN8_EXIT_REQ 0x7870
> +#define UNIP_DME_HIBERN8_EXIT_CNF_RESULT 0x7874
> +#define UNIP_DME_HIBERN8_EXIT_IND_RESULT 0x7878
> +#define UNIP_DME_PWR_REQ 0x7880
> +#define UNIP_DME_PWR_REQ_POWERMODE 0x7884
> +#define UNIP_DME_PWR_REQ_LOCALL2TIMER0 0x7888
> +#define UNIP_DME_PWR_REQ_LOCALL2TIMER1 0x788C
> +#define UNIP_DME_PWR_REQ_LOCALL2TIMER2 0x7890
> +#define UNIP_DME_PWR_REQ_REMOTEL2TIMER0 0x78B8
> +#define UNIP_DME_PWR_REQ_REMOTEL2TIMER1 0x78BC
> +#define UNIP_DME_PWR_REQ_REMOTEL2TIMER2 0x78C0
> +#define UNIP_DME_PWR_CNF_RESULT 0x78E8
> +#define UNIP_DME_PWR_IND_RESULT 0x78EC
> +#define UNIP_DME_TEST_MODE_REQ 0x7900
> +#define UNIP_DME_TEST_MODE_CNF_RESULT 0x7904
> +
> +#define UNIP_DME_ERROR_IND_LAYER 0x0C0
> +#define UNIP_DME_ERROR_IND_ERRCODE 0x0C4
> +#define UNIP_DME_PACP_CNFBIT 0x0C8
> +#define UNIP_DME_DL_FRAME_IND 0x0D0
> +#define UNIP_DME_INTR_STATUS 0x0E0
> +#define UNIP_DME_INTR_ENABLE 0x0E4
> +
> +#define UNIP_DME_GETSET_CONTROL 0x7A00
> +#define UNIP_DME_GETSET_ADDR 0x7A04
> +#define UNIP_DME_GETSET_WDATA 0x7A08
> +#define UNIP_DME_GETSET_RDATA 0x7A0C
> +#define UNIP_DME_GETSET_RESULT 0x7A10
> +#define UNIP_DME_PEER_GETSET_CONTROL 0x7A20
> +#define UNIP_DME_PEER_GETSET_ADDR 0x7A24
> +#define UNIP_DME_PEER_GETSET_WDATA 0x7A28
> +#define UNIP_DME_PEER_GETSET_RDATA 0x7A2C
> +#define UNIP_DME_PEER_GETSET_RESULT 0x7A30
> +
> +#define UNIP_DME_INTR_STATUS_LSB 0x7B00
> +#define UNIP_DME_INTR_STATUS_MSB 0x7B04
> +#define UNIP_DME_INTR_ERROR_CODE 0x7B20
> +#define UNIP_DME_DISCARD_PORT_ID 0x7B24
> +#define UNIP_DME_DBG_OPTION_SUITE 0x7C00
> +#define UNIP_DME_DBG_CTRL_FSM 0x7D00
> +#define UNIP_DME_DBG_FLAG_STATUS 0x7D14
> +#define UNIP_DME_DBG_LINKCFG_FSM 0x7D18
> +
> +#define UNIP_DME_INTR_ERROR_CODE 0x7B20
> +#define UNIP_DME_DEEPSTALL_ENTER_REQ 0x7910
> +#define UNIP_DME_DISCARD_CPORT_ID 0x7B24
> +
> +#define UNIP_DBG_FORCE_DME_CTRL_STATE 0x150
> +#define UNIP_DBG_AUTO_DME_LINKSTARTUP 0x158
> +#define UNIP_DBG_PA_CTRLSTATE 0x15C
> +#define UNIP_DBG_PA_TX_STATE 0x160
> +#define UNIP_DBG_BREAK_DME_CTRL_STATE 0x164
> +#define UNIP_DBG_STEP_DME_CTRL_STATE 0x168
> +#define UNIP_DBG_NEXT_DME_CTRL_STATE 0x16C
> +
> +/*
> + * Driver specific definitions
> + */
> +struct exynos_ufs_phy {
> + void __iomem *reg_pma;
> +};
> +
> +struct exynos_ufs_clk_info {
> + struct list_head list;
> + struct clk *clk;
> + const char *name;
> + u32 freq;
> +};
> +
> +struct exynos_ufs_misc_log {
> + struct list_head clk_list_head;
> + bool isolation;
> +};
> +
> +struct exynos_ufs_sfr_log {
> + const char* name;
> + const u32 offset;
> +#define LOG_STD_HCI_SFR 0xFFFFFFF0
> +#define LOG_VS_HCI_SFR 0xFFFFFFF1
> +#define LOG_FMP_SFR 0xFFFFFFF2
> +#define LOG_UNIPRO_SFR 0xFFFFFFF3
> +#define LOG_PMA_SFR 0xFFFFFFF4
> + u32 val;
> +};
> +
> +struct exynos_ufs_attr_log {
> + const u32 offset;
> + u32 res;
> + u32 val;
> +};
> +
> +struct exynos_ufs_perf {
> + u32 opcode; /* 0: read, 1: write */
> + u32 chunk_size;
> + ktime_t time;
> + u64 total_time;
> + u32 count;
> + u32 total_count;
> +};
> +
> +/* Main structure for debug and performance */
> +struct exynos_ufs_debug {
> + struct exynos_ufs_sfr_log* std_sfr;
> + struct exynos_ufs_sfr_log* sfr;
> + struct exynos_ufs_attr_log* attr;
> + struct exynos_ufs_misc_log misc;
> + struct exynos_ufs_perf perf;
> +};
> +
> +struct exynos_access_cxt {
> + u32 offset;
> + u32 mask;
> + u32 val;
> +};
> +
> +struct uic_pwr_mode {
> + u8 lane;
> + u8 gear;
> + u8 mode;
> + u8 hs_series;
> +};
> +
> +struct exynos_ufs {
> + struct device *dev;
> + struct ufs_hba *hba;
> +
> + void __iomem *reg_hci;
> + void __iomem *reg_unipro;
> +
> + struct regmap *pmureg;
> + struct regmap *sysreg;
> +
> + struct clk *clk_hci;
> + struct clk *pclk;
> + struct clk *clk_unipro;
> + u32 mclk_rate;
> +
> + int num_rx_lanes;
> + int num_tx_lanes;
> +
> + struct exynos_ufs_phy phy;
> + struct uic_pwr_mode req_pmd_parm;
> + struct uic_pwr_mode act_pmd_parm;
> +
> + u32 rx_min_actv_time_cap;
> + u32 rx_hibern8_time_cap;
> + u32 tx_hibern8_time_cap;
> +
> + /* for miscellaneous control */
> + u32 misc_flags;
> +#define EXYNOS_UFS_MISC_TOGGLE_LOG BIT(0)
> +
> + struct exynos_ufs_debug debug;
> +
> + u32 hw_rev;
> +
> + struct exynos_access_cxt cxt_iso; /* phy isolation */
> + struct exynos_access_cxt cxt_coherency; /* io coherency */
> +};
> +
> +static inline struct exynos_ufs *to_exynos_ufs(struct ufs_hba *hba)
> +{
> + return dev_get_platdata(hba->dev);
> +}
> +
> +#ifndef __EXYNOS_UFS_MMIO_FUNC__
> +#define __EXYNOS_UFS_MMIO_FUNC__
I don't understand..why need to check "ifndef __EXYNOS_UFS_MMIO_FUNC__".
(because i didnt find __EXYNOS_UFS_MMIO_FUNC__ anywhere.)
It means that you want to define always, doesn't?
> +#define EXYNOS_UFS_MMIO_FUNC(name) \
> +static inline void name##_writel(struct exynos_ufs *ufs, u32 val, u32 reg) \
> +{ \
> + writel(val, ufs->reg_##name + reg); \
> +} \
> + \
> +static inline u32 name##_readl(struct exynos_ufs *ufs, u32 reg) \
> +{ \
> + return readl(ufs->reg_##name + reg); \
> +}
> +
> +EXYNOS_UFS_MMIO_FUNC(hci);
> +EXYNOS_UFS_MMIO_FUNC(unipro);
Maybe.. i don't like this style..because it's too difficult to debug and find the function.
> +
> +static inline void phy_pma_writel(struct exynos_ufs *ufs, u32 val, u32 reg)
> +{
> + u32 reg1 = hci_readl(ufs, HCI_CLKSTOP_CTRL);
> +
> + hci_writel(ufs, reg1 & ~MPHY_APBCLK_STOP, HCI_CLKSTOP_CTRL);
> + writel(val, ufs->phy.reg_pma + reg);
> + hci_writel(ufs, reg1 | MPHY_APBCLK_STOP, HCI_CLKSTOP_CTRL);
> +}
> +
> +static inline u32 phy_pma_readl(struct exynos_ufs *ufs, u32 reg)
> +{
> + u32 reg1 = hci_readl(ufs, HCI_CLKSTOP_CTRL);
> +
> + hci_writel(ufs, reg1 & ~MPHY_APBCLK_STOP, HCI_CLKSTOP_CTRL);
> + reg = readl(ufs->phy.reg_pma + reg);
> + hci_writel(ufs, reg1 | MPHY_APBCLK_STOP, HCI_CLKSTOP_CTRL);
> +
> + return reg;
> +}
> +#endif
> +
> +#endif /* _UFS_EXYNOS_H_ */
> diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
> index 1332e544da92..1afd5ac9707c 100644
> --- a/drivers/scsi/ufs/ufshcd.h
> +++ b/drivers/scsi/ufs/ufshcd.h
> @@ -308,6 +308,7 @@ struct ufs_hba_variant_ops {
> int (*setup_clocks)(struct ufs_hba *, bool,
> enum ufs_notify_change_status);
> int (*setup_regulators)(struct ufs_hba *, bool);
> + void (*host_reset)(struct ufs_hba *);
> int (*hce_enable_notify)(struct ufs_hba *,
> enum ufs_notify_change_status);
> int (*link_startup_notify)(struct ufs_hba *,
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* RE: [PATCH 2/2] scsi: ufs: add Exynos-specific driver
2017-11-28 8:19 ` Jaehoon Chung
@ 2017-11-28 8:27 ` 김기웅
2017-11-28 9:20 ` Jaehoon Chung
0 siblings, 1 reply; 8+ messages in thread
From: 김기웅 @ 2017-11-28 8:27 UTC (permalink / raw)
To: 'Jaehoon Chung', linux-scsi, 'Martin K. Petersen'
Cc: cpgs, 'HeonGwang Chu', '김부진',
'YOUNGEUN PARK'
Hi.
This is modified from Seungwon's initial patch.
And this has been used for several commercial products.
I think you feel weird because you can't see a bunch of unipro and mphy.
But those stuff has been changed whenever new product comes.
So I didn't keep those in this driver.
> -----Original Message-----
> From: linux-scsi-owner@vger.kernel.org [mailto:linux-scsi-
> owner@vger.kernel.org] On Behalf Of Jaehoon Chung
> Sent: Tuesday, November 28, 2017 5:20 PM
> To: 김기웅; linux-scsi@vger.kernel.org; Martin K. Petersen
> Cc: cpgs@samsung.com; HeonGwang Chu; 김부진; YOUNGEUN PARK
> Subject: Re: [PATCH 2/2] scsi: ufs: add Exynos-specific driver
>
> Hi,
>
> On 11/28/2017 02:36 PM, 김기웅 wrote:
> > This driver is to use UFS devices on Exynos SoC and has been already
> > used for many years for commercial products.
>
> Well, i'm not sure but i remembered there are the similar patches
> before..Seungwon and Alim's patches.
> Is it relevant to them?
>
> Anyway.. i think i can't test with only these patches..
> how did you test this patches?
>
> >
> > Signed-off-by: Kiwoong Kim <kwmad.kim@samsung.com>
> > ---
> > drivers/scsi/ufs/Kconfig | 10 +
> > drivers/scsi/ufs/Makefile | 1 +
> > drivers/scsi/ufs/ufs-exynos.c | 962
> > ++++++++++++++++++++++++++++++++++++++++++
> > drivers/scsi/ufs/ufs-exynos.h | 351 +++++++++++++++
> > drivers/scsi/ufs/ufshcd.h | 1 +
> > 5 files changed, 1325 insertions(+)
> > create mode 100644 drivers/scsi/ufs/ufs-exynos.c create mode 100644
> > drivers/scsi/ufs/ufs-exynos.h
>
> There is no binding file.
>
> >
> > diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig index
> > e27b4d4e6ae2..7d71ad8768c3 100644
> > --- a/drivers/scsi/ufs/Kconfig
> > +++ b/drivers/scsi/ufs/Kconfig
> > @@ -100,3 +100,13 @@ config SCSI_UFS_QCOM
> >
> > Select this if you have UFS controller on QCOM chipset.
> > If unsure, say N.
> > +
> > +config SCSI_UFS_EXYNOS
> > + tristate "EXYNOS UFS Host Controller Driver"
> > + depends on SCSI_UFSHCD && SCSI_UFSHCD_PLATFORM
> > + ---help---
> > + This selects the EXYNOS UFS host controller driver.
> > +
> > + If you have a controller with this interface, say Y or M here.
> > +
> > + If unsure, say N.
> > diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile
> > index 9310c6c83041..3312b052dcff 100644
> > --- a/drivers/scsi/ufs/Makefile
> > +++ b/drivers/scsi/ufs/Makefile
> > @@ -3,6 +3,7 @@
> > obj-$(CONFIG_SCSI_UFS_DWC_TC_PCI) += tc-dwc-g210-pci.o ufshcd-dwc.o
> > tc-dwc-g210.o
> > obj-$(CONFIG_SCSI_UFS_DWC_TC_PLATFORM) += tc-dwc-g210-pltfrm.o
> > ufshcd-dwc.o tc-dwc-g210.o
> > obj-$(CONFIG_SCSI_UFS_QCOM) += ufs-qcom.o
> > +obj-$(CONFIG_SCSI_UFS_EXYNOS) += ufs-exynos.o
> > obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o
> > obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o
> > obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o diff --git
> > a/drivers/scsi/ufs/ufs-exynos.c b/drivers/scsi/ufs/ufs-exynos.c new
> > file mode 100644 index 000000000000..98e5aeb80b06
> > --- /dev/null
> > +++ b/drivers/scsi/ufs/ufs-exynos.c
> > @@ -0,0 +1,962 @@
> > +/*
> > + * UFS Host Controller driver for Exynos specific extensions
> > + *
> > + * Copyright (C) 2013-2014 Samsung Electronics Co., Ltd.
>
> 2013-2014? is it right?
>
> > + *
> > + * This program is free software; you can redistribute it and/or
> > +modify
> > + * it under the terms of the GNU General Public License as published
> > +by
> > + * the Free Software Foundation; either version 2 of the License, or
> > + * (at your option) any later version.
> > + */
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/clk.h>
> > +#include "ufshcd.h"
> > +#include "ufshcd-pltfrm.h"
> > +#include "ufs-exynos.h"
> > +#include <linux/mfd/syscon.h>
> > +#include <linux/regmap.h>
> > +#include <linux/spinlock.h>
>
> ordering about header file.
>
> > +
> > +/*
> > + * Debugging information, SFR/attributes/misc */ static struct
> > +exynos_ufs *ufs_host_backup[1];> +static int ufs_host_index = 0;
>
> It has to use the global? Is there any other solution?
>
> > +
> > +static struct exynos_ufs_sfr_log ufs_log_std_sfr[] = {
> > + {"CAPABILITIES" , REG_CONTROLLER_CAPABILITIES,
> 0},
> > + {"UFS VERSION" , REG_UFS_VERSION,
> 0},
> > + {"PRODUCT ID" , REG_CONTROLLER_DEV_ID,
> 0},
> > + {"MANUFACTURE ID" , REG_CONTROLLER_PROD_ID,
> 0},
> > + {"INTERRUPT STATUS" , REG_INTERRUPT_STATUS,
> 0},
> > + {"INTERRUPT ENABLE" , REG_INTERRUPT_ENABLE,
> 0},
> > + {"CONTROLLER STATUS" , REG_CONTROLLER_STATUS,
> 0},
> > + {"CONTROLLER ENABLE" , REG_CONTROLLER_ENABLE,
> 0},
> > + {"UIC ERR PHY ADAPTER LAYER" ,
> REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER, 0},
> > + {"UIC ERR DATA LINK LAYER" , REG_UIC_ERROR_CODE_DATA_LINK_LAYER,
> 0},
> > + {"UIC ERR NETWORK LATER" , REG_UIC_ERROR_CODE_NETWORK_LAYER,
> 0},
> > + {"UIC ERR TRANSPORT LAYER" , REG_UIC_ERROR_CODE_TRANSPORT_LAYER,
> 0},
> > + {"UIC ERR DME" , REG_UIC_ERROR_CODE_DME,
> 0},
> > + {"UTP TRANSF REQ INT AGG CNTRL" ,
> REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL, 0},
> > + {"UTP TRANSF REQ LIST BASE L" ,
> REG_UTP_TRANSFER_REQ_LIST_BASE_L, 0},
> > + {"UTP TRANSF REQ LIST BASE H" ,
> REG_UTP_TRANSFER_REQ_LIST_BASE_H, 0},
> > + {"UTP TRANSF REQ DOOR BELL" ,
> REG_UTP_TRANSFER_REQ_DOOR_BELL, 0},
> > + {"UTP TRANSF REQ LIST CLEAR" ,
> REG_UTP_TRANSFER_REQ_LIST_CLEAR, 0},
> > + {"UTP TRANSF REQ LIST RUN STOP" ,
> REG_UTP_TRANSFER_REQ_LIST_RUN_STOP, 0},
> > + {"UTP TASK REQ LIST BASE L" ,
> REG_UTP_TASK_REQ_LIST_BASE_L, 0},
> > + {"UTP TASK REQ LIST BASE H" ,
> REG_UTP_TASK_REQ_LIST_BASE_H, 0},
> > + {"UTP TASK REQ DOOR BELL" , REG_UTP_TASK_REQ_DOOR_BELL,
> 0},
> > + {"UTP TASK REQ LIST CLEAR" , REG_UTP_TASK_REQ_LIST_CLEAR,
> 0},
> > + {"UTP TASK REQ LIST RUN STOP" ,
> REG_UTP_TASK_REQ_LIST_RUN_STOP, 0},
> > + {"UIC COMMAND" , REG_UIC_COMMAND,
> 0},
> > + {"UIC COMMAND ARG1" , REG_UIC_COMMAND_ARG_1,
> 0},
> > + {"UIC COMMAND ARG2" , REG_UIC_COMMAND_ARG_2,
> 0},
> > + {"UIC COMMAND ARG3" , REG_UIC_COMMAND_ARG_3,
> 0},
> > +
> > + {},
> > +};
> > +
> > +/* Helper for UFS CAL interface */
> > +static inline int ufs_init_cal(struct exynos_ufs *ufs, int idx,
> > + struct platform_device *pdev)
> > +{
> > + return 0;
> > +}
> > +
> > +static inline int ufs_pre_link(struct exynos_ufs *ufs) {
> > + return 0;
> > +}
> > +
> > +static inline int ufs_post_link(struct exynos_ufs *ufs) {
> > + return 0;
> > +}
> > +
> > +static inline int ufs_pre_gear_change(struct exynos_ufs *ufs,
> > + struct uic_pwr_mode *pmd)
> > +{
> > + return 0;
> > +}
> > +
> > +static inline int ufs_post_gear_change(struct exynos_ufs *ufs) {
> > + return 0;
> > +}
> > +
> > +static inline int ufs_post_h8_enter(struct exynos_ufs *ufs) {
> > + return 0;
> > +}
> > +
> > +static inline int ufs_pre_h8_exit(struct exynos_ufs *ufs) {
> > + return 0;
> > +}
>
> What purpose has these helper fuctions?
>
> > +
> > +static inline void exynos_ufs_ctrl_phy_pwr(struct exynos_ufs *ufs,
> > +bool en) {
> > + int ret = 0;
> > +
> > + if (en)
> > + ret = regmap_update_bits(ufs->pmureg, ufs->cxt_iso.offset,
> > + ufs->cxt_iso.mask, ufs->cxt_iso.val);
> > + else
> > + ret = regmap_update_bits(ufs->pmureg, ufs->cxt_iso.offset,
> > + ufs->cxt_iso.mask, 0);
> > +
> > + if (ret)
> > + dev_err(ufs->dev, "Unable to update PHY ISO control\n"); }
> > +
> > +#ifndef __EXYNOS_UFS_VS_DEBUG__
>
> There is no defined "__EXYNOS_UFS_VS_DEBUG__". Remove it or include it
> into Kconfig.
>
> > +static void exynos_ufs_dump_std_sfr(struct ufs_hba *hba) {
> > + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> > + struct exynos_ufs_sfr_log* cfg = ufs->debug.std_sfr;
> > +
> > + dev_err(hba->dev, ": ----------------------------------------------
> ----- \n");
> > + dev_err(hba->dev, ": \t\tREGISTER DUMP\n");
> > + dev_err(hba->dev, ":
> > +--------------------------------------------------- \n");
> > +
> > + while(cfg) {
> > + if (!cfg->name)
> > + break;
> > + cfg->val = ufshcd_readl(hba, cfg->offset);
> > +
> > + /* Dump */
> > + dev_err(hba->dev, ": %s(0x%04x):\t\t\t\t0x%08x\n",
> > + cfg->name, cfg->offset, cfg->val);
> > +
> > + /* Next SFR */
> > + cfg++;
> > + }
> > +}
> > +#endif
> > +
> > +/*
> > + * Exynos debugging main function
> > + */
> > +static void exynos_ufs_dump_debug_info(struct ufs_hba *hba) { #ifdef
> > +__EXYNOS_UFS_VS_DEBUG__
>
> What is this? Can be remove this.
>
> > +#else
> > + exynos_ufs_dump_std_sfr(hba);
> > +#endif
> > +}
> > +
> > +static inline void exynos_ufs_set_hwacg_control(struct exynos_ufs
> > +*ufs, bool en) {
> > + u32 reg;
> > + if ((ufs->hw_rev != UFS_VER_0004) && (ufs->hw_rev != UFS_VER_0005))
> > + return;
> > +
> > + /*
> > + * default value 1->0 at KC. so,
> > + * need to set "1(disable HWACG)" during UFS init
> > + */
> > + reg = hci_readl(ufs, HCI_UFS_ACG_DISABLE);
> > + if (en)
> > + hci_writel(ufs, reg & (~HCI_UFS_ACG_DISABLE_EN),
> HCI_UFS_ACG_DISABLE);
> > + else
> > + hci_writel(ufs, reg | HCI_UFS_ACG_DISABLE_EN,
> HCI_UFS_ACG_DISABLE);
> > +
> > +}
> > +
> > +static inline void exynos_ufs_ctrl_auto_hci_clk(struct exynos_ufs
> > +*ufs, bool en) {
> > + u32 reg = hci_readl(ufs, HCI_FORCE_HCS);
> > +
> > + if (en)
> > + hci_writel(ufs, reg | HCI_CORECLK_STOP_EN, HCI_FORCE_HCS);
> > + else
> > + hci_writel(ufs, reg & ~HCI_CORECLK_STOP_EN, HCI_FORCE_HCS); }
> > +
> > +static inline void exynos_ufs_ctrl_clk(struct exynos_ufs *ufs, bool
> > +en) {
> > + u32 reg = hci_readl(ufs, HCI_FORCE_HCS);
> > +
> > + if (en)
> > + hci_writel(ufs, reg | CLK_STOP_CTRL_EN_ALL, HCI_FORCE_HCS);
> > + else
> > + hci_writel(ufs, reg & ~CLK_STOP_CTRL_EN_ALL,
> HCI_FORCE_HCS); }
> > +
> > +static inline void exynos_ufs_gate_clk(struct exynos_ufs *ufs, bool
> > +en) {
> > +
> > + u32 reg = hci_readl(ufs, HCI_CLKSTOP_CTRL);
> > +
> > + if (en)
> > + hci_writel(ufs, reg | CLK_STOP_ALL, HCI_CLKSTOP_CTRL);
> > + else
> > + hci_writel(ufs, reg & ~CLK_STOP_ALL, HCI_CLKSTOP_CTRL); }
> > +
> > +static inline void exynos_ufs_set_unipro_mclk(struct exynos_ufs *ufs)
> > +{
> > + ufs->mclk_rate = clk_get_rate(ufs->clk_unipro); }
> > +
> > +static inline void exynos_ufs_fit_aggr_timeout(struct exynos_ufs
> > +*ufs) {
> > + u32 cnt_val;
> > + u32 nVal;
>
> Don't use the upper character.
>
> > +
> > + /* IA_TICK_SEL : 1(1us_TO_CNT_VAL) */
> > + nVal = hci_readl(ufs, HCI_UFSHCI_V2P1_CTRL);
> > + nVal |= IA_TICK_SEL;
> > + hci_writel(ufs, nVal, HCI_UFSHCI_V2P1_CTRL);
> > +
> > + cnt_val = ufs->mclk_rate / 1000000 ;
> > + hci_writel(ufs, cnt_val & CNT_VAL_1US_MASK, HCI_1US_TO_CNT_VAL); }
> > +
> > +static void exynos_ufs_init_pmc_req(struct ufs_hba *hba,
> > + struct ufs_pa_layer_attr *pwr_max,
> > + struct ufs_pa_layer_attr *pwr_req)
> > +{
> > +
> > + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> > + struct uic_pwr_mode *req_pmd = &ufs->req_pmd_parm;
> > + struct uic_pwr_mode *act_pmd = &ufs->act_pmd_parm;
> > +
> > + /* update lane variable after link */
> > + ufs->num_rx_lanes = pwr_max->lane_rx;
> > + ufs->num_tx_lanes = pwr_max->lane_tx;
> > +
> > + pwr_req->gear_rx
> > + = act_pmd->gear= min_t(u8, pwr_max->gear_rx, req_pmd->gear);
> > + pwr_req->gear_tx
> > + = act_pmd->gear = min_t(u8, pwr_max->gear_tx, req_pmd->gear);
> > + pwr_req->lane_rx
> > + = act_pmd->lane = min_t(u8, pwr_max->lane_rx, req_pmd->lane);
> > + pwr_req->lane_tx
> > + = act_pmd->lane = min_t(u8, pwr_max->lane_tx, req_pmd->lane);
> > + pwr_req->pwr_rx = act_pmd->mode = req_pmd->mode;
> > + pwr_req->pwr_tx = act_pmd->mode = req_pmd->mode;
> > + pwr_req->hs_rate = act_pmd->hs_series = req_pmd->hs_series; }
> > +
> > +static void exynos_ufs_config_intr(struct exynos_ufs *ufs, u32 errs,
> > +u8 index) {
> > + switch(index) {
> > + case UNIP_PA_LYR:
> > + hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERROR_EN_PA_LAYER);
> > + break;
> > + case UNIP_DL_LYR:
> > + hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERROR_EN_DL_LAYER);
> > + break;
> > + case UNIP_N_LYR:
> > + hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERROR_EN_N_LAYER);
> > + break;
> > + case UNIP_T_LYR:
> > + hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERROR_EN_T_LAYER);
> > + break;
> > + case UNIP_DME_LYR:
> > + hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERROR_EN_DME_LAYER);
> > + break;
> > + }
> > +}
> > +
> > +static void exynos_ufs_dev_hw_reset(struct ufs_hba *hba) {
> > + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> > +
> > + /* bit[1] for resetn */
> f> + hci_writel(ufs, 0 << 0, HCI_GPIO_OUT);
>
> your comment is "bit[1] for resetn" ..but this bit conotrol is for bit[0].
> I don't want to use "0 << 0" and " 1 << 0".
>
> #define RESETN_HIGH/LOW whatever...
>
> > + udelay(5);
> > + hci_writel(ufs, 1 << 0, HCI_GPIO_OUT); }
> > +
> > +static void exynos_ufs_init_host(struct exynos_ufs *ufs) {
> > + u32 reg;
> > +
> > + /* internal clock control */
> > + exynos_ufs_ctrl_auto_hci_clk(ufs, false);
> > + exynos_ufs_set_unipro_mclk(ufs);
> > +
> > + /* period for interrupt aggregation */
> > + exynos_ufs_fit_aggr_timeout(ufs);
> > +
> > + /* misc HCI configurations */
> > + hci_writel(ufs, 0xA, HCI_DATA_REORDER);
>
> Don't use magin number. what is 0xA?
>
> > + hci_writel(ufs, PRDT_PREFECT_EN | PRDT_SET_SIZE(12),
> > + HCI_TXPRDT_ENTRY_SIZE);
> > + hci_writel(ufs, PRDT_SET_SIZE(12), HCI_RXPRDT_ENTRY_SIZE);
> > + hci_writel(ufs, 0xFFFFFFFF, HCI_UTRL_NEXUS_TYPE);
> > + hci_writel(ufs, 0xFFFFFFFF, HCI_UTMRL_NEXUS_TYPE);
>
> Ditto.
> > +
> > + reg = hci_readl(ufs, HCI_AXIDMA_RWDATA_BURST_LEN) &
> > + ~BURST_LEN(0);
> > + hci_writel(ufs, WLU_EN | BURST_LEN(3),
> > + HCI_AXIDMA_RWDATA_BURST_LEN);
> > +
> > + /*
> > + * Enable HWAGC control by IOP
> > + *
> > + * default value 1->0 at KC.
> > + * always "0"(controlled by UFS_ACG_DISABLE)
> > + */
> > + reg = hci_readl(ufs, HCI_IOP_ACG_DISABLE);
> > + hci_writel(ufs, reg & (~HCI_IOP_ACG_DISABLE_EN),
> > +HCI_IOP_ACG_DISABLE); }
> > +
> > +static int exynos_ufs_init_system(struct exynos_ufs *ufs) {
> > + struct device *dev = ufs->dev;
> > + int ret = 0;
> > + bool is_io_coherency;
> > + bool is_dma_coherent;
> > +
> > + /* PHY isolation bypass */
> > + exynos_ufs_ctrl_phy_pwr(ufs, true);
> > +
> > + /* IO cohernecy */
> > + is_io_coherency = !IS_ERR(ufs->sysreg);
> > + is_dma_coherent = !!of_find_property(dev->of_node,
> > + "dma-coherent", NULL);
> > +
> > + if (is_io_coherency != is_dma_coherent)
> > + BUG();
>
> BUG()?
>
> > +
> > + if (!is_io_coherency)
> > + dev_err(dev, "Not configured to use IO coherency\n");
> > + else
> > + ret = regmap_update_bits(ufs->sysreg, ufs-
> >cxt_coherency.offset,
> > + ufs->cxt_coherency.mask, ufs->cxt_coherency.val);
> > +
> > + return ret;
> > +}
> > +
> > +static int exynos_ufs_get_clks(struct ufs_hba *hba) {
> > + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> > + struct list_head *head = &hba->clk_list_head;
> > + struct ufs_clk_info *clki;
> > + int i = 0;
> > +
> > + ufs_host_backup[ufs_host_index++] = ufs;
> > + ufs->debug.std_sfr = ufs_log_std_sfr;
> > +
> > + if (!head || list_empty(head))
> > + goto out;
> > +
> > + list_for_each_entry(clki, head, list) {
> > + /*
> > + * get clock with an order listed in device tree
> > + */
> > + if (i == 0)
> > + ufs->clk_hci = clki->clk;
> > + else if (i == 1)
> > + ufs->clk_unipro = clki->clk;
> > +
> > + i++;
> > + }
> > +out:
> > + if (!ufs->clk_hci || !ufs->clk_unipro)
> > + return -EINVAL;
> > +
> > + return 0;
> > +}
> > +
> > +static void exynos_ufs_set_features(struct ufs_hba *hba, u32 hw_rev)
> > +{
> > + /* caps */
> > + hba->caps = UFSHCD_CAP_CLK_GATING |
> > + UFSHCD_CAP_HIBERN8_WITH_CLK_GATING |
> > + UFSHCD_CAP_INTR_AGGR;
> > +
> > + /* quirks of common driver */
> > + hba->quirks = UFSHCD_QUIRK_PRDT_BYTE_GRAN;
> > +
> > + /* quirks of exynos-specific driver */
>
> Remove unused comment.
>
> > +}
> > +
> > +/*
> > + * Exynos-specific callback functions
> > + *
> > + * init | Pure SW init & system-related init
> > + * host_reset | Host SW reset & init
> > + * ...
> > + *
> > + * Initializations for software, host controller and system
> > + * should be contained only in ->host_reset() as possible.
> > + */
> > +
> > +static int exynos_ufs_init(struct ufs_hba *hba) {
> > + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> > + int ret;
> > +
> > + /* set features, such as caps or quirks */
> > + exynos_ufs_set_features(hba, ufs->hw_rev);
> > +
> > + /* get some clock sources and debug infomation structures */
> > + ret = exynos_ufs_get_clks(hba);
> > + if (ret)
> > + return ret;
> > +
> > + /* system init */
> > + ret = exynos_ufs_init_system(ufs);
> > + if (ret)
> > + return ret;
> > +
> > + ufs->misc_flags = EXYNOS_UFS_MISC_TOGGLE_LOG;
> > +
> > + return 0;
> > +}
> > +
> > +static void exynos_ufs_host_reset(struct ufs_hba *hba) {
> > + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> > + unsigned long timeout = jiffies + msecs_to_jiffies(1);
> > +
> > + exynos_ufs_ctrl_auto_hci_clk(ufs, false);
> > +
> > + hci_writel(ufs, UFS_SW_RST_MASK, HCI_SW_RST);
> > +
> > + do {
> > + if (!(hci_readl(ufs, HCI_SW_RST) & UFS_SW_RST_MASK))
> > + goto success;
>
> Need to "goto" statement? Just can be located the below code.
> if () {
> exynos_ufs_init_host();
> exynos_ufs_dev_hw_reset();
> return;
> }
>
> > + } while (time_before(jiffies, timeout));
> > +
> > + dev_err(ufs->dev, "timeout host sw-reset\n");
> > +
> > + goto out;
>
> Not need "goto out", instead "return" at here.
>
> > +
> > +success:
> > + /* host init */
> > + exynos_ufs_init_host(ufs);
> > +
> > + /* device reset */
> > + exynos_ufs_dev_hw_reset(hba);
> > +out:
> > + return;
> > +}
> > +
> > +static inline void exynos_ufs_dev_reset_ctrl(struct exynos_ufs *ufs,
> > +bool en) {
> > +
> > + if (en)
> > + hci_writel(ufs, 1 << 0, HCI_GPIO_OUT);
> > + else
> > + hci_writel(ufs, 0 << 0, HCI_GPIO_OUT);
>
> I don't know why this function is need. There is no call anywhere with
> "true".
>
> > +}
> > +
> > +static int exynos_ufs_setup_clocks(struct ufs_hba *hba, bool on,
> > + enum ufs_notify_change_status status) {
> > + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> > + int ret = 0;
> > +
> > + if (status == PRE_CHANGE) {
> > + if (on) {
> > + /*
> > + * Now all used blocks would not be turned off in a
> host.
> > + */
> > + exynos_ufs_ctrl_auto_hci_clk(ufs, false);
> > + exynos_ufs_gate_clk(ufs, false);
> > +
> > + /* HWAGC disable */
> > + exynos_ufs_set_hwacg_control(ufs, false);
> > + } else {
> > + ret = ufs_post_h8_enter(ufs);
> > + }
> > + } else {
> > + if (on) {
> > + ret = ufs_pre_h8_exit(ufs);
> > + } else {
> > + /*
> > + * Now all used blocks would be turned off in a host.
> > + */
> > + exynos_ufs_ctrl_auto_hci_clk(ufs, true);
> > +
> > + /* HWAGC enable */
> > + exynos_ufs_set_hwacg_control(ufs, true);
> > + }
> > + }
> > +
> > + return ret;
> > +}
> > +
> > +static int exynos_ufs_link_startup_notify(struct ufs_hba *hba,
> > + enum ufs_notify_change_status status) {
> > + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> > + int ret = 0;
> > +
> > + switch (status) {
> > + case PRE_CHANGE:
> > + /* refer to hba */
> > + ufs->hba = hba;
> > +
> > + /* hci */
> > + exynos_ufs_config_intr(ufs, DFES_DEF_DL_ERRS, UNIP_DL_LYR);
> > + exynos_ufs_config_intr(ufs, DFES_DEF_N_ERRS, UNIP_N_LYR);
> > + exynos_ufs_config_intr(ufs, DFES_DEF_T_ERRS, UNIP_T_LYR);
> > +
> > + exynos_ufs_ctrl_clk(ufs, true);
> > + exynos_ufs_gate_clk(ufs, false);
> > + exynos_ufs_set_hwacg_control(ufs, false);
> > +
> > + if (ufs->num_rx_lanes == 0 || ufs->num_tx_lanes == 0) {
> > + ufshcd_dme_get(hba, UIC_ARG_MIB(PA_AVAILRXDATALANES),
> > + &ufs->num_rx_lanes);
> > + ufshcd_dme_get(hba, UIC_ARG_MIB(PA_AVAILTXDATALANES),
> > + &ufs->num_tx_lanes);
> > + WARN(ufs->num_rx_lanes != ufs->num_tx_lanes,
> > + "available data lane is not equal(rx:%d,
> tx:%d)\n",
> > + ufs->num_rx_lanes, ufs->num_tx_lanes);
> > + }
> > +
> > + ufs->mclk_rate = clk_get_rate(ufs->clk_unipro);
> > +
> > + ret = ufs_pre_link(ufs);
> > + break;
> > + case POST_CHANGE:
> > + /* UIC configuration table after link startup */
> > + ret = ufs_post_link(ufs);
> > + break;
> > + default:
> > + break;
> > + }
> > +
> > + return ret;
> > +}
> > +
> > +static int exynos_ufs_pwr_change_notify(struct ufs_hba *hba,
> > + enum ufs_notify_change_status status,
> > + struct ufs_pa_layer_attr *pwr_max,
> > + struct ufs_pa_layer_attr *pwr_req) {
> > + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> > + struct uic_pwr_mode *act_pmd = &ufs->act_pmd_parm;
> > + int ret = 0;
> > +
> > + switch (status) {
> > + case PRE_CHANGE:
> > +
> > + /* Set PMC parameters to be requested */
> > + exynos_ufs_init_pmc_req(hba, pwr_max, pwr_req);
> > +
> > + /* UIC configuration table before power mode change */
> > + ret = ufs_pre_gear_change(ufs, act_pmd);
> > +
> > + break;
> > + case POST_CHANGE:
> > + /* UIC configuration table after power mode change */
> > + ret = ufs_post_gear_change(ufs);
> > +
> > + dev_info(ufs->dev,
> > + "Power mode change(%d): M(%d)G(%d)L(%d)HS-
> series(%d)\n",
> > + ret, act_pmd->mode, act_pmd->gear,
> > + act_pmd->lane, act_pmd->hs_series);
>
> Fix the indent
>
> > + break;
> > + default:
> > + break;
> > + }
> > +
> > + return ret;
> > +}
> > +
> > +static void exynos_ufs_set_nexus_t_xfer_req(struct ufs_hba *hba,
> > + int tag, bool op)
> > +{
> > + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> > + u32 type;
> > +
> > + type = hci_readl(ufs, HCI_UTRL_NEXUS_TYPE);
> > +
> > + if (op)
> > + type |= (1 << tag);
> > + else
> > + type &= ~(1 << tag);
> > +
> > + hci_writel(ufs, type, HCI_UTRL_NEXUS_TYPE); }
> > +
> > +static void exynos_ufs_set_nexus_t_task_mgmt(struct ufs_hba *hba, int
> > +tag, u8 tm_func) {
> > + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> > + u32 type;
> > +
> > + type = hci_readl(ufs, HCI_UTMRL_NEXUS_TYPE);
> > +
> > + switch (tm_func) {
> > + case UFS_ABORT_TASK:
> > + case UFS_QUERY_TASK:
> > + type |= (1 << tag);
> > + break;
> > + case UFS_ABORT_TASK_SET:
> > + case UFS_CLEAR_TASK_SET:
> > + case UFS_LOGICAL_RESET:
> > + case UFS_QUERY_TASK_SET:
> > + type &= ~(1 << tag);
> > + break;
> > + }
> > +
> > + hci_writel(ufs, type, HCI_UTMRL_NEXUS_TYPE); }
> > +
> > +static int __exynos_ufs_suspend(struct ufs_hba *hba, enum ufs_pm_op
> > +pm_op) {
> > + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> > +
> > + exynos_ufs_dev_reset_ctrl(ufs, false);
> > +
> > + exynos_ufs_ctrl_phy_pwr(ufs, false);
> > +
> > + return 0;
> > +}
> > +
> > +static int __exynos_ufs_resume(struct ufs_hba *hba, enum ufs_pm_op
> > +pm_op) {
> > + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> > + int ret = 0;
> > +
> > + exynos_ufs_ctrl_phy_pwr(ufs, true);
> > +
> > + /* system init */
> > + ret = exynos_ufs_init_system(ufs);
> > + if (ret)
> > + return ret;
> > +
> > + if (ufshcd_is_clkgating_allowed(hba))
> > + clk_prepare_enable(ufs->clk_hci);
> > + exynos_ufs_ctrl_auto_hci_clk(ufs, false);
> > +
> > + if (ufshcd_is_clkgating_allowed(hba))
> > + clk_disable_unprepare(ufs->clk_hci);
> > +
> > + return 0;
> > +}
> > +
> > +static struct ufs_hba_variant_ops exynos_ufs_ops = {
> > + .init = exynos_ufs_init,
> > + .host_reset = exynos_ufs_host_reset,
> > + .setup_clocks = exynos_ufs_setup_clocks,
> > + .link_startup_notify = exynos_ufs_link_startup_notify,
> > + .pwr_change_notify = exynos_ufs_pwr_change_notify,
> > + .setup_xfer_req = exynos_ufs_set_nexus_t_xfer_req,
> > + .setup_task_mgmt = exynos_ufs_set_nexus_t_task_mgmt,
> > + .dbg_register_dump = exynos_ufs_dump_debug_info,
> > + .suspend = __exynos_ufs_suspend,
> > + .resume = __exynos_ufs_resume,
> > +};
> > +
> > +static int exynos_ufs_populate_dt_phy(struct device *dev, struct
> > +exynos_ufs *ufs) {
> > + struct device_node *ufs_phy;
> > + struct exynos_ufs_phy *phy = &ufs->phy;
> > + struct resource io_res;
> > + int ret;
> > +
> > + ufs_phy = of_get_child_by_name(dev->of_node, "ufs-phy");
> > + if (!ufs_phy) {
> > + dev_err(dev, "failed to get ufs-phy node\n");
> > + return -ENODEV;
> > + }
> > +
> > + ret = of_address_to_resource(ufs_phy, 0, &io_res);
> > + if (ret) {
> > + dev_err(dev, "failed to get i/o address phy pma\n");
> > + goto err_0;
> > + }
> > +
> > + phy->reg_pma = devm_ioremap_resource(dev, &io_res);
> > + if (!phy->reg_pma) {
> > + dev_err(dev, "failed to ioremap for phy pma\n");
> > + ret = -ENOMEM;
> > + goto err_0;
> > + }
> > +
> > +err_0:
> > + of_node_put(ufs_phy);
> > +
> > + return ret;
> > +}
> > +
> > +/*
> > + * This function is to define offset, mask and shift to access
> somewhere.
> > + */
> > +static int exynos_ufs_set_context_for_access(struct device *dev,
> > + const char *name, struct exynos_access_cxt *cxt)
> {
> > + struct device_node *np;
> > + int ret;
> > +
> > + np = of_get_child_by_name(dev->of_node, name);
> > + if (!np) {
> > + dev_err(dev, "failed to get node(%s)\n", name);
> > + return 1;
>
> Don't use the meaningless value.
>
> > + }
> > +
> > + ret = of_property_read_u32(np, "offset", &cxt->offset);
> > + if (IS_ERR(&cxt->offset)) {
> > + dev_err(dev, "failed to set cxt(%s) offset\n", name);
> > + return cxt->offset;
> > + }
> > +
> > + ret = of_property_read_u32(np, "mask", &cxt->mask);
> > + if (IS_ERR(&cxt->mask)) {
> > + dev_err(dev, "failed to set cxt(%s) mask\n", name);
> > + return cxt->mask;
> > + }
> > +
> > + ret = of_property_read_u32(np, "val", &cxt->val);
> > + if (IS_ERR(&cxt->val)) {
> > + dev_err(dev, "failed to set cxt(%s) val\n", name);
> > + return cxt->val;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int exynos_ufs_populate_dt_system(struct device *dev, struct
> > +exynos_ufs *ufs) {
> > + int ret;
> > +
> > + /* regmap pmureg */
> > + ufs->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
> > + "samsung,pmu-phandle");
> > + if (IS_ERR(ufs->pmureg)) {
> > + /*
> > + * phy isolation should be available.
> > + * so this case need to be failed.
> > + */
> > + dev_err(dev, "pmu regmap lookup failed.\n");
> > + return PTR_ERR(ufs->pmureg);
> > + }
> > +
> > + /* Set access context for phy isolation bypass */
> > + ret = exynos_ufs_set_context_for_access(dev, "ufs-phy-iso",
> > + &ufs->cxt_iso);
> > + if (ret == 1) {
> > + /* no device node, default */
> > + ufs->cxt_iso.offset = 0x0724;
> > + ufs->cxt_iso.mask = 0x1;
> > + ufs->cxt_iso.val = 0x1;
> > + ret = 0;
> > + }
> > +
> > + /* regmap sysreg */
> > + ufs->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
> > + "samsung,sysreg-fsys-phandle");
> > + if (IS_ERR(ufs->sysreg)) {
> > + /*
> > + * Currently, ufs driver gets sysreg for io coherency.
> > + * Some architecture might not support this feature.
> > + * So the device node might not exist.
> > + */
> > + dev_err(dev, "sysreg regmap lookup failed.\n");
> > + return 0;
>
> return 0?
>
> > + }
> > +
> > + /* Set access context for io coherency */
> > + ret = exynos_ufs_set_context_for_access(dev, "ufs-dma-coherency",
> > + &ufs->cxt_coherency);
> > + if (ret == 1) {
> > + /* no device node, default */
> > + ufs->cxt_coherency.offset = 0x0700;
> > + ufs->cxt_coherency.mask = 0x300; /* bit 8,9 */
> > + ufs->cxt_coherency.val = 0x3;
> > + ret = 0;
> > + }
> > +
> > + return ret;
> > +}
> > +
> > +static int exynos_ufs_get_pwr_mode(struct device_node *np,
> > + struct exynos_ufs *ufs)
> > +{
> > + struct uic_pwr_mode *pmd = &ufs->req_pmd_parm;
> > +
> > + pmd->mode = FAST_MODE;
> > +
> > + if (of_property_read_u8(np, "ufs,pmd-attr-lane", &pmd->lane))
> > + pmd->lane = 1;
> > +
> > + if (of_property_read_u8(np, "ufs,pmd-attr-gear", &pmd->gear))
> > + pmd->gear = 1;
> > +
> > + pmd->hs_series = PA_HS_MODE_B;
> > +
> > + return 0;
> > +}
> > +
> > +static int exynos_ufs_populate_dt(struct device *dev, struct
> > +exynos_ufs *ufs) {
> > + struct device_node *np = dev->of_node;
> > + int ret;
> > +
> > + /* Get exynos-specific version for featuring */
> > + if (of_property_read_u32(np, "hw-rev", &ufs->hw_rev))
> > + ufs->hw_rev = UFS_VER_0004;
> > +
> > + ret = exynos_ufs_populate_dt_phy(dev, ufs);
> > + if (ret) {
> > + dev_err(dev, "failed to populate dt-phy\n");
> > + goto out;
> > + }
> > +
> > + ret = exynos_ufs_populate_dt_system(dev, ufs);
> > + if (ret) {
> > + dev_err(dev, "failed to populate dt-pmu\n");
> > + goto out;
> > + }
> > +
> > + exynos_ufs_get_pwr_mode(np, ufs);
> > +
> > +out:
> > + return ret;
> > +}
> > +
> > +static u64 exynos_ufs_dma_mask = DMA_BIT_MASK(32);
> > +
> > +static int exynos_ufs_probe(struct platform_device *pdev) {
> > + struct device *dev = &pdev->dev;
> > + struct exynos_ufs *ufs;
> > + struct resource *res;
> > + int ret;
> > +
> > + ufs = devm_kzalloc(dev, sizeof(*ufs), GFP_KERNEL);
> > + if (!ufs) {
> > + dev_err(dev, "cannot allocate mem for exynos-ufs\n");
> > + return -ENOMEM;
> > + }
> > +
> > + /* exynos-specific hci */
> > + res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> > + ufs->reg_hci = devm_ioremap_resource(dev, res);
> > + if (!ufs->reg_hci) {
> > + dev_err(dev, "cannot ioremap for hci vendor register\n");
> > + return -ENOMEM;
> > + }
> > +
> > + /* unipro */
> > + res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> > + ufs->reg_unipro = devm_ioremap_resource(dev, res);
> > + if (!ufs->reg_unipro) {
> > + dev_err(dev, "cannot ioremap for unipro register\n");
> > + return -ENOMEM;
> > + }
> > +
> > + /* This must be before calling exynos_ufs_populate_dt */
> > + ret = ufs_init_cal(ufs, ufs_host_index, pdev);
> > + if (ret)
> > + return ret;
> > +
> > + ret = exynos_ufs_populate_dt(dev, ufs);
> > + if (ret) {
> > + dev_err(dev, "failed to get dt info.\n");
> > + return ret;
> > + }
> > +
> > + ufs->dev = dev;
> > + dev->platform_data = ufs;
> > + dev->dma_mask = &exynos_ufs_dma_mask;
> > +
> > + ret = ufshcd_pltfrm_init(pdev, &exynos_ufs_ops);
> > +
> > + return ret;
> > +}
> > +
> > +static int exynos_ufs_remove(struct platform_device *pdev) {
> > + struct ufs_hba *hba = platform_get_drvdata(pdev);
> > + struct exynos_ufs *ufs = dev_get_platdata(&pdev->dev);
> > +
> > + ufshcd_remove(hba);
> > +
> > + ufs->misc_flags = EXYNOS_UFS_MISC_TOGGLE_LOG;
> > +
> > + exynos_ufs_ctrl_phy_pwr(ufs, false);
> > +
> > + return 0;
> > +}
> > +
> > +#ifdef CONFIG_PM_SLEEP
> > +static int exynos_ufs_suspend(struct device *dev) {
> > + struct ufs_hba *hba = dev_get_drvdata(dev);
> > +
> > + return ufshcd_system_suspend(hba);
> > +}
> > +
> > +static int exynos_ufs_resume(struct device *dev) {
> > + struct ufs_hba *hba = dev_get_drvdata(dev);
> > +
> > + return ufshcd_system_resume(hba);
> > +}
> > +#else
> > +#define exynos_ufs_suspend NULL
> > +#define exynos_ufs_resume NULL
> > +#endif /* CONFIG_PM_SLEEP */
> > +
> > +#ifdef CONFIG_PM_RUNTIME
> > +static int exynos_ufs_runtime_suspend(struct device *dev) {
> > + return ufshcd_system_suspend(dev_get_drvdata(dev));
> > +}
> > +
> > +static int exynos_ufs_runtime_resume(struct device *dev) {
> > + return ufshcd_system_resume(dev_get_drvdata(dev));
> > +}
> > +
> > +static int exynos_ufs_runtime_idle(struct device *dev) {
> > + return ufshcd_runtime_idle(dev_get_drvdata(dev));
> > +}
> > +
> > +#else
> > +#define exynos_ufs_runtime_suspend NULL
> > +#define exynos_ufs_runtime_resume NULL
> > +#define exynos_ufs_runtime_idle NULL
> > +#endif /* CONFIG_PM_RUNTIME */
> > +
> > +static void exynos_ufs_shutdown(struct platform_device *pdev) {
> > + ufshcd_shutdown((struct ufs_hba *)platform_get_drvdata(pdev)); }
> > +
> > +static const struct dev_pm_ops exynos_ufs_dev_pm_ops = {
> > + .suspend = exynos_ufs_suspend,
> > + .resume = exynos_ufs_resume,
> > + .runtime_suspend = exynos_ufs_runtime_suspend,
> > + .runtime_resume = exynos_ufs_runtime_resume,
> > + .runtime_idle = exynos_ufs_runtime_idle,
> > +};
> > +
> > +static const struct of_device_id exynos_ufs_match[] = {
> > + { .compatible = "samsung,exynos-ufs", },
> > + {},
> > +};
> > +MODULE_DEVICE_TABLE(of, exynos_ufs_match);
> > +
> > +static struct platform_driver exynos_ufs_driver = {
> > + .driver = {
> > + .name = "exynos-ufs",
> > + .owner = THIS_MODULE,
> > + .pm = &exynos_ufs_dev_pm_ops,
> > + .of_match_table = exynos_ufs_match,
> > + .suppress_bind_attrs = true,
> > + },
> > + .probe = exynos_ufs_probe,
> > + .remove = exynos_ufs_remove,
> > + .shutdown = exynos_ufs_shutdown,
> > +};
> > +
> > +module_platform_driver(exynos_ufs_driver);
> > +MODULE_DESCRIPTION("Exynos Specific UFSHCI driver");
> > +MODULE_AUTHOR("Seungwon Jeon <tgih.jun@samsung.com>");
> > +MODULE_AUTHOR("Kiwoong Kim <kwmad.kim@samsung.com>");
> > +MODULE_LICENSE("GPL");
> > diff --git a/drivers/scsi/ufs/ufs-exynos.h
> > b/drivers/scsi/ufs/ufs-exynos.h new file mode 100644 index
> > 000000000000..0480fc4a8931
> > --- /dev/null
> > +++ b/drivers/scsi/ufs/ufs-exynos.h
> > @@ -0,0 +1,351 @@
> > +/*
> > + * UFS Host Controller driver for Exynos specific extensions
> > + *
> > + * Copyright (C) 2013-2014 Samsung Electronics Co., Ltd.
> > + *
> > + * This program is free software; you can redistribute it and/or
> > +modify
> > + * it under the terms of the GNU General Public License as published
> > +by
> > + * the Free Software Foundation; either version 2 of the License, or
> > + * (at your option) any later version.
> > + */
> > +
> > +#ifndef _UFS_EXYNOS_H_
> > +#define _UFS_EXYNOS_H_
> > +
> > +#define UFS_VER_0004 4
> > +#define UFS_VER_0005 5
> > +
> > +/*
> > + * Exynos's Vendor specific registers for UFSHCI */
> > +#define HCI_TXPRDT_ENTRY_SIZE 0x00
> > +#define HCI_RXPRDT_ENTRY_SIZE 0x04
> > +#define HCI_TO_CNT_DIV_VAL 0x08
> > +#define HCI_1US_TO_CNT_VAL 0x0C
> > + #define CNT_VAL_1US_MASK 0x3ff
> > +#define HCI_INVALID_UPIU_CTRL 0x10
> > +#define HCI_INVALID_UPIU_BADDR 0x14
> > +#define HCI_INVALID_UPIU_UBADDR 0x18
> > +#define HCI_INVALID_UTMR_OFFSET_ADDR 0x1C
> > +#define HCI_INVALID_UTR_OFFSET_ADDR 0x20
> > +#define HCI_INVALID_DIN_OFFSET_ADDR 0x24
> > +#define HCI_VENDOR_SPECIFIC_IS 0x38
> > +#define HCI_VENDOR_SPECIFIC_IE 0x3C
> > +#define HCI_UTRL_NEXUS_TYPE 0x40
> > +#define HCI_UTMRL_NEXUS_TYPE 0x44
> > +#define HCI_E2EFC_CTRL 0x48
> > +#define HCI_SW_RST 0x50
> > + #define UFS_LINK_SW_RST (1 << 0)
> > + #define UFS_UNIPRO_SW_RST (1 << 1)
> > + #define UFS_SW_RST_MASK (UFS_UNIPRO_SW_RST | UFS_LINK_SW_RST)
> > +#define HCI_LINK_VERSION 0x54
> > +#define HCI_IDLE_TIMER_CONFIG 0x58
> > +#define HCI_RX_UPIU_MATCH_ERROR_CODE 0x5C
> > +#define HCI_DATA_REORDER 0x60
> > +#define HCI_MAX_DOUT_DATA_SIZE 0x64
> > +#define HCI_UNIPRO_APB_CLK_CTRL 0x68
> > +#define HCI_AXIDMA_RWDATA_BURST_LEN 0x6C
> > + #define BURST_LEN(x) ((x) << 27 | (x))
> > + #define WLU_EN (1 << 31)
>
> Use BIT(31)
>
> > + #define AXIDMA_RWDATA_BURST_LEN (0xF)
> > +#define HCI_GPIO_OUT 0x70
> > +#define HCI_WRITE_DMA_CTRL 0x74
> > +#define HCI_ERROR_EN_PA_LAYER 0x78
> > +#define HCI_ERROR_EN_DL_LAYER 0x7C
> > +#define HCI_ERROR_EN_N_LAYER 0x80
> > +#define HCI_ERROR_EN_T_LAYER 0x84
> > +#define HCI_ERROR_EN_DME_LAYER 0x88
> > +#define HCI_UFSHCI_V2P1_CTRL 0X8C
> > +#define IA_TICK_SEL BIT(16)
> > +#define HCI_REQ_HOLD_EN 0xAC
> > +
> > +#define HCI_CLKSTOP_CTRL 0xB0
> > + #define REFCLKOUT_STOP BIT(4)
> > + #define MPHY_APBCLK_STOP BIT(3)
> > + #define REFCLK_STOP BIT(2)
> > + #define UNIPRO_MCLK_STOP BIT(1)
> > + #define UNIPRO_PCLK_STOP BIT(0)
> > + #define CLK_STOP_ALL (REFCLKOUT_STOP |\
> > + REFCLK_STOP |\
> > + UNIPRO_MCLK_STOP |\
> > + UNIPRO_PCLK_STOP)
> > +
> > +#define HCI_FORCE_HCS 0xB4
> > + #define REFCLKOUT_STOP_EN BIT(11)
> > + #define MPHY_APBCLK_STOP_EN BIT(10)
> > + #define UFSP_DRCG_EN BIT(8)
> > + #define REFCLK_STOP_EN BIT(7)
> > + #define UNIPRO_PCLK_STOP_EN BIT(6)
> > + #define UNIPRO_MCLK_STOP_EN BIT(5)
> > + #define HCI_CORECLK_STOP_EN BIT(4)
> > + #define CLK_STOP_CTRL_EN_ALL (UFSP_DRCG_EN |\
> > + MPHY_APBCLK_STOP_EN |\
> > + REFCLKOUT_STOP_EN |\
> > + REFCLK_STOP_EN |\
> > + UNIPRO_PCLK_STOP_EN |\
> > + UNIPRO_MCLK_STOP_EN)
> > +
> > +#define HCI_FSM_MONITOR 0xC0
> > +#define HCI_PRDT_HIT_RATIO 0xC4
> > +#define HCI_DMA0_MONITOR_STATE 0xC8
> > +#define HCI_DMA0_MONITOR_CNT 0xCC
> > +#define HCI_DMA1_MONITOR_STATE 0xD0
> > +#define HCI_DMA1_MONITOR_CNT 0xD4
> > +
> > +#define HCI_UFS_AXI_DMA_IF_CTRL 0xF8
> > +#define HCI_UFS_ACG_DISABLE 0xFC
> > + #define HCI_UFS_ACG_DISABLE_EN BIT(0)
> > +#define HCI_IOP_ACG_DISABLE 0x100
> > + #define HCI_IOP_ACG_DISABLE_EN BIT(0)
> > +#define HCI_MPHY_REFCLK_SEL 0x108
> > + #define MPHY_REFCLK_SEL BIT(0)
> > +
> > +/* Device fatal error */
> > +#define DFES_ERR_EN BIT(31)
> > +#define DFES_DEF_DL_ERRS (UIC_DATA_LINK_LAYER_ERROR_RX_BUF_OF |\
> > + UIC_DATA_LINK_LAYER_ERROR_PA_INIT)
> > +#define DFES_DEF_N_ERRS (UIC_NETWORK_UNSUPPORTED_HEADER_TYPE |\
> > + UIC_NETWORK_BAD_DEVICEID_ENC |\
> > + UIC_NETWORK_LHDR_TRAP_PACKET_DROPPING)
> > +#define DFES_DEF_T_ERRS (UIC_TRANSPORT_UNSUPPORTED_HEADER_TYPE
> |\
> > + UIC_TRANSPORT_UNKNOWN_CPORTID |\
> > + UIC_TRANSPORT_NO_CONNECTION_RX |\
> > + UIC_TRANSPORT_BAD_TC)
> > +
> > +/* TXPRDT defines */
> > +#define PRDT_PREFECT_EN BIT(31)
> > +#define PRDT_SET_SIZE(x) ((x) & 0x1F)
> > +
> > +enum {
> > + UNIP_PA_LYR = 0,
> > + UNIP_DL_LYR,
> > + UNIP_N_LYR,
> > + UNIP_T_LYR,
> > + UNIP_DME_LYR,
> > +};
> > +
> > +/*
> > + * UNIPRO registers
> > + */
> > +#define UNIP_COMP_VERSION 0x000
> > +#define UNIP_COMP_INFO 0x004
> > +#define UNIP_COMP_RESET 0x010
> > +
> > +#define UNIP_DME_POWERON_REQ 0x7800
> > +#define UNIP_DME_POWERON_CNF_RESULT 0x7804
> > +#define UNIP_DME_POWEROFF_REQ 0x7810
> > +#define UNIP_DME_POWEROFF_CNF_RESULT 0x7814
> > +#define UNIP_DME_RESET_REQ 0x7820
> > +#define UNIP_DME_RESET_REQ_LEVEL 0x7824
> > +#define UNIP_DME_ENABLE_REQ 0x7830
> > +#define UNIP_DME_ENABLE_CNF_RESULT 0x7834
> > +#define UNIP_DME_ENDPOINTRESET_REQ 0x7840
> > +#define UNIP_DME_ENDPOINTRESET_CNF_RESULT 0x7844
> > +#define UNIP_DME_LINKSTARTUP_REQ 0x7850
> > +#define UNIP_DME_LINKSTARTUP_CNF_RESULT 0x7854
> > +#define UNIP_DME_HIBERN8_ENTER_REQ 0x7860
> > +#define UNIP_DME_HIBERN8_ENTER_CNF_RESULT 0x7864
> > +#define UNIP_DME_HIBERN8_ENTER_IND_RESULT 0x7868
> > +#define UNIP_DME_HIBERN8_EXIT_REQ 0x7870
> > +#define UNIP_DME_HIBERN8_EXIT_CNF_RESULT 0x7874
> > +#define UNIP_DME_HIBERN8_EXIT_IND_RESULT 0x7878
> > +#define UNIP_DME_PWR_REQ 0x7880
> > +#define UNIP_DME_PWR_REQ_POWERMODE 0x7884
> > +#define UNIP_DME_PWR_REQ_LOCALL2TIMER0 0x7888
> > +#define UNIP_DME_PWR_REQ_LOCALL2TIMER1 0x788C
> > +#define UNIP_DME_PWR_REQ_LOCALL2TIMER2 0x7890
> > +#define UNIP_DME_PWR_REQ_REMOTEL2TIMER0 0x78B8
> > +#define UNIP_DME_PWR_REQ_REMOTEL2TIMER1 0x78BC
> > +#define UNIP_DME_PWR_REQ_REMOTEL2TIMER2 0x78C0
> > +#define UNIP_DME_PWR_CNF_RESULT 0x78E8
> > +#define UNIP_DME_PWR_IND_RESULT 0x78EC
> > +#define UNIP_DME_TEST_MODE_REQ 0x7900
> > +#define UNIP_DME_TEST_MODE_CNF_RESULT 0x7904
> > +
> > +#define UNIP_DME_ERROR_IND_LAYER 0x0C0
> > +#define UNIP_DME_ERROR_IND_ERRCODE 0x0C4
> > +#define UNIP_DME_PACP_CNFBIT 0x0C8
> > +#define UNIP_DME_DL_FRAME_IND 0x0D0
> > +#define UNIP_DME_INTR_STATUS 0x0E0
> > +#define UNIP_DME_INTR_ENABLE 0x0E4
> > +
> > +#define UNIP_DME_GETSET_CONTROL 0x7A00
> > +#define UNIP_DME_GETSET_ADDR 0x7A04
> > +#define UNIP_DME_GETSET_WDATA 0x7A08
> > +#define UNIP_DME_GETSET_RDATA 0x7A0C
> > +#define UNIP_DME_GETSET_RESULT 0x7A10
> > +#define UNIP_DME_PEER_GETSET_CONTROL 0x7A20
> > +#define UNIP_DME_PEER_GETSET_ADDR 0x7A24
> > +#define UNIP_DME_PEER_GETSET_WDATA 0x7A28
> > +#define UNIP_DME_PEER_GETSET_RDATA 0x7A2C
> > +#define UNIP_DME_PEER_GETSET_RESULT 0x7A30
> > +
> > +#define UNIP_DME_INTR_STATUS_LSB 0x7B00
> > +#define UNIP_DME_INTR_STATUS_MSB 0x7B04
> > +#define UNIP_DME_INTR_ERROR_CODE 0x7B20
> > +#define UNIP_DME_DISCARD_PORT_ID 0x7B24
> > +#define UNIP_DME_DBG_OPTION_SUITE 0x7C00
> > +#define UNIP_DME_DBG_CTRL_FSM 0x7D00
> > +#define UNIP_DME_DBG_FLAG_STATUS 0x7D14
> > +#define UNIP_DME_DBG_LINKCFG_FSM 0x7D18
> > +
> > +#define UNIP_DME_INTR_ERROR_CODE 0x7B20
> > +#define UNIP_DME_DEEPSTALL_ENTER_REQ 0x7910
> > +#define UNIP_DME_DISCARD_CPORT_ID 0x7B24
> > +
> > +#define UNIP_DBG_FORCE_DME_CTRL_STATE 0x150
> > +#define UNIP_DBG_AUTO_DME_LINKSTARTUP 0x158
> > +#define UNIP_DBG_PA_CTRLSTATE 0x15C
> > +#define UNIP_DBG_PA_TX_STATE 0x160
> > +#define UNIP_DBG_BREAK_DME_CTRL_STATE 0x164
> > +#define UNIP_DBG_STEP_DME_CTRL_STATE 0x168
> > +#define UNIP_DBG_NEXT_DME_CTRL_STATE 0x16C
> > +
> > +/*
> > + * Driver specific definitions
> > + */
> > +struct exynos_ufs_phy {
> > + void __iomem *reg_pma;
> > +};
> > +
> > +struct exynos_ufs_clk_info {
> > + struct list_head list;
> > + struct clk *clk;
> > + const char *name;
> > + u32 freq;
> > +};
> > +
> > +struct exynos_ufs_misc_log {
> > + struct list_head clk_list_head;
> > + bool isolation;
> > +};
> > +
> > +struct exynos_ufs_sfr_log {
> > + const char* name;
> > + const u32 offset;
> > +#define LOG_STD_HCI_SFR 0xFFFFFFF0
> > +#define LOG_VS_HCI_SFR 0xFFFFFFF1
> > +#define LOG_FMP_SFR 0xFFFFFFF2
> > +#define LOG_UNIPRO_SFR 0xFFFFFFF3
> > +#define LOG_PMA_SFR 0xFFFFFFF4
> > + u32 val;
> > +};
> > +
> > +struct exynos_ufs_attr_log {
> > + const u32 offset;
> > + u32 res;
> > + u32 val;
> > +};
> > +
> > +struct exynos_ufs_perf {
> > + u32 opcode; /* 0: read, 1: write */
> > + u32 chunk_size;
> > + ktime_t time;
> > + u64 total_time;
> > + u32 count;
> > + u32 total_count;
> > +};
> > +
> > +/* Main structure for debug and performance */ struct
> > +exynos_ufs_debug {
> > + struct exynos_ufs_sfr_log* std_sfr;
> > + struct exynos_ufs_sfr_log* sfr;
> > + struct exynos_ufs_attr_log* attr;
> > + struct exynos_ufs_misc_log misc;
> > + struct exynos_ufs_perf perf;
> > +};
> > +
> > +struct exynos_access_cxt {
> > + u32 offset;
> > + u32 mask;
> > + u32 val;
> > +};
> > +
> > +struct uic_pwr_mode {
> > + u8 lane;
> > + u8 gear;
> > + u8 mode;
> > + u8 hs_series;
> > +};
> > +
> > +struct exynos_ufs {
> > + struct device *dev;
> > + struct ufs_hba *hba;
> > +
> > + void __iomem *reg_hci;
> > + void __iomem *reg_unipro;
> > +
> > + struct regmap *pmureg;
> > + struct regmap *sysreg;
> > +
> > + struct clk *clk_hci;
> > + struct clk *pclk;
> > + struct clk *clk_unipro;
> > + u32 mclk_rate;
> > +
> > + int num_rx_lanes;
> > + int num_tx_lanes;
> > +
> > + struct exynos_ufs_phy phy;
> > + struct uic_pwr_mode req_pmd_parm;
> > + struct uic_pwr_mode act_pmd_parm;
> > +
> > + u32 rx_min_actv_time_cap;
> > + u32 rx_hibern8_time_cap;
> > + u32 tx_hibern8_time_cap;
> > +
> > + /* for miscellaneous control */
> > + u32 misc_flags;
> > +#define EXYNOS_UFS_MISC_TOGGLE_LOG BIT(0)
> > +
> > + struct exynos_ufs_debug debug;
> > +
> > + u32 hw_rev;
> > +
> > + struct exynos_access_cxt cxt_iso; /* phy isolation */
> > + struct exynos_access_cxt cxt_coherency; /* io coherency */
> > +};
> > +
> > +static inline struct exynos_ufs *to_exynos_ufs(struct ufs_hba *hba) {
> > + return dev_get_platdata(hba->dev);
> > +}
> > +
> > +#ifndef __EXYNOS_UFS_MMIO_FUNC__
> > +#define __EXYNOS_UFS_MMIO_FUNC__
>
> I don't understand..why need to check "ifndef __EXYNOS_UFS_MMIO_FUNC__".
> (because i didnt find __EXYNOS_UFS_MMIO_FUNC__ anywhere.) It means that
> you want to define always, doesn't?
>
> > +#define EXYNOS_UFS_MMIO_FUNC(name) \
> > +static inline void name##_writel(struct exynos_ufs *ufs, u32 val, u32
> reg) \
> > +{ \
> > + writel(val, ufs->reg_##name + reg); \
> > +} \
> > + \
> > +static inline u32 name##_readl(struct exynos_ufs *ufs, u32 reg)
> \
> > +{ \
> > + return readl(ufs->reg_##name + reg); \
> > +}
> > +
> > +EXYNOS_UFS_MMIO_FUNC(hci);
> > +EXYNOS_UFS_MMIO_FUNC(unipro);
>
> Maybe.. i don't like this style..because it's too difficult to debug and
> find the function.
>
> > +
> > +static inline void phy_pma_writel(struct exynos_ufs *ufs, u32 val,
> > +u32 reg) {
> > + u32 reg1 = hci_readl(ufs, HCI_CLKSTOP_CTRL);
> > +
> > + hci_writel(ufs, reg1 & ~MPHY_APBCLK_STOP, HCI_CLKSTOP_CTRL);
> > + writel(val, ufs->phy.reg_pma + reg);
> > + hci_writel(ufs, reg1 | MPHY_APBCLK_STOP, HCI_CLKSTOP_CTRL); }
> > +
> > +static inline u32 phy_pma_readl(struct exynos_ufs *ufs, u32 reg) {
> > + u32 reg1 = hci_readl(ufs, HCI_CLKSTOP_CTRL);
> > +
> > + hci_writel(ufs, reg1 & ~MPHY_APBCLK_STOP, HCI_CLKSTOP_CTRL);
> > + reg = readl(ufs->phy.reg_pma + reg);
> > + hci_writel(ufs, reg1 | MPHY_APBCLK_STOP, HCI_CLKSTOP_CTRL);
> > +
> > + return reg;
> > +}
> > +#endif
> > +
> > +#endif /* _UFS_EXYNOS_H_ */
> > diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
> > index 1332e544da92..1afd5ac9707c 100644
> > --- a/drivers/scsi/ufs/ufshcd.h
> > +++ b/drivers/scsi/ufs/ufshcd.h
> > @@ -308,6 +308,7 @@ struct ufs_hba_variant_ops {
> > int (*setup_clocks)(struct ufs_hba *, bool,
> > enum ufs_notify_change_status);
> > int (*setup_regulators)(struct ufs_hba *, bool);
> > + void (*host_reset)(struct ufs_hba *);
> > int (*hce_enable_notify)(struct ufs_hba *,
> > enum ufs_notify_change_status);
> > int (*link_startup_notify)(struct ufs_hba *,
> >
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] scsi: ufs: add Exynos-specific driver
2017-11-28 8:27 ` 김기웅
@ 2017-11-28 9:20 ` Jaehoon Chung
2017-11-30 8:35 ` 김기웅
0 siblings, 1 reply; 8+ messages in thread
From: Jaehoon Chung @ 2017-11-28 9:20 UTC (permalink / raw)
To: 김기웅, linux-scsi,
'Martin K. Petersen'
Cc: cpgs, 'HeonGwang Chu', '김부진',
'YOUNGEUN PARK'
On 11/28/2017 05:27 PM, 김기웅 wrote:
> Hi.
> This is modified from Seungwon's initial patch.
> And this has been used for several commercial products.
> I think you feel weird because you can't see a bunch of unipro and mphy.
> But those stuff has been changed whenever new product comes.
> So I didn't keep those in this driver.
Unipro and mphy setting values can be got from device-tree according to each variant boards.
Then it doesn't need to keep in this driver. but there is no usage for them in this patch.
Otherwise, this driver may be a dead driver.
Also anyone doesn't have the interesting about this driver.
And i also added the some comment at below..
Best Regards,
Jaehoon Chung
>
>
>> -----Original Message-----
>> From: linux-scsi-owner@vger.kernel.org [mailto:linux-scsi-
>> owner@vger.kernel.org] On Behalf Of Jaehoon Chung
>> Sent: Tuesday, November 28, 2017 5:20 PM
>> To: 김기웅; linux-scsi@vger.kernel.org; Martin K. Petersen
>> Cc: cpgs@samsung.com; HeonGwang Chu; 김부진; YOUNGEUN PARK
>> Subject: Re: [PATCH 2/2] scsi: ufs: add Exynos-specific driver
>>
>> Hi,
>>
>> On 11/28/2017 02:36 PM, 김기웅 wrote:
>>> This driver is to use UFS devices on Exynos SoC and has been already
>>> used for many years for commercial products.
>>
>> Well, i'm not sure but i remembered there are the similar patches
>> before..Seungwon and Alim's patches.
>> Is it relevant to them?
>>
>> Anyway.. i think i can't test with only these patches..
>> how did you test this patches?
>>
>>>
>>> Signed-off-by: Kiwoong Kim <kwmad.kim@samsung.com>
>>> ---
>>> drivers/scsi/ufs/Kconfig | 10 +
>>> drivers/scsi/ufs/Makefile | 1 +
>>> drivers/scsi/ufs/ufs-exynos.c | 962
>>> ++++++++++++++++++++++++++++++++++++++++++
>>> drivers/scsi/ufs/ufs-exynos.h | 351 +++++++++++++++
>>> drivers/scsi/ufs/ufshcd.h | 1 +
>>> 5 files changed, 1325 insertions(+)
>>> create mode 100644 drivers/scsi/ufs/ufs-exynos.c create mode 100644
>>> drivers/scsi/ufs/ufs-exynos.h
>>
>> There is no binding file.
>>
>>>
>>> diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig index
>>> e27b4d4e6ae2..7d71ad8768c3 100644
>>> --- a/drivers/scsi/ufs/Kconfig
>>> +++ b/drivers/scsi/ufs/Kconfig
>>> @@ -100,3 +100,13 @@ config SCSI_UFS_QCOM
>>>
>>> Select this if you have UFS controller on QCOM chipset.
>>> If unsure, say N.
>>> +
>>> +config SCSI_UFS_EXYNOS
>>> + tristate "EXYNOS UFS Host Controller Driver"
>>> + depends on SCSI_UFSHCD && SCSI_UFSHCD_PLATFORM
>>> + ---help---
>>> + This selects the EXYNOS UFS host controller driver.
>>> +
>>> + If you have a controller with this interface, say Y or M here.
>>> +
>>> + If unsure, say N.
>>> diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile
>>> index 9310c6c83041..3312b052dcff 100644
>>> --- a/drivers/scsi/ufs/Makefile
>>> +++ b/drivers/scsi/ufs/Makefile
>>> @@ -3,6 +3,7 @@
>>> obj-$(CONFIG_SCSI_UFS_DWC_TC_PCI) += tc-dwc-g210-pci.o ufshcd-dwc.o
>>> tc-dwc-g210.o
>>> obj-$(CONFIG_SCSI_UFS_DWC_TC_PLATFORM) += tc-dwc-g210-pltfrm.o
>>> ufshcd-dwc.o tc-dwc-g210.o
>>> obj-$(CONFIG_SCSI_UFS_QCOM) += ufs-qcom.o
>>> +obj-$(CONFIG_SCSI_UFS_EXYNOS) += ufs-exynos.o
>>> obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o
>>> obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o
>>> obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o diff --git
>>> a/drivers/scsi/ufs/ufs-exynos.c b/drivers/scsi/ufs/ufs-exynos.c new
>>> file mode 100644 index 000000000000..98e5aeb80b06
>>> --- /dev/null
>>> +++ b/drivers/scsi/ufs/ufs-exynos.c
>>> @@ -0,0 +1,962 @@
>>> +/*
>>> + * UFS Host Controller driver for Exynos specific extensions
>>> + *
>>> + * Copyright (C) 2013-2014 Samsung Electronics Co., Ltd.
>>
>> 2013-2014? is it right?
>>
>>> + *
>>> + * This program is free software; you can redistribute it and/or
>>> +modify
>>> + * it under the terms of the GNU General Public License as published
>>> +by
>>> + * the Free Software Foundation; either version 2 of the License, or
>>> + * (at your option) any later version.
>>> + */
>>> +#include <linux/module.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/of.h>
>>> +#include <linux/of_address.h>
>>> +#include <linux/clk.h>
>>> +#include "ufshcd.h"
>>> +#include "ufshcd-pltfrm.h"
>>> +#include "ufs-exynos.h"
>>> +#include <linux/mfd/syscon.h>
>>> +#include <linux/regmap.h>
>>> +#include <linux/spinlock.h>
>>
>> ordering about header file.
>>
>>> +
>>> +/*
>>> + * Debugging information, SFR/attributes/misc */ static struct
>>> +exynos_ufs *ufs_host_backup[1];> +static int ufs_host_index = 0;
>>
>> It has to use the global? Is there any other solution?
>>
>>> +
>>> +static struct exynos_ufs_sfr_log ufs_log_std_sfr[] = {
>>> + {"CAPABILITIES" , REG_CONTROLLER_CAPABILITIES,
>> 0},
>>> + {"UFS VERSION" , REG_UFS_VERSION,
>> 0},
>>> + {"PRODUCT ID" , REG_CONTROLLER_DEV_ID,
>> 0},
>>> + {"MANUFACTURE ID" , REG_CONTROLLER_PROD_ID,
>> 0},
>>> + {"INTERRUPT STATUS" , REG_INTERRUPT_STATUS,
>> 0},
>>> + {"INTERRUPT ENABLE" , REG_INTERRUPT_ENABLE,
>> 0},
>>> + {"CONTROLLER STATUS" , REG_CONTROLLER_STATUS,
>> 0},
>>> + {"CONTROLLER ENABLE" , REG_CONTROLLER_ENABLE,
>> 0},
>>> + {"UIC ERR PHY ADAPTER LAYER" ,
>> REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER, 0},
>>> + {"UIC ERR DATA LINK LAYER" , REG_UIC_ERROR_CODE_DATA_LINK_LAYER,
>> 0},
>>> + {"UIC ERR NETWORK LATER" , REG_UIC_ERROR_CODE_NETWORK_LAYER,
>> 0},
>>> + {"UIC ERR TRANSPORT LAYER" , REG_UIC_ERROR_CODE_TRANSPORT_LAYER,
>> 0},
>>> + {"UIC ERR DME" , REG_UIC_ERROR_CODE_DME,
>> 0},
>>> + {"UTP TRANSF REQ INT AGG CNTRL" ,
>> REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL, 0},
>>> + {"UTP TRANSF REQ LIST BASE L" ,
>> REG_UTP_TRANSFER_REQ_LIST_BASE_L, 0},
>>> + {"UTP TRANSF REQ LIST BASE H" ,
>> REG_UTP_TRANSFER_REQ_LIST_BASE_H, 0},
>>> + {"UTP TRANSF REQ DOOR BELL" ,
>> REG_UTP_TRANSFER_REQ_DOOR_BELL, 0},
>>> + {"UTP TRANSF REQ LIST CLEAR" ,
>> REG_UTP_TRANSFER_REQ_LIST_CLEAR, 0},
>>> + {"UTP TRANSF REQ LIST RUN STOP" ,
>> REG_UTP_TRANSFER_REQ_LIST_RUN_STOP, 0},
>>> + {"UTP TASK REQ LIST BASE L" ,
>> REG_UTP_TASK_REQ_LIST_BASE_L, 0},
>>> + {"UTP TASK REQ LIST BASE H" ,
>> REG_UTP_TASK_REQ_LIST_BASE_H, 0},
>>> + {"UTP TASK REQ DOOR BELL" , REG_UTP_TASK_REQ_DOOR_BELL,
>> 0},
>>> + {"UTP TASK REQ LIST CLEAR" , REG_UTP_TASK_REQ_LIST_CLEAR,
>> 0},
>>> + {"UTP TASK REQ LIST RUN STOP" ,
>> REG_UTP_TASK_REQ_LIST_RUN_STOP, 0},
>>> + {"UIC COMMAND" , REG_UIC_COMMAND,
>> 0},
>>> + {"UIC COMMAND ARG1" , REG_UIC_COMMAND_ARG_1,
>> 0},
>>> + {"UIC COMMAND ARG2" , REG_UIC_COMMAND_ARG_2,
>> 0},
>>> + {"UIC COMMAND ARG3" , REG_UIC_COMMAND_ARG_3,
>> 0},
>>> +
>>> + {},
>>> +};
>>> +
>>> +/* Helper for UFS CAL interface */
>>> +static inline int ufs_init_cal(struct exynos_ufs *ufs, int idx,
>>> + struct platform_device *pdev)
>>> +{
>>> + return 0;
>>> +}
>>> +
>>> +static inline int ufs_pre_link(struct exynos_ufs *ufs) {
>>> + return 0;
>>> +}
>>> +
>>> +static inline int ufs_post_link(struct exynos_ufs *ufs) {
>>> + return 0;
>>> +}
>>> +
>>> +static inline int ufs_pre_gear_change(struct exynos_ufs *ufs,
>>> + struct uic_pwr_mode *pmd)
>>> +{
>>> + return 0;
>>> +}
>>> +
>>> +static inline int ufs_post_gear_change(struct exynos_ufs *ufs) {
>>> + return 0;
>>> +}
>>> +
>>> +static inline int ufs_post_h8_enter(struct exynos_ufs *ufs) {
>>> + return 0;
>>> +}
>>> +
>>> +static inline int ufs_pre_h8_exit(struct exynos_ufs *ufs) {
>>> + return 0;
>>> +}
>>
>> What purpose has these helper fuctions?
>>
>>> +
>>> +static inline void exynos_ufs_ctrl_phy_pwr(struct exynos_ufs *ufs,
>>> +bool en) {
>>> + int ret = 0;
>>> +
>>> + if (en)
>>> + ret = regmap_update_bits(ufs->pmureg, ufs->cxt_iso.offset,
>>> + ufs->cxt_iso.mask, ufs->cxt_iso.val);
>>> + else
>>> + ret = regmap_update_bits(ufs->pmureg, ufs->cxt_iso.offset,
>>> + ufs->cxt_iso.mask, 0);
>>> +
>>> + if (ret)
>>> + dev_err(ufs->dev, "Unable to update PHY ISO control\n"); }
>>> +
>>> +#ifndef __EXYNOS_UFS_VS_DEBUG__
>>
>> There is no defined "__EXYNOS_UFS_VS_DEBUG__". Remove it or include it
>> into Kconfig.
>>
>>> +static void exynos_ufs_dump_std_sfr(struct ufs_hba *hba) {
>>> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
>>> + struct exynos_ufs_sfr_log* cfg = ufs->debug.std_sfr;
>>> +
>>> + dev_err(hba->dev, ": ----------------------------------------------
>> ----- \n");
>>> + dev_err(hba->dev, ": \t\tREGISTER DUMP\n");
>>> + dev_err(hba->dev, ":
>>> +--------------------------------------------------- \n");
>>> +
>>> + while(cfg) {
>>> + if (!cfg->name)
>>> + break;
>>> + cfg->val = ufshcd_readl(hba, cfg->offset);
>>> +
>>> + /* Dump */
>>> + dev_err(hba->dev, ": %s(0x%04x):\t\t\t\t0x%08x\n",
>>> + cfg->name, cfg->offset, cfg->val);
>>> +
>>> + /* Next SFR */
>>> + cfg++;
>>> + }
>>> +}
>>> +#endif
>>> +
>>> +/*
>>> + * Exynos debugging main function
>>> + */
>>> +static void exynos_ufs_dump_debug_info(struct ufs_hba *hba) { #ifdef
>>> +__EXYNOS_UFS_VS_DEBUG__
>>
>> What is this? Can be remove this.
>>
>>> +#else
>>> + exynos_ufs_dump_std_sfr(hba);
>>> +#endif
>>> +}
>>> +
>>> +static inline void exynos_ufs_set_hwacg_control(struct exynos_ufs
>>> +*ufs, bool en) {
>>> + u32 reg;
>>> + if ((ufs->hw_rev != UFS_VER_0004) && (ufs->hw_rev != UFS_VER_0005))
>>> + return;
>>> +
>>> + /*
>>> + * default value 1->0 at KC. so,
>>> + * need to set "1(disable HWACG)" during UFS init
>>> + */
>>> + reg = hci_readl(ufs, HCI_UFS_ACG_DISABLE);
>>> + if (en)
>>> + hci_writel(ufs, reg & (~HCI_UFS_ACG_DISABLE_EN),
>> HCI_UFS_ACG_DISABLE);
>>> + else
>>> + hci_writel(ufs, reg | HCI_UFS_ACG_DISABLE_EN,
>> HCI_UFS_ACG_DISABLE);
>>> +
>>> +}
>>> +
>>> +static inline void exynos_ufs_ctrl_auto_hci_clk(struct exynos_ufs
>>> +*ufs, bool en) {
>>> + u32 reg = hci_readl(ufs, HCI_FORCE_HCS);
>>> +
>>> + if (en)
>>> + hci_writel(ufs, reg | HCI_CORECLK_STOP_EN, HCI_FORCE_HCS);
>>> + else
>>> + hci_writel(ufs, reg & ~HCI_CORECLK_STOP_EN, HCI_FORCE_HCS); }
>>> +
>>> +static inline void exynos_ufs_ctrl_clk(struct exynos_ufs *ufs, bool
>>> +en) {
>>> + u32 reg = hci_readl(ufs, HCI_FORCE_HCS);
>>> +
>>> + if (en)
>>> + hci_writel(ufs, reg | CLK_STOP_CTRL_EN_ALL, HCI_FORCE_HCS);
>>> + else
>>> + hci_writel(ufs, reg & ~CLK_STOP_CTRL_EN_ALL,
>> HCI_FORCE_HCS); }
>>> +
>>> +static inline void exynos_ufs_gate_clk(struct exynos_ufs *ufs, bool
>>> +en) {
>>> +
>>> + u32 reg = hci_readl(ufs, HCI_CLKSTOP_CTRL);
>>> +
>>> + if (en)
>>> + hci_writel(ufs, reg | CLK_STOP_ALL, HCI_CLKSTOP_CTRL);
>>> + else
>>> + hci_writel(ufs, reg & ~CLK_STOP_ALL, HCI_CLKSTOP_CTRL); }
>>> +
>>> +static inline void exynos_ufs_set_unipro_mclk(struct exynos_ufs *ufs)
>>> +{
>>> + ufs->mclk_rate = clk_get_rate(ufs->clk_unipro); }
>>> +
>>> +static inline void exynos_ufs_fit_aggr_timeout(struct exynos_ufs
>>> +*ufs) {
>>> + u32 cnt_val;
>>> + u32 nVal;
>>
>> Don't use the upper character.
>>
>>> +
>>> + /* IA_TICK_SEL : 1(1us_TO_CNT_VAL) */
>>> + nVal = hci_readl(ufs, HCI_UFSHCI_V2P1_CTRL);
>>> + nVal |= IA_TICK_SEL;
>>> + hci_writel(ufs, nVal, HCI_UFSHCI_V2P1_CTRL);
>>> +
>>> + cnt_val = ufs->mclk_rate / 1000000 ;
>>> + hci_writel(ufs, cnt_val & CNT_VAL_1US_MASK, HCI_1US_TO_CNT_VAL); }
>>> +
>>> +static void exynos_ufs_init_pmc_req(struct ufs_hba *hba,
>>> + struct ufs_pa_layer_attr *pwr_max,
>>> + struct ufs_pa_layer_attr *pwr_req)
>>> +{
>>> +
>>> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
>>> + struct uic_pwr_mode *req_pmd = &ufs->req_pmd_parm;
>>> + struct uic_pwr_mode *act_pmd = &ufs->act_pmd_parm;
>>> +
>>> + /* update lane variable after link */
>>> + ufs->num_rx_lanes = pwr_max->lane_rx;
>>> + ufs->num_tx_lanes = pwr_max->lane_tx;
>>> +
>>> + pwr_req->gear_rx
>>> + = act_pmd->gear= min_t(u8, pwr_max->gear_rx, req_pmd->gear);
>>> + pwr_req->gear_tx
>>> + = act_pmd->gear = min_t(u8, pwr_max->gear_tx, req_pmd->gear);
>>> + pwr_req->lane_rx
>>> + = act_pmd->lane = min_t(u8, pwr_max->lane_rx, req_pmd->lane);
>>> + pwr_req->lane_tx
>>> + = act_pmd->lane = min_t(u8, pwr_max->lane_tx, req_pmd->lane);
>>> + pwr_req->pwr_rx = act_pmd->mode = req_pmd->mode;
>>> + pwr_req->pwr_tx = act_pmd->mode = req_pmd->mode;
>>> + pwr_req->hs_rate = act_pmd->hs_series = req_pmd->hs_series; }
>>> +
>>> +static void exynos_ufs_config_intr(struct exynos_ufs *ufs, u32 errs,
>>> +u8 index) {
>>> + switch(index) {
>>> + case UNIP_PA_LYR:
>>> + hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERROR_EN_PA_LAYER);
>>> + break;
>>> + case UNIP_DL_LYR:
>>> + hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERROR_EN_DL_LAYER);
>>> + break;
>>> + case UNIP_N_LYR:
>>> + hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERROR_EN_N_LAYER);
>>> + break;
>>> + case UNIP_T_LYR:
>>> + hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERROR_EN_T_LAYER);
>>> + break;
>>> + case UNIP_DME_LYR:
>>> + hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERROR_EN_DME_LAYER);
>>> + break;
>>> + }
>>> +}
>>> +
>>> +static void exynos_ufs_dev_hw_reset(struct ufs_hba *hba) {
>>> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
>>> +
>>> + /* bit[1] for resetn */
>> f> + hci_writel(ufs, 0 << 0, HCI_GPIO_OUT);
>>
>> your comment is "bit[1] for resetn" ..but this bit conotrol is for bit[0].
>> I don't want to use "0 << 0" and " 1 << 0".
>>
>> #define RESETN_HIGH/LOW whatever...
>>
>>> + udelay(5);
>>> + hci_writel(ufs, 1 << 0, HCI_GPIO_OUT); }
>>> +
>>> +static void exynos_ufs_init_host(struct exynos_ufs *ufs) {
>>> + u32 reg;
>>> +
>>> + /* internal clock control */
>>> + exynos_ufs_ctrl_auto_hci_clk(ufs, false);
>>> + exynos_ufs_set_unipro_mclk(ufs);
>>> +
>>> + /* period for interrupt aggregation */
>>> + exynos_ufs_fit_aggr_timeout(ufs);
>>> +
>>> + /* misc HCI configurations */
>>> + hci_writel(ufs, 0xA, HCI_DATA_REORDER);
>>
>> Don't use magin number. what is 0xA?
>>
>>> + hci_writel(ufs, PRDT_PREFECT_EN | PRDT_SET_SIZE(12),
>>> + HCI_TXPRDT_ENTRY_SIZE);
>>> + hci_writel(ufs, PRDT_SET_SIZE(12), HCI_RXPRDT_ENTRY_SIZE);
>>> + hci_writel(ufs, 0xFFFFFFFF, HCI_UTRL_NEXUS_TYPE);
>>> + hci_writel(ufs, 0xFFFFFFFF, HCI_UTMRL_NEXUS_TYPE);
>>
>> Ditto.
>>> +
>>> + reg = hci_readl(ufs, HCI_AXIDMA_RWDATA_BURST_LEN) &
>>> + ~BURST_LEN(0);
>>> + hci_writel(ufs, WLU_EN | BURST_LEN(3),
>>> + HCI_AXIDMA_RWDATA_BURST_LEN);
>>> +
>>> + /*
>>> + * Enable HWAGC control by IOP
>>> + *
>>> + * default value 1->0 at KC.
>>> + * always "0"(controlled by UFS_ACG_DISABLE)
>>> + */
>>> + reg = hci_readl(ufs, HCI_IOP_ACG_DISABLE);
>>> + hci_writel(ufs, reg & (~HCI_IOP_ACG_DISABLE_EN),
>>> +HCI_IOP_ACG_DISABLE); }
>>> +
>>> +static int exynos_ufs_init_system(struct exynos_ufs *ufs) {
>>> + struct device *dev = ufs->dev;
>>> + int ret = 0;
>>> + bool is_io_coherency;
>>> + bool is_dma_coherent;
>>> +
>>> + /* PHY isolation bypass */
>>> + exynos_ufs_ctrl_phy_pwr(ufs, true);
>>> +
>>> + /* IO cohernecy */
>>> + is_io_coherency = !IS_ERR(ufs->sysreg);
>>> + is_dma_coherent = !!of_find_property(dev->of_node,
>>> + "dma-coherent", NULL);
>>> +
>>> + if (is_io_coherency != is_dma_coherent)
>>> + BUG();
>>
>> BUG()?
>>
>>> +
>>> + if (!is_io_coherency)
>>> + dev_err(dev, "Not configured to use IO coherency\n");
>>> + else
>>> + ret = regmap_update_bits(ufs->sysreg, ufs-
>>> cxt_coherency.offset,
>>> + ufs->cxt_coherency.mask, ufs->cxt_coherency.val);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static int exynos_ufs_get_clks(struct ufs_hba *hba) {
>>> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
>>> + struct list_head *head = &hba->clk_list_head;
>>> + struct ufs_clk_info *clki;
>>> + int i = 0;
>>> +
>>> + ufs_host_backup[ufs_host_index++] = ufs;
>>> + ufs->debug.std_sfr = ufs_log_std_sfr;
>>> +
>>> + if (!head || list_empty(head))
>>> + goto out;
>>> +
>>> + list_for_each_entry(clki, head, list) {
>>> + /*
>>> + * get clock with an order listed in device tree
>>> + */
>>> + if (i == 0)
>>> + ufs->clk_hci = clki->clk;
>>> + else if (i == 1)
>>> + ufs->clk_unipro = clki->clk;
>>> +
>>> + i++;
>>> + }
>>> +out:
>>> + if (!ufs->clk_hci || !ufs->clk_unipro)
>>> + return -EINVAL;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static void exynos_ufs_set_features(struct ufs_hba *hba, u32 hw_rev)
>>> +{
>>> + /* caps */
>>> + hba->caps = UFSHCD_CAP_CLK_GATING |
>>> + UFSHCD_CAP_HIBERN8_WITH_CLK_GATING |
>>> + UFSHCD_CAP_INTR_AGGR;
>>> +
>>> + /* quirks of common driver */
>>> + hba->quirks = UFSHCD_QUIRK_PRDT_BYTE_GRAN;
>>> +
>>> + /* quirks of exynos-specific driver */
>>
>> Remove unused comment.
>>
>>> +}
>>> +
>>> +/*
>>> + * Exynos-specific callback functions
>>> + *
>>> + * init | Pure SW init & system-related init
>>> + * host_reset | Host SW reset & init
>>> + * ...
>>> + *
>>> + * Initializations for software, host controller and system
>>> + * should be contained only in ->host_reset() as possible.
>>> + */
>>> +
>>> +static int exynos_ufs_init(struct ufs_hba *hba) {
>>> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
>>> + int ret;
>>> +
>>> + /* set features, such as caps or quirks */
>>> + exynos_ufs_set_features(hba, ufs->hw_rev);
>>> +
>>> + /* get some clock sources and debug infomation structures */
>>> + ret = exynos_ufs_get_clks(hba);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + /* system init */
>>> + ret = exynos_ufs_init_system(ufs);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + ufs->misc_flags = EXYNOS_UFS_MISC_TOGGLE_LOG;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static void exynos_ufs_host_reset(struct ufs_hba *hba) {
>>> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
>>> + unsigned long timeout = jiffies + msecs_to_jiffies(1);
>>> +
>>> + exynos_ufs_ctrl_auto_hci_clk(ufs, false);
>>> +
>>> + hci_writel(ufs, UFS_SW_RST_MASK, HCI_SW_RST);
>>> +
>>> + do {
>>> + if (!(hci_readl(ufs, HCI_SW_RST) & UFS_SW_RST_MASK))
>>> + goto success;
>>
>> Need to "goto" statement? Just can be located the below code.
>> if () {
>> exynos_ufs_init_host();
>> exynos_ufs_dev_hw_reset();
>> return;
>> }
>>
>>> + } while (time_before(jiffies, timeout));
>>> +
>>> + dev_err(ufs->dev, "timeout host sw-reset\n");
>>> +
>>> + goto out;
>>
>> Not need "goto out", instead "return" at here.
>>
>>> +
>>> +success:
>>> + /* host init */
>>> + exynos_ufs_init_host(ufs);
>>> +
>>> + /* device reset */
>>> + exynos_ufs_dev_hw_reset(hba);
>>> +out:
>>> + return;
>>> +}
>>> +
>>> +static inline void exynos_ufs_dev_reset_ctrl(struct exynos_ufs *ufs,
>>> +bool en) {
>>> +
>>> + if (en)
>>> + hci_writel(ufs, 1 << 0, HCI_GPIO_OUT);
>>> + else
>>> + hci_writel(ufs, 0 << 0, HCI_GPIO_OUT);
>>
>> I don't know why this function is need. There is no call anywhere with
>> "true".
>>
>>> +}
>>> +
>>> +static int exynos_ufs_setup_clocks(struct ufs_hba *hba, bool on,
>>> + enum ufs_notify_change_status status) {
>>> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
>>> + int ret = 0;
>>> +
>>> + if (status == PRE_CHANGE) {
>>> + if (on) {
>>> + /*
>>> + * Now all used blocks would not be turned off in a
>> host.
>>> + */
>>> + exynos_ufs_ctrl_auto_hci_clk(ufs, false);
>>> + exynos_ufs_gate_clk(ufs, false);
>>> +
>>> + /* HWAGC disable */
>>> + exynos_ufs_set_hwacg_control(ufs, false);
>>> + } else {
>>> + ret = ufs_post_h8_enter(ufs);
>>> + }
>>> + } else {
>>> + if (on) {
>>> + ret = ufs_pre_h8_exit(ufs);
>>> + } else {
>>> + /*
>>> + * Now all used blocks would be turned off in a host.
>>> + */
>>> + exynos_ufs_ctrl_auto_hci_clk(ufs, true);
>>> +
>>> + /* HWAGC enable */
>>> + exynos_ufs_set_hwacg_control(ufs, true);
>>> + }
>>> + }
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static int exynos_ufs_link_startup_notify(struct ufs_hba *hba,
>>> + enum ufs_notify_change_status status) {
>>> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
>>> + int ret = 0;
>>> +
>>> + switch (status) {
>>> + case PRE_CHANGE:
>>> + /* refer to hba */
>>> + ufs->hba = hba;
>>> +
>>> + /* hci */
>>> + exynos_ufs_config_intr(ufs, DFES_DEF_DL_ERRS, UNIP_DL_LYR);
>>> + exynos_ufs_config_intr(ufs, DFES_DEF_N_ERRS, UNIP_N_LYR);
>>> + exynos_ufs_config_intr(ufs, DFES_DEF_T_ERRS, UNIP_T_LYR);
>>> +
>>> + exynos_ufs_ctrl_clk(ufs, true);
>>> + exynos_ufs_gate_clk(ufs, false);
>>> + exynos_ufs_set_hwacg_control(ufs, false);
>>> +
>>> + if (ufs->num_rx_lanes == 0 || ufs->num_tx_lanes == 0) {
>>> + ufshcd_dme_get(hba, UIC_ARG_MIB(PA_AVAILRXDATALANES),
>>> + &ufs->num_rx_lanes);
>>> + ufshcd_dme_get(hba, UIC_ARG_MIB(PA_AVAILTXDATALANES),
>>> + &ufs->num_tx_lanes);
>>> + WARN(ufs->num_rx_lanes != ufs->num_tx_lanes,
>>> + "available data lane is not equal(rx:%d,
>> tx:%d)\n",
>>> + ufs->num_rx_lanes, ufs->num_tx_lanes);
>>> + }
>>> +
>>> + ufs->mclk_rate = clk_get_rate(ufs->clk_unipro);
>>> +
>>> + ret = ufs_pre_link(ufs);
>>> + break;
>>> + case POST_CHANGE:
>>> + /* UIC configuration table after link startup */
>>> + ret = ufs_post_link(ufs);
>>> + break;
>>> + default:
>>> + break;
>>> + }
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static int exynos_ufs_pwr_change_notify(struct ufs_hba *hba,
>>> + enum ufs_notify_change_status status,
>>> + struct ufs_pa_layer_attr *pwr_max,
>>> + struct ufs_pa_layer_attr *pwr_req) {
>>> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
>>> + struct uic_pwr_mode *act_pmd = &ufs->act_pmd_parm;
>>> + int ret = 0;
>>> +
>>> + switch (status) {
>>> + case PRE_CHANGE:
>>> +
>>> + /* Set PMC parameters to be requested */
>>> + exynos_ufs_init_pmc_req(hba, pwr_max, pwr_req);
>>> +
>>> + /* UIC configuration table before power mode change */
>>> + ret = ufs_pre_gear_change(ufs, act_pmd);
>>> +
>>> + break;
>>> + case POST_CHANGE:
>>> + /* UIC configuration table after power mode change */
>>> + ret = ufs_post_gear_change(ufs);
>>> +
>>> + dev_info(ufs->dev,
>>> + "Power mode change(%d): M(%d)G(%d)L(%d)HS-
>> series(%d)\n",
>>> + ret, act_pmd->mode, act_pmd->gear,
>>> + act_pmd->lane, act_pmd->hs_series);
>>
>> Fix the indent
>>
>>> + break;
>>> + default:
>>> + break;
>>> + }
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static void exynos_ufs_set_nexus_t_xfer_req(struct ufs_hba *hba,
>>> + int tag, bool op)
>>> +{
>>> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
>>> + u32 type;
>>> +
>>> + type = hci_readl(ufs, HCI_UTRL_NEXUS_TYPE);
>>> +
>>> + if (op)
>>> + type |= (1 << tag);
>>> + else
>>> + type &= ~(1 << tag);
>>> +
>>> + hci_writel(ufs, type, HCI_UTRL_NEXUS_TYPE); }
>>> +
>>> +static void exynos_ufs_set_nexus_t_task_mgmt(struct ufs_hba *hba, int
>>> +tag, u8 tm_func) {
>>> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
>>> + u32 type;
>>> +
>>> + type = hci_readl(ufs, HCI_UTMRL_NEXUS_TYPE);
>>> +
>>> + switch (tm_func) {
>>> + case UFS_ABORT_TASK:
>>> + case UFS_QUERY_TASK:
>>> + type |= (1 << tag);
>>> + break;
>>> + case UFS_ABORT_TASK_SET:
>>> + case UFS_CLEAR_TASK_SET:
>>> + case UFS_LOGICAL_RESET:
>>> + case UFS_QUERY_TASK_SET:
>>> + type &= ~(1 << tag);
>>> + break;
>>> + }
>>> +
>>> + hci_writel(ufs, type, HCI_UTMRL_NEXUS_TYPE); }
>>> +
>>> +static int __exynos_ufs_suspend(struct ufs_hba *hba, enum ufs_pm_op
>>> +pm_op) {
>>> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
>>> +
>>> + exynos_ufs_dev_reset_ctrl(ufs, false);
>>> +
>>> + exynos_ufs_ctrl_phy_pwr(ufs, false);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int __exynos_ufs_resume(struct ufs_hba *hba, enum ufs_pm_op
>>> +pm_op) {
>>> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
>>> + int ret = 0;
>>> +
>>> + exynos_ufs_ctrl_phy_pwr(ufs, true);
>>> +
>>> + /* system init */
>>> + ret = exynos_ufs_init_system(ufs);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + if (ufshcd_is_clkgating_allowed(hba))
>>> + clk_prepare_enable(ufs->clk_hci);
>>> + exynos_ufs_ctrl_auto_hci_clk(ufs, false);
>>> +
>>> + if (ufshcd_is_clkgating_allowed(hba))
>>> + clk_disable_unprepare(ufs->clk_hci);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static struct ufs_hba_variant_ops exynos_ufs_ops = {
>>> + .init = exynos_ufs_init,
>>> + .host_reset = exynos_ufs_host_reset,
>>> + .setup_clocks = exynos_ufs_setup_clocks,
>>> + .link_startup_notify = exynos_ufs_link_startup_notify,
>>> + .pwr_change_notify = exynos_ufs_pwr_change_notify,
>>> + .setup_xfer_req = exynos_ufs_set_nexus_t_xfer_req,
>>> + .setup_task_mgmt = exynos_ufs_set_nexus_t_task_mgmt,
>>> + .dbg_register_dump = exynos_ufs_dump_debug_info,
>>> + .suspend = __exynos_ufs_suspend,
>>> + .resume = __exynos_ufs_resume,
>>> +};
>>> +
>>> +static int exynos_ufs_populate_dt_phy(struct device *dev, struct
>>> +exynos_ufs *ufs) {
>>> + struct device_node *ufs_phy;
>>> + struct exynos_ufs_phy *phy = &ufs->phy;
>>> + struct resource io_res;
>>> + int ret;
>>> +
>>> + ufs_phy = of_get_child_by_name(dev->of_node, "ufs-phy");
>>> + if (!ufs_phy) {
>>> + dev_err(dev, "failed to get ufs-phy node\n");
>>> + return -ENODEV;
>>> + }
>>> +
>>> + ret = of_address_to_resource(ufs_phy, 0, &io_res);
>>> + if (ret) {
>>> + dev_err(dev, "failed to get i/o address phy pma\n");
>>> + goto err_0;
>>> + }
>>> +
>>> + phy->reg_pma = devm_ioremap_resource(dev, &io_res);
>>> + if (!phy->reg_pma) {
>>> + dev_err(dev, "failed to ioremap for phy pma\n");
>>> + ret = -ENOMEM;
>>> + goto err_0;
>>> + }
>>> +
>>> +err_0:
>>> + of_node_put(ufs_phy);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +/*
>>> + * This function is to define offset, mask and shift to access
>> somewhere.
>>> + */
>>> +static int exynos_ufs_set_context_for_access(struct device *dev,
>>> + const char *name, struct exynos_access_cxt *cxt)
>> {
>>> + struct device_node *np;
>>> + int ret;
>>> +
>>> + np = of_get_child_by_name(dev->of_node, name);
>>> + if (!np) {
>>> + dev_err(dev, "failed to get node(%s)\n", name);
>>> + return 1;
>>
>> Don't use the meaningless value.
>>
>>> + }
>>> +
>>> + ret = of_property_read_u32(np, "offset", &cxt->offset);
>>> + if (IS_ERR(&cxt->offset)) {
>>> + dev_err(dev, "failed to set cxt(%s) offset\n", name);
>>> + return cxt->offset;
>>> + }
>>> +
>>> + ret = of_property_read_u32(np, "mask", &cxt->mask);
>>> + if (IS_ERR(&cxt->mask)) {
>>> + dev_err(dev, "failed to set cxt(%s) mask\n", name);
>>> + return cxt->mask;
>>> + }
>>> +
>>> + ret = of_property_read_u32(np, "val", &cxt->val);
>>> + if (IS_ERR(&cxt->val)) {
>>> + dev_err(dev, "failed to set cxt(%s) val\n", name);
>>> + return cxt->val;
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int exynos_ufs_populate_dt_system(struct device *dev, struct
>>> +exynos_ufs *ufs) {
>>> + int ret;
>>> +
>>> + /* regmap pmureg */
>>> + ufs->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
>>> + "samsung,pmu-phandle");
>>> + if (IS_ERR(ufs->pmureg)) {
>>> + /*
>>> + * phy isolation should be available.
>>> + * so this case need to be failed.
>>> + */
>>> + dev_err(dev, "pmu regmap lookup failed.\n");
>>> + return PTR_ERR(ufs->pmureg);
>>> + }
>>> +
>>> + /* Set access context for phy isolation bypass */
>>> + ret = exynos_ufs_set_context_for_access(dev, "ufs-phy-iso",
>>> + &ufs->cxt_iso);
>>> + if (ret == 1) {
>>> + /* no device node, default */
>>> + ufs->cxt_iso.offset = 0x0724;
>>> + ufs->cxt_iso.mask = 0x1;
>>> + ufs->cxt_iso.val = 0x1;
>>> + ret = 0;
>>> + }
>>> +
>>> + /* regmap sysreg */
>>> + ufs->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
>>> + "samsung,sysreg-fsys-phandle");
>>> + if (IS_ERR(ufs->sysreg)) {
>>> + /*
>>> + * Currently, ufs driver gets sysreg for io coherency.
>>> + * Some architecture might not support this feature.
>>> + * So the device node might not exist.
>>> + */
>>> + dev_err(dev, "sysreg regmap lookup failed.\n");
>>> + return 0;
>>
>> return 0?
>>
>>> + }
>>> +
>>> + /* Set access context for io coherency */
>>> + ret = exynos_ufs_set_context_for_access(dev, "ufs-dma-coherency",
>>> + &ufs->cxt_coherency);
>>> + if (ret == 1) {
>>> + /* no device node, default */
>>> + ufs->cxt_coherency.offset = 0x0700;
>>> + ufs->cxt_coherency.mask = 0x300; /* bit 8,9 */
>>> + ufs->cxt_coherency.val = 0x3;
>>> + ret = 0;
>>> + }
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static int exynos_ufs_get_pwr_mode(struct device_node *np,
>>> + struct exynos_ufs *ufs)
>>> +{
>>> + struct uic_pwr_mode *pmd = &ufs->req_pmd_parm;
>>> +
>>> + pmd->mode = FAST_MODE;
>>> +
>>> + if (of_property_read_u8(np, "ufs,pmd-attr-lane", &pmd->lane))
>>> + pmd->lane = 1;
>>> +
>>> + if (of_property_read_u8(np, "ufs,pmd-attr-gear", &pmd->gear))
>>> + pmd->gear = 1;
>>> +
>>> + pmd->hs_series = PA_HS_MODE_B;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int exynos_ufs_populate_dt(struct device *dev, struct
>>> +exynos_ufs *ufs) {
>>> + struct device_node *np = dev->of_node;
>>> + int ret;
>>> +
>>> + /* Get exynos-specific version for featuring */
>>> + if (of_property_read_u32(np, "hw-rev", &ufs->hw_rev))
>>> + ufs->hw_rev = UFS_VER_0004;
>>> +
>>> + ret = exynos_ufs_populate_dt_phy(dev, ufs);
>>> + if (ret) {
>>> + dev_err(dev, "failed to populate dt-phy\n");
>>> + goto out;
>>> + }
>>> +
>>> + ret = exynos_ufs_populate_dt_system(dev, ufs);
>>> + if (ret) {
>>> + dev_err(dev, "failed to populate dt-pmu\n");
>>> + goto out;
>>> + }
>>> +
>>> + exynos_ufs_get_pwr_mode(np, ufs);
>>> +
>>> +out:
>>> + return ret;
>>> +}
>>> +
>>> +static u64 exynos_ufs_dma_mask = DMA_BIT_MASK(32);
>>> +
>>> +static int exynos_ufs_probe(struct platform_device *pdev) {
>>> + struct device *dev = &pdev->dev;
>>> + struct exynos_ufs *ufs;
>>> + struct resource *res;
>>> + int ret;
>>> +
>>> + ufs = devm_kzalloc(dev, sizeof(*ufs), GFP_KERNEL);
>>> + if (!ufs) {
>>> + dev_err(dev, "cannot allocate mem for exynos-ufs\n");
>>> + return -ENOMEM;
>>> + }
>>> +
>>> + /* exynos-specific hci */
>>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>>> + ufs->reg_hci = devm_ioremap_resource(dev, res);
>>> + if (!ufs->reg_hci) {
>>> + dev_err(dev, "cannot ioremap for hci vendor register\n");
>>> + return -ENOMEM;
>>> + }
>>> +
>>> + /* unipro */
>>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
>>> + ufs->reg_unipro = devm_ioremap_resource(dev, res);
>>> + if (!ufs->reg_unipro) {
>>> + dev_err(dev, "cannot ioremap for unipro register\n");
>>> + return -ENOMEM;
>>> + }
>>> +
>>> + /* This must be before calling exynos_ufs_populate_dt */
>>> + ret = ufs_init_cal(ufs, ufs_host_index, pdev);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + ret = exynos_ufs_populate_dt(dev, ufs);
>>> + if (ret) {
>>> + dev_err(dev, "failed to get dt info.\n");
>>> + return ret;
>>> + }
>>> +
>>> + ufs->dev = dev;
>>> + dev->platform_data = ufs;
>>> + dev->dma_mask = &exynos_ufs_dma_mask;
>>> +
>>> + ret = ufshcd_pltfrm_init(pdev, &exynos_ufs_ops);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static int exynos_ufs_remove(struct platform_device *pdev) {
>>> + struct ufs_hba *hba = platform_get_drvdata(pdev);
>>> + struct exynos_ufs *ufs = dev_get_platdata(&pdev->dev);
>>> +
>>> + ufshcd_remove(hba);
>>> +
>>> + ufs->misc_flags = EXYNOS_UFS_MISC_TOGGLE_LOG;
>>> +
>>> + exynos_ufs_ctrl_phy_pwr(ufs, false);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +#ifdef CONFIG_PM_SLEEP
>>> +static int exynos_ufs_suspend(struct device *dev) {
>>> + struct ufs_hba *hba = dev_get_drvdata(dev);
>>> +
>>> + return ufshcd_system_suspend(hba);
>>> +}
>>> +
>>> +static int exynos_ufs_resume(struct device *dev) {
>>> + struct ufs_hba *hba = dev_get_drvdata(dev);
>>> +
>>> + return ufshcd_system_resume(hba);
>>> +}
>>> +#else
>>> +#define exynos_ufs_suspend NULL
>>> +#define exynos_ufs_resume NULL
>>> +#endif /* CONFIG_PM_SLEEP */
>>> +
>>> +#ifdef CONFIG_PM_RUNTIME
>>> +static int exynos_ufs_runtime_suspend(struct device *dev) {
>>> + return ufshcd_system_suspend(dev_get_drvdata(dev));
>>> +}
>>> +
>>> +static int exynos_ufs_runtime_resume(struct device *dev) {
>>> + return ufshcd_system_resume(dev_get_drvdata(dev));
>>> +}
>>> +
>>> +static int exynos_ufs_runtime_idle(struct device *dev) {
>>> + return ufshcd_runtime_idle(dev_get_drvdata(dev));
>>> +}
>>> +
>>> +#else
>>> +#define exynos_ufs_runtime_suspend NULL
>>> +#define exynos_ufs_runtime_resume NULL
>>> +#define exynos_ufs_runtime_idle NULL
>>> +#endif /* CONFIG_PM_RUNTIME */
>>> +
>>> +static void exynos_ufs_shutdown(struct platform_device *pdev) {
>>> + ufshcd_shutdown((struct ufs_hba *)platform_get_drvdata(pdev)); }
>>> +
>>> +static const struct dev_pm_ops exynos_ufs_dev_pm_ops = {
>>> + .suspend = exynos_ufs_suspend,
>>> + .resume = exynos_ufs_resume,
>>> + .runtime_suspend = exynos_ufs_runtime_suspend,
>>> + .runtime_resume = exynos_ufs_runtime_resume,
>>> + .runtime_idle = exynos_ufs_runtime_idle,
>>> +};
>>> +
>>> +static const struct of_device_id exynos_ufs_match[] = {
>>> + { .compatible = "samsung,exynos-ufs", },
>>> + {},
>>> +};
>>> +MODULE_DEVICE_TABLE(of, exynos_ufs_match);
>>> +
>>> +static struct platform_driver exynos_ufs_driver = {
>>> + .driver = {
>>> + .name = "exynos-ufs",
>>> + .owner = THIS_MODULE,
>>> + .pm = &exynos_ufs_dev_pm_ops,
>>> + .of_match_table = exynos_ufs_match,
>>> + .suppress_bind_attrs = true,
>>> + },
>>> + .probe = exynos_ufs_probe,
>>> + .remove = exynos_ufs_remove,
>>> + .shutdown = exynos_ufs_shutdown,
>>> +};
>>> +
>>> +module_platform_driver(exynos_ufs_driver);
>>> +MODULE_DESCRIPTION("Exynos Specific UFSHCI driver");
>>> +MODULE_AUTHOR("Seungwon Jeon <tgih.jun@samsung.com>");
>>> +MODULE_AUTHOR("Kiwoong Kim <kwmad.kim@samsung.com>");
>>> +MODULE_LICENSE("GPL");
>>> diff --git a/drivers/scsi/ufs/ufs-exynos.h
>>> b/drivers/scsi/ufs/ufs-exynos.h new file mode 100644 index
>>> 000000000000..0480fc4a8931
>>> --- /dev/null
>>> +++ b/drivers/scsi/ufs/ufs-exynos.h
>>> @@ -0,0 +1,351 @@
>>> +/*
>>> + * UFS Host Controller driver for Exynos specific extensions
>>> + *
>>> + * Copyright (C) 2013-2014 Samsung Electronics Co., Ltd.
>>> + *
>>> + * This program is free software; you can redistribute it and/or
>>> +modify
>>> + * it under the terms of the GNU General Public License as published
>>> +by
>>> + * the Free Software Foundation; either version 2 of the License, or
>>> + * (at your option) any later version.
>>> + */
>>> +
>>> +#ifndef _UFS_EXYNOS_H_
>>> +#define _UFS_EXYNOS_H_
>>> +
>>> +#define UFS_VER_0004 4
>>> +#define UFS_VER_0005 5
>>> +
>>> +/*
>>> + * Exynos's Vendor specific registers for UFSHCI */
>>> +#define HCI_TXPRDT_ENTRY_SIZE 0x00
>>> +#define HCI_RXPRDT_ENTRY_SIZE 0x04
>>> +#define HCI_TO_CNT_DIV_VAL 0x08
>>> +#define HCI_1US_TO_CNT_VAL 0x0C
>>> + #define CNT_VAL_1US_MASK 0x3ff
>>> +#define HCI_INVALID_UPIU_CTRL 0x10
>>> +#define HCI_INVALID_UPIU_BADDR 0x14
>>> +#define HCI_INVALID_UPIU_UBADDR 0x18
>>> +#define HCI_INVALID_UTMR_OFFSET_ADDR 0x1C
>>> +#define HCI_INVALID_UTR_OFFSET_ADDR 0x20
>>> +#define HCI_INVALID_DIN_OFFSET_ADDR 0x24
>>> +#define HCI_VENDOR_SPECIFIC_IS 0x38
>>> +#define HCI_VENDOR_SPECIFIC_IE 0x3C
>>> +#define HCI_UTRL_NEXUS_TYPE 0x40
>>> +#define HCI_UTMRL_NEXUS_TYPE 0x44
>>> +#define HCI_E2EFC_CTRL 0x48
>>> +#define HCI_SW_RST 0x50
>>> + #define UFS_LINK_SW_RST (1 << 0)
>>> + #define UFS_UNIPRO_SW_RST (1 << 1)
>>> + #define UFS_SW_RST_MASK (UFS_UNIPRO_SW_RST | UFS_LINK_SW_RST)
>>> +#define HCI_LINK_VERSION 0x54
>>> +#define HCI_IDLE_TIMER_CONFIG 0x58
>>> +#define HCI_RX_UPIU_MATCH_ERROR_CODE 0x5C
>>> +#define HCI_DATA_REORDER 0x60
>>> +#define HCI_MAX_DOUT_DATA_SIZE 0x64
>>> +#define HCI_UNIPRO_APB_CLK_CTRL 0x68
>>> +#define HCI_AXIDMA_RWDATA_BURST_LEN 0x6C
>>> + #define BURST_LEN(x) ((x) << 27 | (x))
>>> + #define WLU_EN (1 << 31)
>>
>> Use BIT(31)
>>
>>> + #define AXIDMA_RWDATA_BURST_LEN (0xF)
>>> +#define HCI_GPIO_OUT 0x70
>>> +#define HCI_WRITE_DMA_CTRL 0x74
>>> +#define HCI_ERROR_EN_PA_LAYER 0x78
>>> +#define HCI_ERROR_EN_DL_LAYER 0x7C
>>> +#define HCI_ERROR_EN_N_LAYER 0x80
>>> +#define HCI_ERROR_EN_T_LAYER 0x84
>>> +#define HCI_ERROR_EN_DME_LAYER 0x88
>>> +#define HCI_UFSHCI_V2P1_CTRL 0X8C
>>> +#define IA_TICK_SEL BIT(16)
>>> +#define HCI_REQ_HOLD_EN 0xAC
>>> +
>>> +#define HCI_CLKSTOP_CTRL 0xB0
>>> + #define REFCLKOUT_STOP BIT(4)
>>> + #define MPHY_APBCLK_STOP BIT(3)
>>> + #define REFCLK_STOP BIT(2)
>>> + #define UNIPRO_MCLK_STOP BIT(1)
>>> + #define UNIPRO_PCLK_STOP BIT(0)
>>> + #define CLK_STOP_ALL (REFCLKOUT_STOP |\
>>> + REFCLK_STOP |\
>>> + UNIPRO_MCLK_STOP |\
>>> + UNIPRO_PCLK_STOP)
>>> +
>>> +#define HCI_FORCE_HCS 0xB4
>>> + #define REFCLKOUT_STOP_EN BIT(11)
>>> + #define MPHY_APBCLK_STOP_EN BIT(10)
>>> + #define UFSP_DRCG_EN BIT(8)
>>> + #define REFCLK_STOP_EN BIT(7)
>>> + #define UNIPRO_PCLK_STOP_EN BIT(6)
>>> + #define UNIPRO_MCLK_STOP_EN BIT(5)
>>> + #define HCI_CORECLK_STOP_EN BIT(4)
>>> + #define CLK_STOP_CTRL_EN_ALL (UFSP_DRCG_EN |\
>>> + MPHY_APBCLK_STOP_EN |\
>>> + REFCLKOUT_STOP_EN |\
>>> + REFCLK_STOP_EN |\
>>> + UNIPRO_PCLK_STOP_EN |\
>>> + UNIPRO_MCLK_STOP_EN)
>>> +
>>> +#define HCI_FSM_MONITOR 0xC0
>>> +#define HCI_PRDT_HIT_RATIO 0xC4
>>> +#define HCI_DMA0_MONITOR_STATE 0xC8
>>> +#define HCI_DMA0_MONITOR_CNT 0xCC
>>> +#define HCI_DMA1_MONITOR_STATE 0xD0
>>> +#define HCI_DMA1_MONITOR_CNT 0xD4
>>> +
>>> +#define HCI_UFS_AXI_DMA_IF_CTRL 0xF8
>>> +#define HCI_UFS_ACG_DISABLE 0xFC
>>> + #define HCI_UFS_ACG_DISABLE_EN BIT(0)
>>> +#define HCI_IOP_ACG_DISABLE 0x100
>>> + #define HCI_IOP_ACG_DISABLE_EN BIT(0)
>>> +#define HCI_MPHY_REFCLK_SEL 0x108
>>> + #define MPHY_REFCLK_SEL BIT(0)
>>> +
>>> +/* Device fatal error */
>>> +#define DFES_ERR_EN BIT(31)
>>> +#define DFES_DEF_DL_ERRS (UIC_DATA_LINK_LAYER_ERROR_RX_BUF_OF |\
>>> + UIC_DATA_LINK_LAYER_ERROR_PA_INIT)
>>> +#define DFES_DEF_N_ERRS (UIC_NETWORK_UNSUPPORTED_HEADER_TYPE |\
>>> + UIC_NETWORK_BAD_DEVICEID_ENC |\
>>> + UIC_NETWORK_LHDR_TRAP_PACKET_DROPPING)
>>> +#define DFES_DEF_T_ERRS (UIC_TRANSPORT_UNSUPPORTED_HEADER_TYPE
>> |\
>>> + UIC_TRANSPORT_UNKNOWN_CPORTID |\
>>> + UIC_TRANSPORT_NO_CONNECTION_RX |\
>>> + UIC_TRANSPORT_BAD_TC)
>>> +
>>> +/* TXPRDT defines */
>>> +#define PRDT_PREFECT_EN BIT(31)
>>> +#define PRDT_SET_SIZE(x) ((x) & 0x1F)
>>> +
>>> +enum {
>>> + UNIP_PA_LYR = 0,
>>> + UNIP_DL_LYR,
>>> + UNIP_N_LYR,
>>> + UNIP_T_LYR,
>>> + UNIP_DME_LYR,
>>> +};
>>> +
>>> +/*
>>> + * UNIPRO registers
>>> + */
>>> +#define UNIP_COMP_VERSION 0x000
>>> +#define UNIP_COMP_INFO 0x004
>>> +#define UNIP_COMP_RESET 0x010
>>> +
>>> +#define UNIP_DME_POWERON_REQ 0x7800
>>> +#define UNIP_DME_POWERON_CNF_RESULT 0x7804
>>> +#define UNIP_DME_POWEROFF_REQ 0x7810
>>> +#define UNIP_DME_POWEROFF_CNF_RESULT 0x7814
>>> +#define UNIP_DME_RESET_REQ 0x7820
>>> +#define UNIP_DME_RESET_REQ_LEVEL 0x7824
>>> +#define UNIP_DME_ENABLE_REQ 0x7830
>>> +#define UNIP_DME_ENABLE_CNF_RESULT 0x7834
>>> +#define UNIP_DME_ENDPOINTRESET_REQ 0x7840
>>> +#define UNIP_DME_ENDPOINTRESET_CNF_RESULT 0x7844
>>> +#define UNIP_DME_LINKSTARTUP_REQ 0x7850
>>> +#define UNIP_DME_LINKSTARTUP_CNF_RESULT 0x7854
>>> +#define UNIP_DME_HIBERN8_ENTER_REQ 0x7860
>>> +#define UNIP_DME_HIBERN8_ENTER_CNF_RESULT 0x7864
>>> +#define UNIP_DME_HIBERN8_ENTER_IND_RESULT 0x7868
>>> +#define UNIP_DME_HIBERN8_EXIT_REQ 0x7870
>>> +#define UNIP_DME_HIBERN8_EXIT_CNF_RESULT 0x7874
>>> +#define UNIP_DME_HIBERN8_EXIT_IND_RESULT 0x7878
>>> +#define UNIP_DME_PWR_REQ 0x7880
>>> +#define UNIP_DME_PWR_REQ_POWERMODE 0x7884
>>> +#define UNIP_DME_PWR_REQ_LOCALL2TIMER0 0x7888
>>> +#define UNIP_DME_PWR_REQ_LOCALL2TIMER1 0x788C
>>> +#define UNIP_DME_PWR_REQ_LOCALL2TIMER2 0x7890
>>> +#define UNIP_DME_PWR_REQ_REMOTEL2TIMER0 0x78B8
>>> +#define UNIP_DME_PWR_REQ_REMOTEL2TIMER1 0x78BC
>>> +#define UNIP_DME_PWR_REQ_REMOTEL2TIMER2 0x78C0
>>> +#define UNIP_DME_PWR_CNF_RESULT 0x78E8
>>> +#define UNIP_DME_PWR_IND_RESULT 0x78EC
>>> +#define UNIP_DME_TEST_MODE_REQ 0x7900
>>> +#define UNIP_DME_TEST_MODE_CNF_RESULT 0x7904
>>> +
>>> +#define UNIP_DME_ERROR_IND_LAYER 0x0C0
>>> +#define UNIP_DME_ERROR_IND_ERRCODE 0x0C4
>>> +#define UNIP_DME_PACP_CNFBIT 0x0C8
>>> +#define UNIP_DME_DL_FRAME_IND 0x0D0
>>> +#define UNIP_DME_INTR_STATUS 0x0E0
>>> +#define UNIP_DME_INTR_ENABLE 0x0E4
>>> +
>>> +#define UNIP_DME_GETSET_CONTROL 0x7A00
>>> +#define UNIP_DME_GETSET_ADDR 0x7A04
>>> +#define UNIP_DME_GETSET_WDATA 0x7A08
>>> +#define UNIP_DME_GETSET_RDATA 0x7A0C
>>> +#define UNIP_DME_GETSET_RESULT 0x7A10
>>> +#define UNIP_DME_PEER_GETSET_CONTROL 0x7A20
>>> +#define UNIP_DME_PEER_GETSET_ADDR 0x7A24
>>> +#define UNIP_DME_PEER_GETSET_WDATA 0x7A28
>>> +#define UNIP_DME_PEER_GETSET_RDATA 0x7A2C
>>> +#define UNIP_DME_PEER_GETSET_RESULT 0x7A30
>>> +
>>> +#define UNIP_DME_INTR_STATUS_LSB 0x7B00
>>> +#define UNIP_DME_INTR_STATUS_MSB 0x7B04
>>> +#define UNIP_DME_INTR_ERROR_CODE 0x7B20
>>> +#define UNIP_DME_DISCARD_PORT_ID 0x7B24
>>> +#define UNIP_DME_DBG_OPTION_SUITE 0x7C00
>>> +#define UNIP_DME_DBG_CTRL_FSM 0x7D00
>>> +#define UNIP_DME_DBG_FLAG_STATUS 0x7D14
>>> +#define UNIP_DME_DBG_LINKCFG_FSM 0x7D18
>>> +
>>> +#define UNIP_DME_INTR_ERROR_CODE 0x7B20
>>> +#define UNIP_DME_DEEPSTALL_ENTER_REQ 0x7910
>>> +#define UNIP_DME_DISCARD_CPORT_ID 0x7B24
>>> +
>>> +#define UNIP_DBG_FORCE_DME_CTRL_STATE 0x150
>>> +#define UNIP_DBG_AUTO_DME_LINKSTARTUP 0x158
>>> +#define UNIP_DBG_PA_CTRLSTATE 0x15C
>>> +#define UNIP_DBG_PA_TX_STATE 0x160
>>> +#define UNIP_DBG_BREAK_DME_CTRL_STATE 0x164
>>> +#define UNIP_DBG_STEP_DME_CTRL_STATE 0x168
>>> +#define UNIP_DBG_NEXT_DME_CTRL_STATE 0x16C
>>> +
>>> +/*
>>> + * Driver specific definitions
>>> + */
>>> +struct exynos_ufs_phy {
>>> + void __iomem *reg_pma;
>>> +};
>>> +
>>> +struct exynos_ufs_clk_info {
>>> + struct list_head list;
>>> + struct clk *clk;
>>> + const char *name;
>>> + u32 freq;
>>> +};
>>> +
>>> +struct exynos_ufs_misc_log {
>>> + struct list_head clk_list_head;
>>> + bool isolation;
>>> +};
>>> +
>>> +struct exynos_ufs_sfr_log {
>>> + const char* name;
>>> + const u32 offset;
>>> +#define LOG_STD_HCI_SFR 0xFFFFFFF0
>>> +#define LOG_VS_HCI_SFR 0xFFFFFFF1
>>> +#define LOG_FMP_SFR 0xFFFFFFF2
>>> +#define LOG_UNIPRO_SFR 0xFFFFFFF3
>>> +#define LOG_PMA_SFR 0xFFFFFFF4
>>> + u32 val;
>>> +};
>>> +
>>> +struct exynos_ufs_attr_log {
>>> + const u32 offset;
>>> + u32 res;
>>> + u32 val;
>>> +};
>>> +
>>> +struct exynos_ufs_perf {
>>> + u32 opcode; /* 0: read, 1: write */
>>> + u32 chunk_size;
>>> + ktime_t time;
>>> + u64 total_time;
>>> + u32 count;
>>> + u32 total_count;
>>> +};
>>> +
>>> +/* Main structure for debug and performance */ struct
>>> +exynos_ufs_debug {
>>> + struct exynos_ufs_sfr_log* std_sfr;
>>> + struct exynos_ufs_sfr_log* sfr;
>>> + struct exynos_ufs_attr_log* attr;
>>> + struct exynos_ufs_misc_log misc;
>>> + struct exynos_ufs_perf perf;
>>> +};
>>> +
>>> +struct exynos_access_cxt {
>>> + u32 offset;
>>> + u32 mask;
>>> + u32 val;
>>> +};
>>> +
>>> +struct uic_pwr_mode {
>>> + u8 lane;
>>> + u8 gear;
>>> + u8 mode;
>>> + u8 hs_series;
>>> +};
>>> +
>>> +struct exynos_ufs {
>>> + struct device *dev;
>>> + struct ufs_hba *hba;
>>> +
>>> + void __iomem *reg_hci;
>>> + void __iomem *reg_unipro;
>>> +
>>> + struct regmap *pmureg;
>>> + struct regmap *sysreg;
>>> +
>>> + struct clk *clk_hci;
>>> + struct clk *pclk;
>>> + struct clk *clk_unipro;
>>> + u32 mclk_rate;
>>> +
>>> + int num_rx_lanes;
>>> + int num_tx_lanes;
>>> +
>>> + struct exynos_ufs_phy phy;
>>> + struct uic_pwr_mode req_pmd_parm;
>>> + struct uic_pwr_mode act_pmd_parm;
>>> +
>>> + u32 rx_min_actv_time_cap;
>>> + u32 rx_hibern8_time_cap;
>>> + u32 tx_hibern8_time_cap;
>>> +
>>> + /* for miscellaneous control */
>>> + u32 misc_flags;
>>> +#define EXYNOS_UFS_MISC_TOGGLE_LOG BIT(0)
>>> +
>>> + struct exynos_ufs_debug debug;
>>> +
>>> + u32 hw_rev;
>>> +
>>> + struct exynos_access_cxt cxt_iso; /* phy isolation */
>>> + struct exynos_access_cxt cxt_coherency; /* io coherency */
>>> +};
>>> +
>>> +static inline struct exynos_ufs *to_exynos_ufs(struct ufs_hba *hba) {
>>> + return dev_get_platdata(hba->dev);
>>> +}
>>> +
>>> +#ifndef __EXYNOS_UFS_MMIO_FUNC__
>>> +#define __EXYNOS_UFS_MMIO_FUNC__
>>
>> I don't understand..why need to check "ifndef __EXYNOS_UFS_MMIO_FUNC__".
>> (because i didnt find __EXYNOS_UFS_MMIO_FUNC__ anywhere.) It means that
>> you want to define always, doesn't?
>>
>>> +#define EXYNOS_UFS_MMIO_FUNC(name) \
>>> +static inline void name##_writel(struct exynos_ufs *ufs, u32 val, u32
>> reg) \
>>> +{ \
>>> + writel(val, ufs->reg_##name + reg); \
>>> +} \
>>> + \
>>> +static inline u32 name##_readl(struct exynos_ufs *ufs, u32 reg)
>> \
>>> +{ \
>>> + return readl(ufs->reg_##name + reg); \
>>> +}
>>> +
>>> +EXYNOS_UFS_MMIO_FUNC(hci);
>>> +EXYNOS_UFS_MMIO_FUNC(unipro);
>>
>> Maybe.. i don't like this style..because it's too difficult to debug and
>> find the function.
>>
>>> +
>>> +static inline void phy_pma_writel(struct exynos_ufs *ufs, u32 val,
>>> +u32 reg) {
>>> + u32 reg1 = hci_readl(ufs, HCI_CLKSTOP_CTRL);
>>> +
>>> + hci_writel(ufs, reg1 & ~MPHY_APBCLK_STOP, HCI_CLKSTOP_CTRL);
>>> + writel(val, ufs->phy.reg_pma + reg);
>>> + hci_writel(ufs, reg1 | MPHY_APBCLK_STOP, HCI_CLKSTOP_CTRL); }
>>> +
>>> +static inline u32 phy_pma_readl(struct exynos_ufs *ufs, u32 reg) {
>>> + u32 reg1 = hci_readl(ufs, HCI_CLKSTOP_CTRL);
>>> +
>>> + hci_writel(ufs, reg1 & ~MPHY_APBCLK_STOP, HCI_CLKSTOP_CTRL);
>>> + reg = readl(ufs->phy.reg_pma + reg);
>>> + hci_writel(ufs, reg1 | MPHY_APBCLK_STOP, HCI_CLKSTOP_CTRL);
>>> +
>>> + return reg;
>>> +}
>>> +#endif
>>> +
>>> +#endif /* _UFS_EXYNOS_H_ */
>>> diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
>>> index 1332e544da92..1afd5ac9707c 100644
>>> --- a/drivers/scsi/ufs/ufshcd.h
>>> +++ b/drivers/scsi/ufs/ufshcd.h
>>> @@ -308,6 +308,7 @@ struct ufs_hba_variant_ops {
>>> int (*setup_clocks)(struct ufs_hba *, bool,
>>> enum ufs_notify_change_status);
>>> int (*setup_regulators)(struct ufs_hba *, bool);
>>> + void (*host_reset)(struct ufs_hba *);
>>> int (*hce_enable_notify)(struct ufs_hba *,
>>> enum ufs_notify_change_status);
>>> int (*link_startup_notify)(struct ufs_hba *,
>>>
>>
>
>
>
>
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] scsi: ufs: add Exynos-specific driver
2017-11-28 5:36 ` [PATCH 2/2] scsi: ufs: add Exynos-specific driver 김기웅
2017-11-28 8:19 ` Jaehoon Chung
@ 2017-11-28 14:24 ` Christoph Hellwig
2017-11-30 7:46 ` 김기웅
1 sibling, 1 reply; 8+ messages in thread
From: Christoph Hellwig @ 2017-11-28 14:24 UTC (permalink / raw)
To: 김기웅
Cc: linux-scsi, Martin K. Petersen, cpgs, HeonGwang Chu,
김부진, YOUNGEUN PARK
On Tue, Nov 28, 2017 at 02:36:31PM +0900, 김기웅 wrote:
> This driver is to use UFS devices on Exynos SoC and
> has been already used for many years for commercial products.
So why do you only submit it only now?
> +/* Helper for UFS CAL interface */
> +static inline int ufs_init_cal(struct exynos_ufs *ufs, int idx,
> + struct platform_device *pdev)
> +{
> + return 0;
> +}
> +
> +static inline int ufs_pre_link(struct exynos_ufs *ufs)
> +{
> + return 0;
> +}
> +
> +static inline int ufs_post_link(struct exynos_ufs *ufs)
> +{
> + return 0;
> +}
> +
> +static inline int ufs_pre_gear_change(struct exynos_ufs *ufs,
> + struct uic_pwr_mode *pmd)
> +{
> + return 0;
> +}
> +
> +static inline int ufs_post_gear_change(struct exynos_ufs *ufs)
> +{
> + return 0;
> +}
> +
> +static inline int ufs_post_h8_enter(struct exynos_ufs *ufs)
> +{
> + return 0;
> +}
> +
> +static inline int ufs_pre_h8_exit(struct exynos_ufs *ufs)
> +{
> + return 0;
> +}
These are all dummys, please rmeove them.
> +#ifndef __EXYNOS_UFS_VS_DEBUG__
Please don't have ifdef code that isn't Kconfig selectable.
> +#ifndef __EXYNOS_UFS_MMIO_FUNC__
> +#define __EXYNOS_UFS_MMIO_FUNC__
> +#define EXYNOS_UFS_MMIO_FUNC(name) \
> +static inline void name##_writel(struct exynos_ufs *ufs, u32 val, u32 reg) \
> +{ \
> + writel(val, ufs->reg_##name + reg); \
> +} \
> + \
> +static inline u32 name##_readl(struct exynos_ufs *ufs, u32 reg) \
> +{ \
> + return readl(ufs->reg_##name + reg); \
> +}
> +
> +EXYNOS_UFS_MMIO_FUNC(hci);
> +EXYNOS_UFS_MMIO_FUNC(unipro);
Please remove this macro magic.
> diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
> index 1332e544da92..1afd5ac9707c 100644
> --- a/drivers/scsi/ufs/ufshcd.h
> +++ b/drivers/scsi/ufs/ufshcd.h
> @@ -308,6 +308,7 @@ struct ufs_hba_variant_ops {
> int (*setup_clocks)(struct ufs_hba *, bool,
> enum ufs_notify_change_status);
> int (*setup_regulators)(struct ufs_hba *, bool);
> + void (*host_reset)(struct ufs_hba *);
> int (*hce_enable_notify)(struct ufs_hba *,
> enum ufs_notify_change_status);
> int (*link_startup_notify)(struct ufs_hba *,
New ufs core methods should be added in a separate patch with
a good description, and also with actual callers using them.
^ permalink raw reply [flat|nested] 8+ messages in thread
* RE: [PATCH 2/2] scsi: ufs: add Exynos-specific driver
2017-11-28 14:24 ` Christoph Hellwig
@ 2017-11-30 7:46 ` 김기웅
0 siblings, 0 replies; 8+ messages in thread
From: 김기웅 @ 2017-11-30 7:46 UTC (permalink / raw)
To: 'Christoph Hellwig'
Cc: linux-scsi, 'Martin K. Petersen', cpgs,
'HeonGwang Chu', '김부진',
'YOUNGEUN PARK'
Dear Christoph
Thank you for your comments.
> -----Original Message-----
> From: linux-scsi-owner@vger.kernel.org [mailto:linux-scsi-
> owner@vger.kernel.org] On Behalf Of Christoph Hellwig
> Sent: Tuesday, November 28, 2017 11:25 PM
> To: 김기웅
> Cc: linux-scsi@vger.kernel.org; Martin K. Petersen; cpgs@samsung.com;
> HeonGwang Chu; 김부진; YOUNGEUN PARK
> Subject: Re: [PATCH 2/2] scsi: ufs: add Exynos-specific driver
>
> On Tue, Nov 28, 2017 at 02:36:31PM +0900, 김기웅 wrote:
> > This driver is to use UFS devices on Exynos SoC and has been already
> > used for many years for commercial products.
>
> So why do you only submit it only now?
The 1st author of this didn't complete submitting it.
And then following products has been coming and I need time to organize this.
>
> > +/* Helper for UFS CAL interface */
> > +static inline int ufs_init_cal(struct exynos_ufs *ufs, int idx,
> > + struct platform_device *pdev)
> > +{
> > + return 0;
> > +}
> > +
> > +static inline int ufs_pre_link(struct exynos_ufs *ufs) {
> > + return 0;
> > +}
> > +
> > +static inline int ufs_post_link(struct exynos_ufs *ufs) {
> > + return 0;
> > +}
> > +
> > +static inline int ufs_pre_gear_change(struct exynos_ufs *ufs,
> > + struct uic_pwr_mode *pmd)
> > +{
> > + return 0;
> > +}
> > +
> > +static inline int ufs_post_gear_change(struct exynos_ufs *ufs) {
> > + return 0;
> > +}
> > +
> > +static inline int ufs_post_h8_enter(struct exynos_ufs *ufs) {
> > + return 0;
> > +}
> > +
> > +static inline int ufs_pre_h8_exit(struct exynos_ufs *ufs) {
> > + return 0;
> > +}
>
> These are all dummys, please rmeove them.
>
> > +#ifndef __EXYNOS_UFS_VS_DEBUG__
>
> Please don't have ifdef code that isn't Kconfig selectable.
Okay.
>
> > +#ifndef __EXYNOS_UFS_MMIO_FUNC__
> > +#define __EXYNOS_UFS_MMIO_FUNC__
> > +#define EXYNOS_UFS_MMIO_FUNC(name) \
> > +static inline void name##_writel(struct exynos_ufs *ufs, u32 val, u32
> reg) \
> > +{ \
> > + writel(val, ufs->reg_##name + reg); \
> > +} \
> > + \
> > +static inline u32 name##_readl(struct exynos_ufs *ufs, u32 reg)
> \
> > +{ \
> > + return readl(ufs->reg_##name + reg); \
> > +}
> > +
> > +EXYNOS_UFS_MMIO_FUNC(hci);
> > +EXYNOS_UFS_MMIO_FUNC(unipro);
>
> Please remove this macro magic.
Could you explain an intention of this comment?
Because this driver needs MMIO-based accesses for multi regions?
For example, is this kind of a rule not to use macro?
>
> > diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
> > index 1332e544da92..1afd5ac9707c 100644
> > --- a/drivers/scsi/ufs/ufshcd.h
> > +++ b/drivers/scsi/ufs/ufshcd.h
> > @@ -308,6 +308,7 @@ struct ufs_hba_variant_ops {
> > int (*setup_clocks)(struct ufs_hba *, bool,
> > enum ufs_notify_change_status);
> > int (*setup_regulators)(struct ufs_hba *, bool);
> > + void (*host_reset)(struct ufs_hba *);
> > int (*hce_enable_notify)(struct ufs_hba *,
> > enum ufs_notify_change_status);
> > int (*link_startup_notify)(struct ufs_hba *,
>
> New ufs core methods should be added in a separate patch with a good
> description, and also with actual callers using them.
Okay.
^ permalink raw reply [flat|nested] 8+ messages in thread
* RE: [PATCH 2/2] scsi: ufs: add Exynos-specific driver
2017-11-28 9:20 ` Jaehoon Chung
@ 2017-11-30 8:35 ` 김기웅
2017-11-30 23:29 ` Jaehoon Chung
0 siblings, 1 reply; 8+ messages in thread
From: 김기웅 @ 2017-11-30 8:35 UTC (permalink / raw)
To: 'Jaehoon Chung', linux-scsi, 'Martin K. Petersen'
Cc: cpgs, 'HeonGwang Chu', '김부진',
'YOUNGEUN PARK'
Dear Jaehoon
Actually, I used to use the way what you mentioned, but
now a new way has been using with latest products.
Anyway, I would refer to your comment.
Thank you.
> -----Original Message-----
> From: linux-scsi-owner@vger.kernel.org [mailto:linux-scsi-
> owner@vger.kernel.org] On Behalf Of Jaehoon Chung
> Sent: Tuesday, November 28, 2017 6:21 PM
> To: 김기웅; linux-scsi@vger.kernel.org; 'Martin K. Petersen'
> Cc: cpgs@samsung.com; 'HeonGwang Chu'; '김부진'; 'YOUNGEUN PARK'
> Subject: Re: [PATCH 2/2] scsi: ufs: add Exynos-specific driver
>
> On 11/28/2017 05:27 PM, 김기웅 wrote:
> > Hi.
> > This is modified from Seungwon's initial patch.
> > And this has been used for several commercial products.
> > I think you feel weird because you can't see a bunch of unipro and mphy.
> > But those stuff has been changed whenever new product comes.
> > So I didn't keep those in this driver.
>
> Unipro and mphy setting values can be got from device-tree according to
> each variant boards.
> Then it doesn't need to keep in this driver. but there is no usage for
> them in this patch.
> Otherwise, this driver may be a dead driver.
>
> Also anyone doesn't have the interesting about this driver.
>
> And i also added the some comment at below..
>
> Best Regards,
> Jaehoon Chung
>
> >
> >
> >> -----Original Message-----
> >> From: linux-scsi-owner@vger.kernel.org [mailto:linux-scsi-
> >> owner@vger.kernel.org] On Behalf Of Jaehoon Chung
> >> Sent: Tuesday, November 28, 2017 5:20 PM
> >> To: 김기웅; linux-scsi@vger.kernel.org; Martin K. Petersen
> >> Cc: cpgs@samsung.com; HeonGwang Chu; 김부진; YOUNGEUN PARK
> >> Subject: Re: [PATCH 2/2] scsi: ufs: add Exynos-specific driver
> >>
> >> Hi,
> >>
> >> On 11/28/2017 02:36 PM, 김기웅 wrote:
> >>> This driver is to use UFS devices on Exynos SoC and has been already
> >>> used for many years for commercial products.
> >>
> >> Well, i'm not sure but i remembered there are the similar patches
> >> before..Seungwon and Alim's patches.
> >> Is it relevant to them?
> >>
> >> Anyway.. i think i can't test with only these patches..
> >> how did you test this patches?
> >>
> >>>
> >>> Signed-off-by: Kiwoong Kim <kwmad.kim@samsung.com>
> >>> ---
> >>> drivers/scsi/ufs/Kconfig | 10 +
> >>> drivers/scsi/ufs/Makefile | 1 +
> >>> drivers/scsi/ufs/ufs-exynos.c | 962
> >>> ++++++++++++++++++++++++++++++++++++++++++
> >>> drivers/scsi/ufs/ufs-exynos.h | 351 +++++++++++++++
> >>> drivers/scsi/ufs/ufshcd.h | 1 +
> >>> 5 files changed, 1325 insertions(+) create mode 100644
> >>> drivers/scsi/ufs/ufs-exynos.c create mode 100644
> >>> drivers/scsi/ufs/ufs-exynos.h
> >>
> >> There is no binding file.
> >>
> >>>
> >>> diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig
> >>> index
> >>> e27b4d4e6ae2..7d71ad8768c3 100644
> >>> --- a/drivers/scsi/ufs/Kconfig
> >>> +++ b/drivers/scsi/ufs/Kconfig
> >>> @@ -100,3 +100,13 @@ config SCSI_UFS_QCOM
> >>>
> >>> Select this if you have UFS controller on QCOM chipset.
> >>> If unsure, say N.
> >>> +
> >>> +config SCSI_UFS_EXYNOS
> >>> + tristate "EXYNOS UFS Host Controller Driver"
> >>> + depends on SCSI_UFSHCD && SCSI_UFSHCD_PLATFORM
> >>> + ---help---
> >>> + This selects the EXYNOS UFS host controller driver.
> >>> +
> >>> + If you have a controller with this interface, say Y or M here.
> >>> +
> >>> + If unsure, say N.
> >>> diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile
> >>> index 9310c6c83041..3312b052dcff 100644
> >>> --- a/drivers/scsi/ufs/Makefile
> >>> +++ b/drivers/scsi/ufs/Makefile
> >>> @@ -3,6 +3,7 @@
> >>> obj-$(CONFIG_SCSI_UFS_DWC_TC_PCI) += tc-dwc-g210-pci.o ufshcd-dwc.o
> >>> tc-dwc-g210.o
> >>> obj-$(CONFIG_SCSI_UFS_DWC_TC_PLATFORM) += tc-dwc-g210-pltfrm.o
> >>> ufshcd-dwc.o tc-dwc-g210.o
> >>> obj-$(CONFIG_SCSI_UFS_QCOM) += ufs-qcom.o
> >>> +obj-$(CONFIG_SCSI_UFS_EXYNOS) += ufs-exynos.o
> >>> obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o
> >>> obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o
> >>> obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o diff --git
> >>> a/drivers/scsi/ufs/ufs-exynos.c b/drivers/scsi/ufs/ufs-exynos.c new
> >>> file mode 100644 index 000000000000..98e5aeb80b06
> >>> --- /dev/null
> >>> +++ b/drivers/scsi/ufs/ufs-exynos.c
> >>> @@ -0,0 +1,962 @@
> >>> +/*
> >>> + * UFS Host Controller driver for Exynos specific extensions
> >>> + *
> >>> + * Copyright (C) 2013-2014 Samsung Electronics Co., Ltd.
> >>
> >> 2013-2014? is it right?
> >>
> >>> + *
> >>> + * This program is free software; you can redistribute it and/or
> >>> +modify
> >>> + * it under the terms of the GNU General Public License as
> >>> +published by
> >>> + * the Free Software Foundation; either version 2 of the License,
> >>> +or
> >>> + * (at your option) any later version.
> >>> + */
> >>> +#include <linux/module.h>
> >>> +#include <linux/platform_device.h>
> >>> +#include <linux/of.h>
> >>> +#include <linux/of_address.h>
> >>> +#include <linux/clk.h>
> >>> +#include "ufshcd.h"
> >>> +#include "ufshcd-pltfrm.h"
> >>> +#include "ufs-exynos.h"
> >>> +#include <linux/mfd/syscon.h>
> >>> +#include <linux/regmap.h>
> >>> +#include <linux/spinlock.h>
> >>
> >> ordering about header file.
> >>
> >>> +
> >>> +/*
> >>> + * Debugging information, SFR/attributes/misc */ static struct
> >>> +exynos_ufs *ufs_host_backup[1];> +static int ufs_host_index = 0;
> >>
> >> It has to use the global? Is there any other solution?
> >>
> >>> +
> >>> +static struct exynos_ufs_sfr_log ufs_log_std_sfr[] = {
> >>> + {"CAPABILITIES" , REG_CONTROLLER_CAPABILITIES,
> >> 0},
> >>> + {"UFS VERSION" , REG_UFS_VERSION,
> >> 0},
> >>> + {"PRODUCT ID" , REG_CONTROLLER_DEV_ID,
> >> 0},
> >>> + {"MANUFACTURE ID" , REG_CONTROLLER_PROD_ID,
> >> 0},
> >>> + {"INTERRUPT STATUS" , REG_INTERRUPT_STATUS,
> >> 0},
> >>> + {"INTERRUPT ENABLE" , REG_INTERRUPT_ENABLE,
> >> 0},
> >>> + {"CONTROLLER STATUS" , REG_CONTROLLER_STATUS,
> >> 0},
> >>> + {"CONTROLLER ENABLE" , REG_CONTROLLER_ENABLE,
> >> 0},
> >>> + {"UIC ERR PHY ADAPTER LAYER" ,
> >> REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER, 0},
> >>> + {"UIC ERR DATA LINK LAYER" , REG_UIC_ERROR_CODE_DATA_LINK_LAYER,
> >> 0},
> >>> + {"UIC ERR NETWORK LATER" , REG_UIC_ERROR_CODE_NETWORK_LAYER,
> >> 0},
> >>> + {"UIC ERR TRANSPORT LAYER" , REG_UIC_ERROR_CODE_TRANSPORT_LAYER,
> >> 0},
> >>> + {"UIC ERR DME" , REG_UIC_ERROR_CODE_DME,
> >> 0},
> >>> + {"UTP TRANSF REQ INT AGG CNTRL" ,
> >> REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL, 0},
> >>> + {"UTP TRANSF REQ LIST BASE L" ,
> >> REG_UTP_TRANSFER_REQ_LIST_BASE_L, 0},
> >>> + {"UTP TRANSF REQ LIST BASE H" ,
> >> REG_UTP_TRANSFER_REQ_LIST_BASE_H, 0},
> >>> + {"UTP TRANSF REQ DOOR BELL" ,
> >> REG_UTP_TRANSFER_REQ_DOOR_BELL, 0},
> >>> + {"UTP TRANSF REQ LIST CLEAR" ,
> >> REG_UTP_TRANSFER_REQ_LIST_CLEAR, 0},
> >>> + {"UTP TRANSF REQ LIST RUN STOP" ,
> >> REG_UTP_TRANSFER_REQ_LIST_RUN_STOP, 0},
> >>> + {"UTP TASK REQ LIST BASE L" ,
> >> REG_UTP_TASK_REQ_LIST_BASE_L, 0},
> >>> + {"UTP TASK REQ LIST BASE H" ,
> >> REG_UTP_TASK_REQ_LIST_BASE_H, 0},
> >>> + {"UTP TASK REQ DOOR BELL" , REG_UTP_TASK_REQ_DOOR_BELL,
> >> 0},
> >>> + {"UTP TASK REQ LIST CLEAR" , REG_UTP_TASK_REQ_LIST_CLEAR,
> >> 0},
> >>> + {"UTP TASK REQ LIST RUN STOP" ,
> >> REG_UTP_TASK_REQ_LIST_RUN_STOP, 0},
> >>> + {"UIC COMMAND" , REG_UIC_COMMAND,
> >> 0},
> >>> + {"UIC COMMAND ARG1" , REG_UIC_COMMAND_ARG_1,
> >> 0},
> >>> + {"UIC COMMAND ARG2" , REG_UIC_COMMAND_ARG_2,
> >> 0},
> >>> + {"UIC COMMAND ARG3" , REG_UIC_COMMAND_ARG_3,
> >> 0},
> >>> +
> >>> + {},
> >>> +};
> >>> +
> >>> +/* Helper for UFS CAL interface */
> >>> +static inline int ufs_init_cal(struct exynos_ufs *ufs, int idx,
> >>> + struct platform_device *pdev)
> >>> +{
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static inline int ufs_pre_link(struct exynos_ufs *ufs) {
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static inline int ufs_post_link(struct exynos_ufs *ufs) {
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static inline int ufs_pre_gear_change(struct exynos_ufs *ufs,
> >>> + struct uic_pwr_mode *pmd)
> >>> +{
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static inline int ufs_post_gear_change(struct exynos_ufs *ufs) {
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static inline int ufs_post_h8_enter(struct exynos_ufs *ufs) {
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static inline int ufs_pre_h8_exit(struct exynos_ufs *ufs) {
> >>> + return 0;
> >>> +}
> >>
> >> What purpose has these helper fuctions?
> >>
> >>> +
> >>> +static inline void exynos_ufs_ctrl_phy_pwr(struct exynos_ufs *ufs,
> >>> +bool en) {
> >>> + int ret = 0;
> >>> +
> >>> + if (en)
> >>> + ret = regmap_update_bits(ufs->pmureg, ufs->cxt_iso.offset,
> >>> + ufs->cxt_iso.mask, ufs->cxt_iso.val);
> >>> + else
> >>> + ret = regmap_update_bits(ufs->pmureg, ufs->cxt_iso.offset,
> >>> + ufs->cxt_iso.mask, 0);
> >>> +
> >>> + if (ret)
> >>> + dev_err(ufs->dev, "Unable to update PHY ISO control\n"); }
> >>> +
> >>> +#ifndef __EXYNOS_UFS_VS_DEBUG__
> >>
> >> There is no defined "__EXYNOS_UFS_VS_DEBUG__". Remove it or include
> >> it into Kconfig.
> >>
> >>> +static void exynos_ufs_dump_std_sfr(struct ufs_hba *hba) {
> >>> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> >>> + struct exynos_ufs_sfr_log* cfg = ufs->debug.std_sfr;
> >>> +
> >>> + dev_err(hba->dev, ":
> >>> +----------------------------------------------
> >> ----- \n");
> >>> + dev_err(hba->dev, ": \t\tREGISTER DUMP\n");
> >>> + dev_err(hba->dev, ":
> >>> +--------------------------------------------------- \n");
> >>> +
> >>> + while(cfg) {
> >>> + if (!cfg->name)
> >>> + break;
> >>> + cfg->val = ufshcd_readl(hba, cfg->offset);
> >>> +
> >>> + /* Dump */
> >>> + dev_err(hba->dev, ": %s(0x%04x):\t\t\t\t0x%08x\n",
> >>> + cfg->name, cfg->offset, cfg->val);
> >>> +
> >>> + /* Next SFR */
> >>> + cfg++;
> >>> + }
> >>> +}
> >>> +#endif
> >>> +
> >>> +/*
> >>> + * Exynos debugging main function
> >>> + */
> >>> +static void exynos_ufs_dump_debug_info(struct ufs_hba *hba) {
> >>> +#ifdef __EXYNOS_UFS_VS_DEBUG__
> >>
> >> What is this? Can be remove this.
> >>
> >>> +#else
> >>> + exynos_ufs_dump_std_sfr(hba);
> >>> +#endif
> >>> +}
> >>> +
> >>> +static inline void exynos_ufs_set_hwacg_control(struct exynos_ufs
> >>> +*ufs, bool en) {
> >>> + u32 reg;
> >>> + if ((ufs->hw_rev != UFS_VER_0004) && (ufs->hw_rev != UFS_VER_0005))
> >>> + return;
> >>> +
> >>> + /*
> >>> + * default value 1->0 at KC. so,
> >>> + * need to set "1(disable HWACG)" during UFS init
> >>> + */
> >>> + reg = hci_readl(ufs, HCI_UFS_ACG_DISABLE);
> >>> + if (en)
> >>> + hci_writel(ufs, reg & (~HCI_UFS_ACG_DISABLE_EN),
> >> HCI_UFS_ACG_DISABLE);
> >>> + else
> >>> + hci_writel(ufs, reg | HCI_UFS_ACG_DISABLE_EN,
> >> HCI_UFS_ACG_DISABLE);
> >>> +
> >>> +}
> >>> +
> >>> +static inline void exynos_ufs_ctrl_auto_hci_clk(struct exynos_ufs
> >>> +*ufs, bool en) {
> >>> + u32 reg = hci_readl(ufs, HCI_FORCE_HCS);
> >>> +
> >>> + if (en)
> >>> + hci_writel(ufs, reg | HCI_CORECLK_STOP_EN, HCI_FORCE_HCS);
> >>> + else
> >>> + hci_writel(ufs, reg & ~HCI_CORECLK_STOP_EN, HCI_FORCE_HCS); }
> >>> +
> >>> +static inline void exynos_ufs_ctrl_clk(struct exynos_ufs *ufs, bool
> >>> +en) {
> >>> + u32 reg = hci_readl(ufs, HCI_FORCE_HCS);
> >>> +
> >>> + if (en)
> >>> + hci_writel(ufs, reg | CLK_STOP_CTRL_EN_ALL, HCI_FORCE_HCS);
> >>> + else
> >>> + hci_writel(ufs, reg & ~CLK_STOP_CTRL_EN_ALL,
> >> HCI_FORCE_HCS); }
> >>> +
> >>> +static inline void exynos_ufs_gate_clk(struct exynos_ufs *ufs, bool
> >>> +en) {
> >>> +
> >>> + u32 reg = hci_readl(ufs, HCI_CLKSTOP_CTRL);
> >>> +
> >>> + if (en)
> >>> + hci_writel(ufs, reg | CLK_STOP_ALL, HCI_CLKSTOP_CTRL);
> >>> + else
> >>> + hci_writel(ufs, reg & ~CLK_STOP_ALL, HCI_CLKSTOP_CTRL); }
> >>> +
> >>> +static inline void exynos_ufs_set_unipro_mclk(struct exynos_ufs
> >>> +*ufs) {
> >>> + ufs->mclk_rate = clk_get_rate(ufs->clk_unipro); }
> >>> +
> >>> +static inline void exynos_ufs_fit_aggr_timeout(struct exynos_ufs
> >>> +*ufs) {
> >>> + u32 cnt_val;
> >>> + u32 nVal;
> >>
> >> Don't use the upper character.
> >>
> >>> +
> >>> + /* IA_TICK_SEL : 1(1us_TO_CNT_VAL) */
> >>> + nVal = hci_readl(ufs, HCI_UFSHCI_V2P1_CTRL);
> >>> + nVal |= IA_TICK_SEL;
> >>> + hci_writel(ufs, nVal, HCI_UFSHCI_V2P1_CTRL);
> >>> +
> >>> + cnt_val = ufs->mclk_rate / 1000000 ;
> >>> + hci_writel(ufs, cnt_val & CNT_VAL_1US_MASK, HCI_1US_TO_CNT_VAL); }
> >>> +
> >>> +static void exynos_ufs_init_pmc_req(struct ufs_hba *hba,
> >>> + struct ufs_pa_layer_attr *pwr_max,
> >>> + struct ufs_pa_layer_attr *pwr_req) {
> >>> +
> >>> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> >>> + struct uic_pwr_mode *req_pmd = &ufs->req_pmd_parm;
> >>> + struct uic_pwr_mode *act_pmd = &ufs->act_pmd_parm;
> >>> +
> >>> + /* update lane variable after link */
> >>> + ufs->num_rx_lanes = pwr_max->lane_rx;
> >>> + ufs->num_tx_lanes = pwr_max->lane_tx;
> >>> +
> >>> + pwr_req->gear_rx
> >>> + = act_pmd->gear= min_t(u8, pwr_max->gear_rx, req_pmd->gear);
> >>> + pwr_req->gear_tx
> >>> + = act_pmd->gear = min_t(u8, pwr_max->gear_tx, req_pmd->gear);
> >>> + pwr_req->lane_rx
> >>> + = act_pmd->lane = min_t(u8, pwr_max->lane_rx, req_pmd->lane);
> >>> + pwr_req->lane_tx
> >>> + = act_pmd->lane = min_t(u8, pwr_max->lane_tx, req_pmd->lane);
> >>> + pwr_req->pwr_rx = act_pmd->mode = req_pmd->mode;
> >>> + pwr_req->pwr_tx = act_pmd->mode = req_pmd->mode;
> >>> + pwr_req->hs_rate = act_pmd->hs_series = req_pmd->hs_series; }
> >>> +
> >>> +static void exynos_ufs_config_intr(struct exynos_ufs *ufs, u32
> >>> +errs,
> >>> +u8 index) {
> >>> + switch(index) {
> >>> + case UNIP_PA_LYR:
> >>> + hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERROR_EN_PA_LAYER);
> >>> + break;
> >>> + case UNIP_DL_LYR:
> >>> + hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERROR_EN_DL_LAYER);
> >>> + break;
> >>> + case UNIP_N_LYR:
> >>> + hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERROR_EN_N_LAYER);
> >>> + break;
> >>> + case UNIP_T_LYR:
> >>> + hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERROR_EN_T_LAYER);
> >>> + break;
> >>> + case UNIP_DME_LYR:
> >>> + hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERROR_EN_DME_LAYER);
> >>> + break;
> >>> + }
> >>> +}
> >>> +
> >>> +static void exynos_ufs_dev_hw_reset(struct ufs_hba *hba) {
> >>> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> >>> +
> >>> + /* bit[1] for resetn */
> >> f> + hci_writel(ufs, 0 << 0, HCI_GPIO_OUT);
> >>
> >> your comment is "bit[1] for resetn" ..but this bit conotrol is for
> bit[0].
> >> I don't want to use "0 << 0" and " 1 << 0".
> >>
> >> #define RESETN_HIGH/LOW whatever...
> >>
> >>> + udelay(5);
> >>> + hci_writel(ufs, 1 << 0, HCI_GPIO_OUT); }
> >>> +
> >>> +static void exynos_ufs_init_host(struct exynos_ufs *ufs) {
> >>> + u32 reg;
> >>> +
> >>> + /* internal clock control */
> >>> + exynos_ufs_ctrl_auto_hci_clk(ufs, false);
> >>> + exynos_ufs_set_unipro_mclk(ufs);
> >>> +
> >>> + /* period for interrupt aggregation */
> >>> + exynos_ufs_fit_aggr_timeout(ufs);
> >>> +
> >>> + /* misc HCI configurations */
> >>> + hci_writel(ufs, 0xA, HCI_DATA_REORDER);
> >>
> >> Don't use magin number. what is 0xA?
> >>
> >>> + hci_writel(ufs, PRDT_PREFECT_EN | PRDT_SET_SIZE(12),
> >>> + HCI_TXPRDT_ENTRY_SIZE);
> >>> + hci_writel(ufs, PRDT_SET_SIZE(12), HCI_RXPRDT_ENTRY_SIZE);
> >>> + hci_writel(ufs, 0xFFFFFFFF, HCI_UTRL_NEXUS_TYPE);
> >>> + hci_writel(ufs, 0xFFFFFFFF, HCI_UTMRL_NEXUS_TYPE);
> >>
> >> Ditto.
> >>> +
> >>> + reg = hci_readl(ufs, HCI_AXIDMA_RWDATA_BURST_LEN) &
> >>> + ~BURST_LEN(0);
> >>> + hci_writel(ufs, WLU_EN | BURST_LEN(3),
> >>> + HCI_AXIDMA_RWDATA_BURST_LEN);
> >>> +
> >>> + /*
> >>> + * Enable HWAGC control by IOP
> >>> + *
> >>> + * default value 1->0 at KC.
> >>> + * always "0"(controlled by UFS_ACG_DISABLE)
> >>> + */
> >>> + reg = hci_readl(ufs, HCI_IOP_ACG_DISABLE);
> >>> + hci_writel(ufs, reg & (~HCI_IOP_ACG_DISABLE_EN),
> >>> +HCI_IOP_ACG_DISABLE); }
> >>> +
> >>> +static int exynos_ufs_init_system(struct exynos_ufs *ufs) {
> >>> + struct device *dev = ufs->dev;
> >>> + int ret = 0;
> >>> + bool is_io_coherency;
> >>> + bool is_dma_coherent;
> >>> +
> >>> + /* PHY isolation bypass */
> >>> + exynos_ufs_ctrl_phy_pwr(ufs, true);
> >>> +
> >>> + /* IO cohernecy */
> >>> + is_io_coherency = !IS_ERR(ufs->sysreg);
> >>> + is_dma_coherent = !!of_find_property(dev->of_node,
> >>> + "dma-coherent", NULL);
> >>> +
> >>> + if (is_io_coherency != is_dma_coherent)
> >>> + BUG();
> >>
> >> BUG()?
> >>
> >>> +
> >>> + if (!is_io_coherency)
> >>> + dev_err(dev, "Not configured to use IO coherency\n");
> >>> + else
> >>> + ret = regmap_update_bits(ufs->sysreg, ufs-
> >>> cxt_coherency.offset,
> >>> + ufs->cxt_coherency.mask, ufs->cxt_coherency.val);
> >>> +
> >>> + return ret;
> >>> +}
> >>> +
> >>> +static int exynos_ufs_get_clks(struct ufs_hba *hba) {
> >>> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> >>> + struct list_head *head = &hba->clk_list_head;
> >>> + struct ufs_clk_info *clki;
> >>> + int i = 0;
> >>> +
> >>> + ufs_host_backup[ufs_host_index++] = ufs;
> >>> + ufs->debug.std_sfr = ufs_log_std_sfr;
> >>> +
> >>> + if (!head || list_empty(head))
> >>> + goto out;
> >>> +
> >>> + list_for_each_entry(clki, head, list) {
> >>> + /*
> >>> + * get clock with an order listed in device tree
> >>> + */
> >>> + if (i == 0)
> >>> + ufs->clk_hci = clki->clk;
> >>> + else if (i == 1)
> >>> + ufs->clk_unipro = clki->clk;
> >>> +
> >>> + i++;
> >>> + }
> >>> +out:
> >>> + if (!ufs->clk_hci || !ufs->clk_unipro)
> >>> + return -EINVAL;
> >>> +
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static void exynos_ufs_set_features(struct ufs_hba *hba, u32
> >>> +hw_rev) {
> >>> + /* caps */
> >>> + hba->caps = UFSHCD_CAP_CLK_GATING |
> >>> + UFSHCD_CAP_HIBERN8_WITH_CLK_GATING |
> >>> + UFSHCD_CAP_INTR_AGGR;
> >>> +
> >>> + /* quirks of common driver */
> >>> + hba->quirks = UFSHCD_QUIRK_PRDT_BYTE_GRAN;
> >>> +
> >>> + /* quirks of exynos-specific driver */
> >>
> >> Remove unused comment.
> >>
> >>> +}
> >>> +
> >>> +/*
> >>> + * Exynos-specific callback functions
> >>> + *
> >>> + * init | Pure SW init & system-related init
> >>> + * host_reset | Host SW reset & init
> >>> + * ...
> >>> + *
> >>> + * Initializations for software, host controller and system
> >>> + * should be contained only in ->host_reset() as possible.
> >>> + */
> >>> +
> >>> +static int exynos_ufs_init(struct ufs_hba *hba) {
> >>> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> >>> + int ret;
> >>> +
> >>> + /* set features, such as caps or quirks */
> >>> + exynos_ufs_set_features(hba, ufs->hw_rev);
> >>> +
> >>> + /* get some clock sources and debug infomation structures */
> >>> + ret = exynos_ufs_get_clks(hba);
> >>> + if (ret)
> >>> + return ret;
> >>> +
> >>> + /* system init */
> >>> + ret = exynos_ufs_init_system(ufs);
> >>> + if (ret)
> >>> + return ret;
> >>> +
> >>> + ufs->misc_flags = EXYNOS_UFS_MISC_TOGGLE_LOG;
> >>> +
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static void exynos_ufs_host_reset(struct ufs_hba *hba) {
> >>> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> >>> + unsigned long timeout = jiffies + msecs_to_jiffies(1);
> >>> +
> >>> + exynos_ufs_ctrl_auto_hci_clk(ufs, false);
> >>> +
> >>> + hci_writel(ufs, UFS_SW_RST_MASK, HCI_SW_RST);
> >>> +
> >>> + do {
> >>> + if (!(hci_readl(ufs, HCI_SW_RST) & UFS_SW_RST_MASK))
> >>> + goto success;
> >>
> >> Need to "goto" statement? Just can be located the below code.
> >> if () {
> >> exynos_ufs_init_host();
> >> exynos_ufs_dev_hw_reset();
> >> return;
> >> }
> >>
> >>> + } while (time_before(jiffies, timeout));
> >>> +
> >>> + dev_err(ufs->dev, "timeout host sw-reset\n");
> >>> +
> >>> + goto out;
> >>
> >> Not need "goto out", instead "return" at here.
> >>
> >>> +
> >>> +success:
> >>> + /* host init */
> >>> + exynos_ufs_init_host(ufs);
> >>> +
> >>> + /* device reset */
> >>> + exynos_ufs_dev_hw_reset(hba);
> >>> +out:
> >>> + return;
> >>> +}
> >>> +
> >>> +static inline void exynos_ufs_dev_reset_ctrl(struct exynos_ufs
> >>> +*ufs, bool en) {
> >>> +
> >>> + if (en)
> >>> + hci_writel(ufs, 1 << 0, HCI_GPIO_OUT);
> >>> + else
> >>> + hci_writel(ufs, 0 << 0, HCI_GPIO_OUT);
> >>
> >> I don't know why this function is need. There is no call anywhere
> >> with "true".
> >>
> >>> +}
> >>> +
> >>> +static int exynos_ufs_setup_clocks(struct ufs_hba *hba, bool on,
> >>> + enum ufs_notify_change_status status) {
> >>> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> >>> + int ret = 0;
> >>> +
> >>> + if (status == PRE_CHANGE) {
> >>> + if (on) {
> >>> + /*
> >>> + * Now all used blocks would not be turned off in a
> >> host.
> >>> + */
> >>> + exynos_ufs_ctrl_auto_hci_clk(ufs, false);
> >>> + exynos_ufs_gate_clk(ufs, false);
> >>> +
> >>> + /* HWAGC disable */
> >>> + exynos_ufs_set_hwacg_control(ufs, false);
> >>> + } else {
> >>> + ret = ufs_post_h8_enter(ufs);
> >>> + }
> >>> + } else {
> >>> + if (on) {
> >>> + ret = ufs_pre_h8_exit(ufs);
> >>> + } else {
> >>> + /*
> >>> + * Now all used blocks would be turned off in a host.
> >>> + */
> >>> + exynos_ufs_ctrl_auto_hci_clk(ufs, true);
> >>> +
> >>> + /* HWAGC enable */
> >>> + exynos_ufs_set_hwacg_control(ufs, true);
> >>> + }
> >>> + }
> >>> +
> >>> + return ret;
> >>> +}
> >>> +
> >>> +static int exynos_ufs_link_startup_notify(struct ufs_hba *hba,
> >>> + enum ufs_notify_change_status status) {
> >>> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> >>> + int ret = 0;
> >>> +
> >>> + switch (status) {
> >>> + case PRE_CHANGE:
> >>> + /* refer to hba */
> >>> + ufs->hba = hba;
> >>> +
> >>> + /* hci */
> >>> + exynos_ufs_config_intr(ufs, DFES_DEF_DL_ERRS, UNIP_DL_LYR);
> >>> + exynos_ufs_config_intr(ufs, DFES_DEF_N_ERRS, UNIP_N_LYR);
> >>> + exynos_ufs_config_intr(ufs, DFES_DEF_T_ERRS, UNIP_T_LYR);
> >>> +
> >>> + exynos_ufs_ctrl_clk(ufs, true);
> >>> + exynos_ufs_gate_clk(ufs, false);
> >>> + exynos_ufs_set_hwacg_control(ufs, false);
> >>> +
> >>> + if (ufs->num_rx_lanes == 0 || ufs->num_tx_lanes == 0) {
> >>> + ufshcd_dme_get(hba, UIC_ARG_MIB(PA_AVAILRXDATALANES),
> >>> + &ufs->num_rx_lanes);
> >>> + ufshcd_dme_get(hba, UIC_ARG_MIB(PA_AVAILTXDATALANES),
> >>> + &ufs->num_tx_lanes);
> >>> + WARN(ufs->num_rx_lanes != ufs->num_tx_lanes,
> >>> + "available data lane is not equal(rx:%d,
> >> tx:%d)\n",
> >>> + ufs->num_rx_lanes, ufs->num_tx_lanes);
> >>> + }
> >>> +
> >>> + ufs->mclk_rate = clk_get_rate(ufs->clk_unipro);
> >>> +
> >>> + ret = ufs_pre_link(ufs);
> >>> + break;
> >>> + case POST_CHANGE:
> >>> + /* UIC configuration table after link startup */
> >>> + ret = ufs_post_link(ufs);
> >>> + break;
> >>> + default:
> >>> + break;
> >>> + }
> >>> +
> >>> + return ret;
> >>> +}
> >>> +
> >>> +static int exynos_ufs_pwr_change_notify(struct ufs_hba *hba,
> >>> + enum ufs_notify_change_status status,
> >>> + struct ufs_pa_layer_attr *pwr_max,
> >>> + struct ufs_pa_layer_attr *pwr_req) {
> >>> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> >>> + struct uic_pwr_mode *act_pmd = &ufs->act_pmd_parm;
> >>> + int ret = 0;
> >>> +
> >>> + switch (status) {
> >>> + case PRE_CHANGE:
> >>> +
> >>> + /* Set PMC parameters to be requested */
> >>> + exynos_ufs_init_pmc_req(hba, pwr_max, pwr_req);
> >>> +
> >>> + /* UIC configuration table before power mode change */
> >>> + ret = ufs_pre_gear_change(ufs, act_pmd);
> >>> +
> >>> + break;
> >>> + case POST_CHANGE:
> >>> + /* UIC configuration table after power mode change */
> >>> + ret = ufs_post_gear_change(ufs);
> >>> +
> >>> + dev_info(ufs->dev,
> >>> + "Power mode change(%d): M(%d)G(%d)L(%d)HS-
> >> series(%d)\n",
> >>> + ret, act_pmd->mode, act_pmd->gear,
> >>> + act_pmd->lane, act_pmd->hs_series);
> >>
> >> Fix the indent
> >>
> >>> + break;
> >>> + default:
> >>> + break;
> >>> + }
> >>> +
> >>> + return ret;
> >>> +}
> >>> +
> >>> +static void exynos_ufs_set_nexus_t_xfer_req(struct ufs_hba *hba,
> >>> + int tag, bool op)
> >>> +{
> >>> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> >>> + u32 type;
> >>> +
> >>> + type = hci_readl(ufs, HCI_UTRL_NEXUS_TYPE);
> >>> +
> >>> + if (op)
> >>> + type |= (1 << tag);
> >>> + else
> >>> + type &= ~(1 << tag);
> >>> +
> >>> + hci_writel(ufs, type, HCI_UTRL_NEXUS_TYPE); }
> >>> +
> >>> +static void exynos_ufs_set_nexus_t_task_mgmt(struct ufs_hba *hba,
> >>> +int tag, u8 tm_func) {
> >>> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> >>> + u32 type;
> >>> +
> >>> + type = hci_readl(ufs, HCI_UTMRL_NEXUS_TYPE);
> >>> +
> >>> + switch (tm_func) {
> >>> + case UFS_ABORT_TASK:
> >>> + case UFS_QUERY_TASK:
> >>> + type |= (1 << tag);
> >>> + break;
> >>> + case UFS_ABORT_TASK_SET:
> >>> + case UFS_CLEAR_TASK_SET:
> >>> + case UFS_LOGICAL_RESET:
> >>> + case UFS_QUERY_TASK_SET:
> >>> + type &= ~(1 << tag);
> >>> + break;
> >>> + }
> >>> +
> >>> + hci_writel(ufs, type, HCI_UTMRL_NEXUS_TYPE); }
> >>> +
> >>> +static int __exynos_ufs_suspend(struct ufs_hba *hba, enum ufs_pm_op
> >>> +pm_op) {
> >>> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> >>> +
> >>> + exynos_ufs_dev_reset_ctrl(ufs, false);
> >>> +
> >>> + exynos_ufs_ctrl_phy_pwr(ufs, false);
> >>> +
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static int __exynos_ufs_resume(struct ufs_hba *hba, enum ufs_pm_op
> >>> +pm_op) {
> >>> + struct exynos_ufs *ufs = to_exynos_ufs(hba);
> >>> + int ret = 0;
> >>> +
> >>> + exynos_ufs_ctrl_phy_pwr(ufs, true);
> >>> +
> >>> + /* system init */
> >>> + ret = exynos_ufs_init_system(ufs);
> >>> + if (ret)
> >>> + return ret;
> >>> +
> >>> + if (ufshcd_is_clkgating_allowed(hba))
> >>> + clk_prepare_enable(ufs->clk_hci);
> >>> + exynos_ufs_ctrl_auto_hci_clk(ufs, false);
> >>> +
> >>> + if (ufshcd_is_clkgating_allowed(hba))
> >>> + clk_disable_unprepare(ufs->clk_hci);
> >>> +
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static struct ufs_hba_variant_ops exynos_ufs_ops = {
> >>> + .init = exynos_ufs_init,
> >>> + .host_reset = exynos_ufs_host_reset,
> >>> + .setup_clocks = exynos_ufs_setup_clocks,
> >>> + .link_startup_notify = exynos_ufs_link_startup_notify,
> >>> + .pwr_change_notify = exynos_ufs_pwr_change_notify,
> >>> + .setup_xfer_req = exynos_ufs_set_nexus_t_xfer_req,
> >>> + .setup_task_mgmt = exynos_ufs_set_nexus_t_task_mgmt,
> >>> + .dbg_register_dump = exynos_ufs_dump_debug_info,
> >>> + .suspend = __exynos_ufs_suspend,
> >>> + .resume = __exynos_ufs_resume,
> >>> +};
> >>> +
> >>> +static int exynos_ufs_populate_dt_phy(struct device *dev, struct
> >>> +exynos_ufs *ufs) {
> >>> + struct device_node *ufs_phy;
> >>> + struct exynos_ufs_phy *phy = &ufs->phy;
> >>> + struct resource io_res;
> >>> + int ret;
> >>> +
> >>> + ufs_phy = of_get_child_by_name(dev->of_node, "ufs-phy");
> >>> + if (!ufs_phy) {
> >>> + dev_err(dev, "failed to get ufs-phy node\n");
> >>> + return -ENODEV;
> >>> + }
> >>> +
> >>> + ret = of_address_to_resource(ufs_phy, 0, &io_res);
> >>> + if (ret) {
> >>> + dev_err(dev, "failed to get i/o address phy pma\n");
> >>> + goto err_0;
> >>> + }
> >>> +
> >>> + phy->reg_pma = devm_ioremap_resource(dev, &io_res);
> >>> + if (!phy->reg_pma) {
> >>> + dev_err(dev, "failed to ioremap for phy pma\n");
> >>> + ret = -ENOMEM;
> >>> + goto err_0;
> >>> + }
> >>> +
> >>> +err_0:
> >>> + of_node_put(ufs_phy);
> >>> +
> >>> + return ret;
> >>> +}
> >>> +
> >>> +/*
> >>> + * This function is to define offset, mask and shift to access
> >> somewhere.
> >>> + */
> >>> +static int exynos_ufs_set_context_for_access(struct device *dev,
> >>> + const char *name, struct exynos_access_cxt *cxt)
> >> {
> >>> + struct device_node *np;
> >>> + int ret;
> >>> +
> >>> + np = of_get_child_by_name(dev->of_node, name);
> >>> + if (!np) {
> >>> + dev_err(dev, "failed to get node(%s)\n", name);
> >>> + return 1;
> >>
> >> Don't use the meaningless value.
> >>
> >>> + }
> >>> +
> >>> + ret = of_property_read_u32(np, "offset", &cxt->offset);
> >>> + if (IS_ERR(&cxt->offset)) {
> >>> + dev_err(dev, "failed to set cxt(%s) offset\n", name);
> >>> + return cxt->offset;
> >>> + }
> >>> +
> >>> + ret = of_property_read_u32(np, "mask", &cxt->mask);
> >>> + if (IS_ERR(&cxt->mask)) {
> >>> + dev_err(dev, "failed to set cxt(%s) mask\n", name);
> >>> + return cxt->mask;
> >>> + }
> >>> +
> >>> + ret = of_property_read_u32(np, "val", &cxt->val);
> >>> + if (IS_ERR(&cxt->val)) {
> >>> + dev_err(dev, "failed to set cxt(%s) val\n", name);
> >>> + return cxt->val;
> >>> + }
> >>> +
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static int exynos_ufs_populate_dt_system(struct device *dev, struct
> >>> +exynos_ufs *ufs) {
> >>> + int ret;
> >>> +
> >>> + /* regmap pmureg */
> >>> + ufs->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
> >>> + "samsung,pmu-phandle");
> >>> + if (IS_ERR(ufs->pmureg)) {
> >>> + /*
> >>> + * phy isolation should be available.
> >>> + * so this case need to be failed.
> >>> + */
> >>> + dev_err(dev, "pmu regmap lookup failed.\n");
> >>> + return PTR_ERR(ufs->pmureg);
> >>> + }
> >>> +
> >>> + /* Set access context for phy isolation bypass */
> >>> + ret = exynos_ufs_set_context_for_access(dev, "ufs-phy-iso",
> >>> + &ufs->cxt_iso);
> >>> + if (ret == 1) {
> >>> + /* no device node, default */
> >>> + ufs->cxt_iso.offset = 0x0724;
> >>> + ufs->cxt_iso.mask = 0x1;
> >>> + ufs->cxt_iso.val = 0x1;
> >>> + ret = 0;
> >>> + }
> >>> +
> >>> + /* regmap sysreg */
> >>> + ufs->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
> >>> + "samsung,sysreg-fsys-phandle");
> >>> + if (IS_ERR(ufs->sysreg)) {
> >>> + /*
> >>> + * Currently, ufs driver gets sysreg for io coherency.
> >>> + * Some architecture might not support this feature.
> >>> + * So the device node might not exist.
> >>> + */
> >>> + dev_err(dev, "sysreg regmap lookup failed.\n");
> >>> + return 0;
> >>
> >> return 0?
> >>
> >>> + }
> >>> +
> >>> + /* Set access context for io coherency */
> >>> + ret = exynos_ufs_set_context_for_access(dev, "ufs-dma-coherency",
> >>> + &ufs->cxt_coherency);
> >>> + if (ret == 1) {
> >>> + /* no device node, default */
> >>> + ufs->cxt_coherency.offset = 0x0700;
> >>> + ufs->cxt_coherency.mask = 0x300; /* bit 8,9 */
> >>> + ufs->cxt_coherency.val = 0x3;
> >>> + ret = 0;
> >>> + }
> >>> +
> >>> + return ret;
> >>> +}
> >>> +
> >>> +static int exynos_ufs_get_pwr_mode(struct device_node *np,
> >>> + struct exynos_ufs *ufs)
> >>> +{
> >>> + struct uic_pwr_mode *pmd = &ufs->req_pmd_parm;
> >>> +
> >>> + pmd->mode = FAST_MODE;
> >>> +
> >>> + if (of_property_read_u8(np, "ufs,pmd-attr-lane", &pmd->lane))
> >>> + pmd->lane = 1;
> >>> +
> >>> + if (of_property_read_u8(np, "ufs,pmd-attr-gear", &pmd->gear))
> >>> + pmd->gear = 1;
> >>> +
> >>> + pmd->hs_series = PA_HS_MODE_B;
> >>> +
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static int exynos_ufs_populate_dt(struct device *dev, struct
> >>> +exynos_ufs *ufs) {
> >>> + struct device_node *np = dev->of_node;
> >>> + int ret;
> >>> +
> >>> + /* Get exynos-specific version for featuring */
> >>> + if (of_property_read_u32(np, "hw-rev", &ufs->hw_rev))
> >>> + ufs->hw_rev = UFS_VER_0004;
> >>> +
> >>> + ret = exynos_ufs_populate_dt_phy(dev, ufs);
> >>> + if (ret) {
> >>> + dev_err(dev, "failed to populate dt-phy\n");
> >>> + goto out;
> >>> + }
> >>> +
> >>> + ret = exynos_ufs_populate_dt_system(dev, ufs);
> >>> + if (ret) {
> >>> + dev_err(dev, "failed to populate dt-pmu\n");
> >>> + goto out;
> >>> + }
> >>> +
> >>> + exynos_ufs_get_pwr_mode(np, ufs);
> >>> +
> >>> +out:
> >>> + return ret;
> >>> +}
> >>> +
> >>> +static u64 exynos_ufs_dma_mask = DMA_BIT_MASK(32);
> >>> +
> >>> +static int exynos_ufs_probe(struct platform_device *pdev) {
> >>> + struct device *dev = &pdev->dev;
> >>> + struct exynos_ufs *ufs;
> >>> + struct resource *res;
> >>> + int ret;
> >>> +
> >>> + ufs = devm_kzalloc(dev, sizeof(*ufs), GFP_KERNEL);
> >>> + if (!ufs) {
> >>> + dev_err(dev, "cannot allocate mem for exynos-ufs\n");
> >>> + return -ENOMEM;
> >>> + }
> >>> +
> >>> + /* exynos-specific hci */
> >>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> >>> + ufs->reg_hci = devm_ioremap_resource(dev, res);
> >>> + if (!ufs->reg_hci) {
> >>> + dev_err(dev, "cannot ioremap for hci vendor register\n");
> >>> + return -ENOMEM;
> >>> + }
> >>> +
> >>> + /* unipro */
> >>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> >>> + ufs->reg_unipro = devm_ioremap_resource(dev, res);
> >>> + if (!ufs->reg_unipro) {
> >>> + dev_err(dev, "cannot ioremap for unipro register\n");
> >>> + return -ENOMEM;
> >>> + }
> >>> +
> >>> + /* This must be before calling exynos_ufs_populate_dt */
> >>> + ret = ufs_init_cal(ufs, ufs_host_index, pdev);
> >>> + if (ret)
> >>> + return ret;
> >>> +
> >>> + ret = exynos_ufs_populate_dt(dev, ufs);
> >>> + if (ret) {
> >>> + dev_err(dev, "failed to get dt info.\n");
> >>> + return ret;
> >>> + }
> >>> +
> >>> + ufs->dev = dev;
> >>> + dev->platform_data = ufs;
> >>> + dev->dma_mask = &exynos_ufs_dma_mask;
> >>> +
> >>> + ret = ufshcd_pltfrm_init(pdev, &exynos_ufs_ops);
> >>> +
> >>> + return ret;
> >>> +}
> >>> +
> >>> +static int exynos_ufs_remove(struct platform_device *pdev) {
> >>> + struct ufs_hba *hba = platform_get_drvdata(pdev);
> >>> + struct exynos_ufs *ufs = dev_get_platdata(&pdev->dev);
> >>> +
> >>> + ufshcd_remove(hba);
> >>> +
> >>> + ufs->misc_flags = EXYNOS_UFS_MISC_TOGGLE_LOG;
> >>> +
> >>> + exynos_ufs_ctrl_phy_pwr(ufs, false);
> >>> +
> >>> + return 0;
> >>> +}
> >>> +
> >>> +#ifdef CONFIG_PM_SLEEP
> >>> +static int exynos_ufs_suspend(struct device *dev) {
> >>> + struct ufs_hba *hba = dev_get_drvdata(dev);
> >>> +
> >>> + return ufshcd_system_suspend(hba); }
> >>> +
> >>> +static int exynos_ufs_resume(struct device *dev) {
> >>> + struct ufs_hba *hba = dev_get_drvdata(dev);
> >>> +
> >>> + return ufshcd_system_resume(hba);
> >>> +}
> >>> +#else
> >>> +#define exynos_ufs_suspend NULL
> >>> +#define exynos_ufs_resume NULL
> >>> +#endif /* CONFIG_PM_SLEEP */
> >>> +
> >>> +#ifdef CONFIG_PM_RUNTIME
> >>> +static int exynos_ufs_runtime_suspend(struct device *dev) {
> >>> + return ufshcd_system_suspend(dev_get_drvdata(dev));
> >>> +}
> >>> +
> >>> +static int exynos_ufs_runtime_resume(struct device *dev) {
> >>> + return ufshcd_system_resume(dev_get_drvdata(dev));
> >>> +}
> >>> +
> >>> +static int exynos_ufs_runtime_idle(struct device *dev) {
> >>> + return ufshcd_runtime_idle(dev_get_drvdata(dev));
> >>> +}
> >>> +
> >>> +#else
> >>> +#define exynos_ufs_runtime_suspend NULL
> >>> +#define exynos_ufs_runtime_resume NULL
> >>> +#define exynos_ufs_runtime_idle NULL
> >>> +#endif /* CONFIG_PM_RUNTIME */
> >>> +
> >>> +static void exynos_ufs_shutdown(struct platform_device *pdev) {
> >>> + ufshcd_shutdown((struct ufs_hba *)platform_get_drvdata(pdev)); }
> >>> +
> >>> +static const struct dev_pm_ops exynos_ufs_dev_pm_ops = {
> >>> + .suspend = exynos_ufs_suspend,
> >>> + .resume = exynos_ufs_resume,
> >>> + .runtime_suspend = exynos_ufs_runtime_suspend,
> >>> + .runtime_resume = exynos_ufs_runtime_resume,
> >>> + .runtime_idle = exynos_ufs_runtime_idle,
> >>> +};
> >>> +
> >>> +static const struct of_device_id exynos_ufs_match[] = {
> >>> + { .compatible = "samsung,exynos-ufs", },
> >>> + {},
> >>> +};
> >>> +MODULE_DEVICE_TABLE(of, exynos_ufs_match);
> >>> +
> >>> +static struct platform_driver exynos_ufs_driver = {
> >>> + .driver = {
> >>> + .name = "exynos-ufs",
> >>> + .owner = THIS_MODULE,
> >>> + .pm = &exynos_ufs_dev_pm_ops,
> >>> + .of_match_table = exynos_ufs_match,
> >>> + .suppress_bind_attrs = true,
> >>> + },
> >>> + .probe = exynos_ufs_probe,
> >>> + .remove = exynos_ufs_remove,
> >>> + .shutdown = exynos_ufs_shutdown,
> >>> +};
> >>> +
> >>> +module_platform_driver(exynos_ufs_driver);
> >>> +MODULE_DESCRIPTION("Exynos Specific UFSHCI driver");
> >>> +MODULE_AUTHOR("Seungwon Jeon <tgih.jun@samsung.com>");
> >>> +MODULE_AUTHOR("Kiwoong Kim <kwmad.kim@samsung.com>");
> >>> +MODULE_LICENSE("GPL");
> >>> diff --git a/drivers/scsi/ufs/ufs-exynos.h
> >>> b/drivers/scsi/ufs/ufs-exynos.h new file mode 100644 index
> >>> 000000000000..0480fc4a8931
> >>> --- /dev/null
> >>> +++ b/drivers/scsi/ufs/ufs-exynos.h
> >>> @@ -0,0 +1,351 @@
> >>> +/*
> >>> + * UFS Host Controller driver for Exynos specific extensions
> >>> + *
> >>> + * Copyright (C) 2013-2014 Samsung Electronics Co., Ltd.
> >>> + *
> >>> + * This program is free software; you can redistribute it and/or
> >>> +modify
> >>> + * it under the terms of the GNU General Public License as
> >>> +published by
> >>> + * the Free Software Foundation; either version 2 of the License,
> >>> +or
> >>> + * (at your option) any later version.
> >>> + */
> >>> +
> >>> +#ifndef _UFS_EXYNOS_H_
> >>> +#define _UFS_EXYNOS_H_
> >>> +
> >>> +#define UFS_VER_0004 4
> >>> +#define UFS_VER_0005 5
> >>> +
> >>> +/*
> >>> + * Exynos's Vendor specific registers for UFSHCI */
> >>> +#define HCI_TXPRDT_ENTRY_SIZE 0x00
> >>> +#define HCI_RXPRDT_ENTRY_SIZE 0x04
> >>> +#define HCI_TO_CNT_DIV_VAL 0x08
> >>> +#define HCI_1US_TO_CNT_VAL 0x0C
> >>> + #define CNT_VAL_1US_MASK 0x3ff
> >>> +#define HCI_INVALID_UPIU_CTRL 0x10
> >>> +#define HCI_INVALID_UPIU_BADDR 0x14
> >>> +#define HCI_INVALID_UPIU_UBADDR 0x18
> >>> +#define HCI_INVALID_UTMR_OFFSET_ADDR 0x1C
> >>> +#define HCI_INVALID_UTR_OFFSET_ADDR 0x20
> >>> +#define HCI_INVALID_DIN_OFFSET_ADDR 0x24
> >>> +#define HCI_VENDOR_SPECIFIC_IS 0x38
> >>> +#define HCI_VENDOR_SPECIFIC_IE 0x3C
> >>> +#define HCI_UTRL_NEXUS_TYPE 0x40
> >>> +#define HCI_UTMRL_NEXUS_TYPE 0x44
> >>> +#define HCI_E2EFC_CTRL 0x48
> >>> +#define HCI_SW_RST 0x50
> >>> + #define UFS_LINK_SW_RST (1 << 0)
> >>> + #define UFS_UNIPRO_SW_RST (1 << 1)
> >>> + #define UFS_SW_RST_MASK (UFS_UNIPRO_SW_RST | UFS_LINK_SW_RST)
> >>> +#define HCI_LINK_VERSION 0x54
> >>> +#define HCI_IDLE_TIMER_CONFIG 0x58
> >>> +#define HCI_RX_UPIU_MATCH_ERROR_CODE 0x5C
> >>> +#define HCI_DATA_REORDER 0x60
> >>> +#define HCI_MAX_DOUT_DATA_SIZE 0x64
> >>> +#define HCI_UNIPRO_APB_CLK_CTRL 0x68
> >>> +#define HCI_AXIDMA_RWDATA_BURST_LEN 0x6C
> >>> + #define BURST_LEN(x) ((x) << 27 | (x))
> >>> + #define WLU_EN (1 << 31)
> >>
> >> Use BIT(31)
> >>
> >>> + #define AXIDMA_RWDATA_BURST_LEN (0xF)
> >>> +#define HCI_GPIO_OUT 0x70
> >>> +#define HCI_WRITE_DMA_CTRL 0x74
> >>> +#define HCI_ERROR_EN_PA_LAYER 0x78
> >>> +#define HCI_ERROR_EN_DL_LAYER 0x7C
> >>> +#define HCI_ERROR_EN_N_LAYER 0x80
> >>> +#define HCI_ERROR_EN_T_LAYER 0x84
> >>> +#define HCI_ERROR_EN_DME_LAYER 0x88
> >>> +#define HCI_UFSHCI_V2P1_CTRL 0X8C
> >>> +#define IA_TICK_SEL BIT(16)
> >>> +#define HCI_REQ_HOLD_EN 0xAC
> >>> +
> >>> +#define HCI_CLKSTOP_CTRL 0xB0
> >>> + #define REFCLKOUT_STOP BIT(4)
> >>> + #define MPHY_APBCLK_STOP BIT(3)
> >>> + #define REFCLK_STOP BIT(2)
> >>> + #define UNIPRO_MCLK_STOP BIT(1)
> >>> + #define UNIPRO_PCLK_STOP BIT(0)
> >>> + #define CLK_STOP_ALL (REFCLKOUT_STOP |\
> >>> + REFCLK_STOP |\
> >>> + UNIPRO_MCLK_STOP |\
> >>> + UNIPRO_PCLK_STOP)
> >>> +
> >>> +#define HCI_FORCE_HCS 0xB4
> >>> + #define REFCLKOUT_STOP_EN BIT(11)
> >>> + #define MPHY_APBCLK_STOP_EN BIT(10)
> >>> + #define UFSP_DRCG_EN BIT(8)
> >>> + #define REFCLK_STOP_EN BIT(7)
> >>> + #define UNIPRO_PCLK_STOP_EN BIT(6)
> >>> + #define UNIPRO_MCLK_STOP_EN BIT(5)
> >>> + #define HCI_CORECLK_STOP_EN BIT(4)
> >>> + #define CLK_STOP_CTRL_EN_ALL (UFSP_DRCG_EN |\
> >>> + MPHY_APBCLK_STOP_EN |\
> >>> + REFCLKOUT_STOP_EN |\
> >>> + REFCLK_STOP_EN |\
> >>> + UNIPRO_PCLK_STOP_EN |\
> >>> + UNIPRO_MCLK_STOP_EN)
> >>> +
> >>> +#define HCI_FSM_MONITOR 0xC0
> >>> +#define HCI_PRDT_HIT_RATIO 0xC4
> >>> +#define HCI_DMA0_MONITOR_STATE 0xC8
> >>> +#define HCI_DMA0_MONITOR_CNT 0xCC
> >>> +#define HCI_DMA1_MONITOR_STATE 0xD0
> >>> +#define HCI_DMA1_MONITOR_CNT 0xD4
> >>> +
> >>> +#define HCI_UFS_AXI_DMA_IF_CTRL 0xF8
> >>> +#define HCI_UFS_ACG_DISABLE 0xFC
> >>> + #define HCI_UFS_ACG_DISABLE_EN BIT(0)
> >>> +#define HCI_IOP_ACG_DISABLE 0x100
> >>> + #define HCI_IOP_ACG_DISABLE_EN BIT(0)
> >>> +#define HCI_MPHY_REFCLK_SEL 0x108
> >>> + #define MPHY_REFCLK_SEL BIT(0)
> >>> +
> >>> +/* Device fatal error */
> >>> +#define DFES_ERR_EN BIT(31)
> >>> +#define DFES_DEF_DL_ERRS (UIC_DATA_LINK_LAYER_ERROR_RX_BUF_OF |\
> >>> + UIC_DATA_LINK_LAYER_ERROR_PA_INIT)
> >>> +#define DFES_DEF_N_ERRS
> (UIC_NETWORK_UNSUPPORTED_HEADER_TYPE |\
> >>> + UIC_NETWORK_BAD_DEVICEID_ENC |\
> >>> + UIC_NETWORK_LHDR_TRAP_PACKET_DROPPING)
> >>> +#define DFES_DEF_T_ERRS
> (UIC_TRANSPORT_UNSUPPORTED_HEADER_TYPE
> >> |\
> >>> + UIC_TRANSPORT_UNKNOWN_CPORTID |\
> >>> + UIC_TRANSPORT_NO_CONNECTION_RX |\
> >>> + UIC_TRANSPORT_BAD_TC)
> >>> +
> >>> +/* TXPRDT defines */
> >>> +#define PRDT_PREFECT_EN BIT(31)
> >>> +#define PRDT_SET_SIZE(x) ((x) & 0x1F)
> >>> +
> >>> +enum {
> >>> + UNIP_PA_LYR = 0,
> >>> + UNIP_DL_LYR,
> >>> + UNIP_N_LYR,
> >>> + UNIP_T_LYR,
> >>> + UNIP_DME_LYR,
> >>> +};
> >>> +
> >>> +/*
> >>> + * UNIPRO registers
> >>> + */
> >>> +#define UNIP_COMP_VERSION 0x000
> >>> +#define UNIP_COMP_INFO 0x004
> >>> +#define UNIP_COMP_RESET 0x010
> >>> +
> >>> +#define UNIP_DME_POWERON_REQ 0x7800
> >>> +#define UNIP_DME_POWERON_CNF_RESULT 0x7804
> >>> +#define UNIP_DME_POWEROFF_REQ 0x7810
> >>> +#define UNIP_DME_POWEROFF_CNF_RESULT 0x7814
> >>> +#define UNIP_DME_RESET_REQ 0x7820
> >>> +#define UNIP_DME_RESET_REQ_LEVEL 0x7824
> >>> +#define UNIP_DME_ENABLE_REQ 0x7830
> >>> +#define UNIP_DME_ENABLE_CNF_RESULT 0x7834
> >>> +#define UNIP_DME_ENDPOINTRESET_REQ 0x7840
> >>> +#define UNIP_DME_ENDPOINTRESET_CNF_RESULT 0x7844
> >>> +#define UNIP_DME_LINKSTARTUP_REQ 0x7850
> >>> +#define UNIP_DME_LINKSTARTUP_CNF_RESULT 0x7854
> >>> +#define UNIP_DME_HIBERN8_ENTER_REQ 0x7860
> >>> +#define UNIP_DME_HIBERN8_ENTER_CNF_RESULT 0x7864
> >>> +#define UNIP_DME_HIBERN8_ENTER_IND_RESULT 0x7868
> >>> +#define UNIP_DME_HIBERN8_EXIT_REQ 0x7870
> >>> +#define UNIP_DME_HIBERN8_EXIT_CNF_RESULT 0x7874
> >>> +#define UNIP_DME_HIBERN8_EXIT_IND_RESULT 0x7878
> >>> +#define UNIP_DME_PWR_REQ 0x7880
> >>> +#define UNIP_DME_PWR_REQ_POWERMODE 0x7884
> >>> +#define UNIP_DME_PWR_REQ_LOCALL2TIMER0 0x7888
> >>> +#define UNIP_DME_PWR_REQ_LOCALL2TIMER1 0x788C
> >>> +#define UNIP_DME_PWR_REQ_LOCALL2TIMER2 0x7890
> >>> +#define UNIP_DME_PWR_REQ_REMOTEL2TIMER0 0x78B8
> >>> +#define UNIP_DME_PWR_REQ_REMOTEL2TIMER1 0x78BC
> >>> +#define UNIP_DME_PWR_REQ_REMOTEL2TIMER2 0x78C0
> >>> +#define UNIP_DME_PWR_CNF_RESULT 0x78E8
> >>> +#define UNIP_DME_PWR_IND_RESULT 0x78EC
> >>> +#define UNIP_DME_TEST_MODE_REQ 0x7900
> >>> +#define UNIP_DME_TEST_MODE_CNF_RESULT 0x7904
> >>> +
> >>> +#define UNIP_DME_ERROR_IND_LAYER 0x0C0
> >>> +#define UNIP_DME_ERROR_IND_ERRCODE 0x0C4
> >>> +#define UNIP_DME_PACP_CNFBIT 0x0C8
> >>> +#define UNIP_DME_DL_FRAME_IND 0x0D0
> >>> +#define UNIP_DME_INTR_STATUS 0x0E0
> >>> +#define UNIP_DME_INTR_ENABLE 0x0E4
> >>> +
> >>> +#define UNIP_DME_GETSET_CONTROL 0x7A00
> >>> +#define UNIP_DME_GETSET_ADDR 0x7A04
> >>> +#define UNIP_DME_GETSET_WDATA 0x7A08
> >>> +#define UNIP_DME_GETSET_RDATA 0x7A0C
> >>> +#define UNIP_DME_GETSET_RESULT 0x7A10
> >>> +#define UNIP_DME_PEER_GETSET_CONTROL 0x7A20
> >>> +#define UNIP_DME_PEER_GETSET_ADDR 0x7A24
> >>> +#define UNIP_DME_PEER_GETSET_WDATA 0x7A28
> >>> +#define UNIP_DME_PEER_GETSET_RDATA 0x7A2C
> >>> +#define UNIP_DME_PEER_GETSET_RESULT 0x7A30
> >>> +
> >>> +#define UNIP_DME_INTR_STATUS_LSB 0x7B00
> >>> +#define UNIP_DME_INTR_STATUS_MSB 0x7B04
> >>> +#define UNIP_DME_INTR_ERROR_CODE 0x7B20
> >>> +#define UNIP_DME_DISCARD_PORT_ID 0x7B24
> >>> +#define UNIP_DME_DBG_OPTION_SUITE 0x7C00
> >>> +#define UNIP_DME_DBG_CTRL_FSM 0x7D00
> >>> +#define UNIP_DME_DBG_FLAG_STATUS 0x7D14
> >>> +#define UNIP_DME_DBG_LINKCFG_FSM 0x7D18
> >>> +
> >>> +#define UNIP_DME_INTR_ERROR_CODE 0x7B20
> >>> +#define UNIP_DME_DEEPSTALL_ENTER_REQ 0x7910
> >>> +#define UNIP_DME_DISCARD_CPORT_ID 0x7B24
> >>> +
> >>> +#define UNIP_DBG_FORCE_DME_CTRL_STATE 0x150
> >>> +#define UNIP_DBG_AUTO_DME_LINKSTARTUP 0x158
> >>> +#define UNIP_DBG_PA_CTRLSTATE 0x15C
> >>> +#define UNIP_DBG_PA_TX_STATE 0x160
> >>> +#define UNIP_DBG_BREAK_DME_CTRL_STATE 0x164
> >>> +#define UNIP_DBG_STEP_DME_CTRL_STATE 0x168
> >>> +#define UNIP_DBG_NEXT_DME_CTRL_STATE 0x16C
> >>> +
> >>> +/*
> >>> + * Driver specific definitions
> >>> + */
> >>> +struct exynos_ufs_phy {
> >>> + void __iomem *reg_pma;
> >>> +};
> >>> +
> >>> +struct exynos_ufs_clk_info {
> >>> + struct list_head list;
> >>> + struct clk *clk;
> >>> + const char *name;
> >>> + u32 freq;
> >>> +};
> >>> +
> >>> +struct exynos_ufs_misc_log {
> >>> + struct list_head clk_list_head;
> >>> + bool isolation;
> >>> +};
> >>> +
> >>> +struct exynos_ufs_sfr_log {
> >>> + const char* name;
> >>> + const u32 offset;
> >>> +#define LOG_STD_HCI_SFR 0xFFFFFFF0
> >>> +#define LOG_VS_HCI_SFR 0xFFFFFFF1
> >>> +#define LOG_FMP_SFR 0xFFFFFFF2
> >>> +#define LOG_UNIPRO_SFR 0xFFFFFFF3
> >>> +#define LOG_PMA_SFR 0xFFFFFFF4
> >>> + u32 val;
> >>> +};
> >>> +
> >>> +struct exynos_ufs_attr_log {
> >>> + const u32 offset;
> >>> + u32 res;
> >>> + u32 val;
> >>> +};
> >>> +
> >>> +struct exynos_ufs_perf {
> >>> + u32 opcode; /* 0: read, 1: write */
> >>> + u32 chunk_size;
> >>> + ktime_t time;
> >>> + u64 total_time;
> >>> + u32 count;
> >>> + u32 total_count;
> >>> +};
> >>> +
> >>> +/* Main structure for debug and performance */ struct
> >>> +exynos_ufs_debug {
> >>> + struct exynos_ufs_sfr_log* std_sfr;
> >>> + struct exynos_ufs_sfr_log* sfr;
> >>> + struct exynos_ufs_attr_log* attr;
> >>> + struct exynos_ufs_misc_log misc;
> >>> + struct exynos_ufs_perf perf;
> >>> +};
> >>> +
> >>> +struct exynos_access_cxt {
> >>> + u32 offset;
> >>> + u32 mask;
> >>> + u32 val;
> >>> +};
> >>> +
> >>> +struct uic_pwr_mode {
> >>> + u8 lane;
> >>> + u8 gear;
> >>> + u8 mode;
> >>> + u8 hs_series;
> >>> +};
> >>> +
> >>> +struct exynos_ufs {
> >>> + struct device *dev;
> >>> + struct ufs_hba *hba;
> >>> +
> >>> + void __iomem *reg_hci;
> >>> + void __iomem *reg_unipro;
> >>> +
> >>> + struct regmap *pmureg;
> >>> + struct regmap *sysreg;
> >>> +
> >>> + struct clk *clk_hci;
> >>> + struct clk *pclk;
> >>> + struct clk *clk_unipro;
> >>> + u32 mclk_rate;
> >>> +
> >>> + int num_rx_lanes;
> >>> + int num_tx_lanes;
> >>> +
> >>> + struct exynos_ufs_phy phy;
> >>> + struct uic_pwr_mode req_pmd_parm;
> >>> + struct uic_pwr_mode act_pmd_parm;
> >>> +
> >>> + u32 rx_min_actv_time_cap;
> >>> + u32 rx_hibern8_time_cap;
> >>> + u32 tx_hibern8_time_cap;
> >>> +
> >>> + /* for miscellaneous control */
> >>> + u32 misc_flags;
> >>> +#define EXYNOS_UFS_MISC_TOGGLE_LOG BIT(0)
> >>> +
> >>> + struct exynos_ufs_debug debug;
> >>> +
> >>> + u32 hw_rev;
> >>> +
> >>> + struct exynos_access_cxt cxt_iso; /* phy isolation */
> >>> + struct exynos_access_cxt cxt_coherency; /* io coherency */
> >>> +};
> >>> +
> >>> +static inline struct exynos_ufs *to_exynos_ufs(struct ufs_hba *hba) {
> >>> + return dev_get_platdata(hba->dev); }
> >>> +
> >>> +#ifndef __EXYNOS_UFS_MMIO_FUNC__
> >>> +#define __EXYNOS_UFS_MMIO_FUNC__
> >>
> >> I don't understand..why need to check "ifndef __EXYNOS_UFS_MMIO_FUNC__".
> >> (because i didnt find __EXYNOS_UFS_MMIO_FUNC__ anywhere.) It means
> >> that you want to define always, doesn't?
> >>
> >>> +#define EXYNOS_UFS_MMIO_FUNC(name) \
> >>> +static inline void name##_writel(struct exynos_ufs *ufs, u32 val,
> >>> +u32
> >> reg) \
> >>> +{ \
> >>> + writel(val, ufs->reg_##name + reg); \
> >>> +} \
> >>> + \
> >>> +static inline u32 name##_readl(struct exynos_ufs *ufs, u32 reg)
> >> \
> >>> +{ \
> >>> + return readl(ufs->reg_##name + reg); \
> >>> +}
> >>> +
> >>> +EXYNOS_UFS_MMIO_FUNC(hci);
> >>> +EXYNOS_UFS_MMIO_FUNC(unipro);
> >>
> >> Maybe.. i don't like this style..because it's too difficult to debug
> >> and find the function.
> >>
> >>> +
> >>> +static inline void phy_pma_writel(struct exynos_ufs *ufs, u32 val,
> >>> +u32 reg) {
> >>> + u32 reg1 = hci_readl(ufs, HCI_CLKSTOP_CTRL);
> >>> +
> >>> + hci_writel(ufs, reg1 & ~MPHY_APBCLK_STOP, HCI_CLKSTOP_CTRL);
> >>> + writel(val, ufs->phy.reg_pma + reg);
> >>> + hci_writel(ufs, reg1 | MPHY_APBCLK_STOP, HCI_CLKSTOP_CTRL); }
> >>> +
> >>> +static inline u32 phy_pma_readl(struct exynos_ufs *ufs, u32 reg) {
> >>> + u32 reg1 = hci_readl(ufs, HCI_CLKSTOP_CTRL);
> >>> +
> >>> + hci_writel(ufs, reg1 & ~MPHY_APBCLK_STOP, HCI_CLKSTOP_CTRL);
> >>> + reg = readl(ufs->phy.reg_pma + reg);
> >>> + hci_writel(ufs, reg1 | MPHY_APBCLK_STOP, HCI_CLKSTOP_CTRL);
> >>> +
> >>> + return reg;
> >>> +}
> >>> +#endif
> >>> +
> >>> +#endif /* _UFS_EXYNOS_H_ */
> >>> diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
> >>> index 1332e544da92..1afd5ac9707c 100644
> >>> --- a/drivers/scsi/ufs/ufshcd.h
> >>> +++ b/drivers/scsi/ufs/ufshcd.h
> >>> @@ -308,6 +308,7 @@ struct ufs_hba_variant_ops {
> >>> int (*setup_clocks)(struct ufs_hba *, bool,
> >>> enum ufs_notify_change_status);
> >>> int (*setup_regulators)(struct ufs_hba *, bool);
> >>> + void (*host_reset)(struct ufs_hba *);
> >>> int (*hce_enable_notify)(struct ufs_hba *,
> >>> enum ufs_notify_change_status);
> >>> int (*link_startup_notify)(struct ufs_hba *,
> >>>
> >>
> >
> >
> >
> >
> >
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] scsi: ufs: add Exynos-specific driver
2017-11-30 8:35 ` 김기웅
@ 2017-11-30 23:29 ` Jaehoon Chung
0 siblings, 0 replies; 8+ messages in thread
From: Jaehoon Chung @ 2017-11-30 23:29 UTC (permalink / raw)
To: 김기웅, linux-scsi,
'Martin K. Petersen'
Cc: cpgs, 'HeonGwang Chu', '김부진',
'YOUNGEUN PARK'
Hi,
On 11/30/2017 05:35 PM, 김기웅 wrote:
> Dear Jaehoon
>
> Actually, I used to use the way what you mentioned, but
> now a new way has been using with latest products.
>
> Anyway, I would refer to your comment.
If you will send the next version, you need to run "checkpatch".
When i run checkpatch, there are too many errors and warnings.
total: 37 errors, 62 warnings, 1340 lines checked
I also want that exynos ufs driver will be merged.
Best Regards,
Jaehoon Chung
>
> Thank you.
>
>> -----Original Message-----
>> From: linux-scsi-owner@vger.kernel.org [mailto:linux-scsi-
>> owner@vger.kernel.org] On Behalf Of Jaehoon Chung
>> Sent: Tuesday, November 28, 2017 6:21 PM
>> To: 김기웅; linux-scsi@vger.kernel.org; 'Martin K. Petersen'
>> Cc: cpgs@samsung.com; 'HeonGwang Chu'; '김부진'; 'YOUNGEUN PARK'
>> Subject: Re: [PATCH 2/2] scsi: ufs: add Exynos-specific driver
>>
>> On 11/28/2017 05:27 PM, 김기웅 wrote:
>>> Hi.
>>> This is modified from Seungwon's initial patch.
>>> And this has been used for several commercial products.
>>> I think you feel weird because you can't see a bunch of unipro and mphy.
>>> But those stuff has been changed whenever new product comes.
>>> So I didn't keep those in this driver.
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2017-11-30 23:29 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <CGME20171128053631epcas1p389908b55511552b79b4083109dddc515@epcas1p3.samsung.com>
2017-11-28 5:36 ` [PATCH 2/2] scsi: ufs: add Exynos-specific driver 김기웅
2017-11-28 8:19 ` Jaehoon Chung
2017-11-28 8:27 ` 김기웅
2017-11-28 9:20 ` Jaehoon Chung
2017-11-30 8:35 ` 김기웅
2017-11-30 23:29 ` Jaehoon Chung
2017-11-28 14:24 ` Christoph Hellwig
2017-11-30 7:46 ` 김기웅
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).