From 5e8d90070b87df4237d3dc88ffa652f30badce85 Mon Sep 17 00:00:00 2001 From: Adam Radford Date: Wed, 9 Jul 2014 15:17:58 -0700 Subject: megaraid_sas: Version and Changelog update The following patch for megaraid_sas updates the driver version and Documentation/scsi/ChangeLog.megaraid_sas. Signed-off-by: Adam Radford Reviewed-by: Martin K. Petersen Signed-off-by: Christoph Hellwig --- Documentation/scsi/ChangeLog.megaraid_sas | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'Documentation') diff --git a/Documentation/scsi/ChangeLog.megaraid_sas b/Documentation/scsi/ChangeLog.megaraid_sas index 91ba58ef02d..18b57099004 100644 --- a/Documentation/scsi/ChangeLog.megaraid_sas +++ b/Documentation/scsi/ChangeLog.megaraid_sas @@ -1,3 +1,17 @@ +Release Date : Thu. Jun 19, 2014 17:00:00 PST 2014 - + (emaild-id:megaraidlinux@lsi.com) + Adam Radford + Kashyap Desai + Sumit Saxena + Uday Lingala +Current Version : 06.803.02.00-rc1 +Old Version : 06.803.01.00-rc1 + 1. Fix reset_mutex leak in megasas_reset_fusion(). + 2. Remove unused variables in megasas_instance. + 3. Fix LD/VF affiliation parsing. + 4. Add missing initial call to megasas_get_ld_vf_affiliation(). + 5. Version and Changelog update. +------------------------------------------------------------------------------- Release Date : Mon. Mar 10, 2014 17:00:00 PST 2014 - (emaild-id:megaraidlinux@lsi.com) Adam Radford -- cgit v1.2.3-70-g09d2 From aa497613093412ee26ef4bfa4ffec8391553dfca Mon Sep 17 00:00:00 2001 From: Sujit Reddy Thumma Date: Thu, 25 Sep 2014 15:32:22 +0300 Subject: ufs: Add regulator enable support UFS devices are powered by at most three external power supplies - - VCC - The flash memory core power supply, 2.7V to 3.6V or 1.70V to 1.95V - VCCQ - The controller and I/O power supply, 1.1V to 1.3V - VCCQ2 - Secondary controller and/or I/O power supply, 1.65V to 1.95V For some devices VCCQ or VCCQ2 are optional as they can be generated using internal LDO inside the UFS device. Add DT bindings for voltage regulators that can be controlled from host driver. Signed-off-by: Sujit Reddy Thumma Signed-off-by: Dolev Raviv Signed-off-by: Christoph Hellwig --- .../devicetree/bindings/ufs/ufshcd-pltfrm.txt | 24 +++ drivers/scsi/ufs/ufs.h | 25 +++ drivers/scsi/ufs/ufshcd-pltfrm.c | 100 +++++++++++ drivers/scsi/ufs/ufshcd.c | 193 ++++++++++++++++++++- drivers/scsi/ufs/ufshcd.h | 3 + 5 files changed, 342 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt index 20468b2a751..65e31173dcc 100644 --- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt +++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt @@ -8,9 +8,33 @@ Required properties: - interrupts : - reg : +Optional properties: +- vcc-supply : phandle to VCC supply regulator node +- vccq-supply : phandle to VCCQ supply regulator node +- vccq2-supply : phandle to VCCQ2 supply regulator node +- vcc-supply-1p8 : For embedded UFS devices, valid VCC range is 1.7-1.95V + or 2.7-3.6V. This boolean property when set, specifies + to use low voltage range of 1.7-1.95V. Note for external + UFS cards this property is invalid and valid VCC range is + always 2.7-3.6V. +- vcc-max-microamp : specifies max. load that can be drawn from vcc supply +- vccq-max-microamp : specifies max. load that can be drawn from vccq supply +- vccq2-max-microamp : specifies max. load that can be drawn from vccq2 supply + +Note: If above properties are not defined it can be assumed that the supply +regulators are always on. + Example: ufshc@0xfc598000 { compatible = "jedec,ufs-1.1"; reg = <0xfc598000 0x800>; interrupts = <0 28 0>; + + vcc-supply = <&xxx_reg1>; + vcc-supply-1p8; + vccq-supply = <&xxx_reg2>; + vccq2-supply = <&xxx_reg3>; + vcc-max-microamp = 500000; + vccq-max-microamp = 200000; + vccq2-max-microamp = 200000; }; diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index fafcf5e354c..729ce7d62d1 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -362,4 +362,29 @@ struct ufs_query_res { struct utp_upiu_query upiu_res; }; +#define UFS_VREG_VCC_MIN_UV 2700000 /* uV */ +#define UFS_VREG_VCC_MAX_UV 3600000 /* uV */ +#define UFS_VREG_VCC_1P8_MIN_UV 1700000 /* uV */ +#define UFS_VREG_VCC_1P8_MAX_UV 1950000 /* uV */ +#define UFS_VREG_VCCQ_MIN_UV 1100000 /* uV */ +#define UFS_VREG_VCCQ_MAX_UV 1300000 /* uV */ +#define UFS_VREG_VCCQ2_MIN_UV 1650000 /* uV */ +#define UFS_VREG_VCCQ2_MAX_UV 1950000 /* uV */ + +struct ufs_vreg { + struct regulator *reg; + const char *name; + bool enabled; + int min_uV; + int max_uV; + int min_uA; + int max_uA; +}; + +struct ufs_vreg_info { + struct ufs_vreg *vcc; + struct ufs_vreg *vccq; + struct ufs_vreg *vccq2; +}; + #endif /* End of Header */ diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index d727b1a83e9..51e47c429a1 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -53,6 +53,99 @@ static struct ufs_hba_variant_ops *get_variant_ops(struct device *dev) return NULL; } +#define MAX_PROP_SIZE 32 +static int ufshcd_populate_vreg(struct device *dev, const char *name, + struct ufs_vreg **out_vreg) +{ + int ret = 0; + char prop_name[MAX_PROP_SIZE]; + struct ufs_vreg *vreg = NULL; + struct device_node *np = dev->of_node; + + if (!np) { + dev_err(dev, "%s: non DT initialization\n", __func__); + goto out; + } + + snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", name); + if (!of_parse_phandle(np, prop_name, 0)) { + dev_info(dev, "%s: Unable to find %s regulator, assuming enabled\n", + __func__, prop_name); + goto out; + } + + vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL); + if (!vreg) { + dev_err(dev, "No memory for %s regulator\n", name); + goto out; + } + + vreg->name = kstrdup(name, GFP_KERNEL); + + snprintf(prop_name, MAX_PROP_SIZE, "%s-max-microamp", name); + ret = of_property_read_u32(np, prop_name, &vreg->max_uA); + if (ret) { + dev_err(dev, "%s: unable to find %s err %d\n", + __func__, prop_name, ret); + goto out_free; + } + + vreg->min_uA = 0; + if (!strcmp(name, "vcc")) { + if (of_property_read_bool(np, "vcc-supply-1p8")) { + vreg->min_uV = UFS_VREG_VCC_1P8_MIN_UV; + vreg->max_uV = UFS_VREG_VCC_1P8_MAX_UV; + } else { + vreg->min_uV = UFS_VREG_VCC_MIN_UV; + vreg->max_uV = UFS_VREG_VCC_MAX_UV; + } + } else if (!strcmp(name, "vccq")) { + vreg->min_uV = UFS_VREG_VCCQ_MIN_UV; + vreg->max_uV = UFS_VREG_VCCQ_MAX_UV; + } else if (!strcmp(name, "vccq2")) { + vreg->min_uV = UFS_VREG_VCCQ2_MIN_UV; + vreg->max_uV = UFS_VREG_VCCQ2_MAX_UV; + } + + goto out; + +out_free: + devm_kfree(dev, vreg); + vreg = NULL; +out: + if (!ret) + *out_vreg = vreg; + return ret; +} + +/** + * ufshcd_parse_regulator_info - get regulator info from device tree + * @hba: per adapter instance + * + * Get regulator info from device tree for vcc, vccq, vccq2 power supplies. + * If any of the supplies are not defined it is assumed that they are always-on + * and hence return zero. If the property is defined but parsing is failed + * then return corresponding error. + */ +static int ufshcd_parse_regulator_info(struct ufs_hba *hba) +{ + int err; + struct device *dev = hba->dev; + struct ufs_vreg_info *info = &hba->vreg_info; + + err = ufshcd_populate_vreg(dev, "vcc", &info->vcc); + if (err) + goto out; + + err = ufshcd_populate_vreg(dev, "vccq", &info->vccq); + if (err) + goto out; + + err = ufshcd_populate_vreg(dev, "vccq2", &info->vccq2); +out: + return err; +} + #ifdef CONFIG_PM /** * ufshcd_pltfrm_suspend - suspend power management function @@ -173,6 +266,13 @@ static int ufshcd_pltfrm_probe(struct platform_device *pdev) hba->vops = get_variant_ops(&pdev->dev); + err = ufshcd_parse_regulator_info(hba); + if (err) { + dev_err(&pdev->dev, "%s: regulator init failed %d\n", + __func__, err); + goto out; + } + pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index d0565b07dc8..ef8519e70e9 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -68,6 +68,16 @@ /* Interrupt aggregation default timeout, unit: 40us */ #define INT_AGGR_DEF_TO 0x02 +#define ufshcd_toggle_vreg(_dev, _vreg, _on) \ + ({ \ + int _ret; \ + if (_on) \ + _ret = ufshcd_enable_vreg(_dev, _vreg); \ + else \ + _ret = ufshcd_disable_vreg(_dev, _vreg); \ + _ret; \ + }) + enum { UFSHCD_MAX_CHANNEL = 0, UFSHCD_MAX_ID = 1, @@ -3193,6 +3203,153 @@ static struct scsi_host_template ufshcd_driver_template = { .can_queue = UFSHCD_CAN_QUEUE, }; +static int ufshcd_config_vreg(struct device *dev, + struct ufs_vreg *vreg, bool on) +{ + int ret = 0; + struct regulator *reg = vreg->reg; + const char *name = vreg->name; + int min_uV, uA_load; + + BUG_ON(!vreg); + + if (regulator_count_voltages(reg) > 0) { + min_uV = on ? vreg->min_uV : 0; + ret = regulator_set_voltage(reg, min_uV, vreg->max_uV); + if (ret) { + dev_err(dev, "%s: %s set voltage failed, err=%d\n", + __func__, name, ret); + goto out; + } + + uA_load = on ? vreg->max_uA : 0; + ret = regulator_set_optimum_mode(reg, uA_load); + if (ret >= 0) { + /* + * regulator_set_optimum_mode() returns new regulator + * mode upon success. + */ + ret = 0; + } else { + dev_err(dev, "%s: %s set optimum mode(uA_load=%d) failed, err=%d\n", + __func__, name, uA_load, ret); + goto out; + } + } +out: + return ret; +} + +static int ufshcd_enable_vreg(struct device *dev, struct ufs_vreg *vreg) +{ + int ret = 0; + + if (!vreg || vreg->enabled) + goto out; + + ret = ufshcd_config_vreg(dev, vreg, true); + if (!ret) + ret = regulator_enable(vreg->reg); + + if (!ret) + vreg->enabled = true; + else + dev_err(dev, "%s: %s enable failed, err=%d\n", + __func__, vreg->name, ret); +out: + return ret; +} + +static int ufshcd_disable_vreg(struct device *dev, struct ufs_vreg *vreg) +{ + int ret = 0; + + if (!vreg || !vreg->enabled) + goto out; + + ret = regulator_disable(vreg->reg); + + if (!ret) { + /* ignore errors on applying disable config */ + ufshcd_config_vreg(dev, vreg, false); + vreg->enabled = false; + } else { + dev_err(dev, "%s: %s disable failed, err=%d\n", + __func__, vreg->name, ret); + } +out: + return ret; +} + +static int ufshcd_setup_vreg(struct ufs_hba *hba, bool on) +{ + int ret = 0; + struct device *dev = hba->dev; + struct ufs_vreg_info *info = &hba->vreg_info; + + if (!info) + goto out; + + ret = ufshcd_toggle_vreg(dev, info->vcc, on); + if (ret) + goto out; + + ret = ufshcd_toggle_vreg(dev, info->vccq, on); + if (ret) + goto out; + + ret = ufshcd_toggle_vreg(dev, info->vccq2, on); + if (ret) + goto out; + +out: + if (ret) { + ufshcd_toggle_vreg(dev, info->vccq2, false); + ufshcd_toggle_vreg(dev, info->vccq, false); + ufshcd_toggle_vreg(dev, info->vcc, false); + } + return ret; +} + +static int ufshcd_get_vreg(struct device *dev, struct ufs_vreg *vreg) +{ + int ret = 0; + + if (!vreg) + goto out; + + vreg->reg = devm_regulator_get(dev, vreg->name); + if (IS_ERR(vreg->reg)) { + ret = PTR_ERR(vreg->reg); + dev_err(dev, "%s: %s get failed, err=%d\n", + __func__, vreg->name, ret); + } +out: + return ret; +} + +static int ufshcd_init_vreg(struct ufs_hba *hba) +{ + int ret = 0; + struct device *dev = hba->dev; + struct ufs_vreg_info *info = &hba->vreg_info; + + if (!info) + goto out; + + ret = ufshcd_get_vreg(dev, info->vcc); + if (ret) + goto out; + + ret = ufshcd_get_vreg(dev, info->vccq); + if (ret) + goto out; + + ret = ufshcd_get_vreg(dev, info->vccq2); +out: + return ret; +} + static int ufshcd_variant_hba_init(struct ufs_hba *hba) { int err = 0; @@ -3248,6 +3405,36 @@ static void ufshcd_variant_hba_exit(struct ufs_hba *hba) hba->vops->exit(hba); } +static int ufshcd_hba_init(struct ufs_hba *hba) +{ + int err; + + err = ufshcd_init_vreg(hba); + if (err) + goto out; + + err = ufshcd_setup_vreg(hba, true); + if (err) + goto out; + + err = ufshcd_variant_hba_init(hba); + if (err) + goto out_disable_vreg; + + goto out; + +out_disable_vreg: + ufshcd_setup_vreg(hba, false); +out: + return err; +} + +static void ufshcd_hba_exit(struct ufs_hba *hba) +{ + ufshcd_variant_hba_exit(hba); + ufshcd_setup_vreg(hba, false); +} + /** * ufshcd_suspend - suspend power management function * @hba: per adapter instance @@ -3333,7 +3520,7 @@ void ufshcd_remove(struct ufs_hba *hba) scsi_host_put(hba->host); - ufshcd_variant_hba_exit(hba); + ufshcd_hba_exit(hba); } EXPORT_SYMBOL_GPL(ufshcd_remove); @@ -3412,7 +3599,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) hba->mmio_base = mmio_base; hba->irq = irq; - err = ufshcd_variant_hba_init(hba); + err = ufshcd_hba_init(hba); if (err) goto out_error; @@ -3504,7 +3691,7 @@ out_remove_scsi_host: scsi_remove_host(hba->host); out_disable: scsi_host_put(host); - ufshcd_variant_hba_exit(hba); + ufshcd_hba_exit(hba); out_error: return err; } diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 8c6bec05283..c0232f95f5b 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -52,6 +52,7 @@ #include #include #include +#include #include #include @@ -219,6 +220,7 @@ struct ufs_hba_variant_ops { * @saved_uic_err: sticky UIC error mask * @dev_cmd: ufs device management command information * @auto_bkops_enabled: to track whether bkops is enabled in device + * @vreg_info: UFS device voltage regulator information */ struct ufs_hba { void __iomem *mmio_base; @@ -279,6 +281,7 @@ struct ufs_hba { struct ufs_dev_cmd dev_cmd; bool auto_bkops_enabled; + struct ufs_vreg_info vreg_info; }; #define ufshcd_writel(hba, val, reg) \ -- cgit v1.2.3-70-g09d2 From c6e79dacd86fd7ddd452fa52b3f4ca996db31e49 Mon Sep 17 00:00:00 2001 From: Sujit Reddy Thumma Date: Thu, 25 Sep 2014 15:32:23 +0300 Subject: ufs: Add clock initialization support Add generic clock initialization support for UFSHCD platform driver. The clock info is read from device tree using standard clock bindings. A generic max-clock-frequency-hz property is defined to save information on maximum operating clock frequency the h/w supports. Signed-off-by: Sujit Reddy Thumma Signed-off-by: Dolev Raviv Signed-off-by: Christoph Hellwig --- .../devicetree/bindings/ufs/ufshcd-pltfrm.txt | 15 +++- drivers/scsi/ufs/ufshcd-pci.c | 2 + drivers/scsi/ufs/ufshcd-pltfrm.c | 71 +++++++++++++++++ drivers/scsi/ufs/ufshcd.c | 89 +++++++++++++++++++++- drivers/scsi/ufs/ufshcd.h | 18 +++++ 5 files changed, 192 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt index 65e31173dcc..b0f791a8b28 100644 --- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt +++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt @@ -21,8 +21,17 @@ Optional properties: - vccq-max-microamp : specifies max. load that can be drawn from vccq supply - vccq2-max-microamp : specifies max. load that can be drawn from vccq2 supply +- clocks : List of phandle and clock specifier pairs +- clock-names : List of clock input name strings sorted in the same + order as the clocks property. +- max-clock-frequency-hz : List of maximum operating frequency stored in the same + order as the clocks property. If this property is not + defined or a value in the array is "0" then it is assumed + that the frequency is set by the parent clock or a + fixed rate clock source. + Note: If above properties are not defined it can be assumed that the supply -regulators are always on. +regulators or clocks are always on. Example: ufshc@0xfc598000 { @@ -37,4 +46,8 @@ Example: vcc-max-microamp = 500000; vccq-max-microamp = 200000; vccq2-max-microamp = 200000; + + clocks = <&core 0>, <&ref 0>, <&iface 0>; + clock-names = "core_clk", "ref_clk", "iface_clk"; + max-clock-frequency-hz = <100000000 19200000 0>; }; diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c index 7a6edbc55f9..2a26faa95b7 100644 --- a/drivers/scsi/ufs/ufshcd-pci.c +++ b/drivers/scsi/ufs/ufshcd-pci.c @@ -170,6 +170,8 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) return err; } + INIT_LIST_HEAD(&hba->clk_list_head); + err = ufshcd_init(hba, mmio_base, pdev->irq); if (err) { dev_err(&pdev->dev, "Initialization failed\n"); diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index 51e47c429a1..642d80fe1f8 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -53,6 +53,71 @@ static struct ufs_hba_variant_ops *get_variant_ops(struct device *dev) return NULL; } +static int ufshcd_parse_clock_info(struct ufs_hba *hba) +{ + int ret = 0; + int cnt; + int i; + struct device *dev = hba->dev; + struct device_node *np = dev->of_node; + char *name; + u32 *clkfreq = NULL; + struct ufs_clk_info *clki; + + if (!np) + goto out; + + INIT_LIST_HEAD(&hba->clk_list_head); + + cnt = of_property_count_strings(np, "clock-names"); + if (!cnt || (cnt == -EINVAL)) { + dev_info(dev, "%s: Unable to find clocks, assuming enabled\n", + __func__); + } else if (cnt < 0) { + dev_err(dev, "%s: count clock strings failed, err %d\n", + __func__, cnt); + ret = cnt; + } + + if (cnt <= 0) + goto out; + + clkfreq = kzalloc(cnt * sizeof(*clkfreq), GFP_KERNEL); + if (!clkfreq) { + ret = -ENOMEM; + dev_err(dev, "%s: memory alloc failed\n", __func__); + goto out; + } + + ret = of_property_read_u32_array(np, + "max-clock-frequency-hz", clkfreq, cnt); + if (ret && (ret != -EINVAL)) { + dev_err(dev, "%s: invalid max-clock-frequency-hz property, %d\n", + __func__, ret); + goto out; + } + + for (i = 0; i < cnt; i++) { + ret = of_property_read_string_index(np, + "clock-names", i, (const char **)&name); + if (ret) + goto out; + + clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL); + if (!clki) { + ret = -ENOMEM; + goto out; + } + + clki->max_freq = clkfreq[i]; + clki->name = kstrdup(name, GFP_KERNEL); + list_add_tail(&clki->list, &hba->clk_list_head); + } +out: + kfree(clkfreq); + return ret; +} + #define MAX_PROP_SIZE 32 static int ufshcd_populate_vreg(struct device *dev, const char *name, struct ufs_vreg **out_vreg) @@ -266,6 +331,12 @@ static int ufshcd_pltfrm_probe(struct platform_device *pdev) hba->vops = get_variant_ops(&pdev->dev); + err = ufshcd_parse_clock_info(hba); + if (err) { + dev_err(&pdev->dev, "%s: clock parse failed %d\n", + __func__, err); + goto out; + } err = ufshcd_parse_regulator_info(hba); if (err) { dev_err(&pdev->dev, "%s: regulator init failed %d\n", diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index ef8519e70e9..b0337029207 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -3350,6 +3350,80 @@ out: return ret; } +static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on) +{ + int ret = 0; + struct ufs_clk_info *clki; + struct list_head *head = &hba->clk_list_head; + + if (!head || list_empty(head)) + goto out; + + list_for_each_entry(clki, head, list) { + if (!IS_ERR_OR_NULL(clki->clk)) { + if (on && !clki->enabled) { + ret = clk_prepare_enable(clki->clk); + if (ret) { + dev_err(hba->dev, "%s: %s prepare enable failed, %d\n", + __func__, clki->name, ret); + goto out; + } + } else if (!on && clki->enabled) { + clk_disable_unprepare(clki->clk); + } + clki->enabled = on; + dev_dbg(hba->dev, "%s: clk: %s %sabled\n", __func__, + clki->name, on ? "en" : "dis"); + } + } +out: + if (ret) { + list_for_each_entry(clki, head, list) { + if (!IS_ERR_OR_NULL(clki->clk) && clki->enabled) + clk_disable_unprepare(clki->clk); + } + } + return ret; +} + +static int ufshcd_init_clocks(struct ufs_hba *hba) +{ + int ret = 0; + struct ufs_clk_info *clki; + struct device *dev = hba->dev; + struct list_head *head = &hba->clk_list_head; + + if (!head || list_empty(head)) + goto out; + + list_for_each_entry(clki, head, list) { + if (!clki->name) + continue; + + clki->clk = devm_clk_get(dev, clki->name); + if (IS_ERR(clki->clk)) { + ret = PTR_ERR(clki->clk); + dev_err(dev, "%s: %s clk get failed, %d\n", + __func__, clki->name, ret); + goto out; + } + + if (clki->max_freq) { + ret = clk_set_rate(clki->clk, clki->max_freq); + if (ret) { + dev_err(hba->dev, "%s: %s clk set rate(%dHz) failed, %d\n", + __func__, clki->name, + clki->max_freq, ret); + goto out; + } + } + dev_dbg(dev, "%s: clk: %s, rate: %lu\n", __func__, + clki->name, clk_get_rate(clki->clk)); + } +out: + return ret; +} + static int ufshcd_variant_hba_init(struct ufs_hba *hba) { int err = 0; @@ -3409,14 +3483,22 @@ static int ufshcd_hba_init(struct ufs_hba *hba) { int err; - err = ufshcd_init_vreg(hba); + err = ufshcd_init_clocks(hba); if (err) goto out; - err = ufshcd_setup_vreg(hba, true); + err = ufshcd_setup_clocks(hba, true); if (err) goto out; + err = ufshcd_init_vreg(hba); + if (err) + goto out_disable_clks; + + err = ufshcd_setup_vreg(hba, true); + if (err) + goto out_disable_clks; + err = ufshcd_variant_hba_init(hba); if (err) goto out_disable_vreg; @@ -3425,6 +3507,8 @@ static int ufshcd_hba_init(struct ufs_hba *hba) out_disable_vreg: ufshcd_setup_vreg(hba, false); +out_disable_clks: + ufshcd_setup_clocks(hba, false); out: return err; } @@ -3433,6 +3517,7 @@ static void ufshcd_hba_exit(struct ufs_hba *hba) { ufshcd_variant_hba_exit(hba); ufshcd_setup_vreg(hba, false); + ufshcd_setup_clocks(hba, false); } /** diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index c0232f95f5b..bc0f7ed0260 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -155,6 +155,22 @@ struct ufs_dev_cmd { struct ufs_query query; }; +/** + * struct ufs_clk_info - UFS clock related info + * @list: list headed by hba->clk_list_head + * @clk: clock node + * @name: clock name + * @max_freq: maximum frequency supported by the clock + * @enabled: variable to check against multiple enable/disable + */ +struct ufs_clk_info { + struct list_head list; + struct clk *clk; + const char *name; + u32 max_freq; + bool enabled; +}; + #define PRE_CHANGE 0 #define POST_CHANGE 1 /** @@ -221,6 +237,7 @@ struct ufs_hba_variant_ops { * @dev_cmd: ufs device management command information * @auto_bkops_enabled: to track whether bkops is enabled in device * @vreg_info: UFS device voltage regulator information + * @clk_list_head: UFS host controller clocks list node head */ struct ufs_hba { void __iomem *mmio_base; @@ -282,6 +299,7 @@ struct ufs_hba { bool auto_bkops_enabled; struct ufs_vreg_info vreg_info; + struct list_head clk_list_head; }; #define ufshcd_writel(hba, val, reg) \ -- cgit v1.2.3-70-g09d2 From 6a771a656041f404fae143e5d753d37f5c0688e7 Mon Sep 17 00:00:00 2001 From: Raviv Shvili Date: Thu, 25 Sep 2014 15:32:24 +0300 Subject: ufs: add voting support for host controller power Add the support for voting of the regulator powering the host controller logic. Signed-off-by: Raviv Shvili Signed-off-by: Subhash Jadavani Signed-off-by: Dolev Raviv Signed-off-by: Christoph Hellwig --- .../devicetree/bindings/ufs/ufshcd-pltfrm.txt | 4 +++ drivers/scsi/ufs/ufs.h | 1 + drivers/scsi/ufs/ufshcd-pltfrm.c | 9 +++++ drivers/scsi/ufs/ufshcd.c | 42 ++++++++++++++++++++-- 4 files changed, 54 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt index b0f791a8b28..fb1234e0532 100644 --- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt +++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt @@ -9,6 +9,7 @@ Required properties: - reg : Optional properties: +- vdd-hba-supply : phandle to UFS host controller supply regulator node - vcc-supply : phandle to VCC supply regulator node - vccq-supply : phandle to VCCQ supply regulator node - vccq2-supply : phandle to VCCQ2 supply regulator node @@ -20,6 +21,7 @@ Optional properties: - vcc-max-microamp : specifies max. load that can be drawn from vcc supply - vccq-max-microamp : specifies max. load that can be drawn from vccq supply - vccq2-max-microamp : specifies max. load that can be drawn from vccq2 supply +- -fixed-regulator : boolean property specifying that -supply is a fixed regulator - clocks : List of phandle and clock specifier pairs - clock-names : List of clock input name strings sorted in the same @@ -39,6 +41,8 @@ Example: reg = <0xfc598000 0x800>; interrupts = <0 28 0>; + vdd-hba-supply = <&xxx_reg0>; + vdd-hba-fixed-regulator; vcc-supply = <&xxx_reg1>; vcc-supply-1p8; vccq-supply = <&xxx_reg2>; diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index 729ce7d62d1..9bb691962c4 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -385,6 +385,7 @@ struct ufs_vreg_info { struct ufs_vreg *vcc; struct ufs_vreg *vccq; struct ufs_vreg *vccq2; + struct ufs_vreg *vdd_hba; }; #endif /* End of Header */ diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index 642d80fe1f8..dde4e6e3be7 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -147,6 +147,11 @@ static int ufshcd_populate_vreg(struct device *dev, const char *name, vreg->name = kstrdup(name, GFP_KERNEL); + /* if fixed regulator no need further initialization */ + snprintf(prop_name, MAX_PROP_SIZE, "%s-fixed-regulator", name); + if (of_property_read_bool(np, prop_name)) + goto out; + snprintf(prop_name, MAX_PROP_SIZE, "%s-max-microamp", name); ret = of_property_read_u32(np, prop_name, &vreg->max_uA); if (ret) { @@ -198,6 +203,10 @@ 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); + if (err) + goto out; + err = ufshcd_populate_vreg(dev, "vcc", &info->vcc); if (err) goto out; diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index b0337029207..26301b8325e 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -3311,6 +3311,16 @@ out: return ret; } +static int ufshcd_setup_hba_vreg(struct ufs_hba *hba, bool on) +{ + struct ufs_vreg_info *info = &hba->vreg_info; + + if (info) + return ufshcd_toggle_vreg(hba->dev, info->vdd_hba, on); + + return 0; +} + static int ufshcd_get_vreg(struct device *dev, struct ufs_vreg *vreg) { int ret = 0; @@ -3350,6 +3360,16 @@ out: return ret; } +static int ufshcd_init_hba_vreg(struct ufs_hba *hba) +{ + struct ufs_vreg_info *info = &hba->vreg_info; + + if (info) + return ufshcd_get_vreg(hba->dev, info->vdd_hba); + + return 0; +} + static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on) { int ret = 0; @@ -3483,14 +3503,29 @@ static int ufshcd_hba_init(struct ufs_hba *hba) { int err; - err = ufshcd_init_clocks(hba); + /* + * Handle host controller power separately from the UFS device power + * rails as it will help controlling the UFS host controller power + * collapse easily which is different than UFS device power collapse. + * Also, enable the host controller power before we go ahead with rest + * of the initialization here. + */ + err = ufshcd_init_hba_vreg(hba); if (err) goto out; - err = ufshcd_setup_clocks(hba, true); + err = ufshcd_setup_hba_vreg(hba, true); if (err) goto out; + err = ufshcd_init_clocks(hba); + if (err) + goto out_disable_hba_vreg; + + err = ufshcd_setup_clocks(hba, true); + if (err) + goto out_disable_hba_vreg; + err = ufshcd_init_vreg(hba); if (err) goto out_disable_clks; @@ -3509,6 +3544,8 @@ out_disable_vreg: ufshcd_setup_vreg(hba, false); out_disable_clks: ufshcd_setup_clocks(hba, false); +out_disable_hba_vreg: + ufshcd_setup_hba_vreg(hba, false); out: return err; } @@ -3518,6 +3555,7 @@ static void ufshcd_hba_exit(struct ufs_hba *hba) ufshcd_variant_hba_exit(hba); ufshcd_setup_vreg(hba, false); ufshcd_setup_clocks(hba, false); + ufshcd_setup_hba_vreg(hba, false); } /** -- cgit v1.2.3-70-g09d2 From 4cff6d991e4a291cf50fe2659da2ea9ad46620bf Mon Sep 17 00:00:00 2001 From: Sahitya Tummala Date: Thu, 25 Sep 2014 15:32:33 +0300 Subject: ufs: Add freq-table-hz property for UFS device Add freq-table-hz propery for UFS device to keep track of frequencies supported by UFS clocks. Signed-off-by: Sahitya Tummala Signed-off-by: Dolev Raviv Signed-off-by: Christoph Hellwig --- .../devicetree/bindings/ufs/ufshcd-pltfrm.txt | 12 +++--- drivers/scsi/ufs/ufshcd-pltfrm.c | 48 ++++++++++++++++------ drivers/scsi/ufs/ufshcd.h | 2 + 3 files changed, 43 insertions(+), 19 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt index fb1234e0532..53579197eca 100644 --- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt +++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt @@ -26,11 +26,11 @@ Optional properties: - clocks : List of phandle and clock specifier pairs - clock-names : List of clock input name strings sorted in the same order as the clocks property. -- max-clock-frequency-hz : List of maximum operating frequency stored in the same - order as the clocks property. If this property is not - defined or a value in the array is "0" then it is assumed - that the frequency is set by the parent clock or a - fixed rate clock source. +- freq-table-hz : Array of operating frequencies stored in the same + order as the clocks property. If this property is not + defined or a value in the array is "0" then it is assumed + that the frequency is set by the parent clock or a + fixed rate clock source. Note: If above properties are not defined it can be assumed that the supply regulators or clocks are always on. @@ -53,5 +53,5 @@ Example: clocks = <&core 0>, <&ref 0>, <&iface 0>; clock-names = "core_clk", "ref_clk", "iface_clk"; - max-clock-frequency-hz = <100000000 19200000 0>; + freq-table-hz = <100000000 200000000>, <0 0>, <0 0>; }; diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index 2482bbac368..8adf067ff01 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -63,6 +63,8 @@ static int ufshcd_parse_clock_info(struct ufs_hba *hba) char *name; u32 *clkfreq = NULL; struct ufs_clk_info *clki; + int len = 0; + size_t sz = 0; if (!np) goto out; @@ -82,39 +84,59 @@ static int ufshcd_parse_clock_info(struct ufs_hba *hba) if (cnt <= 0) goto out; - clkfreq = kzalloc(cnt * sizeof(*clkfreq), GFP_KERNEL); + if (!of_get_property(np, "freq-table-hz", &len)) { + dev_info(dev, "freq-table-hz property not specified\n"); + goto out; + } + + if (len <= 0) + goto out; + + sz = len / sizeof(*clkfreq); + if (sz != 2 * cnt) { + dev_err(dev, "%s len mismatch\n", "freq-table-hz"); + ret = -EINVAL; + goto out; + } + + clkfreq = devm_kzalloc(dev, sz * sizeof(*clkfreq), + GFP_KERNEL); if (!clkfreq) { + dev_err(dev, "%s: no memory\n", "freq-table-hz"); ret = -ENOMEM; - dev_err(dev, "%s: memory alloc failed\n", __func__); goto out; } - ret = of_property_read_u32_array(np, - "max-clock-frequency-hz", clkfreq, cnt); + ret = of_property_read_u32_array(np, "freq-table-hz", + clkfreq, sz); if (ret && (ret != -EINVAL)) { - dev_err(dev, "%s: invalid max-clock-frequency-hz property, %d\n", - __func__, ret); - goto out; + dev_err(dev, "%s: error reading array %d\n", + "freq-table-hz", ret); + goto free_clkfreq; } - for (i = 0; i < cnt; i++) { + for (i = 0; i < sz; i += 2) { ret = of_property_read_string_index(np, - "clock-names", i, (const char **)&name); + "clock-names", i/2, (const char **)&name); if (ret) - goto out; + goto free_clkfreq; clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL); if (!clki) { ret = -ENOMEM; - goto out; + goto free_clkfreq; } - clki->max_freq = clkfreq[i]; + clki->min_freq = clkfreq[i]; + clki->max_freq = clkfreq[i+1]; clki->name = kstrdup(name, GFP_KERNEL); + dev_dbg(dev, "%s: min %u max %u name %s\n", "freq-table-hz", + clki->min_freq, clki->max_freq, clki->name); list_add_tail(&clki->list, &hba->clk_list_head); } -out: +free_clkfreq: kfree(clkfreq); +out: return ret; } diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 29d34d3aa5e..ac72a6daf50 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -209,6 +209,7 @@ struct ufs_dev_cmd { * @clk: clock node * @name: clock name * @max_freq: maximum frequency supported by the clock + * @min_freq: min frequency that can be used for clock scaling * @enabled: variable to check against multiple enable/disable */ struct ufs_clk_info { @@ -216,6 +217,7 @@ struct ufs_clk_info { struct clk *clk; const char *name; u32 max_freq; + u32 min_freq; bool enabled; }; -- cgit v1.2.3-70-g09d2