diff options
Diffstat (limited to 'drivers/scsi/ufs/ufs-qcom.c')
-rw-r--r-- | drivers/scsi/ufs/ufs-qcom.c | 227 |
1 files changed, 73 insertions, 154 deletions
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index 3aeadb14aae1..b4d1b5c22987 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -1,21 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2013-2016, Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <linux/time.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/phy/phy.h> +#include <linux/reset-controller.h> #include "ufshcd.h" #include "ufshcd-pltfrm.h" @@ -49,6 +41,11 @@ static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host); static int ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(struct ufs_hba *hba, u32 clk_cycles); +static struct ufs_qcom_host *rcdev_to_ufs_host(struct reset_controller_dev *rcd) +{ + return container_of(rcd, struct ufs_qcom_host, rcdev); +} + static void ufs_qcom_dump_regs_wrapper(struct ufs_hba *hba, int offset, int len, const char *prefix, void *priv) { @@ -255,11 +252,6 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba) if (is_rate_B) phy_set_mode(phy, PHY_MODE_UFS_HS_B); - /* Assert PHY reset and apply PHY calibration values */ - ufs_qcom_assert_reset(hba); - /* provide 1ms delay to let the reset pulse propagate */ - usleep_range(1000, 1100); - /* phy initialization - calibrate the phy */ ret = phy_init(phy); if (ret) { @@ -268,15 +260,6 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba) goto out; } - /* De-assert PHY reset and start serdes */ - ufs_qcom_deassert_reset(hba); - - /* - * after reset deassertion, phy will need all ref clocks, - * voltage, current to settle down before starting serdes. - */ - usleep_range(1000, 1100); - /* power on phy - start serdes and phy's power and clocks */ ret = phy_power_on(phy); if (ret) { @@ -290,7 +273,6 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba) return 0; out_disable_phy: - ufs_qcom_assert_reset(hba); phy_exit(phy); out: return ret; @@ -554,21 +536,10 @@ static int ufs_qcom_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) ufs_qcom_disable_lane_clks(host); phy_power_off(phy); - /* Assert PHY soft reset */ - ufs_qcom_assert_reset(hba); - goto out; - } - - /* - * If UniPro link is not active, PHY ref_clk, main PHY analog power - * rail and low noise analog power rail for PLL can be switched off. - */ - if (!ufs_qcom_is_link_active(hba)) { + } else if (!ufs_qcom_is_link_active(hba)) { ufs_qcom_disable_lane_clks(host); - phy_power_off(phy); } -out: return ret; } @@ -578,118 +549,25 @@ static int ufs_qcom_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) struct phy *phy = host->generic_phy; int err; - err = phy_power_on(phy); - if (err) { - dev_err(hba->dev, "%s: failed enabling regs, err = %d\n", - __func__, err); - goto out; - } - - err = ufs_qcom_enable_lane_clks(host); - if (err) - goto out; - - hba->is_sys_suspended = false; - -out: - return err; -} - -struct ufs_qcom_dev_params { - u32 pwm_rx_gear; /* pwm rx gear to work in */ - u32 pwm_tx_gear; /* pwm tx gear to work in */ - u32 hs_rx_gear; /* hs rx gear to work in */ - u32 hs_tx_gear; /* hs tx gear to work in */ - u32 rx_lanes; /* number of rx lanes */ - u32 tx_lanes; /* number of tx lanes */ - u32 rx_pwr_pwm; /* rx pwm working pwr */ - u32 tx_pwr_pwm; /* tx pwm working pwr */ - u32 rx_pwr_hs; /* rx hs working pwr */ - u32 tx_pwr_hs; /* tx hs working pwr */ - u32 hs_rate; /* rate A/B to work in HS */ - u32 desired_working_mode; -}; + if (ufs_qcom_is_link_off(hba)) { + err = phy_power_on(phy); + if (err) { + dev_err(hba->dev, "%s: failed PHY power on: %d\n", + __func__, err); + return err; + } -static int ufs_qcom_get_pwr_dev_param(struct ufs_qcom_dev_params *qcom_param, - struct ufs_pa_layer_attr *dev_max, - struct ufs_pa_layer_attr *agreed_pwr) -{ - int min_qcom_gear; - int min_dev_gear; - bool is_dev_sup_hs = false; - bool is_qcom_max_hs = false; - - if (dev_max->pwr_rx == FAST_MODE) - is_dev_sup_hs = true; - - if (qcom_param->desired_working_mode == FAST) { - is_qcom_max_hs = true; - min_qcom_gear = min_t(u32, qcom_param->hs_rx_gear, - qcom_param->hs_tx_gear); - } else { - min_qcom_gear = min_t(u32, qcom_param->pwm_rx_gear, - qcom_param->pwm_tx_gear); - } + err = ufs_qcom_enable_lane_clks(host); + if (err) + return err; - /* - * device doesn't support HS but qcom_param->desired_working_mode is - * HS, thus device and qcom_param don't agree - */ - if (!is_dev_sup_hs && is_qcom_max_hs) { - pr_err("%s: failed to agree on power mode (device doesn't support HS but requested power is HS)\n", - __func__); - return -ENOTSUPP; - } else if (is_dev_sup_hs && is_qcom_max_hs) { - /* - * since device supports HS, it supports FAST_MODE. - * since qcom_param->desired_working_mode is also HS - * then final decision (FAST/FASTAUTO) is done according - * to qcom_params as it is the restricting factor - */ - agreed_pwr->pwr_rx = agreed_pwr->pwr_tx = - qcom_param->rx_pwr_hs; - } else { - /* - * here qcom_param->desired_working_mode is PWM. - * it doesn't matter whether device supports HS or PWM, - * in both cases qcom_param->desired_working_mode will - * determine the mode - */ - agreed_pwr->pwr_rx = agreed_pwr->pwr_tx = - qcom_param->rx_pwr_pwm; + } else if (!ufs_qcom_is_link_active(hba)) { + err = ufs_qcom_enable_lane_clks(host); + if (err) + return err; } - /* - * we would like tx to work in the minimum number of lanes - * between device capability and vendor preferences. - * the same decision will be made for rx - */ - agreed_pwr->lane_tx = min_t(u32, dev_max->lane_tx, - qcom_param->tx_lanes); - agreed_pwr->lane_rx = min_t(u32, dev_max->lane_rx, - qcom_param->rx_lanes); - - /* device maximum gear is the minimum between device rx and tx gears */ - min_dev_gear = min_t(u32, dev_max->gear_rx, dev_max->gear_tx); - - /* - * if both device capabilities and vendor pre-defined preferences are - * both HS or both PWM then set the minimum gear to be the chosen - * working gear. - * if one is PWM and one is HS then the one that is PWM get to decide - * what is the gear, as it is the one that also decided previously what - * pwr the device will be configured to. - */ - if ((is_dev_sup_hs && is_qcom_max_hs) || - (!is_dev_sup_hs && !is_qcom_max_hs)) - agreed_pwr->gear_rx = agreed_pwr->gear_tx = - min_t(u32, min_dev_gear, min_qcom_gear); - else if (!is_dev_sup_hs) - agreed_pwr->gear_rx = agreed_pwr->gear_tx = min_dev_gear; - else - agreed_pwr->gear_rx = agreed_pwr->gear_tx = min_qcom_gear; - - agreed_pwr->hs_rate = qcom_param->hs_rate; + hba->is_sys_suspended = false; return 0; } @@ -920,7 +798,7 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba, { u32 val; struct ufs_qcom_host *host = ufshcd_get_variant(hba); - struct ufs_qcom_dev_params ufs_qcom_cap; + struct ufs_dev_params ufs_qcom_cap; int ret = 0; if (!dev_req_params) { @@ -959,9 +837,9 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba, ufs_qcom_cap.hs_rx_gear = UFS_HS_G2; } - ret = ufs_qcom_get_pwr_dev_param(&ufs_qcom_cap, - dev_max_params, - dev_req_params); + ret = ufshcd_get_pwr_dev_param(&ufs_qcom_cap, + dev_max_params, + dev_req_params); if (ret) { pr_err("%s: failed to determine capabilities\n", __func__); @@ -1118,8 +996,6 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on, return 0; if (on && (status == POST_CHANGE)) { - phy_power_on(host->generic_phy); - /* enable the device ref clock for HS mode*/ if (ufshcd_is_hs_mode(&hba->pwr_info)) ufs_qcom_dev_ref_clk_ctrl(host, true); @@ -1131,9 +1007,6 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on, if (!ufs_qcom_is_link_active(hba)) { /* disable device ref_clk */ ufs_qcom_dev_ref_clk_ctrl(host, false); - - /* powering off PHY during aggressive clk gating */ - phy_power_off(host->generic_phy); } vote = host->bus_vote.min_bw_vote; @@ -1147,6 +1020,41 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on, return err; } +static int +ufs_qcom_reset_assert(struct reset_controller_dev *rcdev, unsigned long id) +{ + struct ufs_qcom_host *host = rcdev_to_ufs_host(rcdev); + + /* Currently this code only knows about a single reset. */ + WARN_ON(id); + ufs_qcom_assert_reset(host->hba); + /* provide 1ms delay to let the reset pulse propagate. */ + usleep_range(1000, 1100); + return 0; +} + +static int +ufs_qcom_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id) +{ + struct ufs_qcom_host *host = rcdev_to_ufs_host(rcdev); + + /* Currently this code only knows about a single reset. */ + WARN_ON(id); + ufs_qcom_deassert_reset(host->hba); + + /* + * after reset deassertion, phy will need all ref clocks, + * voltage, current to settle down before starting serdes. + */ + usleep_range(1000, 1100); + return 0; +} + +static const struct reset_control_ops ufs_qcom_reset_ops = { + .assert = ufs_qcom_reset_assert, + .deassert = ufs_qcom_reset_deassert, +}; + #define ANDROID_BOOT_DEV_MAX 30 static char android_boot_dev[ANDROID_BOOT_DEV_MAX]; @@ -1191,6 +1099,17 @@ static int ufs_qcom_init(struct ufs_hba *hba) host->hba = hba; ufshcd_set_variant(hba, host); + /* Fire up the reset controller. Failure here is non-fatal. */ + host->rcdev.of_node = dev->of_node; + host->rcdev.ops = &ufs_qcom_reset_ops; + host->rcdev.owner = dev->driver->owner; + host->rcdev.nr_resets = 1; + err = devm_reset_controller_register(dev, &host->rcdev); + if (err) { + dev_warn(dev, "Failed to register reset controller\n"); + err = 0; + } + /* * voting/devoting device ref_clk source is time consuming hence * skip devoting it during aggressive clock gating. This clock |