Loading Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt +62 −1 Original line number Diff line number Diff line Loading @@ -54,7 +54,8 @@ Example: max-clock-frequency-hz = <100000000 19200000 0>; }; For UFS host controller in MSM platform following clocks are required - ==== MSM UFS platform driver properties ===== * For UFS host controller in MSM platform following clocks are required - Controller clock source - "core_clk_src", max-clock-frequency-hz = 200MHz Loading @@ -73,3 +74,63 @@ For UFS host controller in MSM platform following clocks are required - Optional reference clock input to UFS device "ref_clk", max-clock-frequency-hz = 19.2MHz * Following bus parameters are required - - qcom,msm-bus,name - qcom,msm-bus,num-cases - qcom,msm-bus,num-paths - qcom,msm-bus,vectors-KBps For the above four properties please refer to Documentation/devicetree/bindings/arm/msm/msm_bus.txt Note: The instantaneous bandwidth (IB) value in the vectors-KBps field should be zero as UFS data transfer path doesn't have latency requirements and voting for aggregated bandwidth (AB) should take care of providing optimum throughput requested. - qcom,bus-vector-names: specifies string IDs for the corresponding bus vectors in the same order as qcom,msm-bus,vectors-KBps property. Example: ufshc@0xfc598000 { ... qcom,msm-bus,name = "ufs1"; qcom,msm-bus,num-cases = <22>; qcom,msm-bus,num-paths = <2>; qcom,msm-bus,vectors-KBps = <95 512 0 0>, <1 650 0 0>, /* No vote */ <95 512 922 0>, <1 650 1000 0>, /* PWM G1 */ <95 512 1844 0>, <1 650 1000 0>, /* PWM G2 */ <95 512 3688 0>, <1 650 1000 0>, /* PWM G3 */ <95 512 7376 0>, <1 650 1000 0>, /* PWM G4 */ <95 512 1844 0>, <1 650 1000 0>, /* PWM G1 L2 */ <95 512 3688 0>, <1 650 1000 0>, /* PWM G2 L2 */ <95 512 7376 0>, <1 650 1000 0>, /* PWM G3 L2 */ <95 512 14752 0>, <1 650 1000 0>, /* PWM G4 L2 */ <95 512 127796 0>, <1 650 1000 0>, /* HS G1 RA */ <95 512 255591 0>, <1 650 1000 0>, /* HS G2 RA */ <95 512 511181 0>, <1 650 1000 0>, /* HS G3 RA */ <95 512 255591 0>, <1 650 1000 0>, /* HS G1 RA L2 */ <95 512 511181 0>, <1 650 1000 0>, /* HS G2 RA L2 */ <95 512 1022362 0>, <1 650 1000 0>, /* HS G3 RA L2 */ <95 512 149422 0>, <1 650 1000 0>, /* HS G1 RB */ <95 512 298189 0>, <1 650 1000 0>, /* HS G2 RB */ <95 512 596378 0>, <1 650 1000 0>, /* HS G3 RB */ <95 512 298189 0>, <1 650 1000 0>, /* HS G1 RB L2 */ <95 512 596378 0>, <1 650 1000 0>, /* HS G2 RB L2 */ <95 512 1192756 0>, <1 650 1000 0>, /* HS G3 RB L2 */ <95 512 4096000 0>, <1 650 1000 0>; /* Max. bandwidth */ qcom,bus-vector-names = "MIN", "PWM_G1_L1", "PWM_G2_L1", "PWM_G3_L1", "PWM_G4_L1", "PWM_G1_L2", "PWM_G2_L2", "PWM_G3_L2", "PWM_G4_L2", "HS_RA_G1_L1", "HS_RA_G2_L1", "HS_RA_G3_L1", "HS_RA_G1_L2", "HS_RA_G2_L2", "HS_RA_G3_L2", "HS_RB_G1_L1", "HS_RB_G2_L1", "HS_RB_G3_L1", "HS_RB_G1_L2", "HS_RB_G2_L2", "HS_RB_G3_L2", "MAX"; }; arch/arm/boot/dts/apq8084.dtsi +40 −0 Original line number Diff line number Diff line Loading @@ -2016,6 +2016,46 @@ max-clock-frequency-hz = <200000000>, <0>, <0>, <0>, <0>, <0>, <0>, <0>, <0>; qcom,msm-bus,name = "ufs1"; qcom,msm-bus,num-cases = <22>; qcom,msm-bus,num-paths = <2>; qcom,msm-bus,vectors-KBps = <95 512 0 0>, <1 650 0 0>, /* No vote */ <95 512 922 0>, <1 650 1000 0>, /* PWM G1 */ <95 512 1844 0>, <1 650 1000 0>, /* PWM G2 */ <95 512 3688 0>, <1 650 1000 0>, /* PWM G3 */ <95 512 7376 0>, <1 650 1000 0>, /* PWM G4 */ <95 512 1844 0>, <1 650 1000 0>, /* PWM G1 L2 */ <95 512 3688 0>, <1 650 1000 0>, /* PWM G2 L2 */ <95 512 7376 0>, <1 650 1000 0>, /* PWM G3 L2 */ <95 512 14752 0>, <1 650 1000 0>, /* PWM G4 L2 */ <95 512 127796 0>, <1 650 1000 0>, /* HS G1 RA */ <95 512 255591 0>, <1 650 1000 0>, /* HS G2 RA */ <95 512 511181 0>, <1 650 1000 0>, /* HS G3 RA */ <95 512 255591 0>, <1 650 1000 0>, /* HS G1 RA L2 */ <95 512 511181 0>, <1 650 1000 0>, /* HS G2 RA L2 */ <95 512 1022362 0>, <1 650 1000 0>, /* HS G3 RA L2 */ <95 512 149422 0>, <1 650 1000 0>, /* HS G1 RB */ <95 512 298189 0>, <1 650 1000 0>, /* HS G2 RB */ <95 512 596378 0>, <1 650 1000 0>, /* HS G3 RB */ <95 512 298189 0>, <1 650 1000 0>, /* HS G1 RB L2 */ <95 512 596378 0>, <1 650 1000 0>, /* HS G2 RB L2 */ <95 512 1192756 0>, <1 650 1000 0>, /* HS G3 RB L2 */ <95 512 4096000 0>, <1 650 1000 0>; /* Max. bandwidth */ qcom,bus-vector-names = "MIN", "PWM_G1_L1", "PWM_G2_L1", "PWM_G3_L1", "PWM_G4_L1", "PWM_G1_L2", "PWM_G2_L2", "PWM_G3_L2", "PWM_G4_L2", "HS_RA_G1_L1", "HS_RA_G2_L1", "HS_RA_G3_L1", "HS_RA_G1_L2", "HS_RA_G2_L2", "HS_RA_G3_L2", "HS_RB_G1_L1", "HS_RB_G2_L1", "HS_RB_G3_L1", "HS_RB_G1_L2", "HS_RB_G2_L2", "HS_RB_G3_L2", "MAX"; status = "disabled"; }; Loading drivers/scsi/ufs/ufs-msm.c +296 −42 Original line number Diff line number Diff line Loading @@ -20,6 +20,9 @@ #include <linux/of.h> #include <linux/iopoll.h> #include <linux/platform_device.h> #include <mach/msm_bus.h> #include "ufshcd.h" #include "unipro.h" Loading @@ -28,9 +31,7 @@ #define TX_FSM_HIBERN8 0x1 #define HBRN8_POLL_TOUT_MS 100 #define DEFAULT_CLK_RATE_HZ 1000000 static unsigned long msm_ufs_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs); #define BUS_VECTOR_NAME_LEN 32 /* MSM UFS host controller vendor specific registers */ enum { Loading Loading @@ -58,6 +59,58 @@ enum { MASK_CLK_NS_REG = 0xFFFC00, }; static LIST_HEAD(phy_list); struct msm_ufs_phy_calibration { u32 reg_offset; u32 cfg_value; }; struct msm_ufs_phy_vreg { const char *name; struct regulator *reg; int max_uA; int min_uV; int max_uV; bool enabled; }; struct msm_ufs_phy { struct list_head list; struct device *dev; void __iomem *mmio; struct clk *tx_iface_clk; struct clk *rx_iface_clk; bool is_iface_clk_enabled; struct clk *ref_clk_src; struct clk *ref_clk_parent; struct clk *ref_clk; bool is_ref_clk_enabled; struct msm_ufs_phy_vreg vdda_pll; struct msm_ufs_phy_vreg vdda_phy; }; struct msm_ufs_bus_vote { uint32_t client_handle; uint32_t curr_vote; int min_bw_vote; int max_bw_vote; int saved_vote; bool is_max_bw_needed; struct device_attribute max_bus_bw; }; struct msm_ufs_host { struct msm_ufs_phy *phy; struct ufs_hba *hba; struct msm_ufs_bus_vote bus_vote; struct ufs_pa_layer_attr dev_req_params; }; static unsigned long msm_ufs_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs); static int msm_ufs_update_bus_bw_vote(struct msm_ufs_host *host); /* MSM UFS PHY control registers */ #define COM_OFF(x) (0x000 + x) Loading Loading @@ -312,37 +365,6 @@ enum { #define VDDA_PLL_MIN_UV 1800000 #define VDDA_PLL_MAX_UV 1800000 static LIST_HEAD(phy_list); struct msm_ufs_phy_calibration { u32 reg_offset; u32 cfg_value; }; struct msm_ufs_phy_vreg { const char *name; struct regulator *reg; int max_uA; int min_uV; int max_uV; bool enabled; }; struct msm_ufs_phy { struct list_head list; struct device *dev; void __iomem *mmio; struct clk *tx_iface_clk; struct clk *rx_iface_clk; bool is_iface_clk_enabled; struct clk *ref_clk_src; struct clk *ref_clk_parent; struct clk *ref_clk; bool is_ref_clk_enabled; struct msm_ufs_phy_vreg vdda_pll; struct msm_ufs_phy_vreg vdda_phy; }; static struct msm_ufs_phy_calibration phy_cal_table[] = { { .cfg_value = 0x01, Loading Loading @@ -1171,7 +1193,8 @@ static int msm_ufs_enable_tx_lanes(struct ufs_hba *hba) int err; u32 tx_lanes; u32 val; struct msm_ufs_phy *phy = hba->priv; struct msm_ufs_host *host = hba->priv; struct msm_ufs_phy *phy = host->phy; err = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES), &tx_lanes); Loading Loading @@ -1298,7 +1321,8 @@ static inline void msm_ufs_deassert_reset(struct ufs_hba *hba) static int msm_ufs_hce_enable_notify(struct ufs_hba *hba, bool status) { struct msm_ufs_phy *phy = hba->priv; struct msm_ufs_host *host = hba->priv; struct msm_ufs_phy *phy = host->phy; u32 val; int err = -EINVAL; Loading Loading @@ -1365,7 +1389,8 @@ static int msm_ufs_link_startup_notify(struct ufs_hba *hba, bool status) static int msm_ufs_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) { struct msm_ufs_phy *phy = hba->priv; struct msm_ufs_host *host = hba->priv; struct msm_ufs_phy *phy = host->phy; int ret = 0; if (!phy) Loading Loading @@ -1398,7 +1423,8 @@ out: static int msm_ufs_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) { struct msm_ufs_phy *phy = hba->priv; struct msm_ufs_host *host = hba->priv; struct msm_ufs_phy *phy = host->phy; if (!phy) return 0; Loading Loading @@ -1675,7 +1701,8 @@ static int msm_ufs_pwr_change_notify(struct ufs_hba *hba, struct ufs_pa_layer_attr *dev_req_params) { int val; struct msm_ufs_phy *phy = hba->priv; struct msm_ufs_host *host = hba->priv; struct msm_ufs_phy *phy = host->phy; struct ufs_msm_dev_params ufs_msm_cap; int ret = 0; Loading Loading @@ -1717,6 +1744,11 @@ static int msm_ufs_pwr_change_notify(struct ufs_hba *hba, val = ~(MAX_U32 << dev_req_params->lane_tx); writel_relaxed(val, phy->mmio + UFS_PHY_TX_LANE_ENABLE); mb(); /* cache the power mode parameters to use internally */ memcpy(&host->dev_req_params, dev_req_params, sizeof(*dev_req_params)); msm_ufs_update_bus_bw_vote(host); break; default: ret = -EINVAL; Loading Loading @@ -1767,6 +1799,204 @@ static void msm_ufs_advertise_quirks(struct ufs_hba *hba) | UFSHCD_QUIRK_BROKEN_SUSPEND); } static int msm_ufs_get_bus_vote(struct msm_ufs_host *host, const char *speed_mode) { struct device *dev = host->hba->dev; struct device_node *np = dev->of_node; int err; const char *key = "qcom,bus-vector-names"; if (!speed_mode) { err = -EINVAL; goto out; } if (host->bus_vote.is_max_bw_needed && !!strcmp(speed_mode, "MIN")) err = of_property_match_string(np, key, "MAX"); else err = of_property_match_string(np, key, speed_mode); out: if (err < 0) dev_err(dev, "%s: Invalid %s mode %d\n", __func__, speed_mode, err); return err; } static int msm_ufs_set_bus_vote(struct msm_ufs_host *host, int vote) { int err = 0; if (vote != host->bus_vote.curr_vote) { err = msm_bus_scale_client_update_request( host->bus_vote.client_handle, vote); if (err) { dev_err(host->hba->dev, "%s: msm_bus_scale_client_update_request() failed: bus_client_handle=0x%x, vote=%d, err=%d\n", __func__, host->bus_vote.client_handle, vote, err); goto out; } host->bus_vote.curr_vote = vote; } out: return err; } static int msm_ufs_get_speed_mode(struct ufs_pa_layer_attr *p, char *result) { int err = 0; int gear = max_t(u32, p->gear_rx, p->gear_tx); int lanes = max_t(u32, p->lane_rx, p->lane_tx); int pwr = max_t(u32, map_unmap_pwr_mode(p->pwr_rx, true), map_unmap_pwr_mode(p->pwr_tx, true)); /* default to PWM Gear 1, Lane 1 if power mode is not initialized */ if (!gear) gear = 1; if (!lanes) lanes = 1; if (!p->pwr_rx && !p->pwr_tx) pwr = 0; pwr = map_unmap_pwr_mode(pwr, false); if (pwr < 0) { err = pwr; goto out; } if (pwr == FAST_MODE || pwr == FASTAUTO_MODE) snprintf(result, BUS_VECTOR_NAME_LEN, "%s_R%s_G%d_L%d", "HS", p->hs_rate == PA_HS_MODE_B ? "B" : "A", gear, lanes); else snprintf(result, BUS_VECTOR_NAME_LEN, "%s_G%d_L%d", "PWM", gear, lanes); out: return err; } static int msm_ufs_update_bus_bw_vote(struct msm_ufs_host *host) { int vote; int err = 0; char mode[BUS_VECTOR_NAME_LEN]; err = msm_ufs_get_speed_mode(&host->dev_req_params, mode); if (err) goto out; vote = msm_ufs_get_bus_vote(host, mode); if (vote >= 0) err = msm_ufs_set_bus_vote(host, vote); else err = vote; out: if (err) dev_err(host->hba->dev, "%s: failed %d\n", __func__, err); else host->bus_vote.saved_vote = vote; return err; } static int msm_ufs_setup_clocks(struct ufs_hba *hba, bool on) { struct msm_ufs_host *host = hba->priv; int err; int vote; if (on) { vote = host->bus_vote.saved_vote; if (vote == host->bus_vote.min_bw_vote) msm_ufs_update_bus_bw_vote(host); } else { vote = host->bus_vote.min_bw_vote; } err = msm_ufs_set_bus_vote(host, vote); if (err) dev_err(hba->dev, "%s: set bus vote failed %d\n", __func__, err); return err; } static ssize_t show_ufs_to_mem_max_bus_bw(struct device *dev, struct device_attribute *attr, char *buf) { struct ufs_hba *hba = dev_get_drvdata(dev); struct msm_ufs_host *host = hba->priv; return snprintf(buf, PAGE_SIZE, "%u\n", host->bus_vote.is_max_bw_needed); } static ssize_t store_ufs_to_mem_max_bus_bw(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ufs_hba *hba = dev_get_drvdata(dev); struct msm_ufs_host *host = hba->priv; uint32_t value; if (!kstrtou32(buf, 0, &value)) { host->bus_vote.is_max_bw_needed = !!value; msm_ufs_update_bus_bw_vote(host); } return count; } static int msm_ufs_bus_register(struct msm_ufs_host *host) { int err; struct msm_bus_scale_pdata *bus_pdata; struct device *dev = host->hba->dev; struct platform_device *pdev = to_platform_device(dev); struct device_node *np = dev->of_node; bus_pdata = msm_bus_cl_get_pdata(pdev); if (!bus_pdata) { dev_err(dev, "%s: failed to get bus vectors\n", __func__); err = -ENODATA; goto out; } err = of_property_count_strings(np, "qcom,bus-vector-names"); if (err < 0 || err != bus_pdata->num_usecases) { dev_err(dev, "%s: qcom,bus-vector-names not specified correctly %d\n", __func__, err); goto out; } host->bus_vote.client_handle = msm_bus_scale_register_client(bus_pdata); if (!host->bus_vote.client_handle) { dev_err(dev, "%s: msm_bus_scale_register_client failed\n", __func__); err = -EFAULT; goto out; } /* cache the vote index for minimum and maximum bandwidth */ host->bus_vote.min_bw_vote = msm_ufs_get_bus_vote(host, "MIN"); host->bus_vote.max_bw_vote = msm_ufs_get_bus_vote(host, "MAX"); host->bus_vote.max_bus_bw.show = show_ufs_to_mem_max_bus_bw; host->bus_vote.max_bus_bw.store = store_ufs_to_mem_max_bus_bw; sysfs_attr_init(&host->bus_vote.max_bus_bw.attr); host->bus_vote.max_bus_bw.attr.name = "max_bus_bw"; host->bus_vote.max_bus_bw.attr.mode = S_IRUGO | S_IWUSR; err = device_create_file(dev, &host->bus_vote.max_bus_bw); out: return err; } /** * msm_ufs_init - bind phy with controller * @hba: host controller instance Loading @@ -1780,18 +2010,33 @@ static void msm_ufs_advertise_quirks(struct ufs_hba *hba) static int msm_ufs_init(struct ufs_hba *hba) { int err; struct device *dev = hba->dev; struct msm_ufs_phy *phy = msm_get_ufs_phy(hba->dev); struct msm_ufs_host *host; if (IS_ERR(phy)) { err = PTR_ERR(phy); goto out; } hba->priv = (void *)phy; host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); if (!host) { err = -ENOMEM; dev_err(dev, "%s: no memory for msm ufs host\n", __func__); goto out; } host->phy = phy; host->hba = hba; hba->priv = (void *)host; err = msm_ufs_bus_register(host); if (err) goto out_host_free; err = msm_ufs_phy_power_on(phy); if (err) hba->priv = NULL; goto out_host_free; msm_ufs_advertise_quirks(hba); if (hba->quirks & UFSHCD_QUIRK_BROKEN_SUSPEND) { Loading Loading @@ -1819,14 +2064,22 @@ static int msm_ufs_init(struct ufs_hba *hba) hba->rpm_lvl = UFS_PM_LVL_3; hba->spm_lvl = UFS_PM_LVL_3; } out_host_free: if (err) { devm_kfree(dev, host); hba->priv = NULL; } out: return err; } static void msm_ufs_exit(struct ufs_hba *hba) { struct msm_ufs_phy *phy = hba->priv; struct msm_ufs_host *host = hba->priv; struct msm_ufs_phy *phy = host->phy; msm_bus_scale_unregister_client(host->bus_vote.client_handle); msm_ufs_phy_power_off(phy); } Loading Loading @@ -1977,6 +2230,7 @@ const struct ufs_hba_variant_ops ufs_hba_msm_vops = { .name = "msm", .init = msm_ufs_init, .exit = msm_ufs_exit, .setup_clocks = msm_ufs_setup_clocks, .hce_enable_notify = msm_ufs_hce_enable_notify, .link_startup_notify = msm_ufs_link_startup_notify, .pwr_change_notify = msm_ufs_pwr_change_notify, Loading Loading
Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt +62 −1 Original line number Diff line number Diff line Loading @@ -54,7 +54,8 @@ Example: max-clock-frequency-hz = <100000000 19200000 0>; }; For UFS host controller in MSM platform following clocks are required - ==== MSM UFS platform driver properties ===== * For UFS host controller in MSM platform following clocks are required - Controller clock source - "core_clk_src", max-clock-frequency-hz = 200MHz Loading @@ -73,3 +74,63 @@ For UFS host controller in MSM platform following clocks are required - Optional reference clock input to UFS device "ref_clk", max-clock-frequency-hz = 19.2MHz * Following bus parameters are required - - qcom,msm-bus,name - qcom,msm-bus,num-cases - qcom,msm-bus,num-paths - qcom,msm-bus,vectors-KBps For the above four properties please refer to Documentation/devicetree/bindings/arm/msm/msm_bus.txt Note: The instantaneous bandwidth (IB) value in the vectors-KBps field should be zero as UFS data transfer path doesn't have latency requirements and voting for aggregated bandwidth (AB) should take care of providing optimum throughput requested. - qcom,bus-vector-names: specifies string IDs for the corresponding bus vectors in the same order as qcom,msm-bus,vectors-KBps property. Example: ufshc@0xfc598000 { ... qcom,msm-bus,name = "ufs1"; qcom,msm-bus,num-cases = <22>; qcom,msm-bus,num-paths = <2>; qcom,msm-bus,vectors-KBps = <95 512 0 0>, <1 650 0 0>, /* No vote */ <95 512 922 0>, <1 650 1000 0>, /* PWM G1 */ <95 512 1844 0>, <1 650 1000 0>, /* PWM G2 */ <95 512 3688 0>, <1 650 1000 0>, /* PWM G3 */ <95 512 7376 0>, <1 650 1000 0>, /* PWM G4 */ <95 512 1844 0>, <1 650 1000 0>, /* PWM G1 L2 */ <95 512 3688 0>, <1 650 1000 0>, /* PWM G2 L2 */ <95 512 7376 0>, <1 650 1000 0>, /* PWM G3 L2 */ <95 512 14752 0>, <1 650 1000 0>, /* PWM G4 L2 */ <95 512 127796 0>, <1 650 1000 0>, /* HS G1 RA */ <95 512 255591 0>, <1 650 1000 0>, /* HS G2 RA */ <95 512 511181 0>, <1 650 1000 0>, /* HS G3 RA */ <95 512 255591 0>, <1 650 1000 0>, /* HS G1 RA L2 */ <95 512 511181 0>, <1 650 1000 0>, /* HS G2 RA L2 */ <95 512 1022362 0>, <1 650 1000 0>, /* HS G3 RA L2 */ <95 512 149422 0>, <1 650 1000 0>, /* HS G1 RB */ <95 512 298189 0>, <1 650 1000 0>, /* HS G2 RB */ <95 512 596378 0>, <1 650 1000 0>, /* HS G3 RB */ <95 512 298189 0>, <1 650 1000 0>, /* HS G1 RB L2 */ <95 512 596378 0>, <1 650 1000 0>, /* HS G2 RB L2 */ <95 512 1192756 0>, <1 650 1000 0>, /* HS G3 RB L2 */ <95 512 4096000 0>, <1 650 1000 0>; /* Max. bandwidth */ qcom,bus-vector-names = "MIN", "PWM_G1_L1", "PWM_G2_L1", "PWM_G3_L1", "PWM_G4_L1", "PWM_G1_L2", "PWM_G2_L2", "PWM_G3_L2", "PWM_G4_L2", "HS_RA_G1_L1", "HS_RA_G2_L1", "HS_RA_G3_L1", "HS_RA_G1_L2", "HS_RA_G2_L2", "HS_RA_G3_L2", "HS_RB_G1_L1", "HS_RB_G2_L1", "HS_RB_G3_L1", "HS_RB_G1_L2", "HS_RB_G2_L2", "HS_RB_G3_L2", "MAX"; };
arch/arm/boot/dts/apq8084.dtsi +40 −0 Original line number Diff line number Diff line Loading @@ -2016,6 +2016,46 @@ max-clock-frequency-hz = <200000000>, <0>, <0>, <0>, <0>, <0>, <0>, <0>, <0>; qcom,msm-bus,name = "ufs1"; qcom,msm-bus,num-cases = <22>; qcom,msm-bus,num-paths = <2>; qcom,msm-bus,vectors-KBps = <95 512 0 0>, <1 650 0 0>, /* No vote */ <95 512 922 0>, <1 650 1000 0>, /* PWM G1 */ <95 512 1844 0>, <1 650 1000 0>, /* PWM G2 */ <95 512 3688 0>, <1 650 1000 0>, /* PWM G3 */ <95 512 7376 0>, <1 650 1000 0>, /* PWM G4 */ <95 512 1844 0>, <1 650 1000 0>, /* PWM G1 L2 */ <95 512 3688 0>, <1 650 1000 0>, /* PWM G2 L2 */ <95 512 7376 0>, <1 650 1000 0>, /* PWM G3 L2 */ <95 512 14752 0>, <1 650 1000 0>, /* PWM G4 L2 */ <95 512 127796 0>, <1 650 1000 0>, /* HS G1 RA */ <95 512 255591 0>, <1 650 1000 0>, /* HS G2 RA */ <95 512 511181 0>, <1 650 1000 0>, /* HS G3 RA */ <95 512 255591 0>, <1 650 1000 0>, /* HS G1 RA L2 */ <95 512 511181 0>, <1 650 1000 0>, /* HS G2 RA L2 */ <95 512 1022362 0>, <1 650 1000 0>, /* HS G3 RA L2 */ <95 512 149422 0>, <1 650 1000 0>, /* HS G1 RB */ <95 512 298189 0>, <1 650 1000 0>, /* HS G2 RB */ <95 512 596378 0>, <1 650 1000 0>, /* HS G3 RB */ <95 512 298189 0>, <1 650 1000 0>, /* HS G1 RB L2 */ <95 512 596378 0>, <1 650 1000 0>, /* HS G2 RB L2 */ <95 512 1192756 0>, <1 650 1000 0>, /* HS G3 RB L2 */ <95 512 4096000 0>, <1 650 1000 0>; /* Max. bandwidth */ qcom,bus-vector-names = "MIN", "PWM_G1_L1", "PWM_G2_L1", "PWM_G3_L1", "PWM_G4_L1", "PWM_G1_L2", "PWM_G2_L2", "PWM_G3_L2", "PWM_G4_L2", "HS_RA_G1_L1", "HS_RA_G2_L1", "HS_RA_G3_L1", "HS_RA_G1_L2", "HS_RA_G2_L2", "HS_RA_G3_L2", "HS_RB_G1_L1", "HS_RB_G2_L1", "HS_RB_G3_L1", "HS_RB_G1_L2", "HS_RB_G2_L2", "HS_RB_G3_L2", "MAX"; status = "disabled"; }; Loading
drivers/scsi/ufs/ufs-msm.c +296 −42 Original line number Diff line number Diff line Loading @@ -20,6 +20,9 @@ #include <linux/of.h> #include <linux/iopoll.h> #include <linux/platform_device.h> #include <mach/msm_bus.h> #include "ufshcd.h" #include "unipro.h" Loading @@ -28,9 +31,7 @@ #define TX_FSM_HIBERN8 0x1 #define HBRN8_POLL_TOUT_MS 100 #define DEFAULT_CLK_RATE_HZ 1000000 static unsigned long msm_ufs_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs); #define BUS_VECTOR_NAME_LEN 32 /* MSM UFS host controller vendor specific registers */ enum { Loading Loading @@ -58,6 +59,58 @@ enum { MASK_CLK_NS_REG = 0xFFFC00, }; static LIST_HEAD(phy_list); struct msm_ufs_phy_calibration { u32 reg_offset; u32 cfg_value; }; struct msm_ufs_phy_vreg { const char *name; struct regulator *reg; int max_uA; int min_uV; int max_uV; bool enabled; }; struct msm_ufs_phy { struct list_head list; struct device *dev; void __iomem *mmio; struct clk *tx_iface_clk; struct clk *rx_iface_clk; bool is_iface_clk_enabled; struct clk *ref_clk_src; struct clk *ref_clk_parent; struct clk *ref_clk; bool is_ref_clk_enabled; struct msm_ufs_phy_vreg vdda_pll; struct msm_ufs_phy_vreg vdda_phy; }; struct msm_ufs_bus_vote { uint32_t client_handle; uint32_t curr_vote; int min_bw_vote; int max_bw_vote; int saved_vote; bool is_max_bw_needed; struct device_attribute max_bus_bw; }; struct msm_ufs_host { struct msm_ufs_phy *phy; struct ufs_hba *hba; struct msm_ufs_bus_vote bus_vote; struct ufs_pa_layer_attr dev_req_params; }; static unsigned long msm_ufs_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs); static int msm_ufs_update_bus_bw_vote(struct msm_ufs_host *host); /* MSM UFS PHY control registers */ #define COM_OFF(x) (0x000 + x) Loading Loading @@ -312,37 +365,6 @@ enum { #define VDDA_PLL_MIN_UV 1800000 #define VDDA_PLL_MAX_UV 1800000 static LIST_HEAD(phy_list); struct msm_ufs_phy_calibration { u32 reg_offset; u32 cfg_value; }; struct msm_ufs_phy_vreg { const char *name; struct regulator *reg; int max_uA; int min_uV; int max_uV; bool enabled; }; struct msm_ufs_phy { struct list_head list; struct device *dev; void __iomem *mmio; struct clk *tx_iface_clk; struct clk *rx_iface_clk; bool is_iface_clk_enabled; struct clk *ref_clk_src; struct clk *ref_clk_parent; struct clk *ref_clk; bool is_ref_clk_enabled; struct msm_ufs_phy_vreg vdda_pll; struct msm_ufs_phy_vreg vdda_phy; }; static struct msm_ufs_phy_calibration phy_cal_table[] = { { .cfg_value = 0x01, Loading Loading @@ -1171,7 +1193,8 @@ static int msm_ufs_enable_tx_lanes(struct ufs_hba *hba) int err; u32 tx_lanes; u32 val; struct msm_ufs_phy *phy = hba->priv; struct msm_ufs_host *host = hba->priv; struct msm_ufs_phy *phy = host->phy; err = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES), &tx_lanes); Loading Loading @@ -1298,7 +1321,8 @@ static inline void msm_ufs_deassert_reset(struct ufs_hba *hba) static int msm_ufs_hce_enable_notify(struct ufs_hba *hba, bool status) { struct msm_ufs_phy *phy = hba->priv; struct msm_ufs_host *host = hba->priv; struct msm_ufs_phy *phy = host->phy; u32 val; int err = -EINVAL; Loading Loading @@ -1365,7 +1389,8 @@ static int msm_ufs_link_startup_notify(struct ufs_hba *hba, bool status) static int msm_ufs_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) { struct msm_ufs_phy *phy = hba->priv; struct msm_ufs_host *host = hba->priv; struct msm_ufs_phy *phy = host->phy; int ret = 0; if (!phy) Loading Loading @@ -1398,7 +1423,8 @@ out: static int msm_ufs_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) { struct msm_ufs_phy *phy = hba->priv; struct msm_ufs_host *host = hba->priv; struct msm_ufs_phy *phy = host->phy; if (!phy) return 0; Loading Loading @@ -1675,7 +1701,8 @@ static int msm_ufs_pwr_change_notify(struct ufs_hba *hba, struct ufs_pa_layer_attr *dev_req_params) { int val; struct msm_ufs_phy *phy = hba->priv; struct msm_ufs_host *host = hba->priv; struct msm_ufs_phy *phy = host->phy; struct ufs_msm_dev_params ufs_msm_cap; int ret = 0; Loading Loading @@ -1717,6 +1744,11 @@ static int msm_ufs_pwr_change_notify(struct ufs_hba *hba, val = ~(MAX_U32 << dev_req_params->lane_tx); writel_relaxed(val, phy->mmio + UFS_PHY_TX_LANE_ENABLE); mb(); /* cache the power mode parameters to use internally */ memcpy(&host->dev_req_params, dev_req_params, sizeof(*dev_req_params)); msm_ufs_update_bus_bw_vote(host); break; default: ret = -EINVAL; Loading Loading @@ -1767,6 +1799,204 @@ static void msm_ufs_advertise_quirks(struct ufs_hba *hba) | UFSHCD_QUIRK_BROKEN_SUSPEND); } static int msm_ufs_get_bus_vote(struct msm_ufs_host *host, const char *speed_mode) { struct device *dev = host->hba->dev; struct device_node *np = dev->of_node; int err; const char *key = "qcom,bus-vector-names"; if (!speed_mode) { err = -EINVAL; goto out; } if (host->bus_vote.is_max_bw_needed && !!strcmp(speed_mode, "MIN")) err = of_property_match_string(np, key, "MAX"); else err = of_property_match_string(np, key, speed_mode); out: if (err < 0) dev_err(dev, "%s: Invalid %s mode %d\n", __func__, speed_mode, err); return err; } static int msm_ufs_set_bus_vote(struct msm_ufs_host *host, int vote) { int err = 0; if (vote != host->bus_vote.curr_vote) { err = msm_bus_scale_client_update_request( host->bus_vote.client_handle, vote); if (err) { dev_err(host->hba->dev, "%s: msm_bus_scale_client_update_request() failed: bus_client_handle=0x%x, vote=%d, err=%d\n", __func__, host->bus_vote.client_handle, vote, err); goto out; } host->bus_vote.curr_vote = vote; } out: return err; } static int msm_ufs_get_speed_mode(struct ufs_pa_layer_attr *p, char *result) { int err = 0; int gear = max_t(u32, p->gear_rx, p->gear_tx); int lanes = max_t(u32, p->lane_rx, p->lane_tx); int pwr = max_t(u32, map_unmap_pwr_mode(p->pwr_rx, true), map_unmap_pwr_mode(p->pwr_tx, true)); /* default to PWM Gear 1, Lane 1 if power mode is not initialized */ if (!gear) gear = 1; if (!lanes) lanes = 1; if (!p->pwr_rx && !p->pwr_tx) pwr = 0; pwr = map_unmap_pwr_mode(pwr, false); if (pwr < 0) { err = pwr; goto out; } if (pwr == FAST_MODE || pwr == FASTAUTO_MODE) snprintf(result, BUS_VECTOR_NAME_LEN, "%s_R%s_G%d_L%d", "HS", p->hs_rate == PA_HS_MODE_B ? "B" : "A", gear, lanes); else snprintf(result, BUS_VECTOR_NAME_LEN, "%s_G%d_L%d", "PWM", gear, lanes); out: return err; } static int msm_ufs_update_bus_bw_vote(struct msm_ufs_host *host) { int vote; int err = 0; char mode[BUS_VECTOR_NAME_LEN]; err = msm_ufs_get_speed_mode(&host->dev_req_params, mode); if (err) goto out; vote = msm_ufs_get_bus_vote(host, mode); if (vote >= 0) err = msm_ufs_set_bus_vote(host, vote); else err = vote; out: if (err) dev_err(host->hba->dev, "%s: failed %d\n", __func__, err); else host->bus_vote.saved_vote = vote; return err; } static int msm_ufs_setup_clocks(struct ufs_hba *hba, bool on) { struct msm_ufs_host *host = hba->priv; int err; int vote; if (on) { vote = host->bus_vote.saved_vote; if (vote == host->bus_vote.min_bw_vote) msm_ufs_update_bus_bw_vote(host); } else { vote = host->bus_vote.min_bw_vote; } err = msm_ufs_set_bus_vote(host, vote); if (err) dev_err(hba->dev, "%s: set bus vote failed %d\n", __func__, err); return err; } static ssize_t show_ufs_to_mem_max_bus_bw(struct device *dev, struct device_attribute *attr, char *buf) { struct ufs_hba *hba = dev_get_drvdata(dev); struct msm_ufs_host *host = hba->priv; return snprintf(buf, PAGE_SIZE, "%u\n", host->bus_vote.is_max_bw_needed); } static ssize_t store_ufs_to_mem_max_bus_bw(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ufs_hba *hba = dev_get_drvdata(dev); struct msm_ufs_host *host = hba->priv; uint32_t value; if (!kstrtou32(buf, 0, &value)) { host->bus_vote.is_max_bw_needed = !!value; msm_ufs_update_bus_bw_vote(host); } return count; } static int msm_ufs_bus_register(struct msm_ufs_host *host) { int err; struct msm_bus_scale_pdata *bus_pdata; struct device *dev = host->hba->dev; struct platform_device *pdev = to_platform_device(dev); struct device_node *np = dev->of_node; bus_pdata = msm_bus_cl_get_pdata(pdev); if (!bus_pdata) { dev_err(dev, "%s: failed to get bus vectors\n", __func__); err = -ENODATA; goto out; } err = of_property_count_strings(np, "qcom,bus-vector-names"); if (err < 0 || err != bus_pdata->num_usecases) { dev_err(dev, "%s: qcom,bus-vector-names not specified correctly %d\n", __func__, err); goto out; } host->bus_vote.client_handle = msm_bus_scale_register_client(bus_pdata); if (!host->bus_vote.client_handle) { dev_err(dev, "%s: msm_bus_scale_register_client failed\n", __func__); err = -EFAULT; goto out; } /* cache the vote index for minimum and maximum bandwidth */ host->bus_vote.min_bw_vote = msm_ufs_get_bus_vote(host, "MIN"); host->bus_vote.max_bw_vote = msm_ufs_get_bus_vote(host, "MAX"); host->bus_vote.max_bus_bw.show = show_ufs_to_mem_max_bus_bw; host->bus_vote.max_bus_bw.store = store_ufs_to_mem_max_bus_bw; sysfs_attr_init(&host->bus_vote.max_bus_bw.attr); host->bus_vote.max_bus_bw.attr.name = "max_bus_bw"; host->bus_vote.max_bus_bw.attr.mode = S_IRUGO | S_IWUSR; err = device_create_file(dev, &host->bus_vote.max_bus_bw); out: return err; } /** * msm_ufs_init - bind phy with controller * @hba: host controller instance Loading @@ -1780,18 +2010,33 @@ static void msm_ufs_advertise_quirks(struct ufs_hba *hba) static int msm_ufs_init(struct ufs_hba *hba) { int err; struct device *dev = hba->dev; struct msm_ufs_phy *phy = msm_get_ufs_phy(hba->dev); struct msm_ufs_host *host; if (IS_ERR(phy)) { err = PTR_ERR(phy); goto out; } hba->priv = (void *)phy; host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); if (!host) { err = -ENOMEM; dev_err(dev, "%s: no memory for msm ufs host\n", __func__); goto out; } host->phy = phy; host->hba = hba; hba->priv = (void *)host; err = msm_ufs_bus_register(host); if (err) goto out_host_free; err = msm_ufs_phy_power_on(phy); if (err) hba->priv = NULL; goto out_host_free; msm_ufs_advertise_quirks(hba); if (hba->quirks & UFSHCD_QUIRK_BROKEN_SUSPEND) { Loading Loading @@ -1819,14 +2064,22 @@ static int msm_ufs_init(struct ufs_hba *hba) hba->rpm_lvl = UFS_PM_LVL_3; hba->spm_lvl = UFS_PM_LVL_3; } out_host_free: if (err) { devm_kfree(dev, host); hba->priv = NULL; } out: return err; } static void msm_ufs_exit(struct ufs_hba *hba) { struct msm_ufs_phy *phy = hba->priv; struct msm_ufs_host *host = hba->priv; struct msm_ufs_phy *phy = host->phy; msm_bus_scale_unregister_client(host->bus_vote.client_handle); msm_ufs_phy_power_off(phy); } Loading Loading @@ -1977,6 +2230,7 @@ const struct ufs_hba_variant_ops ufs_hba_msm_vops = { .name = "msm", .init = msm_ufs_init, .exit = msm_ufs_exit, .setup_clocks = msm_ufs_setup_clocks, .hce_enable_notify = msm_ufs_hce_enable_notify, .link_startup_notify = msm_ufs_link_startup_notify, .pwr_change_notify = msm_ufs_pwr_change_notify, Loading