diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-11-02 15:13:50 -1000 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-11-02 15:13:50 -1000 |
commit | 6ed92e559a2ea572ae2bac5cbeddd1dc8cb667b6 (patch) | |
tree | aecc082cb2371d6cf3bceef203342fee48957ae0 /drivers/ufs/host/ufshcd-pltfrm.c | |
parent | 90a300dc0553c5c4a3324ca6de5877c834d27af7 (diff) | |
parent | a75a16c62a2540f11eeae4f2b50e95deefb652ea (diff) | |
download | lwn-6ed92e559a2ea572ae2bac5cbeddd1dc8cb667b6.tar.gz lwn-6ed92e559a2ea572ae2bac5cbeddd1dc8cb667b6.zip |
Merge tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
Pull SCSI updates from James Bottomley:
"Updates to the usual drivers (ufs, megaraid_sas, lpfc, target, ibmvfc,
scsi_debug) plus the usual assorted minor fixes and updates.
The major change this time around is a prep patch for rethreading of
the driver reset handler API not to take a scsi_cmd structure which
starts to reduce various drivers' dependence on scsi_cmd in error
handling"
* tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi: (132 commits)
scsi: ufs: core: Leave space for '\0' in utf8 desc string
scsi: ufs: core: Conversion to bool not necessary
scsi: ufs: core: Fix race between force complete and ISR
scsi: megaraid: Fix up debug message in megaraid_abort_and_reset()
scsi: aic79xx: Fix up NULL command in ahd_done()
scsi: message: fusion: Initialize return value in mptfc_bus_reset()
scsi: mpt3sas: Fix loop logic
scsi: snic: Remove useless code in snic_dr_clean_pending_req()
scsi: core: Add comment to target_destroy in scsi_host_template
scsi: core: Clean up scsi_dev_queue_ready()
scsi: pmcraid: Add missing scsi_device_put() in pmcraid_eh_target_reset_handler()
scsi: target: core: Fix kernel-doc comment
scsi: pmcraid: Fix kernel-doc comment
scsi: core: Handle depopulation and restoration in progress
scsi: ufs: core: Add support for parsing OPP
scsi: ufs: core: Add OPP support for scaling clocks and regulators
scsi: ufs: dt-bindings: common: Add OPP table
scsi: scsi_debug: Add param to control sdev's allow_restart
scsi: scsi_debug: Add debugfs interface to fail target reset
scsi: scsi_debug: Add new error injection type: Reset LUN failed
...
Diffstat (limited to 'drivers/ufs/host/ufshcd-pltfrm.c')
-rw-r--r-- | drivers/ufs/host/ufshcd-pltfrm.c | 93 |
1 files changed, 88 insertions, 5 deletions
diff --git a/drivers/ufs/host/ufshcd-pltfrm.c b/drivers/ufs/host/ufshcd-pltfrm.c index 797a4dfe45d9..da2558e274b4 100644 --- a/drivers/ufs/host/ufshcd-pltfrm.c +++ b/drivers/ufs/host/ufshcd-pltfrm.c @@ -10,6 +10,7 @@ #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pm_opp.h> #include <linux/pm_runtime.h> #include <linux/of.h> @@ -121,7 +122,7 @@ static bool phandle_exists(const struct device_node *np, #define MAX_PROP_SIZE 32 int ufshcd_populate_vreg(struct device *dev, const char *name, - struct ufs_vreg **out_vreg) + struct ufs_vreg **out_vreg, bool skip_current) { char prop_name[MAX_PROP_SIZE]; struct ufs_vreg *vreg = NULL; @@ -147,6 +148,11 @@ int ufshcd_populate_vreg(struct device *dev, const char *name, if (!vreg->name) return -ENOMEM; + if (skip_current) { + vreg->max_uA = 0; + goto out; + } + snprintf(prop_name, MAX_PROP_SIZE, "%s-max-microamp", name); if (of_property_read_u32(np, prop_name, &vreg->max_uA)) { dev_info(dev, "%s: unable to find %s\n", __func__, prop_name); @@ -175,19 +181,19 @@ static int ufshcd_parse_regulator_info(struct ufs_hba *hba) struct device *dev = hba->dev; struct ufs_vreg_info *info = &hba->vreg_info; - err = ufshcd_populate_vreg(dev, "vdd-hba", &info->vdd_hba); + err = ufshcd_populate_vreg(dev, "vdd-hba", &info->vdd_hba, true); if (err) goto out; - err = ufshcd_populate_vreg(dev, "vcc", &info->vcc); + err = ufshcd_populate_vreg(dev, "vcc", &info->vcc, false); if (err) goto out; - err = ufshcd_populate_vreg(dev, "vccq", &info->vccq); + err = ufshcd_populate_vreg(dev, "vccq", &info->vccq, false); if (err) goto out; - err = ufshcd_populate_vreg(dev, "vccq2", &info->vccq2); + err = ufshcd_populate_vreg(dev, "vccq2", &info->vccq2, false); out: return err; } @@ -207,6 +213,77 @@ static void ufshcd_init_lanes_per_dir(struct ufs_hba *hba) } } +static int ufshcd_parse_operating_points(struct ufs_hba *hba) +{ + struct device *dev = hba->dev; + struct device_node *np = dev->of_node; + struct dev_pm_opp_config config = {}; + struct ufs_clk_info *clki; + const char **clk_names; + int cnt, i, ret; + + if (!of_find_property(np, "operating-points-v2", NULL)) + return 0; + + if (of_find_property(np, "freq-table-hz", NULL)) { + dev_err(dev, "%s: operating-points and freq-table-hz are incompatible\n", + __func__); + return -EINVAL; + } + + cnt = of_property_count_strings(np, "clock-names"); + if (cnt <= 0) { + dev_err(dev, "%s: Missing clock-names\n", __func__); + return -ENODEV; + } + + /* OPP expects clk_names to be NULL terminated */ + clk_names = devm_kcalloc(dev, cnt + 1, sizeof(*clk_names), GFP_KERNEL); + if (!clk_names) + return -ENOMEM; + + /* + * We still need to get reference to all clocks as the UFS core uses + * them separately. + */ + for (i = 0; i < cnt; i++) { + ret = of_property_read_string_index(np, "clock-names", i, + &clk_names[i]); + if (ret) + return ret; + + clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL); + if (!clki) + return -ENOMEM; + + clki->name = devm_kstrdup(dev, clk_names[i], GFP_KERNEL); + if (!clki->name) + return -ENOMEM; + + if (!strcmp(clk_names[i], "ref_clk")) + clki->keep_link_active = true; + + list_add_tail(&clki->list, &hba->clk_list_head); + } + + config.clk_names = clk_names, + config.config_clks = ufshcd_opp_config_clks; + + ret = devm_pm_opp_set_config(dev, &config); + if (ret) + return ret; + + ret = devm_pm_opp_of_add_table(dev); + if (ret) { + dev_err(dev, "Failed to add OPP table: %d\n", ret); + return ret; + } + + hba->use_pm_opp = true; + + return 0; +} + /** * ufshcd_get_pwr_dev_param - get finally agreed attributes for * power mode change @@ -373,6 +450,12 @@ int ufshcd_pltfrm_init(struct platform_device *pdev, ufshcd_init_lanes_per_dir(hba); + err = ufshcd_parse_operating_points(hba); + if (err) { + dev_err(dev, "%s: OPP parse failed %d\n", __func__, err); + goto dealloc_host; + } + err = ufshcd_init(hba, mmio_base, irq); if (err) { dev_err_probe(dev, err, "Initialization failed with error %d\n", |