| xj | b04a402 | 2021-11-25 15:01:52 +0800 | [diff] [blame] | 1 | /* |
| 2 | * HiSilicon Hixxxx UFS Driver |
| 3 | * |
| 4 | * Copyright (c) 2016-2017 Linaro Ltd. |
| 5 | * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. |
| 6 | * |
| 7 | * Released under the GPLv2 only. |
| 8 | * SPDX-License-Identifier: GPL-2.0 |
| 9 | */ |
| 10 | |
| 11 | #include <linux/time.h> |
| 12 | #include <linux/of.h> |
| 13 | #include <linux/of_address.h> |
| 14 | #include <linux/dma-mapping.h> |
| 15 | #include <linux/platform_device.h> |
| 16 | #include <linux/reset.h> |
| 17 | |
| 18 | #include "ufshcd.h" |
| 19 | #include "ufshcd-pltfrm.h" |
| 20 | #include "unipro.h" |
| 21 | #include "ufs-hisi.h" |
| 22 | #include "ufshci.h" |
| 23 | #include "ufs_quirks.h" |
| 24 | |
| 25 | static int ufs_hisi_check_hibern8(struct ufs_hba *hba) |
| 26 | { |
| 27 | int err = 0; |
| 28 | u32 tx_fsm_val_0 = 0; |
| 29 | u32 tx_fsm_val_1 = 0; |
| 30 | unsigned long timeout = jiffies + msecs_to_jiffies(HBRN8_POLL_TOUT_MS); |
| 31 | |
| 32 | do { |
| 33 | err = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, 0), |
| 34 | &tx_fsm_val_0); |
| 35 | err |= ufshcd_dme_get(hba, |
| 36 | UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, 1), &tx_fsm_val_1); |
| 37 | if (err || (tx_fsm_val_0 == TX_FSM_HIBERN8 && |
| 38 | tx_fsm_val_1 == TX_FSM_HIBERN8)) |
| 39 | break; |
| 40 | |
| 41 | /* sleep for max. 200us */ |
| 42 | usleep_range(100, 200); |
| 43 | } while (time_before(jiffies, timeout)); |
| 44 | |
| 45 | /* |
| 46 | * we might have scheduled out for long during polling so |
| 47 | * check the state again. |
| 48 | */ |
| 49 | if (time_after(jiffies, timeout)) { |
| 50 | err = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, 0), |
| 51 | &tx_fsm_val_0); |
| 52 | err |= ufshcd_dme_get(hba, |
| 53 | UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, 1), &tx_fsm_val_1); |
| 54 | } |
| 55 | |
| 56 | if (err) { |
| 57 | dev_err(hba->dev, "%s: unable to get TX_FSM_STATE, err %d\n", |
| 58 | __func__, err); |
| 59 | } else if (tx_fsm_val_0 != TX_FSM_HIBERN8 || |
| 60 | tx_fsm_val_1 != TX_FSM_HIBERN8) { |
| 61 | err = -1; |
| 62 | dev_err(hba->dev, "%s: invalid TX_FSM_STATE, lane0 = %d, lane1 = %d\n", |
| 63 | __func__, tx_fsm_val_0, tx_fsm_val_1); |
| 64 | } |
| 65 | |
| 66 | return err; |
| 67 | } |
| 68 | |
| 69 | static void ufs_hi3660_clk_init(struct ufs_hba *hba) |
| 70 | { |
| 71 | struct ufs_hisi_host *host = ufshcd_get_variant(hba); |
| 72 | |
| 73 | ufs_sys_ctrl_clr_bits(host, BIT_SYSCTRL_REF_CLOCK_EN, PHY_CLK_CTRL); |
| 74 | if (ufs_sys_ctrl_readl(host, PHY_CLK_CTRL) & BIT_SYSCTRL_REF_CLOCK_EN) |
| 75 | mdelay(1); |
| 76 | /* use abb clk */ |
| 77 | ufs_sys_ctrl_clr_bits(host, BIT_UFS_REFCLK_SRC_SEl, UFS_SYSCTRL); |
| 78 | ufs_sys_ctrl_clr_bits(host, BIT_UFS_REFCLK_ISO_EN, PHY_ISO_EN); |
| 79 | /* open mphy ref clk */ |
| 80 | ufs_sys_ctrl_set_bits(host, BIT_SYSCTRL_REF_CLOCK_EN, PHY_CLK_CTRL); |
| 81 | } |
| 82 | |
| 83 | static void ufs_hi3660_soc_init(struct ufs_hba *hba) |
| 84 | { |
| 85 | struct ufs_hisi_host *host = ufshcd_get_variant(hba); |
| 86 | u32 reg; |
| 87 | |
| 88 | if (!IS_ERR(host->rst)) |
| 89 | reset_control_assert(host->rst); |
| 90 | |
| 91 | /* HC_PSW powerup */ |
| 92 | ufs_sys_ctrl_set_bits(host, BIT_UFS_PSW_MTCMOS_EN, PSW_POWER_CTRL); |
| 93 | udelay(10); |
| 94 | /* notify PWR ready */ |
| 95 | ufs_sys_ctrl_set_bits(host, BIT_SYSCTRL_PWR_READY, HC_LP_CTRL); |
| 96 | ufs_sys_ctrl_writel(host, MASK_UFS_DEVICE_RESET | 0, |
| 97 | UFS_DEVICE_RESET_CTRL); |
| 98 | |
| 99 | reg = ufs_sys_ctrl_readl(host, PHY_CLK_CTRL); |
| 100 | reg = (reg & ~MASK_SYSCTRL_CFG_CLOCK_FREQ) | UFS_FREQ_CFG_CLK; |
| 101 | /* set cfg clk freq */ |
| 102 | ufs_sys_ctrl_writel(host, reg, PHY_CLK_CTRL); |
| 103 | /* set ref clk freq */ |
| 104 | ufs_sys_ctrl_clr_bits(host, MASK_SYSCTRL_REF_CLOCK_SEL, PHY_CLK_CTRL); |
| 105 | /* bypass ufs clk gate */ |
| 106 | ufs_sys_ctrl_set_bits(host, MASK_UFS_CLK_GATE_BYPASS, |
| 107 | CLOCK_GATE_BYPASS); |
| 108 | ufs_sys_ctrl_set_bits(host, MASK_UFS_SYSCRTL_BYPASS, UFS_SYSCTRL); |
| 109 | |
| 110 | /* open psw clk */ |
| 111 | ufs_sys_ctrl_set_bits(host, BIT_SYSCTRL_PSW_CLK_EN, PSW_CLK_CTRL); |
| 112 | /* disable ufshc iso */ |
| 113 | ufs_sys_ctrl_clr_bits(host, BIT_UFS_PSW_ISO_CTRL, PSW_POWER_CTRL); |
| 114 | /* disable phy iso */ |
| 115 | ufs_sys_ctrl_clr_bits(host, BIT_UFS_PHY_ISO_CTRL, PHY_ISO_EN); |
| 116 | /* notice iso disable */ |
| 117 | ufs_sys_ctrl_clr_bits(host, BIT_SYSCTRL_LP_ISOL_EN, HC_LP_CTRL); |
| 118 | |
| 119 | /* disable lp_reset_n */ |
| 120 | ufs_sys_ctrl_set_bits(host, BIT_SYSCTRL_LP_RESET_N, RESET_CTRL_EN); |
| 121 | mdelay(1); |
| 122 | |
| 123 | ufs_sys_ctrl_writel(host, MASK_UFS_DEVICE_RESET | BIT_UFS_DEVICE_RESET, |
| 124 | UFS_DEVICE_RESET_CTRL); |
| 125 | |
| 126 | msleep(20); |
| 127 | |
| 128 | /* |
| 129 | * enable the fix of linereset recovery, |
| 130 | * and enable rx_reset/tx_rest beat |
| 131 | * enable ref_clk_en override(bit5) & |
| 132 | * override value = 1(bit4), with mask |
| 133 | */ |
| 134 | ufs_sys_ctrl_writel(host, 0x03300330, UFS_DEVICE_RESET_CTRL); |
| 135 | |
| 136 | if (!IS_ERR(host->rst)) |
| 137 | reset_control_deassert(host->rst); |
| 138 | } |
| 139 | |
| 140 | static int ufs_hisi_link_startup_pre_change(struct ufs_hba *hba) |
| 141 | { |
| 142 | int err; |
| 143 | uint32_t value; |
| 144 | uint32_t reg; |
| 145 | |
| 146 | /* Unipro VS_mphy_disable */ |
| 147 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xD0C1, 0x0), 0x1); |
| 148 | /* PA_HSSeries */ |
| 149 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x156A, 0x0), 0x2); |
| 150 | /* MPHY CBRATESEL */ |
| 151 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x8114, 0x0), 0x1); |
| 152 | /* MPHY CBOVRCTRL2 */ |
| 153 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x8121, 0x0), 0x2D); |
| 154 | /* MPHY CBOVRCTRL3 */ |
| 155 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x8122, 0x0), 0x1); |
| 156 | /* Unipro VS_MphyCfgUpdt */ |
| 157 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xD085, 0x0), 0x1); |
| 158 | /* MPHY RXOVRCTRL4 rx0 */ |
| 159 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x800D, 0x4), 0x58); |
| 160 | /* MPHY RXOVRCTRL4 rx1 */ |
| 161 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x800D, 0x5), 0x58); |
| 162 | /* MPHY RXOVRCTRL5 rx0 */ |
| 163 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x800E, 0x4), 0xB); |
| 164 | /* MPHY RXOVRCTRL5 rx1 */ |
| 165 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x800E, 0x5), 0xB); |
| 166 | /* MPHY RXSQCONTROL rx0 */ |
| 167 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x8009, 0x4), 0x1); |
| 168 | /* MPHY RXSQCONTROL rx1 */ |
| 169 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x8009, 0x5), 0x1); |
| 170 | /* Unipro VS_MphyCfgUpdt */ |
| 171 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xD085, 0x0), 0x1); |
| 172 | |
| 173 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x8113, 0x0), 0x1); |
| 174 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xD085, 0x0), 0x1); |
| 175 | |
| 176 | /* Tactive RX */ |
| 177 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x008F, 0x4), 0x7); |
| 178 | /* Tactive RX */ |
| 179 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x008F, 0x5), 0x7); |
| 180 | |
| 181 | /* Gear3 Synclength */ |
| 182 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x0095, 0x4), 0x4F); |
| 183 | /* Gear3 Synclength */ |
| 184 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x0095, 0x5), 0x4F); |
| 185 | /* Gear2 Synclength */ |
| 186 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x0094, 0x4), 0x4F); |
| 187 | /* Gear2 Synclength */ |
| 188 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x0094, 0x5), 0x4F); |
| 189 | /* Gear1 Synclength */ |
| 190 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x008B, 0x4), 0x4F); |
| 191 | /* Gear1 Synclength */ |
| 192 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x008B, 0x5), 0x4F); |
| 193 | /* Thibernate Tx */ |
| 194 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x000F, 0x0), 0x5); |
| 195 | /* Thibernate Tx */ |
| 196 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x000F, 0x1), 0x5); |
| 197 | |
| 198 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xD085, 0x0), 0x1); |
| 199 | /* Unipro VS_mphy_disable */ |
| 200 | ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(0xD0C1, 0x0), &value); |
| 201 | if (value != 0x1) |
| 202 | dev_info(hba->dev, |
| 203 | "Warring!!! Unipro VS_mphy_disable is 0x%x\n", value); |
| 204 | |
| 205 | /* Unipro VS_mphy_disable */ |
| 206 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xD0C1, 0x0), 0x0); |
| 207 | err = ufs_hisi_check_hibern8(hba); |
| 208 | if (err) |
| 209 | dev_err(hba->dev, "ufs_hisi_check_hibern8 error\n"); |
| 210 | |
| 211 | ufshcd_writel(hba, UFS_HCLKDIV_NORMAL_VALUE, UFS_REG_HCLKDIV); |
| 212 | |
| 213 | /* disable auto H8 */ |
| 214 | reg = ufshcd_readl(hba, REG_AUTO_HIBERNATE_IDLE_TIMER); |
| 215 | reg = reg & (~UFS_AHIT_AH8ITV_MASK); |
| 216 | ufshcd_writel(hba, reg, REG_AUTO_HIBERNATE_IDLE_TIMER); |
| 217 | |
| 218 | /* Unipro PA_Local_TX_LCC_Enable */ |
| 219 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x155E, 0x0), 0x0); |
| 220 | /* close Unipro VS_Mk2ExtnSupport */ |
| 221 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xD0AB, 0x0), 0x0); |
| 222 | ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(0xD0AB, 0x0), &value); |
| 223 | if (value != 0) { |
| 224 | /* Ensure close success */ |
| 225 | dev_info(hba->dev, "WARN: close VS_Mk2ExtnSupport failed\n"); |
| 226 | } |
| 227 | |
| 228 | return err; |
| 229 | } |
| 230 | |
| 231 | static int ufs_hisi_link_startup_post_change(struct ufs_hba *hba) |
| 232 | { |
| 233 | struct ufs_hisi_host *host = ufshcd_get_variant(hba); |
| 234 | |
| 235 | /* Unipro DL_AFC0CreditThreshold */ |
| 236 | ufshcd_dme_set(hba, UIC_ARG_MIB(0x2044), 0x0); |
| 237 | /* Unipro DL_TC0OutAckThreshold */ |
| 238 | ufshcd_dme_set(hba, UIC_ARG_MIB(0x2045), 0x0); |
| 239 | /* Unipro DL_TC0TXFCThreshold */ |
| 240 | ufshcd_dme_set(hba, UIC_ARG_MIB(0x2040), 0x9); |
| 241 | |
| 242 | /* not bypass ufs clk gate */ |
| 243 | ufs_sys_ctrl_clr_bits(host, MASK_UFS_CLK_GATE_BYPASS, |
| 244 | CLOCK_GATE_BYPASS); |
| 245 | ufs_sys_ctrl_clr_bits(host, MASK_UFS_SYSCRTL_BYPASS, |
| 246 | UFS_SYSCTRL); |
| 247 | |
| 248 | /* select received symbol cnt */ |
| 249 | ufshcd_dme_set(hba, UIC_ARG_MIB(0xd09a), 0x80000000); |
| 250 | /* reset counter0 and enable */ |
| 251 | ufshcd_dme_set(hba, UIC_ARG_MIB(0xd09c), 0x00000005); |
| 252 | |
| 253 | return 0; |
| 254 | } |
| 255 | |
| 256 | static int ufs_hi3660_link_startup_notify(struct ufs_hba *hba, |
| 257 | enum ufs_notify_change_status status) |
| 258 | { |
| 259 | int err = 0; |
| 260 | |
| 261 | switch (status) { |
| 262 | case PRE_CHANGE: |
| 263 | err = ufs_hisi_link_startup_pre_change(hba); |
| 264 | break; |
| 265 | case POST_CHANGE: |
| 266 | err = ufs_hisi_link_startup_post_change(hba); |
| 267 | break; |
| 268 | default: |
| 269 | break; |
| 270 | } |
| 271 | |
| 272 | return err; |
| 273 | } |
| 274 | |
| 275 | static void ufs_hisi_set_dev_cap(struct ufs_dev_params *hisi_param) |
| 276 | { |
| 277 | hisi_param->rx_lanes = UFS_HISI_LIMIT_NUM_LANES_RX; |
| 278 | hisi_param->tx_lanes = UFS_HISI_LIMIT_NUM_LANES_TX; |
| 279 | hisi_param->hs_rx_gear = UFS_HISI_LIMIT_HSGEAR_RX; |
| 280 | hisi_param->hs_tx_gear = UFS_HISI_LIMIT_HSGEAR_TX; |
| 281 | hisi_param->pwm_rx_gear = UFS_HISI_LIMIT_PWMGEAR_RX; |
| 282 | hisi_param->pwm_tx_gear = UFS_HISI_LIMIT_PWMGEAR_TX; |
| 283 | hisi_param->rx_pwr_pwm = UFS_HISI_LIMIT_RX_PWR_PWM; |
| 284 | hisi_param->tx_pwr_pwm = UFS_HISI_LIMIT_TX_PWR_PWM; |
| 285 | hisi_param->rx_pwr_hs = UFS_HISI_LIMIT_RX_PWR_HS; |
| 286 | hisi_param->tx_pwr_hs = UFS_HISI_LIMIT_TX_PWR_HS; |
| 287 | hisi_param->hs_rate = UFS_HISI_LIMIT_HS_RATE; |
| 288 | hisi_param->desired_working_mode = UFS_HISI_LIMIT_DESIRED_MODE; |
| 289 | } |
| 290 | |
| 291 | static void ufs_hisi_pwr_change_pre_change(struct ufs_hba *hba) |
| 292 | { |
| 293 | if (hba->dev_quirks & UFS_DEVICE_QUIRK_HOST_VS_DEBUGSAVECONFIGTIME) { |
| 294 | pr_info("ufs flash device must set VS_DebugSaveConfigTime 0x10\n"); |
| 295 | /* VS_DebugSaveConfigTime */ |
| 296 | ufshcd_dme_set(hba, UIC_ARG_MIB(0xD0A0), 0x10); |
| 297 | /* sync length */ |
| 298 | ufshcd_dme_set(hba, UIC_ARG_MIB(0x1556), 0x48); |
| 299 | } |
| 300 | |
| 301 | /* update */ |
| 302 | ufshcd_dme_set(hba, UIC_ARG_MIB(0x15A8), 0x1); |
| 303 | /* PA_TxSkip */ |
| 304 | ufshcd_dme_set(hba, UIC_ARG_MIB(0x155c), 0x0); |
| 305 | /*PA_PWRModeUserData0 = 8191, default is 0*/ |
| 306 | ufshcd_dme_set(hba, UIC_ARG_MIB(0x15b0), 8191); |
| 307 | /*PA_PWRModeUserData1 = 65535, default is 0*/ |
| 308 | ufshcd_dme_set(hba, UIC_ARG_MIB(0x15b1), 65535); |
| 309 | /*PA_PWRModeUserData2 = 32767, default is 0*/ |
| 310 | ufshcd_dme_set(hba, UIC_ARG_MIB(0x15b2), 32767); |
| 311 | /*DME_FC0ProtectionTimeOutVal = 8191, default is 0*/ |
| 312 | ufshcd_dme_set(hba, UIC_ARG_MIB(0xd041), 8191); |
| 313 | /*DME_TC0ReplayTimeOutVal = 65535, default is 0*/ |
| 314 | ufshcd_dme_set(hba, UIC_ARG_MIB(0xd042), 65535); |
| 315 | /*DME_AFC0ReqTimeOutVal = 32767, default is 0*/ |
| 316 | ufshcd_dme_set(hba, UIC_ARG_MIB(0xd043), 32767); |
| 317 | /*PA_PWRModeUserData3 = 8191, default is 0*/ |
| 318 | ufshcd_dme_set(hba, UIC_ARG_MIB(0x15b3), 8191); |
| 319 | /*PA_PWRModeUserData4 = 65535, default is 0*/ |
| 320 | ufshcd_dme_set(hba, UIC_ARG_MIB(0x15b4), 65535); |
| 321 | /*PA_PWRModeUserData5 = 32767, default is 0*/ |
| 322 | ufshcd_dme_set(hba, UIC_ARG_MIB(0x15b5), 32767); |
| 323 | /*DME_FC1ProtectionTimeOutVal = 8191, default is 0*/ |
| 324 | ufshcd_dme_set(hba, UIC_ARG_MIB(0xd044), 8191); |
| 325 | /*DME_TC1ReplayTimeOutVal = 65535, default is 0*/ |
| 326 | ufshcd_dme_set(hba, UIC_ARG_MIB(0xd045), 65535); |
| 327 | /*DME_AFC1ReqTimeOutVal = 32767, default is 0*/ |
| 328 | ufshcd_dme_set(hba, UIC_ARG_MIB(0xd046), 32767); |
| 329 | } |
| 330 | |
| 331 | static int ufs_hi3660_pwr_change_notify(struct ufs_hba *hba, |
| 332 | enum ufs_notify_change_status status, |
| 333 | struct ufs_pa_layer_attr *dev_max_params, |
| 334 | struct ufs_pa_layer_attr *dev_req_params) |
| 335 | { |
| 336 | struct ufs_dev_params ufs_hisi_cap; |
| 337 | int ret = 0; |
| 338 | |
| 339 | if (!dev_req_params) { |
| 340 | dev_err(hba->dev, |
| 341 | "%s: incoming dev_req_params is NULL\n", __func__); |
| 342 | ret = -EINVAL; |
| 343 | goto out; |
| 344 | } |
| 345 | |
| 346 | switch (status) { |
| 347 | case PRE_CHANGE: |
| 348 | ufs_hisi_set_dev_cap(&ufs_hisi_cap); |
| 349 | ret = ufshcd_get_pwr_dev_param(&ufs_hisi_cap, |
| 350 | dev_max_params, dev_req_params); |
| 351 | if (ret) { |
| 352 | dev_err(hba->dev, |
| 353 | "%s: failed to determine capabilities\n", __func__); |
| 354 | goto out; |
| 355 | } |
| 356 | |
| 357 | ufs_hisi_pwr_change_pre_change(hba); |
| 358 | break; |
| 359 | case POST_CHANGE: |
| 360 | break; |
| 361 | default: |
| 362 | ret = -EINVAL; |
| 363 | break; |
| 364 | } |
| 365 | out: |
| 366 | return ret; |
| 367 | } |
| 368 | |
| 369 | static int ufs_hisi_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) |
| 370 | { |
| 371 | struct ufs_hisi_host *host = ufshcd_get_variant(hba); |
| 372 | |
| 373 | if (ufshcd_is_runtime_pm(pm_op)) |
| 374 | return 0; |
| 375 | |
| 376 | if (host->in_suspend) { |
| 377 | WARN_ON(1); |
| 378 | return 0; |
| 379 | } |
| 380 | |
| 381 | ufs_sys_ctrl_clr_bits(host, BIT_SYSCTRL_REF_CLOCK_EN, PHY_CLK_CTRL); |
| 382 | udelay(10); |
| 383 | /* set ref_dig_clk override of PHY PCS to 0 */ |
| 384 | ufs_sys_ctrl_writel(host, 0x00100000, UFS_DEVICE_RESET_CTRL); |
| 385 | |
| 386 | host->in_suspend = true; |
| 387 | |
| 388 | return 0; |
| 389 | } |
| 390 | |
| 391 | static int ufs_hisi_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) |
| 392 | { |
| 393 | struct ufs_hisi_host *host = ufshcd_get_variant(hba); |
| 394 | |
| 395 | if (!host->in_suspend) |
| 396 | return 0; |
| 397 | |
| 398 | /* set ref_dig_clk override of PHY PCS to 1 */ |
| 399 | ufs_sys_ctrl_writel(host, 0x00100010, UFS_DEVICE_RESET_CTRL); |
| 400 | udelay(10); |
| 401 | ufs_sys_ctrl_set_bits(host, BIT_SYSCTRL_REF_CLOCK_EN, PHY_CLK_CTRL); |
| 402 | |
| 403 | host->in_suspend = false; |
| 404 | return 0; |
| 405 | } |
| 406 | |
| 407 | static int ufs_hisi_get_resource(struct ufs_hisi_host *host) |
| 408 | { |
| 409 | struct resource *mem_res; |
| 410 | struct device *dev = host->hba->dev; |
| 411 | struct platform_device *pdev = to_platform_device(dev); |
| 412 | |
| 413 | /* get resource of ufs sys ctrl */ |
| 414 | mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
| 415 | host->ufs_sys_ctrl = devm_ioremap_resource(dev, mem_res); |
| 416 | if (IS_ERR(host->ufs_sys_ctrl)) |
| 417 | return PTR_ERR(host->ufs_sys_ctrl); |
| 418 | |
| 419 | return 0; |
| 420 | } |
| 421 | |
| 422 | static void ufs_hisi_set_pm_lvl(struct ufs_hba *hba) |
| 423 | { |
| 424 | hba->rpm_lvl = UFS_PM_LVL_1; |
| 425 | hba->spm_lvl = UFS_PM_LVL_3; |
| 426 | } |
| 427 | |
| 428 | /** |
| 429 | * ufs_hisi_init_common |
| 430 | * @hba: host controller instance |
| 431 | */ |
| 432 | static int ufs_hisi_init_common(struct ufs_hba *hba) |
| 433 | { |
| 434 | int err = 0; |
| 435 | struct device *dev = hba->dev; |
| 436 | struct ufs_hisi_host *host; |
| 437 | |
| 438 | host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); |
| 439 | if (!host) |
| 440 | return -ENOMEM; |
| 441 | |
| 442 | /* |
| 443 | * Inline crypto is currently broken with ufs-hisi because the keyslots |
| 444 | * overlap with the vendor-specific SYS CTRL registers -- and even if |
| 445 | * software uses only non-overlapping keyslots, the kernel crashes when |
| 446 | * programming a key or a UFS error occurs on the first encrypted I/O. |
| 447 | */ |
| 448 | hba->quirks |= UFSHCD_QUIRK_BROKEN_CRYPTO; |
| 449 | |
| 450 | host->hba = hba; |
| 451 | ufshcd_set_variant(hba, host); |
| 452 | |
| 453 | host->rst = devm_reset_control_get(dev, "rst"); |
| 454 | if (IS_ERR(host->rst)) { |
| 455 | dev_err(dev, "%s: failed to get reset control\n", __func__); |
| 456 | return PTR_ERR(host->rst); |
| 457 | } |
| 458 | |
| 459 | ufs_hisi_set_pm_lvl(hba); |
| 460 | |
| 461 | err = ufs_hisi_get_resource(host); |
| 462 | if (err) { |
| 463 | ufshcd_set_variant(hba, NULL); |
| 464 | return err; |
| 465 | } |
| 466 | |
| 467 | return 0; |
| 468 | } |
| 469 | |
| 470 | static int ufs_hi3660_init(struct ufs_hba *hba) |
| 471 | { |
| 472 | int ret = 0; |
| 473 | struct device *dev = hba->dev; |
| 474 | |
| 475 | ret = ufs_hisi_init_common(hba); |
| 476 | if (ret) { |
| 477 | dev_err(dev, "%s: ufs common init fail\n", __func__); |
| 478 | return ret; |
| 479 | } |
| 480 | |
| 481 | ufs_hi3660_clk_init(hba); |
| 482 | |
| 483 | ufs_hi3660_soc_init(hba); |
| 484 | |
| 485 | return 0; |
| 486 | } |
| 487 | |
| 488 | static struct ufs_hba_variant_ops ufs_hba_hisi_vops = { |
| 489 | .name = "hi3660", |
| 490 | .init = ufs_hi3660_init, |
| 491 | .link_startup_notify = ufs_hi3660_link_startup_notify, |
| 492 | .pwr_change_notify = ufs_hi3660_pwr_change_notify, |
| 493 | .suspend = ufs_hisi_suspend, |
| 494 | .resume = ufs_hisi_resume, |
| 495 | }; |
| 496 | |
| 497 | static int ufs_hisi_probe(struct platform_device *pdev) |
| 498 | { |
| 499 | return ufshcd_pltfrm_init(pdev, &ufs_hba_hisi_vops); |
| 500 | } |
| 501 | |
| 502 | static int ufs_hisi_remove(struct platform_device *pdev) |
| 503 | { |
| 504 | struct ufs_hba *hba = platform_get_drvdata(pdev); |
| 505 | |
| 506 | ufshcd_remove(hba); |
| 507 | return 0; |
| 508 | } |
| 509 | |
| 510 | static const struct of_device_id ufs_hisi_of_match[] = { |
| 511 | { .compatible = "hisilicon,hi3660-ufs" }, |
| 512 | {}, |
| 513 | }; |
| 514 | |
| 515 | MODULE_DEVICE_TABLE(of, ufs_hisi_of_match); |
| 516 | |
| 517 | static const struct dev_pm_ops ufs_hisi_pm_ops = { |
| 518 | .suspend = ufshcd_pltfrm_suspend, |
| 519 | .resume = ufshcd_pltfrm_resume, |
| 520 | .runtime_suspend = ufshcd_pltfrm_runtime_suspend, |
| 521 | .runtime_resume = ufshcd_pltfrm_runtime_resume, |
| 522 | .runtime_idle = ufshcd_pltfrm_runtime_idle, |
| 523 | }; |
| 524 | |
| 525 | static struct platform_driver ufs_hisi_pltform = { |
| 526 | .probe = ufs_hisi_probe, |
| 527 | .remove = ufs_hisi_remove, |
| 528 | .shutdown = ufshcd_pltfrm_shutdown, |
| 529 | .driver = { |
| 530 | .name = "ufshcd-hisi", |
| 531 | .pm = &ufs_hisi_pm_ops, |
| 532 | .of_match_table = of_match_ptr(ufs_hisi_of_match), |
| 533 | }, |
| 534 | }; |
| 535 | module_platform_driver(ufs_hisi_pltform); |
| 536 | |
| 537 | MODULE_LICENSE("GPL"); |
| 538 | MODULE_ALIAS("platform:ufshcd-hisi"); |
| 539 | MODULE_DESCRIPTION("HiSilicon Hixxxx UFS Driver"); |