Message ID | 002a01d3680a$d8817200$89845600$@samsung.com (mailing list archive) |
---|---|
State | Changes Requested |
Headers | show |
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 *, >
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 *, > > >
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 *, >>> >> > > > > >
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.
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.
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 *, > >>> > >> > > > > > > > > > > >
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.
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 *,
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